From 0ef0e75b3bc706ee5f15866f95b19e160dcbe554 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Fri, 17 Apr 2026 19:22:36 +0200 Subject: [PATCH 01/54] [civetweb] download instead of bundle and define proper CMake target, and bump to 1.16 --- builtins/civetweb/CMakeLists.txt | 77 + cmake/modules/FindFastCGI.cmake | 7 + cmake/modules/SearchInstalledSoftware.cmake | 24 +- net/http/CMakeLists.txt | 72 +- net/http/civetweb/CREDITS.md | 294 - net/http/civetweb/LICENSE.md | 252 - net/http/civetweb/README.md | 193 - net/http/civetweb/civetweb.c | 23202 ------------------ net/http/civetweb/civetweb.h | 1833 -- net/http/civetweb/handle_form.inl | 1117 - net/http/civetweb/match.inl | 262 - net/http/civetweb/md5.inl | 472 - net/http/civetweb/openssl_dl.inl | 545 - net/http/civetweb/response.inl | 342 - net/http/civetweb/sha1.inl | 323 - net/http/civetweb/sort.inl | 48 - 16 files changed, 98 insertions(+), 28965 deletions(-) create mode 100644 builtins/civetweb/CMakeLists.txt delete mode 100644 net/http/civetweb/CREDITS.md delete mode 100644 net/http/civetweb/LICENSE.md delete mode 100644 net/http/civetweb/README.md delete mode 100644 net/http/civetweb/civetweb.c delete mode 100644 net/http/civetweb/civetweb.h delete mode 100644 net/http/civetweb/handle_form.inl delete mode 100644 net/http/civetweb/match.inl delete mode 100644 net/http/civetweb/md5.inl delete mode 100644 net/http/civetweb/openssl_dl.inl delete mode 100644 net/http/civetweb/response.inl delete mode 100644 net/http/civetweb/sha1.inl delete mode 100644 net/http/civetweb/sort.inl diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt new file mode 100644 index 0000000000000..78d1260bfb9b0 --- /dev/null +++ b/builtins/civetweb/CMakeLists.txt @@ -0,0 +1,77 @@ +# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. +# All rights reserved. +# +# For the licensing terms see $ROOTSYS/LICENSE. +# For the list of contributors see $ROOTSYS/README/CREDITS. + +# **PLEASE UPDATE ALSO THE FOLLOWING LINE WHEN UPDATING THE VERSION** +# 10 Apr 2023, https://github.com/civetweb/civetweb/releases/tag/v1.16 +set(ROOT_CIVETWEB_VERSION 1.16) +set(ROOT_CIVETWEB_HASH "f0e471c1bf4e7804a6cfb41ea9d13e7d623b2bcc7bc1e2a4dd54951a24d60285") +set(ROOT_CIVETWEB_PREFIX ${CMAKE_BINARY_DIR}/builtins/CIVETWEB-prefix) +set(ROOT_CIVETWEB_LIBRARY ${ROOT_CIVETWEB_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}civetweb${CMAKE_STATIC_LIBRARY_SUFFIX}) + +if (NOT DEFINED GIT_EXECUTABLE) + set(GIT_EXECUTABLE "git") +endif() + +include(ExternalProject) + +# Clear cache variables set by find_package(CIVETWEB) +# to ensure that we use the builtin version +foreach(var CIVETWEB_LIBRARIES CIVETWEB_LIBRARY CIVETWEB_LIBRARY_DEBUG CIVETWEB_LIBRARY_RELEASE CIVETWEB_FOUND CIVETWEB_VERSION CIVETWEB_INCLUDE_DIR CIVETWEB_LIBRARY CIVETWEB_LIBRARIES) + unset(${var}) + unset(${var} CACHE) +endforeach() + +if(WIN32 AND NOT CMAKE_GENERATOR MATCHES Ninja) + if(winrtdebug) + set(ROOT_CIVETWEB_BUILD_COMMAND_FLAGS "--config Debug") + else() + set(ROOT_CIVETWEB_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") + endif() +endif() + +ExternalProject_Add(BUILTIN_CIVETWEB + PREFIX ${ROOT_CIVETWEB_PREFIX} + # URL ${lcgpackages}/civetweb-${ROOT_CIVETWEB_VERSION}.tar.gz + URL https://github.com/civetweb/civetweb/archive/refs/tags/v1.16.tar.gz # TODO move to LCG + URL_HASH SHA256=${ROOT_CIVETWEB_HASH} + + LOG_DOWNLOAD TRUE + LOG_CONFIGURE TRUE + LOG_BUILD TRUE + LOG_INSTALL TRUE + LOG_OUTPUT_ON_FAILURE TRUE + + CMAKE_ARGS -G ${CMAKE_GENERATOR} + -DCMAKE_INSTALL_PREFIX:PATH= + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} + -DBUILD_SHARED_LIBS:BOOL=FALSE + -DBUILD_TESTING=FALSE + -DCIVETWEB_BUILD_TESTING=FALSE + -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} + INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install + BUILD_BYPRODUCTS + ${ROOT_CIVETWEB_LIBRARY} + TIMEOUT 600 +) + +file(MAKE_DIRECTORY ${ROOT_CIVETWEB_PREFIX}/include) +add_library(civetweb::civetweb IMPORTED STATIC GLOBAL) +add_dependencies(civetweb::civetweb BUILTIN_CIVETWEB) +set_target_properties(civetweb::civetweb PROPERTIES + IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${ROOT_CIVETWEB_PREFIX}/include) +target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) # needed for Win32 since public flag is not correctly propagated to parent scope (BUILD_SHARED_LIBS works fine for building but when installing, flag info is lost) + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(civetweb_INCLUDE_DIRS ${ROOT_CIVETWEB_PREFIX}/include PARENT_SCOPE) +set(civetweb_LIBRARIES ${ROOT_CIVETWEB_LIBRARY} PARENT_SCOPE) +set(civetweb_FOUND TRUE PARENT_SCOPE) +set(civetweb_VERSION ${ROOT_CIVETWEB_VERSION} PARENT_SCOPE) diff --git a/cmake/modules/FindFastCGI.cmake b/cmake/modules/FindFastCGI.cmake index 6d81a65086b81..8a5b4a51d750e 100644 --- a/cmake/modules/FindFastCGI.cmake +++ b/cmake/modules/FindFastCGI.cmake @@ -29,4 +29,11 @@ if(FASTCGI_FOUND) if(NOT FASTCGI_LIBRARIES) set(FASTCGI_LIBRARIES ${FASTCGI_LIBRARY}) endif() + + if(NOT TARGET FastCGI::FastCGI) + add_library(FastCGI::FastCGI UNKNOWN IMPORTED) + set_target_properties(FastCGI::FastCGI PROPERTIES + IMPORTED_LOCATION "${FASTCGI_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${FASTCGI_INCLUDE_DIR}") + endif() endif() diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 62abca092ace0..8ebdcfa681da0 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -212,24 +212,22 @@ endif() #--- Check for civetweb --------------------------------------------------------- if(http AND NOT builtin_civetweb) message(STATUS "Looking for civetweb") - if(NOT "$ENV{CIVETWEB_BUILD}" STREQUAL "" AND NOT "$ENV{CIVETWEB_SRC}" STREQUAL "") - set(civetweb_LIBRARIES $ENV{CIVETWEB_BUILD}/src/libcivetweb.so) - set(civetweb_INCLUDE_DIR $ENV{CIVETWEB_SRC}/include) - message(STATUS "Use civetweb ${civetweb_LIBRARIES} and ${civetweb_INCLUDE_DIR}") + if(fail-on-missing) + find_package(civetweb 1.15 REQUIRED) else() - if(fail-on-missing) - find_package(civetweb 1.15 REQUIRED) + find_package(civetweb 1.15 QUIET) + if(civetweb_FOUND) + message(STATUS "Found civetweb version ${civetweb_VERSION}") else() - find_package(civetweb 1.15 QUIET) - if(civetweb_FOUND) - message(STATUS "Found civetweb version ${civetweb_VERSION}") - else() - message(STATUS "civetweb not found. Switching on builtin_civetweb option") - set(builtin_civetweb ON CACHE BOOL "Enabled because civetweb not found" FORCE) - endif() + message(STATUS "civetweb not found. Switching on builtin_civetweb option") + set(builtin_civetweb ON CACHE BOOL "Enabled because civetweb not found" FORCE) endif() endif() endif() +if(http AND builtin_civetweb) + list(APPEND ROOT_BUILTINS BUILTIN_CIVETWEB) + add_subdirectory(builtins/civetweb) +endif() #---Check for Unuran ------------------------------------------------------------------ if(unuran AND NOT builtin_unuran) diff --git a/net/http/CMakeLists.txt b/net/http/CMakeLists.txt index 3121f376cf253..784b8bb760a7a 100644 --- a/net/http/CMakeLists.txt +++ b/net/http/CMakeLists.txt @@ -14,17 +14,6 @@ if(NOT FASTCGI_FOUND) set(FASTCGI_LIBRARY "") endif() -# look for the realtime extensions library and use it if it exists -find_library(RT_LIBRARY rt) -if(RT_LIBRARY) - set(RT_LIBRARIES ${RT_LIBRARY}) -endif() - -if(builtin_civetweb) - set(_civetweb_src civetweb/civetweb.c) - set(_civetweb_libs ${RT_LIBRARIES}) -endif() - ROOT_STANDARD_LIBRARY_PACKAGE(RHTTP HEADERS THttpCallArg.h @@ -48,72 +37,17 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RHTTP src/THttpWSHandler.cxx src/TRootSniffer.cxx src/TRootSnifferStore.cxx - ${_civetweb_src} LIBRARIES ZLIB::ZLIB - ${_civetweb_libs} - ${FASTCGI_LIBRARY} + civetweb::civetweb ${CMAKE_DL_LIBS} DEPENDENCIES RIO Thread ) -if(builtin_civetweb) - target_include_directories(RHTTP PRIVATE civetweb) - - if(ssl) - target_include_directories(RHTTP SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) - endif() - - target_compile_definitions(RHTTP PRIVATE -DUSE_WEBSOCKET) - - if(NOT MSVC) - target_compile_definitions(RHTTP PRIVATE -DUSE_X_DOM_SOCKET) - endif() - - if(ssl) - if(OPENSSL_VERSION) - string(REPLACE "." ";" lst ${OPENSSL_VERSION}) - list(GET lst 0 ssl_major) - list(GET lst 1 ssl_minor) - endif() - - if((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "1")) - MESSAGE(STATUS "Use SSL API VERSION 1.1 for civetweb") - target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_1_1) - set(link_ssl ON) - elseif((${ssl_major} EQUAL "3")) - MESSAGE(STATUS "Use SSL API VERSION 3.${ssl_minor} for civetweb") - target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_3_0) - set(link_ssl ON) - elseif((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "0")) - MESSAGE(STATUS "Use SSL API VERSION 1.0 for civetweb") - target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_1_0) - set(link_ssl ON) - else() - MESSAGE(WARNING "Not able to recognize SSL version ${OPENSSL_VERSION}, disable SSL") - target_compile_definitions(RHTTP PUBLIC -DNO_SSL) - endif() - if(link_ssl) - target_compile_definitions(RHTTP PUBLIC -DNO_SSL_DL) - target_link_libraries(RHTTP PRIVATE ${OPENSSL_LIBRARIES}) - endif() - else() - target_compile_definitions(RHTTP PUBLIC -DNO_SSL) - endif() - -else() - - target_include_directories(RHTTP SYSTEM PRIVATE ${civetweb_INCLUDE_DIR}) - target_link_libraries(RHTTP PRIVATE ${civetweb_LIBRARIES}) - # workaround for R__memcompress failure with external civetweb - target_compile_definitions(RHTTP PRIVATE -D_EXTERNAL_CIVETWEB) - -endif(builtin_civetweb) - -if(FASTCGI_FOUND) - target_include_directories(RHTTP PRIVATE ${FASTCGI_INCLUDE_DIR}) +if(TARGET FastCGI::FastCGI) + target_link_libraries(RHTTP PRIVATE FastCGI::FastCGI) else() target_compile_definitions(RHTTP PUBLIC -DHTTP_WITHOUT_FASTCGI) endif() diff --git a/net/http/civetweb/CREDITS.md b/net/http/civetweb/CREDITS.md deleted file mode 100644 index f6a2a79dcb836..0000000000000 --- a/net/http/civetweb/CREDITS.md +++ /dev/null @@ -1,294 +0,0 @@ -# Civetweb Contributors - -* Abhishek Lekshmanan -* Abramo Bagnara -* Adam Bailey -* Adam Hunyadi -* Adam Rowell -* Alan Somers -* Alberto Bignotti -* Alex Kozlov -* Alexander Kozhinov -* AndreyArsov -* Anton Te -* beaver -* bel2125 -* Ben M. Ward -* Bernhard Lehner -* BigJoe -* Bjoern Petri -* Braedy Kuzma -* Breno Ramalho Lemes -* brett -* Brian Lambert -* Brian Spratke -* cdbishop -* celeron55 -* Charles Olivi -* Chen Yufei -* Chris Han -* Chris Jones -* Chris Rehn -* Christian Mauderer -* Christopher Friedt -* Christopher Galas -* cjh -* Colden Cullen -* Colm Sloan -* Cortronic -* Daniel Oaks -* Daniel Rempel -* Danny Al-Gaaf -* Dave Brower -* daveelton -* David Arnold -* David Korczynski -* David Loffredo -* DavidKorczynski -* dennis -* Dialga -* DL6ER -* Domenico Di Iorio -* dprandle -* Drew Wells -* drew-wells -* duong2179 -* ehlertjd -* Elan P. Kugelmass -* Eric Tsau -* Erick Vieyra -* Erik Beran -* Erik Partridge -* eugene -* extergnoto -* F-Secure Corporation -* Fabrice Fontaine -* feneuilflo -* Fernando G. Aranda -* forworldm -* Frank Hilliger -* gajanak -* Gerard Marull-Paretas -* Girish Joshi -* goodmenzy -* Grahack -* Gregor Jasny -* grenclave -* grunk -* guangqing.chen -* Guilherme Amadio -* Gustavo Romero -* Gábor Csárdi -* hansipie -* HariKamath Kamath -* Henry Chang -* Herumb Shandilya -* Herve Codina -* huangminhang -* Iain Morton -* ImgBotApp -* Ivan Dlugos -* IZI -* Jack -* Jacob Repp -* Jacob Skillin -* Jan Kowalewski -* Jan Pohanka -* Jan Willem Janssen -* Jens Wallgren -* Jeremy Lin -* Jesse Williamson -* Jim Evans -* jmc- -* Joakim L. Gilje -* Jochen Scheib -* Joe Mucchiello -* Joel Gallant -* Johan De Taeye -* John Faith -* Jonas Hahnfeld -* Jonas Rembser -* Jordan -* Jordan Shelley -* Joshua Boyd -* Joshua D. Boyd -* Justin Standring -* k-mds -* kakwa -* kalphamon -* Karol Mroz -* Keith Holman -* Keith Kyzivat -* Ken Walters -* Kevin Branigan -* Kevin Wojniak -* Khem Raj -* Kimmo Mustonen -* Kjeld Flarup -* krpano -* Krzysztof Kozlowski -* Lammert Bies -* Lars Immisch -* Lawrence -* Lev275568 -* Li Peng -* Lianghui -* Lorenzo Canepa -* Luka Rahne -* Lukas Martanovic -* Maarten Fremouw -* makrsmark -* marco -* Mario Trangoni -* Mark Lakata -* Martin Gaida -* Mateusz Gralka -* Matt Clarkson -* Mellnik -* MHU-valantic -* Mike Crowe -* mingodad -* Morgan McGuire -* mrdvlpr.xnu -* Nat! -* Neil Jensen -* newsoft -* nfrmtkr -* Nick Hildebrant -* Nigel Stewart -* nihildeb -* Niklas Fiekas -* No Face Press -* Olliver Schinagl -* palortoff -* Patrick Drechsler -* Patrick Trinkle -* Paul Sokolovsky -* Paulo Brizolara -* pavel.pimenov -* PavelVozenilek -* Perttu Ahola -* Peter Foerster -* Peter Huber -* Philipp Friedenberger -* Philipp Hasper -* Pieter Cardoen -* Piotr Zierhoffer -* pkvamme -* Ponnuvel Palaniyappan -* qinch -* qinchao -* r-j-s -* Radoslaw Zarzynski -* Rajdeep Roy Chowdhury -* Red54 -* Retallack Mark mark.retallack -* Richard Screene -* Rimas Misevi-ìius -* Rinat Dobrokhotov -* ryankopf -* Sage Weil -* Sangwhan Moon -* Saumitra Vikram -* sbruceheart -* Scott Fennell -* Scott Nations -* Sebastien Jodogne -* Sergey Linev -* sgmesservey -* shantanugadgil -* Sherwyn Sen -* shreyajaggi8 -* Silas Parker -* silverches -* Simon Hailes -* slidertom -* SpaceIm -* SpaceLord -* Stefan Codrescu -* sunfch -* suzukibitman -* Símal Rasmussen -* Tamotsu Kanoh -* thewaterymoon -* Thiago Macedo -* THILMANT, Bernard -* Thomas Davis -* Thomas Klausner -* Thorsten Horstmann -* Tim Gates -* Tim Hudson -* tnoho -* Tom Deblauwe -* Tomas Andrle -* Tomasz Gorochowik -* Toni Wilk -* Torben Jonas -* Uilian Ries -* Ulrich Hertlein -* videofan3d -* Walt Steverson -* wangli28 -* webxer -* William Greathouse -* Wolfram Rösler -* xeoshow -* xtne6f -* Yehuda Sadeh -* Yury Z -* zhen.wang -* Zopolis4 - -and others. - -# Mongoose Contributors -CivetWeb is based on the Mongoose code. The following users contributed to the original Mongoose release between 2010 and 2013. This list was generated from the Mongoose GIT logs. It does not contain contributions from the Mongoose mailing list. There is no record for contributors prior to 2010. - -* Sergey Lyubka -* Arnout Vandecappelle (Essensium/Mind) -* Benoît Amiaux -* Cody Hanson -* Colin Leitner -* Daniel Oaks -* Eric Bakan -* Erik Oomen -* Filipp Kovalev -* Ger Hobbelt -* Hendrik Polczynski -* Henrique Mendonça -* Igor Okulist -* Jay -* Joe Mucchiello -* John Safranek -* Joseph Mainwaring -* José Miguel Gonçalves -* KIU Shueng Chuan -* Katerina Blinova -* Konstantin Sorokin -* Marin Atanasov Nikolov -* Matt Healy -* Miguel Morales -* Mikhail Nikalyukin -* MikieMorales -* Mitch Hendrickson -* Nigel Stewart -* Pavel -* Pavel Khlebovich -* Rogerz Zhang -* Sebastian Reinhard -* Stefan Doehla -* Thileepan -* abadc0de -* arvidn -* bick -* ff.feng -* jmucchiello -* jwang -* lsm -* migal -* mlamb -* nullable.type -* shantanugadgil -* tayS -* test -* valenok diff --git a/net/http/civetweb/LICENSE.md b/net/http/civetweb/LICENSE.md deleted file mode 100644 index a74a0fe4ef07c..0000000000000 --- a/net/http/civetweb/LICENSE.md +++ /dev/null @@ -1,252 +0,0 @@ -ALL LICENSES -===== - -This document includes several copyright licenses for different -aspects of the software. Not all licenses may apply depending -on the features chosen. - - -Civetweb License ------ - -### Included with all features. - -> Copyright (c) 2013-2021 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) -> -> Copyright (c) 2004-2013 Sergey Lyubka -> -> Copyright (c) 2013 No Face Press, LLC (Thomas Davis) -> -> Copyright (c) 2013 F-Secure Corporation -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - - -Lua License ------- - -### Included only if built with Lua support. - -http://www.lua.org/license.html - -> Copyright (C) 1994-2020 Lua.org, PUC-Rio. -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - - -Additional components Copyright (C) Lua.org, PUC-Rio, with MIT license: -http://www.inf.puc-rio.br/~roberto/struct/ - - -SQLite3 License ------- - -### Included only if built with Lua and SQLite support. - -http://www.sqlite.org/copyright.html - -> 2001-09-15 -> -> 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. - - -lsqlite3 License ------- - -### Included only if built with Lua and SQLite support. - -> Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie -> All rights reserved. -> Author : Tiago Dionizio -> Author : Doug Currie -> Library : lsqlite3 - an SQLite 3 database binding for Lua 5 -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - - -Lua File System License ------- - -### Included only if built with Lua support. - -https://github.com/keplerproject/luafilesystem/blob/master/LICENSE - -> Copyright © 2003-2020 Kepler Project. -> -> Permission is hereby granted, free of charge, to any person -> obtaining a copy of this software and associated documentation -> files (the "Software"), to deal in the Software without -> restriction, including without limitation the rights to use, copy, -> modify, merge, publish, distribute, sublicense, and/or sell copies -> of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be -> included in all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -> BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -> ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -> CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -> SOFTWARE. - - -LuaXML License ------- - -### Included only if built with Lua and LuaXML support. - -Version 1.8.0 (Lua 5.2), 2013-06-10 by Gerald Franz, eludi.net - -Modified and extended 2015 by Bernhard Nortmann, https://github.com/n1tehawk/LuaXML – version 2.0.x, compatible with Lua 5.1 to 5.3 and LuaJIT. - - -> LuaXML License -> -> LuaXml is licensed under the terms of the MIT license reproduced below, -> the same as Lua itself. This means that LuaXml is free software and can be -> used for both academic and commercial purposes at absolutely no cost. -> -> Copyright (C) 2007-2013 Gerald Franz, eludi.net -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - - -Duktape License ------- - -### Included only if built with Duktape support. - -https://github.com/svaarala/duktape/blob/master/LICENSE.txt - -> =============== -> Duktape license -> =============== -> -> (http://opensource.org/licenses/MIT) -> -> Copyright (c) 2013-2017 by Duktape authors (see AUTHORS.rst) -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - - - -zlib License ------- - -### Included only if built with zlib support. - -https://www.zlib.net/zlib_license.html - -> zlib.h -- interface of the 'zlib' general purpose compression library -> version 1.2.11, January 15th, 2017 -> -> Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler -> -> This software is provided 'as-is', without any express or implied -> warranty. In no event will the authors be held liable for any damages -> arising from the use of this software. -> -> Permission is granted to anyone to use this software for any purpose, -> including commercial applications, and to alter it and redistribute it -> freely, subject to the following restrictions: -> -> 1. The origin of this software must not be misrepresented; you must not -> claim that you wrote the original software. If you use this software -> in a product, an acknowledgment in the product documentation would be -> appreciated but is not required. -> 2. Altered source versions must be plainly marked as such, and must not be -> misrepresented as being the original software. -> 3. This notice may not be removed or altered from any source distribution. -> -> Jean-loup Gailly Mark Adler -> jloup@gzip.org madler@alumni.caltech.edu - diff --git a/net/http/civetweb/README.md b/net/http/civetweb/README.md deleted file mode 100644 index dae058791bd64..0000000000000 --- a/net/http/civetweb/README.md +++ /dev/null @@ -1,193 +0,0 @@ -![CivetWeb](/resources/civetweb_64x64.png "CivetWeb") CivetWeb -======= - -**The official home of CivetWeb is on GitHub [https://github.com/civetweb/civetweb](https://github.com/civetweb/civetweb)** - -[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) -[![GitHub contributors](https://img.shields.io/github/contributors/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/blob/master/CREDITS.md) -[![Stargazers](https://img.shields.io/github/stars/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/stargazers) -[![Forks](https://img.shields.io/github/forks/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/network/members) -[![Latest Release](https://img.shields.io/github/v/release/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/releases) - -Continuous integration for Linux and macOS ([Travis CI](https://app.travis-ci.com/github/civetweb/civetweb)): - -[![Travis Build Status](https://api.travis-ci.com/civetweb/civetweb.svg?branch=master)](https://app.travis-ci.com/github/civetweb/civetweb) - -Continuous integration for Windows ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): - -[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/civetweb/civetweb?svg=true)](https://ci.appveyor.com/project/civetweb/civetweb/branch/master) - -Test coverage check ([coveralls](https://coveralls.io/github/civetweb/civetweb), [codecov](https://codecov.io/gh/civetweb/civetweb/branch/master)) (using different tools/settings): - -[![Coveralls](https://img.shields.io/coveralls/civetweb/civetweb.svg?maxAge=3600)]() -[![Coverage Status](https://coveralls.io/repos/github/civetweb/civetweb/badge.svg?branch=master)](https://coveralls.io/github/civetweb/civetweb?branch=master) -[![codecov](https://codecov.io/gh/civetweb/civetweb/branch/master/graph/badge.svg)](https://codecov.io/gh/civetweb/civetweb) - -Static source code analysis ([Coverity](https://scan.coverity.com/projects/5784)): [![Coverity Scan Build Status](https://scan.coverity.com/projects/5784/badge.svg)](https://scan.coverity.com/projects/5784) - -CodeQL semantic code analysis: [![CodeQL](https://github.com/civetweb/civetweb/workflows/CodeQL/badge.svg)](https://github.com/civetweb/civetweb/actions/workflows/codeql-analysis.yml) - - -Project Mission ------------------ - -Project mission is to provide easy to use, powerful, C (C/C++) embeddable web server with optional CGI, SSL and Lua support. -CivetWeb has a MIT license so you can innovate without restrictions. - -CivetWeb can be used by developers as a library, to add web server functionality to an existing application. - -It can also be used by end users as a stand-alone web server running on a Windows or Linux PC. It is available as single executable, no installation is required. - - -Where to find the official version? ------------------------------------ - -End users can download CivetWeb binaries / releases from here on GitHub [https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases) or SourceForge -[https://sourceforge.net/projects/civetweb/](https://sourceforge.net/projects/civetweb/) - -Developers can contribute to CivetWeb via GitHub -[https://github.com/civetweb/civetweb](https://github.com/civetweb/civetweb) - -Due to a [bug in Git for Windows V2.24](https://github.com/git-for-windows/git/issues/2435) -CivetWeb must be used with an earlier or later version (see also [here](https://github.com/civetweb/civetweb/issues/812)). - -Bugs and requests should be filed on GitHub -[https://github.com/civetweb/civetweb/issues](https://github.com/civetweb/civetweb/issues) - -New releases are announced on Google Groups -[https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb) - -Formerly some support question and discussion threads have been at [Google groups](https://groups.google.com/d/forum/civetweb). -Recent questions and discussions use [GitHub issues](https://github.com/civetweb/civetweb/issues). - -Source releases can be found on GitHub -[https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases) - -A very brief overview can be found on GitHub Pages -[https://civetweb.github.io/civetweb/](https://civetweb.github.io/civetweb/) - - -Quick start documentation --------------------------- - -- [docs/Installing.md](https://github.com/civetweb/civetweb/blob/master/docs/Installing.md) - Install Guide (for end users using pre-built binaries) -- [docs/UserManual.md](https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md) - End User Guide -- [docs/Building.md](https://github.com/civetweb/civetweb/blob/master/docs/Building.md) - Building the Server (quick start guide) -- [docs/Embedding.md](https://github.com/civetweb/civetweb/blob/master/docs/Embedding.md) - Embedding (how to add HTTP support to an existing application) -- [docs/OpenSSL.md](https://github.com/civetweb/civetweb/blob/master/docs/OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. -- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use CivetWeb in a Docker container. -- [API documentation](https://github.com/civetweb/civetweb/tree/master/docs/api) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). -- [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md) - Release Notes -- [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - Security Policy -- [LICENSE.md](https://github.com/civetweb/civetweb/blob/master/LICENSE.md) - Copyright License - - -Overview --------- - -CivetWeb keeps the balance between functionality and -simplicity by a carefully selected list of features: - -- Forked from [Mongoose](https://code.google.com/p/mongoose/) in 2013, before - it changed the licence from MIT to commercial + GPL. A lot of enhancements - have been added since then, see - [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md). -- Maintains the liberal, permissive, commercial-friendly, - [MIT license](https://en.wikipedia.org/wiki/MIT_License) -- Project is free from copy-left licenses, like GPL, because you should innovate without - restrictions. -- Works on Windows, Mac, Linux, UNIX, IOS, Android, Buildroot, and many - other platforms. -- Scripting and database support (CGI, Lua Server Pages, Server side Lua scripts, Lua SQLite database, - Server side JavaScript). - This provides a ready to go, powerful web development platform in a one - single-click executable with **no dependencies**. 0 -- Support for CGI, SSI, HTTP digest (MD5) authorization, WebSocket, WebDAV. -- Experimental HTTP/2 support. -- HTTPS (SSL/TLS) support using [OpenSSL](https://www.openssl.org/). -- Optional support for authentication using client side X.509 certificates. -- Resumed download, URL rewrite, file blacklist, IP-based ACL. -- Can run as a Windows service or systemd service. -- Download speed limit based on client subnet or URI pattern. -- Simple and clean embedding API. -- The source is in single file for drop in compilation. -- Embedding examples included. -- HTTP client capable of sending arbitrary HTTP/HTTPS requests. -- Websocket client functionality available (WS/WSS). - - -### Optionally included software - -[![Lua](/resources/lua-logo.jpg "Lua Logo")](https://lua.org) -[![LuaFileSystem](/resources/luafilesystem-logo.jpg "LuaFileSystem Logo")](https://keplerproject.github.io/luafilesystem/) -[![LuaSQLite3](/resources/luasqlite-logo.jpg "LuaSQLite3 Logo")](https://lua.sqlite.org/index.cgi/index) -[![Sqlite3](/resources/sqlite3-logo.jpg "Sqlite3 Logo")](https://sqlite.org) -[![LuaXML](/resources/luaxml-logo.jpg "LuaXML Logo")](https://github.com/n1tehawk/LuaXML) -[![Duktape](/resources/duktape-logo.png "Duktape Logo")](https://duktape.org) - - -### Optional dependencies - -[zlib](https://zlib.net) - -[OpenSSL](https://www.openssl.org/) - -[Mbed TLS](https://github.com/ARMmbed/mbedtls) - -[GNU TLS](https://gnutls.org) - - -Support -------- - -This project is very easy to install and use. -Please read the [documentation](https://github.com/civetweb/civetweb/blob/master/docs/) -and have a look at the [examples](https://github.com/civetweb/civetweb/blob/master/examples/). - -Recent questions and discussions usually use [GitHub issues](https://github.com/civetweb/civetweb/issues). -Some old information may be found on the [mailing list](https://groups.google.com/d/forum/civetweb), -but this information may be outdated. - -Feel free to create a GitHub issue for bugs, feature requests, questions, suggestions or if you want to share tips and tricks. -When creating an issues for a bug, add enough description to reproduce the issue - at least add CivetWeb version and operating system. -Please see also the guidelines for [Contributions](https://github.com/civetweb/civetweb/blob/master/docs/Contribution.md) and the [Security Policy](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - -Note: We do not take any liability or warranty for any linked contents. Visit these pages and try the community support suggestions at your own risk. -Any link provided in this project (including source and documentation) is provided in the hope that this information will be helpful. -However, we cannot accept any responsibility for any content on an external page. - - -Contributions -------------- - -Contributions are welcome provided all contributions carry the MIT license. - -DO NOT APPLY fixes copied from Mongoose to this project to prevent GPL tainting. -Since 2013, CivetWeb and Mongoose have been developed independently. -By now the code base differs, so patches cannot be safely transferred in either direction. - -Some guidelines can be found in [docs/Contribution.md](https://github.com/civetweb/civetweb/blob/master/docs/Contribution.md). - - -Authors -------- - -CivetWeb was forked from the last MIT version of Mongoose in August 2013. -Since then, CivetWeb has seen many improvements from various authors -(Copyright (c) 2013-2021 the CivetWeb developers, MIT license). - -A list of authors can be found in [CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md). - -CivetWeb is based on the [Mongoose project](https://github.com/cesanta/mongoose). The original author of Mongoose was -Sergey Lyubka(2004-2013) who released it under the MIT license. -However, on August 16, 2013, -[Mongoose was relicensed to a dual GPL V2 + commercial license](https://groups.google.com/forum/#!topic/mongoose-users/aafbOnHonkI) -and CiwetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose". -The license change and CivetWeb fork was mentioned on the Mongoose -[Wikipedia](https://en.wikipedia.org/wiki/Mongoose_(web_server)) -page as well, but it's getting deleted (and added again) there every -now and then. - -Using the CivetWeb project ensures the MIT licenses terms are applied and -GPL cannot be imposed on any of this code, as long as it is sourced from -here. This code will remain free with the MIT license protection. diff --git a/net/http/civetweb/civetweb.c b/net/http/civetweb/civetweb.c deleted file mode 100644 index c814caa7f2798..0000000000000 --- a/net/http/civetweb/civetweb.c +++ /dev/null @@ -1,23202 +0,0 @@ -/* Copyright (c) 2013-2024 the Civetweb developers - * Copyright (c) 2004-2013 Sergey Lyubka - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#if defined(__GNUC__) || defined(__MINGW32__) -#ifndef GCC_VERSION -#define GCC_VERSION \ - (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#endif -#if GCC_VERSION >= 40500 -/* gcc diagnostic pragmas available */ -#define GCC_DIAGNOSTIC -#endif -#endif - -#if defined(GCC_DIAGNOSTIC) -/* Disable unused macros warnings - not all defines are required - * for all systems and all compilers. */ -#pragma GCC diagnostic ignored "-Wunused-macros" -/* A padding warning is just plain useless */ -#pragma GCC diagnostic ignored "-Wpadded" -#endif - -#if defined(__clang__) /* GCC does not (yet) support this pragma */ -/* We must set some flags for the headers we include. These flags - * are reserved ids according to C99, so we need to disable a - * warning for that. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wreserved-id-macro" -#endif - -#if defined(_WIN32) -#if !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ -#endif -#if !defined(_WIN32_WINNT) /* Minimum API version */ -#define _WIN32_WINNT 0x0601 -#endif -#else -#if !defined(_GNU_SOURCE) -#define _GNU_SOURCE /* for setgroups(), pthread_setname_np() */ -#endif -#if defined(__linux__) && !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ -#endif -#if defined(__LSB_VERSION__) || defined(__sun) -#define NEED_TIMEGM -#define NO_THREAD_NAME -#endif -#if !defined(_LARGEFILE_SOURCE) -#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ -#endif -#if !defined(_FILE_OFFSET_BITS) -#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ -#endif -#if !defined(__STDC_FORMAT_MACROS) -#define __STDC_FORMAT_MACROS /* wants this for C++ */ -#endif -#if !defined(__STDC_LIMIT_MACROS) -#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ -#endif -#if !defined(_DARWIN_UNLIMITED_SELECT) -#define _DARWIN_UNLIMITED_SELECT -#endif -#if defined(__sun) -#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ -#define __inline inline /* not recognized on older compiler versions */ -#endif -#endif - -#if defined(__clang__) -/* Enable reserved-id-macro warning again. */ -#pragma GCC diagnostic pop -#endif - - -#if defined(USE_LUA) -#define USE_TIMERS -#endif - -#if defined(_MSC_VER) -/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */ -#pragma warning(disable : 4306) -/* conditional expression is constant: introduced by FD_SET(..) */ -#pragma warning(disable : 4127) -/* non-constant aggregate initializer: issued due to missing C99 support */ -#pragma warning(disable : 4204) -/* padding added after data member */ -#pragma warning(disable : 4820) -/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */ -#pragma warning(disable : 4668) -/* no function prototype given: converting '()' to '(void)' */ -#pragma warning(disable : 4255) -/* function has been selected for automatic inline expansion */ -#pragma warning(disable : 4711) -#endif - - -/* This code uses static_assert to check some conditions. - * Unfortunately some compilers still do not support it, so we have a - * replacement function here. */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201100L -#define mg_static_assert _Static_assert -#elif defined(__cplusplus) && __cplusplus >= 201103L -#define mg_static_assert static_assert -#else -char static_assert_replacement[1]; -#define mg_static_assert(cond, txt) \ - extern char static_assert_replacement[(cond) ? 1 : -1] -#endif - -mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8, - "int data type size check"); -mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8, - "pointer data type size check"); -mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check"); - - -/* Select queue implementation. Diagnosis features originally only implemented - * for the "ALTERNATIVE_QUEUE" have been ported to the previous queue - * implementation (NO_ALTERNATIVE_QUEUE) as well. The new configuration value - * "CONNECTION_QUEUE_SIZE" is only available for the previous queue - * implementation, since the queue length is independent from the number of - * worker threads there, while the new queue is one element per worker thread. - * - */ -#if defined(NO_ALTERNATIVE_QUEUE) && defined(ALTERNATIVE_QUEUE) -/* The queues are exclusive or - only one can be used. */ -#error \ - "Define ALTERNATIVE_QUEUE or NO_ALTERNATIVE_QUEUE (or none of them), but not both" -#endif -#if !defined(NO_ALTERNATIVE_QUEUE) && !defined(ALTERNATIVE_QUEUE) -/* Use a default implementation */ -#define NO_ALTERNATIVE_QUEUE -#endif - -#if defined(NO_FILESYSTEMS) && !defined(NO_FILES) -/* File system access: - * NO_FILES = do not serve any files from the file system automatically. - * However, with NO_FILES CivetWeb may still write log files, read access - * control files, default error page files or use API functions like - * mg_send_file in callbacks to send files from the server local - * file system. - * NO_FILES only disables the automatic mapping between URLs and local - * file names. - * NO_FILESYSTEM = do not access any file at all. Useful for embedded - * devices without file system. Logging to files in not available - * (use callbacks instead) and API functions like mg_send_file are not - * available. - * If NO_FILESYSTEM is set, NO_FILES must be set as well. - */ -#error "Inconsistent build flags, NO_FILESYSTEMS requires NO_FILES" -#endif - -/* DTL -- including winsock2.h works better if lean and mean */ -#if !defined(WIN32_LEAN_AND_MEAN) -#define WIN32_LEAN_AND_MEAN -#endif - -#if defined(__SYMBIAN32__) -/* According to https://en.wikipedia.org/wiki/Symbian#History, - * Symbian is no longer maintained since 2014-01-01. - * Support for Symbian has been removed from CivetWeb - */ -#error "Symbian is no longer maintained. CivetWeb no longer supports Symbian." -#endif /* __SYMBIAN32__ */ - -#if defined(__rtems__) -#include -#endif - -#if defined(__ZEPHYR__) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Max worker threads is the max of pthreads minus the main application thread - * and minus the main civetweb thread, thus -2 - */ -#define MAX_WORKER_THREADS (CONFIG_MAX_PTHREAD_COUNT - 2) - -#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) -#define ZEPHYR_STACK_SIZE USE_STACK_SIZE -#else -#define ZEPHYR_STACK_SIZE (1024 * 16) -#endif - -K_THREAD_STACK_DEFINE(civetweb_main_stack, ZEPHYR_STACK_SIZE); -K_THREAD_STACK_ARRAY_DEFINE(civetweb_worker_stacks, - MAX_WORKER_THREADS, - ZEPHYR_STACK_SIZE); - -static int zephyr_worker_stack_index; - -#endif - -#if !defined(CIVETWEB_HEADER_INCLUDED) -/* Include the header file here, so the CivetWeb interface is defined for the - * entire implementation, including the following forward definitions. */ -#include "civetweb.h" -#endif - -#if !defined(DEBUG_TRACE) -#if defined(DEBUG) -static void DEBUG_TRACE_FUNC(const char *func, - unsigned line, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(3, 4); - -#define DEBUG_TRACE(fmt, ...) \ - DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) - -#define NEED_DEBUG_TRACE_FUNC -#if !defined(DEBUG_TRACE_STREAM) -#define DEBUG_TRACE_STREAM stderr -#endif - -#else -#define DEBUG_TRACE(fmt, ...) \ - do { \ - } while (0) -#endif /* DEBUG */ -#endif /* DEBUG_TRACE */ - - -#if !defined(DEBUG_ASSERT) -#if defined(DEBUG) -#include -#define DEBUG_ASSERT(cond) \ - do { \ - if (!(cond)) { \ - DEBUG_TRACE("ASSERTION FAILED: %s", #cond); \ - exit(2); /* Exit with error */ \ - } \ - } while (0) -#else -#define DEBUG_ASSERT(cond) -#endif /* DEBUG */ -#endif - - -#if defined(__GNUC__) && defined(GCC_INSTRUMENTATION) -void __cyg_profile_func_enter(void *this_fn, void *call_site) - __attribute__((no_instrument_function)); - -void __cyg_profile_func_exit(void *this_fn, void *call_site) - __attribute__((no_instrument_function)); - -void -__cyg_profile_func_enter(void *this_fn, void *call_site) -{ - if ((void *)this_fn != (void *)printf) { - printf("E %p %p\n", this_fn, call_site); - } -} - -void -__cyg_profile_func_exit(void *this_fn, void *call_site) -{ - if ((void *)this_fn != (void *)printf) { - printf("X %p %p\n", this_fn, call_site); - } -} -#endif - - -#if !defined(IGNORE_UNUSED_RESULT) -#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1)) -#endif - - -#if defined(__GNUC__) || defined(__MINGW32__) - -/* GCC unused function attribute seems fundamentally broken. - * Several attempts to tell the compiler "THIS FUNCTION MAY BE USED - * OR UNUSED" for individual functions failed. - * Either the compiler creates an "unused-function" warning if a - * function is not marked with __attribute__((unused)). - * On the other hand, if the function is marked with this attribute, - * but is used, the compiler raises a completely idiotic - * "used-but-marked-unused" warning - and - * #pragma GCC diagnostic ignored "-Wused-but-marked-unused" - * raises error: unknown option after "#pragma GCC diagnostic". - * Disable this warning completely, until the GCC guys sober up - * again. - */ - -#pragma GCC diagnostic ignored "-Wunused-function" - -#define FUNCTION_MAY_BE_UNUSED /* __attribute__((unused)) */ - -#else -#define FUNCTION_MAY_BE_UNUSED -#endif - - -/* Some ANSI #includes are not available on Windows CE and Zephyr */ -#if !defined(_WIN32_WCE) && !defined(__ZEPHYR__) -#include -#include -#include -#include -#include -#include -#endif /* !_WIN32_WCE */ - - -#if defined(__clang__) -/* When using -Weverything, clang does not accept it's own headers - * in a release build configuration. Disable what is too much in - * -Weverything. */ -#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" -#endif - -#if defined(__GNUC__) || defined(__MINGW32__) -/* Who on earth came to the conclusion, using __DATE__ should rise - * an "expansion of date or time macro is not reproducible" - * warning. That's exactly what was intended by using this macro. - * Just disable this nonsense warning. */ - -/* And disabling them does not work either: - * #pragma clang diagnostic ignored "-Wno-error=date-time" - * #pragma clang diagnostic ignored "-Wdate-time" - * So we just have to disable ALL warnings for some lines - * of code. - * This seems to be a known GCC bug, not resolved since 2012: - * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431 - */ -#endif - - -#if defined(__MACH__) && defined(__APPLE__) /* Apple OSX section */ - -#if defined(__clang__) -#if (__clang_major__ == 3) && ((__clang_minor__ == 7) || (__clang_minor__ == 8)) -/* Avoid warnings for Xcode 7. It seems it does no longer exist in Xcode 8 */ -#pragma clang diagnostic ignored "-Wno-reserved-id-macro" -#pragma clang diagnostic ignored "-Wno-keyword-macro" -#endif -#endif - -#ifndef CLOCK_MONOTONIC -#define CLOCK_MONOTONIC (1) -#endif -#ifndef CLOCK_REALTIME -#define CLOCK_REALTIME (2) -#endif - -#include -#include -#include -#include -#include - -/* clock_gettime is not implemented on OSX prior to 10.12 */ -static int -_civet_clock_gettime(int clk_id, struct timespec *t) -{ - memset(t, 0, sizeof(*t)); - if (clk_id == CLOCK_REALTIME) { - struct timeval now; - int rv = gettimeofday(&now, NULL); - if (rv) { - return rv; - } - t->tv_sec = now.tv_sec; - t->tv_nsec = now.tv_usec * 1000; - return 0; - - } else if (clk_id == CLOCK_MONOTONIC) { - static uint64_t clock_start_time = 0; - static mach_timebase_info_data_t timebase_ifo = {0, 0}; - - uint64_t now = mach_absolute_time(); - - if (clock_start_time == 0) { - kern_return_t mach_status = mach_timebase_info(&timebase_ifo); - DEBUG_ASSERT(mach_status == KERN_SUCCESS); - - /* appease "unused variable" warning for release builds */ - (void)mach_status; - - clock_start_time = now; - } - - now = (uint64_t)((double)(now - clock_start_time) - * (double)timebase_ifo.numer - / (double)timebase_ifo.denom); - - t->tv_sec = now / 1000000000; - t->tv_nsec = now % 1000000000; - return 0; - } - return -1; /* EINVAL - Clock ID is unknown */ -} - -/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */ -#if defined(__CLOCK_AVAILABILITY) -/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be - * declared but it may be NULL at runtime. So we need to check before using - * it. */ -static int -_civet_safe_clock_gettime(int clk_id, struct timespec *t) -{ - if (clock_gettime) { - return clock_gettime(clk_id, t); - } - return _civet_clock_gettime(clk_id, t); -} -#define clock_gettime _civet_safe_clock_gettime -#else -#define clock_gettime _civet_clock_gettime -#endif - -#endif - - -#if defined(_WIN32) -#define ERROR_TRY_AGAIN(err) ((err) == WSAEWOULDBLOCK) -#else -/* Unix might return different error codes indicating to try again. - * For Linux EAGAIN==EWOULDBLOCK, maybe EAGAIN!=EWOULDBLOCK is history from - * decades ago, but better check both and let the compiler optimize it. */ -#define ERROR_TRY_AGAIN(err) \ - (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR)) -#endif - -#if defined(USE_ZLIB) -#include "zconf.h" -#include "zlib.h" -#endif - - -/********************************************************************/ -/* CivetWeb configuration defines */ -/********************************************************************/ - -/* Maximum number of threads that can be configured. - * The number of threads actually created depends on the "num_threads" - * configuration parameter, but this is the upper limit. */ -#if !defined(MAX_WORKER_THREADS) -#define MAX_WORKER_THREADS (1024 * 64) /* in threads (count) */ -#endif - -/* Timeout interval for select/poll calls. - * The timeouts depend on "*_timeout_ms" configuration values, but long - * timeouts are split into timouts as small as SOCKET_TIMEOUT_QUANTUM. - * This reduces the time required to stop the server. */ -#if !defined(SOCKET_TIMEOUT_QUANTUM) -#define SOCKET_TIMEOUT_QUANTUM (2000) /* in ms */ -#endif - -/* Do not try to compress files smaller than this limit. */ -#if !defined(MG_FILE_COMPRESSION_SIZE_LIMIT) -#define MG_FILE_COMPRESSION_SIZE_LIMIT (1024) /* in bytes */ -#endif - -#if !defined(PASSWORDS_FILE_NAME) -#define PASSWORDS_FILE_NAME ".htpasswd" -#endif - -/* Initial buffer size for all CGI environment variables. In case there is - * not enough space, another block is allocated. */ -#if !defined(CGI_ENVIRONMENT_SIZE) -#define CGI_ENVIRONMENT_SIZE (4096) /* in bytes */ -#endif - -/* Maximum number of environment variables. */ -#if !defined(MAX_CGI_ENVIR_VARS) -#define MAX_CGI_ENVIR_VARS (256) /* in variables (count) */ -#endif - -/* General purpose buffer size. */ -#if !defined(MG_BUF_LEN) /* in bytes */ -#define MG_BUF_LEN (1024 * 8) -#endif - - -/********************************************************************/ - -/* Helper macros */ -#if !defined(ARRAY_SIZE) -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -#endif - -#include - -/* Standard defines */ -#if !defined(INT64_MAX) -#define INT64_MAX (9223372036854775807) -#endif - -#define SHUTDOWN_RD (0) -#define SHUTDOWN_WR (1) -#define SHUTDOWN_BOTH (2) - -mg_static_assert(MAX_WORKER_THREADS >= 1, - "worker threads must be a positive number"); - -mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, - "size_t data type size check"); - - -#if defined(_WIN32) /* WINDOWS include block */ -#include /* *alloc( */ -#include /* *alloc( */ -#include /* struct timespec */ -#include -#include /* DTL add for SO_EXCLUSIVE */ -#include - -typedef const char *SOCK_OPT_TYPE; - -/* For a detailed description of these *_PATH_MAX defines, see - * https://github.com/civetweb/civetweb/issues/937. */ - -/* UTF8_PATH_MAX is a char buffer size for 259 BMP characters in UTF-8 plus - * null termination, rounded up to the next 4 bytes boundary */ -#define UTF8_PATH_MAX (3 * 260) -/* UTF16_PATH_MAX is the 16-bit wchar_t buffer size required for 259 BMP - * characters plus termination. (Note: wchar_t is 16 bit on Windows) */ -#define UTF16_PATH_MAX (260) - -#if !defined(_IN_PORT_T) -#if !defined(in_port_t) -#define in_port_t u_short -#endif -#endif - -#if defined(_WIN32_WCE) -#error "WinCE support has ended" -#endif - -#include -#include -#include - - -#define MAKEUQUAD(lo, hi) \ - ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32)) -#define RATE_DIFF (10000000) /* 100 nsecs */ -#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de)) -#define SYS2UNIX_TIME(lo, hi) \ - ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)) - -/* Visual Studio 6 does not know __func__ or __FUNCTION__ - * The rest of MS compilers use __FUNCTION__, not C99 __func__ - * Also use _strtoui64 on modern M$ compilers */ -#if defined(_MSC_VER) -#if (_MSC_VER < 1300) -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ __FILE__ ":" STR(__LINE__) -#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x)) -#define strtoll(x, y, z) (_atoi64(x)) -#else -#define __func__ __FUNCTION__ -#define strtoull(x, y, z) (_strtoui64(x, y, z)) -#define strtoll(x, y, z) (_strtoi64(x, y, z)) -#endif -#endif /* _MSC_VER */ - - -#define ERRNO ((int)(GetLastError())) -#define NO_SOCKLEN_T - - -#if defined(_WIN64) || defined(__MINGW64__) -#if !defined(SSL_LIB) - -#if defined(OPENSSL_API_3_0) -#define SSL_LIB "libssl-3-x64.dll" -#define CRYPTO_LIB "libcrypto-3-x64.dll" -#endif - -#if defined(OPENSSL_API_1_1) -#define SSL_LIB "libssl-1_1-x64.dll" -#define CRYPTO_LIB "libcrypto-1_1-x64.dll" -#endif /* OPENSSL_API_1_1 */ - -#if defined(OPENSSL_API_1_0) -#define SSL_LIB "ssleay64.dll" -#define CRYPTO_LIB "libeay64.dll" -#endif /* OPENSSL_API_1_0 */ - -#endif -#else /* defined(_WIN64) || defined(__MINGW64__) */ -#if !defined(SSL_LIB) - -#if defined(OPENSSL_API_3_0) -#define SSL_LIB "libssl-3.dll" -#define CRYPTO_LIB "libcrypto-3.dll" -#endif - -#if defined(OPENSSL_API_1_1) -#define SSL_LIB "libssl-1_1.dll" -#define CRYPTO_LIB "libcrypto-1_1.dll" -#endif /* OPENSSL_API_1_1 */ - -#if defined(OPENSSL_API_1_0) -#define SSL_LIB "ssleay32.dll" -#define CRYPTO_LIB "libeay32.dll" -#endif /* OPENSSL_API_1_0 */ - -#endif /* SSL_LIB */ -#endif /* defined(_WIN64) || defined(__MINGW64__) */ - - -#define O_NONBLOCK (0) -#if !defined(W_OK) -#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */ -#endif -#define _POSIX_ -#define INT64_FMT "I64d" -#define UINT64_FMT "I64u" - -#define WINCDECL __cdecl -#define vsnprintf_impl _vsnprintf -#define access _access -#define mg_sleep(x) (Sleep(x)) - -#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY) -#if !defined(popen) -#define popen(x, y) (_popen(x, y)) -#endif -#if !defined(pclose) -#define pclose(x) (_pclose(x)) -#endif -#define close(x) (_close(x)) -#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y))) -#define RTLD_LAZY (0) -#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0) -#define fdopen(x, y) (_fdopen((x), (y))) -#define write(x, y, z) (_write((x), (y), (unsigned)z)) -#define read(x, y, z) (_read((x), (y), (unsigned)z)) -#define flockfile(x) ((void)pthread_mutex_lock(&global_log_file_lock)) -#define funlockfile(x) ((void)pthread_mutex_unlock(&global_log_file_lock)) -#define sleep(x) (Sleep((x)*1000)) -#define rmdir(x) (_rmdir(x)) -#if defined(_WIN64) || !defined(__MINGW32__) -/* Only MinGW 32 bit is missing this function */ -#define timegm(x) (_mkgmtime(x)) -#else -time_t timegm(struct tm *tm); -#define NEED_TIMEGM -#endif - - -#if !defined(fileno) -#define fileno(x) (_fileno(x)) -#endif /* !fileno MINGW #defines fileno */ - -typedef struct { - CRITICAL_SECTION sec; /* Immovable */ -} pthread_mutex_t; -typedef DWORD pthread_key_t; -typedef HANDLE pthread_t; -typedef struct { - pthread_mutex_t threadIdSec; - struct mg_workerTLS *waiting_thread; /* The chain of threads */ -} pthread_cond_t; - -#if !defined(__clockid_t_defined) -typedef DWORD clockid_t; -#endif -#if !defined(CLOCK_MONOTONIC) -#define CLOCK_MONOTONIC (1) -#endif -#if !defined(CLOCK_REALTIME) -#define CLOCK_REALTIME (2) -#endif -#if !defined(CLOCK_THREAD) -#define CLOCK_THREAD (3) -#endif -#if !defined(CLOCK_PROCESS) -#define CLOCK_PROCESS (4) -#endif - - -#if defined(_MSC_VER) && (_MSC_VER >= 1900) -#define _TIMESPEC_DEFINED -#endif -#if !defined(_TIMESPEC_DEFINED) -struct timespec { - time_t tv_sec; /* seconds */ - long tv_nsec; /* nanoseconds */ -}; -#endif - -#if !defined(WIN_PTHREADS_TIME_H) -#define MUST_IMPLEMENT_CLOCK_GETTIME -#endif - -#if defined(MUST_IMPLEMENT_CLOCK_GETTIME) -#define clock_gettime mg_clock_gettime -static int -clock_gettime(clockid_t clk_id, struct timespec *tp) -{ - FILETIME ft; - ULARGE_INTEGER li, li2; - BOOL ok = FALSE; - double d; - static double perfcnt_per_sec = 0.0; - static BOOL initialized = FALSE; - - if (!initialized) { - QueryPerformanceFrequency((LARGE_INTEGER *)&li); - perfcnt_per_sec = 1.0 / li.QuadPart; - initialized = TRUE; - } - - if (tp) { - memset(tp, 0, sizeof(*tp)); - - if (clk_id == CLOCK_REALTIME) { - - /* BEGIN: CLOCK_REALTIME = wall clock (date and time) */ - GetSystemTimeAsFileTime(&ft); - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */ - tp->tv_sec = (time_t)(li.QuadPart / 10000000); - tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; - ok = TRUE; - /* END: CLOCK_REALTIME */ - - } else if (clk_id == CLOCK_MONOTONIC) { - - /* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */ - QueryPerformanceCounter((LARGE_INTEGER *)&li); - d = li.QuadPart * perfcnt_per_sec; - tp->tv_sec = (time_t)d; - d -= (double)tp->tv_sec; - tp->tv_nsec = (long)(d * 1.0E9); - ok = TRUE; - /* END: CLOCK_MONOTONIC */ - - } else if (clk_id == CLOCK_THREAD) { - - /* BEGIN: CLOCK_THREAD = CPU usage of thread */ - FILETIME t_create, t_exit, t_kernel, t_user; - if (GetThreadTimes(GetCurrentThread(), - &t_create, - &t_exit, - &t_kernel, - &t_user)) { - li.LowPart = t_user.dwLowDateTime; - li.HighPart = t_user.dwHighDateTime; - li2.LowPart = t_kernel.dwLowDateTime; - li2.HighPart = t_kernel.dwHighDateTime; - li.QuadPart += li2.QuadPart; - tp->tv_sec = (time_t)(li.QuadPart / 10000000); - tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; - ok = TRUE; - } - /* END: CLOCK_THREAD */ - - } else if (clk_id == CLOCK_PROCESS) { - - /* BEGIN: CLOCK_PROCESS = CPU usage of process */ - FILETIME t_create, t_exit, t_kernel, t_user; - if (GetProcessTimes(GetCurrentProcess(), - &t_create, - &t_exit, - &t_kernel, - &t_user)) { - li.LowPart = t_user.dwLowDateTime; - li.HighPart = t_user.dwHighDateTime; - li2.LowPart = t_kernel.dwLowDateTime; - li2.HighPart = t_kernel.dwHighDateTime; - li.QuadPart += li2.QuadPart; - tp->tv_sec = (time_t)(li.QuadPart / 10000000); - tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; - ok = TRUE; - } - /* END: CLOCK_PROCESS */ - - } else { - - /* BEGIN: unknown clock */ - /* ok = FALSE; already set by init */ - /* END: unknown clock */ - } - } - - return ok ? 0 : -1; -} -#endif - - -#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ - -static int pthread_mutex_lock(pthread_mutex_t *); -static int pthread_mutex_unlock(pthread_mutex_t *); -static void path_to_unicode(const struct mg_connection *conn, - const char *path, - wchar_t *wbuf, - size_t wbuf_len); - -/* All file operations need to be rewritten to solve #246. */ - -struct mg_file; - -static const char *mg_fgets(char *buf, size_t size, struct mg_file *filep); - - -/* POSIX dirent interface */ -struct dirent { - char d_name[UTF8_PATH_MAX]; -}; - -typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; - -#if defined(HAVE_POLL) -#define mg_pollfd pollfd -#else -struct mg_pollfd { - SOCKET fd; - short events; - short revents; -}; -#endif - -/* Mark required libraries */ -#if defined(_MSC_VER) -#pragma comment(lib, "Ws2_32.lib") -#endif - -#else /* defined(_WIN32) - WINDOWS vs UNIX include block */ - -#include - -/* Linux & co. internally use UTF8 */ -#define UTF8_PATH_MAX (PATH_MAX) - -typedef const void *SOCK_OPT_TYPE; - -#if defined(ANDROID) -typedef unsigned short int in_port_t; -#endif - -#if !defined(__ZEPHYR__) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if !defined(__rtems__) -#include -#endif -#include -#include -#include -#if defined(USE_X_DOM_SOCKET) -#include -#endif -#endif - -#define vsnprintf_impl vsnprintf - -#if !defined(NO_SSL_DL) && !defined(NO_SSL) -#include -#endif - -#if defined(__MACH__) && defined(__APPLE__) - -#if defined(OPENSSL_API_3_0) -#define SSL_LIB "libssl.3.dylib" -#define CRYPTO_LIB "libcrypto.3.dylib" -#endif - -#if defined(OPENSSL_API_1_1) -#define SSL_LIB "libssl.1.1.dylib" -#define CRYPTO_LIB "libcrypto.1.1.dylib" -#endif /* OPENSSL_API_1_1 */ - -#if defined(OPENSSL_API_1_0) -#define SSL_LIB "libssl.1.0.dylib" -#define CRYPTO_LIB "libcrypto.1.0.dylib" -#endif /* OPENSSL_API_1_0 */ - -#else -#if !defined(SSL_LIB) -#define SSL_LIB "libssl.so" -#endif -#if !defined(CRYPTO_LIB) -#define CRYPTO_LIB "libcrypto.so" -#endif -#endif -#if !defined(O_BINARY) -#define O_BINARY (0) -#endif /* O_BINARY */ -#define closesocket(a) (close(a)) -#define mg_mkdir(conn, path, mode) (mkdir(path, mode)) -#define mg_remove(conn, x) (remove(x)) -#define mg_sleep(x) (usleep((x)*1000)) -#define mg_opendir(conn, x) (opendir(x)) -#define mg_closedir(x) (closedir(x)) -#define mg_readdir(x) (readdir(x)) -#define ERRNO (errno) -#define INVALID_SOCKET (-1) -#define INT64_FMT PRId64 -#define UINT64_FMT PRIu64 -typedef int SOCKET; -#define WINCDECL - -#if defined(__hpux) -/* HPUX 11 does not have monotonic, fall back to realtime */ -#if !defined(CLOCK_MONOTONIC) -#define CLOCK_MONOTONIC CLOCK_REALTIME -#endif - -/* HPUX defines socklen_t incorrectly as size_t which is 64bit on - * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED - * the prototypes use int* rather than socklen_t* which matches the - * actual library expectation. When called with the wrong size arg - * accept() returns a zero client inet addr and check_acl() always - * fails. Since socklen_t is widely used below, just force replace - * their typedef with int. - DTL - */ -#define socklen_t int -#endif /* hpux */ - -#define mg_pollfd pollfd - -#endif /* defined(_WIN32) - WINDOWS vs UNIX include block */ - -/* In case our C library is missing "timegm", provide an implementation */ -#if defined(NEED_TIMEGM) -static inline int -is_leap(int y) -{ - return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; -} - -static inline int -count_leap(int y) -{ - return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400; -} - -static time_t -timegm(struct tm *tm) -{ - static const unsigned short ydays[] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; - int year = tm->tm_year + 1900; - int mon = tm->tm_mon; - int mday = tm->tm_mday - 1; - int hour = tm->tm_hour; - int min = tm->tm_min; - int sec = tm->tm_sec; - - if (year < 1970 || mon < 0 || mon > 11 || mday < 0 - || (mday >= ydays[mon + 1] - ydays[mon] - + (mon == 1 && is_leap(year) ? 1 : 0)) - || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60) - return -1; - - time_t res = year - 1970; - res *= 365; - res += mday; - res += ydays[mon] + (mon > 1 && is_leap(year) ? 1 : 0); - res += count_leap(year); - - res *= 24; - res += hour; - res *= 60; - res += min; - res *= 60; - res += sec; - return res; -} -#endif /* NEED_TIMEGM */ - - -/* va_copy should always be a macro, C99 and C++11 - DTL */ -#if !defined(va_copy) -#define va_copy(x, y) ((x) = (y)) -#endif - - -#if defined(_WIN32) -/* Create substitutes for POSIX functions in Win32. */ - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif - - -static pthread_mutex_t global_log_file_lock; - -FUNCTION_MAY_BE_UNUSED -static DWORD -pthread_self(void) -{ - return GetCurrentThreadId(); -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_key_create( - pthread_key_t *key, - void (*_ignored)(void *) /* destructor not supported for Windows */ -) -{ - (void)_ignored; - - if ((key != 0)) { - *key = TlsAlloc(); - return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1; - } - return -2; -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_key_delete(pthread_key_t key) -{ - return TlsFree(key) ? 0 : 1; -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_setspecific(pthread_key_t key, void *value) -{ - return TlsSetValue(key, value) ? 0 : 1; -} - - -FUNCTION_MAY_BE_UNUSED -static void * -pthread_getspecific(pthread_key_t key) -{ - return TlsGetValue(key); -} - -#if defined(GCC_DIAGNOSTIC) -/* Enable unused function warning again */ -#pragma GCC diagnostic pop -#endif - -static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL; -#else -static pthread_mutexattr_t pthread_mutex_attr; -#endif /* _WIN32 */ - - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif /* defined(GCC_DIAGNOSTIC) */ -#if defined(__clang__) -/* Show no warning in case system functions are not used. */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -#endif - -static pthread_mutex_t global_lock_mutex; - - -FUNCTION_MAY_BE_UNUSED -static void -mg_global_lock(void) -{ - (void)pthread_mutex_lock(&global_lock_mutex); -} - - -FUNCTION_MAY_BE_UNUSED -static void -mg_global_unlock(void) -{ - (void)pthread_mutex_unlock(&global_lock_mutex); -} - - -#if defined(_WIN64) -mg_static_assert(SIZE_MAX == 0xFFFFFFFFFFFFFFFFu, "Mismatch for atomic types"); -#elif defined(_WIN32) -mg_static_assert(SIZE_MAX == 0xFFFFFFFFu, "Mismatch for atomic types"); -#endif - - -/* Atomic functions working on ptrdiff_t ("signed size_t"). - * Operations: Increment, Decrement, Add, Maximum. - * Up to size_t, they do not an atomic "load" operation. - */ -FUNCTION_MAY_BE_UNUSED -static ptrdiff_t -mg_atomic_inc(volatile ptrdiff_t *addr) -{ - ptrdiff_t ret; - -#if defined(_WIN64) && !defined(NO_ATOMICS) - ret = InterlockedIncrement64(addr); -#elif defined(_WIN32) && !defined(NO_ATOMICS) -#ifdef __cplusplus - /* For C++ the Microsoft Visual Studio compiler can not decide what - * overloaded function prototpye in the SDC corresponds to "ptrdiff_t". */ - static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch"); - static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch"); - ret = InterlockedIncrement((LONG *)addr); -#else - ret = InterlockedIncrement(addr); -#endif -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - ret = __sync_add_and_fetch(addr, 1); -#else - mg_global_lock(); - ret = (++(*addr)); - mg_global_unlock(); -#endif - return ret; -} - - -FUNCTION_MAY_BE_UNUSED -static ptrdiff_t -mg_atomic_dec(volatile ptrdiff_t *addr) -{ - ptrdiff_t ret; - -#if defined(_WIN64) && !defined(NO_ATOMICS) - ret = InterlockedDecrement64(addr); -#elif defined(_WIN32) && !defined(NO_ATOMICS) -#ifdef __cplusplus - /* see mg_atomic_inc */ - static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch"); - static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch"); - ret = InterlockedDecrement((LONG *)addr); -#else - ret = InterlockedDecrement(addr); -#endif -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - ret = __sync_sub_and_fetch(addr, 1); -#else - mg_global_lock(); - ret = (--(*addr)); - mg_global_unlock(); -#endif - return ret; -} - - -#if defined(USE_SERVER_STATS) || defined(STOP_FLAG_NEEDS_LOCK) -static ptrdiff_t -mg_atomic_add(volatile ptrdiff_t *addr, ptrdiff_t value) -{ - ptrdiff_t ret; - -#if defined(_WIN64) && !defined(NO_ATOMICS) - ret = InterlockedAdd64(addr, value); -#elif defined(_WIN32) && !defined(NO_ATOMICS) - ret = InterlockedExchangeAdd(addr, value) + value; -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - ret = __sync_add_and_fetch(addr, value); -#else - mg_global_lock(); - *addr += value; - ret = (*addr); - mg_global_unlock(); -#endif - return ret; -} - - -FUNCTION_MAY_BE_UNUSED -static ptrdiff_t -mg_atomic_compare_and_swap(volatile ptrdiff_t *addr, - ptrdiff_t oldval, - ptrdiff_t newval) -{ - ptrdiff_t ret; - -#if defined(_WIN64) && !defined(NO_ATOMICS) - ret = InterlockedCompareExchange64(addr, newval, oldval); -#elif defined(_WIN32) && !defined(NO_ATOMICS) - ret = InterlockedCompareExchange(addr, newval, oldval); -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - ret = __sync_val_compare_and_swap(addr, oldval, newval); -#else - mg_global_lock(); - ret = *addr; - if ((ret != newval) && (ret == oldval)) { - *addr = newval; - } - mg_global_unlock(); -#endif - return ret; -} - - -static void -mg_atomic_max(volatile ptrdiff_t *addr, ptrdiff_t value) -{ - register ptrdiff_t tmp = *addr; - -#if defined(_WIN64) && !defined(NO_ATOMICS) - while (tmp < value) { - tmp = InterlockedCompareExchange64(addr, value, tmp); - } -#elif defined(_WIN32) && !defined(NO_ATOMICS) - while (tmp < value) { - tmp = InterlockedCompareExchange(addr, value, tmp); - } -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - while (tmp < value) { - tmp = __sync_val_compare_and_swap(addr, tmp, value); - } -#else - mg_global_lock(); - if (*addr < value) { - *addr = value; - } - mg_global_unlock(); -#endif -} - - -static int64_t -mg_atomic_add64(volatile int64_t *addr, int64_t value) -{ - int64_t ret; - -#if defined(_WIN64) && !defined(NO_ATOMICS) - ret = InterlockedAdd64(addr, value); -#elif defined(_WIN32) && !defined(NO_ATOMICS) - ret = InterlockedExchangeAdd64(addr, value) + value; -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - ret = __sync_add_and_fetch(addr, value); -#else - mg_global_lock(); - *addr += value; - ret = (*addr); - mg_global_unlock(); -#endif - return ret; -} -#endif - - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic pop -#endif /* defined(GCC_DIAGNOSTIC) */ -#if defined(__clang__) -/* Show no warning in case system functions are not used. */ -#pragma clang diagnostic pop -#endif - - -#if defined(USE_SERVER_STATS) - -struct mg_memory_stat { - volatile ptrdiff_t totalMemUsed; - volatile ptrdiff_t maxMemUsed; - volatile ptrdiff_t blockCount; -}; - - -static struct mg_memory_stat *get_memory_stat(struct mg_context *ctx); - - -static void * -mg_malloc_ex(size_t size, - struct mg_context *ctx, - const char *file, - unsigned line) -{ - void *data = malloc(size + 2 * sizeof(uintptr_t)); - void *memory = 0; - struct mg_memory_stat *mstat = get_memory_stat(ctx); - -#if defined(MEMORY_DEBUGGING) - char mallocStr[256]; -#else - (void)file; - (void)line; -#endif - - if (data) { - uintptr_t *tmp = (uintptr_t *)data; - ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size); - mg_atomic_max(&mstat->maxMemUsed, mmem); - mg_atomic_inc(&mstat->blockCount); - tmp[0] = size; - tmp[1] = (uintptr_t)mstat; - memory = (void *)&tmp[2]; - } - -#if defined(MEMORY_DEBUGGING) - sprintf(mallocStr, - "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", - memory, - (unsigned long)size, - (unsigned long)mstat->totalMemUsed, - (unsigned long)mstat->blockCount, - file, - line); - DEBUG_TRACE("%s", mallocStr); -#endif - - return memory; -} - - -static void * -mg_calloc_ex(size_t count, - size_t size, - struct mg_context *ctx, - const char *file, - unsigned line) -{ - void *data = mg_malloc_ex(size * count, ctx, file, line); - - if (data) { - memset(data, 0, size * count); - } - return data; -} - - -static void -mg_free_ex(void *memory, const char *file, unsigned line) -{ -#if defined(MEMORY_DEBUGGING) - char mallocStr[256]; -#else - (void)file; - (void)line; -#endif - - if (memory) { - void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t)); - uintptr_t size = ((uintptr_t *)data)[0]; - struct mg_memory_stat *mstat = - (struct mg_memory_stat *)(((uintptr_t *)data)[1]); - mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)size); - mg_atomic_dec(&mstat->blockCount); - -#if defined(MEMORY_DEBUGGING) - sprintf(mallocStr, - "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", - memory, - (unsigned long)size, - (unsigned long)mstat->totalMemUsed, - (unsigned long)mstat->blockCount, - file, - line); - DEBUG_TRACE("%s", mallocStr); -#endif - free(data); - } -} - - -static void * -mg_realloc_ex(void *memory, - size_t newsize, - struct mg_context *ctx, - const char *file, - unsigned line) -{ - void *data; - void *_realloc; - uintptr_t oldsize; - -#if defined(MEMORY_DEBUGGING) - char mallocStr[256]; -#else - (void)file; - (void)line; -#endif - - if (newsize) { - if (memory) { - /* Reallocate existing block */ - struct mg_memory_stat *mstat; - data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t)); - oldsize = ((uintptr_t *)data)[0]; - mstat = (struct mg_memory_stat *)((uintptr_t *)data)[1]; - _realloc = realloc(data, newsize + 2 * sizeof(uintptr_t)); - if (_realloc) { - data = _realloc; - mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)oldsize); -#if defined(MEMORY_DEBUGGING) - sprintf(mallocStr, - "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", - memory, - (unsigned long)oldsize, - (unsigned long)mstat->totalMemUsed, - (unsigned long)mstat->blockCount, - file, - line); - DEBUG_TRACE("%s", mallocStr); -#endif - mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)newsize); - -#if defined(MEMORY_DEBUGGING) - sprintf(mallocStr, - "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", - memory, - (unsigned long)newsize, - (unsigned long)mstat->totalMemUsed, - (unsigned long)mstat->blockCount, - file, - line); - DEBUG_TRACE("%s", mallocStr); -#endif - *(uintptr_t *)data = newsize; - data = (void *)(((char *)data) + 2 * sizeof(uintptr_t)); - } else { -#if defined(MEMORY_DEBUGGING) - DEBUG_TRACE("%s", "MEM: realloc failed\n"); -#endif - return _realloc; - } - } else { - /* Allocate new block */ - data = mg_malloc_ex(newsize, ctx, file, line); - } - } else { - /* Free existing block */ - data = 0; - mg_free_ex(memory, file, line); - } - - return data; -} - - -#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__) -#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__) -#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__) -#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) - -#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__) -#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__) -#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__) - - -#else /* USE_SERVER_STATS */ - - -static __inline void * -mg_malloc(size_t a) -{ - return malloc(a); -} - -static __inline void * -mg_calloc(size_t a, size_t b) -{ - return calloc(a, b); -} - -static __inline void * -mg_realloc(void *a, size_t b) -{ - return realloc(a, b); -} - -static __inline void -mg_free(void *a) -{ - free(a); -} - -#define mg_malloc_ctx(a, c) mg_malloc(a) -#define mg_calloc_ctx(a, b, c) mg_calloc(a, b) -#define mg_realloc_ctx(a, b, c) mg_realloc(a, b) -#define mg_free_ctx(a, c) mg_free(a) - -#endif /* USE_SERVER_STATS */ - - -static void mg_vsnprintf(const struct mg_connection *conn, - int *truncated, - char *buf, - size_t buflen, - const char *fmt, - va_list ap); - -static void mg_snprintf(const struct mg_connection *conn, - int *truncated, - char *buf, - size_t buflen, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(5, 6); - -/* This following lines are just meant as a reminder to use the mg-functions - * for memory management */ -#if defined(malloc) -#undef malloc -#endif -#if defined(calloc) -#undef calloc -#endif -#if defined(realloc) -#undef realloc -#endif -#if defined(free) -#undef free -#endif -#if defined(snprintf) -#undef snprintf -#endif -#if defined(vsnprintf) -#undef vsnprintf -#endif -#if !defined(NDEBUG) -#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc -#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc -#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc -#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free -#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf -#endif -#if defined(_WIN32) -/* vsnprintf must not be used in any system, - * but this define only works well for Windows. */ -#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf -#endif - - -/* mg_init_library counter */ -static int mg_init_library_called = 0; - -#if !defined(NO_SSL) -#if defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \ - || defined(OPENSSL_API_3_0) -static int mg_openssl_initialized = 0; -#endif -#if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1) \ - && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS) \ - && !defined(USE_GNUTLS) -#error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS" -#endif -#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1) -#error "Multiple OPENSSL_API versions defined" -#endif -#if defined(OPENSSL_API_1_1) && defined(OPENSSL_API_3_0) -#error "Multiple OPENSSL_API versions defined" -#endif -#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_3_0) -#error "Multiple OPENSSL_API versions defined" -#endif -#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \ - || defined(OPENSSL_API_3_0)) \ - && (defined(USE_MBEDTLS) || defined(USE_GNUTLS)) -#error "Multiple SSL libraries defined" -#endif -#if defined(USE_MBEDTLS) && defined(USE_GNUTLS) -#error "Multiple SSL libraries defined" -#endif -#endif - - -static pthread_key_t sTlsKey; /* Thread local storage index */ -static volatile ptrdiff_t thread_idx_max = 0; - -#if defined(MG_LEGACY_INTERFACE) -#define MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE -#endif - -struct mg_workerTLS { - int is_master; - unsigned long thread_idx; - void *user_ptr; -#if defined(_WIN32) - HANDLE pthread_cond_helper_mutex; - struct mg_workerTLS *next_waiting_thread; -#endif - const char *alpn_proto; -#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE) - char txtbuf[4]; -#endif -}; - - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif /* defined(GCC_DIAGNOSTIC) */ -#if defined(__clang__) -/* Show no warning in case system functions are not used. */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -#endif - - -/* Get a unique thread ID as unsigned long, independent from the data type - * of thread IDs defined by the operating system API. - * If two calls to mg_current_thread_id return the same value, they calls - * are done from the same thread. If they return different values, they are - * done from different threads. (Provided this function is used in the same - * process context and threads are not repeatedly created and deleted, but - * CivetWeb does not do that). - * This function must match the signature required for SSL id callbacks: - * CRYPTO_set_id_callback - */ -FUNCTION_MAY_BE_UNUSED -static unsigned long -mg_current_thread_id(void) -{ -#if defined(_WIN32) - return GetCurrentThreadId(); -#else - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" - /* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)" - * or not, so one of the two conditions will be unreachable by construction. - * Unfortunately the C standard does not define a way to check this at - * compile time, since the #if preprocessor conditions can not use the - * sizeof operator as an argument. */ -#endif - - if (sizeof(pthread_t) > sizeof(unsigned long)) { - /* This is the problematic case for CRYPTO_set_id_callback: - * The OS pthread_t can not be cast to unsigned long. */ - struct mg_workerTLS *tls = - (struct mg_workerTLS *)pthread_getspecific(sTlsKey); - if (tls == NULL) { - /* SSL called from an unknown thread: Create some thread index. - */ - tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS)); - tls->is_master = -2; /* -2 means "3rd party thread" */ - tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max); - pthread_setspecific(sTlsKey, tls); - } - return tls->thread_idx; - } else { - /* pthread_t may be any data type, so a simple cast to unsigned long - * can rise a warning/error, depending on the platform. - * Here memcpy is used as an anything-to-anything cast. */ - unsigned long ret = 0; - pthread_t t = pthread_self(); - memcpy(&ret, &t, sizeof(pthread_t)); - return ret; - } - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#endif -} - - -FUNCTION_MAY_BE_UNUSED -static uint64_t -mg_get_current_time_ns(void) -{ - struct timespec tsnow; - clock_gettime(CLOCK_REALTIME, &tsnow); - return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec; -} - - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic pop -#endif /* defined(GCC_DIAGNOSTIC) */ -#if defined(__clang__) -/* Show no warning in case system functions are not used. */ -#pragma clang diagnostic pop -#endif - - -#if defined(NEED_DEBUG_TRACE_FUNC) -static void -DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...) -{ - va_list args; - struct timespec tsnow; - - /* Get some operating system independent thread id */ - unsigned long thread_id = mg_current_thread_id(); - - clock_gettime(CLOCK_REALTIME, &tsnow); - - flockfile(DEBUG_TRACE_STREAM); - fprintf(DEBUG_TRACE_STREAM, - "*** %lu.%09lu %lu %s:%u: ", - (unsigned long)tsnow.tv_sec, - (unsigned long)tsnow.tv_nsec, - thread_id, - func, - line); - va_start(args, fmt); - vfprintf(DEBUG_TRACE_STREAM, fmt, args); - va_end(args); - putc('\n', DEBUG_TRACE_STREAM); - fflush(DEBUG_TRACE_STREAM); - funlockfile(DEBUG_TRACE_STREAM); -} -#endif /* NEED_DEBUG_TRACE_FUNC */ - - -#define MD5_STATIC static -#include "md5.inl" - -/* Darwin prior to 7.0 and Win32 do not have socklen_t */ -#if defined(NO_SOCKLEN_T) -typedef int socklen_t; -#endif /* NO_SOCKLEN_T */ - -#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */ - -#if !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL (0) -#endif - - -/* SSL: mbedTLS vs. GnuTLS vs. no-ssl vs. OpenSSL */ -#if defined(USE_MBEDTLS) -/* mbedTLS */ -#include "mod_mbedtls.inl" - -#elif defined(USE_GNUTLS) -/* GnuTLS */ -#include "mod_gnutls.inl" - -#elif defined(NO_SSL) -/* no SSL */ -typedef struct SSL SSL; /* dummy for SSL argument to push/pull */ -typedef struct SSL_CTX SSL_CTX; - -#elif defined(NO_SSL_DL) -/* OpenSSL without dynamic loading */ -#include -#include -#include -#include -// remove to avoid problems with future Fedora, see https://github.com/root-project/root/commit/d75eeb8 -// #include -#include -#include -#include -#include -#include -#include - -#if defined(WOLFSSL_VERSION) -/* Additional defines for WolfSSL, see - * https://github.com/civetweb/civetweb/issues/583 */ -#include "wolfssl_extras.inl" -#endif - -#if defined(OPENSSL_IS_BORINGSSL) -/* From boringssl/src/include/openssl/mem.h: - * - * OpenSSL has, historically, had a complex set of malloc debugging options. - * However, that was written in a time before Valgrind and ASAN. Since we now - * have those tools, the OpenSSL allocation functions are simply macros around - * the standard memory functions. - * - * #define OPENSSL_free free */ -#define free free -// disable for boringssl -#define CONF_modules_unload(a) ((void)0) -#define ENGINE_cleanup() ((void)0) -#endif - -/* If OpenSSL headers are included, automatically select the API version */ -#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) -#if !defined(OPENSSL_API_3_0) -#define OPENSSL_API_3_0 -#endif -#define OPENSSL_REMOVE_THREAD_STATE() -#else -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) -#if !defined(OPENSSL_API_1_1) -#define OPENSSL_API_1_1 -#endif -#define OPENSSL_REMOVE_THREAD_STATE() -#else -#if !defined(OPENSSL_API_1_0) -#define OPENSSL_API_1_0 -#endif -#define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_thread_state(NULL) -#endif -#endif - - -#else -/* SSL loaded dynamically from DLL / shared object */ -/* Add all prototypes here, to be independent from OpenSSL source - * installation. */ -#include "openssl_dl.inl" - -#endif /* Various SSL bindings */ - - -#if !defined(NO_CACHING) -static const char month_names[][4] = {"Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec"}; -#endif /* !NO_CACHING */ - - -/* Unified socket address. For IPv6 support, add IPv6 address structure in - * the union u. */ -union usa { - struct sockaddr sa; - struct sockaddr_in sin; -#if defined(USE_IPV6) - struct sockaddr_in6 sin6; -#endif -#if defined(USE_X_DOM_SOCKET) - struct sockaddr_un sun; -#endif -}; - -#if defined(USE_X_DOM_SOCKET) -static unsigned short -USA_IN_PORT_UNSAFE(union usa *s) -{ - if (s->sa.sa_family == AF_INET) - return s->sin.sin_port; -#if defined(USE_IPV6) - if (s->sa.sa_family == AF_INET6) - return s->sin6.sin6_port; -#endif - return 0; -} -#endif -#if defined(USE_IPV6) -#define USA_IN_PORT_UNSAFE(s) \ - (((s)->sa.sa_family == AF_INET6) ? (s)->sin6.sin6_port : (s)->sin.sin_port) -#else -#define USA_IN_PORT_UNSAFE(s) ((s)->sin.sin_port) -#endif - -/* Describes a string (chunk of memory). */ -struct vec { - const char *ptr; - size_t len; -}; - -struct mg_file_stat { - /* File properties filled by mg_stat: */ - uint64_t size; - time_t last_modified; - int is_directory; /* Set to 1 if mg_stat is called for a directory */ - int is_gzipped; /* Set to 1 if the content is gzipped, in which - * case we need a "Content-Eencoding: gzip" header */ - int location; /* 0 = nowhere, 1 = on disk, 2 = in memory */ -}; - - -struct mg_file_access { - /* File properties filled by mg_fopen: */ - FILE *fp; -}; - -struct mg_file { - struct mg_file_stat stat; - struct mg_file_access access; -}; - - -#define STRUCT_FILE_INITIALIZER \ - { \ - {(uint64_t)0, (time_t)0, 0, 0, 0}, \ - { \ - (FILE *)NULL \ - } \ - } - - -/* Describes listening socket, or socket which was accept()-ed by the master - * thread and queued for future handling by the worker thread. */ -struct socket { - SOCKET sock; /* Listening socket */ - union usa lsa; /* Local socket address */ - union usa rsa; /* Remote socket address */ - unsigned char is_ssl; /* Is port SSL-ed */ - unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL - * port */ - unsigned char - is_optional; /* Shouldn't cause us to exit if we can't bind to it */ - unsigned char in_use; /* 0: invalid, 1: valid, 2: free */ -}; - - -/* Enum const for all options must be in sync with - * static struct mg_option config_options[] - * This is tested in the unit test (test/private.c) - * "Private Config Options" - */ -enum { - /* Once for each server */ - LISTENING_PORTS, - NUM_THREADS, - PRESPAWN_THREADS, - RUN_AS_USER, - CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the - * socket option typedef TCP_NODELAY. */ - MAX_REQUEST_SIZE, - LINGER_TIMEOUT, - CONNECTION_QUEUE_SIZE, - LISTEN_BACKLOG_SIZE, -#if defined(__linux__) - ALLOW_SENDFILE_CALL, -#endif -#if defined(_WIN32) - CASE_SENSITIVE_FILES, -#endif - THROTTLE, - ENABLE_KEEP_ALIVE, - REQUEST_TIMEOUT, - KEEP_ALIVE_TIMEOUT, -#if defined(USE_WEBSOCKET) - WEBSOCKET_TIMEOUT, - ENABLE_WEBSOCKET_PING_PONG, -#endif - DECODE_URL, - DECODE_QUERY_STRING, -#if defined(USE_LUA) - LUA_BACKGROUND_SCRIPT, - LUA_BACKGROUND_SCRIPT_PARAMS, -#endif -#if defined(USE_HTTP2) - ENABLE_HTTP2, -#endif - - /* Once for each domain */ - DOCUMENT_ROOT, - FALLBACK_DOCUMENT_ROOT, - - ACCESS_LOG_FILE, - ERROR_LOG_FILE, - - CGI_EXTENSIONS, - CGI_ENVIRONMENT, - CGI_INTERPRETER, - CGI_INTERPRETER_ARGS, -#if defined(USE_TIMERS) - CGI_TIMEOUT, -#endif - CGI_BUFFERING, - - CGI2_EXTENSIONS, - CGI2_ENVIRONMENT, - CGI2_INTERPRETER, - CGI2_INTERPRETER_ARGS, -#if defined(USE_TIMERS) - CGI2_TIMEOUT, -#endif - CGI2_BUFFERING, - -#if defined(USE_4_CGI) - CGI3_EXTENSIONS, - CGI3_ENVIRONMENT, - CGI3_INTERPRETER, - CGI3_INTERPRETER_ARGS, -#if defined(USE_TIMERS) - CGI3_TIMEOUT, -#endif - CGI3_BUFFERING, - - CGI4_EXTENSIONS, - CGI4_ENVIRONMENT, - CGI4_INTERPRETER, - CGI4_INTERPRETER_ARGS, -#if defined(USE_TIMERS) - CGI4_TIMEOUT, -#endif - CGI4_BUFFERING, -#endif - - PUT_DELETE_PASSWORDS_FILE, /* must follow CGI_* */ - PROTECT_URI, - AUTHENTICATION_DOMAIN, - ENABLE_AUTH_DOMAIN_CHECK, - SSI_EXTENSIONS, - ENABLE_DIRECTORY_LISTING, - ENABLE_WEBDAV, - GLOBAL_PASSWORDS_FILE, - INDEX_FILES, - ACCESS_CONTROL_LIST, - EXTRA_MIME_TYPES, - SSL_CERTIFICATE, - SSL_CERTIFICATE_CHAIN, - URL_REWRITE_PATTERN, - HIDE_FILES, - SSL_DO_VERIFY_PEER, - SSL_CACHE_TIMEOUT, - SSL_CA_PATH, - SSL_CA_FILE, - SSL_VERIFY_DEPTH, - SSL_DEFAULT_VERIFY_PATHS, - SSL_CIPHER_LIST, - SSL_PROTOCOL_VERSION, - SSL_SHORT_TRUST, - -#if defined(USE_LUA) - LUA_PRELOAD_FILE, - LUA_SCRIPT_EXTENSIONS, - LUA_SERVER_PAGE_EXTENSIONS, -#if defined(MG_EXPERIMENTAL_INTERFACES) - LUA_DEBUG_PARAMS, -#endif -#endif -#if defined(USE_DUKTAPE) - DUKTAPE_SCRIPT_EXTENSIONS, -#endif - -#if defined(USE_WEBSOCKET) - WEBSOCKET_ROOT, - FALLBACK_WEBSOCKET_ROOT, -#endif -#if defined(USE_LUA) && defined(USE_WEBSOCKET) - LUA_WEBSOCKET_EXTENSIONS, -#endif - - ACCESS_CONTROL_ALLOW_ORIGIN, - ACCESS_CONTROL_ALLOW_METHODS, - ACCESS_CONTROL_ALLOW_HEADERS, - ACCESS_CONTROL_EXPOSE_HEADERS, - ACCESS_CONTROL_ALLOW_CREDENTIALS, - ERROR_PAGES, -#if !defined(NO_CACHING) - STATIC_FILE_MAX_AGE, - STATIC_FILE_CACHE_CONTROL, -#endif -#if !defined(NO_SSL) - STRICT_HTTPS_MAX_AGE, -#endif - ADDITIONAL_HEADER, - ALLOW_INDEX_SCRIPT_SUB_RES, - - NUM_OPTIONS -}; - - -/* Config option name, config types, default value. - * Must be in the same order as the enum const above. - */ -static const struct mg_option config_options[] = { - - /* Once for each server */ - {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"}, - {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"}, - {"prespawn_threads", MG_CONFIG_TYPE_NUMBER, "0"}, - {"run_as_user", MG_CONFIG_TYPE_STRING, NULL}, - {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"}, - {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"}, - {"linger_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL}, - {"connection_queue", MG_CONFIG_TYPE_NUMBER, "20"}, - {"listen_backlog", MG_CONFIG_TYPE_NUMBER, "200"}, -#if defined(__linux__) - {"allow_sendfile_call", MG_CONFIG_TYPE_BOOLEAN, "yes"}, -#endif -#if defined(_WIN32) - {"case_sensitive", MG_CONFIG_TYPE_BOOLEAN, "no"}, -#endif - {"throttle", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"enable_keep_alive", MG_CONFIG_TYPE_BOOLEAN, "no"}, - {"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"}, - {"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"}, -#if defined(USE_WEBSOCKET) - {"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL}, - {"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN, "no"}, -#endif - {"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - {"decode_query_string", MG_CONFIG_TYPE_BOOLEAN, "no"}, -#if defined(USE_LUA) - {"lua_background_script", MG_CONFIG_TYPE_FILE, NULL}, - {"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL}, -#endif -#if defined(USE_HTTP2) - {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"}, -#endif - - /* Once for each domain */ - {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, - {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, - - {"access_log_file", MG_CONFIG_TYPE_FILE, NULL}, - {"error_log_file", MG_CONFIG_TYPE_FILE, NULL}, - - {"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, - {"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL}, - {"cgi_interpreter_args", MG_CONFIG_TYPE_STRING, NULL}, -#if defined(USE_TIMERS) - {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL}, -#endif - {"cgi_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - - {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL}, - {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL}, - {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL}, -#if defined(USE_TIMERS) - {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL}, -#endif - {"cgi2_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - -#if defined(USE_4_CGI) - {"cgi3_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL}, - {"cgi3_environment", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"cgi3_interpreter", MG_CONFIG_TYPE_FILE, NULL}, - {"cgi3_interpreter_args", MG_CONFIG_TYPE_STRING, NULL}, -#if defined(USE_TIMERS) - {"cgi3_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL}, -#endif - {"cgi3_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - - {"cgi4_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL}, - {"cgi4_environment", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"cgi4_interpreter", MG_CONFIG_TYPE_FILE, NULL}, - {"cgi4_interpreter_args", MG_CONFIG_TYPE_STRING, NULL}, -#if defined(USE_TIMERS) - {"cgi4_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL}, -#endif - {"cgi4_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - -#endif - - {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL}, - {"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"}, - {"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - {"ssi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"}, - {"enable_directory_listing", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - {"enable_webdav", MG_CONFIG_TYPE_BOOLEAN, "no"}, - {"global_auth_file", MG_CONFIG_TYPE_FILE, NULL}, - {"index_files", - MG_CONFIG_TYPE_STRING_LIST, -#if defined(USE_LUA) - "index.xhtml,index.html,index.htm," - "index.lp,index.lsp,index.lua,index.cgi," - "index.shtml,index.php"}, -#else - "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"}, -#endif - {"access_control_list", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"extra_mime_types", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"ssl_certificate", MG_CONFIG_TYPE_FILE, NULL}, - {"ssl_certificate_chain", MG_CONFIG_TYPE_FILE, NULL}, - {"url_rewrite_patterns", MG_CONFIG_TYPE_STRING_LIST, NULL}, - {"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL}, - - {"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL, "no"}, - {"ssl_cache_timeout", MG_CONFIG_TYPE_NUMBER, "-1"}, - - {"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL}, - {"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL}, - {"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER, "9"}, - {"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN, "yes"}, - {"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL}, - - /* HTTP2 requires ALPN, and anyway TLS1.2 should be considered - * as a minimum in 2020 */ - {"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "4"}, - - {"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN, "no"}, - -#if defined(USE_LUA) - {"lua_preload_file", MG_CONFIG_TYPE_FILE, NULL}, - {"lua_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, - {"lua_server_page_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, -#if defined(MG_EXPERIMENTAL_INTERFACES) - {"lua_debug", MG_CONFIG_TYPE_STRING, NULL}, -#endif -#endif -#if defined(USE_DUKTAPE) - /* The support for duktape is still in alpha version state. - * The name of this config option might change. */ - {"duktape_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"}, -#endif - -#if defined(USE_WEBSOCKET) - {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, - {"fallback_websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, -#endif -#if defined(USE_LUA) && defined(USE_WEBSOCKET) - {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, -#endif - {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_expose_headers", MG_CONFIG_TYPE_STRING, ""}, - {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""}, - {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL}, -#if !defined(NO_CACHING) - {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"}, - {"static_file_cache_control", MG_CONFIG_TYPE_STRING, NULL}, -#endif -#if !defined(NO_SSL) - {"strict_transport_security_max_age", MG_CONFIG_TYPE_NUMBER, NULL}, -#endif - {"additional_header", MG_CONFIG_TYPE_STRING_MULTILINE, NULL}, - {"allow_index_script_resource", MG_CONFIG_TYPE_BOOLEAN, "no"}, - - {NULL, MG_CONFIG_TYPE_UNKNOWN, NULL}}; - - -/* Check if the config_options and the corresponding enum have compatible - * sizes. */ -mg_static_assert((sizeof(config_options) / sizeof(config_options[0])) - == (NUM_OPTIONS + 1), - "config_options and enum not sync"); - - -enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER }; - - -struct mg_handler_info { - /* Name/Pattern of the URI. */ - char *uri; - size_t uri_len; - - /* handler type */ - int handler_type; - - /* Handler for http/https or requests. */ - mg_request_handler handler; - unsigned int refcount; - int removing; - - /* Handler for ws/wss (websocket) requests. */ - mg_websocket_connect_handler connect_handler; - mg_websocket_ready_handler ready_handler; - mg_websocket_data_handler data_handler; - mg_websocket_close_handler close_handler; - - /* accepted subprotocols for ws/wss requests. */ - struct mg_websocket_subprotocols *subprotocols; - - /* Handler for authorization requests */ - mg_authorization_handler auth_handler; - - /* User supplied argument for the handler function. */ - void *cbdata; - - /* next handler in a linked list */ - struct mg_handler_info *next; -}; - - -enum { - CONTEXT_INVALID, - CONTEXT_SERVER, - CONTEXT_HTTP_CLIENT, - CONTEXT_WS_CLIENT -}; - - -struct mg_domain_context { - SSL_CTX *ssl_ctx; /* SSL context */ - char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */ - struct mg_handler_info *handlers; /* linked list of uri handlers */ - int64_t ssl_cert_last_mtime; - - /* Server nonce */ - uint64_t auth_nonce_mask; /* Mask for all nonce values */ - unsigned long nonce_count; /* Used nonces, used for authentication */ - -#if defined(USE_LUA) && defined(USE_WEBSOCKET) - /* linked list of shared lua websockets */ - struct mg_shared_lua_websocket_list *shared_lua_websockets; -#endif - - /* Linked list of domains */ - struct mg_domain_context *next; -}; - - -/* Stop flag can be "volatile" or require a lock. - * MSDN uses volatile for "Interlocked" operations, but also explicitly - * states a read operation for int is always atomic. */ -#if defined(STOP_FLAG_NEEDS_LOCK) - -typedef ptrdiff_t volatile stop_flag_t; - -static int -STOP_FLAG_IS_ZERO(const stop_flag_t *f) -{ - stop_flag_t sf = mg_atomic_add((stop_flag_t *)f, 0); - return (sf == 0); -} - -static int -STOP_FLAG_IS_TWO(stop_flag_t *f) -{ - stop_flag_t sf = mg_atomic_add(f, 0); - return (sf == 2); -} - -static void -STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v) -{ - stop_flag_t sf = 0; - do { - sf = mg_atomic_compare_and_swap(f, - __atomic_load_n(f, __ATOMIC_SEQ_CST), - v); - } while (sf != v); -} - -#else /* STOP_FLAG_NEEDS_LOCK */ - -typedef int volatile stop_flag_t; -#define STOP_FLAG_IS_ZERO(f) ((*(f)) == 0) -#define STOP_FLAG_IS_TWO(f) ((*(f)) == 2) -#define STOP_FLAG_ASSIGN(f, v) ((*(f)) = (v)) - -#endif /* STOP_FLAG_NEEDS_LOCK */ - - -#if !defined(NUM_WEBDAV_LOCKS) -#define NUM_WEBDAV_LOCKS 10 -#endif -#if !defined(LOCK_DURATION_S) -#define LOCK_DURATION_S 60 -#endif - - -struct twebdav_lock { - uint64_t locktime; - char token[33]; - char path[UTF8_PATH_MAX * 2]; - char user[UTF8_PATH_MAX * 2]; -}; - - -struct mg_context { - - /* Part 1 - Physical context: - * This holds threads, ports, timeouts, ... - * set for the entire server, independent from the - * addressed hostname. - */ - - /* Connection related */ - int context_type; /* See CONTEXT_* above */ - - struct socket *listening_sockets; - struct mg_pollfd *listening_socket_fds; - unsigned int num_listening_sockets; - - struct mg_connection *worker_connections; /* The connection struct, pre- - * allocated for each worker */ - -#if defined(USE_SERVER_STATS) - volatile ptrdiff_t active_connections; - volatile ptrdiff_t max_active_connections; - volatile ptrdiff_t total_connections; - volatile ptrdiff_t total_requests; - volatile int64_t total_data_read; - volatile int64_t total_data_written; -#endif - - /* Thread related */ - stop_flag_t stop_flag; /* Should we stop event loop */ - pthread_mutex_t thread_mutex; /* Protects client_socks or queue */ - - pthread_t masterthreadid; /* The master thread ID */ - unsigned int cfg_max_worker_threads; /* How many worker-threads we are - allowed to create, total */ - - unsigned int spawned_worker_threads; /* How many worker-threads currently - exist (modified by master thread) */ - unsigned int - idle_worker_thread_count; /* How many worker-threads are currently - sitting around with nothing to do */ - /* Access to this value MUST be synchronized by thread_mutex */ - - pthread_t *worker_threadids; /* The worker thread IDs */ - unsigned long starter_thread_idx; /* thread index which called mg_start */ - - /* Connection to thread dispatching */ -#if defined(ALTERNATIVE_QUEUE) - struct socket *client_socks; - void **client_wait_events; -#else - struct socket *squeue; /* Socket queue (sq) : accepted sockets waiting for a - worker thread */ - volatile int sq_head; /* Head of the socket queue */ - volatile int sq_tail; /* Tail of the socket queue */ - pthread_cond_t sq_full; /* Signaled when socket is produced */ - pthread_cond_t sq_empty; /* Signaled when socket is consumed */ - volatile int sq_blocked; /* Status information: sq is full */ - int sq_size; /* No of elements in socket queue */ -#if defined(USE_SERVER_STATS) - int sq_max_fill; -#endif /* USE_SERVER_STATS */ -#endif /* ALTERNATIVE_QUEUE */ - - /* Memory related */ - unsigned int max_request_size; /* The max request size */ - -#if defined(USE_SERVER_STATS) - struct mg_memory_stat ctx_memory; -#endif - - /* WebDAV lock structures */ - struct twebdav_lock webdav_lock[NUM_WEBDAV_LOCKS]; - - /* Operating system related */ - char *systemName; /* What operating system is running */ - time_t start_time; /* Server start time, used for authentication - * and for diagnstics. */ - -#if defined(USE_TIMERS) - struct ttimers *timers; -#endif - - /* Lua specific: Background operations and shared websockets */ -#if defined(USE_LUA) - void *lua_background_state; /* lua_State (here as void *) */ - pthread_mutex_t lua_bg_mutex; /* Protect background state */ - int lua_bg_log_available; /* Use Lua background state for access log */ -#endif - - int user_shutdown_notification_socket; /* mg_stop() will close this - socket... */ - int thread_shutdown_notification_socket; /* to cause poll() in all threads - to return immediately */ - - /* Server nonce */ - pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers, - * ssl_cert_last_mtime, nonce_count, and - * next (linked list) */ - - /* Server callbacks */ - struct mg_callbacks callbacks; /* User-defined callback function */ - void *user_data; /* User-defined data */ - - /* Part 2 - Logical domain: - * This holds hostname, TLS certificate, document root, ... - * set for a domain hosted at the server. - * There may be multiple domains hosted at one physical server. - * The default domain "dd" is the first element of a list of - * domains. - */ - struct mg_domain_context dd; /* default domain */ -}; - - -#if defined(USE_SERVER_STATS) -static struct mg_memory_stat mg_common_memory = {0, 0, 0}; - -static struct mg_memory_stat * -get_memory_stat(struct mg_context *ctx) -{ - if (ctx) { - return &(ctx->ctx_memory); - } - return &mg_common_memory; -} -#endif - -enum { - CONNECTION_TYPE_INVALID = 0, - CONNECTION_TYPE_REQUEST = 1, - CONNECTION_TYPE_RESPONSE = 2 -}; - -enum { - PROTOCOL_TYPE_HTTP1 = 0, - PROTOCOL_TYPE_WEBSOCKET = 1, - PROTOCOL_TYPE_HTTP2 = 2 -}; - - -#if defined(USE_HTTP2) -#if !defined(HTTP2_DYN_TABLE_SIZE) -#define HTTP2_DYN_TABLE_SIZE (256) -#endif - -struct mg_http2_connection { - uint32_t stream_id; - uint32_t dyn_table_size; - struct mg_header dyn_table[HTTP2_DYN_TABLE_SIZE]; -}; -#endif - - -struct mg_connection { - int connection_type; /* see CONNECTION_TYPE_* above */ - int protocol_type; /* see PROTOCOL_TYPE_*: 0=http/1.x, 1=ws, 2=http/2 */ - int request_state; /* 0: nothing sent, 1: header partially sent, 2: header - fully sent */ -#if defined(USE_HTTP2) - struct mg_http2_connection http2; -#endif - - struct mg_request_info request_info; - struct mg_response_info response_info; - - struct mg_context *phys_ctx; - struct mg_domain_context *dom_ctx; - -#if defined(USE_SERVER_STATS) - int conn_state; /* 0 = undef, numerical value may change in different - * versions. For the current definition, see - * mg_get_connection_info_impl */ -#endif - SSL *ssl; /* SSL descriptor */ - struct socket client; /* Connected client */ - time_t conn_birth_time; /* Time (wall clock) when connection was - * established */ -#if defined(USE_SERVER_STATS) - time_t conn_close_time; /* Time (wall clock) when connection was - * closed (or 0 if still open) */ - double processing_time; /* Processing time for one request. */ -#endif - struct timespec req_time; /* Time (since system start) when the request - * was received */ - int64_t num_bytes_sent; /* Total bytes sent to client */ - int64_t content_len; /* How many bytes of content can be read - * !is_chunked: Content-Length header value - * or -1 (until connection closed, - * not allowed for a request) - * is_chunked: >= 0, appended gradually - */ - int64_t consumed_content; /* How many bytes of content have been read */ - int is_chunked; /* Transfer-Encoding is chunked: - * 0 = not chunked, - * 1 = chunked, not yet, or some data read, - * 2 = chunked, has error, - * 3 = chunked, all data read except trailer, - * 4 = chunked, all data read - */ - char *buf; /* Buffer for received data */ - char *path_info; /* PATH_INFO part of the URL */ - - int must_close; /* 1 if connection must be closed */ - int accept_gzip; /* 1 if gzip encoding is accepted */ - int in_error_handler; /* 1 if in handler for user defined error - * pages */ -#if defined(USE_WEBSOCKET) - int in_websocket_handling; /* 1 if in read_websocket */ -#endif -#if defined(USE_ZLIB) && defined(USE_WEBSOCKET) \ - && defined(MG_EXPERIMENTAL_INTERFACES) - /* Parameters for websocket data compression according to rfc7692 */ - int websocket_deflate_server_max_windows_bits; - int websocket_deflate_client_max_windows_bits; - int websocket_deflate_server_no_context_takeover; - int websocket_deflate_client_no_context_takeover; - int websocket_deflate_initialized; - int websocket_deflate_flush; - z_stream websocket_deflate_state; - z_stream websocket_inflate_state; -#endif - int handled_requests; /* Number of requests handled by this connection - */ - int buf_size; /* Buffer size */ - int request_len; /* Size of the request + headers in a buffer */ - int data_len; /* Total size of data in a buffer */ - int status_code; /* HTTP reply status code, e.g. 200 */ - int throttle; /* Throttling, bytes/sec. <= 0 means no - * throttle */ - - time_t last_throttle_time; /* Last time throttled data was sent */ - int last_throttle_bytes; /* Bytes sent this second */ - pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure - * atomic transmissions for websockets */ -#if defined(USE_LUA) && defined(USE_WEBSOCKET) - void *lua_websocket_state; /* Lua_State for a websocket connection */ -#endif - - void *tls_user_ptr; /* User defined pointer in thread local storage, - * for quick access */ -}; - - -/* Directory entry */ -struct de { - char *file_name; - struct mg_file_stat file; -}; - - -#define mg_cry_internal(conn, fmt, ...) \ - mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__) - -#define mg_cry_ctx_internal(ctx, fmt, ...) \ - mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__) - -static void mg_cry_internal_wrap(const struct mg_connection *conn, - struct mg_context *ctx, - const char *func, - unsigned line, - const char *fmt, - ...) PRINTF_ARGS(5, 6); - - -#if !defined(NO_THREAD_NAME) -#if defined(_WIN32) && defined(_MSC_VER) -/* Set the thread name for debugging purposes in Visual Studio - * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx - */ -#pragma pack(push, 8) -typedef struct tagTHREADNAME_INFO { - DWORD dwType; /* Must be 0x1000. */ - LPCSTR szName; /* Pointer to name (in user addr space). */ - DWORD dwThreadID; /* Thread ID (-1=caller thread). */ - DWORD dwFlags; /* Reserved for future use, must be zero. */ -} THREADNAME_INFO; -#pragma pack(pop) - -#elif defined(__linux__) - -#include -#include -#if defined(ALTERNATIVE_QUEUE) -#include -#endif /* ALTERNATIVE_QUEUE */ - - -#if defined(ALTERNATIVE_QUEUE) - -static void * -event_create(void) -{ - int evhdl = eventfd(0, EFD_CLOEXEC); - int *ret; - - if (evhdl == -1) { - /* Linux uses -1 on error, Windows NULL. */ - /* However, Linux does not return 0 on success either. */ - return 0; - } - - ret = (int *)mg_malloc(sizeof(int)); - if (ret) { - *ret = evhdl; - } else { - (void)close(evhdl); - } - - return (void *)ret; -} - - -static int -event_wait(void *eventhdl) -{ - uint64_t u; - int evhdl, s; - - if (!eventhdl) { - /* error */ - return 0; - } - evhdl = *(int *)eventhdl; - - s = (int)read(evhdl, &u, sizeof(u)); - if (s != sizeof(u)) { - /* error */ - return 0; - } - (void)u; /* the value is not required */ - return 1; -} - - -static int -event_signal(void *eventhdl) -{ - uint64_t u = 1; - int evhdl, s; - - if (!eventhdl) { - /* error */ - return 0; - } - evhdl = *(int *)eventhdl; - - s = (int)write(evhdl, &u, sizeof(u)); - if (s != sizeof(u)) { - /* error */ - return 0; - } - return 1; -} - - -static void -event_destroy(void *eventhdl) -{ - int evhdl; - - if (!eventhdl) { - /* error */ - return; - } - evhdl = *(int *)eventhdl; - - close(evhdl); - mg_free(eventhdl); -} - - -#endif - -#endif - - -#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE) - -struct posix_event { - pthread_mutex_t mutex; - pthread_cond_t cond; - int signaled; -}; - - -static void * -event_create(void) -{ - struct posix_event *ret = mg_malloc(sizeof(struct posix_event)); - if (ret == 0) { - /* out of memory */ - return 0; - } - if (0 != pthread_mutex_init(&(ret->mutex), NULL)) { - /* pthread mutex not available */ - mg_free(ret); - return 0; - } - if (0 != pthread_cond_init(&(ret->cond), NULL)) { - /* pthread cond not available */ - pthread_mutex_destroy(&(ret->mutex)); - mg_free(ret); - return 0; - } - ret->signaled = 0; - return (void *)ret; -} - - -static int -event_wait(void *eventhdl) -{ - struct posix_event *ev = (struct posix_event *)eventhdl; - pthread_mutex_lock(&(ev->mutex)); - while (!ev->signaled) { - pthread_cond_wait(&(ev->cond), &(ev->mutex)); - } - ev->signaled = 0; - pthread_mutex_unlock(&(ev->mutex)); - return 1; -} - - -static int -event_signal(void *eventhdl) -{ - struct posix_event *ev = (struct posix_event *)eventhdl; - pthread_mutex_lock(&(ev->mutex)); - pthread_cond_signal(&(ev->cond)); - ev->signaled = 1; - pthread_mutex_unlock(&(ev->mutex)); - return 1; -} - - -static void -event_destroy(void *eventhdl) -{ - struct posix_event *ev = (struct posix_event *)eventhdl; - pthread_cond_destroy(&(ev->cond)); - pthread_mutex_destroy(&(ev->mutex)); - mg_free(ev); -} -#endif - - -static void -mg_set_thread_name(const char *name) -{ - char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */ - - mg_snprintf( - NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name); - -#if defined(_WIN32) -#if defined(_MSC_VER) - /* Windows and Visual Studio Compiler */ - __try { - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = threadName; - info.dwThreadID = ~0U; - info.dwFlags = 0; - - RaiseException(0x406D1388, - 0, - sizeof(info) / sizeof(ULONG_PTR), - (ULONG_PTR *)&info); - } __except (EXCEPTION_EXECUTE_HANDLER) { - } -#elif defined(__MINGW32__) - /* No option known to set thread name for MinGW known */ -#endif -#elif defined(_GNU_SOURCE) && defined(__GLIBC__) \ - && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12))) - /* pthread_setname_np first appeared in glibc in version 2.12 */ -#if defined(__MACH__) && defined(__APPLE__) - /* OS X only current thread name can be changed */ - (void)pthread_setname_np(threadName); -#else - (void)pthread_setname_np(pthread_self(), threadName); -#endif -#elif defined(__linux__) - /* On Linux we can use the prctl function. - * When building for Linux Standard Base (LSB) use - * NO_THREAD_NAME. However, thread names are a big - * help for debugging, so the stadard is to set them. - */ - (void)prctl(PR_SET_NAME, threadName, 0, 0, 0); -#endif -} -#else /* !defined(NO_THREAD_NAME) */ -static void -mg_set_thread_name(const char *threadName) -{ -} -#endif - - -CIVETWEB_API const struct mg_option * -mg_get_valid_options(void) -{ - return config_options; -} - - -/* Do not open file (unused) */ -#define MG_FOPEN_MODE_NONE (0) - -/* Open file for read only access */ -#define MG_FOPEN_MODE_READ (1) - -/* Open file for writing, create and overwrite */ -#define MG_FOPEN_MODE_WRITE (2) - -/* Open file for writing, create and append */ -#define MG_FOPEN_MODE_APPEND (4) - - -static int -is_file_opened(const struct mg_file_access *fileacc) -{ - if (!fileacc) { - return 0; - } - - return (fileacc->fp != NULL); -} - - -#if !defined(NO_FILESYSTEMS) -static int mg_stat(const struct mg_connection *conn, - const char *path, - struct mg_file_stat *filep); - - -/* Reject files with special characters (for Windows) */ -static int -mg_path_suspicious(const struct mg_connection *conn, const char *path) -{ - const uint8_t *c = (const uint8_t *)path; - (void)conn; /* not used */ - - if ((c == NULL) || (c[0] == 0)) { - /* Null pointer or empty path --> suspicious */ - return 1; - } - -#if defined(_WIN32) - while (*c) { - if (*c < 32) { - /* Control character */ - return 1; - } - if ((*c == '>') || (*c == '<') || (*c == '|')) { - /* stdin/stdout redirection character */ - return 1; - } - if ((*c == '*') || (*c == '?')) { - /* Wildcard character */ - return 1; - } - if (*c == '"') { - /* Windows quotation */ - return 1; - } - c++; - } -#endif - - /* Nothing suspicious found */ - return 0; -} - - -/* mg_fopen will open a file either in memory or on the disk. - * The input parameter path is a string in UTF-8 encoding. - * The input parameter mode is MG_FOPEN_MODE_* - * On success, fp will be set in the output struct mg_file. - * All status members will also be set. - * The function returns 1 on success, 0 on error. */ -static int -mg_fopen(const struct mg_connection *conn, - const char *path, - int mode, - struct mg_file *filep) -{ - int found; - - if (!filep) { - return 0; - } - filep->access.fp = NULL; - - if (mg_path_suspicious(conn, path)) { - return 0; - } - - /* filep is initialized in mg_stat: all fields with memset to, - * some fields like size and modification date with values */ - found = mg_stat(conn, path, &(filep->stat)); - - if ((mode == MG_FOPEN_MODE_READ) && (!found)) { - /* file does not exist and will not be created */ - return 0; - } - -#if defined(_WIN32) - { - wchar_t wbuf[UTF16_PATH_MAX]; - path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); - switch (mode) { - case MG_FOPEN_MODE_READ: - filep->access.fp = _wfopen(wbuf, L"rb"); - break; - case MG_FOPEN_MODE_WRITE: - filep->access.fp = _wfopen(wbuf, L"wb"); - break; - case MG_FOPEN_MODE_APPEND: - filep->access.fp = _wfopen(wbuf, L"ab"); - break; - } - } -#else - /* Linux et al already use unicode. No need to convert. */ - switch (mode) { - case MG_FOPEN_MODE_READ: - filep->access.fp = fopen(path, "r"); - break; - case MG_FOPEN_MODE_WRITE: - filep->access.fp = fopen(path, "w"); - break; - case MG_FOPEN_MODE_APPEND: - filep->access.fp = fopen(path, "a"); - break; - } - -#endif - if (!found) { - /* File did not exist before fopen was called. - * Maybe it has been created now. Get stat info - * like creation time now. */ - found = mg_stat(conn, path, &(filep->stat)); - (void)found; - } - - /* return OK if file is opened */ - return (filep->access.fp != NULL); -} - - -/* return 0 on success, just like fclose */ -static int -mg_fclose(struct mg_file_access *fileacc) -{ - int ret = -1; - if (fileacc != NULL) { - if (fileacc->fp != NULL) { - ret = fclose(fileacc->fp); - } - /* reset all members of fileacc */ - memset(fileacc, 0, sizeof(*fileacc)); - } - return ret; -} -#endif /* NO_FILESYSTEMS */ - - -static void -mg_strlcpy(char *dst, const char *src, size_t n) -{ - for (; *src != '\0' && n > 1; n--) { - *dst++ = *src++; - } - *dst = '\0'; -} - - -static int -lowercase(const char *s) -{ - return tolower((unsigned char)*s); -} - - -CIVETWEB_API int -mg_strncasecmp(const char *s1, const char *s2, size_t len) -{ - int diff = 0; - - if (len > 0) { - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0' && --len > 0); - } - - return diff; -} - - -CIVETWEB_API int -mg_strcasecmp(const char *s1, const char *s2) -{ - int diff; - - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0'); - - return diff; -} - - -static char * -mg_strndup_ctx(const char *ptr, size_t len, struct mg_context *ctx) -{ - char *p; - (void)ctx; /* Avoid Visual Studio warning if USE_SERVER_STATS is not - * defined */ - - if ((p = (char *)mg_malloc_ctx(len + 1, ctx)) != NULL) { - mg_strlcpy(p, ptr, len + 1); - } - - return p; -} - - -static char * -mg_strdup_ctx(const char *str, struct mg_context *ctx) -{ - return mg_strndup_ctx(str, strlen(str), ctx); -} - -static char * -mg_strdup(const char *str) -{ - return mg_strndup_ctx(str, strlen(str), NULL); -} - - -static const char * -mg_strcasestr(const char *big_str, const char *small_str) -{ - size_t i, big_len = strlen(big_str), small_len = strlen(small_str); - - if (big_len >= small_len) { - for (i = 0; i <= (big_len - small_len); i++) { - if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { - return big_str + i; - } - } - } - - return NULL; -} - - -/* Return null terminated string of given maximum length. - * Report errors if length is exceeded. */ -static void -mg_vsnprintf(const struct mg_connection *conn, - int *truncated, - char *buf, - size_t buflen, - const char *fmt, - va_list ap) -{ - int n, ok; - - if (buflen == 0) { - if (truncated) { - *truncated = 1; - } - return; - } - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" - /* Using fmt as a non-literal is intended here, since it is mostly called - * indirectly by mg_snprintf */ -#endif - - n = (int)vsnprintf_impl(buf, buflen, fmt, ap); - ok = (n >= 0) && ((size_t)n < buflen); - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - - if (ok) { - if (truncated) { - *truncated = 0; - } - } else { - if (truncated) { - *truncated = 1; - } - mg_cry_internal(conn, - "truncating vsnprintf buffer: [%.*s]", - (int)((buflen > 200) ? 200 : (buflen - 1)), - buf); - n = (int)buflen - 1; - } - buf[n] = '\0'; -} - - -static void -mg_snprintf(const struct mg_connection *conn, - int *truncated, - char *buf, - size_t buflen, - const char *fmt, - ...) -{ - va_list ap; - - va_start(ap, fmt); - mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap); - va_end(ap); -} - - -static int -get_option_index(const char *name) -{ - int i; - - for (i = 0; config_options[i].name != NULL; i++) { - if (strcmp(config_options[i].name, name) == 0) { - return i; - } - } - return -1; -} - - -CIVETWEB_API const char * -mg_get_option(const struct mg_context *ctx, const char *name) -{ - int i; - if ((i = get_option_index(name)) == -1) { - return NULL; - } else if (!ctx || ctx->dd.config[i] == NULL) { - return ""; - } else { - return ctx->dd.config[i]; - } -} - -#define mg_get_option DO_NOT_USE_THIS_FUNCTION_INTERNALLY__access_directly - -CIVETWEB_API struct mg_context * -mg_get_context(const struct mg_connection *conn) -{ - return (conn == NULL) ? (struct mg_context *)NULL : (conn->phys_ctx); -} - - -CIVETWEB_API void * -mg_get_user_data(const struct mg_context *ctx) -{ - return (ctx == NULL) ? NULL : ctx->user_data; -} - - -CIVETWEB_API void * -mg_get_user_context_data(const struct mg_connection *conn) -{ - return mg_get_user_data(mg_get_context(conn)); -} - - -CIVETWEB_API void * -mg_get_thread_pointer(const struct mg_connection *conn) -{ - /* both methods should return the same pointer */ - if (conn) { - /* quick access, in case conn is known */ - return conn->tls_user_ptr; - } else { - /* otherwise get pointer from thread local storage (TLS) */ - struct mg_workerTLS *tls = - (struct mg_workerTLS *)pthread_getspecific(sTlsKey); - return tls->user_ptr; - } -} - - -CIVETWEB_API void -mg_set_user_connection_data(const struct mg_connection *const_conn, void *data) -{ - if (const_conn != NULL) { - /* Const cast, since "const struct mg_connection *" does not mean - * the connection object is not modified. Here "const" is used, - * to indicate mg_read/mg_write/mg_send/.. must not be called. */ - struct mg_connection *conn = (struct mg_connection *)const_conn; - conn->request_info.conn_data = data; - } -} - - -CIVETWEB_API void * -mg_get_user_connection_data(const struct mg_connection *conn) -{ - if (conn != NULL) { - return conn->request_info.conn_data; - } - return NULL; -} - - -CIVETWEB_API int -mg_get_server_ports(const struct mg_context *ctx, - int size, - struct mg_server_port *ports) -{ - int i, cnt = 0; - - if (size <= 0) { - return -1; - } - memset(ports, 0, sizeof(*ports) * (size_t)size); - if (!ctx) { - return -1; - } - if (!ctx->listening_sockets) { - return -1; - } - - for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) { - - ports[cnt].port = - ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa))); - ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl; - ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir; - ports[cnt].is_optional = ctx->listening_sockets[i].is_optional; - ports[cnt].is_bound = ctx->listening_sockets[i].sock != INVALID_SOCKET; - - if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) { - /* IPv4 */ - ports[cnt].protocol = 1; - cnt++; - } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) { - /* IPv6 */ - ports[cnt].protocol = 3; - cnt++; - } - } - - return cnt; -} - - -#if defined(USE_X_DOM_SOCKET) && !defined(UNIX_DOMAIN_SOCKET_SERVER_NAME) -#define UNIX_DOMAIN_SOCKET_SERVER_NAME "*" -#endif - -static void -sockaddr_to_string(char *buf, size_t len, const union usa *usa) -{ - buf[0] = '\0'; - - if (!usa) { - return; - } - - if (usa->sa.sa_family == AF_INET) { - getnameinfo(&usa->sa, - sizeof(usa->sin), - buf, - (unsigned)len, - NULL, - 0, - NI_NUMERICHOST); - } -#if defined(USE_IPV6) - else if (usa->sa.sa_family == AF_INET6) { - getnameinfo(&usa->sa, - sizeof(usa->sin6), - buf, - (unsigned)len, - NULL, - 0, - NI_NUMERICHOST); - } -#endif -#if defined(USE_X_DOM_SOCKET) - else if (usa->sa.sa_family == AF_UNIX) { - /* TODO: Define a remote address for unix domain sockets. - * This code will always return "localhost", identical to http+tcp: - getnameinfo(&usa->sa, - sizeof(usa->sun), - buf, - (unsigned)len, - NULL, - 0, - NI_NUMERICHOST); - */ - mg_strlcpy(buf, UNIX_DOMAIN_SOCKET_SERVER_NAME, len); - } -#endif -} - - -/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be - * included in all responses other than 100, 101, 5xx. */ -static void -gmt_time_string(char *buf, size_t buf_len, time_t *t) -{ -#if !defined(REENTRANT_TIME) - struct tm *tm; - - tm = ((t != NULL) ? gmtime(t) : NULL); - if (tm != NULL) { -#else - struct tm _tm; - struct tm *tm = &_tm; - - if (t != NULL) { - gmtime_r(t, tm); -#endif - strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); - } else { - mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); - } -} - - -/* difftime for struct timespec. Return value is in seconds. */ -static double -mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before) -{ - return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9 - + (double)(ts_now->tv_sec - ts_before->tv_sec); -} - - -#if defined(MG_EXTERNAL_FUNCTION_mg_cry_internal_impl) -static void mg_cry_internal_impl(const struct mg_connection *conn, - const char *func, - unsigned line, - const char *fmt, - va_list ap); -#include "external_mg_cry_internal_impl.inl" -#elif !defined(NO_FILESYSTEMS) - -/* Print error message to the opened error log stream. */ -static void -mg_cry_internal_impl(const struct mg_connection *conn, - const char *func, - unsigned line, - const char *fmt, - va_list ap) -{ - char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; - struct mg_file fi; - time_t timestamp; - - /* Unused, in the RELEASE build */ - (void)func; - (void)line; - -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -#endif - - IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap)); - -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic pop -#endif - - buf[sizeof(buf) - 1] = 0; - - DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf); - - if (!conn) { - fputs(buf, stderr); - return; - } - - /* Do not lock when getting the callback value, here and below. - * I suppose this is fine, since function cannot disappear in the - * same way string option can. */ - if ((conn->phys_ctx->callbacks.log_message == NULL) - || (conn->phys_ctx->callbacks.log_message(conn, buf) == 0)) { - - if (conn->dom_ctx->config[ERROR_LOG_FILE] != NULL) { - if (mg_fopen(conn, - conn->dom_ctx->config[ERROR_LOG_FILE], - MG_FOPEN_MODE_APPEND, - &fi) - == 0) { - fi.access.fp = NULL; - } - } else { - fi.access.fp = NULL; - } - - if (fi.access.fp != NULL) { - flockfile(fi.access.fp); - timestamp = time(NULL); - - sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - fprintf(fi.access.fp, - "[%010lu] [error] [client %s] ", - (unsigned long)timestamp, - src_addr); - - if (conn->request_info.request_method != NULL) { - fprintf(fi.access.fp, - "%s %s: ", - conn->request_info.request_method, - conn->request_info.request_uri - ? conn->request_info.request_uri - : ""); - } - - fprintf(fi.access.fp, "%s", buf); - fputc('\n', fi.access.fp); - fflush(fi.access.fp); - funlockfile(fi.access.fp); - (void)mg_fclose(&fi.access); /* Ignore errors. We can't call - * mg_cry here anyway ;-) */ - } - } -} -#else -#error Must either enable filesystems or provide a custom mg_cry_internal_impl implementation -#endif /* Externally provided function */ - - -/* Construct fake connection structure. Used for logging, if connection - * is not applicable at the moment of logging. */ -static struct mg_connection * -fake_connection(struct mg_connection *fc, struct mg_context *ctx) -{ - static const struct mg_connection conn_zero = {0}; - *fc = conn_zero; - fc->phys_ctx = ctx; - fc->dom_ctx = &(ctx->dd); - return fc; -} - - -static void -mg_cry_internal_wrap(const struct mg_connection *conn, - struct mg_context *ctx, - const char *func, - unsigned line, - const char *fmt, - ...) -{ - va_list ap; - va_start(ap, fmt); - if (!conn && ctx) { - struct mg_connection fc; - mg_cry_internal_impl(fake_connection(&fc, ctx), func, line, fmt, ap); - } else { - mg_cry_internal_impl(conn, func, line, fmt, ap); - } - va_end(ap); -} - - -CIVETWEB_API void -mg_cry(const struct mg_connection *conn, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - mg_cry_internal_impl(conn, "user", 0, fmt, ap); - va_end(ap); -} - - -#define mg_cry DO_NOT_USE_THIS_FUNCTION__USE_mg_cry_internal - - -CIVETWEB_API const char * -mg_version(void) -{ - return CIVETWEB_VERSION; -} - - -CIVETWEB_API const struct mg_request_info * -mg_get_request_info(const struct mg_connection *conn) -{ - if (!conn) { - return NULL; - } -#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE) - if (conn->connection_type == CONNECTION_TYPE_RESPONSE) { - char txt[16]; - struct mg_workerTLS *tls = - (struct mg_workerTLS *)pthread_getspecific(sTlsKey); - - sprintf(txt, "%03i", conn->response_info.status_code); - if (strlen(txt) == 3) { - memcpy(tls->txtbuf, txt, 4); - } else { - strcpy(tls->txtbuf, "ERR"); - } - - ((struct mg_connection *)conn)->request_info.local_uri = - tls->txtbuf; /* use thread safe buffer */ - ((struct mg_connection *)conn)->request_info.local_uri_raw = - tls->txtbuf; /* use the same thread safe buffer */ - ((struct mg_connection *)conn)->request_info.request_uri = - tls->txtbuf; /* use the same thread safe buffer */ - - ((struct mg_connection *)conn)->request_info.num_headers = - conn->response_info.num_headers; - memcpy(((struct mg_connection *)conn)->request_info.http_headers, - conn->response_info.http_headers, - sizeof(conn->response_info.http_headers)); - } else -#endif - if (conn->connection_type != CONNECTION_TYPE_REQUEST) { - return NULL; - } - return &conn->request_info; -} - - -CIVETWEB_API const struct mg_response_info * -mg_get_response_info(const struct mg_connection *conn) -{ - if (!conn) { - return NULL; - } - if (conn->connection_type != CONNECTION_TYPE_RESPONSE) { - return NULL; - } - return &conn->response_info; -} - - -static const char * -get_proto_name(const struct mg_connection *conn) -{ -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" - /* Depending on USE_WEBSOCKET and NO_SSL, some oft the protocols might be - * not supported. Clang raises an "unreachable code" warning for parts of ?: - * unreachable, but splitting into four different #ifdef clauses here is - * more complicated. - */ -#endif - - const struct mg_request_info *ri = &conn->request_info; - - const char *proto = ((conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET) - ? (ri->is_ssl ? "wss" : "ws") - : (ri->is_ssl ? "https" : "http")); - - return proto; - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -} - - -static int -mg_construct_local_link(const struct mg_connection *conn, - char *buf, - size_t buflen, - const char *define_proto, - int define_port, - const char *define_uri) -{ - if ((buflen < 1) || (buf == 0) || (conn == 0)) { - return -1; - } else { - int i, j; - int truncated = 0; - const struct mg_request_info *ri = &conn->request_info; - - const char *proto = - (define_proto != NULL) ? define_proto : get_proto_name(conn); - const char *uri = - (define_uri != NULL) - ? define_uri - : ((ri->request_uri != NULL) ? ri->request_uri : ri->local_uri); - int port = (define_port > 0) ? define_port : ri->server_port; - int default_port = 80; - char *uri_encoded; - size_t uri_encoded_len; - - if (uri == NULL) { - return -1; - } - - uri_encoded_len = strlen(uri) * 3 + 1; - uri_encoded = (char *)mg_malloc_ctx(uri_encoded_len, conn->phys_ctx); - if (uri_encoded == NULL) { - return -1; - } - mg_url_encode(uri, uri_encoded, uri_encoded_len); - - /* Directory separator should be preserved. */ - for (i = j = 0; uri_encoded[i]; j++) { - if (!strncmp(uri_encoded + i, "%2f", 3)) { - uri_encoded[j] = '/'; - i += 3; - } else { - uri_encoded[j] = uri_encoded[i++]; - } - } - uri_encoded[j] = '\0'; - -#if defined(USE_X_DOM_SOCKET) - if (conn->client.lsa.sa.sa_family == AF_UNIX) { - /* TODO: Define and document a link for UNIX domain sockets. */ - /* There seems to be no official standard for this. - * Common uses seem to be "httpunix://", "http.unix://" or - * "http+unix://" as a protocol definition string, followed by - * "localhost" or "127.0.0.1" or "/tmp/unix/path" or - * "%2Ftmp%2Funix%2Fpath" (url % encoded) or - * "localhost:%2Ftmp%2Funix%2Fpath" (domain socket path as port) or - * "" (completely skipping the server name part). In any case, the - * last part is the server local path. */ - const char *server_name = UNIX_DOMAIN_SOCKET_SERVER_NAME; - mg_snprintf(conn, - &truncated, - buf, - buflen, - "%s.unix://%s%s", - proto, - server_name, - ri->local_uri); - default_port = 0; - mg_free(uri_encoded); - return 0; - } -#endif - - if (define_proto) { - /* If we got a protocol name, use the default port accordingly. */ - if ((0 == strcmp(define_proto, "https")) - || (0 == strcmp(define_proto, "wss"))) { - default_port = 443; - } - } else if (ri->is_ssl) { - /* If we did not get a protocol name, use TLS as default if it is - * already used. */ - default_port = 443; - } - - { -#if defined(USE_IPV6) - int is_ipv6 = (conn->client.lsa.sa.sa_family == AF_INET6); -#endif - int auth_domain_check_enabled = - conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK] - && (!mg_strcasecmp( - conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes")); - - const char *server_domain = - conn->dom_ctx->config[AUTHENTICATION_DOMAIN]; - - char portstr[16]; - char server_ip[48]; - - if (port != default_port) { - sprintf(portstr, ":%u", (unsigned)port); - } else { - portstr[0] = 0; - } - - if (!auth_domain_check_enabled || !server_domain) { - - sockaddr_to_string(server_ip, - sizeof(server_ip), - &conn->client.lsa); - - server_domain = server_ip; - } - - mg_snprintf(conn, - &truncated, - buf, - buflen, -#if defined(USE_IPV6) - "%s://%s%s%s%s%s", - proto, - (is_ipv6 && (server_domain == server_ip)) ? "[" : "", - server_domain, - (is_ipv6 && (server_domain == server_ip)) ? "]" : "", -#else - "%s://%s%s%s", - proto, - server_domain, -#endif - portstr, - uri_encoded); - - mg_free(uri_encoded); - if (truncated) { - return -1; - } - return 0; - } - } -} - - -CIVETWEB_API int -mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen) -{ - return mg_construct_local_link(conn, buf, buflen, NULL, -1, NULL); -} - - -/* Skip the characters until one of the delimiters characters found. - * 0-terminate resulting word. Skip the delimiter and following whitespaces. - * Advance pointer to buffer to the next word. Return found 0-terminated - * word. - * Delimiters can be quoted with quotechar. */ -static char * -skip_quoted(char **buf, - const char *delimiters, - const char *whitespace, - char quotechar) -{ - char *p, *begin_word, *end_word, *end_whitespace; - - begin_word = *buf; - end_word = begin_word + strcspn(begin_word, delimiters); - - /* Check for quotechar */ - if (end_word > begin_word) { - p = end_word - 1; - while (*p == quotechar) { - /* While the delimiter is quoted, look for the next delimiter. */ - /* This happens, e.g., in calls from parse_auth_header, - * if the user name contains a " character. */ - - /* If there is anything beyond end_word, copy it. */ - if (*end_word != '\0') { - size_t end_off = strcspn(end_word + 1, delimiters); - memmove(p, end_word, end_off + 1); - p += end_off; /* p must correspond to end_word - 1 */ - end_word += end_off + 1; - } else { - *p = '\0'; - break; - } - } - for (p++; p < end_word; p++) { - *p = '\0'; - } - } - - if (*end_word == '\0') { - *buf = end_word; - } else { - -#if defined(GCC_DIAGNOSTIC) - /* Disable spurious conversion warning for GCC */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#endif /* defined(GCC_DIAGNOSTIC) */ - - end_whitespace = end_word + strspn(&end_word[1], whitespace) + 1; - -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic pop -#endif /* defined(GCC_DIAGNOSTIC) */ - - for (p = end_word; p < end_whitespace; p++) { - *p = '\0'; - } - - *buf = end_whitespace; - } - - return begin_word; -} - - -/* Return HTTP header value, or NULL if not found. */ -static const char * -get_header(const struct mg_header *hdr, int num_hdr, const char *name) -{ - int i; - for (i = 0; i < num_hdr; i++) { - if (!mg_strcasecmp(name, hdr[i].name)) { - return hdr[i].value; - } - } - - return NULL; -} - - -/* Retrieve requested HTTP header multiple values, and return the number of - * found occurrences */ -static int -get_req_headers(const struct mg_request_info *ri, - const char *name, - const char **output, - int output_max_size) -{ - int i; - int cnt = 0; - if (ri) { - for (i = 0; i < ri->num_headers && cnt < output_max_size; i++) { - if (!mg_strcasecmp(name, ri->http_headers[i].name)) { - output[cnt++] = ri->http_headers[i].value; - } - } - } - return cnt; -} - - -CIVETWEB_API const char * -mg_get_header(const struct mg_connection *conn, const char *name) -{ - if (!conn) { - return NULL; - } - - if (conn->connection_type == CONNECTION_TYPE_REQUEST) { - return get_header(conn->request_info.http_headers, - conn->request_info.num_headers, - name); - } - if (conn->connection_type == CONNECTION_TYPE_RESPONSE) { - return get_header(conn->response_info.http_headers, - conn->response_info.num_headers, - name); - } - return NULL; -} - - -static const char * -get_http_version(const struct mg_connection *conn) -{ - if (!conn) { - return NULL; - } - - if (conn->connection_type == CONNECTION_TYPE_REQUEST) { - return conn->request_info.http_version; - } - if (conn->connection_type == CONNECTION_TYPE_RESPONSE) { - return conn->response_info.http_version; - } - return NULL; -} - - -/* A helper function for traversing a comma separated list of values. - * It returns a list pointer shifted to the next value, or NULL if the end - * of the list found. - * Value is stored in val vector. If value has form "x=y", then eq_val - * vector is initialized to point to the "y" part, and val vector length - * is adjusted to point only to "x". */ -static const char * -next_option(const char *list, struct vec *val, struct vec *eq_val) -{ - int end; - -reparse: - if (val == NULL || list == NULL || *list == '\0') { - /* End of the list */ - return NULL; - } - - /* Skip over leading LWS */ - while (*list == ' ' || *list == '\t') - list++; - - val->ptr = list; - if ((list = strchr(val->ptr, ',')) != NULL) { - /* Comma found. Store length and shift the list ptr */ - val->len = ((size_t)(list - val->ptr)); - list++; - } else { - /* This value is the last one */ - list = val->ptr + strlen(val->ptr); - val->len = ((size_t)(list - val->ptr)); - } - - /* Adjust length for trailing LWS */ - end = (int)val->len - 1; - while (end >= 0 && ((val->ptr[end] == ' ') || (val->ptr[end] == '\t'))) - end--; - val->len = (size_t)(end) + (size_t)(1); - - if (val->len == 0) { - /* Ignore any empty entries. */ - goto reparse; - } - - if (eq_val != NULL) { - /* Value has form "x=y", adjust pointers and lengths - * so that val points to "x", and eq_val points to "y". */ - eq_val->len = 0; - eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len); - if (eq_val->ptr != NULL) { - eq_val->ptr++; /* Skip over '=' character */ - eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len; - val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1; - } - } - - return list; -} - - -/* A helper function for checking if a comma separated list of values - * contains - * the given option (case insensitvely). - * 'header' can be NULL, in which case false is returned. */ -static int -header_has_option(const char *header, const char *option) -{ - struct vec opt_vec; - struct vec eq_vec; - - DEBUG_ASSERT(option != NULL); - DEBUG_ASSERT(option[0] != '\0'); - - while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) { - if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0) - return 1; - } - - return 0; -} - - -/* Sorting function implemented in a separate file */ -#include "sort.inl" - -/* Pattern matching has been reimplemented in a new file */ -#include "match.inl" - - -/* HTTP 1.1 assumes keep alive if "Connection:" header is not set - * This function must tolerate situations when connection info is not - * set up, for example if request parsing failed. */ -static int -should_keep_alive(const struct mg_connection *conn) -{ - const char *http_version; - const char *header; - - /* First satisfy needs of the server */ - if ((conn == NULL) || conn->must_close) { - /* Close, if civetweb framework needs to close */ - return 0; - } - - if (mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0) { - /* Close, if keep alive is not enabled */ - return 0; - } - - /* Check explicit wish of the client */ - header = mg_get_header(conn, "Connection"); - if (header) { - /* If there is a connection header from the client, obey */ - if (header_has_option(header, "keep-alive")) { - return 1; - } - return 0; - } - - /* Use default of the standard */ - http_version = get_http_version(conn); - if (http_version && (0 == strcmp(http_version, "1.1"))) { - /* HTTP 1.1 default is keep alive */ - return 1; - } - - /* HTTP 1.0 (and earlier) default is to close the connection */ - return 0; -} - - -static int -should_decode_url(const struct mg_connection *conn) -{ - if (!conn || !conn->dom_ctx) { - return 0; - } - - return (mg_strcasecmp(conn->dom_ctx->config[DECODE_URL], "yes") == 0); -} - - -static int -should_decode_query_string(const struct mg_connection *conn) -{ - if (!conn || !conn->dom_ctx) { - return 0; - } - - return (mg_strcasecmp(conn->dom_ctx->config[DECODE_QUERY_STRING], "yes") - == 0); -} - - -static const char * -suggest_connection_header(const struct mg_connection *conn) -{ - return should_keep_alive(conn) ? "keep-alive" : "close"; -} - - -#include "response.inl" - - -static void -send_no_cache_header(struct mg_connection *conn) -{ - /* Send all current and obsolete cache opt-out directives. */ - mg_response_header_add(conn, - "Cache-Control", - "no-cache, no-store, " - "must-revalidate, private, max-age=0", - -1); - mg_response_header_add(conn, "Expires", "0", -1); - - if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) { - /* Obsolete, but still send it for HTTP/1.0 */ - mg_response_header_add(conn, "Pragma", "no-cache", -1); - } -} - - -static void -send_static_cache_header(struct mg_connection *conn) -{ -#if !defined(NO_CACHING) - int max_age; - char val[64]; - - const char *cache_control = - conn->dom_ctx->config[STATIC_FILE_CACHE_CONTROL]; - - /* If there is a full cache-control option configured,0 use it */ - if (cache_control != NULL) { - mg_response_header_add(conn, "Cache-Control", cache_control, -1); - return; - } - - /* Read the server config to check how long a file may be cached. - * The configuration is in seconds. */ - max_age = atoi(conn->dom_ctx->config[STATIC_FILE_MAX_AGE]); - if (max_age <= 0) { - /* 0 means "do not cache". All values <0 are reserved - * and may be used differently in the future. */ - /* If a file should not be cached, do not only send - * max-age=0, but also pragmas and Expires headers. */ - send_no_cache_header(conn); - return; - } - - /* Use "Cache-Control: max-age" instead of "Expires" header. - * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */ - /* See also https://www.mnot.net/cache_docs/ */ - /* According to RFC 2616, Section 14.21, caching times should not exceed - * one year. A year with 365 days corresponds to 31536000 seconds, a - * leap - * year to 31622400 seconds. For the moment, we just send whatever has - * been configured, still the behavior for >1 year should be considered - * as undefined. */ - mg_snprintf( - conn, NULL, val, sizeof(val), "max-age=%lu", (unsigned long)max_age); - mg_response_header_add(conn, "Cache-Control", val, -1); - -#else /* NO_CACHING */ - - send_no_cache_header(conn); -#endif /* !NO_CACHING */ -} - - -static void -send_additional_header(struct mg_connection *conn) -{ - const char *header = conn->dom_ctx->config[ADDITIONAL_HEADER]; - -#if !defined(NO_SSL) - if (conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]) { - long max_age = atol(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]); - if (max_age >= 0) { - char val[64]; - mg_snprintf(conn, - NULL, - val, - sizeof(val), - "max-age=%lu", - (unsigned long)max_age); - mg_response_header_add(conn, "Strict-Transport-Security", val, -1); - } - } -#endif - - // Content-Security-Policy - - if (header && header[0]) { - mg_response_header_add_lines(conn, header); - } -} - - -static void -send_cors_header(struct mg_connection *conn) -{ - const char *origin_hdr = mg_get_header(conn, "Origin"); - const char *cors_orig_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; - const char *cors_cred_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; - const char *cors_hdr_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS]; - const char *cors_exphdr_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; - const char *cors_meth_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; - - if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { - /* Cross-origin resource sharing (CORS), see - * http://www.html5rocks.com/en/tutorials/cors/, - * http://www.html5rocks.com/static/images/cors_server_flowchart.png - * CORS preflight is not supported for files. */ - mg_response_header_add(conn, - "Access-Control-Allow-Origin", - cors_orig_cfg, - -1); - } - - if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) { - /* Cross-origin resource sharing (CORS), see - * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials - */ - mg_response_header_add(conn, - "Access-Control-Allow-Credentials", - cors_cred_cfg, - -1); - } - - if (cors_hdr_cfg && *cors_hdr_cfg) { - mg_response_header_add(conn, - "Access-Control-Allow-Headers", - cors_hdr_cfg, - -1); - } - - if (cors_exphdr_cfg && *cors_exphdr_cfg) { - mg_response_header_add(conn, - "Access-Control-Expose-Headers", - cors_exphdr_cfg, - -1); - } - - if (cors_meth_cfg && *cors_meth_cfg) { - mg_response_header_add(conn, - "Access-Control-Allow-Methods", - cors_meth_cfg, - -1); - } -} - - -#if !defined(NO_FILESYSTEMS) -static void handle_file_based_request(struct mg_connection *conn, - const char *path, - struct mg_file *filep); -#endif /* NO_FILESYSTEMS */ - - -CIVETWEB_API const char * -mg_get_response_code_text(const struct mg_connection *conn, int response_code) -{ - /* See IANA HTTP status code assignment: - * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml - */ - - switch (response_code) { - /* RFC2616 Section 10.1 - Informational 1xx */ - case 100: - return "Continue"; /* RFC2616 Section 10.1.1 */ - case 101: - return "Switching Protocols"; /* RFC2616 Section 10.1.2 */ - case 102: - return "Processing"; /* RFC2518 Section 10.1 */ - - /* RFC2616 Section 10.2 - Successful 2xx */ - case 200: - return "OK"; /* RFC2616 Section 10.2.1 */ - case 201: - return "Created"; /* RFC2616 Section 10.2.2 */ - case 202: - return "Accepted"; /* RFC2616 Section 10.2.3 */ - case 203: - return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */ - case 204: - return "No Content"; /* RFC2616 Section 10.2.5 */ - case 205: - return "Reset Content"; /* RFC2616 Section 10.2.6 */ - case 206: - return "Partial Content"; /* RFC2616 Section 10.2.7 */ - case 207: - return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 - */ - case 208: - return "Already Reported"; /* RFC5842 Section 7.1 */ - - case 226: - return "IM used"; /* RFC3229 Section 10.4.1 */ - - /* RFC2616 Section 10.3 - Redirection 3xx */ - case 300: - return "Multiple Choices"; /* RFC2616 Section 10.3.1 */ - case 301: - return "Moved Permanently"; /* RFC2616 Section 10.3.2 */ - case 302: - return "Found"; /* RFC2616 Section 10.3.3 */ - case 303: - return "See Other"; /* RFC2616 Section 10.3.4 */ - case 304: - return "Not Modified"; /* RFC2616 Section 10.3.5 */ - case 305: - return "Use Proxy"; /* RFC2616 Section 10.3.6 */ - case 307: - return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */ - case 308: - return "Permanent Redirect"; /* RFC7238 Section 3 */ - - /* RFC2616 Section 10.4 - Client Error 4xx */ - case 400: - return "Bad Request"; /* RFC2616 Section 10.4.1 */ - case 401: - return "Unauthorized"; /* RFC2616 Section 10.4.2 */ - case 402: - return "Payment Required"; /* RFC2616 Section 10.4.3 */ - case 403: - return "Forbidden"; /* RFC2616 Section 10.4.4 */ - case 404: - return "Not Found"; /* RFC2616 Section 10.4.5 */ - case 405: - return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */ - case 406: - return "Not Acceptable"; /* RFC2616 Section 10.4.7 */ - case 407: - return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */ - case 408: - return "Request Time-out"; /* RFC2616 Section 10.4.9 */ - case 409: - return "Conflict"; /* RFC2616 Section 10.4.10 */ - case 410: - return "Gone"; /* RFC2616 Section 10.4.11 */ - case 411: - return "Length Required"; /* RFC2616 Section 10.4.12 */ - case 412: - return "Precondition Failed"; /* RFC2616 Section 10.4.13 */ - case 413: - return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */ - case 414: - return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */ - case 415: - return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */ - case 416: - return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 - */ - case 417: - return "Expectation Failed"; /* RFC2616 Section 10.4.18 */ - - case 421: - return "Misdirected Request"; /* RFC7540 Section 9.1.2 */ - case 422: - return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918 - * Section 11.2 */ - case 423: - return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */ - case 424: - return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918 - * Section 11.4 */ - - case 426: - return "Upgrade Required"; /* RFC 2817 Section 4 */ - - case 428: - return "Precondition Required"; /* RFC 6585, Section 3 */ - case 429: - return "Too Many Requests"; /* RFC 6585, Section 4 */ - - case 431: - return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */ - - case 451: - return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05, - * Section 3 */ - - /* RFC2616 Section 10.5 - Server Error 5xx */ - case 500: - return "Internal Server Error"; /* RFC2616 Section 10.5.1 */ - case 501: - return "Not Implemented"; /* RFC2616 Section 10.5.2 */ - case 502: - return "Bad Gateway"; /* RFC2616 Section 10.5.3 */ - case 503: - return "Service Unavailable"; /* RFC2616 Section 10.5.4 */ - case 504: - return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */ - case 505: - return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */ - case 506: - return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */ - case 507: - return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918 - * Section 11.5 */ - case 508: - return "Loop Detected"; /* RFC5842 Section 7.1 */ - - case 510: - return "Not Extended"; /* RFC 2774, Section 7 */ - case 511: - return "Network Authentication Required"; /* RFC 6585, Section 6 */ - - /* Other status codes, not shown in the IANA HTTP status code - * assignment. - * E.g., "de facto" standards due to common use, ... */ - case 418: - return "I am a teapot"; /* RFC2324 Section 2.3.2 */ - case 419: - return "Authentication Timeout"; /* common use */ - case 420: - return "Enhance Your Calm"; /* common use */ - case 440: - return "Login Timeout"; /* common use */ - case 509: - return "Bandwidth Limit Exceeded"; /* common use */ - - default: - /* This error code is unknown. This should not happen. */ - if (conn) { - mg_cry_internal(conn, - "Unknown HTTP response code: %u", - response_code); - } - - /* Return at least a category according to RFC 2616 Section 10. */ - if (response_code >= 100 && response_code < 200) { - /* Unknown informational status code */ - return "Information"; - } - if (response_code >= 200 && response_code < 300) { - /* Unknown success code */ - return "Success"; - } - if (response_code >= 300 && response_code < 400) { - /* Unknown redirection code */ - return "Redirection"; - } - if (response_code >= 400 && response_code < 500) { - /* Unknown request error code */ - return "Client Error"; - } - if (response_code >= 500 && response_code < 600) { - /* Unknown server error code */ - return "Server Error"; - } - - /* Response code not even within reasonable range */ - return ""; - } -} - - -static int -mg_send_http_error_impl(struct mg_connection *conn, - int status, - const char *fmt, - va_list args) -{ - char errmsg_buf[MG_BUF_LEN]; - va_list ap; - int has_body; - -#if !defined(NO_FILESYSTEMS) - char path_buf[UTF8_PATH_MAX]; - int len, i, page_handler_found, scope, truncated; - const char *error_handler = NULL; - struct mg_file error_page_file = STRUCT_FILE_INITIALIZER; - const char *error_page_file_ext, *tstr; -#endif /* NO_FILESYSTEMS */ - int handled_by_callback = 0; - - if ((conn == NULL) || (fmt == NULL)) { - return -2; - } - - /* Set status (for log) */ - conn->status_code = status; - - /* Errors 1xx, 204 and 304 MUST NOT send a body */ - has_body = ((status > 199) && (status != 204) && (status != 304)); - - /* Prepare message in buf, if required */ - if (has_body - || (!conn->in_error_handler - && (conn->phys_ctx->callbacks.http_error != NULL))) { - /* Store error message in errmsg_buf */ - va_copy(ap, args); - mg_vsnprintf(conn, NULL, errmsg_buf, sizeof(errmsg_buf), fmt, ap); - va_end(ap); - /* In a debug build, print all html errors */ - DEBUG_TRACE("Error %i - [%s]", status, errmsg_buf); - } - - /* If there is a http_error callback, call it. - * But don't do it recursively, if callback calls mg_send_http_error again. - */ - if (!conn->in_error_handler - && (conn->phys_ctx->callbacks.http_error != NULL)) { - /* Mark in_error_handler to avoid recursion and call user callback. */ - conn->in_error_handler = 1; - handled_by_callback = - (conn->phys_ctx->callbacks.http_error(conn, status, errmsg_buf) - == 0); - conn->in_error_handler = 0; - } - - if (!handled_by_callback) { - /* Check for recursion */ - if (conn->in_error_handler) { - DEBUG_TRACE( - "Recursion when handling error %u - fall back to default", - status); -#if !defined(NO_FILESYSTEMS) - } else { - /* Send user defined error pages, if defined */ - error_handler = conn->dom_ctx->config[ERROR_PAGES]; - error_page_file_ext = conn->dom_ctx->config[INDEX_FILES]; - page_handler_found = 0; - - if (error_handler != NULL) { - for (scope = 1; (scope <= 3) && !page_handler_found; scope++) { - switch (scope) { - case 1: /* Handler for specific error, e.g. 404 error */ - mg_snprintf(conn, - &truncated, - path_buf, - sizeof(path_buf) - 32, - "%serror%03u.", - error_handler, - status); - break; - case 2: /* Handler for error group, e.g., 5xx error - * handler - * for all server errors (500-599) */ - mg_snprintf(conn, - &truncated, - path_buf, - sizeof(path_buf) - 32, - "%serror%01uxx.", - error_handler, - status / 100); - break; - default: /* Handler for all errors */ - mg_snprintf(conn, - &truncated, - path_buf, - sizeof(path_buf) - 32, - "%serror.", - error_handler); - break; - } - - /* String truncation in buf may only occur if - * error_handler is too long. This string is - * from the config, not from a client. */ - (void)truncated; - - /* The following code is redundant, but it should avoid - * false positives in static source code analyzers and - * vulnerability scanners. - */ - path_buf[sizeof(path_buf) - 32] = 0; - len = (int)strlen(path_buf); - if (len > (int)sizeof(path_buf) - 32) { - len = (int)sizeof(path_buf) - 32; - } - - /* Start with the file extension from the configuration. */ - tstr = strchr(error_page_file_ext, '.'); - - while (tstr) { - for (i = 1; - (i < 32) && (tstr[i] != 0) && (tstr[i] != ','); - i++) { - /* buffer overrun is not possible here, since - * (i < 32) && (len < sizeof(path_buf) - 32) - * ==> (i + len) < sizeof(path_buf) */ - path_buf[len + i - 1] = tstr[i]; - } - /* buffer overrun is not possible here, since - * (i <= 32) && (len < sizeof(path_buf) - 32) - * ==> (i + len) <= sizeof(path_buf) */ - path_buf[len + i - 1] = 0; - - if (mg_stat(conn, path_buf, &error_page_file.stat)) { - DEBUG_TRACE("Check error page %s - found", - path_buf); - page_handler_found = 1; - break; - } - DEBUG_TRACE("Check error page %s - not found", - path_buf); - - /* Continue with the next file extension from the - * configuration (if there is a next one). */ - tstr = strchr(tstr + i, '.'); - } - } - } - - if (page_handler_found) { - conn->in_error_handler = 1; - handle_file_based_request(conn, path_buf, &error_page_file); - conn->in_error_handler = 0; - return 0; - } -#endif /* NO_FILESYSTEMS */ - } - - /* No custom error page. Send default error page. */ - conn->must_close = 1; - mg_response_header_start(conn, status); - send_no_cache_header(conn); - send_additional_header(conn); - send_cors_header(conn); - if (has_body) { - mg_response_header_add(conn, - "Content-Type", - "text/plain; charset=utf-8", - -1); - } - mg_response_header_send(conn); - - /* HTTP responses 1xx, 204 and 304 MUST NOT send a body */ - if (has_body) { - /* For other errors, send a generic error message. */ - const char *status_text = mg_get_response_code_text(conn, status); - mg_printf(conn, "Error %d: %s\n", status, status_text); - mg_write(conn, errmsg_buf, strlen(errmsg_buf)); - - } else { - /* No body allowed. Close the connection. */ - DEBUG_TRACE("Error %i", status); - } - } - return 0; -} - - -CIVETWEB_API int -mg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = mg_send_http_error_impl(conn, status, fmt, ap); - va_end(ap); - - return ret; -} - - -CIVETWEB_API int -mg_send_http_ok(struct mg_connection *conn, - const char *mime_type, - long long content_length) -{ - if ((mime_type == NULL) || (*mime_type == 0)) { - /* No content type defined: default to text/html */ - mime_type = "text/html"; - } - - mg_response_header_start(conn, 200); - send_no_cache_header(conn); - send_additional_header(conn); - send_cors_header(conn); - mg_response_header_add(conn, "Content-Type", mime_type, -1); - if (content_length < 0) { - /* Size not known. Use chunked encoding (HTTP/1.x) */ - if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) { - /* Only HTTP/1.x defines "chunked" encoding, HTTP/2 does not*/ - mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1); - } - } else { - char len[32]; - int trunc = 0; - mg_snprintf(conn, - &trunc, - len, - sizeof(len), - "%" UINT64_FMT, - (uint64_t)content_length); - if (!trunc) { - /* Since 32 bytes is enough to hold any 64 bit decimal number, - * !trunc is always true */ - mg_response_header_add(conn, "Content-Length", len, -1); - } - } - mg_response_header_send(conn); - - return 0; -} - - -CIVETWEB_API int -mg_send_http_redirect(struct mg_connection *conn, - const char *target_url, - int redirect_code) -{ - /* Send a 30x redirect response. - * - * Redirect types (status codes): - * - * Status | Perm/Temp | Method | Version - * 301 | permanent | POST->GET undefined | HTTP/1.0 - * 302 | temporary | POST->GET undefined | HTTP/1.0 - * 303 | temporary | always use GET | HTTP/1.1 - * 307 | temporary | always keep method | HTTP/1.1 - * 308 | permanent | always keep method | HTTP/1.1 - */ - -#if defined(MG_SEND_REDIRECT_BODY) - char redirect_body[MG_BUF_LEN]; - size_t content_len = 0; - char content_len_text[32]; -#endif - - /* In case redirect_code=0, use 307. */ - if (redirect_code == 0) { - redirect_code = 307; - } - - /* In case redirect_code is none of the above, return error. */ - if ((redirect_code != 301) && (redirect_code != 302) - && (redirect_code != 303) && (redirect_code != 307) - && (redirect_code != 308)) { - /* Parameter error */ - return -2; - } - - /* If target_url is not defined, redirect to "/". */ - if ((target_url == NULL) || (*target_url == 0)) { - target_url = "/"; - } - -#if defined(MG_SEND_REDIRECT_BODY) - /* TODO: condition name? */ - - /* Prepare a response body with a hyperlink. - * - * According to RFC2616 (and RFC1945 before): - * Unless the request method was HEAD, the entity of the - * response SHOULD contain a short hypertext note with a hyperlink to - * the new URI(s). - * - * However, this response body is not useful in M2M communication. - * Probably the original reason in the RFC was, clients not supporting - * a 30x HTTP redirect could still show the HTML page and let the user - * press the link. Since current browsers support 30x HTTP, the additional - * HTML body does not seem to make sense anymore. - * - * The new RFC7231 (Section 6.4) does no longer recommend it ("SHOULD"), - * but it only notes: - * The server's response payload usually contains a short - * hypertext note with a hyperlink to the new URI(s). - * - * Deactivated by default. If you need the 30x body, set the define. - */ - mg_snprintf( - conn, - NULL /* ignore truncation */, - redirect_body, - sizeof(redirect_body), - "%s%s", - redirect_text, - target_url, - target_url); - content_len = strlen(reply); - sprintf(content_len_text, "%lu", (unsigned long)content_len); -#endif - - /* Send all required headers */ - mg_response_header_start(conn, redirect_code); - mg_response_header_add(conn, "Location", target_url, -1); - if ((redirect_code == 301) || (redirect_code == 308)) { - /* Permanent redirect */ - send_static_cache_header(conn); - } else { - /* Temporary redirect */ - send_no_cache_header(conn); - } - send_additional_header(conn); - send_cors_header(conn); -#if defined(MG_SEND_REDIRECT_BODY) - mg_response_header_add(conn, "Content-Type", "text/html", -1); - mg_response_header_add(conn, "Content-Length", content_len_text, -1); -#else - mg_response_header_add(conn, "Content-Length", "0", 1); -#endif - mg_response_header_send(conn); - -#if defined(MG_SEND_REDIRECT_BODY) - /* Send response body */ - /* ... unless it is a HEAD request */ - if (0 != strcmp(conn->request_info.request_method, "HEAD")) { - ret = mg_write(conn, redirect_body, content_len); - } -#endif - - return 1; -} - - -#if defined(_WIN32) -/* Create substitutes for POSIX functions in Win32. */ - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif - - -static int -pthread_mutex_init(pthread_mutex_t *mutex, void *unused) -{ - (void)unused; - /* Always initialize as PTHREAD_MUTEX_RECURSIVE */ - InitializeCriticalSection(&mutex->sec); - return 0; -} - - -static int -pthread_mutex_destroy(pthread_mutex_t *mutex) -{ - DeleteCriticalSection(&mutex->sec); - return 0; -} - - -static int -pthread_mutex_lock(pthread_mutex_t *mutex) -{ - EnterCriticalSection(&mutex->sec); - return 0; -} - - -static int -pthread_mutex_unlock(pthread_mutex_t *mutex) -{ - LeaveCriticalSection(&mutex->sec); - return 0; -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_cond_init(pthread_cond_t *cv, const void *unused) -{ - (void)unused; - (void)pthread_mutex_init(&cv->threadIdSec, &pthread_mutex_attr); - cv->waiting_thread = NULL; - return 0; -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_cond_timedwait(pthread_cond_t *cv, - pthread_mutex_t *mutex, - FUNCTION_MAY_BE_UNUSED const struct timespec *abstime) -{ - struct mg_workerTLS **ptls, - *tls = (struct mg_workerTLS *)pthread_getspecific(sTlsKey); - int ok; - uint64_t nsnow, nswaitabs; - int64_t nswaitrel; - DWORD mswaitrel; - - pthread_mutex_lock(&cv->threadIdSec); - /* Add this thread to cv's waiting list */ - ptls = &cv->waiting_thread; - for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread) - ; - tls->next_waiting_thread = NULL; - *ptls = tls; - pthread_mutex_unlock(&cv->threadIdSec); - - if (abstime) { - nsnow = mg_get_current_time_ns(); - nswaitabs = - (((uint64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec; - nswaitrel = nswaitabs - nsnow; - if (nswaitrel < 0) { - nswaitrel = 0; - } - mswaitrel = (DWORD)(nswaitrel / 1000000); - } else { - mswaitrel = (DWORD)INFINITE; - } - - pthread_mutex_unlock(mutex); - ok = (WAIT_OBJECT_0 - == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel)); - if (!ok) { - ok = 1; - pthread_mutex_lock(&cv->threadIdSec); - ptls = &cv->waiting_thread; - for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread) { - if (*ptls == tls) { - *ptls = tls->next_waiting_thread; - ok = 0; - break; - } - } - pthread_mutex_unlock(&cv->threadIdSec); - if (ok) { - WaitForSingleObject(tls->pthread_cond_helper_mutex, - (DWORD)INFINITE); - } - } - /* This thread has been removed from cv's waiting list */ - pthread_mutex_lock(mutex); - - return ok ? 0 : -1; -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) -{ - return pthread_cond_timedwait(cv, mutex, NULL); -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_cond_signal(pthread_cond_t *cv) -{ - HANDLE wkup = NULL; - BOOL ok = FALSE; - - pthread_mutex_lock(&cv->threadIdSec); - if (cv->waiting_thread) { - wkup = cv->waiting_thread->pthread_cond_helper_mutex; - cv->waiting_thread = cv->waiting_thread->next_waiting_thread; - - ok = SetEvent(wkup); - DEBUG_ASSERT(ok); - } - pthread_mutex_unlock(&cv->threadIdSec); - - return ok ? 0 : 1; -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_cond_broadcast(pthread_cond_t *cv) -{ - pthread_mutex_lock(&cv->threadIdSec); - while (cv->waiting_thread) { - pthread_cond_signal(cv); - } - pthread_mutex_unlock(&cv->threadIdSec); - - return 0; -} - - -FUNCTION_MAY_BE_UNUSED -static int -pthread_cond_destroy(pthread_cond_t *cv) -{ - pthread_mutex_lock(&cv->threadIdSec); - DEBUG_ASSERT(cv->waiting_thread == NULL); - pthread_mutex_unlock(&cv->threadIdSec); - pthread_mutex_destroy(&cv->threadIdSec); - - return 0; -} - - -#if defined(ALTERNATIVE_QUEUE) -FUNCTION_MAY_BE_UNUSED -static void * -event_create(void) -{ - return (void *)CreateEvent(NULL, FALSE, FALSE, NULL); -} - - -FUNCTION_MAY_BE_UNUSED -static int -event_wait(void *eventhdl) -{ - int res = WaitForSingleObject((HANDLE)eventhdl, (DWORD)INFINITE); - return (res == WAIT_OBJECT_0); -} - - -FUNCTION_MAY_BE_UNUSED -static int -event_signal(void *eventhdl) -{ - return (int)SetEvent((HANDLE)eventhdl); -} - - -FUNCTION_MAY_BE_UNUSED -static void -event_destroy(void *eventhdl) -{ - CloseHandle((HANDLE)eventhdl); -} -#endif - - -#if defined(GCC_DIAGNOSTIC) -/* Enable unused function warning again */ -#pragma GCC diagnostic pop -#endif - - -/* For Windows, change all slashes to backslashes in path names. */ -static void -change_slashes_to_backslashes(char *path) -{ - int i; - - for (i = 0; path[i] != '\0'; i++) { - if (path[i] == '/') { - path[i] = '\\'; - } - - /* remove double backslash (check i > 0 to preserve UNC paths, - * like \\server\file.txt) */ - if ((i > 0) && (path[i] == '\\')) { - while ((path[i + 1] == '\\') || (path[i + 1] == '/')) { - (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1)); - } - } - } -} - - -static int -mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2) -{ - int diff; - - do { - diff = ((*s1 >= L'A') && (*s1 <= L'Z') ? (*s1 - L'A' + L'a') : *s1) - - ((*s2 >= L'A') && (*s2 <= L'Z') ? (*s2 - L'A' + L'a') : *s2); - s1++; - s2++; - } while ((diff == 0) && (s1[-1] != L'\0')); - - return diff; -} - - -/* Encode 'path' which is assumed UTF-8 string, into UNICODE string. - * wbuf and wbuf_len is a target buffer and its length. */ -static void -path_to_unicode(const struct mg_connection *conn, - const char *path, - wchar_t *wbuf, - size_t wbuf_len) -{ - char buf[UTF8_PATH_MAX], buf2[UTF8_PATH_MAX]; - wchar_t wbuf2[UTF16_PATH_MAX + 1]; - DWORD long_len, err; - int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp; - - mg_strlcpy(buf, path, sizeof(buf)); - change_slashes_to_backslashes(buf); - - /* Convert to Unicode and back. If doubly-converted string does not - * match the original, something is fishy, reject. */ - memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len); - WideCharToMultiByte( - CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL); - if (strcmp(buf, buf2) != 0) { - wbuf[0] = L'\0'; - } - - /* Windows file systems are not case sensitive, but you can still use - * uppercase and lowercase letters (on all modern file systems). - * The server can check if the URI uses the same upper/lowercase - * letters an the file system, effectively making Windows servers - * case sensitive (like Linux servers are). It is still not possible - * to use two files with the same name in different cases on Windows - * (like /a and /A) - this would be possible in Linux. - * As a default, Windows is not case sensitive, but the case sensitive - * file name check can be activated by an additional configuration. */ - if (conn) { - if (conn->dom_ctx->config[CASE_SENSITIVE_FILES] - && !mg_strcasecmp(conn->dom_ctx->config[CASE_SENSITIVE_FILES], - "yes")) { - /* Use case sensitive compare function */ - fcompare = wcscmp; - } - } - (void)conn; /* conn is currently unused */ - - /* Only accept a full file path, not a Windows short (8.3) path. */ - memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t)); - long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1); - if (long_len == 0) { - err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) { - /* File does not exist. This is not always a problem here. */ - return; - } - } - if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) { - /* Short name is used. */ - wbuf[0] = L'\0'; - } -} - - -#if !defined(NO_FILESYSTEMS) -/* Get file information, return 1 if file exists, 0 if not */ -static int -mg_stat(const struct mg_connection *conn, - const char *path, - struct mg_file_stat *filep) -{ - wchar_t wbuf[UTF16_PATH_MAX]; - WIN32_FILE_ATTRIBUTE_DATA info; - time_t creation_time; - size_t len; - - if (!filep) { - return 0; - } - memset(filep, 0, sizeof(*filep)); - - if (mg_path_suspicious(conn, path)) { - return 0; - } - - path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); - /* Windows happily opens files with some garbage at the end of file name. - * For example, fopen("a.cgi ", "r") on Windows successfully opens - * "a.cgi", despite one would expect an error back. */ - len = strlen(path); - if ((len > 0) && (path[len - 1] != ' ') && (path[len - 1] != '.') - && (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0)) { - filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); - filep->last_modified = - SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime, - info.ftLastWriteTime.dwHighDateTime); - - /* On Windows, the file creation time can be higher than the - * modification time, e.g. when a file is copied. - * Since the Last-Modified timestamp is used for caching - * it should be based on the most recent timestamp. */ - creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime, - info.ftCreationTime.dwHighDateTime); - if (creation_time > filep->last_modified) { - filep->last_modified = creation_time; - } - - filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - return 1; - } - - return 0; -} -#endif - - -static int -mg_remove(const struct mg_connection *conn, const char *path) -{ - wchar_t wbuf[UTF16_PATH_MAX]; - path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); - return DeleteFileW(wbuf) ? 0 : -1; -} - - -static int -mg_mkdir(const struct mg_connection *conn, const char *path, int mode) -{ - wchar_t wbuf[UTF16_PATH_MAX]; - (void)mode; - path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); - return CreateDirectoryW(wbuf, NULL) ? 0 : -1; -} - - -/* Create substitutes for POSIX functions in Win32. */ - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif - - -/* Implementation of POSIX opendir/closedir/readdir for Windows. */ -FUNCTION_MAY_BE_UNUSED -static DIR * -mg_opendir(const struct mg_connection *conn, const char *name) -{ - DIR *dir = NULL; - wchar_t wpath[UTF16_PATH_MAX]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath)); - attrs = GetFileAttributesW(wpath); - if ((wcslen(wpath) + 2 < ARRAY_SIZE(wpath)) && (attrs != 0xFFFFFFFF) - && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)) { - (void)wcscat(wpath, L"\\*"); - dir->handle = FindFirstFileW(wpath, &dir->info); - dir->result.d_name[0] = '\0'; - } else { - mg_free(dir); - dir = NULL; - } - } - - return dir; -} - - -FUNCTION_MAY_BE_UNUSED -static int -mg_closedir(DIR *dir) -{ - int result = 0; - - if (dir != NULL) { - if (dir->handle != INVALID_HANDLE_VALUE) - result = FindClose(dir->handle) ? 0 : -1; - - mg_free(dir); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return result; -} - - -FUNCTION_MAY_BE_UNUSED -static struct dirent * -mg_readdir(DIR *dir) -{ - struct dirent *result = 0; - - if (dir) { - if (dir->handle != INVALID_HANDLE_VALUE) { - result = &dir->result; - (void)WideCharToMultiByte(CP_UTF8, - 0, - dir->info.cFileName, - -1, - result->d_name, - sizeof(result->d_name), - NULL, - NULL); - - if (!FindNextFileW(dir->handle, &dir->info)) { - (void)FindClose(dir->handle); - dir->handle = INVALID_HANDLE_VALUE; - } - - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return result; -} - - -#if !defined(HAVE_POLL) -#undef POLLIN -#undef POLLPRI -#undef POLLOUT -#undef POLLERR -#define POLLIN (1) /* Data ready - read will not block. */ -#define POLLPRI (2) /* Priority data ready. */ -#define POLLOUT (4) /* Send queue not full - write will not block. */ -#define POLLERR (8) /* Error event */ - -FUNCTION_MAY_BE_UNUSED -static int -poll(struct mg_pollfd *pfd, unsigned int n, int milliseconds) -{ - struct timeval tv; - fd_set rset; - fd_set wset; - fd_set eset; - unsigned int i; - int result; - SOCKET maxfd = 0; - - memset(&tv, 0, sizeof(tv)); - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = (milliseconds % 1000) * 1000; - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); - - for (i = 0; i < n; i++) { - if (pfd[i].events & (POLLIN | POLLOUT | POLLERR)) { - if (pfd[i].events & POLLIN) { - FD_SET(pfd[i].fd, &rset); - } - if (pfd[i].events & POLLOUT) { - FD_SET(pfd[i].fd, &wset); - } - /* Check for errors for any FD in the set */ - FD_SET(pfd[i].fd, &eset); - } - pfd[i].revents = 0; - - if (pfd[i].fd > maxfd) { - maxfd = pfd[i].fd; - } - } - - if ((result = select((int)maxfd + 1, &rset, &wset, &eset, &tv)) > 0) { - for (i = 0; i < n; i++) { - if (FD_ISSET(pfd[i].fd, &rset)) { - pfd[i].revents |= POLLIN; - } - if (FD_ISSET(pfd[i].fd, &wset)) { - pfd[i].revents |= POLLOUT; - } - if (FD_ISSET(pfd[i].fd, &eset)) { - pfd[i].revents |= POLLERR; - } - } - } - - /* We should subtract the time used in select from remaining - * "milliseconds", in particular if called from mg_poll with a - * timeout quantum. - * Unfortunately, the remaining time is not stored in "tv" in all - * implementations, so the result in "tv" must be considered undefined. - * See http://man7.org/linux/man-pages/man2/select.2.html */ - - return result; -} -#endif /* HAVE_POLL */ - - -#if defined(GCC_DIAGNOSTIC) -/* Enable unused function warning again */ -#pragma GCC diagnostic pop -#endif - - -static void -set_close_on_exec(SOCKET sock, - const struct mg_connection *conn /* may be null */, - struct mg_context *ctx /* may be null */) -{ - (void)conn; /* Unused. */ - (void)ctx; - - (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0); -} - - -CIVETWEB_API int -mg_start_thread(mg_thread_func_t f, void *p) -{ -#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) - /* Compile-time option to control stack size, e.g. - * -DUSE_STACK_SIZE=16384 - */ - return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p) - == ((uintptr_t)(-1L))) - ? -1 - : 0); -#else - return ( - (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L))) - ? -1 - : 0); -#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ -} - - -/* Start a thread storing the thread context. */ -static int -mg_start_thread_with_id(unsigned(__stdcall *f)(void *), - void *p, - pthread_t *threadidptr) -{ - uintptr_t uip; - HANDLE threadhandle; - int result = -1; - - uip = _beginthreadex(NULL, 0, f, p, 0, NULL); - threadhandle = (HANDLE)uip; - if ((uip != 0) && (threadidptr != NULL)) { - *threadidptr = threadhandle; - result = 0; - } - - return result; -} - - -/* Wait for a thread to finish. */ -static int -mg_join_thread(pthread_t threadid) -{ - int result; - DWORD dwevent; - - result = -1; - dwevent = WaitForSingleObject(threadid, (DWORD)INFINITE); - if (dwevent == WAIT_FAILED) { - DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO); - } else { - if (dwevent == WAIT_OBJECT_0) { - CloseHandle(threadid); - result = 0; - } - } - - return result; -} - -#if !defined(NO_SSL_DL) && !defined(NO_SSL) -/* If SSL is loaded dynamically, dlopen/dlclose is required. */ -/* Create substitutes for POSIX functions in Win32. */ - -#if defined(GCC_DIAGNOSTIC) -/* Show no warning in case system functions are not used. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif - - -FUNCTION_MAY_BE_UNUSED -static HANDLE -dlopen(const char *dll_name, int flags) -{ - wchar_t wbuf[UTF16_PATH_MAX]; - (void)flags; - path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf)); - return LoadLibraryW(wbuf); -} - - -FUNCTION_MAY_BE_UNUSED -static int -dlclose(void *handle) -{ - int result; - - if (FreeLibrary((HMODULE)handle) != 0) { - result = 0; - } else { - result = -1; - } - - return result; -} - - -#if defined(GCC_DIAGNOSTIC) -/* Enable unused function warning again */ -#pragma GCC diagnostic pop -#endif - -#endif - - -#if !defined(NO_CGI) -#define SIGKILL (0) - - -static int -kill(pid_t pid, int sig_num) -{ - (void)TerminateProcess((HANDLE)pid, (UINT)sig_num); - (void)CloseHandle((HANDLE)pid); - return 0; -} - - -#if !defined(WNOHANG) -#define WNOHANG (1) -#endif - - -static pid_t -waitpid(pid_t pid, int *status, int flags) -{ - DWORD timeout = INFINITE; - DWORD waitres; - - (void)status; /* Currently not used by any client here */ - - if ((flags | WNOHANG) == WNOHANG) { - timeout = 0; - } - - waitres = WaitForSingleObject((HANDLE)pid, timeout); - if (waitres == WAIT_OBJECT_0) { - return pid; - } - if (waitres == WAIT_TIMEOUT) { - return 0; - } - return (pid_t)-1; -} - - -static void -trim_trailing_whitespaces(char *s) -{ - char *e = s + strlen(s); - while ((e > s) && isspace((unsigned char)e[-1])) { - *(--e) = '\0'; - } -} - - -static pid_t -spawn_process(struct mg_connection *conn, - const char *prog, - char *envblk, - char *envp[], - int fdin[2], - int fdout[2], - int fderr[2], - const char *dir, - int cgi_config_idx) -{ - HANDLE me; - char *interp; - char *interp_arg = 0; - char full_dir[UTF8_PATH_MAX], cmdline[UTF8_PATH_MAX], buf[UTF8_PATH_MAX]; - int truncated; - struct mg_file file = STRUCT_FILE_INITIALIZER; - STARTUPINFOA si; - PROCESS_INFORMATION pi = {0}; - - (void)envp; - - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - - me = GetCurrentProcess(); - DuplicateHandle(me, - (HANDLE)_get_osfhandle(fdin[0]), - me, - &si.hStdInput, - 0, - TRUE, - DUPLICATE_SAME_ACCESS); - DuplicateHandle(me, - (HANDLE)_get_osfhandle(fdout[1]), - me, - &si.hStdOutput, - 0, - TRUE, - DUPLICATE_SAME_ACCESS); - DuplicateHandle(me, - (HANDLE)_get_osfhandle(fderr[1]), - me, - &si.hStdError, - 0, - TRUE, - DUPLICATE_SAME_ACCESS); - - /* Mark handles that should not be inherited. See - * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx - */ - SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]), - HANDLE_FLAG_INHERIT, - 0); - SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]), - HANDLE_FLAG_INHERIT, - 0); - SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]), - HANDLE_FLAG_INHERIT, - 0); - - /* First check, if there is a CGI interpreter configured for all CGI - * scripts. */ - interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx]; - if (interp != NULL) { - /* If there is a configured interpreter, check for additional arguments - */ - interp_arg = - conn->dom_ctx->config[CGI_INTERPRETER_ARGS + cgi_config_idx]; - } else { - /* Otherwise, the interpreter must be stated in the first line of the - * CGI script file, after a #! (shebang) mark. */ - buf[0] = buf[1] = '\0'; - - /* Get the full script path */ - mg_snprintf( - conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog); - - if (truncated) { - pi.hProcess = (pid_t)-1; - goto spawn_cleanup; - } - - /* Open the script file, to read the first line */ - if (mg_fopen(conn, cmdline, MG_FOPEN_MODE_READ, &file)) { - - /* Read the first line of the script into the buffer */ - mg_fgets(buf, sizeof(buf), &file); - (void)mg_fclose(&file.access); /* ignore error on read only file */ - buf[sizeof(buf) - 1] = '\0'; - } - - if ((buf[0] == '#') && (buf[1] == '!')) { - trim_trailing_whitespaces(buf + 2); - } else { - buf[2] = '\0'; - } - interp = buf + 2; - } - - GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL); - - if (interp[0] != '\0') { - /* This is an interpreted script file. We must call the interpreter. */ - if ((interp_arg != 0) && (interp_arg[0] != 0)) { - mg_snprintf(conn, - &truncated, - cmdline, - sizeof(cmdline), - "\"%s\" %s \"%s\\%s\"", - interp, - interp_arg, - full_dir, - prog); - } else { - mg_snprintf(conn, - &truncated, - cmdline, - sizeof(cmdline), - "\"%s\" \"%s\\%s\"", - interp, - full_dir, - prog); - } - } else { - /* This is (probably) a compiled program. We call it directly. */ - mg_snprintf(conn, - &truncated, - cmdline, - sizeof(cmdline), - "\"%s\\%s\"", - full_dir, - prog); - } - - if (truncated) { - pi.hProcess = (pid_t)-1; - goto spawn_cleanup; - } - - DEBUG_TRACE("Running [%s]", cmdline); - if (CreateProcessA(NULL, - cmdline, - NULL, - NULL, - TRUE, - CREATE_NEW_PROCESS_GROUP, - envblk, - NULL, - &si, - &pi) - == 0) { - mg_cry_internal( - conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO); - pi.hProcess = (pid_t)-1; - /* goto spawn_cleanup; */ - } - -spawn_cleanup: - (void)CloseHandle(si.hStdOutput); - (void)CloseHandle(si.hStdError); - (void)CloseHandle(si.hStdInput); - if (pi.hThread != NULL) { - (void)CloseHandle(pi.hThread); - } - - return (pid_t)pi.hProcess; -} -#endif /* !NO_CGI */ - - -static int -set_blocking_mode(SOCKET sock) -{ - unsigned long non_blocking = 0; - return ioctlsocket(sock, (long)FIONBIO, &non_blocking); -} - - -static int -set_non_blocking_mode(SOCKET sock) -{ - unsigned long non_blocking = 1; - return ioctlsocket(sock, (long)FIONBIO, &non_blocking); -} - - -#else - - -#if !defined(NO_FILESYSTEMS) -static int -mg_stat(const struct mg_connection *conn, - const char *path, - struct mg_file_stat *filep) -{ - struct stat st; - if (!filep) { - return 0; - } - memset(filep, 0, sizeof(*filep)); - - if (mg_path_suspicious(conn, path)) { - return 0; - } - - if (0 == stat(path, &st)) { - filep->size = (uint64_t)(st.st_size); - filep->last_modified = st.st_mtime; - filep->is_directory = S_ISDIR(st.st_mode); - return 1; - } - - return 0; -} -#endif /* NO_FILESYSTEMS */ - - -static void -set_close_on_exec(int fd, - const struct mg_connection *conn /* may be null */, - struct mg_context *ctx /* may be null */) -{ -#if defined(__ZEPHYR__) - (void)fd; - (void)conn; - (void)ctx; -#else - if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { - if (conn || ctx) { - struct mg_connection fc; - mg_cry_internal((conn ? conn : fake_connection(&fc, ctx)), - "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", - __func__, - strerror(ERRNO)); - } - } -#endif -} - - -CIVETWEB_API int -mg_start_thread(mg_thread_func_t func, void *param) -{ - pthread_t thread_id; - pthread_attr_t attr; - int result; - - (void)pthread_attr_init(&attr); - (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - -#if defined(__ZEPHYR__) - pthread_attr_setstack(&attr, &civetweb_main_stack, ZEPHYR_STACK_SIZE); -#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) - /* Compile-time option to control stack size, - * e.g. -DUSE_STACK_SIZE=16384 */ - (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); -#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ - - result = pthread_create(&thread_id, &attr, func, param); - pthread_attr_destroy(&attr); - - return result; -} - - -/* Start a thread storing the thread context. */ -static int -mg_start_thread_with_id(mg_thread_func_t func, - void *param, - pthread_t *threadidptr) -{ - pthread_t thread_id; - pthread_attr_t attr; - int result; - - (void)pthread_attr_init(&attr); - -#if defined(__ZEPHYR__) - pthread_attr_setstack(&attr, - &civetweb_worker_stacks[zephyr_worker_stack_index++], - ZEPHYR_STACK_SIZE); -#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) - /* Compile-time option to control stack size, - * e.g. -DUSE_STACK_SIZE=16384 */ - (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); -#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */ - - result = pthread_create(&thread_id, &attr, func, param); - pthread_attr_destroy(&attr); - if ((result == 0) && (threadidptr != NULL)) { - *threadidptr = thread_id; - } - return result; -} - - -/* Wait for a thread to finish. */ -static int -mg_join_thread(pthread_t threadid) -{ - int result; - - result = pthread_join(threadid, NULL); - return result; -} - - -#if !defined(NO_CGI) -static pid_t -spawn_process(struct mg_connection *conn, - const char *prog, - char *envblk, - char *envp[], - int fdin[2], - int fdout[2], - int fderr[2], - const char *dir, - int cgi_config_idx) -{ - pid_t pid; - const char *interp; - - (void)envblk; - - if ((pid = fork()) == -1) { - /* Parent */ - mg_cry_internal(conn, "%s: fork(): %s", __func__, strerror(ERRNO)); - } else if (pid != 0) { - /* Make sure children close parent-side descriptors. - * The caller will close the child-side immediately. */ - set_close_on_exec(fdin[1], conn, NULL); /* stdin write */ - set_close_on_exec(fdout[0], conn, NULL); /* stdout read */ - set_close_on_exec(fderr[0], conn, NULL); /* stderr read */ - } else { - /* Child */ - if (chdir(dir) != 0) { - mg_cry_internal( - conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); - } else if (dup2(fdin[0], 0) == -1) { - mg_cry_internal(conn, - "%s: dup2(%d, 0): %s", - __func__, - fdin[0], - strerror(ERRNO)); - } else if (dup2(fdout[1], 1) == -1) { - mg_cry_internal(conn, - "%s: dup2(%d, 1): %s", - __func__, - fdout[1], - strerror(ERRNO)); - } else if (dup2(fderr[1], 2) == -1) { - mg_cry_internal(conn, - "%s: dup2(%d, 2): %s", - __func__, - fderr[1], - strerror(ERRNO)); - } else { - struct sigaction sa; - - /* Keep stderr and stdout in two different pipes. - * Stdout will be sent back to the client, - * stderr should go into a server error log. */ - (void)close(fdin[0]); - (void)close(fdout[1]); - (void)close(fderr[1]); - - /* Close write end fdin and read end fdout and fderr */ - (void)close(fdin[1]); - (void)close(fdout[0]); - (void)close(fderr[0]); - - /* After exec, all signal handlers are restored to their default - * values, with one exception of SIGCHLD. According to - * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler - * will leave unchanged after exec if it was set to be ignored. - * Restore it to default action. */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGCHLD, &sa, NULL); - - interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx]; - if (interp == NULL) { - /* no interpreter configured, call the program directly */ - (void)execle(prog, prog, NULL, envp); - mg_cry_internal(conn, - "%s: execle(%s): %s", - __func__, - prog, - strerror(ERRNO)); - } else { - /* call the configured interpreter */ - const char *interp_args = - conn->dom_ctx - ->config[CGI_INTERPRETER_ARGS + cgi_config_idx]; - - if ((interp_args != NULL) && (interp_args[0] != 0)) { - (void)execle(interp, interp, interp_args, prog, NULL, envp); - } else { - (void)execle(interp, interp, prog, NULL, envp); - } - mg_cry_internal(conn, - "%s: execle(%s %s): %s", - __func__, - interp, - prog, - strerror(ERRNO)); - } - } - exit(EXIT_FAILURE); - } - - return pid; -} -#endif /* !NO_CGI */ - - -static int -set_non_blocking_mode(SOCKET sock) -{ - int flags = fcntl(sock, F_GETFL, 0); - if (flags < 0) { - return -1; - } - - if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) < 0) { - return -1; - } - return 0; -} - -static int -set_blocking_mode(SOCKET sock) -{ - int flags = fcntl(sock, F_GETFL, 0); - if (flags < 0) { - return -1; - } - - if (fcntl(sock, F_SETFL, flags & (~(int)(O_NONBLOCK))) < 0) { - return -1; - } - return 0; -} -#endif /* _WIN32 / else */ - -/* End of initial operating system specific define block. */ - - -/* Get a random number (independent of C rand function) */ -static uint64_t -get_random(void) -{ - static uint64_t lfsr = 0; /* Linear feedback shift register */ - static uint64_t lcg = 0; /* Linear congruential generator */ - uint64_t now = mg_get_current_time_ns(); - - if (lfsr == 0) { - /* lfsr will be only 0 if has not been initialized, - * so this code is called only once. */ - lfsr = mg_get_current_time_ns(); - lcg = mg_get_current_time_ns(); - } else { - /* Get the next step of both random number generators. */ - lfsr = (lfsr >> 1) - | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1) - << 63); - lcg = lcg * 6364136223846793005LL + 1442695040888963407LL; - } - - /* Combining two pseudo-random number generators and a high resolution - * part - * of the current server time will make it hard (impossible?) to guess - * the - * next number. */ - return (lfsr ^ lcg ^ now); -} - - -static int -mg_poll(struct mg_pollfd *pfd, - unsigned int n, - int milliseconds, - const stop_flag_t *stop_flag) -{ - /* Call poll, but only for a maximum time of a few seconds. - * This will allow to stop the server after some seconds, instead - * of having to wait for a long socket timeout. */ - int ms_now = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */ - - int check_pollerr = 0; - if ((n == 1) && ((pfd[0].events & POLLERR) == 0)) { - /* If we wait for only one file descriptor, wait on error as well */ - pfd[0].events |= POLLERR; - check_pollerr = 1; - } - - do { - int result; - - if (!STOP_FLAG_IS_ZERO(&*stop_flag)) { - /* Shut down signal */ - return -2; - } - - if ((milliseconds >= 0) && (milliseconds < ms_now)) { - ms_now = milliseconds; - } - - result = poll(pfd, n, ms_now); - if (result != 0) { - int err = ERRNO; - if ((result == 1) || (!ERROR_TRY_AGAIN(err))) { - /* Poll returned either success (1) or error (-1). - * Forward both to the caller. */ - if ((check_pollerr) - && ((pfd[0].revents & (POLLIN | POLLOUT | POLLERR)) - == POLLERR)) { - /* One and only file descriptor returned error */ - return -1; - } - return result; - } - } - - /* Poll returned timeout (0). */ - if (milliseconds > 0) { - milliseconds -= ms_now; - } - - } while (milliseconds > 0); - - /* timeout: return 0 */ - return 0; -} - - -/* Write data to the IO channel - opened file descriptor, socket or SSL - * descriptor. - * Return value: - * >=0 .. number of bytes successfully written - * -1 .. timeout - * -2 .. error - */ -static int -push_inner(struct mg_context *ctx, - FILE *fp, - SOCKET sock, - SSL *ssl, - const char *buf, - int len, - double timeout) -{ - uint64_t start = 0, now = 0, timeout_ns = 0; - int n, err; - unsigned ms_wait = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */ - -#if defined(_WIN32) - typedef int len_t; -#else - typedef size_t len_t; -#endif - - if (timeout > 0) { - now = mg_get_current_time_ns(); - start = now; - timeout_ns = (uint64_t)(timeout * 1.0E9); - } - - if (ctx == NULL) { - return -2; - } - -#if defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) - if (ssl) { - return -2; - } -#endif - - /* Try to read until it succeeds, fails, times out, or the server - * shuts down. */ - for (;;) { - -#if defined(USE_MBEDTLS) - if (ssl != NULL) { - n = mbed_ssl_write(ssl, (const unsigned char *)buf, len); - if (n <= 0) { - if ((n == MBEDTLS_ERR_SSL_WANT_READ) - || (n == MBEDTLS_ERR_SSL_WANT_WRITE) - || n == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) { - n = 0; - } else { - fprintf(stderr, "SSL write failed, error %d\n", n); - return -2; - } - } else { - err = 0; - } - } else -#elif defined(USE_GNUTLS) - if (ssl != NULL) { - n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t)len); - if (n < 0) { - fprintf(stderr, - "SSL write failed (%d): %s", - n, - gnutls_strerror(n)); - return -2; - } else { - err = 0; - } - } else -#elif !defined(NO_SSL) - if (ssl != NULL) { - ERR_clear_error(); - n = SSL_write(ssl, buf, len); - if (n <= 0) { - err = SSL_get_error(ssl, n); - if ((err == SSL_ERROR_SYSCALL) && (n == -1)) { - err = ERRNO; - } else if ((err == SSL_ERROR_WANT_READ) - || (err == SSL_ERROR_WANT_WRITE)) { - n = 0; - } else { - DEBUG_TRACE("SSL_write() failed, error %d", err); - ERR_clear_error(); - return -2; - } - ERR_clear_error(); - } else { - err = 0; - } - } else -#endif - - if (fp != NULL) { - n = (int)fwrite(buf, 1, (size_t)len, fp); - if (ferror(fp)) { - n = -1; - err = ERRNO; - } else { - err = 0; - } - } else { - n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL); - err = (n < 0) ? ERRNO : 0; - if (ERROR_TRY_AGAIN(err)) { - err = 0; - n = 0; - } - if (n < 0) { - /* shutdown of the socket at client side */ - return -2; - } - } - - if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) { - return -2; - } - - if ((n > 0) || ((n == 0) && (len == 0))) { - /* some data has been read, or no data was requested */ - return n; - } - if (n < 0) { - /* socket error - check errno */ - DEBUG_TRACE("send() failed, error %d", err); - - /* TODO (mid): error handling depending on the error code. - * These codes are different between Windows and Linux. - * Currently there is no problem with failing send calls, - * if there is a reproducible situation, it should be - * investigated in detail. - */ - return -2; - } - - /* Only in case n=0 (timeout), repeat calling the write function */ - - /* If send failed, wait before retry */ - if (fp != NULL) { - /* For files, just wait a fixed time. - * Maybe it helps, maybe not. */ - mg_sleep(5); - } else { - /* For sockets, wait for the socket using poll */ - struct mg_pollfd pfd[2]; - int pollres; - unsigned int num_sock = 1; - - pfd[0].fd = sock; - pfd[0].events = POLLOUT; - - if (ctx->context_type == CONTEXT_SERVER) { - pfd[num_sock].fd = ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - - pollres = mg_poll(pfd, num_sock, (int)(ms_wait), &(ctx->stop_flag)); - if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) { - return -2; - } - if (pollres > 0) { - continue; - } - } - - if (timeout > 0) { - now = mg_get_current_time_ns(); - if ((now - start) > timeout_ns) { - /* Timeout */ - break; - } - } - } - - (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not - used */ - - return -1; -} - - -static int -push_all(struct mg_context *ctx, - FILE *fp, - SOCKET sock, - SSL *ssl, - const char *buf, - int len) -{ - double timeout = -1.0; - int n, nwritten = 0; - - if (ctx == NULL) { - return -1; - } - - if (ctx->dd.config[REQUEST_TIMEOUT]) { - timeout = atoi(ctx->dd.config[REQUEST_TIMEOUT]) / 1000.0; - } - if (timeout <= 0.0) { - timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL) - / 1000.0; - } - - while ((len > 0) && STOP_FLAG_IS_ZERO(&ctx->stop_flag)) { - n = push_inner(ctx, fp, sock, ssl, buf + nwritten, len, timeout); - if (n < 0) { - if (nwritten == 0) { - nwritten = -1; /* Propagate the error */ - } - break; - } else if (n == 0) { - break; /* No more data to write */ - } else { - nwritten += n; - len -= n; - } - } - - return nwritten; -} - - -/* Read from IO channel - opened file descriptor, socket, or SSL descriptor. - * Return value: - * >=0 .. number of bytes successfully read - * -1 .. timeout - * -2 .. error - */ -static int -pull_inner(FILE *fp, - struct mg_connection *conn, - char *buf, - int len, - double timeout) -{ - int nread, err = 0; - -#if defined(_WIN32) - typedef int len_t; -#else - typedef size_t len_t; -#endif - - /* We need an additional wait loop around this, because in some cases - * with TLSwe may get data from the socket but not from SSL_read. - * In this case we need to repeat at least once. - */ - - if (fp != NULL) { - /* Use read() instead of fread(), because if we're reading from the - * CGI pipe, fread() may block until IO buffer is filled up. We - * cannot afford to block and must pass all read bytes immediately - * to the client. */ - nread = (int)read(fileno(fp), buf, (size_t)len); - - err = (nread < 0) ? ERRNO : 0; - if ((nread == 0) && (len > 0)) { - /* Should get data, but got EOL */ - return -2; - } - -#if defined(USE_MBEDTLS) - } else if (conn->ssl != NULL) { - struct mg_pollfd pfd[2]; - int to_read; - int pollres; - unsigned int num_sock = 1; - - to_read = mbedtls_ssl_get_bytes_avail(conn->ssl); - - if (to_read > 0) { - /* We already know there is no more data buffered in conn->buf - * but there is more available in the SSL layer. So don't poll - * conn->client.sock yet. */ - - pollres = 1; - if (to_read > len) - to_read = len; - } else { - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - pfd[num_sock].fd = - conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - - to_read = len; - - pollres = mg_poll(pfd, - num_sock, - (int)(timeout * 1000.0), - &(conn->phys_ctx->stop_flag)); - - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - return -2; - } - } - - if (pollres > 0) { - nread = mbed_ssl_read(conn->ssl, (unsigned char *)buf, to_read); - if (nread <= 0) { - if ((nread == MBEDTLS_ERR_SSL_WANT_READ) - || (nread == MBEDTLS_ERR_SSL_WANT_WRITE) - || nread == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) { - nread = 0; - } else { - fprintf(stderr, "SSL read failed, error %d\n", nread); - return -2; - } - } else { - err = 0; - } - - } else if (pollres < 0) { - /* Error */ - return -2; - } else { - /* pollres = 0 means timeout */ - nread = 0; - } - -#elif defined(USE_GNUTLS) - } else if (conn->ssl != NULL) { - struct mg_pollfd pfd[2]; - size_t to_read; - int pollres; - unsigned int num_sock = 1; - - to_read = gnutls_record_check_pending(conn->ssl->sess); - - if (to_read > 0) { - /* We already know there is no more data buffered in conn->buf - * but there is more available in the SSL layer. So don't poll - * conn->client.sock yet. */ - - pollres = 1; - if (to_read > (size_t)len) - to_read = (size_t)len; - } else { - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - pfd[num_sock].fd = - conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - - to_read = (size_t)len; - - pollres = mg_poll(pfd, - num_sock, - (int)(timeout * 1000.0), - &(conn->phys_ctx->stop_flag)); - - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - return -2; - } - } - - if (pollres > 0) { - nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read); - if (nread < 0) { - fprintf(stderr, - "SSL read failed (%d): %s", - nread, - gnutls_strerror(nread)); - return -2; - } else { - err = 0; - } - } else if (pollres < 0) { - /* Error */ - return -2; - } else { - /* pollres = 0 means timeout */ - nread = 0; - } - -#elif !defined(NO_SSL) - } else if (conn->ssl != NULL) { - int ssl_pending; - struct mg_pollfd pfd[2]; - int pollres; - unsigned int num_sock = 1; - - if ((ssl_pending = SSL_pending(conn->ssl)) > 0) { - /* We already know there is no more data buffered in conn->buf - * but there is more available in the SSL layer. So don't poll - * conn->client.sock yet. */ - if (ssl_pending > len) { - ssl_pending = len; - } - pollres = 1; - } else { - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - pfd[num_sock].fd = - conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - - pollres = mg_poll(pfd, - num_sock, - (int)(timeout * 1000.0), - &(conn->phys_ctx->stop_flag)); - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - return -2; - } - } - if (pollres > 0) { - ERR_clear_error(); - nread = - SSL_read(conn->ssl, buf, (ssl_pending > 0) ? ssl_pending : len); - if (nread <= 0) { - err = SSL_get_error(conn->ssl, nread); - if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) { - err = ERRNO; - } else if ((err == SSL_ERROR_WANT_READ) - || (err == SSL_ERROR_WANT_WRITE)) { - nread = 0; - } else { - /* All errors should return -2 */ - DEBUG_TRACE("SSL_read() failed, error %d", err); - ERR_clear_error(); - return -2; - } - ERR_clear_error(); - } else { - err = 0; - } - } else if (pollres < 0) { - /* Error */ - return -2; - } else { - /* pollres = 0 means timeout */ - nread = 0; - } -#endif - - } else { - struct mg_pollfd pfd[2]; - int pollres; - unsigned int num_sock = 1; - - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - pfd[num_sock].fd = - conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - - pollres = mg_poll(pfd, - num_sock, - (int)(timeout * 1000.0), - &(conn->phys_ctx->stop_flag)); - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - return -2; - } - if (pollres > 0) { - nread = (int)recv(conn->client.sock, buf, (len_t)len, 0); - err = (nread < 0) ? ERRNO : 0; - if (nread <= 0) { - /* shutdown of the socket at client side */ - return -2; - } - } else if (pollres < 0) { - /* error calling poll */ - return -2; - } else { - /* pollres = 0 means timeout */ - nread = 0; - } - } - - if (conn != NULL && !STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - return -2; - } - - if ((nread > 0) || ((nread == 0) && (len == 0))) { - /* some data has been read, or no data was requested */ - return nread; - } - - if (nread < 0) { - /* socket error - check errno */ -#if defined(_WIN32) - if (err == WSAEWOULDBLOCK) { - /* TODO (low): check if this is still required */ - /* standard case if called from close_socket_gracefully */ - return -2; - } else if (err == WSAETIMEDOUT) { - /* TODO (low): check if this is still required */ - /* timeout is handled by the while loop */ - return 0; - } else if (err == WSAECONNABORTED) { - /* See https://www.chilkatsoft.com/p/p_299.asp */ - return -2; - } else { - DEBUG_TRACE("read()/recv() failed, error %d", err); - return -2; - } -#else - /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases, - * if the timeout is reached and if the socket was set to non- - * blocking in close_socket_gracefully, so we can not distinguish - * here. We have to wait for the timeout in both cases for now. - */ - if (ERROR_TRY_AGAIN(err)) { - /* TODO (low): check if this is still required */ - /* EAGAIN/EWOULDBLOCK: - * standard case if called from close_socket_gracefully - * => should return -1 */ - /* or timeout occurred - * => the code must stay in the while loop */ - - /* EINTR can be generated on a socket with a timeout set even - * when SA_RESTART is effective for all relevant signals - * (see signal(7)). - * => stay in the while loop */ - } else { - DEBUG_TRACE("read()/recv() failed, error %d", err); - return -2; - } -#endif - } - - /* Timeout occurred, but no data available. */ - return -1; -} - - -static int -pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) -{ - int n, nread = 0; - double timeout = -1.0; - uint64_t start_time = 0, now = 0, timeout_ns = 0; - - if (conn->dom_ctx->config[REQUEST_TIMEOUT]) { - timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0; - } - if (timeout <= 0.0) { - timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL) - / 1000.0; - } - start_time = mg_get_current_time_ns(); - timeout_ns = (uint64_t)(timeout * 1.0E9); - - while ((len > 0) && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - n = pull_inner(fp, conn, buf + nread, len, timeout); - if (n == -2) { - if (nread == 0) { - nread = -1; /* Propagate the error */ - } - break; - } else if (n == -1) { - /* timeout */ - if (timeout >= 0.0) { - now = mg_get_current_time_ns(); - if ((now - start_time) <= timeout_ns) { - continue; - } - } - break; - } else if (n == 0) { - break; /* No more data to read */ - } else { - nread += n; - len -= n; - } - } - - return nread; -} - - -static void -discard_unread_request_data(struct mg_connection *conn) -{ - char buf[MG_BUF_LEN]; - - while (mg_read(conn, buf, sizeof(buf)) > 0) - ; -} - - -static int -mg_read_inner(struct mg_connection *conn, void *buf, size_t len) -{ - int64_t content_len, n, buffered_len, nread; - int64_t len64 = - (int64_t)((len > INT_MAX) ? INT_MAX : len); /* since the return value is - * int, we may not read more - * bytes */ - const char *body; - - if (conn == NULL) { - return 0; - } - - /* If Content-Length is not set for a response with body data, - * we do not know in advance how much data should be read. */ - content_len = conn->content_len; - if (content_len < 0) { - /* The body data is completed when the connection is closed. */ - content_len = INT64_MAX; - } - - nread = 0; - if (conn->consumed_content < content_len) { - /* Adjust number of bytes to read. */ - int64_t left_to_read = content_len - conn->consumed_content; - if (left_to_read < len64) { - /* Do not read more than the total content length of the - * request. - */ - len64 = left_to_read; - } - - /* Return buffered data */ - buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len - - conn->consumed_content; - if (buffered_len > 0) { - if (len64 < buffered_len) { - buffered_len = len64; - } - body = conn->buf + conn->request_len + conn->consumed_content; - memcpy(buf, body, (size_t)buffered_len); - len64 -= buffered_len; - conn->consumed_content += buffered_len; - nread += buffered_len; - buf = (char *)buf + buffered_len; - } - - /* We have returned all buffered data. Read new data from the remote - * socket. - */ - if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) { - conn->consumed_content += n; - nread += n; - } else { - nread = ((nread > 0) ? nread : n); - } - } - return (int)nread; -} - - -/* Forward declarations */ -static void handle_request(struct mg_connection *); -static void log_access(const struct mg_connection *); - - -/* Handle request, update statistics and call access log */ -static void -handle_request_stat_log(struct mg_connection *conn) -{ -#if defined(USE_SERVER_STATS) - struct timespec tnow; - conn->conn_state = 4; /* processing */ -#endif - - handle_request(conn); - - -#if defined(USE_SERVER_STATS) - conn->conn_state = 5; /* processed */ - - clock_gettime(CLOCK_MONOTONIC, &tnow); - conn->processing_time = mg_difftimespec(&tnow, &(conn->req_time)); - - mg_atomic_add64(&(conn->phys_ctx->total_data_read), conn->consumed_content); - mg_atomic_add64(&(conn->phys_ctx->total_data_written), - conn->num_bytes_sent); -#endif - - DEBUG_TRACE("%s", "handle_request done"); - - if (conn->phys_ctx->callbacks.end_request != NULL) { - conn->phys_ctx->callbacks.end_request(conn, conn->status_code); - DEBUG_TRACE("%s", "end_request callback done"); - } - log_access(conn); -} - - -#if defined(USE_HTTP2) -#if defined(NO_SSL) -#error "HTTP2 requires ALPN, ALPN requires SSL/TLS" -#endif -#define USE_ALPN -#include "http2.inl" -/* Not supported with HTTP/2 */ -#define HTTP1_only \ - { \ - if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { \ - http2_must_use_http1(conn); \ - DEBUG_TRACE("%s", "must use HTTP/1.x"); \ - return; \ - } \ - } -#else -#define HTTP1_only -#endif - - -CIVETWEB_API int -mg_read(struct mg_connection *conn, void *buf, size_t len) -{ - if (len > INT_MAX) { - len = INT_MAX; - } - - if (conn == NULL) { - return 0; - } - - if (conn->is_chunked) { - size_t all_read = 0; - - while (len > 0) { - if (conn->is_chunked >= 3) { - /* No more data left to read */ - return 0; - } - if (conn->is_chunked != 1) { - /* Has error */ - return -1; - } - - if (conn->consumed_content != conn->content_len) { - /* copy from the current chunk */ - int read_ret = mg_read_inner(conn, (char *)buf + all_read, len); - - if (read_ret < 1) { - /* read error */ - conn->is_chunked = 2; - return -1; - } - - all_read += (size_t)read_ret; - len -= (size_t)read_ret; - - if (conn->consumed_content == conn->content_len) { - /* Add data bytes in the current chunk have been read, - * so we are expecting \r\n now. */ - char x[2]; - conn->content_len += 2; - if ((mg_read_inner(conn, x, 2) != 2) || (x[0] != '\r') - || (x[1] != '\n')) { - /* Protocol violation */ - conn->is_chunked = 2; - return -1; - } - } - - } else { - /* fetch a new chunk */ - size_t i; - char lenbuf[64]; - char *end = NULL; - unsigned long chunkSize = 0; - - for (i = 0; i < (sizeof(lenbuf) - 1); i++) { - conn->content_len++; - if (mg_read_inner(conn, lenbuf + i, 1) != 1) { - lenbuf[i] = 0; - } - if ((i > 0) && (lenbuf[i] == ';')) { - // chunk extension --> skip chars until next CR - // - // RFC 2616, 3.6.1 Chunked Transfer Coding - // (https://www.rfc-editor.org/rfc/rfc2616#page-25) - // - // chunk = chunk-size [ chunk-extension ] CRLF - // chunk-data CRLF - // ... - // chunk-extension= *( ";" chunk-ext-name [ "=" - // chunk-ext-val ] ) - do - ++conn->content_len; - while (mg_read_inner(conn, lenbuf + i, 1) == 1 - && lenbuf[i] != '\r'); - } - if ((i > 0) && (lenbuf[i] == '\r') - && (lenbuf[i - 1] != '\r')) { - continue; - } - if ((i > 1) && (lenbuf[i] == '\n') - && (lenbuf[i - 1] == '\r')) { - lenbuf[i + 1] = 0; - chunkSize = strtoul(lenbuf, &end, 16); - if (chunkSize == 0) { - /* regular end of content */ - conn->is_chunked = 3; - } - break; - } - if (!isxdigit((unsigned char)lenbuf[i])) { - /* illegal character for chunk length */ - conn->is_chunked = 2; - return -1; - } - } - if ((end == NULL) || (*end != '\r')) { - /* chunksize not set correctly */ - conn->is_chunked = 2; - return -1; - } - if (conn->is_chunked == 3) { - /* try discarding trailer for keep-alive */ - - // We found the last chunk (length 0) including the - // CRLF that terminates that chunk. Now follows a possibly - // empty trailer and a final CRLF. - // - // see RFC 2616, 3.6.1 Chunked Transfer Coding - // (https://www.rfc-editor.org/rfc/rfc2616#page-25) - // - // Chunked-Body = *chunk - // last-chunk - // trailer - // CRLF - // ... - // last-chunk = 1*("0") [ chunk-extension ] CRLF - // ... - // trailer = *(entity-header CRLF) - - int crlf_count = 2; // one CRLF already determined - - while (crlf_count < 4 && conn->is_chunked == 3) { - ++conn->content_len; - if (mg_read_inner(conn, lenbuf, 1) == 1) { - if ((crlf_count == 0 || crlf_count == 2)) { - if (lenbuf[0] == '\r') - ++crlf_count; - else - crlf_count = 0; - } else { - // previous character was a CR - // --> next character must be LF - - if (lenbuf[0] == '\n') - ++crlf_count; - else - conn->is_chunked = 2; - } - } else - // premature end of trailer - conn->is_chunked = 2; - } - - if (conn->is_chunked == 2) - return -1; - else - conn->is_chunked = 4; - - break; - } - - /* append a new chunk */ - conn->content_len += (int64_t)chunkSize; - } - } - - return (int)all_read; - } - return mg_read_inner(conn, buf, len); -} - - -CIVETWEB_API int -mg_write(struct mg_connection *conn, const void *buf, size_t len) -{ - time_t now; - int n, total, allowed; - - if (conn == NULL) { - return 0; - } - if (len > INT_MAX) { - return -1; - } - - /* Mark connection as "data sent" */ - conn->request_state = 10; -#if defined(USE_HTTP2) - if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { - http2_data_frame_head(conn, len, 0); - } -#endif - - if (conn->throttle > 0) { - if ((now = time(NULL)) != conn->last_throttle_time) { - conn->last_throttle_time = now; - conn->last_throttle_bytes = 0; - } - allowed = conn->throttle - conn->last_throttle_bytes; - if (allowed > (int)len) { - allowed = (int)len; - } - - total = push_all(conn->phys_ctx, - NULL, - conn->client.sock, - conn->ssl, - (const char *)buf, - allowed); - - if (total == allowed) { - - buf = (const char *)buf + total; - conn->last_throttle_bytes += total; - while ((total < (int)len) - && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - allowed = (conn->throttle > ((int)len - total)) - ? (int)len - total - : conn->throttle; - - n = push_all(conn->phys_ctx, - NULL, - conn->client.sock, - conn->ssl, - (const char *)buf, - allowed); - - if (n != allowed) { - break; - } - sleep(1); - conn->last_throttle_bytes = allowed; - conn->last_throttle_time = time(NULL); - buf = (const char *)buf + n; - total += n; - } - } - } else { - total = push_all(conn->phys_ctx, - NULL, - conn->client.sock, - conn->ssl, - (const char *)buf, - (int)len); - } - if (total > 0) { - conn->num_bytes_sent += total; - } - return total; -} - - -/* Send a chunk, if "Transfer-Encoding: chunked" is used */ -CIVETWEB_API int -mg_send_chunk(struct mg_connection *conn, - const char *chunk, - unsigned int chunk_len) -{ - char lenbuf[16]; - size_t lenbuf_len; - int ret; - int t; - - /* First store the length information in a text buffer. */ - sprintf(lenbuf, "%x\r\n", chunk_len); - lenbuf_len = strlen(lenbuf); - - /* Then send length information, chunk and terminating \r\n. */ - ret = mg_write(conn, lenbuf, lenbuf_len); - if (ret != (int)lenbuf_len) { - return -1; - } - t = ret; - - ret = mg_write(conn, chunk, chunk_len); - if (ret != (int)chunk_len) { - return -1; - } - t += ret; - - ret = mg_write(conn, "\r\n", 2); - if (ret != 2) { - return -1; - } - t += ret; - - return t; -} - - -#if defined(GCC_DIAGNOSTIC) -/* This block forwards format strings to printf implementations, - * so we need to disable the format-nonliteral warning. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -#endif - - -/* Alternative alloc_vprintf() for non-compliant C runtimes */ -static int -alloc_vprintf2(char **buf, const char *fmt, va_list ap) -{ - va_list ap_copy; - size_t size = MG_BUF_LEN / 4; - int len = -1; - - *buf = NULL; - while (len < 0) { - if (*buf) { - mg_free(*buf); - } - - size *= 4; - *buf = (char *)mg_malloc(size); - if (!*buf) { - break; - } - - va_copy(ap_copy, ap); - len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy); - va_end(ap_copy); - (*buf)[size - 1] = 0; - } - - return len; -} - - -/* Print message to buffer. If buffer is large enough to hold the message, - * return buffer. If buffer is to small, allocate large enough buffer on - * heap, - * and return allocated buffer. */ -static int -alloc_vprintf(char **out_buf, - char *prealloc_buf, - size_t prealloc_size, - const char *fmt, - va_list ap) -{ - va_list ap_copy; - int len; - - /* Windows is not standard-compliant, and vsnprintf() returns -1 if - * buffer is too small. Also, older versions of msvcrt.dll do not have - * _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. - * Therefore, we make two passes: on first pass, get required message - * length. - * On second pass, actually print the message. */ - va_copy(ap_copy, ap); - len = vsnprintf_impl(NULL, 0, fmt, ap_copy); - va_end(ap_copy); - - if (len < 0) { - /* C runtime is not standard compliant, vsnprintf() returned -1. - * Switch to alternative code path that uses incremental - * allocations. - */ - va_copy(ap_copy, ap); - len = alloc_vprintf2(out_buf, fmt, ap_copy); - va_end(ap_copy); - - } else if ((size_t)(len) >= prealloc_size) { - /* The pre-allocated buffer not large enough. */ - /* Allocate a new buffer. */ - *out_buf = (char *)mg_malloc((size_t)(len) + 1); - if (!*out_buf) { - /* Allocation failed. Return -1 as "out of memory" error. */ - return -1; - } - /* Buffer allocation successful. Store the string there. */ - va_copy(ap_copy, ap); - IGNORE_UNUSED_RESULT( - vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy)); - va_end(ap_copy); - - } else { - /* The pre-allocated buffer is large enough. - * Use it to store the string and return the address. */ - va_copy(ap_copy, ap); - IGNORE_UNUSED_RESULT( - vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy)); - va_end(ap_copy); - *out_buf = prealloc_buf; - } - - return len; -} - - -static int -alloc_printf(char **out_buf, const char *fmt, ...) -{ - va_list ap; - int result; - - va_start(ap, fmt); - result = alloc_vprintf(out_buf, NULL, 0, fmt, ap); - va_end(ap); - - return result; -} - - -#if defined(GCC_DIAGNOSTIC) -/* Enable format-nonliteral warning again. */ -#pragma GCC diagnostic pop -#endif - - -static int -mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) -{ - char mem[MG_BUF_LEN]; - char *buf = NULL; - int len; - - if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) { - len = mg_write(conn, buf, (size_t)len); - } - if (buf != mem) { - mg_free(buf); - } - - return len; -} - - -CIVETWEB_API int -mg_printf(struct mg_connection *conn, const char *fmt, ...) -{ - va_list ap; - int result; - - va_start(ap, fmt); - result = mg_vprintf(conn, fmt, ap); - va_end(ap); - - return result; -} - - -CIVETWEB_API int -mg_url_decode(const char *src, - int src_len, - char *dst, - int dst_len, - int is_form_url_encoded) -{ - int i, j, a, b; -#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W')) - - for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) { - if ((i < src_len - 2) && (src[i] == '%') - && isxdigit((unsigned char)src[i + 1]) - && isxdigit((unsigned char)src[i + 2])) { - a = tolower((unsigned char)src[i + 1]); - b = tolower((unsigned char)src[i + 2]); - dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } else if (is_form_url_encoded && (src[i] == '+')) { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; /* Null-terminate the destination */ - - return (i >= src_len) ? j : -1; -} - - -/* form url decoding of an entire string */ -static void -url_decode_in_place(char *buf) -{ - int len = (int)strlen(buf); - (void)mg_url_decode(buf, len, buf, len + 1, 1); -} - - -CIVETWEB_API int -mg_get_var(const char *data, - size_t data_len, - const char *name, - char *dst, - size_t dst_len) -{ - return mg_get_var2(data, data_len, name, dst, dst_len, 0); -} - - -CIVETWEB_API int -mg_get_var2(const char *data, - size_t data_len, - const char *name, - char *dst, - size_t dst_len, - size_t occurrence) -{ - const char *p, *e, *s; - size_t name_len; - int len; - - if ((dst == NULL) || (dst_len == 0)) { - len = -2; - } else if ((data == NULL) || (name == NULL) || (data_len == 0)) { - len = -1; - dst[0] = '\0'; - } else { - name_len = strlen(name); - e = data + data_len; - len = -1; - dst[0] = '\0'; - - /* data is "var1=val1&var2=val2...". Find variable first */ - for (p = data; p + name_len < e; p++) { - if (((p == data) || (p[-1] == '&')) && (p[name_len] == '=') - && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) { - /* Point p to variable value */ - p += name_len + 1; - - /* Point s to the end of the value */ - s = (const char *)memchr(p, '&', (size_t)(e - p)); - if (s == NULL) { - s = e; - } - DEBUG_ASSERT(s >= p); - if (s < p) { - return -3; - } - - /* Decode variable into destination buffer */ - len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1); - - /* Redirect error code from -1 to -2 (destination buffer too - * small). */ - if (len == -1) { - len = -2; - } - break; - } - } - } - - return len; -} - - -/* split a string "key1=val1&key2=val2" into key/value pairs */ -CIVETWEB_API int -mg_split_form_urlencoded(char *data, - struct mg_header *form_fields, - unsigned num_form_fields) -{ - char *b; - int i; - int num = 0; - - if (data == NULL) { - /* parameter error */ - return -1; - } - - if ((form_fields == NULL) && (num_form_fields == 0)) { - /* determine the number of expected fields */ - if (data[0] == 0) { - return 0; - } - /* count number of & to return the number of key-value-pairs */ - num = 1; - while (*data) { - if (*data == '&') { - num++; - } - data++; - } - return num; - } - - if ((form_fields == NULL) || ((int)num_form_fields <= 0)) { - /* parameter error */ - return -1; - } - - for (i = 0; i < (int)num_form_fields; i++) { - /* extract key-value pairs from input data */ - while ((*data == ' ') || (*data == '\t')) { - /* skip initial spaces */ - data++; - } - if (*data == 0) { - /* end of string reached */ - break; - } - form_fields[num].name = data; - - /* find & or = */ - b = data; - while ((*b != 0) && (*b != '&') && (*b != '=')) { - b++; - } - - if (*b == 0) { - /* last key without value */ - form_fields[num].value = NULL; - } else if (*b == '&') { - /* mid key without value */ - form_fields[num].value = NULL; - } else { - /* terminate string */ - *b = 0; - /* value starts after '=' */ - data = b + 1; - form_fields[num].value = data; - } - - /* new field is stored */ - num++; - - /* find a next key */ - b = strchr(data, '&'); - if (b == 0) { - /* no more data */ - break; - } else { - /* terminate value of last field at '&' */ - *b = 0; - /* next key-value-pairs starts after '&' */ - data = b + 1; - } - } - - /* Decode all values */ - for (i = 0; i < num; i++) { - if (form_fields[i].name) { - url_decode_in_place((char *)form_fields[i].name); - } - if (form_fields[i].value) { - url_decode_in_place((char *)form_fields[i].value); - } - } - - /* return number of fields found */ - return num; -} - - -/* HCP24: some changes to compare whole var_name */ -CIVETWEB_API int -mg_get_cookie(const char *cookie_header, - const char *var_name, - char *dst, - size_t dst_size) -{ - const char *s, *p, *end; - int name_len, len = -1; - - if ((dst == NULL) || (dst_size == 0)) { - return -2; - } - - dst[0] = '\0'; - if ((var_name == NULL) || ((s = cookie_header) == NULL)) { - return -1; - } - - name_len = (int)strlen(var_name); - end = s + strlen(s); - for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { - if (s[name_len] == '=') { - /* HCP24: now check is it a substring or a full cookie name */ - if ((s == cookie_header) || (s[-1] == ' ')) { - s += name_len + 1; - if ((p = strchr(s, ' ')) == NULL) { - p = end; - } - if (p[-1] == ';') { - p--; - } - if ((*s == '"') && (p[-1] == '"') && (p > s + 1)) { - s++; - p--; - } - if ((size_t)(p - s) < dst_size) { - len = (int)(p - s); - mg_strlcpy(dst, s, (size_t)len + 1); - } else { - len = -3; - } - break; - } - } - } - return len; -} - - -CIVETWEB_API int -mg_base64_encode(const unsigned char *src, - size_t src_len, - char *dst, - size_t *dst_len) -{ - static const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - size_t i, j; - int a, b, c; - - if (dst_len != NULL) { - /* Expected length including 0 termination: */ - /* IN 1 -> OUT 5, IN 2 -> OUT 5, IN 3 -> OUT 5, IN 4 -> OUT 9, - * IN 5 -> OUT 9, IN 6 -> OUT 9, IN 7 -> OUT 13, etc. */ - size_t expected_len = ((src_len + 2) / 3) * 4 + 1; - if (*dst_len < expected_len) { - if (*dst_len > 0) { - dst[0] = '\0'; - } - *dst_len = expected_len; - return 0; - } - } - - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = ((i + 1) >= src_len) ? 0 : src[i + 1]; - c = ((i + 2) >= src_len) ? 0 : src[i + 2]; - - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 63]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; - - if (dst_len != NULL) { - *dst_len = (size_t)j; - } - - /* Return -1 for "OK" */ - return -1; -} - - -static unsigned char -b64reverse(char letter) -{ - if ((letter >= 'A') && (letter <= 'Z')) { - return (unsigned char)(letter - 'A'); - } - if ((letter >= 'a') && (letter <= 'z')) { - return (unsigned char)(letter - 'a' + 26); - } - if ((letter >= '0') && (letter <= '9')) { - return (unsigned char)(letter - '0' + 52); - } - if (letter == '+') { - return 62; - } - if (letter == '/') { - return 63; - } - if (letter == '=') { - return 255; /* normal end */ - } - return 254; /* error */ -} - - -CIVETWEB_API int -mg_base64_decode(const char *src, - size_t src_len, - unsigned char *dst, - size_t *dst_len) -{ - size_t i; - unsigned char a, b, c, d; - size_t dst_len_limit = (size_t)-1; - size_t dst_len_used = 0; - - if (dst_len != NULL) { - dst_len_limit = *dst_len; - *dst_len = 0; - } - - for (i = 0; i < src_len; i += 4) { - /* Read 4 characters from BASE64 string */ - a = b64reverse(src[i]); - if (a >= 254) { - return (int)i; - } - - b = b64reverse(((i + 1) >= src_len) ? 0 : src[i + 1]); - if (b >= 254) { - return (int)i + 1; - } - - c = b64reverse(((i + 2) >= src_len) ? 0 : src[i + 2]); - if (c == 254) { - return (int)i + 2; - } - - d = b64reverse(((i + 3) >= src_len) ? 0 : src[i + 3]); - if (d == 254) { - return (int)i + 3; - } - - /* Add first (of 3) decoded character */ - if (dst_len_used < dst_len_limit) { - dst[dst_len_used] = (unsigned char)((unsigned char)(a << 2) - + (unsigned char)(b >> 4)); - } - dst_len_used++; - - if (c != 255) { - if (dst_len_used < dst_len_limit) { - - dst[dst_len_used] = (unsigned char)((unsigned char)(b << 4) - + (unsigned char)(c >> 2)); - } - dst_len_used++; - if (d != 255) { - if (dst_len_used < dst_len_limit) { - dst[dst_len_used] = - (unsigned char)((unsigned char)(c << 6) + d); - } - dst_len_used++; - } - } - } - - /* Add terminating zero */ - if (dst_len_used < dst_len_limit) { - dst[dst_len_used] = '\0'; - } - dst_len_used++; - if (dst_len != NULL) { - *dst_len = dst_len_used; - } - - if (dst_len_used > dst_len_limit) { - /* Out of memory */ - return 0; - } - - /* Return -1 for "OK" */ - return -1; -} - - -static int -is_put_or_delete_method(const struct mg_connection *conn) -{ - if (conn) { - const char *s = conn->request_info.request_method; - if (s != NULL) { - /* PUT, DELETE, MKCOL, PATCH, LOCK, UNLOCK, PROPPATCH, MOVE, COPY */ - return (!strcmp(s, "PUT") || !strcmp(s, "DELETE") - || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH") - || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK") - || !strcmp(s, "PROPPATCH") || !strcmp(s, "MOVE") - || !strcmp(s, "COPY")); - } - } - return 0; -} - - -static int -is_civetweb_webdav_method(const struct mg_connection *conn) -{ - /* Note: Here we only have to identify the WebDav methods that need special - * handling in the CivetWeb code - not all methods used in WebDav. In - * particular, methods used on directories (when using Windows Explorer as - * WebDav client). - */ - if (conn) { - const char *s = conn->request_info.request_method; - if (s != NULL) { - /* These are the civetweb builtin DAV methods */ - return (!strcmp(s, "PROPFIND") || !strcmp(s, "PROPPATCH") - || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK") - || !strcmp(s, "MOVE") || !strcmp(s, "COPY")); - } - } - return 0; -} - - -#if !defined(NO_FILES) -static int -extention_matches_script( - struct mg_connection *conn, /* in: request (must be valid) */ - const char *filename /* in: filename (must be valid) */ -) -{ -#if !defined(NO_CGI) - int cgi_config_idx, inc, max; -#endif - -#if defined(USE_LUA) - if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS], - filename) - > 0) { - return 1; - } -#endif -#if defined(USE_DUKTAPE) - if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], - filename) - > 0) { - return 1; - } -#endif -#if !defined(NO_CGI) - inc = CGI2_EXTENSIONS - CGI_EXTENSIONS; - max = PUT_DELETE_PASSWORDS_FILE - CGI_EXTENSIONS; - for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) { - if ((conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL) - && (match_prefix_strlen( - conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx], - filename) - > 0)) { - return 1; - } - } -#endif - /* filename and conn could be unused, if all preocessor conditions - * are false (no script language supported). */ - (void)filename; - (void)conn; - - return 0; -} - - -static int -extention_matches_template_text( - struct mg_connection *conn, /* in: request (must be valid) */ - const char *filename /* in: filename (must be valid) */ -) -{ -#if defined(USE_LUA) - if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS], - filename) - > 0) { - return 1; - } -#endif - if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], filename) - > 0) { - return 1; - } - return 0; -} - - -/* For given directory path, substitute it to valid index file. - * Return 1 if index file has been found, 0 if not found. - * If the file is found, it's stats is returned in stp. */ -static int -substitute_index_file_aux(struct mg_connection *conn, - char *path, - size_t path_len, - struct mg_file_stat *filestat) -{ - const char *list = conn->dom_ctx->config[INDEX_FILES]; - struct vec filename_vec; - size_t n = strlen(path); - int found = 0; - - /* The 'path' given to us points to the directory. Remove all trailing - * directory separator characters from the end of the path, and - * then append single directory separator character. */ - while ((n > 0) && (path[n - 1] == '/')) { - n--; - } - path[n] = '/'; - - /* Traverse index files list. For each entry, append it to the given - * path and see if the file exists. If it exists, break the loop */ - while ((list = next_option(list, &filename_vec, NULL)) != NULL) { - /* Ignore too long entries that may overflow path buffer */ - if ((filename_vec.len + 1) > (path_len - (n + 1))) { - continue; - } - - /* Prepare full path to the index file */ - mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); - - /* Does it exist? */ - if (mg_stat(conn, path, filestat)) { - /* Yes it does, break the loop */ - found = 1; - break; - } - } - - /* If no index file exists, restore directory path */ - if (!found) { - path[n] = '\0'; - } - - return found; -} - -/* Same as above, except if the first try fails and a fallback-root is - * configured, we'll try there also */ -static int -substitute_index_file(struct mg_connection *conn, - char *path, - size_t path_len, - struct mg_file_stat *filestat) -{ - int ret = substitute_index_file_aux(conn, path, path_len, filestat); - if (ret == 0) { - const char *root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT]; - const char *fallback_root_prefix = - conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]; - if ((root_prefix) && (fallback_root_prefix)) { - const size_t root_prefix_len = strlen(root_prefix); - if ((strncmp(path, root_prefix, root_prefix_len) == 0)) { - char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid - side effects if we fail */ - size_t sub_path_len; - - const size_t fallback_root_prefix_len = - strlen(fallback_root_prefix); - const char *sub_path = path + root_prefix_len; - while (*sub_path == '/') { - sub_path++; - } - sub_path_len = strlen(sub_path); - - if (((fallback_root_prefix_len + 1 + sub_path_len + 1) - < sizeof(scratch_path))) { - /* The concatenations below are all safe because we - * pre-verified string lengths above */ - char *nul; - strcpy(scratch_path, fallback_root_prefix); - nul = strchr(scratch_path, '\0'); - if ((nul > scratch_path) && (*(nul - 1) != '/')) { - *nul++ = '/'; - *nul = '\0'; - } - strcat(scratch_path, sub_path); - if (substitute_index_file_aux(conn, - scratch_path, - sizeof(scratch_path), - filestat)) { - mg_strlcpy(path, scratch_path, path_len); - return 1; - } - } - } - } - } - return ret; -} - -#endif - - -static void -interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ - char *filename, /* out: filename */ - size_t filename_buf_len, /* in: size of filename buffer */ - struct mg_file_stat *filestat, /* out: file status structure */ - int *is_found, /* out: file found (directly) */ - int *is_script_resource, /* out: handled by a script? */ - int *is_websocket_request, /* out: websocket connection? */ - int *is_put_or_delete_request, /* out: put/delete a file? */ - int *is_webdav_request, /* out: webdav request? */ - int *is_template_text /* out: SSI file or LSP file? */ -) -{ - char const *accept_encoding; - -#if !defined(NO_FILES) - const char *uri = conn->request_info.local_uri; - const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT], - conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT], - NULL}; - int fileExists = 0; - const char *rewrite; - struct vec a, b; - ptrdiff_t match_len; - char gz_path[UTF8_PATH_MAX]; - int truncated; - int i; -#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) - char *tmp_str; - size_t tmp_str_len, sep_pos; - int allow_substitute_script_subresources; -#endif -#else - (void)filename_buf_len; /* unused if NO_FILES is defined */ -#endif - - /* Step 1: Set all initially unknown outputs to zero */ - memset(filestat, 0, sizeof(*filestat)); - *filename = 0; - *is_found = 0; - *is_script_resource = 0; - *is_template_text = 0; - - /* Step 2: Classify the request method */ - /* Step 2a: Check if the request attempts to modify the file system */ - *is_put_or_delete_request = is_put_or_delete_method(conn); - /* Step 2b: Check if the request uses WebDav method that requires special - * handling */ - *is_webdav_request = is_civetweb_webdav_method(conn); - - /* Step 3: Check if it is a websocket request, and modify the document - * root if required */ -#if defined(USE_WEBSOCKET) - *is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET); -#if !defined(NO_FILES) - if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) { - roots[0] = conn->dom_ctx->config[WEBSOCKET_ROOT]; - roots[1] = conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]; - } -#endif /* !NO_FILES */ -#else /* USE_WEBSOCKET */ - *is_websocket_request = 0; -#endif /* USE_WEBSOCKET */ - - /* Step 4: Check if gzip encoded response is allowed */ - conn->accept_gzip = 0; - if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { - if (strstr(accept_encoding, "gzip") != NULL) { - conn->accept_gzip = 1; - } - } - -#if !defined(NO_FILES) - /* Step 5: If there is no root directory, don't look for files. */ - /* Note that roots[0] == NULL is a regular use case here. This occurs, - * if all requests are handled by callbacks, so the WEBSOCKET_ROOT - * config is not required. */ - if (roots[0] == NULL) { - /* all file related outputs have already been set to 0, just return - */ - return; - } - - for (i = 0; roots[i] != NULL; i++) { - /* Step 6: Determine the local file path from the root path and the - * request uri. */ - /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift - * part of the path one byte on the right. */ - truncated = 0; - mg_snprintf(conn, - &truncated, - filename, - filename_buf_len - 1, - "%s%s", - roots[i], - uri); - - if (truncated) { - goto interpret_cleanup; - } - - /* Step 7: URI rewriting */ - rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN]; - while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { - if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { - mg_snprintf(conn, - &truncated, - filename, - filename_buf_len - 1, - "%.*s%s", - (int)b.len, - b.ptr, - uri + match_len); - break; - } - } - - if (truncated) { - goto interpret_cleanup; - } - - /* Step 8: Check if the file exists at the server */ - /* Local file path and name, corresponding to requested URI - * is now stored in "filename" variable. */ - if (mg_stat(conn, filename, filestat)) { - fileExists = 1; - break; - } - } - - if (fileExists) { - int uri_len = (int)strlen(uri); - int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/'); - - /* 8.1: File exists. */ - *is_found = 1; - - /* 8.2: Check if it is a script type. */ - if (extention_matches_script(conn, filename)) { - /* The request addresses a CGI resource, Lua script or - * server-side javascript. - * The URI corresponds to the script itself (like - * /path/script.cgi), and there is no additional resource - * path (like /path/script.cgi/something). - * Requests that modify (replace or delete) a resource, like - * PUT and DELETE requests, should replace/delete the script - * file. - * Requests that read or write from/to a resource, like GET and - * POST requests, should call the script and return the - * generated response. */ - *is_script_resource = (!*is_put_or_delete_request); - } - - /* 8.3: Check for SSI and LSP files */ - if (extention_matches_template_text(conn, filename)) { - /* Same as above, but for *.lsp and *.shtml files. */ - /* A "template text" is a file delivered directly to the client, - * but with some text tags replaced by dynamic content. - * E.g. a Server Side Include (SSI) or Lua Page/Lua Server Page - * (LP, LSP) file. */ - *is_template_text = (!*is_put_or_delete_request); - } - - /* 8.4: If the request target is a directory, there could be - * a substitute file (index.html, index.cgi, ...). */ - /* But do not substitute a directory for a WebDav request */ - if (filestat->is_directory && is_uri_end_slash - && (!*is_webdav_request)) { - /* Use a local copy here, since substitute_index_file will - * change the content of the file status */ - struct mg_file_stat tmp_filestat; - memset(&tmp_filestat, 0, sizeof(tmp_filestat)); - - if (substitute_index_file( - conn, filename, filename_buf_len, &tmp_filestat)) { - - /* Substitute file found. Copy stat to the output, then - * check if the file is a script file */ - *filestat = tmp_filestat; - - if (extention_matches_script(conn, filename)) { - /* Substitute file is a script file */ - *is_script_resource = 1; - } else if (extention_matches_template_text(conn, filename)) { - /* Substitute file is a LSP or SSI file */ - *is_template_text = 1; - } else { - /* Substitute file is a regular file */ - *is_script_resource = 0; - *is_found = (mg_stat(conn, filename, filestat) ? 1 : 0); - } - } - /* If there is no substitute file, the server could return - * a directory listing in a later step */ - } - return; - } - - /* Step 9: Check for zipped files: */ - /* If we can't find the actual file, look for the file - * with the same name but a .gz extension. If we find it, - * use that and set the gzipped flag in the file struct - * to indicate that the response need to have the content- - * encoding: gzip header. - * We can only do this if the browser declares support. */ - if (conn->accept_gzip) { - mg_snprintf( - conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename); - - if (truncated) { - goto interpret_cleanup; - } - - if (mg_stat(conn, gz_path, filestat)) { - if (filestat) { - filestat->is_gzipped = 1; - *is_found = 1; - } - /* Currently gz files can not be scripts. */ - return; - } - } - -#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) - /* Step 10: Script resources may handle sub-resources */ - /* Support PATH_INFO for CGI scripts. */ - tmp_str_len = strlen(filename); - tmp_str = - (char *)mg_malloc_ctx(tmp_str_len + UTF8_PATH_MAX + 1, conn->phys_ctx); - if (!tmp_str) { - /* Out of memory */ - goto interpret_cleanup; - } - memcpy(tmp_str, filename, tmp_str_len + 1); - - /* Check config, if index scripts may have sub-resources */ - allow_substitute_script_subresources = - !mg_strcasecmp(conn->dom_ctx->config[ALLOW_INDEX_SCRIPT_SUB_RES], - "yes"); - if (*is_webdav_request) { - /* TO BE DEFINED: Should scripts handle special WebDAV methods lile - * PROPFIND for their subresources? */ - /* allow_substitute_script_subresources = 0; */ - } - - sep_pos = tmp_str_len; - while (sep_pos > 0) { - sep_pos--; - if (tmp_str[sep_pos] == '/') { - int is_script = 0, does_exist = 0; - - tmp_str[sep_pos] = 0; - if (tmp_str[0]) { - is_script = extention_matches_script(conn, tmp_str); - does_exist = mg_stat(conn, tmp_str, filestat); - } - - if (does_exist && is_script) { - filename[sep_pos] = 0; - memmove(filename + sep_pos + 2, - filename + sep_pos + 1, - strlen(filename + sep_pos + 1) + 1); - conn->path_info = filename + sep_pos + 1; - filename[sep_pos + 1] = '/'; - *is_script_resource = 1; - *is_found = 1; - break; - } - - if (allow_substitute_script_subresources) { - if (substitute_index_file( - conn, tmp_str, tmp_str_len + UTF8_PATH_MAX, filestat)) { - - /* some intermediate directory has an index file */ - if (extention_matches_script(conn, tmp_str)) { - - size_t script_name_len = strlen(tmp_str); - - /* subres_name read before this memory locatio will be - overwritten */ - char *subres_name = filename + sep_pos; - size_t subres_name_len = strlen(subres_name); - - DEBUG_TRACE("Substitute script %s serving path %s", - tmp_str, - filename); - - /* this index file is a script */ - if ((script_name_len + subres_name_len + 2) - >= filename_buf_len) { - mg_free(tmp_str); - goto interpret_cleanup; - } - - conn->path_info = - filename + script_name_len + 1; /* new target */ - memmove(conn->path_info, subres_name, subres_name_len); - conn->path_info[subres_name_len] = 0; - memcpy(filename, tmp_str, script_name_len + 1); - - *is_script_resource = 1; - *is_found = 1; - break; - - } else { - - DEBUG_TRACE("Substitute file %s serving path %s", - tmp_str, - filename); - - /* non-script files will not have sub-resources */ - filename[sep_pos] = 0; - conn->path_info = 0; - *is_script_resource = 0; - *is_found = 0; - break; - } - } - } - - tmp_str[sep_pos] = '/'; - } - } - - mg_free(tmp_str); - -#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */ -#endif /* !defined(NO_FILES) */ - return; - -#if !defined(NO_FILES) -/* Reset all outputs */ -interpret_cleanup: - memset(filestat, 0, sizeof(*filestat)); - *filename = 0; - *is_found = 0; - *is_script_resource = 0; - *is_websocket_request = 0; - *is_put_or_delete_request = 0; -#endif /* !defined(NO_FILES) */ -} - - -/* Check whether full request is buffered. Return: - * -1 if request or response is malformed - * 0 if request or response is not yet fully buffered - * >0 actual request length, including last \r\n\r\n */ -static int -get_http_header_len(const char *buf, int buflen) -{ - int i; - for (i = 0; i < buflen; i++) { - /* Do an unsigned comparison in some conditions below */ - const unsigned char c = (unsigned char)buf[i]; - - if ((c < 128) && ((char)c != '\r') && ((char)c != '\n') - && !isprint(c)) { - /* abort scan as soon as one malformed character is found */ - return -1; - } - - if (i < buflen - 1) { - if ((buf[i] == '\n') && (buf[i + 1] == '\n')) { - /* Two newline, no carriage return - not standard compliant, - * but it should be accepted */ - return i + 2; - } - } - - if (i < buflen - 3) { - if ((buf[i] == '\r') && (buf[i + 1] == '\n') && (buf[i + 2] == '\r') - && (buf[i + 3] == '\n')) { - /* Two \r\n - standard compliant */ - return i + 4; - } - } - } - - return 0; -} - - -#if !defined(NO_CACHING) -/* Convert month to the month number. Return -1 on error, or month number */ -static int -get_month_index(const char *s) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(month_names); i++) { - if (!strcmp(s, month_names[i])) { - return (int)i; - } - } - - return -1; -} - - -/* Parse UTC date-time string, and return the corresponding time_t value. */ -static time_t -parse_date_string(const char *datetime) -{ - char month_str[32] = {0}; - int second, minute, hour, day, month, year; - time_t result = (time_t)0; - struct tm tm; - - if ((sscanf(datetime, - "%d/%3s/%d %d:%d:%d", - &day, - month_str, - &year, - &hour, - &minute, - &second) - == 6) - || (sscanf(datetime, - "%d %3s %d %d:%d:%d", - &day, - month_str, - &year, - &hour, - &minute, - &second) - == 6) - || (sscanf(datetime, - "%*3s, %d %3s %d %d:%d:%d", - &day, - month_str, - &year, - &hour, - &minute, - &second) - == 6) - || (sscanf(datetime, - "%d-%3s-%d %d:%d:%d", - &day, - month_str, - &year, - &hour, - &minute, - &second) - == 6)) { - month = get_month_index(month_str); - if ((month >= 0) && (year >= 1970)) { - memset(&tm, 0, sizeof(tm)); - tm.tm_year = year - 1900; - tm.tm_mon = month; - tm.tm_mday = day; - tm.tm_hour = hour; - tm.tm_min = minute; - tm.tm_sec = second; - result = timegm(&tm); - } - } - - return result; -} -#endif /* !NO_CACHING */ - - -/* Pre-process URIs according to RFC + protect against directory disclosure - * attacks by removing '..', excessive '/' and '\' characters */ -static void -remove_dot_segments(char *inout) -{ - /* Windows backend protection - * (https://tools.ietf.org/html/rfc3986#section-7.3): Replace backslash - * in URI by slash */ - char *out_end = inout; - char *in = inout; - - if (!in) { - /* Param error. */ - return; - } - - while (*in) { - if (*in == '\\') { - *in = '/'; - } - in++; - } - - /* Algorithm "remove_dot_segments" from - * https://tools.ietf.org/html/rfc3986#section-5.2.4 */ - /* Step 1: - * The input buffer is initialized. - * The output buffer is initialized to the empty string. - */ - in = inout; - - /* Step 2: - * While the input buffer is not empty, loop as follows: - */ - /* Less than out_end of the inout buffer is used as output, so keep - * condition: out_end <= in */ - while (*in) { - /* Step 2a: - * If the input buffer begins with a prefix of "../" or "./", - * then remove that prefix from the input buffer; - */ - if (!strncmp(in, "../", 3)) { - in += 3; - } else if (!strncmp(in, "./", 2)) { - in += 2; - } - /* otherwise */ - /* Step 2b: - * if the input buffer begins with a prefix of "/./" or "/.", - * where "." is a complete path segment, then replace that - * prefix with "/" in the input buffer; - */ - else if (!strncmp(in, "/./", 3)) { - in += 2; - } else if (!strcmp(in, "/.")) { - in[1] = 0; - } - /* otherwise */ - /* Step 2c: - * if the input buffer begins with a prefix of "/../" or "/..", - * where ".." is a complete path segment, then replace that - * prefix with "/" in the input buffer and remove the last - * segment and its preceding "/" (if any) from the output - * buffer; - */ - else if (!strncmp(in, "/../", 4)) { - in += 3; - if (inout != out_end) { - /* remove last segment */ - do { - out_end--; - } while ((inout != out_end) && (*out_end != '/')); - } - } else if (!strcmp(in, "/..")) { - in[1] = 0; - if (inout != out_end) { - /* remove last segment */ - do { - out_end--; - } while ((inout != out_end) && (*out_end != '/')); - } - } - /* otherwise */ - /* Step 2d: - * if the input buffer consists only of "." or "..", then remove - * that from the input buffer; - */ - else if (!strcmp(in, ".") || !strcmp(in, "..")) { - *in = 0; - } - /* otherwise */ - /* Step 2e: - * move the first path segment in the input buffer to the end of - * the output buffer, including the initial "/" character (if - * any) and any subsequent characters up to, but not including, - * the next "/" character or the end of the input buffer. - */ - else { - do { - *out_end = *in; - out_end++; - in++; - } while ((*in != 0) && (*in != '/')); - } - } - - /* Step 3: - * Finally, the output buffer is returned as the result of - * remove_dot_segments. - */ - /* Terminate output */ - *out_end = 0; - - /* For Windows, the files/folders "x" and "x." (with a dot but without - * extension) are identical. Replace all "./" by "/" and remove a "." at - * the end. Also replace all "//" by "/". Repeat until there is no "./" - * or "//" anymore. - */ - out_end = in = inout; - while (*in) { - if (*in == '.') { - /* remove . at the end or preceding of / */ - char *in_ahead = in; - do { - in_ahead++; - } while (*in_ahead == '.'); - if (*in_ahead == '/') { - in = in_ahead; - if ((out_end != inout) && (out_end[-1] == '/')) { - /* remove generated // */ - out_end--; - } - } else if (*in_ahead == 0) { - in = in_ahead; - } else { - do { - *out_end++ = '.'; - in++; - } while (in != in_ahead); - } - } else if (*in == '/') { - /* replace // by / */ - *out_end++ = '/'; - do { - in++; - } while (*in == '/'); - } else { - *out_end++ = *in; - in++; - } - } - *out_end = 0; -} - - -static const struct { - const char *extension; - size_t ext_len; - const char *mime_type; -} builtin_mime_types[] = { - /* IANA registered MIME types - * (http://www.iana.org/assignments/media-types) - * application types */ - {".bin", 4, "application/octet-stream"}, - {".cer", 4, "application/pkix-cert"}, - {".crl", 4, "application/pkix-crl"}, - {".crt", 4, "application/pkix-cert"}, - {".deb", 4, "application/octet-stream"}, - {".dmg", 4, "application/octet-stream"}, - {".dll", 4, "application/octet-stream"}, - {".doc", 4, "application/msword"}, - {".eps", 4, "application/postscript"}, - {".exe", 4, "application/octet-stream"}, - {".iso", 4, "application/octet-stream"}, - {".js", 3, "application/javascript"}, - {".json", 5, "application/json"}, - {".mjs", 4, "application/javascript"}, - {".msi", 4, "application/octet-stream"}, - {".pem", 4, "application/x-pem-file"}, - {".pdf", 4, "application/pdf"}, - {".ps", 3, "application/postscript"}, - {".rtf", 4, "application/rtf"}, - {".wasm", 5, "application/wasm"}, - {".xhtml", 6, "application/xhtml+xml"}, - {".xsl", 4, "application/xml"}, - {".xslt", 5, "application/xml"}, - - /* fonts */ - {".ttf", 4, "application/font-sfnt"}, - {".cff", 4, "application/font-sfnt"}, - {".otf", 4, "application/font-sfnt"}, - {".aat", 4, "application/font-sfnt"}, - {".sil", 4, "application/font-sfnt"}, - {".pfr", 4, "application/font-tdpfr"}, - {".woff", 5, "application/font-woff"}, - {".woff2", 6, "application/font-woff2"}, - - /* audio */ - {".mp3", 4, "audio/mpeg"}, - {".oga", 4, "audio/ogg"}, - {".ogg", 4, "audio/ogg"}, - - /* image */ - {".gif", 4, "image/gif"}, - {".ief", 4, "image/ief"}, - {".jpeg", 5, "image/jpeg"}, - {".jpg", 4, "image/jpeg"}, - {".jpm", 4, "image/jpm"}, - {".jpx", 4, "image/jpx"}, - {".png", 4, "image/png"}, - {".svg", 4, "image/svg+xml"}, - {".tif", 4, "image/tiff"}, - {".tiff", 5, "image/tiff"}, - - /* model */ - {".wrl", 4, "model/vrml"}, - - /* text */ - {".css", 4, "text/css"}, - {".csv", 4, "text/csv"}, - {".htm", 4, "text/html"}, - {".html", 5, "text/html"}, - {".sgm", 4, "text/sgml"}, - {".shtm", 5, "text/html"}, - {".shtml", 6, "text/html"}, - {".txt", 4, "text/plain"}, - {".xml", 4, "text/xml"}, - - /* video */ - {".mov", 4, "video/quicktime"}, - {".mp4", 4, "video/mp4"}, - {".mpeg", 5, "video/mpeg"}, - {".mpg", 4, "video/mpeg"}, - {".ogv", 4, "video/ogg"}, - {".qt", 3, "video/quicktime"}, - - /* not registered types - * (http://reference.sitepoint.com/html/mime-types-full, - * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */ - {".arj", 4, "application/x-arj-compressed"}, - {".gz", 3, "application/x-gunzip"}, - {".rar", 4, "application/x-arj-compressed"}, - {".swf", 4, "application/x-shockwave-flash"}, - {".tar", 4, "application/x-tar"}, - {".tgz", 4, "application/x-tar-gz"}, - {".torrent", 8, "application/x-bittorrent"}, - {".ppt", 4, "application/x-mspowerpoint"}, - {".xls", 4, "application/x-msexcel"}, - {".zip", 4, "application/x-zip-compressed"}, - {".aac", - 4, - "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */ - {".flac", 5, "audio/flac"}, - {".aif", 4, "audio/x-aif"}, - {".m3u", 4, "audio/x-mpegurl"}, - {".mid", 4, "audio/x-midi"}, - {".ra", 3, "audio/x-pn-realaudio"}, - {".ram", 4, "audio/x-pn-realaudio"}, - {".wav", 4, "audio/x-wav"}, - {".bmp", 4, "image/bmp"}, - {".ico", 4, "image/x-icon"}, - {".pct", 4, "image/x-pct"}, - {".pict", 5, "image/pict"}, - {".rgb", 4, "image/x-rgb"}, - {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */ - {".asf", 4, "video/x-ms-asf"}, - {".avi", 4, "video/x-msvideo"}, - {".m4v", 4, "video/x-m4v"}, - {NULL, 0, NULL}}; - - -CIVETWEB_API const char * -mg_get_builtin_mime_type(const char *path) -{ - const char *ext; - size_t i, path_len; - - path_len = strlen(path); - - for (i = 0; builtin_mime_types[i].extension != NULL; i++) { - ext = path + (path_len - builtin_mime_types[i].ext_len); - if ((path_len > builtin_mime_types[i].ext_len) - && (mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0)) { - return builtin_mime_types[i].mime_type; - } - } - - return "text/plain"; -} - - -/* Look at the "path" extension and figure what mime type it has. - * Store mime type in the vector. */ -static void -get_mime_type(struct mg_connection *conn, const char *path, struct vec *vec) -{ - struct vec ext_vec, mime_vec; - const char *list, *ext; - size_t path_len; - - path_len = strlen(path); - - if ((conn == NULL) || (vec == NULL)) { - if (vec != NULL) { - memset(vec, '\0', sizeof(struct vec)); - } - return; - } - - /* Scan user-defined mime types first, in case user wants to - * override default mime types. */ - list = conn->dom_ctx->config[EXTRA_MIME_TYPES]; - while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { - /* ext now points to the path suffix */ - ext = path + path_len - ext_vec.len; - if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { - *vec = mime_vec; - return; - } - } - - vec->ptr = mg_get_builtin_mime_type(path); - vec->len = strlen(vec->ptr); -} - - -/* Stringify binary data. Output buffer must be twice as big as input, - * because each byte takes 2 bytes in string representation */ -static void -bin2str(char *to, const unsigned char *p, size_t len) -{ - static const char *hex = "0123456789abcdef"; - - for (; len--; p++) { - *to++ = hex[p[0] >> 4]; - *to++ = hex[p[0] & 0x0f]; - } - *to = '\0'; -} - - -/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. - */ -CIVETWEB_API char * -mg_md5(char buf[33], ...) -{ - md5_byte_t hash[16]; - const char *p; - va_list ap; - md5_state_t ctx; - - md5_init(&ctx); - - va_start(ap, buf); - while ((p = va_arg(ap, const char *)) != NULL) { - md5_append(&ctx, (const md5_byte_t *)p, strlen(p)); - } - va_end(ap); - - md5_finish(&ctx, hash); - bin2str(buf, hash, sizeof(hash)); - return buf; -} - - -/* Check the user's password, return 1 if OK */ -static int -check_password_digest(const char *method, - const char *ha1, - const char *uri, - const char *nonce, - const char *nc, - const char *cnonce, - const char *qop, - const char *response) -{ - char ha2[32 + 1], expected_response[32 + 1]; - - /* Some of the parameters may be NULL */ - if ((method == NULL) || (nonce == NULL) || (nc == NULL) || (cnonce == NULL) - || (qop == NULL) || (response == NULL)) { - return 0; - } - - /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */ - if (strlen(response) != 32) { - return 0; - } - - mg_md5(ha2, method, ":", uri, NULL); - mg_md5(expected_response, - ha1, - ":", - nonce, - ":", - nc, - ":", - cnonce, - ":", - qop, - ":", - ha2, - NULL); - - return mg_strcasecmp(response, expected_response) == 0; -} - - -#if !defined(NO_FILESYSTEMS) -/* Use the global passwords file, if specified by auth_gpass option, - * or search for .htpasswd in the requested directory. */ -static void -open_auth_file(struct mg_connection *conn, - const char *path, - struct mg_file *filep) -{ - if ((conn != NULL) && (conn->dom_ctx != NULL)) { - char name[UTF8_PATH_MAX]; - const char *p, *e, - *gpass = conn->dom_ctx->config[GLOBAL_PASSWORDS_FILE]; - int truncated; - - if (gpass != NULL) { - /* Use global passwords file */ - if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) { -#if defined(DEBUG) - /* Use mg_cry_internal here, since gpass has been - * configured. */ - mg_cry_internal(conn, "fopen(%s): %s", gpass, strerror(ERRNO)); -#endif - } - /* Important: using local struct mg_file to test path for - * is_directory flag. If filep is used, mg_stat() makes it - * appear as if auth file was opened. - * TODO(mid): Check if this is still required after rewriting - * mg_stat */ - } else if (mg_stat(conn, path, &filep->stat) - && filep->stat.is_directory) { - mg_snprintf(conn, - &truncated, - name, - sizeof(name), - "%s/%s", - path, - PASSWORDS_FILE_NAME); - - if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) { -#if defined(DEBUG) - /* Don't use mg_cry_internal here, but only a trace, since - * this is a typical case. It will occur for every directory - * without a password file. */ - DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO)); -#endif - } - } else { - /* Try to find .htpasswd in requested directory. */ - for (p = path, e = p + strlen(p) - 1; e > p; e--) { - if (e[0] == '/') { - break; - } - } - mg_snprintf(conn, - &truncated, - name, - sizeof(name), - "%.*s/%s", - (int)(e - p), - p, - PASSWORDS_FILE_NAME); - - if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) { -#if defined(DEBUG) - /* Don't use mg_cry_internal here, but only a trace, since - * this is a typical case. It will occur for every directory - * without a password file. */ - DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO)); -#endif - } - } - } -} -#endif /* NO_FILESYSTEMS */ - - -/* Parsed Authorization header */ -struct auth_header { - char *user; - int type; /* 1 = basic, 2 = digest */ - char *plain_password; /* Basic only */ - char *uri, *cnonce, *response, *qop, *nc, *nonce; /* Digest only */ -}; - - -/* Return 1 on success. Always initializes the auth_header structure. */ -static int -parse_auth_header(struct mg_connection *conn, - char *buf, - size_t buf_size, - struct auth_header *auth_header) -{ - char *name, *value, *s; - const char *ah; - uint64_t nonce; - - if (!auth_header || !conn) { - return 0; - } - - (void)memset(auth_header, 0, sizeof(*auth_header)); - ah = mg_get_header(conn, "Authorization"); - - if (ah == NULL) { - /* No Authorization header at all */ - return 0; - } - if (0 == mg_strncasecmp(ah, "Basic ", 6)) { - /* Basic Auth (we never asked for this, but some client may send it) */ - char *split; - const char *userpw_b64 = ah + 6; - size_t userpw_b64_len = strlen(userpw_b64); - size_t buf_len_r = buf_size; - if (mg_base64_decode( - userpw_b64, userpw_b64_len, (unsigned char *)buf, &buf_len_r) - != -1) { - return 0; /* decode error */ - } - split = strchr(buf, ':'); - if (!split) { - return 0; /* Format error */ - } - - /* Separate string at ':' */ - *split = 0; - - /* User name is before ':', Password is after ':' */ - auth_header->user = buf; - auth_header->type = 1; - auth_header->plain_password = split + 1; - - return 1; - - } else if (0 == mg_strncasecmp(ah, "Digest ", 7)) { - /* Digest Auth ... implemented below */ - auth_header->type = 2; - - } else { - /* Unknown or invalid Auth method */ - return 0; - } - - /* Make modifiable copy of the auth header */ - (void)mg_strlcpy(buf, ah + 7, buf_size); - s = buf; - - /* Parse authorization header */ - for (;;) { - /* Gobble initial spaces */ - while (isspace((unsigned char)*s)) { - s++; - } - name = skip_quoted(&s, "=", " ", 0); - /* Value is either quote-delimited, or ends at first comma or space. - */ - if (s[0] == '\"') { - s++; - value = skip_quoted(&s, "\"", " ", '\\'); - if (s[0] == ',') { - s++; - } - } else { - value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF - * uses spaces */ - } - if (*name == '\0') { - break; - } - - if (!strcmp(name, "username")) { - auth_header->user = value; - } else if (!strcmp(name, "cnonce")) { - auth_header->cnonce = value; - } else if (!strcmp(name, "response")) { - auth_header->response = value; - } else if (!strcmp(name, "uri")) { - auth_header->uri = value; - } else if (!strcmp(name, "qop")) { - auth_header->qop = value; - } else if (!strcmp(name, "nc")) { - auth_header->nc = value; - } else if (!strcmp(name, "nonce")) { - auth_header->nonce = value; - } - } - -#if !defined(NO_NONCE_CHECK) - /* Read the nonce from the response. */ - if (auth_header->nonce == NULL) { - return 0; - } - s = NULL; - nonce = strtoull(auth_header->nonce, &s, 10); - if ((s == NULL) || (*s != 0)) { - return 0; - } - - /* Convert the nonce from the client to a number. */ - nonce ^= conn->dom_ctx->auth_nonce_mask; - - /* The converted number corresponds to the time the nounce has been - * created. This should not be earlier than the server start. */ - /* Server side nonce check is valuable in all situations but one: - * if the server restarts frequently, but the client should not see - * that, so the server should accept nonces from previous starts. */ - /* However, the reasonable default is to not accept a nonce from a - * previous start, so if anyone changed the access rights between - * two restarts, a new login is required. */ - if (nonce < (uint64_t)conn->phys_ctx->start_time) { - /* nonce is from a previous start of the server and no longer valid - * (replay attack?) */ - return 0; - } - /* Check if the nonce is too high, so it has not (yet) been used by the - * server. */ - if (nonce >= ((uint64_t)conn->phys_ctx->start_time - + conn->dom_ctx->nonce_count)) { - return 0; - } -#else - (void)nonce; -#endif - - return (auth_header->user != NULL); -} - - -static const char * -mg_fgets(char *buf, size_t size, struct mg_file *filep) -{ - if (!filep) { - return NULL; - } - - if (filep->access.fp != NULL) { - return fgets(buf, (int)size, filep->access.fp); - } else { - return NULL; - } -} - -/* Define the initial recursion depth for procesesing htpasswd files that - * include other htpasswd - * (or even the same) files. It is not difficult to provide a file or files - * s.t. they force civetweb - * to infinitely recurse and then crash. - */ -#define INITIAL_DEPTH 9 -#if INITIAL_DEPTH <= 0 -#error Bad INITIAL_DEPTH for recursion, set to at least 1 -#endif - -#if !defined(NO_FILESYSTEMS) -struct read_auth_file_struct { - struct mg_connection *conn; - struct auth_header auth_header; - const char *domain; - char buf[256 + 256 + 40]; - const char *f_user; - const char *f_domain; - const char *f_ha1; -}; - - -static int -read_auth_file(struct mg_file *filep, - struct read_auth_file_struct *workdata, - int depth) -{ - int is_authorized = 0; - struct mg_file fp; - size_t l; - - if (!filep || !workdata || (0 == depth)) { - return 0; - } - - /* Loop over passwords file */ - while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep) != NULL) { - l = strlen(workdata->buf); - while (l > 0) { - if (isspace((unsigned char)workdata->buf[l - 1]) - || iscntrl((unsigned char)workdata->buf[l - 1])) { - l--; - workdata->buf[l] = 0; - } else - break; - } - if (l < 1) { - continue; - } - - workdata->f_user = workdata->buf; - - if (workdata->f_user[0] == ':') { - /* user names may not contain a ':' and may not be empty, - * so lines starting with ':' may be used for a special purpose - */ - if (workdata->f_user[1] == '#') { - /* :# is a comment */ - continue; - } else if (!strncmp(workdata->f_user + 1, "include=", 8)) { - if (mg_fopen(workdata->conn, - workdata->f_user + 9, - MG_FOPEN_MODE_READ, - &fp)) { - is_authorized = read_auth_file(&fp, workdata, depth - 1); - (void)mg_fclose( - &fp.access); /* ignore error on read only file */ - - /* No need to continue processing files once we have a - * match, since nothing will reset it back - * to 0. - */ - if (is_authorized) { - return is_authorized; - } - } else { - mg_cry_internal(workdata->conn, - "%s: cannot open authorization file: %s", - __func__, - workdata->buf); - } - continue; - } - /* everything is invalid for the moment (might change in the - * future) */ - mg_cry_internal(workdata->conn, - "%s: syntax error in authorization file: %s", - __func__, - workdata->buf); - continue; - } - - workdata->f_domain = strchr(workdata->f_user, ':'); - if (workdata->f_domain == NULL) { - mg_cry_internal(workdata->conn, - "%s: syntax error in authorization file: %s", - __func__, - workdata->buf); - continue; - } - *(char *)(workdata->f_domain) = 0; - (workdata->f_domain)++; - - workdata->f_ha1 = strchr(workdata->f_domain, ':'); - if (workdata->f_ha1 == NULL) { - mg_cry_internal(workdata->conn, - "%s: syntax error in authorization file: %s", - __func__, - workdata->buf); - continue; - } - *(char *)(workdata->f_ha1) = 0; - (workdata->f_ha1)++; - - if (!strcmp(workdata->auth_header.user, workdata->f_user) - && !strcmp(workdata->domain, workdata->f_domain)) { - switch (workdata->auth_header.type) { - case 1: /* Basic */ - { - char md5[33]; - mg_md5(md5, - workdata->f_user, - ":", - workdata->domain, - ":", - workdata->auth_header.plain_password, - NULL); - return 0 == memcmp(workdata->f_ha1, md5, 33); - } - case 2: /* Digest */ - return check_password_digest( - workdata->conn->request_info.request_method, - workdata->f_ha1, - workdata->auth_header.uri, - workdata->auth_header.nonce, - workdata->auth_header.nc, - workdata->auth_header.cnonce, - workdata->auth_header.qop, - workdata->auth_header.response); - default: /* None/Other/Unknown */ - return 0; - } - } - } - - return is_authorized; -} - - -/* Authorize against the opened passwords file. Return 1 if authorized. */ -static int -authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm) -{ - struct read_auth_file_struct workdata; - char buf[MG_BUF_LEN]; - - if (!conn || !conn->dom_ctx) { - return 0; - } - - memset(&workdata, 0, sizeof(workdata)); - workdata.conn = conn; - - if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.auth_header)) { - return 0; - } - - /* CGI needs it as REMOTE_USER */ - conn->request_info.remote_user = - mg_strdup_ctx(workdata.auth_header.user, conn->phys_ctx); - - if (realm) { - workdata.domain = realm; - } else { - workdata.domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN]; - } - - return read_auth_file(filep, &workdata, INITIAL_DEPTH); -} - - -/* Public function to check http digest authentication header */ -CIVETWEB_API int -mg_check_digest_access_authentication(struct mg_connection *conn, - const char *realm, - const char *filename) -{ - struct mg_file file = STRUCT_FILE_INITIALIZER; - int auth; - - if (!conn || !filename) { - return -1; - } - if (!mg_fopen(conn, filename, MG_FOPEN_MODE_READ, &file)) { - return -2; - } - - auth = authorize(conn, &file, realm); - - mg_fclose(&file.access); - - return auth; -} -#endif /* NO_FILESYSTEMS */ - - -/* Return 1 if request is authorised, 0 otherwise. */ -static int -check_authorization(struct mg_connection *conn, const char *path) -{ -#if !defined(NO_FILESYSTEMS) - char fname[UTF8_PATH_MAX]; - struct vec uri_vec, filename_vec; - const char *list; - struct mg_file file = STRUCT_FILE_INITIALIZER; - int authorized = 1, truncated; - - if (!conn || !conn->dom_ctx) { - return 0; - } - - list = conn->dom_ctx->config[PROTECT_URI]; - while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { - if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) { - mg_snprintf(conn, - &truncated, - fname, - sizeof(fname), - "%.*s", - (int)filename_vec.len, - filename_vec.ptr); - - if (truncated - || !mg_fopen(conn, fname, MG_FOPEN_MODE_READ, &file)) { - mg_cry_internal(conn, - "%s: cannot open %s: %s", - __func__, - fname, - strerror(errno)); - } - break; - } - } - - if (!is_file_opened(&file.access)) { - open_auth_file(conn, path, &file); - } - - if (is_file_opened(&file.access)) { - authorized = authorize(conn, &file, NULL); - (void)mg_fclose(&file.access); /* ignore error on read only file */ - } - - return authorized; -#else - (void)conn; - (void)path; - return 1; -#endif /* NO_FILESYSTEMS */ -} - - -/* Internal function. Assumes conn is valid */ -static void -send_authorization_request(struct mg_connection *conn, const char *realm) -{ - uint64_t nonce = (uint64_t)(conn->phys_ctx->start_time); - int trunc = 0; - char buf[128]; - - if (!realm) { - realm = conn->dom_ctx->config[AUTHENTICATION_DOMAIN]; - } - - mg_lock_context(conn->phys_ctx); - nonce += conn->dom_ctx->nonce_count; - ++conn->dom_ctx->nonce_count; - mg_unlock_context(conn->phys_ctx); - - nonce ^= conn->dom_ctx->auth_nonce_mask; - conn->must_close = 1; - - /* Create 401 response */ - mg_response_header_start(conn, 401); - send_no_cache_header(conn); - send_additional_header(conn); - mg_response_header_add(conn, "Content-Length", "0", -1); - - /* Content for "WWW-Authenticate" header */ - mg_snprintf(conn, - &trunc, - buf, - sizeof(buf), - "Digest qop=\"auth\", realm=\"%s\", " - "nonce=\"%" UINT64_FMT "\"", - realm, - nonce); - - if (!trunc) { - /* !trunc should always be true */ - mg_response_header_add(conn, "WWW-Authenticate", buf, -1); - } - - /* Send all headers */ - mg_response_header_send(conn); -} - - -/* Interface function. Parameters are provided by the user, so do - * at least some basic checks. - */ -CIVETWEB_API int -mg_send_digest_access_authentication_request(struct mg_connection *conn, - const char *realm) -{ - if (conn && conn->dom_ctx) { - send_authorization_request(conn, realm); - return 0; - } - return -1; -} - - -#if !defined(NO_FILES) -static int -is_authorized_for_put(struct mg_connection *conn) -{ - int ret = 0; - - if (conn) { - struct mg_file file = STRUCT_FILE_INITIALIZER; - const char *passfile = conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE]; - - if (passfile != NULL - && mg_fopen(conn, passfile, MG_FOPEN_MODE_READ, &file)) { - ret = authorize(conn, &file, NULL); - (void)mg_fclose(&file.access); /* ignore error on read only file */ - } - } - - DEBUG_TRACE("file write authorization: %i", ret); - return ret; -} -#endif - - -CIVETWEB_API int -mg_modify_passwords_file_ha1(const char *fname, - const char *domain, - const char *user, - const char *ha1) -{ - int found = 0, i, result = 1; - char line[512], u[256], d[256], h[256]; - struct stat st = {0}; - FILE *fp = NULL; - char *temp_file = NULL; - int temp_file_offs = 0; - - /* Regard empty password as no password - remove user record. */ - if ((ha1 != NULL) && (ha1[0] == '\0')) { - ha1 = NULL; - } - - /* Other arguments must not be empty */ - if ((fname == NULL) || (domain == NULL) || (user == NULL)) { - return 0; - } - - /* Using the given file format, user name and domain must not contain - * the ':' character */ - if (strchr(user, ':') != NULL) { - return 0; - } - if (strchr(domain, ':') != NULL) { - return 0; - } - - /* Do not allow control characters like newline in user name and domain. - * Do not allow excessively long names either. */ - for (i = 0; ((i < 255) && (user[i] != 0)); i++) { - if (iscntrl((unsigned char)user[i])) { - return 0; - } - } - if (user[i]) { - return 0; /* user name too long */ - } - for (i = 0; ((i < 255) && (domain[i] != 0)); i++) { - if (iscntrl((unsigned char)domain[i])) { - return 0; - } - } - if (domain[i]) { - return 0; /* domain name too long */ - } - - /* The maximum length of the path to the password file is limited */ - if (strlen(fname) >= UTF8_PATH_MAX) { - return 0; - } - - /* Check if the file exists, and get file size */ - if (0 == stat(fname, &st)) { - int temp_buf_len; - if (st.st_size > 10485760) { - /* Some funster provided a >10 MB text file */ - return 0; - } - - /* Add enough space for one more line */ - temp_buf_len = (int)st.st_size + 1024; - - /* Allocate memory (instead of using a temporary file) */ - temp_file = (char *)mg_calloc((size_t)temp_buf_len, 1); - if (!temp_file) { - /* Out of memory */ - return 0; - } - - /* File exists. Read it into a memory buffer. */ - fp = fopen(fname, "r"); - if (fp == NULL) { - /* Cannot read file. No permission? */ - mg_free(temp_file); - return 0; - } - - /* Read content and store in memory */ - while ((fgets(line, sizeof(line), fp) != NULL) - && ((temp_file_offs + 600) < temp_buf_len)) { - /* file format is "user:domain:hash\n" */ - if (sscanf(line, "%255[^:]:%255[^:]:%255s", u, d, h) != 3) { - continue; - } - u[255] = 0; - d[255] = 0; - h[255] = 0; - - if (!strcmp(u, user) && !strcmp(d, domain)) { - /* Found the user: change the password hash or drop the user - */ - if ((ha1 != NULL) && (!found)) { - i = sprintf(temp_file + temp_file_offs, - "%s:%s:%s\n", - user, - domain, - ha1); - if (i < 1) { - fclose(fp); - mg_free(temp_file); - return 0; - } - temp_file_offs += i; - } - found = 1; - } else { - /* Copy existing user, including password hash */ - i = sprintf(temp_file + temp_file_offs, "%s:%s:%s\n", u, d, h); - if (i < 1) { - fclose(fp); - mg_free(temp_file); - return 0; - } - temp_file_offs += i; - } - } - fclose(fp); - } - - /* Create new file */ - fp = fopen(fname, "w"); - if (!fp) { - mg_free(temp_file); - return 0; - } - -#if !defined(_WIN32) - /* On Linux & co., restrict file read/write permissions to the owner */ - if (fchmod(fileno(fp), S_IRUSR | S_IWUSR) != 0) { - result = 0; - } -#endif - - if ((temp_file != NULL) && (temp_file_offs > 0)) { - /* Store buffered content of old file */ - if (fwrite(temp_file, 1, (size_t)temp_file_offs, fp) - != (size_t)temp_file_offs) { - result = 0; - } - } - - /* If new user, just add it */ - if ((ha1 != NULL) && (!found)) { - if (fprintf(fp, "%s:%s:%s\n", user, domain, ha1) < 6) { - result = 0; - } - } - - /* All data written */ - if (fclose(fp) != 0) { - result = 0; - } - - mg_free(temp_file); - return result; -} - - -CIVETWEB_API int -mg_modify_passwords_file(const char *fname, - const char *domain, - const char *user, - const char *pass) -{ - char ha1buf[33]; - if ((fname == NULL) || (domain == NULL) || (user == NULL)) { - return 0; - } - if ((pass == NULL) || (pass[0] == 0)) { - return mg_modify_passwords_file_ha1(fname, domain, user, NULL); - } - - mg_md5(ha1buf, user, ":", domain, ":", pass, NULL); - return mg_modify_passwords_file_ha1(fname, domain, user, ha1buf); -} - - -static int -is_valid_port(unsigned long port) -{ - return (port <= 0xffff); -} - - -static int -mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src) -{ - struct addrinfo hints, *res, *ressave; - int func_ret = 0; - int gai_ret; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = af; - if (!resolve_src) { - hints.ai_flags = AI_NUMERICHOST; - } - - gai_ret = getaddrinfo(src, NULL, &hints, &res); - if (gai_ret != 0) { - /* gai_strerror could be used to convert gai_ret to a string */ - /* POSIX return values: see - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html - */ - /* Windows return values: see - * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx - */ - return 0; - } - - ressave = res; - - while (res) { - if ((dstlen >= (size_t)res->ai_addrlen) - && (res->ai_addr->sa_family == af)) { - memcpy(dst, res->ai_addr, res->ai_addrlen); - func_ret = 1; - } - res = res->ai_next; - } - - freeaddrinfo(ressave); - return func_ret; -} - - -static int -connect_socket( - struct mg_context *ctx /* may be NULL */, - const char *host, - int port, /* 1..65535, or -99 for domain sockets (may be changed) */ - int use_ssl, /* 0 or 1 */ - struct mg_error_data *error, - SOCKET *sock /* output: socket, must not be NULL */, - union usa *sa /* output: socket address, must not be NULL */ -) -{ - int ip_ver = 0; - int conn_ret = -1; - int sockerr = 0; - *sock = INVALID_SOCKET; - memset(sa, 0, sizeof(*sa)); - - if (host == NULL) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_PARAM; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "%s", - "NULL host"); - } - return 0; - } - -#if defined(USE_X_DOM_SOCKET) - if (port == -99) { - /* Unix domain socket */ - size_t hostlen = strlen(host); - if (hostlen >= sizeof(sa->sun.sun_path)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_PARAM; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "%s", - "host length exceeds limit"); - } - return 0; - } - } else -#endif - if ((port <= 0) || !is_valid_port((unsigned)port)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_PARAM; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "%s", - "invalid port"); - } - return 0; - } - -#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) \ - && !defined(NO_SSL_DL) -#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) - if (use_ssl && (TLS_client_method == NULL)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "%s", - "SSL is not initialized"); - } - return 0; - } -#else - if (use_ssl && (SSLv23_client_method == NULL)) { - if (error != 0) { - error->code = MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "%s", - "SSL is not initialized"); - } - return 0; - } -#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0*/ -#else - (void)use_ssl; -#endif /* NO SSL */ - -#if defined(USE_X_DOM_SOCKET) - if (port == -99) { - size_t hostlen = strlen(host); - /* check (hostlen < sizeof(sun.sun_path)) already passed above */ - ip_ver = -99; - sa->sun.sun_family = AF_UNIX; - memset(sa->sun.sun_path, 0, sizeof(sa->sun.sun_path)); - memcpy(sa->sun.sun_path, host, hostlen); - } else -#endif - if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin), 1)) { - sa->sin.sin_port = htons((uint16_t)port); - ip_ver = 4; -#if defined(USE_IPV6) - } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6), 1)) { - sa->sin6.sin6_port = htons((uint16_t)port); - ip_ver = 6; - } else if (host[0] == '[') { - /* While getaddrinfo on Windows will work with [::1], - * getaddrinfo on Linux only works with ::1 (without []). */ - size_t l = strlen(host + 1); - char *h = (l > 1) ? mg_strdup_ctx(host + 1, ctx) : NULL; - if (h) { - h[l - 1] = 0; - if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6), 0)) { - sa->sin6.sin6_port = htons((uint16_t)port); - ip_ver = 6; - } - mg_free(h); - } -#endif - } - - if (ip_ver == 0) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_HOST_NOT_FOUND; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "%s", - "host not found"); - } - return 0; - } - - if (ip_ver == 4) { - *sock = socket(PF_INET, SOCK_STREAM, 0); - } -#if defined(USE_IPV6) - else if (ip_ver == 6) { - *sock = socket(PF_INET6, SOCK_STREAM, 0); - } -#endif -#if defined(USE_X_DOM_SOCKET) - else if (ip_ver == -99) { - *sock = socket(AF_UNIX, SOCK_STREAM, 0); - } -#endif - - if (*sock == INVALID_SOCKET) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OS_ERROR; - error->code_sub = (unsigned)ERRNO; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "socket(): %s", - strerror(ERRNO)); - } - return 0; - } - - if (0 != set_non_blocking_mode(*sock)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OS_ERROR; - error->code_sub = (unsigned)ERRNO; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "Cannot set socket to non-blocking: %s", - strerror(ERRNO)); - } - closesocket(*sock); - *sock = INVALID_SOCKET; - return 0; - } - - set_close_on_exec(*sock, NULL, ctx); - - if (ip_ver == 4) { - /* connected with IPv4 */ - conn_ret = connect(*sock, - (struct sockaddr *)((void *)&sa->sin), - sizeof(sa->sin)); - } -#if defined(USE_IPV6) - else if (ip_ver == 6) { - /* connected with IPv6 */ - conn_ret = connect(*sock, - (struct sockaddr *)((void *)&sa->sin6), - sizeof(sa->sin6)); - } -#endif -#if defined(USE_X_DOM_SOCKET) - else if (ip_ver == -99) { - /* connected to domain socket */ - conn_ret = connect(*sock, - (struct sockaddr *)((void *)&sa->sun), - sizeof(sa->sun)); - } -#endif - - if (conn_ret != 0) { - sockerr = ERRNO; - } - -#if defined(_WIN32) - if ((conn_ret != 0) && (sockerr == WSAEWOULDBLOCK)) { -#else - if ((conn_ret != 0) && (sockerr == EINPROGRESS)) { -#endif - /* Data for getsockopt */ - void *psockerr = &sockerr; - int ret; - -#if defined(_WIN32) - int len = (int)sizeof(sockerr); -#else - socklen_t len = (socklen_t)sizeof(sockerr); -#endif - - /* Data for poll */ - struct mg_pollfd pfd[2]; - int pollres; - int ms_wait = 10000; /* 10 second timeout */ - stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */ - unsigned int num_sock = 1; /* use one or two sockets */ - - /* For a non-blocking socket, the connect sequence is: - * 1) call connect (will not block) - * 2) wait until the socket is ready for writing (select or poll) - * 3) check connection state with getsockopt - */ - pfd[0].fd = *sock; - pfd[0].events = POLLOUT; - - if (ctx && (ctx->context_type == CONTEXT_SERVER)) { - pfd[num_sock].fd = ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - - pollres = - mg_poll(pfd, num_sock, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop); - - if (pollres != 1) { - /* Not connected */ - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_CONNECT_TIMEOUT; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "connect(%s:%d): timeout", - host, - port); - } - closesocket(*sock); - *sock = INVALID_SOCKET; - return 0; - } - -#if defined(_WIN32) - ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, (char *)psockerr, &len); -#else - ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, psockerr, &len); -#endif - - if ((ret == 0) && (sockerr == 0)) { - conn_ret = 0; - } - } - - if (conn_ret != 0) { - /* Not connected */ - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_CONNECT_FAILED; - error->code_sub = (unsigned)ERRNO; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "connect(%s:%d): error %s", - host, - port, - strerror(sockerr)); - } - closesocket(*sock); - *sock = INVALID_SOCKET; - return 0; - } - - return 1; -} - - -CIVETWEB_API int -mg_url_encode(const char *src, char *dst, size_t dst_len) -{ - static const char *dont_escape = "._-$,;~()"; - static const char *hex = "0123456789abcdef"; - char *pos = dst; - const char *end = dst + dst_len - 1; - - for (; ((*src != '\0') && (pos < end)); src++, pos++) { - if (isalnum((unsigned char)*src) - || (strchr(dont_escape, *src) != NULL)) { - *pos = *src; - } else if (pos + 2 < end) { - pos[0] = '%'; - pos[1] = hex[(unsigned char)*src >> 4]; - pos[2] = hex[(unsigned char)*src & 0xf]; - pos += 2; - } else { - break; - } - } - - *pos = '\0'; - return (*src == '\0') ? (int)(pos - dst) : -1; -} - -/* Return 0 on success, non-zero if an error occurs. */ - -static int -print_dir_entry(struct mg_connection *conn, struct de *de) -{ - size_t namesize, escsize, i; - char *href, *esc, *p; - char size[64], mod[64]; -#if defined(REENTRANT_TIME) - struct tm _tm; - struct tm *tm = &_tm; -#else - struct tm *tm; -#endif - - /* Estimate worst case size for encoding and escaping */ - namesize = strlen(de->file_name) + 1; - escsize = de->file_name[strcspn(de->file_name, "&<>")] ? namesize * 5 : 0; - href = (char *)mg_malloc(namesize * 3 + escsize); - if (href == NULL) { - return -1; - } - mg_url_encode(de->file_name, href, namesize * 3); - esc = NULL; - if (escsize > 0) { - /* HTML escaping needed */ - esc = href + namesize * 3; - for (i = 0, p = esc; de->file_name[i]; i++, p += strlen(p)) { - mg_strlcpy(p, de->file_name + i, 2); - if (*p == '&') { - strcpy(p, "&"); - } else if (*p == '<') { - strcpy(p, "<"); - } else if (*p == '>') { - strcpy(p, ">"); - } - } - } - - if (de->file.is_directory) { - mg_snprintf(conn, - NULL, /* Buffer is big enough */ - size, - sizeof(size), - "%s", - "[DIRECTORY]"); - } else { - /* We use (signed) cast below because MSVC 6 compiler cannot - * convert unsigned __int64 to double. Sigh. */ - if (de->file.size < 1024) { - mg_snprintf(conn, - NULL, /* Buffer is big enough */ - size, - sizeof(size), - "%d", - (int)de->file.size); - } else if (de->file.size < 0x100000) { - mg_snprintf(conn, - NULL, /* Buffer is big enough */ - size, - sizeof(size), - "%.1fk", - (double)de->file.size / 1024.0); - } else if (de->file.size < 0x40000000) { - mg_snprintf(conn, - NULL, /* Buffer is big enough */ - size, - sizeof(size), - "%.1fM", - (double)de->file.size / 1048576); - } else { - mg_snprintf(conn, - NULL, /* Buffer is big enough */ - size, - sizeof(size), - "%.1fG", - (double)de->file.size / 1073741824); - } - } - - /* Note: mg_snprintf will not cause a buffer overflow above. - * So, string truncation checks are not required here. */ - -#if defined(REENTRANT_TIME) - localtime_r(&de->file.last_modified, tm); -#else - tm = localtime(&de->file.last_modified); -#endif - if (tm != NULL) { - strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm); - } else { - mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod)); - } - mg_printf(conn, - "%s%s" - " %s  %s\n", - href, - de->file.is_directory ? "/" : "", - esc ? esc : de->file_name, - de->file.is_directory ? "/" : "", - mod, - size); - mg_free(href); - return 0; -} - - -/* This function is called from send_directory() and used for - * sorting directory entries by size, name, or modification time. */ -static int -compare_dir_entries(const void *p1, const void *p2, void *arg) -{ - const char *query_string = (const char *)(arg != NULL ? arg : ""); - if (p1 && p2) { - const struct de *a = (const struct de *)p1, *b = (const struct de *)p2; - int cmp_result = 0; - - if ((query_string == NULL) || (query_string[0] == '\0')) { - query_string = "n"; - } - - /* Sort Directories vs Files */ - if (a->file.is_directory && !b->file.is_directory) { - return -1; /* Always put directories on top */ - } else if (!a->file.is_directory && b->file.is_directory) { - return 1; /* Always put directories on top */ - } - - /* Sort by size or date */ - if (*query_string == 's') { - cmp_result = (a->file.size == b->file.size) - ? 0 - : ((a->file.size > b->file.size) ? 1 : -1); - } else if (*query_string == 'd') { - cmp_result = - (a->file.last_modified == b->file.last_modified) - ? 0 - : ((a->file.last_modified > b->file.last_modified) ? 1 - : -1); - } - - /* Sort by name: - * if (*query_string == 'n') ... - * but also sort files of same size/date by name as secondary criterion. - */ - if (cmp_result == 0) { - cmp_result = strcmp(a->file_name, b->file_name); - } - - /* For descending order, invert result */ - return (query_string[1] == 'd') ? -cmp_result : cmp_result; - } - return 0; -} - - -static int -must_hide_file(struct mg_connection *conn, const char *path) -{ - if (conn && conn->dom_ctx) { - const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; - const char *pattern = conn->dom_ctx->config[HIDE_FILES]; - return (match_prefix_strlen(pw_pattern, path) > 0) - || (match_prefix_strlen(pattern, path) > 0); - } - return 0; -} - - -#if !defined(NO_FILESYSTEMS) -static int -scan_directory(struct mg_connection *conn, - const char *dir, - void *data, - int (*cb)(struct de *, void *)) -{ - char path[UTF8_PATH_MAX]; - struct dirent *dp; - DIR *dirp; - struct de de; - int truncated; - - if ((dirp = mg_opendir(conn, dir)) == NULL) { - return 0; - } else { - - while ((dp = mg_readdir(dirp)) != NULL) { - /* Do not show current dir and hidden files */ - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") - || must_hide_file(conn, dp->d_name)) { - continue; - } - - mg_snprintf( - conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name); - - /* If we don't memset stat structure to zero, mtime will have - * garbage and strftime() will segfault later on in - * print_dir_entry(). memset is required only if mg_stat() - * fails. For more details, see - * http://code.google.com/p/mongoose/issues/detail?id=79 */ - memset(&de.file, 0, sizeof(de.file)); - - if (truncated) { - /* If the path is not complete, skip processing. */ - continue; - } - - if (!mg_stat(conn, path, &de.file)) { - mg_cry_internal(conn, - "%s: mg_stat(%s) failed: %s", - __func__, - path, - strerror(ERRNO)); - } - de.file_name = dp->d_name; - if (cb(&de, data)) { - /* stopped */ - break; - } - } - (void)mg_closedir(dirp); - } - return 1; -} -#endif /* NO_FILESYSTEMS */ - - -#if !defined(NO_FILES) -static int -remove_directory(struct mg_connection *conn, const char *dir) -{ - char path[UTF8_PATH_MAX]; - struct dirent *dp; - DIR *dirp; - struct de de; - int truncated; - int ok = 1; - - if ((dirp = mg_opendir(conn, dir)) == NULL) { - return 0; - } else { - - while ((dp = mg_readdir(dirp)) != NULL) { - /* Do not show current dir (but show hidden files as they will - * also be removed) */ - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { - continue; - } - - mg_snprintf( - conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name); - - /* If we don't memset stat structure to zero, mtime will have - * garbage and strftime() will segfault later on in - * print_dir_entry(). memset is required only if mg_stat() - * fails. For more details, see - * http://code.google.com/p/mongoose/issues/detail?id=79 */ - memset(&de.file, 0, sizeof(de.file)); - - if (truncated) { - /* Do not delete anything shorter */ - ok = 0; - continue; - } - - if (!mg_stat(conn, path, &de.file)) { - mg_cry_internal(conn, - "%s: mg_stat(%s) failed: %s", - __func__, - path, - strerror(ERRNO)); - ok = 0; - } - - if (de.file.is_directory) { - if (remove_directory(conn, path) == 0) { - ok = 0; - } - } else { - /* This will fail file is the file is in memory */ - if (mg_remove(conn, path) == 0) { - ok = 0; - } - } - } - (void)mg_closedir(dirp); - - IGNORE_UNUSED_RESULT(rmdir(dir)); - } - - return ok; -} -#endif - - -struct dir_scan_data { - struct de *entries; - size_t num_entries; - size_t arr_size; -}; - - -#if !defined(NO_FILESYSTEMS) -static int -dir_scan_callback(struct de *de, void *data) -{ - struct dir_scan_data *dsd = (struct dir_scan_data *)data; - struct de *entries = dsd->entries; - - if ((entries == NULL) || (dsd->num_entries >= dsd->arr_size)) { - /* Here "entries" is a temporary pointer and can be replaced, - * "dsd->entries" is the original pointer */ - entries = - (struct de *)mg_realloc(entries, - dsd->arr_size * 2 * sizeof(entries[0])); - if (entries == NULL) { - /* stop scan */ - return 1; - } - dsd->entries = entries; - dsd->arr_size *= 2; - } - entries[dsd->num_entries].file_name = mg_strdup(de->file_name); - if (entries[dsd->num_entries].file_name == NULL) { - /* stop scan */ - return 1; - } - entries[dsd->num_entries].file = de->file; - dsd->num_entries++; - - return 0; -} - - -static void -handle_directory_request(struct mg_connection *conn, const char *dir) -{ - size_t i; - int sort_direction; - struct dir_scan_data data = {NULL, 0, 128}; - char date[64], *esc, *p; - const char *title; - time_t curtime = time(NULL); - - if (!conn) { - return; - } - - if (!scan_directory(conn, dir, &data, dir_scan_callback)) { - mg_send_http_error(conn, - 500, - "Error: Cannot open directory\nopendir(%s): %s", - dir, - strerror(ERRNO)); - return; - } - - gmt_time_string(date, sizeof(date), &curtime); - - esc = NULL; - title = conn->request_info.local_uri; - if (title[strcspn(title, "&<>")]) { - /* HTML escaping needed */ - esc = (char *)mg_malloc(strlen(title) * 5 + 1); - if (esc) { - for (i = 0, p = esc; title[i]; i++, p += strlen(p)) { - mg_strlcpy(p, title + i, 2); - if (*p == '&') { - strcpy(p, "&"); - } else if (*p == '<') { - strcpy(p, "<"); - } else if (*p == '>') { - strcpy(p, ">"); - } - } - } else { - title = ""; - } - } - - sort_direction = ((conn->request_info.query_string != NULL) - && (conn->request_info.query_string[0] != '\0') - && (conn->request_info.query_string[1] == 'd')) - ? 'a' - : 'd'; - - conn->must_close = 1; - - /* Create 200 OK response */ - mg_response_header_start(conn, 200); - send_static_cache_header(conn); - send_additional_header(conn); - mg_response_header_add(conn, - "Content-Type", - "text/html; charset=utf-8", - -1); - - /* Send all headers */ - mg_response_header_send(conn); - - /* Body */ - mg_printf(conn, - "" - "Index of %s" - "" - "

Index of %s

"
-	          ""
-	          ""
-	          ""
-	          "",
-	          esc ? esc : title,
-	          esc ? esc : title,
-	          sort_direction,
-	          sort_direction,
-	          sort_direction);
-	mg_free(esc);
-
-	/* Print first entry - link to a parent directory */
-	mg_printf(conn,
-	          ""
-	          "\n",
-	          "..",
-	          "Parent directory",
-	          "-",
-	          "-");
-
-	/* Sort and print directory entries */
-	if (data.entries != NULL) {
-		mg_sort(data.entries,
-		        data.num_entries,
-		        sizeof(data.entries[0]),
-		        compare_dir_entries,
-		        (void *)conn->request_info.query_string);
-		for (i = 0; i < data.num_entries; i++) {
-			print_dir_entry(conn, &data.entries[i]);
-			mg_free(data.entries[i].file_name);
-		}
-		mg_free(data.entries);
-	}
-
-	mg_printf(conn, "%s", "
NameModifiedSize

%s %s  %s
"); - conn->status_code = 200; -} -#endif /* NO_FILESYSTEMS */ - - -/* Send len bytes from the opened file to the client. */ -static void -send_file_data(struct mg_connection *conn, - struct mg_file *filep, - int64_t offset, - int64_t len, - int no_buffering) -{ - char buf[MG_BUF_LEN]; - int to_read, num_read, num_written; - int64_t size; - - if (!filep || !conn) { - return; - } - - /* Sanity check the offset */ - size = (filep->stat.size > INT64_MAX) ? INT64_MAX - : (int64_t)(filep->stat.size); - offset = (offset < 0) ? 0 : ((offset > size) ? size : offset); - - if (len > 0 && filep->access.fp != NULL) { - /* file stored on disk */ -#if defined(__linux__) - /* sendfile is only available for Linux */ - if ((conn->ssl == 0) && (conn->throttle == 0) - && (!mg_strcasecmp(conn->dom_ctx->config[ALLOW_SENDFILE_CALL], - "yes"))) { - off_t sf_offs = (off_t)offset; - ssize_t sf_sent; - int sf_file = fileno(filep->access.fp); - int loop_cnt = 0; - - do { - /* 2147479552 (0x7FFFF000) is a limit found by experiment on - * 64 bit Linux (2^31 minus one memory page of 4k?). */ - size_t sf_tosend = - (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000); - sf_sent = - sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend); - if (sf_sent > 0) { - len -= sf_sent; - offset += sf_sent; - } else if (loop_cnt == 0) { - /* This file can not be sent using sendfile. - * This might be the case for pseudo-files in the - * /sys/ and /proc/ file system. - * Use the regular user mode copy code instead. */ - break; - } else if (sf_sent == 0) { - /* No error, but 0 bytes sent. May be EOF? */ - return; - } - loop_cnt++; - - } while ((len > 0) && (sf_sent >= 0)); - - if (sf_sent > 0) { - return; /* OK */ - } - - /* sf_sent<0 means error, thus fall back to the classic way */ - /* This is always the case, if sf_file is not a "normal" file, - * e.g., for sending data from the output of a CGI process. */ - offset = (int64_t)sf_offs; - } -#endif - if ((offset > 0) && (fseeko(filep->access.fp, offset, SEEK_SET) != 0)) { - mg_cry_internal(conn, - "%s: fseeko() failed: %s", - __func__, - strerror(ERRNO)); - mg_send_http_error( - conn, - 500, - "%s", - "Error: Unable to access file at requested position."); - } else { - while (len > 0) { - /* Calculate how much to read from the file into the buffer. */ - /* If no_buffering is set, we should not wait until the - * CGI->Server buffer is filled, but send everything - * immediately. In theory buffering could be turned off using - * setbuf(filep->access.fp, NULL); - * setvbuf(filep->access.fp, NULL, _IONBF, 0); - * but in practice this does not work. A "Linux only" solution - * may be to use select(). The only portable way is to read byte - * by byte, but this is quite inefficient from a performance - * point of view. */ - to_read = no_buffering ? 1 : sizeof(buf); - if ((int64_t)to_read > len) { - to_read = (int)len; - } - - /* Read from file, exit the loop on error */ - if ((num_read = pull_inner(filep->access.fp, - NULL, - buf, - to_read, - /* unused */ 0.0)) - <= 0) { - break; - } - - /* Send read bytes to the client, exit the loop on error */ - if ((num_written = mg_write(conn, buf, (size_t)num_read)) - != num_read) { - break; - } - - /* Both read and were successful, adjust counters */ - len -= num_written; - } - } - } -} - - -static int -parse_range_header(const char *header, int64_t *a, int64_t *b) -{ - return sscanf(header, - "bytes=%" INT64_FMT "-%" INT64_FMT, - a, - b); // NOLINT(cert-err34-c) 'sscanf' used to convert a string - // to an integer value, but function will not report - // conversion errors; consider using 'strtol' instead -} - - -static void -construct_etag(char *buf, size_t buf_len, const struct mg_file_stat *filestat) -{ - if ((filestat != NULL) && (buf != NULL)) { - mg_snprintf(NULL, - NULL, /* All calls to construct_etag use 64 byte buffer */ - buf, - buf_len, - "\"%lx.%" INT64_FMT "\"", - (unsigned long)filestat->last_modified, - filestat->size); - } -} - - -static void -fclose_on_exec(struct mg_file_access *filep, struct mg_connection *conn) -{ - if (filep != NULL && filep->fp != NULL) { -#if defined(_WIN32) - (void)conn; /* Unused. */ -#else - if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) { - mg_cry_internal(conn, - "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", - __func__, - strerror(ERRNO)); - } -#endif - } -} - - -#if defined(USE_ZLIB) -#include "mod_zlib.inl" -#endif - - -#if !defined(NO_FILESYSTEMS) -static void -handle_static_file_request(struct mg_connection *conn, - const char *path, - struct mg_file *filep, - const char *mime_type, - const char *additional_headers) -{ - char lm[64], etag[64]; - char range[128]; /* large enough, so there will be no overflow */ - const char *range_hdr; - int64_t cl, r1, r2; - struct vec mime_vec; - int n, truncated; - char gz_path[UTF8_PATH_MAX]; - const char *encoding = 0; - int is_head_request; - -#if defined(USE_ZLIB) - /* Compression is allowed, unless there is a reason not to use - * compression. If the file is already compressed, too small or a - * "range" request was made, on the fly compression is not possible. */ - int allow_on_the_fly_compression = 1; -#endif - - if ((conn == NULL) || (conn->dom_ctx == NULL) || (filep == NULL)) { - return; - } - - is_head_request = !strcmp(conn->request_info.request_method, "HEAD"); - - if (mime_type == NULL) { - get_mime_type(conn, path, &mime_vec); - } else { - mime_vec.ptr = mime_type; - mime_vec.len = strlen(mime_type); - } - if (filep->stat.size > INT64_MAX) { - mg_send_http_error(conn, - 500, - "Error: File size is too large to send\n%" INT64_FMT, - filep->stat.size); - return; - } - cl = (int64_t)filep->stat.size; - conn->status_code = 200; - range[0] = '\0'; - -#if defined(USE_ZLIB) - /* if this file is in fact a pre-gzipped file, rewrite its filename - * it's important to rewrite the filename after resolving - * the mime type from it, to preserve the actual file's type */ - if (!conn->accept_gzip) { - allow_on_the_fly_compression = 0; - } -#endif - - /* Check if there is a range header */ - range_hdr = mg_get_header(conn, "Range"); - - /* For gzipped files, add *.gz */ - if (filep->stat.is_gzipped) { - mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path); - - if (truncated) { - mg_send_http_error(conn, - 500, - "Error: Path of zipped file too long (%s)", - path); - return; - } - - path = gz_path; - encoding = "gzip"; - -#if defined(USE_ZLIB) - /* File is already compressed. No "on the fly" compression. */ - allow_on_the_fly_compression = 0; -#endif - } else if ((conn->accept_gzip) && (range_hdr == NULL) - && (filep->stat.size >= MG_FILE_COMPRESSION_SIZE_LIMIT)) { - struct mg_file_stat file_stat; - - mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path); - - if (!truncated && mg_stat(conn, gz_path, &file_stat) - && !file_stat.is_directory) { - file_stat.is_gzipped = 1; - filep->stat = file_stat; - cl = (int64_t)filep->stat.size; - path = gz_path; - encoding = "gzip"; - -#if defined(USE_ZLIB) - /* File is already compressed. No "on the fly" compression. */ - allow_on_the_fly_compression = 0; -#endif - } - } - - if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) { - mg_send_http_error(conn, - 500, - "Error: Cannot open file\nfopen(%s): %s", - path, - strerror(ERRNO)); - return; - } - - fclose_on_exec(&filep->access, conn); - - /* If "Range" request was made: parse header, send only selected part - * of the file. */ - r1 = r2 = 0; - if ((range_hdr != NULL) - && ((n = parse_range_header(range_hdr, &r1, &r2)) > 0) && (r1 >= 0) - && (r2 >= 0)) { - /* actually, range requests don't play well with a pre-gzipped - * file (since the range is specified in the uncompressed space) */ - if (filep->stat.is_gzipped) { - mg_send_http_error( - conn, - 416, /* 416 = Range Not Satisfiable */ - "%s", - "Error: Range requests in gzipped files are not supported"); - (void)mg_fclose( - &filep->access); /* ignore error on read only file */ - return; - } - conn->status_code = 206; - cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1); - mg_snprintf(conn, - NULL, /* range buffer is big enough */ - range, - sizeof(range), - "bytes " - "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT, - r1, - r1 + cl - 1, - filep->stat.size); - -#if defined(USE_ZLIB) - /* Do not compress ranges. */ - allow_on_the_fly_compression = 0; -#endif - } - - /* Do not compress small files. Small files do not benefit from file - * compression, but there is still some overhead. */ -#if defined(USE_ZLIB) - if (filep->stat.size < MG_FILE_COMPRESSION_SIZE_LIMIT) { - /* File is below the size limit. */ - allow_on_the_fly_compression = 0; - } -#endif - - /* Prepare Etag, and Last-Modified headers. */ - gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified); - construct_etag(etag, sizeof(etag), &filep->stat); - - /* Create 2xx (200, 206) response */ - mg_response_header_start(conn, conn->status_code); - send_static_cache_header(conn); - send_additional_header(conn); - send_cors_header(conn); - mg_response_header_add(conn, - "Content-Type", - mime_vec.ptr, - (int)mime_vec.len); - mg_response_header_add(conn, "Last-Modified", lm, -1); - mg_response_header_add(conn, "Etag", etag, -1); - -#if defined(USE_ZLIB) - /* On the fly compression allowed */ - if (allow_on_the_fly_compression) { - /* For on the fly compression, we don't know the content size in - * advance, so we have to use chunked encoding */ - encoding = "gzip"; - if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) { - /* HTTP/2 is always using "chunks" (frames) */ - mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1); - } - - } else -#endif - { - /* Without on-the-fly compression, we know the content-length - * and we can use ranges (with on-the-fly compression we cannot). - * So we send these response headers only in this case. */ - char len[32]; - int trunc = 0; - mg_snprintf(conn, &trunc, len, sizeof(len), "%" INT64_FMT, cl); - - if (!trunc) { - mg_response_header_add(conn, "Content-Length", len, -1); - } - - mg_response_header_add(conn, "Accept-Ranges", "bytes", -1); - } - - if (encoding) { - mg_response_header_add(conn, "Content-Encoding", encoding, -1); - } - if (range[0] != 0) { - mg_response_header_add(conn, "Content-Range", range, -1); - } - - /* The code above does not add any header starting with X- to make - * sure no one of the additional_headers is included twice */ - if ((additional_headers != NULL) && (*additional_headers != 0)) { - mg_response_header_add_lines(conn, additional_headers); - } - - /* Send all headers */ - mg_response_header_send(conn); - - if (!is_head_request) { -#if defined(USE_ZLIB) - if (allow_on_the_fly_compression) { - /* Compress and send */ - send_compressed_data(conn, filep); - } else -#endif - { - /* Send file directly */ - send_file_data(conn, filep, r1, cl, 0); /* send static file */ - } - } - (void)mg_fclose(&filep->access); /* ignore error on read only file */ -} - - -CIVETWEB_API int -mg_send_file_body(struct mg_connection *conn, const char *path) -{ - struct mg_file file = STRUCT_FILE_INITIALIZER; - if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) { - return -1; - } - fclose_on_exec(&file.access, conn); - send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */ - (void)mg_fclose(&file.access); /* Ignore errors for readonly files */ - return 0; /* >= 0 for OK */ -} -#endif /* NO_FILESYSTEMS */ - - -#if !defined(NO_CACHING) -/* Return True if we should reply 304 Not Modified. */ -static int -is_not_modified(const struct mg_connection *conn, - const struct mg_file_stat *filestat) -{ - char etag[64]; - const char *ims = mg_get_header(conn, "If-Modified-Since"); - const char *inm = mg_get_header(conn, "If-None-Match"); - construct_etag(etag, sizeof(etag), filestat); - - return ((inm != NULL) && !mg_strcasecmp(etag, inm)) - || ((ims != NULL) - && (filestat->last_modified <= parse_date_string(ims))); -} - - -static void -handle_not_modified_static_file_request(struct mg_connection *conn, - struct mg_file *filep) -{ - char lm[64], etag[64]; - - if ((conn == NULL) || (filep == NULL)) { - return; - } - - gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified); - construct_etag(etag, sizeof(etag), &filep->stat); - - /* Create 304 "not modified" response */ - mg_response_header_start(conn, 304); - send_static_cache_header(conn); - send_additional_header(conn); - mg_response_header_add(conn, "Last-Modified", lm, -1); - mg_response_header_add(conn, "Etag", etag, -1); - - /* Send all headers */ - mg_response_header_send(conn); -} -#endif - - -#if !defined(NO_FILESYSTEMS) -CIVETWEB_API void -mg_send_file(struct mg_connection *conn, const char *path) -{ - mg_send_mime_file2(conn, path, NULL, NULL); -} - - -CIVETWEB_API void -mg_send_mime_file(struct mg_connection *conn, - const char *path, - const char *mime_type) -{ - mg_send_mime_file2(conn, path, mime_type, NULL); -} - - -CIVETWEB_API void -mg_send_mime_file2(struct mg_connection *conn, - const char *path, - const char *mime_type, - const char *additional_headers) -{ - struct mg_file file = STRUCT_FILE_INITIALIZER; - - if (!conn) { - /* No conn */ - return; - } - - if (mg_stat(conn, path, &file.stat)) { -#if !defined(NO_CACHING) - if (is_not_modified(conn, &file.stat)) { - /* Send 304 "Not Modified" - this must not send any body data */ - handle_not_modified_static_file_request(conn, &file); - } else -#endif /* NO_CACHING */ - if (file.stat.is_directory) { - if (!mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING], - "yes")) { - handle_directory_request(conn, path); - } else { - mg_send_http_error(conn, - 403, - "%s", - "Error: Directory listing denied"); - } - } else { - handle_static_file_request( - conn, path, &file, mime_type, additional_headers); - } - } else { - mg_send_http_error(conn, 404, "%s", "Error: File not found"); - } -} - - -/* For a given PUT path, create all intermediate subdirectories. - * Return 0 if the path itself is a directory. - * Return 1 if the path leads to a file. - * Return -1 for if the path is too long. - * Return -2 if path can not be created. - */ -static int -put_dir(struct mg_connection *conn, const char *path) -{ - char buf[UTF8_PATH_MAX]; - const char *s, *p; - struct mg_file file = STRUCT_FILE_INITIALIZER; - size_t len; - int res = 1; - - for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { - len = (size_t)(p - path); - if (len >= sizeof(buf)) { - /* path too long */ - res = -1; - break; - } - memcpy(buf, path, len); - buf[len] = '\0'; - - /* Try to create intermediate directory */ - DEBUG_TRACE("mkdir(%s)", buf); - if (!mg_stat(conn, buf, &file.stat) && mg_mkdir(conn, buf, 0755) != 0) { - /* path does not exist and can not be created */ - res = -2; - break; - } - - /* Is path itself a directory? */ - if (p[1] == '\0') { - res = 0; - } - } - - return res; -} - - -static void -remove_bad_file(const struct mg_connection *conn, const char *path) -{ - int r = mg_remove(conn, path); - if (r != 0) { - mg_cry_internal(conn, - "%s: Cannot remove invalid file %s", - __func__, - path); - } -} - - -CIVETWEB_API long long -mg_store_body(struct mg_connection *conn, const char *path) -{ - char buf[MG_BUF_LEN]; - long long len = 0; - int ret, n; - struct mg_file fi; - - if (conn->consumed_content != 0) { - mg_cry_internal(conn, "%s: Contents already consumed", __func__); - return -11; - } - - ret = put_dir(conn, path); - if (ret < 0) { - /* -1 for path too long, - * -2 for path can not be created. */ - return ret; - } - if (ret != 1) { - /* Return 0 means, path itself is a directory. */ - return 0; - } - - if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fi) == 0) { - return -12; - } - - ret = mg_read(conn, buf, sizeof(buf)); - while (ret > 0) { - n = (int)fwrite(buf, 1, (size_t)ret, fi.access.fp); - if (n != ret) { - (void)mg_fclose( - &fi.access); /* File is bad and will be removed anyway. */ - remove_bad_file(conn, path); - return -13; - } - len += ret; - ret = mg_read(conn, buf, sizeof(buf)); - } - - /* File is open for writing. If fclose fails, there was probably an - * error flushing the buffer to disk, so the file on disk might be - * broken. Delete it and return an error to the caller. */ - if (mg_fclose(&fi.access) != 0) { - remove_bad_file(conn, path); - return -14; - } - - return len; -} -#endif /* NO_FILESYSTEMS */ - - -/* Parse a buffer: - * Forward the string pointer till the end of a word, then - * terminate it and forward till the begin of the next word. - */ -static int -skip_to_end_of_word_and_terminate(char **ppw, int eol) -{ - /* Forward until a space is found - use isgraph here */ - /* Extended ASCII characters are also treated as word characters. */ - /* See http://www.cplusplus.com/reference/cctype/ */ - while ((unsigned char)**ppw > 127 || isgraph((unsigned char)**ppw)) { - (*ppw)++; - } - - /* Check end of word */ - if (eol) { - /* must be a end of line */ - if ((**ppw != '\r') && (**ppw != '\n')) { - return -1; - } - } else { - /* must be a end of a word, but not a line */ - if (**ppw != ' ') { - return -1; - } - } - - /* Terminate and forward to the next word */ - do { - **ppw = 0; - (*ppw)++; - } while (isspace((unsigned char)**ppw)); - - /* Check after term */ - if (!eol) { - /* if it's not the end of line, there must be a next word */ - if (!isgraph((unsigned char)**ppw)) { - return -1; - } - } - - /* ok */ - return 1; -} - - -/* Parse HTTP headers from the given buffer, advance buf pointer - * to the point where parsing stopped. - * All parameters must be valid pointers (not NULL). - * Return <0 on error. */ -static int -parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]) -{ - int i; - int num_headers = 0; - - for (i = 0; i < (int)MG_MAX_HEADERS; i++) { - char *dp = *buf; - - /* Skip all ASCII characters (>SPACE, <127), to find a ':' */ - while ((*dp != ':') && (*dp >= 33) && (*dp <= 126)) { - dp++; - } - if (dp == *buf) { - /* End of headers reached. */ - break; - } - - /* Drop all spaces after header name before : */ - while (*dp == ' ') { - *dp = 0; - dp++; - } - if (*dp != ':') { - /* This is not a valid field. */ - return -1; - } - - /* End of header key (*dp == ':') */ - /* Truncate here and set the key name */ - *dp = 0; - hdr[i].name = *buf; - - /* Skip all spaces */ - do { - dp++; - } while ((*dp == ' ') || (*dp == '\t')); - - /* The rest of the line is the value */ - hdr[i].value = dp; - - /* Find end of line */ - while ((*dp != 0) && (*dp != '\r') && (*dp != '\n')) { - dp++; - }; - - /* eliminate \r */ - if (*dp == '\r') { - *dp = 0; - dp++; - if (*dp != '\n') { - /* This is not a valid line. */ - return -1; - } - } - - /* here *dp is either 0 or '\n' */ - /* in any case, we have found a complete header */ - num_headers = i + 1; - - if (*dp) { - *dp = 0; - dp++; - *buf = dp; - - if ((dp[0] == '\r') || (dp[0] == '\n')) { - /* We've had CRLF twice in a row - * This is the end of the headers */ - break; - } - /* continue within the loop, find the next header */ - } else { - *buf = dp; - break; - } - } - return num_headers; -} - - -struct mg_http_method_info { - const char *name; - int request_has_body; - int response_has_body; - int is_safe; - int is_idempotent; - int is_cacheable; -}; - - -/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods */ -static const struct mg_http_method_info http_methods[] = { - /* HTTP (RFC 2616) */ - {"GET", 0, 1, 1, 1, 1}, - {"POST", 1, 1, 0, 0, 0}, - {"PUT", 1, 0, 0, 1, 0}, - {"DELETE", 0, 0, 0, 1, 0}, - {"HEAD", 0, 0, 1, 1, 1}, - {"OPTIONS", 0, 0, 1, 1, 0}, - {"CONNECT", 1, 1, 0, 0, 0}, - /* TRACE method (RFC 2616) is not supported for security reasons */ - - /* PATCH method (RFC 5789) */ - {"PATCH", 1, 0, 0, 0, 0}, - /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */ - - /* WEBDAV (RFC 2518) */ - {"PROPFIND", 0, 1, 1, 1, 0}, - /* http://www.webdav.org/specs/rfc4918.html, 9.1: - * Some PROPFIND results MAY be cached, with care, - * as there is no cache validation mechanism for - * most properties. This method is both safe and - * idempotent (see Section 9.1 of [RFC2616]). */ - {"MKCOL", 0, 0, 0, 1, 0}, - /* http://www.webdav.org/specs/rfc4918.html, 9.1: - * When MKCOL is invoked without a request body, - * the newly created collection SHOULD have no - * members. A MKCOL request message may contain - * a message body. The precise behavior of a MKCOL - * request when the body is present is undefined, - * ... ==> We do not support MKCOL with body data. - * This method is idempotent, but not safe (see - * Section 9.1 of [RFC2616]). Responses to this - * method MUST NOT be cached. */ - - /* Methods for write access to files on WEBDAV (RFC 2518) */ - {"LOCK", 1, 1, 0, 0, 0}, - {"UNLOCK", 1, 0, 0, 0, 0}, - {"PROPPATCH", 1, 1, 0, 0, 0}, - {"COPY", 1, 0, 0, 0, 0}, - {"MOVE", 1, 1, 0, 0, 0}, - - /* Unsupported WEBDAV Methods: */ - /* + 11 methods from RFC 3253 */ - /* ORDERPATCH (RFC 3648) */ - /* ACL (RFC 3744) */ - /* SEARCH (RFC 5323) */ - /* + MicroSoft extensions - * https://msdn.microsoft.com/en-us/library/aa142917.aspx */ - - /* REPORT method (RFC 3253) */ - {"REPORT", 1, 1, 1, 1, 1}, - /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */ - /* It was defined for WEBDAV in RFC 3253, Sec. 3.6 - * (https://tools.ietf.org/html/rfc3253#section-3.6), but seems - * to be useful for REST in case a "GET request with body" is - * required. */ - - {NULL, 0, 0, 0, 0, 0} - /* end of list */ -}; - - -/* All method names */ -static char *all_methods = NULL; /* Built by mg_init_library */ - - -static const struct mg_http_method_info * -get_http_method_info(const char *method) -{ - /* Check if the method is known to the server. The list of all known - * HTTP methods can be found here at - * http://www.iana.org/assignments/http-methods/http-methods.xhtml - */ - const struct mg_http_method_info *m = http_methods; - - while (m->name) { - if (!strcmp(m->name, method)) { - return m; - } - m++; - } - return NULL; -} - - -static int -is_valid_http_method(const char *method) -{ - return (get_http_method_info(method) != NULL); -} - - -/* Parse HTTP request, fill in mg_request_info structure. - * This function modifies the buffer by NUL-terminating - * HTTP request components, header names and header values. - * Parameters: - * buf (in/out): pointer to the HTTP header to parse and split - * len (in): length of HTTP header buffer - * re (out): parsed header as mg_request_info - * buf and ri must be valid pointers (not NULL), len>0. - * Returns <0 on error. */ -static int -parse_http_request(char *buf, int len, struct mg_request_info *ri) -{ - int request_length; - int init_skip = 0; - - /* Reset attributes. DO NOT TOUCH is_ssl, remote_addr, - * remote_port */ - ri->remote_user = ri->request_method = ri->request_uri = ri->http_version = - NULL; - ri->num_headers = 0; - - /* RFC says that all initial whitespaces should be ignored */ - /* This included all leading \r and \n (isspace) */ - /* See table: http://www.cplusplus.com/reference/cctype/ */ - while ((len > 0) && isspace((unsigned char)*buf)) { - buf++; - len--; - init_skip++; - } - - if (len == 0) { - /* Incomplete request */ - return 0; - } - - /* Control characters are not allowed, including zero */ - if (iscntrl((unsigned char)*buf)) { - return -1; - } - - /* Find end of HTTP header */ - request_length = get_http_header_len(buf, len); - if (request_length <= 0) { - return request_length; - } - buf[request_length - 1] = '\0'; - - if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) { - return -1; - } - - /* The first word has to be the HTTP method */ - ri->request_method = buf; - - if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) { - return -1; - } - - /* The second word is the URI */ - ri->request_uri = buf; - - if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) { - return -1; - } - - /* Next would be the HTTP version */ - ri->http_version = buf; - - if (skip_to_end_of_word_and_terminate(&buf, 1) <= 0) { - return -1; - } - - /* Check for a valid HTTP version key */ - if (strncmp(ri->http_version, "HTTP/", 5) != 0) { - /* Invalid request */ - return -1; - } - ri->http_version += 5; - - /* Check for a valid http method */ - if (!is_valid_http_method(ri->request_method)) { - return -1; - } - - /* Parse all HTTP headers */ - ri->num_headers = parse_http_headers(&buf, ri->http_headers); - if (ri->num_headers < 0) { - /* Error while parsing headers */ - return -1; - } - - return request_length + init_skip; -} - - -static int -parse_http_response(char *buf, int len, struct mg_response_info *ri) -{ - int response_length; - int init_skip = 0; - char *tmp, *tmp2; - long l; - - /* Initialize elements. */ - ri->http_version = ri->status_text = NULL; - ri->num_headers = ri->status_code = 0; - - /* RFC says that all initial whitespaces should be ignored */ - /* This included all leading \r and \n (isspace) */ - /* See table: http://www.cplusplus.com/reference/cctype/ */ - while ((len > 0) && isspace((unsigned char)*buf)) { - buf++; - len--; - init_skip++; - } - - if (len == 0) { - /* Incomplete request */ - return 0; - } - - /* Control characters are not allowed, including zero */ - if (iscntrl((unsigned char)*buf)) { - return -1; - } - - /* Find end of HTTP header */ - response_length = get_http_header_len(buf, len); - if (response_length <= 0) { - return response_length; - } - buf[response_length - 1] = '\0'; - - if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) { - return -1; - } - - /* The first word is the HTTP version */ - /* Check for a valid HTTP version key */ - if (strncmp(buf, "HTTP/", 5) != 0) { - /* Invalid request */ - return -1; - } - buf += 5; - if (!isgraph((unsigned char)buf[0])) { - /* Invalid request */ - return -1; - } - ri->http_version = buf; - - if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) { - return -1; - } - - /* The second word is the status as a number */ - tmp = buf; - - if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) { - return -1; - } - - l = strtol(tmp, &tmp2, 10); - if ((l < 100) || (l >= 1000) || ((tmp2 - tmp) != 3) || (*tmp2 != 0)) { - /* Everything else but a 3 digit code is invalid */ - return -1; - } - ri->status_code = (int)l; - - /* The rest of the line is the status text */ - ri->status_text = buf; - - /* Find end of status text */ - /* isgraph or isspace = isprint */ - while (isprint((unsigned char)*buf)) { - buf++; - } - if ((*buf != '\r') && (*buf != '\n')) { - return -1; - } - /* Terminate string and forward buf to next line */ - do { - *buf = 0; - buf++; - } while (isspace((unsigned char)*buf)); - - /* Parse all HTTP headers */ - ri->num_headers = parse_http_headers(&buf, ri->http_headers); - if (ri->num_headers < 0) { - /* Error while parsing headers */ - return -1; - } - - return response_length + init_skip; -} - - -/* Keep reading the input (either opened file descriptor fd, or socket sock, - * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the - * buffer (which marks the end of HTTP request). Buffer buf may already - * have some data. The length of the data is stored in nread. - * Upon every read operation, increase nread by the number of bytes read. */ -static int -read_message(FILE *fp, - struct mg_connection *conn, - char *buf, - int bufsiz, - int *nread) -{ - int request_len, n = 0; - struct timespec last_action_time; - double request_timeout; - - if (!conn) { - return 0; - } - - memset(&last_action_time, 0, sizeof(last_action_time)); - - if (conn->dom_ctx->config[REQUEST_TIMEOUT]) { - /* value of request_timeout is in seconds, config in milliseconds */ - request_timeout = - strtod(conn->dom_ctx->config[REQUEST_TIMEOUT], NULL) / 1000.0; - } else { - request_timeout = - strtod(config_options[REQUEST_TIMEOUT].default_value, NULL) - / 1000.0; - } - if (conn->handled_requests > 0) { - if (conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) { - request_timeout = - strtod(conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT], NULL) - / 1000.0; - } - } - - request_len = get_http_header_len(buf, *nread); - - while (request_len == 0) { - /* Full request not yet received */ - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - /* Server is to be stopped. */ - return -1; - } - - if (*nread >= bufsiz) { - /* Request too long */ - return -2; - } - - n = pull_inner( - fp, conn, buf + *nread, bufsiz - *nread, request_timeout); - if (n == -2) { - /* Receive error */ - return -1; - } - - /* update clock after every read request */ - clock_gettime(CLOCK_MONOTONIC, &last_action_time); - - if (n > 0) { - *nread += n; - request_len = get_http_header_len(buf, *nread); - } - - if ((n <= 0) && (request_timeout >= 0)) { - if (mg_difftimespec(&last_action_time, &(conn->req_time)) - > request_timeout) { - /* Timeout */ - return -3; - } - } - } - - return request_len; -} - - -#if !defined(NO_CGI) || !defined(NO_FILES) -static int -forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl) -{ - const char *expect; - char buf[MG_BUF_LEN]; - int success = 0; - - if (!conn) { - return 0; - } - - expect = mg_get_header(conn, "Expect"); - DEBUG_ASSERT(fp != NULL); - if (!fp) { - mg_send_http_error(conn, 500, "%s", "Error: NULL File"); - return 0; - } - - if ((expect != NULL) && (mg_strcasecmp(expect, "100-continue") != 0)) { - /* Client sent an "Expect: xyz" header and xyz is not 100-continue. - */ - mg_send_http_error(conn, 417, "Error: Can not fulfill expectation"); - } else { - if (expect != NULL) { - (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); - conn->status_code = 100; - } else { - conn->status_code = 200; - } - - DEBUG_ASSERT(conn->consumed_content == 0); - - if (conn->consumed_content != 0) { - mg_send_http_error(conn, 500, "%s", "Error: Size mismatch"); - return 0; - } - - for (;;) { - int nread = mg_read(conn, buf, sizeof(buf)); - if (nread <= 0) { - success = (nread == 0); - break; - } - if (push_all(conn->phys_ctx, fp, sock, ssl, buf, nread) != nread) { - break; - } - } - - /* Each error code path in this function must send an error */ - if (!success) { - /* NOTE: Maybe some data has already been sent. */ - /* TODO (low): If some data has been sent, a correct error - * reply can no longer be sent, so just close the connection */ - mg_send_http_error(conn, 500, "%s", ""); - } - } - - return success; -} -#endif - - -#if defined(USE_TIMERS) - -#define TIMER_API static -#include "timer.inl" - -#endif /* USE_TIMERS */ - - -#if !defined(NO_CGI) -/* This structure helps to create an environment for the spawned CGI - * program. - * Environment is an array of "VARIABLE=VALUE\0" ASCII strings, - * last element must be NULL. - * However, on Windows there is a requirement that all these - * VARIABLE=VALUE\0 - * strings must reside in a contiguous buffer. The end of the buffer is - * marked by two '\0' characters. - * We satisfy both worlds: we create an envp array (which is vars), all - * entries are actually pointers inside buf. */ -struct cgi_environment { - struct mg_connection *conn; - /* Data block */ - char *buf; /* Environment buffer */ - size_t buflen; /* Space available in buf */ - size_t bufused; /* Space taken in buf */ - /* Index block */ - char **var; /* char **envp */ - size_t varlen; /* Number of variables available in var */ - size_t varused; /* Number of variables stored in var */ -}; - - -static void addenv(struct cgi_environment *env, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(2, 3); - -/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective - * pointer into the vars array. Assumes env != NULL and fmt != NULL. */ -static void -addenv(struct cgi_environment *env, const char *fmt, ...) -{ - size_t i, n, space; - int truncated = 0; - char *added; - va_list ap; - - if ((env->varlen - env->varused) < 2) { - mg_cry_internal(env->conn, - "%s: Cannot register CGI variable [%s]", - __func__, - fmt); - return; - } - - /* Calculate how much space is left in the buffer */ - space = (env->buflen - env->bufused); - - do { - /* Space for "\0\0" is always needed. */ - if (space <= 2) { - /* Allocate new buffer */ - n = env->buflen + CGI_ENVIRONMENT_SIZE; - added = (char *)mg_realloc_ctx(env->buf, n, env->conn->phys_ctx); - if (!added) { - /* Out of memory */ - mg_cry_internal( - env->conn, - "%s: Cannot allocate memory for CGI variable [%s]", - __func__, - fmt); - return; - } - /* Retarget pointers */ - env->buf = added; - env->buflen = n; - for (i = 0, n = 0; i < env->varused; i++) { - env->var[i] = added + n; - n += strlen(added + n) + 1; - } - space = (env->buflen - env->bufused); - } - - /* Make a pointer to the free space int the buffer */ - added = env->buf + env->bufused; - - /* Copy VARIABLE=VALUE\0 string into the free space */ - va_start(ap, fmt); - mg_vsnprintf(env->conn, &truncated, added, space - 1, fmt, ap); - va_end(ap); - - /* Do not add truncated strings to the environment */ - if (truncated) { - /* Reallocate the buffer */ - space = 0; - } - } while (truncated); - - /* Calculate number of bytes added to the environment */ - n = strlen(added) + 1; - env->bufused += n; - - /* Append a pointer to the added string into the envp array */ - env->var[env->varused] = added; - env->varused++; -} - -/* Return 0 on success, non-zero if an error occurs. */ - -static int -prepare_cgi_environment(struct mg_connection *conn, - const char *prog, - struct cgi_environment *env, - int cgi_config_idx) -{ - const char *s; - struct vec var_vec; - char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128]; - int i, truncated, uri_len; - - if ((conn == NULL) || (prog == NULL) || (env == NULL)) { - return -1; - } - - env->conn = conn; - env->buflen = CGI_ENVIRONMENT_SIZE; - env->bufused = 0; - env->buf = (char *)mg_malloc_ctx(env->buflen, conn->phys_ctx); - if (env->buf == NULL) { - mg_cry_internal(conn, - "%s: Not enough memory for environmental buffer", - __func__); - return -1; - } - env->varlen = MAX_CGI_ENVIR_VARS; - env->varused = 0; - env->var = - (char **)mg_malloc_ctx(env->varlen * sizeof(char *), conn->phys_ctx); - if (env->var == NULL) { - mg_cry_internal(conn, - "%s: Not enough memory for environmental variables", - __func__); - mg_free(env->buf); - return -1; - } - - addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); - addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); - addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); - if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { - addenv(env, - "FALLBACK_DOCUMENT_ROOT=%s", - conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); - } - addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version()); - - /* Prepare the environment block */ - addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1"); - addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1"); - addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */ - - addenv(env, "SERVER_PORT=%d", conn->request_info.server_port); - - sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - addenv(env, "REMOTE_ADDR=%s", src_addr); - - addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method); - addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port); - - addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri); - addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri); - addenv(env, "LOCAL_URI_RAW=%s", conn->request_info.local_uri_raw); - - /* SCRIPT_NAME */ - uri_len = (int)strlen(conn->request_info.local_uri); - if (conn->path_info == NULL) { - if (conn->request_info.local_uri[uri_len - 1] != '/') { - /* URI: /path_to_script/script.cgi */ - addenv(env, "SCRIPT_NAME=%s", conn->request_info.local_uri); - } else { - /* URI: /path_to_script/ ... using index.cgi */ - const char *index_file = strrchr(prog, '/'); - if (index_file) { - addenv(env, - "SCRIPT_NAME=%s%s", - conn->request_info.local_uri, - index_file + 1); - } - } - } else { - /* URI: /path_to_script/script.cgi/path_info */ - addenv(env, - "SCRIPT_NAME=%.*s", - uri_len - (int)strlen(conn->path_info), - conn->request_info.local_uri); - } - - addenv(env, "SCRIPT_FILENAME=%s", prog); - if (conn->path_info == NULL) { - addenv(env, "PATH_TRANSLATED=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); - } else { - addenv(env, - "PATH_TRANSLATED=%s%s", - conn->dom_ctx->config[DOCUMENT_ROOT], - conn->path_info); - } - - addenv(env, "HTTPS=%s", (conn->ssl == NULL) ? "off" : "on"); - - if ((s = mg_get_header(conn, "Content-Type")) != NULL) { - addenv(env, "CONTENT_TYPE=%s", s); - } - if (conn->request_info.query_string != NULL) { - addenv(env, "QUERY_STRING=%s", conn->request_info.query_string); - } - if ((s = mg_get_header(conn, "Content-Length")) != NULL) { - addenv(env, "CONTENT_LENGTH=%s", s); - } - if ((s = getenv("PATH")) != NULL) { - addenv(env, "PATH=%s", s); - } - if (conn->path_info != NULL) { - addenv(env, "PATH_INFO=%s", conn->path_info); - } - - if (conn->status_code > 0) { - /* CGI error handler should show the status code */ - addenv(env, "STATUS=%d", conn->status_code); - } - -#if defined(_WIN32) - if ((s = getenv("COMSPEC")) != NULL) { - addenv(env, "COMSPEC=%s", s); - } - if ((s = getenv("SYSTEMROOT")) != NULL) { - addenv(env, "SYSTEMROOT=%s", s); - } - if ((s = getenv("SystemDrive")) != NULL) { - addenv(env, "SystemDrive=%s", s); - } - if ((s = getenv("ProgramFiles")) != NULL) { - addenv(env, "ProgramFiles=%s", s); - } - if ((s = getenv("ProgramFiles(x86)")) != NULL) { - addenv(env, "ProgramFiles(x86)=%s", s); - } -#else - if ((s = getenv("LD_LIBRARY_PATH")) != NULL) { - addenv(env, "LD_LIBRARY_PATH=%s", s); - } -#endif /* _WIN32 */ - - if ((s = getenv("PERLLIB")) != NULL) { - addenv(env, "PERLLIB=%s", s); - } - - if (conn->request_info.remote_user != NULL) { - addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user); - addenv(env, "%s", "AUTH_TYPE=Digest"); - } - - /* Add all headers as HTTP_* variables */ - for (i = 0; i < conn->request_info.num_headers; i++) { - - (void)mg_snprintf(conn, - &truncated, - http_var_name, - sizeof(http_var_name), - "HTTP_%s", - conn->request_info.http_headers[i].name); - - if (truncated) { - mg_cry_internal(conn, - "%s: HTTP header variable too long [%s]", - __func__, - conn->request_info.http_headers[i].name); - continue; - } - - /* Convert variable name into uppercase, and change - to _ */ - for (p = http_var_name; *p != '\0'; p++) { - if (*p == '-') { - *p = '_'; - } - *p = (char)toupper((unsigned char)*p); - } - - addenv(env, - "%s=%s", - http_var_name, - conn->request_info.http_headers[i].value); - } - - /* Add user-specified variables */ - s = conn->dom_ctx->config[CGI_ENVIRONMENT + cgi_config_idx]; - while ((s = next_option(s, &var_vec, NULL)) != NULL) { - addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr); - } - - env->var[env->varused] = NULL; - env->buf[env->bufused] = '\0'; - - return 0; -} - - -/* Data for CGI process control: PID and number of references */ -struct process_control_data { - pid_t pid; - ptrdiff_t references; -}; - -static int -abort_cgi_process(void *data) -{ - /* Waitpid checks for child status and won't work for a pid that does - * not identify a child of the current process. Thus, if the pid is - * reused, we will not affect a different process. */ - struct process_control_data *proc = (struct process_control_data *)data; - int status = 0; - ptrdiff_t refs; - pid_t ret_pid; - - ret_pid = waitpid(proc->pid, &status, WNOHANG); - if ((ret_pid != (pid_t)-1) && (status == 0)) { - /* Stop child process */ - DEBUG_TRACE("CGI timer: Stop child process %d\n", proc->pid); - kill(proc->pid, SIGABRT); - - /* Wait until process is terminated (don't leave zombies) */ - while (waitpid(proc->pid, &status, 0) != (pid_t)-1) /* nop */ - ; - } else { - DEBUG_TRACE("CGI timer: Child process %d already stopped\n", proc->pid); - } - /* Dec reference counter */ - refs = mg_atomic_dec(&proc->references); - if (refs == 0) { - /* no more references - free data */ - mg_free(data); - } - - return 0; -} - - -/* Local (static) function assumes all arguments are valid. */ -static void -handle_cgi_request(struct mg_connection *conn, - const char *prog, - int cgi_config_idx) -{ - char *buf; - size_t buflen; - int headers_len, data_len, i, truncated; - int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1}; - const char *status, *status_text; - char *pbuf, dir[UTF8_PATH_MAX], *p; - struct mg_request_info ri; - struct cgi_environment blk; - FILE *in = NULL, *out = NULL, *err = NULL; - struct mg_file fout = STRUCT_FILE_INITIALIZER; - pid_t pid = (pid_t)-1; - struct process_control_data *proc = NULL; - char *cfg_buffering = conn->dom_ctx->config[CGI_BUFFERING + cgi_config_idx]; - int no_buffering = 0; - -#if defined(USE_TIMERS) - double cgi_timeout; - if (conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) { - /* Get timeout in seconds */ - cgi_timeout = - atof(conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) * 0.001; - } else { - cgi_timeout = - atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001; - } -#endif - if (cfg_buffering != NULL) { - if (!mg_strcasecmp(cfg_buffering, "no")) { - no_buffering = 1; - } - } - - buf = NULL; - buflen = conn->phys_ctx->max_request_size; - i = prepare_cgi_environment(conn, prog, &blk, cgi_config_idx); - if (i != 0) { - blk.buf = NULL; - blk.var = NULL; - goto done; - } - - /* CGI must be executed in its own directory. 'dir' must point to the - * directory containing executable program, 'p' must point to the - * executable program name relative to 'dir'. */ - (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog); - - if (truncated) { - mg_cry_internal(conn, "Error: CGI program \"%s\": Path too long", prog); - mg_send_http_error(conn, 500, "Error: %s", "CGI path too long"); - goto done; - } - - if ((p = strrchr(dir, '/')) != NULL) { - *p++ = '\0'; - } else { - dir[0] = '.'; - dir[1] = '\0'; - p = (char *)prog; - } - - if ((pipe(fdin) != 0) || (pipe(fdout) != 0) || (pipe(fderr) != 0)) { - status = strerror(ERRNO); - mg_cry_internal( - conn, - "Error: CGI program \"%s\": Can not create CGI pipes: %s", - prog, - status); - mg_send_http_error(conn, - 500, - "Error: Cannot create CGI pipe: %s", - status); - goto done; - } - - proc = (struct process_control_data *) - mg_malloc_ctx(sizeof(struct process_control_data), conn->phys_ctx); - if (proc == NULL) { - mg_cry_internal(conn, "Error: CGI program \"%s\": Out or memory", prog); - mg_send_http_error(conn, 500, "Error: Out of memory [%s]", prog); - goto done; - } - - DEBUG_TRACE("CGI: spawn %s %s\n", dir, p); - pid = spawn_process( - conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir, cgi_config_idx); - - if (pid == (pid_t)-1) { - status = strerror(ERRNO); - mg_cry_internal( - conn, - "Error: CGI program \"%s\": Can not spawn CGI process: %s", - prog, - status); - mg_send_http_error(conn, 500, "Error: Cannot spawn CGI process"); - mg_free(proc); - proc = NULL; - goto done; - } - - /* Store data in shared process_control_data */ - proc->pid = pid; - proc->references = 1; - -#if defined(USE_TIMERS) - if (cgi_timeout > 0.0) { - proc->references = 2; - - // Start a timer for CGI - timer_add(conn->phys_ctx, - cgi_timeout /* in seconds */, - 0.0, - 1, - abort_cgi_process, - (void *)proc, - NULL); - } -#endif - - /* Parent closes only one side of the pipes. - * If we don't mark them as closed, close() attempt before - * return from this function throws an exception on Windows. - * Windows does not like when closed descriptor is closed again. */ - (void)close(fdin[0]); - (void)close(fdout[1]); - (void)close(fderr[1]); - fdin[0] = fdout[1] = fderr[1] = -1; - - if (((in = fdopen(fdin[1], "wb")) == NULL) - || ((out = fdopen(fdout[0], "rb")) == NULL) - || ((err = fdopen(fderr[0], "rb")) == NULL)) { - status = strerror(ERRNO); - mg_cry_internal(conn, - "Error: CGI program \"%s\": Can not open fd: %s", - prog, - status); - mg_send_http_error(conn, - 500, - "Error: CGI can not open fd\nfdopen: %s", - status); - goto done; - } - - setbuf(in, NULL); - setbuf(out, NULL); - setbuf(err, NULL); - fout.access.fp = out; - - if ((conn->content_len != 0) || (conn->is_chunked)) { - DEBUG_TRACE("CGI: send body data (%" INT64_FMT ")\n", - conn->content_len); - - /* This is a POST/PUT request, or another request with body data. */ - if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) { - /* Error sending the body data */ - mg_cry_internal( - conn, - "Error: CGI program \"%s\": Forward body data failed", - prog); - goto done; - } - } - - /* Close so child gets an EOF. */ - fclose(in); - in = NULL; - fdin[1] = -1; - - /* Now read CGI reply into a buffer. We need to set correct - * status code, thus we need to see all HTTP headers first. - * Do not send anything back to client, until we buffer in all - * HTTP headers. */ - data_len = 0; - buf = (char *)mg_malloc_ctx(buflen, conn->phys_ctx); - if (buf == NULL) { - mg_send_http_error(conn, - 500, - "Error: Not enough memory for CGI buffer (%u bytes)", - (unsigned int)buflen); - mg_cry_internal( - conn, - "Error: CGI program \"%s\": Not enough memory for buffer (%u " - "bytes)", - prog, - (unsigned int)buflen); - goto done; - } - - DEBUG_TRACE("CGI: %s", "wait for response"); - headers_len = read_message(out, conn, buf, (int)buflen, &data_len); - DEBUG_TRACE("CGI: response: %li", (signed long)headers_len); - - if (headers_len <= 0) { - - /* Could not parse the CGI response. Check if some error message on - * stderr. */ - i = pull_all(err, conn, buf, (int)buflen); - if (i > 0) { - /* CGI program explicitly sent an error */ - /* Write the error message to the internal log */ - mg_cry_internal(conn, - "Error: CGI program \"%s\" sent error " - "message: [%.*s]", - prog, - i, - buf); - /* Don't send the error message back to the client */ - mg_send_http_error(conn, - 500, - "Error: CGI program \"%s\" failed.", - prog); - } else { - /* CGI program did not explicitly send an error, but a broken - * respon header */ - mg_cry_internal(conn, - "Error: CGI program sent malformed or too big " - "(>%u bytes) HTTP headers: [%.*s]", - (unsigned)buflen, - data_len, - buf); - - mg_send_http_error(conn, - 500, - "Error: CGI program sent malformed or too big " - "(>%u bytes) HTTP headers: [%.*s]", - (unsigned)buflen, - data_len, - buf); - } - - /* in both cases, abort processing CGI */ - goto done; - } - - pbuf = buf; - buf[headers_len - 1] = '\0'; - ri.num_headers = parse_http_headers(&pbuf, ri.http_headers); - - /* Make up and send the status line */ - status_text = "OK"; - if ((status = get_header(ri.http_headers, ri.num_headers, "Status")) - != NULL) { - conn->status_code = atoi(status); - status_text = status; - while (isdigit((unsigned char)*status_text) || *status_text == ' ') { - status_text++; - } - } else if (get_header(ri.http_headers, ri.num_headers, "Location") - != NULL) { - conn->status_code = 307; - } else { - conn->status_code = 200; - } - - if (!should_keep_alive(conn)) { - conn->must_close = 1; - } - - DEBUG_TRACE("CGI: response %u %s", conn->status_code, status_text); - - (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text); - - /* Send headers */ - for (i = 0; i < ri.num_headers; i++) { - DEBUG_TRACE("CGI header: %s: %s", - ri.http_headers[i].name, - ri.http_headers[i].value); - mg_printf(conn, - "%s: %s\r\n", - ri.http_headers[i].name, - ri.http_headers[i].value); - } - mg_write(conn, "\r\n", 2); - - /* Send chunk of data that may have been read after the headers */ - mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len)); - - /* Read the rest of CGI output and send to the client */ - DEBUG_TRACE("CGI: %s", "forward all data"); - send_file_data(conn, &fout, 0, INT64_MAX, no_buffering); /* send CGI data */ - DEBUG_TRACE("CGI: %s", "all data sent"); - -done: - mg_free(blk.var); - mg_free(blk.buf); - - if (pid != (pid_t)-1) { - abort_cgi_process((void *)proc); - } - - if (fdin[0] != -1) { - close(fdin[0]); - } - if (fdout[1] != -1) { - close(fdout[1]); - } - if (fderr[1] != -1) { - close(fderr[1]); - } - - if (in != NULL) { - fclose(in); - } else if (fdin[1] != -1) { - close(fdin[1]); - } - - if (out != NULL) { - fclose(out); - } else if (fdout[0] != -1) { - close(fdout[0]); - } - - if (err != NULL) { - fclose(err); - } else if (fderr[0] != -1) { - close(fderr[0]); - } - - mg_free(buf); -} -#endif /* !NO_CGI */ - - -#if !defined(NO_FILES) -static void -dav_mkcol(struct mg_connection *conn, const char *path) -{ - int rc, body_len; - struct de de; - - if (conn == NULL) { - return; - } - - /* TODO (mid): Check the mg_send_http_error situations in this function - */ - - memset(&de.file, 0, sizeof(de.file)); - if (!mg_stat(conn, path, &de.file)) { - mg_cry_internal(conn, - "%s: mg_stat(%s) failed: %s", - __func__, - path, - strerror(ERRNO)); - } - - if (de.file.last_modified) { - /* TODO (mid): This check does not seem to make any sense ! */ - /* TODO (mid): Add a webdav unit test first, before changing - * anything here. */ - mg_send_http_error( - conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO)); - return; - } - - body_len = conn->data_len - conn->request_len; - if (body_len > 0) { - mg_send_http_error( - conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO)); - return; - } - - rc = mg_mkdir(conn, path, 0755); - DEBUG_TRACE("mkdir %s: %i", path, rc); - if (rc == 0) { - /* Create 201 "Created" response */ - mg_response_header_start(conn, 201); - send_static_cache_header(conn); - send_additional_header(conn); - mg_response_header_add(conn, "Content-Length", "0", -1); - - /* Send all headers - there is no body */ - mg_response_header_send(conn); - } else { - int http_status = 500; - switch (errno) { - case EEXIST: - http_status = 405; - break; - case EACCES: - http_status = 403; - break; - case ENOENT: - http_status = 409; - break; - } - - mg_send_http_error(conn, - http_status, - "Error processing %s: %s", - path, - strerror(ERRNO)); - } -} - - -/* Forward decrlaration */ -static int get_uri_type(const char *uri); -static const char * -get_rel_url_at_current_server(const char *uri, - const struct mg_connection *conn); - - -static void -dav_move_file(struct mg_connection *conn, const char *path, int do_copy) -{ - const char *overwrite_hdr; - const char *destination_hdr; - const char *root; - int rc, dest_uri_type; - int http_status = 400; - int do_overwrite = 0; - int destination_ok = 0; - char dest_path[UTF8_PATH_MAX]; - struct mg_file_stat ignored; - - if (conn == NULL) { - return; - } - - root = conn->dom_ctx->config[DOCUMENT_ROOT]; - overwrite_hdr = mg_get_header(conn, "Overwrite"); - destination_hdr = mg_get_header(conn, "Destination"); - if ((overwrite_hdr != NULL) && (toupper(overwrite_hdr[0]) == 'T')) { - do_overwrite = 1; - } - - if ((destination_hdr == NULL) || (destination_hdr[0] == 0)) { - mg_send_http_error(conn, 400, "%s", "Missing destination"); - return; - } - - if (root != NULL) { - char *local_dest = NULL; - dest_uri_type = get_uri_type(destination_hdr); - if (dest_uri_type == 2) { - local_dest = mg_strdup_ctx(destination_hdr, conn->phys_ctx); - } else if ((dest_uri_type == 3) || (dest_uri_type == 4)) { - const char *h = - get_rel_url_at_current_server(destination_hdr, conn); - if (h) { - size_t len = strlen(h); - local_dest = mg_malloc_ctx(len + 1, conn->phys_ctx); - mg_url_decode(h, (int)len, local_dest, (int)len + 1, 0); - } - } - if (local_dest != NULL) { - remove_dot_segments(local_dest); - if (local_dest[0] == '/') { - int trunc_check = 0; - mg_snprintf(conn, - &trunc_check, - dest_path, - sizeof(dest_path), - "%s/%s", - root, - local_dest); - if (trunc_check == 0) { - destination_ok = 1; - } - } - mg_free(local_dest); - } - } - - if (!destination_ok) { - mg_send_http_error(conn, 502, "%s", "Illegal destination"); - return; - } - - /* Check now if this file exists */ - if (mg_stat(conn, dest_path, &ignored)) { - /* File exists */ - if (do_overwrite) { - /* Overwrite allowed: delete the file first */ - if (0 != remove(dest_path)) { - /* No overwrite: return error */ - mg_send_http_error(conn, - 403, - "Cannot overwrite file: %s", - dest_path); - return; - } - } else { - /* No overwrite: return error */ - mg_send_http_error(conn, - 412, - "Destination already exists: %s", - dest_path); - return; - } - } - - /* Copy / Move / Rename operation. */ - DEBUG_TRACE("%s %s to %s", (do_copy ? "copy" : "move"), path, dest_path); -#if defined(_WIN32) - { - /* For Windows, we need to convert from UTF-8 to UTF-16 first. */ - wchar_t wSource[UTF16_PATH_MAX]; - wchar_t wDest[UTF16_PATH_MAX]; - BOOL ok; - - path_to_unicode(conn, path, wSource, ARRAY_SIZE(wSource)); - path_to_unicode(conn, dest_path, wDest, ARRAY_SIZE(wDest)); - if (do_copy) { - ok = CopyFileW(wSource, wDest, do_overwrite ? FALSE : TRUE); - } else { - ok = MoveFileExW(wSource, - wDest, - do_overwrite ? MOVEFILE_REPLACE_EXISTING : 0); - } - if (ok) { - rc = 0; - } else { - DWORD lastErr = GetLastError(); - if (lastErr == ERROR_ALREADY_EXISTS) { - mg_send_http_error(conn, - 412, - "Destination already exists: %s", - dest_path); - return; - } - rc = -1; - http_status = 400; - } - } - -#else - { - /* Linux uses already UTF-8, we don't need to convert file names. */ - - if (do_copy) { - /* TODO: COPY for Linux. */ - mg_send_http_error(conn, 403, "%s", "COPY forbidden"); - return; - } - - rc = rename(path, dest_path); - if (rc) { - switch (errno) { - case EEXIST: - http_status = 412; - break; - case EACCES: - http_status = 403; - break; - case ENOENT: - http_status = 409; - break; - } - } - } -#endif - - if (rc == 0) { - /* Create 204 "No Content" response */ - mg_response_header_start(conn, 204); - mg_response_header_add(conn, "Content-Length", "0", -1); - - /* Send all headers - there is no body */ - mg_response_header_send(conn); - } else { - mg_send_http_error(conn, http_status, "Operation failed"); - } -} - - -static void -put_file(struct mg_connection *conn, const char *path) -{ - struct mg_file file = STRUCT_FILE_INITIALIZER; - const char *range; - int64_t r1, r2; - int rc; - - if (conn == NULL) { - return; - } - - DEBUG_TRACE("store %s", path); - - if (mg_stat(conn, path, &file.stat)) { - /* File already exists */ - conn->status_code = 200; - - if (file.stat.is_directory) { - /* This is an already existing directory, - * so there is nothing to do for the server. */ - rc = 0; - - } else { - /* File exists and is not a directory. */ - /* Can it be replaced? */ - - /* Check if the server may write this file */ - if (access(path, W_OK) == 0) { - /* Access granted */ - rc = 1; - } else { - mg_send_http_error( - conn, - 403, - "Error: Put not possible\nReplacing %s is not allowed", - path); - return; - } - } - } else { - /* File should be created */ - conn->status_code = 201; - rc = put_dir(conn, path); - } - - if (rc == 0) { - /* put_dir returns 0 if path is a directory */ - - /* Create response */ - mg_response_header_start(conn, conn->status_code); - send_no_cache_header(conn); - send_additional_header(conn); - mg_response_header_add(conn, "Content-Length", "0", -1); - - /* Send all headers - there is no body */ - mg_response_header_send(conn); - - /* Request to create a directory has been fulfilled successfully. - * No need to put a file. */ - return; - } - - if (rc == -1) { - /* put_dir returns -1 if the path is too long */ - mg_send_http_error(conn, - 414, - "Error: Path too long\nput_dir(%s): %s", - path, - strerror(ERRNO)); - return; - } - - if (rc == -2) { - /* put_dir returns -2 if the directory can not be created */ - mg_send_http_error(conn, - 500, - "Error: Can not create directory\nput_dir(%s): %s", - path, - strerror(ERRNO)); - return; - } - - /* A file should be created or overwritten. */ - /* Currently CivetWeb does not need read+write access. */ - if (!mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &file) - || file.access.fp == NULL) { - (void)mg_fclose(&file.access); - mg_send_http_error(conn, - 500, - "Error: Can not create file\nfopen(%s): %s", - path, - strerror(ERRNO)); - return; - } - - fclose_on_exec(&file.access, conn); - range = mg_get_header(conn, "Content-Range"); - r1 = r2 = 0; - if ((range != NULL) && parse_range_header(range, &r1, &r2) > 0) { - conn->status_code = 206; /* Partial content */ - if (0 != fseeko(file.access.fp, r1, SEEK_SET)) { - mg_send_http_error(conn, - 500, - "Error: Internal error processing file %s", - path); - return; - } - } - - if (!forward_body_data(conn, file.access.fp, INVALID_SOCKET, NULL)) { - /* forward_body_data failed. - * The error code has already been sent to the client, - * and conn->status_code is already set. */ - (void)mg_fclose(&file.access); - return; - } - - if (mg_fclose(&file.access) != 0) { - /* fclose failed. This might have different reasons, but a likely - * one is "no space on disk", http 507. */ - conn->status_code = 507; - } - - /* Create response (status_code has been set before) */ - mg_response_header_start(conn, conn->status_code); - send_no_cache_header(conn); - send_additional_header(conn); - mg_response_header_add(conn, "Content-Length", "0", -1); - - /* Send all headers - there is no body */ - mg_response_header_send(conn); -} - - -static void -delete_file(struct mg_connection *conn, const char *path) -{ - struct de de; - memset(&de.file, 0, sizeof(de.file)); - if (!mg_stat(conn, path, &de.file)) { - /* mg_stat returns 0 if the file does not exist */ - mg_send_http_error(conn, - 404, - "Error: Cannot delete file\nFile %s not found", - path); - return; - } - - DEBUG_TRACE("delete %s", path); - - if (de.file.is_directory) { - if (remove_directory(conn, path)) { - /* Delete is successful: Return 204 without content. */ - mg_send_http_error(conn, 204, "%s", ""); - } else { - /* Delete is not successful: Return 500 (Server error). */ - mg_send_http_error(conn, 500, "Error: Could not delete %s", path); - } - return; - } - - /* This is an existing file (not a directory). - * Check if write permission is granted. */ - if (access(path, W_OK) != 0) { - /* File is read only */ - mg_send_http_error( - conn, - 403, - "Error: Delete not possible\nDeleting %s is not allowed", - path); - return; - } - - /* Try to delete it. */ - if (mg_remove(conn, path) == 0) { - /* Delete was successful: Return 204 without content. */ - mg_response_header_start(conn, 204); - send_no_cache_header(conn); - send_additional_header(conn); - mg_response_header_add(conn, "Content-Length", "0", -1); - mg_response_header_send(conn); - - } else { - /* Delete not successful (file locked). */ - mg_send_http_error(conn, - 423, - "Error: Cannot delete file\nremove(%s): %s", - path, - strerror(ERRNO)); - } -} -#endif /* !NO_FILES */ - - -#if !defined(NO_FILESYSTEMS) -static void -send_ssi_file(struct mg_connection *, const char *, struct mg_file *, int); - - -static void -do_ssi_include(struct mg_connection *conn, - const char *ssi, - char *tag, - int include_level) -{ - char file_name[MG_BUF_LEN], path[512], *p; - struct mg_file file = STRUCT_FILE_INITIALIZER; - size_t len; - int truncated = 0; - - if (conn == NULL) { - return; - } - - /* sscanf() is safe here, since send_ssi_file() also uses buffer - * of size MG_BUF_LEN to get the tag. So strlen(tag) is - * always < MG_BUF_LEN. */ - if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) { - /* File name is relative to the webserver root */ - file_name[511] = 0; - (void)mg_snprintf(conn, - &truncated, - path, - sizeof(path), - "%s/%s", - conn->dom_ctx->config[DOCUMENT_ROOT], - file_name); - - } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) { - /* File name is relative to the webserver working directory - * or it is absolute system path */ - file_name[511] = 0; - (void) - mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name); - - } else if ((sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1) - || (sscanf(tag, " \"%511[^\"]\"", file_name) == 1)) { - /* File name is relative to the current document */ - file_name[511] = 0; - (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi); - - if (!truncated) { - if ((p = strrchr(path, '/')) != NULL) { - p[1] = '\0'; - } - len = strlen(path); - (void)mg_snprintf(conn, - &truncated, - path + len, - sizeof(path) - len, - "%s", - file_name); - } - - } else { - mg_cry_internal(conn, "Bad SSI #include: [%s]", tag); - return; - } - - if (truncated) { - mg_cry_internal(conn, "SSI #include path length overflow: [%s]", tag); - return; - } - - if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) { - mg_cry_internal(conn, - "Cannot open SSI #include: [%s]: fopen(%s): %s", - tag, - path, - strerror(ERRNO)); - } else { - fclose_on_exec(&file.access, conn); - if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path) - > 0) { - send_ssi_file(conn, path, &file, include_level + 1); - } else { - send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */ - } - (void)mg_fclose(&file.access); /* Ignore errors for readonly files */ - } -} - - -#if !defined(NO_POPEN) -static void -do_ssi_exec(struct mg_connection *conn, char *tag) -{ - char cmd[1024] = ""; - struct mg_file file = STRUCT_FILE_INITIALIZER; - - if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) { - mg_cry_internal(conn, "Bad SSI #exec: [%s]", tag); - } else { - cmd[1023] = 0; - if ((file.access.fp = popen(cmd, "r")) == NULL) { - mg_cry_internal(conn, - "Cannot SSI #exec: [%s]: %s", - cmd, - strerror(ERRNO)); - } else { - send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */ - pclose(file.access.fp); - } - } -} -#endif /* !NO_POPEN */ - - -static int -mg_fgetc(struct mg_file *filep) -{ - if (filep == NULL) { - return EOF; - } - - if (filep->access.fp != NULL) { - return fgetc(filep->access.fp); - } else { - return EOF; - } -} - - -static void -send_ssi_file(struct mg_connection *conn, - const char *path, - struct mg_file *filep, - int include_level) -{ - char buf[MG_BUF_LEN]; - int ch, len, in_tag, in_ssi_tag; - - if (include_level > 10) { - mg_cry_internal(conn, "SSI #include level is too deep (%s)", path); - return; - } - - in_tag = in_ssi_tag = len = 0; - - /* Read file, byte by byte, and look for SSI include tags */ - while ((ch = mg_fgetc(filep)) != EOF) { - - if (in_tag) { - /* We are in a tag, either SSI tag or html tag */ - - if (ch == '>') { - /* Tag is closing */ - buf[len++] = '>'; - - if (in_ssi_tag) { - /* Handle SSI tag */ - buf[len] = 0; - - if ((len > 12) && !memcmp(buf + 5, "include", 7)) { - do_ssi_include(conn, path, buf + 12, include_level + 1); -#if !defined(NO_POPEN) - } else if ((len > 9) && !memcmp(buf + 5, "exec", 4)) { - do_ssi_exec(conn, buf + 9); -#endif /* !NO_POPEN */ - } else { - mg_cry_internal(conn, - "%s: unknown SSI " - "command: \"%s\"", - path, - buf); - } - len = 0; - in_ssi_tag = in_tag = 0; - - } else { - /* Not an SSI tag */ - /* Flush buffer */ - (void)mg_write(conn, buf, (size_t)len); - len = 0; - in_tag = 0; - } - - } else { - /* Tag is still open */ - buf[len++] = (char)(ch & 0xff); - - if ((len == 5) && !memcmp(buf, " Error */ - return -1; - } - - /* Upgrade to ... */ - if (0 != mg_strcasestr(upgrade_to, "websocket")) { - /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and - * "Sec-WebSocket-Version" are also required. - * Don't check them here, since even an unsupported websocket protocol - * request still IS a websocket request (in contrast to a standard HTTP - * request). It will fail later in handle_websocket_request. - */ - return PROTOCOL_TYPE_WEBSOCKET; /* Websocket */ - } - if (0 != mg_strcasestr(upgrade_to, "h2")) { - return PROTOCOL_TYPE_HTTP2; /* Websocket */ - } - - /* Upgrade to another protocol */ - return -1; -} - - -static int -parse_match_net(const struct vec *vec, const union usa *sa, int no_strict) -{ - int n; - unsigned int a, b, c, d, slash; - - if (sscanf(vec->ptr, "%u.%u.%u.%u/%u%n", &a, &b, &c, &d, &slash, &n) - != 5) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to an - // integer value, but function will not report conversion - // errors; consider using 'strtol' instead - slash = 32; - if (sscanf(vec->ptr, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) - != 4) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to - // an integer value, but function will not report conversion - // errors; consider using 'strtol' instead - n = 0; - } - } - - if ((n > 0) && ((size_t)n == vec->len)) { - if ((a < 256) && (b < 256) && (c < 256) && (d < 256) && (slash < 33)) { - /* IPv4 format */ - if (sa->sa.sa_family == AF_INET) { - uint32_t ip = ntohl(sa->sin.sin_addr.s_addr); - uint32_t net = ((uint32_t)a << 24) | ((uint32_t)b << 16) - | ((uint32_t)c << 8) | (uint32_t)d; - uint32_t mask = slash ? (0xFFFFFFFFu << (32 - slash)) : 0; - return (ip & mask) == net; - } - return 0; - } - } -#if defined(USE_IPV6) - else { - char ad[50]; - const char *p; - - if (sscanf(vec->ptr, "[%49[^]]]/%u%n", ad, &slash, &n) != 2) { - slash = 128; - if (sscanf(vec->ptr, "[%49[^]]]%n", ad, &n) != 1) { - n = 0; - } - } - - if ((n <= 0) && no_strict) { - /* no square brackets? */ - p = strchr(vec->ptr, '/'); - if (p && (p < (vec->ptr + vec->len))) { - if (((size_t)(p - vec->ptr) < sizeof(ad)) - && (sscanf(p, "/%u%n", &slash, &n) == 1)) { - n += (int)(p - vec->ptr); - mg_strlcpy(ad, vec->ptr, (size_t)(p - vec->ptr) + 1); - } else { - n = 0; - } - } else if (vec->len < sizeof(ad)) { - n = (int)vec->len; - slash = 128; - mg_strlcpy(ad, vec->ptr, vec->len + 1); - } - } - - if ((n > 0) && ((size_t)n == vec->len) && (slash < 129)) { - p = ad; - c = 0; - /* zone indexes are unsupported, at least two colons are needed */ - while (isxdigit((unsigned char)*p) || (*p == '.') || (*p == ':')) { - if (*(p++) == ':') { - c++; - } - } - if ((*p == '\0') && (c >= 2)) { - struct sockaddr_in6 sin6; - unsigned int i; - - /* for strict validation, an actual IPv6 argument is needed */ - if (sa->sa.sa_family != AF_INET6) { - return 0; - } - if (mg_inet_pton(AF_INET6, ad, &sin6, sizeof(sin6), 0)) { - /* IPv6 format */ - for (i = 0; i < 16; i++) { - uint8_t ip = sa->sin6.sin6_addr.s6_addr[i]; - uint8_t net = sin6.sin6_addr.s6_addr[i]; - uint8_t mask = 0; - - if (8 * i + 8 < slash) { - mask = 0xFFu; - } else if (8 * i < slash) { - mask = (uint8_t)(0xFFu << (8 * i + 8 - slash)); - } - if ((ip & mask) != net) { - return 0; - } - } - return 1; - } - } - } - } -#else - (void)no_strict; -#endif - - /* malformed */ - return -1; -} - - -static int -set_throttle(const char *spec, const union usa *rsa, const char *uri) -{ - int throttle = 0; - struct vec vec, val; - char mult; - double v; - - while ((spec = next_option(spec, &vec, &val)) != NULL) { - mult = ','; - if ((val.ptr == NULL) - || (sscanf(val.ptr, "%lf%c", &v, &mult) - < 1) // NOLINT(cert-err34-c) 'sscanf' used to convert a string - // to an integer value, but function will not report - // conversion errors; consider using 'strtol' instead - || (v < 0) - || ((lowercase(&mult) != 'k') && (lowercase(&mult) != 'm') - && (mult != ','))) { - continue; - } - v *= (lowercase(&mult) == 'k') - ? 1024 - : ((lowercase(&mult) == 'm') ? 1048576 : 1); - if (vec.len == 1 && vec.ptr[0] == '*') { - throttle = (int)v; - } else { - int matched = parse_match_net(&vec, rsa, 0); - if (matched >= 0) { - /* a valid IP subnet */ - if (matched) { - throttle = (int)v; - } - } else if (match_prefix(vec.ptr, vec.len, uri) > 0) { - throttle = (int)v; - } - } - } - - return throttle; -} - - -/* The mg_upload function is superseded by mg_handle_form_request. */ -#include "handle_form.inl" - - -static int -get_first_ssl_listener_index(const struct mg_context *ctx) -{ - unsigned int i; - int idx = -1; - if (ctx) { - for (i = 0; ((idx == -1) && (i < ctx->num_listening_sockets)); i++) { - idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1; - } - } - return idx; -} - - -/* Return host (without port) */ -static void -get_host_from_request_info(struct vec *host, const struct mg_request_info *ri) -{ - const char *host_header = - get_header(ri->http_headers, ri->num_headers, "Host"); - - host->ptr = NULL; - host->len = 0; - - if (host_header != NULL) { - const char *pos; - - /* If the "Host" is an IPv6 address, like [::1], parse until ] - * is found. */ - if (*host_header == '[') { - pos = strchr(host_header, ']'); - if (!pos) { - /* Malformed hostname starts with '[', but no ']' found */ - DEBUG_TRACE("%s", "Host name format error '[' without ']'"); - return; - } - /* terminate after ']' */ - host->ptr = host_header; - host->len = (size_t)(pos + 1 - host_header); - } else { - /* Otherwise, a ':' separates hostname and port number */ - pos = strchr(host_header, ':'); - if (pos != NULL) { - host->len = (size_t)(pos - host_header); - } else { - host->len = strlen(host_header); - } - host->ptr = host_header; - } - } -} - - -static int -switch_domain_context(struct mg_connection *conn) -{ - struct vec host; - - get_host_from_request_info(&host, &conn->request_info); - - if (host.ptr) { - if (conn->ssl) { - /* This is a HTTPS connection, maybe we have a hostname - * from SNI (set in ssl_servername_callback). */ - const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN]; - if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) { - /* We are not using the default domain */ - if ((strlen(sslhost) != host.len) - || mg_strncasecmp(host.ptr, sslhost, host.len)) { - /* Mismatch between SNI domain and HTTP domain */ - DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %.*s", - sslhost, - (int)host.len, - host.ptr); - return 0; - } - } - - } else { - struct mg_domain_context *dom = &(conn->phys_ctx->dd); - while (dom) { - const char *domName = dom->config[AUTHENTICATION_DOMAIN]; - size_t domNameLen = strlen(domName); - if ((domNameLen == host.len) - && !mg_strncasecmp(host.ptr, domName, host.len)) { - - /* Found matching domain */ - DEBUG_TRACE("HTTP domain %s found", - dom->config[AUTHENTICATION_DOMAIN]); - - /* TODO: Check if this is a HTTP or HTTPS domain */ - conn->dom_ctx = dom; - break; - } - mg_lock_context(conn->phys_ctx); - dom = dom->next; - mg_unlock_context(conn->phys_ctx); - } - } - - DEBUG_TRACE("HTTP%s Host: %.*s", - conn->ssl ? "S" : "", - (int)host.len, - host.ptr); - - } else { - DEBUG_TRACE("HTTP%s Host is not set", conn->ssl ? "S" : ""); - return 1; - } - - return 1; -} - - -static void -redirect_to_https_port(struct mg_connection *conn, int port) -{ - char target_url[MG_BUF_LEN]; - int truncated = 0; - const char *expect_proto = - (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET) ? "wss" : "https"; - - /* Use "308 Permanent Redirect" */ - int redirect_code = 308; - - /* In any case, close the current connection */ - conn->must_close = 1; - - /* Send host, port, uri and (if it exists) ?query_string */ - if (mg_construct_local_link( - conn, target_url, sizeof(target_url), expect_proto, port, NULL) - < 0) { - truncated = 1; - } else if (conn->request_info.query_string != NULL) { - size_t slen1 = strlen(target_url); - size_t slen2 = strlen(conn->request_info.query_string); - if ((slen1 + slen2 + 2) < sizeof(target_url)) { - target_url[slen1] = '?'; - memcpy(target_url + slen1 + 1, - conn->request_info.query_string, - slen2); - target_url[slen1 + slen2 + 1] = 0; - } else { - truncated = 1; - } - } - - /* Check overflow in location buffer (will not occur if MG_BUF_LEN - * is used as buffer size) */ - if (truncated) { - mg_send_http_error(conn, 500, "%s", "Redirect URL too long"); - return; - } - - /* Use redirect helper function */ - mg_send_http_redirect(conn, target_url, redirect_code); -} - - -static void -mg_set_handler_type(struct mg_context *phys_ctx, - struct mg_domain_context *dom_ctx, - const char *uri, - int handler_type, - int is_delete_request, - mg_request_handler handler, - struct mg_websocket_subprotocols *subprotocols, - mg_websocket_connect_handler connect_handler, - mg_websocket_ready_handler ready_handler, - mg_websocket_data_handler data_handler, - mg_websocket_close_handler close_handler, - mg_authorization_handler auth_handler, - void *cbdata) -{ - struct mg_handler_info *tmp_rh, **lastref; - size_t urilen = strlen(uri); - - if (handler_type == WEBSOCKET_HANDLER) { - DEBUG_ASSERT(handler == NULL); - DEBUG_ASSERT(is_delete_request || connect_handler != NULL - || ready_handler != NULL || data_handler != NULL - || close_handler != NULL); - - DEBUG_ASSERT(auth_handler == NULL); - if (handler != NULL) { - return; - } - if (!is_delete_request && (connect_handler == NULL) - && (ready_handler == NULL) && (data_handler == NULL) - && (close_handler == NULL)) { - return; - } - if (auth_handler != NULL) { - return; - } - - } else if (handler_type == REQUEST_HANDLER) { - DEBUG_ASSERT(connect_handler == NULL && ready_handler == NULL - && data_handler == NULL && close_handler == NULL); - DEBUG_ASSERT(is_delete_request || (handler != NULL)); - DEBUG_ASSERT(auth_handler == NULL); - - if ((connect_handler != NULL) || (ready_handler != NULL) - || (data_handler != NULL) || (close_handler != NULL)) { - return; - } - if (!is_delete_request && (handler == NULL)) { - return; - } - if (auth_handler != NULL) { - return; - } - - } else if (handler_type == AUTH_HANDLER) { - DEBUG_ASSERT(handler == NULL); - DEBUG_ASSERT(connect_handler == NULL && ready_handler == NULL - && data_handler == NULL && close_handler == NULL); - DEBUG_ASSERT(is_delete_request || (auth_handler != NULL)); - if (handler != NULL) { - return; - } - if ((connect_handler != NULL) || (ready_handler != NULL) - || (data_handler != NULL) || (close_handler != NULL)) { - return; - } - if (!is_delete_request && (auth_handler == NULL)) { - return; - } - } else { - /* Unknown handler type. */ - return; - } - - if (!phys_ctx || !dom_ctx) { - /* no context available */ - return; - } - - mg_lock_context(phys_ctx); - - /* first try to find an existing handler */ - do { - lastref = &(dom_ctx->handlers); - for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL; - tmp_rh = tmp_rh->next) { - if (tmp_rh->handler_type == handler_type - && (urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) { - if (!is_delete_request) { - /* update existing handler */ - if (handler_type == REQUEST_HANDLER) { - /* Wait for end of use before updating */ - if (tmp_rh->refcount) { - mg_unlock_context(phys_ctx); - mg_sleep(1); - mg_lock_context(phys_ctx); - /* tmp_rh might have been freed, search again. */ - break; - } - /* Ok, the handler is no more use -> Update it */ - tmp_rh->handler = handler; - } else if (handler_type == WEBSOCKET_HANDLER) { - tmp_rh->subprotocols = subprotocols; - tmp_rh->connect_handler = connect_handler; - tmp_rh->ready_handler = ready_handler; - tmp_rh->data_handler = data_handler; - tmp_rh->close_handler = close_handler; - } else { /* AUTH_HANDLER */ - tmp_rh->auth_handler = auth_handler; - } - tmp_rh->cbdata = cbdata; - } else { - /* remove existing handler */ - if (handler_type == REQUEST_HANDLER) { - /* Wait for end of use before removing */ - if (tmp_rh->refcount) { - tmp_rh->removing = 1; - mg_unlock_context(phys_ctx); - mg_sleep(1); - mg_lock_context(phys_ctx); - /* tmp_rh might have been freed, search again. */ - break; - } - /* Ok, the handler is no more used */ - } - *lastref = tmp_rh->next; - mg_free(tmp_rh->uri); - mg_free(tmp_rh); - } - mg_unlock_context(phys_ctx); - return; - } - lastref = &(tmp_rh->next); - } - } while (tmp_rh != NULL); - - if (is_delete_request) { - /* no handler to set, this was a remove request to a non-existing - * handler */ - mg_unlock_context(phys_ctx); - return; - } - - tmp_rh = - (struct mg_handler_info *)mg_calloc_ctx(1, - sizeof(struct mg_handler_info), - phys_ctx); - if (tmp_rh == NULL) { - mg_unlock_context(phys_ctx); - mg_cry_ctx_internal(phys_ctx, - "%s", - "Cannot create new request handler struct, OOM"); - return; - } - tmp_rh->uri = mg_strdup_ctx(uri, phys_ctx); - if (!tmp_rh->uri) { - mg_unlock_context(phys_ctx); - mg_free(tmp_rh); - mg_cry_ctx_internal(phys_ctx, - "%s", - "Cannot create new request handler struct, OOM"); - return; - } - tmp_rh->uri_len = urilen; - if (handler_type == REQUEST_HANDLER) { - tmp_rh->refcount = 0; - tmp_rh->removing = 0; - tmp_rh->handler = handler; - } else if (handler_type == WEBSOCKET_HANDLER) { - tmp_rh->subprotocols = subprotocols; - tmp_rh->connect_handler = connect_handler; - tmp_rh->ready_handler = ready_handler; - tmp_rh->data_handler = data_handler; - tmp_rh->close_handler = close_handler; - } else { /* AUTH_HANDLER */ - tmp_rh->auth_handler = auth_handler; - } - tmp_rh->cbdata = cbdata; - tmp_rh->handler_type = handler_type; - tmp_rh->next = NULL; - - *lastref = tmp_rh; - mg_unlock_context(phys_ctx); -} - - -CIVETWEB_API void -mg_set_request_handler(struct mg_context *ctx, - const char *uri, - mg_request_handler handler, - void *cbdata) -{ - mg_set_handler_type(ctx, - &(ctx->dd), - uri, - REQUEST_HANDLER, - handler == NULL, - handler, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - cbdata); -} - - -CIVETWEB_API void -mg_set_websocket_handler(struct mg_context *ctx, - const char *uri, - mg_websocket_connect_handler connect_handler, - mg_websocket_ready_handler ready_handler, - mg_websocket_data_handler data_handler, - mg_websocket_close_handler close_handler, - void *cbdata) -{ - mg_set_websocket_handler_with_subprotocols(ctx, - uri, - NULL, - connect_handler, - ready_handler, - data_handler, - close_handler, - cbdata); -} - - -CIVETWEB_API void -mg_set_websocket_handler_with_subprotocols( - struct mg_context *ctx, - const char *uri, - struct mg_websocket_subprotocols *subprotocols, - mg_websocket_connect_handler connect_handler, - mg_websocket_ready_handler ready_handler, - mg_websocket_data_handler data_handler, - mg_websocket_close_handler close_handler, - void *cbdata) -{ - int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL) - && (data_handler == NULL) - && (close_handler == NULL); - mg_set_handler_type(ctx, - &(ctx->dd), - uri, - WEBSOCKET_HANDLER, - is_delete_request, - NULL, - subprotocols, - connect_handler, - ready_handler, - data_handler, - close_handler, - NULL, - cbdata); -} - - -CIVETWEB_API void -mg_set_auth_handler(struct mg_context *ctx, - const char *uri, - mg_authorization_handler handler, - void *cbdata) -{ - mg_set_handler_type(ctx, - &(ctx->dd), - uri, - AUTH_HANDLER, - handler == NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - handler, - cbdata); -} - - -static int -get_request_handler(struct mg_connection *conn, - int handler_type, - mg_request_handler *handler, - struct mg_websocket_subprotocols **subprotocols, - mg_websocket_connect_handler *connect_handler, - mg_websocket_ready_handler *ready_handler, - mg_websocket_data_handler *data_handler, - mg_websocket_close_handler *close_handler, - mg_authorization_handler *auth_handler, - void **cbdata, - struct mg_handler_info **handler_info) -{ - const struct mg_request_info *request_info = mg_get_request_info(conn); - if (request_info) { - const char *uri = request_info->local_uri; - size_t urilen = strlen(uri); - struct mg_handler_info *tmp_rh; - int step, matched; - - if (!conn || !conn->phys_ctx || !conn->dom_ctx) { - return 0; - } - - mg_lock_context(conn->phys_ctx); - - for (step = 0; step < 3; step++) { - for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL; - tmp_rh = tmp_rh->next) { - if (tmp_rh->handler_type != handler_type) { - continue; - } - if (step == 0) { - /* first try for an exact match */ - matched = (tmp_rh->uri_len == urilen) - && (strcmp(tmp_rh->uri, uri) == 0); - } else if (step == 1) { - /* next try for a partial match, we will accept - uri/something */ - matched = - (tmp_rh->uri_len < urilen) - && (uri[tmp_rh->uri_len] == '/') - && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0); - } else { - /* finally try for pattern match */ - matched = - match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0; - } - if (matched) { - if (handler_type == WEBSOCKET_HANDLER) { - *subprotocols = tmp_rh->subprotocols; - *connect_handler = tmp_rh->connect_handler; - *ready_handler = tmp_rh->ready_handler; - *data_handler = tmp_rh->data_handler; - *close_handler = tmp_rh->close_handler; - } else if (handler_type == REQUEST_HANDLER) { - if (tmp_rh->removing) { - /* Treat as none found */ - step = 2; - break; - } - *handler = tmp_rh->handler; - /* Acquire handler and give it back */ - tmp_rh->refcount++; - *handler_info = tmp_rh; - } else { /* AUTH_HANDLER */ - *auth_handler = tmp_rh->auth_handler; - } - *cbdata = tmp_rh->cbdata; - mg_unlock_context(conn->phys_ctx); - return 1; - } - } - } - - mg_unlock_context(conn->phys_ctx); - } - return 0; /* none found */ -} - - -/* Check if the script file is in a path, allowed for script files. - * This can be used if uploading files is possible not only for the server - * admin, and the upload mechanism does not check the file extension. - */ -static int -is_in_script_path(const struct mg_connection *conn, const char *path) -{ - /* TODO (Feature): Add config value for allowed script path. - * Default: All allowed. */ - (void)conn; - (void)path; - return 1; -} - - -#if defined(USE_WEBSOCKET) && defined(MG_EXPERIMENTAL_INTERFACES) -static int -experimental_websocket_client_data_wrapper(struct mg_connection *conn, - int bits, - char *data, - size_t len, - void *cbdata) -{ - struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata; - if (pcallbacks->websocket_data) { - return pcallbacks->websocket_data(conn, bits, data, len); - } - /* No handler set - assume "OK" */ - return 1; -} - - -static void -experimental_websocket_client_close_wrapper(const struct mg_connection *conn, - void *cbdata) -{ - struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata; - if (pcallbacks->connection_close) { - pcallbacks->connection_close(conn); - } -} -#endif - - -/* Decrement recount of handler. conn must not be NULL, handler_info may be NULL - */ -static void -release_handler_ref(struct mg_connection *conn, - struct mg_handler_info *handler_info) -{ - if (handler_info != NULL) { - /* Use context lock for ref counter */ - mg_lock_context(conn->phys_ctx); - handler_info->refcount--; - mg_unlock_context(conn->phys_ctx); - } -} - - -/* This is the heart of the Civetweb's logic. - * This function is called when the request is read, parsed and validated, - * and Civetweb must decide what action to take: serve a file, or - * a directory, or call embedded function, etcetera. */ -static void -handle_request(struct mg_connection *conn) -{ - struct mg_request_info *ri = &conn->request_info; - char path[UTF8_PATH_MAX]; - int uri_len, ssl_index; - int is_found = 0, is_script_resource = 0, is_websocket_request = 0, - is_put_or_delete_request = 0, is_callback_resource = 0, - is_template_text_file = 0, is_webdav_request = 0; - int i; - struct mg_file file = STRUCT_FILE_INITIALIZER; - mg_request_handler callback_handler = NULL; - struct mg_handler_info *handler_info = NULL; - struct mg_websocket_subprotocols *subprotocols; - mg_websocket_connect_handler ws_connect_handler = NULL; - mg_websocket_ready_handler ws_ready_handler = NULL; - mg_websocket_data_handler ws_data_handler = NULL; - mg_websocket_close_handler ws_close_handler = NULL; - void *callback_data = NULL; - mg_authorization_handler auth_handler = NULL; - void *auth_callback_data = NULL; - int handler_type; - time_t curtime = time(NULL); - char date[64]; - char *tmp; - - path[0] = 0; - - /* 0. Reset internal state (required for HTTP/2 proxy) */ - conn->request_state = 0; - - /* 1. get the request url */ - /* 1.1. split into url and query string */ - if ((conn->request_info.query_string = strchr(ri->request_uri, '?')) - != NULL) { - *((char *)conn->request_info.query_string++) = '\0'; - } - - /* 1.2. do a https redirect, if required. Do not decode URIs yet. */ - if (!conn->client.is_ssl && conn->client.ssl_redir) { - ssl_index = get_first_ssl_listener_index(conn->phys_ctx); - if (ssl_index >= 0) { - int port = (int)ntohs(USA_IN_PORT_UNSAFE( - &(conn->phys_ctx->listening_sockets[ssl_index].lsa))); - redirect_to_https_port(conn, port); - } else { - /* A http to https forward port has been specified, - * but no https port to forward to. */ - mg_send_http_error(conn, - 503, - "%s", - "Error: SSL forward not configured properly"); - mg_cry_internal(conn, - "%s", - "Can not redirect to SSL, no SSL port available"); - } - return; - } - uri_len = (int)strlen(ri->local_uri); - - /* 1.3. decode url (if config says so) */ - if (should_decode_url(conn)) { - url_decode_in_place((char *)ri->local_uri); - } - - /* URL decode the query-string only if explicitly set in the configuration - */ - if (conn->request_info.query_string) { - if (should_decode_query_string(conn)) { - url_decode_in_place((char *)conn->request_info.query_string); - } - } - - /* 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is not - * possible. The fact that we cleaned the URI is stored in that the - * pointer to ri->local_ur and ri->local_uri_raw are now different. - * ri->local_uri_raw still points to memory allocated in - * worker_thread_run(). ri->local_uri is private to the request so we - * don't have to use preallocated memory here. */ - tmp = mg_strdup(ri->local_uri_raw); - if (!tmp) { - /* Out of memory. We cannot do anything reasonable here. */ - return; - } - remove_dot_segments(tmp); - ri->local_uri = tmp; - - /* step 1. completed, the url is known now */ - DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri); - - /* 2. if this ip has limited speed, set it for this connection */ - conn->throttle = set_throttle(conn->dom_ctx->config[THROTTLE], - &conn->client.rsa, - ri->local_uri); - - /* 3. call a "handle everything" callback, if registered */ - if (conn->phys_ctx->callbacks.begin_request != NULL) { - /* Note that since V1.7 the "begin_request" function is called - * before an authorization check. If an authorization check is - * required, use a request_handler instead. */ - i = conn->phys_ctx->callbacks.begin_request(conn); - if (i > 0) { - /* callback already processed the request. Store the - return value as a status code for the access log. */ - conn->status_code = i; - if (!conn->must_close) { - discard_unread_request_data(conn); - } - DEBUG_TRACE("%s", "begin_request handled request"); - return; - } else if (i == 0) { - /* civetweb should process the request */ - } else { - /* unspecified - may change with the next version */ - DEBUG_TRACE("%s", "done (undocumented behavior)"); - return; - } - } - - /* request not yet handled by a handler or redirect, so the request - * is processed here */ - - /* 4. Check for CORS preflight requests and handle them (if configured). - * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS - */ - if (!strcmp(ri->request_method, "OPTIONS")) { - /* Send a response to CORS preflights only if - * access_control_allow_methods is not NULL and not an empty string. - * In this case, scripts can still handle CORS. */ - const char *cors_meth_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; - const char *cors_orig_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; - const char *cors_origin = - get_header(ri->http_headers, ri->num_headers, "Origin"); - const char *cors_acrm = get_header(ri->http_headers, - ri->num_headers, - "Access-Control-Request-Method"); - - /* Todo: check if cors_origin is in cors_orig_cfg. - * Or, let the client check this. */ - - if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) - && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) - && (cors_origin != NULL) && (cors_acrm != NULL)) { - /* This is a valid CORS preflight, and the server is configured - * to handle it automatically. */ - const char *cors_acrh = - get_header(ri->http_headers, - ri->num_headers, - "Access-Control-Request-Headers"); - const char *cors_cred_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; - const char *cors_exphdr_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; - - gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, - "HTTP/1.1 200 OK\r\n" - "Date: %s\r\n" - "Access-Control-Allow-Origin: %s\r\n" - "Access-Control-Allow-Methods: %s\r\n" - "Content-Length: 0\r\n" - "Connection: %s\r\n", - date, - cors_orig_cfg, - ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), - suggest_connection_header(conn)); - - if (cors_cred_cfg && *cors_cred_cfg) { - mg_printf(conn, - "Access-Control-Allow-Credentials: %s\r\n", - cors_cred_cfg); - } - - if (cors_exphdr_cfg && *cors_exphdr_cfg) { - mg_printf(conn, - "Access-Control-Expose-Headers: %s\r\n", - cors_exphdr_cfg); - } - - if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) { - /* CORS request is asking for additional headers */ - const char *cors_hdr_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS]; - - if ((cors_hdr_cfg != NULL) && (*cors_hdr_cfg != 0)) { - /* Allow only if access_control_allow_headers is - * not NULL and not an empty string. If this - * configuration is set to *, allow everything. - * Otherwise this configuration must be a list - * of allowed HTTP header names. */ - mg_printf(conn, - "Access-Control-Allow-Headers: %s\r\n", - ((cors_hdr_cfg[0] == '*') ? cors_acrh - : cors_hdr_cfg)); - } - } - mg_printf(conn, "Access-Control-Max-Age: 60\r\n"); - mg_printf(conn, "\r\n"); - DEBUG_TRACE("%s", "OPTIONS done"); - return; - } - } - - /* 5. interpret the url to find out how the request must be handled - */ - /* 5.1. first test, if the request targets the regular http(s):// - * protocol namespace or the websocket ws(s):// protocol namespace. - */ - is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET); -#if defined(USE_WEBSOCKET) - handler_type = is_websocket_request ? WEBSOCKET_HANDLER : REQUEST_HANDLER; -#else - handler_type = REQUEST_HANDLER; -#endif /* defined(USE_WEBSOCKET) */ - - if (is_websocket_request) { - HTTP1_only; - } - - /* 5.2. check if the request will be handled by a callback */ - if (get_request_handler(conn, - handler_type, - &callback_handler, - &subprotocols, - &ws_connect_handler, - &ws_ready_handler, - &ws_data_handler, - &ws_close_handler, - NULL, - &callback_data, - &handler_info)) { - /* 5.2.1. A callback will handle this request. All requests - * handled by a callback have to be considered as requests - * to a script resource. */ - is_callback_resource = 1; - is_script_resource = 1; - is_put_or_delete_request = is_put_or_delete_method(conn); - /* Never handle a C callback according to File WebDav rules, - * even if it is a webdav method */ - is_webdav_request = 0; /* is_civetweb_webdav_method(conn); */ - } else { - no_callback_resource: - - /* 5.2.2. No callback is responsible for this request. The URI - * addresses a file based resource (static content or Lua/cgi - * scripts in the file system). */ - is_callback_resource = 0; - interpret_uri(conn, - path, - sizeof(path), - &file.stat, - &is_found, - &is_script_resource, - &is_websocket_request, - &is_put_or_delete_request, - &is_webdav_request, - &is_template_text_file); - } - - /* 5.3. A webdav request (PROPFIND/PROPPATCH/LOCK/UNLOCK) */ - if (is_webdav_request) { - /* TODO: Do we need a config option? */ - const char *webdav_enable = conn->dom_ctx->config[ENABLE_WEBDAV]; - if (webdav_enable[0] != 'y') { - mg_send_http_error(conn, - 405, - "%s method not allowed", - conn->request_info.request_method); - DEBUG_TRACE("%s", "webdav rejected"); - return; - } - } - - /* 6. authorization check */ - /* 6.1. a custom authorization handler is installed */ - if (get_request_handler(conn, - AUTH_HANDLER, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - &auth_handler, - &auth_callback_data, - NULL)) { - if (!auth_handler(conn, auth_callback_data)) { - - /* Callback handler will not be used anymore. Release it */ - release_handler_ref(conn, handler_info); - DEBUG_TRACE("%s", "auth handler rejected request"); - return; - } - } else if (is_put_or_delete_request && !is_script_resource - && !is_callback_resource) { - HTTP1_only; - /* 6.2. this request is a PUT/DELETE to a real file */ - /* 6.2.1. thus, the server must have real files */ -#if defined(NO_FILES) - if (1) { -#else - if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL - || conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL) { -#endif - /* This code path will not be called for request handlers */ - DEBUG_ASSERT(handler_info == NULL); - - /* This server does not have any real files, thus the - * PUT/DELETE methods are not valid. */ - mg_send_http_error(conn, - 405, - "%s method not allowed", - conn->request_info.request_method); - DEBUG_TRACE("%s", "all file based put/delete requests rejected"); - return; - } - -#if !defined(NO_FILES) - /* 6.2.2. Check if put authorization for static files is - * available. - */ - if (!is_authorized_for_put(conn)) { - send_authorization_request(conn, NULL); - DEBUG_TRACE("%s", "file write needs authorization"); - return; - } -#endif - - } else { - /* 6.3. This is either a OPTIONS, GET, HEAD or POST request, - * or it is a PUT or DELETE request to a resource that does not - * correspond to a file. Check authorization. */ - if (!check_authorization(conn, path)) { - send_authorization_request(conn, NULL); - - /* Callback handler will not be used anymore. Release it */ - release_handler_ref(conn, handler_info); - DEBUG_TRACE("%s", "access authorization required"); - return; - } - } - - /* request is authorized or does not need authorization */ - - /* 7. check if there are request handlers for this uri */ - if (is_callback_resource) { - HTTP1_only; - if (!is_websocket_request) { - i = callback_handler(conn, callback_data); - - /* Callback handler will not be used anymore. Release it */ - release_handler_ref(conn, handler_info); - - if (i > 0) { - /* Do nothing, callback has served the request. Store - * then return value as status code for the log and discard - * all data from the client not used by the callback. */ - conn->status_code = i; - if (!conn->must_close) { - discard_unread_request_data(conn); - } - } else { - /* The handler did NOT handle the request. */ - /* Some proper reactions would be: - * a) close the connections without sending anything - * b) send a 404 not found - * c) try if there is a file matching the URI - * It would be possible to do a, b or c in the callback - * implementation, and return 1 - we cannot do anything - * here, that is not possible in the callback. - * - * TODO: What would be the best reaction here? - * (Note: The reaction may change, if there is a better - * idea.) - */ - - /* For the moment, use option c: We look for a proper file, - * but since a file request is not always a script resource, - * the authorization check might be different. */ - callback_handler = NULL; - - /* Here we are at a dead end: - * According to URI matching, a callback should be - * responsible for handling the request, - * we called it, but the callback declared itself - * not responsible. - * We use a goto here, to get out of this dead end, - * and continue with the default handling. - * A goto here is simpler and better to understand - * than some curious loop. */ - goto no_callback_resource; - } - } else { -#if defined(USE_WEBSOCKET) - handle_websocket_request(conn, - path, - is_callback_resource, - subprotocols, - ws_connect_handler, - ws_ready_handler, - ws_data_handler, - ws_close_handler, - callback_data); -#endif - } - DEBUG_TRACE("%s", "websocket handling done"); - return; - } - - /* 8. handle websocket requests */ -#if defined(USE_WEBSOCKET) - if (is_websocket_request) { - HTTP1_only; - if (is_script_resource) { - - if (is_in_script_path(conn, path)) { - /* Websocket Lua script */ - handle_websocket_request(conn, - path, - 0 /* Lua Script */, - NULL, - NULL, - NULL, - NULL, - NULL, - conn->phys_ctx->user_data); - } else { - /* Script was in an illegal path */ - mg_send_http_error(conn, 403, "%s", "Forbidden"); - } - } else { - mg_send_http_error(conn, 404, "%s", "Not found"); - } - DEBUG_TRACE("%s", "websocket script done"); - return; - } else -#endif - -#if defined(NO_FILES) - /* 9a. In case the server uses only callbacks, this uri is - * unknown. - * Then, all request handling ends here. */ - mg_send_http_error(conn, 404, "%s", "Not Found"); - -#else - /* 9b. This request is either for a static file or resource handled - * by a script file. Thus, a DOCUMENT_ROOT must exist. */ - if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL) { - mg_send_http_error(conn, 404, "%s", "Not Found"); - DEBUG_TRACE("%s", "no document root available"); - return; - } - - /* 10. Request is handled by a script */ - if (is_script_resource) { - HTTP1_only; - handle_file_based_request(conn, path, &file); - DEBUG_TRACE("%s", "script handling done"); - return; - } - - /* Request was not handled by a callback or script. It will be - * handled by a server internal method. */ - - /* 11. Handle put/delete/mkcol requests */ - if (is_put_or_delete_request) { - HTTP1_only; - /* 11.1. PUT method */ - if (!strcmp(ri->request_method, "PUT")) { - put_file(conn, path); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - /* 11.2. DELETE method */ - if (!strcmp(ri->request_method, "DELETE")) { - delete_file(conn, path); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - /* 11.3. MKCOL method */ - if (!strcmp(ri->request_method, "MKCOL")) { - dav_mkcol(conn, path); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - /* 11.4. MOVE method */ - if (!strcmp(ri->request_method, "MOVE")) { - dav_move_file(conn, path, 0); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - if (!strcmp(ri->request_method, "COPY")) { - dav_move_file(conn, path, 1); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - /* 11.5. LOCK method */ - if (!strcmp(ri->request_method, "LOCK")) { - dav_lock_file(conn, path); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - /* 11.6. UNLOCK method */ - if (!strcmp(ri->request_method, "UNLOCK")) { - dav_unlock_file(conn, path); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - /* 11.7. PROPPATCH method */ - if (!strcmp(ri->request_method, "PROPPATCH")) { - dav_proppatch(conn, path); - DEBUG_TRACE("handling %s request to %s done", - ri->request_method, - path); - return; - } - /* 11.8. Other methods, e.g.: PATCH - * This method is not supported for static resources, - * only for scripts (Lua, CGI) and callbacks. */ - mg_send_http_error(conn, - 405, - "%s method not allowed", - conn->request_info.request_method); - DEBUG_TRACE("method %s on %s is not supported", - ri->request_method, - path); - return; - } - - /* 11. File does not exist, or it was configured that it should be - * hidden */ - if (!is_found || (must_hide_file(conn, path))) { - mg_send_http_error(conn, 404, "%s", "Not found"); - DEBUG_TRACE("handling %s request to %s: file not found", - ri->request_method, - path); - return; - } - - /* 12. Directory uris should end with a slash */ - if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0) - && (ri->local_uri[uri_len - 1] != '/')) { - - /* Path + server root */ - size_t buflen = UTF8_PATH_MAX * 2 + 2; - char *new_path; - - if (ri->query_string) { - buflen += strlen(ri->query_string); - } - new_path = (char *)mg_malloc_ctx(buflen, conn->phys_ctx); - if (!new_path) { - mg_send_http_error(conn, 500, "out or memory"); - } else { - mg_get_request_link(conn, new_path, buflen - 1); - strcat(new_path, "/"); - if (ri->query_string) { - /* Append ? and query string */ - strcat(new_path, "?"); - strcat(new_path, ri->query_string); - } - mg_send_http_redirect(conn, new_path, 301); - mg_free(new_path); - } - DEBUG_TRACE("%s request to %s: directory redirection sent", - ri->request_method, - path); - return; - } - - /* 13. Handle other methods than GET/HEAD */ - /* 13.1. Handle PROPFIND */ - if (!strcmp(ri->request_method, "PROPFIND")) { - handle_propfind(conn, path, &file.stat); - DEBUG_TRACE("handling %s request to %s done", ri->request_method, path); - return; - } - /* 13.2. Handle OPTIONS for files */ - if (!strcmp(ri->request_method, "OPTIONS")) { - /* This standard handler is only used for real files. - * Scripts should support the OPTIONS method themselves, to allow a - * maximum flexibility. - * Lua and CGI scripts may fully support CORS this way (including - * preflights). */ - send_options(conn); - DEBUG_TRACE("handling %s request to %s done", ri->request_method, path); - return; - } - /* 13.3. everything but GET and HEAD (e.g. POST) */ - if ((0 != strcmp(ri->request_method, "GET")) - && (0 != strcmp(ri->request_method, "HEAD"))) { - mg_send_http_error(conn, - 405, - "%s method not allowed", - conn->request_info.request_method); - DEBUG_TRACE("handling %s request to %s done", ri->request_method, path); - return; - } - - /* 14. directories */ - if (file.stat.is_directory) { - /* Substitute files have already been handled above. */ - /* Here we can either generate and send a directory listing, - * or send an "access denied" error. */ - if (!mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING], - "yes")) { - handle_directory_request(conn, path); - } else { - mg_send_http_error(conn, - 403, - "%s", - "Error: Directory listing denied"); - } - DEBUG_TRACE("handling %s request to %s done", ri->request_method, path); - return; - } - - /* 15. Files with search/replace patterns: LSP and SSI */ - if (is_template_text_file) { - HTTP1_only; - handle_file_based_request(conn, path, &file); - DEBUG_TRACE("handling %s request to %s done (template)", - ri->request_method, - path); - return; - } - - /* 16. Static file - maybe cached */ -#if !defined(NO_CACHING) - if ((!conn->in_error_handler) && is_not_modified(conn, &file.stat)) { - /* Send 304 "Not Modified" - this must not send any body data */ - handle_not_modified_static_file_request(conn, &file); - DEBUG_TRACE("handling %s request to %s done (not modified)", - ri->request_method, - path); - return; - } -#endif /* !NO_CACHING */ - - /* 17. Static file - not cached */ - handle_static_file_request(conn, path, &file, NULL, NULL); - DEBUG_TRACE("handling %s request to %s done (static)", - ri->request_method, - path); - -#endif /* !defined(NO_FILES) */ -} - - -#if !defined(NO_FILESYSTEMS) -static void -handle_file_based_request(struct mg_connection *conn, - const char *path, - struct mg_file *file) -{ -#if !defined(NO_CGI) - int cgi_config_idx, inc, max; -#endif - - if (!conn || !conn->dom_ctx) { - return; - } - -#if defined(USE_LUA) - if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS], - path) - > 0) { - if (is_in_script_path(conn, path)) { - /* Lua server page: an SSI like page containing mostly plain - * html code plus some tags with server generated contents. */ - handle_lsp_request(conn, path, file, NULL); - } else { - /* Script was in an illegal path */ - mg_send_http_error(conn, 403, "%s", "Forbidden"); - } - return; - } - - if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS], path) - > 0) { - if (is_in_script_path(conn, path)) { - /* Lua in-server module script: a CGI like script used to - * generate the entire reply. */ - mg_exec_lua_script(conn, path, NULL); - } else { - /* Script was in an illegal path */ - mg_send_http_error(conn, 403, "%s", "Forbidden"); - } - return; - } -#endif - -#if defined(USE_DUKTAPE) - if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], - path) - > 0) { - if (is_in_script_path(conn, path)) { - /* Call duktape to generate the page */ - mg_exec_duktape_script(conn, path); - } else { - /* Script was in an illegal path */ - mg_send_http_error(conn, 403, "%s", "Forbidden"); - } - return; - } -#endif - -#if !defined(NO_CGI) - inc = CGI2_EXTENSIONS - CGI_EXTENSIONS; - max = PUT_DELETE_PASSWORDS_FILE - CGI_EXTENSIONS; - for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) { - if (conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL) { - if (match_prefix_strlen( - conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx], - path) - > 0) { - if (is_in_script_path(conn, path)) { - /* CGI scripts may support all HTTP methods */ - handle_cgi_request(conn, path, cgi_config_idx); - } else { - /* Script was in an illegal path */ - mg_send_http_error(conn, 403, "%s", "Forbidden"); - } - return; - } - } - } -#endif /* !NO_CGI */ - - if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path) > 0) { - if (is_in_script_path(conn, path)) { - handle_ssi_file_request(conn, path, file); - } else { - /* Script was in an illegal path */ - mg_send_http_error(conn, 403, "%s", "Forbidden"); - } - return; - } - -#if !defined(NO_CACHING) - if ((!conn->in_error_handler) && is_not_modified(conn, &file->stat)) { - /* Send 304 "Not Modified" - this must not send any body data */ - handle_not_modified_static_file_request(conn, file); - return; - } -#endif /* !NO_CACHING */ - - handle_static_file_request(conn, path, file, NULL, NULL); -} -#endif /* NO_FILESYSTEMS */ - - -static void -close_all_listening_sockets(struct mg_context *ctx) -{ - unsigned int i; - if (!ctx) { - return; - } - - for (i = 0; i < ctx->num_listening_sockets; i++) { - closesocket(ctx->listening_sockets[i].sock); -#if defined(USE_X_DOM_SOCKET) - /* For unix domain sockets, the socket name represents a file that has - * to be deleted. */ - /* See - * https://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix - */ - if ((ctx->listening_sockets[i].lsa.sin.sin_family == AF_UNIX) - && (ctx->listening_sockets[i].sock != INVALID_SOCKET)) { - IGNORE_UNUSED_RESULT( - remove(ctx->listening_sockets[i].lsa.sun.sun_path)); - } -#endif - ctx->listening_sockets[i].sock = INVALID_SOCKET; - } - mg_free(ctx->listening_sockets); - ctx->listening_sockets = NULL; - mg_free(ctx->listening_socket_fds); - ctx->listening_socket_fds = NULL; -} - - -/* Valid listening port specification is: [ip_address:]port[s] - * Examples for IPv4: 80, 443s, 127.0.0.1:3128, 192.0.2.3:8080s - * Examples for IPv6: [::]:80, [::1]:80, - * [2001:0db8:7654:3210:FEDC:BA98:7654:3210]:443s - * see https://tools.ietf.org/html/rfc3513#section-2.2 - * In order to bind to both, IPv4 and IPv6, you can either add - * both ports using 8080,[::]:8080, or the short form +8080. - * Both forms differ in detail: 8080,[::]:8080 create two sockets, - * one only accepting IPv4 the other only IPv6. +8080 creates - * one socket accepting IPv4 and IPv6. Depending on the IPv6 - * environment, they might work differently, or might not work - * at all - it must be tested what options work best in the - * relevant network environment. - */ -static int -parse_port_string(const struct vec *vec, struct socket *so, int *ip_version) -{ - unsigned int a, b, c, d; - unsigned port; - unsigned long portUL; - int len; - const char *cb; - char *endptr; -#if defined(USE_IPV6) - char buf[100] = {0}; -#endif - - /* MacOS needs that. If we do not zero it, subsequent bind() will fail. - * Also, all-zeroes in the socket address means binding to all addresses - * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */ - memset(so, 0, sizeof(*so)); - so->lsa.sin.sin_family = AF_INET; - *ip_version = 0; - - /* Initialize len as invalid. */ - port = 0; - len = 0; - - /* Test for different ways to format this string */ - if (sscanf(vec->ptr, - "%u.%u.%u.%u:%u%n", - &a, - &b, - &c, - &d, - &port, - &len) // NOLINT(cert-err34-c) 'sscanf' used to convert a string - // to an integer value, but function will not report - // conversion errors; consider using 'strtol' instead - == 5) { - /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ - so->lsa.sin.sin_addr.s_addr = - htonl((a << 24) | (b << 16) | (c << 8) | d); - so->lsa.sin.sin_port = htons((uint16_t)port); - *ip_version = 4; - -#if defined(USE_IPV6) - } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2 - && ((size_t)len <= vec->len) - && mg_inet_pton( - AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6), 0)) { - /* IPv6 address, examples: see above */ - /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton - */ - so->lsa.sin6.sin6_port = htons((uint16_t)port); - *ip_version = 6; -#endif - - } else if ((vec->ptr[0] == '+') - && (sscanf(vec->ptr + 1, "%u%n", &port, &len) - == 1)) { // NOLINT(cert-err34-c) 'sscanf' used to convert a - // string to an integer value, but function will not - // report conversion errors; consider using 'strtol' - // instead - - /* Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY */ - /* Add 1 to len for the + character we skipped before */ - len++; - -#if defined(USE_IPV6) - /* Set socket family to IPv6, do not use IPV6_V6ONLY */ - so->lsa.sin6.sin6_family = AF_INET6; - so->lsa.sin6.sin6_port = htons((uint16_t)port); - *ip_version = 4 + 6; -#else - /* Bind to IPv4 only, since IPv6 is not built in. */ - so->lsa.sin.sin_port = htons((uint16_t)port); - *ip_version = 4; -#endif - - } else if (is_valid_port(portUL = strtoul(vec->ptr, &endptr, 0)) - && (vec->ptr != endptr)) { - len = (int)(endptr - vec->ptr); - port = (uint16_t)portUL; - /* If only port is specified, bind to IPv4, INADDR_ANY */ - so->lsa.sin.sin_port = htons((uint16_t)port); - *ip_version = 4; - - } else if ((cb = strchr(vec->ptr, ':')) != NULL) { - /* String could be a hostname. This check algorithm - * will only work for RFC 952 compliant hostnames, - * starting with a letter, containing only letters, - * digits and hyphen ('-'). Newer specs may allow - * more, but this is not guaranteed here, since it - * may interfere with rules for port option lists. */ - - /* According to RFC 1035, hostnames are restricted to 255 characters - * in total (63 between two dots). */ - char hostname[256]; - size_t hostnlen = (size_t)(cb - vec->ptr); - - if ((hostnlen >= vec->len) || (hostnlen >= sizeof(hostname))) { - /* This would be invalid in any case */ - *ip_version = 0; - return 0; - } - - mg_strlcpy(hostname, vec->ptr, hostnlen + 1); - - if (mg_inet_pton( - AF_INET, hostname, &so->lsa.sin, sizeof(so->lsa.sin), 1)) { - if (sscanf(cb + 1, "%u%n", &port, &len) - == 1) { // NOLINT(cert-err34-c) 'sscanf' used to convert a - // string to an integer value, but function will not - // report conversion errors; consider using 'strtol' - // instead - *ip_version = 4; - so->lsa.sin.sin_port = htons((uint16_t)port); - len += (int)(hostnlen + 1); - } else { - len = 0; - } -#if defined(USE_IPV6) - } else if (mg_inet_pton(AF_INET6, - hostname, - &so->lsa.sin6, - sizeof(so->lsa.sin6), - 1)) { - if (sscanf(cb + 1, "%u%n", &port, &len) == 1) { - *ip_version = 6; - so->lsa.sin6.sin6_port = htons((uint16_t)port); - len += (int)(hostnlen + 1); - } else { - len = 0; - } -#endif - } else { - len = 0; - } - -#if defined(USE_X_DOM_SOCKET) - - } else if (vec->ptr[0] == 'x') { - /* unix (linux) domain socket */ - if (vec->len < sizeof(so->lsa.sun.sun_path)) { - len = vec->len; - so->lsa.sun.sun_family = AF_UNIX; - memset(so->lsa.sun.sun_path, 0, sizeof(so->lsa.sun.sun_path)); - memcpy(so->lsa.sun.sun_path, (char *)vec->ptr + 1, vec->len - 1); - port = 0; - *ip_version = 99; - } else { - /* String too long */ - len = 0; - } -#endif - - } else { - /* Parsing failure. */ - len = 0; - } - - /* sscanf and the option splitting code ensure the following condition - * Make sure the port is valid and vector ends with the port, 'o', 's', or - * 'r' */ - if ((len > 0) && (is_valid_port(port))) { - int bad_suffix = 0; - size_t i; - - /* Parse any suffix character(s) after the port number */ - for (i = len; i < vec->len; i++) { - unsigned char *opt = NULL; - switch (vec->ptr[i]) { - case 'o': - opt = &so->is_optional; - break; - case 'r': - opt = &so->ssl_redir; - break; - case 's': - opt = &so->is_ssl; - break; - default: /* empty */ - break; - } - - if ((opt) && (*opt == 0)) - *opt = 1; - else { - bad_suffix = 1; - break; - } - } - - if ((bad_suffix == 0) && ((so->is_ssl == 0) || (so->ssl_redir == 0))) { - return 1; - } - } - - /* Reset ip_version to 0 if there is an error */ - *ip_version = 0; - return 0; -} - - -/* Is there any SSL port in use? */ -static int -is_ssl_port_used(const char *ports) -{ - if (ports) { - /* There are several different allowed syntax variants: - * - "80" for a single port using every network interface - * - "localhost:80" for a single port using only localhost - * - "80,localhost:8080" for two ports, one bound to localhost - * - "80,127.0.0.1:8084,[::1]:8086" for three ports, one bound - * to IPv4 localhost, one to IPv6 localhost - * - "+80" use port 80 for IPv4 and IPv6 - * - "+80r,+443s" port 80 (HTTP) is a redirect to port 443 (HTTPS), - * for both: IPv4 and IPv4 - * - "+443s,localhost:8080" port 443 (HTTPS) for every interface, - * additionally port 8080 bound to localhost connections - * - * If we just look for 's' anywhere in the string, "localhost:80" - * will be detected as SSL (false positive). - * Looking for 's' after a digit may cause false positives in - * "my24service:8080". - * Looking from 's' backward if there are only ':' and numbers - * before will not work for "24service:8080" (non SSL, port 8080) - * or "24s" (SSL, port 24). - * - * Remark: Initially hostnames were not allowed to start with a - * digit (according to RFC 952), this was allowed later (RFC 1123, - * Section 2.1). - * - * To get this correct, the entire string must be parsed as a whole, - * reading it as a list element for element and parsing with an - * algorithm equivalent to parse_port_string. - * - * In fact, we use local interface names here, not arbitrary - * hostnames, so in most cases the only name will be "localhost". - * - * So, for now, we use this simple algorithm, that may still return - * a false positive in bizarre cases. - */ - int i; - int portslen = (int)strlen(ports); - char prevIsNumber = 0; - - for (i = 0; i < portslen; i++) { - if (prevIsNumber) { - int suffixCharIdx = (ports[i] == 'o') - ? (i + 1) - : i; /* allow "os" and "or" suffixes */ - if (ports[suffixCharIdx] == 's' - || ports[suffixCharIdx] == 'r') { - return 1; - } - } - if (ports[i] >= '0' && ports[i] <= '9') { - prevIsNumber = 1; - } else { - prevIsNumber = 0; - } - } - } - return 0; -} - - -static int -set_ports_option(struct mg_context *phys_ctx) -{ - const char *list; - int on = 1; -#if defined(USE_IPV6) - int off = 0; -#endif - struct vec vec; - struct socket so, *ptr; - - struct mg_pollfd *pfd; - union usa usa; - socklen_t len; - int ip_version; - - int portsTotal = 0; - int portsOk = 0; - - const char *opt_txt; - long opt_listen_backlog; - - if (!phys_ctx) { - return 0; - } - - memset(&so, 0, sizeof(so)); - memset(&usa, 0, sizeof(usa)); - len = sizeof(usa); - list = phys_ctx->dd.config[LISTENING_PORTS]; - - while ((list = next_option(list, &vec, NULL)) != NULL) { - - portsTotal++; - - if (!parse_port_string(&vec, &so, &ip_version)) { - mg_cry_ctx_internal( - phys_ctx, - "%.*s: invalid port spec (entry %i). Expecting list of: %s", - (int)vec.len, - vec.ptr, - portsTotal, - "[IP_ADDRESS:]PORT[s|r]"); - continue; - } - -#if !defined(NO_SSL) - if (so.is_ssl && phys_ctx->dd.ssl_ctx == NULL) { - - mg_cry_ctx_internal(phys_ctx, - "Cannot add SSL socket (entry %i)", - portsTotal); - continue; - } -#endif - /* Create socket. */ - /* For a list of protocol numbers (e.g., TCP==6) see: - * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml - */ - if ((so.sock = - socket(so.lsa.sa.sa_family, - SOCK_STREAM, - (ip_version == 99) ? (/* LOCAL */ 0) : (/* TCP */ 6))) - == INVALID_SOCKET) { - - mg_cry_ctx_internal(phys_ctx, - "cannot create socket (entry %i)", - portsTotal); - if (so.is_optional) { - portsOk++; /* it's okay if we couldn't create a socket, - this port is optional anyway */ - } - continue; - } - -#if defined(_WIN32) - /* Windows SO_REUSEADDR lets many procs binds to a - * socket, SO_EXCLUSIVEADDRUSE makes the bind fail - * if someone already has the socket -- DTL */ - /* NOTE: If SO_EXCLUSIVEADDRUSE is used, - * Windows might need a few seconds before - * the same port can be used again in the - * same process, so a short Sleep may be - * required between mg_stop and mg_start. - */ - if (setsockopt(so.sock, - SOL_SOCKET, - SO_EXCLUSIVEADDRUSE, - (SOCK_OPT_TYPE)&on, - sizeof(on)) - != 0) { - - /* Set reuse option, but don't abort on errors. */ - mg_cry_ctx_internal( - phys_ctx, - "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)", - portsTotal); - } -#else - if (setsockopt(so.sock, - SOL_SOCKET, - SO_REUSEADDR, - (SOCK_OPT_TYPE)&on, - sizeof(on)) - != 0) { - - /* Set reuse option, but don't abort on errors. */ - mg_cry_ctx_internal( - phys_ctx, - "cannot set socket option SO_REUSEADDR (entry %i)", - portsTotal); - } -#endif - -#if defined(USE_X_DOM_SOCKET) - if (ip_version == 99) { - /* Unix domain socket */ - } else -#endif - - if (ip_version > 4) { - /* Could be 6 for IPv6 onlyor 10 (4+6) for IPv4+IPv6 */ -#if defined(USE_IPV6) - if (ip_version > 6) { - if (so.lsa.sa.sa_family == AF_INET6 - && setsockopt(so.sock, - IPPROTO_IPV6, - IPV6_V6ONLY, - (void *)&off, - sizeof(off)) - != 0) { - - /* Set IPv6 only option, but don't abort on errors. */ - mg_cry_ctx_internal(phys_ctx, - "cannot set socket option " - "IPV6_V6ONLY=off (entry %i)", - portsTotal); - } - } else { - if (so.lsa.sa.sa_family == AF_INET6 - && setsockopt(so.sock, - IPPROTO_IPV6, - IPV6_V6ONLY, - (void *)&on, - sizeof(on)) - != 0) { - - /* Set IPv6 only option, but don't abort on errors. */ - mg_cry_ctx_internal(phys_ctx, - "cannot set socket option " - "IPV6_V6ONLY=on (entry %i)", - portsTotal); - } - } -#else - mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available"); - closesocket(so.sock); - if (so.is_optional) { - portsOk++; /* it's okay if we couldn't set the socket option, - this port is optional anyway */ - } - so.sock = INVALID_SOCKET; - continue; -#endif - } - - if (so.lsa.sa.sa_family == AF_INET) { - - len = sizeof(so.lsa.sin); - if (bind(so.sock, &so.lsa.sa, len) != 0) { - mg_cry_ctx_internal(phys_ctx, - "cannot bind to %.*s: %d (%s)", - (int)vec.len, - vec.ptr, - (int)ERRNO, - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - if (so.is_optional) { - portsOk++; /* it's okay if we couldn't bind, this port is - optional anyway */ - } - continue; - } - } -#if defined(USE_IPV6) - else if (so.lsa.sa.sa_family == AF_INET6) { - - len = sizeof(so.lsa.sin6); - if (bind(so.sock, &so.lsa.sa, len) != 0) { - mg_cry_ctx_internal(phys_ctx, - "cannot bind to IPv6 %.*s: %d (%s)", - (int)vec.len, - vec.ptr, - (int)ERRNO, - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - if (so.is_optional) { - portsOk++; /* it's okay if we couldn't bind, this port is - optional anyway */ - } - continue; - } - } -#endif -#if defined(USE_X_DOM_SOCKET) - else if (so.lsa.sa.sa_family == AF_UNIX) { - - len = sizeof(so.lsa.sun); - if (bind(so.sock, &so.lsa.sa, len) != 0) { - mg_cry_ctx_internal(phys_ctx, - "cannot bind to unix socket %s: %d (%s)", - so.lsa.sun.sun_path, - (int)ERRNO, - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - if (so.is_optional) { - portsOk++; /* it's okay if we couldn't bind, this port is - optional anyway */ - } - continue; - } - } -#endif - else { - mg_cry_ctx_internal( - phys_ctx, - "cannot bind: address family not supported (entry %i)", - portsTotal); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - continue; - } - - opt_txt = phys_ctx->dd.config[LISTEN_BACKLOG_SIZE]; - opt_listen_backlog = strtol(opt_txt, NULL, 10); - if ((opt_listen_backlog > INT_MAX) || (opt_listen_backlog < 1)) { - mg_cry_ctx_internal(phys_ctx, - "%s value \"%s\" is invalid", - config_options[LISTEN_BACKLOG_SIZE].name, - opt_txt); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - continue; - } - - if (listen(so.sock, (int)opt_listen_backlog) != 0) { - - mg_cry_ctx_internal(phys_ctx, - "cannot listen to %.*s: %d (%s)", - (int)vec.len, - vec.ptr, - (int)ERRNO, - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - continue; - } - - if ((getsockname(so.sock, &(usa.sa), &len) != 0) - || (usa.sa.sa_family != so.lsa.sa.sa_family)) { - - int err = (int)ERRNO; - mg_cry_ctx_internal(phys_ctx, - "call to getsockname failed %.*s: %d (%s)", - (int)vec.len, - vec.ptr, - err, - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - continue; - } - - /* Update lsa port in case of random free ports */ -#if defined(USE_IPV6) - if (so.lsa.sa.sa_family == AF_INET6) { - so.lsa.sin6.sin6_port = usa.sin6.sin6_port; - } else -#endif - { - so.lsa.sin.sin_port = usa.sin.sin_port; - } - - if ((ptr = (struct socket *) - mg_realloc_ctx(phys_ctx->listening_sockets, - (phys_ctx->num_listening_sockets + 1) - * sizeof(phys_ctx->listening_sockets[0]), - phys_ctx)) - == NULL) { - - mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory"); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - continue; - } - - /* The +2 below includes the original +1 (for the socket we're about to - * add), plus another +1 for the thread_shutdown_notification_socket - * that we'll also want to poll() on so that mg_stop() can return - * quickly - */ - if ((pfd = (struct mg_pollfd *) - mg_realloc_ctx(phys_ctx->listening_socket_fds, - (phys_ctx->num_listening_sockets + 2) - * sizeof(phys_ctx->listening_socket_fds[0]), - phys_ctx)) - == NULL) { - - mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory"); - closesocket(so.sock); - so.sock = INVALID_SOCKET; - mg_free(ptr); - continue; - } - - set_close_on_exec(so.sock, NULL, phys_ctx); - phys_ctx->listening_sockets = ptr; - phys_ctx->listening_sockets[phys_ctx->num_listening_sockets] = so; - phys_ctx->listening_socket_fds = pfd; - phys_ctx->num_listening_sockets++; - portsOk++; - } - - if (portsOk != portsTotal) { - close_all_listening_sockets(phys_ctx); - portsOk = 0; - } - - return portsOk; -} - - -static const char * -header_val(const struct mg_connection *conn, const char *header) -{ - const char *header_value; - - if ((header_value = mg_get_header(conn, header)) == NULL) { - return "-"; - } else { - return header_value; - } -} - - -#if defined(MG_EXTERNAL_FUNCTION_log_access) -#include "external_log_access.inl" -#elif !defined(NO_FILESYSTEMS) - -static void -log_access(const struct mg_connection *conn) -{ - const struct mg_request_info *ri; - struct mg_file fi; - char date[64], src_addr[IP_ADDR_STR_LEN]; -#if defined(REENTRANT_TIME) - struct tm _tm; - struct tm *tm = &_tm; -#else - struct tm *tm; -#endif - - const char *referer; - const char *user_agent; - - char log_buf[4096]; - - if (!conn || !conn->dom_ctx) { - return; - } - - /* Set log message to "empty" */ - log_buf[0] = 0; - -#if defined(USE_LUA) - if (conn->phys_ctx->lua_bg_log_available) { - int ret; - struct mg_context *ctx = conn->phys_ctx; - lua_State *lstate = (lua_State *)ctx->lua_background_state; - pthread_mutex_lock(&ctx->lua_bg_mutex); - /* call "log()" in Lua */ - lua_getglobal(lstate, "log"); - prepare_lua_request_info_inner(conn, lstate); - push_lua_response_log_data(conn, lstate); - - ret = lua_pcall(lstate, /* args */ 2, /* results */ 1, 0); - if (ret == 0) { - int t = lua_type(lstate, -1); - if (t == LUA_TBOOLEAN) { - if (lua_toboolean(lstate, -1) == 0) { - /* log() returned false: do not log */ - pthread_mutex_unlock(&ctx->lua_bg_mutex); - return; - } - /* log returned true: continue logging */ - } else if (t == LUA_TSTRING) { - size_t len; - const char *txt = lua_tolstring(lstate, -1, &len); - if ((len == 0) || (*txt == 0)) { - /* log() returned empty string: do not log */ - pthread_mutex_unlock(&ctx->lua_bg_mutex); - return; - } - /* Copy test from Lua into log_buf */ - if (len >= sizeof(log_buf)) { - len = sizeof(log_buf) - 1; - } - memcpy(log_buf, txt, len); - log_buf[len] = 0; - } - } else { - lua_cry(conn, ret, lstate, "lua_background_script", "log"); - } - pthread_mutex_unlock(&ctx->lua_bg_mutex); - } -#endif - - if (conn->dom_ctx->config[ACCESS_LOG_FILE] != NULL) { - if (mg_fopen(conn, - conn->dom_ctx->config[ACCESS_LOG_FILE], - MG_FOPEN_MODE_APPEND, - &fi) - == 0) { - fi.access.fp = NULL; - } - } else { - fi.access.fp = NULL; - } - - /* Log is written to a file and/or a callback. If both are not set, - * executing the rest of the function is pointless. */ - if ((fi.access.fp == NULL) - && (conn->phys_ctx->callbacks.log_access == NULL)) { - return; - } - - /* If we did not get a log message from Lua, create it here. */ - if (!log_buf[0]) { -#if defined(REENTRANT_TIME) - localtime_r(&conn->conn_birth_time, tm); -#else - tm = localtime(&conn->conn_birth_time); -#endif - if (tm != NULL) { - strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm); - } else { - mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date)); - } - - ri = &conn->request_info; - - sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - referer = header_val(conn, "Referer"); - user_agent = header_val(conn, "User-Agent"); - - mg_snprintf(conn, - NULL, /* Ignore truncation in access log */ - log_buf, - sizeof(log_buf), - "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT - " %s %s", - src_addr, - (ri->remote_user == NULL) ? "-" : ri->remote_user, - date, - ri->request_method ? ri->request_method : "-", - ri->request_uri ? ri->request_uri : "-", - ri->query_string ? "?" : "", - ri->query_string ? ri->query_string : "", - ri->http_version, - conn->status_code, - conn->num_bytes_sent, - referer, - user_agent); - } - - /* Here we have a log message in log_buf. Call the callback */ - if (conn->phys_ctx->callbacks.log_access) { - if (conn->phys_ctx->callbacks.log_access(conn, log_buf)) { - /* do not log if callback returns non-zero */ - if (fi.access.fp) { - mg_fclose(&fi.access); - } - return; - } - } - - /* Store in file */ - if (fi.access.fp) { - int ok = 1; - flockfile(fi.access.fp); - if (fprintf(fi.access.fp, "%s\n", log_buf) < 1) { - ok = 0; - } - if (fflush(fi.access.fp) != 0) { - ok = 0; - } - funlockfile(fi.access.fp); - if (mg_fclose(&fi.access) != 0) { - ok = 0; - } - if (!ok) { - mg_cry_internal(conn, - "Error writing log file %s", - conn->dom_ctx->config[ACCESS_LOG_FILE]); - } - } -} -#else -#error "Either enable filesystems or provide a custom log_access implementation" -#endif /* Externally provided function */ - - -/* Verify given socket address against the ACL. - * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. - */ -static int -check_acl(struct mg_context *phys_ctx, const union usa *sa) -{ - int allowed, flag, matched; - struct vec vec; - - if (phys_ctx) { - const char *list = phys_ctx->dd.config[ACCESS_CONTROL_LIST]; - - /* If any ACL is set, deny by default */ - allowed = (list == NULL) ? '+' : '-'; - - while ((list = next_option(list, &vec, NULL)) != NULL) { - flag = vec.ptr[0]; - matched = -1; - if ((vec.len > 0) && ((flag == '+') || (flag == '-'))) { - vec.ptr++; - vec.len--; - matched = parse_match_net(&vec, sa, 1); - } - if (matched < 0) { - mg_cry_ctx_internal(phys_ctx, - "%s: subnet must be [+|-]IP-addr[/x]", - __func__); - return -1; - } - if (matched) { - allowed = flag; - } - } - - return allowed == '+'; - } - return -1; -} - - -#if !defined(_WIN32) && !defined(__ZEPHYR__) -static int -set_uid_option(struct mg_context *phys_ctx) -{ - int success = 0; - - if (phys_ctx) { - /* We are currently running as curr_uid. */ - const uid_t curr_uid = getuid(); - /* If set, we want to run as run_as_user. */ - const char *run_as_user = phys_ctx->dd.config[RUN_AS_USER]; - const struct passwd *to_pw = NULL; - - if ((run_as_user != NULL) && (to_pw = getpwnam(run_as_user)) == NULL) { - /* run_as_user does not exist on the system. We can't proceed - * further. */ - mg_cry_ctx_internal(phys_ctx, - "%s: unknown user [%s]", - __func__, - run_as_user); - } else if ((run_as_user == NULL) || (curr_uid == to_pw->pw_uid)) { - /* There was either no request to change user, or we're already - * running as run_as_user. Nothing else to do. - */ - success = 1; - } else { - /* Valid change request. */ - if (setgid(to_pw->pw_gid) == -1) { - mg_cry_ctx_internal(phys_ctx, - "%s: setgid(%s): %s", - __func__, - run_as_user, - strerror(errno)); - } else if (setgroups(0, NULL) == -1) { - mg_cry_ctx_internal(phys_ctx, - "%s: setgroups(): %s", - __func__, - strerror(errno)); - } else if (setuid(to_pw->pw_uid) == -1) { - mg_cry_ctx_internal(phys_ctx, - "%s: setuid(%s): %s", - __func__, - run_as_user, - strerror(errno)); - } else { - success = 1; - } - } - } - - return success; -} -#endif /* !_WIN32 */ - - -static void -tls_dtor(void *key) -{ - struct mg_workerTLS *tls = (struct mg_workerTLS *)key; - /* key == pthread_getspecific(sTlsKey); */ - - if (tls) { - if (tls->is_master == 2) { - tls->is_master = -3; /* Mark memory as dead */ - mg_free(tls); - } - } - pthread_setspecific(sTlsKey, NULL); -} - - -#if defined(USE_MBEDTLS) -/* Check if SSL is required. - * If so, set up ctx->ssl_ctx pointer. */ -static int -mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) -{ - if (!phys_ctx) { - return 0; - } - - if (!dom_ctx) { - dom_ctx = &(phys_ctx->dd); - } - - if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) { - /* No SSL port is set. No need to setup SSL. */ - return 1; - } - - dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx)); - if (dom_ctx->ssl_ctx == NULL) { - fprintf(stderr, "ssl_ctx malloc failed\n"); - return 0; - } - - return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE]) - == 0 - ? 1 - : 0; -} - -#elif defined(USE_GNUTLS) -/* Check if SSL is required. - * If so, set up ctx->ssl_ctx pointer. */ -static int -mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) -{ - if (!phys_ctx) { - return 0; - } - - if (!dom_ctx) { - dom_ctx = &(phys_ctx->dd); - } - - if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) { - /* No SSL port is set. No need to setup SSL. */ - return 1; - } - - dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx)); - if (dom_ctx->ssl_ctx == NULL) { - fprintf(stderr, "ssl_ctx malloc failed\n"); - return 0; - } - - return gtls_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE]) - == 0 - ? 1 - : 0; -} - -#elif !defined(NO_SSL) - -static int ssl_use_pem_file(struct mg_context *phys_ctx, - struct mg_domain_context *dom_ctx, - const char *pem, - const char *chain); -static const char *ssl_error(void); - - -static int -refresh_trust(struct mg_connection *conn) -{ - struct stat cert_buf; - int64_t t = 0; - const char *pem; - const char *chain; - int should_verify_peer; - - if ((pem = conn->dom_ctx->config[SSL_CERTIFICATE]) == NULL) { - /* If pem is NULL and conn->phys_ctx->callbacks.init_ssl is not, - * refresh_trust still can not work. */ - return 0; - } - chain = conn->dom_ctx->config[SSL_CERTIFICATE_CHAIN]; - if (chain == NULL) { - /* pem is not NULL here */ - chain = pem; - } - if (*chain == 0) { - chain = NULL; - } - - if (stat(pem, &cert_buf) != -1) { - t = (int64_t)cert_buf.st_mtime; - } - - mg_lock_context(conn->phys_ctx); - if ((t != 0) && (conn->dom_ctx->ssl_cert_last_mtime != t)) { - conn->dom_ctx->ssl_cert_last_mtime = t; - - should_verify_peer = 0; - if (conn->dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) { - if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER], "yes") - == 0) { - should_verify_peer = 1; - } else if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER], - "optional") - == 0) { - should_verify_peer = 1; - } - } - - if (should_verify_peer) { - char *ca_path = conn->dom_ctx->config[SSL_CA_PATH]; - char *ca_file = conn->dom_ctx->config[SSL_CA_FILE]; - if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx, - ca_file, - ca_path) - != 1) { - mg_unlock_context(conn->phys_ctx); - mg_cry_ctx_internal( - conn->phys_ctx, - "SSL_CTX_load_verify_locations error: %s " - "ssl_verify_peer requires setting " - "either ssl_ca_path or ssl_ca_file. Is any of them " - "present in " - "the .conf file?", - ssl_error()); - return 0; - } - } - - if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain) == 0) { - mg_unlock_context(conn->phys_ctx); - return 0; - } - } - mg_unlock_context(conn->phys_ctx); - - return 1; -} - -#if defined(OPENSSL_API_1_1) -#else -static pthread_mutex_t *ssl_mutexes; -#endif /* OPENSSL_API_1_1 */ - -static int -sslize(struct mg_connection *conn, - int (*func)(SSL *), - const struct mg_client_options *client_options) -{ - int ret, err; - int short_trust; - unsigned timeout = 1024; - unsigned i; - - if (!conn) { - return 0; - } - - short_trust = - (conn->dom_ctx->config[SSL_SHORT_TRUST] != NULL) - && (mg_strcasecmp(conn->dom_ctx->config[SSL_SHORT_TRUST], "yes") == 0); - - if (short_trust) { - int trust_ret = refresh_trust(conn); - if (!trust_ret) { - return trust_ret; - } - } - - mg_lock_context(conn->phys_ctx); - conn->ssl = SSL_new(conn->dom_ctx->ssl_ctx); - mg_unlock_context(conn->phys_ctx); - if (conn->ssl == NULL) { - mg_cry_internal(conn, "sslize error: %s", ssl_error()); - OPENSSL_REMOVE_THREAD_STATE(); - return 0; - } - SSL_set_app_data(conn->ssl, (char *)conn); - - ret = SSL_set_fd(conn->ssl, conn->client.sock); - if (ret != 1) { - mg_cry_internal(conn, "sslize error: %s", ssl_error()); - SSL_free(conn->ssl); - conn->ssl = NULL; - OPENSSL_REMOVE_THREAD_STATE(); - return 0; - } - - if (client_options) { - if (client_options->host_name) { - SSL_set_tlsext_host_name(conn->ssl, client_options->host_name); - } - } - - /* Reuse the request timeout for the SSL_Accept/SSL_connect timeout */ - if (conn->dom_ctx->config[REQUEST_TIMEOUT]) { - /* NOTE: The loop below acts as a back-off, so we can end - * up sleeping for more (or less) than the REQUEST_TIMEOUT. */ - int to = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]); - if (to >= 0) { - timeout = (unsigned)to; - } - } - - /* SSL functions may fail and require to be called again: - * see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html - * Here "func" could be SSL_connect or SSL_accept. */ - for (i = 0; i <= timeout; i += 50) { - ERR_clear_error(); - /* conn->dom_ctx may be changed here (see ssl_servername_callback) */ - ret = func(conn->ssl); - if (ret != 1) { - err = SSL_get_error(conn->ssl, ret); - if ((err == SSL_ERROR_WANT_CONNECT) - || (err == SSL_ERROR_WANT_ACCEPT) - || (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE) - || (err == SSL_ERROR_WANT_X509_LOOKUP)) { - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { - /* Don't wait if the server is going to be stopped. */ - break; - } - if (err == SSL_ERROR_WANT_X509_LOOKUP) { - /* Simply retry the function call. */ - mg_sleep(50); - } else { - /* Need to retry the function call "later". - * See https://linux.die.net/man/3/ssl_get_error - * This is typical for non-blocking sockets. */ - struct mg_pollfd pfd[2]; - int pollres; - unsigned int num_sock = 1; - pfd[0].fd = conn->client.sock; - pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT) - || (err == SSL_ERROR_WANT_WRITE)) - ? POLLOUT - : POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - pfd[num_sock].fd = - conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - - pollres = mg_poll(pfd, - num_sock, - 50, - &(conn->phys_ctx->stop_flag)); - if (pollres < 0) { - /* Break if error occurred (-1) - * or server shutdown (-2) */ - break; - } - } - - } else if (err == SSL_ERROR_SYSCALL) { - /* This is an IO error. Look at errno. */ - mg_cry_internal(conn, "SSL syscall error %i", ERRNO); - break; - - } else { - /* This is an SSL specific error, e.g. SSL_ERROR_SSL */ - mg_cry_internal(conn, "sslize error: %s", ssl_error()); - break; - } - - } else { - /* success */ - break; - } - } - ERR_clear_error(); - - if (ret != 1) { - SSL_free(conn->ssl); - conn->ssl = NULL; - OPENSSL_REMOVE_THREAD_STATE(); - return 0; - } - - return 1; -} - - -/* Return OpenSSL error message (from CRYPTO lib) */ -static const char * -ssl_error(void) -{ - unsigned long err; - err = ERR_get_error(); - return ((err == 0) ? "" : ERR_error_string(err, NULL)); -} - - -static int -hexdump2string(void *mem, int memlen, char *buf, int buflen) -{ - int i; - const char hexdigit[] = "0123456789abcdef"; - - if ((memlen <= 0) || (buflen <= 0)) { - return 0; - } - if (buflen < (3 * memlen)) { - return 0; - } - - for (i = 0; i < memlen; i++) { - if (i > 0) { - buf[3 * i - 1] = ' '; - } - buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4) & 0xF]; - buf[3 * i + 1] = hexdigit[((uint8_t *)mem)[i] & 0xF]; - } - buf[3 * memlen - 1] = 0; - - return 1; -} - - -static int -ssl_get_client_cert_info(const struct mg_connection *conn, - struct mg_client_cert *client_cert) -{ - X509 *cert = SSL_get_peer_certificate(conn->ssl); - if (cert) { - char str_buf[1024]; - unsigned char buf[256]; - char *str_serial = NULL; - unsigned int ulen; - int ilen; - unsigned char *tmp_buf; - unsigned char *tmp_p; - - /* Handle to algorithm used for fingerprint */ - const EVP_MD *digest = EVP_get_digestbyname("sha1"); - - /* Get Subject and issuer */ - X509_NAME *subj = X509_get_subject_name(cert); - X509_NAME *iss = X509_get_issuer_name(cert); - - /* Get serial number */ - ASN1_INTEGER *serial = X509_get_serialNumber(cert); - - /* Translate serial number to a hex string */ - BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial, NULL); - if (serial_bn) { - str_serial = BN_bn2hex(serial_bn); - BN_free(serial_bn); - } - client_cert->serial = - str_serial ? mg_strdup_ctx(str_serial, conn->phys_ctx) : NULL; - - /* Translate subject and issuer to a string */ - (void)X509_NAME_oneline(subj, str_buf, (int)sizeof(str_buf)); - client_cert->subject = mg_strdup_ctx(str_buf, conn->phys_ctx); - (void)X509_NAME_oneline(iss, str_buf, (int)sizeof(str_buf)); - client_cert->issuer = mg_strdup_ctx(str_buf, conn->phys_ctx); - - /* Calculate SHA1 fingerprint and store as a hex string */ - ulen = 0; - - /* ASN1_digest is deprecated. Do the calculation manually, - * using EVP_Digest. */ - ilen = i2d_X509(cert, NULL); - tmp_buf = (ilen > 0) - ? (unsigned char *)mg_malloc_ctx((unsigned)ilen + 1, - conn->phys_ctx) - : NULL; - if (tmp_buf) { - tmp_p = tmp_buf; - (void)i2d_X509(cert, &tmp_p); - if (!EVP_Digest( - tmp_buf, (unsigned)ilen, buf, &ulen, digest, NULL)) { - ulen = 0; - } - mg_free(tmp_buf); - } - - if (!hexdump2string(buf, (int)ulen, str_buf, (int)sizeof(str_buf))) { - *str_buf = 0; - } - client_cert->finger = mg_strdup_ctx(str_buf, conn->phys_ctx); - - client_cert->peer_cert = (void *)cert; - - /* Strings returned from bn_bn2hex must be freed using OPENSSL_free, - * see https://linux.die.net/man/3/bn_bn2hex */ - OPENSSL_free(str_serial); - return 1; - } - return 0; -} - - -#if defined(OPENSSL_API_1_1) -#else -static void -ssl_locking_callback(int mode, int mutex_num, const char *file, int line) -{ - (void)line; - (void)file; - - if (mode & 1) { - /* 1 is CRYPTO_LOCK */ - (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]); - } else { - (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]); - } -} -#endif /* OPENSSL_API_1_1 */ - - -#if !defined(NO_SSL_DL) -/* Load a DLL/Shared Object with a TLS/SSL implementation. */ -static void * -load_tls_dll(char *ebuf, - size_t ebuf_len, - const char *dll_name, - struct ssl_func *sw, - int *feature_missing) -{ - union { - void *p; - void (*fp)(void); - } u; - void *dll_handle; - struct ssl_func *fp; - int ok; - int truncated = 0; - - if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) { - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s: cannot load %s", - __func__, - dll_name); - return NULL; - } - - ok = 1; - for (fp = sw; fp->name != NULL; fp++) { -#if defined(_WIN32) - /* GetProcAddress() returns pointer to function */ - u.fp = (void (*)(void))dlsym(dll_handle, fp->name); -#else - /* dlsym() on UNIX returns void *. ISO C forbids casts of data - * pointers to function pointers. We need to use a union to make a - * cast. */ - u.p = dlsym(dll_handle, fp->name); -#endif /* _WIN32 */ - - /* Set pointer (might be NULL) */ - fp->ptr = u.fp; - - if (u.fp == NULL) { - DEBUG_TRACE("Missing function: %s\n", fp->name); - if (feature_missing) { - feature_missing[fp->required]++; - } - if (fp->required == TLS_Mandatory) { - /* Mandatory function is missing */ - if (ok) { - /* This is the first missing function. - * Create a new error message. */ - mg_snprintf(NULL, - &truncated, - ebuf, - ebuf_len, - "%s: %s: cannot find %s", - __func__, - dll_name, - fp->name); - ok = 0; - } else { - /* This is yet anothermissing function. - * Append existing error message. */ - size_t cur_len = strlen(ebuf); - if (!truncated && ((ebuf_len - cur_len) > 3)) { - mg_snprintf(NULL, - &truncated, - ebuf + cur_len, - ebuf_len - cur_len - 3, - ", %s", - fp->name); - if (truncated) { - /* If truncated, add "..." */ - strcat(ebuf, "..."); - } - } - } - } - } - } - - if (!ok) { - (void)dlclose(dll_handle); - return NULL; - } - - return dll_handle; -} - - -static void *ssllib_dll_handle; /* Store the ssl library handle. */ -static void *cryptolib_dll_handle; /* Store the crypto library handle. */ - -#endif /* NO_SSL_DL */ - - -#if defined(SSL_ALREADY_INITIALIZED) -static volatile ptrdiff_t cryptolib_users = - 1; /* Reference counter for crypto library. */ -#else -static volatile ptrdiff_t cryptolib_users = - 0; /* Reference counter for crypto library. */ -#endif - - -static int -initialize_openssl(char *ebuf, size_t ebuf_len) -{ -#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0) - int i, num_locks; - size_t size; -#endif - - if (ebuf_len > 0) { - ebuf[0] = 0; - } - -#if !defined(NO_SSL_DL) - if (!cryptolib_dll_handle) { - memset(tls_feature_missing, 0, sizeof(tls_feature_missing)); - cryptolib_dll_handle = load_tls_dll( - ebuf, ebuf_len, CRYPTO_LIB, crypto_sw, tls_feature_missing); - if (!cryptolib_dll_handle) { - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s: error loading library %s", - __func__, - CRYPTO_LIB); - DEBUG_TRACE("%s", ebuf); - return 0; - } - } -#endif /* NO_SSL_DL */ - - if (mg_atomic_inc(&cryptolib_users) > 1) { - return 1; - } - -#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0) - /* Initialize locking callbacks, needed for thread safety. - * http://www.openssl.org/support/faq.html#PROG1 - */ - num_locks = CRYPTO_num_locks(); - if (num_locks < 0) { - num_locks = 0; - } - size = sizeof(pthread_mutex_t) * ((size_t)(num_locks)); - - /* allocate mutex array, if required */ - if (num_locks == 0) { - /* No mutex array required */ - ssl_mutexes = NULL; - } else { - /* Mutex array required - allocate it */ - ssl_mutexes = (pthread_mutex_t *)mg_malloc(size); - - /* Check OOM */ - if (ssl_mutexes == NULL) { - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s: cannot allocate mutexes: %s", - __func__, - ssl_error()); - DEBUG_TRACE("%s", ebuf); - return 0; - } - - /* initialize mutex array */ - for (i = 0; i < num_locks; i++) { - if (0 != pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr)) { - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s: error initializing mutex %i of %i", - __func__, - i, - num_locks); - DEBUG_TRACE("%s", ebuf); - mg_free(ssl_mutexes); - return 0; - } - } - } - - CRYPTO_set_locking_callback(&ssl_locking_callback); - CRYPTO_set_id_callback(&mg_current_thread_id); -#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */ - -#if !defined(NO_SSL_DL) - if (!ssllib_dll_handle) { - ssllib_dll_handle = - load_tls_dll(ebuf, ebuf_len, SSL_LIB, ssl_sw, tls_feature_missing); - if (!ssllib_dll_handle) { -#if !defined(OPENSSL_API_1_1) - mg_free(ssl_mutexes); -#endif - DEBUG_TRACE("%s", ebuf); - return 0; - } - } -#endif /* NO_SSL_DL */ - -#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \ - && !defined(NO_SSL_DL) - /* Initialize SSL library */ - OPENSSL_init_ssl(0, NULL); - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS - | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, - NULL); -#else - /* Initialize SSL library */ - SSL_library_init(); - SSL_load_error_strings(); -#endif - - return 1; -} - - -static int -ssl_use_pem_file(struct mg_context *phys_ctx, - struct mg_domain_context *dom_ctx, - const char *pem, - const char *chain) -{ - if (SSL_CTX_use_certificate_file(dom_ctx->ssl_ctx, pem, 1) == 0) { - mg_cry_ctx_internal(phys_ctx, - "%s: cannot open certificate file %s: %s", - __func__, - pem, - ssl_error()); - return 0; - } - - /* could use SSL_CTX_set_default_passwd_cb_userdata */ - if (SSL_CTX_use_PrivateKey_file(dom_ctx->ssl_ctx, pem, 1) == 0) { - mg_cry_ctx_internal(phys_ctx, - "%s: cannot open private key file %s: %s", - __func__, - pem, - ssl_error()); - return 0; - } - - if (SSL_CTX_check_private_key(dom_ctx->ssl_ctx) == 0) { - mg_cry_ctx_internal(phys_ctx, - "%s: certificate and private key do not match: %s", - __func__, - pem); - return 0; - } - - /* In contrast to OpenSSL, wolfSSL does not support certificate - * chain files that contain private keys and certificates in - * SSL_CTX_use_certificate_chain_file. - * The CivetWeb-Server used pem-Files that contained both information. - * In order to make wolfSSL work, it is split in two files. - * One file that contains key and certificate used by the server and - * an optional chain file for the ssl stack. - */ - if (chain) { - if (SSL_CTX_use_certificate_chain_file(dom_ctx->ssl_ctx, chain) == 0) { - mg_cry_ctx_internal(phys_ctx, - "%s: cannot use certificate chain file %s: %s", - __func__, - chain, - ssl_error()); - return 0; - } - } - return 1; -} - - -#if defined(OPENSSL_API_1_1) -static unsigned long -ssl_get_protocol(int version_id) -{ - long unsigned ret = (long unsigned)SSL_OP_ALL; - if (version_id > 0) - ret |= SSL_OP_NO_SSLv2; - if (version_id > 1) - ret |= SSL_OP_NO_SSLv3; - if (version_id > 2) - ret |= SSL_OP_NO_TLSv1; - if (version_id > 3) - ret |= SSL_OP_NO_TLSv1_1; - if (version_id > 4) - ret |= SSL_OP_NO_TLSv1_2; -#if defined(SSL_OP_NO_TLSv1_3) - if (version_id > 5) - ret |= SSL_OP_NO_TLSv1_3; -#endif - return ret; -} -#else -static long -ssl_get_protocol(int version_id) -{ - unsigned long ret = (unsigned long)SSL_OP_ALL; - if (version_id > 0) - ret |= SSL_OP_NO_SSLv2; - if (version_id > 1) - ret |= SSL_OP_NO_SSLv3; - if (version_id > 2) - ret |= SSL_OP_NO_TLSv1; - if (version_id > 3) - ret |= SSL_OP_NO_TLSv1_1; - if (version_id > 4) - ret |= SSL_OP_NO_TLSv1_2; -#if defined(SSL_OP_NO_TLSv1_3) - if (version_id > 5) - ret |= SSL_OP_NO_TLSv1_3; -#endif - return (long)ret; -} -#endif /* OPENSSL_API_1_1 */ - - -/* SSL callback documentation: - * https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_info_callback.html - * https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_info_callback(3) - * https://linux.die.net/man/3/ssl_set_info_callback */ -/* Note: There is no "const" for the first argument in the documentation - * examples, however some (maybe most, but not all) headers of OpenSSL - * versions / OpenSSL compatibility layers have it. Having a different - * definition will cause a warning in C and an error in C++. Use "const SSL - * *", while automatic conversion from "SSL *" works for all compilers, - * but not other way around */ -static void -ssl_info_callback(const SSL *ssl, int what, int ret) -{ - (void)ret; - - if (what & SSL_CB_HANDSHAKE_START) { - SSL_get_app_data(ssl); - } - if (what & SSL_CB_HANDSHAKE_DONE) { - /* TODO: check for openSSL 1.1 */ - //#define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001 - // ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; - } -} - - -static int -ssl_servername_callback(SSL *ssl, int *ad, void *arg) -{ -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" -#endif /* defined(GCC_DIAGNOSTIC) */ - - /* We used an aligned pointer in SSL_set_app_data */ - struct mg_connection *conn = (struct mg_connection *)SSL_get_app_data(ssl); - -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic pop -#endif /* defined(GCC_DIAGNOSTIC) */ - - const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - - (void)ad; - (void)arg; - - if ((conn == NULL) || (conn->phys_ctx == NULL)) { - DEBUG_ASSERT(0); - return SSL_TLSEXT_ERR_NOACK; - } - conn->dom_ctx = &(conn->phys_ctx->dd); - - /* Old clients (Win XP) will not support SNI. Then, there - * is no server name available in the request - we can - * only work with the default certificate. - * Multiple HTTPS hosts on one IP+port are only possible - * with a certificate containing all alternative names. - */ - if ((servername == NULL) || (*servername == 0)) { - DEBUG_TRACE("%s", "SSL connection not supporting SNI"); - mg_lock_context(conn->phys_ctx); - SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx); - mg_unlock_context(conn->phys_ctx); - return SSL_TLSEXT_ERR_NOACK; - } - - DEBUG_TRACE("TLS connection to host %s", servername); - - while (conn->dom_ctx) { - if (!mg_strcasecmp(servername, - conn->dom_ctx->config[AUTHENTICATION_DOMAIN])) { - /* Found matching domain */ - DEBUG_TRACE("TLS domain %s found", - conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); - break; - } - mg_lock_context(conn->phys_ctx); - conn->dom_ctx = conn->dom_ctx->next; - mg_unlock_context(conn->phys_ctx); - } - - if (conn->dom_ctx == NULL) { - /* Default domain */ - DEBUG_TRACE("TLS default domain %s used", - conn->phys_ctx->dd.config[AUTHENTICATION_DOMAIN]); - conn->dom_ctx = &(conn->phys_ctx->dd); - } - mg_lock_context(conn->phys_ctx); - SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx); - mg_unlock_context(conn->phys_ctx); - return SSL_TLSEXT_ERR_OK; -} - - -#if defined(USE_ALPN) -static const char alpn_proto_list[] = "\x02h2\x08http/1.1\x08http/1.0"; -static const char *alpn_proto_order_http1[] = {alpn_proto_list + 3, - alpn_proto_list + 3 + 8, - NULL}; -#if defined(USE_HTTP2) -static const char *alpn_proto_order_http2[] = {alpn_proto_list, - alpn_proto_list + 3, - alpn_proto_list + 3 + 8, - NULL}; -#endif - -static int -alpn_select_cb(SSL *ssl, - const unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg) -{ - struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg; - unsigned int i, j, enable_http2 = 0; - const char **alpn_proto_order = alpn_proto_order_http1; - - struct mg_workerTLS *tls = - (struct mg_workerTLS *)pthread_getspecific(sTlsKey); - - (void)ssl; - - if (tls == NULL) { - /* Need to store protocol in Thread Local Storage */ - /* If there is no Thread Local Storage, don't use ALPN */ - return SSL_TLSEXT_ERR_NOACK; - } - -#if defined(USE_HTTP2) - enable_http2 = (0 == strcmp(dom_ctx->config[ENABLE_HTTP2], "yes")); - if (enable_http2) { - alpn_proto_order = alpn_proto_order_http2; - } -#endif - - for (j = 0; alpn_proto_order[j] != NULL; j++) { - /* check all accepted protocols in this order */ - const char *alpn_proto = alpn_proto_order[j]; - /* search input for matching protocol */ - for (i = 0; i < inlen; i++) { - if (!memcmp(in + i, alpn_proto, (unsigned char)alpn_proto[0])) { - *out = in + i + 1; - *outlen = in[i]; - tls->alpn_proto = alpn_proto; - return SSL_TLSEXT_ERR_OK; - } - } - } - - /* Nothing found */ - return SSL_TLSEXT_ERR_NOACK; -} - - -static int -next_protos_advertised_cb(SSL *ssl, - const unsigned char **data, - unsigned int *len, - void *arg) -{ - struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg; - *data = (const unsigned char *)alpn_proto_list; - *len = (unsigned int)strlen((const char *)data); - - (void)ssl; - (void)dom_ctx; - - return SSL_TLSEXT_ERR_OK; -} - - -static int -init_alpn(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) -{ - unsigned int alpn_len = (unsigned int)strlen((char *)alpn_proto_list); - int ret = SSL_CTX_set_alpn_protos(dom_ctx->ssl_ctx, - (const unsigned char *)alpn_proto_list, - alpn_len); - if (ret != 0) { - mg_cry_ctx_internal(phys_ctx, - "SSL_CTX_set_alpn_protos error: %s", - ssl_error()); - } - - SSL_CTX_set_alpn_select_cb(dom_ctx->ssl_ctx, - alpn_select_cb, - (void *)dom_ctx); - - SSL_CTX_set_next_protos_advertised_cb(dom_ctx->ssl_ctx, - next_protos_advertised_cb, - (void *)dom_ctx); - - return ret; -} -#endif - - -/* Setup SSL CTX as required by CivetWeb */ -static int -init_ssl_ctx_impl(struct mg_context *phys_ctx, - struct mg_domain_context *dom_ctx, - const char *pem, - const char *chain) -{ - int callback_ret; - int should_verify_peer; - int peer_certificate_optional; - const char *ca_path; - const char *ca_file; - int use_default_verify_paths; - int verify_depth; - struct timespec now_mt; - md5_byte_t ssl_context_id[16]; - md5_state_t md5state; - int protocol_ver; - int ssl_cache_timeout; - -#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \ - && !defined(NO_SSL_DL) - if ((dom_ctx->ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) { - mg_cry_ctx_internal(phys_ctx, - "SSL_CTX_new (server) error: %s", - ssl_error()); - return 0; - } -#else - if ((dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { - mg_cry_ctx_internal(phys_ctx, - "SSL_CTX_new (server) error: %s", - ssl_error()); - return 0; - } -#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */ - -#if defined(SSL_OP_NO_TLSv1_3) - SSL_CTX_clear_options(dom_ctx->ssl_ctx, - SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 - | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 - | SSL_OP_NO_TLSv1_3); -#else - SSL_CTX_clear_options(dom_ctx->ssl_ctx, - SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 - | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); -#endif - - protocol_ver = atoi(dom_ctx->config[SSL_PROTOCOL_VERSION]); - SSL_CTX_set_options(dom_ctx->ssl_ctx, ssl_get_protocol(protocol_ver)); - SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - SSL_CTX_set_options(dom_ctx->ssl_ctx, - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_COMPRESSION); - -#if defined(SSL_OP_NO_RENEGOTIATION) - SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_RENEGOTIATION); -#endif - -#if !defined(NO_SSL_DL) - SSL_CTX_set_ecdh_auto(dom_ctx->ssl_ctx, 1); -#endif /* NO_SSL_DL */ - - /* In SSL documentation examples callback defined without const - * specifier 'void (*)(SSL *, int, int)' See: - * https://www.openssl.org/docs/man1.0.2/ssl/ssl.html - * https://www.openssl.org/docs/man1.1.0/ssl/ssl.html - * But in the source code const SSL is used: - * 'void (*)(const SSL *, int, int)' See: - * https://github.com/openssl/openssl/blob/1d97c8435171a7af575f73c526d79e1ef0ee5960/ssl/ssl.h#L1173 - * Problem about wrong documentation described, but not resolved: - * https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1147526 - * Wrong const cast ignored on C or can be suppressed by compiler flags. - * But when compiled with modern C++ compiler, correct const should be - * provided - */ - SSL_CTX_set_info_callback(dom_ctx->ssl_ctx, ssl_info_callback); - - SSL_CTX_set_tlsext_servername_callback(dom_ctx->ssl_ctx, - ssl_servername_callback); - - /* If a callback has been specified, call it. */ - callback_ret = (phys_ctx->callbacks.init_ssl == NULL) - ? 0 - : (phys_ctx->callbacks.init_ssl(dom_ctx->ssl_ctx, - phys_ctx->user_data)); - - /* If callback returns 0, civetweb sets up the SSL certificate. - * If it returns 1, civetweb assumes the callback already did this. - * If it returns -1, initializing ssl fails. */ - if (callback_ret < 0) { - mg_cry_ctx_internal(phys_ctx, - "SSL callback returned error: %i", - callback_ret); - return 0; - } - if (callback_ret > 0) { - /* Callback did everything. */ - return 1; - } - - /* If a domain callback has been specified, call it. */ - callback_ret = (phys_ctx->callbacks.init_ssl_domain == NULL) - ? 0 - : (phys_ctx->callbacks.init_ssl_domain( - dom_ctx->config[AUTHENTICATION_DOMAIN], - dom_ctx->ssl_ctx, - phys_ctx->user_data)); - - /* If domain callback returns 0, civetweb sets up the SSL certificate. - * If it returns 1, civetweb assumes the callback already did this. - * If it returns -1, initializing ssl fails. */ - if (callback_ret < 0) { - mg_cry_ctx_internal(phys_ctx, - "Domain SSL callback returned error: %i", - callback_ret); - return 0; - } - if (callback_ret > 0) { - /* Domain callback did everything. */ - return 1; - } - - /* Use some combination of start time, domain and port as a SSL - * context ID. This should be unique on the current machine. */ - md5_init(&md5state); - clock_gettime(CLOCK_MONOTONIC, &now_mt); - md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt)); - md5_append(&md5state, - (const md5_byte_t *)phys_ctx->dd.config[LISTENING_PORTS], - strlen(phys_ctx->dd.config[LISTENING_PORTS])); - md5_append(&md5state, - (const md5_byte_t *)dom_ctx->config[AUTHENTICATION_DOMAIN], - strlen(dom_ctx->config[AUTHENTICATION_DOMAIN])); - md5_append(&md5state, (const md5_byte_t *)phys_ctx, sizeof(*phys_ctx)); - md5_append(&md5state, (const md5_byte_t *)dom_ctx, sizeof(*dom_ctx)); - md5_finish(&md5state, ssl_context_id); - - SSL_CTX_set_session_id_context(dom_ctx->ssl_ctx, - (unsigned char *)ssl_context_id, - sizeof(ssl_context_id)); - - if (pem != NULL) { - if (!ssl_use_pem_file(phys_ctx, dom_ctx, pem, chain)) { - return 0; - } - } - - /* Should we support client certificates? */ - /* Default is "no". */ - should_verify_peer = 0; - peer_certificate_optional = 0; - if (dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) { - if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0) { - /* Yes, they are mandatory */ - should_verify_peer = 1; - } else if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER], - "optional") - == 0) { - /* Yes, they are optional */ - should_verify_peer = 1; - peer_certificate_optional = 1; - } - } - - use_default_verify_paths = - (dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL) - && (mg_strcasecmp(dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes") - == 0); - - if (should_verify_peer) { - ca_path = dom_ctx->config[SSL_CA_PATH]; - ca_file = dom_ctx->config[SSL_CA_FILE]; - if (SSL_CTX_load_verify_locations(dom_ctx->ssl_ctx, ca_file, ca_path) - != 1) { - mg_cry_ctx_internal(phys_ctx, - "SSL_CTX_load_verify_locations error: %s " - "ssl_verify_peer requires setting " - "either ssl_ca_path or ssl_ca_file. " - "Is any of them present in the " - ".conf file?", - ssl_error()); - return 0; - } - - if (peer_certificate_optional) { - SSL_CTX_set_verify(dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); - } else { - SSL_CTX_set_verify(dom_ctx->ssl_ctx, - SSL_VERIFY_PEER - | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - NULL); - } - - if (use_default_verify_paths - && (SSL_CTX_set_default_verify_paths(dom_ctx->ssl_ctx) != 1)) { - mg_cry_ctx_internal(phys_ctx, - "SSL_CTX_set_default_verify_paths error: %s", - ssl_error()); - return 0; - } - - if (dom_ctx->config[SSL_VERIFY_DEPTH]) { - verify_depth = atoi(dom_ctx->config[SSL_VERIFY_DEPTH]); - SSL_CTX_set_verify_depth(dom_ctx->ssl_ctx, verify_depth); - } - } - - if (dom_ctx->config[SSL_CIPHER_LIST] != NULL) { - if (SSL_CTX_set_cipher_list(dom_ctx->ssl_ctx, - dom_ctx->config[SSL_CIPHER_LIST]) - != 1) { - mg_cry_ctx_internal(phys_ctx, - "SSL_CTX_set_cipher_list error: %s", - ssl_error()); - } - } - - /* SSL session caching */ - ssl_cache_timeout = ((dom_ctx->config[SSL_CACHE_TIMEOUT] != NULL) - ? atoi(dom_ctx->config[SSL_CACHE_TIMEOUT]) - : 0); - if (ssl_cache_timeout > 0) { - SSL_CTX_set_session_cache_mode(dom_ctx->ssl_ctx, SSL_SESS_CACHE_BOTH); - /* SSL_CTX_sess_set_cache_size(dom_ctx->ssl_ctx, 10000); ... use - * default */ - SSL_CTX_set_timeout(dom_ctx->ssl_ctx, (long)ssl_cache_timeout); - } - -#if defined(USE_ALPN) - /* Initialize ALPN only of TLS library (OpenSSL version) supports ALPN */ -#if !defined(NO_SSL_DL) - if (!tls_feature_missing[TLS_ALPN]) -#endif - { - init_alpn(phys_ctx, dom_ctx); - } -#endif - - return 1; -} - - -/* Check if SSL is required. - * If so, dynamically load SSL library - * and set up ctx->ssl_ctx pointer. */ -static int -init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) -{ - void *ssl_ctx = 0; - int callback_ret; - const char *pem; - const char *chain; - char ebuf[128]; - - if (!phys_ctx) { - return 0; - } - - if (!dom_ctx) { - dom_ctx = &(phys_ctx->dd); - } - - if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) { - /* No SSL port is set. No need to setup SSL. */ - return 1; - } - - /* Check for external SSL_CTX */ - callback_ret = - (phys_ctx->callbacks.external_ssl_ctx == NULL) - ? 0 - : (phys_ctx->callbacks.external_ssl_ctx(&ssl_ctx, - phys_ctx->user_data)); - - if (callback_ret < 0) { - /* Callback exists and returns <0: Initializing failed. */ - mg_cry_ctx_internal(phys_ctx, - "external_ssl_ctx callback returned error: %i", - callback_ret); - return 0; - } else if (callback_ret > 0) { - /* Callback exists and returns >0: Initializing complete, - * civetweb should not modify the SSL context. */ - dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx; - if (!initialize_openssl(ebuf, sizeof(ebuf))) { - mg_cry_ctx_internal(phys_ctx, "%s", ebuf); - return 0; - } - return 1; - } - /* If the callback does not exist or return 0, civetweb must initialize - * the SSL context. Handle "domain" callback next. */ - - /* Check for external domain SSL_CTX callback. */ - callback_ret = (phys_ctx->callbacks.external_ssl_ctx_domain == NULL) - ? 0 - : (phys_ctx->callbacks.external_ssl_ctx_domain( - dom_ctx->config[AUTHENTICATION_DOMAIN], - &ssl_ctx, - phys_ctx->user_data)); - - if (callback_ret < 0) { - /* Callback < 0: Error. Abort init. */ - mg_cry_ctx_internal( - phys_ctx, - "external_ssl_ctx_domain callback returned error: %i", - callback_ret); - return 0; - } else if (callback_ret > 0) { - /* Callback > 0: Consider init done. */ - dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx; - if (!initialize_openssl(ebuf, sizeof(ebuf))) { - mg_cry_ctx_internal(phys_ctx, "%s", ebuf); - return 0; - } - return 1; - } - /* else: external_ssl_ctx/external_ssl_ctx_domain do not exist or return - * 0, CivetWeb should continue initializing SSL */ - - /* If PEM file is not specified and the init_ssl callbacks - * are not specified, setup will fail. */ - if (((pem = dom_ctx->config[SSL_CERTIFICATE]) == NULL) - && (phys_ctx->callbacks.init_ssl == NULL) - && (phys_ctx->callbacks.init_ssl_domain == NULL)) { - /* No certificate and no init_ssl callbacks: - * Essential data to set up TLS is missing. - */ - mg_cry_ctx_internal(phys_ctx, - "Initializing SSL failed: -%s is not set", - config_options[SSL_CERTIFICATE].name); - return 0; - } - - /* If a certificate chain is configured, use it. */ - chain = dom_ctx->config[SSL_CERTIFICATE_CHAIN]; - if (chain == NULL) { - /* Default: certificate chain in PEM file */ - chain = pem; - } - if ((chain != NULL) && (*chain == 0)) { - /* If the chain is an empty string, don't use it. */ - chain = NULL; - } - - if (!initialize_openssl(ebuf, sizeof(ebuf))) { - mg_cry_ctx_internal(phys_ctx, "%s", ebuf); - return 0; - } - - return init_ssl_ctx_impl(phys_ctx, dom_ctx, pem, chain); -} - - -static void -uninitialize_openssl(void) -{ -#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) - - if (mg_atomic_dec(&cryptolib_users) == 0) { - - /* Shutdown according to - * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup - * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl - */ - CONF_modules_unload(1); -#else - int i; - - if (mg_atomic_dec(&cryptolib_users) == 0) { - - /* Shutdown according to - * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup - * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl - */ - CRYPTO_set_locking_callback(NULL); - CRYPTO_set_id_callback(NULL); - ENGINE_cleanup(); - CONF_modules_unload(1); - ERR_free_strings(); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); - OPENSSL_REMOVE_THREAD_STATE(); - - for (i = 0; i < CRYPTO_num_locks(); i++) { - pthread_mutex_destroy(&ssl_mutexes[i]); - } - mg_free(ssl_mutexes); - ssl_mutexes = NULL; -#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */ - } -} -#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) */ - - -#if !defined(NO_FILESYSTEMS) -static int -set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) -{ - if (phys_ctx) { - struct mg_file file = STRUCT_FILE_INITIALIZER; - const char *path; - struct mg_connection fc; - if (!dom_ctx) { - dom_ctx = &(phys_ctx->dd); - } - path = dom_ctx->config[GLOBAL_PASSWORDS_FILE]; - if ((path != NULL) - && !mg_stat(fake_connection(&fc, phys_ctx), path, &file.stat)) { - mg_cry_ctx_internal(phys_ctx, - "Cannot open %s: %s", - path, - strerror(ERRNO)); - return 0; - } - return 1; - } - return 0; -} -#endif /* NO_FILESYSTEMS */ - - -static int -set_acl_option(struct mg_context *phys_ctx) -{ - union usa sa; - memset(&sa, 0, sizeof(sa)); -#if defined(USE_IPV6) - sa.sin6.sin6_family = AF_INET6; -#else - sa.sin.sin_family = AF_INET; -#endif - return check_acl(phys_ctx, &sa) != -1; -} - - -static void -reset_per_request_attributes(struct mg_connection *conn) -{ - if (!conn) { - return; - } - - conn->num_bytes_sent = conn->consumed_content = 0; - - conn->path_info = NULL; - conn->status_code = -1; - conn->content_len = -1; - conn->is_chunked = 0; - conn->must_close = 0; - conn->request_len = 0; - conn->request_state = 0; - conn->throttle = 0; - conn->accept_gzip = 0; - - conn->response_info.content_length = conn->request_info.content_length = -1; - conn->response_info.http_version = conn->request_info.http_version = NULL; - conn->response_info.num_headers = conn->request_info.num_headers = 0; - conn->response_info.status_text = NULL; - conn->response_info.status_code = 0; - - conn->request_info.remote_user = NULL; - conn->request_info.request_method = NULL; - conn->request_info.request_uri = NULL; - - /* Free cleaned local URI (if any) */ - if (conn->request_info.local_uri != conn->request_info.local_uri_raw) { - mg_free((void *)conn->request_info.local_uri); - conn->request_info.local_uri = NULL; - } - conn->request_info.local_uri = NULL; - -#if defined(USE_SERVER_STATS) - conn->processing_time = 0; -#endif -} - - -static int -set_tcp_nodelay(const struct socket *so, int nodelay_on) -{ - if ((so->lsa.sa.sa_family == AF_INET) - || (so->lsa.sa.sa_family == AF_INET6)) { - /* Only for TCP sockets */ - if (setsockopt(so->sock, - IPPROTO_TCP, - TCP_NODELAY, - (SOCK_OPT_TYPE)&nodelay_on, - sizeof(nodelay_on)) - != 0) { - /* Error */ - return 1; - } - } - /* OK */ - return 0; -} - - -#if !defined(__ZEPHYR__) -static void -close_socket_gracefully(struct mg_connection *conn) -{ -#if defined(_WIN32) - char buf[MG_BUF_LEN]; - int n; -#endif - struct linger linger; - int error_code = 0; - int linger_timeout = -2; - socklen_t opt_len = sizeof(error_code); - - if (!conn) { - return; - } - - /* http://msdn.microsoft.com/en-us/library/ms739165(v=vs.85).aspx: - * "Note that enabling a nonzero timeout on a nonblocking socket - * is not recommended.", so set it to blocking now */ - set_blocking_mode(conn->client.sock); - - /* Send FIN to the client */ - shutdown(conn->client.sock, SHUTDOWN_WR); - -#if defined(_WIN32) - /* Read and discard pending incoming data. If we do not do that and - * close - * the socket, the data in the send buffer may be discarded. This - * behaviour is seen on Windows, when client keeps sending data - * when server decides to close the connection; then when client - * does recv() it gets no data back. */ - do { - n = pull_inner(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0); - } while (n > 0); -#endif - - if (conn->dom_ctx->config[LINGER_TIMEOUT]) { - linger_timeout = atoi(conn->dom_ctx->config[LINGER_TIMEOUT]); - } - - /* Set linger option according to configuration */ - if (linger_timeout >= 0) { - /* Set linger option to avoid socket hanging out after close. This - * prevent ephemeral port exhaust problem under high QPS. */ - linger.l_onoff = 1; - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4244) -#endif -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - /* Data type of linger structure elements may differ, - * so we don't know what cast we need here. - * Disable type conversion warnings. */ - - linger.l_linger = (linger_timeout + 999) / 1000; - -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic pop -#endif -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - - } else { - linger.l_onoff = 0; - linger.l_linger = 0; - } - - if (linger_timeout < -1) { - /* Default: don't configure any linger */ - } else if (getsockopt(conn->client.sock, - SOL_SOCKET, - SO_ERROR, -#if defined(_WIN32) /* WinSock uses different data type here */ - (char *)&error_code, -#else - &error_code, -#endif - &opt_len) - != 0) { - /* Cannot determine if socket is already closed. This should - * not occur and never did in a test. Log an error message - * and continue. */ - mg_cry_internal(conn, - "%s: getsockopt(SOL_SOCKET SO_ERROR) failed: %s", - __func__, - strerror(ERRNO)); -#if defined(_WIN32) - } else if (error_code == WSAECONNRESET) { -#else - } else if (error_code == ECONNRESET) { -#endif - /* Socket already closed by client/peer, close socket without linger - */ - } else { - - /* Set linger timeout */ - if (setsockopt(conn->client.sock, - SOL_SOCKET, - SO_LINGER, - (char *)&linger, - sizeof(linger)) - != 0) { - mg_cry_internal( - conn, - "%s: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s", - __func__, - linger.l_onoff, - linger.l_linger, - strerror(ERRNO)); - } - } - - /* Now we know that our FIN is ACK-ed, safe to close */ - closesocket(conn->client.sock); - conn->client.sock = INVALID_SOCKET; -} -#endif - - -static void -close_connection(struct mg_connection *conn) -{ -#if defined(USE_SERVER_STATS) - conn->conn_state = 6; /* to close */ -#endif - -#if defined(USE_LUA) && defined(USE_WEBSOCKET) - if (conn->lua_websocket_state) { - lua_websocket_close(conn, conn->lua_websocket_state); - conn->lua_websocket_state = NULL; - } -#endif - - mg_lock_connection(conn); - - /* Set close flag, so keep-alive loops will stop */ - conn->must_close = 1; - - /* call the connection_close callback if assigned */ - if (conn->phys_ctx->callbacks.connection_close != NULL) { - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - conn->phys_ctx->callbacks.connection_close(conn); - } - } - - /* Reset user data, after close callback is called. - * Do not reuse it. If the user needs a destructor, - * it must be done in the connection_close callback. */ - mg_set_user_connection_data(conn, NULL); - -#if defined(USE_SERVER_STATS) - conn->conn_state = 7; /* closing */ -#endif - -#if defined(USE_MBEDTLS) - if (conn->ssl != NULL) { - mbed_ssl_close(conn->ssl); - conn->ssl = NULL; - } -#elif defined(USE_GNUTLS) - if (conn->ssl != NULL) { - gtls_ssl_close(conn->ssl); - conn->ssl = NULL; - } -#elif !defined(NO_SSL) - if (conn->ssl != NULL) { - /* Run SSL_shutdown twice to ensure completely close SSL connection - */ - SSL_shutdown(conn->ssl); - SSL_free(conn->ssl); - OPENSSL_REMOVE_THREAD_STATE(); - conn->ssl = NULL; - } -#endif - if (conn->client.sock != INVALID_SOCKET) { -#if defined(__ZEPHYR__) - closesocket(conn->client.sock); -#else - close_socket_gracefully(conn); -#endif - conn->client.sock = INVALID_SOCKET; - } - - /* call the connection_closed callback if assigned */ - if (conn->phys_ctx->callbacks.connection_closed != NULL) { - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - conn->phys_ctx->callbacks.connection_closed(conn); - } - } - - mg_unlock_connection(conn); - -#if defined(USE_SERVER_STATS) - conn->conn_state = 8; /* closed */ -#endif -} - - -CIVETWEB_API void -mg_close_connection(struct mg_connection *conn) -{ - if ((conn == NULL) || (conn->phys_ctx == NULL)) { - return; - } - -#if defined(USE_WEBSOCKET) - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - if (conn->in_websocket_handling) { - /* Set close flag, so the server thread can exit. */ - conn->must_close = 1; - return; - } - } - if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) { - - unsigned int i; - - /* client context: loops must end */ - STOP_FLAG_ASSIGN(&conn->phys_ctx->stop_flag, 1); - conn->must_close = 1; - - /* We need to get the client thread out of the select/recv call - * here. */ - /* Since we use a sleep quantum of some seconds to check for recv - * timeouts, we will just wait a few seconds in mg_join_thread. */ - - /* join worker thread */ - for (i = 0; i < conn->phys_ctx->spawned_worker_threads; i++) { - mg_join_thread(conn->phys_ctx->worker_threadids[i]); - } - } -#endif /* defined(USE_WEBSOCKET) */ - - close_connection(conn); - -#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ - && !defined(USE_GNUTLS) // TODO: mbedTLS client - if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) - || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT)) - && (conn->phys_ctx->dd.ssl_ctx != NULL)) { - SSL_CTX_free(conn->phys_ctx->dd.ssl_ctx); - } -#endif - -#if defined(USE_WEBSOCKET) - if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) { - mg_free(conn->phys_ctx->worker_threadids); - (void)pthread_mutex_destroy(&conn->mutex); - mg_free(conn); - } else if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) { - (void)pthread_mutex_destroy(&conn->mutex); - mg_free(conn); - } -#else - if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) { /* Client */ - (void)pthread_mutex_destroy(&conn->mutex); - mg_free(conn); - } -#endif /* defined(USE_WEBSOCKET) */ -} - - -static struct mg_connection * -mg_connect_client_impl(const struct mg_client_options *client_options, - int use_ssl, - struct mg_init_data *init, - struct mg_error_data *error) -{ - struct mg_connection *conn = NULL; - SOCKET sock; - union usa sa; - struct sockaddr *psa; - socklen_t len; - - unsigned max_req_size = - (unsigned)atoi(config_options[MAX_REQUEST_SIZE].default_value); - - /* Size of structures, aligned to 8 bytes */ - size_t conn_size = ((sizeof(struct mg_connection) + 7) >> 3) << 3; - size_t ctx_size = ((sizeof(struct mg_context) + 7) >> 3) << 3; - size_t alloc_size = conn_size + ctx_size + max_req_size; - - (void)init; /* TODO: Implement required options */ - - conn = (struct mg_connection *)mg_calloc(1, alloc_size); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OK; - error->code_sub = 0; - if (error->text_buffer_size > 0) { - error->text[0] = 0; - } - } - - if (conn == NULL) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)alloc_size; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "calloc(): %s", - strerror(ERRNO)); - } - return NULL; - } - -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" -#endif /* defined(GCC_DIAGNOSTIC) */ - /* conn_size is aligned to 8 bytes */ - - conn->phys_ctx = (struct mg_context *)(((char *)conn) + conn_size); - -#if defined(GCC_DIAGNOSTIC) -#pragma GCC diagnostic pop -#endif /* defined(GCC_DIAGNOSTIC) */ - - conn->buf = (((char *)conn) + conn_size + ctx_size); - conn->buf_size = (int)max_req_size; - conn->phys_ctx->context_type = CONTEXT_HTTP_CLIENT; - conn->dom_ctx = &(conn->phys_ctx->dd); - - if (!connect_socket(conn->phys_ctx, - client_options->host, - client_options->port, - use_ssl, - error, - &sock, - &sa)) { - /* "error" will be set by connect_socket. */ - /* free all memory and return NULL; */ - mg_free(conn); - return NULL; - } - -#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ - && !defined(USE_GNUTLS) // TODO: mbedTLS client -#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \ - && !defined(NO_SSL_DL) - - if (use_ssl - && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(TLS_client_method())) - == NULL) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "SSL_CTX_new error: %s", - ssl_error()); - } - - closesocket(sock); - mg_free(conn); - return NULL; - } - -#else - - if (use_ssl - && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) - == NULL) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "SSL_CTX_new error: %s", - ssl_error()); - } - - closesocket(sock); - mg_free(conn); - return NULL; - } - -#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */ -#endif /* NO_SSL */ - -#if defined(USE_IPV6) - len = (sa.sa.sa_family == AF_INET) ? sizeof(conn->client.rsa.sin) - : sizeof(conn->client.rsa.sin6); - psa = (sa.sa.sa_family == AF_INET) - ? (struct sockaddr *)&(conn->client.rsa.sin) - : (struct sockaddr *)&(conn->client.rsa.sin6); -#else - len = sizeof(conn->client.rsa.sin); - psa = (struct sockaddr *)&(conn->client.rsa.sin); -#endif - - conn->client.sock = sock; - conn->client.lsa = sa; - - if (getsockname(sock, psa, &len) != 0) { - mg_cry_internal(conn, - "%s: getsockname() failed: %s", - __func__, - strerror(ERRNO)); - } - - conn->client.is_ssl = use_ssl ? 1 : 0; - if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OS_ERROR; - error->code_sub = (unsigned)ERRNO; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "Can not create mutex"); - } -#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ - && !defined(USE_GNUTLS) // TODO: mbedTLS client - SSL_CTX_free(conn->dom_ctx->ssl_ctx); -#endif - closesocket(sock); - mg_free(conn); - return NULL; - } - -#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ - && !defined(USE_GNUTLS) // TODO: mbedTLS client - if (use_ssl) { - /* TODO: Check ssl_verify_peer and ssl_ca_path here. - * SSL_CTX_set_verify call is needed to switch off server - * certificate checking, which is off by default in OpenSSL and - * on in yaSSL. */ - /* TODO: SSL_CTX_set_verify(conn->dom_ctx, - * SSL_VERIFY_PEER, verify_ssl_server); */ - - if (client_options->client_cert) { - if (!ssl_use_pem_file(conn->phys_ctx, - conn->dom_ctx, - client_options->client_cert, - NULL)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_TLS_CLIENT_CERT_ERROR; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "Can not use SSL client certificate"); - } - - SSL_CTX_free(conn->dom_ctx->ssl_ctx); - closesocket(sock); - mg_free(conn); - return NULL; - } - } - - if (client_options->server_cert) { - if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx, - client_options->server_cert, - NULL) - != 1) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_TLS_SERVER_CERT_ERROR; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "SSL_CTX_load_verify_locations error: %s", - ssl_error()); - } - SSL_CTX_free(conn->dom_ctx->ssl_ctx); - closesocket(sock); - mg_free(conn); - return NULL; - } - SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); - } else { - SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_NONE, NULL); - } - - if (!sslize(conn, SSL_connect, client_options)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_TLS_CONNECT_ERROR; - mg_snprintf(NULL, - NULL, /* No truncation check for ebuf */ - error->text, - error->text_buffer_size, - "SSL connection error"); - } - SSL_CTX_free(conn->dom_ctx->ssl_ctx); - closesocket(sock); - mg_free(conn); - return NULL; - } - } -#endif - - return conn; -} - - -CIVETWEB_API struct mg_connection * -mg_connect_client_secure(const struct mg_client_options *client_options, - char *error_buffer, - size_t error_buffer_size) -{ - struct mg_init_data init; - struct mg_error_data error; - - memset(&init, 0, sizeof(init)); - memset(&error, 0, sizeof(error)); - error.text_buffer_size = error_buffer_size; - error.text = error_buffer; - return mg_connect_client_impl(client_options, 1, &init, &error); -} - - -CIVETWEB_API struct mg_connection * -mg_connect_client(const char *host, - int port, - int use_ssl, - char *error_buffer, - size_t error_buffer_size) -{ - struct mg_client_options opts; - struct mg_init_data init; - struct mg_error_data error; - - memset(&init, 0, sizeof(init)); - - memset(&error, 0, sizeof(error)); - error.text_buffer_size = error_buffer_size; - error.text = error_buffer; - - memset(&opts, 0, sizeof(opts)); - opts.host = host; - opts.port = port; - if (use_ssl) { - opts.host_name = host; - } - - return mg_connect_client_impl(&opts, use_ssl, &init, &error); -} - - -#if defined(MG_EXPERIMENTAL_INTERFACES) -CIVETWEB_API struct mg_connection * -mg_connect_client2(const char *host, - const char *protocol, - int port, - const char *path, - struct mg_init_data *init, - struct mg_error_data *error) -{ - (void)path; - - int is_ssl, is_ws; - /* void *user_data = (init != NULL) ? init->user_data : NULL; -- TODO */ - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OK; - error->code_sub = 0; - if (error->text_buffer_size > 0) { - *error->text = 0; - } - } - - if ((host == NULL) || (protocol == NULL)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_PARAM; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - "Invalid parameters"); - } - return NULL; - } - - /* check all known protocols */ - if (!mg_strcasecmp(protocol, "http")) { - is_ssl = 0; - is_ws = 0; - } else if (!mg_strcasecmp(protocol, "https")) { - is_ssl = 1; - is_ws = 0; -#if defined(USE_WEBSOCKET) - } else if (!mg_strcasecmp(protocol, "ws")) { - is_ssl = 0; - is_ws = 1; - } else if (!mg_strcasecmp(protocol, "wss")) { - is_ssl = 1; - is_ws = 1; -#endif - } else { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_PARAM; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Protocol %s not supported", - protocol); - } - return NULL; - } - - /* TODO: The current implementation here just calls the old - * implementations, without using any new options. This is just a first - * step to test the new interfaces. */ -#if defined(USE_WEBSOCKET) - if (is_ws) { - /* TODO: implement all options */ - return mg_connect_websocket_client( - host, - port, - is_ssl, - ((error != NULL) ? error->text : NULL), - ((error != NULL) ? error->text_buffer_size : 0), - (path ? path : ""), - NULL /* TODO: origin */, - experimental_websocket_client_data_wrapper, - experimental_websocket_client_close_wrapper, - (void *)init->callbacks); - } -#else - (void)is_ws; -#endif - - /* TODO: all additional options */ - struct mg_client_options opts; - - memset(&opts, 0, sizeof(opts)); - opts.host = host; - opts.port = port; - - return mg_connect_client_impl(&opts, is_ssl, init, error); -} -#endif - - -static const struct { - const char *proto; - size_t proto_len; - unsigned default_port; -} abs_uri_protocols[] = {{"http://", 7, 80}, - {"https://", 8, 443}, - {"ws://", 5, 80}, - {"wss://", 6, 443}, - {NULL, 0, 0}}; - - -/* Check if the uri is valid. - * return 0 for invalid uri, - * return 1 for *, - * return 2 for relative uri, - * return 3 for absolute uri without port, - * return 4 for absolute uri with port */ -static int -get_uri_type(const char *uri) -{ - int i; - const char *hostend, *portbegin; - char *portend; - unsigned long port; - - /* According to the HTTP standard - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 - * URI can be an asterisk (*) or should start with slash (relative uri), - * or it should start with the protocol (absolute uri). */ - if ((uri[0] == '*') && (uri[1] == '\0')) { - /* asterisk */ - return 1; - } - - /* Valid URIs according to RFC 3986 - * (https://www.ietf.org/rfc/rfc3986.txt) - * must only contain reserved characters :/?#[]@!$&'()*+,;= - * and unreserved characters A-Z a-z 0-9 and -._~ - * and % encoded symbols. - */ - for (i = 0; uri[i] != 0; i++) { - if ((unsigned char)uri[i] < 33) { - /* control characters and spaces are invalid */ - return 0; - } - /* Allow everything else here (See #894) */ - } - - /* A relative uri starts with a / character */ - if (uri[0] == '/') { - /* relative uri */ - return 2; - } - - /* It could be an absolute uri: */ - /* This function only checks if the uri is valid, not if it is - * addressing the current server. So civetweb can also be used - * as a proxy server. */ - for (i = 0; abs_uri_protocols[i].proto != NULL; i++) { - if (mg_strncasecmp(uri, - abs_uri_protocols[i].proto, - abs_uri_protocols[i].proto_len) - == 0) { - - hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/'); - if (!hostend) { - return 0; - } - portbegin = strchr(uri + abs_uri_protocols[i].proto_len, ':'); - if (!portbegin) { - return 3; - } - - port = strtoul(portbegin + 1, &portend, 10); - if ((portend != hostend) || (port <= 0) || !is_valid_port(port)) { - return 0; - } - - return 4; - } - } - - return 0; -} - - -/* Return NULL or the relative uri at the current server */ -static const char * -get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn) -{ - const char *server_domain; - size_t server_domain_len; - size_t request_domain_len = 0; - unsigned long port = 0; - int i, auth_domain_check_enabled; - const char *hostbegin = NULL; - const char *hostend = NULL; - const char *portbegin; - char *portend; - - auth_domain_check_enabled = - !mg_strcasecmp(conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes"); - - /* DNS is case insensitive, so use case insensitive string compare here - */ - for (i = 0; abs_uri_protocols[i].proto != NULL; i++) { - if (mg_strncasecmp(uri, - abs_uri_protocols[i].proto, - abs_uri_protocols[i].proto_len) - == 0) { - - hostbegin = uri + abs_uri_protocols[i].proto_len; - hostend = strchr(hostbegin, '/'); - if (!hostend) { - return 0; - } - portbegin = strchr(hostbegin, ':'); - if ((!portbegin) || (portbegin > hostend)) { - port = abs_uri_protocols[i].default_port; - request_domain_len = (size_t)(hostend - hostbegin); - } else { - port = strtoul(portbegin + 1, &portend, 10); - if ((portend != hostend) || (port <= 0) - || !is_valid_port(port)) { - return 0; - } - request_domain_len = (size_t)(portbegin - hostbegin); - } - /* protocol found, port set */ - break; - } - } - - if (!port) { - /* port remains 0 if the protocol is not found */ - return 0; - } - - /* Check if the request is directed to a different server. */ - /* First check if the port is the same. */ - if (ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)) != port) { - /* Request is directed to a different port */ - return 0; - } - - /* Finally check if the server corresponds to the authentication - * domain of the server (the server domain). - * Allow full matches (like http://mydomain.com/path/file.ext), and - * allow subdomain matches (like http://www.mydomain.com/path/file.ext), - * but do not allow substrings (like - * http://notmydomain.com/path/file.ext - * or http://mydomain.com.fake/path/file.ext). - */ - if (auth_domain_check_enabled) { - server_domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN]; - server_domain_len = strlen(server_domain); - if ((server_domain_len == 0) || (hostbegin == NULL)) { - return 0; - } - if ((request_domain_len == server_domain_len) - && (!memcmp(server_domain, hostbegin, server_domain_len))) { - /* Request is directed to this server - full name match. */ - } else { - if (request_domain_len < (server_domain_len + 2)) { - /* Request is directed to another server: The server name - * is longer than the request name. - * Drop this case here to avoid overflows in the - * following checks. */ - return 0; - } - if (hostbegin[request_domain_len - server_domain_len - 1] != '.') { - /* Request is directed to another server: It could be a - * substring - * like notmyserver.com */ - return 0; - } - if (0 - != memcmp(server_domain, - hostbegin + request_domain_len - server_domain_len, - server_domain_len)) { - /* Request is directed to another server: - * The server name is different. */ - return 0; - } - } - } - - return hostend; -} - - -static int -get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) -{ - if (ebuf_len > 0) { - ebuf[0] = '\0'; - } - *err = 0; - - reset_per_request_attributes(conn); - - if (!conn) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Internal error"); - *err = 500; - return 0; - } - - /* Set the time the request was received. This value should be used for - * timeouts. */ - clock_gettime(CLOCK_MONOTONIC, &(conn->req_time)); - - conn->request_len = - read_message(NULL, conn, conn->buf, conn->buf_size, &conn->data_len); - DEBUG_ASSERT(conn->request_len < 0 || conn->data_len >= conn->request_len); - if ((conn->request_len >= 0) && (conn->data_len < conn->request_len)) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Invalid message size"); - *err = 500; - return 0; - } - - if ((conn->request_len == 0) && (conn->data_len == conn->buf_size)) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Message too large"); - *err = 413; - return 0; - } - - if (conn->request_len <= 0) { - if (conn->data_len > 0) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - conn->request_len == -3 ? "Request timeout" - : "Malformed message"); - *err = 400; - } else { - /* Server did not recv anything -> just close the connection */ - conn->must_close = 1; - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "No data received"); - *err = 0; - } - return 0; - } - return 1; -} - - -static int -get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) -{ - const char *cl; - - conn->connection_type = - CONNECTION_TYPE_REQUEST; /* request (valid of not) */ - - if (!get_message(conn, ebuf, ebuf_len, err)) { - return 0; - } - - if (parse_http_request(conn->buf, conn->buf_size, &conn->request_info) - <= 0) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Bad request"); - *err = 400; - return 0; - } - - /* Message is a valid request */ - - if (!switch_domain_context(conn)) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Bad request: Host mismatch"); - *err = 400; - return 0; - } - -#if USE_ZLIB - if (((cl = get_header(conn->request_info.http_headers, - conn->request_info.num_headers, - "Accept-Encoding")) - != NULL) - && strstr(cl, "gzip")) { - conn->accept_gzip = 1; - } -#endif - if (((cl = get_header(conn->request_info.http_headers, - conn->request_info.num_headers, - "Transfer-Encoding")) - != NULL) - && mg_strcasecmp(cl, "identity")) { - if (mg_strcasecmp(cl, "chunked")) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Bad request"); - *err = 400; - return 0; - } - conn->is_chunked = 1; - conn->content_len = 0; /* not yet read */ - } else if ((cl = get_header(conn->request_info.http_headers, - conn->request_info.num_headers, - "Content-Length")) - != NULL) { - /* Request has content length set */ - char *endptr = NULL; - conn->content_len = strtoll(cl, &endptr, 10); - if ((endptr == cl) || (conn->content_len < 0)) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Bad request"); - *err = 411; - return 0; - } - /* Publish the content length back to the request info. */ - conn->request_info.content_length = conn->content_len; - } else { - /* There is no exception, see RFC7230. */ - conn->content_len = 0; - } - - return 1; -} - - -/* conn is assumed to be valid in this internal function */ -static int -get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) -{ - const char *cl; - - conn->connection_type = - CONNECTION_TYPE_RESPONSE; /* response (valid or not) */ - - if (!get_message(conn, ebuf, ebuf_len, err)) { - return 0; - } - - if (parse_http_response(conn->buf, conn->buf_size, &conn->response_info) - <= 0) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Bad response"); - *err = 400; - return 0; - } - - /* Message is a valid response */ - - if (((cl = get_header(conn->response_info.http_headers, - conn->response_info.num_headers, - "Transfer-Encoding")) - != NULL) - && mg_strcasecmp(cl, "identity")) { - if (mg_strcasecmp(cl, "chunked")) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Bad request"); - *err = 400; - return 0; - } - conn->is_chunked = 1; - conn->content_len = 0; /* not yet read */ - } else if ((cl = get_header(conn->response_info.http_headers, - conn->response_info.num_headers, - "Content-Length")) - != NULL) { - char *endptr = NULL; - conn->content_len = strtoll(cl, &endptr, 10); - if ((endptr == cl) || (conn->content_len < 0)) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Bad request"); - *err = 411; - return 0; - } - /* Publish the content length back to the response info. */ - conn->response_info.content_length = conn->content_len; - - /* TODO: check if it is still used in response_info */ - conn->request_info.content_length = conn->content_len; - - /* TODO: we should also consider HEAD method */ - if (conn->response_info.status_code == 304) { - conn->content_len = 0; - } - } else { - /* TODO: we should also consider HEAD method */ - if (((conn->response_info.status_code >= 100) - && (conn->response_info.status_code <= 199)) - || (conn->response_info.status_code == 204) - || (conn->response_info.status_code == 304)) { - conn->content_len = 0; - } else { - conn->content_len = -1; /* unknown content length */ - } - } - - return 1; -} - - -CIVETWEB_API int -mg_get_response(struct mg_connection *conn, - char *ebuf, - size_t ebuf_len, - int timeout) -{ - int err, ret; - char txt[32]; /* will not overflow */ - char *save_timeout; - char *new_timeout; - - if (ebuf_len > 0) { - ebuf[0] = '\0'; - } - - if (!conn) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Parameter error"); - return -1; - } - - /* Reset the previous responses */ - conn->data_len = 0; - - /* Implementation of API function for HTTP clients */ - save_timeout = conn->dom_ctx->config[REQUEST_TIMEOUT]; - - if (timeout >= 0) { - mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout); - new_timeout = txt; - } else { - new_timeout = NULL; - } - - conn->dom_ctx->config[REQUEST_TIMEOUT] = new_timeout; - ret = get_response(conn, ebuf, ebuf_len, &err); - conn->dom_ctx->config[REQUEST_TIMEOUT] = save_timeout; - - /* TODO: here, the URI is the http response code */ - conn->request_info.local_uri_raw = conn->request_info.request_uri; - conn->request_info.local_uri = conn->request_info.local_uri_raw; - - /* TODO (mid): Define proper return values - maybe return length? - * For the first test use <0 for error and >0 for OK */ - return (ret == 0) ? -1 : +1; -} - - -CIVETWEB_API struct mg_connection * -mg_download(const char *host, - int port, - int use_ssl, - char *ebuf, - size_t ebuf_len, - const char *fmt, - ...) -{ - struct mg_connection *conn; - va_list ap; - int i; - int reqerr; - - if (ebuf_len > 0) { - ebuf[0] = '\0'; - } - - va_start(ap, fmt); - - /* open a connection */ - conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len); - - if (conn != NULL) { - i = mg_vprintf(conn, fmt, ap); - if (i <= 0) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - ebuf_len, - "%s", - "Error sending request"); - } else { - /* make sure the buffer is clear */ - conn->data_len = 0; - get_response(conn, ebuf, ebuf_len, &reqerr); - - /* TODO: here, the URI is the http response code */ - conn->request_info.local_uri = conn->request_info.request_uri; - } - } - - /* if an error occurred, close the connection */ - if ((ebuf[0] != '\0') && (conn != NULL)) { - mg_close_connection(conn); - conn = NULL; - } - - va_end(ap); - return conn; -} - - -struct websocket_client_thread_data { - struct mg_connection *conn; - mg_websocket_data_handler data_handler; - mg_websocket_close_handler close_handler; - void *callback_data; -}; - - -#if defined(USE_WEBSOCKET) -#if defined(_WIN32) -static unsigned __stdcall websocket_client_thread(void *data) -#else -static void * -websocket_client_thread(void *data) -#endif -{ - struct websocket_client_thread_data *cdata = - (struct websocket_client_thread_data *)data; - - void *user_thread_ptr = NULL; - -#if !defined(_WIN32) && !defined(__ZEPHYR__) - struct sigaction sa; - - /* Ignore SIGPIPE */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); -#endif - - mg_set_thread_name("ws-clnt"); - - if (cdata->conn->phys_ctx) { - if (cdata->conn->phys_ctx->callbacks.init_thread) { - /* 3 indicates a websocket client thread */ - /* TODO: check if conn->phys_ctx can be set */ - user_thread_ptr = cdata->conn->phys_ctx->callbacks.init_thread( - cdata->conn->phys_ctx, 3); - } - } - - read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data); - - DEBUG_TRACE("%s", "Websocket client thread exited\n"); - - if (cdata->close_handler != NULL) { - cdata->close_handler(cdata->conn, cdata->callback_data); - } - - /* The websocket_client context has only this thread. If it runs out, - set the stop_flag to 2 (= "stopped"). */ - STOP_FLAG_ASSIGN(&cdata->conn->phys_ctx->stop_flag, 2); - - if (cdata->conn->phys_ctx->callbacks.exit_thread) { - cdata->conn->phys_ctx->callbacks.exit_thread(cdata->conn->phys_ctx, - 3, - user_thread_ptr); - } - - mg_free((void *)cdata); - -#if defined(_WIN32) - return 0; -#else - return NULL; -#endif -} -#endif - - -#if defined(USE_WEBSOCKET) -static void -generate_websocket_magic(char *magic25) -{ - uint64_t rnd; - unsigned char buffer[2 * sizeof(rnd)]; - - rnd = get_random(); - memcpy(buffer, &rnd, sizeof(rnd)); - rnd = get_random(); - memcpy(buffer + sizeof(rnd), &rnd, sizeof(rnd)); - - size_t dst_len = 24 + 1; - mg_base64_encode(buffer, sizeof(buffer), magic25, &dst_len); -} -#endif - - -static struct mg_connection * -mg_connect_websocket_client_impl(const struct mg_client_options *client_options, - int use_ssl, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - const char *extensions, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data) -{ - struct mg_connection *conn = NULL; - -#if defined(USE_WEBSOCKET) - struct websocket_client_thread_data *thread_data; - char magic[32]; - generate_websocket_magic(magic); - - const char *host = client_options->host; - int i; - - struct mg_init_data init; - struct mg_error_data error; - - memset(&init, 0, sizeof(init)); - memset(&error, 0, sizeof(error)); - error.text_buffer_size = error_buffer_size; - error.text = error_buffer; - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif - - /* Establish the client connection and request upgrade */ - conn = mg_connect_client_impl(client_options, use_ssl, &init, &error); - - /* Connection object will be null if something goes wrong */ - if (conn == NULL) { - /* error_buffer should be already filled ... */ - if (!error_buffer[0]) { - /* ... if not add an error message */ - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - error_buffer, - error_buffer_size, - "Unexpected error"); - } - return NULL; - } - - if (origin != NULL) { - if (extensions != NULL) { - i = mg_printf(conn, - "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: %s\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Sec-WebSocket-Extensions: %s\r\n" - "Origin: %s\r\n" - "\r\n", - path, - host, - magic, - extensions, - origin); - } else { - i = mg_printf(conn, - "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: %s\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Origin: %s\r\n" - "\r\n", - path, - host, - magic, - origin); - } - } else { - - if (extensions != NULL) { - i = mg_printf(conn, - "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: %s\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Sec-WebSocket-Extensions: %s\r\n" - "\r\n", - path, - host, - magic, - extensions); - } else { - i = mg_printf(conn, - "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: %s\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n", - path, - host, - magic); - } - } - if (i <= 0) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - error_buffer, - error_buffer_size, - "%s", - "Error sending request"); - mg_close_connection(conn); - return NULL; - } - - conn->data_len = 0; - if (!get_response(conn, error_buffer, error_buffer_size, &i)) { - mg_close_connection(conn); - return NULL; - } - conn->request_info.local_uri_raw = conn->request_info.request_uri; - conn->request_info.local_uri = conn->request_info.local_uri_raw; - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - - if (conn->response_info.status_code != 101) { - /* We sent an "upgrade" request. For a correct websocket - * protocol handshake, we expect a "101 Continue" response. - * Otherwise it is a protocol violation. Maybe the HTTP - * Server does not know websockets. */ - if (!*error_buffer) { - /* set an error, if not yet set */ - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - error_buffer, - error_buffer_size, - "Unexpected server reply"); - } - - DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer); - mg_close_connection(conn); - return NULL; - } - - thread_data = (struct websocket_client_thread_data *)mg_calloc_ctx( - 1, sizeof(struct websocket_client_thread_data), conn->phys_ctx); - if (!thread_data) { - DEBUG_TRACE("%s\r\n", "Out of memory"); - mg_close_connection(conn); - return NULL; - } - - thread_data->conn = conn; - thread_data->data_handler = data_func; - thread_data->close_handler = close_func; - thread_data->callback_data = user_data; - - conn->phys_ctx->worker_threadids = - (pthread_t *)mg_calloc_ctx(1, sizeof(pthread_t), conn->phys_ctx); - if (!conn->phys_ctx->worker_threadids) { - DEBUG_TRACE("%s\r\n", "Out of memory"); - mg_free(thread_data); - mg_close_connection(conn); - return NULL; - } - - /* Now upgrade to ws/wss client context */ - conn->phys_ctx->user_data = user_data; - conn->phys_ctx->context_type = CONTEXT_WS_CLIENT; - conn->phys_ctx->cfg_max_worker_threads = 1; /* one worker thread */ - conn->phys_ctx->spawned_worker_threads = 1; /* one worker thread */ - - /* Start a thread to read the websocket client connection - * This thread will automatically stop when mg_disconnect is - * called on the client connection */ - if (mg_start_thread_with_id(websocket_client_thread, - thread_data, - conn->phys_ctx->worker_threadids) - != 0) { - conn->phys_ctx->spawned_worker_threads = 0; - mg_free(thread_data); - mg_close_connection(conn); - conn = NULL; - DEBUG_TRACE("%s", - "Websocket client connect thread could not be started\r\n"); - } - -#else - /* Appease "unused parameter" warnings */ - (void)client_options; - (void)use_ssl; - (void)error_buffer; - (void)error_buffer_size; - (void)path; - (void)origin; - (void)extensions; - (void)user_data; - (void)data_func; - (void)close_func; -#endif - - return conn; -} - - -CIVETWEB_API struct mg_connection * -mg_connect_websocket_client(const char *host, - int port, - int use_ssl, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data) -{ - struct mg_client_options client_options; - memset(&client_options, 0, sizeof(client_options)); - client_options.host = host; - client_options.port = port; - if (use_ssl) { - client_options.host_name = host; - } - - return mg_connect_websocket_client_impl(&client_options, - use_ssl, - error_buffer, - error_buffer_size, - path, - origin, - NULL, - data_func, - close_func, - user_data); -} - - -CIVETWEB_API struct mg_connection * -mg_connect_websocket_client_secure( - const struct mg_client_options *client_options, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data) -{ - if (!client_options) { - return NULL; - } - return mg_connect_websocket_client_impl(client_options, - 1, - error_buffer, - error_buffer_size, - path, - origin, - NULL, - data_func, - close_func, - user_data); -} - - -CIVETWEB_API struct mg_connection * -mg_connect_websocket_client_extensions(const char *host, - int port, - int use_ssl, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - const char *extensions, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data) -{ - struct mg_client_options client_options; - memset(&client_options, 0, sizeof(client_options)); - client_options.host = host; - client_options.port = port; - - return mg_connect_websocket_client_impl(&client_options, - use_ssl, - error_buffer, - error_buffer_size, - path, - origin, - extensions, - data_func, - close_func, - user_data); -} - - -CIVETWEB_API struct mg_connection * -mg_connect_websocket_client_secure_extensions( - const struct mg_client_options *client_options, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - const char *extensions, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data) -{ - if (!client_options) { - return NULL; - } - return mg_connect_websocket_client_impl(client_options, - 1, - error_buffer, - error_buffer_size, - path, - origin, - extensions, - data_func, - close_func, - user_data); -} - - -/* Prepare connection data structure */ -static void -init_connection(struct mg_connection *conn) -{ - /* Is keep alive allowed by the server */ - int keep_alive_enabled = - !mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes"); - - if (!keep_alive_enabled) { - conn->must_close = 1; - } - - /* Important: on new connection, reset the receiving buffer. Credit - * goes to crule42. */ - conn->data_len = 0; - conn->handled_requests = 0; - conn->connection_type = CONNECTION_TYPE_INVALID; - conn->request_info.acceptedWebSocketSubprotocol = NULL; - mg_set_user_connection_data(conn, NULL); - -#if defined(USE_SERVER_STATS) - conn->conn_state = 2; /* init */ -#endif - - /* call the init_connection callback if assigned */ - if (conn->phys_ctx->callbacks.init_connection != NULL) { - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { - void *conn_data = NULL; - conn->phys_ctx->callbacks.init_connection(conn, &conn_data); - mg_set_user_connection_data(conn, conn_data); - } - } -} - - -/* Process a connection - may handle multiple requests - * using the same connection. - * Must be called with a valid connection (conn and - * conn->phys_ctx must be valid). - */ -static void -process_new_connection(struct mg_connection *conn) -{ - struct mg_request_info *ri = &conn->request_info; - int keep_alive, discard_len; - char ebuf[100]; - const char *hostend; - int reqerr, uri_type; - -#if defined(USE_SERVER_STATS) - ptrdiff_t mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections)); - mg_atomic_add(&(conn->phys_ctx->total_connections), 1); - mg_atomic_max(&(conn->phys_ctx->max_active_connections), mcon); -#endif - - DEBUG_TRACE("Start processing connection from %s", - conn->request_info.remote_addr); - - /* Loop over multiple requests sent using the same connection - * (while "keep alive"). */ - do { - DEBUG_TRACE("calling get_request (%i times for this connection)", - conn->handled_requests + 1); - -#if defined(USE_SERVER_STATS) - conn->conn_state = 3; /* ready */ -#endif - - if (!get_request(conn, ebuf, sizeof(ebuf), &reqerr)) { - /* The request sent by the client could not be understood by - * the server, or it was incomplete or a timeout. Send an - * error message and close the connection. */ - if (reqerr > 0) { - DEBUG_ASSERT(ebuf[0] != '\0'); - mg_send_http_error(conn, reqerr, "%s", ebuf); - } - - } else if (strcmp(ri->http_version, "1.0") - && strcmp(ri->http_version, "1.1")) { - /* HTTP/2 is not allowed here */ - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - sizeof(ebuf), - "Bad HTTP version: [%s]", - ri->http_version); - mg_send_http_error(conn, 505, "%s", ebuf); - } - - if (ebuf[0] == '\0') { - uri_type = get_uri_type(conn->request_info.request_uri); - switch (uri_type) { - case 1: - /* Asterisk */ - conn->request_info.local_uri_raw = 0; - /* TODO: Deal with '*'. */ - break; - case 2: - /* relative uri */ - conn->request_info.local_uri_raw = - conn->request_info.request_uri; - break; - case 3: - case 4: - /* absolute uri (with/without port) */ - hostend = get_rel_url_at_current_server( - conn->request_info.request_uri, conn); - if (hostend) { - conn->request_info.local_uri_raw = hostend; - } else { - conn->request_info.local_uri_raw = NULL; - } - break; - default: - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, - sizeof(ebuf), - "Invalid URI"); - mg_send_http_error(conn, 400, "%s", ebuf); - conn->request_info.local_uri_raw = NULL; - break; - } - conn->request_info.local_uri = - (char *)conn->request_info.local_uri_raw; - } - - if (ebuf[0] != '\0') { - conn->protocol_type = -1; - - } else { - /* HTTP/1 allows protocol upgrade */ - conn->protocol_type = should_switch_to_protocol(conn); - - if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { - /* This will occur, if a HTTP/1.1 request should be upgraded - * to HTTP/2 - but not if HTTP/2 is negotiated using ALPN. - * Since most (all?) major browsers only support HTTP/2 using - * ALPN, this is hard to test and very low priority. - * Deactivate it (at least for now). - */ - conn->protocol_type = PROTOCOL_TYPE_HTTP1; - } - } - - DEBUG_TRACE("http: %s, error: %s", - (ri->http_version ? ri->http_version : "none"), - (ebuf[0] ? ebuf : "none")); - - if (ebuf[0] == '\0') { - if (conn->request_info.local_uri) { - - /* handle request to local server */ - handle_request_stat_log(conn); - - } else { - /* TODO: handle non-local request (PROXY) */ - conn->must_close = 1; - } - } else { - conn->must_close = 1; - } - - /* Response complete. Free header buffer */ - free_buffered_response_header_list(conn); - - if (ri->remote_user != NULL) { - mg_free((void *)ri->remote_user); - /* Important! When having connections with and without auth - * would cause double free and then crash */ - ri->remote_user = NULL; - } - - /* NOTE(lsm): order is important here. should_keep_alive() call - * is using parsed request, which will be invalid after - * memmove's below. - * Therefore, memorize should_keep_alive() result now for later - * use in loop exit condition. */ - /* Enable it only if this request is completely discardable. */ - keep_alive = STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag) - && should_keep_alive(conn) && (conn->content_len >= 0) - && (conn->request_len > 0) - && ((conn->is_chunked == 4) - || (!conn->is_chunked - && ((conn->consumed_content == conn->content_len) - || ((conn->request_len + conn->content_len) - <= conn->data_len)))) - && (conn->protocol_type == PROTOCOL_TYPE_HTTP1); - - if (keep_alive) { - /* Discard all buffered data for this request */ - discard_len = - ((conn->request_len + conn->content_len) < conn->data_len) - ? (int)(conn->request_len + conn->content_len) - : conn->data_len; - conn->data_len -= discard_len; - - if (conn->data_len > 0) { - DEBUG_TRACE("discard_len = %d", discard_len); - memmove(conn->buf, - conn->buf + discard_len, - (size_t)conn->data_len); - } - } - - DEBUG_ASSERT(conn->data_len >= 0); - DEBUG_ASSERT(conn->data_len <= conn->buf_size); - - if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) { - DEBUG_TRACE("internal error: data_len = %li, buf_size = %li", - (long int)conn->data_len, - (long int)conn->buf_size); - break; - } - conn->handled_requests++; - } while (keep_alive); - - DEBUG_TRACE("Done processing connection from %s (%f sec)", - conn->request_info.remote_addr, - difftime(time(NULL), conn->conn_birth_time)); - - close_connection(conn); - -#if defined(USE_SERVER_STATS) - mg_atomic_add(&(conn->phys_ctx->total_requests), conn->handled_requests); - mg_atomic_dec(&(conn->phys_ctx->active_connections)); -#endif -} - -static int -mg_start_worker_thread(struct mg_context *ctx, - int only_if_no_idle_threads); /* forward declaration */ - -#if defined(ALTERNATIVE_QUEUE) - -static void -produce_socket(struct mg_context *ctx, const struct socket *sp) -{ - unsigned int i; - - (void)mg_start_worker_thread( - ctx, 1); /* will start a worker-thread only if there aren't currently - any idle worker-threads */ - - while (!ctx->stop_flag) { - for (i = 0; i < ctx->spawned_worker_threads; i++) { - /* find a free worker slot and signal it */ - if (ctx->client_socks[i].in_use == 2) { - (void)pthread_mutex_lock(&ctx->thread_mutex); - if ((ctx->client_socks[i].in_use == 2) && !ctx->stop_flag) { - ctx->client_socks[i] = *sp; - ctx->client_socks[i].in_use = 1; - /* socket has been moved to the consumer */ - (void)pthread_mutex_unlock(&ctx->thread_mutex); - (void)event_signal(ctx->client_wait_events[i]); - return; - } - (void)pthread_mutex_unlock(&ctx->thread_mutex); - } - } - /* queue is full */ - mg_sleep(1); - } - /* must consume */ - set_blocking_mode(sp->sock); - closesocket(sp->sock); -} - - -static int -consume_socket(struct mg_context *ctx, - struct socket *sp, - int thread_index, - int counter_was_preincremented) -{ - DEBUG_TRACE("%s", "going idle"); - (void)pthread_mutex_lock(&ctx->thread_mutex); - if (counter_was_preincremented - == 0) { /* first call only: the master-thread pre-incremented this - before he spawned us */ - ctx->idle_worker_thread_count++; - } - ctx->client_socks[thread_index].in_use = 2; - (void)pthread_mutex_unlock(&ctx->thread_mutex); - - event_wait(ctx->client_wait_events[thread_index]); - - (void)pthread_mutex_lock(&ctx->thread_mutex); - *sp = ctx->client_socks[thread_index]; - if (ctx->stop_flag) { - (void)pthread_mutex_unlock(&ctx->thread_mutex); - if (sp->in_use == 1) { - /* must consume */ - set_blocking_mode(sp->sock); - closesocket(sp->sock); - } - return 0; - } - ctx->idle_worker_thread_count--; - (void)pthread_mutex_unlock(&ctx->thread_mutex); - if (sp->in_use == 1) { - DEBUG_TRACE("grabbed socket %d, going busy", sp->sock); - return 1; - } - /* must not reach here */ - DEBUG_ASSERT(0); - return 0; -} - -#else /* ALTERNATIVE_QUEUE */ - -/* Worker threads take accepted socket from the queue */ -static int -consume_socket(struct mg_context *ctx, - struct socket *sp, - int thread_index, - int counter_was_preincremented) -{ - (void)thread_index; - - DEBUG_TRACE("%s", "going idle"); - (void)pthread_mutex_lock(&ctx->thread_mutex); - if (counter_was_preincremented - == 0) { /* first call only: the master-thread pre-incremented this - before he spawned us */ - ctx->idle_worker_thread_count++; - } - - /* If the queue is empty, wait. We're idle at this point. */ - while ((ctx->sq_head == ctx->sq_tail) - && (STOP_FLAG_IS_ZERO(&ctx->stop_flag))) { - pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex); - } - - /* If we're stopping, sq_head may be equal to sq_tail. */ - if (ctx->sq_head > ctx->sq_tail) { - /* Copy socket from the queue and increment tail */ - *sp = ctx->squeue[ctx->sq_tail % ctx->sq_size]; - ctx->sq_tail++; - - DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1); - - /* Wrap pointers if needed */ - while (ctx->sq_tail > ctx->sq_size) { - ctx->sq_tail -= ctx->sq_size; - ctx->sq_head -= ctx->sq_size; - } - } - - (void)pthread_cond_signal(&ctx->sq_empty); - - ctx->idle_worker_thread_count--; - (void)pthread_mutex_unlock(&ctx->thread_mutex); - - return STOP_FLAG_IS_ZERO(&ctx->stop_flag); -} - - -/* Master thread adds accepted socket to a queue */ -static void -produce_socket(struct mg_context *ctx, const struct socket *sp) -{ - int queue_filled; - - (void)pthread_mutex_lock(&ctx->thread_mutex); - - queue_filled = ctx->sq_head - ctx->sq_tail; - - /* If the queue is full, wait */ - while (STOP_FLAG_IS_ZERO(&ctx->stop_flag) - && (queue_filled >= ctx->sq_size)) { - ctx->sq_blocked = 1; /* Status information: All threads busy */ -#if defined(USE_SERVER_STATS) - if (queue_filled > ctx->sq_max_fill) { - ctx->sq_max_fill = queue_filled; - } -#endif - (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex); - ctx->sq_blocked = 0; /* Not blocked now */ - queue_filled = ctx->sq_head - ctx->sq_tail; - } - - if (queue_filled < ctx->sq_size) { - /* Copy socket to the queue and increment head */ - ctx->squeue[ctx->sq_head % ctx->sq_size] = *sp; - ctx->sq_head++; - DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1); - } - - queue_filled = ctx->sq_head - ctx->sq_tail; -#if defined(USE_SERVER_STATS) - if (queue_filled > ctx->sq_max_fill) { - ctx->sq_max_fill = queue_filled; - } -#endif - - (void)pthread_cond_signal(&ctx->sq_full); - (void)pthread_mutex_unlock(&ctx->thread_mutex); - - (void)mg_start_worker_thread( - ctx, 1); /* will start a worker-thread only if there aren't currently - any idle worker-threads */ -} -#endif /* ALTERNATIVE_QUEUE */ - - -static void -worker_thread_run(struct mg_connection *conn) -{ - struct mg_context *ctx = conn->phys_ctx; - int thread_index; - struct mg_workerTLS tls; - int first_call_to_consume_socket = 1; - - mg_set_thread_name("worker"); - - tls.is_master = 0; - tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max); -#if defined(_WIN32) - tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL); -#endif - - /* Initialize thread local storage before calling any callback */ - pthread_setspecific(sTlsKey, &tls); - - /* Check if there is a user callback */ - if (ctx->callbacks.init_thread) { - /* call init_thread for a worker thread (type 1), and store the - * return value */ - tls.user_ptr = ctx->callbacks.init_thread(ctx, 1); - } else { - /* No callback: set user pointer to NULL */ - tls.user_ptr = NULL; - } - - /* Connection structure has been pre-allocated */ - thread_index = (int)(conn - ctx->worker_connections); - if ((thread_index < 0) - || ((unsigned)thread_index >= (unsigned)ctx->cfg_max_worker_threads)) { - mg_cry_ctx_internal(ctx, - "Internal error: Invalid worker index %i", - thread_index); - return; - } - - /* Request buffers are not pre-allocated. They are private to the - * request and do not contain any state information that might be - * of interest to anyone observing a server status. */ - conn->buf = (char *)mg_malloc_ctx(ctx->max_request_size, conn->phys_ctx); - if (conn->buf == NULL) { - mg_cry_ctx_internal( - ctx, - "Out of memory: Cannot allocate buffer for worker %i", - thread_index); - return; - } - conn->buf_size = (int)ctx->max_request_size; - - conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */ - - conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */ - - conn->request_info.user_data = ctx->user_data; - /* Allocate a mutex for this connection to allow communication both - * within the request handler and from elsewhere in the application - */ - if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) { - mg_free(conn->buf); - mg_cry_ctx_internal(ctx, "%s", "Cannot create mutex"); - return; - } - -#if defined(USE_SERVER_STATS) - conn->conn_state = 1; /* not consumed */ -#endif - - /* Call consume_socket() even when ctx->stop_flag > 0, to let it - * signal sq_empty condvar to wake up the master waiting in - * produce_socket() */ - while (consume_socket( - ctx, &conn->client, thread_index, first_call_to_consume_socket)) { - first_call_to_consume_socket = 0; - - /* New connections must start with new protocol negotiation */ - tls.alpn_proto = NULL; - -#if defined(USE_SERVER_STATS) - conn->conn_close_time = 0; -#endif - conn->conn_birth_time = time(NULL); - - /* Fill in IP, port info early so even if SSL setup below fails, - * error handler would have the corresponding info. - * Thanks to Johannes Winkelmann for the patch. - */ - conn->request_info.remote_port = - ntohs(USA_IN_PORT_UNSAFE(&conn->client.rsa)); - - conn->request_info.server_port = - ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)); - - sockaddr_to_string(conn->request_info.remote_addr, - sizeof(conn->request_info.remote_addr), - &conn->client.rsa); - - DEBUG_TRACE("Incoming %sconnection from %s", - (conn->client.is_ssl ? "SSL " : ""), - conn->request_info.remote_addr); - - conn->request_info.is_ssl = conn->client.is_ssl; - - if (conn->client.is_ssl) { - -#if defined(USE_MBEDTLS) - /* HTTPS connection */ - if (mbed_ssl_accept(&(conn->ssl), - conn->dom_ctx->ssl_ctx, - (int *)&(conn->client.sock), - conn->phys_ctx) - == 0) { - /* conn->dom_ctx is set in get_request */ - /* process HTTPS connection */ - init_connection(conn); - conn->connection_type = CONNECTION_TYPE_REQUEST; - conn->protocol_type = PROTOCOL_TYPE_HTTP1; - process_new_connection(conn); - } else { - /* make sure the connection is cleaned up on SSL failure */ - close_connection(conn); - } - -#elif defined(USE_GNUTLS) - /* HTTPS connection */ - if (gtls_ssl_accept(&(conn->ssl), - conn->dom_ctx->ssl_ctx, - conn->client.sock, - conn->phys_ctx) - == 0) { - /* conn->dom_ctx is set in get_request */ - /* process HTTPS connection */ - init_connection(conn); - conn->connection_type = CONNECTION_TYPE_REQUEST; - conn->protocol_type = PROTOCOL_TYPE_HTTP1; - process_new_connection(conn); - } else { - /* make sure the connection is cleaned up on SSL failure */ - close_connection(conn); - } - -#elif !defined(NO_SSL) - /* HTTPS connection */ - if (sslize(conn, SSL_accept, NULL)) { - /* conn->dom_ctx is set in get_request */ - - /* Get SSL client certificate information (if set) */ - struct mg_client_cert client_cert; - if (ssl_get_client_cert_info(conn, &client_cert)) { - conn->request_info.client_cert = &client_cert; - } - - /* process HTTPS connection */ -#if defined(USE_HTTP2) - if ((tls.alpn_proto != NULL) - && (!memcmp(tls.alpn_proto, "\x02h2", 3))) { - /* process HTTPS/2 connection */ - init_connection(conn); - conn->connection_type = CONNECTION_TYPE_REQUEST; - conn->protocol_type = PROTOCOL_TYPE_HTTP2; - conn->content_len = - -1; /* content length is not predefined */ - conn->is_chunked = 0; /* HTTP2 is never chunked */ - process_new_http2_connection(conn); - } else -#endif - { - /* process HTTPS/1.x or WEBSOCKET-SECURE connection */ - init_connection(conn); - conn->connection_type = CONNECTION_TYPE_REQUEST; - /* Start with HTTP, WS will be an "upgrade" request later */ - conn->protocol_type = PROTOCOL_TYPE_HTTP1; - process_new_connection(conn); - } - - /* Free client certificate info */ - if (conn->request_info.client_cert) { - mg_free((void *)(conn->request_info.client_cert->subject)); - mg_free((void *)(conn->request_info.client_cert->issuer)); - mg_free((void *)(conn->request_info.client_cert->serial)); - mg_free((void *)(conn->request_info.client_cert->finger)); - /* Free certificate memory */ - X509_free( - (X509 *)conn->request_info.client_cert->peer_cert); - conn->request_info.client_cert->peer_cert = 0; - conn->request_info.client_cert->subject = 0; - conn->request_info.client_cert->issuer = 0; - conn->request_info.client_cert->serial = 0; - conn->request_info.client_cert->finger = 0; - conn->request_info.client_cert = 0; - } - } else { - /* make sure the connection is cleaned up on SSL failure */ - close_connection(conn); - } -#endif - - } else { - /* process HTTP connection */ - init_connection(conn); - conn->connection_type = CONNECTION_TYPE_REQUEST; - /* Start with HTTP, WS will be an "upgrade" request later */ - conn->protocol_type = PROTOCOL_TYPE_HTTP1; - process_new_connection(conn); - } - - DEBUG_TRACE("%s", "Connection closed"); - -#if defined(USE_SERVER_STATS) - conn->conn_close_time = time(NULL); -#endif - } - - /* Call exit thread user callback */ - if (ctx->callbacks.exit_thread) { - ctx->callbacks.exit_thread(ctx, 1, tls.user_ptr); - } - - /* delete thread local storage objects */ - pthread_setspecific(sTlsKey, NULL); -#if defined(_WIN32) - CloseHandle(tls.pthread_cond_helper_mutex); -#endif - pthread_mutex_destroy(&conn->mutex); - - /* Free the request buffer. */ - conn->buf_size = 0; - mg_free(conn->buf); - conn->buf = NULL; - - /* Free cleaned URI (if any) */ - if (conn->request_info.local_uri != conn->request_info.local_uri_raw) { - mg_free((void *)conn->request_info.local_uri); - conn->request_info.local_uri = NULL; - } - -#if defined(USE_SERVER_STATS) - conn->conn_state = 9; /* done */ -#endif - - DEBUG_TRACE("%s", "exiting"); -} - - -/* Threads have different return types on Windows and Unix. */ -#if defined(_WIN32) -static unsigned __stdcall worker_thread(void *thread_func_param) -{ - worker_thread_run((struct mg_connection *)thread_func_param); - return 0; -} -#else -static void * -worker_thread(void *thread_func_param) -{ -#if !defined(__ZEPHYR__) - struct sigaction sa; - - /* Ignore SIGPIPE */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); -#endif - - worker_thread_run((struct mg_connection *)thread_func_param); - return NULL; -} -#endif /* _WIN32 */ - - -/* This is an internal function, thus all arguments are expected to be - * valid - a NULL check is not required. */ -static void -accept_new_connection(const struct socket *listener, struct mg_context *ctx) -{ - struct socket so; - char src_addr[IP_ADDR_STR_LEN]; - socklen_t len = sizeof(so.rsa); -#if !defined(__ZEPHYR__) - int on = 1; -#endif - memset(&so, 0, sizeof(so)); - - if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) - == INVALID_SOCKET) { - } else if (check_acl(ctx, &so.rsa) != 1) { - sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa); - mg_cry_ctx_internal(ctx, - "%s: %s is not allowed to connect", - __func__, - src_addr); - closesocket(so.sock); - } else { - /* Put so socket structure into the queue */ - DEBUG_TRACE("Accepted socket %d", (int)so.sock); - set_close_on_exec(so.sock, NULL, ctx); - so.is_ssl = listener->is_ssl; - so.ssl_redir = listener->ssl_redir; - so.is_optional = listener->is_optional; - if (getsockname(so.sock, &so.lsa.sa, &len) != 0) { - mg_cry_ctx_internal(ctx, - "%s: getsockname() failed: %s", - __func__, - strerror(ERRNO)); - } - -#if !defined(__ZEPHYR__) - if ((so.lsa.sa.sa_family == AF_INET) - || (so.lsa.sa.sa_family == AF_INET6)) { - /* Set TCP keep-alive for TCP sockets (IPv4 and IPv6). - * This is needed because if HTTP-level keep-alive - * is enabled, and client resets the connection, server won't get - * TCP FIN or RST and will keep the connection open forever. With - * TCP keep-alive, next keep-alive handshake will figure out that - * the client is down and will close the server end. - * Thanks to Igor Klopov who suggested the patch. */ - if (setsockopt(so.sock, - SOL_SOCKET, - SO_KEEPALIVE, - (SOCK_OPT_TYPE)&on, - sizeof(on)) - != 0) { - mg_cry_ctx_internal( - ctx, - "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s", - __func__, - strerror(ERRNO)); - } - } -#endif - - /* Disable TCP Nagle's algorithm. Normally TCP packets are coalesced - * to effectively fill up the underlying IP packet payload and - * reduce the overhead of sending lots of small buffers. However - * this hurts the server's throughput (ie. operations per second) - * when HTTP 1.1 persistent connections are used and the responses - * are relatively small (eg. less than 1400 bytes). - */ - if ((ctx->dd.config[CONFIG_TCP_NODELAY] != NULL) - && (!strcmp(ctx->dd.config[CONFIG_TCP_NODELAY], "1"))) { - if (set_tcp_nodelay(&so, 1) != 0) { - mg_cry_ctx_internal( - ctx, - "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s", - __func__, - strerror(ERRNO)); - } - } - - /* The "non blocking" property should already be - * inherited from the parent socket. Set it for - * non-compliant socket implementations. */ - set_non_blocking_mode(so.sock); - - so.in_use = 0; - produce_socket(ctx, &so); - } -} - - -static void -master_thread_run(struct mg_context *ctx) -{ - struct mg_workerTLS tls; - struct mg_pollfd *pfd; - unsigned int i; - unsigned int workerthreadcount; - - if (!ctx || !ctx->listening_socket_fds) { - return; - } - - mg_set_thread_name("master"); - - /* Increase priority of the master thread */ -#if defined(_WIN32) - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); -#elif defined(USE_MASTER_THREAD_PRIORITY) - int min_prio = sched_get_priority_min(SCHED_RR); - int max_prio = sched_get_priority_max(SCHED_RR); - if ((min_prio >= 0) && (max_prio >= 0) - && ((USE_MASTER_THREAD_PRIORITY) <= max_prio) - && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) { - struct sched_param sched_param = {0}; - sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY); - pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param); - } -#endif - - /* Initialize thread local storage */ -#if defined(_WIN32) - tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL); -#endif - tls.is_master = 1; - pthread_setspecific(sTlsKey, &tls); - - if (ctx->callbacks.init_thread) { - /* Callback for the master thread (type 0) */ - tls.user_ptr = ctx->callbacks.init_thread(ctx, 0); - } else { - tls.user_ptr = NULL; - } - - /* Lua background script "start" event */ -#if defined(USE_LUA) - if (ctx->lua_background_state) { - lua_State *lstate = (lua_State *)ctx->lua_background_state; - pthread_mutex_lock(&ctx->lua_bg_mutex); - - /* call "start()" in Lua */ - lua_getglobal(lstate, "start"); - if (lua_type(lstate, -1) == LUA_TFUNCTION) { - int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0); - if (ret != 0) { - struct mg_connection fc; - lua_cry(fake_connection(&fc, ctx), - ret, - lstate, - "lua_background_script", - "start"); - } - } else { - lua_pop(lstate, 1); - } - - /* determine if there is a "log()" function in Lua background script */ - lua_getglobal(lstate, "log"); - if (lua_type(lstate, -1) == LUA_TFUNCTION) { - ctx->lua_bg_log_available = 1; - } - lua_pop(lstate, 1); - - pthread_mutex_unlock(&ctx->lua_bg_mutex); - } -#endif - - /* Server starts *now* */ - ctx->start_time = time(NULL); - - /* Server accept loop */ - pfd = ctx->listening_socket_fds; - while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) { - for (i = 0; i < ctx->num_listening_sockets; i++) { - pfd[i].fd = ctx->listening_sockets[i].sock; - pfd[i].events = POLLIN; - } - - /* We listen on this socket just so that mg_stop() can cause mg_poll() - * to return ASAP. Don't worry, we did allocate an extra slot at the end - * of listening_socket_fds[] just to hold this - */ - pfd[ctx->num_listening_sockets].fd = - ctx->thread_shutdown_notification_socket; - pfd[ctx->num_listening_sockets].events = POLLIN; - - if (mg_poll(pfd, - ctx->num_listening_sockets - + 1, // +1 for the thread_shutdown_notification_socket - SOCKET_TIMEOUT_QUANTUM, - &(ctx->stop_flag)) - > 0) { - for (i = 0; i < ctx->num_listening_sockets; i++) { - /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the - * successful poll, and POLLIN is defined as - * (POLLRDNORM | POLLRDBAND) - * Therefore, we're checking pfd[i].revents & POLLIN, not - * pfd[i].revents == POLLIN. */ - if (STOP_FLAG_IS_ZERO(&ctx->stop_flag) - && (pfd[i].revents & POLLIN)) { - accept_new_connection(&ctx->listening_sockets[i], ctx); - } - } - } - } - - /* Here stop_flag is 1 - Initiate shutdown. */ - DEBUG_TRACE("%s", "stopping workers"); - - /* Stop signal received: somebody called mg_stop. Quit. */ - close_all_listening_sockets(ctx); - - /* Wakeup workers that are waiting for connections to handle. */ -#if defined(ALTERNATIVE_QUEUE) - for (i = 0; i < ctx->spawned_worker_threads; i++) { - event_signal(ctx->client_wait_events[i]); - } -#else - (void)pthread_mutex_lock(&ctx->thread_mutex); - pthread_cond_broadcast(&ctx->sq_full); - (void)pthread_mutex_unlock(&ctx->thread_mutex); -#endif - - /* Join all worker threads to avoid leaking threads. */ - workerthreadcount = ctx->spawned_worker_threads; - for (i = 0; i < workerthreadcount; i++) { - if (ctx->worker_threadids[i] != 0) { - mg_join_thread(ctx->worker_threadids[i]); - } - } - -#if defined(USE_LUA) - /* Free Lua state of lua background task */ - if (ctx->lua_background_state) { - lua_State *lstate = (lua_State *)ctx->lua_background_state; - ctx->lua_bg_log_available = 0; - - /* call "stop()" in Lua */ - pthread_mutex_lock(&ctx->lua_bg_mutex); - lua_getglobal(lstate, "stop"); - if (lua_type(lstate, -1) == LUA_TFUNCTION) { - int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0); - if (ret != 0) { - struct mg_connection fc; - lua_cry(fake_connection(&fc, ctx), - ret, - lstate, - "lua_background_script", - "stop"); - } - } - DEBUG_TRACE("Close Lua background state %p", lstate); - lua_close(lstate); - - ctx->lua_background_state = 0; - pthread_mutex_unlock(&ctx->lua_bg_mutex); - } -#endif - - DEBUG_TRACE("%s", "exiting"); - - /* call exit thread callback */ - if (ctx->callbacks.exit_thread) { - /* Callback for the master thread (type 0) */ - ctx->callbacks.exit_thread(ctx, 0, tls.user_ptr); - } - -#if defined(_WIN32) - CloseHandle(tls.pthread_cond_helper_mutex); -#endif - pthread_setspecific(sTlsKey, NULL); - - /* Signal mg_stop() that we're done. - * WARNING: This must be the very last thing this - * thread does, as ctx becomes invalid after this line. */ - STOP_FLAG_ASSIGN(&ctx->stop_flag, 2); -} - - -/* Threads have different return types on Windows and Unix. */ -#if defined(_WIN32) -static unsigned __stdcall master_thread(void *thread_func_param) -{ - master_thread_run((struct mg_context *)thread_func_param); - return 0; -} -#else -static void * -master_thread(void *thread_func_param) -{ -#if !defined(__ZEPHYR__) - struct sigaction sa; - - /* Ignore SIGPIPE */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); -#endif - - master_thread_run((struct mg_context *)thread_func_param); - return NULL; -} -#endif /* _WIN32 */ - - -static void -free_context(struct mg_context *ctx) -{ - int i; - struct mg_handler_info *tmp_rh; - - if (ctx == NULL) { - return; - } - - /* Call user callback */ - if (ctx->callbacks.exit_context) { - ctx->callbacks.exit_context(ctx); - } - - /* All threads exited, no sync is needed. Destroy thread mutex and - * condvars - */ - (void)pthread_mutex_destroy(&ctx->thread_mutex); - -#if defined(ALTERNATIVE_QUEUE) - mg_free(ctx->client_socks); - if (ctx->client_wait_events != NULL) { - for (i = 0; (unsigned)i < ctx->spawned_worker_threads; i++) { - event_destroy(ctx->client_wait_events[i]); - } - mg_free(ctx->client_wait_events); - } -#else - (void)pthread_cond_destroy(&ctx->sq_empty); - (void)pthread_cond_destroy(&ctx->sq_full); - mg_free(ctx->squeue); -#endif - - /* Destroy other context global data structures mutex */ - (void)pthread_mutex_destroy(&ctx->nonce_mutex); - -#if defined(USE_LUA) - (void)pthread_mutex_destroy(&ctx->lua_bg_mutex); -#endif - - /* Deallocate shutdown-triggering socket-pair */ - if (ctx->user_shutdown_notification_socket >= 0) { - closesocket(ctx->user_shutdown_notification_socket); - } - if (ctx->thread_shutdown_notification_socket >= 0) { - closesocket(ctx->thread_shutdown_notification_socket); - } - - /* Deallocate config parameters */ - for (i = 0; i < NUM_OPTIONS; i++) { - if (ctx->dd.config[i] != NULL) { -#if defined(_MSC_VER) -#pragma warning(suppress : 6001) -#endif - mg_free(ctx->dd.config[i]); - } - } - - /* Deallocate request handlers */ - while (ctx->dd.handlers) { - tmp_rh = ctx->dd.handlers; - ctx->dd.handlers = tmp_rh->next; - mg_free(tmp_rh->uri); - mg_free(tmp_rh); - } - -#if defined(USE_MBEDTLS) - if (ctx->dd.ssl_ctx != NULL) { - mbed_sslctx_uninit(ctx->dd.ssl_ctx); - mg_free(ctx->dd.ssl_ctx); - ctx->dd.ssl_ctx = NULL; - } - -#elif defined(USE_GNUTLS) - if (ctx->dd.ssl_ctx != NULL) { - gtls_sslctx_uninit(ctx->dd.ssl_ctx); - mg_free(ctx->dd.ssl_ctx); - ctx->dd.ssl_ctx = NULL; - } - -#elif !defined(NO_SSL) - /* Deallocate SSL context */ - if (ctx->dd.ssl_ctx != NULL) { - void *ssl_ctx = (void *)ctx->dd.ssl_ctx; - int callback_ret = - (ctx->callbacks.external_ssl_ctx == NULL) - ? 0 - : (ctx->callbacks.external_ssl_ctx(&ssl_ctx, ctx->user_data)); - - if (callback_ret == 0) { - SSL_CTX_free(ctx->dd.ssl_ctx); - } - /* else: ignore error and omit SSL_CTX_free in case - * callback_ret is 1 */ - } -#endif /* !NO_SSL */ - - /* Deallocate worker thread ID array */ - mg_free(ctx->worker_threadids); - - /* Deallocate worker thread ID array */ - mg_free(ctx->worker_connections); - - /* deallocate system name string */ - mg_free(ctx->systemName); - - /* Deallocate context itself */ - mg_free(ctx); -} - - -CIVETWEB_API void -mg_stop(struct mg_context *ctx) -{ - pthread_t mt; - if (!ctx) { - return; - } - - /* We don't use a lock here. Calling mg_stop with the same ctx from - * two threads is not allowed. */ - mt = ctx->masterthreadid; - if (mt == 0) { - return; - } - - ctx->masterthreadid = 0; - - /* Set stop flag, so all threads know they have to exit. */ - STOP_FLAG_ASSIGN(&ctx->stop_flag, 1); - - /* Closing this socket will cause mg_poll() in all the I/O threads to return - * immediately */ - closesocket(ctx->user_shutdown_notification_socket); - ctx->user_shutdown_notification_socket = - -1; /* to avoid calling closesocket() again in free_context() */ - - /* Join timer thread */ -#if defined(USE_TIMERS) - timers_exit(ctx); -#endif - - /* Wait until everything has stopped. */ - while (!STOP_FLAG_IS_TWO(&ctx->stop_flag)) { - (void)mg_sleep(10); - } - - /* Wait to stop master thread */ - mg_join_thread(mt); - - /* Close remaining Lua states */ -#if defined(USE_LUA) - lua_ctx_exit(ctx); -#endif - - /* Free memory */ - free_context(ctx); -} - - -static void -get_system_name(char **sysName) -{ -#if defined(_WIN32) - char name[128]; - DWORD dwVersion = 0; - DWORD dwMajorVersion = 0; - DWORD dwMinorVersion = 0; - DWORD dwBuild = 0; - BOOL wowRet, isWoW = FALSE; - -#if defined(_MSC_VER) -#pragma warning(push) - /* GetVersion was declared deprecated */ -#pragma warning(disable : 4996) -#endif - dwVersion = GetVersion(); -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - - dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); - dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); - dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0); - (void)dwBuild; - - wowRet = IsWow64Process(GetCurrentProcess(), &isWoW); - - sprintf(name, - "Windows %u.%u%s", - (unsigned)dwMajorVersion, - (unsigned)dwMinorVersion, - (wowRet ? (isWoW ? " (WoW64)" : "") : " (?)")); - - *sysName = mg_strdup(name); - -#elif defined(__rtems__) - *sysName = mg_strdup("RTEMS"); -#elif defined(__ZEPHYR__) - *sysName = mg_strdup("Zephyr OS"); -#else - struct utsname name; - memset(&name, 0, sizeof(name)); - uname(&name); - *sysName = mg_strdup(name.sysname); -#endif -} - - -static void -legacy_init(const char **options) -{ - const char *ports_option = config_options[LISTENING_PORTS].default_value; - - if (options) { - const char **run_options = options; - const char *optname = config_options[LISTENING_PORTS].name; - - /* Try to find the "listening_ports" option */ - while (*run_options) { - if (!strcmp(*run_options, optname)) { - ports_option = run_options[1]; - } - run_options += 2; - } - } - - if (is_ssl_port_used(ports_option)) { - /* Initialize with SSL support */ - mg_init_library(MG_FEATURES_TLS); - } else { - /* Initialize without SSL support */ - mg_init_library(MG_FEATURES_DEFAULT); - } -} - -/* we'll assume it's only Windows that doesn't have socketpair() available */ -#if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32) -#define HAVE_SOCKETPAIR 1 -#endif - -static int -mg_socketpair(int *sockA, int *sockB) -{ - int temp[2] = {-1, -1}; - int asock = -1; - - /** Default to unallocated */ - *sockA = -1; - *sockB = -1; - -#if defined(HAVE_SOCKETPAIR) - int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, temp); - if (ret == 0) { - *sockA = temp[0]; - *sockB = temp[1]; - set_close_on_exec(*sockA, NULL, NULL); - set_close_on_exec(*sockB, NULL, NULL); - } - (void)asock; /* not used */ - return ret; -#else - /** No socketpair() call is available, so we'll have to roll our own - * implementation */ - asock = socket(PF_INET, SOCK_STREAM, 0); - if (asock >= 0) { - struct sockaddr_in addr; - struct sockaddr *pa = (struct sockaddr *)&addr; - socklen_t addrLen = sizeof(addr); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_port = 0; - - if ((bind(asock, pa, sizeof(addr)) == 0) - && (getsockname(asock, pa, &addrLen) == 0) - && (listen(asock, 1) == 0)) { - temp[0] = socket(PF_INET, SOCK_STREAM, 0); - if ((temp[0] >= 0) && (connect(temp[0], pa, sizeof(addr)) == 0)) { - temp[1] = accept(asock, pa, &addrLen); - if (temp[1] >= 0) { - closesocket(asock); - *sockA = temp[0]; - *sockB = temp[1]; - set_close_on_exec(*sockA, NULL, NULL); - set_close_on_exec(*sockB, NULL, NULL); - return 0; /* success! */ - } - } - } - } - - /* Cleanup */ - if (asock >= 0) - closesocket(asock); - if (temp[0] >= 0) - closesocket(temp[0]); - if (temp[1] >= 0) - closesocket(temp[1]); - return -1; /* fail! */ -#endif -} - -static int -mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads) -{ - const unsigned int i = ctx->spawned_worker_threads; - if (i >= ctx->cfg_max_worker_threads) { - return -1; /* Oops, we hit our worker-thread limit! No more worker - threads, ever! */ - } - - (void)pthread_mutex_lock(&ctx->thread_mutex); -#if defined(ALTERNATIVE_QUEUE) - if ((only_if_no_idle_threads) && (ctx->idle_worker_thread_count > 0)) { -#else - if ((only_if_no_idle_threads) - && (ctx->idle_worker_thread_count - > (unsigned)(ctx->sq_head - ctx->sq_tail))) { -#endif - (void)pthread_mutex_unlock(&ctx->thread_mutex); - return -2; /* There are idle threads available, so no need to spawn a - new worker thread now */ - } - ctx->idle_worker_thread_count++; /* we do this here to avoid a race - condition while the thread is starting - up */ - (void)pthread_mutex_unlock(&ctx->thread_mutex); - - ctx->worker_connections[i].phys_ctx = ctx; - int ret = mg_start_thread_with_id(worker_thread, - &ctx->worker_connections[i], - &ctx->worker_threadids[i]); - if (ret == 0) { - ctx->spawned_worker_threads++; /* note that we've filled another slot in - the table */ - DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads); - } else { - (void)pthread_mutex_lock(&ctx->thread_mutex); - ctx->idle_worker_thread_count--; /* whoops, roll-back on error */ - (void)pthread_mutex_unlock(&ctx->thread_mutex); - } - return ret; -} - -CIVETWEB_API struct mg_context * -mg_start2(struct mg_init_data *init, struct mg_error_data *error) -{ - struct mg_context *ctx; - const char *name, *value, *default_value; - int idx, ok, prespawnthreadcount, workerthreadcount; - unsigned int i; - int itmp; - void (*exit_callback)(const struct mg_context *ctx) = 0; - const char **options = - ((init != NULL) ? (init->configuration_options) : (NULL)); - - struct mg_workerTLS tls; - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OK; - error->code_sub = 0; - if (error->text_buffer_size > 0) { - *error->text = 0; - } - } - - if (mg_init_library_called == 0) { - /* Legacy INIT, if mg_start is called without mg_init_library. - * Note: This will cause a memory leak when unloading the library. - */ - legacy_init(options); - } - if (mg_init_library_called == 0) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - "Library uninitialized"); - } - return NULL; - } - - /* Allocate context and initialize reasonable general case defaults. */ - ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx)); - if (ctx == NULL) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)sizeof(*ctx); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - "Out of memory"); - } - return NULL; - } - - /* Random number generator will initialize at the first call */ - ctx->dd.auth_nonce_mask = - (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options); - - /* Save started thread index to reuse in other external API calls - * For the sake of thread synchronization all non-civetweb threads - * can be considered as single external thread */ - ctx->starter_thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max); - tls.is_master = -1; /* Thread calling mg_start */ - tls.thread_idx = ctx->starter_thread_idx; -#if defined(_WIN32) - tls.pthread_cond_helper_mutex = NULL; -#endif - pthread_setspecific(sTlsKey, &tls); - - ok = (0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr)); -#if !defined(ALTERNATIVE_QUEUE) - ok &= (0 == pthread_cond_init(&ctx->sq_empty, NULL)); - ok &= (0 == pthread_cond_init(&ctx->sq_full, NULL)); - ctx->sq_blocked = 0; -#endif - ok &= (0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr)); -#if defined(USE_LUA) - ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr)); -#endif - - /** mg_stop() will close the user_shutdown_notification_socket, and that - * will cause poll() to return immediately in the master-thread, so that - * mg_stop() can also return immediately. - */ - ok &= (0 - == mg_socketpair(&ctx->user_shutdown_notification_socket, - &ctx->thread_shutdown_notification_socket)); - - if (!ok) { - unsigned error_id = (unsigned)ERRNO; - const char *err_msg = - "Cannot initialize thread synchronization objects"; - /* Fatal error - abort start. However, this situation should never - * occur in practice. */ - - mg_cry_ctx_internal(ctx, "%s", err_msg); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OS_ERROR; - error->code_sub = error_id; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - mg_free(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - - if ((init != NULL) && (init->callbacks != NULL)) { - /* Set all callbacks except exit_context. */ - ctx->callbacks = *init->callbacks; - exit_callback = init->callbacks->exit_context; - /* The exit callback is activated once the context is successfully - * created. It should not be called, if an incomplete context object - * is deleted during a failed initialization. */ - ctx->callbacks.exit_context = 0; - } - ctx->user_data = ((init != NULL) ? (init->user_data) : (NULL)); - ctx->dd.handlers = NULL; - ctx->dd.next = NULL; - -#if defined(USE_LUA) - lua_ctx_init(ctx); -#endif - - /* Store options */ - while (options && (name = *options++) != NULL) { - idx = get_option_index(name); - if (idx == -1) { - mg_cry_ctx_internal(ctx, "Invalid option: %s", name); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = (unsigned)-1; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid configuration option: %s", - name); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - - } else if ((value = *options++) == NULL) { - mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = (unsigned)idx; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid configuration option value: %s", - name); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - if (ctx->dd.config[idx] != NULL) { - /* A duplicate configuration option is not an error - the last - * option value will be used. */ - mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name); - mg_free(ctx->dd.config[idx]); - } - ctx->dd.config[idx] = mg_strdup_ctx(value, ctx); - DEBUG_TRACE("[%s] -> [%s]", name, value); - } - - /* Set default value if needed */ - for (i = 0; config_options[i].name != NULL; i++) { - default_value = config_options[i].default_value; - if ((ctx->dd.config[i] == NULL) && (default_value != NULL)) { - ctx->dd.config[i] = mg_strdup_ctx(default_value, ctx); - } - } - - /* Request size option */ - itmp = atoi(ctx->dd.config[MAX_REQUEST_SIZE]); - if (itmp < 1024) { - mg_cry_ctx_internal(ctx, - "%s too small", - config_options[MAX_REQUEST_SIZE].name); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = (unsigned)MAX_REQUEST_SIZE; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid configuration option value: %s", - config_options[MAX_REQUEST_SIZE].name); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - ctx->max_request_size = (unsigned)itmp; - - /* Queue length */ -#if !defined(ALTERNATIVE_QUEUE) - itmp = atoi(ctx->dd.config[CONNECTION_QUEUE_SIZE]); - if (itmp < 1) { - mg_cry_ctx_internal(ctx, - "%s too small", - config_options[CONNECTION_QUEUE_SIZE].name); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = CONNECTION_QUEUE_SIZE; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid configuration option value: %s", - config_options[CONNECTION_QUEUE_SIZE].name); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - ctx->squeue = - (struct socket *)mg_calloc((unsigned int)itmp, sizeof(struct socket)); - if (ctx->squeue == NULL) { - mg_cry_ctx_internal(ctx, - "Out of memory: Cannot allocate %s", - config_options[CONNECTION_QUEUE_SIZE].name); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)itmp * (unsigned)sizeof(struct socket); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Out of memory: Cannot allocate %s", - config_options[CONNECTION_QUEUE_SIZE].name); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - ctx->sq_size = itmp; -#endif - - /* Worker thread count option */ - workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]); - prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]); - - if ((prespawnthreadcount < 0) - || (prespawnthreadcount > workerthreadcount)) { - prespawnthreadcount = - workerthreadcount; /* can't prespawn more than all of them! */ - } - - if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) { - if (workerthreadcount <= 0) { - mg_cry_ctx_internal(ctx, "%s", "Invalid number of worker threads"); - } else { - mg_cry_ctx_internal(ctx, "%s", "Too many worker threads"); - } - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = NUM_THREADS; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid configuration option value: %s", - config_options[NUM_THREADS].name); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - - /* Document root */ -#if defined(NO_FILES) - if (ctx->dd.config[DOCUMENT_ROOT] != NULL) { - mg_cry_ctx_internal(ctx, "%s", "Document root must not be set"); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = (unsigned)DOCUMENT_ROOT; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid configuration option value: %s", - config_options[DOCUMENT_ROOT].name); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } -#endif - - get_system_name(&ctx->systemName); - -#if defined(USE_LUA) - /* If a Lua background script has been configured, start it. */ - ctx->lua_bg_log_available = 0; - if (ctx->dd.config[LUA_BACKGROUND_SCRIPT] != NULL) { - char ebuf[256]; - struct vec opt_vec; - struct vec eq_vec; - const char *sparams; - - memset(ebuf, 0, sizeof(ebuf)); - pthread_mutex_lock(&ctx->lua_bg_mutex); - - /* Create a Lua state, load all standard libraries and the mg table */ - lua_State *state = mg_lua_context_script_prepare( - ctx->dd.config[LUA_BACKGROUND_SCRIPT], ctx, ebuf, sizeof(ebuf)); - if (!state) { - mg_cry_ctx_internal(ctx, - "lua_background_script load error: %s", - ebuf); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_SCRIPT_ERROR; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Error in script %s: %s", - config_options[LUA_BACKGROUND_SCRIPT].name, - ebuf); - } - - pthread_mutex_unlock(&ctx->lua_bg_mutex); - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - - /* Add a table with parameters into mg.params */ - sparams = ctx->dd.config[LUA_BACKGROUND_SCRIPT_PARAMS]; - if (sparams && sparams[0]) { - lua_getglobal(state, "mg"); - lua_pushstring(state, "params"); - lua_newtable(state); - - while ((sparams = next_option(sparams, &opt_vec, &eq_vec)) - != NULL) { - reg_llstring( - state, opt_vec.ptr, opt_vec.len, eq_vec.ptr, eq_vec.len); - if (mg_strncasecmp(sparams, opt_vec.ptr, opt_vec.len) == 0) - break; - } - lua_rawset(state, -3); - lua_pop(state, 1); - } - - /* Call script */ - state = mg_lua_context_script_run(state, - ctx->dd.config[LUA_BACKGROUND_SCRIPT], - ctx, - ebuf, - sizeof(ebuf)); - if (!state) { - mg_cry_ctx_internal(ctx, - "lua_background_script start error: %s", - ebuf); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_SCRIPT_ERROR; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Error in script %s: %s", - config_options[DOCUMENT_ROOT].name, - ebuf); - } - pthread_mutex_unlock(&ctx->lua_bg_mutex); - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - - /* state remains valid */ - ctx->lua_background_state = (void *)state; - pthread_mutex_unlock(&ctx->lua_bg_mutex); - - } else { - ctx->lua_background_state = 0; - } -#endif - - /* Step by step initialization of ctx - depending on build options */ -#if !defined(NO_FILESYSTEMS) - if (!set_gpass_option(ctx, NULL)) { - const char *err_msg = "Invalid global password file"; - /* Fatal error - abort start. */ - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_PASS_FILE; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } -#endif - -#if defined(USE_MBEDTLS) || defined(USE_GNUTLS) - if (!mg_sslctx_init(ctx, NULL)) { - const char *err_msg = "Error initializing SSL context"; - /* Fatal error - abort start. */ - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - -#elif !defined(NO_SSL) - if (!init_ssl_ctx(ctx, NULL)) { - const char *err_msg = "Error initializing SSL context"; - /* Fatal error - abort start. */ - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } -#endif - - if (!set_ports_option(ctx)) { - const char *err_msg = "Failed to setup server ports"; - /* Fatal error - abort start. */ - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_PORTS_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - -#if !defined(_WIN32) && !defined(__ZEPHYR__) - if (!set_uid_option(ctx)) { - const char *err_msg = "Failed to run as configured user"; - /* Fatal error - abort start. */ - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_USER_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } -#endif - - if (!set_acl_option(ctx)) { - const char *err_msg = "Failed to setup access control list"; - /* Fatal error - abort start. */ - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_ACL_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - - ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount)); - ctx->worker_threadids = - (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(pthread_t), - ctx); - - if (ctx->worker_threadids == NULL) { - const char *err_msg = "Not enough memory for worker thread ID array"; - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)ctx->cfg_max_worker_threads - * (unsigned)sizeof(pthread_t); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - ctx->worker_connections = - (struct mg_connection *)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(struct mg_connection), - ctx); - if (ctx->worker_connections == NULL) { - const char *err_msg = - "Not enough memory for worker thread connection array"; - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)ctx->cfg_max_worker_threads - * (unsigned)sizeof(struct mg_connection); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - -#if defined(ALTERNATIVE_QUEUE) - ctx->client_wait_events = - (void **)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(ctx->client_wait_events[0]), - ctx); - if (ctx->client_wait_events == NULL) { - const char *err_msg = "Not enough memory for worker event array"; - mg_cry_ctx_internal(ctx, "%s", err_msg); - mg_free(ctx->worker_threadids); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)ctx->cfg_max_worker_threads - * (unsigned)sizeof(ctx->client_wait_events[0]); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - - ctx->client_socks = - (struct socket *)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(ctx->client_socks[0]), - ctx); - if (ctx->client_socks == NULL) { - const char *err_msg = "Not enough memory for worker socket array"; - mg_cry_ctx_internal(ctx, "%s", err_msg); - mg_free(ctx->client_wait_events); - mg_free(ctx->worker_threadids); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)ctx->cfg_max_worker_threads - * (unsigned)sizeof(ctx->client_socks[0]); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - - for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) { - ctx->client_wait_events[i] = event_create(); - if (ctx->client_wait_events[i] == 0) { - const char *err_msg = "Error creating worker event %i"; - mg_cry_ctx_internal(ctx, err_msg, i); - while (i > 0) { - i--; - event_destroy(ctx->client_wait_events[i]); - } - mg_free(ctx->client_socks); - mg_free(ctx->client_wait_events); - mg_free(ctx->worker_threadids); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OS_ERROR; - error->code_sub = (unsigned)ERRNO; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - err_msg, - i); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - } -#endif - -#if defined(USE_TIMERS) - if (timers_init(ctx) != 0) { - const char *err_msg = "Error creating timers"; - mg_cry_ctx_internal(ctx, "%s", err_msg); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OS_ERROR; - error->code_sub = (unsigned)ERRNO; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - err_msg); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } -#endif - - /* Context has been created - init user libraries */ - if (ctx->callbacks.init_context) { - ctx->callbacks.init_context(ctx); - } - - /* From now, the context is successfully created. - * When it is destroyed, the exit callback should be called. */ - ctx->callbacks.exit_context = exit_callback; - ctx->context_type = CONTEXT_SERVER; /* server context */ - - /* Start worker threads */ - for (i = 0; (int)i < prespawnthreadcount; i++) { - /* worker_thread sets up the other fields */ - if (mg_start_worker_thread(ctx, 0) != 0) { - long error_no = (long)ERRNO; - - /* thread was not created */ - if (ctx->spawned_worker_threads > 0) { - /* If the second, third, ... thread cannot be created, set a - * warning, but keep running. */ - mg_cry_ctx_internal(ctx, - "Cannot start worker thread %i: error %ld", - ctx->spawned_worker_threads + 1, - error_no); - - /* If the server initialization should stop here, all - * threads that have already been created must be stopped - * first, before any free_context(ctx) call. - */ - - } else { - /* If the first worker thread cannot be created, stop - * initialization and free the entire server context. */ - mg_cry_ctx_internal(ctx, - "Cannot create threads: error %ld", - error_no); - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OS_ERROR; - error->code_sub = (unsigned)error_no; - mg_snprintf( - NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Cannot create first worker thread: error %ld", - error_no); - } - - free_context(ctx); - pthread_setspecific(sTlsKey, NULL); - return NULL; - } - break; - } - } - - /* Start master (listening) thread */ - mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid); - - pthread_setspecific(sTlsKey, NULL); - return ctx; -} - - -CIVETWEB_API struct mg_context * -mg_start(const struct mg_callbacks *callbacks, - void *user_data, - const char **options) -{ - struct mg_init_data init = {0}; - init.callbacks = callbacks; - init.user_data = user_data; - init.configuration_options = options; - - return mg_start2(&init, NULL); -} - - -/* Add an additional domain to an already running web server. */ -CIVETWEB_API int -mg_start_domain2(struct mg_context *ctx, - const char **options, - struct mg_error_data *error) -{ - const char *name; - const char *value; - const char *default_value; - struct mg_domain_context *new_dom; - struct mg_domain_context *dom; - int idx, i; - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OK; - error->code_sub = 0; - if (error->text_buffer_size > 0) { - *error->text = 0; - } - } - - if ((ctx == NULL) || (options == NULL)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_PARAM; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - "Invalid parameters"); - } - return -1; - } - - if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) { - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_SERVER_STOPPED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - "Server already stopped"); - } - return -7; - } - - new_dom = (struct mg_domain_context *) - mg_calloc_ctx(1, sizeof(struct mg_domain_context), ctx); - - if (!new_dom) { - /* Out of memory */ - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = (unsigned)sizeof(struct mg_domain_context); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - "Out or memory"); - } - return -6; - } - - /* Store options - TODO: unite duplicate code */ - while (options && (name = *options++) != NULL) { - idx = get_option_index(name); - if (idx == -1) { - mg_cry_ctx_internal(ctx, "Invalid option: %s", name); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = (unsigned)-1; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid option: %s", - name); - } - mg_free(new_dom); - return -2; - } else if ((value = *options++) == NULL) { - mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INVALID_OPTION; - error->code_sub = (unsigned)idx; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Invalid option value: %s", - name); - } - mg_free(new_dom); - return -2; - } - if (new_dom->config[idx] != NULL) { - /* Duplicate option: Later values overwrite earlier ones. */ - mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name); - mg_free(new_dom->config[idx]); - } - new_dom->config[idx] = mg_strdup_ctx(value, ctx); - DEBUG_TRACE("[%s] -> [%s]", name, value); - } - - /* Authentication domain is mandatory */ - /* TODO: Maybe use a new option hostname? */ - if (!new_dom->config[AUTHENTICATION_DOMAIN]) { - mg_cry_ctx_internal(ctx, "%s", "authentication domain required"); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_MISSING_OPTION; - error->code_sub = AUTHENTICATION_DOMAIN; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Mandatory option %s missing", - config_options[AUTHENTICATION_DOMAIN].name); - } - mg_free(new_dom); - return -4; - } - - /* Set default value if needed. Take the config value from - * ctx as a default value. */ - for (i = 0; config_options[i].name != NULL; i++) { - default_value = ctx->dd.config[i]; - if ((new_dom->config[i] == NULL) && (default_value != NULL)) { - new_dom->config[i] = mg_strdup_ctx(default_value, ctx); - } - } - - new_dom->handlers = NULL; - new_dom->next = NULL; - new_dom->nonce_count = 0; - new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31); - -#if defined(USE_LUA) && defined(USE_WEBSOCKET) - new_dom->shared_lua_websockets = NULL; -#endif - -#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) - if (!init_ssl_ctx(ctx, new_dom)) { - /* Init SSL failed */ - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "%s", - "Initializing SSL context failed"); - } - mg_free(new_dom); - return -3; - } -#endif - - /* Add element to linked list. */ - mg_lock_context(ctx); - - idx = 0; - dom = &(ctx->dd); - for (;;) { - if (!mg_strcasecmp(new_dom->config[AUTHENTICATION_DOMAIN], - dom->config[AUTHENTICATION_DOMAIN])) { - /* Domain collision */ - mg_cry_ctx_internal(ctx, - "domain %s already in use", - new_dom->config[AUTHENTICATION_DOMAIN]); - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_DUPLICATE_DOMAIN; - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, - error->text_buffer_size, - "Domain %s specified by %s is already in use", - new_dom->config[AUTHENTICATION_DOMAIN], - config_options[AUTHENTICATION_DOMAIN].name); - } - mg_free(new_dom); - mg_unlock_context(ctx); - return -5; - } - - /* Count number of domains */ - idx++; - - if (dom->next == NULL) { - dom->next = new_dom; - break; - } - dom = dom->next; - } - - mg_unlock_context(ctx); - - /* Return domain number */ - return idx; -} - - -CIVETWEB_API int -mg_start_domain(struct mg_context *ctx, const char **options) -{ - return mg_start_domain2(ctx, options, NULL); -} - - -/* Feature check API function */ -CIVETWEB_API unsigned -mg_check_feature(unsigned feature) -{ - static const unsigned feature_set = 0 - /* Set bits for available features according to API documentation. - * This bit mask is created at compile time, according to the active - * preprocessor defines. It is a single const value at runtime. */ -#if !defined(NO_FILES) - | MG_FEATURES_FILES -#endif -#if !defined(NO_SSL) || defined(USE_MBEDTLS) || defined(USE_GNUTLS) - | MG_FEATURES_SSL -#endif -#if !defined(NO_CGI) - | MG_FEATURES_CGI -#endif -#if defined(USE_IPV6) - | MG_FEATURES_IPV6 -#endif -#if defined(USE_WEBSOCKET) - | MG_FEATURES_WEBSOCKET -#endif -#if defined(USE_LUA) - | MG_FEATURES_LUA -#endif -#if defined(USE_DUKTAPE) - | MG_FEATURES_SSJS -#endif -#if !defined(NO_CACHING) - | MG_FEATURES_CACHE -#endif -#if defined(USE_SERVER_STATS) - | MG_FEATURES_STATS -#endif -#if defined(USE_ZLIB) - | MG_FEATURES_COMPRESSION -#endif -#if defined(USE_HTTP2) - | MG_FEATURES_HTTP2 -#endif -#if defined(USE_X_DOM_SOCKET) - | MG_FEATURES_X_DOMAIN_SOCKET -#endif - - /* Set some extra bits not defined in the API documentation. - * These bits may change without further notice. */ -#if defined(MG_LEGACY_INTERFACE) - | 0x80000000u -#endif -#if defined(MG_EXPERIMENTAL_INTERFACES) - | 0x40000000u -#endif -#if !defined(NO_RESPONSE_BUFFERING) - | 0x20000000u -#endif -#if defined(MEMORY_DEBUGGING) - | 0x10000000u -#endif - ; - return (feature & feature_set); -} - - -static size_t -mg_str_append(char **dst, char *end, const char *src) -{ - size_t len = strlen(src); - if (*dst != end) { - /* Append src if enough space, or close dst. */ - if ((size_t)(end - *dst) > len) { - strcpy(*dst, src); - *dst += len; - } else { - *dst = end; - } - } - return len; -} - - -/* Get system information. It can be printed or stored by the caller. - * Return the size of available information. */ -CIVETWEB_API int -mg_get_system_info(char *buffer, int buflen) -{ - char *end, *append_eoobj = NULL, block[256]; - size_t system_info_length = 0; - -#if defined(_WIN32) - static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n"; -#else - static const char eol[] = "\n", eoobj[] = "\n}\n"; -#endif - - if ((buffer == NULL) || (buflen < 1)) { - buflen = 0; - end = buffer; - } else { - *buffer = 0; - end = buffer + buflen; - } - if (buflen > (int)(sizeof(eoobj) - 1)) { - /* has enough space to append eoobj */ - append_eoobj = buffer; - if (end) { - end -= sizeof(eoobj) - 1; - } - } - - system_info_length += mg_str_append(&buffer, end, "{"); - - /* Server version */ - { - const char *version = mg_version(); - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s\"version\" : \"%s\"", - eol, - version); - system_info_length += mg_str_append(&buffer, end, block); - } - - /* System info */ - { -#if defined(_WIN32) - DWORD dwVersion = 0; - DWORD dwMajorVersion = 0; - DWORD dwMinorVersion = 0; - SYSTEM_INFO si; - - GetSystemInfo(&si); - -#if defined(_MSC_VER) -#pragma warning(push) - /* GetVersion was declared deprecated */ -#pragma warning(disable : 4996) -#endif - dwVersion = GetVersion(); -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - - dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); - dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); - - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"os\" : \"Windows %u.%u\"", - eol, - (unsigned)dwMajorVersion, - (unsigned)dwMinorVersion); - system_info_length += mg_str_append(&buffer, end, block); - - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"cpu\" : \"type %u, cores %u, mask %x\"", - eol, - (unsigned)si.wProcessorArchitecture, - (unsigned)si.dwNumberOfProcessors, - (unsigned)si.dwActiveProcessorMask); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__rtems__) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"os\" : \"%s %s\"", - eol, - "RTEMS", - rtems_version()); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__ZEPHYR__) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"os\" : \"%s\"", - eol, - "Zephyr OS", - ZEPHYR_VERSION); - system_info_length += mg_str_append(&buffer, end, block); -#else - struct utsname name; - memset(&name, 0, sizeof(name)); - uname(&name); - - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"os\" : \"%s %s (%s) - %s\"", - eol, - name.sysname, - name.version, - name.release, - name.machine); - system_info_length += mg_str_append(&buffer, end, block); -#endif - } - - /* Features */ - { - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"features\" : %lu" - ",%s\"feature_list\" : \"Server:%s%s%s%s%s%s%s%s%s\"", - eol, - (unsigned long)mg_check_feature(0xFFFFFFFFu), - eol, - mg_check_feature(MG_FEATURES_FILES) ? " Files" : "", - mg_check_feature(MG_FEATURES_SSL) ? " HTTPS" : "", - mg_check_feature(MG_FEATURES_CGI) ? " CGI" : "", - mg_check_feature(MG_FEATURES_IPV6) ? " IPv6" : "", - mg_check_feature(MG_FEATURES_WEBSOCKET) ? " WebSockets" - : "", - mg_check_feature(MG_FEATURES_LUA) ? " Lua" : "", - mg_check_feature(MG_FEATURES_SSJS) ? " JavaScript" : "", - mg_check_feature(MG_FEATURES_CACHE) ? " Cache" : "", - mg_check_feature(MG_FEATURES_STATS) ? " Stats" : ""); - system_info_length += mg_str_append(&buffer, end, block); - -#if defined(USE_LUA) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"lua_version\" : \"%u (%s)\"", - eol, - (unsigned)LUA_VERSION_NUM, - LUA_RELEASE); - system_info_length += mg_str_append(&buffer, end, block); -#endif -#if defined(USE_DUKTAPE) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"javascript\" : \"Duktape %u.%u.%u\"", - eol, - (unsigned)DUK_VERSION / 10000, - ((unsigned)DUK_VERSION / 100) % 100, - (unsigned)DUK_VERSION % 100); - system_info_length += mg_str_append(&buffer, end, block); -#endif - } - - /* Build identifier. If BUILD_DATE is not set, __DATE__ will be used. */ - { -#if defined(BUILD_DATE) - const char *bd = BUILD_DATE; -#else -#if defined(GCC_DIAGNOSTIC) -#if GCC_VERSION >= 40900 -#pragma GCC diagnostic push - /* Disable idiotic compiler warning -Wdate-time, appeared in gcc5. This - * does not work in some versions. If "BUILD_DATE" is defined to some - * string, it is used instead of __DATE__. */ -#pragma GCC diagnostic ignored "-Wdate-time" -#endif -#endif - const char *bd = __DATE__; -#if defined(GCC_DIAGNOSTIC) -#if GCC_VERSION >= 40900 -#pragma GCC diagnostic pop -#endif -#endif -#endif - - mg_snprintf( - NULL, NULL, block, sizeof(block), ",%s\"build\" : \"%s\"", eol, bd); - - system_info_length += mg_str_append(&buffer, end, block); - } - - /* Compiler information */ - /* http://sourceforge.net/p/predef/wiki/Compilers/ */ - { -#if defined(_MSC_VER) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"MSC: %u (%u)\"", - eol, - (unsigned)_MSC_VER, - (unsigned)_MSC_FULL_VER); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__MINGW64__) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"MinGW64: %u.%u\"", - eol, - (unsigned)__MINGW64_VERSION_MAJOR, - (unsigned)__MINGW64_VERSION_MINOR); - system_info_length += mg_str_append(&buffer, end, block); - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"MinGW32: %u.%u\"", - eol, - (unsigned)__MINGW32_MAJOR_VERSION, - (unsigned)__MINGW32_MINOR_VERSION); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__MINGW32__) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"MinGW32: %u.%u\"", - eol, - (unsigned)__MINGW32_MAJOR_VERSION, - (unsigned)__MINGW32_MINOR_VERSION); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__clang__) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"clang: %u.%u.%u (%s)\"", - eol, - __clang_major__, - __clang_minor__, - __clang_patchlevel__, - __clang_version__); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__GNUC__) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"gcc: %u.%u.%u\"", - eol, - (unsigned)__GNUC__, - (unsigned)__GNUC_MINOR__, - (unsigned)__GNUC_PATCHLEVEL__); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__INTEL_COMPILER) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"Intel C/C++: %u\"", - eol, - (unsigned)__INTEL_COMPILER); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__BORLANDC__) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"Borland C: 0x%x\"", - eol, - (unsigned)__BORLANDC__); - system_info_length += mg_str_append(&buffer, end, block); -#elif defined(__SUNPRO_C) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"Solaris: 0x%x\"", - eol, - (unsigned)__SUNPRO_C); - system_info_length += mg_str_append(&buffer, end, block); -#else - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"compiler\" : \"other\"", - eol); - system_info_length += mg_str_append(&buffer, end, block); -#endif - } - - /* Determine 32/64 bit data mode. - * see https://en.wikipedia.org/wiki/64-bit_computing */ - { - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"data_model\" : \"int:%u/%u/%u/%u, float:%u/%u/%u, " - "char:%u/%u, " - "ptr:%u, size:%u, time:%u\"", - eol, - (unsigned)sizeof(short), - (unsigned)sizeof(int), - (unsigned)sizeof(long), - (unsigned)sizeof(long long), - (unsigned)sizeof(float), - (unsigned)sizeof(double), - (unsigned)sizeof(long double), - (unsigned)sizeof(char), - (unsigned)sizeof(wchar_t), - (unsigned)sizeof(void *), - (unsigned)sizeof(size_t), - (unsigned)sizeof(time_t)); - system_info_length += mg_str_append(&buffer, end, block); - } - - /* Terminate string */ - if (append_eoobj) { - strcat(append_eoobj, eoobj); - } - system_info_length += sizeof(eoobj) - 1; - - return (int)system_info_length; -} - - -/* Get context information. It can be printed or stored by the caller. - * Return the size of available information. */ -CIVETWEB_API int -mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen) -{ -#if defined(USE_SERVER_STATS) - char *end, *append_eoobj = NULL, block[256]; - size_t context_info_length = 0; - -#if defined(_WIN32) - static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n"; -#else - static const char eol[] = "\n", eoobj[] = "\n}\n"; -#endif - struct mg_memory_stat *ms = get_memory_stat((struct mg_context *)ctx); - - if ((buffer == NULL) || (buflen < 1)) { - buflen = 0; - end = buffer; - } else { - *buffer = 0; - end = buffer + buflen; - } - if (buflen > (int)(sizeof(eoobj) - 1)) { - /* has enough space to append eoobj */ - append_eoobj = buffer; - end -= sizeof(eoobj) - 1; - } - - context_info_length += mg_str_append(&buffer, end, "{"); - - if (ms) { /* <-- should be always true */ - /* Memory information */ - int blockCount = (int)ms->blockCount; - int64_t totalMemUsed = ms->totalMemUsed; - int64_t maxMemUsed = ms->maxMemUsed; - if (totalMemUsed > maxMemUsed) { - maxMemUsed = totalMemUsed; - } - - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s\"memory\" : {%s" - "\"blocks\" : %i,%s" - "\"used\" : %" INT64_FMT ",%s" - "\"maxUsed\" : %" INT64_FMT "%s" - "}", - eol, - eol, - blockCount, - eol, - totalMemUsed, - eol, - maxMemUsed, - eol); - context_info_length += mg_str_append(&buffer, end, block); - } - - if (ctx) { - /* Declare all variables at begin of the block, to comply - * with old C standards. */ - char start_time_str[64] = {0}; - char now_str[64] = {0}; - time_t start_time = ctx->start_time; - time_t now = time(NULL); - int64_t total_data_read, total_data_written; - int active_connections = (int)ctx->active_connections; - int max_active_connections = (int)ctx->max_active_connections; - int total_connections = (int)ctx->total_connections; - if (active_connections > max_active_connections) { - max_active_connections = active_connections; - } - if (active_connections > total_connections) { - total_connections = active_connections; - } - - /* Connections information */ - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"connections\" : {%s" - "\"active\" : %i,%s" - "\"maxActive\" : %i,%s" - "\"total\" : %i%s" - "}", - eol, - eol, - active_connections, - eol, - max_active_connections, - eol, - total_connections, - eol); - context_info_length += mg_str_append(&buffer, end, block); - - /* Queue information */ -#if !defined(ALTERNATIVE_QUEUE) - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"queue\" : {%s" - "\"length\" : %i,%s" - "\"filled\" : %i,%s" - "\"maxFilled\" : %i,%s" - "\"full\" : %s%s" - "}", - eol, - eol, - ctx->sq_size, - eol, - ctx->sq_head - ctx->sq_tail, - eol, - ctx->sq_max_fill, - eol, - (ctx->sq_blocked ? "true" : "false"), - eol); - context_info_length += mg_str_append(&buffer, end, block); -#endif - - /* Requests information */ - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"requests\" : {%s" - "\"total\" : %lu%s" - "}", - eol, - eol, - (unsigned long)ctx->total_requests, - eol); - context_info_length += mg_str_append(&buffer, end, block); - - /* Data information */ - total_data_read = - mg_atomic_add64((volatile int64_t *)&ctx->total_data_read, 0); - total_data_written = - mg_atomic_add64((volatile int64_t *)&ctx->total_data_written, 0); - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"data\" : {%s" - "\"read\" : %" INT64_FMT ",%s" - "\"written\" : %" INT64_FMT "%s" - "}", - eol, - eol, - total_data_read, - eol, - total_data_written, - eol); - context_info_length += mg_str_append(&buffer, end, block); - - /* Execution time information */ - gmt_time_string(start_time_str, - sizeof(start_time_str) - 1, - &start_time); - gmt_time_string(now_str, sizeof(now_str) - 1, &now); - - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - ",%s\"time\" : {%s" - "\"uptime\" : %.0f,%s" - "\"start\" : \"%s\",%s" - "\"now\" : \"%s\"%s" - "}", - eol, - eol, - difftime(now, start_time), - eol, - start_time_str, - eol, - now_str, - eol); - context_info_length += mg_str_append(&buffer, end, block); - } - - /* Terminate string */ - if (append_eoobj) { - strcat(append_eoobj, eoobj); - } - context_info_length += sizeof(eoobj) - 1; - - return (int)context_info_length; -#else - (void)ctx; - if ((buffer != NULL) && (buflen > 0)) { - *buffer = 0; - } - return 0; -#endif -} - - -CIVETWEB_API void -mg_disable_connection_keep_alive(struct mg_connection *conn) -{ - /* https://github.com/civetweb/civetweb/issues/727 */ - if (conn != NULL) { - conn->must_close = 1; - } -} - - -#if defined(MG_EXPERIMENTAL_INTERFACES) -/* Get connection information. It can be printed or stored by the caller. - * Return the size of available information. */ -CIVETWEB_API int -mg_get_connection_info(const struct mg_context *ctx, - int idx, - char *buffer, - int buflen) -{ - const struct mg_connection *conn; - const struct mg_request_info *ri; - char *end, *append_eoobj = NULL, block[256]; - size_t connection_info_length = 0; - int state = 0; - const char *state_str = "unknown"; - -#if defined(_WIN32) - static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n"; -#else - static const char eol[] = "\n", eoobj[] = "\n}\n"; -#endif - - if ((buffer == NULL) || (buflen < 1)) { - buflen = 0; - end = buffer; - } else { - *buffer = 0; - end = buffer + buflen; - } - if (buflen > (int)(sizeof(eoobj) - 1)) { - /* has enough space to append eoobj */ - append_eoobj = buffer; - end -= sizeof(eoobj) - 1; - } - - if ((ctx == NULL) || (idx < 0)) { - /* Parameter error */ - return 0; - } - - if ((unsigned)idx >= ctx->cfg_max_worker_threads) { - /* Out of range */ - return 0; - } - - /* Take connection [idx]. This connection is not locked in - * any way, so some other thread might use it. */ - conn = (ctx->worker_connections) + idx; - - /* Initialize output string */ - connection_info_length += mg_str_append(&buffer, end, "{"); - - /* Init variables */ - ri = &(conn->request_info); - -#if defined(USE_SERVER_STATS) - state = conn->conn_state; - - /* State as string */ - switch (state) { - case 0: - state_str = "undefined"; - break; - case 1: - state_str = "not used"; - break; - case 2: - state_str = "init"; - break; - case 3: - state_str = "ready"; - break; - case 4: - state_str = "processing"; - break; - case 5: - state_str = "processed"; - break; - case 6: - state_str = "to close"; - break; - case 7: - state_str = "closing"; - break; - case 8: - state_str = "closed"; - break; - case 9: - state_str = "done"; - break; - } -#endif - - /* Connection info */ - if ((state >= 3) && (state < 9)) { - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s\"connection\" : {%s" - "\"remote\" : {%s" - "\"protocol\" : \"%s\",%s" - "\"addr\" : \"%s\",%s" - "\"port\" : %u%s" - "},%s" - "\"handled_requests\" : %u%s" - "}", - eol, - eol, - eol, - get_proto_name(conn), - eol, - ri->remote_addr, - eol, - ri->remote_port, - eol, - eol, - conn->handled_requests, - eol); - connection_info_length += mg_str_append(&buffer, end, block); - } - - /* Request info */ - if ((state >= 4) && (state < 6)) { - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s%s\"request_info\" : {%s" - "\"method\" : \"%s\",%s" - "\"uri\" : \"%s\",%s" - "\"query\" : %s%s%s%s" - "}", - (connection_info_length > 1 ? "," : ""), - eol, - eol, - ri->request_method, - eol, - ri->request_uri, - eol, - ri->query_string ? "\"" : "", - ri->query_string ? ri->query_string : "null", - ri->query_string ? "\"" : "", - eol); - connection_info_length += mg_str_append(&buffer, end, block); - } - - /* Execution time information */ - if ((state >= 2) && (state < 9)) { - char start_time_str[64] = {0}; - char close_time_str[64] = {0}; - time_t start_time = conn->conn_birth_time; - time_t close_time = 0; - double time_diff; - - gmt_time_string(start_time_str, - sizeof(start_time_str) - 1, - &start_time); -#if defined(USE_SERVER_STATS) - close_time = conn->conn_close_time; -#endif - if (close_time != 0) { - time_diff = difftime(close_time, start_time); - gmt_time_string(close_time_str, - sizeof(close_time_str) - 1, - &close_time); - } else { - time_t now = time(NULL); - time_diff = difftime(now, start_time); - close_time_str[0] = 0; /* or use "now" ? */ - } - - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s%s\"time\" : {%s" - "\"uptime\" : %.0f,%s" - "\"start\" : \"%s\",%s" - "\"closed\" : \"%s\"%s" - "}", - (connection_info_length > 1 ? "," : ""), - eol, - eol, - time_diff, - eol, - start_time_str, - eol, - close_time_str, - eol); - connection_info_length += mg_str_append(&buffer, end, block); - } - - /* Remote user name */ - if ((ri->remote_user) && (state < 9)) { - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s%s\"user\" : {%s" - "\"name\" : \"%s\",%s" - "}", - (connection_info_length > 1 ? "," : ""), - eol, - eol, - ri->remote_user, - eol); - connection_info_length += mg_str_append(&buffer, end, block); - } - - /* Data block */ - if (state >= 3) { - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s%s\"data\" : {%s" - "\"read\" : %" INT64_FMT ",%s" - "\"written\" : %" INT64_FMT "%s" - "}", - (connection_info_length > 1 ? "," : ""), - eol, - eol, - conn->consumed_content, - eol, - conn->num_bytes_sent, - eol); - connection_info_length += mg_str_append(&buffer, end, block); - } - - /* State */ - mg_snprintf(NULL, - NULL, - block, - sizeof(block), - "%s%s\"state\" : \"%s\"", - (connection_info_length > 1 ? "," : ""), - eol, - state_str); - connection_info_length += mg_str_append(&buffer, end, block); - - /* Terminate string */ - if (append_eoobj) { - strcat(append_eoobj, eoobj); - } - connection_info_length += sizeof(eoobj) - 1; - - return (int)connection_info_length; -} - - -#if 0 -/* Get handler information. Not fully implemented. Is it required? */ -CIVETWEB_API int -mg_get_handler_info(struct mg_context *ctx, - char *buffer, - int buflen) -{ - int handler_info_len = 0; - struct mg_handler_info *tmp_rh; - mg_lock_context(ctx); - - for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { - - if (buflen > handler_info_len + tmp_rh->uri_len) { - memcpy(buffer + handler_info_len, tmp_rh->uri, tmp_rh->uri_len); - } - handler_info_len += tmp_rh->uri_len; - - switch (tmp_rh->handler_type) { - case REQUEST_HANDLER: - (void)tmp_rh->handler; - break; - case WEBSOCKET_HANDLER: - (void)tmp_rh->connect_handler; - (void)tmp_rh->ready_handler; - (void)tmp_rh->data_handler; - (void)tmp_rh->close_handler; - break; - case AUTH_HANDLER: - (void)tmp_rh->auth_handler; - break; - } - (void)cbdata; - } - - mg_unlock_context(ctx); - return handler_info_len; -} -#endif -#endif - - -/* Initialize this library. This function does not need to be thread safe. - */ -CIVETWEB_API unsigned -mg_init_library(unsigned features) -{ - unsigned features_to_init = mg_check_feature(features & 0xFFu); - unsigned features_inited = features_to_init; - - if (mg_init_library_called <= 0) { - /* Not initialized yet */ - if (0 != pthread_mutex_init(&global_lock_mutex, NULL)) { - return 0; - } - } - - mg_global_lock(); - - if (mg_init_library_called <= 0) { - int i; - size_t len; - -#if defined(_WIN32) - int file_mutex_init = 1; - int wsa = 1; -#else - int mutexattr_init = 1; -#endif - int failed = 1; - int key_create = pthread_key_create(&sTlsKey, tls_dtor); - - if (key_create == 0) { -#if defined(_WIN32) - file_mutex_init = - pthread_mutex_init(&global_log_file_lock, &pthread_mutex_attr); - if (file_mutex_init == 0) { - /* Start WinSock */ - WSADATA data; - failed = wsa = WSAStartup(MAKEWORD(2, 2), &data); - } -#else - mutexattr_init = pthread_mutexattr_init(&pthread_mutex_attr); - if (mutexattr_init == 0) { - failed = pthread_mutexattr_settype(&pthread_mutex_attr, - PTHREAD_MUTEX_RECURSIVE); - } -#endif - } - - if (failed) { -#if defined(_WIN32) - if (wsa == 0) { - (void)WSACleanup(); - } - if (file_mutex_init == 0) { - (void)pthread_mutex_destroy(&global_log_file_lock); - } -#else - if (mutexattr_init == 0) { - (void)pthread_mutexattr_destroy(&pthread_mutex_attr); - } -#endif - if (key_create == 0) { - (void)pthread_key_delete(sTlsKey); - } - mg_global_unlock(); - (void)pthread_mutex_destroy(&global_lock_mutex); - return 0; - } - - len = 1; - for (i = 0; http_methods[i].name != NULL; i++) { - size_t sl = strlen(http_methods[i].name); - len += sl; - if (i > 0) { - len += 2; - } - } - all_methods = (char *)mg_malloc(len); - if (!all_methods) { - /* Must never happen */ - mg_global_unlock(); - (void)pthread_mutex_destroy(&global_lock_mutex); - return 0; - } - all_methods[0] = 0; - for (i = 0; http_methods[i].name != NULL; i++) { - if (i > 0) { - strcat(all_methods, ", "); - strcat(all_methods, http_methods[i].name); - } else { - strcpy(all_methods, http_methods[i].name); - } - } - } - -#if defined(USE_LUA) - lua_init_optional_libraries(); -#endif - -#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \ - || defined(OPENSSL_API_3_0)) \ - && !defined(NO_SSL) - - if (features_to_init & MG_FEATURES_SSL) { - if (!mg_openssl_initialized) { - char ebuf[128]; - if (initialize_openssl(ebuf, sizeof(ebuf))) { - mg_openssl_initialized = 1; - } else { - (void)ebuf; - DEBUG_TRACE("Initializing SSL failed: %s", ebuf); - features_inited &= ~((unsigned)(MG_FEATURES_SSL)); - } - } else { - /* ssl already initialized */ - } - } - -#endif - - if (mg_init_library_called <= 0) { - mg_init_library_called = 1; - } else { - mg_init_library_called++; - } - mg_global_unlock(); - - return features_inited; -} - - -/* Un-initialize this library. */ -CIVETWEB_API unsigned -mg_exit_library(void) -{ - if (mg_init_library_called <= 0) { - return 0; - } - - mg_global_lock(); - - mg_init_library_called--; - if (mg_init_library_called == 0) { -#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)) && !defined(NO_SSL) - if (mg_openssl_initialized) { - uninitialize_openssl(); - mg_openssl_initialized = 0; - } -#endif - -#if defined(_WIN32) - (void)WSACleanup(); - (void)pthread_mutex_destroy(&global_log_file_lock); -#else - (void)pthread_mutexattr_destroy(&pthread_mutex_attr); -#endif - - (void)pthread_key_delete(sTlsKey); - -#if defined(USE_LUA) - lua_exit_optional_libraries(); -#endif - mg_free(all_methods); - all_methods = NULL; - - mg_global_unlock(); - (void)pthread_mutex_destroy(&global_lock_mutex); - return 1; - } - - mg_global_unlock(); - return 1; -} - - -/* End of civetweb.c */ diff --git a/net/http/civetweb/civetweb.h b/net/http/civetweb/civetweb.h deleted file mode 100644 index 2665d64609b1c..0000000000000 --- a/net/http/civetweb/civetweb.h +++ /dev/null @@ -1,1833 +0,0 @@ -/* Copyright (c) 2013-2024 the Civetweb developers - * Copyright (c) 2004-2013 Sergey Lyubka - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef CIVETWEB_HEADER_INCLUDED -#define CIVETWEB_HEADER_INCLUDED - -#define CIVETWEB_VERSION "1.17" -#define CIVETWEB_VERSION_MAJOR (1) -#define CIVETWEB_VERSION_MINOR (17) -#define CIVETWEB_VERSION_PATCH (0) - -#ifndef CIVETWEB_API -#if defined(_WIN32) -#if defined(CIVETWEB_DLL_EXPORTS) -#define CIVETWEB_API __declspec(dllexport) -#elif defined(CIVETWEB_DLL_IMPORTS) -#define CIVETWEB_API __declspec(dllimport) -#else -#define CIVETWEB_API -#endif -#elif __GNUC__ >= 4 -#define CIVETWEB_API __attribute__((visibility("default"))) -#else -#define CIVETWEB_API -#endif -#endif - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -/* Init Features */ -enum { - MG_FEATURES_DEFAULT = 0x0u, - - /* Support files from local directories */ - /* Will only work, if NO_FILES is not set. */ - MG_FEATURES_FILES = 0x1u, - - /* Support transport layer security (TLS). */ - /* SSL is still often used synonymously for TLS. */ - /* Will only work, if NO_SSL is not set. */ - MG_FEATURES_TLS = 0x2u, - MG_FEATURES_SSL = 0x2u, - - /* Support common gateway interface (CGI). */ - /* Will only work, if NO_CGI is not set. */ - MG_FEATURES_CGI = 0x4u, - - /* Support IPv6. */ - /* Will only work, if USE_IPV6 is set. */ - MG_FEATURES_IPV6 = 0x8u, - - /* Support WebSocket protocol. */ - /* Will only work, if USE_WEBSOCKET is set. */ - MG_FEATURES_WEBSOCKET = 0x10u, - - /* Support server side Lua scripting. */ - /* Will only work, if USE_LUA is set. */ - MG_FEATURES_LUA = 0x20u, - - /* Support server side JavaScript scripting. */ - /* Will only work, if USE_DUKTAPE is set. */ - MG_FEATURES_SSJS = 0x40u, - - /* Provide data required for caching files. */ - /* Will only work, if NO_CACHING is not set. */ - MG_FEATURES_CACHE = 0x80u, - - /* Collect server status information. */ - /* Will only work, if USE_SERVER_STATS is set. */ - MG_FEATURES_STATS = 0x100u, - - /* Support on-the-fly compression. */ - /* Will only work, if USE_ZLIB is set. */ - MG_FEATURES_COMPRESSION = 0x200u, - - /* HTTP/2 support enabled. */ - MG_FEATURES_HTTP2 = 0x400u, - - /* Support unix domain sockets. */ - MG_FEATURES_X_DOMAIN_SOCKET = 0x800u, - - /* Bit mask for all feature defines. */ - MG_FEATURES_ALL = 0xFFFFu -}; - - -/* Initialize this library. This should be called once before any other - * function from this library. This function is not guaranteed to be - * thread safe. - * Parameters: - * features: bit mask for features to be initialized. - * Note: The TLS libraries (like OpenSSL) is initialized - * only if the MG_FEATURES_TLS bit is set. - * Currently the other bits do not influence - * initialization, but this may change in future - * versions. - * Return value: - * initialized features - * 0: error - */ -CIVETWEB_API unsigned mg_init_library(unsigned features); - - -/* Un-initialize this library. - * Return value: - * 0: error - */ -CIVETWEB_API unsigned mg_exit_library(void); - - -struct mg_context; /* Handle for the HTTP service itself */ -struct mg_connection; /* Handle for the individual connection */ - - -/* Maximum number of headers */ -#define MG_MAX_HEADERS (64) - -struct mg_header { - const char *name; /* HTTP header name */ - const char *value; /* HTTP header value */ -}; - - -/* This structure contains information about the HTTP request. */ -struct mg_request_info { - const char *request_method; /* "GET", "POST", etc */ - const char *request_uri; /* URL-decoded URI (absolute or relative, - * as in the request) */ - const char *local_uri_raw; /* URL-decoded URI (relative). Can be NULL - * if the request_uri does not address a - * resource at the server host. */ - const char *local_uri; /* Same as local_uri_raw, however, cleaned - * so a path like - * allowed_dir/../forbidden_file - * is not possible. */ - const char *http_version; /* E.g. "1.0", "1.1" */ - const char *query_string; /* URL part after '?', not including '?', or - NULL */ - const char *remote_user; /* Authenticated user, or NULL if no auth - used */ - char remote_addr[48]; /* Client's IP address as a string. */ - - long long content_length; /* Length (in bytes) of the request body, - can be -1 if no length was given. */ - int remote_port; /* Port at client side */ - int server_port; /* Port at server side (one of the listening - ports) */ - int is_ssl; /* 1 if HTTPS or WS is used (SSL/TLS used), - 0 if not */ - void *user_data; /* User data pointer passed to mg_start() */ - void *conn_data; /* Connection-specific user data */ - - int num_headers; /* Number of HTTP headers */ - struct mg_header - http_headers[MG_MAX_HEADERS]; /* Allocate maximum headers */ - - struct mg_client_cert *client_cert; /* Client certificate information */ - - const char *acceptedWebSocketSubprotocol; /* websocket subprotocol, - * accepted during handshake */ -}; - - -/* This structure contains information about the HTTP request. */ -/* This structure may be extended in future versions. */ -struct mg_response_info { - int status_code; /* E.g. 200 */ - const char *status_text; /* E.g. "OK" */ - const char *http_version; /* E.g. "1.0", "1.1" */ - - long long content_length; /* Length (in bytes) of the request body, - can be -1 if no length was given. */ - - int num_headers; /* Number of HTTP headers */ - struct mg_header - http_headers[MG_MAX_HEADERS]; /* Allocate maximum headers */ -}; - - -/* Client certificate information (part of mg_request_info) */ -struct mg_client_cert { - void *peer_cert; - const char *subject; - const char *issuer; - const char *serial; - const char *finger; -}; - - -/* This structure needs to be passed to mg_start(), to let civetweb know - which callbacks to invoke. For a detailed description, see - https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md */ -struct mg_callbacks { - /* Called when civetweb has received new HTTP request. - If the callback returns one, it must process the request - by sending valid HTTP headers and a body. Civetweb will not do - any further processing. Otherwise it must return zero. - Note that since V1.7 the "begin_request" function is called - before an authorization check. If an authorization check is - required, use a request_handler instead. - Return value: - 0: civetweb will process the request itself. In this case, - the callback must not send any data to the client. - 1-999: callback already processed the request. Civetweb will - not send any data after the callback returned. The - return code is stored as a HTTP status code for the - access log. */ - int (*begin_request)(struct mg_connection *); - - /* Called when civetweb has finished processing request. */ - void (*end_request)(const struct mg_connection *, int reply_status_code); - - /* Called when civetweb is about to log a message. If callback returns - non-zero, civetweb does not log anything. */ - int (*log_message)(const struct mg_connection *, const char *message); - - /* Called when civetweb is about to log access. If callback returns - non-zero, civetweb does not log anything. */ - int (*log_access)(const struct mg_connection *, const char *message); - - /* Called when civetweb initializes SSL library. - Parameters: - ssl_ctx: SSL_CTX pointer. - user_data: parameter user_data passed when starting the server. - Return value: - 0: civetweb will set up the SSL certificate. - 1: civetweb assumes the callback already set up the certificate. - -1: initializing ssl fails. */ - int (*init_ssl)(void *ssl_ctx, void *user_data); - - /* Called when civetweb initializes SSL library for a domain. - Parameters: - server_domain: authentication_domain from the domain config. - ssl_ctx: SSL_CTX pointer. - user_data: parameter user_data passed when starting the server. - Return value: - 0: civetweb will set up the SSL certificate. - 1: civetweb assumes the callback already set up the certificate. - -1: initializing ssl fails. */ - int (*init_ssl_domain)(const char *server_domain, - void *ssl_ctx, - void *user_data); - - /* Called when civetweb is about to create or free a SSL_CTX. - Parameters: - ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when - mg_context will be freed user_data: parameter user_data passed when starting - the server. Return value: 0: civetweb will continue to create the context, - just as if the callback would not be present. The value in *ssl_ctx when the - function returns is ignored. 1: civetweb will copy the value from *ssl_ctx - to the civetweb context and doesn't create its own. -1: initializing ssl - fails.*/ - int (*external_ssl_ctx)(void **ssl_ctx, void *user_data); - - /* Called when civetweb is about to create or free a SSL_CTX for a domain. - Parameters: - server_domain: authentication_domain from the domain config. - ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when - mg_context will be freed user_data: parameter user_data passed when starting - the server. Return value: 0: civetweb will continue to create the context, - just as if the callback would not be present. The value in *ssl_ctx when the - function returns is ignored. 1: civetweb will copy the value from *ssl_ctx - to the civetweb context and doesn't create its own. -1: initializing ssl - fails.*/ - int (*external_ssl_ctx_domain)(const char *server_domain, - void **ssl_ctx, - void *user_data); - -#if defined(MG_EXPERIMENTAL_INTERFACES) /* 2019-11-03 */ - /* Called when data frame has been received from the peer. - Parameters: - bits: first byte of the websocket frame, see websocket RFC at - http://tools.ietf.org/html/rfc6455, section 5.2 - data, data_len: payload, with mask (if any) already applied. - Return value: - 1: keep this websocket connection open. - 0: close this websocket connection. - This callback is deprecated: Use mg_set_websocket_handler instead. */ - int (*websocket_data)(struct mg_connection *, - int bits, - char *data, - size_t data_len); -#endif /* MG_LEGACY_INTERFACE */ - - /* Called when civetweb is closing a connection. The per-context mutex is - locked when this is invoked. - - Websockets: - Before mg_set_websocket_handler has been added, it was primarily useful - for noting when a websocket is closing, and used to remove it from any - application-maintained list of clients. - Using this callback for websocket connections is deprecated: Use - mg_set_websocket_handler instead. - */ - void (*connection_close)(const struct mg_connection *); - - /* Called after civetweb has closed a connection. The per-context mutex is - locked when this is invoked. - - Connection specific data: - If memory has been allocated for the connection specific user data - (mg_request_info->conn_data, mg_get_user_connection_data), - this is the last chance to free it. - */ - void (*connection_closed)(const struct mg_connection *); - - - /* init_lua is called when civetweb is about to serve Lua server page. - exit_lua is called when the Lua processing is complete. - Both will work only if Lua support is enabled. - Parameters: - conn: current connection. - lua_context: "lua_State *" pointer. - context_flags: context type information as bitmask: - context_flags & 0x0F: (0-15) Lua environment type - */ - void (*init_lua)(const struct mg_connection *conn, - void *lua_context, - unsigned context_flags); - void (*exit_lua)(const struct mg_connection *conn, - void *lua_context, - unsigned context_flags); - - - /* Called when civetweb is about to send HTTP error to the client. - Implementing this callback allows to create custom error pages. - Parameters: - conn: current connection. - status: HTTP error status code. - errmsg: error message text. - Return value: - 1: run civetweb error handler. - 0: callback already handled the error. */ - int (*http_error)(struct mg_connection *conn, - int status, - const char *errmsg); - - /* Called after civetweb context has been created, before requests - are processed. - Parameters: - ctx: context handle */ - void (*init_context)(const struct mg_context *ctx); - - /* Called when civetweb context is deleted. - Parameters: - ctx: context handle */ - void (*exit_context)(const struct mg_context *ctx); - - /* Called when a new worker thread is initialized. - * It is always called from the newly created thread and can be used to - * initialize thread local storage data. - * Parameters: - * ctx: context handle - * thread_type: - * 0 indicates the master thread - * 1 indicates a worker thread handling client connections - * 2 indicates an internal helper thread (timer thread) - * Return value: - * This function returns a user supplied pointer. The pointer is assigned - * to the thread and can be obtained from the mg_connection object using - * mg_get_thread_pointer in all server callbacks. Note: A connection and - * a thread are not directly related. Threads will serve several different - * connections, and data from a single connection may call different - * callbacks using different threads. The thread pointer can be obtained - * in a callback handler, but should not be stored beyond the scope of - * one call to one callback. - */ - void *(*init_thread)(const struct mg_context *ctx, int thread_type); - - /* Called when a worker exits. - * The parameters "ctx" and "thread_type" correspond to the "init_thread" - * call. The "thread_pointer" parameter is the value returned by - * "init_thread". - */ - void (*exit_thread)(const struct mg_context *ctx, - int thread_type, - void *thread_pointer); - - /* Called when initializing a new connection object. - * Can be used to initialize the connection specific user data - * (mg_request_info->conn_data, mg_get_user_connection_data). - * When the callback is called, it is not yet known if a - * valid HTTP(S) request will be made. - * Parameters: - * conn: not yet fully initialized connection object - * conn_data: output parameter, set to initialize the - * connection specific user data - * Return value: - * must be 0 - * Otherwise, the result is undefined - */ - int (*init_connection)(const struct mg_connection *conn, void **conn_data); -}; - - -/* Start web server. - - Parameters: - callbacks: mg_callbacks structure with user-defined callbacks. - options: NULL terminated list of option_name, option_value pairs that - specify Civetweb configuration parameters. - - Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom - processing is required for these, signal handlers must be set up - after calling mg_start(). - - - Example: - const char *options[] = { - "document_root", "/var/www", - "listening_ports", "80,443s", - NULL - }; - struct mg_context *ctx = mg_start(&my_func, NULL, options); - - Refer to https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md - for the list of valid option and their possible values. - - Return: - web server context, or NULL on error. */ -CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks, - void *user_data, - const char **configuration_options); - - -/* Stop the web server. - - Must be called last, when an application wants to stop the web server and - release all associated resources. This function blocks until all Civetweb - threads are stopped. Context pointer becomes invalid. */ -CIVETWEB_API void mg_stop(struct mg_context *); - - -/* Add an additional domain to an already running web server. - * - * Parameters: - * ctx: Context handle of a server started by mg_start. - * options: NULL terminated list of option_name, option_value pairs that - * specify CivetWeb configuration parameters. - * - * Return: - * < 0 in case of an error - * -1 for a parameter error - * -2 invalid options - * -3 initializing SSL failed - * -4 mandatory domain option missing - * -5 duplicate domain - * -6 out of memory - * > 0 index / handle of a new domain - */ -CIVETWEB_API int mg_start_domain(struct mg_context *ctx, - const char **configuration_options); - - -/* mg_request_handler - - Called when a new request comes in. This callback is URI based - and configured with mg_set_request_handler(). - - Parameters: - conn: current connection information. - cbdata: the callback data configured with mg_set_request_handler(). - Returns: - 0: the handler could not handle the request, so fall through. - 1 - 999: the handler processed the request. The return code is - stored as a HTTP status code for the access log. */ -typedef int (*mg_request_handler)(struct mg_connection *conn, void *cbdata); - - -/* mg_set_request_handler - - Sets or removes a URI mapping for a request handler. - This function waits until a removing/updating handler becomes unused, so - do not call from the handler itself. - - URI's are ordered and prefixed URI's are supported. For example, - consider two URIs: /a/b and /a - /a matches /a - /a/b matches /a/b - /a/c matches /a - - Parameters: - ctx: server context - uri: the URI (exact or pattern) for the handler - handler: the callback handler to use when the URI is requested. - If NULL, an already registered handler for this URI will - be removed. - The URI used to remove a handler must match exactly the - one used to register it (not only a pattern match). - cbdata: the callback data to give to the handler when it is called. */ -CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, - const char *uri, - mg_request_handler handler, - void *cbdata); - - -/* Callback types for websocket handlers in C/C++. - - mg_websocket_connect_handler - Is called when the client intends to establish a websocket connection, - before websocket handshake. - Return value: - 0: civetweb proceeds with websocket handshake. - 1: connection is closed immediately. - - mg_websocket_ready_handler - Is called when websocket handshake is successfully completed, and - connection is ready for data exchange. - - mg_websocket_data_handler - Is called when a data frame has been received from the client. - Parameters: - bits: first byte of the websocket frame, see websocket RFC at - http://tools.ietf.org/html/rfc6455, section 5.2 - data, data_len: payload, with mask (if any) already applied. - Return value: - 1: keep this websocket connection open. - 0: close this websocket connection. - - mg_connection_close_handler - Is called, when the connection is closed.*/ -typedef int (*mg_websocket_connect_handler)(const struct mg_connection *, - void *); -typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *); -typedef int (*mg_websocket_data_handler)(struct mg_connection *, - int, - char *, - size_t, - void *); -typedef void (*mg_websocket_close_handler)(const struct mg_connection *, - void *); - -/* struct mg_websocket_subprotocols - * - * List of accepted subprotocols - */ -struct mg_websocket_subprotocols { - int nb_subprotocols; - const char **subprotocols; -}; - -/* mg_set_websocket_handler - - Set or remove handler functions for websocket connections. - This function works similar to mg_set_request_handler - see there. */ -CIVETWEB_API void -mg_set_websocket_handler(struct mg_context *ctx, - const char *uri, - mg_websocket_connect_handler connect_handler, - mg_websocket_ready_handler ready_handler, - mg_websocket_data_handler data_handler, - mg_websocket_close_handler close_handler, - void *cbdata); - -/* mg_set_websocket_handler - - Set or remove handler functions for websocket connections. - This function works similar to mg_set_request_handler - see there. */ -CIVETWEB_API void mg_set_websocket_handler_with_subprotocols( - struct mg_context *ctx, - const char *uri, - struct mg_websocket_subprotocols *subprotocols, - mg_websocket_connect_handler connect_handler, - mg_websocket_ready_handler ready_handler, - mg_websocket_data_handler data_handler, - mg_websocket_close_handler close_handler, - void *cbdata); - - -/* mg_authorization_handler - - Callback function definition for mg_set_auth_handler - - Parameters: - conn: current connection information. - cbdata: the callback data configured with mg_set_request_handler(). - Returns: - 0: access denied - 1: access granted - */ -typedef int (*mg_authorization_handler)(struct mg_connection *conn, - void *cbdata); - - -/* mg_set_auth_handler - - Sets or removes a URI mapping for an authorization handler. - This function works similar to mg_set_request_handler - see there. */ -CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx, - const char *uri, - mg_authorization_handler handler, - void *cbdata); - - -/* Get the value of particular configuration parameter. - The value returned is read-only. Civetweb does not allow changing - configuration at run time. - If given parameter name is not valid, NULL is returned. For valid - names, return value is guaranteed to be non-NULL. If parameter is not - set, zero-length string is returned. */ -CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx, - const char *name); - - -/* Get context from connection. */ -CIVETWEB_API struct mg_context * -mg_get_context(const struct mg_connection *conn); - - -/* Get user data passed to mg_start from context. */ -CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx); - - -/* Get user data passed to mg_start from connection. */ -CIVETWEB_API void *mg_get_user_context_data(const struct mg_connection *conn); - - -/* Get user defined thread pointer for server threads (see init_thread). */ -CIVETWEB_API void *mg_get_thread_pointer(const struct mg_connection *conn); - - -/* Set user data for the current connection. */ -/* Note: CivetWeb callbacks use "struct mg_connection *conn" as input - when mg_read/mg_write callbacks are allowed in the callback, - while "const struct mg_connection *conn" is used as input in case - calling mg_read/mg_write is not allowed. - Setting the user connection data will modify the connection - object represented by mg_connection *, but it will not read from - or write to the connection. */ -/* Note: An alternative is to use the init_connection callback - instead to initialize the user connection data pointer. It is - recommended to supply a pointer to some user defined data structure - as conn_data initializer in init_connection. In case it is required - to change some data after the init_connection call, store another - data pointer in the user defined data structure and modify that - pointer. In either case, after the init_connection callback, only - calls to mg_get_user_connection_data should be required. */ -CIVETWEB_API void mg_set_user_connection_data(const struct mg_connection *conn, - void *data); - - -/* Get user data set for the current connection. */ -CIVETWEB_API void * -mg_get_user_connection_data(const struct mg_connection *conn); - - -/* Get a formatted link corresponding to the current request - - Parameters: - conn: current connection information. - buf: string buffer (out) - buflen: length of the string buffer - Returns: - <0: error - >=0: ok */ -CIVETWEB_API int -mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen); - - -struct mg_option { - const char *name; - int type; - const char *default_value; -}; - - -/* Configuration types */ -enum { - MG_CONFIG_TYPE_UNKNOWN = 0x0, - MG_CONFIG_TYPE_NUMBER = 0x1, - MG_CONFIG_TYPE_STRING = 0x2, - MG_CONFIG_TYPE_FILE = 0x3, - MG_CONFIG_TYPE_DIRECTORY = 0x4, - MG_CONFIG_TYPE_BOOLEAN = 0x5, - MG_CONFIG_TYPE_EXT_PATTERN = 0x6, - MG_CONFIG_TYPE_STRING_LIST = 0x7, - MG_CONFIG_TYPE_STRING_MULTILINE = 0x8, - MG_CONFIG_TYPE_YES_NO_OPTIONAL = 0x9 -}; - -/* Return array of struct mg_option, representing all valid configuration - options of civetweb.c. - The array is terminated by a NULL name option. */ -CIVETWEB_API const struct mg_option *mg_get_valid_options(void); - - -struct mg_server_port { - int protocol; /* 1 = IPv4, 2 = IPv6, 3 = both */ - int port; /* port number */ - int is_ssl; /* https port: 0 = no, 1 = yes */ - int is_redirect; /* redirect all requests: 0 = no, 1 = yes */ - int is_optional; /* optional: 0 = no, 1 = yes */ - int is_bound; /* bound: 0 = no, 1 = yes, relevant for optional ports */ - int _reserved3; - int _reserved4; -}; - -/* Legacy name */ -#define mg_server_ports mg_server_port - - -/* Get the list of ports that civetweb is listening on. - The parameter size is the size of the ports array in elements. - The caller is responsibility to allocate the required memory. - This function returns the number of struct mg_server_port elements - filled in, or <0 in case of an error. */ -CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx, - int size, - struct mg_server_port *ports); - - -/* Add, edit or delete the entry in the passwords file. - * - * This function allows an application to manipulate .htpasswd files on the - * fly by adding, deleting and changing user records. This is one of the - * several ways of implementing authentication on the server side. For another, - * cookie-based way please refer to the examples/chat in the source tree. - * - * Parameter: - * passwords_file_name: Path and name of a file storing multiple passwords - * realm: HTTP authentication realm (authentication domain) name - * user: User name - * password: - * If password is not NULL, entry modified or added. - * If password is NULL, entry is deleted. - * - * Return: - * 1 on success, 0 on error. - */ -CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name, - const char *realm, - const char *user, - const char *password); - - -/* Same as mg_modify_passwords_file, but instead of the plain-text - * password, the HA1 hash is specified. The plain-text password is - * not made known to civetweb. - * - * The HA1 hash is the MD5 checksum of a "user:realm:password" string - * in lower-case hex format. For example, if the user name is "myuser", - * the realm is "myrealm", and the password is "secret", then the HA1 is - * e67fd3248b58975c3e89ff18ecb75e2f. - */ -CIVETWEB_API int mg_modify_passwords_file_ha1(const char *passwords_file_name, - const char *realm, - const char *user, - const char *ha1); - - -/* Return information associated with the request. - * Use this function to implement a server and get data about a request - * from a HTTP/HTTPS client. - * Note: Before CivetWeb 1.10, this function could be used to read - * a response from a server, when implementing a client, although the - * values were never returned in appropriate mg_request_info elements. - * It is strongly advised to use mg_get_response_info for clients. - */ -CIVETWEB_API const struct mg_request_info * -mg_get_request_info(const struct mg_connection *); - - -/* Return information associated with a HTTP/HTTPS response. - * Use this function in a client, to check the response from - * the server. */ -CIVETWEB_API const struct mg_response_info * -mg_get_response_info(const struct mg_connection *); - - -/* Send data to the client. - Return: - 0 when the connection has been closed - -1 on error - >0 number of bytes written on success */ -CIVETWEB_API int mg_write(struct mg_connection *, const void *buf, size_t len); - - -/* Send data to a websocket client wrapped in a websocket frame. Uses - mg_lock_connection to ensure that the transmission is not interrupted, - i.e., when the application is proactively communicating and responding to - a request simultaneously. - - Send data to a websocket client wrapped in a websocket frame. - This function is available when civetweb is compiled with -DUSE_WEBSOCKET - - Return: - 0 when the connection has been closed - -1 on error - >0 number of bytes written on success */ -CIVETWEB_API int mg_websocket_write(struct mg_connection *conn, - int opcode, - const char *data, - size_t data_len); - - -/* Send data to a websocket server wrapped in a masked websocket frame. Uses - mg_lock_connection to ensure that the transmission is not interrupted, - i.e., when the application is proactively communicating and responding to - a request simultaneously. - - Send data to a websocket server wrapped in a masked websocket frame. - This function is available when civetweb is compiled with -DUSE_WEBSOCKET - - Return: - 0 when the connection has been closed - -1 on error - >0 number of bytes written on success */ -CIVETWEB_API int mg_websocket_client_write(struct mg_connection *conn, - int opcode, - const char *data, - size_t data_len); - - -/* Blocks until unique access is obtained to this connection. Intended for use - with websockets only. - Invoke this before mg_write or mg_printf when communicating with a - websocket if your code has server-initiated communication as well as - communication in direct response to a message. - Do not acquire this lock while holding mg_lock_context(). */ -CIVETWEB_API void mg_lock_connection(struct mg_connection *conn); -CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn); - - -/* Lock server context. This lock may be used to protect resources - that are shared between different connection/worker threads. - If the given context is not server, these functions do nothing. */ -CIVETWEB_API void mg_lock_context(struct mg_context *ctx); -CIVETWEB_API void mg_unlock_context(struct mg_context *ctx); - - -/* WebSocket OpcCodes, from http://tools.ietf.org/html/rfc6455 */ -enum { - MG_WEBSOCKET_OPCODE_CONTINUATION = 0x0, - MG_WEBSOCKET_OPCODE_TEXT = 0x1, - MG_WEBSOCKET_OPCODE_BINARY = 0x2, - MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8, - MG_WEBSOCKET_OPCODE_PING = 0x9, - MG_WEBSOCKET_OPCODE_PONG = 0xa -}; - - -/* Macros for enabling compiler-specific checks for printf-like arguments. */ -#undef PRINTF_FORMAT_STRING -#if defined(_MSC_VER) && _MSC_VER >= 1400 -#include -#if defined(_MSC_VER) && _MSC_VER > 1400 -#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s -#else -#define PRINTF_FORMAT_STRING(s) __format_string s -#endif -#else -#define PRINTF_FORMAT_STRING(s) s -#endif - -#ifdef __GNUC__ -#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y))) -#else -#define PRINTF_ARGS(x, y) -#endif - - -/* Send data to the client using printf() semantics. - Works exactly like mg_write(), but allows to do message formatting. */ -CIVETWEB_API int mg_printf(struct mg_connection *, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(2, 3); - - -/* Send a part of the message body, if chunked transfer encoding is set. - * Only use this function after sending a complete HTTP request or response - * header with "Transfer-Encoding: chunked" set. */ -CIVETWEB_API int mg_send_chunk(struct mg_connection *conn, - const char *chunk, - unsigned int chunk_len); - - -/* Send contents of the entire file together with HTTP headers. - * Parameters: - * conn: Current connection information. - * path: Full path to the file to send. - * This function has been superseded by mg_send_mime_file - */ -CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path); - - -/* Send contents of the file without HTTP headers. - * The code must send a valid HTTP response header before using this function. - * - * Parameters: - * conn: Current connection information. - * path: Full path to the file to send. - * - * Return: - * < 0 Error - */ -CIVETWEB_API int mg_send_file_body(struct mg_connection *conn, - const char *path); - - -/* Send HTTP error reply. */ -CIVETWEB_API int mg_send_http_error(struct mg_connection *conn, - int status_code, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(3, 4); - - -/* Send "HTTP 200 OK" response header. - * After calling this function, use mg_write or mg_send_chunk to send the - * response body. - * Parameters: - * conn: Current connection handle. - * mime_type: Set Content-Type for the following content. - * content_length: Size of the following content, if content_length >= 0. - * Will set transfer-encoding to chunked, if set to -1. - * Return: - * < 0 Error - */ -CIVETWEB_API int mg_send_http_ok(struct mg_connection *conn, - const char *mime_type, - long long content_length); - - -/* Send "HTTP 30x" redirect response. - * The response has content-size zero: do not send any body data after calling - * this function. - * Parameters: - * conn: Current connection handle. - * target_url: New location. - * redirect_code: HTTP redirect type. Could be 301, 302, 303, 307, 308. - * Return: - * < 0 Error (-1 send error, -2 parameter error) - */ -CIVETWEB_API int mg_send_http_redirect(struct mg_connection *conn, - const char *target_url, - int redirect_code); - - -/* Send HTTP digest access authentication request. - * Browsers will send a user name and password in their next request, showing - * an authentication dialog if the password is not stored. - * Parameters: - * conn: Current connection handle. - * realm: Authentication realm. If NULL is supplied, the sever domain - * set in the authentication_domain configuration is used. - * Return: - * < 0 Error - */ -CIVETWEB_API int -mg_send_digest_access_authentication_request(struct mg_connection *conn, - const char *realm); - - -/* Check if the current request has a valid authentication token set. - * A file is used to provide a list of valid user names, realms and - * password hashes. The file can be created and modified using the - * mg_modify_passwords_file API function. - * Parameters: - * conn: Current connection handle. - * realm: Authentication realm. If NULL is supplied, the sever domain - * set in the authentication_domain configuration is used. - * filename: Path and name of a file storing multiple password hashes. - * Return: - * > 0 Valid authentication - * 0 Invalid authentication - * < 0 Error (all values < 0 should be considered as invalid - * authentication, future error codes will have negative - * numbers) - * -1 Parameter error - * -2 File not found - */ -CIVETWEB_API int -mg_check_digest_access_authentication(struct mg_connection *conn, - const char *realm, - const char *filename); - - -/* Send contents of the entire file together with HTTP headers. - * Parameters: - * conn: Current connection handle. - * path: Full path to the file to send. - * mime_type: Content-Type for file. NULL will cause the type to be - * looked up by the file extension. - */ -CIVETWEB_API void mg_send_mime_file(struct mg_connection *conn, - const char *path, - const char *mime_type); - - -/* Send contents of the entire file together with HTTP headers. - Parameters: - conn: Current connection information. - path: Full path to the file to send. - mime_type: Content-Type for file. NULL will cause the type to be - looked up by the file extension. - additional_headers: Additional custom header fields appended to the header. - Each header should start with an X-, to ensure it is - not included twice. - NULL does not append anything. -*/ -CIVETWEB_API void mg_send_mime_file2(struct mg_connection *conn, - const char *path, - const char *mime_type, - const char *additional_headers); - - -/* Store body data into a file. */ -CIVETWEB_API long long mg_store_body(struct mg_connection *conn, - const char *path); -/* Read entire request body and store it in a file "path". - Return: - < 0 Error - >= 0 Number of bytes stored in file "path". -*/ - - -/* Read data from the remote end, return number of bytes read. - Return: - 0 connection has been closed by peer. No more data could be read. - < 0 read error. No more data could be read from the connection. - > 0 number of bytes read into the buffer. */ -CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len); - - -/* Get the value of particular HTTP header. - - This is a helper function. It traverses request_info->http_headers array, - and if the header is present in the array, returns its value. If it is - not present, NULL is returned. */ -CIVETWEB_API const char *mg_get_header(const struct mg_connection *, - const char *name); - - -/* Get a value of particular form variable. - - Parameters: - data: pointer to form-uri-encoded buffer. This could be either POST data, - or request_info.query_string. - data_len: length of the encoded data. - var_name: variable name to decode from the buffer - dst: destination buffer for the decoded variable - dst_len: length of the destination buffer - - Return: - On success, length of the decoded variable. - On error: - -1 (variable not found). - -2 (destination buffer is NULL, zero length or too small to hold the - decoded variable). - - Destination buffer is guaranteed to be '\0' - terminated if it is not - NULL or zero length. */ -CIVETWEB_API int mg_get_var(const char *data, - size_t data_len, - const char *var_name, - char *dst, - size_t dst_len); - - -/* Get a value of particular form variable. - - Parameters: - data: pointer to form-uri-encoded buffer. This could be either POST data, - or request_info.query_string. - data_len: length of the encoded data. - var_name: variable name to decode from the buffer - dst: destination buffer for the decoded variable - dst_len: length of the destination buffer - occurrence: which occurrence of the variable, 0 is the 1st, 1 the 2nd, ... - this makes it possible to parse a query like - b=x&a=y&a=z which will have occurrence values b:0, a:0 and a:1 - - Return: - On success, length of the decoded variable. - On error: - -1 (variable not found). - -2 (destination buffer is NULL, zero length or too small to hold the - decoded variable). - - Destination buffer is guaranteed to be '\0' - terminated if it is not - NULL or zero length. */ -CIVETWEB_API int mg_get_var2(const char *data, - size_t data_len, - const char *var_name, - char *dst, - size_t dst_len, - size_t occurrence); - - -/* Split form encoded data into a list of key value pairs. - A form encoded input might be a query string, the body of a - x-www-form-urlencoded POST request or any other data with this - structure: "keyName1=value1&keyName2=value2&keyName3=value3". - Values might be percent-encoded - this function will transform - them to the unencoded characters. - The input string is modified by this function: To split the - "query_string" member of struct request_info, create a copy first - (e.g., using strdup). - The function itself does not allocate memory. Thus, it is not - required to free any pointer returned from this function. - The output list of is limited to MG_MAX_FORM_FIELDS name-value- - pairs. The default value is reasonably oversized for typical - applications, however, for special purpose systems it might be - required to increase this value at compile time. - - Parameters: - data: form encoded input string. Will be modified by this function. - form_fields: output list of name/value-pairs. A buffer with a size - specified by num_form_fields must be provided by the - caller. - num_form_fields: Size of provided form_fields buffer in number of - "struct mg_header" elements. - - Return: - On success: number of form_fields filled - On error: - -1 (parameter error). */ -CIVETWEB_API int mg_split_form_urlencoded(char *data, - struct mg_header *form_fields, - unsigned num_form_fields); - - -/* Fetch value of certain cookie variable into the destination buffer. - - Destination buffer is guaranteed to be '\0' - terminated. In case of - failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same - parameter. This function returns only first occurrence. - - Return: - On success, value length. - On error: - -1 (either "Cookie:" header is not present at all or the requested - parameter is not found). - -2 (destination buffer is NULL, zero length or too small to hold the - value). */ -CIVETWEB_API int mg_get_cookie(const char *cookie, - const char *var_name, - char *buf, - size_t buf_len); - - -/* Download data from the remote web server. - host: host name to connect to, e.g. "foo.com", or "10.12.40.1". - port: port number, e.g. 80. - use_ssl: whether to use SSL connection. - error_buffer, error_buffer_size: error message placeholder. - request_fmt,...: HTTP request. - Return: - On success, valid pointer to the new connection, suitable for mg_read(). - On error, NULL. error_buffer contains error message. - Example: - char ebuf[100]; - struct mg_connection *conn; - conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf), - "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n"); - - mg_download is equivalent to calling mg_connect_client followed by - mg_printf and mg_get_response. Using these three functions directly may - allow more control as compared to using mg_download. - */ -CIVETWEB_API struct mg_connection * -mg_download(const char *host, - int port, - int use_ssl, - char *error_buffer, - size_t error_buffer_size, - PRINTF_FORMAT_STRING(const char *request_fmt), - ...) PRINTF_ARGS(6, 7); - - -/* Close the connection opened by mg_download(). */ -CIVETWEB_API void mg_close_connection(struct mg_connection *conn); - - -/* This structure contains callback functions for handling form fields. - It is used as an argument to mg_handle_form_request. */ -struct mg_form_data_handler { - /* This callback function is called, if a new field has been found. - * The return value of this callback is used to define how the field - * should be processed. - * - * Parameters: - * key: Name of the field ("name" property of the HTML input field). - * filename: Name of a file to upload, at the client computer. - * Only set for input fields of type "file", otherwise NULL. - * path: Output parameter: File name (incl. path) to store the file - * at the server computer. Only used if MG_FORM_FIELD_STORAGE_STORE - * is returned by this callback. Existing files will be - * overwritten. - * pathlen: Length of the buffer for path. - * user_data: Value of the member user_data of mg_form_data_handler - * - * Return value: - * The callback must return the intended storage for this field - * (See MG_FORM_FIELD_STORAGE_*). - */ - int (*field_found)(const char *key, - const char *filename, - char *path, - size_t pathlen, - void *user_data); - - /* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_GET, - * this callback will receive the field data. - * - * Parameters: - * key: Name of the field ("name" property of the HTML input field). - * value: Value of the input field. - * user_data: Value of the member user_data of mg_form_data_handler - * - * Return value: - * The return code determines how the server should continue processing - * the current request (See MG_FORM_FIELD_HANDLE_*). - */ - int (*field_get)(const char *key, - const char *value, - size_t valuelen, - void *user_data); - - /* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_STORE, - * the data will be stored into a file. If the file has been written - * successfully, this callback will be called. This callback will - * not be called for only partially uploaded files. The - * mg_handle_form_request function will either store the file completely - * and call this callback, or it will remove any partial content and - * not call this callback function. - * - * Parameters: - * path: Path of the file stored at the server. - * file_size: Size of the stored file in bytes. - * user_data: Value of the member user_data of mg_form_data_handler - * - * Return value: - * The return code determines how the server should continue processing - * the current request (See MG_FORM_FIELD_HANDLE_*). - */ - int (*field_store)(const char *path, long long file_size, void *user_data); - - /* User supplied argument, passed to all callback functions. */ - void *user_data; -}; - - -/* Return values definition for the "field_found" callback in - * mg_form_data_handler. */ -enum { - /* Skip this field (neither get nor store it). Continue with the - * next field. */ - MG_FORM_FIELD_STORAGE_SKIP = 0x0, - /* Get the field value. */ - MG_FORM_FIELD_STORAGE_GET = 0x1, - /* Store the field value into a file. */ - MG_FORM_FIELD_STORAGE_STORE = 0x2, - /* Stop parsing this request. Skip the remaining fields. */ - MG_FORM_FIELD_STORAGE_ABORT = 0x10 -}; - -/* Return values for "field_get" and "field_store" */ -enum { - /* Only "field_get": If there is more data in this field, get the next - * chunk. Otherwise: handle the next field. */ - MG_FORM_FIELD_HANDLE_GET = 0x1, - /* Handle the next field */ - MG_FORM_FIELD_HANDLE_NEXT = 0x8, - /* Stop parsing this request */ - MG_FORM_FIELD_HANDLE_ABORT = 0x10 -}; - - -/* Process form data. - * Returns the number of fields handled, or < 0 in case of an error. - * Note: It is possible that several fields are already handled successfully - * (e.g., stored into files), before the request handling is stopped with an - * error. In this case a number < 0 is returned as well. - * In any case, it is the duty of the caller to remove files once they are - * no longer required. */ -CIVETWEB_API int mg_handle_form_request(struct mg_connection *conn, - struct mg_form_data_handler *fdh); - - -/* Convenience function -- create detached thread. - Return: 0 on success, non-0 on error. */ -typedef void *(*mg_thread_func_t)(void *); -CIVETWEB_API int mg_start_thread(mg_thread_func_t f, void *p); - - -/* Return builtin mime type for the given file name. - For unrecognized extensions, "text/plain" is returned. */ -CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name); - - -/* Get text representation of HTTP status code. */ -CIVETWEB_API const char * -mg_get_response_code_text(const struct mg_connection *conn, int response_code); - - -/* Return CivetWeb version. */ -CIVETWEB_API const char *mg_version(void); - - -/* URL-decode input buffer into destination buffer. - 0-terminate the destination buffer. - form-url-encoded data differs from URI encoding in a way that it - uses '+' as character for space, see RFC 1866 section 8.2.1 - http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt - Return: length of the decoded data, or -1 if dst buffer is too small. */ -CIVETWEB_API int mg_url_decode(const char *src, - int src_len, - char *dst, - int dst_len, - int is_form_url_encoded); - - -/* URL-encode input buffer into destination buffer. - returns the length of the resulting buffer or -1 - is the buffer is too small. */ -CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len); - - -/* BASE64-encode input buffer into destination buffer. - returns -1 on OK. */ -CIVETWEB_API int mg_base64_encode(const unsigned char *src, - size_t src_len, - char *dst, - size_t *dst_len); - - -/* BASE64-decode input buffer into destination buffer. - returns -1 on OK. */ -CIVETWEB_API int mg_base64_decode(const char *src, - size_t src_len, - unsigned char *dst, - size_t *dst_len); - - -/* MD5 hash given strings. - Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of - ASCIIz strings. When function returns, buf will contain human-readable - MD5 hash. Example: - char buf[33]; - mg_md5(buf, "aa", "bb", NULL); */ -CIVETWEB_API char *mg_md5(char buf[33], ...); - - -#if !defined(MG_MATCH_CONTEXT_MAX_MATCHES) -#define MG_MATCH_CONTEXT_MAX_MATCHES (32) -#endif - -struct mg_match_element { - const char *str; /* First character matching wildcard */ - size_t len; /* Number of character matching wildcard */ -}; - -struct mg_match_context { - int case_sensitive; /* Input: 1 (case sensitive) or 0 (insensitive) */ - size_t num_matches; /* Output: Number of wildcard matches returned. */ - struct mg_match_element match[MG_MATCH_CONTEXT_MAX_MATCHES]; /* Output */ -}; - - -#if defined(MG_EXPERIMENTAL_INTERFACES) -/* Pattern matching and extraction function. - Parameters: - pat: Pattern string (see UserManual.md) - str: String to search for match patterns. - mcx: Match context (optional, can be NULL). - - Return: - Number of characters matched. - -1 if no valid match was found. - Note: 0 characters might be a valid match for some patterns. -*/ -CIVETWEB_API ptrdiff_t mg_match(const char *pat, - const char *str, - struct mg_match_context *mcx); -#endif - - -/* Print error message to the opened error log stream. - This utilizes the provided logging configuration. - conn: connection (not used for sending data, but to get perameters) - fmt: format string without the line return - ...: variable argument list - Example: - mg_cry(conn,"i like %s", "logging"); */ -CIVETWEB_API void mg_cry(const struct mg_connection *conn, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(2, 3); - - -/* utility methods to compare two buffers, case insensitive. */ -CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2); -CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len); - - -/* Connect to a websocket as a client - Parameters: - host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or - "localhost" - port: server port - use_ssl: make a secure connection to server - error_buffer, error_buffer_size: buffer for an error message - path: server path you are trying to connect to, i.e. if connection to - localhost/app, path should be "/app" - origin: value of the Origin HTTP header - data_func: callback that should be used when data is received from the - server - user_data: user supplied argument - - Return: - On success, valid mg_connection object. - On error, NULL. Se error_buffer for details. -*/ -CIVETWEB_API struct mg_connection * -mg_connect_websocket_client(const char *host, - int port, - int use_ssl, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data); - -CIVETWEB_API struct mg_connection * -mg_connect_websocket_client_extensions(const char *host, - int port, - int use_ssl, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - const char *extensions, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data); - - -/* Connect to a TCP server as a client (can be used to connect to a HTTP server) - Parameters: - host: host to connect to, i.e. "www.wikipedia.org" or "192.168.1.1" or - "localhost" - port: server port - use_ssl: make a secure connection to server - error_buffer, error_buffer_size: buffer for an error message - - Return: - On success, valid mg_connection object. - On error, NULL. Se error_buffer for details. -*/ -CIVETWEB_API struct mg_connection *mg_connect_client(const char *host, - int port, - int use_ssl, - char *error_buffer, - size_t error_buffer_size); - - -struct mg_client_options { - const char *host; - int port; - const char *client_cert; - const char *server_cert; - const char *host_name; - /* TODO: add more data */ -}; - - -CIVETWEB_API struct mg_connection * -mg_connect_client_secure(const struct mg_client_options *client_options, - char *error_buffer, - size_t error_buffer_size); - - -CIVETWEB_API struct mg_connection *mg_connect_websocket_client_secure( - const struct mg_client_options *client_options, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data); - -CIVETWEB_API struct mg_connection * -mg_connect_websocket_client_secure_extensions( - const struct mg_client_options *client_options, - char *error_buffer, - size_t error_buffer_size, - const char *path, - const char *origin, - const char *extensions, - mg_websocket_data_handler data_func, - mg_websocket_close_handler close_func, - void *user_data); - -#if defined(MG_LEGACY_INTERFACE) /* 2019-11-02 */ -enum { TIMEOUT_INFINITE = -1 }; -#endif -enum { MG_TIMEOUT_INFINITE = -1 }; - - -/* Wait for a response from the server - Parameters: - conn: connection - ebuf, ebuf_len: error message placeholder. - timeout: time to wait for a response in milliseconds (if < 0 then wait - forever) - - Return: - On success, >= 0 - On error/timeout, < 0 -*/ -CIVETWEB_API int mg_get_response(struct mg_connection *conn, - char *ebuf, - size_t ebuf_len, - int timeout); - - -/* mg_response_header_* functions can be used from server callbacks - * to prepare HTTP server response headers. Using this function will - * allow a callback to work with HTTP/1.x and HTTP/2. - */ - -/* Initialize a new HTTP response - * Parameters: - * conn: Current connection handle. - * status: HTTP status code (e.g., 200 for "OK"). - * Return: - * 0: ok - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: network error (only if built with NO_RESPONSE_BUFFERING) - */ -CIVETWEB_API int mg_response_header_start(struct mg_connection *conn, - int status); - - -/* Add a new HTTP response header line - * Parameters: - * conn: Current connection handle. - * header: Header name. - * value: Header value. - * value_len: Length of header value, excluding the terminating zero. - * Use -1 for "strlen(value)". - * Return: - * 0: ok - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: too many headers - * -5: out of memory - */ -CIVETWEB_API int mg_response_header_add(struct mg_connection *conn, - const char *header, - const char *value, - int value_len); - - -/* Add a complete header string (key + value). - * This function is less efficient as compared to mg_response_header_add, - * and should only be used to convert complete HTTP/1.x header lines. - * Parameters: - * conn: Current connection handle. - * http1_headers: Header line(s) in the form "name: value\r\n". - * Return: - * >=0: no error, number of header lines added - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: too many headers - * -5: out of memory - */ -CIVETWEB_API int mg_response_header_add_lines(struct mg_connection *conn, - const char *http1_headers); - - -/* Send http response - * Parameters: - * conn: Current connection handle. - * Return: - * 0: ok - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: sending failed (network error) - */ -CIVETWEB_API int mg_response_header_send(struct mg_connection *conn); - - -/* Check which features where set when the civetweb library has been compiled. - The function explicitly addresses compile time defines used when building - the library - it does not mean, the feature has been initialized using a - mg_init_library call. - mg_check_feature can be called anytime, even before mg_init_library has - been called. - - Parameters: - feature: specifies which feature should be checked - The value is a bit mask. The individual bits are defined as: - 1 serve files (NO_FILES not set) - 2 support HTTPS (NO_SSL not set) - 4 support CGI (NO_CGI not set) - 8 support IPv6 (USE_IPV6 set) - 16 support WebSocket (USE_WEBSOCKET set) - 32 support Lua scripts and Lua server pages (USE_LUA is set) - 64 support server side JavaScript (USE_DUKTAPE is set) - 128 support caching (NO_CACHING not set) - 256 support server statistics (USE_SERVER_STATS is set) - 512 support for on the fly compression (USE_ZLIB is set) - - These values are defined as MG_FEATURES_* - - The result is undefined, if bits are set that do not represent a - defined feature (currently: feature >= 1024). - The result is undefined, if no bit is set (feature == 0). - - Return: - If a feature is available, the corresponding bit is set - If a feature is not available, the bit is 0 -*/ -CIVETWEB_API unsigned mg_check_feature(unsigned feature); - - -/* Get information on the system. Useful for support requests. - Parameters: - buffer: Store system information as string here. - buflen: Length of buffer (including a byte required for a terminating 0). - Return: - Available size of system information, excluding a terminating 0. - The information is complete, if the return value is smaller than buflen. - The result is a JSON formatted string, the exact content may vary. - Note: - It is possible to determine the required buflen, by first calling this - function with buffer = NULL and buflen = NULL. The required buflen is - one byte more than the returned value. -*/ -CIVETWEB_API int mg_get_system_info(char *buffer, int buflen); - - -/* Get context information. Useful for server diagnosis. - Parameters: - ctx: Context handle - buffer: Store context information here. - buflen: Length of buffer (including a byte required for a terminating 0). - Return: - Available size of system information, excluding a terminating 0. - The information is complete, if the return value is smaller than buflen. - The result is a JSON formatted string, the exact content may vary. - Note: - It is possible to determine the required buflen, by first calling this - function with buffer = NULL and buflen = NULL. The required buflen is - one byte more than the returned value. However, since the available - context information changes, you should allocate a few bytes more. -*/ -CIVETWEB_API int -mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen); - - -/* Disable HTTP keep-alive on a per-connection basis. - Reference: https://github.com/civetweb/civetweb/issues/727 - Parameters: - conn: Current connection handle. -*/ -CIVETWEB_API void mg_disable_connection_keep_alive(struct mg_connection *conn); - - -#if defined(MG_EXPERIMENTAL_INTERFACES) -/* Get connection information. Useful for server diagnosis. - Parameters: - ctx: Context handle - idx: Connection index - buffer: Store context information here. - buflen: Length of buffer (including a byte required for a terminating 0). - Return: - Available size of system information, excluding a terminating 0. - The information is complete, if the return value is smaller than buflen. - The result is a JSON formatted string, the exact content may vary. - Note: - It is possible to determine the required buflen, by first calling this - function with buffer = NULL and buflen = NULL. The required buflen is - one byte more than the returned value. However, since the available - context information changes, you should allocate a few bytes more. -*/ -CIVETWEB_API int mg_get_connection_info(const struct mg_context *ctx, - int idx, - char *buffer, - int buflen); -#endif - - -/* New APIs for enhanced option and error handling. - These mg_*2 API functions have the same purpose as their original versions, - but provide additional options and/or provide improved error diagnostics. - - Note: Experimental interfaces may change -*/ -struct mg_error_data { - unsigned code; /* error code (number) */ - unsigned code_sub; /* error sub code (number) */ - char *text; /* buffer for error text */ - size_t text_buffer_size; /* size of buffer of "text" */ -}; - - -/* Values for error "code" in mg_error_data */ -enum { - /* No error */ - MG_ERROR_DATA_CODE_OK = 0u, - - /* Caller provided invalid parameter */ - MG_ERROR_DATA_CODE_INVALID_PARAM = 1u, - - /* "configuration_option" contains invalid element */ - MG_ERROR_DATA_CODE_INVALID_OPTION = 2u, - - /* Initializen TLS / SSL library failed */ - MG_ERROR_DATA_CODE_INIT_TLS_FAILED = 3u, - - /* Mandatory "configuration_option" missing */ - MG_ERROR_DATA_CODE_MISSING_OPTION = 4u, - - /* Duplicate "authentication_domain" option */ - MG_ERROR_DATA_CODE_DUPLICATE_DOMAIN = 5u, - - /* Not enough memory */ - MG_ERROR_DATA_CODE_OUT_OF_MEMORY = 6u, - - /* Server already stopped */ - MG_ERROR_DATA_CODE_SERVER_STOPPED = 7u, - - /* mg_init_library must be called first */ - MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED = 8u, - - /* Operating system function failed */ - MG_ERROR_DATA_CODE_OS_ERROR = 9u, - - /* Failed to bind to server ports */ - MG_ERROR_DATA_CODE_INIT_PORTS_FAILED = 10u, - - /* Failed to switch user (option "run_as_user") */ - MG_ERROR_DATA_CODE_INIT_USER_FAILED = 11u, - - /* Access Control List error */ - MG_ERROR_DATA_CODE_INIT_ACL_FAILED = 12u, - - /* Global password file error */ - MG_ERROR_DATA_CODE_INVALID_PASS_FILE = 13u, - - /* Lua background script init error */ - MG_ERROR_DATA_CODE_SCRIPT_ERROR = 14u, - - /* Client: Host not found, invalid IP to connect */ - MG_ERROR_DATA_CODE_HOST_NOT_FOUND = 15u, - - /* Client: TCP connect timeout */ - MG_ERROR_DATA_CODE_CONNECT_TIMEOUT = 16u, - - /* Client: TCP connect failed */ - MG_ERROR_DATA_CODE_CONNECT_FAILED = 17u, - - /* Error using TLS client certificate */ - MG_ERROR_DATA_CODE_TLS_CLIENT_CERT_ERROR = 18u, - - /* Error setting trusted TLS server certificate for client connection */ - MG_ERROR_DATA_CODE_TLS_SERVER_CERT_ERROR = 19u, - - /* Error establishing TLS connection to HTTPS server */ - MG_ERROR_DATA_CODE_TLS_CONNECT_ERROR = 20u -}; - - -struct mg_init_data { - const struct mg_callbacks *callbacks; /* callback function pointer */ - void *user_data; /* data */ - const char **configuration_options; -}; - - -#if defined(MG_EXPERIMENTAL_INTERFACES) - -CIVETWEB_API struct mg_connection * -mg_connect_client2(const char *host, - const char *protocol, - int port, - const char *path, - struct mg_init_data *init, - struct mg_error_data *error); - -CIVETWEB_API int mg_get_response2(struct mg_connection *conn, - struct mg_error_data *error, - int timeout); -#endif - - -CIVETWEB_API struct mg_context *mg_start2(struct mg_init_data *init, - struct mg_error_data *error); - -CIVETWEB_API int mg_start_domain2(struct mg_context *ctx, - const char **configuration_options, - struct mg_error_data *error); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CIVETWEB_HEADER_INCLUDED */ diff --git a/net/http/civetweb/handle_form.inl b/net/http/civetweb/handle_form.inl deleted file mode 100644 index 14235ec96765e..0000000000000 --- a/net/http/civetweb/handle_form.inl +++ /dev/null @@ -1,1117 +0,0 @@ -/* Copyright (c) 2016-2021 the Civetweb developers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -static int -url_encoded_field_found(const struct mg_connection *conn, - const char *key, - size_t key_len, - const char *filename, - size_t filename_len, - char *path, - size_t path_len, - struct mg_form_data_handler *fdh) -{ - char key_dec[1024]; - char filename_dec[1024]; - int key_dec_len; - int filename_dec_len; - int ret; - - key_dec_len = - mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); - - if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) { - return MG_FORM_FIELD_STORAGE_SKIP; - } - - if (filename) { - filename_dec_len = mg_url_decode(filename, - (int)filename_len, - filename_dec, - (int)sizeof(filename_dec), - 1); - - if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec)) - || (filename_dec_len < 0)) { - /* Log error message and skip this field. */ - mg_cry_internal(conn, "%s: Cannot decode filename", __func__); - return MG_FORM_FIELD_STORAGE_SKIP; - } - remove_dot_segments(filename_dec); - - } else { - filename_dec[0] = 0; - } - - ret = - fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data); - - if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_GET) { - if (fdh->field_get == NULL) { - mg_cry_internal(conn, - "%s: Function \"Get\" not available", - __func__); - return MG_FORM_FIELD_STORAGE_SKIP; - } - } - if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_STORE) { - if (fdh->field_store == NULL) { - mg_cry_internal(conn, - "%s: Function \"Store\" not available", - __func__); - return MG_FORM_FIELD_STORAGE_SKIP; - } - } - - return ret; -} - -static int -url_encoded_field_get( - const struct mg_connection *conn, - const char *key, - size_t key_len, - const char *value, - size_t *value_len, /* IN: number of bytes available in "value", OUT: number - of bytes processed */ - struct mg_form_data_handler *fdh) -{ - char key_dec[1024]; - - char *value_dec = (char *)mg_malloc_ctx(*value_len + 1, conn->phys_ctx); - int value_dec_len, ret; - - if (!value_dec) { - /* Log error message and stop parsing the form data. */ - mg_cry_internal(conn, - "%s: Not enough memory (required: %lu)", - __func__, - (unsigned long)(*value_len + 1)); - return MG_FORM_FIELD_STORAGE_ABORT; - } - - mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); - - if (*value_len >= 2 && value[*value_len - 2] == '%') - *value_len -= 2; - else if (*value_len >= 1 && value[*value_len - 1] == '%') - (*value_len)--; - value_dec_len = mg_url_decode( - value, (int)*value_len, value_dec, ((int)*value_len) + 1, 1); - - ret = fdh->field_get(key_dec, - value_dec, - (size_t)value_dec_len, - fdh->user_data); - - mg_free(value_dec); - - return ret; -} - -static int -unencoded_field_get(const struct mg_connection *conn, - const char *key, - size_t key_len, - const char *value, - size_t value_len, - struct mg_form_data_handler *fdh) -{ - char key_dec[1024]; - (void)conn; - - mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); - - return fdh->field_get(key_dec, value, value_len, fdh->user_data); -} - -static int -field_stored(const struct mg_connection *conn, - const char *path, - long long file_size, - struct mg_form_data_handler *fdh) -{ - /* Equivalent to "upload" callback of "mg_upload". */ - - (void)conn; /* we do not need mg_cry here, so conn is currently unused */ - - return fdh->field_store(path, file_size, fdh->user_data); -} - -static const char * -search_boundary(const char *buf, - size_t buf_len, - const char *boundary, - size_t boundary_len) -{ - char *boundary_start = "\r\n--"; - size_t boundary_start_len = strlen(boundary_start); - - /* We must do a binary search here, not a string search, since the - * buffer may contain '\x00' bytes, if binary data is transferred. */ - int clen = (int)buf_len - (int)boundary_len - boundary_start_len; - int i; - - for (i = 0; i <= clen; i++) { - if (!memcmp(buf + i, boundary_start, boundary_start_len)) { - if (!memcmp(buf + i + boundary_start_len, boundary, boundary_len)) { - return buf + i; - } - } - } - return NULL; -} - -int -mg_handle_form_request(struct mg_connection *conn, - struct mg_form_data_handler *fdh) -{ - const char *content_type; - char path[512]; - char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */ - int field_storage; - size_t buf_fill = 0; - int r; - int field_count = 0; - struct mg_file fstore = STRUCT_FILE_INITIALIZER; - int64_t file_size = 0; /* init here, to a avoid a false positive - "uninitialized variable used" warning */ - - int has_body_data = - (conn->request_info.content_length > 0) || (conn->is_chunked); - - /* Unused without filesystems */ - (void)fstore; - (void)file_size; - - /* There are three ways to encode data from a HTML form: - * 1) method: GET (default) - * The form data is in the HTTP query string. - * 2) method: POST, enctype: "application/x-www-form-urlencoded" - * The form data is in the request body. - * The body is url encoded (the default encoding for POST). - * 3) method: POST, enctype: "multipart/form-data". - * The form data is in the request body of a multipart message. - * This is the typical way to handle file upload from a form. - */ - - if (!has_body_data) { - const char *data; - - if (0 != strcmp(conn->request_info.request_method, "GET")) { - /* No body data, but not a GET request. - * This is not a valid form request. */ - return -1; - } - - /* GET request: form data is in the query string. */ - /* The entire data has already been loaded, so there is no need to - * call mg_read. We just need to split the query string into key-value - * pairs. */ - data = conn->request_info.query_string; - if (!data) { - /* No query string. */ - return -1; - } - - /* Split data in a=1&b=xy&c=3&c=4 ... */ - while (*data) { - const char *val = strchr(data, '='); - const char *next; - ptrdiff_t keylen, vallen; - - if (!val) { - break; - } - keylen = val - data; - - /* In every "field_found" callback we ask what to do with the - * data ("field_storage"). This could be: - * MG_FORM_FIELD_STORAGE_SKIP (0): - * ignore the value of this field - * MG_FORM_FIELD_STORAGE_GET (1): - * read the data and call the get callback function - * MG_FORM_FIELD_STORAGE_STORE (2): - * store the data in a file - * MG_FORM_FIELD_STORAGE_READ (3): - * let the user read the data (for parsing long data on the fly) - * MG_FORM_FIELD_STORAGE_ABORT (flag): - * stop parsing - */ - memset(path, 0, sizeof(path)); - field_count++; - field_storage = url_encoded_field_found(conn, - data, - (size_t)keylen, - NULL, - 0, - path, - sizeof(path) - 1, - fdh); - - val++; - next = strchr(val, '&'); - if (next) { - vallen = next - val; - } else { - vallen = (ptrdiff_t)strlen(val); - } - - if (field_storage == MG_FORM_FIELD_STORAGE_GET) { - /* Call callback */ - r = url_encoded_field_get( - conn, data, (size_t)keylen, val, (size_t *)&vallen, fdh); - if (r == MG_FORM_FIELD_HANDLE_ABORT) { - /* Stop request handling */ - break; - } - if (r == MG_FORM_FIELD_HANDLE_NEXT) { - /* Skip to next field */ - field_storage = MG_FORM_FIELD_STORAGE_SKIP; - } - } - - if (next) { - next++; - } else { - /* vallen may have been modified by url_encoded_field_get */ - next = val + vallen; - } - -#if !defined(NO_FILESYSTEMS) - if (field_storage == MG_FORM_FIELD_STORAGE_STORE) { - /* Store the content to a file */ - if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) { - fstore.access.fp = NULL; - } - file_size = 0; - if (fstore.access.fp != NULL) { - size_t n = (size_t) - fwrite(val, 1, (size_t)vallen, fstore.access.fp); - if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) { - mg_cry_internal(conn, - "%s: Cannot write file %s", - __func__, - path); - (void)mg_fclose(&fstore.access); - remove_bad_file(conn, path); - } - file_size += (int64_t)n; - - if (fstore.access.fp) { - r = mg_fclose(&fstore.access); - if (r == 0) { - /* stored successfully */ - r = field_stored(conn, path, file_size, fdh); - if (r == MG_FORM_FIELD_HANDLE_ABORT) { - /* Stop request handling */ - break; - } - - } else { - mg_cry_internal(conn, - "%s: Error saving file %s", - __func__, - path); - remove_bad_file(conn, path); - } - fstore.access.fp = NULL; - } - - } else { - mg_cry_internal(conn, - "%s: Cannot create file %s", - __func__, - path); - } - } -#endif /* NO_FILESYSTEMS */ - - /* if (field_storage == MG_FORM_FIELD_STORAGE_READ) { */ - /* The idea of "field_storage=read" is to let the API user read - * data chunk by chunk and to some data processing on the fly. - * This should avoid the need to store data in the server: - * It should neither be stored in memory, like - * "field_storage=get" does, nor in a file like - * "field_storage=store". - * However, for a "GET" request this does not make any much - * sense, since the data is already stored in memory, as it is - * part of the query string. - */ - /* } */ - - if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) - == MG_FORM_FIELD_STORAGE_ABORT) { - /* Stop parsing the request */ - break; - } - - /* Proceed to next entry */ - data = next; - } - - return field_count; - } - - content_type = mg_get_header(conn, "Content-Type"); - - if (!content_type - || !mg_strncasecmp(content_type, - "APPLICATION/X-WWW-FORM-URLENCODED", - 33) - || !mg_strncasecmp(content_type, - "APPLICATION/WWW-FORM-URLENCODED", - 31)) { - /* The form data is in the request body data, encoded in key/value - * pairs. */ - int all_data_read = 0; - - /* Read body data and split it in keys and values. - * The encoding is like in the "GET" case above: a=1&b&c=3&c=4. - * Here we use "POST", and read the data from the request body. - * The data read on the fly, so it is not required to buffer the - * entire request in memory before processing it. */ - for (;;) { - const char *val; - const char *next; - ptrdiff_t keylen, vallen; - ptrdiff_t used; - int end_of_key_value_pair_found = 0; - int get_block; - - if (buf_fill < (sizeof(buf) - 1)) { - - size_t to_read = sizeof(buf) - 1 - buf_fill; - r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { - /* read error */ - return -1; - } - if (r == 0) { - /* TODO: Create a function to get "all_data_read" from - * the conn object. All data is read if the Content-Length - * has been reached, or if chunked encoding is used and - * the end marker has been read, or if the connection has - * been closed. */ - all_data_read = (buf_fill == 0); - } - buf_fill += r; - buf[buf_fill] = 0; - if (buf_fill < 1) { - break; - } - } - - val = strchr(buf, '='); - - if (!val) { - break; - } - keylen = val - buf; - val++; - - /* Call callback */ - memset(path, 0, sizeof(path)); - field_count++; - field_storage = url_encoded_field_found(conn, - buf, - (size_t)keylen, - NULL, - 0, - path, - sizeof(path) - 1, - fdh); - - if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) - == MG_FORM_FIELD_STORAGE_ABORT) { - /* Stop parsing the request */ - break; - } - -#if !defined(NO_FILESYSTEMS) - if (field_storage == MG_FORM_FIELD_STORAGE_STORE) { - if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) { - fstore.access.fp = NULL; - } - file_size = 0; - if (!fstore.access.fp) { - mg_cry_internal(conn, - "%s: Cannot create file %s", - __func__, - path); - } - } -#endif /* NO_FILESYSTEMS */ - - get_block = 0; - /* Loop to read values larger than sizeof(buf)-keylen-2 */ - do { - next = strchr(val, '&'); - if (next) { - vallen = next - val; - end_of_key_value_pair_found = 1; - } else { - vallen = (ptrdiff_t)strlen(val); - end_of_key_value_pair_found = all_data_read; - } - - if (field_storage == MG_FORM_FIELD_STORAGE_GET) { -#if 0 - if (!end_of_key_value_pair_found && !all_data_read) { - /* This callback will deliver partial contents */ - } -#endif - - /* Call callback */ - r = url_encoded_field_get(conn, - ((get_block > 0) ? NULL : buf), - ((get_block > 0) - ? 0 - : (size_t)keylen), - val, - (size_t *)&vallen, - fdh); - get_block++; - if (r == MG_FORM_FIELD_HANDLE_ABORT) { - /* Stop request handling */ - break; - } - if (r == MG_FORM_FIELD_HANDLE_NEXT) { - /* Skip to next field */ - field_storage = MG_FORM_FIELD_STORAGE_SKIP; - } - } - - if (next) { - next++; - } else { - /* vallen may have been modified by url_encoded_field_get */ - next = val + vallen; - } - -#if !defined(NO_FILESYSTEMS) - if (fstore.access.fp) { - size_t n = (size_t) - fwrite(val, 1, (size_t)vallen, fstore.access.fp); - if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) { - mg_cry_internal(conn, - "%s: Cannot write file %s", - __func__, - path); - mg_fclose(&fstore.access); - remove_bad_file(conn, path); - } - file_size += (int64_t)n; - } -#endif /* NO_FILESYSTEMS */ - - if (!end_of_key_value_pair_found) { - used = next - buf; - memmove(buf, - buf + (size_t)used, - sizeof(buf) - (size_t)used); - next = buf; - buf_fill -= used; - if (buf_fill < (sizeof(buf) - 1)) { - - size_t to_read = sizeof(buf) - 1 - buf_fill; - r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { -#if !defined(NO_FILESYSTEMS) - /* read error */ - if (fstore.access.fp) { - mg_fclose(&fstore.access); - remove_bad_file(conn, path); - } - return -1; -#endif /* NO_FILESYSTEMS */ - } - if (r == 0) { - /* TODO: Create a function to get "all_data_read" - * from the conn object. All data is read if the - * Content-Length has been reached, or if chunked - * encoding is used and the end marker has been - * read, or if the connection has been closed. */ - all_data_read = (buf_fill == 0); - } - buf_fill += r; - buf[buf_fill] = 0; - if (buf_fill < 1) { - break; - } - val = buf; - } - } - - } while (!end_of_key_value_pair_found); - -#if !defined(NO_FILESYSTEMS) - if (fstore.access.fp) { - r = mg_fclose(&fstore.access); - if (r == 0) { - /* stored successfully */ - r = field_stored(conn, path, file_size, fdh); - if (r == MG_FORM_FIELD_HANDLE_ABORT) { - /* Stop request handling */ - break; - } - } else { - mg_cry_internal(conn, - "%s: Error saving file %s", - __func__, - path); - remove_bad_file(conn, path); - } - fstore.access.fp = NULL; - } -#endif /* NO_FILESYSTEMS */ - - if (all_data_read && (buf_fill == 0)) { - /* nothing more to process */ - break; - } - - /* Proceed to next entry */ - used = next - buf; - memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); - buf_fill -= used; - } - - return field_count; - } - - if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) { - /* The form data is in the request body data, encoded as multipart - * content (see https://www.ietf.org/rfc/rfc1867.txt, - * https://www.ietf.org/rfc/rfc2388.txt). */ - char *boundary; - size_t bl; - ptrdiff_t used; - struct mg_request_info part_header; - char *hbuf; - const char *content_disp, *hend, *fbeg, *fend, *nbeg, *nend; - const char *next; - unsigned part_no; - int all_data_read = 0; - - memset(&part_header, 0, sizeof(part_header)); - - /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */ - bl = 20; - while (content_type[bl] == ' ') { - bl++; - } - - /* There has to be a BOUNDARY definition in the Content-Type header */ - if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) { - /* Malformed request */ - return -1; - } - - /* Copy boundary string to variable "boundary" */ - /* fbeg is pointer to start of value of boundary */ - fbeg = content_type + bl + 9; - bl = strlen(fbeg); - boundary = (char *)mg_malloc(bl + 1); - if (!boundary) { - /* Out of memory */ - mg_cry_internal(conn, - "%s: Cannot allocate memory for boundary [%lu]", - __func__, - (unsigned long)bl); - return -1; - } - memcpy(boundary, fbeg, bl); - boundary[bl] = 0; - - /* RFC 2046 permits the boundary string to be quoted. */ - /* If the boundary is quoted, trim the quotes */ - if (boundary[0] == '"') { - hbuf = strchr(boundary + 1, '"'); - if ((!hbuf) || (*hbuf != '"')) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - *hbuf = 0; - memmove(boundary, boundary + 1, bl); - bl = strlen(boundary); - } - - /* Do some sanity checks for boundary lengths */ - if (bl > 70) { - /* From RFC 2046: - * Boundary delimiters must not appear within the - * encapsulated material, and must be no longer - * than 70 characters, not counting the two - * leading hyphens. - */ - - /* The algorithm can not work if bl >= sizeof(buf), or if buf - * can not hold the multipart header plus the boundary. - * Requests with long boundaries are not RFC compliant, maybe they - * are intended attacks to interfere with this algorithm. */ - mg_free(boundary); - return -1; - } - if (bl < 4) { - /* Sanity check: A boundary string of less than 4 bytes makes - * no sense either. */ - mg_free(boundary); - return -1; - } - - for (part_no = 0;; part_no++) { - size_t towrite, fnlen, n; - int get_block; - size_t to_read = sizeof(buf) - 1 - buf_fill; - - /* Unused without filesystems */ - (void)n; - - r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { - /* read error */ - mg_free(boundary); - return -1; - } - if (r == 0) { - all_data_read = (buf_fill == 0); - } - - buf_fill += r; - buf[buf_fill] = 0; - if (buf_fill < 1) { - /* No data */ - mg_free(boundary); - return -1; - } - - /* @see https://www.rfc-editor.org/rfc/rfc2046.html#section-5.1.1 - * - * multipart-body := [preamble CRLF] - * dash-boundary transport-padding CRLF - * body-part *encapsulation - * close-delimiter transport-padding - * [CRLF epilogue] - */ - - if (part_no == 0) { - size_t preamble_length = 0; - /* skip over the preamble until we find a complete boundary - * limit the preamble length to prevent abuse */ - /* +2 for the -- preceding the boundary */ - while (preamble_length < 1024 - && (preamble_length < buf_fill - bl) - && strncmp(buf + preamble_length + 2, boundary, bl)) { - preamble_length++; - } - /* reset the start of buf to remove the preamble */ - if (0 == strncmp(buf + preamble_length + 2, boundary, bl)) { - memmove(buf, - buf + preamble_length, - (unsigned)buf_fill - (unsigned)preamble_length); - buf_fill -= preamble_length; - buf[buf_fill] = 0; - } - } - - /* either it starts with a boundary and it's fine, or it's malformed - * because: - * - the preamble was longer than accepted - * - couldn't find a boundary at all in the body - * - didn't have a terminating boundary */ - if (buf_fill < (bl + 2) || strncmp(buf, "--", 2) - || strncmp(buf + 2, boundary, bl)) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - - /* skip the -- */ - char *boundary_start = buf + 2; - size_t transport_padding = 0; - while (boundary_start[bl + transport_padding] == ' ' - || boundary_start[bl + transport_padding] == '\t') { - transport_padding++; - } - char *boundary_end = boundary_start + bl + transport_padding; - - /* after the transport padding, if the boundary isn't - * immediately followed by a \r\n then it is either... */ - if (strncmp(boundary_end, "\r\n", 2)) { - /* ...the final boundary, and it is followed by --, (in which - * case it's the end of the request) or it's a malformed - * request */ - if (strncmp(boundary_end, "--", 2)) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - /* Ingore any epilogue here */ - break; - } - - /* skip the \r\n */ - hbuf = boundary_end + 2; - /* Next, we need to get the part header: Read until \r\n\r\n */ - hend = strstr(hbuf, "\r\n\r\n"); - if (!hend) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - - part_header.num_headers = - parse_http_headers(&hbuf, part_header.http_headers); - if ((hend + 2) != hbuf) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - - /* Skip \r\n\r\n */ - hend += 4; - - /* According to the RFC, every part has to have a header field like: - * Content-Disposition: form-data; name="..." */ - content_disp = get_header(part_header.http_headers, - part_header.num_headers, - "Content-Disposition"); - if (!content_disp) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - - /* Get the mandatory name="..." part of the Content-Disposition - * header. */ - nbeg = strstr(content_disp, "name=\""); - while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) { - /* It could be somethingname= instead of name= */ - nbeg = strstr(nbeg + 1, "name=\""); - } - - /* This line is not required, but otherwise some compilers - * generate spurious warnings. */ - nend = nbeg; - /* And others complain, the result is unused. */ - (void)nend; - - /* If name=" is found, search for the closing " */ - if (nbeg) { - nbeg += 6; - nend = strchr(nbeg, '\"'); - if (!nend) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - } else { - /* name= without quotes is also allowed */ - nbeg = strstr(content_disp, "name="); - while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) { - /* It could be somethingname= instead of name= */ - nbeg = strstr(nbeg + 1, "name="); - } - if (!nbeg) { - /* Malformed request */ - mg_free(boundary); - return -1; - } - nbeg += 5; - - /* RFC 2616 Sec. 2.2 defines a list of allowed - * separators, but many of them make no sense - * here, e.g. various brackets or slashes. - * If they are used, probably someone is - * trying to attack with curious hand made - * requests. Only ; , space and tab seem to be - * reasonable here. Ignore everything else. */ - nend = nbeg + strcspn(nbeg, ",; \t"); - } - - /* Get the optional filename="..." part of the Content-Disposition - * header. */ - fbeg = strstr(content_disp, "filename=\""); - while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) { - /* It could be somethingfilename= instead of filename= */ - fbeg = strstr(fbeg + 1, "filename=\""); - } - - /* This line is not required, but otherwise some compilers - * generate spurious warnings. */ - fend = fbeg; - - /* If filename=" is found, search for the closing " */ - if (fbeg) { - fbeg += 10; - fend = strchr(fbeg, '\"'); - - if (!fend) { - /* Malformed request (the filename field is optional, but if - * it exists, it needs to be terminated correctly). */ - mg_free(boundary); - return -1; - } - - /* TODO: check Content-Type */ - /* Content-Type: application/octet-stream */ - } - if (!fbeg) { - /* Try the same without quotes */ - fbeg = strstr(content_disp, "filename="); - while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) { - /* It could be somethingfilename= instead of filename= */ - fbeg = strstr(fbeg + 1, "filename="); - } - if (fbeg) { - fbeg += 9; - fend = fbeg + strcspn(fbeg, ",; \t"); - } - } - - if (!fbeg || !fend) { - fbeg = NULL; - fend = NULL; - fnlen = 0; - } else { - fnlen = (size_t)(fend - fbeg); - } - - /* In theory, it could be possible that someone crafts - * a request like name=filename=xyz. Check if name and - * filename do not overlap. */ - if (!(((ptrdiff_t)fbeg > (ptrdiff_t)nend) - || ((ptrdiff_t)nbeg > (ptrdiff_t)fend))) { - mg_free(boundary); - return -1; - } - - /* Call callback for new field */ - memset(path, 0, sizeof(path)); - field_count++; - field_storage = url_encoded_field_found(conn, - nbeg, - (size_t)(nend - nbeg), - ((fnlen > 0) ? fbeg : NULL), - fnlen, - path, - sizeof(path) - 1, - fdh); - - /* If the boundary is already in the buffer, get the address, - * otherwise next will be NULL. */ - next = search_boundary(hbuf, - (size_t)((buf - hbuf) + buf_fill), - boundary, - bl); - -#if !defined(NO_FILESYSTEMS) - if (field_storage == MG_FORM_FIELD_STORAGE_STORE) { - /* Store the content to a file */ - if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) { - fstore.access.fp = NULL; - } - file_size = 0; - - if (!fstore.access.fp) { - mg_cry_internal(conn, - "%s: Cannot create file %s", - __func__, - path); - } - } -#endif /* NO_FILESYSTEMS */ - - get_block = 0; - while (!next) { - /* Set "towrite" to the number of bytes available - * in the buffer */ - towrite = (size_t)(buf - hend + buf_fill); - - if (towrite < bl + 4) { - /* Not enough data stored. */ - /* Incomplete request. */ - mg_free(boundary); - return -1; - } - - /* Subtract the boundary length, to deal with - * cases the boundary is only partially stored - * in the buffer. */ - towrite -= bl + 4; - - if (field_storage == MG_FORM_FIELD_STORAGE_GET) { - r = unencoded_field_get(conn, - ((get_block > 0) ? NULL : nbeg), - ((get_block > 0) - ? 0 - : (size_t)(nend - nbeg)), - hend, - towrite, - fdh); - get_block++; - if (r == MG_FORM_FIELD_HANDLE_ABORT) { - /* Stop request handling */ - break; - } - if (r == MG_FORM_FIELD_HANDLE_NEXT) { - /* Skip to next field */ - field_storage = MG_FORM_FIELD_STORAGE_SKIP; - } - } - -#if !defined(NO_FILESYSTEMS) - if (field_storage == MG_FORM_FIELD_STORAGE_STORE) { - if (fstore.access.fp) { - - /* Store the content of the buffer. */ - n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp); - if ((n != towrite) || (ferror(fstore.access.fp))) { - mg_cry_internal(conn, - "%s: Cannot write file %s", - __func__, - path); - mg_fclose(&fstore.access); - remove_bad_file(conn, path); - } - file_size += (int64_t)n; - } - } -#endif /* NO_FILESYSTEMS */ - - memmove(buf, hend + towrite, bl + 4); - buf_fill = bl + 4; - hend = buf; - - /* Read new data */ - to_read = sizeof(buf) - 1 - buf_fill; - r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { -#if !defined(NO_FILESYSTEMS) - /* read error */ - if (fstore.access.fp) { - mg_fclose(&fstore.access); - remove_bad_file(conn, path); - } -#endif /* NO_FILESYSTEMS */ - mg_free(boundary); - return -1; - } - /* r==0 already handled, all_data_read is false here */ - - buf_fill += r; - buf[buf_fill] = 0; - /* buf_fill is at least 8 here */ - - /* Find boundary */ - next = search_boundary(buf, buf_fill, boundary, bl); - - if (!next && (r == 0)) { - /* incomplete request */ - all_data_read = 1; - } - } - - towrite = (next ? (size_t)(next - hend) : 0); - - if (field_storage == MG_FORM_FIELD_STORAGE_GET) { - /* Call callback */ - r = unencoded_field_get(conn, - ((get_block > 0) ? NULL : nbeg), - ((get_block > 0) - ? 0 - : (size_t)(nend - nbeg)), - hend, - towrite, - fdh); - if (r == MG_FORM_FIELD_HANDLE_ABORT) { - /* Stop request handling */ - break; - } - if (r == MG_FORM_FIELD_HANDLE_NEXT) { - /* Skip to next field */ - field_storage = MG_FORM_FIELD_STORAGE_SKIP; - } - } - -#if !defined(NO_FILESYSTEMS) - if (field_storage == MG_FORM_FIELD_STORAGE_STORE) { - - if (fstore.access.fp) { - n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp); - if ((n != towrite) || (ferror(fstore.access.fp))) { - mg_cry_internal(conn, - "%s: Cannot write file %s", - __func__, - path); - mg_fclose(&fstore.access); - remove_bad_file(conn, path); - } else { - file_size += (int64_t)n; - r = mg_fclose(&fstore.access); - if (r == 0) { - /* stored successfully */ - r = field_stored(conn, path, file_size, fdh); - if (r == MG_FORM_FIELD_HANDLE_ABORT) { - /* Stop request handling */ - break; - } - } else { - mg_cry_internal(conn, - "%s: Error saving file %s", - __func__, - path); - remove_bad_file(conn, path); - } - } - fstore.access.fp = NULL; - } - } -#endif /* NO_FILESYSTEMS */ - - if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) - == MG_FORM_FIELD_STORAGE_ABORT) { - /* Stop parsing the request */ - break; - } - - /* Remove from the buffer */ - if (next) { - used = next - buf + 2; - memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); - buf_fill -= used; - } else { - buf_fill = 0; - } - } - - /* All parts handled */ - mg_free(boundary); - return field_count; - } - - /* Unknown Content-Type */ - return -1; -} - -/* End of handle_form.inl */ diff --git a/net/http/civetweb/match.inl b/net/http/civetweb/match.inl deleted file mode 100644 index 34152755e3c3e..0000000000000 --- a/net/http/civetweb/match.inl +++ /dev/null @@ -1,262 +0,0 @@ -/* Reimplementation of pattern matching */ -/* This file is part of the CivetWeb web server. - * See https://github.com/civetweb/civetweb/ - */ - - -/* Initialize structure with 0 matches */ -static void -match_context_reset(struct mg_match_context *mcx) -{ - mcx->num_matches = 0; - memset(mcx->match, 0, sizeof(mcx->match)); -} - - -/* Add a new match to the list of matches */ -static void -match_context_push(const char *str, size_t len, struct mg_match_context *mcx) -{ - if (mcx->num_matches < MG_MATCH_CONTEXT_MAX_MATCHES) { - mcx->match[mcx->num_matches].str = str; - mcx->match[mcx->num_matches].len = len; - mcx->num_matches++; - } -} - - -static ptrdiff_t -mg_match_impl(const char *pat, - size_t pat_len, - const char *str, - struct mg_match_context *mcx) -{ - /* Parse string */ - size_t i_pat = 0; /* Pattern index */ - size_t i_str = 0; /* Pattern index */ - - int case_sensitive = ((mcx != NULL) ? mcx->case_sensitive : 0); /* 0 or 1 */ - - while (i_pat < pat_len) { - - /* Pattern ? matches one character, except / and NULL character */ - if ((pat[i_pat] == '?') && (str[i_str] != '\0') - && (str[i_str] != '/')) { - size_t i_str_start = i_str; - do { - /* Advance as long as there are ? */ - i_pat++; - i_str++; - } while ((i_pat < pat_len) && (pat[i_pat] == '?') - && (str[i_str] != '\0') && (str[i_str] != '/')); - - /* If we have a match context, add the substring we just found */ - if (mcx) { - match_context_push(str + i_str_start, i_str - i_str_start, mcx); - } - - /* Reached end of pattern ? */ - if (i_pat == pat_len) { - return (ptrdiff_t)i_str; - } - } - - /* Pattern $ matches end of string */ - if (pat[i_pat] == '$') { - return (str[i_str] == '\0') ? (ptrdiff_t)i_str : -1; - } - - /* Pattern * or ** matches multiple characters */ - if (pat[i_pat] == '*') { - size_t len; /* length matched by "*" or "**" */ - ptrdiff_t ret; - - i_pat++; - if ((i_pat < pat_len) && (pat[i_pat] == '*')) { - /* Pattern ** matches all */ - i_pat++; - len = strlen(str + i_str); - } else { - /* Pattern * matches all except / character */ - len = strcspn(str + i_str, "/"); - } - - if (i_pat == pat_len) { - /* End of pattern reached. Add all to match context. */ - if (mcx) { - match_context_push(str + i_str, len, mcx); - } - return ((ptrdiff_t)(i_str + len)); - } - - /* This loop searches for the longest possible match */ - do { - ret = mg_match_impl(pat + i_pat, - (pat_len - (size_t)i_pat), - str + i_str + len, - mcx); - } while ((ret == -1) && (len-- > 0)); - - /* If we have a match context, add the substring we just found */ - if (ret >= 0) { - if (mcx) { - match_context_push(str + i_str, len, mcx); - } - return ((ptrdiff_t)i_str + ret + (ptrdiff_t)len); - } - - return -1; - } - - - /* Single character compare */ - if (case_sensitive) { - if (pat[i_pat] != str[i_str]) { - /* case sensitive compare: mismatch */ - return -1; - } - } else if (lowercase(&pat[i_pat]) != lowercase(&str[i_str])) { - /* case insensitive compare: mismatch */ - return -1; - } - - i_pat++; - i_str++; - } - return (ptrdiff_t)i_str; -} - - -static ptrdiff_t -mg_match_alternatives(const char *pat, - size_t pat_len, - const char *str, - struct mg_match_context *mcx) -{ - const char *match_alternative = (const char *)memchr(pat, '|', pat_len); - - if (mcx != NULL) { - match_context_reset(mcx); - } - - while (match_alternative != NULL) { - /* Split at | for alternative match */ - size_t left_size = (size_t)(match_alternative - pat); - - /* Try left string first */ - ptrdiff_t ret = mg_match_impl(pat, left_size, str, mcx); - if (ret >= 0) { - /* A 0-byte match is also valid */ - return ret; - } - - /* Reset possible incomplete match data */ - if (mcx != NULL) { - match_context_reset(mcx); - } - - /* If no match: try right side */ - pat += left_size + 1; - pat_len -= left_size + 1; - match_alternative = (const char *)memchr(pat, '|', pat_len); - } - - /* Handled all | operators. This is the final string. */ - return mg_match_impl(pat, pat_len, str, mcx); -} - - -static int -match_compare(const void *p1, const void *p2, void *user) -{ - const struct mg_match_element *e1 = (const struct mg_match_element *)p1; - const struct mg_match_element *e2 = (const struct mg_match_element *)p2; - - /* unused */ - (void)user; - - if (e1->str > e2->str) { - return +1; - } - if (e1->str < e2->str) { - return -1; - } - return 0; -} - - -#if defined(MG_EXPERIMENTAL_INTERFACES) -CIVETWEB_API -#else -static -#endif -ptrdiff_t -mg_match(const char *pat, const char *str, struct mg_match_context *mcx) -{ - size_t pat_len = strlen(pat); - ptrdiff_t ret = mg_match_alternatives(pat, pat_len, str, mcx); - if (mcx != NULL) { - if (ret < 0) { - /* Remove possible incomplete data */ - match_context_reset(mcx); - } else { - /* Join "?*" to one pattern. */ - size_t i, j; - - /* Use difference of two array elements instead of sizeof, since - * there may be some additional padding bytes. */ - size_t elmsize = - (size_t)(&mcx->match[1]) - (size_t)(&mcx->match[0]); - - /* First sort the matches by address ("str" begin to end) */ - mg_sort(mcx->match, mcx->num_matches, elmsize, match_compare, NULL); - - /* Join consecutive matches */ - i = 1; - while (i < mcx->num_matches) { - if ((mcx->match[i - 1].str + mcx->match[i - 1].len) - == mcx->match[i].str) { - /* Two matches are consecutive. Join length. */ - mcx->match[i - 1].len += mcx->match[i].len; - - /* Shift all list elements. */ - for (j = i + 1; j < mcx->num_matches; j++) { - mcx->match[j - 1].len = mcx->match[j].len; - mcx->match[j - 1].str = mcx->match[j].str; - } - - /* Remove/blank last list element. */ - mcx->num_matches--; - mcx->match[mcx->num_matches].str = NULL; - mcx->match[mcx->num_matches].len = 0; - - } else { - i++; - } - } - } - } - return ret; -} - - -static ptrdiff_t -match_prefix(const char *pattern, size_t pattern_len, const char *str) -{ - if (pattern == NULL) { - return -1; - } - return mg_match_alternatives(pattern, pattern_len, str, NULL); -} - - -static ptrdiff_t -match_prefix_strlen(const char *pattern, const char *str) -{ - if (pattern == NULL) { - return -1; - } - return mg_match_alternatives(pattern, strlen(pattern), str, NULL); -} - -/* End of match.inl */ diff --git a/net/http/civetweb/md5.inl b/net/http/civetweb/md5.inl deleted file mode 100644 index 5dd3a6017de7f..0000000000000 --- a/net/http/civetweb/md5.inl +++ /dev/null @@ -1,472 +0,0 @@ -/* - * This an amalgamation of md5.c and md5.h into a single file - * with all static declaration to reduce linker conflicts - * in Civetweb. - * - * The MD5_STATIC declaration was added to facilitate static - * inclusion. - * No Face Press, LLC - */ - -/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.h is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Removed support for non-ANSI compilers; removed - references to Ghostscript; clarified derivation from RFC 1321; - now handles byte order either statically or dynamically. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); - added conditionalization for C++ compilation from Martin - Purschke . - 1999-05-03 lpd Original version. - */ - -#if !defined(md5_INCLUDED) -#define md5_INCLUDED - -/* - * This package supports both compile-time and run-time determination of CPU - * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be - * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is - * defined as non-zero, the code will be compiled to run only on big-endian - * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to - * run on either big- or little-endian CPUs, but will run slightly less - * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. - */ - -typedef unsigned char md5_byte_t; /* 8-bit byte */ -typedef unsigned int md5_word_t; /* 32-bit word */ - -/* Define the state of the MD5 Algorithm. */ -typedef struct md5_state_s { - md5_word_t count[2]; /* message length in bits, lsw first */ - md5_word_t abcd[4]; /* digest buffer */ - md5_byte_t buf[64]; /* accumulate block */ -} md5_state_t; - -#if defined(__cplusplus) -extern "C" { -#endif - -/* Initialize the algorithm. */ -MD5_STATIC void md5_init(md5_state_t *pms); - -/* Append a string to the message. */ -MD5_STATIC void -md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes); - -/* Finish the message and return the digest. */ -MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); - -#if defined(__cplusplus) -} /* end extern "C" */ -#endif - -#endif /* md5_INCLUDED */ - -/* - Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.c is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order - either statically or dynamically; added missing #include - in library. - 2002-03-11 lpd Corrected argument list for main(), and added int return - type, in test program and T value program. - 2002-02-21 lpd Added missing #include in test program. - 2000-07-03 lpd Patched to eliminate warnings about "constant is - unsigned in ANSI C, signed in traditional"; made test program - self-checking. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). - 1999-05-03 lpd Original version. - */ - -#if !defined(MD5_STATIC) -#include -#include -#endif - -#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ -#if defined(ARCH_IS_BIG_ENDIAN) -#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) -#else -#define BYTE_ORDER (0) -#endif - -#define T_MASK ((md5_word_t)~0) -#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) -#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) -#define T3 (0x242070db) -#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) -#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) -#define T6 (0x4787c62a) -#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) -#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) -#define T9 (0x698098d8) -#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) -#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) -#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) -#define T13 (0x6b901122) -#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) -#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) -#define T16 (0x49b40821) -#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) -#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) -#define T19 (0x265e5a51) -#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) -#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) -#define T22 (0x02441453) -#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) -#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) -#define T25 (0x21e1cde6) -#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) -#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) -#define T28 (0x455a14ed) -#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) -#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) -#define T31 (0x676f02d9) -#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) -#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) -#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) -#define T35 (0x6d9d6122) -#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) -#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) -#define T38 (0x4bdecfa9) -#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) -#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) -#define T41 (0x289b7ec6) -#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) -#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) -#define T44 (0x04881d05) -#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) -#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) -#define T47 (0x1fa27cf8) -#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) -#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) -#define T50 (0x432aff97) -#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) -#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) -#define T53 (0x655b59c3) -#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) -#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) -#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) -#define T57 (0x6fa87e4f) -#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) -#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) -#define T60 (0x4e0811a1) -#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) -#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) -#define T63 (0x2ad7d2bb) -#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) - -static void -md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) -{ - md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], - d = pms->abcd[3]; - md5_word_t t; -#if BYTE_ORDER > 0 - /* Define storage only for big-endian CPUs. */ - md5_word_t X[16]; -#else - /* Define storage for little-endian or both types of CPUs. */ - md5_word_t xbuf[16]; - const md5_word_t *X; -#endif - - { -#if BYTE_ORDER == 0 - /* - * Determine dynamically whether this is a big-endian or - * little-endian machine, since we can use a more efficient - * algorithm on the latter. - */ - static const int w = 1; - - if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ -#endif -#if BYTE_ORDER <= 0 /* little-endian */ - { - /* - * On little-endian machines, we can process properly aligned - * data without copying it. - */ - if (!(((uintptr_t)data) & 3)) { - /* data are properly aligned, a direct assignment is possible */ - /* cast through a (void *) should avoid a compiler warning, - see - https://github.com/bel2125/civetweb/issues/94#issuecomment-98112861 - */ - X = (const md5_word_t *)(const void *)data; - } else { - /* not aligned */ - memcpy(xbuf, data, 64); - X = xbuf; - } - } -#endif -#if BYTE_ORDER == 0 - else /* dynamic big-endian */ -#endif -#if BYTE_ORDER >= 0 /* big-endian */ - { - /* - * On big-endian machines, we must arrange the bytes in the - * right order. - */ - const md5_byte_t *xp = data; - int i; - -#if BYTE_ORDER == 0 - X = xbuf; /* (dynamic only) */ -#else -#define xbuf X /* (static only) */ -#endif - for (i = 0; i < 16; ++i, xp += 4) - xbuf[i] = (md5_word_t)(xp[0]) + (md5_word_t)(xp[1] << 8) - + (md5_word_t)(xp[2] << 16) - + (md5_word_t)(xp[3] << 24); - } -#endif - } - -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - -/* Round 1. */ -/* Let [abcd k s i] denote the operation - a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ -#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = (a) + F(b, c, d) + X[k] + (Ti); \ - (a) = ROTATE_LEFT(t, s) + (b) - - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 7, T1); - SET(d, a, b, c, 1, 12, T2); - SET(c, d, a, b, 2, 17, T3); - SET(b, c, d, a, 3, 22, T4); - SET(a, b, c, d, 4, 7, T5); - SET(d, a, b, c, 5, 12, T6); - SET(c, d, a, b, 6, 17, T7); - SET(b, c, d, a, 7, 22, T8); - SET(a, b, c, d, 8, 7, T9); - SET(d, a, b, c, 9, 12, T10); - SET(c, d, a, b, 10, 17, T11); - SET(b, c, d, a, 11, 22, T12); - SET(a, b, c, d, 12, 7, T13); - SET(d, a, b, c, 13, 12, T14); - SET(c, d, a, b, 14, 17, T15); - SET(b, c, d, a, 15, 22, T16); -#undef SET - -/* Round 2. */ -/* Let [abcd k s i] denote the operation - a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ -#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = (a) + G(b, c, d) + X[k] + (Ti); \ - (a) = ROTATE_LEFT(t, s) + (b) - - /* Do the following 16 operations. */ - SET(a, b, c, d, 1, 5, T17); - SET(d, a, b, c, 6, 9, T18); - SET(c, d, a, b, 11, 14, T19); - SET(b, c, d, a, 0, 20, T20); - SET(a, b, c, d, 5, 5, T21); - SET(d, a, b, c, 10, 9, T22); - SET(c, d, a, b, 15, 14, T23); - SET(b, c, d, a, 4, 20, T24); - SET(a, b, c, d, 9, 5, T25); - SET(d, a, b, c, 14, 9, T26); - SET(c, d, a, b, 3, 14, T27); - SET(b, c, d, a, 8, 20, T28); - SET(a, b, c, d, 13, 5, T29); - SET(d, a, b, c, 2, 9, T30); - SET(c, d, a, b, 7, 14, T31); - SET(b, c, d, a, 12, 20, T32); -#undef SET - -/* Round 3. */ -/* Let [abcd k s t] denote the operation - a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define SET(a, b, c, d, k, s, Ti) \ - t = (a) + H(b, c, d) + X[k] + (Ti); \ - (a) = ROTATE_LEFT(t, s) + b - - /* Do the following 16 operations. */ - SET(a, b, c, d, 5, 4, T33); - SET(d, a, b, c, 8, 11, T34); - SET(c, d, a, b, 11, 16, T35); - SET(b, c, d, a, 14, 23, T36); - SET(a, b, c, d, 1, 4, T37); - SET(d, a, b, c, 4, 11, T38); - SET(c, d, a, b, 7, 16, T39); - SET(b, c, d, a, 10, 23, T40); - SET(a, b, c, d, 13, 4, T41); - SET(d, a, b, c, 0, 11, T42); - SET(c, d, a, b, 3, 16, T43); - SET(b, c, d, a, 6, 23, T44); - SET(a, b, c, d, 9, 4, T45); - SET(d, a, b, c, 12, 11, T46); - SET(c, d, a, b, 15, 16, T47); - SET(b, c, d, a, 2, 23, T48); -#undef SET - -/* Round 4. */ -/* Let [abcd k s t] denote the operation - a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ -#define I(x, y, z) ((y) ^ ((x) | ~(z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = (a) + I(b, c, d) + X[k] + (Ti); \ - (a) = ROTATE_LEFT(t, s) + (b) - - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 6, T49); - SET(d, a, b, c, 7, 10, T50); - SET(c, d, a, b, 14, 15, T51); - SET(b, c, d, a, 5, 21, T52); - SET(a, b, c, d, 12, 6, T53); - SET(d, a, b, c, 3, 10, T54); - SET(c, d, a, b, 10, 15, T55); - SET(b, c, d, a, 1, 21, T56); - SET(a, b, c, d, 8, 6, T57); - SET(d, a, b, c, 15, 10, T58); - SET(c, d, a, b, 6, 15, T59); - SET(b, c, d, a, 13, 21, T60); - SET(a, b, c, d, 4, 6, T61); - SET(d, a, b, c, 11, 10, T62); - SET(c, d, a, b, 2, 15, T63); - SET(b, c, d, a, 9, 21, T64); -#undef SET - - /* Then perform the following additions. (That is increment each - of the four registers by the value it had before this block - was started.) */ - pms->abcd[0] += a; - pms->abcd[1] += b; - pms->abcd[2] += c; - pms->abcd[3] += d; -} - -MD5_STATIC void -md5_init(md5_state_t *pms) -{ - pms->count[0] = pms->count[1] = 0; - pms->abcd[0] = 0x67452301; - pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; - pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; - pms->abcd[3] = 0x10325476; -} - -MD5_STATIC void -md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes) -{ - const md5_byte_t *p = data; - size_t left = nbytes; - size_t offset = (pms->count[0] >> 3) & 63; - md5_word_t nbits = (md5_word_t)(nbytes << 3); - - if (nbytes <= 0) - return; - - /* Update the message length. */ - pms->count[1] += (md5_word_t)(nbytes >> 29); - pms->count[0] += nbits; - if (pms->count[0] < nbits) - pms->count[1]++; - - /* Process an initial partial block. */ - if (offset) { - size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes); - - memcpy(pms->buf + offset, p, copy); - if (offset + copy < 64) - return; - p += copy; - left -= copy; - md5_process(pms, pms->buf); - } - - /* Process full blocks. */ - for (; left >= 64; p += 64, left -= 64) - md5_process(pms, p); - - /* Process a final partial block. */ - if (left) - memcpy(pms->buf, p, left); -} - -MD5_STATIC void -md5_finish(md5_state_t *pms, md5_byte_t digest[16]) -{ - static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - md5_byte_t data[8]; - int i; - - /* Save the length before padding. */ - for (i = 0; i < 8; ++i) - data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); - /* Pad to 56 bytes mod 64. */ - md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); - /* Append the length. */ - md5_append(pms, data, 8); - for (i = 0; i < 16; ++i) - digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); -} - - -/* End of md5.inl */ diff --git a/net/http/civetweb/openssl_dl.inl b/net/http/civetweb/openssl_dl.inl deleted file mode 100644 index 46e5e6fee8294..0000000000000 --- a/net/http/civetweb/openssl_dl.inl +++ /dev/null @@ -1,545 +0,0 @@ -/* Copyright (c) 2013-2021 the Civetweb developers - * Copyright (c) 2004-2013 Sergey Lyubka - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -typedef struct ssl_st SSL; -typedef struct ssl_method_st SSL_METHOD; -typedef struct ssl_ctx_st SSL_CTX; -typedef struct x509_store_ctx_st X509_STORE_CTX; -typedef struct x509_name X509_NAME; -typedef struct asn1_integer ASN1_INTEGER; -typedef struct bignum BIGNUM; -typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS; -typedef struct evp_md EVP_MD; -typedef struct x509 X509; - - -#define SSL_CTRL_OPTIONS (32) -#define SSL_CTRL_CLEAR_OPTIONS (77) -#define SSL_CTRL_SET_ECDH_AUTO (94) - -#define OPENSSL_INIT_NO_LOAD_SSL_STRINGS 0x00100000L -#define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L -#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L - -#define SSL_VERIFY_NONE (0) -#define SSL_VERIFY_PEER (1) -#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2) -#define SSL_VERIFY_CLIENT_ONCE (4) - -#define SSL_OP_ALL (0x80000BFFul) - -#define SSL_OP_NO_SSLv2 (0x01000000ul) -#define SSL_OP_NO_SSLv3 (0x02000000ul) -#define SSL_OP_NO_TLSv1 (0x04000000ul) -#define SSL_OP_NO_TLSv1_2 (0x08000000ul) -#define SSL_OP_NO_TLSv1_1 (0x10000000ul) -#define SSL_OP_NO_TLSv1_3 (0x20000000ul) -#define SSL_OP_SINGLE_DH_USE (0x00100000ul) -#define SSL_OP_CIPHER_SERVER_PREFERENCE (0x00400000ul) -#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (0x00010000ul) -#define SSL_OP_NO_COMPRESSION (0x00020000ul) -#define SSL_OP_NO_RENEGOTIATION (0x40000000ul) - -#define SSL_CB_HANDSHAKE_START (0x10) -#define SSL_CB_HANDSHAKE_DONE (0x20) - -#define SSL_ERROR_NONE (0) -#define SSL_ERROR_SSL (1) -#define SSL_ERROR_WANT_READ (2) -#define SSL_ERROR_WANT_WRITE (3) -#define SSL_ERROR_WANT_X509_LOOKUP (4) -#define SSL_ERROR_SYSCALL (5) /* see errno */ -#define SSL_ERROR_ZERO_RETURN (6) -#define SSL_ERROR_WANT_CONNECT (7) -#define SSL_ERROR_WANT_ACCEPT (8) - -#define TLSEXT_TYPE_server_name (0) -#define TLSEXT_NAMETYPE_host_name (0) -#define SSL_TLSEXT_ERR_OK (0) -#define SSL_TLSEXT_ERR_ALERT_WARNING (1) -#define SSL_TLSEXT_ERR_ALERT_FATAL (2) -#define SSL_TLSEXT_ERR_NOACK (3) - -#define SSL_SESS_CACHE_BOTH (3) - -enum ssl_func_category { - TLS_Mandatory, /* required for HTTPS */ - TLS_ALPN, /* required for Application Layer Protocol Negotiation */ - TLS_END_OF_LIST -}; - -/* Check if all TLS functions/features are available */ -static int tls_feature_missing[TLS_END_OF_LIST] = {0}; - -struct ssl_func { - const char *name; /* SSL function name */ - enum ssl_func_category required; /* Mandatory or optional */ - void (*ptr)(void); /* Function pointer */ -}; - - -#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \ - && !defined(NO_SSL_DL) - -#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr) -#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr) -#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr) -#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr) -#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr) -#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr) -#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr) -#define SSL_new (*(SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr) -#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr) -#define TLS_server_method (*(SSL_METHOD * (*)(void)) ssl_sw[9].ptr) -#define OPENSSL_init_ssl \ - (*(int (*)(uint64_t opts, \ - const OPENSSL_INIT_SETTINGS *settings))ssl_sw[10] \ - .ptr) -#define SSL_CTX_use_PrivateKey_file \ - (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr) -#define SSL_CTX_use_certificate_file \ - (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr) -#define SSL_CTX_set_default_passwd_cb \ - (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr) -#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr) -#define SSL_CTX_use_certificate_chain_file \ - (*(int (*)(SSL_CTX *, const char *))ssl_sw[15].ptr) -#define TLS_client_method (*(SSL_METHOD * (*)(void)) ssl_sw[16].ptr) -#define SSL_pending (*(int (*)(SSL *))ssl_sw[17].ptr) -#define SSL_CTX_set_verify \ - (*(void (*)(SSL_CTX *, \ - int, \ - int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[18] \ - .ptr) -#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[19].ptr) -#define SSL_CTX_load_verify_locations \ - (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[20].ptr) -#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[21].ptr) -#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[22].ptr) -#define SSL_get_peer_certificate (*(X509 * (*)(SSL *)) ssl_sw[23].ptr) -#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[24].ptr) -#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *)) ssl_sw[25].ptr) -#define SSL_CIPHER_get_name \ - (*(const char *(*)(const SSL_CIPHER *))ssl_sw[26].ptr) -#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[27].ptr) -#define SSL_CTX_set_session_id_context \ - (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[28].ptr) -#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[29].ptr) -#define SSL_CTX_set_cipher_list \ - (*(int (*)(SSL_CTX *, const char *))ssl_sw[30].ptr) -#define SSL_CTX_set_options \ - (*(unsigned long (*)(SSL_CTX *, unsigned long))ssl_sw[31].ptr) -#define SSL_CTX_set_info_callback \ - (*(void (*)(SSL_CTX * ctx, void (*callback)(const SSL *, int, int))) \ - ssl_sw[32] \ - .ptr) -#define SSL_get_ex_data (*(char *(*)(const SSL *, int))ssl_sw[33].ptr) -#define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr) -#define SSL_CTX_callback_ctrl \ - (*(long (*)(SSL_CTX *, int, void (*)(void)))ssl_sw[35].ptr) -#define SSL_get_servername \ - (*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr) -#define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *)) ssl_sw[37].ptr) -#define SSL_ctrl (*(long (*)(SSL *, int, long, void *))ssl_sw[38].ptr) -#define SSL_CTX_set_alpn_protos \ - (*(int (*)(SSL_CTX *, const unsigned char *, unsigned))ssl_sw[39].ptr) -typedef int (*tSSL_alpn_select_cb)(SSL *ssl, - const unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg); -#define SSL_CTX_set_alpn_select_cb \ - (*(void (*)(SSL_CTX *, tSSL_alpn_select_cb, void *))ssl_sw[40].ptr) -typedef int (*tSSL_next_protos_advertised_cb)(SSL *ssl, - const unsigned char **out, - unsigned int *outlen, - void *arg); -#define SSL_CTX_set_next_protos_advertised_cb \ - (*(void (*)(SSL_CTX *, tSSL_next_protos_advertised_cb, void *))ssl_sw[41] \ - .ptr) - -#define SSL_CTX_set_timeout (*(long (*)(SSL_CTX *, long))ssl_sw[42].ptr) - -#define SSL_CTX_clear_options(ctx, op) \ - SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL) -#define SSL_CTX_set_ecdh_auto(ctx, onoff) \ - SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL) - -#define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53 -#define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54 -#define SSL_CTRL_SET_TLSEXT_HOSTNAME 55 -#define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \ - SSL_CTX_callback_ctrl(ctx, \ - SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \ - (void (*)(void))cb) -#define SSL_set_tlsext_host_name(ctx, arg) \ - SSL_ctrl(ctx, SSL_CTRL_SET_TLSEXT_HOSTNAME, 0, (void *)arg) - -#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore) -#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter) - -#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg)) -#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0)) - -#define SSL_CTX_sess_set_cache_size(ctx, size) SSL_CTX_ctrl(ctx, 42, size, NULL) -#define SSL_CTX_set_session_cache_mode(ctx, mode) \ - SSL_CTX_ctrl(ctx, 44, mode, NULL) - - -#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[0].ptr) -#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[1].ptr) -#define CONF_modules_unload (*(void (*)(int))crypto_sw[2].ptr) -#define X509_free (*(void (*)(X509 *))crypto_sw[3].ptr) -#define X509_get_subject_name (*(X509_NAME * (*)(X509 *)) crypto_sw[4].ptr) -#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *)) crypto_sw[5].ptr) -#define X509_NAME_oneline \ - (*(char *(*)(X509_NAME *, char *, int))crypto_sw[6].ptr) -#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *)) crypto_sw[7].ptr) -#define EVP_get_digestbyname \ - (*(const EVP_MD *(*)(const char *))crypto_sw[8].ptr) -#define EVP_Digest \ - (*(int (*)( \ - const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \ - crypto_sw[9] \ - .ptr) -#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[10].ptr) -#define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[11].ptr) -#define ASN1_INTEGER_to_BN \ - (*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn)) crypto_sw[12].ptr) -#define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[13].ptr) -#define CRYPTO_free (*(void (*)(void *addr))crypto_sw[14].ptr) -#define ERR_clear_error (*(void (*)(void))crypto_sw[15].ptr) - -#define OPENSSL_free(a) CRYPTO_free(a) - -#define OPENSSL_REMOVE_THREAD_STATE() - -/* init_ssl_ctx() function updates this array. - * It loads SSL library dynamically and changes NULLs to the actual addresses - * of respective functions. The macros above (like SSL_connect()) are really - * just calling these functions indirectly via the pointer. */ -static struct ssl_func ssl_sw[] = { - {"SSL_free", TLS_Mandatory, NULL}, - {"SSL_accept", TLS_Mandatory, NULL}, - {"SSL_connect", TLS_Mandatory, NULL}, - {"SSL_read", TLS_Mandatory, NULL}, - {"SSL_write", TLS_Mandatory, NULL}, - {"SSL_get_error", TLS_Mandatory, NULL}, - {"SSL_set_fd", TLS_Mandatory, NULL}, - {"SSL_new", TLS_Mandatory, NULL}, - {"SSL_CTX_new", TLS_Mandatory, NULL}, - {"TLS_server_method", TLS_Mandatory, NULL}, - {"OPENSSL_init_ssl", TLS_Mandatory, NULL}, - {"SSL_CTX_use_PrivateKey_file", TLS_Mandatory, NULL}, - {"SSL_CTX_use_certificate_file", TLS_Mandatory, NULL}, - {"SSL_CTX_set_default_passwd_cb", TLS_Mandatory, NULL}, - {"SSL_CTX_free", TLS_Mandatory, NULL}, - {"SSL_CTX_use_certificate_chain_file", TLS_Mandatory, NULL}, - {"TLS_client_method", TLS_Mandatory, NULL}, - {"SSL_pending", TLS_Mandatory, NULL}, - {"SSL_CTX_set_verify", TLS_Mandatory, NULL}, - {"SSL_shutdown", TLS_Mandatory, NULL}, - {"SSL_CTX_load_verify_locations", TLS_Mandatory, NULL}, - {"SSL_CTX_set_default_verify_paths", TLS_Mandatory, NULL}, - {"SSL_CTX_set_verify_depth", TLS_Mandatory, NULL}, -#if defined(OPENSSL_API_3_0) - {"SSL_get1_peer_certificate", TLS_Mandatory, NULL}, -#else - {"SSL_get_peer_certificate", TLS_Mandatory, NULL}, -#endif - {"SSL_get_version", TLS_Mandatory, NULL}, - {"SSL_get_current_cipher", TLS_Mandatory, NULL}, - {"SSL_CIPHER_get_name", TLS_Mandatory, NULL}, - {"SSL_CTX_check_private_key", TLS_Mandatory, NULL}, - {"SSL_CTX_set_session_id_context", TLS_Mandatory, NULL}, - {"SSL_CTX_ctrl", TLS_Mandatory, NULL}, - {"SSL_CTX_set_cipher_list", TLS_Mandatory, NULL}, - {"SSL_CTX_set_options", TLS_Mandatory, NULL}, - {"SSL_CTX_set_info_callback", TLS_Mandatory, NULL}, - {"SSL_get_ex_data", TLS_Mandatory, NULL}, - {"SSL_set_ex_data", TLS_Mandatory, NULL}, - {"SSL_CTX_callback_ctrl", TLS_Mandatory, NULL}, - {"SSL_get_servername", TLS_Mandatory, NULL}, - {"SSL_set_SSL_CTX", TLS_Mandatory, NULL}, - {"SSL_ctrl", TLS_Mandatory, NULL}, - {"SSL_CTX_set_alpn_protos", TLS_ALPN, NULL}, - {"SSL_CTX_set_alpn_select_cb", TLS_ALPN, NULL}, - {"SSL_CTX_set_next_protos_advertised_cb", TLS_ALPN, NULL}, - {"SSL_CTX_set_timeout", TLS_Mandatory, NULL}, - {NULL, TLS_END_OF_LIST, NULL}}; - - -/* Similar array as ssl_sw. These functions could be located in different - * lib. */ -static struct ssl_func crypto_sw[] = { - {"ERR_get_error", TLS_Mandatory, NULL}, - {"ERR_error_string", TLS_Mandatory, NULL}, - {"CONF_modules_unload", TLS_Mandatory, NULL}, - {"X509_free", TLS_Mandatory, NULL}, - {"X509_get_subject_name", TLS_Mandatory, NULL}, - {"X509_get_issuer_name", TLS_Mandatory, NULL}, - {"X509_NAME_oneline", TLS_Mandatory, NULL}, - {"X509_get_serialNumber", TLS_Mandatory, NULL}, - {"EVP_get_digestbyname", TLS_Mandatory, NULL}, - {"EVP_Digest", TLS_Mandatory, NULL}, - {"i2d_X509", TLS_Mandatory, NULL}, - {"BN_bn2hex", TLS_Mandatory, NULL}, - {"ASN1_INTEGER_to_BN", TLS_Mandatory, NULL}, - {"BN_free", TLS_Mandatory, NULL}, - {"CRYPTO_free", TLS_Mandatory, NULL}, - {"ERR_clear_error", TLS_Mandatory, NULL}, - {NULL, TLS_END_OF_LIST, NULL}}; -#endif - - -#if defined(OPENSSL_API_1_0) - -#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr) -#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr) -#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr) -#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr) -#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr) -#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr) -#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr) -#define SSL_new (*(SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr) -#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr) -#define SSLv23_server_method (*(SSL_METHOD * (*)(void)) ssl_sw[9].ptr) -#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr) -#define SSL_CTX_use_PrivateKey_file \ - (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr) -#define SSL_CTX_use_certificate_file \ - (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr) -#define SSL_CTX_set_default_passwd_cb \ - (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr) -#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr) -#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr) -#define SSL_CTX_use_certificate_chain_file \ - (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr) -#define SSLv23_client_method (*(SSL_METHOD * (*)(void)) ssl_sw[17].ptr) -#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr) -#define SSL_CTX_set_verify \ - (*(void (*)(SSL_CTX *, \ - int, \ - int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19] \ - .ptr) -#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr) -#define SSL_CTX_load_verify_locations \ - (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr) -#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr) -#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr) -#define SSL_get_peer_certificate (*(X509 * (*)(SSL *)) ssl_sw[24].ptr) -#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr) -#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *)) ssl_sw[26].ptr) -#define SSL_CIPHER_get_name \ - (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr) -#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr) -#define SSL_CTX_set_session_id_context \ - (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr) -#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr) -#define SSL_CTX_set_cipher_list \ - (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr) -#define SSL_CTX_set_info_callback \ - (*(void (*)(SSL_CTX *, void (*callback)(const SSL *, int, int)))ssl_sw[32] \ - .ptr) -#define SSL_get_ex_data (*(char *(*)(const SSL *, int))ssl_sw[33].ptr) -#define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr) -#define SSL_CTX_callback_ctrl \ - (*(long (*)(SSL_CTX *, int, void (*)(void)))ssl_sw[35].ptr) -#define SSL_get_servername \ - (*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr) -#define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *)) ssl_sw[37].ptr) -#define SSL_ctrl (*(long (*)(SSL *, int, long, void *))ssl_sw[38].ptr) -#define SSL_CTX_set_alpn_protos \ - (*(int (*)(SSL_CTX *, const unsigned char *, unsigned))ssl_sw[39].ptr) -typedef int (*tSSL_alpn_select_cb)(SSL *ssl, - const unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg); -#define SSL_CTX_set_alpn_select_cb \ - (*(void (*)(SSL_CTX *, tSSL_alpn_select_cb, void *))ssl_sw[40].ptr) -typedef int (*tSSL_next_protos_advertised_cb)(SSL *ssl, - const unsigned char **out, - unsigned int *outlen, - void *arg); -#define SSL_CTX_set_next_protos_advertised_cb \ - (*(void (*)(SSL_CTX *, tSSL_next_protos_advertised_cb, void *))ssl_sw[41] \ - .ptr) - -#define SSL_CTX_set_timeout (*(long (*)(SSL_CTX *, long))ssl_sw[42].ptr) - - -#define SSL_CTX_set_options(ctx, op) \ - SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL) -#define SSL_CTX_clear_options(ctx, op) \ - SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL) -#define SSL_CTX_set_ecdh_auto(ctx, onoff) \ - SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL) - -#define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53 -#define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54 -#define SSL_CTRL_SET_TLSEXT_HOSTNAME 55 -#define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \ - SSL_CTX_callback_ctrl(ctx, \ - SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \ - (void (*)(void))cb) -#define SSL_set_tlsext_host_name(ctx, arg) \ - SSL_ctrl(ctx, SSL_CTRL_SET_TLSEXT_HOSTNAME, 0, (void *)arg) - -#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore) -#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter) - -#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg)) -#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0)) - -#define SSL_CTX_sess_set_cache_size(ctx, size) SSL_CTX_ctrl(ctx, 42, size, NULL) -#define SSL_CTX_set_session_cache_mode(ctx, mode) \ - SSL_CTX_ctrl(ctx, 44, mode, NULL) - - -#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr) -#define CRYPTO_set_locking_callback \ - (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr) -#define CRYPTO_set_id_callback \ - (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr) -#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr) -#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr) -#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr) -#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr) -#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr) -#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr) -#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr) -#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr) -#define X509_free (*(void (*)(X509 *))crypto_sw[11].ptr) -#define X509_get_subject_name (*(X509_NAME * (*)(X509 *)) crypto_sw[12].ptr) -#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *)) crypto_sw[13].ptr) -#define X509_NAME_oneline \ - (*(char *(*)(X509_NAME *, char *, int))crypto_sw[14].ptr) -#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *)) crypto_sw[15].ptr) -#define i2c_ASN1_INTEGER \ - (*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr) -#define EVP_get_digestbyname \ - (*(const EVP_MD *(*)(const char *))crypto_sw[17].ptr) -#define EVP_Digest \ - (*(int (*)( \ - const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \ - crypto_sw[18] \ - .ptr) -#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[19].ptr) -#define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[20].ptr) -#define ASN1_INTEGER_to_BN \ - (*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn)) crypto_sw[21].ptr) -#define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[22].ptr) -#define CRYPTO_free (*(void (*)(void *addr))crypto_sw[23].ptr) -#define ERR_clear_error (*(void (*)(void))crypto_sw[24].ptr) - -#define OPENSSL_free(a) CRYPTO_free(a) - -/* use here ERR_remove_state, - * while on some platforms function is not included into library due to - * deprication */ -#define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_state(0) - -/* init_ssl_ctx() function updates this array. - * It loads SSL library dynamically and changes NULLs to the actual addresses - * of respective functions. The macros above (like SSL_connect()) are really - * just calling these functions indirectly via the pointer. */ -static struct ssl_func ssl_sw[] = { - {"SSL_free", TLS_Mandatory, NULL}, - {"SSL_accept", TLS_Mandatory, NULL}, - {"SSL_connect", TLS_Mandatory, NULL}, - {"SSL_read", TLS_Mandatory, NULL}, - {"SSL_write", TLS_Mandatory, NULL}, - {"SSL_get_error", TLS_Mandatory, NULL}, - {"SSL_set_fd", TLS_Mandatory, NULL}, - {"SSL_new", TLS_Mandatory, NULL}, - {"SSL_CTX_new", TLS_Mandatory, NULL}, - {"SSLv23_server_method", TLS_Mandatory, NULL}, - {"SSL_library_init", TLS_Mandatory, NULL}, - {"SSL_CTX_use_PrivateKey_file", TLS_Mandatory, NULL}, - {"SSL_CTX_use_certificate_file", TLS_Mandatory, NULL}, - {"SSL_CTX_set_default_passwd_cb", TLS_Mandatory, NULL}, - {"SSL_CTX_free", TLS_Mandatory, NULL}, - {"SSL_load_error_strings", TLS_Mandatory, NULL}, - {"SSL_CTX_use_certificate_chain_file", TLS_Mandatory, NULL}, - {"SSLv23_client_method", TLS_Mandatory, NULL}, - {"SSL_pending", TLS_Mandatory, NULL}, - {"SSL_CTX_set_verify", TLS_Mandatory, NULL}, - {"SSL_shutdown", TLS_Mandatory, NULL}, - {"SSL_CTX_load_verify_locations", TLS_Mandatory, NULL}, - {"SSL_CTX_set_default_verify_paths", TLS_Mandatory, NULL}, - {"SSL_CTX_set_verify_depth", TLS_Mandatory, NULL}, - {"SSL_get_peer_certificate", TLS_Mandatory, NULL}, - {"SSL_get_version", TLS_Mandatory, NULL}, - {"SSL_get_current_cipher", TLS_Mandatory, NULL}, - {"SSL_CIPHER_get_name", TLS_Mandatory, NULL}, - {"SSL_CTX_check_private_key", TLS_Mandatory, NULL}, - {"SSL_CTX_set_session_id_context", TLS_Mandatory, NULL}, - {"SSL_CTX_ctrl", TLS_Mandatory, NULL}, - {"SSL_CTX_set_cipher_list", TLS_Mandatory, NULL}, - {"SSL_CTX_set_info_callback", TLS_Mandatory, NULL}, - {"SSL_get_ex_data", TLS_Mandatory, NULL}, - {"SSL_set_ex_data", TLS_Mandatory, NULL}, - {"SSL_CTX_callback_ctrl", TLS_Mandatory, NULL}, - {"SSL_get_servername", TLS_Mandatory, NULL}, - {"SSL_set_SSL_CTX", TLS_Mandatory, NULL}, - {"SSL_ctrl", TLS_Mandatory, NULL}, - {"SSL_CTX_set_alpn_protos", TLS_ALPN, NULL}, - {"SSL_CTX_set_alpn_select_cb", TLS_ALPN, NULL}, - {"SSL_CTX_set_next_protos_advertised_cb", TLS_ALPN, NULL}, - {"SSL_CTX_set_timeout", TLS_Mandatory, NULL}, - {NULL, TLS_END_OF_LIST, NULL}}; - - -/* Similar array as ssl_sw. These functions could be located in different - * lib. */ -static struct ssl_func crypto_sw[] = { - {"CRYPTO_num_locks", TLS_Mandatory, NULL}, - {"CRYPTO_set_locking_callback", TLS_Mandatory, NULL}, - {"CRYPTO_set_id_callback", TLS_Mandatory, NULL}, - {"ERR_get_error", TLS_Mandatory, NULL}, - {"ERR_error_string", TLS_Mandatory, NULL}, - {"ERR_remove_state", TLS_Mandatory, NULL}, - {"ERR_free_strings", TLS_Mandatory, NULL}, - {"ENGINE_cleanup", TLS_Mandatory, NULL}, - {"CONF_modules_unload", TLS_Mandatory, NULL}, - {"CRYPTO_cleanup_all_ex_data", TLS_Mandatory, NULL}, - {"EVP_cleanup", TLS_Mandatory, NULL}, - {"X509_free", TLS_Mandatory, NULL}, - {"X509_get_subject_name", TLS_Mandatory, NULL}, - {"X509_get_issuer_name", TLS_Mandatory, NULL}, - {"X509_NAME_oneline", TLS_Mandatory, NULL}, - {"X509_get_serialNumber", TLS_Mandatory, NULL}, - {"i2c_ASN1_INTEGER", TLS_Mandatory, NULL}, - {"EVP_get_digestbyname", TLS_Mandatory, NULL}, - {"EVP_Digest", TLS_Mandatory, NULL}, - {"i2d_X509", TLS_Mandatory, NULL}, - {"BN_bn2hex", TLS_Mandatory, NULL}, - {"ASN1_INTEGER_to_BN", TLS_Mandatory, NULL}, - {"BN_free", TLS_Mandatory, NULL}, - {"CRYPTO_free", TLS_Mandatory, NULL}, - {"ERR_clear_error", TLS_Mandatory, NULL}, - {NULL, TLS_END_OF_LIST, NULL}}; -#endif /* OPENSSL_API_1_0 */ diff --git a/net/http/civetweb/response.inl b/net/http/civetweb/response.inl deleted file mode 100644 index 35e6ba8223a84..0000000000000 --- a/net/http/civetweb/response.inl +++ /dev/null @@ -1,342 +0,0 @@ -/* response.inl - * - * Bufferring for HTTP headers for HTTP response. - * This function are only intended to be used at the server side. - * Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2. - * - * This file is part of the CivetWeb project. - */ - -#if defined(NO_RESPONSE_BUFFERING) && defined(USE_HTTP2) -#error "HTTP2 works only if NO_RESPONSE_BUFFERING is not set" -#endif - - -/* Internal function to free header list */ -static void -free_buffered_response_header_list(struct mg_connection *conn) -{ -#if !defined(NO_RESPONSE_BUFFERING) - while (conn->response_info.num_headers > 0) { - conn->response_info.num_headers--; - mg_free((void *)conn->response_info - .http_headers[conn->response_info.num_headers] - .name); - conn->response_info.http_headers[conn->response_info.num_headers].name = - 0; - mg_free((void *)conn->response_info - .http_headers[conn->response_info.num_headers] - .value); - conn->response_info.http_headers[conn->response_info.num_headers] - .value = 0; - } -#else - (void)conn; /* Nothing to do */ -#endif -} - - -/* Send first line of HTTP/1.x response */ -static int -send_http1_response_status_line(struct mg_connection *conn) -{ - const char *status_txt; - const char *http_version = conn->request_info.http_version; - int status_code = conn->status_code; - - if ((status_code < 100) || (status_code > 999)) { - /* Set invalid status code to "500 Internal Server Error" */ - status_code = 500; - } - if (!http_version) { - http_version = "1.0"; - } - - /* mg_get_response_code_text will never return NULL */ - status_txt = mg_get_response_code_text(conn, conn->status_code); - - if (mg_printf( - conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt) - < 10) { - /* Network sending failed */ - return 0; - } - return 1; -} - - -/* Initialize a new HTTP response - * Parameters: - * conn: Current connection handle. - * status: HTTP status code (e.g., 200 for "OK"). - * Return: - * 0: ok - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: network error (only if built with NO_RESPONSE_BUFFERING) - */ -int -mg_response_header_start(struct mg_connection *conn, int status) -{ - int ret = 0; - if ((conn == NULL) || (status < 100) || (status > 999)) { - /* Parameter error */ - return -1; - } - if ((conn->connection_type != CONNECTION_TYPE_REQUEST) - || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) { - /* Only allowed in server context */ - return -2; - } - if (conn->request_state != 0) { - /* only allowed if nothing was sent up to now */ - return -3; - } - conn->status_code = status; - conn->request_state = 1; - - /* Buffered response is stored, unbuffered response will be sent directly, - * but we can only send HTTP/1.x response here */ -#if !defined(NO_RESPONSE_BUFFERING) - free_buffered_response_header_list(conn); -#else - if (!send_http1_response_status_line(conn)) { - ret = -4; - }; - conn->request_state = 1; /* Reset from 10 to 1 */ -#endif - - return ret; -} - - -/* Add a new HTTP response header line - * Parameters: - * conn: Current connection handle. - * header: Header name. - * value: Header value. - * value_len: Length of header value, excluding the terminating zero. - * Use -1 for "strlen(value)". - * Return: - * 0: ok - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: too many headers - * -5: out of memory - */ -int -mg_response_header_add(struct mg_connection *conn, - const char *header, - const char *value, - int value_len) -{ -#if !defined(NO_RESPONSE_BUFFERING) - int hidx; -#endif - - if ((conn == NULL) || (header == NULL) || (value == NULL)) { - /* Parameter error */ - return -1; - } - if ((conn->connection_type != CONNECTION_TYPE_REQUEST) - || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) { - /* Only allowed in server context */ - return -2; - } - if (conn->request_state != 1) { - /* only allowed if mg_response_header_start has been called before */ - return -3; - } - -#if !defined(NO_RESPONSE_BUFFERING) - hidx = conn->response_info.num_headers; - if (hidx >= MG_MAX_HEADERS) { - /* Too many headers */ - return -4; - } - - /* Alloc new element */ - conn->response_info.http_headers[hidx].name = - mg_strdup_ctx(header, conn->phys_ctx); - if (value_len >= 0) { - char *hbuf = - (char *)mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx); - if (hbuf) { - memcpy(hbuf, value, (unsigned)value_len); - hbuf[value_len] = 0; - } - conn->response_info.http_headers[hidx].value = hbuf; - } else { - conn->response_info.http_headers[hidx].value = - mg_strdup_ctx(value, conn->phys_ctx); - } - - if ((conn->response_info.http_headers[hidx].name == 0) - || (conn->response_info.http_headers[hidx].value == 0)) { - /* Out of memory */ - mg_free((void *)conn->response_info.http_headers[hidx].name); - conn->response_info.http_headers[hidx].name = 0; - mg_free((void *)conn->response_info.http_headers[hidx].value); - conn->response_info.http_headers[hidx].value = 0; - return -5; - } - - /* OK, header stored */ - conn->response_info.num_headers++; - -#else - if (value_len >= 0) { - mg_printf(conn, "%s: %.*s\r\n", header, (int)value_len, value); - } else { - mg_printf(conn, "%s: %s\r\n", header, value); - } - conn->request_state = 1; /* Reset from 10 to 1 */ -#endif - - return 0; -} - - -/* forward */ -static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]); - - -/* Add a complete header string (key + value). - * Parameters: - * conn: Current connection handle. - * http1_headers: Header line(s) in the form "name: value". - * Return: - * >=0: no error, number of header lines added - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: too many headers - * -5: out of memory - */ -int -mg_response_header_add_lines(struct mg_connection *conn, - const char *http1_headers) -{ - struct mg_header add_hdr[MG_MAX_HEADERS]; - int num_hdr, i, ret; - char *workbuffer, *parse; - - /* We need to work on a copy of the work buffer, sice parse_http_headers - * will modify */ - workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx); - if (!workbuffer) { - /* Out of memory */ - return -5; - } - - /* Call existing method to split header buffer */ - parse = workbuffer; - num_hdr = parse_http_headers(&parse, add_hdr); - ret = num_hdr; - - for (i = 0; i < num_hdr; i++) { - int lret = - mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1); - if ((ret > 0) && (lret < 0)) { - /* Store error return value */ - ret = lret; - } - } - - /* mg_response_header_add created a copy, so we can free the original */ - mg_free(workbuffer); - return ret; -} - - -#if defined(USE_HTTP2) -static int http2_send_response_headers(struct mg_connection *conn); -#endif - - -/* Send http response - * Parameters: - * conn: Current connection handle. - * Return: - * 0: ok - * -1: parameter error - * -2: invalid connection type - * -3: invalid connection status - * -4: network send failed - */ -int -mg_response_header_send(struct mg_connection *conn) -{ -#if !defined(NO_RESPONSE_BUFFERING) - int i; - int has_date = 0; - int has_connection = 0; -#endif - - if (conn == NULL) { - /* Parameter error */ - return -1; - } - if ((conn->connection_type != CONNECTION_TYPE_REQUEST) - || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) { - /* Only allowed in server context */ - return -2; - } - if (conn->request_state != 1) { - /* only allowed if mg_response_header_start has been called before */ - return -3; - } - - /* State: 2 */ - conn->request_state = 2; - -#if !defined(NO_RESPONSE_BUFFERING) -#if defined(USE_HTTP2) - if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { - int ret = http2_send_response_headers(conn); - free_buffered_response_header_list(conn); - return (ret ? 0 : -4); - } -#endif - - /* Send */ - if (!send_http1_response_status_line(conn)) { - free_buffered_response_header_list(conn); - return -4; - }; - for (i = 0; i < conn->response_info.num_headers; i++) { - mg_printf(conn, - "%s: %s\r\n", - conn->response_info.http_headers[i].name, - conn->response_info.http_headers[i].value); - - /* Check for some special headers */ - if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) { - has_date = 1; - } - if (!mg_strcasecmp("Connection", - conn->response_info.http_headers[i].name)) { - has_connection = 1; - } - } - - if (!has_date) { - time_t curtime = time(NULL); - char date[64]; - gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, "Date: %s\r\n", date); - } - if (!has_connection) { - mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn)); - } -#endif - - mg_write(conn, "\r\n", 2); - conn->request_state = 3; - - /* ok */ - free_buffered_response_header_list(conn); - return 0; -} diff --git a/net/http/civetweb/sha1.inl b/net/http/civetweb/sha1.inl deleted file mode 100644 index 6af0757718581..0000000000000 --- a/net/http/civetweb/sha1.inl +++ /dev/null @@ -1,323 +0,0 @@ -/* -SHA-1 in C -By Steve Reid -100% Public Domain - ------------------ -Modified 7/98 -By James H. Brown -Still 100% Public Domain - -Corrected a problem which generated improper hash values on 16 bit machines -Routine SHA1Update changed from - void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned int -len) -to - void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned -long len) - -The 'len' parameter was declared an int which works fine on 32 bit machines. -However, on 16 bit machines an int is too small for the shifts being done -against -it. This caused the hash function to generate incorrect values if len was -greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). - -Since the file IO in main() reads 16K at a time, any file 8K or larger would -be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million -"a"s). - -I also changed the declaration of variables i & j in SHA1Update to -unsigned long from unsigned int for the same reason. - -These changes should make no difference to any 32 bit implementations since -an -int and a long are the same size in those environments. - --- -I also corrected a few compiler warnings generated by Borland C. -1. Added #include for exit() prototype -2. Removed unused variable 'j' in SHA1Final -3. Changed exit(0) to return(0) at end of main. - -ALL changes I made can be located by searching for comments containing 'JHB' ------------------ -Modified 8/98 -By Steve Reid -Still 100% public domain - -1- Removed #include and used return() instead of exit() -2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) -3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net - ------------------ -Modified 4/01 -By Saul Kravitz -Still 100% PD -Modified to run on Compaq Alpha hardware. - ------------------ -Modified 07/2002 -By Ralph Giles -Still 100% public domain -modified for use with stdint types, autoconf -code cleanup, removed attribution comments -switched SHA1Final() argument order for consistency -use SHA1_ prefix for public api -move public api to sha1.h -*/ - -/* -11/2016 adapted for CivetWeb: - include sha1.h in sha1.c, - rename to sha1.inl - remove unused #ifdef sections - make endian independent - align buffer to 4 bytes - remove unused variable assignments -*/ - -/* -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -#include -#include - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - uint8_t buffer[64]; -} SHA_CTX; - -#define SHA1_DIGEST_SIZE 20 - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ - - -typedef union { - uint8_t c[64]; - uint32_t l[16]; -} CHAR64LONG16; - - -static uint32_t -blk0(CHAR64LONG16 *block, int i) -{ - static const uint32_t n = 1u; - if ((*((uint8_t *)(&n))) == 1) { - /* little endian / intel byte order */ - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) - | (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -#define blk(block, i) \ - ((block)->l[(i)&15] = \ - rol((block)->l[((i) + 13) & 15] ^ (block)->l[((i) + 8) & 15] \ - ^ (block)->l[((i) + 2) & 15] ^ (block)->l[(i)&15], \ - 1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); \ - w = rol(w, 30); -#define R3(v, w, x, y, z, i) \ - z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); \ - w = rol(w, 30); - - -/* Hash a single 512-bit block. This is the core of the algorithm. */ -static void -SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) -{ - uint32_t a, b, c, d, e; - - /* Must use an aligned, read/write buffer */ - CHAR64LONG16 block[1]; - memcpy(block, buffer, sizeof(block)); - - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a, b, c, d, e, 0); - R0(e, a, b, c, d, 1); - R0(d, e, a, b, c, 2); - R0(c, d, e, a, b, 3); - R0(b, c, d, e, a, 4); - R0(a, b, c, d, e, 5); - R0(e, a, b, c, d, 6); - R0(d, e, a, b, c, 7); - R0(c, d, e, a, b, 8); - R0(b, c, d, e, a, 9); - R0(a, b, c, d, e, 10); - R0(e, a, b, c, d, 11); - R0(d, e, a, b, c, 12); - R0(c, d, e, a, b, 13); - R0(b, c, d, e, a, 14); - R0(a, b, c, d, e, 15); - R1(e, a, b, c, d, 16); - R1(d, e, a, b, c, 17); - R1(c, d, e, a, b, 18); - R1(b, c, d, e, a, 19); - R2(a, b, c, d, e, 20); - R2(e, a, b, c, d, 21); - R2(d, e, a, b, c, 22); - R2(c, d, e, a, b, 23); - R2(b, c, d, e, a, 24); - R2(a, b, c, d, e, 25); - R2(e, a, b, c, d, 26); - R2(d, e, a, b, c, 27); - R2(c, d, e, a, b, 28); - R2(b, c, d, e, a, 29); - R2(a, b, c, d, e, 30); - R2(e, a, b, c, d, 31); - R2(d, e, a, b, c, 32); - R2(c, d, e, a, b, 33); - R2(b, c, d, e, a, 34); - R2(a, b, c, d, e, 35); - R2(e, a, b, c, d, 36); - R2(d, e, a, b, c, 37); - R2(c, d, e, a, b, 38); - R2(b, c, d, e, a, 39); - R3(a, b, c, d, e, 40); - R3(e, a, b, c, d, 41); - R3(d, e, a, b, c, 42); - R3(c, d, e, a, b, 43); - R3(b, c, d, e, a, 44); - R3(a, b, c, d, e, 45); - R3(e, a, b, c, d, 46); - R3(d, e, a, b, c, 47); - R3(c, d, e, a, b, 48); - R3(b, c, d, e, a, 49); - R3(a, b, c, d, e, 50); - R3(e, a, b, c, d, 51); - R3(d, e, a, b, c, 52); - R3(c, d, e, a, b, 53); - R3(b, c, d, e, a, 54); - R3(a, b, c, d, e, 55); - R3(e, a, b, c, d, 56); - R3(d, e, a, b, c, 57); - R3(c, d, e, a, b, 58); - R3(b, c, d, e, a, 59); - R4(a, b, c, d, e, 60); - R4(e, a, b, c, d, 61); - R4(d, e, a, b, c, 62); - R4(c, d, e, a, b, 63); - R4(b, c, d, e, a, 64); - R4(a, b, c, d, e, 65); - R4(e, a, b, c, d, 66); - R4(d, e, a, b, c, 67); - R4(c, d, e, a, b, 68); - R4(b, c, d, e, a, 69); - R4(a, b, c, d, e, 70); - R4(e, a, b, c, d, 71); - R4(d, e, a, b, c, 72); - R4(c, d, e, a, b, 73); - R4(b, c, d, e, a, 74); - R4(a, b, c, d, e, 75); - R4(e, a, b, c, d, 76); - R4(d, e, a, b, c, 77); - R4(c, d, e, a, b, 78); - R4(b, c, d, e, a, 79); - - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; -} - - -/* SHA1Init - Initialize new context */ -SHA_API void -SHA1_Init(SHA_CTX *context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -SHA_API void -SHA1_Update(SHA_CTX *context, const uint8_t *data, const uint32_t len) -{ - uint32_t i, j; - - j = context->count[0]; - if ((context->count[0] += (len << 3)) < j) { - context->count[1]++; - } - context->count[1] += (len >> 29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - i = 64 - j; - memcpy(&context->buffer[j], data, i); - SHA1_Transform(context->state, context->buffer); - for (; i + 63 < len; i += 64) { - SHA1_Transform(context->state, &data[i]); - } - j = 0; - } else { - i = 0; - } - memcpy(&context->buffer[j], &data[i], len - i); -} - - -/* Add padding and return the message digest. */ -SHA_API void -SHA1_Final(unsigned char *digest, SHA_CTX *context) -{ - uint32_t i; - uint8_t finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = - (uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) - & 255); /* Endian independent */ - } - SHA1_Update(context, (uint8_t *)"\x80", 1); - while ((context->count[0] & 504) != 448) { - SHA1_Update(context, (uint8_t *)"\x00", 1); - } - SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ - for (i = 0; i < SHA1_DIGEST_SIZE; i++) { - digest[i] = - (uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); - } - - /* Wipe variables */ - memset(context, '\0', sizeof(*context)); -} - - -/* End of sha1.inl */ diff --git a/net/http/civetweb/sort.inl b/net/http/civetweb/sort.inl deleted file mode 100644 index 87a5eb79198e4..0000000000000 --- a/net/http/civetweb/sort.inl +++ /dev/null @@ -1,48 +0,0 @@ -/* Sort function. */ -/* from https://github.com/bel2125/sort_r */ - -static void -mg_sort(void *data, - size_t elemcount, - size_t elemsize, - int (*compfunc)(const void *data1, const void *data2, void *userarg), - void *userarg) -{ - /* We cannot use qsort_r here. For a detailed reason, see - * https://github.com/civetweb/civetweb/issues/1048#issuecomment-1047093014 - * https://stackoverflow.com/questions/39560773/different-declarations-of-qsort-r-on-mac-and-linux - */ - - /* We use ShellSort here with this gap sequence: https://oeis.org/A102549 */ - size_t A102549[9] = {1, 4, 10, 23, 57, 132, 301, 701, 1750}; - size_t gap, i, j, k; - int Aidx; - void *tmp = alloca(elemsize); - - for (Aidx = 8; Aidx >= 0; Aidx--) { - gap = A102549[Aidx]; - if (gap > (elemcount / 2)) { - continue; - } - for (i = 0; i < gap; i++) { - for (j = i; j < elemcount; j += gap) { - memcpy(tmp, (void *)((size_t)data + elemsize * j), elemsize); - - for (k = j; k >= gap; k -= gap) { - void *cmp = (void *)((size_t)data + elemsize * (k - gap)); - int cmpres = compfunc(cmp, tmp, userarg); - if (cmpres > 0) { - memcpy((void *)((size_t)data + elemsize * k), - cmp, - elemsize); - } else { - break; - } - } - memcpy((void *)((size_t)data + elemsize * k), tmp, elemsize); - } - } - } -} - -/* end if sort.inl */ From 6c4416be4b6c87680719e5e25f6664f744119ffe Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Fri, 17 Apr 2026 19:28:16 +0200 Subject: [PATCH 02/54] [ci] explicit enable in alma10clang-ninja even if it was getting on implicitly --- .../workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt b/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt index 2472ad9444983..dcf0cd2f90683 100644 --- a/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt +++ b/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt @@ -1,6 +1,7 @@ CMAKE_C_COMPILER=clang CMAKE_CXX_COMPILER=clang++ CMAKE_GENERATOR=Ninja +builtin_civetweb=ON builtin_freetype=ON builtin_gif=ON builtin_gl2ps=ON From 868f2e4cdd1ca160c2bb85ac9a7ba9cf348ba2a8 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Fri, 17 Apr 2026 19:28:42 +0200 Subject: [PATCH 03/54] todo list --- builtins/civetweb/CMakeLists.txt | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 78d1260bfb9b0..3d9b19a3277ee 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -75,3 +75,65 @@ set(civetweb_INCLUDE_DIRS ${ROOT_CIVETWEB_PREFIX}/include PARENT_SCOPE) set(civetweb_LIBRARIES ${ROOT_CIVETWEB_LIBRARY} PARENT_SCOPE) set(civetweb_FOUND TRUE PARENT_SCOPE) set(civetweb_VERSION ${ROOT_CIVETWEB_VERSION} PARENT_SCOPE) + +# TODO: +#~ # look for the realtime extensions library and use it if it exists +#~ find_library(RT_LIBRARY rt) +#~ if(RT_LIBRARY) + #~ set(RT_LIBRARIES ${RT_LIBRARY}) +#~ endif() + +#~ if(builtin_civetweb) + #~ set(_civetweb_src civetweb/civetweb.c) + #~ set(_civetweb_libs ${RT_LIBRARIES}) +#~ endif() + + + + +#~ if(builtin_civetweb) + #~ target_include_directories(RHTTP PRIVATE civetweb) + + #~ if(ssl) + #~ target_include_directories(RHTTP SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) + #~ endif() + + #~ target_compile_definitions(RHTTP PRIVATE -DUSE_WEBSOCKET) + + #~ if(NOT MSVC) + #~ target_compile_definitions(RHTTP PRIVATE -DUSE_X_DOM_SOCKET) + #~ endif() + + #~ if(ssl) + #~ if(OPENSSL_VERSION) + #~ string(REPLACE "." ";" lst ${OPENSSL_VERSION}) + #~ list(GET lst 0 ssl_major) + #~ list(GET lst 1 ssl_minor) + #~ endif() + + #~ if((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "1")) + #~ MESSAGE(STATUS "Use SSL API VERSION 1.1 for civetweb") + #~ target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_1_1) + #~ set(link_ssl ON) + #~ elseif((${ssl_major} EQUAL "3")) + #~ MESSAGE(STATUS "Use SSL API VERSION 3.${ssl_minor} for civetweb") + #~ target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_3_0) + #~ set(link_ssl ON) + #~ elseif((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "0")) + #~ MESSAGE(STATUS "Use SSL API VERSION 1.0 for civetweb") + #~ target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_1_0) + #~ set(link_ssl ON) + #~ else() + #~ MESSAGE(WARNING "Not able to recognize SSL version ${OPENSSL_VERSION}, disable SSL") + #~ target_compile_definitions(RHTTP PUBLIC -DNO_SSL) + #~ endif() + #~ if(link_ssl) + #~ target_compile_definitions(RHTTP PUBLIC -DNO_SSL_DL) + #~ target_link_libraries(RHTTP PRIVATE ${OPENSSL_LIBRARIES}) + #~ endif() + #~ else() + #~ target_compile_definitions(RHTTP PUBLIC -DNO_SSL) + #~ endif() + + #~ # workaround for R__memcompress failure with external civetweb + #~ target_compile_definitions(RHTTP PRIVATE -D_EXTERNAL_CIVETWEB) From f4946d2c679e3fb6b639fd1de92705e4de3c5313 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 14:59:29 +0200 Subject: [PATCH 04/54] fixes for SSL --- builtins/civetweb/CMakeLists.txt | 84 +++++++++++---------- builtins/openssl/CMakeLists.txt | 1 + cmake/modules/SearchInstalledSoftware.cmake | 40 +++++----- 3 files changed, 64 insertions(+), 61 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 3d9b19a3277ee..073415e69f065 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -32,10 +32,41 @@ if(WIN32 AND NOT CMAKE_GENERATOR MATCHES Ninja) endif() endif() +if(ssl) + if(OPENSSL_VERSION) + string(REPLACE "." ";" lst ${OPENSSL_VERSION}) + list(GET lst 0 ssl_major) + list(GET lst 1 ssl_minor) + else() + MESSAGE(SEND_ERROR "No openSSL version defined.") + endif() + + if((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "1")) + MESSAGE(STATUS "Use SSL API VERSION 1.1 for civetweb") + set(ROOT_SSL_API "-DCIVETWEB_SSL_OPENSSL_API_1_1=ON" "-DCIVETWEB_SSL_OPENSSL_API_3_0=OFF" "-DCIVETWEB_SSL_OPENSSL_API_1_0=OFF" "-DCIVETWEB_ENABLE_SSL=ON") + elseif((${ssl_major} EQUAL "3")) + MESSAGE(STATUS "Use SSL API VERSION 3.${ssl_minor} for civetweb") + set(ROOT_SSL_API "-DCIVETWEB_SSL_OPENSSL_API_3_0=ON" "-DCIVETWEB_SSL_OPENSSL_API_1_1=OFF" "-DCIVETWEB_SSL_OPENSSL_API_1_0=OFF" "-DCIVETWEB_ENABLE_SSL=ON") + elseif((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "0")) + MESSAGE(STATUS "Use SSL API VERSION 1.0 for civetweb") + set(ROOT_SSL_API "-DCIVETWEB_SSL_OPENSSL_API_1_0=ON" "-DCIVETWEB_SSL_OPENSSL_API_3_0=OFF" "-DCIVETWEB_SSL_OPENSSL_API_1_1=OFF" "-DCIVETWEB_ENABLE_SSL=ON") + else() + MESSAGE(WARNING "Not able to recognize SSL version ${OPENSSL_VERSION}, disable SSL") + set(ROOT_SSL_API "") + endif() +else() + set(ROOT_SSL_API "") +endif() +if(NOT MSVC) + set(ROOT_SOCKET_FLAGS "-DUSE_X_DOM_SOCKET") +else() + set(ROOT_SOCKET_FLAGS "") +endif() + ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} # URL ${lcgpackages}/civetweb-${ROOT_CIVETWEB_VERSION}.tar.gz - URL https://github.com/civetweb/civetweb/archive/refs/tags/v1.16.tar.gz # TODO move to LCG + URL https://github.com/civetweb/civetweb/archive/refs/tags/v${ROOT_CIVETWEB_VERSION}.tar.gz # TODO move to LCG URL_HASH SHA256=${ROOT_CIVETWEB_HASH} LOG_DOWNLOAD TRUE @@ -49,11 +80,16 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} + -DCMAKE_C_FLAGS:STRING=${ROOT_SOCKET_FLAGS} + -DCMAKE_CXX_FLAGS:STRING=${ROOT_SOCKET_FLAGS} -DBUILD_SHARED_LIBS:BOOL=FALSE -DBUILD_TESTING=FALSE -DCIVETWEB_BUILD_TESTING=FALSE -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + -DCIVETWEB_ENABLE_WEBSOCKETS=ON + -DOpenSSL_DIR=${OPENSSL_ROOT} # TODO check if it correctly picks up builtin if simultaneous to system-wide. Maybe we need to pass explicit OpenSSL_DIR + ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install BUILD_BYPRODUCTS @@ -64,10 +100,16 @@ ExternalProject_Add(BUILTIN_CIVETWEB file(MAKE_DIRECTORY ${ROOT_CIVETWEB_PREFIX}/include) add_library(civetweb::civetweb IMPORTED STATIC GLOBAL) add_dependencies(civetweb::civetweb BUILTIN_CIVETWEB) +if(builtin_ssl) + add_dependencies(BUILTIN_CIVETWEB BUILTIN_SSL) +endif() set_target_properties(civetweb::civetweb PROPERTIES IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_CIVETWEB_PREFIX}/include) target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) # needed for Win32 since public flag is not correctly propagated to parent scope (BUILD_SHARED_LIBS works fine for building but when installing, flag info is lost) +if (NOT ROOT_SSL_API STREQUAL "") + target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) +endif() # Set the canonical output of find_package according to # https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names @@ -91,49 +133,9 @@ set(civetweb_VERSION ${ROOT_CIVETWEB_VERSION} PARENT_SCOPE) -#~ if(builtin_civetweb) - #~ target_include_directories(RHTTP PRIVATE civetweb) - - #~ if(ssl) - #~ target_include_directories(RHTTP SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) #~ endif() - #~ target_compile_definitions(RHTTP PRIVATE -DUSE_WEBSOCKET) - #~ if(NOT MSVC) - #~ target_compile_definitions(RHTTP PRIVATE -DUSE_X_DOM_SOCKET) - #~ endif() - - #~ if(ssl) - #~ if(OPENSSL_VERSION) - #~ string(REPLACE "." ";" lst ${OPENSSL_VERSION}) - #~ list(GET lst 0 ssl_major) - #~ list(GET lst 1 ssl_minor) - #~ endif() - - #~ if((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "1")) - #~ MESSAGE(STATUS "Use SSL API VERSION 1.1 for civetweb") - #~ target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_1_1) - #~ set(link_ssl ON) - #~ elseif((${ssl_major} EQUAL "3")) - #~ MESSAGE(STATUS "Use SSL API VERSION 3.${ssl_minor} for civetweb") - #~ target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_3_0) - #~ set(link_ssl ON) - #~ elseif((${ssl_major} EQUAL "1") AND (${ssl_minor} EQUAL "0")) - #~ MESSAGE(STATUS "Use SSL API VERSION 1.0 for civetweb") - #~ target_compile_definitions(RHTTP PUBLIC -DOPENSSL_API_1_0) - #~ set(link_ssl ON) - #~ else() - #~ MESSAGE(WARNING "Not able to recognize SSL version ${OPENSSL_VERSION}, disable SSL") - #~ target_compile_definitions(RHTTP PUBLIC -DNO_SSL) - #~ endif() - #~ if(link_ssl) - #~ target_compile_definitions(RHTTP PUBLIC -DNO_SSL_DL) - #~ target_link_libraries(RHTTP PRIVATE ${OPENSSL_LIBRARIES}) - #~ endif() - #~ else() - #~ target_compile_definitions(RHTTP PUBLIC -DNO_SSL) - #~ endif() #~ # workaround for R__memcompress failure with external civetweb #~ target_compile_definitions(RHTTP PRIVATE -D_EXTERNAL_CIVETWEB) diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 963e400aada40..491f4d5a1095d 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -87,3 +87,4 @@ find_package_handle_standard_args(OpenSSL ) set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS OPENSSL) +set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 8ebdcfa681da0..29545e5a54c8a 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -209,26 +209,6 @@ if(builtin_nlohmannjson) add_subdirectory(builtins/nlohmann) endif() -#--- Check for civetweb --------------------------------------------------------- -if(http AND NOT builtin_civetweb) - message(STATUS "Looking for civetweb") - if(fail-on-missing) - find_package(civetweb 1.15 REQUIRED) - else() - find_package(civetweb 1.15 QUIET) - if(civetweb_FOUND) - message(STATUS "Found civetweb version ${civetweb_VERSION}") - else() - message(STATUS "civetweb not found. Switching on builtin_civetweb option") - set(builtin_civetweb ON CACHE BOOL "Enabled because civetweb not found" FORCE) - endif() - endif() -endif() -if(http AND builtin_civetweb) - list(APPEND ROOT_BUILTINS BUILTIN_CIVETWEB) - add_subdirectory(builtins/civetweb) -endif() - #---Check for Unuran ------------------------------------------------------------------ if(unuran AND NOT builtin_unuran) message(STATUS "Looking for Unuran") @@ -547,6 +527,26 @@ if(fcgi) endif() endif() +#--- Check for civetweb - (has to after SSL) ------------------------------------------ +if(http AND NOT builtin_civetweb) + message(STATUS "Looking for civetweb") + if(fail-on-missing) + find_package(civetweb 1.15 REQUIRED) + else() + find_package(civetweb 1.15 QUIET) + if(civetweb_FOUND) + message(STATUS "Found civetweb version ${civetweb_VERSION}") + else() + message(STATUS "civetweb not found. Switching on builtin_civetweb option") + set(builtin_civetweb ON CACHE BOOL "Enabled because civetweb not found" FORCE) + endif() + endif() +endif() +if(http AND builtin_civetweb) + list(APPEND ROOT_BUILTINS BUILTIN_CIVETWEB) + add_subdirectory(builtins/civetweb) +endif() + #---Check for SQLite------------------------------------------------------------------- if(sqlite) message(STATUS "Looking for SQLite") From 6ee0bd1537ce05c11a2bc80ebfccc28c0ab66b2c Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 15:00:33 +0200 Subject: [PATCH 05/54] fixes for SSL --- builtins/openssl/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 491f4d5a1095d..c5a16893cc163 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -88,3 +88,4 @@ find_package_handle_standard_args(OpenSSL set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS OPENSSL) set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) +set(OPENSSL_ROOT ${OPENSSL_ROOT} PARENT_SCOPE) From bc1b46372c284d00877c07dc2e500e1bc1153425 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 15:13:23 +0200 Subject: [PATCH 06/54] cleanup --- builtins/civetweb/CMakeLists.txt | 23 +---------------------- net/http/CMakeLists.txt | 1 - 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 073415e69f065..2ffb1f41cb435 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -110,6 +110,7 @@ target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) if (NOT ROOT_SSL_API STREQUAL "") target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) endif() +target_compile_definitions(civetweb::civetweb INTERFACE _EXTERNAL_CIVETWEB) # temporary hack for: with external civeweb one gets failure R__memcompress # Set the canonical output of find_package according to # https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names @@ -117,25 +118,3 @@ set(civetweb_INCLUDE_DIRS ${ROOT_CIVETWEB_PREFIX}/include PARENT_SCOPE) set(civetweb_LIBRARIES ${ROOT_CIVETWEB_LIBRARY} PARENT_SCOPE) set(civetweb_FOUND TRUE PARENT_SCOPE) set(civetweb_VERSION ${ROOT_CIVETWEB_VERSION} PARENT_SCOPE) - -# TODO: -#~ # look for the realtime extensions library and use it if it exists -#~ find_library(RT_LIBRARY rt) -#~ if(RT_LIBRARY) - #~ set(RT_LIBRARIES ${RT_LIBRARY}) -#~ endif() - -#~ if(builtin_civetweb) - #~ set(_civetweb_src civetweb/civetweb.c) - #~ set(_civetweb_libs ${RT_LIBRARIES}) -#~ endif() - - - - - #~ endif() - - - - #~ # workaround for R__memcompress failure with external civetweb - #~ target_compile_definitions(RHTTP PRIVATE -D_EXTERNAL_CIVETWEB) diff --git a/net/http/CMakeLists.txt b/net/http/CMakeLists.txt index 784b8bb760a7a..e70c4b4720356 100644 --- a/net/http/CMakeLists.txt +++ b/net/http/CMakeLists.txt @@ -40,7 +40,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RHTTP LIBRARIES ZLIB::ZLIB civetweb::civetweb - ${CMAKE_DL_LIBS} DEPENDENCIES RIO Thread From 7e597b2d38ef074a569f1fd2eccb24bd7d5efc14 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 15:18:19 +0200 Subject: [PATCH 07/54] simplify --- builtins/civetweb/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 2ffb1f41cb435..0856bbc53f265 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -107,9 +107,9 @@ set_target_properties(civetweb::civetweb PROPERTIES IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_CIVETWEB_PREFIX}/include) target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) # needed for Win32 since public flag is not correctly propagated to parent scope (BUILD_SHARED_LIBS works fine for building but when installing, flag info is lost) -if (NOT ROOT_SSL_API STREQUAL "") - target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) -endif() +#~ if (NOT ROOT_SSL_API STREQUAL "") + #~ target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) +#~ endif() target_compile_definitions(civetweb::civetweb INTERFACE _EXTERNAL_CIVETWEB) # temporary hack for: with external civeweb one gets failure R__memcompress # Set the canonical output of find_package according to From 46c9702752f3fdb930a2bebfdf9d051a6bc4909c Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 16:19:12 +0200 Subject: [PATCH 08/54] try with installDir --- builtins/civetweb/CMakeLists.txt | 3 +-- cmake/modules/SearchInstalledSoftware.cmake | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 0856bbc53f265..a2990ab609fa6 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -74,7 +74,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB LOG_BUILD TRUE LOG_INSTALL TRUE LOG_OUTPUT_ON_FAILURE TRUE - + INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} @@ -110,7 +110,6 @@ target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) #~ if (NOT ROOT_SSL_API STREQUAL "") #~ target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) #~ endif() -target_compile_definitions(civetweb::civetweb INTERFACE _EXTERNAL_CIVETWEB) # temporary hack for: with external civeweb one gets failure R__memcompress # Set the canonical output of find_package according to # https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 29545e5a54c8a..5cab89795d521 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -541,6 +541,9 @@ if(http AND NOT builtin_civetweb) set(builtin_civetweb ON CACHE BOOL "Enabled because civetweb not found" FORCE) endif() endif() + if (NOT builtin_civetweb) + target_compile_definitions(civetweb::civetweb INTERFACE _EXTERNAL_CIVETWEB) # temporary hack for: with external civetweb one gets failure R__memcompress + endif() endif() if(http AND builtin_civetweb) list(APPEND ROOT_BUILTINS BUILTIN_CIVETWEB) From a76675182d4bdf14b55319b5e50b21fe2f01c5e5 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 16:23:39 +0200 Subject: [PATCH 09/54] fix asan --- builtins/civetweb/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index a2990ab609fa6..cde5bf170ec66 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -84,6 +84,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_CXX_FLAGS:STRING=${ROOT_SOCKET_FLAGS} -DBUILD_SHARED_LIBS:BOOL=FALSE -DBUILD_TESTING=FALSE + -DCIVETWEB_ENABLE_ASAN=OFF # If set to ON, you need to set below link_library interface to UBSAN libs -DCIVETWEB_BUILD_TESTING=FALSE -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5 From 94b6ca50579097f1b57eeb66f962d49a607b6129 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 16:26:48 +0200 Subject: [PATCH 10/54] fix default --- builtins/civetweb/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index cde5bf170ec66..44e2a4e1ecc29 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -52,7 +52,7 @@ if(ssl) set(ROOT_SSL_API "-DCIVETWEB_SSL_OPENSSL_API_1_0=ON" "-DCIVETWEB_SSL_OPENSSL_API_3_0=OFF" "-DCIVETWEB_SSL_OPENSSL_API_1_1=OFF" "-DCIVETWEB_ENABLE_SSL=ON") else() MESSAGE(WARNING "Not able to recognize SSL version ${OPENSSL_VERSION}, disable SSL") - set(ROOT_SSL_API "") + set(ROOT_SSL_API "-DCIVETWEB_ENABLE_SSL=OFF") endif() else() set(ROOT_SSL_API "") @@ -108,7 +108,7 @@ set_target_properties(civetweb::civetweb PROPERTIES IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_CIVETWEB_PREFIX}/include) target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) # needed for Win32 since public flag is not correctly propagated to parent scope (BUILD_SHARED_LIBS works fine for building but when installing, flag info is lost) -#~ if (NOT ROOT_SSL_API STREQUAL "") +#~ if (NOT ROOT_SSL_API STREQUAL "DCIVETWEB_ENABLE_SSL=OFF") #~ target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) #~ endif() From 8caf98c23c7014a427e64af3757d5b060ff231e8 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 16:38:43 +0200 Subject: [PATCH 11/54] mention deprecation --- builtins/openssl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index c5a16893cc163..63bd5bf80f4f3 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -87,5 +87,5 @@ find_package_handle_standard_args(OpenSSL ) set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS OPENSSL) -set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) +set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) # CMake 4.2 changes to small case set(OPENSSL_ROOT ${OPENSSL_ROOT} PARENT_SCOPE) From 9b71c7ccf329640e3e958b131bfb07f43ae6209f Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 17:50:39 +0200 Subject: [PATCH 12/54] try fix path --- builtins/civetweb/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 44e2a4e1ecc29..1beae9f771cb6 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -77,6 +77,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_INSTALL_PREFIX:PATH= + -DCMAKE_INSTALL_LIBDIR:PATH=/lib -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} From c74096773d7791b4c7b1c0d33ac4f754c2be8393 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 18:47:34 +0200 Subject: [PATCH 13/54] disable SSL dynamic loading --- builtins/civetweb/CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 1beae9f771cb6..47203c2115c72 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -55,7 +55,7 @@ if(ssl) set(ROOT_SSL_API "-DCIVETWEB_ENABLE_SSL=OFF") endif() else() - set(ROOT_SSL_API "") + set(ROOT_SSL_API "-DCIVETWEB_ENABLE_SSL=OFF") endif() if(NOT MSVC) set(ROOT_SOCKET_FLAGS "-DUSE_X_DOM_SOCKET") @@ -86,6 +86,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DBUILD_SHARED_LIBS:BOOL=FALSE -DBUILD_TESTING=FALSE -DCIVETWEB_ENABLE_ASAN=OFF # If set to ON, you need to set below link_library interface to UBSAN libs + -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=OFF # If set to ON, you need to set below link_library interface to CMAKE_DL_LIBS -DCIVETWEB_BUILD_TESTING=FALSE -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5 @@ -109,9 +110,9 @@ set_target_properties(civetweb::civetweb PROPERTIES IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_CIVETWEB_PREFIX}/include) target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) # needed for Win32 since public flag is not correctly propagated to parent scope (BUILD_SHARED_LIBS works fine for building but when installing, flag info is lost) -#~ if (NOT ROOT_SSL_API STREQUAL "DCIVETWEB_ENABLE_SSL=OFF") - #~ target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) -#~ endif() +if (NOT ROOT_SSL_API STREQUAL "DCIVETWEB_ENABLE_SSL=OFF") + target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) +endif() # Set the canonical output of find_package according to # https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names From 7ca3952786ab2c981682f5534dd894811dea1db8 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 20 Apr 2026 19:20:31 +0200 Subject: [PATCH 14/54] Update CMakeLists.txt --- builtins/civetweb/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 47203c2115c72..50efb6a3ea4da 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -110,7 +110,7 @@ set_target_properties(civetweb::civetweb PROPERTIES IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_CIVETWEB_PREFIX}/include) target_compile_definitions(civetweb::civetweb INTERFACE CIVETWEB_LIBRARY_STATIC) # needed for Win32 since public flag is not correctly propagated to parent scope (BUILD_SHARED_LIBS works fine for building but when installing, flag info is lost) -if (NOT ROOT_SSL_API STREQUAL "DCIVETWEB_ENABLE_SSL=OFF") +if (ssl) target_link_libraries(civetweb::civetweb INTERFACE OpenSSL::SSL) endif() From f3bcf6e40b278d29c1bfde3eba0f3a3449fc30a6 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 08:27:42 +0200 Subject: [PATCH 15/54] fix openssl include --- builtins/civetweb/CMakeLists.txt | 4 +++- builtins/openssl/CMakeLists.txt | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 50efb6a3ea4da..49491b7bab66f 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -91,7 +91,9 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCIVETWEB_ENABLE_WEBSOCKETS=ON - -DOpenSSL_DIR=${OPENSSL_ROOT} # TODO check if it correctly picks up builtin if simultaneous to system-wide. Maybe we need to pass explicit OpenSSL_DIR + -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} + -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} + -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 63bd5bf80f4f3..28051355a5649 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -88,4 +88,7 @@ find_package_handle_standard_args(OpenSSL set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS OPENSSL) set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) # CMake 4.2 changes to small case -set(OPENSSL_ROOT ${OPENSSL_ROOT} PARENT_SCOPE) +set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT} PARENT_SCOPE) +set(OPENSSL_SSL_LIBRARY ${OPENSSL_LIBRARY} PARENT_SCOPE} +set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} PARENT_SCOPE} +set(OPENSSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR} PARENT_SCOPE} From 12b683ecbfc116528d7f8f4ca473f48999d53940 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 08:41:19 +0200 Subject: [PATCH 16/54] fix typo --- builtins/openssl/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 28051355a5649..6b003e7eb30cf 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -89,6 +89,6 @@ find_package_handle_standard_args(OpenSSL set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS OPENSSL) set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) # CMake 4.2 changes to small case set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT} PARENT_SCOPE) -set(OPENSSL_SSL_LIBRARY ${OPENSSL_LIBRARY} PARENT_SCOPE} -set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} PARENT_SCOPE} -set(OPENSSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR} PARENT_SCOPE} +set(OPENSSL_SSL_LIBRARY ${OPENSSL_LIBRARY} PARENT_SCOPE) +set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} PARENT_SCOPE) +set(OPENSSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR} PARENT_SCOPE) From 3a9921b1a68354b518590c6d1c10bcb805b35645 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 10:49:01 +0200 Subject: [PATCH 17/54] fix builtin ssl name --- builtins/civetweb/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 49491b7bab66f..9b01ba2e66ff3 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -106,7 +106,7 @@ file(MAKE_DIRECTORY ${ROOT_CIVETWEB_PREFIX}/include) add_library(civetweb::civetweb IMPORTED STATIC GLOBAL) add_dependencies(civetweb::civetweb BUILTIN_CIVETWEB) if(builtin_ssl) - add_dependencies(BUILTIN_CIVETWEB BUILTIN_SSL) + add_dependencies(BUILTIN_CIVETWEB OPENSSL) endif() set_target_properties(civetweb::civetweb PROPERTIES IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} From 65e312a92e1dae88b7cea40db2923ca654e3765c Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 13:54:51 +0200 Subject: [PATCH 18/54] fix builtin name --- builtins/civetweb/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 9b01ba2e66ff3..809f71bfcfb43 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -105,7 +105,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB file(MAKE_DIRECTORY ${ROOT_CIVETWEB_PREFIX}/include) add_library(civetweb::civetweb IMPORTED STATIC GLOBAL) add_dependencies(civetweb::civetweb BUILTIN_CIVETWEB) -if(builtin_ssl) +if(builtin_openssl) add_dependencies(BUILTIN_CIVETWEB OPENSSL) endif() set_target_properties(civetweb::civetweb PROPERTIES From 461a8e0dcb1181f7907fc38197b72431950f748e Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 14:02:30 +0200 Subject: [PATCH 19/54] rename builtin name and copy-paste additional variables from xrootd to civetweb --- builtins/civetweb/CMakeLists.txt | 5 ++++- builtins/openssl/CMakeLists.txt | 6 +++--- builtins/xrootd/CMakeLists.txt | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 809f71bfcfb43..9f8b4d25dbe3b 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -94,6 +94,9 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} + -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} + -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 + -DOPENSSL_ROOT=${OPENSSL_ROOT} ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install @@ -106,7 +109,7 @@ file(MAKE_DIRECTORY ${ROOT_CIVETWEB_PREFIX}/include) add_library(civetweb::civetweb IMPORTED STATIC GLOBAL) add_dependencies(civetweb::civetweb BUILTIN_CIVETWEB) if(builtin_openssl) - add_dependencies(BUILTIN_CIVETWEB OPENSSL) + add_dependencies(BUILTIN_CIVETWEB BUILTIN_OPENSSL) endif() set_target_properties(civetweb::civetweb PROPERTIES IMPORTED_LOCATION ${ROOT_CIVETWEB_LIBRARY} diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 6b003e7eb30cf..0893bbb2b8cf1 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -37,7 +37,7 @@ else() set(OPENSSL_CONFIG_CMD ./config) endif() -ExternalProject_Add(OPENSSL +ExternalProject_Add(BUILTIN_OPENSSL URL ${OPENSSL_URL} URL_HASH ${OPENSSL_URLHASH} INSTALL_DIR ${OPENSSL_PREFIX} CONFIGURE_COMMAND ${OPENSSL_CONFIG_CMD} no-shared --prefix= --openssldir= @@ -64,12 +64,12 @@ file(MAKE_DIRECTORY ${OPENSSL_INCLUDE_DIR}) add_library(builtin_crypto INTERFACE) target_include_directories(builtin_crypto INTERFACE $) target_link_libraries(builtin_crypto INTERFACE $) -add_dependencies(builtin_crypto OPENSSL) +add_dependencies(builtin_crypto BUILTIN_OPENSSL) add_library(builtin_ssl INTERFACE) target_include_directories(builtin_ssl INTERFACE $) target_link_libraries(builtin_ssl INTERFACE $) -add_dependencies(builtin_ssl OPENSSL) +add_dependencies(builtin_ssl BUILTIN_OPENSSL) add_library(OpenSSL::Crypto ALIAS builtin_crypto) add_library(OpenSSL::SSL ALIAS builtin_ssl) diff --git a/builtins/xrootd/CMakeLists.txt b/builtins/xrootd/CMakeLists.txt index b87fe68eb5207..a83b381a94129 100644 --- a/builtins/xrootd/CMakeLists.txt +++ b/builtins/xrootd/CMakeLists.txt @@ -57,7 +57,7 @@ ExternalProject_Add( file(MAKE_DIRECTORY ${XROOTD_PREFIX}/include/xrootd) if(builtin_openssl) - add_dependencies(BUILTIN_XROOTD OPENSSL) + add_dependencies(BUILTIN_XROOTD BUILTIN_OPENSSL) endif() install(DIRECTORY ${XROOTD_PREFIX}/lib/ DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries FILES_MATCHING PATTERN "libXrd*") From 536b91f96577fa4866202ee3cda964dc70df305f Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 17:22:56 +0200 Subject: [PATCH 20/54] try fix openssl mac include not found --- builtins/civetweb/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 9f8b4d25dbe3b..6d6b3fdf55ac2 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -94,9 +94,11 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} + -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} - -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 - -DOPENSSL_ROOT=${OPENSSL_ROOT} + # -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 + # -DOPENSSL_ROOT=${OPENSSL_ROOT} + -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT} ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install From 2ee55c60590971ab1de62b85ffd1da26c71fdf95 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 17:23:48 +0200 Subject: [PATCH 21/54] Update CMakeLists.txt --- builtins/civetweb/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 6d6b3fdf55ac2..f70a636e9b5d0 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -96,9 +96,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} - # -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 - # -DOPENSSL_ROOT=${OPENSSL_ROOT} - -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT} + -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install From 222664a7c02fc375188acca013b8bca6a91a915b Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Tue, 21 Apr 2026 17:32:03 +0200 Subject: [PATCH 22/54] Update cmake/modules/SearchInstalledSoftware.cmake --- cmake/modules/SearchInstalledSoftware.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 5cab89795d521..7367b8f7a401d 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -527,7 +527,7 @@ if(fcgi) endif() endif() -#--- Check for civetweb - (has to after SSL) ------------------------------------------ +#--- Check for civetweb - (has to go after SSL) --------------------------------------- if(http AND NOT builtin_civetweb) message(STATUS "Looking for civetweb") if(fail-on-missing) From 177f76d521e0e03d7211580ba8d4d2083e001ac5 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 08:05:23 +0200 Subject: [PATCH 23/54] Apply suggestion from @ferdymercury --- builtins/openssl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 0893bbb2b8cf1..37af30c943ff8 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -86,7 +86,7 @@ find_package_handle_standard_args(OpenSSL OPENSSL_VERSION_STRING ) -set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS OPENSSL) +set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS BUILTIN_OPENSSL) set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) # CMake 4.2 changes to small case set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT} PARENT_SCOPE) set(OPENSSL_SSL_LIBRARY ${OPENSSL_LIBRARY} PARENT_SCOPE) From 014ebc492888f99782796f5f4d9a082919266b68 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 14:50:51 +0200 Subject: [PATCH 24/54] print some debug info --- builtins/civetweb/CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index f70a636e9b5d0..e05b8a8b969ca 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -63,6 +63,18 @@ else() set(ROOT_SOCKET_FLAGS "") endif() +message(WARNING ${OPENSSL_INCLUDE_DIR}) +message(WARNING ${OPENSSL_CRYPTO_LIBRARY}) +message(WARNING ${OPENSSL_SSL_LIBRARY}) +message(WARNING ${OPENSSL_INCLUDE_DIR}) +message(WARNING ${OPENSSL_LIBRARIES}) +message(WARNING ${OPENSSL_PREFIX}) +message(WARNING ${OPENSSL_ROOT_DIR}) +file(GLOB sslheaders + "${OPENSSL_INCLUDE_DIR}/openssl/*.h" +) +message(WARNING ${sslheaders}) + ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} # URL ${lcgpackages}/civetweb-${ROOT_CIVETWEB_VERSION}.tar.gz @@ -76,6 +88,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB LOG_OUTPUT_ON_FAILURE TRUE INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} CMAKE_ARGS -G ${CMAKE_GENERATOR} + -DCMAKE_VERBOSE_BUILD=ON -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_LIBDIR:PATH=/lib -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} From ed92de5494902ed67151237e1d286ce245b11c27 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 15:31:36 +0200 Subject: [PATCH 25/54] try more debugging --- builtins/civetweb/0001-requiredopenssl.patch | 25 ++++++++++++++++++++ builtins/civetweb/CMakeLists.txt | 13 +--------- 2 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 builtins/civetweb/0001-requiredopenssl.patch diff --git a/builtins/civetweb/0001-requiredopenssl.patch b/builtins/civetweb/0001-requiredopenssl.patch new file mode 100644 index 0000000000000..27c7641279214 --- /dev/null +++ b/builtins/civetweb/0001-requiredopenssl.patch @@ -0,0 +1,25 @@ +From 19539b116ea69a8ceffc505297c152d52cc87bef Mon Sep 17 00:00:00 2001 +From: ferdymercury +Date: Wed, 22 Apr 2026 15:27:40 +0200 +Subject: [PATCH] requiredopenssl + +--- + src/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 43f3a77e..39319c6e 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -53,7 +53,7 @@ if (CIVETWEB_ENABLE_SSL) + target_link_libraries(civetweb-c-library -ldl) + endif() + else() +- find_package(OpenSSL) ++ find_package(OpenSSL REQUIRED) + include_directories(${OPENSSL_INCLUDE_DIR}) + message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") + target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES}) +-- +2.34.1 + diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index e05b8a8b969ca..b565b6a3be18e 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -63,18 +63,6 @@ else() set(ROOT_SOCKET_FLAGS "") endif() -message(WARNING ${OPENSSL_INCLUDE_DIR}) -message(WARNING ${OPENSSL_CRYPTO_LIBRARY}) -message(WARNING ${OPENSSL_SSL_LIBRARY}) -message(WARNING ${OPENSSL_INCLUDE_DIR}) -message(WARNING ${OPENSSL_LIBRARIES}) -message(WARNING ${OPENSSL_PREFIX}) -message(WARNING ${OPENSSL_ROOT_DIR}) -file(GLOB sslheaders - "${OPENSSL_INCLUDE_DIR}/openssl/*.h" -) -message(WARNING ${sslheaders}) - ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} # URL ${lcgpackages}/civetweb-${ROOT_CIVETWEB_VERSION}.tar.gz @@ -87,6 +75,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB LOG_INSTALL TRUE LOG_OUTPUT_ON_FAILURE TRUE INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} + PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-requiredopenssl.patch # for debugging purposes CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_VERBOSE_BUILD=ON -DCMAKE_INSTALL_PREFIX:PATH= From 1551b64a6e7655d07e4d98ff74e3408867bab03e Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 16:44:41 +0200 Subject: [PATCH 26/54] another attempt --- builtins/civetweb/0001-fix-inc-path.patch | 24 +++++++++++++++++++ builtins/civetweb/0001-requiredopenssl.patch | 25 -------------------- builtins/civetweb/CMakeLists.txt | 2 +- 3 files changed, 25 insertions(+), 26 deletions(-) create mode 100644 builtins/civetweb/0001-fix-inc-path.patch delete mode 100644 builtins/civetweb/0001-requiredopenssl.patch diff --git a/builtins/civetweb/0001-fix-inc-path.patch b/builtins/civetweb/0001-fix-inc-path.patch new file mode 100644 index 0000000000000..06fb2f119d17f --- /dev/null +++ b/builtins/civetweb/0001-fix-inc-path.patch @@ -0,0 +1,24 @@ +From ecb6e72a7be25f3f8bfc1a0b507ccae91fa5bf4f Mon Sep 17 00:00:00 2001 +From: ferdymercury +Date: Wed, 22 Apr 2026 16:42:49 +0200 +Subject: [PATCH] fix inc path + +--- + src/CMakeLists.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 43f3a77e..7d25bfd5 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -53,6 +53,7 @@ if (CIVETWEB_ENABLE_SSL) + target_link_libraries(civetweb-c-library -ldl) + endif() + else() ++ include_directories(${OPENSSL_INCLUDE_DIR}) + find_package(OpenSSL) + include_directories(${OPENSSL_INCLUDE_DIR}) + message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") +-- +2.34.1 + diff --git a/builtins/civetweb/0001-requiredopenssl.patch b/builtins/civetweb/0001-requiredopenssl.patch deleted file mode 100644 index 27c7641279214..0000000000000 --- a/builtins/civetweb/0001-requiredopenssl.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 19539b116ea69a8ceffc505297c152d52cc87bef Mon Sep 17 00:00:00 2001 -From: ferdymercury -Date: Wed, 22 Apr 2026 15:27:40 +0200 -Subject: [PATCH] requiredopenssl - ---- - src/CMakeLists.txt | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 43f3a77e..39319c6e 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -53,7 +53,7 @@ if (CIVETWEB_ENABLE_SSL) - target_link_libraries(civetweb-c-library -ldl) - endif() - else() -- find_package(OpenSSL) -+ find_package(OpenSSL REQUIRED) - include_directories(${OPENSSL_INCLUDE_DIR}) - message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") - target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES}) --- -2.34.1 - diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index b565b6a3be18e..4046d203b2a14 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -75,7 +75,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB LOG_INSTALL TRUE LOG_OUTPUT_ON_FAILURE TRUE INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} - PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-requiredopenssl.patch # for debugging purposes + PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-fix-inc-path.patch CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_VERBOSE_BUILD=ON -DCMAKE_INSTALL_PREFIX:PATH= From a724119dbbbd9a6df2cda363c055e5d962229aff Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 17:04:40 +0200 Subject: [PATCH 27/54] avoid cache vars --- builtins/civetweb/0001-fix-inc-path.patch | 24 --------------------- builtins/civetweb/CMakeLists.txt | 3 ++- builtins/openssl/CMakeLists.txt | 26 ++++++++++++----------- 3 files changed, 16 insertions(+), 37 deletions(-) delete mode 100644 builtins/civetweb/0001-fix-inc-path.patch diff --git a/builtins/civetweb/0001-fix-inc-path.patch b/builtins/civetweb/0001-fix-inc-path.patch deleted file mode 100644 index 06fb2f119d17f..0000000000000 --- a/builtins/civetweb/0001-fix-inc-path.patch +++ /dev/null @@ -1,24 +0,0 @@ -From ecb6e72a7be25f3f8bfc1a0b507ccae91fa5bf4f Mon Sep 17 00:00:00 2001 -From: ferdymercury -Date: Wed, 22 Apr 2026 16:42:49 +0200 -Subject: [PATCH] fix inc path - ---- - src/CMakeLists.txt | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 43f3a77e..7d25bfd5 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -53,6 +53,7 @@ if (CIVETWEB_ENABLE_SSL) - target_link_libraries(civetweb-c-library -ldl) - endif() - else() -+ include_directories(${OPENSSL_INCLUDE_DIR}) - find_package(OpenSSL) - include_directories(${OPENSSL_INCLUDE_DIR}) - message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") --- -2.34.1 - diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 4046d203b2a14..7c47c2bcf6f27 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -75,7 +75,6 @@ ExternalProject_Add(BUILTIN_CIVETWEB LOG_INSTALL TRUE LOG_OUTPUT_ON_FAILURE TRUE INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} - PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-fix-inc-path.patch CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_VERBOSE_BUILD=ON -DCMAKE_INSTALL_PREFIX:PATH= @@ -99,6 +98,8 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} + -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 + -DOPENSSL_ROOT=${OPENSSL_ROOT} ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 37af30c943ff8..540ed4e6d0e61 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -48,15 +48,11 @@ ExternalProject_Add(BUILTIN_OPENSSL BUILD_BYPRODUCTS ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_SSL_LIBRARY} TIMEOUT 600) -set(OPENSSL_FOUND TRUE CACHE BOOL "" FORCE) -set(OPENSSL_VERSION ${OPENSSL_VERSION} CACHE INTERNAL "" FORCE) -set(OPENSSL_VERSION_STRING "${OPENSSL_VERSION}" CACHE INTERNAL "" FORCE) -set(OPENSSL_INCLUDE_DIR ${OPENSSL_PREFIX}/include CACHE INTERNAL "" FORCE) -set(OPENSSL_INCLUDE_DIRS ${OPENSSL_PREFIX}/include CACHE INTERNAL "" FORCE) -set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} CACHE INTERNAL "" FORCE) -set(OPENSSL_SSL_LIBRARY ${OPENSSL_SSL_LIBRARY} CACHE INTERNAL "" FORCE) -set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} CACHE INTERNAL "" FORCE) -set(OPENSSL_ROOT ${OPENSSL_PREFIX} CACHE INTERNAL "Location of the builtin OpenSSL installation" FORCE) +set(OPENSSL_FOUND TRUE) +set(OPENSSL_INCLUDE_DIR ${OPENSSL_PREFIX}/include) +set(OPENSSL_INCLUDE_DIRS ${OPENSSL_PREFIX}/include) +set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS}) +set(OPENSSL_ROOT ${OPENSSL_PREFIX}) # Dependent libraries might check for the existence of the include directories file(MAKE_DIRECTORY ${OPENSSL_INCLUDE_DIR}) @@ -87,8 +83,14 @@ find_package_handle_standard_args(OpenSSL ) set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS BUILTIN_OPENSSL) + +set(OPENSSL_FOUND TRUE) set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) # CMake 4.2 changes to small case -set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT} PARENT_SCOPE) -set(OPENSSL_SSL_LIBRARY ${OPENSSL_LIBRARY} PARENT_SCOPE) -set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} PARENT_SCOPE) +set(OPENSSL_VERSION_STRING ${OPENSSL_VERSION_STRING} PARENT_SCOPE set(OPENSSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR} PARENT_SCOPE) +set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIRS} PARENT_SCOPE) +set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} PARENT_SCOPE) +set(OPENSSL_SSL_LIBRARY ${OPENSSL_SSL_LIBRARY} PARENT_SCOPE) +set(OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES} PARENT_SCOPE) +set(OPENSSL_ROOT ${OPENSSL_ROOT}) +set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT} PARENT_SCOPE) From 04739055c6215253be0794da5e0c06f51ecc7b5c Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 17:19:03 +0200 Subject: [PATCH 28/54] fix typo --- builtins/openssl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 540ed4e6d0e61..477edf5146e5d 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -86,7 +86,7 @@ set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS BUILTIN_OPENSSL) set(OPENSSL_FOUND TRUE) set(OPENSSL_VERSION ${OPENSSL_VERSION} PARENT_SCOPE) # CMake 4.2 changes to small case -set(OPENSSL_VERSION_STRING ${OPENSSL_VERSION_STRING} PARENT_SCOPE +set(OPENSSL_VERSION_STRING ${OPENSSL_VERSION_STRING} PARENT_SCOPE) set(OPENSSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR} PARENT_SCOPE) set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIRS} PARENT_SCOPE) set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} PARENT_SCOPE) From c0c058def7f30499170a4ff0f8a963db06154348 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 18:14:30 +0200 Subject: [PATCH 29/54] more --- builtins/civetweb/CMakeLists.txt | 13 +++++++++---- builtins/xrootd/CMakeLists.txt | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 7c47c2bcf6f27..c12fbe42b41b4 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -62,6 +62,8 @@ if(NOT MSVC) else() set(ROOT_SOCKET_FLAGS "") endif() +message(WARNING ${ROOT_SOCKET_FLAGS}) +message(WARNING ${ROOT_SSL_API}) ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} @@ -80,10 +82,10 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_LIBDIR:PATH=/lib -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} - -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} - -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} - -DCMAKE_C_FLAGS:STRING=${ROOT_SOCKET_FLAGS} - -DCMAKE_CXX_FLAGS:STRING=${ROOT_SOCKET_FLAGS} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} # ROOT_SOCKET_FLAGS + -DCMAKE_CXX_FLAGS=${ROOT_EXTERNAL_CXX_FLAGS} -DBUILD_SHARED_LIBS:BOOL=FALSE -DBUILD_TESTING=FALSE -DCIVETWEB_ENABLE_ASAN=OFF # If set to ON, you need to set below link_library interface to UBSAN libs @@ -100,6 +102,9 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 -DOPENSSL_ROOT=${OPENSSL_ROOT} + -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} + -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} + # -DCMAKE_INSTALL_RPATH:STRING=${rpath_origin} ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install diff --git a/builtins/xrootd/CMakeLists.txt b/builtins/xrootd/CMakeLists.txt index a83b381a94129..3edd003b47292 100644 --- a/builtins/xrootd/CMakeLists.txt +++ b/builtins/xrootd/CMakeLists.txt @@ -11,12 +11,12 @@ set(XROOTD_PREFIX ${CMAKE_BINARY_DIR}/XROOTD-prefix) message(STATUS "Downloading and building XROOTD version ${XROOTD_VERSION}") -set(XROOTD_INCLUDE_DIRS ${XROOTD_PREFIX}/include/xrootd CACHE INTERNAL "" FORCE) +set(XROOTD_INCLUDE_DIRS ${XROOTD_PREFIX}/include/xrootd) set(XRDCL_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}XrdCl${CMAKE_SHARED_LIBRARY_SUFFIX}) set(XRDUTILS_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}XrdUtils${CMAKE_SHARED_LIBRARY_SUFFIX}) -set(XROOTD_CLIENT_LIBRARIES ${XROOTD_PREFIX}/lib/${XRDCL_NAME} CACHE INTERNAL "" FORCE) -set(XROOTD_UTILS_LIBRARIES ${XROOTD_PREFIX}/lib/${XRDUTILS_NAME} CACHE INTERNAL "" FORCE) -set(XROOTD_LIBRARIES ${XROOTD_PREFIX}/lib/${XRDCL_NAME} CACHE INTERNAL "" FORCE) +set(XROOTD_CLIENT_LIBRARIES ${XROOTD_PREFIX}/lib/${XRDCL_NAME}) +set(XROOTD_UTILS_LIBRARIES ${XROOTD_PREFIX}/lib/${XRDUTILS_NAME}) +set(XROOTD_LIBRARIES ${XROOTD_PREFIX}/lib/${XRDCL_NAME}) if(APPLE) set(rpath_origin "@loader_path") @@ -65,3 +65,8 @@ install(DIRECTORY ${XROOTD_PREFIX}/include/xrootd/ DESTINATION ${CMAKE_INSTALL_I set(CMAKE_BUILD_RPATH ${CMAKE_BUILD_RPATH} ${XROOTD_PREFIX}/lib PARENT_SCOPE) set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS BUILTIN_XROOTD) + +set(XROOTD_INCLUDE_DIRS ${XROOTD_INCLUDE_DIRS} PARENT_SCOPE) +set(XROOTD_CLIENT_LIBRARIES ${XROOTD_CLIENT_LIBRARIES} PARENT_SCOPE) +set(XROOTD_UTILS_LIBRARIES ${XROOTD_UTILS_LIBRARIES} PARENT_SCOPE) +set(XROOTD_LIBRARIES ${XROOTD_LIBRARIES} PARENT_SCOPE) From 89e98d83732dea3688162473921903e872f61598 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 18:44:08 +0200 Subject: [PATCH 30/54] verbose --- builtins/civetweb/CMakeLists.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index c12fbe42b41b4..d56ac886ba52a 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -58,12 +58,10 @@ else() set(ROOT_SSL_API "-DCIVETWEB_ENABLE_SSL=OFF") endif() if(NOT MSVC) - set(ROOT_SOCKET_FLAGS "-DUSE_X_DOM_SOCKET") + set(ROOT_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_X_DOM_SOCKET") else() - set(ROOT_SOCKET_FLAGS "") + set(ROOT_C_FLAGS ${CMAKE_C_FLAGS}) endif() -message(WARNING ${ROOT_SOCKET_FLAGS}) -message(WARNING ${ROOT_SSL_API}) ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} @@ -72,19 +70,20 @@ ExternalProject_Add(BUILTIN_CIVETWEB URL_HASH SHA256=${ROOT_CIVETWEB_HASH} LOG_DOWNLOAD TRUE - LOG_CONFIGURE TRUE - LOG_BUILD TRUE - LOG_INSTALL TRUE - LOG_OUTPUT_ON_FAILURE TRUE + LOG_CONFIGURE FALSE + LOG_BUILD FALSE + LOG_INSTALL FALSE + LOG_OUTPUT_ON_FAILURE FALSE INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_VERBOSE_BUILD=ON + -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_LIBDIR:PATH=/lib -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} - -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} # ROOT_SOCKET_FLAGS + -DCMAKE_C_FLAGS=${ROOT_C_FLAGS} -DCMAKE_CXX_FLAGS=${ROOT_EXTERNAL_CXX_FLAGS} -DBUILD_SHARED_LIBS:BOOL=FALSE -DBUILD_TESTING=FALSE @@ -100,6 +99,8 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} + -DOPENSSL_FOUND=TRUE + -DOpenSSL_FOUND=TRUE -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 -DOPENSSL_ROOT=${OPENSSL_ROOT} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} From 31ca26cf887af0a402b631e76a0490c2ca83a7d2 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 19:03:16 +0200 Subject: [PATCH 31/54] vninja --- builtins/civetweb/CMakeLists.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index d56ac886ba52a..07bc3828bd26a 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -74,9 +74,13 @@ ExternalProject_Add(BUILTIN_CIVETWEB LOG_BUILD FALSE LOG_INSTALL FALSE LOG_OUTPUT_ON_FAILURE FALSE + USES_TERMINAL_DOWNLOAD true + USES_TERMINAL_UPDATE true + USES_TERMINAL_BUILD true + USES_TERMINAL_INSTALL true + USES_TERMINAL_TEST true INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} CMAKE_ARGS -G ${CMAKE_GENERATOR} - -DCMAKE_VERBOSE_BUILD=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_LIBDIR:PATH=/lib @@ -102,13 +106,12 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_FOUND=TRUE -DOpenSSL_FOUND=TRUE -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 - -DOPENSSL_ROOT=${OPENSSL_ROOT} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} # -DCMAKE_INSTALL_RPATH:STRING=${rpath_origin} ${ROOT_SSL_API} - BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} - INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} -- -j 1 -v + INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install -v BUILD_BYPRODUCTS ${ROOT_CIVETWEB_LIBRARY} TIMEOUT 600 From ae7a543618bc25979d414b442bcafff330f66862 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 19:22:10 +0200 Subject: [PATCH 32/54] crypto --- builtins/civetweb/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 07bc3828bd26a..4a4005d0329ad 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -98,9 +98,9 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCIVETWEB_ENABLE_WEBSOCKETS=ON -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} - -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} - -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} - -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} + #-DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} + #-DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} + #-DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} -DOPENSSL_FOUND=TRUE From ce08a249538022faba309f25df6c4e577f4d2cae Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 19:48:22 +0200 Subject: [PATCH 33/54] more mssges --- builtins/civetweb/0001-message-libs.patch | 25 +++++++++++++++++++++++ builtins/civetweb/CMakeLists.txt | 4 ++++ 2 files changed, 29 insertions(+) create mode 100644 builtins/civetweb/0001-message-libs.patch diff --git a/builtins/civetweb/0001-message-libs.patch b/builtins/civetweb/0001-message-libs.patch new file mode 100644 index 0000000000000..ffaacecf4ceba --- /dev/null +++ b/builtins/civetweb/0001-message-libs.patch @@ -0,0 +1,25 @@ +From a548eb755121c1d4afb8b788c27b653ad6567d5a Mon Sep 17 00:00:00 2001 +From: ferdymercury +Date: Wed, 22 Apr 2026 19:46:28 +0200 +Subject: [PATCH] message libs + +--- + src/CMakeLists.txt | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 43f3a77e..eab761fb 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -56,6 +56,8 @@ if (CIVETWEB_ENABLE_SSL) + find_package(OpenSSL) + include_directories(${OPENSSL_INCLUDE_DIR}) + message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") ++ message(STATUS "OpenSSL libraries: ${OPENSSL_LIBRARIES}") ++ target_include_directories(civetweb-c-library ${OPENSSL_INCLUDE_DIR}) + target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES}) + endif() + endif() +-- +2.34.1 + diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 4a4005d0329ad..59769fe87b00b 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -62,6 +62,9 @@ if(NOT MSVC) else() set(ROOT_C_FLAGS ${CMAKE_C_FLAGS}) endif() +if (CMAKE_OSX_SYSROOT) + set(ROOT_C_FLAGS "${ROOT_C_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") +endif() ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} @@ -80,6 +83,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB USES_TERMINAL_INSTALL true USES_TERMINAL_TEST true INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} + PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-message-libs.patch CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH= From a652ac3173013b9c6e4d75ccb32d7ca9ddff3326 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 19:53:12 +0200 Subject: [PATCH 34/54] fix --- builtins/civetweb/0001-message-libs.patch | 2 +- builtins/civetweb/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/0001-message-libs.patch b/builtins/civetweb/0001-message-libs.patch index ffaacecf4ceba..751eabf11389b 100644 --- a/builtins/civetweb/0001-message-libs.patch +++ b/builtins/civetweb/0001-message-libs.patch @@ -16,7 +16,7 @@ index 43f3a77e..eab761fb 100644 include_directories(${OPENSSL_INCLUDE_DIR}) message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") + message(STATUS "OpenSSL libraries: ${OPENSSL_LIBRARIES}") -+ target_include_directories(civetweb-c-library ${OPENSSL_INCLUDE_DIR}) ++ target_include_directories(civetweb-c-library PUBLIC ${OPENSSL_INCLUDE_DIR}) target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES}) endif() endif() diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 59769fe87b00b..96bbf2b13ec39 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -103,7 +103,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCIVETWEB_ENABLE_WEBSOCKETS=ON -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} #-DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} - #-DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} + -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} #-DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} From 569965897ffb4e440fb7b3f178426973d55b0e9b Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Wed, 22 Apr 2026 20:30:07 +0200 Subject: [PATCH 35/54] Update CMakeLists.txt --- builtins/civetweb/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 96bbf2b13ec39..fa1e5facc7ae0 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -102,7 +102,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCIVETWEB_ENABLE_WEBSOCKETS=ON -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} - #-DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} + -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} #-DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} From 6a750b31fe3558d9d573c937ab6559d25e20a127 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Thu, 23 Apr 2026 07:51:12 +0200 Subject: [PATCH 36/54] Update CMakeLists.txt --- builtins/civetweb/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index fa1e5facc7ae0..311f60aa1720f 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -104,7 +104,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} - #-DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} + -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} -DOPENSSL_FOUND=TRUE @@ -114,8 +114,8 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} # -DCMAKE_INSTALL_RPATH:STRING=${rpath_origin} ${ROOT_SSL_API} - BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} -- -j 1 -v - INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install -v + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} -- -j1 -v + INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install -- -v BUILD_BYPRODUCTS ${ROOT_CIVETWEB_LIBRARY} TIMEOUT 600 From c46471607a4e22309d5b27d34dc712ecbb0a2364 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Thu, 23 Apr 2026 08:31:15 +0200 Subject: [PATCH 37/54] Update CMakeLists.txt --- builtins/civetweb/CMakeLists.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 311f60aa1720f..c32d47959979c 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -65,6 +65,11 @@ endif() if (CMAKE_OSX_SYSROOT) set(ROOT_C_FLAGS "${ROOT_C_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") endif() +if(APPLE) +set(ROOT_GENERATOR "Unix Makefiles") +else() +set(ROOT_GENERATOR ${CMAKE_GENERATOR}) +endif() ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} @@ -84,7 +89,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB USES_TERMINAL_TEST true INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-message-libs.patch - CMAKE_ARGS -G ${CMAKE_GENERATOR} + CMAKE_ARGS -G ${ROOT_GENERATOR} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_LIBDIR:PATH=/lib @@ -114,8 +119,8 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} # -DCMAKE_INSTALL_RPATH:STRING=${rpath_origin} ${ROOT_SSL_API} - BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} -- -j1 -v - INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install -- -v + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target civetweb-c-library -- -j1 -v + INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install BUILD_BYPRODUCTS ${ROOT_CIVETWEB_LIBRARY} TIMEOUT 600 From 1ad58d212ff771a7e665ae3f105c12af806a3739 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Thu, 23 Apr 2026 09:20:24 +0200 Subject: [PATCH 38/54] Update CMakeLists.txt --- builtins/civetweb/CMakeLists.txt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index c32d47959979c..f0a60a620a508 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -65,11 +65,6 @@ endif() if (CMAKE_OSX_SYSROOT) set(ROOT_C_FLAGS "${ROOT_C_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") endif() -if(APPLE) -set(ROOT_GENERATOR "Unix Makefiles") -else() -set(ROOT_GENERATOR ${CMAKE_GENERATOR}) -endif() ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} @@ -89,7 +84,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB USES_TERMINAL_TEST true INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-message-libs.patch - CMAKE_ARGS -G ${ROOT_GENERATOR} + CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_LIBDIR:PATH=/lib @@ -119,7 +114,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} # -DCMAKE_INSTALL_RPATH:STRING=${rpath_origin} ${ROOT_SSL_API} - BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target civetweb-c-library -- -j1 -v + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} --target install BUILD_BYPRODUCTS ${ROOT_CIVETWEB_LIBRARY} From 12734e61662f3b73b48ef36757c7cb0b4cfb011d Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Thu, 23 Apr 2026 09:27:07 +0200 Subject: [PATCH 39/54] print headers --- builtins/civetweb/0001-message-libs.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/0001-message-libs.patch b/builtins/civetweb/0001-message-libs.patch index 751eabf11389b..84a2e4ff59c18 100644 --- a/builtins/civetweb/0001-message-libs.patch +++ b/builtins/civetweb/0001-message-libs.patch @@ -15,8 +15,8 @@ index 43f3a77e..eab761fb 100644 find_package(OpenSSL) include_directories(${OPENSSL_INCLUDE_DIR}) message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") -+ message(STATUS "OpenSSL libraries: ${OPENSSL_LIBRARIES}") -+ target_include_directories(civetweb-c-library PUBLIC ${OPENSSL_INCLUDE_DIR}) ++ file(GLOB_RECURSE MyHeaders ${OPENSSL_INCLUDE_DIR}/*.h) ++ message(WARNING ${MyHeaders}) target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES}) endif() endif() From e48cc3da7a666c359cf054da48ca0ca420fe291c Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Thu, 23 Apr 2026 10:02:27 +0200 Subject: [PATCH 40/54] Update 0001-message-libs.patch --- builtins/civetweb/0001-message-libs.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/civetweb/0001-message-libs.patch b/builtins/civetweb/0001-message-libs.patch index 84a2e4ff59c18..253d86e56e8a6 100644 --- a/builtins/civetweb/0001-message-libs.patch +++ b/builtins/civetweb/0001-message-libs.patch @@ -15,7 +15,7 @@ index 43f3a77e..eab761fb 100644 find_package(OpenSSL) include_directories(${OPENSSL_INCLUDE_DIR}) message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") -+ file(GLOB_RECURSE MyHeaders ${OPENSSL_INCLUDE_DIR}/*.h) ++ file(GLOB_RECURSE MyHeaders ${OPENSSL_INCLUDE_DIR}/../*.h) + message(WARNING ${MyHeaders}) target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES}) endif() From f52dcf9f0dbb065a1002692f3609a10e5afb12a7 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Thu, 23 Apr 2026 10:14:47 +0200 Subject: [PATCH 41/54] Update CMakeLists.txt From 8dd485541de10694938e99cc6834b0f1250e8dbf Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Thu, 23 Apr 2026 11:57:27 +0200 Subject: [PATCH 42/54] undo --- builtins/civetweb/0001-message-libs.patch | 25 ----------------------- builtins/civetweb/CMakeLists.txt | 20 ++++++++---------- 2 files changed, 9 insertions(+), 36 deletions(-) delete mode 100644 builtins/civetweb/0001-message-libs.patch diff --git a/builtins/civetweb/0001-message-libs.patch b/builtins/civetweb/0001-message-libs.patch deleted file mode 100644 index 253d86e56e8a6..0000000000000 --- a/builtins/civetweb/0001-message-libs.patch +++ /dev/null @@ -1,25 +0,0 @@ -From a548eb755121c1d4afb8b788c27b653ad6567d5a Mon Sep 17 00:00:00 2001 -From: ferdymercury -Date: Wed, 22 Apr 2026 19:46:28 +0200 -Subject: [PATCH] message libs - ---- - src/CMakeLists.txt | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 43f3a77e..eab761fb 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -56,6 +56,8 @@ if (CIVETWEB_ENABLE_SSL) - find_package(OpenSSL) - include_directories(${OPENSSL_INCLUDE_DIR}) - message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}") -+ file(GLOB_RECURSE MyHeaders ${OPENSSL_INCLUDE_DIR}/../*.h) -+ message(WARNING ${MyHeaders}) - target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES}) - endif() - endif() --- -2.34.1 - diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index f0a60a620a508..b70703f473197 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -83,9 +83,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB USES_TERMINAL_INSTALL true USES_TERMINAL_TEST true INSTALL_DIR ${ROOT_CIVETWEB_PREFIX} - PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/0001-message-libs.patch CMAKE_ARGS -G ${CMAKE_GENERATOR} - -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_LIBDIR:PATH=/lib -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} @@ -102,16 +100,16 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCIVETWEB_ENABLE_WEBSOCKETS=ON -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} - -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} - -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} + #-DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} + #-DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} - -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} - -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} - -DOPENSSL_FOUND=TRUE - -DOpenSSL_FOUND=TRUE - -DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 - -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} - -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} + #-DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} + #-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} + #-DOPENSSL_FOUND=TRUE + #-DOpenSSL_FOUND=TRUE + #-DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27 + #-DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} + #-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} # -DCMAKE_INSTALL_RPATH:STRING=${rpath_origin} ${ROOT_SSL_API} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_CIVETWEB_BUILD_COMMAND_FLAGS} From da2181e22de6c96d8a178e288b16634c1d5f235f Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 10:55:51 +0200 Subject: [PATCH 43/54] [net] document civetweb builtin mechanism and version link --- net/http/src/TCivetweb.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/http/src/TCivetweb.cxx b/net/http/src/TCivetweb.cxx index fce363e8e94a7..dd60c79364a59 100644 --- a/net/http/src/TCivetweb.cxx +++ b/net/http/src/TCivetweb.cxx @@ -512,7 +512,9 @@ static int begin_request_handler(struct mg_connection *conn, void *) THttpEngine implementation, based on civetweb embedded server It is default kind of engine, created for THttpServer -Currently v1.15 from https://github.com/civetweb/civetweb is used +When `builtin_civetweb=ON` (default), the release is grabbed from https://github.com/civetweb/civetweb corresponding +to the version defined in https://github.com/root-project/root/blob/master/builtins/civetweb/CMakeLists.txt +Switching to `builtin_civetweb=OFF` in order to use system-packages instead might lead to instabilities on some platforms. Additional options can be specified: From 81a80306dfb960b95fdee622425377c305c8de96 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 15:44:39 +0200 Subject: [PATCH 44/54] [civetweb] add patches after 1.16 that solve several CVE --- ...0001-only-include-engine-when-needed.patch | 26 + builtins/civetweb/CMakeLists.txt | 5 +- builtins/civetweb/v1_17.patch | 186907 +++++++++++++++ 3 files changed, 186937 insertions(+), 1 deletion(-) create mode 100644 builtins/civetweb/0001-only-include-engine-when-needed.patch create mode 100644 builtins/civetweb/v1_17.patch diff --git a/builtins/civetweb/0001-only-include-engine-when-needed.patch b/builtins/civetweb/0001-only-include-engine-when-needed.patch new file mode 100644 index 0000000000000..80f850596afd8 --- /dev/null +++ b/builtins/civetweb/0001-only-include-engine-when-needed.patch @@ -0,0 +1,26 @@ +From 32dd125e9bdec6844fc8885eb467fb9bf24d44fa Mon Sep 17 00:00:00 2001 +From: ferdymercury +Date: Mon, 27 Apr 2026 15:34:11 +0200 +Subject: [PATCH] only include engine when needed + +--- + src/civetweb.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index f6a60e21..003e9e28 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1797,7 +1797,9 @@ typedef struct SSL_CTX SSL_CTX; + #include + #include + #include ++#if defined(OPENSSL_API_1_0) + #include ++#endif + #include + #include + #include +-- +2.34.1 + diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index b70703f473197..c6fd130177646 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -10,6 +10,8 @@ set(ROOT_CIVETWEB_VERSION 1.16) set(ROOT_CIVETWEB_HASH "f0e471c1bf4e7804a6cfb41ea9d13e7d623b2bcc7bc1e2a4dd54951a24d60285") set(ROOT_CIVETWEB_PREFIX ${CMAKE_BINARY_DIR}/builtins/CIVETWEB-prefix) set(ROOT_CIVETWEB_LIBRARY ${ROOT_CIVETWEB_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}civetweb${CMAKE_STATIC_LIBRARY_SUFFIX}) +set(ROOT_CIVETWEB_PATCH_FILE_1 ${CMAKE_CURRENT_SOURCE_DIR}/v1_17.patch) # From master branch (https://github.com/civetweb/civetweb/commit/588860e30721bf5453b0440c390865a8e85dcae5): git format-patch v1.16 --stdout > v1_17.patch +set(ROOT_CIVETWEB_PATCH_FILE_2 ${CMAKE_CURRENT_SOURCE_DIR}/0001-only-include-engine-when-needed.patch) # From https://github.com/civetweb/civetweb/pull/1389 git format-patch -1 if (NOT DEFINED GIT_EXECUTABLE) set(GIT_EXECUTABLE "git") @@ -71,10 +73,11 @@ ExternalProject_Add(BUILTIN_CIVETWEB # URL ${lcgpackages}/civetweb-${ROOT_CIVETWEB_VERSION}.tar.gz URL https://github.com/civetweb/civetweb/archive/refs/tags/v${ROOT_CIVETWEB_VERSION}.tar.gz # TODO move to LCG URL_HASH SHA256=${ROOT_CIVETWEB_HASH} - + PATCH_COMMAND ${GIT_EXECUTABLE} apply ${ROOT_CIVETWEB_PATCH_FILE_1} ${ROOT_CIVETWEB_PATCH_FILE_2} LOG_DOWNLOAD TRUE LOG_CONFIGURE FALSE LOG_BUILD FALSE + LOG_PATCH TRUE LOG_INSTALL FALSE LOG_OUTPUT_ON_FAILURE FALSE USES_TERMINAL_DOWNLOAD true diff --git a/builtins/civetweb/v1_17.patch b/builtins/civetweb/v1_17.patch new file mode 100644 index 0000000000000..ecad2b3d4cd7d --- /dev/null +++ b/builtins/civetweb/v1_17.patch @@ -0,0 +1,186907 @@ +From 2bdeaed9f982abc6d551932363a2ae9e2e8dd9ef Mon Sep 17 00:00:00 2001 +From: Lorenzo Canepa +Date: Sat, 19 Feb 2022 19:59:06 +0100 +Subject: [PATCH 001/173] should fix *** CID 349582: Null pointer dereferences + (REVERSE_INULL) + +Signed-off-by: Lorenzo Canepa +--- + src/civetweb.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 2c10a06f..f1c25b3c 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -12711,11 +12711,14 @@ dav_lock_file(struct mg_connection *conn, const char *path) + int i; + uint64_t LOCK_DURATION_NS = + (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000; +- struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock; ++ struct twebdav_lock *dav_lock = NULL; + +- if (!path || !conn->dom_ctx || !conn->request_info.remote_user) { ++ if (!path || !conn ++ || !conn->dom_ctx || !conn->request_info.remote_user || !conn->phys_ctx) { + return; + } ++ ++ dav_lock = conn->phys_ctx->webdav_lock; + mg_get_request_link(conn, link_buf, sizeof(link_buf)); + + /* const char *refresh = mg_get_header(conn, "If"); */ +-- +2.34.1 + + +From 49332567ab616f106d18f8c34e19be51281cf8cf Mon Sep 17 00:00:00 2001 +From: Niklas Fiekas +Date: Tue, 11 Apr 2023 09:04:44 +0200 +Subject: [PATCH 002/173] Fix -Wmissing-field-initializers in C++ code + +--- + src/CivetServer.cpp | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp +index fafd6e18..2bcf5c31 100644 +--- a/src/CivetServer.cpp ++++ b/src/CivetServer.cpp +@@ -406,12 +406,12 @@ CivetServer::CivetServer(const char **options, + userCloseHandler = NULL; + } + callbacks.connection_close = closeHandler; +- struct mg_init_data mg_start_init_data = {0}; ++ struct mg_init_data mg_start_init_data = {}; + mg_start_init_data.callbacks = &callbacks; + mg_start_init_data.user_data = this; + mg_start_init_data.configuration_options = options; + +- struct mg_error_data mg_start_error_data = {0}; ++ struct mg_error_data mg_start_error_data = {}; + char errtxtbuf[256] = {0}; + mg_start_error_data.text = errtxtbuf; + mg_start_error_data.text_buffer_size = sizeof(errtxtbuf); +@@ -450,12 +450,12 @@ CivetServer::CivetServer(const std::vector &options, + } + pointers.back() = NULL; + +- struct mg_init_data mg_start_init_data = {0}; ++ struct mg_init_data mg_start_init_data = {}; + mg_start_init_data.callbacks = &callbacks; + mg_start_init_data.user_data = this; + mg_start_init_data.configuration_options = &pointers[0]; + +- struct mg_error_data mg_start_error_data = {0}; ++ struct mg_error_data mg_start_error_data = {}; + char errtxtbuf[256] = {0}; + mg_start_error_data.text = errtxtbuf; + mg_start_error_data.text_buffer_size = sizeof(errtxtbuf); +-- +2.34.1 + + +From 23095a8d89fd15e9211c0b9e0023cda38c46b418 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Sat, 15 Apr 2023 12:11:16 +0200 +Subject: [PATCH 003/173] Fix compilation of USE_LUA without USE_TIMERS + +Signed-off-by: DL6ER +--- + src/mod_lua.inl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index f1073e55..d280e897 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -2905,8 +2905,8 @@ prepare_lua_environment(struct mg_context *ctx, + || (lua_env_type == LUA_ENV_TYPE_BACKGROUND)) { + reg_function(L, "set_timeout", lwebsocket_set_timeout); + reg_function(L, "set_interval", lwebsocket_set_interval); +-#endif + } ++#endif + + reg_conn_function(L, "get_mime_type", lsp_get_mime_type, conn); + reg_conn_function(L, "get_option", lsp_get_option, conn); +-- +2.34.1 + + +From 5a9aec4f86289523960812ea3569856e57e6220e Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 16 Apr 2023 11:57:45 +0200 +Subject: [PATCH 004/173] Update version number to V1.17 + +--- + RELEASE_NOTES.md | 13 +++++++++++++ + VisualStudio/civetweb_lua/civetweb_lua.vcxproj | 7 ++++--- + include/civetweb.h | 4 ++-- + 3 files changed, 19 insertions(+), 5 deletions(-) + +diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md +index 354061ee..0bdd1c5a 100644 +--- a/RELEASE_NOTES.md ++++ b/RELEASE_NOTES.md +@@ -1,3 +1,13 @@ ++Release Notes v1.17 (work in progress) ++=== ++### Objectives: *to be defined* ++ ++Changes ++------- ++ ++- Update version number ++ ++ + Release Notes v1.16 + === + ### Objectives: *bug fixes, documentation, WebDAV* +@@ -21,6 +31,9 @@ Changes + - Remove Conan support + - Update version number + ++Note: A precompiled 32-bit executables for Windows is no longer provided, but only a 64 bit version. ++The source code itself still supports 32-bit platforms. ++ + Known Issues + ----- + +diff --git a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj +index 7eb6fd9f..58e7fc7c 100644 +--- a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj ++++ b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj +@@ -110,7 +110,8 @@ + true + + +- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.14 --set-product-version 1.14 ++ ++ + + + +@@ -159,7 +160,7 @@ + true + + +- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 ++ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 + + + +@@ -180,7 +181,7 @@ + true + + +- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 ++ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 + + + +diff --git a/include/civetweb.h b/include/civetweb.h +index 167c6a76..78149722 100644 +--- a/include/civetweb.h ++++ b/include/civetweb.h +@@ -23,9 +23,9 @@ + #ifndef CIVETWEB_HEADER_INCLUDED + #define CIVETWEB_HEADER_INCLUDED + +-#define CIVETWEB_VERSION "1.16" ++#define CIVETWEB_VERSION "1.17" + #define CIVETWEB_VERSION_MAJOR (1) +-#define CIVETWEB_VERSION_MINOR (16) ++#define CIVETWEB_VERSION_MINOR (17) + #define CIVETWEB_VERSION_PATCH (0) + + #ifndef CIVETWEB_API +-- +2.34.1 + + +From c4bb956f20ee1ddbbdb5704d5c59da6ac753cad1 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 16 Apr 2023 12:03:57 +0200 +Subject: [PATCH 005/173] Add a cast for Interlocked functions, required only + for C++ in Windows + +See #1151 +--- + src/civetweb.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 9e321edf..93bbed9f 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1125,7 +1125,15 @@ mg_atomic_inc(volatile ptrdiff_t *addr) + #if defined(_WIN64) && !defined(NO_ATOMICS) + ret = InterlockedIncrement64(addr); + #elif defined(_WIN32) && !defined(NO_ATOMICS) ++#ifdef __cplusplus ++ /* For C++ the Microsoft Visual Studio compiler can not decide what ++ * overloaded function prototpye in the SDC corresponds to "ptrdiff_t". */ ++ static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch"); ++ static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch"); ++ ret = InterlockedIncrement((LONG *)addr); ++#else + ret = InterlockedIncrement(addr); ++#endif + #elif defined(__GNUC__) \ + && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ + && !defined(NO_ATOMICS) +@@ -1148,7 +1156,14 @@ mg_atomic_dec(volatile ptrdiff_t *addr) + #if defined(_WIN64) && !defined(NO_ATOMICS) + ret = InterlockedDecrement64(addr); + #elif defined(_WIN32) && !defined(NO_ATOMICS) ++#ifdef __cplusplus ++ /* see mg_atomic_inc */ ++ static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch"); ++ static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch"); ++ ret = InterlockedDecrement((LONG *)addr); ++#else + ret = InterlockedDecrement(addr); ++#endif + #elif defined(__GNUC__) \ + && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ + && !defined(NO_ATOMICS) +-- +2.34.1 + + +From 180db0fcafb40393db16224a971eb48841973fec Mon Sep 17 00:00:00 2001 +From: Niklas Fiekas +Date: Fri, 21 Apr 2023 08:34:44 +0200 +Subject: [PATCH 006/173] Fix mg_get_cookie() parameter description + +--- + docs/api/mg_get_cookie.md | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/docs/api/mg_get_cookie.md b/docs/api/mg_get_cookie.md +index a738bc07..637caeee 100644 +--- a/docs/api/mg_get_cookie.md ++++ b/docs/api/mg_get_cookie.md +@@ -6,8 +6,8 @@ + + | Parameter | Type | Description | + | :--- | :--- | :--- | +-|**`cookie`**|`const char *`|The cookie name| +-|**`var_name`**|`const char *`|The variable name| ++|**`cookie`**|`const char *`|The unparsed cookie header| ++|**`var_name`**|`const char *`|The cookie name| + |**`buf`**|`char *`|The buffer where to store the contents of the cookie| + |**`buf_len`**|`size_t`|The length of the cookie buffer, including the terminating NUL| + +-- +2.34.1 + + +From 5d2faa55481b8299d6f16c131cfc1a1ec06e9786 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 23 Apr 2023 22:13:00 +0200 +Subject: [PATCH 007/173] Format code after merge + +--- + src/civetweb.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 71ed388f..f99d7039 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -12825,8 +12825,8 @@ dav_lock_file(struct mg_connection *conn, const char *path) + (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000; + struct twebdav_lock *dav_lock = NULL; + +- if (!path || !conn +- || !conn->dom_ctx || !conn->request_info.remote_user || !conn->phys_ctx) { ++ if (!path || !conn || !conn->dom_ctx || !conn->request_info.remote_user ++ || !conn->phys_ctx) { + return; + } + +-- +2.34.1 + + +From 59391a97fea8cdb442150c4784fdc3b7edbb190f Mon Sep 17 00:00:00 2001 +From: Biswapriyo Nath +Date: Fri, 28 Apr 2023 19:16:24 +0530 +Subject: [PATCH 008/173] Fix compiler error with clang + +This fixes the following +error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes] +--- + src/main.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/main.c b/src/main.c +index 66510d0f..f056a954 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -2474,7 +2474,7 @@ show_settings_dialog(void) + + + static void +-change_password_file() ++change_password_file(void) + { + /* Parameter for size/format tuning of the dialog */ + short HEIGHT = 15; +@@ -2685,7 +2685,7 @@ sysinfo_reload(struct dlg_proc_param *prm) + + + int +-show_system_info() ++show_system_info(void) + { + /* Parameter for size/format tuning of the dialog */ + short HEIGHT = 15; +-- +2.34.1 + + +From ac00b3bec380665d25fa70a755bb8c1de4ad3db1 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 30 Apr 2023 09:07:18 +0200 +Subject: [PATCH 009/173] Avoid Memory Sanitizer warning + +Fixes #1154 +--- + src/civetweb.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index f99d7039..5be49462 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -2325,7 +2325,7 @@ STOP_FLAG_IS_TWO(stop_flag_t *f) + static void + STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v) + { +- stop_flag_t sf; ++ stop_flag_t sf = 0; + do { + sf = mg_atomic_compare_and_swap(f, *f, v); + } while (sf != v); +@@ -9528,9 +9528,8 @@ connect_socket( + /* Data for poll */ + struct mg_pollfd pfd[1]; + int pollres; +- int ms_wait = 10000; /* 10 second timeout */ +- stop_flag_t nonstop; +- STOP_FLAG_ASSIGN(&nonstop, 0); ++ int ms_wait = 10000; /* 10 second timeout */ ++ stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */ + + /* For a non-blocking socket, the connect sequence is: + * 1) call connect (will not block) +-- +2.34.1 + + +From 2b33a24c54c847ca9da2031adafa7cb8e3298065 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Mon, 1 May 2023 21:53:55 +0200 +Subject: [PATCH 010/173] Merge with 2023 update branch + +--- + src/civetweb.c | 90 +++++++++++++++++++++++++------------------------- + 1 file changed, 45 insertions(+), 45 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 5be49462..fdea2db4 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1,4 +1,4 @@ +-/* Copyright (c) 2013-2021 the Civetweb developers ++/* Copyright (c) 2013-2023 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy +@@ -51,8 +51,8 @@ + #if !defined(_CRT_SECURE_NO_WARNINGS) + #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ + #endif +-#if !defined(_WIN32_WINNT) /* defined for tdm-gcc so we can use getnameinfo */ +-#define _WIN32_WINNT 0x0502 ++#if !defined(_WIN32_WINNT) /* Minimum API version */ ++#define _WIN32_WINNT 0x0601 + #endif + #else + #if !defined(_GNU_SOURCE) +@@ -525,10 +525,10 @@ mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, + + + #if defined(_WIN32) /* WINDOWS include block */ +-#include /* *alloc( */ +-#include /* *alloc( */ +-#include /* struct timespec */ +-#include ++#include ++#include /* *alloc( */ ++#include /* *alloc( */ ++#include /* struct timespec */ + #include /* DTL add for SO_EXCLUSIVE */ + #include + +@@ -1322,13 +1322,13 @@ mg_malloc_ex(size_t size, + #endif + + if (data) { ++ uintptr_t *tmp = (uintptr_t *)data; + ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size); + mg_atomic_max(&mstat->maxMemUsed, mmem); +- + mg_atomic_inc(&mstat->blockCount); +- ((uintptr_t *)data)[0] = size; +- ((uintptr_t *)data)[1] = (uintptr_t)mstat; +- memory = (void *)(((char *)data) + 2 * sizeof(uintptr_t)); ++ tmp[0] = size; ++ tmp[1] = (uintptr_t)mstat; ++ memory = (void *)&tmp[2]; + } + + #if defined(MEMORY_DEBUGGING) +@@ -22315,44 +22315,44 @@ mg_get_connection_info(const struct mg_context *ctx, + return (int)connection_info_length; + } + ++ + #if 0 +-/* Get handler information. It can be printed or stored by the caller. +- * Return the size of available information. */ ++/* Get handler information. Not fully implemented. Is it required? */ + CIVETWEB_API int + mg_get_handler_info(struct mg_context *ctx, +- char *buffer, +- int buflen) ++ char *buffer, ++ int buflen) + { +- int handler_info_len = 0; +- struct mg_handler_info *tmp_rh; +- mg_lock_context(ctx); +- +- for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { +- +- if (buflen > handler_info_len+ tmp_rh->uri_len) { +- memcpy(buffer+handler_info_len, tmp_rh->uri, tmp_rh->uri_len); +- } +- handler_info_len += tmp_rh->uri_len; +- +- switch (tmp_rh->handler_type) { +- case REQUEST_HANDLER: +- (void)tmp_rh->handler; +- break; +- case WEBSOCKET_HANDLER: +- (void)tmp_rh->connect_handler; +- (void) tmp_rh->ready_handler; +- (void) tmp_rh->data_handler; +- (void) tmp_rh->close_handler; +- break; +- case AUTH_HANDLER: +- (void) tmp_rh->auth_handler; +- break; +- } +- (void)cbdata; +- } +- +- mg_unlock_context(ctx); +- return handler_info_len; ++ int handler_info_len = 0; ++ struct mg_handler_info *tmp_rh; ++ mg_lock_context(ctx); ++ ++ for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { ++ ++ if (buflen > handler_info_len + tmp_rh->uri_len) { ++ memcpy(buffer + handler_info_len, tmp_rh->uri, tmp_rh->uri_len); ++ } ++ handler_info_len += tmp_rh->uri_len; ++ ++ switch (tmp_rh->handler_type) { ++ case REQUEST_HANDLER: ++ (void)tmp_rh->handler; ++ break; ++ case WEBSOCKET_HANDLER: ++ (void)tmp_rh->connect_handler; ++ (void)tmp_rh->ready_handler; ++ (void)tmp_rh->data_handler; ++ (void)tmp_rh->close_handler; ++ break; ++ case AUTH_HANDLER: ++ (void)tmp_rh->auth_handler; ++ break; ++ } ++ (void)cbdata; ++ } ++ ++ mg_unlock_context(ctx); ++ return handler_info_len; + } + #endif + #endif +-- +2.34.1 + + +From 62781b775c5338cffd5f41bb29758f5efde6c116 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Mon, 1 May 2023 22:01:13 +0200 +Subject: [PATCH 011/173] Use 64 bit timer for 64 bit Windows + +--- + src/timer.inl | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/timer.inl b/src/timer.inl +index 9b8d5539..39d68dfc 100644 +--- a/src/timer.inl ++++ b/src/timer.inl +@@ -39,13 +39,16 @@ TIMER_API double + timer_getcurrenttime(struct mg_context *ctx) + { + #if defined(_WIN32) ++ uint64_t now_tick64 = 0; ++#if defined(_WIN64) ++ now_tick64 = GetTickCount64(); ++#else + /* GetTickCount returns milliseconds since system start as + * unsigned 32 bit value. It will wrap around every 49.7 days. + * We need to use a 64 bit counter (will wrap in 500 mio. years), + * by adding the 32 bit difference since the last call to a + * 64 bit counter. This algorithm will only work, if this + * function is called at least once every 7 weeks. */ +- uint64_t now_tick64 = 0; + DWORD now_tick = GetTickCount(); + + if (ctx->timers) { +@@ -55,6 +58,7 @@ timer_getcurrenttime(struct mg_context *ctx) + ctx->timers->last_tick = now_tick; + pthread_mutex_unlock(&ctx->timers->mutex); + } ++#endif + return (double)now_tick64 * 1.0E-3; + #else + struct timespec now_ts; +-- +2.34.1 + + +From 68479acb5fb8db378d9a5aefcb84d9c3c0403400 Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Fri, 5 May 2023 13:08:49 -0700 +Subject: [PATCH 012/173] fallback_document_root and fallback_websocket_root + initial implementation + +--- + docs/UserManual.md | 16 +++++++ + examples/https/civetweb.conf | 3 ++ + src/civetweb.c | 85 +++++++++++++++++++++--------------- + src/main.c | 3 ++ + src/mod_lua.inl | 8 ++++ + test/page3.ssjs | 2 + + unittest/private.c | 2 + + 7 files changed, 85 insertions(+), 34 deletions(-) + +diff --git a/docs/UserManual.md b/docs/UserManual.md +index df9b3685..358895f6 100644 +--- a/docs/UserManual.md ++++ b/docs/UserManual.md +@@ -296,6 +296,15 @@ The current directory is commonly referenced as dot (`.`). + It is recommended to use an absolute path for document\_root, in order to + avoid accidentally serving the wrong directory. + ++### fallback\_document\_root `.` ++An optional second directory to check for a file to serve, if the requested ++filename was not found in the document\_root directory. ++This can be useful in cases where an app ships with a read-only HTML content ++directory as part of its install, but you nevertheless want to allow the user ++to customize the served content by placing modified or additional files into ++a writable directory, where they will take precedence over their read-only ++counterparts, on a per-file basis. ++ + ### enable\_auth\_domain\_check `yes` + When using absolute URLs, verify the host is identical to the authentication\_domain. + If enabled, requests to absolute URLs will only be processed +@@ -806,6 +815,11 @@ be used for websockets as well. Since websockets use a different URL scheme + websockets may also be served from a different directory. By default, + the document\_root is used as websocket\_root as well. + ++### fallback\_websocket\_root ++An optional second directory to check for websocket-files that were ++not found in the websocket\_root directory. (See the documentation for ++fallback\_root for details) ++ + ### websocket\_timeout\_ms + Timeout for network read and network write operations for websockets, WS(S), + in milliseconds. If this value is not set, the value of request\_timeout\_ms +@@ -969,6 +983,7 @@ mg (table): + mg.onerror(msg) -- error handler, can be overridden + mg.auth_domain -- a string that holds the HTTP authentication domain + mg.document_root -- a string that holds the document root directory ++ mg.fallback_document_root -- a string that holds an optional second document root directory + mg.lua_type -- a string that holds the lua script type + mg.system -- a string that holds the operating system name + mg.version -- a string that holds CivetWeb version +@@ -1028,6 +1043,7 @@ If websocket and timers support is enabled then the following is also available: + mg.set_timeout(fn,delay,[interval]) -- call function after delay at an interval + mg.set_interval(fn,delay,[interval]) -- call function after delay at an interval + mg.websocket_root -- a string that holds the websocket root ++ mg.fallback_websocket_root -- a string that holds an optional second websocket root + + connect (function): + +diff --git a/examples/https/civetweb.conf b/examples/https/civetweb.conf +index c1ecc642..552a565f 100644 +--- a/examples/https/civetweb.conf ++++ b/examples/https/civetweb.conf +@@ -36,6 +36,9 @@ listening_ports 80r,443s + #document_root tdb + #authentication_domain mydomain.com + ++# Optional fallback document root, checked for file-paths not found in document_root ++#fallback_document_root tdb_fallback ++ + # Set the a certificate + ssl_certificate ../../resources/cert/server.pem + +diff --git a/src/civetweb.c b/src/civetweb.c +index fdea2db4..064aca29 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1967,6 +1967,7 @@ enum { + + /* Once for each domain */ + DOCUMENT_ROOT, ++ FALLBACK_DOCUMENT_ROOT, + + ACCESS_LOG_FILE, + ERROR_LOG_FILE, +@@ -2048,6 +2049,7 @@ enum { + + #if defined(USE_WEBSOCKET) + WEBSOCKET_ROOT, ++ FALLBACK_WEBSOCKET_ROOT, + #endif + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + LUA_WEBSOCKET_EXTENSIONS, +@@ -2111,6 +2113,7 @@ static const struct mg_option config_options[] = { + + /* Once for each domain */ + {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, ++ {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, + + {"access_log_file", MG_CONFIG_TYPE_FILE, NULL}, + {"error_log_file", MG_CONFIG_TYPE_FILE, NULL}, +@@ -2209,6 +2212,7 @@ static const struct mg_option config_options[] = { + + #if defined(USE_WEBSOCKET) + {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, ++ {"fallback_websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, + #endif + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, +@@ -7650,7 +7654,8 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ + + #if !defined(NO_FILES) + const char *uri = conn->request_info.local_uri; +- const char *root = conn->dom_ctx->config[DOCUMENT_ROOT]; ++ const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT], conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT], NULL}; ++ int fileExists = 0; + const char *rewrite; + struct vec a, b; + ptrdiff_t match_len; +@@ -7685,7 +7690,8 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ + *is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET); + #if !defined(NO_FILES) + if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) { +- root = conn->dom_ctx->config[WEBSOCKET_ROOT]; ++ roots[0] = conn->dom_ctx->config[WEBSOCKET_ROOT]; ++ roots[1] = conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]; + } + #endif /* !NO_FILES */ + #else /* USE_WEBSOCKET */ +@@ -7702,51 +7708,59 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ + + #if !defined(NO_FILES) + /* Step 5: If there is no root directory, don't look for files. */ +- /* Note that root == NULL is a regular use case here. This occurs, ++ /* Note that roots[0] == NULL is a regular use case here. This occurs, + * if all requests are handled by callbacks, so the WEBSOCKET_ROOT + * config is not required. */ +- if (root == NULL) { ++ if (roots[0] == NULL) { + /* all file related outputs have already been set to 0, just return + */ + return; + } + +- /* Step 6: Determine the local file path from the root path and the +- * request uri. */ +- /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift +- * part of the path one byte on the right. */ +- truncated = 0; +- mg_snprintf( +- conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri); ++ for (int i=0; roots[i] != NULL; i++) ++ { ++ /* Step 6: Determine the local file path from the root path and the ++ * request uri. */ ++ /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift ++ * part of the path one byte on the right. */ ++ truncated = 0; ++ mg_snprintf( ++ conn, &truncated, filename, filename_buf_len - 1, "%s%s", roots[i], uri); + +- if (truncated) { +- goto interpret_cleanup; +- } ++ if (truncated) { ++ goto interpret_cleanup; ++ } + +- /* Step 7: URI rewriting */ +- rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN]; +- while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { +- if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { +- mg_snprintf(conn, +- &truncated, +- filename, +- filename_buf_len - 1, +- "%.*s%s", +- (int)b.len, +- b.ptr, +- uri + match_len); +- break; ++ /* Step 7: URI rewriting */ ++ rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN]; ++ while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { ++ if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { ++ mg_snprintf(conn, ++ &truncated, ++ filename, ++ filename_buf_len - 1, ++ "%.*s%s", ++ (int)b.len, ++ b.ptr, ++ uri + match_len); ++ break; ++ } + } +- } + +- if (truncated) { +- goto interpret_cleanup; ++ if (truncated) { ++ goto interpret_cleanup; ++ } ++ ++ /* Step 8: Check if the file exists at the server */ ++ /* Local file path and name, corresponding to requested URI ++ * is now stored in "filename" variable. */ ++ if (mg_stat(conn, filename, filestat)) { ++ fileExists = 1; ++ break; ++ } + } + +- /* Step 8: Check if the file exists at the server */ +- /* Local file path and name, corresponding to requested URI +- * is now stored in "filename" variable. */ +- if (mg_stat(conn, filename, filestat)) { ++ if (fileExists) { + int uri_len = (int)strlen(uri); + int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/'); + +@@ -11404,6 +11418,9 @@ prepare_cgi_environment(struct mg_connection *conn, + addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); + addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); + addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); ++ if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { ++ addenv(env, "FALLBACK_DOCUMENT_ROOT=%s", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); ++ } + addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version()); + + /* Prepare the environment block */ +diff --git a/src/main.c b/src/main.c +index f056a954..f4d408e2 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -1142,6 +1142,7 @@ sanitize_options(const char *options[] /* server options */, + int ok = 1; + /* Make sure we have absolute paths for files and directories */ + set_absolute_path(options, "document_root", arg0); ++ set_absolute_path(options, "fallback_document_root", arg0); + set_absolute_path(options, "put_delete_auth_file", arg0); + set_absolute_path(options, "cgi_interpreter", arg0); + set_absolute_path(options, "access_log_file", arg0); +@@ -1155,6 +1156,8 @@ sanitize_options(const char *options[] /* server options */, + /* Make extra verification for certain options */ + if (!verify_existence(options, "document_root", 1)) + ok = 0; ++ if (!verify_existence(options, "fallback_document_root", 1)) ++ ok = 0; + if (!verify_existence(options, "cgi_interpreter", 0)) + ok = 0; + if (!verify_existence(options, "ssl_certificate", 0)) +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index d280e897..ec7611c0 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -2935,6 +2935,9 @@ prepare_lua_environment(struct mg_context *ctx, + + if ((conn != NULL) && (conn->dom_ctx != NULL)) { + reg_string(L, "document_root", conn->dom_ctx->config[DOCUMENT_ROOT]); ++ if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { ++ reg_string(L, "fallback_document_root", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); ++ } + reg_string(L, + "auth_domain", + conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); +@@ -2943,6 +2946,11 @@ prepare_lua_environment(struct mg_context *ctx, + reg_string(L, + "websocket_root", + conn->dom_ctx->config[WEBSOCKET_ROOT]); ++ if (conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]) { ++ reg_string(L, ++ "fallback_websocket_root", ++ conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]); ++ } + } else { + reg_string(L, + "websocket_root", +diff --git a/test/page3.ssjs b/test/page3.ssjs +index 71e55e34..24537998 100644 +--- a/test/page3.ssjs ++++ b/test/page3.ssjs +@@ -19,6 +19,7 @@ opts = [ + "extra_mime_types", + "listening_ports", + "document_root", ++"fallback_document_root", + "ssl_certificate", + "num_threads", + "run_as_user", +@@ -32,6 +33,7 @@ opts = [ + "lua_server_page_pattern", + "_experimental_duktape_script_pattern", + "websocket_root", ++"fallback_websocket_root", + "lua_websocket_pattern", + "access_control_allow_origin", + "error_pages", +diff --git a/unittest/private.c b/unittest/private.c +index 8e85d71e..8ea35452 100644 +--- a/unittest/private.c ++++ b/unittest/private.c +@@ -1621,6 +1621,7 @@ START_TEST(test_config_options) + ck_assert_str_eq("extra_mime_types", config_options[EXTRA_MIME_TYPES].name); + ck_assert_str_eq("listening_ports", config_options[LISTENING_PORTS].name); + ck_assert_str_eq("document_root", config_options[DOCUMENT_ROOT].name); ++ ck_assert_str_eq("fallback_document_root", config_options[FALLBACK_DOCUMENT_ROOT].name); + ck_assert_str_eq("ssl_certificate", config_options[SSL_CERTIFICATE].name); + ck_assert_str_eq("ssl_certificate_chain", + config_options[SSL_CERTIFICATE_CHAIN].name); +@@ -1672,6 +1673,7 @@ START_TEST(test_config_options) + #endif + #if defined(USE_WEBSOCKET) + ck_assert_str_eq("websocket_root", config_options[WEBSOCKET_ROOT].name); ++ ck_assert_str_eq("fallback_websocket_root", config_options[FALLBACK_WEBSOCKET_ROOT].name); + #endif + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + ck_assert_str_eq("lua_websocket_pattern", +-- +2.34.1 + + +From c7fb4dab4a9f1dca91558b5a0867975daeeefdb1 Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Fri, 5 May 2023 18:46:44 -0700 +Subject: [PATCH 013/173] Speed up mg_stop() by using a socket-pair to cause + all mg_poll() calls to return immediately on server shutdown + +--- + src/civetweb.c | 147 ++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 128 insertions(+), 19 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index fdea2db4..7575cba8 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -2438,6 +2438,9 @@ struct mg_context { + int lua_bg_log_available; /* Use Lua background state for access log */ + #endif + ++ int user_shutdown_notification_socket; /* mg_stop() will close this socket... */ ++ int thread_shutdown_notification_socket; /* to cause poll() in all threads to return immediately */ ++ + /* Server nonce */ + pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers, + * ssl_cert_last_mtime, nonce_count, and +@@ -6143,12 +6146,15 @@ push_inner(struct mg_context *ctx, + mg_sleep(5); + } else { + /* For sockets, wait for the socket using poll */ +- struct mg_pollfd pfd[1]; ++ struct mg_pollfd pfd[2]; + int pollres; + + pfd[0].fd = sock; + pfd[0].events = POLLOUT; +- pollres = mg_poll(pfd, 1, (int)(ms_wait), &(ctx->stop_flag)); ++ ++ pfd[1].fd = ctx->thread_shutdown_notification_socket; ++ pfd[1].events = POLLIN; ++ pollres = mg_poll(pfd, 2, (int)(ms_wait), &(ctx->stop_flag)); + if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) { + return -2; + } +@@ -6256,7 +6262,7 @@ pull_inner(FILE *fp, + + #if defined(USE_MBEDTLS) + } else if (conn->ssl != NULL) { +- struct mg_pollfd pfd[1]; ++ struct mg_pollfd pfd[2]; + int to_read; + int pollres; + +@@ -6274,10 +6280,13 @@ pull_inner(FILE *fp, + pfd[0].fd = conn->client.sock; + pfd[0].events = POLLIN; + ++ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[1].events = POLLIN; ++ + to_read = len; + + pollres = mg_poll(pfd, +- 1, ++ 2, + (int)(timeout * 1000.0), + &(conn->phys_ctx->stop_flag)); + +@@ -6312,7 +6321,7 @@ pull_inner(FILE *fp, + #elif !defined(NO_SSL) + } else if (conn->ssl != NULL) { + int ssl_pending; +- struct mg_pollfd pfd[1]; ++ struct mg_pollfd pfd[2]; + int pollres; + + if ((ssl_pending = SSL_pending(conn->ssl)) > 0) { +@@ -6326,8 +6335,11 @@ pull_inner(FILE *fp, + } else { + pfd[0].fd = conn->client.sock; + pfd[0].events = POLLIN; ++ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[1].events = POLLIN; ++ + pollres = mg_poll(pfd, +- 1, ++ 2, + (int)(timeout * 1000.0), + &(conn->phys_ctx->stop_flag)); + if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { +@@ -6365,13 +6377,17 @@ pull_inner(FILE *fp, + #endif + + } else { +- struct mg_pollfd pfd[1]; ++ struct mg_pollfd pfd[2]; + int pollres; + + pfd[0].fd = conn->client.sock; + pfd[0].events = POLLIN; ++ ++ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[1].events = POLLIN; ++ + pollres = mg_poll(pfd, +- 1, ++ 2, + (int)(timeout * 1000.0), + &(conn->phys_ctx->stop_flag)); + if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { +@@ -9526,7 +9542,7 @@ connect_socket( + #endif + + /* Data for poll */ +- struct mg_pollfd pfd[1]; ++ struct mg_pollfd pfd[2]; + int pollres; + int ms_wait = 10000; /* 10 second timeout */ + stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */ +@@ -9538,7 +9554,11 @@ connect_socket( + */ + pfd[0].fd = *sock; + pfd[0].events = POLLOUT; +- pollres = mg_poll(pfd, 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop); ++ ++ pfd[1].fd = ctx ? ctx->thread_shutdown_notification_socket : -1; ++ pfd[1].events = POLLIN; ++ ++ pollres = mg_poll(pfd, ctx ? 2 : 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop); + + if (pollres != 1) { + /* Not connected */ +@@ -16028,9 +16048,13 @@ set_ports_option(struct mg_context *phys_ctx) + continue; + } + ++ /* The +2 below includes the original +1 (for the socket we're about to add), ++ * plus another +1 for the thread_shutdown_notification_socket that we'll ++ * also want to poll() on so that mg_stop() can return quickly ++ */ + if ((pfd = (struct mg_pollfd *) + mg_realloc_ctx(phys_ctx->listening_socket_fds, +- (phys_ctx->num_listening_sockets + 1) ++ (phys_ctx->num_listening_sockets + 2) + * sizeof(phys_ctx->listening_socket_fds[0]), + phys_ctx)) + == NULL) { +@@ -16552,15 +16576,18 @@ sslize(struct mg_connection *conn, + /* Need to retry the function call "later". + * See https://linux.die.net/man/3/ssl_get_error + * This is typical for non-blocking sockets. */ +- struct mg_pollfd pfd; ++ struct mg_pollfd pfd[2]; + int pollres; +- pfd.fd = conn->client.sock; +- pfd.events = ((err == SSL_ERROR_WANT_CONNECT) +- || (err == SSL_ERROR_WANT_WRITE)) +- ? POLLOUT +- : POLLIN; ++ pfd[0].fd = conn->client.sock; ++ pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT) ++ || (err == SSL_ERROR_WANT_WRITE)) ++ ? POLLOUT ++ : POLLIN; ++ ++ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[1].events = POLLIN; + pollres = +- mg_poll(&pfd, 1, 50, &(conn->phys_ctx->stop_flag)); ++ mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag)); + if (pollres < 0) { + /* Break if error occurred (-1) + * or server shutdown (-2) */ +@@ -20144,8 +20171,14 @@ master_thread_run(struct mg_context *ctx) + pfd[i].events = POLLIN; + } + ++ /* We listen on this socket just so that mg_stop() can cause mg_poll() to return ASAP. ++ * Don't worry, we did allocate an extra slot at the end of listening_socket_fds[] just to hold this ++ */ ++ pfd[ctx->num_listening_sockets].fd = ctx->thread_shutdown_notification_socket; ++ pfd[ctx->num_listening_sockets].events = POLLIN; ++ + if (mg_poll(pfd, +- ctx->num_listening_sockets, ++ ctx->num_listening_sockets+1, // +1 for the thread_shutdown_notification_socket + SOCKET_TIMEOUT_QUANTUM, + &(ctx->stop_flag)) + > 0) { +@@ -20303,6 +20336,14 @@ free_context(struct mg_context *ctx) + (void)pthread_mutex_destroy(&ctx->lua_bg_mutex); + #endif + ++ /* Deallocate shutdown-triggering socket-pair */ ++ if (ctx->user_shutdown_notification_socket >= 0) { ++ closesocket(ctx->user_shutdown_notification_socket); ++ } ++ if (ctx->thread_shutdown_notification_socket >= 0) { ++ closesocket(ctx->thread_shutdown_notification_socket); ++ } ++ + /* Deallocate config parameters */ + for (i = 0; i < NUM_OPTIONS; i++) { + if (ctx->dd.config[i] != NULL) { +@@ -20379,6 +20420,10 @@ mg_stop(struct mg_context *ctx) + /* Set stop flag, so all threads know they have to exit. */ + STOP_FLAG_ASSIGN(&ctx->stop_flag, 1); + ++ /* Closing this socket will cause mg_poll() in all the I/O threads to return immediately */ ++ closesocket(ctx->user_shutdown_notification_socket); ++ ctx->user_shutdown_notification_socket = -1; /* to avoid calling closesocket() again in free_context() */ ++ + /* Join timer thread */ + #if defined(USE_TIMERS) + timers_exit(ctx); +@@ -20476,6 +20521,64 @@ legacy_init(const char **options) + } + } + ++/* we'll assume it's only Windows that doesn't have socketpair() available */ ++#if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32) ++# define HAVE_SOCKETPAIR 1 ++#endif ++ ++static int ++mg_socketpair(int * sockA, ++ int * sockB) ++{ ++ int temp[2] = {-1, -1}; ++ ++ /** Default to unallocated */ ++ *sockA = -1; ++ *sockB = -1; ++ ++#if defined(HAVE_SOCKETPAIR) ++ int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, temp); ++ if (ret == 0) { ++ *sockA = temp[0]; ++ *sockB = temp[1]; ++ } ++ return ret; ++#else ++ /** No socketpair() call is available, so we'll have to roll our own implementation */ ++ int asock = socket(PF_INET, SOCK_STREAM, 0); ++ if (asock >= 0) { ++ struct sockaddr_in addr; ++ struct sockaddr * pa = (struct sockaddr *) &addr; ++ socklen_t addrLen = sizeof(addr); ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.sin_family = AF_INET; ++ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); ++ addr.sin_port = 0; ++ ++ if ((bind(asock, pa, sizeof(addr)) == 0) ++ &&(getsockname(asock, pa, &addrLen) == 0) ++ &&(listen(asock, 1) == 0)) { ++ temp[0] = socket(PF_INET, SOCK_STREAM, 0); ++ if ((temp[0] >= 0)&&(connect(temp[0], pa, sizeof(addr)) == 0)) { ++ temp[1] = accept(asock, pa, &addrLen); ++ if (temp[1] >= 0) { ++ closesocket(asock); ++ *sockA = temp[0]; ++ *sockB = temp[1]; ++ return 0; /* success! */ ++ } ++ } ++ } ++ } ++ ++ /* Cleanup */ ++ if (asock >= 0) closesocket(asock); ++ if (temp[0] >= 0) closesocket(temp[0]); ++ if (temp[1] >= 0) closesocket(temp[1]); ++ return -1; /* fail! */ ++#endif ++} + + CIVETWEB_API struct mg_context * + mg_start2(struct mg_init_data *init, struct mg_error_data *error) +@@ -20559,6 +20662,12 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + #if defined(USE_LUA) + ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr)); + #endif ++ ++ /** mg_stop() will close the user_shutdown_notification_socket, and that will cause poll() ++ * to return immediately in the master-thread, so that mg_stop() can also return immediately. ++ */ ++ ok &= (0 == mg_socketpair(&ctx->user_shutdown_notification_socket, &ctx->thread_shutdown_notification_socket)); ++ + if (!ok) { + unsigned error_id = (unsigned)ERRNO; + const char *err_msg = +-- +2.34.1 + + +From 91dae7a6755babd4e102207ba63d7dc92c0df905 Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Fri, 5 May 2023 21:51:01 -0700 +Subject: [PATCH 014/173] Added set_close_on_exec() calls inside + mg_socketpair(), to avoid potential issues with child processes holding the + socketpair open via implicitly duplicated file descriptors + +--- + src/civetweb.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 7575cba8..baaa017f 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -20541,6 +20541,8 @@ mg_socketpair(int * sockA, + if (ret == 0) { + *sockA = temp[0]; + *sockB = temp[1]; ++ set_close_on_exec(*sockA, NULL, NULL); ++ set_close_on_exec(*sockB, NULL, NULL); + } + return ret; + #else +@@ -20566,6 +20568,8 @@ mg_socketpair(int * sockA, + closesocket(asock); + *sockA = temp[0]; + *sockB = temp[1]; ++ set_close_on_exec(*sockA, NULL, NULL); ++ set_close_on_exec(*sockB, NULL, NULL); + return 0; /* success! */ + } + } +-- +2.34.1 + + +From 9859676ac9ace1db7e4add044493d306312e7d5b Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Tue, 9 May 2023 18:00:55 -0700 +Subject: [PATCH 015/173] Updated substitute_index_file() to look in the + fallback-root, if no index.html substitution can be found in the regular + document-root + +--- + src/civetweb.c | 40 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 39 insertions(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 064aca29..6ac2e3b4 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -7590,7 +7590,7 @@ extention_matches_template_text( + * Return 1 if index file has been found, 0 if not found. + * If the file is found, it's stats is returned in stp. */ + static int +-substitute_index_file(struct mg_connection *conn, ++substitute_index_file_aux(struct mg_connection *conn, + char *path, + size_t path_len, + struct mg_file_stat *filestat) +@@ -7634,6 +7634,44 @@ substitute_index_file(struct mg_connection *conn, + + return found; + } ++ ++/* Same as above, except if the first try fails and a fallback-root is configured, we'll try there also */ ++static int ++substitute_index_file(struct mg_connection *conn, ++ char *path, ++ size_t path_len, ++ struct mg_file_stat *filestat) ++{ ++ int ret = substitute_index_file_aux(conn, path, path_len, filestat); ++ if (ret == 0) { ++ const char * root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT]; ++ const char * fallback_root_prefix = conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]; ++ if ((root_prefix)&&(fallback_root_prefix)) { ++ const size_t root_prefix_len = strlen(root_prefix); ++ if ((strncmp(path, root_prefix, root_prefix_len) == 0)) { ++ const size_t fallback_root_prefix_len = strlen(fallback_root_prefix); ++ const char * sub_path = path+root_prefix_len; ++ while(*sub_path == '/') sub_path++; ++ const size_t sub_path_len = strlen(sub_path); ++ ++ char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid side effects if we fail */ ++ if (((fallback_root_prefix_len + 1 + sub_path_len + 1) < sizeof(scratch_path))) { ++ /* The concatenations below are all safe because we pre-verified string lengths above */ ++ strcpy(scratch_path, fallback_root_prefix); ++ char * nul = strchr(scratch_path, '\0'); ++ if ((nul > scratch_path)&&(*(nul-1) != '/')) {*nul++ = '/'; *nul = '\0';} ++ strcat(scratch_path, sub_path); ++ if (substitute_index_file_aux(conn, scratch_path, sizeof(scratch_path), filestat)) { ++ mg_strlcpy(path, scratch_path, path_len); ++ return 1; ++ } ++ } ++ } ++ } ++ } ++ return ret; ++} ++ + #endif + + +-- +2.34.1 + + +From 220a55ecaeddae5df23872ebf26b20ff9f32c644 Mon Sep 17 00:00:00 2001 +From: Mingjie Shen +Date: Sat, 13 May 2023 16:05:54 -0400 +Subject: [PATCH 016/173] Fix offset used before range check + +These uses of offset 'i_pat' should follow the range check. +--- + src/match.inl | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/match.inl b/src/match.inl +index a5011f57..9c2765d7 100644 +--- a/src/match.inl ++++ b/src/match.inl +@@ -47,8 +47,8 @@ mg_match_impl(const char *pat, + /* Advance as long as there are ? */ + i_pat++; + i_str++; +- } while ((pat[i_pat] == '?') && (str[i_str] != '\0') +- && (str[i_str] != '/') && (i_pat < pat_len)); ++ } while ((i_pat < pat_len) && (pat[i_pat] == '?') ++ && (str[i_str] != '\0') && (str[i_str] != '/')); + + /* If we have a match context, add the substring we just found */ + if (mcx) { +@@ -72,7 +72,7 @@ mg_match_impl(const char *pat, + ptrdiff_t ret; + + i_pat++; +- if ((pat[i_pat] == '*') && (i_pat < pat_len)) { ++ if ((i_pat < pat_len) && (pat[i_pat] == '*')) { + /* Pattern ** matches all */ + i_pat++; + len = strlen(str + i_str); +-- +2.34.1 + + +From 4f89995634b9a116857604e32ac71d7070a39dae Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Sat, 13 May 2023 18:40:46 -0700 +Subject: [PATCH 017/173] Added demand-spawning of threads, and + prespawn_threads config argument + +--- + docs/Embedding.md | 11 +++- + docs/UserManual.md | 17 ++++-- + resources/civetweb.conf | 1 + + src/civetweb.c | 125 +++++++++++++++++++++++++++++----------- + test/page3.ssjs | 1 + + unittest/private.c | 1 + + 6 files changed, 114 insertions(+), 42 deletions(-) + +diff --git a/docs/Embedding.md b/docs/Embedding.md +index 0a0fa336..0291ff92 100644 +--- a/docs/Embedding.md ++++ b/docs/Embedding.md +@@ -213,10 +213,15 @@ about web server instance: + + When `mg_start()` returns, all initialization is guaranteed to be complete + (e.g. listening ports are opened, SSL is initialized, etc). `mg_start()` starts +-some threads: a master thread, that accepts new connections, and several +-worker threads, that process accepted connections. The number of worker threads +-is configurable via `num_threads` configuration option. That number puts a ++some threads: a master thread, that accepts new connections, and optionally some ++worker threads, that process accepted connections. The maximum number of worker ++threads is configurable via `num_threads` configuration option. That number puts a + limit on number of simultaneous requests that can be handled by CivetWeb. ++ ++The number of worker threads to be pre-spawned at startup is specified via the ++'prespawn_threads' configuration option; worker threads that are not pre-spawned ++will instead be demand-created the first time they are needed. ++ + If you embed CivetWeb into a program that uses SSL outside CivetWeb as well, + you may need to initialize SSL before calling `mg_start()`, and set the pre- + processor define `SSL_ALREADY_INITIALIZED`. This is not required if SSL is +diff --git a/docs/UserManual.md b/docs/UserManual.md +index 358895f6..e90ceef4 100644 +--- a/docs/UserManual.md ++++ b/docs/UserManual.md +@@ -558,8 +558,8 @@ The configuration value is approximate, the real limit might be a few bytes off. + The minimum is 1024 (1 kB). + + ### num\_threads `50` +-Number of worker threads. CivetWeb handles each incoming connection in a +-separate thread. Therefore, the value of this option is effectively the number ++Maximum number of worker threads allowed. CivetWeb handles each incoming connection ++in a separate thread. Therefore, the value of this option is effectively the number + of concurrent HTTP connections CivetWeb can handle. + + If there are more simultaneous requests (connection attempts), they are queued. +@@ -572,6 +572,15 @@ In case the clients are web browsers, it is recommended to use `num_threads` of + at least 5, since browsers often establish multiple connections to load a single + web page, including all linked documents (CSS, JavaScript, images, ...). + ++### prespawn\_threads '0' ++Number of worker threads that should be pre-spawned by mg_start(). Defaults to ++0, meaning no worker threads will be pre-spawned at startup; rather, worker threads ++will be spawned when a new connection comes in and there aren't currently any ++idle worker threads available to handle it (if we haven't already reached the ++maximum worker-thread count as specified by num_threads). If this value is ++specified less than zero, or greater than the value of num_threads, it will be ++treated as if it was specified to be equal to the value of num_threads. ++ + ### listen\_backlog `200` + Maximum number of connections waiting to be accepted by the server operating system. + Internally, this parameter is passed to the "listen" socket/system call. +@@ -876,8 +885,8 @@ All port, socket, process and thread specific parameters are per server: + `enable_http2`, `enable_keep_alive`, `enable_websocket_ping_pong`, + `keep_alive_timeout_ms`, `linger_timeout_ms`, `listen_backlog`, + `listening_ports`, `lua_background_script`, `lua_background_script_params`, +-`max_request_size`, `num_threads`, `request_timeout_ms`, `run_as_user`, +-`tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`. ++`max_request_size`, `num_threads`, 'prespawn_threads', `request_timeout_ms`, ++`run_as_user`, `tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`. + + All other options can be set per domain. In particular + `authentication_domain`, `document_root` and (for HTTPS) `ssl_certificate` +diff --git a/resources/civetweb.conf b/resources/civetweb.conf +index c5675481..293f27b5 100644 +--- a/resources/civetweb.conf ++++ b/resources/civetweb.conf +@@ -26,6 +26,7 @@ listening_ports 8080 + # extra_mime_types + # ssl_certificate + # num_threads 50 ++# prespawn_threads 0 + # run_as_user + # url_rewrite_patterns + # hide_files_patterns +diff --git a/src/civetweb.c b/src/civetweb.c +index 064aca29..88883aee 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1934,6 +1934,7 @@ enum { + /* Once for each server */ + LISTENING_PORTS, + NUM_THREADS, ++ PRESPAWN_THREADS, + RUN_AS_USER, + CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the + * socket option typedef TCP_NODELAY. */ +@@ -2081,6 +2082,7 @@ static const struct mg_option config_options[] = { + /* Once for each server */ + {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"}, + {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"}, ++ {"prespawn_threads", MG_CONFIG_TYPE_NUMBER, "0"}, + {"run_as_user", MG_CONFIG_TYPE_STRING, NULL}, + {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"}, + {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"}, +@@ -2394,7 +2396,14 @@ struct mg_context { + + pthread_t masterthreadid; /* The master thread ID */ + unsigned int +- cfg_worker_threads; /* The number of configured worker threads. */ ++ cfg_max_worker_threads; /* How many worker-threads we are allowed to create, total */ ++ ++ unsigned int ++ spawned_worker_threads; /* How many worker-threads currently exist (modified by master thread) */ ++ unsigned int ++ idle_worker_thread_count; /* How many worker-threads are currently sitting around with nothing to do */ ++ /* Access to this value MUST be synchronized by thread_mutex */ ++ + pthread_t *worker_threadids; /* The worker thread IDs */ + unsigned long starter_thread_idx; /* thread index which called mg_start */ + +@@ -17997,7 +18006,7 @@ mg_close_connection(struct mg_connection *conn) + * timeouts, we will just wait a few seconds in mg_join_thread. */ + + /* join worker thread */ +- for (i = 0; i < conn->phys_ctx->cfg_worker_threads; i++) { ++ for (i = 0; i < conn->phys_ctx->spawned_worker_threads; i++) { + mg_join_thread(conn->phys_ctx->worker_threadids[i]); + } + } +@@ -19207,7 +19216,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options, + /* Now upgrade to ws/wss client context */ + conn->phys_ctx->user_data = user_data; + conn->phys_ctx->context_type = CONTEXT_WS_CLIENT; +- conn->phys_ctx->cfg_worker_threads = 1; /* one worker thread */ ++ conn->phys_ctx->cfg_max_worker_threads = 1; /* one worker thread */ ++ conn->phys_ctx->spawned_worker_threads = 1; /* one worker thread */ + + /* Start a thread to read the websocket client connection + * This thread will automatically stop when mg_disconnect is +@@ -19216,7 +19226,7 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options, + thread_data, + conn->phys_ctx->worker_threadids) + != 0) { +- conn->phys_ctx->cfg_worker_threads = 0; ++ conn->phys_ctx->spawned_worker_threads = 0; + mg_free(thread_data); + mg_close_connection(conn); + conn = NULL; +@@ -19587,6 +19597,7 @@ process_new_connection(struct mg_connection *conn) + #endif + } + ++static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads); /* forward declaration */ + + #if defined(ALTERNATIVE_QUEUE) + +@@ -19595,8 +19606,10 @@ produce_socket(struct mg_context *ctx, const struct socket *sp) + { + unsigned int i; + ++ (void)mg_start_worker_thread(ctx, 1); /* will start a worker-thread only if there aren't currently any idle worker-threads */ ++ + while (!ctx->stop_flag) { +- for (i = 0; i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; i < ctx->spawned_worker_threads; i++) { + /* find a free worker slot and signal it */ + if (ctx->client_socks[i].in_use == 2) { + (void)pthread_mutex_lock(&ctx->thread_mutex); +@@ -19621,10 +19634,13 @@ produce_socket(struct mg_context *ctx, const struct socket *sp) + + + static int +-consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) ++consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented) + { + DEBUG_TRACE("%s", "going idle"); + (void)pthread_mutex_lock(&ctx->thread_mutex); ++ if (counter_was_preincremented == 0) { /* first call only: the master-thread pre-incremented this before he spawned us */ ++ ctx->idle_worker_thread_count++; ++ } + ctx->client_socks[thread_index].in_use = 2; + (void)pthread_mutex_unlock(&ctx->thread_mutex); + +@@ -19641,6 +19657,7 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) + } + return 0; + } ++ ctx->idle_worker_thread_count--; + (void)pthread_mutex_unlock(&ctx->thread_mutex); + if (sp->in_use == 1) { + DEBUG_TRACE("grabbed socket %d, going busy", sp->sock); +@@ -19655,12 +19672,15 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) + + /* Worker threads take accepted socket from the queue */ + static int +-consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) ++consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented) + { + (void)thread_index; + +- (void)pthread_mutex_lock(&ctx->thread_mutex); + DEBUG_TRACE("%s", "going idle"); ++ (void)pthread_mutex_lock(&ctx->thread_mutex); ++ if (counter_was_preincremented == 0) { /* first call only: the master-thread pre-incremented this before he spawned us */ ++ ctx->idle_worker_thread_count++; ++ } + + /* If the queue is empty, wait. We're idle at this point. */ + while ((ctx->sq_head == ctx->sq_tail) +@@ -19684,6 +19704,8 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) + } + + (void)pthread_cond_signal(&ctx->sq_empty); ++ ++ ctx->idle_worker_thread_count--; + (void)pthread_mutex_unlock(&ctx->thread_mutex); + + return STOP_FLAG_IS_ZERO(&ctx->stop_flag); +@@ -19730,6 +19752,8 @@ produce_socket(struct mg_context *ctx, const struct socket *sp) + + (void)pthread_cond_signal(&ctx->sq_full); + (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ ++ (void)mg_start_worker_thread(ctx, 1); /* will start a worker-thread only if there aren't currently any idle worker-threads */ + } + #endif /* ALTERNATIVE_QUEUE */ + +@@ -19740,6 +19764,7 @@ worker_thread_run(struct mg_connection *conn) + struct mg_context *ctx = conn->phys_ctx; + int thread_index; + struct mg_workerTLS tls; ++ int first_call_to_consume_socket = 1; + + mg_set_thread_name("worker"); + +@@ -19765,7 +19790,7 @@ worker_thread_run(struct mg_connection *conn) + /* Connection structure has been pre-allocated */ + thread_index = (int)(conn - ctx->worker_connections); + if ((thread_index < 0) +- || ((unsigned)thread_index >= (unsigned)ctx->cfg_worker_threads)) { ++ || ((unsigned)thread_index >= (unsigned)ctx->cfg_max_worker_threads)) { + mg_cry_ctx_internal(ctx, + "Internal error: Invalid worker index %i", + thread_index); +@@ -19806,7 +19831,8 @@ worker_thread_run(struct mg_connection *conn) + /* Call consume_socket() even when ctx->stop_flag > 0, to let it + * signal sq_empty condvar to wake up the master waiting in + * produce_socket() */ +- while (consume_socket(ctx, &conn->client, thread_index)) { ++ while (consume_socket(ctx, &conn->client, thread_index, first_call_to_consume_socket)) { ++ first_call_to_consume_socket = 0; + + /* New connections must start with new protocol negotiation */ + tls.alpn_proto = NULL; +@@ -20188,7 +20214,7 @@ master_thread_run(struct mg_context *ctx) + + /* Wakeup workers that are waiting for connections to handle. */ + #if defined(ALTERNATIVE_QUEUE) +- for (i = 0; i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; i < ctx->spawned_worker_threads; i++) { + event_signal(ctx->client_wait_events[i]); + } + #else +@@ -20198,7 +20224,7 @@ master_thread_run(struct mg_context *ctx) + #endif + + /* Join all worker threads to avoid leaking threads. */ +- workerthreadcount = ctx->cfg_worker_threads; ++ workerthreadcount = ctx->spawned_worker_threads; + for (i = 0; i < workerthreadcount; i++) { + if (ctx->worker_threadids[i] != 0) { + mg_join_thread(ctx->worker_threadids[i]); +@@ -20302,7 +20328,7 @@ free_context(struct mg_context *ctx) + #if defined(ALTERNATIVE_QUEUE) + mg_free(ctx->client_socks); + if (ctx->client_wait_events != NULL) { +- for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; (unsigned)i < ctx->spawned_worker_threads; i++) { + event_destroy(ctx->client_wait_events[i]); + } + mg_free(ctx->client_wait_events); +@@ -20494,12 +20520,41 @@ legacy_init(const char **options) + } + + ++static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads) { ++ const unsigned int i = ctx->spawned_worker_threads; ++ if (i >= ctx->cfg_max_worker_threads) { ++ return -1; /* Oops, we hit our worker-thread limit! No more worker threads, ever! */ ++ } ++ ++ (void)pthread_mutex_lock(&ctx->thread_mutex); ++ if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) { ++ (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ return -2; /* There are idle threads available, so no need to spawn a new worker thread now */ ++ } ++ ctx->idle_worker_thread_count++; /* we do this here to avoid a race condition while the thread is starting up */ ++ (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ ++ ctx->worker_connections[i].phys_ctx = ctx; ++ int ret = mg_start_thread_with_id(worker_thread, ++ &ctx->worker_connections[i], ++ &ctx->worker_threadids[i]); ++ if (ret == 0) { ++ ctx->spawned_worker_threads++; /* note that we've filled another slot in the table */ ++ DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads); ++ } else { ++ (void)pthread_mutex_lock(&ctx->thread_mutex); ++ ctx->idle_worker_thread_count--; /* whoops, roll-back on error */ ++ (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ } ++ return ret; ++} ++ + CIVETWEB_API struct mg_context * + mg_start2(struct mg_init_data *init, struct mg_error_data *error) + { + struct mg_context *ctx; + const char *name, *value, *default_value; +- int idx, ok, workerthreadcount; ++ int idx, ok, prespawnthreadcount, workerthreadcount; + unsigned int i; + int itmp; + void (*exit_callback)(const struct mg_context *ctx) = 0; +@@ -20742,7 +20797,12 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + #endif + + /* Worker thread count option */ +- workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]); ++ workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]); ++ prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]); ++ ++ if ((prespawnthreadcount < 0)||(prespawnthreadcount > workerthreadcount)) { ++ prespawnthreadcount = workerthreadcount; /* can't prespawn more than all of them! */ ++ } + + if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) { + if (workerthreadcount <= 0) { +@@ -21007,8 +21067,8 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + return NULL; + } + +- ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount)); +- ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_worker_threads, ++ ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount)); ++ ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads, + sizeof(pthread_t), + ctx); + +@@ -21019,7 +21079,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; + error->code_sub = +- (unsigned)ctx->cfg_worker_threads * (unsigned)sizeof(pthread_t); ++ (unsigned)ctx->cfg_max_worker_threads * (unsigned)sizeof(pthread_t); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ + error->text, +@@ -21033,7 +21093,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + return NULL; + } + ctx->worker_connections = +- (struct mg_connection *)mg_calloc_ctx(ctx->cfg_worker_threads, ++ (struct mg_connection *)mg_calloc_ctx(ctx->cfg_max_worker_threads, + sizeof(struct mg_connection), + ctx); + if (ctx->worker_connections == NULL) { +@@ -21043,7 +21103,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; +- error->code_sub = (unsigned)ctx->cfg_worker_threads ++ error->code_sub = (unsigned)ctx->cfg_max_worker_threads + * (unsigned)sizeof(struct mg_connection); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ +@@ -21060,7 +21120,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + + #if defined(ALTERNATIVE_QUEUE) + ctx->client_wait_events = +- (void **)mg_calloc_ctx(ctx->cfg_worker_threads, ++ (void **)mg_calloc_ctx(ctx->cfg_max_worker_threads, + sizeof(ctx->client_wait_events[0]), + ctx); + if (ctx->client_wait_events == NULL) { +@@ -21070,7 +21130,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; +- error->code_sub = (unsigned)ctx->cfg_worker_threads ++ error->code_sub = (unsigned)ctx->cfg_max_worker_threads + * (unsigned)sizeof(ctx->client_wait_events[0]); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ +@@ -21086,7 +21146,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + } + + ctx->client_socks = +- (struct socket *)mg_calloc_ctx(ctx->cfg_worker_threads, ++ (struct socket *)mg_calloc_ctx(ctx->cfg_max_worker_threads, + sizeof(ctx->client_socks[0]), + ctx); + if (ctx->client_socks == NULL) { +@@ -21097,7 +21157,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; +- error->code_sub = (unsigned)ctx->cfg_worker_threads ++ error->code_sub = (unsigned)ctx->cfg_max_worker_threads + * (unsigned)sizeof(ctx->client_socks[0]); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ +@@ -21112,7 +21172,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + return NULL; + } + +- for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) { + ctx->client_wait_events[i] = event_create(); + if (ctx->client_wait_events[i] == 0) { + const char *err_msg = "Error creating worker event %i"; +@@ -21176,23 +21236,18 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + ctx->context_type = CONTEXT_SERVER; /* server context */ + + /* Start worker threads */ +- for (i = 0; i < ctx->cfg_worker_threads; i++) { ++ for (int i = 0; i < prespawnthreadcount; i++) { + /* worker_thread sets up the other fields */ +- ctx->worker_connections[i].phys_ctx = ctx; +- if (mg_start_thread_with_id(worker_thread, +- &ctx->worker_connections[i], +- &ctx->worker_threadids[i]) +- != 0) { +- ++ if (mg_start_worker_thread(ctx, 0) != 0) { + long error_no = (long)ERRNO; + + /* thread was not created */ +- if (i > 0) { ++ if (ctx->spawned_worker_threads > 0) { + /* If the second, third, ... thread cannot be created, set a + * warning, but keep running. */ + mg_cry_ctx_internal(ctx, + "Cannot start worker thread %i: error %ld", +- i + 1, ++ ctx->spawned_worker_threads + 1, + error_no); + + /* If the server initialization should stop here, all +@@ -22122,7 +22177,7 @@ mg_get_connection_info(const struct mg_context *ctx, + return 0; + } + +- if ((unsigned)idx >= ctx->cfg_worker_threads) { ++ if ((unsigned)idx >= ctx->cfg_max_worker_threads) { + /* Out of range */ + return 0; + } +diff --git a/test/page3.ssjs b/test/page3.ssjs +index 24537998..6e4f62f9 100644 +--- a/test/page3.ssjs ++++ b/test/page3.ssjs +@@ -22,6 +22,7 @@ opts = [ + "fallback_document_root", + "ssl_certificate", + "num_threads", ++"prespawn_threads", + "run_as_user", + "url_rewrite_patterns", + "hide_files_patterns", +diff --git a/unittest/private.c b/unittest/private.c +index 8ea35452..f9d6015c 100644 +--- a/unittest/private.c ++++ b/unittest/private.c +@@ -1626,6 +1626,7 @@ START_TEST(test_config_options) + ck_assert_str_eq("ssl_certificate_chain", + config_options[SSL_CERTIFICATE_CHAIN].name); + ck_assert_str_eq("num_threads", config_options[NUM_THREADS].name); ++ ck_assert_str_eq("prespawn_threads", config_options[PRESPAWN_THREADS].name); + ck_assert_str_eq("run_as_user", config_options[RUN_AS_USER].name); + ck_assert_str_eq("url_rewrite_patterns", + config_options[URL_REWRITE_PATTERN].name); +-- +2.34.1 + + +From 3e0761c14ddba5597e8adae08e70ac9ea88c1555 Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Sat, 13 May 2023 18:56:57 -0700 +Subject: [PATCH 018/173] Fixed a compiler warning + +--- + src/civetweb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 88883aee..fdbb16e3 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -21236,7 +21236,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + ctx->context_type = CONTEXT_SERVER; /* server context */ + + /* Start worker threads */ +- for (int i = 0; i < prespawnthreadcount; i++) { ++ for (i = 0; (int)i < prespawnthreadcount; i++) { + /* worker_thread sets up the other fields */ + if (mg_start_worker_thread(ctx, 0) != 0) { + long error_no = (long)ERRNO; +-- +2.34.1 + + +From a588b5ffe7b67a71f7cba4006700b12eaf3af25d Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Sat, 13 May 2023 22:34:43 -0700 +Subject: [PATCH 019/173] Fix mg_start_worker_thread() to take the number of + socket-connections already enqueued into account when deciding whether or not + to spawn a new thread + +--- + src/civetweb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index fdbb16e3..cb902dba 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -20527,7 +20527,7 @@ static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_th + } + + (void)pthread_mutex_lock(&ctx->thread_mutex); +- if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) { ++ if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > (unsigned)(ctx->sq_head-ctx->sq_tail))) { + (void)pthread_mutex_unlock(&ctx->thread_mutex); + return -2; /* There are idle threads available, so no need to spawn a new worker thread now */ + } +-- +2.34.1 + + +From 0891e955d129810dccc7682dd374db09ffffff58 Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Sat, 13 May 2023 22:44:04 -0700 +Subject: [PATCH 020/173] Fix for ALTERNATIVE_QUEUE mode + +--- + src/civetweb.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index cb902dba..5287afdf 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -20527,7 +20527,11 @@ static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_th + } + + (void)pthread_mutex_lock(&ctx->thread_mutex); ++#if defined(ALTERNATIVE_QUEUE) ++ if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) { ++#else + if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > (unsigned)(ctx->sq_head-ctx->sq_tail))) { ++#endif + (void)pthread_mutex_unlock(&ctx->thread_mutex); + return -2; /* There are idle threads available, so no need to spawn a new worker thread now */ + } +-- +2.34.1 + + +From c1cf4be2d3a1c118c64a815672e215af0772ffbe Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 14 May 2023 10:59:26 +0200 +Subject: [PATCH 021/173] Use lowercase "windows.h" instead of "Windows.h" + +Fixes #1165 + +While the Microsoft documentation uses "Windows.h" some other source files already use "windows.h". +--- + src/civetweb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 064aca29..1de1531f 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -525,7 +525,7 @@ mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, + + + #if defined(_WIN32) /* WINDOWS include block */ +-#include ++#include + #include /* *alloc( */ + #include /* *alloc( */ + #include /* struct timespec */ +-- +2.34.1 + + +From 8db42679a4b52b2ebcc10cf24da3777b369d84e5 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 14 May 2023 11:27:59 +0200 +Subject: [PATCH 022/173] Format code after merge + +--- + src/civetweb.c | 158 ++++++++++++++++++++++++++++----------------- + src/match.inl | 2 +- + src/mod_lua.inl | 8 ++- + unittest/private.c | 6 +- + 4 files changed, 108 insertions(+), 66 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index c82d7371..49d77f8e 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -525,10 +525,10 @@ mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, + + + #if defined(_WIN32) /* WINDOWS include block */ ++#include /* *alloc( */ ++#include /* *alloc( */ ++#include /* struct timespec */ + #include +-#include /* *alloc( */ +-#include /* *alloc( */ +-#include /* struct timespec */ + #include /* DTL add for SO_EXCLUSIVE */ + #include + +@@ -2442,8 +2442,10 @@ struct mg_context { + int lua_bg_log_available; /* Use Lua background state for access log */ + #endif + +- int user_shutdown_notification_socket; /* mg_stop() will close this socket... */ +- int thread_shutdown_notification_socket; /* to cause poll() in all threads to return immediately */ ++ int user_shutdown_notification_socket; /* mg_stop() will close this ++ socket... */ ++ int thread_shutdown_notification_socket; /* to cause poll() in all threads ++ to return immediately */ + + /* Server nonce */ + pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers, +@@ -7607,9 +7609,9 @@ extention_matches_template_text( + * If the file is found, it's stats is returned in stp. */ + static int + substitute_index_file_aux(struct mg_connection *conn, +- char *path, +- size_t path_len, +- struct mg_file_stat *filestat) ++ char *path, ++ size_t path_len, ++ struct mg_file_stat *filestat) + { + const char *list = conn->dom_ctx->config[INDEX_FILES]; + struct vec filename_vec; +@@ -7651,7 +7653,8 @@ substitute_index_file_aux(struct mg_connection *conn, + return found; + } + +-/* Same as above, except if the first try fails and a fallback-root is configured, we'll try there also */ ++/* Same as above, except if the first try fails and a fallback-root is ++ * configured, we'll try there also */ + static int + substitute_index_file(struct mg_connection *conn, + char *path, +@@ -7660,24 +7663,36 @@ substitute_index_file(struct mg_connection *conn, + { + int ret = substitute_index_file_aux(conn, path, path_len, filestat); + if (ret == 0) { +- const char * root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT]; +- const char * fallback_root_prefix = conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]; +- if ((root_prefix)&&(fallback_root_prefix)) { ++ const char *root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT]; ++ const char *fallback_root_prefix = ++ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]; ++ if ((root_prefix) && (fallback_root_prefix)) { + const size_t root_prefix_len = strlen(root_prefix); + if ((strncmp(path, root_prefix, root_prefix_len) == 0)) { +- const size_t fallback_root_prefix_len = strlen(fallback_root_prefix); +- const char * sub_path = path+root_prefix_len; +- while(*sub_path == '/') sub_path++; ++ const size_t fallback_root_prefix_len = ++ strlen(fallback_root_prefix); ++ const char *sub_path = path + root_prefix_len; ++ while (*sub_path == '/') ++ sub_path++; + const size_t sub_path_len = strlen(sub_path); + +- char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid side effects if we fail */ +- if (((fallback_root_prefix_len + 1 + sub_path_len + 1) < sizeof(scratch_path))) { +- /* The concatenations below are all safe because we pre-verified string lengths above */ ++ char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid ++ side effects if we fail */ ++ if (((fallback_root_prefix_len + 1 + sub_path_len + 1) ++ < sizeof(scratch_path))) { ++ /* The concatenations below are all safe because we ++ * pre-verified string lengths above */ + strcpy(scratch_path, fallback_root_prefix); +- char * nul = strchr(scratch_path, '\0'); +- if ((nul > scratch_path)&&(*(nul-1) != '/')) {*nul++ = '/'; *nul = '\0';} ++ char *nul = strchr(scratch_path, '\0'); ++ if ((nul > scratch_path) && (*(nul - 1) != '/')) { ++ *nul++ = '/'; ++ *nul = '\0'; ++ } + strcat(scratch_path, sub_path); +- if (substitute_index_file_aux(conn, scratch_path, sizeof(scratch_path), filestat)) { ++ if (substitute_index_file_aux(conn, ++ scratch_path, ++ sizeof(scratch_path), ++ filestat)) { + mg_strlcpy(path, scratch_path, path_len); + return 1; + } +@@ -7708,7 +7723,9 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ + + #if !defined(NO_FILES) + const char *uri = conn->request_info.local_uri; +- const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT], conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT], NULL}; ++ const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT], ++ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT], ++ NULL}; + int fileExists = 0; + const char *rewrite; + struct vec a, b; +@@ -7771,15 +7788,19 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ + return; + } + +- for (int i=0; roots[i] != NULL; i++) +- { ++ for (int i = 0; roots[i] != NULL; i++) { + /* Step 6: Determine the local file path from the root path and the + * request uri. */ + /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift + * part of the path one byte on the right. */ + truncated = 0; +- mg_snprintf( +- conn, &truncated, filename, filename_buf_len - 1, "%s%s", roots[i], uri); ++ mg_snprintf(conn, ++ &truncated, ++ filename, ++ filename_buf_len - 1, ++ "%s%s", ++ roots[i], ++ uri); + + if (truncated) { + goto interpret_cleanup; +@@ -9610,7 +9631,10 @@ connect_socket( + pfd[1].fd = ctx ? ctx->thread_shutdown_notification_socket : -1; + pfd[1].events = POLLIN; + +- pollres = mg_poll(pfd, ctx ? 2 : 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop); ++ pollres = mg_poll(pfd, ++ ctx ? 2 : 1, ++ ms_wait, ++ ctx ? &(ctx->stop_flag) : &nonstop); + + if (pollres != 1) { + /* Not connected */ +@@ -11477,7 +11501,9 @@ prepare_cgi_environment(struct mg_connection *conn, + addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); + addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); + if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { +- addenv(env, "FALLBACK_DOCUMENT_ROOT=%s", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); ++ addenv(env, ++ "FALLBACK_DOCUMENT_ROOT=%s", ++ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); + } + addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version()); + +@@ -16103,9 +16129,10 @@ set_ports_option(struct mg_context *phys_ctx) + continue; + } + +- /* The +2 below includes the original +1 (for the socket we're about to add), +- * plus another +1 for the thread_shutdown_notification_socket that we'll +- * also want to poll() on so that mg_stop() can return quickly ++ /* The +2 below includes the original +1 (for the socket we're about to ++ * add), plus another +1 for the thread_shutdown_notification_socket ++ * that we'll also want to poll() on so that mg_stop() can return ++ * quickly + */ + if ((pfd = (struct mg_pollfd *) + mg_realloc_ctx(phys_ctx->listening_socket_fds, +@@ -16635,14 +16662,14 @@ sslize(struct mg_connection *conn, + int pollres; + pfd[0].fd = conn->client.sock; + pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT) +- || (err == SSL_ERROR_WANT_WRITE)) +- ? POLLOUT +- : POLLIN; ++ || (err == SSL_ERROR_WANT_WRITE)) ++ ? POLLOUT ++ : POLLIN; + +- pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[1].fd = ++ conn->phys_ctx->thread_shutdown_notification_socket; + pfd[1].events = POLLIN; +- pollres = +- mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag)); ++ pollres = mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag)); + if (pollres < 0) { + /* Break if error occurred (-1) + * or server shutdown (-2) */ +@@ -20226,14 +20253,17 @@ master_thread_run(struct mg_context *ctx) + pfd[i].events = POLLIN; + } + +- /* We listen on this socket just so that mg_stop() can cause mg_poll() to return ASAP. +- * Don't worry, we did allocate an extra slot at the end of listening_socket_fds[] just to hold this ++ /* We listen on this socket just so that mg_stop() can cause mg_poll() ++ * to return ASAP. Don't worry, we did allocate an extra slot at the end ++ * of listening_socket_fds[] just to hold this + */ +- pfd[ctx->num_listening_sockets].fd = ctx->thread_shutdown_notification_socket; ++ pfd[ctx->num_listening_sockets].fd = ++ ctx->thread_shutdown_notification_socket; + pfd[ctx->num_listening_sockets].events = POLLIN; + + if (mg_poll(pfd, +- ctx->num_listening_sockets+1, // +1 for the thread_shutdown_notification_socket ++ ctx->num_listening_sockets ++ + 1, // +1 for the thread_shutdown_notification_socket + SOCKET_TIMEOUT_QUANTUM, + &(ctx->stop_flag)) + > 0) { +@@ -20475,9 +20505,11 @@ mg_stop(struct mg_context *ctx) + /* Set stop flag, so all threads know they have to exit. */ + STOP_FLAG_ASSIGN(&ctx->stop_flag, 1); + +- /* Closing this socket will cause mg_poll() in all the I/O threads to return immediately */ ++ /* Closing this socket will cause mg_poll() in all the I/O threads to return ++ * immediately */ + closesocket(ctx->user_shutdown_notification_socket); +- ctx->user_shutdown_notification_socket = -1; /* to avoid calling closesocket() again in free_context() */ ++ ctx->user_shutdown_notification_socket = ++ -1; /* to avoid calling closesocket() again in free_context() */ + + /* Join timer thread */ + #if defined(USE_TIMERS) +@@ -20578,12 +20610,11 @@ legacy_init(const char **options) + + /* we'll assume it's only Windows that doesn't have socketpair() available */ + #if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32) +-# define HAVE_SOCKETPAIR 1 ++#define HAVE_SOCKETPAIR 1 + #endif + + static int +-mg_socketpair(int * sockA, +- int * sockB) ++mg_socketpair(int *sockA, int *sockB) + { + int temp[2] = {-1, -1}; + +@@ -20601,11 +20632,12 @@ mg_socketpair(int * sockA, + } + return ret; + #else +- /** No socketpair() call is available, so we'll have to roll our own implementation */ ++ /** No socketpair() call is available, so we'll have to roll our own ++ * implementation */ + int asock = socket(PF_INET, SOCK_STREAM, 0); + if (asock >= 0) { + struct sockaddr_in addr; +- struct sockaddr * pa = (struct sockaddr *) &addr; ++ struct sockaddr *pa = (struct sockaddr *)&addr; + socklen_t addrLen = sizeof(addr); + + memset(&addr, 0, sizeof(addr)); +@@ -20614,10 +20646,10 @@ mg_socketpair(int * sockA, + addr.sin_port = 0; + + if ((bind(asock, pa, sizeof(addr)) == 0) +- &&(getsockname(asock, pa, &addrLen) == 0) +- &&(listen(asock, 1) == 0)) { ++ && (getsockname(asock, pa, &addrLen) == 0) ++ && (listen(asock, 1) == 0)) { + temp[0] = socket(PF_INET, SOCK_STREAM, 0); +- if ((temp[0] >= 0)&&(connect(temp[0], pa, sizeof(addr)) == 0)) { ++ if ((temp[0] >= 0) && (connect(temp[0], pa, sizeof(addr)) == 0)) { + temp[1] = accept(asock, pa, &addrLen); + if (temp[1] >= 0) { + closesocket(asock); +@@ -20625,17 +20657,20 @@ mg_socketpair(int * sockA, + *sockB = temp[1]; + set_close_on_exec(*sockA, NULL, NULL); + set_close_on_exec(*sockB, NULL, NULL); +- return 0; /* success! */ ++ return 0; /* success! */ + } + } + } + } + + /* Cleanup */ +- if (asock >= 0) closesocket(asock); +- if (temp[0] >= 0) closesocket(temp[0]); +- if (temp[1] >= 0) closesocket(temp[1]); +- return -1; /* fail! */ ++ if (asock >= 0) ++ closesocket(asock); ++ if (temp[0] >= 0) ++ closesocket(temp[0]); ++ if (temp[1] >= 0) ++ closesocket(temp[1]); ++ return -1; /* fail! */ + #endif + } + +@@ -20722,10 +20757,13 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr)); + #endif + +- /** mg_stop() will close the user_shutdown_notification_socket, and that will cause poll() +- * to return immediately in the master-thread, so that mg_stop() can also return immediately. +- */ +- ok &= (0 == mg_socketpair(&ctx->user_shutdown_notification_socket, &ctx->thread_shutdown_notification_socket)); ++ /** mg_stop() will close the user_shutdown_notification_socket, and that ++ * will cause poll() to return immediately in the master-thread, so that ++ * mg_stop() can also return immediately. ++ */ ++ ok &= (0 ++ == mg_socketpair(&ctx->user_shutdown_notification_socket, ++ &ctx->thread_shutdown_notification_socket)); + + if (!ok) { + unsigned error_id = (unsigned)ERRNO; +diff --git a/src/match.inl b/src/match.inl +index 9c2765d7..34ee00ef 100644 +--- a/src/match.inl ++++ b/src/match.inl +@@ -48,7 +48,7 @@ mg_match_impl(const char *pat, + i_pat++; + i_str++; + } while ((i_pat < pat_len) && (pat[i_pat] == '?') +- && (str[i_str] != '\0') && (str[i_str] != '/')); ++ && (str[i_str] != '\0') && (str[i_str] != '/')); + + /* If we have a match context, add the substring we just found */ + if (mcx) { +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index ec7611c0..f1070d58 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -2936,7 +2936,9 @@ prepare_lua_environment(struct mg_context *ctx, + if ((conn != NULL) && (conn->dom_ctx != NULL)) { + reg_string(L, "document_root", conn->dom_ctx->config[DOCUMENT_ROOT]); + if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { +- reg_string(L, "fallback_document_root", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); ++ reg_string(L, ++ "fallback_document_root", ++ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); + } + reg_string(L, + "auth_domain", +@@ -2948,8 +2950,8 @@ prepare_lua_environment(struct mg_context *ctx, + conn->dom_ctx->config[WEBSOCKET_ROOT]); + if (conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]) { + reg_string(L, +- "fallback_websocket_root", +- conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]); ++ "fallback_websocket_root", ++ conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]); + } + } else { + reg_string(L, +diff --git a/unittest/private.c b/unittest/private.c +index 8ea35452..9af3939b 100644 +--- a/unittest/private.c ++++ b/unittest/private.c +@@ -1621,7 +1621,8 @@ START_TEST(test_config_options) + ck_assert_str_eq("extra_mime_types", config_options[EXTRA_MIME_TYPES].name); + ck_assert_str_eq("listening_ports", config_options[LISTENING_PORTS].name); + ck_assert_str_eq("document_root", config_options[DOCUMENT_ROOT].name); +- ck_assert_str_eq("fallback_document_root", config_options[FALLBACK_DOCUMENT_ROOT].name); ++ ck_assert_str_eq("fallback_document_root", ++ config_options[FALLBACK_DOCUMENT_ROOT].name); + ck_assert_str_eq("ssl_certificate", config_options[SSL_CERTIFICATE].name); + ck_assert_str_eq("ssl_certificate_chain", + config_options[SSL_CERTIFICATE_CHAIN].name); +@@ -1673,7 +1674,8 @@ START_TEST(test_config_options) + #endif + #if defined(USE_WEBSOCKET) + ck_assert_str_eq("websocket_root", config_options[WEBSOCKET_ROOT].name); +- ck_assert_str_eq("fallback_websocket_root", config_options[FALLBACK_WEBSOCKET_ROOT].name); ++ ck_assert_str_eq("fallback_websocket_root", ++ config_options[FALLBACK_WEBSOCKET_ROOT].name); + #endif + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + ck_assert_str_eq("lua_websocket_pattern", +-- +2.34.1 + + +From de57e79919920b1027a3c9dd650ba3f042d9087e Mon Sep 17 00:00:00 2001 +From: Sergey Linev +Date: Fri, 26 May 2023 08:50:24 +0200 +Subject: [PATCH 023/173] Add optional support of + Access-Control-Allow-Credentials header + +Can be set with Access-Control-Allow-Origin header +--- + src/civetweb.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 60c99fa4..cc68b402 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -2059,6 +2059,7 @@ enum { + ACCESS_CONTROL_ALLOW_ORIGIN, + ACCESS_CONTROL_ALLOW_METHODS, + ACCESS_CONTROL_ALLOW_HEADERS, ++ ACCESS_CONTROL_ALLOW_CREDENTIALS, + ERROR_PAGES, + #if !defined(NO_CACHING) + STATIC_FILE_MAX_AGE, +@@ -2222,6 +2223,7 @@ static const struct mg_option config_options[] = { + {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, ++ {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""}, + {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL}, + #if !defined(NO_CACHING) + {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"}, +@@ -4195,6 +4197,18 @@ send_cors_header(struct mg_connection *conn) + cors_orig_cfg, + -1); + } ++ ++ const char *cors_cred_cfg = ++ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; ++ if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) { ++ /* Cross-origin resource sharing (CORS), see ++ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials */ ++ mg_response_header_add(conn, ++ "Access-Control-Allow-Credentials", ++ cors_cred_cfg, ++ -1); ++ } ++ + } + + +-- +2.34.1 + + +From 4523dd5bae7101f02ac5bdc883c44a7815ca4880 Mon Sep 17 00:00:00 2001 +From: Sergey Linev +Date: Fri, 26 May 2023 10:40:03 +0200 +Subject: [PATCH 024/173] Also send allowed headers and methods in + send_cors_header + +--- + src/civetweb.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index cc68b402..901c5419 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -4209,6 +4209,24 @@ send_cors_header(struct mg_connection *conn) + -1); + } + ++ const char *cors_hdr_cfg = ++ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS]; ++ if (cors_hdr_cfg && *cors_hdr_cfg) { ++ mg_response_header_add(conn, ++ "Access-Control-Allow-Headers", ++ cors_hdr_cfg, ++ -1); ++ } ++ ++ const char *cors_meth_cfg = ++ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; ++ if (cors_meth_cfg && *cors_meth_cfg) { ++ mg_response_header_add(conn, ++ "Access-Control-Allow-Methods", ++ cors_meth_cfg, ++ -1); ++ } ++ + } + + +-- +2.34.1 + + +From 4442d645a0c14a3c392aeeff19364d4200868a35 Mon Sep 17 00:00:00 2001 +From: Sergey Linev +Date: Fri, 26 May 2023 10:41:56 +0200 +Subject: [PATCH 025/173] Add Access-Control-Allow-Credentials header in normal + replay + +Only when really configured +--- + src/civetweb.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 901c5419..58032d6d 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -15024,6 +15024,13 @@ handle_request(struct mg_connection *conn) + ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), + suggest_connection_header(conn)); + ++ const char *cors_cred_cfg = ++ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; ++ if (cors_cred_cfg && *cors_cred_cfg) ++ mg_printf(conn, ++ "Access-Control-Allow-Credentials: %s\r\n", ++ cors_cred_cfg); ++ + if (cors_acrh != NULL) { + /* CORS request is asking for additional headers */ + const char *cors_hdr_cfg = +-- +2.34.1 + + +From d04032cc0ba695ac39c2f5517e8961251426a77a Mon Sep 17 00:00:00 2001 +From: Sergey Linev +Date: Fri, 26 May 2023 10:54:54 +0200 +Subject: [PATCH 026/173] Add CORS Access-Control-Expose-Headers + +Can be used together with Access-Control-Allow-Credentials +Allows to access 'unsafe' headers from http reply by client +See: + +https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers +--- + src/civetweb.c | 24 ++++++++++++++++++++++-- + 1 file changed, 22 insertions(+), 2 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 58032d6d..306f2e37 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -2059,6 +2059,7 @@ enum { + ACCESS_CONTROL_ALLOW_ORIGIN, + ACCESS_CONTROL_ALLOW_METHODS, + ACCESS_CONTROL_ALLOW_HEADERS, ++ ACCESS_CONTROL_EXPOSE_HEADERS, + ACCESS_CONTROL_ALLOW_CREDENTIALS, + ERROR_PAGES, + #if !defined(NO_CACHING) +@@ -2223,6 +2224,7 @@ static const struct mg_option config_options[] = { + {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, ++ {"access_control_expose_headers", MG_CONFIG_TYPE_STRING, ""}, + {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""}, + {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL}, + #if !defined(NO_CACHING) +@@ -4218,6 +4220,15 @@ send_cors_header(struct mg_connection *conn) + -1); + } + ++ const char *cors_exphdr_cfg = ++ conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; ++ if (cors_exphdr_cfg && *cors_exphdr_cfg) { ++ mg_response_header_add(conn, ++ "Access-Control-Expose-Headers", ++ cors_exphdr_cfg, ++ -1); ++ } ++ + const char *cors_meth_cfg = + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; + if (cors_meth_cfg && *cors_meth_cfg) { +@@ -15026,12 +15037,21 @@ handle_request(struct mg_connection *conn) + + const char *cors_cred_cfg = + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; +- if (cors_cred_cfg && *cors_cred_cfg) ++ if (cors_cred_cfg && *cors_cred_cfg) { + mg_printf(conn, + "Access-Control-Allow-Credentials: %s\r\n", + cors_cred_cfg); ++ } ++ ++ const char *cors_exphdr_cfg = ++ conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; ++ if (cors_exphdr_cfg && *cors_exphdr_cfg) { ++ mg_printf(conn, ++ "Access-Control-Expose-Headers: %s\r\n", ++ cors_exphdr_cfg); ++ } + +- if (cors_acrh != NULL) { ++ if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) { + /* CORS request is asking for additional headers */ + const char *cors_hdr_cfg = + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS]; +-- +2.34.1 + + +From d7ae5df016e227ec462cf166e28edf6857d166ad Mon Sep 17 00:00:00 2001 +From: liangmingyuan +Date: Mon, 14 Aug 2023 09:25:50 +0800 +Subject: [PATCH 027/173] Fix the wrong report of http header transferring + timeout. + +For now, the connection is not closed at first keep_alive_timeout_ms +interval, if the whole header can not be read in the second interval +at one time, then return failed. +However, the http header can be transferred in two packages. +Especially in https, there will be one byte exchanged before the +real data transferring. This can lead to wrong header transferred +timeout when two request are sended more than a keep_alive_timeout_ms +interval. +--- + src/civetweb.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 306f2e37..afb03295 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -11328,11 +11328,11 @@ read_message(FILE *fp, + request_len = get_http_header_len(buf, *nread); + } + +- if ((request_len == 0) && (request_timeout >= 0)) { ++ if ((n <= 0) && (request_timeout >= 0)) { + if (mg_difftimespec(&last_action_time, &(conn->req_time)) + > request_timeout) { + /* Timeout */ +- return -1; ++ return -3; + } + } + } +@@ -18816,7 +18816,7 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) + ebuf, + ebuf_len, + "%s", +- "Malformed message"); ++ conn->request_len == -3 ? "Request timeout" : "Malformed message"); + *err = 400; + } else { + /* Server did not recv anything -> just close the connection */ +-- +2.34.1 + + +From 2624754dc09d75ee3764964911019d44e5c63f27 Mon Sep 17 00:00:00 2001 +From: Jeremy Friesner +Date: Thu, 17 Aug 2023 14:35:11 -0700 +Subject: [PATCH 028/173] Added optional tag for ports in civetweb + +--- + src/civetweb.c | 53 +++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 42 insertions(+), 11 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 306f2e37..c7f28117 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1921,6 +1921,7 @@ struct socket { + unsigned char is_ssl; /* Is port SSL-ed */ + unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL + * port */ ++ unsigned char is_optional; /* Shouldn't cause us to exit if we can't bind to it */ + unsigned char in_use; /* 0: invalid, 1: valid, 2: free */ + }; + +@@ -15689,7 +15690,7 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version) + unsigned int a, b, c, d; + unsigned port; + unsigned long portUL; +- int ch, len; ++ int len; + const char *cb; + char *endptr; + #if defined(USE_IPV6) +@@ -15842,14 +15843,31 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version) + } + + /* sscanf and the option splitting code ensure the following condition +- * Make sure the port is valid and vector ends with the port, 's' or 'r' */ +- if ((len > 0) && is_valid_port(port) +- && (((size_t)len == vec->len) || (((size_t)len + 1) == vec->len))) { +- /* Next character after the port number */ +- ch = ((size_t)len < vec->len) ? vec->ptr[len] : '\0'; +- so->is_ssl = (ch == 's'); +- so->ssl_redir = (ch == 'r'); +- if ((ch == '\0') || (ch == 's') || (ch == 'r')) { ++ * Make sure the port is valid and vector ends with the port, 'o', 's', or 'r' */ ++ if ((len > 0) && (is_valid_port(port))) { ++ int bad_suffix = 0; ++ ++ /* Parse any suffix character(s) after the port number */ ++ for (size_t i=len; ilen; i++) ++ { ++ unsigned char * opt = NULL; ++ switch(vec->ptr[i]) ++ { ++ case 'o': opt = &so->is_optional; break; ++ case 'r': opt = &so->ssl_redir; break; ++ case 's': opt = &so->is_ssl; break; ++ default: /* empty */ break; ++ } ++ ++ if ((opt)&&(*opt == 0)) *opt = 1; ++ else ++ { ++ bad_suffix = 1; ++ break; ++ } ++ } ++ ++ if ((bad_suffix == 0)&&((so->is_ssl == 0)||(so->ssl_redir == 0))) { + return 1; + } + } +@@ -15904,8 +15922,11 @@ is_ssl_port_used(const char *ports) + char prevIsNumber = 0; + + for (i = 0; i < portslen; i++) { +- if (prevIsNumber && (ports[i] == 's' || ports[i] == 'r')) { +- return 1; ++ if (prevIsNumber) { ++ int suffixCharIdx = (ports[i] == 'o') ? (i+1) : i; /* allow "os" and "or" suffixes */ ++ if (ports[suffixCharIdx] == 's' || ports[suffixCharIdx] == 'r') { ++ return 1; ++ } + } + if (ports[i] >= '0' && ports[i] <= '9') { + prevIsNumber = 1; +@@ -16088,6 +16109,9 @@ set_ports_option(struct mg_context *phys_ctx) + strerror(errno)); + closesocket(so.sock); + so.sock = INVALID_SOCKET; ++ if (so.is_optional) { ++ portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */ ++ } + continue; + } + } +@@ -16104,6 +16128,9 @@ set_ports_option(struct mg_context *phys_ctx) + strerror(errno)); + closesocket(so.sock); + so.sock = INVALID_SOCKET; ++ if (so.is_optional) { ++ portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */ ++ } + continue; + } + } +@@ -16120,6 +16147,9 @@ set_ports_option(struct mg_context *phys_ctx) + strerror(errno)); + closesocket(so.sock); + so.sock = INVALID_SOCKET; ++ if (so.is_optional) { ++ portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */ ++ } + continue; + } + } +@@ -20190,6 +20220,7 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx) + set_close_on_exec(so.sock, NULL, ctx); + so.is_ssl = listener->is_ssl; + so.ssl_redir = listener->ssl_redir; ++ so.is_optional = listener->is_optional; + if (getsockname(so.sock, &so.lsa.sa, &len) != 0) { + mg_cry_ctx_internal(ctx, + "%s: getsockname() failed: %s", +-- +2.34.1 + + +From ebd2f1f99a45468d27c1557dfa757b778a874ea3 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Tue, 22 Aug 2023 16:10:14 +0300 +Subject: [PATCH 029/173] Fix GCC compilation + +On GNUC use the GCC extension "##__VA_ARGS__" +--- + src/civetweb.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 306f2e37..964c3d2f 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -230,8 +230,13 @@ static void DEBUG_TRACE_FUNC(const char *func, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(3, 4); + ++#if defined(__GNUC__) ++#define DEBUG_TRACE(fmt, ...) \ ++ DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, ##__VA_ARGS__) ++#else + #define DEBUG_TRACE(fmt, ...) \ + DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) ++#endif + + #define NEED_DEBUG_TRACE_FUNC + #if !defined(DEBUG_TRACE_STREAM) +-- +2.34.1 + + +From 3549bfbbf198d45aa7e8ba65c52b311ef16c8f23 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Tue, 22 Aug 2023 16:23:35 +0300 +Subject: [PATCH 030/173] OpenSSL 3 support + +add flag to select OpenSSL 3 at build time +--- + .travis.yml | 2 ++ + CMakeLists.txt | 2 +- + Makefile | 3 +++ + appveyor.yml | 3 +++ + docs/Building.md | 2 +- + 5 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index 8dabd5b4..077fbd89 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -82,6 +82,7 @@ before_script: + -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} + -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} + -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} + -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} + -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} + -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} +@@ -107,6 +108,7 @@ before_script: + -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} + -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} + -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} + -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} + -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} + -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c5368c08..7a48123e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -227,7 +227,7 @@ message(STATUS "Compile for OpenSSL 1.0 API - ${CIVETWEB_SSL_OPENSSL_API_1_0}") + option(CIVETWEB_SSL_OPENSSL_API_1_1 "Use the OpenSSL 1.1 API" ON) + message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}") + +-# OpenSSL 1.1 API ++# OpenSSL 3.0 API + option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF) + message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}") + +diff --git a/Makefile b/Makefile +index 31954e47..076d8739 100644 +--- a/Makefile ++++ b/Makefile +@@ -100,6 +100,8 @@ else ifdef WITH_OPENSSL_API_1_0 + CFLAGS += -DOPENSSL_API_1_0 + else ifdef WITH_OPENSSL_API_1_1 + CFLAGS += -DOPENSSL_API_1_1 ++else ifdef WITH_OPENSSL_API_3_0 ++ CFLAGS += -DOPENSSL_API_3_0 + else + #Use OpenSSL 1.1 API version as default + CFLAGS += -DOPENSSL_API_1_1 +@@ -298,6 +300,7 @@ help: + @echo " WITH_MBEDTLS=1 build with mbedTLS support." + @echo " WITH_OPENSSL_API_1_0=1 build with OpenSSL 1.0.x support." + @echo " WITH_OPENSSL_API_1_1=1 build with OpenSSL 1.1.x support." ++ @echo " WITH_OPENSSL_API_3_0=1 build with OpenSSL 3.0.x support." + @echo " NO_SSL=1 build without SSL support. Build will not need libcrypto/libssl." + @echo " NO_CGI=1 build without CGI support." + @echo " NO_CACHING=1 disable caching. Send no-cache/no-store headers." +diff --git a/appveyor.yml b/appveyor.yml +index 41cde75f..6008c5ee 100644 +--- a/appveyor.yml ++++ b/appveyor.yml +@@ -440,6 +440,7 @@ before_build: + -DCIVETWEB_CXX_STANDARD=%cxx_standard% + -DCIVETWEB_SSL_OPENSSL_API_1_0=NO + -DCIVETWEB_SSL_OPENSSL_API_1_1=YES ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO + "%source_path%" + - cmake + -G "%generator%" %arch_arg% +@@ -461,6 +462,8 @@ before_build: + -DCIVETWEB_CXX_STANDARD=%cxx_standard% + -DCIVETWEB_SSL_OPENSSL_API_1_0=NO + -DCIVETWEB_SSL_OPENSSL_API_1_1=YES ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO ++ + "%source_path%" + - powershell Push-AppveyorArtifact CMakeCache.txt + - cd "%source_path%" +diff --git a/docs/Building.md b/docs/Building.md +index ed30bd77..c6f92add 100644 +--- a/docs/Building.md ++++ b/docs/Building.md +@@ -179,7 +179,7 @@ make build COPT="-DNDEBUG -DNO_CGI" + | `SSL_ALREADY_INITIALIZED` | do not initialize libcrypto | + | `OPENSSL_API_1_0` | Use OpenSSL V1.0.x interface | + | `OPENSSL_API_1_1` | Use OpenSSL V1.1.x interface | +-| `OPENSSL_API_3_0` | Use OpenSSL V3.x interface | ++| `OPENSSL_API_3_0` | Use OpenSSL V3.0.x interface | + | `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_*) | + | | | + | `BUILD_DATE` | define as a string to be used as build id instead of __DATE__ | +-- +2.34.1 + + +From 633bb32c4aea7cf3843e2bac40e6d889f92116c5 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Wed, 23 Aug 2023 12:42:35 +0300 +Subject: [PATCH 031/173] more conditional compilation + +standard manner using __VA_OPT__ +--- + src/civetweb.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 964c3d2f..9adededb 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -230,7 +230,10 @@ static void DEBUG_TRACE_FUNC(const char *func, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(3, 4); + +-#if defined(__GNUC__) ++#if defined(__cplusplus) && (__cplusplus >= 202002L) ++#define DEBUG_TRACE(fmt, ...) \ ++ DEBUG_TRACE_FUNC(__func__, __LINE__, fmt __VA_OPT__(, ) __VA_ARGS__) ++#elif defined(__GNUC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) + #define DEBUG_TRACE(fmt, ...) \ + DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, ##__VA_ARGS__) + #else +-- +2.34.1 + + +From eeddc01ac226b0720a9bbefa6b664a5d4ae004a8 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Tue, 29 Aug 2023 23:19:20 +0300 +Subject: [PATCH 032/173] add missing message format + +Alternative fix for build issue in debug mode with GCC. +--- + src/civetweb.c | 8 -------- + src/http2.inl | 4 ++-- + 2 files changed, 2 insertions(+), 10 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 601ace48..df161c0b 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -230,16 +230,8 @@ static void DEBUG_TRACE_FUNC(const char *func, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(3, 4); + +-#if defined(__cplusplus) && (__cplusplus >= 202002L) +-#define DEBUG_TRACE(fmt, ...) \ +- DEBUG_TRACE_FUNC(__func__, __LINE__, fmt __VA_OPT__(, ) __VA_ARGS__) +-#elif defined(__GNUC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +-#define DEBUG_TRACE(fmt, ...) \ +- DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, ##__VA_ARGS__) +-#else + #define DEBUG_TRACE(fmt, ...) \ + DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) +-#endif + + #define NEED_DEBUG_TRACE_FUNC + #if !defined(DEBUG_TRACE_STREAM) +diff --git a/src/http2.inl b/src/http2.inl +index cea695ed..a4bfd8d0 100644 +--- a/src/http2.inl ++++ b/src/http2.inl +@@ -1442,7 +1442,7 @@ handle_http2(struct mg_connection *conn) + hpack_decode(buf, &i, (int)bytes_read, conn->phys_ctx); + CHECK_LEAK_HDR_ALLOC(key); + if (!key) { +- DEBUG_TRACE("HTTP2 key decoding error"); ++ DEBUG_TRACE("%s", "HTTP2 key decoding error"); + goto clean_http2; + } + } else if (/*(idx >= 15) &&*/ (idx <= 61)) { +@@ -1509,7 +1509,7 @@ handle_http2(struct mg_connection *conn) + conn->phys_ctx); /* leak? */ + CHECK_LEAK_HDR_ALLOC(val); + if (!val) { +- DEBUG_TRACE("HTTP2 value decoding error"); ++ DEBUG_TRACE("%s", "HTTP2 value decoding error"); + mg_free((void *)key); + goto clean_http2; + } +-- +2.34.1 + + +From 07f002e49c8437dbd7b49d44a3f972952d3a5da1 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Tue, 29 Aug 2023 22:17:50 +0300 +Subject: [PATCH 033/173] Create Dockerfile + +--- + Dockerfile | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + create mode 100644 Dockerfile + +diff --git a/Dockerfile b/Dockerfile +new file mode 100644 +index 00000000..ac87841c +--- /dev/null ++++ b/Dockerfile +@@ -0,0 +1,35 @@ ++#################################################### ++# build stage ++#################################################### ++ ++FROM alpine:3.18 AS build ++RUN apk update && \ ++ apk add --no-cache \ ++ build-base zlib-dev ++ ++WORKDIR /civetweb ++COPY src ./src/ ++COPY include ./include/ ++COPY Makefile ./ ++COPY resources ./resources/ ++COPY *.md ./ ++ ++RUN make build && \ ++ make WITH_ALL=1 && \ ++ make install PREFIX=/app ++ ++#################################################### ++# image stage ++#################################################### ++ ++FROM alpine:3.18 ++RUN apk update && \ ++ apk add --no-cache \ ++ libstdc++ zlib ++ ++RUN addgroup -S civetweb && adduser -S civetweb -G civetweb ++USER civetweb ++ ++COPY --chown=civetweb:civetweb --from=build /app/ /app/ ++ ++ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] +-- +2.34.1 + + +From 6653ce89f6307d37fab28ee3861f88e4d0a591e6 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Wed, 30 Aug 2023 00:41:40 +0300 +Subject: [PATCH 034/173] Docker documentation + +--- + Dockerfile | 32 +++++++++++++---- + README.md | 1 + + docs/Docker.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 122 insertions(+), 7 deletions(-) + create mode 100644 docs/Docker.md + +diff --git a/Dockerfile b/Dockerfile +index ac87841c..3bb7540a 100644 +--- a/Dockerfile ++++ b/Dockerfile +@@ -1,35 +1,53 @@ +-#################################################### +-# build stage +-#################################################### ++# --------------------------------------------------- ++# Build Stage ++# --------------------------------------------------- + ++# Use Alpine Linux 3.18 as the base image for the build stage + FROM alpine:3.18 AS build ++ ++# Update package list and install build dependencies + RUN apk update && \ + apk add --no-cache \ + build-base zlib-dev +- ++ ++# Set the working directory inside the container + WORKDIR /civetweb ++ ++# Copy source code and other necessary files into the container + COPY src ./src/ + COPY include ./include/ + COPY Makefile ./ + COPY resources ./resources/ + COPY *.md ./ + ++# Build Civetweb with all features and install it into /app directory + RUN make build && \ + make WITH_ALL=1 && \ + make install PREFIX=/app + +-#################################################### +-# image stage +-#################################################### ++# --------------------------------------------------- ++# Image Stage ++# --------------------------------------------------- + ++# Use Alpine Linux 3.18 as the base image for the final stage + FROM alpine:3.18 ++ ++# Update package list and install runtime dependencies + RUN apk update && \ + apk add --no-cache \ + libstdc++ zlib + ++# Create a non-root user and group for running Civetweb + RUN addgroup -S civetweb && adduser -S civetweb -G civetweb ++ ++# Switch to the non-root user + USER civetweb + ++# Copy the built application from the build stage into this stage + COPY --chown=civetweb:civetweb --from=build /app/ /app/ + ++# Expose port 8080 for the application ++EXPOSE 8080 ++ ++# Set the entry point for the container + ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] +diff --git a/README.md b/README.md +index 1d1c82b8..2c9e40a2 100644 +--- a/README.md ++++ b/README.md +@@ -75,6 +75,7 @@ Quick start documentation + - [docs/Building.md](https://github.com/civetweb/civetweb/blob/master/docs/Building.md) - Building the Server (quick start guide) + - [docs/Embedding.md](https://github.com/civetweb/civetweb/blob/master/docs/Embedding.md) - Embedding (how to add HTTP support to an existing application) + - [docs/OpenSSL.md](https://github.com/civetweb/civetweb/blob/master/docs/OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. ++- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use civetweb in a Docker container. + - [API documentation](https://github.com/civetweb/civetweb/tree/master/docs/api) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). + - [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md) - Release Notes + - [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - Security Policy +diff --git a/docs/Docker.md b/docs/Docker.md +new file mode 100644 +index 00000000..b21c3f47 +--- /dev/null ++++ b/docs/Docker.md +@@ -0,0 +1,96 @@ ++# Running Civetweb in Docker ++ ++## Overview ++ ++This guide explains how to build and run Civetweb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. ++ ++## Prerequisites ++ ++- Docker installed on your machine ++- Basic understanding of Docker commands ++ ++## Dockerfile Explained ++ ++### Build Stage ++ ++The build stage uses Alpine Linux 3.18 and installs the necessary build tools and libraries. ++ ++```Dockerfile ++FROM alpine:3.18 AS build ++RUN apk update && \ ++ apk add --no-cache \ ++ build-base zlib-dev ++``` ++ ++The source code and other necessary files are copied into the `/civetweb` directory in the container. ++ ++```Dockerfile ++WORKDIR /civetweb ++COPY src ./src/ ++COPY include ./include/ ++COPY Makefile ./ ++COPY resources ./resources/ ++COPY *.md ./ ++``` ++ ++The Civetweb server is then built and installed into the `/app` directory. ++ ++```Dockerfile ++RUN make build && \ ++ make WITH_ALL=1 && \ ++ make install PREFIX=/app ++``` ++ ++### Image Stage ++ ++The image stage also uses Alpine Linux 3.18 but installs only the runtime dependencies. ++ ++```Dockerfile ++FROM alpine:3.18 ++RUN apk update && \ ++ apk add --no-cache \ ++ libstdc++ zlib ++``` ++ ++A non-root user `civetweb` is created for security reasons. ++ ++```Dockerfile ++RUN addgroup -S civetweb && adduser -S civetweb -G civetweb ++USER civetweb ++``` ++ ++The built application from the build stage is copied into this stage. ++ ++```Dockerfile ++COPY --chown=civetweb:civetweb --from=build /app/ /app/ ++``` ++ ++The container will listen on port 8080 at runtime. ++ ++```Dockerfile ++EXPOSE 8080 ++``` ++ ++Finally, the entry point for the container is set. ++ ++```Dockerfile ++ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] ++``` ++ ++## Build and Run ++ ++To build the Docker image, run: ++ ++```bash ++docker build -t civetweb:latest . ++``` ++ ++To run the container, execute: ++ ++```bash ++docker run -p 8080:8080 civetweb:latest ++``` ++ ++## Conclusion ++ ++This Dockerfile provides a secure and efficient way to build and run Civetweb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. +-- +2.34.1 + + +From 26a22d3e31727c2f86093610aaa677c9d48b1df1 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Wed, 30 Aug 2023 00:49:12 +0300 +Subject: [PATCH 035/173] fix typo + +--- + docs/Docker.md | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/docs/Docker.md b/docs/Docker.md +index b21c3f47..ac0fadc9 100644 +--- a/docs/Docker.md ++++ b/docs/Docker.md +@@ -1,8 +1,8 @@ +-# Running Civetweb in Docker ++# Running CivetWeb in Docker + + ## Overview + +-This guide explains how to build and run Civetweb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. ++This guide explains how to build and run CivetWeb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. + + ## Prerequisites + +@@ -93,4 +93,4 @@ docker run -p 8080:8080 civetweb:latest + + ## Conclusion + +-This Dockerfile provides a secure and efficient way to build and run Civetweb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. ++This Dockerfile provides a secure and efficient way to build and run CivetWeb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. +-- +2.34.1 + + +From a08666e1424be313c5f0df135fb82025d8650390 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Wed, 30 Aug 2023 00:51:39 +0300 +Subject: [PATCH 036/173] Update README.md + +--- + docs/README.md | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/docs/README.md b/docs/README.md +index 54ed7128..76e57732 100644 +--- a/docs/README.md ++++ b/docs/README.md +@@ -43,6 +43,7 @@ Documentation + - [Building.md](Building.md) - Building the Server (quick start guide) + - [Embedding.md](Embedding.md) - Embedding (how to add HTTP support to an existing application) + - [OpenSSL.md](OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. ++- [Docker.md](Docker.md) - Use CivetWeb in a Docker container. + - [APIReference.md](APIReference.md) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). + + [Authors](https://github.com/civetweb/civetweb/blob/master/CREDITS.md) +-- +2.34.1 + + +From 06f7ac416cfffee2111fc3633f99b8cb0d359a34 Mon Sep 17 00:00:00 2001 +From: Hansi P +Date: Wed, 30 Aug 2023 00:53:04 +0300 +Subject: [PATCH 037/173] fix typo + +--- + README.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/README.md b/README.md +index 2c9e40a2..d0624ab5 100644 +--- a/README.md ++++ b/README.md +@@ -75,7 +75,7 @@ Quick start documentation + - [docs/Building.md](https://github.com/civetweb/civetweb/blob/master/docs/Building.md) - Building the Server (quick start guide) + - [docs/Embedding.md](https://github.com/civetweb/civetweb/blob/master/docs/Embedding.md) - Embedding (how to add HTTP support to an existing application) + - [docs/OpenSSL.md](https://github.com/civetweb/civetweb/blob/master/docs/OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. +-- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use civetweb in a Docker container. ++- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use CivetWeb in a Docker container. + - [API documentation](https://github.com/civetweb/civetweb/tree/master/docs/api) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). + - [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md) - Release Notes + - [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - Security Policy +-- +2.34.1 + + +From 3c62c8314720c7975ae68d343d6bfe66e6c9802b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tomasz=20K=C5=82oczko?= +Date: Sat, 9 Sep 2023 11:27:58 +0000 +Subject: [PATCH 038/173] install pkgconfig files in libdir +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +pkgconfig files are arch dependend files so they needs to be installed +in libdir. + +Signed-off-by: Tomasz Kłoczko +--- + CMakeLists.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7a48123e..ae00462a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -629,13 +629,13 @@ configure_file( + install( + FILES + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" +- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" ++ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + ) + + install( + FILES + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-cpp.pc" +- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" ++ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + ) + + write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake +-- +2.34.1 + + +From ee7ea1bb767c769e15bfa25266fd92145f7a05ab Mon Sep 17 00:00:00 2001 +From: Evan Fedorov +Date: Sun, 1 Oct 2023 19:17:30 +0300 +Subject: [PATCH 039/173] fix minor typo in docs/api/mg_request_info.md + +Added missing underscore symbol to `remote_addr`. +--- + docs/api/mg_request_info.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/api/mg_request_info.md b/docs/api/mg_request_info.md +index 0f9d720d..b7e0b6e1 100644 +--- a/docs/api/mg_request_info.md ++++ b/docs/api/mg_request_info.md +@@ -14,7 +14,7 @@ + |**`http_version`**|`const char *`| The HTTP version as mentioned in the client request. This can be "1.0", "1.1", etc. | + |**`query_string`**|`const char *`| The HTTP query string, defined as URL part after the first '?' character, not including '?'. NULL if there is no '?'. | + |**`remote_user`**|`const char *`| The name of the authenticated remote user, or NULL if no authentication was used. Only used for HTTP (digest) authentication, not for cookie based authentication. | +-|**`remote addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address. Example: "127.0.0.1" | ++|**`remote_addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address. Example: "127.0.0.1" | + |~~`remote_ip`~~|`long`| *Deprecated. Use* `remote_addr` *instead* | + |**`content_length`**|`long long`| The content length of the request body. This value can be -1 if no content length was provided. The request may still have body data, but the server cannot determine the length until all data has arrived (e.g. when the client closes the connection, or the final chunk of a chunked request has been received). | + |**`remote_port`**|`int`| The port number at the client side (an integer number between 1 and 65535). | +-- +2.34.1 + + +From e443671abc1e55474ac60ee43a692570c02d8108 Mon Sep 17 00:00:00 2001 +From: Brian +Date: Wed, 11 Oct 2023 20:05:37 -0400 +Subject: [PATCH 040/173] Add CodeQL Workflow for Code Security Analysis + +Add CodeQL Workflow for Code Security Analysis + +This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats. + +We added a new CodeQL workflow file (.github/workflows/codeql.yml) that +- Runs on every push and pull request to the main branch. +- Excludes queries with a high false positive rate or low-severity findings. +- Does not display results for third-party code, focusing only on our own codebase. + +Testing: +To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code. + +Deployment: +Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps: +1. Under the repository name, click on the Security tab. +2. In the left sidebar, click Code scanning alerts. + +Additional Information: +- You can further customize the workflow to adapt to your specific needs by modifying the workflow file. +- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation. + +Signed-off-by: Brian +--- + .github/workflows/codeql-buildscript.sh | 3 + + .github/workflows/codeql.yml | 123 ++++++++++++++++++++++++ + 2 files changed, 126 insertions(+) + create mode 100644 .github/workflows/codeql-buildscript.sh + create mode 100644 .github/workflows/codeql.yml + +diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh +new file mode 100644 +index 00000000..366db622 +--- /dev/null ++++ b/.github/workflows/codeql-buildscript.sh +@@ -0,0 +1,3 @@ ++#!/usr/bin/env bash ++ ++make build WITH_ALL=1 +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +new file mode 100644 +index 00000000..eaa9599b +--- /dev/null ++++ b/.github/workflows/codeql.yml +@@ -0,0 +1,123 @@ ++# For most projects, this workflow file will not need changing; you simply need ++# to commit it to your repository. ++# ++# You may wish to alter this file to override the set of languages analyzed, ++# or to provide custom queries or build logic. ++# ++# ******** NOTE ******** ++# We have attempted to detect the languages in your repository. Please check ++# the `language` matrix defined below to confirm you have the correct set of ++# supported CodeQL languages. ++# ++name: "CodeQL" ++ ++on: ++ push: ++ branches: [ "main", "master" ] ++ pull_request: ++ # The branches below must be a subset of the branches above ++ branches: [ "main", "master" ] ++ schedule: ++ - cron: '28 21 * * 0' ++ ++jobs: ++ analyze: ++ name: Analyze ++ # Runner size impacts CodeQL analysis time. To learn more, please see: ++ # - https://gh.io/recommended-hardware-resources-for-running-codeql ++ # - https://gh.io/supported-runners-and-hardware-resources ++ # - https://gh.io/using-larger-runners ++ # Consider using larger runners for possible analysis time improvements. ++ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }} ++ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} ++ permissions: ++ actions: read ++ contents: read ++ security-events: write ++ ++ strategy: ++ fail-fast: false ++ matrix: ++ language: [ 'cpp' ] ++ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] ++ # Use only 'java' to analyze code written in Java, Kotlin or both ++ # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both ++ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support ++ ++ steps: ++ - name: Checkout repository ++ uses: actions/checkout@v3 ++ with: ++ submodules: recursive ++ ++ # Initializes the CodeQL tools for scanning. ++ - name: Initialize CodeQL ++ uses: github/codeql-action/init@v2 ++ with: ++ languages: ${{ matrix.language }} ++ # If you wish to specify custom queries, you can do so here or in a config file. ++ # By default, queries listed here will override any specified in a config file. ++ # Prefix the list here with "+" to use these queries and those in the config file. ++ ++ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs ++ # queries: security-extended,security-and-quality ++ queries: security-and-quality ++ ++ ++ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). ++ # If this step fails, then you should remove it and run the build manually (see below) ++ #- name: Autobuild ++ # uses: github/codeql-action/autobuild@v2 ++ ++ # ℹ️ Command-line programs to run using the OS shell. ++ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun ++ ++ # If the Autobuild fails above, remove it and uncomment the following three lines. ++ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. ++ ++ - run: | ++ ./.github/workflows/codeql-buildscript.sh ++ ++ - name: Perform CodeQL Analysis ++ uses: github/codeql-action/analyze@v2 ++ with: ++ category: "/language:${{matrix.language}}" ++ upload: false ++ id: step1 ++ ++ # Filter out rules with low severity or high false positve rate ++ # Also filter out warnings in third-party code ++ - name: Filter out unwanted errors and warnings ++ uses: advanced-security/filter-sarif@v1 ++ with: ++ patterns: | ++ -**:cpp/path-injection ++ -**:cpp/world-writable-file-creation ++ -**:cpp/poorly-documented-function ++ -**:cpp/potentially-dangerous-function ++ -**:cpp/use-of-goto ++ -**:cpp/integer-multiplication-cast-to-long ++ -**:cpp/comparison-with-wider-type ++ -**:cpp/leap-year/* ++ -**:cpp/ambiguously-signed-bit-field ++ -**:cpp/suspicious-pointer-scaling ++ -**:cpp/suspicious-pointer-scaling-void ++ -**:cpp/unsigned-comparison-zero ++ -**/third*party/** ++ -**/3rd*party/** ++ -**/external/** ++ input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif ++ output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif ++ ++ - name: Upload SARIF ++ uses: github/codeql-action/upload-sarif@v2 ++ with: ++ sarif_file: ${{ steps.step1.outputs.sarif-output }} ++ category: "/language:${{matrix.language}}" ++ ++ - name: Archive CodeQL results ++ uses: actions/upload-artifact@v3 ++ with: ++ name: codeql-results ++ path: ${{ steps.step1.outputs.sarif-output }} ++ retention-days: 5 +\ No newline at end of file +-- +2.34.1 + + +From a5c11a43f68a66aeac34de0d8848315e6bc2b877 Mon Sep 17 00:00:00 2001 +From: "E. van Putten" +Date: Thu, 12 Oct 2023 13:58:02 +0200 +Subject: [PATCH 041/173] Self-signed certificate key size from 1024 to 2048 + +The current self-signed certificate doesn't work. It will cause an SSL error (EE_KEY_TOO_SMALL) when loading the PEM file. +--- + docs/OpenSSL.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/OpenSSL.md b/docs/OpenSSL.md +index 9822e9a4..f37c6b38 100644 +--- a/docs/OpenSSL.md ++++ b/docs/OpenSSL.md +@@ -46,7 +46,7 @@ One can use the following steps in Windows (in Linux replace "copy" by "cp" + and "type" by "cat"): + +
+-  openssl genrsa -des3 -out server.key 1024
++  openssl genrsa -des3 -out server.key 2048
+ 
+   openssl req -new -key server.key -out server.csr
+ 
+-- 
+2.34.1
+
+
+From 01d1034aa00d4348ab3f8fc3c23fcff79f465115 Mon Sep 17 00:00:00 2001
+From: HypoYoung 
+Date: Wed, 18 Oct 2023 20:43:32 +0800
+Subject: [PATCH 042/173] cmake compile support mbedtls
+
+---
+ CMakeLists.txt     |  5 +++++
+ src/CMakeLists.txt | 25 ++++++++++++++++---------
+ 2 files changed, 21 insertions(+), 9 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index ae00462a..a435a975 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -231,6 +231,9 @@ message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}")
+ option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF)
+ message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}")
+ 
++option(CIVETWEB_ENABLE_MBEDTLS "Use the MbedTls" OFF)
++message(STATUS "SSL support - ${CIVETWEB_ENABLE_MBEDTLS}")
++
+ # Dynamically load or link the SSL libraries
+ cmake_dependent_option(
+   CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING "Dynamically loads the SSL library rather than linking it" ON
+@@ -523,6 +526,8 @@ if (CIVETWEB_ENABLE_MEMORY_DEBUGGING)
+ endif()
+ if (NOT CIVETWEB_ENABLE_SSL)
+   add_definitions(-DNO_SSL)
++elseif (CIVETWEB_ENABLE_MBEDTLS)
++  add_definitions(-DUSE_MBEDTLS)
+ elseif (NOT CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
+   add_definitions(-DNO_SSL_DL)
+ else()
+diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
+index 43f3a77e..47a9b0c8 100644
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -47,16 +47,23 @@ endif()
+ 
+ # We need to link OpenSSL if not dynamically loading
+ if (CIVETWEB_ENABLE_SSL)
+-  if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
+-    find_package(LibDl)
+-    if (LIBDL_FOUND)
+-      target_link_libraries(civetweb-c-library -ldl)
+-    endif()
++  if (CIVETWEB_ENBALE_MBEDTLS)
++    find_package(MbedTLS)
++    include_directories(${MbedTLS_INCLUDE_DIR})
++    message(STATUS "MbedTLS include directory: {MbedTLS_INCLUDE_DIR}")
++    target_link_libraries(civetweb-c-library ${MbedTLS_LIBRARIES})
+   else()
+-    find_package(OpenSSL)
+-    include_directories(${OPENSSL_INCLUDE_DIR})
+-    message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
+-    target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
++    if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
++      find_package(LibDl)
++      if (LIBDL_FOUND)
++        target_link_libraries(civetweb-c-library -ldl)
++      endif()
++    else()
++      find_package(OpenSSL)
++      include_directories(${OPENSSL_INCLUDE_DIR})
++      message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
++      target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
++    endif()
+   endif()
+ endif()
+ 
+-- 
+2.34.1
+
+
+From b70fe7454719dd0635363836b2fde8dc7609eb75 Mon Sep 17 00:00:00 2001
+From: Brian 
+Date: Wed, 18 Oct 2023 16:53:01 -0400
+Subject: [PATCH 043/173] Add CodeQL Workflow for Code Security Analysis
+
+Add CodeQL Workflow for Code Security Analysis
+
+This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.
+
+We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
+- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
+- Runs daily.
+- Excludes queries with a high false positive rate or low-severity findings.
+- Does not display results for git submodules, focusing only on our own codebase.
+
+Testing:
+To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.
+
+Deployment:
+Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
+1. Under the repository name, click on the Security tab.
+2. In the left sidebar, click Code scanning alerts.
+
+Additional Information:
+- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
+- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).
+
+Signed-off-by: Brian 
+---
+ .github/workflows/codeql.yml | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
+index eaa9599b..3ef873fc 100644
+--- a/.github/workflows/codeql.yml
++++ b/.github/workflows/codeql.yml
+@@ -14,11 +14,10 @@ name: "CodeQL"
+ on:
+   push:
+     branches: [ "main", "master" ]
+-  pull_request:
+-    # The branches below must be a subset of the branches above
+-    branches: [ "main", "master" ]
+   schedule:
+-    - cron: '28 21 * * 0'
++    - cron: '0 0 * * *'
++  pull_request:
++    branches: '*'
+ 
+ jobs:
+   analyze:
+@@ -103,21 +102,25 @@ jobs:
+           -**:cpp/suspicious-pointer-scaling
+           -**:cpp/suspicious-pointer-scaling-void
+           -**:cpp/unsigned-comparison-zero
+-          -**/third*party/**
+-          -**/3rd*party/**
+-          -**/external/**
++          -**/cmake*/Modules/**
+         input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
+         output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
+ 
+-    - name: Upload SARIF
++    - name: Upload CodeQL results to code scanning
+       uses: github/codeql-action/upload-sarif@v2
+       with:
+         sarif_file: ${{ steps.step1.outputs.sarif-output }}
+         category: "/language:${{matrix.language}}"
+ 
+-    - name: Archive CodeQL results
++    - name: Upload CodeQL results as an artifact
++      if: success() || failure()
+       uses: actions/upload-artifact@v3
+       with:
+         name: codeql-results
+         path: ${{ steps.step1.outputs.sarif-output }}
+-        retention-days: 5
+\ No newline at end of file
++        retention-days: 5
++
++    - name: Fail if an error is found
++      run: |
++        ./.github/workflows/fail_on_error.py \
++          ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
+-- 
+2.34.1
+
+
+From 22dbe1c2f067d75e6d7de28ac9a91b0718bc6c0b Mon Sep 17 00:00:00 2001
+From: Brian 
+Date: Fri, 20 Oct 2023 01:03:35 -0400
+Subject: [PATCH 044/173] Add CodeQL Workflow for Code Security Analysis
+
+Add CodeQL Workflow for Code Security Analysis
+
+This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.
+
+We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
+- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
+- Runs daily.
+- Excludes queries with a high false positive rate or low-severity findings.
+- Does not display results for git submodules, focusing only on our own codebase.
+
+Testing:
+To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.
+
+Deployment:
+Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
+1. Under the repository name, click on the Security tab.
+2. In the left sidebar, click Code scanning alerts.
+
+Additional Information:
+- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
+- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).
+
+Signed-off-by: Brian 
+---
+ .github/workflows/fail_on_error.py | 34 ++++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+ create mode 100755 .github/workflows/fail_on_error.py
+
+diff --git a/.github/workflows/fail_on_error.py b/.github/workflows/fail_on_error.py
+new file mode 100755
+index 00000000..29791742
+--- /dev/null
++++ b/.github/workflows/fail_on_error.py
+@@ -0,0 +1,34 @@
++#!/usr/bin/env python3
++
++import json
++import sys
++
++# Return whether SARIF file contains error-level results
++def codeql_sarif_contain_error(filename):
++    with open(filename, 'r') as f:
++        s = json.load(f)
++
++    for run in s.get('runs', []):
++        rules_metadata = run['tool']['driver']['rules']
++        if not rules_metadata:
++            rules_metadata = run['tool']['extensions'][0]['rules']
++
++        for res in run.get('results', []):
++            if 'ruleIndex' in res:
++                rule_index = res['ruleIndex']
++            elif 'rule' in res and 'index' in res['rule']:
++                rule_index = res['rule']['index']
++            else:
++                continue
++            try:
++                rule_level = rules_metadata[rule_index]['defaultConfiguration']['level']
++            except IndexError as e:
++                print(e, rule_index, len(rules_metadata))
++            else:
++                if rule_level == 'error':
++                    return True
++    return False
++
++if __name__ == "__main__":
++    if codeql_sarif_contain_error(sys.argv[1]):
++        sys.exit(1)
+-- 
+2.34.1
+
+
+From 9279eb8f6d980a123b543de9e160649e73873cdf Mon Sep 17 00:00:00 2001
+From: Brian 
+Date: Sun, 29 Oct 2023 15:29:20 -0400
+Subject: [PATCH 045/173] Add CodeQL Workflow for Code Security Analysis
+
+Add CodeQL Workflow for Code Security Analysis
+
+This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.
+
+We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
+- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
+- Runs daily.
+- Excludes queries with a high false positive rate or low-severity findings.
+- Does not display results for git submodules, focusing only on our own codebase.
+
+Testing:
+To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.
+
+Deployment:
+Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
+1. Under the repository name, click on the Security tab.
+2. In the left sidebar, click Code scanning alerts.
+
+Additional Information:
+- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
+- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).
+
+Signed-off-by: Brian 
+---
+ .github/workflows/codeql.yml | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
+index 3ef873fc..81649227 100644
+--- a/.github/workflows/codeql.yml
++++ b/.github/workflows/codeql.yml
+@@ -12,8 +12,8 @@
+ name: "CodeQL"
+ 
+ on:
+-  push:
+-    branches: [ "main", "master" ]
++  # push:
++  #   branches: [ "main", "master" ]
+   schedule:
+     - cron: '0 0 * * *'
+   pull_request:
+-- 
+2.34.1
+
+
+From 2d7d597486025feb37faec7826cee7f2c28a7827 Mon Sep 17 00:00:00 2001
+From: DL6ER 
+Date: Tue, 31 Oct 2023 08:47:45 +0100
+Subject: [PATCH 046/173] Truly allow all non-control characters in URIs
+
+Signed-off-by: DL6ER 
+---
+ src/civetweb.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index df161c0b..7e869887 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -10843,8 +10843,9 @@ static int
+ skip_to_end_of_word_and_terminate(char **ppw, int eol)
+ {
+ 	/* Forward until a space is found - use isgraph here */
++	/* Extended ASCII characters are also treated as word characters. */
+ 	/* See http://www.cplusplus.com/reference/cctype/ */
+-	while (isgraph((unsigned char)**ppw)) {
++	while ((unsigned char)**ppw > 127 || isgraph((unsigned char)**ppw)) {
+ 		(*ppw)++;
+ 	}
+ 
+@@ -18639,7 +18640,7 @@ get_uri_type(const char *uri)
+ 	 * and % encoded symbols.
+ 	 */
+ 	for (i = 0; uri[i] != 0; i++) {
+-		if (uri[i] < 33) {
++		if ((unsigned char)uri[i] < 33) {
+ 			/* control characters and spaces are invalid */
+ 			return 0;
+ 		}
+-- 
+2.34.1
+
+
+From 8a15283648c5fdba0120544e41759a40fd39b42e Mon Sep 17 00:00:00 2001
+From: DL6ER 
+Date: Tue, 31 Oct 2023 08:42:58 +0100
+Subject: [PATCH 047/173] struct mbedtls_ssl_context->state is only available
+ using the macro MBEDTLS_PRIVATE(state)
+
+Signed-off-by: DL6ER 
+---
+ src/mod_mbedtls.inl | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl
+index 822fc2e0..cada215e 100644
+--- a/src/mod_mbedtls.inl
++++ b/src/mod_mbedtls.inl
+@@ -188,7 +188,11 @@ mbed_ssl_accept(mbedtls_ssl_context **ssl,
+ 		return -1;
+ 	}
+ 
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000
++	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->MBEDTLS_PRIVATE(state));
++#else
+ 	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->state);
++#endif
+ 	return 0;
+ }
+ 
+@@ -214,7 +218,11 @@ mbed_ssl_handshake(mbedtls_ssl_context *ssl)
+ 		}
+ 	}
+ 
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000
++	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->MBEDTLS_PRIVATE(state));
++#else
+ 	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->state);
++#endif
+ 	return rc;
+ }
+ 
+-- 
+2.34.1
+
+
+From 3afcd237e509a6700c449056dcc5b9092af31e6a Mon Sep 17 00:00:00 2001
+From: Fabrice Fontaine 
+Date: Mon, 6 Nov 2023 21:38:02 +0100
+Subject: [PATCH 048/173] resources/Makefile.in-lua: fix build with Lua 5.1
+
+Fix the following build failure with Lua 5.1 raised since version 1.16
+and
+https://github.com/civetweb/civetweb/commit/82ba5a04c9a1ca980d8dfe00c18e086017e8ab25:
+
+/home/buildroot/autobuild/instance-1/output-1/host/opt/ext-toolchain/bin/../lib/gcc/sh4-buildroot-linux-gnu/13.2.0/../../../../sh4-buildroot-linux-gnu/bin/ld: out/src/third_party/lua_struct.o: in function `luaopen_struct':
+lua_struct.c:(.text+0xce4): undefined reference to `luaL_newlib'
+
+Fixes:
+ - http://autobuild.buildroot.org/results/7459b504e52f473c5830c0f3c7bffd037f6e1770
+
+Signed-off-by: Fabrice Fontaine 
+---
+ resources/Makefile.in-lua | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/resources/Makefile.in-lua b/resources/Makefile.in-lua
+index d3038f66..45f80181 100644
+--- a/resources/Makefile.in-lua
++++ b/resources/Makefile.in-lua
+@@ -150,6 +150,7 @@ CFLAGS += -DUSE_LUA_FILE_SYSTEM
+ #SOURCE_DIRS = $(LFS_DIR)
+ 
+ 
++ifneq ($(WITH_LUA_VERSION), 501)
+ LXX_DIR = src/third_party
+ LXX_SOURCE_FILES = lua_struct.c
+ LXX_SOURCES = $(addprefix $(LXX_DIR)/, $(LXX_SOURCE_FILES))
+@@ -159,6 +160,7 @@ OBJECTS += $(LXX_OBJECTS)
+ CFLAGS += $(LXX_CFLAGS)
+ CFLAGS += -DUSE_LUA_STRUCT
+ #SOURCE_DIRS = $(LXX_DIR)
++endif
+ 
+ 
+ ifneq ($(WITH_LUA_VERSION), 501)
+-- 
+2.34.1
+
+
+From 6b1aed0962db94cde10da8a769be7db6b461cdc6 Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Sun, 19 Nov 2023 13:51:06 +0100
+Subject: [PATCH 049/173] Format code
+
+---
+ src/civetweb.c      | 204 +++++++++++++++++++++++++++-----------------
+ src/mod_mbedtls.inl |   8 +-
+ 2 files changed, 130 insertions(+), 82 deletions(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 7e869887..c8df640a 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -1921,8 +1921,9 @@ struct socket {
+ 	unsigned char is_ssl;    /* Is port SSL-ed */
+ 	unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
+ 	                          * port */
+-	unsigned char is_optional; /* Shouldn't cause us to exit if we can't bind to it */
+-	unsigned char in_use;    /* 0: invalid, 1: valid, 2: free */
++	unsigned char
++	    is_optional; /* Shouldn't cause us to exit if we can't bind to it */
++	unsigned char in_use; /* 0: invalid, 1: valid, 2: free */
+ };
+ 
+ 
+@@ -2399,17 +2400,18 @@ struct mg_context {
+ 	stop_flag_t stop_flag;        /* Should we stop event loop */
+ 	pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
+ 
+-	pthread_t masterthreadid; /* The master thread ID */
+-	unsigned int
+-	    cfg_max_worker_threads;  /* How many worker-threads we are allowed to create, total */
++	pthread_t masterthreadid;            /* The master thread ID */
++	unsigned int cfg_max_worker_threads; /* How many worker-threads we are
++	                                        allowed to create, total */
+ 
++	unsigned int spawned_worker_threads; /* How many worker-threads currently
++	                                        exist (modified by master thread) */
+ 	unsigned int
+-	    spawned_worker_threads;  /* How many worker-threads currently exist (modified by master thread) */
+-	unsigned int
+-	    idle_worker_thread_count; /* How many worker-threads are currently sitting around with nothing to do */
+-	                              /* Access to this value MUST be synchronized by thread_mutex */
++	    idle_worker_thread_count; /* How many worker-threads are currently
++	                                 sitting around with nothing to do */
++	/* Access to this value MUST be synchronized by thread_mutex */
+ 
+-	pthread_t *worker_threadids; /* The worker thread IDs */
++	pthread_t *worker_threadids;      /* The worker thread IDs */
+ 	unsigned long starter_thread_idx; /* thread index which called mg_start */
+ 
+ 	/* Connection to thread dispatching */
+@@ -4205,7 +4207,8 @@ send_cors_header(struct mg_connection *conn)
+ 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
+ 	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
+ 		/* Cross-origin resource sharing (CORS), see
+-		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials */
++		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
++		 */
+ 		mg_response_header_add(conn,
+ 		                       "Access-Control-Allow-Credentials",
+ 		                       cors_cred_cfg,
+@@ -4215,30 +4218,29 @@ send_cors_header(struct mg_connection *conn)
+ 	const char *cors_hdr_cfg =
+ 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
+ 	if (cors_hdr_cfg && *cors_hdr_cfg) {
+-	   mg_response_header_add(conn,
+-	                          "Access-Control-Allow-Headers",
+-	                          cors_hdr_cfg,
+-	                          -1);
++		mg_response_header_add(conn,
++		                       "Access-Control-Allow-Headers",
++		                       cors_hdr_cfg,
++		                       -1);
+ 	}
+ 
+ 	const char *cors_exphdr_cfg =
+-	      conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
++	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
+ 	if (cors_exphdr_cfg && *cors_exphdr_cfg) {
+-	   mg_response_header_add(conn,
+-	                          "Access-Control-Expose-Headers",
+-	                          cors_exphdr_cfg,
+-	                          -1);
++		mg_response_header_add(conn,
++		                       "Access-Control-Expose-Headers",
++		                       cors_exphdr_cfg,
++		                       -1);
+ 	}
+ 
+ 	const char *cors_meth_cfg =
+-	      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
++	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
+ 	if (cors_meth_cfg && *cors_meth_cfg) {
+-	   mg_response_header_add(conn,
+-	                          "Access-Control-Allow-Methods",
+-	                          cors_meth_cfg,
+-	                          -1);
++		mg_response_header_add(conn,
++		                       "Access-Control-Allow-Methods",
++		                       cors_meth_cfg,
++		                       -1);
+ 	}
+-
+ }
+ 
+ 
+@@ -15038,19 +15040,19 @@ handle_request(struct mg_connection *conn)
+ 			          suggest_connection_header(conn));
+ 
+ 			const char *cors_cred_cfg =
+-			      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
++			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
+ 			if (cors_cred_cfg && *cors_cred_cfg) {
+-			   mg_printf(conn,
+-			             "Access-Control-Allow-Credentials: %s\r\n",
+-			             cors_cred_cfg);
++				mg_printf(conn,
++				          "Access-Control-Allow-Credentials: %s\r\n",
++				          cors_cred_cfg);
+ 			}
+ 
+ 			const char *cors_exphdr_cfg =
+-			      conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
++			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
+ 			if (cors_exphdr_cfg && *cors_exphdr_cfg) {
+-			   mg_printf(conn,
+-			             "Access-Control-Expose-Headers: %s\r\n",
+-			             cors_exphdr_cfg);
++				mg_printf(conn,
++				          "Access-Control-Expose-Headers: %s\r\n",
++				          cors_exphdr_cfg);
+ 			}
+ 
+ 			if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) {
+@@ -15844,31 +15846,37 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
+ 	}
+ 
+ 	/* sscanf and the option splitting code ensure the following condition
+-	 * Make sure the port is valid and vector ends with the port, 'o', 's', or 'r' */
++	 * Make sure the port is valid and vector ends with the port, 'o', 's', or
++	 * 'r' */
+ 	if ((len > 0) && (is_valid_port(port))) {
+ 		int bad_suffix = 0;
+ 
+ 		/* Parse any suffix character(s) after the port number */
+-		for (size_t i=len; ilen; i++)
+-		{
+-			unsigned char * opt = NULL;
+-			switch(vec->ptr[i])
+-			{
+-				case 'o': opt = &so->is_optional; break;
+-				case 'r': opt = &so->ssl_redir;   break;
+-				case 's': opt = &so->is_ssl;      break;
+-				default:  /* empty */             break;
++		for (size_t i = len; i < vec->len; i++) {
++			unsigned char *opt = NULL;
++			switch (vec->ptr[i]) {
++			case 'o':
++				opt = &so->is_optional;
++				break;
++			case 'r':
++				opt = &so->ssl_redir;
++				break;
++			case 's':
++				opt = &so->is_ssl;
++				break;
++			default: /* empty */
++				break;
+ 			}
+ 
+-			if ((opt)&&(*opt == 0)) *opt = 1;
+-			else
+-			{
++			if ((opt) && (*opt == 0))
++				*opt = 1;
++			else {
+ 				bad_suffix = 1;
+ 				break;
+ 			}
+ 		}
+ 
+-		if ((bad_suffix == 0)&&((so->is_ssl == 0)||(so->ssl_redir == 0))) {
++		if ((bad_suffix == 0) && ((so->is_ssl == 0) || (so->ssl_redir == 0))) {
+ 			return 1;
+ 		}
+ 	}
+@@ -15924,8 +15932,11 @@ is_ssl_port_used(const char *ports)
+ 
+ 		for (i = 0; i < portslen; i++) {
+ 			if (prevIsNumber) {
+-				int suffixCharIdx = (ports[i] == 'o') ? (i+1) : i;  /* allow "os" and "or" suffixes */
+-				if (ports[suffixCharIdx] == 's' || ports[suffixCharIdx] == 'r') {
++				int suffixCharIdx = (ports[i] == 'o')
++				                        ? (i + 1)
++				                        : i; /* allow "os" and "or" suffixes */
++				if (ports[suffixCharIdx] == 's'
++				    || ports[suffixCharIdx] == 'r') {
+ 					return 1;
+ 				}
+ 			}
+@@ -16111,7 +16122,8 @@ set_ports_option(struct mg_context *phys_ctx)
+ 				closesocket(so.sock);
+ 				so.sock = INVALID_SOCKET;
+ 				if (so.is_optional) {
+-					portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */
++					portsOk++; /* it's okay if we couldn't bind, this port is
++					              optional anyway */
+ 				}
+ 				continue;
+ 			}
+@@ -16130,7 +16142,8 @@ set_ports_option(struct mg_context *phys_ctx)
+ 				closesocket(so.sock);
+ 				so.sock = INVALID_SOCKET;
+ 				if (so.is_optional) {
+-					portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */
++					portsOk++; /* it's okay if we couldn't bind, this port is
++					              optional anyway */
+ 				}
+ 				continue;
+ 			}
+@@ -16149,7 +16162,8 @@ set_ports_option(struct mg_context *phys_ctx)
+ 				closesocket(so.sock);
+ 				so.sock = INVALID_SOCKET;
+ 				if (so.is_optional) {
+-					portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */
++					portsOk++; /* it's okay if we couldn't bind, this port is
++					              optional anyway */
+ 				}
+ 				continue;
+ 			}
+@@ -18847,7 +18861,8 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
+ 			            ebuf,
+ 			            ebuf_len,
+ 			            "%s",
+-			            conn->request_len == -3 ? "Request timeout" : "Malformed message");
++			            conn->request_len == -3 ? "Request timeout"
++			                                    : "Malformed message");
+ 			*err = 400;
+ 		} else {
+ 			/* Server did not recv anything -> just close the connection */
+@@ -19779,7 +19794,9 @@ process_new_connection(struct mg_connection *conn)
+ #endif
+ }
+ 
+-static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads);  /* forward declaration */
++static int
++mg_start_worker_thread(struct mg_context *ctx,
++                       int only_if_no_idle_threads); /* forward declaration */
+ 
+ #if defined(ALTERNATIVE_QUEUE)
+ 
+@@ -19788,7 +19805,9 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
+ {
+ 	unsigned int i;
+ 
+-	(void)mg_start_worker_thread(ctx, 1);  /* will start a worker-thread only if there aren't currently any idle worker-threads */
++	(void)mg_start_worker_thread(
++	    ctx, 1); /* will start a worker-thread only if there aren't currently
++	                any idle worker-threads */
+ 
+ 	while (!ctx->stop_flag) {
+ 		for (i = 0; i < ctx->spawned_worker_threads; i++) {
+@@ -19816,11 +19835,16 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
+ 
+ 
+ static int
+-consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented)
++consume_socket(struct mg_context *ctx,
++               struct socket *sp,
++               int thread_index,
++               int counter_was_preincremented)
+ {
+ 	DEBUG_TRACE("%s", "going idle");
+ 	(void)pthread_mutex_lock(&ctx->thread_mutex);
+-	if (counter_was_preincremented == 0) {  /* first call only: the master-thread pre-incremented this before he spawned us */
++	if (counter_was_preincremented
++	    == 0) { /* first call only: the master-thread pre-incremented this
++		           before he spawned us */
+ 		ctx->idle_worker_thread_count++;
+ 	}
+ 	ctx->client_socks[thread_index].in_use = 2;
+@@ -19854,13 +19878,18 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int
+ 
+ /* Worker threads take accepted socket from the queue */
+ static int
+-consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented)
++consume_socket(struct mg_context *ctx,
++               struct socket *sp,
++               int thread_index,
++               int counter_was_preincremented)
+ {
+ 	(void)thread_index;
+ 
+ 	DEBUG_TRACE("%s", "going idle");
+ 	(void)pthread_mutex_lock(&ctx->thread_mutex);
+-	if (counter_was_preincremented == 0) {  /* first call only: the master-thread pre-incremented this before he spawned us */
++	if (counter_was_preincremented
++	    == 0) { /* first call only: the master-thread pre-incremented this
++		           before he spawned us */
+ 		ctx->idle_worker_thread_count++;
+ 	}
+ 
+@@ -19935,7 +19964,9 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
+ 	(void)pthread_cond_signal(&ctx->sq_full);
+ 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
+ 
+-	(void)mg_start_worker_thread(ctx, 1);  /* will start a worker-thread only if there aren't currently any idle worker-threads */
++	(void)mg_start_worker_thread(
++	    ctx, 1); /* will start a worker-thread only if there aren't currently
++	                any idle worker-threads */
+ }
+ #endif /* ALTERNATIVE_QUEUE */
+ 
+@@ -20013,7 +20044,8 @@ worker_thread_run(struct mg_connection *conn)
+ 	/* Call consume_socket() even when ctx->stop_flag > 0, to let it
+ 	 * signal sq_empty condvar to wake up the master waiting in
+ 	 * produce_socket() */
+-	while (consume_socket(ctx, &conn->client, thread_index, first_call_to_consume_socket)) {
++	while (consume_socket(
++	    ctx, &conn->client, thread_index, first_call_to_consume_socket)) {
+ 		first_call_to_consume_socket = 0;
+ 
+ 		/* New connections must start with new protocol negotiation */
+@@ -20791,34 +20823,43 @@ mg_socketpair(int *sockA, int *sockB)
+ #endif
+ }
+ 
+-static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads) {
++static int
++mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
++{
+ 	const unsigned int i = ctx->spawned_worker_threads;
+ 	if (i >= ctx->cfg_max_worker_threads) {
+-		return -1;  /* Oops, we hit our worker-thread limit!  No more worker threads, ever! */
++		return -1; /* Oops, we hit our worker-thread limit!  No more worker
++		              threads, ever! */
+ 	}
+ 
+ 	(void)pthread_mutex_lock(&ctx->thread_mutex);
+ #if defined(ALTERNATIVE_QUEUE)
+-	if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) {
++	if ((only_if_no_idle_threads) && (ctx->idle_worker_thread_count > 0)) {
+ #else
+-	if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > (unsigned)(ctx->sq_head-ctx->sq_tail))) {
++	if ((only_if_no_idle_threads)
++	    && (ctx->idle_worker_thread_count
++	        > (unsigned)(ctx->sq_head - ctx->sq_tail))) {
+ #endif
+ 		(void)pthread_mutex_unlock(&ctx->thread_mutex);
+-		return -2;  /* There are idle threads available, so no need to spawn a new worker thread now */
++		return -2; /* There are idle threads available, so no need to spawn a
++		              new worker thread now */
+ 	}
+-	ctx->idle_worker_thread_count++;  /* we do this here to avoid a race condition while the thread is starting up */
++	ctx->idle_worker_thread_count++; /* we do this here to avoid a race
++	                                    condition while the thread is starting
++	                                    up */
+ 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
+ 
+ 	ctx->worker_connections[i].phys_ctx = ctx;
+ 	int ret = mg_start_thread_with_id(worker_thread,
+-	                            &ctx->worker_connections[i],
+-	                            &ctx->worker_threadids[i]);
++	                                  &ctx->worker_connections[i],
++	                                  &ctx->worker_threadids[i]);
+ 	if (ret == 0) {
+-		ctx->spawned_worker_threads++;  /* note that we've filled another slot in the table */
++		ctx->spawned_worker_threads++; /* note that we've filled another slot in
++		                                  the table */
+ 		DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads);
+ 	} else {
+ 		(void)pthread_mutex_lock(&ctx->thread_mutex);
+-		ctx->idle_worker_thread_count--;  /* whoops, roll-back on error */
++		ctx->idle_worker_thread_count--; /* whoops, roll-back on error */
+ 		(void)pthread_mutex_unlock(&ctx->thread_mutex);
+ 	}
+ 	return ret;
+@@ -21081,11 +21122,13 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
+ #endif
+ 
+ 	/* Worker thread count option */
+-	workerthreadcount   = atoi(ctx->dd.config[NUM_THREADS]);
++	workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
+ 	prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]);
+ 
+-	if ((prespawnthreadcount < 0)||(prespawnthreadcount > workerthreadcount)) {
+-		prespawnthreadcount = workerthreadcount;  /* can't prespawn more than all of them! */
++	if ((prespawnthreadcount < 0)
++	    || (prespawnthreadcount > workerthreadcount)) {
++		prespawnthreadcount =
++		    workerthreadcount; /* can't prespawn more than all of them! */
+ 	}
+ 
+ 	if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) {
+@@ -21352,9 +21395,10 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
+ 	}
+ 
+ 	ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount));
+-	ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
+-	                                                   sizeof(pthread_t),
+-	                                                   ctx);
++	ctx->worker_threadids =
++	    (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
++	                               sizeof(pthread_t),
++	                               ctx);
+ 
+ 	if (ctx->worker_threadids == NULL) {
+ 		const char *err_msg = "Not enough memory for worker thread ID array";
+@@ -21362,8 +21406,8 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
+ 
+ 		if (error != NULL) {
+ 			error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
+-			error->code_sub =
+-			    (unsigned)ctx->cfg_max_worker_threads * (unsigned)sizeof(pthread_t);
++			error->code_sub = (unsigned)ctx->cfg_max_worker_threads
++			                  * (unsigned)sizeof(pthread_t);
+ 			mg_snprintf(NULL,
+ 			            NULL, /* No truncation check for error buffers */
+ 			            error->text,
+diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl
+index cada215e..c0333b21 100644
+--- a/src/mod_mbedtls.inl
++++ b/src/mod_mbedtls.inl
+@@ -189,7 +189,9 @@ mbed_ssl_accept(mbedtls_ssl_context **ssl,
+ 	}
+ 
+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000
+-	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->MBEDTLS_PRIVATE(state));
++	DEBUG_TRACE("TLS connection %p accepted, state: %d",
++	            ssl,
++	            (*ssl)->MBEDTLS_PRIVATE(state));
+ #else
+ 	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->state);
+ #endif
+@@ -219,7 +221,9 @@ mbed_ssl_handshake(mbedtls_ssl_context *ssl)
+ 	}
+ 
+ #if MBEDTLS_VERSION_NUMBER >= 0x03000000
+-	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->MBEDTLS_PRIVATE(state));
++	DEBUG_TRACE("TLS handshake rc: %d, state: %d",
++	            rc,
++	            ssl->MBEDTLS_PRIVATE(state));
+ #else
+ 	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->state);
+ #endif
+-- 
+2.34.1
+
+
+From 56ba821ed207c475cf81676f19d707aba46e9f4d Mon Sep 17 00:00:00 2001
+From: The-EG <>
+Date: Sun, 19 Nov 2023 14:07:36 +0100
+Subject: [PATCH 050/173] Better error handling in Lua scripts
+
+(Merge code change from #1213)
+---
+ src/mod_lua.inl | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/mod_lua.inl b/src/mod_lua.inl
+index f1070d58..9a2164c3 100644
+--- a/src/mod_lua.inl
++++ b/src/mod_lua.inl
+@@ -3068,9 +3068,14 @@ mg_exec_lua_script(struct mg_connection *conn,
+ 		}
+ 
+ 		if (luaL_loadfile(L, path) != 0) {
++			mg_send_http_error(conn, 500, "Lua error:\r\n");
+ 			lua_error_handler(L);
+ 		} else {
+-			lua_pcall(L, 0, 0, -2);
++			int call_status = lua_pcall(L, 0, 0, 0);
++			if (call_status != 0) {
++				mg_send_http_error(conn, 500, "Lua error:\r\n");
++				lua_error_handler(L);
++			}
+ 		}
+ 		DEBUG_TRACE("Close Lua environment %p", L);
+ 		lua_close(L);
+-- 
+2.34.1
+
+
+From 05b95ea4f97387faec1d2d1f0b769aaa715ca2d9 Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Sun, 19 Nov 2023 19:31:54 +0100
+Subject: [PATCH 051/173] Add shutdown sockets only for server
+
+Test possible fix for #1214
+---
+ src/civetweb.c | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index c8df640a..b45a264c 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -9672,8 +9672,9 @@ connect_socket(
+ 		/* Data for poll */
+ 		struct mg_pollfd pfd[2];
+ 		int pollres;
+-		int ms_wait = 10000;     /* 10 second timeout */
+-		stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */
++		int ms_wait = 10000;       /* 10 second timeout */
++		stop_flag_t nonstop = 0;   /* STOP_FLAG_ASSIGN(&nonstop, 0); */
++		unsigned int num_sock = 1; /* use one or two sockets */
+ 
+ 		/* For a non-blocking socket, the connect sequence is:
+ 		 * 1) call connect (will not block)
+@@ -9683,13 +9684,14 @@ connect_socket(
+ 		pfd[0].fd = *sock;
+ 		pfd[0].events = POLLOUT;
+ 
+-		pfd[1].fd = ctx ? ctx->thread_shutdown_notification_socket : -1;
+-		pfd[1].events = POLLIN;
++		if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
++			pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
++			pfd[num_sock].events = POLLIN;
++			num_sock++;
++		}
+ 
+-		pollres = mg_poll(pfd,
+-		                  ctx ? 2 : 1,
+-		                  ms_wait,
+-		                  ctx ? &(ctx->stop_flag) : &nonstop);
++		pollres =
++		    mg_poll(pfd, num_sock, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
+ 
+ 		if (pollres != 1) {
+ 			/* Not connected */
+-- 
+2.34.1
+
+
+From 9da7ba333fef54db11cadab2a92ef78575672d1a Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Sun, 19 Nov 2023 19:33:54 +0100
+Subject: [PATCH 052/173] Appveyor build issue: Remove empty line in appveyor
+ file
+
+---
+ appveyor.yml   |  1 -
+ src/civetweb.c | 16 +++++++++++-----
+ 2 files changed, 11 insertions(+), 6 deletions(-)
+
+diff --git a/appveyor.yml b/appveyor.yml
+index 6008c5ee..fb936d1f 100644
+--- a/appveyor.yml
++++ b/appveyor.yml
+@@ -463,7 +463,6 @@ before_build:
+     -DCIVETWEB_SSL_OPENSSL_API_1_0=NO
+     -DCIVETWEB_SSL_OPENSSL_API_1_1=YES
+     -DCIVETWEB_SSL_OPENSSL_API_3_0=NO
+-
+     "%source_path%"
+   - powershell Push-AppveyorArtifact CMakeCache.txt
+   - cd "%source_path%"
+diff --git a/src/civetweb.c b/src/civetweb.c
+index b45a264c..34b4f661 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -4179,6 +4179,8 @@ send_additional_header(struct mg_connection *conn)
+ 	}
+ #endif
+ 
++	// Content-Security-Policy
++
+ 	if (header && header[0]) {
+ 		mg_response_header_add_lines(conn, header);
+ 	}
+@@ -7724,21 +7726,25 @@ substitute_index_file(struct mg_connection *conn,
+ 		if ((root_prefix) && (fallback_root_prefix)) {
+ 			const size_t root_prefix_len = strlen(root_prefix);
+ 			if ((strncmp(path, root_prefix, root_prefix_len) == 0)) {
++				char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
++				                                  side effects if we fail */
++				size_t sub_path_len;
++
+ 				const size_t fallback_root_prefix_len =
+ 				    strlen(fallback_root_prefix);
+ 				const char *sub_path = path + root_prefix_len;
+-				while (*sub_path == '/')
++				while (*sub_path == '/') {
+ 					sub_path++;
+-				const size_t sub_path_len = strlen(sub_path);
++				}
++				sub_path_len = strlen(sub_path);
+ 
+-				char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
+-				                                     side effects if we fail */
+ 				if (((fallback_root_prefix_len + 1 + sub_path_len + 1)
+ 				     < sizeof(scratch_path))) {
+ 					/* The concatenations below are all safe because we
+ 					 * pre-verified string lengths above */
++					char *nul;
+ 					strcpy(scratch_path, fallback_root_prefix);
+-					char *nul = strchr(scratch_path, '\0');
++					nul = strchr(scratch_path, '\0');
+ 					if ((nul > scratch_path) && (*(nul - 1) != '/')) {
+ 						*nul++ = '/';
+ 						*nul = '\0';
+-- 
+2.34.1
+
+
+From a4d305373d2bf884292d31c5ee28050f5d9eedad Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Wed, 22 Nov 2023 07:39:01 +0100
+Subject: [PATCH 053/173] Fixes for old coding standard
+
+---
+ src/civetweb.c | 31 +++++++++++++++++--------------
+ 1 file changed, 17 insertions(+), 14 deletions(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 34b4f661..0e80e77a 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -4193,6 +4193,14 @@ send_cors_header(struct mg_connection *conn)
+ 	const char *origin_hdr = mg_get_header(conn, "Origin");
+ 	const char *cors_orig_cfg =
+ 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
++	const char *cors_cred_cfg =
++	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
++	const char *cors_hdr_cfg =
++	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
++	const char *cors_exphdr_cfg =
++	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
++	const char *cors_meth_cfg =
++	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
+ 
+ 	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
+ 		/* Cross-origin resource sharing (CORS), see
+@@ -4205,8 +4213,6 @@ send_cors_header(struct mg_connection *conn)
+ 		                       -1);
+ 	}
+ 
+-	const char *cors_cred_cfg =
+-	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
+ 	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
+ 		/* Cross-origin resource sharing (CORS), see
+ 		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
+@@ -4217,8 +4223,6 @@ send_cors_header(struct mg_connection *conn)
+ 		                       -1);
+ 	}
+ 
+-	const char *cors_hdr_cfg =
+-	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
+ 	if (cors_hdr_cfg && *cors_hdr_cfg) {
+ 		mg_response_header_add(conn,
+ 		                       "Access-Control-Allow-Headers",
+@@ -4226,8 +4230,6 @@ send_cors_header(struct mg_connection *conn)
+ 		                       -1);
+ 	}
+ 
+-	const char *cors_exphdr_cfg =
+-	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
+ 	if (cors_exphdr_cfg && *cors_exphdr_cfg) {
+ 		mg_response_header_add(conn,
+ 		                       "Access-Control-Expose-Headers",
+@@ -4235,8 +4237,6 @@ send_cors_header(struct mg_connection *conn)
+ 		                       -1);
+ 	}
+ 
+-	const char *cors_meth_cfg =
+-	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
+ 	if (cors_meth_cfg && *cors_meth_cfg) {
+ 		mg_response_header_add(conn,
+ 		                       "Access-Control-Allow-Methods",
+@@ -7793,6 +7793,7 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
+ 	ptrdiff_t match_len;
+ 	char gz_path[UTF8_PATH_MAX];
+ 	int truncated;
++	int i;
+ #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
+ 	char *tmp_str;
+ 	size_t tmp_str_len, sep_pos;
+@@ -7849,7 +7850,7 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
+ 		return;
+ 	}
+ 
+-	for (int i = 0; roots[i] != NULL; i++) {
++	for (i = 0; roots[i] != NULL; i++) {
+ 		/* Step 6: Determine the local file path from the root path and the
+ 		 * request uri. */
+ 		/* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
+@@ -15033,6 +15034,10 @@ handle_request(struct mg_connection *conn)
+ 			    get_header(ri->http_headers,
+ 			               ri->num_headers,
+ 			               "Access-Control-Request-Headers");
++			const char *cors_cred_cfg =
++			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
++			const char *cors_exphdr_cfg =
++			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
+ 
+ 			gmt_time_string(date, sizeof(date), &curtime);
+ 			mg_printf(conn,
+@@ -15047,16 +15052,12 @@ handle_request(struct mg_connection *conn)
+ 			          ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
+ 			          suggest_connection_header(conn));
+ 
+-			const char *cors_cred_cfg =
+-			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
+ 			if (cors_cred_cfg && *cors_cred_cfg) {
+ 				mg_printf(conn,
+ 				          "Access-Control-Allow-Credentials: %s\r\n",
+ 				          cors_cred_cfg);
+ 			}
+ 
+-			const char *cors_exphdr_cfg =
+-			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
+ 			if (cors_exphdr_cfg && *cors_exphdr_cfg) {
+ 				mg_printf(conn,
+ 				          "Access-Control-Expose-Headers: %s\r\n",
+@@ -20774,6 +20775,7 @@ static int
+ mg_socketpair(int *sockA, int *sockB)
+ {
+ 	int temp[2] = {-1, -1};
++	int asock = -1;
+ 
+ 	/** Default to unallocated */
+ 	*sockA = -1;
+@@ -20787,11 +20789,12 @@ mg_socketpair(int *sockA, int *sockB)
+ 		set_close_on_exec(*sockA, NULL, NULL);
+ 		set_close_on_exec(*sockB, NULL, NULL);
+ 	}
++	(void)asock; /* not used */
+ 	return ret;
+ #else
+ 	/** No socketpair() call is available, so we'll have to roll our own
+ 	 * implementation */
+-	int asock = socket(PF_INET, SOCK_STREAM, 0);
++	asock = socket(PF_INET, SOCK_STREAM, 0);
+ 	if (asock >= 0) {
+ 		struct sockaddr_in addr;
+ 		struct sockaddr *pa = (struct sockaddr *)&addr;
+-- 
+2.34.1
+
+
+From 3da9aeaed53c2acd15445b7ba898fe91a989d795 Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Mon, 4 Dec 2023 08:32:52 +0100
+Subject: [PATCH 054/173] Add wasm mime type
+
+Fixes #1217
+---
+ src/civetweb.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 0e80e77a..4cb8f485 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -8406,6 +8406,7 @@ static const struct {
+     {".pdf", 4, "application/pdf"},
+     {".ps", 3, "application/postscript"},
+     {".rtf", 4, "application/rtf"},
++    {".wasm", 5, "application/wasm"},
+     {".xhtml", 6, "application/xhtml+xml"},
+     {".xsl", 4, "application/xml"},
+     {".xslt", 5, "application/xml"},
+-- 
+2.34.1
+
+
+From 08b7241f4bce808c3a932a5fddc1181acee5c0c1 Mon Sep 17 00:00:00 2001
+From: Sergey Linev 
+Date: Mon, 11 Dec 2023 10:57:43 +0100
+Subject: [PATCH 055/173] [cmake] let enable X DOM sockets
+
+Missing in cmake
+---
+ CMakeLists.txt | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index a435a975..44cebaec 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -88,6 +88,11 @@ message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}")
+ option(CIVETWEB_ENABLE_WEBSOCKETS "Enable websockets connections" OFF)
+ message(STATUS "Websockets support - ${CIVETWEB_ENABLE_WEBSOCKETS}")
+ 
++# X DOM sockets support
++option(CIVETWEB_ENABLE_X_DOM_SOCKET "Enable X DOM sockets support" OFF)
++message(STATUS "X DOM sockets support - ${CIVETWEB_ENABLE_X_DOM_SOCKET}")
++
++
+ # Server statistics support
+ option(CIVETWEB_ENABLE_SERVER_STATS "Enable server statistics" OFF)
+ message(STATUS "Server statistics support - ${CIVETWEB_ENABLE_SERVER_STATS}")
+@@ -497,6 +502,9 @@ endif()
+ if (CIVETWEB_ENABLE_WEBSOCKETS)
+   add_definitions(-DUSE_WEBSOCKET)
+ endif()
++if (CIVETWEB_ENABLE_X_DOM_SOCKET)
++   add_definitions(-DUSE_X_DOM_SOCKET)
++endif()
+ if (CIVETWEB_ENABLE_SERVER_STATS)
+   add_definitions(-DUSE_SERVER_STATS)
+ endif()
+@@ -632,13 +640,13 @@ configure_file(
+ )
+ 
+ install(
+-  FILES 
++  FILES
+     "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc"
+     DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
+ )
+ 
+ install(
+-  FILES 
++  FILES
+     "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-cpp.pc"
+     DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
+ )
+-- 
+2.34.1
+
+
+From 19b70adc70ca66cc862f3fd065e354af64b7b011 Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Sat, 30 Dec 2023 11:18:38 +0100
+Subject: [PATCH 056/173] websocket client "magic" key should be random
+
+Fixes #1220
+---
+ CREDITS.md               |  1 +
+ src/civetweb.c           | 21 ++++++++++++++++++++-
+ unittest/public_server.c |  2 +-
+ 3 files changed, 22 insertions(+), 2 deletions(-)
+
+diff --git a/CREDITS.md b/CREDITS.md
+index fc98b637..f1e1d150 100644
+--- a/CREDITS.md
++++ b/CREDITS.md
+@@ -225,6 +225,7 @@
+ * Torben Jonas
+ * Uilian Ries
+ * Ulrich Hertlein
++* videofan3d
+ * Walt Steverson
+ * wangli28
+ * webxer
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 4cb8f485..be4f4cde 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -19243,6 +19243,24 @@ websocket_client_thread(void *data)
+ #endif
+ 
+ 
++#if defined(USE_WEBSOCKET)
++static void
++generate_websocket_magic(char *magic25)
++{
++	uint64_t rnd;
++	unsigned char buffer[2 * sizeof(rnd)];
++
++	rnd = get_random();
++	memcpy(buffer, &rnd, sizeof(rnd));
++	rnd = get_random();
++	memcpy(buffer + sizeof(rnd), &rnd, sizeof(rnd));
++
++	size_t dst_len = 24 + 1;
++	mg_base64_encode(buffer, sizeof(buffer), magic25, &dst_len);
++}
++#endif
++
++
+ static struct mg_connection *
+ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
+                                  int use_ssl,
+@@ -19259,7 +19277,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
+ 
+ #if defined(USE_WEBSOCKET)
+ 	struct websocket_client_thread_data *thread_data;
+-	static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
++	char magic[32];
++	generate_websocket_magic(magic);
+ 
+ 	const char *host = client_options->host;
+ 	int i;
+diff --git a/unittest/public_server.c b/unittest/public_server.c
+index 02bf5255..42625e66 100644
+--- a/unittest/public_server.c
++++ b/unittest/public_server.c
+@@ -1316,7 +1316,7 @@ START_TEST(test_request_handlers)
+ 	char cmd_buf[1024];
+ 	char *cgi_env_opt;
+ 
+-	const char *server_host = "test.domain";
++	const char *server_host = "localhost"; //"test.domain";
+ 
+ 	mark_point();
+ 
+-- 
+2.34.1
+
+
+From 3771053e48c2d48b9c1f956d7f70ad4d77275f3c Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Sat, 30 Dec 2023 21:54:42 +0100
+Subject: [PATCH 057/173] Some compilers might have an issue with declarations
+ inside "for"
+
+Fixes #1228
+---
+ src/civetweb.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index be4f4cde..7f15670e 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -15860,9 +15860,10 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
+ 	 * 'r' */
+ 	if ((len > 0) && (is_valid_port(port))) {
+ 		int bad_suffix = 0;
++		size_t i;
+ 
+ 		/* Parse any suffix character(s) after the port number */
+-		for (size_t i = len; i < vec->len; i++) {
++		for (i = len; i < vec->len; i++) {
+ 			unsigned char *opt = NULL;
+ 			switch (vec->ptr[i]) {
+ 			case 'o':
+-- 
+2.34.1
+
+
+From b59be51aac17ab2dc07e840fa9d82f532718b2a8 Mon Sep 17 00:00:00 2001
+From: Egor Konovalov 
+Date: Mon, 12 Feb 2024 13:56:05 +0000
+Subject: [PATCH 058/173] Fix typo in /docs/UserManual.md
+
+---
+ docs/UserManual.md | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/docs/UserManual.md b/docs/UserManual.md
+index e90ceef4..13011fbb 100644
+--- a/docs/UserManual.md
++++ b/docs/UserManual.md
+@@ -180,7 +180,7 @@ This option can be specified multiple times. All specified header lines will be
+ ### allow\_index\_script\_resource `no`
+ Index scripts (like `index.cgi` or `index.lua`) may have script handled resources.
+ 
+-It this feature is activated, that /some/path/file.ext might be handled by:
++If this feature is activated, then /some/path/file.ext might be handled by:
+   1. /some/path/file.ext (with PATH\_INFO='/', if ext = cgi)
+   2. /some/path/index.lua with mg.request\_info.path\_info='/file.ext'
+   3. /some/path/index.cgi with PATH\_INFO='/file.ext'
+-- 
+2.34.1
+
+
+From 8d7c857ebee4b101a700cce51bd7a30d5f8cf8fd Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Tue, 27 Feb 2024 21:08:32 +0100
+Subject: [PATCH 059/173] Remove script. See #1231
+
+---
+ .github/workflows/fail_on_error.py | 36 ++----------------------------
+ 1 file changed, 2 insertions(+), 34 deletions(-)
+
+diff --git a/.github/workflows/fail_on_error.py b/.github/workflows/fail_on_error.py
+index 29791742..f0a06ad6 100755
+--- a/.github/workflows/fail_on_error.py
++++ b/.github/workflows/fail_on_error.py
+@@ -1,34 +1,2 @@
+-#!/usr/bin/env python3
+-
+-import json
+-import sys
+-
+-# Return whether SARIF file contains error-level results
+-def codeql_sarif_contain_error(filename):
+-    with open(filename, 'r') as f:
+-        s = json.load(f)
+-
+-    for run in s.get('runs', []):
+-        rules_metadata = run['tool']['driver']['rules']
+-        if not rules_metadata:
+-            rules_metadata = run['tool']['extensions'][0]['rules']
+-
+-        for res in run.get('results', []):
+-            if 'ruleIndex' in res:
+-                rule_index = res['ruleIndex']
+-            elif 'rule' in res and 'index' in res['rule']:
+-                rule_index = res['rule']['index']
+-            else:
+-                continue
+-            try:
+-                rule_level = rules_metadata[rule_index]['defaultConfiguration']['level']
+-            except IndexError as e:
+-                print(e, rule_index, len(rules_metadata))
+-            else:
+-                if rule_level == 'error':
+-                    return True
+-    return False
+-
+-if __name__ == "__main__":
+-    if codeql_sarif_contain_error(sys.argv[1]):
+-        sys.exit(1)
++# content removed, because it floods my email inbox.
++# see https://github.com/civetweb/civetweb/issues/1231#issuecomment-1967503931
+-- 
+2.34.1
+
+
+From 4f4593ca9c54fcc841005c4d93add0ecd2c7591f Mon Sep 17 00:00:00 2001
+From: Yiheng Cao <65160922+Crispy-fried-chicken@users.noreply.github.com>
+Date: Wed, 28 Feb 2024 15:15:06 +0800
+Subject: [PATCH 060/173] fix Integer overflow
+
+---
+ src/third_party/lua_struct.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/src/third_party/lua_struct.c b/src/third_party/lua_struct.c
+index 17cd1a7d..505e7df2 100644
+--- a/src/third_party/lua_struct.c
++++ b/src/third_party/lua_struct.c
+@@ -88,12 +88,14 @@ typedef struct Header {
+ } Header;
+ 
+ 
+-static int getnum (const char **fmt, int df) {
++static int getnum (lua_State *L, const char **fmt, int df) {
+   if (!isdigit(**fmt))  /* no number? */
+     return df;  /* return default value */
+   else {
+     int a = 0;
+     do {
++      if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
++        luaL_error(L, "integral size overflow");
+       a = a*10 + *((*fmt)++) - '0';
+     } while (isdigit(**fmt));
+     return a;
+@@ -114,9 +116,9 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
+     case 'f':  return sizeof(float);
+     case 'd':  return sizeof(double);
+     case 'x': return 1;
+-    case 'c': return getnum(fmt, 1);
++    case 'c': return getnum(L, fmt, 1);
+     case 'i': case 'I': {
+-      int sz = getnum(fmt, sizeof(int));
++      int sz = getnum(L, fmt, sizeof(int));
+       if (sz > MAXINTSIZE)
+         luaL_error(L, "integral size %d is larger than limit of %d",
+                        sz, MAXINTSIZE);
+@@ -149,7 +151,7 @@ static void controloptions (lua_State *L, int opt, const char **fmt,
+     case '>': h->endian = BIG; return;
+     case '<': h->endian = LITTLE; return;
+     case '!': {
+-      int a = getnum(fmt, MAXALIGN);
++      int a = getnum(L, fmt, MAXALIGN);
+       if (!isp2(a))
+         luaL_error(L, "alignment %d is not a power of 2", a);
+       h->align = a;
+-- 
+2.34.1
+
+
+From 95ef692f3ce6657778935cf8629bb03bf89b5bfc Mon Sep 17 00:00:00 2001
+From: Yiheng Cao <65160922+Crispy-fried-chicken@users.noreply.github.com>
+Date: Wed, 28 Feb 2024 15:21:15 +0800
+Subject: [PATCH 061/173] fix the negation overflow
+
+---
+ src/third_party/lua-5.2.4/src/ldebug.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/src/third_party/lua-5.2.4/src/ldebug.c b/src/third_party/lua-5.2.4/src/ldebug.c
+index 96118461..1a30433b 100644
+--- a/src/third_party/lua-5.2.4/src/ldebug.c
++++ b/src/third_party/lua-5.2.4/src/ldebug.c
+@@ -116,10 +116,11 @@ static const char *upvalname (Proto *p, int uv) {
+ 
+ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
+   int nparams = clLvalue(ci->func)->p->numparams;
+-  if (n >= ci->u.l.base - ci->func - nparams)
++  int nvararg  = ci->u.l.base - ci->func - nparams;
++  if (n < -nvararg)
+     return NULL;  /* no such vararg */
+   else {
+-    *pos = ci->func + nparams + n;
++    *pos = ci->func + nparams - n;
+     return "(*vararg)";  /* generic name for any vararg */
+   }
+ }
+@@ -131,7 +132,7 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
+   StkId base;
+   if (isLua(ci)) {
+     if (n < 0)  /* access to vararg values? */
+-      return findvararg(ci, -n, pos);
++      return findvararg(ci, n, pos);
+     else {
+       base = ci->u.l.base;
+       name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
+-- 
+2.34.1
+
+
+From bad817953013d6bb110fa4684fe82ece70d696cb Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Sun, 24 Mar 2024 17:40:05 +0100
+Subject: [PATCH 062/173] Add MIME type for .mjs extension (#1252)
+
+---
+ src/civetweb.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 7f15670e..2b6ae416 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -8402,6 +8402,7 @@ static const struct {
+     {".iso", 4, "application/octet-stream"},
+     {".js", 3, "application/javascript"},
+     {".json", 5, "application/json"},
++    {".mjs", 4, "application/javascript"},
+     {".msi", 4, "application/octet-stream"},
+     {".pdf", 4, "application/pdf"},
+     {".ps", 3, "application/postscript"},
+-- 
+2.34.1
+
+
+From a31495b9029608c00097a23498a963dd519f3f06 Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Sun, 24 Mar 2024 17:41:39 +0100
+Subject: [PATCH 063/173] Update copyright year
+
+---
+ include/civetweb.h | 2 +-
+ src/civetweb.c     | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/include/civetweb.h b/include/civetweb.h
+index 78149722..5ae6a0c7 100644
+--- a/include/civetweb.h
++++ b/include/civetweb.h
+@@ -1,4 +1,4 @@
+-/* Copyright (c) 2013-2021 the Civetweb developers
++/* Copyright (c) 2013-2024 the Civetweb developers
+  * Copyright (c) 2004-2013 Sergey Lyubka
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a copy
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 2b6ae416..e2336bd8 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -1,4 +1,4 @@
+-/* Copyright (c) 2013-2023 the Civetweb developers
++/* Copyright (c) 2013-2024 the Civetweb developers
+  * Copyright (c) 2004-2013 Sergey Lyubka
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a copy
+-- 
+2.34.1
+
+
+From d6c9d63cb55ae61321d3da2bece9bf5875badac1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Tim=20=C4=8Cas?= 
+Date: Wed, 3 Apr 2024 13:10:10 +0200
+Subject: [PATCH 064/173] Fix building with MbedTLS
+
+---
+ src/CMakeLists.txt | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
+index 47a9b0c8..28c4e8cc 100644
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -47,10 +47,10 @@ endif()
+ 
+ # We need to link OpenSSL if not dynamically loading
+ if (CIVETWEB_ENABLE_SSL)
+-  if (CIVETWEB_ENBALE_MBEDTLS)
++  if (CIVETWEB_ENABLE_MBEDTLS)
+     find_package(MbedTLS)
+     include_directories(${MbedTLS_INCLUDE_DIR})
+-    message(STATUS "MbedTLS include directory: {MbedTLS_INCLUDE_DIR}")
++    message(STATUS "MbedTLS include directory: ${MbedTLS_INCLUDE_DIR}")
+     target_link_libraries(civetweb-c-library ${MbedTLS_LIBRARIES})
+   else()
+     if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
+-- 
+2.34.1
+
+
+From 8a0d7c87fe090b63ce64e8eb13783c917bef3fdb Mon Sep 17 00:00:00 2001
+From: drew-wells <58978640+drew-wells@users.noreply.github.com>
+Date: Mon, 22 Apr 2024 15:11:44 +0100
+Subject: [PATCH 065/173] Fix for slow CGI
+
+Don't use fread() to read from CGI scripts because of possible buffering.
+---
+ src/civetweb.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index e2336bd8..5258fee1 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -6472,7 +6472,7 @@ pull_inner(FILE *fp,
+ 		}
+ 	}
+ 
+-	if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
++	if (conn != NULL && !STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
+ 		return -2;
+ 	}
+ 
+@@ -6496,7 +6496,7 @@ pull_inner(FILE *fp,
+ 			/* See https://www.chilkatsoft.com/p/p_299.asp */
+ 			return -2;
+ 		} else {
+-			DEBUG_TRACE("recv() failed, error %d", err);
++			DEBUG_TRACE("read()/recv() failed, error %d", err);
+ 			return -2;
+ 		}
+ #else
+@@ -6518,7 +6518,7 @@ pull_inner(FILE *fp,
+ 			 * (see signal(7)).
+ 			 * => stay in the while loop */
+ 		} else {
+-			DEBUG_TRACE("recv() failed, error %d", err);
++			DEBUG_TRACE("read()/recv() failed, error %d", err);
+ 			return -2;
+ 		}
+ #endif
+@@ -10322,7 +10322,7 @@ send_file_data(struct mg_connection *conn,
+ 
+ 				/* Read from file, exit the loop on error */
+ 				if ((num_read =
+-				         (int)fread(buf, 1, (size_t)to_read, filep->access.fp))
++				         pull_inner(filep->access.fp, NULL, buf, to_read, /* unused */ 0.0))
+ 				    <= 0) {
+ 					break;
+ 				}
+-- 
+2.34.1
+
+
+From b95b7468f3ca1a7ba5b78a2fdcb72e683f5fb947 Mon Sep 17 00:00:00 2001
+From: Turiiya <34311583+ttytm@users.noreply.github.com>
+Date: Fri, 31 May 2024 04:41:13 +0200
+Subject: [PATCH 066/173] Disable `mg_` reminders when NDEBUG is defined
+
+---
+ src/civetweb.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 5258fee1..d99e1f50 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -1551,11 +1551,13 @@ static void mg_snprintf(const struct mg_connection *conn,
+ #if defined(vsnprintf)
+ #undef vsnprintf
+ #endif
++#if !defined(NDEBUG)
+ #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
+ #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
+ #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
+ #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
+ #define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
++#endif
+ #if defined(_WIN32)
+ /* vsnprintf must not be used in any system,
+  * but this define only works well for Windows. */
+-- 
+2.34.1
+
+
+From f0c6cd0bf849a34c5d3c567d48e23be4bcbf936e Mon Sep 17 00:00:00 2001
+From: morgonf <96179229+morgonf@users.noreply.github.com>
+Date: Tue, 11 Jun 2024 16:36:24 +0300
+Subject: [PATCH 067/173] Update CMakeLists.txt
+
+To correctly fill in the keyword fields "Version" in the pkg-config files section, you must specify the value of the "VERSION" option when calling the project() command
+---
+ CMakeLists.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 44cebaec..172b1ed5 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -5,7 +5,7 @@ cmake_policy(SET CMP0054 NEW)
+ cmake_policy(SET CMP0057 NEW)
+ 
+ # Set up the project
+-project (civetweb)
++project (civetweb VERSION 1.16.0)
+ 
+ # Detect the platform reliably
+ if(ZEPHYR_BASE)
+-- 
+2.34.1
+
+
+From 45d4af05a03c643dcd1ec0a88c5e36a89a9683f2 Mon Sep 17 00:00:00 2001
+From: Jamie St Martin 
+Date: Wed, 12 Jun 2024 11:11:06 -0700
+Subject: [PATCH 068/173] Update CIVETWEB_VERSION to 1.16.0 in CMakeLists.txt
+
+Our call to `find_package(civetweb 1.16.0 EXACT CONFIG REQUIRED)` is failing with version 1.16 of civetweb installed via vcpkg, because this variable is set incorrectly and therefore `civetweb-config-version.cmake` contains the wrong version.
+---
+ CMakeLists.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 44cebaec..691bdbfa 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -37,7 +37,7 @@ include(AddCXXCompilerFlag)
+ include(DetermineTargetArchitecture)
+ include(CMakeDependentOption)
+ 
+-set(CIVETWEB_VERSION "1.15.0" CACHE STRING "The version of the civetweb library")
++set(CIVETWEB_VERSION "1.16.0" CACHE STRING "The version of the civetweb library")
+ string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CIVETWEB_VERSION_MATCH "${CIVETWEB_VERSION}")
+ if ("${CIVETWEB_VERSION_MATCH}" STREQUAL "")
+   message(FATAL_ERROR "Must specify a semantic version: major.minor.patch")
+-- 
+2.34.1
+
+
+From 8e82e3b78f2214d58de49e6e3b7c30c1095800db Mon Sep 17 00:00:00 2001
+From: Turiiya <34311583+ttytm@users.noreply.github.com>
+Date: Sun, 16 Jun 2024 19:01:07 +0200
+Subject: [PATCH 069/173] Rename ah struct to auth_header
+
+---
+ src/civetweb.c | 76 +++++++++++++++++++++++++-------------------------
+ 1 file changed, 38 insertions(+), 38 deletions(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 5258fee1..be688e4f 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -8710,7 +8710,7 @@ open_auth_file(struct mg_connection *conn,
+ 
+ 
+ /* Parsed Authorization header */
+-struct ah {
++struct auth_header {
+ 	char *user;
+ 	int type;             /* 1 = basic, 2 = digest */
+ 	char *plain_password; /* Basic only */
+@@ -8718,32 +8718,32 @@ struct ah {
+ };
+ 
+ 
+-/* Return 1 on success. Always initializes the ah structure. */
++/* Return 1 on success. Always initializes the auth_header structure. */
+ static int
+ parse_auth_header(struct mg_connection *conn,
+                   char *buf,
+                   size_t buf_size,
+-                  struct ah *ah)
++                  struct auth_header *auth_header)
+ {
+ 	char *name, *value, *s;
+-	const char *auth_header;
++	const char *ah;
+ 	uint64_t nonce;
+ 
+-	if (!ah || !conn) {
++	if (!auth_header || !conn) {
+ 		return 0;
+ 	}
+ 
+-	(void)memset(ah, 0, sizeof(*ah));
+-	auth_header = mg_get_header(conn, "Authorization");
++	(void)memset(auth_header, 0, sizeof(*auth_header));
++	ah = mg_get_header(conn, "Authorization");
+ 
+-	if (auth_header == NULL) {
++	if (ah == NULL) {
+ 		/* No Authorization header at all */
+ 		return 0;
+ 	}
+-	if (0 == mg_strncasecmp(auth_header, "Basic ", 6)) {
++	if (0 == mg_strncasecmp(ah, "Basic ", 6)) {
+ 		/* Basic Auth (we never asked for this, but some client may send it) */
+ 		char *split;
+-		const char *userpw_b64 = auth_header + 6;
++		const char *userpw_b64 = ah + 6;
+ 		size_t userpw_b64_len = strlen(userpw_b64);
+ 		size_t buf_len_r = buf_size;
+ 		if (mg_base64_decode(
+@@ -8760,15 +8760,15 @@ parse_auth_header(struct mg_connection *conn,
+ 		*split = 0;
+ 
+ 		/* User name is before ':', Password is after ':'  */
+-		ah->user = buf;
+-		ah->type = 1;
+-		ah->plain_password = split + 1;
++		auth_header->user = buf;
++		auth_header->type = 1;
++		auth_header->plain_password = split + 1;
+ 
+ 		return 1;
+ 
+-	} else if (0 == mg_strncasecmp(auth_header, "Digest ", 7)) {
++	} else if (0 == mg_strncasecmp(ah, "Digest ", 7)) {
+ 		/* Digest Auth ... implemented below */
+-		ah->type = 2;
++		auth_header->type = 2;
+ 
+ 	} else {
+ 		/* Unknown or invalid Auth method */
+@@ -8776,7 +8776,7 @@ parse_auth_header(struct mg_connection *conn,
+ 	}
+ 
+ 	/* Make modifiable copy of the auth header */
+-	(void)mg_strlcpy(buf, auth_header + 7, buf_size);
++	(void)mg_strlcpy(buf, ah + 7, buf_size);
+ 	s = buf;
+ 
+ 	/* Parse authorization header */
+@@ -8803,29 +8803,29 @@ parse_auth_header(struct mg_connection *conn,
+ 		}
+ 
+ 		if (!strcmp(name, "username")) {
+-			ah->user = value;
++			auth_header->user = value;
+ 		} else if (!strcmp(name, "cnonce")) {
+-			ah->cnonce = value;
++			auth_header->cnonce = value;
+ 		} else if (!strcmp(name, "response")) {
+-			ah->response = value;
++			auth_header->response = value;
+ 		} else if (!strcmp(name, "uri")) {
+-			ah->uri = value;
++			auth_header->uri = value;
+ 		} else if (!strcmp(name, "qop")) {
+-			ah->qop = value;
++			auth_header->qop = value;
+ 		} else if (!strcmp(name, "nc")) {
+-			ah->nc = value;
++			auth_header->nc = value;
+ 		} else if (!strcmp(name, "nonce")) {
+-			ah->nonce = value;
++			auth_header->nonce = value;
+ 		}
+ 	}
+ 
+ #if !defined(NO_NONCE_CHECK)
+ 	/* Read the nonce from the response. */
+-	if (ah->nonce == NULL) {
++	if (auth_header->nonce == NULL) {
+ 		return 0;
+ 	}
+ 	s = NULL;
+-	nonce = strtoull(ah->nonce, &s, 10);
++	nonce = strtoull(auth_header->nonce, &s, 10);
+ 	if ((s == NULL) || (*s != 0)) {
+ 		return 0;
+ 	}
+@@ -8856,7 +8856,7 @@ parse_auth_header(struct mg_connection *conn,
+ 	(void)nonce;
+ #endif
+ 
+-	return (ah->user != NULL);
++	return (auth_header->user != NULL);
+ }
+ 
+ 
+@@ -8888,7 +8888,7 @@ mg_fgets(char *buf, size_t size, struct mg_file *filep)
+ #if !defined(NO_FILESYSTEMS)
+ struct read_auth_file_struct {
+ 	struct mg_connection *conn;
+-	struct ah ah;
++	struct auth_header auth_header;
+ 	const char *domain;
+ 	char buf[256 + 256 + 40];
+ 	const char *f_user;
+@@ -8989,9 +8989,9 @@ read_auth_file(struct mg_file *filep,
+ 		*(char *)(workdata->f_ha1) = 0;
+ 		(workdata->f_ha1)++;
+ 
+-		if (!strcmp(workdata->ah.user, workdata->f_user)
++		if (!strcmp(workdata->auth_header.user, workdata->f_user)
+ 		    && !strcmp(workdata->domain, workdata->f_domain)) {
+-			switch (workdata->ah.type) {
++			switch (workdata->auth_header.type) {
+ 			case 1: /* Basic */
+ 			{
+ 				char md5[33];
+@@ -9000,7 +9000,7 @@ read_auth_file(struct mg_file *filep,
+ 				       ":",
+ 				       workdata->domain,
+ 				       ":",
+-				       workdata->ah.plain_password,
++				       workdata->auth_header.plain_password,
+ 				       NULL);
+ 				return 0 == memcmp(workdata->f_ha1, md5, 33);
+ 			}
+@@ -9008,12 +9008,12 @@ read_auth_file(struct mg_file *filep,
+ 				return check_password_digest(
+ 				    workdata->conn->request_info.request_method,
+ 				    workdata->f_ha1,
+-				    workdata->ah.uri,
+-				    workdata->ah.nonce,
+-				    workdata->ah.nc,
+-				    workdata->ah.cnonce,
+-				    workdata->ah.qop,
+-				    workdata->ah.response);
++				    workdata->auth_header.uri,
++				    workdata->auth_header.nonce,
++				    workdata->auth_header.nc,
++				    workdata->auth_header.cnonce,
++				    workdata->auth_header.qop,
++				    workdata->auth_header.response);
+ 			default: /* None/Other/Unknown */
+ 				return 0;
+ 			}
+@@ -9038,13 +9038,13 @@ authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
+ 	memset(&workdata, 0, sizeof(workdata));
+ 	workdata.conn = conn;
+ 
+-	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
++	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.auth_header)) {
+ 		return 0;
+ 	}
+ 
+ 	/* CGI needs it as REMOTE_USER */
+ 	conn->request_info.remote_user =
+-	    mg_strdup_ctx(workdata.ah.user, conn->phys_ctx);
++	    mg_strdup_ctx(workdata.auth_header.user, conn->phys_ctx);
+ 
+ 	if (realm) {
+ 		workdata.domain = realm;
+-- 
+2.34.1
+
+
+From e5cfc072cf21c45d786f45ddc3bc397706ec36b3 Mon Sep 17 00:00:00 2001
+From: Martin Mahringer 
+Date: Fri, 5 Jul 2024 14:01:11 +0200
+Subject: [PATCH 070/173] Only poll thread_shutdown_notification_socket for
+ server context
+
+The socket thread_shutdown_notification_socket must only be used for
+context type CONTEXT_SERVER.
+---
+ src/civetweb.c | 54 +++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 38 insertions(+), 16 deletions(-)
+
+diff --git a/src/civetweb.c b/src/civetweb.c
+index 5258fee1..46c1545c 100644
+--- a/src/civetweb.c
++++ b/src/civetweb.c
+@@ -6211,13 +6211,18 @@ push_inner(struct mg_context *ctx,
+ 			/* For sockets, wait for the socket using poll */
+ 			struct mg_pollfd pfd[2];
+ 			int pollres;
++			unsigned int num_sock = 1;
+ 
+ 			pfd[0].fd = sock;
+ 			pfd[0].events = POLLOUT;
+ 
+-			pfd[1].fd = ctx->thread_shutdown_notification_socket;
+-			pfd[1].events = POLLIN;
+-			pollres = mg_poll(pfd, 2, (int)(ms_wait), &(ctx->stop_flag));
++			if (ctx->context_type == CONTEXT_SERVER) {
++				pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
++				pfd[num_sock].events = POLLIN;
++				num_sock++;
++			}
++
++			pollres = mg_poll(pfd, num_sock, (int)(ms_wait), &(ctx->stop_flag));
+ 			if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
+ 				return -2;
+ 			}
+@@ -6328,6 +6333,7 @@ pull_inner(FILE *fp,
+ 		struct mg_pollfd pfd[2];
+ 		int to_read;
+ 		int pollres;
++		unsigned int num_sock = 1;
+ 
+ 		to_read = mbedtls_ssl_get_bytes_avail(conn->ssl);
+ 
+@@ -6343,13 +6349,16 @@ pull_inner(FILE *fp,
+ 			pfd[0].fd = conn->client.sock;
+ 			pfd[0].events = POLLIN;
+ 
+-			pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
+-			pfd[1].events = POLLIN;
++			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
++				pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
++				pfd[num_sock].events = POLLIN;
++				num_sock++;
++			}
+ 
+ 			to_read = len;
+ 
+ 			pollres = mg_poll(pfd,
+-			                  2,
++			                  num_sock,
+ 			                  (int)(timeout * 1000.0),
+ 			                  &(conn->phys_ctx->stop_flag));
+ 
+@@ -6386,6 +6395,7 @@ pull_inner(FILE *fp,
+ 		int ssl_pending;
+ 		struct mg_pollfd pfd[2];
+ 		int pollres;
++		unsigned int num_sock = 1;
+ 
+ 		if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
+ 			/* We already know there is no more data buffered in conn->buf
+@@ -6398,11 +6408,15 @@ pull_inner(FILE *fp,
+ 		} else {
+ 			pfd[0].fd = conn->client.sock;
+ 			pfd[0].events = POLLIN;
+-			pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
+-			pfd[1].events = POLLIN;
++
++			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
++				pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
++				pfd[num_sock].events = POLLIN;
++				num_sock++;
++			}
+ 
+ 			pollres = mg_poll(pfd,
+-			                  2,
++			                  num_sock,
+ 			                  (int)(timeout * 1000.0),
+ 			                  &(conn->phys_ctx->stop_flag));
+ 			if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
+@@ -6442,15 +6456,19 @@ pull_inner(FILE *fp,
+ 	} else {
+ 		struct mg_pollfd pfd[2];
+ 		int pollres;
++		unsigned int num_sock = 1;
+ 
+ 		pfd[0].fd = conn->client.sock;
+ 		pfd[0].events = POLLIN;
+ 
+-		pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
+-		pfd[1].events = POLLIN;
++		if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
++			pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
++			pfd[num_sock].events = POLLIN;
++			num_sock++;
++		}
+ 
+ 		pollres = mg_poll(pfd,
+-		                  2,
++		                  num_sock,
+ 		                  (int)(timeout * 1000.0),
+ 		                  &(conn->phys_ctx->stop_flag));
+ 		if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
+@@ -16785,16 +16803,20 @@ sslize(struct mg_connection *conn,
+ 					 * This is typical for non-blocking sockets. */
+ 					struct mg_pollfd pfd[2];
+ 					int pollres;
++					unsigned int num_sock = 1;
+ 					pfd[0].fd = conn->client.sock;
+ 					pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT)
+ 					                 || (err == SSL_ERROR_WANT_WRITE))
+ 					                    ? POLLOUT
+ 					                    : POLLIN;
+ 
+-					pfd[1].fd =
+-					    conn->phys_ctx->thread_shutdown_notification_socket;
+-					pfd[1].events = POLLIN;
+-					pollres = mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag));
++					if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
++						pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
++						pfd[num_sock].events = POLLIN;
++						num_sock++;
++					}
++
++					pollres = mg_poll(pfd, num_sock, 50, &(conn->phys_ctx->stop_flag));
+ 					if (pollres < 0) {
+ 						/* Break if error occurred (-1)
+ 						 * or server shutdown (-2) */
+-- 
+2.34.1
+
+
+From b8773bda25100339fb8ea58e4061936ef75c930a Mon Sep 17 00:00:00 2001
+From: bel2125 
+Date: Thu, 11 Jul 2024 21:39:48 +0200
+Subject: [PATCH 071/173] Remove donation links
+
+---
+ docs/README.md   |  3 ---
+ test/donate.html | 32 --------------------------------
+ 2 files changed, 35 deletions(-)
+ delete mode 100644 test/donate.html
+
+diff --git a/docs/README.md b/docs/README.md
+index 76e57732..d3fb0e5d 100644
+--- a/docs/README.md
++++ b/docs/README.md
+@@ -31,9 +31,6 @@ Source releases can be found on GitHub
+ 
+ A security policy can be found in [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md).
+ 
+-CivetWeb is free of charge, however, donations for maintenance are welcome:
+-[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=88ZLXZ6U77GJU)
+-
+ 
+ Documentation
+ ---------------
+diff --git a/test/donate.html b/test/donate.html
+deleted file mode 100644
+index da6e3978..00000000
+--- a/test/donate.html
++++ /dev/null
+@@ -1,32 +0,0 @@
+-
+-
+-
+-
+-
+-CivetWeb web server maintenance (#523):
+-
+-

+-

+- +- +- +- +-
+-

+- +-

+- +-donate +- +-

+- +-

+- +-donate +- +-

+- +- +- +- +- +-- +2.34.1 + + +From 51f8443b632ddd1b126c3d182e8587f56a728f61 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Thu, 11 Jul 2024 21:40:52 +0200 +Subject: [PATCH 072/173] Format source after merge + +--- + src/civetweb.c | 24 +++++++++++++++++------- + 1 file changed, 17 insertions(+), 7 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index e8e60bf5..9e098045 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -6350,7 +6350,8 @@ pull_inner(FILE *fp, + pfd[0].events = POLLIN; + + if (conn->phys_ctx->context_type == CONTEXT_SERVER) { +- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[num_sock].fd = ++ conn->phys_ctx->thread_shutdown_notification_socket; + pfd[num_sock].events = POLLIN; + num_sock++; + } +@@ -6410,7 +6411,8 @@ pull_inner(FILE *fp, + pfd[0].events = POLLIN; + + if (conn->phys_ctx->context_type == CONTEXT_SERVER) { +- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[num_sock].fd = ++ conn->phys_ctx->thread_shutdown_notification_socket; + pfd[num_sock].events = POLLIN; + num_sock++; + } +@@ -6462,7 +6464,8 @@ pull_inner(FILE *fp, + pfd[0].events = POLLIN; + + if (conn->phys_ctx->context_type == CONTEXT_SERVER) { +- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[num_sock].fd = ++ conn->phys_ctx->thread_shutdown_notification_socket; + pfd[num_sock].events = POLLIN; + num_sock++; + } +@@ -10339,8 +10342,11 @@ send_file_data(struct mg_connection *conn, + } + + /* Read from file, exit the loop on error */ +- if ((num_read = +- pull_inner(filep->access.fp, NULL, buf, to_read, /* unused */ 0.0)) ++ if ((num_read = pull_inner(filep->access.fp, ++ NULL, ++ buf, ++ to_read, ++ /* unused */ 0.0)) + <= 0) { + break; + } +@@ -16811,12 +16817,16 @@ sslize(struct mg_connection *conn, + : POLLIN; + + if (conn->phys_ctx->context_type == CONTEXT_SERVER) { +- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[num_sock].fd = ++ conn->phys_ctx->thread_shutdown_notification_socket; + pfd[num_sock].events = POLLIN; + num_sock++; + } + +- pollres = mg_poll(pfd, num_sock, 50, &(conn->phys_ctx->stop_flag)); ++ pollres = mg_poll(pfd, ++ num_sock, ++ 50, ++ &(conn->phys_ctx->stop_flag)); + if (pollres < 0) { + /* Break if error occurred (-1) + * or server shutdown (-2) */ +-- +2.34.1 + + +From 715da022e9851141c0a510ce6b2db8d2a8654d09 Mon Sep 17 00:00:00 2001 +From: yafiyogi <1446544+yafiyogi@users.noreply.github.com> +Date: Sat, 20 Jul 2024 15:12:08 +0100 +Subject: [PATCH 073/173] Add cmake option CIVETWEB_ENABLE_HTTP2 to allow HTTP2 + builds. + +--- + CMakeLists.txt | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 07abf861..45a17adc 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -80,6 +80,10 @@ message(STATUS "Disable caching support - ${CIVETWEB_DISABLE_CACHING}") + option(CIVETWEB_ENABLE_CXX "Enables the C++ wrapper library" OFF) + message(STATUS "C++ wrappers - ${CIVETWEB_ENABLE_CXX}") + ++# HTTP2 Support ++option(CIVETWEB_ENABLE_HTTP2 "Enables HTTP2 support" OFF) ++message(STATUS "Use HTPP2 - ${CIVETWEB_ENABLE_HTTP2}") ++ + # IP Version 6 + option(CIVETWEB_ENABLE_IPV6 "Enables the IP version 6 support" ON) + message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}") +@@ -496,6 +500,10 @@ if (${CMAKE_BUILD_TYPE} MATCHES "[Dd]ebug") + add_definitions(-O0) + add_definitions(-g) + endif() ++if (CIVETWEB_ENABLE_HTTP2) ++ add_definitions(-DUSE_HTTP2) ++ add_definitions(-Wno-gnu-zero-variadic-macro-arguments) ++endif() + if (CIVETWEB_ENABLE_IPV6) + add_definitions(-DUSE_IPV6) + endif() +@@ -699,4 +707,3 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_PACKAGE_DEPENDS}") + + # Finalize CPack settings + include(CPack) +- +-- +2.34.1 + + +From 1a5c587add95d257fbfca98c90e03930bc563256 Mon Sep 17 00:00:00 2001 +From: yafiyogi <1446544+yafiyogi@users.noreply.github.com> +Date: Mon, 22 Jul 2024 11:14:04 +0100 +Subject: [PATCH 074/173] Remove + 'add_definitions(-Wno-gnu-zero-variadic-macro-arguments)'. + +--- + CMakeLists.txt | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 45a17adc..e1aa002b 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -502,7 +502,6 @@ if (${CMAKE_BUILD_TYPE} MATCHES "[Dd]ebug") + endif() + if (CIVETWEB_ENABLE_HTTP2) + add_definitions(-DUSE_HTTP2) +- add_definitions(-Wno-gnu-zero-variadic-macro-arguments) + endif() + if (CIVETWEB_ENABLE_IPV6) + add_definitions(-DUSE_IPV6) +-- +2.34.1 + + +From 0b50decfa0a5864e60d2793f6c487c5d754411a6 Mon Sep 17 00:00:00 2001 +From: tim +Date: Tue, 6 Aug 2024 02:25:30 +0100 +Subject: [PATCH 075/173] [#1276] add test cases for multipart form data + +--- + unittest/public_server.c | 569 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 569 insertions(+) + +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 42625e66..efca8a16 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -2922,6 +2922,575 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + ++ /* Handle form: "POST multipart/form-data" without trailing CRLF*/ ++ /* ++ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ */ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ ++ /* Handle form: "POST multipart/form-data" with epilogue*/ ++ /* ++ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ * ++ * epilogue := discard-text ++ * ++ * discard-text := *(*text CRLF) *text ++ * ++ * text := ++ */ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n" ++ "epilogue\r\n" ++ "epilogue\r\n" ++ "\r\n" ++ "epilogue\r\n" ++ "\r\n" ++ "\r\n" ++ "\r\n" ++ "epilogue\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ ++ /* Handle form: "POST multipart/form-data" with preamble*/ ++ /* ++ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ * ++ * preamble := discard-text ++ * ++ * discard-text := *(*text CRLF) *text ++ * ++ * text := ++ */ ++ multipart_body = ++ "\r\n" ++ "\r\npreamble" ++ "\r\npreamble" ++ "\r\npreamble" ++ "\r\n" ++ "\r\npreamble" ++ "\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ ++ ++ /* Handle form: "POST multipart/form-data" with transport padding*/ ++ /* ++ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ * ++ * transport-padding := *LWSP-char ++ * ++ * LWSP-char := SPACE / HTAB ++ */ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388 \r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); + + /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ + client_conn = +-- +2.34.1 + + +From ee8e08cc4a364f3f0645b5c380223031447d0e25 Mon Sep 17 00:00:00 2001 +From: tim +Date: Thu, 8 Aug 2024 02:36:33 +0100 +Subject: [PATCH 076/173] [#1276] mutipart form data + +- Add some tests +- Add fixes for preamble, epilogue, transport padding, boundary lines +--- + src/civetweb.c | 6 +- + src/handle_form.inl | 78 +++++-- + unittest/public_server.c | 460 ++++++++++++++++++++++++++++++--------- + 3 files changed, 416 insertions(+), 128 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 9e098045..bf44a0f7 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -10979,7 +10979,7 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]) + } + + /* here *dp is either 0 or '\n' */ +- /* in any case, we have a new header */ ++ /* in any case, we have found a complete header */ + num_headers = i + 1; + + if (*dp) { +@@ -10988,9 +10988,11 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]) + *buf = dp; + + if ((dp[0] == '\r') || (dp[0] == '\n')) { +- /* This is the end of the header */ ++ /* We've had CRLF twice in a row ++ * This is the end of the headers */ + break; + } ++ /* continue within the loop, find the next header */ + } else { + *buf = dp; + break; +diff --git a/src/handle_form.inl b/src/handle_form.inl +index be477a05..b22f17ef 100644 +--- a/src/handle_form.inl ++++ b/src/handle_form.inl +@@ -162,14 +162,17 @@ search_boundary(const char *buf, + const char *boundary, + size_t boundary_len) + { +- /* We must do a binary search here, not a string search, since the buffer +- * may contain '\x00' bytes, if binary data is transferred. */ +- int clen = (int)buf_len - (int)boundary_len - 4; ++ char *boundary_start = "\r\n--"; ++ size_t boundary_start_len = strlen(boundary_start); ++ ++ /* We must do a binary search here, not a string search, since the ++ * buffer may contain '\x00' bytes, if binary data is transferred. */ ++ int clen = (int)buf_len - (int)boundary_len - boundary_start_len; + int i; + + for (i = 0; i <= clen; i++) { +- if (!memcmp(buf + i, "\r\n--", 4)) { +- if (!memcmp(buf + i + 4, boundary, boundary_len)) { ++ if (!memcmp(buf + i, boundary_start, boundary_start_len)) { ++ if (!memcmp(buf + i + boundary_start_len, boundary, boundary_len)) { + return buf + i; + } + } +@@ -624,6 +627,7 @@ mg_handle_form_request(struct mg_connection *conn, + } + + /* Copy boundary string to variable "boundary" */ ++ /* fbeg is pointer to start of value of boundary */ + fbeg = content_type + bl + 9; + bl = strlen(fbeg); + boundary = (char *)mg_malloc(bl + 1); +@@ -701,43 +705,71 @@ mg_handle_form_request(struct mg_connection *conn, + return -1; + } + ++ /* @see https://www.rfc-editor.org/rfc/rfc2046.html#section-5.1.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ */ ++ + if (part_no == 0) { +- int d = 0; +- while ((d < buf_fill) && (buf[d] != '-')) { +- d++; ++ size_t preamble_length = 0; ++ /* skip over the preamble until we find a complete boundary */ ++ /* +2 for the -- preceding the boundary */ ++ while ((preamble_length < buf_fill - bl) ++ && strncmp(buf + preamble_length + 2, boundary, bl)) { ++ preamble_length++; + } +- if ((d > 0) && (buf[d] == '-')) { +- memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d); +- buf_fill -= d; ++ /* reset the start of buf to remove the preamble */ ++ if (0 == strncmp(buf + preamble_length + 2, boundary, bl)) { ++ memmove(buf, ++ buf + preamble_length, ++ (unsigned)buf_fill - (unsigned)preamble_length); ++ buf_fill -= preamble_length; + buf[buf_fill] = 0; + } + } + +- if (buf[0] != '-' || buf[1] != '-') { ++ /* either it starts with a boundary ++ * or we couldn't find a boundary at all in the body ++ * or we didn't have a terminating boundary */ ++ if (buf_fill < (bl + 2) || strncmp(buf, "--", 2) ++ || strncmp(buf + 2, boundary, bl)) { + /* Malformed request */ + mg_free(boundary); + return -1; + } +- if (0 != strncmp(buf + 2, boundary, bl)) { +- /* Malformed request */ +- mg_free(boundary); +- return -1; ++ ++ /* skip the -- */ ++ char *boundary_start = buf + 2; ++ size_t transport_padding = 0; ++ while (boundary_start[bl + transport_padding] == ' ' ++ || boundary_start[bl + transport_padding] == '\t') { ++ transport_padding++; + } +- if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') { +- /* Every part must end with \r\n, if there is another part. +- * The end of the request has an extra -- */ +- if (((size_t)buf_fill != (size_t)(bl + 6)) +- || (strncmp(buf + bl + 2, "--\r\n", 4))) { ++ char *boundary_end = boundary_start + bl + transport_padding; ++ ++ /* after the transport padding, if the boundary isn't ++ * immediately followed by a \r\n then it is either... */ ++ if (strncmp(boundary_end, "\r\n", 2)) ++ { ++ /* ...the final boundary, and it is followed by --, (in which ++ * case it's the end of the request) or it's a malformed ++ * request */ ++ if (strncmp(boundary_end, "--", 2)) { + /* Malformed request */ + mg_free(boundary); + return -1; + } +- /* End of the request */ ++ /* Ingore any epilogue here */ + break; + } + ++ /* skip the \r\n */ ++ hbuf = boundary_end + 2; + /* Next, we need to get the part header: Read until \r\n\r\n */ +- hbuf = buf + bl + 4; + hend = strstr(hbuf, "\r\n\r\n"); + if (!hend) { + /* Malformed request */ +diff --git a/unittest/public_server.c b/unittest/public_server.c +index efca8a16..2d244297 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -57,6 +57,79 @@ + #define SLEEP_AFTER_MG_STOP (5) + + ++#ifdef HEXDUMP ++#else ++void ++hexDumpth(const char *desc, const void *addr, const int len, int perLine) ++{ ++ // Silently ignore silly per-line values. ++ ++ if (perLine < 4 || perLine > 64) ++ perLine = 16; ++ ++ int i; ++ unsigned char buff[perLine + 1]; ++ const unsigned char *pc = (const unsigned char *)addr; ++ ++ // Output description if given. ++ ++ if (desc != NULL) ++ fprintf(stderr, "%s:\n", desc); ++ ++ // Length checks. ++ ++ if (len == 0) { ++ fprintf(stderr, " ZERO LENGTH\n"); ++ return; ++ } ++ if (len < 0) { ++ fprintf(stderr, " NEGATIVE LENGTH: %d\n", len); ++ return; ++ } ++ ++ // Process every byte in the data. ++ ++ for (i = 0; i < len; i++) { ++ // Multiple of perLine means new or first line (with line offset). ++ ++ if ((i % perLine) == 0) { ++ // Only print previous-line ASCII buffer for lines beyond first. ++ ++ if (i != 0) ++ fprintf(stderr, " %s\n", buff); ++ ++ // Output the offset of current line. ++ ++ fprintf(stderr, " %04x ", i); ++ } ++ ++ // Now the hex code for the specific character. ++ ++ fprintf(stderr, " %02x", pc[i]); ++ ++ // And buffer a printable ASCII character for later. ++ ++ if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better. ++ buff[i % perLine] = '.'; ++ else ++ buff[i % perLine] = pc[i]; ++ buff[(i % perLine) + 1] = '\0'; ++ } ++ ++ // Pad out last line if not exactly perLine characters. ++ ++ while ((i % perLine) != 0) { ++ fprintf(stderr, " "); ++ i++; ++ } ++ ++ // And print the final ASCII buffer. ++ ++ fprintf(stderr, " %s\n", buff); ++} ++#define HEXDUMP ++#endif ++ + /* Try to communicate with an external http server. */ + static const char * + get_external_server_ip(void) +@@ -2608,6 +2681,36 @@ FormGet(struct mg_connection *conn, void *cbdata) + } + + ++static int ++FormError(struct mg_connection *conn, void *cbdata) ++{ ++ const struct mg_request_info *req_info = mg_get_request_info(conn); ++ int ret; ++ struct mg_form_data_handler fdh = {NULL, NULL, NULL, NULL}; ++ ++ (void)cbdata; ++ ++ ck_assert(req_info != NULL); ++ ++ mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n"); ++ fdh.user_data = (void *)&g_field_found_return; ++ ++ /* Call the form handler */ ++ g_field_step = 0; ++ g_field_found_return = MG_FORM_FIELD_STORAGE_GET; ++ ret = mg_handle_form_request(conn, &fdh); ++ g_field_found_return = -888; ++ ck_assert_int_eq(ret, -1); ++ ck_assert_int_eq(g_field_step, 0); ++ mg_printf(conn, "%i\r\n", ret); ++ g_field_step = 1000; ++ ++ mark_point(); ++ ++ return 1; ++} ++ ++ + static int + FormStore(struct mg_connection *conn, + void *cbdata, +@@ -2727,6 +2830,7 @@ START_TEST(test_handle_form) + ck_assert_str_eq(opt, "8884"); + + mg_set_request_handler(ctx, "/handle_form", FormGet, NULL); ++ mg_set_request_handler(ctx, "/handle_form_error", FormError, NULL); + mg_set_request_handler(ctx, "/handle_form_store", FormStore1, NULL); + mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, NULL); + +@@ -2797,6 +2901,23 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + ++ /* ++ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ * ++ * preamble := discard-text ++ * epilogue := discard-text ++ * ++ * discard-text := *(*text CRLF) *text ++ * ++ * text := ++ */ ++ + /* Handle form: "POST multipart/form-data" */ + multipart_body = + "--multipart-form-data-boundary--see-RFC-2388\r\n" +@@ -2922,16 +3043,102 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + ++ ++ /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ ++ /* use the most universal possible (no edge cases) body*/ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "%s", ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Transfer-Encoding: chunked\r\n" ++ "\r\n"); ++ ++ ck_assert(client_conn != NULL); ++ ++ body_len = strlen(multipart_body); ++ chunk_len = 1; ++ body_sent = 0; ++ while (body_len > body_sent) { ++ if (chunk_len > (body_len - body_sent)) { ++ chunk_len = body_len - body_sent; ++ } ++ ck_assert_int_gt((int)chunk_len, 0); ++ mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); ++ mg_write(client_conn, multipart_body + body_sent, chunk_len); ++ mg_printf(client_conn, "\r\n"); ++ body_sent += chunk_len; ++ chunk_len = (chunk_len % 40) + 1; ++ } ++ mg_printf(client_conn, "0\r\n\r\n"); ++ ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ ++ /* Handle form: "POST multipart/form-data" with chunked transfer ++ * encoding, using a quoted boundary string */ ++ client_conn = mg_download( ++ "localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "%s", ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=\"multipart-form-data-boundary--see-RFC-2388\"\r\n" ++ "Transfer-Encoding: chunked\r\n" ++ "\r\n"); ++ ++ ck_assert(client_conn != NULL); ++ ++ body_len = strlen(multipart_body); ++ chunk_len = 1; ++ body_sent = 0; ++ while (body_len > body_sent) { ++ if (chunk_len > (body_len - body_sent)) { ++ chunk_len = body_len - body_sent; ++ } ++ ck_assert_int_gt((int)chunk_len, 0); ++ mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); ++ mg_write(client_conn, multipart_body + body_sent, chunk_len); ++ mg_printf(client_conn, "\r\n"); ++ body_sent += chunk_len; ++ chunk_len = (chunk_len % 40) + 1; ++ } ++ mg_printf(client_conn, "0\r\n\r\n"); ++ ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ + /* Handle form: "POST multipart/form-data" without trailing CRLF*/ +- /* +- * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 +- * +- * multipart-body := [preamble CRLF] +- * dash-boundary transport-padding CRLF +- * body-part *encapsulation +- * close-delimiter transport-padding +- * [CRLF epilogue] +- */ + multipart_body = + "--multipart-form-data-boundary--see-RFC-2388\r\n" + "Content-Disposition: form-data; name=\"textin\"\r\n" +@@ -3057,21 +3264,6 @@ START_TEST(test_handle_form) + mg_close_connection(client_conn); + + /* Handle form: "POST multipart/form-data" with epilogue*/ +- /* +- * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 +- * +- * multipart-body := [preamble CRLF] +- * dash-boundary transport-padding CRLF +- * body-part *encapsulation +- * close-delimiter transport-padding +- * [CRLF epilogue] +- * +- * epilogue := discard-text +- * +- * discard-text := *(*text CRLF) *text +- * +- * text := +- */ + multipart_body = + "--multipart-form-data-boundary--see-RFC-2388\r\n" + "Content-Disposition: form-data; name=\"textin\"\r\n" +@@ -3164,16 +3356,16 @@ START_TEST(test_handle_form) + "\r\n" + "Text area default text.\r\n" + "--multipart-form-data-boundary--see-RFC-2388--\r\n" +- "epilogue\r\n" +- "epilogue\r\n" +- "\r\n" +- "epilogue\r\n" +- "\r\n" +- "\r\n" +- "\r\n" +- "epilogue\r\n"; ++ "epilogue\r\n" ++ "epilogue\r\n" ++ "\r\n" ++ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" ++ "\r\n" ++ "\r\n" ++ "\r\n" ++ "epilogue\r\n"; + body_len = strlen(multipart_body); +- ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ck_assert_uint_eq(body_len, 2453); /* not required */ + + client_conn = + mg_download("localhost", +@@ -3228,6 +3420,9 @@ START_TEST(test_handle_form) + "\r\n" + "\r\npreamble" + "\r\n" ++ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" ++ "\r\n" ++ "\r\n\t\t\t \t\t\t" + "\r\n" + "\r\n" + "--multipart-form-data-boundary--see-RFC-2388\r\n" +@@ -3322,7 +3517,7 @@ START_TEST(test_handle_form) + "Text area default text.\r\n" + "--multipart-form-data-boundary--see-RFC-2388--\r\n"; + body_len = strlen(multipart_body); +- ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ck_assert_uint_eq(body_len, 2478); /* not required */ + + client_conn = + mg_download("localhost", +@@ -3353,21 +3548,7 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + +- + /* Handle form: "POST multipart/form-data" with transport padding*/ +- /* +- * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 +- * +- * multipart-body := [preamble CRLF] +- * dash-boundary transport-padding CRLF +- * body-part *encapsulation +- * close-delimiter transport-padding +- * [CRLF epilogue] +- * +- * transport-padding := *LWSP-char +- * +- * LWSP-char := SPACE / HTAB +- */ + multipart_body = + "--multipart-form-data-boundary--see-RFC-2388 \r\n" + "Content-Disposition: form-data; name=\"textin\"\r\n" +@@ -3461,7 +3642,7 @@ START_TEST(test_handle_form) + "Text area default text.\r\n" + "--multipart-form-data-boundary--see-RFC-2388--\r\n"; + body_len = strlen(multipart_body); +- ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ck_assert_uint_eq(body_len, 2382); /* not required */ + + client_conn = + mg_download("localhost", +@@ -3492,40 +3673,125 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + +- /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ ++ /* Handle form: "POST multipart/form-data" with custom name fields in the ++ * Content-Disposition */ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; " ++ "custom1name=\"1\"; " ++ "custom2name=\"2\"; " ++ "custom3name=\"3\"; " ++ "custom4name=\"4\"; " ++ "name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2439); /* not required */ ++ + client_conn = + mg_download("localhost", + 8884, + 0, + ebuf, + sizeof(ebuf), +- "%s", + "POST /handle_form HTTP/1.1\r\n" + "Host: localhost:8884\r\n" + "Connection: close\r\n" + "Content-Type: multipart/form-data; " + "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" +- "Transfer-Encoding: chunked\r\n" +- "\r\n"); ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); + + ck_assert(client_conn != NULL); +- +- body_len = strlen(multipart_body); +- chunk_len = 1; +- body_sent = 0; +- while (body_len > body_sent) { +- if (chunk_len > (body_len - body_sent)) { +- chunk_len = body_len - body_sent; +- } +- ck_assert_int_gt((int)chunk_len, 0); +- mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); +- mg_write(client_conn, multipart_body + body_sent, chunk_len); +- mg_printf(client_conn, "\r\n"); +- body_sent += chunk_len; +- chunk_len = (chunk_len % 40) + 1; +- } +- mg_printf(client_conn, "0\r\n\r\n"); +- + for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { + test_sleep(1); + if (g_field_step == 1000) { +@@ -3538,41 +3804,29 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + +- /* Handle form: "POST multipart/form-data" with chunked transfer +- * encoding, using a quoted boundary string */ +- client_conn = mg_download( +- "localhost", +- 8884, +- 0, +- ebuf, +- sizeof(ebuf), +- "%s", +- "POST /handle_form HTTP/1.1\r\n" +- "Host: localhost:8884\r\n" +- "Connection: close\r\n" +- "Content-Type: multipart/form-data; " +- "boundary=\"multipart-form-data-boundary--see-RFC-2388\"\r\n" +- "Transfer-Encoding: chunked\r\n" +- "\r\n"); +- +- ck_assert(client_conn != NULL); +- ++ /* Handle form error cases */ ++ /* Handle form: "POST multipart/form-data" empty body */ ++ multipart_body = ""; + body_len = strlen(multipart_body); +- chunk_len = 1; +- body_sent = 0; +- while (body_len > body_sent) { +- if (chunk_len > (body_len - body_sent)) { +- chunk_len = body_len - body_sent; +- } +- ck_assert_int_gt((int)chunk_len, 0); +- mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); +- mg_write(client_conn, multipart_body + body_sent, chunk_len); +- mg_printf(client_conn, "\r\n"); +- body_sent += chunk_len; +- chunk_len = (chunk_len % 40) + 1; +- } +- mg_printf(client_conn, "0\r\n\r\n"); ++ ck_assert_uint_eq(body_len, 0); /* not required */ + ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form_error HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); + for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { + test_sleep(1); + if (g_field_step == 1000) { +-- +2.34.1 + + +From 644d3cdeaa8939e661cd26c9005b901130f44f2a Mon Sep 17 00:00:00 2001 +From: tim +Date: Thu, 8 Aug 2024 23:28:30 +0100 +Subject: [PATCH 077/173] [#1276] remove debug method + +--- + unittest/public_server.c | 73 ---------------------------------------- + 1 file changed, 73 deletions(-) + +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 2d244297..75663dac 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -57,79 +57,6 @@ + #define SLEEP_AFTER_MG_STOP (5) + + +-#ifdef HEXDUMP +-#else +-void +-hexDumpth(const char *desc, const void *addr, const int len, int perLine) +-{ +- // Silently ignore silly per-line values. +- +- if (perLine < 4 || perLine > 64) +- perLine = 16; +- +- int i; +- unsigned char buff[perLine + 1]; +- const unsigned char *pc = (const unsigned char *)addr; +- +- // Output description if given. +- +- if (desc != NULL) +- fprintf(stderr, "%s:\n", desc); +- +- // Length checks. +- +- if (len == 0) { +- fprintf(stderr, " ZERO LENGTH\n"); +- return; +- } +- if (len < 0) { +- fprintf(stderr, " NEGATIVE LENGTH: %d\n", len); +- return; +- } +- +- // Process every byte in the data. +- +- for (i = 0; i < len; i++) { +- // Multiple of perLine means new or first line (with line offset). +- +- if ((i % perLine) == 0) { +- // Only print previous-line ASCII buffer for lines beyond first. +- +- if (i != 0) +- fprintf(stderr, " %s\n", buff); +- +- // Output the offset of current line. +- +- fprintf(stderr, " %04x ", i); +- } +- +- // Now the hex code for the specific character. +- +- fprintf(stderr, " %02x", pc[i]); +- +- // And buffer a printable ASCII character for later. +- +- if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better. +- buff[i % perLine] = '.'; +- else +- buff[i % perLine] = pc[i]; +- buff[(i % perLine) + 1] = '\0'; +- } +- +- // Pad out last line if not exactly perLine characters. +- +- while ((i % perLine) != 0) { +- fprintf(stderr, " "); +- i++; +- } +- +- // And print the final ASCII buffer. +- +- fprintf(stderr, " %s\n", buff); +-} +-#define HEXDUMP +-#endif +- + /* Try to communicate with an external http server. */ + static const char * + get_external_server_ip(void) +-- +2.34.1 + + +From c8bdc706b69f3276e381334c0ab6421204df08e4 Mon Sep 17 00:00:00 2001 +From: tim +Date: Thu, 8 Aug 2024 23:37:15 +0100 +Subject: [PATCH 078/173] [#1276] limit preamble, add test case + +--- + src/handle_form.inl | 14 ++++--- + unittest/public_server.c | 80 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 89 insertions(+), 5 deletions(-) + +diff --git a/src/handle_form.inl b/src/handle_form.inl +index b22f17ef..4de8d4f5 100644 +--- a/src/handle_form.inl ++++ b/src/handle_form.inl +@@ -716,9 +716,11 @@ mg_handle_form_request(struct mg_connection *conn, + + if (part_no == 0) { + size_t preamble_length = 0; +- /* skip over the preamble until we find a complete boundary */ ++ /* skip over the preamble until we find a complete boundary ++ * limit the preamble length to prevent abuse */ + /* +2 for the -- preceding the boundary */ +- while ((preamble_length < buf_fill - bl) ++ while (preamble_length < 1024 ++ && (preamble_length < buf_fill - bl) + && strncmp(buf + preamble_length + 2, boundary, bl)) { + preamble_length++; + } +@@ -732,9 +734,11 @@ mg_handle_form_request(struct mg_connection *conn, + } + } + +- /* either it starts with a boundary +- * or we couldn't find a boundary at all in the body +- * or we didn't have a terminating boundary */ ++ /* either it starts with a boundary and it's fine, or it's malformed ++ * because: ++ * - the preamble was longer than accepted ++ * - couldn't find a boundary at all in the body ++ * - didn't have a terminating boundary */ + if (buf_fill < (bl + 2) || strncmp(buf, "--", 2) + || strncmp(buf + 2, boundary, bl)) { + /* Malformed request */ +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 75663dac..6e0f71c7 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -3766,6 +3766,86 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + ++ /* Handle form: "POST multipart/form-data" very long preamble */ ++ multipart_body = ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n"; ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 1768); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form_error HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); + + /* Now test form_store */ + +-- +2.34.1 + + +From 1c5cce5b61ce05565ed0a776292db6a6d77596c2 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Sat, 10 Aug 2024 12:58:15 +0200 +Subject: [PATCH 079/173] Respect NO_DLOPEN also in Lua code + +Signed-off-by: DL6ER +--- + src/mod_lua.inl | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index 9a2164c3..ea17776f 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -3679,7 +3679,7 @@ lua_init_optional_libraries(void) + lua_shared_init(); + + /* UUID library */ +-#if !defined(_WIN32) ++#if !defined(_WIN32) && !defined(NO_DLOPEN) + lib_handle_uuid = dlopen("libuuid.so", RTLD_LAZY); + pf_uuid_generate.p = + (lib_handle_uuid ? dlsym(lib_handle_uuid, "uuid_generate") : 0); +@@ -3693,7 +3693,7 @@ static void + lua_exit_optional_libraries(void) + { + /* UUID library */ +-#if !defined(_WIN32) ++#if !defined(_WIN32) && !defined(NO_DLOPEN) + if (lib_handle_uuid) { + dlclose(lib_handle_uuid); + } +-- +2.34.1 + + +From b1251ad6ccb3f25e2cb107582cdf755dc305f5ff Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Sat, 10 Aug 2024 13:04:10 +0200 +Subject: [PATCH 080/173] Send correct HTTP error code when serving an error + page. Currently, this is broken and even 404 errors are served with HTTP 200 + OK while they clearly shouldn't. + +Signed-off-by: DL6ER +--- + src/mod_lua.inl | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index 9a2164c3..1988a4fd 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -641,7 +641,10 @@ run_lsp_kepler(struct mg_connection *conn, + /* Only send a HTML header, if this is the top level page. + * If this page is included by some mg.include calls, do not add a + * header. */ +- mg_printf(conn, "HTTP/1.1 200 OK\r\n"); ++ if(conn->status_code < 0) ++ mg_printf(conn, "HTTP/1.1 200 OK\r\n"); ++ else ++ mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(conn, conn->status_code)); + send_no_cache_header(conn); + send_additional_header(conn); + mg_printf(conn, +-- +2.34.1 + + +From 179eefe8427f449d2c147015aa28e2012315777c Mon Sep 17 00:00:00 2001 +From: leyuskckiran1510 +Date: Mon, 12 Aug 2024 22:24:37 +0545 +Subject: [PATCH 081/173] docs: fix typo on mg_form_data_handler api docs + +--- + docs/api/mg_form_data_handler.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/api/mg_form_data_handler.md b/docs/api/mg_form_data_handler.md +index 368d7786..f9d1f43a 100644 +--- a/docs/api/mg_form_data_handler.md ++++ b/docs/api/mg_form_data_handler.md +@@ -9,7 +9,7 @@ + |**`field_found`**|**`int field_found( const char *key, const char *filename, char *path, size_t pathlen, void *user_data )`**;| + ||The callback function `field_found()` is called when a new field has been found. The return value of this callback is used to define how the field should be processed. The parameters contain the following information:| + ||**`key`** - The name of the field as it was named with the `name` tag in the HTML source.| +-||**`filename`** - The name of the file to upload. Please not that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.| ++||**`filename`** - The name of the file to upload. Please note that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.| + ||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.| + ||**`pathlen`** - The length of the buffer where the output path can be stored.| + ||**`user_data`** - A pointer to the value of the field `user_data` of the structure `struct mg_form_data_handler`.| +-- +2.34.1 + + +From 9ebf36b3e8aa2b5c9166437ca0c9f35e8863d2e3 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Thu, 15 Aug 2024 21:33:26 +0200 +Subject: [PATCH 082/173] Fix comparison of integer expressions of different + signedness warnings in mg_handle_form_request (GCC -Wsign-compare) + +Signed-off-by: DL6ER +--- + src/handle_form.inl | 32 ++++++++++++++++---------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +diff --git a/src/handle_form.inl b/src/handle_form.inl +index 4de8d4f5..a7b7fc10 100644 +--- a/src/handle_form.inl ++++ b/src/handle_form.inl +@@ -188,7 +188,7 @@ mg_handle_form_request(struct mg_connection *conn, + char path[512]; + char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */ + int field_storage; +- int buf_fill = 0; ++ size_t buf_fill = 0; + int r; + int field_count = 0; + struct mg_file fstore = STRUCT_FILE_INITIALIZER; +@@ -397,10 +397,10 @@ mg_handle_form_request(struct mg_connection *conn, + int end_of_key_value_pair_found = 0; + int get_block; + +- if ((size_t)buf_fill < (sizeof(buf) - 1)) { ++ if (buf_fill < (sizeof(buf) - 1)) { + +- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ size_t to_read = sizeof(buf) - 1 - buf_fill; ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + /* read error */ + return -1; +@@ -529,11 +529,11 @@ mg_handle_form_request(struct mg_connection *conn, + buf + (size_t)used, + sizeof(buf) - (size_t)used); + next = buf; +- buf_fill -= (int)used; +- if ((size_t)buf_fill < (sizeof(buf) - 1)) { ++ buf_fill -= used; ++ if (buf_fill < (sizeof(buf) - 1)) { + +- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ size_t to_read = sizeof(buf) - 1 - buf_fill; ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + #if !defined(NO_FILESYSTEMS) + /* read error */ +@@ -592,7 +592,7 @@ mg_handle_form_request(struct mg_connection *conn, + /* Proceed to next entry */ + used = next - buf; + memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); +- buf_fill -= (int)used; ++ buf_fill -= used; + } + + return field_count; +@@ -682,12 +682,12 @@ mg_handle_form_request(struct mg_connection *conn, + for (part_no = 0;; part_no++) { + size_t towrite, fnlen, n; + int get_block; +- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; ++ size_t to_read = sizeof(buf) - 1 - buf_fill; + + /* Unused without filesystems */ + (void)n; + +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + /* read error */ + mg_free(boundary); +@@ -1001,12 +1001,12 @@ mg_handle_form_request(struct mg_connection *conn, + #endif /* NO_FILESYSTEMS */ + + memmove(buf, hend + towrite, bl + 4); +- buf_fill = (int)(bl + 4); ++ buf_fill = bl + 4; + hend = buf; + + /* Read new data */ +- to_read = sizeof(buf) - 1 - (size_t)buf_fill; +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ to_read = sizeof(buf) - 1 - buf_fill; ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + #if !defined(NO_FILESYSTEMS) + /* read error */ +@@ -1025,7 +1025,7 @@ mg_handle_form_request(struct mg_connection *conn, + /* buf_fill is at least 8 here */ + + /* Find boundary */ +- next = search_boundary(buf, (size_t)buf_fill, boundary, bl); ++ next = search_boundary(buf, buf_fill, boundary, bl); + + if (!next && (r == 0)) { + /* incomplete request */ +@@ -1100,7 +1100,7 @@ mg_handle_form_request(struct mg_connection *conn, + if (next) { + used = next - buf + 2; + memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); +- buf_fill -= (int)used; ++ buf_fill -= used; + } else { + buf_fill = 0; + } +-- +2.34.1 + + +From b527020950de516b2a45cfed8660744445d1aee5 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Fri, 16 Aug 2024 18:57:35 +0200 +Subject: [PATCH 083/173] Add missing PSA (Platform Security Architecture) + cryptography API initialization + +It is mandatory when PSA is used which is, in turn, mandatory when using TLS 1.3 (`MBEDTLS_SSL_PROTO_TLS1_3`). When not being initialized, the server will finish startup but any encrypted traffic will cause errors deep inside the mbedTLS library (return code `0x6c00 == MBEDTLS_ERR_SSL_INTERNAL_ERROR`). + +Signed-off-by: DL6ER +--- + src/mod_mbedtls.inl | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl +index c0333b21..93f1bb29 100644 +--- a/src/mod_mbedtls.inl ++++ b/src/mod_mbedtls.inl +@@ -88,6 +88,18 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + mbedtls_ctr_drbg_init(&ctx->ctr); + mbedtls_x509_crt_init(&ctx->cert); + ++#ifdef MBEDTLS_PSA_CRYPTO_C ++ /* Initialize PSA crypto (mandatory with TLS 1.3) ++ * This must be done before calling any other PSA Crypto ++ * functions or they will fail with PSA_ERROR_BAD_STATE ++ */ ++ const psa_status_t status = psa_crypto_init(); ++ if (status != PSA_SUCCESS) { ++ DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n", (int) status); ++ return -1; ++ } ++#endif ++ + rc = mbedtls_ctr_drbg_seed(&ctx->ctr, + mbedtls_entropy_func, + &ctx->entropy, +-- +2.34.1 + + +From 616830868fe52bf37ee1141d8f349ed3ba24ab01 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Sun, 18 Aug 2024 07:21:34 +0200 +Subject: [PATCH 084/173] Fix header generation for Lua Server Pages (Kepler + syntax). + +The currently used code has send_no_cache_header() and send_additional_header() but NOT actually a call to mg_response_header_send(). When NO_RESPONSE_BUFFERING is not set (default), this merely adds the additional headers to the buffer but does not send them. As a final call to mg_response_header_send() was indeed missing here, they are never sent causing, e.g., headers added through the "additional_headers" config string to be ignored for Kepler LSP. Similarly, CORS and cache-control headers were missing in this case. + +Signed-off-by: DL6ER +--- + src/mod_lua.inl | 22 +++++++++++++--------- + 1 file changed, 13 insertions(+), 9 deletions(-) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index 1988a4fd..94f413a7 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -641,17 +641,21 @@ run_lsp_kepler(struct mg_connection *conn, + /* Only send a HTML header, if this is the top level page. + * If this page is included by some mg.include calls, do not add a + * header. */ +- if(conn->status_code < 0) +- mg_printf(conn, "HTTP/1.1 200 OK\r\n"); +- else +- mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(conn, conn->status_code)); ++ ++ /* Initialize a new HTTP response, either with some-predefined ++ * status code (e.g. 404 if this is called from an error ++ * handler) or with 200 OK */ ++ mg_response_header_start(conn, conn->status_code > 0 ? conn->status_code : 200); ++ ++ /* Add additional headers */ + send_no_cache_header(conn); + send_additional_header(conn); +- mg_printf(conn, +- "Date: %s\r\n" +- "Connection: close\r\n" +- "Content-Type: text/html; charset=utf-8\r\n\r\n", +- date); ++ ++ /* Add content type */ ++ mg_response_header_add(conn, "Content-Type", "text/html; charset=utf-8", -1); ++ ++ /* Send the HTTP response (status and all headers) */ ++ mg_response_header_send(conn); + } + + data.begin = p; +-- +2.34.1 + + +From 7fbfe8e32524a7ec590d98d824fe06f2da185c64 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 09:55:20 +0200 +Subject: [PATCH 085/173] Add devcontainer + +Signed-off-by: yubiuser +--- + .devcontainer/Dockerfile | 12 ++++++++++++ + .devcontainer/devcontainer.json | 16 ++++++++++++++++ + 2 files changed, 28 insertions(+) + create mode 100644 .devcontainer/Dockerfile + create mode 100644 .devcontainer/devcontainer.json + +diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile +new file mode 100644 +index 00000000..71bc1462 +--- /dev/null ++++ b/.devcontainer/Dockerfile +@@ -0,0 +1,12 @@ ++# syntax=docker/dockerfile:1 ++ARG ubuntu_version=24.04 ++ ++FROM ubuntu:${ubuntu_version} ++RUN apt-get update && \ ++ apt-get install -y \ ++ build-essential \ ++ ca-certificates \ ++ cmake \ ++ git \ ++ nano\ ++ openssh-server +\ No newline at end of file +diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json +new file mode 100644 +index 00000000..b3030fdb +--- /dev/null ++++ b/.devcontainer/devcontainer.json +@@ -0,0 +1,16 @@ ++{ ++ "name": "Civetweb", ++ "dockerFile": "Dockerfile", ++ "customizations": { ++ "vscode": { ++ "extensions": [ ++ "ms-vscode.cpptools", ++ "eamodio.gitlens" ++ ] ++ } ++ }, ++ "mounts": [ ++ "type=bind,source=/home/${localEnv:USER}/.ssh,target=/root/.ssh,readonly" ++ ] ++ ++ } +\ No newline at end of file +-- +2.34.1 + + +From 67d91b1e4f4dff2eb21fff7296533494fed47127 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 10:29:46 +0200 +Subject: [PATCH 086/173] Add linux build + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 332 ++++++++++++++++++++++++++++++++++ + 1 file changed, 332 insertions(+) + create mode 100644 .github/workflows/cibuild.yml + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +new file mode 100644 +index 00000000..3dc5fe81 +--- /dev/null ++++ b/.github/workflows/cibuild.yml +@@ -0,0 +1,332 @@ ++name: CI build ++ ++on: ++ push: ++ pull_request: ++ release: ++ types: [published] ++ workflow_dispatch: ++jobs: ++ build: ++ runs-on: ${{ matrix.os }} ++ strategy: ++ fail-fast: true ++ matrix: ++ include: ++ - os: ubuntu-latest ++ compiler: clang ++ env: ++ idx: 1 ++ N: Clang-Linux-Minimal-Debug ++ BUILD_TYPE: Debug ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: YES ++ ENABLE_SSL: NO ++ NO_CGI: YES ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_SERVER_STATS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ ++ - os: ubuntu-latest ++ compiler: clang ++ env: ++ idx: 3 ++ N: Clang-Linux-Default-Release ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_SERVER_STATS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ idx: 5 ++ N: GCC-Linux-Complete-NoLua-Release ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: YES ++ ENABLE_WEBSOCKETS: YES ++ ENABLE_SERVER_STATS: YES ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: YES ++ ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 ++ ++ - os: ubuntu-latest ++ compiler: clang ++ env: ++ idx: 6 ++ N: CLANG-AnyVersion-Linux-Coverage ++ BUILD_TYPE: Coverage ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: YES ++ ENABLE_WEBSOCKETS: YES ++ ENABLE_SERVER_STATS: YES ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 ++ ++ - os: ubuntu-latest ++ compiler: clang ++ env: ++ idx: 9 ++ N: Clang-Linux-Default-Shared ++ BUILD_TYPE: Debug ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: YES ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_SERVER_STATS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ idx: 15 ++ N: GCCLinuxDefault_RelWithDebInfo ++ BUILD_TYPE: RelWithDebInfo ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ idx: 16 ++ N: GCCLinuxDefault_MinSizeRel ++ BUILD_TYPE: MinSizeRel ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ idx: 17 ++ N: GCCLinuxDefault_None ++ BUILD_TYPE: None ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ idx: 20 ++ N: GCCLinuxDefault_xenial ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ idx: 23 ++ N: GCCLinuxDefault_focal ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 ++ ++# Remove Lua build, until someone knows how to fix the CMake files ++ # - os: ubuntu-latest ++ # compiler: clang ++ # env: ++ # idx: 99 ++ # N: Clang-Linux-Complete-WithLua-Debug ++ # BUILD_TYPE: Debug ++ # ENABLE_SSL_DYNAMIC_LOADING: YES ++ # OPENSSL_1_0: NO ++ # OPENSSL_1_1: YES ++ # ENABLE_CXX: NO ++ # C_STANDARD: auto ++ # CXX_STANDARD: auto ++ # BUILD_SHARED: NO ++ # NO_FILES: NO ++ # ENABLE_SSL: YES ++ # NO_CGI: NO ++ # ENABLE_IPV6: YES ++ # ENABLE_WEBSOCKETS: YES ++ # ENABLE_SERVER_STATS: YES ++ # ENABLE_LUA: YES ++ # ENABLE_LUA_SHARED: YES ++ # ENABLE_DUKTAPE: NO ++ # NO_CACHING: YES ++ # ALLOW_WARNINGS: YES ++ ++ ++ steps: ++ - name: Checkout code ++ uses: actions/checkout@v4.1.7 ++ ++ - name: Install clang on Linux ++ if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' ++ run: | ++ sudo apt-get install -y clang ++ sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 ++ sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 ++ ++ - name: Build ++ run: | ++ cmake -S . -B CMakeFiles\ ++ -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ ++ -DBUILD_SHARED_LIBS=${{ matrix.env.BUILD_SHARED }}\ ++ -DCIVETWEB_THIRD_PARTY_DIR=../src/third-party\ ++ -DCIVETWEB_ENABLE_THIRD_PARTY_OUTPUT=YES\ ++ -DCIVETWEB_ENABLE_SSL=${{ matrix.env.ENABLE_SSL }}\ ++ -DCIVETWEB_DISABLE_CGI=${{ matrix.env.NO_CGI }}\ ++ -DCIVETWEB_SERVE_NO_FILES=${{ matrix.env.NO_FILES }}\ ++ -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${{ matrix.env.ENABLE_SSL_DYNAMIC_LOADING }}\ ++ -DCIVETWEB_SSL_OPENSSL_API_1_0=${{ matrix.env.OPENSSL_1_0 }}\ ++ -DCIVETWEB_SSL_OPENSSL_API_1_1=${{ matrix.env.OPENSSL_1_1 }}\ ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=${{ matrix.env.OPENSSL_3_0 }}\ ++ -DCIVETWEB_ENABLE_WEBSOCKETS=${{ matrix.env.ENABLE_WEBSOCKETS }}\ ++ -DCIVETWEB_ENABLE_CXX=${{ matrix.env.ENABLE_CXX }}\ ++ -DCIVETWEB_ENABLE_SERVER_STATS=${{ matrix.env.ENABLE_SERVER_STATS }}\ ++ -DCIVETWEB_ENABLE_LUA=${{ matrix.env.ENABLE_LUA }}\ ++ -DCIVETWEB_ENABLE_LUA_SHARED=${{ matrix.env.ENABLE_LUA_SHARED }}\ ++ -DCIVETWEB_ENABLE_DUKTAPE=${{ matrix.env.ENABLE_DUKTAPE }}\ ++ -DCIVETWEB_DISABLE_CACHING=${{ matrix.env.NO_CACHING }}\ ++ -DCIVETWEB_C_STANDARD=${{ matrix.env.C_STANDARD }}\ ++ -DCIVETWEB_CXX_STANDARD=${{ matrix.env.CXX_STANDARD }}\ ++ -DCIVETWEB_ALLOW_WARNINGS=${{ matrix.env.ALLOW_WARNINGS }}\ ++ -DCIVETWEB_ENABLE_IPV6=${{ matrix.env.ENABLE_IPV6 }}\ ++ ${{ env.ADDITIONAL_CMAKE_ARGS }} ++ cmake --build CMakeFiles -- -j $(nproc) ++ ++ - name: Verify ++ run: | ++ ./CMakeFiles/src/civetweb -I +\ No newline at end of file +-- +2.34.1 + + +From 92904b6aea8a7453f49cbcd4fd45828921d00985 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 21:59:31 +0200 +Subject: [PATCH 087/173] MacOS builds + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 108 ++++++++++++++++++++++++++++++---- + 1 file changed, 95 insertions(+), 13 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 3dc5fe81..c3ed4ad6 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -37,7 +37,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES +- ++ + - os: ubuntu-latest + compiler: clang + env: +@@ -62,7 +62,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES +- ++ + - os: ubuntu-latest + compiler: gcc + env: +@@ -88,7 +88,7 @@ jobs: + NO_CACHING: YES + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 +- ++ + - os: ubuntu-latest + compiler: clang + env: +@@ -114,7 +114,7 @@ jobs: + NO_CACHING: NO + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 +- ++ + - os: ubuntu-latest + compiler: clang + env: +@@ -139,7 +139,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES +- ++ + - os: ubuntu-latest + compiler: gcc + env: +@@ -163,7 +163,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES +- ++ + - os: ubuntu-latest + compiler: gcc + env: +@@ -187,7 +187,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES +- ++ + - os: ubuntu-latest + compiler: gcc + env: +@@ -211,7 +211,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES +- ++ + - os: ubuntu-latest + compiler: gcc + env: +@@ -235,7 +235,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES +- ++ + - os: ubuntu-latest + compiler: gcc + env: +@@ -287,19 +287,93 @@ jobs: + # NO_CACHING: YES + # ALLOW_WARNINGS: YES + ++ - os: macos-latest ++ compiler: clang ++ env: ++ idx: 8 ++ N: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: YES ++ ENABLE_WEBSOCKETS: YES ++ ENABLE_SERVER_STATS: YES ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: YES ++ ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 ++ ++ - os: macos-latest ++ compiler: clang ++ env: ++ idx: 11 ++ N: OSX-Package ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: YES ++ OPENSSL_1_1: NO ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: YES ++ ENABLE_WEBSOCKETS: YES ++ ENABLE_SERVER_STATS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ MACOSX_PACKAGE: 1 ++ + + steps: + - name: Checkout code + uses: actions/checkout@v4.1.7 +- ++ + - name: Install clang on Linux + if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install -y clang + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 ++ ++ - name: Set up OpenSSL 1.1 on MacOS ++ if: matrix.os == 'macos-latest' && matrix.env.OPENSSL_1_1 == 'YES' ++ run: | ++ OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) ++ LDFLAGS=-L{$OPENSSL_ROOT_DIR}/lib ++ CFLAGS=-I${OPENSSL_ROOT_DIR}/include ++ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" ++ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig + +- - name: Build ++ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV ++ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV ++ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH ++ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV ++ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ ++ - name: Print tool version information ++ run: | ++ openssl version ++ cc --version ++ cmake --version ++ clang --version ++ ++ - name: Run CMake + run: | + cmake -S . -B CMakeFiles\ + -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ +@@ -325,8 +399,16 @@ jobs: + -DCIVETWEB_ALLOW_WARNINGS=${{ matrix.env.ALLOW_WARNINGS }}\ + -DCIVETWEB_ENABLE_IPV6=${{ matrix.env.ENABLE_IPV6 }}\ + ${{ env.ADDITIONAL_CMAKE_ARGS }} +- cmake --build CMakeFiles -- -j $(nproc) ++ ++ - name: Build MacOS Package ++ if: matrix.env.MACOSX_PACKAGE == 1 ++ run: | ++ make -f Makefile.osx package + +- - name: Verify ++ - name: Build executable ++ run: | ++ cmake --build CMakeFiles -- -j $(nproc) ++ ++ - name: Check executable + run: | + ./CMakeFiles/src/civetweb -I +\ No newline at end of file +-- +2.34.1 + + +From c62054609add2d14f34a9dd796edfd7f46bcf222 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 22:00:28 +0200 +Subject: [PATCH 088/173] Drop i386 arch for MacOS + +Signed-off-by: yubiuser +--- + Makefile.osx | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile.osx b/Makefile.osx +index 44260e84..970579fc 100644 +--- a/Makefile.osx ++++ b/Makefile.osx +@@ -13,7 +13,7 @@ WITH_LUA = 1 + PACKAGE = Civetweb + BUILD_DIR = out + +-CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch i386 -arch x86_64 ++CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch x86_64 -arch arm64 + LDFLAGS += -framework Cocoa + + DMG_DIR = $(BUILD_DIR)/dmg +-- +2.34.1 + + +From d04bbfe89b88eb6706a9ec17b0e2cbbb88f06ab1 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 22:01:30 +0200 +Subject: [PATCH 089/173] Do not set XOPEN__SOURCE when USE_COCOA is set + +Signed-off-by: yubiuser +--- + src/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/main.c b/src/main.c +index f4d408e2..9d3a0ea7 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -41,7 +41,7 @@ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wreserved-id-macro" + #endif +-#if !defined(_XOPEN_SOURCE) ++#if !defined(_XOPEN_SOURCE) && !defined(USE_COCOA) + #define _XOPEN_SOURCE 600 /* For PATH_MAX on linux */ + /* This should also be sufficient for "realpath", according to + * http://man7.org/linux/man-pages/man3/realpath.3.html, but in +-- +2.34.1 + + +From 160a078eb28b767421926e0bcb6f45a93fc2d220 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 22:02:04 +0200 +Subject: [PATCH 090/173] Bump macosx-version-min to 10.6 to fix deprecation + warning + +Signed-off-by: yubiuser +--- + Makefile.osx | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile.osx b/Makefile.osx +index 970579fc..a14fdee9 100644 +--- a/Makefile.osx ++++ b/Makefile.osx +@@ -13,7 +13,7 @@ WITH_LUA = 1 + PACKAGE = Civetweb + BUILD_DIR = out + +-CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch x86_64 -arch arm64 ++CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.6 -ObjC -arch x86_64 -arch arm64 + LDFLAGS += -framework Cocoa + + DMG_DIR = $(BUILD_DIR)/dmg +-- +2.34.1 + + +From b43cc5be976255bcd09a18ca9103f41b5efff05e Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 22:23:44 +0200 +Subject: [PATCH 091/173] Add dependabot to keep github-actions up-to-date + +Signed-off-by: yubiuser +--- + .github/dependabot.yml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + create mode 100644 .github/dependabot.yml + +diff --git a/.github/dependabot.yml b/.github/dependabot.yml +new file mode 100644 +index 00000000..c20f0118 +--- /dev/null ++++ b/.github/dependabot.yml +@@ -0,0 +1,10 @@ ++version: 2 ++updates: ++- package-ecosystem: github-actions ++ directory: "/" ++ schedule: ++ interval: weekly ++ day: saturday ++ time: "10:00" ++ open-pull-requests-limit: 10 ++ target-branch: master +-- +2.34.1 + + +From 0088462e95e78374308f878bb1b6adf655c6d9e7 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 18 Aug 2024 22:34:10 +0200 +Subject: [PATCH 092/173] Fix Lua SQLite Verification Hash + +Signed-off-by: yubiuser +--- + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e1aa002b..4ffd41b9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -188,7 +188,7 @@ if (CIVETWEB_ENABLE_LUA) + mark_as_advanced(CIVETWEB_LUA_SQLITE_VERSION) + + # Lua SQLite Verification Hash +- set(CIVETWEB_LUA_SQLITE_MD5_HASH 43234ae08197dfce6da02482ed14ec92 CACHE STRING ++ set(CIVETWEB_LUA_SQLITE_MD5_HASH ff7abd4aa8bd549eb18298fb954612f8 CACHE STRING + "The hash of Lua SQLite archive to be downloaded") + set_property(CACHE CIVETWEB_LUA_SQLITE_MD5_HASH PROPERTY VALUE ${CIVETWEB_LUA_SQLITE_MD5_HASH}) + mark_as_advanced(CIVETWEB_LUA_SQLITE_MD5_HASH) +-- +2.34.1 + + +From 2ff558cdde172468f5d8a77d46eb8a6b650c8717 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Tue, 20 Aug 2024 13:03:27 +0200 +Subject: [PATCH 093/173] Add clang and set --no-install-recommends + +Signed-off-by: yubiuser +--- + .devcontainer/Dockerfile | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile +index 71bc1462..81fdf3e9 100644 +--- a/.devcontainer/Dockerfile ++++ b/.devcontainer/Dockerfile +@@ -3,10 +3,11 @@ ARG ubuntu_version=24.04 + + FROM ubuntu:${ubuntu_version} + RUN apt-get update && \ +- apt-get install -y \ ++ apt-get install --no-install-recommends -y \ + build-essential \ + ca-certificates \ + cmake \ ++ clang \ + git \ + nano\ + openssh-server +\ No newline at end of file +-- +2.34.1 + + +From dbcf24552d2c6bf514bf9459d0d5e59b4ccbc31c Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Tue, 20 Aug 2024 13:50:55 +0200 +Subject: [PATCH 094/173] --no-install-recommends + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index c3ed4ad6..39b0bc86 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -347,7 +347,7 @@ jobs: + - name: Install clang on Linux + if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' + run: | +- sudo apt-get install -y clang ++ sudo apt-get install --no-install-recommends -y clang + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 + +-- +2.34.1 + + +From 2a306508b5f8f83383c49cd755cafbbc6e8832c7 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Fri, 23 Aug 2024 12:55:10 +0200 +Subject: [PATCH 095/173] Run unittests + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 112 +++++++++++++++++++++++++++++++--- + .gitignore | 1 + + 2 files changed, 104 insertions(+), 9 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 39b0bc86..7fe95108 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -37,6 +37,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + - os: ubuntu-latest + compiler: clang +@@ -62,6 +63,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + - os: ubuntu-latest + compiler: gcc +@@ -139,6 +141,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + - os: ubuntu-latest + compiler: gcc +@@ -163,6 +166,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + - os: ubuntu-latest + compiler: gcc +@@ -187,6 +191,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + - os: ubuntu-latest + compiler: gcc +@@ -211,6 +216,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + - os: ubuntu-latest + compiler: gcc +@@ -235,6 +241,7 @@ jobs: + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + - os: ubuntu-latest + compiler: gcc +@@ -261,8 +268,9 @@ jobs: + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 + +-# Remove Lua build, until someone knows how to fix the CMake files +- # - os: ubuntu-latest ++ # Disable Lua build, until someone knows how to fix the CMake files ++ # see https://github.com/civetweb/civetweb/issues/543 ++ # - os: ubuntu-lastest + # compiler: clang + # env: + # idx: 99 +@@ -286,6 +294,7 @@ jobs: + # ENABLE_DUKTAPE: NO + # NO_CACHING: YES + # ALLOW_WARNINGS: YES ++ # RUN_UNITTEST: 1 + + - os: macos-latest + compiler: clang +@@ -313,7 +322,10 @@ jobs: + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 + +- - os: macos-latest ++ # mac-os 13 is the last version of MacOS runner using x86_64 architecture ++ # mac-os 14 and later are using arm64 architecture ++ # but OpenSSL 1.0 can't compile on arm64, so we set it fixed to mac-os 13 ++ - os: macos-13 + compiler: clang + env: + idx: 11 +@@ -338,6 +350,7 @@ jobs: + NO_CACHING: NO + ALLOW_WARNINGS: YES + MACOSX_PACKAGE: 1 ++ RUN_UNITTEST: 1 + + + steps: +@@ -350,12 +363,38 @@ jobs: + sudo apt-get install --no-install-recommends -y clang + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 ++ ++ - name: Install OpenSSL 1.0 on modern MacOS ++ # Needed for recent versions of MacOS as they ship with OpenSSL 1.1 by default ++ if: matrix.os == 'macos-13' && matrix.env.OPENSSL_1_0 == 'YES' ++ run: | ++ curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz ++ tar -xzf openssl-1.0.2u.tar.gz ++ cd openssl-1.0.2u ++ ./Configure --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared shared darwin64-x86_64-cc ++ #make depend ++ make -j $(nproc) ++ sudo make install_sw -j $(nproc) ++ ++ OPENSSL_ROOT_DIR=/usr/local/ssl ++ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib ++ CFLAGS=-I${OPENSSL_ROOT_DIR}/include ++ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" ++ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig ++ ++ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV ++ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV ++ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH ++ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV ++ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ echo "DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV + +- - name: Set up OpenSSL 1.1 on MacOS ++ - name: Set up OpenSSL 1.1 on modern MacOS ++ # OpenSSL 1.1 is installed by default, so we just need to set the paths + if: matrix.os == 'macos-latest' && matrix.env.OPENSSL_1_1 == 'YES' + run: | + OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) +- LDFLAGS=-L{$OPENSSL_ROOT_DIR}/lib ++ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" + PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig +@@ -365,6 +404,44 @@ jobs: + echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH + echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ ++ - name: Install OpenSSL 1.0 on modern Linux ++ # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default ++ if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_0 == 'YES' ++ run: | ++ curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz ++ tar -xzf openssl-1.0.2u.tar.gz ++ cd openssl-1.0.2u ++ ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared ++ make depend ++ make -j $(nproc) ++ sudo make install_sw -j $(nproc) ++ sudo ldconfig ++ ++ OPENSSL_ROOT_DIR=/usr/local/ssl ++ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib ++ CFLAGS=-I${OPENSSL_ROOT_DIR}/include ++ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" ++ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig ++ ++ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV ++ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV ++ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH ++ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV ++ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ echo "LD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV ++ ++ - name: Install OpenSSL 1.1 on modern Linux ++ # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default ++ if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_1 == 'YES' ++ run: | ++ curl -O -L https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz ++ tar -xzf openssl-1.1.1w.tar.gz ++ cd openssl-1.1.1w ++ ./config ++ make -j $(nproc) ++ sudo make install_sw -j $(nproc) ++ sudo ldconfig + + - name: Print tool version information + run: | +@@ -375,7 +452,7 @@ jobs: + + - name: Run CMake + run: | +- cmake -S . -B CMakeFiles\ ++ cmake -S . -B output\ + -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ + -DBUILD_SHARED_LIBS=${{ matrix.env.BUILD_SHARED }}\ + -DCIVETWEB_THIRD_PARTY_DIR=../src/third-party\ +@@ -403,12 +480,29 @@ jobs: + - name: Build MacOS Package + if: matrix.env.MACOSX_PACKAGE == 1 + run: | +- make -f Makefile.osx package ++ make -f Makefile.osx package -j $(nproc) + + - name: Build executable + run: | +- cmake --build CMakeFiles -- -j $(nproc) ++ cmake --build output -- -j $(nproc) + + - name: Check executable + run: | +- ./CMakeFiles/src/civetweb -I +\ No newline at end of file ++ ./output/src/civetweb -I ++ ++ - name: Run unit tests ++ if: matrix.env.RUN_UNITTEST == 1 ++ run: | ++ # kill processes that are using port 8084, which is used in the unit tests ++ # Currently, this affects linux only, where 'mono' is using this port ++ pid_8084=$(sudo lsof -t -i:8084 || true;) ++ ++ if [[ -n ${pid_8084} ]]; then ++ echo "Killing process using port 8084: ${pid_8084}" ++ sudo kill -9 ${pid_8084} ++ fi ++ ++ # Run unit tests ++ gcc unittest/cgi_test.c -o output/cgi_test.cgi ++ cd output ++ CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test -j $(nproc) +\ No newline at end of file +diff --git a/.gitignore b/.gitignore +index 8a3ae869..e5194fb8 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -27,6 +27,7 @@ log.out + /CMakeCache.txt + /CMakeFiles + /mingw-builds ++/output + + ################# + ## Eclipse +-- +2.34.1 + + +From 2775a1b860590a4a00f847073238371c718dcf9c Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Fri, 23 Aug 2024 12:57:42 +0200 +Subject: [PATCH 096/173] Revert + https://github.com/civetweb/civetweb/commit/19b70adc70ca66cc862f3fd065e354af64b7b011 + +Signed-off-by: yubiuser +--- + unittest/public_server.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 6e0f71c7..93641f0d 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -1316,7 +1316,7 @@ START_TEST(test_request_handlers) + char cmd_buf[1024]; + char *cgi_env_opt; + +- const char *server_host = "localhost"; //"test.domain"; ++ const char *server_host = "test.domain"; + + mark_point(); + +-- +2.34.1 + + +From 8d4bcef5469073b2446b2d56c17930a206597acb Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Fri, 23 Aug 2024 13:25:02 +0200 +Subject: [PATCH 097/173] Keep only FreeBSD on Travis + +Signed-off-by: yubiuser +--- + .travis.yml | 519 +--------------------------------------------------- + 1 file changed, 1 insertion(+), 518 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index 077fbd89..3d8e516b 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -9,60 +9,17 @@ cache: + directories: + - $HOME/third-party + +-osx_image: xcode9 +- +-addons: +- apt: +- packages: +- - cmake +- - openssl +- - libssl-dev +- - gdb +- sources: +- - kubuntu-backports +- +- + before_install: +- - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then +- mkdir $HOME/usr; +- export PATH="$HOME/usr/bin:$PATH"; +- wget https://cmake.org/files/v3.7/cmake-3.7.2-Linux-x86_64.sh --no-check-certificate; +- chmod +x cmake-3.7.2-Linux-x86_64.sh; +- ./cmake-3.7.2-Linux-x86_64.sh --prefix=$HOME/usr --exclude-subdir --skip-license; +- fi + - cmake --version + +- + install: +- - if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then +- PATH=~/.local/bin:${PATH}; +- pip install --user --upgrade pip; +- pip install --user cpp-coveralls; +- pip install --user codecov; +- pip install --user coverage; +- fi + + before_script: +- # Add an IPv6 config - see the corresponding Travis issue +- # https://github.com/travis-ci/travis-ci/issues/8361 +- - if [ "${ENABLE_IPV6}" == "YES" -a "${TRAVIS_OS_NAME}" == "linux" ]; then +- echo "Activating IPv6 on Travis"; +- sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; +- fi + # Check some settings of the build server (operating system, IPv6 availability, directory) + - uname -a +- - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then +- lsb_release -a; +- cat /etc/network/interfaces || true; +- fi + - ifconfig + - pwd + - ls -la +- - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then +- apt-cache search gcc | grep "GNU C compiler"; +- apt-cache search clang | grep compiler; +- fi +- - if [[ "${BUILD_TYPE}" == "OSX_OPENSSL_1_1" ]]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install openssl@1.1 ;fi + # Generate the build scripts with CMake + - mkdir output + - openssl version +@@ -130,16 +87,7 @@ after_failure: + - if [[ -f "$COREFILE" ]]; then gdb -c "$COREFILE" example -ex "thread apply all bt" -ex "set pagination 0" -batch; fi + + +-# Modifications due to Travis IPv6 issues: +-# https://github.com/travis-ci/travis-ci/issues/8711 +-# https://github.com/travis-ci/travis-ci/issues/8361 +-# DCIVETWEB_ENABLE_IPV6=${ENABLE_IPV6} or =NO +- + script: +- - if [ "${MACOSX_PACKAGE}" == "1" ]; then +- cd "${TRAVIS_BUILD_DIR}"; +- make -f Makefile.osx package; +- fi + - if [ "${RUN_UNITTEST}" == "1" ]; then + CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test; + fi +@@ -152,16 +100,7 @@ script: + fi + - echo "Build and test script DONE" + +-# Coveralls options: https://github.com/eddyxu/cpp-coveralls/blob/master/README.md + after_success: +- - if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then +- echo "Preparing coverage tests"; +- echo "Creating coveralls coverage report"; +- coveralls --include src --exclude src/main.c --exclude src/third_party --include include --gcov-options '\-lp' --root .. --build-root .; +- echo "Creating codecov coverage report"; +- bash <(curl -s https://codecov.io/bash); +- echo "All coverage reports created"; +- fi + + + ######################################################################################### +@@ -173,350 +112,6 @@ after_success: + matrix: + fast_finish: true + include: +- +- +-######################################################################################### +-##### TRUSTY ######################################################################## +-######################################################################################### +- +- - dist: trusty +- sudo: false +- os: linux +- compiler: clang +- addons: +- apt: +- sources: +- - ubuntu-toolchain-r-test +- - llvm-toolchain-precise-3.8 +- packages: +- - clang-3.8 +- env: +- idx=1 +- N=Clang3.8-Linux-Minimal-Debug +- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" +- BUILD_TYPE=Debug +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=NO +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=YES +- ENABLE_SSL=NO +- NO_CGI=YES +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_SERVER_STATS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +- - dist: trusty +- sudo: false +- os: linux +- compiler: clang +- addons: +- apt: +- sources: +- - ubuntu-toolchain-r-test +- - llvm-toolchain-precise-3.8 +- packages: +- - clang-3.8 +- env: +- idx=3 +- N=Clang3.8-Linux-Default-Release +- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" +- BUILD_TYPE=Release +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_SERVER_STATS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +- - dist: trusty +- sudo: required +- os: linux +- compiler: gcc +- addons: +- apt: +- sources: +- - ubuntu-toolchain-r-test +- packages: +- - g++-5 +- env: +- idx=5 +- N=GCC5-Linux-Complete-NoLua-Release +- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" +- BUILD_TYPE=Release +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=YES +- ENABLE_WEBSOCKETS=YES +- ENABLE_SERVER_STATS=YES +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=YES +- ALLOW_WARNINGS=YES +- RUN_UNITTEST=1 +- +- +-######################################################################################### +-##### COVERAGE ###################################################################### +-######################################################################################### +- +- - os: linux +- sudo: required +- compiler: clang +- env: +- idx=6 +- N=GCCAnyVersion-Linux-Coverage +- BUILD_TYPE=Coverage +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=YES +- ENABLE_WEBSOCKETS=YES +- ENABLE_SERVER_STATS=YES +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- RUN_UNITTEST=1 +- +-######################################################################################### +-##### SHARED ######################################################################## +-######################################################################################### +- +- - sudo: false +- os: linux +- compiler: clang +- env: +- idx=9 +- N=Clang-Linux-Default-Shared +- BUILD_TYPE=Debug +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=NO +- OPENSSL_1_1=YES +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=YES +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_SERVER_STATS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +- +-######################################################################################### +-##### BUILD TYPES ################################################################### +-######################################################################################### +- +-# According to CMakeLists, options are: +-# None Debug Release RelWithDebInfo MinSizeRel Coverage +- +- - +- os: linux +- compiler: gcc +- env: +- idx=15 +- N=GCCLinuxDefault_RelWithDebInfo +- BUILD_TYPE=RelWithDebInfo +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +- - +- os: linux +- compiler: gcc +- env: +- idx=16 +- N=GCCLinuxDefault_MinSizeRel +- BUILD_TYPE=MinSizeRel +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +- - +- os: linux +- compiler: gcc +- env: +- idx=17 +- N=GCCLinuxDefault_None +- BUILD_TYPE=None +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +-######################################################################################### +-##### XENIAL, BIONIC, FOCAL ######################################################### +-######################################################################################### +- +- - +- os: linux +- compiler: gcc +- dist: xenial +- env: +- idx=20 +- N=GCCLinuxDefault_xenial +- BUILD_TYPE=Release +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +- - +- os: linux +- compiler: gcc +- dist: bionic +- env: +- idx=21 +- N=GCCLinuxDefault_bionic +- BUILD_TYPE=Release +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=NO +- OPENSSL_1_1=YES +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- +- - +- os: linux +- compiler: gcc +- dist: focal +- addons: +- apt: +- packages: +- - lsb-core +- env: +- idx=23 +- N=GCCLinuxDefault_focal +- BUILD_TYPE=Release +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=NO +- OPENSSL_1_1=YES +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=NO +- ENABLE_WEBSOCKETS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- RUN_UNITTEST=1 +- +- + ######################################################################################### + ##### FREEBSD BUILD ###########=##################################################### + ######################################################################################### +@@ -547,116 +142,4 @@ matrix: + ENABLE_DUKTAPE=NO + NO_CACHING=NO + ALLOW_WARNINGS=YES +- RUN_UNITTEST=1 +- +- +-######################################################################################### +-##### OSX BUILD ##################################################################### +-######################################################################################### +- +- - +- os: osx +- sudo: required +- compiler: clang +- env: +- idx=8 +- N=Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad +- BUILD_TYPE=Release +- ENABLE_SSL_DYNAMIC_LOADING=NO +- OPENSSL_1_0=NO +- OPENSSL_1_1=YES +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=YES +- ENABLE_WEBSOCKETS=YES +- ENABLE_SERVER_STATS=YES +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=YES +- ALLOW_WARNINGS=YES +- OPENSSL_ROOT_DIR="/usr/local/opt/openssl@1.1" +- LDFLAGS="-L${OPENSSL_ROOT_DIR}/lib" +- CFLAGS="-I${OPENSSL_ROOT_DIR}/include" +- ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DCMAKE_C_FLAGS=${CFLAGS}" +- PATH="${OPENSSL_ROOT_DIR}/bin:$PATH" +- DYLD_LIBRARY_PATH="${OPENSSL_ROOT_DIR}/lib:${DYLD_LIBRARY_PATH}" +- RUN_UNITTEST=1 +- +- - +- os: osx +- sudo: required +- compiler: clang +- env: +- idx=11 +- N=OSX-Package +- BUILD_TYPE=Release +- ENABLE_SSL_DYNAMIC_LOADING=YES +- OPENSSL_1_0=YES +- OPENSSL_1_1=NO +- ENABLE_CXX=NO +- ENABLE_LUA_SHARED=NO +- C_STANDARD=auto +- CXX_STANDARD=auto +- BUILD_SHARED=NO +- NO_FILES=NO +- ENABLE_SSL=YES +- NO_CGI=NO +- ENABLE_IPV6=YES +- ENABLE_WEBSOCKETS=YES +- ENABLE_SERVER_STATS=NO +- ENABLE_LUA=NO +- ENABLE_DUKTAPE=NO +- NO_CACHING=NO +- ALLOW_WARNINGS=YES +- MACOSX_PACKAGE=1 +- +-######################################################################################### +-######################################################################################### +-##### END OF BUILD MATRIX ########################################################### +-######################################################################################### +-######################################################################################### +- +-# Remove Lua build, until someone knows how to fix the CMake files +-# +-# - dist: trusty +-# sudo: required +-# os: linux +-# compiler: clang +-# addons: +-# apt: +-# sources: +-# - ubuntu-toolchain-r-test +-# - llvm-toolchain-precise-3.8 +-# packages: +-# - clang-3.8 +-# - lua5.2 +-# env: +-# idx=99 +-# N=Clang3.8-Linux-Complete-WithLua-Debug +-# MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" +-# BUILD_TYPE=Debug +-# ENABLE_SSL_DYNAMIC_LOADING=YES +-# OPENSSL_1_0=NO +-# OPENSSL_1_1=YES +-# ENABLE_CXX=NO +-# ENABLE_LUA_SHARED=YES +-# C_STANDARD=auto +-# CXX_STANDARD=auto +-# BUILD_SHARED=NO +-# NO_FILES=NO +-# ENABLE_SSL=YES +-# NO_CGI=NO +-# ENABLE_IPV6=YES +-# ENABLE_WEBSOCKETS=YES +-# ENABLE_SERVER_STATS=YES +-# ENABLE_LUA=YES +-# ENABLE_LUA_SHARED=YES +-# ENABLE_DUKTAPE=NO +-# NO_CACHING=YES +-# ALLOW_WARNINGS=YES ++ RUN_UNITTEST=1 +\ No newline at end of file +-- +2.34.1 + + +From 6ab2ee088e6973d935071393d09cfd4b069b1ab5 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Fri, 23 Aug 2024 13:40:49 +0200 +Subject: [PATCH 098/173] Use wildcard matching + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 36 ++++++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 11 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 7fe95108..9e905e87 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -358,7 +358,7 @@ jobs: + uses: actions/checkout@v4.1.7 + + - name: Install clang on Linux +- if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' ++ if: matrix.compiler == 'clang' && startsWith(matrix.os,'ubuntu') + run: | + sudo apt-get install --no-install-recommends -y clang + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 +@@ -366,17 +366,17 @@ jobs: + + - name: Install OpenSSL 1.0 on modern MacOS + # Needed for recent versions of MacOS as they ship with OpenSSL 1.1 by default +- if: matrix.os == 'macos-13' && matrix.env.OPENSSL_1_0 == 'YES' ++ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_0 == 'YES' + run: | + curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz + tar -xzf openssl-1.0.2u.tar.gz + cd openssl-1.0.2u +- ./Configure --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared shared darwin64-x86_64-cc +- #make depend ++ ./Configure --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared shared darwin64-x86_64-cc ++ make depend + make -j $(nproc) + sudo make install_sw -j $(nproc) + +- OPENSSL_ROOT_DIR=/usr/local/ssl ++ OPENSSL_ROOT_DIR=/usr/local/ssl1.0 + LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" +@@ -391,7 +391,7 @@ jobs: + + - name: Set up OpenSSL 1.1 on modern MacOS + # OpenSSL 1.1 is installed by default, so we just need to set the paths +- if: matrix.os == 'macos-latest' && matrix.env.OPENSSL_1_1 == 'YES' ++ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_1 == 'YES' + run: | + OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) + LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib +@@ -407,18 +407,18 @@ jobs: + + - name: Install OpenSSL 1.0 on modern Linux + # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default +- if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_0 == 'YES' ++ if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_0 == 'YES' + run: | + curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz + tar -xzf openssl-1.0.2u.tar.gz + cd openssl-1.0.2u +- ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared ++ ./config --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared + make depend + make -j $(nproc) + sudo make install_sw -j $(nproc) + sudo ldconfig + +- OPENSSL_ROOT_DIR=/usr/local/ssl ++ OPENSSL_ROOT_DIR=/usr/local/ssl1.0 + LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" +@@ -433,15 +433,29 @@ jobs: + + - name: Install OpenSSL 1.1 on modern Linux + # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default +- if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_1 == 'YES' ++ if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_1 == 'YES' + run: | + curl -O -L https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz + tar -xzf openssl-1.1.1w.tar.gz + cd openssl-1.1.1w +- ./config ++ ./config --prefix=/usr/local/ssl1.1 --openssldir=/usr/local/ssl1.1 shared ++ make depend + make -j $(nproc) + sudo make install_sw -j $(nproc) + sudo ldconfig ++ ++ OPENSSL_ROOT_DIR=/usr/local/ssl1.1 ++ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib ++ CFLAGS=-I${OPENSSL_ROOT_DIR}/include ++ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" ++ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig ++ ++ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV ++ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV ++ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH ++ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV ++ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ echo "LD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV + + - name: Print tool version information + run: | +-- +2.34.1 + + +From 13b54b2fcfd8fee34a60c4b9dceae25da46c481d Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sun, 15 Sep 2024 07:38:44 +0000 +Subject: [PATCH 099/173] Bump github/codeql-action from 1 to 3 + +Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 3. +- [Release notes](https://github.com/github/codeql-action/releases) +- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) +- [Commits](https://github.com/github/codeql-action/compare/v1...v3) + +--- +updated-dependencies: +- dependency-name: github/codeql-action + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/codeql-analysis.yml | 6 +++--- + .github/workflows/codeql.yml | 6 +++--- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml +index 1268ef38..58b52c82 100644 +--- a/.github/workflows/codeql-analysis.yml ++++ b/.github/workflows/codeql-analysis.yml +@@ -38,7 +38,7 @@ jobs: + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL +- uses: github/codeql-action/init@v1 ++ uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. +@@ -49,7 +49,7 @@ jobs: + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild +- uses: github/codeql-action/autobuild@v1 ++ uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl +@@ -63,4 +63,4 @@ jobs: + # make release + + - name: Perform CodeQL Analysis +- uses: github/codeql-action/analyze@v1 ++ uses: github/codeql-action/analyze@v3 +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 81649227..efa1f8ca 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -51,7 +51,7 @@ jobs: + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL +- uses: github/codeql-action/init@v2 ++ uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. +@@ -78,7 +78,7 @@ jobs: + ./.github/workflows/codeql-buildscript.sh + + - name: Perform CodeQL Analysis +- uses: github/codeql-action/analyze@v2 ++ uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" + upload: false +@@ -107,7 +107,7 @@ jobs: + output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + + - name: Upload CodeQL results to code scanning +- uses: github/codeql-action/upload-sarif@v2 ++ uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.step1.outputs.sarif-output }} + category: "/language:${{matrix.language}}" +-- +2.34.1 + + +From 0c237b78c35061416c9efd09e176c8f23986ff98 Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sun, 15 Sep 2024 07:38:46 +0000 +Subject: [PATCH 100/173] Bump actions/upload-artifact from 1 to 4 + +Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 1 to 4. +- [Release notes](https://github.com/actions/upload-artifact/releases) +- [Commits](https://github.com/actions/upload-artifact/compare/v1...v4) + +--- +updated-dependencies: +- dependency-name: actions/upload-artifact + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/cifuzz.yml | 2 +- + .github/workflows/codeql.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml +index afa5846d..d62ff855 100644 +--- a/.github/workflows/cifuzz.yml ++++ b/.github/workflows/cifuzz.yml +@@ -18,7 +18,7 @@ jobs: + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash +- uses: actions/upload-artifact@v1 ++ uses: actions/upload-artifact@v4 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 81649227..d2a505a3 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -114,7 +114,7 @@ jobs: + + - name: Upload CodeQL results as an artifact + if: success() || failure() +- uses: actions/upload-artifact@v3 ++ uses: actions/upload-artifact@v4 + with: + name: codeql-results + path: ${{ steps.step1.outputs.sarif-output }} +-- +2.34.1 + + +From aeb03a812e96562e42c688ac6d2919537439ba38 Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sun, 15 Sep 2024 07:38:48 +0000 +Subject: [PATCH 101/173] Bump actions/checkout from 2 to 4 + +Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. +- [Release notes](https://github.com/actions/checkout/releases) +- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) +- [Commits](https://github.com/actions/checkout/compare/v2...v4) + +--- +updated-dependencies: +- dependency-name: actions/checkout + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/codeql-analysis.yml | 2 +- + .github/workflows/codeql.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml +index 1268ef38..d1640226 100644 +--- a/.github/workflows/codeql-analysis.yml ++++ b/.github/workflows/codeql-analysis.yml +@@ -25,7 +25,7 @@ jobs: + + steps: + - name: Checkout repository +- uses: actions/checkout@v2 ++ uses: actions/checkout@v4 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 81649227..ec64d43b 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -45,7 +45,7 @@ jobs: + + steps: + - name: Checkout repository +- uses: actions/checkout@v3 ++ uses: actions/checkout@v4 + with: + submodules: recursive + +-- +2.34.1 + + +From af3825fcd33c483501386432c1c51d677077e186 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sun, 15 Sep 2024 12:29:52 +0200 +Subject: [PATCH 102/173] Remove duplicated codeql workflow + +Signed-off-by: yubiuser +--- + .github/workflows/codeql-analysis.yml | 66 ------------------------- + .github/workflows/codeql-buildscript.sh | 3 -- + .github/workflows/codeql.yml | 8 +-- + 3 files changed, 4 insertions(+), 73 deletions(-) + delete mode 100644 .github/workflows/codeql-analysis.yml + delete mode 100644 .github/workflows/codeql-buildscript.sh + +diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml +deleted file mode 100644 +index 6f033055..00000000 +--- a/.github/workflows/codeql-analysis.yml ++++ /dev/null +@@ -1,66 +0,0 @@ +-name: "CodeQL" +- +-on: +- push: +- branches: [master] +- pull_request: +- # The branches below must be a subset of the branches above +- branches: [master] +- schedule: +- - cron: '0 19 * * 4' +- +-jobs: +- analyze: +- name: Analyze +- runs-on: ubuntu-latest +- +- strategy: +- fail-fast: false +- matrix: +- # Override automatic language detection by changing the below list +- # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] +- language: ['cpp'] +- # Learn more... +- # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection +- +- steps: +- - name: Checkout repository +- uses: actions/checkout@v4 +- with: +- # We must fetch at least the immediate parents so that if this is +- # a pull request then we can checkout the head. +- fetch-depth: 2 +- +- # If this run was triggered by a pull request event, then checkout +- # the head of the pull request instead of the merge commit. +- - run: git checkout HEAD^2 +- if: ${{ github.event_name == 'pull_request' }} +- +- # Initializes the CodeQL tools for scanning. +- - name: Initialize CodeQL +- uses: github/codeql-action/init@v3 +- with: +- languages: ${{ matrix.language }} +- # If you wish to specify custom queries, you can do so here or in a config file. +- # By default, queries listed here will override any specified in a config file. +- # Prefix the list here with "+" to use these queries and those in the config file. +- # queries: ./path/to/local/query, your-org/your-repo/queries@main +- +- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). +- # If this step fails, then you should remove it and run the build manually (see below) +- - name: Autobuild +- uses: github/codeql-action/autobuild@v3 +- +- # ℹ️ Command-line programs to run using the OS shell. +- # 📚 https://git.io/JvXDl +- +- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines +- # and modify them (or add more) to build your code if your project +- # uses a compiled language +- +- #- run: | +- # make bootstrap +- # make release +- +- - name: Perform CodeQL Analysis +- uses: github/codeql-action/analyze@v3 +diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh +deleted file mode 100644 +index 366db622..00000000 +--- a/.github/workflows/codeql-buildscript.sh ++++ /dev/null +@@ -1,3 +0,0 @@ +-#!/usr/bin/env bash +- +-make build WITH_ALL=1 +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 51632e65..d9f751b4 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -12,8 +12,8 @@ + name: "CodeQL" + + on: +- # push: +- # branches: [ "main", "master" ] ++ push: ++ branches: [ "master" ] + schedule: + - cron: '0 0 * * *' + pull_request: +@@ -27,7 +27,7 @@ jobs: + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. +- runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }} ++ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read +@@ -75,7 +75,7 @@ jobs: + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + - run: | +- ./.github/workflows/codeql-buildscript.sh ++ make build WITH_ALL=1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 +-- +2.34.1 + + +From e0cd57edd1a7b8ce1d0fd6d9a6457c86e1b266e3 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Fri, 20 Sep 2024 19:00:58 +0200 +Subject: [PATCH 103/173] Fix LUA error reporting + +Signed-off-by: DL6ER +--- + src/mod_lua.inl | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index ec5e1aeb..7bacf6f3 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -673,7 +673,9 @@ run_lsp_kepler(struct mg_connection *conn, + + } else { + /* Success loading chunk. Call it. */ +- lua_pcall(L, 0, 0, 1); ++ lua_ok = lua_pcall(L, 0, 0, 0); ++ if(lua_ok != LUA_OK) ++ lua_cry(conn, lua_ok, L, "LSP", "call"); + } + return 0; + } +@@ -789,7 +791,9 @@ run_lsp_civetweb(struct mg_connection *conn, + lua_pcall(L, 1, 0, 0); + } else { + /* Success loading chunk. Call it. */ +- lua_pcall(L, 0, 0, 1); ++ lua_ok = lua_pcall(L, 0, 0, 0); ++ if(lua_ok != LUA_OK) ++ lua_cry(conn, lua_ok, L, "LSP", "call"); + } + + /* Progress until after the Lua closing tag. */ +-- +2.34.1 + + +From 53e305b43a7256cbee6a91bcfa3ba6e4ddb15db2 Mon Sep 17 00:00:00 2001 +From: Kacper Stasik +Date: Sun, 22 Sep 2024 22:43:57 +0200 +Subject: [PATCH 104/173] fix: missing host name for ssl + +--- + src/civetweb.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index dced1362..6d8a0c1a 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -19532,6 +19532,9 @@ mg_connect_websocket_client(const char *host, + memset(&client_options, 0, sizeof(client_options)); + client_options.host = host; + client_options.port = port; ++ if (use_ssl) { ++ client_options.host_name = host; ++ } + + return mg_connect_websocket_client_impl(&client_options, + use_ssl, +-- +2.34.1 + + +From b02d56d6d03996446071c7de0fc1c71473a504d3 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Mon, 23 Sep 2024 13:13:35 +0200 +Subject: [PATCH 105/173] Abort early on Lua errors to avoid sending half-done + pages to the user and call the lua_error_handler to get the error to the user + as well as to the log file + +Signed-off-by: DL6ER + +Handle stack properly and add static prototype in/for lua_error_handler() + +Signed-off-by: DL6ER +--- + src/mod_lua.inl | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index 7bacf6f3..df4b5bfe 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -10,6 +10,9 @@ + #include "civetweb_lua.h" + #include "civetweb_private_lua.h" + ++/* Prototypes */ ++static int ++lua_error_handler(lua_State *L); + + #if defined(_WIN32) + static void * +@@ -669,13 +672,19 @@ run_lsp_kepler(struct mg_connection *conn, + /* Syntax error or OOM. + * Error message is pushed on stack. */ + lua_pcall(L, 1, 0, 0); +- lua_cry(conn, lua_ok, L, "LSP", "execute"); /* XXX TODO: everywhere ! */ ++ lua_cry(conn, lua_ok, L, "LSP Kepler", "execute"); ++ lua_error_handler(L); ++ return 1; + + } else { + /* Success loading chunk. Call it. */ + lua_ok = lua_pcall(L, 0, 0, 0); + if(lua_ok != LUA_OK) +- lua_cry(conn, lua_ok, L, "LSP", "call"); ++ { ++ lua_cry(conn, lua_ok, L, "LSP Kepler", "call"); ++ lua_error_handler(L); ++ return 1; ++ } + } + return 0; + } +@@ -789,11 +798,18 @@ run_lsp_civetweb(struct mg_connection *conn, + /* Syntax error or OOM. + * Error message is pushed on stack. */ + lua_pcall(L, 1, 0, 0); ++ lua_cry(conn, lua_ok, L, "LSP", "execute"); ++ lua_error_handler(L); ++ return 1; + } else { + /* Success loading chunk. Call it. */ + lua_ok = lua_pcall(L, 0, 0, 0); + if(lua_ok != LUA_OK) ++ { + lua_cry(conn, lua_ok, L, "LSP", "call"); ++ lua_error_handler(L); ++ return 1; ++ } + } + + /* Progress until after the Lua closing tag. */ +@@ -2786,12 +2802,13 @@ lua_error_handler(lua_State *L) + lua_call(L, 2, 0); + IGNORE_UNUSED_RESULT( + luaL_dostring(L, "mg.write(debug.traceback(), '\\n')")); ++ lua_pop(L, 1); /* pop mg */ + } else { + printf("Lua error: [%s]\n", error_msg); + IGNORE_UNUSED_RESULT( + luaL_dostring(L, "print(debug.traceback(), '\\n')")); + } +- /* TODO(lsm, low): leave the stack balanced */ ++ lua_pop(L, 1); /* pop error message */ + + return 0; + } +-- +2.34.1 + + +From 0f6a77d46012c5f2b22378883eeead5d3851799e Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Mon, 23 Sep 2024 20:35:54 +0200 +Subject: [PATCH 106/173] Properly generate traceback in lua_error_handler() + avoiding the first line showing the manual call to debug.traceback() itself + +Signed-off-by: DL6ER +--- + src/mod_lua.inl | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index df4b5bfe..e97a28cc 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -2796,13 +2796,33 @@ lua_error_handler(lua_State *L) + + lua_getglobal(L, "mg"); + if (!lua_isnil(L, -1)) { +- lua_getfield(L, -1, "write"); /* call mg.write() */ ++ /* Write the error message to the error log */ ++ lua_getfield(L, -1, "write"); + lua_pushstring(L, error_msg); + lua_pushliteral(L, "\n"); +- lua_call(L, 2, 0); +- IGNORE_UNUSED_RESULT( +- luaL_dostring(L, "mg.write(debug.traceback(), '\\n')")); ++ lua_call(L, 2, 0); /* call mg.write(error_msg + \n) */ + lua_pop(L, 1); /* pop mg */ ++ ++ /* Get Lua traceback */ ++ lua_getglobal(L, "debug"); ++ lua_getfield(L, -1, "traceback"); ++ lua_call(L, 0, 1); /* call debug.traceback() */ ++ lua_remove(L, -2); /* remove debug */ ++ ++ /* Write the Lua traceback to the error log */ ++ lua_getglobal(L, "mg"); ++ lua_getfield(L, -1, "write"); ++ lua_pushvalue(L, -3); /* push the traceback */ ++ ++ /* Only print the traceback if it is not empty */ ++ if (strcmp(lua_tostring(L, -1), "stack traceback:") != 0) { ++ lua_pushliteral(L, "\n"); /* append a newline */ ++ lua_call(L, 2, 0); /* call mg.write(traceback + \n) */ ++ lua_pop(L, 2); /* pop mg and traceback */ ++ } else { ++ lua_pop(L, 3); /* pop mg, traceback and error message */ ++ } ++ + } else { + printf("Lua error: [%s]\n", error_msg); + IGNORE_UNUSED_RESULT( +-- +2.34.1 + + +From aa7de369a47903de2417e2a8bf5be1f3df2acd10 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Mon, 23 Sep 2024 21:27:36 +0200 +Subject: [PATCH 107/173] Add linux and macOS test using OpenSSL3.0 + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 85 +++++++++++++++++++++++++++++------ + unittest/public_server.c | 2 +- + 2 files changed, 72 insertions(+), 15 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 9e905e87..815a9d84 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -16,7 +16,6 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- idx: 1 + N: Clang-Linux-Minimal-Debug + BUILD_TYPE: Debug + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -42,7 +41,6 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- idx: 3 + N: Clang-Linux-Default-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -68,7 +66,6 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- idx: 5 + N: GCC-Linux-Complete-NoLua-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -94,7 +91,6 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- idx: 6 + N: CLANG-AnyVersion-Linux-Coverage + BUILD_TYPE: Coverage + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -120,7 +116,6 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- idx: 9 + N: Clang-Linux-Default-Shared + BUILD_TYPE: Debug + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -146,7 +141,6 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- idx: 15 + N: GCCLinuxDefault_RelWithDebInfo + BUILD_TYPE: RelWithDebInfo + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -171,7 +165,6 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- idx: 16 + N: GCCLinuxDefault_MinSizeRel + BUILD_TYPE: MinSizeRel + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -196,7 +189,6 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- idx: 17 + N: GCCLinuxDefault_None + BUILD_TYPE: None + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -221,7 +213,6 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- idx: 20 + N: GCCLinuxDefault_xenial + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -246,7 +237,6 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- idx: 23 + N: GCCLinuxDefault_focal + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -267,13 +257,37 @@ jobs: + NO_CACHING: NO + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ N: GCCLinuxDefault_OpenSSL_3_0 ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: NO ++ OpenSSL_3_0: YES ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + + # Disable Lua build, until someone knows how to fix the CMake files + # see https://github.com/civetweb/civetweb/issues/543 + # - os: ubuntu-lastest + # compiler: clang + # env: +- # idx: 99 + # N: Clang-Linux-Complete-WithLua-Debug + # BUILD_TYPE: Debug + # ENABLE_SSL_DYNAMIC_LOADING: YES +@@ -299,7 +313,6 @@ jobs: + - os: macos-latest + compiler: clang + env: +- idx: 8 + N: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: NO +@@ -328,8 +341,7 @@ jobs: + - os: macos-13 + compiler: clang + env: +- idx: 11 +- N: OSX-Package ++ N: OSX-Package_OpenSSL_1_0 + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: YES +@@ -352,6 +364,33 @@ jobs: + MACOSX_PACKAGE: 1 + RUN_UNITTEST: 1 + ++ - os: macos-latest ++ compiler: clang ++ env: ++ N: OSX-Package_OpenSSL_3_0 ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: NO ++ OPENSSL_3_0: YES ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: YES ++ ENABLE_WEBSOCKETS: YES ++ ENABLE_SERVER_STATS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ MACOSX_PACKAGE: 1 ++ RUN_UNITTEST: 1 ++ + + steps: + - name: Checkout code +@@ -404,7 +443,25 @@ jobs: + echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH + echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ ++ - name: Install OpenSSL 3.0 on modern MacOS ++ # OpenSSL 1.1 is installed by default, so we need to install 3.0 manually ++ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_3_0 == 'YES' ++ run: | ++ brew install openssl@3.0 + ++ OPENSSL_ROOT_DIR=$(brew --prefix openssl@3.0) ++ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib ++ CFLAGS=-I${OPENSSL_ROOT_DIR}/include ++ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" ++ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig ++ ++ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV ++ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV ++ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH ++ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV ++ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ + - name: Install OpenSSL 1.0 on modern Linux + # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default + if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_0 == 'YES' +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 93641f0d..4983ee8c 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -823,7 +823,7 @@ START_TEST(test_mg_server_and_client_tls) + * while Ubuntu Xenial, Ubuntu Trusty and Windows test containers at + * Travis CI do not. Maybe it is OpenSSL version specific. + */ +-#if defined(OPENSSL_API_1_1) ++#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) + if (client_conn) { + /* Connect succeeds, but the connection is unusable. */ + mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n"); +-- +2.34.1 + + +From 69265e7a0d0655a24f9b3e8e301d1c6e72cd51c4 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Mon, 23 Sep 2024 22:16:01 +0200 +Subject: [PATCH 108/173] Remove test for OpenSSL 1.0 + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 100 +++++++++------------------------- + 1 file changed, 25 insertions(+), 75 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 815a9d84..60f5089f 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -44,8 +44,8 @@ jobs: + N: Clang-Linux-Default-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -69,8 +69,8 @@ jobs: + N: GCC-Linux-Complete-NoLua-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -94,8 +94,8 @@ jobs: + N: CLANG-AnyVersion-Linux-Coverage + BUILD_TYPE: Coverage + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -144,8 +144,8 @@ jobs: + N: GCCLinuxDefault_RelWithDebInfo + BUILD_TYPE: RelWithDebInfo + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -168,8 +168,8 @@ jobs: + N: GCCLinuxDefault_MinSizeRel + BUILD_TYPE: MinSizeRel + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -192,8 +192,8 @@ jobs: + N: GCCLinuxDefault_None + BUILD_TYPE: None + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -216,8 +216,8 @@ jobs: + N: GCCLinuxDefault_xenial + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -237,7 +237,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCCLinuxDefault_focal ++ N: GCCLinuxDefault + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -335,17 +335,14 @@ jobs: + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 + +- # mac-os 13 is the last version of MacOS runner using x86_64 architecture +- # mac-os 14 and later are using arm64 architecture +- # but OpenSSL 1.0 can't compile on arm64, so we set it fixed to mac-os 13 +- - os: macos-13 ++ - os: macos-latest + compiler: clang + env: +- N: OSX-Package_OpenSSL_1_0 ++ N: OSX-Package_OpenSSL_1_1 + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES +- OPENSSL_1_0: YES +- OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto +@@ -403,31 +400,6 @@ jobs: + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 + +- - name: Install OpenSSL 1.0 on modern MacOS +- # Needed for recent versions of MacOS as they ship with OpenSSL 1.1 by default +- if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_0 == 'YES' +- run: | +- curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz +- tar -xzf openssl-1.0.2u.tar.gz +- cd openssl-1.0.2u +- ./Configure --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared shared darwin64-x86_64-cc +- make depend +- make -j $(nproc) +- sudo make install_sw -j $(nproc) +- +- OPENSSL_ROOT_DIR=/usr/local/ssl1.0 +- LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib +- CFLAGS=-I${OPENSSL_ROOT_DIR}/include +- ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" +- PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig +- +- echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV +- echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV +- echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH +- echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV +- echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV +- echo "DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV +- + - name: Set up OpenSSL 1.1 on modern MacOS + # OpenSSL 1.1 is installed by default, so we just need to set the paths + if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_1 == 'YES' +@@ -437,57 +409,35 @@ jobs: + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" + PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig ++ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib + + echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV + echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV + echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH + echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV + + - name: Install OpenSSL 3.0 on modern MacOS + # OpenSSL 1.1 is installed by default, so we need to install 3.0 manually + if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_3_0 == 'YES' + run: | + brew install openssl@3.0 +- ++ + OPENSSL_ROOT_DIR=$(brew --prefix openssl@3.0) + LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" + PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig ++ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib + + echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV + echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV + echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH + echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV +- +- - name: Install OpenSSL 1.0 on modern Linux +- # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default +- if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_0 == 'YES' +- run: | +- curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz +- tar -xzf openssl-1.0.2u.tar.gz +- cd openssl-1.0.2u +- ./config --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared +- make depend +- make -j $(nproc) +- sudo make install_sw -j $(nproc) +- sudo ldconfig +- +- OPENSSL_ROOT_DIR=/usr/local/ssl1.0 +- LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib +- CFLAGS=-I${OPENSSL_ROOT_DIR}/include +- ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" +- PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig +- +- echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV +- echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV +- echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH +- echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV +- echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV +- echo "LD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV +- ++ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV ++ + - name: Install OpenSSL 1.1 on modern Linux + # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default + if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_1 == 'YES' +-- +2.34.1 + + +From 0a4a47143c835a4db7cae1b39e9994b2e38173c9 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Mon, 23 Sep 2024 22:49:28 +0200 +Subject: [PATCH 109/173] Set name of job in github UI + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 33 +++++++++++++++++---------------- + 1 file changed, 17 insertions(+), 16 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 60f5089f..5150cd13 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -7,8 +7,9 @@ on: + types: [published] + workflow_dispatch: + jobs: +- build: ++ build-and-test: + runs-on: ${{ matrix.os }} ++ name: ${{ matrix.env.NAME }} + strategy: + fail-fast: true + matrix: +@@ -16,7 +17,7 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- N: Clang-Linux-Minimal-Debug ++ NAME: Clang-Linux-Minimal-Debug + BUILD_TYPE: Debug + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -41,7 +42,7 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- N: Clang-Linux-Default-Release ++ NAME: Clang-Linux-Default-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -66,7 +67,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCC-Linux-Complete-NoLua-Release ++ NAME: GCC-Linux-Complete-NoLua-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -91,7 +92,7 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- N: CLANG-AnyVersion-Linux-Coverage ++ NAME: CLANG-AnyVersion-Linux-Coverage + BUILD_TYPE: Coverage + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -116,7 +117,7 @@ jobs: + - os: ubuntu-latest + compiler: clang + env: +- N: Clang-Linux-Default-Shared ++ NAME: Clang-Linux-Default-Shared + BUILD_TYPE: Debug + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -141,7 +142,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCCLinuxDefault_RelWithDebInfo ++ NAME: GCCLinuxDefault_RelWithDebInfo + BUILD_TYPE: RelWithDebInfo + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -165,7 +166,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCCLinuxDefault_MinSizeRel ++ NAME: GCCLinuxDefault_MinSizeRel + BUILD_TYPE: MinSizeRel + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -189,7 +190,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCCLinuxDefault_None ++ NAME: GCCLinuxDefault_None + BUILD_TYPE: None + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -213,7 +214,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCCLinuxDefault_xenial ++ NAME: GCCLinuxDefault_xenial + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -237,7 +238,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCCLinuxDefault ++ NAME: GCCLinuxDefault + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -261,7 +262,7 @@ jobs: + - os: ubuntu-latest + compiler: gcc + env: +- N: GCCLinuxDefault_OpenSSL_3_0 ++ NAME: GCCLinuxDefault_OpenSSL_3_0 + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -288,7 +289,7 @@ jobs: + # - os: ubuntu-lastest + # compiler: clang + # env: +- # N: Clang-Linux-Complete-WithLua-Debug ++ # NAME: Clang-Linux-Complete-WithLua-Debug + # BUILD_TYPE: Debug + # ENABLE_SSL_DYNAMIC_LOADING: YES + # OPENSSL_1_0: NO +@@ -313,7 +314,7 @@ jobs: + - os: macos-latest + compiler: clang + env: +- N: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad ++ NAME: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: NO + OPENSSL_1_0: NO +@@ -338,7 +339,7 @@ jobs: + - os: macos-latest + compiler: clang + env: +- N: OSX-Package_OpenSSL_1_1 ++ NAME: OSX-Package_OpenSSL_1_1 + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +@@ -364,7 +365,7 @@ jobs: + - os: macos-latest + compiler: clang + env: +- N: OSX-Package_OpenSSL_3_0 ++ NAME: OSX-Package_OpenSSL_3_0 + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO +-- +2.34.1 + + +From 945118e5ee71ee84df12dd7b31d3ae4909e082b9 Mon Sep 17 00:00:00 2001 +From: Chris Johns +Date: Fri, 27 Sep 2024 14:12:54 +1000 +Subject: [PATCH 110/173] rtems: Add support for RTEMS + +--- + Makefile | 9 +++++++-- + src/civetweb.c | 22 ++++++++++++++++++++-- + 2 files changed, 27 insertions(+), 4 deletions(-) + +diff --git a/Makefile b/Makefile +index 076d8739..3e396581 100644 +--- a/Makefile ++++ b/Makefile +@@ -71,7 +71,11 @@ ifdef WITH_CFLAGS + CFLAGS += $(WITH_CFLAGS) + endif + +-LIBS = -lpthread -lm $(LOPT) ++LIBS = ++ifneq ($(TARGET_OS), RTEMS) ++LIBS += -lpthread ++endif ++LIBS += -lm $(LOPT) + + ifdef WITH_DEBUG + CFLAGS += -g -DDEBUG +@@ -178,7 +182,9 @@ ifdef WITH_COMPRESSION + endif + + ifdef WITH_ZLIB ++ifneq ($(TARGET_OS), RTEMS) + LIBS += -lz ++endif + CFLAGS += -DUSE_ZLIB + endif + +@@ -446,4 +452,3 @@ indent: + astyle --suffix=none --style=linux --indent=spaces=4 --lineend=linux include/*.h src/*.c src/*.cpp src/*.inl examples/*/*.c examples/*/*.cpp + + .PHONY: all help build install clean lib so +- +diff --git a/src/civetweb.c b/src/civetweb.c +index dced1362..7fdf226d 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -183,6 +183,10 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check"); + #error "Symbian is no longer maintained. CivetWeb no longer supports Symbian." + #endif /* __SYMBIAN32__ */ + ++#if defined(__rtems__) ++#include ++#endif ++ + #if defined(__ZEPHYR__) + #include + #include +@@ -885,7 +889,9 @@ typedef unsigned short int in_port_t; + #include + #include + #include ++#if !defined(__rtems__) + #include ++#endif + #include + #include + #include +@@ -963,7 +969,7 @@ count_leap(int y) + return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400; + } + +-time_t ++static time_t + timegm(struct tm *tm) + { + static const unsigned short ydays[] = { +@@ -20786,6 +20792,8 @@ get_system_name(char **sysName) + + *sysName = mg_strdup(name); + ++#elif defined(__rtems__) ++ *sysName = mg_strdup("RTEMS"); + #elif defined(__ZEPHYR__) + *sysName = mg_strdup("Zephyr OS"); + #else +@@ -22079,13 +22087,23 @@ mg_get_system_info(char *buffer, int buflen) + (unsigned)si.dwNumberOfProcessors, + (unsigned)si.dwActiveProcessorMask); + system_info_length += mg_str_append(&buffer, end, block); +-#elif defined(__ZEPHYR__) ++#elif defined(__rtems__) + mg_snprintf(NULL, + NULL, + block, + sizeof(block), + ",%s\"os\" : \"%s %s\"", + eol, ++ "RTEMS", ++ rtems_version()); ++ system_info_length += mg_str_append(&buffer, end, block); ++#elif defined(__ZEPHYR__) ++ mg_snprintf(NULL, ++ NULL, ++ block, ++ sizeof(block), ++ ",%s\"os\" : \"%s\"", ++ eol, + "Zephyr OS", + ZEPHYR_VERSION); + system_info_length += mg_str_append(&buffer, end, block); +-- +2.34.1 + + +From b056de7d8c626200dbf732d55c11cb2f975ce5e3 Mon Sep 17 00:00:00 2001 +From: Egor Konovalov <73017521+egorkonovalov@users.noreply.github.com> +Date: Mon, 7 Oct 2024 17:37:51 +0200 +Subject: [PATCH 111/173] Fix typo + +--- + docs/UserManual.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/UserManual.md b/docs/UserManual.md +index 13011fbb..f0a784b7 100644 +--- a/docs/UserManual.md ++++ b/docs/UserManual.md +@@ -514,7 +514,7 @@ The script can define callbacks to be notified when the server starts + or stops. Furthermore, it can be used for log filtering or formatting. + The Lua state remains open until the server is stopped. + +-For a detailed descriotion of available Lua callbacks see section ++For a detailed description of available Lua callbacks see section + "Lua background script" below. + + ### lua\_background\_script\_params +-- +2.34.1 + + +From b88f02b776fadd06db3a48a8923b2a32280a9107 Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Mon, 23 Sep 2024 15:33:41 +0200 +Subject: [PATCH 112/173] Replace nproc on MacOS + +Signed-off-by: yubiuser +--- + .github/workflows/cibuild.yml | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 5150cd13..d8e1cc6c 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -393,7 +393,16 @@ jobs: + steps: + - name: Checkout code + uses: actions/checkout@v4.1.7 +- ++ ++ - name: Export number of CPUs ++ run: | ++ if [ "$RUNNER_OS" == "Linux" ]; then ++ echo "cores=$(nproc)" >> $GITHUB_ENV ++ fi ++ if [ "$RUNNER_OS" == "macOS" ]; then ++ echo "cores=$(sysctl -n hw.logicalcpu)" >> $GITHUB_ENV ++ fi ++ + - name: Install clang on Linux + if: matrix.compiler == 'clang' && startsWith(matrix.os,'ubuntu') + run: | +@@ -448,8 +457,8 @@ jobs: + cd openssl-1.1.1w + ./config --prefix=/usr/local/ssl1.1 --openssldir=/usr/local/ssl1.1 shared + make depend +- make -j $(nproc) +- sudo make install_sw -j $(nproc) ++ make -j ${{ env.cores }} ++ sudo make install_sw -j ${{ env.cores }} + sudo ldconfig + + OPENSSL_ROOT_DIR=/usr/local/ssl1.1 +@@ -502,11 +511,11 @@ jobs: + - name: Build MacOS Package + if: matrix.env.MACOSX_PACKAGE == 1 + run: | +- make -f Makefile.osx package -j $(nproc) ++ make -f Makefile.osx package -j ${{ env.cores }} + + - name: Build executable + run: | +- cmake --build output -- -j $(nproc) ++ cmake --build output -- -j ${{ env.cores }} + + - name: Check executable + run: | +@@ -527,4 +536,4 @@ jobs: + # Run unit tests + gcc unittest/cgi_test.c -o output/cgi_test.cgi + cd output +- CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test -j $(nproc) +\ No newline at end of file ++ CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test -j ${{ env.cores }} +\ No newline at end of file +-- +2.34.1 + + +From fcaa2ea2d032d23432fd2e88b99c792b32204203 Mon Sep 17 00:00:00 2001 +From: Alejandro +Date: Thu, 24 Oct 2024 16:26:18 +0200 +Subject: [PATCH 113/173] #1147 Load version-specific dylib + +--- + src/civetweb.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index b3295cdd..c52c8e7a 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -907,8 +907,22 @@ typedef unsigned short int in_port_t; + #endif + + #if defined(__MACH__) && defined(__APPLE__) +-#define SSL_LIB "libssl.dylib" +-#define CRYPTO_LIB "libcrypto.dylib" ++ ++#if defined(OPENSSL_API_3_0) ++#define SSL_LIB "libssl.3.dylib" ++#define CRYPTO_LIB "libcrypto.3.dylib" ++#endif ++ ++#if defined(OPENSSL_API_1_1) ++#define SSL_LIB "libssl.1.1.dylib" ++#define CRYPTO_LIB "libcrypto.1.1.dylib" ++#endif /* OPENSSL_API_1_1 */ ++ ++#if defined(OPENSSL_API_1_0) ++#define SSL_LIB "libssl.1.0.dylib" ++#define CRYPTO_LIB "libcrypto.1.0.dylib" ++#endif /* OPENSSL_API_1_0 */ ++ + #else + #if !defined(SSL_LIB) + #define SSL_LIB "libssl.so" +-- +2.34.1 + + +From e927db7979e07ca5ceb06a61889a69b733dc029d Mon Sep 17 00:00:00 2001 +From: Niklas Fiekas +Date: Sun, 17 Nov 2024 16:33:35 +0100 +Subject: [PATCH 114/173] Bump minimum CMake version to 3.10 + +--- + CMakeLists.txt | 7 ++----- + examples/linux_ws_server_cpp/CMakeLists.txt | 2 +- + 2 files changed, 3 insertions(+), 6 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 4ffd41b9..293dabac 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,8 +1,5 @@ +-# Use at least CMake 3.3 +-cmake_minimum_required (VERSION 3.3.0) +-cmake_policy(VERSION 3.2.2) +-cmake_policy(SET CMP0054 NEW) +-cmake_policy(SET CMP0057 NEW) ++cmake_minimum_required(VERSION 3.10) ++cmake_policy(VERSION 3.10) + + # Set up the project + project (civetweb VERSION 1.16.0) +diff --git a/examples/linux_ws_server_cpp/CMakeLists.txt b/examples/linux_ws_server_cpp/CMakeLists.txt +index 6a71798e..8f765ac5 100644 +--- a/examples/linux_ws_server_cpp/CMakeLists.txt ++++ b/examples/linux_ws_server_cpp/CMakeLists.txt +@@ -1,4 +1,4 @@ +-cmake_minimum_required(VERSION 3.5.1) ++cmake_minimum_required(VERSION 3.10) + project(linux_ws_server) + + set(TARGET_NAME ${PROJECT_NAME}) +-- +2.34.1 + + +From e0448e48d08e66260f11a5b6d22144cba6f9073d Mon Sep 17 00:00:00 2001 +From: PythonGermany <97847597+PythonGermany@users.noreply.github.com> +Date: Fri, 6 Dec 2024 13:04:41 +0100 +Subject: [PATCH 115/173] Update listening_ports documentation + +Add description for optional port configuration +--- + docs/UserManual.md | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/docs/UserManual.md b/docs/UserManual.md +index f0a784b7..7289cfa0 100644 +--- a/docs/UserManual.md ++++ b/docs/UserManual.md +@@ -440,7 +440,9 @@ of 1000. + ### listening\_ports `8080` + Comma-separated list of ports to listen on. If the port is SSL, a + letter `s` must be appended, for example, `80,443s` will open +-port 80 and port 443, and connections on port 443 will be SSL-ed. ++port 80 and port 443, and connections on port 443 will be SSL-ed. If the port ++should be optional the letter `o` must be appended, for example with `80o,443s` ++starting the server will not exit if binding to port 80 is not possible. + For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'. + Redirect ports will redirect all their traffic to the first configured + SSL port. For example, if `listening_ports` is `80r,443s`, then all +-- +2.34.1 + + +From 68ec0a171ae6c2a35e42b5e1290e121043b423a2 Mon Sep 17 00:00:00 2001 +From: PythonGermany <97847597+PythonGermany@users.noreply.github.com> +Date: Fri, 6 Dec 2024 13:16:49 +0100 +Subject: [PATCH 116/173] Update listening_ports documentation + +--- + docs/UserManual.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/UserManual.md b/docs/UserManual.md +index 7289cfa0..f036b230 100644 +--- a/docs/UserManual.md ++++ b/docs/UserManual.md +@@ -442,7 +442,7 @@ Comma-separated list of ports to listen on. If the port is SSL, a + letter `s` must be appended, for example, `80,443s` will open + port 80 and port 443, and connections on port 443 will be SSL-ed. If the port + should be optional the letter `o` must be appended, for example with `80o,443s` +-starting the server will not exit if binding to port 80 is not possible. ++the server will not exit if binding to port 80 is not possible during startup. + For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'. + Redirect ports will redirect all their traffic to the first configured + SSL port. For example, if `listening_ports` is `80r,443s`, then all +-- +2.34.1 + + +From 87a305c5bcd6ea463124069659ea6492d4eeb4ec Mon Sep 17 00:00:00 2001 +From: PythonGermany <97847597+PythonGermany@users.noreply.github.com> +Date: Fri, 6 Dec 2024 13:59:23 +0100 +Subject: [PATCH 117/173] Update listening_ports documentation + +Add explanation on how port redirection is configured +--- + docs/UserManual.md | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/docs/UserManual.md b/docs/UserManual.md +index f0a784b7..e36db229 100644 +--- a/docs/UserManual.md ++++ b/docs/UserManual.md +@@ -445,6 +445,8 @@ For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'. + Redirect ports will redirect all their traffic to the first configured + SSL port. For example, if `listening_ports` is `80r,443s`, then all + HTTP traffic coming at port 80 will be redirected to HTTPS port 443. ++For ports with redirection configured `authentication_domain` will ++be used as host component of the redirection url. + + It is possible to specify an IP address to bind to. In this case, + an IP address and a colon must be prepended to the port number. +-- +2.34.1 + + +From cf1e77659d05c6c67a1a0f7ce076311cc56ae266 Mon Sep 17 00:00:00 2001 +From: catch-error +Date: Fri, 27 Dec 2024 19:56:19 +0100 +Subject: [PATCH 118/173] Add GnutTLS support. + +Signed-off-by: catch-error +--- + CMakeLists.txt | 7 +- + Makefile | 4 + + README.md | 2 + + docs/Building.md | 5 +- + docs/UserManual.md | 3 +- + docs/gnutls.md | 20 ++++ + format.bat | 1 + + src/CMakeLists.txt | 7 +- + src/civetweb.c | 164 ++++++++++++++++++++++++++++--- + src/mod_gnutls.inl | 240 +++++++++++++++++++++++++++++++++++++++++++++ + 10 files changed, 433 insertions(+), 20 deletions(-) + create mode 100644 docs/gnutls.md + create mode 100644 src/mod_gnutls.inl + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 293dabac..602fbe41 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -237,8 +237,11 @@ message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}") + option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF) + message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}") + ++option(CIVETWEB_ENABLE_GNUTLS "Use the GnuTls" OFF) ++message(STATUS "SSL support (GnuTLS) - ${CIVETWEB_ENABLE_GNUTLS}") ++ + option(CIVETWEB_ENABLE_MBEDTLS "Use the MbedTls" OFF) +-message(STATUS "SSL support - ${CIVETWEB_ENABLE_MBEDTLS}") ++message(STATUS "SSL support (MbedTLS) - ${CIVETWEB_ENABLE_MBEDTLS}") + + # Dynamically load or link the SSL libraries + cmake_dependent_option( +@@ -538,6 +541,8 @@ if (CIVETWEB_ENABLE_MEMORY_DEBUGGING) + endif() + if (NOT CIVETWEB_ENABLE_SSL) + add_definitions(-DNO_SSL) ++elseif (CIVETWEB_ENABLE_GNUTLS) ++ add_definitions(-DUSE_GNUTLS) + elseif (CIVETWEB_ENABLE_MBEDTLS) + add_definitions(-DUSE_MBEDTLS) + elseif (NOT CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING) +diff --git a/Makefile b/Makefile +index 3e396581..7831ce39 100644 +--- a/Makefile ++++ b/Makefile +@@ -97,6 +97,9 @@ endif + + ifdef NO_SSL + CFLAGS += -DNO_SSL ++else ifdef WITH_GNUTLS ++ CFLAGS += -DUSE_GNUTLS ++ LIBS += -lgnutls -lhogweed -lgmp -lnettle + else ifdef WITH_MBEDTLS + CFLAGS += -DUSE_MBEDTLS + LIBS += -lmbedcrypto -lmbedtls -lmbedx509 +@@ -303,6 +306,7 @@ help: + @echo " WITH_CPP=1 build library with c++ classes" + @echo " WITH_EXPERIMENTAL=1 build with experimental features" + @echo " WITH_DAEMONIZE=1 build with daemonize." ++ @echo " WITH_GNUTLS=1 build with GnuTLS support." + @echo " WITH_MBEDTLS=1 build with mbedTLS support." + @echo " WITH_OPENSSL_API_1_0=1 build with OpenSSL 1.0.x support." + @echo " WITH_OPENSSL_API_1_1=1 build with OpenSSL 1.1.x support." +diff --git a/README.md b/README.md +index d0624ab5..dae05879 100644 +--- a/README.md ++++ b/README.md +@@ -134,6 +134,8 @@ simplicity by a carefully selected list of features: + + [Mbed TLS](https://github.com/ARMmbed/mbedtls) + ++[GNU TLS](https://gnutls.org) ++ + + Support + ------- +diff --git a/docs/Building.md b/docs/Building.md +index c6f92add..b6185333 100644 +--- a/docs/Building.md ++++ b/docs/Building.md +@@ -179,8 +179,9 @@ make build COPT="-DNDEBUG -DNO_CGI" + | `SSL_ALREADY_INITIALIZED` | do not initialize libcrypto | + | `OPENSSL_API_1_0` | Use OpenSSL V1.0.x interface | + | `OPENSSL_API_1_1` | Use OpenSSL V1.1.x interface | +-| `OPENSSL_API_3_0` | Use OpenSSL V3.0.x interface | +-| `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_*) | ++| `OPENSSL_API_3_0` | Use OpenSSL V3.0.x interface | ++| `USE_GNUTLS` | Use GnuTLS (cannot be combined with OPENSSL_API_* or USE_MBEDTLS) | ++| `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_* or USE_GNUTLS) | + | | | + | `BUILD_DATE` | define as a string to be used as build id instead of __DATE__ | + | | | +diff --git a/docs/UserManual.md b/docs/UserManual.md +index ba009fec..7dc48c38 100644 +--- a/docs/UserManual.md ++++ b/docs/UserManual.md +@@ -692,7 +692,8 @@ The OpenSSL cipher string uses different cipher names than IANA + (see [this mapping](https://testssl.sh/openssl-iana.mapping.html)). + + In case CivetWeb is built with a TLS library other than OpenSSL +-(e.g., [mbedTLS](https://tls.mbed.org/supported-ssl-ciphersuites)), ++(e.g., [mbedTLS](https://tls.mbed.org/supported-ssl-ciphersuites) ++or [GnuTLS](https://www.gnutls.org/manual/html_node/Supported-ciphersuites.html)), + the cipher names may be different. + + ### ssl\_default\_verify\_paths `yes` +diff --git a/docs/gnutls.md b/docs/gnutls.md +new file mode 100644 +index 00000000..c247850f +--- /dev/null ++++ b/docs/gnutls.md +@@ -0,0 +1,20 @@ ++#### Use GnuTLS instead of OpenSSL ++===== ++ ++1 [Build libgmp](https://gmplib.org) ++ ++ - 1.1 [Download source](https://gmplib.org/#DOWNLOAD) ++ - 1.2 ./configure && make && make install ++ ++2 [Build libhogweed and libnettle](https://www.lysator.liu.se/~nisse/nettle/) ++ ++ - 2.1 [Download source](https://ftp.gnu.org/gnu/nettle/) ++ - 2.2 ./configure && make && make install ++ ++3 Build civetweb ++ ++ - make build WITH_GNUTLS=1 ++ ++4 Run civetweb ++ - export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH ++ - ./civetweb -listening_ports 8443s -ssl_certificate resources/cert/server.pem -document_root ./test/htmldir/ +diff --git a/format.bat b/format.bat +index 203b0446..3bb94e65 100755 +--- a/format.bat ++++ b/format.bat +@@ -17,6 +17,7 @@ clang-format -i src/handle_form.inl + clang-format -i src/response.inl + clang-format -i src/http2.inl + clang-format -i src/mod_mbedtls.inl ++clang-format -i src/mod_gnutls.inl + + clang-format -i src/third_party/civetweb_lua.h + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 28c4e8cc..8736e126 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -47,7 +47,12 @@ endif() + + # We need to link OpenSSL if not dynamically loading + if (CIVETWEB_ENABLE_SSL) +- if (CIVETWEB_ENABLE_MBEDTLS) ++ if (CIVETWEB_ENABLE_GNUTLS) ++ find_package(GnuTLS) ++ include_directories(${GNUTLS_INCLUDE_DIR}) ++ message(STATUS "GnuTLS include directory: ${GNUTLS_INCLUDE_DIR}") ++ target_link_libraries(civetweb-c-library ${GNUTLS_LIBRARIES}) ++ elseif (CIVETWEB_ENABLE_MBEDTLS) + find_package(MbedTLS) + include_directories(${MbedTLS_INCLUDE_DIR}) + message(STATUS "MbedTLS include directory: ${MbedTLS_INCLUDE_DIR}") +diff --git a/src/civetweb.c b/src/civetweb.c +index c52c8e7a..814e1de0 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1594,8 +1594,9 @@ static int mg_init_library_called = 0; + static int mg_openssl_initialized = 0; + #endif + #if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1) \ +- && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS) +-#error "Please define OPENSSL_API_#_# or USE_MBEDTLS" ++ && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS) \ ++ && !defined(USE_GNUTLS) ++#error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS" + #endif + #if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1) + #error "Multiple OPENSSL_API versions defined" +@@ -1608,7 +1609,10 @@ static int mg_openssl_initialized = 0; + #endif + #if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \ + || defined(OPENSSL_API_3_0)) \ +- && defined(USE_MBEDTLS) ++ && (defined(USE_MBEDTLS) || defined(USE_GNUTLS)) ++#error "Multiple SSL libraries defined" ++#endif ++#if defined(USE_MBEDTLS) && defined(USE_GNUTLS) + #error "Multiple SSL libraries defined" + #endif + #endif +@@ -1773,11 +1777,15 @@ typedef int socklen_t; + #endif + + +-/* SSL: mbedTLS vs. no-ssl vs. OpenSSL */ ++/* SSL: mbedTLS vs. GnuTLS vs. no-ssl vs. OpenSSL */ + #if defined(USE_MBEDTLS) + /* mbedTLS */ + #include "mod_mbedtls.inl" + ++#elif defined(USE_GNUTLS) ++/* GnuTLS */ ++#include "mod_gnutls.inl" ++ + #elif defined(NO_SSL) + /* no SSL */ + typedef struct SSL SSL; /* dummy for SSL argument to push/pull */ +@@ -2564,7 +2572,6 @@ struct mg_connection { + * versions. For the current definition, see + * mg_get_connection_info_impl */ + #endif +- + SSL *ssl; /* SSL descriptor */ + struct socket client; /* Connected client */ + time_t conn_birth_time; /* Time (wall clock) when connection was +@@ -6131,7 +6138,7 @@ push_inner(struct mg_context *ctx, + return -2; + } + +-#if defined(NO_SSL) && !defined(USE_MBEDTLS) ++#if defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) + if (ssl) { + return -2; + } +@@ -6157,6 +6164,16 @@ push_inner(struct mg_context *ctx, + err = 0; + } + } else ++#elif defined(USE_GNUTLS) ++ if (ssl != NULL) { ++ n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t) len); ++ if (n < 0) { ++ fprintf(stderr, "SSL write failed (%d): %s", n, gnutls_strerror(n)); ++ return -2; ++ } else { ++ err = 0; ++ } ++ } else + #elif !defined(NO_SSL) + if (ssl != NULL) { + ERR_clear_error(); +@@ -6413,6 +6430,62 @@ pull_inner(FILE *fp, + nread = 0; + } + ++#elif defined(USE_GNUTLS) ++ } else if (conn->ssl != NULL) { ++ struct mg_pollfd pfd[2]; ++ size_t to_read; ++ int pollres; ++ unsigned int num_sock = 1; ++ ++ to_read = gnutls_record_check_pending(conn->ssl->sess); ++ ++ if (to_read > 0) { ++ /* We already know there is no more data buffered in conn->buf ++ * but there is more available in the SSL layer. So don't poll ++ * conn->client.sock yet. */ ++ ++ pollres = 1; ++ if (to_read > (size_t)len) ++ to_read = (size_t)len; ++ } else { ++ pfd[0].fd = conn->client.sock; ++ pfd[0].events = POLLIN; ++ ++ if (conn->phys_ctx->context_type == CONTEXT_SERVER) { ++ pfd[num_sock].fd = ++ conn->phys_ctx->thread_shutdown_notification_socket; ++ pfd[num_sock].events = POLLIN; ++ num_sock++; ++ } ++ ++ to_read = (size_t)len; ++ ++ pollres = mg_poll(pfd, ++ num_sock, ++ (int)(timeout * 1000.0), ++ &(conn->phys_ctx->stop_flag)); ++ ++ if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { ++ return -2; ++ } ++ } ++ ++ if (pollres > 0) { ++ nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read); ++ if (nread < 0) { ++ fprintf(stderr, "SSL read failed (%d): %s", nread, gnutls_strerror(nread)); ++ return -2; ++ } else { ++ err = 0; ++ } ++ } else if (pollres < 0) { ++ /* Error */ ++ return -2; ++ } else { ++ /* pollres = 0 means timeout */ ++ nread = 0; ++ } ++ + #elif !defined(NO_SSL) + } else if (conn->ssl != NULL) { + int ssl_pending; +@@ -9555,7 +9628,7 @@ connect_socket( + return 0; + } + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL) ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) && !defined(NO_SSL_DL) + #if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) + if (use_ssl && (TLS_client_method == NULL)) { + if (error != NULL) { +@@ -16660,6 +16733,37 @@ mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) + : 0; + } + ++#elif defined(USE_GNUTLS) ++/* Check if SSL is required. ++ * If so, set up ctx->ssl_ctx pointer. */ ++static int ++mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) ++{ ++ if (!phys_ctx) { ++ return 0; ++ } ++ ++ if (!dom_ctx) { ++ dom_ctx = &(phys_ctx->dd); ++ } ++ ++ if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) { ++ /* No SSL port is set. No need to setup SSL. */ ++ return 1; ++ } ++ ++ dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx)); ++ if (dom_ctx->ssl_ctx == NULL) { ++ fprintf(stderr, "ssl_ctx malloc failed\n"); ++ return 0; ++ } ++ ++ return gtls_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE]) ++ == 0 ++ ? 1 ++ : 0; ++} ++ + #elif !defined(NO_SSL) + + static int ssl_use_pem_file(struct mg_context *phys_ctx, +@@ -17935,7 +18039,7 @@ uninitialize_openssl(void) + #endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */ + } + } +-#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) */ ++#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) */ + + + #if !defined(NO_FILESYSTEMS) +@@ -18207,6 +18311,11 @@ close_connection(struct mg_connection *conn) + mbed_ssl_close(conn->ssl); + conn->ssl = NULL; + } ++#elif defined(USE_GNUTLS) ++ if (conn->ssl != NULL) { ++ gtls_ssl_close(conn->ssl); ++ conn->ssl = NULL; ++ } + #elif !defined(NO_SSL) + if (conn->ssl != NULL) { + /* Run SSL_shutdown twice to ensure completely close SSL connection +@@ -18278,7 +18387,7 @@ mg_close_connection(struct mg_connection *conn) + + close_connection(conn); + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client + if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) + || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT)) + && (conn->phys_ctx->dd.ssl_ctx != NULL)) { +@@ -18380,7 +18489,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options, + return NULL; + } + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client + #if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \ + && !defined(NO_SSL_DL) + +@@ -18457,7 +18566,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options, + error->text_buffer_size, + "Can not create mutex"); + } +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client + SSL_CTX_free(conn->dom_ctx->ssl_ctx); + #endif + closesocket(sock); +@@ -18465,7 +18574,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options, + return NULL; + } + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client + if (use_ssl) { + /* TODO: Check ssl_verify_peer and ssl_ca_path here. + * SSL_CTX_set_verify call is needed to switch off server +@@ -20186,6 +20295,24 @@ worker_thread_run(struct mg_connection *conn) + close_connection(conn); + } + ++#elif defined(USE_GNUTLS) ++ /* HTTPS connection */ ++ if (gtls_ssl_accept(&(conn->ssl), ++ conn->dom_ctx->ssl_ctx, ++ conn->client.sock, ++ conn->phys_ctx) ++ == 0) { ++ /* conn->dom_ctx is set in get_request */ ++ /* process HTTPS connection */ ++ init_connection(conn); ++ conn->connection_type = CONNECTION_TYPE_REQUEST; ++ conn->protocol_type = PROTOCOL_TYPE_HTTP1; ++ process_new_connection(conn); ++ } else { ++ /* make sure the connection is cleaned up on SSL failure */ ++ close_connection(conn); ++ } ++ + #elif !defined(NO_SSL) + /* HTTPS connection */ + if (sslize(conn, SSL_accept, NULL)) { +@@ -20693,6 +20820,13 @@ free_context(struct mg_context *ctx) + ctx->dd.ssl_ctx = NULL; + } + ++#elif defined(USE_GNUTLS) ++ if (ctx->dd.ssl_ctx != NULL) { ++ gtls_sslctx_uninit(ctx->dd.ssl_ctx); ++ mg_free(ctx->dd.ssl_ctx); ++ ctx->dd.ssl_ctx = NULL; ++ } ++ + #elif !defined(NO_SSL) + /* Deallocate SSL context */ + if (ctx->dd.ssl_ctx != NULL) { +@@ -21383,7 +21517,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + } + #endif + +-#if defined(USE_MBEDTLS) ++#if defined(USE_MBEDTLS) || defined(USE_GNUTLS) + if (!mg_sslctx_init(ctx, NULL)) { + const char *err_msg = "Error initializing SSL context"; + /* Fatal error - abort start. */ +@@ -21868,7 +22002,7 @@ mg_start_domain2(struct mg_context *ctx, + new_dom->shared_lua_websockets = NULL; + #endif + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) + if (!init_ssl_ctx(ctx, new_dom)) { + /* Init SSL failed */ + if (error != NULL) { +@@ -21947,7 +22081,7 @@ mg_check_feature(unsigned feature) + #if !defined(NO_FILES) + | MG_FEATURES_FILES + #endif +-#if !defined(NO_SSL) || defined(USE_MBEDTLS) ++#if !defined(NO_SSL) || defined(USE_MBEDTLS) || defined(USE_GNUTLS) + | MG_FEATURES_SSL + #endif + #if !defined(NO_CGI) +diff --git a/src/mod_gnutls.inl b/src/mod_gnutls.inl +new file mode 100644 +index 00000000..b4ca5d47 +--- /dev/null ++++ b/src/mod_gnutls.inl +@@ -0,0 +1,240 @@ ++#if defined(USE_GNUTLS) // USE_GNUTLS used with NO_SSL ++ ++#include ++#include ++ ++typedef struct { ++ gnutls_session_t sess; ++} SSL; ++typedef struct { ++ gnutls_certificate_credentials_t cred; ++ gnutls_priority_t prio; ++} SSL_CTX; ++ ++ ++/* public api */ ++CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt); ++CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx); ++CIVETWEB_API void gtls_ssl_close(SSL *ssl); ++CIVETWEB_API int gtls_ssl_accept(SSL **ssl, ++ SSL_CTX *ssl_ctx, ++ int sock, ++ struct mg_context *phys_ctx); ++CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len); ++CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len); ++ ++ ++CIVETWEB_API int ++gtls_sslctx_init(SSL_CTX *ctx, const char *crt) ++{ ++ int rc; ++ ++ if (ctx == NULL || crt == NULL) { ++ return -1; ++ } ++ ++ DEBUG_TRACE("%s", "Initializing GnuTLS SSL"); ++ ++ rc = gnutls_certificate_allocate_credentials(&ctx->cred); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("Failed to allocate credentials (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_priority_init(&ctx->prio, NULL, NULL); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("Failed to allocate priority cache (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_certificate_set_x509_key_file2(ctx->cred, ++ crt, ++ crt, ++ GNUTLS_X509_FMT_PEM, ++ NULL, ++ GNUTLS_PKCS_PLAIN ++ | GNUTLS_PKCS_NULL_PASSWORD); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("TLS parse crt/key file failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ return 0; ++ ++failed: ++ gtls_sslctx_uninit(ctx); ++ ++ return -1; ++} ++ ++ ++CIVETWEB_API void ++gtls_sslctx_uninit(SSL_CTX *ctx) ++{ ++ if (ctx != NULL) { ++ gnutls_certificate_free_credentials(ctx->cred); ++ gnutls_priority_deinit(ctx->prio); ++ ctx->cred = NULL; ++ ctx->prio = NULL; ++ } ++} ++ ++ ++CIVETWEB_API int ++gtls_ssl_accept(SSL **ssl, ++ SSL_CTX *ssl_ctx, ++ int sock, ++ struct mg_context *phys_ctx) ++{ ++ int rc; ++ ++ if (ssl == NULL || ssl_ctx == NULL) { ++ return -1; ++ } ++ ++ DEBUG_TRACE("TLS accept processing %p", ssl); ++ ++ *ssl = (SSL *)mg_calloc_ctx(1, sizeof(SSL), phys_ctx); ++ if (*ssl == NULL) { ++ DEBUG_TRACE("Failed to allocate memory for session %zu", sizeof(SSL)); ++ return -1; ++ } ++ ++ rc = gnutls_init(&(*ssl)->sess, GNUTLS_SERVER); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("Failed to initialize session (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_priority_set((*ssl)->sess, ssl_ctx->prio); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("TLS set priortities failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_credentials_set((*ssl)->sess, ++ GNUTLS_CRD_CERTIFICATE, ++ ssl_ctx->cred); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("TLS set credentials failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ gnutls_certificate_send_x509_rdn_sequence((*ssl)->sess, 1); ++ gnutls_certificate_server_set_request((*ssl)->sess, GNUTLS_CERT_IGNORE); ++ gnutls_handshake_set_timeout((*ssl)->sess, ++ GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); ++ gnutls_transport_set_int((*ssl)->sess, sock); ++ ++ while ((rc = gnutls_handshake((*ssl)->sess)) != GNUTLS_E_SUCCESS) { ++ if (gnutls_error_is_fatal(rc)) { ++ if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { ++ DEBUG_TRACE("TLS fatal alert received: %s", ++ gnutls_alert_get_name( ++ gnutls_alert_get((*ssl)->sess))); ++ } else { ++ DEBUG_TRACE("TLS handshake failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ } ++ ++ goto failed; ++ } ++ } ++ ++ DEBUG_TRACE("TLS connection %p accepted", *ssl); ++ ++ return 0; ++ ++failed: ++ gnutls_deinit((*ssl)->sess); ++ mg_free(*ssl); ++ *ssl = NULL; ++ ++ return -1; ++} ++ ++ ++CIVETWEB_API void ++gtls_ssl_close(SSL *ssl) ++{ ++ int rc; ++ ++ if (ssl == NULL) { ++ return; ++ } ++ ++ while ((rc = gnutls_bye(ssl->sess, GNUTLS_SHUT_RDWR)) != GNUTLS_E_SUCCESS) { ++ switch (rc) { ++ case GNUTLS_E_AGAIN: /* fall through */ ++ case GNUTLS_E_INTERRUPTED: ++ continue; ++ default: /* should actually never happen */ ++ break; ++ } ++ } ++ ++ DEBUG_TRACE("TLS connection %p closed", ssl); ++ gnutls_deinit(ssl->sess); ++ mg_free(ssl); ++} ++ ++ ++CIVETWEB_API int ++gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len) ++{ ++ ssize_t rc; ++ ++ if (ssl == NULL) { ++ return GNUTLS_E_INVALID_SESSION; ++ } ++ ++ while ((rc = gnutls_record_recv(ssl->sess, buf, len)) < 0) { ++ switch (rc) { ++ case GNUTLS_E_AGAIN: /* fall through */ ++ case GNUTLS_E_INTERRUPTED: ++ continue; ++ default: ++ break; ++ } ++ } ++ /* DEBUG_TRACE("gnutls_record_recv: %d", rc); */ ++ return (int)rc; ++} ++ ++ ++CIVETWEB_API int ++gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len) ++{ ++ ssize_t rc; ++ ++ if (ssl == NULL) { ++ return GNUTLS_E_INVALID_SESSION; ++ } ++ ++ while ((rc = gnutls_record_send(ssl->sess, buf, len)) < 0) { ++ switch (rc) { ++ case GNUTLS_E_AGAIN: /* fall through */ ++ case GNUTLS_E_INTERRUPTED: ++ continue; ++ default: ++ break; ++ } ++ } ++ /* DEBUG_TRACE("gnutls_record_send: %d", rc); */ ++ return (int)rc; ++} ++ ++#endif /* USE_GNUTLS */ +-- +2.34.1 + + +From e92dbe12bfe0ff449a2f5a2f1984bb2c2dfb14bc Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Sun, 12 Jan 2025 19:42:07 +0100 +Subject: [PATCH 119/173] Expose is_optional property of listining_ports via + mg_get_server_ports() + +Signed-off-by: DL6ER +--- + docs/api/mg_server_port.md | 2 +- + include/civetweb.h | 2 +- + src/civetweb.c | 1 + + unittest/public_server.c | 16 ++++++++++++++-- + 4 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/docs/api/mg_server_port.md b/docs/api/mg_server_port.md +index 072b2979..01c9c42c 100644 +--- a/docs/api/mg_server_port.md ++++ b/docs/api/mg_server_port.md +@@ -10,7 +10,7 @@ + |**`port`**|`int`|The port number on which the service listens| + |**`is_ssl`**|`int`|**0** for `HTTP` communication, **1** for `HTTPS`| + |**`is_redirect`**|`int`|**1** if all requests are redirected, otherwise **0**| +-|**`_reserved1`**|`int`|Reserved for internal use| ++|**`is_optional`**|`int`|**1** if prot is optional, otherwise **0**| + |**`_reserved2`**|`int`|Reserved for internal use| + |**`_reserved3`**|`int`|Reserved for internal use| + |**`_reserved4`**|`int`|Reserved for internal use| +diff --git a/include/civetweb.h b/include/civetweb.h +index 5ae6a0c7..0b72eaf3 100644 +--- a/include/civetweb.h ++++ b/include/civetweb.h +@@ -714,7 +714,7 @@ struct mg_server_port { + int port; /* port number */ + int is_ssl; /* https port: 0 = no, 1 = yes */ + int is_redirect; /* redirect all requests: 0 = no, 1 = yes */ +- int _reserved1; ++ int is_optional; /* optional: 0 = no, 1 = yes */ + int _reserved2; + int _reserved3; + int _reserved4; +diff --git a/src/civetweb.c b/src/civetweb.c +index 814e1de0..9f9ef2e8 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -3337,6 +3337,7 @@ mg_get_server_ports(const struct mg_context *ctx, + ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa))); + ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl; + ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir; ++ ports[cnt].is_optional = ctx->listening_sockets[i].is_optional; + + if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) { + /* IPv4 */ +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 4983ee8c..bad2b77f 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -457,10 +457,12 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) + ck_assert_int_eq(portinfo[0].port, 0); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 0); + + ret = mg_get_server_ports(ctx, 4, portinfo); + ck_assert_int_eq(ret, 1); +@@ -472,10 +474,12 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) + ck_assert_int_eq(portinfo[0].port, 8080); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 0); + + test_sleep(1); + +@@ -649,7 +653,7 @@ START_TEST(test_mg_start_stop_https_server) + OPTIONS[opt_idx++] = "."; + #endif + OPTIONS[opt_idx++] = "listening_ports"; +- OPTIONS[opt_idx++] = "8080r,8443s"; ++ OPTIONS[opt_idx++] = "8080r,8443os"; + OPTIONS[opt_idx++] = "ssl_certificate"; + OPTIONS[opt_idx++] = ssl_cert; + +@@ -674,10 +678,12 @@ START_TEST(test_mg_start_stop_https_server) + ck_assert_int_eq(portinfo[0].port, 0); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 0); + + ret = mg_get_server_ports(ctx, 4, portinfo); + ck_assert_int_eq(ret, 2); +@@ -685,14 +691,17 @@ START_TEST(test_mg_start_stop_https_server) + ck_assert_int_eq(portinfo[0].port, 8080); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 1); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); + ck_assert_int_eq(portinfo[1].protocol, 1); + ck_assert_int_eq(portinfo[1].port, 8443); + ck_assert_int_eq(portinfo[1].is_ssl, 1); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 1); + ck_assert_int_eq(portinfo[2].protocol, 0); + ck_assert_int_eq(portinfo[2].port, 0); + ck_assert_int_eq(portinfo[2].is_ssl, 0); + ck_assert_int_eq(portinfo[2].is_redirect, 0); ++ ck_assert_int_eq(portinfo[2].is_optional, 0); + + test_sleep(1); + +@@ -771,7 +780,7 @@ START_TEST(test_mg_server_and_client_tls) + OPTIONS[opt_idx++] = "."; + #endif + OPTIONS[opt_idx++] = "listening_ports"; +- OPTIONS[opt_idx++] = "8080r,8443s"; ++ OPTIONS[opt_idx++] = "8080r,8443os"; + OPTIONS[opt_idx++] = "ssl_certificate"; + OPTIONS[opt_idx++] = server_cert; + OPTIONS[opt_idx++] = "ssl_verify_peer"; +@@ -800,14 +809,17 @@ START_TEST(test_mg_server_and_client_tls) + ck_assert_int_eq(ports[0].port, 8080); + ck_assert_int_eq(ports[0].is_ssl, 0); + ck_assert_int_eq(ports[0].is_redirect, 1); ++ ck_assert_int_eq(ports[0].is_optional, 0); + ck_assert_int_eq(ports[1].protocol, 1); + ck_assert_int_eq(ports[1].port, 8443); + ck_assert_int_eq(ports[1].is_ssl, 1); + ck_assert_int_eq(ports[1].is_redirect, 0); ++ ck_assert_int_eq(ports[1].is_optional, 1); + ck_assert_int_eq(ports[2].protocol, 0); + ck_assert_int_eq(ports[2].port, 0); + ck_assert_int_eq(ports[2].is_ssl, 0); + ck_assert_int_eq(ports[2].is_redirect, 0); ++ ck_assert_int_eq(ports[2].is_optional, 0); + + test_sleep(1); + +-- +2.34.1 + + +From e1d7d71e0b7973006fadad5bb06af7cc2af69021 Mon Sep 17 00:00:00 2001 +From: phi-go +Date: Thu, 23 Jan 2025 10:01:42 +0100 +Subject: [PATCH 120/173] return 0 for TestOneInput + +libFuzzer discards inputs when returning non-zero, and so gets stuck for +some targets +--- + fuzztest/fuzzmain.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c +index e8226de2..9be1bb2d 100644 +--- a/fuzztest/fuzzmain.c ++++ b/fuzztest/fuzzmain.c +@@ -497,21 +497,26 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) + { + #if defined(TEST_FUZZ1) + /* fuzz target 1: different URI for HTTP/1 server */ +- return LLVMFuzzerTestOneInput_URI(data, size); ++ LLVMFuzzerTestOneInput_URI(data, size); ++ return 0; + #elif defined(TEST_FUZZ2) + /* fuzz target 2: different requests for HTTP/1 server */ +- return LLVMFuzzerTestOneInput_REQUEST(data, size); ++ LLVMFuzzerTestOneInput_REQUEST(data, size); ++ return 0; + #elif defined(TEST_FUZZ3) + /* fuzz target 3: different responses for HTTP/1 client */ +- return LLVMFuzzerTestOneInput_RESPONSE(data, size); ++ LLVMFuzzerTestOneInput_RESPONSE(data, size); ++ return 0; + #elif defined(TEST_FUZZ4) + #error "Only useful in HTTP/2 feature branch" + /* fuzz target 4: different requests for HTTP/2 server */ +- return LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size); ++ LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size); ++ return 0; + #elif defined(TEST_FUZZ5) + /* fuzz target 5: calling an internal server test function, + * bypassing network sockets */ +- return LLVMFuzzerTestOneInput_process_new_connection(data, size); ++ LLVMFuzzerTestOneInput_process_new_connection(data, size); ++ return 0; + #else + /* planned targets */ + #error "Unknown fuzz target" +-- +2.34.1 + + +From 413387fcd813cb3cc77c9d6f4f9e9fbcaad44f98 Mon Sep 17 00:00:00 2001 +From: phi-go +Date: Thu, 23 Jan 2025 10:49:03 +0100 +Subject: [PATCH 121/173] formatting + +--- + fuzztest/fuzzmain.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c +index 9be1bb2d..89424656 100644 +--- a/fuzztest/fuzzmain.c ++++ b/fuzztest/fuzzmain.c +@@ -498,25 +498,25 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) + #if defined(TEST_FUZZ1) + /* fuzz target 1: different URI for HTTP/1 server */ + LLVMFuzzerTestOneInput_URI(data, size); +- return 0; ++ return 0; + #elif defined(TEST_FUZZ2) + /* fuzz target 2: different requests for HTTP/1 server */ + LLVMFuzzerTestOneInput_REQUEST(data, size); +- return 0; ++ return 0; + #elif defined(TEST_FUZZ3) + /* fuzz target 3: different responses for HTTP/1 client */ + LLVMFuzzerTestOneInput_RESPONSE(data, size); +- return 0; ++ return 0; + #elif defined(TEST_FUZZ4) + #error "Only useful in HTTP/2 feature branch" + /* fuzz target 4: different requests for HTTP/2 server */ + LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size); +- return 0; ++ return 0; + #elif defined(TEST_FUZZ5) + /* fuzz target 5: calling an internal server test function, + * bypassing network sockets */ + LLVMFuzzerTestOneInput_process_new_connection(data, size); +- return 0; ++ return 0; + #else + /* planned targets */ + #error "Unknown fuzz target" +-- +2.34.1 + + +From b2c1d1bee777a13a7feb8a8d123b6552e3bd9fa7 Mon Sep 17 00:00:00 2001 +From: phi-go +Date: Thu, 23 Jan 2025 11:57:59 +0100 +Subject: [PATCH 122/173] use LLVMFuzzerInitialize + +--- + fuzztest/fuzzmain.c | 45 ++++++++++++++++++++++++--------------------- + 1 file changed, 24 insertions(+), 21 deletions(-) + +diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c +index 89424656..df26768c 100644 +--- a/fuzztest/fuzzmain.c ++++ b/fuzztest/fuzzmain.c +@@ -45,9 +45,6 @@ unsigned short PORT_NUM_HTTP = 0; /* set dynamically */ + } + + +-static uint64_t call_count = 0; +- +- + /********************************************************/ + /* Init CivetWeb server ... test with mock client */ + /********************************************************/ +@@ -110,6 +107,17 @@ civetweb_init(void) + atexit(civetweb_exit); + } + ++int LLVMFuzzerInitialize(int *argc, char ***argv); ++ ++int ++LLVMFuzzerInitialize(int *argc, char ***argv) { ++ // Silence unused args warning. ++ (void)(argc); ++ (void)(argv); ++ ++ civetweb_init(); ++ return 0; ++} + + #if defined(TEST_FUZZ1) + static int +@@ -202,19 +210,12 @@ test_civetweb_client(const char *server, + return 0; + } + +- + static int + LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size) + { + static char URI[1024 * 64]; /* static, to avoid stack overflow */ + +- if (call_count == 0) { +- memset(URI, 0, sizeof(URI)); +- civetweb_init(); +- } +- call_count++; +- +- if (size < sizeof(URI)) { ++ if (size+1 < sizeof(URI)) { + memcpy(URI, data, size); + URI[size] = 0; + } else { +@@ -230,11 +231,6 @@ LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size) + static int + LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size) + { +- if (call_count == 0) { +- civetweb_init(); +- } +- call_count++; +- + int r; + SOCKET sock = socket(AF_INET, SOCK_STREAM, 6); + if (sock == -1) { +@@ -446,15 +442,22 @@ mock_server_init(void) + atexit(mock_server_exit); + } + ++int LLVMFuzzerInitialize(int *argc, char ***argv); ++ ++int ++LLVMFuzzerInitialize(int *argc, char ***argv) { ++ // Silence unused args warning. ++ (void)(argc); ++ (void)(argv); ++ ++ mock_server_init(); ++ return 0; ++} ++ + + static int + LLVMFuzzerTestOneInput_RESPONSE(const uint8_t *data, size_t size) + { +- if (call_count == 0) { +- mock_server_init(); +- } +- call_count++; +- + if (size > sizeof(RESPONSE.data)) { + return 1; + } +-- +2.34.1 + + +From dfdb047498dd4c79aa5f7b1cc2d6fef0c5b26707 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Mon, 10 Feb 2025 18:21:51 +0100 +Subject: [PATCH 123/173] Expose if server ports are bound + +Signed-off-by: DL6ER +--- + docs/api/mg_server_port.md | 4 ++-- + include/civetweb.h | 2 +- + src/civetweb.c | 1 + + unittest/public_server.c | 12 ++++++++++++ + 4 files changed, 16 insertions(+), 3 deletions(-) + +diff --git a/docs/api/mg_server_port.md b/docs/api/mg_server_port.md +index 01c9c42c..0159be8d 100644 +--- a/docs/api/mg_server_port.md ++++ b/docs/api/mg_server_port.md +@@ -10,8 +10,8 @@ + |**`port`**|`int`|The port number on which the service listens| + |**`is_ssl`**|`int`|**0** for `HTTP` communication, **1** for `HTTPS`| + |**`is_redirect`**|`int`|**1** if all requests are redirected, otherwise **0**| +-|**`is_optional`**|`int`|**1** if prot is optional, otherwise **0**| +-|**`_reserved2`**|`int`|Reserved for internal use| ++|**`is_optional`**|`int`|**1** if port is optional, otherwise **0**| ++|**`is_bound`**|`int`|**1** if the port is bound, otherwise **0**| + |**`_reserved3`**|`int`|Reserved for internal use| + |**`_reserved4`**|`int`|Reserved for internal use| + +diff --git a/include/civetweb.h b/include/civetweb.h +index 0b72eaf3..0ce344dc 100644 +--- a/include/civetweb.h ++++ b/include/civetweb.h +@@ -715,7 +715,7 @@ struct mg_server_port { + int is_ssl; /* https port: 0 = no, 1 = yes */ + int is_redirect; /* redirect all requests: 0 = no, 1 = yes */ + int is_optional; /* optional: 0 = no, 1 = yes */ +- int _reserved2; ++ int is_bound; /* bound: 0 = no, 1 = yes, relevant for optional ports */ + int _reserved3; + int _reserved4; + }; +diff --git a/src/civetweb.c b/src/civetweb.c +index 9f9ef2e8..2ee166cf 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -3338,6 +3338,7 @@ mg_get_server_ports(const struct mg_context *ctx, + ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl; + ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir; + ports[cnt].is_optional = ctx->listening_sockets[i].is_optional; ++ ports[cnt].is_bound = ctx->listening_sockets[i].sock != INVALID_SOCKET; + + if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) { + /* IPv4 */ +diff --git a/unittest/public_server.c b/unittest/public_server.c +index bad2b77f..3ea5c45f 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -458,11 +458,13 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); + ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 0); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); + ck_assert_int_eq(portinfo[1].is_optional, 0); ++ ck_assert_int_eq(portinfo[1].is_bound, 0); + + ret = mg_get_server_ports(ctx, 4, portinfo); + ck_assert_int_eq(ret, 1); +@@ -475,11 +477,13 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); + ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 1); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); + ck_assert_int_eq(portinfo[1].is_optional, 0); ++ ck_assert_int_eq(portinfo[1].is_bound, 0); + + test_sleep(1); + +@@ -679,11 +683,13 @@ START_TEST(test_mg_start_stop_https_server) + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); + ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 0); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); + ck_assert_int_eq(portinfo[1].is_optional, 0); ++ ck_assert_int_eq(portinfo[1].is_bound, 0); + + ret = mg_get_server_ports(ctx, 4, portinfo); + ck_assert_int_eq(ret, 2); +@@ -692,16 +698,19 @@ START_TEST(test_mg_start_stop_https_server) + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 1); + ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 1); + ck_assert_int_eq(portinfo[1].protocol, 1); + ck_assert_int_eq(portinfo[1].port, 8443); + ck_assert_int_eq(portinfo[1].is_ssl, 1); + ck_assert_int_eq(portinfo[1].is_redirect, 0); + ck_assert_int_eq(portinfo[1].is_optional, 1); ++ ck_assert_int_eq(portinfo[1].is_bound, 1); + ck_assert_int_eq(portinfo[2].protocol, 0); + ck_assert_int_eq(portinfo[2].port, 0); + ck_assert_int_eq(portinfo[2].is_ssl, 0); + ck_assert_int_eq(portinfo[2].is_redirect, 0); + ck_assert_int_eq(portinfo[2].is_optional, 0); ++ ck_assert_int_eq(portinfo[2].is_bound, 0); + + test_sleep(1); + +@@ -810,16 +819,19 @@ START_TEST(test_mg_server_and_client_tls) + ck_assert_int_eq(ports[0].is_ssl, 0); + ck_assert_int_eq(ports[0].is_redirect, 1); + ck_assert_int_eq(ports[0].is_optional, 0); ++ ck_assert_int_eq(ports[0].is_bound, 1); + ck_assert_int_eq(ports[1].protocol, 1); + ck_assert_int_eq(ports[1].port, 8443); + ck_assert_int_eq(ports[1].is_ssl, 1); + ck_assert_int_eq(ports[1].is_redirect, 0); + ck_assert_int_eq(ports[1].is_optional, 1); ++ ck_assert_int_eq(ports[1].is_bound, 1); + ck_assert_int_eq(ports[2].protocol, 0); + ck_assert_int_eq(ports[2].port, 0); + ck_assert_int_eq(ports[2].is_ssl, 0); + ck_assert_int_eq(ports[2].is_redirect, 0); + ck_assert_int_eq(ports[2].is_optional, 0); ++ ck_assert_int_eq(ports[2].is_bound, 0); + + test_sleep(1); + +-- +2.34.1 + + +From f4d9bf25f10bf4cea4a4d345a3afc6ce92039ef2 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Sat, 1 Mar 2025 11:50:54 +0100 +Subject: [PATCH 124/173] Terminate master_thread early when there are no + listening sockets to prevent CivetWeb from crashing + +Signed-off-by: DL6ER +--- + src/civetweb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 2ee166cf..edfbfd3b 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -20542,7 +20542,7 @@ master_thread_run(struct mg_context *ctx) + unsigned int i; + unsigned int workerthreadcount; + +- if (!ctx) { ++ if (!ctx || !ctx->listening_socket_fds) { + return; + } + +-- +2.34.1 + + +From d92557b845252f22e91cb33e74323693040df80c Mon Sep 17 00:00:00 2001 +From: Abra Mixabra <46976171+abra-mixabra@users.noreply.github.com> +Date: Sun, 2 Mar 2025 18:44:35 +0000 +Subject: [PATCH 125/173] Add X509 certificate and CRL MIME-types + +Add the following MIME types: +- application/pkix-cert +- application/pkix-crl +- application/x-pem-file +--- + src/civetweb.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 2ee166cf..30bf430c 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -8511,6 +8511,9 @@ static const struct { + * (http://www.iana.org/assignments/media-types) + * application types */ + {".bin", 4, "application/octet-stream"}, ++ {".cer", 4, "application/pkix-cert"}, ++ {".crl", 4, "application/pkix-crl"}, ++ {".crt", 4, "application/pkix-cert"}, + {".deb", 4, "application/octet-stream"}, + {".dmg", 4, "application/octet-stream"}, + {".dll", 4, "application/octet-stream"}, +@@ -8522,6 +8525,7 @@ static const struct { + {".json", 5, "application/json"}, + {".mjs", 4, "application/javascript"}, + {".msi", 4, "application/octet-stream"}, ++ {".pem", 4, "application/x-pem-file"}, + {".pdf", 4, "application/pdf"}, + {".ps", 3, "application/postscript"}, + {".rtf", 4, "application/rtf"}, +-- +2.34.1 + + +From ae42b3a7342adb957175bb227a278c1cce4d45db Mon Sep 17 00:00:00 2001 +From: stevenwdv +Date: Thu, 6 Mar 2025 11:53:40 +0100 +Subject: [PATCH 126/173] Use `stderr` for `DEBUG_TRACE_STREAM` + +Closes #1330 +--- + src/civetweb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 2ee166cf..c6366547 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -239,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func, + + #define NEED_DEBUG_TRACE_FUNC + #if !defined(DEBUG_TRACE_STREAM) +-#define DEBUG_TRACE_STREAM stdout ++#define DEBUG_TRACE_STREAM stderr + #endif + + #else +-- +2.34.1 + + +From dc375064b36fbf6ec9d29ce7c0c5005f4d3cf543 Mon Sep 17 00:00:00 2001 +From: stevenwdv +Date: Thu, 6 Mar 2025 14:11:14 +0100 +Subject: [PATCH 127/173] Print to stderr instead of stdout in + mg_cry_internal_impl + +--- + src/civetweb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index c6366547..64f58ece 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -3480,7 +3480,7 @@ mg_cry_internal_impl(const struct mg_connection *conn, + DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf); + + if (!conn) { +- puts(buf); ++ fputs(buf, stderr); + return; + } + +-- +2.34.1 + + +From 267a58096be0bb547e158949cef321942111604f Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Mon, 10 Mar 2025 19:11:43 +0100 +Subject: [PATCH 128/173] Tolerate socket creation error if port is optional + +When users specify `[::]:80o` as port but the system has IPv6 disabled globally, e.g., with +``` +GRUB_CMDLINE_LINUX="ipv6.disable=1" +``` +then CivetWeb refuses to start even though it should skip the port if it cannot bind to it. However, when the port is specified as being optional, there is no point in treating non-availability of the protocol differently than the port already being taken. In this case, this port should simply be skipped. + +Signed-off-by: DL6ER +--- + src/civetweb.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 2ee166cf..be598023 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -16154,6 +16154,10 @@ set_ports_option(struct mg_context *phys_ctx) + mg_cry_ctx_internal(phys_ctx, + "cannot create socket (entry %i)", + portsTotal); ++ if (so.is_optional) { ++ portsOk++; /* it's okay if we couldn't create a socket, ++ this port is optional anyway */ ++ } + continue; + } + +@@ -16239,6 +16243,10 @@ set_ports_option(struct mg_context *phys_ctx) + #else + mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available"); + closesocket(so.sock); ++ if (so.is_optional) { ++ portsOk++; /* it's okay if we couldn't set the socket option, ++ this port is optional anyway */ ++ } + so.sock = INVALID_SOCKET; + continue; + #endif +-- +2.34.1 + + +From 19bd43a7e17a50c26aa26d8d95d16b2087715d5f Mon Sep 17 00:00:00 2001 +From: dmdmdm <3187057+dmdmdm@users.noreply.github.com> +Date: Thu, 3 Apr 2025 10:07:59 -0400 +Subject: [PATCH 129/173] Add prefix MG_ in a few places + +--- + docs/api/mg_form_data_handler.md | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/docs/api/mg_form_data_handler.md b/docs/api/mg_form_data_handler.md +index f9d1f43a..8f477b0d 100644 +--- a/docs/api/mg_form_data_handler.md ++++ b/docs/api/mg_form_data_handler.md +@@ -10,14 +10,14 @@ + ||The callback function `field_found()` is called when a new field has been found. The return value of this callback is used to define how the field should be processed. The parameters contain the following information:| + ||**`key`** - The name of the field as it was named with the `name` tag in the HTML source.| + ||**`filename`** - The name of the file to upload. Please note that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.| +-||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.| ++||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `MG_FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.| + ||**`pathlen`** - The length of the buffer where the output path can be stored.| + ||**`user_data`** - A pointer to the value of the field `user_data` of the structure `struct mg_form_data_handler`.| + ||The callback function `field_found()` can return the following values back to Civetweb:| +-||**`FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field| +-||**`FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data| +-||**`FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists| +-||**`FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields| ++||**`MG_FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field| ++||**`MG_FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data| ++||**`MG_FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists| ++||**`MG_FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields| + |**`field_get`**|**`int field_get( const char *key, const char *value, size_t valuelen, void *user_data );`**| + ||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_GET`, Civetweb will call `field_get()` one or more times to pass back the data for this field.| + ||**`key`** - the name of the field being decoded, note this is only passed on the first call for file parameters (bug?)| +@@ -28,7 +28,7 @@ + ||**`return` `MG_FORM_FIELD_HANDLE_NEXT`** - to skip further calls to get for this field and move on to the next field. | + ||**`return` `MG_FORM_FIELD_HANDLE_ABORT`** - to stop parsing this form and abandon the search for further fields. | + |**`field_store`**|**`int field_store( const char *path, long long file_size, void *user_data );`**| +-||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call| ++||If the callback function `field_found()` returned `MG_FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call| + ||**`path`** - The path on the server where the file was stored| + ||**`file_size`** - The size of the stored file in bytes| + ||**`user_data`** - The value of the field `user_data` when the callback functions were registered with a call to `mg_handle_form_request();`| +-- +2.34.1 + + +From 547504fd12c615f9a39ac446ebafba5e1e7b2717 Mon Sep 17 00:00:00 2001 +From: dmdmdm <3187057+dmdmdm@users.noreply.github.com> +Date: Mon, 7 Apr 2025 11:52:03 -0400 +Subject: [PATCH 130/173] Added MG_ prefix in a few places + +--- + include/civetweb.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/include/civetweb.h b/include/civetweb.h +index 0ce344dc..2665d646 100644 +--- a/include/civetweb.h ++++ b/include/civetweb.h +@@ -1208,7 +1208,7 @@ struct mg_form_data_handler { + * filename: Name of a file to upload, at the client computer. + * Only set for input fields of type "file", otherwise NULL. + * path: Output parameter: File name (incl. path) to store the file +- * at the server computer. Only used if FORM_FIELD_STORAGE_STORE ++ * at the server computer. Only used if MG_FORM_FIELD_STORAGE_STORE + * is returned by this callback. Existing files will be + * overwritten. + * pathlen: Length of the buffer for path. +@@ -1216,7 +1216,7 @@ struct mg_form_data_handler { + * + * Return value: + * The callback must return the intended storage for this field +- * (See FORM_FIELD_STORAGE_*). ++ * (See MG_FORM_FIELD_STORAGE_*). + */ + int (*field_found)(const char *key, + const char *filename, +@@ -1224,7 +1224,7 @@ struct mg_form_data_handler { + size_t pathlen, + void *user_data); + +- /* If the "field_found" callback returned FORM_FIELD_STORAGE_GET, ++ /* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_GET, + * this callback will receive the field data. + * + * Parameters: +@@ -1241,7 +1241,7 @@ struct mg_form_data_handler { + size_t valuelen, + void *user_data); + +- /* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE, ++ /* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_STORE, + * the data will be stored into a file. If the file has been written + * successfully, this callback will be called. This callback will + * not be called for only partially uploaded files. The +-- +2.34.1 + + +From a87a7aaffd182ee5b5f158f74f5825728e162306 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Fri, 18 Apr 2025 10:54:32 +0200 +Subject: [PATCH 131/173] Modify STOP_FLAG_ASSING to make TSAN happy (#1333) + +--- + CREDITS.md | 1 + + src/civetweb.c | 4 +++- + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/CREDITS.md b/CREDITS.md +index f1e1d150..f6a2a79d 100644 +--- a/CREDITS.md ++++ b/CREDITS.md +@@ -130,6 +130,7 @@ + * Lammert Bies + * Lars Immisch + * Lawrence ++* Lev275568 + * Li Peng + * Lianghui + * Lorenzo Canepa +diff --git a/src/civetweb.c b/src/civetweb.c +index 9e098045..e09bcf3a 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -2339,7 +2339,9 @@ STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v) + { + stop_flag_t sf = 0; + do { +- sf = mg_atomic_compare_and_swap(f, *f, v); ++ sf = mg_atomic_compare_and_swap(f, ++ __atomic_load_n(f, __ATOMIC_SEQ_CST), ++ v); + } while (sf != v); + } + +-- +2.34.1 + + +From d6110adc811e2d2384de477c6b402aa7f716e94d Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Fri, 18 Apr 2025 10:55:36 +0200 +Subject: [PATCH 132/173] Auto-format code + +--- + fuzztest/fuzzmain.c | 16 +++--- + src/civetweb.c | 33 ++++++++---- + src/handle_form.inl | 3 +- + src/mod_gnutls.inl | 6 +-- + src/mod_lua.inl | 24 +++++---- + src/mod_mbedtls.inl | 3 +- + unittest/public_server.c | 107 +++++++++++++++++++-------------------- + 7 files changed, 103 insertions(+), 89 deletions(-) + +diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c +index df26768c..c7e27943 100644 +--- a/fuzztest/fuzzmain.c ++++ b/fuzztest/fuzzmain.c +@@ -110,13 +110,14 @@ civetweb_init(void) + int LLVMFuzzerInitialize(int *argc, char ***argv); + + int +-LLVMFuzzerInitialize(int *argc, char ***argv) { +- // Silence unused args warning. ++LLVMFuzzerInitialize(int *argc, char ***argv) ++{ ++ // Silence unused args warning. + (void)(argc); + (void)(argv); + + civetweb_init(); +- return 0; ++ return 0; + } + + #if defined(TEST_FUZZ1) +@@ -215,7 +216,7 @@ LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size) + { + static char URI[1024 * 64]; /* static, to avoid stack overflow */ + +- if (size+1 < sizeof(URI)) { ++ if (size + 1 < sizeof(URI)) { + memcpy(URI, data, size); + URI[size] = 0; + } else { +@@ -445,13 +446,14 @@ mock_server_init(void) + int LLVMFuzzerInitialize(int *argc, char ***argv); + + int +-LLVMFuzzerInitialize(int *argc, char ***argv) { +- // Silence unused args warning. ++LLVMFuzzerInitialize(int *argc, char ***argv) ++{ ++ // Silence unused args warning. + (void)(argc); + (void)(argv); + + mock_server_init(); +- return 0; ++ return 0; + } + + +diff --git a/src/civetweb.c b/src/civetweb.c +index 37763ff1..dc2acd81 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1595,7 +1595,7 @@ static int mg_openssl_initialized = 0; + #endif + #if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1) \ + && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS) \ +- && !defined(USE_GNUTLS) ++ && !defined(USE_GNUTLS) + #error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS" + #endif + #if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1) +@@ -6170,9 +6170,12 @@ push_inner(struct mg_context *ctx, + } else + #elif defined(USE_GNUTLS) + if (ssl != NULL) { +- n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t) len); ++ n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t)len); + if (n < 0) { +- fprintf(stderr, "SSL write failed (%d): %s", n, gnutls_strerror(n)); ++ fprintf(stderr, ++ "SSL write failed (%d): %s", ++ n, ++ gnutls_strerror(n)); + return -2; + } else { + err = 0; +@@ -6477,7 +6480,10 @@ pull_inner(FILE *fp, + if (pollres > 0) { + nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read); + if (nread < 0) { +- fprintf(stderr, "SSL read failed (%d): %s", nread, gnutls_strerror(nread)); ++ fprintf(stderr, ++ "SSL read failed (%d): %s", ++ nread, ++ gnutls_strerror(nread)); + return -2; + } else { + err = 0; +@@ -9632,7 +9638,8 @@ connect_socket( + return 0; + } + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) && !defined(NO_SSL_DL) ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) \ ++ && !defined(NO_SSL_DL) + #if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) + if (use_ssl && (TLS_client_method == NULL)) { + if (error != NULL) { +@@ -16158,7 +16165,7 @@ set_ports_option(struct mg_context *phys_ctx) + portsTotal); + if (so.is_optional) { + portsOk++; /* it's okay if we couldn't create a socket, +- this port is optional anyway */ ++ this port is optional anyway */ + } + continue; + } +@@ -18399,7 +18406,8 @@ mg_close_connection(struct mg_connection *conn) + + close_connection(conn); + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ ++ && !defined(USE_GNUTLS) // TODO: mbedTLS client + if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) + || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT)) + && (conn->phys_ctx->dd.ssl_ctx != NULL)) { +@@ -18501,7 +18509,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options, + return NULL; + } + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ ++ && !defined(USE_GNUTLS) // TODO: mbedTLS client + #if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \ + && !defined(NO_SSL_DL) + +@@ -18578,7 +18587,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options, + error->text_buffer_size, + "Can not create mutex"); + } +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ ++ && !defined(USE_GNUTLS) // TODO: mbedTLS client + SSL_CTX_free(conn->dom_ctx->ssl_ctx); + #endif + closesocket(sock); +@@ -18586,7 +18596,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options, + return NULL; + } + +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \ ++ && !defined(USE_GNUTLS) // TODO: mbedTLS client + if (use_ssl) { + /* TODO: Check ssl_verify_peer and ssl_ca_path here. + * SSL_CTX_set_verify call is needed to switch off server +@@ -22257,7 +22268,7 @@ mg_get_system_info(char *buffer, int buflen) + sizeof(block), + ",%s\"os\" : \"%s %s\"", + eol, +- "RTEMS", ++ "RTEMS", + rtems_version()); + system_info_length += mg_str_append(&buffer, end, block); + #elif defined(__ZEPHYR__) +diff --git a/src/handle_form.inl b/src/handle_form.inl +index a7b7fc10..14235ec9 100644 +--- a/src/handle_form.inl ++++ b/src/handle_form.inl +@@ -757,8 +757,7 @@ mg_handle_form_request(struct mg_connection *conn, + + /* after the transport padding, if the boundary isn't + * immediately followed by a \r\n then it is either... */ +- if (strncmp(boundary_end, "\r\n", 2)) +- { ++ if (strncmp(boundary_end, "\r\n", 2)) { + /* ...the final boundary, and it is followed by --, (in which + * case it's the end of the request) or it's a malformed + * request */ +diff --git a/src/mod_gnutls.inl b/src/mod_gnutls.inl +index b4ca5d47..f765362b 100644 +--- a/src/mod_gnutls.inl ++++ b/src/mod_gnutls.inl +@@ -17,9 +17,9 @@ CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt); + CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx); + CIVETWEB_API void gtls_ssl_close(SSL *ssl); + CIVETWEB_API int gtls_ssl_accept(SSL **ssl, +- SSL_CTX *ssl_ctx, +- int sock, +- struct mg_context *phys_ctx); ++ SSL_CTX *ssl_ctx, ++ int sock, ++ struct mg_context *phys_ctx); + CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len); + CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len); + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index e97a28cc..2a1d3a57 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -11,8 +11,7 @@ + #include "civetweb_private_lua.h" + + /* Prototypes */ +-static int +-lua_error_handler(lua_State *L); ++static int lua_error_handler(lua_State *L); + + #if defined(_WIN32) + static void * +@@ -648,14 +647,19 @@ run_lsp_kepler(struct mg_connection *conn, + /* Initialize a new HTTP response, either with some-predefined + * status code (e.g. 404 if this is called from an error + * handler) or with 200 OK */ +- mg_response_header_start(conn, conn->status_code > 0 ? conn->status_code : 200); ++ mg_response_header_start(conn, ++ conn->status_code > 0 ? conn->status_code ++ : 200); + + /* Add additional headers */ + send_no_cache_header(conn); + send_additional_header(conn); + + /* Add content type */ +- mg_response_header_add(conn, "Content-Type", "text/html; charset=utf-8", -1); ++ mg_response_header_add(conn, ++ "Content-Type", ++ "text/html; charset=utf-8", ++ -1); + + /* Send the HTTP response (status and all headers) */ + mg_response_header_send(conn); +@@ -679,8 +683,7 @@ run_lsp_kepler(struct mg_connection *conn, + } else { + /* Success loading chunk. Call it. */ + lua_ok = lua_pcall(L, 0, 0, 0); +- if(lua_ok != LUA_OK) +- { ++ if (lua_ok != LUA_OK) { + lua_cry(conn, lua_ok, L, "LSP Kepler", "call"); + lua_error_handler(L); + return 1; +@@ -804,8 +807,7 @@ run_lsp_civetweb(struct mg_connection *conn, + } else { + /* Success loading chunk. Call it. */ + lua_ok = lua_pcall(L, 0, 0, 0); +- if(lua_ok != LUA_OK) +- { ++ if (lua_ok != LUA_OK) { + lua_cry(conn, lua_ok, L, "LSP", "call"); + lua_error_handler(L); + return 1; +@@ -2801,7 +2803,7 @@ lua_error_handler(lua_State *L) + lua_pushstring(L, error_msg); + lua_pushliteral(L, "\n"); + lua_call(L, 2, 0); /* call mg.write(error_msg + \n) */ +- lua_pop(L, 1); /* pop mg */ ++ lua_pop(L, 1); /* pop mg */ + + /* Get Lua traceback */ + lua_getglobal(L, "debug"); +@@ -2817,8 +2819,8 @@ lua_error_handler(lua_State *L) + /* Only print the traceback if it is not empty */ + if (strcmp(lua_tostring(L, -1), "stack traceback:") != 0) { + lua_pushliteral(L, "\n"); /* append a newline */ +- lua_call(L, 2, 0); /* call mg.write(traceback + \n) */ +- lua_pop(L, 2); /* pop mg and traceback */ ++ lua_call(L, 2, 0); /* call mg.write(traceback + \n) */ ++ lua_pop(L, 2); /* pop mg and traceback */ + } else { + lua_pop(L, 3); /* pop mg, traceback and error message */ + } +diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl +index 93f1bb29..03d189ea 100644 +--- a/src/mod_mbedtls.inl ++++ b/src/mod_mbedtls.inl +@@ -95,7 +95,8 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + */ + const psa_status_t status = psa_crypto_init(); + if (status != PSA_SUCCESS) { +- DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n", (int) status); ++ DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n", ++ (int)status); + return -1; + } + #endif +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 3ea5c45f..27a3eb9f 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -847,7 +847,7 @@ START_TEST(test_mg_server_and_client_tls) + * while Ubuntu Xenial, Ubuntu Trusty and Windows test containers at + * Travis CI do not. Maybe it is OpenSSL version specific. + */ +-#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) ++#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) + if (client_conn) { + /* Connect succeeds, but the connection is unusable. */ + mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n"); +@@ -3091,7 +3091,7 @@ START_TEST(test_handle_form) + + /* Handle form: "POST multipart/form-data" without trailing CRLF*/ + multipart_body = +- "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" + "Content-Disposition: form-data; name=\"textin\"\r\n" + "\r\n" + "text\r\n" +@@ -3629,11 +3629,11 @@ START_TEST(test_handle_form) + multipart_body = + "--multipart-form-data-boundary--see-RFC-2388\r\n" + "Content-Disposition: form-data; " +- "custom1name=\"1\"; " +- "custom2name=\"2\"; " +- "custom3name=\"3\"; " +- "custom4name=\"4\"; " +- "name=\"textin\"\r\n" ++ "custom1name=\"1\"; " ++ "custom2name=\"2\"; " ++ "custom3name=\"3\"; " ++ "custom4name=\"4\"; " ++ "name=\"textin\"\r\n" + "\r\n" + "text\r\n" + "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" +@@ -3791,53 +3791,52 @@ START_TEST(test_handle_form) + mg_close_connection(client_conn); + + /* Handle form: "POST multipart/form-data" very long preamble */ +- multipart_body = +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "preamblepreamblepreamblepreamblepreamble\r\n" +- "--multipart-form-data-boundary--see-RFC-2388\r\n"; +- "Content-Disposition: form-data; name=\"passwordin\"\r\n" +- "\r\n" +- "\r\n" +- "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ multipart_body = "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n"; ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; + + body_len = strlen(multipart_body); + ck_assert_uint_eq(body_len, 1768); /* not required */ +-- +2.34.1 + + +From 0c2dc8f5dfb2d0e2069cba9f73d92b833f243ef7 Mon Sep 17 00:00:00 2001 +From: Tim Lebedkov +Date: Thu, 1 May 2025 13:19:56 +0200 +Subject: [PATCH 133/173] Use the Markdown syntax + +--- + docs/OpenSSL.md | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/docs/OpenSSL.md b/docs/OpenSSL.md +index f37c6b38..657a6c35 100644 +--- a/docs/OpenSSL.md ++++ b/docs/OpenSSL.md +@@ -1,5 +1,4 @@ +-Adding OpenSSL Support +-===== ++# Adding OpenSSL Support + + Civetweb supports *HTTPS* connections using the OpenSSL transport layer + security (TLS) library. OpenSSL is a free, open source library (see +-- +2.34.1 + + +From 7cdc05292211699d410e99d74722fba2eac9b280 Mon Sep 17 00:00:00 2001 +From: Tim Lebedkov +Date: Thu, 1 May 2025 13:21:40 +0200 +Subject: [PATCH 134/173] Use the Markdown syntax + +--- + docs/gnutls.md | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/docs/gnutls.md b/docs/gnutls.md +index c247850f..0c4ded53 100644 +--- a/docs/gnutls.md ++++ b/docs/gnutls.md +@@ -1,5 +1,4 @@ +-#### Use GnuTLS instead of OpenSSL +-===== ++# Use GnuTLS instead of OpenSSL + + 1 [Build libgmp](https://gmplib.org) + +-- +2.34.1 + + +From a91d8caf6392bd7effc85f190bb760fe1283ec3f Mon Sep 17 00:00:00 2001 +From: yubiuser +Date: Sat, 3 May 2025 21:22:23 +0200 +Subject: [PATCH 135/173] Prevent CRLF injection attempts + +Signed-off-by: yubiuser +--- + src/civetweb.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 3a87a4e5..27f9b50e 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -18858,6 +18858,19 @@ get_uri_type(const char *uri) + * and % encoded symbols. + */ + for (i = 0; uri[i] != 0; i++) { ++ /* Check for CRLF injection attempts */ ++ if (uri[i] == '%') { ++ if (uri[i+1] == '0' && (uri[i+2] == 'd' || uri[i+2] == 'D')) { ++ /* Found %0d (CR) */ ++ DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri); ++ return 0; ++ } ++ if (uri[i+1] == '0' && (uri[i+2] == 'a' || uri[i+2] == 'A')) { ++ /* Found %0a (LF) */ ++ DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri); ++ return 0; ++ } ++ } + if ((unsigned char)uri[i] < 33) { + /* control characters and spaces are invalid */ + return 0; +-- +2.34.1 + + +From 76e222bcb77ba8452e5da4e82ae6cecd499c25e0 Mon Sep 17 00:00:00 2001 +From: krispybyte +Date: Sat, 21 Jun 2025 23:33:50 +0300 +Subject: [PATCH 136/173] Fix heap overflow in directory URI slash redirection + +--- + src/civetweb.c | 23 ++++++++++++++++++----- + 1 file changed, 18 insertions(+), 5 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index bbc9aa8b..e969c939 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -15579,7 +15579,6 @@ handle_request(struct mg_connection *conn) + /* 12. Directory uris should end with a slash */ + if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0) + && (ri->local_uri[uri_len - 1] != '/')) { +- + /* Path + server root */ + size_t buflen = UTF8_PATH_MAX * 2 + 2; + char *new_path; +@@ -15592,12 +15591,26 @@ handle_request(struct mg_connection *conn) + mg_send_http_error(conn, 500, "out or memory"); + } else { + mg_get_request_link(conn, new_path, buflen - 1); +- strcat(new_path, "/"); ++ ++ size_t len = strlen(new_path); ++ if (len + 1 < buflen) { ++ new_path[len] = '/'; ++ new_path[len + 1] = '\0'; ++ len += 1; ++ } ++ + if (ri->query_string) { +- /* Append ? and query string */ +- strcat(new_path, "?"); +- strcat(new_path, ri->query_string); ++ if (len + 1 < buflen) { ++ new_path[len] = '?'; ++ new_path[len + 1] = '\0'; ++ len += 1; ++ } ++ ++ /* Append with size of space left for query string + null terminator */ ++ size_t max_append = buflen - len - 1; ++ strncat(new_path, ri->query_string, max_append); + } ++ + mg_send_http_redirect(conn, new_path, 301); + mg_free(new_path); + } +-- +2.34.1 + + +From d5321963b1d0bc953101de91f8588bf83db73bf5 Mon Sep 17 00:00:00 2001 +From: krispybyte +Date: Sun, 22 Jun 2025 00:23:06 +0300 +Subject: [PATCH 137/173] Fit code style + +--- + src/civetweb.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index e969c939..6af91f87 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -15596,14 +15596,14 @@ handle_request(struct mg_connection *conn) + if (len + 1 < buflen) { + new_path[len] = '/'; + new_path[len + 1] = '\0'; +- len += 1; ++ len++; + } + + if (ri->query_string) { + if (len + 1 < buflen) { + new_path[len] = '?'; + new_path[len + 1] = '\0'; +- len += 1; ++ len++; + } + + /* Append with size of space left for query string + null terminator */ +-- +2.34.1 + + +From e788cece2d5051c31af4d68c668bc425429eb195 Mon Sep 17 00:00:00 2001 +From: Geeoon Chung +Date: Mon, 7 Jul 2025 17:44:10 -0500 +Subject: [PATCH 138/173] Fixed typos where CivetWeb was spelled CiwetWeb + +--- + README.md | 2 +- + test/page4.lp | 4 ++-- + test/page4kepler.lp | 4 ++-- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/README.md b/README.md +index dae05879..c3e6f0ce 100644 +--- a/README.md ++++ b/README.md +@@ -182,7 +182,7 @@ CivetWeb is based on the [Mongoose project](https://github.com/cesanta/mongoose) + Sergey Lyubka(2004-2013) who released it under the MIT license. + However, on August 16, 2013, + [Mongoose was relicensed to a dual GPL V2 + commercial license](https://groups.google.com/forum/#!topic/mongoose-users/aafbOnHonkI) +-and CiwetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose". ++and CivetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose". + The license change and CivetWeb fork was mentioned on the Mongoose + [Wikipedia](https://en.wikipedia.org/wiki/Mongoose_(web_server)) + page as well, but it's getting deleted (and added again) there every +diff --git a/test/page4.lp b/test/page4.lp +index 52374907..7a4934e3 100644 +--- a/test/page4.lp ++++ b/test/page4.lp +@@ -22,10 +22,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr +

Tags

+ + +-<? greeting = 'CiwetWeb' ?>
++<? greeting = 'CivetWeb' ?>
+ <strong><?= greeting %></strong>
+

+- ++ + ==>
+ +
+diff --git a/test/page4kepler.lp b/test/page4kepler.lp +index 8a986b5d..a7054897 100644 +--- a/test/page4kepler.lp ++++ b/test/page4kepler.lp +@@ -19,10 +19,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr +

Tags

+ + +-<? greeting = 'CiwetWeb' ?>
++<? greeting = 'CivetWeb' ?>
+ <strong><?= greeting %></strong>
+

+- ++ + ==>
+ +
+-- +2.34.1 + + +From db3aebf784da75d8fd7fae8ddc5a03dc4a787ff0 Mon Sep 17 00:00:00 2001 +From: "Marcel F." +Date: Wed, 13 Aug 2025 10:46:10 +0200 +Subject: [PATCH 139/173] Add `replace_asterisk_with_origin` option for + Acces-Control-Allow-Origin + +If you have multiple allowed origins and require `allow_credentials` to be true, CORS might be a problem since it blocks all requests with Allow-Origin "*" in combination with Credentials. Therefore this feature adds a config paramter `replace_asterisk_with_origin`. If it is set to `yes` and the passed `Access-Control-Allow-Origin` is an asterisk, this will be replaced by the origin of the request in the corresponding response. +--- + src/civetweb.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index bbc9aa8b..90943bb1 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -239,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func, + + #define NEED_DEBUG_TRACE_FUNC + #if !defined(DEBUG_TRACE_STREAM) +-#define DEBUG_TRACE_STREAM stderr ++#define DEBUG_TRACE_STREAM stdout + #endif + + #else +@@ -2087,7 +2087,7 @@ enum { + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + LUA_WEBSOCKET_EXTENSIONS, + #endif +- ++ REPLACE_ASTERISK_WITH_ORIGIN, + ACCESS_CONTROL_ALLOW_ORIGIN, + ACCESS_CONTROL_ALLOW_METHODS, + ACCESS_CONTROL_ALLOW_HEADERS, +@@ -2146,7 +2146,6 @@ static const struct mg_option config_options[] = { + #if defined(USE_HTTP2) + {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"}, + #endif +- + /* Once for each domain */ + {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, + {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, +@@ -2253,6 +2252,7 @@ static const struct mg_option config_options[] = { + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, + #endif ++ {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"}, + {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, +@@ -3482,7 +3482,7 @@ mg_cry_internal_impl(const struct mg_connection *conn, + DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf); + + if (!conn) { +- fputs(buf, stderr); ++ puts(buf); + return; + } + +@@ -4235,15 +4235,24 @@ send_cors_header(struct mg_connection *conn) + const char *cors_meth_cfg = + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; + ++ int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); ++ + if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { + /* Cross-origin resource sharing (CORS), see + * http://www.html5rocks.com/en/tutorials/cors/, + * http://www.html5rocks.com/static/images/cors_server_flowchart.png + * CORS preflight is not supported for files. */ +- mg_response_header_add(conn, ++ if (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') { ++ mg_response_header_add(conn, ++ "Access-Control-Allow-Origin", ++ origin_hdr, ++ -1); ++ } else { ++ mg_response_header_add(conn, + "Access-Control-Allow-Origin", + cors_orig_cfg, + -1); ++ } + } + + if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) { +@@ -15159,6 +15168,7 @@ handle_request(struct mg_connection *conn) + const char *cors_acrm = get_header(ri->http_headers, + ri->num_headers, + "Access-Control-Request-Method"); ++ int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); + + /* Todo: check if cors_origin is in cors_orig_cfg. + * Or, let the client check this. */ +@@ -15186,7 +15196,7 @@ handle_request(struct mg_connection *conn) + "Content-Length: 0\r\n" + "Connection: %s\r\n", + date, +- cors_orig_cfg, ++ (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, + ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), + suggest_connection_header(conn)); + +-- +2.34.1 + + +From d24ca7d2d29ea3948ccb659dd20b5df95ccb4abd Mon Sep 17 00:00:00 2001 +From: "Marcel F." +Date: Wed, 13 Aug 2025 10:54:03 +0200 +Subject: [PATCH 140/173] Update civetweb.c + +Update to latest master +--- + src/civetweb.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 90943bb1..d11479f2 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -239,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func, + + #define NEED_DEBUG_TRACE_FUNC + #if !defined(DEBUG_TRACE_STREAM) +-#define DEBUG_TRACE_STREAM stdout ++#define DEBUG_TRACE_STREAM stderr + #endif + + #else +@@ -2146,6 +2146,7 @@ static const struct mg_option config_options[] = { + #if defined(USE_HTTP2) + {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"}, + #endif ++ + /* Once for each domain */ + {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, + {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, +@@ -3482,7 +3483,7 @@ mg_cry_internal_impl(const struct mg_connection *conn, + DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf); + + if (!conn) { +- puts(buf); ++ fputs(buf, stderr); + return; + } + +-- +2.34.1 + + +From eac68303f2473207ea67533e109450ec053e5859 Mon Sep 17 00:00:00 2001 +From: "Marcel F." +Date: Wed, 13 Aug 2025 12:38:07 +0200 +Subject: [PATCH 141/173] Fix names + +--- + src/civetweb.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index d11479f2..cffc7732 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -4235,15 +4235,15 @@ send_cors_header(struct mg_connection *conn) + conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; + const char *cors_meth_cfg = + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; +- +- int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); + + if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { ++ int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); ++ + /* Cross-origin resource sharing (CORS), see + * http://www.html5rocks.com/en/tutorials/cors/, + * http://www.html5rocks.com/static/images/cors_server_flowchart.png + * CORS preflight is not supported for files. */ +- if (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') { ++ if (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { + mg_response_header_add(conn, + "Access-Control-Allow-Origin", + origin_hdr, +@@ -15169,14 +15169,14 @@ handle_request(struct mg_connection *conn) + const char *cors_acrm = get_header(ri->http_headers, + ri->num_headers, + "Access-Control-Request-Method"); +- int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); +- + /* Todo: check if cors_origin is in cors_orig_cfg. + * Or, let the client check this. */ + + if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) + && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) + && (cors_origin != NULL) && (cors_acrm != NULL)) { ++ int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); ++ + /* This is a valid CORS preflight, and the server is configured + * to handle it automatically. */ + const char *cors_acrh = +@@ -15197,7 +15197,7 @@ handle_request(struct mg_connection *conn) + "Content-Length: 0\r\n" + "Connection: %s\r\n", + date, +- (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, ++ (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, + ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), + suggest_connection_header(conn)); + +-- +2.34.1 + + +From 1eee9d7e673763d849fddab8ca9bd172aceff9b3 Mon Sep 17 00:00:00 2001 +From: "Marcel F." +Date: Wed, 13 Aug 2025 12:39:28 +0200 +Subject: [PATCH 142/173] Fix names + +--- + src/civetweb.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index cffc7732..62c51ac3 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -4237,13 +4237,13 @@ send_cors_header(struct mg_connection *conn) + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; + + if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { +- int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); ++ int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); + + /* Cross-origin resource sharing (CORS), see + * http://www.html5rocks.com/en/tutorials/cors/, + * http://www.html5rocks.com/static/images/cors_server_flowchart.png + * CORS preflight is not supported for files. */ +- if (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { ++ if (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { + mg_response_header_add(conn, + "Access-Control-Allow-Origin", + origin_hdr, +@@ -15175,7 +15175,7 @@ handle_request(struct mg_connection *conn) + if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) + && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) + && (cors_origin != NULL) && (cors_acrm != NULL)) { +- int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); ++ int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); + + /* This is a valid CORS preflight, and the server is configured + * to handle it automatically. */ +@@ -15197,7 +15197,7 @@ handle_request(struct mg_connection *conn) + "Content-Length: 0\r\n" + "Connection: %s\r\n", + date, +- (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, ++ (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, + ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), + suggest_connection_header(conn)); + +-- +2.34.1 + + +From 0530ed0f2ac1cf9eb57b2caf8779cc5447097c94 Mon Sep 17 00:00:00 2001 +From: "Marcel F." +Date: Wed, 13 Aug 2025 13:56:32 +0200 +Subject: [PATCH 143/173] Update civetweb.c + +--- + src/civetweb.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 62c51ac3..cb2e9a17 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -4235,15 +4235,17 @@ send_cors_header(struct mg_connection *conn) + conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; + const char *cors_meth_cfg = + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; ++ const char *cors_repl_asterisk_with_orig_cfg = ++ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; + +- if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { +- int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); ++ if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr && cors_repl_asterisk_with_orig_cfg && *cors_repl_asterisk_with_orig_cfg) { ++ int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); + + /* Cross-origin resource sharing (CORS), see + * http://www.html5rocks.com/en/tutorials/cors/, + * http://www.html5rocks.com/static/images/cors_server_flowchart.png + * CORS preflight is not supported for files. */ +- if (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { ++ if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') { + mg_response_header_add(conn, + "Access-Control-Allow-Origin", + origin_hdr, +@@ -15169,13 +15171,17 @@ handle_request(struct mg_connection *conn) + const char *cors_acrm = get_header(ri->http_headers, + ri->num_headers, + "Access-Control-Request-Method"); ++ const char *cors_repl_asterisk_with_orig_cfg = ++ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; ++ + /* Todo: check if cors_origin is in cors_orig_cfg. + * Or, let the client check this. */ + + if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) + && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) +- && (cors_origin != NULL) && (cors_acrm != NULL)) { +- int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); ++ && (cors_origin != NULL) && (cors_acrm != NULL) ++ && (cors_repl_asterisk_with_orig_cfg != NULL) && (*cors_repl_asterisk_with_orig_cfg != 0)) { ++ int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); + + /* This is a valid CORS preflight, and the server is configured + * to handle it automatically. */ +@@ -15197,7 +15203,7 @@ handle_request(struct mg_connection *conn) + "Content-Length: 0\r\n" + "Connection: %s\r\n", + date, +- (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, ++ (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, + ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), + suggest_connection_header(conn)); + +-- +2.34.1 + + +From 15ada377340fdee385b27bfbe14b0bf9f6336385 Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sat, 16 Aug 2025 10:46:32 +0000 +Subject: [PATCH 144/173] Bump actions/checkout from 4 to 5 + +Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. +- [Release notes](https://github.com/actions/checkout/releases) +- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) +- [Commits](https://github.com/actions/checkout/compare/v4...v5) + +--- +updated-dependencies: +- dependency-name: actions/checkout + dependency-version: '5' + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/cibuild.yml | 2 +- + .github/workflows/codeql.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index d8e1cc6c..04773117 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -392,7 +392,7 @@ jobs: + + steps: + - name: Checkout code +- uses: actions/checkout@v4.1.7 ++ uses: actions/checkout@v5 + + - name: Export number of CPUs + run: | +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index d9f751b4..22fad79d 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -45,7 +45,7 @@ jobs: + + steps: + - name: Checkout repository +- uses: actions/checkout@v4 ++ uses: actions/checkout@v5 + with: + submodules: recursive + +-- +2.34.1 + + +From cfe55d75cca2adc8c4077b4d1ee24441eef201b0 Mon Sep 17 00:00:00 2001 +From: Aryan Rahar +Date: Thu, 21 Aug 2025 20:10:32 +0000 +Subject: [PATCH 145/173] civetweb: avoid duplicate uri_len computations in + handle_request + +- Compute URI length once after local_uri is finalized. +- Use cached value in directory trailing-slash check. +- Skip computing in NO_FILES builds. +- No functional change. +--- + src/civetweb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index bbc9aa8b..2e93236b 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -15079,7 +15079,7 @@ handle_request(struct mg_connection *conn) + } + return; + } +- uri_len = (int)strlen(ri->local_uri); ++ + + /* 1.3. decode url (if config says so) */ + if (should_decode_url(conn)) { +@@ -15107,6 +15107,12 @@ handle_request(struct mg_connection *conn) + } + remove_dot_segments(tmp); + ri->local_uri = tmp; ++ #if !defined(NO_FILES) /* Only compute if later code can actually use it */ ++ /* Cache URI length once; recompute only if the buffer changes later. */ ++ uri_len = (int)strlen(ri->local_uri); ++ #endif ++ ++ + + /* step 1. completed, the url is known now */ + DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri); +@@ -15577,8 +15583,8 @@ handle_request(struct mg_connection *conn) + } + + /* 12. Directory uris should end with a slash */ +- if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0) +- && (ri->local_uri[uri_len - 1] != '/')) { ++ if (file.stat.is_directory && (uri_len > 0) ++ && (ri->local_uri[uri_len - 1] != '/')) { + + /* Path + server root */ + size_t buflen = UTF8_PATH_MAX * 2 + 2; +-- +2.34.1 + + +From 76c9260abc25d5c04c9c14c6ecbfa80599892568 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Tue, 2 Sep 2025 10:22:22 +0200 +Subject: [PATCH 146/173] Fix: Server with multiple SSL certificates and + mg_set_request_handler() not working + +Fixes #1335 +--- + src/civetweb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index bbc9aa8b..0756b6d1 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -22020,7 +22020,7 @@ mg_start_domain2(struct mg_context *ctx, + } + } + +- new_dom->handlers = NULL; ++ new_dom->handlers = ctx->dd.handlers; + new_dom->next = NULL; + new_dom->nonce_count = 0; + new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31); +-- +2.34.1 + + +From 26aa1ecf6e4f38dfdac43a44ba486341b048ffc3 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Tue, 2 Sep 2025 11:09:31 +0200 +Subject: [PATCH 147/173] Fix If-None-Match precedence (fixes caching problems + for static content) + +Fixes #1342 +--- + src/civetweb.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 0756b6d1..33ab8bd7 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -10783,9 +10783,13 @@ is_not_modified(const struct mg_connection *conn, + const char *inm = mg_get_header(conn, "If-None-Match"); + construct_etag(etag, sizeof(etag), filestat); + +- return ((inm != NULL) && !mg_strcasecmp(etag, inm)) +- || ((ims != NULL) +- && (filestat->last_modified <= parse_date_string(ims))); ++ if (inm) { ++ return !mg_strcasecmp(etag, inm); ++ } ++ if (ims) { ++ return (filestat->last_modified <= parse_date_string(ims)); ++ } ++ return 0; + } + + +-- +2.34.1 + + +From 782e18903515f43bafbf2e668994e82bdfa51133 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Tue, 2 Sep 2025 14:08:41 +0200 +Subject: [PATCH 148/173] Make parsing of URL encoded forms more robust + +Reject requests that obviously violate the URL encoding. +Fixes #1348 +--- + src/civetweb.c | 7 ++++++- + src/handle_form.inl | 46 +++++++++++++++++++++++++++++++++++++-------- + 2 files changed, 44 insertions(+), 9 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 33ab8bd7..58e95c5f 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1,4 +1,4 @@ +-/* Copyright (c) 2013-2024 the Civetweb developers ++/* Copyright (c) 2013-2025 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy +@@ -7272,6 +7272,7 @@ mg_url_decode(const char *src, + int is_form_url_encoded) + { + int i, j, a, b; ++ + #define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W')) + + for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) { +@@ -7284,11 +7285,15 @@ mg_url_decode(const char *src, + i += 2; + } else if (is_form_url_encoded && (src[i] == '+')) { + dst[j] = ' '; ++ } else if ((unsigned char)src[i] <= ' ') { ++ return -1; /* invalid character */ + } else { + dst[j] = src[i]; + } + } + ++#undef HEXTOI ++ + dst[j] = '\0'; /* Null-terminate the destination */ + + return (i >= src_len) ? j : -1; +diff --git a/src/handle_form.inl b/src/handle_form.inl +index 14235ec9..cdff2a13 100644 +--- a/src/handle_form.inl ++++ b/src/handle_form.inl +@@ -1,4 +1,4 @@ +-/* Copyright (c) 2016-2021 the Civetweb developers ++/* Copyright (c) 2016-2025 the Civetweb developers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal +@@ -39,7 +39,7 @@ url_encoded_field_found(const struct mg_connection *conn, + mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); + + if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) { +- return MG_FORM_FIELD_STORAGE_SKIP; ++ return MG_FORM_FIELD_STORAGE_ABORT; + } + + if (filename) { +@@ -53,7 +53,7 @@ url_encoded_field_found(const struct mg_connection *conn, + || (filename_dec_len < 0)) { + /* Log error message and skip this field. */ + mg_cry_internal(conn, "%s: Cannot decode filename", __func__); +- return MG_FORM_FIELD_STORAGE_SKIP; ++ return MG_FORM_FIELD_STORAGE_ABORT; + } + remove_dot_segments(filename_dec); + +@@ -95,6 +95,7 @@ url_encoded_field_get( + struct mg_form_data_handler *fdh) + { + char key_dec[1024]; ++ int key_dec_len; + + char *value_dec = (char *)mg_malloc_ctx(*value_len + 1, conn->phys_ctx); + int value_dec_len, ret; +@@ -108,7 +109,8 @@ url_encoded_field_get( + return MG_FORM_FIELD_STORAGE_ABORT; + } + +- mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); ++ key_dec_len = mg_url_decode( ++ key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); + + if (*value_len >= 2 && value[*value_len - 2] == '%') + *value_len -= 2; +@@ -117,6 +119,11 @@ url_encoded_field_get( + value_dec_len = mg_url_decode( + value, (int)*value_len, value_dec, ((int)*value_len) + 1, 1); + ++ if ((key_dec_len < 0) || (value_dec_len < 0)) { ++ mg_free(value_dec); ++ return MG_FORM_FIELD_STORAGE_ABORT; ++ } ++ + ret = fdh->field_get(key_dec, + value_dec, + (size_t)value_dec_len, +@@ -136,9 +143,13 @@ unencoded_field_get(const struct mg_connection *conn, + struct mg_form_data_handler *fdh) + { + char key_dec[1024]; ++ int key_dec_len; + (void)conn; + +- mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); ++ key_dec_len = mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); ++ if (key_dec_len < 0) { ++ return MG_FORM_FIELD_STORAGE_ABORT; ++ } + + return fdh->field_get(key_dec, value, value_len, fdh->user_data); + } +@@ -191,6 +202,7 @@ mg_handle_form_request(struct mg_connection *conn, + size_t buf_fill = 0; + int r; + int field_count = 0; ++ int abort_read = 0; + struct mg_file fstore = STRUCT_FILE_INITIALIZER; + int64_t file_size = 0; /* init here, to a avoid a false positive + "uninitialized variable used" warning */ +@@ -281,6 +293,7 @@ mg_handle_form_request(struct mg_connection *conn, + conn, data, (size_t)keylen, val, (size_t *)&vallen, fdh); + if (r == MG_FORM_FIELD_HANDLE_ABORT) { + /* Stop request handling */ ++ abort_read = 1; + break; + } + if (r == MG_FORM_FIELD_HANDLE_NEXT) { +@@ -323,6 +336,7 @@ mg_handle_form_request(struct mg_connection *conn, + r = field_stored(conn, path, file_size, fdh); + if (r == MG_FORM_FIELD_HANDLE_ABORT) { + /* Stop request handling */ ++ abort_read = 1; + break; + } + +@@ -361,6 +375,7 @@ mg_handle_form_request(struct mg_connection *conn, + if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) + == MG_FORM_FIELD_STORAGE_ABORT) { + /* Stop parsing the request */ ++ abort_read = 1; + break; + } + +@@ -389,7 +404,7 @@ mg_handle_form_request(struct mg_connection *conn, + * Here we use "POST", and read the data from the request body. + * The data read on the fly, so it is not required to buffer the + * entire request in memory before processing it. */ +- for (;;) { ++ while (!abort_read) { + const char *val; + const char *next; + ptrdiff_t keylen, vallen; +@@ -443,6 +458,7 @@ mg_handle_form_request(struct mg_connection *conn, + if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) + == MG_FORM_FIELD_STORAGE_ABORT) { + /* Stop parsing the request */ ++ abort_read = 1; + break; + } + +@@ -471,6 +487,15 @@ mg_handle_form_request(struct mg_connection *conn, + } else { + vallen = (ptrdiff_t)strlen(val); + end_of_key_value_pair_found = all_data_read; ++ if ((buf + buf_fill) > (val + vallen)) { ++ /* Avoid DoS attacks by having a zero byte in the middle of ++ * a request that is supposed to be URL encoded. Since this ++ * request is certainly invalid, according to the protocol ++ * specification, stop processing it. Fixes #1348 */ ++ abort_read = 1; ++ break; ++ } ++ + } + + if (field_storage == MG_FORM_FIELD_STORAGE_GET) { +@@ -492,6 +517,7 @@ mg_handle_form_request(struct mg_connection *conn, + get_block++; + if (r == MG_FORM_FIELD_HANDLE_ABORT) { + /* Stop request handling */ ++ abort_read = 1; + break; + } + if (r == MG_FORM_FIELD_HANDLE_NEXT) { +@@ -560,7 +586,6 @@ mg_handle_form_request(struct mg_connection *conn, + val = buf; + } + } +- + } while (!end_of_key_value_pair_found); + + #if !defined(NO_FILESYSTEMS) +@@ -571,6 +596,7 @@ mg_handle_form_request(struct mg_connection *conn, + r = field_stored(conn, path, file_size, fdh); + if (r == MG_FORM_FIELD_HANDLE_ABORT) { + /* Stop request handling */ ++ abort_read = 1; + break; + } + } else { +@@ -584,7 +610,7 @@ mg_handle_form_request(struct mg_connection *conn, + } + #endif /* NO_FILESYSTEMS */ + +- if (all_data_read && (buf_fill == 0)) { ++ if ((all_data_read && (buf_fill == 0)) || abort_read) { + /* nothing more to process */ + break; + } +@@ -972,6 +998,7 @@ mg_handle_form_request(struct mg_connection *conn, + get_block++; + if (r == MG_FORM_FIELD_HANDLE_ABORT) { + /* Stop request handling */ ++ abort_read = 1; + break; + } + if (r == MG_FORM_FIELD_HANDLE_NEXT) { +@@ -1046,6 +1073,7 @@ mg_handle_form_request(struct mg_connection *conn, + fdh); + if (r == MG_FORM_FIELD_HANDLE_ABORT) { + /* Stop request handling */ ++ abort_read = 1; + break; + } + if (r == MG_FORM_FIELD_HANDLE_NEXT) { +@@ -1074,6 +1102,7 @@ mg_handle_form_request(struct mg_connection *conn, + r = field_stored(conn, path, file_size, fdh); + if (r == MG_FORM_FIELD_HANDLE_ABORT) { + /* Stop request handling */ ++ abort_read = 1; + break; + } + } else { +@@ -1092,6 +1121,7 @@ mg_handle_form_request(struct mg_connection *conn, + if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) + == MG_FORM_FIELD_STORAGE_ABORT) { + /* Stop parsing the request */ ++ abort_read = 1; + break; + } + +-- +2.34.1 + + +From d8c74f7cce312036816efa832196d57dd044eb90 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Tue, 2 Sep 2025 15:38:08 +0200 +Subject: [PATCH 149/173] Update README + +Remove mentioning some old services that are no longer used/maintained +--- + README.md | 18 ++++-------------- + 1 file changed, 4 insertions(+), 14 deletions(-) + +diff --git a/README.md b/README.md +index dae05879..7165ed30 100644 +--- a/README.md ++++ b/README.md +@@ -9,18 +9,12 @@ + [![Forks](https://img.shields.io/github/forks/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/network/members) + [![Latest Release](https://img.shields.io/github/v/release/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/releases) + +-Continuous integration for Linux and macOS ([Travis CI](https://app.travis-ci.com/github/civetweb/civetweb)): +- +-[![Travis Build Status](https://api.travis-ci.com/civetweb/civetweb.svg?branch=master)](https://app.travis-ci.com/github/civetweb/civetweb) +- +-Continuous integration for Windows ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): ++Continuous integration ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): + + [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/civetweb/civetweb?svg=true)](https://ci.appveyor.com/project/civetweb/civetweb/branch/master) + + Test coverage check ([coveralls](https://coveralls.io/github/civetweb/civetweb), [codecov](https://codecov.io/gh/civetweb/civetweb/branch/master)) (using different tools/settings): + +-[![Coveralls](https://img.shields.io/coveralls/civetweb/civetweb.svg?maxAge=3600)]() +-[![Coverage Status](https://coveralls.io/repos/github/civetweb/civetweb/badge.svg?branch=master)](https://coveralls.io/github/civetweb/civetweb?branch=master) + [![codecov](https://codecov.io/gh/civetweb/civetweb/branch/master/graph/badge.svg)](https://codecov.io/gh/civetweb/civetweb) + + Static source code analysis ([Coverity](https://scan.coverity.com/projects/5784)): [![Coverity Scan Build Status](https://scan.coverity.com/projects/5784/badge.svg)](https://scan.coverity.com/projects/5784) +@@ -54,18 +48,14 @@ CivetWeb must be used with an earlier or later version (see also [here](https:// + Bugs and requests should be filed on GitHub + [https://github.com/civetweb/civetweb/issues](https://github.com/civetweb/civetweb/issues) + +-New releases are announced on Google Groups +-[https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb) +- +-Formerly some support question and discussion threads have been at [Google groups](https://groups.google.com/d/forum/civetweb). +-Recent questions and discussions use [GitHub issues](https://github.com/civetweb/civetweb/issues). +- + Source releases can be found on GitHub + [https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases) + + A very brief overview can be found on GitHub Pages + [https://civetweb.github.io/civetweb/](https://civetweb.github.io/civetweb/) + ++Note: The [Google group](https://groups.google.com/d/forum/civetweb) is no longer used but has been replaced by [GitHub issues](https://github.com/civetweb/civetweb/issues). ++ + + Quick start documentation + -------------------------- +@@ -174,7 +164,7 @@ Authors + + CivetWeb was forked from the last MIT version of Mongoose in August 2013. + Since then, CivetWeb has seen many improvements from various authors +-(Copyright (c) 2013-2021 the CivetWeb developers, MIT license). ++(Copyright (c) 2013-2025 the CivetWeb developers, MIT license). + + A list of authors can be found in [CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md). + +-- +2.34.1 + + +From 3e7eceddeb626f5195f7c813b788a118c20eda1e Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Tue, 2 Sep 2025 15:56:22 +0200 +Subject: [PATCH 150/173] Update SECURITY.md and LICENSE.md + +--- + LICENSE.md | 2 +- + SECURITY.md | 10 +++------- + 2 files changed, 4 insertions(+), 8 deletions(-) + +diff --git a/LICENSE.md b/LICENSE.md +index a74a0fe4..db39daf4 100644 +--- a/LICENSE.md ++++ b/LICENSE.md +@@ -11,7 +11,7 @@ Civetweb License + + ### Included with all features. + +-> Copyright (c) 2013-2021 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) ++> Copyright (c) 2013-2025 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) + > + > Copyright (c) 2004-2013 Sergey Lyubka + > +diff --git a/SECURITY.md b/SECURITY.md +index b1781aff..78c85541 100644 +--- a/SECURITY.md ++++ b/SECURITY.md +@@ -14,16 +14,12 @@ Selected, critical defects are fixed in the latest release as well. + Note that different security policies apply to different files/folders in this project: + - The core components are include/civetweb.h, src/civetweb.c and src/*.inl. These files are part of every server instance in production. Therefore they have to undergo the most intensive security tests and reviews. + - The src/main.c file is used by the standalone server. It is used in various tests in combination with the aforementioned core components. Applications embedding civetweb will not use main.c and, thus, do not suffer from any vulnerabilities therein. +-- The example folders contain different usage examples in different maintenance state. This is explained in detail in the README file there. ++- The example folders contain different usage examples in different maintenance state. Some of them skipped error handling to keep the code shorter and easier to understand. This is explained in more detail in the README file there. + - The content of all test folders (test, unittest, fuzztest) is used to test the server functionality. These tests are not designed with security in mind - on the contrary, some tests contain scripts or settings that even introduce security leaks on purpose. All tests are only meant to be run in a test environment. You should not use the content of any test folder in production. Also certificates in "resources/cert" are only meant to be used in test environments and must never be used in production. + + + ## Reporting a Vulnerability + +-Please send vulnerability reports by email to bel2125 at gmail com. +-Vulnerability with low severity can be sent directly by email. +- +-For high severity vulnerabilities, you can get an individual gpg key to encrypt your detailed description of vulnerabilities you want to report. +- +-If you do not get any response within one week, your email might have been lost (e.g., deleted as false positive by a spam filter). In this case, please open a GitHub issue. ++Ideally open a github issue for suspected vulnerabilities, even if you do not post all details there. This will help against emails getting lost. ++Detailed vulnerability reports including exploit code should be sent by email to bel2125 at gmail com. + +-- +2.34.1 + + +From 4bd10c495faabc7789d6764c1c583142989b710c Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sat, 6 Sep 2025 12:26:11 +0200 +Subject: [PATCH 151/173] AppVeyor build: Replace obsolete Visual Studio builds + by new one + +Visual Studio 2010, 2012, 2013 are no longer supported. +--- + appveyor.yml | 69 ++++++++++++---------------------------------------- + 1 file changed, 15 insertions(+), 54 deletions(-) + +diff --git a/appveyor.yml b/appveyor.yml +index fb936d1f..e54c2d5e 100644 +--- a/appveyor.yml ++++ b/appveyor.yml +@@ -159,59 +159,6 @@ environment: + enable_stats: YES + configuration: Release + platform: x64 +- # Visual Studio 2010 +- - id: Full-VS2010-x86 +- compiler: msvc-16-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x86 +- # Visual Studio 2012 +- - id: Full-VS2012-x86 +- compiler: msvc-17-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x86 +- # Visual Studio 2013 +- - id: Full-VS2013-x86 +- compiler: msvc-18-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x86 +- image: Visual Studio 2013 +- - id: Full-VS2013-x64 +- compiler: msvc-18-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x64 +- image: Visual Studio 2013 + # Visual Studio 2015 + - id: Full-VS2015-x86 + compiler: msvc-19-seh +@@ -280,7 +227,21 @@ environment: + configuration: Release + platform: x64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 +- # Ubuntu ++ # Visual Studio 2022 ++ - id: Full-VS2022-x64 ++ compiler: msvc-22-seh ++ build_shared: NO ++ no_files: NO ++ enable_ipv6: YES ++ enable_ssl: YES ++ enable_websockets: YES ++ no_cgi: NO ++ no_caching: NO ++ enable_stats: YES ++ configuration: Release ++ platform: x64 ++ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 ++ # Ubuntu + - id: Ubuntu1604-GCC-x64 + compiler: gcc-5.1.0-posix + build_shared: NO +-- +2.34.1 + + +From acaf7e3fe0f406fb12b0cb9a073938995b66cc2a Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Mon, 8 Sep 2025 14:44:33 +0200 +Subject: [PATCH 152/173] Use atomic operations in timer test + +--- + unittest/timertest.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/unittest/timertest.c b/unittest/timertest.c +index 270dbec7..17716d16 100644 +--- a/unittest/timertest.c ++++ b/unittest/timertest.c +@@ -51,22 +51,22 @@ static int action_dec_ret; + static int + action_dec(void *arg) + { +- int *p = (int *)arg; +- (*p)--; ++ ptrdiff_t *p = (ptrdiff_t *)arg; ++ ptrdiff_t v = mg_atomic_dec(p); + +- if (*p < -1) { ++ if (v < -1) { + ck_abort_msg("Periodic timer called too often"); + /* return 0 here would be unreachable code */ + } + +- return (*p >= -3) ? action_dec_ret : 0; ++ return (v >= -3) ? action_dec_ret : 0; + } + + + static void + action_cancel(void *arg) + { +- int *p = (int *)arg; ++ ptrdiff_t *p = (ptrdiff_t *)arg; + + /* test convention: store cancel counter after timer counter */ + p += TIMERS_IN_TEST; +@@ -76,29 +76,29 @@ action_cancel(void *arg) + /* return 0 here would be unreachable code */ + } + +- (*p)++; ++ mg_atomic_inc(p); + } + + + static int + action_dec_to_0(void *arg) + { +- int *p = (int *)arg; +- (*p)--; ++ ptrdiff_t *p = (ptrdiff_t *)arg; ++ ptrdiff_t v = mg_atomic_dec(p); + +- if (*p <= -1) { ++ if (v <= -1) { + ck_abort_msg("Periodic timer called too often"); + /* return 0 here would be unreachable code */ + } + +- return (*p > 0); ++ return (v > 0); + } + + + START_TEST(test_timer_cyclic) + { + struct mg_context ctx; +- int c[TIMERS_IN_TEST * 2]; ++ ptrdiff_t c[TIMERS_IN_TEST * 2]; + memset(&ctx, 0, sizeof(ctx)); + memset(c, 0, sizeof(c)); + +-- +2.34.1 + + +From 618790d4d5a2fd3b410753aa3a4060a55976ca2a Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Wed, 10 Sep 2025 00:26:36 +0200 +Subject: [PATCH 153/173] Unittest: fix alignment + +--- + unittest/timertest.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/unittest/timertest.c b/unittest/timertest.c +index 17716d16..e445421b 100644 +--- a/unittest/timertest.c ++++ b/unittest/timertest.c +@@ -97,8 +97,8 @@ action_dec_to_0(void *arg) + + START_TEST(test_timer_cyclic) + { ++ static ptrdiff_t c[TIMERS_IN_TEST * 2]; + struct mg_context ctx; +- ptrdiff_t c[TIMERS_IN_TEST * 2]; + memset(&ctx, 0, sizeof(ctx)); + memset(c, 0, sizeof(c)); + +-- +2.34.1 + + +From 2fefc5c8a7006ea997ebb332cd435bf70381d5ce Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Thu, 11 Sep 2025 19:50:56 +0200 +Subject: [PATCH 154/173] Add Lua 5.5 support + +Signed-off-by: DL6ER +--- + resources/Makefile.in-lua | 15 +- + src/mod_lua.inl | 12 +- + src/mod_lua_shared.inl | 2 +- + src/third_party/civetweb_lua.h | 18 +- + src/third_party/lfs.c | 2 +- + src/third_party/lua-5.5.0-beta/Makefile | 106 + + src/third_party/lua-5.5.0-beta/README | 6 + + .../doc/OSIApproved_100X125.png | Bin 0 -> 12127 bytes + .../lua-5.5.0-beta/doc/contents.html | 679 + + src/third_party/lua-5.5.0-beta/doc/index.css | 21 + + src/third_party/lua-5.5.0-beta/doc/logo.gif | Bin 0 -> 9893 bytes + src/third_party/lua-5.5.0-beta/doc/lua.1 | 155 + + src/third_party/lua-5.5.0-beta/doc/lua.css | 162 + + src/third_party/lua-5.5.0-beta/doc/luac.1 | 118 + + src/third_party/lua-5.5.0-beta/doc/manual.css | 21 + + .../lua-5.5.0-beta/doc/manual.html | 12398 ++++++++++++++++ + .../lua-5.5.0-beta/doc/readme.html | 333 + + src/third_party/lua-5.5.0-beta/src/Makefile | 229 + + src/third_party/lua-5.5.0-beta/src/lapi.c | 1467 ++ + src/third_party/lua-5.5.0-beta/src/lapi.h | 65 + + src/third_party/lua-5.5.0-beta/src/lauxlib.c | 1198 ++ + src/third_party/lua-5.5.0-beta/src/lauxlib.h | 268 + + src/third_party/lua-5.5.0-beta/src/lbaselib.c | 558 + + src/third_party/lua-5.5.0-beta/src/lcode.c | 1911 +++ + src/third_party/lua-5.5.0-beta/src/lcode.h | 103 + + src/third_party/lua-5.5.0-beta/src/lcorolib.c | 223 + + src/third_party/lua-5.5.0-beta/src/lctype.c | 64 + + src/third_party/lua-5.5.0-beta/src/lctype.h | 101 + + src/third_party/lua-5.5.0-beta/src/ldblib.c | 477 + + src/third_party/lua-5.5.0-beta/src/ldebug.c | 971 ++ + src/third_party/lua-5.5.0-beta/src/ldebug.h | 64 + + src/third_party/lua-5.5.0-beta/src/ldo.c | 1131 ++ + src/third_party/lua-5.5.0-beta/src/ldo.h | 98 + + src/third_party/lua-5.5.0-beta/src/ldump.c | 303 + + src/third_party/lua-5.5.0-beta/src/lfunc.c | 315 + + src/third_party/lua-5.5.0-beta/src/lfunc.h | 65 + + src/third_party/lua-5.5.0-beta/src/lgc.c | 1803 +++ + src/third_party/lua-5.5.0-beta/src/lgc.h | 268 + + src/third_party/lua-5.5.0-beta/src/linit.c | 63 + + src/third_party/lua-5.5.0-beta/src/liolib.c | 841 ++ + src/third_party/lua-5.5.0-beta/src/ljumptab.h | 112 + + src/third_party/lua-5.5.0-beta/src/llex.c | 604 + + src/third_party/lua-5.5.0-beta/src/llex.h | 93 + + src/third_party/lua-5.5.0-beta/src/llimits.h | 315 + + src/third_party/lua-5.5.0-beta/src/lmathlib.c | 752 + + src/third_party/lua-5.5.0-beta/src/lmem.c | 215 + + src/third_party/lua-5.5.0-beta/src/lmem.h | 96 + + src/third_party/lua-5.5.0-beta/src/loadlib.c | 743 + + src/third_party/lua-5.5.0-beta/src/lobject.c | 717 + + src/third_party/lua-5.5.0-beta/src/lobject.h | 854 ++ + src/third_party/lua-5.5.0-beta/src/lopcodes.c | 138 + + src/third_party/lua-5.5.0-beta/src/lopcodes.h | 429 + + src/third_party/lua-5.5.0-beta/src/lopnames.h | 103 + + src/third_party/lua-5.5.0-beta/src/loslib.c | 432 + + src/third_party/lua-5.5.0-beta/src/lparser.c | 2091 +++ + src/third_party/lua-5.5.0-beta/src/lparser.h | 191 + + src/third_party/lua-5.5.0-beta/src/lprefix.h | 45 + + src/third_party/lua-5.5.0-beta/src/lstate.c | 420 + + src/third_party/lua-5.5.0-beta/src/lstate.h | 455 + + src/third_party/lua-5.5.0-beta/src/lstring.c | 359 + + src/third_party/lua-5.5.0-beta/src/lstring.h | 73 + + src/third_party/lua-5.5.0-beta/src/lstrlib.c | 1883 +++ + src/third_party/lua-5.5.0-beta/src/ltable.c | 1325 ++ + src/third_party/lua-5.5.0-beta/src/ltable.h | 184 + + src/third_party/lua-5.5.0-beta/src/ltablib.c | 426 + + src/third_party/lua-5.5.0-beta/src/ltm.c | 276 + + src/third_party/lua-5.5.0-beta/src/ltm.h | 104 + + src/third_party/lua-5.5.0-beta/src/lua.c | 761 + + src/third_party/lua-5.5.0-beta/src/lua.h | 554 + + src/third_party/lua-5.5.0-beta/src/lua.hpp | 10 + + src/third_party/lua-5.5.0-beta/src/luac.c | 724 + + src/third_party/lua-5.5.0-beta/src/luaconf.h | 827 ++ + src/third_party/lua-5.5.0-beta/src/lualib.h | 65 + + src/third_party/lua-5.5.0-beta/src/lundump.c | 423 + + src/third_party/lua-5.5.0-beta/src/lundump.h | 40 + + src/third_party/lua-5.5.0-beta/src/lutf8lib.c | 291 + + src/third_party/lua-5.5.0-beta/src/lvm.c | 1926 +++ + src/third_party/lua-5.5.0-beta/src/lvm.h | 136 + + src/third_party/lua-5.5.0-beta/src/lzio.c | 89 + + src/third_party/lua-5.5.0-beta/src/lzio.h | 67 + + 80 files changed, 45968 insertions(+), 14 deletions(-) + create mode 100644 src/third_party/lua-5.5.0-beta/Makefile + create mode 100644 src/third_party/lua-5.5.0-beta/README + create mode 100644 src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png + create mode 100644 src/third_party/lua-5.5.0-beta/doc/contents.html + create mode 100644 src/third_party/lua-5.5.0-beta/doc/index.css + create mode 100644 src/third_party/lua-5.5.0-beta/doc/logo.gif + create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.1 + create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.css + create mode 100644 src/third_party/lua-5.5.0-beta/doc/luac.1 + create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.css + create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.html + create mode 100644 src/third_party/lua-5.5.0-beta/doc/readme.html + create mode 100644 src/third_party/lua-5.5.0-beta/src/Makefile + create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lbaselib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lcorolib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldblib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldump.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/linit.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/liolib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ljumptab.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/llimits.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lmathlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/loadlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lopnames.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/loslib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lprefix.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstrlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltablib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.hpp + create mode 100644 src/third_party/lua-5.5.0-beta/src/luac.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/luaconf.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lualib.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lutf8lib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.h + +diff --git a/resources/Makefile.in-lua b/resources/Makefile.in-lua +index 45f80181..8afff851 100644 +--- a/resources/Makefile.in-lua ++++ b/resources/Makefile.in-lua +@@ -39,7 +39,14 @@ ifeq ($(WITH_LUA_VERSION), 504) + $(info Lua: Using version 5.4.3) + LUA_DIR = src/third_party/lua-5.4.3/src + LUA_SHARED_LIB_FLAG = -llua5.4 +- LUA_CFLAGS = -DLUA_COMPAT_5_2 -DLUA_VERSION_MAKEFILE=504 ++ LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=504 ++ LUA_VERSION_KNOWN = 1 ++endif ++ifeq ($(WITH_LUA_VERSION), 505) ++ $(info Lua: Using version 5.5.0-beta) ++ LUA_DIR = src/third_party/lua-5.5.0-beta/src ++ LUA_SHARED_LIB_FLAG = -llua5.5 ++ LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=505 + LUA_VERSION_KNOWN = 1 + endif + +@@ -112,6 +119,12 @@ ifeq ($(WITH_LUA_VERSION), 504) + lctype.c \ + lutf8lib.c + endif ++ifeq ($(WITH_LUA_VERSION), 505) ++ LUA_SOURCE_FILES += \ ++ lcorolib.c \ ++ lctype.c \ ++ lutf8lib.c ++endif + + $(info Lua: using static library) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index 2a1d3a57..f2e40237 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -2695,8 +2695,14 @@ static void + civetweb_open_lua_libs(lua_State *L) + { + { ++#if LUA_VERSION_NUM < 505 + extern void luaL_openlibs(lua_State *); + luaL_openlibs(L); ++#else ++ // In Lua 5.5 and later has become a macro ++ extern void (luaL_openselectedlibs) (lua_State *L, int load, int preload); ++ luaL_openselectedlibs(L, ~0, 0); ++#endif + } + #if defined(USE_LUA_SQLITE3) + { +@@ -3091,7 +3097,7 @@ mg_exec_lua_script(struct mg_connection *conn, + + /* Execute a plain Lua script. */ + if (path != NULL +- && (L = lua_newstate(lua_allocator, (void *)(conn->phys_ctx))) ++ && (L = mg_lua_newstate(lua_allocator, (void *)(conn->phys_ctx))) + != NULL) { + prepare_lua_environment( + conn->phys_ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE); +@@ -3207,7 +3213,7 @@ handle_lsp_request(struct mg_connection *conn, + L = ls; + } else { + /* We need to create a Lua state. */ +- L = lua_newstate(lua_allocator, (void *)(conn->phys_ctx)); ++ L = mg_lua_newstate(lua_allocator, (void *)(conn->phys_ctx)); + if (L == NULL) { + /* We neither got a Lua state from the command line, + * nor did we succeed in creating our own state. +@@ -3353,7 +3359,7 @@ lua_websocket_new(const char *script, struct mg_connection *conn) + } + pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr); + (void)pthread_mutex_lock(&(ws->ws_mutex)); +- ws->state = lua_newstate(lua_allocator, (void *)(conn->phys_ctx)); ++ ws->state = mg_lua_newstate(lua_allocator, (void *)(conn->phys_ctx)); + ws->conn[0] = conn; + ws->references = 1; + prepare_lua_environment(conn->phys_ctx, +diff --git a/src/mod_lua_shared.inl b/src/mod_lua_shared.inl +index 5f69e285..fed94cd9 100644 +--- a/src/mod_lua_shared.inl ++++ b/src/mod_lua_shared.inl +@@ -40,7 +40,7 @@ lua_shared_init(void) + { + /* Create a new Lua state to store all shared data. + * In fact, this is used as a hashmap. */ +- L_shared = lua_newstate(lua_allocator, NULL); ++ L_shared = mg_lua_newstate(lua_allocator, NULL); + + lua_newtable(L_shared); + lua_setglobal(L_shared, "shared"); +diff --git a/src/third_party/civetweb_lua.h b/src/third_party/civetweb_lua.h +index 40382022..15a76c1d 100644 +--- a/src/third_party/civetweb_lua.h ++++ b/src/third_party/civetweb_lua.h +@@ -49,7 +49,7 @@ extern "C" { + #define LUA_ERRGCMM 999 /* not supported */ + #define mg_lua_load(a, b, c, d, e) lua_load(a, b, c, d) + #define lua_rawlen lua_objlen +-#define lua_newstate(a, b) \ ++#define mg_lua_newstate(a, b) \ + luaL_newstate() /* Must use luaL_newstate() for 64 bit target */ + #define lua_pushinteger lua_pushnumber + #define luaL_newlib(L, t) \ +@@ -62,17 +62,19 @@ extern "C" { + } + #define luaL_setfuncs(L, r, u) lua_register(L, r->name, r->func) + +-#elif LUA_VERSION_NUM == 502 +-/* Lua 5.2 detected */ ++#elif LUA_VERSION_NUM == 502 || \ ++ LUA_VERSION_NUM == 503 || \ ++ LUA_VERSION_NUM == 504 ++/* Lua 5.2 - 5.4 detected */ + #define mg_lua_load lua_load ++#define mg_lua_newstate lua_newstate + +-#elif LUA_VERSION_NUM == 503 +-/* Lua 5.3 detected */ +-#define mg_lua_load lua_load + +-#elif LUA_VERSION_NUM == 504 +-/* Lua 5.4 detected */ ++#elif LUA_VERSION_NUM == 505 ++/* Lua 5.2 - 5.5 detected */ + #define mg_lua_load lua_load ++// A third parameter "seed for the hashing of strings" has been added ++#define mg_lua_newstate(f, ud) lua_newstate(f, ud, luaL_makeseed(NULL)) + + #else + #error "Lua version not supported (yet?)" +diff --git a/src/third_party/lfs.c b/src/third_party/lfs.c +index 95ab63b4..e69dfec7 100644 +--- a/src/third_party/lfs.c ++++ b/src/third_party/lfs.c +@@ -324,7 +324,7 @@ static FILE *check_file(lua_State * L, int idx, const char *funcname) + return 0; + } else + return *fh; +-#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 504 ++#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 505 + luaL_Stream *fh = (luaL_Stream *) luaL_checkudata(L, idx, "FILE*"); + if (fh->closef == 0 || fh->f == NULL) { + luaL_error(L, "%s: closed file", funcname); +diff --git a/src/third_party/lua-5.5.0-beta/Makefile b/src/third_party/lua-5.5.0-beta/Makefile +new file mode 100644 +index 00000000..388fa17f +--- /dev/null ++++ b/src/third_party/lua-5.5.0-beta/Makefile +@@ -0,0 +1,106 @@ ++# Makefile for installing Lua ++# See doc/readme.html for installation and customization instructions. ++ ++# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= ++ ++# Your platform. See PLATS for possible values. ++PLAT= guess ++ ++# Where to install. The installation starts in the src and doc directories, ++# so take care if INSTALL_TOP is not an absolute path. See the local target. ++# You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with ++# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h. ++INSTALL_TOP= /usr/local ++INSTALL_BIN= $(INSTALL_TOP)/bin ++INSTALL_INC= $(INSTALL_TOP)/include ++INSTALL_LIB= $(INSTALL_TOP)/lib ++INSTALL_MAN= $(INSTALL_TOP)/man/man1 ++INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V ++INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V ++ ++# How to install. If your install program does not support "-p", then ++# you may have to run ranlib on the installed liblua.a. ++INSTALL= install -p ++INSTALL_EXEC= $(INSTALL) -m 0755 ++INSTALL_DATA= $(INSTALL) -m 0644 ++# ++# If you don't have "install" you can use "cp" instead. ++# INSTALL= cp -p ++# INSTALL_EXEC= $(INSTALL) ++# INSTALL_DATA= $(INSTALL) ++ ++# Other utilities. ++MKDIR= mkdir -p ++RM= rm -f ++ ++# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= ++ ++# Convenience platforms targets. ++PLATS= guess aix bsd c89 freebsd generic ios linux macosx mingw posix solaris ++ ++# What to install. ++TO_BIN= lua luac ++TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp ++TO_LIB= liblua.a ++TO_MAN= lua.1 luac.1 ++ ++# Lua version and release. ++V= 5.5 ++R= $V.0 ++ ++# Targets start here. ++all: $(PLAT) ++ ++$(PLATS) help test clean: ++ @cd src && $(MAKE) $@ ++ ++install: dummy ++ cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD) ++ cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN) ++ cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC) ++ cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB) ++ cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN) ++ ++uninstall: ++ cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN) ++ cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC) ++ cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB) ++ cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN) ++ ++local: ++ $(MAKE) install INSTALL_TOP=../install ++ ++# make may get confused with install/ if it does not support .PHONY. ++dummy: ++ ++# Echo config parameters. ++echo: ++ @cd src && $(MAKE) -s echo ++ @echo "PLAT= $(PLAT)" ++ @echo "V= $V" ++ @echo "R= $R" ++ @echo "TO_BIN= $(TO_BIN)" ++ @echo "TO_INC= $(TO_INC)" ++ @echo "TO_LIB= $(TO_LIB)" ++ @echo "TO_MAN= $(TO_MAN)" ++ @echo "INSTALL_TOP= $(INSTALL_TOP)" ++ @echo "INSTALL_BIN= $(INSTALL_BIN)" ++ @echo "INSTALL_INC= $(INSTALL_INC)" ++ @echo "INSTALL_LIB= $(INSTALL_LIB)" ++ @echo "INSTALL_MAN= $(INSTALL_MAN)" ++ @echo "INSTALL_LMOD= $(INSTALL_LMOD)" ++ @echo "INSTALL_CMOD= $(INSTALL_CMOD)" ++ @echo "INSTALL_EXEC= $(INSTALL_EXEC)" ++ @echo "INSTALL_DATA= $(INSTALL_DATA)" ++ ++# Echo pkg-config data. ++pc: ++ @echo "version=$R" ++ @echo "prefix=$(INSTALL_TOP)" ++ @echo "libdir=$(INSTALL_LIB)" ++ @echo "includedir=$(INSTALL_INC)" ++ ++# Targets that do not create files (not all makes understand .PHONY). ++.PHONY: all $(PLATS) help test clean install uninstall local dummy echo pc ++ ++# (end of Makefile) +diff --git a/src/third_party/lua-5.5.0-beta/README b/src/third_party/lua-5.5.0-beta/README +new file mode 100644 +index 00000000..25384b86 +--- /dev/null ++++ b/src/third_party/lua-5.5.0-beta/README +@@ -0,0 +1,6 @@ ++ ++This is Lua 5.5.0 (beta), released on 28 Jun 2025. ++ ++For installation instructions, license details, and ++further information about Lua, see doc/readme.html. ++ +diff --git a/src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png b/src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png +new file mode 100644 +index 0000000000000000000000000000000000000000..795f7a06ed5b32131e69f245f1e1e0f4fc22c5aa +GIT binary patch +literal 12127 +zcmV-lFQCwgP) +zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=QHaxA-%ME|i0Edlee90Ufl-hq~{<5@}G;a!i%le3hucKDazdZ7YlV7_3{F~3u;PdnI`B(G% +zTKM&O*Y^8DGs~aj^2LW!q*1w`}$J`zMX#L)M70)w9L;OeQR~_dyhUh>bAAe +z)Sml)-{|-McVBOftSO!pl7D{2&nnaBdp?i)Ie1@4iQfxf`uVQb(K436@8hlQThxE{ +z@1L#S{k;6Q?Z32jtv|Q>`?t5-?yCzmzAvQTx>Ua(|M+|;Ul;nVtxxZM24Mfb^Doc0 +z8^X_9c3?cIq^K0g1%3@p~=d +zx$Ssvw?g;Z`5ky`3|tKK{O4cpUwz|!W`Y90#RnFwD++b2!Ux)~2i`_U!hMTco&tY; +z{r*pVO?0q4W$tWndUKykSP4JCm7Yc?-dK42%u;}EJ{O=w+`6!skm!J~A(c>BQH!yK +zKp{H~nk+r09E}cyQsQQjF{e~%4l~TX`AiGXTVsizry;|X9b;Ehqcnw4__KMnF6 +zc`}!|lu}D8y^Jzzs+zD`TNSI$mRfG5)z(^fZM4}_&%N~8Tkn1JIpRpLZW+wu=wpmI +zx#{G}lTT0HFsDUJmaSN|X5EIfxq#2gtE{@(>T9gI<4&77-gUR#_t^7<1C&la<{%W`;NIAn!7AHeeMEwhT5ozZOgm0 +z82a&fGvYP()x&Cs*BB3GD2!(C9iny0J!KuZYi@^x)HGYR=BI^ivz>#QmNQdlRcBW7 +zW497^KXncYxLQqvX;^9BGxsUOHaJ?%+D&1sd#p^zcTnDXQ(VR*TiI=fz4EXw;K~RGDAUj8DMw%ZOaUx@R_Ev_~KY6V7n!25LY+@T%W}HvuQcl +zISMg!-!OL#eb&Bb=+;77<6M>i66-SD$Ta|0T1%AC`Yk9=8H2W28`EcFQm;?-29}O^ +zQrcQ=9Aj%co=||)Xx<$;tabNJAo_Yfdw@V6B_^YNaRZFh+2#b(XIR>!EvgSP4okcJ +zw0Y~gbPbzxX5OhVe>K)IZTk6Ox3BmINPk1Slx#>u(W;X}@|)Ze+#pn2D?)Yn9o0Kk +zhQCm{@@#Hw5Ht?=VgSpr?v8DmzJq1@Up*^I>D1L3Iu`hNf(-OerAvAj#lE2>q=V~} +z3>Mpt^u^ic^Z3mwQ34)yc-FnV0ON&F#05WtW!Za_;@nsewyB1c{NWDbhMJhZ86 +z!wxXQr#dtD&o6BJFOTYG4St_hbM7qvtUVI5cI@0zxo{m!+YGbrWQ^?I@Z}yAb~@+O +z6v5PCQ2ClBvv~7Zl^%nsGu@8yo|+9;b=mBOMSIcrfsHddkz2TVQVIwFVl$cvaWLlW +z*V<`Y0PEA8HAC6^f{nA%i9x^K)R?-OO5ea;wPITL>Sr_ZkUbX~G2hG_2*DV;>|o); +z|4ix?K~l(t|M>p=B18T@f)0jttTh+lm4nQpFg6rDKz1@eTxTuK@;%_T8o7i?y{uuu +z_aFf1GYfm|UM3W^HwM$UHE|sKW=29Z!cescIr6SPZOyrAKg>kE15Kb(U}ko{ +zcpA#ZF2)a+d=QE5YD9zh-aJKj<6zJE!NNJu$W?wnpAF)M{<7n*NB5Uf__B*-oQh8n +z%Jj4dx7u<-%8oFydtjc~@!$y*roc^gqqIoYl0}C32O_*mgBsA)L9HgDRDH>PFQb=I +zyh_6XGnJ}LL0Nl_!8EyZ@d9{dddm2A;#(_b=su`Rb51+}W#?aSs4DS@qB7KT?sZGU +zeavfPS#jD^*^tVw4W~|x)x}(C=xS&~7!toU%l30`*b4VS(`=SYNW6~egQaz?)8VlS +zSX$Fs2TnjF!2lp9l&1C(Md3_zr<4b3SHd{d>Kw8NH-V^(-DlVkvS=vL5^34Ru0Bk5 +zD0Tmt)NoeAtXI;RWkfjyx6T&F;xR^9A=+7Ni!Hi}V8kFka#I25G36IMdfU27n3L6x27|Xd0tAGZAF8uG#Xku~NSR2d;!V~?8voeMX>;Qd8%Y*lu>Syq(YWj{~2JzD{{c<_5afCz} +z9(Zr#^%ltH1v}XP?3d1fpkwCpG7=T4WLA(oZ|Qj4hp*OgXE2?xKzR~Cz-xm@C-Oj9 +zG0TfdpyFrjTNqV_=zx$>+GW9%6U&Ika0zT>-HZUVwvlK{0tS*T&A{OYrmRLVv${Mq +zJY}SSy-}Q&DH*s;jBrKXXug5~2tEsd&5Xx%5IsbQL{V)|s=)KWueD~Z71D;Q#TCUi +zhyl3i29rQ(D3h9WF#xz6^Wu;kU~#zl1cIdA23M~dz<<|`gD2B6S^+P0VGR` +zSPn#`5*kiH*O24Jjfgh!hX9m=_m$VKt*nLtLUr*#*kNoQVAIJ2M1VQ74&lM(<~Fy( +z(?zOhszqSeau|b{EmfqyBHHbl!G+mJBE&lw1ty{+HhJBKt~)C5;uz$jMKlnEAu^|3 +zm`^Z+QiNi*n1$k4DqKDQal$_642%xZwT1A|xz-VmWY8A@gNV2u+q|bdxyOcZ2V8i< +z*ubV+(YQ{$JN^|O;T +zJB0Lz&IevG9K&8yV_+;bU~9+^3@KiTpuqU;j;FyrP-}$)yD-?6+k;t}qJJ4WCP|FQ +zk)qcLPDXK{0hvQd#S@Y05vt>gM1VzVr|LL*`~p^xv{R;HcoSAY* +z(|F1-OzcG?*pUkJrv2o&H1^OO;;&n*#+0`*@?2v;#Kbs6F0bX<(+vz59AjXLI6HxV +zguB<(i48McjZd;YoCu)kD=BS~<#nJrN7G3F$cC`65x|Swyol}w)G-;*G#IXo125jD +zj_{U>3s}$%DWz+0j$u^cdvHXJB36uYXAxtBdU0I~n~5)`4cKG_Fgzzo|ADPy^{*sI +z>Yzr13|ccP-e%!==r+mDXKJ`HRAfG5C)j%cs9WGoNvb#E)4(s2dDL+78e_08_AMy` +zBUR@i4sJw(wk?`1SgHidQNRVjz3jIDJ0dRz~ +z?ztUSXkhTcZk51O4!k5;VRxR^$-_Xaya_(xC+89pHf&b9K +z%s~GbP$)XcD3_`dBRs5fgDu84E&!=mg%3b}biyQ4!`gjX!ozLUR7Sn&7|sM_!YFB- +zz8sLwzSj60fDp6%kBlFiEWZsLXH*Fi-_#}0)Ejjrrjen5J1QZS*0RMrkn#l|nf?sK +z_8Zw4^tTjra)u3*;m6WUWVxk@Nm63015@P=qClvSY@5f5@hT(o +z7}g-;jly_Beoc?hh8e-Od9hj3?NX5i;fkx*%#ji9R)yva9f7BiJ!_4+8fecB2kx;H={=A-_Y@iP%Ul6x&-|6m6#yc +zM@P5|_Q!oBaLbz%AINSbL;wT*QrzV>c9$5d;ekYhPLLou{k33oK_~|qtU4pbrJs-O +z6$z1~rVf6RPPxe0$yL%BOdE3)tqI9yO$lD=l{1eL4CCj;?BG0Eqf +zHoIBbj)i4hhndHj(+?DXFv0=9o5e5q;SXIS(m-jkMxtb*N{g{T`H#75&*65|sj8l< +z4O*kvh$tqOs<4j^yYTUIbo`qnl-Ng{awjfg+#tnz97BUqELV6zlm@vr%EG0kVORV< +z6po;Zs#5XdnXU2s5h>rLJb;J5;Z6sQ1C@v)tvG~|Q-GW>ZLrHYGwJ+rHl0=DfoRCk +z5z(L#;mCK{w>O +zE>3K#B?;*Ct@Ei;k!322871`6daOVyU<{QQn030skfQw_NrYRwa%QK{?37j){=?xA +zci_0alvi0~;cZ&daVTPat`%lu>VGFB_tl +z-y>>~j-bpVQq-|-q}!cRVL8~;dvy97MLFTV=*j=-&j*n +zj->U@sBiFq;?mG231{@m8k!~CHHt7@YlX0>LmXzi>bR1bX5BrLYfYy0$emu;HtneG +zdz5_D9-!apLp{&c55zX`8L(D@N^k&p&6494IbNO6O{H*}z(+~B1?BFQ@JBI3jdHOC +zz5$TN+~+DUu7gx;e|Y&ZmDD$L7NaN4E+z}EYzHht4>}~5Wq}r<^)h327|uv-@GySq +zumK9Sgm;BTbIA+>E;ewG?=@l~>kBRBbsu*~MfNq4)k6xKs#+)Q2;5okT;p6LFlxKOL`*ZZES&IQak$9FF +zW`%fzczUy9aNZ{lvx2M=pA(OnbV1@rt}8CTan3s|@XWB8O3o99iN$;yD{agQrbawP +z98oo$@`bd^D(5ZETB*vK_v9}OX7!b2uG1Vw9E(_j1Q80VD58W4qO|IySV+=-%)>wA +z_$6{FM<`C{82BS2smXw+=```ES{CxHJMxYAqx +zavhlYB)!(sB1b@I8@RY`Y04gOxdRM78L}xmlAorK%K`6a^i3IH;1=jvb9-y<p(JFpkg{pJ$)-R^!a^tkLMVxmfzW%k0UL}lxPhCl +zELmMMBh6^q-9OHEM`tc0TV?LJpznF~Xy(>4_dQ>G&v(u>*iEHwQ5{y&%CijXP+%r7 +z2bc&P2#f~`fPBCJVn9C-1U3P!z*=A(up9`O4JLbn7`szrY8KUa4a+zII0ZNn_ypj~ +z!ll;$F9FX0Pniv7U<{B!#9!|>fa8JlfknWCK@YaT@EUjvJ$Ujxc=8Ouz)DyoqDhjG +z1j(4SvtJbzVE%4kEN*OQjEn)IfG7dJ23!tI%ha;sDTNeP6_8iv!&mCVSLnr)@7dvX +zt%OCqFF`yUCl-v;-w`F;5+Tu_%yf7ca3^rRxy0Op^)UoQfVcv<%Bc;$5-+9ul~6RP +zfP#vA3~!d2XF)U=BhuDSZ(|>kwy5)n1aJ@V^WgU2hMw1Y_I^O7oHoVq=6f#!elE^A +z)q?T^ODL@?p@SnhsfIRz- +z6|>42H??%9{vg-DbiUI=ye~~-o&dgXHki$OJRtsh|774^V7|Src%MQl539ge;$wG| +zWZWX~L6EN10W=5S4xE<=C!TD6!hKx4E`f0*@GNi$fZ;Q!o;QIB2anrrv?62g^cWOY +z6;R?YBH9%t5l#XW1Lt`1JpCNPtNriw?>T@Ng$Diw{?mcSfJ%UZ%6ulDSWQuN0egyg +z3%r!>TY_ale|yxnB%dxw7VKL#qwLY(+F)|GH(7P3)sf6ko&(%v16_7N36+OcWG{T( +z1=8CTrfpd#Rx)M791U$63r3dT(@+s&A=k&Ca$OL0vRecQr{nt`dntn!)V +zivw!|)@}uezurF&cofCFQaQJR@&ilR+r*pip`@mWUNejpw?Hw_Rg>mTdaQF*=k5WB +zzurFuc+RFk#vfeHxM^kVt>Ve|P&}oO(3Y_6Q|85^@wRAw^v(G8_^$OKwTo&!zy9Dv!r!Fy+MRUB*Z4Dk-SQClQVl?T!Id08`3mmp>F( +z6UeQG)GVy=0JkF}c~wV_-)*T7w`Ibi75GY09q~evd?x26%P@>ffWrWa{Y8}27V~ix +zPo9VBd1*sBH~Q=S`8hLLVkegX#fAqaA6JDZ&%?)Ad?h{-kp$7M7(y3y1=a?ZtQLb~(OcA7cXJXO-)W#I^opS9en2UPFQp|{j+E>23{g8gz`uhUGbr1)WDJ1}S$xHel$KNjXSt~% +zrvP;T<_1M>6AeT<(oF6ThBujrz4We<_ID9PxK2kYXYIDJhKf*jftDKG;# +z3RRZsO~#URyxmQ3b1xH*swA&8Hz9iA#6k>@VY9&t{q_D|45!e0D6Rag{h?@LZpOu} +zxJBFYPQ9lAyaxO-X(b=uwq#pIHJNeV3_`u3Y6&~91j?daF`Ay<&gA2(D6De7{T`n| +zaZRD!_9Ml?w+>b`o77J#mgF&%?fF?BhD4Yb7mOmv+#%EmZ>@1TIbHC;ztX{%u44 +zRsMzk$tce4RNItHo?k^_rTd@+H}}&1W|#f-D`tave^#O_3#$y64Nx?tASd*C*SkGB +z7+ZlO%?5MmV5o*;Hkd6I*6G0SZJ@Wm(%~$Gb43a!q|sT&W-aVBFy`4>lnZqaw?^n$ +zAJTore6zt^KhoyOEXw1yw06IaEx4RscCj=WATmj+O|q +zKq|kuyK`;lv)Q_~*?CJ8sE9QpGeujhko-^0AuL=s%3k=xy! +z$>!+5{!s%`TVHFd8@^(9%~doI)4x8w^`Bea%A%hGdKDn<7EL?&zR9BoBxxi;4c@z6 +zzz|F4rrvhzK8=eT6Tm8f{!VvImZOBWWYmBdmH|+d;o{jhS+TUlyUS&Ny9vOGStMO# +zhaLP`{-^;-+n?KdtE9`YlT +z@^EvZ0LSQM*QfyzX#?!JcJ9UOrFpuQJ;udb9RO4T6jtWD`kNAwv`)MwTYzk^eTZX% +zeRgAZpFz>26nS~PzurH=rCWU=MQykYh{LLMWDAfKvhj$!W=ktc^`UsDY?~_s_(saJ +zPb_pdw;JH(k{GPX79guq*>q3LT|o9J(w*#gr7$m>asg`A6J-aM;B~ojCeo2&0ycXL +zPb^!2tVjXkG8yN|_fRo2t*+@0HT5;w(vE4s^>#)uZo0d(yb}FM;-Qp9S-y4g(5avu +z0uqnJn*bA_uf<*N2cUdvsb1dyRRiOix->ADPSC^*}-lXJVd*rdXVzQusY_B +zcD6j;0`M}Z@|t*W++8in^DyzKwCQ5MN?OU^`0M?{euDFuhriyx2zVXX57g2gZfl1! +zS83Mdm8=0G@BR@4HL^fgC_hZ8vgrKq|9 +z|7Rvqx^Eh_wI6U(?Ux1}fICLd^ou4Hu+J$I89#emPBcS3qqI)@;IPg1&JGBz4uM)` +zN!_q1H(CN}9KZS#6PR>tnhATe@t=)@o$@@Yjtg&*m&wOhQ&5qgv-V;^;KS5VbP$6_ +zvJQyIeLx7HXMGSW$3^Cj9O%!39cMO}y~wzEBAn23ft*OByEVbTME~Gns!GK?xOGdV|`EUuHpU8uu^PL{&AWZf{$Usc5 +z4td(+@oXI8sdu8LDG>NDl!HXDUrz~E;Nf2*PL79jusPRU4gA%Pw}YG9{js5~X+Gel +zp>je^k2Q6m=F=1HiqYTg9uH?FEjm}GExdenmdMIR0K1X;uOhjs+izP!toB8YHH +zSTEDL#auGHj5lc|pG3xUf*X5XDfx+o;&i^9(vDu>J0tZSBL+nMT5d$mXd=?uulM9E +zhLE9wyJ?~9P-WYaZ7JuTj9Xa`)vbg@+Z$<%V_w&?tY!0PuR9_@%!kY*aNdCF>LJpW +zy(BE!6Qh5CF@4xFtdVxR+wCAkE9TCMbgu5v+xV={^4$<`9=-V<@vxb>#N327B)a|8 +z4rfW|sO+*Pw=_N2G@|Pi78EujY;~lyKdbEv3~2Cn;DXIdHfO~qj(i-`0?V-OLq_6~ +zu_W7HX?Ipz4Br~Yk^~3%piSO9?-L`v)(7`}kTP2tqP7wGwunA|Fb(!5a( +zEH^#agyAu+My+H`EEuQd<@Ui|o-!oqZ3^r8&0n@Wuw`^T&teHu&J_GGG^zk3($>%R +zSJMheE-@R-7qe*!M?KQtxVSM1oQWElBibFK<)yX}Yl;>i#75%GGnm#GYB@p@ktE^P +zk@>pt_6RL6wd;WX)G&-&$+Bdn^ulZ~`+(0OQwgHoF}6M5Ml3kIN|pZ3sJ(_Ez=NUf +zp|&jj&H=)4)9{^pXloxWFQp^qS!z53*TFi>}o@w7uS`lc~Qc@A$z1Y@VVgD;GagrQw>F2nX49D8zitc@ND_a^0!cHZ51aXNrIbtNkkIlkIPG+gJ(h8 +zo1G+8bQT8A53CJjbyj9zZ6H=Yv-|+yh-5TL(WHWptQi^J9-(zvC&5j9I$*B?pEVoI +zxB8piAAxjr@?cL{c~I1Fps58|D(Tn;c0_N-=5|)nZxJX~1vM +z79#-EdR9Keev=KrQD%erx-0GYVl7tES_K?z7*IB~l+flr=Nv~1H7@`C=4YC>B!UTY +zDLGkkt?|J|%m(vOEM}wL?zt|-TXn~DFy8SbJg>Xfj>QAc-Lh29qk&1}Ue89i=E(2XfH)pd +zv4qOSM0mh_+|0#2k3qeO`UQ{WS-Ew|R>ovZ#$-&!WK8x}IR^C_ycNKkz@JE8^XWL$ +zM4nfG{efSg=0QA#_C{!tLxI1^{^j!d5N-cOa;-n2mU?{vcnkHiu~T=_&M%=VD*psr +z159)5KNB^8b_4Kb$G%y>_cL7co21W`|B3wng0%6A+P-8gugIJEgR>`>S#@X9V$8lZATyOz4lHpomV6GhVGuajenjOc*9pLHmOJEJ^ +zjSZIy&=+d^?+4ZkP}cx!1#CkDE7OGjw}G!Z62+gQ#(RDaski8SQ?mI#13!>!PnCV2 +z1A3&5mjWw+hen(~X%FaY(g^zj{UZY)0n{v~m$hwI$Z__z1h7DQREL&?>44avUx}KR +zXK(xAAnja*wgkFEPL*~|)V5WkNWw3S0FY-KG}Aa#+0U7Sq!OEff77;2kdArs&K|x~ +zjb<~j`q@$jDC^6CGPFgNHF-!Vk#%CqklycOU{!{&B!D(YG=a>kb>1K*sF{H|DTBTJ +z>|yDF9?=G!Q$G!OQ2@5319)0G#>2zxq`#L=rJHn6Q|6p92elI47R?GEmgERA#X|-} +zUua7w{ksK_Ofx3!D&uy?b;fJdObJ?rib$rJ`^_3?JCp0gWL+VC;%2e;ebUZM3;k|$ +z*!iJ~b0vmqxmG3$)Sg}R14rV$)$vS+1W1ea*>;fm(g5c}PjPnge}Bgh=n%7)FFkdk +zMfQl+ml?*A8n!se +z#jO%mA?o#eah8Red2-6>qPoA;w!J95=P~&{5%oftsp6zB0}jk$RQLw)2Q2`21UOOa +zt+wg=H1MQ+Zxv0_-`#YOqhk#w%Y+L%br2ua;_yR5mQ118g}@7<6<286*Gp*Ulun7v +zY+0i@|I6he9RQpyf3`d`N)Y}o_xeB{{N+e>iG<%ziM_sAI>*Hl#xK@DG$pwByO`Ju +zC8*h%v;zMn=iMNdW2*SFrNFmPBa{b8@KFXF4g7~hdxJ`*0jf6Cl!I=dNf5skxI~Le +z4$>a(jna^IRF&Yxn%V}$lr+k9-xCeIS;9p7RTDjuqdX@MsX)xha?!R-i?@U%YMUjU +z=O(!>n!^oApy5YNHo8W0&T%=WRyvJO_HO~+m){QRUB6Gt^U&ob31>ekPP{{o3rT?U +zkhJX}0sA>Q$Cc>GuOd!9RSo1^IFp-k=v2X06ss; +zHkS%-_Oa@O-$fQG9)|n`c*hZ*If6iCEA+!aRS80@Rxbb(-Kc4di#sx +zn0$?}9Ewat=BhiSW_N#p*{;i)AzAZ#^8F-vn3GYg=NYJllGi!TIb7oNxhOJof}A&7 +zv}dM-rt37{cQSB3iVmM5p>bS3|4Pp7L%o=5i_kaq62_m5TF7Rzqx~mK!n#pQjxRy6 +zxA6?e@AgjpIMUhK3s95&?~%NsUvirN)s8tqOdY8AwC4#kP$j?s%tx`nXh8o?j@^bD +z*SJsiO%;FPlWSDT=Q&Kqdz=dj*^_nzR&p?Ox9dEra&_1UbHo}) +zzN53n9OiY840|6ZoxS}~e(-1(KC~-u3&r&0YXQMfBq;RGIoh^!Wm^zcGZvT6b~M>9 +zZ9d9vC+xO#5ArZaQxkTE_?jZrq|1>MX5T6F#-;LokruMsKJ0eKIjaPiJsOZ~&)A=a +zA_UKX)S4nwkyW^dAuovOJVV0a9@&Nf+=Ywl(ChAVM)`~s9Y@?T%zSQpXW(V@GmVg3L#5% +zNZyeYdp;30{#}Bi@|TLEeL-@D`6$}^+tSOQ7aQLt*EwCxj{Pz)`@Tudzvanwor7=B +zOS|VwmS2GyUU-W!8Iv&?lQD6_hm6Va<#6D5VF0!mW6xuk%1NjKjY`z8MXART0^2k} +z;U4(ou0_^^bVwzcYps4wI$^mhq*|gx11XEr0Xda{3iXA1ARwoZ&Q00UF8PX6&O>`A +z)h>1!MJU$Vs{?W_s=#X_>Ge}nP_(!QRcLmqmQe18Ds{<64a1(q07o^Q0pf1~1C`-* +zz*!RCd*yE~YFq>@f0>Ia!Lg~4b5Nszx|!V%_=;R}xHH=qFu-Hkz4eQd_}PrEN9_CA +zq!-Z5L5=ZLOQwIUw$CH&JWm(|`&k)s+}R8i3BCy&EP2~>w3@I|Nu2ILjWfg;7>l@$ +zfeLWD^yg{WcL%DF`W0=TU7-D}BzsF``?+Ew^r;T3P~#QvN|I(%CO?(^pO$RflH}38 +zw@);ppMmKO8zqC!M3H1#Wm->07s)m3cD*bK>#G8ENdDV}gPVj=2x{m2PNQ_}sT3A! +zu&hWZn56bQzNaP12jh5w@i}r(z4USmHUB{e?)M#Sc2m$emS!)#F;B5xNJzF2(HM){uRbpCB +z5)D0W2M@hmOq>0u&vW@u%*_H+QNuLxDX(jU>7BqorA<3ph{lIvgzrJs9VaB{dDU^P +ze~6ZKX1d4=@(}%w*gYY(&^B2cGzYdsbLy66N-Wg00NbfM3yUujP;{qWt^Jr**?ysR +z&UeI|>2nY3#NpfX7Xt$9U?$7?cc{auU#vM{OB{27Xsv$UQq8Hq?_h>)=2|~KA)Pf} +z*kpa*iow+Na5-S5W8cAYq0dXybF3tc3C*G2ArEi5^x`h9XHA#r%}G~JMpcY=$Pd)b +z&QwX_R+BzdI!Vq+%Jpnj-&fkdRQA ++ ++ ++Lua 5.5 Reference Manual - contents ++ ++ ++ ++ ++ ++ ++ ++

++Lua ++Lua 5.5 Reference Manual ++

++ ++

++The reference manual is the official definition of the Lua language. ++
++For a complete introduction to Lua programming, see the book ++Programming in Lua. ++ ++

++ ++

++ ++Copyright © 2020–2025 Lua.org, PUC-Rio. ++Freely available under the terms of the ++Lua license. ++ ++ ++

Contents

++ +-** xLock() increases the lock. xUnlock() decreases the lock. ++** xLock() upgrades the database file lock. In other words, xLock() moves the ++** database file lock in the direction NONE toward EXCLUSIVE. The argument to ++** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ++** SQLITE_LOCK_NONE. If the database file lock is already at or above the ++** requested lock, then the call to xLock() is a no-op. ++** xUnlock() downgrades the database file lock to either SHARED or NONE. ++** If the lock is already at or below the requested lock state, then the call ++** to xUnlock() is a no-op. + ** The xCheckReservedLock() method checks whether any database connection, + ** either in this process or in some other process, is holding a RESERVED, +-** PENDING, or EXCLUSIVE lock on the file. It returns true +-** if such a lock exists and false otherwise. ++** PENDING, or EXCLUSIVE lock on the file. It returns, via its output ++** pointer parameter, true if such a lock exists and false otherwise. + ** + ** The xFileControl() method is a generic interface that allows custom + ** VFS implementations to directly control an open file using the +@@ -1808,6 +1141,7 @@ struct sqlite3_file { + **
  • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] + **
  • [SQLITE_IOCAP_IMMUTABLE] + **
  • [SQLITE_IOCAP_BATCH_ATOMIC] ++**
  • [SQLITE_IOCAP_SUBPAGE_READ] + ** + ** + ** The SQLITE_IOCAP_ATOMIC property means that all writes of +@@ -1868,9 +1202,8 @@ struct sqlite3_io_methods { + ** opcode causes the xFileControl method to write the current state of + ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], + ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) +-** into an integer that the pArg argument points to. This capability +-** is used during testing and is only available when the SQLITE_TEST +-** compile-time option is used. ++** into an integer that the pArg argument points to. ++** This capability is only available if SQLite is compiled with [SQLITE_DEBUG]. + ** + **
  • [[SQLITE_FCNTL_SIZE_HINT]] + ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS +@@ -2086,6 +1419,11 @@ struct sqlite3_io_methods { + ** pointed to by the pArg argument. This capability is used during testing + ** and only needs to be supported when SQLITE_TEST is defined. + ** ++**
  • [[SQLITE_FCNTL_NULL_IO]] ++** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor ++** or file handle for the [sqlite3_file] object such that it will no longer ++** read or write to the database file. ++** + **
  • [[SQLITE_FCNTL_WAL_BLOCK]] + ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might + ** be advantageous to block on the next WAL lock if the lock is not immediately +@@ -2144,6 +1482,12 @@ struct sqlite3_io_methods { + ** the value that M is to be set to. Before returning, the 32-bit signed + ** integer is overwritten with the previous value of M. + ** ++**
  • [[SQLITE_FCNTL_BLOCK_ON_CONNECT]] ++** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the ++** VFS to block when taking a SHARED lock to connect to a wal mode database. ++** This is used to implement the functionality associated with ++** SQLITE_SETLK_BLOCK_ON_CONNECT. ++** + **
  • [[SQLITE_FCNTL_DATA_VERSION]] + ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to + ** a database file. The argument is a pointer to a 32-bit unsigned integer. +@@ -2174,6 +1518,28 @@ struct sqlite3_io_methods { + ** in wal mode after the client has finished copying pages from the wal + ** file to the database file, but before the *-shm file is updated to + ** record the fact that the pages have been checkpointed. ++** ++**
  • [[SQLITE_FCNTL_EXTERNAL_READER]] ++** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect ++** whether or not there is a database client in another process with a wal-mode ++** transaction open on the database or not. It is only available on unix.The ++** (void*) argument passed with this file-control should be a pointer to a ++** value of type (int). The integer value is set to 1 if the database is a wal ++** mode database and there exists at least one client in another process that ++** currently has an SQL transaction open on the database. It is set to 0 if ++** the database is not a wal-mode db, or if there is no such connection in any ++** other process. This opcode cannot be used to detect transactions opened ++** by clients within the current process, only within other processes. ++** ++**
  • [[SQLITE_FCNTL_CKSM_FILE]] ++** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the ++** [checksum VFS shim] only. ++** ++**
  • [[SQLITE_FCNTL_RESET_CACHE]] ++** If there is currently no transaction open on the database, and the ++** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control ++** purges the contents of the in-memory page cache. If there is an open ++** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. + ** + */ + #define SQLITE_FCNTL_LOCKSTATE 1 +@@ -2214,6 +1580,11 @@ struct sqlite3_io_methods { + #define SQLITE_FCNTL_CKPT_DONE 37 + #define SQLITE_FCNTL_RESERVE_BYTES 38 + #define SQLITE_FCNTL_CKPT_START 39 ++#define SQLITE_FCNTL_EXTERNAL_READER 40 ++#define SQLITE_FCNTL_CKSM_FILE 41 ++#define SQLITE_FCNTL_RESET_CACHE 42 ++#define SQLITE_FCNTL_NULL_IO 43 ++#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 + + /* deprecated names */ + #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE +@@ -2243,6 +1614,26 @@ typedef struct sqlite3_mutex sqlite3_mutex; + */ + typedef struct sqlite3_api_routines sqlite3_api_routines; + ++/* ++** CAPI3REF: File Name ++** ++** Type [sqlite3_filename] is used by SQLite to pass filenames to the ++** xOpen method of a [VFS]. It may be cast to (const char*) and treated ++** as a normal, nul-terminated, UTF-8 buffer containing the filename, but ++** may also be passed to special APIs such as: ++** ++**
      ++**
    • sqlite3_filename_database() ++**
    • sqlite3_filename_journal() ++**
    • sqlite3_filename_wal() ++**
    • sqlite3_uri_parameter() ++**
    • sqlite3_uri_boolean() ++**
    • sqlite3_uri_int64() ++**
    • sqlite3_uri_key() ++**
    ++*/ ++typedef const char *sqlite3_filename; ++ + /* + ** CAPI3REF: OS Interface Object + ** +@@ -2421,7 +1812,7 @@ struct sqlite3_vfs { + sqlite3_vfs *pNext; /* Next registered VFS */ + const char *zName; /* Name of this virtual file system */ + void *pAppData; /* Pointer to application-specific data */ +- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, ++ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*, + int flags, int *pOutFlags); + int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); + int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); +@@ -2608,20 +1999,23 @@ SQLITE_API int sqlite3_os_end(void); + ** must ensure that no other SQLite interfaces are invoked by other + ** threads while sqlite3_config() is running. + ** +-** The sqlite3_config() interface +-** may only be invoked prior to library initialization using +-** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. +-** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +-** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. +-** Note, however, that ^sqlite3_config() can be called as part of the +-** implementation of an application-defined [sqlite3_os_init()]. +-** + ** The first argument to sqlite3_config() is an integer + ** [configuration option] that determines + ** what property of SQLite is to be configured. Subsequent arguments + ** vary depending on the [configuration option] + ** in the first argument. + ** ++** For most configuration options, the sqlite3_config() interface ++** may only be invoked prior to library initialization using ++** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. ++** The exceptional configuration options that may be invoked at any time ++** are called "anytime configuration options". ++** ^If sqlite3_config() is called after [sqlite3_initialize()] and before ++** [sqlite3_shutdown()] with a first argument that is not an anytime ++** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. ++** Note, however, that ^sqlite3_config() can be called as part of the ++** implementation of an application-defined [sqlite3_os_init()]. ++** + ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. + ** ^If the option is unknown or SQLite is unable to set the option + ** then this routine returns a non-zero [error code]. +@@ -2729,6 +2123,23 @@ struct sqlite3_mem_methods { + ** These constants are the available integer configuration options that + ** can be passed as the first argument to the [sqlite3_config()] interface. + ** ++** Most of the configuration options for sqlite3_config() ++** will only work if invoked prior to [sqlite3_initialize()] or after ++** [sqlite3_shutdown()]. The few exceptions to this rule are called ++** "anytime configuration options". ++** ^Calling [sqlite3_config()] with a first argument that is not an ++** anytime configuration option in between calls to [sqlite3_initialize()] and ++** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE. ++** ++** The set of anytime configuration options can change (by insertions ++** and/or deletions) from one release of SQLite to the next. ++** As of SQLite version 3.42.0, the complete set of anytime configuration ++** options is: ++**
      ++**
    • SQLITE_CONFIG_LOG ++**
    • SQLITE_CONFIG_PCACHE_HDRSZ ++**
    ++** + ** New configuration options may be added in future releases of SQLite. + ** Existing configuration options might be discontinued. Applications + ** should check the return code from [sqlite3_config()] to make sure that +@@ -2904,13 +2315,16 @@ struct sqlite3_mem_methods { + ** + ** [[SQLITE_CONFIG_LOOKASIDE]]
    SQLITE_CONFIG_LOOKASIDE
    + **
    ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine +-** the default size of lookaside memory on each [database connection]. ++** the default size of [lookaside memory] on each [database connection]. + ** The first argument is the +-** size of each lookaside buffer slot and the second is the number of +-** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE +-** sets the default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] +-** option to [sqlite3_db_config()] can be used to change the lookaside +-** configuration on individual connections.)^
    ++** size of each lookaside buffer slot ("sz") and the second is the number of ++** slots allocated to each database connection ("cnt").)^ ++** ^(SQLITE_CONFIG_LOOKASIDE sets the default lookaside size. ++** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can ++** be used to change the lookaside configuration on individual connections.)^ ++** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the ++** default lookaside configuration at compile-time. ++** + ** + ** [[SQLITE_CONFIG_PCACHE2]]
    SQLITE_CONFIG_PCACHE2
    + **
    ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is +@@ -3059,7 +2473,7 @@ struct sqlite3_mem_methods { + ** is stored in each sorted record and the required column values loaded + ** from the database as records are returned in sorted order. The default + ** value for this option is to never use this optimization. Specifying a +-** negative value for this option restores the default behaviour. ++** negative value for this option restores the default behavior. + ** This option is only available if SQLite is compiled with the + ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. + ** +@@ -3073,30 +2487,46 @@ struct sqlite3_mem_methods { + ** configuration setting is never used, then the default maximum is determined + ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that + ** compile-time option is not set, then the default maximum is 1073741824. ++** ++** [[SQLITE_CONFIG_ROWID_IN_VIEW]] ++**
    SQLITE_CONFIG_ROWID_IN_VIEW ++**
    The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability ++** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is ++** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability ++** defaults to on. This configuration option queries the current setting or ++** changes the setting to off or on. The argument is a pointer to an integer. ++** If that integer initially holds a value of 1, then the ability for VIEWs to ++** have ROWIDs is activated. If the integer initially holds zero, then the ++** ability is deactivated. Any other initial value for the integer leaves the ++** setting unchanged. After changes, if any, the integer is written with ++** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite ++** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and ++** recommended case) then the integer is always filled with zero, regardless ++** if its initial value. + ** + */ +-#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +-#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +-#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +-#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +-#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +-#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ +-#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +-#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +-#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +-#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +-#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +-/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +-#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +-#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +-#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +-#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +-#define SQLITE_CONFIG_URI 17 /* int */ +-#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +-#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ ++#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ ++#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ ++#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ ++#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ ++#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ ++#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ ++#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ ++#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ ++#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ ++#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ ++#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ ++/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ ++#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ ++#define SQLITE_CONFIG_PCACHE 14 /* no-op */ ++#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ ++#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ ++#define SQLITE_CONFIG_URI 17 /* int */ ++#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ ++#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ + #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ +-#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ +-#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ ++#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ ++#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ + #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ + #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ + #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ +@@ -3104,12 +2534,21 @@ struct sqlite3_mem_methods { + #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ + #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ + #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ ++#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ + + /* + ** CAPI3REF: Database Connection Configuration Options + ** + ** These constants are the available integer configuration options that +-** can be passed as the second argument to the [sqlite3_db_config()] interface. ++** can be passed as the second parameter to the [sqlite3_db_config()] interface. ++** ++** The [sqlite3_db_config()] interface is a var-args functions. It takes a ++** variable number of parameters, though always at least two. The number of ++** parameters passed into sqlite3_db_config() depends on which of these ++** constants is given as the second parameter. This documentation page ++** refers to parameters beyond the second as "arguments". Thus, when this ++** page says "the N-th argument" it means "the N-th parameter past the ++** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()". + ** + ** New configuration options may be added in future releases of SQLite. + ** Existing configuration options might be discontinued. Applications +@@ -3121,31 +2560,57 @@ struct sqlite3_mem_methods { + **
    + ** [[SQLITE_DBCONFIG_LOOKASIDE]] + **
    SQLITE_DBCONFIG_LOOKASIDE
    +-**
    ^This option takes three additional arguments that determine the +-** [lookaside memory allocator] configuration for the [database connection]. +-** ^The first argument (the third parameter to [sqlite3_db_config()] is a ++**
    The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the ++** configuration of the [lookaside memory allocator] within a database ++** connection. ++** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are not ++** in the [DBCONFIG arguments|usual format]. ++** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two, ++** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE ++** should have a total of five parameters. ++**
      ++**
    1. The first argument ("buf") is a + ** pointer to a memory buffer to use for lookaside memory. +-** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb +-** may be NULL in which case SQLite will allocate the +-** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the +-** size of each lookaside buffer slot. ^The third argument is the number of +-** slots. The size of the buffer in the first argument must be greater than +-** or equal to the product of the second and third arguments. The buffer +-** must be aligned to an 8-byte boundary. ^If the second argument to +-** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally +-** rounded down to the next smaller multiple of 8. ^(The lookaside memory ++** The first argument may be NULL in which case SQLite will allocate the ++** lookaside buffer itself using [sqlite3_malloc()]. ++**

    2. The second argument ("sz") is the ++** size of each lookaside buffer slot. Lookaside is disabled if "sz" ++** is less than 8. The "sz" argument should be a multiple of 8 less than ++** 65536. If "sz" does not meet this constraint, it is reduced in size until ++** it does. ++**

    3. The third argument ("cnt") is the number of slots. Lookaside is disabled ++** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so ++** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" ++** parameter is usually chosen so that the product of "sz" and "cnt" is less ++** than 1,000,000. ++**

    ++**

    If the "buf" argument is not NULL, then it must ++** point to a memory buffer with a size that is greater than ++** or equal to the product of "sz" and "cnt". ++** The buffer must be aligned to an 8-byte boundary. ++** The lookaside memory + ** configuration for a database connection can only be changed when that + ** connection is not currently using lookaside memory, or in other words +-** when the "current value" returned by +-** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. ++** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero. + ** Any attempt to change the lookaside memory configuration when lookaside + ** memory is in use leaves the configuration unchanged and returns +-** [SQLITE_BUSY].)^

    ++** [SQLITE_BUSY]. ++** If the "buf" argument is NULL and an attempt ++** to allocate memory based on "sz" and "cnt" fails, then ++** lookaside is silently disabled. ++**

    ++** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the ++** default lookaside configuration at initialization. The ++** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside ++** configuration at compile-time. Typical values for lookaside are 1200 for ++** "sz" and 40 to 100 for "cnt". ++**

    + ** + ** [[SQLITE_DBCONFIG_ENABLE_FKEY]] + **
    SQLITE_DBCONFIG_ENABLE_FKEY
    + **
    ^This option is used to enable or disable the enforcement of +-** [foreign key constraints]. There should be two additional arguments. ++** [foreign key constraints]. This is the same setting that is ++** enabled or disabled by the [PRAGMA foreign_keys] statement. + ** The first argument is an integer which is 0 to disable FK enforcement, + ** positive to enable FK enforcement or negative to leave FK enforcement + ** unchanged. The second parameter is a pointer to an integer into which +@@ -3162,25 +2627,37 @@ struct sqlite3_mem_methods { + ** The second parameter is a pointer to an integer into which + ** is written 0 or 1 to indicate whether triggers are disabled or enabled + ** following this call. The second parameter may be a NULL pointer, in +-** which case the trigger setting is not reported back.
    ++** which case the trigger setting is not reported back. ++** ++**

    Originally this option disabled all triggers. ^(However, since ++** SQLite version 3.35.0, TEMP triggers are still allowed even if ++** this option is off. So, in other words, this option now only disables ++** triggers in the main database schema or in the schemas of [ATTACH]-ed ++** databases.)^ + ** + ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] + **

    SQLITE_DBCONFIG_ENABLE_VIEW
    + **
    ^This option is used to enable or disable [CREATE VIEW | views]. +-** There should be two additional arguments. ++** There must be two additional arguments. + ** The first argument is an integer which is 0 to disable views, + ** positive to enable views or negative to leave the setting unchanged. + ** The second parameter is a pointer to an integer into which + ** is written 0 or 1 to indicate whether views are disabled or enabled + ** following this call. The second parameter may be a NULL pointer, in +-** which case the view setting is not reported back.
    ++** which case the view setting is not reported back. ++** ++**

    Originally this option disabled all views. ^(However, since ++** SQLite version 3.35.0, TEMP views are still allowed even if ++** this option is off. So, in other words, this option now only disables ++** views in the main database schema or in the schemas of ATTACH-ed ++** databases.)^ + ** + ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] + **

    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
    + **
    ^This option is used to enable or disable the + ** [fts3_tokenizer()] function which is part of the + ** [FTS3] full-text search engine extension. +-** There should be two additional arguments. ++** There must be two additional arguments. + ** The first argument is an integer which is 0 to disable fts3_tokenizer() or + ** positive to enable fts3_tokenizer() or negative to leave the setting + ** unchanged. +@@ -3195,7 +2672,7 @@ struct sqlite3_mem_methods { + ** interface independently of the [load_extension()] SQL function. + ** The [sqlite3_enable_load_extension()] API enables or disables both the + ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. +-** There should be two additional arguments. ++** There must be two additional arguments. + ** When the first argument to this interface is 1, then only the C-API is + ** enabled and the SQL function remains disabled. If the first argument to + ** this interface is 0, then both the C-API and the SQL function are disabled. +@@ -3209,23 +2686,30 @@ struct sqlite3_mem_methods { + ** + ** [[SQLITE_DBCONFIG_MAINDBNAME]]
    SQLITE_DBCONFIG_MAINDBNAME
    + **
    ^This option is used to change the name of the "main" database +-** schema. ^The sole argument is a pointer to a constant UTF8 string +-** which will become the new schema name in place of "main". ^SQLite +-** does not make a copy of the new main schema name string, so the application +-** must ensure that the argument passed into this DBCONFIG option is unchanged +-** until after the database connection closes. ++** schema. This option does not follow the ++** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. ++** This option takes exactly one additional argument so that the ++** [sqlite3_db_config()] call has a total of three parameters. The ++** extra argument must be a pointer to a constant UTF8 string which ++** will become the new schema name in place of "main". ^SQLite does ++** not make a copy of the new main schema name string, so the application ++** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME ++** is unchanged until after the database connection closes. + **
    + ** + ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] + **
    SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
    +-**
    Usually, when a database in wal mode is closed or detached from a +-** database handle, SQLite checks if this will mean that there are now no +-** connections at all to the database. If so, it performs a checkpoint +-** operation before closing the connection. This option may be used to +-** override this behaviour. The first parameter passed to this operation +-** is an integer - positive to disable checkpoints-on-close, or zero (the +-** default) to enable them, and negative to leave the setting unchanged. +-** The second parameter is a pointer to an integer ++**
    Usually, when a database in [WAL mode] is closed or detached from a ++** database handle, SQLite checks if if there are other connections to the ++** same database, and if there are no other database connection (if the ++** connection being closed is the last open connection to the database), ++** then SQLite performs a [checkpoint] before closing the connection and ++** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can ++** be used to override that behavior. The first argument passed to this ++** operation (the third parameter to [sqlite3_db_config()]) is an integer ++** which is positive to disable checkpoints-on-close, or zero (the default) ++** to enable them, and negative to leave the setting unchanged. ++** The second argument (the fourth parameter) is a pointer to an integer + ** into which is written 0 or 1 to indicate whether checkpoints-on-close + ** have been disabled - 0 if they are not disabled, 1 if they are. + **
    +@@ -3275,8 +2759,12 @@ struct sqlite3_mem_methods { + **
  • sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); + ** + ** Because resetting a database is destructive and irreversible, the +-** process requires the use of this obscure API and multiple steps to help +-** ensure that it does not happen by accident. ++** process requires the use of this obscure API and multiple steps to ++** help ensure that it does not happen by accident. Because this ++** feature must be capable of resetting corrupt databases, and ++** shutting down virtual tables may require access to that corrupt ++** storage, the library must abandon any installed virtual tables ++** without calling their xDestroy() methods. + ** + ** [[SQLITE_DBCONFIG_DEFENSIVE]]
    SQLITE_DBCONFIG_DEFENSIVE
    + **
    The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the +@@ -3287,6 +2775,7 @@ struct sqlite3_mem_methods { + **
      + **
    • The [PRAGMA writable_schema=ON] statement. + **
    • The [PRAGMA journal_mode=OFF] statement. ++**
    • The [PRAGMA schema_version=N] statement. + **
    • Writes to the [sqlite_dbpage] virtual table. + **
    • Direct writes to [shadow tables]. + **
    +@@ -3314,7 +2803,7 @@ struct sqlite3_mem_methods { + **
    + ** + ** [[SQLITE_DBCONFIG_DQS_DML]] +-**
    SQLITE_DBCONFIG_DQS_DML ++**
    SQLITE_DBCONFIG_DQS_DML
    + **
    The SQLITE_DBCONFIG_DQS_DML option activates or deactivates + ** the legacy [double-quoted string literal] misfeature for DML statements + ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The +@@ -3323,7 +2812,7 @@ struct sqlite3_mem_methods { + **
    + ** + ** [[SQLITE_DBCONFIG_DQS_DDL]] +-**
    SQLITE_DBCONFIG_DQS_DDL ++**
    SQLITE_DBCONFIG_DQS_DDL
    + **
    The SQLITE_DBCONFIG_DQS option activates or deactivates + ** the legacy [double-quoted string literal] misfeature for DDL statements, + ** such as CREATE TABLE and CREATE INDEX. The +@@ -3332,7 +2821,7 @@ struct sqlite3_mem_methods { + **
    + ** + ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] +-**
    SQLITE_DBCONFIG_TRUSTED_SCHEMA ++**
    SQLITE_DBCONFIG_TRUSTED_SCHEMA
    + **
    The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to + ** assume that database schemas are untainted by malicious content. + ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite +@@ -3352,7 +2841,7 @@ struct sqlite3_mem_methods { + **
    + ** + ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] +-**
    SQLITE_DBCONFIG_LEGACY_FILE_FORMAT ++**
    SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
    + **
    The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates + ** the legacy file format flag. When activated, this flag causes all newly + ** created database file to have a schema format version number (the 4-byte +@@ -3361,7 +2850,7 @@ struct sqlite3_mem_methods { + ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, + ** newly created databases are generally not understandable by SQLite versions + ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there +-** is now scarcely any need to generated database files that are compatible ++** is now scarcely any need to generate database files that are compatible + ** all the way back to version 3.0.0, and so this setting is of little + ** practical use, but is provided so that SQLite can continue to claim the + ** ability to generate new database files that are compatible with version +@@ -3370,9 +2859,110 @@ struct sqlite3_mem_methods { + ** the [VACUUM] command will fail with an obscure error when attempting to + ** process a table with generated columns and a descending index. This is + ** not considered a bug since SQLite versions 3.3.0 and earlier do not support +-** either generated columns or decending indexes. ++** either generated columns or descending indexes. ++**
    ++** ++** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] ++**
    SQLITE_DBCONFIG_STMT_SCANSTATUS
    ++**
    The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in ++** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears ++** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() ++** statistics. For statistics to be collected, the flag must be set on ++** the database handle both when the SQL statement is prepared and when it ++** is stepped. The flag is set (collection of statistics is enabled) ++** by default.

    This option takes two arguments: an integer and a pointer to ++** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the statement scanstatus option. If the second argument ++** is not NULL, then the value of the statement scanstatus setting after ++** processing the first argument is written into the integer that the second ++** argument points to. ++**

    ++** ++** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] ++**
    SQLITE_DBCONFIG_REVERSE_SCANORDER
    ++**
    The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order ++** in which tables and indexes are scanned so that the scans start at the end ++** and work toward the beginning rather than starting at the beginning and ++** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the ++** same as setting [PRAGMA reverse_unordered_selects].

    This option takes ++** two arguments which are an integer and a pointer to an integer. The first ++** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ++** reverse scan order flag, respectively. If the second argument is not NULL, ++** then 0 or 1 is written into the integer that the second argument points to ++** depending on if the reverse scan order flag is set after processing the ++** first argument. ++**

    ++** ++** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] ++**
    SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
    ++**
    The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables ++** the ability of the [ATTACH DATABASE] SQL command to create a new database ++** file if the database filed named in the ATTACH command does not already ++** exist. This ability of ATTACH to create a new database is enabled by ++** default. Applications can disable or reenable the ability for ATTACH to ++** create new database files using this DBCONFIG option.

    ++** This option takes two arguments which are an integer and a pointer ++** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the attach-create flag, respectively. If the second ++** argument is not NULL, then 0 or 1 is written into the integer that the ++** second argument points to depending on if the attach-create flag is set ++** after processing the first argument. ++**

    ++** ++** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] ++**
    SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
    ++**
    The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the ++** ability of the [ATTACH DATABASE] SQL command to open a database for writing. ++** This capability is enabled by default. Applications can disable or ++** reenable this capability using the current DBCONFIG option. If the ++** the this capability is disabled, the [ATTACH] command will still work, ++** but the database will be opened read-only. If this option is disabled, ++** then the ability to create a new database using [ATTACH] is also disabled, ++** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] ++** option.

    ++** This option takes two arguments which are an integer and a pointer ++** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the ability to ATTACH another database for writing, ++** respectively. If the second argument is not NULL, then 0 or 1 is written ++** into the integer to which the second argument points, depending on whether ++** the ability to ATTACH a read/write database is enabled or disabled ++** after processing the first argument. ++**

    ++** ++** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] ++**
    SQLITE_DBCONFIG_ENABLE_COMMENTS
    ++**
    The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the ++** ability to include comments in SQL text. Comments are enabled by default. ++** An application can disable or reenable comments in SQL text using this ++** DBCONFIG option.

    ++** This option takes two arguments which are an integer and a pointer ++** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the ability to use comments in SQL text, ++** respectively. If the second argument is not NULL, then 0 or 1 is written ++** into the integer that the second argument points to depending on if ++** comments are allowed in SQL text after processing the first argument. + **

    ++** + ** ++** ++** [[DBCONFIG arguments]]

    Arguments To SQLITE_DBCONFIG Options

    ++** ++**

    Most of the SQLITE_DBCONFIG options take two arguments, so that the ++** overall call to [sqlite3_db_config()] has a total of four parameters. ++** The first argument (the third parameter to sqlite3_db_config()) is a integer. ++** The second argument is a pointer to an integer. If the first argument is 1, ++** then the option becomes enabled. If the first integer argument is 0, then the ++** option is disabled. If the first argument is -1, then the option setting ++** is unchanged. The second argument, the pointer to an integer, may be NULL. ++** If the second argument is not NULL, then a value of 0 or 1 is written into ++** the integer to which the second argument points, depending on whether the ++** setting is disabled or enabled after applying any changes specified by ++** the first argument. ++** ++**

    While most SQLITE_DBCONFIG options use the argument format ++** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] ++** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the ++** documentation of those exceptional options for details. + */ + #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ + #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +@@ -3392,7 +2982,12 @@ struct sqlite3_mem_methods { + #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ + #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ + #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ +-#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */ ++#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ ++#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ ++#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ ++#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ ++#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ ++#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ + + /* + ** CAPI3REF: Enable Or Disable Extended Result Codes +@@ -3480,11 +3075,18 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); + ** CAPI3REF: Count The Number Of Rows Modified + ** METHOD: sqlite3 + ** +-** ^This function returns the number of rows modified, inserted or ++** ^These functions return the number of rows modified, inserted or + ** deleted by the most recently completed INSERT, UPDATE or DELETE + ** statement on the database connection specified by the only parameter. +-** ^Executing any other type of SQL statement does not modify the value +-** returned by this function. ++** The two functions are identical except for the type of the return value ++** and that if the number of rows modified by the most recent INSERT, UPDATE, ++** or DELETE is greater than the maximum value supported by type "int", then ++** the return value of sqlite3_changes() is undefined. ^Executing any other ++** type of SQL statement does not modify the value returned by these functions. ++** For the purposes of this interface, a CREATE TABLE AS SELECT statement ++** does not count as an INSERT, UPDATE or DELETE statement and hence the rows ++** added to the new table by the CREATE TABLE AS SELECT statement are not ++** counted. + ** + ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are + ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], +@@ -3533,16 +3135,21 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); + ** + */ + SQLITE_API int sqlite3_changes(sqlite3*); ++SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*); + + /* + ** CAPI3REF: Total Number Of Rows Modified + ** METHOD: sqlite3 + ** +-** ^This function returns the total number of rows inserted, modified or ++** ^These functions return the total number of rows inserted, modified or + ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed + ** since the database connection was opened, including those executed as +-** part of trigger programs. ^Executing any other type of SQL statement +-** does not affect the value returned by sqlite3_total_changes(). ++** part of trigger programs. The two functions are identical except for the ++** type of the return value and that if the number of rows modified by the ++** connection exceeds the maximum value supported by type "int", then ++** the return value of sqlite3_total_changes() is undefined. ^Executing ++** any other type of SQL statement does not affect the value returned by ++** sqlite3_total_changes(). + ** + ** ^Changes made as part of [foreign key actions] are included in the + ** count, but those made as part of REPLACE constraint resolution are +@@ -3570,6 +3177,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); + ** + */ + SQLITE_API int sqlite3_total_changes(sqlite3*); ++SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); + + /* + ** CAPI3REF: Interrupt A Long-Running Query +@@ -3605,8 +3213,13 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); + ** ^A call to sqlite3_interrupt(D) that occurs when there are no running + ** SQL statements is a no-op and has no effect on SQL statements + ** that are started after the sqlite3_interrupt() call returns. ++** ++** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether ++** or not an interrupt is currently in effect for [database connection] D. ++** It returns 1 if an interrupt is currently in effect, or 0 otherwise. + */ + SQLITE_API void sqlite3_interrupt(sqlite3*); ++SQLITE_API int sqlite3_is_interrupted(sqlite3*); + + /* + ** CAPI3REF: Determine If An SQL Statement Is Complete +@@ -3728,6 +3341,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*); + */ + SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); + ++/* ++** CAPI3REF: Set the Setlk Timeout ++** METHOD: sqlite3 ++** ++** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If ++** the VFS supports blocking locks, it sets the timeout in ms used by ++** eligible locks taken on wal mode databases by the specified database ++** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does ++** not support blocking locks, this function is a no-op. ++** ++** Passing 0 to this function disables blocking locks altogether. Passing ++** -1 to this function requests that the VFS blocks for a long time - ++** indefinitely if possible. The results of passing any other negative value ++** are undefined. ++** ++** Internally, each SQLite database handle store two timeout values - the ++** busy-timeout (used for rollback mode databases, or if the VFS does not ++** support blocking locks) and the setlk-timeout (used for blocking locks ++** on wal-mode databases). The sqlite3_busy_timeout() method sets both ++** values, this function sets only the setlk-timeout value. Therefore, ++** to configure separate busy-timeout and setlk-timeout values for a single ++** database handle, call sqlite3_busy_timeout() followed by this function. ++** ++** Whenever the number of connections to a wal mode database falls from ++** 1 to 0, the last connection takes an exclusive lock on the database, ++** then checkpoints and deletes the wal file. While it is doing this, any ++** new connection that tries to read from the database fails with an ++** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is ++** passed to this API, the new connection blocks until the exclusive lock ++** has been released. ++*/ ++SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); ++ ++/* ++** CAPI3REF: Flags for sqlite3_setlk_timeout() ++*/ ++#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 ++ + /* + ** CAPI3REF: Convenience Routines For Running Queries + ** METHOD: sqlite3 +@@ -4153,8 +3804,8 @@ SQLITE_API int sqlite3_set_authorizer( + #define SQLITE_RECURSIVE 33 /* NULL NULL */ + + /* +-** CAPI3REF: Tracing And Profiling Functions +-** METHOD: sqlite3 ++** CAPI3REF: Deprecated Tracing And Profiling Functions ++** DEPRECATED + ** + ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface + ** instead of the routines described here. +@@ -4224,8 +3875,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, + **

    ^An SQLITE_TRACE_PROFILE callback provides approximately the same + ** information as is provided by the [sqlite3_profile()] callback. + ** ^The P argument is a pointer to the [prepared statement] and the +-** X argument points to a 64-bit integer which is the estimated of +-** the number of nanosecond that the prepared statement took to run. ++** X argument points to a 64-bit integer which is approximately ++** the number of nanoseconds that the prepared statement took to run. + ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. + ** + ** [[SQLITE_TRACE_ROW]]
    SQLITE_TRACE_ROW
    +@@ -4257,8 +3908,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, + ** M argument should be the bitwise OR-ed combination of + ** zero or more [SQLITE_TRACE] constants. + ** +-** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides +-** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). ++** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P) ++** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or ++** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each ++** database connection may have at most one trace callback. + ** + ** ^The X callback is invoked whenever any of the events identified by + ** mask M occur. ^The integer return value from the callback is currently +@@ -4288,7 +3941,7 @@ SQLITE_API int sqlite3_trace_v2( + ** + ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback + ** function X to be invoked periodically during long running calls to +-** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for ++** [sqlite3_step()] and [sqlite3_prepare()] and similar for + ** database connection D. An example use for this + ** interface is to keep a GUI updated during a large query. + ** +@@ -4313,6 +3966,13 @@ SQLITE_API int sqlite3_trace_v2( + ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their + ** database connections for the meaning of "modify" in this paragraph. + ** ++** The progress handler callback would originally only be invoked from the ++** bytecode engine. It still might be invoked during [sqlite3_prepare()] ++** and similar because those routines might force a reparse of the schema ++** which involves running the bytecode engine. However, beginning with ++** SQLite version 3.41.0, the progress handler callback might also be ++** invoked directly from [sqlite3_prepare()] while analyzing and generating ++** code for complex queries. + */ + SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + +@@ -4349,13 +4009,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + ** + **
    + ** ^(
    [SQLITE_OPEN_READONLY]
    +-**
    The database is opened in read-only mode. If the database does not +-** already exist, an error is returned.
    )^ ++**
    The database is opened in read-only mode. If the database does ++** not already exist, an error is returned.
    )^ + ** + ** ^(
    [SQLITE_OPEN_READWRITE]
    +-**
    The database is opened for reading and writing if possible, or reading +-** only if the file is write protected by the operating system. In either +-** case the database must already exist, otherwise an error is returned.
    )^ ++**
    The database is opened for reading and writing if possible, or ++** reading only if the file is write protected by the operating ++** system. In either case the database must already exist, otherwise ++** an error is returned. For historical reasons, if opening in ++** read-write mode fails due to OS-level permissions, an attempt is ++** made to open it in read-only mode. [sqlite3_db_readonly()] can be ++** used to determine whether the database is actually ++** read-write.
    )^ + ** + ** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    + **
    The database is opened for reading and writing, and is created if +@@ -4393,20 +4058,39 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + **
    The database is opened [shared cache] enabled, overriding + ** the default shared cache setting provided by + ** [sqlite3_enable_shared_cache()].)^ ++** The [use of shared cache mode is discouraged] and hence shared cache ++** capabilities may be omitted from many builds of SQLite. In such cases, ++** this option is a no-op. + ** + ** ^(
    [SQLITE_OPEN_PRIVATECACHE]
    + **
    The database is opened [shared cache] disabled, overriding + ** the default shared cache setting provided by + ** [sqlite3_enable_shared_cache()].)^ + ** ++** [[OPEN_EXRESCODE]] ^(
    [SQLITE_OPEN_EXRESCODE]
    ++**
    The database connection comes up in "extended result code mode". ++** In other words, the database behaves as if ++** [sqlite3_extended_result_codes(db,1)] were called on the database ++** connection as soon as the connection is created. In addition to setting ++** the extended result code mode, this flag also causes [sqlite3_open_v2()] ++** to return an extended result code.
    ++** + ** [[OPEN_NOFOLLOW]] ^(
    [SQLITE_OPEN_NOFOLLOW]
    +-**
    The database filename is not allowed to be a symbolic link
    ++**
    The database filename is not allowed to contain a symbolic link
    + **
    )^ + ** + ** If the 3rd parameter to sqlite3_open_v2() is not one of the + ** required combinations shown above optionally combined with other + ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] +-** then the behavior is undefined. ++** then the behavior is undefined. Historic versions of SQLite ++** have silently ignored surplus bits in the flags parameter to ++** sqlite3_open_v2(), however that behavior might not be carried through ++** into future versions of SQLite and so applications should not rely ++** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op ++** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause ++** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE ++** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not ++** by sqlite3_open_v2(). + ** + ** ^The fourth parameter to sqlite3_open_v2() is the name of the + ** [sqlite3_vfs] object that defines the operating system interface that +@@ -4546,6 +4230,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + ** that uses dot-files in place of posix advisory locking. + ** file:data.db?mode=readonly + ** An error. "readonly" is not a valid option for the "mode" parameter. ++** Use "ro" instead: "file:data.db?mode=ro". + ** + ** + ** ^URI hexadecimal escape sequences (%HH) are supported within the path and +@@ -4595,7 +4280,7 @@ SQLITE_API int sqlite3_open_v2( + ** as F) must be one of: + **
      + **
    • A database filename pointer created by the SQLite core and +-** passed into the xOpen() method of a VFS implemention, or ++** passed into the xOpen() method of a VFS implementation, or + **
    • A filename obtained from [sqlite3_db_filename()], or + **
    • A new filename constructed using [sqlite3_create_filename()]. + **
    +@@ -4650,10 +4335,10 @@ SQLITE_API int sqlite3_open_v2( + ** + ** See the [URI filename] documentation for additional information. + */ +-SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); +-SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +-SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); +-SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); ++SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam); ++SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault); ++SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64); ++SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N); + + /* + ** CAPI3REF: Translate filenames +@@ -4682,9 +4367,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); + ** return value from [sqlite3_db_filename()], then the result is + ** undefined and is likely a memory access violation. + */ +-SQLITE_API const char *sqlite3_filename_database(const char*); +-SQLITE_API const char *sqlite3_filename_journal(const char*); +-SQLITE_API const char *sqlite3_filename_wal(const char*); ++SQLITE_API const char *sqlite3_filename_database(sqlite3_filename); ++SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename); ++SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename); + + /* + ** CAPI3REF: Database File Corresponding To A Journal +@@ -4708,12 +4393,12 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); + /* + ** CAPI3REF: Create and Destroy VFS Filenames + ** +-** These interfces are provided for use by [VFS shim] implementations and ++** These interfaces are provided for use by [VFS shim] implementations and + ** are not useful outside of that context. + ** + ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of + ** database filename D with corresponding journal file J and WAL file W and +-** with N URI parameters key/values pairs in the array P. The result from ++** an array P of N URI Key/Value pairs. The result from + ** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that + ** is safe to pass to routines like: + **
      +@@ -4744,20 +4429,20 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); + ** If the Y parameter to sqlite3_free_filename(Y) is anything other + ** than a NULL pointer or a pointer previously acquired from + ** sqlite3_create_filename(), then bad things such as heap +-** corruption or segfaults may occur. The value Y should be ++** corruption or segfaults may occur. The value Y should not be + ** used again after sqlite3_free_filename(Y) has been called. This means + ** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y, + ** then the corresponding [sqlite3_module.xClose() method should also be + ** invoked prior to calling sqlite3_free_filename(Y). + */ +-SQLITE_API char *sqlite3_create_filename( ++SQLITE_API sqlite3_filename sqlite3_create_filename( + const char *zDatabase, + const char *zJournal, + const char *zWal, + int nParam, + const char **azParam + ); +-SQLITE_API void sqlite3_free_filename(char*); ++SQLITE_API void sqlite3_free_filename(sqlite3_filename); + + /* + ** CAPI3REF: Error Codes And Messages +@@ -4776,27 +4461,38 @@ SQLITE_API void sqlite3_free_filename(char*); + ** sqlite3_extended_errcode() might change with each API call. + ** Except, there are some interfaces that are guaranteed to never + ** change the value of the error code. The error-code preserving +-** interfaces are: ++** interfaces include the following: + ** + **
        + **
      • sqlite3_errcode() + **
      • sqlite3_extended_errcode() + **
      • sqlite3_errmsg() + **
      • sqlite3_errmsg16() ++**
      • sqlite3_error_offset() + **
      + ** + ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language +-** text that describes the error, as either UTF-8 or UTF-16 respectively. ++** text that describes the error, as either UTF-8 or UTF-16 respectively, ++** or NULL if no error message is available. ++** (See how SQLite handles [invalid UTF] for exceptions to this rule.) + ** ^(Memory to hold the error message string is managed internally. + ** The application does not need to worry about freeing the result. + ** However, the error string might be overwritten or deallocated by + ** subsequent calls to other SQLite interface functions.)^ + ** +-** ^The sqlite3_errstr() interface returns the English-language text +-** that describes the [result code], as UTF-8. ++** ^The sqlite3_errstr(E) interface returns the English-language text ++** that describes the [result code] E, as UTF-8, or NULL if E is not an ++** result code for which a text error message is available. + ** ^(Memory to hold the error message string is managed internally + ** and must not be freed by the application)^. + ** ++** ^If the most recent error references a specific token in the input ++** SQL, the sqlite3_error_offset() interface returns the byte offset ++** of the start of that token. ^The byte offset returned by ++** sqlite3_error_offset() assumes that the input SQL is UTF8. ++** ^If the most recent error does not reference a specific token in the input ++** SQL, then the sqlite3_error_offset() function returns -1. ++** + ** When the serialized [threading mode] is in use, it might be the + ** case that a second error occurs on a separate thread in between + ** the time of the first error and the call to these interfaces. +@@ -4816,6 +4512,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); + SQLITE_API const char *sqlite3_errmsg(sqlite3*); + SQLITE_API const void *sqlite3_errmsg16(sqlite3*); + SQLITE_API const char *sqlite3_errstr(int); ++SQLITE_API int sqlite3_error_offset(sqlite3 *db); + + /* + ** CAPI3REF: Prepared Statement Object +@@ -4987,11 +4684,22 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); + **
      The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler + ** to return an error (error code SQLITE_ERROR) if the statement uses + ** any virtual tables. ++** ++** [[SQLITE_PREPARE_DONT_LOG]]
      SQLITE_PREPARE_DONT_LOG
      ++**
      The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler ++** errors from being sent to the error log defined by ++** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test ++** compiles to see if some SQL syntax is well-formed, without generating ++** messages on the global error log when it is not. If the test compile ++** fails, the sqlite3_prepare_v3() call returns the same error indications ++** with or without this flag; it just omits the call to [sqlite3_log()] that ++** logs the error. + ** + */ + #define SQLITE_PREPARE_PERSISTENT 0x01 + #define SQLITE_PREPARE_NORMALIZE 0x02 + #define SQLITE_PREPARE_NO_VTAB 0x04 ++#define SQLITE_PREPARE_DONT_LOG 0x10 + + /* + ** CAPI3REF: Compiling An SQL Statement +@@ -5024,13 +4732,17 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); + ** and sqlite3_prepare16_v3() use UTF-16. + ** + ** ^If the nByte argument is negative, then zSql is read up to the +-** first zero terminator. ^If nByte is positive, then it is the +-** number of bytes read from zSql. ^If nByte is zero, then no prepared ++** first zero terminator. ^If nByte is positive, then it is the maximum ++** number of bytes read from zSql. When nByte is positive, zSql is read ++** up to the first zero terminator or until the nByte bytes have been read, ++** whichever comes first. ^If nByte is zero, then no prepared + ** statement is generated. + ** If the caller knows that the supplied string is nul-terminated, then + ** there is a small performance advantage to passing an nByte parameter that + ** is the number of bytes in the input string including + ** the nul-terminator. ++** Note that nByte measure the length of the input in bytes, not ++** characters, even for the UTF-16 interfaces. + ** + ** ^If pzTail is not NULL then *pzTail is made to point to the first byte + ** past the end of the first SQL statement in zSql. These routines only +@@ -5173,12 +4885,17 @@ SQLITE_API int sqlite3_prepare16_v3( + ** are managed by SQLite and are automatically freed when the prepared + ** statement is finalized. + ** ^The string returned by sqlite3_expanded_sql(P), on the other hand, +-** is obtained from [sqlite3_malloc()] and must be free by the application ++** is obtained from [sqlite3_malloc()] and must be freed by the application + ** by passing it to [sqlite3_free()]. ++** ++** ^The sqlite3_normalized_sql() interface is only available if ++** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. + */ + SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); + SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); ++#ifdef SQLITE_ENABLE_NORMALIZE + SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); ++#endif + + /* + ** CAPI3REF: Determine If An SQL Statement Writes The Database +@@ -5213,6 +4930,19 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); + ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and + ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so + ** sqlite3_stmt_readonly() returns false for those commands. ++** ++** ^This routine returns false if there is any possibility that the ++** statement might change the database file. ^A false return does ++** not guarantee that the statement will change the database file. ++** ^For example, an UPDATE statement might have a WHERE clause that ++** makes it a no-op, but the sqlite3_stmt_readonly() result would still ++** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a ++** read-only no-op if the table already exists, but ++** sqlite3_stmt_readonly() still returns false for such a statement. ++** ++** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] ++** statement, then sqlite3_stmt_readonly(X) returns the same value as ++** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. + */ + SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); + +@@ -5228,6 +4958,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); + */ + SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); + ++/* ++** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement ++** METHOD: sqlite3_stmt ++** ++** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN ++** setting for [prepared statement] S. If E is zero, then S becomes ++** a normal prepared statement. If E is 1, then S behaves as if ++** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if ++** its SQL text began with "[EXPLAIN QUERY PLAN]". ++** ++** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared. ++** SQLite tries to avoid a reprepare, but a reprepare might be necessary ++** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode. ++** ++** Because of the potential need to reprepare, a call to ++** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be ++** reprepared because it was created using [sqlite3_prepare()] instead of ++** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and ++** hence has no saved SQL text with which to reprepare. ++** ++** Changing the explain setting for a prepared statement does not change ++** the original SQL text for the statement. Hence, if the SQL text originally ++** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) ++** is called to convert the statement into an ordinary statement, the EXPLAIN ++** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) ++** output, even though the statement now acts like a normal SQL statement. ++** ++** This routine returns SQLITE_OK if the explain mode is successfully ++** changed, or an error code if the explain mode could not be changed. ++** The explain mode cannot be changed while a statement is active. ++** Hence, it is good practice to call [sqlite3_reset(S)] ++** immediately prior to calling sqlite3_stmt_explain(S,E). ++*/ ++SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode); ++ + /* + ** CAPI3REF: Determine If A Prepared Statement Has Been Reset + ** METHOD: sqlite3_stmt +@@ -5281,6 +5046,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + ** + ** ^The sqlite3_value objects that are passed as parameters into the + ** implementation of [application-defined SQL functions] are protected. ++** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()] ++** are protected. + ** ^The sqlite3_value object returned by + ** [sqlite3_column_value()] is unprotected. + ** Unprotected sqlite3_value objects may only be used as arguments +@@ -5312,7 +5079,7 @@ typedef struct sqlite3_context sqlite3_context; + ** METHOD: sqlite3_stmt + ** + ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, +-** literals may be replaced by a [parameter] that matches one of following ++** literals may be replaced by a [parameter] that matches one of the following + ** templates: + ** + **
        +@@ -5357,7 +5124,7 @@ typedef struct sqlite3_context sqlite3_context; + ** + ** [[byte-order determination rules]] ^The byte-order of + ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) +-** found in first character, which is removed, or in the absence of a BOM ++** found in the first character, which is removed, or in the absence of a BOM + ** the byte order is the native byte order of the host + ** machine for sqlite3_bind_text16() or the byte order specified in + ** the 6th parameter for sqlite3_bind_text64().)^ +@@ -5377,23 +5144,27 @@ typedef struct sqlite3_context sqlite3_context; + ** or sqlite3_bind_text16() or sqlite3_bind_text64() then + ** that parameter must be the byte offset + ** where the NUL terminator would occur assuming the string were NUL +-** terminated. If any NUL characters occurs at byte offsets less than ++** terminated. If any NUL characters occur at byte offsets less than + ** the value of the fourth parameter then the resulting string value will + ** contain embedded NULs. The result of expressions involving strings + ** with embedded NULs is undefined. + ** +-** ^The fifth argument to the BLOB and string binding interfaces +-** is a destructor used to dispose of the BLOB or +-** string after SQLite has finished with it. ^The destructor is called +-** to dispose of the BLOB or string even if the call to the bind API fails, +-** except the destructor is not called if the third parameter is a NULL +-** pointer or the fourth parameter is negative. +-** ^If the fifth argument is +-** the special value [SQLITE_STATIC], then SQLite assumes that the +-** information is in static, unmanaged space and does not need to be freed. +-** ^If the fifth argument has the value [SQLITE_TRANSIENT], then +-** SQLite makes its own private copy of the data immediately, before +-** the sqlite3_bind_*() routine returns. ++** ^The fifth argument to the BLOB and string binding interfaces controls ++** or indicates the lifetime of the object referenced by the third parameter. ++** These three options exist: ++** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished ++** with it may be passed. ^It is called to dispose of the BLOB or string even ++** if the call to the bind API fails, except the destructor is not called if ++** the third parameter is a NULL pointer or the fourth parameter is negative. ++** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that ++** the application remains responsible for disposing of the object. ^In this ++** case, the object and the provided pointer to it must remain valid until ++** either the prepared statement is finalized or the same SQL parameter is ++** bound to something else, whichever occurs sooner. ++** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the ++** object is to be copied prior to the return from sqlite3_bind_*(). ^The ++** object and pointer to it must remain valid until then. ^SQLite will then ++** manage the lifetime of its private copy. + ** + ** ^The sixth argument to sqlite3_bind_text64() must be one of + ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] +@@ -5585,7 +5356,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); + ** METHOD: sqlite3_stmt + ** + ** ^These routines provide a means to determine the database, table, and +-** table column that is the origin of a particular result column in ++** table column that is the origin of a particular result column in a + ** [SELECT] statement. + ** ^The name of the database or table or column can be returned as + ** either a UTF-8 or UTF-16 string. ^The _database_ routines return +@@ -5723,7 +5494,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); + ** other than [SQLITE_ROW] before any subsequent invocation of + ** sqlite3_step(). Failure to reset the prepared statement using + ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from +-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], ++** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), + ** sqlite3_step() began + ** calling [sqlite3_reset()] automatically in this circumstance rather + ** than returning [SQLITE_MISUSE]. This is not considered a compatibility +@@ -5898,6 +5669,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + ** even empty strings, are always zero-terminated. ^The return + ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. + ** ++** ^Strings returned by sqlite3_column_text16() always have the endianness ++** which is native to the platform, regardless of the text encoding set ++** for the database. ++** + ** Warning: ^The object returned by [sqlite3_column_value()] is an + ** [unprotected sqlite3_value] object. In a multithreaded environment, + ** an unprotected sqlite3_value object may only be used safely with +@@ -5911,7 +5686,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + ** [application-defined SQL functions] or [virtual tables], not within + ** top-level application code. + ** +-** The these routines may attempt to convert the datatype of the result. ++** These routines may attempt to convert the datatype of the result. + ** ^For example, if the internal representation is FLOAT and a text result + ** is requested, [sqlite3_snprintf()] is used internally to perform the + ** conversion automatically. ^(The following table details the conversions +@@ -5936,7 +5711,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + ** TEXT BLOB No change + ** BLOB INTEGER [CAST] to INTEGER + ** BLOB FLOAT [CAST] to REAL +-** BLOB TEXT Add a zero terminator if needed ++** BLOB TEXT [CAST] to TEXT, ensure zero terminator + ** + ** )^ + ** +@@ -6060,20 +5835,33 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); + ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S + ** back to the beginning of its program. + ** +-** ^If the most recent call to [sqlite3_step(S)] for the +-** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +-** or if [sqlite3_step(S)] has never before been called on S, +-** then [sqlite3_reset(S)] returns [SQLITE_OK]. ++** ^The return code from [sqlite3_reset(S)] indicates whether or not ++** the previous evaluation of prepared statement S completed successfully. ++** ^If [sqlite3_step(S)] has never before been called on S or if ++** [sqlite3_step(S)] has not been called since the previous call ++** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return ++** [SQLITE_OK]. + ** + ** ^If the most recent call to [sqlite3_step(S)] for the + ** [prepared statement] S indicated an error, then + ** [sqlite3_reset(S)] returns an appropriate [error code]. ++** ^The [sqlite3_reset(S)] interface might also return an [error code] ++** if there were no prior errors but the process of resetting ++** the prepared statement caused a new error. ^For example, if an ++** [INSERT] statement with a [RETURNING] clause is only stepped one time, ++** that one call to [sqlite3_step(S)] might return SQLITE_ROW but ++** the overall statement might still fail and the [sqlite3_reset(S)] call ++** might return SQLITE_BUSY if locking constraints prevent the ++** database change from committing. Therefore, it is important that ++** applications check the return code from [sqlite3_reset(S)] even if ++** no prior call to [sqlite3_step(S)] indicated a problem. + ** + ** ^The [sqlite3_reset(S)] interface does not change the values + ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. + */ + SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + ++ + /* + ** CAPI3REF: Create Or Redefine SQL Functions + ** KEYWORDS: {function creation routines} +@@ -6135,17 +5923,15 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, + ** index expressions, or the WHERE clause of partial indexes. + ** +-** + ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for + ** all application-defined SQL functions that do not need to be +-** used inside of triggers, view, CHECK constraints, or other elements of +-** the database schema. This flags is especially recommended for SQL ++** used inside of triggers, views, CHECK constraints, or other elements of ++** the database schema. This flag is especially recommended for SQL + ** functions that have side effects or reveal internal application state. + ** Without this flag, an attacker might be able to modify the schema of + ** a database file to include invocations of the function with parameters + ** chosen by the attacker, which the application will then execute when + ** the database file is opened and read. +-** + ** + ** ^(The fifth parameter is an arbitrary pointer. The implementation of the + ** function can gain access to this pointer using [sqlite3_user_data()].)^ +@@ -6171,7 +5957,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + ** [user-defined window functions|available here]. + ** + ** ^(If the final parameter to sqlite3_create_function_v2() or +-** sqlite3_create_window_function() is not NULL, then it is destructor for ++** sqlite3_create_window_function() is not NULL, then it is the destructor for + ** the application data pointer. The destructor is invoked when the function + ** is deleted, either by being overloaded or when the database connection + ** closes.)^ ^The destructor is also invoked if the call to +@@ -6281,10 +6067,21 @@ SQLITE_API int sqlite3_create_window_function( + ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in + ** schema structures such as [CHECK constraints], [DEFAULT clauses], + ** [expression indexes], [partial indexes], or [generated columns]. +-** The SQLITE_DIRECTONLY flags is a security feature which is recommended +-** for all [application-defined SQL functions], and especially for functions +-** that have side-effects or that could potentially leak sensitive +-** information. ++**

        ++** The SQLITE_DIRECTONLY flag is recommended for any ++** [application-defined SQL function] ++** that has side-effects or that could potentially leak sensitive information. ++** This will prevent attacks in which an application is tricked ++** into using a database file that has had its schema surreptitiously ++** modified to invoke the application-defined function in ways that are ++** harmful. ++**

        ++** Some people say it is good practice to set SQLITE_DIRECTONLY on all ++** [application-defined SQL functions], regardless of whether or not they ++** are security sensitive, as doing so prevents those functions from being used ++** inside of the database schema, and thus ensures that the database ++** can be inspected and modified using generic tools (such as the [CLI]) ++** that do not have access to the application-defined functions. + **

      + ** + ** [[SQLITE_INNOCUOUS]]
      SQLITE_INNOCUOUS
      +@@ -6311,13 +6108,36 @@ SQLITE_API int sqlite3_create_window_function( + **
      + ** + ** [[SQLITE_SUBTYPE]]
      SQLITE_SUBTYPE
      +-** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call ++** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call + ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. +-** Specifying this flag makes no difference for scalar or aggregate user +-** functions. However, if it is not specified for a user-defined window +-** function, then any sub-types belonging to arguments passed to the window +-** function may be discarded before the window function is called (i.e. +-** sqlite3_value_subtype() will always return 0). ++** This flag instructs SQLite to omit some corner-case optimizations that ++** might disrupt the operation of the [sqlite3_value_subtype()] function, ++** causing it to return zero rather than the correct subtype(). ++** All SQL functions that invoke [sqlite3_value_subtype()] should have this ++** property. If the SQLITE_SUBTYPE property is omitted, then the return ++** value from [sqlite3_value_subtype()] might sometimes be zero even though ++** a non-zero subtype was specified by the function argument expression. ++** ++** [[SQLITE_RESULT_SUBTYPE]]
      SQLITE_RESULT_SUBTYPE
      ++** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call ++** [sqlite3_result_subtype()] to cause a sub-type to be associated with its ++** result. ++** Every function that invokes [sqlite3_result_subtype()] should have this ++** property. If it does not, then the call to [sqlite3_result_subtype()] ++** might become a no-op if the function is used as term in an ++** [expression index]. On the other hand, SQL functions that never invoke ++** [sqlite3_result_subtype()] should avoid setting this property, as the ++** purpose of this property is to disable certain optimizations that are ++** incompatible with subtypes. ++** ++** [[SQLITE_SELFORDER1]]
      SQLITE_SELFORDER1
      ++** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate ++** that internally orders the values provided to the first argument. The ++** ordered-set aggregate SQL notation with a single ORDER BY term can be ++** used to invoke this function. If the ordered-set aggregate notation is ++** used on a function that lacks this flag, then an error is raised. Note ++** that the ordered-set aggregate syntax is only available if SQLite is ++** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. + **
      + ** + */ +@@ -6325,6 +6145,8 @@ SQLITE_API int sqlite3_create_window_function( + #define SQLITE_DIRECTONLY 0x000080000 + #define SQLITE_SUBTYPE 0x000100000 + #define SQLITE_INNOCUOUS 0x000200000 ++#define SQLITE_RESULT_SUBTYPE 0x001000000 ++#define SQLITE_SELFORDER1 0x002000000 + + /* + ** CAPI3REF: Deprecated Functions +@@ -6490,6 +6312,28 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); + SQLITE_API int sqlite3_value_nochange(sqlite3_value*); + SQLITE_API int sqlite3_value_frombind(sqlite3_value*); + ++/* ++** CAPI3REF: Report the internal text encoding state of an sqlite3_value object ++** METHOD: sqlite3_value ++** ++** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], ++** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding ++** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) ++** returns something other than SQLITE_TEXT, then the return value from ++** sqlite3_value_encoding(X) is meaningless. ^Calls to ++** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)], ++** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or ++** [sqlite3_value_bytes16(X)] might change the encoding of the value X and ++** thus change the return from subsequent calls to sqlite3_value_encoding(X). ++** ++** This routine is intended for used by applications that test and validate ++** the SQLite implementation. This routine is inquiring about the opaque ++** internal state of an [sqlite3_value] object. Ordinary applications should ++** not need to know what the internal state of an sqlite3_value object is and ++** hence should not need to use this interface. ++*/ ++SQLITE_API int sqlite3_value_encoding(sqlite3_value*); ++ + /* + ** CAPI3REF: Finding The Subtype Of SQL Values + ** METHOD: sqlite3_value +@@ -6499,6 +6343,12 @@ SQLITE_API int sqlite3_value_frombind(sqlite3_value*); + ** information can be used to pass a limited amount of context from + ** one SQL function to another. Use the [sqlite3_result_subtype()] + ** routine to set the subtype for the return value of an SQL function. ++** ++** Every [application-defined SQL function] that invokes this interface ++** should include the [SQLITE_SUBTYPE] property in the text ++** encoding argument when the function is [sqlite3_create_function|registered]. ++** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() ++** might return zero instead of the upstream subtype in some corner cases. + */ + SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); + +@@ -6507,10 +6357,11 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); + ** METHOD: sqlite3_value + ** + ** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value] +-** object D and returns a pointer to that copy. ^The [sqlite3_value] returned ++** object V and returns a pointer to that copy. ^The [sqlite3_value] returned + ** is a [protected sqlite3_value] object even if the input is not. + ** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a +-** memory allocation fails. ++** memory allocation fails. ^If V is a [pointer value], then the result ++** of sqlite3_value_dup(V) is a NULL value. + ** + ** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object + ** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer +@@ -6541,10 +6392,10 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*); + ** + ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer + ** when first called if N is less than or equal to zero or if a memory +-** allocate error occurs. ++** allocation error occurs. + ** + ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is +-** determined by the N parameter on first successful call. Changing the ++** determined by the N parameter on the first successful call. Changing the + ** value of N in any subsequent call to sqlite3_aggregate_context() within + ** the same aggregate function instance will not resize the memory + ** allocation.)^ Within the xFinal callback, it is customary to set +@@ -6596,48 +6447,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + ** METHOD: sqlite3_context + ** + ** These functions may be used by (non-aggregate) SQL functions to +-** associate metadata with argument values. If the same value is passed to +-** multiple invocations of the same SQL function during query execution, under +-** some circumstances the associated metadata may be preserved. An example +-** of where this might be useful is in a regular-expression matching +-** function. The compiled version of the regular expression can be stored as +-** metadata associated with the pattern string. ++** associate auxiliary data with argument values. If the same argument ++** value is passed to multiple invocations of the same SQL function during ++** query execution, under some circumstances the associated auxiliary data ++** might be preserved. An example of where this might be useful is in a ++** regular-expression matching function. The compiled version of the regular ++** expression can be stored as auxiliary data associated with the pattern string. + ** Then as long as the pattern string remains the same, + ** the compiled regular expression can be reused on multiple + ** invocations of the same function. + ** +-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata ++** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data + ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument + ** value to the application-defined function. ^N is zero for the left-most +-** function argument. ^If there is no metadata ++** function argument. ^If there is no auxiliary data + ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface + ** returns a NULL pointer. + ** +-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th +-** argument of the application-defined function. ^Subsequent ++** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the ++** N-th argument of the application-defined function. ^Subsequent + ** calls to sqlite3_get_auxdata(C,N) return P from the most recent +-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or +-** NULL if the metadata has been discarded. ++** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or ++** NULL if the auxiliary data has been discarded. + ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, + ** SQLite will invoke the destructor function X with parameter P exactly +-** once, when the metadata is discarded. +-** SQLite is free to discard the metadata at any time, including:
        ++** once, when the auxiliary data is discarded. ++** SQLite is free to discard the auxiliary data at any time, including:
          + **
        • ^(when the corresponding function parameter changes)^, or + **
        • ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the + ** SQL statement)^, or + **
        • ^(when sqlite3_set_auxdata() is invoked again on the same + ** parameter)^, or + **
        • ^(during the original sqlite3_set_auxdata() call when a memory +-** allocation error occurs.)^
        ++** allocation error occurs.)^ ++**
      • ^(during the original sqlite3_set_auxdata() call if the function ++** is evaluated during query planning instead of during query execution, ++** as sometimes happens with [SQLITE_ENABLE_STAT4].)^
      + ** +-** Note the last bullet in particular. The destructor X in ++** Note the last two bullets in particular. The destructor X in + ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the + ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() + ** should be called near the end of the function implementation and the + ** function implementation should not make any use of P after +-** sqlite3_set_auxdata() has been called. +-** +-** ^(In practice, metadata is preserved between function calls for ++** sqlite3_set_auxdata() has been called. Furthermore, a call to ++** sqlite3_get_auxdata() that occurs immediately after a corresponding call ++** to sqlite3_set_auxdata() might still return NULL if an out-of-memory ++** condition occurred during the sqlite3_set_auxdata() call or if the ++** function is being evaluated during query planning rather than during ++** query execution. ++** ++** ^(In practice, auxiliary data is preserved between function calls for + ** function parameters that are compile-time constants, including literal + ** values and [parameters] and expressions composed from the same.)^ + ** +@@ -6647,10 +6506,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + ** + ** These routines must be called from the same thread in which + ** the SQL function is running. ++** ++** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()]. + */ + SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); + SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); + ++/* ++** CAPI3REF: Database Connection Client Data ++** METHOD: sqlite3 ++** ++** These functions are used to associate one or more named pointers ++** with a [database connection]. ++** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P ++** to be attached to [database connection] D using name N. Subsequent ++** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P ++** or a NULL pointer if there were no prior calls to ++** sqlite3_set_clientdata() with the same values of D and N. ++** Names are compared using strcmp() and are thus case sensitive. ++** ++** If P and X are both non-NULL, then the destructor X is invoked with ++** argument P on the first of the following occurrences: ++**
        ++**
      • An out-of-memory error occurs during the call to ++** sqlite3_set_clientdata() which attempts to register pointer P. ++**
      • A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made ++** with the same D and N parameters. ++**
      • The database connection closes. SQLite does not make any guarantees ++** about the order in which destructors are called, only that all ++** destructors will be called exactly once at some point during the ++** database connection closing process. ++**
      ++** ++** SQLite does not do anything with client data other than invoke ++** destructors on the client data at the appropriate time. The intended ++** use for client data is to provide a mechanism for wrapper libraries ++** to store additional information about an SQLite database connection. ++** ++** There is no limit (other than available memory) on the number of different ++** client data pointers (with different names) that can be attached to a ++** single database connection. However, the implementation is optimized ++** for the case of having only one or two different client data names. ++** Applications and wrapper libraries are discouraged from using more than ++** one client data name each. ++** ++** There is no way to enumerate the client data pointers ++** associated with a database connection. The N parameter can be thought ++** of as a secret key such that only code that knows the secret key is able ++** to access the associated data. ++** ++** Security Warning: These interfaces should not be exposed in scripting ++** languages or in other circumstances where it might be possible for an ++** attacker to invoke them. Any agent that can invoke these interfaces ++** can probably also take control of the process. ++** ++** Database connection client data is only available for SQLite ++** version 3.44.0 ([dateof:3.44.0]) and later. ++** ++** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. ++*/ ++SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*); ++SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*)); + + /* + ** CAPI3REF: Constants Defining Special Destructor Behavior +@@ -6746,15 +6662,16 @@ typedef void (*sqlite3_destructor_type)(void*); + ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. + ** ^SQLite takes the text result from the application from + ** the 2nd parameter of the sqlite3_result_text* interfaces. +-** ^If the 3rd parameter to the sqlite3_result_text* interfaces +-** is negative, then SQLite takes result text from the 2nd parameter +-** through the first zero character. ++** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces ++** other than sqlite3_result_text64() is negative, then SQLite computes ++** the string length itself by searching the 2nd parameter for the first ++** zero character. + ** ^If the 3rd parameter to the sqlite3_result_text* interfaces + ** is non-negative, then as many bytes (not characters) of the text + ** pointed to by the 2nd parameter are taken as the application-defined + ** function result. If the 3rd parameter is non-negative, then it + ** must be the byte offset into the string where the NUL terminator would +-** appear if the string where NUL terminated. If any NUL characters occur ++** appear if the string were NUL terminated. If any NUL characters occur + ** in the string at a byte offset that is less than the value of the 3rd + ** parameter, then the resulting string will contain embedded NULs and the + ** result of expressions operating on strings with embedded NULs is undefined. +@@ -6812,7 +6729,7 @@ typedef void (*sqlite3_destructor_type)(void*); + ** string and preferably a string literal. The sqlite3_result_pointer() + ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. + ** +-** If these routines are called from within the different thread ++** If these routines are called from within a different thread + ** than the one containing the application-defined function that received + ** the [sqlite3_context] pointer, the results are undefined. + */ +@@ -6851,6 +6768,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); + ** higher order bits are discarded. + ** The number of subtype bytes preserved by SQLite might increase + ** in future releases of SQLite. ++** ++** Every [application-defined SQL function] that invokes this interface ++** should include the [SQLITE_RESULT_SUBTYPE] property in its ++** text encoding argument when the SQL function is ++** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] ++** property is omitted from the function that invokes sqlite3_result_subtype(), ++** then in some cases the sqlite3_result_subtype() might fail to set ++** the result subtype. ++** ++** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any ++** SQL function that invokes the sqlite3_result_subtype() interface ++** and that does not have the SQLITE_RESULT_SUBTYPE property will raise ++** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 ++** by default. + */ + SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); + +@@ -7022,6 +6953,13 @@ SQLITE_API void sqlite3_activate_cerod( + ** of the default VFS is not implemented correctly, or not implemented at + ** all, then the behavior of sqlite3_sleep() may deviate from the description + ** in the previous paragraphs. ++** ++** If a negative argument is passed to sqlite3_sleep() the results vary by ++** VFS and operating system. Some system treat a negative argument as an ++** instruction to sleep forever. Others understand it to mean do not sleep ++** at all. ^In SQLite version 3.42.0 and later, a negative ++** argument passed into sqlite3_sleep() is changed to zero before it is relayed ++** down into the xSleep method of the VFS. + */ + SQLITE_API int sqlite3_sleep(int); + +@@ -7192,6 +7130,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); + */ + SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + ++/* ++** CAPI3REF: Return The Schema Name For A Database Connection ++** METHOD: sqlite3 ++** ++** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name ++** for the N-th database on database connection D, or a NULL pointer if N is ++** out of range. An N value of 0 means the main database file. An N of 1 is ++** the "temp" schema. Larger values of N correspond to various ATTACH-ed ++** databases. ++** ++** Space to hold the string that is returned by sqlite3_db_name() is managed ++** by SQLite itself. The string might be deallocated by any operation that ++** changes the schema, including [ATTACH] or [DETACH] or calls to ++** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that ++** occur on a different thread. Applications that need to ++** remember the string long-term should make their own copy. Applications that ++** are accessing the same database connection simultaneously on multiple ++** threads should mutex-protect calls to this API and should make their own ++** private copy of the result prior to releasing the mutex. ++*/ ++SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N); ++ + /* + ** CAPI3REF: Return The Filename For A Database Connection + ** METHOD: sqlite3 +@@ -7222,7 +7182,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + **
    • [sqlite3_filename_wal()] + **
    + */ +-SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); ++SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName); + + /* + ** CAPI3REF: Determine if a database is read-only +@@ -7234,6 +7194,57 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); + */ + SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); + ++/* ++** CAPI3REF: Determine the transaction state of a database ++** METHOD: sqlite3 ++** ++** ^The sqlite3_txn_state(D,S) interface returns the current ++** [transaction state] of schema S in database connection D. ^If S is NULL, ++** then the highest transaction state of any schema on database connection D ++** is returned. Transaction states are (in order of lowest to highest): ++**
      ++**
    1. SQLITE_TXN_NONE ++**
    2. SQLITE_TXN_READ ++**
    3. SQLITE_TXN_WRITE ++**
    ++** ^If the S argument to sqlite3_txn_state(D,S) is not the name of ++** a valid schema, then -1 is returned. ++*/ ++SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); ++ ++/* ++** CAPI3REF: Allowed return values from sqlite3_txn_state() ++** KEYWORDS: {transaction state} ++** ++** These constants define the current transaction state of a database file. ++** ^The [sqlite3_txn_state(D,S)] interface returns one of these ++** constants in order to describe the transaction state of schema S ++** in [database connection] D. ++** ++**
    ++** [[SQLITE_TXN_NONE]]
    SQLITE_TXN_NONE
    ++**
    The SQLITE_TXN_NONE state means that no transaction is currently ++** pending.
    ++** ++** [[SQLITE_TXN_READ]]
    SQLITE_TXN_READ
    ++**
    The SQLITE_TXN_READ state means that the database is currently ++** in a read transaction. Content has been read from the database file ++** but nothing in the database file has changed. The transaction state ++** will be advanced to SQLITE_TXN_WRITE if any changes occur and there are ++** no other conflicting concurrent write transactions. The transaction ++** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or ++** [COMMIT].
    ++** ++** [[SQLITE_TXN_WRITE]]
    SQLITE_TXN_WRITE
    ++**
    The SQLITE_TXN_WRITE state means that the database is currently ++** in a write transaction. Content has been written to the database file ++** but has not yet committed. The transaction state will change to ++** SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].
    ++*/ ++#define SQLITE_TXN_NONE 0 ++#define SQLITE_TXN_READ 1 ++#define SQLITE_TXN_WRITE 2 ++ + /* + ** CAPI3REF: Find the next prepared statement + ** METHOD: sqlite3 +@@ -7300,6 +7311,72 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); + SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); + SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + ++/* ++** CAPI3REF: Autovacuum Compaction Amount Callback ++** METHOD: sqlite3 ++** ++** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback ++** function C that is invoked prior to each autovacuum of the database ++** file. ^The callback is passed a copy of the generic data pointer (P), ++** the schema-name of the attached database that is being autovacuumed, ++** the size of the database file in pages, the number of free pages, ++** and the number of bytes per page, respectively. The callback should ++** return the number of free pages that should be removed by the ++** autovacuum. ^If the callback returns zero, then no autovacuum happens. ++** ^If the value returned is greater than or equal to the number of ++** free pages, then a complete autovacuum happens. ++** ++**

    ^If there are multiple ATTACH-ed database files that are being ++** modified as part of a transaction commit, then the autovacuum pages ++** callback is invoked separately for each file. ++** ++**

    The callback is not reentrant. The callback function should ++** not attempt to invoke any other SQLite interface. If it does, bad ++** things may happen, including segmentation faults and corrupt database ++** files. The callback function should be a simple function that ++** does some arithmetic on its input parameters and returns a result. ++** ++** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional ++** destructor for the P parameter. ^If X is not NULL, then X(P) is ++** invoked whenever the database connection closes or when the callback ++** is overwritten by another invocation of sqlite3_autovacuum_pages(). ++** ++**

    ^There is only one autovacuum pages callback per database connection. ++** ^Each call to the sqlite3_autovacuum_pages() interface overrides all ++** previous invocations for that database connection. ^If the callback ++** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, ++** then the autovacuum steps callback is canceled. The return value ++** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might ++** be some other error code if something goes wrong. The current ++** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other ++** return codes might be added in future releases. ++** ++**

    If no autovacuum pages callback is specified (the usual case) or ++** a NULL pointer is provided for the callback, ++** then the default behavior is to vacuum all free pages. So, in other ++** words, the default behavior is the same as if the callback function ++** were something like this: ++** ++**

    ++**     unsigned int demonstration_autovac_pages_callback(
    ++**       void *pClientData,
    ++**       const char *zSchema,
    ++**       unsigned int nDbPage,
    ++**       unsigned int nFreePage,
    ++**       unsigned int nBytePerPage
    ++**     ){
    ++**       return nFreePage;
    ++**     }
    ++** 
    ++*/ ++SQLITE_API int sqlite3_autovacuum_pages( ++ sqlite3 *db, ++ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), ++ void*, ++ void(*)(void*) ++); ++ ++ + /* + ** CAPI3REF: Data Change Notification Callbacks + ** METHOD: sqlite3 +@@ -7313,6 +7390,8 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + ** + ** ^The second argument is a pointer to the function to invoke when a + ** row is updated, inserted or deleted in a rowid table. ++** ^The update hook is disabled by invoking sqlite3_update_hook() ++** with a NULL pointer as the second parameter. + ** ^The first argument to the callback is a copy of the third argument + ** to sqlite3_update_hook(). + ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], +@@ -7334,6 +7413,12 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + ** The exceptions defined in this paragraph might change in a future + ** release of SQLite. + ** ++** Whether the update hook is invoked before or after the ++** corresponding change is currently unspecified and may differ ++** depending on the type of change. Do not rely on the order of the ++** hook call with regards to the final result of the operation which ++** triggers the hook. ++** + ** The update hook implementation must not do anything that will modify + ** the database connection that invoked the update hook. Any actions + ** to modify the database connection must be deferred until after the +@@ -7363,6 +7448,11 @@ SQLITE_API void *sqlite3_update_hook( + ** to the same database. Sharing is enabled if the argument is true + ** and disabled if the argument is false.)^ + ** ++** This interface is omitted if SQLite is compiled with ++** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE] ++** compile-time option is recommended because the ++** [use of shared cache mode is discouraged]. ++** + ** ^Cache sharing is enabled and disabled for an entire process. + ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). + ** In prior versions of SQLite, +@@ -7430,7 +7520,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); + ** CAPI3REF: Impose A Limit On Heap Size + ** + ** These interfaces impose limits on the amount of heap memory that will be +-** by all database connections within a single process. ++** used by all database connections within a single process. + ** + ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the + ** soft limit on the amount of heap memory that may be allocated by SQLite. +@@ -7461,7 +7551,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); + ** ^The soft heap limit may not be greater than the hard heap limit. + ** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N) + ** is invoked with a value of N that is greater than the hard heap limit, +-** the the soft heap limit is set to the value of the hard heap limit. ++** the soft heap limit is set to the value of the hard heap limit. + ** ^The soft heap limit is automatically enabled whenever the hard heap + ** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and + ** the soft heap limit is outside the range of 1..N, then the soft heap +@@ -7488,7 +7578,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); + ** )^ + ** + ** The circumstances under which SQLite will enforce the heap limits may +-** changes in future releases of SQLite. ++** change in future releases of SQLite. + */ + SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); + SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N); +@@ -7603,8 +7693,8 @@ SQLITE_API int sqlite3_table_column_metadata( + ** ^The entry point is zProc. + ** ^(zProc may be 0, in which case SQLite will try to come up with an + ** entry point name on its own. It first tries "sqlite3_extension_init". +-** If that does not work, it constructs a name "sqlite3_X_init" where the +-** X is consists of the lower-case equivalent of all ASCII alphabetic ++** If that does not work, it constructs a name "sqlite3_X_init" where ++** X consists of the lower-case equivalent of all ASCII alphabetic + ** characters in the filename from the last "/" to the first following + ** "." and omitting any initial "lib".)^ + ** ^The sqlite3_load_extension() interface returns +@@ -7675,7 +7765,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); + ** ^(Even though the function prototype shows that xEntryPoint() takes + ** no arguments and returns void, SQLite invokes xEntryPoint() with three + ** arguments and expects an integer result as if the signature of the +-** entry point where as follows: ++** entry point were as follows: + ** + **
    + **    int xEntryPoint(
    +@@ -7722,15 +7812,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
    + */
    + SQLITE_API void sqlite3_reset_auto_extension(void);
    + 
    +-/*
    +-** The interface to the virtual-table mechanism is currently considered
    +-** to be experimental.  The interface might change in incompatible ways.
    +-** If this is a problem for you, do not use the interface at this time.
    +-**
    +-** When the virtual-table mechanism stabilizes, we will declare the
    +-** interface fixed, support it indefinitely, and remove this comment.
    +-*/
    +-
    + /*
    + ** Structures used by the virtual table interface
    + */
    +@@ -7791,6 +7872,10 @@ struct sqlite3_module {
    +   /* The methods above are in versions 1 and 2 of the sqlite_module object.
    +   ** Those below are for version 3 and greater. */
    +   int (*xShadowName)(const char*);
    ++  /* The methods above are in versions 1 through 3 of the sqlite_module object.
    ++  ** Those below are for version 4 and greater. */
    ++  int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
    ++                    const char *zTabName, int mFlags, char **pzErr);
    + };
    + 
    + /*
    +@@ -7844,15 +7929,15 @@ struct sqlite3_module {
    + ** virtual table and might not be checked again by the byte code.)^ ^(The
    + ** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
    + ** is left in its default setting of false, the constraint will always be
    +-** checked separately in byte code.  If the omit flag is change to true, then
    ++** checked separately in byte code.  If the omit flag is changed to true, then
    + ** the constraint may or may not be checked in byte code.  In other words,
    + ** when the omit flag is true there is no guarantee that the constraint will
    + ** not be checked again using byte code.)^
    + **
    +-** ^The idxNum and idxPtr values are recorded and passed into the
    ++** ^The idxNum and idxStr values are recorded and passed into the
    + ** [xFilter] method.
    +-** ^[sqlite3_free()] is used to free idxPtr if and only if
    +-** needToFreeIdxPtr is true.
    ++** ^[sqlite3_free()] is used to free idxStr if and only if
    ++** needToFreeIdxStr is true.
    + **
    + ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
    + ** the correct order to satisfy the ORDER BY clause so that no separate
    +@@ -7868,9 +7953,11 @@ struct sqlite3_module {
    + ** will be returned by the strategy.
    + **
    + ** The xBestIndex method may optionally populate the idxFlags field with a
    +-** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
    +-** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
    +-** assumes that the strategy may visit at most one row.
    ++** mask of SQLITE_INDEX_SCAN_* flags. One such flag is
    ++** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN]
    ++** output to show the idxNum as hex instead of as decimal.  Another flag is
    ++** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will
    ++** return at most one row.
    + **
    + ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
    + ** SQLite also assumes that if a call to the xUpdate() method is made as
    +@@ -7934,31 +8021,65 @@ struct sqlite3_index_info {
    + ** [sqlite3_index_info].idxFlags field to some combination of
    + ** these bits.
    + */
    +-#define SQLITE_INDEX_SCAN_UNIQUE      1     /* Scan visits at most 1 row */
    ++#define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */
    ++#define SQLITE_INDEX_SCAN_HEX    0x00000002 /* Display idxNum as hex */
    ++                                            /* in EXPLAIN QUERY PLAN */
    + 
    + /*
    + ** CAPI3REF: Virtual Table Constraint Operator Codes
    + **
    + ** These macros define the allowed values for the
    + ** [sqlite3_index_info].aConstraint[].op field.  Each value represents
    +-** an operator that is part of a constraint term in the wHERE clause of
    ++** an operator that is part of a constraint term in the WHERE clause of
    + ** a query that uses a [virtual table].
    +-*/
    +-#define SQLITE_INDEX_CONSTRAINT_EQ         2
    +-#define SQLITE_INDEX_CONSTRAINT_GT         4
    +-#define SQLITE_INDEX_CONSTRAINT_LE         8
    +-#define SQLITE_INDEX_CONSTRAINT_LT        16
    +-#define SQLITE_INDEX_CONSTRAINT_GE        32
    +-#define SQLITE_INDEX_CONSTRAINT_MATCH     64
    +-#define SQLITE_INDEX_CONSTRAINT_LIKE      65
    +-#define SQLITE_INDEX_CONSTRAINT_GLOB      66
    +-#define SQLITE_INDEX_CONSTRAINT_REGEXP    67
    +-#define SQLITE_INDEX_CONSTRAINT_NE        68
    +-#define SQLITE_INDEX_CONSTRAINT_ISNOT     69
    +-#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
    +-#define SQLITE_INDEX_CONSTRAINT_ISNULL    71
    +-#define SQLITE_INDEX_CONSTRAINT_IS        72
    +-#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
    ++**
    ++** ^The left-hand operand of the operator is given by the corresponding
    ++** aConstraint[].iColumn field.  ^An iColumn of -1 indicates the left-hand
    ++** operand is the rowid.
    ++** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
    ++** operators have no left-hand operand, and so for those operators the
    ++** corresponding aConstraint[].iColumn is meaningless and should not be
    ++** used.
    ++**
    ++** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
    ++** value 255 are reserved to represent functions that are overloaded
    ++** by the [xFindFunction|xFindFunction method] of the virtual table
    ++** implementation.
    ++**
    ++** The right-hand operands for each constraint might be accessible using
    ++** the [sqlite3_vtab_rhs_value()] interface.  Usually the right-hand
    ++** operand is only available if it appears as a single constant literal
    ++** in the input SQL.  If the right-hand operand is another column or an
    ++** expression (even a constant expression) or a parameter, then the
    ++** sqlite3_vtab_rhs_value() probably will not be able to extract it.
    ++** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
    ++** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
    ++** and hence calls to sqlite3_vtab_rhs_value() for those operators will
    ++** always return SQLITE_NOTFOUND.
    ++**
    ++** The collating sequence to be used for comparison can be found using
    ++** the [sqlite3_vtab_collation()] interface.  For most real-world virtual
    ++** tables, the collating sequence of constraints does not matter (for example
    ++** because the constraints are numeric) and so the sqlite3_vtab_collation()
    ++** interface is not commonly needed.
    ++*/
    ++#define SQLITE_INDEX_CONSTRAINT_EQ          2
    ++#define SQLITE_INDEX_CONSTRAINT_GT          4
    ++#define SQLITE_INDEX_CONSTRAINT_LE          8
    ++#define SQLITE_INDEX_CONSTRAINT_LT         16
    ++#define SQLITE_INDEX_CONSTRAINT_GE         32
    ++#define SQLITE_INDEX_CONSTRAINT_MATCH      64
    ++#define SQLITE_INDEX_CONSTRAINT_LIKE       65
    ++#define SQLITE_INDEX_CONSTRAINT_GLOB       66
    ++#define SQLITE_INDEX_CONSTRAINT_REGEXP     67
    ++#define SQLITE_INDEX_CONSTRAINT_NE         68
    ++#define SQLITE_INDEX_CONSTRAINT_ISNOT      69
    ++#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL  70
    ++#define SQLITE_INDEX_CONSTRAINT_ISNULL     71
    ++#define SQLITE_INDEX_CONSTRAINT_IS         72
    ++#define SQLITE_INDEX_CONSTRAINT_LIMIT      73
    ++#define SQLITE_INDEX_CONSTRAINT_OFFSET     74
    ++#define SQLITE_INDEX_CONSTRAINT_FUNCTION  150
    + 
    + /*
    + ** CAPI3REF: Register A Virtual Table Implementation
    +@@ -7975,7 +8096,7 @@ struct sqlite3_index_info {
    + ** the implementation of the [virtual table module].   ^The fourth
    + ** parameter is an arbitrary client data pointer that is passed through
    + ** into the [xCreate] and [xConnect] methods of the virtual table module
    +-** when a new virtual table is be being created or reinitialized.
    ++** when a new virtual table is being created or reinitialized.
    + **
    + ** ^The sqlite3_create_module_v2() interface has a fifth parameter which
    + ** is a pointer to a destructor for the pClientData.  ^SQLite will
    +@@ -7987,7 +8108,7 @@ struct sqlite3_index_info {
    + ** destructor.
    + **
    + ** ^If the third parameter (the pointer to the sqlite3_module object) is
    +-** NULL then no new module is create and any existing modules with the
    ++** NULL then no new module is created and any existing modules with the
    + ** same name are dropped.
    + **
    + ** See also: [sqlite3_drop_modules()]
    +@@ -8099,16 +8220,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
    + */
    + SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
    + 
    +-/*
    +-** The interface to the virtual-table mechanism defined above (back up
    +-** to a comment remarkably similar to this one) is currently considered
    +-** to be experimental.  The interface might change in incompatible ways.
    +-** If this is a problem for you, do not use the interface at this time.
    +-**
    +-** When the virtual-table mechanism stabilizes, we will declare the
    +-** interface fixed, support it indefinitely, and remove this comment.
    +-*/
    +-
    + /*
    + ** CAPI3REF: A Handle To An Open BLOB
    + ** KEYWORDS: {BLOB handle} {BLOB handles}
    +@@ -8150,7 +8261,7 @@ typedef struct sqlite3_blob sqlite3_blob;
    + ** in *ppBlob. Otherwise an [error code] is returned and, unless the error
    + ** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
    + ** the API is not misused, it is always safe to call [sqlite3_blob_close()]
    +-** on *ppBlob after this function it returns.
    ++** on *ppBlob after this function returns.
    + **
    + ** This function fails with SQLITE_ERROR if any of the following are true:
    + ** 
      +@@ -8256,7 +8367,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); + ** code is returned and the transaction rolled back. + ** + ** Calling this function with an argument that is not a NULL pointer or an +-** open blob handle results in undefined behaviour. ^Calling this routine ++** open blob handle results in undefined behavior. ^Calling this routine + ** with a null pointer (such as would be returned by a failed call to + ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function + ** is passed a valid open blob handle, the values returned by the +@@ -8270,7 +8381,7 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); + ** + ** ^Returns the size in bytes of the BLOB accessible via the + ** successfully opened [BLOB handle] in its only argument. ^The +-** incremental blob I/O routines can only read or overwriting existing ++** incremental blob I/O routines can only read or overwrite existing + ** blob content; they cannot change the size of a blob. + ** + ** This routine only works on a [BLOB handle] which has been created +@@ -8420,7 +8531,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + ** ^The sqlite3_mutex_alloc() routine allocates a new + ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() + ** routine returns NULL if it is unable to allocate the requested +-** mutex. The argument to sqlite3_mutex_alloc() must one of these ++** mutex. The argument to sqlite3_mutex_alloc() must be one of these + ** integer constants: + ** + **
        +@@ -8483,18 +8594,20 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + ** + ** ^(Some systems (for example, Windows 95) do not support the operation + ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() +-** will always return SQLITE_BUSY. The SQLite core only ever uses +-** sqlite3_mutex_try() as an optimization so this is acceptable +-** behavior.)^ ++** will always return SQLITE_BUSY. In most cases the SQLite core only uses ++** sqlite3_mutex_try() as an optimization, so this is acceptable ++** behavior. The exceptions are unix builds that set the ++** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working ++** sqlite3_mutex_try() is required.)^ + ** + ** ^The sqlite3_mutex_leave() routine exits a mutex that was + ** previously entered by the same thread. The behavior + ** is undefined if the mutex is not currently entered by the + ** calling thread or is not currently allocated. + ** +-** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or +-** sqlite3_mutex_leave() is a NULL pointer, then all three routines +-** behave as no-ops. ++** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), ++** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, ++** then any of the four routines behaves as a no-op. + ** + ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. + */ +@@ -8651,7 +8764,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); + ** CAPI3REF: Retrieve the mutex for a database connection + ** METHOD: sqlite3 + ** +-** ^This interface returns a pointer the [sqlite3_mutex] object that ++** ^This interface returns a pointer to the [sqlite3_mutex] object that + ** serializes access to the [database connection] given in the argument + ** when the [threading mode] is Serialized. + ** ^If the [threading mode] is Single-thread or Multi-thread then this +@@ -8736,6 +8849,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); + #define SQLITE_TESTCTRL_PRNG_SAVE 5 + #define SQLITE_TESTCTRL_PRNG_RESTORE 6 + #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ ++#define SQLITE_TESTCTRL_FK_NO_ACTION 7 + #define SQLITE_TESTCTRL_BITVEC_TEST 8 + #define SQLITE_TESTCTRL_FAULT_INSTALL 9 + #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 +@@ -8743,8 +8857,10 @@ SQLITE_API int sqlite3_test_control(int op, ...); + #define SQLITE_TESTCTRL_ASSERT 12 + #define SQLITE_TESTCTRL_ALWAYS 13 + #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ ++#define SQLITE_TESTCTRL_JSON_SELFCHECK 14 + #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 + #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ ++#define SQLITE_TESTCTRL_GETOPT 16 + #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ + #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 + #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +@@ -8760,20 +8876,25 @@ SQLITE_API int sqlite3_test_control(int op, ...); + #define SQLITE_TESTCTRL_RESULT_INTREAL 27 + #define SQLITE_TESTCTRL_PRNG_SEED 28 + #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 +-#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ ++#define SQLITE_TESTCTRL_SEEK_COUNT 30 ++#define SQLITE_TESTCTRL_TRACEFLAGS 31 ++#define SQLITE_TESTCTRL_TUNE 32 ++#define SQLITE_TESTCTRL_LOGEST 33 ++#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ ++#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ + + /* + ** CAPI3REF: SQL Keyword Checking + ** + ** These routines provide access to the set of SQL language keywords +-** recognized by SQLite. Applications can uses these routines to determine ++** recognized by SQLite. Applications can use these routines to determine + ** whether or not a specific identifier needs to be escaped (for example, + ** by enclosing in double-quotes) so as not to confuse the parser. + ** + ** The sqlite3_keyword_count() interface returns the number of distinct + ** keywords understood by SQLite. + ** +-** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and ++** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and + ** makes *Z point to that keyword expressed as UTF8 and writes the number + ** of bytes in the keyword into *L. The string that *Z points to is not + ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns +@@ -8934,7 +9055,7 @@ SQLITE_API void sqlite3_str_reset(sqlite3_str*); + ** content of the dynamic string under construction in X. The value + ** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X + ** and might be freed or altered by any subsequent method on the same +-** [sqlite3_str] object. Applications must not used the pointer returned ++** [sqlite3_str] object. Applications must not use the pointer returned by + ** [sqlite3_str_value(X)] after any subsequent method call on the same + ** object. ^Applications may change the content of the string returned + ** by [sqlite3_str_value(X)] as long as they do not write into any bytes +@@ -9020,7 +9141,7 @@ SQLITE_API int sqlite3_status64( + ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] + ** buffer and where forced to overflow to [sqlite3_malloc()]. The + ** returned value includes allocations that overflowed because they +-** where too large (they were larger than the "sz" parameter to ++** were too large (they were larger than the "sz" parameter to + ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because + ** no space was left in the page cache.)^ + ** +@@ -9104,28 +9225,29 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
        SQLITE_DBSTATUS_LOOKASIDE_HIT
        + **
        This parameter returns the number of malloc attempts that were + ** satisfied using lookaside memory. Only the high-water value is meaningful; +-** the current value is always zero.)^ ++** the current value is always zero.
        )^ + ** + ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] + ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
        +-**
        This parameter returns the number malloc attempts that might have ++**
        This parameter returns the number of malloc attempts that might have + ** been satisfied using lookaside memory but failed due to the amount of + ** memory requested being larger than the lookaside slot size. + ** Only the high-water value is meaningful; +-** the current value is always zero.)^ ++** the current value is always zero.
        )^ + ** + ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] + ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
        +-**
        This parameter returns the number malloc attempts that might have ++**
        This parameter returns the number of malloc attempts that might have + ** been satisfied using lookaside memory but failed due to all lookaside + ** memory already being in use. + ** Only the high-water value is meaningful; +-** the current value is always zero.)^ ++** the current value is always zero.
        )^ + ** + ** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
        SQLITE_DBSTATUS_CACHE_USED
        + **
        This parameter returns the approximate number of bytes of heap + ** memory used by all pager caches associated with the database connection.)^ + ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ++**
        + ** + ** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]] + ** ^(
        SQLITE_DBSTATUS_CACHE_USED_SHARED
        +@@ -9134,10 +9256,10 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** memory used by that pager cache is divided evenly between the attached + ** connections.)^ In other words, if none of the pager caches associated + ** with the database connection are shared, this request returns the same +-** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are ++** value as DBSTATUS_CACHE_USED. Or, if one or more of the pager caches are + ** shared, the value returned by this call will be smaller than that returned + ** by DBSTATUS_CACHE_USED. ^The highwater mark associated with +-** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. ++** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. + ** + ** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
        SQLITE_DBSTATUS_SCHEMA_USED
        + **
        This parameter returns the approximate number of bytes of heap +@@ -9147,6 +9269,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** schema memory is shared with other database connections due to + ** [shared cache mode] being enabled. + ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. ++**
        + ** + ** [[SQLITE_DBSTATUS_STMT_USED]] ^(
        SQLITE_DBSTATUS_STMT_USED
        + **
        This parameter returns the approximate number of bytes of heap +@@ -9183,7 +9306,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** been written to disk in the middle of a transaction due to the page + ** cache overflowing. Transactions are more efficient if they are written + ** to disk all at once. When pages spill mid-transaction, that introduces +-** additional overhead. This parameter can be used help identify ++** additional overhead. This parameter can be used to help identify + ** inefficiencies that can be resolved by increasing the cache size. + **
        + ** +@@ -9254,13 +9377,13 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + ** [[SQLITE_STMTSTATUS_SORT]]
        SQLITE_STMTSTATUS_SORT
        + **
        ^This is the number of sort operations that have occurred. + ** A non-zero value in this counter may indicate an opportunity to +-** improvement performance through careful use of indices.
        ++** improve performance through careful use of indices. + ** + ** [[SQLITE_STMTSTATUS_AUTOINDEX]]
        SQLITE_STMTSTATUS_AUTOINDEX
        + **
        ^This is the number of rows inserted into transient indices that + ** were created automatically in order to help joins run faster. + ** A non-zero value in this counter may indicate an opportunity to +-** improvement performance by adding permanent indices that do not ++** improve performance by adding permanent indices that do not + ** need to be reinitialized each time the statement is run.
        + ** + ** [[SQLITE_STMTSTATUS_VM_STEP]]
        SQLITE_STMTSTATUS_VM_STEP
        +@@ -9269,19 +9392,29 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + ** to 2147483647. The number of virtual machine operations can be + ** used as a proxy for the total work done by the prepared statement. + ** If the number of virtual machine operations exceeds 2147483647 +-** then the value returned by this statement status code is undefined. ++** then the value returned by this statement status code is undefined. + ** + ** [[SQLITE_STMTSTATUS_REPREPARE]]
        SQLITE_STMTSTATUS_REPREPARE
        + **
        ^This is the number of times that the prepare statement has been + ** automatically regenerated due to schema changes or changes to +-** [bound parameters] that might affect the query plan. ++** [bound parameters] that might affect the query plan.
        + ** + ** [[SQLITE_STMTSTATUS_RUN]]
        SQLITE_STMTSTATUS_RUN
        + **
        ^This is the number of times that the prepared statement has + ** been run. A single "run" for the purposes of this counter is one + ** or more calls to [sqlite3_step()] followed by a call to [sqlite3_reset()]. + ** The counter is incremented on the first [sqlite3_step()] call of each +-** cycle. ++** cycle.
        ++** ++** [[SQLITE_STMTSTATUS_FILTER_MISS]] ++** [[SQLITE_STMTSTATUS_FILTER HIT]] ++**
        SQLITE_STMTSTATUS_FILTER_HIT
        ++** SQLITE_STMTSTATUS_FILTER_MISS
        ++**
        ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join ++** step was bypassed because a Bloom filter returned not-found. The ++** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of ++** times that the Bloom filter returned a find, and thus the join step ++** had to be processed as normal.
        + ** + ** [[SQLITE_STMTSTATUS_MEMUSED]]
        SQLITE_STMTSTATUS_MEMUSED
        + **
        ^This is the approximate number of bytes of heap memory +@@ -9297,6 +9430,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + #define SQLITE_STMTSTATUS_VM_STEP 4 + #define SQLITE_STMTSTATUS_REPREPARE 5 + #define SQLITE_STMTSTATUS_RUN 6 ++#define SQLITE_STMTSTATUS_FILTER_MISS 7 ++#define SQLITE_STMTSTATUS_FILTER_HIT 8 + #define SQLITE_STMTSTATUS_MEMUSED 99 + + /* +@@ -9384,9 +9519,9 @@ struct sqlite3_pcache_page { + ** SQLite will typically create one cache instance for each open database file, + ** though this is not guaranteed. ^The + ** first parameter, szPage, is the size in bytes of the pages that must +-** be allocated by the cache. ^szPage will always a power of two. ^The ++** be allocated by the cache. ^szPage will always be a power of two. ^The + ** second parameter szExtra is a number of bytes of extra storage +-** associated with each page cache entry. ^The szExtra parameter will ++** associated with each page cache entry. ^The szExtra parameter will be + ** a number less than 250. SQLite will use the + ** extra szExtra bytes on each page to store metadata about the underlying + ** database page on disk. The value passed into szExtra depends +@@ -9394,17 +9529,17 @@ struct sqlite3_pcache_page { + ** ^The third argument to xCreate(), bPurgeable, is true if the cache being + ** created will be used to cache database pages of a file stored on disk, or + ** false if it is used for an in-memory database. The cache implementation +-** does not have to do anything special based with the value of bPurgeable; ++** does not have to do anything special based upon the value of bPurgeable; + ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will + ** never invoke xUnpin() except to deliberately delete a page. + ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to + ** false will always have the "discard" flag set to true. +-** ^Hence, a cache created with bPurgeable false will ++** ^Hence, a cache created with bPurgeable set to false will + ** never contain any unpinned pages. + ** + ** [[the xCachesize() page cache method]] + ** ^(The xCachesize() method may be called at any time by SQLite to set the +-** suggested maximum cache-size (number of pages stored by) the cache ++** suggested maximum cache-size (number of pages stored) for the cache + ** instance passed as the first argument. This is the value configured using + ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable + ** parameter, the implementation is not required to do anything with this +@@ -9431,12 +9566,12 @@ struct sqlite3_pcache_page { + ** implementation must return a pointer to the page buffer with its content + ** intact. If the requested page is not already in the cache, then the + ** cache implementation should use the value of the createFlag +-** parameter to help it determined what action to take: ++** parameter to help it determine what action to take: + ** + ** + **
        createFlag Behavior when page is not already in cache + **
        0 Do not allocate a new page. Return NULL. +-**
        1 Allocate a new page if it easy and convenient to do so. ++**
        1 Allocate a new page if it is easy and convenient to do so. + ** Otherwise return NULL. + **
        2 Make every effort to allocate a new page. Only return + ** NULL if allocating a new page is effectively impossible. +@@ -9453,7 +9588,7 @@ struct sqlite3_pcache_page { + ** as its second argument. If the third parameter, discard, is non-zero, + ** then the page must be evicted from the cache. + ** ^If the discard parameter is +-** zero, then the page may be discarded or retained at the discretion of ++** zero, then the page may be discarded or retained at the discretion of the + ** page cache implementation. ^The page cache implementation + ** may choose to evict unpinned pages at any time. + ** +@@ -9471,7 +9606,7 @@ struct sqlite3_pcache_page { + ** When SQLite calls the xTruncate() method, the cache must discard all + ** existing cache entries with page numbers (keys) greater than or equal + ** to the value of the iLimit parameter passed to xTruncate(). If any +-** of these pages are pinned, they are implicitly unpinned, meaning that ++** of these pages are pinned, they become implicitly unpinned, meaning that + ** they can be safely discarded. + ** + ** [[the xDestroy() page cache method]] +@@ -9651,7 +9786,7 @@ typedef struct sqlite3_backup sqlite3_backup; + ** external process or via a database connection other than the one being + ** used by the backup operation, then the backup will be automatically + ** restarted by the next call to sqlite3_backup_step(). ^If the source +-** database is modified by the using the same database connection as is used ++** database is modified by using the same database connection as is used + ** by the backup operation, then the backup database is automatically + ** updated at the same time. + ** +@@ -9668,7 +9803,7 @@ typedef struct sqlite3_backup sqlite3_backup; + ** and may not be used following a call to sqlite3_backup_finish(). + ** + ** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no +-** sqlite3_backup_step() errors occurred, regardless or whether or not ++** sqlite3_backup_step() errors occurred, regardless of whether or not + ** sqlite3_backup_step() completed. + ** ^If an out-of-memory condition or IO error occurred during any prior + ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then +@@ -9708,7 +9843,7 @@ typedef struct sqlite3_backup sqlite3_backup; + ** if the application incorrectly accesses the destination [database connection] + ** and so no error code is reported, but the operations may malfunction + ** nevertheless. Use of the destination database connection while a +-** backup is in progress might also also cause a mutex deadlock. ++** backup is in progress might also cause a mutex deadlock. + ** + ** If running in [shared cache mode], the application must + ** guarantee that the shared cache used by the destination database +@@ -9723,6 +9858,16 @@ typedef struct sqlite3_backup sqlite3_backup; + ** APIs are not strictly speaking threadsafe. If they are invoked at the + ** same time as another thread is invoking sqlite3_backup_step() it is + ** possible that they return invalid values. ++** ++** Alternatives To Using The Backup API ++** ++** Other techniques for safely creating a consistent backup of an SQLite ++** database include: ++** ++**
          ++**
        • The [VACUUM INTO] command. ++**
        • The [sqlite3_rsync] utility program. ++**
        + */ + SQLITE_API sqlite3_backup *sqlite3_backup_init( + sqlite3 *pDest, /* Destination database handle */ +@@ -9760,7 +9905,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); + ** application receives an SQLITE_LOCKED error, it may call the + ** sqlite3_unlock_notify() method with the blocked connection handle as + ** the first argument to register for a callback that will be invoked +-** when the blocking connections current transaction is concluded. ^The ++** when the blocking connection's current transaction is concluded. ^The + ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] + ** call that concludes the blocking connection's transaction. + ** +@@ -9780,7 +9925,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); + ** blocked connection already has a registered unlock-notify callback, + ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is + ** called with a NULL pointer as its second argument, then any existing +-** unlock-notify callback is canceled. ^The blocked connections ++** unlock-notify callback is canceled. ^The blocked connection's + ** unlock-notify callback may also be canceled by closing the blocked + ** connection using [sqlite3_close()]. + ** +@@ -9960,8 +10105,9 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); + ** + ** A single database handle may have at most a single write-ahead log callback + ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any +-** previously registered write-ahead log callback. ^Note that the +-** [sqlite3_wal_autocheckpoint()] interface and the ++** previously registered write-ahead log callback. ^The return value is ++** a copy of the third parameter from the previous call, if any, or 0. ++** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the + ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will + ** overwrite any prior [sqlite3_wal_hook()] settings. + */ +@@ -10135,7 +10281,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( + */ + #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ + #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ +-#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ ++#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */ + #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ + + /* +@@ -10177,7 +10323,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + ** support constraints. In this configuration (which is the default) if + ** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire + ** statement is rolled back as if [ON CONFLICT | OR ABORT] had been +-** specified as part of the users SQL statement, regardless of the actual ++** specified as part of the user's SQL statement, regardless of the actual + ** ON CONFLICT mode specified. + ** + ** If X is non-zero, then the virtual table implementation guarantees +@@ -10203,7 +10349,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + ** [[SQLITE_VTAB_DIRECTONLY]]
        SQLITE_VTAB_DIRECTONLY
        + **
        Calls of the form + ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the +-** the [xConnect] or [xCreate] methods of a [virtual table] implmentation ++** the [xConnect] or [xCreate] methods of a [virtual table] implementation + ** prohibits that virtual table from being used from within triggers and + ** views. + **
        +@@ -10211,18 +10357,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + ** [[SQLITE_VTAB_INNOCUOUS]]
        SQLITE_VTAB_INNOCUOUS
        + **
        Calls of the form + ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the +-** the [xConnect] or [xCreate] methods of a [virtual table] implmentation ++** [xConnect] or [xCreate] methods of a [virtual table] implementation + ** identify that virtual table as being safe to use from within triggers + ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the + ** virtual table can do no serious harm even if it is controlled by a + ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS + ** flag unless absolutely necessary. + **
        ++** ++** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]
        SQLITE_VTAB_USES_ALL_SCHEMAS
        ++**
        Calls of the form ++** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the ++** the [xConnect] or [xCreate] methods of a [virtual table] implementation ++** instruct the query planner to begin at least a read transaction on ++** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the ++** virtual table is used. ++**
        + ** + */ + #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 + #define SQLITE_VTAB_INNOCUOUS 2 + #define SQLITE_VTAB_DIRECTONLY 3 ++#define SQLITE_VTAB_USES_ALL_SCHEMAS 4 + + /* + ** CAPI3REF: Determine The Virtual Table Conflict Policy +@@ -10240,10 +10396,11 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + ** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE + ** + ** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn] +-** method of a [virtual table], then it returns true if and only if the ++** method of a [virtual table], then it might return true if the + ** column is being fetched as part of an UPDATE operation during which the +-** column value will not change. Applications might use this to substitute +-** a return value that is less expensive to compute and that the corresponding ++** column value will not change. The virtual table implementation can use ++** this hint as permission to substitute a return value that is less ++** expensive to compute and that the corresponding + ** [xUpdate] method understands as a "no-change" value. + ** + ** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that +@@ -10252,31 +10409,314 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + ** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces]. + ** In that case, [sqlite3_value_nochange(X)] will return true for the + ** same column in the [xUpdate] method. ++** ++** The sqlite3_vtab_nochange() routine is an optimization. Virtual table ++** implementations should continue to give a correct answer even if the ++** sqlite3_vtab_nochange() interface were to always return false. In the ++** current implementation, the sqlite3_vtab_nochange() interface does always ++** returns false for the enhanced [UPDATE FROM] statement. + */ + SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*); + + /* + ** CAPI3REF: Determine The Collation For a Virtual Table Constraint ++** METHOD: sqlite3_index_info + ** + ** This function may only be called from within a call to the [xBestIndex] +-** method of a [virtual table]. ++** method of a [virtual table]. This function returns a pointer to a string ++** that is the name of the appropriate collation sequence to use for text ++** comparisons on the constraint identified by its arguments. + ** +-** The first argument must be the sqlite3_index_info object that is the +-** first parameter to the xBestIndex() method. The second argument must be +-** an index into the aConstraint[] array belonging to the sqlite3_index_info +-** structure passed to xBestIndex. This function returns a pointer to a buffer +-** containing the name of the collation sequence for the corresponding +-** constraint. ++** The first argument must be the pointer to the [sqlite3_index_info] object ++** that is the first parameter to the xBestIndex() method. The second argument ++** must be an index into the aConstraint[] array belonging to the ++** sqlite3_index_info structure passed to xBestIndex. ++** ++** Important: ++** The first parameter must be the same pointer that is passed into the ++** xBestMethod() method. The first parameter may not be a pointer to a ++** different [sqlite3_index_info] object, even an exact copy. ++** ++** The return value is computed as follows: ++** ++**
          ++**
        1. If the constraint comes from a WHERE clause expression that contains ++** a [COLLATE operator], then the name of the collation specified by ++** that COLLATE operator is returned. ++**

        2. If there is no COLLATE operator, but the column that is the subject ++** of the constraint specifies an alternative collating sequence via ++** a [COLLATE clause] on the column definition within the CREATE TABLE ++** statement that was passed into [sqlite3_declare_vtab()], then the ++** name of that alternative collating sequence is returned. ++**

        3. Otherwise, "BINARY" is returned. ++**

        + */ +-SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ++SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ++ ++/* ++** CAPI3REF: Determine if a virtual table query is DISTINCT ++** METHOD: sqlite3_index_info ++** ++** This API may only be used from within an [xBestIndex|xBestIndex method] ++** of a [virtual table] implementation. The result of calling this ++** interface from outside of xBestIndex() is undefined and probably harmful. ++** ++** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and ++** 3. The integer returned by sqlite3_vtab_distinct() ++** gives the virtual table additional information about how the query ++** planner wants the output to be ordered. As long as the virtual table ++** can meet the ordering requirements of the query planner, it may set ++** the "orderByConsumed" flag. ++** ++**
        1. ++** ^If the sqlite3_vtab_distinct() interface returns 0, that means ++** that the query planner needs the virtual table to return all rows in the ++** sort order defined by the "nOrderBy" and "aOrderBy" fields of the ++** [sqlite3_index_info] object. This is the default expectation. If the ++** virtual table outputs all rows in sorted order, then it is always safe for ++** the xBestIndex method to set the "orderByConsumed" flag, regardless of ++** the return value from sqlite3_vtab_distinct(). ++**

        2. ++** ^(If the sqlite3_vtab_distinct() interface returns 1, that means ++** that the query planner does not need the rows to be returned in sorted order ++** as long as all rows with the same values in all columns identified by the ++** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ++** is doing a GROUP BY. ++**

        3. ++** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ++** that the query planner does not need the rows returned in any particular ++** order, as long as rows with the same values in all columns identified ++** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows ++** contain the same values for all columns identified by "colUsed", all but ++** one such row may optionally be omitted from the result.)^ ++** The virtual table is not required to omit rows that are duplicates ++** over the "colUsed" columns, but if the virtual table can do that without ++** too much extra effort, it could potentially help the query to run faster. ++** This mode is used for a DISTINCT query. ++**

        4. ++** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the ++** virtual table must return rows in the order defined by "aOrderBy" as ++** if the sqlite3_vtab_distinct() interface had returned 0. However if ++** two or more rows in the result have the same values for all columns ++** identified by "colUsed", then all but one such row may optionally be ++** omitted.)^ Like when the return value is 2, the virtual table ++** is not required to omit rows that are duplicates over the "colUsed" ++** columns, but if the virtual table can do that without ++** too much extra effort, it could potentially help the query to run faster. ++** This mode is used for queries ++** that have both DISTINCT and ORDER BY clauses. ++**

        ++** ++**

        The following table summarizes the conditions under which the ++** virtual table is allowed to set the "orderByConsumed" flag based on ++** the value returned by sqlite3_vtab_distinct(). This table is a ++** restatement of the previous four paragraphs: ++** ++** ++** ++**
        sqlite3_vtab_distinct() return value ++** Rows are returned in aOrderBy order ++** Rows with the same value in all aOrderBy columns are adjacent ++** Duplicates over all colUsed columns may be omitted ++**
        0yesyesno ++**
        1noyesno ++**
        2noyesyes ++**
        3yesyesyes ++**
        ++** ++** ^For the purposes of comparing virtual table output values to see if the ++** values are the same value for sorting purposes, two NULL values are considered ++** to be the same. In other words, the comparison operator is "IS" ++** (or "IS NOT DISTINCT FROM") and not "==". ++** ++** If a virtual table implementation is unable to meet the requirements ++** specified above, then it must not set the "orderByConsumed" flag in the ++** [sqlite3_index_info] object or an incorrect answer may result. ++** ++** ^A virtual table implementation is always free to return rows in any order ++** it wants, as long as the "orderByConsumed" flag is not set. ^When the ++** "orderByConsumed" flag is unset, the query planner will add extra ++** [bytecode] to ensure that the final results returned by the SQL query are ++** ordered correctly. The use of the "orderByConsumed" flag and the ++** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful ++** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed" ++** flag might help queries against a virtual table to run faster. Being ++** overly aggressive and setting the "orderByConsumed" flag when it is not ++** valid to do so, on the other hand, might cause SQLite to return incorrect ++** results. ++*/ ++SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); ++ ++/* ++** CAPI3REF: Identify and handle IN constraints in xBestIndex ++** ++** This interface may only be used from within an ++** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. ++** The result of invoking this interface from any other context is ++** undefined and probably harmful. ++** ++** ^(A constraint on a virtual table of the form ++** "[IN operator|column IN (...)]" is ++** communicated to the xBestIndex method as a ++** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ++** this constraint, it must set the corresponding ++** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under ++** the usual mode of handling IN operators, SQLite generates [bytecode] ++** that invokes the [xFilter|xFilter() method] once for each value ++** on the right-hand side of the IN operator.)^ Thus the virtual table ++** only sees a single value from the right-hand side of the IN operator ++** at a time. ++** ++** In some cases, however, it would be advantageous for the virtual ++** table to see all values on the right-hand of the IN operator all at ++** once. The sqlite3_vtab_in() interfaces facilitates this in two ways: ++** ++**

          ++**
        1. ++** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero) ++** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint ++** is an [IN operator] that can be processed all at once. ^In other words, ++** sqlite3_vtab_in() with -1 in the third argument is a mechanism ++** by which the virtual table can ask SQLite if all-at-once processing ++** of the IN operator is even possible. ++** ++**

        2. ++** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates ++** to SQLite that the virtual table does or does not want to process ++** the IN operator all-at-once, respectively. ^Thus when the third ++** parameter (F) is non-negative, this interface is the mechanism by ++** which the virtual table tells SQLite how it wants to process the ++** IN operator. ++**

        ++** ++** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times ++** within the same xBestIndex method call. ^For any given P,N pair, ++** the return value from sqlite3_vtab_in(P,N,F) will always be the same ++** within the same xBestIndex call. ^If the interface returns true ++** (non-zero), that means that the constraint is an IN operator ++** that can be processed all-at-once. ^If the constraint is not an IN ++** operator or cannot be processed all-at-once, then the interface returns ++** false. ++** ++** ^(All-at-once processing of the IN operator is selected if both of the ++** following conditions are met: ++** ++**
          ++**
        1. The P->aConstraintUsage[N].argvIndex value is set to a positive ++** integer. This is how the virtual table tells SQLite that it wants to ++** use the N-th constraint. ++** ++**

        2. The last call to sqlite3_vtab_in(P,N,F) for which F was ++** non-negative had F>=1. ++**

        )^ ++** ++** ^If either or both of the conditions above are false, then SQLite uses ++** the traditional one-at-a-time processing strategy for the IN constraint. ++** ^If both conditions are true, then the argvIndex-th parameter to the ++** xFilter method will be an [sqlite3_value] that appears to be NULL, ++** but which can be passed to [sqlite3_vtab_in_first()] and ++** [sqlite3_vtab_in_next()] to find all values on the right-hand side ++** of the IN constraint. ++*/ ++SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); ++ ++/* ++** CAPI3REF: Find all elements on the right-hand side of an IN constraint. ++** ++** These interfaces are only useful from within the ++** [xFilter|xFilter() method] of a [virtual table] implementation. ++** The result of invoking these interfaces from any other context ++** is undefined and probably harmful. ++** ++** The X parameter in a call to sqlite3_vtab_in_first(X,P) or ++** sqlite3_vtab_in_next(X,P) should be one of the parameters to the ++** xFilter method which invokes these routines, and specifically ++** a parameter that was previously selected for all-at-once IN constraint ++** processing using the [sqlite3_vtab_in()] interface in the ++** [xBestIndex|xBestIndex method]. ^(If the X parameter is not ++** an xFilter argument that was selected for all-at-once IN constraint ++** processing, then these routines return [SQLITE_ERROR].)^ ++** ++** ^(Use these routines to access all values on the right-hand side ++** of the IN constraint using code like the following: ++** ++**
        ++**    for(rc=sqlite3_vtab_in_first(pList, &pVal);
        ++**        rc==SQLITE_OK && pVal;
        ++**        rc=sqlite3_vtab_in_next(pList, &pVal)
        ++**    ){
        ++**      // do something with pVal
        ++**    }
        ++**    if( rc!=SQLITE_OK ){
        ++**      // an error has occurred
        ++**    }
        ++** 
        )^ ++** ++** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P) ++** routines return SQLITE_OK and set *P to point to the first or next value ++** on the RHS of the IN constraint. ^If there are no more values on the ++** right hand side of the IN constraint, then *P is set to NULL and these ++** routines return [SQLITE_DONE]. ^The return value might be ++** some other value, such as SQLITE_NOMEM, in the event of a malfunction. ++** ++** The *ppOut values returned by these routines are only valid until the ++** next call to either of these routines or until the end of the xFilter ++** method from which these routines were called. If the virtual table ++** implementation needs to retain the *ppOut values for longer, it must make ++** copies. The *ppOut values are [protected sqlite3_value|protected]. ++*/ ++SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); ++SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); ++ ++/* ++** CAPI3REF: Constraint values in xBestIndex() ++** METHOD: sqlite3_index_info ++** ++** This API may only be used from within the [xBestIndex|xBestIndex method] ++** of a [virtual table] implementation. The result of calling this interface ++** from outside of an xBestIndex method are undefined and probably harmful. ++** ++** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within ++** the [xBestIndex] method of a [virtual table] implementation, with P being ++** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and ++** J being a 0-based index into P->aConstraint[], then this routine ++** attempts to set *V to the value of the right-hand operand of ++** that constraint if the right-hand operand is known. ^If the ++** right-hand operand is not known, then *V is set to a NULL pointer. ++** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if ++** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) ++** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th ++** constraint is not available. ^The sqlite3_vtab_rhs_value() interface ++** can return a result code other than SQLITE_OK or SQLITE_NOTFOUND if ++** something goes wrong. ++** ++** The sqlite3_vtab_rhs_value() interface is usually only successful if ++** the right-hand operand of a constraint is a literal value in the original ++** SQL statement. If the right-hand operand is an expression or a reference ++** to some other column or a [host parameter], then sqlite3_vtab_rhs_value() ++** will probably return [SQLITE_NOTFOUND]. ++** ++** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and ++** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such ++** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ ++** ++** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value ++** and remains valid for the duration of the xBestIndex method call. ++** ^When xBestIndex returns, the sqlite3_value object returned by ++** sqlite3_vtab_rhs_value() is automatically deallocated. ++** ++** The "_rhs_" in the name of this routine is an abbreviation for ++** "Right-Hand Side". ++*/ ++SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal); + + /* + ** CAPI3REF: Conflict resolution modes + ** KEYWORDS: {conflict resolution mode} + ** + ** These constants are returned by [sqlite3_vtab_on_conflict()] to +-** inform a [virtual table] implementation what the [ON CONFLICT] mode +-** is for the SQL statement being evaluated. ++** inform a [virtual table] implementation of the [ON CONFLICT] mode ++** for the SQL statement being evaluated. + ** + ** Note that the [SQLITE_IGNORE] constant is also used as a potential + ** return value from the [sqlite3_set_authorizer()] callback and that +@@ -10300,6 +10740,10 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + ** managed by the prepared statement S and will be automatically freed when + ** S is finalized. + ** ++** Not all values are available for all query elements. When a value is ++** not available, the output variable is set to -1 if the value is numeric, ++** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). ++** + **
        + ** [[SQLITE_SCANSTAT_NLOOP]]
        SQLITE_SCANSTAT_NLOOP
        + **
        ^The [sqlite3_int64] variable pointed to by the V parameter will be +@@ -10312,27 +10756,39 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + ** [[SQLITE_SCANSTAT_EST]]
        SQLITE_SCANSTAT_EST
        + **
        ^The "double" variable pointed to by the V parameter will be set to the + ** query planner's estimate for the average number of rows output from each +-** iteration of the X-th loop. If the query planner's estimates was accurate, ++** iteration of the X-th loop. If the query planner's estimate was accurate, + ** then this value will approximate the quotient NVISIT/NLOOP and the + ** product of this value for all prior loops with the same SELECTID will +-** be the NLOOP value for the current loop. ++** be the NLOOP value for the current loop.
        + ** + ** [[SQLITE_SCANSTAT_NAME]]
        SQLITE_SCANSTAT_NAME
        + **
        ^The "const char *" variable pointed to by the V parameter will be set + ** to a zero-terminated UTF-8 string containing the name of the index or table +-** used for the X-th loop. ++** used for the X-th loop.
        + ** + ** [[SQLITE_SCANSTAT_EXPLAIN]]
        SQLITE_SCANSTAT_EXPLAIN
        + **
        ^The "const char *" variable pointed to by the V parameter will be set + ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] +-** description for the X-th loop. ++** description for the X-th loop.
        + ** +-** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECT
        ++** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECTID
        + **
        ^The "int" variable pointed to by the V parameter will be set to the +-** "select-id" for the X-th loop. The select-id identifies which query or +-** subquery the loop is part of. The main query has a select-id of zero. +-** The select-id is the same value as is output in the first column +-** of an [EXPLAIN QUERY PLAN] query. ++** id for the X-th query plan element. The id value is unique within the ++** statement. The select-id is the same value as is output in the first ++** column of an [EXPLAIN QUERY PLAN] query.
        ++** ++** [[SQLITE_SCANSTAT_PARENTID]]
        SQLITE_SCANSTAT_PARENTID
        ++**
        The "int" variable pointed to by the V parameter will be set to the ++** id of the parent of the current query element, if applicable, or ++** to zero if the query element has no parent. This is the same value as ++** returned in the second column of an [EXPLAIN QUERY PLAN] query.
        ++** ++** [[SQLITE_SCANSTAT_NCYCLE]]
        SQLITE_SCANSTAT_NCYCLE
        ++**
        The sqlite3_int64 output value is set to the number of cycles, ++** according to the processor time-stamp counter, that elapsed while the ++** query element was being processed. This value is not available for ++** all query elements - if it is unavailable the output variable is ++** set to -1.
        + **
        + */ + #define SQLITE_SCANSTAT_NLOOP 0 +@@ -10341,12 +10797,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + #define SQLITE_SCANSTAT_NAME 3 + #define SQLITE_SCANSTAT_EXPLAIN 4 + #define SQLITE_SCANSTAT_SELECTID 5 ++#define SQLITE_SCANSTAT_PARENTID 6 ++#define SQLITE_SCANSTAT_NCYCLE 7 + + /* + ** CAPI3REF: Prepared Statement Scan Status + ** METHOD: sqlite3_stmt + ** +-** This interface returns information about the predicted and measured ++** These interfaces return information about the predicted and measured + ** performance for pStmt. Advanced applications can use this + ** interface to compare the predicted and the measured performance and + ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. +@@ -10357,19 +10815,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + ** + ** The "iScanStatusOp" parameter determines which status information to return. + ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior +-** of this interface is undefined. +-** ^The requested measurement is written into a variable pointed to by +-** the "pOut" parameter. +-** Parameter "idx" identifies the specific loop to retrieve statistics for. +-** Loops are numbered starting from zero. ^If idx is out of range - less than +-** zero or greater than or equal to the total number of loops used to implement +-** the statement - a non-zero value is returned and the variable that pOut +-** points to is unchanged. +-** +-** ^Statistics might not be available for all loops in all statements. ^In cases +-** where there exist loops with no available statistics, this function behaves +-** as if the loop did not exist - it returns non-zero and leave the variable +-** that pOut points to unchanged. ++** of this interface is undefined. ^The requested measurement is written into ++** a variable pointed to by the "pOut" parameter. ++** ++** The "flags" parameter must be passed a mask of flags. At present only ++** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX ++** is specified, then status information is available for all elements ++** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If ++** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements ++** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of ++** the EXPLAIN QUERY PLAN output) are available. Invoking API ++** sqlite3_stmt_scanstatus() is equivalent to calling ++** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. ++** ++** Parameter "idx" identifies the specific query element to retrieve statistics ++** for. Query elements are numbered starting from zero. A value of -1 may ++** retrieve statistics for the entire query. ^If idx is out of range ++** - less than -1 or greater than or equal to the total number of query ++** elements used to implement the statement - a non-zero value is returned and ++** the variable that pOut points to is unchanged. + ** + ** See also: [sqlite3_stmt_scanstatus_reset()] + */ +@@ -10379,6 +10843,19 @@ SQLITE_API int sqlite3_stmt_scanstatus( + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + void *pOut /* Result written here */ + ); ++SQLITE_API int sqlite3_stmt_scanstatus_v2( ++ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ ++ int idx, /* Index of loop to report on */ ++ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ ++ int flags, /* Mask of flags defined below */ ++ void *pOut /* Result written here */ ++); ++ ++/* ++** CAPI3REF: Prepared Statement Scan Status ++** KEYWORDS: {scan status flags} ++*/ ++#define SQLITE_SCANSTAT_COMPLEX 0x0001 + + /* + ** CAPI3REF: Zero Scan-Status Counters +@@ -10393,9 +10870,10 @@ SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); + + /* + ** CAPI3REF: Flush caches to disk mid-transaction ++** METHOD: sqlite3 + ** + ** ^If a write-transaction is open on [database connection] D when the +-** [sqlite3_db_cacheflush(D)] interface invoked, any dirty ++** [sqlite3_db_cacheflush(D)] interface is invoked, any dirty + ** pages in the pager-cache that are not currently in use are written out + ** to disk. A dirty page may be in use if a database cursor created by an + ** active SQL statement is reading from it, or if it is page 1 of a database +@@ -10425,6 +10903,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); + + /* + ** CAPI3REF: The pre-update hook. ++** METHOD: sqlite3 + ** + ** ^These interfaces are only available if SQLite is compiled using the + ** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option. +@@ -10465,7 +10944,11 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); + ** seventh parameter is the final rowid value of the row being inserted + ** or updated. The value of the seventh parameter passed to the callback + ** function is not defined for operations on WITHOUT ROWID tables, or for +-** INSERT operations on rowid tables. ++** DELETE operations on rowid tables. ++** ++** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from ++** the previous call on the same [database connection] D, or NULL for ++** the first call on D. + ** + ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], + ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces +@@ -10503,6 +10986,15 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); + ** triggers; or 2 for changes resulting from triggers called by top-level + ** triggers; and so forth. + ** ++** When the [sqlite3_blob_write()] API is used to update a blob column, ++** the pre-update hook is invoked with SQLITE_DELETE, because ++** the new values are not yet available. In this case, when a ++** callback made with op==SQLITE_DELETE is actually a write using the ++** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ++** the index of the column being written. In other cases, where the ++** pre-update hook is being invoked for some other reason, including a ++** regular DELETE, sqlite3_preupdate_blobwrite() returns -1. ++** + ** See also: [sqlite3_update_hook()] + */ + #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) +@@ -10523,10 +11015,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); + SQLITE_API int sqlite3_preupdate_count(sqlite3 *); + SQLITE_API int sqlite3_preupdate_depth(sqlite3 *); + SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); ++SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *); + #endif + + /* + ** CAPI3REF: Low-level system error code ++** METHOD: sqlite3 + ** + ** ^Attempt to return the underlying operating system error code or error + ** number that caused the most recent I/O error or failure to open a file. +@@ -10573,6 +11067,14 @@ typedef struct sqlite3_snapshot { + ** If there is not already a read-transaction open on schema S when + ** this function is called, one is opened automatically. + ** ++** If a read-transaction is opened by this function, then it is guaranteed ++** that the returned snapshot object may not be invalidated by a database ++** writer or checkpointer until after the read-transaction is closed. This ++** is not guaranteed if a read-transaction is already open when this ++** function is called. In that case, any subsequent write or checkpoint ++** operation on the database may invalidate the returned snapshot handle, ++** even while the read-transaction remains open. ++** + ** The following must be true for this function to succeed. If any of + ** the following statements are false when sqlite3_snapshot_get() is + ** called, SQLITE_ERROR is returned. The final value of *P is undefined +@@ -10730,15 +11232,16 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c + /* + ** CAPI3REF: Serialize a database + ** +-** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory +-** that is a serialization of the S database on [database connection] D. ++** The sqlite3_serialize(D,S,P,F) interface returns a pointer to ++** memory that is a serialization of the S database on ++** [database connection] D. If S is a NULL pointer, the main database is used. + ** If P is not a NULL pointer, then the size of the database in bytes + ** is written into *P. + ** + ** For an ordinary on-disk database file, the serialization is just a + ** copy of the disk file. For an in-memory database or a "TEMP" database, + ** the serialization is the same sequence of bytes which would be written +-** to disk if that database where backed up to disk. ++** to disk if that database were backed up to disk. + ** + ** The usual case is that sqlite3_serialize() copies the serialization of + ** the database into memory obtained from [sqlite3_malloc64()] and returns +@@ -10747,7 +11250,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c + ** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations + ** are made, and the sqlite3_serialize() function will return a pointer + ** to the contiguous memory representation of the database that SQLite +-** is currently using for that database, or NULL if the no such contiguous ++** is currently using for that database, or NULL if no such contiguous + ** memory representation of the database exists. A contiguous memory + ** representation of the database will usually only exist if there has + ** been a prior call to [sqlite3_deserialize(D,S,...)] with the same +@@ -10756,12 +11259,19 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c + ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy + ** of the database exists. + ** ++** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, ++** the returned buffer content will remain accessible and unchanged ++** until either the next write operation on the connection or when ++** the connection is closed, and applications must not modify the ++** buffer. If the bit had been clear, the returned buffer will not ++** be accessed by SQLite after the call. ++** + ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the + ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory + ** allocation error occurs. + ** +-** This interface is only available if SQLite is compiled with the +-** [SQLITE_ENABLE_DESERIALIZE] option. ++** This interface is omitted if SQLite is compiled with the ++** [SQLITE_OMIT_DESERIALIZE] option. + */ + SQLITE_API unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ +@@ -10804,22 +11314,36 @@ SQLITE_API unsigned char *sqlite3_serialize( + ** SQLite will try to increase the buffer size using sqlite3_realloc64() + ** if writes on the database cause it to grow larger than M bytes. + ** ++** Applications must not modify the buffer P or invalidate it before ++** the database connection D is closed. ++** + ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the + ** database is currently in a read transaction or is involved in a backup + ** operation. + ** ++** It is not possible to deserialize into the TEMP database. If the ++** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ++** function returns SQLITE_ERROR. ++** ++** The deserialized database should not be in [WAL mode]. If the database ++** is in WAL mode, then any attempt to use the database file will result ++** in an [SQLITE_CANTOPEN] error. The application can set the ++** [file format version numbers] (bytes 18 and 19) of the input database P ++** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the ++** database file into rollback mode and work around this limitation. ++** + ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the + ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then + ** [sqlite3_free()] is invoked on argument P prior to returning. + ** +-** This interface is only available if SQLite is compiled with the +-** [SQLITE_ENABLE_DESERIALIZE] option. ++** This interface is omitted if SQLite is compiled with the ++** [SQLITE_OMIT_DESERIALIZE] option. + */ + SQLITE_API int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ +- sqlite3_int64 szDb, /* Number bytes in the deserialization */ ++ sqlite3_int64 szDb, /* Number of bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ + ); +@@ -10827,7 +11351,7 @@ SQLITE_API int sqlite3_deserialize( + /* + ** CAPI3REF: Flags for sqlite3_deserialize() + ** +-** The following are allowed values for 6th argument (the F argument) to ++** The following are allowed values for the 6th argument (the F argument) to + ** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. + ** + ** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization +@@ -10857,10 +11381,21 @@ SQLITE_API int sqlite3_deserialize( + # undef double + #endif + ++#if defined(__wasi__) ++# undef SQLITE_WASI ++# define SQLITE_WASI 1 ++# ifndef SQLITE_OMIT_LOAD_EXTENSION ++# define SQLITE_OMIT_LOAD_EXTENSION ++# endif ++# ifndef SQLITE_THREADSAFE ++# define SQLITE_THREADSAFE 0 ++# endif ++#endif ++ + #if 0 + } /* End of the 'extern "C"' block */ + #endif +-#endif /* SQLITE3_H */ ++/* #endif for SQLITE3_H will be added by mksqlite3.tcl */ + + /******** Begin file sqlite3rtree.h *********/ + /* +@@ -11062,6 +11597,51 @@ SQLITE_API int sqlite3session_create( + */ + SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); + ++/* ++** CAPI3REF: Configure a Session Object ++** METHOD: sqlite3_session ++** ++** This method is used to configure a session object after it has been ++** created. At present the only valid values for the second parameter are ++** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ++** ++*/ ++SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); ++ ++/* ++** CAPI3REF: Options for sqlite3session_object_config ++** ++** The following values may passed as the the 2nd parameter to ++** sqlite3session_object_config(). ++** ++**
        SQLITE_SESSION_OBJCONFIG_SIZE
        ++** This option is used to set, clear or query the flag that enables ++** the [sqlite3session_changeset_size()] API. Because it imposes some ++** computational overhead, this API is disabled by default. Argument ++** pArg must point to a value of type (int). If the value is initially ++** 0, then the sqlite3session_changeset_size() API is disabled. If it ++** is greater than 0, then the same API is enabled. Or, if the initial ++** value is less than zero, no change is made. In all cases the (int) ++** variable is set to 1 if the sqlite3session_changeset_size() API is ++** enabled following the current call, or 0 otherwise. ++** ++** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ++** the first table has been attached to the session object. ++** ++**
        SQLITE_SESSION_OBJCONFIG_ROWID
        ++** This option is used to set, clear or query the flag that enables ++** collection of data for tables with no explicit PRIMARY KEY. ++** ++** Normally, tables with no explicit PRIMARY KEY are simply ignored ++** by the sessions module. However, if this flag is set, it behaves ++** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted ++** as their leftmost columns. ++** ++** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ++** the first table has been attached to the session object. ++*/ ++#define SQLITE_SESSION_OBJCONFIG_SIZE 1 ++#define SQLITE_SESSION_OBJCONFIG_ROWID 2 + + /* + ** CAPI3REF: Enable Or Disable A Session Object +@@ -11296,9 +11876,10 @@ SQLITE_API void sqlite3session_table_filter( + ** is inserted while a session object is enabled, then later deleted while + ** the same session object is disabled, no INSERT record will appear in the + ** changeset, even though the delete took place while the session was disabled. +-** Or, if one field of a row is updated while a session is disabled, and +-** another field of the same row is updated while the session is enabled, the +-** resulting changeset will contain an UPDATE change that updates both fields. ++** Or, if one field of a row is updated while a session is enabled, and ++** then another field of the same row is updated while the session is disabled, ++** the resulting changeset will contain an UPDATE change that updates both ++** fields. + */ + SQLITE_API int sqlite3session_changeset( + sqlite3_session *pSession, /* Session object */ +@@ -11306,6 +11887,22 @@ SQLITE_API int sqlite3session_changeset( + void **ppChangeset /* OUT: Buffer containing changeset */ + ); + ++/* ++** CAPI3REF: Return An Upper-limit For The Size Of The Changeset ++** METHOD: sqlite3_session ++** ++** By default, this function always returns 0. For it to return ++** a useful result, the sqlite3_session object must have been configured ++** to enable this API using sqlite3session_object_config() with the ++** SQLITE_SESSION_OBJCONFIG_SIZE verb. ++** ++** When enabled, this function returns an upper limit, in bytes, for the size ++** of the changeset that might be produced if sqlite3session_changeset() were ++** called. The final changeset size might be equal to or smaller than the ++** size in bytes returned by this function. ++*/ ++SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession); ++ + /* + ** CAPI3REF: Load The Difference Between Tables Into A Session + ** METHOD: sqlite3_session +@@ -11354,8 +11951,9 @@ SQLITE_API int sqlite3session_changeset( + ** database zFrom the contents of the two compatible tables would be + ** identical. + ** +-** It an error if database zFrom does not exist or does not contain the +-** required compatible table. ++** Unless the call to this function is a no-op as described above, it is an ++** error if database zFrom does not exist or does not contain the required ++** compatible table. + ** + ** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite + ** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg +@@ -11423,6 +12021,14 @@ SQLITE_API int sqlite3session_patchset( + */ + SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession); + ++/* ++** CAPI3REF: Query for the amount of heap memory used by a session object. ++** ++** This API returns the total amount of heap memory in bytes currently ++** used by the session object passed as the only argument. ++*/ ++SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession); ++ + /* + ** CAPI3REF: Create An Iterator To Traverse A Changeset + ** CONSTRUCTOR: sqlite3_changeset_iter +@@ -11482,7 +12088,7 @@ SQLITE_API int sqlite3changeset_start_v2( + ** The following flags may passed via the 4th parameter to + ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: + ** +-**
        SQLITE_CHANGESETAPPLY_INVERT
        ++**
        SQLITE_CHANGESETSTART_INVERT
        + ** Invert the changeset while iterating through it. This is equivalent to + ** inverting a changeset using sqlite3changeset_invert() before applying it. + ** It is an error to specify this flag with a patchset. +@@ -11525,18 +12131,23 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); + ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this + ** is not the case, this function returns [SQLITE_MISUSE]. + ** +-** If argument pzTab is not NULL, then *pzTab is set to point to a +-** nul-terminated utf-8 encoded string containing the name of the table +-** affected by the current change. The buffer remains valid until either +-** sqlite3changeset_next() is called on the iterator or until the +-** conflict-handler function returns. If pnCol is not NULL, then *pnCol is +-** set to the number of columns in the table affected by the change. If +-** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ++** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three ++** outputs are set through these pointers: ++** ++** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], ++** depending on the type of change that the iterator currently points to; ++** ++** *pnCol is set to the number of columns in the table affected by the change; and ++** ++** *pzTab is set to point to a nul-terminated utf-8 encoded string containing ++** the name of the table affected by the current change. The buffer remains ++** valid until either sqlite3changeset_next() is called on the iterator ++** or until the conflict-handler function returns. ++** ++** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change + ** is an indirect change, or false (0) otherwise. See the documentation for + ** [sqlite3session_indirect()] for a description of direct and indirect +-** changes. Finally, if pOp is not NULL, then *pOp is set to one of +-** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the +-** type of change that the iterator currently points to. ++** changes. + ** + ** If no error occurs, SQLITE_OK is returned. If an error does occur, an + ** SQLite error code is returned. The values of the output variables may not +@@ -11792,7 +12403,6 @@ SQLITE_API int sqlite3changeset_concat( + void **ppOut /* OUT: Buffer containing output changeset */ + ); + +- + /* + ** CAPI3REF: Changegroup Handle + ** +@@ -11839,6 +12449,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; + */ + SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); + ++/* ++** CAPI3REF: Add a Schema to a Changegroup ++** METHOD: sqlite3_changegroup_schema ++** ++** This method may be used to optionally enforce the rule that the changesets ++** added to the changegroup handle must match the schema of database zDb ++** ("main", "temp", or the name of an attached database). If ++** sqlite3changegroup_add() is called to add a changeset that is not compatible ++** with the configured schema, SQLITE_SCHEMA is returned and the changegroup ++** object is left in an undefined state. ++** ++** A changeset schema is considered compatible with the database schema in ++** the same way as for sqlite3changeset_apply(). Specifically, for each ++** table in the changeset, there exists a database table with: ++** ++**
          ++**
        • The name identified by the changeset, and ++**
        • at least as many columns as recorded in the changeset, and ++**
        • the primary key columns in the same position as recorded in ++** the changeset. ++**
        ++** ++** The output of the changegroup object always has the same schema as the ++** database nominated using this function. In cases where changesets passed ++** to sqlite3changegroup_add() have fewer columns than the corresponding table ++** in the database schema, these are filled in using the default column ++** values from the database schema. This makes it possible to combined ++** changesets that have different numbers of columns for a single table ++** within a changegroup, provided that they are otherwise compatible. ++*/ ++SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); ++ + /* + ** CAPI3REF: Add A Changeset To A Changegroup + ** METHOD: sqlite3_changegroup +@@ -11907,16 +12549,45 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); + ** If the new changeset contains changes to a table that is already present + ** in the changegroup, then the number of columns and the position of the + ** primary key columns for the table must be consistent. If this is not the +-** case, this function fails with SQLITE_SCHEMA. If the input changeset +-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is +-** returned. Or, if an out-of-memory condition occurs during processing, this +-** function returns SQLITE_NOMEM. In all cases, if an error occurs the state +-** of the final contents of the changegroup is undefined. ++** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup ++** object has been configured with a database schema using the ++** sqlite3changegroup_schema() API, then it is possible to combine changesets ++** with different numbers of columns for a single table, provided that ++** they are otherwise compatible. + ** +-** If no error occurs, SQLITE_OK is returned. ++** If the input changeset appears to be corrupt and the corruption is ++** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ++** occurs during processing, this function returns SQLITE_NOMEM. ++** ++** In all cases, if an error occurs the state of the final contents of the ++** changegroup is undefined. If no error occurs, SQLITE_OK is returned. + */ + SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); + ++/* ++** CAPI3REF: Add A Single Change To A Changegroup ++** METHOD: sqlite3_changegroup ++** ++** This function adds the single change currently indicated by the iterator ++** passed as the second argument to the changegroup object. The rules for ++** adding the change are just as described for [sqlite3changegroup_add()]. ++** ++** If the change is successfully added to the changegroup, SQLITE_OK is ++** returned. Otherwise, an SQLite error code is returned. ++** ++** The iterator must point to a valid entry when this function is called. ++** If it does not, SQLITE_ERROR is returned and no change is added to the ++** changegroup. Additionally, the iterator must not have been opened with ++** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also ++** returned. ++*/ ++SQLITE_API int sqlite3changegroup_add_change( ++ sqlite3_changegroup*, ++ sqlite3_changeset_iter* ++); ++ ++ ++ + /* + ** CAPI3REF: Obtain A Composite Changeset From A Changegroup + ** METHOD: sqlite3_changegroup +@@ -12165,9 +12836,30 @@ SQLITE_API int sqlite3changeset_apply_v2( + ** Invert the changeset before applying it. This is equivalent to inverting + ** a changeset using sqlite3changeset_invert() before applying it. It is + ** an error to specify this flag with a patchset. ++** ++**
        SQLITE_CHANGESETAPPLY_IGNORENOOP
        ++** Do not invoke the conflict handler callback for any changes that ++** would not actually modify the database even if they were applied. ++** Specifically, this means that the conflict handler is not invoked ++** for: ++**
          ++**
        • a delete change if the row being deleted cannot be found, ++**
        • an update change if the modified fields are already set to ++** their new values in the conflicting row, or ++**
        • an insert change if all fields of the conflicting row match ++** the row being inserted. ++**
        ++** ++**
        SQLITE_CHANGESETAPPLY_FKNOACTION
        ++** If this flag it set, then all foreign key constraints in the target ++** database behave as if they were declared with "ON UPDATE NO ACTION ON ++** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL ++** or SET DEFAULT. + */ + #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 + #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 ++#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 ++#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 + + /* + ** CAPI3REF: Constants Passed To The Conflict Handler +@@ -12700,8 +13392,8 @@ struct Fts5PhraseIter { + ** EXTENSION API FUNCTIONS + ** + ** xUserData(pFts): +-** Return a copy of the context pointer the extension function was +-** registered with. ++** Return a copy of the pUserData pointer passed to the xCreateFunction() ++** API when the extension function was registered. + ** + ** xColumnTotalSize(pFts, iCol, pnToken): + ** If parameter iCol is less than zero, set output variable *pnToken +@@ -12733,8 +13425,11 @@ struct Fts5PhraseIter { + ** created with the "columnsize=0" option. + ** + ** xColumnText: +-** This function attempts to retrieve the text of column iCol of the +-** current document. If successful, (*pz) is set to point to a buffer ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of columns in the table, SQLITE_RANGE is returned. ++** ++** Otherwise, this function attempts to retrieve the text of column iCol of ++** the current document. If successful, (*pz) is set to point to a buffer + ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes + ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, + ** if an error occurs, an SQLite error code is returned and the final values +@@ -12744,8 +13439,10 @@ struct Fts5PhraseIter { + ** Returns the number of phrases in the current query expression. + ** + ** xPhraseSize: +-** Returns the number of tokens in phrase iPhrase of the query. Phrases +-** are numbered starting from zero. ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of phrases in the current query, as returned by xPhraseCount, ++** 0 is returned. Otherwise, this function returns the number of tokens in ++** phrase iPhrase of the query. Phrases are numbered starting from zero. + ** + ** xInstCount: + ** Set *pnInst to the total number of occurrences of all phrases within +@@ -12761,12 +13458,13 @@ struct Fts5PhraseIter { + ** Query for the details of phrase match iIdx within the current row. + ** Phrase matches are numbered starting from zero, so the iIdx argument + ** should be greater than or equal to zero and smaller than the value +-** output by xInstCount(). ++** output by xInstCount(). If iIdx is less than zero or greater than ++** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. + ** +-** Usually, output parameter *piPhrase is set to the phrase number, *piCol ++** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol + ** to the column in which it occurs and *piOff the token offset of the +-** first token of the phrase. Returns SQLITE_OK if successful, or an error +-** code (i.e. SQLITE_NOMEM) if an error occurs. ++** first token of the phrase. SQLITE_OK is returned if successful, or an ++** error code (i.e. SQLITE_NOMEM) if an error occurs. + ** + ** This API can be quite slow if used with an FTS5 table created with the + ** "detail=none" or "detail=column" option. +@@ -12792,6 +13490,10 @@ struct Fts5PhraseIter { + ** Invoking Api.xUserData() returns a copy of the pointer passed as + ** the third argument to pUserData. + ** ++** If parameter iPhrase is less than zero, or greater than or equal to ++** the number of phrases in the query, as returned by xPhraseCount(), ++** this function returns SQLITE_RANGE. ++** + ** If the callback function returns any value other than SQLITE_OK, the + ** query is abandoned and the xQueryPhrase function returns immediately. + ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. +@@ -12873,6 +13575,10 @@ struct Fts5PhraseIter { + ** (i.e. if it is a contentless table), then this API always iterates + ** through an empty set (all calls to xPhraseFirst() set iCol to -1). + ** ++** In all cases, matches are visited in (column ASC, offset ASC) order. ++** i.e. all those in column 0, sorted by offset, followed by those in ++** column 1, etc. ++** + ** xPhraseNext() + ** See xPhraseFirst above. + ** +@@ -12906,9 +13612,80 @@ struct Fts5PhraseIter { + ** + ** xPhraseNextColumn() + ** See xPhraseFirstColumn above. ++** ++** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) ++** This is used to access token iToken of phrase iPhrase of the current ++** query. Before returning, output parameter *ppToken is set to point ++** to a buffer containing the requested token, and *pnToken to the ++** size of this buffer in bytes. ++** ++** If iPhrase or iToken are less than zero, or if iPhrase is greater than ++** or equal to the number of phrases in the query as reported by ++** xPhraseCount(), or if iToken is equal to or greater than the number of ++** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken ++ are both zeroed. ++** ++** The output text is not a copy of the query text that specified the ++** token. It is the output of the tokenizer module. For tokendata=1 ++** tables, this includes any embedded 0x00 and trailing data. ++** ++** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ++** This is used to access token iToken of phrase hit iIdx within the ++** current row. If iIdx is less than zero or greater than or equal to the ++** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ++** output variable (*ppToken) is set to point to a buffer containing the ++** matching document token, and (*pnToken) to the size of that buffer in ++** bytes. ++** ++** The output text is not a copy of the document text that was tokenized. ++** It is the output of the tokenizer module. For tokendata=1 tables, this ++** includes any embedded 0x00 and trailing data. ++** ++** This API may be slow in some cases if the token identified by parameters ++** iIdx and iToken matched a prefix token in the query. In most cases, the ++** first call to this API for each prefix token in the query is forced ++** to scan the portion of the full-text index that matches the prefix ++** token to collect the extra data required by this API. If the prefix ++** token matches a large number of token instances in the document set, ++** this may be a performance problem. ++** ++** If the user knows in advance that a query may use this API for a ++** prefix token, FTS5 may be configured to collect all required data as part ++** of the initial querying of the full-text index, avoiding the second scan ++** entirely. This also causes prefix queries that do not use this API to ++** run more slowly and use more memory. FTS5 may be configured in this way ++** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ++** option, or on a per-query basis using the ++** [fts5_insttoken | fts5_insttoken()] user function. ++** ++** This API can be quite slow if used with an FTS5 table created with the ++** "detail=none" or "detail=column" option. ++** ++** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of columns in the table, SQLITE_RANGE is returned. ++** ++** Otherwise, this function attempts to retrieve the locale associated ++** with column iCol of the current row. Usually, there is no associated ++** locale, and output parameters (*pzLocale) and (*pnLocale) are set ++** to NULL and 0, respectively. However, if the fts5_locale() function ++** was used to associate a locale with the value when it was inserted ++** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated ++** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) ++** is set to the size in bytes of the buffer, not including the ++** nul-terminator. ++** ++** If successful, SQLITE_OK is returned. Or, if an error occurs, an ++** SQLite error code is returned. The final value of the output parameters ++** is undefined in this case. ++** ++** xTokenize_v2: ++** Tokenize text using the tokenizer belonging to the FTS5 table. This ++** API is the same as the xTokenize() API, except that it allows a tokenizer ++** locale to be specified. + */ + struct Fts5ExtensionApi { +- int iVersion; /* Currently always set to 3 */ ++ int iVersion; /* Currently always set to 4 */ + + void *(*xUserData)(Fts5Context*); + +@@ -12943,6 +13720,22 @@ struct Fts5ExtensionApi { + + int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); + void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); ++ ++ /* Below this point are iVersion>=3 only */ ++ int (*xQueryToken)(Fts5Context*, ++ int iPhrase, int iToken, ++ const char **ppToken, int *pnToken ++ ); ++ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); ++ ++ /* Below this point are iVersion>=4 only */ ++ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); ++ int (*xTokenize_v2)(Fts5Context*, ++ const char *pText, int nText, /* Text to tokenize */ ++ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ ++ void *pCtx, /* Context passed to xToken() */ ++ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ ++ ); + }; + + /* +@@ -12963,7 +13756,7 @@ struct Fts5ExtensionApi { + ** A tokenizer instance is required to actually tokenize text. + ** + ** The first argument passed to this function is a copy of the (void*) +-** pointer provided by the application when the fts5_tokenizer object ++** pointer provided by the application when the fts5_tokenizer_v2 object + ** was registered with FTS5 (the third argument to xCreateTokenizer()). + ** The second and third arguments are an array of nul-terminated strings + ** containing the tokenizer arguments, if any, specified following the +@@ -12987,7 +13780,7 @@ struct Fts5ExtensionApi { + ** argument passed to this function is a pointer to an Fts5Tokenizer object + ** returned by an earlier call to xCreate(). + ** +-** The second argument indicates the reason that FTS5 is requesting ++** The third argument indicates the reason that FTS5 is requesting + ** tokenization of the supplied text. This is always one of the following + ** four values: + ** +@@ -13011,6 +13804,13 @@ struct Fts5ExtensionApi { + ** on a columnsize=0 database. + ** + ** ++** The sixth and seventh arguments passed to xTokenize() - pLocale and ++** nLocale - are a pointer to a buffer containing the locale to use for ++** tokenization (e.g. "en_US") and its size in bytes, respectively. The ++** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in ++** which case nLocale is always 0) to indicate that the tokenizer should ++** use its default locale. ++** + ** For each token in the input string, the supplied callback xToken() must + ** be invoked. The first argument to it should be a copy of the pointer + ** passed as the second argument to xTokenize(). The third and fourth +@@ -13034,6 +13834,30 @@ struct Fts5ExtensionApi { + ** may abandon the tokenization and return any error code other than + ** SQLITE_OK or SQLITE_DONE. + ** ++** If the tokenizer is registered using an fts5_tokenizer_v2 object, ++** then the xTokenize() method has two additional arguments - pLocale ++** and nLocale. These specify the locale that the tokenizer should use ++** for the current request. If pLocale and nLocale are both 0, then the ++** tokenizer should use its default locale. Otherwise, pLocale points to ++** an nLocale byte buffer containing the name of the locale to use as utf-8 ++** text. pLocale is not nul-terminated. ++** ++** FTS5_TOKENIZER ++** ++** There is also an fts5_tokenizer object. This is an older, deprecated, ++** version of fts5_tokenizer_v2. It is similar except that: ++** ++**
          ++**
        • There is no "iVersion" field, and ++**
        • The xTokenize() method does not take a locale argument. ++**
        ++** ++** Legacy fts5_tokenizer tokenizers must be registered using the ++** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). ++** ++** Tokenizer implementations registered using either API may be retrieved ++** using both xFindTokenizer() and xFindTokenizer_v2(). ++** + ** SYNONYM SUPPORT + ** + ** Custom tokenizers may also support synonyms. Consider a case in which a +@@ -13137,11 +13961,38 @@ struct Fts5ExtensionApi { + ** as separate queries of the FTS index are required for each synonym. + ** + ** When using methods (2) or (3), it is important that the tokenizer only +-** provide synonyms when tokenizing document text (method (2)) or query +-** text (method (3)), not both. Doing so will not cause any errors, but is ++** provide synonyms when tokenizing document text (method (3)) or query ++** text (method (2)), not both. Doing so will not cause any errors, but is + ** inefficient. + */ + typedef struct Fts5Tokenizer Fts5Tokenizer; ++typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; ++struct fts5_tokenizer_v2 { ++ int iVersion; /* Currently always 2 */ ++ ++ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); ++ void (*xDelete)(Fts5Tokenizer*); ++ int (*xTokenize)(Fts5Tokenizer*, ++ void *pCtx, ++ int flags, /* Mask of FTS5_TOKENIZE_* flags */ ++ const char *pText, int nText, ++ const char *pLocale, int nLocale, ++ int (*xToken)( ++ void *pCtx, /* Copy of 2nd argument to xTokenize() */ ++ int tflags, /* Mask of FTS5_TOKEN_* flags */ ++ const char *pToken, /* Pointer to buffer containing token */ ++ int nToken, /* Size of token in bytes */ ++ int iStart, /* Byte offset of token within input text */ ++ int iEnd /* Byte offset of end of token within input text */ ++ ) ++ ); ++}; ++ ++/* ++** New code should use the fts5_tokenizer_v2 type to define tokenizer ++** implementations. The following type is included for legacy applications ++** that still use it. ++*/ + typedef struct fts5_tokenizer fts5_tokenizer; + struct fts5_tokenizer { + int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); +@@ -13161,6 +14012,7 @@ struct fts5_tokenizer { + ); + }; + ++ + /* Flags that may be passed as the third argument to xTokenize() */ + #define FTS5_TOKENIZE_QUERY 0x0001 + #define FTS5_TOKENIZE_PREFIX 0x0002 +@@ -13180,13 +14032,13 @@ struct fts5_tokenizer { + */ + typedef struct fts5_api fts5_api; + struct fts5_api { +- int iVersion; /* Currently always set to 2 */ ++ int iVersion; /* Currently always set to 3 */ + + /* Create a new tokenizer */ + int (*xCreateTokenizer)( + fts5_api *pApi, + const char *zName, +- void *pContext, ++ void *pUserData, + fts5_tokenizer *pTokenizer, + void (*xDestroy)(void*) + ); +@@ -13195,7 +14047,7 @@ struct fts5_api { + int (*xFindTokenizer)( + fts5_api *pApi, + const char *zName, +- void **ppContext, ++ void **ppUserData, + fts5_tokenizer *pTokenizer + ); + +@@ -13203,10 +14055,29 @@ struct fts5_api { + int (*xCreateFunction)( + fts5_api *pApi, + const char *zName, +- void *pContext, ++ void *pUserData, + fts5_extension_function xFunction, + void (*xDestroy)(void*) + ); ++ ++ /* APIs below this point are only available if iVersion>=3 */ ++ ++ /* Create a new tokenizer */ ++ int (*xCreateTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void *pUserData, ++ fts5_tokenizer_v2 *pTokenizer, ++ void (*xDestroy)(void*) ++ ); ++ ++ /* Find an existing tokenizer */ ++ int (*xFindTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void **ppUserData, ++ fts5_tokenizer_v2 **ppTokenizer ++ ); + }; + + /* +@@ -13220,16 +14091,22 @@ struct fts5_api { + #endif /* _FTS5_H */ + + /******** End of fts5.h *********/ ++#endif /* SQLITE3_H */ + + /************** End of sqlite3.h *********************************************/ + /************** Continuing where we left off in sqliteInt.h ******************/ + ++/* ++** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory. ++*/ ++#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1 ++ + /* + ** Include the configuration header output by 'configure' if we're using the + ** autoconf-based build + */ + #if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) +-/* #include "config.h" */ ++#include "sqlite_cfg.h" + #define SQLITECONFIG_H 1 + #endif + +@@ -13260,6 +14137,7 @@ struct fts5_api { + #ifndef SQLITE_MAX_LENGTH + # define SQLITE_MAX_LENGTH 1000000000 + #endif ++#define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ + + /* + ** This is the maximum number of +@@ -13272,14 +14150,22 @@ struct fts5_api { + ** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. + ** * Terms in the VALUES clause of an INSERT statement + ** +-** The hard upper limit here is 32676. Most database people will ++** The hard upper limit here is 32767. Most database people will + ** tell you that in a well-normalized database, you usually should + ** not have more than a dozen or so columns in any table. And if + ** that is the case, there is no point in having more than a few + ** dozen values in any of the other situations described above. ++** ++** An index can only have SQLITE_MAX_COLUMN columns from the user ++** point of view, but the underlying b-tree that implements the index ++** might have up to twice as many columns in a WITHOUT ROWID table, ++** since must also store the primary key at the end. Hence the ++** column count for Index is u16 instead of i16. + */ +-#ifndef SQLITE_MAX_COLUMN ++#if !defined(SQLITE_MAX_COLUMN) + # define SQLITE_MAX_COLUMN 2000 ++#elif SQLITE_MAX_COLUMN>32767 ++# error SQLITE_MAX_COLUMN may not exceed 32767 + #endif + + /* +@@ -13297,11 +14183,7 @@ struct fts5_api { + ** The maximum depth of an expression tree. This is limited to + ** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might + ** want to place more severe limits on the complexity of an +-** expression. +-** +-** A value of 0 used to mean that the limit was not enforced. +-** But that is no longer true. The limit is now strictly enforced +-** at all times. ++** expression. A value of 0 means that there is no limit. + */ + #ifndef SQLITE_MAX_EXPR_DEPTH + # define SQLITE_MAX_EXPR_DEPTH 1000 +@@ -13313,7 +14195,7 @@ struct fts5_api { + ** level of recursion for each term. A stack overflow can result + ** if the number of terms is too large. In practice, most SQL + ** never has more than 3 or 4 terms. Use a value of 0 to disable +-** any limit on the number of terms in a compount SELECT. ++** any limit on the number of terms in a compound SELECT. + */ + #ifndef SQLITE_MAX_COMPOUND_SELECT + # define SQLITE_MAX_COMPOUND_SELECT 500 +@@ -13329,9 +14211,13 @@ struct fts5_api { + + /* + ** The maximum number of arguments to an SQL function. ++** ++** This value has a hard upper limit of 32767 due to storage ++** constraints (it needs to fit inside a i16). We keep it ++** lower than that to prevent abuse. + */ + #ifndef SQLITE_MAX_FUNCTION_ARG +-# define SQLITE_MAX_FUNCTION_ARG 127 ++# define SQLITE_MAX_FUNCTION_ARG 1000 + #endif + + /* +@@ -13428,7 +14314,7 @@ struct fts5_api { + ** max_page_count macro. + */ + #ifndef SQLITE_MAX_PAGE_COUNT +-# define SQLITE_MAX_PAGE_COUNT 1073741823 ++# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */ + #endif + + /* +@@ -13463,16 +14349,18 @@ struct fts5_api { + #endif + + /* +-** WAL mode depends on atomic aligned 32-bit loads and stores in a few +-** places. The following macros try to make this explicit. ++** A few places in the code require atomic load/store of aligned ++** integer values. + */ + #ifndef __has_extension + # define __has_extension(x) 0 /* compatibility with non-clang compilers */ + #endif + #if GCC_VERSION>=4007000 || __has_extension(c_atomic) ++# define SQLITE_ATOMIC_INTRINSICS 1 + # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) + # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) + #else ++# define SQLITE_ATOMIC_INTRINSICS 0 + # define AtomicLoad(PTR) (*(PTR)) + # define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) + #endif +@@ -13518,15 +14406,22 @@ struct fts5_api { + #endif + + /* +-** A macro to hint to the compiler that a function should not be ++** Macros to hint to the compiler that a function should or should not be + ** inlined. + */ + #if defined(__GNUC__) + # define SQLITE_NOINLINE __attribute__((noinline)) ++# define SQLITE_INLINE __attribute__((always_inline)) inline + #elif defined(_MSC_VER) && _MSC_VER>=1310 + # define SQLITE_NOINLINE __declspec(noinline) ++# define SQLITE_INLINE __forceinline + #else + # define SQLITE_NOINLINE ++# define SQLITE_INLINE ++#endif ++#if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__) ++# undef SQLITE_INLINE ++# define SQLITE_INLINE + #endif + + /* +@@ -13548,6 +14443,29 @@ struct fts5_api { + # endif + #endif + ++/* ++** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit ++** SEH support if the -DSQLITE_OMIT_SEH option is given. ++*/ ++#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) ++# define SQLITE_USE_SEH 1 ++#else ++# undef SQLITE_USE_SEH ++#endif ++ ++/* ++** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly ++** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0 ++*/ ++#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1 ++ /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */ ++# undef SQLITE_DIRECT_OVERFLOW_READ ++#else ++ /* In all other cases, enable */ ++# define SQLITE_DIRECT_OVERFLOW_READ 1 ++#endif ++ ++ + /* + ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. + ** 0 means mutexes are permanently disable and the library is never +@@ -13677,11 +14595,12 @@ struct fts5_api { + ** is significant and used at least once. On switch statements + ** where multiple cases go to the same block of code, testcase() + ** can insure that all cases are evaluated. +-** + */ +-#ifdef SQLITE_COVERAGE_TEST +-SQLITE_PRIVATE void sqlite3Coverage(int); +-# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); } ++#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) ++# ifndef SQLITE_AMALGAMATION ++ extern unsigned int sqlite3CoverageCounter; ++# endif ++# define testcase(X) if( X ){ sqlite3CoverageCounter += (unsigned)__LINE__; } + #else + # define testcase(X) + #endif +@@ -13711,6 +14630,14 @@ SQLITE_PRIVATE void sqlite3Coverage(int); + # define VVA_ONLY(X) + #endif + ++/* ++** Disable ALWAYS() and NEVER() (make them pass-throughs) for coverage ++** and mutation testing ++*/ ++#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) ++# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 ++#endif ++ + /* + ** The ALWAYS and NEVER macros surround boolean expressions which + ** are intended to always be true or false, respectively. Such +@@ -13726,7 +14653,7 @@ SQLITE_PRIVATE void sqlite3Coverage(int); + ** be true and false so that the unreachable code they specify will + ** not be counted as untested code. + */ +-#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) ++#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) + # define ALWAYS(X) (1) + # define NEVER(X) (0) + #elif !defined(NDEBUG) +@@ -13737,26 +14664,6 @@ SQLITE_PRIVATE void sqlite3Coverage(int); + # define NEVER(X) (X) + #endif + +-/* +-** The harmless(X) macro indicates that expression X is usually false +-** but can be true without causing any problems, but we don't know of +-** any way to cause X to be true. +-** +-** In debugging and testing builds, this macro will abort if X is ever +-** true. In this way, developers are alerted to a possible test case +-** that causes X to be true. If a harmless macro ever fails, that is +-** an opportunity to change the macro into a testcase() and add a new +-** test case to the test suite. +-** +-** For normal production builds, harmless(X) is a no-op, since it does +-** not matter whether expression X is true or false. +-*/ +-#ifdef SQLITE_DEBUG +-# define harmless(X) assert(!(X)); +-#else +-# define harmless(X) +-#endif +- + /* + ** Some conditionals are optimizations only. In other words, if the + ** conditionals are replaced with a constant 1 (true) or 0 (false) then +@@ -13820,6 +14727,15 @@ SQLITE_PRIVATE void sqlite3Coverage(int); + # undef SQLITE_ENABLE_EXPLAIN_COMMENTS + #endif + ++/* ++** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE ++*/ ++#if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) ++# define SQLITE_OMIT_ALTERTABLE ++#endif ++ ++#define SQLITE_DIGIT_SEPARATOR '_' ++ + /* + ** Return true (non-zero) if the input is an integer that is too large + ** to fit in 32-bits. This macro is used inside of various testcase() +@@ -13901,6 +14817,7 @@ struct HashElem { + HashElem *next, *prev; /* Next and previous elements in the table */ + void *data; /* Data associated with this element */ + const char *pKey; /* Key associated with this element */ ++ unsigned int h; /* hash for pKey */ + }; + + /* +@@ -13932,7 +14849,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + /* + ** Number of entries in a hash table + */ +-/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ ++#define sqliteHashCount(H) ((H)->count) + + #endif /* SQLITE_HASH_H */ + +@@ -13964,8 +14881,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + #define TK_LP 22 + #define TK_RP 23 + #define TK_AS 24 +-#define TK_WITHOUT 25 +-#define TK_COMMA 26 ++#define TK_COMMA 25 ++#define TK_WITHOUT 26 + #define TK_ABORT 27 + #define TK_ACTION 28 + #define TK_AFTER 29 +@@ -13985,141 +14902,147 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + #define TK_OR 43 + #define TK_AND 44 + #define TK_IS 45 +-#define TK_MATCH 46 +-#define TK_LIKE_KW 47 +-#define TK_BETWEEN 48 +-#define TK_IN 49 +-#define TK_ISNULL 50 +-#define TK_NOTNULL 51 +-#define TK_NE 52 +-#define TK_EQ 53 +-#define TK_GT 54 +-#define TK_LE 55 +-#define TK_LT 56 +-#define TK_GE 57 +-#define TK_ESCAPE 58 +-#define TK_ID 59 +-#define TK_COLUMNKW 60 +-#define TK_DO 61 +-#define TK_FOR 62 +-#define TK_IGNORE 63 +-#define TK_INITIALLY 64 +-#define TK_INSTEAD 65 +-#define TK_NO 66 +-#define TK_KEY 67 +-#define TK_OF 68 +-#define TK_OFFSET 69 +-#define TK_PRAGMA 70 +-#define TK_RAISE 71 +-#define TK_RECURSIVE 72 +-#define TK_REPLACE 73 +-#define TK_RESTRICT 74 +-#define TK_ROW 75 +-#define TK_ROWS 76 +-#define TK_TRIGGER 77 +-#define TK_VACUUM 78 +-#define TK_VIEW 79 +-#define TK_VIRTUAL 80 +-#define TK_WITH 81 +-#define TK_NULLS 82 +-#define TK_FIRST 83 +-#define TK_LAST 84 +-#define TK_CURRENT 85 +-#define TK_FOLLOWING 86 +-#define TK_PARTITION 87 +-#define TK_PRECEDING 88 +-#define TK_RANGE 89 +-#define TK_UNBOUNDED 90 +-#define TK_EXCLUDE 91 +-#define TK_GROUPS 92 +-#define TK_OTHERS 93 +-#define TK_TIES 94 +-#define TK_GENERATED 95 +-#define TK_ALWAYS 96 +-#define TK_REINDEX 97 +-#define TK_RENAME 98 +-#define TK_CTIME_KW 99 +-#define TK_ANY 100 +-#define TK_BITAND 101 +-#define TK_BITOR 102 +-#define TK_LSHIFT 103 +-#define TK_RSHIFT 104 +-#define TK_PLUS 105 +-#define TK_MINUS 106 +-#define TK_STAR 107 +-#define TK_SLASH 108 +-#define TK_REM 109 +-#define TK_CONCAT 110 +-#define TK_COLLATE 111 +-#define TK_BITNOT 112 +-#define TK_ON 113 +-#define TK_INDEXED 114 +-#define TK_STRING 115 +-#define TK_JOIN_KW 116 +-#define TK_CONSTRAINT 117 +-#define TK_DEFAULT 118 +-#define TK_NULL 119 +-#define TK_PRIMARY 120 +-#define TK_UNIQUE 121 +-#define TK_CHECK 122 +-#define TK_REFERENCES 123 +-#define TK_AUTOINCR 124 +-#define TK_INSERT 125 +-#define TK_DELETE 126 +-#define TK_UPDATE 127 +-#define TK_SET 128 +-#define TK_DEFERRABLE 129 +-#define TK_FOREIGN 130 +-#define TK_DROP 131 +-#define TK_UNION 132 +-#define TK_ALL 133 +-#define TK_EXCEPT 134 +-#define TK_INTERSECT 135 +-#define TK_SELECT 136 +-#define TK_VALUES 137 +-#define TK_DISTINCT 138 +-#define TK_DOT 139 +-#define TK_FROM 140 +-#define TK_JOIN 141 +-#define TK_USING 142 +-#define TK_ORDER 143 +-#define TK_GROUP 144 +-#define TK_HAVING 145 +-#define TK_LIMIT 146 +-#define TK_WHERE 147 +-#define TK_INTO 148 +-#define TK_NOTHING 149 +-#define TK_FLOAT 150 +-#define TK_BLOB 151 +-#define TK_INTEGER 152 +-#define TK_VARIABLE 153 +-#define TK_CASE 154 +-#define TK_WHEN 155 +-#define TK_THEN 156 +-#define TK_ELSE 157 +-#define TK_INDEX 158 +-#define TK_ALTER 159 +-#define TK_ADD 160 +-#define TK_WINDOW 161 +-#define TK_OVER 162 +-#define TK_FILTER 163 +-#define TK_COLUMN 164 +-#define TK_AGG_FUNCTION 165 +-#define TK_AGG_COLUMN 166 +-#define TK_TRUEFALSE 167 +-#define TK_ISNOT 168 +-#define TK_FUNCTION 169 +-#define TK_UMINUS 170 +-#define TK_UPLUS 171 +-#define TK_TRUTH 172 +-#define TK_REGISTER 173 +-#define TK_VECTOR 174 +-#define TK_SELECT_COLUMN 175 +-#define TK_IF_NULL_ROW 176 +-#define TK_ASTERISK 177 +-#define TK_SPAN 178 +-#define TK_SPACE 179 +-#define TK_ILLEGAL 180 ++#define TK_ISNOT 46 ++#define TK_MATCH 47 ++#define TK_LIKE_KW 48 ++#define TK_BETWEEN 49 ++#define TK_IN 50 ++#define TK_ISNULL 51 ++#define TK_NOTNULL 52 ++#define TK_NE 53 ++#define TK_EQ 54 ++#define TK_GT 55 ++#define TK_LE 56 ++#define TK_LT 57 ++#define TK_GE 58 ++#define TK_ESCAPE 59 ++#define TK_ID 60 ++#define TK_COLUMNKW 61 ++#define TK_DO 62 ++#define TK_FOR 63 ++#define TK_IGNORE 64 ++#define TK_INITIALLY 65 ++#define TK_INSTEAD 66 ++#define TK_NO 67 ++#define TK_KEY 68 ++#define TK_OF 69 ++#define TK_OFFSET 70 ++#define TK_PRAGMA 71 ++#define TK_RAISE 72 ++#define TK_RECURSIVE 73 ++#define TK_REPLACE 74 ++#define TK_RESTRICT 75 ++#define TK_ROW 76 ++#define TK_ROWS 77 ++#define TK_TRIGGER 78 ++#define TK_VACUUM 79 ++#define TK_VIEW 80 ++#define TK_VIRTUAL 81 ++#define TK_WITH 82 ++#define TK_NULLS 83 ++#define TK_FIRST 84 ++#define TK_LAST 85 ++#define TK_CURRENT 86 ++#define TK_FOLLOWING 87 ++#define TK_PARTITION 88 ++#define TK_PRECEDING 89 ++#define TK_RANGE 90 ++#define TK_UNBOUNDED 91 ++#define TK_EXCLUDE 92 ++#define TK_GROUPS 93 ++#define TK_OTHERS 94 ++#define TK_TIES 95 ++#define TK_GENERATED 96 ++#define TK_ALWAYS 97 ++#define TK_MATERIALIZED 98 ++#define TK_REINDEX 99 ++#define TK_RENAME 100 ++#define TK_CTIME_KW 101 ++#define TK_ANY 102 ++#define TK_BITAND 103 ++#define TK_BITOR 104 ++#define TK_LSHIFT 105 ++#define TK_RSHIFT 106 ++#define TK_PLUS 107 ++#define TK_MINUS 108 ++#define TK_STAR 109 ++#define TK_SLASH 110 ++#define TK_REM 111 ++#define TK_CONCAT 112 ++#define TK_PTR 113 ++#define TK_COLLATE 114 ++#define TK_BITNOT 115 ++#define TK_ON 116 ++#define TK_INDEXED 117 ++#define TK_STRING 118 ++#define TK_JOIN_KW 119 ++#define TK_CONSTRAINT 120 ++#define TK_DEFAULT 121 ++#define TK_NULL 122 ++#define TK_PRIMARY 123 ++#define TK_UNIQUE 124 ++#define TK_CHECK 125 ++#define TK_REFERENCES 126 ++#define TK_AUTOINCR 127 ++#define TK_INSERT 128 ++#define TK_DELETE 129 ++#define TK_UPDATE 130 ++#define TK_SET 131 ++#define TK_DEFERRABLE 132 ++#define TK_FOREIGN 133 ++#define TK_DROP 134 ++#define TK_UNION 135 ++#define TK_ALL 136 ++#define TK_EXCEPT 137 ++#define TK_INTERSECT 138 ++#define TK_SELECT 139 ++#define TK_VALUES 140 ++#define TK_DISTINCT 141 ++#define TK_DOT 142 ++#define TK_FROM 143 ++#define TK_JOIN 144 ++#define TK_USING 145 ++#define TK_ORDER 146 ++#define TK_GROUP 147 ++#define TK_HAVING 148 ++#define TK_LIMIT 149 ++#define TK_WHERE 150 ++#define TK_RETURNING 151 ++#define TK_INTO 152 ++#define TK_NOTHING 153 ++#define TK_FLOAT 154 ++#define TK_BLOB 155 ++#define TK_INTEGER 156 ++#define TK_VARIABLE 157 ++#define TK_CASE 158 ++#define TK_WHEN 159 ++#define TK_THEN 160 ++#define TK_ELSE 161 ++#define TK_INDEX 162 ++#define TK_ALTER 163 ++#define TK_ADD 164 ++#define TK_WINDOW 165 ++#define TK_OVER 166 ++#define TK_FILTER 167 ++#define TK_COLUMN 168 ++#define TK_AGG_FUNCTION 169 ++#define TK_AGG_COLUMN 170 ++#define TK_TRUEFALSE 171 ++#define TK_FUNCTION 172 ++#define TK_UPLUS 173 ++#define TK_UMINUS 174 ++#define TK_TRUTH 175 ++#define TK_REGISTER 176 ++#define TK_VECTOR 177 ++#define TK_SELECT_COLUMN 178 ++#define TK_IF_NULL_ROW 179 ++#define TK_ASTERISK 180 ++#define TK_SPAN 181 ++#define TK_ERROR 182 ++#define TK_QNUMBER 183 ++#define TK_SPACE 184 ++#define TK_COMMENT 185 ++#define TK_ILLEGAL 186 + + /************** End of parse.h ***********************************************/ + /************** Continuing where we left off in sqliteInt.h ******************/ +@@ -14128,6 +15051,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + #include + #include + #include ++#include + + /* + ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. +@@ -14148,7 +15072,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + #ifdef SQLITE_OMIT_FLOATING_POINT + # define double sqlite_int64 + # define float sqlite_int64 +-# define LONGDOUBLE_TYPE sqlite_int64 ++# define fabs(X) ((X)<0?-(X):(X)) ++# define sqlite3IsOverflow(X) 0 + # ifndef SQLITE_BIG_DBL + # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) + # endif +@@ -14225,7 +15150,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + ** number of pages. A negative number N translations means that a buffer + ** of -1024*N bytes is allocated and used for as many pages as it will hold. + ** +-** The default value of "20" was choosen to minimize the run-time of the ++** The default value of "20" was chosen to minimize the run-time of the + ** speedtest1 test program with options: --shrink-memory --reprepare + */ + #ifndef SQLITE_DEFAULT_PCACHE_INITSZ +@@ -14253,7 +15178,17 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + ** ourselves. + */ + #ifndef offsetof +-#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) ++#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) ++#endif ++ ++/* ++** Work around C99 "flex-array" syntax for pre-C99 compilers, so as ++** to avoid complaints from -fsanitize=strict-bounds. ++*/ ++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ++# define FLEXARRAY ++#else ++# define FLEXARRAY 1 + #endif + + /* +@@ -14323,9 +15258,6 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); + # define INT8_TYPE signed char + # endif + #endif +-#ifndef LONGDOUBLE_TYPE +-# define LONGDOUBLE_TYPE long double +-#endif + typedef sqlite_int64 i64; /* 8-byte signed integer */ + typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ + typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ +@@ -14334,6 +15266,11 @@ typedef INT16_TYPE i16; /* 2-byte signed integer */ + typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ + typedef INT8_TYPE i8; /* 1-byte signed integer */ + ++/* A bitfield type for use inside of structures. Always follow with :N where ++** N is the number of bits. ++*/ ++typedef unsigned bft; /* Bit Field Type */ ++ + /* + ** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value + ** that can be stored in a u32 without loss of data. The value +@@ -14344,15 +15281,9 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ + + /* + ** The datatype used to store estimates of the number of rows in a +-** table or index. This is an unsigned integer type. For 99.9% of +-** the world, a 32-bit integer is sufficient. But a 64-bit integer +-** can be used at compile-time if desired. ++** table or index. + */ +-#ifdef SQLITE_64BIT_STATS +- typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ +-#else +- typedef u32 tRowcnt; /* 32-bit is the default */ +-#endif ++typedef u64 tRowcnt; + + /* + ** Estimated quantities used for query planning are stored as 16-bit +@@ -14378,6 +15309,8 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ + ** 0.5 -> -10 0.1 -> -33 0.0625 -> -40 + */ + typedef INT16_TYPE LogEst; ++#define LOGEST_MIN (-32768) ++#define LOGEST_MAX (32767) + + /* + ** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer +@@ -14387,6 +15320,7 @@ typedef INT16_TYPE LogEst; + # define SQLITE_PTRSIZE __SIZEOF_POINTER__ + # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ ++ (defined(__APPLE__) && defined(__ppc__)) || \ + (defined(__TOS_AIX__) && !defined(__64BIT__)) + # define SQLITE_PTRSIZE 4 + # else +@@ -14412,8 +15346,31 @@ typedef INT16_TYPE LogEst; + ** the end of buffer S. This macro returns true if P points to something + ** contained within the buffer S. + */ +-#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) ++#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) + ++/* ++** P is one byte past the end of a large buffer. Return true if a span of bytes ++** between S..E crosses the end of that buffer. In other words, return true ++** if the sub-buffer S..E-1 overflows the buffer whose last byte is P-1. ++** ++** S is the start of the span. E is one byte past the end of end of span. ++** ++** P ++** |-----------------| FALSE ++** |-------| ++** S E ++** ++** P ++** |-----------------| ++** |-------| TRUE ++** S E ++** ++** P ++** |-----------------| ++** |-------| FALSE ++** S E ++*/ ++#define SQLITE_OVERFLOW(P,S,E) (((uptr)(S)<(uptr)(P))&&((uptr)(E)>(uptr)(P))) + + /* + ** Macros to determine whether the machine is big or little endian, +@@ -14423,16 +15380,33 @@ typedef INT16_TYPE LogEst; + ** using C-preprocessor macros. If that is unsuccessful, or if + ** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined + ** at run-time. ++** ++** If you are building SQLite on some obscure platform for which the ++** following ifdef magic does not work, you can always include either: ++** ++** -DSQLITE_BYTEORDER=1234 ++** ++** or ++** ++** -DSQLITE_BYTEORDER=4321 ++** ++** to cause the build to work for little-endian or big-endian processors, ++** respectively. + */ +-#ifndef SQLITE_BYTEORDER +-# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ ++#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */ ++# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ ++# define SQLITE_BYTEORDER 4321 ++# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ ++# define SQLITE_BYTEORDER 1234 ++# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 ++# define SQLITE_BYTEORDER 4321 ++# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ + defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) +-# define SQLITE_BYTEORDER 1234 +-# elif defined(sparc) || defined(__ppc__) || \ +- defined(__ARMEB__) || defined(__AARCH64EB__) +-# define SQLITE_BYTEORDER 4321 ++# define SQLITE_BYTEORDER 1234 ++# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) ++# define SQLITE_BYTEORDER 4321 + # else + # define SQLITE_BYTEORDER 0 + # endif +@@ -14465,11 +15439,30 @@ typedef INT16_TYPE LogEst; + #define LARGEST_UINT64 (0xffffffff|(((u64)0xffffffff)<<32)) + #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) + ++/* ++** Macro SMXV(n) return the maximum value that can be held in variable n, ++** assuming n is a signed integer type. UMXV(n) is similar for unsigned ++** integer types. ++*/ ++#define SMXV(n) ((((i64)1)<<(sizeof(n)*8-1))-1) ++#define UMXV(n) ((((i64)1)<<(sizeof(n)*8))-1) ++ + /* + ** Round up a number to the next larger multiple of 8. This is used + ** to force 8-byte alignment on 64-bit architectures. ++** ++** ROUND8() always does the rounding, for any argument. ++** ++** ROUND8P() assumes that the argument is already an integer number of ++** pointers in size, and so it is a no-op on systems where the pointer ++** size is 8. + */ + #define ROUND8(x) (((x)+7)&~7) ++#if SQLITE_PTRSIZE==8 ++# define ROUND8P(x) (x) ++#else ++# define ROUND8P(x) (((x)+7)&~7) ++#endif + + /* + ** Round down to the nearest multiple of 8 +@@ -14486,9 +15479,9 @@ typedef INT16_TYPE LogEst; + ** pointers. In that case, only verify 4-byte alignment. + */ + #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC +-# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) ++# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) + #else +-# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) ++# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) + #endif + + /* +@@ -14532,25 +15525,94 @@ typedef INT16_TYPE LogEst; + #endif + + /* +-** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not +-** the Select query generator tracing logic is turned on. ++** TREETRACE_ENABLED will be either 1 or 0 depending on whether or not ++** the Abstract Syntax Tree tracing logic is turned on. + */ +-#if defined(SQLITE_ENABLE_SELECTTRACE) +-# define SELECTTRACE_ENABLED 1 +-#else +-# define SELECTTRACE_ENABLED 0 ++#if !defined(SQLITE_AMALGAMATION) ++SQLITE_PRIVATE u32 sqlite3TreeTrace; + #endif +-#if defined(SQLITE_ENABLE_SELECTTRACE) +-# define SELECTTRACE_ENABLED 1 +-# define SELECTTRACE(K,P,S,X) \ +- if(sqlite3_unsupported_selecttrace&(K)) \ ++#if defined(SQLITE_DEBUG) \ ++ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \ ++ || defined(SQLITE_ENABLE_TREETRACE)) ++# define TREETRACE_ENABLED 1 ++# define TREETRACE(K,P,S,X) \ ++ if(sqlite3TreeTrace&(K)) \ + sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ + sqlite3DebugPrintf X + #else +-# define SELECTTRACE(K,P,S,X) +-# define SELECTTRACE_ENABLED 0 ++# define TREETRACE(K,P,S,X) ++# define TREETRACE_ENABLED 0 ++#endif ++ ++/* TREETRACE flag meanings: ++** ++** 0x00000001 Beginning and end of SELECT processing ++** 0x00000002 WHERE clause processing ++** 0x00000004 Query flattener ++** 0x00000008 Result-set wildcard expansion ++** 0x00000010 Query name resolution ++** 0x00000020 Aggregate analysis ++** 0x00000040 Window functions ++** 0x00000080 Generated column names ++** 0x00000100 Move HAVING terms into WHERE ++** 0x00000200 Count-of-view optimization ++** 0x00000400 Compound SELECT processing ++** 0x00000800 Drop superfluous ORDER BY ++** 0x00001000 LEFT JOIN simplifies to JOIN ++** 0x00002000 Constant propagation ++** 0x00004000 Push-down optimization ++** 0x00008000 After all FROM-clause analysis ++** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing ++** 0x00020000 Transform DISTINCT into GROUP BY ++** 0x00040000 SELECT tree dump after all code has been generated ++** 0x00080000 NOT NULL strength reduction ++*/ ++ ++/* ++** Macros for "wheretrace" ++*/ ++SQLITE_PRIVATE u32 sqlite3WhereTrace; ++#if defined(SQLITE_DEBUG) \ ++ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) ++# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X ++# define WHERETRACE_ENABLED 1 ++#else ++# define WHERETRACE(K,X) + #endif + ++/* ++** Bits for the sqlite3WhereTrace mask: ++** ++** (---any--) Top-level block structure ++** 0x-------F High-level debug messages ++** 0x----FFF- More detail ++** 0xFFFF---- Low-level debug messages ++** ++** 0x00000001 Code generation ++** 0x00000002 Solver (Use 0x40000 for less detail) ++** 0x00000004 Solver costs ++** 0x00000008 WhereLoop inserts ++** ++** 0x00000010 Display sqlite3_index_info xBestIndex calls ++** 0x00000020 Range an equality scan metrics ++** 0x00000040 IN operator decisions ++** 0x00000080 WhereLoop cost adjustments ++** 0x00000100 ++** 0x00000200 Covering index decisions ++** 0x00000400 OR optimization ++** 0x00000800 Index scanner ++** 0x00001000 More details associated with code generation ++** 0x00002000 ++** 0x00004000 Show all WHERE terms at key points ++** 0x00008000 Show the full SELECT statement at key places ++** ++** 0x00010000 Show more detail when printing WHERE terms ++** 0x00020000 Show WHERE terms returned from whereScanNext() ++** 0x00040000 Solver overview messages ++** 0x00080000 Star-query heuristic ++*/ ++ ++ + /* + ** An instance of the following structure is used to store the busy-handler + ** callback for a given sqlite handle. +@@ -14569,11 +15631,25 @@ struct BusyHandler { + + /* + ** Name of table that holds the database schema. ++** ++** The PREFERRED names are used wherever possible. But LEGACY is also ++** used for backwards compatibility. ++** ++** 1. Queries can use either the PREFERRED or the LEGACY names ++** 2. The sqlite3_set_authorizer() callback uses the LEGACY name ++** 3. The PRAGMA table_list statement uses the PREFERRED name ++** ++** The LEGACY names are stored in the internal symbol hash table ++** in support of (2). Names are translated using sqlite3PreferredTableName() ++** for (3). The sqlite3FindTable() function takes care of translating ++** names for (1). ++** ++** Note that "sqlite_temp_schema" can also be called "temp.sqlite_schema". + */ +-#define DFLT_SCHEMA_TABLE "sqlite_master" +-#define DFLT_TEMP_SCHEMA_TABLE "sqlite_temp_master" +-#define ALT_SCHEMA_TABLE "sqlite_schema" +-#define ALT_TEMP_SCHEMA_TABLE "sqlite_temp_schema" ++#define LEGACY_SCHEMA_TABLE "sqlite_master" ++#define LEGACY_TEMP_SCHEMA_TABLE "sqlite_temp_master" ++#define PREFERRED_SCHEMA_TABLE "sqlite_schema" ++#define PREFERRED_TEMP_SCHEMA_TABLE "sqlite_temp_schema" + + + /* +@@ -14585,7 +15661,7 @@ struct BusyHandler { + ** The name of the schema table. The name is different for TEMP. + */ + #define SCHEMA_TABLE(x) \ +- ((!OMIT_TEMPDB)&&(x==1)?DFLT_TEMP_SCHEMA_TABLE:DFLT_SCHEMA_TABLE) ++ ((!OMIT_TEMPDB)&&(x==1)?LEGACY_TEMP_SCHEMA_TABLE:LEGACY_SCHEMA_TABLE) + + /* + ** A convenience macro that returns the number of elements in +@@ -14606,7 +15682,7 @@ struct BusyHandler { + ** pointer will work here as long as it is distinct from SQLITE_STATIC + ** and SQLITE_TRANSIENT. + */ +-#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomFault) ++#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomClear) + + /* + ** When SQLITE_OMIT_WSD is defined, it means that the target platform does +@@ -14662,16 +15738,22 @@ typedef struct AutoincInfo AutoincInfo; + typedef struct Bitvec Bitvec; + typedef struct CollSeq CollSeq; + typedef struct Column Column; ++typedef struct Cte Cte; ++typedef struct CteUse CteUse; + typedef struct Db Db; ++typedef struct DbClientData DbClientData; ++typedef struct DbFixer DbFixer; + typedef struct Schema Schema; + typedef struct Expr Expr; + typedef struct ExprList ExprList; + typedef struct FKey FKey; ++typedef struct FpDecode FpDecode; + typedef struct FuncDestructor FuncDestructor; + typedef struct FuncDef FuncDef; + typedef struct FuncDefHash FuncDefHash; + typedef struct IdList IdList; + typedef struct Index Index; ++typedef struct IndexedExpr IndexedExpr; + typedef struct IndexSample IndexSample; + typedef struct KeyClass KeyClass; + typedef struct KeyInfo KeyInfo; +@@ -14679,15 +15761,21 @@ typedef struct Lookaside Lookaside; + typedef struct LookasideSlot LookasideSlot; + typedef struct Module Module; + typedef struct NameContext NameContext; ++typedef struct OnOrUsing OnOrUsing; + typedef struct Parse Parse; ++typedef struct ParseCleanup ParseCleanup; + typedef struct PreUpdate PreUpdate; + typedef struct PrintfArguments PrintfArguments; ++typedef struct RCStr RCStr; + typedef struct RenameToken RenameToken; ++typedef struct Returning Returning; + typedef struct RowSet RowSet; + typedef struct Savepoint Savepoint; + typedef struct Select Select; + typedef struct SQLiteThread SQLiteThread; + typedef struct SelectDest SelectDest; ++typedef struct Subquery Subquery; ++typedef struct SrcItem SrcItem; + typedef struct SrcList SrcList; + typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */ + typedef struct Table Table; +@@ -14728,10 +15816,12 @@ typedef struct With With; + /* + ** A bit in a Bitmask + */ +-#define MASKBIT(n) (((Bitmask)1)<<(n)) +-#define MASKBIT64(n) (((u64)1)<<(n)) +-#define MASKBIT32(n) (((unsigned int)1)<<(n)) +-#define ALLBITS ((Bitmask)-1) ++#define MASKBIT(n) (((Bitmask)1)<<(n)) ++#define MASKBIT64(n) (((u64)1)<<(n)) ++#define MASKBIT32(n) (((unsigned int)1)<<(n)) ++#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0) ++#define ALLBITS ((Bitmask)-1) ++#define TOPBIT (((Bitmask)1)<<(BMS-1)) + + /* A VList object records a mapping between parameters/variables/wildcards + ** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer +@@ -14746,6 +15836,331 @@ typedef int VList; + ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque + ** pointer types (i.e. FuncDef) defined above. + */ ++/************** Include os.h in the middle of sqliteInt.h ********************/ ++/************** Begin file os.h **********************************************/ ++/* ++** 2001 September 16 ++** ++** 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 header file (together with is companion C source-code file ++** "os.c") attempt to abstract the underlying operating system so that ++** the SQLite library will work on both POSIX and windows systems. ++** ++** This header file is #include-ed by sqliteInt.h and thus ends up ++** being included by every source file. ++*/ ++#ifndef _SQLITE_OS_H_ ++#define _SQLITE_OS_H_ ++ ++/* ++** Attempt to automatically detect the operating system and setup the ++** necessary pre-processor macros for it. ++*/ ++/************** Include os_setup.h in the middle of os.h *********************/ ++/************** Begin file os_setup.h ****************************************/ ++/* ++** 2013 November 25 ++** ++** 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 contains pre-processor directives related to operating system ++** detection and/or setup. ++*/ ++#ifndef SQLITE_OS_SETUP_H ++#define SQLITE_OS_SETUP_H ++ ++/* ++** Figure out if we are dealing with Unix, Windows, or some other operating ++** system. ++** ++** After the following block of preprocess macros, all of ++** ++** SQLITE_OS_KV ++** SQLITE_OS_OTHER ++** SQLITE_OS_UNIX ++** SQLITE_OS_WIN ++** ++** will defined to either 1 or 0. One of them will be 1. The others will be 0. ++** If none of the macros are initially defined, then select either ++** SQLITE_OS_UNIX or SQLITE_OS_WIN depending on the target platform. ++** ++** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application ++** must provide its own VFS implementation together with sqlite3_os_init() ++** and sqlite3_os_end() routines. ++*/ ++#if !defined(SQLITE_OS_KV) && !defined(SQLITE_OS_OTHER) && \ ++ !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_WIN) ++# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \ ++ defined(__MINGW32__) || defined(__BORLANDC__) ++# define SQLITE_OS_WIN 1 ++# define SQLITE_OS_UNIX 0 ++# else ++# define SQLITE_OS_WIN 0 ++# define SQLITE_OS_UNIX 1 ++# endif ++#endif ++#if SQLITE_OS_OTHER+1>1 ++# undef SQLITE_OS_KV ++# define SQLITE_OS_KV 0 ++# undef SQLITE_OS_UNIX ++# define SQLITE_OS_UNIX 0 ++# undef SQLITE_OS_WIN ++# define SQLITE_OS_WIN 0 ++#endif ++#if SQLITE_OS_KV+1>1 ++# undef SQLITE_OS_OTHER ++# define SQLITE_OS_OTHER 0 ++# undef SQLITE_OS_UNIX ++# define SQLITE_OS_UNIX 0 ++# undef SQLITE_OS_WIN ++# define SQLITE_OS_WIN 0 ++# define SQLITE_OMIT_LOAD_EXTENSION 1 ++# define SQLITE_OMIT_WAL 1 ++# define SQLITE_OMIT_DEPRECATED 1 ++# undef SQLITE_TEMP_STORE ++# define SQLITE_TEMP_STORE 3 /* Always use memory for temporary storage */ ++# define SQLITE_DQS 0 ++# define SQLITE_OMIT_SHARED_CACHE 1 ++# define SQLITE_OMIT_AUTOINIT 1 ++#endif ++#if SQLITE_OS_UNIX+1>1 ++# undef SQLITE_OS_KV ++# define SQLITE_OS_KV 0 ++# undef SQLITE_OS_OTHER ++# define SQLITE_OS_OTHER 0 ++# undef SQLITE_OS_WIN ++# define SQLITE_OS_WIN 0 ++#endif ++#if SQLITE_OS_WIN+1>1 ++# undef SQLITE_OS_KV ++# define SQLITE_OS_KV 0 ++# undef SQLITE_OS_OTHER ++# define SQLITE_OS_OTHER 0 ++# undef SQLITE_OS_UNIX ++# define SQLITE_OS_UNIX 0 ++#endif ++ ++ ++#endif /* SQLITE_OS_SETUP_H */ ++ ++/************** End of os_setup.h ********************************************/ ++/************** Continuing where we left off in os.h *************************/ ++ ++/* If the SET_FULLSYNC macro is not defined above, then make it ++** a no-op ++*/ ++#ifndef SET_FULLSYNC ++# define SET_FULLSYNC(x,y) ++#endif ++ ++/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h ++*/ ++#ifndef SQLITE_MAX_PATHLEN ++# define SQLITE_MAX_PATHLEN FILENAME_MAX ++#endif ++ ++/* Maximum number of symlinks that will be resolved while trying to ++** expand a filename in xFullPathname() in the VFS. ++*/ ++#ifndef SQLITE_MAX_SYMLINK ++# define SQLITE_MAX_SYMLINK 200 ++#endif ++ ++/* ++** The default size of a disk sector ++*/ ++#ifndef SQLITE_DEFAULT_SECTOR_SIZE ++# define SQLITE_DEFAULT_SECTOR_SIZE 4096 ++#endif ++ ++/* ++** Temporary files are named starting with this prefix followed by 16 random ++** alphanumeric characters, and no file extension. They are stored in the ++** OS's standard temporary file directory, and are deleted prior to exit. ++** If sqlite is being embedded in another program, you may wish to change the ++** prefix to reflect your program's name, so that if your program exits ++** prematurely, old temporary files can be easily identified. This can be done ++** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. ++** ++** 2006-10-31: The default prefix used to be "sqlite_". But then ++** Mcafee started using SQLite in their anti-virus product and it ++** started putting files with the "sqlite" name in the c:/temp folder. ++** This annoyed many windows users. Those users would then do a ++** Google search for "sqlite", find the telephone numbers of the ++** developers and call to wake them up at night and complain. ++** For this reason, the default name prefix is changed to be "sqlite" ++** spelled backwards. So the temp files are still identified, but ++** anybody smart enough to figure out the code is also likely smart ++** enough to know that calling the developer will not help get rid ++** of the file. ++*/ ++#ifndef SQLITE_TEMP_FILE_PREFIX ++# define SQLITE_TEMP_FILE_PREFIX "etilqs_" ++#endif ++ ++/* ++** The following values may be passed as the second argument to ++** sqlite3OsLock(). The various locks exhibit the following semantics: ++** ++** SHARED: Any number of processes may hold a SHARED lock simultaneously. ++** RESERVED: A single process may hold a RESERVED lock on a file at ++** any time. Other processes may hold and obtain new SHARED locks. ++** PENDING: A single process may hold a PENDING lock on a file at ++** any one time. Existing SHARED locks may persist, but no new ++** SHARED locks may be obtained by other processes. ++** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. ++** ++** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a ++** process that requests an EXCLUSIVE lock may actually obtain a PENDING ++** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to ++** sqlite3OsLock(). ++*/ ++#define NO_LOCK 0 ++#define SHARED_LOCK 1 ++#define RESERVED_LOCK 2 ++#define PENDING_LOCK 3 ++#define EXCLUSIVE_LOCK 4 ++ ++/* ++** File Locking Notes: (Mostly about windows but also some info for Unix) ++** ++** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because ++** those functions are not available. So we use only LockFile() and ++** UnlockFile(). ++** ++** LockFile() prevents not just writing but also reading by other processes. ++** A SHARED_LOCK is obtained by locking a single randomly-chosen ++** byte out of a specific range of bytes. The lock byte is obtained at ++** random so two separate readers can probably access the file at the ++** same time, unless they are unlucky and choose the same lock byte. ++** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. ++** There can only be one writer. A RESERVED_LOCK is obtained by locking ++** a single byte of the file that is designated as the reserved lock byte. ++** A PENDING_LOCK is obtained by locking a designated byte different from ++** the RESERVED_LOCK byte. ++** ++** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, ++** which means we can use reader/writer locks. When reader/writer locks ++** are used, the lock is placed on the same range of bytes that is used ++** for probabilistic locking in Win95/98/ME. Hence, the locking scheme ++** will support two or more Win95 readers or two or more WinNT readers. ++** But a single Win95 reader will lock out all WinNT readers and a single ++** WinNT reader will lock out all other Win95 readers. ++** ++** The following #defines specify the range of bytes used for locking. ++** SHARED_SIZE is the number of bytes available in the pool from which ++** a random byte is selected for a shared lock. The pool of bytes for ++** shared locks begins at SHARED_FIRST. ++** ++** The same locking strategy and ++** byte ranges are used for Unix. This leaves open the possibility of having ++** clients on win95, winNT, and unix all talking to the same shared file ++** and all locking correctly. To do so would require that samba (or whatever ++** tool is being used for file sharing) implements locks correctly between ++** windows and unix. I'm guessing that isn't likely to happen, but by ++** using the same locking range we are at least open to the possibility. ++** ++** Locking in windows is manditory. For this reason, we cannot store ++** actual data in the bytes used for locking. The pager never allocates ++** the pages involved in locking therefore. SHARED_SIZE is selected so ++** that all locks will fit on a single page even at the minimum page size. ++** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE ++** is set high so that we don't have to allocate an unused page except ++** for very large databases. But one should test the page skipping logic ++** by setting PENDING_BYTE low and running the entire regression suite. ++** ++** Changing the value of PENDING_BYTE results in a subtly incompatible ++** file format. Depending on how it is changed, you might not notice ++** the incompatibility right away, even running a full regression test. ++** The default location of PENDING_BYTE is the first byte past the ++** 1GB boundary. ++** ++*/ ++#ifdef SQLITE_OMIT_WSD ++# define PENDING_BYTE (0x40000000) ++#else ++# define PENDING_BYTE sqlite3PendingByte ++#endif ++#define RESERVED_BYTE (PENDING_BYTE+1) ++#define SHARED_FIRST (PENDING_BYTE+2) ++#define SHARED_SIZE 510 ++ ++/* ++** Wrapper around OS specific sqlite3_os_init() function. ++*/ ++SQLITE_PRIVATE int sqlite3OsInit(void); ++ ++/* ++** Functions for accessing sqlite3_file methods ++*/ ++SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*); ++SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); ++SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); ++SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); ++SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); ++SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); ++SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); ++SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); ++SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); ++SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); ++SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); ++#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 ++SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); ++SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); ++#ifndef SQLITE_OMIT_WAL ++SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); ++SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); ++SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); ++SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); ++#endif /* SQLITE_OMIT_WAL */ ++SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **); ++SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *); ++ ++ ++/* ++** Functions for accessing sqlite3_vfs methods ++*/ ++SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); ++SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); ++SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); ++SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); ++#ifndef SQLITE_OMIT_LOAD_EXTENSION ++SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); ++SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); ++SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); ++SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); ++#endif /* SQLITE_OMIT_LOAD_EXTENSION */ ++SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); ++SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); ++SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*); ++SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); ++ ++/* ++** Convenience functions for opening and closing files using ++** sqlite3_malloc() to obtain space for the file-handle structure. ++*/ ++SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); ++SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *); ++ ++#endif /* _SQLITE_OS_H_ */ ++ ++/************** End of os.h **************************************************/ ++/************** Continuing where we left off in sqliteInt.h ******************/ + /************** Include pager.h in the middle of sqliteInt.h *****************/ + /************** Begin file pager.h *******************************************/ + /* +@@ -14793,14 +16208,15 @@ typedef struct Pager Pager; + typedef struct PgHdr DbPage; + + /* +-** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is ++** Page number PAGER_SJ_PGNO is never used in an SQLite database (it is + ** reserved for working around a windows/posix incompatibility). It is + ** used in the journal to signify that the remainder of the journal file + ** is devoted to storing a super-journal name - there are no more pages to + ** roll back. See comments for function writeSuperJournal() in pager.c + ** for details. + */ +-#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) ++#define PAGER_SJ_PGNO_COMPUTED(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) ++#define PAGER_SJ_PGNO(x) ((x)->lckPgno) + + /* + ** Allowed values for the flags parameter to sqlite3PagerOpen(). +@@ -14832,6 +16248,22 @@ typedef struct PgHdr DbPage; + #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ + #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ + ++#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL) ++ ++/* ++** The argument to this macro is a file descriptor (type sqlite3_file*). ++** Return 0 if it is not open, or non-zero (but not 1) if it is. ++** ++** This is so that expressions can be written as: ++** ++** if( isOpen(pPager->jfd) ){ ... ++** ++** instead of ++** ++** if( pPager->jfd->pMethods ){ ... ++*/ ++#define isOpen(pFd) ((pFd)->pMethods!=0) ++ + /* + ** Flags that make up the mask passed to sqlite3PagerGet(). + */ +@@ -14965,7 +16397,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); + SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); + SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); + SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); +-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); ++SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*); + SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); + SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); + +@@ -14989,6 +16421,10 @@ SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); + # define enable_simulated_io_errors() + #endif + ++#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) ++SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager*); ++#endif ++ + #endif /* SQLITE_PAGER_H */ + + /************** End of pager.h ***********************************************/ +@@ -15082,16 +16518,24 @@ SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); + SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int); + SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); + SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags); +-SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); +-SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); ++SQLITE_PRIVATE int sqlite3BtreeTxnState(Btree*); + SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); ++ + SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); + SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); + #ifndef SQLITE_OMIT_SHARED_CACHE + SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); + #endif ++ ++/* Savepoints are named, nestable SQL transactions mostly implemented */ ++/* in vdbe.c and pager.c See https://sqlite.org/lang_savepoint.html */ + SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); + ++/* "Checkpoint" only refers to WAL. See https://sqlite.org/wal.html#ckpt */ ++#ifndef SQLITE_OMIT_WAL ++SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); ++#endif ++ + SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); + SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); + SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); +@@ -15112,7 +16556,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); + #define BTREE_BLOBKEY 2 /* Table has keys only - no data */ + + SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); +-SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); ++SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, i64*); + SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor*); + SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree*, int, int); + +@@ -15172,7 +16616,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); + ** reduce network bandwidth. + ** + ** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by +-** standard SQLite. The other hints are provided for extentions that use ++** standard SQLite. The other hints are provided for extensions that use + ** the SQLite parser and code generator but substitute their own storage + ** engine. + */ +@@ -15229,6 +16673,9 @@ SQLITE_PRIVATE int sqlite3BtreeCursor( + ); + SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); + SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); ++#ifdef SQLITE_DEBUG ++SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(Btree*,BtCursor*); ++#endif + SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); + SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); + #ifdef SQLITE_ENABLE_CURSOR_HINTS +@@ -15236,13 +16683,17 @@ SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...); + #endif + + SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); +-SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( ++SQLITE_PRIVATE int sqlite3BtreeTableMoveto( + BtCursor*, +- UnpackedRecord *pUnKey, + i64 intKey, + int bias, + int *pRes + ); ++SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( ++ BtCursor*, ++ UnpackedRecord *pUnKey, ++ int *pRes ++); + SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*); + SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*); + SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags); +@@ -15251,6 +16702,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags); + #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ + #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ + #define BTREE_APPEND 0x08 /* Insert is likely an append */ ++#define BTREE_PREFORMAT 0x80 /* Inserted data is a preformated cell */ + + /* An instance of the BtreePayload object describes the content of a single + ** entry in either an index or table btree. +@@ -15305,15 +16757,22 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int flags); + SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor*); + SQLITE_PRIVATE void sqlite3BtreeCursorPin(BtCursor*); + SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor*); +-#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); +-#endif + SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); + SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); + SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); + SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); + +-SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*); ++SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( ++ sqlite3 *db, /* Database connection that is running the check */ ++ Btree *p, /* The btree to be checked */ ++ Pgno *aRoot, /* An array of root pages numbers for individual trees */ ++ sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */ ++ int nRoot, /* Number of entries in aRoot[] */ ++ int mxErr, /* Stop reporting errors after this many */ ++ int *pnErr, /* OUT: Write number of errors seen to this variable */ ++ char **pzOut /* OUT: Write the error message string here */ ++); + SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); + SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*); + +@@ -15328,6 +16787,12 @@ SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask); + SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *pBt); + SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void); + ++#ifdef SQLITE_DEBUG ++SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree*); ++#else ++# define sqlite3BtreeSeekCount(X) 0 ++#endif ++ + #ifndef NDEBUG + SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); + #endif +@@ -15344,6 +16809,10 @@ SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); + SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); + #endif + ++SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64); ++ ++SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree*); ++ + /* + ** If we are not using shared cache, then there is no need to + ** use mutexes to access the BtShared structures. So make the +@@ -15425,6 +16894,20 @@ typedef struct Vdbe Vdbe; + */ + typedef struct sqlite3_value Mem; + typedef struct SubProgram SubProgram; ++typedef struct SubrtnSig SubrtnSig; ++ ++/* ++** A signature for a reusable subroutine that materializes the RHS of ++** an IN operator. ++*/ ++struct SubrtnSig { ++ int selId; /* SELECT-id for the SELECT statement on the RHS */ ++ u8 bComplete; /* True if fully coded and available for reusable */ ++ char *zAff; /* Affinity of the overall IN expression */ ++ int iTable; /* Ephemeral table generated by the subroutine */ ++ int iAddr; /* Subroutine entry address */ ++ int regReturn; /* Register used to hold return address */ ++}; + + /* + ** A single instruction of the virtual machine has an opcode +@@ -15453,22 +16936,22 @@ struct VdbeOp { + u32 *ai; /* Used when p4type is P4_INTARRAY */ + SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ + Table *pTab; /* Used when p4type is P4_TABLE */ ++ SubrtnSig *pSubrtnSig; /* Used when p4type is P4_SUBRTNSIG */ + #ifdef SQLITE_ENABLE_CURSOR_HINTS + Expr *pExpr; /* Used when p4type is P4_EXPR */ + #endif +- int (*xAdvance)(BtCursor *, int); + } p4; + #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + char *zComment; /* Comment to improve readability */ + #endif +-#ifdef VDBE_PROFILE +- u32 cnt; /* Number of times this instruction was executed */ +- u64 cycles; /* Total time spent executing this instruction */ +-#endif + #ifdef SQLITE_VDBE_COVERAGE + u32 iSrcLine; /* Source-code line that generated this opcode + ** with flags in the upper 8 bits */ + #endif ++#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) ++ u64 nExec; ++ u64 nCycle; ++#endif + }; + typedef struct VdbeOp VdbeOp; + +@@ -15507,21 +16990,21 @@ typedef struct VdbeOpList VdbeOpList; + #define P4_COLLSEQ (-2) /* P4 is a pointer to a CollSeq structure */ + #define P4_INT32 (-3) /* P4 is a 32-bit signed integer */ + #define P4_SUBPROGRAM (-4) /* P4 is a pointer to a SubProgram structure */ +-#define P4_ADVANCE (-5) /* P4 is a pointer to BtreeNext() or BtreePrev() */ +-#define P4_TABLE (-6) /* P4 is a pointer to a Table structure */ ++#define P4_TABLE (-5) /* P4 is a pointer to a Table structure */ + /* Above do not own any resources. Must free those below */ +-#define P4_FREE_IF_LE (-7) +-#define P4_DYNAMIC (-7) /* Pointer to memory from sqliteMalloc() */ +-#define P4_FUNCDEF (-8) /* P4 is a pointer to a FuncDef structure */ +-#define P4_KEYINFO (-9) /* P4 is a pointer to a KeyInfo structure */ +-#define P4_EXPR (-10) /* P4 is a pointer to an Expr tree */ +-#define P4_MEM (-11) /* P4 is a pointer to a Mem* structure */ +-#define P4_VTAB (-12) /* P4 is a pointer to an sqlite3_vtab structure */ +-#define P4_REAL (-13) /* P4 is a 64-bit floating point value */ +-#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */ +-#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ +-#define P4_FUNCCTX (-16) /* P4 is a pointer to an sqlite3_context object */ +-#define P4_DYNBLOB (-17) /* Pointer to memory from sqliteMalloc() */ ++#define P4_FREE_IF_LE (-6) ++#define P4_DYNAMIC (-6) /* Pointer to memory from sqliteMalloc() */ ++#define P4_FUNCDEF (-7) /* P4 is a pointer to a FuncDef structure */ ++#define P4_KEYINFO (-8) /* P4 is a pointer to a KeyInfo structure */ ++#define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ ++#define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ ++#define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */ ++#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ ++#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ ++#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ ++#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ ++#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ ++#define P4_SUBRTNSIG (-17) /* P4 is a SubrtnSig pointer */ + + /* Error message codes for OP_Halt */ + #define P5_ConstraintNotNull 1 +@@ -15566,179 +17049,193 @@ typedef struct VdbeOpList VdbeOpList; + #define OP_Savepoint 0 + #define OP_AutoCommit 1 + #define OP_Transaction 2 +-#define OP_SorterNext 3 /* jump */ +-#define OP_Prev 4 /* jump */ +-#define OP_Next 5 /* jump */ +-#define OP_Checkpoint 6 +-#define OP_JournalMode 7 +-#define OP_Vacuum 8 +-#define OP_VFilter 9 /* jump, synopsis: iplan=r[P3] zplan='P4' */ +-#define OP_VUpdate 10 /* synopsis: data=r[P3@P2] */ +-#define OP_Goto 11 /* jump */ +-#define OP_Gosub 12 /* jump */ +-#define OP_InitCoroutine 13 /* jump */ +-#define OP_Yield 14 /* jump */ +-#define OP_MustBeInt 15 /* jump */ +-#define OP_Jump 16 /* jump */ +-#define OP_Once 17 /* jump */ +-#define OP_If 18 /* jump */ ++#define OP_Checkpoint 3 ++#define OP_JournalMode 4 ++#define OP_Vacuum 5 ++#define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */ ++#define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */ ++#define OP_Init 8 /* jump0, synopsis: Start at P2 */ ++#define OP_Goto 9 /* jump */ ++#define OP_Gosub 10 /* jump */ ++#define OP_InitCoroutine 11 /* jump0 */ ++#define OP_Yield 12 /* jump0 */ ++#define OP_MustBeInt 13 /* jump0 */ ++#define OP_Jump 14 /* jump */ ++#define OP_Once 15 /* jump */ ++#define OP_If 16 /* jump */ ++#define OP_IfNot 17 /* jump */ ++#define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */ + #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ +-#define OP_IfNot 20 /* jump */ +-#define OP_IfNullRow 21 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ +-#define OP_SeekLT 22 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_SeekLE 23 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_SeekGE 24 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_SeekGT 25 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_IfNotOpen 26 /* jump, synopsis: if( !csr[P1] ) goto P2 */ +-#define OP_IfNoHope 27 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_NoConflict 28 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_NotFound 29 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_Found 30 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_SeekRowid 31 /* jump, synopsis: intkey=r[P3] */ +-#define OP_NotExists 32 /* jump, synopsis: intkey=r[P3] */ +-#define OP_Last 33 /* jump */ +-#define OP_IfSmaller 34 /* jump */ +-#define OP_SorterSort 35 /* jump */ +-#define OP_Sort 36 /* jump */ +-#define OP_Rewind 37 /* jump */ +-#define OP_IdxLE 38 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_IdxGT 39 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_IdxLT 40 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_IdxGE 41 /* jump, synopsis: key=r[P3@P4] */ +-#define OP_RowSetRead 42 /* jump, synopsis: r[P3]=rowset(P1) */ ++#define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ ++#define OP_SeekLT 21 /* jump0, synopsis: key=r[P3@P4] */ ++#define OP_SeekLE 22 /* jump0, synopsis: key=r[P3@P4] */ ++#define OP_SeekGE 23 /* jump0, synopsis: key=r[P3@P4] */ ++#define OP_SeekGT 24 /* jump0, synopsis: key=r[P3@P4] */ ++#define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */ ++#define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */ ++#define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */ ++#define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */ ++#define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */ ++#define OP_SeekRowid 30 /* jump0, synopsis: intkey=r[P3] */ ++#define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */ ++#define OP_Last 32 /* jump0 */ ++#define OP_IfSizeBetween 33 /* jump */ ++#define OP_SorterSort 34 /* jump */ ++#define OP_Sort 35 /* jump */ ++#define OP_Rewind 36 /* jump0 */ ++#define OP_SorterNext 37 /* jump */ ++#define OP_Prev 38 /* jump */ ++#define OP_Next 39 /* jump */ ++#define OP_IdxLE 40 /* jump, synopsis: key=r[P3@P4] */ ++#define OP_IdxGT 41 /* jump, synopsis: key=r[P3@P4] */ ++#define OP_IdxLT 42 /* jump, synopsis: key=r[P3@P4] */ + #define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */ + #define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */ +-#define OP_RowSetTest 45 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ +-#define OP_Program 46 /* jump */ +-#define OP_FkIfZero 47 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ +-#define OP_IfPos 48 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ +-#define OP_IfNotZero 49 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ +-#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ +-#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ +-#define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ +-#define OP_Eq 53 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */ +-#define OP_Gt 54 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */ +-#define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ +-#define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ +-#define OP_ElseNotEq 58 /* jump, same as TK_ESCAPE */ +-#define OP_DecrJumpZero 59 /* jump, synopsis: if (--r[P1])==0 goto P2 */ +-#define OP_IncrVacuum 60 /* jump */ +-#define OP_VNext 61 /* jump */ +-#define OP_Init 62 /* jump, synopsis: Start at P2 */ +-#define OP_PureFunc 63 /* synopsis: r[P3]=func(r[P2@NP]) */ +-#define OP_Function 64 /* synopsis: r[P3]=func(r[P2@NP]) */ +-#define OP_Return 65 +-#define OP_EndCoroutine 66 +-#define OP_HaltIfNull 67 /* synopsis: if r[P3]=null halt */ +-#define OP_Halt 68 +-#define OP_Integer 69 /* synopsis: r[P2]=P1 */ +-#define OP_Int64 70 /* synopsis: r[P2]=P4 */ +-#define OP_String 71 /* synopsis: r[P2]='P4' (len=P1) */ +-#define OP_Null 72 /* synopsis: r[P2..P3]=NULL */ +-#define OP_SoftNull 73 /* synopsis: r[P1]=NULL */ +-#define OP_Blob 74 /* synopsis: r[P2]=P4 (len=P1) */ +-#define OP_Variable 75 /* synopsis: r[P2]=parameter(P1,P4) */ +-#define OP_Move 76 /* synopsis: r[P2@P3]=r[P1@P3] */ +-#define OP_Copy 77 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ +-#define OP_SCopy 78 /* synopsis: r[P2]=r[P1] */ +-#define OP_IntCopy 79 /* synopsis: r[P2]=r[P1] */ +-#define OP_ResultRow 80 /* synopsis: output=r[P1@P2] */ +-#define OP_CollSeq 81 +-#define OP_AddImm 82 /* synopsis: r[P1]=r[P1]+P2 */ +-#define OP_RealAffinity 83 +-#define OP_Cast 84 /* synopsis: affinity(r[P1]) */ +-#define OP_Permutation 85 +-#define OP_Compare 86 /* synopsis: r[P1@P3] <-> r[P2@P3] */ +-#define OP_IsTrue 87 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ +-#define OP_Offset 88 /* synopsis: r[P3] = sqlite_offset(P1) */ +-#define OP_Column 89 /* synopsis: r[P3]=PX */ +-#define OP_Affinity 90 /* synopsis: affinity(r[P1@P2]) */ +-#define OP_MakeRecord 91 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +-#define OP_Count 92 /* synopsis: r[P2]=count() */ +-#define OP_ReadCookie 93 +-#define OP_SetCookie 94 +-#define OP_ReopenIdx 95 /* synopsis: root=P2 iDb=P3 */ +-#define OP_OpenRead 96 /* synopsis: root=P2 iDb=P3 */ +-#define OP_OpenWrite 97 /* synopsis: root=P2 iDb=P3 */ +-#define OP_OpenDup 98 +-#define OP_OpenAutoindex 99 /* synopsis: nColumn=P2 */ +-#define OP_OpenEphemeral 100 /* synopsis: nColumn=P2 */ +-#define OP_BitAnd 101 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ +-#define OP_BitOr 102 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ +-#define OP_ShiftLeft 103 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ +-#define OP_Add 105 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ +-#define OP_Subtract 106 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ +-#define OP_Multiply 107 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ +-#define OP_Divide 108 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ +-#define OP_Remainder 109 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ +-#define OP_Concat 110 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ +-#define OP_SorterOpen 111 +-#define OP_BitNot 112 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ +-#define OP_SequenceTest 113 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ +-#define OP_OpenPseudo 114 /* synopsis: P3 columns in r[P2] */ +-#define OP_String8 115 /* same as TK_STRING, synopsis: r[P2]='P4' */ +-#define OP_Close 116 +-#define OP_ColumnsUsed 117 +-#define OP_SeekHit 118 /* synopsis: seekHit=P2 */ +-#define OP_Sequence 119 /* synopsis: r[P2]=cursor[P1].ctr++ */ +-#define OP_NewRowid 120 /* synopsis: r[P2]=rowid */ +-#define OP_Insert 121 /* synopsis: intkey=r[P3] data=r[P2] */ +-#define OP_Delete 122 +-#define OP_ResetCount 123 +-#define OP_SorterCompare 124 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +-#define OP_SorterData 125 /* synopsis: r[P2]=data */ +-#define OP_RowData 126 /* synopsis: r[P2]=data */ +-#define OP_Rowid 127 /* synopsis: r[P2]=rowid */ +-#define OP_NullRow 128 +-#define OP_SeekEnd 129 +-#define OP_IdxInsert 130 /* synopsis: key=r[P2] */ +-#define OP_SorterInsert 131 /* synopsis: key=r[P2] */ +-#define OP_IdxDelete 132 /* synopsis: key=r[P2@P3] */ +-#define OP_DeferredSeek 133 /* synopsis: Move P3 to P1.rowid if needed */ +-#define OP_IdxRowid 134 /* synopsis: r[P2]=rowid */ +-#define OP_FinishSeek 135 +-#define OP_Destroy 136 +-#define OP_Clear 137 +-#define OP_ResetSorter 138 +-#define OP_CreateBtree 139 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +-#define OP_SqlExec 140 +-#define OP_ParseSchema 141 +-#define OP_LoadAnalysis 142 +-#define OP_DropTable 143 +-#define OP_DropIndex 144 +-#define OP_DropTrigger 145 +-#define OP_IntegrityCk 146 +-#define OP_RowSetAdd 147 /* synopsis: rowset(P1)=r[P2] */ +-#define OP_Param 148 +-#define OP_FkCounter 149 /* synopsis: fkctr[P1]+=P2 */ +-#define OP_Real 150 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ +-#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +-#define OP_OffsetLimit 152 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +-#define OP_AggInverse 153 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +-#define OP_AggStep 154 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +-#define OP_AggStep1 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +-#define OP_AggValue 156 /* synopsis: r[P3]=value N=P2 */ +-#define OP_AggFinal 157 /* synopsis: accum=r[P1] N=P2 */ +-#define OP_Expire 158 +-#define OP_CursorLock 159 +-#define OP_CursorUnlock 160 +-#define OP_TableLock 161 /* synopsis: iDb=P1 root=P2 write=P3 */ +-#define OP_VBegin 162 +-#define OP_VCreate 163 +-#define OP_VDestroy 164 +-#define OP_VOpen 165 +-#define OP_VColumn 166 /* synopsis: r[P3]=vcolumn(P2) */ +-#define OP_VRename 167 +-#define OP_Pagecount 168 +-#define OP_MaxPgcnt 169 +-#define OP_Trace 170 +-#define OP_CursorHint 171 +-#define OP_ReleaseReg 172 /* synopsis: release r[P1@P2] mask P3 */ +-#define OP_Noop 173 +-#define OP_Explain 174 +-#define OP_Abortable 175 ++#define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */ ++#define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */ ++#define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ ++#define OP_Program 48 /* jump0 */ ++#define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ ++#define OP_IfPos 50 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ ++#define OP_IsNull 51 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ ++#define OP_NotNull 52 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ ++#define OP_Ne 53 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ ++#define OP_Eq 54 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */ ++#define OP_Gt 55 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */ ++#define OP_Le 56 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ ++#define OP_Lt 57 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ ++#define OP_ElseEq 59 /* jump, same as TK_ESCAPE */ ++#define OP_IfNotZero 60 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ ++#define OP_DecrJumpZero 61 /* jump, synopsis: if (--r[P1])==0 goto P2 */ ++#define OP_IncrVacuum 62 /* jump */ ++#define OP_VNext 63 /* jump */ ++#define OP_Filter 64 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */ ++#define OP_PureFunc 65 /* synopsis: r[P3]=func(r[P2@NP]) */ ++#define OP_Function 66 /* synopsis: r[P3]=func(r[P2@NP]) */ ++#define OP_Return 67 ++#define OP_EndCoroutine 68 ++#define OP_HaltIfNull 69 /* synopsis: if r[P3]=null halt */ ++#define OP_Halt 70 ++#define OP_Integer 71 /* synopsis: r[P2]=P1 */ ++#define OP_Int64 72 /* synopsis: r[P2]=P4 */ ++#define OP_String 73 /* synopsis: r[P2]='P4' (len=P1) */ ++#define OP_BeginSubrtn 74 /* synopsis: r[P2]=NULL */ ++#define OP_Null 75 /* synopsis: r[P2..P3]=NULL */ ++#define OP_SoftNull 76 /* synopsis: r[P1]=NULL */ ++#define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */ ++#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1) */ ++#define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */ ++#define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ ++#define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */ ++#define OP_IntCopy 82 /* synopsis: r[P2]=r[P1] */ ++#define OP_FkCheck 83 ++#define OP_ResultRow 84 /* synopsis: output=r[P1@P2] */ ++#define OP_CollSeq 85 ++#define OP_AddImm 86 /* synopsis: r[P1]=r[P1]+P2 */ ++#define OP_RealAffinity 87 ++#define OP_Cast 88 /* synopsis: affinity(r[P1]) */ ++#define OP_Permutation 89 ++#define OP_Compare 90 /* synopsis: r[P1@P3] <-> r[P2@P3] */ ++#define OP_IsTrue 91 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ ++#define OP_ZeroOrNull 92 /* synopsis: r[P2] = 0 OR NULL */ ++#define OP_Offset 93 /* synopsis: r[P3] = sqlite_offset(P1) */ ++#define OP_Column 94 /* synopsis: r[P3]=PX cursor P1 column P2 */ ++#define OP_TypeCheck 95 /* synopsis: typecheck(r[P1@P2]) */ ++#define OP_Affinity 96 /* synopsis: affinity(r[P1@P2]) */ ++#define OP_MakeRecord 97 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ ++#define OP_Count 98 /* synopsis: r[P2]=count() */ ++#define OP_ReadCookie 99 ++#define OP_SetCookie 100 ++#define OP_ReopenIdx 101 /* synopsis: root=P2 iDb=P3 */ ++#define OP_OpenRead 102 /* synopsis: root=P2 iDb=P3 */ ++#define OP_BitAnd 103 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ ++#define OP_BitOr 104 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ ++#define OP_ShiftLeft 105 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ ++#define OP_Add 107 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ ++#define OP_Subtract 108 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ ++#define OP_Multiply 109 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ ++#define OP_Divide 110 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ ++#define OP_Remainder 111 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ ++#define OP_Concat 112 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ ++#define OP_OpenWrite 113 /* synopsis: root=P2 iDb=P3 */ ++#define OP_OpenDup 114 ++#define OP_BitNot 115 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ ++#define OP_OpenAutoindex 116 /* synopsis: nColumn=P2 */ ++#define OP_OpenEphemeral 117 /* synopsis: nColumn=P2 */ ++#define OP_String8 118 /* same as TK_STRING, synopsis: r[P2]='P4' */ ++#define OP_SorterOpen 119 ++#define OP_SequenceTest 120 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ ++#define OP_OpenPseudo 121 /* synopsis: P3 columns in r[P2] */ ++#define OP_Close 122 ++#define OP_ColumnsUsed 123 ++#define OP_SeekScan 124 /* synopsis: Scan-ahead up to P1 rows */ ++#define OP_SeekHit 125 /* synopsis: set P2<=seekHit<=P3 */ ++#define OP_Sequence 126 /* synopsis: r[P2]=cursor[P1].ctr++ */ ++#define OP_NewRowid 127 /* synopsis: r[P2]=rowid */ ++#define OP_Insert 128 /* synopsis: intkey=r[P3] data=r[P2] */ ++#define OP_RowCell 129 ++#define OP_Delete 130 ++#define OP_ResetCount 131 ++#define OP_SorterCompare 132 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ ++#define OP_SorterData 133 /* synopsis: r[P2]=data */ ++#define OP_RowData 134 /* synopsis: r[P2]=data */ ++#define OP_Rowid 135 /* synopsis: r[P2]=PX rowid of P1 */ ++#define OP_NullRow 136 ++#define OP_SeekEnd 137 ++#define OP_IdxInsert 138 /* synopsis: key=r[P2] */ ++#define OP_SorterInsert 139 /* synopsis: key=r[P2] */ ++#define OP_IdxDelete 140 /* synopsis: key=r[P2@P3] */ ++#define OP_DeferredSeek 141 /* synopsis: Move P3 to P1.rowid if needed */ ++#define OP_IdxRowid 142 /* synopsis: r[P2]=rowid */ ++#define OP_FinishSeek 143 ++#define OP_Destroy 144 ++#define OP_Clear 145 ++#define OP_ResetSorter 146 ++#define OP_CreateBtree 147 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ ++#define OP_SqlExec 148 ++#define OP_ParseSchema 149 ++#define OP_LoadAnalysis 150 ++#define OP_DropTable 151 ++#define OP_DropIndex 152 ++#define OP_DropTrigger 153 ++#define OP_Real 154 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ ++#define OP_IntegrityCk 155 ++#define OP_RowSetAdd 156 /* synopsis: rowset(P1)=r[P2] */ ++#define OP_Param 157 ++#define OP_FkCounter 158 /* synopsis: fkctr[P1]+=P2 */ ++#define OP_MemMax 159 /* synopsis: r[P1]=max(r[P1],r[P2]) */ ++#define OP_OffsetLimit 160 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ ++#define OP_AggInverse 161 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ ++#define OP_AggStep 162 /* synopsis: accum=r[P3] step(r[P2@P5]) */ ++#define OP_AggStep1 163 /* synopsis: accum=r[P3] step(r[P2@P5]) */ ++#define OP_AggValue 164 /* synopsis: r[P3]=value N=P2 */ ++#define OP_AggFinal 165 /* synopsis: accum=r[P1] N=P2 */ ++#define OP_Expire 166 ++#define OP_CursorLock 167 ++#define OP_CursorUnlock 168 ++#define OP_TableLock 169 /* synopsis: iDb=P1 root=P2 write=P3 */ ++#define OP_VBegin 170 ++#define OP_VCreate 171 ++#define OP_VDestroy 172 ++#define OP_VOpen 173 ++#define OP_VCheck 174 ++#define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */ ++#define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */ ++#define OP_VRename 177 ++#define OP_Pagecount 178 ++#define OP_MaxPgcnt 179 ++#define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */ ++#define OP_GetSubtype 181 /* synopsis: r[P2] = r[P1].subtype */ ++#define OP_SetSubtype 182 /* synopsis: r[P2].subtype = r[P1] */ ++#define OP_FilterAdd 183 /* synopsis: filter(P1) += key(P3@P4) */ ++#define OP_Trace 184 ++#define OP_CursorHint 185 ++#define OP_ReleaseReg 186 /* synopsis: release r[P1@P2] mask P3 */ ++#define OP_Noop 187 ++#define OP_Explain 188 ++#define OP_Abortable 189 + + /* Properties such as "out2" or "jump" that are specified in + ** comments following the "case" for each opcode in the vdbe.c +@@ -15750,38 +17247,41 @@ typedef struct VdbeOpList VdbeOpList; + #define OPFLG_IN3 0x08 /* in3: P3 is an input */ + #define OPFLG_OUT2 0x10 /* out2: P2 is an output */ + #define OPFLG_OUT3 0x20 /* out3: P3 is an output */ ++#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */ ++#define OPFLG_JUMP0 0x80 /* jump0: P2 might be zero */ + #define OPFLG_INITIALIZER {\ +-/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x10,\ +-/* 8 */ 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03,\ +-/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x01, 0x09, 0x09,\ +-/* 24 */ 0x09, 0x09, 0x01, 0x09, 0x09, 0x09, 0x09, 0x09,\ +-/* 32 */ 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\ +-/* 40 */ 0x01, 0x01, 0x23, 0x26, 0x26, 0x0b, 0x01, 0x01,\ +-/* 48 */ 0x03, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ +-/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00,\ +-/* 64 */ 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10, 0x10,\ +-/* 72 */ 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\ +-/* 80 */ 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x12,\ +-/* 88 */ 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ +-/* 96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26,\ +-/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00,\ +-/* 112 */ 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,\ +-/* 120 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ +-/* 128 */ 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10, 0x00,\ +-/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ +-/* 144 */ 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x10, 0x04,\ +-/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +-/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +-/* 168 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +-} +- +-/* The sqlite3P2Values() routine is able to run faster if it knows ++/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\ ++/* 8 */ 0x81, 0x01, 0x01, 0x81, 0x83, 0x83, 0x01, 0x01,\ ++/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\ ++/* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\ ++/* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x41, 0x41,\ ++/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ ++/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\ ++/* 56 */ 0x0b, 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x01, 0x41,\ ++/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ ++/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ ++/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\ ++/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40, 0x00,\ ++/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x40, 0x26,\ ++/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ ++/* 112 */ 0x26, 0x00, 0x40, 0x12, 0x40, 0x40, 0x10, 0x00,\ ++/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ ++/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\ ++/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ ++/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ ++/* 152 */ 0x00, 0x00, 0x10, 0x00, 0x06, 0x10, 0x00, 0x04,\ ++/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ ++/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\ ++/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\ ++/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} ++ ++/* The resolve3P2Values() routine is able to run faster if it knows + ** the value of the largest JUMP opcode. The smaller the maximum + ** JUMP opcode the better, so the mkopcodeh.tcl script that + ** generated this include file strives to group all JUMP opcodes + ** together near the beginning of the list. + */ +-#define SQLITE_MX_JUMP_OPCODE 62 /* Maximum JUMP opcode */ ++#define SQLITE_MX_JUMP_OPCODE 64 /* Maximum JUMP opcode */ + + /************** End of opcodes.h *********************************************/ + /************** Continuing where we left off in vdbe.h ***********************/ +@@ -15790,7 +17290,7 @@ typedef struct VdbeOpList VdbeOpList; + ** Additional non-public SQLITE_PREPARE_* flags + */ + #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ +-#define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */ ++#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ + + /* + ** Prototypes for the VDBE interface. See comments on the implementation +@@ -15819,19 +17319,27 @@ SQLITE_PRIVATE void sqlite3VdbeVerifyNoResultRow(Vdbe *p); + #endif + #if defined(SQLITE_DEBUG) + SQLITE_PRIVATE void sqlite3VdbeVerifyAbortable(Vdbe *p, int); ++SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int); + #else + # define sqlite3VdbeVerifyAbortable(A,B) ++# define sqlite3VdbeNoJumpsOutsideSubrtn(A,B,C,D) + #endif + SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); + #ifndef SQLITE_OMIT_EXPLAIN +-SQLITE_PRIVATE void sqlite3VdbeExplain(Parse*,u8,const char*,...); ++SQLITE_PRIVATE int sqlite3VdbeExplain(Parse*,u8,const char*,...); + SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse*); + SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*); + # define ExplainQueryPlan(P) sqlite3VdbeExplain P ++# ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++# define ExplainQueryPlan2(V,P) (V = sqlite3VdbeExplain P) ++# else ++# define ExplainQueryPlan2(V,P) ExplainQueryPlan(P) ++# endif + # define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P) + # define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P) + #else + # define ExplainQueryPlan(P) ++# define ExplainQueryPlan2(V,P) + # define ExplainQueryPlanPop(P) + # define ExplainQueryPlanParent(P) 0 + # define sqlite3ExplainBreakpoint(A,B) /*no-op*/ +@@ -15841,12 +17349,13 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char*,const char*); + #else + # define sqlite3ExplainBreakpoint(A,B) /*no-op*/ + #endif +-SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); ++SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*, int, char*, u16); + SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8); + SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); + SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); + SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); + SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u16 P5); ++SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe*, int); + SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); + SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe*, int addr); + SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr); +@@ -15861,11 +17370,11 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type); + SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); + SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); + SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); ++SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe*); + SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse*); + SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); + SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*); + SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); +-SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*); + SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*); + SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); + SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); +@@ -15907,13 +17416,15 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); + SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); + SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*); + ++SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val); ++ + SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); + #ifdef SQLITE_ENABLE_BYTECODE_VTAB + SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); + #endif + +-/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on +-** each VDBE opcode. ++/* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra ++** comments on each VDBE opcode. + ** + ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op + ** comments in VDBE programs that show key decision points in the code +@@ -15939,7 +17450,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); + ** The VdbeCoverage macros are used to set a coverage testing point + ** for VDBE branch instructions. The coverage testing points are line + ** numbers in the sqlite3.c source file. VDBE branch coverage testing +-** only works with an amalagmation build. That's ok since a VDBE branch ++** only works with an amalgamation build. That's ok since a VDBE branch + ** coverage build designed for testing the test suite only. No application + ** should ever ship with VDBE branch coverage measuring turned on. + ** +@@ -15957,7 +17468,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); + ** // NULL option is not possible + ** + ** VdbeCoverageEqNe(v) // Previous OP_Jump is only interested +-** // in distingishing equal and not-equal. ++** // in distinguishing equal and not-equal. + ** + ** Every VDBE branch operation must be tagged with one of the macros above. + ** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and +@@ -15967,7 +17478,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); + ** During testing, the test application will invoke + ** sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE,...) to set a callback + ** routine that is invoked as each bytecode branch is taken. The callback +-** contains the sqlite3.c source line number ov the VdbeCoverage macro and ++** contains the sqlite3.c source line number of the VdbeCoverage macro and + ** flags to indicate whether or not the branch was taken. The test application + ** is responsible for keeping track of this and reporting byte-code branches + ** that are never taken. +@@ -16003,14 +17514,22 @@ SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int); + + #ifdef SQLITE_ENABLE_STMT_SCANSTATUS + SQLITE_PRIVATE void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); ++SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(Vdbe*, int, int, int); ++SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int); + #else +-# define sqlite3VdbeScanStatus(a,b,c,d,e) ++# define sqlite3VdbeScanStatus(a,b,c,d,e,f) ++# define sqlite3VdbeScanStatusRange(a,b,c,d) ++# define sqlite3VdbeScanStatusCounters(a,b,c,d) + #endif + + #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); + #endif + ++#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) ++SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr); ++#endif ++ + #endif /* SQLITE_VDBE_H */ + + /************** End of vdbe.h ************************************************/ +@@ -16059,7 +17578,7 @@ struct PgHdr { + ** private to pcache.c and should not be accessed by other modules. + ** pCache is grouped with the public elements for efficiency. + */ +- i16 nRef; /* Number of users of this page */ ++ i64 nRef; /* Number of users of this page */ + PgHdr *pDirtyNext; /* Next element in list of dirty pages */ + PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ + /* NB: pDirtyNext and pDirtyPrev are undefined if the +@@ -16140,12 +17659,12 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); + SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); + + /* Return the total number of outstanding page references */ +-SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); ++SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache*); + + /* Increment the reference count of an existing page */ + SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); + +-SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); ++SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr*); + + /* Return the total number of pages stored in the cache */ + SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); +@@ -16210,284 +17729,6 @@ SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache); + + /************** End of pcache.h **********************************************/ + /************** Continuing where we left off in sqliteInt.h ******************/ +-/************** Include os.h in the middle of sqliteInt.h ********************/ +-/************** Begin file os.h **********************************************/ +-/* +-** 2001 September 16 +-** +-** 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 header file (together with is companion C source-code file +-** "os.c") attempt to abstract the underlying operating system so that +-** the SQLite library will work on both POSIX and windows systems. +-** +-** This header file is #include-ed by sqliteInt.h and thus ends up +-** being included by every source file. +-*/ +-#ifndef _SQLITE_OS_H_ +-#define _SQLITE_OS_H_ +- +-/* +-** Attempt to automatically detect the operating system and setup the +-** necessary pre-processor macros for it. +-*/ +-/************** Include os_setup.h in the middle of os.h *********************/ +-/************** Begin file os_setup.h ****************************************/ +-/* +-** 2013 November 25 +-** +-** 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 contains pre-processor directives related to operating system +-** detection and/or setup. +-*/ +-#ifndef SQLITE_OS_SETUP_H +-#define SQLITE_OS_SETUP_H +- +-/* +-** Figure out if we are dealing with Unix, Windows, or some other operating +-** system. +-** +-** After the following block of preprocess macros, all of SQLITE_OS_UNIX, +-** SQLITE_OS_WIN, and SQLITE_OS_OTHER will defined to either 1 or 0. One of +-** the three will be 1. The other two will be 0. +-*/ +-#if defined(SQLITE_OS_OTHER) +-# if SQLITE_OS_OTHER==1 +-# undef SQLITE_OS_UNIX +-# define SQLITE_OS_UNIX 0 +-# undef SQLITE_OS_WIN +-# define SQLITE_OS_WIN 0 +-# else +-# undef SQLITE_OS_OTHER +-# endif +-#endif +-#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) +-# define SQLITE_OS_OTHER 0 +-# ifndef SQLITE_OS_WIN +-# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \ +- defined(__MINGW32__) || defined(__BORLANDC__) +-# define SQLITE_OS_WIN 1 +-# define SQLITE_OS_UNIX 0 +-# else +-# define SQLITE_OS_WIN 0 +-# define SQLITE_OS_UNIX 1 +-# endif +-# else +-# define SQLITE_OS_UNIX 0 +-# endif +-#else +-# ifndef SQLITE_OS_WIN +-# define SQLITE_OS_WIN 0 +-# endif +-#endif +- +-#endif /* SQLITE_OS_SETUP_H */ +- +-/************** End of os_setup.h ********************************************/ +-/************** Continuing where we left off in os.h *************************/ +- +-/* If the SET_FULLSYNC macro is not defined above, then make it +-** a no-op +-*/ +-#ifndef SET_FULLSYNC +-# define SET_FULLSYNC(x,y) +-#endif +- +-/* +-** The default size of a disk sector +-*/ +-#ifndef SQLITE_DEFAULT_SECTOR_SIZE +-# define SQLITE_DEFAULT_SECTOR_SIZE 4096 +-#endif +- +-/* +-** Temporary files are named starting with this prefix followed by 16 random +-** alphanumeric characters, and no file extension. They are stored in the +-** OS's standard temporary file directory, and are deleted prior to exit. +-** If sqlite is being embedded in another program, you may wish to change the +-** prefix to reflect your program's name, so that if your program exits +-** prematurely, old temporary files can be easily identified. This can be done +-** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. +-** +-** 2006-10-31: The default prefix used to be "sqlite_". But then +-** Mcafee started using SQLite in their anti-virus product and it +-** started putting files with the "sqlite" name in the c:/temp folder. +-** This annoyed many windows users. Those users would then do a +-** Google search for "sqlite", find the telephone numbers of the +-** developers and call to wake them up at night and complain. +-** For this reason, the default name prefix is changed to be "sqlite" +-** spelled backwards. So the temp files are still identified, but +-** anybody smart enough to figure out the code is also likely smart +-** enough to know that calling the developer will not help get rid +-** of the file. +-*/ +-#ifndef SQLITE_TEMP_FILE_PREFIX +-# define SQLITE_TEMP_FILE_PREFIX "etilqs_" +-#endif +- +-/* +-** The following values may be passed as the second argument to +-** sqlite3OsLock(). The various locks exhibit the following semantics: +-** +-** SHARED: Any number of processes may hold a SHARED lock simultaneously. +-** RESERVED: A single process may hold a RESERVED lock on a file at +-** any time. Other processes may hold and obtain new SHARED locks. +-** PENDING: A single process may hold a PENDING lock on a file at +-** any one time. Existing SHARED locks may persist, but no new +-** SHARED locks may be obtained by other processes. +-** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +-** +-** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a +-** process that requests an EXCLUSIVE lock may actually obtain a PENDING +-** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +-** sqlite3OsLock(). +-*/ +-#define NO_LOCK 0 +-#define SHARED_LOCK 1 +-#define RESERVED_LOCK 2 +-#define PENDING_LOCK 3 +-#define EXCLUSIVE_LOCK 4 +- +-/* +-** File Locking Notes: (Mostly about windows but also some info for Unix) +-** +-** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +-** those functions are not available. So we use only LockFile() and +-** UnlockFile(). +-** +-** LockFile() prevents not just writing but also reading by other processes. +-** A SHARED_LOCK is obtained by locking a single randomly-chosen +-** byte out of a specific range of bytes. The lock byte is obtained at +-** random so two separate readers can probably access the file at the +-** same time, unless they are unlucky and choose the same lock byte. +-** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +-** There can only be one writer. A RESERVED_LOCK is obtained by locking +-** a single byte of the file that is designated as the reserved lock byte. +-** A PENDING_LOCK is obtained by locking a designated byte different from +-** the RESERVED_LOCK byte. +-** +-** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +-** which means we can use reader/writer locks. When reader/writer locks +-** are used, the lock is placed on the same range of bytes that is used +-** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +-** will support two or more Win95 readers or two or more WinNT readers. +-** But a single Win95 reader will lock out all WinNT readers and a single +-** WinNT reader will lock out all other Win95 readers. +-** +-** The following #defines specify the range of bytes used for locking. +-** SHARED_SIZE is the number of bytes available in the pool from which +-** a random byte is selected for a shared lock. The pool of bytes for +-** shared locks begins at SHARED_FIRST. +-** +-** The same locking strategy and +-** byte ranges are used for Unix. This leaves open the possibility of having +-** clients on win95, winNT, and unix all talking to the same shared file +-** and all locking correctly. To do so would require that samba (or whatever +-** tool is being used for file sharing) implements locks correctly between +-** windows and unix. I'm guessing that isn't likely to happen, but by +-** using the same locking range we are at least open to the possibility. +-** +-** Locking in windows is manditory. For this reason, we cannot store +-** actual data in the bytes used for locking. The pager never allocates +-** the pages involved in locking therefore. SHARED_SIZE is selected so +-** that all locks will fit on a single page even at the minimum page size. +-** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE +-** is set high so that we don't have to allocate an unused page except +-** for very large databases. But one should test the page skipping logic +-** by setting PENDING_BYTE low and running the entire regression suite. +-** +-** Changing the value of PENDING_BYTE results in a subtly incompatible +-** file format. Depending on how it is changed, you might not notice +-** the incompatibility right away, even running a full regression test. +-** The default location of PENDING_BYTE is the first byte past the +-** 1GB boundary. +-** +-*/ +-#ifdef SQLITE_OMIT_WSD +-# define PENDING_BYTE (0x40000000) +-#else +-# define PENDING_BYTE sqlite3PendingByte +-#endif +-#define RESERVED_BYTE (PENDING_BYTE+1) +-#define SHARED_FIRST (PENDING_BYTE+2) +-#define SHARED_SIZE 510 +- +-/* +-** Wrapper around OS specific sqlite3_os_init() function. +-*/ +-SQLITE_PRIVATE int sqlite3OsInit(void); +- +-/* +-** Functions for accessing sqlite3_file methods +-*/ +-SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*); +-SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); +-SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); +-SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); +-SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); +-SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); +-SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); +-SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); +-SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); +-SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); +-SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); +-#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 +-SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); +-SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); +-#ifndef SQLITE_OMIT_WAL +-SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); +-SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); +-SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); +-SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); +-#endif /* SQLITE_OMIT_WAL */ +-SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **); +-SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *); +- +- +-/* +-** Functions for accessing sqlite3_vfs methods +-*/ +-SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); +-SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); +-SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); +-SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); +-#ifndef SQLITE_OMIT_LOAD_EXTENSION +-SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); +-SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); +-SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); +-SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); +-#endif /* SQLITE_OMIT_LOAD_EXTENSION */ +-SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); +-SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); +-SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*); +-SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); +- +-/* +-** Convenience functions for opening and closing files using +-** sqlite3_malloc() to obtain space for the file-handle structure. +-*/ +-SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); +-SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *); +- +-#endif /* _SQLITE_OS_H_ */ +- +-/************** End of os.h **************************************************/ +-/************** Continuing where we left off in sqliteInt.h ******************/ + /************** Include mutex.h in the middle of sqliteInt.h *****************/ + /************** Begin file mutex.h *******************************************/ + /* +@@ -16576,7 +17817,7 @@ SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); + /* + ** Default synchronous levels. + ** +-** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ ++** Note that (for historical reasons) the PAGER_SYNCHRONOUS_* macros differ + ** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1. + ** + ** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS +@@ -16615,7 +17856,7 @@ struct Db { + ** An instance of the following structure stores a database schema. + ** + ** Most Schema objects are associated with a Btree. The exception is +-** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. ++** the Schema for the TEMP database (sqlite3.aDb[1]) which is free-standing. + ** In shared cache mode, a single Schema object can be shared by multiple + ** Btrees that refer to the same underlying BtShared object. + ** +@@ -16726,13 +17967,14 @@ struct Lookaside { + LookasideSlot *pInit; /* List of buffers not previously used */ + LookasideSlot *pFree; /* List of available buffers */ + #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE +- LookasideSlot *pSmallInit; /* List of small buffers not prediously used */ ++ LookasideSlot *pSmallInit; /* List of small buffers not previously used */ + LookasideSlot *pSmallFree; /* List of available small buffers */ + void *pMiddle; /* First byte past end of full-size buffers and + ** the first byte of LOOKASIDE_SMALL buffers */ + #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ + void *pStart; /* First byte of available memory space */ + void *pEnd; /* First byte past end of available space */ ++ void *pTrueEnd; /* True value of pEnd, when db->pnBytesFreed!=0 */ + }; + struct LookasideSlot { + LookasideSlot *pNext; /* Next buffer in the list of free buffers */ +@@ -16742,7 +17984,7 @@ struct LookasideSlot { + #define EnableLookaside db->lookaside.bDisable--;\ + db->lookaside.sz=db->lookaside.bDisable?0:db->lookaside.szTrue + +-/* Size of the smaller allocations in two-size lookside */ ++/* Size of the smaller allocations in two-size lookaside */ + #ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE + # define LOOKASIDE_SMALL 0 + #else +@@ -16763,43 +18005,11 @@ struct FuncDefHash { + }; + #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) + +-#ifdef SQLITE_USER_AUTHENTICATION +-/* +-** Information held in the "sqlite3" database connection object and used +-** to manage user authentication. +-*/ +-typedef struct sqlite3_userauth sqlite3_userauth; +-struct sqlite3_userauth { +- u8 authLevel; /* Current authentication level */ +- int nAuthPW; /* Size of the zAuthPW in bytes */ +- char *zAuthPW; /* Password used to authenticate */ +- char *zAuthUser; /* User name used to authenticate */ +-}; +- +-/* Allowed values for sqlite3_userauth.authLevel */ +-#define UAUTH_Unknown 0 /* Authentication not yet checked */ +-#define UAUTH_Fail 1 /* User authentication failed */ +-#define UAUTH_User 2 /* Authenticated as a normal user */ +-#define UAUTH_Admin 3 /* Authenticated as an administrator */ +- +-/* Functions used only by user authorization logic */ +-SQLITE_PRIVATE int sqlite3UserAuthTable(const char*); +-SQLITE_PRIVATE int sqlite3UserAuthCheckLogin(sqlite3*,const char*,u8*); +-SQLITE_PRIVATE void sqlite3UserAuthInit(sqlite3*); +-SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); +- +-#endif /* SQLITE_USER_AUTHENTICATION */ +- + /* + ** typedef for the authorization callback function. + */ +-#ifdef SQLITE_USER_AUTHENTICATION +- typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, +- const char*, const char*); +-#else +- typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, +- const char*); +-#endif ++typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, ++ const char*); + + #ifndef SQLITE_OMIT_DEPRECATED + /* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing +@@ -16813,6 +18023,11 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); + #endif /* SQLITE_OMIT_DEPRECATED */ + #define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */ + ++/* ++** Maximum number of sqlite3.aDb[] entries. This is the number of attached ++** databases plus 2 for "main" and "temp". ++*/ ++#define SQLITE_MAX_DB (SQLITE_MAX_ATTACHED+2) + + /* + ** Each database connection is an instance of the following structure. +@@ -16831,9 +18046,10 @@ struct sqlite3 { + u32 nSchemaLock; /* Do not reset the schema when non-zero */ + unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ + int errCode; /* Most recent error code (SQLITE_*) */ ++ int errByteOffset; /* Byte offset of error in SQL statement */ + int errMask; /* & result codes with this before returning */ + int iSysErrno; /* Errno value from last system error */ +- u16 dbOptFlags; /* Flags to enable/disable optimizations */ ++ u32 dbOptFlags; /* Flags to enable/disable optimizations */ + u8 enc; /* Text encoding */ + u8 autoCommit; /* The auto-commit flag. */ + u8 temp_store; /* 1: file 2: memory 0: default */ +@@ -16847,10 +18063,10 @@ struct sqlite3 { + u8 mTrace; /* zero or more SQLITE_TRACE flags */ + u8 noSharedCache; /* True if no shared-cache backends */ + u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ ++ u8 eOpenState; /* Current condition of the connection */ + int nextPagesize; /* Pagesize after VACUUM if >0 */ +- u32 magic; /* Magic number for detect library misuse */ +- int nChange; /* Value returned by sqlite3_changes() */ +- int nTotalChange; /* Value returned by sqlite3_total_changes() */ ++ i64 nChange; /* Value returned by sqlite3_changes() */ ++ i64 nTotalChange; /* Value returned by sqlite3_total_changes() */ + int aLimit[SQLITE_N_LIMIT]; /* Limits */ + int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ + struct sqlite3InitInfo { /* Information used during initialization */ +@@ -16860,7 +18076,7 @@ struct sqlite3 { + unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ + unsigned imposterTable : 1; /* Building an imposter table */ + unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ +- char **azInit; /* "type", "name", and "tbl_name" columns */ ++ const char **azInit; /* "type", "name", and "tbl_name" columns */ + } init; + int nVdbeActive; /* Number of VDBEs currently running */ + int nVdbeRead; /* Number of active VDBEs that read or write */ +@@ -16870,10 +18086,10 @@ struct sqlite3 { + int nExtension; /* Number of loaded extensions */ + void **aExtension; /* Array of shared library handles */ + union { +- void (*xLegacy)(void*,const char*); /* Legacy trace function */ +- int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ ++ void (*xLegacy)(void*,const char*); /* mTrace==SQLITE_TRACE_LEGACY */ ++ int (*xV2)(u32,void*,void*,void*); /* All other mTrace values */ + } trace; +- void *pTraceArg; /* Argument to the trace function */ ++ void *pTraceArg; /* Argument to the trace function */ + #ifndef SQLITE_OMIT_DEPRECATED + void (*xProfile)(void*,const char*,u64); /* Profiling function */ + void *pProfileArg; /* Argument to profile function */ +@@ -16884,6 +18100,9 @@ struct sqlite3 { + void (*xRollbackCallback)(void*); /* Invoked at every commit. */ + void *pUpdateArg; + void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); ++ void *pAutovacPagesArg; /* Client argument to autovac_pages */ ++ void (*xAutovacDestr)(void*); /* Destructor for pAutovacPAgesArg */ ++ unsigned int (*xAutovacPages)(void*,const char*,u32,u32,u32); + Parse *pParse; /* Current parse */ + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK + void *pPreUpdateArg; /* First argument to xPreUpdateCallback */ +@@ -16928,11 +18147,16 @@ struct sqlite3 { + Savepoint *pSavepoint; /* List of active savepoints */ + int nAnalysisLimit; /* Number of index rows to ANALYZE */ + int busyTimeout; /* Busy handler timeout, in msec */ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ int setlkTimeout; /* Blocking lock timeout, in msec. -1 -> inf. */ ++ int setlkFlags; /* Flags passed to setlk_timeout() */ ++#endif + int nSavepoint; /* Number of non-transaction savepoints */ + int nStatement; /* Number of nested statement-transactions */ + i64 nDeferredCons; /* Net deferred constraints this transaction. */ + i64 nDeferredImmCons; /* Net deferred immediate constraints */ + int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ ++ DbClientData *pDbData; /* sqlite3_set_clientdata() content */ + #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + /* The following variables are all protected by the STATIC_MAIN + ** mutex, not by sqlite3.mutex. They are used by code in notify.c. +@@ -16950,9 +18174,6 @@ struct sqlite3 { + void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ + sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ + #endif +-#ifdef SQLITE_USER_AUTHENTICATION +- sqlite3_userauth auth; /* User authentication information */ +-#endif + }; + + /* +@@ -16988,7 +18209,7 @@ struct sqlite3 { + #define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ + /* result set is empty */ + #define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */ +-#define SQLITE_ReadUncommit 0x00000400 /* READ UNCOMMITTED in shared-cache */ ++#define SQLITE_StmtScanStatus 0x00000400 /* Enable stmt_scanstats() counters */ + #define SQLITE_NoCkptOnClose 0x00000800 /* No checkpoint on close()/DETACH */ + #define SQLITE_ReverseOrder 0x00001000 /* Reverse unordered SELECTs */ + #define SQLITE_RecTriggers 0x00002000 /* Enable recursive triggers */ +@@ -17013,6 +18234,12 @@ struct sqlite3 { + #define SQLITE_CountRows HI(0x00001) /* Count rows changed by INSERT, */ + /* DELETE, or UPDATE and return */ + /* the count using a callback. */ ++#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ ++#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */ ++#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */ ++#define SQLITE_AttachCreate HI(0x00010) /* ATTACH allowed to create new dbs */ ++#define SQLITE_AttachWrite HI(0x00020) /* ATTACH allowed to open for write */ ++#define SQLITE_Comments HI(0x00040) /* Enable SQL comments */ + + /* Flags used only if debugging */ + #ifdef SQLITE_DEBUG +@@ -17040,24 +18267,40 @@ struct sqlite3 { + ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to + ** selectively disable various optimizations. + */ +-#define SQLITE_QueryFlattener 0x0001 /* Query flattening */ +-#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */ +-#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ +-#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ +-#define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */ +-#define SQLITE_CoverIdxScan 0x0020 /* Covering index scans */ +-#define SQLITE_OrderByIdxJoin 0x0040 /* ORDER BY of joins via index */ +-#define SQLITE_Transitive 0x0080 /* Transitive constraints */ +-#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */ +-#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */ +-#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */ +-#define SQLITE_Stat4 0x0800 /* Use STAT4 data */ +- /* TH3 expects the Stat4 ^^^^^^ value to be 0x0800. Don't change it */ +-#define SQLITE_PushDown 0x1000 /* The push-down optimization */ +-#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */ +-#define SQLITE_SkipScan 0x4000 /* Skip-scans */ +-#define SQLITE_PropagateConst 0x8000 /* The constant propagation opt */ +-#define SQLITE_AllOpts 0xffff /* All optimizations */ ++#define SQLITE_QueryFlattener 0x00000001 /* Query flattening */ ++#define SQLITE_WindowFunc 0x00000002 /* Use xInverse for window functions */ ++#define SQLITE_GroupByOrder 0x00000004 /* GROUPBY cover of ORDERBY */ ++#define SQLITE_FactorOutConst 0x00000008 /* Constant factoring */ ++#define SQLITE_DistinctOpt 0x00000010 /* DISTINCT using indexes */ ++#define SQLITE_CoverIdxScan 0x00000020 /* Covering index scans */ ++#define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */ ++#define SQLITE_Transitive 0x00000080 /* Transitive constraints */ ++#define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */ ++#define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */ ++#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */ ++#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */ ++ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */ ++#define SQLITE_PushDown 0x00001000 /* WHERE-clause push-down opt */ ++#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */ ++#define SQLITE_SkipScan 0x00004000 /* Skip-scans */ ++#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */ ++#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */ ++#define SQLITE_SeekScan 0x00020000 /* The OP_SeekScan optimization */ ++#define SQLITE_OmitOrderBy 0x00040000 /* Omit pointless ORDER BY */ ++ /* TH3 expects this value ^^^^^^^^^^ to be 0x40000. Coordinate any change */ ++#define SQLITE_BloomFilter 0x00080000 /* Use a Bloom filter on searches */ ++#define SQLITE_BloomPulldown 0x00100000 /* Run Bloom filters early */ ++#define SQLITE_BalancedMerge 0x00200000 /* Balance multi-way merges */ ++#define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */ ++#define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ ++ /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ ++#define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ ++#define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ ++#define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ ++#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ ++#define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */ ++#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */ ++#define SQLITE_AllOpts 0xffffffff /* All optimizations */ + + /* + ** Macros for testing whether or not optimizations are enabled or disabled. +@@ -17071,17 +18314,16 @@ struct sqlite3 { + */ + #define ConstFactorOk(P) ((P)->okConstFactor) + +-/* +-** Possible values for the sqlite.magic field. +-** The numbers are obtained at random and have no special meaning, other +-** than being distinct from one another. ++/* Possible values for the sqlite3.eOpenState field. ++** The numbers are randomly selected such that a minimum of three bits must ++** change to convert any number to another or to zero + */ +-#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ +-#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ +-#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ +-#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ +-#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ +-#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ ++#define SQLITE_STATE_OPEN 0x76 /* Database is open */ ++#define SQLITE_STATE_CLOSED 0xce /* Database is closed */ ++#define SQLITE_STATE_SICK 0xba /* Error and awaiting close */ ++#define SQLITE_STATE_BUSY 0x6d /* Database currently in use */ ++#define SQLITE_STATE_ERROR 0xd5 /* An SQLITE_MISUSE error occurred */ ++#define SQLITE_STATE_ZOMBIE 0xa7 /* Close with last statement close */ + + /* + ** Each SQL function is defined by an instance of the following +@@ -17094,7 +18336,7 @@ struct sqlite3 { + ** field is used by per-connection app-def functions. + */ + struct FuncDef { +- i8 nArg; /* Number of arguments. -1 means unlimited */ ++ i16 nArg; /* Number of arguments. -1 means unlimited */ + u32 funcFlags; /* Some combination of SQLITE_FUNC_* */ + void *pUserData; /* User data parameter */ + FuncDef *pNext; /* Next function with same name */ +@@ -17106,7 +18348,7 @@ struct FuncDef { + union { + FuncDef *pHash; /* Next with a different name but the same hash */ + FuncDestructor *pDestructor; /* Reference counted destructor function */ +- } u; ++ } u; /* pHash if SQLITE_FUNC_BUILTIN, pDestructor otherwise */ + }; + + /* +@@ -17136,13 +18378,21 @@ struct FuncDestructor { + ** are assert() statements in the code to verify this. + ** + ** Value constraints (enforced via assert()): +-** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg +-** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG +-** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG +-** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API +-** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API +-** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS ++** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg ++** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd ++** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG ++** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG ++** SQLITE_FUNC_BYTELEN == OPFLAG_BYTELENARG ++** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API ++** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API ++** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!! + ** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API ++** ++** Note that even though SQLITE_FUNC_UNSAFE and SQLITE_INNOCUOUS have the ++** same bit value, their meanings are inverted. SQLITE_FUNC_UNSAFE is ++** used internally and if set means that the function has side effects. ++** SQLITE_INNOCUOUS is used by application code and means "not unsafe". ++** See multiple instances of tag-20230109-1. + */ + #define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ + #define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */ +@@ -17151,6 +18401,7 @@ struct FuncDestructor { + #define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ + #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ + #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ ++#define SQLITE_FUNC_BYTELEN 0x00c0 /* Built-in octet_length() function */ + #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ + /* 0x0200 -- available for reuse */ + #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ +@@ -17159,13 +18410,16 @@ struct FuncDestructor { + #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a + ** single query - might change over time */ + #define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */ +-#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ ++#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */ + #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ + #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ + #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ +-#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */ ++/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */ + #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ + #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ ++#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ ++/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */ ++#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ + + /* Identifier numbers for each in-line function */ + #define INLINEFUNC_coalesce 0 +@@ -17174,6 +18428,7 @@ struct FuncDestructor { + #define INLINEFUNC_expr_compare 3 + #define INLINEFUNC_affinity 4 + #define INLINEFUNC_iif 5 ++#define INLINEFUNC_sqlite_offset 6 + #define INLINEFUNC_unlikely 99 /* Default case */ + + /* +@@ -17213,6 +18468,9 @@ struct FuncDestructor { + ** a single query. The iArg is ignored. The user-data is always set + ** to a NULL pointer. The bNC parameter is not used. + ** ++** MFUNCTION(zName, nArg, xPtr, xFunc) ++** For math-library functions. xPtr is an arbitrary pointer. ++** + ** PURE_DATE(zName, nArg, iArg, bNC, xFunc) + ** Used for "pure" date/time functions, this macro is like DFUNCTION + ** except that it does set the SQLITE_FUNC_CONSTANT flags. iArg is +@@ -17225,7 +18483,7 @@ struct FuncDestructor { + ** are interpreted in the same way as the first 4 parameters to + ** FUNCTION(). + ** +-** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse) ++** WAGGREGATE(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 +@@ -17240,41 +18498,56 @@ struct FuncDestructor { + ** parameter. + */ + #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ +- {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ ++ {nArg, SQLITE_FUNC_BUILTIN|\ ++ SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + 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), \ ++ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } + #define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \ +- {nArg, SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ ++ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } ++#define MFUNCTION(zName, nArg, xPtr, xFunc) \ ++ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ ++ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } ++#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \ ++ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ ++ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\ ++ ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ ++ SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} } + #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ +- {nArg, SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ ++ {nArg, SQLITE_FUNC_BUILTIN|\ ++ SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ + SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } + #define TEST_FUNC(zName, nArg, iArg, mFlags) \ +- {nArg, SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ ++ {nArg, SQLITE_FUNC_BUILTIN|\ ++ SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ + SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ + SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } + #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ +- {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ ++ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ + 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, \ ++ {nArg, SQLITE_FUNC_BUILTIN|\ ++ SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + (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,\ ++ {nArg, SQLITE_FUNC_BUILTIN|\ ++ SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ + 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), \ ++ {nArg, SQLITE_FUNC_BUILTIN|\ ++ SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + pArg, 0, xFunc, 0, 0, 0, #zName, } + #define LIKEFUNC(zName, nArg, arg, flags) \ +- {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ ++ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ + (void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} } + #define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \ +- {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ ++ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}} + #define INTERNAL_FUNCTION(zName, nArg, xFunc) \ +- {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ ++ {nArg, SQLITE_FUNC_BUILTIN|\ ++ SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + 0, 0, xFunc, 0, 0, 0, #zName, {0} } + + +@@ -17330,19 +18603,48 @@ struct Module { + ** or equal to the table column index. It is + ** equal if and only if there are no VIRTUAL + ** columns to the left. ++** ++** Notes on zCnName: ++** The zCnName field stores the name of the column, the datatype of the ++** column, and the collating sequence for the column, in that order, all in ++** a single allocation. Each string is 0x00 terminated. The datatype ++** is only included if the COLFLAG_HASTYPE bit of colFlags is set and the ++** collating sequence name is only included if the COLFLAG_HASCOLL bit is ++** set. + */ + struct Column { +- char *zName; /* Name of this column, \000, then the type */ +- Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */ +- char *zColl; /* Collating sequence. If NULL, use the default */ +- u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ +- char affinity; /* One of the SQLITE_AFF_... values */ +- u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ +- u8 hName; /* Column name hash for faster lookup */ +- u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ ++ char *zCnName; /* Name of this column */ ++ unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */ ++ unsigned eCType :4; /* One of the standard types */ ++ char affinity; /* One of the SQLITE_AFF_... values */ ++ u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */ ++ u8 hName; /* Column name hash for faster lookup */ ++ u16 iDflt; /* 1-based index of DEFAULT. 0 means "none" */ ++ u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ + }; + +-/* Allowed values for Column.colFlags: ++/* Allowed values for Column.eCType. ++** ++** Values must match entries in the global constant arrays ++** sqlite3StdTypeLen[] and sqlite3StdType[]. Each value is one more ++** than the offset into these arrays for the corresponding name. ++** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. ++*/ ++#define COLTYPE_CUSTOM 0 /* Type appended to zName */ ++#define COLTYPE_ANY 1 ++#define COLTYPE_BLOB 2 ++#define COLTYPE_INT 3 ++#define COLTYPE_INTEGER 4 ++#define COLTYPE_REAL 5 ++#define COLTYPE_TEXT 6 ++#define SQLITE_N_STDTYPE 6 /* Number of standard types */ ++ ++/* Allowed values for Column.colFlags. ++** ++** Constraints: ++** TF_HasVirtual == COLFLAG_VIRTUAL ++** TF_HasStored == COLFLAG_STORED ++** TF_HasHidden == COLFLAG_HIDDEN + */ + #define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ + #define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ +@@ -17353,6 +18655,8 @@ struct Column { + #define COLFLAG_STORED 0x0040 /* GENERATED ALWAYS AS ... STORED */ + #define COLFLAG_NOTAVAIL 0x0080 /* STORED column not yet calculated */ + #define COLFLAG_BUSY 0x0100 /* Blocks recursion on GENERATED columns */ ++#define COLFLAG_HASCOLL 0x0200 /* Has collating sequence name in zCnName */ ++#define COLFLAG_NOEXPAND 0x0400 /* Omit this column when expanding "*" */ + #define COLFLAG_GENERATED 0x0060 /* Combo: _STORED, _VIRTUAL */ + #define COLFLAG_NOINSERT 0x0062 /* Combo: _HIDDEN, _STORED, _VIRTUAL */ + +@@ -17400,6 +18704,8 @@ struct CollSeq { + #define SQLITE_AFF_NUMERIC 0x43 /* 'C' */ + #define SQLITE_AFF_INTEGER 0x44 /* 'D' */ + #define SQLITE_AFF_REAL 0x45 /* 'E' */ ++#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */ ++#define SQLITE_AFF_DEFER 0x58 /* 'X' - defer computation until later */ + + #define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) + +@@ -17418,9 +18724,7 @@ struct CollSeq { + ** operator is NULL. It is added to certain comparison operators to + ** prove that the operands are always NOT NULL. + */ +-#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */ + #define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */ +-#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */ + #define SQLITE_NULLEQ 0x80 /* NULL=NULL */ + #define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */ + +@@ -17472,6 +18776,7 @@ struct VTable { + sqlite3_vtab *pVtab; /* Pointer to vtab instance */ + int nRef; /* Number of pointers to this structure */ + u8 bConstraint; /* True if constraints are supported */ ++ u8 bAllSchemas; /* True if might use any attached schema */ + u8 eVtabRisk; /* Riskiness of allowing hacker access */ + int iSavepoint; /* Depth of the SAVEPOINT stack */ + VTable *pNext; /* Next in linked list (see above) */ +@@ -17484,15 +18789,13 @@ struct VTable { + #define SQLITE_VTABRISK_High 2 + + /* +-** The schema for each SQL table and view is represented in memory +-** by an instance of the following structure. ++** The schema for each SQL table, virtual table, and view is represented ++** in memory by an instance of the following structure. + */ + struct Table { + char *zName; /* Name of the table or view */ + Column *aCol; /* Information about each column */ + Index *pIndex; /* List of SQL indexes on this table. */ +- Select *pSelect; /* NULL for tables. Points to definition if a view. */ +- FKey *pFKey; /* Linked list of all foreign keys in this table */ + char *zColAff; /* String defining the affinity of each column */ + ExprList *pCheck; /* All CHECK constraints */ + /* ... also used as column name list in a VIEW */ +@@ -17508,17 +18811,26 @@ struct Table { + LogEst costMult; /* Cost multiplier for using this table */ + #endif + u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ +-#ifndef SQLITE_OMIT_ALTERTABLE +- int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ +-#endif +-#ifndef SQLITE_OMIT_VIRTUALTABLE +- int nModuleArg; /* Number of arguments to the module */ +- char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */ +- VTable *pVTable; /* List of VTable objects. */ +-#endif +- Trigger *pTrigger; /* List of triggers stored in pSchema */ ++ u8 eTabType; /* 0: normal, 1: virtual, 2: view */ ++ union { ++ struct { /* Used by ordinary tables: */ ++ int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ ++ FKey *pFKey; /* Linked list of all foreign keys in this table */ ++ ExprList *pDfltList; /* DEFAULT clauses on various columns. ++ ** Or the AS clause for generated columns. */ ++ } tab; ++ struct { /* Used by views: */ ++ Select *pSelect; /* View definition */ ++ } view; ++ struct { /* Used by virtual tables only: */ ++ int nArg; /* Number of arguments to the module */ ++ char **azArg; /* 0: module 1: schema 2: vtab name 3...: args */ ++ VTable *p; /* List of VTable objects. */ ++ } vtab; ++ } u; ++ Trigger *pTrigger; /* List of triggers on this object */ + Schema *pSchema; /* Schema that contains this table */ +- Table *pNextZombie; /* Next on the Parse.pZombieTab list */ ++ u8 aHx[16]; /* Column aHt[K%sizeof(aHt)] might have hash K */ + }; + + /* +@@ -17532,24 +18844,38 @@ struct Table { + ** + ** Constraints: + ** +-** TF_HasVirtual == COLFLAG_Virtual +-** TF_HasStored == COLFLAG_Stored +-*/ +-#define TF_Readonly 0x0001 /* Read-only system table */ +-#define TF_Ephemeral 0x0002 /* An ephemeral table */ +-#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */ +-#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */ +-#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */ +-#define TF_HasVirtual 0x0020 /* Has one or more VIRTUAL columns */ +-#define TF_HasStored 0x0040 /* Has one or more STORED columns */ +-#define TF_HasGenerated 0x0060 /* Combo: HasVirtual + HasStored */ +-#define TF_WithoutRowid 0x0080 /* No rowid. PRIMARY KEY is the key */ +-#define TF_StatsUsed 0x0100 /* Query planner decisions affected by +- ** Index.aiRowLogEst[] values */ +-#define TF_NoVisibleRowid 0x0200 /* No user-visible "rowid" column */ +-#define TF_OOOHidden 0x0400 /* Out-of-Order hidden columns */ +-#define TF_HasNotNull 0x0800 /* Contains NOT NULL constraints */ +-#define TF_Shadow 0x1000 /* True for a shadow table */ ++** TF_HasVirtual == COLFLAG_VIRTUAL ++** TF_HasStored == COLFLAG_STORED ++** TF_HasHidden == COLFLAG_HIDDEN ++*/ ++#define TF_Readonly 0x00000001 /* Read-only system table */ ++#define TF_HasHidden 0x00000002 /* Has one or more hidden columns */ ++#define TF_HasPrimaryKey 0x00000004 /* Table has a primary key */ ++#define TF_Autoincrement 0x00000008 /* Integer primary key is autoincrement */ ++#define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */ ++#define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */ ++#define TF_HasStored 0x00000040 /* Has one or more STORED columns */ ++#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ ++#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ ++#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */ ++#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ ++#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ ++#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ ++#define TF_Shadow 0x00001000 /* True for a shadow table */ ++#define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ ++#define TF_Ephemeral 0x00004000 /* An ephemeral table */ ++#define TF_Eponymous 0x00008000 /* An eponymous virtual table */ ++#define TF_Strict 0x00010000 /* STRICT mode */ ++ ++/* ++** Allowed values for Table.eTabType ++*/ ++#define TABTYP_NORM 0 /* Ordinary table */ ++#define TABTYP_VTAB 1 /* Virtual table */ ++#define TABTYP_VIEW 2 /* A view */ ++ ++#define IsView(X) ((X)->eTabType==TABTYP_VIEW) ++#define IsOrdinaryTable(X) ((X)->eTabType==TABTYP_NORM) + + /* + ** Test to see whether or not a table is a virtual table. This is +@@ -17557,9 +18883,9 @@ struct Table { + ** table support is omitted from the build. + */ + #ifndef SQLITE_OMIT_VIRTUALTABLE +-# define IsVirtual(X) ((X)->nModuleArg) ++# define IsVirtual(X) ((X)->eTabType==TABTYP_VTAB) + # define ExprIsVtab(X) \ +- ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) ++ ((X)->op==TK_COLUMN && (X)->y.pTab->eTabType==TABTYP_VTAB) + #else + # define IsVirtual(X) 0 + # define ExprIsVtab(X) 0 +@@ -17587,6 +18913,15 @@ struct Table { + #define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0) + #define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0) + ++/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is ++** available. By default, this macro is false ++*/ ++#ifndef SQLITE_ALLOW_ROWID_IN_VIEW ++# define ViewCanHaveRowid 0 ++#else ++# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0) ++#endif ++ + /* + ** Each foreign key constraint is an instance of the following structure. + ** +@@ -17629,9 +18964,13 @@ struct FKey { + struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ + int iFrom; /* Index of column in pFrom */ + char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ +- } aCol[1]; /* One entry for each of nCol columns */ ++ } aCol[FLEXARRAY]; /* One entry for each of nCol columns */ + }; + ++/* The size (in bytes) of an FKey object holding N columns. The answer ++** does NOT include space to hold the zTo name. */ ++#define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap)) ++ + /* + ** SQLite supports many different ways to resolve a constraint + ** error. ROLLBACK processing means that a constraint violation +@@ -17646,16 +18985,22 @@ struct FKey { + ** is returned. REPLACE means that preexisting database rows that caused + ** a UNIQUE constraint violation are removed so that the new insert or + ** update can proceed. Processing continues and no error is reported. ++** UPDATE applies to insert operations only and means that the insert ++** is omitted and the DO UPDATE clause of an upsert is run instead. + ** +-** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. ++** RESTRICT, SETNULL, SETDFLT, and CASCADE actions apply only to foreign keys. + ** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the + ** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign +-** key is set to NULL. CASCADE means that a DELETE or UPDATE of the ++** key is set to NULL. SETDFLT means that the foreign key is set ++** to its default value. CASCADE means that a DELETE or UPDATE of the + ** referenced table row is propagated into the row that holds the + ** foreign key. + ** ++** The OE_Default value is a place holder that means to use whatever ++** conflict resolution algorithm is required from context. ++** + ** The following symbolic values are used to record which type +-** of action to take. ++** of conflict resolution action to take. + */ + #define OE_None 0 /* There is no constraint to check */ + #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ +@@ -17687,9 +19032,12 @@ struct KeyInfo { + u16 nAllField; /* Total columns, including key plus others */ + sqlite3 *db; /* The database connection */ + u8 *aSortFlags; /* Sort order for each column. */ +- CollSeq *aColl[1]; /* Collating sequence for each term of the key */ ++ CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */ + }; + ++/* The size (in bytes) of a KeyInfo object with up to N fields */ ++#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*)) ++ + /* + ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. + */ +@@ -17734,6 +19082,11 @@ struct KeyInfo { + struct UnpackedRecord { + KeyInfo *pKeyInfo; /* Collation and sort-order information */ + Mem *aMem; /* Values */ ++ union { ++ char *z; /* Cache of aMem[0].z for vdbeRecordCompareString() */ ++ i64 i; /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */ ++ } u; ++ int n; /* Cache of aMem[0].n used by vdbeRecordCompareString() */ + u16 nField; /* Number of entries in apMem[] */ + i8 default_rc; /* Comparison result if keys are equal */ + u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */ +@@ -17765,10 +19118,22 @@ struct UnpackedRecord { + ** The Index.onError field determines whether or not the indexed columns + ** must be unique and what to do if they are not. When Index.onError=OE_None, + ** it means this is not a unique index. Otherwise it is a unique index +-** and the value of Index.onError indicate the which conflict resolution +-** algorithm to employ whenever an attempt is made to insert a non-unique ++** and the value of Index.onError indicates which conflict resolution ++** algorithm to employ when an attempt is made to insert a non-unique + ** element. + ** ++** The colNotIdxed bitmask is used in combination with SrcItem.colUsed ++** for a fast test to see if an index can serve as a covering index. ++** colNotIdxed has a 1 bit for every column of the original table that ++** is *not* available in the index. Thus the expression ++** "colUsed & colNotIdxed" will be non-zero if the index is not a ++** covering index. The most significant bit of of colNotIdxed will always ++** be true (note-20221022-a). If a column beyond the 63rd column of the ++** table is used, the "colUsed & colNotIdxed" test will always be non-zero ++** and we have to assume either that the index is not covering, or use ++** an alternative (slower) algorithm to determine whether or not ++** the index is covering. ++** + ** While parsing a CREATE TABLE or CREATE INDEX statement in order to + ** generate VDBE code (as opposed to parsing one read from an sqlite_schema + ** table as part of parsing an existing database schema), transient instances +@@ -17792,7 +19157,7 @@ struct Index { + Pgno tnum; /* DB Page containing root of this index */ + LogEst szIdxRow; /* Estimated average row size in bytes */ + u16 nKeyCol; /* Number of columns forming the key */ +- u16 nColumn; /* Number of columns stored in the index */ ++ u16 nColumn; /* Nr columns in btree. Can be 2*Table.nCol */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ + unsigned bUnordered:1; /* Use this index for == or IN queries only */ +@@ -17804,15 +19169,18 @@ struct Index { + unsigned bNoQuery:1; /* Do not use this index to optimize queries */ + unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ + unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ ++ unsigned bHasExpr:1; /* Index contains an expression, either a literal ++ ** expression, or a reference to a VIRTUAL column */ + #ifdef SQLITE_ENABLE_STAT4 + int nSample; /* Number of elements in aSample[] */ ++ int mxSample; /* Number of slots allocated to aSample[] */ + int nSampleCol; /* Size of IndexSample.anEq[] and so on */ + tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ + IndexSample *aSample; /* Samples of the left-most key */ + tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ + tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ + #endif +- Bitmask colNotIdxed; /* 0 for unindexed columns in pTab */ ++ Bitmask colNotIdxed; /* Unindexed columns in pTab */ + }; + + /* +@@ -17887,18 +19255,17 @@ struct AggInfo { + ** from source tables rather than from accumulators */ + u8 useSortingIdx; /* In direct mode, reference the sorting index rather + ** than the source table */ ++ u32 nSortingColumn; /* Number of columns in the sorting index */ + int sortingIdx; /* Cursor number of the sorting index */ + int sortingIdxPTab; /* Cursor number of pseudo-table */ +- int nSortingColumn; /* Number of columns in the sorting index */ +- int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ ++ int iFirstReg; /* First register in range for aCol[] and aFunc[] */ + ExprList *pGroupBy; /* The group by clause */ + struct AggInfo_col { /* For each column used in source tables */ + Table *pTab; /* Source table */ + Expr *pCExpr; /* The original expression */ + int iTable; /* Cursor number of the source table */ +- int iMem; /* Memory location that acts as accumulator */ +- i16 iColumn; /* Column number within the source table */ +- i16 iSorterColumn; /* Column number in the sorting index */ ++ int iColumn; /* Column number within the source table */ ++ int iSorterColumn; /* Column number in the sorting index */ + } *aCol; + int nColumn; /* Number of used entries in aCol[] */ + int nAccumulator; /* Number of columns that show through to the output. +@@ -17907,14 +19274,37 @@ struct AggInfo { + struct AggInfo_func { /* For each aggregate function */ + Expr *pFExpr; /* Expression encoding the function */ + FuncDef *pFunc; /* The aggregate function implementation */ +- int iMem; /* Memory location that acts as accumulator */ + int iDistinct; /* Ephemeral table used to enforce DISTINCT */ ++ int iDistAddr; /* Address of OP_OpenEphemeral */ ++ int iOBTab; /* Ephemeral table to implement ORDER BY */ ++ u8 bOBPayload; /* iOBTab has payload columns separate from key */ ++ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */ ++ u8 bUseSubtype; /* Transfer subtype info through sorter */ + } *aFunc; + int nFunc; /* Number of entries in aFunc[] */ + u32 selId; /* Select to which this AggInfo belongs */ +- AggInfo *pNext; /* Next in list of them all */ ++#ifdef SQLITE_DEBUG ++ Select *pSelect; /* SELECT statement that this AggInfo supports */ ++#endif + }; + ++/* ++** Macros to compute aCol[] and aFunc[] register numbers. ++** ++** These macros should not be used prior to the call to ++** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg. ++** The assert()s that are part of this macro verify that constraint. ++*/ ++#ifndef NDEBUG ++#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I)) ++#define AggInfoFuncReg(A,I) \ ++ (assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I)) ++#else ++#define AggInfoColumnReg(A,I) ((A)->iFirstReg+(I)) ++#define AggInfoFuncReg(A,I) \ ++ ((A)->iFirstReg+(A)->nColumn+(I)) ++#endif ++ + /* + ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. + ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater +@@ -17942,10 +19332,10 @@ typedef int ynVar; + ** tree. + ** + ** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, +-** or TK_STRING), then Expr.token contains the text of the SQL literal. If +-** the expression is a variable (TK_VARIABLE), then Expr.token contains the ++** or TK_STRING), then Expr.u.zToken contains the text of the SQL literal. If ++** the expression is a variable (TK_VARIABLE), then Expr.u.zToken contains the + ** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), +-** then Expr.token contains the name of the function. ++** then Expr.u.zToken contains the name of the function. + ** + ** Expr.pRight and Expr.pLeft are the left and right subexpressions of a + ** binary operator. Either or both may be NULL. +@@ -17985,7 +19375,7 @@ typedef int ynVar; + ** help reduce memory requirements, sometimes an Expr object will be + ** truncated. And to reduce the number of memory allocations, sometimes + ** two or more Expr objects will be stored in a single memory allocation, +-** together with Expr.zToken strings. ++** together with Expr.u.zToken strings. + ** + ** If the EP_Reduced and EP_TokenOnly flags are set when + ** an Expr object is truncated. When EP_Reduced is set, then all +@@ -18034,19 +19424,23 @@ struct Expr { + ** TK_REGISTER: register number + ** TK_TRIGGER: 1 -> new, 0 -> old + ** EP_Unlikely: 134217728 times likelihood +- ** TK_IN: ephemerial table holding RHS ++ ** TK_IN: ephemeral table holding RHS + ** TK_SELECT_COLUMN: Number of columns on the LHS + ** TK_SELECT: 1st register of result vector */ + ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. + ** TK_VARIABLE: variable number (always >= 1). + ** TK_SELECT_COLUMN: column of the result vector */ + i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ +- i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ ++ union { ++ int iJoin; /* If EP_OuterON or EP_InnerON, the right table */ ++ int iOfst; /* else: start of token from start of statement */ ++ } w; + AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ + union { + Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL + ** for a column of an index on an expression */ + Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */ ++ int nReg; /* TK_NULLS: Number of registers to NULL out */ + struct { /* TK_IN, TK_SELECT, and TK_EXISTS */ + int iAddr; /* Subroutine entry address */ + int regReturn; /* Register used to hold return address */ +@@ -18054,36 +19448,35 @@ struct Expr { + } y; + }; + +-/* +-** The following are the meanings of bits in the Expr.flags field. ++/* The following are the meanings of bits in the Expr.flags field. + ** Value restrictions: + ** + ** EP_Agg == NC_HasAgg == SF_HasAgg + ** EP_Win == NC_HasWin + */ +-#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ +-#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */ +-#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */ +-#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */ ++#define EP_OuterON 0x000001 /* Originates in ON/USING clause of outer join */ ++#define EP_InnerON 0x000002 /* Originates in ON/USING of an inner join */ ++#define EP_Distinct 0x000004 /* Aggregate function with DISTINCT keyword */ ++#define EP_HasFunc 0x000008 /* Contains one or more functions of any kind */ + #define EP_Agg 0x000010 /* Contains one or more aggregate functions */ +-#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ +-#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ +-#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ +-#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */ +-#define EP_Commuted 0x000200 /* Comparison operator has been commuted */ +-#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ +-#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */ +-#define EP_Skip 0x001000 /* Operator does not contribute to affinity */ +-#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ +-#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ ++#define EP_FixedCol 0x000020 /* TK_Column with a known fixed value */ ++#define EP_VarSelect 0x000040 /* pSelect is correlated, not constant */ ++#define EP_DblQuoted 0x000080 /* token.z was originally in "..." */ ++#define EP_InfixFunc 0x000100 /* True for an infix function: LIKE, GLOB, etc */ ++#define EP_Collate 0x000200 /* Tree contains a TK_COLLATE operator */ ++#define EP_Commuted 0x000400 /* Comparison operator has been commuted */ ++#define EP_IntValue 0x000800 /* Integer value contained in u.iValue */ ++#define EP_xIsSelect 0x001000 /* x.pSelect is valid (otherwise x.pList is) */ ++#define EP_Skip 0x002000 /* Operator does not contribute to affinity */ ++#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ + #define EP_Win 0x008000 /* Contains window functions */ +-#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ +- /* 0x020000 // available for reuse */ +-#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ +-#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ +-#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ +-#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ +-#define EP_Alias 0x400000 /* Is an alias for a result set column */ ++#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ ++#define EP_FullSize 0x020000 /* Expr structure must remain full sized */ ++#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */ ++#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */ ++#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ ++#define EP_CanBeNull 0x200000 /* Can be null despite NOT NULL constraint */ ++#define EP_Subquery 0x400000 /* Tree contains a TK_SELECT operator */ + #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ + #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ + #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ +@@ -18092,25 +19485,36 @@ struct Expr { + #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ + #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ + #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ +- /* 0x80000000 // Available */ ++#define EP_SubtArg 0x80000000 /* Is argument to SQLITE_SUBTYPE function */ + +-/* +-** The EP_Propagate mask is a set of properties that automatically propagate ++/* The EP_Propagate mask is a set of properties that automatically propagate + ** upwards into parent nodes. + */ + #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) + +-/* +-** These macros can be used to test, set, or clear bits in the ++/* Macros can be used to test, set, or clear bits in the + ** Expr.flags field. + */ +-#define ExprHasProperty(E,P) (((E)->flags&(P))!=0) +-#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) +-#define ExprSetProperty(E,P) (E)->flags|=(P) +-#define ExprClearProperty(E,P) (E)->flags&=~(P) +-#define ExprAlwaysTrue(E) (((E)->flags&(EP_FromJoin|EP_IsTrue))==EP_IsTrue) +-#define ExprAlwaysFalse(E) (((E)->flags&(EP_FromJoin|EP_IsFalse))==EP_IsFalse) +- ++#define ExprHasProperty(E,P) (((E)->flags&(u32)(P))!=0) ++#define ExprHasAllProperty(E,P) (((E)->flags&(u32)(P))==(u32)(P)) ++#define ExprSetProperty(E,P) (E)->flags|=(u32)(P) ++#define ExprClearProperty(E,P) (E)->flags&=~(u32)(P) ++#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) ++#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) ++#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0) ++ ++/* Macros used to ensure that the correct members of unions are accessed ++** in Expr. ++*/ ++#define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0) ++#define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0) ++#define ExprUseWOfst(E) (((E)->flags&(EP_InnerON|EP_OuterON))==0) ++#define ExprUseWJoin(E) (((E)->flags&(EP_InnerON|EP_OuterON))!=0) ++#define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0) ++#define ExprUseXSelect(E) (((E)->flags&EP_xIsSelect)!=0) ++#define ExprUseYTab(E) (((E)->flags&(EP_WinFunc|EP_Subrtn))==0) ++#define ExprUseYWin(E) (((E)->flags&EP_WinFunc)!=0) ++#define ExprUseYSub(E) (((E)->flags&EP_Subrtn)!=0) + + /* Flags for use with Expr.vvaFlags + */ +@@ -18182,31 +19586,45 @@ struct Expr { + */ + struct ExprList { + int nExpr; /* Number of expressions on the list */ ++ int nAlloc; /* Number of a[] slots allocated */ + struct ExprList_item { /* For each expression in the list */ + Expr *pExpr; /* The parse tree for this expression */ + char *zEName; /* Token associated with this expression */ +- u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ +- unsigned eEName :2; /* Meaning of zEName */ +- unsigned done :1; /* A flag to indicate when processing is finished */ +- unsigned reusable :1; /* Constant expression is reusable */ +- unsigned bSorterRef :1; /* Defer evaluation until after sorting */ +- unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */ ++ struct { ++ u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ ++ unsigned eEName :2; /* Meaning of zEName */ ++ unsigned done :1; /* Indicates when processing is finished */ ++ unsigned reusable :1; /* Constant expression is reusable */ ++ unsigned bSorterRef :1; /* Defer evaluation until after sorting */ ++ unsigned bNulls :1; /* True if explicit "NULLS FIRST/LAST" */ ++ unsigned bUsed :1; /* This column used in a SF_NestedFrom subquery */ ++ unsigned bUsingTerm:1; /* Term from the USING clause of a NestedFrom */ ++ unsigned bNoExpand: 1; /* Term is an auxiliary in NestedFrom and should ++ ** not be expanded by "*" in parent queries */ ++ } fg; + union { +- struct { ++ struct { /* Used by any ExprList other than Parse.pConsExpr */ + u16 iOrderByCol; /* For ORDER BY, column number in result set */ + u16 iAlias; /* Index into Parse.aAlias[] for zName */ + } x; +- int iConstExprReg; /* Register in which Expr value is cached */ ++ int iConstExprReg; /* Register in which Expr value is cached. Used only ++ ** by Parse.pConstExpr */ + } u; +- } a[1]; /* One slot for each expression in the list */ ++ } a[FLEXARRAY]; /* One slot for each expression in the list */ + }; + ++/* The size (in bytes) of an ExprList object that is big enough to hold ++** as many as N expressions. */ ++#define SZ_EXPRLIST(N) \ ++ (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item)) ++ + /* + ** Allowed values for Expr.a.eEName + */ + #define ENAME_NAME 0 /* The AS clause of a result set */ + #define ENAME_SPAN 1 /* Complete text of the result set expression */ + #define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */ ++#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */ + + /* + ** An instance of this structure can hold a simple list of identifiers, +@@ -18224,23 +19642,36 @@ struct ExprList { + ** If "a" is the k-th column of table "t", then IdList.a[0].idx==k. + */ + struct IdList { ++ int nId; /* Number of identifiers on the list */ + struct IdList_item { + char *zName; /* Name of the identifier */ +- int idx; /* Index in some Table.aCol[] of a column named zName */ +- } *a; +- int nId; /* Number of identifiers on the list */ ++ } a[FLEXARRAY]; + }; + ++/* The size (in bytes) of an IdList object that can hold up to N IDs. */ ++#define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item)) ++ + /* +-** The following structure describes the FROM clause of a SELECT statement. +-** Each table or subquery in the FROM clause is a separate element of +-** the SrcList.a[] array. +-** +-** With the addition of multiple database support, the following structure +-** can also be used to describe a particular table such as the table that +-** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL, +-** such a table must be a simple name: ID. But in SQLite, the table can +-** now be identified by a database name, a dot, then the table name: ID.ID. ++** Allowed values for IdList.eType, which determines which value of the a.u4 ++** is valid. ++*/ ++#define EU4_NONE 0 /* Does not use IdList.a.u4 */ ++#define EU4_IDX 1 /* Uses IdList.a.u4.idx */ ++#define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */ ++ ++/* ++** Details of the implementation of a subquery. ++*/ ++struct Subquery { ++ Select *pSelect; /* A SELECT statement used in place of a table name */ ++ int addrFillSub; /* Address of subroutine to initialize a subquery */ ++ int regReturn; /* Register holding return address of addrFillSub */ ++ int regResult; /* Registers holding results of a co-routine */ ++}; ++ ++/* ++** The SrcItem object represents a single term in the FROM clause of a query. ++** The SrcList object is mostly an array of SrcItems. + ** + ** The jointype starts out showing the join type between the current table + ** and the next table on the list. The parser builds the list this way. +@@ -18249,53 +19680,121 @@ struct IdList { + ** + ** In the colUsed field, the high-order bit (bit 63) is set if the table + ** contains more than 63 columns and the 64-th or later column is used. +-*/ +-struct SrcList { +- int nSrc; /* Number of tables or subqueries in the FROM clause */ +- u32 nAlloc; /* Number of entries allocated in a[] below */ +- struct SrcList_item { ++** ++** Aggressive use of "union" helps keep the size of the object small. This ++** has been shown to boost performance, in addition to saving memory. ++** Access to union elements is gated by the following rules which should ++** always be checked, either by an if-statement or by an assert(). ++** ++** Field Only access if this is true ++** --------------- ----------------------------------- ++** u1.zIndexedBy fg.isIndexedBy ++** u1.pFuncArg fg.isTabFunc ++** u1.nRow !fg.isTabFunc && !fg.isIndexedBy ++** ++** u2.pIBIndex fg.isIndexedBy ++** u2.pCteUse fg.isCte ++** ++** u3.pOn !fg.isUsing ++** u3.pUsing fg.isUsing ++** ++** u4.zDatabase !fg.fixedSchema && !fg.isSubquery ++** u4.pSchema fg.fixedSchema ++** u4.pSubq fg.isSubquery ++** ++** See also the sqlite3SrcListDelete() routine for assert() statements that ++** check invariants on the fields of this object, especially the flags ++** inside the fg struct. ++*/ ++struct SrcItem { ++ char *zName; /* Name of the table */ ++ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ ++ Table *pSTab; /* Table object for zName. Mnemonic: Srcitem-TABle */ ++ struct { ++ u8 jointype; /* Type of join between this table and the previous */ ++ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ ++ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ ++ unsigned isSubquery :1; /* True if this term is a subquery */ ++ unsigned isTabFunc :1; /* True if table-valued-function syntax */ ++ unsigned isCorrelated :1; /* True if sub-query is correlated */ ++ unsigned isMaterialized:1; /* This is a materialized view */ ++ unsigned viaCoroutine :1; /* Implemented as a co-routine */ ++ unsigned isRecursive :1; /* True for recursive reference in WITH */ ++ unsigned fromDDL :1; /* Comes from sqlite_schema */ ++ unsigned isCte :1; /* This is a CTE */ ++ unsigned notCte :1; /* This item may not match a CTE */ ++ unsigned isUsing :1; /* u3.pUsing is valid */ ++ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ ++ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ ++ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ ++ unsigned rowidUsed :1; /* The ROWID of this table is referenced */ ++ unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */ ++ unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */ ++ } fg; ++ int iCursor; /* The VDBE cursor number used to access this table */ ++ Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ ++ union { ++ char *zIndexedBy; /* Identifier from "INDEXED BY " clause */ ++ ExprList *pFuncArg; /* Arguments to table-valued-function */ ++ u32 nRow; /* Number of rows in a VALUES clause */ ++ } u1; ++ union { ++ Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ ++ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ ++ } u2; ++ union { ++ Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ ++ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ ++ } u3; ++ union { + Schema *pSchema; /* Schema to which this item is fixed */ + char *zDatabase; /* Name of database holding this table */ +- char *zName; /* Name of the table */ +- char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ +- Table *pTab; /* An SQL table corresponding to zName */ +- Select *pSelect; /* A SELECT statement used in place of a table name */ +- int addrFillSub; /* Address of subroutine to manifest a subquery */ +- int regReturn; /* Register holding return address of addrFillSub */ +- int regResult; /* Registers holding results of a co-routine */ +- struct { +- u8 jointype; /* Type of join between this table and the previous */ +- unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ +- unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ +- unsigned isTabFunc :1; /* True if table-valued-function syntax */ +- unsigned isCorrelated :1; /* True if sub-query is correlated */ +- unsigned viaCoroutine :1; /* Implemented as a co-routine */ +- unsigned isRecursive :1; /* True for recursive reference in WITH */ +- unsigned fromDDL :1; /* Comes from sqlite_schema */ +- } fg; +- int iCursor; /* The VDBE cursor number used to access this table */ +- Expr *pOn; /* The ON clause of a join */ +- IdList *pUsing; /* The USING clause of a join */ +- Bitmask colUsed; /* Bit N (1<" clause */ +- ExprList *pFuncArg; /* Arguments to table-valued-function */ +- } u1; +- Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ +- } a[1]; /* One entry for each identifier on the list */ ++ Subquery *pSubq; /* Description of a subquery */ ++ } u4; + }; + + /* +-** Permitted values of the SrcList.a.jointype field ++** The OnOrUsing object represents either an ON clause or a USING clause. ++** It can never be both at the same time, but it can be neither. ++*/ ++struct OnOrUsing { ++ Expr *pOn; /* The ON clause of a join */ ++ IdList *pUsing; /* The USING clause of a join */ ++}; ++ ++/* ++** This object represents one or more tables that are the source of ++** content for an SQL statement. For example, a single SrcList object ++** is used to hold the FROM clause of a SELECT statement. SrcList also ++** represents the target tables for DELETE, INSERT, and UPDATE statements. ++** + */ +-#define JT_INNER 0x0001 /* Any kind of inner or cross join */ +-#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */ +-#define JT_NATURAL 0x0004 /* True for a "natural" join */ +-#define JT_LEFT 0x0008 /* Left outer join */ +-#define JT_RIGHT 0x0010 /* Right outer join */ +-#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */ +-#define JT_ERROR 0x0040 /* unknown or unsupported join type */ ++struct SrcList { ++ int nSrc; /* Number of tables or subqueries in the FROM clause */ ++ u32 nAlloc; /* Number of entries allocated in a[] below */ ++ SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */ ++}; + ++/* Size (in bytes) of a SrcList object that can hold as many as N ++** SrcItem objects. */ ++#define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem)) ++ ++/* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a ++** special case of SZ_SRCITEM(1) that comes up often. */ ++#define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem)) ++ ++/* ++** Permitted values of the SrcList.a.jointype field ++*/ ++#define JT_INNER 0x01 /* Any kind of inner or cross join */ ++#define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */ ++#define JT_NATURAL 0x04 /* True for a "natural" join */ ++#define JT_LEFT 0x08 /* Left outer join */ ++#define JT_RIGHT 0x10 /* Right outer join */ ++#define JT_OUTER 0x20 /* The "OUTER" keyword is present */ ++#define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN ++ ** Mnemonic: Left Table Of Right Join */ ++#define JT_ERROR 0x80 /* unknown or unsupported join type */ + + /* + ** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin() +@@ -18316,10 +19815,10 @@ struct SrcList { + #define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */ + #define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */ + #define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */ +-#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */ ++#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */ + #define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */ +-#define WHERE_SEEK_UNIQ_TABLE 0x1000 /* Do not defer seeks if unique */ +- /* 0x2000 not currently used */ ++#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */ ++#define WHERE_KEEP_ALL_JOINS 0x2000 /* Do not do the omit-noop-join opt */ + #define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */ + /* 0x8000 not currently used */ + +@@ -18358,11 +19857,13 @@ struct NameContext { + ExprList *pEList; /* Optional list of result-set columns */ + AggInfo *pAggInfo; /* Information about aggregates at this level */ + Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */ ++ int iBaseReg; /* For TK_REGISTER when parsing RETURNING */ + } uNC; + NameContext *pNext; /* Next outer name context. NULL for outermost */ + int nRef; /* Number of names resolved by this context */ +- int nErr; /* Number of errors encountered while resolving names */ ++ int nNcErr; /* Number of errors encountered while resolving names */ + int ncFlags; /* Zero or more NC_* flags defined below */ ++ u32 nNestedSelect; /* Number of nested selects using this NC */ + Select *pWinSelect; /* SELECT statement for any window functions */ + }; + +@@ -18370,29 +19871,34 @@ struct NameContext { + ** Allowed values for the NameContext, ncFlags field. + ** + ** Value constraints (all checked via assert()): +-** NC_HasAgg == SF_HasAgg == EP_Agg +-** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX ++** NC_HasAgg == SF_HasAgg == EP_Agg ++** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX ++** NC_OrderAgg == SF_OrderByReqd == SQLITE_FUNC_ANYORDER + ** NC_HasWin == EP_Win + ** + */ +-#define NC_AllowAgg 0x00001 /* Aggregate functions are allowed here */ +-#define NC_PartIdx 0x00002 /* True if resolving a partial index WHERE */ +-#define NC_IsCheck 0x00004 /* True if resolving a CHECK constraint */ +-#define NC_GenCol 0x00008 /* True for a GENERATED ALWAYS AS clause */ +-#define NC_HasAgg 0x00010 /* One or more aggregate functions seen */ +-#define NC_IdxExpr 0x00020 /* True if resolving columns of CREATE INDEX */ +-#define NC_SelfRef 0x0002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ +-#define NC_VarSelect 0x00040 /* A correlated subquery has been seen */ +-#define NC_UEList 0x00080 /* True if uNC.pEList is used */ +-#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */ +-#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */ +-#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */ +-#define NC_Complex 0x02000 /* True if a function or subquery seen */ +-#define NC_AllowWin 0x04000 /* Window functions are allowed here */ +-#define NC_HasWin 0x08000 /* One or more window functions seen */ +-#define NC_IsDDL 0x10000 /* Resolving names in a CREATE statement */ +-#define NC_InAggFunc 0x20000 /* True if analyzing arguments to an agg func */ +-#define NC_FromDDL 0x40000 /* SQL text comes from sqlite_schema */ ++#define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */ ++#define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */ ++#define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */ ++#define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */ ++#define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ ++#define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ ++#define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ ++#define NC_Subquery 0x000040 /* A subquery has been seen */ ++#define NC_UEList 0x000080 /* True if uNC.pEList is used */ ++#define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */ ++#define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ ++#define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */ ++#define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */ ++/* 0x002000 // available for reuse */ ++#define NC_AllowWin 0x004000 /* Window functions are allowed here */ ++#define NC_HasWin 0x008000 /* One or more window functions seen */ ++#define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */ ++#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */ ++#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */ ++#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */ ++#define NC_Where 0x100000 /* Processing WHERE clause of a SELECT */ ++#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ + + /* + ** An instance of the following object describes a single ON CONFLICT +@@ -18409,15 +19915,22 @@ struct NameContext { + ** WHERE clause is omitted. + */ + struct Upsert { +- ExprList *pUpsertTarget; /* Optional description of conflicting index */ ++ ExprList *pUpsertTarget; /* Optional description of conflict target */ + Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */ + ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ + Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ +- /* The fields above comprise the parse tree for the upsert clause. +- ** The fields below are used to transfer information from the INSERT +- ** processing down into the UPDATE processing while generating code. +- ** Upsert owns the memory allocated above, but not the memory below. */ +- Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */ ++ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ ++ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ ++ u8 isDup; /* True if 2nd or later with same pUpsertIdx */ ++ /* Above this point is the parse tree for the ON CONFLICT clauses. ++ ** The next group of fields stores intermediate data. */ ++ void *pToFree; /* Free memory when deleting the Upsert object */ ++ /* All fields above are owned by the Upsert object and must be freed ++ ** when the Upsert is destroyed. The fields below are used to transfer ++ ** information from the INSERT processing down into the UPDATE processing ++ ** while generating code. The fields below are owned by the INSERT ++ ** statement and will be freed by INSERT processing. */ ++ Index *pUpsertIdx; /* UNIQUE constraint specified by pUpsertTarget */ + SrcList *pUpsertSrc; /* Table to be updated */ + int regData; /* First register holding array of VALUES */ + int iDataCur; /* Index of the data cursor */ +@@ -18469,9 +19982,10 @@ struct Select { + ** "Select Flag". + ** + ** Value constraints (all checked via assert()) +-** SF_HasAgg == NC_HasAgg +-** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX +-** SF_FixedLimit == WHERE_USE_LIMIT ++** SF_HasAgg == NC_HasAgg ++** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX ++** SF_OrderByReqd == NC_OrderAgg == SQLITE_FUNC_ANYORDER ++** SF_FixedLimit == WHERE_USE_LIMIT + */ + #define SF_Distinct 0x0000001 /* Output should be DISTINCT */ + #define SF_All 0x0000002 /* Includes the ALL keyword */ +@@ -18496,7 +20010,18 @@ struct Select { + #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ + #define SF_View 0x0200000 /* SELECT statement is a view */ + #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ +-#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ ++#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ ++#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ ++#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ ++#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ ++#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ ++#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ ++#define SF_Correlated 0x20000000 /* True if references the outer context */ ++ ++/* True if SrcItem X is a subquery that has SF_NestedFrom */ ++#define IsNestedFrom(X) \ ++ ((X)->fg.isSubquery && \ ++ ((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0) + + /* + ** The results of a SELECT can be distributed in several ways, as defined +@@ -18515,9 +20040,6 @@ struct Select { + ** statements within triggers whose only purpose is + ** the side-effects of functions. + ** +-** All of the above are free to ignore their ORDER BY clause. Those that +-** follow must honor the ORDER BY clause. +-** + ** SRT_Output Generate a row of output (using the OP_ResultRow + ** opcode) for each row in the result set. + ** +@@ -18529,7 +20051,11 @@ struct Select { + ** SRT_Set The result must be a single column. Store each + ** row of result as the key in table pDest->iSDParm. + ** Apply the affinity pDest->affSdst before storing +-** results. Used to implement "IN (SELECT ...)". ++** results. if pDest->iSDParm2 is positive, then it is ++** a register holding a Bloom filter for the IN operator ++** that should be populated in addition to the ++** pDest->iSDParm table. This SRT is used to ++** implement "IN (SELECT ...)". + ** + ** SRT_EphemTab Create an temporary table pDest->iSDParm and store + ** the result there. The cursor is left open after +@@ -18574,13 +20100,18 @@ struct Select { + #define SRT_Except 2 /* Remove result from a UNION index */ + #define SRT_Exists 3 /* Store 1 if the result is not empty */ + #define SRT_Discard 4 /* Do not save the results anywhere */ +-#define SRT_Fifo 5 /* Store result as data with an automatic rowid */ +-#define SRT_DistFifo 6 /* Like SRT_Fifo, but unique results only */ ++#define SRT_DistFifo 5 /* Like SRT_Fifo, but unique results only */ ++#define SRT_DistQueue 6 /* Like SRT_Queue, but unique results only */ ++ ++/* The DISTINCT clause is ignored for all of the above. Not that ++** IgnorableDistinct() implies IgnorableOrderby() */ ++#define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue) ++ + #define SRT_Queue 7 /* Store result in an queue */ +-#define SRT_DistQueue 8 /* Like SRT_Queue, but unique results only */ ++#define SRT_Fifo 8 /* Store result as data with an automatic rowid */ + + /* The ORDER BY clause is ignored for all of the above */ +-#define IgnorableOrderby(X) ((X->eDest)<=SRT_DistQueue) ++#define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo) + + #define SRT_Output 9 /* Output each row of result */ + #define SRT_Mem 10 /* Store result in a memory cell */ +@@ -18600,7 +20131,7 @@ struct SelectDest { + int iSDParm2; /* A second parameter for the eDest disposal method */ + int iSdst; /* Base register where results are written */ + int nSdst; /* Number of registers allocated */ +- char *zAffSdst; /* Affinity used when eDest==SRT_Set */ ++ char *zAffSdst; /* Affinity used for SRT_Set */ + ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */ + }; + +@@ -18659,11 +20190,45 @@ struct TriggerPrg { + #else + typedef unsigned int yDbMask; + # define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0) +-# define DbMaskZero(M) (M)=0 +-# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I)) +-# define DbMaskAllZero(M) (M)==0 +-# define DbMaskNonZero(M) (M)!=0 ++# define DbMaskZero(M) ((M)=0) ++# define DbMaskSet(M,I) ((M)|=(((yDbMask)1)<<(I))) ++# define DbMaskAllZero(M) ((M)==0) ++# define DbMaskNonZero(M) ((M)!=0) ++#endif ++ ++/* ++** For each index X that has as one of its arguments either an expression ++** or the name of a virtual generated column, and if X is in scope such that ++** the value of the expression can simply be read from the index, then ++** there is an instance of this object on the Parse.pIdxExpr list. ++** ++** During code generation, while generating code to evaluate expressions, ++** this list is consulted and if a matching expression is found, the value ++** is read from the index rather than being recomputed. ++*/ ++struct IndexedExpr { ++ Expr *pExpr; /* The expression contained in the index */ ++ int iDataCur; /* The data cursor associated with the index */ ++ int iIdxCur; /* The index cursor */ ++ int iIdxCol; /* The index column that contains value of pExpr */ ++ u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */ ++ u8 aff; /* Affinity of the pExpr expression */ ++ IndexedExpr *pIENext; /* Next in a list of all indexed expressions */ ++#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS ++ const char *zIdxName; /* Name of index, used only for bytecode comments */ + #endif ++}; ++ ++/* ++** An instance of the ParseCleanup object specifies an operation that ++** should be performed after parsing to deallocation resources obtained ++** during the parse and which are no longer needed. ++*/ ++struct ParseCleanup { ++ ParseCleanup *pNext; /* Next cleanup task */ ++ void *pPtr; /* Pointer to object to deallocate */ ++ void (*xCleanup)(sqlite3*,void*); /* Deallocation routine */ ++}; + + /* + ** An SQL parser context. A copy of this structure is passed through +@@ -18686,16 +20251,32 @@ struct Parse { + char *zErrMsg; /* An error message */ + Vdbe *pVdbe; /* An engine for executing database bytecode */ + int rc; /* Return code from execution */ +- u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */ +- u8 checkSchema; /* Causes schema cookie check after an error */ ++ LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ + u8 nested; /* Number of nested calls to the parser/code generator */ + u8 nTempReg; /* Number of temporary registers in aTempReg[] */ + u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ + u8 mayAbort; /* True if statement may throw an ABORT exception */ + u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ +- u8 okConstFactor; /* OK to factor out constants */ + u8 disableLookaside; /* Number of times lookaside has been disabled */ +- u8 disableVtab; /* Disable all virtual tables for this parse */ ++ u8 prepFlags; /* SQLITE_PREPARE_* flags */ ++ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ ++ u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */ ++ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ ++ u8 bReturning; /* Coding a RETURNING trigger */ ++ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ ++ u8 disableTriggers; /* True to disable triggers */ ++#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) ++ u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ ++#endif ++#ifdef SQLITE_DEBUG ++ u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ ++ u8 isCreate; /* CREATE TABLE, INDEX, or VIEW (but not TRIGGER) ++ ** and ALTER TABLE ADD COLUMN. */ ++#endif ++ bft colNamesSet :1; /* TRUE after OP_ColumnName has been issued to pVdbe */ ++ bft bHasWith :1; /* True if statement contains WITH */ ++ bft okConstFactor :1; /* OK to factor out constants */ ++ bft checkSchema :1; /* Causes schema cookie check after an error */ + int nRangeReg; /* Size of the temporary register block */ + int iRangeReg; /* First register in temporary register block */ + int nErr; /* Number of errors seen */ +@@ -18708,13 +20289,15 @@ struct Parse { + int nLabelAlloc; /* Number of slots in aLabel */ + int *aLabel; /* Space to hold the labels */ + ExprList *pConstExpr;/* Constant expressions */ +- Token constraintName;/* Name of the constraint currently being parsed */ ++ IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */ ++ IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */ + yDbMask writeMask; /* Start a write transaction on these databases */ + yDbMask cookieMask; /* Bitmask of schema verified databases */ +- int regRowid; /* Register holding rowid of CREATE TABLE entry */ +- int regRoot; /* Register holding root page number for new objects */ +- int nMaxArg; /* Max args passed to user function by sub-program */ ++ int nMaxArg; /* Max args to xUpdate and xFilter vtab methods */ + int nSelect; /* Number of SELECT stmts. Counter for Select.selId */ ++#ifndef SQLITE_OMIT_PROGRESS_CALLBACK ++ u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */ ++#endif + #ifndef SQLITE_OMIT_SHARED_CACHE + int nTableLock; /* Number of locks in aTableLock */ + TableLock *aTableLock; /* Required table locks for shared-cache mode */ +@@ -18722,15 +20305,8 @@ struct Parse { + AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ + Parse *pToplevel; /* Parse structure for main program (or NULL) */ + Table *pTriggerTab; /* Table triggers are being coded for */ +- Parse *pParentParse; /* Parent parser if this parser is nested */ +- AggInfo *pAggList; /* List of all AggInfo objects */ +- int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ +- u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ +- u32 oldmask; /* Mask of old.* columns referenced */ +- u32 newmask; /* Mask of new.* columns referenced */ +- u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ +- u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ +- u8 disableTriggers; /* True to disable triggers */ ++ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ ++ ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */ + + /************************************************************************** + ** Fields above must be initialized to zero. The fields that follow, +@@ -18740,7 +20316,21 @@ struct Parse { + **************************************************************************/ + + int aTempReg[8]; /* Holding area for temporary registers */ ++ Parse *pOuterParse; /* Outer Parse object when nested */ + Token sNameToken; /* Token with unqualified schema object name */ ++ u32 oldmask; /* Mask of old.* columns referenced */ ++ u32 newmask; /* Mask of new.* columns referenced */ ++ union { ++ struct { /* These fields available when isCreate is true */ ++ int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ ++ int regRowid; /* Register holding rowid of CREATE TABLE entry */ ++ int regRoot; /* Register holding root page for new objects */ ++ Token constraintName; /* Name of the constraint currently being parsed */ ++ } cr; ++ struct { /* These fields available to all other statements */ ++ Returning *pReturning; /* The RETURNING clause */ ++ } d; ++ } u1; + + /************************************************************************ + ** Above is constant between recursions. Below is reset before and after +@@ -18758,9 +20348,7 @@ struct Parse { + int nVtabLock; /* Number of virtual tables to lock */ + #endif + int nHeight; /* Expression tree height of current sub-select */ +-#ifndef SQLITE_OMIT_EXPLAIN + int addrExplain; /* Address of current OP_Explain opcode */ +-#endif + VList *pVList; /* Mapping between variable names and numbers */ + Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ + const char *zTail; /* All SQL text past the last semicolon parsed */ +@@ -18774,15 +20362,14 @@ struct Parse { + Token sArg; /* Complete text of a module argument */ + Table **apVtabLock; /* Pointer to virtual tables needing locking */ + #endif +- Table *pZombieTab; /* List of Table objects to delete after code gen */ +- TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ + With *pWith; /* Current WITH clause, or NULL */ +- With *pWithToFree; /* Free this WITH object at the end of the parse */ + #ifndef SQLITE_OMIT_ALTERTABLE + RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */ + #endif + }; + ++/* Allowed values for Parse.eParseMode ++*/ + #define PARSE_MODE_NORMAL 0 + #define PARSE_MODE_DECLARE_VTAB 1 + #define PARSE_MODE_RENAME 2 +@@ -18791,7 +20378,8 @@ struct Parse { + /* + ** Sizes and pointers of various parts of the Parse object. + */ +-#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/ ++#define PARSE_HDR(X) (((char*)(X))+offsetof(Parse,zErrMsg)) ++#define PARSE_HDR_SZ (offsetof(Parse,aTempReg)-offsetof(Parse,zErrMsg)) /* Recursive part w/o aColCache*/ + #define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */ + #define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */ + #define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */ +@@ -18849,6 +20437,7 @@ struct AuthContext { + #define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */ + #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ + #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ ++#define OPFLAG_BYTELENARG 0xc0 /* OP_Column only for octet_length() */ + #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ + #define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ + #define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */ +@@ -18857,27 +20446,29 @@ struct AuthContext { + #define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */ + #define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */ + #define OPFLAG_NOCHNG_MAGIC 0x6d /* OP_MakeRecord: serialtype 10 is ok */ ++#define OPFLAG_PREFORMAT 0x80 /* OP_Insert uses preformatted cell */ + + /* +- * Each trigger present in the database schema is stored as an instance of +- * struct Trigger. +- * +- * Pointers to instances of struct Trigger are stored in two ways. +- * 1. In the "trigHash" hash table (part of the sqlite3* that represents the +- * database). This allows Trigger structures to be retrieved by name. +- * 2. All triggers associated with a single table form a linked list, using the +- * pNext member of struct Trigger. A pointer to the first element of the +- * linked list is stored as the "pTrigger" member of the associated +- * struct Table. +- * +- * The "step_list" member points to the first element of a linked list +- * containing the SQL statements specified as the trigger program. +- */ ++** Each trigger present in the database schema is stored as an instance of ++** struct Trigger. ++** ++** Pointers to instances of struct Trigger are stored in two ways. ++** 1. In the "trigHash" hash table (part of the sqlite3* that represents the ++** database). This allows Trigger structures to be retrieved by name. ++** 2. All triggers associated with a single table form a linked list, using the ++** pNext member of struct Trigger. A pointer to the first element of the ++** linked list is stored as the "pTrigger" member of the associated ++** struct Table. ++** ++** The "step_list" member points to the first element of a linked list ++** containing the SQL statements specified as the trigger program. ++*/ + struct Trigger { + char *zName; /* The name of the trigger */ + char *table; /* The table or view to which the trigger applies */ + u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ + u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ ++ u8 bReturning; /* This trigger implements a RETURNING clause */ + Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */ + IdList *pColumns; /* If this is an UPDATE OF trigger, + the is stored here */ +@@ -18898,52 +20489,58 @@ struct Trigger { + #define TRIGGER_AFTER 2 + + /* +- * An instance of struct TriggerStep is used to store a single SQL statement +- * that is a part of a trigger-program. +- * +- * Instances of struct TriggerStep are stored in a singly linked list (linked +- * using the "pNext" member) referenced by the "step_list" member of the +- * associated struct Trigger instance. The first element of the linked list is +- * the first step of the trigger-program. +- * +- * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or +- * "SELECT" statement. The meanings of the other members is determined by the +- * value of "op" as follows: +- * +- * (op == TK_INSERT) +- * orconf -> stores the ON CONFLICT algorithm +- * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then +- * this stores a pointer to the SELECT statement. Otherwise NULL. +- * zTarget -> Dequoted name of the table to insert into. +- * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then +- * this stores values to be inserted. Otherwise NULL. +- * pIdList -> If this is an INSERT INTO ... () VALUES ... +- * statement, then this stores the column-names to be +- * inserted into. +- * +- * (op == TK_DELETE) +- * zTarget -> Dequoted name of the table to delete from. +- * pWhere -> The WHERE clause of the DELETE statement if one is specified. +- * Otherwise NULL. +- * +- * (op == TK_UPDATE) +- * zTarget -> Dequoted name of the table to update. +- * pWhere -> The WHERE clause of the UPDATE statement if one is specified. +- * Otherwise NULL. +- * pExprList -> A list of the columns to update and the expressions to update +- * them to. See sqlite3Update() documentation of "pChanges" +- * argument. +- * +- */ ++** An instance of struct TriggerStep is used to store a single SQL statement ++** that is a part of a trigger-program. ++** ++** Instances of struct TriggerStep are stored in a singly linked list (linked ++** using the "pNext" member) referenced by the "step_list" member of the ++** associated struct Trigger instance. The first element of the linked list is ++** the first step of the trigger-program. ++** ++** The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or ++** "SELECT" statement. The meanings of the other members is determined by the ++** value of "op" as follows: ++** ++** (op == TK_INSERT) ++** orconf -> stores the ON CONFLICT algorithm ++** pSelect -> The content to be inserted - either a SELECT statement or ++** a VALUES clause. ++** zTarget -> Dequoted name of the table to insert into. ++** pIdList -> If this is an INSERT INTO ... () VALUES ... ++** statement, then this stores the column-names to be ++** inserted into. ++** pUpsert -> The ON CONFLICT clauses for an Upsert ++** ++** (op == TK_DELETE) ++** zTarget -> Dequoted name of the table to delete from. ++** pWhere -> The WHERE clause of the DELETE statement if one is specified. ++** Otherwise NULL. ++** ++** (op == TK_UPDATE) ++** zTarget -> Dequoted name of the table to update. ++** pWhere -> The WHERE clause of the UPDATE statement if one is specified. ++** Otherwise NULL. ++** pExprList -> A list of the columns to update and the expressions to update ++** them to. See sqlite3Update() documentation of "pChanges" ++** argument. ++** ++** (op == TK_SELECT) ++** pSelect -> The SELECT statement ++** ++** (op == TK_RETURNING) ++** pExprList -> The list of expressions that follow the RETURNING keyword. ++** ++*/ + struct TriggerStep { +- u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ ++ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT, ++ ** or TK_RETURNING */ + u8 orconf; /* OE_Rollback etc. */ + Trigger *pTrig; /* The trigger that this step is a part of */ + Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ + char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ + SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */ + Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ +- ExprList *pExprList; /* SET clause for UPDATE */ ++ ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */ + IdList *pIdList; /* Column names for INSERT */ + Upsert *pUpsert; /* Upsert clauses on an INSERT */ + char *zSpan; /* Original SQL text of this command */ +@@ -18952,22 +20549,21 @@ struct TriggerStep { + }; + + /* +-** The following structure contains information used by the sqliteFix... +-** routines as they walk the parse tree to make database references +-** explicit. ++** Information about a RETURNING clause + */ +-typedef struct DbFixer DbFixer; +-struct DbFixer { +- Parse *pParse; /* The parsing context. Error messages written here */ +- Schema *pSchema; /* Fix items to this schema */ +- u8 bTemp; /* True for TEMP schema entries */ +- const char *zDb; /* Make sure all objects are contained in this database */ +- const char *zType; /* Type of the container - used for error messages */ +- const Token *pName; /* Name of the container - used for error messages */ ++struct Returning { ++ Parse *pParse; /* The parse that includes the RETURNING clause */ ++ ExprList *pReturnEL; /* List of expressions to return */ ++ Trigger retTrig; /* The transient trigger that implements RETURNING */ ++ TriggerStep retTStep; /* The trigger step */ ++ int iRetCur; /* Transient table holding RETURNING results */ ++ int nRetCol; /* Number of in pReturnEL after expansion */ ++ int iRetReg; /* Register array for holding a row of RETURNING */ ++ char zName[40]; /* Name of trigger: "sqlite_returning_%p" */ + }; + + /* +-** An objected used to accumulate the text of a string where we ++** An object used to accumulate the text of a string where we + ** do not necessarily know how big the string will be in the end. + */ + struct sqlite3_str { +@@ -18981,10 +20577,32 @@ struct sqlite3_str { + }; + #define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */ + #define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */ +-#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */ ++#define SQLITE_PRINTF_MALLOCED 0x04 /* True if zText is allocated space */ + + #define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0) + ++/* ++** The following object is the header for an "RCStr" or "reference-counted ++** string". An RCStr is passed around and used like any other char* ++** that has been dynamically allocated. The important interface ++** differences: ++** ++** 1. RCStr strings are reference counted. They are deallocated ++** when the reference count reaches zero. ++** ++** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than ++** sqlite3_free() ++** ++** 3. Make a (read-only) copy of a read-only RCStr string using ++** sqlite3RCStrRef(). ++** ++** "String" is in the name, but an RCStr object can also be used to hold ++** binary data. ++*/ ++struct RCStr { ++ u64 nRCRef; /* Number of references */ ++ /* Total structure size should be a multiple of 8 bytes for alignment */ ++}; + + /* + ** A pointer to this structure is used to communicate information +@@ -19003,7 +20621,26 @@ typedef struct { + /* + ** Allowed values for mInitFlags + */ +-#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */ ++#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */ ++#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */ ++#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */ ++#define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */ ++ ++/* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled ++** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning ++** parameters are for temporary use during development, to help find ++** optimal values for parameters in the query planner. The should not ++** be used on trunk check-ins. They are a temporary mechanism available ++** for transient development builds only. ++** ++** Tuning parameters are numbered starting with 1. ++*/ ++#define SQLITE_NTUNE 6 /* Should be zero for all trunk check-ins */ ++#ifdef SQLITE_DEBUG ++# define Tuning(X) (sqlite3Config.aTune[(X)-1]) ++#else ++# define Tuning(X) 0 ++#endif + + /* + ** Structure containing global configuration data for the SQLite library. +@@ -19018,6 +20655,9 @@ struct Sqlite3Config { + u8 bUseCis; /* Use covering indices for full-scans */ + u8 bSmallMalloc; /* Avoid large memory allocations if true */ + u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ ++#ifdef SQLITE_DEBUG ++ u8 bJsonSelfcheck; /* Double-check JSON parsing */ ++#endif + int mxStrlen; /* Maximum string length */ + int neverCorrupt; /* Database is always well-formed */ + int szLookaside; /* Default lookaside buffer size */ +@@ -19059,16 +20699,26 @@ struct Sqlite3Config { + void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */ + void *pVdbeBranchArg; /* 1st argument */ + #endif +-#ifdef SQLITE_ENABLE_DESERIALIZE ++#ifndef SQLITE_OMIT_DESERIALIZE + sqlite3_int64 mxMemdbSize; /* Default max memdb size */ + #endif + #ifndef SQLITE_UNTESTABLE + int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ ++#endif ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW ++ ** feature is disabled. 0 if rowids can ++ ** occur in views. */ + #endif + int bLocaltimeFault; /* True to fail localtime() calls */ ++ int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ + int iOnceResetThreshold; /* When to reset OP_Once counters */ + u32 szSorterRef; /* Min size in bytes to use sorter-refs */ + unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */ ++ /* vvvv--- must be last ---vvv */ ++#ifdef SQLITE_DEBUG ++ sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */ ++#endif + }; + + /* +@@ -19099,28 +20749,47 @@ struct Walker { + void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ + int walkerDepth; /* Number of subqueries */ + u16 eCode; /* A small processing code */ ++ u16 mWFlags; /* Use-dependent flags */ + union { /* Extra data for callback */ + NameContext *pNC; /* Naming context */ + int n; /* A counter */ + int iCur; /* A cursor number */ + SrcList *pSrcList; /* FROM clause */ +- struct SrcCount *pSrcCount; /* Counting column references */ + struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ ++ struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */ + int *aiCol; /* array of column indexes */ + struct IdxCover *pIdxCover; /* Check for index coverage */ +- 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 */ + struct WhereConst *pConst; /* WHERE clause constants */ + struct RenameCtx *pRename; /* RENAME COLUMN context */ + struct Table *pTab; /* Table of generated column */ +- struct SrcList_item *pSrcItem; /* A single FROM clause item */ ++ struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */ ++ SrcItem *pSrcItem; /* A single FROM clause item */ ++ DbFixer *pFix; /* See sqlite3FixSelect() */ ++ Mem *aMem; /* See sqlite3BtreeCursorHint() */ + } u; + }; + ++/* ++** The following structure contains information used by the sqliteFix... ++** routines as they walk the parse tree to make database references ++** explicit. ++*/ ++struct DbFixer { ++ Parse *pParse; /* The parsing context. Error messages written here */ ++ Walker w; /* Walker object */ ++ Schema *pSchema; /* Fix items to this schema */ ++ u8 bTemp; /* True for TEMP schema entries */ ++ const char *zDb; /* Make sure all objects are contained in this database */ ++ const char *zType; /* Type of the container - used for error messages */ ++ const Token *pName; /* Name of the container - used for error messages */ ++}; ++ + /* Forward declarations */ + SQLITE_PRIVATE int sqlite3WalkExpr(Walker*, Expr*); ++SQLITE_PRIVATE int sqlite3WalkExprNN(Walker*, Expr*); + SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*); + SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*); + SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*); +@@ -19130,11 +20799,18 @@ SQLITE_PRIVATE int sqlite3SelectWalkNoop(Walker*, Select*); + SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker*, Select*); + SQLITE_PRIVATE int sqlite3WalkerDepthIncrease(Walker*,Select*); + SQLITE_PRIVATE void sqlite3WalkerDepthDecrease(Walker*,Select*); ++SQLITE_PRIVATE void sqlite3WalkWinDefnDummyCallback(Walker*,Select*); + + #ifdef SQLITE_DEBUG + SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*); + #endif + ++#ifndef SQLITE_OMIT_CTE ++SQLITE_PRIVATE void sqlite3SelectPopWith(Walker*, Select*); ++#else ++# define sqlite3SelectPopWith 0 ++#endif ++ + /* + ** Return code from the parse-tree walking primitives and their + ** callbacks. +@@ -19144,20 +20820,74 @@ SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*); + #define WRC_Abort 2 /* Abandon the tree walk */ + + /* +-** An instance of this structure represents a set of one or more CTEs +-** (common table expressions) created by a single WITH clause. ++** A single common table expression ++*/ ++struct Cte { ++ char *zName; /* Name of this CTE */ ++ ExprList *pCols; /* List of explicit column names, or NULL */ ++ Select *pSelect; /* The definition of this CTE */ ++ const char *zCteErr; /* Error message for circular references */ ++ CteUse *pUse; /* Usage information for this CTE */ ++ u8 eM10d; /* The MATERIALIZED flag */ ++}; ++ ++/* ++** Allowed values for the materialized flag (eM10d): ++*/ ++#define M10d_Yes 0 /* AS MATERIALIZED */ ++#define M10d_Any 1 /* Not specified. Query planner's choice */ ++#define M10d_No 2 /* AS NOT MATERIALIZED */ ++ ++/* ++** An instance of the With object represents a WITH clause containing ++** one or more CTEs (common table expressions). + */ + struct With { +- int nCte; /* Number of CTEs in the WITH clause */ +- With *pOuter; /* Containing WITH clause, or NULL */ +- struct Cte { /* For each CTE in the WITH clause.... */ +- char *zName; /* Name of this CTE */ +- ExprList *pCols; /* List of explicit column names, or NULL */ +- Select *pSelect; /* The definition of this CTE */ +- const char *zCteErr; /* Error message for circular references */ +- } a[1]; ++ int nCte; /* Number of CTEs in the WITH clause */ ++ int bView; /* Belongs to the outermost Select of a view */ ++ With *pOuter; /* Containing WITH clause, or NULL */ ++ Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */ ++}; ++ ++/* The size (in bytes) of a With object that can hold as many ++** as N different CTEs. */ ++#define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte)) ++ ++/* ++** The Cte object is not guaranteed to persist for the entire duration ++** of code generation. (The query flattener or other parser tree ++** edits might delete it.) The following object records information ++** about each Common Table Expression that must be preserved for the ++** duration of the parse. ++** ++** The CteUse objects are freed using sqlite3ParserAddCleanup() rather ++** than sqlite3SelectDelete(), which is what enables them to persist ++** until the end of code generation. ++*/ ++struct CteUse { ++ int nUse; /* Number of users of this CTE */ ++ int addrM9e; /* Start of subroutine to compute materialization */ ++ int regRtn; /* Return address register for addrM9e subroutine */ ++ int iCur; /* Ephemeral table holding the materialization */ ++ LogEst nRowEst; /* Estimated number of rows in the table */ ++ u8 eM10d; /* The MATERIALIZED flag */ ++}; ++ ++ ++/* Client data associated with sqlite3_set_clientdata() and ++** sqlite3_get_clientdata(). ++*/ ++struct DbClientData { ++ DbClientData *pNext; /* Next in a linked list */ ++ void *pData; /* The data */ ++ void (*xDestructor)(void*); /* Destructor. Might be NULL */ ++ char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */ + }; + ++/* The size (in bytes) of a DbClientData object that can has a name ++** that is N bytes long, including the zero-terminator. */ ++#define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N)) ++ + #ifdef SQLITE_DEBUG + /* + ** An instance of the TreeView object is used for printing the content of +@@ -19207,7 +20937,7 @@ struct Window { + Window **ppThis; /* Pointer to this object in Select.pWin list */ + Window *pNextWin; /* Next window function belonging to this SELECT */ + Expr *pFilter; /* The FILTER expression */ +- FuncDef *pFunc; /* The function */ ++ FuncDef *pWFunc; /* The function */ + int iEphCsr; /* Partition buffer or Peer buffer */ + int regAccum; /* Accumulator */ + int regResult; /* Interim result */ +@@ -19224,6 +20954,9 @@ struct Window { + ** due to the SQLITE_SUBTYPE flag */ + }; + ++SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow); ++SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal); ++ + #ifndef SQLITE_OMIT_WINDOWFUNC + SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); + SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*); +@@ -19231,11 +20964,10 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p); + SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); + SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*); + SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin); +-SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*, int); ++SQLITE_PRIVATE int sqlite3WindowCompare(const Parse*, const Window*, const Window*, int); + SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Select*); + SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int); + SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*); +-SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, struct SrcList_item*); + SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*); + SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p); + SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p); +@@ -19305,15 +21037,6 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); + # define SQLITE_ENABLE_FTS3 1 + #endif + +-/* +-** The ctype.h header is needed for non-ASCII systems. It is also +-** needed by FTS3 when FTS3 is included in the amalgamation. +-*/ +-#if !defined(SQLITE_ASCII) || \ +- (defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_AMALGAMATION)) +-# include +-#endif +- + /* + ** The following macros mimic the standard library functions toupper(), + ** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The +@@ -19328,6 +21051,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); + # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) + # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) + # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) ++# define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) ++# define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) + #else + # define sqlite3Toupper(x) toupper((unsigned char)(x)) + # define sqlite3Isspace(x) isspace((unsigned char)(x)) +@@ -19337,6 +21062,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); + # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) + # define sqlite3Tolower(x) tolower((unsigned char)(x)) + # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') ++# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') ++# define sqlite3JsonId2(x) sqlite3IsIdChar(x) + #endif + SQLITE_PRIVATE int sqlite3IsIdChar(u8); + +@@ -19364,8 +21091,9 @@ SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64); + SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64); + SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*); + SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*); +-SQLITE_PRIVATE int sqlite3MallocSize(void*); +-SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*); ++SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3*, void*); ++SQLITE_PRIVATE int sqlite3MallocSize(const void*); ++SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, const void*); + SQLITE_PRIVATE void *sqlite3PageMalloc(int); + SQLITE_PRIVATE void sqlite3PageFree(void*); + SQLITE_PRIVATE void sqlite3MemSetDefault(void); +@@ -19384,12 +21112,14 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void); + */ + #ifdef SQLITE_USE_ALLOCA + # define sqlite3StackAllocRaw(D,N) alloca(N) +-# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N) ++# define sqlite3StackAllocRawNN(D,N) alloca(N) + # define sqlite3StackFree(D,P) ++# define sqlite3StackFreeNN(D,P) + #else + # define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N) +-# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N) ++# define sqlite3StackAllocRawNN(D,N) sqlite3DbMallocRawNN(D,N) + # define sqlite3StackFree(D,P) sqlite3DbFree(D,P) ++# define sqlite3StackFreeNN(D,P) sqlite3DbFreeNN(D,P) + #endif + + /* Do not allow both MEMSYS5 and MEMSYS3 to be defined together. If they +@@ -19437,10 +21167,13 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*); + # define EXP754 (((u64)0x7ff)<<52) + # define MAN754 ((((u64)1)<<52)-1) + # define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0) ++# define IsOvfl(X) (((X)&EXP754)==EXP754) + SQLITE_PRIVATE int sqlite3IsNaN(double); ++SQLITE_PRIVATE int sqlite3IsOverflow(double); + #else +-# define IsNaN(X) 0 +-# define sqlite3IsNaN(X) 0 ++# define IsNaN(X) 0 ++# define sqlite3IsNaN(X) 0 ++# define sqlite3IsOVerflow(X) 0 + #endif + + /* +@@ -19453,6 +21186,20 @@ struct PrintfArguments { + sqlite3_value **apArg; /* The argument values */ + }; + ++/* ++** An instance of this object receives the decoding of a floating point ++** value into an approximate decimal representation. ++*/ ++struct FpDecode { ++ char sign; /* '+' or '-' */ ++ char isSpecial; /* 1: Infinity 2: NaN */ ++ int n; /* Significant digits in the decode */ ++ int iDP; /* Location of the decimal point */ ++ char *z; /* Start of significant digits */ ++ char zBuf[24]; /* Storage for significant digits */ ++}; ++ ++SQLITE_PRIVATE void sqlite3FpDecode(FpDecode*,double,int,int); + SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); + SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); + #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) +@@ -19463,33 +21210,75 @@ SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*); + #endif + + #if defined(SQLITE_DEBUG) ++SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView*, const char *zFormat, ...); + SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8); + SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*); + SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*); ++SQLITE_PRIVATE void sqlite3TreeViewBareIdList(TreeView*, const IdList*, const char*); ++SQLITE_PRIVATE void sqlite3TreeViewIdList(TreeView*, const IdList*, u8, const char*); ++SQLITE_PRIVATE void sqlite3TreeViewColumnList(TreeView*, const Column*, int, u8); + SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView*, const SrcList*); + SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8); + SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8); ++SQLITE_PRIVATE void sqlite3TreeViewUpsert(TreeView*, const Upsert*, u8); ++#if TREETRACE_ENABLED ++SQLITE_PRIVATE void sqlite3TreeViewDelete(const With*, const SrcList*, const Expr*, ++ const ExprList*,const Expr*, const Trigger*); ++SQLITE_PRIVATE void sqlite3TreeViewInsert(const With*, const SrcList*, ++ const IdList*, const Select*, const ExprList*, ++ int, const Upsert*, const Trigger*); ++SQLITE_PRIVATE void sqlite3TreeViewUpdate(const With*, const SrcList*, const ExprList*, ++ const Expr*, int, const ExprList*, const Expr*, ++ const Upsert*, const Trigger*); ++#endif ++#ifndef SQLITE_OMIT_TRIGGER ++SQLITE_PRIVATE void sqlite3TreeViewTriggerStep(TreeView*, const TriggerStep*, u8, u8); ++SQLITE_PRIVATE void sqlite3TreeViewTrigger(TreeView*, const Trigger*, u8, u8); ++#endif + #ifndef SQLITE_OMIT_WINDOWFUNC + SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView*, const Window*, u8); + SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8); + #endif ++SQLITE_PRIVATE void sqlite3ShowExpr(const Expr*); ++SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList*); ++SQLITE_PRIVATE void sqlite3ShowIdList(const IdList*); ++SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList*); ++SQLITE_PRIVATE void sqlite3ShowSelect(const Select*); ++SQLITE_PRIVATE void sqlite3ShowWith(const With*); ++SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert*); ++#ifndef SQLITE_OMIT_TRIGGER ++SQLITE_PRIVATE void sqlite3ShowTriggerStep(const TriggerStep*); ++SQLITE_PRIVATE void sqlite3ShowTriggerStepList(const TriggerStep*); ++SQLITE_PRIVATE void sqlite3ShowTrigger(const Trigger*); ++SQLITE_PRIVATE void sqlite3ShowTriggerList(const Trigger*); ++#endif ++#ifndef SQLITE_OMIT_WINDOWFUNC ++SQLITE_PRIVATE void sqlite3ShowWindow(const Window*); ++SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window*); ++#endif + #endif +- + + SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*); ++SQLITE_PRIVATE void sqlite3ProgressCheck(Parse*); + SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); + SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); + SQLITE_PRIVATE void sqlite3Dequote(char*); + SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); ++SQLITE_PRIVATE void sqlite3DequoteToken(Token*); ++SQLITE_PRIVATE void sqlite3DequoteNumber(Parse*, Expr*); + SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*); + SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); +-SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **); ++SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*); + SQLITE_PRIVATE void sqlite3FinishCoding(Parse*); + SQLITE_PRIVATE int sqlite3GetTempReg(Parse*); + SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); + SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); + SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); + SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); ++SQLITE_PRIVATE void sqlite3TouchRegister(Parse*,int); ++#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) ++SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse*,int); ++#endif + #ifdef SQLITE_DEBUG + SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int); + #endif +@@ -19500,17 +21289,23 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); + SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*); + SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*); + SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); +-SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*, int); +-SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,Expr*,FuncDef*); ++SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); ++SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); ++SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*); ++SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); + SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); + SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); ++SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*); ++SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse*, Expr*); + SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); + SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); + SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); ++SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*); + SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int); +-SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); ++SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); + SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); + SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); ++SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*); + SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); + SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); + SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); +@@ -19524,13 +21319,18 @@ SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*); + SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int); + SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*); + SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*); ++SQLITE_PRIVATE void sqlite3ColumnSetExpr(Parse*,Table*,Column*,Expr*); ++SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table*,Column*); ++SQLITE_PRIVATE void sqlite3ColumnSetColl(sqlite3*,Column*,const char*zColl); ++SQLITE_PRIVATE const char *sqlite3ColumnColl(Column*); + SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*); ++SQLITE_PRIVATE void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect); + SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); +-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char); ++SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char); + SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); + SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int); + SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*); +-SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16); ++SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index*, int); + #ifdef SQLITE_OMIT_GENERATED_COLUMNS + # define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */ + # define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */ +@@ -19544,14 +21344,15 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table*, Column*); + #else + # define sqlite3ColumnPropertiesFromName(T,C) /* no-op */ + #endif +-SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*,Token*); ++SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token,Token); + SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int); + SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int); +-SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*); ++SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*); + SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*); + SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); + SQLITE_PRIVATE void sqlite3AddGenerated(Parse*,Expr*,Token*); +-SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); ++SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u32,Select*); ++SQLITE_PRIVATE void sqlite3AddReturning(Parse*,ExprList*); + SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, + sqlite3_vfs**,char**,char **); + #define sqlite3CodecQueryParameters(A,B,C) 0 +@@ -19595,6 +21396,7 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask); + SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int); + SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int); + SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*); ++SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*); + SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*); + #ifndef SQLITE_OMIT_AUTOINCREMENT + SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse); +@@ -19613,16 +21415,20 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); + SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); + SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); + SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); ++SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3*,Subquery*); ++SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3*,SrcItem*); ++SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery(Parse*, SrcItem*, Select*, int); + SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, +- Token*, Select*, Expr*, IdList*); ++ Token*, Select*, OnOrUsing*); + SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); + SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); +-SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, struct SrcList_item *); +-SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*); ++SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *); ++SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*); + SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); + SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); ++SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*); + SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); +-SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); ++SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,int,int,char**); + SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, + Expr*, int, int, u8); + SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); +@@ -19630,21 +21436,25 @@ SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); + SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, + Expr*,ExprList*,u32,Expr*); + SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*); ++SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*); + SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*); +-SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int); ++SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*); + SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); + #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) + SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); + #endif ++SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*); + SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); + SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, + Upsert*); +-SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); ++SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, ++ ExprList*,Select*,u16,int); + SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*); + SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*); + SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*); + SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*); + SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo*); ++SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*); + SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*); + SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*); + SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*); +@@ -19657,13 +21467,15 @@ SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int + SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); + SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); + SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int); ++SQLITE_PRIVATE void sqlite3ExprToRegister(Expr *pExpr, int iReg); + SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int); + #ifndef SQLITE_OMIT_GENERATED_COLUMNS +-SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Column*, int); ++SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); + #endif + SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int); + SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int); + SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int); ++SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int); + SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); + SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); + SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); +@@ -19678,23 +21490,24 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*); + #define LOCATE_VIEW 0x01 + #define LOCATE_NOERR 0x02 + SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); +-SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *); ++SQLITE_PRIVATE const char *sqlite3PreferredTableName(const char*); ++SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *); + SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*); + SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); + SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); + SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*,Expr*); + SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); +-SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*); +-SQLITE_PRIVATE int sqlite3ExprCompare(Parse*,Expr*, Expr*, int); +-SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*, Expr*, int); +-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int); +-SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); +-SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int); ++SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, const Token*); ++SQLITE_PRIVATE int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int); ++SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*,Expr*,int); ++SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList*,const ExprList*, int); ++SQLITE_PRIVATE int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); ++SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int,int); + SQLITE_PRIVATE void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); + SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); + SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); + SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); +-SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); ++SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse*, Expr*, SrcList*); + SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*); + #ifndef SQLITE_UNTESTABLE + SQLITE_PRIVATE void sqlite3PrngSaveState(void); +@@ -19711,18 +21524,18 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*); + SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char*); + SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*); + SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*); +-SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); +-SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); ++SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse*,Expr*); + SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); + SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); +-SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); ++SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int); + #ifdef SQLITE_ENABLE_CURSOR_HINTS + SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); + #endif +-SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); ++SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*, Parse*); + SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); + SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); + SQLITE_PRIVATE int sqlite3IsRowid(const char*); ++SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); + SQLITE_PRIVATE void sqlite3GenerateRowDelete( + Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); + SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); +@@ -19744,20 +21557,27 @@ SQLITE_PRIVATE void sqlite3MayAbort(Parse*); + SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); + SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*); + SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*); +-SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int); +-SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); +-SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); +-SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*); +-SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int); ++SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,const Expr*,int); ++SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,const ExprList*,int); ++SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,const SrcList*,int); ++SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*); ++SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int); + SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); + SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); + SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); ++SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int); ++SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32); + SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); + SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); ++SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void); + SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); ++#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) ++SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3*); ++#endif + SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); + SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*); + SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int); ++SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p); + + #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) + SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); +@@ -19806,7 +21626,9 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*); + #endif + + SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*); +-SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr*,int); ++SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol); ++SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem*,int); ++SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr*,int,u32); + SQLITE_PRIVATE void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); + SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse*, int); + #ifndef SQLITE_OMIT_AUTHORIZATION +@@ -19828,29 +21650,25 @@ SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Tok + SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); + SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); + SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); +-SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); + SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); ++ + SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); +-SQLITE_PRIVATE void sqlite3Int64ToText(i64,char*); ++SQLITE_PRIVATE i64 sqlite3RealToI64(double); ++SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*); + SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); + SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); + SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); + SQLITE_PRIVATE int sqlite3Atoi(const char*); + #ifndef SQLITE_OMIT_UTF16 +-SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); ++SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nByte, int nChar); + #endif + SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); + SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); ++SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); + SQLITE_PRIVATE LogEst sqlite3LogEst(u64); + SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst); +-#ifndef SQLITE_OMIT_VIRTUALTABLE + SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double); +-#endif +-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ +- defined(SQLITE_ENABLE_STAT4) || \ +- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) + SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst); +-#endif + SQLITE_PRIVATE VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int); + SQLITE_PRIVATE const char *sqlite3VListNumToName(VList*,int); + SQLITE_PRIVATE int sqlite3VListNameToNum(VList*,const char*,int); +@@ -19882,17 +21700,22 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v); + + + SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*); ++SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3*,const Table*); + SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int); + SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2); + SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); +-SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table*,int); ++SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table*,int); + SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr); ++SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr); + SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); + SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*); + SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); + SQLITE_PRIVATE void sqlite3Error(sqlite3*,int); ++SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*); + SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int); ++#if !defined(SQLITE_OMIT_BLOB_LITERAL) + SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); ++#endif + SQLITE_PRIVATE u8 sqlite3HexToInt(int h); + SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); + +@@ -19900,8 +21723,11 @@ SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); + SQLITE_PRIVATE const char *sqlite3ErrName(int); + #endif + +-#ifdef SQLITE_ENABLE_DESERIALIZE ++#ifndef SQLITE_OMIT_DESERIALIZE + SQLITE_PRIVATE int sqlite3MemdbInit(void); ++SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs*); ++#else ++# define sqlite3IsMemdb(X) 0 + #endif + + SQLITE_PRIVATE const char *sqlite3ErrStr(int); +@@ -19913,14 +21739,14 @@ SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8); + SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); + SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); + SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); +-SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); +-SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); ++SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(const Parse *pParse, Expr*, const Token*, int); ++SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(const Parse*,Expr*,const char*); + SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*); + SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr*); + SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); + SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*); + SQLITE_PRIVATE int sqlite3CheckObjectName(Parse*, const char*,const char*,const char*); +-SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int); ++SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, i64); + SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64); + SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64); + SQLITE_PRIVATE int sqlite3MulInt64(i64*,i64); +@@ -19933,6 +21759,7 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*); + SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8); + + SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8); ++SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); + SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8); + SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, + void(*)(void*)); +@@ -19945,16 +21772,21 @@ SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); + #ifndef SQLITE_OMIT_UTF16 + SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); + #endif +-SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); ++SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, const Expr *, u8, u8, sqlite3_value **); + SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); + #ifndef SQLITE_AMALGAMATION + SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[]; + SQLITE_PRIVATE const char sqlite3StrBINARY[]; ++SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[]; ++SQLITE_PRIVATE const char sqlite3StdTypeAffinity[]; ++SQLITE_PRIVATE const char *sqlite3StdType[]; + SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; ++SQLITE_PRIVATE const unsigned char *sqlite3aLTb; ++SQLITE_PRIVATE const unsigned char *sqlite3aEQb; ++SQLITE_PRIVATE const unsigned char *sqlite3aGTb; + SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; + SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; + SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; +-SQLITE_API extern u32 sqlite3_unsupported_selecttrace; + #ifndef SQLITE_OMIT_WSD + SQLITE_PRIVATE int sqlite3PendingByte; + #endif +@@ -19973,12 +21805,14 @@ SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); + SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int); + SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*); + SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); ++SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, SrcItem*); + SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); + SQLITE_PRIVATE int sqlite3MatchEName( + const struct ExprList_item*, + const char*, + const char*, +- const char* ++ const char*, ++ int* + ); + SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*); + SQLITE_PRIVATE u8 sqlite3StrIHash(const char*); +@@ -19990,8 +21824,9 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const + SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); + SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); + SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *); +-SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse*, void*, Token*); +-SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom); ++SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse*, SrcList*, const Token*); ++SQLITE_PRIVATE const void *sqlite3RenameTokenMap(Parse*, const void*, const Token*); ++SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, const void *pTo, const void *pFrom); + SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*); + SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*); + SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); +@@ -20013,6 +21848,7 @@ SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*); + SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*); + SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*); + SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int); ++SQLITE_PRIVATE const char *sqlite3SelectOpName(int); + SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*); + + #ifdef SQLITE_DEBUG +@@ -20027,15 +21863,25 @@ SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, + FuncDestructor *pDestructor + ); + SQLITE_PRIVATE void sqlite3NoopDestructor(void*); +-SQLITE_PRIVATE void sqlite3OomFault(sqlite3*); ++SQLITE_PRIVATE void *sqlite3OomFault(sqlite3*); + SQLITE_PRIVATE void sqlite3OomClear(sqlite3*); + SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); + SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); + ++SQLITE_PRIVATE char *sqlite3RCStrRef(char*); ++SQLITE_PRIVATE void sqlite3RCStrUnref(void*); ++SQLITE_PRIVATE char *sqlite3RCStrNew(u64); ++SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64); ++ + SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); ++SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, i64); + SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); ++SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum*, u8); ++SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*); + SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int); + SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); ++SQLITE_PRIVATE void sqlite3RecordErrorByteOffset(sqlite3*,const char*); ++SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3*,const Expr*); + + SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *); + SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); +@@ -20086,7 +21932,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*); + #endif + + #ifdef SQLITE_OMIT_VIRTUALTABLE +-# define sqlite3VtabClear(Y) ++# define sqlite3VtabClear(D,T) + # define sqlite3VtabSync(X,Y) SQLITE_OK + # define sqlite3VtabRollback(X) + # define sqlite3VtabCommit(X) +@@ -20123,9 +21969,11 @@ SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db); + #ifndef SQLITE_OMIT_VIRTUALTABLE + SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName); + SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3*,Table*,const char*); ++SQLITE_PRIVATE void sqlite3MarkAllShadowTablesOf(sqlite3*, Table*); + #else + # define sqlite3ShadowTableName(A,B) 0 + # define sqlite3IsShadowTableOf(A,B,C) 0 ++# define sqlite3MarkAllShadowTablesOf(A,B) + #endif + SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*); + SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*); +@@ -20138,11 +21986,15 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); + SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*); + SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); + SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); ++ + SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); ++SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse*); + SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); + SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); + SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); +-SQLITE_PRIVATE void sqlite3ParserReset(Parse*); ++SQLITE_PRIVATE void sqlite3ParseObjectInit(Parse*,sqlite3*); ++SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse*); ++SQLITE_PRIVATE void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*); + #ifdef SQLITE_ENABLE_NORMALIZE + SQLITE_PRIVATE char *sqlite3Normalize(Vdbe*, const char*); + #endif +@@ -20157,23 +22009,33 @@ SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); + SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); + #endif + #ifndef SQLITE_OMIT_CTE +-SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*); ++SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); ++SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); ++SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); + SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); +-SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8); ++SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3*,void*); ++SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); + #else +-#define sqlite3WithPush(x,y,z) +-#define sqlite3WithDelete(x,y) ++# define sqlite3CteNew(P,T,E,S) ((void*)0) ++# define sqlite3CteDelete(D,C) ++# define sqlite3CteWithAdd(P,W,C) ((void*)0) ++# define sqlite3WithDelete(x,y) ++# define sqlite3WithPush(x,y,z) ((void*)0) + #endif + #ifndef SQLITE_OMIT_UPSERT +-SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*); ++SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); + SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*); + SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); +-SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*); ++SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*); + SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); ++SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*); ++SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*); + #else +-#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0) ++#define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0) + #define sqlite3UpsertDelete(x,y) +-#define sqlite3UpsertDup(x,y) ((Upsert*)0) ++#define sqlite3UpsertDup(x,y) ((Upsert*)0) ++#define sqlite3UpsertOfIndex(x,y) ((Upsert*)0) ++#define sqlite3UpsertNextIsIPK(x) 0 + #endif + + +@@ -20191,6 +22053,7 @@ SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int + SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int); + SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*); + SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *); ++SQLITE_PRIVATE void sqlite3FkClearTriggerCache(sqlite3*,int); + #else + #define sqlite3FkActions(a,b,c,d,e,f) + #define sqlite3FkCheck(a,b,c,d,e,f) +@@ -20198,6 +22061,7 @@ SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *); + #define sqlite3FkOldmask(a,b) 0 + #define sqlite3FkRequired(a,b,c,d) 0 + #define sqlite3FkReferences(a) 0 ++ #define sqlite3FkClearTriggerCache(a,b) + #endif + #ifndef SQLITE_OMIT_FOREIGN_KEY + SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*); +@@ -20255,12 +22119,13 @@ SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *); + + SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); + #if SQLITE_MAX_EXPR_DEPTH>0 +-SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *); ++SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *); + SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); + #else + #define sqlite3SelectExprHeight(x) 0 + #define sqlite3ExprCheckHeight(x,y) + #endif ++SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr*,int); + + SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*); + SQLITE_PRIVATE void sqlite3Put4byte(u8*, u32); +@@ -20326,8 +22191,8 @@ SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...); + */ + #ifdef SQLITE_MEMDEBUG + SQLITE_PRIVATE void sqlite3MemdebugSetType(void*,u8); +-SQLITE_PRIVATE int sqlite3MemdebugHasType(void*,u8); +-SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8); ++SQLITE_PRIVATE int sqlite3MemdebugHasType(const void*,u8); ++SQLITE_PRIVATE int sqlite3MemdebugNoType(const void*,u8); + #else + # define sqlite3MemdebugSetType(X,Y) /* no-op */ + # define sqlite3MemdebugHasType(X,Y) 1 +@@ -20352,22 +22217,38 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3*); + SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*); + #endif + +-SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr); +-SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr); ++SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr); ++SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr); + SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr*, int); +-SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int); ++SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int); + SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse*, Expr*); + + #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS + SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt); + #endif + ++#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) ++SQLITE_PRIVATE int sqlite3KvvfsInit(void); ++#endif ++ ++#if defined(VDBE_PROFILE) \ ++ || defined(SQLITE_PERFORMANCE_TRACE) \ ++ || defined(SQLITE_ENABLE_STMT_SCANSTATUS) ++SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void); ++#endif ++ ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++# define IS_STMT_SCANSTATUS(db) (db->flags & SQLITE_StmtScanStatus) ++#else ++# define IS_STMT_SCANSTATUS(db) 0 ++#endif ++ + #endif /* SQLITEINT_H */ + + /************** End of sqliteInt.h *******************************************/ +-/************** Begin file global.c ******************************************/ ++/************** Begin file os_common.h ***************************************/ + /* +-** 2008 June 13 ++** 2004 May 22 + ** + ** The author disclaims copyright to this source code. In place of + ** a legal notice, here is a blessing: +@@ -20376,321 +22257,108 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt); + ** May you find forgiveness for yourself and forgive others. + ** May you share freely, never taking more than you give. + ** +-************************************************************************* ++****************************************************************************** + ** +-** This file contains definitions of global variables and constants. +-*/ +-/* #include "sqliteInt.h" */ +- +-/* An array to map all upper-case characters into their corresponding +-** lower-case character. ++** This file contains macros and a little bit of code that is common to ++** all of the platform-specific files (os_*.c) and is #included into those ++** files. + ** +-** SQLite only considers US-ASCII (or EBCDIC) characters. We do not +-** handle case conversions for the UTF character set since the tables +-** involved are nearly as big or bigger than SQLite itself. ++** This file should be #included by the os_*.c files only. It is not a ++** general purpose header file. + */ +-SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { +-#ifdef SQLITE_ASCII +- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, +- 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, +- 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +- 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, +- 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, +- 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, +- 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, +- 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, +- 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, +- 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, +- 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, +- 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, +- 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, +- 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, +- 252,253,254,255 +-#endif +-#ifdef SQLITE_EBCDIC +- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ +- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ +- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ +- 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ +- 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ +- 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ +- 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */ +- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */ +- 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ +- 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */ +- 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ +- 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ +- 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ +- 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ +- 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ +- 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ +-#endif +-}; ++#ifndef _OS_COMMON_H_ ++#define _OS_COMMON_H_ + + /* +-** The following 256 byte lookup table is used to support SQLites built-in +-** equivalents to the following standard library functions: +-** +-** isspace() 0x01 +-** isalpha() 0x02 +-** isdigit() 0x04 +-** isalnum() 0x06 +-** isxdigit() 0x08 +-** toupper() 0x20 +-** SQLite identifier character 0x40 +-** Quote character 0x80 +-** +-** Bit 0x20 is set if the mapped character requires translation to upper +-** case. i.e. if the character is a lower-case ASCII character. +-** If x is a lower-case ASCII character, then its upper-case equivalent +-** is (x - 0x20). Therefore toupper() can be implemented as: +-** +-** (x & ~(map[x]&0x20)) +-** +-** The equivalent of tolower() is implemented using the sqlite3UpperToLower[] +-** array. tolower() is used more often than toupper() by SQLite. +-** +-** Bit 0x40 is set if the character is non-alphanumeric and can be used in an +-** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any +-** non-ASCII UTF character. Hence the test for whether or not a character is +-** part of an identifier is 0x46. +-*/ +-SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */ +- 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ +- 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */ +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ +- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ +- 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ +- +- 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ +- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ +- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ +- 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ +- 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ +- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ +- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ +- 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ +- +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ +- +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ +- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ +-}; +- +-/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards +-** compatibility for legacy applications, the URI filename capability is +-** disabled by default. +-** +-** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled +-** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. +-** +-** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally +-** disabled. The default value may be changed by compiling with the +-** SQLITE_USE_URI symbol defined. +-*/ +-#ifndef SQLITE_USE_URI +-# define SQLITE_USE_URI 0 +-#endif +- +-/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the +-** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if +-** that compile-time option is omitted. +-*/ +-#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN) +-# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 +-#else +-# if !SQLITE_ALLOW_COVERING_INDEX_SCAN +-# error "Compile-time disabling of covering index scan using the\ +- -DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\ +- Contact SQLite developers if this is a problem for you, and\ +- delete this #error macro to continue with your build." +-# endif +-#endif +- +-/* The minimum PMA size is set to this value multiplied by the database +-** page size in bytes. +-*/ +-#ifndef SQLITE_SORTER_PMASZ +-# define SQLITE_SORTER_PMASZ 250 +-#endif +- +-/* Statement journals spill to disk when their size exceeds the following +-** threshold (in bytes). 0 means that statement journals are created and +-** written to disk immediately (the default behavior for SQLite versions +-** before 3.12.0). -1 means always keep the entire statement journal in +-** memory. (The statement journal is also always held entirely in memory +-** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this +-** setting.) ++** At least two bugs have slipped in because we changed the MEMORY_DEBUG ++** macro to SQLITE_DEBUG and some older makefiles have not yet made the ++** switch. The following code should catch this problem at compile-time. + */ +-#ifndef SQLITE_STMTJRNL_SPILL +-# define SQLITE_STMTJRNL_SPILL (64*1024) ++#ifdef MEMORY_DEBUG ++# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." + #endif + + /* +-** The default lookaside-configuration, the format "SZ,N". SZ is the +-** number of bytes in each lookaside slot (should be a multiple of 8) +-** and N is the number of slots. The lookaside-configuration can be +-** changed as start-time using sqlite3_config(SQLITE_CONFIG_LOOKASIDE) +-** or at run-time for an individual database connection using +-** sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE); +-** +-** With the two-size-lookaside enhancement, less lookaside is required. +-** The default configuration of 1200,40 actually provides 30 1200-byte slots +-** and 93 128-byte slots, which is more lookaside than is available +-** using the older 1200,100 configuration without two-size-lookaside. ++** Macros for performance tracing. Normally turned off. Only works ++** on i486 hardware. + */ +-#ifndef SQLITE_DEFAULT_LOOKASIDE +-# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE +-# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */ +-# else +-# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */ +-# endif +-#endif +- ++#ifdef SQLITE_PERFORMANCE_TRACE + +-/* The default maximum size of an in-memory database created using +-** sqlite3_deserialize() +-*/ +-#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE +-# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 ++static sqlite_uint64 g_start; ++static sqlite_uint64 g_elapsed; ++#define TIMER_START g_start=sqlite3Hwtime() ++#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start ++#define TIMER_ELAPSED g_elapsed ++#else ++#define TIMER_START ++#define TIMER_END ++#define TIMER_ELAPSED ((sqlite_uint64)0) + #endif + + /* +-** The following singleton contains the global configuration for +-** the SQLite library. ++** If we compile with the SQLITE_TEST macro set, then the following block ++** of code will give us the ability to simulate a disk I/O error. This ++** is used for testing the I/O recovery logic. + */ +-SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { +- SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ +- 1, /* bCoreMutex */ +- SQLITE_THREADSAFE==1, /* bFullMutex */ +- SQLITE_USE_URI, /* bOpenUri */ +- SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ +- 0, /* bSmallMalloc */ +- 1, /* bExtraSchemaChecks */ +- 0x7ffffffe, /* mxStrlen */ +- 0, /* neverCorrupt */ +- SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ +- SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ +- {0,0,0,0,0,0,0,0}, /* m */ +- {0,0,0,0,0,0,0,0,0}, /* mutex */ +- {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ +- (void*)0, /* pHeap */ +- 0, /* nHeap */ +- 0, 0, /* mnHeap, mxHeap */ +- SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ +- SQLITE_MAX_MMAP_SIZE, /* mxMmap */ +- (void*)0, /* pPage */ +- 0, /* szPage */ +- SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ +- 0, /* mxParserStack */ +- 0, /* sharedCacheEnabled */ +- SQLITE_SORTER_PMASZ, /* szPma */ +- /* All the rest should always be initialized to zero */ +- 0, /* isInit */ +- 0, /* inProgress */ +- 0, /* isMutexInit */ +- 0, /* isMallocInit */ +- 0, /* isPCacheInit */ +- 0, /* nRefInitMutex */ +- 0, /* pInitMutex */ +- 0, /* xLog */ +- 0, /* pLogArg */ +-#ifdef SQLITE_ENABLE_SQLLOG +- 0, /* xSqllog */ +- 0, /* pSqllogArg */ +-#endif +-#ifdef SQLITE_VDBE_COVERAGE +- 0, /* xVdbeBranch */ +- 0, /* pVbeBranchArg */ +-#endif +-#ifdef SQLITE_ENABLE_DESERIALIZE +- SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ +-#endif +-#ifndef SQLITE_UNTESTABLE +- 0, /* xTestCallback */ +-#endif +- 0, /* bLocaltimeFault */ +- 0x7ffffffe, /* iOnceResetThreshold */ +- SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ +- 0, /* iPrngSeed */ +-}; ++#if defined(SQLITE_TEST) ++SQLITE_API extern int sqlite3_io_error_hit; ++SQLITE_API extern int sqlite3_io_error_hardhit; ++SQLITE_API extern int sqlite3_io_error_pending; ++SQLITE_API extern int sqlite3_io_error_persist; ++SQLITE_API extern int sqlite3_io_error_benign; ++SQLITE_API extern int sqlite3_diskfull_pending; ++SQLITE_API extern int sqlite3_diskfull; ++#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) ++#define SimulateIOError(CODE) \ ++ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ ++ || sqlite3_io_error_pending-- == 1 ) \ ++ { local_ioerr(); CODE; } ++static void local_ioerr(){ ++ IOTRACE(("IOERR\n")); ++ sqlite3_io_error_hit++; ++ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; ++} ++#define SimulateDiskfullError(CODE) \ ++ if( sqlite3_diskfull_pending ){ \ ++ if( sqlite3_diskfull_pending == 1 ){ \ ++ local_ioerr(); \ ++ sqlite3_diskfull = 1; \ ++ sqlite3_io_error_hit = 1; \ ++ CODE; \ ++ }else{ \ ++ sqlite3_diskfull_pending--; \ ++ } \ ++ } ++#else ++#define SimulateIOErrorBenign(X) ++#define SimulateIOError(A) ++#define SimulateDiskfullError(A) ++#endif /* defined(SQLITE_TEST) */ + + /* +-** Hash table for global functions - functions common to all +-** database connections. After initialization, this table is +-** read-only. ++** When testing, keep a count of the number of open files. + */ +-SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; ++#if defined(SQLITE_TEST) ++SQLITE_API extern int sqlite3_open_file_count; ++#define OpenCounter(X) sqlite3_open_file_count+=(X) ++#else ++#define OpenCounter(X) ++#endif /* defined(SQLITE_TEST) */ + +-#ifdef VDBE_PROFILE +-/* +-** The following performance counter can be used in place of +-** sqlite3Hwtime() for profiling. This is a no-op on standard builds. +-*/ +-SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt = 0; +-#endif ++#endif /* !defined(_OS_COMMON_H_) */ + +-/* +-** The value of the "pending" byte must be 0x40000000 (1 byte past the +-** 1-gibabyte boundary) in a compatible database. SQLite never uses +-** the database page that contains the pending byte. It never attempts +-** to read or write that page. The pending byte page is set aside +-** for use by the VFS layers as space for managing file locks. +-** +-** During testing, it is often desirable to move the pending byte to +-** a different position in the file. This allows code that has to +-** deal with the pending byte to run on files that are much smaller +-** than 1 GiB. The sqlite3_test_control() interface can be used to +-** move the pending byte. ++/************** End of os_common.h *******************************************/ ++/************** Begin file ctime.c *******************************************/ ++/* DO NOT EDIT! ++** This file is automatically generated by the script in the canonical ++** SQLite source tree at tool/mkctimec.tcl. + ** +-** IMPORTANT: Changing the pending byte to any value other than +-** 0x40000000 results in an incompatible database file format! +-** Changing the pending byte during operation will result in undefined +-** and incorrect behavior. +-*/ +-#ifndef SQLITE_OMIT_WSD +-SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; +-#endif +- +-/* +-** Flags for select tracing and the ".selecttrace" macro of the CLI +-*/ +-SQLITE_API u32 sqlite3_unsupported_selecttrace = 0; +- +-/* #include "opcodes.h" */ +-/* +-** Properties of opcodes. The OPFLG_INITIALIZER macro is +-** created by mkopcodeh.awk during compilation. Data is obtained +-** from the comments following the "case OP_xxxx:" statements in +-** the vdbe.c file. ++** To modify this header, edit any of the various lists in that script ++** which specify categories of generated conditionals in this file. + */ +-SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; +- +-/* +-** Name of the default collating sequence +-*/ +-SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY"; + +-/************** End of global.c **********************************************/ +-/************** Begin file status.c ******************************************/ + /* +-** 2008 June 18 ++** 2010 February 23 + ** + ** The author disclaims copyright to this source code. In place of + ** a legal notice, here is a blessing: +@@ -20701,444 +22369,1728 @@ SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY"; + ** + ************************************************************************* + ** +-** This module implements the sqlite3_status() interface and related +-** functionality. +-*/ +-/* #include "sqliteInt.h" */ +-/************** Include vdbeInt.h in the middle of status.c ******************/ +-/************** Begin file vdbeInt.h *****************************************/ +-/* +-** 2003 September 6 +-** +-** 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 is the header file for information that is private to the +-** VDBE. This information used to all be at the top of the single +-** source code file "vdbe.c". When that file became too big (over +-** 6000 lines long) it was split up into several smaller files and +-** this header information was factored out. +-*/ +-#ifndef SQLITE_VDBEINT_H +-#define SQLITE_VDBEINT_H +- +-/* +-** The maximum number of times that a statement will try to reparse +-** itself before giving up and returning SQLITE_SCHEMA. ++** This file implements routines used to report what compile-time options ++** SQLite was built with. + */ +-#ifndef SQLITE_MAX_SCHEMA_RETRY +-# define SQLITE_MAX_SCHEMA_RETRY 50 +-#endif ++#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ + + /* +-** VDBE_DISPLAY_P4 is true or false depending on whether or not the +-** "explain" P4 display logic is enabled. ++** Include the configuration header output by 'configure' if we're using the ++** autoconf-based build + */ +-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ +- || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ +- || defined(SQLITE_ENABLE_BYTECODE_VTAB) +-# define VDBE_DISPLAY_P4 1 +-#else +-# define VDBE_DISPLAY_P4 0 ++#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) ++/* #include "sqlite_cfg.h" */ ++#define SQLITECONFIG_H 1 + #endif + +-/* +-** SQL is translated into a sequence of instructions to be +-** executed by a virtual machine. Each instruction is an instance +-** of the following structure. +-*/ +-typedef struct VdbeOp Op; +- +-/* +-** Boolean values +-*/ +-typedef unsigned Bool; +- +-/* Opaque type used by code in vdbesort.c */ +-typedef struct VdbeSorter VdbeSorter; +- +-/* Elements of the linked list at Vdbe.pAuxData */ +-typedef struct AuxData AuxData; ++/* These macros are provided to "stringify" the value of the define ++** for those options in which the value is meaningful. */ ++#define CTIMEOPT_VAL_(opt) #opt ++#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) + +-/* Types of VDBE cursors */ +-#define CURTYPE_BTREE 0 +-#define CURTYPE_SORTER 1 +-#define CURTYPE_VTAB 2 +-#define CURTYPE_PSEUDO 3 ++/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This ++** option requires a separate macro because legal values contain a single ++** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */ ++#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2 ++#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt) ++/* #include "sqliteInt.h" */ + + /* +-** A VdbeCursor is an superclass (a wrapper) for various cursor objects: ++** An array of names of all compile-time options. This array should ++** be sorted A-Z. + ** +-** * A b-tree cursor +-** - In the main database or in an ephemeral database +-** - On either an index or a table +-** * A sorter +-** * A virtual table +-** * A one-row "pseudotable" stored in a single register ++** This array looks large, but in a typical installation actually uses ++** only a handful of compile-time options, so most times this array is usually ++** rather short and uses little memory space. + */ +-typedef struct VdbeCursor VdbeCursor; +-struct VdbeCursor { +- u8 eCurType; /* One of the CURTYPE_* values above */ +- i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ +- u8 nullRow; /* True if pointing to a row with no data */ +- u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ +- u8 isTable; /* True for rowid tables. False for indexes */ +-#ifdef SQLITE_DEBUG +- u8 seekOp; /* Most recent seek operation on this cursor */ +- u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ +-#endif +- Bool isEphemeral:1; /* True for an ephemeral table */ +- Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ +- Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ +- Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ +- Btree *pBtx; /* Separate file holding temporary table */ +- i64 seqCount; /* Sequence counter */ +- u32 *aAltMap; /* Mapping from table to index column numbers */ +- +- /* Cached OP_Column parse information is only valid if cacheStatus matches +- ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of +- ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that +- ** the cache is out of date. */ +- u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ +- int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0 +- ** if there have been no prior seeks on the cursor. */ +- /* seekResult does not distinguish between "no seeks have ever occurred +- ** on this cursor" and "the most recent seek was an exact match". +- ** For CURTYPE_PSEUDO, seekResult is the register holding the record */ ++static const char * const sqlite3azCompileOpt[] = { + +- /* When a new VdbeCursor is allocated, only the fields above are zeroed. +- ** The fields that follow are uninitialized, and must be individually +- ** initialized prior to first use. */ +- VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ +- union { +- BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */ +- sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ +- VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ +- } uc; +- KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ +- u32 iHdrOffset; /* Offset to next unparsed byte of the header */ +- Pgno pgnoRoot; /* Root page of the open btree cursor */ +- i16 nField; /* Number of fields in the header */ +- u16 nHdrParsed; /* Number of header fields parsed so far */ +- i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ +- u32 *aOffset; /* Pointer to aType[nField] */ +- const u8 *aRow; /* Data for the current row, if all on one page */ +- u32 payloadSize; /* Total number of bytes in the record */ +- u32 szRow; /* Byte available in aRow */ +-#ifdef SQLITE_ENABLE_COLUMN_USED_MASK +- u64 maskUsed; /* Mask of columns used by this cursor */ ++#ifdef SQLITE_32BIT_ROWID ++ "32BIT_ROWID", + #endif +- +- /* 2*nField extra array elements allocated for aType[], beyond the one +- ** static element declared in the structure. nField total array slots for +- ** aType[] and nField+1 array slots for aOffset[] */ +- u32 aType[1]; /* Type values record decode. MUST BE LAST */ +-}; +- +- +-/* +-** A value for VdbeCursor.cacheStatus that means the cache is always invalid. +-*/ +-#define CACHE_STALE 0 +- +-/* +-** When a sub-program is executed (OP_Program), a structure of this type +-** is allocated to store the current value of the program counter, as +-** well as the current memory cell array and various other frame specific +-** values stored in the Vdbe struct. When the sub-program is finished, +-** these values are copied back to the Vdbe from the VdbeFrame structure, +-** restoring the state of the VM to as it was before the sub-program +-** began executing. +-** +-** The memory for a VdbeFrame object is allocated and managed by a memory +-** cell in the parent (calling) frame. When the memory cell is deleted or +-** overwritten, the VdbeFrame object is not freed immediately. Instead, it +-** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame +-** list is deleted when the VM is reset in VdbeHalt(). The reason for doing +-** this instead of deleting the VdbeFrame immediately is to avoid recursive +-** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the +-** child frame are released. +-** +-** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is +-** set to NULL if the currently executing frame is the main program. +-*/ +-typedef struct VdbeFrame VdbeFrame; +-struct VdbeFrame { +- Vdbe *v; /* VM this frame belongs to */ +- VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ +- Op *aOp; /* Program instructions for parent frame */ +- i64 *anExec; /* Event counters from parent frame */ +- Mem *aMem; /* Array of memory cells for parent frame */ +- VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ +- u8 *aOnce; /* Bitmask used by OP_Once */ +- void *token; /* Copy of SubProgram.token */ +- i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ +- AuxData *pAuxData; /* Linked list of auxdata allocations */ +-#if SQLITE_DEBUG +- u32 iFrameMagic; /* magic number for sanity checking */ ++#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC ++ "4_BYTE_ALIGNED_MALLOC", + #endif +- int nCursor; /* Number of entries in apCsr */ +- int pc; /* Program Counter in parent (calling) frame */ +- int nOp; /* Size of aOp array */ +- int nMem; /* Number of entries in aMem */ +- int nChildMem; /* Number of memory cells for child frame */ +- int nChildCsr; /* Number of cursors for child frame */ +- int nChange; /* Statement changes (Vdbe.nChange) */ +- int nDbChange; /* Value of db->nChange */ +-}; +- +-/* Magic number for sanity checking on VdbeFrame objects */ +-#define SQLITE_FRAME_MAGIC 0x879fb71e +- +-/* +-** Return a pointer to the array of registers allocated for use +-** by a VdbeFrame. +-*/ +-#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) +- +-/* +-** Internally, the vdbe manipulates nearly all SQL values as Mem +-** structures. Each Mem struct may cache multiple representations (string, +-** integer etc.) of the same value. +-*/ +-struct sqlite3_value { +- union MemValue { +- double r; /* Real value used when MEM_Real is set in flags */ +- i64 i; /* Integer value used when MEM_Int is set in flags */ +- int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ +- const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ +- FuncDef *pDef; /* Used only when flags==MEM_Agg */ +- } u; +- u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ +- u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ +- u8 eSubtype; /* Subtype for this value */ +- int n; /* Number of characters in string value, excluding '\0' */ +- char *z; /* String or BLOB value */ +- /* ShallowCopy only needs to copy the information above */ +- char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ +- int szMalloc; /* Size of the zMalloc allocation */ +- u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ +- sqlite3 *db; /* The associated database connection */ +- void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ +-#ifdef SQLITE_DEBUG +- Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ +- u16 mScopyFlags; /* flags value immediately after the shallow copy */ ++#ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN ++# if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 ++ "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), ++# endif + #endif +-}; +- +-/* +-** Size of struct Mem not including the Mem.zMalloc member or anything that +-** follows. +-*/ +-#define MEMCELLSIZE offsetof(Mem,zMalloc) +- +-/* One or more of the following flags are set to indicate the validOK +-** representations of the value stored in the Mem struct. +-** +-** If the MEM_Null flag is set, then the value is an SQL NULL value. +-** For a pointer type created using sqlite3_bind_pointer() or +-** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. +-** +-** If the MEM_Str flag is set then Mem.z points at a string representation. +-** Usually this is encoded in the same unicode encoding as the main +-** database (see below for exceptions). If the MEM_Term flag is also +-** set, then the string is nul terminated. The MEM_Int and MEM_Real +-** flags may coexist with the MEM_Str flag. +-*/ +-#define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ +-#define MEM_Str 0x0002 /* Value is a string */ +-#define MEM_Int 0x0004 /* Value is an integer */ +-#define MEM_Real 0x0008 /* Value is a real number */ +-#define MEM_Blob 0x0010 /* Value is a BLOB */ +-#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */ +-#define MEM_AffMask 0x003f /* Mask of affinity bits */ +-#define MEM_FromBind 0x0040 /* Value originates from sqlite3_bind() */ +-#define MEM_Undefined 0x0080 /* Value is undefined */ +-#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ +-#define MEM_TypeMask 0xc1bf /* Mask of type bits */ +- +- +-/* Whenever Mem contains a valid string or blob representation, one of +-** the following flags must be set to determine the memory management +-** policy for Mem.z. The MEM_Term flag tells us whether or not the +-** string is \000 or \u0000 terminated +-*/ +-#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ +-#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */ +-#define MEM_Static 0x0800 /* Mem.z points to a static string */ +-#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ +-#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ +-#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ +-#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */ +-#ifdef SQLITE_OMIT_INCRBLOB +- #undef MEM_Zero +- #define MEM_Zero 0x0000 ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ "ALLOW_ROWID_IN_VIEW", + #endif +- +-/* Return TRUE if Mem X contains dynamically allocated content - anything +-** that needs to be deallocated to avoid a leak. +-*/ +-#define VdbeMemDynamic(X) \ +- (((X)->flags&(MEM_Agg|MEM_Dyn))!=0) +- +-/* +-** Clear any existing type flags from a Mem and replace them with f +-*/ +-#define MemSetTypeFlag(p, f) \ +- ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) +- +-/* +-** True if Mem X is a NULL-nochng type. +-*/ +-#define MemNullNochng(X) \ +- (((X)->flags&MEM_TypeMask)==(MEM_Null|MEM_Zero) \ +- && (X)->n==0 && (X)->u.nZero==0) +- +-/* +-** Return true if a memory cell is not marked as invalid. This macro +-** is for use inside assert() statements only. +-*/ +-#ifdef SQLITE_DEBUG +-#define memIsValid(M) ((M)->flags & MEM_Undefined)==0 ++#ifdef SQLITE_ALLOW_URI_AUTHORITY ++ "ALLOW_URI_AUTHORITY", + #endif +- +-/* +-** Each auxiliary data pointer stored by a user defined function +-** implementation calling sqlite3_set_auxdata() is stored in an instance +-** of this structure. All such structures associated with a single VM +-** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed +-** when the VM is halted (if not before). +-*/ +-struct AuxData { +- int iAuxOp; /* Instruction number of OP_Function opcode */ +- int iAuxArg; /* Index of function argument. */ +- void *pAux; /* Aux data pointer */ +- void (*xDeleteAux)(void*); /* Destructor for the aux data */ +- AuxData *pNextAux; /* Next element in list */ +-}; +- +-/* +-** The "context" argument for an installable function. A pointer to an +-** instance of this structure is the first argument to the routines used +-** implement the SQL functions. +-** +-** There is a typedef for this structure in sqlite.h. So all routines, +-** even the public interface to SQLite, can use a pointer to this structure. +-** But this file is the only place where the internal details of this +-** structure are known. +-** +-** This structure is defined inside of vdbeInt.h because it uses substructures +-** (Mem) which are only defined there. +-*/ +-struct sqlite3_context { +- Mem *pOut; /* The return value is stored here */ +- FuncDef *pFunc; /* Pointer to function information */ +- Mem *pMem; /* Memory cell used to store aggregate context */ +- Vdbe *pVdbe; /* The VM that owns this context */ +- int iOp; /* Instruction number of OP_Function */ +- int isError; /* Error code returned by the function. */ +- u8 skipFlag; /* Skip accumulator loading if true */ +- u8 argc; /* Number of arguments */ +- sqlite3_value *argv[1]; /* Argument set */ +-}; +- +-/* A bitfield type for use inside of structures. Always follow with :N where +-** N is the number of bits. +-*/ +-typedef unsigned bft; /* Bit Field Type */ +- +-/* The ScanStatus object holds a single value for the +-** sqlite3_stmt_scanstatus() interface. +-*/ +-typedef struct ScanStatus ScanStatus; +-struct ScanStatus { +- int addrExplain; /* OP_Explain for loop */ +- int addrLoop; /* Address of "loops" counter */ +- int addrVisit; /* Address of "rows visited" counter */ +- int iSelectID; /* The "Select-ID" for this loop */ +- LogEst nEst; /* Estimated output rows per loop */ +- char *zName; /* Name of table or index */ +-}; +- +-/* The DblquoteStr object holds the text of a double-quoted +-** string for a prepared statement. A linked list of these objects +-** is constructed during statement parsing and is held on Vdbe.pDblStr. +-** When computing a normalized SQL statement for an SQL statement, that +-** list is consulted for each double-quoted identifier to see if the +-** identifier should really be a string literal. +-*/ +-typedef struct DblquoteStr DblquoteStr; +-struct DblquoteStr { +- DblquoteStr *pNextStr; /* Next string literal in the list */ +- char z[8]; /* Dequoted value for the string */ +-}; +- +-/* +-** An instance of the virtual machine. This structure contains the complete +-** state of the virtual machine. +-** +-** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() +-** is really a pointer to an instance of this structure. +-*/ +-struct Vdbe { +- sqlite3 *db; /* The database connection that owns this statement */ +- Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ +- Parse *pParse; /* Parsing context used to create this Vdbe */ +- ynVar nVar; /* Number of entries in aVar[] */ +- u32 magic; /* Magic number for sanity checking */ +- int nMem; /* Number of memory locations currently allocated */ +- int nCursor; /* Number of slots in apCsr[] */ +- u32 cacheCtr; /* VdbeCursor row cache generation counter */ +- int pc; /* The program counter */ +- int rc; /* Value to return */ +- int nChange; /* Number of db changes made since last reset */ +- int iStatement; /* Statement number (or 0 if has no opened stmt) */ +- i64 iCurrentTime; /* Value of julianday('now') for this statement */ +- i64 nFkConstraint; /* Number of imm. FK constraints this VM */ +- i64 nStmtDefCons; /* Number of def. constraints when stmt started */ +- i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ +- Mem *aMem; /* The memory locations */ +- Mem **apArg; /* Arguments to currently executing user function */ +- VdbeCursor **apCsr; /* One element of this array for each open cursor */ +- Mem *aVar; /* Values for the OP_Variable opcode. */ +- +- /* When allocating a new Vdbe object, all of the fields below should be +- ** initialized to zero or NULL */ +- +- Op *aOp; /* Space to hold the virtual machine's program */ +- int nOp; /* Number of instructions in the program */ +- int nOpAlloc; /* Slots allocated for aOp[] */ +- Mem *aColName; /* Column names to return */ +- Mem *pResultSet; /* Pointer to an array of results */ +- char *zErrMsg; /* Error message written here */ +- VList *pVList; /* Name of variables */ +-#ifndef SQLITE_OMIT_TRACE +- i64 startTime; /* Time when query started - used for profiling */ ++#ifdef SQLITE_ATOMIC_INTRINSICS ++ "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS), + #endif +-#ifdef SQLITE_DEBUG +- int rcApp; /* errcode set by sqlite3_result_error_code() */ +- u32 nWrite; /* Number of write operations that have occurred */ ++#ifdef SQLITE_BITMASK_TYPE ++ "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), + #endif +- u16 nResColumn; /* Number of columns in one row of the result set */ +- u8 errorAction; /* Recovery action to do in case of an error */ +- u8 minWriteFileFormat; /* Minimum file format for writable database files */ +- u8 prepFlags; /* SQLITE_PREPARE_* flags */ +- u8 doingRerun; /* True if rerunning after an auto-reprepare */ +- bft expired:2; /* 1: recompile VM immediately 2: when convenient */ +- bft explain:2; /* True if EXPLAIN present on SQL command */ +- bft changeCntOn:1; /* True to update the change-counter */ +- bft runOnlyOnce:1; /* Automatically expire on reset */ ++#ifdef SQLITE_BUG_COMPATIBLE_20160819 ++ "BUG_COMPATIBLE_20160819", ++#endif ++#ifdef SQLITE_BUG_COMPATIBLE_20250510 ++ "BUG_COMPATIBLE_20250510", ++#endif ++#ifdef SQLITE_CASE_SENSITIVE_LIKE ++ "CASE_SENSITIVE_LIKE", ++#endif ++#ifdef SQLITE_CHECK_PAGES ++ "CHECK_PAGES", ++#endif ++#if defined(__clang__) && defined(__clang_major__) ++ "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." ++ CTIMEOPT_VAL(__clang_minor__) "." ++ CTIMEOPT_VAL(__clang_patchlevel__), ++#elif defined(_MSC_VER) ++ "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), ++#elif defined(__GNUC__) && defined(__VERSION__) ++ "COMPILER=gcc-" __VERSION__, ++#endif ++#ifdef SQLITE_COVERAGE_TEST ++ "COVERAGE_TEST", ++#endif ++#ifdef SQLITE_DEBUG ++ "DEBUG", ++#endif ++#ifdef SQLITE_DEFAULT_AUTOMATIC_INDEX ++ "DEFAULT_AUTOMATIC_INDEX", ++#endif ++#ifdef SQLITE_DEFAULT_AUTOVACUUM ++ "DEFAULT_AUTOVACUUM", ++#endif ++#ifdef SQLITE_DEFAULT_CACHE_SIZE ++ "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), ++#endif ++#ifdef SQLITE_DEFAULT_CKPTFULLFSYNC ++ "DEFAULT_CKPTFULLFSYNC", ++#endif ++#ifdef SQLITE_DEFAULT_FILE_FORMAT ++ "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT), ++#endif ++#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS ++ "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS), ++#endif ++#ifdef SQLITE_DEFAULT_FOREIGN_KEYS ++ "DEFAULT_FOREIGN_KEYS", ++#endif ++#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT ++ "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT), ++#endif ++#ifdef SQLITE_DEFAULT_LOCKING_MODE ++ "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), ++#endif ++#ifdef SQLITE_DEFAULT_LOOKASIDE ++ "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE), ++#endif ++#ifdef SQLITE_DEFAULT_MEMSTATUS ++# if SQLITE_DEFAULT_MEMSTATUS != 1 ++ "DEFAULT_MEMSTATUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_MEMSTATUS), ++# endif ++#endif ++#ifdef SQLITE_DEFAULT_MMAP_SIZE ++ "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), ++#endif ++#ifdef SQLITE_DEFAULT_PAGE_SIZE ++ "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE), ++#endif ++#ifdef SQLITE_DEFAULT_PCACHE_INITSZ ++ "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ), ++#endif ++#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS ++ "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS), ++#endif ++#ifdef SQLITE_DEFAULT_RECURSIVE_TRIGGERS ++ "DEFAULT_RECURSIVE_TRIGGERS", ++#endif ++#ifdef SQLITE_DEFAULT_ROWEST ++ "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST), ++#endif ++#ifdef SQLITE_DEFAULT_SECTOR_SIZE ++ "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE), ++#endif ++#ifdef SQLITE_DEFAULT_SYNCHRONOUS ++ "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), ++#endif ++#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT ++ "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT), ++#endif ++#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS ++ "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), ++#endif ++#ifdef SQLITE_DEFAULT_WORKER_THREADS ++ "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS), ++#endif ++#ifdef SQLITE_DIRECT_OVERFLOW_READ ++ "DIRECT_OVERFLOW_READ", ++#endif ++#ifdef SQLITE_DISABLE_DIRSYNC ++ "DISABLE_DIRSYNC", ++#endif ++#ifdef SQLITE_DISABLE_FTS3_UNICODE ++ "DISABLE_FTS3_UNICODE", ++#endif ++#ifdef SQLITE_DISABLE_FTS4_DEFERRED ++ "DISABLE_FTS4_DEFERRED", ++#endif ++#ifdef SQLITE_DISABLE_INTRINSIC ++ "DISABLE_INTRINSIC", ++#endif ++#ifdef SQLITE_DISABLE_LFS ++ "DISABLE_LFS", ++#endif ++#ifdef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS ++ "DISABLE_PAGECACHE_OVERFLOW_STATS", ++#endif ++#ifdef SQLITE_DISABLE_SKIPAHEAD_DISTINCT ++ "DISABLE_SKIPAHEAD_DISTINCT", ++#endif ++#ifdef SQLITE_DQS ++ "DQS=" CTIMEOPT_VAL(SQLITE_DQS), ++#endif ++#ifdef SQLITE_ENABLE_8_3_NAMES ++ "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), ++#endif ++#ifdef SQLITE_ENABLE_API_ARMOR ++ "ENABLE_API_ARMOR", ++#endif ++#ifdef SQLITE_ENABLE_ATOMIC_WRITE ++ "ENABLE_ATOMIC_WRITE", ++#endif ++#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE ++ "ENABLE_BATCH_ATOMIC_WRITE", ++#endif ++#ifdef SQLITE_ENABLE_BYTECODE_VTAB ++ "ENABLE_BYTECODE_VTAB", ++#endif ++#ifdef SQLITE_ENABLE_CEROD ++ "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), ++#endif ++#ifdef SQLITE_ENABLE_COLUMN_METADATA ++ "ENABLE_COLUMN_METADATA", ++#endif ++#ifdef SQLITE_ENABLE_COLUMN_USED_MASK ++ "ENABLE_COLUMN_USED_MASK", ++#endif ++#ifdef SQLITE_ENABLE_COSTMULT ++ "ENABLE_COSTMULT", ++#endif ++#ifdef SQLITE_ENABLE_CURSOR_HINTS ++ "ENABLE_CURSOR_HINTS", ++#endif ++#ifdef SQLITE_ENABLE_DBPAGE_VTAB ++ "ENABLE_DBPAGE_VTAB", ++#endif ++#ifdef SQLITE_ENABLE_DBSTAT_VTAB ++ "ENABLE_DBSTAT_VTAB", ++#endif ++#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT ++ "ENABLE_EXPENSIVE_ASSERT", ++#endif ++#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS ++ "ENABLE_EXPLAIN_COMMENTS", ++#endif ++#ifdef SQLITE_ENABLE_FTS3 ++ "ENABLE_FTS3", ++#endif ++#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS ++ "ENABLE_FTS3_PARENTHESIS", ++#endif ++#ifdef SQLITE_ENABLE_FTS3_TOKENIZER ++ "ENABLE_FTS3_TOKENIZER", ++#endif ++#ifdef SQLITE_ENABLE_FTS4 ++ "ENABLE_FTS4", ++#endif ++#ifdef SQLITE_ENABLE_FTS5 ++ "ENABLE_FTS5", ++#endif ++#ifdef SQLITE_ENABLE_GEOPOLY ++ "ENABLE_GEOPOLY", ++#endif ++#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS ++ "ENABLE_HIDDEN_COLUMNS", ++#endif ++#ifdef SQLITE_ENABLE_ICU ++ "ENABLE_ICU", ++#endif ++#ifdef SQLITE_ENABLE_IOTRACE ++ "ENABLE_IOTRACE", ++#endif ++#ifdef SQLITE_ENABLE_LOAD_EXTENSION ++ "ENABLE_LOAD_EXTENSION", ++#endif ++#ifdef SQLITE_ENABLE_LOCKING_STYLE ++ "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), ++#endif ++#ifdef SQLITE_ENABLE_MATH_FUNCTIONS ++ "ENABLE_MATH_FUNCTIONS", ++#endif ++#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT ++ "ENABLE_MEMORY_MANAGEMENT", ++#endif ++#ifdef SQLITE_ENABLE_MEMSYS3 ++ "ENABLE_MEMSYS3", ++#endif ++#ifdef SQLITE_ENABLE_MEMSYS5 ++ "ENABLE_MEMSYS5", ++#endif ++#ifdef SQLITE_ENABLE_MULTIPLEX ++ "ENABLE_MULTIPLEX", ++#endif ++#ifdef SQLITE_ENABLE_NORMALIZE ++ "ENABLE_NORMALIZE", ++#endif ++#ifdef SQLITE_ENABLE_NULL_TRIM ++ "ENABLE_NULL_TRIM", ++#endif ++#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC ++ "ENABLE_OFFSET_SQL_FUNC", ++#endif ++#ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES ++ "ENABLE_ORDERED_SET_AGGREGATES", ++#endif ++#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK ++ "ENABLE_OVERSIZE_CELL_CHECK", ++#endif ++#ifdef SQLITE_ENABLE_PREUPDATE_HOOK ++ "ENABLE_PREUPDATE_HOOK", ++#endif ++#ifdef SQLITE_ENABLE_QPSG ++ "ENABLE_QPSG", ++#endif ++#ifdef SQLITE_ENABLE_RBU ++ "ENABLE_RBU", ++#endif ++#ifdef SQLITE_ENABLE_RTREE ++ "ENABLE_RTREE", ++#endif ++#ifdef SQLITE_ENABLE_SESSION ++ "ENABLE_SESSION", ++#endif ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ "ENABLE_SETLK_TIMEOUT", ++#endif ++#ifdef SQLITE_ENABLE_SNAPSHOT ++ "ENABLE_SNAPSHOT", ++#endif ++#ifdef SQLITE_ENABLE_SORTER_REFERENCES ++ "ENABLE_SORTER_REFERENCES", ++#endif ++#ifdef SQLITE_ENABLE_SQLLOG ++ "ENABLE_SQLLOG", ++#endif ++#ifdef SQLITE_ENABLE_STAT4 ++ "ENABLE_STAT4", ++#endif ++#ifdef SQLITE_ENABLE_STMTVTAB ++ "ENABLE_STMTVTAB", ++#endif ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ "ENABLE_STMT_SCANSTATUS", ++#endif ++#ifdef SQLITE_ENABLE_TREETRACE ++ "ENABLE_TREETRACE", ++#endif ++#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION ++ "ENABLE_UNKNOWN_SQL_FUNCTION", ++#endif ++#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY ++ "ENABLE_UNLOCK_NOTIFY", ++#endif ++#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT ++ "ENABLE_UPDATE_DELETE_LIMIT", ++#endif ++#ifdef SQLITE_ENABLE_URI_00_ERROR ++ "ENABLE_URI_00_ERROR", ++#endif ++#ifdef SQLITE_ENABLE_VFSTRACE ++ "ENABLE_VFSTRACE", ++#endif ++#ifdef SQLITE_ENABLE_WHERETRACE ++ "ENABLE_WHERETRACE", ++#endif ++#ifdef SQLITE_ENABLE_ZIPVFS ++ "ENABLE_ZIPVFS", ++#endif ++#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS ++ "EXPLAIN_ESTIMATED_ROWS", ++#endif ++#ifdef SQLITE_EXTRA_AUTOEXT ++ "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT), ++#endif ++#ifdef SQLITE_EXTRA_IFNULLROW ++ "EXTRA_IFNULLROW", ++#endif ++#ifdef SQLITE_EXTRA_INIT ++ "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), ++#endif ++#ifdef SQLITE_EXTRA_INIT_MUTEXED ++ "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED), ++#endif ++#ifdef SQLITE_EXTRA_SHUTDOWN ++ "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), ++#endif ++#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH ++ "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), ++#endif ++#ifdef SQLITE_FTS5_ENABLE_TEST_MI ++ "FTS5_ENABLE_TEST_MI", ++#endif ++#ifdef SQLITE_FTS5_NO_WITHOUT_ROWID ++ "FTS5_NO_WITHOUT_ROWID", ++#endif ++#if HAVE_ISNAN || SQLITE_HAVE_ISNAN ++ "HAVE_ISNAN", ++#endif ++#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX ++# if SQLITE_HOMEGROWN_RECURSIVE_MUTEX != 1 ++ "HOMEGROWN_RECURSIVE_MUTEX=" CTIMEOPT_VAL(SQLITE_HOMEGROWN_RECURSIVE_MUTEX), ++# endif ++#endif ++#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS ++ "IGNORE_AFP_LOCK_ERRORS", ++#endif ++#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS ++ "IGNORE_FLOCK_LOCK_ERRORS", ++#endif ++#ifdef SQLITE_INLINE_MEMCPY ++ "INLINE_MEMCPY", ++#endif ++#ifdef SQLITE_INT64_TYPE ++ "INT64_TYPE", ++#endif ++#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX ++ "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), ++#endif ++#ifdef SQLITE_LEGACY_JSON_VALID ++ "LEGACY_JSON_VALID", ++#endif ++#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS ++ "LIKE_DOESNT_MATCH_BLOBS", ++#endif ++#ifdef SQLITE_LOCK_TRACE ++ "LOCK_TRACE", ++#endif ++#ifdef SQLITE_LOG_CACHE_SPILL ++ "LOG_CACHE_SPILL", ++#endif ++#ifdef SQLITE_MALLOC_SOFT_LIMIT ++ "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT), ++#endif ++#ifdef SQLITE_MAX_ATTACHED ++ "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED), ++#endif ++#ifdef SQLITE_MAX_COLUMN ++ "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN), ++#endif ++#ifdef SQLITE_MAX_COMPOUND_SELECT ++ "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT), ++#endif ++#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE ++ "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE), ++#endif ++#ifdef SQLITE_MAX_EXPR_DEPTH ++ "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH), ++#endif ++#ifdef SQLITE_MAX_FUNCTION_ARG ++ "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG), ++#endif ++#ifdef SQLITE_MAX_LENGTH ++ "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH), ++#endif ++#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH ++ "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH), ++#endif ++#ifdef SQLITE_MAX_MEMORY ++ "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY), ++#endif ++#ifdef SQLITE_MAX_MMAP_SIZE ++ "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), ++#endif ++#ifdef SQLITE_MAX_MMAP_SIZE_ ++ "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_), ++#endif ++#ifdef SQLITE_MAX_PAGE_COUNT ++ "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT), ++#endif ++#ifdef SQLITE_MAX_PAGE_SIZE ++ "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE), ++#endif ++#ifdef SQLITE_MAX_SCHEMA_RETRY ++ "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), ++#endif ++#ifdef SQLITE_MAX_SQL_LENGTH ++ "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH), ++#endif ++#ifdef SQLITE_MAX_TRIGGER_DEPTH ++ "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH), ++#endif ++#ifdef SQLITE_MAX_VARIABLE_NUMBER ++ "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER), ++#endif ++#ifdef SQLITE_MAX_VDBE_OP ++ "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP), ++#endif ++#ifdef SQLITE_MAX_WORKER_THREADS ++ "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS), ++#endif ++#ifdef SQLITE_MEMDEBUG ++ "MEMDEBUG", ++#endif ++#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT ++ "MIXED_ENDIAN_64BIT_FLOAT", ++#endif ++#ifdef SQLITE_MMAP_READWRITE ++ "MMAP_READWRITE", ++#endif ++#ifdef SQLITE_MUTEX_NOOP ++ "MUTEX_NOOP", ++#endif ++#ifdef SQLITE_MUTEX_OMIT ++ "MUTEX_OMIT", ++#endif ++#ifdef SQLITE_MUTEX_PTHREADS ++ "MUTEX_PTHREADS", ++#endif ++#ifdef SQLITE_MUTEX_W32 ++ "MUTEX_W32", ++#endif ++#ifdef SQLITE_NEED_ERR_NAME ++ "NEED_ERR_NAME", ++#endif ++#ifdef SQLITE_NO_SYNC ++ "NO_SYNC", ++#endif ++#ifdef SQLITE_OMIT_ALTERTABLE ++ "OMIT_ALTERTABLE", ++#endif ++#ifdef SQLITE_OMIT_ANALYZE ++ "OMIT_ANALYZE", ++#endif ++#ifdef SQLITE_OMIT_ATTACH ++ "OMIT_ATTACH", ++#endif ++#ifdef SQLITE_OMIT_AUTHORIZATION ++ "OMIT_AUTHORIZATION", ++#endif ++#ifdef SQLITE_OMIT_AUTOINCREMENT ++ "OMIT_AUTOINCREMENT", ++#endif ++#ifdef SQLITE_OMIT_AUTOINIT ++ "OMIT_AUTOINIT", ++#endif ++#ifdef SQLITE_OMIT_AUTOMATIC_INDEX ++ "OMIT_AUTOMATIC_INDEX", ++#endif ++#ifdef SQLITE_OMIT_AUTORESET ++ "OMIT_AUTORESET", ++#endif ++#ifdef SQLITE_OMIT_AUTOVACUUM ++ "OMIT_AUTOVACUUM", ++#endif ++#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION ++ "OMIT_BETWEEN_OPTIMIZATION", ++#endif ++#ifdef SQLITE_OMIT_BLOB_LITERAL ++ "OMIT_BLOB_LITERAL", ++#endif ++#ifdef SQLITE_OMIT_CAST ++ "OMIT_CAST", ++#endif ++#ifdef SQLITE_OMIT_CHECK ++ "OMIT_CHECK", ++#endif ++#ifdef SQLITE_OMIT_COMPLETE ++ "OMIT_COMPLETE", ++#endif ++#ifdef SQLITE_OMIT_COMPOUND_SELECT ++ "OMIT_COMPOUND_SELECT", ++#endif ++#ifdef SQLITE_OMIT_CONFLICT_CLAUSE ++ "OMIT_CONFLICT_CLAUSE", ++#endif ++#ifdef SQLITE_OMIT_CTE ++ "OMIT_CTE", ++#endif ++#if defined(SQLITE_OMIT_DATETIME_FUNCS) || defined(SQLITE_OMIT_FLOATING_POINT) ++ "OMIT_DATETIME_FUNCS", ++#endif ++#ifdef SQLITE_OMIT_DECLTYPE ++ "OMIT_DECLTYPE", ++#endif ++#ifdef SQLITE_OMIT_DEPRECATED ++ "OMIT_DEPRECATED", ++#endif ++#ifdef SQLITE_OMIT_DESERIALIZE ++ "OMIT_DESERIALIZE", ++#endif ++#ifdef SQLITE_OMIT_DISKIO ++ "OMIT_DISKIO", ++#endif ++#ifdef SQLITE_OMIT_EXPLAIN ++ "OMIT_EXPLAIN", ++#endif ++#ifdef SQLITE_OMIT_FLAG_PRAGMAS ++ "OMIT_FLAG_PRAGMAS", ++#endif ++#ifdef SQLITE_OMIT_FLOATING_POINT ++ "OMIT_FLOATING_POINT", ++#endif ++#ifdef SQLITE_OMIT_FOREIGN_KEY ++ "OMIT_FOREIGN_KEY", ++#endif ++#ifdef SQLITE_OMIT_GET_TABLE ++ "OMIT_GET_TABLE", ++#endif ++#ifdef SQLITE_OMIT_HEX_INTEGER ++ "OMIT_HEX_INTEGER", ++#endif ++#ifdef SQLITE_OMIT_INCRBLOB ++ "OMIT_INCRBLOB", ++#endif ++#ifdef SQLITE_OMIT_INTEGRITY_CHECK ++ "OMIT_INTEGRITY_CHECK", ++#endif ++#ifdef SQLITE_OMIT_INTROSPECTION_PRAGMAS ++ "OMIT_INTROSPECTION_PRAGMAS", ++#endif ++#ifdef SQLITE_OMIT_JSON ++ "OMIT_JSON", ++#endif ++#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION ++ "OMIT_LIKE_OPTIMIZATION", ++#endif ++#ifdef SQLITE_OMIT_LOAD_EXTENSION ++ "OMIT_LOAD_EXTENSION", ++#endif ++#ifdef SQLITE_OMIT_LOCALTIME ++ "OMIT_LOCALTIME", ++#endif ++#ifdef SQLITE_OMIT_LOOKASIDE ++ "OMIT_LOOKASIDE", ++#endif ++#ifdef SQLITE_OMIT_MEMORYDB ++ "OMIT_MEMORYDB", ++#endif ++#ifdef SQLITE_OMIT_OR_OPTIMIZATION ++ "OMIT_OR_OPTIMIZATION", ++#endif ++#ifdef SQLITE_OMIT_PAGER_PRAGMAS ++ "OMIT_PAGER_PRAGMAS", ++#endif ++#ifdef SQLITE_OMIT_PARSER_TRACE ++ "OMIT_PARSER_TRACE", ++#endif ++#ifdef SQLITE_OMIT_POPEN ++ "OMIT_POPEN", ++#endif ++#ifdef SQLITE_OMIT_PRAGMA ++ "OMIT_PRAGMA", ++#endif ++#ifdef SQLITE_OMIT_PROGRESS_CALLBACK ++ "OMIT_PROGRESS_CALLBACK", ++#endif ++#ifdef SQLITE_OMIT_QUICKBALANCE ++ "OMIT_QUICKBALANCE", ++#endif ++#ifdef SQLITE_OMIT_REINDEX ++ "OMIT_REINDEX", ++#endif ++#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS ++ "OMIT_SCHEMA_PRAGMAS", ++#endif ++#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS ++ "OMIT_SCHEMA_VERSION_PRAGMAS", ++#endif ++#ifdef SQLITE_OMIT_SEH ++ "OMIT_SEH", ++#endif ++#ifdef SQLITE_OMIT_SHARED_CACHE ++ "OMIT_SHARED_CACHE", ++#endif ++#ifdef SQLITE_OMIT_SHUTDOWN_DIRECTORIES ++ "OMIT_SHUTDOWN_DIRECTORIES", ++#endif ++#ifdef SQLITE_OMIT_SUBQUERY ++ "OMIT_SUBQUERY", ++#endif ++#ifdef SQLITE_OMIT_TCL_VARIABLE ++ "OMIT_TCL_VARIABLE", ++#endif ++#ifdef SQLITE_OMIT_TEMPDB ++ "OMIT_TEMPDB", ++#endif ++#ifdef SQLITE_OMIT_TEST_CONTROL ++ "OMIT_TEST_CONTROL", ++#endif ++#ifdef SQLITE_OMIT_TRACE ++# if SQLITE_OMIT_TRACE != 1 ++ "OMIT_TRACE=" CTIMEOPT_VAL(SQLITE_OMIT_TRACE), ++# endif ++#endif ++#ifdef SQLITE_OMIT_TRIGGER ++ "OMIT_TRIGGER", ++#endif ++#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION ++ "OMIT_TRUNCATE_OPTIMIZATION", ++#endif ++#ifdef SQLITE_OMIT_UTF16 ++ "OMIT_UTF16", ++#endif ++#ifdef SQLITE_OMIT_VACUUM ++ "OMIT_VACUUM", ++#endif ++#ifdef SQLITE_OMIT_VIEW ++ "OMIT_VIEW", ++#endif ++#ifdef SQLITE_OMIT_VIRTUALTABLE ++ "OMIT_VIRTUALTABLE", ++#endif ++#ifdef SQLITE_OMIT_WAL ++ "OMIT_WAL", ++#endif ++#ifdef SQLITE_OMIT_WSD ++ "OMIT_WSD", ++#endif ++#ifdef SQLITE_OMIT_XFER_OPT ++ "OMIT_XFER_OPT", ++#endif ++#ifdef SQLITE_PERFORMANCE_TRACE ++ "PERFORMANCE_TRACE", ++#endif ++#ifdef SQLITE_POWERSAFE_OVERWRITE ++# if SQLITE_POWERSAFE_OVERWRITE != 1 ++ "POWERSAFE_OVERWRITE=" CTIMEOPT_VAL(SQLITE_POWERSAFE_OVERWRITE), ++# endif ++#endif ++#ifdef SQLITE_PREFER_PROXY_LOCKING ++ "PREFER_PROXY_LOCKING", ++#endif ++#ifdef SQLITE_PROXY_DEBUG ++ "PROXY_DEBUG", ++#endif ++#ifdef SQLITE_REVERSE_UNORDERED_SELECTS ++ "REVERSE_UNORDERED_SELECTS", ++#endif ++#ifdef SQLITE_RTREE_INT_ONLY ++ "RTREE_INT_ONLY", ++#endif ++#ifdef SQLITE_SECURE_DELETE ++ "SECURE_DELETE", ++#endif ++#ifdef SQLITE_SMALL_STACK ++ "SMALL_STACK", ++#endif ++#ifdef SQLITE_SORTER_PMASZ ++ "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), ++#endif ++#ifdef SQLITE_SOUNDEX ++ "SOUNDEX", ++#endif ++#ifdef SQLITE_STAT4_SAMPLES ++ "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES), ++#endif ++#ifdef SQLITE_STMTJRNL_SPILL ++ "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), ++#endif ++#ifdef SQLITE_SUBSTR_COMPATIBILITY ++ "SUBSTR_COMPATIBILITY", ++#endif ++#if (!defined(SQLITE_WIN32_MALLOC) \ ++ && !defined(SQLITE_ZERO_MALLOC) \ ++ && !defined(SQLITE_MEMDEBUG) \ ++ ) || defined(SQLITE_SYSTEM_MALLOC) ++ "SYSTEM_MALLOC", ++#endif ++#ifdef SQLITE_TCL ++ "TCL", ++#endif ++#ifdef SQLITE_TEMP_STORE ++ "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), ++#endif ++#ifdef SQLITE_TEST ++ "TEST", ++#endif ++#if defined(SQLITE_THREADSAFE) ++ "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), ++#elif defined(THREADSAFE) ++ "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), ++#else ++ "THREADSAFE=1", ++#endif ++#ifdef SQLITE_UNLINK_AFTER_CLOSE ++ "UNLINK_AFTER_CLOSE", ++#endif ++#ifdef SQLITE_UNTESTABLE ++ "UNTESTABLE", ++#endif ++#ifdef SQLITE_USE_ALLOCA ++ "USE_ALLOCA", ++#endif ++#ifdef SQLITE_USE_FCNTL_TRACE ++ "USE_FCNTL_TRACE", ++#endif ++#ifdef SQLITE_USE_URI ++ "USE_URI", ++#endif ++#ifdef SQLITE_VDBE_COVERAGE ++ "VDBE_COVERAGE", ++#endif ++#ifdef SQLITE_WIN32_MALLOC ++ "WIN32_MALLOC", ++#endif ++#ifdef SQLITE_ZERO_MALLOC ++ "ZERO_MALLOC", ++#endif ++ ++} ; ++ ++SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){ ++ *pnOpt = sizeof(sqlite3azCompileOpt) / sizeof(sqlite3azCompileOpt[0]); ++ return (const char**)sqlite3azCompileOpt; ++} ++ ++#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ ++ ++/************** End of ctime.c ***********************************************/ ++/************** Begin file global.c ******************************************/ ++/* ++** 2008 June 13 ++** ++** 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 contains definitions of global variables and constants. ++*/ ++/* #include "sqliteInt.h" */ ++ ++/* An array to map all upper-case characters into their corresponding ++** lower-case character. ++** ++** SQLite only considers US-ASCII (or EBCDIC) characters. We do not ++** handle case conversions for the UTF character set since the tables ++** involved are nearly as big or bigger than SQLite itself. ++*/ ++SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { ++#ifdef SQLITE_ASCII ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, ++ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, ++ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, ++ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, ++ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, ++ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, ++ 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, ++ 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, ++ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, ++ 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, ++ 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, ++ 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, ++ 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, ++ 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, ++ 252,253,254,255, ++#endif ++#ifdef SQLITE_EBCDIC ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ ++ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ ++ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ ++ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ ++ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ ++ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ ++ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */ ++ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */ ++ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ ++ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */ ++ 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ ++ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ ++ 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ ++ 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ ++ 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ ++ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ ++#endif ++/* All of the upper-to-lower conversion data is above. The following ++** 18 integers are completely unrelated. They are appended to the ++** sqlite3UpperToLower[] array to avoid UBSAN warnings. Here's what is ++** going on: ++** ++** The SQL comparison operators (<>, =, >, <=, <, and >=) are implemented ++** by invoking sqlite3MemCompare(A,B) which compares values A and B and ++** returns negative, zero, or positive if A is less then, equal to, or ++** greater than B, respectively. Then the true false results is found by ++** consulting sqlite3aLTb[opcode], sqlite3aEQb[opcode], or ++** sqlite3aGTb[opcode] depending on whether the result of compare(A,B) ++** is negative, zero, or positive, where opcode is the specific opcode. ++** The only works because the comparison opcodes are consecutive and in ++** this order: NE EQ GT LE LT GE. Various assert()s throughout the code ++** ensure that is the case. ++** ++** These elements must be appended to another array. Otherwise the ++** index (here shown as [256-OP_Ne]) would be out-of-bounds and thus ++** be undefined behavior. That's goofy, but the C-standards people thought ++** it was a good idea, so here we are. ++*/ ++/* NE EQ GT LE LT GE */ ++ 1, 0, 0, 1, 1, 0, /* aLTb[]: Use when compare(A,B) less than zero */ ++ 0, 1, 0, 1, 0, 1, /* aEQb[]: Use when compare(A,B) equals zero */ ++ 1, 0, 1, 0, 0, 1 /* aGTb[]: Use when compare(A,B) greater than zero*/ ++}; ++SQLITE_PRIVATE const unsigned char *sqlite3aLTb = &sqlite3UpperToLower[256-OP_Ne]; ++SQLITE_PRIVATE const unsigned char *sqlite3aEQb = &sqlite3UpperToLower[256+6-OP_Ne]; ++SQLITE_PRIVATE const unsigned char *sqlite3aGTb = &sqlite3UpperToLower[256+12-OP_Ne]; ++ ++/* ++** The following 256 byte lookup table is used to support SQLites built-in ++** equivalents to the following standard library functions: ++** ++** isspace() 0x01 ++** isalpha() 0x02 ++** isdigit() 0x04 ++** isalnum() 0x06 ++** isxdigit() 0x08 ++** toupper() 0x20 ++** SQLite identifier character 0x40 $, _, or non-ascii ++** Quote character 0x80 ++** ++** Bit 0x20 is set if the mapped character requires translation to upper ++** case. i.e. if the character is a lower-case ASCII character. ++** If x is a lower-case ASCII character, then its upper-case equivalent ++** is (x - 0x20). Therefore toupper() can be implemented as: ++** ++** (x & ~(map[x]&0x20)) ++** ++** The equivalent of tolower() is implemented using the sqlite3UpperToLower[] ++** array. tolower() is used more often than toupper() by SQLite. ++** ++** Bit 0x40 is set if the character is non-alphanumeric and can be used in an ++** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any ++** non-ASCII UTF character. Hence the test for whether or not a character is ++** part of an identifier is 0x46. ++*/ ++SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */ ++ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ ++ 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ ++ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ ++ 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ ++ ++ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ ++ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ ++ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ ++ 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ ++ 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ ++ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ ++ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ ++ 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ ++ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ ++ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ ++ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ ++}; ++ ++/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards ++** compatibility for legacy applications, the URI filename capability is ++** disabled by default. ++** ++** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled ++** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. ++** ++** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally ++** disabled. The default value may be changed by compiling with the ++** SQLITE_USE_URI symbol defined. ++*/ ++#ifndef SQLITE_USE_URI ++# define SQLITE_USE_URI 0 ++#endif ++ ++/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the ++** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if ++** that compile-time option is omitted. ++*/ ++#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN) ++# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 ++#else ++# if !SQLITE_ALLOW_COVERING_INDEX_SCAN ++# error "Compile-time disabling of covering index scan using the\ ++ -DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\ ++ Contact SQLite developers if this is a problem for you, and\ ++ delete this #error macro to continue with your build." ++# endif ++#endif ++ ++/* The minimum PMA size is set to this value multiplied by the database ++** page size in bytes. ++*/ ++#ifndef SQLITE_SORTER_PMASZ ++# define SQLITE_SORTER_PMASZ 250 ++#endif ++ ++/* Statement journals spill to disk when their size exceeds the following ++** threshold (in bytes). 0 means that statement journals are created and ++** written to disk immediately (the default behavior for SQLite versions ++** before 3.12.0). -1 means always keep the entire statement journal in ++** memory. (The statement journal is also always held entirely in memory ++** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this ++** setting.) ++*/ ++#ifndef SQLITE_STMTJRNL_SPILL ++# define SQLITE_STMTJRNL_SPILL (64*1024) ++#endif ++ ++/* ++** The default lookaside-configuration, the format "SZ,N". SZ is the ++** number of bytes in each lookaside slot (should be a multiple of 8) ++** and N is the number of slots. The lookaside-configuration can be ++** changed as start-time using sqlite3_config(SQLITE_CONFIG_LOOKASIDE) ++** or at run-time for an individual database connection using ++** sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE); ++** ++** With the two-size-lookaside enhancement, less lookaside is required. ++** The default configuration of 1200,40 actually provides 30 1200-byte slots ++** and 93 128-byte slots, which is more lookaside than is available ++** using the older 1200,100 configuration without two-size-lookaside. ++*/ ++#ifndef SQLITE_DEFAULT_LOOKASIDE ++# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE ++# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */ ++# else ++# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */ ++# endif ++#endif ++ ++ ++/* The default maximum size of an in-memory database created using ++** sqlite3_deserialize() ++*/ ++#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE ++# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 ++#endif ++ ++/* ++** The following singleton contains the global configuration for ++** the SQLite library. ++*/ ++SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { ++ SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ ++ 1, /* bCoreMutex */ ++ SQLITE_THREADSAFE==1, /* bFullMutex */ ++ SQLITE_USE_URI, /* bOpenUri */ ++ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ ++ 0, /* bSmallMalloc */ ++ 1, /* bExtraSchemaChecks */ ++#ifdef SQLITE_DEBUG ++ 0, /* bJsonSelfcheck */ ++#endif ++ 0x7ffffffe, /* mxStrlen */ ++ 0, /* neverCorrupt */ ++ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ ++ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ ++ {0,0,0,0,0,0,0,0}, /* m */ ++ {0,0,0,0,0,0,0,0,0}, /* mutex */ ++ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ ++ (void*)0, /* pHeap */ ++ 0, /* nHeap */ ++ 0, 0, /* mnHeap, mxHeap */ ++ SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ ++ SQLITE_MAX_MMAP_SIZE, /* mxMmap */ ++ (void*)0, /* pPage */ ++ 0, /* szPage */ ++ SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ ++ 0, /* mxParserStack */ ++ 0, /* sharedCacheEnabled */ ++ SQLITE_SORTER_PMASZ, /* szPma */ ++ /* All the rest should always be initialized to zero */ ++ 0, /* isInit */ ++ 0, /* inProgress */ ++ 0, /* isMutexInit */ ++ 0, /* isMallocInit */ ++ 0, /* isPCacheInit */ ++ 0, /* nRefInitMutex */ ++ 0, /* pInitMutex */ ++ 0, /* xLog */ ++ 0, /* pLogArg */ ++#ifdef SQLITE_ENABLE_SQLLOG ++ 0, /* xSqllog */ ++ 0, /* pSqllogArg */ ++#endif ++#ifdef SQLITE_VDBE_COVERAGE ++ 0, /* xVdbeBranch */ ++ 0, /* pVbeBranchArg */ ++#endif ++#ifndef SQLITE_OMIT_DESERIALIZE ++ SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ ++#endif ++#ifndef SQLITE_UNTESTABLE ++ 0, /* xTestCallback */ ++#endif ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */ ++#endif ++ 0, /* bLocaltimeFault */ ++ 0, /* xAltLocaltime */ ++ 0x7ffffffe, /* iOnceResetThreshold */ ++ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ ++ 0, /* iPrngSeed */ ++#ifdef SQLITE_DEBUG ++ {0,0,0,0,0,0}, /* aTune */ ++#endif ++}; ++ ++/* ++** Hash table for global functions - functions common to all ++** database connections. After initialization, this table is ++** read-only. ++*/ ++SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; ++ ++#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) ++/* ++** Counter used for coverage testing. Does not come into play for ++** release builds. ++** ++** Access to this global variable is not mutex protected. This might ++** result in TSAN warnings. But as the variable does not exist in ++** release builds, that should not be a concern. ++*/ ++SQLITE_PRIVATE unsigned int sqlite3CoverageCounter; ++#endif /* SQLITE_COVERAGE_TEST || SQLITE_DEBUG */ ++ ++#ifdef VDBE_PROFILE ++/* ++** The following performance counter can be used in place of ++** sqlite3Hwtime() for profiling. This is a no-op on standard builds. ++*/ ++SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt = 0; ++#endif ++ ++/* ++** The value of the "pending" byte must be 0x40000000 (1 byte past the ++** 1-gibabyte boundary) in a compatible database. SQLite never uses ++** the database page that contains the pending byte. It never attempts ++** to read or write that page. The pending byte page is set aside ++** for use by the VFS layers as space for managing file locks. ++** ++** During testing, it is often desirable to move the pending byte to ++** a different position in the file. This allows code that has to ++** deal with the pending byte to run on files that are much smaller ++** than 1 GiB. The sqlite3_test_control() interface can be used to ++** move the pending byte. ++** ++** IMPORTANT: Changing the pending byte to any value other than ++** 0x40000000 results in an incompatible database file format! ++** Changing the pending byte during operation will result in undefined ++** and incorrect behavior. ++*/ ++#ifndef SQLITE_OMIT_WSD ++SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; ++#endif ++ ++/* ++** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS. ++*/ ++SQLITE_PRIVATE u32 sqlite3TreeTrace = 0; ++SQLITE_PRIVATE u32 sqlite3WhereTrace = 0; ++ ++/* #include "opcodes.h" */ ++/* ++** Properties of opcodes. The OPFLG_INITIALIZER macro is ++** created by mkopcodeh.awk during compilation. Data is obtained ++** from the comments following the "case OP_xxxx:" statements in ++** the vdbe.c file. ++*/ ++SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; ++ ++/* ++** Name of the default collating sequence ++*/ ++SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY"; ++ ++/* ++** Standard typenames. These names must match the COLTYPE_* definitions. ++** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. ++** ++** sqlite3StdType[] The actual names of the datatypes. ++** ++** sqlite3StdTypeLen[] The length (in bytes) of each entry ++** in sqlite3StdType[]. ++** ++** sqlite3StdTypeAffinity[] The affinity associated with each entry ++** in sqlite3StdType[]. ++*/ ++SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 }; ++SQLITE_PRIVATE const char sqlite3StdTypeAffinity[] = { ++ SQLITE_AFF_NUMERIC, ++ SQLITE_AFF_BLOB, ++ SQLITE_AFF_INTEGER, ++ SQLITE_AFF_INTEGER, ++ SQLITE_AFF_REAL, ++ SQLITE_AFF_TEXT ++}; ++SQLITE_PRIVATE const char *sqlite3StdType[] = { ++ "ANY", ++ "BLOB", ++ "INT", ++ "INTEGER", ++ "REAL", ++ "TEXT" ++}; ++ ++/************** End of global.c **********************************************/ ++/************** Begin file status.c ******************************************/ ++/* ++** 2008 June 18 ++** ++** 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 module implements the sqlite3_status() interface and related ++** functionality. ++*/ ++/* #include "sqliteInt.h" */ ++/************** Include vdbeInt.h in the middle of status.c ******************/ ++/************** Begin file vdbeInt.h *****************************************/ ++/* ++** 2003 September 6 ++** ++** 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 is the header file for information that is private to the ++** VDBE. This information used to all be at the top of the single ++** source code file "vdbe.c". When that file became too big (over ++** 6000 lines long) it was split up into several smaller files and ++** this header information was factored out. ++*/ ++#ifndef SQLITE_VDBEINT_H ++#define SQLITE_VDBEINT_H ++ ++/* ++** The maximum number of times that a statement will try to reparse ++** itself before giving up and returning SQLITE_SCHEMA. ++*/ ++#ifndef SQLITE_MAX_SCHEMA_RETRY ++# define SQLITE_MAX_SCHEMA_RETRY 50 ++#endif ++ ++/* ++** VDBE_DISPLAY_P4 is true or false depending on whether or not the ++** "explain" P4 display logic is enabled. ++*/ ++#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ ++ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ ++ || defined(SQLITE_ENABLE_BYTECODE_VTAB) ++# define VDBE_DISPLAY_P4 1 ++#else ++# define VDBE_DISPLAY_P4 0 ++#endif ++ ++/* ++** SQL is translated into a sequence of instructions to be ++** executed by a virtual machine. Each instruction is an instance ++** of the following structure. ++*/ ++typedef struct VdbeOp Op; ++ ++/* ++** Boolean values ++*/ ++typedef unsigned Bool; ++ ++/* Opaque type used by code in vdbesort.c */ ++typedef struct VdbeSorter VdbeSorter; ++ ++/* Elements of the linked list at Vdbe.pAuxData */ ++typedef struct AuxData AuxData; ++ ++/* A cache of large TEXT or BLOB values in a VdbeCursor */ ++typedef struct VdbeTxtBlbCache VdbeTxtBlbCache; ++ ++/* Types of VDBE cursors */ ++#define CURTYPE_BTREE 0 ++#define CURTYPE_SORTER 1 ++#define CURTYPE_VTAB 2 ++#define CURTYPE_PSEUDO 3 ++ ++/* ++** A VdbeCursor is an superclass (a wrapper) for various cursor objects: ++** ++** * A b-tree cursor ++** - In the main database or in an ephemeral database ++** - On either an index or a table ++** * A sorter ++** * A virtual table ++** * A one-row "pseudotable" stored in a single register ++*/ ++typedef struct VdbeCursor VdbeCursor; ++struct VdbeCursor { ++ u8 eCurType; /* One of the CURTYPE_* values above */ ++ i8 iDb; /* Index of cursor database in db->aDb[] */ ++ u8 nullRow; /* True if pointing to a row with no data */ ++ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ ++ u8 isTable; /* True for rowid tables. False for indexes */ ++#ifdef SQLITE_DEBUG ++ u8 seekOp; /* Most recent seek operation on this cursor */ ++ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ ++#endif ++ Bool isEphemeral:1; /* True for an ephemeral table */ ++ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ ++ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ ++ Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */ ++ Bool colCache:1; /* pCache pointer is initialized and non-NULL */ ++ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ ++ union { /* pBtx for isEphermeral. pAltMap otherwise */ ++ Btree *pBtx; /* Separate file holding temporary table */ ++ u32 *aAltMap; /* Mapping from table to index column numbers */ ++ } ub; ++ i64 seqCount; /* Sequence counter */ ++ ++ /* Cached OP_Column parse information is only valid if cacheStatus matches ++ ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of ++ ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that ++ ** the cache is out of date. */ ++ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ ++ int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0 ++ ** if there have been no prior seeks on the cursor. */ ++ /* seekResult does not distinguish between "no seeks have ever occurred ++ ** on this cursor" and "the most recent seek was an exact match". ++ ** For CURTYPE_PSEUDO, seekResult is the register holding the record */ ++ ++ /* When a new VdbeCursor is allocated, only the fields above are zeroed. ++ ** The fields that follow are uninitialized, and must be individually ++ ** initialized prior to first use. */ ++ VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ ++ union { ++ BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */ ++ sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ ++ VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ ++ } uc; ++ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ ++ u32 iHdrOffset; /* Offset to next unparsed byte of the header */ ++ Pgno pgnoRoot; /* Root page of the open btree cursor */ ++ i16 nField; /* Number of fields in the header */ ++ u16 nHdrParsed; /* Number of header fields parsed so far */ ++ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ ++ u32 *aOffset; /* Pointer to aType[nField] */ ++ const u8 *aRow; /* Data for the current row, if all on one page */ ++ u32 payloadSize; /* Total number of bytes in the record */ ++ u32 szRow; /* Byte available in aRow */ ++#ifdef SQLITE_ENABLE_COLUMN_USED_MASK ++ u64 maskUsed; /* Mask of columns used by this cursor */ ++#endif ++ VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ ++ ++ /* Space is allocated for aType to hold at least 2*nField+1 entries: ++ ** nField slots for aType[] and nField+1 array slots for aOffset[] */ ++ u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */ ++}; ++ ++/* ++** The size (in bytes) of a VdbeCursor object that has an nField value of N ++** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple ++** of 8. ++*/ ++#define SZ_VDBECURSOR(N) \ ++ (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64)) ++ ++/* Return true if P is a null-only cursor ++*/ ++#define IsNullCursor(P) \ ++ ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) ++ ++/* ++** A value for VdbeCursor.cacheStatus that means the cache is always invalid. ++*/ ++#define CACHE_STALE 0 ++ ++/* ++** Large TEXT or BLOB values can be slow to load, so we want to avoid ++** loading them more than once. For that reason, large TEXT and BLOB values ++** can be stored in a cache defined by this object, and attached to the ++** VdbeCursor using the pCache field. ++*/ ++struct VdbeTxtBlbCache { ++ char *pCValue; /* A RCStr buffer to hold the value */ ++ i64 iOffset; /* File offset of the row being cached */ ++ int iCol; /* Column for which the cache is valid */ ++ u32 cacheStatus; /* Vdbe.cacheCtr value */ ++ u32 colCacheCtr; /* Column cache counter */ ++}; ++ ++/* ++** When a sub-program is executed (OP_Program), a structure of this type ++** is allocated to store the current value of the program counter, as ++** well as the current memory cell array and various other frame specific ++** values stored in the Vdbe struct. When the sub-program is finished, ++** these values are copied back to the Vdbe from the VdbeFrame structure, ++** restoring the state of the VM to as it was before the sub-program ++** began executing. ++** ++** The memory for a VdbeFrame object is allocated and managed by a memory ++** cell in the parent (calling) frame. When the memory cell is deleted or ++** overwritten, the VdbeFrame object is not freed immediately. Instead, it ++** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame ++** list is deleted when the VM is reset in VdbeHalt(). The reason for doing ++** this instead of deleting the VdbeFrame immediately is to avoid recursive ++** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the ++** child frame are released. ++** ++** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is ++** set to NULL if the currently executing frame is the main program. ++*/ ++typedef struct VdbeFrame VdbeFrame; ++struct VdbeFrame { ++ Vdbe *v; /* VM this frame belongs to */ ++ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ ++ Op *aOp; /* Program instructions for parent frame */ ++ Mem *aMem; /* Array of memory cells for parent frame */ ++ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ ++ u8 *aOnce; /* Bitmask used by OP_Once */ ++ void *token; /* Copy of SubProgram.token */ ++ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ ++ AuxData *pAuxData; /* Linked list of auxdata allocations */ ++#if SQLITE_DEBUG ++ u32 iFrameMagic; /* magic number for sanity checking */ ++#endif ++ int nCursor; /* Number of entries in apCsr */ ++ int pc; /* Program Counter in parent (calling) frame */ ++ int nOp; /* Size of aOp array */ ++ int nMem; /* Number of entries in aMem */ ++ int nChildMem; /* Number of memory cells for child frame */ ++ int nChildCsr; /* Number of cursors for child frame */ ++ i64 nChange; /* Statement changes (Vdbe.nChange) */ ++ i64 nDbChange; /* Value of db->nChange */ ++}; ++ ++/* Magic number for sanity checking on VdbeFrame objects */ ++#define SQLITE_FRAME_MAGIC 0x879fb71e ++ ++/* ++** Return a pointer to the array of registers allocated for use ++** by a VdbeFrame. ++*/ ++#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) ++ ++/* ++** Internally, the vdbe manipulates nearly all SQL values as Mem ++** structures. Each Mem struct may cache multiple representations (string, ++** integer etc.) of the same value. ++*/ ++struct sqlite3_value { ++ union MemValue { ++ double r; /* Real value used when MEM_Real is set in flags */ ++ i64 i; /* Integer value used when MEM_Int is set in flags */ ++ int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ ++ const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ ++ FuncDef *pDef; /* Used only when flags==MEM_Agg */ ++ } u; ++ char *z; /* String or BLOB value */ ++ int n; /* Number of characters in string value, excluding '\0' */ ++ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ ++ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ ++ u8 eSubtype; /* Subtype for this value */ ++ /* ShallowCopy only needs to copy the information above */ ++ sqlite3 *db; /* The associated database connection */ ++ int szMalloc; /* Size of the zMalloc allocation */ ++ u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ ++ char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ ++ void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ ++#ifdef SQLITE_DEBUG ++ Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ ++ u16 mScopyFlags; /* flags value immediately after the shallow copy */ ++ u8 bScopy; /* The pScopyFrom of some other Mem *might* point here */ ++#endif ++}; ++ ++/* ++** Size of struct Mem not including the Mem.zMalloc member or anything that ++** follows. ++*/ ++#define MEMCELLSIZE offsetof(Mem,db) ++ ++/* One or more of the following flags are set to indicate the ++** representations of the value stored in the Mem struct. ++** ++** * MEM_Null An SQL NULL value ++** ++** * MEM_Null|MEM_Zero An SQL NULL with the virtual table ++** UPDATE no-change flag set ++** ++** * MEM_Null|MEM_Term| An SQL NULL, but also contains a ++** MEM_Subtype pointer accessible using ++** sqlite3_value_pointer(). ++** ++** * MEM_Null|MEM_Cleared Special SQL NULL that compares non-equal ++** to other NULLs even using the IS operator. ++** ++** * MEM_Str A string, stored in Mem.z with ++** length Mem.n. Zero-terminated if ++** MEM_Term is set. This flag is ++** incompatible with MEM_Blob and ++** MEM_Null, but can appear with MEM_Int, ++** MEM_Real, and MEM_IntReal. ++** ++** * MEM_Blob A blob, stored in Mem.z length Mem.n. ++** Incompatible with MEM_Str, MEM_Null, ++** MEM_Int, MEM_Real, and MEM_IntReal. ++** ++** * MEM_Blob|MEM_Zero A blob in Mem.z of length Mem.n plus ++** MEM.u.i extra 0x00 bytes at the end. ++** ++** * MEM_Int Integer stored in Mem.u.i. ++** ++** * MEM_Real Real stored in Mem.u.r. ++** ++** * MEM_IntReal Real stored as an integer in Mem.u.i. ++** ++** If the MEM_Null flag is set, then the value is an SQL NULL value. ++** For a pointer type created using sqlite3_bind_pointer() or ++** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. ++** ++** If the MEM_Str flag is set then Mem.z points at a string representation. ++** Usually this is encoded in the same unicode encoding as the main ++** database (see below for exceptions). If the MEM_Term flag is also ++** set, then the string is nul terminated. The MEM_Int and MEM_Real ++** flags may coexist with the MEM_Str flag. ++*/ ++#define MEM_Undefined 0x0000 /* Value is undefined */ ++#define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ ++#define MEM_Str 0x0002 /* Value is a string */ ++#define MEM_Int 0x0004 /* Value is an integer */ ++#define MEM_Real 0x0008 /* Value is a real number */ ++#define MEM_Blob 0x0010 /* Value is a BLOB */ ++#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */ ++#define MEM_AffMask 0x003f /* Mask of affinity bits */ ++ ++/* Extra bits that modify the meanings of the core datatypes above ++*/ ++#define MEM_FromBind 0x0040 /* Value originates from sqlite3_bind() */ ++ /* 0x0080 // Available */ ++#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ ++#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ ++#define MEM_Zero 0x0400 /* Mem.i contains count of 0s appended to blob */ ++#define MEM_Subtype 0x0800 /* Mem.eSubtype is valid */ ++#define MEM_TypeMask 0x0dbf /* Mask of type bits */ ++ ++/* Bits that determine the storage for Mem.z for a string or blob or ++** aggregate accumulator. ++*/ ++#define MEM_Dyn 0x1000 /* Need to call Mem.xDel() on Mem.z */ ++#define MEM_Static 0x2000 /* Mem.z points to a static string */ ++#define MEM_Ephem 0x4000 /* Mem.z points to an ephemeral string */ ++#define MEM_Agg 0x8000 /* Mem.z points to an agg function context */ ++ ++/* Return TRUE if Mem X contains dynamically allocated content - anything ++** that needs to be deallocated to avoid a leak. ++*/ ++#define VdbeMemDynamic(X) \ ++ (((X)->flags&(MEM_Agg|MEM_Dyn))!=0) ++ ++/* ++** Clear any existing type flags from a Mem and replace them with f ++*/ ++#define MemSetTypeFlag(p, f) \ ++ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) ++ ++/* ++** True if Mem X is a NULL-nochng type. ++*/ ++#define MemNullNochng(X) \ ++ (((X)->flags&MEM_TypeMask)==(MEM_Null|MEM_Zero) \ ++ && (X)->n==0 && (X)->u.nZero==0) ++ ++/* ++** Return true if a memory cell has been initialized and is valid. ++** is for use inside assert() statements only. ++** ++** A Memory cell is initialized if at least one of the ++** MEM_Null, MEM_Str, MEM_Int, MEM_Real, MEM_Blob, or MEM_IntReal bits ++** is set. It is "undefined" if all those bits are zero. ++*/ ++#ifdef SQLITE_DEBUG ++#define memIsValid(M) ((M)->flags & MEM_AffMask)!=0 ++#endif ++ ++/* ++** Each auxiliary data pointer stored by a user defined function ++** implementation calling sqlite3_set_auxdata() is stored in an instance ++** of this structure. All such structures associated with a single VM ++** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed ++** when the VM is halted (if not before). ++*/ ++struct AuxData { ++ int iAuxOp; /* Instruction number of OP_Function opcode */ ++ int iAuxArg; /* Index of function argument. */ ++ void *pAux; /* Aux data pointer */ ++ void (*xDeleteAux)(void*); /* Destructor for the aux data */ ++ AuxData *pNextAux; /* Next element in list */ ++}; ++ ++/* ++** The "context" argument for an installable function. A pointer to an ++** instance of this structure is the first argument to the routines used ++** implement the SQL functions. ++** ++** There is a typedef for this structure in sqlite.h. So all routines, ++** even the public interface to SQLite, can use a pointer to this structure. ++** But this file is the only place where the internal details of this ++** structure are known. ++** ++** This structure is defined inside of vdbeInt.h because it uses substructures ++** (Mem) which are only defined there. ++*/ ++struct sqlite3_context { ++ Mem *pOut; /* The return value is stored here */ ++ FuncDef *pFunc; /* Pointer to function information */ ++ Mem *pMem; /* Memory cell used to store aggregate context */ ++ Vdbe *pVdbe; /* The VM that owns this context */ ++ int iOp; /* Instruction number of OP_Function */ ++ int isError; /* Error code returned by the function. */ ++ u8 enc; /* Encoding to use for results */ ++ u8 skipFlag; /* Skip accumulator loading if true */ ++ u16 argc; /* Number of arguments */ ++ sqlite3_value *argv[FLEXARRAY]; /* Argument set */ ++}; ++ ++/* ++** The size (in bytes) of an sqlite3_context object that holds N ++** argv[] arguments. ++*/ ++#define SZ_CONTEXT(N) \ ++ (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*)) ++ ++ ++/* The ScanStatus object holds a single value for the ++** sqlite3_stmt_scanstatus() interface. ++** ++** aAddrRange[]: ++** This array is used by ScanStatus elements associated with EQP ++** notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is ++** an array of up to 3 ranges of VM addresses for which the Vdbe.anCycle[] ++** values should be summed to calculate the NCYCLE value. Each pair of ++** integer addresses is a start and end address (both inclusive) for a range ++** instructions. A start value of 0 indicates an empty range. ++*/ ++typedef struct ScanStatus ScanStatus; ++struct ScanStatus { ++ int addrExplain; /* OP_Explain for loop */ ++ int aAddrRange[6]; ++ int addrLoop; /* Address of "loops" counter */ ++ int addrVisit; /* Address of "rows visited" counter */ ++ int iSelectID; /* The "Select-ID" for this loop */ ++ LogEst nEst; /* Estimated output rows per loop */ ++ char *zName; /* Name of table or index */ ++}; ++ ++/* The DblquoteStr object holds the text of a double-quoted ++** string for a prepared statement. A linked list of these objects ++** is constructed during statement parsing and is held on Vdbe.pDblStr. ++** When computing a normalized SQL statement for an SQL statement, that ++** list is consulted for each double-quoted identifier to see if the ++** identifier should really be a string literal. ++*/ ++typedef struct DblquoteStr DblquoteStr; ++struct DblquoteStr { ++ DblquoteStr *pNextStr; /* Next string literal in the list */ ++ char z[8]; /* Dequoted value for the string */ ++}; ++ ++/* ++** An instance of the virtual machine. This structure contains the complete ++** state of the virtual machine. ++** ++** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() ++** is really a pointer to an instance of this structure. ++*/ ++struct Vdbe { ++ sqlite3 *db; /* The database connection that owns this statement */ ++ Vdbe **ppVPrev,*pVNext; /* Linked list of VDBEs with the same Vdbe.db */ ++ Parse *pParse; /* Parsing context used to create this Vdbe */ ++ ynVar nVar; /* Number of entries in aVar[] */ ++ int nMem; /* Number of memory locations currently allocated */ ++ int nCursor; /* Number of slots in apCsr[] */ ++ u32 cacheCtr; /* VdbeCursor row cache generation counter */ ++ int pc; /* The program counter */ ++ int rc; /* Value to return */ ++ i64 nChange; /* Number of db changes made since last reset */ ++ int iStatement; /* Statement number (or 0 if has no opened stmt) */ ++ i64 iCurrentTime; /* Value of julianday('now') for this statement */ ++ i64 nFkConstraint; /* Number of imm. FK constraints this VM */ ++ i64 nStmtDefCons; /* Number of def. constraints when stmt started */ ++ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ ++ Mem *aMem; /* The memory locations */ ++ Mem **apArg; /* Arguments xUpdate and xFilter vtab methods */ ++ VdbeCursor **apCsr; /* One element of this array for each open cursor */ ++ Mem *aVar; /* Values for the OP_Variable opcode. */ ++ ++ /* When allocating a new Vdbe object, all of the fields below should be ++ ** initialized to zero or NULL */ ++ ++ Op *aOp; /* Space to hold the virtual machine's program */ ++ int nOp; /* Number of instructions in the program */ ++ int nOpAlloc; /* Slots allocated for aOp[] */ ++ Mem *aColName; /* Column names to return */ ++ Mem *pResultRow; /* Current output row */ ++ char *zErrMsg; /* Error message written here */ ++ VList *pVList; /* Name of variables */ ++#ifndef SQLITE_OMIT_TRACE ++ i64 startTime; /* Time when query started - used for profiling */ ++#endif ++#ifdef SQLITE_DEBUG ++ int rcApp; /* errcode set by sqlite3_result_error_code() */ ++ u32 nWrite; /* Number of write operations that have occurred */ ++ int napArg; /* Size of the apArg[] array */ ++#endif ++ u16 nResColumn; /* Number of columns in one row of the result set */ ++ u16 nResAlloc; /* Column slots allocated to aColName[] */ ++ u8 errorAction; /* Recovery action to do in case of an error */ ++ u8 minWriteFileFormat; /* Minimum file format for writable database files */ ++ u8 prepFlags; /* SQLITE_PREPARE_* flags */ ++ u8 eVdbeState; /* On of the VDBE_*_STATE values */ ++ bft expired:2; /* 1: recompile VM immediately 2: when convenient */ ++ bft explain:2; /* 0: normal, 1: EXPLAIN, 2: EXPLAIN QUERY PLAN */ ++ bft changeCntOn:1; /* True to update the change-counter */ + bft usesStmtJournal:1; /* True if uses a statement journal */ + bft readOnly:1; /* True for statements that do not write */ + bft bIsReader:1; /* True for statements that read */ ++ bft haveEqpOps:1; /* Bytecode supports EXPLAIN QUERY PLAN */ + yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ + yDbMask lockMask; /* Subset of btreeMask that requires a lock */ +- u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ ++ u32 aCounter[9]; /* Counters used by sqlite3_stmt_status() */ + char *zSql; /* Text of the SQL statement that generated this */ + #ifdef SQLITE_ENABLE_NORMALIZE + char *zNormSql; /* Normalization of the associated SQL statement */ +@@ -21152,20 +24104,18 @@ struct Vdbe { + SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ + AuxData *pAuxData; /* Linked list of auxdata allocations */ + #ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- i64 *anExec; /* Number of times each op has been executed */ + int nScan; /* Entries in aScan[] */ + ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */ + #endif + }; + + /* +-** The following are allowed values for Vdbe.magic ++** The following are allowed values for Vdbe.eVdbeState + */ +-#define VDBE_MAGIC_INIT 0x16bceaa5 /* Building a VDBE program */ +-#define VDBE_MAGIC_RUN 0x2df20da3 /* VDBE is ready to execute */ +-#define VDBE_MAGIC_HALT 0x319c2973 /* VDBE has completed execution */ +-#define VDBE_MAGIC_RESET 0x48fa9f76 /* Reset and ready to run again */ +-#define VDBE_MAGIC_DEAD 0x5606c3c8 /* The VDBE has been deallocated */ ++#define VDBE_INIT_STATE 0 /* Prepared statement under construction */ ++#define VDBE_READY_STATE 1 /* Ready to run but not yet started */ ++#define VDBE_RUN_STATE 2 /* Run in progress */ ++#define VDBE_HALT_STATE 3 /* Finished. Need reset() or finalize() */ + + /* + ** Structure used to store the context required by the +@@ -21176,30 +24126,65 @@ struct PreUpdate { + VdbeCursor *pCsr; /* Cursor to read old values from */ + int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ + u8 *aRecord; /* old.* database record */ +- KeyInfo keyinfo; ++ KeyInfo *pKeyinfo; /* Key information */ + UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ + UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ + int iNewReg; /* Register for new.* values */ ++ int iBlobWrite; /* Value returned by preupdate_blobwrite() */ + i64 iKey1; /* First key value passed to hook */ + i64 iKey2; /* Second key value passed to hook */ ++ Mem oldipk; /* Memory cell holding "old" IPK value */ + Mem *aNew; /* Array of new.* values */ +- Table *pTab; /* Schema object being upated */ ++ Table *pTab; /* Schema object being updated */ + Index *pPk; /* PK index if pTab is WITHOUT ROWID */ ++ sqlite3_value **apDflt; /* Array of default values, if required */ ++ u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */ + }; + ++/* ++** An instance of this object is used to pass an vector of values into ++** OP_VFilter, the xFilter method of a virtual table. The vector is the ++** set of values on the right-hand side of an IN constraint. ++** ++** The value as passed into xFilter is an sqlite3_value with a "pointer" ++** type, such as is generated by sqlite3_result_pointer() and read by ++** sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null ++** and a subtype of 'p'. The sqlite3_vtab_in_first() and _next() interfaces ++** know how to use this object to step through all the values in the ++** right operand of the IN constraint. ++*/ ++typedef struct ValueList ValueList; ++struct ValueList { ++ BtCursor *pCsr; /* An ephemeral table holding all values */ ++ sqlite3_value *pOut; /* Register to hold each decoded output value */ ++}; ++ ++/* Size of content associated with serial types that fit into a ++** single-byte varint. ++*/ ++#ifndef SQLITE_AMALGAMATION ++SQLITE_PRIVATE const u8 sqlite3SmallTypeSizes[]; ++#endif ++ + /* + ** Function prototypes + */ + SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); + SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); ++SQLITE_PRIVATE void sqlite3VdbeFreeCursorNN(Vdbe*,VdbeCursor*); + void sqliteVdbePopStack(Vdbe*,int); ++SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeHandleMovedCursor(VdbeCursor *p); + SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); +-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, u32*); + SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*); + SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); + SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8); +-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); +-SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); ++#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT ++SQLITE_PRIVATE u64 sqlite3FloatSwap(u64 in); ++# define swapMixedEndianFloat(X) X = sqlite3FloatSwap(X) ++#else ++# define swapMixedEndianFloat(X) ++#endif ++SQLITE_PRIVATE void sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); + SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int); + + int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); +@@ -21223,7 +24208,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); + SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); + SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); + SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); +-SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); ++SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); + SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); + #ifdef SQLITE_OMIT_FLOATING_POINT + # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 +@@ -21233,14 +24218,20 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); + SQLITE_PRIVATE void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*)); + SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16); + SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*); ++#ifndef SQLITE_OMIT_INCRBLOB + SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int); ++#else ++SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem*,int); ++#endif + #ifdef SQLITE_DEBUG + SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*); + #endif + SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem*); ++SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem*); + SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*); + SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8); +-SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*); ++SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); ++SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem*); + SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*); + SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*); + SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem*, int ifNull); +@@ -21251,6 +24242,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem*,u8,u8); + SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*); + SQLITE_PRIVATE int sqlite3VdbeMemFromBtreeZeroOffset(BtCursor*,u32,Mem*); + SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); ++SQLITE_PRIVATE void sqlite3VdbeMemReleaseMalloc(Mem*p); + SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); + #ifndef SQLITE_OMIT_WINDOWFUNC + SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); +@@ -21268,7 +24260,8 @@ SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */ + SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */ + SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK +-SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int); ++SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( ++ Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int); + #endif + SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); + +@@ -21281,6 +24274,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); + SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); + SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); + ++SQLITE_PRIVATE void sqlite3VdbeValueListFree(void*); ++ + #ifdef SQLITE_DEBUG + SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*); + SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*); +@@ -21510,8 +24505,9 @@ SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){ + nInit += countLookasideSlots(db->lookaside.pSmallInit); + nFree += countLookasideSlots(db->lookaside.pSmallFree); + #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ +- if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; +- return db->lookaside.nSlot - (nInit+nFree); ++ assert( db->lookaside.nSlot >= nInit+nFree ); ++ if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit); ++ return (int)(db->lookaside.nSlot - (nInit+nFree)); + } + + /* +@@ -21564,7 +24560,7 @@ SQLITE_API int sqlite3_db_status( + assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); + assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); + *pCurrent = 0; +- *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; ++ *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT]; + if( resetFlag ){ + db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; + } +@@ -21609,6 +24605,8 @@ SQLITE_API int sqlite3_db_status( + + sqlite3BtreeEnterAll(db); + db->pnBytesFreed = &nByte; ++ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); ++ db->lookaside.pEnd = db->lookaside.pStart; + for(i=0; inDb; i++){ + Schema *pSchema = db->aDb[i].pSchema; + if( ALWAYS(pSchema!=0) ){ +@@ -21634,6 +24632,7 @@ SQLITE_API int sqlite3_db_status( + } + } + db->pnBytesFreed = 0; ++ db->lookaside.pEnd = db->lookaside.pTrueEnd; + sqlite3BtreeLeaveAll(db); + + *pHighwater = 0; +@@ -21651,10 +24650,12 @@ SQLITE_API int sqlite3_db_status( + int nByte = 0; /* Used to accumulate return value */ + + db->pnBytesFreed = &nByte; +- for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ +- sqlite3VdbeClearObject(db, pVdbe); +- sqlite3DbFree(db, pVdbe); ++ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); ++ db->lookaside.pEnd = db->lookaside.pStart; ++ for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pVNext){ ++ sqlite3VdbeDelete(pVdbe); + } ++ db->lookaside.pEnd = db->lookaside.pTrueEnd; + db->pnBytesFreed = 0; + + *pHighwater = 0; /* IMP: R-64479-57858 */ +@@ -21675,7 +24676,7 @@ SQLITE_API int sqlite3_db_status( + case SQLITE_DBSTATUS_CACHE_MISS: + case SQLITE_DBSTATUS_CACHE_WRITE:{ + int i; +- int nRet = 0; ++ u64 nRet = 0; + assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); + assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); + +@@ -21688,7 +24689,7 @@ SQLITE_API int sqlite3_db_status( + *pHighwater = 0; /* IMP: R-42420-56072 */ + /* IMP: R-54100-20147 */ + /* IMP: R-29431-39229 */ +- *pCurrent = nRet; ++ *pCurrent = (int)nRet & 0x7fffffff; + break; + } + +@@ -21785,12 +24786,14 @@ struct DateTime { + int tz; /* Timezone offset in minutes */ + double s; /* Seconds */ + char validJD; /* True (1) if iJD is valid */ +- char rawS; /* Raw numeric value stored in s */ + char validYMD; /* True (1) if Y,M,D are valid */ + char validHMS; /* True (1) if h,m,s are valid */ +- char validTZ; /* True (1) if tz is valid */ +- char tzSet; /* Timezone was set explicitly */ +- char isError; /* An overflow has occurred */ ++ char nFloor; /* Days to implement "floor" */ ++ unsigned rawS : 1; /* Raw numeric value stored in s */ ++ unsigned isError : 1; /* An overflow has occurred */ ++ unsigned useSubsec : 1; /* Display subsecond precision */ ++ unsigned isUtc : 1; /* Time is known to be UTC */ ++ unsigned isLocal : 1; /* Time is known to be localtime */ + }; + + +@@ -21823,8 +24826,8 @@ struct DateTime { + */ + static int getDigits(const char *zDate, const char *zFormat, ...){ + /* The aMx[] array translates the 3rd character of each format +- ** spec into a max size: a b c d e f */ +- static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 }; ++ ** spec into a max size: a b c d e f */ ++ static const u16 aMx[] = { 12, 14, 24, 31, 59, 14712 }; + va_list ap; + int cnt = 0; + char nextC; +@@ -21888,6 +24891,8 @@ static int parseTimezone(const char *zDate, DateTime *p){ + sgn = +1; + }else if( c=='Z' || c=='z' ){ + zDate++; ++ p->isLocal = 0; ++ p->isUtc = 1; + goto zulu_time; + }else{ + return c!=0; +@@ -21900,7 +24905,6 @@ static int parseTimezone(const char *zDate, DateTime *p){ + p->tz = sgn*(nMn + nHr*60); + zulu_time: + while( sqlite3Isspace(*zDate) ){ zDate++; } +- p->tzSet = 1; + return *zDate!=0; + } + +@@ -21933,6 +24937,9 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ + zDate++; + } + ms /= rScale; ++ /* Truncate to avoid problems with sub-milliseconds ++ ** rounding. https://sqlite.org/forum/forumpost/766a2c9231 */ ++ if( ms>0.999 ) ms = 0.999; + } + }else{ + s = 0; +@@ -21944,7 +24951,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ + p->m = m; + p->s = s + ms; + if( parseTimezone(zDate, p) ) return 1; +- p->validTZ = (p->tz!=0)?1:0; + return 0; + } + +@@ -21983,23 +24989,48 @@ static void computeJD(DateTime *p){ + Y--; + M += 12; + } +- A = Y/100; +- B = 2 - A + (A/4); ++ A = (Y+4800)/100; ++ B = 38 - A + (A/4); + X1 = 36525*(Y+4716)/100; + X2 = 306001*(M+1)/10000; + p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); + p->validJD = 1; + if( p->validHMS ){ +- p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000); +- if( p->validTZ ){ ++ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); ++ if( p->tz ){ + p->iJD -= p->tz*60000; + p->validYMD = 0; + p->validHMS = 0; +- p->validTZ = 0; ++ p->tz = 0; ++ p->isUtc = 1; ++ p->isLocal = 0; + } + } + } + ++/* ++** Given the YYYY-MM-DD information current in p, determine if there ++** is day-of-month overflow and set nFloor to the number of days that ++** would need to be subtracted from the date in order to bring the ++** date back to the end of the month. ++*/ ++static void computeFloor(DateTime *p){ ++ assert( p->validYMD || p->isError ); ++ assert( p->D>=0 && p->D<=31 ); ++ assert( p->M>=0 && p->M<=12 ); ++ if( p->D<=28 ){ ++ p->nFloor = 0; ++ }else if( (1<M) & 0x15aa ){ ++ p->nFloor = 0; ++ }else if( p->M!=2 ){ ++ p->nFloor = (p->D==31); ++ }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){ ++ p->nFloor = p->D - 28; ++ }else{ ++ p->nFloor = p->D - 29; ++ } ++} ++ + /* + ** Parse dates of the form + ** +@@ -22038,12 +25069,16 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){ + p->Y = neg ? -Y : Y; + p->M = M; + p->D = D; +- if( p->validTZ ){ ++ computeFloor(p); ++ if( p->tz ){ + computeJD(p); + } + return 0; + } + ++ ++static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */ ++ + /* + ** Set the time to the current time reported by the VFS. + ** +@@ -22053,6 +25088,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ + p->iJD = sqlite3StmtCurrentTime(context); + if( p->iJD>0 ){ + p->validJD = 1; ++ p->isUtc = 1; ++ p->isLocal = 0; ++ clearYMD_HMS_TZ(p); + return 0; + }else{ + return 1; +@@ -22105,6 +25143,11 @@ static int parseDateOrTime( + }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ + setRawDateNumber(p, r); + return 0; ++ }else if( (sqlite3StrICmp(zDate,"subsec")==0 ++ || sqlite3StrICmp(zDate,"subsecond")==0) ++ && sqlite3NotPureFunc(context) ){ ++ p->useSubsec = 1; ++ return setDateTimeToCurrent(context, p); + } + return 1; + } +@@ -22131,7 +25174,7 @@ static int validJulianDay(sqlite3_int64 iJD){ + ** Compute the Year, Month, and Day from the julian day number. + */ + static void computeYMD(DateTime *p){ +- int Z, A, B, C, D, E, X1; ++ int Z, alpha, A, B, C, D, E, X1; + if( p->validYMD ) return; + if( !p->validJD ){ + p->Y = 2000; +@@ -22142,8 +25185,8 @@ static void computeYMD(DateTime *p){ + return; + }else{ + Z = (int)((p->iJD + 43200000)/86400000); +- A = (int)((Z - 1867216.25)/36524.25); +- A = Z + 1 + A - (A/4); ++ alpha = (int)((Z + 32044.75)/36524.25) - 52; ++ A = Z + 1 + alpha - ((alpha+100)/4) + 25; + B = A + 1524; + C = (int)((B - 122.1)/365.25); + D = (36525*(C&32767))/100; +@@ -22160,17 +25203,14 @@ static void computeYMD(DateTime *p){ + ** Compute the Hour, Minute, and Seconds from the julian day number. + */ + static void computeHMS(DateTime *p){ +- int s; ++ int day_ms, day_min; /* milliseconds, minutes into the day */ + if( p->validHMS ) return; + computeJD(p); +- s = (int)((p->iJD + 43200000) % 86400000); +- p->s = s/1000.0; +- s = (int)p->s; +- p->s -= s; +- p->h = s/3600; +- s -= p->h*3600; +- p->m = s/60; +- p->s += s - p->m*60; ++ day_ms = (int)((p->iJD + 43200000) % 86400000); ++ p->s = (day_ms % 60000)/1000.0; ++ day_min = day_ms/60000; ++ p->m = day_min % 60; ++ p->h = day_min / 60; + p->rawS = 0; + p->validHMS = 1; + } +@@ -22189,7 +25229,7 @@ static void computeYMD_HMS(DateTime *p){ + static void clearYMD_HMS_TZ(DateTime *p){ + p->validYMD = 0; + p->validHMS = 0; +- p->validTZ = 0; ++ p->tz = 0; + } + + #ifndef SQLITE_OMIT_LOCALTIME +@@ -22217,8 +25257,10 @@ static void clearYMD_HMS_TZ(DateTime *p){ + ** is available. This routine returns 0 on success and + ** non-zero on any kind of error. + ** +-** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this +-** routine will always fail. ++** If the sqlite3GlobalConfig.bLocaltimeFault variable is non-zero then this ++** routine will always fail. If bLocaltimeFault is nonzero and ++** sqlite3GlobalConfig.xAltLocaltime is not NULL, then xAltLocaltime() is ++** invoked in place of the OS-defined localtime() function. + ** + ** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C + ** library function localtime_r() is used to assist in the calculation of +@@ -22234,14 +25276,30 @@ static int osLocaltime(time_t *t, struct tm *pTm){ + sqlite3_mutex_enter(mutex); + pX = localtime(t); + #ifndef SQLITE_UNTESTABLE +- if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; ++ if( sqlite3GlobalConfig.bLocaltimeFault ){ ++ if( sqlite3GlobalConfig.xAltLocaltime!=0 ++ && 0==sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm) ++ ){ ++ pX = pTm; ++ }else{ ++ pX = 0; ++ } ++ } + #endif + if( pX ) *pTm = *pX; ++#if SQLITE_THREADSAFE>0 + sqlite3_mutex_leave(mutex); ++#endif + rc = pX==0; + #else + #ifndef SQLITE_UNTESTABLE +- if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; ++ if( sqlite3GlobalConfig.bLocaltimeFault ){ ++ if( sqlite3GlobalConfig.xAltLocaltime!=0 ){ ++ return sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm); ++ }else{ ++ return 1; ++ } ++ } + #endif + #if HAVE_LOCALTIME_R + rc = localtime_r(t, pTm)==0; +@@ -22256,67 +25314,56 @@ static int osLocaltime(time_t *t, struct tm *pTm){ + + #ifndef SQLITE_OMIT_LOCALTIME + /* +-** Compute the difference (in milliseconds) between localtime and UTC +-** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs, +-** return this value and set *pRc to SQLITE_OK. +-** +-** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value +-** is undefined in this case. ++** Assuming the input DateTime is UTC, move it to its localtime equivalent. + */ +-static sqlite3_int64 localtimeOffset( +- DateTime *p, /* Date at which to calculate offset */ +- sqlite3_context *pCtx, /* Write error here if one occurs */ +- int *pRc /* OUT: Error code. SQLITE_OK or ERROR */ ++static int toLocaltime( ++ DateTime *p, /* Date at which to calculate offset */ ++ sqlite3_context *pCtx /* Write error here if one occurs */ + ){ +- DateTime x, y; + time_t t; + struct tm sLocal; ++ int iYearDiff; + + /* Initialize the contents of sLocal to avoid a compiler warning. */ + memset(&sLocal, 0, sizeof(sLocal)); + +- x = *p; +- computeYMD_HMS(&x); +- if( x.Y<1971 || x.Y>=2038 ){ ++ computeJD(p); ++ if( p->iJD<2108667600*(i64)100000 /* 1970-01-01 */ ++ || p->iJD>2130141456*(i64)100000 /* 2038-01-18 */ ++ ){ + /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only + ** works for years between 1970 and 2037. For dates outside this range, + ** SQLite attempts to map the year into an equivalent year within this + ** range, do the calculation, then map the year back. + */ +- x.Y = 2000; +- x.M = 1; +- x.D = 1; +- x.h = 0; +- x.m = 0; +- x.s = 0.0; +- } else { +- int s = (int)(x.s + 0.5); +- x.s = s; ++ DateTime x = *p; ++ computeYMD_HMS(&x); ++ iYearDiff = (2000 + x.Y%4) - x.Y; ++ x.Y += iYearDiff; ++ x.validJD = 0; ++ computeJD(&x); ++ t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); ++ }else{ ++ iYearDiff = 0; ++ t = (time_t)(p->iJD/1000 - 21086676*(i64)10000); + } +- x.tz = 0; +- x.validJD = 0; +- computeJD(&x); +- t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); + if( osLocaltime(&t, &sLocal) ){ + sqlite3_result_error(pCtx, "local time unavailable", -1); +- *pRc = SQLITE_ERROR; +- return 0; ++ return SQLITE_ERROR; + } +- y.Y = sLocal.tm_year + 1900; +- y.M = sLocal.tm_mon + 1; +- y.D = sLocal.tm_mday; +- y.h = sLocal.tm_hour; +- y.m = sLocal.tm_min; +- y.s = sLocal.tm_sec; +- y.validYMD = 1; +- y.validHMS = 1; +- y.validJD = 0; +- y.rawS = 0; +- y.validTZ = 0; +- y.isError = 0; +- computeJD(&y); +- *pRc = SQLITE_OK; +- return y.iJD - x.iJD; ++ p->Y = sLocal.tm_year + 1900 - iYearDiff; ++ p->M = sLocal.tm_mon + 1; ++ p->D = sLocal.tm_mday; ++ p->h = sLocal.tm_hour; ++ p->m = sLocal.tm_min; ++ p->s = sLocal.tm_sec + (p->iJD%1000)*0.001; ++ p->validYMD = 1; ++ p->validHMS = 1; ++ p->validJD = 0; ++ p->rawS = 0; ++ p->tz = 0; ++ p->isError = 0; ++ return SQLITE_OK; + } + #endif /* SQLITE_OMIT_LOCALTIME */ + +@@ -22329,20 +25376,38 @@ static sqlite3_int64 localtimeOffset( + ** of several units of time. + */ + static const struct { +- u8 eType; /* Transformation type code */ +- u8 nName; /* Length of th name */ +- char *zName; /* Name of the transformation */ +- double rLimit; /* Maximum NNN value for this transform */ +- double rXform; /* Constant used for this transform */ ++ u8 nName; /* Length of the name */ ++ char zName[7]; /* Name of the transformation */ ++ float rLimit; /* Maximum NNN value for this transform */ ++ float rXform; /* Constant used for this transform */ + } aXformType[] = { +- { 0, 6, "second", 464269060800.0, 1000.0 }, +- { 0, 6, "minute", 7737817680.0, 60000.0 }, +- { 0, 4, "hour", 128963628.0, 3600000.0 }, +- { 0, 3, "day", 5373485.0, 86400000.0 }, +- { 1, 5, "month", 176546.0, 2592000000.0 }, +- { 2, 4, "year", 14713.0, 31536000000.0 }, ++ /* 0 */ { 6, "second", 4.6427e+14, 1.0 }, ++ /* 1 */ { 6, "minute", 7.7379e+12, 60.0 }, ++ /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 }, ++ /* 3 */ { 3, "day", 5373485.0, 86400.0 }, ++ /* 4 */ { 5, "month", 176546.0, 2592000.0 }, ++ /* 5 */ { 4, "year", 14713.0, 31536000.0 }, + }; + ++/* ++** If the DateTime p is raw number, try to figure out if it is ++** a julian day number of a unix timestamp. Set the p value ++** appropriately. ++*/ ++static void autoAdjustDate(DateTime *p){ ++ if( !p->rawS || p->validJD ){ ++ p->rawS = 0; ++ }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ ++ && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ ++ ){ ++ double r = p->s*1000.0 + 210866760000000.0; ++ clearYMD_HMS_TZ(p); ++ p->iJD = (sqlite3_int64)(r + 0.5); ++ p->validJD = 1; ++ p->rawS = 0; ++ } ++} ++ + /* + ** Process a modifier to a date-time stamp. The modifiers are + ** as follows: +@@ -22353,14 +25418,20 @@ static const struct { + ** NNN.NNNN seconds + ** NNN months + ** NNN years ++** +/-YYYY-MM-DD HH:MM:SS.SSS ++** ceiling ++** floor + ** start of month + ** start of year + ** start of week + ** start of day + ** weekday N + ** unixepoch ++** auto + ** localtime + ** utc ++** subsec ++** subsecond + ** + ** Return 0 on success and 1 if there is any kind of error. If the error + ** is in a system call (i.e. localtime()), then an error message is written +@@ -22371,11 +25442,75 @@ static int parseModifier( + sqlite3_context *pCtx, /* Function context */ + const char *z, /* The text of the modifier */ + int n, /* Length of zMod in bytes */ +- DateTime *p /* The date/time value to be modified */ ++ DateTime *p, /* The date/time value to be modified */ ++ int idx /* Parameter index of the modifier */ + ){ + int rc = 1; + double r; + switch(sqlite3UpperToLower[(u8)z[0]] ){ ++ case 'a': { ++ /* ++ ** auto ++ ** ++ ** If rawS is available, then interpret as a julian day number, or ++ ** a unix timestamp, depending on its magnitude. ++ */ ++ if( sqlite3_stricmp(z, "auto")==0 ){ ++ if( idx>1 ) return 1; /* IMP: R-33611-57934 */ ++ autoAdjustDate(p); ++ rc = 0; ++ } ++ break; ++ } ++ case 'c': { ++ /* ++ ** ceiling ++ ** ++ ** Resolve day-of-month overflow by rolling forward into the next ++ ** month. As this is the default action, this modifier is really ++ ** a no-op that is only included for symmetry. See "floor". ++ */ ++ if( sqlite3_stricmp(z, "ceiling")==0 ){ ++ computeJD(p); ++ clearYMD_HMS_TZ(p); ++ rc = 0; ++ p->nFloor = 0; ++ } ++ break; ++ } ++ case 'f': { ++ /* ++ ** floor ++ ** ++ ** Resolve day-of-month overflow by rolling back to the end of the ++ ** previous month. ++ */ ++ if( sqlite3_stricmp(z, "floor")==0 ){ ++ computeJD(p); ++ p->iJD -= p->nFloor*86400000; ++ clearYMD_HMS_TZ(p); ++ rc = 0; ++ } ++ break; ++ } ++ case 'j': { ++ /* ++ ** julianday ++ ** ++ ** Always interpret the prior number as a julian-day value. If this ++ ** is not the first modifier, or if the prior argument is not a numeric ++ ** value in the allowed range of julian day numbers understood by ++ ** SQLite (0..5373484.5) then the result will be NULL. ++ */ ++ if( sqlite3_stricmp(z, "julianday")==0 ){ ++ if( idx>1 ) return 1; /* IMP: R-31176-64601 */ ++ if( p->validJD && p->rawS ){ ++ rc = 0; ++ p->rawS = 0; ++ } ++ } ++ break; ++ } + #ifndef SQLITE_OMIT_LOCALTIME + case 'l': { + /* localtime +@@ -22384,9 +25519,9 @@ static int parseModifier( + ** show local time. + */ + if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ +- computeJD(p); +- p->iJD += localtimeOffset(p, pCtx, &rc); +- clearYMD_HMS_TZ(p); ++ rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx); ++ p->isUtc = 0; ++ p->isLocal = 1; + } + break; + } +@@ -22399,6 +25534,7 @@ static int parseModifier( + ** seconds since 1970. Convert to a real julian day number. + */ + if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){ ++ if( idx>1 ) return 1; /* IMP: R-49255-55373 */ + r = p->s*1000.0 + 210866760000000.0; + if( r>=0.0 && r<464269060800000.0 ){ + clearYMD_HMS_TZ(p); +@@ -22410,19 +25546,33 @@ static int parseModifier( + } + #ifndef SQLITE_OMIT_LOCALTIME + else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ +- if( p->tzSet==0 ){ +- sqlite3_int64 c1; ++ if( p->isUtc==0 ){ ++ i64 iOrigJD; /* Original localtime */ ++ i64 iGuess; /* Guess at the corresponding utc time */ ++ int cnt = 0; /* Safety to prevent infinite loop */ ++ i64 iErr; /* Guess is off by this much */ ++ + computeJD(p); +- c1 = localtimeOffset(p, pCtx, &rc); +- if( rc==SQLITE_OK ){ +- p->iJD -= c1; +- clearYMD_HMS_TZ(p); +- p->iJD += c1 - localtimeOffset(p, pCtx, &rc); +- } +- p->tzSet = 1; +- }else{ +- rc = SQLITE_OK; ++ iGuess = iOrigJD = p->iJD; ++ iErr = 0; ++ do{ ++ DateTime new; ++ memset(&new, 0, sizeof(new)); ++ iGuess -= iErr; ++ new.iJD = iGuess; ++ new.validJD = 1; ++ rc = toLocaltime(&new, pCtx); ++ if( rc ) return rc; ++ computeJD(&new); ++ iErr = new.iJD - iOrigJD; ++ }while( iErr && cnt++<3 ); ++ memset(p, 0, sizeof(*p)); ++ p->iJD = iGuess; ++ p->validJD = 1; ++ p->isUtc = 1; ++ p->isLocal = 0; + } ++ rc = SQLITE_OK; + } + #endif + break; +@@ -22437,10 +25587,10 @@ static int parseModifier( + */ + if( sqlite3_strnicmp(z, "weekday ", 8)==0 + && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0 +- && (n=(int)r)==r && n>=0 && r<7 ){ ++ && r>=0.0 && r<7.0 && (n=(int)r)==r ){ + sqlite3_int64 Z; + computeYMD_HMS(p); +- p->validTZ = 0; ++ p->tz = 0; + p->validJD = 0; + computeJD(p); + Z = ((p->iJD + 129600000)/86400000) % 7; +@@ -22457,8 +25607,22 @@ static int parseModifier( + ** + ** Move the date backwards to the beginning of the current day, + ** or month or year. ++ ** ++ ** subsecond ++ ** subsec ++ ** ++ ** Show subsecond precision in the output of datetime() and ++ ** unixepoch() and strftime('%s'). + */ +- if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; ++ if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){ ++ if( sqlite3_stricmp(z, "subsec")==0 ++ || sqlite3_stricmp(z, "subsecond")==0 ++ ){ ++ p->useSubsec = 1; ++ rc = 0; ++ } ++ break; ++ } + if( !p->validJD && !p->validYMD && !p->validHMS ) break; + z += 9; + computeYMD(p); +@@ -22466,7 +25630,7 @@ static int parseModifier( + p->h = p->m = 0; + p->s = 0.0; + p->rawS = 0; +- p->validTZ = 0; ++ p->tz = 0; + p->validJD = 0; + if( sqlite3_stricmp(z,"month")==0 ){ + p->D = 1; +@@ -22494,18 +25658,74 @@ static int parseModifier( + case '9': { + double rRounder; + int i; +- for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} ++ int Y,M,D,h,m,x; ++ const char *z2 = z; ++ char z0 = z[0]; ++ for(n=1; z[n]; n++){ ++ if( z[n]==':' ) break; ++ if( sqlite3Isspace(z[n]) ) break; ++ if( z[n]=='-' ){ ++ if( n==5 && getDigits(&z[1], "40f", &Y)==1 ) break; ++ if( n==6 && getDigits(&z[1], "50f", &Y)==1 ) break; ++ } ++ } + if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){ +- rc = 1; ++ assert( rc==1 ); + break; + } +- if( z[n]==':' ){ ++ if( z[n]=='-' ){ ++ /* A modifier of the form (+|-)YYYY-MM-DD adds or subtracts the ++ ** specified number of years, months, and days. MM is limited to ++ ** the range 0-11 and DD is limited to 0-30. ++ */ ++ if( z0!='+' && z0!='-' ) break; /* Must start with +/- */ ++ if( n==5 ){ ++ if( getDigits(&z[1], "40f-20a-20d", &Y, &M, &D)!=3 ) break; ++ }else{ ++ assert( n==6 ); ++ if( getDigits(&z[1], "50f-20a-20d", &Y, &M, &D)!=3 ) break; ++ z++; ++ } ++ if( M>=12 ) break; /* M range 0..11 */ ++ if( D>=31 ) break; /* D range 0..30 */ ++ computeYMD_HMS(p); ++ p->validJD = 0; ++ if( z0=='-' ){ ++ p->Y -= Y; ++ p->M -= M; ++ D = -D; ++ }else{ ++ p->Y += Y; ++ p->M += M; ++ } ++ x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; ++ p->Y += x; ++ p->M -= x*12; ++ computeFloor(p); ++ computeJD(p); ++ p->validHMS = 0; ++ p->validYMD = 0; ++ p->iJD += (i64)D*86400000; ++ if( z[11]==0 ){ ++ rc = 0; ++ break; ++ } ++ if( sqlite3Isspace(z[11]) ++ && getDigits(&z[12], "20c:20e", &h, &m)==2 ++ ){ ++ z2 = &z[12]; ++ n = 2; ++ }else{ ++ break; ++ } ++ } ++ if( z2[n]==':' ){ + /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the + ** specified number of hours, minutes, seconds, and fractional seconds + ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be + ** omitted. + */ +- const char *z2 = z; ++ + DateTime tx; + sqlite3_int64 day; + if( !sqlite3Isdigit(*z2) ) z2++; +@@ -22515,7 +25735,7 @@ static int parseModifier( + tx.iJD -= 43200000; + day = tx.iJD/86400000; + tx.iJD -= day*86400000; +- if( z[0]=='-' ) tx.iJD = -tx.iJD; ++ if( z0=='-' ) tx.iJD = -tx.iJD; + computeJD(p); + clearYMD_HMS_TZ(p); + p->iJD += tx.iJD; +@@ -22528,39 +25748,44 @@ static int parseModifier( + z += n; + while( sqlite3Isspace(*z) ) z++; + n = sqlite3Strlen30(z); +- if( n>10 || n<3 ) break; ++ if( n<3 || n>10 ) break; + if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; + computeJD(p); +- rc = 1; ++ assert( rc==1 ); + rRounder = r<0 ? -0.5 : +0.5; ++ p->nFloor = 0; + for(i=0; i-aXformType[i].rLimit && rM += (int)r; + x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; + p->Y += x; + p->M -= x*12; ++ computeFloor(p); + p->validJD = 0; + r -= (int)r; + break; + } +- case 2: { /* Special processing to add years */ ++ case 5: { /* Special processing to add years */ + int y = (int)r; ++ assert( strcmp(aXformType[5].zName,"year")==0 ); + computeYMD_HMS(p); ++ assert( p->M>=0 && p->M<=12 ); + p->Y += y; ++ computeFloor(p); + p->validJD = 0; + r -= (int)r; + break; + } + } + computeJD(p); +- p->iJD += (sqlite3_int64)(r*aXformType[i].rXform + rRounder); ++ p->iJD += (sqlite3_int64)(r*1000.0*aXformType[i].rXform + rRounder); + rc = 0; + break; + } +@@ -22595,6 +25820,7 @@ static int isDate( + int eType; + memset(p, 0, sizeof(*p)); + if( argc==0 ){ ++ if( !sqlite3NotPureFunc(context) ) return 1; + return setDateTimeToCurrent(context, p); + } + if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT +@@ -22609,10 +25835,16 @@ static int isDate( + for(i=1; iisError || !validJulianDay(p->iJD) ) return 1; ++ if( argc==1 && p->validYMD && p->D>28 ){ ++ /* Make sure a YYYY-MM-DD is normalized. ++ ** Example: 2023-02-31 -> 2023-03-03 */ ++ assert( p->validJD ); ++ p->validYMD = 0; ++ } + return 0; + } + +@@ -22639,6 +25871,28 @@ static void juliandayFunc( + } + } + ++/* ++** unixepoch( TIMESTRING, MOD, MOD, ...) ++** ++** Return the number of seconds (including fractional seconds) since ++** the unix epoch of 1970-01-01 00:00:00 GMT. ++*/ ++static void unixepochFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ DateTime x; ++ if( isDate(context, argc, argv, &x)==0 ){ ++ computeJD(&x); ++ if( x.useSubsec ){ ++ sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0); ++ }else{ ++ sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); ++ } ++ } ++} ++ + /* + ** datetime( TIMESTRING, MOD, MOD, ...) + ** +@@ -22651,11 +25905,51 @@ static void datetimeFunc( + ){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ +- char zBuf[100]; ++ int Y, s, n; ++ char zBuf[32]; + computeYMD_HMS(&x); +- sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d", +- x.Y, x.M, x.D, x.h, x.m, (int)(x.s)); +- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); ++ Y = x.Y; ++ if( Y<0 ) Y = -Y; ++ zBuf[1] = '0' + (Y/1000)%10; ++ zBuf[2] = '0' + (Y/100)%10; ++ zBuf[3] = '0' + (Y/10)%10; ++ zBuf[4] = '0' + (Y)%10; ++ zBuf[5] = '-'; ++ zBuf[6] = '0' + (x.M/10)%10; ++ zBuf[7] = '0' + (x.M)%10; ++ zBuf[8] = '-'; ++ zBuf[9] = '0' + (x.D/10)%10; ++ zBuf[10] = '0' + (x.D)%10; ++ zBuf[11] = ' '; ++ zBuf[12] = '0' + (x.h/10)%10; ++ zBuf[13] = '0' + (x.h)%10; ++ zBuf[14] = ':'; ++ zBuf[15] = '0' + (x.m/10)%10; ++ zBuf[16] = '0' + (x.m)%10; ++ zBuf[17] = ':'; ++ if( x.useSubsec ){ ++ s = (int)(1000.0*x.s + 0.5); ++ zBuf[18] = '0' + (s/10000)%10; ++ zBuf[19] = '0' + (s/1000)%10; ++ zBuf[20] = '.'; ++ zBuf[21] = '0' + (s/100)%10; ++ zBuf[22] = '0' + (s/10)%10; ++ zBuf[23] = '0' + (s)%10; ++ zBuf[24] = 0; ++ n = 24; ++ }else{ ++ s = (int)x.s; ++ zBuf[18] = '0' + (s/10)%10; ++ zBuf[19] = '0' + (s)%10; ++ zBuf[20] = 0; ++ n = 20; ++ } ++ if( x.Y<0 ){ ++ zBuf[0] = '-'; ++ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); ++ }else{ ++ sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT); ++ } + } + } + +@@ -22671,10 +25965,33 @@ static void timeFunc( + ){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ +- char zBuf[100]; ++ int s, n; ++ char zBuf[16]; + computeHMS(&x); +- sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s); +- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); ++ zBuf[0] = '0' + (x.h/10)%10; ++ zBuf[1] = '0' + (x.h)%10; ++ zBuf[2] = ':'; ++ zBuf[3] = '0' + (x.m/10)%10; ++ zBuf[4] = '0' + (x.m)%10; ++ zBuf[5] = ':'; ++ if( x.useSubsec ){ ++ s = (int)(1000.0*x.s + 0.5); ++ zBuf[6] = '0' + (s/10000)%10; ++ zBuf[7] = '0' + (s/1000)%10; ++ zBuf[8] = '.'; ++ zBuf[9] = '0' + (s/100)%10; ++ zBuf[10] = '0' + (s/10)%10; ++ zBuf[11] = '0' + (s)%10; ++ zBuf[12] = 0; ++ n = 12; ++ }else{ ++ s = (int)x.s; ++ zBuf[6] = '0' + (s/10)%10; ++ zBuf[7] = '0' + (s)%10; ++ zBuf[8] = 0; ++ n = 8; ++ } ++ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); + } + } + +@@ -22690,29 +26007,108 @@ static void dateFunc( + ){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ +- char zBuf[100]; ++ int Y; ++ char zBuf[16]; + computeYMD(&x); +- sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D); +- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); ++ Y = x.Y; ++ if( Y<0 ) Y = -Y; ++ zBuf[1] = '0' + (Y/1000)%10; ++ zBuf[2] = '0' + (Y/100)%10; ++ zBuf[3] = '0' + (Y/10)%10; ++ zBuf[4] = '0' + (Y)%10; ++ zBuf[5] = '-'; ++ zBuf[6] = '0' + (x.M/10)%10; ++ zBuf[7] = '0' + (x.M)%10; ++ zBuf[8] = '-'; ++ zBuf[9] = '0' + (x.D/10)%10; ++ zBuf[10] = '0' + (x.D)%10; ++ zBuf[11] = 0; ++ if( x.Y<0 ){ ++ zBuf[0] = '-'; ++ sqlite3_result_text(context, zBuf, 11, SQLITE_TRANSIENT); ++ }else{ ++ sqlite3_result_text(context, &zBuf[1], 10, SQLITE_TRANSIENT); ++ } + } + } + ++/* ++** Compute the number of days after the most recent January 1. ++** ++** In other words, compute the zero-based day number for the ++** current year: ++** ++** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ... ++** Dec31 = 364 or 365. ++*/ ++static int daysAfterJan01(DateTime *pDate){ ++ DateTime jan01 = *pDate; ++ assert( jan01.validYMD ); ++ assert( jan01.validHMS ); ++ assert( pDate->validJD ); ++ jan01.validJD = 0; ++ jan01.M = 1; ++ jan01.D = 1; ++ computeJD(&jan01); ++ return (int)((pDate->iJD-jan01.iJD+43200000)/86400000); ++} ++ ++/* ++** Return the number of days after the most recent Monday. ++** ++** In other words, return the day of the week according ++** to this code: ++** ++** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday. ++*/ ++static int daysAfterMonday(DateTime *pDate){ ++ assert( pDate->validJD ); ++ return (int)((pDate->iJD+43200000)/86400000) % 7; ++} ++ ++/* ++** Return the number of days after the most recent Sunday. ++** ++** In other words, return the day of the week according ++** to this code: ++** ++** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday ++*/ ++static int daysAfterSunday(DateTime *pDate){ ++ assert( pDate->validJD ); ++ return (int)((pDate->iJD+129600000)/86400000) % 7; ++} ++ + /* + ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) + ** + ** Return a string described by FORMAT. Conversions as follows: + ** +-** %d day of month ++** %d day of month 01-31 ++** %e day of month 1-31 + ** %f ** fractional seconds SS.SSS ++** %F ISO date. YYYY-MM-DD ++** %G ISO year corresponding to %V 0000-9999. ++** %g 2-digit ISO year corresponding to %V 00-99 + ** %H hour 00-24 +-** %j day of year 000-366 ++** %k hour 0-24 (leading zero converted to space) ++** %I hour 01-12 ++** %j day of year 001-366 + ** %J ** julian day number ++** %l hour 1-12 (leading zero converted to space) + ** %m month 01-12 + ** %M minute 00-59 ++** %p "am" or "pm" ++** %P "AM" or "PM" ++** %R time as HH:MM + ** %s seconds since 1970-01-01 + ** %S seconds 00-59 +-** %w day of week 0-6 sunday==0 +-** %W week of year 00-53 ++** %T time as HH:MM:SS ++** %u day of week 1-7 Monday==1, Sunday==7 ++** %w day of week 0-6 Sunday==0, Monday==1 ++** %U week of year 00-53 (First Sunday is start of week 01) ++** %V week of year 01-53 (First week containing Thursday is week 01) ++** %W week of year 00-53 (First Monday is start of week 01) + ** %Y year 0000-9999 + ** %% % + */ +@@ -22722,131 +26118,161 @@ static void strftimeFunc( + sqlite3_value **argv + ){ + DateTime x; +- u64 n; + size_t i,j; +- char *z; + sqlite3 *db; + const char *zFmt; +- char zBuf[100]; ++ sqlite3_str sRes; ++ ++ + if( argc==0 ) return; + zFmt = (const char*)sqlite3_value_text(argv[0]); + if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; + db = sqlite3_context_db_handle(context); +- for(i=0, n=1; zFmt[i]; i++, n++){ +- if( zFmt[i]=='%' ){ +- switch( zFmt[i+1] ){ +- case 'd': +- case 'H': +- case 'm': +- case 'M': +- case 'S': +- case 'W': +- n++; +- /* fall thru */ +- case 'w': +- case '%': +- break; +- case 'f': +- n += 8; +- break; +- case 'j': +- n += 3; +- break; +- case 'Y': +- n += 8; +- break; +- case 's': +- case 'J': +- n += 50; +- break; +- default: +- return; /* ERROR. return a NULL */ +- } +- i++; +- } +- } +- testcase( n==sizeof(zBuf)-1 ); +- testcase( n==sizeof(zBuf) ); +- testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); +- testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ); +- if( n(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){ +- sqlite3_result_error_toobig(context); +- return; +- }else{ +- z = sqlite3DbMallocRawNN(db, (int)n); +- if( z==0 ){ +- sqlite3_result_error_nomem(context); +- return; +- } +- } ++ sqlite3StrAccumInit(&sRes, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); ++ + computeJD(&x); + computeYMD_HMS(&x); + for(i=j=0; zFmt[i]; i++){ +- if( zFmt[i]!='%' ){ +- z[j++] = zFmt[i]; +- }else{ +- i++; +- switch( zFmt[i] ){ +- case 'd': sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break; +- case 'f': { +- double s = x.s; +- if( s>59.999 ) s = 59.999; +- sqlite3_snprintf(7, &z[j],"%06.3f", s); +- j += sqlite3Strlen30(&z[j]); +- break; +- } +- case 'H': sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break; +- case 'W': /* Fall thru */ +- case 'j': { +- int nDay; /* Number of days since 1st day of year */ +- DateTime y = x; +- y.validJD = 0; +- y.M = 1; +- y.D = 1; +- computeJD(&y); +- nDay = (int)((x.iJD-y.iJD+43200000)/86400000); +- if( zFmt[i]=='W' ){ +- int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ +- wd = (int)(((x.iJD+43200000)/86400000)%7); +- sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7); +- j += 2; +- }else{ +- sqlite3_snprintf(4, &z[j],"%03d",nDay+1); +- j += 3; +- } +- break; ++ char cf; ++ if( zFmt[i]!='%' ) continue; ++ if( j59.999) ) s = 59.999; ++ sqlite3_str_appendf(&sRes, "%06.3f", s); ++ break; ++ } ++ case 'F': { ++ sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D); ++ break; ++ } ++ case 'G': /* Fall thru */ ++ case 'g': { ++ DateTime y = x; ++ assert( y.validJD ); ++ /* Move y so that it is the Thursday in the same week as x */ ++ y.iJD += (3 - daysAfterMonday(&x))*86400000; ++ y.validYMD = 0; ++ computeYMD(&y); ++ if( cf=='g' ){ ++ sqlite3_str_appendf(&sRes, "%02d", y.Y%100); ++ }else{ ++ sqlite3_str_appendf(&sRes, "%04d", y.Y); + } +- case 'J': { +- sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); +- j+=sqlite3Strlen30(&z[j]); +- break; ++ break; ++ } ++ case 'H': ++ case 'k': { ++ sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h); ++ break; ++ } ++ case 'I': /* Fall thru */ ++ case 'l': { ++ int h = x.h; ++ if( h>12 ) h -= 12; ++ if( h==0 ) h = 12; ++ sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); ++ break; ++ } ++ case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */ ++ sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1); ++ break; ++ } ++ case 'J': { /* Julian day number. (Non-standard) */ ++ sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); ++ break; ++ } ++ case 'm': { ++ sqlite3_str_appendf(&sRes,"%02d",x.M); ++ break; ++ } ++ case 'M': { ++ sqlite3_str_appendf(&sRes,"%02d",x.m); ++ break; ++ } ++ case 'p': /* Fall thru */ ++ case 'P': { ++ if( x.h>=12 ){ ++ sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2); ++ }else{ ++ sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2); + } +- case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; +- case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; +- case 's': { ++ break; ++ } ++ case 'R': { ++ sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m); ++ break; ++ } ++ case 's': { ++ if( x.useSubsec ){ ++ sqlite3_str_appendf(&sRes,"%.3f", ++ (x.iJD - 21086676*(i64)10000000)/1000.0); ++ }else{ + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); +- sqlite3Int64ToText(iS, &z[j]); +- j += sqlite3Strlen30(&z[j]); +- break; +- } +- case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; +- case 'w': { +- z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; +- break; ++ sqlite3_str_appendf(&sRes,"%lld",iS); + } +- case 'Y': { +- sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]); +- break; +- } +- default: z[j++] = '%'; break; ++ break; ++ } ++ case 'S': { ++ sqlite3_str_appendf(&sRes,"%02d",(int)x.s); ++ break; ++ } ++ case 'T': { ++ sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); ++ break; ++ } ++ case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */ ++ case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */ ++ char c = (char)daysAfterSunday(&x) + '0'; ++ if( c=='0' && cf=='u' ) c = '7'; ++ sqlite3_str_appendchar(&sRes, 1, c); ++ break; ++ } ++ case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */ ++ sqlite3_str_appendf(&sRes,"%02d", ++ (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7); ++ break; ++ } ++ case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */ ++ DateTime y = x; ++ /* Adjust y so that is the Thursday in the same week as x */ ++ assert( y.validJD ); ++ y.iJD += (3 - daysAfterMonday(&x))*86400000; ++ y.validYMD = 0; ++ computeYMD(&y); ++ sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1); ++ break; ++ } ++ case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */ ++ sqlite3_str_appendf(&sRes,"%02d", ++ (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7); ++ break; ++ } ++ case 'Y': { ++ sqlite3_str_appendf(&sRes,"%04d",x.Y); ++ break; ++ } ++ case '%': { ++ sqlite3_str_appendchar(&sRes, 1, '%'); ++ break; ++ } ++ default: { ++ sqlite3_str_reset(&sRes); ++ return; + } + } + } +- z[j] = 0; +- sqlite3_result_text(context, z, -1, +- z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC); ++ if( j=d2.iJD ){ ++ sign = '+'; ++ Y = d1.Y - d2.Y; ++ if( Y ){ ++ d2.Y = d1.Y; ++ d2.validJD = 0; ++ computeJD(&d2); ++ } ++ M = d1.M - d2.M; ++ if( M<0 ){ ++ Y--; ++ M += 12; ++ } ++ if( M!=0 ){ ++ d2.M = d1.M; ++ d2.validJD = 0; ++ computeJD(&d2); ++ } ++ while( d1.iJDd2.iJD ){ ++ M--; ++ if( M<0 ){ ++ M = 11; ++ Y--; ++ } ++ d2.M++; ++ if( d2.M>12 ){ ++ d2.M = 1; ++ d2.Y++; ++ } ++ d2.validJD = 0; ++ computeJD(&d2); ++ } ++ d1.iJD = d2.iJD - d1.iJD; ++ d1.iJD += (u64)1486995408 * (u64)100000; ++ } ++ clearYMD_HMS_TZ(&d1); ++ computeYMD_HMS(&d1); ++ sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); ++ sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", ++ sign, Y, M, d1.D-1, d1.h, d1.m, d1.s); ++ sqlite3ResultStrAccum(context, &sRes); ++} ++ ++ + /* + ** current_timestamp() + ** +@@ -22937,6 +26472,36 @@ static void currentTimeFunc( + } + #endif + ++#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG) ++/* ++** datedebug(...) ++** ++** This routine returns JSON that describes the internal DateTime object. ++** Used for debugging and testing only. Subject to change. ++*/ ++static void datedebugFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ DateTime x; ++ if( isDate(context, argc, argv, &x)==0 ){ ++ char *zJson; ++ zJson = sqlite3_mprintf( ++ "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d," ++ "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d," ++ "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d," ++ "isUtc:%d,isLocal:%d}", ++ x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz, ++ x.s, x.validJD, x.validYMD, x.validHMS, ++ x.nFloor, x.rawS, x.isError, x.useSubsec, ++ x.isUtc, x.isLocal); ++ sqlite3_result_text(context, zJson, -1, sqlite3_free); ++ } ++} ++#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */ ++ ++ + /* + ** This function registered all of the above C functions as SQL + ** functions. This should be the only routine in this file with +@@ -22946,10 +26511,15 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){ + static FuncDef aDateTimeFuncs[] = { + #ifndef SQLITE_OMIT_DATETIME_FUNCS + PURE_DATE(julianday, -1, 0, 0, juliandayFunc ), ++ PURE_DATE(unixepoch, -1, 0, 0, unixepochFunc ), + PURE_DATE(date, -1, 0, 0, dateFunc ), + PURE_DATE(time, -1, 0, 0, timeFunc ), + PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), + PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), ++ PURE_DATE(timediff, 2, 0, 0, timediffFunc ), ++#ifdef SQLITE_DEBUG ++ PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ), ++#endif + DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), + DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), + DFUNCTION(current_date, 0, 0, 0, cdateFunc ), +@@ -23072,9 +26642,11 @@ SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){ + } + SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file *id, int lockType){ + DO_OS_MALLOC_TEST(id); ++ assert( lockType>=SQLITE_LOCK_SHARED && lockType<=SQLITE_LOCK_EXCLUSIVE ); + return id->pMethods->xLock(id, lockType); + } + SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file *id, int lockType){ ++ assert( lockType==SQLITE_LOCK_NONE || lockType==SQLITE_LOCK_SHARED ); + return id->pMethods->xUnlock(id, lockType); + } + SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){ +@@ -23095,17 +26667,24 @@ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ + #ifdef SQLITE_TEST + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO + && op!=SQLITE_FCNTL_LOCK_TIMEOUT ++ && op!=SQLITE_FCNTL_CKPT_DONE ++ && op!=SQLITE_FCNTL_CKPT_START + ){ + /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite + ** is using a regular VFS, it is called after the corresponding + ** transaction has been committed. Injecting a fault at this point +- ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM ++ ** confuses the test scripts - the COMMIT command returns SQLITE_NOMEM + ** but the transaction is committed anyway. + ** + ** The core must call OsFileControl() though, not OsFileControlHint(), + ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably + ** means the commit really has failed and an error should be returned +- ** to the user. */ ++ ** to the user. ++ ** ++ ** The CKPT_DONE and CKPT_START file-controls are write-only signals ++ ** to the cksumvfs. Their return code is meaningless and is ignored ++ ** by the SQLite core, so there is no point in simulating OOMs for them. ++ */ + DO_OS_MALLOC_TEST(id); + } + #endif +@@ -23120,6 +26699,7 @@ SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){ + return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); + } + SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ ++ if( NEVER(id->pMethods==0) ) return 0; + return id->pMethods->xDeviceCharacteristics(id); + } + #ifndef SQLITE_OMIT_WAL +@@ -23181,6 +26761,7 @@ SQLITE_PRIVATE int sqlite3OsOpen( + ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, + ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before + ** reaching the VFS. */ ++ assert( zPath || (flags & SQLITE_OPEN_EXCLUSIVE) ); + rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut); + assert( rc==SQLITE_OK || pFile->pMethods==0 ); + return rc; +@@ -23188,7 +26769,7 @@ SQLITE_PRIVATE int sqlite3OsOpen( + SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + DO_OS_MALLOC_TEST(0); + assert( dirSync==0 || dirSync==1 ); +- return pVfs->xDelete(pVfs, zPath, dirSync); ++ return pVfs->xDelete!=0 ? pVfs->xDelete(pVfs, zPath, dirSync) : SQLITE_OK; + } + SQLITE_PRIVATE int sqlite3OsAccess( + sqlite3_vfs *pVfs, +@@ -23211,6 +26792,8 @@ SQLITE_PRIVATE int sqlite3OsFullPathname( + } + #ifndef SQLITE_OMIT_LOAD_EXTENSION + SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ ++ assert( zPath!=0 ); ++ assert( strlen(zPath)<=SQLITE_MAX_PATHLEN ); /* tag-20210611-1 */ + return pVfs->xDlOpen(pVfs, zPath); + } + SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ +@@ -23272,12 +26855,15 @@ SQLITE_PRIVATE int sqlite3OsOpenMalloc( + rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags); + if( rc!=SQLITE_OK ){ + sqlite3_free(pFile); ++ *ppFile = 0; + }else{ + *ppFile = pFile; + } + }else{ ++ *ppFile = 0; + rc = SQLITE_NOMEM_BKPT; + } ++ assert( *ppFile!=0 || rc!=SQLITE_OK ); + return rc; + } + SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *pFile){ +@@ -23708,7 +27294,7 @@ static void *sqlite3MemMalloc(int nByte){ + ** or sqlite3MemRealloc(). + ** + ** For this low-level routine, we already know that pPrior!=0 since +-** cases where pPrior==0 will have been intecepted and dealt with ++** cases where pPrior==0 will have been intercepted and dealt with + ** by higher-level routines. + */ + static void sqlite3MemFree(void *pPrior){ +@@ -23796,7 +27382,7 @@ static int sqlite3MemInit(void *NotUsed){ + return SQLITE_OK; + } + len = sizeof(cpuCount); +- /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ ++ /* One usually wants to use hw.activecpu for MT decisions, but not here */ + sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); + if( cpuCount>1 ){ + /* defer MT decisions to system malloc */ +@@ -23995,7 +27581,7 @@ static void adjustStats(int iSize, int increment){ + ** This routine checks the guards at either end of the allocation and + ** if they are incorrect it asserts. + */ +-static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){ ++static struct MemBlockHdr *sqlite3MemsysGetHeader(const void *pAllocation){ + struct MemBlockHdr *p; + int *pInt; + u8 *pU8; +@@ -24242,7 +27828,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){ + ** + ** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + */ +-SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){ ++SQLITE_PRIVATE int sqlite3MemdebugHasType(const void *p, u8 eType){ + int rc = 1; + if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ + struct MemBlockHdr *pHdr; +@@ -24264,7 +27850,7 @@ SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){ + ** + ** assert( sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); + */ +-SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){ ++SQLITE_PRIVATE int sqlite3MemdebugNoType(const void *p, u8 eType){ + int rc = 1; + if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ + struct MemBlockHdr *pHdr; +@@ -25487,8 +29073,17 @@ static void *memsys5Realloc(void *pPrior, int nBytes){ + */ + static int memsys5Roundup(int n){ + int iFullSz; +- if( n > 0x40000000 ) return 0; +- for(iFullSz=mem5.szAtom; iFullSz0x10000000 ){ ++ if( n>0x40000000 ) return 0; ++ if( n>0x20000000 ) return 0x40000000; ++ return 0x20000000; ++ } ++ for(iFullSz=mem5.szAtom*8; iFullSz=(i64)n ) return iFullSz/2; + return iFullSz; + } + +@@ -25779,7 +29374,7 @@ static void checkMutexFree(sqlite3_mutex *p){ + assert( SQLITE_MUTEX_FAST<2 ); + assert( SQLITE_MUTEX_WARNONCONTENTION<2 ); + +-#if SQLITE_ENABLE_API_ARMOR ++#ifdef SQLITE_ENABLE_API_ARMOR + if( ((CheckMutex*)p)->iType<2 ) + #endif + { +@@ -25993,16 +29588,29 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ + /* + ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are + ** intended for use inside assert() statements. ++** ++** Because these routines raise false-positive alerts in TSAN, disable ++** them (make them always return 1) when compiling with TSAN. + */ + SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ ++# if defined(__has_feature) ++# if __has_feature(thread_sanitizer) ++ p = 0; ++# endif ++# endif + assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld ); + return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); + } + SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ ++# if defined(__has_feature) ++# if __has_feature(thread_sanitizer) ++ p = 0; ++# endif ++# endif + assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld ); + return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); + } +-#endif ++#endif /* NDEBUG */ + + #endif /* !defined(SQLITE_MUTEX_OMIT) */ + +@@ -26254,7 +29862,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ + + /* + ** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields +-** are necessary under two condidtions: (1) Debug builds and (2) using ++** are necessary under two conditions: (1) Debug builds and (2) using + ** home-grown mutexes. Encapsulate these conditions into a single #define. + */ + #if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) +@@ -26451,7 +30059,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ + */ + static void pthreadMutexFree(sqlite3_mutex *p){ + assert( p->nRef==0 ); +-#if SQLITE_ENABLE_API_ARMOR ++#ifdef SQLITE_ENABLE_API_ARMOR + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) + #endif + { +@@ -26642,205 +30250,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ + /* + ** Include code that is common to all os_*.c files + */ +-/************** Include os_common.h in the middle of mutex_w32.c *************/ +-/************** Begin file os_common.h ***************************************/ +-/* +-** 2004 May 22 +-** +-** 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 contains macros and a little bit of code that is common to +-** all of the platform-specific files (os_*.c) and is #included into those +-** files. +-** +-** This file should be #included by the os_*.c files only. It is not a +-** general purpose header file. +-*/ +-#ifndef _OS_COMMON_H_ +-#define _OS_COMMON_H_ +- +-/* +-** At least two bugs have slipped in because we changed the MEMORY_DEBUG +-** macro to SQLITE_DEBUG and some older makefiles have not yet made the +-** switch. The following code should catch this problem at compile-time. +-*/ +-#ifdef MEMORY_DEBUG +-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." +-#endif +- +-/* +-** Macros for performance tracing. Normally turned off. Only works +-** on i486 hardware. +-*/ +-#ifdef SQLITE_PERFORMANCE_TRACE +- +-/* +-** hwtime.h contains inline assembler code for implementing +-** high-performance timing routines. +-*/ +-/************** Include hwtime.h in the middle of os_common.h ****************/ +-/************** Begin file hwtime.h ******************************************/ +-/* +-** 2008 May 27 +-** +-** 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 contains inline asm code for retrieving "high-performance" +-** counters for x86 and x86_64 class CPUs. +-*/ +-#ifndef SQLITE_HWTIME_H +-#define SQLITE_HWTIME_H +- +-/* +-** The following routine only works on pentium-class (or newer) processors. +-** It uses the RDTSC opcode to read the cycle count value out of the +-** processor and returns that value. This can be used for high-res +-** profiling. +-*/ +-#if !defined(__STRICT_ANSI__) && \ +- (defined(__GNUC__) || defined(_MSC_VER)) && \ +- (defined(i386) || defined(__i386__) || defined(_M_IX86)) +- +- #if defined(__GNUC__) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned int lo, hi; +- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +- return (sqlite_uint64)hi << 32 | lo; +- } +- +- #elif defined(_MSC_VER) +- +- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ +- __asm { +- rdtsc +- ret ; return value at EDX:EAX +- } +- } +- +- #endif +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long val; +- __asm__ __volatile__ ("rdtsc" : "=A" (val)); +- return val; +- } +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long long retval; +- unsigned long junk; +- __asm__ __volatile__ ("\n\ +- 1: mftbu %1\n\ +- mftb %L0\n\ +- mftbu %0\n\ +- cmpw %0,%1\n\ +- bne 1b" +- : "=r" (retval), "=r" (junk)); +- return retval; +- } +- +-#else +- +- /* +- ** asm() is needed for hardware timing support. Without asm(), +- ** disable the sqlite3Hwtime() routine. +- ** +- ** sqlite3Hwtime() is only used for some obscure debugging +- ** and analysis configurations, not in any deliverable, so this +- ** should not be a great loss. +- */ +-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } +- +-#endif +- +-#endif /* !defined(SQLITE_HWTIME_H) */ +- +-/************** End of hwtime.h **********************************************/ +-/************** Continuing where we left off in os_common.h ******************/ +- +-static sqlite_uint64 g_start; +-static sqlite_uint64 g_elapsed; +-#define TIMER_START g_start=sqlite3Hwtime() +-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start +-#define TIMER_ELAPSED g_elapsed +-#else +-#define TIMER_START +-#define TIMER_END +-#define TIMER_ELAPSED ((sqlite_uint64)0) +-#endif +- +-/* +-** If we compile with the SQLITE_TEST macro set, then the following block +-** of code will give us the ability to simulate a disk I/O error. This +-** is used for testing the I/O recovery logic. +-*/ +-#if defined(SQLITE_TEST) +-SQLITE_API extern int sqlite3_io_error_hit; +-SQLITE_API extern int sqlite3_io_error_hardhit; +-SQLITE_API extern int sqlite3_io_error_pending; +-SQLITE_API extern int sqlite3_io_error_persist; +-SQLITE_API extern int sqlite3_io_error_benign; +-SQLITE_API extern int sqlite3_diskfull_pending; +-SQLITE_API extern int sqlite3_diskfull; +-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) +-#define SimulateIOError(CODE) \ +- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ +- || sqlite3_io_error_pending-- == 1 ) \ +- { local_ioerr(); CODE; } +-static void local_ioerr(){ +- IOTRACE(("IOERR\n")); +- sqlite3_io_error_hit++; +- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; +-} +-#define SimulateDiskfullError(CODE) \ +- if( sqlite3_diskfull_pending ){ \ +- if( sqlite3_diskfull_pending == 1 ){ \ +- local_ioerr(); \ +- sqlite3_diskfull = 1; \ +- sqlite3_io_error_hit = 1; \ +- CODE; \ +- }else{ \ +- sqlite3_diskfull_pending--; \ +- } \ +- } +-#else +-#define SimulateIOErrorBenign(X) +-#define SimulateIOError(A) +-#define SimulateDiskfullError(A) +-#endif /* defined(SQLITE_TEST) */ +- +-/* +-** When testing, keep a count of the number of open files. +-*/ +-#if defined(SQLITE_TEST) +-SQLITE_API extern int sqlite3_open_file_count; +-#define OpenCounter(X) sqlite3_open_file_count+=(X) +-#else +-#define OpenCounter(X) +-#endif /* defined(SQLITE_TEST) */ +- +-#endif /* !defined(_OS_COMMON_H_) */ +- +-/************** End of os_common.h *******************************************/ +-/************** Continuing where we left off in mutex_w32.c ******************/ ++/* #include "os_common.h" */ + + /* + ** Include the header file for the Windows VFS. +@@ -26871,6 +30281,8 @@ SQLITE_API extern int sqlite3_open_file_count; + + #ifdef __CYGWIN__ + # include ++# include /* amalgamator: dontcache */ ++# include /* amalgamator: dontcache */ + # include /* amalgamator: dontcache */ + #endif + +@@ -26953,7 +30365,7 @@ struct sqlite3_mutex { + CRITICAL_SECTION mutex; /* Mutex controlling the lock */ + int id; /* Mutex type */ + #ifdef SQLITE_DEBUG +- volatile int nRef; /* Number of enterances */ ++ volatile int nRef; /* Number of entrances */ + volatile DWORD owner; /* Thread holding this mutex */ + volatile LONG trace; /* True to trace changes */ + #endif +@@ -27002,7 +30414,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){ + SQLITE_MEMORY_BARRIER; + #elif defined(__GNUC__) + __sync_synchronize(); +-#elif MSVC_VERSION>=1300 ++#elif MSVC_VERSION>=1400 + _ReadWriteBarrier(); + #elif defined(MemoryBarrier) + MemoryBarrier(); +@@ -27478,7 +30890,6 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){ + if( sqlite3GlobalConfig.m.xMalloc==0 ){ + sqlite3MemSetDefault(); + } +- memset(&mem0, 0, sizeof(mem0)); + mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); + if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 + || sqlite3GlobalConfig.nPage<=0 ){ +@@ -27539,6 +30950,24 @@ static void sqlite3MallocAlarm(int nByte){ + sqlite3_mutex_enter(mem0.mutex); + } + ++#ifdef SQLITE_DEBUG ++/* ++** This routine is called whenever an out-of-memory condition is seen, ++** It's only purpose to to serve as a breakpoint for gdb or similar ++** code debuggers when working on out-of-memory conditions, for example ++** caused by PRAGMA hard_heap_limit=N. ++*/ ++static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){ ++ static u64 nOomFault = 0; ++ nOomFault += n; ++ /* The assert() is never reached in a human lifetime. It is here mostly ++ ** to prevent code optimizers from optimizing out this function. */ ++ assert( (nOomFault>>32) < 0xffffffff ); ++} ++#else ++# define test_oom_breakpoint(X) /* No-op for production builds */ ++#endif ++ + /* + ** Do a memory allocation with statistics and alarms. Assume the + ** lock is already held. +@@ -27565,6 +30994,7 @@ static void mallocWithAlarm(int n, void **pp){ + if( mem0.hardLimit ){ + nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); + if( nUsed >= mem0.hardLimit - nFull ){ ++ test_oom_breakpoint(1); + *pp = 0; + return; + } +@@ -27588,18 +31018,34 @@ static void mallocWithAlarm(int n, void **pp){ + *pp = p; + } + ++/* ++** Maximum size of any single memory allocation. ++** ++** This is not a limit on the total amount of memory used. This is ++** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc(). ++** ++** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 ++** This provides a 256-byte safety margin for defense against 32-bit ++** signed integer overflow bugs when computing memory allocation sizes. ++** Paranoid applications might want to reduce the maximum allocation size ++** further for an even larger safety margin. 0x3fffffff or 0x0fffffff ++** or even smaller would be reasonable upper bounds on the size of a memory ++** allocations for most applications. ++*/ ++#ifndef SQLITE_MAX_ALLOCATION_SIZE ++# define SQLITE_MAX_ALLOCATION_SIZE 2147483391 ++#endif ++#if SQLITE_MAX_ALLOCATION_SIZE>2147483391 ++# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391 ++#endif ++ + /* + ** Allocate memory. This routine is like sqlite3_malloc() except that it + ** assumes the memory subsystem has already been initialized. + */ + SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ + void *p; +- if( n==0 || n>=0x7fffff00 ){ +- /* A memory allocation of a number of bytes which is near the maximum +- ** signed integer value might cause an integer overflow inside of the +- ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving +- ** 255 bytes of overhead. SQLite itself will never use anything near +- ** this amount. The only way to reach the limit is with sqlite3_malloc() */ ++ if( n==0 || n>SQLITE_MAX_ALLOCATION_SIZE ){ + p = 0; + }else if( sqlite3GlobalConfig.bMemstat ){ + sqlite3_mutex_enter(mem0.mutex); +@@ -27634,8 +31080,8 @@ SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ + ** TRUE if p is a lookaside memory allocation from db + */ + #ifndef SQLITE_OMIT_LOOKASIDE +-static int isLookaside(sqlite3 *db, void *p){ +- return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); ++static int isLookaside(sqlite3 *db, const void *p){ ++ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pTrueEnd); + } + #else + #define isLookaside(A,B) 0 +@@ -27645,32 +31091,30 @@ static int isLookaside(sqlite3 *db, void *p){ + ** Return the size of a memory allocation previously obtained from + ** sqlite3Malloc() or sqlite3_malloc(). + */ +-SQLITE_PRIVATE int sqlite3MallocSize(void *p){ ++SQLITE_PRIVATE int sqlite3MallocSize(const void *p){ + assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); +- return sqlite3GlobalConfig.m.xSize(p); ++ return sqlite3GlobalConfig.m.xSize((void*)p); + } +-static int lookasideMallocSize(sqlite3 *db, void *p){ ++static int lookasideMallocSize(sqlite3 *db, const void *p){ + #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + return plookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL; + #else + return db->lookaside.szTrue; + #endif + } +-SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ ++SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, const void *p){ + assert( p!=0 ); + #ifdef SQLITE_DEBUG +- if( db==0 || !isLookaside(db,p) ){ +- if( db==0 ){ +- assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); +- assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); +- }else{ +- assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); +- assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); +- } ++ if( db==0 ){ ++ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); ++ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); ++ }else if( !isLookaside(db,p) ){ ++ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); ++ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + } + #endif + if( db ){ +- if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ ++ if( ((uptr)p)<(uptr)(db->lookaside.pTrueEnd) ){ + #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ + assert( sqlite3_mutex_held(db->mutex) ); +@@ -27683,7 +31127,7 @@ SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ + } + } + } +- return sqlite3GlobalConfig.m.xSize(p); ++ return sqlite3GlobalConfig.m.xSize((void*)p); + } + SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); +@@ -27726,14 +31170,11 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ + assert( db==0 || sqlite3_mutex_held(db->mutex) ); + assert( p!=0 ); + if( db ){ +- if( db->pnBytesFreed ){ +- measureAllocationSize(db, p); +- return; +- } + if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ + #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ + LookasideSlot *pBuf = (LookasideSlot*)p; ++ assert( db->pnBytesFreed==0 ); + #ifdef SQLITE_DEBUG + memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ + #endif +@@ -27744,6 +31185,7 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ + #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ + if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ + LookasideSlot *pBuf = (LookasideSlot*)p; ++ assert( db->pnBytesFreed==0 ); + #ifdef SQLITE_DEBUG + memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ + #endif +@@ -27752,6 +31194,10 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ + return; + } + } ++ if( db->pnBytesFreed ){ ++ measureAllocationSize(db, p); ++ return; ++ } + } + assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); +@@ -27759,6 +31205,43 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ + sqlite3MemdebugSetType(p, MEMTYPE_HEAP); + sqlite3_free(p); + } ++SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3 *db, void *p){ ++ assert( db!=0 ); ++ assert( sqlite3_mutex_held(db->mutex) ); ++ assert( p!=0 ); ++ if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ ++#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE ++ if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ ++ LookasideSlot *pBuf = (LookasideSlot*)p; ++ assert( db->pnBytesFreed==0 ); ++#ifdef SQLITE_DEBUG ++ memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ ++#endif ++ pBuf->pNext = db->lookaside.pSmallFree; ++ db->lookaside.pSmallFree = pBuf; ++ return; ++ } ++#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ ++ if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ ++ LookasideSlot *pBuf = (LookasideSlot*)p; ++ assert( db->pnBytesFreed==0 ); ++#ifdef SQLITE_DEBUG ++ memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ ++#endif ++ pBuf->pNext = db->lookaside.pFree; ++ db->lookaside.pFree = pBuf; ++ return; ++ } ++ } ++ if( db->pnBytesFreed ){ ++ measureAllocationSize(db, p); ++ return; ++ } ++ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); ++ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); ++ sqlite3MemdebugSetType(p, MEMTYPE_HEAP); ++ sqlite3_free(p); ++} + SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){ + assert( db==0 || sqlite3_mutex_held(db->mutex) ); + if( p ) sqlite3DbFreeNN(db, p); +@@ -27791,12 +31274,18 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ + if( nOld==nNew ){ + pNew = pOld; + }else if( sqlite3GlobalConfig.bMemstat ){ ++ sqlite3_int64 nUsed; + sqlite3_mutex_enter(mem0.mutex); + sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); + nDiff = nNew - nOld; +- if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= ++ if( nDiff>0 && (nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >= + mem0.alarmThreshold-nDiff ){ + sqlite3MallocAlarm(nDiff); ++ if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ ++ sqlite3_mutex_leave(mem0.mutex); ++ test_oom_breakpoint(1); ++ return 0; ++ } + } + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); + #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +@@ -28053,9 +31542,14 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){ + */ + SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){ + int n; ++#ifdef SQLITE_DEBUG ++ /* Because of the way the parser works, the span is guaranteed to contain ++ ** at least one non-space character */ ++ for(n=0; sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n]0) && sqlite3Isspace(zStart[n-1]) ) n--; ++ while( sqlite3Isspace(zStart[n-1]) ) n--; + return sqlite3DbStrNDup(db, zStart, n); + } + +@@ -28063,8 +31557,9 @@ SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const cha + ** Free any prior content in *pz and replace it with a copy of zNew. + */ + SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){ ++ char *z = sqlite3DbStrDup(db, zNew); + sqlite3DbFree(db, *pz); +- *pz = sqlite3DbStrDup(db, zNew); ++ *pz = z; + } + + /* +@@ -28072,8 +31567,15 @@ SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){ + ** has happened. This routine will set db->mallocFailed, and also + ** temporarily disable the lookaside memory allocator and interrupt + ** any running VDBEs. ++** ++** Always return a NULL pointer so that this routine can be invoked using ++** ++** return sqlite3OomFault(db); ++** ++** and thereby avoid unnecessary stack frame allocations for the overwhelmingly ++** common case where no OOM occurs. + */ +-SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ ++SQLITE_PRIVATE void *sqlite3OomFault(sqlite3 *db){ + if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ + db->mallocFailed = 1; + if( db->nVdbeExec>0 ){ +@@ -28081,9 +31583,16 @@ SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ + } + DisableLookaside; + if( db->pParse ){ ++ Parse *pParse; ++ sqlite3ErrorMsg(db->pParse, "out of memory"); + db->pParse->rc = SQLITE_NOMEM_BKPT; ++ for(pParse=db->pParse->pOuterParse; pParse; pParse = pParse->pOuterParse){ ++ pParse->nErr++; ++ pParse->rc = SQLITE_NOMEM; ++ } + } + } ++ return 0; + } + + /* +@@ -28103,12 +31612,15 @@ SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){ + } + + /* +-** Take actions at the end of an API call to indicate an OOM error ++** Take actions at the end of an API call to deal with error codes. + */ +-static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ +- sqlite3OomClear(db); +- sqlite3Error(db, SQLITE_NOMEM); +- return SQLITE_NOMEM_BKPT; ++static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){ ++ if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ ++ sqlite3OomClear(db); ++ sqlite3Error(db, SQLITE_NOMEM); ++ return SQLITE_NOMEM_BKPT; ++ } ++ return rc & db->errMask; + } + + /* +@@ -28130,10 +31642,10 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ + */ + assert( db!=0 ); + assert( sqlite3_mutex_held(db->mutex) ); +- if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ +- return apiOomError(db); ++ if( db->mallocFailed || rc ){ ++ return apiHandleError(db, rc); + } +- return rc & db->errMask; ++ return 0; + } + + /************** End of malloc.c **********************************************/ +@@ -28165,17 +31677,17 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ + #define etPERCENT 7 /* Percent symbol. %% */ + #define etCHARX 8 /* Characters. %c */ + /* The rest are extensions, not normally found in printf() */ +-#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ +-#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', +- NULL pointers replaced by SQL NULL. %Q */ +-#define etTOKEN 11 /* a pointer to a Token structure */ +-#define etSRCLIST 12 /* a pointer to a SrcList */ +-#define etPOINTER 13 /* The %p conversion */ +-#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ +-#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ +-#define etDECIMAL 16 /* %d or %u, but not %x, %o */ ++#define etESCAPE_q 9 /* Strings with '\'' doubled. %q */ ++#define etESCAPE_Q 10 /* Strings with '\'' doubled and enclosed in '', ++ NULL pointers replaced by SQL NULL. %Q */ ++#define etTOKEN 11 /* a pointer to a Token structure */ ++#define etSRCITEM 12 /* a pointer to a SrcItem */ ++#define etPOINTER 13 /* The %p conversion */ ++#define etESCAPE_w 14 /* %w -> Strings with '\"' doubled */ ++#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ ++#define etDECIMAL 16 /* %d or %u, but not %x, %o */ + +-#define etINVALID 17 /* Any unrecognized conversion type */ ++#define etINVALID 17 /* Any unrecognized conversion type */ + + + /* +@@ -28214,9 +31726,9 @@ static const et_info fmtinfo[] = { + { 's', 0, 4, etSTRING, 0, 0 }, + { 'g', 0, 1, etGENERIC, 30, 0 }, + { 'z', 0, 4, etDYNSTRING, 0, 0 }, +- { 'q', 0, 4, etSQLESCAPE, 0, 0 }, +- { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, +- { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, ++ { 'q', 0, 4, etESCAPE_q, 0, 0 }, ++ { 'Q', 0, 4, etESCAPE_Q, 0, 0 }, ++ { 'w', 0, 4, etESCAPE_w, 0, 0 }, + { 'c', 0, 0, etCHARX, 0, 0 }, + { 'o', 8, 0, etRADIX, 0, 2 }, + { 'u', 10, 0, etDECIMAL, 0, 0 }, +@@ -28235,51 +31747,20 @@ static const et_info fmtinfo[] = { + + /* All the rest are undocumented and are for internal use only */ + { 'T', 0, 0, etTOKEN, 0, 0 }, +- { 'S', 0, 0, etSRCLIST, 0, 0 }, ++ { 'S', 0, 0, etSRCITEM, 0, 0 }, + { 'r', 10, 1, etORDINAL, 0, 0 }, + }; + +-/* Floating point constants used for rounding */ +-static const double arRound[] = { +- 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, +- 5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10, +-}; +- +-/* +-** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point +-** conversions will work. +-*/ +-#ifndef SQLITE_OMIT_FLOATING_POINT +-/* +-** "*val" is a double such that 0.1 <= *val < 10.0 +-** Return the ascii code for the leading digit of *val, then +-** multiply "*val" by 10.0 to renormalize. ++/* Notes: + ** +-** Example: +-** input: *val = 3.14159 +-** output: *val = 1.4159 function return = '3' +-** +-** The counter *cnt is incremented each time. After counter exceeds +-** 16 (the number of significant digits in a 64-bit float) '0' is +-** always returned. +-*/ +-static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ +- int digit; +- LONGDOUBLE_TYPE d; +- if( (*cnt)<=0 ) return '0'; +- (*cnt)--; +- digit = (int)*val; +- d = digit; +- digit += '0'; +- *val = (*val - d)*10.0; +- return (char)digit; +-} +-#endif /* SQLITE_OMIT_FLOATING_POINT */ ++** %S Takes a pointer to SrcItem. Shows name or database.name ++** %!S Like %S but prefer the zName over the zAlias ++*/ + + /* + ** Set the StrAccum object to an error mode. + */ +-static void setStrAccumError(StrAccum *p, u8 eError){ ++SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum *p, u8 eError){ + assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); + p->accError = eError; + if( p->mxAlloc ) sqlite3_str_reset(p); +@@ -28315,12 +31796,12 @@ static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ + char *z; + if( pAccum->accError ) return 0; + if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ +- setStrAccumError(pAccum, SQLITE_TOOBIG); ++ sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG); + return 0; + } + z = sqlite3DbMallocRaw(pAccum->db, n); + if( z==0 ){ +- setStrAccumError(pAccum, SQLITE_NOMEM); ++ sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM); + } + return z; + } +@@ -28367,18 +31848,15 @@ SQLITE_API void sqlite3_str_vappendf( + u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ + char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ + sqlite_uint64 longvalue; /* Value for integer types */ +- LONGDOUBLE_TYPE realvalue; /* Value for real types */ ++ double realvalue; /* Value for real types */ + const et_info *infop; /* Pointer to the appropriate info structure */ + char *zOut; /* Rendering buffer */ + int nOut; /* Size of the rendering buffer */ + char *zExtra = 0; /* Malloced memory used by some conversion */ +-#ifndef SQLITE_OMIT_FLOATING_POINT +- int exp, e2; /* exponent of real numbers */ +- int nsd; /* Number of significant digits returned */ +- double rounder; /* Used for rounding floating point values */ ++ int exp, e2; /* exponent of real numbers */ + etByte flag_dp; /* True if decimal point should be shown */ + etByte flag_rtz; /* True if trailing zeros should be removed */ +-#endif ++ + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ + char buf[etBUFSIZE]; /* Conversion buffer */ + +@@ -28567,11 +32045,10 @@ SQLITE_API void sqlite3_str_vappendf( + v = va_arg(ap,int); + } + if( v<0 ){ +- if( v==SMALLEST_INT64 ){ +- longvalue = ((u64)1)<<63; +- }else{ +- longvalue = -v; +- } ++ testcase( v==SMALLEST_INT64 ); ++ testcase( v==(-1) ); ++ longvalue = ~v; ++ longvalue++; + prefix = '-'; + }else{ + longvalue = v; +@@ -28654,74 +32131,69 @@ SQLITE_API void sqlite3_str_vappendf( + break; + case etFLOAT: + case etEXP: +- case etGENERIC: ++ case etGENERIC: { ++ FpDecode s; ++ int iRound; ++ int j; ++ + if( bArgList ){ + realvalue = getDoubleArg(pArgList); + }else{ + realvalue = va_arg(ap,double); + } +-#ifdef SQLITE_OMIT_FLOATING_POINT +- length = 0; +-#else + if( precision<0 ) precision = 6; /* Set default precision */ + #ifdef SQLITE_FP_PRECISION_LIMIT + if( precision>SQLITE_FP_PRECISION_LIMIT ){ + precision = SQLITE_FP_PRECISION_LIMIT; + } + #endif +- if( realvalue<0.0 ){ +- realvalue = -realvalue; +- prefix = '-'; +- }else{ +- prefix = flag_prefix; +- } +- if( xtype==etGENERIC && precision>0 ) precision--; +- testcase( precision>0xfff ); +- idx = precision & 0xfff; +- rounder = arRound[idx%10]; +- while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } + if( xtype==etFLOAT ){ +- double rx = (double)realvalue; +- sqlite3_uint64 u; +- int ex; +- memcpy(&u, &rx, sizeof(u)); +- ex = -1023 + (int)((u>>52)&0x7ff); +- if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; +- realvalue += rounder; +- } +- /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ +- exp = 0; +- if( sqlite3IsNaN((double)realvalue) ){ +- bufpt = "NaN"; +- length = 3; +- break; ++ iRound = -precision; ++ }else if( xtype==etGENERIC ){ ++ if( precision==0 ) precision = 1; ++ iRound = precision; ++ }else{ ++ iRound = precision+1; + } +- if( realvalue>0.0 ){ +- LONGDOUBLE_TYPE scale = 1.0; +- while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} +- while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; } +- while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } +- realvalue /= scale; +- while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } +- while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } +- if( exp>350 ){ ++ sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); ++ if( s.isSpecial ){ ++ if( s.isSpecial==2 ){ ++ bufpt = flag_zeropad ? "null" : "NaN"; ++ length = sqlite3Strlen30(bufpt); ++ break; ++ }else if( flag_zeropad ){ ++ s.z[0] = '9'; ++ s.iDP = 1000; ++ s.n = 1; ++ }else{ ++ memcpy(buf, "-Inf", 5); + bufpt = buf; +- buf[0] = prefix; +- memcpy(buf+(prefix!=0),"Inf",4); +- length = 3+(prefix!=0); ++ if( s.sign=='-' ){ ++ /* no-op */ ++ }else if( flag_prefix ){ ++ buf[0] = flag_prefix; ++ }else{ ++ bufpt++; ++ } ++ length = sqlite3Strlen30(bufpt); + break; + } + } +- bufpt = buf; ++ if( s.sign=='-' ){ ++ prefix = '-'; ++ }else{ ++ prefix = flag_prefix; ++ } ++ ++ exp = s.iDP-1; ++ + /* + ** If the field type is etGENERIC, then convert to either etEXP + ** or etFLOAT, as appropriate. + */ +- if( xtype!=etFLOAT ){ +- realvalue += rounder; +- if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } +- } + if( xtype==etGENERIC ){ ++ assert( precision>0 ); ++ precision--; + flag_rtz = !flag_alternateform; + if( exp<-4 || exp>precision ){ + xtype = etEXP; +@@ -28735,29 +32207,32 @@ SQLITE_API void sqlite3_str_vappendf( + if( xtype==etEXP ){ + e2 = 0; + }else{ +- e2 = exp; ++ e2 = s.iDP - 1; + } ++ bufpt = buf; + { + i64 szBufNeeded; /* Size of a temporary buffer needed */ + szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; ++ if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; + if( szBufNeeded > etBUFSIZE ){ + bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); + if( bufpt==0 ) return; + } + } + zOut = bufpt; +- nsd = 16 + flag_altform2*10; + flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; + /* The sign in front of the number */ + if( prefix ){ + *(bufpt++) = prefix; + } + /* Digits prior to the decimal point */ ++ j = 0; + if( e2<0 ){ + *(bufpt++) = '0'; + }else{ + for(; e2>=0; e2--){ +- *(bufpt++) = et_getdigit(&realvalue,&nsd); ++ *(bufpt++) = j1 ) *(bufpt++) = ','; + } + } + /* The decimal point */ +@@ -28766,13 +32241,12 @@ SQLITE_API void sqlite3_str_vappendf( + } + /* "0" digits after the decimal point but before the first + ** significant digit of the number */ +- for(e2++; e2<0; precision--, e2++){ +- assert( precision>0 ); ++ for(e2++; e2<0 && precision>0; precision--, e2++){ + *(bufpt++) = '0'; + } + /* Significant digits after the decimal point */ + while( (precision--)>0 ){ +- *(bufpt++) = et_getdigit(&realvalue,&nsd); ++ *(bufpt++) = jcharset]; + if( exp<0 ){ + *(bufpt++) = '-'; exp = -exp; +@@ -28821,8 +32296,8 @@ SQLITE_API void sqlite3_str_vappendf( + while( nPad-- ) bufpt[i++] = '0'; + length = width; + } +-#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ + break; ++ } + case etSIZE: + if( !bArgList ){ + *(va_arg(ap,int*)) = pAccum->nChar; +@@ -28850,34 +32325,29 @@ SQLITE_API void sqlite3_str_vappendf( + } + }else{ + unsigned int ch = va_arg(ap,unsigned int); +- if( ch<0x00080 ){ +- buf[0] = ch & 0xff; +- length = 1; +- }else if( ch<0x00800 ){ +- buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); +- buf[1] = 0x80 + (u8)(ch & 0x3f); +- length = 2; +- }else if( ch<0x10000 ){ +- buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); +- buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); +- buf[2] = 0x80 + (u8)(ch & 0x3f); +- length = 3; +- }else{ +- buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); +- buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); +- buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); +- buf[3] = 0x80 + (u8)(ch & 0x3f); +- length = 4; +- } ++ length = sqlite3AppendOneUtf8Character(buf, ch); + } + if( precision>1 ){ ++ i64 nPrior = 1; + width -= precision-1; + if( width>1 && !flag_leftjustify ){ + sqlite3_str_appendchar(pAccum, width-1, ' '); + width = 0; + } +- while( precision-- > 1 ){ +- sqlite3_str_append(pAccum, buf, length); ++ sqlite3_str_append(pAccum, buf, length); ++ precision--; ++ while( precision > 1 ){ ++ i64 nCopyBytes; ++ if( nPrior > precision-1 ) nPrior = precision - 1; ++ nCopyBytes = length*nPrior; ++ if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){ ++ sqlite3StrAccumEnlarge(pAccum, nCopyBytes); ++ } ++ if( pAccum->accError ) break; ++ sqlite3_str_append(pAccum, ++ &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes); ++ precision -= nPrior; ++ nPrior *= 2; + } + } + bufpt = buf; +@@ -28935,22 +32405,31 @@ SQLITE_API void sqlite3_str_vappendf( + while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; + } + break; +- case etSQLESCAPE: /* %q: Escape ' characters */ +- case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ +- case etSQLESCAPE3: { /* %w: Escape " characters */ +- int i, j, k, n, isnull; +- int needQuote; ++ case etESCAPE_q: /* %q: Escape ' characters */ ++ case etESCAPE_Q: /* %Q: Escape ' and enclose in '...' */ ++ case etESCAPE_w: { /* %w: Escape " characters */ ++ i64 i, j, k, n; ++ int needQuote = 0; + char ch; +- char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ + char *escarg; ++ char q; + + if( bArgList ){ + escarg = getTextArg(pArgList); + }else{ + escarg = va_arg(ap,char*); + } +- isnull = escarg==0; +- if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); ++ if( escarg==0 ){ ++ escarg = (xtype==etESCAPE_Q ? "NULL" : "(NULL)"); ++ }else if( xtype==etESCAPE_Q ){ ++ needQuote = 1; ++ } ++ if( xtype==etESCAPE_w ){ ++ q = '"'; ++ flag_alternateform = 0; ++ }else{ ++ q = '\''; ++ } + /* For %q, %Q, and %w, the precision is the number of bytes (or + ** characters if the ! flags is present) to use from the input. + ** Because of the extra quoting characters inserted, the number +@@ -28963,7 +32442,30 @@ SQLITE_API void sqlite3_str_vappendf( + while( (escarg[i+1]&0xc0)==0x80 ){ i++; } + } + } +- needQuote = !isnull && xtype==etSQLESCAPE2; ++ if( flag_alternateform ){ ++ /* For %#q, do unistr()-style backslash escapes for ++ ** all control characters, and for backslash itself. ++ ** For %#Q, do the same but only if there is at least ++ ** one control character. */ ++ u32 nBack = 0; ++ u32 nCtrl = 0; ++ for(k=0; ketBUFSIZE ){ + bufpt = zExtra = printfTempBuf(pAccum, n); +@@ -28972,43 +32474,97 @@ SQLITE_API void sqlite3_str_vappendf( + bufpt = buf; + } + j = 0; +- if( needQuote ) bufpt[j++] = q; ++ if( needQuote ){ ++ if( needQuote==2 ){ ++ memcpy(&bufpt[j], "unistr('", 8); ++ j += 8; ++ }else{ ++ bufpt[j++] = '\''; ++ } ++ } + k = i; +- for(i=0; i=0x10 ? '1' : '0'; ++ bufpt[j++] = "0123456789abcdef"[ch&0xf]; ++ } ++ } ++ }else{ ++ for(i=0; iprintfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; +- pToken = va_arg(ap, Token*); +- assert( bArgList==0 ); +- if( pToken && pToken->n ){ +- sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); ++ if( flag_alternateform ){ ++ /* %#T means an Expr pointer that uses Expr.u.zToken */ ++ Expr *pExpr = va_arg(ap,Expr*); ++ if( ALWAYS(pExpr) && ALWAYS(!ExprHasProperty(pExpr,EP_IntValue)) ){ ++ sqlite3_str_appendall(pAccum, (const char*)pExpr->u.zToken); ++ sqlite3RecordErrorOffsetOfExpr(pAccum->db, pExpr); ++ } ++ }else{ ++ /* %T means a Token pointer */ ++ Token *pToken = va_arg(ap, Token*); ++ assert( bArgList==0 ); ++ if( pToken && pToken->n ){ ++ sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); ++ sqlite3RecordErrorByteOffset(pAccum->db, pToken->z); ++ } + } + length = width = 0; + break; + } +- case etSRCLIST: { +- SrcList *pSrc; +- int k; +- struct SrcList_item *pItem; ++ case etSRCITEM: { ++ SrcItem *pItem; + if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; +- pSrc = va_arg(ap, SrcList*); +- k = va_arg(ap, int); +- pItem = &pSrc->a[k]; ++ pItem = va_arg(ap, SrcItem*); + assert( bArgList==0 ); +- assert( k>=0 && knSrc ); +- if( pItem->zDatabase ){ +- sqlite3_str_appendall(pAccum, pItem->zDatabase); +- sqlite3_str_append(pAccum, ".", 1); ++ if( pItem->zAlias && !flag_altform2 ){ ++ sqlite3_str_appendall(pAccum, pItem->zAlias); ++ }else if( pItem->zName ){ ++ if( pItem->fg.fixedSchema==0 ++ && pItem->fg.isSubquery==0 ++ && pItem->u4.zDatabase!=0 ++ ){ ++ sqlite3_str_appendall(pAccum, pItem->u4.zDatabase); ++ sqlite3_str_append(pAccum, ".", 1); ++ } ++ sqlite3_str_appendall(pAccum, pItem->zName); ++ }else if( pItem->zAlias ){ ++ sqlite3_str_appendall(pAccum, pItem->zAlias); ++ }else if( ALWAYS(pItem->fg.isSubquery) ){/* Because of tag-20240424-1 */ ++ Select *pSel = pItem->u4.pSubq->pSelect; ++ assert( pSel!=0 ); ++ if( pSel->selFlags & SF_NestedFrom ){ ++ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); ++ }else if( pSel->selFlags & SF_MultiValue ){ ++ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); ++ sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", ++ pItem->u1.nRow); ++ }else{ ++ sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); ++ } + } +- sqlite3_str_appendall(pAccum, pItem->zName); + length = width = 0; + break; + } +@@ -29041,6 +32597,45 @@ SQLITE_API void sqlite3_str_vappendf( + }/* End for loop over the format string */ + } /* End of function */ + ++ ++/* ++** The z string points to the first character of a token that is ++** associated with an error. If db does not already have an error ++** byte offset recorded, try to compute the error byte offset for ++** z and set the error byte offset in db. ++*/ ++SQLITE_PRIVATE void sqlite3RecordErrorByteOffset(sqlite3 *db, const char *z){ ++ const Parse *pParse; ++ const char *zText; ++ const char *zEnd; ++ assert( z!=0 ); ++ if( NEVER(db==0) ) return; ++ if( db->errByteOffset!=(-2) ) return; ++ pParse = db->pParse; ++ if( NEVER(pParse==0) ) return; ++ zText =pParse->zTail; ++ if( NEVER(zText==0) ) return; ++ zEnd = &zText[strlen(zText)]; ++ if( SQLITE_WITHIN(z,zText,zEnd) ){ ++ db->errByteOffset = (int)(z-zText); ++ } ++} ++ ++/* ++** If pExpr has a byte offset for the start of a token, record that as ++** as the error offset. ++*/ ++SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){ ++ while( pExpr ++ && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) ++ ){ ++ pExpr = pExpr->pLeft; ++ } ++ if( pExpr==0 ) return; ++ if( ExprHasProperty(pExpr, EP_FromDDL) ) return; ++ db->errByteOffset = pExpr->w.iOfst; ++} ++ + /* + ** Enlarge the memory allocation on a StrAccum object so that it is + ** able to accept at least N more bytes of text. +@@ -29048,21 +32643,20 @@ SQLITE_API void sqlite3_str_vappendf( + ** Return the number of bytes of text that StrAccum is able to accept + ** after the attempted enlargement. The value returned might be zero. + */ +-static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ ++SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, i64 N){ + char *zNew; +- assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ ++ assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */ + if( p->accError ){ + testcase(p->accError==SQLITE_TOOBIG); + testcase(p->accError==SQLITE_NOMEM); + return 0; + } + if( p->mxAlloc==0 ){ +- setStrAccumError(p, SQLITE_TOOBIG); ++ sqlite3StrAccumSetError(p, SQLITE_TOOBIG); + return p->nAlloc - p->nChar - 1; + }else{ + char *zOld = isMalloced(p) ? p->zText : 0; +- i64 szNew = p->nChar; +- szNew += N + 1; ++ i64 szNew = p->nChar + N + 1; + if( szNew+p->nChar<=p->mxAlloc ){ + /* Force exponential buffer size growth as long as it does not overflow, + ** to avoid having to call this routine too often */ +@@ -29070,7 +32664,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ + } + if( szNew > p->mxAlloc ){ + sqlite3_str_reset(p); +- setStrAccumError(p, SQLITE_TOOBIG); ++ sqlite3StrAccumSetError(p, SQLITE_TOOBIG); + return 0; + }else{ + p->nAlloc = (int)szNew; +@@ -29088,11 +32682,12 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ + p->printfFlags |= SQLITE_PRINTF_MALLOCED; + }else{ + sqlite3_str_reset(p); +- setStrAccumError(p, SQLITE_NOMEM); ++ sqlite3StrAccumSetError(p, SQLITE_NOMEM); + return 0; + } + } +- return N; ++ assert( N>=0 && N<=0x7fffffff ); ++ return (int)N; + } + + /* +@@ -29156,12 +32751,12 @@ SQLITE_API void sqlite3_str_appendall(sqlite3_str *p, const char *z){ + static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ + char *zText; + assert( p->mxAlloc>0 && !isMalloced(p) ); +- zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); ++ zText = sqlite3DbMallocRaw(p->db, 1+(u64)p->nChar ); + if( zText ){ + memcpy(zText, p->zText, p->nChar+1); + p->printfFlags |= SQLITE_PRINTF_MALLOCED; + }else{ +- setStrAccumError(p, SQLITE_NOMEM); ++ sqlite3StrAccumSetError(p, SQLITE_NOMEM); + } + p->zText = zText; + return zText; +@@ -29176,6 +32771,22 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ + return p->zText; + } + ++/* ++** Use the content of the StrAccum passed as the second argument ++** as the result of an SQL function. ++*/ ++SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context *pCtx, StrAccum *p){ ++ if( p->accError ){ ++ sqlite3_result_error_code(pCtx, p->accError); ++ sqlite3_str_reset(p); ++ }else if( isMalloced(p) ){ ++ sqlite3_result_text(pCtx, p->zText, p->nChar, SQLITE_DYNAMIC); ++ }else{ ++ sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); ++ sqlite3_str_reset(p); ++ } ++} ++ + /* + ** This singleton is an sqlite3_str object that is returned if + ** sqlite3_malloc() fails to provide space for a real one. This +@@ -29367,14 +32978,33 @@ SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_li + return zBuf; + } + SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ +- char *z; ++ StrAccum acc; + va_list ap; ++ if( n<=0 ) return zBuf; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( zBuf==0 || zFormat==0 ) { ++ (void)SQLITE_MISUSE_BKPT; ++ if( zBuf ) zBuf[0] = 0; ++ return zBuf; ++ } ++#endif ++ sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); + va_start(ap,zFormat); +- z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); ++ sqlite3_str_vappendf(&acc, zFormat, ap); + va_end(ap); +- return z; ++ zBuf[acc.nChar] = 0; ++ return zBuf; + } + ++/* Maximum size of an sqlite3_log() message. */ ++#if defined(SQLITE_MAX_LOG_MESSAGE) ++ /* Leave the definition as supplied */ ++#elif SQLITE_PRINT_BUF_SIZE*10>10000 ++# define SQLITE_MAX_LOG_MESSAGE 10000 ++#else ++# define SQLITE_MAX_LOG_MESSAGE (SQLITE_PRINT_BUF_SIZE*10) ++#endif ++ + /* + ** This is the routine that actually formats the sqlite3_log() message. + ** We house it in a separate routine from sqlite3_log() to avoid using +@@ -29391,7 +33021,7 @@ SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ + */ + static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ + StrAccum acc; /* String accumulator */ +- char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ ++ char zMsg[SQLITE_MAX_LOG_MESSAGE]; /* Complete log message */ + + sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); + sqlite3_str_vappendf(&acc, zFormat, ap); +@@ -29450,6 +33080,75 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ + va_end(ap); + } + ++ ++/***************************************************************************** ++** Reference counted string/blob storage ++*****************************************************************************/ ++ ++/* ++** Increase the reference count of the string by one. ++** ++** The input parameter is returned. ++*/ ++SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){ ++ RCStr *p = (RCStr*)z; ++ assert( p!=0 ); ++ p--; ++ p->nRCRef++; ++ return z; ++} ++ ++/* ++** Decrease the reference count by one. Free the string when the ++** reference count reaches zero. ++*/ ++SQLITE_PRIVATE void sqlite3RCStrUnref(void *z){ ++ RCStr *p = (RCStr*)z; ++ assert( p!=0 ); ++ p--; ++ assert( p->nRCRef>0 ); ++ if( p->nRCRef>=2 ){ ++ p->nRCRef--; ++ }else{ ++ sqlite3_free(p); ++ } ++} ++ ++/* ++** Create a new string that is capable of holding N bytes of text, not counting ++** the zero byte at the end. The string is uninitialized. ++** ++** The reference count is initially 1. Call sqlite3RCStrUnref() to free the ++** newly allocated string. ++** ++** This routine returns 0 on an OOM. ++*/ ++SQLITE_PRIVATE char *sqlite3RCStrNew(u64 N){ ++ RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 ); ++ if( p==0 ) return 0; ++ p->nRCRef = 1; ++ return (char*)&p[1]; ++} ++ ++/* ++** Change the size of the string so that it is able to hold N bytes. ++** The string might be reallocated, so return the new allocation. ++*/ ++SQLITE_PRIVATE char *sqlite3RCStrResize(char *z, u64 N){ ++ RCStr *p = (RCStr*)z; ++ RCStr *pNew; ++ assert( p!=0 ); ++ p--; ++ assert( p->nRCRef==1 ); ++ pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1); ++ if( pNew==0 ){ ++ sqlite3_free(p); ++ return 0; ++ }else{ ++ return (char*)&pNew[1]; ++ } ++} ++ + /************** End of printf.c **********************************************/ + /************** Begin file treeview.c ****************************************/ + /* +@@ -29478,40 +33177,44 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ + ** Add a new subitem to the tree. The moreToFollow flag indicates that this + ** is not the last item in the tree. + */ +-static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){ ++static void sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){ ++ TreeView *p = *pp; + if( p==0 ){ +- p = sqlite3_malloc64( sizeof(*p) ); +- if( p==0 ) return 0; ++ *pp = p = sqlite3_malloc64( sizeof(*p) ); ++ if( p==0 ) return; + memset(p, 0, sizeof(*p)); + }else{ + p->iLevel++; + } + assert( moreToFollow==0 || moreToFollow==1 ); +- if( p->iLevelbLine) ) p->bLine[p->iLevel] = moreToFollow; +- return p; ++ if( p->iLevel<(int)sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow; + } + + /* + ** Finished with one layer of the tree + */ +-static void sqlite3TreeViewPop(TreeView *p){ ++static void sqlite3TreeViewPop(TreeView **pp){ ++ TreeView *p = *pp; + if( p==0 ) return; + p->iLevel--; +- if( p->iLevel<0 ) sqlite3_free(p); ++ if( p->iLevel<0 ){ ++ sqlite3_free(p); ++ *pp = 0; ++ } + } + + /* + ** Generate a single line of output for the tree, with a prefix that contains + ** all the appropriate tree lines + */ +-static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ ++SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ + va_list ap; + int i; + StrAccum acc; +- char zBuf[500]; ++ char zBuf[1000]; + sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); + if( p ){ +- for(i=0; iiLevel && ibLine)-1; i++){ ++ for(i=0; iiLevel && i<(int)sizeof(p->bLine)-1; i++){ + sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4); + } + sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); +@@ -29532,10 +33235,57 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ + ** Shorthand for starting a new tree item that consists of a single label + */ + static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){ +- p = sqlite3TreeViewPush(p, moreFollows); ++ sqlite3TreeViewPush(&p, moreFollows); + sqlite3TreeViewLine(p, "%s", zLabel); + } + ++/* ++** Show a list of Column objects in tree format. ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewColumnList( ++ TreeView *pView, ++ const Column *aCol, ++ int nCol, ++ u8 moreToFollow ++){ ++ int i; ++ sqlite3TreeViewPush(&pView, moreToFollow); ++ sqlite3TreeViewLine(pView, "COLUMNS"); ++ for(i=0; inCte>0 ){ +- pView = sqlite3TreeViewPush(pView, 1); ++ sqlite3TreeViewPush(&pView, moreToFollow); + for(i=0; inCte; i++){ + StrAccum x; + char zLine[1000]; +@@ -29565,13 +33315,20 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m + } + sqlite3_str_appendf(&x, ")"); + } +- sqlite3_str_appendf(&x, " AS"); ++ if( pCte->eM10d!=M10d_Any ){ ++ sqlite3_str_appendf(&x, " %sMATERIALIZED", ++ pCte->eM10d==M10d_No ? "NOT " : ""); ++ } ++ if( pCte->pUse ){ ++ sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse, ++ pCte->pUse->nUse); ++ } + sqlite3StrAccumFinish(&x); + sqlite3TreeViewItem(pView, zLine, inCte-1); + sqlite3TreeViewSelect(pView, pCte->pSelect, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + } + +@@ -29580,39 +33337,77 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m + */ + SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ + int i; ++ if( pSrc==0 ) return; + for(i=0; inSrc; i++){ +- const struct SrcList_item *pItem = &pSrc->a[i]; ++ const SrcItem *pItem = &pSrc->a[i]; + StrAccum x; +- char zLine[100]; ++ int n = 0; ++ char zLine[1000]; + sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); +- sqlite3_str_appendf(&x, "{%d:*}", pItem->iCursor); +- if( pItem->zDatabase ){ +- sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName); +- }else if( pItem->zName ){ +- sqlite3_str_appendf(&x, " %s", pItem->zName); +- } +- if( pItem->pTab ){ +- sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", +- pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); +- } +- if( pItem->zAlias ){ +- sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); +- } +- if( pItem->fg.jointype & JT_LEFT ){ ++ x.printfFlags |= SQLITE_PRINTF_INTERNAL; ++ sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); ++ if( pItem->pSTab ){ ++ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", ++ pItem->pSTab->zName, pItem->pSTab->nCol, pItem->pSTab, ++ pItem->colUsed, ++ pItem->fg.rowidUsed ? "+rowid" : ""); ++ } ++ if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ ++ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); ++ }else if( pItem->fg.jointype & JT_LEFT ){ + sqlite3_str_appendf(&x, " LEFT-JOIN"); ++ }else if( pItem->fg.jointype & JT_RIGHT ){ ++ sqlite3_str_appendf(&x, " RIGHT-JOIN"); ++ }else if( pItem->fg.jointype & JT_CROSS ){ ++ sqlite3_str_appendf(&x, " CROSS-JOIN"); ++ } ++ if( pItem->fg.jointype & JT_LTORJ ){ ++ sqlite3_str_appendf(&x, " LTORJ"); + } + if( pItem->fg.fromDDL ){ + sqlite3_str_appendf(&x, " DDL"); + } ++ if( pItem->fg.isCte ){ ++ static const char *aMat[] = {",MAT", "", ",NO-MAT"}; ++ sqlite3_str_appendf(&x, " CteUse=%d%s", ++ pItem->u2.pCteUse->nUse, ++ aMat[pItem->u2.pCteUse->eM10d]); ++ } ++ if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ ++ sqlite3_str_appendf(&x, " isOn"); ++ } ++ if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc"); ++ if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated"); ++ if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized"); ++ if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); ++ if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte"); ++ if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom"); ++ if( pItem->fg.fixedSchema ) sqlite3_str_appendf(&x, " fixedSchema"); ++ if( pItem->fg.hadSchema ) sqlite3_str_appendf(&x, " hadSchema"); ++ if( pItem->fg.isSubquery ) sqlite3_str_appendf(&x, " isSubquery"); ++ + sqlite3StrAccumFinish(&x); + sqlite3TreeViewItem(pView, zLine, inSrc-1); +- if( pItem->pSelect ){ +- sqlite3TreeViewSelect(pView, pItem->pSelect, 0); ++ n = 0; ++ if( pItem->fg.isSubquery ) n++; ++ if( pItem->fg.isTabFunc ) n++; ++ if( pItem->fg.isUsing ) n++; ++ if( pItem->fg.isUsing ){ ++ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); ++ } ++ if( pItem->fg.isSubquery ){ ++ assert( n==1 ); ++ if( pItem->pSTab ){ ++ Table *pTab = pItem->pSTab; ++ sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); ++ } ++ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); ++ sqlite3TreeViewSelect(pView, pItem->u4.pSubq->pSelect, 0); + } + if( pItem->fg.isTabFunc ){ + sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); + } +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + } + +@@ -29626,11 +33421,11 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m + sqlite3TreeViewLine(pView, "nil-SELECT"); + return; + } +- pView = sqlite3TreeViewPush(pView, moreToFollow); ++ sqlite3TreeViewPush(&pView, moreToFollow); + if( p->pWith ){ + sqlite3TreeViewWith(pView, p->pWith, 1); + cnt = 1; +- sqlite3TreeViewPush(pView, 1); ++ sqlite3TreeViewPush(&pView, 1); + } + do{ + if( p->selFlags & SF_WhereBegin ){ +@@ -29644,12 +33439,12 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m + (int)p->nSelectRow + ); + } +- if( cnt++ ) sqlite3TreeViewPop(pView); ++ if( cnt++ ) sqlite3TreeViewPop(&pView); + if( p->pPrior ){ + n = 1000; + }else{ + n = 0; +- if( p->pSrc && p->pSrc->nSrc ) n++; ++ if( p->pSrc && p->pSrc->nSrc && p->pSrc->nAlloc ) n++; + if( p->pWhere ) n++; + if( p->pGroupBy ) n++; + if( p->pHaving ) n++; +@@ -29667,24 +33462,24 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m + #ifndef SQLITE_OMIT_WINDOWFUNC + if( p->pWin ){ + Window *pX; +- pView = sqlite3TreeViewPush(pView, (n--)>0); ++ sqlite3TreeViewPush(&pView, (n--)>0); + sqlite3TreeViewLine(pView, "window-functions"); + for(pX=p->pWin; pX; pX=pX->pNextWin){ + sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0); + } +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + #endif +- if( p->pSrc && p->pSrc->nSrc ){ +- pView = sqlite3TreeViewPush(pView, (n--)>0); ++ if( p->pSrc && p->pSrc->nSrc && p->pSrc->nAlloc ){ ++ sqlite3TreeViewPush(&pView, (n--)>0); + sqlite3TreeViewLine(pView, "FROM"); + sqlite3TreeViewSrcList(pView, p->pSrc); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + if( p->pWhere ){ + sqlite3TreeViewItem(pView, "WHERE", (n--)>0); + sqlite3TreeViewExpr(pView, p->pWhere, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + if( p->pGroupBy ){ + sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY"); +@@ -29692,7 +33487,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m + if( p->pHaving ){ + sqlite3TreeViewItem(pView, "HAVING", (n--)>0); + sqlite3TreeViewExpr(pView, p->pHaving, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + #ifndef SQLITE_OMIT_WINDOWFUNC + if( p->pWinDefn ){ +@@ -29701,7 +33496,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m + for(pX=p->pWinDefn; pX; pX=pX->pNextWin){ + sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0); + } +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + #endif + if( p->pOrderBy ){ +@@ -29711,11 +33506,11 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m + sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); + sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); + if( p->pLimit->pRight ){ +- sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); ++ sqlite3TreeViewItem(pView, "OFFSET", 0); + sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + if( p->pPrior ){ + const char *zOp = "UNION"; +@@ -29728,7 +33523,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m + } + p = p->pPrior; + }while( p!=0 ); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + + #ifndef SQLITE_OMIT_WINDOWFUNC +@@ -29744,24 +33539,24 @@ SQLITE_PRIVATE void sqlite3TreeViewBound( + switch( eBound ){ + case TK_UNBOUNDED: { + sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + break; + } + case TK_CURRENT: { + sqlite3TreeViewItem(pView, "CURRENT", moreToFollow); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + break; + } + case TK_PRECEDING: { + sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow); + sqlite3TreeViewExpr(pView, pExpr, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + break; + } + case TK_FOLLOWING: { + sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow); + sqlite3TreeViewExpr(pView, pExpr, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + break; + } + } +@@ -29774,12 +33569,14 @@ SQLITE_PRIVATE void sqlite3TreeViewBound( + */ + SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ + int nElement = 0; ++ if( pWin==0 ) return; + if( pWin->pFilter ){ + sqlite3TreeViewItem(pView, "FILTER", 1); + sqlite3TreeViewExpr(pView, pWin->pFilter, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); ++ if( pWin->eFrmType==TK_FILTER ) return; + } +- pView = sqlite3TreeViewPush(pView, more); ++ sqlite3TreeViewPush(&pView, more); + if( pWin->zName ){ + sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); + }else{ +@@ -29787,12 +33584,12 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u + } + if( pWin->zBase ) nElement++; + if( pWin->pOrderBy ) nElement++; +- if( pWin->eFrmType ) nElement++; ++ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++; + if( pWin->eExclude ) nElement++; + if( pWin->zBase ){ +- sqlite3TreeViewPush(pView, (--nElement)>0); ++ sqlite3TreeViewPush(&pView, (--nElement)>0); + sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + if( pWin->pPartition ){ + sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); +@@ -29800,7 +33597,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u + if( pWin->pOrderBy ){ + sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); + } +- if( pWin->eFrmType ){ ++ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){ + char zBuf[30]; + const char *zFrmType = "ROWS"; + if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; +@@ -29810,7 +33607,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u + sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); + sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); + sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + if( pWin->eExclude ){ + char zBuf[30]; +@@ -29825,11 +33622,11 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u + zExclude = zBuf; + break; + } +- sqlite3TreeViewPush(pView, 0); ++ sqlite3TreeViewPush(&pView, 0); + sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + #endif /* SQLITE_OMIT_WINDOWFUNC */ + +@@ -29838,11 +33635,12 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u + ** Generate a human-readable explanation for a Window Function object + */ + SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){ +- pView = sqlite3TreeViewPush(pView, more); ++ if( pWin==0 ) return; ++ sqlite3TreeViewPush(&pView, more); + sqlite3TreeViewLine(pView, "WINFUNC %s(%d)", +- pWin->pFunc->zName, pWin->pFunc->nArg); ++ pWin->pWFunc->zName, pWin->pWFunc->nArg); + sqlite3TreeViewWindow(pView, pWin, 0); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + #endif /* SQLITE_OMIT_WINDOWFUNC */ + +@@ -29853,19 +33651,22 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + const char *zBinOp = 0; /* Binary operator */ + const char *zUniOp = 0; /* Unary operator */ + char zFlgs[200]; +- pView = sqlite3TreeViewPush(pView, moreToFollow); ++ sqlite3TreeViewPush(&pView, moreToFollow); + if( pExpr==0 ){ + sqlite3TreeViewLine(pView, "nil"); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + return; + } +- if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ ++ if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){ + StrAccum x; + sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); + sqlite3_str_appendf(&x, " fg.af=%x.%c", + pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n'); +- if( ExprHasProperty(pExpr, EP_FromJoin) ){ +- sqlite3_str_appendf(&x, " iRJT=%d", pExpr->iRightJoinTable); ++ if( ExprHasProperty(pExpr, EP_OuterON) ){ ++ sqlite3_str_appendf(&x, " outer.iJoin=%d", pExpr->w.iJoin); ++ } ++ if( ExprHasProperty(pExpr, EP_InnerON) ){ ++ sqlite3_str_appendf(&x, " inner.iJoin=%d", pExpr->w.iJoin); + } + if( ExprHasProperty(pExpr, EP_FromDDL) ){ + sqlite3_str_appendf(&x, " DDL"); +@@ -29873,6 +33674,9 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ + sqlite3_str_appendf(&x, " IMMUTABLE"); + } ++ if( pExpr->pAggInfo!=0 ){ ++ sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg); ++ } + sqlite3StrAccumFinish(&x); + }else{ + zFlgs[0] = 0; +@@ -29895,6 +33699,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + sqlite3TreeViewLine(pView, "COLUMN(%d)%s%s", + pExpr->iColumn, zFlgs, zOp2); + }else{ ++ assert( ExprUseYTab(pExpr) ); + sqlite3TreeViewLine(pView, "{%d:%d} pTab=%p%s", + pExpr->iTable, pExpr->iColumn, + pExpr->y.pTab, zFlgs); +@@ -29914,11 +33719,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + } + #ifndef SQLITE_OMIT_FLOATING_POINT + case TK_FLOAT: { ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); + break; + } + #endif + case TK_STRING: { ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken); + break; + } +@@ -29927,17 +33734,19 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + break; + } + case TK_TRUEFALSE: { +- sqlite3TreeViewLine(pView, +- sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE"); ++ sqlite3TreeViewLine(pView,"%s%s", ++ sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE", zFlgs); + break; + } + #ifndef SQLITE_OMIT_BLOB_LITERAL + case TK_BLOB: { ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); + break; + } + #endif + case TK_VARIABLE: { ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)", + pExpr->u.zToken, pExpr->iColumn); + break; +@@ -29947,12 +33756,14 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + break; + } + case TK_ID: { ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); + break; + } + #ifndef SQLITE_OMIT_CAST + case TK_CAST: { + /* Expressions of the form: CAST(pLeft AS token) */ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken); + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; +@@ -29995,13 +33806,15 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + }; + assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); + assert( pExpr->pRight ); +- assert( sqlite3ExprSkipCollate(pExpr->pRight)->op==TK_TRUEFALSE ); ++ assert( sqlite3ExprSkipCollateAndLikely(pExpr->pRight)->op ++ == TK_TRUEFALSE ); + x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight); + zUniOp = azOp[x]; + break; + } + + case TK_SPAN: { ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; +@@ -30013,6 +33826,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE + ** operators that appear in the original SQL always have the + ** EP_Collate bit set and appear in treeview output as just "COLLATE" */ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s", + !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "", + pExpr->u.zToken, zFlgs); +@@ -30028,13 +33842,15 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + pFarg = 0; + pWin = 0; + }else{ ++ assert( ExprUseXList(pExpr) ); + pFarg = pExpr->x.pList; + #ifndef SQLITE_OMIT_WINDOWFUNC +- pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0; ++ pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0; + #else + pWin = 0; + #endif + } ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + if( pExpr->op==TK_AGG_FUNCTION ){ + sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s agg=%d[%d]/%p", + pExpr->op2, pExpr->u.zToken, zFlgs, +@@ -30055,7 +33871,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs); + } + if( pFarg ){ +- sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0); ++ sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0); ++ if( pExpr->pLeft ){ ++ Expr *pOB = pExpr->pLeft; ++ assert( pOB->op==TK_ORDER ); ++ assert( ExprUseXList(pOB) ); ++ sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY"); ++ } + } + #ifndef SQLITE_OMIT_WINDOWFUNC + if( pWin ){ +@@ -30064,21 +33886,37 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + #endif + break; + } ++ case TK_ORDER: { ++ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY"); ++ break; ++ } + #ifndef SQLITE_OMIT_SUBQUERY + case TK_EXISTS: { ++ assert( ExprUseXSelect(pExpr) ); + sqlite3TreeViewLine(pView, "EXISTS-expr flags=0x%x", pExpr->flags); + sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); + break; + } + case TK_SELECT: { ++ assert( ExprUseXSelect(pExpr) ); + sqlite3TreeViewLine(pView, "subquery-expr flags=0x%x", pExpr->flags); + sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); + break; + } + case TK_IN: { +- sqlite3TreeViewLine(pView, "IN flags=0x%x", pExpr->flags); ++ sqlite3_str *pStr = sqlite3_str_new(0); ++ char *z; ++ sqlite3_str_appendf(pStr, "IN flags=0x%x", pExpr->flags); ++ if( pExpr->iTable ) sqlite3_str_appendf(pStr, " iTable=%d",pExpr->iTable); ++ if( ExprHasProperty(pExpr, EP_Subrtn) ){ ++ sqlite3_str_appendf(pStr, " subrtn(%d,%d)", ++ pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); ++ } ++ z = sqlite3_str_finish(pStr); ++ sqlite3TreeViewLine(pView, z); ++ sqlite3_free(z); + sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); + }else{ + sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); +@@ -30099,10 +33937,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + ** Z is stored in pExpr->pList->a[1].pExpr. + */ + case TK_BETWEEN: { +- Expr *pX = pExpr->pLeft; +- Expr *pY = pExpr->x.pList->a[0].pExpr; +- Expr *pZ = pExpr->x.pList->a[1].pExpr; +- sqlite3TreeViewLine(pView, "BETWEEN"); ++ const Expr *pX, *pY, *pZ; ++ pX = pExpr->pLeft; ++ assert( ExprUseXList(pExpr) ); ++ assert( pExpr->x.pList->nExpr==2 ); ++ pY = pExpr->x.pList->a[0].pExpr; ++ pZ = pExpr->x.pList->a[1].pExpr; ++ sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs); + sqlite3TreeViewExpr(pView, pX, 1); + sqlite3TreeViewExpr(pView, pY, 1); + sqlite3TreeViewExpr(pView, pZ, 0); +@@ -30123,6 +33964,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + case TK_CASE: { + sqlite3TreeViewLine(pView, "CASE"); + sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); ++ assert( ExprUseXList(pExpr) ); + sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); + break; + } +@@ -30135,7 +33977,9 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + case OE_Fail: zType = "fail"; break; + case OE_Ignore: zType = "ignore"; break; + } +- sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ sqlite3TreeViewLine(pView, "RAISE %s", zType); ++ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; + } + #endif +@@ -30147,12 +33991,16 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + } + case TK_VECTOR: { + char *z = sqlite3_mprintf("VECTOR%s",zFlgs); ++ assert( ExprUseXList(pExpr) ); + sqlite3TreeViewBareExprList(pView, pExpr->x.pList, z); + sqlite3_free(z); + break; + } + case TK_SELECT_COLUMN: { +- sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); ++ sqlite3TreeViewLine(pView, "SELECT-COLUMN %d of [0..%d]%s", ++ pExpr->iColumn, pExpr->iTable-1, ++ pExpr->pRight==pExpr->pLeft ? " (SELECT-owner)" : ""); ++ assert( ExprUseXSelect(pExpr->pLeft) ); + sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); + break; + } +@@ -30161,6 +34009,23 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; + } ++ case TK_ERROR: { ++ Expr tmp; ++ sqlite3TreeViewLine(pView, "ERROR"); ++ tmp = *pExpr; ++ tmp.op = pExpr->op2; ++ sqlite3TreeViewExpr(pView, &tmp, 0); ++ break; ++ } ++ case TK_ROW: { ++ if( pExpr->iColumn<=0 ){ ++ sqlite3TreeViewLine(pView, "First FROM table rowid"); ++ }else{ ++ sqlite3TreeViewLine(pView, "First FROM table column %d", ++ pExpr->iColumn-1); ++ } ++ break; ++ } + default: { + sqlite3TreeViewLine(pView, "op=%d", pExpr->op); + break; +@@ -30174,7 +34039,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m + sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs); + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + } +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + + +@@ -30194,25 +34059,43 @@ SQLITE_PRIVATE void sqlite3TreeViewBareExprList( + sqlite3TreeViewLine(pView, "%s", zLabel); + for(i=0; inExpr; i++){ + int j = pList->a[i].u.x.iOrderByCol; ++ u8 sortFlags = pList->a[i].fg.sortFlags; + char *zName = pList->a[i].zEName; + int moreToFollow = inExpr - 1; +- if( pList->a[i].eEName!=ENAME_NAME ) zName = 0; +- if( j || zName ){ +- sqlite3TreeViewPush(pView, moreToFollow); ++ if( j || zName || sortFlags ){ ++ sqlite3TreeViewPush(&pView, moreToFollow); + moreToFollow = 0; + sqlite3TreeViewLine(pView, 0); + if( zName ){ +- fprintf(stdout, "AS %s ", zName); ++ switch( pList->a[i].fg.eEName ){ ++ default: ++ fprintf(stdout, "AS %s ", zName); ++ break; ++ case ENAME_TAB: ++ fprintf(stdout, "TABLE-ALIAS-NAME(\"%s\") ", zName); ++ if( pList->a[i].fg.bUsed ) fprintf(stdout, "(used) "); ++ if( pList->a[i].fg.bUsingTerm ) fprintf(stdout, "(USING-term) "); ++ if( pList->a[i].fg.bNoExpand ) fprintf(stdout, "(NoExpand) "); ++ break; ++ case ENAME_SPAN: ++ fprintf(stdout, "SPAN(\"%s\") ", zName); ++ break; ++ } + } + if( j ){ +- fprintf(stdout, "iOrderByCol=%d", j); ++ fprintf(stdout, "iOrderByCol=%d ", j); ++ } ++ if( sortFlags & KEYINFO_ORDER_DESC ){ ++ fprintf(stdout, "DESC "); ++ }else if( sortFlags & KEYINFO_ORDER_BIGNULL ){ ++ fprintf(stdout, "NULLS-LAST"); + } + fprintf(stdout, "\n"); + fflush(stdout); + } + sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow); +- if( j || zName ){ +- sqlite3TreeViewPop(pView); ++ if( j || zName || sortFlags ){ ++ sqlite3TreeViewPop(&pView); + } + } + } +@@ -30223,11 +34106,368 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList( + u8 moreToFollow, + const char *zLabel + ){ +- pView = sqlite3TreeViewPush(pView, moreToFollow); ++ sqlite3TreeViewPush(&pView, moreToFollow); + sqlite3TreeViewBareExprList(pView, pList, zLabel); +- sqlite3TreeViewPop(pView); ++ sqlite3TreeViewPop(&pView); + } + ++/* ++** Generate a human-readable explanation of an id-list. ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewBareIdList( ++ TreeView *pView, ++ const IdList *pList, ++ const char *zLabel ++){ ++ if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; ++ if( pList==0 ){ ++ sqlite3TreeViewLine(pView, "%s (empty)", zLabel); ++ }else{ ++ int i; ++ sqlite3TreeViewLine(pView, "%s", zLabel); ++ for(i=0; inId; i++){ ++ char *zName = pList->a[i].zName; ++ int moreToFollow = inId - 1; ++ if( zName==0 ) zName = "(null)"; ++ sqlite3TreeViewPush(&pView, moreToFollow); ++ sqlite3TreeViewLine(pView, 0); ++ fprintf(stdout, "%s\n", zName); ++ sqlite3TreeViewPop(&pView); ++ } ++ } ++} ++SQLITE_PRIVATE void sqlite3TreeViewIdList( ++ TreeView *pView, ++ const IdList *pList, ++ u8 moreToFollow, ++ const char *zLabel ++){ ++ sqlite3TreeViewPush(&pView, moreToFollow); ++ sqlite3TreeViewBareIdList(pView, pList, zLabel); ++ sqlite3TreeViewPop(&pView); ++} ++ ++/* ++** Generate a human-readable explanation of a list of Upsert objects ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewUpsert( ++ TreeView *pView, ++ const Upsert *pUpsert, ++ u8 moreToFollow ++){ ++ if( pUpsert==0 ) return; ++ sqlite3TreeViewPush(&pView, moreToFollow); ++ while( pUpsert ){ ++ int n; ++ sqlite3TreeViewPush(&pView, pUpsert->pNextUpsert!=0 || moreToFollow); ++ sqlite3TreeViewLine(pView, "ON CONFLICT DO %s", ++ pUpsert->isDoUpdate ? "UPDATE" : "NOTHING"); ++ n = (pUpsert->pUpsertSet!=0) + (pUpsert->pUpsertWhere!=0); ++ sqlite3TreeViewExprList(pView, pUpsert->pUpsertTarget, (n--)>0, "TARGET"); ++ sqlite3TreeViewExprList(pView, pUpsert->pUpsertSet, (n--)>0, "SET"); ++ if( pUpsert->pUpsertWhere ){ ++ sqlite3TreeViewItem(pView, "WHERE", (n--)>0); ++ sqlite3TreeViewExpr(pView, pUpsert->pUpsertWhere, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ sqlite3TreeViewPop(&pView); ++ pUpsert = pUpsert->pNextUpsert; ++ } ++ sqlite3TreeViewPop(&pView); ++} ++ ++#if TREETRACE_ENABLED ++/* ++** Generate a human-readable diagram of the data structure that go ++** into generating an DELETE statement. ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewDelete( ++ const With *pWith, ++ const SrcList *pTabList, ++ const Expr *pWhere, ++ const ExprList *pOrderBy, ++ const Expr *pLimit, ++ const Trigger *pTrigger ++){ ++ int n = 0; ++ TreeView *pView = 0; ++ sqlite3TreeViewPush(&pView, 0); ++ sqlite3TreeViewLine(pView, "DELETE"); ++ if( pWith ) n++; ++ if( pTabList ) n++; ++ if( pWhere ) n++; ++ if( pOrderBy ) n++; ++ if( pLimit ) n++; ++ if( pTrigger ) n++; ++ if( pWith ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewWith(pView, pWith, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pTabList ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "FROM"); ++ sqlite3TreeViewSrcList(pView, pTabList); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pWhere ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "WHERE"); ++ sqlite3TreeViewExpr(pView, pWhere, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pOrderBy ){ ++ sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); ++ } ++ if( pLimit ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "LIMIT"); ++ sqlite3TreeViewExpr(pView, pLimit, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pTrigger ){ ++ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); ++ } ++ sqlite3TreeViewPop(&pView); ++} ++#endif /* TREETRACE_ENABLED */ ++ ++#if TREETRACE_ENABLED ++/* ++** Generate a human-readable diagram of the data structure that go ++** into generating an INSERT statement. ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewInsert( ++ const With *pWith, ++ const SrcList *pTabList, ++ const IdList *pColumnList, ++ const Select *pSelect, ++ const ExprList *pExprList, ++ int onError, ++ const Upsert *pUpsert, ++ const Trigger *pTrigger ++){ ++ TreeView *pView = 0; ++ int n = 0; ++ const char *zLabel = "INSERT"; ++ switch( onError ){ ++ case OE_Replace: zLabel = "REPLACE"; break; ++ case OE_Ignore: zLabel = "INSERT OR IGNORE"; break; ++ case OE_Rollback: zLabel = "INSERT OR ROLLBACK"; break; ++ case OE_Abort: zLabel = "INSERT OR ABORT"; break; ++ case OE_Fail: zLabel = "INSERT OR FAIL"; break; ++ } ++ sqlite3TreeViewPush(&pView, 0); ++ sqlite3TreeViewLine(pView, zLabel); ++ if( pWith ) n++; ++ if( pTabList ) n++; ++ if( pColumnList ) n++; ++ if( pSelect ) n++; ++ if( pExprList ) n++; ++ if( pUpsert ) n++; ++ if( pTrigger ) n++; ++ if( pWith ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewWith(pView, pWith, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pTabList ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "INTO"); ++ sqlite3TreeViewSrcList(pView, pTabList); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pColumnList ){ ++ sqlite3TreeViewIdList(pView, pColumnList, (--n)>0, "COLUMNS"); ++ } ++ if( pSelect ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "DATA-SOURCE"); ++ sqlite3TreeViewSelect(pView, pSelect, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pExprList ){ ++ sqlite3TreeViewExprList(pView, pExprList, (--n)>0, "VALUES"); ++ } ++ if( pUpsert ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "UPSERT"); ++ sqlite3TreeViewUpsert(pView, pUpsert, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pTrigger ){ ++ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); ++ } ++ sqlite3TreeViewPop(&pView); ++} ++#endif /* TREETRACE_ENABLED */ ++ ++#if TREETRACE_ENABLED ++/* ++** Generate a human-readable diagram of the data structure that go ++** into generating an UPDATE statement. ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewUpdate( ++ const With *pWith, ++ const SrcList *pTabList, ++ const ExprList *pChanges, ++ const Expr *pWhere, ++ int onError, ++ const ExprList *pOrderBy, ++ const Expr *pLimit, ++ const Upsert *pUpsert, ++ const Trigger *pTrigger ++){ ++ int n = 0; ++ TreeView *pView = 0; ++ const char *zLabel = "UPDATE"; ++ switch( onError ){ ++ case OE_Replace: zLabel = "UPDATE OR REPLACE"; break; ++ case OE_Ignore: zLabel = "UPDATE OR IGNORE"; break; ++ case OE_Rollback: zLabel = "UPDATE OR ROLLBACK"; break; ++ case OE_Abort: zLabel = "UPDATE OR ABORT"; break; ++ case OE_Fail: zLabel = "UPDATE OR FAIL"; break; ++ } ++ sqlite3TreeViewPush(&pView, 0); ++ sqlite3TreeViewLine(pView, zLabel); ++ if( pWith ) n++; ++ if( pTabList ) n++; ++ if( pChanges ) n++; ++ if( pWhere ) n++; ++ if( pOrderBy ) n++; ++ if( pLimit ) n++; ++ if( pUpsert ) n++; ++ if( pTrigger ) n++; ++ if( pWith ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewWith(pView, pWith, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pTabList ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "FROM"); ++ sqlite3TreeViewSrcList(pView, pTabList); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pChanges ){ ++ sqlite3TreeViewExprList(pView, pChanges, (--n)>0, "SET"); ++ } ++ if( pWhere ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "WHERE"); ++ sqlite3TreeViewExpr(pView, pWhere, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pOrderBy ){ ++ sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); ++ } ++ if( pLimit ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "LIMIT"); ++ sqlite3TreeViewExpr(pView, pLimit, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pUpsert ){ ++ sqlite3TreeViewPush(&pView, (--n)>0); ++ sqlite3TreeViewLine(pView, "UPSERT"); ++ sqlite3TreeViewUpsert(pView, pUpsert, 0); ++ sqlite3TreeViewPop(&pView); ++ } ++ if( pTrigger ){ ++ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); ++ } ++ sqlite3TreeViewPop(&pView); ++} ++#endif /* TREETRACE_ENABLED */ ++ ++#ifndef SQLITE_OMIT_TRIGGER ++/* ++** Show a human-readable graph of a TriggerStep ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewTriggerStep( ++ TreeView *pView, ++ const TriggerStep *pStep, ++ u8 moreToFollow, ++ u8 showFullList ++){ ++ int cnt = 0; ++ if( pStep==0 ) return; ++ sqlite3TreeViewPush(&pView, ++ moreToFollow || (showFullList && pStep->pNext!=0)); ++ do{ ++ if( cnt++ && pStep->pNext==0 ){ ++ sqlite3TreeViewPop(&pView); ++ sqlite3TreeViewPush(&pView, 0); ++ } ++ sqlite3TreeViewLine(pView, "%s", pStep->zSpan ? pStep->zSpan : "RETURNING"); ++ }while( showFullList && (pStep = pStep->pNext)!=0 ); ++ sqlite3TreeViewPop(&pView); ++} ++ ++/* ++** Show a human-readable graph of a Trigger ++*/ ++SQLITE_PRIVATE void sqlite3TreeViewTrigger( ++ TreeView *pView, ++ const Trigger *pTrigger, ++ u8 moreToFollow, ++ u8 showFullList ++){ ++ int cnt = 0; ++ if( pTrigger==0 ) return; ++ sqlite3TreeViewPush(&pView, ++ moreToFollow || (showFullList && pTrigger->pNext!=0)); ++ do{ ++ if( cnt++ && pTrigger->pNext==0 ){ ++ sqlite3TreeViewPop(&pView); ++ sqlite3TreeViewPush(&pView, 0); ++ } ++ sqlite3TreeViewLine(pView, "TRIGGER %s", pTrigger->zName); ++ sqlite3TreeViewPush(&pView, 0); ++ sqlite3TreeViewTriggerStep(pView, pTrigger->step_list, 0, 1); ++ sqlite3TreeViewPop(&pView); ++ }while( showFullList && (pTrigger = pTrigger->pNext)!=0 ); ++ sqlite3TreeViewPop(&pView); ++} ++#endif /* SQLITE_OMIT_TRIGGER */ ++ ++ ++/* ++** These simplified versions of the tree-view routines omit unnecessary ++** parameters. These variants are intended to be used from a symbolic ++** debugger, such as "gdb", during interactive debugging sessions. ++** ++** This routines are given external linkage so that they will always be ++** accessible to the debugging, and to avoid warnings about unused ++** functions. But these routines only exist in debugging builds, so they ++** do not contaminate the interface. ++** ++** See Also: ++** ++** sqlite3ShowWhereTerm() in where.c ++*/ ++SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); } ++SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);} ++SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); } ++SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); } ++SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); } ++SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); } ++SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert *p){ sqlite3TreeViewUpsert(0,p,0); } ++#ifndef SQLITE_OMIT_TRIGGER ++SQLITE_PRIVATE void sqlite3ShowTriggerStep(const TriggerStep *p){ ++ sqlite3TreeViewTriggerStep(0,p,0,0); ++} ++SQLITE_PRIVATE void sqlite3ShowTriggerStepList(const TriggerStep *p){ ++ sqlite3TreeViewTriggerStep(0,p,0,1); ++} ++SQLITE_PRIVATE void sqlite3ShowTrigger(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,0); } ++SQLITE_PRIVATE void sqlite3ShowTriggerList(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,1);} ++#endif ++#ifndef SQLITE_OMIT_WINDOWFUNC ++SQLITE_PRIVATE void sqlite3ShowWindow(const Window *p){ sqlite3TreeViewWindow(0,p,0); } ++SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window *p){ sqlite3TreeViewWinFunc(0,p,0); } ++#endif ++ + #endif /* SQLITE_DEBUG */ + + /************** End of treeview.c ********************************************/ +@@ -30256,16 +34496,41 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList( + ** This structure is the current state of the generator. + */ + static SQLITE_WSD struct sqlite3PrngType { +- unsigned char isInit; /* True if initialized */ +- unsigned char i, j; /* State variables */ +- unsigned char s[256]; /* State variables */ ++ u32 s[16]; /* 64 bytes of chacha20 state */ ++ u8 out[64]; /* Output bytes */ ++ u8 n; /* Output bytes remaining */ + } sqlite3Prng; + ++ ++/* The RFC-7539 ChaCha20 block function ++*/ ++#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) ++#define QR(a, b, c, d) ( \ ++ a += b, d ^= a, d = ROTL(d,16), \ ++ c += d, b ^= c, b = ROTL(b,12), \ ++ a += b, d ^= a, d = ROTL(d, 8), \ ++ c += d, b ^= c, b = ROTL(b, 7)) ++static void chacha_block(u32 *out, const u32 *in){ ++ int i; ++ u32 x[16]; ++ memcpy(x, in, 64); ++ for(i=0; i<10; i++){ ++ QR(x[0], x[4], x[ 8], x[12]); ++ QR(x[1], x[5], x[ 9], x[13]); ++ QR(x[2], x[6], x[10], x[14]); ++ QR(x[3], x[7], x[11], x[15]); ++ QR(x[0], x[5], x[10], x[15]); ++ QR(x[1], x[6], x[11], x[12]); ++ QR(x[2], x[7], x[ 8], x[13]); ++ QR(x[3], x[4], x[ 9], x[14]); ++ } ++ for(i=0; i<16; i++) out[i] = x[i]+in[i]; ++} ++ + /* + ** Return N random bytes. + */ + SQLITE_API void sqlite3_randomness(int N, void *pBuf){ +- unsigned char t; + unsigned char *zBuf = pBuf; + + /* The "wsdPrng" macro will resolve to the pseudo-random number generator +@@ -30295,48 +34560,46 @@ SQLITE_API void sqlite3_randomness(int N, void *pBuf){ + + sqlite3_mutex_enter(mutex); + if( N<=0 || pBuf==0 ){ +- wsdPrng.isInit = 0; ++ wsdPrng.s[0] = 0; + sqlite3_mutex_leave(mutex); + return; + } + + /* Initialize the state of the random number generator once, +- ** the first time this routine is called. The seed value does +- ** not need to contain a lot of randomness since we are not +- ** trying to do secure encryption or anything like that... +- ** +- ** Nothing in this file or anywhere else in SQLite does any kind of +- ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random +- ** number generator) not as an encryption device. ++ ** the first time this routine is called. + */ +- if( !wsdPrng.isInit ){ +- int i; +- char k[256]; +- wsdPrng.j = 0; +- wsdPrng.i = 0; +- sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k); +- for(i=0; i<256; i++){ +- wsdPrng.s[i] = (u8)i; +- } +- for(i=0; i<256; i++){ +- wsdPrng.j += wsdPrng.s[i] + k[i]; +- t = wsdPrng.s[wsdPrng.j]; +- wsdPrng.s[wsdPrng.j] = wsdPrng.s[i]; +- wsdPrng.s[i] = t; ++ if( wsdPrng.s[0]==0 ){ ++ sqlite3_vfs *pVfs = sqlite3_vfs_find(0); ++ static const u32 chacha20_init[] = { ++ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 ++ }; ++ memcpy(&wsdPrng.s[0], chacha20_init, 16); ++ if( NEVER(pVfs==0) ){ ++ memset(&wsdPrng.s[4], 0, 44); ++ }else{ ++ sqlite3OsRandomness(pVfs, 44, (char*)&wsdPrng.s[4]); + } +- wsdPrng.isInit = 1; ++ wsdPrng.s[15] = wsdPrng.s[12]; ++ wsdPrng.s[12] = 0; ++ wsdPrng.n = 0; + } + + assert( N>0 ); +- do{ +- wsdPrng.i++; +- t = wsdPrng.s[wsdPrng.i]; +- wsdPrng.j += t; +- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j]; +- wsdPrng.s[wsdPrng.j] = t; +- t += wsdPrng.s[wsdPrng.i]; +- *(zBuf++) = wsdPrng.s[t]; +- }while( --N ); ++ while( 1 /* exit by break */ ){ ++ if( N<=wsdPrng.n ){ ++ memcpy(zBuf, &wsdPrng.out[wsdPrng.n-N], N); ++ wsdPrng.n -= N; ++ break; ++ } ++ if( wsdPrng.n>0 ){ ++ memcpy(zBuf, wsdPrng.out, wsdPrng.n); ++ N -= wsdPrng.n; ++ zBuf += wsdPrng.n; ++ } ++ wsdPrng.s[12]++; ++ chacha_block((u32*)wsdPrng.out, wsdPrng.s); ++ wsdPrng.n = 64; ++ } + sqlite3_mutex_leave(mutex); + } + +@@ -30753,6 +35016,35 @@ static const unsigned char sqlite3Utf8Trans1[] = { + } \ + } + ++/* ++** Write a single UTF8 character whose value is v into the ++** buffer starting at zOut. zOut must be sized to hold at ++** least four bytes. Return the number of bytes needed ++** to encode the new character. ++*/ ++SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char *zOut, u32 v){ ++ if( v<0x00080 ){ ++ zOut[0] = (u8)(v & 0xff); ++ return 1; ++ } ++ if( v<0x00800 ){ ++ zOut[0] = 0xc0 + (u8)((v>>6) & 0x1f); ++ zOut[1] = 0x80 + (u8)(v & 0x3f); ++ return 2; ++ } ++ if( v<0x10000 ){ ++ zOut[0] = 0xe0 + (u8)((v>>12) & 0x0f); ++ zOut[1] = 0x80 + (u8)((v>>6) & 0x3f); ++ zOut[2] = 0x80 + (u8)(v & 0x3f); ++ return 3; ++ } ++ zOut[0] = 0xf0 + (u8)((v>>18) & 0x07); ++ zOut[1] = 0x80 + (u8)((v>>12) & 0x3f); ++ zOut[2] = 0x80 + (u8)((v>>6) & 0x3f); ++ zOut[3] = 0x80 + (u8)(v & 0x3f); ++ return 4; ++} ++ + /* + ** Translate a single UTF-8 character. Return the unicode value. + ** +@@ -30784,7 +35076,7 @@ static const unsigned char sqlite3Utf8Trans1[] = { + c = *(zIn++); \ + if( c>=0xc0 ){ \ + c = sqlite3Utf8Trans1[c-0xc0]; \ +- while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ ++ while( zIn0 ); ++ c = z[0]; ++ if( c>=0xc0 ){ ++ c = sqlite3Utf8Trans1[c-0xc0]; ++ if( n>4 ) n = 4; ++ while( i=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; ++ if( c>=0xd8 && c<0xdc && z<=zEnd && z[0]>=0xdc && z[0]<0xe0 ) z += 2; + n++; + } + return (int)(z-(unsigned char const *)zIn) +@@ -31207,20 +35532,10 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){ + #include + #endif + +-/* +-** Routine needed to support the testcase() macro. +-*/ +-#ifdef SQLITE_COVERAGE_TEST +-SQLITE_PRIVATE void sqlite3Coverage(int x){ +- static unsigned dummy = 0; +- dummy += (unsigned)x; +-} +-#endif +- + /* + ** Calls to sqlite3FaultSim() are used to simulate a failure during testing, + ** or to bypass normal error detection during testing in order to let +-** execute proceed futher downstream. ++** execute proceed further downstream. + ** + ** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The + ** sqlite3FaultSim() function only returns non-zero during testing. +@@ -31246,11 +35561,34 @@ SQLITE_PRIVATE int sqlite3FaultSim(int iTest){ + #ifndef SQLITE_OMIT_FLOATING_POINT + /* + ** Return true if the floating point value is Not a Number (NaN). ++** ++** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. ++** Otherwise, we have our own implementation that works on most systems. + */ + SQLITE_PRIVATE int sqlite3IsNaN(double x){ ++ int rc; /* The value return */ ++#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN + u64 y; + memcpy(&y,&x,sizeof(y)); +- return IsNaN(y); ++ rc = IsNaN(y); ++#else ++ rc = isnan(x); ++#endif /* HAVE_ISNAN */ ++ testcase( rc ); ++ return rc; ++} ++#endif /* SQLITE_OMIT_FLOATING_POINT */ ++ ++#ifndef SQLITE_OMIT_FLOATING_POINT ++/* ++** Return true if the floating point value is NaN or +Inf or -Inf. ++*/ ++SQLITE_PRIVATE int sqlite3IsOverflow(double x){ ++ int rc; /* The value return */ ++ u64 y; ++ memcpy(&y,&x,sizeof(y)); ++ rc = IsOvfl(y); ++ return rc; + } + #endif /* SQLITE_OMIT_FLOATING_POINT */ + +@@ -31275,8 +35613,14 @@ SQLITE_PRIVATE int sqlite3Strlen30(const char *z){ + ** the column name if and only if the COLFLAG_HASTYPE flag is set. + */ + SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){ +- if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt; +- return pCol->zName + strlen(pCol->zName) + 1; ++ if( pCol->colFlags & COLFLAG_HASTYPE ){ ++ return pCol->zCnName + strlen(pCol->zCnName) + 1; ++ }else if( pCol->eCType ){ ++ assert( pCol->eCType<=SQLITE_N_STDTYPE ); ++ return (char*)sqlite3StdType[pCol->eCType-1]; ++ }else{ ++ return zDflt; ++ } + } + + /* +@@ -31297,7 +35641,22 @@ static SQLITE_NOINLINE void sqlite3ErrorFinish(sqlite3 *db, int err_code){ + SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){ + assert( db!=0 ); + db->errCode = err_code; +- if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code); ++ if( err_code || db->pErr ){ ++ sqlite3ErrorFinish(db, err_code); ++ }else{ ++ db->errByteOffset = -1; ++ } ++} ++ ++/* ++** The equivalent of sqlite3Error(db, SQLITE_OK). Clear the error state ++** and error message. ++*/ ++SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){ ++ assert( db!=0 ); ++ db->errCode = SQLITE_OK; ++ db->errByteOffset = -1; ++ if( db->pErr ) sqlite3ValueSetNull(db->pErr); + } + + /* +@@ -31306,6 +35665,23 @@ SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){ + */ + SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){ + if( rc==SQLITE_IOERR_NOMEM ) return; ++#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) ++ if( rc==SQLITE_IOERR_IN_PAGE ){ ++ int ii; ++ int iErr; ++ sqlite3BtreeEnterAll(db); ++ for(ii=0; iinDb; ii++){ ++ if( db->aDb[ii].pBt ){ ++ iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt)); ++ if( iErr ){ ++ db->iSysErrno = iErr; ++ } ++ } ++ } ++ sqlite3BtreeLeaveAll(db); ++ return; ++ } ++#endif + rc &= 0xff; + if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){ + db->iSysErrno = sqlite3OsGetLastError(db->pVfs); +@@ -31317,17 +35693,8 @@ SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){ + ** handle "db". The error code is set to "err_code". + ** + ** If it is not NULL, string zFormat specifies the format of the +-** error string in the style of the printf functions: The following +-** format characters are allowed: +-** +-** %s Insert a string +-** %z A string that should be freed after use +-** %d Insert an integer +-** %T Insert a token +-** %S Insert the first element of a SrcList +-** +-** zFormat and any string tokens that follow it are assumed to be +-** encoded in UTF-8. ++** error string. zFormat and any string tokens that follow it are ++** assumed to be encoded in UTF-8. + ** + ** To clear the most recent error for sqlite handle "db", sqlite3Error + ** should be called with err_code set to SQLITE_OK and zFormat set +@@ -31349,15 +35716,32 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *z + } + } + ++/* ++** Check for interrupts and invoke progress callback. ++*/ ++SQLITE_PRIVATE void sqlite3ProgressCheck(Parse *p){ ++ sqlite3 *db = p->db; ++ if( AtomicLoad(&db->u1.isInterrupted) ){ ++ p->nErr++; ++ p->rc = SQLITE_INTERRUPT; ++ } ++#ifndef SQLITE_OMIT_PROGRESS_CALLBACK ++ if( db->xProgress ){ ++ if( p->rc==SQLITE_INTERRUPT ){ ++ p->nProgressSteps = 0; ++ }else if( (++p->nProgressSteps)>=db->nProgressOps ){ ++ if( db->xProgress(db->pProgressArg) ){ ++ p->nErr++; ++ p->rc = SQLITE_INTERRUPT; ++ } ++ p->nProgressSteps = 0; ++ } ++ } ++#endif ++} ++ + /* + ** Add an error message to pParse->zErrMsg and increment pParse->nErr. +-** The following formatting characters are allowed: +-** +-** %s Insert a string +-** %z A string that should be freed after use +-** %d Insert an integer +-** %T Insert a token +-** %S Insert the first element of a SrcList + ** + ** This function should be used to report any error that occurs while + ** compiling an SQL statement (i.e. within sqlite3_prepare()). The +@@ -31370,11 +35754,19 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ + char *zMsg; + va_list ap; + sqlite3 *db = pParse->db; ++ assert( db!=0 ); ++ assert( db->pParse==pParse || db->pParse->pToplevel==pParse ); ++ db->errByteOffset = -2; + va_start(ap, zFormat); + zMsg = sqlite3VMPrintf(db, zFormat, ap); + va_end(ap); ++ if( db->errByteOffset<-1 ) db->errByteOffset = -1; + if( db->suppressErr ){ + sqlite3DbFree(db, zMsg); ++ if( db->mallocFailed ){ ++ pParse->nErr++; ++ pParse->rc = SQLITE_NOMEM; ++ } + }else{ + pParse->nErr++; + sqlite3DbFree(db, pParse->zErrMsg); +@@ -31437,11 +35829,72 @@ SQLITE_PRIVATE void sqlite3Dequote(char *z){ + z[j] = 0; + } + SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){ ++ assert( !ExprHasProperty(p, EP_IntValue) ); + assert( sqlite3Isquote(p->u.zToken[0]) ); + p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; + sqlite3Dequote(p->u.zToken); + } + ++/* ++** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken ++** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those ++** that contain '_' characters that must be removed before further processing. ++*/ ++SQLITE_PRIVATE void sqlite3DequoteNumber(Parse *pParse, Expr *p){ ++ assert( p!=0 || pParse->db->mallocFailed ); ++ if( p ){ ++ const char *pIn = p->u.zToken; ++ char *pOut = p->u.zToken; ++ int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X')); ++ int iValue; ++ assert( p->op==TK_QNUMBER ); ++ p->op = TK_INTEGER; ++ do { ++ if( *pIn!=SQLITE_DIGIT_SEPARATOR ){ ++ *pOut++ = *pIn; ++ if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT; ++ }else{ ++ if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1]))) ++ || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1]))) ++ ){ ++ sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken); ++ } ++ } ++ }while( *pIn++ ); ++ if( bHex ) p->op = TK_INTEGER; ++ ++ /* tag-20240227-a: If after dequoting, the number is an integer that ++ ** fits in 32 bits, then it must be converted into EP_IntValue. Other ++ ** parts of the code expect this. See also tag-20240227-b. */ ++ if( p->op==TK_INTEGER && sqlite3GetInt32(p->u.zToken, &iValue) ){ ++ p->u.iValue = iValue; ++ p->flags |= EP_IntValue; ++ } ++ } ++} ++ ++/* ++** If the input token p is quoted, try to adjust the token to remove ++** the quotes. This is not always possible: ++** ++** "abc" -> abc ++** "ab""cd" -> (not possible because of the interior "") ++** ++** Remove the quotes if possible. This is a optimization. The overall ++** system should still return the correct answer even if this routine ++** is always a no-op. ++*/ ++SQLITE_PRIVATE void sqlite3DequoteToken(Token *p){ ++ unsigned int i; ++ if( p->n<2 ) return; ++ if( !sqlite3Isquote(p->z[0]) ) return; ++ for(i=1; in-1; i++){ ++ if( sqlite3Isquote(p->z[i]) ) return; ++ } ++ p->n -= 2; ++ p->z++; ++} ++ + /* + ** Generate a Token object from a string + */ +@@ -31516,43 +35969,40 @@ SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){ + return h; + } + +-/* +-** Compute 10 to the E-th power. Examples: E==1 results in 10. +-** E==2 results in 100. E==50 results in 1.0e50. ++/* Double-Double multiplication. (x[0],x[1]) *= (y,yy) + ** +-** This routine only works for values of E between 1 and 341. ++** Reference: ++** T. J. Dekker, "A Floating-Point Technique for Extending the ++** Available Precision". 1971-07-26. + */ +-static LONGDOUBLE_TYPE sqlite3Pow10(int E){ +-#if defined(_MSC_VER) +- static const LONGDOUBLE_TYPE x[] = { +- 1.0e+001L, +- 1.0e+002L, +- 1.0e+004L, +- 1.0e+008L, +- 1.0e+016L, +- 1.0e+032L, +- 1.0e+064L, +- 1.0e+128L, +- 1.0e+256L +- }; +- LONGDOUBLE_TYPE r = 1.0; +- int i; +- assert( E>=0 && E<=307 ); +- for(i=0; E!=0; i++, E >>=1){ +- if( E & 1 ) r *= x[i]; +- } +- return r; +-#else +- LONGDOUBLE_TYPE x = 10.0; +- LONGDOUBLE_TYPE r = 1.0; +- while(1){ +- if( E & 1 ) r *= x; +- E >>= 1; +- if( E==0 ) break; +- x *= x; +- } +- return r; +-#endif ++static void dekkerMul2(volatile double *x, double y, double yy){ ++ /* ++ ** The "volatile" keywords on parameter x[] and on local variables ++ ** below are needed force intermediate results to be truncated to ++ ** binary64 rather than be carried around in an extended-precision ++ ** format. The truncation is necessary for the Dekker algorithm to ++ ** work. Intel x86 floating point might omit the truncation without ++ ** the use of volatile. ++ */ ++ volatile double tx, ty, p, q, c, cc; ++ double hx, hy; ++ u64 m; ++ memcpy(&m, (void*)&x[0], 8); ++ m &= 0xfffffffffc000000LL; ++ memcpy(&hx, &m, 8); ++ tx = x[0] - hx; ++ memcpy(&m, &y, 8); ++ m &= 0xfffffffffc000000LL; ++ memcpy(&hy, &m, 8); ++ ty = y - hy; ++ p = hx*hy; ++ q = hx*ty + tx*hy; ++ c = p+q; ++ cc = p - c + q + tx*ty; ++ cc = x[0]*yy + x[1]*y + cc; ++ x[0] = c + cc; ++ x[1] = c - x[0]; ++ x[1] += cc; + } + + /* +@@ -31593,14 +36043,15 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en + const char *zEnd; + /* sign * significand * (10 ^ (esign * exponent)) */ + int sign = 1; /* sign of significand */ +- i64 s = 0; /* significand */ ++ u64 s = 0; /* significand */ + int d = 0; /* adjust exponent for shifting decimal point */ + int esign = 1; /* sign of exponent */ + int e = 0; /* exponent */ + int eValid = 1; /* True exponent is either not used or is well-formed */ +- double result; + int nDigit = 0; /* Number of digits processed */ + int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ ++ u64 s2; /* round-tripped significand */ ++ double rr[2]; + + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); + *pResult = 0.0; /* Default return value, in case of an error */ +@@ -31638,7 +36089,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en + while( z=((LARGEST_INT64-9)/10) ){ ++ if( s>=((LARGEST_UINT64-9)/10) ){ + /* skip non-significant significand digits + ** (increase exponent by d to shift decimal left) */ + while( z0 ){ /*OPTIMIZATION-IF-TRUE*/ +- if( esign>0 ){ +- if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/ +- s *= 10; +- }else{ +- if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/ +- s /= 10; +- } +- e--; +- } ++ /* Try to adjust the exponent to make it smaller */ ++ while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){ ++ s *= 10; ++ e--; ++ } ++ while( e<0 && (s%10)==0 ){ ++ s /= 10; ++ e++; ++ } + +- /* adjust the sign of significand */ +- s = sign<0 ? -s : s; ++ rr[0] = (double)s; ++ assert( sizeof(s2)==sizeof(rr[0]) ); ++#ifdef SQLITE_DEBUG ++ rr[1] = 18446744073709549568.0; ++ memcpy(&s2, &rr[1], sizeof(s2)); ++ assert( s2==0x43efffffffffffffLL ); ++#endif ++ /* Largest double that can be safely converted to u64 ++ ** vvvvvvvvvvvvvvvvvvvvvv */ ++ if( rr[0]<=18446744073709549568.0 ){ ++ s2 = (u64)rr[0]; ++ rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); ++ }else{ ++ rr[1] = 0.0; ++ } ++ assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */ + +- if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/ +- result = (double)s; +- }else{ +- /* attempt to handle extremely small/large numbers better */ +- if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/ +- if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/ +- LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308); +- if( esign<0 ){ +- result = s / scale; +- result /= 1.0e+308; +- }else{ +- result = s * scale; +- result *= 1.0e+308; +- } +- }else{ assert( e>=342 ); +- if( esign<0 ){ +- result = 0.0*s; +- }else{ +-#ifdef INFINITY +- result = INFINITY*s; +-#else +- result = 1e308*1e308*s; /* Infinity */ +-#endif +- } +- } +- }else{ +- LONGDOUBLE_TYPE scale = sqlite3Pow10(e); +- if( esign<0 ){ +- result = s / scale; +- }else{ +- result = s * scale; +- } +- } ++ if( e>0 ){ ++ while( e>=100 ){ ++ e -= 100; ++ dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); ++ } ++ while( e>=10 ){ ++ e -= 10; ++ dekkerMul2(rr, 1.0e+10, 0.0); ++ } ++ while( e>=1 ){ ++ e -= 1; ++ dekkerMul2(rr, 1.0e+01, 0.0); ++ } ++ }else{ ++ while( e<=-100 ){ ++ e += 100; ++ dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); ++ } ++ while( e<=-10 ){ ++ e += 10; ++ dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); ++ } ++ while( e<=-1 ){ ++ e += 1; ++ dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); + } + } ++ *pResult = rr[0]+rr[1]; ++ if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; ++ if( sign<0 ) *pResult = -*pResult; ++ assert( !sqlite3IsNaN(*pResult) ); + +- /* store the result */ +- *pResult = result; +- +- /* return true if number and no extra non-whitespace chracters after */ ++atof_return: ++ /* return true if number and no extra non-whitespace characters after */ + if( z==zEnd && nDigit>0 && eValid && eType>0 ){ + return eType; + }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){ +@@ -31782,11 +36230,14 @@ do_atof_calc: + #endif + + /* +-** Render an signed 64-bit integer as text. Store the result in zOut[]. ++** Render an signed 64-bit integer as text. Store the result in zOut[] and ++** return the length of the string that was stored, in bytes. The value ++** returned does not include the zero terminator at the end of the output ++** string. + ** + ** The caller must ensure that zOut[] is at least 21 bytes in size. + */ +-SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ ++SQLITE_PRIVATE int sqlite3Int64ToText(i64 v, char *zOut){ + int i; + u64 x; + char zTemp[22]; +@@ -31797,12 +36248,15 @@ SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ + } + i = sizeof(zTemp)-2; + zTemp[sizeof(zTemp)-1] = 0; +- do{ +- zTemp[i--] = (x%10) + '0'; ++ while( 1 /*exit-by-break*/ ){ ++ zTemp[i] = (x%10) + '0'; + x = x/10; +- }while( x ); +- if( v<0 ) zTemp[i--] = '-'; +- memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); ++ if( x==0 ) break; ++ i--; ++ }; ++ if( v<0 ) zTemp[--i] = '-'; ++ memcpy(zOut, &zTemp[i], sizeof(zTemp)-i); ++ return sizeof(zTemp)-1-i; + } + + /* +@@ -31867,6 +36321,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc + incr = 1; + }else{ + incr = 2; ++ length &= ~1; + assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); + for(i=3-enc; i16 ) return 2; ++ if( z[k]!=0 ) return 1; ++ return 0; + }else + #endif /* SQLITE_OMIT_HEX_INTEGER */ + { +- return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); ++ int n = (int)(0x3fffffff&strspn(z,"+- \n\t0123456789")); ++ if( z[n] ) n++; ++ return sqlite3Atoi64(z, pOut, n, SQLITE_UTF8); + } + } + +@@ -32002,7 +36461,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ + u32 u = 0; + zNum += 2; + while( zNum[0]=='0' ) zNum++; +- for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){ ++ for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){ + u = u*16 + sqlite3HexToInt(zNum[i]); + } + if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ +@@ -32049,6 +36508,146 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ + return x; + } + ++/* ++** Decode a floating-point value into an approximate decimal ++** representation. ++** ++** If iRound<=0 then round to -iRound significant digits to the ++** the left of the decimal point, or to a maximum of mxRound total ++** significant digits. ++** ++** If iRound>0 round to min(iRound,mxRound) significant digits total. ++** ++** mxRound must be positive. ++** ++** The significant digits of the decimal representation are ++** stored in p->z[] which is a often (but not always) a pointer ++** into the middle of p->zBuf[]. There are p->n significant digits. ++** The p->z[] array is *not* zero-terminated. ++*/ ++SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ ++ int i; ++ u64 v; ++ int e, exp = 0; ++ double rr[2]; ++ ++ p->isSpecial = 0; ++ p->z = p->zBuf; ++ assert( mxRound>0 ); ++ ++ /* Convert negative numbers to positive. Deal with Infinity, 0.0, and ++ ** NaN. */ ++ if( r<0.0 ){ ++ p->sign = '-'; ++ r = -r; ++ }else if( r==0.0 ){ ++ p->sign = '+'; ++ p->n = 1; ++ p->iDP = 1; ++ p->z = "0"; ++ return; ++ }else{ ++ p->sign = '+'; ++ } ++ memcpy(&v,&r,8); ++ e = v>>52; ++ if( (e&0x7ff)==0x7ff ){ ++ p->isSpecial = 1 + (v!=0x7ff0000000000000LL); ++ p->n = 0; ++ p->iDP = 0; ++ return; ++ } ++ ++ /* Multiply r by powers of ten until it lands somewhere in between ++ ** 1.0e+19 and 1.0e+17. ++ ** ++ ** Use Dekker-style double-double computation to increase the ++ ** precision. ++ ** ++ ** The error terms on constants like 1.0e+100 computed using the ++ ** decimal extension, for example as follows: ++ ** ++ ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); ++ */ ++ rr[0] = r; ++ rr[1] = 0.0; ++ if( rr[0]>9.223372036854774784e+18 ){ ++ while( rr[0]>9.223372036854774784e+118 ){ ++ exp += 100; ++ dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); ++ } ++ while( rr[0]>9.223372036854774784e+28 ){ ++ exp += 10; ++ dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); ++ } ++ while( rr[0]>9.223372036854774784e+18 ){ ++ exp += 1; ++ dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); ++ } ++ }else{ ++ while( rr[0]<9.223372036854774784e-83 ){ ++ exp -= 100; ++ dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); ++ } ++ while( rr[0]<9.223372036854774784e+07 ){ ++ exp -= 10; ++ dekkerMul2(rr, 1.0e+10, 0.0); ++ } ++ while( rr[0]<9.22337203685477478e+17 ){ ++ exp -= 1; ++ dekkerMul2(rr, 1.0e+01, 0.0); ++ } ++ } ++ v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; ++ ++ /* Extract significant digits. */ ++ i = sizeof(p->zBuf)-1; ++ assert( v>0 ); ++ while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } ++ assert( i>=0 && izBuf)-1 ); ++ p->n = sizeof(p->zBuf) - 1 - i; ++ assert( p->n>0 ); ++ assert( p->nzBuf) ); ++ p->iDP = p->n + exp; ++ if( iRound<=0 ){ ++ iRound = p->iDP - iRound; ++ if( iRound==0 && p->zBuf[i+1]>='5' ){ ++ iRound = 1; ++ p->zBuf[i--] = '0'; ++ p->n++; ++ p->iDP++; ++ } ++ } ++ if( iRound>0 && (iRoundn || p->n>mxRound) ){ ++ char *z = &p->zBuf[i+1]; ++ if( iRound>mxRound ) iRound = mxRound; ++ p->n = iRound; ++ if( z[iRound]>='5' ){ ++ int j = iRound-1; ++ while( 1 /*exit-by-break*/ ){ ++ z[j]++; ++ if( z[j]<='9' ) break; ++ z[j] = '0'; ++ if( j==0 ){ ++ p->z[i--] = '1'; ++ p->n++; ++ p->iDP++; ++ break; ++ }else{ ++ j--; ++ } ++ } ++ } ++ } ++ p->z = &p->zBuf[i+1]; ++ assert( i+p->n < sizeof(p->zBuf) ); ++ assert( p->n>0 ); ++ while( p->z[p->n-1]=='0' ){ ++ p->n--; ++ assert( p->n>0 ); ++ } ++} ++ + /* + ** Try to convert z into an unsigned 32-bit integer. Return true on + ** success and false if there is an error. +@@ -32312,121 +36911,32 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ + ** this function assumes the single-byte case has already been handled. + */ + SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ +- u32 a,b; ++ u64 v64; ++ u8 n; + +- /* The 1-byte case. Overwhelmingly the most common. Handled inline +- ** by the getVarin32() macro */ +- a = *p; +- /* a: p0 (unmasked) */ +-#ifndef getVarint32 +- if (!(a&0x80)) +- { +- /* Values between 0 and 127 */ +- *v = a; +- return 1; +- } +-#endif ++ /* Assume that the single-byte case has already been handled by ++ ** the getVarint32() macro */ ++ assert( (p[0] & 0x80)!=0 ); + +- /* The 2-byte case */ +- p++; +- b = *p; +- /* b: p1 (unmasked) */ +- if (!(b&0x80)) +- { +- /* Values between 128 and 16383 */ +- a &= 0x7f; +- a = a<<7; +- *v = a | b; ++ if( (p[1] & 0x80)==0 ){ ++ /* This is the two-byte case */ ++ *v = ((p[0]&0x7f)<<7) | p[1]; + return 2; + } +- +- /* The 3-byte case */ +- p++; +- a = a<<14; +- a |= *p; +- /* a: p0<<14 | p2 (unmasked) */ +- if (!(a&0x80)) +- { +- /* Values between 16384 and 2097151 */ +- a &= (0x7f<<14)|(0x7f); +- b &= 0x7f; +- b = b<<7; +- *v = a | b; ++ if( (p[2] & 0x80)==0 ){ ++ /* This is the three-byte case */ ++ *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2]; + return 3; + } +- +- /* A 32-bit varint is used to store size information in btrees. +- ** Objects are rarely larger than 2MiB limit of a 3-byte varint. +- ** A 3-byte varint is sufficient, for example, to record the size +- ** of a 1048569-byte BLOB or string. +- ** +- ** We only unroll the first 1-, 2-, and 3- byte cases. The very +- ** rare larger cases can be handled by the slower 64-bit varint +- ** routine. +- */ +-#if 1 +- { +- u64 v64; +- u8 n; +- +- n = sqlite3GetVarint(p-2, &v64); +- assert( n>3 && n<=9 ); +- if( (v64 & SQLITE_MAX_U32)!=v64 ){ +- *v = 0xffffffff; +- }else{ +- *v = (u32)v64; +- } +- return n; +- } +- +-#else +- /* For following code (kept for historical record only) shows an +- ** unrolling for the 3- and 4-byte varint cases. This code is +- ** slightly faster, but it is also larger and much harder to test. +- */ +- p++; +- b = b<<14; +- b |= *p; +- /* b: p1<<14 | p3 (unmasked) */ +- if (!(b&0x80)) +- { +- /* Values between 2097152 and 268435455 */ +- b &= (0x7f<<14)|(0x7f); +- a &= (0x7f<<14)|(0x7f); +- a = a<<7; +- *v = a | b; +- return 4; +- } +- +- p++; +- a = a<<14; +- a |= *p; +- /* a: p0<<28 | p2<<14 | p4 (unmasked) */ +- if (!(a&0x80)) +- { +- /* Values between 268435456 and 34359738367 */ +- a &= SLOT_4_2_0; +- b &= SLOT_4_2_0; +- b = b<<7; +- *v = a | b; +- return 5; +- } +- +- /* We can only reach this point when reading a corrupt database +- ** file. In that case we are not in any hurry. Use the (relatively +- ** slow) general-purpose sqlite3GetVarint() routine to extract the +- ** value. */ +- { +- u64 v64; +- u8 n; +- +- p -= 4; +- n = sqlite3GetVarint(p, &v64); +- assert( n>5 && n<=9 ); ++ /* four or more bytes */ ++ n = sqlite3GetVarint(p, &v64); ++ assert( n>3 && n<=9 ); ++ if( (v64 & SQLITE_MAX_U32)!=v64 ){ ++ *v = 0xffffffff; ++ }else{ + *v = (u32)v64; +- return n; + } +-#endif ++ return n; + } + + /* +@@ -32546,13 +37056,13 @@ static void logBadConnection(const char *zType){ + ** used as an argument to sqlite3_errmsg() or sqlite3_close(). + */ + SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){ +- u32 magic; ++ u8 eOpenState; + if( db==0 ){ + logBadConnection("NULL"); + return 0; + } +- magic = db->magic; +- if( magic!=SQLITE_MAGIC_OPEN ){ ++ eOpenState = db->eOpenState; ++ if( eOpenState!=SQLITE_STATE_OPEN ){ + if( sqlite3SafetyCheckSickOrOk(db) ){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + logBadConnection("unopened"); +@@ -32563,11 +37073,11 @@ SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){ + } + } + SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ +- u32 magic; +- magic = db->magic; +- if( magic!=SQLITE_MAGIC_SICK && +- magic!=SQLITE_MAGIC_OPEN && +- magic!=SQLITE_MAGIC_BUSY ){ ++ u8 eOpenState; ++ eOpenState = db->eOpenState; ++ if( eOpenState!=SQLITE_STATE_SICK && ++ eOpenState!=SQLITE_STATE_OPEN && ++ eOpenState!=SQLITE_STATE_BUSY ){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + logBadConnection("invalid"); + return 0; +@@ -32577,7 +37087,7 @@ SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ + } + + /* +-** Attempt to add, substract, or multiply the 64-bit signed value iB against ++** Attempt to add, subtract, or multiply the 64-bit signed value iB against + ** the other 64-bit signed integer at *pA and store the result in *pA. + ** Return 0 on success. Or if the operation would have resulted in an + ** overflow, leave *pA unchanged and return 1. +@@ -32640,7 +37150,7 @@ SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){ + } + + /* +-** Compute the absolute value of a 32-bit signed integer, of possible. Or ++** Compute the absolute value of a 32-bit signed integer, if possible. Or + ** if the integer has a value of -2147483648, return +2147483647 + */ + SQLITE_PRIVATE int sqlite3AbsInt32(int x){ +@@ -32732,7 +37242,6 @@ SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){ + return a[x&7] + y - 10; + } + +-#ifndef SQLITE_OMIT_VIRTUALTABLE + /* + ** Convert a double into a LogEst + ** In other words, compute an approximation for 10*log2(x). +@@ -32747,16 +37256,9 @@ SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){ + e = (a>>52) - 1022; + return e*10; + } +-#endif /* SQLITE_OMIT_VIRTUALTABLE */ + +-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ +- defined(SQLITE_ENABLE_STAT4) || \ +- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) + /* + ** Convert a LogEst into an integer. +-** +-** Note that this routine is only used when one or more of various +-** non-standard compile-time options is enabled. + */ + SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){ + u64 n; +@@ -32764,17 +37266,9 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){ + x /= 10; + if( n>=5 ) n -= 2; + else if( n>=1 ) n -= 1; +-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ +- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) + if( x>60 ) return (u64)LARGEST_INT64; +-#else +- /* If only SQLITE_ENABLE_STAT4 is on, then the largest input +- ** possible to this routine is 310, resulting in a maximum x of 31 */ +- assert( x<=60 ); +-#endif + return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x); + } +-#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */ + + /* + ** Add a new name/number pair to a VList. This might require that the +@@ -32937,12 +37431,19 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){ + */ + static unsigned int strHash(const char *z){ + unsigned int h = 0; +- unsigned char c; +- while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/ ++ while( z[0] ){ /*OPTIMIZATION-IF-TRUE*/ + /* Knuth multiplicative hashing. (Sorting & Searching, p. 510). + ** 0x9e3779b1 is 2654435761 which is the closest prime number to +- ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */ +- h += sqlite3UpperToLower[c]; ++ ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. ++ ** ++ ** Only bits 0xdf for ASCII and bits 0xbf for EBCDIC each octet are ++ ** hashed since the omitted bits determine the upper/lower case difference. ++ */ ++#ifdef SQLITE_EBCDIC ++ h += 0xbf & (unsigned char)*(z++); ++#else ++ h += 0xdf & (unsigned char)*(z++); ++#endif + h *= 0x9e3779b1; + } + return h; +@@ -32980,7 +37481,7 @@ static void insertElement( + } + + +-/* Resize the hash table so that it cantains "new_size" buckets. ++/* Resize the hash table so that it contains "new_size" buckets. + ** + ** The hash table might fail to resize if sqlite3_malloc() fails or + ** if the new size is the same as the prior size. +@@ -33015,9 +37516,8 @@ static int rehash(Hash *pH, unsigned int new_size){ + pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht); + memset(new_ht, 0, new_size*sizeof(struct _ht)); + for(elem=pH->first, pH->first=0; elem; elem = next_elem){ +- unsigned int h = strHash(elem->pKey) % new_size; + next_elem = elem->next; +- insertElement(pH, &new_ht[h], elem); ++ insertElement(pH, &new_ht[elem->h % new_size], elem); + } + return 1; + } +@@ -33035,26 +37535,26 @@ static HashElem *findElementWithHash( + HashElem *elem; /* Used to loop thru the element list */ + unsigned int count; /* Number of elements left to test */ + unsigned int h; /* The computed hash */ +- static HashElem nullElement = { 0, 0, 0, 0 }; ++ static HashElem nullElement = { 0, 0, 0, 0, 0 }; + ++ h = strHash(pKey); + if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/ + struct _ht *pEntry; +- h = strHash(pKey) % pH->htsize; +- pEntry = &pH->ht[h]; ++ pEntry = &pH->ht[h % pH->htsize]; + elem = pEntry->chain; + count = pEntry->count; + }else{ +- h = 0; + elem = pH->first; + count = pH->count; + } + if( pHash ) *pHash = h; +- while( count-- ){ ++ while( count ){ + assert( elem!=0 ); +- if( sqlite3StrICmp(elem->pKey,pKey)==0 ){ ++ if( h==elem->h && sqlite3StrICmp(elem->pKey,pKey)==0 ){ + return elem; + } + elem = elem->next; ++ count--; + } + return &nullElement; + } +@@ -33062,10 +37562,9 @@ static HashElem *findElementWithHash( + /* Remove a single entry from the hash table given a pointer to that + ** element and a hash on the element's key. + */ +-static void removeElementGivenHash( ++static void removeElement( + Hash *pH, /* The pH containing "elem" */ +- HashElem* elem, /* The element to be removed from the pH */ +- unsigned int h /* Hash value for the element */ ++ HashElem *elem /* The element to be removed from the pH */ + ){ + struct _ht *pEntry; + if( elem->prev ){ +@@ -33077,7 +37576,7 @@ static void removeElementGivenHash( + elem->next->prev = elem->prev; + } + if( pH->ht ){ +- pEntry = &pH->ht[h]; ++ pEntry = &pH->ht[elem->h % pH->htsize]; + if( pEntry->chain==elem ){ + pEntry->chain = elem->next; + } +@@ -33128,7 +37627,7 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ + if( elem->data ){ + void *old_data = elem->data; + if( data==0 ){ +- removeElementGivenHash(pH,elem,h); ++ removeElement(pH,elem); + }else{ + elem->data = data; + elem->pKey = pKey; +@@ -33139,15 +37638,13 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ + new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) ); + if( new_elem==0 ) return data; + new_elem->pKey = pKey; ++ new_elem->h = h; + new_elem->data = data; + pH->count++; +- if( pH->count>=10 && pH->count > 2*pH->htsize ){ +- if( rehash(pH, pH->count*2) ){ +- assert( pH->htsize>0 ); +- h = strHash(pKey) % pH->htsize; +- } ++ if( pH->count>=5 && pH->count > 2*pH->htsize ){ ++ rehash(pH, pH->count*3); + } +- insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem); ++ insertElement(pH, pH->ht ? &pH->ht[new_elem->h % pH->htsize] : 0, new_elem); + return 0; + } + +@@ -33168,185 +37665,1181 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ + /* 0 */ "Savepoint" OpHelp(""), + /* 1 */ "AutoCommit" OpHelp(""), + /* 2 */ "Transaction" OpHelp(""), +- /* 3 */ "SorterNext" OpHelp(""), +- /* 4 */ "Prev" OpHelp(""), +- /* 5 */ "Next" OpHelp(""), +- /* 6 */ "Checkpoint" OpHelp(""), +- /* 7 */ "JournalMode" OpHelp(""), +- /* 8 */ "Vacuum" OpHelp(""), +- /* 9 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), +- /* 10 */ "VUpdate" OpHelp("data=r[P3@P2]"), +- /* 11 */ "Goto" OpHelp(""), +- /* 12 */ "Gosub" OpHelp(""), +- /* 13 */ "InitCoroutine" OpHelp(""), +- /* 14 */ "Yield" OpHelp(""), +- /* 15 */ "MustBeInt" OpHelp(""), +- /* 16 */ "Jump" OpHelp(""), +- /* 17 */ "Once" OpHelp(""), +- /* 18 */ "If" OpHelp(""), ++ /* 3 */ "Checkpoint" OpHelp(""), ++ /* 4 */ "JournalMode" OpHelp(""), ++ /* 5 */ "Vacuum" OpHelp(""), ++ /* 6 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), ++ /* 7 */ "VUpdate" OpHelp("data=r[P3@P2]"), ++ /* 8 */ "Init" OpHelp("Start at P2"), ++ /* 9 */ "Goto" OpHelp(""), ++ /* 10 */ "Gosub" OpHelp(""), ++ /* 11 */ "InitCoroutine" OpHelp(""), ++ /* 12 */ "Yield" OpHelp(""), ++ /* 13 */ "MustBeInt" OpHelp(""), ++ /* 14 */ "Jump" OpHelp(""), ++ /* 15 */ "Once" OpHelp(""), ++ /* 16 */ "If" OpHelp(""), ++ /* 17 */ "IfNot" OpHelp(""), ++ /* 18 */ "IsType" OpHelp("if typeof(P1.P3) in P5 goto P2"), + /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), +- /* 20 */ "IfNot" OpHelp(""), +- /* 21 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), +- /* 22 */ "SeekLT" OpHelp("key=r[P3@P4]"), +- /* 23 */ "SeekLE" OpHelp("key=r[P3@P4]"), +- /* 24 */ "SeekGE" OpHelp("key=r[P3@P4]"), +- /* 25 */ "SeekGT" OpHelp("key=r[P3@P4]"), +- /* 26 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), +- /* 27 */ "IfNoHope" OpHelp("key=r[P3@P4]"), +- /* 28 */ "NoConflict" OpHelp("key=r[P3@P4]"), +- /* 29 */ "NotFound" OpHelp("key=r[P3@P4]"), +- /* 30 */ "Found" OpHelp("key=r[P3@P4]"), +- /* 31 */ "SeekRowid" OpHelp("intkey=r[P3]"), +- /* 32 */ "NotExists" OpHelp("intkey=r[P3]"), +- /* 33 */ "Last" OpHelp(""), +- /* 34 */ "IfSmaller" OpHelp(""), +- /* 35 */ "SorterSort" OpHelp(""), +- /* 36 */ "Sort" OpHelp(""), +- /* 37 */ "Rewind" OpHelp(""), +- /* 38 */ "IdxLE" OpHelp("key=r[P3@P4]"), +- /* 39 */ "IdxGT" OpHelp("key=r[P3@P4]"), +- /* 40 */ "IdxLT" OpHelp("key=r[P3@P4]"), +- /* 41 */ "IdxGE" OpHelp("key=r[P3@P4]"), +- /* 42 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), ++ /* 20 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), ++ /* 21 */ "SeekLT" OpHelp("key=r[P3@P4]"), ++ /* 22 */ "SeekLE" OpHelp("key=r[P3@P4]"), ++ /* 23 */ "SeekGE" OpHelp("key=r[P3@P4]"), ++ /* 24 */ "SeekGT" OpHelp("key=r[P3@P4]"), ++ /* 25 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), ++ /* 26 */ "IfNoHope" OpHelp("key=r[P3@P4]"), ++ /* 27 */ "NoConflict" OpHelp("key=r[P3@P4]"), ++ /* 28 */ "NotFound" OpHelp("key=r[P3@P4]"), ++ /* 29 */ "Found" OpHelp("key=r[P3@P4]"), ++ /* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"), ++ /* 31 */ "NotExists" OpHelp("intkey=r[P3]"), ++ /* 32 */ "Last" OpHelp(""), ++ /* 33 */ "IfSizeBetween" OpHelp(""), ++ /* 34 */ "SorterSort" OpHelp(""), ++ /* 35 */ "Sort" OpHelp(""), ++ /* 36 */ "Rewind" OpHelp(""), ++ /* 37 */ "SorterNext" OpHelp(""), ++ /* 38 */ "Prev" OpHelp(""), ++ /* 39 */ "Next" OpHelp(""), ++ /* 40 */ "IdxLE" OpHelp("key=r[P3@P4]"), ++ /* 41 */ "IdxGT" OpHelp("key=r[P3@P4]"), ++ /* 42 */ "IdxLT" OpHelp("key=r[P3@P4]"), + /* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), + /* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), +- /* 45 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), +- /* 46 */ "Program" OpHelp(""), +- /* 47 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), +- /* 48 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), +- /* 49 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), +- /* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), +- /* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), +- /* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), +- /* 53 */ "Eq" OpHelp("IF r[P3]==r[P1]"), +- /* 54 */ "Gt" OpHelp("IF r[P3]>r[P1]"), +- /* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"), +- /* 56 */ "Lt" OpHelp("IF r[P3]=r[P1]"), +- /* 58 */ "ElseNotEq" OpHelp(""), +- /* 59 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), +- /* 60 */ "IncrVacuum" OpHelp(""), +- /* 61 */ "VNext" OpHelp(""), +- /* 62 */ "Init" OpHelp("Start at P2"), +- /* 63 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), +- /* 64 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), +- /* 65 */ "Return" OpHelp(""), +- /* 66 */ "EndCoroutine" OpHelp(""), +- /* 67 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), +- /* 68 */ "Halt" OpHelp(""), +- /* 69 */ "Integer" OpHelp("r[P2]=P1"), +- /* 70 */ "Int64" OpHelp("r[P2]=P4"), +- /* 71 */ "String" OpHelp("r[P2]='P4' (len=P1)"), +- /* 72 */ "Null" OpHelp("r[P2..P3]=NULL"), +- /* 73 */ "SoftNull" OpHelp("r[P1]=NULL"), +- /* 74 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), +- /* 75 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), +- /* 76 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), +- /* 77 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), +- /* 78 */ "SCopy" OpHelp("r[P2]=r[P1]"), +- /* 79 */ "IntCopy" OpHelp("r[P2]=r[P1]"), +- /* 80 */ "ResultRow" OpHelp("output=r[P1@P2]"), +- /* 81 */ "CollSeq" OpHelp(""), +- /* 82 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), +- /* 83 */ "RealAffinity" OpHelp(""), +- /* 84 */ "Cast" OpHelp("affinity(r[P1])"), +- /* 85 */ "Permutation" OpHelp(""), +- /* 86 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), +- /* 87 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), +- /* 88 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), +- /* 89 */ "Column" OpHelp("r[P3]=PX"), +- /* 90 */ "Affinity" OpHelp("affinity(r[P1@P2])"), +- /* 91 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), +- /* 92 */ "Count" OpHelp("r[P2]=count()"), +- /* 93 */ "ReadCookie" OpHelp(""), +- /* 94 */ "SetCookie" OpHelp(""), +- /* 95 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), +- /* 96 */ "OpenRead" OpHelp("root=P2 iDb=P3"), +- /* 97 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), +- /* 98 */ "OpenDup" OpHelp(""), +- /* 99 */ "OpenAutoindex" OpHelp("nColumn=P2"), +- /* 100 */ "OpenEphemeral" OpHelp("nColumn=P2"), +- /* 101 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), +- /* 102 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), +- /* 103 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), +- /* 105 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), +- /* 106 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), +- /* 107 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), +- /* 108 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), +- /* 109 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), +- /* 110 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), +- /* 111 */ "SorterOpen" OpHelp(""), +- /* 112 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), +- /* 113 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), +- /* 114 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), +- /* 115 */ "String8" OpHelp("r[P2]='P4'"), +- /* 116 */ "Close" OpHelp(""), +- /* 117 */ "ColumnsUsed" OpHelp(""), +- /* 118 */ "SeekHit" OpHelp("seekHit=P2"), +- /* 119 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), +- /* 120 */ "NewRowid" OpHelp("r[P2]=rowid"), +- /* 121 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), +- /* 122 */ "Delete" OpHelp(""), +- /* 123 */ "ResetCount" OpHelp(""), +- /* 124 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), +- /* 125 */ "SorterData" OpHelp("r[P2]=data"), +- /* 126 */ "RowData" OpHelp("r[P2]=data"), +- /* 127 */ "Rowid" OpHelp("r[P2]=rowid"), +- /* 128 */ "NullRow" OpHelp(""), +- /* 129 */ "SeekEnd" OpHelp(""), +- /* 130 */ "IdxInsert" OpHelp("key=r[P2]"), +- /* 131 */ "SorterInsert" OpHelp("key=r[P2]"), +- /* 132 */ "IdxDelete" OpHelp("key=r[P2@P3]"), +- /* 133 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), +- /* 134 */ "IdxRowid" OpHelp("r[P2]=rowid"), +- /* 135 */ "FinishSeek" OpHelp(""), +- /* 136 */ "Destroy" OpHelp(""), +- /* 137 */ "Clear" OpHelp(""), +- /* 138 */ "ResetSorter" OpHelp(""), +- /* 139 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), +- /* 140 */ "SqlExec" OpHelp(""), +- /* 141 */ "ParseSchema" OpHelp(""), +- /* 142 */ "LoadAnalysis" OpHelp(""), +- /* 143 */ "DropTable" OpHelp(""), +- /* 144 */ "DropIndex" OpHelp(""), +- /* 145 */ "DropTrigger" OpHelp(""), +- /* 146 */ "IntegrityCk" OpHelp(""), +- /* 147 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), +- /* 148 */ "Param" OpHelp(""), +- /* 149 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), +- /* 150 */ "Real" OpHelp("r[P2]=P4"), +- /* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), +- /* 152 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), +- /* 153 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), +- /* 154 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), +- /* 155 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), +- /* 156 */ "AggValue" OpHelp("r[P3]=value N=P2"), +- /* 157 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), +- /* 158 */ "Expire" OpHelp(""), +- /* 159 */ "CursorLock" OpHelp(""), +- /* 160 */ "CursorUnlock" OpHelp(""), +- /* 161 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), +- /* 162 */ "VBegin" OpHelp(""), +- /* 163 */ "VCreate" OpHelp(""), +- /* 164 */ "VDestroy" OpHelp(""), +- /* 165 */ "VOpen" OpHelp(""), +- /* 166 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), +- /* 167 */ "VRename" OpHelp(""), +- /* 168 */ "Pagecount" OpHelp(""), +- /* 169 */ "MaxPgcnt" OpHelp(""), +- /* 170 */ "Trace" OpHelp(""), +- /* 171 */ "CursorHint" OpHelp(""), +- /* 172 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), +- /* 173 */ "Noop" OpHelp(""), +- /* 174 */ "Explain" OpHelp(""), +- /* 175 */ "Abortable" OpHelp(""), ++ /* 45 */ "IdxGE" OpHelp("key=r[P3@P4]"), ++ /* 46 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), ++ /* 47 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), ++ /* 48 */ "Program" OpHelp(""), ++ /* 49 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), ++ /* 50 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), ++ /* 51 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), ++ /* 52 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), ++ /* 53 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), ++ /* 54 */ "Eq" OpHelp("IF r[P3]==r[P1]"), ++ /* 55 */ "Gt" OpHelp("IF r[P3]>r[P1]"), ++ /* 56 */ "Le" OpHelp("IF r[P3]<=r[P1]"), ++ /* 57 */ "Lt" OpHelp("IF r[P3]=r[P1]"), ++ /* 59 */ "ElseEq" OpHelp(""), ++ /* 60 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), ++ /* 61 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), ++ /* 62 */ "IncrVacuum" OpHelp(""), ++ /* 63 */ "VNext" OpHelp(""), ++ /* 64 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"), ++ /* 65 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), ++ /* 66 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), ++ /* 67 */ "Return" OpHelp(""), ++ /* 68 */ "EndCoroutine" OpHelp(""), ++ /* 69 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), ++ /* 70 */ "Halt" OpHelp(""), ++ /* 71 */ "Integer" OpHelp("r[P2]=P1"), ++ /* 72 */ "Int64" OpHelp("r[P2]=P4"), ++ /* 73 */ "String" OpHelp("r[P2]='P4' (len=P1)"), ++ /* 74 */ "BeginSubrtn" OpHelp("r[P2]=NULL"), ++ /* 75 */ "Null" OpHelp("r[P2..P3]=NULL"), ++ /* 76 */ "SoftNull" OpHelp("r[P1]=NULL"), ++ /* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), ++ /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1)"), ++ /* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), ++ /* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), ++ /* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"), ++ /* 82 */ "IntCopy" OpHelp("r[P2]=r[P1]"), ++ /* 83 */ "FkCheck" OpHelp(""), ++ /* 84 */ "ResultRow" OpHelp("output=r[P1@P2]"), ++ /* 85 */ "CollSeq" OpHelp(""), ++ /* 86 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), ++ /* 87 */ "RealAffinity" OpHelp(""), ++ /* 88 */ "Cast" OpHelp("affinity(r[P1])"), ++ /* 89 */ "Permutation" OpHelp(""), ++ /* 90 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), ++ /* 91 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), ++ /* 92 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"), ++ /* 93 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), ++ /* 94 */ "Column" OpHelp("r[P3]=PX cursor P1 column P2"), ++ /* 95 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"), ++ /* 96 */ "Affinity" OpHelp("affinity(r[P1@P2])"), ++ /* 97 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), ++ /* 98 */ "Count" OpHelp("r[P2]=count()"), ++ /* 99 */ "ReadCookie" OpHelp(""), ++ /* 100 */ "SetCookie" OpHelp(""), ++ /* 101 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), ++ /* 102 */ "OpenRead" OpHelp("root=P2 iDb=P3"), ++ /* 103 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), ++ /* 104 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), ++ /* 105 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), ++ /* 107 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), ++ /* 108 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), ++ /* 109 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), ++ /* 110 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), ++ /* 111 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), ++ /* 112 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), ++ /* 113 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), ++ /* 114 */ "OpenDup" OpHelp(""), ++ /* 115 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), ++ /* 116 */ "OpenAutoindex" OpHelp("nColumn=P2"), ++ /* 117 */ "OpenEphemeral" OpHelp("nColumn=P2"), ++ /* 118 */ "String8" OpHelp("r[P2]='P4'"), ++ /* 119 */ "SorterOpen" OpHelp(""), ++ /* 120 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), ++ /* 121 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), ++ /* 122 */ "Close" OpHelp(""), ++ /* 123 */ "ColumnsUsed" OpHelp(""), ++ /* 124 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"), ++ /* 125 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"), ++ /* 126 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), ++ /* 127 */ "NewRowid" OpHelp("r[P2]=rowid"), ++ /* 128 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), ++ /* 129 */ "RowCell" OpHelp(""), ++ /* 130 */ "Delete" OpHelp(""), ++ /* 131 */ "ResetCount" OpHelp(""), ++ /* 132 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), ++ /* 133 */ "SorterData" OpHelp("r[P2]=data"), ++ /* 134 */ "RowData" OpHelp("r[P2]=data"), ++ /* 135 */ "Rowid" OpHelp("r[P2]=PX rowid of P1"), ++ /* 136 */ "NullRow" OpHelp(""), ++ /* 137 */ "SeekEnd" OpHelp(""), ++ /* 138 */ "IdxInsert" OpHelp("key=r[P2]"), ++ /* 139 */ "SorterInsert" OpHelp("key=r[P2]"), ++ /* 140 */ "IdxDelete" OpHelp("key=r[P2@P3]"), ++ /* 141 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), ++ /* 142 */ "IdxRowid" OpHelp("r[P2]=rowid"), ++ /* 143 */ "FinishSeek" OpHelp(""), ++ /* 144 */ "Destroy" OpHelp(""), ++ /* 145 */ "Clear" OpHelp(""), ++ /* 146 */ "ResetSorter" OpHelp(""), ++ /* 147 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), ++ /* 148 */ "SqlExec" OpHelp(""), ++ /* 149 */ "ParseSchema" OpHelp(""), ++ /* 150 */ "LoadAnalysis" OpHelp(""), ++ /* 151 */ "DropTable" OpHelp(""), ++ /* 152 */ "DropIndex" OpHelp(""), ++ /* 153 */ "DropTrigger" OpHelp(""), ++ /* 154 */ "Real" OpHelp("r[P2]=P4"), ++ /* 155 */ "IntegrityCk" OpHelp(""), ++ /* 156 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), ++ /* 157 */ "Param" OpHelp(""), ++ /* 158 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), ++ /* 159 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), ++ /* 160 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), ++ /* 161 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), ++ /* 162 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), ++ /* 163 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), ++ /* 164 */ "AggValue" OpHelp("r[P3]=value N=P2"), ++ /* 165 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), ++ /* 166 */ "Expire" OpHelp(""), ++ /* 167 */ "CursorLock" OpHelp(""), ++ /* 168 */ "CursorUnlock" OpHelp(""), ++ /* 169 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), ++ /* 170 */ "VBegin" OpHelp(""), ++ /* 171 */ "VCreate" OpHelp(""), ++ /* 172 */ "VDestroy" OpHelp(""), ++ /* 173 */ "VOpen" OpHelp(""), ++ /* 174 */ "VCheck" OpHelp(""), ++ /* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), ++ /* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), ++ /* 177 */ "VRename" OpHelp(""), ++ /* 178 */ "Pagecount" OpHelp(""), ++ /* 179 */ "MaxPgcnt" OpHelp(""), ++ /* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), ++ /* 181 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"), ++ /* 182 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"), ++ /* 183 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), ++ /* 184 */ "Trace" OpHelp(""), ++ /* 185 */ "CursorHint" OpHelp(""), ++ /* 186 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), ++ /* 187 */ "Noop" OpHelp(""), ++ /* 188 */ "Explain" OpHelp(""), ++ /* 189 */ "Abortable" OpHelp(""), + }; + return azName[i]; + } + #endif + + /************** End of opcodes.c *********************************************/ ++/************** Begin file os_kv.c *******************************************/ ++/* ++** 2022-09-06 ++** ++** 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 contains an experimental VFS layer that operates on a ++** Key/Value storage engine where both keys and values must be pure ++** text. ++*/ ++/* #include */ ++#if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)) ++ ++/***************************************************************************** ++** Debugging logic ++*/ ++ ++/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */ ++#if 0 ++#define SQLITE_KV_TRACE(X) printf X ++#else ++#define SQLITE_KV_TRACE(X) ++#endif ++ ++/* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */ ++#if 0 ++#define SQLITE_KV_LOG(X) printf X ++#else ++#define SQLITE_KV_LOG(X) ++#endif ++ ++ ++/* ++** Forward declaration of objects used by this VFS implementation ++*/ ++typedef struct KVVfsFile KVVfsFile; ++ ++/* A single open file. There are only two files represented by this ++** VFS - the database and the rollback journal. ++*/ ++struct KVVfsFile { ++ sqlite3_file base; /* IO methods */ ++ const char *zClass; /* Storage class */ ++ int isJournal; /* True if this is a journal file */ ++ unsigned int nJrnl; /* Space allocated for aJrnl[] */ ++ char *aJrnl; /* Journal content */ ++ int szPage; /* Last known page size */ ++ sqlite3_int64 szDb; /* Database file size. -1 means unknown */ ++ char *aData; /* Buffer to hold page data */ ++}; ++#define SQLITE_KVOS_SZ 133073 ++ ++/* ++** Methods for KVVfsFile ++*/ ++static int kvvfsClose(sqlite3_file*); ++static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); ++static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); ++static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64); ++static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64); ++static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size); ++static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size); ++static int kvvfsSyncDb(sqlite3_file*, int flags); ++static int kvvfsSyncJrnl(sqlite3_file*, int flags); ++static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize); ++static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize); ++static int kvvfsLock(sqlite3_file*, int); ++static int kvvfsUnlock(sqlite3_file*, int); ++static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut); ++static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg); ++static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg); ++static int kvvfsSectorSize(sqlite3_file*); ++static int kvvfsDeviceCharacteristics(sqlite3_file*); ++ ++/* ++** Methods for sqlite3_vfs ++*/ ++static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); ++static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); ++static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); ++static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); ++static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename); ++static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); ++static int kvvfsSleep(sqlite3_vfs*, int microseconds); ++static int kvvfsCurrentTime(sqlite3_vfs*, double*); ++static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); ++ ++static sqlite3_vfs sqlite3OsKvvfsObject = { ++ 1, /* iVersion */ ++ sizeof(KVVfsFile), /* szOsFile */ ++ 1024, /* mxPathname */ ++ 0, /* pNext */ ++ "kvvfs", /* zName */ ++ 0, /* pAppData */ ++ kvvfsOpen, /* xOpen */ ++ kvvfsDelete, /* xDelete */ ++ kvvfsAccess, /* xAccess */ ++ kvvfsFullPathname, /* xFullPathname */ ++ kvvfsDlOpen, /* xDlOpen */ ++ 0, /* xDlError */ ++ 0, /* xDlSym */ ++ 0, /* xDlClose */ ++ kvvfsRandomness, /* xRandomness */ ++ kvvfsSleep, /* xSleep */ ++ kvvfsCurrentTime, /* xCurrentTime */ ++ 0, /* xGetLastError */ ++ kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ ++}; ++ ++/* Methods for sqlite3_file objects referencing a database file ++*/ ++static sqlite3_io_methods kvvfs_db_io_methods = { ++ 1, /* iVersion */ ++ kvvfsClose, /* xClose */ ++ kvvfsReadDb, /* xRead */ ++ kvvfsWriteDb, /* xWrite */ ++ kvvfsTruncateDb, /* xTruncate */ ++ kvvfsSyncDb, /* xSync */ ++ kvvfsFileSizeDb, /* xFileSize */ ++ kvvfsLock, /* xLock */ ++ kvvfsUnlock, /* xUnlock */ ++ kvvfsCheckReservedLock, /* xCheckReservedLock */ ++ kvvfsFileControlDb, /* xFileControl */ ++ kvvfsSectorSize, /* xSectorSize */ ++ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ ++ 0, /* xShmMap */ ++ 0, /* xShmLock */ ++ 0, /* xShmBarrier */ ++ 0, /* xShmUnmap */ ++ 0, /* xFetch */ ++ 0 /* xUnfetch */ ++}; ++ ++/* Methods for sqlite3_file objects referencing a rollback journal ++*/ ++static sqlite3_io_methods kvvfs_jrnl_io_methods = { ++ 1, /* iVersion */ ++ kvvfsClose, /* xClose */ ++ kvvfsReadJrnl, /* xRead */ ++ kvvfsWriteJrnl, /* xWrite */ ++ kvvfsTruncateJrnl, /* xTruncate */ ++ kvvfsSyncJrnl, /* xSync */ ++ kvvfsFileSizeJrnl, /* xFileSize */ ++ kvvfsLock, /* xLock */ ++ kvvfsUnlock, /* xUnlock */ ++ kvvfsCheckReservedLock, /* xCheckReservedLock */ ++ kvvfsFileControlJrnl, /* xFileControl */ ++ kvvfsSectorSize, /* xSectorSize */ ++ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ ++ 0, /* xShmMap */ ++ 0, /* xShmLock */ ++ 0, /* xShmBarrier */ ++ 0, /* xShmUnmap */ ++ 0, /* xFetch */ ++ 0 /* xUnfetch */ ++}; ++ ++/****** Storage subsystem **************************************************/ ++#include ++#include ++#include ++ ++/* Forward declarations for the low-level storage engine ++*/ ++static int kvstorageWrite(const char*, const char *zKey, const char *zData); ++static int kvstorageDelete(const char*, const char *zKey); ++static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); ++#define KVSTORAGE_KEY_SZ 32 ++ ++/* Expand the key name with an appropriate prefix and put the result ++** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least ++** KVSTORAGE_KEY_SZ bytes. ++*/ ++static void kvstorageMakeKey( ++ const char *zClass, ++ const char *zKeyIn, ++ char *zKeyOut ++){ ++ sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); ++} ++ ++/* Write content into a key. zClass is the particular namespace of the ++** underlying key/value store to use - either "local" or "session". ++** ++** Both zKey and zData are zero-terminated pure text strings. ++** ++** Return the number of errors. ++*/ ++static int kvstorageWrite( ++ const char *zClass, ++ const char *zKey, ++ const char *zData ++){ ++ FILE *fd; ++ char zXKey[KVSTORAGE_KEY_SZ]; ++ kvstorageMakeKey(zClass, zKey, zXKey); ++ fd = fopen(zXKey, "wb"); ++ if( fd ){ ++ SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, ++ (int)strlen(zData), zData, ++ strlen(zData)>50 ? "..." : "")); ++ fputs(zData, fd); ++ fclose(fd); ++ return 0; ++ }else{ ++ return 1; ++ } ++} ++ ++/* Delete a key (with its corresponding data) from the key/value ++** namespace given by zClass. If the key does not previously exist, ++** this routine is a no-op. ++*/ ++static int kvstorageDelete(const char *zClass, const char *zKey){ ++ char zXKey[KVSTORAGE_KEY_SZ]; ++ kvstorageMakeKey(zClass, zKey, zXKey); ++ unlink(zXKey); ++ SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); ++ return 0; ++} ++ ++/* Read the value associated with a zKey from the key/value namespace given ++** by zClass and put the text data associated with that key in the first ++** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large ++** enough to hold it all. The value put into zBuf must always be zero ++** terminated, even if it gets truncated because nBuf is not large enough. ++** ++** Return the total number of bytes in the data, without truncation, and ++** not counting the final zero terminator. Return -1 if the key does ++** not exist. ++** ++** If nBuf<=0 then this routine simply returns the size of the data without ++** actually reading it. ++*/ ++static int kvstorageRead( ++ const char *zClass, ++ const char *zKey, ++ char *zBuf, ++ int nBuf ++){ ++ FILE *fd; ++ struct stat buf; ++ char zXKey[KVSTORAGE_KEY_SZ]; ++ kvstorageMakeKey(zClass, zKey, zXKey); ++ if( access(zXKey, R_OK)!=0 ++ || stat(zXKey, &buf)!=0 ++ || !S_ISREG(buf.st_mode) ++ ){ ++ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); ++ return -1; ++ } ++ if( nBuf<=0 ){ ++ return (int)buf.st_size; ++ }else if( nBuf==1 ){ ++ zBuf[0] = 0; ++ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey, ++ (int)buf.st_size)); ++ return (int)buf.st_size; ++ } ++ if( nBuf > buf.st_size + 1 ){ ++ nBuf = buf.st_size + 1; ++ } ++ fd = fopen(zXKey, "rb"); ++ if( fd==0 ){ ++ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); ++ return -1; ++ }else{ ++ sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd); ++ fclose(fd); ++ zBuf[n] = 0; ++ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey, ++ n, zBuf, n>50 ? "..." : "")); ++ return (int)n; ++ } ++} ++ ++/* ++** An internal level of indirection which enables us to replace the ++** kvvfs i/o methods with JavaScript implementations in WASM builds. ++** Maintenance reminder: if this struct changes in any way, the JSON ++** rendering of its structure must be updated in ++** sqlite3_wasm_enum_json(). There are no binary compatibility ++** concerns, so it does not need an iVersion member. This file is ++** necessarily always compiled together with sqlite3_wasm_enum_json(), ++** and JS code dynamically creates the mapping of members based on ++** that JSON description. ++*/ ++typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods; ++struct sqlite3_kvvfs_methods { ++ int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf); ++ int (*xWrite)(const char *zClass, const char *zKey, const char *zData); ++ int (*xDelete)(const char *zClass, const char *zKey); ++ const int nKeySize; ++}; ++ ++/* ++** This object holds the kvvfs I/O methods which may be swapped out ++** for JavaScript-side implementations in WASM builds. In such builds ++** it cannot be const, but in native builds it should be so that ++** the compiler can hopefully optimize this level of indirection out. ++** That said, kvvfs is intended primarily for use in WASM builds. ++** ++** Note that this is not explicitly flagged as static because the ++** amalgamation build will tag it with SQLITE_PRIVATE. ++*/ ++#ifndef SQLITE_WASM ++const ++#endif ++SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = { ++kvstorageRead, ++kvstorageWrite, ++kvstorageDelete, ++KVSTORAGE_KEY_SZ ++}; ++ ++/****** Utility subroutines ************************************************/ ++ ++/* ++** Encode binary into the text encoded used to persist on disk. ++** The output text is stored in aOut[], which must be at least ++** nData+1 bytes in length. ++** ++** Return the actual length of the encoded text, not counting the ++** zero terminator at the end. ++** ++** Encoding format ++** --------------- ++** ++** * Non-zero bytes are encoded as upper-case hexadecimal ++** ++** * A sequence of one or more zero-bytes that are not at the ++** beginning of the buffer are encoded as a little-endian ++** base-26 number using a..z. "a" means 0. "b" means 1, ++** "z" means 25. "ab" means 26. "ac" means 52. And so forth. ++** ++** * Because there is no overlap between the encoding characters ++** of hexadecimal and base-26 numbers, it is always clear where ++** one stops and the next begins. ++*/ ++static int kvvfsEncode(const char *aData, int nData, char *aOut){ ++ int i, j; ++ const unsigned char *a = (const unsigned char*)aData; ++ for(i=j=0; i>4]; ++ aOut[j++] = "0123456789ABCDEF"[c&0xf]; ++ }else{ ++ /* A sequence of 1 or more zeros is stored as a little-endian ++ ** base-26 number using a..z as the digits. So one zero is "b". ++ ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", ++ ** and so forth. ++ */ ++ int k; ++ for(k=1; i+k0 ){ ++ aOut[j++] = 'a'+(k%26); ++ k /= 26; ++ } ++ } ++ } ++ aOut[j] = 0; ++ return j; ++} ++ ++static const signed char kvvfsHexValue[256] = { ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, ++ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ++}; ++ ++/* ++** Decode the text encoding back to binary. The binary content is ++** written into pOut, which must be at least nOut bytes in length. ++** ++** The return value is the number of bytes actually written into aOut[]. ++*/ ++static int kvvfsDecode(const char *a, char *aOut, int nOut){ ++ int i, j; ++ int c; ++ const unsigned char *aIn = (const unsigned char*)a; ++ i = 0; ++ j = 0; ++ while( 1 ){ ++ c = kvvfsHexValue[aIn[i]]; ++ if( c<0 ){ ++ int n = 0; ++ int mult = 1; ++ c = aIn[i]; ++ if( c==0 ) break; ++ while( c>='a' && c<='z' ){ ++ n += (c - 'a')*mult; ++ mult *= 26; ++ c = aIn[++i]; ++ } ++ if( j+n>nOut ) return -1; ++ memset(&aOut[j], 0, n); ++ j += n; ++ if( c==0 || mult==1 ) break; /* progress stalled if mult==1 */ ++ }else{ ++ aOut[j] = c<<4; ++ c = kvvfsHexValue[aIn[++i]]; ++ if( c<0 ) break; ++ aOut[j++] += c; ++ i++; ++ } ++ } ++ return j; ++} ++ ++/* ++** Decode a complete journal file. Allocate space in pFile->aJrnl ++** and store the decoding there. Or leave pFile->aJrnl set to NULL ++** if an error is encountered. ++** ++** The first few characters of the text encoding will be a little-endian ++** base-26 number (digits a..z) that is the total number of bytes ++** in the decoded journal file image. This base-26 number is followed ++** by a single space, then the encoding of the journal. The space ++** separator is required to act as a terminator for the base-26 number. ++*/ ++static void kvvfsDecodeJournal( ++ KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ ++ const char *zTxt, /* Text encoding. Zero-terminated */ ++ int nTxt /* Bytes in zTxt, excluding zero terminator */ ++){ ++ unsigned int n = 0; ++ int c, i, mult; ++ i = 0; ++ mult = 1; ++ while( (c = zTxt[i++])>='a' && c<='z' ){ ++ n += (zTxt[i] - 'a')*mult; ++ mult *= 26; ++ } ++ sqlite3_free(pFile->aJrnl); ++ pFile->aJrnl = sqlite3_malloc64( n ); ++ if( pFile->aJrnl==0 ){ ++ pFile->nJrnl = 0; ++ return; ++ } ++ pFile->nJrnl = n; ++ n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); ++ if( nnJrnl ){ ++ sqlite3_free(pFile->aJrnl); ++ pFile->aJrnl = 0; ++ pFile->nJrnl = 0; ++ } ++} ++ ++/* ++** Read or write the "sz" element, containing the database file size. ++*/ ++static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ ++ char zData[50]; ++ zData[0] = 0; ++ sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1); ++ return strtoll(zData, 0, 0); ++} ++static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ ++ char zData[50]; ++ sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); ++ return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData); ++} ++ ++/****** sqlite3_io_methods methods ******************************************/ ++ ++/* ++** Close an kvvfs-file. ++*/ ++static int kvvfsClose(sqlite3_file *pProtoFile){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ ++ SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, ++ pFile->isJournal ? "journal" : "db")); ++ sqlite3_free(pFile->aJrnl); ++ sqlite3_free(pFile->aData); ++ return SQLITE_OK; ++} ++ ++/* ++** Read from the -journal file. ++*/ ++static int kvvfsReadJrnl( ++ sqlite3_file *pProtoFile, ++ void *zBuf, ++ int iAmt, ++ sqlite_int64 iOfst ++){ ++ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; ++ assert( pFile->isJournal ); ++ SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); ++ if( pFile->aJrnl==0 ){ ++ int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); ++ char *aTxt; ++ if( szTxt<=4 ){ ++ return SQLITE_IOERR; ++ } ++ aTxt = sqlite3_malloc64( szTxt+1 ); ++ if( aTxt==0 ) return SQLITE_NOMEM; ++ kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); ++ kvvfsDecodeJournal(pFile, aTxt, szTxt); ++ sqlite3_free(aTxt); ++ if( pFile->aJrnl==0 ) return SQLITE_IOERR; ++ } ++ if( iOfst+iAmt>pFile->nJrnl ){ ++ return SQLITE_IOERR_SHORT_READ; ++ } ++ memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); ++ return SQLITE_OK; ++} ++ ++/* ++** Read from the database file. ++*/ ++static int kvvfsReadDb( ++ sqlite3_file *pProtoFile, ++ void *zBuf, ++ int iAmt, ++ sqlite_int64 iOfst ++){ ++ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; ++ unsigned int pgno; ++ int got, n; ++ char zKey[30]; ++ char *aData = pFile->aData; ++ assert( iOfst>=0 ); ++ assert( iAmt>=0 ); ++ SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); ++ if( iOfst+iAmt>=512 ){ ++ if( (iOfst % iAmt)!=0 ){ ++ return SQLITE_IOERR_READ; ++ } ++ if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ ++ return SQLITE_IOERR_READ; ++ } ++ pFile->szPage = iAmt; ++ pgno = 1 + iOfst/iAmt; ++ }else{ ++ pgno = 1; ++ } ++ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); ++ got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, ++ aData, SQLITE_KVOS_SZ-1); ++ if( got<0 ){ ++ n = 0; ++ }else{ ++ aData[got] = 0; ++ if( iOfst+iAmt<512 ){ ++ int k = iOfst+iAmt; ++ aData[k*2] = 0; ++ n = kvvfsDecode(aData, &aData[2000], SQLITE_KVOS_SZ-2000); ++ if( n>=iOfst+iAmt ){ ++ memcpy(zBuf, &aData[2000+iOfst], iAmt); ++ n = iAmt; ++ }else{ ++ n = 0; ++ } ++ }else{ ++ n = kvvfsDecode(aData, zBuf, iAmt); ++ } ++ } ++ if( nzClass, iAmt, iOfst)); ++ if( iEnd>=0x10000000 ) return SQLITE_FULL; ++ if( pFile->aJrnl==0 || pFile->nJrnlaJrnl, iEnd); ++ if( aNew==0 ){ ++ return SQLITE_IOERR_NOMEM; ++ } ++ pFile->aJrnl = aNew; ++ if( pFile->nJrnlaJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); ++ } ++ pFile->nJrnl = iEnd; ++ } ++ memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); ++ return SQLITE_OK; ++} ++ ++/* ++** Write into the database file. ++*/ ++static int kvvfsWriteDb( ++ sqlite3_file *pProtoFile, ++ const void *zBuf, ++ int iAmt, ++ sqlite_int64 iOfst ++){ ++ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; ++ unsigned int pgno; ++ char zKey[30]; ++ char *aData = pFile->aData; ++ SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); ++ assert( iAmt>=512 && iAmt<=65536 ); ++ assert( (iAmt & (iAmt-1))==0 ); ++ assert( pFile->szPage<0 || pFile->szPage==iAmt ); ++ pFile->szPage = iAmt; ++ pgno = 1 + iOfst/iAmt; ++ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); ++ kvvfsEncode(zBuf, iAmt, aData); ++ if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){ ++ return SQLITE_IOERR; ++ } ++ if( iOfst+iAmt > pFile->szDb ){ ++ pFile->szDb = iOfst + iAmt; ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Truncate an kvvfs-file. ++*/ ++static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); ++ assert( size==0 ); ++ sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl"); ++ sqlite3_free(pFile->aJrnl); ++ pFile->aJrnl = 0; ++ pFile->nJrnl = 0; ++ return SQLITE_OK; ++} ++static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ if( pFile->szDb>size ++ && pFile->szPage>0 ++ && (size % pFile->szPage)==0 ++ ){ ++ char zKey[50]; ++ unsigned int pgno, pgnoMax; ++ SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); ++ pgno = 1 + size/pFile->szPage; ++ pgnoMax = 2 + pFile->szDb/pFile->szPage; ++ while( pgno<=pgnoMax ){ ++ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); ++ sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey); ++ pgno++; ++ } ++ pFile->szDb = size; ++ return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; ++ } ++ return SQLITE_IOERR; ++} ++ ++/* ++** Sync an kvvfs-file. ++*/ ++static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ ++ int i, n; ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ char *zOut; ++ SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); ++ if( pFile->nJrnl<=0 ){ ++ return kvvfsTruncateJrnl(pProtoFile, 0); ++ } ++ zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); ++ if( zOut==0 ){ ++ return SQLITE_IOERR_NOMEM; ++ } ++ n = pFile->nJrnl; ++ i = 0; ++ do{ ++ zOut[i++] = 'a' + (n%26); ++ n /= 26; ++ }while( n>0 ); ++ zOut[i++] = ' '; ++ kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); ++ i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut); ++ sqlite3_free(zOut); ++ return i ? SQLITE_IOERR : SQLITE_OK; ++} ++static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ ++ return SQLITE_OK; ++} ++ ++/* ++** Return the current file-size of an kvvfs-file. ++*/ ++static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); ++ *pSize = pFile->nJrnl; ++ return SQLITE_OK; ++} ++static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); ++ if( pFile->szDb>=0 ){ ++ *pSize = pFile->szDb; ++ }else{ ++ *pSize = kvvfsReadFileSize(pFile); ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Lock an kvvfs-file. ++*/ ++static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ assert( !pFile->isJournal ); ++ SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); ++ ++ if( eLock!=SQLITE_LOCK_NONE ){ ++ pFile->szDb = kvvfsReadFileSize(pFile); ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Unlock an kvvfs-file. ++*/ ++static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ assert( !pFile->isJournal ); ++ SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); ++ if( eLock==SQLITE_LOCK_NONE ){ ++ pFile->szDb = -1; ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Check if another file-handle holds a RESERVED lock on an kvvfs-file. ++*/ ++static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ ++ SQLITE_KV_LOG(("xCheckReservedLock\n")); ++ *pResOut = 0; ++ return SQLITE_OK; ++} ++ ++/* ++** File control method. For custom operations on an kvvfs-file. ++*/ ++static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){ ++ SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op)); ++ return SQLITE_NOTFOUND; ++} ++static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){ ++ SQLITE_KV_LOG(("xFileControl(%d) on database\n", op)); ++ if( op==SQLITE_FCNTL_SYNC ){ ++ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; ++ int rc = SQLITE_OK; ++ SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); ++ if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ ++ rc = SQLITE_IOERR; ++ } ++ return rc; ++ } ++ return SQLITE_NOTFOUND; ++} ++ ++/* ++** Return the sector-size in bytes for an kvvfs-file. ++*/ ++static int kvvfsSectorSize(sqlite3_file *pFile){ ++ return 512; ++} ++ ++/* ++** Return the device characteristic flags supported by an kvvfs-file. ++*/ ++static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ ++ return 0; ++} ++ ++/****** sqlite3_vfs methods *************************************************/ ++ ++/* ++** Open an kvvfs file handle. ++*/ ++static int kvvfsOpen( ++ sqlite3_vfs *pProtoVfs, ++ const char *zName, ++ sqlite3_file *pProtoFile, ++ int flags, ++ int *pOutFlags ++){ ++ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; ++ if( zName==0 ) zName = ""; ++ SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); ++ if( strcmp(zName, "local")==0 ++ || strcmp(zName, "session")==0 ++ ){ ++ pFile->isJournal = 0; ++ pFile->base.pMethods = &kvvfs_db_io_methods; ++ }else ++ if( strcmp(zName, "local-journal")==0 ++ || strcmp(zName, "session-journal")==0 ++ ){ ++ pFile->isJournal = 1; ++ pFile->base.pMethods = &kvvfs_jrnl_io_methods; ++ }else{ ++ return SQLITE_CANTOPEN; ++ } ++ if( zName[0]=='s' ){ ++ pFile->zClass = "session"; ++ }else{ ++ pFile->zClass = "local"; ++ } ++ pFile->aData = sqlite3_malloc64(SQLITE_KVOS_SZ); ++ if( pFile->aData==0 ){ ++ return SQLITE_NOMEM; ++ } ++ pFile->aJrnl = 0; ++ pFile->nJrnl = 0; ++ pFile->szPage = -1; ++ pFile->szDb = -1; ++ return SQLITE_OK; ++} ++ ++/* ++** Delete the file located at zPath. If the dirSync argument is true, ++** ensure the file-system modifications are synced to disk before ++** returning. ++*/ ++static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ ++ if( strcmp(zPath, "local-journal")==0 ){ ++ sqlite3KvvfsMethods.xDelete("local", "jrnl"); ++ }else ++ if( strcmp(zPath, "session-journal")==0 ){ ++ sqlite3KvvfsMethods.xDelete("session", "jrnl"); ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Test for access permissions. Return true if the requested permission ++** is available, or false otherwise. ++*/ ++static int kvvfsAccess( ++ sqlite3_vfs *pProtoVfs, ++ const char *zPath, ++ int flags, ++ int *pResOut ++){ ++ SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); ++ if( strcmp(zPath, "local-journal")==0 ){ ++ *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0; ++ }else ++ if( strcmp(zPath, "session-journal")==0 ){ ++ *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0; ++ }else ++ if( strcmp(zPath, "local")==0 ){ ++ *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0; ++ }else ++ if( strcmp(zPath, "session")==0 ){ ++ *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0; ++ }else ++ { ++ *pResOut = 0; ++ } ++ SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); ++ return SQLITE_OK; ++} ++ ++/* ++** Populate buffer zOut with the full canonical pathname corresponding ++** to the pathname in zPath. zOut is guaranteed to point to a buffer ++** of at least (INST_MAX_PATHNAME+1) bytes. ++*/ ++static int kvvfsFullPathname( ++ sqlite3_vfs *pVfs, ++ const char *zPath, ++ int nOut, ++ char *zOut ++){ ++ size_t nPath; ++#ifdef SQLITE_OS_KV_ALWAYS_LOCAL ++ zPath = "local"; ++#endif ++ nPath = strlen(zPath); ++ SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); ++ if( nOut ++static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ ++ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; ++ struct timeval sNow; ++ (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ ++ *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; ++ return SQLITE_OK; ++} ++#endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */ ++ ++#if SQLITE_OS_KV ++/* ++** This routine is called initialize the KV-vfs as the default VFS. ++*/ ++SQLITE_API int sqlite3_os_init(void){ ++ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1); ++} ++SQLITE_API int sqlite3_os_end(void){ ++ return SQLITE_OK; ++} ++#endif /* SQLITE_OS_KV */ ++ ++#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) ++SQLITE_PRIVATE int sqlite3KvvfsInit(void){ ++ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0); ++} ++#endif ++ ++/************** End of os_kv.c ***********************************************/ + /************** Begin file os_unix.c *****************************************/ + /* + ** 2004 May 22 +@@ -33372,7 +38865,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ + ** This source file is organized into divisions where the logic for various + ** subfunctions is contained within the appropriate division. PLEASE + ** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed +-** in the correct division and should be clearly labeled. ++** in the correct division and should be clearly labelled. + ** + ** The layout of divisions is as follows: + ** +@@ -33422,7 +38915,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ + #endif + + /* Use pread() and pwrite() if they are available */ +-#if defined(__APPLE__) ++#if defined(__APPLE__) || defined(__linux__) + # define HAVE_PREAD 1 + # define HAVE_PWRITE 1 + #endif +@@ -33437,15 +38930,16 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ + /* + ** standard include files. + */ +-#include +-#include ++#include /* amalgamator: keep */ ++#include /* amalgamator: keep */ + #include + #include +-#include ++#include /* amalgamator: keep */ + /* #include */ +-#include ++#include /* amalgamator: keep */ + #include +-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 ++#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ ++ && !defined(SQLITE_WASI) + # include + #endif + +@@ -33472,7 +38966,8 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ + # if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ + (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) + # if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \ +- && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0)) ++ && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))\ ++ && (!defined(TARGET_OS_MACCATALYST) || (TARGET_OS_MACCATALYST==0)) + # undef HAVE_GETHOSTUUID + # define HAVE_GETHOSTUUID 1 + # else +@@ -33532,9 +39027,46 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ + */ + #define SQLITE_MAX_SYMLINKS 100 + ++/* ++** Remove and stub certain info for WASI (WebAssembly System ++** Interface) builds. ++*/ ++#ifdef SQLITE_WASI ++# undef HAVE_FCHMOD ++# undef HAVE_FCHOWN ++# undef HAVE_MREMAP ++# define HAVE_MREMAP 0 ++# ifndef SQLITE_DEFAULT_UNIX_VFS ++# define SQLITE_DEFAULT_UNIX_VFS "unix-dotfile" ++ /* ^^^ should SQLITE_DEFAULT_UNIX_VFS be "unix-none"? */ ++# endif ++# ifndef F_RDLCK ++# define F_RDLCK 0 ++# define F_WRLCK 1 ++# define F_UNLCK 2 ++# if __LONG_MAX == 0x7fffffffL ++# define F_GETLK 12 ++# define F_SETLK 13 ++# define F_SETLKW 14 ++# else ++# define F_GETLK 5 ++# define F_SETLK 6 ++# define F_SETLKW 7 ++# endif ++# endif ++#else /* !SQLITE_WASI */ ++# ifndef HAVE_FCHMOD ++# define HAVE_FCHMOD 1 ++# endif ++#endif /* SQLITE_WASI */ ++ ++#ifdef SQLITE_WASI ++# define osGetpid(X) (pid_t)1 ++#else + /* Always cast the getpid() return type for compatibility with + ** kernel modules in VxWorks. */ +-#define osGetpid(X) (pid_t)getpid() ++# define osGetpid(X) (pid_t)getpid() ++#endif + + /* + ** Only set the lastErrno if the error code is a real error and not +@@ -33595,6 +39127,7 @@ struct unixFile { + #endif + #ifdef SQLITE_ENABLE_SETLK_TIMEOUT + unsigned iBusyTimeout; /* Wait this many millisec on locks */ ++ int bBlockOnConnect; /* True to block for SHARED locks */ + #endif + #if OS_VXWORKS + struct vxworksFileId *pId; /* Unique file ID */ +@@ -33633,7 +39166,7 @@ static pid_t randomnessPid = 0; + #define UNIXFILE_EXCL 0x01 /* Connections from one process only */ + #define UNIXFILE_RDONLY 0x02 /* Connection is read only */ + #define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ +-#ifndef SQLITE_DISABLE_DIRSYNC ++#if !defined(SQLITE_DISABLE_DIRSYNC) && !defined(_AIX) + # define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ + #else + # define UNIXFILE_DIRSYNC 0x00 +@@ -33646,205 +39179,7 @@ static pid_t randomnessPid = 0; + /* + ** Include code that is common to all os_*.c files + */ +-/************** Include os_common.h in the middle of os_unix.c ***************/ +-/************** Begin file os_common.h ***************************************/ +-/* +-** 2004 May 22 +-** +-** 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 contains macros and a little bit of code that is common to +-** all of the platform-specific files (os_*.c) and is #included into those +-** files. +-** +-** This file should be #included by the os_*.c files only. It is not a +-** general purpose header file. +-*/ +-#ifndef _OS_COMMON_H_ +-#define _OS_COMMON_H_ +- +-/* +-** At least two bugs have slipped in because we changed the MEMORY_DEBUG +-** macro to SQLITE_DEBUG and some older makefiles have not yet made the +-** switch. The following code should catch this problem at compile-time. +-*/ +-#ifdef MEMORY_DEBUG +-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." +-#endif +- +-/* +-** Macros for performance tracing. Normally turned off. Only works +-** on i486 hardware. +-*/ +-#ifdef SQLITE_PERFORMANCE_TRACE +- +-/* +-** hwtime.h contains inline assembler code for implementing +-** high-performance timing routines. +-*/ +-/************** Include hwtime.h in the middle of os_common.h ****************/ +-/************** Begin file hwtime.h ******************************************/ +-/* +-** 2008 May 27 +-** +-** 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 contains inline asm code for retrieving "high-performance" +-** counters for x86 and x86_64 class CPUs. +-*/ +-#ifndef SQLITE_HWTIME_H +-#define SQLITE_HWTIME_H +- +-/* +-** The following routine only works on pentium-class (or newer) processors. +-** It uses the RDTSC opcode to read the cycle count value out of the +-** processor and returns that value. This can be used for high-res +-** profiling. +-*/ +-#if !defined(__STRICT_ANSI__) && \ +- (defined(__GNUC__) || defined(_MSC_VER)) && \ +- (defined(i386) || defined(__i386__) || defined(_M_IX86)) +- +- #if defined(__GNUC__) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned int lo, hi; +- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +- return (sqlite_uint64)hi << 32 | lo; +- } +- +- #elif defined(_MSC_VER) +- +- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ +- __asm { +- rdtsc +- ret ; return value at EDX:EAX +- } +- } +- +- #endif +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long val; +- __asm__ __volatile__ ("rdtsc" : "=A" (val)); +- return val; +- } +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long long retval; +- unsigned long junk; +- __asm__ __volatile__ ("\n\ +- 1: mftbu %1\n\ +- mftb %L0\n\ +- mftbu %0\n\ +- cmpw %0,%1\n\ +- bne 1b" +- : "=r" (retval), "=r" (junk)); +- return retval; +- } +- +-#else +- +- /* +- ** asm() is needed for hardware timing support. Without asm(), +- ** disable the sqlite3Hwtime() routine. +- ** +- ** sqlite3Hwtime() is only used for some obscure debugging +- ** and analysis configurations, not in any deliverable, so this +- ** should not be a great loss. +- */ +-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } +- +-#endif +- +-#endif /* !defined(SQLITE_HWTIME_H) */ +- +-/************** End of hwtime.h **********************************************/ +-/************** Continuing where we left off in os_common.h ******************/ +- +-static sqlite_uint64 g_start; +-static sqlite_uint64 g_elapsed; +-#define TIMER_START g_start=sqlite3Hwtime() +-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start +-#define TIMER_ELAPSED g_elapsed +-#else +-#define TIMER_START +-#define TIMER_END +-#define TIMER_ELAPSED ((sqlite_uint64)0) +-#endif +- +-/* +-** If we compile with the SQLITE_TEST macro set, then the following block +-** of code will give us the ability to simulate a disk I/O error. This +-** is used for testing the I/O recovery logic. +-*/ +-#if defined(SQLITE_TEST) +-SQLITE_API extern int sqlite3_io_error_hit; +-SQLITE_API extern int sqlite3_io_error_hardhit; +-SQLITE_API extern int sqlite3_io_error_pending; +-SQLITE_API extern int sqlite3_io_error_persist; +-SQLITE_API extern int sqlite3_io_error_benign; +-SQLITE_API extern int sqlite3_diskfull_pending; +-SQLITE_API extern int sqlite3_diskfull; +-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) +-#define SimulateIOError(CODE) \ +- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ +- || sqlite3_io_error_pending-- == 1 ) \ +- { local_ioerr(); CODE; } +-static void local_ioerr(){ +- IOTRACE(("IOERR\n")); +- sqlite3_io_error_hit++; +- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; +-} +-#define SimulateDiskfullError(CODE) \ +- if( sqlite3_diskfull_pending ){ \ +- if( sqlite3_diskfull_pending == 1 ){ \ +- local_ioerr(); \ +- sqlite3_diskfull = 1; \ +- sqlite3_io_error_hit = 1; \ +- CODE; \ +- }else{ \ +- sqlite3_diskfull_pending--; \ +- } \ +- } +-#else +-#define SimulateIOErrorBenign(X) +-#define SimulateIOError(A) +-#define SimulateDiskfullError(A) +-#endif /* defined(SQLITE_TEST) */ +- +-/* +-** When testing, keep a count of the number of open files. +-*/ +-#if defined(SQLITE_TEST) +-SQLITE_API extern int sqlite3_open_file_count; +-#define OpenCounter(X) sqlite3_open_file_count+=(X) +-#else +-#define OpenCounter(X) +-#endif /* defined(SQLITE_TEST) */ +- +-#endif /* !defined(_OS_COMMON_H_) */ +- +-/************** End of os_common.h *******************************************/ +-/************** Continuing where we left off in os_unix.c ********************/ ++/* #include "os_common.h" */ + + /* + ** Define various macros that are missing from some systems. +@@ -34004,7 +39339,11 @@ static struct unix_syscall { + #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\ + aSyscall[13].pCurrent) + ++#if defined(HAVE_FCHMOD) + { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, ++#else ++ { "fchmod", (sqlite3_syscall_ptr)0, 0 }, ++#endif + #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) + + #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE +@@ -34040,14 +39379,16 @@ static struct unix_syscall { + #endif + #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) + +-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 ++#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ ++ && !defined(SQLITE_WASI) + { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, + #else + { "mmap", (sqlite3_syscall_ptr)0, 0 }, + #endif + #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) + +-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 ++#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ ++ && !defined(SQLITE_WASI) + { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, + #else + { "munmap", (sqlite3_syscall_ptr)0, 0 }, +@@ -34112,7 +39453,7 @@ static int robustFchown(int fd, uid_t uid, gid_t gid){ + + /* + ** This is the xSetSystemCall() method of sqlite3_vfs for all of the +-** "unix" VFSes. Return SQLITE_OK opon successfully updating the ++** "unix" VFSes. Return SQLITE_OK upon successfully updating the + ** system call pointer, or SQLITE_NOTFOUND if there is no configurable + ** system call named zName. + */ +@@ -34233,6 +39574,9 @@ static int robust_open(const char *z, int f, mode_t m){ + break; + } + if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; ++ if( (f & (O_EXCL|O_CREAT))==(O_EXCL|O_CREAT) ){ ++ (void)osUnlink(z); ++ } + osClose(fd); + sqlite3_log(SQLITE_WARNING, + "attempt to open \"%s\" as file descriptor %d", z, fd); +@@ -34631,7 +39975,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ + ** If you close a file descriptor that points to a file that has locks, + ** all locks on that file that are owned by the current process are + ** released. To work around this problem, each unixInodeInfo object +-** maintains a count of the number of pending locks on tha inode. ++** maintains a count of the number of pending locks on the inode. + ** When an attempt is made to close an unixFile, if there are + ** other unixFile open on the same inode that are holding locks, the call + ** to close() the file descriptor is deferred until all of the locks clear. +@@ -34645,7 +39989,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ + ** not posix compliant. Under LinuxThreads, a lock created by thread + ** A cannot be modified or overridden by a different thread B. + ** Only thread A can modify the lock. Locking behavior is correct +-** if the appliation uses the newer Native Posix Thread Library (NPTL) ++** if the application uses the newer Native Posix Thread Library (NPTL) + ** on linux - with NPTL a lock created by thread A can override locks + ** in thread B. But there is no way to know at compile-time which + ** threading library is being used. So there is no way to know at +@@ -34795,8 +40139,12 @@ static int unixLogErrorAtLine( + ** available, the error message will often be an empty string. Not a + ** huge problem. Incorrectly concluding that the GNU version is available + ** could lead to a segfault though. ++ ** ++ ** Forum post 3f13857fa4062301 reports that the Android SDK may use ++ ** int-type return, depending on its version. + */ +-#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) ++#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \ ++ && !defined(ANDROID) && !defined(__ANDROID__) + zErr = + # endif + strerror_r(iErrno, aErr, sizeof(aErr)-1); +@@ -34847,7 +40195,7 @@ static void storeLastErrno(unixFile *pFile, int error){ + } + + /* +-** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. ++** Close all file descriptors accumulated in the unixInodeInfo->pUnused list. + */ + static void closePendingFds(unixFile *pFile){ + unixInodeInfo *pInode = pFile->pInode; +@@ -35092,6 +40440,9 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ + return rc; + } + ++/* Forward declaration*/ ++static int unixSleep(sqlite3_vfs*,int); ++ + /* + ** Set a posix-advisory-lock. + ** +@@ -35121,7 +40472,7 @@ static int osSetPosixAdvisoryLock( + ** generic posix, however, there is no such API. So we simply try the + ** lock once every millisecond until either the timeout expires, or until + ** the lock is obtained. */ +- usleep(1000); ++ unixSleep(0,1000); + rc = osFcntl(h,F_SETLK,pLock); + tm--; + } +@@ -35157,7 +40508,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ + if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ + if( pInode->bProcessLock==0 ){ + struct flock lock; +- assert( pInode->nLock==0 ); ++ /* assert( pInode->nLock==0 ); <-- Not true if unix-excl READONLY used */ + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; +@@ -35170,6 +40521,13 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ + rc = 0; + } + }else{ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ if( pFile->bBlockOnConnect && pLock->l_type==F_RDLCK ++ && pLock->l_start==SHARED_FIRST && pLock->l_len==SHARED_SIZE ++ ){ ++ rc = osFcntl(pFile->h, F_SETLKW, pLock); ++ }else ++#endif + rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); + } + return rc; +@@ -35192,7 +40550,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ + ** + ** UNLOCKED -> SHARED + ** SHARED -> RESERVED +-** SHARED -> (PENDING) -> EXCLUSIVE ++** SHARED -> EXCLUSIVE + ** RESERVED -> (PENDING) -> EXCLUSIVE + ** PENDING -> EXCLUSIVE + ** +@@ -35207,7 +40565,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ + ** slightly in order to be compatible with Windows95 systems simultaneously + ** accessing the same database file, in case that is ever required. + ** +- ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved ++ ** Symbols defined in os.h identify the 'pending byte' and the 'reserved + ** byte', each single bytes at well known offsets, and the 'shared byte + ** range', a range of 510 bytes at a well known offset. + ** +@@ -35215,7 +40573,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ + ** byte'. If this is successful, 'shared byte range' is read-locked + ** and the lock on the 'pending byte' released. (Legacy note: When + ** SQLite was first developed, Windows95 systems were still very common, +- ** and Widnows95 lacks a shared-lock capability. So on Windows95, a ++ ** and Windows95 lacks a shared-lock capability. So on Windows95, a + ** single randomly selected by from the 'shared byte range' is locked. + ** Windows95 is now pretty much extinct, but this work-around for the + ** lack of shared-locks on Windows95 lives on, for backwards +@@ -35225,19 +40583,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){ + ** A RESERVED lock is implemented by grabbing a write-lock on the + ** 'reserved byte'. + ** +- ** A process may only obtain a PENDING lock after it has obtained a +- ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock +- ** on the 'pending byte'. This ensures that no new SHARED locks can be +- ** obtained, but existing SHARED locks are allowed to persist. A process +- ** does not have to obtain a RESERVED lock on the way to a PENDING lock. +- ** This property is used by the algorithm for rolling back a journal file +- ** after a crash. ++ ** An EXCLUSIVE lock may only be requested after either a SHARED or ++ ** RESERVED lock is held. An EXCLUSIVE lock is implemented by obtaining ++ ** a write-lock on the entire 'shared byte range'. Since all other locks ++ ** require a read-lock on one of the bytes within this range, this ensures ++ ** that no other locks are held on the database. + ** +- ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is +- ** implemented by obtaining a write-lock on the entire 'shared byte +- ** range'. Since all other locks require a read-lock on one of the bytes +- ** within this range, this ensures that no other locks are held on the +- ** database. ++ ** If a process that holds a RESERVED lock requests an EXCLUSIVE, then ++ ** a PENDING lock is obtained first. A PENDING lock is implemented by ++ ** obtaining a write-lock on the 'pending byte'. This ensures that no new ++ ** SHARED locks can be obtained, but existing SHARED locks are allowed to ++ ** persist. If the call to this function fails to obtain the EXCLUSIVE ++ ** lock in this case, it holds the PENDING lock instead. The client may ++ ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED ++ ** locks have cleared. + */ + int rc = SQLITE_OK; + unixFile *pFile = (unixFile*)id; +@@ -35308,7 +40667,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ + lock.l_len = 1L; + lock.l_whence = SEEK_SET; + if( eFileLock==SHARED_LOCK +- || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLockeFileLock==RESERVED_LOCK) + ){ + lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); + lock.l_start = PENDING_BYTE; +@@ -35319,6 +40678,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){ + storeLastErrno(pFile, tErrno); + } + goto end_lock; ++ }else if( eFileLock==EXCLUSIVE_LOCK ){ ++ pFile->eFileLock = PENDING_LOCK; ++ pInode->eFileLock = PENDING_LOCK; + } + } + +@@ -35406,13 +40768,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){ + } + #endif + +- + if( rc==SQLITE_OK ){ + pFile->eFileLock = eFileLock; + pInode->eFileLock = eFileLock; +- }else if( eFileLock==EXCLUSIVE_LOCK ){ +- pFile->eFileLock = PENDING_LOCK; +- pInode->eFileLock = PENDING_LOCK; + } + + end_lock: +@@ -35692,6 +41050,7 @@ static int unixClose(sqlite3_file *id){ + } + sqlite3_mutex_leave(pInode->pLockMutex); + releaseInodeInfo(pFile); ++ assert( pFile->pShm==0 ); + rc = closeUnixFile(id); + unixLeaveMutex(); + return rc; +@@ -35771,26 +41130,22 @@ static int nolockClose(sqlite3_file *id) { + + /* + ** This routine checks if there is a RESERVED lock held on the specified +-** file by this or any other process. If such a lock is held, set *pResOut +-** to a non-zero value otherwise *pResOut is set to zero. The return value +-** is set to SQLITE_OK unless an I/O error occurs during lock checking. +-** +-** In dotfile locking, either a lock exists or it does not. So in this +-** variation of CheckReservedLock(), *pResOut is set to true if any lock +-** is held on the file and false if the file is unlocked. ++** file by this or any other process. If the caller holds a SHARED ++** or greater lock when it is called, then it is assumed that no other ++** client may hold RESERVED. Or, if the caller holds no lock, then it ++** is assumed another client holds RESERVED if the lock-file exists. + */ + static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { +- int rc = SQLITE_OK; +- int reserved = 0; + unixFile *pFile = (unixFile*)id; +- + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + +- assert( pFile ); +- reserved = osAccess((const char*)pFile->lockingContext, 0)==0; +- OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); +- *pResOut = reserved; +- return rc; ++ if( pFile->eFileLock>=SHARED_LOCK ){ ++ *pResOut = 0; ++ }else{ ++ *pResOut = osAccess((const char*)pFile->lockingContext, 0)==0; ++ } ++ OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, 0, *pResOut)); ++ return SQLITE_OK; + } + + /* +@@ -35960,54 +41315,33 @@ static int robust_flock(int fd, int op){ + ** is set to SQLITE_OK unless an I/O error occurs during lock checking. + */ + static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ +- int rc = SQLITE_OK; +- int reserved = 0; ++#ifdef SQLITE_DEBUG + unixFile *pFile = (unixFile*)id; ++#else ++ UNUSED_PARAMETER(id); ++#endif + + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + + assert( pFile ); ++ assert( pFile->eFileLock<=SHARED_LOCK ); + +- /* Check if a thread in this process holds such a lock */ +- if( pFile->eFileLock>SHARED_LOCK ){ +- reserved = 1; +- } +- +- /* Otherwise see if some other process holds it. */ +- if( !reserved ){ +- /* attempt to get the lock */ +- int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); +- if( !lrc ){ +- /* got the lock, unlock it */ +- lrc = robust_flock(pFile->h, LOCK_UN); +- if ( lrc ) { +- int tErrno = errno; +- /* unlock failed with an error */ +- lrc = SQLITE_IOERR_UNLOCK; +- storeLastErrno(pFile, tErrno); +- rc = lrc; +- } +- } else { +- int tErrno = errno; +- reserved = 1; +- /* someone else might have it reserved */ +- lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); +- if( IS_LOCK_ERROR(lrc) ){ +- storeLastErrno(pFile, tErrno); +- rc = lrc; +- } +- } +- } +- OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); ++ /* The flock VFS only ever takes exclusive locks (see function flockLock). ++ ** Therefore, if this connection is holding any lock at all, no other ++ ** connection may be holding a RESERVED lock. So set *pResOut to 0 ++ ** in this case. ++ ** ++ ** Or, this connection may be holding no lock. In that case, set *pResOut to ++ ** 0 as well. The caller will then attempt to take an EXCLUSIVE lock on the ++ ** db in order to roll the hot journal back. If there is another connection ++ ** holding a lock, that attempt will fail and an SQLITE_BUSY returned to ++ ** the user. With other VFS, we try to avoid this, in order to allow a reader ++ ** to proceed while a writer is preparing its transaction. But that won't ++ ** work with the flock VFS - as it always takes EXCLUSIVE locks - so it is ++ ** not a problem in this case. */ ++ *pResOut = 0; + +-#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS +- if( (rc & 0xff) == SQLITE_IOERR ){ +- rc = SQLITE_OK; +- reserved=1; +- } +-#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ +- *pResOut = reserved; +- return rc; ++ return SQLITE_OK; + } + + /* +@@ -36597,7 +41931,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ + if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + + pInode->sharedByte, 1, 0)) ){ + int failed2 = SQLITE_OK; +- /* now attemmpt to get the exclusive lock range */ ++ /* now attempt to get the exclusive lock range */ + failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, + SHARED_SIZE, 1); + if( failed && (failed2 = afpSetLock(context->dbPath, pFile, +@@ -36646,9 +41980,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { + unixInodeInfo *pInode; + afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; + int skipShared = 0; +-#ifdef SQLITE_TEST +- int h = pFile->h; +-#endif + + assert( pFile ); + OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, +@@ -36664,9 +41995,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { + assert( pInode->nShared!=0 ); + if( pFile->eFileLock>SHARED_LOCK ){ + assert( pInode->eFileLock==pFile->eFileLock ); +- SimulateIOErrorBenign(1); +- SimulateIOError( h=(-1) ) +- SimulateIOErrorBenign(0); + + #ifdef SQLITE_DEBUG + /* When reducing a lock such that other processes can start +@@ -36715,9 +42043,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { + unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; + pInode->nShared--; + if( pInode->nShared==0 ){ +- SimulateIOErrorBenign(1); +- SimulateIOError( h=(-1) ) +- SimulateIOErrorBenign(0); + if( !skipShared ){ + rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); + } +@@ -36818,12 +42143,6 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){ + ** Seek to the offset passed as the second argument, then read cnt + ** bytes into pBuf. Return the number of bytes actually read. + ** +-** NB: If you define USE_PREAD or USE_PREAD64, then it might also +-** be necessary to define _XOPEN_SOURCE to be 500. This varies from +-** one system to another. Since SQLite does not define USE_PREAD +-** in any form by default, we will not attempt to define _XOPEN_SOURCE. +-** See tickets #2741 and #2681. +-** + ** To avoid stomping the errno value on a failed read the lastErrno value + ** is set before returning. + */ +@@ -36898,7 +42217,7 @@ static int unixRead( + #endif + + #if SQLITE_MAX_MMAP_SIZE>0 +- /* Deal with as much of this read request as possible by transfering ++ /* Deal with as much of this read request as possible by transferring + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ +@@ -36918,7 +42237,24 @@ static int unixRead( + if( got==amt ){ + return SQLITE_OK; + }else if( got<0 ){ +- /* lastErrno set by seekAndRead */ ++ /* pFile->lastErrno has been set by seekAndRead(). ++ ** Usually we return SQLITE_IOERR_READ here, though for some ++ ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The ++ ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT ++ ** prior to returning to the application by the sqlite3ApiExit() ++ ** routine. ++ */ ++ switch( pFile->lastErrno ){ ++ case ERANGE: ++ case EIO: ++#ifdef ENXIO ++ case ENXIO: ++#endif ++#ifdef EDEVERR ++ case EDEVERR: ++#endif ++ return SQLITE_IOERR_CORRUPTFS; ++ } + return SQLITE_IOERR_READ; + }else{ + storeLastErrno(pFile, 0); /* not a system error */ +@@ -37033,7 +42369,7 @@ static int unixWrite( + #endif + + #if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 +- /* Deal with as much of this write request as possible by transfering ++ /* Deal with as much of this write request as possible by transferring + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ +@@ -37155,7 +42491,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ + /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a + ** no-op. But go ahead and call fstat() to validate the file + ** descriptor as we need a method to provoke a failure during +- ** coverate testing. ++ ** coverage testing. + */ + #ifdef SQLITE_NO_SYNC + { +@@ -37477,6 +42813,9 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ + + /* Forward declaration */ + static int unixGetTempname(int nBuf, char *zBuf); ++#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) ++ static int unixFcntlExternalReader(unixFile*, int*); ++#endif + + /* + ** Information and control of an open file handle. +@@ -37499,6 +42838,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ + } + #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ + ++ case SQLITE_FCNTL_NULL_IO: { ++ osClose(pFile->h); ++ pFile->h = -1; ++ return SQLITE_OK; ++ } + case SQLITE_FCNTL_LOCKSTATE: { + *(int*)pArg = pFile->eFileLock; + return SQLITE_OK; +@@ -37545,11 +42889,23 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ + #ifdef SQLITE_ENABLE_SETLK_TIMEOUT + case SQLITE_FCNTL_LOCK_TIMEOUT: { + int iOld = pFile->iBusyTimeout; +- pFile->iBusyTimeout = *(int*)pArg; ++ int iNew = *(int*)pArg; ++#if SQLITE_ENABLE_SETLK_TIMEOUT==1 ++ pFile->iBusyTimeout = iNew<0 ? 0x7FFFFFFF : (unsigned)iNew; ++#elif SQLITE_ENABLE_SETLK_TIMEOUT==2 ++ pFile->iBusyTimeout = !!(*(int*)pArg); ++#else ++# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" ++#endif + *(int*)pArg = iOld; + return SQLITE_OK; + } +-#endif ++ case SQLITE_FCNTL_BLOCK_ON_CONNECT: { ++ int iNew = *(int*)pArg; ++ pFile->bBlockOnConnect = iNew; ++ return SQLITE_OK; ++ } ++#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ + #if SQLITE_MAX_MMAP_SIZE>0 + case SQLITE_FCNTL_MMAP_SIZE: { + i64 newLimit = *(i64*)pArg; +@@ -37593,6 +42949,15 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ + return proxyFileControl(id,op,pArg); + } + #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ ++ ++ case SQLITE_FCNTL_EXTERNAL_READER: { ++#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) ++ return unixFcntlExternalReader((unixFile*)id, (int*)pArg); ++#else ++ *(int*)pArg = 0; ++ return SQLITE_OK; ++#endif ++ } + } + return SQLITE_NOTFOUND; + } +@@ -37625,6 +42990,7 @@ static void setDeviceCharacteristics(unixFile *pFd){ + if( pFd->ctrlFlags & UNIXFILE_PSOW ){ + pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; + } ++ pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ; + + pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; + } +@@ -37675,7 +43041,7 @@ static void setDeviceCharacteristics(unixFile *pFile){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* full bitset of atomics from max sector size and smaller */ +- ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | ++ (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; +@@ -37683,7 +43049,7 @@ static void setDeviceCharacteristics(unixFile *pFile){ + pFile->sectorSize = fsInfo.f_bsize; + pFile->deviceCharacteristics = + /* full bitset of atomics from max sector size and smaller */ +- ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | ++ (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind + ** so it is ordered */ + 0; +@@ -37759,7 +43125,7 @@ static int unixGetpagesize(void){ + + #endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */ + +-#ifndef SQLITE_OMIT_WAL ++#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) + + /* + ** Object used to represent an shared memory buffer. +@@ -37789,6 +43155,25 @@ static int unixGetpagesize(void){ + ** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and + ** unixMutexHeld() is true when reading or writing any other field + ** in this structure. ++** ++** aLock[SQLITE_SHM_NLOCK]: ++** This array records the various locks held by clients on each of the ++** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no ++** locks are held by the process on this slot. If it is set to -1, then ++** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[] ++** value is set to a positive value, then it is the number of shared ++** locks currently held on the slot. ++** ++** aMutex[SQLITE_SHM_NLOCK]: ++** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex ++** pShmMutex is used to protect the aLock[] array and the right to ++** call fcntl() on unixShmNode.hShm to obtain or release locks. ++** ++** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array ++** of mutexes - one for each locking slot. To read or write locking ++** slot aLock[iSlot], the caller must hold the corresponding mutex ++** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a ++** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held. + */ + struct unixShmNode { + unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ +@@ -37802,9 +43187,11 @@ struct unixShmNode { + char **apRegion; /* Array of mapped shared-memory regions */ + int nRef; /* Number of unixShm objects pointing to this */ + unixShm *pFirst; /* All unixShm objects pointing to this */ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK]; ++#endif ++ int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ + #ifdef SQLITE_DEBUG +- u8 exclMask; /* Mask of exclusive locks held */ +- u8 sharedMask; /* Mask of shared locks held */ + u8 nextShmId; /* Next available unixShm.id value */ + #endif + }; +@@ -37837,6 +43224,40 @@ struct unixShm { + #define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ + #define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ + ++/* ++** Use F_GETLK to check whether or not there are any readers with open ++** wal-mode transactions in other processes on database file pFile. If ++** no error occurs, return SQLITE_OK and set (*piOut) to 1 if there are ++** such transactions, or 0 otherwise. If an error occurs, return an ++** SQLite error code. The final value of *piOut is undefined in this ++** case. ++*/ ++static int unixFcntlExternalReader(unixFile *pFile, int *piOut){ ++ int rc = SQLITE_OK; ++ *piOut = 0; ++ if( pFile->pShm){ ++ unixShmNode *pShmNode = pFile->pShm->pShmNode; ++ struct flock f; ++ ++ memset(&f, 0, sizeof(f)); ++ f.l_type = F_WRLCK; ++ f.l_whence = SEEK_SET; ++ f.l_start = UNIX_SHM_BASE + 3; ++ f.l_len = SQLITE_SHM_NLOCK - 3; ++ ++ sqlite3_mutex_enter(pShmNode->pShmMutex); ++ if( osFcntl(pShmNode->hShm, F_GETLK, &f)<0 ){ ++ rc = SQLITE_IOERR_LOCK; ++ }else{ ++ *piOut = (f.l_type!=F_UNLCK); ++ } ++ sqlite3_mutex_leave(pShmNode->pShmMutex); ++ } ++ ++ return rc; ++} ++ ++ + /* + ** Apply posix advisory locks for all bytes from ofst through ofst+n-1. + ** +@@ -37853,16 +43274,35 @@ static int unixShmSystemLock( + struct flock f; /* The posix advisory locking structure */ + int rc = SQLITE_OK; /* Result code form fcntl() */ + +- /* Access to the unixShmNode object is serialized by the caller */ + pShmNode = pFile->pInode->pShmNode; +- assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); +- assert( pShmNode->nRef>0 || unixMutexHeld() ); ++ ++ /* Assert that the parameters are within expected range and that the ++ ** correct mutex or mutexes are held. */ ++ assert( pShmNode->nRef>=0 ); ++ assert( (ofst==UNIX_SHM_DMS && n==1) ++ || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK)) ++ ); ++ if( ofst==UNIX_SHM_DMS ){ ++ assert( pShmNode->nRef>0 || unixMutexHeld() ); ++ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); ++ }else{ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ int ii; ++ for(ii=ofst-UNIX_SHM_BASE; iiaMutex[ii]) ); ++ } ++#else ++ assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); ++ assert( pShmNode->nRef>0 ); ++#endif ++ } + + /* Shared locks never span more than one byte */ + assert( n==1 || lockType!=F_RDLCK ); + + /* Locks are within range */ + assert( n>=1 && n<=SQLITE_SHM_NLOCK ); ++ assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) ); + + if( pShmNode->hShm>=0 ){ + int res; +@@ -37873,7 +43313,7 @@ static int unixShmSystemLock( + f.l_len = n; + res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); + if( res==-1 ){ +-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1 + rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); + #else + rc = SQLITE_BUSY; +@@ -37881,39 +43321,28 @@ static int unixShmSystemLock( + } + } + +- /* Update the global lock state and do debug tracing */ ++ /* Do debug tracing */ + #ifdef SQLITE_DEBUG +- { u16 mask; + OSTRACE(("SHM-LOCK ")); +- mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<exclMask &= ~mask; +- pShmNode->sharedMask &= ~mask; ++ OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1)); + }else if( lockType==F_RDLCK ){ +- OSTRACE(("read-lock %d ok", ofst)); +- pShmNode->exclMask &= ~mask; +- pShmNode->sharedMask |= mask; ++ OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1)); + }else{ + assert( lockType==F_WRLCK ); +- OSTRACE(("write-lock %d ok", ofst)); +- pShmNode->exclMask |= mask; +- pShmNode->sharedMask &= ~mask; ++ OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1)); + } + }else{ + if( lockType==F_UNLCK ){ +- OSTRACE(("unlock %d failed", ofst)); ++ OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1)); + }else if( lockType==F_RDLCK ){ +- OSTRACE(("read-lock failed")); ++ OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1)); + }else{ + assert( lockType==F_WRLCK ); +- OSTRACE(("write-lock %d failed", ofst)); ++ OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1)); + } + } +- OSTRACE((" - afterwards %03x,%03x\n", +- pShmNode->sharedMask, pShmNode->exclMask)); +- } + #endif + + return rc; +@@ -37950,6 +43379,11 @@ static void unixShmPurge(unixFile *pFd){ + int i; + assert( p->pInode==pFd->pInode ); + sqlite3_mutex_free(p->pShmMutex); ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ for(i=0; iaMutex[i]); ++ } ++#endif + for(i=0; inRegion; i+=nShmPerMap){ + if( p->hShm>=0 ){ + osMunmap(p->apRegion[i], p->szRegion); +@@ -38009,7 +43443,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ + pShmNode->isUnlocked = 1; + rc = SQLITE_READONLY_CANTINIT; + }else{ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ /* Do not use a blocking lock here. If the lock cannot be obtained ++ ** immediately, it means some other connection is truncating the ++ ** *-shm file. And after it has done so, it will not release its ++ ** lock, but only downgrade it to a shared lock. So no point in ++ ** blocking here. The call below to obtain the shared DMS lock may ++ ** use a blocking lock. */ ++ int iSaveTimeout = pDbFd->iBusyTimeout; ++ pDbFd->iBusyTimeout = 0; ++#endif + rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ pDbFd->iBusyTimeout = iSaveTimeout; ++#endif + /* The first connection to attach must truncate the -shm file. We + ** truncate to 3 bytes (an arbitrary small number, less than the + ** -shm header size) rather than 0 as a system debugging aid, to +@@ -38130,6 +43577,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ + rc = SQLITE_NOMEM_BKPT; + goto shm_open_err; + } ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ { ++ int ii; ++ for(ii=0; iiaMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); ++ if( pShmNode->aMutex[ii]==0 ){ ++ rc = SQLITE_NOMEM_BKPT; ++ goto shm_open_err; ++ } ++ } ++ } ++#endif + } + + if( pInode->bProcessLock==0 ){ +@@ -38342,10 +43801,45 @@ shmpage_out: + return rc; + } + ++/* ++** Check that the pShmNode->aLock[] array comports with the locking bitmasks ++** held by each client. Return true if it does, or false otherwise. This ++** is to be used in an assert(). e.g. ++** ++** assert( assertLockingArrayOk(pShmNode) ); ++*/ ++#ifdef SQLITE_DEBUG ++static int assertLockingArrayOk(unixShmNode *pShmNode){ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ return 1; ++#else ++ unixShm *pX; ++ int aLock[SQLITE_SHM_NLOCK]; ++ ++ memset(aLock, 0, sizeof(aLock)); ++ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ ++ int i; ++ for(i=0; iexclMask & (1<sharedMask & (1<=0 ); ++ aLock[i]++; ++ } ++ } ++ } ++ ++ assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); ++ return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); ++#endif ++} ++#endif ++ + /* + ** Change the lock state for a shared-memory segment. + ** +-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little ++** Note that the relationship between SHARED and EXCLUSIVE locks is a little + ** different here than in posix. In xShmLock(), one can go from unlocked + ** to shared and back or from unlocked to exclusive and back. But one may + ** not go from shared to exclusive or from exclusive to shared. +@@ -38357,11 +43851,17 @@ static int unixShmLock( + int flags /* What to do with the lock */ + ){ + unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ +- unixShm *p = pDbFd->pShm; /* The shared memory being locked */ +- unixShm *pX; /* For looping over all siblings */ +- unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ ++ unixShm *p; /* The shared memory being locked */ ++ unixShmNode *pShmNode; /* The underlying file iNode */ + int rc = SQLITE_OK; /* Result code */ +- u16 mask; /* Mask of locks to take or release */ ++ u16 mask = (1<<(ofst+n)) - (1<pShm; ++ if( p==0 ) return SQLITE_IOERR_SHMLOCK; ++ pShmNode = p->pShmNode; ++ if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; ++ aLock = pShmNode->aLock; + + assert( pShmNode==pDbFd->pInode->pShmNode ); + assert( pShmNode->pInode==pDbFd->pInode ); +@@ -38378,101 +43878,161 @@ static int unixShmLock( + /* Check that, if this to be a blocking lock, no locks that occur later + ** in the following list than the lock being obtained are already held: + ** +- ** 1. Checkpointer lock (ofst==1). +- ** 2. Write lock (ofst==0). +- ** 3. Read locks (ofst>=3 && ofst=3 && ofstiBusyTimeout==0 || ( +- (ofst!=2) /* not RECOVER */ +- && (ofst!=1 || (p->exclMask|p->sharedMask)==0) +- && (ofst!=0 || (p->exclMask|p->sharedMask)<3) +- && (ofst<3 || (p->exclMask|p->sharedMask)<(1<exclMask|p->sharedMask); ++ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( ++ (ofst!=2 || lockMask==0) ++ && (ofst!=1 || lockMask==0 || lockMask==2) ++ && (ofst!=0 || lockMask<3) ++ && (ofst<3 || lockMask<(1<1 || mask==(1<pShmMutex); +- if( flags & SQLITE_SHM_UNLOCK ){ +- u16 allMask = 0; /* Mask of locks held by siblings */ +- +- /* See if any siblings hold this same lock */ +- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +- if( pX==p ) continue; +- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); +- allMask |= pX->sharedMask; +- } ++ /* Check if there is any work to do. There are three cases: ++ ** ++ ** a) An unlock operation where there are locks to unlock, ++ ** b) An shared lock where the requested lock is not already held ++ ** c) An exclusive lock where the requested lock is not already held ++ ** ++ ** The SQLite core never requests an exclusive lock that it already holds. ++ ** This is assert()ed below. ++ */ ++ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK) ++ || 0==(p->exclMask & mask) ++ ); ++ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) ++ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) ++ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) ++ ){ + +- /* Unlock the system-level locks */ +- if( (mask & allMask)==0 ){ +- rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); +- }else{ +- rc = SQLITE_OK; ++ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if ++ ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any ++ ** other thread is holding this mutex, then it is either holding or about ++ ** to hold a lock exclusive to the one being requested, and we may ++ ** therefore return SQLITE_BUSY to the caller. ++ ** ++ ** Doing this prevents some deadlock scenarios. For example, thread 1 may ++ ** be a checkpointer blocked waiting on the WRITER lock. And thread 2 ++ ** may be a normal SQL client upgrading to a write transaction. In this ++ ** case thread 2 does a non-blocking request for the WRITER lock. But - ++ ** if it were to use sqlite3_mutex_enter() then it would effectively ++ ** become a (doomed) blocking request, as thread 2 would block until thread ++ ** 1 obtained WRITER and released the mutex. Since thread 2 already holds ++ ** a lock on a read-locking slot at this point, this breaks the ++ ** anti-deadlock rules (see above). */ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ int iMutex; ++ for(iMutex=ofst; iMutexaMutex[iMutex]); ++ if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes; ++ }else{ ++ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]); ++ } + } ++#else ++ sqlite3_mutex_enter(pShmNode->pShmMutex); ++#endif ++ ++ if( ALWAYS(rc==SQLITE_OK) ){ ++ if( flags & SQLITE_SHM_UNLOCK ){ ++ /* Case (a) - unlock. */ ++ int bUnlock = 1; ++ assert( (p->exclMask & p->sharedMask)==0 ); ++ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); ++ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); ++ ++ /* If this is a SHARED lock being unlocked, it is possible that other ++ ** clients within this process are holding the same SHARED lock. In ++ ** this case, set bUnlock to 0 so that the posix lock is not removed ++ ** from the file-descriptor below. */ ++ if( flags & SQLITE_SHM_SHARED ){ ++ assert( n==1 ); ++ assert( aLock[ofst]>=1 ); ++ if( aLock[ofst]>1 ){ ++ bUnlock = 0; ++ aLock[ofst]--; ++ p->sharedMask &= ~mask; ++ } ++ } + +- /* Undo the local locks */ +- if( rc==SQLITE_OK ){ +- p->exclMask &= ~mask; +- p->sharedMask &= ~mask; +- } +- }else if( flags & SQLITE_SHM_SHARED ){ +- u16 allShared = 0; /* Union of locks held by connections other than "p" */ ++ if( bUnlock ){ ++ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); ++ if( rc==SQLITE_OK ){ ++ memset(&aLock[ofst], 0, sizeof(int)*n); ++ p->sharedMask &= ~mask; ++ p->exclMask &= ~mask; ++ } ++ } ++ }else if( flags & SQLITE_SHM_SHARED ){ ++ /* Case (b) - a shared lock. */ + +- /* Find out which shared locks are already held by sibling connections. +- ** If any sibling already holds an exclusive lock, go ahead and return +- ** SQLITE_BUSY. +- */ +- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +- if( (pX->exclMask & mask)!=0 ){ +- rc = SQLITE_BUSY; +- break; +- } +- allShared |= pX->sharedMask; +- } ++ if( aLock[ofst]<0 ){ ++ /* An exclusive lock is held by some other connection. BUSY. */ ++ rc = SQLITE_BUSY; ++ }else if( aLock[ofst]==0 ){ ++ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); ++ } + +- /* Get shared locks at the system level, if necessary */ +- if( rc==SQLITE_OK ){ +- if( (allShared & mask)==0 ){ +- rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); ++ /* Get the local shared locks */ ++ if( rc==SQLITE_OK ){ ++ p->sharedMask |= mask; ++ aLock[ofst]++; ++ } + }else{ +- rc = SQLITE_OK; +- } +- } ++ /* Case (c) - an exclusive lock. */ ++ int ii; + +- /* Get the local shared locks */ +- if( rc==SQLITE_OK ){ +- p->sharedMask |= mask; +- } +- }else{ +- /* Make sure no sibling connections hold locks that will block this +- ** lock. If any do, return SQLITE_BUSY right away. +- */ +- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ +- rc = SQLITE_BUSY; +- break; ++ assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ); ++ assert( (p->sharedMask & mask)==0 ); ++ assert( (p->exclMask & mask)==0 ); ++ ++ /* Make sure no sibling connections hold locks that will block this ++ ** lock. If any do, return SQLITE_BUSY right away. */ ++ for(ii=ofst; iiexclMask |= mask; ++ for(ii=ofst; iisharedMask & mask)==0 ); +- p->exclMask |= mask; +- } ++ /* Drop the mutexes acquired above. */ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ leave_shmnode_mutexes: ++ for(iMutex--; iMutex>=ofst; iMutex--){ ++ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]); + } ++#else ++ sqlite3_mutex_leave(pShmNode->pShmMutex); ++#endif + } +- sqlite3_mutex_leave(pShmNode->pShmMutex); ++ + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", + p->id, osGetpid(0), p->sharedMask, p->exclMask)); + return rc; +@@ -38722,11 +44282,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ + + #if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->mmapSizeMax>0 ){ ++ /* Ensure that there is always at least a 256 byte buffer of addressable ++ ** memory following the returned page. If the database is corrupt, ++ ** SQLite may overread the page slightly (in practice only a few bytes, ++ ** but 256 is safe, round, number). */ ++ const int nEofBuffer = 256; + if( pFd->pMapRegion==0 ){ + int rc = unixMapfile(pFd, -1); + if( rc!=SQLITE_OK ) return rc; + } +- if( pFd->mmapSize >= iOff+nAmt ){ ++ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ + *pp = &((u8 *)pFd->pMapRegion)[iOff]; + pFd->nFetchOut++; + } +@@ -39247,25 +44812,35 @@ static int fillInUnixFile( + return rc; + } + ++/* ++** Directories to consider for temp files. ++*/ ++static const char *azTempDirs[] = { ++ 0, ++ 0, ++ "/var/tmp", ++ "/usr/tmp", ++ "/tmp", ++ "." ++}; ++ ++/* ++** Initialize first two members of azTempDirs[] array. ++*/ ++static void unixTempFileInit(void){ ++ azTempDirs[0] = getenv("SQLITE_TMPDIR"); ++ azTempDirs[1] = getenv("TMPDIR"); ++} ++ + /* + ** Return the name of a directory in which to put temporary files. + ** If no suitable temporary file directory can be found, return NULL. + */ + static const char *unixTempFileDir(void){ +- static const char *azDirs[] = { +- 0, +- 0, +- "/var/tmp", +- "/usr/tmp", +- "/tmp", +- "." +- }; + unsigned int i = 0; + struct stat buf; + const char *zDir = sqlite3_temp_directory; + +- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); +- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); + while(1){ + if( zDir!=0 + && osStat(zDir, &buf)==0 +@@ -39274,8 +44849,8 @@ static const char *unixTempFileDir(void){ + ){ + return zDir; + } +- if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break; +- zDir = azDirs[i++]; ++ if( i>=sizeof(azTempDirs)/sizeof(azTempDirs[0]) ) break; ++ zDir = azTempDirs[i++]; + } + return 0; + } +@@ -39288,6 +44863,7 @@ static const char *unixTempFileDir(void){ + static int unixGetTempname(int nBuf, char *zBuf){ + const char *zDir; + int iLimit = 0; ++ int rc = SQLITE_OK; + + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this +@@ -39296,18 +44872,26 @@ static int unixGetTempname(int nBuf, char *zBuf){ + zBuf[0] = 0; + SimulateIOError( return SQLITE_IOERR ); + ++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + zDir = unixTempFileDir(); +- if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH; +- do{ +- u64 r; +- sqlite3_randomness(sizeof(r), &r); +- assert( nBuf>2 ); +- zBuf[nBuf-2] = 0; +- sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", +- zDir, r, 0); +- if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR; +- }while( osAccess(zBuf,0)==0 ); +- return SQLITE_OK; ++ if( zDir==0 ){ ++ rc = SQLITE_IOERR_GETTEMPPATH; ++ }else{ ++ do{ ++ u64 r; ++ sqlite3_randomness(sizeof(r), &r); ++ assert( nBuf>2 ); ++ zBuf[nBuf-2] = 0; ++ sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", ++ zDir, r, 0); ++ if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){ ++ rc = SQLITE_ERROR; ++ break; ++ } ++ }while( osAccess(zBuf,0)==0 ); ++ } ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); ++ return rc; + } + + #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) +@@ -39450,20 +45034,23 @@ static int findCreateFileMode( + ** + ** where NN is a decimal number. The NN naming schemes are + ** used by the test_multiplex.c module. ++ ** ++ ** In normal operation, the journal file name will always contain ++ ** a '-' character. However in 8+3 filename mode, or if a corrupt ++ ** rollback journal specifies a super-journal with a goofy name, then ++ ** the '-' might be missing or the '-' might be the first character in ++ ** the filename. In that case, just return SQLITE_OK with *pMode==0. + */ + nDb = sqlite3Strlen30(zPath) - 1; +- while( zPath[nDb]!='-' ){ +- /* In normal operation, the journal file name will always contain +- ** a '-' character. However in 8+3 filename mode, or if a corrupt +- ** rollback journal specifies a super-journal with a goofy name, then +- ** the '-' might be missing. */ +- if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK; ++ while( nDb>0 && zPath[nDb]!='.' ){ ++ if( zPath[nDb]=='-' ){ ++ memcpy(zDb, zPath, nDb); ++ zDb[nDb] = '\0'; ++ rc = getFileMode(zDb, pMode, pUid, pGid); ++ break; ++ } + nDb--; + } +- memcpy(zDb, zPath, nDb); +- zDb[nDb] = '\0'; +- +- rc = getFileMode(zDb, pMode, pUid, pGid); + }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ + *pMode = 0600; + }else if( flags & SQLITE_OPEN_URI ){ +@@ -39581,6 +45168,11 @@ static int unixOpen( + } + memset(p, 0, sizeof(unixFile)); + ++#ifdef SQLITE_ASSERT_NO_FILES ++ /* Applications that never read or write a persistent disk files */ ++ assert( zName==0 ); ++#endif ++ + if( eType==SQLITE_OPEN_MAIN_DB ){ + UnixUnusedFd *pUnused; + pUnused = findReusableFd(zName, flags); +@@ -39643,12 +45235,19 @@ static int unixOpen( + rc = SQLITE_READONLY_DIRECTORY; + }else if( errno!=EISDIR && isReadWrite ){ + /* Failed to open the file for read/write access. Try read-only. */ ++ UnixUnusedFd *pReadonly = 0; + flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); + openFlags &= ~(O_RDWR|O_CREAT); + flags |= SQLITE_OPEN_READONLY; + openFlags |= O_RDONLY; + isReadonly = 1; +- fd = robust_open(zName, openFlags, openMode); ++ pReadonly = findReusableFd(zName, flags); ++ if( pReadonly ){ ++ fd = pReadonly->fd; ++ sqlite3_free(pReadonly); ++ }else{ ++ fd = robust_open(zName, openFlags, openMode); ++ } + } + } + if( fd<0 ){ +@@ -39848,30 +45447,97 @@ static int unixAccess( + } + + /* +-** ++** A pathname under construction + */ +-static int mkFullPathname( +- const char *zPath, /* Input path */ +- char *zOut, /* Output buffer */ +- int nOut /* Allocated size of buffer zOut */ ++typedef struct DbPath DbPath; ++struct DbPath { ++ int rc; /* Non-zero following any error */ ++ int nSymlink; /* Number of symlinks resolved */ ++ char *zOut; /* Write the pathname here */ ++ int nOut; /* Bytes of space available to zOut[] */ ++ int nUsed; /* Bytes of zOut[] currently being used */ ++}; ++ ++/* Forward reference */ ++static void appendAllPathElements(DbPath*,const char*); ++ ++/* ++** Append a single path element to the DbPath under construction ++*/ ++static void appendOnePathElement( ++ DbPath *pPath, /* Path under construction, to which to append zName */ ++ const char *zName, /* Name to append to pPath. Not zero-terminated */ ++ int nName /* Number of significant bytes in zName */ + ){ +- int nPath = sqlite3Strlen30(zPath); +- int iOff = 0; +- if( zPath[0]!='/' ){ +- if( osGetcwd(zOut, nOut-2)==0 ){ +- return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); ++ assert( nName>0 ); ++ assert( zName!=0 ); ++ if( zName[0]=='.' ){ ++ if( nName==1 ) return; ++ if( zName[1]=='.' && nName==2 ){ ++ if( pPath->nUsed>1 ){ ++ assert( pPath->zOut[0]=='/' ); ++ while( pPath->zOut[--pPath->nUsed]!='/' ){} ++ } ++ return; + } +- iOff = sqlite3Strlen30(zOut); +- zOut[iOff++] = '/'; + } +- if( (iOff+nPath+1)>nOut ){ +- /* SQLite assumes that xFullPathname() nul-terminates the output buffer +- ** even if it returns an error. */ +- zOut[iOff] = '\0'; +- return SQLITE_CANTOPEN_BKPT; ++ if( pPath->nUsed + nName + 2 >= pPath->nOut ){ ++ pPath->rc = SQLITE_ERROR; ++ return; + } +- sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); +- return SQLITE_OK; ++ pPath->zOut[pPath->nUsed++] = '/'; ++ memcpy(&pPath->zOut[pPath->nUsed], zName, nName); ++ pPath->nUsed += nName; ++#if defined(HAVE_READLINK) && defined(HAVE_LSTAT) ++ if( pPath->rc==SQLITE_OK ){ ++ const char *zIn; ++ struct stat buf; ++ pPath->zOut[pPath->nUsed] = 0; ++ zIn = pPath->zOut; ++ if( osLstat(zIn, &buf)!=0 ){ ++ if( errno!=ENOENT ){ ++ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); ++ } ++ }else if( S_ISLNK(buf.st_mode) ){ ++ ssize_t got; ++ char zLnk[SQLITE_MAX_PATHLEN+2]; ++ if( pPath->nSymlink++ > SQLITE_MAX_SYMLINK ){ ++ pPath->rc = SQLITE_CANTOPEN_BKPT; ++ return; ++ } ++ got = osReadlink(zIn, zLnk, sizeof(zLnk)-2); ++ if( got<=0 || got>=(ssize_t)sizeof(zLnk)-2 ){ ++ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); ++ return; ++ } ++ zLnk[got] = 0; ++ if( zLnk[0]=='/' ){ ++ pPath->nUsed = 0; ++ }else{ ++ pPath->nUsed -= nName + 1; ++ } ++ appendAllPathElements(pPath, zLnk); ++ } ++ } ++#endif ++} ++ ++/* ++** Append all path elements in zPath to the DbPath under construction. ++*/ ++static void appendAllPathElements( ++ DbPath *pPath, /* Path under construction, to which to append zName */ ++ const char *zPath /* Path to append to pPath. Is zero-terminated */ ++){ ++ int i = 0; ++ int j = 0; ++ do{ ++ while( zPath[i] && zPath[i]!='/' ){ i++; } ++ if( i>j ){ ++ appendOnePathElement(pPath, &zPath[j], i-j); ++ } ++ j = i+1; ++ }while( zPath[i++] ); + } + + /* +@@ -39889,86 +45555,27 @@ static int unixFullPathname( + int nOut, /* Size of output buffer in bytes */ + char *zOut /* Output buffer */ + ){ +-#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT) +- return mkFullPathname(zPath, zOut, nOut); +-#else +- int rc = SQLITE_OK; +- int nByte; +- int nLink = 0; /* Number of symbolic links followed so far */ +- const char *zIn = zPath; /* Input path for each iteration of loop */ +- char *zDel = 0; +- +- assert( pVfs->mxPathname==MAX_PATHNAME ); ++ DbPath path; + UNUSED_PARAMETER(pVfs); +- +- /* It's odd to simulate an io-error here, but really this is just +- ** using the io-error infrastructure to test that SQLite handles this +- ** function failing. This function could fail if, for example, the +- ** current working directory has been unlinked. +- */ +- SimulateIOError( return SQLITE_ERROR ); +- +- do { +- +- /* Call stat() on path zIn. Set bLink to true if the path is a symbolic +- ** link, or false otherwise. */ +- int bLink = 0; +- struct stat buf; +- if( osLstat(zIn, &buf)!=0 ){ +- if( errno!=ENOENT ){ +- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); +- } +- }else{ +- bLink = S_ISLNK(buf.st_mode); +- } +- +- if( bLink ){ +- nLink++; +- if( zDel==0 ){ +- zDel = sqlite3_malloc(nOut); +- if( zDel==0 ) rc = SQLITE_NOMEM_BKPT; +- }else if( nLink>=SQLITE_MAX_SYMLINKS ){ +- rc = SQLITE_CANTOPEN_BKPT; +- } +- +- if( rc==SQLITE_OK ){ +- nByte = osReadlink(zIn, zDel, nOut-1); +- if( nByte<0 ){ +- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); +- }else{ +- if( zDel[0]!='/' ){ +- int n; +- for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); +- if( nByte+n+1>nOut ){ +- rc = SQLITE_CANTOPEN_BKPT; +- }else{ +- memmove(&zDel[n], zDel, nByte+1); +- memcpy(zDel, zIn, n); +- nByte += n; +- } +- } +- zDel[nByte] = '\0'; +- } +- } +- +- zIn = zDel; +- } +- +- assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' ); +- if( rc==SQLITE_OK && zIn!=zOut ){ +- rc = mkFullPathname(zIn, zOut, nOut); ++ path.rc = 0; ++ path.nUsed = 0; ++ path.nSymlink = 0; ++ path.nOut = nOut; ++ path.zOut = zOut; ++ if( zPath[0]!='/' ){ ++ char zPwd[SQLITE_MAX_PATHLEN+2]; ++ if( osGetcwd(zPwd, sizeof(zPwd)-2)==0 ){ ++ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); + } +- if( bLink==0 ) break; +- zIn = zOut; +- }while( rc==SQLITE_OK ); +- +- sqlite3_free(zDel); +- if( rc==SQLITE_OK && nLink ) rc = SQLITE_OK_SYMLINK; +- return rc; +-#endif /* HAVE_READLINK && HAVE_LSTAT */ ++ appendAllPathElements(&path, zPwd); ++ } ++ appendAllPathElements(&path, zPath); ++ zOut[path.nUsed] = 0; ++ if( path.rc || path.nUsed<2 ) return SQLITE_CANTOPEN_BKPT; ++ if( path.nSymlink ) return SQLITE_OK_SYMLINK; ++ return SQLITE_OK; + } + +- + #ifndef SQLITE_OMIT_LOAD_EXTENSION + /* + ** Interfaces for opening a shared library, finding entry points +@@ -40082,16 +45689,22 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ + ** than the argument. + */ + static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ +-#if OS_VXWORKS ++#if !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP+0 + struct timespec sp; +- + sp.tv_sec = microseconds / 1000000; + sp.tv_nsec = (microseconds % 1000000) * 1000; ++ ++ /* Almost all modern unix systems support nanosleep(). But if you are ++ ** compiling for one of the rare exceptions, you can use ++ ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if ++ ** usleep() is available) in order to bypass the use of nanosleep() */ + nanosleep(&sp, NULL); ++ + UNUSED_PARAMETER(NotUsed); + return microseconds; + #elif defined(HAVE_USLEEP) && HAVE_USLEEP +- usleep(microseconds); ++ if( microseconds>=1000000 ) sleep(microseconds/1000000); ++ if( microseconds%1000000 ) usleep(microseconds%1000000); + UNUSED_PARAMETER(NotUsed); + return microseconds; + #else +@@ -40664,7 +46277,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ + + if( nTries==1 ){ + conchModTime = buf.st_mtimespec; +- usleep(500000); /* wait 0.5 sec and try the lock again*/ ++ unixSleep(0,500000); /* wait 0.5 sec and try the lock again*/ + continue; + } + +@@ -40690,7 +46303,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ + /* don't break the lock on short read or a version mismatch */ + return SQLITE_BUSY; + } +- usleep(10000000); /* wait 10 sec and try the lock again */ ++ unixSleep(0,10000000); /* wait 10 sec and try the lock again */ + continue; + } + +@@ -41463,9 +47076,39 @@ SQLITE_API int sqlite3_os_init(void){ + + /* Register all VFSes defined in the aVfs[] array */ + for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ ++#ifdef SQLITE_DEFAULT_UNIX_VFS ++ sqlite3_vfs_register(&aVfs[i], ++ 0==strcmp(aVfs[i].zName,SQLITE_DEFAULT_UNIX_VFS)); ++#else + sqlite3_vfs_register(&aVfs[i], i==0); ++#endif + } ++#ifdef SQLITE_OS_KV_OPTIONAL ++ sqlite3KvvfsInit(); ++#endif + unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); ++ ++#ifndef SQLITE_OMIT_WAL ++ /* Validate lock assumptions */ ++ assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */ ++ assert( UNIX_SHM_BASE==120 ); /* Start of locking area */ ++ /* Locks: ++ ** WRITE UNIX_SHM_BASE 120 ++ ** CKPT UNIX_SHM_BASE+1 121 ++ ** RECOVER UNIX_SHM_BASE+2 122 ++ ** READ-0 UNIX_SHM_BASE+3 123 ++ ** READ-1 UNIX_SHM_BASE+4 124 ++ ** READ-2 UNIX_SHM_BASE+5 125 ++ ** READ-3 UNIX_SHM_BASE+6 126 ++ ** READ-4 UNIX_SHM_BASE+7 127 ++ ** DMS UNIX_SHM_BASE+8 128 ++ */ ++ assert( UNIX_SHM_DMS==128 ); /* Byte offset of the deadman-switch */ ++#endif ++ ++ /* Initialize temp file dir array. */ ++ unixTempFileInit(); ++ + return SQLITE_OK; + } + +@@ -41505,205 +47148,7 @@ SQLITE_API int sqlite3_os_end(void){ + /* + ** Include code that is common to all os_*.c files + */ +-/************** Include os_common.h in the middle of os_win.c ****************/ +-/************** Begin file os_common.h ***************************************/ +-/* +-** 2004 May 22 +-** +-** 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 contains macros and a little bit of code that is common to +-** all of the platform-specific files (os_*.c) and is #included into those +-** files. +-** +-** This file should be #included by the os_*.c files only. It is not a +-** general purpose header file. +-*/ +-#ifndef _OS_COMMON_H_ +-#define _OS_COMMON_H_ +- +-/* +-** At least two bugs have slipped in because we changed the MEMORY_DEBUG +-** macro to SQLITE_DEBUG and some older makefiles have not yet made the +-** switch. The following code should catch this problem at compile-time. +-*/ +-#ifdef MEMORY_DEBUG +-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." +-#endif +- +-/* +-** Macros for performance tracing. Normally turned off. Only works +-** on i486 hardware. +-*/ +-#ifdef SQLITE_PERFORMANCE_TRACE +- +-/* +-** hwtime.h contains inline assembler code for implementing +-** high-performance timing routines. +-*/ +-/************** Include hwtime.h in the middle of os_common.h ****************/ +-/************** Begin file hwtime.h ******************************************/ +-/* +-** 2008 May 27 +-** +-** 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 contains inline asm code for retrieving "high-performance" +-** counters for x86 and x86_64 class CPUs. +-*/ +-#ifndef SQLITE_HWTIME_H +-#define SQLITE_HWTIME_H +- +-/* +-** The following routine only works on pentium-class (or newer) processors. +-** It uses the RDTSC opcode to read the cycle count value out of the +-** processor and returns that value. This can be used for high-res +-** profiling. +-*/ +-#if !defined(__STRICT_ANSI__) && \ +- (defined(__GNUC__) || defined(_MSC_VER)) && \ +- (defined(i386) || defined(__i386__) || defined(_M_IX86)) +- +- #if defined(__GNUC__) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned int lo, hi; +- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +- return (sqlite_uint64)hi << 32 | lo; +- } +- +- #elif defined(_MSC_VER) +- +- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ +- __asm { +- rdtsc +- ret ; return value at EDX:EAX +- } +- } +- +- #endif +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long val; +- __asm__ __volatile__ ("rdtsc" : "=A" (val)); +- return val; +- } +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long long retval; +- unsigned long junk; +- __asm__ __volatile__ ("\n\ +- 1: mftbu %1\n\ +- mftb %L0\n\ +- mftbu %0\n\ +- cmpw %0,%1\n\ +- bne 1b" +- : "=r" (retval), "=r" (junk)); +- return retval; +- } +- +-#else +- +- /* +- ** asm() is needed for hardware timing support. Without asm(), +- ** disable the sqlite3Hwtime() routine. +- ** +- ** sqlite3Hwtime() is only used for some obscure debugging +- ** and analysis configurations, not in any deliverable, so this +- ** should not be a great loss. +- */ +-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } +- +-#endif +- +-#endif /* !defined(SQLITE_HWTIME_H) */ +- +-/************** End of hwtime.h **********************************************/ +-/************** Continuing where we left off in os_common.h ******************/ +- +-static sqlite_uint64 g_start; +-static sqlite_uint64 g_elapsed; +-#define TIMER_START g_start=sqlite3Hwtime() +-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start +-#define TIMER_ELAPSED g_elapsed +-#else +-#define TIMER_START +-#define TIMER_END +-#define TIMER_ELAPSED ((sqlite_uint64)0) +-#endif +- +-/* +-** If we compile with the SQLITE_TEST macro set, then the following block +-** of code will give us the ability to simulate a disk I/O error. This +-** is used for testing the I/O recovery logic. +-*/ +-#if defined(SQLITE_TEST) +-SQLITE_API extern int sqlite3_io_error_hit; +-SQLITE_API extern int sqlite3_io_error_hardhit; +-SQLITE_API extern int sqlite3_io_error_pending; +-SQLITE_API extern int sqlite3_io_error_persist; +-SQLITE_API extern int sqlite3_io_error_benign; +-SQLITE_API extern int sqlite3_diskfull_pending; +-SQLITE_API extern int sqlite3_diskfull; +-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) +-#define SimulateIOError(CODE) \ +- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ +- || sqlite3_io_error_pending-- == 1 ) \ +- { local_ioerr(); CODE; } +-static void local_ioerr(){ +- IOTRACE(("IOERR\n")); +- sqlite3_io_error_hit++; +- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; +-} +-#define SimulateDiskfullError(CODE) \ +- if( sqlite3_diskfull_pending ){ \ +- if( sqlite3_diskfull_pending == 1 ){ \ +- local_ioerr(); \ +- sqlite3_diskfull = 1; \ +- sqlite3_io_error_hit = 1; \ +- CODE; \ +- }else{ \ +- sqlite3_diskfull_pending--; \ +- } \ +- } +-#else +-#define SimulateIOErrorBenign(X) +-#define SimulateIOError(A) +-#define SimulateDiskfullError(A) +-#endif /* defined(SQLITE_TEST) */ +- +-/* +-** When testing, keep a count of the number of open files. +-*/ +-#if defined(SQLITE_TEST) +-SQLITE_API extern int sqlite3_open_file_count; +-#define OpenCounter(X) sqlite3_open_file_count+=(X) +-#else +-#define OpenCounter(X) +-#endif /* defined(SQLITE_TEST) */ +- +-#endif /* !defined(_OS_COMMON_H_) */ +- +-/************** End of os_common.h *******************************************/ +-/************** Continuing where we left off in os_win.c *********************/ ++/* #include "os_common.h" */ + + /* + ** Include the header file for the Windows VFS. +@@ -41972,8 +47417,18 @@ struct winFile { + sqlite3_int64 mmapSize; /* Size of mapped region */ + sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ + #endif ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ DWORD iBusyTimeout; /* Wait this many millisec on locks */ ++ int bBlockOnConnect; ++#endif + }; + ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++# define winFileBusyTimeout(pDbFd) pDbFd->iBusyTimeout ++#else ++# define winFileBusyTimeout(pDbFd) 0 ++#endif ++ + /* + ** The winVfsAppData structure is used for the pAppData member for all of the + ** Win32 VFS variants. +@@ -42292,7 +47747,7 @@ static struct win_syscall { + { "FileTimeToLocalFileTime", (SYSCALL)0, 0 }, + #endif + +-#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \ ++#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(const FILETIME*, \ + LPFILETIME))aSyscall[11].pCurrent) + + #if SQLITE_OS_WINCE +@@ -42301,7 +47756,7 @@ static struct win_syscall { + { "FileTimeToSystemTime", (SYSCALL)0, 0 }, + #endif + +-#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \ ++#define osFileTimeToSystemTime ((BOOL(WINAPI*)(const FILETIME*, \ + LPSYSTEMTIME))aSyscall[12].pCurrent) + + { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 }, +@@ -42407,6 +47862,12 @@ static struct win_syscall { + #define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ + LPWSTR*))aSyscall[25].pCurrent) + ++/* ++** For GetLastError(), MSDN says: ++** ++** Minimum supported client: Windows XP [desktop apps | UWP apps] ++** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] ++*/ + { "GetLastError", (SYSCALL)GetLastError, 0 }, + + #define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) +@@ -42575,7 +48036,7 @@ static struct win_syscall { + { "LockFile", (SYSCALL)0, 0 }, + #endif + +-#ifndef osLockFile ++#if !defined(osLockFile) && defined(SQLITE_WIN32_HAS_ANSI) + #define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + DWORD))aSyscall[47].pCurrent) + #endif +@@ -42639,7 +48100,7 @@ static struct win_syscall { + + { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, + +-#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ ++#define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \ + LPFILETIME))aSyscall[56].pCurrent) + + #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +@@ -42648,7 +48109,7 @@ static struct win_syscall { + { "UnlockFile", (SYSCALL)0, 0 }, + #endif + +-#ifndef osUnlockFile ++#if !defined(osUnlockFile) && defined(SQLITE_WIN32_HAS_ANSI) + #define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + DWORD))aSyscall[57].pCurrent) + #endif +@@ -42689,11 +48150,13 @@ static struct win_syscall { + #define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ + DWORD,DWORD))aSyscall[62].pCurrent) + +-#if !SQLITE_OS_WINRT ++/* ++** For WaitForSingleObject(), MSDN says: ++** ++** Minimum supported client: Windows XP [desktop apps | UWP apps] ++** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] ++*/ + { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, +-#else +- { "WaitForSingleObject", (SYSCALL)0, 0 }, +-#endif + + #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ + DWORD))aSyscall[63].pCurrent) +@@ -42840,11 +48303,102 @@ static struct win_syscall { + #define osFlushViewOfFile \ + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) + ++/* ++** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() ++** to implement blocking locks with timeouts. MSDN says: ++** ++** Minimum supported client: Windows XP [desktop apps | UWP apps] ++** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] ++*/ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ { "CreateEvent", (SYSCALL)CreateEvent, 0 }, ++#else ++ { "CreateEvent", (SYSCALL)0, 0 }, ++#endif ++ ++#define osCreateEvent ( \ ++ (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ ++ aSyscall[80].pCurrent \ ++) ++ ++/* ++** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() ++** for the case where a timeout expires and a lock request must be ++** cancelled. ++** ++** Minimum supported client: Windows XP [desktop apps | UWP apps] ++** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] ++*/ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ { "CancelIo", (SYSCALL)CancelIo, 0 }, ++#else ++ { "CancelIo", (SYSCALL)0, 0 }, ++#endif ++ ++#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent) ++ ++#if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32) ++ { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 }, ++#else ++ { "GetModuleHandleW", (SYSCALL)0, 0 }, ++#endif ++ ++#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent) ++ ++#ifndef _WIN32 ++ { "getenv", (SYSCALL)getenv, 0 }, ++#else ++ { "getenv", (SYSCALL)0, 0 }, ++#endif ++ ++#define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent) ++ ++#ifndef _WIN32 ++ { "getcwd", (SYSCALL)getcwd, 0 }, ++#else ++ { "getcwd", (SYSCALL)0, 0 }, ++#endif ++ ++#define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent) ++ ++#ifndef _WIN32 ++ { "readlink", (SYSCALL)readlink, 0 }, ++#else ++ { "readlink", (SYSCALL)0, 0 }, ++#endif ++ ++#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent) ++ ++#ifndef _WIN32 ++ { "lstat", (SYSCALL)lstat, 0 }, ++#else ++ { "lstat", (SYSCALL)0, 0 }, ++#endif ++ ++#define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent) ++ ++#ifndef _WIN32 ++ { "__errno", (SYSCALL)__errno, 0 }, ++#else ++ { "__errno", (SYSCALL)0, 0 }, ++#endif ++ ++#define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)()) ++ ++#ifndef _WIN32 ++ { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 }, ++#else ++ { "cygwin_conv_path", (SYSCALL)0, 0 }, ++#endif ++ ++#define osCygwin_conv_path ((size_t(*)(unsigned int, \ ++ const void *, void *, size_t))aSyscall[88].pCurrent) ++ + }; /* End of the overrideable system calls */ + + /* + ** This is the xSetSystemCall() method of sqlite3_vfs for all of the +-** "win32" VFSes. Return SQLITE_OK opon successfully updating the ++** "win32" VFSes. Return SQLITE_OK upon successfully updating the + ** system call pointer, or SQLITE_NOTFOUND if there is no configurable + ** system call named zName. + */ +@@ -43013,6 +48567,7 @@ SQLITE_API int sqlite3_win32_reset_heap(){ + } + #endif /* SQLITE_WIN32_MALLOC */ + ++#ifdef _WIN32 + /* + ** This function outputs the specified (ANSI) string to the Win32 debugger + ** (if available). +@@ -43055,6 +48610,7 @@ SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ + } + #endif + } ++#endif /* _WIN32 */ + + /* + ** The following routine suspends the current thread for at least ms +@@ -43138,7 +48694,9 @@ SQLITE_API int sqlite3_win32_is_nt(void){ + } + return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; + #elif SQLITE_TEST +- return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; ++ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2 ++ || osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ++ ; + #else + /* + ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are +@@ -43353,6 +48911,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){ + } + #endif /* SQLITE_WIN32_MALLOC */ + ++#ifdef _WIN32 + /* + ** Convert a UTF-8 string to Microsoft Unicode. + ** +@@ -43378,6 +48937,7 @@ static LPWSTR winUtf8ToUnicode(const char *zText){ + } + return zWideText; + } ++#endif /* _WIN32 */ + + /* + ** Convert a Microsoft Unicode string to UTF-8. +@@ -43412,28 +48972,29 @@ static char *winUnicodeToUtf8(LPCWSTR zWideText){ + ** Space to hold the returned string is obtained from sqlite3_malloc(). + */ + static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){ +- int nByte; ++ int nWideChar; + LPWSTR zMbcsText; + int codepage = useAnsi ? CP_ACP : CP_OEMCP; + +- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL, +- 0)*sizeof(WCHAR); +- if( nByte==0 ){ ++ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, NULL, ++ 0); ++ if( nWideChar==0 ){ + return 0; + } +- zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) ); ++ zMbcsText = sqlite3MallocZero( nWideChar*sizeof(WCHAR) ); + if( zMbcsText==0 ){ + return 0; + } +- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText, +- nByte); +- if( nByte==0 ){ ++ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText, ++ nWideChar); ++ if( nWideChar==0 ){ + sqlite3_free(zMbcsText); + zMbcsText = 0; + } + return zMbcsText; + } + ++#ifdef _WIN32 + /* + ** Convert a Microsoft Unicode string to a multi-byte character string, + ** using the ANSI or OEM code page. +@@ -43461,6 +49022,7 @@ static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){ + } + return zText; + } ++#endif /* _WIN32 */ + + /* + ** Convert a multi-byte character string to UTF-8. +@@ -43480,6 +49042,7 @@ static char *winMbcsToUtf8(const char *zText, int useAnsi){ + return zTextUtf8; + } + ++#ifdef _WIN32 + /* + ** Convert a UTF-8 string to a multi-byte character string. + ** +@@ -43529,6 +49092,7 @@ SQLITE_API char *sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){ + #endif + return winUnicodeToUtf8(zWideText); + } ++#endif /* _WIN32 */ + + /* + ** This is a public wrapper for the winMbcsToUtf8() function. +@@ -43546,6 +49110,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){ + return winMbcsToUtf8(zText, osAreFileApisANSI()); + } + ++#ifdef _WIN32 + /* + ** This is a public wrapper for the winMbcsToUtf8() function. + */ +@@ -43603,10 +49168,12 @@ SQLITE_API int sqlite3_win32_set_directory8( + const char *zValue /* New value for directory being set or reset */ + ){ + char **ppDirectory = 0; ++ int rc; + #ifndef SQLITE_OMIT_AUTOINIT +- int rc = sqlite3_initialize(); ++ rc = sqlite3_initialize(); + if( rc ) return rc; + #endif ++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){ + ppDirectory = &sqlite3_data_directory; + }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){ +@@ -43621,14 +49188,19 @@ SQLITE_API int sqlite3_win32_set_directory8( + if( zValue && zValue[0] ){ + zCopy = sqlite3_mprintf("%s", zValue); + if ( zCopy==0 ){ +- return SQLITE_NOMEM_BKPT; ++ rc = SQLITE_NOMEM_BKPT; ++ goto set_directory8_done; + } + } + sqlite3_free(*ppDirectory); + *ppDirectory = zCopy; +- return SQLITE_OK; ++ rc = SQLITE_OK; ++ }else{ ++ rc = SQLITE_ERROR; + } +- return SQLITE_ERROR; ++set_directory8_done: ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); ++ return rc; + } + + /* +@@ -43663,6 +49235,7 @@ SQLITE_API int sqlite3_win32_set_directory( + ){ + return sqlite3_win32_set_directory16(type, zValue); + } ++#endif /* _WIN32 */ + + /* + ** The return value of winGetLastErrorMsg +@@ -44211,13 +49784,98 @@ static BOOL winLockFile( + ovlp.Offset = offsetLow; + ovlp.OffsetHigh = offsetHigh; + return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); ++#ifdef SQLITE_WIN32_HAS_ANSI + }else{ + return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, + numBytesHigh); ++#endif + } + #endif + } + ++/* ++** Lock a region of nByte bytes starting at offset offset of file hFile. ++** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock ++** otherwise. If nMs is greater than zero and the lock cannot be obtained ++** immediately, block for that many ms before giving up. ++** ++** This function returns SQLITE_OK if the lock is obtained successfully. If ++** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or ++** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR. ++*/ ++static int winHandleLockTimeout( ++ HANDLE hFile, ++ DWORD offset, ++ DWORD nByte, ++ int bExcl, ++ DWORD nMs ++){ ++ DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0); ++ int rc = SQLITE_OK; ++ BOOL ret; ++ ++ if( !osIsNT() ){ ++ ret = winLockFile(&hFile, flags, offset, 0, nByte, 0); ++ }else{ ++ OVERLAPPED ovlp; ++ memset(&ovlp, 0, sizeof(OVERLAPPED)); ++ ovlp.Offset = offset; ++ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ if( nMs!=0 ){ ++ flags &= ~LOCKFILE_FAIL_IMMEDIATELY; ++ } ++ ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL); ++ if( ovlp.hEvent==NULL ){ ++ return SQLITE_IOERR_LOCK; ++ } ++#endif ++ ++ ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp); ++ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was ++ ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to ++ ** LockFileEx() may fail because the request is still pending. This can ++ ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified. ++ ** ++ ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags ++ ** passed to LockFileEx(). In this case, if the operation is pending, ++ ** block indefinitely until it is finished. ++ ** ++ ** Otherwise, wait for up to nMs ms for the operation to finish. nMs ++ ** may be set to INFINITE. ++ */ ++ if( !ret && GetLastError()==ERROR_IO_PENDING ){ ++ DWORD nDelay = (nMs==0 ? INFINITE : nMs); ++ DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay); ++ if( res==WAIT_OBJECT_0 ){ ++ ret = TRUE; ++ }else if( res==WAIT_TIMEOUT ){ ++#if SQLITE_ENABLE_SETLK_TIMEOUT==1 ++ rc = SQLITE_BUSY_TIMEOUT; ++#else ++ rc = SQLITE_BUSY; ++#endif ++ }else{ ++ /* Some other error has occurred */ ++ rc = SQLITE_IOERR_LOCK; ++ } ++ ++ /* If it is still pending, cancel the LockFileEx() call. */ ++ osCancelIo(hFile); ++ } ++ ++ osCloseHandle(ovlp.hEvent); ++#endif ++ } ++ ++ if( rc==SQLITE_OK && !ret ){ ++ rc = SQLITE_BUSY; ++ } ++ return rc; ++} ++ + /* + ** Unlock a file region. + */ +@@ -44242,13 +49900,23 @@ static BOOL winUnlockFile( + ovlp.Offset = offsetLow; + ovlp.OffsetHigh = offsetHigh; + return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); ++#ifdef SQLITE_WIN32_HAS_ANSI + }else{ + return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, + numBytesHigh); ++#endif + } + #endif + } + ++/* ++** Remove an nByte lock starting at offset iOff from HANDLE h. ++*/ ++static int winHandleUnlock(HANDLE h, int iOff, int nByte){ ++ BOOL ret = winUnlockFile(&h, iOff, 0, nByte, 0); ++ return (ret ? SQLITE_OK : SQLITE_IOERR_UNLOCK); ++} ++ + /***************************************************************************** + ** The next group of routines implement the I/O methods specified + ** by the sqlite3_io_methods object. +@@ -44262,66 +49930,70 @@ static BOOL winUnlockFile( + #endif + + /* +-** Move the current position of the file handle passed as the first +-** argument to offset iOffset within the file. If successful, return 0. +-** Otherwise, set pFile->lastErrno and return non-zero. ++** Seek the file handle h to offset nByte of the file. ++** ++** If successful, return SQLITE_OK. Or, if an error occurs, return an SQLite ++** error code. + */ +-static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ ++static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){ ++ int rc = SQLITE_OK; /* Return value */ ++ + #if !SQLITE_OS_WINRT + LONG upperBits; /* Most sig. 32 bits of new offset */ + LONG lowerBits; /* Least sig. 32 bits of new offset */ + DWORD dwRet; /* Value returned by SetFilePointer() */ +- DWORD lastErrno; /* Value returned by GetLastError() */ +- +- OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); + + upperBits = (LONG)((iOffset>>32) & 0x7fffffff); + lowerBits = (LONG)(iOffset & 0xffffffff); + ++ dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN); ++ + /* API oddity: If successful, SetFilePointer() returns a dword + ** containing the lower 32-bits of the new file-offset. Or, if it fails, + ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, + ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine + ** whether an error has actually occurred, it is also necessary to call +- ** GetLastError(). +- */ +- dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); +- +- if( (dwRet==INVALID_SET_FILE_POINTER +- && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ +- pFile->lastErrno = lastErrno; +- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, +- "winSeekFile", pFile->zPath); +- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); +- return 1; ++ ** GetLastError(). */ ++ if( dwRet==INVALID_SET_FILE_POINTER ){ ++ DWORD lastErrno = osGetLastError(); ++ if( lastErrno!=NO_ERROR ){ ++ rc = SQLITE_IOERR_SEEK; ++ } + } +- +- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); +- return 0; + #else +- /* +- ** Same as above, except that this implementation works for WinRT. +- */ +- ++ /* This implementation works for WinRT. */ + LARGE_INTEGER x; /* The new offset */ + BOOL bRet; /* Value returned by SetFilePointerEx() */ + + x.QuadPart = iOffset; +- bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); ++ bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN); + + if(!bRet){ +- pFile->lastErrno = osGetLastError(); +- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, +- "winSeekFile", pFile->zPath); +- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); +- return 1; ++ rc = SQLITE_IOERR_SEEK; + } +- +- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); +- return 0; + #endif ++ ++ OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc))); ++ return rc; + } + ++/* ++** Move the current position of the file handle passed as the first ++** argument to offset iOffset within the file. If successful, return 0. ++** Otherwise, set pFile->lastErrno and return non-zero. ++*/ ++static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ ++ int rc; ++ ++ rc = winHandleSeek(pFile->h, iOffset); ++ if( rc!=SQLITE_OK ){ ++ pFile->lastErrno = osGetLastError(); ++ winLogError(rc, pFile->lastErrno, "winSeekFile", pFile->zPath); ++ } ++ return rc; ++} ++ ++ + #if SQLITE_MAX_MMAP_SIZE>0 + /* Forward references to VFS helper methods used for memory mapped files */ + static int winMapfile(winFile*, sqlite3_int64); +@@ -44417,7 +50089,7 @@ static int winRead( + pFile->h, pBuf, amt, offset, pFile->locktype)); + + #if SQLITE_MAX_MMAP_SIZE>0 +- /* Deal with as much of this read request as possible by transfering ++ /* Deal with as much of this read request as possible by transferring + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ +@@ -44495,7 +50167,7 @@ static int winWrite( + pFile->h, pBuf, amt, offset, pFile->locktype)); + + #if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 +- /* Deal with as much of this write request as possible by transfering ++ /* Deal with as much of this write request as possible by transferring + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ +@@ -44581,6 +50253,60 @@ static int winWrite( + return SQLITE_OK; + } + ++/* ++** Truncate the file opened by handle h to nByte bytes in size. ++*/ ++static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){ ++ int rc = SQLITE_OK; /* Return code */ ++ rc = winHandleSeek(h, nByte); ++ if( rc==SQLITE_OK ){ ++ if( 0==osSetEndOfFile(h) ){ ++ rc = SQLITE_IOERR_TRUNCATE; ++ } ++ } ++ return rc; ++} ++ ++/* ++** Determine the size in bytes of the file opened by the handle passed as ++** the first argument. ++*/ ++static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){ ++ int rc = SQLITE_OK; ++ ++#if SQLITE_OS_WINRT ++ FILE_STANDARD_INFO info; ++ BOOL b; ++ b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info)); ++ if( b ){ ++ *pnByte = info.EndOfFile.QuadPart; ++ }else{ ++ rc = SQLITE_IOERR_FSTAT; ++ } ++#else ++ DWORD upperBits = 0; ++ DWORD lowerBits = 0; ++ ++ assert( pnByte ); ++ lowerBits = osGetFileSize(h, &upperBits); ++ *pnByte = (((sqlite3_int64)upperBits)<<32) + lowerBits; ++ if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){ ++ rc = SQLITE_IOERR_FSTAT; ++ } ++#endif ++ ++ return rc; ++} ++ ++/* ++** Close the handle passed as the only argument. ++*/ ++static void winHandleClose(HANDLE h){ ++ if( h!=INVALID_HANDLE_VALUE ){ ++ osCloseHandle(h); ++ } ++} ++ + /* + ** Truncate an open file to a specified size + */ +@@ -44605,7 +50331,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ + ** all references to memory-mapped content are closed. That is doable, + ** but involves adding a few branches in the common write code path which + ** could slow down normal operations slightly. Hence, we have decided for +- ** now to simply make trancations a no-op if there are pending reads. We ++ ** now to simply make transactions a no-op if there are pending reads. We + ** can maybe revisit this decision in the future. + */ + return SQLITE_OK; +@@ -44664,7 +50390,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ + #ifdef SQLITE_TEST + /* + ** Count the number of fullsyncs and normal syncs. This is used to test +-** that syncs and fullsyncs are occuring at the right times. ++** that syncs and fullsyncs are occurring at the right times. + */ + SQLITE_API int sqlite3_sync_count = 0; + SQLITE_API int sqlite3_fullsync_count = 0; +@@ -44836,8 +50562,9 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ + ** Different API routines are called depending on whether or not this + ** is Win9x or WinNT. + */ +-static int winGetReadLock(winFile *pFile){ ++static int winGetReadLock(winFile *pFile, int bBlock){ + int res; ++ DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0); + OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); + if( osIsNT() ){ + #if SQLITE_OS_WINCE +@@ -44847,7 +50574,7 @@ static int winGetReadLock(winFile *pFile){ + */ + res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); + #else +- res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, ++ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0, + SHARED_SIZE, 0); + #endif + } +@@ -44856,7 +50583,7 @@ static int winGetReadLock(winFile *pFile){ + int lk; + sqlite3_randomness(sizeof(lk), &lk); + pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); +- res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, ++ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask, + SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); + } + #endif +@@ -44951,46 +50678,62 @@ static int winLock(sqlite3_file *id, int locktype){ + assert( locktype!=PENDING_LOCK ); + assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); + +- /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or ++ /* Lock the PENDING_LOCK byte if we need to acquire an EXCLUSIVE lock or + ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of + ** the PENDING_LOCK byte is temporary. + */ + newLocktype = pFile->locktype; +- if( pFile->locktype==NO_LOCK +- || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK) ++ if( locktype==SHARED_LOCK ++ || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) + ){ + int cnt = 3; +- while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, +- PENDING_BYTE, 0, 1, 0))==0 ){ ++ ++ /* Flags for the LockFileEx() call. This should be an exclusive lock if ++ ** this call is to obtain EXCLUSIVE, or a shared lock if this call is to ++ ** obtain SHARED. */ ++ int flags = LOCKFILE_FAIL_IMMEDIATELY; ++ if( locktype==EXCLUSIVE_LOCK ){ ++ flags |= LOCKFILE_EXCLUSIVE_LOCK; ++ } ++ while( cnt>0 ){ + /* Try 3 times to get the pending lock. This is needed to work + ** around problems caused by indexing and/or anti-virus software on + ** Windows systems. ++ ** + ** If you are using this code as a model for alternative VFSes, do not +- ** copy this retry logic. It is a hack intended for Windows only. +- */ ++ ** copy this retry logic. It is a hack intended for Windows only. */ ++ res = winLockFile(&pFile->h, flags, PENDING_BYTE, 0, 1, 0); ++ if( res ) break; ++ + lastErrno = osGetLastError(); + OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", +- pFile->h, cnt, res)); ++ pFile->h, cnt, res ++ )); ++ + if( lastErrno==ERROR_INVALID_HANDLE ){ + pFile->lastErrno = lastErrno; + rc = SQLITE_IOERR_LOCK; + OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", +- pFile->h, cnt, sqlite3ErrName(rc))); ++ pFile->h, cnt, sqlite3ErrName(rc) ++ )); + return rc; + } +- if( cnt ) sqlite3_win32_sleep(1); ++ ++ cnt--; ++ if( cnt>0 ) sqlite3_win32_sleep(1); + } + gotPendingLock = res; +- if( !res ){ +- lastErrno = osGetLastError(); +- } + } + + /* Acquire a shared lock + */ + if( locktype==SHARED_LOCK && res ){ + assert( pFile->locktype==NO_LOCK ); +- res = winGetReadLock(pFile); ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ res = winGetReadLock(pFile, pFile->bBlockOnConnect); ++#else ++ res = winGetReadLock(pFile, 0); ++#endif + if( res ){ + newLocktype = SHARED_LOCK; + }else{ +@@ -45021,14 +50764,14 @@ static int winLock(sqlite3_file *id, int locktype){ + */ + if( locktype==EXCLUSIVE_LOCK && res ){ + assert( pFile->locktype>=SHARED_LOCK ); +- res = winUnlockReadLock(pFile); ++ (void)winUnlockReadLock(pFile); + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, + SHARED_SIZE, 0); + if( res ){ + newLocktype = EXCLUSIVE_LOCK; + }else{ + lastErrno = osGetLastError(); +- winGetReadLock(pFile); ++ winGetReadLock(pFile, 0); + } + } + +@@ -45108,7 +50851,7 @@ static int winUnlock(sqlite3_file *id, int locktype){ + type = pFile->locktype; + if( type>=EXCLUSIVE_LOCK ){ + winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); +- if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ ++ if( locktype==SHARED_LOCK && !winGetReadLock(pFile, 0) ){ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), +@@ -45277,6 +51020,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ + return SQLITE_OK; + } + #endif ++ case SQLITE_FCNTL_NULL_IO: { ++ (void)osCloseHandle(pFile->h); ++ pFile->h = NULL; ++ return SQLITE_OK; ++ } + case SQLITE_FCNTL_TEMPFILENAME: { + char *zTFile = 0; + int rc = winGetTempname(pFile->pVfs, &zTFile); +@@ -45313,6 +51061,28 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ + return rc; + } + #endif ++ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ case SQLITE_FCNTL_LOCK_TIMEOUT: { ++ int iOld = pFile->iBusyTimeout; ++ int iNew = *(int*)pArg; ++#if SQLITE_ENABLE_SETLK_TIMEOUT==1 ++ pFile->iBusyTimeout = (iNew < 0) ? INFINITE : (DWORD)iNew; ++#elif SQLITE_ENABLE_SETLK_TIMEOUT==2 ++ pFile->iBusyTimeout = (DWORD)(!!iNew); ++#else ++# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" ++#endif ++ *(int*)pArg = iOld; ++ return SQLITE_OK; ++ } ++ case SQLITE_FCNTL_BLOCK_ON_CONNECT: { ++ int iNew = *(int*)pArg; ++ pFile->bBlockOnConnect = iNew; ++ return SQLITE_OK; ++ } ++#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ ++ + } + OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); + return SQLITE_NOTFOUND; +@@ -45338,7 +51108,7 @@ static int winSectorSize(sqlite3_file *id){ + */ + static int winDeviceCharacteristics(sqlite3_file *id){ + winFile *p = (winFile*)id; +- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | ++ return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ | + ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); + } + +@@ -45393,23 +51163,27 @@ static int winShmMutexHeld(void) { + ** + ** The following fields are read-only after the object is created: + ** +-** fid + ** zFilename + ** + ** Either winShmNode.mutex must be held or winShmNode.nRef==0 and + ** winShmMutexHeld() is true when reading or writing any other field + ** in this structure. + ** ++** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate ++** the *-shm file if the DMS-locking protocol demands it, and (c) map ++** regions of the *-shm file into memory using MapViewOfFile() or ++** similar. Other locks are taken by individual clients using the ++** winShm.hShm handles. + */ + struct winShmNode { + sqlite3_mutex *mutex; /* Mutex to access this object */ + char *zFilename; /* Name of the file */ +- winFile hFile; /* File handle from winOpen */ ++ HANDLE hSharedShm; /* File handle open on zFilename */ + ++ int isUnlocked; /* DMS lock has not yet been obtained */ ++ int isReadonly; /* True if read-only */ + int szRegion; /* Size of shared-memory regions */ + int nRegion; /* Size of array apRegion */ +- u8 isReadonly; /* True if read-only */ +- u8 isUnlocked; /* True if no DMS lock held */ + + struct ShmRegion { + HANDLE hMap; /* File handle from CreateFileMapping */ +@@ -45418,7 +51192,6 @@ struct winShmNode { + DWORD lastErrno; /* The Windows errno from the last I/O error */ + + int nRef; /* Number of winShm objects pointing to this */ +- winShm *pFirst; /* All winShm objects pointing to this */ + winShmNode *pNext; /* Next in list of all winShmNode objects */ + #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) + u8 nextShmId; /* Next available winShm.id value */ +@@ -45434,23 +51207,15 @@ static winShmNode *winShmNodeList = 0; + + /* + ** Structure used internally by this VFS to record the state of an +-** open shared memory connection. +-** +-** The following fields are initialized when this object is created and +-** are read-only thereafter: +-** +-** winShm.pShmNode +-** winShm.id +-** +-** All other fields are read/write. The winShm.pShmNode->mutex must be held +-** while accessing any read/write fields. ++** open shared memory connection. There is one such structure for each ++** winFile open on a wal mode database. + */ + struct winShm { + winShmNode *pShmNode; /* The underlying winShmNode object */ +- winShm *pNext; /* Next winShm with the same winShmNode */ +- u8 hasMutex; /* True if holding the winShmNode mutex */ + u16 sharedMask; /* Mask of shared locks held */ + u16 exclMask; /* Mask of exclusive locks held */ ++ HANDLE hShm; /* File-handle on *-shm file. For locking. */ ++ int bReadonly; /* True if hShm is opened read-only */ + #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) + u8 id; /* Id of this connection with its winShmNode */ + #endif +@@ -45462,50 +51227,6 @@ struct winShm { + #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ + #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ + +-/* +-** Apply advisory locks for all n bytes beginning at ofst. +-*/ +-#define WINSHM_UNLCK 1 +-#define WINSHM_RDLCK 2 +-#define WINSHM_WRLCK 3 +-static int winShmSystemLock( +- winShmNode *pFile, /* Apply locks to this open shared-memory segment */ +- int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */ +- int ofst, /* Offset to first byte to be locked/unlocked */ +- int nByte /* Number of bytes to lock or unlock */ +-){ +- int rc = 0; /* Result code form Lock/UnlockFileEx() */ +- +- /* Access to the winShmNode object is serialized by the caller */ +- assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) ); +- +- OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", +- pFile->hFile.h, lockType, ofst, nByte)); +- +- /* Release/Acquire the system-level lock */ +- if( lockType==WINSHM_UNLCK ){ +- rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); +- }else{ +- /* Initialize the locking parameters */ +- DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; +- if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; +- rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); +- } +- +- if( rc!= 0 ){ +- rc = SQLITE_OK; +- }else{ +- pFile->lastErrno = osGetLastError(); +- rc = SQLITE_BUSY; +- } +- +- OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", +- pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" : +- "winLockFile", pFile->lastErrno, sqlite3ErrName(rc))); +- +- return rc; +-} +- + /* Forward references to VFS methods */ + static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); + static int winDelete(sqlite3_vfs *,const char*,int); +@@ -45537,11 +51258,7 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ + osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); + UNUSED_VARIABLE_VALUE(bRc); + } +- if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ +- SimulateIOErrorBenign(1); +- winClose((sqlite3_file *)&p->hFile); +- SimulateIOErrorBenign(0); +- } ++ winHandleClose(p->hSharedShm); + if( deleteFlag ){ + SimulateIOErrorBenign(1); + sqlite3BeginBenignMalloc(); +@@ -45559,42 +51276,239 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ + } + + /* +-** The DMS lock has not yet been taken on shm file pShmNode. Attempt to +-** take it now. Return SQLITE_OK if successful, or an SQLite error +-** code otherwise. +-** +-** If the DMS cannot be locked because this is a readonly_shm=1 +-** connection and no other process already holds a lock, return +-** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. ++** The DMS lock has not yet been taken on the shm file associated with ++** pShmNode. Take the lock. Truncate the *-shm file if required. ++** Return SQLITE_OK if successful, or an SQLite error code otherwise. + */ +-static int winLockSharedMemory(winShmNode *pShmNode){ +- int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1); ++static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){ ++ HANDLE h = pShmNode->hSharedShm; ++ int rc = SQLITE_OK; + ++ assert( sqlite3_mutex_held(pShmNode->mutex) ); ++ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0); + if( rc==SQLITE_OK ){ ++ /* We have an EXCLUSIVE lock on the DMS byte. This means that this ++ ** is the first process to open the file. Truncate it to zero bytes ++ ** in this case. */ + if( pShmNode->isReadonly ){ +- pShmNode->isUnlocked = 1; +- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); +- return SQLITE_READONLY_CANTINIT; +- }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){ +- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); +- return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), +- "winLockSharedMemory", pShmNode->zFilename); ++ rc = SQLITE_READONLY_CANTINIT; ++ }else{ ++ rc = winHandleTruncate(h, 0); + } ++ ++ /* Release the EXCLUSIVE lock acquired above. */ ++ winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0); ++ }else if( (rc & 0xFF)==SQLITE_BUSY ){ ++ rc = SQLITE_OK; + } + + if( rc==SQLITE_OK ){ +- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); ++ /* Take a SHARED lock on the DMS byte. */ ++ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs); ++ if( rc==SQLITE_OK ){ ++ pShmNode->isUnlocked = 0; ++ } + } + +- return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1); ++ return rc; + } + ++ + /* +-** Open the shared-memory area associated with database file pDbFd. ++** Convert a UTF-8 filename into whatever form the underlying ++** operating system wants filenames in. Space to hold the result ++** is obtained from malloc and must be freed by the calling ++** function ++** ++** On Cygwin, 3 possible input forms are accepted: ++** - If the filename starts with ":/" or ":\", ++** it is converted to UTF-16 as-is. ++** - If the filename contains '/', it is assumed to be a ++** Cygwin absolute path, it is converted to a win32 ++** absolute path in UTF-16. ++** - Otherwise it must be a filename only, the win32 filename ++** is returned in UTF-16. ++** Note: If the function cygwin_conv_path() fails, only ++** UTF-8 -> UTF-16 conversion will be done. This can only ++** happen when the file path >32k, in which case winUtf8ToUnicode() ++** will fail too. ++*/ ++static void *winConvertFromUtf8Filename(const char *zFilename){ ++ void *zConverted = 0; ++ if( osIsNT() ){ ++#ifdef __CYGWIN__ ++ int nChar; ++ LPWSTR zWideFilename; ++ ++ if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename) ++ && winIsDirSep(zFilename[2])) ){ ++ i64 nByte; ++ int convertflag = CCP_POSIX_TO_WIN_W; ++ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE; ++ nByte = (i64)osCygwin_conv_path(convertflag, ++ zFilename, 0, 0); ++ if( nByte>0 ){ ++ zConverted = sqlite3MallocZero(12+(u64)nByte); ++ if ( zConverted==0 ){ ++ return zConverted; ++ } ++ zWideFilename = zConverted; ++ /* Filenames should be prefixed, except when converted ++ * full path already starts with "\\?\". */ ++ if( osCygwin_conv_path(convertflag, zFilename, ++ zWideFilename+4, nByte)==0 ){ ++ if( (convertflag&CCP_RELATIVE) ){ ++ memmove(zWideFilename, zWideFilename+4, nByte); ++ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){ ++ memcpy(zWideFilename, L"\\\\?\\", 8); ++ }else if( zWideFilename[6]!='?' ){ ++ memmove(zWideFilename+6, zWideFilename+4, nByte); ++ memcpy(zWideFilename, L"\\\\?\\UNC", 14); ++ }else{ ++ memmove(zWideFilename, zWideFilename+4, nByte); ++ } ++ return zConverted; ++ } ++ sqlite3_free(zConverted); ++ } ++ } ++ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); ++ if( nChar==0 ){ ++ return 0; ++ } ++ zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 ); ++ if( zWideFilename==0 ){ ++ return 0; ++ } ++ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, ++ zWideFilename, nChar); ++ if( nChar==0 ){ ++ sqlite3_free(zWideFilename); ++ zWideFilename = 0; ++ }else if( nChar>MAX_PATH ++ && winIsDriveLetterAndColon(zFilename) ++ && winIsDirSep(zFilename[2]) ){ ++ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR)); ++ zWideFilename[2] = '\\'; ++ memcpy(zWideFilename, L"\\\\?\\", 8); ++ }else if( nChar>MAX_PATH ++ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1]) ++ && zFilename[2] != '?' ){ ++ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR)); ++ memcpy(zWideFilename, L"\\\\?\\UNC", 14); ++ } ++ zConverted = zWideFilename; ++#else ++ zConverted = winUtf8ToUnicode(zFilename); ++#endif /* __CYGWIN__ */ ++ } ++#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32) ++ else{ ++ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); ++ } ++#endif ++ /* caller will handle out of memory */ ++ return zConverted; ++} ++ ++/* ++** This function is used to open a handle on a *-shm file. + ** +-** When opening a new shared-memory file, if no other instances of that +-** file are currently open, in this process or in other processes, then +-** the file must be truncated to zero length or have its header cleared. ++** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file ++** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not. ++*/ ++static int winHandleOpen( ++ const char *zUtf8, /* File to open */ ++ int *pbReadonly, /* IN/OUT: True for readonly handle */ ++ HANDLE *ph /* OUT: New HANDLE for file */ ++){ ++ int rc = SQLITE_OK; ++ void *zConverted = 0; ++ int bReadonly = *pbReadonly; ++ HANDLE h = INVALID_HANDLE_VALUE; ++ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ const DWORD flag_overlapped = FILE_FLAG_OVERLAPPED; ++#else ++ const DWORD flag_overlapped = 0; ++#endif ++ ++ /* Convert the filename to the system encoding. */ ++ zConverted = winConvertFromUtf8Filename(zUtf8); ++ if( zConverted==0 ){ ++ OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8)); ++ rc = SQLITE_IOERR_NOMEM_BKPT; ++ goto winopenfile_out; ++ } ++ ++ /* Ensure the file we are trying to open is not actually a directory. */ ++ if( winIsDir(zConverted) ){ ++ OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8)); ++ rc = SQLITE_CANTOPEN_ISDIR; ++ goto winopenfile_out; ++ } ++ ++ /* TODO: platforms. ++ ** TODO: retry-on-ioerr. ++ */ ++ if( osIsNT() ){ ++#if SQLITE_OS_WINRT ++ CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; ++ memset(&extendedParameters, 0, sizeof(extendedParameters)); ++ extendedParameters.dwSize = sizeof(extendedParameters); ++ extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; ++ extendedParameters.dwFileFlags = flag_overlapped; ++ extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; ++ h = osCreateFile2((LPCWSTR)zConverted, ++ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */ ++ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ ++ OPEN_ALWAYS, /* dwCreationDisposition */ ++ &extendedParameters ++ ); ++#else ++ h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */ ++ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ ++ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ ++ NULL, /* lpSecurityAttributes */ ++ OPEN_ALWAYS, /* dwCreationDisposition */ ++ FILE_ATTRIBUTE_NORMAL|flag_overlapped, ++ NULL ++ ); ++#endif ++ }else{ ++ /* Due to pre-processor directives earlier in this file, ++ ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */ ++#ifdef SQLITE_WIN32_HAS_ANSI ++ h = osCreateFileA((LPCSTR)zConverted, ++ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ ++ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ ++ NULL, /* lpSecurityAttributes */ ++ OPEN_ALWAYS, /* dwCreationDisposition */ ++ FILE_ATTRIBUTE_NORMAL|flag_overlapped, ++ NULL ++ ); ++#endif ++ } ++ ++ if( h==INVALID_HANDLE_VALUE ){ ++ if( bReadonly==0 ){ ++ bReadonly = 1; ++ rc = winHandleOpen(zUtf8, &bReadonly, &h); ++ }else{ ++ rc = SQLITE_CANTOPEN_BKPT; ++ } ++ } ++ ++ winopenfile_out: ++ sqlite3_free(zConverted); ++ *pbReadonly = bReadonly; ++ *ph = h; ++ return rc; ++} ++ ++ ++/* ++** Open the shared-memory area associated with database file pDbFd. + */ + static int winOpenSharedMemory(winFile *pDbFd){ + struct winShm *p; /* The connection to be opened */ +@@ -45606,98 +51520,83 @@ static int winOpenSharedMemory(winFile *pDbFd){ + assert( pDbFd->pShm==0 ); /* Not previously opened */ + + /* Allocate space for the new sqlite3_shm object. Also speculatively +- ** allocate space for a new winShmNode and filename. +- */ ++ ** allocate space for a new winShmNode and filename. */ + p = sqlite3MallocZero( sizeof(*p) ); + if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; + nName = sqlite3Strlen30(pDbFd->zPath); +- pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); ++ pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 ); + if( pNew==0 ){ + sqlite3_free(p); + return SQLITE_IOERR_NOMEM_BKPT; + } + pNew->zFilename = (char*)&pNew[1]; ++ pNew->hSharedShm = INVALID_HANDLE_VALUE; ++ pNew->isUnlocked = 1; + sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); + sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); + ++ /* Open a file-handle on the *-shm file for this connection. This file-handle ++ ** is only used for locking. The mapping of the *-shm file is created using ++ ** the shared file handle in winShmNode.hSharedShm. */ ++ p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0); ++ rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm); ++ + /* Look to see if there is an existing winShmNode that can be used. +- ** If no matching winShmNode currently exists, create a new one. +- */ ++ ** If no matching winShmNode currently exists, then create a new one. */ + winShmEnterMutex(); + for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ + /* TBD need to come up with better match here. Perhaps +- ** use FILE_ID_BOTH_DIR_INFO Structure. +- */ ++ ** use FILE_ID_BOTH_DIR_INFO Structure. */ + if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; + } +- if( pShmNode ){ +- sqlite3_free(pNew); +- }else{ +- int inFlags = SQLITE_OPEN_WAL; +- int outFlags = 0; +- ++ if( pShmNode==0 ){ + pShmNode = pNew; +- pNew = 0; +- ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; +- pShmNode->pNext = winShmNodeList; +- winShmNodeList = pShmNode; + ++ /* Allocate a mutex for this winShmNode object, if one is required. */ + if( sqlite3GlobalConfig.bCoreMutex ){ + pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); +- if( pShmNode->mutex==0 ){ +- rc = SQLITE_IOERR_NOMEM_BKPT; +- goto shm_open_err; +- } ++ if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT; + } + +- if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ +- inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; +- }else{ +- inFlags |= SQLITE_OPEN_READONLY; +- } +- rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, +- (sqlite3_file*)&pShmNode->hFile, +- inFlags, &outFlags); +- if( rc!=SQLITE_OK ){ +- rc = winLogError(rc, osGetLastError(), "winOpenShm", +- pShmNode->zFilename); +- goto shm_open_err; ++ /* Open a file-handle to use for mappings, and for the DMS lock. */ ++ if( rc==SQLITE_OK ){ ++ HANDLE h = INVALID_HANDLE_VALUE; ++ pShmNode->isReadonly = p->bReadonly; ++ rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h); ++ pShmNode->hSharedShm = h; + } +- if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1; + +- rc = winLockSharedMemory(pShmNode); +- if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; ++ /* If successful, link the new winShmNode into the global list. If an ++ ** error occurred, free the object. */ ++ if( rc==SQLITE_OK ){ ++ pShmNode->pNext = winShmNodeList; ++ winShmNodeList = pShmNode; ++ pNew = 0; ++ }else{ ++ sqlite3_mutex_free(pShmNode->mutex); ++ if( pShmNode->hSharedShm!=INVALID_HANDLE_VALUE ){ ++ osCloseHandle(pShmNode->hSharedShm); ++ } ++ } + } + +- /* Make the new connection a child of the winShmNode */ +- p->pShmNode = pShmNode; ++ /* If no error has occurred, link the winShm object to the winShmNode and ++ ** the winShm to pDbFd. */ ++ if( rc==SQLITE_OK ){ ++ p->pShmNode = pShmNode; ++ pShmNode->nRef++; + #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) +- p->id = pShmNode->nextShmId++; ++ p->id = pShmNode->nextShmId++; + #endif +- pShmNode->nRef++; +- pDbFd->pShm = p; +- winShmLeaveMutex(); +- +- /* The reference count on pShmNode has already been incremented under +- ** the cover of the winShmEnterMutex() mutex and the pointer from the +- ** new (struct winShm) object to the pShmNode has been set. All that is +- ** left to do is to link the new object into the linked list starting +- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex +- ** mutex. +- */ +- sqlite3_mutex_enter(pShmNode->mutex); +- p->pNext = pShmNode->pFirst; +- pShmNode->pFirst = p; +- sqlite3_mutex_leave(pShmNode->mutex); +- return rc; ++ pDbFd->pShm = p; ++ }else if( p ){ ++ winHandleClose(p->hShm); ++ sqlite3_free(p); ++ } + +- /* Jump here on any error */ +-shm_open_err: +- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); +- winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ +- sqlite3_free(p); +- sqlite3_free(pNew); ++ assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 ); + winShmLeaveMutex(); ++ sqlite3_free(pNew); + return rc; + } + +@@ -45712,27 +51611,19 @@ static int winShmUnmap( + winFile *pDbFd; /* Database holding shared-memory */ + winShm *p; /* The connection to be closed */ + winShmNode *pShmNode; /* The underlying shared-memory file */ +- winShm **pp; /* For looping over sibling connections */ + + pDbFd = (winFile*)fd; + p = pDbFd->pShm; + if( p==0 ) return SQLITE_OK; +- pShmNode = p->pShmNode; +- +- /* Remove connection p from the set of connections associated +- ** with pShmNode */ +- sqlite3_mutex_enter(pShmNode->mutex); +- for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} +- *pp = p->pNext; ++ if( p->hShm!=INVALID_HANDLE_VALUE ){ ++ osCloseHandle(p->hShm); ++ } + +- /* Free the connection p */ +- sqlite3_free(p); +- pDbFd->pShm = 0; +- sqlite3_mutex_leave(pShmNode->mutex); ++ pShmNode = p->pShmNode; ++ winShmEnterMutex(); + + /* If pShmNode->nRef has reached 0, then close the underlying +- ** shared-memory file, too */ +- winShmEnterMutex(); ++ ** shared-memory file, too. */ + assert( pShmNode->nRef>0 ); + pShmNode->nRef--; + if( pShmNode->nRef==0 ){ +@@ -45740,6 +51631,9 @@ static int winShmUnmap( + } + winShmLeaveMutex(); + ++ /* Free the connection p */ ++ sqlite3_free(p); ++ pDbFd->pShm = 0; + return SQLITE_OK; + } + +@@ -45754,10 +51648,13 @@ static int winShmLock( + ){ + winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ + winShm *p = pDbFd->pShm; /* The shared memory being locked */ +- winShm *pX; /* For looping over all siblings */ +- winShmNode *pShmNode = p->pShmNode; ++ winShmNode *pShmNode; + int rc = SQLITE_OK; /* Result code */ +- u16 mask; /* Mask of locks to take or release */ ++ u16 mask = (u16)((1U<<(ofst+n)) - (1U<pShmNode; ++ if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; + + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); + assert( n>=1 ); +@@ -45767,85 +51664,81 @@ static int winShmLock( + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + +- mask = (u16)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex); +- if( flags & SQLITE_SHM_UNLOCK ){ +- u16 allMask = 0; /* Mask of locks held by siblings */ +- +- /* See if any siblings hold this same lock */ +- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +- if( pX==p ) continue; +- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); +- allMask |= pX->sharedMask; +- } ++ /* Check that, if this to be a blocking lock, no locks that occur later ++ ** in the following list than the lock being obtained are already held: ++ ** ++ ** 1. Recovery lock (ofst==2). ++ ** 2. Checkpointer lock (ofst==1). ++ ** 3. Write lock (ofst==0). ++ ** 4. Read locks (ofst>=3 && ofstexclMask|p->sharedMask); ++ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( ++ (ofst!=2 || lockMask==0) ++ && (ofst!=1 || lockMask==0 || lockMask==2) ++ && (ofst!=0 || lockMask<3) ++ && (ofst<3 || lockMask<(1<exclMask & mask) ++ ); ++ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) ++ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) ++ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) ++ ){ + +- /* Undo the local locks */ +- if( rc==SQLITE_OK ){ +- p->exclMask &= ~mask; +- p->sharedMask &= ~mask; +- } +- }else if( flags & SQLITE_SHM_SHARED ){ +- u16 allShared = 0; /* Union of locks held by connections other than "p" */ ++ if( flags & SQLITE_SHM_UNLOCK ){ ++ /* Case (a) - unlock. */ + +- /* Find out which shared locks are already held by sibling connections. +- ** If any sibling already holds an exclusive lock, go ahead and return +- ** SQLITE_BUSY. +- */ +- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +- if( (pX->exclMask & mask)!=0 ){ +- rc = SQLITE_BUSY; +- break; +- } +- allShared |= pX->sharedMask; +- } ++ assert( (p->exclMask & p->sharedMask)==0 ); ++ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); ++ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); + +- /* Get shared locks at the system level, if necessary */ +- if( rc==SQLITE_OK ){ +- if( (allShared & mask)==0 ){ +- rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n); +- }else{ +- rc = SQLITE_OK; +- } +- } ++ rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n); + +- /* Get the local shared locks */ +- if( rc==SQLITE_OK ){ +- p->sharedMask |= mask; +- } +- }else{ +- /* Make sure no sibling connections hold locks that will block this +- ** lock. If any do, return SQLITE_BUSY right away. +- */ +- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ +- rc = SQLITE_BUSY; +- break; ++ /* If successful, also clear the bits in sharedMask/exclMask */ ++ if( rc==SQLITE_OK ){ ++ p->exclMask = (p->exclMask & ~mask); ++ p->sharedMask = (p->sharedMask & ~mask); + } +- } +- +- /* Get the exclusive locks at the system level. Then if successful +- ** also mark the local connection as being locked. +- */ +- if( rc==SQLITE_OK ){ +- rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n); ++ }else{ ++ int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0); ++ DWORD nMs = winFileBusyTimeout(pDbFd); ++ rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs); + if( rc==SQLITE_OK ){ +- assert( (p->sharedMask & mask)==0 ); +- p->exclMask |= mask; ++ if( bExcl ){ ++ p->exclMask = (p->exclMask | mask); ++ }else{ ++ p->sharedMask = (p->sharedMask | mask); ++ } + } + } + } +- sqlite3_mutex_leave(pShmNode->mutex); +- OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", +- osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, +- sqlite3ErrName(rc))); ++ ++ OSTRACE(( ++ "SHM-LOCK(%d,%d,%d) pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x," ++ " rc=%s\n", ++ ofst, n, flags, ++ osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, ++ sqlite3ErrName(rc)) ++ ); + return rc; + } + +@@ -45907,13 +51800,15 @@ static int winShmMap( + + sqlite3_mutex_enter(pShmNode->mutex); + if( pShmNode->isUnlocked ){ +- rc = winLockSharedMemory(pShmNode); ++ /* Take the DMS lock. */ ++ assert( pShmNode->nRegion==0 ); ++ rc = winLockSharedMemory(pShmNode, winFileBusyTimeout(pDbFd)); + if( rc!=SQLITE_OK ) goto shmpage_out; +- pShmNode->isUnlocked = 0; + } +- assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); + ++ assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); + if( pShmNode->nRegion<=iRegion ){ ++ HANDLE hShared = pShmNode->hSharedShm; + struct ShmRegion *apNew; /* New aRegion[] array */ + int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ + sqlite3_int64 sz; /* Current size of wal-index file */ +@@ -45924,10 +51819,9 @@ static int winShmMap( + ** Check to see if it has been allocated (i.e. if the wal-index file is + ** large enough to contain the requested region). + */ +- rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); ++ rc = winHandleSize(hShared, &sz); + if( rc!=SQLITE_OK ){ +- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), +- "winShmMap1", pDbFd->zPath); ++ rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath); + goto shmpage_out; + } + +@@ -45936,19 +51830,17 @@ static int winShmMap( + ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. + ** + ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate +- ** the requested memory region. +- */ ++ ** the requested memory region. */ + if( !isWrite ) goto shmpage_out; +- rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); ++ rc = winHandleTruncate(hShared, nByte); + if( rc!=SQLITE_OK ){ +- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), +- "winShmMap2", pDbFd->zPath); ++ rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath); + goto shmpage_out; + } + } + + /* Map the requested memory region into this processes address space. */ +- apNew = (struct ShmRegion *)sqlite3_realloc64( ++ apNew = (struct ShmRegion*)sqlite3_realloc64( + pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) + ); + if( !apNew ){ +@@ -45967,18 +51859,13 @@ static int winShmMap( + void *pMap = 0; /* Mapped memory region */ + + #if SQLITE_OS_WINRT +- hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, +- NULL, protect, nByte, NULL +- ); ++ hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL); + #elif defined(SQLITE_WIN32_HAS_WIDE) +- hMap = osCreateFileMappingW(pShmNode->hFile.h, +- NULL, protect, 0, nByte, NULL +- ); ++ hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL); + #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA +- hMap = osCreateFileMappingA(pShmNode->hFile.h, +- NULL, protect, 0, nByte, NULL +- ); ++ hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL); + #endif ++ + OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", + osGetCurrentProcessId(), pShmNode->nRegion, nByte, + hMap ? "ok" : "failed")); +@@ -46021,7 +51908,9 @@ shmpage_out: + }else{ + *pp = 0; + } +- if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; ++ if( pShmNode->isReadonly && rc==SQLITE_OK ){ ++ rc = SQLITE_READONLY; ++ } + sqlite3_mutex_leave(pShmNode->mutex); + return rc; + } +@@ -46195,6 +52084,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ + + #if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->mmapSizeMax>0 ){ ++ /* Ensure that there is always at least a 256 byte buffer of addressable ++ ** memory following the returned page. If the database is corrupt, ++ ** SQLite may overread the page slightly (in practice only a few bytes, ++ ** but 256 is safe, round, number). */ ++ const int nEofBuffer = 256; + if( pFd->pMapRegion==0 ){ + int rc = winMapfile(pFd, -1); + if( rc!=SQLITE_OK ){ +@@ -46203,7 +52097,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ + return rc; + } + } +- if( pFd->mmapSize >= iOff+nAmt ){ ++ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ + assert( pFd->pMapRegion!=0 ); + *pp = &((u8 *)pFd->pMapRegion)[iOff]; + pFd->nFetchOut++; +@@ -46336,47 +52230,6 @@ static winVfsAppData winNolockAppData = { + ** sqlite3_vfs object. + */ + +-#if defined(__CYGWIN__) +-/* +-** Convert a filename from whatever the underlying operating system +-** supports for filenames into UTF-8. Space to hold the result is +-** obtained from malloc and must be freed by the calling function. +-*/ +-static char *winConvertToUtf8Filename(const void *zFilename){ +- char *zConverted = 0; +- if( osIsNT() ){ +- zConverted = winUnicodeToUtf8(zFilename); +- } +-#ifdef SQLITE_WIN32_HAS_ANSI +- else{ +- zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI()); +- } +-#endif +- /* caller will handle out of memory */ +- return zConverted; +-} +-#endif +- +-/* +-** Convert a UTF-8 filename into whatever form the underlying +-** operating system wants filenames in. Space to hold the result +-** is obtained from malloc and must be freed by the calling +-** function. +-*/ +-static void *winConvertFromUtf8Filename(const char *zFilename){ +- void *zConverted = 0; +- if( osIsNT() ){ +- zConverted = winUtf8ToUnicode(zFilename); +- } +-#ifdef SQLITE_WIN32_HAS_ANSI +- else{ +- zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); +- } +-#endif +- /* caller will handle out of memory */ +- return zConverted; +-} +- + /* + ** This function returns non-zero if the specified UTF-8 string buffer + ** ends with a directory separator character or one was successfully +@@ -46389,7 +52242,14 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){ + if( winIsDirSep(zBuf[nLen-1]) ){ + return 1; + }else if( nLen+1mxPathname; nBuf = nMax + 2; ++ nMax = pVfs->mxPathname; ++ nBuf = 2 + (i64)nMax; + zBuf = sqlite3MallocZero( nBuf ); + if( !zBuf ){ + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); +@@ -46434,22 +52309,25 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ + */ + nDir = nMax - (nPre + 15); + assert( nDir>0 ); +- if( sqlite3_temp_directory ){ ++ if( winTempDirDefined() ){ + int nDirLen = sqlite3Strlen30(sqlite3_temp_directory); + if( nDirLen>0 ){ + if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){ + nDirLen++; + } + if( nDirLen>nDir ){ ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); + return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0); + } + sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory); + } ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + } ++ + #if defined(__CYGWIN__) +- else{ ++ else if( osGetenv!=NULL ){ + static const char *azDirs[] = { + 0, /* getenv("SQLITE_TMPDIR") */ + 0, /* getenv("TMPDIR") */ +@@ -46465,11 +52343,11 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ + unsigned int i; + const char *zDir = 0; + +- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); +- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); +- if( !azDirs[2] ) azDirs[2] = getenv("TMP"); +- if( !azDirs[3] ) azDirs[3] = getenv("TEMP"); +- if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE"); ++ if( !azDirs[0] ) azDirs[0] = osGetenv("SQLITE_TMPDIR"); ++ if( !azDirs[1] ) azDirs[1] = osGetenv("TMPDIR"); ++ if( !azDirs[2] ) azDirs[2] = osGetenv("TMP"); ++ if( !azDirs[3] ) azDirs[3] = osGetenv("TEMP"); ++ if( !azDirs[4] ) azDirs[4] = osGetenv("USERPROFILE"); + for(i=0; i>= 8; + zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + } + zBuf[j] = 0; +@@ -46649,7 +52498,7 @@ static int winIsDir(const void *zConverted){ + return 0; /* Invalid name? */ + } + attr = sAttrData.dwFileAttributes; +-#if SQLITE_OS_WINCE==0 ++#if SQLITE_OS_WINCE==0 && defined(SQLITE_WIN32_HAS_ANSI) + }else{ + attr = osGetFileAttributesA((char*)zConverted); + #endif +@@ -46665,6 +52514,12 @@ static int winAccess( + int *pResOut /* OUT: Result */ + ); + ++/* ++** The Windows version of xAccess() accepts an extra bit in the flags ++** parameter that prevents an anti-virus retry loop. ++*/ ++#define NORETRY 0x4000 ++ + /* + ** Open a file. + */ +@@ -46689,6 +52544,7 @@ static int winOpen( + void *zConverted; /* Filename in OS encoding */ + const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ + int cnt = 0; ++ int isRO = 0; /* file is known to be accessible readonly */ + + /* If argument zPath is a NULL pointer, this function is required to open + ** a temporary file. Use this buffer to store the file name in. +@@ -46697,7 +52553,7 @@ static int winOpen( + + int rc = SQLITE_OK; /* Function Return Code */ + #if !defined(NDEBUG) || SQLITE_OS_WINCE +- int eType = flags&0xFFFFFF00; /* Type of file to open */ ++ int eType = flags&0x0FFF00; /* Type of file to open */ + #endif + + int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); +@@ -46811,7 +52667,11 @@ static int winOpen( + dwCreationDisposition = OPEN_EXISTING; + } + +- dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; ++ if( 0==sqlite3_uri_boolean(zName, "exclusive", 0) ){ ++ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; ++ }else{ ++ dwShareMode = 0; ++ } + + if( isDelete ){ + #if SQLITE_OS_WINCE +@@ -46849,9 +52709,9 @@ static int winOpen( + &extendedParameters); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ +- int rc2, isRO = 0; ++ int rc2; + sqlite3BeginBenignMalloc(); +- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); ++ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); + sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } +@@ -46866,9 +52726,9 @@ static int winOpen( + NULL); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ +- int rc2, isRO = 0; ++ int rc2; + sqlite3BeginBenignMalloc(); +- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); ++ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); + sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } +@@ -46886,9 +52746,9 @@ static int winOpen( + NULL); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ +- int rc2, isRO = 0; ++ int rc2; + sqlite3BeginBenignMalloc(); +- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); ++ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); + sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } +@@ -46903,7 +52763,7 @@ static int winOpen( + if( h==INVALID_HANDLE_VALUE ){ + sqlite3_free(zConverted); + sqlite3_free(zTmpname); +- if( isReadWrite && !isExclusive ){ ++ if( isReadWrite && isRO && !isExclusive ){ + return winOpen(pVfs, zName, id, + ((flags|SQLITE_OPEN_READONLY) & + ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), +@@ -47105,12 +52965,25 @@ static int winAccess( + int rc = 0; + DWORD lastErrno = 0; + void *zConverted; ++ int noRetry = 0; /* Do not use winRetryIoerr() */ + UNUSED_PARAMETER(pVfs); + ++ if( (flags & NORETRY)!=0 ){ ++ noRetry = 1; ++ flags &= ~NORETRY; ++ } ++ + SimulateIOError( return SQLITE_IOERR_ACCESS; ); + OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", + zFilename, flags, pResOut)); + ++ if( zFilename==0 ){ ++ *pResOut = 0; ++ OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", ++ zFilename, pResOut, *pResOut)); ++ return SQLITE_OK; ++ } ++ + zConverted = winConvertFromUtf8Filename(zFilename); + if( zConverted==0 ){ + OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); +@@ -47122,7 +52995,10 @@ static int winAccess( + memset(&sAttrData, 0, sizeof(sAttrData)); + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, + GetFileExInfoStandard, +- &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} ++ &sAttrData)) ++ && !noRetry ++ && winRetryIoerr(&cnt, &lastErrno) ++ ){ /* Loop until true */} + if( rc ){ + /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file + ** as if it does not exist. +@@ -47190,6 +53066,7 @@ static BOOL winIsDriveLetterAndColon( + return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ); + } + ++#ifdef _WIN32 + /* + ** Returns non-zero if the specified path name should be used verbatim. If + ** non-zero is returned from this function, the calling function must simply +@@ -47226,20 +53103,84 @@ static BOOL winIsVerbatimPathname( + */ + return FALSE; + } ++#endif /* _WIN32 */ ++ ++#ifdef __CYGWIN__ ++/* ++** Simplify a filename into its canonical form ++** by making the following changes: ++** ++** * convert any '/' to '\' (win32) or reverse (Cygwin) ++** * removing any trailing and duplicate / (except for UNC paths) ++** * convert /./ into just / ++** ++** Changes are made in-place. Return the new name length. ++** ++** The original filename is in z[0..]. If the path is shortened, ++** no-longer used bytes will be written by '\0'. ++*/ ++static void winSimplifyName(char *z){ ++ int i, j; ++ for(i=j=0; z[i]; ++i){ ++ if( winIsDirSep(z[i]) ){ ++#if !defined(SQLITE_TEST) ++ /* Some test-cases assume that "./foo" and "foo" are different */ ++ if( z[i+1]=='.' && winIsDirSep(z[i+2]) ){ ++ ++i; ++ continue; ++ } ++#endif ++ if( !z[i+1] || (winIsDirSep(z[i+1]) && (i!=0)) ){ ++ continue; ++ } ++ z[j++] = osGetenv?'/':'\\'; ++ }else{ ++ z[j++] = z[i]; ++ } ++ } ++ while(jnOut ){ ++ /* SQLite assumes that xFullPathname() nul-terminates the output buffer ++ ** even if it returns an error. */ ++ zOut[iOff] = '\0'; ++ return SQLITE_CANTOPEN_BKPT; ++ } ++ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); ++ return SQLITE_OK; ++} ++#endif /* __CYGWIN__ */ + + /* + ** Turn a relative pathname into a full pathname. Write the full + ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname + ** bytes in size. + */ +-static int winFullPathname( ++static int winFullPathnameNoMutex( + sqlite3_vfs *pVfs, /* Pointer to vfs object */ + const char *zRelative, /* Possibly relative input path */ + int nFull, /* Size of output buffer in bytes */ + char *zFull /* Output buffer */ + ){ +-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) +- DWORD nByte; ++#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT ++ int nByte; + void *zConverted; + char *zOut; + #endif +@@ -47252,64 +53193,82 @@ static int winFullPathname( + zRelative++; + } + +-#if defined(__CYGWIN__) + SimulateIOError( return SQLITE_ERROR ); +- UNUSED_PARAMETER(nFull); +- assert( nFull>=pVfs->mxPathname ); +- if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ +- /* +- ** NOTE: We are dealing with a relative path name and the data +- ** directory has been set. Therefore, use it as the basis +- ** for converting the relative path name to an absolute +- ** one by prepending the data directory and a slash. +- */ +- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); +- if( !zOut ){ +- return SQLITE_IOERR_NOMEM_BKPT; +- } +- if( cygwin_conv_path( +- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | +- CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ +- sqlite3_free(zOut); +- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, +- "winFullPathname1", zRelative); +- }else{ +- char *zUtf8 = winConvertToUtf8Filename(zOut); +- if( !zUtf8 ){ +- sqlite3_free(zOut); +- return SQLITE_IOERR_NOMEM_BKPT; +- } +- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", +- sqlite3_data_directory, winGetDirSep(), zUtf8); +- sqlite3_free(zUtf8); +- sqlite3_free(zOut); +- } +- }else{ +- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); +- if( !zOut ){ +- return SQLITE_IOERR_NOMEM_BKPT; +- } +- if( cygwin_conv_path( +- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), +- zRelative, zOut, pVfs->mxPathname+1)<0 ){ +- sqlite3_free(zOut); +- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, +- "winFullPathname2", zRelative); +- }else{ +- char *zUtf8 = winConvertToUtf8Filename(zOut); +- if( !zUtf8 ){ +- sqlite3_free(zOut); +- return SQLITE_IOERR_NOMEM_BKPT; +- } +- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); +- sqlite3_free(zUtf8); +- sqlite3_free(zOut); ++ ++#ifdef __CYGWIN__ ++ if( osGetcwd ){ ++ zFull[nFull-1] = '\0'; ++ if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){ ++ int rc = SQLITE_OK; ++ int nLink = 1; /* Number of symbolic links followed so far */ ++ const char *zIn = zRelative; /* Input path for each iteration of loop */ ++ char *zDel = 0; ++ struct stat buf; ++ ++ UNUSED_PARAMETER(pVfs); ++ ++ do { ++ /* Call lstat() on path zIn. Set bLink to true if the path is a symbolic ++ ** link, or false otherwise. */ ++ int bLink = 0; ++ if( osLstat && osReadlink ) { ++ if( osLstat(zIn, &buf)!=0 ){ ++ int myErrno = osErrno; ++ if( myErrno!=ENOENT ){ ++ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, "lstat", zIn); ++ } ++ }else{ ++ bLink = ((buf.st_mode & 0170000) == 0120000); ++ } ++ ++ if( bLink ){ ++ if( zDel==0 ){ ++ zDel = sqlite3MallocZero(nFull); ++ if( zDel==0 ) rc = SQLITE_NOMEM; ++ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){ ++ rc = SQLITE_CANTOPEN_BKPT; ++ } ++ ++ if( rc==SQLITE_OK ){ ++ nByte = osReadlink(zIn, zDel, nFull-1); ++ if( nByte ==(DWORD)-1 ){ ++ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "readlink", zIn); ++ }else{ ++ if( zDel[0]!='/' ){ ++ int n; ++ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); ++ if( nByte+n+1>nFull ){ ++ rc = SQLITE_CANTOPEN_BKPT; ++ }else{ ++ memmove(&zDel[n], zDel, nByte+1); ++ memcpy(zDel, zIn, n); ++ nByte += n; ++ } ++ } ++ zDel[nByte] = '\0'; ++ } ++ } ++ ++ zIn = zDel; ++ } ++ } ++ ++ assert( rc!=SQLITE_OK || zIn!=zFull || zIn[0]=='/' ); ++ if( rc==SQLITE_OK && zIn!=zFull ){ ++ rc = mkFullPathname(zIn, zFull, nFull); ++ } ++ if( bLink==0 ) break; ++ zIn = zFull; ++ }while( rc==SQLITE_OK ); ++ ++ sqlite3_free(zDel); ++ winSimplifyName(zFull); ++ return rc; + } + } +- return SQLITE_OK; +-#endif ++#endif /* __CYGWIN__ */ + +-#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__) ++#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && defined(_WIN32) + SimulateIOError( return SQLITE_ERROR ); + /* WinCE has no concept of a relative pathname, or so I am told. */ + /* WinRT has no way to convert a relative path to an absolute one. */ +@@ -47328,7 +53287,8 @@ static int winFullPathname( + return SQLITE_OK; + #endif + +-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) ++#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT ++#if defined(_WIN32) + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. This function could fail if, for example, the +@@ -47346,6 +53306,7 @@ static int winFullPathname( + sqlite3_data_directory, winGetDirSep(), zRelative); + return SQLITE_OK; + } ++#endif + zConverted = winConvertFromUtf8Filename(zRelative); + if( zConverted==0 ){ + return SQLITE_IOERR_NOMEM_BKPT; +@@ -47384,13 +53345,12 @@ static int winFullPathname( + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname3", zRelative); + } +- nByte += 3; +- zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); ++ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) + 3*sizeof(zTemp[0]) ); + if( zTemp==0 ){ + sqlite3_free(zConverted); + return SQLITE_IOERR_NOMEM_BKPT; + } +- nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); ++ nByte = osGetFullPathNameA((char*)zConverted, nByte+3, zTemp, 0); + if( nByte==0 ){ + sqlite3_free(zConverted); + sqlite3_free(zTemp); +@@ -47403,7 +53363,26 @@ static int winFullPathname( + } + #endif + if( zOut ){ ++#ifdef __CYGWIN__ ++ if( memcmp(zOut, "\\\\?\\", 4) ){ ++ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); ++ }else if( memcmp(zOut+4, "UNC\\", 4) ){ ++ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+4); ++ }else{ ++ char *p = zOut+6; ++ *p = '\\'; ++ if( osGetcwd ){ ++ /* On Cygwin, UNC paths use forward slashes */ ++ while( *p ){ ++ if( *p=='\\' ) *p = '/'; ++ ++p; ++ } ++ } ++ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+6); ++ } ++#else + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); ++#endif /* __CYGWIN__ */ + sqlite3_free(zOut); + return SQLITE_OK; + }else{ +@@ -47411,6 +53390,20 @@ static int winFullPathname( + } + #endif + } ++static int winFullPathname( ++ sqlite3_vfs *pVfs, /* Pointer to vfs object */ ++ const char *zRelative, /* Possibly relative input path */ ++ int nFull, /* Size of output buffer in bytes */ ++ char *zFull /* Output buffer */ ++){ ++ int rc; ++ MUTEX_LOGIC( sqlite3_mutex *pMutex; ) ++ MUTEX_LOGIC( pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); ) ++ sqlite3_mutex_enter(pMutex); ++ rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull); ++ sqlite3_mutex_leave(pMutex); ++ return rc; ++} + + #ifndef SQLITE_OMIT_LOAD_EXTENSION + /* +@@ -47419,25 +53412,8 @@ static int winFullPathname( + */ + static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ + HANDLE h; +-#if defined(__CYGWIN__) +- int nFull = pVfs->mxPathname+1; +- char *zFull = sqlite3MallocZero( nFull ); +- void *zConverted = 0; +- if( zFull==0 ){ +- OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); +- return 0; +- } +- if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){ +- sqlite3_free(zFull); +- OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); +- return 0; +- } +- zConverted = winConvertFromUtf8Filename(zFull); +- sqlite3_free(zFull); +-#else + void *zConverted = winConvertFromUtf8Filename(zFilename); + UNUSED_PARAMETER(pVfs); +-#endif + if( zConverted==0 ){ + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); + return 0; +@@ -47786,7 +53762,7 @@ SQLITE_API int sqlite3_os_init(void){ + + /* Double-check that the aSyscall[] array has been constructed + ** correctly. See ticket [bb3a86e890c8e96ab] */ +- assert( ArraySize(aSyscall)==80 ); ++ assert( ArraySize(aSyscall)==89 ); + + /* get memory map allocation granularity */ + memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); +@@ -47855,31 +53831,88 @@ SQLITE_API int sqlite3_os_end(void){ + ** sqlite3_deserialize(). + */ + /* #include "sqliteInt.h" */ +-#ifdef SQLITE_ENABLE_DESERIALIZE ++#ifndef SQLITE_OMIT_DESERIALIZE + + /* + ** Forward declaration of objects used by this utility + */ + typedef struct sqlite3_vfs MemVfs; + typedef struct MemFile MemFile; ++typedef struct MemStore MemStore; + + /* Access to a lower-level VFS that (might) implement dynamic loading, + ** access to randomness, etc. + */ + #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) + +-/* An open file */ +-struct MemFile { +- sqlite3_file base; /* IO methods */ ++/* Storage for a memdb file. ++** ++** An memdb object can be shared or separate. Shared memdb objects can be ++** used by more than one database connection. Mutexes are used by shared ++** memdb objects to coordinate access. Separate memdb objects are only ++** connected to a single database connection and do not require additional ++** mutexes. ++** ++** Shared memdb objects have .zFName!=0 and .pMutex!=0. They are created ++** using "file:/name?vfs=memdb". The first character of the name must be ++** "/" or else the object will be a separate memdb object. All shared ++** memdb objects are stored in memdb_g.apMemStore[] in an arbitrary order. ++** ++** Separate memdb objects are created using a name that does not begin ++** with "/" or using sqlite3_deserialize(). ++** ++** Access rules for shared MemStore objects: ++** ++** * .zFName is initialized when the object is created and afterwards ++** is unchanged until the object is destroyed. So it can be accessed ++** at any time as long as we know the object is not being destroyed, ++** which means while either the SQLITE_MUTEX_STATIC_VFS1 or ++** .pMutex is held or the object is not part of memdb_g.apMemStore[]. ++** ++** * Can .pMutex can only be changed while holding the ++** SQLITE_MUTEX_STATIC_VFS1 mutex or while the object is not part ++** of memdb_g.apMemStore[]. ++** ++** * Other fields can only be changed while holding the .pMutex mutex ++** or when the .nRef is less than zero and the object is not part of ++** memdb_g.apMemStore[]. ++** ++** * The .aData pointer has the added requirement that it can can only ++** be changed (for resizing) when nMmap is zero. ++** ++*/ ++struct MemStore { + sqlite3_int64 sz; /* Size of the file */ + sqlite3_int64 szAlloc; /* Space allocated to aData */ + sqlite3_int64 szMax; /* Maximum allowed size of the file */ + unsigned char *aData; /* content of the file */ ++ sqlite3_mutex *pMutex; /* Used by shared stores only */ + int nMmap; /* Number of memory mapped pages */ + unsigned mFlags; /* Flags */ ++ int nRdLock; /* Number of readers */ ++ int nWrLock; /* Number of writers. (Always 0 or 1) */ ++ int nRef; /* Number of users of this MemStore */ ++ char *zFName; /* The filename for shared stores */ ++}; ++ ++/* An open file */ ++struct MemFile { ++ sqlite3_file base; /* IO methods */ ++ MemStore *pStore; /* The storage */ + int eLock; /* Most recent lock against this file */ + }; + ++/* ++** File-scope variables for holding the memdb files that are accessible ++** to multiple database connections in separate threads. ++** ++** Must hold SQLITE_MUTEX_STATIC_VFS1 to access any part of this object. ++*/ ++static struct MemFS { ++ int nMemStore; /* Number of shared MemStore objects */ ++ MemStore **apMemStore; /* Array of all shared MemStore objects */ ++} memdb_g; ++ + /* + ** Methods for MemFile + */ +@@ -47890,6 +53923,7 @@ static int memdbTruncate(sqlite3_file*, sqlite3_int64 size); + static int memdbSync(sqlite3_file*, int flags); + static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize); + static int memdbLock(sqlite3_file*, int); ++static int memdbUnlock(sqlite3_file*, int); + /* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */ + static int memdbFileControl(sqlite3_file*, int op, void *pArg); + /* static int memdbSectorSize(sqlite3_file*); // not used */ +@@ -47933,7 +53967,10 @@ static sqlite3_vfs memdb_vfs = { + memdbSleep, /* xSleep */ + 0, /* memdbCurrentTime, */ /* xCurrentTime */ + memdbGetLastError, /* xGetLastError */ +- memdbCurrentTimeInt64 /* xCurrentTimeInt64 */ ++ memdbCurrentTimeInt64, /* xCurrentTimeInt64 */ ++ 0, /* xSetSystemCall */ ++ 0, /* xGetSystemCall */ ++ 0, /* xNextSystemCall */ + }; + + static const sqlite3_io_methods memdb_io_methods = { +@@ -47945,7 +53982,7 @@ static const sqlite3_io_methods memdb_io_methods = { + memdbSync, /* xSync */ + memdbFileSize, /* xFileSize */ + memdbLock, /* xLock */ +- memdbLock, /* xUnlock - same as xLock in this case */ ++ memdbUnlock, /* xUnlock */ + 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */ + memdbFileControl, /* xFileControl */ + 0, /* memdbSectorSize,*/ /* xSectorSize */ +@@ -47958,17 +53995,68 @@ static const sqlite3_io_methods memdb_io_methods = { + memdbUnfetch /* xUnfetch */ + }; + ++/* ++** Enter/leave the mutex on a MemStore ++*/ ++#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 ++static void memdbEnter(MemStore *p){ ++ UNUSED_PARAMETER(p); ++} ++static void memdbLeave(MemStore *p){ ++ UNUSED_PARAMETER(p); ++} ++#else ++static void memdbEnter(MemStore *p){ ++ sqlite3_mutex_enter(p->pMutex); ++} ++static void memdbLeave(MemStore *p){ ++ sqlite3_mutex_leave(p->pMutex); ++} ++#endif ++ + + + /* + ** Close an memdb-file. +-** +-** The pData pointer is owned by the application, so there is nothing +-** to free. ++** Free the underlying MemStore object when its refcount drops to zero ++** or less. + */ + static int memdbClose(sqlite3_file *pFile){ +- MemFile *p = (MemFile *)pFile; +- if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData); ++ MemStore *p = ((MemFile*)pFile)->pStore; ++ if( p->zFName ){ ++ int i; ++#ifndef SQLITE_MUTEX_OMIT ++ sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); ++#endif ++ sqlite3_mutex_enter(pVfsMutex); ++ for(i=0; ALWAYS(inRef==1 ){ ++ memdb_g.apMemStore[i] = memdb_g.apMemStore[--memdb_g.nMemStore]; ++ if( memdb_g.nMemStore==0 ){ ++ sqlite3_free(memdb_g.apMemStore); ++ memdb_g.apMemStore = 0; ++ } ++ } ++ break; ++ } ++ } ++ sqlite3_mutex_leave(pVfsMutex); ++ }else{ ++ memdbEnter(p); ++ } ++ p->nRef--; ++ if( p->nRef<=0 ){ ++ if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){ ++ sqlite3_free(p->aData); ++ } ++ memdbLeave(p); ++ sqlite3_mutex_free(p->pMutex); ++ sqlite3_free(p); ++ }else{ ++ memdbLeave(p); ++ } + return SQLITE_OK; + } + +@@ -47981,22 +54069,25 @@ static int memdbRead( + int iAmt, + sqlite_int64 iOfst + ){ +- MemFile *p = (MemFile *)pFile; ++ MemStore *p = ((MemFile*)pFile)->pStore; ++ memdbEnter(p); + if( iOfst+iAmt>p->sz ){ + memset(zBuf, 0, iAmt); + if( iOfstsz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); ++ memdbLeave(p); + return SQLITE_IOERR_SHORT_READ; + } + memcpy(zBuf, p->aData+iOfst, iAmt); ++ memdbLeave(p); + return SQLITE_OK; + } + + /* + ** Try to enlarge the memory allocation to hold at least sz bytes + */ +-static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ ++static int memdbEnlarge(MemStore *p, sqlite3_int64 newSz){ + unsigned char *pNew; +- if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ ++ if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || NEVER(p->nMmap>0) ){ + return SQLITE_FULL; + } + if( newSz>p->szMax ){ +@@ -48005,7 +54096,7 @@ static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ + newSz *= 2; + if( newSz>p->szMax ) newSz = p->szMax; + pNew = sqlite3Realloc(p->aData, newSz); +- if( pNew==0 ) return SQLITE_NOMEM; ++ if( pNew==0 ) return SQLITE_IOERR_NOMEM; + p->aData = pNew; + p->szAlloc = newSz; + return SQLITE_OK; +@@ -48020,19 +54111,27 @@ static int memdbWrite( + int iAmt, + sqlite_int64 iOfst + ){ +- MemFile *p = (MemFile *)pFile; +- if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ) return SQLITE_READONLY; ++ MemStore *p = ((MemFile*)pFile)->pStore; ++ memdbEnter(p); ++ if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ){ ++ /* Can't happen: memdbLock() will return SQLITE_READONLY before ++ ** reaching this point */ ++ memdbLeave(p); ++ return SQLITE_IOERR_WRITE; ++ } + if( iOfst+iAmt>p->sz ){ + int rc; + if( iOfst+iAmt>p->szAlloc + && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK + ){ ++ memdbLeave(p); + return rc; + } + if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz); + p->sz = iOfst+iAmt; + } + memcpy(p->aData+iOfst, z, iAmt); ++ memdbLeave(p); + return SQLITE_OK; + } + +@@ -48044,16 +54143,25 @@ static int memdbWrite( + ** the size of a file, never to increase the size. + */ + static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){ +- MemFile *p = (MemFile *)pFile; +- if( NEVER(size>p->sz) ) return SQLITE_FULL; +- p->sz = size; +- return SQLITE_OK; ++ MemStore *p = ((MemFile*)pFile)->pStore; ++ int rc = SQLITE_OK; ++ memdbEnter(p); ++ if( size>p->sz ){ ++ /* This can only happen with a corrupt wal mode db */ ++ rc = SQLITE_CORRUPT; ++ }else{ ++ p->sz = size; ++ } ++ memdbLeave(p); ++ return rc; + } + + /* + ** Sync an memdb-file. + */ + static int memdbSync(sqlite3_file *pFile, int flags){ ++ UNUSED_PARAMETER(pFile); ++ UNUSED_PARAMETER(flags); + return SQLITE_OK; + } + +@@ -48061,8 +54169,10 @@ static int memdbSync(sqlite3_file *pFile, int flags){ + ** Return the current file-size of an memdb-file. + */ + static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ +- MemFile *p = (MemFile *)pFile; ++ MemStore *p = ((MemFile*)pFile)->pStore; ++ memdbEnter(p); + *pSize = p->sz; ++ memdbLeave(p); + return SQLITE_OK; + } + +@@ -48070,19 +54180,90 @@ static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + ** Lock an memdb-file. + */ + static int memdbLock(sqlite3_file *pFile, int eLock){ +- MemFile *p = (MemFile *)pFile; +- if( eLock>SQLITE_LOCK_SHARED +- && (p->mFlags & SQLITE_DESERIALIZE_READONLY)!=0 +- ){ +- return SQLITE_READONLY; ++ MemFile *pThis = (MemFile*)pFile; ++ MemStore *p = pThis->pStore; ++ int rc = SQLITE_OK; ++ if( eLock<=pThis->eLock ) return SQLITE_OK; ++ memdbEnter(p); ++ ++ assert( p->nWrLock==0 || p->nWrLock==1 ); ++ assert( pThis->eLock<=SQLITE_LOCK_SHARED || p->nWrLock==1 ); ++ assert( pThis->eLock==SQLITE_LOCK_NONE || p->nRdLock>=1 ); ++ ++ if( eLock>SQLITE_LOCK_SHARED && (p->mFlags & SQLITE_DESERIALIZE_READONLY) ){ ++ rc = SQLITE_READONLY; ++ }else{ ++ switch( eLock ){ ++ case SQLITE_LOCK_SHARED: { ++ assert( pThis->eLock==SQLITE_LOCK_NONE ); ++ if( p->nWrLock>0 ){ ++ rc = SQLITE_BUSY; ++ }else{ ++ p->nRdLock++; ++ } ++ break; ++ }; ++ ++ case SQLITE_LOCK_RESERVED: ++ case SQLITE_LOCK_PENDING: { ++ assert( pThis->eLock>=SQLITE_LOCK_SHARED ); ++ if( ALWAYS(pThis->eLock==SQLITE_LOCK_SHARED) ){ ++ if( p->nWrLock>0 ){ ++ rc = SQLITE_BUSY; ++ }else{ ++ p->nWrLock = 1; ++ } ++ } ++ break; ++ } ++ ++ default: { ++ assert( eLock==SQLITE_LOCK_EXCLUSIVE ); ++ assert( pThis->eLock>=SQLITE_LOCK_SHARED ); ++ if( p->nRdLock>1 ){ ++ rc = SQLITE_BUSY; ++ }else if( pThis->eLock==SQLITE_LOCK_SHARED ){ ++ p->nWrLock = 1; ++ } ++ break; ++ } ++ } + } +- p->eLock = eLock; ++ if( rc==SQLITE_OK ) pThis->eLock = eLock; ++ memdbLeave(p); ++ return rc; ++} ++ ++/* ++** Unlock an memdb-file. ++*/ ++static int memdbUnlock(sqlite3_file *pFile, int eLock){ ++ MemFile *pThis = (MemFile*)pFile; ++ MemStore *p = pThis->pStore; ++ if( eLock>=pThis->eLock ) return SQLITE_OK; ++ memdbEnter(p); ++ ++ assert( eLock==SQLITE_LOCK_SHARED || eLock==SQLITE_LOCK_NONE ); ++ if( eLock==SQLITE_LOCK_SHARED ){ ++ if( ALWAYS(pThis->eLock>SQLITE_LOCK_SHARED) ){ ++ p->nWrLock--; ++ } ++ }else{ ++ if( pThis->eLock>SQLITE_LOCK_SHARED ){ ++ p->nWrLock--; ++ } ++ p->nRdLock--; ++ } ++ ++ pThis->eLock = eLock; ++ memdbLeave(p); + return SQLITE_OK; + } + +-#if 0 /* Never used because memdbAccess() always returns false */ ++#if 0 + /* +-** Check if another file-handle holds a RESERVED lock on an memdb-file. ++** This interface is only used for crash recovery, which does not ++** occur on an in-memory database. + */ + static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + *pResOut = 0; +@@ -48090,12 +54271,14 @@ static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + } + #endif + ++ + /* + ** File control method. For custom operations on an memdb-file. + */ + static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ +- MemFile *p = (MemFile *)pFile; ++ MemStore *p = ((MemFile*)pFile)->pStore; + int rc = SQLITE_NOTFOUND; ++ memdbEnter(p); + if( op==SQLITE_FCNTL_VFSNAME ){ + *(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); + rc = SQLITE_OK; +@@ -48113,6 +54296,7 @@ static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ + *(sqlite3_int64*)pArg = iLimit; + rc = SQLITE_OK; + } ++ memdbLeave(p); + return rc; + } + +@@ -48129,6 +54313,7 @@ static int memdbSectorSize(sqlite3_file *pFile){ + ** Return the device characteristic flags supported by an memdb-file. + */ + static int memdbDeviceCharacteristics(sqlite3_file *pFile){ ++ UNUSED_PARAMETER(pFile); + return SQLITE_IOCAP_ATOMIC | + SQLITE_IOCAP_POWERSAFE_OVERWRITE | + SQLITE_IOCAP_SAFE_APPEND | +@@ -48142,20 +54327,26 @@ static int memdbFetch( + int iAmt, + void **pp + ){ +- MemFile *p = (MemFile *)pFile; +- if( iOfst+iAmt>p->sz ){ ++ MemStore *p = ((MemFile*)pFile)->pStore; ++ memdbEnter(p); ++ if( iOfst+iAmt>p->sz || (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)!=0 ){ + *pp = 0; + }else{ + p->nMmap++; + *pp = (void*)(p->aData + iOfst); + } ++ memdbLeave(p); + return SQLITE_OK; + } + + /* Release a memory-mapped page */ + static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ +- MemFile *p = (MemFile *)pFile; ++ MemStore *p = ((MemFile*)pFile)->pStore; ++ UNUSED_PARAMETER(iOfst); ++ UNUSED_PARAMETER(pPage); ++ memdbEnter(p); + p->nMmap--; ++ memdbLeave(p); + return SQLITE_OK; + } + +@@ -48165,20 +54356,79 @@ static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ + static int memdbOpen( + sqlite3_vfs *pVfs, + const char *zName, +- sqlite3_file *pFile, ++ sqlite3_file *pFd, + int flags, + int *pOutFlags + ){ +- MemFile *p = (MemFile*)pFile; +- if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ +- return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); ++ MemFile *pFile = (MemFile*)pFd; ++ MemStore *p = 0; ++ int szName; ++ UNUSED_PARAMETER(pVfs); ++ ++ memset(pFile, 0, sizeof(*pFile)); ++ szName = sqlite3Strlen30(zName); ++ if( szName>1 && (zName[0]=='/' || zName[0]=='\\') ){ ++ int i; ++#ifndef SQLITE_MUTEX_OMIT ++ sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); ++#endif ++ sqlite3_mutex_enter(pVfsMutex); ++ for(i=0; izFName,zName)==0 ){ ++ p = memdb_g.apMemStore[i]; ++ break; ++ } ++ } ++ if( p==0 ){ ++ MemStore **apNew; ++ p = sqlite3Malloc( sizeof(*p) + (i64)szName + 3 ); ++ if( p==0 ){ ++ sqlite3_mutex_leave(pVfsMutex); ++ return SQLITE_NOMEM; ++ } ++ apNew = sqlite3Realloc(memdb_g.apMemStore, ++ sizeof(apNew[0])*(1+(i64)memdb_g.nMemStore) ); ++ if( apNew==0 ){ ++ sqlite3_free(p); ++ sqlite3_mutex_leave(pVfsMutex); ++ return SQLITE_NOMEM; ++ } ++ apNew[memdb_g.nMemStore++] = p; ++ memdb_g.apMemStore = apNew; ++ memset(p, 0, sizeof(*p)); ++ p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE|SQLITE_DESERIALIZE_FREEONCLOSE; ++ p->szMax = sqlite3GlobalConfig.mxMemdbSize; ++ p->zFName = (char*)&p[1]; ++ memcpy(p->zFName, zName, szName+1); ++ p->pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); ++ if( p->pMutex==0 ){ ++ memdb_g.nMemStore--; ++ sqlite3_free(p); ++ sqlite3_mutex_leave(pVfsMutex); ++ return SQLITE_NOMEM; ++ } ++ p->nRef = 1; ++ memdbEnter(p); ++ }else{ ++ memdbEnter(p); ++ p->nRef++; ++ } ++ sqlite3_mutex_leave(pVfsMutex); ++ }else{ ++ p = sqlite3Malloc( sizeof(*p) ); ++ if( p==0 ){ ++ return SQLITE_NOMEM; ++ } ++ memset(p, 0, sizeof(*p)); ++ p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; ++ p->szMax = sqlite3GlobalConfig.mxMemdbSize; + } +- memset(p, 0, sizeof(*p)); +- p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; +- assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ +- *pOutFlags = flags | SQLITE_OPEN_MEMORY; +- pFile->pMethods = &memdb_io_methods; +- p->szMax = sqlite3GlobalConfig.mxMemdbSize; ++ pFile->pStore = p; ++ if( pOutFlags!=0 ){ ++ *pOutFlags = flags | SQLITE_OPEN_MEMORY; ++ } ++ pFd->pMethods = &memdb_io_methods; ++ memdbLeave(p); + return SQLITE_OK; + } + +@@ -48206,6 +54456,9 @@ static int memdbAccess( + int flags, + int *pResOut + ){ ++ UNUSED_PARAMETER(pVfs); ++ UNUSED_PARAMETER(zPath); ++ UNUSED_PARAMETER(flags); + *pResOut = 0; + return SQLITE_OK; + } +@@ -48221,6 +54474,7 @@ static int memdbFullPathname( + int nOut, + char *zOut + ){ ++ UNUSED_PARAMETER(pVfs); + sqlite3_snprintf(nOut, zOut, "%s", zPath); + return SQLITE_OK; + } +@@ -48293,9 +54547,14 @@ static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ + */ + static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){ + MemFile *p = 0; ++ MemStore *pStore; + int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p); + if( rc ) return 0; + if( p->base.pMethods!=&memdb_io_methods ) return 0; ++ pStore = p->pStore; ++ memdbEnter(pStore); ++ if( pStore->zFName!=0 ) p = 0; ++ memdbLeave(pStore); + return p; + } + +@@ -48331,12 +54590,14 @@ SQLITE_API unsigned char *sqlite3_serialize( + if( piSize ) *piSize = -1; + if( iDb<0 ) return 0; + if( p ){ +- if( piSize ) *piSize = p->sz; ++ MemStore *pStore = p->pStore; ++ assert( pStore->pMutex==0 ); ++ if( piSize ) *piSize = pStore->sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ +- pOut = p->aData; ++ pOut = pStore->aData; + }else{ +- pOut = sqlite3_malloc64( p->sz ); +- if( pOut ) memcpy(pOut, p->aData, p->sz); ++ pOut = sqlite3_malloc64( pStore->sz ); ++ if( pOut ) memcpy(pOut, pStore->aData, pStore->sz); + } + return pOut; + } +@@ -48352,6 +54613,14 @@ SQLITE_API unsigned char *sqlite3_serialize( + pOut = 0; + }else{ + sz = sqlite3_column_int64(pStmt, 0)*szPage; ++ if( sz==0 ){ ++ sqlite3_reset(pStmt); ++ sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0); ++ rc = sqlite3_step(pStmt); ++ if( rc==SQLITE_ROW ){ ++ sz = sqlite3_column_int64(pStmt, 0)*szPage; ++ } ++ } + if( piSize ) *piSize = sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = 0; +@@ -48406,13 +54675,18 @@ SQLITE_API int sqlite3_deserialize( + sqlite3_mutex_enter(db->mutex); + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + iDb = sqlite3FindDbName(db, zSchema); +- if( iDb<0 ){ ++ testcase( iDb==1 ); ++ if( iDb<2 && iDb!=0 ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema); +- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); +- sqlite3_free(zSql); ++ if( zSql==0 ){ ++ rc = SQLITE_NOMEM; ++ }else{ ++ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); ++ sqlite3_free(zSql); ++ } + if( rc ) goto end_deserialize; + db->init.iDb = (u8)iDb; + db->init.reopenMemdb = 1; +@@ -48426,30 +54700,44 @@ SQLITE_API int sqlite3_deserialize( + if( p==0 ){ + rc = SQLITE_ERROR; + }else{ +- p->aData = pData; +- p->sz = szDb; +- p->szAlloc = szBuf; +- p->szMax = szBuf; +- if( p->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; +- } +- p->mFlags = mFlags; ++ MemStore *pStore = p->pStore; ++ pStore->aData = pData; ++ pData = 0; ++ pStore->sz = szDb; ++ pStore->szAlloc = szBuf; ++ pStore->szMax = szBuf; ++ if( pStore->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; ++ } ++ pStore->mFlags = mFlags; + rc = SQLITE_OK; + } + + end_deserialize: + sqlite3_finalize(pStmt); ++ if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){ ++ sqlite3_free(pData); ++ } + sqlite3_mutex_leave(db->mutex); + return rc; + } + ++/* ++** Return true if the VFS is the memvfs. ++*/ ++SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs *pVfs){ ++ return pVfs==&memdb_vfs; ++} ++ + /* + ** This routine is called when the extension is loaded. + ** Register the new VFS. + */ + SQLITE_PRIVATE int sqlite3MemdbInit(void){ + sqlite3_vfs *pLower = sqlite3_vfs_find(0); +- int sz = pLower->szOsFile; ++ unsigned int sz; ++ if( NEVER(pLower==0) ) return SQLITE_ERROR; ++ sz = pLower->szOsFile; + memdb_vfs.pAppData = pLower; + /* The following conditional can only be true when compiled for + ** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave +@@ -48459,7 +54747,7 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){ + memdb_vfs.szOsFile = sz; + return sqlite3_vfs_register(&memdb_vfs, 0); + } +-#endif /* SQLITE_ENABLE_DESERIALIZE */ ++#endif /* SQLITE_OMIT_DESERIALIZE */ + + /************** End of memdb.c ***********************************************/ + /************** Begin file bitvec.c ******************************************/ +@@ -48532,7 +54820,7 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){ + ** no fewer collisions than the no-op *1. */ + #define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT) + +-#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *)) ++#define BITVEC_NPTR ((u32)(BITVEC_USIZE/sizeof(Bitvec *))) + + + /* +@@ -48653,7 +54941,7 @@ SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){ + h = BITVEC_HASH(i++); + /* if there wasn't a hash collision, and this doesn't */ + /* completely fill the hash, then just add it without */ +- /* worring about sub-dividing and re-hashing. */ ++ /* worrying about sub-dividing and re-hashing. */ + if( !p->u.aHash[h] ){ + if (p->nSet<(BITVEC_NINT-1)) { + goto bitvec_set_end; +@@ -48681,7 +54969,9 @@ bitvec_set_rehash: + }else{ + memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); + memset(p->u.apSub, 0, sizeof(p->u.apSub)); +- p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR; ++ p->iDivisor = p->iSize/BITVEC_NPTR; ++ if( (p->iSize%BITVEC_NPTR)!=0 ) p->iDivisor++; ++ if( p->iDivisoriDivisor = BITVEC_NBIT; + rc = sqlite3BitvecSet(p, i); + for(j=0; jiSize<=BITVEC_NBIT ){ +- p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); ++ p->u.aBitmap[i/BITVEC_SZELEM] &= ~(BITVEC_TELEM)(1<<(i&(BITVEC_SZELEM-1))); + }else{ + unsigned int j; + u32 *aiValues = pBuf; +@@ -48766,7 +55056,7 @@ SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec *p){ + ** individual bits within V. + */ + #define SETBIT(V,I) V[I>>3] |= (1<<(I&7)) +-#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) ++#define CLEARBIT(V,I) V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7)) + #define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 + + /* +@@ -48809,7 +55099,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ + /* Allocate the Bitvec to be tested and a linear array of + ** bits to act as the reference */ + pBitvec = sqlite3BitvecCreate( sz ); +- pV = sqlite3MallocZero( (sz+7)/8 + 1 ); ++ pV = sqlite3MallocZero( (7+(i64)sz)/8 + 1 ); + pTmpSpace = sqlite3_malloc64(BITVEC_SZ); + if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; + +@@ -48818,7 +55108,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ + sqlite3BitvecClear(0, 1, pTmpSpace); + + /* Run the program */ +- pc = 0; ++ pc = i = 0; + while( (op = aOp[pc])!=0 ){ + switch( op ){ + case 1: +@@ -48920,7 +55210,7 @@ bitvec_end: + struct PCache { + PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ + PgHdr *pSynced; /* Last synced page in dirty page list */ +- int nRefSum; /* Sum of ref counts over all pages */ ++ i64 nRefSum; /* Sum of ref counts over all pages */ + int szCache; /* Configured cache size */ + int szSpill; /* Size before spilling occurs */ + int szPage; /* Size of every page in this cache */ +@@ -48945,12 +55235,24 @@ struct PCache { + int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */ + int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */ + # define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;} +- void pcacheDump(PCache *pCache){ +- int N; +- int i, j; +- sqlite3_pcache_page *pLower; ++ static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){ + PgHdr *pPg; + unsigned char *a; ++ int j; ++ if( pLower==0 ){ ++ printf("%3d: NULL\n", i); ++ }else{ ++ pPg = (PgHdr*)pLower->pExtra; ++ printf("%3d: nRef %2lld flgs %02x data ", i, pPg->nRef, pPg->flags); ++ a = (unsigned char *)pLower->pBuf; ++ for(j=0; j<12; j++) printf("%02x", a[j]); ++ printf(" ptr %p\n", pPg); ++ } ++ } ++ static void pcacheDump(PCache *pCache){ ++ int N; ++ int i; ++ sqlite3_pcache_page *pLower; + + if( sqlite3PcacheTrace<2 ) return; + if( pCache->pCache==0 ) return; +@@ -48958,22 +55260,42 @@ struct PCache { + if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump; + for(i=1; i<=N; i++){ + pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); +- if( pLower==0 ) continue; +- pPg = (PgHdr*)pLower->pExtra; +- printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); +- a = (unsigned char *)pLower->pBuf; +- for(j=0; j<12; j++) printf("%02x", a[j]); +- printf("\n"); +- if( pPg->pPage==0 ){ ++ pcachePageTrace(i, pLower); ++ if( pLower && ((PgHdr*)pLower)->pPage==0 ){ + sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); + } + } + } +- #else ++#else + # define pcacheTrace(X) ++# define pcachePageTrace(PGNO, X) + # define pcacheDump(X) + #endif + ++/* ++** Return 1 if pPg is on the dirty list for pCache. Return 0 if not. ++** This routine runs inside of assert() statements only. ++*/ ++#if defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) ++static int pageOnDirtyList(PCache *pCache, PgHdr *pPg){ ++ PgHdr *p; ++ for(p=pCache->pDirty; p; p=p->pDirtyNext){ ++ if( p==pPg ) return 1; ++ } ++ return 0; ++} ++static int pageNotOnDirtyList(PCache *pCache, PgHdr *pPg){ ++ PgHdr *p; ++ for(p=pCache->pDirty; p; p=p->pDirtyNext){ ++ if( p==pPg ) return 0; ++ } ++ return 1; ++} ++#else ++# define pageOnDirtyList(A,B) 1 ++# define pageNotOnDirtyList(A,B) 1 ++#endif ++ + /* + ** Check invariants on a PgHdr entry. Return true if everything is OK. + ** Return false if any invariant is violated. +@@ -48992,8 +55314,13 @@ SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){ + assert( pCache!=0 ); /* Every page has an associated PCache */ + if( pPg->flags & PGHDR_CLEAN ){ + assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ +- assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */ +- assert( pCache->pDirtyTail!=pPg ); ++ assert( pageNotOnDirtyList(pCache, pPg) );/* CLEAN pages not on dirtylist */ ++ }else{ ++ assert( (pPg->flags & PGHDR_DIRTY)!=0 );/* If not CLEAN must be DIRTY */ ++ assert( pPg->pDirtyNext==0 || pPg->pDirtyNext->pDirtyPrev==pPg ); ++ assert( pPg->pDirtyPrev==0 || pPg->pDirtyPrev->pDirtyNext==pPg ); ++ assert( pPg->pDirtyPrev!=0 || pCache->pDirty==pPg ); ++ assert( pageOnDirtyList(pCache, pPg) ); + } + /* WRITEABLE pages must also be DIRTY */ + if( pPg->flags & PGHDR_WRITEABLE ){ +@@ -49122,11 +55449,14 @@ static int numberOfCachePages(PCache *p){ + ** suggested cache size is set to N. */ + return p->szCache; + }else{ +- /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the ++ i64 n; ++ /* IMPLEMENTATION-OF: R-59858-46238 If the argument N is negative, then the + ** number of cache pages is adjusted to be a number of pages that would + ** use approximately abs(N*1024) bytes of memory based on the current + ** page size. */ +- return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); ++ n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); ++ if( n>1000000000 ) n = 1000000000; ++ return (int)n; + } + } + +@@ -49264,8 +55594,9 @@ SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch( + assert( createFlag==0 || pCache->eCreate==eCreate ); + assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); + pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); +- pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno, ++ pcacheTrace(("%p.FETCH %d%s (result: %p) ",pCache,pgno, + createFlag?" create":"",pRes)); ++ pcachePageTrace(pgno, pRes); + return pRes; + } + +@@ -49350,6 +55681,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( + pPgHdr->pData = pPage->pBuf; + pPgHdr->pExtra = (void *)&pPgHdr[1]; + memset(pPgHdr->pExtra, 0, 8); ++ assert( EIGHT_BYTE_ALIGNMENT( pPgHdr->pExtra ) ); + pPgHdr->pCache = pCache; + pPgHdr->pgno = pgno; + pPgHdr->flags = PGHDR_CLEAN; +@@ -49393,6 +55725,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ + pcacheUnpin(p); + }else{ + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); ++ assert( sqlite3PcachePageSanity(p) ); + } + } + } +@@ -49436,6 +55769,7 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){ + pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno)); + assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY ); + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); ++ assert( sqlite3PcachePageSanity(p) ); + } + assert( sqlite3PcachePageSanity(p) ); + } +@@ -49498,14 +55832,24 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){ + */ + SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ + PCache *pCache = p->pCache; ++ sqlite3_pcache_page *pOther; + assert( p->nRef>0 ); + assert( newPgno>0 ); + assert( sqlite3PcachePageSanity(p) ); + pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno)); ++ pOther = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0); ++ if( pOther ){ ++ PgHdr *pXPage = (PgHdr*)pOther->pExtra; ++ assert( pXPage->nRef==0 ); ++ pXPage->nRef++; ++ pCache->nRefSum++; ++ sqlite3PcacheDrop(pXPage); ++ } + sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); + p->pgno = newPgno; + if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); ++ assert( sqlite3PcachePageSanity(p) ); + } + } + +@@ -49595,7 +55939,7 @@ static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ + } + + /* +-** Sort the list of pages in accending order by pgno. Pages are ++** Sort the list of pages in ascending order by pgno. Pages are + ** connected by pDirty pointers. The pDirtyPrev pointers are + ** corrupted by this sort. + ** +@@ -49654,14 +55998,14 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){ + ** This is not the total number of pages referenced, but the sum of the + ** reference count for all pages. + */ +-SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){ ++SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache *pCache){ + return pCache->nRefSum; + } + + /* + ** Return the number of references to the page supplied as an argument. + */ +-SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){ ++SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr *p){ + return p->nRef; + } + +@@ -49803,12 +56147,13 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd + ** size can vary according to architecture, compile-time options, and + ** SQLite library version number. + ** +-** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained +-** using a separate memory allocation from the database page content. This +-** seeks to overcome the "clownshoe" problem (also called "internal +-** fragmentation" in academic literature) of allocating a few bytes more +-** than a power of two with the memory allocator rounding up to the next +-** power of two, and leaving the rounded-up space unused. ++** Historical note: It used to be that if the SQLITE_PCACHE_SEPARATE_HEADER ++** was defined, then the page content would be held in a separate memory ++** allocation from the PgHdr1. This was intended to avoid clownshoe memory ++** allocations. However, the btree layer needs a small (16-byte) overrun ++** area after the page content buffer. The header serves as that overrun ++** area. Therefore SQLITE_PCACHE_SEPARATE_HEADER was discontinued to avoid ++** any possibility of a memory error. + ** + ** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates + ** with this module. Information is passed back and forth as PgHdr1 pointers. +@@ -49834,7 +56179,7 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd + ** If N is positive, then N pages worth of memory are allocated using a single + ** sqlite3Malloc() call and that memory is used for the first N pages allocated. + ** Or if N is negative, then -1024*N bytes of memory are allocated and used +-** for as many pages as can be accomodated. ++** for as many pages as can be accommodated. + ** + ** Only one of (2) or (3) can be used. Once the memory available to (2) or + ** (3) is exhausted, subsequent allocations fail over to the general-purpose +@@ -49853,30 +56198,40 @@ typedef struct PGroup PGroup; + + /* + ** Each cache entry is represented by an instance of the following +-** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of +-** PgHdr1.pCache->szPage bytes is allocated directly before this structure +-** in memory. ++** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated ++** directly before this structure and is used to cache the page content. ++** ++** When reading a corrupt database file, it is possible that SQLite might ++** read a few bytes (no more than 16 bytes) past the end of the page buffer. ++** It will only read past the end of the page buffer, never write. This ++** object is positioned immediately after the page buffer to serve as an ++** overrun area, so that overreads are harmless. + ** +-** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, ++** Variables isBulkLocal and isAnchor were once type "u8". That works, + ** but causes a 2-byte gap in the structure for most architectures (since + ** pointers must be either 4 or 8-byte aligned). As this structure is located + ** in memory directly after the associated page data, if the database is + ** corrupt, code at the b-tree layer may overread the page buffer and + ** read part of this structure before the corruption is detected. This +-** can cause a valgrind error if the unitialized gap is accessed. Using u16 +-** ensures there is no such gap, and therefore no bytes of unitialized memory +-** in the structure. ++** can cause a valgrind error if the uninitialized gap is accessed. Using u16 ++** ensures there is no such gap, and therefore no bytes of uninitialized ++** memory in the structure. ++** ++** The pLruNext and pLruPrev pointers form a double-linked circular list ++** of all pages that are unpinned. The PGroup.lru element (which should be ++** the only element on the list with PgHdr1.isAnchor set to 1) forms the ++** beginning and the end of the list. + */ + struct PgHdr1 { +- sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ +- unsigned int iKey; /* Key value (page number) */ +- u16 isBulkLocal; /* This page from bulk local storage */ +- u16 isAnchor; /* This is the PGroup.lru element */ +- PgHdr1 *pNext; /* Next in hash table chain */ +- PCache1 *pCache; /* Cache that currently owns this page */ +- PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ +- PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ +- /* NB: pLruPrev is only valid if pLruNext!=0 */ ++ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ ++ unsigned int iKey; /* Key value (page number) */ ++ u16 isBulkLocal; /* This page from bulk local storage */ ++ u16 isAnchor; /* This is the PGroup.lru element */ ++ PgHdr1 *pNext; /* Next in hash table chain */ ++ PCache1 *pCache; /* Cache that currently owns this page */ ++ PgHdr1 *pLruNext; /* Next in circular LRU list of unpinned pages */ ++ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ ++ /* NB: pLruPrev is only valid if pLruNext!=0 */ + }; + + /* +@@ -49985,10 +56340,6 @@ static SQLITE_WSD struct PCacheGlobal { + sqlite3_mutex *mutex; /* Mutex for accessing the following: */ + PgFreeslot *pFree; /* Free page blocks */ + int nFreeSlot; /* Number of unused pcache slots */ +- /* The following value requires a mutex to change. We skip the mutex on +- ** reading because (1) most platforms read a 32-bit integer atomically and +- ** (2) even if an incorrect value is read, no great harm is done since this +- ** is really just an optimization. */ + int bUnderPressure; /* True if low on PAGECACHE memory */ + } pcache1_g; + +@@ -50036,7 +56387,7 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ + pcache1.nReserve = n>90 ? 10 : (n/10 + 1); + pcache1.pStart = pBuf; + pcache1.pFree = 0; +- pcache1.bUnderPressure = 0; ++ AtomicStore(&pcache1.bUnderPressure,0); + while( n-- ){ + p = (PgFreeslot*)pBuf; + p->pNext = pcache1.pFree; +@@ -50073,7 +56424,8 @@ static int pcache1InitBulk(PCache1 *pCache){ + do{ + PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; + pX->page.pBuf = zBulk; +- pX->page.pExtra = &pX[1]; ++ pX->page.pExtra = (u8*)pX + ROUND8(sizeof(*pX)); ++ assert( EIGHT_BYTE_ALIGNMENT( pX->page.pExtra ) ); + pX->isBulkLocal = 1; + pX->isAnchor = 0; + pX->pNext = pCache->pFree; +@@ -50103,7 +56455,7 @@ static void *pcache1Alloc(int nByte){ + if( p ){ + pcache1.pFree = pcache1.pFree->pNext; + pcache1.nFreeSlot--; +- pcache1.bUnderPressure = pcache1.nFreeSlot=0 ); + sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); + sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); +@@ -50142,7 +56494,7 @@ static void pcache1Free(void *p){ + pSlot->pNext = pcache1.pFree; + pcache1.pFree = pSlot; + pcache1.nFreeSlot++; +- pcache1.bUnderPressure = pcache1.nFreeSlotpGroup); + #endif + if( benignMalloc ){ sqlite3BeginBenignMalloc(); } +-#ifdef SQLITE_PCACHE_SEPARATE_HEADER +- pPg = pcache1Alloc(pCache->szPage); +- p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); +- if( !pPg || !p ){ +- pcache1Free(pPg); +- sqlite3_free(p); +- pPg = 0; +- } +-#else + pPg = pcache1Alloc(pCache->szAlloc); +-#endif + if( benignMalloc ){ sqlite3EndBenignMalloc(); } + #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + pcache1EnterMutex(pCache->pGroup); + #endif + if( pPg==0 ) return 0; +-#ifndef SQLITE_PCACHE_SEPARATE_HEADER + p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; +-#endif + p->page.pBuf = pPg; +- p->page.pExtra = &p[1]; ++ p->page.pExtra = (u8*)p + ROUND8(sizeof(*p)); ++ assert( EIGHT_BYTE_ALIGNMENT( p->page.pExtra ) ); + p->isBulkLocal = 0; + p->isAnchor = 0; ++ p->pLruPrev = 0; /* Initializing this saves a valgrind error */ + } + (*pCache->pnPurgeable)++; + return p; +@@ -50243,9 +56585,6 @@ static void pcache1FreePage(PgHdr1 *p){ + pCache->pFree = p; + }else{ + pcache1Free(p->page.pBuf); +-#ifdef SQLITE_PCACHE_SEPARATE_HEADER +- sqlite3_free(p); +-#endif + } + (*pCache->pnPurgeable)--; + } +@@ -50286,7 +56625,7 @@ SQLITE_PRIVATE void sqlite3PageFree(void *p){ + */ + static int pcache1UnderMemoryPressure(PCache1 *pCache){ + if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ +- return pcache1.bUnderPressure; ++ return AtomicLoad(&pcache1.bUnderPressure); + }else{ + return sqlite3HeapNearlyFull(); + } +@@ -50303,12 +56642,12 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){ + */ + static void pcache1ResizeHash(PCache1 *p){ + PgHdr1 **apNew; +- unsigned int nNew; +- unsigned int i; ++ u64 nNew; ++ u32 i; + + assert( sqlite3_mutex_held(p->pGroup->mutex) ); + +- nNew = p->nHash*2; ++ nNew = 2*(u64)p->nHash; + if( nNew<256 ){ + nNew = 256; + } +@@ -50531,7 +56870,7 @@ static void pcache1Destroy(sqlite3_pcache *p); + static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ + PCache1 *pCache; /* The newly created page cache */ + PGroup *pGroup; /* The group the new page cache will belong to */ +- int sz; /* Bytes of memory required to allocate the new cache */ ++ i64 sz; /* Bytes of memory required to allocate the new cache */ + + assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); + assert( szExtra < 300 ); +@@ -50580,12 +56919,18 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ + */ + static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ + PCache1 *pCache = (PCache1 *)p; ++ u32 n; ++ assert( nMax>=0 ); + if( pCache->bPurgeable ){ + PGroup *pGroup = pCache->pGroup; + pcache1EnterMutex(pGroup); +- pGroup->nMaxPage += (nMax - pCache->nMax); ++ n = (u32)nMax; ++ if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){ ++ n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax; ++ } ++ pGroup->nMaxPage += (n - pCache->nMax); + pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; +- pCache->nMax = nMax; ++ pCache->nMax = n; + pCache->n90pct = pCache->nMax*9/10; + pcache1EnforceMaxPage(pCache); + pcache1LeaveMutex(pGroup); +@@ -50601,7 +56946,7 @@ static void pcache1Shrink(sqlite3_pcache *p){ + PCache1 *pCache = (PCache1*)p; + if( pCache->bPurgeable ){ + PGroup *pGroup = pCache->pGroup; +- int savedMaxPage; ++ unsigned int savedMaxPage; + pcache1EnterMutex(pGroup); + savedMaxPage = pGroup->nMaxPage; + pGroup->nMaxPage = 0; +@@ -50880,23 +57225,26 @@ static void pcache1Rekey( + PCache1 *pCache = (PCache1 *)p; + PgHdr1 *pPage = (PgHdr1 *)pPg; + PgHdr1 **pp; +- unsigned int h; ++ unsigned int hOld, hNew; + assert( pPage->iKey==iOld ); + assert( pPage->pCache==pCache ); ++ assert( iOld!=iNew ); /* The page number really is changing */ + + pcache1EnterMutex(pCache->pGroup); + +- h = iOld%pCache->nHash; +- pp = &pCache->apHash[h]; ++ assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */ ++ hOld = iOld%pCache->nHash; ++ pp = &pCache->apHash[hOld]; + while( (*pp)!=pPage ){ + pp = &(*pp)->pNext; + } + *pp = pPage->pNext; + +- h = iNew%pCache->nHash; ++ assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */ ++ hNew = iNew%pCache->nHash; + pPage->iKey = iNew; +- pPage->pNext = pCache->apHash[h]; +- pCache->apHash[h] = pPage; ++ pPage->pNext = pCache->apHash[hNew]; ++ pCache->apHash[hNew] = pPage; + if( iNew>pCache->iMaxKey ){ + pCache->iMaxKey = iNew; + } +@@ -51003,9 +57351,6 @@ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ + && p->isAnchor==0 + ){ + nFree += pcache1MemSize(p->page.pBuf); +-#ifdef SQLITE_PCACHE_SEPARATE_HEADER +- nFree += sqlite3MemSize(p); +-#endif + assert( PAGE_IS_UNPINNED(p) ); + pcache1PinPage(p); + pcache1RemoveFromHash(p, 1); +@@ -51086,7 +57431,7 @@ SQLITE_PRIVATE void sqlite3PcacheStats( + ** The TEST primitive includes a "batch" number. The TEST primitive + ** will only see elements that were inserted before the last change + ** in the batch number. In other words, if an INSERT occurs between +-** two TESTs where the TESTs have the same batch nubmer, then the ++** two TESTs where the TESTs have the same batch number, then the + ** value added by the INSERT will not be visible to the second TEST. + ** The initial batch number is zero, so if the very first TEST contains + ** a non-zero batch number, it will see all prior INSERTs. +@@ -51618,6 +57963,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 + # define sqlite3WalFramesize(z) 0 + # define sqlite3WalFindFrame(x,y,z) 0 + # define sqlite3WalFile(x) 0 ++# undef SQLITE_USE_SEH + #else + + #define WAL_SAVEPOINT_NDATA 4 +@@ -51724,6 +58070,10 @@ SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock); + SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db); + #endif + ++#ifdef SQLITE_USE_SEH ++SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal*); ++#endif ++ + #endif /* ifndef SQLITE_OMIT_WAL */ + #endif /* SQLITE_WAL_H */ + +@@ -52009,7 +58359,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ + ** outstanding transactions have been abandoned, the pager is able to + ** transition back to OPEN state, discarding the contents of the + ** page-cache and any other in-memory state at the same time. Everything +-** is reloaded from disk (and, if necessary, hot-journal rollback peformed) ++** is reloaded from disk (and, if necessary, hot-journal rollback performed) + ** when a read-transaction is next opened on the pager (transitioning + ** the pager into READER state). At that point the system has recovered + ** from the error. +@@ -52143,6 +58493,7 @@ struct PagerSavepoint { + Bitvec *pInSavepoint; /* Set of pages in this savepoint */ + Pgno nOrig; /* Original number of pages in file */ + Pgno iSubRec; /* Index of first record in sub-journal */ ++ int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */ + #ifndef SQLITE_OMIT_WAL + u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ + #endif +@@ -52337,6 +58688,7 @@ struct Pager { + u8 noLock; /* Do not lock (except in WAL mode) */ + u8 readOnly; /* True for a read-only database */ + u8 memDb; /* True to inhibit all file I/O */ ++ u8 memVfs; /* VFS-implemented memory database */ + + /************************************************************************** + ** The following block contains those class members that change during +@@ -52386,14 +58738,15 @@ struct Pager { + i16 nReserve; /* Number of unused bytes at end of each page */ + u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ + u32 sectorSize; /* Assumed sector size during rollback */ +- int pageSize; /* Number of bytes in a page */ + Pgno mxPgno; /* Maximum allowed size of the database */ ++ Pgno lckPgno; /* Page number for the locking page */ ++ i64 pageSize; /* Number of bytes in a page */ + i64 journalSizeLimit; /* Size limit for persistent journal files */ + char *zFilename; /* Name of the database file */ + char *zJournal; /* Name of the journal file */ + int (*xBusyHandler)(void*); /* Function to call when busy */ + void *pBusyHandlerArg; /* Context argument for xBusyHandler */ +- int aStat[4]; /* Total cache hits, misses, writes, spills */ ++ u32 aStat[4]; /* Total cache hits, misses, writes, spills */ + #ifdef SQLITE_TEST + int nRead; /* Database pages read */ + #endif +@@ -52405,6 +58758,9 @@ struct Pager { + Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ + char *zWal; /* File name for write-ahead log */ + #endif ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ sqlite3 *dbWal; ++#endif + }; + + /* +@@ -52494,40 +58850,33 @@ static const unsigned char aJournalMagic[] = { + # define USEFETCH(x) 0 + #endif + +-/* +-** The argument to this macro is a file descriptor (type sqlite3_file*). +-** Return 0 if it is not open, or non-zero (but not 1) if it is. +-** +-** This is so that expressions can be written as: +-** +-** if( isOpen(pPager->jfd) ){ ... +-** +-** instead of +-** +-** if( pPager->jfd->pMethods ){ ... +-*/ +-#define isOpen(pFd) ((pFd)->pMethods!=0) +- + #ifdef SQLITE_DIRECT_OVERFLOW_READ + /* + ** Return true if page pgno can be read directly from the database file + ** by the b-tree layer. This is the case if: + ** +-** * the database file is open, +-** * there are no dirty pages in the cache, and +-** * the desired page is not currently in the wal file. ++** (1) the database file is open ++** (2) the VFS for the database is able to do unaligned sub-page reads ++** (3) there are no dirty pages in the cache, and ++** (4) the desired page is not currently in the wal file. + */ + SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ +- if( pPager->fd->pMethods==0 ) return 0; +- if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; ++ assert( pPager!=0 ); ++ assert( pPager->fd!=0 ); ++ if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */ ++ if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */ + #ifndef SQLITE_OMIT_WAL + if( pPager->pWal ){ + u32 iRead = 0; +- int rc; +- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); +- return (rc==SQLITE_OK && iRead==0); ++ (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); ++ if( iRead ) return 0; /* Case (4) */ + } + #endif ++ assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); ++ if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd) ++ & SQLITE_IOCAP_SUBPAGE_READ)==0 ){ ++ return 0; /* Case (2) */ ++ } + return 1; + } + #endif +@@ -52778,6 +59127,9 @@ static int subjRequiresPage(PgHdr *pPg){ + for(i=0; inSavepoint; i++){ + p = &pPager->aSavepoint[i]; + if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){ ++ for(i=i+1; inSavepoint; i++){ ++ pPager->aSavepoint[i].bTruncateOnRelease = 0; ++ } + return 1; + } + } +@@ -53000,7 +59352,7 @@ static void checkPage(PgHdr *pPg){ + ** If an error occurs while reading from the journal file, an SQLite + ** error code is returned. + */ +-static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u32 nSuper){ ++static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u64 nSuper){ + int rc; /* Return code */ + u32 len; /* Length in bytes of super-journal name */ + i64 szJ; /* Total size in bytes of journal file pJrnl */ +@@ -53194,9 +59546,32 @@ static int writeJournalHdr(Pager *pPager){ + memset(zHeader, 0, sizeof(aJournalMagic)+4); + } + ++ ++ + /* The random check-hash initializer */ +- sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); ++ if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ ++ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); ++ } ++#ifdef SQLITE_DEBUG ++ else{ ++ /* The Pager.cksumInit variable is usually randomized above to protect ++ ** against there being existing records in the journal file. This is ++ ** dangerous, as following a crash they may be mistaken for records ++ ** written by the current transaction and rolled back into the database ++ ** file, causing corruption. The following assert statements verify ++ ** that this is not required in "journal_mode=memory" mode, as in that ++ ** case the journal file is always 0 bytes in size at this point. ++ ** It is advantageous to avoid the sqlite3_randomness() call if possible ++ ** as it takes the global PRNG mutex. */ ++ i64 sz = 0; ++ sqlite3OsFileSize(pPager->jfd, &sz); ++ assert( sz==0 ); ++ assert( pPager->journalOff==journalHdrOffset(pPager) ); ++ assert( sqlite3JournalIsInMemory(pPager->jfd) ); ++ } ++#endif + put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); ++ + /* The initial database size */ + put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize); + /* The assumed sector size for this process */ +@@ -53370,13 +59745,13 @@ static int readJournalHdr( + ** journal file descriptor is advanced to the next sector boundary before + ** anything is written. The format is: + ** +-** + 4 bytes: PAGER_MJ_PGNO. ++** + 4 bytes: PAGER_SJ_PGNO. + ** + N bytes: super-journal filename in utf-8. + ** + 4 bytes: N (length of super-journal name in bytes, no nul-terminator). + ** + 4 bytes: super-journal name checksum. + ** + 8 bytes: aJournalMagic[]. + ** +-** The super-journal page checksum is the sum of the bytes in thesuper-journal ++** The super-journal page checksum is the sum of the bytes in the super-journal + ** name, where each byte is interpreted as a signed 8-bit integer. + ** + ** If zSuper is a NULL pointer (occurs for a single database transaction), +@@ -53418,7 +59793,7 @@ static int writeSuperJournal(Pager *pPager, const char *zSuper){ + /* Write the super-journal data to the end of the journal file. If + ** an error occurs, return the error code to the caller. + */ +- if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager)))) ++ if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_SJ_PGNO(pPager)))) + || (0 != (rc = sqlite3OsWrite(pPager->jfd, zSuper, nSuper, iHdrOff+4))) + || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper, nSuper))) + || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper+4, cksum))) +@@ -53429,7 +59804,7 @@ static int writeSuperJournal(Pager *pPager, const char *zSuper){ + } + pPager->journalOff += (nSuper+20); + +- /* If the pager is in peristent-journal mode, then the physical ++ /* If the pager is in persistent-journal mode, then the physical + ** journal-file may extend past the end of the super-journal name + ** and 8 bytes of magic data just written to the file. This is + ** dangerous because the code to rollback a hot-journal file +@@ -53532,6 +59907,15 @@ static void pager_unlock(Pager *pPager){ + + if( pagerUseWal(pPager) ){ + assert( !isOpen(pPager->jfd) ); ++ if( pPager->eState==PAGER_ERROR ){ ++ /* If an IO error occurs in wal.c while attempting to wrap the wal file, ++ ** then the Wal object may be holding a write-lock but no read-lock. ++ ** This call ensures that the write-lock is dropped as well. We cannot ++ ** have sqlite3WalEndReadTransaction() drop the write-lock, as it once ++ ** did, because this would break "BEGIN EXCLUSIVE" handling for ++ ** SQLITE_ENABLE_SETLK_TIMEOUT builds. */ ++ sqlite3WalEndWriteTransaction(pPager->pWal); ++ } + sqlite3WalEndReadTransaction(pPager->pWal); + pPager->eState = PAGER_OPEN; + }else if( !pPager->exclusiveMode ){ +@@ -53599,7 +59983,7 @@ static void pager_unlock(Pager *pPager){ + + /* + ** This function is called whenever an IOERR or FULL error that requires +-** the pager to transition into the ERROR state may ahve occurred. ++** the pager to transition into the ERROR state may have occurred. + ** The first argument is a pointer to the pager structure, the second + ** the error-code about to be returned by a pager API function. The + ** value returned is a copy of the second argument to this function. +@@ -53760,7 +60144,7 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ + } + pPager->journalOff = 0; + }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST +- || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) ++ || (pPager->exclusiveMode && pPager->journalModetempFile); + pPager->journalOff = 0; +@@ -53840,6 +60224,9 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ + return (rc==SQLITE_OK?rc2:rc); + } + ++/* Forward reference */ ++static int pager_playback(Pager *pPager, int isHot); ++ + /* + ** Execute a rollback if a transaction is active and unlock the + ** database file. +@@ -53868,13 +60255,28 @@ static void pagerUnlockAndRollback(Pager *pPager){ + assert( pPager->eState==PAGER_READER ); + pager_end_transaction(pPager, 0, 0); + } ++ }else if( pPager->eState==PAGER_ERROR ++ && pPager->journalMode==PAGER_JOURNALMODE_MEMORY ++ && isOpen(pPager->jfd) ++ ){ ++ /* Special case for a ROLLBACK due to I/O error with an in-memory ++ ** journal: We have to rollback immediately, before the journal is ++ ** closed, because once it is closed, all content is forgotten. */ ++ int errCode = pPager->errCode; ++ u8 eLock = pPager->eLock; ++ pPager->eState = PAGER_OPEN; ++ pPager->errCode = SQLITE_OK; ++ pPager->eLock = EXCLUSIVE_LOCK; ++ pager_playback(pPager, 1); ++ pPager->errCode = errCode; ++ pPager->eLock = eLock; + } + pager_unlock(pPager); + } + + /* + ** Parameter aData must point to a buffer of pPager->pageSize bytes +-** of data. Compute and return a checksum based ont the contents of the ++** of data. Compute and return a checksum based on the contents of the + ** page of data and the current value of pPager->cksumInit. + ** + ** This is not a real checksum. It is really just the sum of the +@@ -53928,7 +60330,7 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ + ** corrupted, SQLITE_DONE is returned. Data is considered corrupted in + ** two circumstances: + ** +-** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or ++** * If the record page-number is illegal (0 or PAGER_SJ_PGNO), or + ** * If the record is being rolled back from the main journal file + ** and the checksum field does not match the record content. + ** +@@ -53988,7 +60390,7 @@ static int pager_playback_one_page( + ** it could cause invalid data to be written into the journal. We need to + ** detect this invalid data (with high probability) and ignore it. + */ +- if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ ++ if( pgno==0 || pgno==PAGER_SJ_PGNO(pPager) ){ + assert( !isSavepnt ); + return SQLITE_DONE; + } +@@ -54194,12 +60596,13 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ + i64 nSuperJournal; /* Size of super-journal file */ + char *zJournal; /* Pointer to one journal within MJ file */ + char *zSuperPtr; /* Space to hold super-journal filename */ +- int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ ++ char *zFree = 0; /* Free this buffer */ ++ i64 nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ + + /* Allocate space for both the pJournal and pSuper file descriptors. + ** If successful, open the super-journal file for reading. + */ +- pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); ++ pSuper = (sqlite3_file *)sqlite3MallocZero(2 * (i64)pVfs->szOsFile); + if( !pSuper ){ + rc = SQLITE_NOMEM_BKPT; + pJournal = 0; +@@ -54217,12 +60620,17 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ + */ + rc = sqlite3OsFileSize(pSuper, &nSuperJournal); + if( rc!=SQLITE_OK ) goto delsuper_out; +- nSuperPtr = pVfs->mxPathname+1; +- zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); +- if( !zSuperJournal ){ ++ nSuperPtr = 1 + (i64)pVfs->mxPathname; ++ assert( nSuperJournal>=0 && nSuperPtr>0 ); ++ zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); ++ if( !zFree ){ + rc = SQLITE_NOMEM_BKPT; + goto delsuper_out; ++ }else{ ++ assert( nSuperJournal<=0x7fffffff ); + } ++ zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; ++ zSuperJournal = &zFree[4]; + zSuperPtr = &zSuperJournal[nSuperJournal+2]; + rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); + if( rc!=SQLITE_OK ) goto delsuper_out; +@@ -54270,7 +60678,7 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ + rc = sqlite3OsDelete(pVfs, zSuper, 0); + + delsuper_out: +- sqlite3_free(zSuperJournal); ++ sqlite3_free(zFree); + if( pSuper ){ + sqlite3OsClose(pSuper); + assert( !isOpen(pJournal) ); +@@ -54304,6 +60712,8 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ + int rc = SQLITE_OK; + assert( pPager->eState!=PAGER_ERROR ); + assert( pPager->eState!=PAGER_READER ); ++ PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage)); ++ + + if( isOpen(pPager->fd) + && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) +@@ -54322,6 +60732,7 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ + memset(pTmp, 0, szPage); + testcase( (newSize-szPage) == currentSize ); + testcase( (newSize-szPage) > currentSize ); ++ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &newSize); + rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); + } + if( rc==SQLITE_OK ){ +@@ -54477,7 +60888,7 @@ static int pager_playback(Pager *pPager, int isHot){ + ** for pageSize. + */ + zSuper = pPager->pTmpSpace; +- rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); ++ rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname); + if( rc==SQLITE_OK && zSuper[0] ){ + rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); + } +@@ -54544,6 +60955,9 @@ static int pager_playback(Pager *pPager, int isHot){ + goto end_playback; + } + pPager->dbSize = mxPg; ++ if( pPager->mxPgnomxPgno = mxPg; ++ } + } + + /* Copy original pages out of the journal and back into the +@@ -54608,8 +61022,12 @@ end_playback: + pPager->changeCountDone = pPager->tempFile; + + if( rc==SQLITE_OK ){ +- zSuper = pPager->pTmpSpace; +- rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); ++ /* Leave 4 bytes of space before the super-journal filename in memory. ++ ** This is because it may end up being passed to sqlite3OsOpen(), in ++ ** which case it requires 4 0x00 bytes in memory immediately before ++ ** the filename. */ ++ zSuper = &pPager->pTmpSpace[4]; ++ rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname); + testcase( rc!=SQLITE_OK ); + } + if( rc==SQLITE_OK +@@ -54625,6 +61043,8 @@ end_playback: + /* If there was a super-journal and this routine will return success, + ** see if it is possible to delete the super-journal. + */ ++ assert( zSuper==&pPager->pTmpSpace[4] ); ++ memset(pPager->pTmpSpace, 0, 4); + rc = pager_delsuper(pPager, zSuper); + testcase( rc!=SQLITE_OK ); + } +@@ -54719,6 +61139,7 @@ static int readDbPage(PgHdr *pPg){ + */ + static void pager_write_changecounter(PgHdr *pPg){ + u32 change_counter; ++ if( NEVER(pPg==0) ) return; + + /* Increment the value just read and write it back to byte 24. */ + change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; +@@ -54824,7 +61245,7 @@ static int pagerWalFrames( + assert( pPager->pWal ); + assert( pList ); + #ifdef SQLITE_DEBUG +- /* Verify that the page list is in accending order */ ++ /* Verify that the page list is in ascending order */ + for(p=pList; p && p->pDirty; p=p->pDirty){ + assert( p->pgno < p->pDirty->pgno ); + } +@@ -54955,7 +61376,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ + #ifndef SQLITE_OMIT_WAL + /* + ** Check if the *-wal file that corresponds to the database opened by pPager +-** exists if the database is not empy, or verify that the *-wal file does ++** exists if the database is not empty, or verify that the *-wal file does + ** not exist (by deleting it) if the database file is empty. + ** + ** If the database is not empty and the *-wal file exists, open the pager +@@ -55244,7 +61665,6 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){ + ** Numeric values associated with these states are OFF==1, NORMAL=2, + ** and FULL=3. + */ +-#ifndef SQLITE_OMIT_PAGER_PRAGMAS + SQLITE_PRIVATE void sqlite3PagerSetFlags( + Pager *pPager, /* The pager to set safety level for */ + unsigned pgFlags /* Various flags */ +@@ -55279,7 +61699,6 @@ SQLITE_PRIVATE void sqlite3PagerSetFlags( + pPager->doNotSpill |= SPILLFLAG_OFF; + } + } +-#endif + + /* + ** The following global variable is incremented whenever the library +@@ -55433,6 +61852,7 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR + pPager->pTmpSpace = pNew; + pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); + pPager->pageSize = pageSize; ++ pPager->lckPgno = (Pgno)(PENDING_BYTE/pageSize) + 1; + }else{ + sqlite3PageFree(pNew); + } +@@ -55593,8 +62013,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ + ** current database image, in pages, OR + ** + ** b) if the page content were written at this time, it would not +-** be necessary to write the current content out to the sub-journal +-** (as determined by function subjRequiresPage()). ++** be necessary to write the current content out to the sub-journal. + ** + ** If the condition asserted by this function were not true, and the + ** dirty page were to be discarded from the cache via the pagerStress() +@@ -55609,8 +62028,16 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ + */ + #if defined(SQLITE_DEBUG) + static void assertTruncateConstraintCb(PgHdr *pPg){ ++ Pager *pPager = pPg->pPager; + assert( pPg->flags&PGHDR_DIRTY ); +- assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); ++ if( pPg->pgno>pPager->dbSize ){ /* if (a) is false */ ++ Pgno pgno = pPg->pgno; ++ int i; ++ for(i=0; ipPager->nSavepoint; i++){ ++ PagerSavepoint *p = &pPager->aSavepoint[i]; ++ assert( p->nOrigpInSavepoint,pgno) ); ++ } ++ } + } + static void assertTruncateConstraint(Pager *pPager){ + sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); +@@ -55631,7 +62058,7 @@ static void assertTruncateConstraint(Pager *pPager){ + ** then continue writing to the database. + */ + SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ +- assert( pPager->dbSize>=nPage ); ++ assert( pPager->dbSize>=nPage || CORRUPT_DB ); + assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); + pPager->dbSize = nPage; + +@@ -55704,6 +62131,7 @@ static int pagerAcquireMapPage( + return SQLITE_NOMEM_BKPT; + } + p->pExtra = (void *)&p[1]; ++ assert( EIGHT_BYTE_ALIGNMENT( p->pExtra ) ); + p->flags = PGHDR_MMAP; + p->nRef = 1; + p->pPager = pPager; +@@ -56359,11 +62787,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + int rc = SQLITE_OK; /* Return code */ + int tempFile = 0; /* True for temp files (incl. in-memory files) */ + int memDb = 0; /* True if this is an in-memory file */ +-#ifdef SQLITE_ENABLE_DESERIALIZE + int memJM = 0; /* Memory journal mode */ +-#else +-# define memJM 0 +-#endif + int readOnly = 0; /* True if this is a read-only file */ + int journalFileSize; /* Bytes to allocate for each journal fd */ + char *zPathname = 0; /* Full path to database file */ +@@ -56373,7 +62797,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ + const char *zUri = 0; /* URI args to copy */ + int nUriByte = 1; /* Number of bytes of URI args at *zUri */ +- int nUri = 0; /* Number of URI parameters */ ++ + + /* Figure out how much space is required for each journal file-handle + ** (there are two of them, the main journal and the sub-journal). */ +@@ -56400,8 +62824,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + */ + if( zFilename && zFilename[0] ){ + const char *z; +- nPathname = pVfs->mxPathname+1; +- zPathname = sqlite3DbMallocRaw(0, nPathname*2); ++ nPathname = pVfs->mxPathname + 1; ++ zPathname = sqlite3DbMallocRaw(0, 2*(i64)nPathname); + if( zPathname==0 ){ + return SQLITE_NOMEM_BKPT; + } +@@ -56421,7 +62845,6 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + while( *z ){ + z += strlen(z)+1; + z += strlen(z)+1; +- nUri++; + } + nUriByte = (int)(&z[1] - zUri); + assert( nUriByte>=1 ); +@@ -56484,18 +62907,19 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + ** specific formatting and order of the various filenames, so if the format + ** changes here, be sure to change it there as well. + */ ++ assert( SQLITE_PTRSIZE==sizeof(Pager*) ); + pPtr = (u8 *)sqlite3MallocZero( + ROUND8(sizeof(*pPager)) + /* Pager structure */ + ROUND8(pcacheSize) + /* PCache object */ + ROUND8(pVfs->szOsFile) + /* The main db file */ +- journalFileSize * 2 + /* The two journal files */ +- sizeof(pPager) + /* Space to hold a pointer */ ++ (u64)journalFileSize * 2 + /* The two journal files */ ++ SQLITE_PTRSIZE + /* Space to hold a pointer */ + 4 + /* Database prefix */ +- nPathname + 1 + /* database filename */ +- nUriByte + /* query parameters */ +- nPathname + 8 + 1 + /* Journal filename */ ++ (u64)nPathname + 1 + /* database filename */ ++ (u64)nUriByte + /* query parameters */ ++ (u64)nPathname + 8 + 1 + /* Journal filename */ + #ifndef SQLITE_OMIT_WAL +- nPathname + 4 + 1 + /* WAL filename */ ++ (u64)nPathname + 4 + 1 + /* WAL filename */ + #endif + 3 /* Terminator */ + ); +@@ -56510,7 +62934,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; + pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; + assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); +- memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); ++ memcpy(pPtr, &pPager, SQLITE_PTRSIZE); pPtr += SQLITE_PTRSIZE; + + /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ + pPtr += 4; /* Skip zero prefix */ +@@ -56552,6 +62976,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + pPager->zWal = 0; + } + #endif ++ (void)pPtr; /* Suppress warning about unused pPtr value */ + + if( nPathname ) sqlite3DbFree(0, zPathname); + pPager->pVfs = pVfs; +@@ -56563,9 +62988,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + int fout = 0; /* VFS flags returned by xOpen() */ + rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); + assert( !memDb ); +-#ifdef SQLITE_ENABLE_DESERIALIZE +- memJM = (fout&SQLITE_OPEN_MEMORY)!=0; +-#endif ++ pPager->memVfs = memJM = (fout&SQLITE_OPEN_MEMORY)!=0; + readOnly = (fout&SQLITE_OPEN_READONLY)!=0; + + /* If the file was successfully opened for read/write access, +@@ -56676,18 +63099,7 @@ act_like_temp_file: + pPager->memDb = (u8)memDb; + pPager->readOnly = (u8)readOnly; + assert( useJournal || pPager->tempFile ); +- pPager->noSync = pPager->tempFile; +- if( pPager->noSync ){ +- assert( pPager->fullSync==0 ); +- assert( pPager->extraSync==0 ); +- assert( pPager->syncFlags==0 ); +- assert( pPager->walSyncFlags==0 ); +- }else{ +- pPager->fullSync = 1; +- pPager->extraSync = 0; +- pPager->syncFlags = SQLITE_SYNC_NORMAL; +- pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2); +- } ++ sqlite3PagerSetFlags(pPager, (SQLITE_DEFAULT_SYNCHRONOUS+1)|PAGER_CACHESPILL); + /* pPager->pFirst = 0; */ + /* pPager->pFirstSynced = 0; */ + /* pPager->pLast = 0; */ +@@ -56713,15 +63125,18 @@ act_like_temp_file: + + /* + ** Return the sqlite3_file for the main database given the name +-** of the corresonding WAL or Journal name as passed into ++** of the corresponding WAL or Journal name as passed into + ** xOpen. + */ + SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){ + Pager *pPager; ++ const char *p; + while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ + zName--; + } +- pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); ++ p = zName - 4 - sizeof(Pager*); ++ assert( EIGHT_BYTE_ALIGNMENT(p) ); ++ pPager = *(Pager**)p; + return pPager->fd; + } + +@@ -56949,7 +63364,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ + ** may mean that the pager was in the error-state when this + ** function was called and the journal file does not exist. + */ +- if( !isOpen(pPager->jfd) ){ ++ if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + sqlite3_vfs * const pVfs = pPager->pVfs; + int bExists; /* True if journal file exists */ + rc = sqlite3OsAccess( +@@ -57194,7 +63609,7 @@ static int getPageNormal( + if( pPg->pPager && !noContent ){ + /* In this case the pcache already contains an initialized copy of + ** the page. Return without further ado. */ +- assert( pgno!=PAGER_MJ_PGNO(pPager) ); ++ assert( pgno!=PAGER_SJ_PGNO(pPager) ); + pPager->aStat[PAGER_STAT_HIT]++; + return SQLITE_OK; + +@@ -57205,7 +63620,7 @@ static int getPageNormal( + ** (*) obsolete. Was: maximum page number is 2^31 + ** (2) Never try to fetch the locking page + */ +- if( pgno==PAGER_MJ_PGNO(pPager) ){ ++ if( pgno==PAGER_SJ_PGNO(pPager) ){ + rc = SQLITE_CORRUPT_BKPT; + goto pager_acquire_err; + } +@@ -57216,6 +63631,10 @@ static int getPageNormal( + if( !isOpen(pPager->fd) || pPager->dbSizepPager->mxPgno ){ + rc = SQLITE_FULL; ++ if( pgno<=pPager->dbSize ){ ++ sqlite3PcacheRelease(pPg); ++ pPg = 0; ++ } + goto pager_acquire_err; + } + if( noContent ){ +@@ -57351,7 +63770,20 @@ SQLITE_PRIVATE int sqlite3PagerGet( + DbPage **ppPage, /* Write a pointer to the page here */ + int flags /* PAGER_GET_XXX flags */ + ){ ++#if 0 /* Trace page fetch by setting to 1 */ ++ int rc; ++ printf("PAGE %u\n", pgno); ++ fflush(stdout); ++ rc = pPager->xGet(pPager, pgno, ppPage, flags); ++ if( rc ){ ++ printf("PAGE %u failed with 0x%02x\n", pgno, rc); ++ fflush(stdout); ++ } ++ return rc; ++#else ++ /* Normal, high-speed version of sqlite3PagerGet() */ + return pPager->xGet(pPager, pgno, ppPage, flags); ++#endif + } + + /* +@@ -57379,10 +63811,12 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ + /* + ** Release a page reference. + ** +-** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be +-** used if we know that the page being released is not the last page. ++** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be used ++** if we know that the page being released is not the last reference to page1. + ** The btree layer always holds page1 open until the end, so these first +-** to routines can be used to release any page other than BtShared.pPage1. ++** two routines can be used to release any page other than BtShared.pPage1. ++** The assert() at tag-20230419-2 proves that this constraint is always ++** honored. + ** + ** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine + ** checks the total number of outstanding pages and if the number of +@@ -57398,7 +63832,7 @@ SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){ + sqlite3PcacheRelease(pPg); + } + /* Do not use this routine to release the last reference to page1 */ +- assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); ++ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); /* tag-20230419-2 */ + } + SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ + if( pPg ) sqlite3PagerUnrefNotNull(pPg); +@@ -57464,6 +63898,7 @@ static int pager_open_journal(Pager *pPager){ + + if( pPager->tempFile ){ + flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); ++ flags |= SQLITE_OPEN_EXCLUSIVE; + nSpill = sqlite3Config.nStmtSpill; + }else{ + flags |= SQLITE_OPEN_MAIN_JOURNAL; +@@ -57499,6 +63934,7 @@ static int pager_open_journal(Pager *pPager){ + if( rc!=SQLITE_OK ){ + sqlite3BitvecDestroy(pPager->pInJournal); + pPager->pInJournal = 0; ++ pPager->journalOff = 0; + }else{ + assert( pPager->eState==PAGER_WRITER_LOCKED ); + pPager->eState = PAGER_WRITER_CACHEMOD; +@@ -57531,7 +63967,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory + assert( pPager->eState>=PAGER_READER && pPager->eStatesubjInMemory = (u8)subjInMemory; + +- if( ALWAYS(pPager->eState==PAGER_READER) ){ ++ if( pPager->eState==PAGER_READER ){ + assert( pPager->pInJournal==0 ); + + if( pagerUseWal(pPager) ){ +@@ -57603,7 +64039,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ + /* We should never write to the journal file the page that + ** contains the database locks. The following assert verifies + ** that we do not. */ +- assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); ++ assert( pPg->pgno!=PAGER_SJ_PGNO(pPager) ); + + assert( pPager->journalHdr<=pPager->journalOff ); + pData2 = pPg->pData; +@@ -57782,7 +64218,7 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){ + Pgno pg = pg1+ii; + PgHdr *pPage; + if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){ +- if( pg!=PAGER_MJ_PGNO(pPager) ){ ++ if( pg!=PAGER_SJ_PGNO(pPager) ){ + rc = sqlite3PagerGet(pPager, pg, &pPage, 0); + if( rc==SQLITE_OK ){ + rc = pager_write(pPage); +@@ -57945,7 +64381,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ + # define DIRECT_MODE isDirectMode + #endif + +- if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ ++ if( !pPager->changeCountDone && pPager->dbSize>0 ){ + PgHdr *pPgHdr; /* Reference to page 1 */ + + assert( !pPager->tempFile && isOpen(pPager->fd) ); +@@ -58223,6 +64659,13 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( + rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0); + if( rc==SQLITE_OK ){ + rc = pager_write_pagelist(pPager, pList); ++ if( rc==SQLITE_OK && pPager->dbSize>pPager->dbFileSize ){ ++ char *pTmp = pPager->pTmpSpace; ++ int szPage = (int)pPager->pageSize; ++ memset(pTmp, 0, szPage); ++ rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, ++ ((i64)pPager->dbSize*pPager->pageSize)-szPage); ++ } + if( rc==SQLITE_OK ){ + rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); + } +@@ -58260,7 +64703,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( + ** last page is never written out to disk, leaving the database file + ** undersized. Fix this now if it is the case. */ + if( pPager->dbSize>pPager->dbFileSize ){ +- Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); ++ Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_SJ_PGNO(pPager)); + assert( pPager->eState==PAGER_WRITER_DBMOD ); + rc = pager_truncate(pPager, nNew); + if( rc!=SQLITE_OK ) goto commit_phase_one_exit; +@@ -58431,8 +64874,8 @@ SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){ + ** used by the pager and its associated cache. + */ + SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager *pPager){ +- int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr) +- + 5*sizeof(void*); ++ int perPageSize = pPager->pageSize + pPager->nExtra ++ + (int)(sizeof(PgHdr) + 5*sizeof(void*)); + return perPageSize*sqlite3PcachePagecount(pPager->pPCache) + + sqlite3MallocSize(pPager) + + pPager->pageSize; +@@ -58457,11 +64900,11 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ + a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; + a[4] = pPager->eState; + a[5] = pPager->errCode; +- a[6] = pPager->aStat[PAGER_STAT_HIT]; +- a[7] = pPager->aStat[PAGER_STAT_MISS]; ++ a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff; ++ a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff; + a[8] = 0; /* Used to be pPager->nOvfl */ + a[9] = pPager->nRead; +- a[10] = pPager->aStat[PAGER_STAT_WRITE]; ++ a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff; + return a; + } + #endif +@@ -58477,7 +64920,7 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ + ** reset parameter is non-zero, the cache hit or miss count is zeroed before + ** returning. + */ +-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ ++SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){ + + assert( eStat==SQLITE_DBSTATUS_CACHE_HIT + || eStat==SQLITE_DBSTATUS_CACHE_MISS +@@ -58501,7 +64944,7 @@ SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, i + ** Return true if this is an in-memory or temp-file backed pager. + */ + SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){ +- return pPager->tempFile; ++ return pPager->tempFile || pPager->memVfs; + } + + /* +@@ -58547,6 +64990,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ + } + aNew[ii].iSubRec = pPager->nSubRec; + aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); ++ aNew[ii].bTruncateOnRelease = 1; + if( !aNew[ii].pInSavepoint ){ + return SQLITE_NOMEM_BKPT; + } +@@ -58625,16 +65069,18 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ + } + pPager->nSavepoint = nNew; + +- /* If this is a release of the outermost savepoint, truncate +- ** the sub-journal to zero bytes in size. */ ++ /* Truncate the sub-journal so that it only includes the parts ++ ** that are still in use. */ + if( op==SAVEPOINT_RELEASE ){ +- if( nNew==0 && isOpen(pPager->sjfd) ){ ++ PagerSavepoint *pRel = &pPager->aSavepoint[nNew]; ++ if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){ + /* Only truncate if it is an in-memory sub-journal. */ + if( sqlite3JournalIsInMemory(pPager->sjfd) ){ +- rc = sqlite3OsTruncate(pPager->sjfd, 0); ++ i64 sz = (pPager->pageSize+4)*(i64)pRel->iSubRec; ++ rc = sqlite3OsTruncate(pPager->sjfd, sz); + assert( rc==SQLITE_OK ); + } +- pPager->nSubRec = 0; ++ pPager->nSubRec = pRel->iSubRec; + } + } + /* Else this is a rollback operation, playback the specified savepoint. +@@ -58682,7 +65128,11 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ + */ + SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){ + static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +- return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename; ++ if( nullIfMemDb && (pPager->memDb || sqlite3IsMemdb(pPager->pVfs)) ){ ++ return &zFake[4]; ++ }else{ ++ return pPager->zFilename; ++ } + } + + /* +@@ -58706,7 +65156,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ + ** This will be either the rollback journal or the WAL file. + */ + SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ +-#if SQLITE_OMIT_WAL ++#ifdef SQLITE_OMIT_WAL + return pPager->jfd; + #else + return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd; +@@ -58818,7 +65268,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i + pPgOld = sqlite3PagerLookup(pPager, pgno); + assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB ); + if( pPgOld ){ +- if( pPgOld->nRef>1 ){ ++ if( NEVER(pPgOld->nRef>1) ){ + sqlite3PagerUnrefNotNull(pPgOld); + return SQLITE_CORRUPT_BKPT; + } +@@ -58953,12 +65403,12 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ + u8 eOld = pPager->journalMode; /* Prior journalmode */ + + /* The eMode parameter is always valid */ +- assert( eMode==PAGER_JOURNALMODE_DELETE +- || eMode==PAGER_JOURNALMODE_TRUNCATE +- || eMode==PAGER_JOURNALMODE_PERSIST +- || eMode==PAGER_JOURNALMODE_OFF +- || eMode==PAGER_JOURNALMODE_WAL +- || eMode==PAGER_JOURNALMODE_MEMORY ); ++ assert( eMode==PAGER_JOURNALMODE_DELETE /* 0 */ ++ || eMode==PAGER_JOURNALMODE_PERSIST /* 1 */ ++ || eMode==PAGER_JOURNALMODE_OFF /* 2 */ ++ || eMode==PAGER_JOURNALMODE_TRUNCATE /* 3 */ ++ || eMode==PAGER_JOURNALMODE_MEMORY /* 4 */ ++ || eMode==PAGER_JOURNALMODE_WAL /* 5 */ ); + + /* This routine is only called from the OP_JournalMode opcode, and + ** the logic there will never allow a temporary file to be changed +@@ -58982,7 +65432,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ + assert( pPager->eState!=PAGER_ERROR ); + pPager->journalMode = (u8)eMode; + +- /* When transistioning from TRUNCATE or PERSIST to any other journal ++ /* When transitioning from TRUNCATE or PERSIST to any other journal + ** mode except WAL, unless the pager is in locking_mode=exclusive mode, + ** delete the journal file. + */ +@@ -58995,7 +65445,6 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ + + assert( isOpen(pPager->fd) || pPager->exclusiveMode ); + if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ +- + /* In this case we would like to delete the journal file. If it is + ** not possible, then that is not a problem. Deleting the journal file + ** here is an optimization only. +@@ -59028,7 +65477,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ + } + assert( state==pPager->eState ); + } +- }else if( eMode==PAGER_JOURNALMODE_OFF ){ ++ }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){ + sqlite3OsClose(pPager->jfd); + } + } +@@ -59107,6 +65556,18 @@ SQLITE_PRIVATE int sqlite3PagerCheckpoint( + int *pnCkpt /* OUT: Final number of checkpointed frames */ + ){ + int rc = SQLITE_OK; ++ if( pPager->pWal==0 && pPager->journalMode==PAGER_JOURNALMODE_WAL ){ ++ /* This only happens when a database file is zero bytes in size opened and ++ ** then "PRAGMA journal_mode=WAL" is run and then sqlite3_wal_checkpoint() ++ ** is invoked without any intervening transactions. We need to start ++ ** a transaction to initialize pWal. The PRAGMA table_list statement is ++ ** used for this since it starts transactions on every database file, ++ ** including all ATTACHed databases. This seems expensive for a single ++ ** sqlite3_wal_checkpoint() call, but it happens very rarely. ++ ** https://sqlite.org/forum/forumpost/fd0f19d229156939 ++ */ ++ sqlite3_exec(db, "PRAGMA table_list",0,0,0); ++ } + if( pPager->pWal ){ + rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode, + (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), +@@ -59138,13 +65599,15 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){ + */ + static int pagerExclusiveLock(Pager *pPager){ + int rc; /* Return code */ ++ u8 eOrigLock; /* Original lock */ + +- assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); ++ assert( pPager->eLock>=SHARED_LOCK ); ++ eOrigLock = pPager->eLock; + rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); + if( rc!=SQLITE_OK ){ + /* If the attempt to grab the exclusive lock failed, release the + ** pending lock that may have been obtained instead. */ +- pagerUnlockDb(pPager, SHARED_LOCK); ++ pagerUnlockDb(pPager, eOrigLock); + } + + return rc; +@@ -59179,6 +65642,11 @@ static int pagerOpenWal(Pager *pPager){ + pPager->fd, pPager->zWal, pPager->exclusiveMode, + pPager->journalSizeLimit, &pPager->pWal + ); ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ if( rc==SQLITE_OK ){ ++ sqlite3WalDb(pPager->pWal, pPager->dbWal); ++ } ++#endif + } + pagerFixMaplimit(pPager); + +@@ -59298,6 +65766,7 @@ SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){ + ** blocking locks are required. + */ + SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){ ++ pPager->dbWal = db; + if( pagerUseWal(pPager) ){ + sqlite3WalDb(pPager->pWal, db); + } +@@ -59397,6 +65866,12 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ + } + #endif + ++#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) ++SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){ ++ return sqlite3WalSystemErrno(pPager->pWal); ++} ++#endif ++ + #endif /* SQLITE_OMIT_DISKIO */ + + /************** End of pager.c ***********************************************/ +@@ -59447,7 +65922,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ + ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). + ** + ** Immediately following the wal-header are zero or more frames. Each +-** frame consists of a 24-byte frame-header followed by a bytes ++** frame consists of a 24-byte frame-header followed by bytes + ** of page data. The frame-header is six big-endian 32-bit unsigned + ** integer values, as follows: + ** +@@ -59564,7 +66039,10 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ + ** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and + ** HASHTABLE_NPAGE are selected so that together the wal-index header and + ** first index block are the same size as all other index blocks in the +-** wal-index. ++** wal-index. The values are: ++** ++** HASHTABLE_NPAGE 4096 ++** HASHTABLE_NPAGE_ONE 4062 + ** + ** Each index block contains two sections, a page-mapping that contains the + ** database page number associated with each wal frame, and a hash-table +@@ -59684,7 +66162,7 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0; + ** + ** Technically, the various VFSes are free to implement these locks however + ** they see fit. However, compatibility is encouraged so that VFSes can +-** interoperate. The standard implemention used on both unix and windows ++** interoperate. The standard implementation used on both unix and windows + ** is for the index number to indicate a byte offset into the + ** WalCkptInfo.aLock[] array in the wal-index header. In other words, all + ** locks are on the shm file. The WALINDEX_LOCK_OFFSET constant (which +@@ -59760,7 +66238,7 @@ struct WalIndexHdr { + ** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff) + ** for any aReadMark[] means that entry is unused. aReadMark[0] is + ** a special case; its value is never used and it exists as a place-holder +-** to avoid having to offset aReadMark[] indexs by one. Readers holding ++** to avoid having to offset aReadMark[] indexes by one. Readers holding + ** WAL_READ_LOCK(0) always ignore the entire WAL and read all content + ** directly from the database. + ** +@@ -59800,6 +66278,70 @@ struct WalCkptInfo { + }; + #define READMARK_NOT_USED 0xffffffff + ++/* ++** This is a schematic view of the complete 136-byte header of the ++** wal-index file (also known as the -shm file): ++** ++** +-----------------------------+ ++** 0: | iVersion | \ ++** +-----------------------------+ | ++** 4: | (unused padding) | | ++** +-----------------------------+ | ++** 8: | iChange | | ++** +-------+-------+-------------+ | ++** 12: | bInit | bBig | szPage | | ++** +-------+-------+-------------+ | ++** 16: | mxFrame | | First copy of the ++** +-----------------------------+ | WalIndexHdr object ++** 20: | nPage | | ++** +-----------------------------+ | ++** 24: | aFrameCksum | | ++** | | | ++** +-----------------------------+ | ++** 32: | aSalt | | ++** | | | ++** +-----------------------------+ | ++** 40: | aCksum | | ++** | | / ++** +-----------------------------+ ++** 48: | iVersion | \ ++** +-----------------------------+ | ++** 52: | (unused padding) | | ++** +-----------------------------+ | ++** 56: | iChange | | ++** +-------+-------+-------------+ | ++** 60: | bInit | bBig | szPage | | ++** +-------+-------+-------------+ | Second copy of the ++** 64: | mxFrame | | WalIndexHdr ++** +-----------------------------+ | ++** 68: | nPage | | ++** +-----------------------------+ | ++** 72: | aFrameCksum | | ++** | | | ++** +-----------------------------+ | ++** 80: | aSalt | | ++** | | | ++** +-----------------------------+ | ++** 88: | aCksum | | ++** | | / ++** +-----------------------------+ ++** 96: | nBackfill | ++** +-----------------------------+ ++** 100: | 5 read marks | ++** | | ++** | | ++** | | ++** | | ++** +-------+-------+------+------+ ++** 120: | Write | Ckpt | Rcvr | Rd0 | \ ++** +-------+-------+------+------+ ) 8 lock bytes ++** | Read1 | Read2 | Rd3 | Rd4 | / ++** +-------+-------+------+------+ ++** 128: | nBackfillAttempted | ++** +-----------------------------+ ++** 132: | (unused padding) | ++** +-----------------------------+ ++*/ + + /* A block of WALINDEX_LOCK_RESERVED bytes beginning at + ** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems +@@ -59838,6 +66380,11 @@ struct WalCkptInfo { + /* + ** An open write-ahead log file is represented by an instance of the + ** following object. ++** ++** writeLock: ++** This is usually set to 1 whenever the WRITER lock is held. However, ++** if it is set to 2, then the WRITER lock is held but must be released ++** by walHandleException() if a SEH exception is thrown. + */ + struct Wal { + sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ +@@ -59864,11 +66411,20 @@ struct Wal { + u32 iReCksum; /* On commit, recalculate checksums from here */ + const char *zWalName; /* Name of WAL file */ + u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ ++#ifdef SQLITE_USE_SEH ++ u32 lockMask; /* Mask of locks held */ ++ void *pFree; /* Pointer to sqlite3_free() if exception thrown */ ++ u32 *pWiValue; /* Value to write into apWiData[iWiPg] */ ++ int iWiPg; /* Write pWiValue into apWiData[iWiPg] */ ++ int iSysErrno; /* System error code following exception */ ++#endif + #ifdef SQLITE_DEBUG ++ int nSehTry; /* Number of nested SEH_TRY{} blocks */ + u8 lockError; /* True if a locking error has occurred */ + #endif + #ifdef SQLITE_ENABLE_SNAPSHOT + WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ ++ int bGetSnapshot; /* Transaction opened for sqlite3_get_snapshot() */ + #endif + #ifdef SQLITE_ENABLE_SETLK_TIMEOUT + sqlite3 *db; +@@ -59919,9 +66475,13 @@ struct WalIterator { + u32 *aPgno; /* Array of page numbers. */ + int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ + int iZero; /* Frame number associated with aPgno[0] */ +- } aSegment[1]; /* One for every 32KB page in the wal-index */ ++ } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */ + }; + ++/* Size (in bytes) of a WalIterator object suitable for N or fewer segments */ ++#define SZ_WALITERATOR(N) \ ++ (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment)) ++ + /* + ** Define the parameters of the hash tables in the wal-index file. There + ** is a hash-table following every HASHTABLE_NPAGE page numbers in the +@@ -59946,6 +66506,113 @@ struct WalIterator { + sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \ + ) + ++/* ++** Structured Exception Handling (SEH) is a Windows-specific technique ++** for catching exceptions raised while accessing memory-mapped files. ++** ++** The -DSQLITE_USE_SEH compile-time option means to use SEH to catch and ++** deal with system-level errors that arise during WAL -shm file processing. ++** Without this compile-time option, any system-level faults that appear ++** while accessing the memory-mapped -shm file will cause a process-wide ++** signal to be deliver, which will more than likely cause the entire ++** process to exit. ++*/ ++#ifdef SQLITE_USE_SEH ++#include ++ ++/* Beginning of a block of code in which an exception might occur */ ++# define SEH_TRY __try { \ ++ assert( walAssertLockmask(pWal) && pWal->nSehTry==0 ); \ ++ VVA_ONLY(pWal->nSehTry++); ++ ++/* The end of a block of code in which an exception might occur */ ++# define SEH_EXCEPT(X) \ ++ VVA_ONLY(pWal->nSehTry--); \ ++ assert( pWal->nSehTry==0 ); \ ++ } __except( sehExceptionFilter(pWal, GetExceptionCode(), GetExceptionInformation() ) ){ X } ++ ++/* Simulate a memory-mapping fault in the -shm file for testing purposes */ ++# define SEH_INJECT_FAULT sehInjectFault(pWal) ++ ++/* ++** The second argument is the return value of GetExceptionCode() for the ++** current exception. Return EXCEPTION_EXECUTE_HANDLER if the exception code ++** indicates that the exception may have been caused by accessing the *-shm ++** file mapping. Or EXCEPTION_CONTINUE_SEARCH otherwise. ++*/ ++static int sehExceptionFilter(Wal *pWal, int eCode, EXCEPTION_POINTERS *p){ ++ VVA_ONLY(pWal->nSehTry--); ++ if( eCode==EXCEPTION_IN_PAGE_ERROR ){ ++ if( p && p->ExceptionRecord && p->ExceptionRecord->NumberParameters>=3 ){ ++ /* From MSDN: For this type of exception, the first element of the ++ ** ExceptionInformation[] array is a read-write flag - 0 if the exception ++ ** was thrown while reading, 1 if while writing. The second element is ++ ** the virtual address being accessed. The "third array element specifies ++ ** the underlying NTSTATUS code that resulted in the exception". */ ++ pWal->iSysErrno = (int)p->ExceptionRecord->ExceptionInformation[2]; ++ } ++ return EXCEPTION_EXECUTE_HANDLER; ++ } ++ return EXCEPTION_CONTINUE_SEARCH; ++} ++ ++/* ++** If one is configured, invoke the xTestCallback callback with 650 as ++** the argument. If it returns true, throw the same exception that is ++** thrown by the system if the *-shm file mapping is accessed after it ++** has been invalidated. ++*/ ++static void sehInjectFault(Wal *pWal){ ++ int res; ++ assert( pWal->nSehTry>0 ); ++ ++ res = sqlite3FaultSim(650); ++ if( res!=0 ){ ++ ULONG_PTR aArg[3]; ++ aArg[0] = 0; ++ aArg[1] = 0; ++ aArg[2] = (ULONG_PTR)res; ++ RaiseException(EXCEPTION_IN_PAGE_ERROR, 0, 3, (const ULONG_PTR*)aArg); ++ } ++} ++ ++/* ++** There are two ways to use this macro. To set a pointer to be freed ++** if an exception is thrown: ++** ++** SEH_FREE_ON_ERROR(0, pPtr); ++** ++** and to cancel the same: ++** ++** SEH_FREE_ON_ERROR(pPtr, 0); ++** ++** In the first case, there must not already be a pointer registered to ++** be freed. In the second case, pPtr must be the registered pointer. ++*/ ++#define SEH_FREE_ON_ERROR(X,Y) \ ++ assert( (X==0 || Y==0) && pWal->pFree==X ); pWal->pFree = Y ++ ++/* ++** There are two ways to use this macro. To arrange for pWal->apWiData[iPg] ++** to be set to pValue if an exception is thrown: ++** ++** SEH_SET_ON_ERROR(iPg, pValue); ++** ++** and to cancel the same: ++** ++** SEH_SET_ON_ERROR(0, 0); ++*/ ++#define SEH_SET_ON_ERROR(X,Y) pWal->iWiPg = X; pWal->pWiValue = Y ++ ++#else ++# define SEH_TRY VVA_ONLY(pWal->nSehTry++); ++# define SEH_EXCEPT(X) VVA_ONLY(pWal->nSehTry--); assert( pWal->nSehTry==0 ); ++# define SEH_INJECT_FAULT assert( pWal->nSehTry>0 ); ++# define SEH_FREE_ON_ERROR(X,Y) ++# define SEH_SET_ON_ERROR(X,Y) ++#endif /* ifdef SQLITE_USE_SEH */ ++ ++ + /* + ** Obtain a pointer to the iPage'th page of the wal-index. The wal-index + ** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are +@@ -59956,9 +66623,13 @@ struct WalIterator { + ** so. It is safe to enlarge the wal-index if pWal->writeLock is true + ** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE. + ** +-** If this call is successful, *ppPage is set to point to the wal-index +-** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, +-** then an SQLite error code is returned and *ppPage is set to 0. ++** Three possible result scenarios: ++** ++** (1) rc==SQLITE_OK and *ppPage==Requested-Wal-Index-Page ++** (2) rc>=SQLITE_ERROR and *ppPage==NULL ++** (3) rc==SQLITE_OK and *ppPage==NULL // only if iPage==0 ++** ++** Scenario (3) can only occur when pWal->writeLock is false and iPage==0 + */ + static SQLITE_NOINLINE int walIndexPageRealloc( + Wal *pWal, /* The WAL context */ +@@ -59969,7 +66640,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc( + + /* Enlarge the pWal->apWiData[] array if required */ + if( pWal->nWiData<=iPage ){ +- sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); ++ sqlite3_int64 nByte = sizeof(u32*)*(1+(i64)iPage); + volatile u32 **apNew; + apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte); + if( !apNew ){ +@@ -59991,7 +66662,9 @@ static SQLITE_NOINLINE int walIndexPageRealloc( + rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, + pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] + ); +- assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); ++ assert( pWal->apWiData[iPage]!=0 ++ || rc!=SQLITE_OK ++ || (pWal->writeLock==0 && iPage==0) ); + testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); + if( rc==SQLITE_OK ){ + if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; +@@ -60012,6 +66685,7 @@ static int walIndexPage( + int iPage, /* The page we seek */ + volatile u32 **ppPage /* Write the page pointer here */ + ){ ++ SEH_INJECT_FAULT; + if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){ + return walIndexPageRealloc(pWal, iPage, ppPage); + } +@@ -60023,6 +66697,7 @@ static int walIndexPage( + */ + static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ + assert( pWal->nWiData>0 && pWal->apWiData[0] ); ++ SEH_INJECT_FAULT; + return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]); + } + +@@ -60031,6 +66706,7 @@ static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ + */ + static volatile WalIndexHdr *walIndexHdr(Wal *pWal){ + assert( pWal->nWiData>0 && pWal->apWiData[0] ); ++ SEH_INJECT_FAULT; + return (volatile WalIndexHdr*)pWal->apWiData[0]; + } + +@@ -60073,22 +66749,41 @@ static void walChecksumBytes( + s1 = s2 = 0; + } + +- assert( nByte>=8 ); +- assert( (nByte&0x00000007)==0 ); +- assert( nByte<=65536 ); ++ /* nByte is a multiple of 8 between 8 and 65536 */ ++ assert( nByte>=8 && (nByte&7)==0 && nByte<=65536 ); + +- if( nativeCksum ){ ++ if( !nativeCksum ){ ++ do { ++ s1 += BYTESWAP32(aData[0]) + s2; ++ s2 += BYTESWAP32(aData[1]) + s1; ++ aData += 2; ++ }while( aDatalockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) ++#ifdef SQLITE_USE_SEH ++ if( rc==SQLITE_OK ) pWal->lockMask |= (1 << lockIdx); ++#endif + return rc; + } + static void walUnlockShared(Wal *pWal, int lockIdx){ + if( pWal->exclusiveMode ) return; + (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, + SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); ++#ifdef SQLITE_USE_SEH ++ pWal->lockMask &= ~(1 << lockIdx); ++#endif + WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); + } + static int walLockExclusive(Wal *pWal, int lockIdx, int n){ +@@ -60283,12 +66984,20 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){ + WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, + walLockName(lockIdx), n, rc ? "failed" : "ok")); + VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) ++#ifdef SQLITE_USE_SEH ++ if( rc==SQLITE_OK ){ ++ pWal->lockMask |= (((1<exclusiveMode ) return; + (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, + SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); ++#ifdef SQLITE_USE_SEH ++ pWal->lockMask &= ~(((1<iZero+N) in the log. + ** +-** Finally, set pLoc->aPgno so that pLoc->aPgno[1] is the page number of the +-** first frame indexed by the hash table, frame (pLoc->iZero+1). ++** Finally, set pLoc->aPgno so that pLoc->aPgno[0] is the page number of the ++** first frame indexed by the hash table, frame (pLoc->iZero). + */ + static int walHashGet( + Wal *pWal, /* WAL handle */ +@@ -60343,7 +67052,7 @@ static int walHashGet( + rc = walIndexPage(pWal, iHash, &pLoc->aPgno); + assert( rc==SQLITE_OK || iHash>0 ); + +- if( rc==SQLITE_OK ){ ++ if( pLoc->aPgno ){ + pLoc->aHash = (volatile ht_slot *)&pLoc->aPgno[HASHTABLE_NPAGE]; + if( iHash==0 ){ + pLoc->aPgno = &pLoc->aPgno[WALINDEX_HDR_SIZE/sizeof(u32)]; +@@ -60351,7 +67060,8 @@ static int walHashGet( + }else{ + pLoc->iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE; + } +- pLoc->aPgno = &pLoc->aPgno[-1]; ++ }else if( NEVER(rc==SQLITE_OK) ){ ++ rc = SQLITE_ERROR; + } + return rc; + } +@@ -60379,6 +67089,7 @@ static int walFramePage(u32 iFrame){ + */ + static u32 walFramePgno(Wal *pWal, u32 iFrame){ + int iHash = walFramePage(iFrame); ++ SEH_INJECT_FAULT; + if( iHash==0 ){ + return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1]; + } +@@ -60402,7 +67113,6 @@ static void walCleanupHash(Wal *pWal){ + int iLimit = 0; /* Zero values greater than this */ + int nByte; /* Number of bytes to zero in aPgno[] */ + int i; /* Used to iterate through aHash[] */ +- int rc; /* Return code form walHashGet() */ + + assert( pWal->writeLock ); + testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); +@@ -60417,8 +67127,8 @@ static void walCleanupHash(Wal *pWal){ + */ + assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); + assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); +- rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); +- if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */ ++ i = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); ++ if( NEVER(i) ) return; /* Defense-in-depth, in case (1) above is wrong */ + + /* Zero all hash-table entries that correspond to frame numbers greater + ** than pWal->hdr.mxFrame. +@@ -60434,8 +67144,9 @@ static void walCleanupHash(Wal *pWal){ + /* Zero the entries in the aPgno array that correspond to frames with + ** frame numbers greater than pWal->hdr.mxFrame. + */ +- nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit+1]); +- memset((void *)&sLoc.aPgno[iLimit+1], 0, nByte); ++ nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit]); ++ assert( nByte>=0 ); ++ memset((void *)&sLoc.aPgno[iLimit], 0, nByte); + + #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT + /* Verify that the every entry in the mapping region is still reachable +@@ -60444,11 +67155,11 @@ static void walCleanupHash(Wal *pWal){ + if( iLimit ){ + int j; /* Loop counter */ + int iKey; /* Hash key */ +- for(j=1; j<=iLimit; j++){ ++ for(j=0; j=0 ); ++ memset((void*)sLoc.aPgno, 0, nByte); + } + + /* If the entry in aPgno[] is already set, then the previous writer +@@ -60491,9 +67202,9 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ + ** Remove the remnants of that writers uncommitted transaction from + ** the hash-table before writing any new entries. + */ +- if( sLoc.aPgno[idx] ){ ++ if( sLoc.aPgno[idx-1] ){ + walCleanupHash(pWal); +- assert( !sLoc.aPgno[idx] ); ++ assert( !sLoc.aPgno[idx-1] ); + } + + /* Write the aPgno[] array entry and the hash-table slot. */ +@@ -60501,7 +67212,7 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ + for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ + if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; + } +- sLoc.aPgno[idx] = iPage; ++ sLoc.aPgno[idx-1] = iPage; + AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); + + #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT +@@ -60522,19 +67233,18 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ + */ + if( (idx&0x3ff)==0 ){ + int i; /* Loop counter */ +- for(i=1; i<=idx; i++){ ++ for(i=0; iapWiData[iPg] = aPrivate; + + for(iFrame=iFirst; iFrame<=iLast; iFrame++){ +@@ -60683,6 +67396,7 @@ static int walIndexRecover(Wal *pWal){ + } + } + pWal->apWiData[iPg] = aShare; ++ SEH_SET_ON_ERROR(0,0); + nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); + nHdr32 = nHdr / sizeof(u32); + #ifndef SQLITE_SAFER_WALINDEX_RECOVERY +@@ -60713,9 +67427,11 @@ static int walIndexRecover(Wal *pWal){ + } + } + #endif ++ SEH_INJECT_FAULT; + if( iFrame<=iLast ) break; + } + ++ SEH_FREE_ON_ERROR(aFrame, 0); + sqlite3_free(aFrame); + } + +@@ -60743,6 +67459,7 @@ finished: + }else{ + pInfo->aReadMark[i] = READMARK_NOT_USED; + } ++ SEH_INJECT_FAULT; + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc!=SQLITE_BUSY ){ + goto recovery_error; +@@ -60814,14 +67531,43 @@ SQLITE_PRIVATE int sqlite3WalOpen( + assert( zWalName && zWalName[0] ); + assert( pDbFd ); + ++ /* Verify the values of various constants. Any changes to the values ++ ** of these constants would result in an incompatible on-disk format ++ ** for the -shm file. Any change that causes one of these asserts to ++ ** fail is a backward compatibility problem, even if the change otherwise ++ ** works. ++ ** ++ ** This table also serves as a helpful cross-reference when trying to ++ ** interpret hex dumps of the -shm file. ++ */ ++ assert( 48 == sizeof(WalIndexHdr) ); ++ assert( 40 == sizeof(WalCkptInfo) ); ++ assert( 120 == WALINDEX_LOCK_OFFSET ); ++ assert( 136 == WALINDEX_HDR_SIZE ); ++ assert( 4096 == HASHTABLE_NPAGE ); ++ assert( 4062 == HASHTABLE_NPAGE_ONE ); ++ assert( 8192 == HASHTABLE_NSLOT ); ++ assert( 383 == HASHTABLE_HASH_1 ); ++ assert( 32768 == WALINDEX_PGSZ ); ++ assert( 8 == SQLITE_SHM_NLOCK ); ++ assert( 5 == WAL_NREADER ); ++ assert( 24 == WAL_FRAME_HDRSIZE ); ++ assert( 32 == WAL_HDRSIZE ); ++ assert( 120 == WALINDEX_LOCK_OFFSET + WAL_WRITE_LOCK ); ++ assert( 121 == WALINDEX_LOCK_OFFSET + WAL_CKPT_LOCK ); ++ assert( 122 == WALINDEX_LOCK_OFFSET + WAL_RECOVER_LOCK ); ++ assert( 123 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(0) ); ++ assert( 124 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(1) ); ++ assert( 125 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(2) ); ++ assert( 126 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(3) ); ++ assert( 127 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(4) ); ++ + /* In the amalgamation, the os_unix.c and os_win.c source files come before + ** this source file. Verify that the #defines of the locking byte offsets + ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value. + ** For that matter, if the lock offset ever changes from its initial design + ** value of 120, we need to know that so there is an assert() to check it. + */ +- assert( 120==WALINDEX_LOCK_OFFSET ); +- assert( 136==WALINDEX_HDR_SIZE ); + #ifdef WIN_SHM_BASE + assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET ); + #endif +@@ -60871,7 +67617,7 @@ SQLITE_PRIVATE int sqlite3WalOpen( + } + + /* +-** Change the size to which the WAL file is trucated on each reset. ++** Change the size to which the WAL file is truncated on each reset. + */ + SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){ + if( pWal ) pWal->mxWalSize = iLimit; +@@ -61094,26 +67840,18 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ + + /* Allocate space for the WalIterator object. */ + nSegment = walFramePage(iLast) + 1; +- nByte = sizeof(WalIterator) +- + (nSegment-1)*sizeof(struct WalSegment) ++ nByte = SZ_WALITERATOR(nSegment) + + iLast*sizeof(ht_slot); +- p = (WalIterator *)sqlite3_malloc64(nByte); ++ p = (WalIterator *)sqlite3_malloc64(nByte ++ + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) ++ ); + if( !p ){ + return SQLITE_NOMEM_BKPT; + } + memset(p, 0, nByte); + p->nSegment = nSegment; +- +- /* Allocate temporary space used by the merge-sort routine. This block +- ** of memory will be freed before this function returns. +- */ +- aTmp = (ht_slot *)sqlite3_malloc64( +- sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) +- ); +- if( !aTmp ){ +- rc = SQLITE_NOMEM_BKPT; +- } +- ++ aTmp = (ht_slot*)&(((u8*)p)[nByte]); ++ SEH_FREE_ON_ERROR(0, p); + for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && iaSegment[i].aPgno = (u32 *)sLoc.aPgno; + } + } +- sqlite3_free(aTmp); +- + if( rc!=SQLITE_OK ){ ++ SEH_FREE_ON_ERROR(p, 0); + walIteratorFree(p); + p = 0; + } +@@ -61153,6 +67889,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ + } + + #ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ ++ ++/* ++** Attempt to enable blocking locks that block for nMs ms. Return 1 if ++** blocking locks are successfully enabled, or 0 otherwise. ++*/ ++static int walEnableBlockingMs(Wal *pWal, int nMs){ ++ int rc = sqlite3OsFileControl( ++ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs ++ ); ++ return (rc==SQLITE_OK); ++} ++ + /* + ** Attempt to enable blocking locks. Blocking locks are enabled only if (a) + ** they are supported by the VFS, and (b) the database handle is configured +@@ -61162,13 +67911,9 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ + static int walEnableBlocking(Wal *pWal){ + int res = 0; + if( pWal->db ){ +- int tmout = pWal->db->busyTimeout; ++ int tmout = pWal->db->setlkTimeout; + if( tmout ){ +- int rc; +- rc = sqlite3OsFileControl( +- pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout +- ); +- res = (rc==SQLITE_OK); ++ res = walEnableBlockingMs(pWal, tmout); + } + } + return res; +@@ -61217,20 +67962,10 @@ SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){ + pWal->db = db; + } + +-/* +-** Take an exclusive WRITE lock. Blocking if so configured. +-*/ +-static int walLockWriter(Wal *pWal){ +- int rc; +- walEnableBlocking(pWal); +- rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); +- walDisableBlocking(pWal); +- return rc; +-} + #else + # define walEnableBlocking(x) 0 + # define walDisableBlocking(x) +-# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) ++# define walEnableBlockingMs(pWal, ms) 0 + # define sqlite3WalDb(pWal, db) + #endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ + +@@ -61370,13 +68105,13 @@ static int walCheckpoint( + mxSafeFrame = pWal->hdr.mxFrame; + mxPage = pWal->hdr.nPage; + for(i=1; iaReadMark+i); ++ u32 y = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; + if( mxSafeFrame>y ){ + assert( y<=pWal->hdr.mxFrame ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); + if( rc==SQLITE_OK ){ + u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); +- AtomicStore(pInfo->aReadMark+i, iMark); ++ AtomicStore(pInfo->aReadMark+i, iMark); SEH_INJECT_FAULT; + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc==SQLITE_BUSY ){ + mxSafeFrame = y; +@@ -61397,8 +68132,7 @@ static int walCheckpoint( + && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK + ){ + u32 nBackfill = pInfo->nBackfill; +- +- pInfo->nBackfillAttempted = mxSafeFrame; ++ pInfo->nBackfillAttempted = mxSafeFrame; SEH_INJECT_FAULT; + + /* Sync the WAL to disk */ + rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); +@@ -61429,6 +68163,7 @@ static int walCheckpoint( + while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ + i64 iOffset; + assert( walFramePgno(pWal, iFrame)==iDbpage ); ++ SEH_INJECT_FAULT; + if( AtomicLoad(&db->u1.isInterrupted) ){ + rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; + break; +@@ -61458,7 +68193,7 @@ static int walCheckpoint( + } + } + if( rc==SQLITE_OK ){ +- AtomicStore(&pInfo->nBackfill, mxSafeFrame); ++ AtomicStore(&pInfo->nBackfill, mxSafeFrame); SEH_INJECT_FAULT; + } + } + +@@ -61480,6 +68215,7 @@ static int walCheckpoint( + */ + if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + assert( pWal->writeLock ); ++ SEH_INJECT_FAULT; + if( pInfo->nBackfillhdr.mxFrame ){ + rc = SQLITE_BUSY; + }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ +@@ -61511,6 +68247,7 @@ static int walCheckpoint( + } + + walcheckpoint_out: ++ SEH_FREE_ON_ERROR(pIter, 0); + walIteratorFree(pIter); + return rc; + } +@@ -61533,6 +68270,95 @@ static void walLimitSize(Wal *pWal, i64 nMax){ + } + } + ++#ifdef SQLITE_USE_SEH ++/* ++** This is the "standard" exception handler used in a few places to handle ++** an exception thrown by reading from the *-shm mapping after it has become ++** invalid in SQLITE_USE_SEH builds. It is used as follows: ++** ++** SEH_TRY { ... } ++** SEH_EXCEPT( rc = walHandleException(pWal); ) ++** ++** This function does three things: ++** ++** 1) Determines the locks that should be held, based on the contents of ++** the Wal.readLock, Wal.writeLock and Wal.ckptLock variables. All other ++** held locks are assumed to be transient locks that would have been ++** released had the exception not been thrown and are dropped. ++** ++** 2) Frees the pointer at Wal.pFree, if any, using sqlite3_free(). ++** ++** 3) Set pWal->apWiData[pWal->iWiPg] to pWal->pWiValue if not NULL ++** ++** 4) Returns SQLITE_IOERR. ++*/ ++static int walHandleException(Wal *pWal){ ++ if( pWal->exclusiveMode==0 ){ ++ static const int S = 1; ++ static const int E = (1<writeLock==2 ) pWal->writeLock = 0; ++ mUnlock = pWal->lockMask & ~( ++ (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) ++ | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) ++ | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) ++ ); ++ for(ii=0; iipFree); ++ pWal->pFree = 0; ++ if( pWal->pWiValue ){ ++ pWal->apWiData[pWal->iWiPg] = pWal->pWiValue; ++ pWal->pWiValue = 0; ++ } ++ return SQLITE_IOERR_IN_PAGE; ++} ++ ++/* ++** Assert that the Wal.lockMask mask, which indicates the locks held ++** by the connection, is consistent with the Wal.readLock, Wal.writeLock ++** and Wal.ckptLock variables. To be used as: ++** ++** assert( walAssertLockmask(pWal) ); ++*/ ++static int walAssertLockmask(Wal *pWal){ ++ if( pWal->exclusiveMode==0 ){ ++ static const int S = 1; ++ static const int E = (1<readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) ++ | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) ++ | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) ++#ifdef SQLITE_ENABLE_SNAPSHOT ++ | (pWal->pSnapshot ? (pWal->lockMask & (1 << WAL_CKPT_LOCK)) : 0) ++#endif ++ ); ++ assert( mExpect==pWal->lockMask ); ++ } ++ return 1; ++} ++ ++/* ++** Return and zero the "system error" field set when an ++** EXCEPTION_IN_PAGE_ERROR exception is caught. ++*/ ++SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal *pWal){ ++ int iRet = 0; ++ if( pWal ){ ++ iRet = pWal->iSysErrno; ++ pWal->iSysErrno = 0; ++ } ++ return iRet; ++} ++ ++#else ++# define walAssertLockmask(x) 1 ++#endif /* ifdef SQLITE_USE_SEH */ ++ + /* + ** Close a connection to a log file. + */ +@@ -61547,6 +68373,8 @@ SQLITE_PRIVATE int sqlite3WalClose( + if( pWal ){ + int isDelete = 0; /* True to unlink wal and wal-index files */ + ++ assert( walAssertLockmask(pWal) ); ++ + /* If an EXCLUSIVE lock can be obtained on the database file (using the + ** ordinary, rollback-mode locking methods, this guarantees that the + ** connection associated with this log file is the only connection to +@@ -61571,7 +68399,7 @@ SQLITE_PRIVATE int sqlite3WalClose( + ); + if( bPersist!=1 ){ + /* Try to delete the WAL file if the checkpoint completed and +- ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal ++ ** fsynced (rc==SQLITE_OK) and if we are not in persistent-wal + ** mode (!bPersist) */ + isDelete = 1; + }else if( pWal->mxWalSize>=0 ){ +@@ -61638,7 +68466,7 @@ static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ + ** give false-positive warnings about these accesses because the tools do not + ** account for the double-read and the memory barrier. The use of mutexes + ** here would be problematic as the memory being accessed is potentially +- ** shared among multiple processes and not all mutex implementions work ++ ** shared among multiple processes and not all mutex implementations work + ** reliably in that environment. + */ + aHdr = walIndexHdr(pWal); +@@ -61740,15 +68568,23 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ + } + }else{ + int bWriteLock = pWal->writeLock; +- if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ +- pWal->writeLock = 1; ++ if( bWriteLock ++ || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ++ ){ ++ /* If the write-lock was just obtained, set writeLock to 2 instead of ++ ** the usual 1. This causes walIndexPage() to behave as if the ++ ** write-lock were held (so that it allocates new pages as required), ++ ** and walHandleException() to unlock the write-lock if a SEH exception ++ ** is thrown. */ ++ if( !bWriteLock ) pWal->writeLock = 2; + if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ + badHdr = walIndexTryHdr(pWal, pChanged); + if( badHdr ){ + /* If the wal-index header is still malformed even while holding + ** a WRITE lock, it can only mean that the header is corrupted and + ** needs to be reconstructed. So run recovery to do exactly that. +- */ ++ ** Disable blocking locks first. */ ++ walDisableBlocking(pWal); + rc = walIndexRecover(pWal); + *pChanged = 1; + } +@@ -61904,7 +68740,9 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ + } + + /* Allocate a buffer to read frames into */ +- szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE; ++ assert( (pWal->szPage & (pWal->szPage-1))==0 ); ++ assert( pWal->szPage>=512 && pWal->szPage<=65536 ); ++ szFrame = pWal->szPage + WAL_FRAME_HDRSIZE; + aFrame = (u8 *)sqlite3_malloc64(szFrame); + if( aFrame==0 ){ + rc = SQLITE_NOMEM_BKPT; +@@ -61918,7 +68756,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ + ** the caller. */ + aSaveCksum[0] = pWal->hdr.aFrameCksum[0]; + aSaveCksum[1] = pWal->hdr.aFrameCksum[1]; +- for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); ++ for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->szPage); + iOffset+szFrame<=szWal; + iOffset+=szFrame + ){ +@@ -61956,6 +68794,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ + return rc; + } + ++/* ++** The final argument passed to walTryBeginRead() is of type (int*). The ++** caller should invoke walTryBeginRead as follows: ++** ++** int cnt = 0; ++** do { ++** rc = walTryBeginRead(..., &cnt); ++** }while( rc==WAL_RETRY ); ++** ++** The final value of "cnt" is of no use to the caller. It is used by ++** the implementation of walTryBeginRead() as follows: ++** ++** + Each time walTryBeginRead() is called, it is incremented. Once ++** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead() ++** has many times been invoked and failed with WAL_RETRY - walTryBeginRead() ++** returns SQLITE_PROTOCOL. ++** ++** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed ++** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS ++** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case ++** the next invocation of walTryBeginRead() may omit an expected call to ++** sqlite3OsSleep(). There has already been a delay when the previous call ++** waited on a lock. ++*/ ++#define WAL_RETRY_PROTOCOL_LIMIT 100 ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++# define WAL_RETRY_BLOCKED_MASK 0x10000000 ++#else ++# define WAL_RETRY_BLOCKED_MASK 0 ++#endif ++ + /* + ** Attempt to start a read transaction. This might fail due to a race or + ** other transient condition. When that happens, it returns WAL_RETRY to +@@ -62006,13 +68875,12 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ + ** so it takes care to hold an exclusive lock on the corresponding + ** WAL_READ_LOCK() while changing values. + */ +-static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ++static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ + volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ +- u32 mxReadMark; /* Largest aReadMark[] value */ +- int mxI; /* Index of largest aReadMark[] value */ +- int i; /* Loop counter */ + int rc = SQLITE_OK; /* Return code */ +- u32 mxFrame; /* Wal frame to lock to */ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ int nBlockTmout = 0; ++#endif + + assert( pWal->readLock<0 ); /* Not currently locked */ + +@@ -62036,14 +68904,34 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ + ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. + ** The total delay time before giving up is less than 10 seconds. + */ +- if( cnt>5 ){ ++ (*pCnt)++; ++ if( *pCnt>5 ){ + int nDelay = 1; /* Pause time in microseconds */ +- if( cnt>100 ){ ++ int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK); ++ if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){ + VVA_ONLY( pWal->lockError = 1; ) + return SQLITE_PROTOCOL; + } +- if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; ++ if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor ++ ** to block for locks for approximately nDelay us. This affects three ++ ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if ++ ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the ++ ** first attempted read fails, and (c) the shared lock taken on the ++ ** read-mark. ++ ** ++ ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error, ++ ** then sleep for the minimum of 1us. The previous call already provided ++ ** an extra delay while it was blocking on the lock. ++ */ ++ nBlockTmout = (nDelay+998) / 1000; ++ if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){ ++ if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1; ++ } ++#endif + sqlite3OsSleep(pWal->pVfs, nDelay); ++ *pCnt &= ~WAL_RETRY_BLOCKED_MASK; + } + + if( !useWal ){ +@@ -62051,6 +68939,12 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ + if( pWal->bShmUnreliable==0 ){ + rc = walIndexReadHdr(pWal, pChanged); + } ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ if( rc==SQLITE_BUSY_TIMEOUT ){ ++ rc = SQLITE_BUSY; ++ *pCnt |= WAL_RETRY_BLOCKED_MASK; ++ } ++#endif + if( rc==SQLITE_BUSY ){ + /* If there is not a recovery running in another thread or process + ** then convert BUSY errors to WAL_RETRY. If recovery is known to +@@ -62060,6 +68954,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ + ** WAL_RETRY this routine will be called again and will probably be + ** right on the second iteration. + */ ++ (void)walEnableBlocking(pWal); + if( pWal->apWiData[0]==0 ){ + /* This branch is taken when the xShmMap() method returns SQLITE_BUSY. + ** We assume this is a transient condition, so return WAL_RETRY. The +@@ -62076,6 +68971,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ + rc = SQLITE_BUSY_RECOVERY; + } + } ++ walDisableBlocking(pWal); + if( rc!=SQLITE_OK ){ + return rc; + } +@@ -62087,136 +68983,201 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ + assert( pWal->nWiData>0 ); + assert( pWal->apWiData[0]!=0 ); + pInfo = walCkptInfo(pWal); +- if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame ++ SEH_INJECT_FAULT; ++ { ++ u32 mxReadMark; /* Largest aReadMark[] value */ ++ int mxI; /* Index of largest aReadMark[] value */ ++ int i; /* Loop counter */ ++ u32 mxFrame; /* Wal frame to lock to */ ++ if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame + #ifdef SQLITE_ENABLE_SNAPSHOT +- && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) ++ && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) + #endif +- ){ +- /* The WAL has been completely backfilled (or it is empty). +- ** and can be safely ignored. +- */ +- rc = walLockShared(pWal, WAL_READ_LOCK(0)); +- walShmBarrier(pWal); +- if( rc==SQLITE_OK ){ +- if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ +- /* It is not safe to allow the reader to continue here if frames +- ** may have been appended to the log before READ_LOCK(0) was obtained. +- ** When holding READ_LOCK(0), the reader ignores the entire log file, +- ** which implies that the database file contains a trustworthy +- ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from +- ** happening, this is usually correct. +- ** +- ** However, if frames have been appended to the log (or if the log +- ** is wrapped and written for that matter) before the READ_LOCK(0) +- ** is obtained, that is not necessarily true. A checkpointer may +- ** have started to backfill the appended frames but crashed before +- ** it finished. Leaving a corrupt image in the database file. +- */ +- walUnlockShared(pWal, WAL_READ_LOCK(0)); +- return WAL_RETRY; ++ ){ ++ /* The WAL has been completely backfilled (or it is empty). ++ ** and can be safely ignored. ++ */ ++ rc = walLockShared(pWal, WAL_READ_LOCK(0)); ++ walShmBarrier(pWal); ++ if( rc==SQLITE_OK ){ ++ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){ ++ /* It is not safe to allow the reader to continue here if frames ++ ** may have been appended to the log before READ_LOCK(0) was obtained. ++ ** When holding READ_LOCK(0), the reader ignores the entire log file, ++ ** which implies that the database file contains a trustworthy ++ ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from ++ ** happening, this is usually correct. ++ ** ++ ** However, if frames have been appended to the log (or if the log ++ ** is wrapped and written for that matter) before the READ_LOCK(0) ++ ** is obtained, that is not necessarily true. A checkpointer may ++ ** have started to backfill the appended frames but crashed before ++ ** it finished. Leaving a corrupt image in the database file. ++ */ ++ walUnlockShared(pWal, WAL_READ_LOCK(0)); ++ return WAL_RETRY; ++ } ++ pWal->readLock = 0; ++ return SQLITE_OK; ++ }else if( rc!=SQLITE_BUSY ){ ++ return rc; + } +- pWal->readLock = 0; +- return SQLITE_OK; +- }else if( rc!=SQLITE_BUSY ){ +- return rc; + } +- } + +- /* If we get this far, it means that the reader will want to use +- ** the WAL to get at content from recent commits. The job now is +- ** to select one of the aReadMark[] entries that is closest to +- ** but not exceeding pWal->hdr.mxFrame and lock that entry. +- */ +- mxReadMark = 0; +- mxI = 0; +- mxFrame = pWal->hdr.mxFrame; ++ /* If we get this far, it means that the reader will want to use ++ ** the WAL to get at content from recent commits. The job now is ++ ** to select one of the aReadMark[] entries that is closest to ++ ** but not exceeding pWal->hdr.mxFrame and lock that entry. ++ */ ++ mxReadMark = 0; ++ mxI = 0; ++ mxFrame = pWal->hdr.mxFrame; + #ifdef SQLITE_ENABLE_SNAPSHOT +- if( pWal->pSnapshot && pWal->pSnapshot->mxFramepSnapshot->mxFrame; +- } +-#endif +- for(i=1; iaReadMark+i); +- if( mxReadMark<=thisMark && thisMark<=mxFrame ){ +- assert( thisMark!=READMARK_NOT_USED ); +- mxReadMark = thisMark; +- mxI = i; ++ if( pWal->pSnapshot && pWal->pSnapshot->mxFramepSnapshot->mxFrame; + } +- } +- if( (pWal->readOnly & WAL_SHM_RDONLY)==0 +- && (mxReadMarkaReadMark+i,mxFrame); +- mxReadMark = mxFrame; ++ u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; ++ if( mxReadMark<=thisMark && thisMark<=mxFrame ){ ++ assert( thisMark!=READMARK_NOT_USED ); ++ mxReadMark = thisMark; + mxI = i; +- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); +- break; +- }else if( rc!=SQLITE_BUSY ){ +- return rc; + } + } +- } +- if( mxI==0 ){ +- assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); +- return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; +- } ++ if( (pWal->readOnly & WAL_SHM_RDONLY)==0 ++ && (mxReadMarkaReadMark+i,mxFrame); ++ mxReadMark = mxFrame; ++ mxI = i; ++ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); ++ break; ++ }else if( rc!=SQLITE_BUSY ){ ++ return rc; ++ } ++ } ++ } ++ if( mxI==0 ){ ++ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); ++ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; ++ } + +- rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); +- if( rc ){ +- return rc==SQLITE_BUSY ? WAL_RETRY : rc; +- } +- /* Now that the read-lock has been obtained, check that neither the +- ** value in the aReadMark[] array or the contents of the wal-index +- ** header have changed. +- ** +- ** It is necessary to check that the wal-index header did not change +- ** between the time it was read and when the shared-lock was obtained +- ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility +- ** that the log file may have been wrapped by a writer, or that frames +- ** that occur later in the log than pWal->hdr.mxFrame may have been +- ** copied into the database by a checkpointer. If either of these things +- ** happened, then reading the database with the current value of +- ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry +- ** instead. +- ** +- ** Before checking that the live wal-index header has not changed +- ** since it was read, set Wal.minFrame to the first frame in the wal +- ** file that has not yet been checkpointed. This client will not need +- ** to read any frames earlier than minFrame from the wal file - they +- ** can be safely read directly from the database file. +- ** +- ** Because a ShmBarrier() call is made between taking the copy of +- ** nBackfill and checking that the wal-header in shared-memory still +- ** matches the one cached in pWal->hdr, it is guaranteed that the +- ** checkpointer that set nBackfill was not working with a wal-index +- ** header newer than that cached in pWal->hdr. If it were, that could +- ** cause a problem. The checkpointer could omit to checkpoint +- ** a version of page X that lies before pWal->minFrame (call that version +- ** A) on the basis that there is a newer version (version B) of the same +- ** page later in the wal file. But if version B happens to like past +- ** frame pWal->hdr.mxFrame - then the client would incorrectly assume +- ** that it can read version A from the database file. However, since +- ** we can guarantee that the checkpointer that set nBackfill could not +- ** see any pages past pWal->hdr.mxFrame, this problem does not come up. +- */ +- pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; +- walShmBarrier(pWal); +- if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark +- || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) +- ){ +- walUnlockShared(pWal, WAL_READ_LOCK(mxI)); +- return WAL_RETRY; +- }else{ +- assert( mxReadMark<=pWal->hdr.mxFrame ); +- pWal->readLock = (i16)mxI; ++ (void)walEnableBlockingMs(pWal, nBlockTmout); ++ rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); ++ walDisableBlocking(pWal); ++ if( rc ){ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ if( rc==SQLITE_BUSY_TIMEOUT ){ ++ *pCnt |= WAL_RETRY_BLOCKED_MASK; ++ } ++#else ++ assert( rc!=SQLITE_BUSY_TIMEOUT ); ++#endif ++ assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT); ++ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; ++ } ++ /* Now that the read-lock has been obtained, check that neither the ++ ** value in the aReadMark[] array or the contents of the wal-index ++ ** header have changed. ++ ** ++ ** It is necessary to check that the wal-index header did not change ++ ** between the time it was read and when the shared-lock was obtained ++ ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility ++ ** that the log file may have been wrapped by a writer, or that frames ++ ** that occur later in the log than pWal->hdr.mxFrame may have been ++ ** copied into the database by a checkpointer. If either of these things ++ ** happened, then reading the database with the current value of ++ ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry ++ ** instead. ++ ** ++ ** Before checking that the live wal-index header has not changed ++ ** since it was read, set Wal.minFrame to the first frame in the wal ++ ** file that has not yet been checkpointed. This client will not need ++ ** to read any frames earlier than minFrame from the wal file - they ++ ** can be safely read directly from the database file. ++ ** ++ ** Because a ShmBarrier() call is made between taking the copy of ++ ** nBackfill and checking that the wal-header in shared-memory still ++ ** matches the one cached in pWal->hdr, it is guaranteed that the ++ ** checkpointer that set nBackfill was not working with a wal-index ++ ** header newer than that cached in pWal->hdr. If it were, that could ++ ** cause a problem. The checkpointer could omit to checkpoint ++ ** a version of page X that lies before pWal->minFrame (call that version ++ ** A) on the basis that there is a newer version (version B) of the same ++ ** page later in the wal file. But if version B happens to like past ++ ** frame pWal->hdr.mxFrame - then the client would incorrectly assume ++ ** that it can read version A from the database file. However, since ++ ** we can guarantee that the checkpointer that set nBackfill could not ++ ** see any pages past pWal->hdr.mxFrame, this problem does not come up. ++ */ ++ pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT; ++ walShmBarrier(pWal); ++ if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark ++ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ++ ){ ++ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); ++ return WAL_RETRY; ++ }else{ ++ assert( mxReadMark<=pWal->hdr.mxFrame ); ++ pWal->readLock = (i16)mxI; ++ } + } + return rc; + } + + #ifdef SQLITE_ENABLE_SNAPSHOT ++/* ++** This function does the work of sqlite3WalSnapshotRecover(). ++*/ ++static int walSnapshotRecover( ++ Wal *pWal, /* WAL handle */ ++ void *pBuf1, /* Temp buffer pWal->szPage bytes in size */ ++ void *pBuf2 /* Temp buffer pWal->szPage bytes in size */ ++){ ++ int szPage = (int)pWal->szPage; ++ int rc; ++ i64 szDb; /* Size of db file in bytes */ ++ ++ rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); ++ if( rc==SQLITE_OK ){ ++ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); ++ u32 i = pInfo->nBackfillAttempted; ++ for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ ++ WalHashLoc sLoc; /* Hash table location */ ++ u32 pgno; /* Page number in db file */ ++ i64 iDbOff; /* Offset of db file entry */ ++ i64 iWalOff; /* Offset of wal file entry */ ++ ++ rc = walHashGet(pWal, walFramePage(i), &sLoc); ++ if( rc!=SQLITE_OK ) break; ++ assert( i - sLoc.iZero - 1 >=0 ); ++ pgno = sLoc.aPgno[i-sLoc.iZero-1]; ++ iDbOff = (i64)(pgno-1) * szPage; ++ ++ if( iDbOff+szPage<=szDb ){ ++ iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; ++ rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); ++ ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); ++ } ++ ++ if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ ++ break; ++ } ++ } ++ ++ pInfo->nBackfillAttempted = i-1; ++ } ++ } ++ ++ return rc; ++} ++ + /* + ** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted + ** variable so that older snapshots can be accessed. To do this, loop +@@ -62242,49 +69203,21 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){ + assert( pWal->readLock>=0 ); + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); + if( rc==SQLITE_OK ){ +- volatile WalCkptInfo *pInfo = walCkptInfo(pWal); +- int szPage = (int)pWal->szPage; +- i64 szDb; /* Size of db file in bytes */ +- +- rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); +- if( rc==SQLITE_OK ){ +- void *pBuf1 = sqlite3_malloc(szPage); +- void *pBuf2 = sqlite3_malloc(szPage); +- if( pBuf1==0 || pBuf2==0 ){ +- rc = SQLITE_NOMEM; +- }else{ +- u32 i = pInfo->nBackfillAttempted; +- for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ +- WalHashLoc sLoc; /* Hash table location */ +- u32 pgno; /* Page number in db file */ +- i64 iDbOff; /* Offset of db file entry */ +- i64 iWalOff; /* Offset of wal file entry */ +- +- rc = walHashGet(pWal, walFramePage(i), &sLoc); +- if( rc!=SQLITE_OK ) break; +- pgno = sLoc.aPgno[i-sLoc.iZero]; +- iDbOff = (i64)(pgno-1) * szPage; +- +- if( iDbOff+szPage<=szDb ){ +- iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; +- rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); +- +- if( rc==SQLITE_OK ){ +- rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); +- } +- +- if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ +- break; +- } +- } +- +- pInfo->nBackfillAttempted = i-1; +- } ++ void *pBuf1 = sqlite3_malloc(pWal->szPage); ++ void *pBuf2 = sqlite3_malloc(pWal->szPage); ++ if( pBuf1==0 || pBuf2==0 ){ ++ rc = SQLITE_NOMEM; ++ }else{ ++ pWal->ckptLock = 1; ++ SEH_TRY { ++ rc = walSnapshotRecover(pWal, pBuf1, pBuf2); + } +- +- sqlite3_free(pBuf1); +- sqlite3_free(pBuf2); ++ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) ++ pWal->ckptLock = 0; + } ++ ++ sqlite3_free(pBuf1); ++ sqlite3_free(pBuf2); + walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); + } + +@@ -62293,28 +69226,20 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){ + #endif /* SQLITE_ENABLE_SNAPSHOT */ + + /* +-** Begin a read transaction on the database. +-** +-** This routine used to be called sqlite3OpenSnapshot() and with good reason: +-** it takes a snapshot of the state of the WAL and wal-index for the current +-** instant in time. The current thread will continue to use this snapshot. +-** Other threads might append new content to the WAL and wal-index but +-** that extra content is ignored by the current thread. +-** +-** If the database contents have changes since the previous read +-** transaction, then *pChanged is set to 1 before returning. The +-** Pager layer will use this to know that its cache is stale and +-** needs to be flushed. ++** This function does the work of sqlite3WalBeginReadTransaction() (see ++** below). That function simply calls this one inside an SEH_TRY{...} block. + */ +-SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ ++static int walBeginReadTransaction(Wal *pWal, int *pChanged){ + int rc; /* Return code */ + int cnt = 0; /* Number of TryBeginRead attempts */ + #ifdef SQLITE_ENABLE_SNAPSHOT ++ int ckptLock = 0; + int bChanged = 0; + WalIndexHdr *pSnapshot = pWal->pSnapshot; + #endif + + assert( pWal->ckptLock==0 ); ++ assert( pWal->nSehTry>0 ); + + #ifdef SQLITE_ENABLE_SNAPSHOT + if( pSnapshot ){ +@@ -62337,12 +69262,12 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ + if( rc!=SQLITE_OK ){ + return rc; + } +- pWal->ckptLock = 1; ++ ckptLock = 1; + } + #endif + + do{ +- rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); ++ rc = walTryBeginRead(pWal, pChanged, 0, &cnt); + }while( rc==WAL_RETRY ); + testcase( (rc&0xff)==SQLITE_BUSY ); + testcase( (rc&0xff)==SQLITE_IOERR ); +@@ -62401,22 +69326,47 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ + } + + /* Release the shared CKPT lock obtained above. */ +- if( pWal->ckptLock ){ ++ if( ckptLock ){ + assert( pSnapshot ); + walUnlockShared(pWal, WAL_CKPT_LOCK); +- pWal->ckptLock = 0; + } + #endif + return rc; + } + ++/* ++** Begin a read transaction on the database. ++** ++** This routine used to be called sqlite3OpenSnapshot() and with good reason: ++** it takes a snapshot of the state of the WAL and wal-index for the current ++** instant in time. The current thread will continue to use this snapshot. ++** Other threads might append new content to the WAL and wal-index but ++** that extra content is ignored by the current thread. ++** ++** If the database contents have changes since the previous read ++** transaction, then *pChanged is set to 1 before returning. The ++** Pager layer will use this to know that its cache is stale and ++** needs to be flushed. ++*/ ++SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ ++ int rc; ++ SEH_TRY { ++ rc = walBeginReadTransaction(pWal, pChanged); ++ } ++ SEH_EXCEPT( rc = walHandleException(pWal); ) ++ return rc; ++} ++ + /* + ** Finish with a read transaction. All this does is release the + ** read-lock. + */ + SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ +- sqlite3WalEndWriteTransaction(pWal); ++#ifndef SQLITE_ENABLE_SETLK_TIMEOUT ++ assert( pWal->writeLock==0 || pWal->readLock<0 ); ++#endif + if( pWal->readLock>=0 ){ ++ sqlite3WalEndWriteTransaction(pWal); + walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); + pWal->readLock = -1; + } +@@ -62430,7 +69380,7 @@ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ + ** Return SQLITE_OK if successful, or an error code if an error occurs. If an + ** error does occur, the final value of *piRead is undefined. + */ +-SQLITE_PRIVATE int sqlite3WalFindFrame( ++static int walFindFrame( + Wal *pWal, /* WAL handle */ + Pgno pgno, /* Database page number to read data for */ + u32 *piRead /* OUT: Frame number (or zero) */ +@@ -62493,13 +69443,15 @@ SQLITE_PRIVATE int sqlite3WalFindFrame( + } + nCollide = HASHTABLE_NSLOT; + iKey = walHash(pgno); ++ SEH_INJECT_FAULT; + while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ + u32 iFrame = iH + sLoc.iZero; +- if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ ++ if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ + assert( iFrame>iRead || CORRUPT_DB ); + iRead = iFrame; + } + if( (nCollide--)==0 ){ ++ *piRead = 0; + return SQLITE_CORRUPT_BKPT; + } + iKey = walNextHash(iKey); +@@ -62529,6 +69481,30 @@ SQLITE_PRIVATE int sqlite3WalFindFrame( + return SQLITE_OK; + } + ++/* ++** Search the wal file for page pgno. If found, set *piRead to the frame that ++** contains the page. Otherwise, if pgno is not in the wal file, set *piRead ++** to zero. ++** ++** Return SQLITE_OK if successful, or an error code if an error occurs. If an ++** error does occur, the final value of *piRead is undefined. ++** ++** The difference between this function and walFindFrame() is that this ++** function wraps walFindFrame() in an SEH_TRY{...} block. ++*/ ++SQLITE_PRIVATE int sqlite3WalFindFrame( ++ Wal *pWal, /* WAL handle */ ++ Pgno pgno, /* Database page number to read data for */ ++ u32 *piRead /* OUT: Frame number (or zero) */ ++){ ++ int rc; ++ SEH_TRY { ++ rc = walFindFrame(pWal, pgno, piRead); ++ } ++ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) ++ return rc; ++} ++ + /* + ** Read the contents of frame iRead from the wal file into buffer pOut + ** (which is nOut bytes in size). Return SQLITE_OK if successful, or an +@@ -62583,7 +69559,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ + ** read-transaction was even opened, making this call a no-op. + ** Return early. */ + if( pWal->writeLock ){ +- assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); ++ assert( !memcmp(&pWal->hdr,(void*)pWal->apWiData[0],sizeof(WalIndexHdr)) ); + return SQLITE_OK; + } + #endif +@@ -62610,12 +69586,17 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ + ** time the read transaction on this connection was started, then + ** the write is disallowed. + */ +- if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ ++ SEH_TRY { ++ if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ ++ rc = SQLITE_BUSY_SNAPSHOT; ++ } ++ } ++ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) ++ ++ if( rc!=SQLITE_OK ){ + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + pWal->writeLock = 0; +- rc = SQLITE_BUSY_SNAPSHOT; + } +- + return rc; + } + +@@ -62651,30 +69632,34 @@ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *p + Pgno iMax = pWal->hdr.mxFrame; + Pgno iFrame; + +- /* Restore the clients cache of the wal-index header to the state it +- ** was in before the client began writing to the database. +- */ +- memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); +- +- for(iFrame=pWal->hdr.mxFrame+1; +- ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; +- iFrame++ +- ){ +- /* This call cannot fail. Unless the page for which the page number +- ** is passed as the second argument is (a) in the cache and +- ** (b) has an outstanding reference, then xUndo is either a no-op +- ** (if (a) is false) or simply expels the page from the cache (if (b) +- ** is false). +- ** +- ** If the upper layer is doing a rollback, it is guaranteed that there +- ** are no outstanding references to any page other than page 1. And +- ** page 1 is never written to the log until the transaction is +- ** committed. As a result, the call to xUndo may not fail. ++ SEH_TRY { ++ /* Restore the clients cache of the wal-index header to the state it ++ ** was in before the client began writing to the database. + */ +- assert( walFramePgno(pWal, iFrame)!=1 ); +- rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); ++ memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); ++ ++ for(iFrame=pWal->hdr.mxFrame+1; ++ ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; ++ iFrame++ ++ ){ ++ /* This call cannot fail. Unless the page for which the page number ++ ** is passed as the second argument is (a) in the cache and ++ ** (b) has an outstanding reference, then xUndo is either a no-op ++ ** (if (a) is false) or simply expels the page from the cache (if (b) ++ ** is false). ++ ** ++ ** If the upper layer is doing a rollback, it is guaranteed that there ++ ** are no outstanding references to any page other than page 1. And ++ ** page 1 is never written to the log until the transaction is ++ ** committed. As a result, the call to xUndo may not fail. ++ */ ++ assert( walFramePgno(pWal, iFrame)!=1 ); ++ rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); ++ } ++ if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); + } +- if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); ++ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) ++ pWal->iReCksum = 0; + } + return rc; + } +@@ -62718,7 +69703,13 @@ SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ + pWal->hdr.mxFrame = aWalData[0]; + pWal->hdr.aFrameCksum[0] = aWalData[1]; + pWal->hdr.aFrameCksum[1] = aWalData[2]; +- walCleanupHash(pWal); ++ SEH_TRY { ++ walCleanupHash(pWal); ++ } ++ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) ++ if( pWal->iReCksum>pWal->hdr.mxFrame ){ ++ pWal->iReCksum = 0; ++ } + } + + return rc; +@@ -62768,7 +69759,7 @@ static int walRestartLog(Wal *pWal){ + cnt = 0; + do{ + int notUsed; +- rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); ++ rc = walTryBeginRead(pWal, ¬Used, 1, &cnt); + }while( rc==WAL_RETRY ); + assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ + testcase( (rc&0xff)==SQLITE_IOERR ); +@@ -62899,7 +69890,7 @@ static int walRewriteChecksums(Wal *pWal, u32 iLast){ + ** Write a set of frames to the log. The caller must hold the write-lock + ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). + */ +-SQLITE_PRIVATE int sqlite3WalFrames( ++static int walFrames( + Wal *pWal, /* Wal handle to write to */ + int szPage, /* Database page-size in bytes */ + PgHdr *pList, /* List of dirty pages to write */ +@@ -62987,7 +69978,9 @@ SQLITE_PRIVATE int sqlite3WalFrames( + if( rc ) return rc; + } + } +- assert( (int)pWal->szPage==szPage ); ++ if( (int)pWal->szPage!=szPage ){ ++ return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */ ++ } + + /* Setup information needed to write frames into the WAL */ + w.pWal = pWal; +@@ -63008,7 +70001,7 @@ SQLITE_PRIVATE int sqlite3WalFrames( + ** checksums must be recomputed when the transaction is committed. */ + if( iFirst && (p->pDirty || isCommit==0) ){ + u32 iWrite = 0; +- VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite); ++ VVA_ONLY(rc =) walFindFrame(pWal, p->pgno, &iWrite); + assert( rc==SQLITE_OK || iWrite==0 ); + if( iWrite>=iFirst ){ + i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; +@@ -63127,6 +70120,29 @@ SQLITE_PRIVATE int sqlite3WalFrames( + return rc; + } + ++/* ++** Write a set of frames to the log. The caller must hold the write-lock ++** on the log file (obtained using sqlite3WalBeginWriteTransaction()). ++** ++** The difference between this function and walFrames() is that this ++** function wraps walFrames() in an SEH_TRY{...} block. ++*/ ++SQLITE_PRIVATE int sqlite3WalFrames( ++ Wal *pWal, /* Wal handle to write to */ ++ int szPage, /* Database page-size in bytes */ ++ PgHdr *pList, /* List of dirty pages to write */ ++ Pgno nTruncate, /* Database size after this commit */ ++ int isCommit, /* True if this is a commit */ ++ int sync_flags /* Flags to pass to OsSync() (or 0) */ ++){ ++ int rc; ++ SEH_TRY { ++ rc = walFrames(pWal, szPage, pList, nTruncate, isCommit, sync_flags); ++ } ++ SEH_EXCEPT( rc = walHandleException(pWal); ) ++ return rc; ++} ++ + /* + ** This routine is called to implement sqlite3_wal_checkpoint() and + ** related interfaces. +@@ -63164,10 +70180,9 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( + if( pWal->readOnly ) return SQLITE_READONLY; + WALTRACE(("WAL%p: checkpoint begins\n", pWal)); + +- /* Enable blocking locks, if possible. If blocking locks are successfully +- ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ ++ /* Enable blocking locks, if possible. */ + sqlite3WalDb(pWal, db); +- (void)walEnableBlocking(pWal); ++ if( xBusy2 ) (void)walEnableBlocking(pWal); + + /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive + ** "checkpoint" lock on the database file. +@@ -63206,30 +70221,38 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( + + + /* Read the wal-index header. */ +- if( rc==SQLITE_OK ){ +- walDisableBlocking(pWal); +- rc = walIndexReadHdr(pWal, &isChanged); +- (void)walEnableBlocking(pWal); +- if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ +- sqlite3OsUnfetch(pWal->pDbFd, 0, 0); ++ SEH_TRY { ++ if( rc==SQLITE_OK ){ ++ /* For a passive checkpoint, do not re-enable blocking locks after ++ ** reading the wal-index header. A passive checkpoint should not block ++ ** or invoke the busy handler. The only lock such a checkpoint may ++ ** attempt to obtain is a lock on a read-slot, and it should give up ++ ** immediately and do a partial checkpoint if it cannot obtain it. */ ++ walDisableBlocking(pWal); ++ rc = walIndexReadHdr(pWal, &isChanged); ++ if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal); ++ if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ ++ sqlite3OsUnfetch(pWal->pDbFd, 0, 0); ++ } + } +- } +- +- /* Copy data from the log to the database file. */ +- if( rc==SQLITE_OK ){ + +- if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ +- rc = SQLITE_CORRUPT_BKPT; +- }else{ +- rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); +- } ++ /* Copy data from the log to the database file. */ ++ if( rc==SQLITE_OK ){ ++ if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ ++ rc = SQLITE_CORRUPT_BKPT; ++ }else{ ++ rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf); ++ } + +- /* If no error occurred, set the output variables. */ +- if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ +- if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; +- if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); ++ /* If no error occurred, set the output variables. */ ++ if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ ++ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; ++ SEH_INJECT_FAULT; ++ if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); ++ } + } + } ++ SEH_EXCEPT( rc = walHandleException(pWal); ) + + if( isChanged ){ + /* If a new wal-index header was loaded before the checkpoint was +@@ -63306,7 +70329,9 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){ + ** locks are taken in this case). Nor should the pager attempt to + ** upgrade to exclusive-mode following such an error. + */ ++#ifndef SQLITE_USE_SEH + assert( pWal->readLock>=0 || pWal->lockError ); ++#endif + assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) ); + + if( op==0 ){ +@@ -63374,7 +70399,20 @@ SQLITE_PRIVATE void sqlite3WalSnapshotOpen( + Wal *pWal, + sqlite3_snapshot *pSnapshot + ){ +- pWal->pSnapshot = (WalIndexHdr*)pSnapshot; ++ if( pSnapshot && ((WalIndexHdr*)pSnapshot)->iVersion==0 ){ ++ /* iVersion==0 means that this is a call to sqlite3_snapshot_get(). In ++ ** this case set the bGetSnapshot flag so that if the call to ++ ** sqlite3_snapshot_get() is about to read transaction on this wal ++ ** file, it does not take read-lock 0 if the wal file has been completely ++ ** checkpointed. Taking read-lock 0 would work, but then it would be ++ ** possible for a subsequent writer to destroy the snapshot even while ++ ** this connection is holding its read-transaction open. This is contrary ++ ** to user expectations, so we avoid it by not taking read-lock 0. */ ++ pWal->bGetSnapshot = 1; ++ }else{ ++ pWal->pSnapshot = (WalIndexHdr*)pSnapshot; ++ pWal->bGetSnapshot = 0; ++ } + } + + /* +@@ -63407,16 +70445,19 @@ SQLITE_API int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){ + */ + SQLITE_PRIVATE int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){ + int rc; +- rc = walLockShared(pWal, WAL_CKPT_LOCK); +- if( rc==SQLITE_OK ){ +- WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; +- if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) +- || pNew->mxFramenBackfillAttempted +- ){ +- rc = SQLITE_ERROR_SNAPSHOT; +- walUnlockShared(pWal, WAL_CKPT_LOCK); ++ SEH_TRY { ++ rc = walLockShared(pWal, WAL_CKPT_LOCK); ++ if( rc==SQLITE_OK ){ ++ WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; ++ if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) ++ || pNew->mxFramenBackfillAttempted ++ ){ ++ rc = SQLITE_ERROR_SNAPSHOT; ++ walUnlockShared(pWal, WAL_CKPT_LOCK); ++ } + } + } ++ SEH_EXCEPT( rc = walHandleException(pWal); ) + return rc; + } + +@@ -63539,7 +70580,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ + ** 22 1 Min embedded payload fraction (must be 32) + ** 23 1 Min leaf payload fraction (must be 32) + ** 24 4 File change counter +-** 28 4 Reserved for future use ++** 28 4 The size of the database in pages + ** 32 4 First freelist page + ** 36 4 Number of freelist pages in the file + ** 40 60 15 4-byte meta values passed to higher layers +@@ -63647,7 +70688,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ + ** byte are used. The integer consists of all bytes that have bit 8 set and + ** the first byte with bit 8 clear. The most significant byte of the integer + ** appears first. A variable-length integer may not be more than 9 bytes long. +-** As a special case, all 8 bytes of the 9th byte are used as data. This ++** As a special case, all 8 bits of the 9th byte are used as data. This + ** allows a 64-bit integer to be encoded in 9 bytes. + ** + ** 0x00 becomes 0x00000000 +@@ -63655,7 +70696,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ + ** 0x81 0x00 becomes 0x00000080 + ** 0x82 0x00 becomes 0x00000100 + ** 0x80 0x7f becomes 0x0000007f +-** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 ++** 0x81 0x91 0xd1 0xac 0x78 becomes 0x12345678 + ** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 + ** + ** Variable length integers are used for rowids and to hold the number of +@@ -63738,7 +70779,7 @@ typedef struct CellInfo CellInfo; + ** page that has been loaded into memory. The information in this object + ** is derived from the raw on-disk page content. + ** +-** As each database page is loaded into memory, the pager allocats an ++** As each database page is loaded into memory, the pager allocates an + ** instance of this object and zeros the first 8 bytes. (This is the + ** "extra" information associated with each page of the pager.) + ** +@@ -63747,7 +70788,6 @@ typedef struct CellInfo CellInfo; + */ + struct MemPage { + u8 isInit; /* True if previously initialized. MUST BE FIRST! */ +- u8 bBusy; /* Prevent endless loops on corrupt database files */ + u8 intKey; /* True if table b-trees. False for index b-trees */ + u8 intKeyLeaf; /* True if the leaf of an intKey table */ + Pgno pgno; /* Page number for this page */ +@@ -63769,7 +70809,9 @@ struct MemPage { + u8 *apOvfl[4]; /* Pointers to the body of overflow cells */ + BtShared *pBt; /* Pointer to BtShared that this page is part of */ + u8 *aData; /* Pointer to disk image of the page data */ +- u8 *aDataEnd; /* One byte past the end of usable data */ ++ u8 *aDataEnd; /* One byte past the end of the entire page - not just ++ ** the usable space, the entire page. Used to prevent ++ ** corruption-induced buffer overflow. */ + u8 *aCellIdx; /* The cell index area */ + u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */ + DbPage *pDbPage; /* Pager page handle */ +@@ -63825,9 +70867,12 @@ struct Btree { + u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */ + int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ + int nBackup; /* Number of backup operations reading this btree */ +- u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ ++ u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */ + Btree *pNext; /* List of other sharable Btrees from the same db */ + Btree *pPrev; /* Back pointer of the same list */ ++#ifdef SQLITE_DEBUG ++ u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */ ++#endif + #ifndef SQLITE_OMIT_SHARED_CACHE + BtLock lock; /* Object used to lock page 1 */ + #endif +@@ -63839,11 +70884,25 @@ struct Btree { + ** If the shared-data extension is enabled, there may be multiple users + ** of the Btree structure. At most one of these may open a write transaction, + ** but any number may have active read transactions. ++** ++** These values must match SQLITE_TXN_NONE, SQLITE_TXN_READ, and ++** SQLITE_TXN_WRITE + */ + #define TRANS_NONE 0 + #define TRANS_READ 1 + #define TRANS_WRITE 2 + ++#if TRANS_NONE!=SQLITE_TXN_NONE ++# error wrong numeric code for no-transaction ++#endif ++#if TRANS_READ!=SQLITE_TXN_READ ++# error wrong numeric code for read-transaction ++#endif ++#if TRANS_WRITE!=SQLITE_TXN_WRITE ++# error wrong numeric code for write-transaction ++#endif ++ ++ + /* + ** An instance of this object represents a single database file. + ** +@@ -63913,6 +70972,7 @@ struct BtShared { + Btree *pWriter; /* Btree with currently open write transaction */ + #endif + u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ ++ int nPreformatSize; /* Size of last cell written by TransferRow() */ + }; + + /* +@@ -63952,6 +71012,12 @@ struct CellInfo { + */ + #define BTCURSOR_MAX_DEPTH 20 + ++/* ++** Maximum amount of storage local to a database page, regardless of ++** page size. ++*/ ++#define BT_MAX_LOCAL 65501 /* 65536 - 35 */ ++ + /* + ** A cursor is a pointer to a particular entry within a particular + ** b-tree within a database file. +@@ -64012,7 +71078,7 @@ struct BtCursor { + #define BTCF_WriteFlag 0x01 /* True if a write cursor */ + #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ + #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ +-#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ ++#define BTCF_AtLast 0x08 /* Cursor is pointing to the last entry */ + #define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ + #define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ + #define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ +@@ -64056,7 +71122,7 @@ struct BtCursor { + /* + ** The database page the PENDING_BYTE occupies. This page is never used. + */ +-# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) ++#define PENDING_BYTE_PAGE(pBt) ((Pgno)((PENDING_BYTE/((pBt)->pageSize))+1)) + + /* + ** These macros define the location of the pointer-map entry for a +@@ -64130,15 +71196,15 @@ struct BtCursor { + ** So, this macro is defined instead. + */ + #ifndef SQLITE_OMIT_AUTOVACUUM +-#define ISAUTOVACUUM (pBt->autoVacuum) ++#define ISAUTOVACUUM(pBt) (pBt->autoVacuum) + #else +-#define ISAUTOVACUUM 0 ++#define ISAUTOVACUUM(pBt) 0 + #endif + + + /* +-** This structure is passed around through all the sanity checking routines +-** in order to keep track of some global state information. ++** This structure is passed around through all the PRAGMA integrity_check ++** checking routines in order to keep track of some global state information. + ** + ** The aRef[] array is allocated so that there is 1 bit for each page in + ** the database. As the integrity-check proceeds, for each page used in +@@ -64151,16 +71217,19 @@ struct IntegrityCk { + BtShared *pBt; /* The tree being checked out */ + Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ + u8 *aPgRef; /* 1 bit per page in the db (see above) */ +- Pgno nPage; /* Number of pages in the database */ ++ Pgno nCkPage; /* Pages in the database. 0 for partial check */ + int mxErr; /* Stop accumulating errors when this reaches zero */ + int nErr; /* Number of messages written to zErrMsg so far */ +- int bOomFault; /* A memory allocation error has occurred */ ++ int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */ ++ u32 nStep; /* Number of steps into the integrity_check process */ + const char *zPfx; /* Error message prefix */ +- Pgno v1; /* Value for first %u substitution in zPfx */ +- int v2; /* Value for second %d substitution in zPfx */ ++ Pgno v0; /* Value for first %u substitution in zPfx (root page) */ ++ Pgno v1; /* Value for second %u substitution in zPfx (current pg) */ ++ int v2; /* Value for third %d substitution in zPfx */ + StrAccum errMsg; /* Accumulate the error message text here */ + u32 *heap; /* Min-heap used for analyzing cell coverage */ + sqlite3 *db; /* Database connection running the check */ ++ i64 nRow; /* Number of rows visited in current tree */ + }; + + /* +@@ -64173,7 +71242,7 @@ struct IntegrityCk { + + /* + ** get2byteAligned(), unlike get2byte(), requires that its argument point to a +-** two-byte aligned address. get2bytea() is only used for accessing the ++** two-byte aligned address. get2byteAligned() is only used for accessing the + ** cell addresses in a btree header. + */ + #if SQLITE_BYTEORDER==4321 +@@ -64350,14 +71419,14 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){ + ** + ** There is a corresponding leave-all procedures. + ** +-** Enter the mutexes in accending order by BtShared pointer address ++** Enter the mutexes in ascending order by BtShared pointer address + ** to avoid the possibility of deadlock when two threads with + ** two or more btrees in common both try to lock all their btrees + ** at the same instant. + */ + static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){ + int i; +- int skipOk = 1; ++ u8 skipOk = 1; + Btree *p; + assert( sqlite3_mutex_held(db->mutex) ); + for(i=0; inDb; i++){ +@@ -64424,6 +71493,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ + SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){ + Btree *p; + assert( db!=0 ); ++ if( db->pVfs==0 && db->nDb==0 ) return 1; + if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema); + assert( iDb>=0 && iDbnDb ); + if( !sqlite3_mutex_held(db->mutex) ) return 0; +@@ -64595,6 +71665,17 @@ SQLITE_API int sqlite3_enable_shared_cache(int enable){ + #define hasReadConflicts(a, b) 0 + #endif + ++#ifdef SQLITE_DEBUG ++/* ++** Return and reset the seek counter for a Btree object. ++*/ ++SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ ++ u64 n = pBt->nSeek; ++ pBt->nSeek = 0; ++ return n; ++} ++#endif ++ + /* + ** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single + ** (MemPage*) as an argument. The (MemPage*) must not be NULL. +@@ -64608,8 +71689,8 @@ SQLITE_API int sqlite3_enable_shared_cache(int enable){ + int corruptPageError(int lineno, MemPage *p){ + char *zMsg; + sqlite3BeginBenignMalloc(); +- zMsg = sqlite3_mprintf("database corruption page %d of %s", +- (int)p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) ++ zMsg = sqlite3_mprintf("database corruption page %u of %s", ++ p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) + ); + sqlite3EndBenignMalloc(); + if( zMsg ){ +@@ -64623,8 +71704,47 @@ int corruptPageError(int lineno, MemPage *p){ + # define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) + #endif + ++/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled ++** or if the lock tracking is disabled. This is always the value for ++** release builds. ++*/ ++#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/ ++ + #ifndef SQLITE_OMIT_SHARED_CACHE + ++#if 0 ++/* ^---- Change to 1 and recompile to enable shared-lock tracing ++** for debugging purposes. ++** ++** Print all shared-cache locks on a BtShared. Debugging use only. ++*/ ++static void sharedLockTrace( ++ BtShared *pBt, ++ const char *zMsg, ++ int iRoot, ++ int eLockType ++){ ++ BtLock *pLock; ++ if( iRoot>0 ){ ++ printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W"); ++ }else{ ++ printf("%s-%p:", zMsg, pBt); ++ } ++ for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ ++ printf(" %p/%u%s", pLock->pBtree, pLock->iTable, ++ pLock->eLock==READ_LOCK ? "R" : "W"); ++ while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){ ++ pLock = pLock->pNext; ++ printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W"); ++ } ++ } ++ printf("\n"); ++ fflush(stdout); ++} ++#undef SHARED_LOCK_TRACE ++#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE) ++#endif /* Shared-lock tracing */ ++ + #ifdef SQLITE_DEBUG + /* + **** This function is only used as part of an assert() statement. *** +@@ -64686,7 +71806,7 @@ static int hasSharedCacheTableLock( + int bSeen = 0; + for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ + Index *pIdx = (Index *)sqliteHashData(p); +- if( pIdx->tnum==(int)iRoot ){ ++ if( pIdx->tnum==iRoot ){ + if( bSeen ){ + /* Two or more indexes share the same root page. There must + ** be imposter tables. So just return true. The assert is not +@@ -64701,6 +71821,8 @@ static int hasSharedCacheTableLock( + iTab = iRoot; + } + ++ SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType); ++ + /* Search for the required lock. Either a write-lock on root-page iTab, a + ** write-lock on the schema table, or (if the client is reading) a + ** read-lock on iTab will suffice. Return 1 if any of these are found. */ +@@ -64834,6 +71956,8 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ + BtLock *pLock = 0; + BtLock *pIter; + ++ SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock); ++ + assert( sqlite3BtreeHoldsMutex(p) ); + assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); + assert( p->db!=0 ); +@@ -64901,6 +72025,8 @@ static void clearAllSharedCacheTableLocks(Btree *p){ + assert( p->sharable || 0==*ppIter ); + assert( p->inTrans>0 ); + ++ SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0); ++ + while( *ppIter ){ + BtLock *pLock = *ppIter; + assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); +@@ -64939,6 +72065,9 @@ static void clearAllSharedCacheTableLocks(Btree *p){ + */ + static void downgradeAllSharedCacheTableLocks(Btree *p){ + BtShared *pBt = p->pBt; ++ ++ SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0); ++ + if( pBt->pWriter==p ){ + BtLock *pLock; + pBt->pWriter = 0; +@@ -65019,7 +72148,7 @@ static void invalidateIncrblobCursors( + int isClearTable /* True if all rows are being deleted */ + ){ + BtCursor *p; +- if( pBtree->hasIncrblobCur==0 ) return; ++ assert( pBtree->hasIncrblobCur ); + assert( sqlite3BtreeHoldsMutex(pBtree) ); + pBtree->hasIncrblobCur = 0; + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ +@@ -65153,7 +72282,7 @@ static int saveCursorKey(BtCursor *pCur){ + ** below. */ + void *pKey; + pCur->nKey = sqlite3BtreePayloadSize(pCur); +- pKey = sqlite3Malloc( pCur->nKey + 9 + 8 ); ++ pKey = sqlite3Malloc( ((i64)pCur->nKey) + 9 + 8 ); + if( pKey ){ + rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); + if( rc==SQLITE_OK ){ +@@ -65279,7 +72408,7 @@ SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *pCur){ + /* + ** In this version of BtreeMoveto, pKey is a packed index record + ** such as is generated by the OP_MakeRecord opcode. Unpack the +-** record and then call BtreeMovetoUnpacked() to do the work. ++** record and then call sqlite3BtreeIndexMoveto() to do the work. + */ + static int btreeMoveto( + BtCursor *pCur, /* Cursor open on the btree to be searched */ +@@ -65299,15 +72428,13 @@ static int btreeMoveto( + sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); + if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ + rc = SQLITE_CORRUPT_BKPT; +- goto moveto_done; ++ }else{ ++ rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes); + } ++ sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); + }else{ + pIdxKey = 0; +- } +- rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); +-moveto_done: +- if( pIdxKey ){ +- sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); ++ rc = sqlite3BtreeTableMoveto(pCur, nKey, bias, pRes); + } + return rc; + } +@@ -65420,15 +72547,32 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow) + */ + SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){ + /* Used only by system that substitute their own storage engine */ ++#ifdef SQLITE_DEBUG ++ if( ALWAYS(eHintType==BTREE_HINT_RANGE) ){ ++ va_list ap; ++ Expr *pExpr; ++ Walker w; ++ memset(&w, 0, sizeof(w)); ++ w.xExprCallback = sqlite3CursorRangeHintExprCheck; ++ va_start(ap, eHintType); ++ pExpr = va_arg(ap, Expr*); ++ w.u.aMem = va_arg(ap, Mem*); ++ va_end(ap); ++ assert( pExpr!=0 ); ++ assert( w.u.aMem!=0 ); ++ sqlite3WalkExpr(&w, pExpr); ++ } ++#endif /* SQLITE_DEBUG */ + } +-#endif ++#endif /* SQLITE_ENABLE_CURSOR_HINTS */ ++ + + /* + ** Provide flag hints to the cursor. + */ + SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){ + assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 ); +- pCur->hints = x; ++ pCur->hints = (u8)x; + } + + +@@ -65506,7 +72650,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ + pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); + + if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ +- TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); ++ TRACE(("PTRMAP_UPDATE: %u->(%u,%u)\n", key, eType, parent)); + *pRC= rc = sqlite3PagerWrite(pDbPage); + if( rc==SQLITE_OK ){ + pPtrmap[offset] = eType; +@@ -65615,6 +72759,25 @@ static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow( + pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4; + } + ++/* ++** Given a record with nPayload bytes of payload stored within btree ++** page pPage, return the number of bytes of payload stored locally. ++*/ ++static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){ ++ int maxLocal; /* Maximum amount of payload held locally */ ++ maxLocal = pPage->maxLocal; ++ assert( nPayload>=0 ); ++ if( nPayload<=maxLocal ){ ++ return (int)nPayload; ++ }else{ ++ int minLocal; /* Minimum amount of payload held locally */ ++ int surplus; /* Overflow payload available for local storage */ ++ minLocal = pPage->minLocal; ++ surplus = (int)(minLocal +(nPayload - minLocal)%(pPage->pBt->usableSize-4)); ++ return (surplus <= maxLocal) ? surplus : minLocal; ++ } ++} ++ + /* + ** The following routines are implementations of the MemPage.xParseCell() + ** method. +@@ -65681,19 +72844,37 @@ static void btreeParseCellPtr( + ** + ** pIter += getVarint(pIter, (u64*)&pInfo->nKey); + ** +- ** The code is inlined to avoid a function call. ++ ** The code is inlined and the loop is unrolled for performance. ++ ** This routine is a high-runner. + */ + iKey = *pIter; + if( iKey>=0x80 ){ +- u8 *pEnd = &pIter[7]; +- iKey &= 0x7f; +- while(1){ +- iKey = (iKey<<7) | (*++pIter & 0x7f); +- if( (*pIter)<0x80 ) break; +- if( pIter>=pEnd ){ +- iKey = (iKey<<8) | *++pIter; +- break; ++ u8 x; ++ iKey = (iKey<<7) ^ (x = *++pIter); ++ if( x>=0x80 ){ ++ iKey = (iKey<<7) ^ (x = *++pIter); ++ if( x>=0x80 ){ ++ iKey = (iKey<<7) ^ 0x10204000 ^ (x = *++pIter); ++ if( x>=0x80 ){ ++ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); ++ if( x>=0x80 ){ ++ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); ++ if( x>=0x80 ){ ++ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); ++ if( x>=0x80 ){ ++ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); ++ if( x>=0x80 ){ ++ iKey = (iKey<<8) ^ 0x8000 ^ (*++pIter); ++ } ++ } ++ } ++ } ++ } ++ }else{ ++ iKey ^= 0x204000; + } ++ }else{ ++ iKey ^= 0x4000; + } + } + pIter++; +@@ -65702,12 +72883,14 @@ static void btreeParseCellPtr( + pInfo->nPayload = nPayload; + pInfo->pPayload = pIter; + testcase( nPayload==pPage->maxLocal ); +- testcase( nPayload==pPage->maxLocal+1 ); ++ testcase( nPayload==(u32)pPage->maxLocal+1 ); ++ assert( nPayload>=0 ); ++ assert( pPage->maxLocal <= BT_MAX_LOCAL ); + if( nPayload<=pPage->maxLocal ){ + /* This is the (easy) common case where the entire payload fits + ** on the local page. No overflow is required. + */ +- pInfo->nSize = nPayload + (u16)(pIter - pCell); ++ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); + if( pInfo->nSize<4 ) pInfo->nSize = 4; + pInfo->nLocal = (u16)nPayload; + }else{ +@@ -65739,12 +72922,14 @@ static void btreeParseCellPtrIndex( + pInfo->nPayload = nPayload; + pInfo->pPayload = pIter; + testcase( nPayload==pPage->maxLocal ); +- testcase( nPayload==pPage->maxLocal+1 ); ++ testcase( nPayload==(u32)pPage->maxLocal+1 ); ++ assert( nPayload>=0 ); ++ assert( pPage->maxLocal <= BT_MAX_LOCAL ); + if( nPayload<=pPage->maxLocal ){ + /* This is the (easy) common case where the entire payload fits + ** on the local page. No overflow is required. + */ +- pInfo->nSize = nPayload + (u16)(pIter - pCell); ++ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); + if( pInfo->nSize<4 ) pInfo->nSize = 4; + pInfo->nLocal = (u16)nPayload; + }else{ +@@ -65769,10 +72954,12 @@ static void btreeParseCell( + ** the space used by the cell pointer. + ** + ** cellSizePtrNoPayload() => table internal nodes +-** cellSizePtr() => all index nodes & table leaf nodes ++** cellSizePtrTableLeaf() => table leaf nodes ++** cellSizePtr() => index internal nodes ++** cellSizeIdxLeaf() => index leaf nodes + */ + static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ +- u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */ ++ u8 *pIter = pCell + 4; /* For looping over bytes of pCell */ + u8 *pEnd; /* End mark for a varint */ + u32 nSize; /* Size value to return */ + +@@ -65785,6 +72972,7 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ + pPage->xParseCell(pPage, pCell, &debuginfo); + #endif + ++ assert( pPage->childPtrSize==4 ); + nSize = *pIter; + if( nSize>=0x80 ){ + pEnd = &pIter[8]; +@@ -65794,15 +72982,50 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ + }while( *(pIter)>=0x80 && pIterintKey ){ +- /* pIter now points at the 64-bit integer key value, a variable length +- ** integer. The following block moves pIter to point at the first byte +- ** past the end of the key value. */ +- pEnd = &pIter[9]; +- while( (*pIter++)&0x80 && pItermaxLocal ); ++ testcase( nSize==(u32)pPage->maxLocal+1 ); ++ if( nSize<=pPage->maxLocal ){ ++ nSize += (u32)(pIter - pCell); ++ assert( nSize>4 ); ++ }else{ ++ int minLocal = pPage->minLocal; ++ nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); ++ testcase( nSize==pPage->maxLocal ); ++ testcase( nSize==(u32)pPage->maxLocal+1 ); ++ if( nSize>pPage->maxLocal ){ ++ nSize = minLocal; ++ } ++ nSize += 4 + (u16)(pIter - pCell); ++ } ++ assert( nSize==debuginfo.nSize || CORRUPT_DB ); ++ return (u16)nSize; ++} ++static u16 cellSizePtrIdxLeaf(MemPage *pPage, u8 *pCell){ ++ u8 *pIter = pCell; /* For looping over bytes of pCell */ ++ u8 *pEnd; /* End mark for a varint */ ++ u32 nSize; /* Size value to return */ ++ ++#ifdef SQLITE_DEBUG ++ /* The value returned by this function should always be the same as ++ ** the (CellInfo.nSize) value found by doing a full parse of the ++ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of ++ ** this function verifies that this invariant is not violated. */ ++ CellInfo debuginfo; ++ pPage->xParseCell(pPage, pCell, &debuginfo); ++#endif ++ ++ assert( pPage->childPtrSize==0 ); ++ nSize = *pIter; ++ if( nSize>=0x80 ){ ++ pEnd = &pIter[8]; ++ nSize &= 0x7f; ++ do{ ++ nSize = (nSize<<7) | (*++pIter & 0x7f); ++ }while( *(pIter)>=0x80 && pItermaxLocal ); +- testcase( nSize==pPage->maxLocal+1 ); ++ testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize<=pPage->maxLocal ){ + nSize += (u32)(pIter - pCell); + if( nSize<4 ) nSize = 4; +@@ -65810,7 +73033,7 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ + int minLocal = pPage->minLocal; + nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); +- testcase( nSize==pPage->maxLocal+1 ); ++ testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize>pPage->maxLocal ){ + nSize = minLocal; + } +@@ -65840,6 +73063,58 @@ static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){ + assert( debuginfo.nSize==(u16)(pIter - pCell) || CORRUPT_DB ); + return (u16)(pIter - pCell); + } ++static u16 cellSizePtrTableLeaf(MemPage *pPage, u8 *pCell){ ++ u8 *pIter = pCell; /* For looping over bytes of pCell */ ++ u8 *pEnd; /* End mark for a varint */ ++ u32 nSize; /* Size value to return */ ++ ++#ifdef SQLITE_DEBUG ++ /* The value returned by this function should always be the same as ++ ** the (CellInfo.nSize) value found by doing a full parse of the ++ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of ++ ** this function verifies that this invariant is not violated. */ ++ CellInfo debuginfo; ++ pPage->xParseCell(pPage, pCell, &debuginfo); ++#endif ++ ++ nSize = *pIter; ++ if( nSize>=0x80 ){ ++ pEnd = &pIter[8]; ++ nSize &= 0x7f; ++ do{ ++ nSize = (nSize<<7) | (*++pIter & 0x7f); ++ }while( *(pIter)>=0x80 && pItermaxLocal ); ++ testcase( nSize==(u32)pPage->maxLocal+1 ); ++ if( nSize<=pPage->maxLocal ){ ++ nSize += (u32)(pIter - pCell); ++ if( nSize<4 ) nSize = 4; ++ }else{ ++ int minLocal = pPage->minLocal; ++ nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); ++ testcase( nSize==pPage->maxLocal ); ++ testcase( nSize==(u32)pPage->maxLocal+1 ); ++ if( nSize>pPage->maxLocal ){ ++ nSize = minLocal; ++ } ++ nSize += 4 + (u16)(pIter - pCell); ++ } ++ assert( nSize==debuginfo.nSize || CORRUPT_DB ); ++ return (u16)nSize; ++} + + + #ifdef SQLITE_DEBUG +@@ -65853,7 +73128,7 @@ static u16 cellSize(MemPage *pPage, int iCell){ + #ifndef SQLITE_OMIT_AUTOVACUUM + /* + ** The cell pCell is currently part of page pSrc but will ultimately be part +-** of pPage. (pSrc and pPager are often the same.) If pCell contains a ++** of pPage. (pSrc and pPage are often the same.) If pCell contains a + ** pointer to an overflow page, insert an entry into the pointer-map for + ** the overflow page that will be valid after pCell has been moved to pPage. + */ +@@ -65864,7 +73139,7 @@ static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ + pPage->xParseCell(pPage, pCell, &info); + if( info.nLocalaDataEnd, pCell, pCell+info.nLocal) ){ ++ if( SQLITE_OVERFLOW(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){ + testcase( pSrc!=pPage ); + *pRC = SQLITE_CORRUPT_BKPT; + return; +@@ -65902,14 +73177,14 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ + unsigned char *src; /* Source of content */ + int iCellFirst; /* First allowable cell index */ + int iCellLast; /* Last possible cell index */ ++ int iCellStart; /* First cell offset in input */ + + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( pPage->pBt!=0 ); + assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); + assert( pPage->nOverflow==0 ); + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); +- temp = 0; +- src = data = pPage->aData; ++ data = pPage->aData; + hdr = pPage->hdrOffset; + cellOffset = pPage->cellOffset; + nCell = pPage->nCell; +@@ -65943,7 +73218,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ + if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); + memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); + sz += sz2; +- }else if( NEVER(iFree+sz>usableSize) ){ ++ }else if( iFree+sz>usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + +@@ -65962,41 +73237,39 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ + + cbrk = usableSize; + iCellLast = usableSize - 4; +- for(i=0; iiCellLast ){ +- return SQLITE_CORRUPT_PAGE(pPage); +- } +- assert( pc>=iCellFirst && pc<=iCellLast ); +- size = pPage->xCellSize(pPage, &src[pc]); +- cbrk -= size; +- if( cbrkusableSize ){ +- return SQLITE_CORRUPT_PAGE(pPage); +- } +- assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); +- testcase( cbrk+size==usableSize ); +- testcase( pc+size==usableSize ); +- put2byte(pAddr, cbrk); +- if( temp==0 ){ +- int x; +- if( cbrk==pc ) continue; +- temp = sqlite3PagerTempSpace(pPage->pBt->pPager); +- x = get2byte(&data[hdr+5]); +- memcpy(&temp[x], &data[x], (cbrk+size) - x); +- src = temp; ++ iCellStart = get2byte(&data[hdr+5]); ++ if( nCell>0 ){ ++ temp = sqlite3PagerTempSpace(pPage->pBt->pPager); ++ memcpy(temp, data, usableSize); ++ src = temp; ++ for(i=0; iiCellLast ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } ++ assert( pc>=0 && pc<=iCellLast ); ++ size = pPage->xCellSize(pPage, &src[pc]); ++ cbrk -= size; ++ if( cbrkusableSize ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } ++ assert( cbrk+size<=usableSize && cbrk>=iCellStart ); ++ testcase( cbrk+size==usableSize ); ++ testcase( pc+size==usableSize ); ++ put2byte(pAddr, cbrk); ++ memcpy(&data[cbrk], &src[pc], size); + } +- memcpy(&data[cbrk], &src[pc], size); + } + data[hdr+7] = 0; + +- defragment_out: ++defragment_out: + assert( pPage->nFree>=0 ); + if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ + return SQLITE_CORRUPT_PAGE(pPage); +@@ -66028,7 +73301,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ + const int hdr = pPg->hdrOffset; /* Offset to page header */ + u8 * const aData = pPg->aData; /* Page data */ + int iAddr = hdr + 1; /* Address of ptr to pc */ +- int pc = get2byte(&aData[iAddr]); /* Address of a free slot */ ++ u8 *pTmp = &aData[iAddr]; /* Temporary ptr into aData[] */ ++ int pc = get2byte(pTmp); /* Address of a free slot */ + int x; /* Excess size of the slot */ + int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */ + int size; /* Size of the free slot */ +@@ -66038,7 +73312,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ + /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each + ** freeblock form a big-endian integer which is the size of the freeblock + ** in bytes, including the 4-byte header. */ +- size = get2byte(&aData[pc+2]); ++ pTmp = &aData[pc+2]; ++ size = get2byte(pTmp); + if( (x = size - nByte)>=0 ){ + testcase( x==4 ); + testcase( x==3 ); +@@ -66051,6 +73326,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ + ** fragmented bytes within the page. */ + memcpy(&aData[iAddr], &aData[pc], 2); + aData[hdr+7] += (u8)x; ++ return &aData[pc]; + }else if( x+pc > maxPC ){ + /* This slot extends off the end of the usable part of the page */ + *pRc = SQLITE_CORRUPT_PAGE(pPg); +@@ -66063,10 +73339,11 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ + return &aData[pc + x]; + } + iAddr = pc; +- pc = get2byte(&aData[pc]); +- if( pc<=iAddr+size ){ ++ pTmp = &aData[pc]; ++ pc = get2byte(pTmp); ++ if( pc<=iAddr ){ + if( pc ){ +- /* The next slot in the chain is not past the end of the current slot */ ++ /* The next slot in the chain comes before the current slot */ + *pRc = SQLITE_CORRUPT_PAGE(pPg); + } + return 0; +@@ -66092,11 +73369,12 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ + ** allocation is being made in order to insert a new cell, so we will + ** also end up needing a new cell pointer. + */ +-static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ ++static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ + const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ + u8 * const data = pPage->aData; /* Local cache of pPage->aData */ + int top; /* First byte of cell content area */ + int rc = SQLITE_OK; /* Integer return code */ ++ u8 *pTmp; /* Temp ptr into data[] */ + int gap; /* First byte of gap between cell pointers and cell content */ + + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); +@@ -66115,14 +73393,16 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ + ** then the cell content offset of an empty page wants to be 65536. + ** However, that integer is too large to be stored in a 2-byte unsigned + ** integer, so a value of 0 is used in its place. */ +- top = get2byte(&data[hdr+5]); +- assert( top<=(int)pPage->pBt->usableSize ); /* by btreeComputeFreeSpace() */ ++ pTmp = &data[hdr+5]; ++ top = get2byte(pTmp); + if( gap>top ){ + if( top==0 && pPage->pBt->usableSize==65536 ){ + top = 65536; + }else{ + return SQLITE_CORRUPT_PAGE(pPage); + } ++ }else if( top>(int)pPage->pBt->usableSize ){ ++ return SQLITE_CORRUPT_PAGE(pPage); + } + + /* If there is enough space between gap and top for one more cell pointer, +@@ -66138,7 +73418,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ + int g2; + assert( pSpace+nByte<=data+pPage->pBt->usableSize ); + *pIdx = g2 = (int)(pSpace-data); +- if( NEVER(g2<=gap) ){ ++ if( g2<=gap ){ + return SQLITE_CORRUPT_PAGE(pPage); + }else{ + return SQLITE_OK; +@@ -66184,19 +73464,20 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ + ** + ** Even though the freeblock list was checked by btreeComputeFreeSpace(), + ** that routine will not detect overlap between cells or freeblocks. Nor +-** does it detect cells or freeblocks that encrouch into the reserved bytes ++** does it detect cells or freeblocks that encroach into the reserved bytes + ** at the end of the page. So do additional corruption checks inside this + ** routine and return SQLITE_CORRUPT if any problems are found. + */ +-static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ +- u16 iPtr; /* Address of ptr to next freeblock */ +- u16 iFreeBlk; /* Address of the next freeblock */ ++static int freeSpace(MemPage *pPage, int iStart, int iSize){ ++ int iPtr; /* Address of ptr to next freeblock */ ++ int iFreeBlk; /* Address of the next freeblock */ + u8 hdr; /* Page header size. 0 or 100 */ +- u8 nFrag = 0; /* Reduction in fragmentation */ +- u16 iOrigSize = iSize; /* Original value of iSize */ +- u16 x; /* Offset to cell content area */ +- u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ ++ int nFrag = 0; /* Reduction in fragmentation */ ++ int iOrigSize = iSize; /* Original value of iSize */ ++ int x; /* Offset to cell content area */ ++ int iEnd = iStart + iSize; /* First byte past the iStart buffer */ + unsigned char *data = pPage->aData; /* Page content */ ++ u8 *pTmp; /* Temporary ptr into data[] */ + + assert( pPage->pBt!=0 ); + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); +@@ -66204,7 +73485,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( iSize>=4 ); /* Minimum cell size is 4 */ +- assert( iStart<=pPage->pBt->usableSize-4 ); ++ assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 ); + + /* The list of freeblocks must be in ascending order. Find the + ** spot on the list where iStart should be inserted. +@@ -66215,16 +73496,16 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */ + }else{ + while( (iFreeBlk = get2byte(&data[iPtr]))pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ ++ if( iFreeBlk>(int)pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ + return SQLITE_CORRUPT_PAGE(pPage); + } +- assert( iFreeBlk>iPtr || iFreeBlk==0 ); ++ assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB ); + + /* At this point: + ** iFreeBlk: First freeblock after iStart, or zero if none +@@ -66236,7 +73517,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + nFrag = iFreeBlk - iEnd; + if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage); + iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); +- if( iEnd > pPage->pBt->usableSize ){ ++ if( iEnd > (int)pPage->pBt->usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); + } + iSize = iEnd - iStart; +@@ -66257,9 +73538,15 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + } + } + if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); +- data[hdr+7] -= nFrag; ++ data[hdr+7] -= (u8)nFrag; ++ } ++ pTmp = &data[hdr+5]; ++ x = get2byte(pTmp); ++ if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ ++ /* Overwrite deleted information with zeros when the secure_delete ++ ** option is enabled */ ++ memset(&data[iStart], 0, iSize); + } +- x = get2byte(&data[hdr+5]); + if( iStart<=x ){ + /* The new freeblock is at the beginning of the cell content area, + ** so just extend the cell content area rather than create another +@@ -66271,14 +73558,10 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + }else{ + /* Insert the new freeblock into the freelist */ + put2byte(&data[iPtr], iStart); ++ put2byte(&data[iStart], iFreeBlk); ++ assert( iSize>=0 && iSize<=0xffff ); ++ put2byte(&data[iStart+2], (u16)iSize); + } +- if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ +- /* Overwrite deleted information with zeros when the secure_delete +- ** option is enabled */ +- memset(&data[iStart], 0, iSize); +- } +- put2byte(&data[iStart], iFreeBlk); +- put2byte(&data[iStart+2], iSize); + pPage->nFree += iOrigSize; + return SQLITE_OK; + } +@@ -66290,57 +73573,67 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + ** Only the following combinations are supported. Anything different + ** indicates a corrupt database files: + ** +-** PTF_ZERODATA +-** PTF_ZERODATA | PTF_LEAF +-** PTF_LEAFDATA | PTF_INTKEY +-** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF ++** PTF_ZERODATA (0x02, 2) ++** PTF_LEAFDATA | PTF_INTKEY (0x05, 5) ++** PTF_ZERODATA | PTF_LEAF (0x0a, 10) ++** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF (0x0d, 13) + */ + static int decodeFlags(MemPage *pPage, int flagByte){ + BtShared *pBt; /* A copy of pPage->pBt */ + + assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); +- pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 ); +- flagByte &= ~PTF_LEAF; +- pPage->childPtrSize = 4-4*pPage->leaf; +- pPage->xCellSize = cellSizePtr; + pBt = pPage->pBt; +- if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ +- /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an +- ** interior table b-tree page. */ +- assert( (PTF_LEAFDATA|PTF_INTKEY)==5 ); +- /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a +- ** leaf table b-tree page. */ +- assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); +- pPage->intKey = 1; +- if( pPage->leaf ){ ++ pPage->max1bytePayload = pBt->max1bytePayload; ++ if( flagByte>=(PTF_ZERODATA | PTF_LEAF) ){ ++ pPage->childPtrSize = 0; ++ pPage->leaf = 1; ++ if( flagByte==(PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF) ){ + pPage->intKeyLeaf = 1; ++ pPage->xCellSize = cellSizePtrTableLeaf; + pPage->xParseCell = btreeParseCellPtr; ++ pPage->intKey = 1; ++ pPage->maxLocal = pBt->maxLeaf; ++ pPage->minLocal = pBt->minLeaf; ++ }else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){ ++ pPage->intKey = 0; ++ pPage->intKeyLeaf = 0; ++ pPage->xCellSize = cellSizePtrIdxLeaf; ++ pPage->xParseCell = btreeParseCellPtrIndex; ++ pPage->maxLocal = pBt->maxLocal; ++ pPage->minLocal = pBt->minLocal; + }else{ ++ pPage->intKey = 0; ++ pPage->intKeyLeaf = 0; ++ pPage->xCellSize = cellSizePtrIdxLeaf; ++ pPage->xParseCell = btreeParseCellPtrIndex; ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } ++ }else{ ++ pPage->childPtrSize = 4; ++ pPage->leaf = 0; ++ if( flagByte==(PTF_ZERODATA) ){ ++ pPage->intKey = 0; ++ pPage->intKeyLeaf = 0; ++ pPage->xCellSize = cellSizePtr; ++ pPage->xParseCell = btreeParseCellPtrIndex; ++ pPage->maxLocal = pBt->maxLocal; ++ pPage->minLocal = pBt->minLocal; ++ }else if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtrNoPayload; + pPage->xParseCell = btreeParseCellPtrNoPayload; ++ pPage->intKey = 1; ++ pPage->maxLocal = pBt->maxLeaf; ++ pPage->minLocal = pBt->minLeaf; ++ }else{ ++ pPage->intKey = 0; ++ pPage->intKeyLeaf = 0; ++ pPage->xCellSize = cellSizePtr; ++ pPage->xParseCell = btreeParseCellPtrIndex; ++ return SQLITE_CORRUPT_PAGE(pPage); + } +- pPage->maxLocal = pBt->maxLeaf; +- pPage->minLocal = pBt->minLeaf; +- }else if( flagByte==PTF_ZERODATA ){ +- /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an +- ** interior index b-tree page. */ +- assert( (PTF_ZERODATA)==2 ); +- /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a +- ** leaf index b-tree page. */ +- assert( (PTF_ZERODATA|PTF_LEAF)==10 ); +- pPage->intKey = 0; +- pPage->intKeyLeaf = 0; +- pPage->xParseCell = btreeParseCellPtrIndex; +- pPage->maxLocal = pBt->maxLocal; +- pPage->minLocal = pBt->minLocal; +- }else{ +- /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is +- ** an error. */ +- return SQLITE_CORRUPT_PAGE(pPage); + } +- pPage->max1bytePayload = pBt->max1bytePayload; + return SQLITE_OK; + } + +@@ -66493,9 +73786,9 @@ static int btreeInitPage(MemPage *pPage){ + assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); + pPage->maskPage = (u16)(pBt->pageSize - 1); + pPage->nOverflow = 0; +- pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; ++ pPage->cellOffset = (u16)(pPage->hdrOffset + 8 + pPage->childPtrSize); + pPage->aCellIdx = data + pPage->childPtrSize + 8; +- pPage->aDataEnd = pPage->aData + pBt->usableSize; ++ pPage->aDataEnd = pPage->aData + pBt->pageSize; + pPage->aDataOfst = pPage->aData + pPage->childPtrSize; + /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the + ** number of cells on the page. */ +@@ -66527,10 +73820,10 @@ static int btreeInitPage(MemPage *pPage){ + static void zeroPage(MemPage *pPage, int flags){ + unsigned char *data = pPage->aData; + BtShared *pBt = pPage->pBt; +- u8 hdr = pPage->hdrOffset; +- u16 first; ++ int hdr = pPage->hdrOffset; ++ int first; + +- assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); ++ assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB ); + assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); + assert( sqlite3PagerGetData(pPage->pDbPage) == data ); + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); +@@ -66545,8 +73838,8 @@ static void zeroPage(MemPage *pPage, int flags){ + put2byte(&data[hdr+5], pBt->usableSize); + pPage->nFree = (u16)(pBt->usableSize - first); + decodeFlags(pPage, flags); +- pPage->cellOffset = first; +- pPage->aDataEnd = &data[pBt->usableSize]; ++ pPage->cellOffset = (u16)first; ++ pPage->aDataEnd = &data[pBt->pageSize]; + pPage->aCellIdx = &data[first]; + pPage->aDataOfst = &data[pPage->childPtrSize]; + pPage->nOverflow = 0; +@@ -66631,68 +73924,41 @@ SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree *p){ + + /* + ** Get a page from the pager and initialize it. +-** +-** If pCur!=0 then the page is being fetched as part of a moveToChild() +-** call. Do additional sanity checking on the page in this case. +-** And if the fetch fails, this routine must decrement pCur->iPage. +-** +-** The page is fetched as read-write unless pCur is not NULL and is +-** a read-only cursor. +-** +-** If an error occurs, then *ppPage is undefined. It +-** may remain unchanged, or it may be set to an invalid value. + */ + static int getAndInitPage( + BtShared *pBt, /* The database file */ + Pgno pgno, /* Number of the page to get */ + MemPage **ppPage, /* Write the page pointer here */ +- BtCursor *pCur, /* Cursor to receive the page, or NULL */ + int bReadOnly /* True for a read-only page */ + ){ + int rc; + DbPage *pDbPage; ++ MemPage *pPage; + assert( sqlite3_mutex_held(pBt->mutex) ); +- assert( pCur==0 || ppPage==&pCur->pPage ); +- assert( pCur==0 || bReadOnly==pCur->curPagerFlags ); +- assert( pCur==0 || pCur->iPage>0 ); + + if( pgno>btreePagecount(pBt) ){ +- rc = SQLITE_CORRUPT_BKPT; +- goto getAndInitPage_error1; ++ *ppPage = 0; ++ return SQLITE_CORRUPT_BKPT; + } + rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); + if( rc ){ +- goto getAndInitPage_error1; ++ *ppPage = 0; ++ return rc; + } +- *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); +- if( (*ppPage)->isInit==0 ){ ++ pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); ++ if( pPage->isInit==0 ){ + btreePageFromDbPage(pDbPage, pgno, pBt); +- rc = btreeInitPage(*ppPage); ++ rc = btreeInitPage(pPage); + if( rc!=SQLITE_OK ){ +- goto getAndInitPage_error2; ++ releasePage(pPage); ++ *ppPage = 0; ++ return rc; + } + } +- assert( (*ppPage)->pgno==pgno ); +- assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) ); +- +- /* If obtaining a child page for a cursor, we must verify that the page is +- ** compatible with the root page. */ +- if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ +- rc = SQLITE_CORRUPT_PGNO(pgno); +- goto getAndInitPage_error2; +- } ++ assert( pPage->pgno==pgno || CORRUPT_DB ); ++ assert( pPage->aData==sqlite3PagerGetData(pDbPage) ); ++ *ppPage = pPage; + return SQLITE_OK; +- +-getAndInitPage_error2: +- releasePage(*ppPage); +-getAndInitPage_error1: +- if( pCur ){ +- pCur->iPage--; +- pCur->pPage = pCur->apPage[pCur->iPage]; +- } +- testcase( pgno==0 ); +- assert( pgno!=0 || rc==SQLITE_CORRUPT ); +- return rc; + } + + /* +@@ -66775,7 +74041,7 @@ static void pageReinit(DbPage *pData){ + ** call to btreeInitPage() will likely return SQLITE_CORRUPT. + ** But no harm is done by this. And it is very important that + ** btreeInitPage() be called on every btree page so we make +- ** the call for every page that comes in for re-initing. */ ++ ** the call for every page that comes in for re-initializing. */ + btreeInitPage(pPage); + } + } +@@ -66954,6 +74220,9 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( + assert( sizeof(u16)==2 ); + assert( sizeof(Pgno)==4 ); + ++ /* Suppress false-positive compiler warning from PVS-Studio */ ++ memset(&zDbHeader[16], 0, 8); ++ + pBt = sqlite3MallocZero( sizeof(*pBt) ); + if( pBt==0 ){ + rc = SQLITE_NOMEM_BKPT; +@@ -67088,7 +74357,7 @@ btree_open_out: + ** do not change the pager-cache size. + */ + if( sqlite3BtreeSchema(p, 0, 0)==0 ){ +- sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); ++ sqlite3BtreeSetCacheSize(p, SQLITE_DEFAULT_CACHE_SIZE); + } + + pFile = sqlite3PagerFile(pBt->pPager); +@@ -67149,30 +74418,38 @@ static int removeFromSharingList(BtShared *pBt){ + ** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child + ** pointer. + */ +-static void allocateTempSpace(BtShared *pBt){ +- if( !pBt->pTmpSpace ){ +- pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize ); +- +- /* One of the uses of pBt->pTmpSpace is to format cells before +- ** inserting them into a leaf page (function fillInCell()). If +- ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes +- ** by the various routines that manipulate binary cells. Which +- ** can mean that fillInCell() only initializes the first 2 or 3 +- ** bytes of pTmpSpace, but that the first 4 bytes are copied from +- ** it into a database page. This is not actually a problem, but it +- ** does cause a valgrind error when the 1 or 2 bytes of unitialized +- ** data is passed to system call write(). So to avoid this error, +- ** zero the first 4 bytes of temp space here. +- ** +- ** Also: Provide four bytes of initialized space before the +- ** beginning of pTmpSpace as an area available to prepend the +- ** left-child pointer to the beginning of a cell. +- */ +- if( pBt->pTmpSpace ){ +- memset(pBt->pTmpSpace, 0, 8); +- pBt->pTmpSpace += 4; +- } ++static SQLITE_NOINLINE int allocateTempSpace(BtShared *pBt){ ++ assert( pBt!=0 ); ++ assert( pBt->pTmpSpace==0 ); ++ /* This routine is called only by btreeCursor() when allocating the ++ ** first write cursor for the BtShared object */ ++ assert( pBt->pCursor!=0 && (pBt->pCursor->curFlags & BTCF_WriteFlag)!=0 ); ++ pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize ); ++ if( pBt->pTmpSpace==0 ){ ++ BtCursor *pCur = pBt->pCursor; ++ pBt->pCursor = pCur->pNext; /* Unlink the cursor */ ++ memset(pCur, 0, sizeof(*pCur)); ++ return SQLITE_NOMEM_BKPT; + } ++ ++ /* One of the uses of pBt->pTmpSpace is to format cells before ++ ** inserting them into a leaf page (function fillInCell()). If ++ ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes ++ ** by the various routines that manipulate binary cells. Which ++ ** can mean that fillInCell() only initializes the first 2 or 3 ++ ** bytes of pTmpSpace, but that the first 4 bytes are copied from ++ ** it into a database page. This is not actually a problem, but it ++ ** does cause a valgrind error when the 1 or 2 bytes of uninitialized ++ ** data is passed to system call write(). So to avoid this error, ++ ** zero the first 4 bytes of temp space here. ++ ** ++ ** Also: Provide four bytes of initialized space before the ++ ** beginning of pTmpSpace as an area available to prepend the ++ ** left-child pointer to the beginning of a cell. ++ */ ++ memset(pBt->pTmpSpace, 0, 8); ++ pBt->pTmpSpace += 4; ++ return SQLITE_OK; + } + + /* +@@ -67191,19 +74468,23 @@ static void freeTempSpace(BtShared *pBt){ + */ + SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ + BtShared *pBt = p->pBt; +- BtCursor *pCur; + + /* Close all cursors opened via this handle. */ + assert( sqlite3_mutex_held(p->db->mutex) ); + sqlite3BtreeEnter(p); +- pCur = pBt->pCursor; +- while( pCur ){ +- BtCursor *pTmp = pCur; +- pCur = pCur->pNext; +- if( pTmp->pBtree==p ){ +- sqlite3BtreeCloseCursor(pTmp); ++ ++ /* Verify that no other cursors have this Btree open */ ++#ifdef SQLITE_DEBUG ++ { ++ BtCursor *pCur = pBt->pCursor; ++ while( pCur ){ ++ BtCursor *pTmp = pCur; ++ pCur = pCur->pNext; ++ assert( pTmp->pBtree!=p ); ++ + } + } ++#endif + + /* Rollback any active transaction and free the handle structure. + ** The call to sqlite3BtreeRollback() drops any table-locks held by +@@ -67343,7 +74624,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, + BtShared *pBt = p->pBt; + assert( nReserve>=0 && nReserve<=255 ); + sqlite3BtreeEnter(p); +- pBt->nReserveWanted = nReserve; ++ pBt->nReserveWanted = (u8)nReserve; + x = pBt->pageSize - pBt->usableSize; + if( nReservebtsFlags & BTS_PAGESIZE_FIXED ){ +@@ -67355,6 +74636,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, + ((pageSize-1)&pageSize)==0 ){ + assert( (pageSize & 7)==0 ); + assert( !pBt->pCursor ); ++ if( nReserve>32 && pageSize==512 ) pageSize = 1024; + pBt->pageSize = (u32)pageSize; + freeTempSpace(pBt); + } +@@ -67392,7 +74674,7 @@ SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){ + + /* + ** Return the number of bytes of space at the end of every page that +-** are intentually left unused. This is the "reserved" space that is ++** are intentionally left unused. This is the "reserved" space that is + ** sometimes used by extensions. + ** + ** The value returned is the larger of the current reserve size and +@@ -67448,7 +74730,7 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ + assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); + if( newFlag>=0 ){ + p->pBt->btsFlags &= ~BTS_FAST_SECURE; +- p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag; ++ p->pBt->btsFlags |= (u16)(BTS_SECURE_DELETE*newFlag); + } + b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; + sqlite3BtreeLeave(p); +@@ -67546,7 +74828,6 @@ static int lockBtree(BtShared *pBt){ + MemPage *pPage1; /* Page 1 of the database file */ + u32 nPage; /* Number of pages in the database */ + u32 nPageFile = 0; /* Number of pages in the database file */ +- u32 nPageHeader; /* Number of pages in the database according to hdr */ + + assert( sqlite3_mutex_held(pBt->mutex) ); + assert( pBt->pPage1==0 ); +@@ -67558,7 +74839,7 @@ static int lockBtree(BtShared *pBt){ + /* Do some checking to help insure the file we opened really is + ** a valid database file. + */ +- nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); ++ nPage = get4byte(28+(u8*)pPage1->aData); + sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile); + if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ + nPage = nPageFile; +@@ -67593,7 +74874,7 @@ static int lockBtree(BtShared *pBt){ + goto page1_init_failed; + } + +- /* If the write version is set to 2, this database should be accessed ++ /* If the read version is set to 2, this database should be accessed + ** in WAL mode. If the log is not already open, open it now. Then + ** return SQLITE_OK and return without populating BtShared.pPage1. + ** The caller detects this and calls this function again. This is +@@ -67640,7 +74921,6 @@ static int lockBtree(BtShared *pBt){ + ){ + goto page1_init_failed; + } +- pBt->btsFlags |= BTS_PAGESIZE_FIXED; + assert( (pageSize & 7)==0 ); + /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte + ** integer at offset 20 is the number of bytes of space at the end of +@@ -67660,14 +74940,19 @@ static int lockBtree(BtShared *pBt){ + releasePageOne(pPage1); + pBt->usableSize = usableSize; + pBt->pageSize = pageSize; ++ pBt->btsFlags |= BTS_PAGESIZE_FIXED; + freeTempSpace(pBt); + rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, + pageSize-usableSize); + return rc; + } +- if( sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){ +- rc = SQLITE_CORRUPT_BKPT; +- goto page1_init_failed; ++ if( nPage>nPageFile ){ ++ if( sqlite3WritableSchema(pBt->db)==0 ){ ++ rc = SQLITE_CORRUPT_BKPT; ++ goto page1_init_failed; ++ }else{ ++ nPage = nPageFile; ++ } + } + /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to + ** be less than 480. In other words, if the page size is 512, then the +@@ -67675,6 +74960,7 @@ static int lockBtree(BtShared *pBt){ + if( usableSize<480 ){ + goto page1_init_failed; + } ++ pBt->btsFlags |= BTS_PAGESIZE_FIXED; + pBt->pageSize = pageSize; + pBt->usableSize = usableSize; + #ifndef SQLITE_OMIT_AUTOVACUUM +@@ -67853,7 +75139,11 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){ + ** when A already has a read lock, we encourage A to give up and let B + ** proceed. + */ +-SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ ++static SQLITE_NOINLINE int btreeBeginTrans( ++ Btree *p, /* The btree in which to start the transaction */ ++ int wrflag, /* True to start a write transaction */ ++ int *pSchemaVersion /* Put schema version number here, if not NULL */ ++){ + BtShared *pBt = p->pBt; + Pager *pPager = pBt->pPager; + int rc = SQLITE_OK; +@@ -67960,6 +75250,13 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers + (void)sqlite3PagerWalWriteLock(pPager, 0); + unlockBtreeIfUnused(pBt); + } ++#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) ++ if( rc==SQLITE_BUSY_TIMEOUT ){ ++ /* If a blocking lock timed out, break out of the loop here so that ++ ** the busy-handler is not invoked. */ ++ break; ++ } ++#endif + }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && + btreeInvokeBusyHandler(pBt) ); + sqlite3PagerWalDb(pPager, 0); +@@ -68025,6 +75322,28 @@ trans_begun: + sqlite3BtreeLeave(p); + return rc; + } ++SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ ++ BtShared *pBt; ++ if( p->sharable ++ || p->inTrans==TRANS_NONE ++ || (p->inTrans==TRANS_READ && wrflag!=0) ++ ){ ++ return btreeBeginTrans(p,wrflag,pSchemaVersion); ++ } ++ pBt = p->pBt; ++ if( pSchemaVersion ){ ++ *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]); ++ } ++ if( wrflag ){ ++ /* This call makes sure that the pager has the correct number of ++ ** open savepoints. If the second parameter is greater than 0 and ++ ** the sub-journal is not already open, then it will be opened here. ++ */ ++ return sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); ++ }else{ ++ return SQLITE_OK; ++ } ++} + + #ifndef SQLITE_OMIT_AUTOVACUUM + +@@ -68111,6 +75430,9 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ + } + } + }else{ ++ if( pCell+4 > pPage->aData+pPage->pBt->usableSize ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } + if( get4byte(pCell)==iFrom ){ + put4byte(pCell, iTo); + break; +@@ -68159,7 +75481,7 @@ static int relocatePage( + if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT; + + /* Move page iDbPage from its current location to page number iFreePage */ +- TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", ++ TRACE(("AUTOVACUUM: Moving %u to free page %u (ptr page %u type %u)\n", + iDbPage, iFreePage, iPtrPage, eType)); + rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit); + if( rc!=SQLITE_OK ){ +@@ -68297,12 +75619,17 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ + } + do { + MemPage *pFreePg; ++ Pgno dbSize = btreePagecount(pBt); + rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); + if( rc!=SQLITE_OK ){ + releasePage(pLastPg); + return rc; + } + releasePage(pFreePg); ++ if( iFreePg>dbSize ){ ++ releasePage(pLastPg); ++ return SQLITE_CORRUPT_BKPT; ++ } + }while( bCommit && iFreePg>nFin ); + assert( iFreePgpPager; +- VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager); ) ++ Pager *pPager; ++ BtShared *pBt; ++ sqlite3 *db; ++ VVA_ONLY( int nRef ); ++ ++ assert( p!=0 ); ++ pBt = p->pBt; ++ pPager = pBt->pPager; ++ VVA_ONLY( nRef = sqlite3PagerRefcount(pPager); ) + + assert( sqlite3_mutex_held(pBt->mutex) ); + invalidateAllOverflowCache(pBt); +@@ -68408,6 +75737,7 @@ static int autoVacuumCommit(BtShared *pBt){ + if( !pBt->incrVacuum ){ + Pgno nFin; /* Number of pages in database after autovacuuming */ + Pgno nFree; /* Number of pages on the freelist initially */ ++ Pgno nVac; /* Number of pages to vacuum */ + Pgno iFree; /* The next page to be freed */ + Pgno nOrig; /* Database size before freeing */ + +@@ -68421,18 +75751,42 @@ static int autoVacuumCommit(BtShared *pBt){ + } + + nFree = get4byte(&pBt->pPage1->aData[36]); +- nFin = finalDbSize(pBt, nOrig, nFree); ++ db = p->db; ++ if( db->xAutovacPages ){ ++ int iDb; ++ for(iDb=0; ALWAYS(iDbnDb); iDb++){ ++ if( db->aDb[iDb].pBt==p ) break; ++ } ++ nVac = db->xAutovacPages( ++ db->pAutovacPagesArg, ++ db->aDb[iDb].zDbSName, ++ nOrig, ++ nFree, ++ pBt->pageSize ++ ); ++ if( nVac>nFree ){ ++ nVac = nFree; ++ } ++ if( nVac==0 ){ ++ return SQLITE_OK; ++ } ++ }else{ ++ nVac = nFree; ++ } ++ nFin = finalDbSize(pBt, nOrig, nVac); + if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; + if( nFinnFin && rc==SQLITE_OK; iFree--){ +- rc = incrVacuumStep(pBt, nFin, iFree, 1); ++ rc = incrVacuumStep(pBt, nFin, iFree, nVac==nFree); + } + if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); +- put4byte(&pBt->pPage1->aData[32], 0); +- put4byte(&pBt->pPage1->aData[36], 0); ++ if( nVac==nFree ){ ++ put4byte(&pBt->pPage1->aData[32], 0); ++ put4byte(&pBt->pPage1->aData[36], 0); ++ } + put4byte(&pBt->pPage1->aData[28], nFin); + pBt->bDoTruncate = 1; + pBt->nPage = nFin; +@@ -68483,7 +75837,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ + sqlite3BtreeEnter(p); + #ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ +- rc = autoVacuumCommit(pBt); ++ rc = autoVacuumCommit(p); + if( rc!=SQLITE_OK ){ + sqlite3BtreeLeave(p); + return rc; +@@ -68584,7 +75938,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ + sqlite3BtreeLeave(p); + return rc; + } +- p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ ++ p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */ + pBt->inTransaction = TRANS_READ; + btreeClearHasContent(pBt); + } +@@ -68670,7 +76024,7 @@ static void btreeSetNPage(BtShared *pBt, MemPage *pPage1){ + int nPage = get4byte(&pPage1->aData[28]); + testcase( nPage==0 ); + if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage); +- testcase( pBt->nPage!=nPage ); ++ testcase( pBt->nPage!=(u32)nPage ); + pBt->nPage = nPage; + } + +@@ -68882,10 +76236,6 @@ static int btreeCursor( + assert( pBt->pPage1 && pBt->pPage1->aData ); + assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 ); + +- if( wrFlag ){ +- allocateTempSpace(pBt); +- if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT; +- } + if( iTable<=1 ){ + if( iTable<1 ){ + return SQLITE_CORRUPT_BKPT; +@@ -68902,19 +76252,25 @@ static int btreeCursor( + pCur->pKeyInfo = pKeyInfo; + pCur->pBtree = p; + pCur->pBt = pBt; +- pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; +- pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; ++ pCur->curFlags = 0; + /* If there are two or more cursors on the same btree, then all such + ** cursors *must* have the BTCF_Multiple flag set. */ + for(pX=pBt->pCursor; pX; pX=pX->pNext){ + if( pX->pgnoRoot==iTable ){ + pX->curFlags |= BTCF_Multiple; +- pCur->curFlags |= BTCF_Multiple; ++ pCur->curFlags = BTCF_Multiple; + } + } ++ pCur->eState = CURSOR_INVALID; + pCur->pNext = pBt->pCursor; + pBt->pCursor = pCur; +- pCur->eState = CURSOR_INVALID; ++ if( wrFlag ){ ++ pCur->curFlags |= BTCF_WriteFlag; ++ pCur->curPagerFlags = 0; ++ if( pBt->pTmpSpace==0 ) return allocateTempSpace(pBt); ++ }else{ ++ pCur->curPagerFlags = PAGER_GET_READONLY; ++ } + return SQLITE_OK; + } + static int btreeCursorWithLock( +@@ -68956,6 +76312,25 @@ SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ + return ROUND8(sizeof(BtCursor)); + } + ++#ifdef SQLITE_DEBUG ++/* ++** Return true if and only if the Btree object will be automatically ++** closed with the BtCursor closes. This is used within assert() statements ++** only. ++*/ ++SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor( ++ Btree *pBtree, /* the btree object */ ++ BtCursor *pCur /* Corresponding cursor */ ++){ ++ BtShared *pBt = pBtree->pBt; ++ if( (pBt->openFlags & BTREE_SINGLE)==0 ) return 0; ++ if( pBt->pCursor!=pCur ) return 0; ++ if( pCur->pNext!=0 ) return 0; ++ if( pCur->pBtree!=pBtree ) return 0; ++ return 1; ++} ++#endif ++ + /* + ** Initialize memory that will be converted into a BtCursor object. + ** +@@ -68994,7 +76369,14 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){ + unlockBtreeIfUnused(pBt); + sqlite3_free(pCur->aOverflow); + sqlite3_free(pCur->pKey); +- sqlite3BtreeLeave(pBtree); ++ if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){ ++ /* Since the BtShared is not sharable, there is no need to ++ ** worry about the missing sqlite3BtreeLeave() call here. */ ++ assert( pBtree->sharable==0 ); ++ sqlite3BtreeClose(pBtree); ++ }else{ ++ sqlite3BtreeLeave(pBtree); ++ } + pCur->pBtree = 0; + } + return SQLITE_OK; +@@ -69076,7 +76458,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor *pCur){ + pCur->curFlags &= ~BTCF_Pinned; + } + +-#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + /* + ** Return the offset into the database file for the start of the + ** payload to which the cursor is pointing. +@@ -69088,7 +76469,6 @@ SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor *pCur){ + return (i64)pCur->pBt->pageSize*((i64)pCur->pPage->pgno - 1) + + (i64)(pCur->info.pPayload - pCur->pPage->aData); + } +-#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ + + /* + ** Return the number of bytes of payload for the entry that pCur is +@@ -69114,7 +76494,7 @@ SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor *pCur){ + ** routine always returns 2147483647 (which is the largest record + ** that SQLite can handle) or more. But returning a smaller value might + ** prevent large memory allocations when trying to interpret a +-** corrupt datrabase. ++** corrupt database. + ** + ** The current implementation merely returns the size of the underlying + ** database file. +@@ -69281,7 +76661,9 @@ static int accessPayload( + assert( pPage ); + assert( eOp==0 || eOp==1 ); + assert( pCur->eState==CURSOR_VALID ); +- assert( pCur->ixnCell ); ++ if( pCur->ix>=pPage->nCell ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } + assert( cursorHoldsMutex(pCur) ); + + getCellInfo(pCur); +@@ -69331,9 +76713,12 @@ static int accessPayload( + if( pCur->aOverflow==0 + || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) + ){ +- Pgno *aNew = (Pgno*)sqlite3Realloc( +- pCur->aOverflow, nOvfl*2*sizeof(Pgno) +- ); ++ Pgno *aNew; ++ if( sqlite3FaultSim(413) ){ ++ aNew = 0; ++ }else{ ++ aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno)); ++ } + if( aNew==0 ){ + return SQLITE_NOMEM_BKPT; + }else{ +@@ -69343,6 +76728,12 @@ static int accessPayload( + memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); + pCur->curFlags |= BTCF_ValidOvfl; + }else{ ++ /* Sanity check the validity of the overflow page cache */ ++ assert( pCur->aOverflow[0]==nextPage ++ || pCur->aOverflow[0]==0 ++ || CORRUPT_DB ); ++ assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 ); ++ + /* If the overflow page-list cache has been allocated and the + ** entry for the first required overflow page is valid, skip + ** directly to it. +@@ -69412,7 +76803,6 @@ static int accessPayload( + assert( aWrite>=pBufStart ); /* due to (6) */ + memcpy(aSave, aWrite, 4); + rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); +- if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT; + nextPage = get4byte(aWrite); + memcpy(aWrite, aSave, 4); + }else +@@ -69468,7 +76858,6 @@ SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPage>=0 && pCur->pPage ); +- assert( pCur->ixpPage->nCell ); + return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); + } + +@@ -69530,7 +76919,7 @@ static const void *fetchPayload( + assert( pCur->eState==CURSOR_VALID ); + assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( cursorOwnsBtShared(pCur) ); +- assert( pCur->ixpPage->nCell ); ++ assert( pCur->ixpPage->nCell || CORRUPT_DB ); + assert( pCur->info.nSize>0 ); + assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB ); + assert( pCur->info.pPayloadpPage->aDataEnd ||CORRUPT_DB); +@@ -69575,8 +76964,7 @@ SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){ + ** vice-versa). + */ + static int moveToChild(BtCursor *pCur, u32 newPgno){ +- BtShared *pBt = pCur->pBt; +- ++ int rc; + assert( cursorOwnsBtShared(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPageapPage[pCur->iPage] = pCur->pPage; + pCur->ix = 0; + pCur->iPage++; +- return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags); ++ rc = getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur->curPagerFlags); ++ assert( pCur->pPage!=0 || rc!=SQLITE_OK ); ++ if( rc==SQLITE_OK ++ && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) ++ ){ ++ releasePage(pCur->pPage); ++ rc = SQLITE_CORRUPT_PGNO(newPgno); ++ } ++ if( rc ){ ++ pCur->pPage = pCur->apPage[--pCur->iPage]; ++ } ++ return rc; + } + + #ifdef SQLITE_DEBUG +@@ -69681,7 +77080,7 @@ static int moveToRoot(BtCursor *pCur){ + while( --pCur->iPage ){ + releasePageNotNull(pCur->apPage[pCur->iPage]); + } +- pCur->pPage = pCur->apPage[0]; ++ pRoot = pCur->pPage = pCur->apPage[0]; + goto skip_init; + } + }else if( pCur->pgnoRoot==0 ){ +@@ -69696,8 +77095,8 @@ static int moveToRoot(BtCursor *pCur){ + } + sqlite3BtreeClearCursor(pCur); + } +- rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage, +- 0, pCur->curPagerFlags); ++ rc = getAndInitPage(pCur->pBt, pCur->pgnoRoot, &pCur->pPage, ++ pCur->curPagerFlags); + if( rc!=SQLITE_OK ){ + pCur->eState = CURSOR_INVALID; + return rc; +@@ -69706,7 +77105,7 @@ static int moveToRoot(BtCursor *pCur){ + pCur->curIntKey = pCur->pPage->intKey; + } + pRoot = pCur->pPage; +- assert( pRoot->pgno==pCur->pgnoRoot ); ++ assert( pRoot->pgno==pCur->pgnoRoot || CORRUPT_DB ); + + /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor + ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is +@@ -69728,7 +77127,6 @@ skip_init: + pCur->info.nSize = 0; + pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); + +- pRoot = pCur->pPage; + if( pRoot->nCell>0 ){ + pCur->eState = CURSOR_VALID; + }else if( !pRoot->leaf ){ +@@ -69810,40 +77208,36 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ + *pRes = 0; + rc = moveToLeftmost(pCur); + }else if( rc==SQLITE_EMPTY ){ +- assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); ++ assert( pCur->pgnoRoot==0 || (pCur->pPage!=0 && pCur->pPage->nCell==0) ); + *pRes = 1; + rc = SQLITE_OK; + } + return rc; + } + ++#ifdef SQLITE_DEBUG ++/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that ++** this flags are true for a consistent database. ++** ++** This routine is is called from within assert() statements only. ++** It is an internal verification routine and does not appear in production ++** builds. ++*/ ++static int cursorIsAtLastEntry(BtCursor *pCur){ ++ int ii; ++ for(ii=0; iiiPage; ii++){ ++ if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0; ++ } ++ return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0; ++} ++#endif ++ + /* Move the cursor to the last entry in the table. Return SQLITE_OK + ** on success. Set *pRes to 0 if the cursor actually points to something + ** or set *pRes to 1 if the table is empty. + */ +-SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ +- int rc; +- +- assert( cursorOwnsBtShared(pCur) ); +- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); +- +- /* If the cursor already points to the last entry, this is a no-op. */ +- if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ +-#ifdef SQLITE_DEBUG +- /* This block serves to assert() that the cursor really does point +- ** to the last entry in the b-tree. */ +- int ii; +- for(ii=0; iiiPage; ii++){ +- assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); +- } +- assert( pCur->ix==pCur->pPage->nCell-1 ); +- assert( pCur->pPage->leaf ); +-#endif +- *pRes = 0; +- return SQLITE_OK; +- } +- +- rc = moveToRoot(pCur); ++static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){ ++ int rc = moveToRoot(pCur); + if( rc==SQLITE_OK ){ + assert( pCur->eState==CURSOR_VALID ); + *pRes = 0; +@@ -69860,13 +77254,250 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ + } + return rc; + } ++SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ ++ assert( cursorOwnsBtShared(pCur) ); ++ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); ++ ++ /* If the cursor already points to the last entry, this is a no-op. */ ++ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ ++ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); ++ *pRes = 0; ++ return SQLITE_OK; ++ } ++ return btreeLast(pCur, pRes); ++} ++ ++/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY) ++** table near the key intKey. Return a success code. ++** ++** If an exact match is not found, then the cursor is always ++** left pointing at a leaf page which would hold the entry if it ++** were present. The cursor might point to an entry that comes ++** before or after the key. ++** ++** An integer is written into *pRes which is the result of ++** comparing the key with the entry to which the cursor is ++** pointing. The meaning of the integer written into ++** *pRes is as follows: ++** ++** *pRes<0 The cursor is left pointing at an entry that ++** is smaller than intKey or if the table is empty ++** and the cursor is therefore left point to nothing. ++** ++** *pRes==0 The cursor is left pointing at an entry that ++** exactly matches intKey. ++** ++** *pRes>0 The cursor is left pointing at an entry that ++** is larger than intKey. ++*/ ++SQLITE_PRIVATE int sqlite3BtreeTableMoveto( ++ BtCursor *pCur, /* The cursor to be moved */ ++ i64 intKey, /* The table key */ ++ int biasRight, /* If true, bias the search to the high end */ ++ int *pRes /* Write search results here */ ++){ ++ int rc; ++ ++ assert( cursorOwnsBtShared(pCur) ); ++ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); ++ assert( pRes ); ++ assert( pCur->pKeyInfo==0 ); ++ assert( pCur->eState!=CURSOR_VALID || pCur->curIntKey!=0 ); ++ ++ /* If the cursor is already positioned at the point we are trying ++ ** to move to, then just return without doing any work */ ++ if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ ++ if( pCur->info.nKey==intKey ){ ++ *pRes = 0; ++ return SQLITE_OK; ++ } ++ if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ ++ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); ++ *pRes = -1; ++ return SQLITE_OK; ++ } ++ /* If the requested key is one more than the previous key, then ++ ** try to get there using sqlite3BtreeNext() rather than a full ++ ** binary search. This is an optimization only. The correct answer ++ ** is still obtained without this case, only a little more slowly. */ ++ if( pCur->info.nKey+1==intKey ){ ++ *pRes = 0; ++ rc = sqlite3BtreeNext(pCur, 0); ++ if( rc==SQLITE_OK ){ ++ getCellInfo(pCur); ++ if( pCur->info.nKey==intKey ){ ++ return SQLITE_OK; ++ } ++ }else if( rc!=SQLITE_DONE ){ ++ return rc; ++ } ++ } ++ } ++ } ++ ++#ifdef SQLITE_DEBUG ++ pCur->pBtree->nSeek++; /* Performance measurement during testing */ ++#endif ++ ++ rc = moveToRoot(pCur); ++ if( rc ){ ++ if( rc==SQLITE_EMPTY ){ ++ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); ++ *pRes = -1; ++ return SQLITE_OK; ++ } ++ return rc; ++ } ++ assert( pCur->pPage ); ++ assert( pCur->pPage->isInit ); ++ assert( pCur->eState==CURSOR_VALID ); ++ assert( pCur->pPage->nCell > 0 ); ++ assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); ++ assert( pCur->curIntKey ); ++ ++ for(;;){ ++ int lwr, upr, idx, c; ++ Pgno chldPg; ++ MemPage *pPage = pCur->pPage; ++ u8 *pCell; /* Pointer to current cell in pPage */ ++ ++ /* pPage->nCell must be greater than zero. If this is the root-page ++ ** the cursor would have been INVALID above and this for(;;) loop ++ ** not run. If this is not the root-page, then the moveToChild() routine ++ ** would have already detected db corruption. Similarly, pPage must ++ ** be the right kind (index or table) of b-tree page. Otherwise ++ ** a moveToChild() or moveToRoot() call would have detected corruption. */ ++ assert( pPage->nCell>0 ); ++ assert( pPage->intKey ); ++ lwr = 0; ++ upr = pPage->nCell-1; ++ assert( biasRight==0 || biasRight==1 ); ++ idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ ++ for(;;){ ++ i64 nCellKey; ++ pCell = findCellPastPtr(pPage, idx); ++ if( pPage->intKeyLeaf ){ ++ while( 0x80 <= *(pCell++) ){ ++ if( pCell>=pPage->aDataEnd ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } ++ } ++ } ++ getVarint(pCell, (u64*)&nCellKey); ++ if( nCellKeyupr ){ c = -1; break; } ++ }else if( nCellKey>intKey ){ ++ upr = idx-1; ++ if( lwr>upr ){ c = +1; break; } ++ }else{ ++ assert( nCellKey==intKey ); ++ pCur->ix = (u16)idx; ++ if( !pPage->leaf ){ ++ lwr = idx; ++ goto moveto_table_next_layer; ++ }else{ ++ pCur->curFlags |= BTCF_ValidNKey; ++ pCur->info.nKey = nCellKey; ++ pCur->info.nSize = 0; ++ *pRes = 0; ++ return SQLITE_OK; ++ } ++ } ++ assert( lwr+upr>=0 ); ++ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ ++ } ++ assert( lwr==upr+1 || !pPage->leaf ); ++ assert( pPage->isInit ); ++ if( pPage->leaf ){ ++ assert( pCur->ixpPage->nCell ); ++ pCur->ix = (u16)idx; ++ *pRes = c; ++ rc = SQLITE_OK; ++ goto moveto_table_finish; ++ } ++moveto_table_next_layer: ++ if( lwr>=pPage->nCell ){ ++ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); ++ }else{ ++ chldPg = get4byte(findCell(pPage, lwr)); ++ } ++ pCur->ix = (u16)lwr; ++ rc = moveToChild(pCur, chldPg); ++ if( rc ) break; ++ } ++moveto_table_finish: ++ pCur->info.nSize = 0; ++ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); ++ return rc; ++} + +-/* Move the cursor so that it points to an entry near the key +-** specified by pIdxKey or intKey. Return a success code. ++/* ++** Compare the "idx"-th cell on the page the cursor pCur is currently ++** pointing to to pIdxKey using xRecordCompare. Return negative or ++** zero if the cell is less than or equal pIdxKey. Return positive ++** if unknown. ++** ++** Return value negative: Cell at pCur[idx] less than pIdxKey ++** ++** Return value is zero: Cell at pCur[idx] equals pIdxKey + ** +-** For INTKEY tables, the intKey parameter is used. pIdxKey +-** must be NULL. For index tables, pIdxKey is used and intKey +-** is ignored. ++** Return value positive: Nothing is known about the relationship ++** of the cell at pCur[idx] and pIdxKey. ++** ++** This routine is part of an optimization. It is always safe to return ++** a positive value as that will cause the optimization to be skipped. ++*/ ++static int indexCellCompare( ++ BtCursor *pCur, ++ int idx, ++ UnpackedRecord *pIdxKey, ++ RecordCompare xRecordCompare ++){ ++ MemPage *pPage = pCur->pPage; ++ int c; ++ int nCell; /* Size of the pCell cell in bytes */ ++ u8 *pCell = findCellPastPtr(pPage, idx); ++ ++ nCell = pCell[0]; ++ if( nCell<=pPage->max1bytePayload ){ ++ /* This branch runs if the record-size field of the cell is a ++ ** single byte varint and the record fits entirely on the main ++ ** b-tree page. */ ++ testcase( pCell+nCell+1==pPage->aDataEnd ); ++ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); ++ }else if( !(pCell[1] & 0x80) ++ && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal ++ ){ ++ /* The record-size field is a 2 byte varint and the record ++ ** fits entirely on the main b-tree page. */ ++ testcase( pCell+nCell+2==pPage->aDataEnd ); ++ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); ++ }else{ ++ /* If the record extends into overflow pages, do not attempt ++ ** the optimization. */ ++ c = 99; ++ } ++ return c; ++} ++ ++/* ++** Return true (non-zero) if pCur is current pointing to the last ++** page of a table. ++*/ ++static int cursorOnLastPage(BtCursor *pCur){ ++ int i; ++ assert( pCur->eState==CURSOR_VALID ); ++ for(i=0; iiPage; i++){ ++ MemPage *pPage = pCur->apPage[i]; ++ if( pCur->aiIdx[i]nCell ) return 0; ++ } ++ return 1; ++} ++ ++/* Move the cursor so that it points to an entry in an index table ++** near the key pIdxKey. Return a success code. + ** + ** If an exact match is not found, then the cursor is always + ** left pointing at a leaf page which would hold the entry if it +@@ -69879,23 +77510,21 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ + ** *pRes is as follows: + ** + ** *pRes<0 The cursor is left pointing at an entry that +-** is smaller than intKey/pIdxKey or if the table is empty ++** is smaller than pIdxKey or if the table is empty + ** and the cursor is therefore left point to nothing. + ** + ** *pRes==0 The cursor is left pointing at an entry that +-** exactly matches intKey/pIdxKey. ++** exactly matches pIdxKey. + ** + ** *pRes>0 The cursor is left pointing at an entry that +-** is larger than intKey/pIdxKey. ++** is larger than pIdxKey. + ** +-** For index tables, the pIdxKey->eqSeen field is set to 1 if there ++** The pIdxKey->eqSeen field is set to 1 if there + ** exists an entry in the table that exactly matches pIdxKey. + */ +-SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( ++SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( + BtCursor *pCur, /* The cursor to be moved */ + UnpackedRecord *pIdxKey, /* Unpacked index key */ +- i64 intKey, /* The table key */ +- int biasRight, /* If true, bias the search to the high end */ + int *pRes /* Write search results here */ + ){ + int rc; +@@ -69904,53 +77533,54 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( + assert( cursorOwnsBtShared(pCur) ); + assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); +- assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); +- assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) ); ++ assert( pCur->pKeyInfo!=0 ); + +- /* If the cursor is already positioned at the point we are trying +- ** to move to, then just return without doing any work */ +- if( pIdxKey==0 +- && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ++#ifdef SQLITE_DEBUG ++ pCur->pBtree->nSeek++; /* Performance measurement during testing */ ++#endif ++ ++ xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); ++ pIdxKey->errCode = 0; ++ assert( pIdxKey->default_rc==1 ++ || pIdxKey->default_rc==0 ++ || pIdxKey->default_rc==-1 ++ ); ++ ++ ++ /* Check to see if we can skip a lot of work. Two cases: ++ ** ++ ** (1) If the cursor is already pointing to the very last cell ++ ** in the table and the pIdxKey search key is greater than or ++ ** equal to that last cell, then no movement is required. ++ ** ++ ** (2) If the cursor is on the last page of the table and the first ++ ** cell on that last page is less than or equal to the pIdxKey ++ ** search key, then we can start the search on the current page ++ ** without needing to go back to root. ++ */ ++ if( pCur->eState==CURSOR_VALID ++ && pCur->pPage->leaf ++ && cursorOnLastPage(pCur) + ){ +- if( pCur->info.nKey==intKey ){ +- *pRes = 0; +- return SQLITE_OK; ++ int c; ++ if( pCur->ix==pCur->pPage->nCell-1 ++ && (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0 ++ && pIdxKey->errCode==SQLITE_OK ++ ){ ++ *pRes = c; ++ return SQLITE_OK; /* Cursor already pointing at the correct spot */ + } +- if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ +- *pRes = -1; +- return SQLITE_OK; +- } +- /* If the requested key is one more than the previous key, then +- ** try to get there using sqlite3BtreeNext() rather than a full +- ** binary search. This is an optimization only. The correct answer +- ** is still obtained without this case, only a little more slowely */ +- if( pCur->info.nKey+1==intKey ){ +- *pRes = 0; +- rc = sqlite3BtreeNext(pCur, 0); +- if( rc==SQLITE_OK ){ +- getCellInfo(pCur); +- if( pCur->info.nKey==intKey ){ +- return SQLITE_OK; +- } +- }else if( rc==SQLITE_DONE ){ +- rc = SQLITE_OK; +- }else{ +- return rc; +- } ++ if( pCur->iPage>0 ++ && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0 ++ && pIdxKey->errCode==SQLITE_OK ++ ){ ++ pCur->curFlags &= ~(BTCF_ValidOvfl|BTCF_AtLast); ++ if( !pCur->pPage->isInit ){ ++ return SQLITE_CORRUPT_BKPT; + } ++ goto bypass_moveto_root; /* Start search on the current page */ + } +- } +- +- if( pIdxKey ){ +- xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); +- pIdxKey->errCode = 0; +- assert( pIdxKey->default_rc==1 +- || pIdxKey->default_rc==0 +- || pIdxKey->default_rc==-1 +- ); +- }else{ +- xRecordCompare = 0; /* All keys are integers */ ++ pIdxKey->errCode = SQLITE_OK; + } + + rc = moveToRoot(pCur); +@@ -69962,12 +77592,14 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( + } + return rc; + } ++ ++bypass_moveto_root: + assert( pCur->pPage ); + assert( pCur->pPage->isInit ); + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->pPage->nCell > 0 ); +- assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); +- assert( pCur->curIntKey || pIdxKey ); ++ assert( pCur->curIntKey==0 ); ++ assert( pIdxKey!=0 ); + for(;;){ + int lwr, upr, idx, c; + Pgno chldPg; +@@ -69981,154 +77613,140 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( + ** be the right kind (index or table) of b-tree page. Otherwise + ** a moveToChild() or moveToRoot() call would have detected corruption. */ + assert( pPage->nCell>0 ); +- assert( pPage->intKey==(pIdxKey==0) ); ++ assert( pPage->intKey==0 ); + lwr = 0; + upr = pPage->nCell-1; +- assert( biasRight==0 || biasRight==1 ); +- idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ +- pCur->ix = (u16)idx; +- if( xRecordCompare==0 ){ +- for(;;){ +- i64 nCellKey; +- pCell = findCellPastPtr(pPage, idx); +- if( pPage->intKeyLeaf ){ +- while( 0x80 <= *(pCell++) ){ +- if( pCell>=pPage->aDataEnd ){ +- return SQLITE_CORRUPT_PAGE(pPage); +- } +- } +- } +- getVarint(pCell, (u64*)&nCellKey); +- if( nCellKeyupr ){ c = -1; break; } +- }else if( nCellKey>intKey ){ +- upr = idx-1; +- if( lwr>upr ){ c = +1; break; } +- }else{ +- assert( nCellKey==intKey ); +- pCur->ix = (u16)idx; +- if( !pPage->leaf ){ +- lwr = idx; +- goto moveto_next_layer; +- }else{ +- pCur->curFlags |= BTCF_ValidNKey; +- pCur->info.nKey = nCellKey; +- pCur->info.nSize = 0; +- *pRes = 0; +- return SQLITE_OK; +- } ++ idx = upr>>1; /* idx = (lwr+upr)/2; */ ++ for(;;){ ++ int nCell; /* Size of the pCell cell in bytes */ ++ pCell = findCellPastPtr(pPage, idx); ++ ++ /* The maximum supported page-size is 65536 bytes. This means that ++ ** the maximum number of record bytes stored on an index B-Tree ++ ** page is less than 16384 bytes and may be stored as a 2-byte ++ ** varint. This information is used to attempt to avoid parsing ++ ** the entire cell by checking for the cases where the record is ++ ** stored entirely within the b-tree page by inspecting the first ++ ** 2 bytes of the cell. ++ */ ++ nCell = pCell[0]; ++ if( nCell<=pPage->max1bytePayload ){ ++ /* This branch runs if the record-size field of the cell is a ++ ** single byte varint and the record fits entirely on the main ++ ** b-tree page. */ ++ testcase( pCell+nCell+1==pPage->aDataEnd ); ++ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); ++ }else if( !(pCell[1] & 0x80) ++ && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal ++ ){ ++ /* The record-size field is a 2 byte varint and the record ++ ** fits entirely on the main b-tree page. */ ++ testcase( pCell+nCell+2==pPage->aDataEnd ); ++ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); ++ }else{ ++ /* The record flows over onto one or more overflow pages. In ++ ** this case the whole cell needs to be parsed, a buffer allocated ++ ** and accessPayload() used to retrieve the record into the ++ ** buffer before VdbeRecordCompare() can be called. ++ ** ++ ** If the record is corrupt, the xRecordCompare routine may read ++ ** up to two varints past the end of the buffer. An extra 18 ++ ** bytes of padding is allocated at the end of the buffer in ++ ** case this happens. */ ++ void *pCellKey; ++ u8 * const pCellBody = pCell - pPage->childPtrSize; ++ const int nOverrun = 18; /* Size of the overrun padding */ ++ pPage->xParseCell(pPage, pCellBody, &pCur->info); ++ nCell = (int)pCur->info.nKey; ++ testcase( nCell<0 ); /* True if key size is 2^32 or more */ ++ testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ ++ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ ++ testcase( nCell==2 ); /* Minimum legal index key size */ ++ if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ ++ rc = SQLITE_CORRUPT_PAGE(pPage); ++ goto moveto_index_finish; ++ } ++ pCellKey = sqlite3Malloc( (u64)nCell+(u64)nOverrun ); ++ if( pCellKey==0 ){ ++ rc = SQLITE_NOMEM_BKPT; ++ goto moveto_index_finish; + } +- assert( lwr+upr>=0 ); +- idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ +- } +- }else{ +- for(;;){ +- int nCell; /* Size of the pCell cell in bytes */ +- pCell = findCellPastPtr(pPage, idx); +- +- /* The maximum supported page-size is 65536 bytes. This means that +- ** the maximum number of record bytes stored on an index B-Tree +- ** page is less than 16384 bytes and may be stored as a 2-byte +- ** varint. This information is used to attempt to avoid parsing +- ** the entire cell by checking for the cases where the record is +- ** stored entirely within the b-tree page by inspecting the first +- ** 2 bytes of the cell. +- */ +- nCell = pCell[0]; +- if( nCell<=pPage->max1bytePayload ){ +- /* This branch runs if the record-size field of the cell is a +- ** single byte varint and the record fits entirely on the main +- ** b-tree page. */ +- testcase( pCell+nCell+1==pPage->aDataEnd ); +- c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); +- }else if( !(pCell[1] & 0x80) +- && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal +- ){ +- /* The record-size field is a 2 byte varint and the record +- ** fits entirely on the main b-tree page. */ +- testcase( pCell+nCell+2==pPage->aDataEnd ); +- c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); +- }else{ +- /* The record flows over onto one or more overflow pages. In +- ** this case the whole cell needs to be parsed, a buffer allocated +- ** and accessPayload() used to retrieve the record into the +- ** buffer before VdbeRecordCompare() can be called. +- ** +- ** If the record is corrupt, the xRecordCompare routine may read +- ** up to two varints past the end of the buffer. An extra 18 +- ** bytes of padding is allocated at the end of the buffer in +- ** case this happens. */ +- void *pCellKey; +- u8 * const pCellBody = pCell - pPage->childPtrSize; +- const int nOverrun = 18; /* Size of the overrun padding */ +- pPage->xParseCell(pPage, pCellBody, &pCur->info); +- nCell = (int)pCur->info.nKey; +- testcase( nCell<0 ); /* True if key size is 2^32 or more */ +- testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ +- testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ +- testcase( nCell==2 ); /* Minimum legal index key size */ +- if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ +- rc = SQLITE_CORRUPT_PAGE(pPage); +- goto moveto_finish; +- } +- pCellKey = sqlite3Malloc( nCell+nOverrun ); +- if( pCellKey==0 ){ +- rc = SQLITE_NOMEM_BKPT; +- goto moveto_finish; +- } +- pCur->ix = (u16)idx; +- rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); +- memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ +- pCur->curFlags &= ~BTCF_ValidOvfl; +- if( rc ){ +- sqlite3_free(pCellKey); +- goto moveto_finish; +- } +- c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); ++ pCur->ix = (u16)idx; ++ rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); ++ memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ ++ pCur->curFlags &= ~BTCF_ValidOvfl; ++ if( rc ){ + sqlite3_free(pCellKey); ++ goto moveto_index_finish; + } +- assert( +- (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) +- && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) +- ); +- if( c<0 ){ +- lwr = idx+1; +- }else if( c>0 ){ +- upr = idx-1; +- }else{ +- assert( c==0 ); +- *pRes = 0; +- rc = SQLITE_OK; +- pCur->ix = (u16)idx; +- if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; +- goto moveto_finish; +- } +- if( lwr>upr ) break; +- assert( lwr+upr>=0 ); +- idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ ++ c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); ++ sqlite3_free(pCellKey); + } ++ assert( ++ (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) ++ && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) ++ ); ++ if( c<0 ){ ++ lwr = idx+1; ++ }else if( c>0 ){ ++ upr = idx-1; ++ }else{ ++ assert( c==0 ); ++ *pRes = 0; ++ rc = SQLITE_OK; ++ pCur->ix = (u16)idx; ++ if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; ++ goto moveto_index_finish; ++ } ++ if( lwr>upr ) break; ++ assert( lwr+upr>=0 ); ++ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ + } + assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); + assert( pPage->isInit ); + if( pPage->leaf ){ +- assert( pCur->ixpPage->nCell ); ++ assert( pCur->ixpPage->nCell || CORRUPT_DB ); + pCur->ix = (u16)idx; + *pRes = c; + rc = SQLITE_OK; +- goto moveto_finish; ++ goto moveto_index_finish; + } +-moveto_next_layer: + if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); + }else{ + chldPg = get4byte(findCell(pPage, lwr)); + } +- pCur->ix = (u16)lwr; +- rc = moveToChild(pCur, chldPg); +- if( rc ) break; +- } +-moveto_finish: ++ ++ /* This block is similar to an in-lined version of: ++ ** ++ ** pCur->ix = (u16)lwr; ++ ** rc = moveToChild(pCur, chldPg); ++ ** if( rc ) break; ++ */ ++ pCur->info.nSize = 0; ++ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); ++ if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ ++ return SQLITE_CORRUPT_BKPT; ++ } ++ pCur->aiIdx[pCur->iPage] = (u16)lwr; ++ pCur->apPage[pCur->iPage] = pCur->pPage; ++ pCur->ix = 0; ++ pCur->iPage++; ++ rc = getAndInitPage(pCur->pBt, chldPg, &pCur->pPage, pCur->curPagerFlags); ++ if( rc==SQLITE_OK ++ && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) ++ ){ ++ releasePage(pCur->pPage); ++ rc = SQLITE_CORRUPT_PGNO(chldPg); ++ } ++ if( rc ){ ++ pCur->pPage = pCur->apPage[--pCur->iPage]; ++ break; ++ } ++ /* ++ ***** End of in-lined moveToChild() call */ ++ } ++moveto_index_finish: + pCur->info.nSize = 0; + assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); + return rc; +@@ -70162,10 +77780,10 @@ SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){ + assert( cursorOwnsBtShared(pCur) ); + assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + +- /* Currently this interface is only called by the OP_IfSmaller +- ** opcode, and it that case the cursor will always be valid and +- ** will always point to a leaf node. */ +- if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; ++ /* Currently this interface is only called by the OP_IfSizeBetween ++ ** opcode and the OP_Count opcode with P3=1. In either case, ++ ** the cursor will always be valid unless the btree is empty. */ ++ if( pCur->eState!=CURSOR_VALID ) return 0; + if( NEVER(pCur->pPage->leaf==0) ) return -1; + + n = pCur->pPage->nCell; +@@ -70218,27 +77836,11 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ + + pPage = pCur->pPage; + idx = ++pCur->ix; ++ if( sqlite3FaultSim(412) ) pPage->isInit = 0; + if( !pPage->isInit ){ +- /* The only known way for this to happen is for there to be a +- ** recursive SQL function that does a DELETE operation as part of a +- ** SELECT which deletes content out from under an active cursor +- ** in a corrupt database file where the table being DELETE-ed from +- ** has pages in common with the table being queried. See TH3 +- ** module cov1/btree78.test testcase 220 (2018-06-08) for an +- ** example. */ + return SQLITE_CORRUPT_BKPT; + } + +- /* If the database file is corrupt, it is possible for the value of idx +- ** to be invalid here. This can only occur if a second cursor modifies +- ** the page while cursor pCur is holding a reference to it. Which can +- ** only happen if the database is corrupt in such a way as to link the +- ** page into more than one b-tree structure. +- ** +- ** Update 2019-12-23: appears to long longer be possible after the +- ** addition of anotherValidCursor() condition on balance_deeper(). */ +- harmless( idx>pPage->nCell ); +- + if( idx>=pPage->nCell ){ + if( !pPage->leaf ){ + rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); +@@ -70327,7 +77929,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ + } + + pPage = pCur->pPage; +- assert( pPage->isInit ); ++ if( sqlite3FaultSim(412) ) pPage->isInit = 0; ++ if( !pPage->isInit ){ ++ return SQLITE_CORRUPT_BKPT; ++ } + if( !pPage->leaf ){ + int idx = pCur->ix; + rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); +@@ -70411,8 +78016,8 @@ static int allocateBtreePage( + assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) ); + pPage1 = pBt->pPage1; + mxPage = btreePagecount(pBt); +- /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 +- ** stores stores the total number of pages on the freelist. */ ++ /* EVIDENCE-OF: R-21003-45125 The 4-byte big-endian integer at offset 36 ++ ** stores the total number of pages on the freelist. */ + n = get4byte(&pPage1->aData[36]); + testcase( n==mxPage-1 ); + if( n>=mxPage ){ +@@ -70498,7 +78103,7 @@ static int allocateBtreePage( + memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); + *ppPage = pTrunk; + pTrunk = 0; +- TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); ++ TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1)); + }else if( k>(u32)(pBt->usableSize/4 - 2) ){ + /* Value of k is out of range. Database corruption */ + rc = SQLITE_CORRUPT_PGNO(iTrunk); +@@ -70564,7 +78169,7 @@ static int allocateBtreePage( + } + } + pTrunk = 0; +- TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); ++ TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1)); + #endif + }else if( k>0 ){ + /* Extract a leaf from the trunk */ +@@ -70599,7 +78204,7 @@ static int allocateBtreePage( + + iPage = get4byte(&aData[8+closest*4]); + testcase( iPage==mxPage ); +- if( iPage>mxPage ){ ++ if( iPage>mxPage || iPage<2 ){ + rc = SQLITE_CORRUPT_PGNO(iTrunk); + goto end_allocate_page; + } +@@ -70609,8 +78214,8 @@ static int allocateBtreePage( + ){ + int noContent; + *pPgno = iPage; +- TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" +- ": %d more free pages\n", ++ TRACE(("ALLOCATE: %u was leaf %u of %u on trunk %u" ++ ": %u more free pages\n", + *pPgno, closest+1, k, pTrunk->pgno, n-1)); + rc = sqlite3PagerWrite(pTrunk->pDbPage); + if( rc ) goto end_allocate_page; +@@ -70666,7 +78271,7 @@ static int allocateBtreePage( + ** becomes a new pointer-map page, the second is used by the caller. + */ + MemPage *pPg = 0; +- TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); ++ TRACE(("ALLOCATE: %u from end of file (pointer-map page)\n", pBt->nPage)); + assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); + rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent); + if( rc==SQLITE_OK ){ +@@ -70689,7 +78294,7 @@ static int allocateBtreePage( + releasePage(*ppPage); + *ppPage = 0; + } +- TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); ++ TRACE(("ALLOCATE: %u from end of file\n", *pPgno)); + } + + assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) ); +@@ -70757,7 +78362,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ + /* If the database supports auto-vacuum, write an entry in the pointer-map + ** to indicate that the page is free. + */ +- if( ISAUTOVACUUM ){ ++ if( ISAUTOVACUUM(pBt) ){ + ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); + if( rc ) goto freepage_out; + } +@@ -70817,7 +78422,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ + } + rc = btreeSetHasContent(pBt, iPage); + } +- TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); ++ TRACE(("FREE-PAGE: %u leaf on trunk page %u\n",pPage->pgno,pTrunk->pgno)); + goto freepage_out; + } + } +@@ -70838,7 +78443,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ + put4byte(pPage->aData, iTrunk); + put4byte(&pPage->aData[4], 0); + put4byte(&pPage1->aData[32], iPage); +- TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); ++ TRACE(("FREE-PAGE: %u new trunk page replacing %u\n", pPage->pgno, iTrunk)); + + freepage_out: + if( pPage ){ +@@ -70855,10 +78460,9 @@ static void freePage(MemPage *pPage, int *pRC){ + } + + /* +-** Free any overflow pages associated with the given Cell. Store +-** size information about the cell in pInfo. ++** Free the overflow pages associated with the given Cell. + */ +-static int clearCell( ++static SQLITE_NOINLINE int clearCellOverflow( + MemPage *pPage, /* The page that contains the Cell */ + unsigned char *pCell, /* First byte of the Cell */ + CellInfo *pInfo /* Size information about the cell */ +@@ -70870,10 +78474,7 @@ static int clearCell( + u32 ovflPageSize; + + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); +- pPage->xParseCell(pPage, pCell, pInfo); +- if( pInfo->nLocal==pInfo->nPayload ){ +- return SQLITE_OK; /* No overflow pages. Return without doing anything */ +- } ++ assert( pInfo->nLocal!=pInfo->nPayload ); + testcase( pCell + pInfo->nSize == pPage->aDataEnd ); + testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd ); + if( pCell + pInfo->nSize > pPage->aDataEnd ){ +@@ -70929,6 +78530,21 @@ static int clearCell( + return SQLITE_OK; + } + ++/* Call xParseCell to compute the size of a cell. If the cell contains ++** overflow, then invoke cellClearOverflow to clear out that overflow. ++** Store the result code (SQLITE_OK or some error code) in rc. ++** ++** Implemented as macro to force inlining for performance. ++*/ ++#define BTREE_CLEAR_CELL(rc, pPage, pCell, sInfo) \ ++ pPage->xParseCell(pPage, pCell, &sInfo); \ ++ if( sInfo.nLocal!=sInfo.nPayload ){ \ ++ rc = clearCellOverflow(pPage, pCell, &sInfo); \ ++ }else{ \ ++ rc = SQLITE_OK; \ ++ } ++ ++ + /* + ** Create the byte sequence used to represent a cell on page pPage + ** and write that byte sequence into pCell[]. Overflow pages are +@@ -70989,7 +78605,10 @@ static int fillInCell( + n = nHeader + nPayload; + testcase( n==3 ); + testcase( n==4 ); +- if( n<4 ) n = 4; ++ if( n<4 ){ ++ n = 4; ++ pPayload[nPayload] = 0; ++ } + *pnSize = n; + assert( nSrc<=nPayload ); + testcase( nSrc=0 && idxnCell ); ++ assert( idx>=0 ); ++ assert( idxnCell ); + assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->nFree>=0 ); + data = pPage->aData; + ptr = &pPage->aCellIdx[2*idx]; ++ assert( pPage->pBt->usableSize > (u32)(ptr-data) ); + pc = get2byte(ptr); + hdr = pPage->hdrOffset; +- testcase( pc==get2byte(&data[hdr+5]) ); ++ testcase( pc==(u32)get2byte(&data[hdr+5]) ); + testcase( pc+sz==pPage->pBt->usableSize ); + if( pc+sz > pPage->pBt->usableSize ){ + *pRC = SQLITE_CORRUPT_BKPT; +@@ -71185,23 +78806,27 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ + ** Allocating a new entry in pPage->aCell[] implies that + ** pPage->nOverflow is incremented. + ** +-** *pRC must be SQLITE_OK when this routine is called. ++** The insertCellFast() routine below works exactly the same as ++** insertCell() except that it lacks the pTemp and iChild parameters ++** which are assumed zero. Other than that, the two routines are the ++** same. ++** ++** Fixes or enhancements to this routine should be reflected in ++** insertCellFast()! + */ +-static void insertCell( ++static int insertCell( + MemPage *pPage, /* Page into which we are copying */ + int i, /* New cell becomes the i-th cell of the page */ + u8 *pCell, /* Content of the new cell */ + int sz, /* Bytes of content in pCell */ + u8 *pTemp, /* Temp storage space for pCell, if needed */ +- Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ +- int *pRC /* Read and write return code from here */ ++ Pgno iChild /* If non-zero, replace first 4 bytes with this value */ + ){ + int idx = 0; /* Where to write new cell content in data[] */ + int j; /* Loop counter */ + u8 *data; /* The content of the whole page */ + u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ + +- assert( *pRC==SQLITE_OK ); + assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); + assert( MX_CELL(pPage->pBt)<=10921 ); + assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); +@@ -71210,14 +78835,103 @@ static void insertCell( + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); + assert( pPage->nFree>=0 ); ++ assert( iChild>0 ); + if( pPage->nOverflow || sz+2>pPage->nFree ){ + if( pTemp ){ + memcpy(pTemp, pCell, sz); + pCell = pTemp; + } +- if( iChild ){ +- put4byte(pCell, iChild); ++ put4byte(pCell, iChild); ++ j = pPage->nOverflow++; ++ /* Comparison against ArraySize-1 since we hold back one extra slot ++ ** as a contingency. In other words, never need more than 3 overflow ++ ** slots but 4 are allocated, just to be safe. */ ++ assert( j < ArraySize(pPage->apOvfl)-1 ); ++ pPage->apOvfl[j] = pCell; ++ pPage->aiOvfl[j] = (u16)i; ++ ++ /* When multiple overflows occur, they are always sequential and in ++ ** sorted order. This invariants arise because multiple overflows can ++ ** only occur when inserting divider cells into the parent page during ++ ** balancing, and the dividers are adjacent and sorted. ++ */ ++ assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */ ++ assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */ ++ }else{ ++ int rc = sqlite3PagerWrite(pPage->pDbPage); ++ if( NEVER(rc!=SQLITE_OK) ){ ++ return rc; ++ } ++ assert( sqlite3PagerIswriteable(pPage->pDbPage) ); ++ data = pPage->aData; ++ assert( &data[pPage->cellOffset]==pPage->aCellIdx ); ++ rc = allocateSpace(pPage, sz, &idx); ++ if( rc ){ return rc; } ++ /* The allocateSpace() routine guarantees the following properties ++ ** if it returns successfully */ ++ assert( idx >= 0 ); ++ assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); ++ assert( idx+sz <= (int)pPage->pBt->usableSize ); ++ pPage->nFree -= (u16)(2 + sz); ++ /* In a corrupt database where an entry in the cell index section of ++ ** a btree page has a value of 3 or less, the pCell value might point ++ ** as many as 4 bytes in front of the start of the aData buffer for ++ ** the source page. Make sure this does not cause problems by not ++ ** reading the first 4 bytes */ ++ memcpy(&data[idx+4], pCell+4, sz-4); ++ put4byte(&data[idx], iChild); ++ pIns = pPage->aCellIdx + i*2; ++ memmove(pIns+2, pIns, 2*(pPage->nCell - i)); ++ put2byte(pIns, idx); ++ pPage->nCell++; ++ /* increment the cell count */ ++ if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++; ++ assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); ++#ifndef SQLITE_OMIT_AUTOVACUUM ++ if( pPage->pBt->autoVacuum ){ ++ int rc2 = SQLITE_OK; ++ /* The cell may contain a pointer to an overflow page. If so, write ++ ** the entry for the overflow page into the pointer map. ++ */ ++ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2); ++ if( rc2 ) return rc2; + } ++#endif ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** This variant of insertCell() assumes that the pTemp and iChild ++** parameters are both zero. Use this variant in sqlite3BtreeInsert() ++** for performance improvement, and also so that this variant is only ++** called from that one place, and is thus inlined, and thus runs must ++** faster. ++** ++** Fixes or enhancements to this routine should be reflected into ++** the insertCell() routine. ++*/ ++static int insertCellFast( ++ MemPage *pPage, /* Page into which we are copying */ ++ int i, /* New cell becomes the i-th cell of the page */ ++ u8 *pCell, /* Content of the new cell */ ++ int sz /* Bytes of content in pCell */ ++){ ++ int idx = 0; /* Where to write new cell content in data[] */ ++ int j; /* Loop counter */ ++ u8 *data; /* The content of the whole page */ ++ u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ ++ ++ assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); ++ assert( MX_CELL(pPage->pBt)<=10921 ); ++ assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); ++ assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) ); ++ assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) ); ++ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); ++ assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); ++ assert( pPage->nFree>=0 ); ++ assert( pPage->nOverflow==0 ); ++ if( sz+2>pPage->nFree ){ + j = pPage->nOverflow++; + /* Comparison against ArraySize-1 since we hold back one extra slot + ** as a contingency. In other words, never need more than 3 overflow +@@ -71236,31 +78950,20 @@ static void insertCell( + }else{ + int rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc!=SQLITE_OK ){ +- *pRC = rc; +- return; ++ return rc; + } + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); + data = pPage->aData; + assert( &data[pPage->cellOffset]==pPage->aCellIdx ); + rc = allocateSpace(pPage, sz, &idx); +- if( rc ){ *pRC = rc; return; } ++ if( rc ){ return rc; } + /* The allocateSpace() routine guarantees the following properties + ** if it returns successfully */ + assert( idx >= 0 ); + assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); + assert( idx+sz <= (int)pPage->pBt->usableSize ); + pPage->nFree -= (u16)(2 + sz); +- if( iChild ){ +- /* In a corrupt database where an entry in the cell index section of +- ** a btree page has a value of 3 or less, the pCell value might point +- ** as many as 4 bytes in front of the start of the aData buffer for +- ** the source page. Make sure this does not cause problems by not +- ** reading the first 4 bytes */ +- memcpy(&data[idx+4], pCell+4, sz-4); +- put4byte(&data[idx], iChild); +- }else{ +- memcpy(&data[idx], pCell, sz); +- } ++ memcpy(&data[idx], pCell, sz); + pIns = pPage->aCellIdx + i*2; + memmove(pIns+2, pIns, 2*(pPage->nCell - i)); + put2byte(pIns, idx); +@@ -71270,13 +78973,16 @@ static void insertCell( + assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); + #ifndef SQLITE_OMIT_AUTOVACUUM + if( pPage->pBt->autoVacuum ){ ++ int rc2 = SQLITE_OK; + /* The cell may contain a pointer to an overflow page. If so, write + ** the entry for the overflow page into the pointer map. + */ +- ptrmapPutOvflPtr(pPage, pPage, pCell, pRC); ++ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2); ++ if( rc2 ) return rc2; + } + #endif + } ++ return SQLITE_OK; + } + + /* +@@ -71377,14 +79083,16 @@ struct CellArray { + ** computed. + */ + static void populateCellCache(CellArray *p, int idx, int N){ ++ MemPage *pRef = p->pRef; ++ u16 *szCell = p->szCell; + assert( idx>=0 && idx+N<=p->nCell ); + while( N>0 ){ + assert( p->apCell[idx]!=0 ); +- if( p->szCell[idx]==0 ){ +- p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]); ++ if( szCell[idx]==0 ){ ++ szCell[idx] = pRef->xCellSize(pRef, p->apCell[idx]); + }else{ + assert( CORRUPT_DB || +- p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) ); ++ szCell[idx]==pRef->xCellSize(pRef, p->apCell[idx]) ); + } + idx++; + N--; +@@ -71438,12 +79146,14 @@ static int rebuildPage( + int k; /* Current slot in pCArray->apEnd[] */ + u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ + ++ assert( nCell>0 ); + assert( i(u32)usableSize) ){ j = 0; } ++ if( j>(u32)usableSize ){ j = 0; } + memcpy(&pTmp[j], &aData[j], usableSize - j); + +- for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kixNx[NB*2-1]>i ); ++ for(k=0; pCArray->ixNx[k]<=i; k++){} + pSrcEnd = pCArray->apEnd[k]; + + pData = pEnd; +@@ -71451,7 +79161,7 @@ static int rebuildPage( + u8 *pCell = pCArray->apCell[i]; + u16 sz = pCArray->szCell[i]; + assert( sz>0 ); +- if( SQLITE_WITHIN(pCell,aData,pEnd) ){ ++ if( SQLITE_WITHIN(pCell,aData+j,pEnd) ){ + if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT; + pCell = &pTmp[pCell - aData]; + }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd +@@ -71464,9 +79174,8 @@ static int rebuildPage( + put2byte(pCellptr, (pData - aData)); + pCellptr += 2; + if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT; +- memcpy(pData, pCell, sz); ++ memmove(pData, pCell, sz); + assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); +- testcase( sz!=pPg->xCellSize(pPg,pCell) ) + i++; + if( i>=iEnd ) break; + if( pCArray->ixNx[k]<=i ){ +@@ -71476,7 +79185,8 @@ static int rebuildPage( + } + + /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ +- pPg->nCell = nCell; ++ assert( nCell < 10922 ); ++ pPg->nCell = (u16)nCell; + pPg->nOverflow = 0; + + put2byte(&aData[hdr+1], 0); +@@ -71507,7 +79217,7 @@ static int rebuildPage( + ** Finally, argument pBegin points to the byte immediately following the + ** end of the space required by this page for the cell-pointer area (for + ** all cells - not just those inserted by the current call). If the content +-** area must be extended to before this point in order to accomodate all ++** area must be extended to before this point in order to accommodate all + ** cells in apCell[], then the cells do not fit and non-zero is returned. + */ + static int pageInsertArray( +@@ -71527,7 +79237,8 @@ static int pageInsertArray( + u8 *pEnd; /* Maximum extent of cell data */ + assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ + if( iEnd<=iFirst ) return 0; +- for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kixNx[NB*2-1]>i ); ++ for(k=0; pCArray->ixNx[k]<=i ; k++){} + pEnd = pCArray->apEnd[k]; + while( 1 /*Exit by break*/ ){ + int sz, rc; +@@ -71585,37 +79296,50 @@ static int pageFreeArray( + u8 * const pEnd = &aData[pPg->pBt->usableSize]; + u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize]; + int nRet = 0; +- int i; ++ int i, j; + int iEnd = iFirst + nCell; +- u8 *pFree = 0; +- int szFree = 0; ++ int nFree = 0; ++ int aOfst[10]; ++ int aAfter[10]; + + for(i=iFirst; iapCell[i]; + if( SQLITE_WITHIN(pCell, pStart, pEnd) ){ + int sz; ++ int iAfter; ++ int iOfst; + /* No need to use cachedCellSize() here. The sizes of all cells that + ** are to be freed have already been computing while deciding which + ** cells need freeing */ + sz = pCArray->szCell[i]; assert( sz>0 ); +- if( pFree!=(pCell + sz) ){ +- if( pFree ){ +- assert( pFree>aData && (pFree - aData)<65536 ); +- freeSpace(pPg, (u16)(pFree - aData), szFree); ++ iOfst = (u16)(pCell - aData); ++ iAfter = iOfst+sz; ++ for(j=0; jpEnd ) return 0; +- }else{ +- pFree = pCell; +- szFree += sz; ++ } ++ if( j>=nFree ){ ++ if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){ ++ for(j=0; jpEnd ) return 0; ++ nFree++; + } + nRet++; + } + } +- if( pFree ){ +- assert( pFree>aData && (pFree - aData)<65536 ); +- freeSpace(pPg, (u16)(pFree - aData), szFree); ++ for(j=0; jpPg->aDataEnd) ) goto editpage_fail; + + /* Add cells to the start of the page */ + if( iNewnCell = nNew; ++ assert( nNew < 10922 ); ++ pPg->nCell = (u16)nNew; + pPg->nOverflow = 0; + + put2byte(&aData[hdr+3], pPg->nCell); +@@ -71731,6 +79460,7 @@ static int editPage( + return SQLITE_OK; + editpage_fail: + /* Unable to edit this page. Rebuild it from scratch instead. */ ++ if( nNew<1 ) return SQLITE_CORRUPT_BKPT; + populateCellCache(pCArray, iNew, nNew); + return rebuildPage(pCArray, iNew, nNew, pPg); + } +@@ -71797,6 +79527,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ + b.szCell = &szCell; + b.apEnd[0] = pPage->aDataEnd; + b.ixNx[0] = 2; ++ b.ixNx[NB*2-1] = 0x7fffffff; + rc = rebuildPage(&b, 0, 1, pNew); + if( NEVER(rc) ){ + releasePage(pNew); +@@ -71808,12 +79539,12 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ + ** with entries for the new page, and any pointer from the + ** cell on the page to an overflow page. If either of these + ** operations fails, the return code is set, but the contents +- ** of the parent page are still manipulated by thh code below. ++ ** of the parent page are still manipulated by the code below. + ** That is Ok, at this point the parent page is guaranteed to + ** be marked as dirty. Returning an error code will cause a + ** rollback, undoing any changes made to the parent page. + */ +- if( ISAUTOVACUUM ){ ++ if( ISAUTOVACUUM(pBt) ){ + ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); + if( szCell>pNew->minLocal ){ + ptrmapPutOvflPtr(pNew, pNew, pCell, &rc); +@@ -71841,8 +79572,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ + + /* Insert the new divider cell into pParent. */ + if( rc==SQLITE_OK ){ +- insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), +- 0, pPage->pgno, &rc); ++ rc = insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), ++ 0, pPage->pgno); + } + + /* Set the right-child pointer of pParent to point to the new page. */ +@@ -71951,7 +79682,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ + /* If this is an auto-vacuum database, update the pointer-map entries + ** for any b-tree or overflow pages that pTo now contains the pointers to. + */ +- if( ISAUTOVACUUM ){ ++ if( ISAUTOVACUUM(pBt) ){ + *pRC = setChildPtrmaps(pTo); + } + } +@@ -72017,7 +79748,7 @@ static int balance_nonroot( + int pageFlags; /* Value of pPage->aData[0] */ + int iSpace1 = 0; /* First unused byte of aSpace1[] */ + int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ +- int szScratch; /* Size of scratch memory requested */ ++ u64 szScratch; /* Size of scratch memory requested */ + MemPage *apOld[NB]; /* pPage and up to two siblings */ + MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ + u8 *pRight; /* Location in parent of right-sibling pointer */ +@@ -72029,13 +79760,12 @@ static int balance_nonroot( + Pgno pgno; /* Temp var to store a page number in */ + u8 abDone[NB+2]; /* True after i'th new page is populated */ + Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */ +- Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */ +- u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */ +- CellArray b; /* Parsed information on cells being balanced */ ++ CellArray b; /* Parsed information on cells being balanced */ + + memset(abDone, 0, sizeof(abDone)); +- b.nCell = 0; +- b.apCell = 0; ++ assert( sizeof(b) - sizeof(b.ixNx) == offsetof(CellArray,ixNx) ); ++ memset(&b, 0, sizeof(b)-sizeof(b.ixNx[0])); ++ b.ixNx[NB*2-1] = 0x7fffffff; + pBt = pParent->pBt; + assert( sqlite3_mutex_held(pBt->mutex) ); + assert( sqlite3PagerIswriteable(pParent->pDbPage) ); +@@ -72086,7 +79816,9 @@ static int balance_nonroot( + } + pgno = get4byte(pRight); + while( 1 ){ +- rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0); ++ if( rc==SQLITE_OK ){ ++ rc = getAndInitPage(pBt, pgno, &apOld[i], 0); ++ } + if( rc ){ + memset(apOld, 0, (i+1)*sizeof(MemPage*)); + goto balance_cleanup; +@@ -72098,6 +79830,7 @@ static int balance_nonroot( + goto balance_cleanup; + } + } ++ nMaxCells += apOld[i]->nCell + ArraySize(pParent->apOvfl); + if( (i--)==0 ) break; + + if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ +@@ -72125,12 +79858,10 @@ static int balance_nonroot( + if( pBt->btsFlags & BTS_FAST_SECURE ){ + int iOff; + ++ /* If the following if() condition is not true, the db is corrupted. ++ ** The call to dropCell() below will detect this. */ + iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); +- if( (iOff+szNew[i])>(int)pBt->usableSize ){ +- rc = SQLITE_CORRUPT_BKPT; +- memset(apOld, 0, (i+1)*sizeof(MemPage*)); +- goto balance_cleanup; +- }else{ ++ if( (iOff+szNew[i])<=(int)pBt->usableSize ){ + memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); + apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; + } +@@ -72141,7 +79872,6 @@ static int balance_nonroot( + + /* Make nMaxCells a multiple of 4 in order to preserve 8-byte + ** alignment */ +- nMaxCells = nOld*(MX_CELL(pBt) + ArraySize(pParent->apOvfl)); + nMaxCells = (nMaxCells + 3)&~3; + + /* +@@ -72194,7 +79924,7 @@ static int balance_nonroot( + ** table-interior, index-leaf, or index-interior). + */ + if( pOld->aData[0]!=apOld[0]->aData[0] ){ +- rc = SQLITE_CORRUPT_BKPT; ++ rc = SQLITE_CORRUPT_PAGE(pOld); + goto balance_cleanup; + } + +@@ -72218,7 +79948,7 @@ static int balance_nonroot( + memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); + if( pOld->nOverflow>0 ){ + if( NEVER(limitaiOvfl[0]) ){ +- rc = SQLITE_CORRUPT_BKPT; ++ rc = SQLITE_CORRUPT_PAGE(pOld); + goto balance_cleanup; + } + limit = pOld->aiOvfl[0]; +@@ -72258,7 +79988,7 @@ static int balance_nonroot( + b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection; + if( !pOld->leaf ){ + assert( leafCorrection==0 ); +- assert( pOld->hdrOffset==0 ); ++ assert( pOld->hdrOffset==0 || CORRUPT_DB ); + /* The right pointer of the child page pOld becomes the left + ** pointer of the divider cell */ + memcpy(b.apCell[b.nCell], &pOld->aData[8], 4); +@@ -72378,15 +80108,17 @@ static int balance_nonroot( + d = r + 1 - leafData; + (void)cachedCellSize(&b, d); + do{ ++ int szR, szD; + assert( d szLeft-(b.szCell[r]+(i==k-1?0:2)))){ ++ && (bBulk || szRight+szD+2 > szLeft-(szR+(i==k-1?0:2)))){ + break; + } +- szRight += b.szCell[d] + 2; +- szLeft -= b.szCell[r] + 2; ++ szRight += szD + 2; ++ szLeft -= szR + 2; + cntNew[i-1] = r; + r--; + d--; +@@ -72399,7 +80131,7 @@ static int balance_nonroot( + } + } + +- /* Sanity check: For a non-corrupt database file one of the follwing ++ /* Sanity check: For a non-corrupt database file one of the following + ** must be true: + ** (1) We found one or more cells (cntNew[0])>0), or + ** (2) pPage is a virtual root page. A virtual root page is when +@@ -72407,7 +80139,7 @@ static int balance_nonroot( + ** that page. + */ + assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB); +- TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n", ++ TRACE(("BALANCE: old: %u(nc=%u) %u(nc=%u) %u(nc=%u)\n", + apOld[0]->pgno, apOld[0]->nCell, + nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0, + nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0 +@@ -72424,6 +80156,11 @@ static int balance_nonroot( + apOld[i] = 0; + rc = sqlite3PagerWrite(pNew->pDbPage); + nNew++; ++ if( sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) ++ && rc==SQLITE_OK ++ ){ ++ rc = SQLITE_CORRUPT_BKPT; ++ } + if( rc ) goto balance_cleanup; + }else{ + assert( i>0 ); +@@ -72435,7 +80172,7 @@ static int balance_nonroot( + cntOld[i] = b.nCell; + + /* Set the pointer-map entry for the new sibling page. */ +- if( ISAUTOVACUUM ){ ++ if( ISAUTOVACUUM(pBt) ){ + ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); + if( rc!=SQLITE_OK ){ + goto balance_cleanup; +@@ -72450,47 +80187,44 @@ static int balance_nonroot( + ** of the table is closer to a linear scan through the file. That in turn + ** helps the operating system to deliver pages from the disk more rapidly. + ** +- ** An O(n^2) insertion sort algorithm is used, but since n is never more +- ** than (NB+2) (a small constant), that should not be a problem. ++ ** An O(N*N) sort algorithm is used, but since N is never more than NB+2 ++ ** (5), that is not a performance concern. + ** + ** When NB==3, this one optimization makes the database about 25% faster + ** for large insertions and deletions. + */ + for(i=0; ipgno; +- aPgFlags[i] = apNew[i]->pDbPage->flags; +- for(j=0; jpgno; ++ assert( apNew[i]->pDbPage->flags & PGHDR_WRITEABLE ); ++ assert( apNew[i]->pDbPage->flags & PGHDR_DIRTY ); + } +- for(i=0; ii ){ +- sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0); +- } +- sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]); +- apNew[i]->pgno = pgno; ++ for(i=0; ipgno < apNew[iB]->pgno ) iB = j; + } +- } + +- TRACE(("BALANCE: new: %d(%d nc=%d) %d(%d nc=%d) %d(%d nc=%d) " +- "%d(%d nc=%d) %d(%d nc=%d)\n", ++ /* If apNew[i] has a page number that is bigger than any of the ++ ** subsequence apNew[i] entries, then swap apNew[i] with the subsequent ++ ** entry that has the smallest page number (which we know to be ++ ** entry apNew[iB]). ++ */ ++ if( iB!=i ){ ++ Pgno pgnoA = apNew[i]->pgno; ++ Pgno pgnoB = apNew[iB]->pgno; ++ Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1; ++ u16 fgA = apNew[i]->pDbPage->flags; ++ u16 fgB = apNew[iB]->pDbPage->flags; ++ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB); ++ sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA); ++ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB); ++ apNew[i]->pgno = pgnoB; ++ apNew[iB]->pgno = pgnoA; ++ } ++ } ++ ++ TRACE(("BALANCE: new: %u(%u nc=%u) %u(%u nc=%u) %u(%u nc=%u) " ++ "%u(%u nc=%u) %u(%u nc=%u)\n", + apNew[0]->pgno, szNew[0], cntNew[0], + nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0, + nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0, +@@ -72531,7 +80265,7 @@ static int balance_nonroot( + ** updated. This happens below, after the sibling pages have been + ** populated, not here. + */ +- if( ISAUTOVACUUM ){ ++ if( ISAUTOVACUUM(pBt) ){ + MemPage *pOld; + MemPage *pNew = pOld = apNew[0]; + int cntOldNext = pNew->nCell + pNew->nOverflow; +@@ -72578,6 +80312,7 @@ static int balance_nonroot( + u8 *pCell; + u8 *pTemp; + int sz; ++ u8 *pSrcEnd; + MemPage *pNew = apNew[i]; + j = cntNew[i]; + +@@ -72621,7 +80356,14 @@ static int balance_nonroot( + iOvflSpace += sz; + assert( sz<=pBt->maxLocal+23 ); + assert( iOvflSpace <= (int)pBt->pageSize ); +- insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc); ++ assert( b.ixNx[NB*2-1]>j ); ++ for(k=0; b.ixNx[k]<=j; k++){} ++ pSrcEnd = b.apEnd[k]; ++ if( SQLITE_OVERFLOW(pSrcEnd, pCell, pCell+sz) ){ ++ rc = SQLITE_CORRUPT_BKPT; ++ goto balance_cleanup; ++ } ++ rc = insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno); + if( rc!=SQLITE_OK ) goto balance_cleanup; + assert( sqlite3PagerIswriteable(pParent->pDbPage) ); + } +@@ -72651,6 +80393,8 @@ static int balance_nonroot( + for(i=1-nNew; i=0 && iPg=1 || i>=0 ); ++ assert( iPg=0 /* On the upwards pass, or... */ + || cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */ +@@ -72717,7 +80461,7 @@ static int balance_nonroot( + ); + copyNodeContent(apNew[0], pParent, &rc); + freePage(apNew[0], &rc); +- }else if( ISAUTOVACUUM && !leafCorrection ){ ++ }else if( ISAUTOVACUUM(pBt) && !leafCorrection ){ + /* Fix the pointer map entries associated with the right-child of each + ** sibling page. All other pointer map entries have already been taken + ** care of. */ +@@ -72728,7 +80472,7 @@ static int balance_nonroot( + } + + assert( pParent->isInit ); +- TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", ++ TRACE(("BALANCE: finished: old=%u new=%u cells=%u\n", + nOld, nNew, b.nCell)); + + /* Free any old pages that were not reused as new pages. +@@ -72738,7 +80482,7 @@ static int balance_nonroot( + } + + #if 0 +- if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){ ++ if( ISAUTOVACUUM(pBt) && rc==SQLITE_OK && apNew[0]->isInit ){ + /* The ptrmapCheckPages() contains assert() statements that verify that + ** all pointer map pages are set correctly. This is helpful while + ** debugging. This is usually disabled because a corrupt database may +@@ -72800,7 +80544,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ + if( rc==SQLITE_OK ){ + rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); + copyNodeContent(pRoot, pChild, &rc); +- if( ISAUTOVACUUM ){ ++ if( ISAUTOVACUUM(pBt) ){ + ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); + } + } +@@ -72813,7 +80557,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ + assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); + assert( pChild->nCell==pRoot->nCell || CORRUPT_DB ); + +- TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); ++ TRACE(("BALANCE: copy root %u into %u\n", pRoot->pgno, pChild->pgno)); + + /* Copy the overflow cells from pRoot to pChild */ + memcpy(pChild->aiOvfl, pRoot->aiOvfl, +@@ -72834,7 +80578,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ + ** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid + ** on the same B-tree as pCur. + ** +-** This can if a database is corrupt with two or more SQL tables ++** This can occur if a database is corrupt with two or more SQL tables + ** pointing to the same b-tree. If an insert occurs on one SQL table + ** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL + ** table linked to the same b-tree. If the secondary insert causes a +@@ -72848,7 +80592,7 @@ static int anotherValidCursor(BtCursor *pCur){ + && pOther->eState==CURSOR_VALID + && pOther->pPage==pCur->pPage + ){ +- return SQLITE_CORRUPT_BKPT; ++ return SQLITE_CORRUPT_PAGE(pCur->pPage); + } + } + return SQLITE_OK; +@@ -72866,7 +80610,6 @@ static int anotherValidCursor(BtCursor *pCur){ + */ + static int balance(BtCursor *pCur){ + int rc = SQLITE_OK; +- const int nMin = pCur->pBt->usableSize * 2 / 3; + u8 aBalanceQuickSpace[13]; + u8 *pFree = 0; + +@@ -72878,7 +80621,11 @@ static int balance(BtCursor *pCur){ + MemPage *pPage = pCur->pPage; + + if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break; +- if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ ++ if( pPage->nOverflow==0 && pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ ++ /* No rebalance required as long as: ++ ** (1) There are no overflow cells ++ ** (2) The amount of free space on the page is less than 2/3rds of ++ ** the total usable space on the page. */ + break; + }else if( (iPage = pCur->iPage)==0 ){ + if( pPage->nOverflow && (rc = anotherValidCursor(pCur))==SQLITE_OK ){ +@@ -72901,6 +80648,11 @@ static int balance(BtCursor *pCur){ + }else{ + break; + } ++ }else if( sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){ ++ /* The page being written is not a root page, and there is currently ++ ** more than one reference to it. This only happens if the page is one ++ ** of its own ancestor pages. Corruption. */ ++ rc = SQLITE_CORRUPT_PAGE(pPage); + }else{ + MemPage * const pParent = pCur->apPage[iPage-1]; + int const iIdx = pCur->aiIdx[iPage-1]; +@@ -72999,7 +80751,7 @@ static int btreeOverwriteContent( + ){ + int nData = pX->nData - iOffset; + if( nData<=0 ){ +- /* Overwritting with zeros */ ++ /* Overwriting with zeros */ + int i; + for(i=0; ipData to write */ + int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ + int rc; /* Return code */ +@@ -73042,16 +80798,12 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ + Pgno ovflPgno; /* Next overflow page to write */ + u32 ovflPageSize; /* Size to write on overflow page */ + +- if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd +- || pCur->info.pPayload < pPage->aData + pPage->cellOffset +- ){ +- return SQLITE_CORRUPT_BKPT; +- } ++ assert( pCur->info.nLocalinfo.pPayload, pX, + 0, pCur->info.nLocal); + if( rc ) return rc; +- if( pCur->info.nLocal==nTotal ) return SQLITE_OK; + + /* Now overwrite the overflow pages */ + iOffset = pCur->info.nLocal; +@@ -73063,8 +80815,8 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ + do{ + rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); + if( rc ) return rc; +- if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){ +- rc = SQLITE_CORRUPT_BKPT; ++ if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ ++ rc = SQLITE_CORRUPT_PAGE(pPage); + }else{ + if( iOffset+ovflPageSize<(u32)nTotal ){ + ovflPgno = get4byte(pPage->aData); +@@ -73081,6 +80833,29 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ + return SQLITE_OK; + } + ++/* ++** Overwrite the cell that cursor pCur is pointing to with fresh content ++** contained in pX. ++*/ ++static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ ++ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ ++ MemPage *pPage = pCur->pPage; /* Page being written */ ++ ++ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ++ || pCur->info.pPayload < pPage->aData + pPage->cellOffset ++ ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } ++ if( pCur->info.nLocal==nTotal ){ ++ /* The entire cell is local */ ++ return btreeOverwriteContent(pPage, pCur->info.pPayload, pX, ++ 0, pCur->info.nLocal); ++ }else{ ++ /* The cell contains overflow content */ ++ return btreeOverwriteOverflowCell(pCur, pX); ++ } ++} ++ + + /* + ** Insert a new record into the BTree. The content of the new record +@@ -73098,7 +80873,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ + ** pX.pData,nData,nZero fields must be zero. + ** + ** If the seekResult parameter is non-zero, then a successful call to +-** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already ++** sqlite3BtreeIndexMoveto() to seek cursor pCur to (pKey,nKey) has already + ** been performed. In other words, if seekResult!=0 then the cursor + ** is currently pointing to a cell that will be adjacent to the cell + ** to be inserted. If seekResult<0 then pCur points to a cell that is +@@ -73116,7 +80891,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + BtCursor *pCur, /* Insert data into the table of this cursor */ + const BtreePayload *pX, /* Content of the row to be inserted */ + int flags, /* True if this is likely an append */ +- int seekResult /* Result of prior MovetoUnpacked() call */ ++ int seekResult /* Result of prior IndexMoveto() call */ + ){ + int rc; + int loc = seekResult; /* -1: before desired location +1: after */ +@@ -73124,29 +80899,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + int idx; + MemPage *pPage; + Btree *p = pCur->pBtree; +- BtShared *pBt = p->pBt; + unsigned char *oldCell; + unsigned char *newCell = 0; + +- assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags ); +- +- if( pCur->eState==CURSOR_FAULT ){ +- assert( pCur->skipNext!=SQLITE_OK ); +- return pCur->skipNext; +- } +- +- assert( cursorOwnsBtShared(pCur) ); +- assert( (pCur->curFlags & BTCF_WriteFlag)!=0 +- && pBt->inTransaction==TRANS_WRITE +- && (pBt->btsFlags & BTS_READ_ONLY)==0 ); +- assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); +- +- /* Assert that the caller has been consistent. If this cursor was opened +- ** expecting an index b-tree, then the caller should be inserting blob +- ** keys with no associated data. If the cursor was opened expecting an +- ** intkey table, the caller should be inserting integer keys with a +- ** blob of associated data. */ +- assert( (pX->pKey==0)==(pCur->pKeyInfo==0) ); ++ assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags ); ++ assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 ); + + /* Save the positions of any other cursors open on this table. + ** +@@ -73160,15 +80917,48 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + ** not to clear the cursor here. + */ + if( pCur->curFlags & BTCF_Multiple ){ +- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); ++ rc = saveAllCursors(p->pBt, pCur->pgnoRoot, pCur); + if( rc ) return rc; ++ if( loc && pCur->iPage<0 ){ ++ /* This can only happen if the schema is corrupt such that there is more ++ ** than one table or index with the same root page as used by the cursor. ++ ** Which can only happen if the SQLITE_NoSchemaError flag was set when ++ ** the schema was loaded. This cannot be asserted though, as a user might ++ ** set the flag, load the schema, and then unset the flag. */ ++ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); ++ } + } + ++ /* Ensure that the cursor is not in the CURSOR_FAULT state and that it ++ ** points to a valid cell. ++ */ ++ if( pCur->eState>=CURSOR_REQUIRESEEK ){ ++ testcase( pCur->eState==CURSOR_REQUIRESEEK ); ++ testcase( pCur->eState==CURSOR_FAULT ); ++ rc = moveToRoot(pCur); ++ if( rc && rc!=SQLITE_EMPTY ) return rc; ++ } ++ ++ assert( cursorOwnsBtShared(pCur) ); ++ assert( (pCur->curFlags & BTCF_WriteFlag)!=0 ++ && p->pBt->inTransaction==TRANS_WRITE ++ && (p->pBt->btsFlags & BTS_READ_ONLY)==0 ); ++ assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); ++ ++ /* Assert that the caller has been consistent. If this cursor was opened ++ ** expecting an index b-tree, then the caller should be inserting blob ++ ** keys with no associated data. If the cursor was opened expecting an ++ ** intkey table, the caller should be inserting integer keys with a ++ ** blob of associated data. */ ++ assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) ); ++ + if( pCur->pKeyInfo==0 ){ + assert( pX->pKey==0 ); + /* If this is an insert into a table b-tree, invalidate any incrblob + ** cursors open on the row being replaced */ +- invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); ++ if( p->hasIncrblobCur ){ ++ invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); ++ } + + /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing + ** to a row with the same key as the new entry being inserted. +@@ -73201,7 +80991,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + ** to an adjacent cell. Move the cursor so that it is pointing either + ** to the cell to be overwritten or an adjacent cell. + */ +- rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc); ++ rc = sqlite3BtreeTableMoveto(pCur, pX->nKey, ++ (flags & BTREE_APPEND)!=0, &loc); + if( rc ) return rc; + } + }else{ +@@ -73224,13 +81015,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + r.aMem = pX->aMem; + r.nField = pX->nMem; + r.default_rc = 0; +- r.errCode = 0; +- r.r1 = 0; +- r.r2 = 0; + r.eqSeen = 0; +- rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc); ++ rc = sqlite3BtreeIndexMoveto(pCur, &r, &loc); + }else{ +- rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc); ++ rc = btreeMoveto(pCur, pX->pKey, pX->nKey, ++ (flags & BTREE_APPEND)!=0, &loc); + } + if( rc ) return rc; + } +@@ -73244,43 +81033,65 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + if( pCur->info.nKey==pX->nKey ){ + BtreePayload x2; + x2.pData = pX->pKey; +- x2.nData = pX->nKey; ++ x2.nData = (int)pX->nKey; assert( pX->nKey<=0x7fffffff ); + x2.nZero = 0; + return btreeOverwriteCell(pCur, &x2); + } + } +- + } + assert( pCur->eState==CURSOR_VALID +- || (pCur->eState==CURSOR_INVALID && loc) +- || CORRUPT_DB ); ++ || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB ); + + pPage = pCur->pPage; +- assert( pPage->intKey || pX->nKey>=0 ); ++ assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); + assert( pPage->leaf || !pPage->intKey ); + if( pPage->nFree<0 ){ +- if( pCur->eState>CURSOR_INVALID ){ +- rc = SQLITE_CORRUPT_BKPT; ++ if( NEVER(pCur->eState>CURSOR_INVALID) ){ ++ /* ^^^^^--- due to the moveToRoot() call above */ ++ rc = SQLITE_CORRUPT_PAGE(pPage); + }else{ + rc = btreeComputeFreeSpace(pPage); + } + if( rc ) return rc; + } + +- TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", ++ TRACE(("INSERT: table=%u nkey=%lld ndata=%u page=%u %s\n", + pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, + loc==0 ? "overwrite" : "new entry")); +- assert( pPage->isInit ); +- newCell = pBt->pTmpSpace; ++ assert( pPage->isInit || CORRUPT_DB ); ++ newCell = p->pBt->pTmpSpace; + assert( newCell!=0 ); +- rc = fillInCell(pPage, newCell, pX, &szNew); +- if( rc ) goto end_insert; ++ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); ++ if( flags & BTREE_PREFORMAT ){ ++ rc = SQLITE_OK; ++ szNew = p->pBt->nPreformatSize; ++ if( szNew<4 ){ ++ szNew = 4; ++ newCell[3] = 0; ++ } ++ if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){ ++ CellInfo info; ++ pPage->xParseCell(pPage, newCell, &info); ++ if( info.nPayload!=info.nLocal ){ ++ Pgno ovfl = get4byte(&newCell[szNew-4]); ++ ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc); ++ if( NEVER(rc) ) goto end_insert; ++ } ++ } ++ }else{ ++ rc = fillInCell(pPage, newCell, pX, &szNew); ++ if( rc ) goto end_insert; ++ } + assert( szNew==pPage->xCellSize(pPage, newCell) ); +- assert( szNew <= MX_CELL_SIZE(pBt) ); ++ assert( szNew <= MX_CELL_SIZE(p->pBt) ); + idx = pCur->ix; ++ pCur->info.nSize = 0; + if( loc==0 ){ + CellInfo info; +- assert( idxnCell ); ++ assert( idx>=0 ); ++ if( idx>=pPage->nCell ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc ){ + goto end_insert; +@@ -73289,11 +81100,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + if( !pPage->leaf ){ + memcpy(newCell, oldCell, 4); + } +- rc = clearCell(pPage, oldCell, &info); ++ BTREE_CLEAR_CELL(rc, pPage, oldCell, info); + testcase( pCur->curFlags & BTCF_ValidOvfl ); + invalidateOverflowCache(pCur); + if( info.nSize==szNew && info.nLocal==info.nPayload +- && (!ISAUTOVACUUM || szNewminLocal) ++ && (!ISAUTOVACUUM(p->pBt) || szNewminLocal) + ){ + /* Overwrite the old cell with the new if they are the same size. + ** We could also try to do this if the old cell is smaller, then add +@@ -73306,10 +81117,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ + assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ + if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ +- return SQLITE_CORRUPT_BKPT; ++ return SQLITE_CORRUPT_PAGE(pPage); + } + if( oldCell+szNew > pPage->aDataEnd ){ +- return SQLITE_CORRUPT_BKPT; ++ return SQLITE_CORRUPT_PAGE(pPage); + } + memcpy(oldCell, newCell, szNew); + return SQLITE_OK; +@@ -73319,11 +81130,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + }else if( loc<0 && pPage->nCell>0 ){ + assert( pPage->leaf ); + idx = ++pCur->ix; +- pCur->curFlags &= ~BTCF_ValidNKey; ++ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); + }else{ + assert( pPage->leaf ); + } +- insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); ++ rc = insertCellFast(pPage, idx, newCell, szNew); + assert( pPage->nOverflow==0 || rc==SQLITE_OK ); + assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); + +@@ -73347,10 +81158,9 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( + ** larger than the largest existing key, it is possible to insert the + ** row without seeking the cursor. This can be a big performance boost. + */ +- pCur->info.nSize = 0; + if( pPage->nOverflow ){ + assert( rc==SQLITE_OK ); +- pCur->curFlags &= ~(BTCF_ValidNKey); ++ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); + rc = balance(pCur); + + /* Must make sure nOverflow is reset to zero even if the balance() +@@ -73380,6 +81190,118 @@ end_insert: + return rc; + } + ++/* ++** This function is used as part of copying the current row from cursor ++** pSrc into cursor pDest. If the cursors are open on intkey tables, then ++** parameter iKey is used as the rowid value when the record is copied ++** into pDest. Otherwise, the record is copied verbatim. ++** ++** This function does not actually write the new value to cursor pDest. ++** Instead, it creates and populates any required overflow pages and ++** writes the data for the new cell into the BtShared.pTmpSpace buffer ++** for the destination database. The size of the cell, in bytes, is left ++** in BtShared.nPreformatSize. The caller completes the insertion by ++** calling sqlite3BtreeInsert() with the BTREE_PREFORMAT flag specified. ++** ++** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ++*/ ++SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){ ++ BtShared *pBt = pDest->pBt; ++ u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */ ++ const u8 *aIn; /* Pointer to next input buffer */ ++ u32 nIn; /* Size of input buffer aIn[] */ ++ u32 nRem; /* Bytes of data still to copy */ ++ ++ getCellInfo(pSrc); ++ if( pSrc->info.nPayload<0x80 ){ ++ *(aOut++) = (u8)pSrc->info.nPayload; ++ }else{ ++ aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload); ++ } ++ if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); ++ nIn = pSrc->info.nLocal; ++ aIn = pSrc->info.pPayload; ++ if( aIn+nIn>pSrc->pPage->aDataEnd ){ ++ return SQLITE_CORRUPT_PAGE(pSrc->pPage); ++ } ++ nRem = pSrc->info.nPayload; ++ if( nIn==nRem && nInpPage->maxLocal ){ ++ memcpy(aOut, aIn, nIn); ++ pBt->nPreformatSize = nIn + (int)(aOut - pBt->pTmpSpace); ++ return SQLITE_OK; ++ }else{ ++ int rc = SQLITE_OK; ++ Pager *pSrcPager = pSrc->pBt->pPager; ++ u8 *pPgnoOut = 0; ++ Pgno ovflIn = 0; ++ DbPage *pPageIn = 0; ++ MemPage *pPageOut = 0; ++ u32 nOut; /* Size of output buffer aOut[] */ ++ ++ nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload); ++ pBt->nPreformatSize = (int)nOut + (int)(aOut - pBt->pTmpSpace); ++ if( nOutinfo.nPayload ){ ++ pPgnoOut = &aOut[nOut]; ++ pBt->nPreformatSize += 4; ++ } ++ ++ if( nRem>nIn ){ ++ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ ++ return SQLITE_CORRUPT_PAGE(pSrc->pPage); ++ } ++ ovflIn = get4byte(&pSrc->info.pPayload[nIn]); ++ } ++ ++ do { ++ nRem -= nOut; ++ do{ ++ assert( nOut>0 ); ++ if( nIn>0 ){ ++ int nCopy = MIN(nOut, nIn); ++ memcpy(aOut, aIn, nCopy); ++ nOut -= nCopy; ++ nIn -= nCopy; ++ aOut += nCopy; ++ aIn += nCopy; ++ } ++ if( nOut>0 ){ ++ sqlite3PagerUnref(pPageIn); ++ pPageIn = 0; ++ rc = sqlite3PagerGet(pSrcPager, ovflIn, &pPageIn, PAGER_GET_READONLY); ++ if( rc==SQLITE_OK ){ ++ aIn = (const u8*)sqlite3PagerGetData(pPageIn); ++ ovflIn = get4byte(aIn); ++ aIn += 4; ++ nIn = pSrc->pBt->usableSize - 4; ++ } ++ } ++ }while( rc==SQLITE_OK && nOut>0 ); ++ ++ if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){ ++ Pgno pgnoNew; ++ MemPage *pNew = 0; ++ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); ++ put4byte(pPgnoOut, pgnoNew); ++ if( ISAUTOVACUUM(pBt) && pPageOut ){ ++ ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc); ++ } ++ releasePage(pPageOut); ++ pPageOut = pNew; ++ if( pPageOut ){ ++ pPgnoOut = pPageOut->aData; ++ put4byte(pPgnoOut, 0); ++ aOut = &pPgnoOut[4]; ++ nOut = MIN(pBt->usableSize - 4, nRem); ++ } ++ } ++ }while( nRem>0 && rc==SQLITE_OK ); ++ ++ releasePage(pPageOut); ++ sqlite3PagerUnref(pPageIn); ++ return rc; ++ } ++} ++ + /* + ** Delete the entry that the cursor is pointing to. + ** +@@ -73400,14 +81322,13 @@ end_insert: + SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + Btree *p = pCur->pBtree; + BtShared *pBt = p->pBt; +- int rc; /* Return code */ +- MemPage *pPage; /* Page to delete cell from */ +- unsigned char *pCell; /* Pointer to cell to delete */ +- int iCellIdx; /* Index of cell to delete */ +- int iCellDepth; /* Depth of node containing pCell */ +- CellInfo info; /* Size of the cell being deleted */ +- int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ +- u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */ ++ int rc; /* Return code */ ++ MemPage *pPage; /* Page to delete cell from */ ++ unsigned char *pCell; /* Pointer to cell to delete */ ++ int iCellIdx; /* Index of cell to delete */ ++ int iCellDepth; /* Depth of node containing pCell */ ++ CellInfo info; /* Size of the cell being deleted */ ++ u8 bPreserve; /* Keep cursor valid. 2 for CURSOR_SKIPNEXT */ + + assert( cursorOwnsBtShared(pCur) ); + assert( pBt->inTransaction==TRANS_WRITE ); +@@ -73416,30 +81337,52 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + assert( !hasReadConflicts(p, pCur->pgnoRoot) ); + assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); +- if( pCur->eState==CURSOR_REQUIRESEEK ){ +- rc = btreeRestoreCursorPosition(pCur); +- if( rc ) return rc; ++ if( pCur->eState!=CURSOR_VALID ){ ++ if( pCur->eState>=CURSOR_REQUIRESEEK ){ ++ rc = btreeRestoreCursorPosition(pCur); ++ assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); ++ if( rc || pCur->eState!=CURSOR_VALID ) return rc; ++ }else{ ++ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); ++ } + } + assert( pCur->eState==CURSOR_VALID ); + + iCellDepth = pCur->iPage; + iCellIdx = pCur->ix; + pPage = pCur->pPage; ++ if( pPage->nCell<=iCellIdx ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } + pCell = findCell(pPage, iCellIdx); +- if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT; ++ if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } ++ if( pCell<&pPage->aCellIdx[pPage->nCell] ){ ++ return SQLITE_CORRUPT_PAGE(pPage); ++ } + +- /* If the bPreserve flag is set to true, then the cursor position must ++ /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must + ** be preserved following this delete operation. If the current delete + ** will cause a b-tree rebalance, then this is done by saving the cursor + ** key and leaving the cursor in CURSOR_REQUIRESEEK state before + ** returning. + ** +- ** Or, if the current delete will not cause a rebalance, then the cursor ++ ** If the current delete will not cause a rebalance, then the cursor + ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately +- ** before or after the deleted entry. In this case set bSkipnext to true. */ ++ ** before or after the deleted entry. ++ ** ++ ** The bPreserve value records which path is required: ++ ** ++ ** bPreserve==0 Not necessary to save the cursor position ++ ** bPreserve==1 Use CURSOR_REQUIRESEEK to save the cursor position ++ ** bPreserve==2 Cursor won't move. Set CURSOR_SKIPNEXT. ++ */ ++ bPreserve = (flags & BTREE_SAVEPOSITION)!=0; + if( bPreserve ){ + if( !pPage->leaf +- || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) ++ || (pPage->nFree+pPage->xCellSize(pPage,pCell)+2) > ++ (int)(pBt->usableSize*2/3) + || pPage->nCell==1 /* See dbfuzz001.test for a test case */ + ){ + /* A b-tree rebalance will be required after deleting this entry. +@@ -73447,7 +81390,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + rc = saveCursorKey(pCur); + if( rc ) return rc; + }else{ +- bSkipnext = 1; ++ bPreserve = 2; + } + } + +@@ -73473,7 +81416,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + + /* If this is a delete operation to remove a row from a table b-tree, + ** invalidate any incrblob cursors open on the row being deleted. */ +- if( pCur->pKeyInfo==0 ){ ++ if( pCur->pKeyInfo==0 && p->hasIncrblobCur ){ + invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0); + } + +@@ -73482,7 +81425,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + ** itself from within the page. */ + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc ) return rc; +- rc = clearCell(pPage, pCell, &info); ++ BTREE_CLEAR_CELL(rc, pPage, pCell, info); + dropCell(pPage, iCellIdx, info.nSize, &rc); + if( rc ) return rc; + +@@ -73507,14 +81450,14 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + n = pCur->pPage->pgno; + } + pCell = findCell(pLeaf, pLeaf->nCell-1); +- if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; ++ if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf); + nCell = pLeaf->xCellSize(pLeaf, pCell); + assert( MX_CELL_SIZE(pBt) >= nCell ); + pTmp = pBt->pTmpSpace; + assert( pTmp!=0 ); + rc = sqlite3PagerWrite(pLeaf->pDbPage); + if( rc==SQLITE_OK ){ +- insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); ++ rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n); + } + dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); + if( rc ) return rc; +@@ -73535,7 +81478,15 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + ** been corrected, so be it. Otherwise, after balancing the leaf node, + ** walk the cursor up the tree to the internal node and balance it as + ** well. */ +- rc = balance(pCur); ++ assert( pCur->pPage->nOverflow==0 ); ++ assert( pCur->pPage->nFree>=0 ); ++ if( pCur->pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ ++ /* Optimization: If the free space is less than 2/3rds of the page, ++ ** then balance() will always be a no-op. No need to invoke it. */ ++ rc = SQLITE_OK; ++ }else{ ++ rc = balance(pCur); ++ } + if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ + releasePageNotNull(pCur->pPage); + pCur->iPage--; +@@ -73547,8 +81498,8 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ + } + + if( rc==SQLITE_OK ){ +- if( bSkipnext ){ +- assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) ); ++ if( bPreserve>1 ){ ++ assert( (pCur->iPage==iCellDepth || CORRUPT_DB) ); + assert( pPage==pCur->pPage || CORRUPT_DB ); + assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell ); + pCur->eState = CURSOR_SKIPNEXT; +@@ -73586,7 +81537,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ + MemPage *pRoot; + Pgno pgnoRoot; + int rc; +- int ptfFlags; /* Page-type flage for the root page of new table */ ++ int ptfFlags; /* Page-type flags for the root page of new table */ + + assert( sqlite3BtreeHoldsMutex(p) ); + assert( pBt->inTransaction==TRANS_WRITE ); +@@ -73615,7 +81566,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ + */ + sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); + if( pgnoRoot>btreePagecount(pBt) ){ +- return SQLITE_CORRUPT_BKPT; ++ return SQLITE_CORRUPT_PGNO(pgnoRoot); + } + pgnoRoot++; + +@@ -73663,7 +81614,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ + } + rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); + if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ +- rc = SQLITE_CORRUPT_BKPT; ++ rc = SQLITE_CORRUPT_PGNO(pgnoRoot); + } + if( rc!=SQLITE_OK ){ + releasePage(pRoot); +@@ -73742,7 +81693,7 @@ static int clearDatabasePage( + BtShared *pBt, /* The BTree that contains the table */ + Pgno pgno, /* Page number to clear */ + int freePageFlag, /* Deallocate page if true */ +- int *pnChange /* Add number of Cells freed to this counter */ ++ i64 *pnChange /* Add number of Cells freed to this counter */ + ){ + MemPage *pPage; + int rc; +@@ -73753,15 +81704,16 @@ static int clearDatabasePage( + + assert( sqlite3_mutex_held(pBt->mutex) ); + if( pgno>btreePagecount(pBt) ){ +- return SQLITE_CORRUPT_BKPT; ++ return SQLITE_CORRUPT_PGNO(pgno); + } +- rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); ++ rc = getAndInitPage(pBt, pgno, &pPage, 0); + if( rc ) return rc; +- if( pPage->bBusy ){ +- rc = SQLITE_CORRUPT_BKPT; ++ if( (pBt->openFlags & BTREE_SINGLE)==0 ++ && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) ++ ){ ++ rc = SQLITE_CORRUPT_PAGE(pPage); + goto cleardatabasepage_out; + } +- pPage->bBusy = 1; + hdr = pPage->hdrOffset; + for(i=0; inCell; i++){ + pCell = findCell(pPage, i); +@@ -73769,14 +81721,15 @@ static int clearDatabasePage( + rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); + if( rc ) goto cleardatabasepage_out; + } +- rc = clearCell(pPage, pCell, &info); ++ BTREE_CLEAR_CELL(rc, pPage, pCell, info); + if( rc ) goto cleardatabasepage_out; + } + if( !pPage->leaf ){ + rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); + if( rc ) goto cleardatabasepage_out; +- }else if( pnChange ){ +- assert( pPage->intKey || CORRUPT_DB ); ++ if( pPage->intKey ) pnChange = 0; ++ } ++ if( pnChange ){ + testcase( !pPage->intKey ); + *pnChange += pPage->nCell; + } +@@ -73787,7 +81740,6 @@ static int clearDatabasePage( + } + + cleardatabasepage_out: +- pPage->bBusy = 0; + releasePage(pPage); + return rc; + } +@@ -73801,11 +81753,10 @@ cleardatabasepage_out: + ** read cursors on the table. Open write cursors are moved to the + ** root of the table. + ** +-** If pnChange is not NULL, then table iTable must be an intkey table. The +-** integer value pointed to by pnChange is incremented by the number of +-** entries in the table. ++** If pnChange is not NULL, then the integer value pointed to by pnChange ++** is incremented by the number of entries in the table. + */ +-SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ ++SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, i64 *pnChange){ + int rc; + BtShared *pBt = p->pBt; + sqlite3BtreeEnter(p); +@@ -73817,7 +81768,9 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ + /* Invalidate all incrblob cursors open on table iTable (assuming iTable + ** is the root of a table b-tree - if it is not, the following call is + ** a no-op). */ +- invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); ++ if( p->hasIncrblobCur ){ ++ invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); ++ } + rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); + } + sqlite3BtreeLeave(p); +@@ -73862,13 +81815,13 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ + assert( p->inTrans==TRANS_WRITE ); + assert( iTable>=2 ); + if( iTable>btreePagecount(pBt) ){ +- return SQLITE_CORRUPT_BKPT; ++ return SQLITE_CORRUPT_PGNO(iTable); + } + +- rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); +- if( rc ) return rc; + rc = sqlite3BtreeClearTable(p, iTable, 0); +- if( rc ){ ++ if( rc ) return rc; ++ rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); ++ if( NEVER(rc) ){ + releasePage(pPage); + return rc; + } +@@ -73977,7 +81930,7 @@ SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ + assert( idx>=0 && idx<=15 ); + + if( idx==BTREE_DATA_VERSION ){ +- *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; ++ *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion; + }else{ + *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); + } +@@ -74103,6 +82056,41 @@ SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){ + } + + #ifndef SQLITE_OMIT_INTEGRITY_CHECK ++/* ++** Record an OOM error during integrity_check ++*/ ++static void checkOom(IntegrityCk *pCheck){ ++ pCheck->rc = SQLITE_NOMEM; ++ pCheck->mxErr = 0; /* Causes integrity_check processing to stop */ ++ if( pCheck->nErr==0 ) pCheck->nErr++; ++} ++ ++/* ++** Invoke the progress handler, if appropriate. Also check for an ++** interrupt. ++*/ ++static void checkProgress(IntegrityCk *pCheck){ ++ sqlite3 *db = pCheck->db; ++ if( AtomicLoad(&db->u1.isInterrupted) ){ ++ pCheck->rc = SQLITE_INTERRUPT; ++ pCheck->nErr++; ++ pCheck->mxErr = 0; ++ } ++#ifndef SQLITE_OMIT_PROGRESS_CALLBACK ++ if( db->xProgress ){ ++ assert( db->nProgressOps>0 ); ++ pCheck->nStep++; ++ if( (pCheck->nStep % db->nProgressOps)==0 ++ && db->xProgress(db->pProgressArg) ++ ){ ++ pCheck->rc = SQLITE_INTERRUPT; ++ pCheck->nErr++; ++ pCheck->mxErr = 0; ++ } ++ } ++#endif ++} ++ + /* + ** Append a message to the error message string. + */ +@@ -74112,6 +82100,7 @@ static void checkAppendMsg( + ... + ){ + va_list ap; ++ checkProgress(pCheck); + if( !pCheck->mxErr ) return; + pCheck->mxErr--; + pCheck->nErr++; +@@ -74120,12 +82109,13 @@ static void checkAppendMsg( + sqlite3_str_append(&pCheck->errMsg, "\n", 1); + } + if( pCheck->zPfx ){ +- sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2); ++ sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, ++ pCheck->v0, pCheck->v1, pCheck->v2); + } + sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap); + va_end(ap); + if( pCheck->errMsg.accError==SQLITE_NOMEM ){ +- pCheck->bOomFault = 1; ++ checkOom(pCheck); + } + } + #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ +@@ -74137,7 +82127,8 @@ static void checkAppendMsg( + ** corresponds to page iPg is already set. + */ + static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ +- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); ++ assert( pCheck->aPgRef!=0 ); ++ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 ); + return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07))); + } + +@@ -74145,7 +82136,8 @@ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ + ** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg. + */ + static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ +- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); ++ assert( pCheck->aPgRef!=0 ); ++ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 ); + pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07)); + } + +@@ -74159,15 +82151,14 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ + ** Also check that the page number is in bounds. + */ + static int checkRef(IntegrityCk *pCheck, Pgno iPage){ +- if( iPage>pCheck->nPage || iPage==0 ){ +- checkAppendMsg(pCheck, "invalid page number %d", iPage); ++ if( iPage>pCheck->nCkPage || iPage==0 ){ ++ checkAppendMsg(pCheck, "invalid page number %u", iPage); + return 1; + } + if( getPageReferenced(pCheck, iPage) ){ +- checkAppendMsg(pCheck, "2nd reference to page %d", iPage); ++ checkAppendMsg(pCheck, "2nd reference to page %u", iPage); + return 1; + } +- if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1; + setPageReferenced(pCheck, iPage); + return 0; + } +@@ -74190,14 +82181,14 @@ static void checkPtrmap( + + rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); + if( rc!=SQLITE_OK ){ +- if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1; +- checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild); ++ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck); ++ checkAppendMsg(pCheck, "Failed to read ptrmap key=%u", iChild); + return; + } + + if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ + checkAppendMsg(pCheck, +- "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", ++ "Bad ptr map entry key=%u expected=(%u,%u) got=(%u,%u)", + iChild, eType, iParent, ePtrmapType, iPtrmapParent); + } + } +@@ -74222,7 +82213,7 @@ static void checkList( + if( checkRef(pCheck, iPage) ) break; + N--; + if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){ +- checkAppendMsg(pCheck, "failed to get page %d", iPage); ++ checkAppendMsg(pCheck, "failed to get page %u", iPage); + break; + } + pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage); +@@ -74235,7 +82226,7 @@ static void checkList( + #endif + if( n>pCheck->pBt->usableSize/4-2 ){ + checkAppendMsg(pCheck, +- "freelist leaf count too big on page %d", iPage); ++ "freelist leaf count too big on page %u", iPage); + N--; + }else{ + for(i=0; i<(int)n; i++){ +@@ -74267,7 +82258,7 @@ static void checkList( + } + if( N && nErrAtStart==pCheck->nErr ){ + checkAppendMsg(pCheck, +- "%s is %d but should be %d", ++ "%s is %u but should be %u", + isFreeList ? "size" : "overflow list length", + expected-N, expected); + } +@@ -74297,7 +82288,9 @@ static void checkList( + ** lower 16 bits are the index of the last byte of that range. + */ + static void btreeHeapInsert(u32 *aHeap, u32 x){ +- u32 j, i = ++aHeap[0]; ++ u32 j, i; ++ assert( aHeap!=0 ); ++ i = ++aHeap[0]; + aHeap[i] = x; + while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){ + x = aHeap[j]; +@@ -74374,15 +82367,18 @@ static int checkTreePage( + + /* Check that the page exists + */ ++ checkProgress(pCheck); ++ if( pCheck->mxErr==0 ) goto end_of_check; + pBt = pCheck->pBt; + usableSize = pBt->usableSize; + if( iPage==0 ) return 0; + if( checkRef(pCheck, iPage) ) return 0; +- pCheck->zPfx = "Page %u: "; ++ pCheck->zPfx = "Tree %u page %u: "; + pCheck->v1 = iPage; + if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ + checkAppendMsg(pCheck, + "unable to get the page. error code=%d", rc); ++ if( rc==SQLITE_IOERR_NOMEM ) pCheck->rc = SQLITE_NOMEM; + goto end_of_check; + } + +@@ -74405,7 +82401,7 @@ static int checkTreePage( + hdr = pPage->hdrOffset; + + /* Set up for cell analysis */ +- pCheck->zPfx = "On tree page %u cell %d: "; ++ pCheck->zPfx = "Tree %u page %u cell %u: "; + contentOffset = get2byteNotZero(&data[hdr+5]); + assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ + +@@ -74413,6 +82409,9 @@ static int checkTreePage( + ** number of cells on the page. */ + nCell = get2byte(&data[hdr+3]); + assert( pPage->nCell==nCell ); ++ if( pPage->leaf || pPage->intKey==0 ){ ++ pCheck->nRow += nCell; ++ } + + /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page + ** immediately follows the b-tree page header. */ +@@ -74425,7 +82424,7 @@ static int checkTreePage( + pgno = get4byte(&data[hdr+8]); + #ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ +- pCheck->zPfx = "On page %u at right child: "; ++ pCheck->zPfx = "Tree %u page %u right child: "; + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); + } + #endif +@@ -74449,7 +82448,7 @@ static int checkTreePage( + pc = get2byteAligned(pCellIdx); + pCellIdx -= 2; + if( pcusableSize-4 ){ +- checkAppendMsg(pCheck, "Offset %d out of range %d..%d", ++ checkAppendMsg(pCheck, "Offset %u out of range %u..%u", + pc, contentOffset, usableSize-4); + doCoverageCheck = 0; + continue; +@@ -74524,6 +82523,7 @@ static int checkTreePage( + btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); + } + } ++ assert( heap!=0 ); + /* Add the freeblocks to the min-heap + ** + ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header +@@ -74581,7 +82581,7 @@ static int checkTreePage( + */ + if( heap[0]==0 && nFrag!=data[hdr+7] ){ + checkAppendMsg(pCheck, +- "Fragmentation of %d bytes reported as %d on page %u", ++ "Fragmentation of %u bytes reported as %u on page %u", + nFrag, data[hdr+7], iPage); + } + } +@@ -74619,13 +82619,15 @@ end_of_check: + ** the unverified btrees. Except, if aRoot[1] is 1, then the freelist + ** checks are still performed. + */ +-SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( ++SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( + sqlite3 *db, /* Database connection that is running the check */ + Btree *p, /* The btree to be checked */ + Pgno *aRoot, /* An array of root pages numbers for individual trees */ ++ Mem *aCnt, /* Memory cells to write counts for each tree to */ + int nRoot, /* Number of entries in aRoot[] */ + int mxErr, /* Stop reporting errors after this many */ +- int *pnErr /* Write number of errors seen to this variable */ ++ int *pnErr, /* OUT: Write number of errors seen to this variable */ ++ char **pzOut /* OUT: Write the error message string here */ + ){ + Pgno i; + IntegrityCk sCheck; +@@ -74635,7 +82637,9 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( + int bPartial = 0; /* True if not checking all btrees */ + int bCkFreelist = 1; /* True to scan the freelist */ + VVA_ONLY( int nRef ); ++ + assert( nRoot>0 ); ++ assert( aCnt!=0 ); + + /* aRoot[0]==0 means this is a partial check */ + if( aRoot[0]==0 ){ +@@ -74648,42 +82652,36 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( + assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); + VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) ); + assert( nRef>=0 ); ++ memset(&sCheck, 0, sizeof(sCheck)); + sCheck.db = db; + sCheck.pBt = pBt; + sCheck.pPager = pBt->pPager; +- sCheck.nPage = btreePagecount(sCheck.pBt); ++ sCheck.nCkPage = btreePagecount(sCheck.pBt); + sCheck.mxErr = mxErr; +- sCheck.nErr = 0; +- sCheck.bOomFault = 0; +- sCheck.zPfx = 0; +- sCheck.v1 = 0; +- sCheck.v2 = 0; +- sCheck.aPgRef = 0; +- sCheck.heap = 0; + sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); + sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL; +- if( sCheck.nPage==0 ){ ++ if( sCheck.nCkPage==0 ){ + goto integrity_ck_cleanup; + } + +- sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1); ++ sCheck.aPgRef = sqlite3MallocZero((sCheck.nCkPage / 8)+ 1); + if( !sCheck.aPgRef ){ +- sCheck.bOomFault = 1; ++ checkOom(&sCheck); + goto integrity_ck_cleanup; + } + sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize ); + if( sCheck.heap==0 ){ +- sCheck.bOomFault = 1; ++ checkOom(&sCheck); + goto integrity_ck_cleanup; + } + + i = PENDING_BYTE_PAGE(pBt); +- if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); ++ if( i<=sCheck.nCkPage ) setPageReferenced(&sCheck, i); + + /* Check the integrity of the freelist + */ + if( bCkFreelist ){ +- sCheck.zPfx = "Main freelist: "; ++ sCheck.zPfx = "Freelist: "; + checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), + get4byte(&pBt->pPage1->aData[36])); + sCheck.zPfx = 0; +@@ -74700,7 +82698,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( + mxInHdr = get4byte(&pBt->pPage1->aData[52]); + if( mx!=mxInHdr ){ + checkAppendMsg(&sCheck, +- "max rootpage (%d) disagrees with header (%d)", ++ "max rootpage (%u) disagrees with header (%u)", + mx, mxInHdr + ); + } +@@ -74714,24 +82712,28 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( + testcase( pBt->db->flags & SQLITE_CellSizeCk ); + pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; + for(i=0; (int)iautoVacuum && aRoot[i]>1 && !bPartial ){ +- checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); +- } ++ if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ ++ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); ++ } + #endif +- checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); ++ sCheck.v0 = aRoot[i]; ++ checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); ++ } ++ sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow); + } + pBt->db->flags = savedDbFlags; + + /* Make sure every page in the file is referenced + */ + if( !bPartial ){ +- for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ ++ for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){ + #ifdef SQLITE_OMIT_AUTOVACUUM + if( getPageReferenced(&sCheck, i)==0 ){ +- checkAppendMsg(&sCheck, "Page %d is never used", i); ++ checkAppendMsg(&sCheck, "Page %u: never used", i); + } + #else + /* If the database supports auto-vacuum, make sure no tables contain +@@ -74739,11 +82741,11 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( + */ + if( getPageReferenced(&sCheck, i)==0 && + (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ +- checkAppendMsg(&sCheck, "Page %d is never used", i); ++ checkAppendMsg(&sCheck, "Page %u: never used", i); + } + if( getPageReferenced(&sCheck, i)!=0 && + (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ +- checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); ++ checkAppendMsg(&sCheck, "Page %u: pointer map referenced", i); + } + #endif + } +@@ -74754,16 +82756,17 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( + integrity_ck_cleanup: + sqlite3PageFree(sCheck.heap); + sqlite3_free(sCheck.aPgRef); +- if( sCheck.bOomFault ){ ++ *pnErr = sCheck.nErr; ++ if( sCheck.nErr==0 ){ + sqlite3_str_reset(&sCheck.errMsg); +- sCheck.nErr++; ++ *pzOut = 0; ++ }else{ ++ *pzOut = sqlite3StrAccumFinish(&sCheck.errMsg); + } +- *pnErr = sCheck.nErr; +- if( sCheck.nErr==0 ) sqlite3_str_reset(&sCheck.errMsg); + /* Make sure this analysis did not leave any unref() pages. */ + assert( nRef==sqlite3PagerRefcount(pBt->pPager) ); + sqlite3BtreeLeave(p); +- return sqlite3StrAccumFinish(&sCheck.errMsg); ++ return sCheck.rc; + } + #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ + +@@ -74793,11 +82796,12 @@ SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *p){ + } + + /* +-** Return non-zero if a transaction is active. ++** Return one of SQLITE_TXN_NONE, SQLITE_TXN_READ, or SQLITE_TXN_WRITE ++** to describe the current transaction state of Btree p. + */ +-SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree *p){ ++SQLITE_PRIVATE int sqlite3BtreeTxnState(Btree *p){ + assert( p==0 || sqlite3_mutex_held(p->db->mutex) ); +- return (p && (p->inTrans==TRANS_WRITE)); ++ return p ? p->inTrans : 0; + } + + #ifndef SQLITE_OMIT_WAL +@@ -74826,14 +82830,8 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int * + #endif + + /* +-** Return non-zero if a read (or write) transaction is active. ++** Return true if there is currently a backup running on Btree p. + */ +-SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree *p){ +- assert( p ); +- assert( sqlite3_mutex_held(p->db->mutex) ); +- return p->inTrans!=TRANS_NONE; +-} +- + SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){ + assert( p ); + assert( sqlite3_mutex_held(p->db->mutex) ); +@@ -74862,6 +82860,7 @@ SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){ + */ + SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ + BtShared *pBt = p->pBt; ++ assert( nBytes==0 || nBytes==sizeof(Schema) ); + sqlite3BtreeEnter(p); + if( !pBt->pSchema && nBytes ){ + pBt->pSchema = sqlite3DbMallocZero(0, nBytes); +@@ -75033,6 +83032,17 @@ SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *p){ + */ + SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } + ++/* ++** If no transaction is active and the database is not a temp-db, clear ++** the in-memory pager cache. ++*/ ++SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree *p){ ++ BtShared *pBt = p->pBt; ++ if( pBt->inTransaction==TRANS_NONE ){ ++ sqlite3PagerClearCache(pBt->pPager); ++ } ++} ++ + #if !defined(SQLITE_OMIT_SHARED_CACHE) + /* + ** Return true if the Btree passed as the only argument is sharable. +@@ -75141,14 +83151,13 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ + if( i==1 ){ + Parse sParse; + int rc = 0; +- memset(&sParse, 0, sizeof(sParse)); +- sParse.db = pDb; ++ sqlite3ParseObjectInit(&sParse,pDb); + if( sqlite3OpenTempDatabase(&sParse) ){ + sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); + rc = SQLITE_ERROR; + } + sqlite3DbFree(pErrorDb, sParse.zErrMsg); +- sqlite3ParserReset(&sParse); ++ sqlite3ParseObjectReset(&sParse); + if( rc ){ + return 0; + } +@@ -75179,7 +83188,7 @@ static int setDestPgsz(sqlite3_backup *p){ + ** message in database handle db. + */ + static int checkReadTransaction(sqlite3 *db, Btree *p){ +- if( sqlite3BtreeIsInReadTrans(p) ){ ++ if( sqlite3BtreeTxnState(p)!=SQLITE_TXN_NONE ){ + sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use"); + return SQLITE_ERROR; + } +@@ -75299,13 +83308,7 @@ static int backupOnePage( + assert( !isFatalError(p->rc) ); + assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); + assert( zSrcData ); +- +- /* Catch the case where the destination is an in-memory database and the +- ** page sizes of the source and destination differ. +- */ +- if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ +- rc = SQLITE_READONLY; +- } ++ assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); + + /* This loop runs once for each destination page spanned by the source + ** page. For each iteration, variable iOff is set to the byte offset +@@ -75410,7 +83413,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ + ** one now. If a transaction is opened here, then it will be closed + ** before this function exits. + */ +- if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ ++ if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(p->pSrc) ){ + rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0); + bCloseTrans = 1; + } +@@ -75438,7 +83441,10 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ + pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); + pgszDest = sqlite3BtreeGetPageSize(p->pDest); + destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); +- if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ ++ if( SQLITE_OK==rc ++ && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) ++ && pgszSrc!=pgszDest ++ ){ + rc = SQLITE_READONLY; + } + +@@ -75782,7 +83788,7 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ + sqlite3BtreeEnter(pTo); + sqlite3BtreeEnter(pFrom); + +- assert( sqlite3BtreeIsInTrans(pTo) ); ++ assert( sqlite3BtreeTxnState(pTo)==SQLITE_TXN_WRITE ); + pFd = sqlite3PagerFile(sqlite3BtreePager(pTo)); + if( pFd->pMethods ){ + i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom); +@@ -75818,7 +83824,7 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ + sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); + } + +- assert( sqlite3BtreeIsInTrans(pTo)==0 ); ++ assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE ); + copy_finished: + sqlite3BtreeLeave(pFrom); + sqlite3BtreeLeave(pTo); +@@ -75905,7 +83911,9 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){ + + /* The szMalloc field holds the correct memory allocation size */ + assert( p->szMalloc==0 +- || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc) ); ++ || (p->flags==MEM_Undefined ++ && p->szMalloc<=sqlite3DbMallocSize(p->db,p->zMalloc)) ++ || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc)); + + /* If p holds a string or blob, the Mem.z must point to exactly + ** one of the following: +@@ -75942,9 +83950,9 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ + i64 x; + assert( (p->flags&MEM_Int)*2==sizeof(x) ); + memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); +- sqlite3Int64ToText(x, zBuf); ++ p->n = sqlite3Int64ToText(x, zBuf); + #else +- sqlite3Int64ToText(p->u.i, zBuf); ++ p->n = sqlite3Int64ToText(p->u.i, zBuf); + #endif + }else{ + sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); +@@ -75952,6 +83960,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ + (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); + assert( acc.zText==zBuf && acc.mxAlloc<=0 ); + zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ ++ p->n = acc.nChar; + } + } + +@@ -75968,7 +83977,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ + ** corresponding string value, then it is important that the string be + ** derived from the numeric value, not the other way around, to ensure + ** that the index and table are consistent. See ticket +-** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for ++** https://sqlite.org/src/info/343634942dd54ab (2018-01-31) for + ** an example. + ** + ** This routine looks at pMem to verify that if it has both a numeric +@@ -75979,10 +83988,12 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ + ** This routine is for use inside of assert() statements only. + */ + SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){ ++ Mem tmp; + char zBuf[100]; + char *z; + int i, j, incr; + if( (p->flags & MEM_Str)==0 ) return 1; ++ if( p->db && p->db->mallocFailed ) return 1; + if( p->flags & MEM_Term ){ + /* Insure that the string is properly zero-terminated. Pay particular + ** attention to the case where p->n is odd */ +@@ -75995,7 +84006,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){ + assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 ); + } + if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1; +- vdbeMemRenderNum(sizeof(zBuf), zBuf, p); ++ memcpy(&tmp, p, sizeof(tmp)); ++ vdbeMemRenderNum(sizeof(zBuf), zBuf, &tmp); + z = p->z; + i = j = 0; + incr = 1; +@@ -76028,10 +84040,15 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ + #ifndef SQLITE_OMIT_UTF16 + int rc; + #endif ++ assert( pMem!=0 ); + assert( !sqlite3VdbeMemIsRowSet(pMem) ); + assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE + || desiredEnc==SQLITE_UTF16BE ); +- if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){ ++ if( !(pMem->flags&MEM_Str) ){ ++ pMem->enc = desiredEnc; ++ return SQLITE_OK; ++ } ++ if( pMem->enc==desiredEnc ){ + return SQLITE_OK; + } + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); +@@ -76069,7 +84086,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre + testcase( bPreserve && pMem->z==0 ); + + assert( pMem->szMalloc==0 +- || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); ++ || (pMem->flags==MEM_Undefined ++ && pMem->szMalloc<=sqlite3DbMallocSize(pMem->db,pMem->zMalloc)) ++ || pMem->szMalloc==sqlite3DbMallocSize(pMem->db,pMem->zMalloc)); + if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ + if( pMem->db ){ + pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n); +@@ -76131,6 +84150,40 @@ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ + return SQLITE_OK; + } + ++/* ++** If pMem is already a string, detect if it is a zero-terminated ++** string, or make it into one if possible, and mark it as such. ++** ++** This is an optimization. Correct operation continues even if ++** this routine is a no-op. ++*/ ++SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ ++ if( (pMem->flags & (MEM_Str|MEM_Term|MEM_Ephem|MEM_Static))!=MEM_Str ){ ++ /* pMem must be a string, and it cannot be an ephemeral or static string */ ++ return; ++ } ++ if( pMem->enc!=SQLITE_UTF8 ) return; ++ assert( pMem->z!=0 ); ++ if( pMem->flags & MEM_Dyn ){ ++ if( pMem->xDel==sqlite3_free ++ && sqlite3_msize(pMem->z) >= (u64)(pMem->n+1) ++ ){ ++ pMem->z[pMem->n] = 0; ++ pMem->flags |= MEM_Term; ++ return; ++ } ++ if( pMem->xDel==sqlite3RCStrUnref ){ ++ /* Blindly assume that all RCStr objects are zero-terminated */ ++ pMem->flags |= MEM_Term; ++ return; ++ } ++ }else if( pMem->szMalloc >= pMem->n+1 ){ ++ pMem->z[pMem->n] = 0; ++ pMem->flags |= MEM_Term; ++ return; ++ } ++} ++ + /* + ** It is already known that pMem contains an unterminated string. + ** Add the zero terminator. +@@ -76158,6 +84211,7 @@ static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){ + ** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. + */ + SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( !sqlite3VdbeMemIsRowSet(pMem) ); + if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){ +@@ -76182,6 +84236,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ + #ifndef SQLITE_OMIT_INCRBLOB + SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ + int nByte; ++ assert( pMem!=0 ); + assert( pMem->flags & MEM_Zero ); + assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); + testcase( sqlite3_value_nochange(pMem) ); +@@ -76197,6 +84252,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ + if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){ + return SQLITE_NOMEM_BKPT; + } ++ assert( pMem->z!=0 ); ++ assert( sqlite3DbMallocSize(pMem->db,pMem->z) >= nByte ); + + memset(&pMem->z[pMem->n], 0, pMem->u.nZero); + pMem->n += pMem->u.nZero; +@@ -76209,6 +84266,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ + ** Make sure the given Mem is \u0000 terminated. + */ + SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){ ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) ); + testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 ); +@@ -76236,6 +84294,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){ + SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ + const int nByte = 32; + ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( !(pMem->flags&MEM_Zero) ); + assert( !(pMem->flags&(MEM_Str|MEM_Blob)) ); +@@ -76251,7 +84310,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ + + vdbeMemRenderNum(nByte, pMem->z, pMem); + assert( pMem->z!=0 ); +- pMem->n = sqlite3Strlen30NN(pMem->z); ++ assert( pMem->n==(int)sqlite3Strlen30NN(pMem->z) ); + pMem->enc = SQLITE_UTF8; + pMem->flags |= MEM_Str|MEM_Term; + if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); +@@ -76271,9 +84330,11 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ + sqlite3_context ctx; + Mem t; + assert( pFunc!=0 ); ++ assert( pMem!=0 ); ++ assert( pMem->db!=0 ); + assert( pFunc->xFinalize!=0 ); + assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); +- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); ++ assert( sqlite3_mutex_held(pMem->db->mutex) ); + memset(&ctx, 0, sizeof(ctx)); + memset(&t, 0, sizeof(t)); + t.flags = MEM_Null; +@@ -76281,6 +84342,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ + ctx.pOut = &t; + ctx.pMem = pMem; + ctx.pFunc = pFunc; ++ ctx.enc = ENC(t.db); + pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ + assert( (pMem->flags & MEM_Dyn)==0 ); + if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc); +@@ -76302,12 +84364,14 @@ SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc + 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) ); ++ assert( pAccum->db!=0 ); ++ assert( sqlite3_mutex_held(pAccum->db->mutex) ); + memset(&ctx, 0, sizeof(ctx)); + sqlite3VdbeMemSetNull(pOut); + ctx.pOut = pOut; + ctx.pMem = pAccum; + ctx.pFunc = pFunc; ++ ctx.enc = ENC(pAccum->db); + pFunc->xValue(&ctx); + return ctx.isError; + } +@@ -76373,34 +84437,12 @@ SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){ + } + } + +-/* +-** Convert a 64-bit IEEE double into a 64-bit signed integer. +-** If the double is out of range of a 64-bit signed integer then +-** return the closest available 64-bit signed integer. ++/* Like sqlite3VdbeMemRelease() but faster for cases where we ++** know in advance that the Mem is not MEM_Dyn or MEM_Agg. + */ +-static SQLITE_NOINLINE i64 doubleToInt64(double r){ +-#ifdef SQLITE_OMIT_FLOATING_POINT +- /* When floating-point is omitted, double and int64 are the same thing */ +- return r; +-#else +- /* +- ** Many compilers we encounter do not define constants for the +- ** minimum and maximum 64-bit integers, or they define them +- ** inconsistently. And many do not understand the "LL" notation. +- ** So we define our own static constants here using nothing +- ** larger than a 32-bit integer constant. +- */ +- static const i64 maxInt = LARGEST_INT64; +- static const i64 minInt = SMALLEST_INT64; +- +- if( r<=(double)minInt ){ +- return minInt; +- }else if( r>=(double)maxInt ){ +- return maxInt; +- }else{ +- return (i64)r; +- } +-#endif ++SQLITE_PRIVATE void sqlite3VdbeMemReleaseMalloc(Mem *p){ ++ assert( !VdbeMemDynamic(p) ); ++ if( p->szMalloc ) vdbeMemClear(p); + } + + /* +@@ -76414,13 +84456,14 @@ static SQLITE_NOINLINE i64 doubleToInt64(double r){ + ** + ** If pMem represents a string value, its encoding might be changed. + */ +-static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){ ++static SQLITE_NOINLINE i64 memIntValue(const Mem *pMem){ + i64 value = 0; + sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); + return value; + } +-SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){ ++SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem *pMem){ + int flags; ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + flags = pMem->flags; +@@ -76428,7 +84471,7 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){ + testcase( flags & MEM_IntReal ); + return pMem->u.i; + }else if( flags & MEM_Real ){ +- return doubleToInt64(pMem->u.r); ++ return sqlite3RealToI64(pMem->u.r); + }else if( (flags & (MEM_Str|MEM_Blob))!=0 && pMem->z!=0 ){ + return memIntValue(pMem); + }else{ +@@ -76449,6 +84492,7 @@ static SQLITE_NOINLINE double memRealValue(Mem *pMem){ + return val; + } + SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){ ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + if( pMem->flags & MEM_Real ){ +@@ -76476,31 +84520,35 @@ SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){ + } + + /* +-** The MEM structure is already a MEM_Real. Try to also make it a +-** MEM_Int if we can. ++** The MEM structure is already a MEM_Real or MEM_IntReal. Try to ++** make it a MEM_Int if we can. + */ + SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ +- i64 ix; +- assert( pMem->flags & MEM_Real ); ++ assert( pMem!=0 ); ++ assert( pMem->flags & (MEM_Real|MEM_IntReal) ); + assert( !sqlite3VdbeMemIsRowSet(pMem) ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + +- ix = doubleToInt64(pMem->u.r); +- +- /* Only mark the value as an integer if +- ** +- ** (1) the round-trip conversion real->int->real is a no-op, and +- ** (2) The integer is neither the largest nor the smallest +- ** possible integer (ticket #3922) +- ** +- ** The second and third terms in the following conditional enforces +- ** the second condition under the assumption that addition overflow causes +- ** values to wrap around. +- */ +- if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; ++ if( pMem->flags & MEM_IntReal ){ + MemSetTypeFlag(pMem, MEM_Int); ++ }else{ ++ i64 ix = sqlite3RealToI64(pMem->u.r); ++ ++ /* Only mark the value as an integer if ++ ** ++ ** (1) the round-trip conversion real->int->real is a no-op, and ++ ** (2) The integer is neither the largest nor the smallest ++ ** possible integer (ticket #3922) ++ ** ++ ** The second and third terms in the following conditional enforces ++ ** the second condition under the assumption that addition overflow causes ++ ** values to wrap around. ++ */ ++ if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; ++ MemSetTypeFlag(pMem, MEM_Int); ++ } + } + } + +@@ -76508,6 +84556,7 @@ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ + ** Convert pMem to type integer. Invalidate any prior representations. + */ + SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){ ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( !sqlite3VdbeMemIsRowSet(pMem) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); +@@ -76522,6 +84571,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){ + ** Invalidate any prior representations. + */ + SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){ ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + +@@ -76546,6 +84596,16 @@ SQLITE_PRIVATE int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){ + && i >= -2251799813685248LL && i < 2251799813685248LL); + } + ++/* Convert a floating point value to its closest integer. Do so in ++** a way that avoids 'outside the range of representable values' warnings ++** from UBSAN. ++*/ ++SQLITE_PRIVATE i64 sqlite3RealToI64(double r){ ++ if( r<-9223372036854774784.0 ) return SMALLEST_INT64; ++ if( r>+9223372036854774784.0 ) return LARGEST_INT64; ++ return (i64)r; ++} ++ + /* + ** Convert pMem so that it has type MEM_Real or MEM_Int. + ** Invalidate any prior representations. +@@ -76555,6 +84615,7 @@ SQLITE_PRIVATE int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){ + ** as much of the string as we can and ignore the rest. + */ + SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ ++ assert( pMem!=0 ); + testcase( pMem->flags & MEM_Int ); + testcase( pMem->flags & MEM_Real ); + testcase( pMem->flags & MEM_IntReal ); +@@ -76566,7 +84627,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); + if( ((rc==0 || rc==1) && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1) +- || sqlite3RealSameAsInt(pMem->u.r, (ix = (i64)pMem->u.r)) ++ || sqlite3RealSameAsInt(pMem->u.r, (ix = sqlite3RealToI64(pMem->u.r))) + ){ + pMem->u.i = ix; + MemSetTypeFlag(pMem, MEM_Int); +@@ -76612,13 +84673,17 @@ SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){ + break; + } + default: { ++ int rc; + assert( aff==SQLITE_AFF_TEXT ); + assert( MEM_Str==(MEM_Blob>>3) ); + pMem->flags |= (pMem->flags&MEM_Blob)>>3; + sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding); + assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); + pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero); +- return sqlite3VdbeChangeEncoding(pMem, encoding); ++ if( encoding!=SQLITE_UTF8 ) pMem->n &= ~1; ++ rc = sqlite3VdbeChangeEncoding(pMem, encoding); ++ if( rc ) return rc; ++ sqlite3VdbeMemZeroTerminateIfAble(pMem); + } + } + return SQLITE_OK; +@@ -76664,6 +84729,7 @@ SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){ + ** Delete any previous value and set the value to be a BLOB of length + ** n containing all zeros. + */ ++#ifndef SQLITE_OMIT_INCRBLOB + SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ + sqlite3VdbeMemRelease(pMem); + pMem->flags = MEM_Blob|MEM_Zero; +@@ -76673,6 +84739,21 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ + pMem->enc = SQLITE_UTF8; + pMem->z = 0; + } ++#else ++SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ ++ int nByte = n>0?n:1; ++ if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){ ++ return SQLITE_NOMEM_BKPT; ++ } ++ assert( pMem->z!=0 ); ++ assert( sqlite3DbMallocSize(pMem->db, pMem->z)>=nByte ); ++ memset(pMem->z, 0, nByte); ++ pMem->n = n>0?n:0; ++ pMem->flags = MEM_Blob; ++ pMem->enc = SQLITE_UTF8; ++ return SQLITE_OK; ++} ++#endif + + /* + ** The pMem is known to contain content that needs to be destroyed prior +@@ -76698,6 +84779,13 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ + } + } + ++/* ++** Set the iIdx'th entry of array aMem[] to contain integer value val. ++*/ ++SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){ ++ sqlite3VdbeMemSetInt64(&aMem[iIdx], val); ++} ++ + /* A no-op destructor */ + SQLITE_PRIVATE void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } + +@@ -76712,6 +84800,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetPointer( + void (*xDestructor)(void*) + ){ + assert( pMem->flags==MEM_Null ); ++ vdbeMemClear(pMem); + pMem->u.zPType = zPType ? zPType : ""; + pMem->z = pPtr; + pMem->flags = MEM_Null|MEM_Dyn|MEM_Subtype|MEM_Term; +@@ -76793,27 +84882,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){ + SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ + int i; + Mem *pX; +- for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ +- if( pX->pScopyFrom==pMem ){ +- u16 mFlags; +- if( pVdbe->db->flags & SQLITE_VdbeTrace ){ +- sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", +- (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); +- } +- /* If pX is marked as a shallow copy of pMem, then try to verify that +- ** no significant changes have been made to pX since the OP_SCopy. +- ** A significant change would indicated a missed call to this +- ** function for pX. Minor changes, such as adding or removing a +- ** dual type, are allowed, as long as the underlying value is the +- ** same. */ +- mFlags = pMem->flags & pX->flags & pX->mScopyFlags; +- assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); +- +- /* pMem is the register that is changing. But also mark pX as +- ** undefined so that we can quickly detect the shallow-copy error */ +- pX->flags = MEM_Undefined; +- pX->pScopyFrom = 0; +- } ++ if( pMem->bScopy ){ ++ for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ ++ if( pX->pScopyFrom==pMem ){ ++ u16 mFlags; ++ if( pVdbe->db->flags & SQLITE_VdbeTrace ){ ++ sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", ++ (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); ++ } ++ /* If pX is marked as a shallow copy of pMem, then try to verify that ++ ** no significant changes have been made to pX since the OP_SCopy. ++ ** A significant change would indicated a missed call to this ++ ** function for pX. Minor changes, such as adding or removing a ++ ** dual type, are allowed, as long as the underlying value is the ++ ** same. */ ++ mFlags = pMem->flags & pX->flags & pX->mScopyFlags; ++ assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); ++ ++ /* pMem is the register that is changing. But also mark pX as ++ ** undefined so that we can quickly detect the shallow-copy error */ ++ pX->flags = MEM_Undefined; ++ pX->pScopyFrom = 0; ++ } ++ } ++ pMem->bScopy = 0; + } + pMem->pScopyFrom = 0; + } +@@ -76894,20 +84986,29 @@ SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){ + ** stored without allocating memory, then it is. If a memory allocation + ** is required to store the string, then value of pMem is unchanged. In + ** either case, SQLITE_TOOBIG is returned. ++** ++** The "enc" parameter is the text encoding for the string, or zero ++** to store a blob. ++** ++** If n is negative, then the string consists of all bytes up to but ++** excluding the first zero character. The n parameter must be ++** non-negative for blobs. + */ + SQLITE_PRIVATE int sqlite3VdbeMemSetStr( + Mem *pMem, /* Memory cell to set to string value */ + const char *z, /* String pointer */ +- int n, /* Bytes in string, or negative */ ++ i64 n, /* Bytes in string, or negative */ + u8 enc, /* Encoding of z. 0 for BLOBs */ + void (*xDel)(void*) /* Destructor function */ + ){ +- int nByte = n; /* New value for pMem->n */ ++ i64 nByte = n; /* New value for pMem->n */ + int iLimit; /* Maximum allowed string or blob size */ +- u16 flags = 0; /* New value for pMem->flags */ ++ u16 flags; /* New value for pMem->flags */ + ++ assert( pMem!=0 ); + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( !sqlite3VdbeMemIsRowSet(pMem) ); ++ assert( enc!=0 || n>=0 ); + + /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ + if( !z ){ +@@ -76920,15 +85021,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( + }else{ + iLimit = SQLITE_MAX_LENGTH; + } +- flags = (enc==0?MEM_Blob:MEM_Str); + if( nByte<0 ){ + assert( enc!=0 ); + if( enc==SQLITE_UTF8 ){ +- nByte = 0x7fffffff & (int)strlen(z); ++ nByte = strlen(z); + }else{ + for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} + } +- flags |= MEM_Term; ++ flags= MEM_Str|MEM_Term; ++ }else if( enc==0 ){ ++ flags = MEM_Blob; ++ enc = SQLITE_UTF8; ++ }else{ ++ flags = MEM_Str; ++ } ++ if( nByte>iLimit ){ ++ if( xDel && xDel!=SQLITE_TRANSIENT ){ ++ if( xDel==SQLITE_DYNAMIC ){ ++ sqlite3DbFree(pMem->db, (void*)z); ++ }else{ ++ xDel((void*)z); ++ } ++ } ++ sqlite3VdbeMemSetNull(pMem); ++ return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); + } + + /* The following block sets the new values of Mem.z and Mem.xDel. It +@@ -76936,13 +85052,10 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( + ** management (one of MEM_Dyn or MEM_Static). + */ + if( xDel==SQLITE_TRANSIENT ){ +- u32 nAlloc = nByte; ++ i64 nAlloc = nByte; + if( flags&MEM_Term ){ + nAlloc += (enc==SQLITE_UTF8?1:2); + } +- if( nByte>iLimit ){ +- return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); +- } + testcase( nAlloc==0 ); + testcase( nAlloc==31 ); + testcase( nAlloc==32 ); +@@ -76962,18 +85075,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( + } + } + +- pMem->n = nByte; ++ pMem->n = (int)(nByte & 0x7fffffff); + pMem->flags = flags; +- if( enc ){ +- pMem->enc = enc; +-#ifdef SQLITE_ENABLE_SESSION +- }else if( pMem->db==0 ){ +- pMem->enc = SQLITE_UTF8; +-#endif +- }else{ +- assert( pMem->db!=0 ); +- pMem->enc = ENC(pMem->db); +- } ++ pMem->enc = enc; + + #ifndef SQLITE_OMIT_UTF16 + if( enc>SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){ +@@ -76981,9 +85085,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( + } + #endif + +- if( nByte>iLimit ){ +- return SQLITE_TOOBIG; +- } + + return SQLITE_OK; + } +@@ -77116,6 +85217,24 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ + return valueToText(pVal, enc); + } + ++/* Return true if sqlit3_value object pVal is a string or blob value ++** that uses the destructor specified in the second argument. ++** ++** TODO: Maybe someday promote this interface into a published API so ++** that third-party extensions can get access to it? ++*/ ++SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){ ++ if( ALWAYS(pVal!=0) ++ && ALWAYS((pVal->flags & (MEM_Str|MEM_Blob))!=0) ++ && (pVal->flags & MEM_Dyn)!=0 ++ && pVal->xDel==xFree ++ ){ ++ return 1; ++ }else{ ++ return 0; ++ } ++} ++ + /* + ** Create a new sqlite3_value object. + */ +@@ -77157,7 +85276,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ + + if( pRec==0 ){ + Index *pIdx = p->pIdx; /* Index being probed */ +- int nByte; /* Bytes of space to allocate */ ++ i64 nByte; /* Bytes of space to allocate */ + int i; /* Counter variable */ + int nCol = pIdx->nColumn; /* Number of index columns including rowid */ + +@@ -77183,6 +85302,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ + } + + pRec->nField = p->iVal+1; ++ sqlite3VdbeMemSetNull(&pRec->aMem[p->iVal]); + return &pRec->aMem[p->iVal]; + } + #else +@@ -77214,7 +85334,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ + #ifdef SQLITE_ENABLE_STAT4 + static int valueFromFunction( + sqlite3 *db, /* The database connection */ +- Expr *p, /* The expression to evaluate */ ++ const Expr *p, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 aff, /* Affinity to use */ + sqlite3_value **ppVal, /* Write the new value here */ +@@ -77222,7 +85342,7 @@ static int valueFromFunction( + ){ + sqlite3_context ctx; /* Context object for function invocation */ + sqlite3_value **apVal = 0; /* Function arguments */ +- int nVal = 0; /* Size of apVal[] array */ ++ int nVal = 0; /* Number of function arguments */ + FuncDef *pFunc = 0; /* Function definition */ + sqlite3_value *pVal = 0; /* New value */ + int rc = SQLITE_OK; /* Return code */ +@@ -77231,12 +85351,17 @@ static int valueFromFunction( + + assert( pCtx!=0 ); + assert( (p->flags & EP_TokenOnly)==0 ); ++ assert( ExprUseXList(p) ); + pList = p->x.pList; + if( pList ) nVal = pList->nExpr; ++ assert( !ExprHasProperty(p, EP_IntValue) ); + pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0); ++#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION ++ if( pFunc==0 ) return SQLITE_OK; ++#endif + assert( pFunc ); + if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 +- || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) ++ || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0 + ){ + return SQLITE_OK; + } +@@ -77248,7 +85373,8 @@ static int valueFromFunction( + goto value_from_function_out; + } + for(i=0; ia[i].pExpr, enc, aff, &apVal[i]); ++ rc = sqlite3Stat4ValueFromExpr(pCtx->pParse, pList->a[i].pExpr, aff, ++ &apVal[i]); + if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; + } + } +@@ -77259,10 +85385,10 @@ static int valueFromFunction( + goto value_from_function_out; + } + +- assert( pCtx->pParse->rc==SQLITE_OK ); + memset(&ctx, 0, sizeof(ctx)); + ctx.pOut = pVal; + ctx.pFunc = pFunc; ++ ctx.enc = ENC(db); + pFunc->xSFunc(&ctx, nVal, apVal); + if( ctx.isError ){ + rc = ctx.isError; +@@ -77271,16 +85397,16 @@ static int valueFromFunction( + sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); + assert( rc==SQLITE_OK ); + rc = sqlite3VdbeChangeEncoding(pVal, enc); +- if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){ ++ if( NEVER(rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal)) ){ + rc = SQLITE_TOOBIG; + pCtx->pParse->nErr++; + } + } +- pCtx->pParse->rc = rc; + + value_from_function_out: + if( rc!=SQLITE_OK ){ + pVal = 0; ++ pCtx->pParse->rc = rc; + } + if( apVal ){ + for(i=0; iop)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; +-#if defined(SQLITE_ENABLE_STAT4) + if( op==TK_REGISTER ) op = pExpr->op2; +-#else +- if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; +-#endif + + /* Compressed expressions only appear when parsing the DEFAULT clause + ** on a table column definition, and hence only when pCtx==0. This +@@ -77336,25 +85458,40 @@ static int valueFromExpr( + assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 ); + + if( op==TK_CAST ){ +- u8 aff = sqlite3AffinityType(pExpr->u.zToken,0); ++ u8 aff; ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ aff = sqlite3AffinityType(pExpr->u.zToken,0); + rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); + testcase( rc!=SQLITE_OK ); + if( *ppVal ){ +- sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8); +- sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8); ++#ifdef SQLITE_ENABLE_STAT4 ++ rc = ExpandBlob(*ppVal); ++#else ++ /* zero-blobs only come from functions, not literal values. And ++ ** functions are only processed under STAT4 */ ++ assert( (ppVal[0][0].flags & MEM_Zero)==0 ); ++#endif ++ sqlite3VdbeMemCast(*ppVal, aff, enc); ++ sqlite3ValueApplyAffinity(*ppVal, affinity, enc); + } + return rc; + } + + /* Handle negative integers in a single step. This is needed in the +- ** case when the value is -9223372036854775808. +- */ +- if( op==TK_UMINUS +- && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ +- pExpr = pExpr->pLeft; +- op = pExpr->op; +- negInt = -1; +- zNeg = "-"; ++ ** case when the value is -9223372036854775808. Except - do not do this ++ ** for hexadecimal literals. */ ++ if( op==TK_UMINUS ){ ++ Expr *pLeft = pExpr->pLeft; ++ if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){ ++ if( ExprHasProperty(pLeft, EP_IntValue) ++ || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X' ++ ){ ++ pExpr = pLeft; ++ op = pExpr->op; ++ negInt = -1; ++ zNeg = "-"; ++ } ++ } + } + + if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ +@@ -77363,12 +85500,26 @@ static int valueFromExpr( + if( ExprHasProperty(pExpr, EP_IntValue) ){ + sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); + }else{ +- zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); +- if( zVal==0 ) goto no_mem; +- sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); +- } +- if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){ +- sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); ++ i64 iVal; ++ if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){ ++ sqlite3VdbeMemSetInt64(pVal, iVal*negInt); ++ }else{ ++ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); ++ if( zVal==0 ) goto no_mem; ++ sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); ++ } ++ } ++ if( affinity==SQLITE_AFF_BLOB ){ ++ if( op==TK_FLOAT ){ ++ assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) ); ++ sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8); ++ pVal->flags = MEM_Real; ++ }else if( op==TK_INTEGER ){ ++ /* This case is required by -9223372036854775808 and other strings ++ ** that look like integers but cannot be handled by the ++ ** sqlite3DecOrHexToI64() call above. */ ++ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); ++ } + }else{ + sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); + } +@@ -77409,6 +85560,7 @@ static int valueFromExpr( + #ifndef SQLITE_OMIT_BLOB_LITERAL + else if( op==TK_BLOB ){ + int nVal; ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); + assert( pExpr->u.zToken[1]=='\'' ); + pVal = valueNew(db, pCtx); +@@ -77426,10 +85578,12 @@ static int valueFromExpr( + } + #endif + else if( op==TK_TRUEFALSE ){ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pVal = valueNew(db, pCtx); + if( pVal ){ + pVal->flags = MEM_Int; + pVal->u.i = pExpr->u.zToken[4]==0; ++ sqlite3ValueApplyAffinity(pVal, affinity, enc); + } + } + +@@ -77438,7 +85592,7 @@ static int valueFromExpr( + + no_mem: + #ifdef SQLITE_ENABLE_STAT4 +- if( pCtx==0 || pCtx->pParse->nErr==0 ) ++ if( pCtx==0 || NEVER(pCtx->pParse->nErr==0) ) + #endif + sqlite3OomFault(db); + sqlite3DbFree(db, zVal); +@@ -77463,7 +85617,7 @@ no_mem: + */ + SQLITE_PRIVATE int sqlite3ValueFromExpr( + sqlite3 *db, /* The database connection */ +- Expr *pExpr, /* The expression to evaluate */ ++ const Expr *pExpr, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 affinity, /* Affinity to use */ + sqlite3_value **ppVal /* Write the new value here */ +@@ -77635,17 +85789,17 @@ SQLITE_PRIVATE int sqlite3Stat4Column( + sqlite3_value **ppVal /* OUT: Extracted value */ + ){ + u32 t = 0; /* a column type code */ +- int nHdr; /* Size of the header in the record */ +- int iHdr; /* Next unread header byte */ +- int iField; /* Next unread data byte */ +- int szField = 0; /* Size of the current data field */ ++ u32 nHdr; /* Size of the header in the record */ ++ u32 iHdr; /* Next unread header byte */ ++ i64 iField; /* Next unread data byte */ ++ u32 szField = 0; /* Size of the current data field */ + int i; /* Column index */ + u8 *a = (u8*)pRec; /* Typecast byte array */ + Mem *pMem = *ppVal; /* Write result into this Mem object */ + + assert( iCol>0 ); + iHdr = getVarint32(a, nHdr); +- if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; ++ if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; + iField = nHdr; + for(i=0; i<=iCol; i++){ + iHdr += getVarint32(&a[iHdr], t); +@@ -77723,6 +85877,9 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){ + if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){ + return p->n; + } ++ if( (p->flags & MEM_Str)!=0 && enc!=SQLITE_UTF8 && pVal->enc!=SQLITE_UTF8 ){ ++ return p->n; ++ } + if( (p->flags & MEM_Blob)!=0 ){ + if( p->flags & MEM_Zero ){ + return p->n + p->u.nZero; +@@ -77768,12 +85925,12 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ + memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp)); + p->db = db; + if( db->pVdbe ){ +- db->pVdbe->pPrev = p; ++ db->pVdbe->ppVPrev = &p->pVNext; + } +- p->pNext = db->pVdbe; +- p->pPrev = 0; ++ p->pVNext = db->pVdbe; ++ p->ppVPrev = &db->pVdbe; + db->pVdbe = p; +- p->magic = VDBE_MAGIC_INIT; ++ assert( p->eVdbeState==VDBE_INIT_STATE ); + p->pParse = pParse; + pParse->pVdbe = p; + assert( pParse->aLabel==0 ); +@@ -77853,21 +86010,28 @@ SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString( + #endif + + /* +-** Swap all content between two VDBE structures. ++** Swap byte-code between two VDBE structures. ++** ++** This happens after pB was previously run and returned ++** SQLITE_SCHEMA. The statement was then reprepared in pA. ++** This routine transfers the new bytecode in pA over to pB ++** so that pB can be run again. The old pB byte code is ++** moved back to pA so that it will be cleaned up when pA is ++** finalized. + */ + SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ +- Vdbe tmp, *pTmp; ++ Vdbe tmp, *pTmp, **ppTmp; + char *zTmp; + assert( pA->db==pB->db ); + tmp = *pA; + *pA = *pB; + *pB = tmp; +- pTmp = pA->pNext; +- pA->pNext = pB->pNext; +- pB->pNext = pTmp; +- pTmp = pA->pPrev; +- pA->pPrev = pB->pPrev; +- pB->pPrev = pTmp; ++ pTmp = pA->pVNext; ++ pA->pVNext = pB->pVNext; ++ pB->pVNext = pTmp; ++ ppTmp = pA->ppVPrev; ++ pA->ppVPrev = pB->ppVPrev; ++ pB->ppVPrev = ppTmp; + zTmp = pA->zSql; + pA->zSql = pB->zSql; + pB->zSql = zTmp; +@@ -77918,7 +86082,7 @@ static int growOpArray(Vdbe *v, int nOp){ + return SQLITE_NOMEM; + } + +- assert( nOp<=(1024/sizeof(Op)) ); ++ assert( nOp<=(int)(1024/sizeof(Op)) ); + assert( nNew>=(v->nOpAlloc+nOp) ); + pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); + if( pNew ){ +@@ -77942,11 +86106,43 @@ static int growOpArray(Vdbe *v, int nOp){ + ** sqlite3CantopenError(lineno) + */ + static void test_addop_breakpoint(int pc, Op *pOp){ +- static int n = 0; ++ static u64 n = 0; ++ (void)pc; ++ (void)pOp; + n++; ++ if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */ + } + #endif + ++/* ++** Slow paths for sqlite3VdbeAddOp3() and sqlite3VdbeAddOp4Int() for the ++** unusual case when we need to increase the size of the Vdbe.aOp[] array ++** before adding the new opcode. ++*/ ++static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ ++ assert( p->nOpAlloc<=p->nOp ); ++ if( growOpArray(p, 1) ) return 1; ++ assert( p->nOpAlloc>p->nOp ); ++ return sqlite3VdbeAddOp3(p, op, p1, p2, p3); ++} ++static SQLITE_NOINLINE int addOp4IntSlow( ++ Vdbe *p, /* Add the opcode to this VM */ ++ int op, /* The new opcode */ ++ int p1, /* The P1 operand */ ++ int p2, /* The P2 operand */ ++ int p3, /* The P3 operand */ ++ int p4 /* The P4 operand as an integer */ ++){ ++ int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); ++ if( p->db->mallocFailed==0 ){ ++ VdbeOp *pOp = &p->aOp[addr]; ++ pOp->p4type = P4_INT32; ++ pOp->p4.i = p4; ++ } ++ return addr; ++} ++ ++ + /* + ** Add a new instruction to the list of instructions current in the + ** VDBE. Return the address of the new instruction. +@@ -77957,30 +86153,31 @@ static void test_addop_breakpoint(int pc, Op *pOp){ + ** + ** op The opcode for this instruction + ** +-** p1, p2, p3 Operands +-** +-** Use the sqlite3VdbeResolveLabel() function to fix an address and +-** the sqlite3VdbeChangeP4() function to change the value of the P4 +-** operand. ++** p1, p2, p3, p4 Operands + */ +-static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ +- assert( p->nOpAlloc<=p->nOp ); +- if( growOpArray(p, 1) ) return 1; +- assert( p->nOpAlloc>p->nOp ); +- return sqlite3VdbeAddOp3(p, op, p1, p2, p3); ++SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ ++ return sqlite3VdbeAddOp3(p, op, 0, 0, 0); ++} ++SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ ++ return sqlite3VdbeAddOp3(p, op, p1, 0, 0); ++} ++SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ ++ return sqlite3VdbeAddOp3(p, op, p1, p2, 0); + } + SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ + int i; + VdbeOp *pOp; + + i = p->nOp; +- assert( p->magic==VDBE_MAGIC_INIT ); ++ assert( p->eVdbeState==VDBE_INIT_STATE ); + assert( op>=0 && op<0xff ); + if( p->nOpAlloc<=i ){ + return growOp3(p, op, p1, p2, p3); + } ++ assert( p->aOp!=0 ); + p->nOp++; + pOp = &p->aOp[i]; ++ assert( pOp!=0 ); + pOp->opcode = (u8)op; + pOp->p5 = 0; + pOp->p1 = p1; +@@ -77988,32 +86185,78 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ + pOp->p3 = p3; + pOp->p4.p = 0; + pOp->p4type = P4_NOTUSED; ++ ++ /* Replicate this logic in sqlite3VdbeAddOp4Int() ++ ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ + #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + pOp->zComment = 0; + #endif ++#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) ++ pOp->nExec = 0; ++ pOp->nCycle = 0; ++#endif + #ifdef SQLITE_DEBUG + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + sqlite3VdbePrintOp(0, i, &p->aOp[i]); + test_addop_breakpoint(i, &p->aOp[i]); + } + #endif +-#ifdef VDBE_PROFILE +- pOp->cycles = 0; +- pOp->cnt = 0; +-#endif + #ifdef SQLITE_VDBE_COVERAGE + pOp->iSrcLine = 0; + #endif ++ /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ** Replicate in sqlite3VdbeAddOp4Int() */ ++ + return i; + } +-SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ +- return sqlite3VdbeAddOp3(p, op, 0, 0, 0); +-} +-SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ +- return sqlite3VdbeAddOp3(p, op, p1, 0, 0); +-} +-SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ +- return sqlite3VdbeAddOp3(p, op, p1, p2, 0); ++SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( ++ Vdbe *p, /* Add the opcode to this VM */ ++ int op, /* The new opcode */ ++ int p1, /* The P1 operand */ ++ int p2, /* The P2 operand */ ++ int p3, /* The P3 operand */ ++ int p4 /* The P4 operand as an integer */ ++){ ++ int i; ++ VdbeOp *pOp; ++ ++ i = p->nOp; ++ if( p->nOpAlloc<=i ){ ++ return addOp4IntSlow(p, op, p1, p2, p3, p4); ++ } ++ p->nOp++; ++ pOp = &p->aOp[i]; ++ assert( pOp!=0 ); ++ pOp->opcode = (u8)op; ++ pOp->p5 = 0; ++ pOp->p1 = p1; ++ pOp->p2 = p2; ++ pOp->p3 = p3; ++ pOp->p4.i = p4; ++ pOp->p4type = P4_INT32; ++ ++ /* Replicate this logic in sqlite3VdbeAddOp3() ++ ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ ++#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS ++ pOp->zComment = 0; ++#endif ++#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) ++ pOp->nExec = 0; ++ pOp->nCycle = 0; ++#endif ++#ifdef SQLITE_DEBUG ++ if( p->db->flags & SQLITE_VdbeAddopTrace ){ ++ sqlite3VdbePrintOp(0, i, &p->aOp[i]); ++ test_addop_breakpoint(i, &p->aOp[i]); ++ } ++#endif ++#ifdef SQLITE_VDBE_COVERAGE ++ pOp->iSrcLine = 0; ++#endif ++ /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ** Replicate in sqlite3VdbeAddOp3() */ ++ ++ return i; + } + + /* Generate code for an unconditional jump to instruction iDest +@@ -78097,12 +86340,10 @@ SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall( + int eCallCtx /* Calling context */ + ){ + Vdbe *v = pParse->pVdbe; +- int nByte; + int addr; + sqlite3_context *pCtx; + assert( v ); +- nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*); +- pCtx = sqlite3DbMallocRawNN(pParse->db, nByte); ++ pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg)); + if( pCtx==0 ){ + assert( pParse->db->mallocFailed ); + freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); +@@ -78117,6 +86358,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall( + addr = sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function, + p1, p2, p3, (char*)pCtx, P4_FUNCCTX); + sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef); ++ sqlite3MayAbort(pParse); + return addr; + } + +@@ -78167,11 +86409,12 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){ + ** If the bPush flag is true, then make this opcode the parent for + ** subsequent Explains until sqlite3VdbeExplainPop() is called. + */ +-SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ +-#ifndef SQLITE_DEBUG ++SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ ++ int addr = 0; ++#if !defined(SQLITE_DEBUG) + /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. + ** But omit them (for performance) during production builds */ +- if( pParse->explain==2 ) ++ if( pParse->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) + #endif + { + char *zMsg; +@@ -78183,13 +86426,15 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt + va_end(ap); + v = pParse->pVdbe; + iThis = v->nOp; +- sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, ++ addr = sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, + zMsg, P4_DYNAMIC); +- sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetOp(v,-1)->p4.z); ++ sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetLastOp(v)->p4.z); + if( bPush){ + pParse->addrExplain = iThis; + } ++ sqlite3VdbeScanStatus(v, iThis, -1, -1, 0, 0); + } ++ return addr; + } + + /* +@@ -78209,30 +86454,12 @@ SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse *pParse){ + ** The zWhere string must have been obtained from sqlite3_malloc(). + ** This routine will take ownership of the allocated memory. + */ +-SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ ++SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, u16 p5){ + int j; + sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); ++ sqlite3VdbeChangeP5(p, p5); + for(j=0; jdb->nDb; j++) sqlite3VdbeUsesBtree(p, j); +-} +- +-/* +-** Add an opcode that includes the p4 value as an integer. +-*/ +-SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( +- Vdbe *p, /* Add the opcode to this VM */ +- int op, /* The new opcode */ +- int p1, /* The P1 operand */ +- int p2, /* The P2 operand */ +- int p3, /* The P3 operand */ +- int p4 /* The P4 operand as an integer */ +-){ +- int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); +- if( p->db->mallocFailed==0 ){ +- VdbeOp *pOp = &p->aOp[addr]; +- pOp->p4type = P4_INT32; +- pOp->p4.i = p4; +- } +- return addr; ++ sqlite3MayAbort(p->pParse); + } + + /* Insert the end of a co-routine +@@ -78295,6 +86522,9 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ + int i; + for(i=p->nLabelAlloc; iaLabel[i] = -1; + #endif ++ if( nNewSize>=100 && (nNewSize/100)>(p->nLabelAlloc/100) ){ ++ sqlite3ProgressCheck(p); ++ } + p->nLabelAlloc = nNewSize; + p->aLabel[j] = v->nOp; + } +@@ -78302,7 +86532,7 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ + SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ + Parse *p = v->pParse; + int j = ADDR(x); +- assert( v->magic==VDBE_MAGIC_INIT ); ++ assert( v->eVdbeState==VDBE_INIT_STATE ); + assert( j<-p->nLabel ); + assert( j>=0 ); + #ifdef SQLITE_DEBUG +@@ -78322,14 +86552,20 @@ SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ + ** Mark the VDBE as one that can only be run one time. + */ + SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe *p){ +- p->runOnlyOnce = 1; ++ sqlite3VdbeAddOp2(p, OP_Expire, 1, 1); + } + + /* +-** Mark the VDBE as one that can only be run multiple times. ++** Mark the VDBE as one that can be run multiple times. + */ + SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe *p){ +- p->runOnlyOnce = 0; ++ int i; ++ for(i=1; ALWAYS(inOp); i++){ ++ if( ALWAYS(p->aOp[i].opcode==OP_Expire) ){ ++ p->aOp[1].opcode = OP_Noop; ++ break; ++ } ++ } + } + + #ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */ +@@ -78383,7 +86619,7 @@ static Op *opIterNext(VdbeOpIter *p){ + } + + if( pRet->p4type==P4_SUBPROGRAM ){ +- int nByte = (p->nSub+1)*sizeof(SubProgram*); ++ i64 nByte = (1+(u64)p->nSub)*sizeof(SubProgram*); + int j; + for(j=0; jnSub; j++){ + if( p->apSub[j]==pRet->p4.pProgram ) break; +@@ -78433,6 +86669,8 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ + int hasInitCoroutine = 0; + Op *pOp; + VdbeOpIter sIter; ++ ++ if( v==0 ) return 0; + memset(&sIter, 0, sizeof(sIter)); + sIter.v = v; + +@@ -78441,7 +86679,8 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ + if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename + || opcode==OP_VDestroy + || opcode==OP_VCreate +- || (opcode==OP_ParseSchema && pOp->p4.z==0) ++ || opcode==OP_ParseSchema ++ || opcode==OP_Function || opcode==OP_PureFunc + || ((opcode==OP_Halt || opcode==OP_HaltIfNull) + && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) + ){ +@@ -78510,13 +86749,13 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){ + ** (1) For each jump instruction with a negative P2 value (a label) + ** resolve the P2 value to an actual address. + ** +-** (2) Compute the maximum number of arguments used by any SQL function +-** and store that value in *pMaxFuncArgs. ++** (2) Compute the maximum number of arguments used by the xUpdate/xFilter ++** methods of any virtual table and store that value in *pMaxVtabArgs. + ** + ** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately + ** indicate what the prepared statement actually does. + ** +-** (4) Initialize the p4.xAdvance pointer on opcodes that use it. ++** (4) (discontinued) + ** + ** (5) Reclaim the memory allocated for storing labels. + ** +@@ -78524,16 +86763,18 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){ + ** script numbers the opcodes correctly. Changes to this routine must be + ** coordinated with changes to mkopcodeh.tcl. + */ +-static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ +- int nMaxArgs = *pMaxFuncArgs; ++static void resolveP2Values(Vdbe *p, int *pMaxVtabArgs){ ++ int nMaxVtabArgs = *pMaxVtabArgs; + Op *pOp; + Parse *pParse = p->pParse; + int *aLabel = pParse->aLabel; ++ ++ assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */ + p->readOnly = 1; + p->bIsReader = 0; + pOp = &p->aOp[p->nOp-1]; +- while(1){ +- ++ assert( p->aOp[0].opcode==OP_Init ); ++ while( 1 /* Loop terminates when it reaches the OP_Init opcode */ ){ + /* Only JUMP opcodes and the short list of special opcodes in the switch + ** below need to be considered. The mkopcodeh.tcl generator script groups + ** all these opcodes together near the front of the opcode list. Skip +@@ -78562,36 +86803,25 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ + p->bIsReader = 1; + break; + } +- case OP_Next: +- case OP_SorterNext: { +- pOp->p4.xAdvance = sqlite3BtreeNext; +- pOp->p4type = P4_ADVANCE; +- /* The code generator never codes any of these opcodes as a jump +- ** to a label. They are always coded as a jump backwards to a +- ** known address */ ++ case OP_Init: { + assert( pOp->p2>=0 ); +- break; +- } +- case OP_Prev: { +- pOp->p4.xAdvance = sqlite3BtreePrevious; +- pOp->p4type = P4_ADVANCE; +- /* The code generator never codes any of these opcodes as a jump +- ** to a label. They are always coded as a jump backwards to a +- ** known address */ +- assert( pOp->p2>=0 ); +- break; ++ goto resolve_p2_values_loop_exit; + } + #ifndef SQLITE_OMIT_VIRTUALTABLE + case OP_VUpdate: { +- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; ++ if( pOp->p2>nMaxVtabArgs ) nMaxVtabArgs = pOp->p2; + break; + } + case OP_VFilter: { + int n; ++ /* The instruction immediately prior to VFilter will be an ++ ** OP_Integer that sets the "argc" value for the VFilter. See ++ ** the code where OP_VFilter is generated at tag-20250207a. */ + assert( (pOp - p->aOp) >= 3 ); + assert( pOp[-1].opcode==OP_Integer ); ++ assert( pOp[-1].p2==pOp->p3+1 ); + n = pOp[-1].p1; +- if( n>nMaxArgs ) nMaxArgs = n; ++ if( n>nMaxVtabArgs ) nMaxVtabArgs = n; + /* Fall through into the default case */ + /* no break */ deliberate_fall_through + } +@@ -78603,8 +86833,18 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ + ** have non-negative values for P2. */ + assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); + assert( ADDR(pOp->p2)<-pParse->nLabel ); ++ assert( aLabel!=0 ); /* True because of tag-20230419-1 */ + pOp->p2 = aLabel[ADDR(pOp->p2)]; + } ++ ++ /* OPFLG_JUMP opcodes never have P2==0, though OPFLG_JUMP0 opcodes ++ ** might */ ++ assert( pOp->p2>0 ++ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP0)!=0 ); ++ ++ /* Jumps never go off the end of the bytecode array */ ++ assert( pOp->p2nOp ++ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)==0 ); + break; + } + } +@@ -78613,21 +86853,112 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ + ** have non-negative values for P2. */ + assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0); + } +- if( pOp==p->aOp ) break; ++ assert( pOp>p->aOp ); + pOp--; + } +- sqlite3DbFree(p->db, pParse->aLabel); +- pParse->aLabel = 0; ++resolve_p2_values_loop_exit: ++ if( aLabel ){ ++ sqlite3DbNNFreeNN(p->db, pParse->aLabel); ++ pParse->aLabel = 0; ++ } + pParse->nLabel = 0; +- *pMaxFuncArgs = nMaxArgs; ++ *pMaxVtabArgs = nMaxVtabArgs; + assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) ); + } + ++#ifdef SQLITE_DEBUG ++/* ++** Check to see if a subroutine contains a jump to a location outside of ++** the subroutine. If a jump outside the subroutine is detected, add code ++** that will cause the program to halt with an error message. ++** ++** The subroutine consists of opcodes between iFirst and iLast. Jumps to ++** locations within the subroutine are acceptable. iRetReg is a register ++** that contains the return address. Jumps to outside the range of iFirst ++** through iLast are also acceptable as long as the jump destination is ++** an OP_Return to iReturnAddr. ++** ++** A jump to an unresolved label means that the jump destination will be ++** beyond the current address. That is normally a jump to an early ++** termination and is consider acceptable. ++** ++** This routine only runs during debug builds. The purpose is (of course) ++** to detect invalid escapes out of a subroutine. The OP_Halt opcode ++** is generated rather than an assert() or other error, so that ".eqp full" ++** will still work to show the original bytecode, to aid in debugging. ++*/ ++SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn( ++ Vdbe *v, /* The byte-code program under construction */ ++ int iFirst, /* First opcode of the subroutine */ ++ int iLast, /* Last opcode of the subroutine */ ++ int iRetReg /* Subroutine return address register */ ++){ ++ VdbeOp *pOp; ++ Parse *pParse; ++ int i; ++ sqlite3_str *pErr = 0; ++ assert( v!=0 ); ++ pParse = v->pParse; ++ assert( pParse!=0 ); ++ if( pParse->nErr ) return; ++ assert( iLast>=iFirst ); ++ assert( iLastnOp ); ++ pOp = &v->aOp[iFirst]; ++ for(i=iFirst; i<=iLast; i++, pOp++){ ++ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ){ ++ int iDest = pOp->p2; /* Jump destination */ ++ if( iDest==0 ) continue; ++ if( pOp->opcode==OP_Gosub ) continue; ++ if( pOp->p3==20230325 && pOp->opcode==OP_NotNull ){ ++ /* This is a deliberately taken illegal branch. tag-20230325-2 */ ++ continue; ++ } ++ if( iDest<0 ){ ++ int j = ADDR(iDest); ++ assert( j>=0 ); ++ if( j>=-pParse->nLabel || pParse->aLabel[j]<0 ){ ++ continue; ++ } ++ iDest = pParse->aLabel[j]; ++ } ++ if( iDestiLast ){ ++ int j = iDest; ++ for(; jnOp; j++){ ++ VdbeOp *pX = &v->aOp[j]; ++ if( pX->opcode==OP_Return ){ ++ if( pX->p1==iRetReg ) break; ++ continue; ++ } ++ if( pX->opcode==OP_Noop ) continue; ++ if( pX->opcode==OP_Explain ) continue; ++ if( pErr==0 ){ ++ pErr = sqlite3_str_new(0); ++ }else{ ++ sqlite3_str_appendchar(pErr, 1, '\n'); ++ } ++ sqlite3_str_appendf(pErr, ++ "Opcode at %d jumps to %d which is outside the " ++ "subroutine at %d..%d", ++ i, iDest, iFirst, iLast); ++ break; ++ } ++ } ++ } ++ } ++ if( pErr ){ ++ char *zErr = sqlite3_str_finish(pErr); ++ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_INTERNAL, OE_Abort, 0, zErr, 0); ++ sqlite3_free(zErr); ++ sqlite3MayAbort(pParse); ++ } ++} ++#endif /* SQLITE_DEBUG */ ++ + /* + ** Return the address of the next instruction to be inserted. + */ + SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){ +- assert( p->magic==VDBE_MAGIC_INIT ); ++ assert( p->eVdbeState==VDBE_INIT_STATE ); + return p->nOp; + } + +@@ -78712,7 +87043,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList( + int i; + VdbeOp *pOut, *pFirst; + assert( nOp>0 ); +- assert( p->magic==VDBE_MAGIC_INIT ); ++ assert( p->eVdbeState==VDBE_INIT_STATE ); + if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ + return 0; + } +@@ -78759,20 +87090,83 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus( + LogEst nEst, /* Estimated number of output rows */ + const char *zName /* Name of table or index being scanned */ + ){ +- sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); +- ScanStatus *aNew; +- aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); +- if( aNew ){ +- ScanStatus *pNew = &aNew[p->nScan++]; +- pNew->addrExplain = addrExplain; +- pNew->addrLoop = addrLoop; +- pNew->addrVisit = addrVisit; +- pNew->nEst = nEst; +- pNew->zName = sqlite3DbStrDup(p->db, zName); +- p->aScan = aNew; ++ if( IS_STMT_SCANSTATUS(p->db) ){ ++ i64 nByte = (1+(i64)p->nScan) * sizeof(ScanStatus); ++ ScanStatus *aNew; ++ aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); ++ if( aNew ){ ++ ScanStatus *pNew = &aNew[p->nScan++]; ++ memset(pNew, 0, sizeof(ScanStatus)); ++ pNew->addrExplain = addrExplain; ++ pNew->addrLoop = addrLoop; ++ pNew->addrVisit = addrVisit; ++ pNew->nEst = nEst; ++ pNew->zName = sqlite3DbStrDup(p->db, zName); ++ p->aScan = aNew; ++ } + } + } +-#endif ++ ++/* ++** Add the range of instructions from addrStart to addrEnd (inclusive) to ++** the set of those corresponding to the sqlite3_stmt_scanstatus() counters ++** associated with the OP_Explain instruction at addrExplain. The ++** sum of the sqlite3Hwtime() values for each of these instructions ++** will be returned for SQLITE_SCANSTAT_NCYCLE requests. ++*/ ++SQLITE_PRIVATE void sqlite3VdbeScanStatusRange( ++ Vdbe *p, ++ int addrExplain, ++ int addrStart, ++ int addrEnd ++){ ++ if( IS_STMT_SCANSTATUS(p->db) ){ ++ ScanStatus *pScan = 0; ++ int ii; ++ for(ii=p->nScan-1; ii>=0; ii--){ ++ pScan = &p->aScan[ii]; ++ if( pScan->addrExplain==addrExplain ) break; ++ pScan = 0; ++ } ++ if( pScan ){ ++ if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1; ++ for(ii=0; iiaAddrRange); ii+=2){ ++ if( pScan->aAddrRange[ii]==0 ){ ++ pScan->aAddrRange[ii] = addrStart; ++ pScan->aAddrRange[ii+1] = addrEnd; ++ break; ++ } ++ } ++ } ++ } ++} ++ ++/* ++** Set the addresses for the SQLITE_SCANSTAT_NLOOP and SQLITE_SCANSTAT_NROW ++** counters for the query element associated with the OP_Explain at ++** addrExplain. ++*/ ++SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters( ++ Vdbe *p, ++ int addrExplain, ++ int addrLoop, ++ int addrVisit ++){ ++ if( IS_STMT_SCANSTATUS(p->db) ){ ++ ScanStatus *pScan = 0; ++ int ii; ++ for(ii=p->nScan-1; ii>=0; ii--){ ++ pScan = &p->aScan[ii]; ++ if( pScan->addrExplain==addrExplain ) break; ++ pScan = 0; ++ } ++ if( pScan ){ ++ if( addrLoop>0 ) pScan->addrLoop = addrLoop; ++ if( addrVisit>0 ) pScan->addrVisit = addrVisit; ++ } ++ } ++} ++#endif /* defined(SQLITE_ENABLE_STMT_SCANSTATUS) */ + + + /* +@@ -78780,15 +87174,19 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus( + ** for a specific instruction. + */ + SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){ ++ assert( addr>=0 ); + sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode; + } + SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){ ++ assert( addr>=0 ); + sqlite3VdbeGetOp(p,addr)->p1 = val; + } + SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){ ++ assert( addr>=0 || p->db->mallocFailed ); + sqlite3VdbeGetOp(p,addr)->p2 = val; + } + SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){ ++ assert( addr>=0 ); + sqlite3VdbeGetOp(p,addr)->p3 = val; + } + SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ +@@ -78796,6 +87194,21 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ + if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5; + } + ++/* ++** If the previous opcode is an OP_Column that delivers results ++** into register iDest, then add the OPFLAG_TYPEOFARG flag to that ++** opcode. ++*/ ++SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){ ++ VdbeOp *pOp = sqlite3VdbeGetLastOp(p); ++#ifdef SQLITE_DEBUG ++ while( pOp->opcode==OP_ReleaseReg ) pOp--; ++#endif ++ if( pOp->p3==iDest && pOp->opcode==OP_Column ){ ++ pOp->p5 |= OPFLAG_TYPEOFARG; ++ } ++} ++ + /* + ** Change the P2 operand of instruction addr so that it points to + ** the address of the next instruction to be coded. +@@ -78824,7 +87237,7 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ + || p->aOp[addr].opcode==OP_FkIfZero ); + assert( p->aOp[addr].p4type==0 ); + #ifdef SQLITE_VDBE_COVERAGE +- sqlite3VdbeGetOp(p,-1)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ ++ sqlite3VdbeGetLastOp(p)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ + #endif + p->nOp--; + }else{ +@@ -78835,11 +87248,12 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ + + /* + ** If the input FuncDef structure is ephemeral, then free it. If +-** the FuncDef is not ephermal, then do nothing. ++** the FuncDef is not ephemeral, then do nothing. + */ + static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ ++ assert( db!=0 ); + if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ +- sqlite3DbFreeNN(db, pDef); ++ sqlite3DbNNFreeNN(db, pDef); + } + } + +@@ -78848,11 +87262,12 @@ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ + */ + static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){ + if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); +- sqlite3DbFreeNN(db, p); ++ sqlite3DbNNFreeNN(db, p); + } + static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){ ++ assert( db!=0 ); + freeEphemeralFunction(db, p->pFunc); +- sqlite3DbFreeNN(db, p); ++ sqlite3DbNNFreeNN(db, p); + } + static void freeP4(sqlite3 *db, int p4type, void *p4){ + assert( db ); +@@ -78864,9 +87279,8 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ + case P4_REAL: + case P4_INT64: + case P4_DYNAMIC: +- case P4_DYNBLOB: + case P4_INTARRAY: { +- sqlite3DbFree(db, p4); ++ if( p4 ) sqlite3DbNNFreeNN(db, p4); + break; + } + case P4_KEYINFO: { +@@ -78895,6 +87309,16 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ + if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); + break; + } ++ case P4_TABLEREF: { ++ if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4); ++ break; ++ } ++ case P4_SUBRTNSIG: { ++ SubrtnSig *pSig = (SubrtnSig*)p4; ++ sqlite3DbFree(db, pSig->zAff); ++ sqlite3DbFree(db, pSig); ++ break; ++ } + } + } + +@@ -78904,15 +87328,19 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ + ** nOp entries. + */ + static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ ++ assert( nOp>=0 ); ++ assert( db!=0 ); + if( aOp ){ +- Op *pOp; +- for(pOp=&aOp[nOp-1]; pOp>=aOp; pOp--){ ++ Op *pOp = &aOp[nOp-1]; ++ while(1){ /* Exit via break */ + if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p); + #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + sqlite3DbFree(db, pOp->zComment); + #endif ++ if( pOp==aOp ) break; ++ pOp--; + } +- sqlite3DbFreeNN(db, aOp); ++ sqlite3DbNNFreeNN(db, aOp); + } + } + +@@ -78972,7 +87400,7 @@ SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters( + u32 mask, /* Mask of registers to NOT release */ + int bUndefine /* If true, mark registers as undefined */ + ){ +- if( N==0 ) return; ++ if( N==0 || OptimizationDisabled(pParse->db, SQLITE_ReleaseReg) ) return; + assert( pParse->pVdbe ); + assert( iFirst>=1 ); + assert( iFirst+N-1<=pParse->nMem ); +@@ -78994,7 +87422,6 @@ SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters( + } + #endif /* SQLITE_DEBUG */ + +- + /* + ** Change the value of the P4 operand for a specific instruction. + ** This routine is useful when a large program is loaded from a +@@ -79019,7 +87446,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full( + int n + ){ + if( pOp->p4type ){ +- freeP4(p->db, pOp->p4type, pOp->p4.p); ++ assert( pOp->p4type > P4_FREE_IF_LE ); + pOp->p4type = 0; + pOp->p4.p = 0; + } +@@ -79036,7 +87463,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int + sqlite3 *db; + assert( p!=0 ); + db = p->db; +- assert( p->magic==VDBE_MAGIC_INIT ); ++ assert( p->eVdbeState==VDBE_INIT_STATE ); + assert( p->aOp!=0 || db->mallocFailed ); + if( db->mallocFailed ){ + if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); +@@ -79081,7 +87508,7 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){ + if( p->db->mallocFailed ){ + freeP4(p->db, n, pP4); + }else{ +- assert( pP4!=0 ); ++ assert( pP4!=0 || n==P4_DYNAMIC ); + assert( p->nOp>0 ); + pOp = &p->aOp[p->nOp-1]; + assert( pOp->p4type==P4_NOTUSED ); +@@ -79112,8 +87539,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ + */ + static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){ + assert( p->nOp>0 || p->aOp==0 ); +- assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed +- || p->pParse->nErr>0 ); ++ assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->pParse->nErr>0 ); + if( p->nOp ){ + assert( p->aOp ); + sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); +@@ -79144,13 +87570,13 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ + ** Set the value if the iSrcLine field for the previously coded instruction. + */ + SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){ +- sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine; ++ sqlite3VdbeGetLastOp(v)->iSrcLine = iLine; + } + #endif /* SQLITE_VDBE_COVERAGE */ + + /* +-** Return the opcode for a given address. If the address is -1, then +-** return the most recently inserted opcode. ++** Return the opcode for a given address. The address must be non-negative. ++** See sqlite3VdbeGetLastOp() to get the most recently added opcode. + ** + ** If a memory allocation error has occurred prior to the calling of this + ** routine, then a pointer to a dummy VdbeOp will be returned. That opcode +@@ -79165,10 +87591,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ + /* C89 specifies that the constant "dummy" will be initialized to all + ** zeros, which is correct. MSVC generates a warning, nevertheless. */ + static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ +- assert( p->magic==VDBE_MAGIC_INIT ); +- if( addr<0 ){ +- addr = p->nOp - 1; +- } ++ assert( p->eVdbeState==VDBE_INIT_STATE ); + assert( (addr>=0 && addrnOp) || p->db->mallocFailed ); + if( p->db->mallocFailed ){ + return (VdbeOp*)&dummy; +@@ -79177,6 +87600,12 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ + } + } + ++/* Return the most recently added opcode ++*/ ++SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){ ++ return sqlite3VdbeGetOp(p, p->nOp - 1); ++} ++ + #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) + /* + ** Return an integer value for one of the parameters to the opcode pOp +@@ -79221,13 +87650,9 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( + if( zOpName[nOpName+1] ){ + int seenCom = 0; + char c; +- zSynopsis = zOpName += nOpName + 1; ++ zSynopsis = zOpName + nOpName + 1; + if( strncmp(zSynopsis,"IF ",3)==0 ){ +- if( pOp->p5 & SQLITE_STOREP2 ){ +- sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3); +- }else{ +- sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); +- } ++ sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); + zSynopsis = zAlt; + } + for(ii=0; (c = zSynopsis[ii])!=0; ii++){ +@@ -79236,8 +87661,11 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( + if( c=='4' ){ + sqlite3_str_appendall(&x, zP4); + }else if( c=='X' ){ +- sqlite3_str_appendall(&x, pOp->zComment); +- seenCom = 1; ++ if( pOp->zComment && pOp->zComment[0] ){ ++ sqlite3_str_appendall(&x, pOp->zComment); ++ seenCom = 1; ++ break; ++ } + }else{ + int v1 = translateP(c, pOp); + int v2; +@@ -79259,7 +87687,7 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( + sqlite3_str_appendf(&x, "%d", v1); + }else if( pCtx->argc>1 ){ + sqlite3_str_appendf(&x, "%d..%d", v1, v1+pCtx->argc-1); +- }else{ ++ }else if( x.accError==0 ){ + assert( x.nChar>2 ); + x.nChar -= 2; + ii++; +@@ -79298,6 +87726,7 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){ + const char *zOp = 0; + switch( pExpr->op ){ + case TK_STRING: ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3_str_appendf(p, "%Q", pExpr->u.zToken); + break; + case TK_INTEGER: +@@ -79400,7 +87829,7 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ + case P4_COLLSEQ: { + static const char *const encnames[] = {"?", "8", "16LE", "16BE"}; + CollSeq *pColl = pOp->p4.pColl; +- assert( pColl->enc>=0 && pColl->enc<4 ); ++ assert( pColl->enc<4 ); + sqlite3_str_appendf(&x, "%.18s-%s", pColl->zName, + encnames[pColl->enc]); + break; +@@ -79465,14 +87894,15 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ + zP4 = "program"; + break; + } +- case P4_DYNBLOB: +- case P4_ADVANCE: { +- break; +- } + case P4_TABLE: { + zP4 = pOp->p4.pTab->zName; + break; + } ++ case P4_SUBRTNSIG: { ++ SubrtnSig *pSig = pOp->p4.pSubrtnSig; ++ sqlite3_str_appendf(&x, "subrtnsig:%d,%s", pSig->selId, pSig->zAff); ++ break; ++ } + default: { + zP4 = pOp->p4.z; + } +@@ -79600,26 +88030,48 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ + + /* + ** Initialize an array of N Mem element. ++** ++** This is a high-runner, so only those fields that really do need to ++** be initialized are set. The Mem structure is organized so that ++** the fields that get initialized are nearby and hopefully on the same ++** cache line. ++** ++** Mem.flags = flags ++** Mem.db = db ++** Mem.szMalloc = 0 ++** ++** All other fields of Mem can safely remain uninitialized for now. They ++** will be initialized before use. + */ + static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){ +- while( (N--)>0 ){ +- p->db = db; +- p->flags = flags; +- p->szMalloc = 0; ++ assert( db!=0 ); ++ if( N>0 ){ ++ do{ ++ p->flags = flags; ++ p->db = db; ++ p->szMalloc = 0; + #ifdef SQLITE_DEBUG +- p->pScopyFrom = 0; ++ p->pScopyFrom = 0; ++ p->bScopy = 0; + #endif +- p++; ++ p++; ++ }while( (--N)>0 ); + } + } + + /* +-** Release an array of N Mem elements ++** Release auxiliary memory held in an array of N Mem elements. ++** ++** After this routine returns, all Mem elements in the array will still ++** be valid. Those Mem elements that were not holding auxiliary resources ++** will be unchanged. Mem elements which had something freed will be ++** set to MEM_Undefined. + */ + static void releaseMemArray(Mem *p, int N){ + if( p && N ){ + Mem *pEnd = &p[N]; + sqlite3 *db = p->db; ++ assert( db!=0 ); + if( db->pnBytesFreed ){ + do{ + if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); +@@ -79644,15 +88096,20 @@ static void releaseMemArray(Mem *p, int N){ + */ + testcase( p->flags & MEM_Agg ); + testcase( p->flags & MEM_Dyn ); +- testcase( p->xDel==sqlite3VdbeFrameMemDel ); + if( p->flags&(MEM_Agg|MEM_Dyn) ){ ++ testcase( (p->flags & MEM_Dyn)!=0 && p->xDel==sqlite3VdbeFrameMemDel ); + sqlite3VdbeMemRelease(p); ++ p->flags = MEM_Undefined; + }else if( p->szMalloc ){ +- sqlite3DbFreeNN(db, p->zMalloc); ++ sqlite3DbNNFreeNN(db, p->zMalloc); + p->szMalloc = 0; ++ p->flags = MEM_Undefined; + } +- +- p->flags = MEM_Undefined; ++#ifdef SQLITE_DEBUG ++ else{ ++ p->flags = MEM_Undefined; ++ } ++#endif + }while( (++p)nChildMem]; + assert( sqlite3VdbeFrameIsValid(p) ); + for(i=0; inChildCsr; i++){ +- sqlite3VdbeFreeCursor(p->v, apCsr[i]); ++ if( apCsr[i] ) sqlite3VdbeFreeCursorNN(p->v, apCsr[i]); + } + releaseMemArray(aMem, p->nChildMem); + sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0); +@@ -79850,7 +88307,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( + Op *pOp; /* Current opcode */ + + assert( p->explain ); +- assert( p->magic==VDBE_MAGIC_RUN ); ++ assert( p->eVdbeState==VDBE_RUN_STATE ); + assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); + + /* Even though this opcode does not use dynamic strings for +@@ -79858,7 +88315,6 @@ SQLITE_PRIVATE int sqlite3VdbeList( + ** sqlite3_column_text16(), causing a translation to UTF-16 encoding. + */ + releaseMemArray(pMem, 8); +- p->pResultSet = 0; + + if( p->rc==SQLITE_NOMEM ){ + /* This happens if a malloc() inside a call to sqlite3_column_text() or +@@ -79894,7 +88350,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( + sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); + sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); + sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free); +- p->nResColumn = 4; ++ assert( p->nResColumn==4 ); + }else{ + sqlite3VdbeMemSetInt64(pMem+0, i); + sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode), +@@ -79913,9 +88369,9 @@ SQLITE_PRIVATE int sqlite3VdbeList( + sqlite3VdbeMemSetNull(pMem+7); + #endif + sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free); +- p->nResColumn = 8; ++ assert( p->nResColumn==8 ); + } +- p->pResultSet = pMem; ++ p->pResultRow = pMem; + if( db->mallocFailed ){ + p->rc = SQLITE_NOMEM; + rc = SQLITE_ERROR; +@@ -80005,11 +88461,11 @@ struct ReusableSpace { + static void *allocSpace( + struct ReusableSpace *p, /* Bulk memory available for allocation */ + void *pBuf, /* Pointer to a prior allocation */ +- sqlite3_int64 nByte /* Bytes of memory needed */ ++ sqlite3_int64 nByte /* Bytes of memory needed. */ + ){ + assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); + if( pBuf==0 ){ +- nByte = ROUND8(nByte); ++ nByte = ROUND8P(nByte); + if( nByte <= p->nFree ){ + p->nFree -= nByte; + pBuf = &p->pSpace[p->nFree]; +@@ -80026,18 +88482,19 @@ static void *allocSpace( + ** running it. + */ + SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){ +-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) ++#if defined(SQLITE_DEBUG) + int i; + #endif + assert( p!=0 ); +- assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET ); ++ assert( p->eVdbeState==VDBE_INIT_STATE ++ || p->eVdbeState==VDBE_READY_STATE ++ || p->eVdbeState==VDBE_HALT_STATE ); + + /* There should be at least one opcode. + */ + assert( p->nOp>0 ); + +- /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */ +- p->magic = VDBE_MAGIC_RUN; ++ p->eVdbeState = VDBE_READY_STATE; + + #ifdef SQLITE_DEBUG + for(i=0; inMem; i++){ +@@ -80054,8 +88511,8 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){ + p->nFkConstraint = 0; + #ifdef VDBE_PROFILE + for(i=0; inOp; i++){ +- p->aOp[i].cnt = 0; +- p->aOp[i].cycles = 0; ++ p->aOp[i].nExec = 0; ++ p->aOp[i].nCycle = 0; + } + #endif + } +@@ -80086,15 +88543,18 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( + int nVar; /* Number of parameters */ + int nMem; /* Number of VM memory registers */ + int nCursor; /* Number of cursors required */ +- int nArg; /* Number of arguments in subprograms */ ++ int nArg; /* Max number args to xFilter or xUpdate */ + int n; /* Loop counter */ + struct ReusableSpace x; /* Reusable bulk memory */ + + assert( p!=0 ); + assert( p->nOp>0 ); + assert( pParse!=0 ); +- assert( p->magic==VDBE_MAGIC_INIT ); ++ assert( p->eVdbeState==VDBE_INIT_STATE ); + assert( pParse==p->pParse ); ++ assert( pParse->db==p->db ); ++ p->pVList = pParse->pVList; ++ pParse->pVList = 0; + db = p->db; + assert( db->mallocFailed==0 ); + nVar = pParse->nVar; +@@ -80114,7 +88574,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( + ** opcode array. This extra memory will be reallocated for other elements + ** of the prepared statement. + */ +- n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ ++ n = ROUND8P(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ + x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */ + assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) ); + x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */ +@@ -80124,26 +88584,9 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( + resolveP2Values(p, &nArg); + p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); + if( pParse->explain ){ +- static const char * const azColName[] = { +- "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", +- "id", "parent", "notused", "detail" +- }; +- int iFirst, mx, i; + if( nMem<10 ) nMem = 10; + p->explain = pParse->explain; +- if( pParse->explain==2 ){ +- sqlite3VdbeSetNumCols(p, 4); +- iFirst = 8; +- mx = 12; +- }else{ +- sqlite3VdbeSetNumCols(p, 8); +- iFirst = 0; +- mx = 8; +- } +- for(i=iFirst; inResColumn = 12 - 4*p->explain; + } + p->expired = 0; + +@@ -80162,9 +88605,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( + p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); +-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); +-#endif + if( x.nNeeded ){ + x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); + x.nFree = x.nNeeded; +@@ -80173,14 +88613,12 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( + p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); +-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); +-#endif + } + } ++#ifdef SQLITE_DEBUG ++ p->napArg = nArg; ++#endif + +- p->pVList = pParse->pVList; +- pParse->pVList = 0; + if( db->mallocFailed ){ + p->nVar = 0; + p->nCursor = 0; +@@ -80192,9 +88630,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( + p->nMem = nMem; + initMemArray(p->aMem, nMem, db, MEM_Undefined); + memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*)); +-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- memset(p->anExec, 0, p->nOp*sizeof(i64)); +-#endif + } + sqlite3VdbeRewind(p); + } +@@ -80204,24 +88639,33 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( + ** happens to hold. + */ + SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ +- if( pCx==0 ){ ++ if( pCx ) sqlite3VdbeFreeCursorNN(p,pCx); ++} ++static SQLITE_NOINLINE void freeCursorWithCache(Vdbe *p, VdbeCursor *pCx){ ++ VdbeTxtBlbCache *pCache = pCx->pCache; ++ assert( pCx->colCache ); ++ pCx->colCache = 0; ++ pCx->pCache = 0; ++ if( pCache->pCValue ){ ++ sqlite3RCStrUnref(pCache->pCValue); ++ pCache->pCValue = 0; ++ } ++ sqlite3DbFree(p->db, pCache); ++ sqlite3VdbeFreeCursorNN(p, pCx); ++} ++SQLITE_PRIVATE void sqlite3VdbeFreeCursorNN(Vdbe *p, VdbeCursor *pCx){ ++ if( pCx->colCache ){ ++ freeCursorWithCache(p, pCx); + return; + } +- assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); + switch( pCx->eCurType ){ + case CURTYPE_SORTER: { + sqlite3VdbeSorterClose(p->db, pCx); + break; + } + case CURTYPE_BTREE: { +- if( pCx->isEphemeral ){ +- if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx); +- /* The pCx->pCursor will be close automatically, if it exists, by +- ** the call above. */ +- }else{ +- assert( pCx->uc.pCursor!=0 ); +- sqlite3BtreeCloseCursor(pCx->uc.pCursor); +- } ++ assert( pCx->uc.pCursor!=0 ); ++ sqlite3BtreeCloseCursor(pCx->uc.pCursor); + break; + } + #ifndef SQLITE_OMIT_VIRTUALTABLE +@@ -80241,14 +88685,12 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ + ** Close all cursors in the current frame. + */ + static void closeCursorsInFrame(Vdbe *p){ +- if( p->apCsr ){ +- int i; +- for(i=0; inCursor; i++){ +- VdbeCursor *pC = p->apCsr[i]; +- if( pC ){ +- sqlite3VdbeFreeCursor(p, pC); +- p->apCsr[i] = 0; +- } ++ int i; ++ for(i=0; inCursor; i++){ ++ VdbeCursor *pC = p->apCsr[i]; ++ if( pC ){ ++ sqlite3VdbeFreeCursorNN(p, pC); ++ p->apCsr[i] = 0; + } + } + } +@@ -80261,9 +88703,6 @@ static void closeCursorsInFrame(Vdbe *p){ + SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ + Vdbe *v = pFrame->v; + closeCursorsInFrame(v); +-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- v->anExec = pFrame->anExec; +-#endif + v->aOp = pFrame->aOp; + v->nOp = pFrame->nOp; + v->aMem = pFrame->aMem; +@@ -80297,9 +88736,7 @@ static void closeAllCursors(Vdbe *p){ + } + assert( p->nFrame==0 ); + closeCursorsInFrame(p); +- if( p->aMem ){ +- releaseMemArray(p->aMem, p->nMem); +- } ++ releaseMemArray(p->aMem, p->nMem); + while( p->pDelFrame ){ + VdbeFrame *pDel = p->pDelFrame; + p->pDelFrame = pDel->pParent; +@@ -80321,12 +88758,12 @@ SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ + int n; + sqlite3 *db = p->db; + +- if( p->nResColumn ){ +- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); ++ if( p->nResAlloc ){ ++ releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N); + sqlite3DbFree(db, p->aColName); + } + n = nResColumn*COLNAME_N; +- p->nResColumn = (u16)nResColumn; ++ p->nResColumn = p->nResAlloc = (u16)nResColumn; + p->aColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n ); + if( p->aColName==0 ) return; + initMemArray(p->aColName, n, db, MEM_Null); +@@ -80351,14 +88788,14 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName( + ){ + int rc; + Mem *pColName; +- assert( idxnResColumn ); ++ assert( idxnResAlloc ); + assert( vardb->mallocFailed ){ + assert( !zName || xDel!=SQLITE_DYNAMIC ); + return SQLITE_NOMEM_BKPT; + } + assert( p->aColName!=0 ); +- pColName = &(p->aColName[idx+var*p->nResColumn]); ++ pColName = &(p->aColName[idx+var*p->nResAlloc]); + rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); + assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); + return rc; +@@ -80401,7 +88838,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ + */ + for(i=0; rc==SQLITE_OK && inDb; i++){ + Btree *pBt = db->aDb[i].pBt; +- if( sqlite3BtreeIsInTrans(pBt) ){ ++ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ + /* Whether or not a database might need a super-journal depends upon + ** its journal mode (among other things). This matrix determines which + ** journal modes use a super-journal and which do not */ +@@ -80536,7 +88973,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ + */ + for(i=0; inDb; i++){ + Btree *pBt = db->aDb[i].pBt; +- if( sqlite3BtreeIsInTrans(pBt) ){ ++ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ + char const *zFile = sqlite3BtreeGetJournalname(pBt); + if( zFile==0 ){ + continue; /* Ignore TEMP and :memory: databases */ +@@ -80646,7 +89083,7 @@ static void checkActiveVdbeCnt(sqlite3 *db){ + if( p->readOnly==0 ) nWrite++; + if( p->bIsReader ) nRead++; + } +- p = p->pNext; ++ p = p->pVNext; + } + assert( cnt==db->nVdbeActive ); + assert( nWrite==db->nVdbeWrite ); +@@ -80739,7 +89176,8 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ + p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; + p->errorAction = OE_Abort; + sqlite3VdbeError(p, "FOREIGN KEY constraint failed"); +- return SQLITE_ERROR; ++ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR; ++ return SQLITE_CONSTRAINT_FOREIGNKEY; + } + return SQLITE_OK; + } +@@ -80750,9 +89188,9 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ + ** has made changes and is in autocommit mode, then commit those + ** changes. If a rollback is needed, then do the rollback. + ** +-** This routine is the only way to move the state of a VM from +-** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. It is harmless to +-** call this on a VM that is in the SQLITE_MAGIC_HALT state. ++** This routine is the only way to move the sqlite3eOpenState of a VM from ++** SQLITE_STATE_RUN to SQLITE_STATE_HALT. It is harmless to ++** call this on a VM that is in the SQLITE_STATE_HALT state. + ** + ** Return an error code. If the commit could not complete because of + ** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it +@@ -80778,9 +89216,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + ** one, or the complete transaction if there is no statement transaction. + */ + +- if( p->magic!=VDBE_MAGIC_RUN ){ +- return SQLITE_OK; +- } ++ assert( p->eVdbeState==VDBE_RUN_STATE ); + if( db->mallocFailed ){ + p->rc = SQLITE_NOMEM_BKPT; + } +@@ -80789,7 +89225,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + + /* No commit or rollback needed if the program never started or if the + ** SQL statement does not read or write a database file. */ +- if( p->pc>=0 && p->bIsReader ){ ++ if( p->bIsReader ){ + int mrc; /* Primary error code from p->rc */ + int eStatementOp = 0; + int isSpecialError; /* Set to true if a 'special' error */ +@@ -80798,9 +89234,15 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + sqlite3VdbeEnter(p); + + /* Check for one of the special errors */ +- mrc = p->rc & 0xff; +- isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR +- || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; ++ if( p->rc ){ ++ mrc = p->rc & 0xff; ++ isSpecialError = mrc==SQLITE_NOMEM ++ || mrc==SQLITE_IOERR ++ || mrc==SQLITE_INTERRUPT ++ || mrc==SQLITE_FULL; ++ }else{ ++ mrc = isSpecialError = 0; ++ } + if( isSpecialError ){ + /* If the query was read-only and the error code is SQLITE_INTERRUPT, + ** no rollback is necessary. Otherwise, at least a savepoint +@@ -80831,7 +89273,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + + /* Check for immediate foreign key violations. */ + if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ +- sqlite3VdbeCheckFk(p, 0); ++ (void)sqlite3VdbeCheckFk(p, 0); + } + + /* If the auto-commit flag is set and this is the only active writer +@@ -80852,6 +89294,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + return SQLITE_ERROR; + } + rc = SQLITE_CONSTRAINT_FOREIGNKEY; ++ }else if( db->flags & SQLITE_CorruptRdOnly ){ ++ rc = SQLITE_CORRUPT; ++ db->flags &= ~SQLITE_CorruptRdOnly; + }else{ + /* The auto-commit flag is true, the vdbe program was successful + ** or hit an 'OR FAIL' constraint and there are no deferred foreign +@@ -80863,6 +89308,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + sqlite3VdbeLeave(p); + return SQLITE_BUSY; + }else if( rc!=SQLITE_OK ){ ++ sqlite3SystemError(db, rc); + p->rc = rc; + sqlite3RollbackAll(db, SQLITE_OK); + p->nChange = 0; +@@ -80872,6 +89318,8 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + db->flags &= ~(u64)SQLITE_DeferFKs; + sqlite3CommitInternalChanges(db); + } ++ }else if( p->rc==SQLITE_SCHEMA && db->nVdbeActive>1 ){ ++ p->nChange = 0; + }else{ + sqlite3RollbackAll(db, SQLITE_OK); + p->nChange = 0; +@@ -80928,15 +89376,13 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ + } + + /* We have successfully halted and closed the VM. Record this fact. */ +- if( p->pc>=0 ){ +- db->nVdbeActive--; +- if( !p->readOnly ) db->nVdbeWrite--; +- if( p->bIsReader ) db->nVdbeRead--; +- assert( db->nVdbeActive>=db->nVdbeRead ); +- assert( db->nVdbeRead>=db->nVdbeWrite ); +- assert( db->nVdbeWrite>=0 ); +- } +- p->magic = VDBE_MAGIC_HALT; ++ db->nVdbeActive--; ++ if( !p->readOnly ) db->nVdbeWrite--; ++ if( p->bIsReader ) db->nVdbeRead--; ++ assert( db->nVdbeActive>=db->nVdbeRead ); ++ assert( db->nVdbeRead>=db->nVdbeWrite ); ++ assert( db->nVdbeWrite>=0 ); ++ p->eVdbeState = VDBE_HALT_STATE; + checkActiveVdbeCnt(db); + if( db->mallocFailed ){ + p->rc = SQLITE_NOMEM_BKPT; +@@ -80985,6 +89431,7 @@ SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){ + sqlite3ValueSetNull(db->pErr); + } + db->errCode = rc; ++ db->errByteOffset = -1; + return rc; + } + +@@ -81017,8 +89464,8 @@ static void vdbeInvokeSqllog(Vdbe *v){ + ** again. + ** + ** To look at it another way, this routine resets the state of the +-** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to +-** VDBE_MAGIC_INIT. ++** virtual machine from VDBE_RUN_STATE or VDBE_HALT_STATE back to ++** VDBE_READY_STATE. + */ + SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ + #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) +@@ -81032,7 +89479,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ + ** error, then it might not have been halted properly. So halt + ** it now. + */ +- sqlite3VdbeHalt(p); ++ if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p); + + /* If the VDBE has been run even partially, then transfer the error code + ** and error message from the VDBE into the main database structure. But +@@ -81046,13 +89493,6 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ + }else{ + db->errCode = p->rc; + } +- if( p->runOnlyOnce ) p->expired = 1; +- }else if( p->rc && p->expired ){ +- /* The expired flag was set on the VDBE before the first call +- ** to sqlite3_step(). For consistency (since sqlite3_step() was +- ** called), set the database error in this case as well. +- */ +- sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); + } + + /* Reset register contents and reclaim error message memory. +@@ -81069,7 +89509,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = 0; + } +- p->pResultSet = 0; ++ p->pResultRow = 0; + #ifdef SQLITE_DEBUG + p->nWrite = 0; + #endif +@@ -81097,10 +89537,12 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ + } + for(i=0; inOp; i++){ + char zHdr[100]; ++ i64 cnt = p->aOp[i].nExec; ++ i64 cycles = p->aOp[i].nCycle; + sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", +- p->aOp[i].cnt, +- p->aOp[i].cycles, +- p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 ++ cnt, ++ cycles, ++ cnt>0 ? cycles/cnt : 0 + ); + fprintf(out, "%s", zHdr); + sqlite3VdbePrintOp(out, i, &p->aOp[i]); +@@ -81109,7 +89551,6 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ + } + } + #endif +- p->magic = VDBE_MAGIC_RESET; + return p->rc & db->errMask; + } + +@@ -81119,7 +89560,10 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ + */ + SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){ + int rc = SQLITE_OK; +- if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ ++ assert( VDBE_RUN_STATE>VDBE_READY_STATE ); ++ assert( VDBE_HALT_STATE>VDBE_READY_STATE ); ++ assert( VDBE_INIT_STATEeVdbeState>=VDBE_READY_STATE ){ + rc = sqlite3VdbeReset(p); + assert( (rc & p->db->errMask)==rc ); + } +@@ -81171,29 +89615,32 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, + ** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with + ** the database connection and frees the object itself. + */ +-SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ ++static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ + SubProgram *pSub, *pNext; ++ assert( db!=0 ); + assert( p->db==0 || p->db==db ); +- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); ++ if( p->aColName ){ ++ releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N); ++ sqlite3DbNNFreeNN(db, p->aColName); ++ } + for(pSub=p->pProgram; pSub; pSub=pNext){ + pNext = pSub->pNext; + vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); + sqlite3DbFree(db, pSub); + } +- if( p->magic!=VDBE_MAGIC_INIT ){ ++ if( p->eVdbeState!=VDBE_INIT_STATE ){ + releaseMemArray(p->aVar, p->nVar); +- sqlite3DbFree(db, p->pVList); +- sqlite3DbFree(db, p->pFree); ++ if( p->pVList ) sqlite3DbNNFreeNN(db, p->pVList); ++ if( p->pFree ) sqlite3DbNNFreeNN(db, p->pFree); + } + vdbeFreeOpArray(db, p->aOp, p->nOp); +- sqlite3DbFree(db, p->aColName); +- sqlite3DbFree(db, p->zSql); ++ if( p->zSql ) sqlite3DbNNFreeNN(db, p->zSql); + #ifdef SQLITE_ENABLE_NORMALIZE + sqlite3DbFree(db, p->zNormSql); + { +- DblquoteStr *pThis, *pNext; +- for(pThis=p->pDblStr; pThis; pThis=pNext){ +- pNext = pThis->pNextStr; ++ DblquoteStr *pThis, *pNxt; ++ for(pThis=p->pDblStr; pThis; pThis=pNxt){ ++ pNxt = pThis->pNextStr; + sqlite3DbFree(db, pThis); + } + } +@@ -81217,20 +89664,17 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){ + + assert( p!=0 ); + db = p->db; ++ assert( db!=0 ); + assert( sqlite3_mutex_held(db->mutex) ); + sqlite3VdbeClearObject(db, p); +- if( p->pPrev ){ +- p->pPrev->pNext = p->pNext; +- }else{ +- assert( db->pVdbe==p ); +- db->pVdbe = p->pNext; +- } +- if( p->pNext ){ +- p->pNext->pPrev = p->pPrev; ++ if( db->pnBytesFreed==0 ){ ++ assert( p->ppVPrev!=0 ); ++ *p->ppVPrev = p->pVNext; ++ if( p->pVNext ){ ++ p->pVNext->ppVPrev = p->ppVPrev; ++ } + } +- p->magic = VDBE_MAGIC_DEAD; +- p->db = 0; +- sqlite3DbFreeNN(db, p); ++ sqlite3DbNNFreeNN(db, p); + } + + /* +@@ -81246,7 +89690,7 @@ SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor *p){ + assert( p->deferredMoveto ); + assert( p->isTable ); + assert( p->eCurType==CURTYPE_BTREE ); +- rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res); ++ rc = sqlite3BtreeTableMoveto(p->uc.pCursor, p->movetoTarget, 0, &res); + if( rc ) return rc; + if( res!=0 ) return SQLITE_CORRUPT_BKPT; + #ifdef SQLITE_TEST +@@ -81264,7 +89708,7 @@ SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor *p){ + ** is supposed to be pointing. If the row was deleted out from under the + ** cursor, set the cursor to point to a NULL row. + */ +-static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ ++SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeHandleMovedCursor(VdbeCursor *p){ + int isDifferentRow, rc; + assert( p->eCurType==CURTYPE_BTREE ); + assert( p->uc.pCursor!=0 ); +@@ -81280,40 +89724,9 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ + ** if need be. Return any I/O error from the restore operation. + */ + SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){ +- assert( p->eCurType==CURTYPE_BTREE ); +- if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ +- return handleMovedCursor(p); +- } +- return SQLITE_OK; +-} +- +-/* +-** Make sure the cursor p is ready to read or write the row to which it +-** was last positioned. Return an error code if an OOM fault or I/O error +-** prevents us from positioning the cursor to its correct position. +-** +-** If a MoveTo operation is pending on the given cursor, then do that +-** MoveTo now. If no move is pending, check to see if the row has been +-** deleted out from under the cursor and if it has, mark the row as +-** a NULL row. +-** +-** If the cursor is already pointing to the correct row and that row has +-** not been deleted out from under the cursor, then this routine is a no-op. +-*/ +-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ +- VdbeCursor *p = *pp; +- assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); +- if( p->deferredMoveto ){ +- u32 iMap; +- if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ +- *pp = p->pAltCursor; +- *piCol = iMap - 1; +- return SQLITE_OK; +- } +- return sqlite3VdbeFinishMoveto(p); +- } ++ assert( p->eCurType==CURTYPE_BTREE || IsNullCursor(p) ); + if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ +- return handleMovedCursor(p); ++ return sqlite3VdbeHandleMovedCursor(p); + } + return SQLITE_OK; + } +@@ -81324,7 +89737,7 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ + ** sqlite3VdbeSerialType() + ** sqlite3VdbeSerialTypeLen() + ** sqlite3VdbeSerialLen() +-** sqlite3VdbeSerialPut() ++** sqlite3VdbeSerialPut() <--- in-lined into OP_MakeRecord as of 2022-04-02 + ** sqlite3VdbeSerialGet() + ** + ** encapsulate the code that serializes values for storage in SQLite +@@ -81436,7 +89849,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){ + /* + ** The sizes for serial types less than 128 + */ +-static const u8 sqlite3SmallTypeSizes[] = { ++SQLITE_PRIVATE const u8 sqlite3SmallTypeSizes[128] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + /* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, + /* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, +@@ -81505,7 +89918,7 @@ SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){ + ** so we trust him. + */ + #ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +-static u64 floatSwap(u64 in){ ++SQLITE_PRIVATE u64 sqlite3FloatSwap(u64 in){ + union { + u64 r; + u32 i[2]; +@@ -81518,59 +89931,8 @@ static u64 floatSwap(u64 in){ + u.i[1] = t; + return u.r; + } +-# define swapMixedEndianFloat(X) X = floatSwap(X) +-#else +-# define swapMixedEndianFloat(X) +-#endif ++#endif /* SQLITE_MIXED_ENDIAN_64BIT_FLOAT */ + +-/* +-** Write the serialized data blob for the value stored in pMem into +-** buf. It is assumed that the caller has allocated sufficient space. +-** Return the number of bytes written. +-** +-** nBuf is the amount of space left in buf[]. The caller is responsible +-** for allocating enough space to buf[] to hold the entire field, exclusive +-** of the pMem->u.nZero bytes for a MEM_Zero value. +-** +-** Return the number of bytes actually written into buf[]. The number +-** of bytes in the zero-filled tail is included in the return value only +-** if those bytes were zeroed in buf[]. +-*/ +-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ +- u32 len; +- +- /* Integer and Real */ +- if( serial_type<=7 && serial_type>0 ){ +- u64 v; +- u32 i; +- if( serial_type==7 ){ +- assert( sizeof(v)==sizeof(pMem->u.r) ); +- memcpy(&v, &pMem->u.r, sizeof(v)); +- swapMixedEndianFloat(v); +- }else{ +- v = pMem->u.i; +- } +- len = i = sqlite3SmallTypeSizes[serial_type]; +- assert( i>0 ); +- do{ +- buf[--i] = (u8)(v&0xFF); +- v >>= 8; +- }while( i ); +- return len; +- } +- +- /* String or blob */ +- if( serial_type>=12 ){ +- assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0) +- == (int)sqlite3VdbeSerialTypeLen(serial_type) ); +- len = pMem->n; +- if( len>0 ) memcpy(buf, pMem->z, len); +- return len; +- } +- +- /* NULL or constants 0 or 1 */ +- return 0; +-} + + /* Input "x" is a sequence of unsigned characters that represent a + ** big-endian integer. Return the equivalent native integer +@@ -81583,14 +89945,14 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ + + /* + ** Deserialize the data blob pointed to by buf as serial type serial_type +-** and store the result in pMem. Return the number of bytes read. ++** and store the result in pMem. + ** + ** This function is implemented as two separate routines for performance. + ** The few cases that require local variables are broken out into a separate + ** routine so that in most cases the overhead of moving the stack pointer + ** is avoided. + */ +-static u32 serialGet( ++static void serialGet( + const unsigned char *buf, /* Buffer to deserialize from */ + u32 serial_type, /* Serial type to deserialize */ + Mem *pMem /* Memory cell to write value into */ +@@ -81624,9 +89986,25 @@ static u32 serialGet( + memcpy(&pMem->u.r, &x, sizeof(x)); + pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; + } +- return 8; + } +-SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( ++static int serialGet7( ++ const unsigned char *buf, /* Buffer to deserialize from */ ++ Mem *pMem /* Memory cell to write value into */ ++){ ++ u64 x = FOUR_BYTE_UINT(buf); ++ u32 y = FOUR_BYTE_UINT(buf+4); ++ x = (x<<32) + y; ++ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); ++ swapMixedEndianFloat(x); ++ memcpy(&pMem->u.r, &x, sizeof(x)); ++ if( IsNaN(x) ){ ++ pMem->flags = MEM_Null; ++ return 1; ++ } ++ pMem->flags = MEM_Real; ++ return 0; ++} ++SQLITE_PRIVATE void sqlite3VdbeSerialGet( + const unsigned char *buf, /* Buffer to deserialize from */ + u32 serial_type, /* Serial type to deserialize */ + Mem *pMem /* Memory cell to write value into */ +@@ -81637,13 +90015,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + pMem->flags = MEM_Null|MEM_Zero; + pMem->n = 0; + pMem->u.nZero = 0; +- break; ++ return; + } + case 11: /* Reserved for future use */ + case 0: { /* Null */ + /* EVIDENCE-OF: R-24078-09375 Value is a NULL. */ + pMem->flags = MEM_Null; +- break; ++ return; + } + case 1: { + /* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement +@@ -81651,7 +90029,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + pMem->u.i = ONE_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); +- return 1; ++ return; + } + case 2: { /* 2-byte signed integer */ + /* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit +@@ -81659,7 +90037,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + pMem->u.i = TWO_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); +- return 2; ++ return; + } + case 3: { /* 3-byte signed integer */ + /* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit +@@ -81667,7 +90045,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + pMem->u.i = THREE_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); +- return 3; ++ return; + } + case 4: { /* 4-byte signed integer */ + /* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit +@@ -81679,7 +90057,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + #endif + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); +- return 4; ++ return; + } + case 5: { /* 6-byte signed integer */ + /* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit +@@ -81687,13 +90065,14 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf); + pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); +- return 6; ++ return; + } + case 6: /* 8-byte signed integer */ + case 7: { /* IEEE floating point */ + /* These use local variables, so do them in a separate routine + ** to avoid having to move the frame pointer in the common case */ +- return serialGet(buf,serial_type,pMem); ++ serialGet(buf,serial_type,pMem); ++ return; + } + case 8: /* Integer 0 */ + case 9: { /* Integer 1 */ +@@ -81701,7 +90080,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + /* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */ + pMem->u.i = serial_type-8; + pMem->flags = MEM_Int; +- return 0; ++ return; + } + default: { + /* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in +@@ -81712,10 +90091,10 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( + pMem->z = (char *)buf; + pMem->n = (serial_type-12)/2; + pMem->flags = aFlag[serial_type&1]; +- return pMem->n; ++ return; + } + } +- return 0; ++ return; + } + /* + ** This routine is used to allocate sufficient space for an UnpackedRecord +@@ -81736,10 +90115,11 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord( + ){ + UnpackedRecord *p; /* Unpacked record to return */ + int nByte; /* Number of bytes required for *p */ +- nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); ++ assert( sizeof(UnpackedRecord) + sizeof(Mem)*65536 < 0x7fffffff ); ++ nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); + p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte); + if( !p ) return 0; +- p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; ++ p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))]; + assert( pKeyInfo->aSortFlags!=0 ); + p->pKeyInfo = pKeyInfo; + p->nField = pKeyInfo->nKeyField + 1; +@@ -81778,7 +90158,8 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( + /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */ + pMem->szMalloc = 0; + pMem->z = 0; +- d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); ++ sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); ++ d += sqlite3VdbeSerialTypeLen(serial_type); + pMem++; + if( (++u)>=p->nField ) break; + } +@@ -81857,12 +90238,22 @@ static int vdbeRecordCompareDebug( + if( d1+(u64)serial_type1+2>(u64)nKey1 + && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1 + ){ ++ if( serial_type1>=1 ++ && serial_type1<=7 ++ && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)<=(u64)nKey1+8 ++ && CORRUPT_DB ++ ){ ++ return 1; /* corrupt record not detected by ++ ** sqlite3VdbeRecordCompareWithSkip(). Return true ++ ** to avoid firing the assert() */ ++ } + break; + } + + /* Extract the values to be compared. + */ +- d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); ++ sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); ++ d1 += sqlite3VdbeSerialTypeLen(serial_type1); + + /* Do the comparison + */ +@@ -81973,8 +90364,8 @@ static int vdbeCompareMemString( + }else{ + rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2); + } +- sqlite3VdbeMemRelease(&c1); +- sqlite3VdbeMemRelease(&c2); ++ sqlite3VdbeMemReleaseMalloc(&c1); ++ sqlite3VdbeMemReleaseMalloc(&c2); + return rc; + } + } +@@ -82024,29 +90415,37 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem + return n1 - n2; + } + ++/* The following two functions are used only within testcase() to prove ++** test coverage. These functions do no exist for production builds. ++** We must use separate SQLITE_NOINLINE functions here, since otherwise ++** optimizer code movement causes gcov to become very confused. ++*/ ++#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) ++static int SQLITE_NOINLINE doubleLt(double a, double b){ return a8 ){ +- LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; +- if( xr ) return +1; +- return 0; ++SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ ++ if( sqlite3IsNaN(r) ){ ++ /* SQLite considers NaN to be a NULL. And all integer values are greater ++ ** than NULL */ ++ return 1; + }else{ + i64 y; +- double s; + if( r<-9223372036854775808.0 ) return +1; + if( r>=9223372036854775808.0 ) return -1; + y = (i64)r; + if( iy ) return +1; +- s = (double)i; +- if( sr ) return +1; +- return 0; ++ testcase( doubleLt(((double)i),r) ); ++ testcase( doubleLt(r,((double)i)) ); ++ testcase( doubleEq(r,((double)i)) ); ++ return (((double)i)r); + } + } + +@@ -82232,14 +90631,22 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( + ** two elements in the keys are equal. Fix the various stack variables so + ** that this routine begins comparing at the second field. */ + if( bSkip ){ +- u32 s1; +- idx1 = 1 + getVarint32(&aKey1[1], s1); ++ u32 s1 = aKey1[1]; ++ if( s1<0x80 ){ ++ idx1 = 2; ++ }else{ ++ idx1 = 1 + sqlite3GetVarint32(&aKey1[1], &s1); ++ } + szHdr1 = aKey1[0]; + d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1); + i = 1; + pRhs++; + }else{ +- idx1 = getVarint32(aKey1, szHdr1); ++ if( (szHdr1 = aKey1[0])<0x80 ){ ++ idx1 = 1; ++ }else{ ++ idx1 = sqlite3GetVarint32(aKey1, &szHdr1); ++ } + d1 = szHdr1; + i = 0; + } +@@ -82254,7 +90661,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( + assert( pPKey2->pKeyInfo->aSortFlags!=0 ); + assert( pPKey2->pKeyInfo->nKeyField>0 ); + assert( idx1<=szHdr1 || CORRUPT_DB ); +- do{ ++ while( 1 /*exit-by-break*/ ){ + u32 serial_type; + + /* RHS is an integer */ +@@ -82264,11 +90671,11 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( + serial_type = aKey1[idx1]; + testcase( serial_type==12 ); + if( serial_type>=10 ){ +- rc = +1; ++ rc = serial_type==10 ? -1 : +1; + }else if( serial_type==0 ){ + rc = -1; + }else if( serial_type==7 ){ +- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); ++ serialGet7(&aKey1[d1], &mem1); + rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); + }else{ + i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); +@@ -82288,19 +90695,23 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( + /* Serial types 12 or greater are strings and blobs (greater than + ** numbers). Types 10 and 11 are currently "reserved for future + ** use", so it doesn't really matter what the results of comparing +- ** them to numberic values are. */ +- rc = +1; ++ ** them to numeric values are. */ ++ rc = serial_type==10 ? -1 : +1; + }else if( serial_type==0 ){ + rc = -1; + }else{ +- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + if( serial_type==7 ){ +- if( mem1.u.ru.r ){ ++ if( serialGet7(&aKey1[d1], &mem1) ){ ++ rc = -1; /* mem1 is a NaN */ ++ }else if( mem1.u.ru.r ){ + rc = -1; + }else if( mem1.u.r>pRhs->u.r ){ + rc = +1; ++ }else{ ++ assert( rc==0 ); + } + }else{ ++ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); + } + } +@@ -82370,7 +90781,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( + /* RHS is null */ + else{ + serial_type = aKey1[idx1]; +- rc = (serial_type!=0); ++ if( serial_type==0 ++ || serial_type==10 ++ || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0) ++ ){ ++ assert( rc==0 ); ++ }else{ ++ rc = 1; ++ } + } + + if( rc!=0 ){ +@@ -82392,8 +90810,13 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( + if( i==pPKey2->nField ) break; + pRhs++; + d1 += sqlite3VdbeSerialTypeLen(serial_type); ++ if( d1>(unsigned)nKey1 ) break; + idx1 += sqlite3VarintLen(serial_type); +- }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 ); ++ if( idx1>=(unsigned)szHdr1 ){ ++ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; ++ return 0; /* Corrupt index */ ++ } ++ } + + /* No memory allocation is ever used on mem1. Prove this using + ** the following assert(). If the assert() fails, it indicates a +@@ -82495,7 +90918,8 @@ static int vdbeRecordCompareInt( + return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2); + } + +- v = pPKey2->aMem[0].u.i; ++ assert( pPKey2->u.i == pPKey2->aMem[0].u.i ); ++ v = pPKey2->u.i; + if( v>lhs ){ + res = pPKey2->r1; + }else if( vaMem[0].flags & MEM_Str ); ++ assert( pPKey2->aMem[0].n == pPKey2->n ); ++ assert( pPKey2->aMem[0].z == pPKey2->u.z ); + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); +- serial_type = (u8)(aKey1[1]); +- if( serial_type >= 0x80 ){ +- sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); +- } ++ serial_type = (signed char)(aKey1[1]); ++ ++vrcs_restart: + if( serial_type<12 ){ ++ if( serial_type<0 ){ ++ sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); ++ if( serial_type>=12 ) goto vrcs_restart; ++ assert( CORRUPT_DB ); ++ } + res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ + }else if( !(serial_type & 0x01) ){ + res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ +@@ -82549,15 +90979,15 @@ static int vdbeRecordCompareString( + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corruption */ + } +- nCmp = MIN( pPKey2->aMem[0].n, nStr ); +- res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp); ++ nCmp = MIN( pPKey2->n, nStr ); ++ res = memcmp(&aKey1[szHdr], pPKey2->u.z, nCmp); + + if( res>0 ){ + res = pPKey2->r2; + }else if( res<0 ){ + res = pPKey2->r1; + }else{ +- res = nStr - pPKey2->aMem[0].n; ++ res = nStr - pPKey2->n; + if( res==0 ){ + if( pPKey2->nField>1 ){ + res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); +@@ -82612,6 +91042,7 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){ + p->r2 = 1; + } + if( (flags & MEM_Int) ){ ++ p->u.i = p->aMem[0].u.i; + return vdbeRecordCompareInt; + } + testcase( flags & MEM_Real ); +@@ -82621,6 +91052,8 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){ + && p->pKeyInfo->aColl[0]==0 + ){ + assert( flags & MEM_Str ); ++ p->u.z = p->aMem[0].z; ++ p->n = p->aMem[0].n; + return vdbeRecordCompareString; + } + } +@@ -82663,7 +91096,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ + /* The index entry must begin with a header size */ + getVarint32NR((u8*)m.z, szHdr); + testcase( szHdr==3 ); +- testcase( szHdr==m.n ); ++ testcase( szHdr==(u32)m.n ); + testcase( szHdr>0x7fffffff ); + assert( m.n>=0 ); + if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){ +@@ -82693,14 +91126,14 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ + /* Fetch the integer off the end of the index record */ + sqlite3VdbeSerialGet((u8*)&m.z[m.n-lenRowid], typeRowid, &v); + *rowid = v.u.i; +- sqlite3VdbeMemRelease(&m); ++ sqlite3VdbeMemReleaseMalloc(&m); + return SQLITE_OK; + + /* Jump here if database corruption is detected after m has been + ** allocated. Free the m object and return SQLITE_CORRUPT. */ + idx_rowid_corruption: + testcase( m.szMalloc!=0 ); +- sqlite3VdbeMemRelease(&m); ++ sqlite3VdbeMemReleaseMalloc(&m); + return SQLITE_CORRUPT_BKPT; + } + +@@ -82742,7 +91175,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( + return rc; + } + *res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, pUnpacked, 0); +- sqlite3VdbeMemRelease(&m); ++ sqlite3VdbeMemReleaseMalloc(&m); + return SQLITE_OK; + } + +@@ -82750,7 +91183,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( + ** This routine sets the value to be returned by subsequent calls to + ** sqlite3_changes() on the database handle 'db'. + */ +-SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){ ++SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, i64 nChange){ + assert( sqlite3_mutex_held(db->mutex) ); + db->nChange = nChange; + db->nTotalChange += nChange; +@@ -82784,7 +91217,7 @@ SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe *v){ + */ + SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){ + Vdbe *p; +- for(p = db->pVdbe; p; p=p->pNext){ ++ for(p = db->pVdbe; p; p=p->pVNext){ + p->expired = iCode+1; + } + } +@@ -82815,7 +91248,8 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff + assert( iVar>0 ); + if( v ){ + Mem *pMem = &v->aVar[iVar-1]; +- assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); ++ assert( (v->db->flags & SQLITE_EnableQPSG)==0 ++ || (v->db->mDbFlags & DBFLAG_InternalFunc)!=0 ); + if( 0==(pMem->flags & MEM_Null) ){ + sqlite3_value *pRet = sqlite3ValueNew(v->db); + if( pRet ){ +@@ -82835,7 +91269,8 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff + */ + SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ + assert( iVar>0 ); +- assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); ++ assert( (v->db->flags & SQLITE_EnableQPSG)==0 ++ || (v->db->mDbFlags & DBFLAG_InternalFunc)!=0 ); + if( iVar>=32 ){ + v->expmask |= 0x80000000; + }else{ +@@ -82877,6 +91312,20 @@ SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context *pCtx){ + return 1; + } + ++#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) ++/* ++** This Walker callback is used to help verify that calls to ++** sqlite3BtreeCursorHint() with opcode BTREE_HINT_RANGE have ++** byte-code register values correctly initialized. ++*/ ++SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr){ ++ if( pExpr->op==TK_REGISTER ){ ++ assert( (pWalker->u.aMem[pExpr->iTable].flags & MEM_Undefined)==0 ); ++ } ++ return WRC_Continue; ++} ++#endif /* SQLITE_ENABLE_CURSOR_HINTS && SQLITE_DEBUG */ ++ + #ifndef SQLITE_OMIT_VIRTUALTABLE + /* + ** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored +@@ -82905,13 +91354,14 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){ + ** the vdbeUnpackRecord() function found in vdbeapi.c. + */ + static void vdbeFreeUnpacked(sqlite3 *db, int nField, UnpackedRecord *p){ ++ assert( db!=0 ); + if( p ){ + int i; + for(i=0; iaMem[i]; +- if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem); ++ if( pMem->zMalloc ) sqlite3VdbeMemReleaseMalloc(pMem); + } +- sqlite3DbFreeNN(db, p); ++ sqlite3DbNNFreeNN(db, p); + } + } + #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +@@ -82930,13 +91380,24 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( + const char *zDb, /* Database name */ + Table *pTab, /* Modified table */ + i64 iKey1, /* Initial key value */ +- int iReg /* Register for new.* record */ ++ int iReg, /* Register for new.* record */ ++ int iBlobWrite + ){ + sqlite3 *db = v->db; + i64 iKey2; + PreUpdate preupdate; + const char *zTbl = pTab->zName; + static const u8 fakeSortOrder = 0; ++#ifdef SQLITE_DEBUG ++ int nRealCol; ++ if( pTab->tabFlags & TF_WithoutRowid ){ ++ nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn; ++ }else if( pTab->tabFlags & TF_HasVirtual ){ ++ nRealCol = pTab->nNVCol; ++ }else{ ++ nRealCol = pTab->nCol; ++ } ++#endif + + assert( db->pPreUpdate==0 ); + memset(&preupdate, 0, sizeof(PreUpdate)); +@@ -82951,34 +91412,46 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( + } + } + +- assert( pCsr->nField==pTab->nCol +- || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) ++ assert( pCsr!=0 ); ++ assert( pCsr->eCurType==CURTYPE_BTREE ); ++ assert( pCsr->nField==nRealCol ++ || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1) + ); + + preupdate.v = v; + preupdate.pCsr = pCsr; + preupdate.op = op; + preupdate.iNewReg = iReg; +- preupdate.keyinfo.db = db; +- preupdate.keyinfo.enc = ENC(db); +- preupdate.keyinfo.nKeyField = pTab->nCol; +- preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; ++ preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace; ++ preupdate.pKeyinfo->db = db; ++ preupdate.pKeyinfo->enc = ENC(db); ++ preupdate.pKeyinfo->nKeyField = pTab->nCol; ++ preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder; + preupdate.iKey1 = iKey1; + preupdate.iKey2 = iKey2; + preupdate.pTab = pTab; ++ preupdate.iBlobWrite = iBlobWrite; + + db->pPreUpdate = &preupdate; + db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); + db->pPreUpdate = 0; + sqlite3DbFree(db, preupdate.aRecord); +- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); +- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); ++ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked); ++ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked); ++ sqlite3VdbeMemRelease(&preupdate.oldipk); + if( preupdate.aNew ){ + int i; + for(i=0; inField; i++){ + sqlite3VdbeMemRelease(&preupdate.aNew[i]); + } +- sqlite3DbFreeNN(db, preupdate.aNew); ++ sqlite3DbNNFreeNN(db, preupdate.aNew); ++ } ++ if( preupdate.apDflt ){ ++ int i; ++ for(i=0; inCol; i++){ ++ sqlite3ValueFree(preupdate.apDflt[i]); ++ } ++ sqlite3DbFree(db, preupdate.apDflt); + } + } + #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +@@ -83002,6 +91475,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( + */ + /* #include "sqliteInt.h" */ + /* #include "vdbeInt.h" */ ++/* #include "opcodes.h" */ + + #ifndef SQLITE_OMIT_DEPRECATED + /* +@@ -83049,7 +91523,6 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ + sqlite3_int64 iNow; + sqlite3_int64 iElapse; + assert( p->startTime>0 ); +- assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); + assert( db->init.busy==0 ); + assert( p->zSql!=0 ); + sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); +@@ -83095,7 +91568,9 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ + if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; + sqlite3_mutex_enter(db->mutex); + checkProfileCallback(db, v); +- rc = sqlite3VdbeFinalize(v); ++ assert( v->eVdbeState>=VDBE_READY_STATE ); ++ rc = sqlite3VdbeReset(v); ++ sqlite3VdbeDelete(v); + rc = sqlite3ApiExit(db, rc); + sqlite3LeaveMutexAndCloseZombie(db); + } +@@ -83136,7 +91611,15 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ + int rc = SQLITE_OK; + Vdbe *p = (Vdbe*)pStmt; + #if SQLITE_THREADSAFE +- sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; ++ sqlite3_mutex *mutex; ++#endif ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pStmt==0 ){ ++ return SQLITE_MISUSE_BKPT; ++ } ++#endif ++#if SQLITE_THREADSAFE ++ mutex = p->db->mutex; + #endif + sqlite3_mutex_enter(mutex); + for(i=0; inVar; i++){ +@@ -83255,7 +91738,7 @@ SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ + SQLITE_NULL, /* 0x1f (not possible) */ + SQLITE_FLOAT, /* 0x20 INTREAL */ + SQLITE_NULL, /* 0x21 (not possible) */ +- SQLITE_TEXT, /* 0x22 INTREAL + TEXT */ ++ SQLITE_FLOAT, /* 0x22 INTREAL + TEXT */ + SQLITE_NULL, /* 0x23 (not possible) */ + SQLITE_FLOAT, /* 0x24 (not possible) */ + SQLITE_NULL, /* 0x25 (not possible) */ +@@ -83303,6 +91786,9 @@ SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ + #endif + return aType[pVal->flags&MEM_AffMask]; + } ++SQLITE_API int sqlite3_value_encoding(sqlite3_value *pVal){ ++ return pVal->enc; ++} + + /* Return true if a parameter to xUpdate represents an unchanged column */ + SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){ +@@ -83332,6 +91818,9 @@ SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){ + sqlite3ValueFree(pNew); + pNew = 0; + } ++ }else if( pNew->flags & MEM_Null ){ ++ /* Do not duplicate pointer values */ ++ pNew->flags &= ~(MEM_Term|MEM_Subtype); + } + return pNew; + } +@@ -83349,11 +91838,11 @@ SQLITE_API void sqlite3_value_free(sqlite3_value *pOld){ + ** the function result. + ** + ** The setStrOrError() function calls sqlite3VdbeMemSetStr() to store the +-** result as a string or blob but if the string or blob is too large, it +-** then sets the error code to SQLITE_TOOBIG ++** result as a string or blob. Appropriate errors are set if the string/blob ++** is too big or if an OOM occurs. + ** + ** The invokeValueDestructor(P,X) routine invokes destructor function X() +-** on value P is not going to be used and need to be destroyed. ++** on value P if P is not going to be used and need to be destroyed. + */ + static void setResultStrOrError( + sqlite3_context *pCtx, /* Function context */ +@@ -83362,14 +91851,28 @@ static void setResultStrOrError( + u8 enc, /* Encoding of z. 0 for BLOBs */ + void (*xDel)(void*) /* Destructor function */ + ){ +- if( sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){ ++ Mem *pOut = pCtx->pOut; ++ int rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); ++ if( rc ){ ++ if( rc==SQLITE_TOOBIG ){ ++ sqlite3_result_error_toobig(pCtx); ++ }else{ ++ /* The only errors possible from sqlite3VdbeMemSetStr are ++ ** SQLITE_TOOBIG and SQLITE_NOMEM */ ++ assert( rc==SQLITE_NOMEM ); ++ sqlite3_result_error_nomem(pCtx); ++ } ++ return; ++ } ++ sqlite3VdbeChangeEncoding(pOut, pCtx->enc); ++ if( sqlite3VdbeMemTooBig(pOut) ){ + sqlite3_result_error_toobig(pCtx); + } + } + static int invokeValueDestructor( + const void *p, /* Value to destroy */ + void (*xDel)(void*), /* The destructor */ +- sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */ ++ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */ + ){ + assert( xDel!=SQLITE_DYNAMIC ); + if( xDel==0 ){ +@@ -83379,7 +91882,14 @@ static int invokeValueDestructor( + }else{ + xDel((void*)p); + } +- if( pCtx ) sqlite3_result_error_toobig(pCtx); ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx!=0 ){ ++ sqlite3_result_error_toobig(pCtx); ++ } ++#else ++ assert( pCtx!=0 ); ++ sqlite3_result_error_toobig(pCtx); ++#endif + return SQLITE_TOOBIG; + } + SQLITE_API void sqlite3_result_blob( +@@ -83388,6 +91898,12 @@ SQLITE_API void sqlite3_result_blob( + int n, + void (*xDel)(void *) + ){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 || n<0 ){ ++ invokeValueDestructor(z, xDel, pCtx); ++ return; ++ } ++#endif + assert( n>=0 ); + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + setResultStrOrError(pCtx, z, n, 0, xDel); +@@ -83398,8 +91914,14 @@ SQLITE_API void sqlite3_result_blob64( + sqlite3_uint64 n, + void (*xDel)(void *) + ){ +- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + assert( xDel!=SQLITE_DYNAMIC ); ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ){ ++ invokeValueDestructor(z, xDel, 0); ++ return; ++ } ++#endif ++ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + if( n>0x7fffffff ){ + (void)invokeValueDestructor(z, xDel, pCtx); + }else{ +@@ -83407,30 +91929,48 @@ SQLITE_API void sqlite3_result_blob64( + } + } + SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); + } + SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + pCtx->isError = SQLITE_ERROR; + sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); + } + #ifndef SQLITE_OMIT_UTF16 + SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + pCtx->isError = SQLITE_ERROR; + sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); + } + #endif + SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); + } + SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); + } + SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlite3VdbeMemSetNull(pCtx->pOut); + } +@@ -83440,14 +91980,37 @@ SQLITE_API void sqlite3_result_pointer( + const char *zPType, + void (*xDestructor)(void*) + ){ +- Mem *pOut = pCtx->pOut; ++ Mem *pOut; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ){ ++ invokeValueDestructor(pPtr, xDestructor, 0); ++ return; ++ } ++#endif ++ pOut = pCtx->pOut; + assert( sqlite3_mutex_held(pOut->db->mutex) ); + sqlite3VdbeMemRelease(pOut); + pOut->flags = MEM_Null; + sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor); + } + SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ +- Mem *pOut = pCtx->pOut; ++ Mem *pOut; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif ++#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0 ++ if( pCtx->pFunc!=0 ++ && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0 ++ ){ ++ char zErr[200]; ++ sqlite3_snprintf(sizeof(zErr), zErr, ++ "misuse of sqlite3_result_subtype() by %s()", ++ pCtx->pFunc->zName); ++ sqlite3_result_error(pCtx, zErr, -1); ++ return; ++ } ++#endif /* SQLITE_STRICT_SUBTYPE */ ++ pOut = pCtx->pOut; + assert( sqlite3_mutex_held(pOut->db->mutex) ); + pOut->eSubtype = eSubtype & 0xff; + pOut->flags |= MEM_Subtype; +@@ -83458,6 +92021,12 @@ SQLITE_API void sqlite3_result_text( + int n, + void (*xDel)(void *) + ){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ){ ++ invokeValueDestructor(z, xDel, 0); ++ return; ++ } ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); + } +@@ -83468,13 +92037,23 @@ SQLITE_API void sqlite3_result_text64( + void (*xDel)(void *), + unsigned char enc + ){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ){ ++ invokeValueDestructor(z, xDel, 0); ++ return; ++ } ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + assert( xDel!=SQLITE_DYNAMIC ); +- if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; ++ if( enc!=SQLITE_UTF8 ){ ++ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; ++ n &= ~(u64)1; ++ } + if( n>0x7fffffff ){ + (void)invokeValueDestructor(z, xDel, pCtx); + }else{ + setResultStrOrError(pCtx, z, (int)n, enc, xDel); ++ sqlite3VdbeMemZeroTerminateIfAble(pCtx->pOut); + } + } + #ifndef SQLITE_OMIT_UTF16 +@@ -83485,7 +92064,7 @@ SQLITE_API void sqlite3_result_text16( + void (*xDel)(void *) + ){ + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +- setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); ++ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16NATIVE, xDel); + } + SQLITE_API void sqlite3_result_text16be( + sqlite3_context *pCtx, +@@ -83494,7 +92073,7 @@ SQLITE_API void sqlite3_result_text16be( + void (*xDel)(void *) + ){ + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +- setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); ++ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16BE, xDel); + } + SQLITE_API void sqlite3_result_text16le( + sqlite3_context *pCtx, +@@ -83503,39 +92082,68 @@ SQLITE_API void sqlite3_result_text16le( + void (*xDel)(void *) + ){ + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +- setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); ++ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16LE, xDel); + } + #endif /* SQLITE_OMIT_UTF16 */ + SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ ++ Mem *pOut; ++ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++ if( pValue==0 ){ ++ sqlite3_result_null(pCtx); ++ return; ++ } ++#endif ++ pOut = pCtx->pOut; + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +- sqlite3VdbeMemCopy(pCtx->pOut, pValue); ++ sqlite3VdbeMemCopy(pOut, pValue); ++ sqlite3VdbeChangeEncoding(pOut, pCtx->enc); ++ if( sqlite3VdbeMemTooBig(pOut) ){ ++ sqlite3_result_error_toobig(pCtx); ++ } + } + SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ +- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +- sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n); ++ sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0); + } + SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){ +- Mem *pOut = pCtx->pOut; ++ Mem *pOut; ++ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return SQLITE_MISUSE_BKPT; ++#endif ++ pOut = pCtx->pOut; + assert( sqlite3_mutex_held(pOut->db->mutex) ); + if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ ++ sqlite3_result_error_toobig(pCtx); + return SQLITE_TOOBIG; + } ++#ifndef SQLITE_OMIT_INCRBLOB + sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); + return SQLITE_OK; ++#else ++ return sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); ++#endif + } + SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + pCtx->isError = errCode ? errCode : -1; + #ifdef SQLITE_DEBUG + if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; + #endif + if( pCtx->pOut->flags & MEM_Null ){ +- sqlite3VdbeMemSetStr(pCtx->pOut, sqlite3ErrStr(errCode), -1, +- SQLITE_UTF8, SQLITE_STATIC); ++ setResultStrOrError(pCtx, sqlite3ErrStr(errCode), -1, SQLITE_UTF8, ++ SQLITE_STATIC); + } + } + + /* Force an SQLITE_TOOBIG error. */ + SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + pCtx->isError = SQLITE_TOOBIG; + sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, +@@ -83544,6 +92152,9 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ + + /* An SQLITE_NOMEM error. */ + SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + sqlite3VdbeMemSetNull(pCtx->pOut); + pCtx->isError = SQLITE_NOMEM_BKPT; +@@ -83604,80 +92215,83 @@ static int sqlite3Step(Vdbe *p){ + int rc; + + assert(p); +- if( p->magic!=VDBE_MAGIC_RUN ){ +- /* We used to require that sqlite3_reset() be called before retrying +- ** sqlite3_step() after any error or after SQLITE_DONE. But beginning +- ** with version 3.7.0, we changed this so that sqlite3_reset() would +- ** be called automatically instead of throwing the SQLITE_MISUSE error. +- ** This "automatic-reset" change is not technically an incompatibility, +- ** since any application that receives an SQLITE_MISUSE is broken by +- ** definition. +- ** +- ** Nevertheless, some published applications that were originally written +- ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE +- ** returns, and those were broken by the automatic-reset change. As a +- ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the +- ** legacy behavior of returning SQLITE_MISUSE for cases where the +- ** previous sqlite3_step() returned something other than a SQLITE_LOCKED +- ** or SQLITE_BUSY error. +- */ +-#ifdef SQLITE_OMIT_AUTORESET +- if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ +- sqlite3_reset((sqlite3_stmt*)p); +- }else{ +- return SQLITE_MISUSE_BKPT; +- } +-#else +- sqlite3_reset((sqlite3_stmt*)p); +-#endif +- } +- +- /* Check that malloc() has not failed. If it has, return early. */ + db = p->db; +- if( db->mallocFailed ){ +- p->rc = SQLITE_NOMEM; +- return SQLITE_NOMEM_BKPT; +- } ++ if( p->eVdbeState!=VDBE_RUN_STATE ){ ++ restart_step: ++ if( p->eVdbeState==VDBE_READY_STATE ){ ++ if( p->expired ){ ++ p->rc = SQLITE_SCHEMA; ++ rc = SQLITE_ERROR; ++ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ ++ /* If this statement was prepared using saved SQL and an ++ ** error has occurred, then return the error code in p->rc to the ++ ** caller. Set the error code in the database handle to the same ++ ** value. ++ */ ++ rc = sqlite3VdbeTransferError(p); ++ } ++ goto end_of_step; ++ } + +- if( p->pc<0 && p->expired ){ +- p->rc = SQLITE_SCHEMA; +- rc = SQLITE_ERROR; +- if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ +- /* If this statement was prepared using saved SQL and an +- ** error has occurred, then return the error code in p->rc to the +- ** caller. Set the error code in the database handle to the same value. ++ /* If there are no other statements currently running, then ++ ** reset the interrupt flag. This prevents a call to sqlite3_interrupt ++ ** from interrupting a statement that has not yet started. + */ +- rc = sqlite3VdbeTransferError(p); +- } +- goto end_of_step; +- } +- if( p->pc<0 ){ +- /* If there are no other statements currently running, then +- ** reset the interrupt flag. This prevents a call to sqlite3_interrupt +- ** from interrupting a statement that has not yet started. +- */ +- if( db->nVdbeActive==0 ){ +- AtomicStore(&db->u1.isInterrupted, 0); +- } ++ if( db->nVdbeActive==0 ){ ++ AtomicStore(&db->u1.isInterrupted, 0); ++ } + +- assert( db->nVdbeWrite>0 || db->autoCommit==0 +- || (db->nDeferredCons==0 && db->nDeferredImmCons==0) +- ); ++ assert( db->nVdbeWrite>0 || db->autoCommit==0 ++ || ((db->nDeferredCons + db->nDeferredImmCons)==0) ++ ); + + #ifndef SQLITE_OMIT_TRACE +- if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 +- && !db->init.busy && p->zSql ){ +- sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); +- }else{ +- assert( p->startTime==0 ); +- } ++ if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ++ && !db->init.busy && p->zSql ){ ++ sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); ++ }else{ ++ assert( p->startTime==0 ); ++ } + #endif + +- db->nVdbeActive++; +- if( p->readOnly==0 ) db->nVdbeWrite++; +- if( p->bIsReader ) db->nVdbeRead++; +- p->pc = 0; ++ db->nVdbeActive++; ++ if( p->readOnly==0 ) db->nVdbeWrite++; ++ if( p->bIsReader ) db->nVdbeRead++; ++ p->pc = 0; ++ p->eVdbeState = VDBE_RUN_STATE; ++ }else ++ ++ if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){ ++ /* We used to require that sqlite3_reset() be called before retrying ++ ** sqlite3_step() after any error or after SQLITE_DONE. But beginning ++ ** with version 3.7.0, we changed this so that sqlite3_reset() would ++ ** be called automatically instead of throwing the SQLITE_MISUSE error. ++ ** This "automatic-reset" change is not technically an incompatibility, ++ ** since any application that receives an SQLITE_MISUSE is broken by ++ ** definition. ++ ** ++ ** Nevertheless, some published applications that were originally written ++ ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE ++ ** returns, and those were broken by the automatic-reset change. As a ++ ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the ++ ** legacy behavior of returning SQLITE_MISUSE for cases where the ++ ** previous sqlite3_step() returned something other than a SQLITE_LOCKED ++ ** or SQLITE_BUSY error. ++ */ ++#ifdef SQLITE_OMIT_AUTORESET ++ if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ ++ sqlite3_reset((sqlite3_stmt*)p); ++ }else{ ++ return SQLITE_MISUSE_BKPT; ++ } ++#else ++ sqlite3_reset((sqlite3_stmt*)p); ++#endif ++ assert( p->eVdbeState==VDBE_READY_STATE ); ++ goto restart_step; ++ } + } ++ + #ifdef SQLITE_DEBUG + p->rcApp = SQLITE_OK; + #endif +@@ -83692,12 +92306,17 @@ static int sqlite3Step(Vdbe *p){ + db->nVdbeExec--; + } + +- if( rc!=SQLITE_ROW ){ ++ if( rc==SQLITE_ROW ){ ++ assert( p->rc==SQLITE_OK ); ++ assert( db->mallocFailed==0 ); ++ db->errCode = SQLITE_ROW; ++ return SQLITE_ROW; ++ }else{ + #ifndef SQLITE_OMIT_TRACE + /* If the statement completed successfully, invoke the profile callback */ + checkProfileCallback(db, p); + #endif +- ++ p->pResultRow = 0; + if( rc==SQLITE_DONE && db->autoCommit ){ + assert( p->rc==SQLITE_OK ); + p->rc = doWalCallbacks(db); +@@ -83744,7 +92363,6 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ + } + db = v->db; + sqlite3_mutex_enter(db->mutex); +- v->doingRerun = 0; + while( (rc = sqlite3Step(v))==SQLITE_SCHEMA + && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){ + int savedPc = v->pc; +@@ -83770,7 +92388,13 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ + break; + } + sqlite3_reset(pStmt); +- if( savedPc>=0 ) v->doingRerun = 1; ++ if( savedPc>=0 ){ ++ /* Setting minWriteFileFormat to 254 is a signal to the OP_Init and ++ ** OP_Trace opcodes to *not* perform SQLITE_TRACE_STMT because it has ++ ** already been done once on a prior invocation that failed due to ++ ** SQLITE_SCHEMA. tag-20220401a */ ++ v->minWriteFileFormat = 254; ++ } + assert( v->expired==0 ); + } + sqlite3_mutex_leave(db->mutex); +@@ -83783,6 +92407,9 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ + ** pointer to it. + */ + SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( p==0 ) return 0; ++#endif + assert( p && p->pFunc ); + return p->pFunc->pUserData; + } +@@ -83798,7 +92425,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ + ** application defined function. + */ + SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( p==0 ) return 0; ++#else + assert( p && p->pOut ); ++#endif + return p->pOut->db; + } + +@@ -83817,10 +92448,96 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ + ** value, as a signal to the xUpdate routine that the column is unchanged. + */ + SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( p==0 ) return 0; ++#else + assert( p ); ++#endif + return sqlite3_value_nochange(p->pOut); + } + ++/* ++** The destructor function for a ValueList object. This needs to be ++** a separate function, unknowable to the application, to ensure that ++** calls to sqlite3_vtab_in_first()/sqlite3_vtab_in_next() that are not ++** preceded by activation of IN processing via sqlite3_vtab_int() do not ++** try to access a fake ValueList object inserted by a hostile extension. ++*/ ++SQLITE_PRIVATE void sqlite3VdbeValueListFree(void *pToDelete){ ++ sqlite3_free(pToDelete); ++} ++ ++/* ++** Implementation of sqlite3_vtab_in_first() (if bNext==0) and ++** sqlite3_vtab_in_next() (if bNext!=0). ++*/ ++static int valueFromValueList( ++ sqlite3_value *pVal, /* Pointer to the ValueList object */ ++ sqlite3_value **ppOut, /* Store the next value from the list here */ ++ int bNext /* 1 for _next(). 0 for _first() */ ++){ ++ int rc; ++ ValueList *pRhs; ++ ++ *ppOut = 0; ++ if( pVal==0 ) return SQLITE_MISUSE_BKPT; ++ if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){ ++ return SQLITE_ERROR; ++ }else{ ++ assert( (pVal->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) == ++ (MEM_Null|MEM_Term|MEM_Subtype) ); ++ assert( pVal->eSubtype=='p' ); ++ assert( pVal->u.zPType!=0 && strcmp(pVal->u.zPType,"ValueList")==0 ); ++ pRhs = (ValueList*)pVal->z; ++ } ++ if( bNext ){ ++ rc = sqlite3BtreeNext(pRhs->pCsr, 0); ++ }else{ ++ int dummy = 0; ++ rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy); ++ assert( rc==SQLITE_OK || sqlite3BtreeEof(pRhs->pCsr) ); ++ if( sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE; ++ } ++ if( rc==SQLITE_OK ){ ++ u32 sz; /* Size of current row in bytes */ ++ Mem sMem; /* Raw content of current row */ ++ memset(&sMem, 0, sizeof(sMem)); ++ sz = sqlite3BtreePayloadSize(pRhs->pCsr); ++ rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem); ++ if( rc==SQLITE_OK ){ ++ u8 *zBuf = (u8*)sMem.z; ++ u32 iSerial; ++ sqlite3_value *pOut = pRhs->pOut; ++ int iOff = 1 + getVarint32(&zBuf[1], iSerial); ++ sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut); ++ pOut->enc = ENC(pOut->db); ++ if( (pOut->flags & MEM_Ephem)!=0 && sqlite3VdbeMemMakeWriteable(pOut) ){ ++ rc = SQLITE_NOMEM; ++ }else{ ++ *ppOut = pOut; ++ } ++ } ++ sqlite3VdbeMemRelease(&sMem); ++ } ++ return rc; ++} ++ ++/* ++** Set the iterator value pVal to point to the first value in the set. ++** Set (*ppOut) to point to this value before returning. ++*/ ++SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){ ++ return valueFromValueList(pVal, ppOut, 0); ++} ++ ++/* ++** Set the iterator value pVal to point to the next value in the set. ++** Set (*ppOut) to point to this value before returning. ++*/ ++SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut){ ++ return valueFromValueList(pVal, ppOut, 1); ++} ++ + /* + ** Return the current time for a statement. If the current time + ** is requested more than once within the same run of a single prepared +@@ -83894,6 +92611,9 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ + SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ + AuxData *pAuxData; + ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return 0; ++#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + #if SQLITE_ENABLE_STAT4 + if( pCtx->pVdbe==0 ) return 0; +@@ -83926,8 +92646,12 @@ SQLITE_API void sqlite3_set_auxdata( + void (*xDelete)(void*) + ){ + AuxData *pAuxData; +- Vdbe *pVdbe = pCtx->pVdbe; ++ Vdbe *pVdbe; + ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pCtx==0 ) return; ++#endif ++ pVdbe= pCtx->pVdbe; + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); + #ifdef SQLITE_ENABLE_STAT4 + if( pVdbe==0 ) goto failed; +@@ -83983,7 +92707,8 @@ SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ + */ + SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ + Vdbe *pVm = (Vdbe *)pStmt; +- return pVm ? pVm->nResColumn : 0; ++ if( pVm==0 ) return 0; ++ return pVm->nResColumn; + } + + /* +@@ -83992,7 +92717,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ + */ + SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){ + Vdbe *pVm = (Vdbe *)pStmt; +- if( pVm==0 || pVm->pResultSet==0 ) return 0; ++ if( pVm==0 || pVm->pResultRow==0 ) return 0; + return pVm->nResColumn; + } + +@@ -84015,19 +92740,20 @@ static const Mem *columnNullValue(void){ + #endif + = { + /* .u = */ {0}, ++ /* .z = */ (char*)0, ++ /* .n = */ (int)0, + /* .flags = */ (u16)MEM_Null, + /* .enc = */ (u8)0, + /* .eSubtype = */ (u8)0, +- /* .n = */ (int)0, +- /* .z = */ (char*)0, +- /* .zMalloc = */ (char*)0, ++ /* .db = */ (sqlite3*)0, + /* .szMalloc = */ (int)0, + /* .uTemp = */ (u32)0, +- /* .db = */ (sqlite3*)0, ++ /* .zMalloc = */ (char*)0, + /* .xDel = */ (void(*)(void*))0, + #ifdef SQLITE_DEBUG + /* .pScopyFrom = */ (Mem*)0, + /* .mScopyFlags= */ 0, ++ /* .bScopy = */ 0, + #endif + }; + return &nullMem; +@@ -84047,8 +92773,8 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ + if( pVm==0 ) return (Mem*)columnNullValue(); + assert( pVm->db ); + sqlite3_mutex_enter(pVm->db->mutex); +- if( pVm->pResultSet!=0 && inResColumn && i>=0 ){ +- pOut = &pVm->pResultSet[i]; ++ if( pVm->pResultRow!=0 && inResColumn && i>=0 ){ ++ pOut = &pVm->pResultRow[i]; + }else{ + sqlite3Error(pVm->db, SQLITE_RANGE); + pOut = (Mem*)columnNullValue(); +@@ -84069,10 +92795,10 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ + ** sqlite3_column_int64() + ** sqlite3_column_text() + ** sqlite3_column_text16() +-** sqlite3_column_real() ++** sqlite3_column_double() + ** sqlite3_column_bytes() + ** sqlite3_column_bytes16() +-** sqiite3_column_blob() ++** sqlite3_column_blob() + */ + static void columnMallocFailure(sqlite3_stmt *pStmt) + { +@@ -84156,6 +92882,32 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ + return iType; + } + ++/* ++** Column names appropriate for EXPLAIN or EXPLAIN QUERY PLAN. ++*/ ++static const char * const azExplainColNames8[] = { ++ "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", /* EXPLAIN */ ++ "id", "parent", "notused", "detail" /* EQP */ ++}; ++static const u16 azExplainColNames16data[] = { ++ /* 0 */ 'a', 'd', 'd', 'r', 0, ++ /* 5 */ 'o', 'p', 'c', 'o', 'd', 'e', 0, ++ /* 12 */ 'p', '1', 0, ++ /* 15 */ 'p', '2', 0, ++ /* 18 */ 'p', '3', 0, ++ /* 21 */ 'p', '4', 0, ++ /* 24 */ 'p', '5', 0, ++ /* 27 */ 'c', 'o', 'm', 'm', 'e', 'n', 't', 0, ++ /* 35 */ 'i', 'd', 0, ++ /* 38 */ 'p', 'a', 'r', 'e', 'n', 't', 0, ++ /* 45 */ 'n', 'o', 't', 'u', 's', 'e', 'd', 0, ++ /* 53 */ 'd', 'e', 't', 'a', 'i', 'l', 0 ++}; ++static const u8 iExplainColNames16[] = { ++ 0, 5, 12, 15, 18, 21, 24, 27, ++ 35, 38, 45, 53 ++}; ++ + /* + ** Convert the N-th element of pStmt->pColName[] into a string using + ** xFunc() then return that string. If N is out of range, return 0. +@@ -84188,15 +92940,29 @@ static const void *columnName( + return 0; + } + #endif ++ if( N<0 ) return 0; + ret = 0; + p = (Vdbe *)pStmt; + db = p->db; + assert( db!=0 ); +- n = sqlite3_column_count(pStmt); +- if( N=0 ){ ++ sqlite3_mutex_enter(db->mutex); ++ ++ if( p->explain ){ ++ if( useType>0 ) goto columnName_end; ++ n = p->explain==1 ? 8 : 4; ++ if( N>=n ) goto columnName_end; ++ if( useUtf16 ){ ++ int i = iExplainColNames16[N + 8*p->explain - 8]; ++ ret = (void*)&azExplainColNames16data[i]; ++ }else{ ++ ret = (void*)azExplainColNames8[N + 8*p->explain - 8]; ++ } ++ goto columnName_end; ++ } ++ n = p->nResColumn; ++ if( NmallocFailed; + N += useType*n; +- sqlite3_mutex_enter(db->mutex); +- assert( db->mallocFailed==0 ); + #ifndef SQLITE_OMIT_UTF16 + if( useUtf16 ){ + ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); +@@ -84208,12 +92974,14 @@ static const void *columnName( + /* A malloc may have failed inside of the _text() call. If this + ** is the case, clear the mallocFailed flag and return NULL. + */ +- if( db->mallocFailed ){ ++ assert( db->mallocFailed==0 || db->mallocFailed==1 ); ++ if( db->mallocFailed > prior_mallocFailed ){ + sqlite3OomClear(db); + ret = 0; + } +- sqlite3_mutex_leave(db->mutex); + } ++columnName_end: ++ sqlite3_mutex_leave(db->mutex); + return ret; + } + +@@ -84306,33 +93074,43 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ + /* + ** Unbind the value bound to variable i in virtual machine p. This is the + ** the same as binding a NULL value to the column. If the "i" parameter is +-** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. ++** out of range, then SQLITE_RANGE is returned. Otherwise SQLITE_OK. + ** + ** A successful evaluation of this routine acquires the mutex on p. + ** the mutex is released if any kind of error occurs. + ** + ** The error code stored in database p->db is overwritten with the return + ** value in any case. ++** ++** (tag-20240917-01) If vdbeUnbind(p,(u32)(i-1)) returns SQLITE_OK, ++** that means all of the the following will be true: ++** ++** p!=0 ++** p->pVar!=0 ++** i>0 ++** i<=p->nVar ++** ++** An assert() is normally added after vdbeUnbind() to help static analyzers ++** realize this. + */ +-static int vdbeUnbind(Vdbe *p, int i){ ++static int vdbeUnbind(Vdbe *p, unsigned int i){ + Mem *pVar; + if( vdbeSafetyNotNull(p) ){ + return SQLITE_MISUSE_BKPT; + } + sqlite3_mutex_enter(p->db->mutex); +- if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ +- sqlite3Error(p->db, SQLITE_MISUSE); ++ if( p->eVdbeState!=VDBE_READY_STATE ){ ++ sqlite3Error(p->db, SQLITE_MISUSE_BKPT); + sqlite3_mutex_leave(p->db->mutex); + sqlite3_log(SQLITE_MISUSE, + "bind on a busy prepared statement: [%s]", p->zSql); + return SQLITE_MISUSE_BKPT; + } +- if( i<1 || i>p->nVar ){ ++ if( i>=(unsigned int)p->nVar ){ + sqlite3Error(p->db, SQLITE_RANGE); + sqlite3_mutex_leave(p->db->mutex); + return SQLITE_RANGE; + } +- i--; + pVar = &p->aVar[i]; + sqlite3VdbeMemRelease(pVar); + pVar->flags = MEM_Null; +@@ -84361,7 +93139,7 @@ static int bindText( + sqlite3_stmt *pStmt, /* The statement to bind against */ + int i, /* Index of the parameter to bind */ + const void *zData, /* Pointer to the data to be bound */ +- int nData, /* Number of bytes of data to be bound */ ++ i64 nData, /* Number of bytes of data to be bound */ + void (*xDel)(void*), /* Destructor for the data */ + u8 encoding /* Encoding for the data */ + ){ +@@ -84369,8 +93147,9 @@ static int bindText( + Mem *pVar; + int rc; + +- rc = vdbeUnbind(p, i); ++ rc = vdbeUnbind(p, (u32)(i-1)); + if( rc==SQLITE_OK ){ ++ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ + if( zData!=0 ){ + pVar = &p->aVar[i-1]; + rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); +@@ -84413,17 +93192,14 @@ SQLITE_API int sqlite3_bind_blob64( + void (*xDel)(void*) + ){ + assert( xDel!=SQLITE_DYNAMIC ); +- if( nData>0x7fffffff ){ +- return invokeValueDestructor(zData, xDel, 0); +- }else{ +- return bindText(pStmt, i, zData, (int)nData, xDel, 0); +- } ++ return bindText(pStmt, i, zData, nData, xDel, 0); + } + SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ + int rc; + Vdbe *p = (Vdbe *)pStmt; +- rc = vdbeUnbind(p, i); ++ rc = vdbeUnbind(p, (u32)(i-1)); + if( rc==SQLITE_OK ){ ++ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ + sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); + sqlite3_mutex_leave(p->db->mutex); + } +@@ -84435,8 +93211,9 @@ SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ + SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ + int rc; + Vdbe *p = (Vdbe *)pStmt; +- rc = vdbeUnbind(p, i); ++ rc = vdbeUnbind(p, (u32)(i-1)); + if( rc==SQLITE_OK ){ ++ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ + sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); + sqlite3_mutex_leave(p->db->mutex); + } +@@ -84445,8 +93222,9 @@ SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValu + SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ + int rc; + Vdbe *p = (Vdbe*)pStmt; +- rc = vdbeUnbind(p, i); ++ rc = vdbeUnbind(p, (u32)(i-1)); + if( rc==SQLITE_OK ){ ++ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ + sqlite3_mutex_leave(p->db->mutex); + } + return rc; +@@ -84460,8 +93238,9 @@ SQLITE_API int sqlite3_bind_pointer( + ){ + int rc; + Vdbe *p = (Vdbe*)pStmt; +- rc = vdbeUnbind(p, i); ++ rc = vdbeUnbind(p, (u32)(i-1)); + if( rc==SQLITE_OK ){ ++ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ + sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); + sqlite3_mutex_leave(p->db->mutex); + }else if( xDestructor ){ +@@ -84487,22 +93266,21 @@ SQLITE_API int sqlite3_bind_text64( + unsigned char enc + ){ + assert( xDel!=SQLITE_DYNAMIC ); +- if( nData>0x7fffffff ){ +- return invokeValueDestructor(zData, xDel, 0); +- }else{ ++ if( enc!=SQLITE_UTF8 ){ + if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; +- return bindText(pStmt, i, zData, (int)nData, xDel, enc); ++ nData &= ~(u64)1; + } ++ return bindText(pStmt, i, zData, nData, xDel, enc); + } + #ifndef SQLITE_OMIT_UTF16 + SQLITE_API int sqlite3_bind_text16( + sqlite3_stmt *pStmt, + int i, + const void *zData, +- int nData, ++ int n, + void (*xDel)(void*) + ){ +- return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); ++ return bindText(pStmt, i, zData, n & ~(u64)1, xDel, SQLITE_UTF16NATIVE); + } + #endif /* SQLITE_OMIT_UTF16 */ + SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ +@@ -84513,7 +93291,10 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu + break; + } + case SQLITE_FLOAT: { +- rc = sqlite3_bind_double(pStmt, i, pValue->u.r); ++ assert( pValue->flags & (MEM_Real|MEM_IntReal) ); ++ rc = sqlite3_bind_double(pStmt, i, ++ (pValue->flags & MEM_Real) ? pValue->u.r : (double)pValue->u.i ++ ); + break; + } + case SQLITE_BLOB: { +@@ -84539,9 +93320,14 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu + SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ + int rc; + Vdbe *p = (Vdbe *)pStmt; +- rc = vdbeUnbind(p, i); ++ rc = vdbeUnbind(p, (u32)(i-1)); + if( rc==SQLITE_OK ){ ++ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ ++#ifndef SQLITE_OMIT_INCRBLOB + sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); ++#else ++ rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); ++#endif + sqlite3_mutex_leave(p->db->mutex); + } + return rc; +@@ -84549,6 +93335,9 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ + SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){ + int rc; + Vdbe *p = (Vdbe *)pStmt; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( p==0 ) return SQLITE_MISUSE_BKPT; ++#endif + sqlite3_mutex_enter(p->db->mutex); + if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){ + rc = SQLITE_TOOBIG; +@@ -84669,12 +93458,48 @@ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){ + return pStmt ? ((Vdbe*)pStmt)->explain : 0; + } + ++/* ++** Set the explain mode for a statement. ++*/ ++SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){ ++ Vdbe *v = (Vdbe*)pStmt; ++ int rc; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( pStmt==0 ) return SQLITE_MISUSE_BKPT; ++#endif ++ sqlite3_mutex_enter(v->db->mutex); ++ if( ((int)v->explain)==eMode ){ ++ rc = SQLITE_OK; ++ }else if( eMode<0 || eMode>2 ){ ++ rc = SQLITE_ERROR; ++ }else if( (v->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){ ++ rc = SQLITE_ERROR; ++ }else if( v->eVdbeState!=VDBE_READY_STATE ){ ++ rc = SQLITE_BUSY; ++ }else if( v->nMem>=10 && (eMode!=2 || v->haveEqpOps) ){ ++ /* No reprepare necessary */ ++ v->explain = eMode; ++ rc = SQLITE_OK; ++ }else{ ++ v->explain = eMode; ++ rc = sqlite3Reprepare(v); ++ v->haveEqpOps = eMode==2; ++ } ++ if( v->explain ){ ++ v->nResColumn = 12 - 4*v->explain; ++ }else{ ++ v->nResColumn = v->nResAlloc; ++ } ++ sqlite3_mutex_leave(v->db->mutex); ++ return rc; ++} ++ + /* + ** Return true if the prepared statement is in need of being reset. + */ + SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ + Vdbe *v = (Vdbe*)pStmt; +- return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0; ++ return v!=0 && v->eVdbeState==VDBE_RUN_STATE; + } + + /* +@@ -84695,7 +93520,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ + if( pStmt==0 ){ + pNext = (sqlite3_stmt*)pDb->pVdbe; + }else{ +- pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pNext; ++ pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pVNext; + } + sqlite3_mutex_leave(pDb->mutex); + return pNext; +@@ -84720,9 +93545,11 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ + sqlite3_mutex_enter(db->mutex); + v = 0; + db->pnBytesFreed = (int*)&v; +- sqlite3VdbeClearObject(db, pVdbe); +- sqlite3DbFree(db, pVdbe); ++ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); ++ db->lookaside.pEnd = db->lookaside.pStart; ++ sqlite3VdbeDelete(pVdbe); + db->pnBytesFreed = 0; ++ db->lookaside.pEnd = db->lookaside.pTrueEnd; + sqlite3_mutex_leave(db->mutex); + }else{ + v = pVdbe->aCounter[op]; +@@ -84806,10 +93633,17 @@ static UnpackedRecord *vdbeUnpackRecord( + ** a field of the row currently being updated or deleted. + */ + SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ +- PreUpdate *p = db->pPreUpdate; ++ PreUpdate *p; + Mem *pMem; + int rc = SQLITE_OK; ++ int iStore = 0; + ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( db==0 || ppValue==0 ){ ++ return SQLITE_MISUSE_BKPT; ++ } ++#endif ++ p = db->pPreUpdate; + /* Test that this call is being made from within an SQLITE_DELETE or + ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ + if( !p || p->op==SQLITE_INSERT ){ +@@ -84817,43 +93651,75 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa + goto preupdate_old_out; + } + if( p->pPk ){ +- iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); ++ iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); ++ }else{ ++ iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); + } +- if( iIdx>=p->pCsr->nField || iIdx<0 ){ ++ if( iStore>=p->pCsr->nField || iStore<0 ){ + rc = SQLITE_RANGE; + goto preupdate_old_out; + } + +- /* If the old.* record has not yet been loaded into memory, do so now. */ +- if( p->pUnpacked==0 ){ +- u32 nRec; +- u8 *aRec; +- +- nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); +- aRec = sqlite3DbMallocRaw(db, nRec); +- if( !aRec ) goto preupdate_old_out; +- rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); +- if( rc==SQLITE_OK ){ +- p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); +- if( !p->pUnpacked ) rc = SQLITE_NOMEM; +- } +- if( rc!=SQLITE_OK ){ +- sqlite3DbFree(db, aRec); +- goto preupdate_old_out; +- } +- p->aRecord = aRec; +- } +- +- pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; + if( iIdx==p->pTab->iPKey ){ ++ *ppValue = pMem = &p->oldipk; + sqlite3VdbeMemSetInt64(pMem, p->iKey1); +- }else if( iIdx>=p->pUnpacked->nField ){ +- *ppValue = (sqlite3_value *)columnNullValue(); +- }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ +- if( pMem->flags & (MEM_Int|MEM_IntReal) ){ +- testcase( pMem->flags & MEM_Int ); +- testcase( pMem->flags & MEM_IntReal ); +- sqlite3VdbeMemRealify(pMem); ++ }else{ ++ ++ /* If the old.* record has not yet been loaded into memory, do so now. */ ++ if( p->pUnpacked==0 ){ ++ u32 nRec; ++ u8 *aRec; ++ ++ assert( p->pCsr->eCurType==CURTYPE_BTREE ); ++ nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); ++ aRec = sqlite3DbMallocRaw(db, nRec); ++ if( !aRec ) goto preupdate_old_out; ++ rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); ++ if( rc==SQLITE_OK ){ ++ p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec); ++ if( !p->pUnpacked ) rc = SQLITE_NOMEM; ++ } ++ if( rc!=SQLITE_OK ){ ++ sqlite3DbFree(db, aRec); ++ goto preupdate_old_out; ++ } ++ p->aRecord = aRec; ++ } ++ ++ pMem = *ppValue = &p->pUnpacked->aMem[iStore]; ++ if( iStore>=p->pUnpacked->nField ){ ++ /* This occurs when the table has been extended using ALTER TABLE ++ ** ADD COLUMN. The value to return is the default value of the column. */ ++ Column *pCol = &p->pTab->aCol[iIdx]; ++ if( pCol->iDflt>0 ){ ++ if( p->apDflt==0 ){ ++ int nByte; ++ assert( sizeof(sqlite3_value*)*UMXV(p->pTab->nCol) < 0x7fffffff ); ++ nByte = sizeof(sqlite3_value*)*p->pTab->nCol; ++ p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte); ++ if( p->apDflt==0 ) goto preupdate_old_out; ++ } ++ if( p->apDflt[iIdx]==0 ){ ++ sqlite3_value *pVal = 0; ++ Expr *pDflt; ++ assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) ); ++ pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; ++ rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal); ++ if( rc==SQLITE_OK && pVal==0 ){ ++ rc = SQLITE_CORRUPT_BKPT; ++ } ++ p->apDflt[iIdx] = pVal; ++ } ++ *ppValue = p->apDflt[iIdx]; ++ }else{ ++ *ppValue = (sqlite3_value *)columnNullValue(); ++ } ++ }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ ++ if( pMem->flags & (MEM_Int|MEM_IntReal) ){ ++ testcase( pMem->flags & MEM_Int ); ++ testcase( pMem->flags & MEM_IntReal ); ++ sqlite3VdbeMemRealify(pMem); ++ } + } + } + +@@ -84869,8 +93735,13 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa + ** the number of columns in the row being updated, deleted or inserted. + */ + SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){ +- PreUpdate *p = db->pPreUpdate; +- return (p ? p->keyinfo.nKeyField : 0); ++ PreUpdate *p; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ p = db!=0 ? db->pPreUpdate : 0; ++#else ++ p = db->pPreUpdate; ++#endif ++ return (p ? p->pKeyinfo->nKeyField : 0); + } + #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + +@@ -84887,29 +93758,60 @@ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){ + ** or SET DEFAULT action is considered a trigger. + */ + SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){ +- PreUpdate *p = db->pPreUpdate; ++ PreUpdate *p; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ p = db!=0 ? db->pPreUpdate : 0; ++#else ++ p = db->pPreUpdate; ++#endif + return (p ? p->v->nFrame : 0); + } + #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + ++#ifdef SQLITE_ENABLE_PREUPDATE_HOOK ++/* ++** This function is designed to be called from within a pre-update callback ++** only. ++*/ ++SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){ ++ PreUpdate *p; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ p = db!=0 ? db->pPreUpdate : 0; ++#else ++ p = db->pPreUpdate; ++#endif ++ return (p ? p->iBlobWrite : -1); ++} ++#endif ++ + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK + /* + ** This function is called from within a pre-update callback to retrieve + ** a field of the row currently being updated or inserted. + */ + SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ +- PreUpdate *p = db->pPreUpdate; ++ PreUpdate *p; + int rc = SQLITE_OK; + Mem *pMem; ++ int iStore = 0; + ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( db==0 || ppValue==0 ){ ++ return SQLITE_MISUSE_BKPT; ++ } ++#endif ++ p = db->pPreUpdate; + if( !p || p->op==SQLITE_DELETE ){ + rc = SQLITE_MISUSE_BKPT; + goto preupdate_new_out; + } + if( p->pPk && p->op!=SQLITE_UPDATE ){ +- iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); ++ iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); ++ }else{ ++ iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); + } +- if( iIdx>=p->pCsr->nField || iIdx<0 ){ ++ ++ if( iStore>=p->pCsr->nField || iStore<0 ){ + rc = SQLITE_RANGE; + goto preupdate_new_out; + } +@@ -84922,40 +93824,41 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa + Mem *pData = &p->v->aMem[p->iNewReg]; + rc = ExpandBlob(pData); + if( rc!=SQLITE_OK ) goto preupdate_new_out; +- pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); ++ pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z); + if( !pUnpack ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + p->pNewUnpacked = pUnpack; + } +- pMem = &pUnpack->aMem[iIdx]; ++ pMem = &pUnpack->aMem[iStore]; + if( iIdx==p->pTab->iPKey ){ + sqlite3VdbeMemSetInt64(pMem, p->iKey2); +- }else if( iIdx>=pUnpack->nField ){ ++ }else if( iStore>=pUnpack->nField ){ + pMem = (sqlite3_value *)columnNullValue(); + } + }else{ +- /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required ++ /* For an UPDATE, memory cell (p->iNewReg+1+iStore) contains the required + ** value. Make a copy of the cell contents and return a pointer to it. + ** It is not safe to return a pointer to the memory cell itself as the + ** caller may modify the value text encoding. + */ + assert( p->op==SQLITE_UPDATE ); + if( !p->aNew ){ +- p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField); ++ assert( sizeof(Mem)*UMXV(p->pCsr->nField) < 0x7fffffff ); ++ p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem)*p->pCsr->nField); + if( !p->aNew ){ + rc = SQLITE_NOMEM; + goto preupdate_new_out; + } + } +- assert( iIdx>=0 && iIdxpCsr->nField ); +- pMem = &p->aNew[iIdx]; ++ assert( iStore>=0 && iStorepCsr->nField ); ++ pMem = &p->aNew[iStore]; + if( pMem->flags==0 ){ + if( iIdx==p->pTab->iPKey ){ + sqlite3VdbeMemSetInt64(pMem, p->iKey2); + }else{ +- rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); ++ rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iStore]); + if( rc!=SQLITE_OK ) goto preupdate_new_out; + } + } +@@ -84972,23 +93875,79 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa + /* + ** Return status data for a single loop within query pStmt. + */ +-SQLITE_API int sqlite3_stmt_scanstatus( ++SQLITE_API int sqlite3_stmt_scanstatus_v2( + sqlite3_stmt *pStmt, /* Prepared statement being queried */ +- int idx, /* Index of loop to report on */ ++ int iScan, /* Index of loop to report on */ + int iScanStatusOp, /* Which metric to return */ ++ int flags, + void *pOut /* OUT: Write the answer here */ + ){ + Vdbe *p = (Vdbe*)pStmt; +- ScanStatus *pScan; +- if( idx<0 || idx>=p->nScan ) return 1; ++ VdbeOp *aOp; ++ int nOp; ++ ScanStatus *pScan = 0; ++ int idx; ++ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( p==0 || pOut==0 ++ || iScanStatusOpSQLITE_SCANSTAT_NCYCLE ){ ++ return 1; ++ } ++#endif ++ aOp = p->aOp; ++ nOp = p->nOp; ++ if( p->pFrame ){ ++ VdbeFrame *pFrame; ++ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); ++ aOp = pFrame->aOp; ++ nOp = pFrame->nOp; ++ } ++ ++ if( iScan<0 ){ ++ int ii; ++ if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){ ++ i64 res = 0; ++ for(ii=0; iinScan; idx++){ ++ pScan = &p->aScan[idx]; ++ if( pScan->zName ){ ++ iScan--; ++ if( iScan<0 ) break; ++ } ++ } ++ } ++ if( idx>=p->nScan ) return 1; ++ assert( pScan==0 || pScan==&p->aScan[idx] ); + pScan = &p->aScan[idx]; ++ + switch( iScanStatusOp ){ + case SQLITE_SCANSTAT_NLOOP: { +- *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; ++ if( pScan->addrLoop>0 ){ ++ *(sqlite3_int64*)pOut = aOp[pScan->addrLoop].nExec; ++ }else{ ++ *(sqlite3_int64*)pOut = -1; ++ } + break; + } + case SQLITE_SCANSTAT_NVISIT: { +- *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; ++ if( pScan->addrVisit>0 ){ ++ *(sqlite3_int64*)pOut = aOp[pScan->addrVisit].nExec; ++ }else{ ++ *(sqlite3_int64*)pOut = -1; ++ } + break; + } + case SQLITE_SCANSTAT_EST: { +@@ -85007,7 +93966,7 @@ SQLITE_API int sqlite3_stmt_scanstatus( + } + case SQLITE_SCANSTAT_EXPLAIN: { + if( pScan->addrExplain ){ +- *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z; ++ *(const char**)pOut = aOp[ pScan->addrExplain ].p4.z; + }else{ + *(const char**)pOut = 0; + } +@@ -85015,12 +93974,51 @@ SQLITE_API int sqlite3_stmt_scanstatus( + } + case SQLITE_SCANSTAT_SELECTID: { + if( pScan->addrExplain ){ +- *(int*)pOut = p->aOp[ pScan->addrExplain ].p1; ++ *(int*)pOut = aOp[ pScan->addrExplain ].p1; ++ }else{ ++ *(int*)pOut = -1; ++ } ++ break; ++ } ++ case SQLITE_SCANSTAT_PARENTID: { ++ if( pScan->addrExplain ){ ++ *(int*)pOut = aOp[ pScan->addrExplain ].p2; + }else{ + *(int*)pOut = -1; + } + break; + } ++ case SQLITE_SCANSTAT_NCYCLE: { ++ i64 res = 0; ++ if( pScan->aAddrRange[0]==0 ){ ++ res = -1; ++ }else{ ++ int ii; ++ for(ii=0; iiaAddrRange); ii+=2){ ++ int iIns = pScan->aAddrRange[ii]; ++ int iEnd = pScan->aAddrRange[ii+1]; ++ if( iIns==0 ) break; ++ if( iIns>0 ){ ++ while( iIns<=iEnd ){ ++ res += aOp[iIns].nCycle; ++ iIns++; ++ } ++ }else{ ++ int iOp; ++ for(iOp=0; iOpp1!=iEnd ) continue; ++ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){ ++ continue; ++ } ++ res += aOp[iOp].nCycle; ++ } ++ } ++ } ++ } ++ *(i64*)pOut = res; ++ break; ++ } + default: { + return 1; + } +@@ -85028,12 +94026,29 @@ SQLITE_API int sqlite3_stmt_scanstatus( + return 0; + } + ++/* ++** Return status data for a single loop within query pStmt. ++*/ ++SQLITE_API int sqlite3_stmt_scanstatus( ++ sqlite3_stmt *pStmt, /* Prepared statement being queried */ ++ int iScan, /* Index of loop to report on */ ++ int iScanStatusOp, /* Which metric to return */ ++ void *pOut /* OUT: Write the answer here */ ++){ ++ return sqlite3_stmt_scanstatus_v2(pStmt, iScan, iScanStatusOp, 0, pOut); ++} ++ + /* + ** Zero all counters associated with the sqlite3_stmt_scanstatus() data. + */ + SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe*)pStmt; +- memset(p->anExec, 0, p->nOp * sizeof(i64)); ++ int ii; ++ for(ii=0; p!=0 && iinOp; ii++){ ++ Op *pOp = &p->aOp[ii]; ++ pOp->nExec = 0; ++ pOp->nCycle = 0; ++ } + } + #endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ + +@@ -85125,11 +94140,9 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( + #ifndef SQLITE_OMIT_UTF16 + Mem utf8; /* Used to convert UTF16 into UTF8 for display */ + #endif +- char zBase[100]; /* Initial working space */ + + db = p->db; +- sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase), +- db->aLimit[SQLITE_LIMIT_LENGTH]); ++ sqlite3StrAccumInit(&out, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); + if( db->nVdbeExec>1 ){ + while( *zRawSql ){ + const char *zStart = zRawSql; +@@ -85166,7 +94179,7 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( + assert( idx>0 ); + } + zRawSql += nToken; +- nextIndex = idx + 1; ++ nextIndex = MAX(idx + 1, nextIndex); + assert( idx>0 && idx<=p->nVar ); + pVar = &p->aVar[idx-1]; + if( pVar->flags & MEM_Null ){ +@@ -85259,6 +94272,104 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( + /* #include "sqliteInt.h" */ + /* #include "vdbeInt.h" */ + ++/* ++** High-resolution hardware timer used for debugging and testing only. ++*/ ++#if defined(VDBE_PROFILE) \ ++ || defined(SQLITE_PERFORMANCE_TRACE) \ ++ || defined(SQLITE_ENABLE_STMT_SCANSTATUS) ++/************** Include hwtime.h in the middle of vdbe.c *********************/ ++/************** Begin file hwtime.h ******************************************/ ++/* ++** 2008 May 27 ++** ++** 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 contains inline asm code for retrieving "high-performance" ++** counters for x86 and x86_64 class CPUs. ++*/ ++#ifndef SQLITE_HWTIME_H ++#define SQLITE_HWTIME_H ++ ++/* ++** The following routine only works on Pentium-class (or newer) processors. ++** It uses the RDTSC opcode to read the cycle count value out of the ++** processor and returns that value. This can be used for high-res ++** profiling. ++*/ ++#if !defined(__STRICT_ANSI__) && \ ++ (defined(__GNUC__) || defined(_MSC_VER)) && \ ++ (defined(i386) || defined(__i386__) || defined(_M_IX86)) ++ ++ #if defined(__GNUC__) ++ ++ __inline__ sqlite_uint64 sqlite3Hwtime(void){ ++ unsigned int lo, hi; ++ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); ++ return (sqlite_uint64)hi << 32 | lo; ++ } ++ ++ #elif defined(_MSC_VER) ++ ++ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ ++ __asm { ++ rdtsc ++ ret ; return value at EDX:EAX ++ } ++ } ++ ++ #endif ++ ++#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) ++ ++ __inline__ sqlite_uint64 sqlite3Hwtime(void){ ++ unsigned int lo, hi; ++ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); ++ return (sqlite_uint64)hi << 32 | lo; ++ } ++ ++#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) ++ ++ __inline__ sqlite_uint64 sqlite3Hwtime(void){ ++ unsigned long long retval; ++ unsigned long junk; ++ __asm__ __volatile__ ("\n\ ++ 1: mftbu %1\n\ ++ mftb %L0\n\ ++ mftbu %0\n\ ++ cmpw %0,%1\n\ ++ bne 1b" ++ : "=r" (retval), "=r" (junk)); ++ return retval; ++ } ++ ++#else ++ ++ /* ++ ** asm() is needed for hardware timing support. Without asm(), ++ ** disable the sqlite3Hwtime() routine. ++ ** ++ ** sqlite3Hwtime() is only used for some obscure debugging ++ ** and analysis configurations, not in any deliverable, so this ++ ** should not be a great loss. ++ */ ++SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } ++ ++#endif ++ ++#endif /* !defined(SQLITE_HWTIME_H) */ ++ ++/************** End of hwtime.h **********************************************/ ++/************** Continuing where we left off in vdbe.c ***********************/ ++#endif ++ + /* + ** Invoke this macro on memory cells just prior to changing the + ** value of the cell. This macro verifies that shallow copies are +@@ -85370,8 +94481,12 @@ SQLITE_API int sqlite3_found_count = 0; + ** sqlite3CantopenError(lineno) + */ + static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ +- static int n = 0; ++ static u64 n = 0; ++ (void)pc; ++ (void)pOp; ++ (void)v; + n++; ++ if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */ + } + #endif + +@@ -85479,7 +94594,6 @@ static VdbeCursor *allocateCursor( + Vdbe *p, /* The virtual machine */ + int iCur, /* Index of the new VdbeCursor */ + int nField, /* Number of fields in the table or index */ +- int iDb, /* Database the cursor belongs to, or -1 */ + u8 eCurType /* Type of the new cursor */ + ){ + /* Find the memory cell that will be used to store the blob of memory +@@ -85502,34 +94616,46 @@ static VdbeCursor *allocateCursor( + */ + Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; + +- int nByte; ++ i64 nByte; + VdbeCursor *pCx = 0; +- nByte = +- ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + +- (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); ++ nByte = SZ_VDBECURSOR(nField); ++ assert( ROUND8(nByte)==nByte ); ++ if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize(); + + assert( iCur>=0 && iCurnCursor ); + if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ +- /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag +- ** is clear. Otherwise, if this is an ephemeral cursor created by +- ** OP_OpenDup, the cursor will not be closed and will still be part +- ** of a BtShared.pCursor list. */ +- if( p->apCsr[iCur]->pBtx==0 ) p->apCsr[iCur]->isEphemeral = 0; +- sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); ++ sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); + p->apCsr[iCur] = 0; + } +- if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ +- p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; +- memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); +- pCx->eCurType = eCurType; +- pCx->iDb = iDb; +- pCx->nField = nField; +- pCx->aOffset = &pCx->aType[nField]; +- if( eCurType==CURTYPE_BTREE ){ +- pCx->uc.pCursor = (BtCursor*) +- &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; +- sqlite3BtreeCursorZero(pCx->uc.pCursor); ++ ++ /* There used to be a call to sqlite3VdbeMemClearAndResize() to make sure ++ ** the pMem used to hold space for the cursor has enough storage available ++ ** in pMem->zMalloc. But for the special case of the aMem[] entries used ++ ** to hold cursors, it is faster to in-line the logic. */ ++ assert( pMem->flags==MEM_Undefined ); ++ assert( (pMem->flags & MEM_Dyn)==0 ); ++ assert( pMem->szMalloc==0 || pMem->z==pMem->zMalloc ); ++ if( pMem->szMallocszMalloc>0 ){ ++ sqlite3DbFreeNN(pMem->db, pMem->zMalloc); ++ } ++ pMem->z = pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, nByte); ++ if( pMem->zMalloc==0 ){ ++ pMem->szMalloc = 0; ++ return 0; + } ++ pMem->szMalloc = (int)nByte; ++ } ++ ++ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc; ++ memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); ++ pCx->eCurType = eCurType; ++ pCx->nField = nField; ++ pCx->aOffset = &pCx->aType[nField]; ++ if( eCurType==CURTYPE_BTREE ){ ++ assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) ); ++ pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)]; ++ sqlite3BtreeCursorZero(pCx->uc.pCursor); + } + return pCx; + } +@@ -85541,7 +94667,8 @@ static VdbeCursor *allocateCursor( + ** return false. + */ + static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){ +- i64 iValue = (double)rValue; ++ i64 iValue; ++ iValue = sqlite3RealToI64(rValue); + if( sqlite3RealSameAsInt(rValue,iValue) ){ + *piValue = iValue; + return 1; +@@ -85597,6 +94724,10 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){ + ** always preferred, even if the affinity is REAL, because + ** an integer representation is more space efficient on disk. + ** ++** SQLITE_AFF_FLEXNUM: ++** If the value is text, then try to convert it into a number of ++** some kind (integer or real) but do not make any other changes. ++** + ** SQLITE_AFF_TEXT: + ** Convert pRec to a text representation. + ** +@@ -85611,11 +94742,11 @@ static void applyAffinity( + ){ + if( affinity>=SQLITE_AFF_NUMERIC ){ + assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL +- || affinity==SQLITE_AFF_NUMERIC ); ++ || affinity==SQLITE_AFF_NUMERIC || affinity==SQLITE_AFF_FLEXNUM ); + if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/ +- if( (pRec->flags & MEM_Real)==0 ){ ++ if( (pRec->flags & (MEM_Real|MEM_IntReal))==0 ){ + if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1); +- }else{ ++ }else if( affinity<=SQLITE_AFF_REAL ){ + sqlite3VdbeIntegerAffinity(pRec); + } + } +@@ -85676,7 +94807,10 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ + sqlite3_int64 ix; + assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ); + assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); +- ExpandBlob(pMem); ++ if( ExpandBlob(pMem) ){ ++ pMem->u.i = 0; ++ return MEM_Int; ++ } + rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); + if( rc<=0 ){ + if( rc==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){ +@@ -85700,17 +94834,18 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ + ** But it does set pMem->u.r and pMem->u.i appropriately. + */ + static u16 numericType(Mem *pMem){ +- if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal) ){ ++ assert( (pMem->flags & MEM_Null)==0 ++ || pMem->db==0 || pMem->db->mallocFailed ); ++ if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null) ){ + testcase( pMem->flags & MEM_Int ); + testcase( pMem->flags & MEM_Real ); + testcase( pMem->flags & MEM_IntReal ); +- return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal); +- } +- if( pMem->flags & (MEM_Str|MEM_Blob) ){ +- testcase( pMem->flags & MEM_Str ); +- testcase( pMem->flags & MEM_Blob ); +- return computeNumericType(pMem); ++ return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null); + } ++ assert( pMem->flags & (MEM_Str|MEM_Blob) ); ++ testcase( pMem->flags & MEM_Str ); ++ testcase( pMem->flags & MEM_Blob ); ++ return computeNumericType(pMem); + return 0; + } + +@@ -85771,6 +94906,9 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){ + sqlite3_str_appendchar(pStr, 1, (c>=0x20&&c<=0x7f) ? c : '.'); + } + sqlite3_str_appendf(pStr, "]%s", encnames[pMem->enc]); ++ if( f & MEM_Term ){ ++ sqlite3_str_appendf(pStr, "(0-term)"); ++ } + } + } + #endif +@@ -85809,11 +94947,17 @@ static void registerTrace(int iReg, Mem *p){ + printf("R[%d] = ", iReg); + memTracePrint(p); + if( p->pScopyFrom ){ ++ assert( p->pScopyFrom->bScopy ); + printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg])); + } + printf("\n"); + sqlite3VdbeCheckMemInvariants(p); + } ++/**/ void sqlite3PrintMem(Mem *pMem){ ++ memTracePrint(pMem); ++ printf("\n"); ++ fflush(stdout); ++} + #endif + + #ifdef SQLITE_DEBUG +@@ -85834,106 +94978,6 @@ SQLITE_PRIVATE void sqlite3VdbeRegisterDump(Vdbe *v){ + # define REGISTER_TRACE(R,M) + #endif + +- +-#ifdef VDBE_PROFILE +- +-/* +-** hwtime.h contains inline assembler code for implementing +-** high-performance timing routines. +-*/ +-/************** Include hwtime.h in the middle of vdbe.c *********************/ +-/************** Begin file hwtime.h ******************************************/ +-/* +-** 2008 May 27 +-** +-** 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 contains inline asm code for retrieving "high-performance" +-** counters for x86 and x86_64 class CPUs. +-*/ +-#ifndef SQLITE_HWTIME_H +-#define SQLITE_HWTIME_H +- +-/* +-** The following routine only works on pentium-class (or newer) processors. +-** It uses the RDTSC opcode to read the cycle count value out of the +-** processor and returns that value. This can be used for high-res +-** profiling. +-*/ +-#if !defined(__STRICT_ANSI__) && \ +- (defined(__GNUC__) || defined(_MSC_VER)) && \ +- (defined(i386) || defined(__i386__) || defined(_M_IX86)) +- +- #if defined(__GNUC__) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned int lo, hi; +- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +- return (sqlite_uint64)hi << 32 | lo; +- } +- +- #elif defined(_MSC_VER) +- +- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ +- __asm { +- rdtsc +- ret ; return value at EDX:EAX +- } +- } +- +- #endif +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long val; +- __asm__ __volatile__ ("rdtsc" : "=A" (val)); +- return val; +- } +- +-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) +- +- __inline__ sqlite_uint64 sqlite3Hwtime(void){ +- unsigned long long retval; +- unsigned long junk; +- __asm__ __volatile__ ("\n\ +- 1: mftbu %1\n\ +- mftb %L0\n\ +- mftbu %0\n\ +- cmpw %0,%1\n\ +- bne 1b" +- : "=r" (retval), "=r" (junk)); +- return retval; +- } +- +-#else +- +- /* +- ** asm() is needed for hardware timing support. Without asm(), +- ** disable the sqlite3Hwtime() routine. +- ** +- ** sqlite3Hwtime() is only used for some obscure debugging +- ** and analysis configurations, not in any deliverable, so this +- ** should not be a great loss. +- */ +-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } +- +-#endif +- +-#endif /* !defined(SQLITE_HWTIME_H) */ +- +-/************** End of hwtime.h **********************************************/ +-/************** Continuing where we left off in vdbe.c ***********************/ +- +-#endif +- + #ifndef NDEBUG + /* + ** This function is only called from within an assert() expression. It +@@ -85977,6 +95021,131 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ + } + } + ++/* ++** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning ++** with pOp->p3. Return the hash. ++*/ ++static u64 filterHash(const Mem *aMem, const Op *pOp){ ++ int i, mx; ++ u64 h = 0; ++ ++ assert( pOp->p4type==P4_INT32 ); ++ for(i=pOp->p3, mx=i+pOp->p4.i; iflags & (MEM_Int|MEM_IntReal) ){ ++ h += p->u.i; ++ }else if( p->flags & MEM_Real ){ ++ h += sqlite3VdbeIntValue(p); ++ }else if( p->flags & (MEM_Str|MEM_Blob) ){ ++ /* All strings have the same hash and all blobs have the same hash, ++ ** though, at least, those hashes are different from each other and ++ ** from NULL. */ ++ h += 4093 + (p->flags & (MEM_Str|MEM_Blob)); ++ } ++ } ++ return h; ++} ++ ++ ++/* ++** For OP_Column, factor out the case where content is loaded from ++** overflow pages, so that the code to implement this case is separate ++** the common case where all content fits on the page. Factoring out ++** the code reduces register pressure and helps the common case ++** to run faster. ++*/ ++static SQLITE_NOINLINE int vdbeColumnFromOverflow( ++ VdbeCursor *pC, /* The BTree cursor from which we are reading */ ++ int iCol, /* The column to read */ ++ int t, /* The serial-type code for the column value */ ++ i64 iOffset, /* Offset to the start of the content value */ ++ u32 cacheStatus, /* Current Vdbe.cacheCtr value */ ++ u32 colCacheCtr, /* Current value of the column cache counter */ ++ Mem *pDest /* Store the value into this register. */ ++){ ++ int rc; ++ sqlite3 *db = pDest->db; ++ int encoding = pDest->enc; ++ int len = sqlite3VdbeSerialTypeLen(t); ++ assert( pC->eCurType==CURTYPE_BTREE ); ++ if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) return SQLITE_TOOBIG; ++ if( len > 4000 && pC->pKeyInfo==0 ){ ++ /* Cache large column values that are on overflow pages using ++ ** an RCStr (reference counted string) so that if they are reloaded, ++ ** that do not have to be copied a second time. The overhead of ++ ** creating and managing the cache is such that this is only ++ ** profitable for larger TEXT and BLOB values. ++ ** ++ ** Only do this on table-btrees so that writes to index-btrees do not ++ ** need to clear the cache. This buys performance in the common case ++ ** in exchange for generality. ++ */ ++ VdbeTxtBlbCache *pCache; ++ char *pBuf; ++ if( pC->colCache==0 ){ ++ pC->pCache = sqlite3DbMallocZero(db, sizeof(VdbeTxtBlbCache) ); ++ if( pC->pCache==0 ) return SQLITE_NOMEM; ++ pC->colCache = 1; ++ } ++ pCache = pC->pCache; ++ if( pCache->pCValue==0 ++ || pCache->iCol!=iCol ++ || pCache->cacheStatus!=cacheStatus ++ || pCache->colCacheCtr!=colCacheCtr ++ || pCache->iOffset!=sqlite3BtreeOffset(pC->uc.pCursor) ++ ){ ++ if( pCache->pCValue ) sqlite3RCStrUnref(pCache->pCValue); ++ pBuf = pCache->pCValue = sqlite3RCStrNew( len+3 ); ++ if( pBuf==0 ) return SQLITE_NOMEM; ++ rc = sqlite3BtreePayload(pC->uc.pCursor, iOffset, len, pBuf); ++ if( rc ) return rc; ++ pBuf[len] = 0; ++ pBuf[len+1] = 0; ++ pBuf[len+2] = 0; ++ pCache->iCol = iCol; ++ pCache->cacheStatus = cacheStatus; ++ pCache->colCacheCtr = colCacheCtr; ++ pCache->iOffset = sqlite3BtreeOffset(pC->uc.pCursor); ++ }else{ ++ pBuf = pCache->pCValue; ++ } ++ assert( t>=12 ); ++ sqlite3RCStrRef(pBuf); ++ if( t&1 ){ ++ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding, ++ sqlite3RCStrUnref); ++ pDest->flags |= MEM_Term; ++ }else{ ++ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0, ++ sqlite3RCStrUnref); ++ } ++ }else{ ++ rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest); ++ if( rc ) return rc; ++ sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); ++ if( (t&1)!=0 && encoding==SQLITE_UTF8 ){ ++ pDest->z[len] = 0; ++ pDest->flags |= MEM_Term; ++ } ++ } ++ pDest->flags &= ~MEM_Ephem; ++ return rc; ++} ++ ++ ++/* ++** Return the symbolic name for the data type of a pMem ++*/ ++static const char *vdbeMemTypeName(Mem *pMem){ ++ static const char *azTypes[] = { ++ /* SQLITE_INTEGER */ "INT", ++ /* SQLITE_FLOAT */ "REAL", ++ /* SQLITE_TEXT */ "TEXT", ++ /* SQLITE_BLOB */ "BLOB", ++ /* SQLITE_NULL */ "NULL" ++ }; ++ return azTypes[sqlite3_value_type(pMem)-1]; ++} + + /* + ** Execute as much of a VDBE program as we can. +@@ -85987,11 +95156,10 @@ SQLITE_PRIVATE int sqlite3VdbeExec( + ){ + Op *aOp = p->aOp; /* Copy of p->aOp */ + Op *pOp = aOp; /* Current operation */ +-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) +- Op *pOrigOp; /* Value of pOp at the top of the loop */ +-#endif + #ifdef SQLITE_DEBUG ++ Op *pOrigOp; /* Value of pOp at the top of the loop */ + int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ ++ u8 iCompareIsInit = 0; /* iCompare is initialized */ + #endif + int rc = SQLITE_OK; /* Value to return */ + sqlite3 *db = p->db; /* The database */ +@@ -86007,13 +95175,17 @@ SQLITE_PRIVATE int sqlite3VdbeExec( + Mem *pIn2 = 0; /* 2nd input operand */ + Mem *pIn3 = 0; /* 3rd input operand */ + Mem *pOut = 0; /* Output operand */ +-#ifdef VDBE_PROFILE +- u64 start; /* CPU clock count at start of opcode */ ++ u32 colCacheCtr = 0; /* Column cache counter */ ++#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) ++ u64 *pnCycle = 0; ++ int bStmtScanStatus = IS_STMT_SCANSTATUS(db)!=0; + #endif + /*** INSERT STACK UNION HERE ***/ + +- assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ +- sqlite3VdbeEnter(p); ++ assert( p->eVdbeState==VDBE_RUN_STATE ); /* sqlite3_step() verifies this */ ++ if( DbMaskNonZero(p->lockMask) ){ ++ sqlite3VdbeEnter(p); ++ } + #ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( db->xProgress ){ + u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; +@@ -86034,7 +95206,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec( + assert( p->bIsReader || p->readOnly!=0 ); + p->iCurrentTime = 0; + assert( p->explain==0 ); +- p->pResultSet = 0; + db->busyHandler.nBusy = 0; + if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; + sqlite3VdbeIOTraceSql(p); +@@ -86071,12 +95242,18 @@ SQLITE_PRIVATE int sqlite3VdbeExec( + assert( rc==SQLITE_OK ); + + assert( pOp>=aOp && pOp<&aOp[p->nOp]); +-#ifdef VDBE_PROFILE +- start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); +-#endif + nVmStep++; +-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; ++ ++#if defined(VDBE_PROFILE) ++ pOp->nExec++; ++ pnCycle = &pOp->nCycle; ++ if( sqlite3NProfileCnt==0 ) *pnCycle -= sqlite3Hwtime(); ++#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) ++ if( bStmtScanStatus ){ ++ pOp->nExec++; ++ pnCycle = &pOp->nCycle; ++ *pnCycle -= sqlite3Hwtime(); ++ } + #endif + + /* Only allow tracing if SQLITE_DEBUG is defined. +@@ -86138,7 +95315,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( + } + } + #endif +-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) ++#ifdef SQLITE_DEBUG + pOrigOp = pOp; + #endif + +@@ -86194,8 +95371,8 @@ SQLITE_PRIVATE int sqlite3VdbeExec( + case OP_Goto: { /* jump */ + + #ifdef SQLITE_DEBUG +- /* In debuggging mode, when the p5 flags is set on an OP_Goto, that +- ** means we should really jump back to the preceeding OP_ReleaseReg ++ /* In debugging mode, when the p5 flags is set on an OP_Goto, that ++ ** means we should really jump back to the preceding OP_ReleaseReg + ** instruction. */ + if( pOp->p5 ){ + assert( pOp->p2 < (int)(pOp - aOp) ); +@@ -86255,24 +95432,39 @@ case OP_Gosub: { /* jump */ + pIn1->flags = MEM_Int; + pIn1->u.i = (int)(pOp-aOp); + REGISTER_TRACE(pOp->p1, pIn1); +- +- /* Most jump operations do a goto to this spot in order to update +- ** the pOp pointer. */ +-jump_to_p2: +- pOp = &aOp[pOp->p2 - 1]; +- break; ++ goto jump_to_p2_and_check_for_interrupt; + } + +-/* Opcode: Return P1 * * * * ++/* Opcode: Return P1 P2 P3 * * ++** ++** Jump to the address stored in register P1. If P1 is a return address ++** register, then this accomplishes a return from a subroutine. ++** ++** If P3 is 1, then the jump is only taken if register P1 holds an integer ++** values, otherwise execution falls through to the next opcode, and the ++** OP_Return becomes a no-op. If P3 is 0, then register P1 must hold an ++** integer or else an assert() is raised. P3 should be set to 1 when ++** this opcode is used in combination with OP_BeginSubrtn, and set to 0 ++** otherwise. ++** ++** The value in register P1 is unchanged by this opcode. + ** +-** Jump to the next instruction after the address in register P1. After +-** the jump, register P1 becomes undefined. ++** P2 is not used by the byte-code engine. However, if P2 is positive ++** and also less than the current address, then the "EXPLAIN" output ++** formatter in the CLI will indent all opcodes from the P2 opcode up ++** to be not including the current Return. P2 should be the first opcode ++** in the subroutine from which this opcode is returning. Thus the P2 ++** value is a byte-code indentation hint. See tag-20220407a in ++** wherecode.c and shell.c. + */ + case OP_Return: { /* in1 */ + pIn1 = &aMem[pOp->p1]; +- assert( pIn1->flags==MEM_Int ); +- pOp = &aOp[pIn1->u.i]; +- pIn1->flags = MEM_Undefined; ++ if( pIn1->flags & MEM_Int ){ ++ if( pOp->p3 ){ VdbeBranchTaken(1, 2); } ++ pOp = &aOp[pIn1->u.i]; ++ }else if( ALWAYS(pOp->p3) ){ ++ VdbeBranchTaken(0, 2); ++ } + break; + } + +@@ -86287,7 +95479,7 @@ case OP_Return: { /* in1 */ + ** + ** See also: EndCoroutine + */ +-case OP_InitCoroutine: { /* jump */ ++case OP_InitCoroutine: { /* jump0 */ + assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); + assert( pOp->p2>=0 && pOp->p2nOp ); + assert( pOp->p3>=0 && pOp->p3nOp ); +@@ -86295,7 +95487,14 @@ case OP_InitCoroutine: { /* jump */ + assert( !VdbeMemDynamic(pOut) ); + pOut->u.i = pOp->p3 - 1; + pOut->flags = MEM_Int; +- if( pOp->p2 ) goto jump_to_p2; ++ if( pOp->p2==0 ) break; ++ ++ /* Most jump operations do a goto to this spot in order to update ++ ** the pOp pointer. */ ++jump_to_p2: ++ assert( pOp->p2>0 ); /* There are never any jumps to instruction 0 */ ++ assert( pOp->p2nOp ); /* Jumps must be in range */ ++ pOp = &aOp[pOp->p2 - 1]; + break; + } + +@@ -86303,7 +95502,9 @@ case OP_InitCoroutine: { /* jump */ + ** + ** The instruction at the address in register P1 is a Yield. + ** Jump to the P2 parameter of that Yield. +-** After the jump, register P1 becomes undefined. ++** After the jump, the value register P1 is left with a value ++** such that subsequent OP_Yields go back to the this same ++** OP_EndCoroutine instruction. + ** + ** See also: InitCoroutine + */ +@@ -86315,8 +95516,8 @@ case OP_EndCoroutine: { /* in1 */ + pCaller = &aOp[pIn1->u.i]; + assert( pCaller->opcode==OP_Yield ); + assert( pCaller->p2>=0 && pCaller->p2nOp ); ++ pIn1->u.i = (int)(pOp - p->aOp) - 1; + pOp = &aOp[pCaller->p2 - 1]; +- pIn1->flags = MEM_Undefined; + break; + } + +@@ -86333,7 +95534,7 @@ case OP_EndCoroutine: { /* in1 */ + ** + ** See also: InitCoroutine + */ +-case OP_Yield: { /* in1, jump */ ++case OP_Yield: { /* in1, jump0 */ + int pcDest; + pIn1 = &aMem[pOp->p1]; + assert( VdbeMemDynamic(pIn1)==0 ); +@@ -86363,7 +95564,7 @@ case OP_HaltIfNull: { /* in3 */ + /* no break */ deliberate_fall_through + } + +-/* Opcode: Halt P1 P2 * P4 P5 ++/* Opcode: Halt P1 P2 P3 P4 P5 + ** + ** Exit immediately. All open cursors, etc are closed + ** automatically. +@@ -86376,18 +95577,22 @@ case OP_HaltIfNull: { /* in3 */ + ** then back out all changes that have occurred during this execution of the + ** VDBE, but do not rollback the transaction. + ** +-** If P4 is not null then it is an error message string. ++** If P3 is not zero and P4 is NULL, then P3 is a register that holds the ++** text of an error message. + ** +-** P5 is a value between 0 and 4, inclusive, that modifies the P4 string. ++** If P3 is zero and P4 is not null then the error message string is held ++** in P4. + ** +-** 0: (no change) +-** 1: NOT NULL contraint failed: P4 ++** P5 is a value between 1 and 4, inclusive, then the P4 error message ++** string is modified as follows: ++** ++** 1: NOT NULL constraint failed: P4 + ** 2: UNIQUE constraint failed: P4 + ** 3: CHECK constraint failed: P4 + ** 4: FOREIGN KEY constraint failed: P4 + ** +-** If P5 is not zero and P4 is NULL, then everything after the ":" is +-** omitted. ++** If P3 is zero and P5 is not zero and P4 is NULL, then everything after ++** the ":" is omitted. + ** + ** There is an implied "Halt 0 0 0" instruction inserted at the very end of + ** every program. So a jump past the last instruction of the program +@@ -86397,11 +95602,19 @@ case OP_Halt: { + VdbeFrame *pFrame; + int pcx; + +- pcx = (int)(pOp - aOp); + #ifdef SQLITE_DEBUG + if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } + #endif +- if( pOp->p1==SQLITE_OK && p->pFrame ){ ++ assert( pOp->p4type==P4_NOTUSED ++ || pOp->p4type==P4_STATIC ++ || pOp->p4type==P4_DYNAMIC ); ++ ++ /* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates ++ ** something is wrong with the code generator. Raise an assertion in order ++ ** to bring this to the attention of fuzzers and other testing tools. */ ++ assert( pOp->p1!=SQLITE_INTERNAL ); ++ ++ if( p->pFrame && pOp->p1==SQLITE_OK ){ + /* Halt the sub-program. Return control to the parent frame. */ + pFrame = p->pFrame; + p->pFrame = pFrame->pParent; +@@ -86423,10 +95636,14 @@ case OP_Halt: { + } + p->rc = pOp->p1; + p->errorAction = (u8)pOp->p2; +- p->pc = pcx; + assert( pOp->p5<=4 ); + if( p->rc ){ +- if( pOp->p5 ){ ++ if( pOp->p3>0 && pOp->p4type==P4_NOTUSED ){ ++ const char *zErr; ++ assert( pOp->p3<=(p->nMem + 1 - p->nCursor) ); ++ zErr = sqlite3ValueText(&aMem[pOp->p3], SQLITE_UTF8); ++ sqlite3VdbeError(p, "%s", zErr); ++ }else if( pOp->p5 ){ + static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", + "FOREIGN KEY" }; + testcase( pOp->p5==1 ); +@@ -86440,7 +95657,8 @@ case OP_Halt: { + }else{ + sqlite3VdbeError(p, "%s", pOp->p4.z); + } +- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg); ++ pcx = (int)(pOp - aOp); ++ sqlite3_log(pOp->p1, "abort at %d: %s; [%s]", pcx, p->zErrMsg, p->zSql); + } + rc = sqlite3VdbeHalt(p); + assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); +@@ -86565,6 +95783,28 @@ case OP_String: { /* out2 */ + break; + } + ++/* Opcode: BeginSubrtn * P2 * * * ++** Synopsis: r[P2]=NULL ++** ++** Mark the beginning of a subroutine that can be entered in-line ++** or that can be called using OP_Gosub. The subroutine should ++** be terminated by an OP_Return instruction that has a P1 operand that ++** is the same as the P2 operand to this opcode and that has P3 set to 1. ++** If the subroutine is entered in-line, then the OP_Return will simply ++** fall through. But if the subroutine is entered using OP_Gosub, then ++** the OP_Return will jump back to the first instruction after the OP_Gosub. ++** ++** This routine works by loading a NULL into the P2 register. When the ++** return address register contains a NULL, the OP_Return instruction is ++** a no-op that simply falls through to the next instruction (assuming that ++** the OP_Return opcode has a P3 value of 1). Thus if the subroutine is ++** entered in-line, then the OP_Return will cause in-line execution to ++** continue. But if the subroutine is entered via OP_Gosub, then the ++** OP_Return will cause a return to the address following the OP_Gosub. ++** ++** This opcode is identical to OP_Null. It has a different name ++** only to make the byte code easier to read and verify. ++*/ + /* Opcode: Null P1 P2 P3 * * + ** Synopsis: r[P2..P3]=NULL + ** +@@ -86577,6 +95817,7 @@ case OP_String: { /* out2 */ + ** NULL values will not compare equal even if SQLITE_NULLEQ is set on + ** OP_Ne or OP_Eq. + */ ++case OP_BeginSubrtn: + case OP_Null: { /* out2 */ + int cnt; + u16 nullFlag; +@@ -86618,30 +95859,32 @@ case OP_SoftNull: { + ** Synopsis: r[P2]=P4 (len=P1) + ** + ** P4 points to a blob of data P1 bytes long. Store this +-** blob in register P2. ++** blob in register P2. If P4 is a NULL pointer, then construct ++** a zero-filled blob that is P1 bytes long in P2. + */ + case OP_Blob: { /* out2 */ + assert( pOp->p1 <= SQLITE_MAX_LENGTH ); + pOut = out2Prerelease(p, pOp); +- sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); ++ if( pOp->p4.z==0 ){ ++ sqlite3VdbeMemSetZeroBlob(pOut, pOp->p1); ++ if( sqlite3VdbeMemExpandBlob(pOut) ) goto no_mem; ++ }else{ ++ sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); ++ } + pOut->enc = encoding; + UPDATE_MAX_BLOBSIZE(pOut); + break; + } + +-/* Opcode: Variable P1 P2 * P4 * +-** Synopsis: r[P2]=parameter(P1,P4) ++/* Opcode: Variable P1 P2 * * * ++** Synopsis: r[P2]=parameter(P1) + ** + ** Transfer the values of bound parameter P1 into register P2 +-** +-** If the parameter is named, then its name appears in P4. +-** The P4 value is used by sqlite3_bind_parameter_name(). + */ + case OP_Variable: { /* out2 */ + Mem *pVar; /* Value being transferred */ + + assert( pOp->p1>0 && pOp->p1<=p->nVar ); +- assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) ); + pVar = &p->aVar[pOp->p1 - 1]; + if( sqlite3VdbeMemTooBig(pVar) ){ + goto too_big; +@@ -86688,6 +95931,7 @@ case OP_Move: { + { int i; + for(i=1; inMem; i++){ + if( aMem[i].pScopyFrom==pIn1 ){ ++ assert( aMem[i].bScopy ); + aMem[i].pScopyFrom = pOut; + } + } +@@ -86701,11 +95945,16 @@ case OP_Move: { + break; + } + +-/* Opcode: Copy P1 P2 P3 * * ++/* Opcode: Copy P1 P2 P3 * P5 + ** Synopsis: r[P2@P3+1]=r[P1@P3+1] + ** + ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. + ** ++** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the ++** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot ++** be merged. The 0x0001 bit is used by the query planner and does not ++** come into play during query execution. ++** + ** This instruction makes a deep copy of the value. A duplicate + ** is made of any string or blob constant. See also OP_SCopy. + */ +@@ -86720,6 +95969,9 @@ case OP_Copy: { + memAboutToChange(p, pOut); + sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); + Deephemeralize(pOut); ++ if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){ ++ pOut->flags &= ~MEM_Subtype; ++ } + #ifdef SQLITE_DEBUG + pOut->pScopyFrom = 0; + #endif +@@ -86752,6 +96004,7 @@ case OP_SCopy: { /* out2 */ + #ifdef SQLITE_DEBUG + pOut->pScopyFrom = pIn1; + pOut->mScopyFlags = pIn1->flags; ++ pIn1->bScopy = 1; + #endif + break; + } +@@ -86772,6 +96025,24 @@ case OP_IntCopy: { /* out2 */ + break; + } + ++/* Opcode: FkCheck * * * * * ++** ++** Halt with an SQLITE_CONSTRAINT error if there are any unresolved ++** foreign key constraint violations. If there are no foreign key ++** constraint violations, this is a no-op. ++** ++** FK constraint violations are also checked when the prepared statement ++** exits. This opcode is used to raise foreign key constraint errors prior ++** to returning results such as a row change count or the result of a ++** RETURNING clause. ++*/ ++case OP_FkCheck: { ++ if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){ ++ goto abort_due_to_error; ++ } ++ break; ++} ++ + /* Opcode: ResultRow P1 P2 * * * + ** Synopsis: output=r[P1@P2] + ** +@@ -86782,73 +96053,32 @@ case OP_IntCopy: { /* out2 */ + ** the result row. + */ + case OP_ResultRow: { +- Mem *pMem; +- int i; + assert( p->nResColumn==pOp->p2 ); +- assert( pOp->p1>0 ); ++ assert( pOp->p1>0 || CORRUPT_DB ); + assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); + +- /* If this statement has violated immediate foreign key constraints, do +- ** not return the number of rows modified. And do not RELEASE the statement +- ** transaction. It needs to be rolled back. */ +- if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){ +- assert( db->flags&SQLITE_CountRows ); +- assert( p->usesStmtJournal ); +- goto abort_due_to_error; +- } +- +- /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then +- ** DML statements invoke this opcode to return the number of rows +- ** modified to the user. This is the only way that a VM that +- ** opens a statement transaction may invoke this opcode. +- ** +- ** In case this is such a statement, close any statement transaction +- ** opened by this VM before returning control to the user. This is to +- ** ensure that statement-transactions are always nested, not overlapping. +- ** If the open statement-transaction is not closed here, then the user +- ** may step another VM that opens its own statement transaction. This +- ** may lead to overlapping statement transactions. +- ** +- ** The statement transaction is never a top-level transaction. Hence +- ** the RELEASE call below can never fail. +- */ +- assert( p->iStatement==0 || db->flags&SQLITE_CountRows ); +- rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE); +- assert( rc==SQLITE_OK ); +- +- /* Invalidate all ephemeral cursor row caches */ + p->cacheCtr = (p->cacheCtr + 2)|1; +- +- /* Make sure the results of the current row are \000 terminated +- ** and have an assigned type. The results are de-ephemeralized as +- ** a side effect. +- */ +- pMem = p->pResultSet = &aMem[pOp->p1]; +- for(i=0; ip2; i++){ +- assert( memIsValid(&pMem[i]) ); +- Deephemeralize(&pMem[i]); +- assert( (pMem[i].flags & MEM_Ephem)==0 +- || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); +- sqlite3VdbeMemNulTerminate(&pMem[i]); +- REGISTER_TRACE(pOp->p1+i, &pMem[i]); ++ p->pResultRow = &aMem[pOp->p1]; + #ifdef SQLITE_DEBUG +- /* The registers in the result will not be used again when the +- ** prepared statement restarts. This is because sqlite3_column() +- ** APIs might have caused type conversions of made other changes to +- ** the register values. Therefore, we can go ahead and break any +- ** OP_SCopy dependencies. */ +- pMem[i].pScopyFrom = 0; +-#endif ++ { ++ Mem *pMem = p->pResultRow; ++ int i; ++ for(i=0; ip2; i++){ ++ assert( memIsValid(&pMem[i]) ); ++ REGISTER_TRACE(pOp->p1+i, &pMem[i]); ++ /* The registers in the result will not be used again when the ++ ** prepared statement restarts. This is because sqlite3_column() ++ ** APIs might have caused type conversions of made other changes to ++ ** the register values. Therefore, we can go ahead and break any ++ ** OP_SCopy dependencies. */ ++ pMem[i].pScopyFrom = 0; ++ } + } ++#endif + if( db->mallocFailed ) goto no_mem; +- + if( db->mTrace & SQLITE_TRACE_ROW ){ + db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); + } +- +- +- /* Return SQLITE_ROW +- */ + p->pc = (int)(pOp - aOp) + 1; + rc = SQLITE_ROW; + goto vdbe_return; +@@ -86903,7 +96133,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ + if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; + } +- if( sqlite3VdbeMemGrow(pOut, (int)nByte+3, pOut==pIn2) ){ ++ if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){ + goto no_mem; + } + MemSetTypeFlag(pOut, MEM_Str); +@@ -86915,9 +96145,9 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ + memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); + assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); + pIn1->flags = flags1; ++ if( encoding>SQLITE_UTF8 ) nByte &= ~1; + pOut->z[nByte]=0; + pOut->z[nByte+1] = 0; +- pOut->z[nByte+2] = 0; + pOut->flags |= MEM_Term; + pOut->n = (int)nByte; + pOut->enc = encoding; +@@ -86968,7 +96198,6 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ + case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ + case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ + case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ +- u16 flags; /* Combined MEM_* flags from both inputs */ + u16 type1; /* Numeric type of left operand */ + u16 type2; /* Numeric type of right operand */ + i64 iA; /* Integer value of left operand */ +@@ -86977,12 +96206,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ + double rB; /* Real value of right operand */ + + pIn1 = &aMem[pOp->p1]; +- type1 = numericType(pIn1); ++ type1 = pIn1->flags; + pIn2 = &aMem[pOp->p2]; +- type2 = numericType(pIn2); ++ type2 = pIn2->flags; + pOut = &aMem[pOp->p3]; +- flags = pIn1->flags | pIn2->flags; + if( (type1 & type2 & MEM_Int)!=0 ){ ++int_math: + iA = pIn1->u.i; + iB = pIn2->u.i; + switch( pOp->opcode ){ +@@ -87004,9 +96233,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ + } + pOut->u.i = iB; + MemSetTypeFlag(pOut, MEM_Int); +- }else if( (flags & MEM_Null)!=0 ){ ++ }else if( ((type1 | type2) & MEM_Null)!=0 ){ + goto arithmetic_result_is_null; + }else{ ++ type1 = numericType(pIn1); ++ type2 = numericType(pIn2); ++ if( (type1 & type2 & MEM_Int)!=0 ) goto int_math; + fp_math: + rA = sqlite3VdbeRealValue(pIn1); + rB = sqlite3VdbeRealValue(pIn2); +@@ -87164,7 +96396,7 @@ case OP_AddImm: { /* in1 */ + pIn1 = &aMem[pOp->p1]; + memAboutToChange(p, pIn1); + sqlite3VdbeMemIntegerify(pIn1); +- pIn1->u.i += pOp->p2; ++ *(u64*)&pIn1->u.i += (u64)pOp->p2; + break; + } + +@@ -87175,7 +96407,7 @@ case OP_AddImm: { /* in1 */ + ** without data loss, then jump immediately to P2, or if P2==0 + ** raise an SQLITE_MISMATCH exception. + */ +-case OP_MustBeInt: { /* jump, in1 */ ++case OP_MustBeInt: { /* jump0, in1 */ + pIn1 = &aMem[pOp->p1]; + if( (pIn1->flags & MEM_Int)==0 ){ + applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); +@@ -87216,7 +96448,7 @@ case OP_RealAffinity: { /* in1 */ + } + #endif + +-#ifndef SQLITE_OMIT_CAST ++#if !defined(SQLITE_OMIT_CAST) || !defined(SQLITE_OMIT_ANALYZE) + /* Opcode: Cast P1 P2 * * * + ** Synopsis: affinity(r[P1]) + ** +@@ -87255,8 +96487,7 @@ case OP_Cast: { /* in1 */ + ** Synopsis: IF r[P3]==r[P1] + ** + ** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then +-** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then +-** store the result of comparison in register P2. ++** jump to address P2. + ** + ** The SQLITE_AFF_MASK portion of P5 must be an affinity character - + ** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made +@@ -87282,9 +96513,8 @@ case OP_Cast: { /* in1 */ + ** If neither operand is NULL the result is the same as it would be if + ** the SQLITE_NULLEQ flag were omitted from P5. + ** +-** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the +-** content of r[P2] is only changed if the new value is NULL or 0 (false). +-** In other words, a prior r[P2] value will not be overwritten by 1 (true). ++** This opcode saves the result of comparison for use by the new ++** OP_Jump opcode. + */ + /* Opcode: Ne P1 P2 P3 P4 P5 + ** Synopsis: IF r[P3]!=r[P1] +@@ -87292,17 +96522,12 @@ case OP_Cast: { /* in1 */ + ** This works just like the Eq opcode except that the jump is taken if + ** the operands in registers P1 and P3 are not equal. See the Eq opcode for + ** additional information. +-** +-** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the +-** content of r[P2] is only changed if the new value is NULL or 1 (true). +-** In other words, a prior r[P2] value will not be overwritten by 0 (false). + */ + /* Opcode: Lt P1 P2 P3 P4 P5 + ** Synopsis: IF r[P3]p3]; + flags1 = pIn1->flags; + flags3 = pIn3->flags; ++ if( (flags1 & flags3 & MEM_Int)!=0 ){ ++ /* Common case of comparison of two integers */ ++ if( pIn3->u.i > pIn1->u.i ){ ++ if( sqlite3aGTb[pOp->opcode] ){ ++ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); ++ goto jump_to_p2; ++ } ++ iCompare = +1; ++ VVA_ONLY( iCompareIsInit = 1; ) ++ }else if( pIn3->u.i < pIn1->u.i ){ ++ if( sqlite3aLTb[pOp->opcode] ){ ++ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); ++ goto jump_to_p2; ++ } ++ iCompare = -1; ++ VVA_ONLY( iCompareIsInit = 1; ) ++ }else{ ++ if( sqlite3aEQb[pOp->opcode] ){ ++ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); ++ goto jump_to_p2; ++ } ++ iCompare = 0; ++ VVA_ONLY( iCompareIsInit = 1; ) ++ } ++ VdbeBranchTaken(0, (pOp->p5 & SQLITE_NULLEQ)?2:3); ++ break; ++ } + if( (flags1 | flags3)&MEM_Null ){ + /* One or both operands are NULL */ + if( pOp->p5 & SQLITE_NULLEQ ){ +@@ -87384,44 +96639,33 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ + ** then the result is always NULL. + ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. + */ +- if( pOp->p5 & SQLITE_STOREP2 ){ +- pOut = &aMem[pOp->p2]; +- iCompare = 1; /* Operands are not equal */ +- memAboutToChange(p, pOut); +- MemSetTypeFlag(pOut, MEM_Null); +- REGISTER_TRACE(pOp->p2, pOut); +- }else{ +- VdbeBranchTaken(2,3); +- if( pOp->p5 & SQLITE_JUMPIFNULL ){ +- goto jump_to_p2; +- } ++ VdbeBranchTaken(2,3); ++ if( pOp->p5 & SQLITE_JUMPIFNULL ){ ++ goto jump_to_p2; + } ++ iCompare = 1; /* Operands are not equal */ ++ VVA_ONLY( iCompareIsInit = 1; ) + break; + } + }else{ +- /* Neither operand is NULL. Do a comparison. */ ++ /* Neither operand is NULL and we couldn't do the special high-speed ++ ** integer comparison case. So do a general-case comparison. */ + affinity = pOp->p5 & SQLITE_AFF_MASK; + if( affinity>=SQLITE_AFF_NUMERIC ){ + if( (flags1 | flags3)&MEM_Str ){ + if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ + applyNumericAffinity(pIn1,0); +- testcase( flags3==pIn3->flags ); ++ assert( flags3==pIn3->flags || CORRUPT_DB ); + flags3 = pIn3->flags; + } + if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ + applyNumericAffinity(pIn3,0); + } + } +- /* Handle the common case of integer comparison here, as an +- ** optimization, to avoid a call to sqlite3MemCompare() */ +- if( (pIn1->flags & pIn3->flags & MEM_Int)!=0 ){ +- if( pIn3->u.i > pIn1->u.i ){ res = +1; goto compare_op; } +- if( pIn3->u.i < pIn1->u.i ){ res = -1; goto compare_op; } +- res = 0; +- goto compare_op; +- } +- }else if( affinity==SQLITE_AFF_TEXT ){ +- if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ ++ }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){ ++ if( (flags1 & MEM_Str)!=0 ){ ++ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); ++ }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ + testcase( pIn1->flags & MEM_Int ); + testcase( pIn1->flags & MEM_Real ); + testcase( pIn1->flags & MEM_IntReal ); +@@ -87430,7 +96674,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ + flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); + if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str; + } +- if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ ++ if( (flags3 & MEM_Str)!=0 ){ ++ pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); ++ }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ + testcase( pIn3->flags & MEM_Int ); + testcase( pIn3->flags & MEM_Real ); + testcase( pIn3->flags & MEM_IntReal ); +@@ -87442,7 +96688,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ + assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); + res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + } +-compare_op: ++ + /* At this point, res is negative, zero, or positive if reg[P1] is + ** less than, equal to, or greater than reg[P3], respectively. Compute + ** the answer to this operator in res2, depending on what the comparison +@@ -87451,16 +96697,15 @@ compare_op: + ** order: NE, EQ, GT, LE, LT, GE */ + assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 ); + assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 ); +- if( res<0 ){ /* ne, eq, gt, le, lt, ge */ +- static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 }; +- res2 = aLTb[pOp->opcode - OP_Ne]; ++ if( res<0 ){ ++ res2 = sqlite3aLTb[pOp->opcode]; + }else if( res==0 ){ +- static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 }; +- res2 = aEQb[pOp->opcode - OP_Ne]; ++ res2 = sqlite3aEQb[pOp->opcode]; + }else{ +- static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 }; +- res2 = aGTb[pOp->opcode - OP_Ne]; ++ res2 = sqlite3aGTb[pOp->opcode]; + } ++ iCompare = res; ++ VVA_ONLY( iCompareIsInit = 1; ) + + /* Undo any changes made by applyAffinity() to the input registers. */ + assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); +@@ -87468,67 +96713,40 @@ compare_op: + assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); + pIn1->flags = flags1; + +- if( pOp->p5 & SQLITE_STOREP2 ){ +- pOut = &aMem[pOp->p2]; +- iCompare = res; +- if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){ +- /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 +- ** and prevents OP_Ne from overwriting NULL with 0. This flag +- ** is only used in contexts where either: +- ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0) +- ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1) +- ** Therefore it is not necessary to check the content of r[P2] for +- ** NULL. */ +- assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq ); +- assert( res2==0 || res2==1 ); +- testcase( res2==0 && pOp->opcode==OP_Eq ); +- testcase( res2==1 && pOp->opcode==OP_Eq ); +- testcase( res2==0 && pOp->opcode==OP_Ne ); +- testcase( res2==1 && pOp->opcode==OP_Ne ); +- if( (pOp->opcode==OP_Eq)==res2 ) break; +- } +- memAboutToChange(p, pOut); +- MemSetTypeFlag(pOut, MEM_Int); +- pOut->u.i = res2; +- REGISTER_TRACE(pOp->p2, pOut); +- }else{ +- VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); +- if( res2 ){ +- goto jump_to_p2; +- } ++ VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); ++ if( res2 ){ ++ goto jump_to_p2; + } + break; + } + +-/* Opcode: ElseNotEq * P2 * * * ++/* Opcode: ElseEq * P2 * * * + ** + ** This opcode must follow an OP_Lt or OP_Gt comparison operator. There + ** can be zero or more OP_ReleaseReg opcodes intervening, but no other + ** opcodes are allowed to occur between this instruction and the previous +-** OP_Lt or OP_Gt. Furthermore, the prior OP_Lt or OP_Gt must have the +-** SQLITE_STOREP2 bit set in the P5 field. ++** OP_Lt or OP_Gt. + ** +-** If result of an OP_Eq comparison on the same two operands as the +-** prior OP_Lt or OP_Gt would have been NULL or false (0), then then +-** jump to P2. If the result of an OP_Eq comparison on the two previous +-** operands would have been true (1), then fall through. ++** If the result of an OP_Eq comparison on the same two operands as ++** the prior OP_Lt or OP_Gt would have been true, then jump to P2. If ++** the result of an OP_Eq comparison on the two previous operands ++** would have been false or NULL, then fall through. + */ +-case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ ++case OP_ElseEq: { /* same as TK_ESCAPE, jump */ + + #ifdef SQLITE_DEBUG + /* Verify the preconditions of this opcode - that it follows an OP_Lt or +- ** OP_Gt with the SQLITE_STOREP2 flag set, with zero or more intervening +- ** OP_ReleaseReg opcodes */ ++ ** OP_Gt with zero or more intervening OP_ReleaseReg opcodes */ + int iAddr; + for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){ + if( aOp[iAddr].opcode==OP_ReleaseReg ) continue; + assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt ); +- assert( aOp[iAddr].p5 & SQLITE_STOREP2 ); + break; + } + #endif /* SQLITE_DEBUG */ +- VdbeBranchTaken(iCompare!=0, 2); +- if( iCompare!=0 ) goto jump_to_p2; ++ assert( iCompareIsInit ); ++ VdbeBranchTaken(iCompare==0, 2); ++ if( iCompare==0 ) goto jump_to_p2; + break; + } + +@@ -87538,9 +96756,8 @@ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ + ** Set the permutation used by the OP_Compare operator in the next + ** instruction. The permutation is stored in the P4 operand. + ** +-** The permutation is only valid until the next OP_Compare that has +-** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should +-** occur immediately prior to the OP_Compare. ++** The permutation is only valid for the next opcode which must be ++** an OP_Compare that has the OPFLAG_PERMUTE bit set in P5. + ** + ** The first integer in the P4 integer array is the length of the array + ** and does not become part of the permutation. +@@ -87572,6 +96789,8 @@ case OP_Permutation: { + ** The comparison is a sort comparison, so NULLs compare equal, + ** NULLs are less than numbers, numbers are less than strings, + ** and strings are less than blobs. ++** ++** This opcode must be immediately followed by an OP_Jump opcode. + */ + case OP_Compare: { + int n; +@@ -87620,6 +96839,7 @@ case OP_Compare: { + pColl = pKeyInfo->aColl[i]; + bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC); + iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); ++ VVA_ONLY( iCompareIsInit = 1; ) + if( iCompare ){ + if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) + && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null)) +@@ -87630,16 +96850,21 @@ case OP_Compare: { + break; + } + } ++ assert( pOp[1].opcode==OP_Jump ); + break; + } + + /* Opcode: Jump P1 P2 P3 * * + ** + ** Jump to the instruction at address P1, P2, or P3 depending on whether +-** in the most recent OP_Compare instruction the P1 vector was less than ++** in the most recent OP_Compare instruction the P1 vector was less than, + ** equal to, or greater than the P2 vector, respectively. ++** ++** This opcode must immediately follow an OP_Compare opcode. + */ + case OP_Jump: { /* jump */ ++ assert( pOp>aOp && pOp[-1].opcode==OP_Compare ); ++ assert( iCompareIsInit ); + if( iCompare<0 ){ + VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1]; + }else if( iCompare==0 ){ +@@ -87759,7 +96984,7 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ + break; + } + +-/* Opcode: Once P1 P2 * * * ++/* Opcode: Once P1 P2 P3 * * + ** + ** Fall through to the next instruction the first time this opcode is + ** encountered on each invocation of the byte-code program. Jump to P2 +@@ -87775,6 +97000,12 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ + ** whether or not the jump should be taken. The bitmask is necessary + ** because the self-altering code trick does not work for recursive + ** triggers. ++** ++** The P3 operand is not used directly by this opcode. However P3 is ++** used by the code generator as follows: If this opcode is the start ++** of a subroutine and that subroutine uses a Bloom filter, then P3 will ++** be the register that holds that Bloom filter. See tag-202407032019 ++** in the source code for implementation details. + */ + case OP_Once: { /* jump */ + u32 iAddr; /* Address of this instruction */ +@@ -87839,6 +97070,117 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ + break; + } + ++/* Opcode: IsType P1 P2 P3 P4 P5 ++** Synopsis: if typeof(P1.P3) in P5 goto P2 ++** ++** Jump to P2 if the type of a column in a btree is one of the types specified ++** by the P5 bitmask. ++** ++** P1 is normally a cursor on a btree for which the row decode cache is ++** valid through at least column P3. In other words, there should have been ++** a prior OP_Column for column P3 or greater. If the cursor is not valid, ++** then this opcode might give spurious results. ++** The the btree row has fewer than P3 columns, then use P4 as the ++** datatype. ++** ++** If P1 is -1, then P3 is a register number and the datatype is taken ++** from the value in that register. ++** ++** P5 is a bitmask of data types. SQLITE_INTEGER is the least significant ++** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04. ++** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10. ++** ++** WARNING: This opcode does not reliably distinguish between NULL and REAL ++** when P1>=0. If the database contains a NaN value, this opcode will think ++** that the datatype is REAL when it should be NULL. When P1<0 and the value ++** is already stored in register P3, then this opcode does reliably ++** distinguish between NULL and REAL. The problem only arises then P1>=0. ++** ++** Take the jump to address P2 if and only if the datatype of the ++** value determined by P1 and P3 corresponds to one of the bits in the ++** P5 bitmask. ++** ++*/ ++case OP_IsType: { /* jump */ ++ VdbeCursor *pC; ++ u16 typeMask; ++ u32 serialType; ++ ++ assert( pOp->p1>=(-1) && pOp->p1nCursor ); ++ assert( pOp->p1>=0 || (pOp->p3>=0 && pOp->p3<=(p->nMem+1 - p->nCursor)) ); ++ if( pOp->p1>=0 ){ ++ pC = p->apCsr[pOp->p1]; ++ assert( pC!=0 ); ++ assert( pOp->p3>=0 ); ++ if( pOp->p3nHdrParsed ){ ++ serialType = pC->aType[pOp->p3]; ++ if( serialType>=12 ){ ++ if( serialType&1 ){ ++ typeMask = 0x04; /* SQLITE_TEXT */ ++ }else{ ++ typeMask = 0x08; /* SQLITE_BLOB */ ++ } ++ }else{ ++ static const unsigned char aMask[] = { ++ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2, ++ 0x01, 0x01, 0x10, 0x10 ++ }; ++ testcase( serialType==0 ); ++ testcase( serialType==1 ); ++ testcase( serialType==2 ); ++ testcase( serialType==3 ); ++ testcase( serialType==4 ); ++ testcase( serialType==5 ); ++ testcase( serialType==6 ); ++ testcase( serialType==7 ); ++ testcase( serialType==8 ); ++ testcase( serialType==9 ); ++ testcase( serialType==10 ); ++ testcase( serialType==11 ); ++ typeMask = aMask[serialType]; ++ } ++ }else{ ++ typeMask = 1 << (pOp->p4.i - 1); ++ testcase( typeMask==0x01 ); ++ testcase( typeMask==0x02 ); ++ testcase( typeMask==0x04 ); ++ testcase( typeMask==0x08 ); ++ testcase( typeMask==0x10 ); ++ } ++ }else{ ++ assert( memIsValid(&aMem[pOp->p3]) ); ++ typeMask = 1 << (sqlite3_value_type((sqlite3_value*)&aMem[pOp->p3])-1); ++ testcase( typeMask==0x01 ); ++ testcase( typeMask==0x02 ); ++ testcase( typeMask==0x04 ); ++ testcase( typeMask==0x08 ); ++ testcase( typeMask==0x10 ); ++ } ++ VdbeBranchTaken( (typeMask & pOp->p5)!=0, 2); ++ if( typeMask & pOp->p5 ){ ++ goto jump_to_p2; ++ } ++ break; ++} ++ ++/* Opcode: ZeroOrNull P1 P2 P3 * * ++** Synopsis: r[P2] = 0 OR NULL ++** ++** If both registers P1 and P3 are NOT NULL, then store a zero in ++** register P2. If either registers P1 or P3 are NULL then put ++** a NULL in register P2. ++*/ ++case OP_ZeroOrNull: { /* in1, in2, out2, in3 */ ++ if( (aMem[pOp->p1].flags & MEM_Null)!=0 ++ || (aMem[pOp->p3].flags & MEM_Null)!=0 ++ ){ ++ sqlite3VdbeMemSetNull(aMem + pOp->p2); ++ }else{ ++ sqlite3VdbeMemSetInt64(aMem + pOp->p2, 0); ++ } ++ break; ++} ++ + /* Opcode: NotNull P1 P2 * * * + ** Synopsis: if r[P1]!=NULL goto P2 + ** +@@ -87860,11 +97202,14 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ + ** If it is, then set register P3 to NULL and jump immediately to P2. + ** If P1 is not on a NULL row, then fall through without making any + ** changes. ++** ++** If P1 is not an open cursor, then this opcode is a no-op. + */ + case OP_IfNullRow: { /* jump */ ++ VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); +- assert( p->apCsr[pOp->p1]!=0 ); +- if( p->apCsr[pOp->p1]->nullRow ){ ++ pC = p->apCsr[pOp->p1]; ++ if( pC && pC->nullRow ){ + sqlite3VdbeMemSetNull(aMem + pOp->p3); + goto jump_to_p2; + } +@@ -87892,22 +97237,30 @@ case OP_Offset: { /* out3 */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + pOut = &p->aMem[pOp->p3]; +- if( NEVER(pC==0) || pC->eCurType!=CURTYPE_BTREE ){ ++ if( pC==0 || pC->eCurType!=CURTYPE_BTREE ){ + sqlite3VdbeMemSetNull(pOut); + }else{ +- sqlite3VdbeMemSetInt64(pOut, sqlite3BtreeOffset(pC->uc.pCursor)); ++ if( pC->deferredMoveto ){ ++ rc = sqlite3VdbeFinishMoveto(pC); ++ if( rc ) goto abort_due_to_error; ++ } ++ if( sqlite3BtreeEof(pC->uc.pCursor) ){ ++ sqlite3VdbeMemSetNull(pOut); ++ }else{ ++ sqlite3VdbeMemSetInt64(pOut, sqlite3BtreeOffset(pC->uc.pCursor)); ++ } + } + break; + } + #endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ + + /* Opcode: Column P1 P2 P3 P4 P5 +-** Synopsis: r[P3]=PX ++** Synopsis: r[P3]=PX cursor P1 column P2 + ** + ** Interpret the data that cursor P1 points to as a structure built using + ** the MakeRecord instruction. (See the MakeRecord opcode for additional + ** information about the format of the data.) Extract the P2-th column +-** from this record. If there are less that (P2+1) ++** from this record. If there are less than (P2+1) + ** values in the record, extract a NULL. + ** + ** The value extracted is stored in register P3. +@@ -87916,15 +97269,17 @@ case OP_Offset: { /* out3 */ + ** if the P4 argument is a P4_MEM use the value of the P4 argument as + ** the result. + ** +-** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then +-** the result is guaranteed to only be used as the argument of a length() +-** or typeof() function, respectively. The loading of large blobs can be +-** skipped for length() and all content loading can be skipped for typeof(). ++** If the OPFLAG_LENGTHARG bit is set in P5 then the result is guaranteed ++** to only be used by the length() function or the equivalent. The content ++** of large blobs is not loaded, thus saving CPU cycles. If the ++** OPFLAG_TYPEOFARG bit is set then the result will only be used by the ++** typeof() function or the IS NULL or IS NOT NULL operators or the ++** equivalent. In this case, all content loading can be omitted. + */ +-case OP_Column: { ++case OP_Column: { /* ncycle */ + u32 p2; /* column number to retrieve */ + VdbeCursor *pC; /* The VDBE cursor */ +- BtCursor *pCrsr; /* The BTree cursor */ ++ BtCursor *pCrsr; /* The B-Tree cursor corresponding to pC */ + u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ + int len; /* The length of the serialized data for the column */ + int i; /* Loop counter */ +@@ -87938,43 +97293,53 @@ case OP_Column: { + Mem *pReg; /* PseudoTable input register */ + + assert( pOp->p1>=0 && pOp->p1nCursor ); ++ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + pC = p->apCsr[pOp->p1]; +- assert( pC!=0 ); + p2 = (u32)pOp->p2; + +- /* If the cursor cache is stale (meaning it is not currently point at +- ** the correct row) then bring it up-to-date by doing the necessary +- ** B-Tree seek. */ +- rc = sqlite3VdbeCursorMoveto(&pC, &p2); +- if( rc ) goto abort_due_to_error; +- +- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); +- pDest = &aMem[pOp->p3]; +- memAboutToChange(p, pDest); ++op_column_restart: + assert( pC!=0 ); +- assert( p2<(u32)pC->nField ); ++ assert( p2<(u32)pC->nField ++ || (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult==0) ); + aOffset = pC->aOffset; ++ assert( aOffset==pC->aType+pC->nField ); + assert( pC->eCurType!=CURTYPE_VTAB ); + assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); + assert( pC->eCurType!=CURTYPE_SORTER ); + + if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ + if( pC->nullRow ){ +- if( pC->eCurType==CURTYPE_PSEUDO ){ ++ if( pC->eCurType==CURTYPE_PSEUDO && pC->seekResult>0 ){ + /* For the special case of as pseudo-cursor, the seekResult field + ** identifies the register that holds the record */ +- assert( pC->seekResult>0 ); + pReg = &aMem[pC->seekResult]; + assert( pReg->flags & MEM_Blob ); + assert( memIsValid(pReg) ); + pC->payloadSize = pC->szRow = pReg->n; + pC->aRow = (u8*)pReg->z; + }else{ ++ pDest = &aMem[pOp->p3]; ++ memAboutToChange(p, pDest); + sqlite3VdbeMemSetNull(pDest); + goto op_column_out; + } + }else{ + pCrsr = pC->uc.pCursor; ++ if( pC->deferredMoveto ){ ++ u32 iMap; ++ assert( !pC->isEphemeral ); ++ if( pC->ub.aAltMap && (iMap = pC->ub.aAltMap[1+p2])>0 ){ ++ pC = pC->pAltCursor; ++ p2 = iMap - 1; ++ goto op_column_restart; ++ } ++ rc = sqlite3VdbeFinishMoveto(pC); ++ if( rc ) goto abort_due_to_error; ++ }else if( sqlite3BtreeCursorHasMoved(pCrsr) ){ ++ rc = sqlite3VdbeHandleMovedCursor(pC); ++ if( rc ) goto abort_due_to_error; ++ goto op_column_restart; ++ } + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pCrsr ); + assert( sqlite3BtreeCursorIsValid(pCrsr) ); +@@ -87982,15 +97347,15 @@ case OP_Column: { + pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &pC->szRow); + assert( pC->szRow<=pC->payloadSize ); + assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */ +- if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ +- goto too_big; +- } + } + pC->cacheStatus = p->cacheCtr; +- pC->iHdrOffset = getVarint32(pC->aRow, aOffset[0]); ++ if( (aOffset[0] = pC->aRow[0])<0x80 ){ ++ pC->iHdrOffset = 1; ++ }else{ ++ pC->iHdrOffset = sqlite3GetVarint32(pC->aRow, aOffset); ++ } + pC->nHdrParsed = 0; + +- + if( pC->szRowaRow does not have to hold the entire row, but it does at least + ** need to cover the header of the record. If pC->aRow does not contain +@@ -88030,6 +97395,10 @@ case OP_Column: { + testcase( aOffset[0]==0 ); + goto op_column_read_header; + } ++ }else if( sqlite3BtreeCursorHasMoved(pC->uc.pCursor) ){ ++ rc = sqlite3VdbeHandleMovedCursor(pC); ++ if( rc ) goto abort_due_to_error; ++ goto op_column_restart; + } + + /* Make sure at least the first p2+1 entries of the header have been +@@ -88098,6 +97467,8 @@ case OP_Column: { + ** columns. So the result will be either the default value or a NULL. + */ + if( pC->nHdrParsed<=p2 ){ ++ pDest = &aMem[pOp->p3]; ++ memAboutToChange(p, pDest); + if( pOp->p4type==P4_MEM ){ + sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); + }else{ +@@ -88115,6 +97486,8 @@ case OP_Column: { + */ + assert( p2nHdrParsed ); + assert( rc==SQLITE_OK ); ++ pDest = &aMem[pOp->p3]; ++ memAboutToChange(p, pDest); + assert( sqlite3VdbeCheckMemInvariants(pDest) ); + if( VdbeMemDynamic(pDest) ){ + sqlite3VdbeMemSetNull(pDest); +@@ -88135,6 +97508,7 @@ case OP_Column: { + pDest->n = len = (t-12)/2; + pDest->enc = encoding; + if( pDest->szMalloc < len+2 ){ ++ if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big; + pDest->flags = MEM_Null; + if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; + }else{ +@@ -88146,11 +97520,16 @@ case OP_Column: { + pDest->flags = aFlag[t&1]; + } + }else{ ++ u8 p5; + pDest->enc = encoding; ++ assert( pDest->db==db ); + /* This branch happens only when content is on overflow pages */ +- if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 +- && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) +- || (len = sqlite3VdbeSerialTypeLen(t))==0 ++ if( ((p5 = (pOp->p5 & OPFLAG_BYTELENARG))!=0 ++ && (p5==OPFLAG_TYPEOFARG ++ || (t>=12 && ((t&1)==0 || p5==OPFLAG_BYTELENARG)) ++ ) ++ ) ++ || sqlite3VdbeSerialTypeLen(t)==0 + ){ + /* Content is irrelevant for + ** 1. the typeof() function, +@@ -88167,10 +97546,13 @@ case OP_Column: { + */ + sqlite3VdbeSerialGet((u8*)sqlite3CtypeMap, t, pDest); + }else{ +- rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest); +- if( rc!=SQLITE_OK ) goto abort_due_to_error; +- sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); +- pDest->flags &= ~MEM_Ephem; ++ rc = vdbeColumnFromOverflow(pC, p2, t, aOffset[p2], ++ p->cacheCtr, colCacheCtr, pDest); ++ if( rc ){ ++ if( rc==SQLITE_NOMEM ) goto no_mem; ++ if( rc==SQLITE_TOOBIG ) goto too_big; ++ goto abort_due_to_error; ++ } + } + } + +@@ -88189,6 +97571,110 @@ op_column_corrupt: + } + } + ++/* Opcode: TypeCheck P1 P2 P3 P4 * ++** Synopsis: typecheck(r[P1@P2]) ++** ++** Apply affinities to the range of P2 registers beginning with P1. ++** Take the affinities from the Table object in P4. If any value ++** cannot be coerced into the correct type, then raise an error. ++** ++** This opcode is similar to OP_Affinity except that this opcode ++** forces the register type to the Table column type. This is used ++** to implement "strict affinity". ++** ++** GENERATED ALWAYS AS ... STATIC columns are only checked if P3 ++** is zero. When P3 is non-zero, no type checking occurs for ++** static generated columns. Virtual columns are computed at query time ++** and so they are never checked. ++** ++** Preconditions: ++** ++**
          ++**
        • P2 should be the number of non-virtual columns in the ++** table of P4. ++**
        • Table P4 should be a STRICT table. ++**
        ++** ++** If any precondition is false, an assertion fault occurs. ++*/ ++case OP_TypeCheck: { ++ Table *pTab; ++ Column *aCol; ++ int i; ++ ++ assert( pOp->p4type==P4_TABLE ); ++ pTab = pOp->p4.pTab; ++ assert( pTab->tabFlags & TF_Strict ); ++ assert( pTab->nNVCol==pOp->p2 ); ++ aCol = pTab->aCol; ++ pIn1 = &aMem[pOp->p1]; ++ for(i=0; inCol; i++){ ++ if( aCol[i].colFlags & COLFLAG_GENERATED ){ ++ if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue; ++ if( pOp->p3 ){ pIn1++; continue; } ++ } ++ assert( pIn1 < &aMem[pOp->p1+pOp->p2] ); ++ applyAffinity(pIn1, aCol[i].affinity, encoding); ++ if( (pIn1->flags & MEM_Null)==0 ){ ++ switch( aCol[i].eCType ){ ++ case COLTYPE_BLOB: { ++ if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error; ++ break; ++ } ++ case COLTYPE_INTEGER: ++ case COLTYPE_INT: { ++ if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error; ++ break; ++ } ++ case COLTYPE_TEXT: { ++ if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error; ++ break; ++ } ++ case COLTYPE_REAL: { ++ testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_Real ); ++ assert( (pIn1->flags & MEM_IntReal)==0 ); ++ if( pIn1->flags & MEM_Int ){ ++ /* When applying REAL affinity, if the result is still an MEM_Int ++ ** that will fit in 6 bytes, then change the type to MEM_IntReal ++ ** so that we keep the high-resolution integer value but know that ++ ** the type really wants to be REAL. */ ++ testcase( pIn1->u.i==140737488355328LL ); ++ testcase( pIn1->u.i==140737488355327LL ); ++ testcase( pIn1->u.i==-140737488355328LL ); ++ testcase( pIn1->u.i==-140737488355329LL ); ++ if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){ ++ pIn1->flags |= MEM_IntReal; ++ pIn1->flags &= ~MEM_Int; ++ }else{ ++ pIn1->u.r = (double)pIn1->u.i; ++ pIn1->flags |= MEM_Real; ++ pIn1->flags &= ~MEM_Int; ++ } ++ }else if( (pIn1->flags & (MEM_Real|MEM_IntReal))==0 ){ ++ goto vdbe_type_error; ++ } ++ break; ++ } ++ default: { ++ /* COLTYPE_ANY. Accept anything. */ ++ break; ++ } ++ } ++ } ++ REGISTER_TRACE((int)(pIn1-aMem), pIn1); ++ pIn1++; ++ } ++ assert( pIn1 == &aMem[pOp->p1+pOp->p2] ); ++ break; ++ ++vdbe_type_error: ++ sqlite3VdbeError(p, "cannot store %s value in %s column %s.%s", ++ vdbeMemTypeName(pIn1), sqlite3StdType[aCol[i].eCType-1], ++ pTab->zName, aCol[i].zCnName); ++ rc = SQLITE_CONSTRAINT_DATATYPE; ++ goto abort_due_to_error; ++} ++ + /* Opcode: Affinity P1 P2 * P4 * + ** Synopsis: affinity(r[P1@P2]) + ** +@@ -88225,7 +97711,7 @@ case OP_Affinity: { + }else{ + pIn1->u.r = (double)pIn1->u.i; + pIn1->flags |= MEM_Real; +- pIn1->flags &= ~MEM_Int; ++ pIn1->flags &= ~(MEM_Int|MEM_Str); + } + } + REGISTER_TRACE((int)(pIn1-aMem), pIn1); +@@ -88275,7 +97761,6 @@ case OP_MakeRecord: { + Mem *pLast; /* Last field of the record */ + int nField; /* Number of fields in the record */ + char *zAffinity; /* The affinity string for the record */ +- int file_format; /* File format to use for encoding */ + u32 len; /* Length of a field */ + u8 *zHdr; /* Where to write next byte of the header */ + u8 *zPayload; /* Where to write next byte of the payload */ +@@ -88304,7 +97789,6 @@ case OP_MakeRecord: { + pData0 = &aMem[nField]; + nField = pOp->p2; + pLast = &pData0[nField-1]; +- file_format = p->minWriteFileFormat; + + /* Identify the output register */ + assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); +@@ -88403,10 +97887,10 @@ case OP_MakeRecord: { + testcase( uu==127 ); testcase( uu==128 ); + testcase( uu==32767 ); testcase( uu==32768 ); + testcase( uu==8388607 ); testcase( uu==8388608 ); +- testcase( uu==2147483647 ); testcase( uu==2147483648 ); ++ testcase( uu==2147483647 ); testcase( uu==2147483648LL ); + testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL ); + if( uu<=127 ){ +- if( (i&1)==i && file_format>=4 ){ ++ if( (i&1)==i && p->minWriteFileFormat>=4 ){ + pRec->uTemp = 8+(u32)uu; + }else{ + nData++; +@@ -88511,18 +97995,70 @@ case OP_MakeRecord: { + zPayload = zHdr + nHdr; + + /* Write the record */ +- zHdr += putVarint32(zHdr, nHdr); ++ if( nHdr<0x80 ){ ++ *(zHdr++) = nHdr; ++ }else{ ++ zHdr += sqlite3PutVarint(zHdr,nHdr); ++ } + assert( pData0<=pLast ); + pRec = pData0; +- do{ ++ while( 1 /*exit-by-break*/ ){ + serial_type = pRec->uTemp; + /* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more +- ** additional varints, one per column. */ +- zHdr += putVarint32(zHdr, serial_type); /* serial type */ +- /* EVIDENCE-OF: R-64536-51728 The values for each column in the record ++ ** additional varints, one per column. ++ ** EVIDENCE-OF: R-64536-51728 The values for each column in the record + ** immediately follow the header. */ +- zPayload += sqlite3VdbeSerialPut(zPayload, pRec, serial_type); /* content */ +- }while( (++pRec)<=pLast ); ++ if( serial_type<=7 ){ ++ *(zHdr++) = serial_type; ++ if( serial_type==0 ){ ++ /* NULL value. No change in zPayload */ ++ }else{ ++ u64 v; ++ if( serial_type==7 ){ ++ assert( sizeof(v)==sizeof(pRec->u.r) ); ++ memcpy(&v, &pRec->u.r, sizeof(v)); ++ swapMixedEndianFloat(v); ++ }else{ ++ v = pRec->u.i; ++ } ++ len = sqlite3SmallTypeSizes[serial_type]; ++ assert( len>=1 && len<=8 && len!=5 && len!=7 ); ++ switch( len ){ ++ default: zPayload[7] = (u8)(v&0xff); v >>= 8; ++ zPayload[6] = (u8)(v&0xff); v >>= 8; ++ /* no break */ deliberate_fall_through ++ case 6: zPayload[5] = (u8)(v&0xff); v >>= 8; ++ zPayload[4] = (u8)(v&0xff); v >>= 8; ++ /* no break */ deliberate_fall_through ++ case 4: zPayload[3] = (u8)(v&0xff); v >>= 8; ++ /* no break */ deliberate_fall_through ++ case 3: zPayload[2] = (u8)(v&0xff); v >>= 8; ++ /* no break */ deliberate_fall_through ++ case 2: zPayload[1] = (u8)(v&0xff); v >>= 8; ++ /* no break */ deliberate_fall_through ++ case 1: zPayload[0] = (u8)(v&0xff); ++ } ++ zPayload += len; ++ } ++ }else if( serial_type<0x80 ){ ++ *(zHdr++) = serial_type; ++ if( serial_type>=14 && pRec->n>0 ){ ++ assert( pRec->z!=0 ); ++ memcpy(zPayload, pRec->z, pRec->n); ++ zPayload += pRec->n; ++ } ++ }else{ ++ zHdr += sqlite3PutVarint(zHdr, serial_type); ++ if( pRec->n ){ ++ assert( pRec->z!=0 ); ++ assert( pRec->z!=(const char*)sqlite3CtypeMap ); ++ memcpy(zPayload, pRec->z, pRec->n); ++ zPayload += pRec->n; ++ } ++ } ++ if( pRec==pLast ) break; ++ pRec++; ++ } + assert( nHdr==(int)(zHdr - (u8*)pOut->z) ); + assert( nByte==(int)(zPayload - (u8*)pOut->z) ); + +@@ -88531,7 +98067,7 @@ case OP_MakeRecord: { + break; + } + +-/* Opcode: Count P1 P2 p3 * * ++/* Opcode: Count P1 P2 P3 * * + ** Synopsis: r[P2]=count() + ** + ** Store the number of entries (an integer value) in the table or index +@@ -88741,7 +98277,10 @@ case OP_Savepoint: { + } + } + if( rc ) goto abort_due_to_error; +- ++ if( p->eVdbeState==VDBE_HALT_STATE ){ ++ rc = SQLITE_DONE; ++ goto vdbe_return; ++ } + break; + } + +@@ -88814,7 +98353,8 @@ case OP_AutoCommit: { + ** active. + ** If P2 is non-zero, then a write-transaction is started, or if a + ** read-transaction is already active, it is upgraded to a write-transaction. +-** If P2 is zero, then a read-transaction is started. ++** If P2 is zero, then a read-transaction is started. If P2 is 2 or more ++** then an exclusive transaction is started. + ** + ** P1 is the index of the database file on which the transaction is + ** started. Index 0 is the main database file and index 1 is the +@@ -88844,17 +98384,28 @@ case OP_AutoCommit: { + */ + case OP_Transaction: { + Btree *pBt; ++ Db *pDb; + int iMeta = 0; + + assert( p->bIsReader ); + assert( p->readOnly==0 || pOp->p2==0 ); ++ assert( pOp->p2>=0 && pOp->p2<=2 ); + assert( pOp->p1>=0 && pOp->p1nDb ); + assert( DbMaskTest(p->btreeMask, pOp->p1) ); +- if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){ +- rc = SQLITE_READONLY; ++ assert( rc==SQLITE_OK ); ++ if( pOp->p2 && (db->flags & (SQLITE_QueryOnly|SQLITE_CorruptRdOnly))!=0 ){ ++ if( db->flags & SQLITE_QueryOnly ){ ++ /* Writes prohibited by the "PRAGMA query_only=TRUE" statement */ ++ rc = SQLITE_READONLY; ++ }else{ ++ /* Writes prohibited due to a prior SQLITE_CORRUPT in the current ++ ** transaction */ ++ rc = SQLITE_CORRUPT; ++ } + goto abort_due_to_error; + } +- pBt = db->aDb[pOp->p1].pBt; ++ pDb = &db->aDb[pOp->p1]; ++ pBt = pDb->pBt; + + if( pBt ){ + rc = sqlite3BtreeBeginTrans(pBt, pOp->p2, &iMeta); +@@ -88873,7 +98424,7 @@ case OP_Transaction: { + && pOp->p2 + && (db->autoCommit==0 || db->nVdbeRead>1) + ){ +- assert( sqlite3BtreeIsInTrans(pBt) ); ++ assert( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ); + if( p->iStatement==0 ){ + assert( db->nStatement>=0 && db->nSavepoint>=0 ); + db->nStatement++; +@@ -88893,9 +98444,9 @@ case OP_Transaction: { + } + } + assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); +- if( pOp->p5 +- && (iMeta!=pOp->p3 +- || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i) ++ if( rc==SQLITE_OK ++ && pOp->p5 ++ && (iMeta!=pOp->p3 || pDb->pSchema->iGeneration!=pOp->p4.i) + ){ + /* + ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema +@@ -88922,6 +98473,11 @@ case OP_Transaction: { + } + p->expired = 1; + rc = SQLITE_SCHEMA; ++ ++ /* Set changeCntOn to 0 to prevent the value returned by sqlite3_changes() ++ ** from being modified in sqlite3VdbeHalt(). If this statement is ++ ** reprepared, changeCntOn will be set again. */ ++ p->changeCntOn = 0; + } + if( rc ) goto abort_due_to_error; + break; +@@ -88988,8 +98544,9 @@ case OP_SetCookie: { + rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); + if( pOp->p2==BTREE_SCHEMA_VERSION ){ + /* When the schema cookie changes, record the new cookie internally */ +- pDb->pSchema->schema_cookie = pOp->p3 - pOp->p5; ++ *(u32*)&pDb->pSchema->schema_cookie = *(u32*)&pOp->p3 - pOp->p5; + db->mDbFlags |= DBFLAG_SchemaChange; ++ sqlite3FkClearTriggerCache(db, pOp->p1); + }else if( pOp->p2==BTREE_FILE_FORMAT ){ + /* Record changes in the file format */ + pDb->pSchema->file_format = pOp->p3; +@@ -89088,7 +98645,7 @@ case OP_SetCookie: { + ** + ** See also: OP_OpenRead, OP_ReopenIdx + */ +-case OP_ReopenIdx: { ++case OP_ReopenIdx: { /* ncycle */ + int nField; + KeyInfo *pKeyInfo; + u32 p2; +@@ -89103,11 +98660,13 @@ case OP_ReopenIdx: { + pCur = p->apCsr[pOp->p1]; + if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ + assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ ++ assert( pCur->eCurType==CURTYPE_BTREE ); ++ sqlite3BtreeClearCursor(pCur->uc.pCursor); + goto open_cursor_set_hints; + } + /* If the cursor is not currently open or is open on a different + ** index, then fall through into OP_OpenRead to force a reopen */ +-case OP_OpenRead: ++case OP_OpenRead: /* ncycle */ + case OP_OpenWrite: + + assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); +@@ -89136,23 +98695,23 @@ case OP_OpenWrite: + if( pDb->pSchema->file_format < p->minWriteFileFormat ){ + p->minWriteFileFormat = pDb->pSchema->file_format; + } ++ if( pOp->p5 & OPFLAG_P2ISREG ){ ++ assert( p2>0 ); ++ assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); ++ pIn2 = &aMem[p2]; ++ assert( memIsValid(pIn2) ); ++ assert( (pIn2->flags & MEM_Int)!=0 ); ++ sqlite3VdbeMemIntegerify(pIn2); ++ p2 = (int)pIn2->u.i; ++ /* The p2 value always comes from a prior OP_CreateBtree opcode and ++ ** that opcode will always set the p2 value to 2 or more or else fail. ++ ** If there were a failure, the prepared statement would have halted ++ ** before reaching this instruction. */ ++ assert( p2>=2 ); ++ } + }else{ + wrFlag = 0; +- } +- if( pOp->p5 & OPFLAG_P2ISREG ){ +- assert( p2>0 ); +- assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); +- assert( pOp->opcode==OP_OpenWrite ); +- pIn2 = &aMem[p2]; +- assert( memIsValid(pIn2) ); +- assert( (pIn2->flags & MEM_Int)!=0 ); +- sqlite3VdbeMemIntegerify(pIn2); +- p2 = (int)pIn2->u.i; +- /* The p2 value always comes from a prior OP_CreateBtree opcode and +- ** that opcode will always set the p2 value to 2 or more or else fail. +- ** If there were a failure, the prepared statement would have halted +- ** before reaching this instruction. */ +- assert( p2>=2 ); ++ assert( (pOp->p5 & OPFLAG_P2ISREG)==0 ); + } + if( pOp->p4type==P4_KEYINFO ){ + pKeyInfo = pOp->p4.pKeyInfo; +@@ -89165,8 +98724,9 @@ case OP_OpenWrite: + assert( pOp->p1>=0 ); + assert( nField>=0 ); + testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ +- pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); ++ pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_BTREE); + if( pCur==0 ) goto no_mem; ++ pCur->iDb = iDb; + pCur->nullRow = 1; + pCur->isOrdered = 1; + pCur->pgnoRoot = p2; +@@ -89200,15 +98760,15 @@ open_cursor_set_hints: + ** + ** Duplicate ephemeral cursors are used for self-joins of materialized views. + */ +-case OP_OpenDup: { ++case OP_OpenDup: { /* ncycle */ + VdbeCursor *pOrig; /* The original cursor to be duplicated */ + VdbeCursor *pCx; /* The new cursor */ + + pOrig = p->apCsr[pOp->p2]; + assert( pOrig ); +- assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */ ++ assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */ + +- pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); ++ pCx = allocateCursor(p, pOp->p1, pOrig->nField, CURTYPE_BTREE); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->isEphemeral = 1; +@@ -89216,7 +98776,10 @@ case OP_OpenDup: { + pCx->isTable = pOrig->isTable; + pCx->pgnoRoot = pOrig->pgnoRoot; + pCx->isOrdered = pOrig->isOrdered; +- rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, ++ pCx->ub.pBtx = pOrig->ub.pBtx; ++ pCx->noReuse = 1; ++ pOrig->noReuse = 1; ++ rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, + pCx->pKeyInfo, pCx->uc.pCursor); + /* The sqlite3BtreeCursor() routine can only fail for the first cursor + ** opened for a database. Since there is already an open cursor when this +@@ -89226,7 +98789,7 @@ case OP_OpenDup: { + } + + +-/* Opcode: OpenEphemeral P1 P2 * P4 P5 ++/* Opcode: OpenEphemeral P1 P2 P3 P4 P5 + ** Synopsis: nColumn=P2 + ** + ** Open a new cursor P1 to a transient table. +@@ -89246,6 +98809,10 @@ case OP_OpenDup: { + ** in btree.h. These flags control aspects of the operation of + ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are + ** added automatically. ++** ++** If P3 is positive, then reg[P3] is modified slightly so that it ++** can be used as zero-length data for OP_Insert. This is an optimization ++** that avoids an extra OP_Blob opcode to initialize that register. + */ + /* Opcode: OpenAutoindex P1 P2 * P4 * + ** Synopsis: nColumn=P2 +@@ -89255,8 +98822,8 @@ case OP_OpenDup: { + ** by this opcode will be used for automatically created transient + ** indices in joins. + */ +-case OP_OpenAutoindex: +-case OP_OpenEphemeral: { ++case OP_OpenAutoindex: /* ncycle */ ++case OP_OpenEphemeral: { /* ncycle */ + VdbeCursor *pCx; + KeyInfo *pKeyInfo; + +@@ -89268,50 +98835,68 @@ case OP_OpenEphemeral: { + SQLITE_OPEN_TRANSIENT_DB; + assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); ++ if( pOp->p3>0 ){ ++ /* Make register reg[P3] into a value that can be used as the data ++ ** form sqlite3BtreeInsert() where the length of the data is zero. */ ++ assert( pOp->p2==0 ); /* Only used when number of columns is zero */ ++ assert( pOp->opcode==OP_OpenEphemeral ); ++ assert( aMem[pOp->p3].flags & MEM_Null ); ++ aMem[pOp->p3].n = 0; ++ aMem[pOp->p3].z = ""; ++ } + pCx = p->apCsr[pOp->p1]; +- if( pCx && pCx->pBtx ){ +- /* If the ephermeral table is already open, erase all existing content +- ** so that the table is empty again, rather than creating a new table. */ ++ if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){ ++ /* If the ephemeral table is already open and has no duplicates from ++ ** OP_OpenDup, then erase all existing content so that the table is ++ ** empty again, rather than creating a new table. */ + assert( pCx->isEphemeral ); + pCx->seqCount = 0; + pCx->cacheStatus = CACHE_STALE; +- rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); ++ rc = sqlite3BtreeClearTable(pCx->ub.pBtx, pCx->pgnoRoot, 0); + }else{ +- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); ++ pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_BTREE); + if( pCx==0 ) goto no_mem; + pCx->isEphemeral = 1; +- rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, ++ rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->ub.pBtx, + BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, + vfsFlags); + if( rc==SQLITE_OK ){ +- rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); +- } +- if( rc==SQLITE_OK ){ +- /* If a transient index is required, create it by calling +- ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before +- ** opening it. If a transient table is required, just use the +- ** automatically created table with root-page 1 (an BLOB_INTKEY table). +- */ +- if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ +- assert( pOp->p4type==P4_KEYINFO ); +- rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, +- BTREE_BLOBKEY | pOp->p5); +- if( rc==SQLITE_OK ){ +- assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); +- assert( pKeyInfo->db==db ); +- assert( pKeyInfo->enc==ENC(db) ); +- rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, +- pKeyInfo, pCx->uc.pCursor); ++ rc = sqlite3BtreeBeginTrans(pCx->ub.pBtx, 1, 0); ++ if( rc==SQLITE_OK ){ ++ /* If a transient index is required, create it by calling ++ ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ++ ** opening it. If a transient table is required, just use the ++ ** automatically created table with root-page 1 (an BLOB_INTKEY table). ++ */ ++ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ ++ assert( pOp->p4type==P4_KEYINFO ); ++ rc = sqlite3BtreeCreateTable(pCx->ub.pBtx, &pCx->pgnoRoot, ++ BTREE_BLOBKEY | pOp->p5); ++ if( rc==SQLITE_OK ){ ++ assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); ++ assert( pKeyInfo->db==db ); ++ assert( pKeyInfo->enc==ENC(db) ); ++ rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, ++ pKeyInfo, pCx->uc.pCursor); ++ } ++ pCx->isTable = 0; ++ }else{ ++ pCx->pgnoRoot = SCHEMA_ROOT; ++ rc = sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR, ++ 0, pCx->uc.pCursor); ++ pCx->isTable = 1; + } +- pCx->isTable = 0; ++ } ++ pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); ++ assert( p->apCsr[pOp->p1]==pCx ); ++ if( rc ){ ++ assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); ++ sqlite3BtreeClose(pCx->ub.pBtx); ++ p->apCsr[pOp->p1] = 0; /* Not required; helps with static analysis */ + }else{ +- pCx->pgnoRoot = SCHEMA_ROOT; +- rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, +- 0, pCx->uc.pCursor); +- pCx->isTable = 1; ++ assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); + } + } +- pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); + } + if( rc ) goto abort_due_to_error; + pCx->nullRow = 1; +@@ -89333,7 +98918,7 @@ case OP_SorterOpen: { + + assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); +- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER); ++ pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_SORTER); + if( pCx==0 ) goto no_mem; + pCx->pKeyInfo = pOp->p4.pKeyInfo; + assert( pCx->pKeyInfo->db==db ); +@@ -89375,14 +98960,15 @@ case OP_SequenceTest: { + ** is the only cursor opcode that works with a pseudo-table. + ** + ** P3 is the number of fields in the records that will be stored by +-** the pseudo-table. ++** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor ++** will return NULL for every column. + */ + case OP_OpenPseudo: { + VdbeCursor *pCx; + + assert( pOp->p1>=0 ); + assert( pOp->p3>=0 ); +- pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO); ++ pCx = allocateCursor(p, pOp->p1, pOp->p3, CURTYPE_PSEUDO); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->seekResult = pOp->p2; +@@ -89401,7 +98987,7 @@ case OP_OpenPseudo: { + ** Close a cursor previously opened as P1. If P1 is not + ** currently open, this instruction is a no-op. + */ +-case OP_Close: { ++case OP_Close: { /* ncycle */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]); + p->apCsr[pOp->p1] = 0; +@@ -89518,10 +99104,10 @@ case OP_ColumnsUsed: { + ** + ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt + */ +-case OP_SeekLT: /* jump, in3, group */ +-case OP_SeekLE: /* jump, in3, group */ +-case OP_SeekGE: /* jump, in3, group */ +-case OP_SeekGT: { /* jump, in3, group */ ++case OP_SeekLT: /* jump0, in3, group, ncycle */ ++case OP_SeekLE: /* jump0, in3, group, ncycle */ ++case OP_SeekGE: /* jump0, in3, group, ncycle */ ++case OP_SeekGT: { /* jump0, in3, group, ncycle */ + int res; /* Comparison result */ + int oc; /* Opcode */ + VdbeCursor *pC; /* The cursor to seek */ +@@ -89570,6 +99156,7 @@ case OP_SeekGT: { /* jump, in3, group */ + /* If the P3 value could not be converted into an integer without + ** loss of information, then special processing is required... */ + if( (newType & (MEM_Int|MEM_IntReal))==0 ){ ++ int c; + if( (newType & MEM_Real)==0 ){ + if( (newType & MEM_Null) || oc>=OP_SeekGE ){ + VdbeBranchTaken(1,2); +@@ -89579,7 +99166,8 @@ case OP_SeekGT: { /* jump, in3, group */ + if( rc!=SQLITE_OK ) goto abort_due_to_error; + goto seek_not_found; + } +- }else ++ } ++ c = sqlite3IntFloatCompare(iKey, pIn3->u.r); + + /* If the approximation iKey is larger than the actual real search + ** term, substitute >= for > and < for <=. e.g. if the search term +@@ -89588,7 +99176,7 @@ case OP_SeekGT: { /* jump, in3, group */ + ** (x > 4.9) -> (x >= 5) + ** (x <= 4.9) -> (x < 5) + */ +- if( pIn3->u.r<(double)iKey ){ ++ if( c>0 ){ + assert( OP_SeekGE==(OP_SeekGT-1) ); + assert( OP_SeekLT==(OP_SeekLE-1) ); + assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) ); +@@ -89597,14 +99185,14 @@ case OP_SeekGT: { /* jump, in3, group */ + + /* If the approximation iKey is smaller than the actual real search + ** term, substitute <= for < and > for >=. */ +- else if( pIn3->u.r>(double)iKey ){ ++ else if( c<0 ){ + assert( OP_SeekLE==(OP_SeekLT+1) ); + assert( OP_SeekGT==(OP_SeekGE+1) ); + assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); + if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; + } + } +- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res); ++ rc = sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)iKey, 0, &res); + pC->movetoTarget = iKey; /* Used by OP_Delete */ + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; +@@ -89648,10 +99236,16 @@ case OP_SeekGT: { /* jump, in3, group */ + + r.aMem = &aMem[pOp->p3]; + #ifdef SQLITE_DEBUG +- { int i; for(i=0; i0 ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]); ++ } ++ } + #endif + r.eqSeen = 0; +- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res); ++ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } +@@ -89710,34 +99304,235 @@ seek_not_found: + break; + } + +-/* Opcode: SeekHit P1 P2 * * * +-** Synopsis: seekHit=P2 ++ ++/* Opcode: SeekScan P1 P2 * * P5 ++** Synopsis: Scan-ahead up to P1 rows ++** ++** This opcode is a prefix opcode to OP_SeekGE. In other words, this ++** opcode must be immediately followed by OP_SeekGE. This constraint is ++** checked by assert() statements. ++** ++** This opcode uses the P1 through P4 operands of the subsequent ++** OP_SeekGE. In the text that follows, the operands of the subsequent ++** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only ++** the P1, P2 and P5 operands of this opcode are also used, and are called ++** This.P1, This.P2 and This.P5. ++** ++** This opcode helps to optimize IN operators on a multi-column index ++** where the IN operator is on the later terms of the index by avoiding ++** unnecessary seeks on the btree, substituting steps to the next row ++** of the b-tree instead. A correct answer is obtained if this opcode ++** is omitted or is a no-op. ++** ++** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which ++** is the desired entry that we want the cursor SeekGE.P1 to be pointing ++** to. Call this SeekGE.P3/P4 row the "target". ++** ++** If the SeekGE.P1 cursor is not currently pointing to a valid row, ++** then this opcode is a no-op and control passes through into the OP_SeekGE. ++** ++** If the SeekGE.P1 cursor is pointing to a valid row, then that row ++** might be the target row, or it might be near and slightly before the ++** target row, or it might be after the target row. If the cursor is ++** currently before the target row, then this opcode attempts to position ++** the cursor on or after the target row by invoking sqlite3BtreeStep() ++** on the cursor between 1 and This.P1 times. ++** ++** The This.P5 parameter is a flag that indicates what to do if the ++** cursor ends up pointing at a valid row that is past the target ++** row. If This.P5 is false (0) then a jump is made to SeekGE.P2. If ++** This.P5 is true (non-zero) then a jump is made to This.P2. The P5==0 ++** case occurs when there are no inequality constraints to the right of ++** the IN constraint. The jump to SeekGE.P2 ends the loop. The P5!=0 case ++** occurs when there are inequality constraints to the right of the IN ++** operator. In that case, the This.P2 will point either directly to or ++** to setup code prior to the OP_IdxGT or OP_IdxGE opcode that checks for ++** loop terminate. ++** ++** Possible outcomes from this opcode:
          ++** ++**
        1. If the cursor is initially not pointed to any valid row, then ++** fall through into the subsequent OP_SeekGE opcode. ++** ++**
        2. If the cursor is left pointing to a row that is before the target ++** row, even after making as many as This.P1 calls to ++** sqlite3BtreeNext(), then also fall through into OP_SeekGE. ++** ++**
        3. If the cursor is left pointing at the target row, either because it ++** was at the target row to begin with or because one or more ++** sqlite3BtreeNext() calls moved the cursor to the target row, ++** then jump to This.P2.., ++** ++**
        4. If the cursor started out before the target row and a call to ++** to sqlite3BtreeNext() moved the cursor off the end of the index ++** (indicating that the target row definitely does not exist in the ++** btree) then jump to SeekGE.P2, ending the loop. ++** ++**
        5. If the cursor ends up on a valid row that is past the target row ++** (indicating that the target row does not exist in the btree) then ++** jump to SeekOP.P2 if This.P5==0 or to This.P2 if This.P5>0. ++**
        ++*/ ++case OP_SeekScan: { /* ncycle */ ++ VdbeCursor *pC; ++ int res; ++ int nStep; ++ UnpackedRecord r; ++ ++ assert( pOp[1].opcode==OP_SeekGE ); ++ ++ /* If pOp->p5 is clear, then pOp->p2 points to the first instruction past the ++ ** OP_IdxGT that follows the OP_SeekGE. Otherwise, it points to the first ++ ** opcode past the OP_SeekGE itself. */ ++ assert( pOp->p2>=(int)(pOp-aOp)+2 ); ++#ifdef SQLITE_DEBUG ++ if( pOp->p5==0 ){ ++ /* There are no inequality constraints following the IN constraint. */ ++ assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); ++ assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); ++ assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); ++ assert( aOp[pOp->p2-1].opcode==OP_IdxGT ++ || aOp[pOp->p2-1].opcode==OP_IdxGE ); ++ testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); ++ }else{ ++ /* There are inequality constraints. */ ++ assert( pOp->p2==(int)(pOp-aOp)+2 ); ++ assert( aOp[pOp->p2-1].opcode==OP_SeekGE ); ++ } ++#endif ++ ++ assert( pOp->p1>0 ); ++ pC = p->apCsr[pOp[1].p1]; ++ assert( pC!=0 ); ++ assert( pC->eCurType==CURTYPE_BTREE ); ++ assert( !pC->isTable ); ++ if( !sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) ){ ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ printf("... cursor not valid - fall through\n"); ++ } ++#endif ++ break; ++ } ++ nStep = pOp->p1; ++ assert( nStep>=1 ); ++ r.pKeyInfo = pC->pKeyInfo; ++ r.nField = (u16)pOp[1].p4.i; ++ r.default_rc = 0; ++ r.aMem = &aMem[pOp[1].p3]; ++#ifdef SQLITE_DEBUG ++ { ++ int i; ++ for(i=0; i0 && pOp->p5==0 ){ ++ seekscan_search_fail: ++ /* Jump to SeekGE.P2, ending the loop */ ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ printf("... %d steps and then skip\n", pOp->p1 - nStep); ++ } ++#endif ++ VdbeBranchTaken(1,3); ++ pOp++; ++ goto jump_to_p2; ++ } ++ if( res>=0 ){ ++ /* Jump to This.P2, bypassing the OP_SeekGE opcode */ ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ printf("... %d steps and then success\n", pOp->p1 - nStep); ++ } ++#endif ++ VdbeBranchTaken(2,3); ++ goto jump_to_p2; ++ break; ++ } ++ if( nStep<=0 ){ ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ printf("... fall through after %d steps\n", pOp->p1); ++ } ++#endif ++ VdbeBranchTaken(0,3); ++ break; ++ } ++ nStep--; ++ pC->cacheStatus = CACHE_STALE; ++ rc = sqlite3BtreeNext(pC->uc.pCursor, 0); ++ if( rc ){ ++ if( rc==SQLITE_DONE ){ ++ rc = SQLITE_OK; ++ goto seekscan_search_fail; ++ }else{ ++ goto abort_due_to_error; ++ } ++ } ++ } ++ ++ break; ++} ++ ++ ++/* Opcode: SeekHit P1 P2 P3 * * ++** Synopsis: set P2<=seekHit<=P3 ++** ++** Increase or decrease the seekHit value for cursor P1, if necessary, ++** so that it is no less than P2 and no greater than P3. + ** +-** Set the seekHit flag on cursor P1 to the value in P2. +-** The seekHit flag is used by the IfNoHope opcode. ++** The seekHit integer represents the maximum of terms in an index for which ++** there is known to be at least one match. If the seekHit value is smaller ++** than the total number of equality terms in an index lookup, then the ++** OP_IfNoHope opcode might run to see if the IN loop can be abandoned ++** early, thus saving work. This is part of the IN-early-out optimization. + ** +-** P1 must be a valid b-tree cursor. P2 must be a boolean value, +-** either 0 or 1. ++** P1 must be a valid b-tree cursor. + */ +-case OP_SeekHit: { ++case OP_SeekHit: { /* ncycle */ + VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); +- assert( pOp->p2==0 || pOp->p2==1 ); +- pC->seekHit = pOp->p2 & 1; ++ assert( pOp->p3>=pOp->p2 ); ++ if( pC->seekHitp2 ){ ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p2); ++ } ++#endif ++ pC->seekHit = pOp->p2; ++ }else if( pC->seekHit>pOp->p3 ){ ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p3); ++ } ++#endif ++ pC->seekHit = pOp->p3; ++ } + break; + } + + /* Opcode: IfNotOpen P1 P2 * * * + ** Synopsis: if( !csr[P1] ) goto P2 + ** +-** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through. ++** If cursor P1 is not open or if P1 is set to a NULL row using the ++** OP_NullRow opcode, then jump to instruction P2. Otherwise, fall through. + */ + case OP_IfNotOpen: { /* jump */ ++ VdbeCursor *pCur; ++ + assert( pOp->p1>=0 && pOp->p1nCursor ); +- VdbeBranchTaken(p->apCsr[pOp->p1]==0, 2); +- if( !p->apCsr[pOp->p1] ){ ++ pCur = p->apCsr[pOp->p1]; ++ VdbeBranchTaken(pCur==0 || pCur->nullRow, 2); ++ if( pCur==0 || pCur->nullRow ){ + goto jump_to_p2_and_check_for_interrupt; + } + break; +@@ -89783,16 +99578,20 @@ case OP_IfNotOpen: { /* jump */ + ** Synopsis: key=r[P3@P4] + ** + ** Register P3 is the first of P4 registers that form an unpacked +-** record. ++** record. Cursor P1 is an index btree. P2 is a jump destination. ++** In other words, the operands to this opcode are the same as the ++** operands to OP_NotFound and OP_IdxGT. + ** +-** Cursor P1 is on an index btree. If the seekHit flag is set on P1, then +-** this opcode is a no-op. But if the seekHit flag of P1 is clear, then +-** check to see if there is any entry in P1 that matches the +-** prefix identified by P3 and P4. If no entry matches the prefix, +-** jump to P2. Otherwise fall through. ++** This opcode is an optimization attempt only. If this opcode always ++** falls through, the correct answer is still obtained, but extra work ++** is performed. + ** +-** This opcode behaves like OP_NotFound if the seekHit +-** flag is clear and it behaves like OP_Noop if the seekHit flag is set. ++** A value of N in the seekHit flag of cursor P1 means that there exists ++** a key P3:N that will match some record in the index. We want to know ++** if it is possible for a record P3:P4 to match some record in the ++** index. If it is not possible, we can skip some work. So if seekHit ++** is less than P4, attempt to find out if a match is possible by running ++** OP_NotFound. + ** + ** This opcode is used in IN clause processing for a multi-column key. + ** If an IN clause is attached to an element of the key other than the +@@ -89829,24 +99628,26 @@ case OP_IfNotOpen: { /* jump */ + ** + ** See also: NotFound, Found, NotExists + */ +-case OP_IfNoHope: { /* jump, in3 */ ++case OP_IfNoHope: { /* jump, in3, ncycle */ + VdbeCursor *pC; + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); +- if( pC->seekHit ) break; ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ printf("seekHit is %d\n", pC->seekHit); ++ } ++#endif ++ if( pC->seekHit>=pOp->p4.i ) break; + /* Fall through into OP_NotFound */ + /* no break */ deliberate_fall_through + } +-case OP_NoConflict: /* jump, in3 */ +-case OP_NotFound: /* jump, in3 */ +-case OP_Found: { /* jump, in3 */ ++case OP_NoConflict: /* jump, in3, ncycle */ ++case OP_NotFound: /* jump, in3, ncycle */ ++case OP_Found: { /* jump, in3, ncycle */ + int alreadyExists; +- int takeJump; + int ii; + VdbeCursor *pC; +- int res; +- UnpackedRecord *pFree; + UnpackedRecord *pIdxKey; + UnpackedRecord r; + +@@ -89861,52 +99662,42 @@ case OP_Found: { /* jump, in3 */ + #ifdef SQLITE_DEBUG + pC->seekOp = pOp->opcode; + #endif +- pIn3 = &aMem[pOp->p3]; ++ r.aMem = &aMem[pOp->p3]; + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); + assert( pC->isTable==0 ); +- if( pOp->p4.i>0 ){ ++ r.nField = (u16)pOp->p4.i; ++ if( r.nField>0 ){ ++ /* Key values in an array of registers */ + r.pKeyInfo = pC->pKeyInfo; +- r.nField = (u16)pOp->p4.i; +- r.aMem = pIn3; ++ r.default_rc = 0; + #ifdef SQLITE_DEBUG ++ (void)sqlite3FaultSim(50); /* For use by --counter in TH3 */ + for(ii=0; iip3+ii, &r.aMem[ii]); + } + #endif +- pIdxKey = &r; +- pFree = 0; ++ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &pC->seekResult); + }else{ +- assert( pIn3->flags & MEM_Blob ); +- rc = ExpandBlob(pIn3); ++ /* Composite key generated by OP_MakeRecord */ ++ assert( r.aMem->flags & MEM_Blob ); ++ assert( pOp->opcode!=OP_NoConflict ); ++ rc = ExpandBlob(r.aMem); + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); + if( rc ) goto no_mem; +- pFree = pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); ++ pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); + if( pIdxKey==0 ) goto no_mem; +- sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); +- } +- pIdxKey->default_rc = 0; +- takeJump = 0; +- if( pOp->opcode==OP_NoConflict ){ +- /* For the OP_NoConflict opcode, take the jump if any of the +- ** input fields are NULL, since any key with a NULL will not +- ** conflict */ +- for(ii=0; iinField; ii++){ +- if( pIdxKey->aMem[ii].flags & MEM_Null ){ +- takeJump = 1; +- break; +- } +- } ++ sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey); ++ pIdxKey->default_rc = 0; ++ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult); ++ sqlite3DbFreeNN(db, pIdxKey); + } +- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); +- if( pFree ) sqlite3DbFreeNN(db, pFree); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } +- pC->seekResult = res; +- alreadyExists = (res==0); ++ alreadyExists = (pC->seekResult==0); + pC->nullRow = 1-alreadyExists; + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; +@@ -89914,8 +99705,25 @@ case OP_Found: { /* jump, in3 */ + VdbeBranchTaken(alreadyExists!=0,2); + if( alreadyExists ) goto jump_to_p2; + }else{ +- VdbeBranchTaken(takeJump||alreadyExists==0,2); +- if( takeJump || !alreadyExists ) goto jump_to_p2; ++ if( !alreadyExists ){ ++ VdbeBranchTaken(1,2); ++ goto jump_to_p2; ++ } ++ if( pOp->opcode==OP_NoConflict ){ ++ /* For the OP_NoConflict opcode, take the jump if any of the ++ ** input fields are NULL, since any key with a NULL will not ++ ** conflict */ ++ for(ii=0; iiopcode==OP_IfNoHope ){ ++ pC->seekHit = pOp->p4.i; ++ } + } + break; + } +@@ -89967,7 +99775,7 @@ case OP_Found: { /* jump, in3 */ + ** + ** See also: Found, NotFound, NoConflict, SeekRowid + */ +-case OP_SeekRowid: { /* jump, in3 */ ++case OP_SeekRowid: { /* jump0, in3, ncycle */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; +@@ -89992,7 +99800,7 @@ case OP_SeekRowid: { /* jump, in3 */ + } + /* Fall through into OP_NotExists */ + /* no break */ deliberate_fall_through +-case OP_NotExists: /* jump, in3 */ ++case OP_NotExists: /* jump, in3, ncycle */ + pIn3 = &aMem[pOp->p3]; + assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); + assert( pOp->p1>=0 && pOp->p1nCursor ); +@@ -90008,7 +99816,7 @@ notExistsWithKey: + pCrsr = pC->uc.pCursor; + assert( pCrsr!=0 ); + res = 0; +- rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); ++ rc = sqlite3BtreeTableMoveto(pCrsr, iKey, 0, &res); + assert( rc==SQLITE_OK || res==0 ); + pC->movetoTarget = iKey; /* Used by OP_Delete */ + pC->nullRow = 0; +@@ -90066,8 +99874,10 @@ case OP_NewRowid: { /* out2 */ + VdbeCursor *pC; /* Cursor of table to get the new rowid */ + int res; /* Result of an sqlite3BtreeLast() */ + int cnt; /* Counter to limit the number of searches */ ++#ifndef SQLITE_OMIT_AUTOINCREMENT + Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ + VdbeFrame *pFrame; /* Root frame of VDBE */ ++#endif + + v = 0; + res = 0; +@@ -90163,7 +99973,7 @@ case OP_NewRowid: { /* out2 */ + do{ + sqlite3_randomness(sizeof(v), &v); + v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */ +- }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v, ++ }while( ((rc = sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)v, + 0, &res))==SQLITE_OK) + && (res==0) + && (++cnt<100)); +@@ -90253,14 +100063,14 @@ case OP_Insert: { + assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) ); + }else{ + pTab = 0; +- zDb = 0; /* Not needed. Silence a compiler warning. */ ++ zDb = 0; + } + + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK + /* Invoke the pre-update hook, if any */ + if( pTab ){ + if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){ +- sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey,pOp->p2); ++ sqlite3VdbePreUpdateHook(p,pC,SQLITE_INSERT,zDb,pTab,x.nKey,pOp->p2,-1); + } + if( db->xUpdateCallback==0 || pTab->aCol==0 ){ + /* Prevent post-update hook from running in cases when it should not */ +@@ -90270,9 +100080,12 @@ case OP_Insert: { + if( pOp->p5 & OPFLAG_ISNOOP ) break; + #endif + +- if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; +- if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; +- assert( pData->flags & (MEM_Blob|MEM_Str) ); ++ assert( (pOp->p5 & OPFLAG_LASTROWID)==0 || (pOp->p5 & OPFLAG_NCHANGE)!=0 ); ++ if( pOp->p5 & OPFLAG_NCHANGE ){ ++ p->nChange++; ++ if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; ++ } ++ assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); + x.pData = pData->z; + x.nData = pData->n; + seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); +@@ -90282,11 +100095,14 @@ case OP_Insert: { + x.nZero = 0; + } + x.pKey = 0; ++ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); + rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, +- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult ++ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), ++ seekResult + ); + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; ++ colCacheCtr++; + + /* Invoke the update-hook if required. */ + if( rc ) goto abort_due_to_error; +@@ -90300,6 +100116,33 @@ case OP_Insert: { + break; + } + ++/* Opcode: RowCell P1 P2 P3 * * ++** ++** P1 and P2 are both open cursors. Both must be opened on the same type ++** of table - intkey or index. This opcode is used as part of copying ++** the current row from P2 into P1. If the cursors are opened on intkey ++** tables, register P3 contains the rowid to use with the new record in ++** P1. If they are opened on index tables, P3 is not used. ++** ++** This opcode must be followed by either an Insert or InsertIdx opcode ++** with the OPFLAG_PREFORMAT flag set to complete the insert operation. ++*/ ++case OP_RowCell: { ++ VdbeCursor *pDest; /* Cursor to write to */ ++ VdbeCursor *pSrc; /* Cursor to read from */ ++ i64 iKey; /* Rowid value to insert with */ ++ assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert ); ++ assert( pOp[1].opcode==OP_Insert || pOp->p3==0 ); ++ assert( pOp[1].opcode==OP_IdxInsert || pOp->p3>0 ); ++ assert( pOp[1].p5 & OPFLAG_PREFORMAT ); ++ pDest = p->apCsr[pOp->p1]; ++ pSrc = p->apCsr[pOp->p2]; ++ iKey = pOp->p3 ? aMem[pOp->p3].u.i : 0; ++ rc = sqlite3BtreeTransferRow(pDest->uc.pCursor, pSrc->uc.pCursor, iKey); ++ if( rc!=SQLITE_OK ) goto abort_due_to_error; ++ break; ++}; ++ + /* Opcode: Delete P1 P2 P3 P4 P5 + ** + ** Delete the record at which the P1 cursor is currently pointing. +@@ -90313,13 +100156,18 @@ case OP_Insert: { + ** left in an undefined state. + ** + ** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this +-** delete one of several associated with deleting a table row and all its +-** associated index entries. Exactly one of those deletes is the "primary" +-** delete. The others are all on OPFLAG_FORDELETE cursors or else are +-** marked with the AUXDELETE flag. ++** delete is one of several associated with deleting a table row and ++** all its associated index entries. Exactly one of those deletes is ++** the "primary" delete. The others are all on OPFLAG_FORDELETE ++** cursors or else are marked with the AUXDELETE flag. + ** +-** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row +-** change count is incremented (otherwise not). ++** If the OPFLAG_NCHANGE (0x01) flag of P2 (NB: P2 not P5) is set, then ++** the row change count is incremented (otherwise not). ++** ++** If the OPFLAG_ISNOOP (0x40) flag of P2 (not P5!) is set, then the ++** pre-update-hook for deletes is run, but the btree is otherwise unchanged. ++** This happens when the OP_Delete is to be shortly followed by an OP_Insert ++** with the same key, causing the btree entry to be overwritten. + ** + ** P1 must not be pseudo-table. It has to be a real table with + ** multiple rows. +@@ -90378,13 +100226,14 @@ case OP_Delete: { + pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor); + } + }else{ +- zDb = 0; /* Not needed. Silence a compiler warning. */ +- pTab = 0; /* Not needed. Silence a compiler warning. */ ++ zDb = 0; ++ pTab = 0; + } + + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK + /* Invoke the pre-update-hook if required. */ +- if( db->xPreUpdateCallback && pOp->p4.pTab ){ ++ assert( db->xPreUpdateCallback==0 || pTab==pOp->p4.pTab ); ++ if( db->xPreUpdateCallback && pTab ){ + assert( !(opflags & OPFLAG_ISUPDATE) + || HasRowid(pTab)==0 + || (aMem[pOp->p3].flags & MEM_Int) +@@ -90392,7 +100241,7 @@ case OP_Delete: { + sqlite3VdbePreUpdateHook(p, pC, + (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, + zDb, pTab, pC->movetoTarget, +- pOp->p3 ++ pOp->p3, -1 + ); + } + if( opflags & OPFLAG_ISNOOP ) break; +@@ -90419,13 +100268,14 @@ case OP_Delete: { + + rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); + pC->cacheStatus = CACHE_STALE; ++ colCacheCtr++; + pC->seekResult = 0; + if( rc ) goto abort_due_to_error; + + /* Invoke the update-hook if required. */ + if( opflags & OPFLAG_NCHANGE ){ + p->nChange++; +- if( db->xUpdateCallback && HasRowid(pTab) ){ ++ if( db->xUpdateCallback && ALWAYS(pTab!=0) && HasRowid(pTab) ){ + db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName, + pC->movetoTarget); + assert( pC->iDb>=0 ); +@@ -90486,13 +100336,13 @@ case OP_SorterCompare: { + ** Write into register P2 the current sorter data for sorter cursor P1. + ** Then clear the column header cache on cursor P3. + ** +-** This opcode is normally use to move a record out of the sorter and into ++** This opcode is normally used to move a record out of the sorter and into + ** a register that is the source for a pseudo-table cursor created using + ** OpenPseudo. That pseudo-table cursor is the one that is identified by + ** parameter P3. Clearing the P3 column cache as part of this opcode saves + ** us from having to issue a separate NullRow instruction to clear that cache. + */ +-case OP_SorterData: { ++case OP_SorterData: { /* ncycle */ + VdbeCursor *pC; + + pOut = &aMem[pOp->p2]; +@@ -90553,7 +100403,7 @@ case OP_RowData: { + /* The OP_RowData opcodes always follow OP_NotExists or + ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions + ** that might invalidate the cursor. +- ** If this where not the case, on of the following assert()s ++ ** If this were not the case, one of the following assert()s + ** would fail. Should this ever change (because of changes in the code + ** generator) then the fix would be to insert a call to + ** sqlite3VdbeCursorMoveto(). +@@ -90575,7 +100425,7 @@ case OP_RowData: { + } + + /* Opcode: Rowid P1 P2 * * * +-** Synopsis: r[P2]=rowid ++** Synopsis: r[P2]=PX rowid of P1 + ** + ** Store in register P2 an integer which is the key of the table entry that + ** P1 is currently point to. +@@ -90584,7 +100434,7 @@ case OP_RowData: { + ** be a separate OP_VRowid opcode for use with virtual tables, but this + ** one opcode now works for both table types. + */ +-case OP_Rowid: { /* out2 */ ++case OP_Rowid: { /* out2, ncycle */ + VdbeCursor *pC; + i64 v; + sqlite3_vtab *pVtab; +@@ -90630,13 +100480,25 @@ case OP_Rowid: { /* out2 */ + ** Move the cursor P1 to a null row. Any OP_Column operations + ** that occur while the cursor is on the null row will always + ** write a NULL. ++** ++** If cursor P1 is not previously opened, open it now to a special ++** pseudo-cursor that always returns NULL for every column. + */ + case OP_NullRow: { + VdbeCursor *pC; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; +- assert( pC!=0 ); ++ if( pC==0 ){ ++ /* If the cursor is not already open, create a special kind of ++ ** pseudo-cursor that always gives null rows. */ ++ pC = allocateCursor(p, pOp->p1, 1, CURTYPE_PSEUDO); ++ if( pC==0 ) goto no_mem; ++ pC->seekResult = 0; ++ pC->isTable = 1; ++ pC->noReuse = 1; ++ pC->uc.pCursor = sqlite3BtreeFakeValidCursor(); ++ } + pC->nullRow = 1; + pC->cacheStatus = CACHE_STALE; + if( pC->eCurType==CURTYPE_BTREE ){ +@@ -90671,8 +100533,8 @@ case OP_NullRow: { + ** from the end toward the beginning. In other words, the cursor is + ** configured to use Prev, not Next. + */ +-case OP_SeekEnd: +-case OP_Last: { /* jump */ ++case OP_SeekEnd: /* ncycle */ ++case OP_Last: { /* jump0, ncycle */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; +@@ -90706,28 +100568,38 @@ case OP_Last: { /* jump */ + break; + } + +-/* Opcode: IfSmaller P1 P2 P3 * * ++/* Opcode: IfSizeBetween P1 P2 P3 P4 * + ** +-** Estimate the number of rows in the table P1. Jump to P2 if that +-** estimate is less than approximately 2**(0.1*P3). ++** Let N be the approximate number of rows in the table or index ++** with cursor P1 and let X be 10*log2(N) if N is positive or -1 ++** if N is zero. ++** ++** Jump to P2 if X is in between P3 and P4, inclusive. + */ +-case OP_IfSmaller: { /* jump */ ++case OP_IfSizeBetween: { /* jump */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; + i64 sz; + + assert( pOp->p1>=0 && pOp->p1nCursor ); ++ assert( pOp->p4type==P4_INT32 ); ++ assert( pOp->p3>=-1 && pOp->p3<=640*2 ); ++ assert( pOp->p4.i>=-1 && pOp->p4.i<=640*2 ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + pCrsr = pC->uc.pCursor; + assert( pCrsr ); + rc = sqlite3BtreeFirst(pCrsr, &res); + if( rc ) goto abort_due_to_error; +- if( res==0 ){ ++ if( res!=0 ){ ++ sz = -1; /* -Infinity encoding */ ++ }else{ + sz = sqlite3BtreeRowCountEst(pCrsr); +- if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)p3 ) res = 1; ++ assert( sz>0 ); ++ sz = sqlite3LogEst((u64)sz); + } ++ res = sz>=pOp->p3 && sz<=pOp->p4.i; + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; + break; +@@ -90755,8 +100627,8 @@ case OP_IfSmaller: { /* jump */ + ** regression tests can determine whether or not the optimizer is + ** correctly optimizing out sorts. + */ +-case OP_SorterSort: /* jump */ +-case OP_Sort: { /* jump */ ++case OP_SorterSort: /* jump ncycle */ ++case OP_Sort: { /* jump ncycle */ + #ifdef SQLITE_TEST + sqlite3_sort_count++; + sqlite3_search_count--; +@@ -90773,17 +100645,22 @@ case OP_Sort: { /* jump */ + ** If the table or index is not empty, fall through to the following + ** instruction. + ** ++** If P2 is zero, that is an assertion that the P1 table is never ++** empty and hence the jump will never be taken. ++** + ** This opcode leaves the cursor configured to move in forward order, + ** from the beginning toward the end. In other words, the cursor is + ** configured to use Next, not Prev. + */ +-case OP_Rewind: { /* jump */ ++case OP_Rewind: { /* jump0, ncycle */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5==0 ); ++ assert( pOp->p2>=0 && pOp->p2nOp ); ++ + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); +@@ -90803,13 +100680,14 @@ case OP_Rewind: { /* jump */ + } + if( rc ) goto abort_due_to_error; + pC->nullRow = (u8)res; +- assert( pOp->p2>0 && pOp->p2nOp ); +- VdbeBranchTaken(res!=0,2); +- if( res ) goto jump_to_p2; ++ if( pOp->p2>0 ){ ++ VdbeBranchTaken(res!=0,2); ++ if( res ) goto jump_to_p2; ++ } + break; + } + +-/* Opcode: Next P1 P2 P3 P4 P5 ++/* Opcode: Next P1 P2 P3 * P5 + ** + ** Advance cursor P1 so that it points to the next key/data pair in its + ** table or index. If there are no more key/value pairs then fall through +@@ -90828,15 +100706,12 @@ case OP_Rewind: { /* jump */ + ** omitted if that index had been unique. P3 is usually 0. P3 is + ** always either 0 or 1. + ** +-** P4 is always of type P4_ADVANCE. The function pointer points to +-** sqlite3BtreeNext(). +-** + ** If P5 is positive and the jump is taken, then event counter + ** number P5-1 in the prepared statement is incremented. + ** + ** See also: Prev + */ +-/* Opcode: Prev P1 P2 P3 P4 P5 ++/* Opcode: Prev P1 P2 P3 * P5 + ** + ** Back up cursor P1 so that it points to the previous key/data pair in its + ** table or index. If there is no previous key/value pairs then fall through +@@ -90856,9 +100731,6 @@ case OP_Rewind: { /* jump */ + ** omitted if that index had been unique. P3 is usually 0. P3 is + ** always either 0 or 1. + ** +-** P4 is always of type P4_ADVANCE. The function pointer points to +-** sqlite3BtreePrevious(). +-** + ** If P5 is positive and the jump is taken, then event counter + ** number P5-1 in the prepared statement is incremented. + */ +@@ -90876,30 +100748,37 @@ case OP_SorterNext: { /* jump */ + assert( isSorter(pC) ); + rc = sqlite3VdbeSorterNext(db, pC); + goto next_tail; +-case OP_Prev: /* jump */ +-case OP_Next: /* jump */ ++ ++case OP_Prev: /* jump, ncycle */ + assert( pOp->p1>=0 && pOp->p1nCursor ); +- assert( pOp->p5aCounter) ); ++ assert( pOp->p5==0 ++ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP ++ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->deferredMoveto==0 ); + assert( pC->eCurType==CURTYPE_BTREE ); +- assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); +- assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); ++ assert( pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE ++ || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope ++ || pC->seekOp==OP_NullRow); ++ rc = sqlite3BtreePrevious(pC->uc.pCursor, pOp->p3); ++ goto next_tail; + +- /* The Next opcode is only used after SeekGT, SeekGE, Rewind, and Found. +- ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */ +- assert( pOp->opcode!=OP_Next +- || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE ++case OP_Next: /* jump, ncycle */ ++ assert( pOp->p1>=0 && pOp->p1nCursor ); ++ assert( pOp->p5==0 ++ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP ++ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX); ++ pC = p->apCsr[pOp->p1]; ++ assert( pC!=0 ); ++ assert( pC->deferredMoveto==0 ); ++ assert( pC->eCurType==CURTYPE_BTREE ); ++ assert( pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE + || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found + || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid + || pC->seekOp==OP_IfNoHope); +- assert( pOp->opcode!=OP_Prev +- || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE +- || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope +- || pC->seekOp==OP_NullRow); ++ rc = sqlite3BtreeNext(pC->uc.pCursor, pOp->p3); + +- rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3); + next_tail: + pC->cacheStatus = CACHE_STALE; + VdbeBranchTaken(rc==SQLITE_OK,2); +@@ -90955,7 +100834,7 @@ case OP_IdxInsert: { /* in2 */ + assert( pC!=0 ); + assert( !isSorter(pC) ); + pIn2 = &aMem[pOp->p2]; +- assert( pIn2->flags & MEM_Blob ); ++ assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) ); + if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->isTable==0 ); +@@ -90966,7 +100845,7 @@ case OP_IdxInsert: { /* in2 */ + x.aMem = aMem + pOp->p3; + x.nMem = (u16)pOp->p4.i; + rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, +- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), ++ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), + ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) + ); + assert( pC->deferredMoveto==0 ); +@@ -91012,7 +100891,8 @@ case OP_SorterInsert: { /* in2 */ + ** an UPDATE or DELETE statement and the index entry to be updated + ** or deleted is not found. For some uses of IdxDelete + ** (example: the EXCEPT operator) it does not matter that no matching +-** entry is found. For those cases, P5 is zero. ++** entry is found. For those cases, P5 is zero. Also, do not raise ++** this (self-correcting and non-critical) error if in writable_schema mode. + */ + case OP_IdxDelete: { + VdbeCursor *pC; +@@ -91033,13 +100913,13 @@ case OP_IdxDelete: { + r.nField = (u16)pOp->p3; + r.default_rc = 0; + r.aMem = &aMem[pOp->p2]; +- rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); ++ rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res); + if( rc ) goto abort_due_to_error; + if( res==0 ){ + rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); + if( rc ) goto abort_due_to_error; +- }else if( pOp->p5 ){ +- rc = SQLITE_CORRUPT_INDEX; ++ }else if( pOp->p5 && !sqlite3WritableSchema(db) ){ ++ rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); + goto abort_due_to_error; + } + assert( pC->deferredMoveto==0 ); +@@ -91076,8 +100956,8 @@ case OP_IdxDelete: { + ** + ** See also: Rowid, MakeRecord. + */ +-case OP_DeferredSeek: +-case OP_IdxRowid: { /* out2 */ ++case OP_DeferredSeek: /* ncycle */ ++case OP_IdxRowid: { /* out2, ncycle */ + VdbeCursor *pC; /* The P1 index cursor */ + VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */ + i64 rowid; /* Rowid that P1 current points to */ +@@ -91085,9 +100965,9 @@ case OP_IdxRowid: { /* out2 */ + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); +- assert( pC->eCurType==CURTYPE_BTREE ); ++ assert( pC->eCurType==CURTYPE_BTREE || IsNullCursor(pC) ); + assert( pC->uc.pCursor!=0 ); +- assert( pC->isTable==0 ); ++ assert( pC->isTable==0 || IsNullCursor(pC) ); + assert( pC->deferredMoveto==0 ); + assert( !pC->nullRow || pOp->opcode==OP_IdxRowid ); + +@@ -91095,10 +100975,10 @@ case OP_IdxRowid: { /* out2 */ + ** of sqlite3VdbeCursorRestore() and sqlite3VdbeIdxRowid(). */ + rc = sqlite3VdbeCursorRestore(pC); + +- /* sqlite3VbeCursorRestore() can only fail if the record has been deleted +- ** out from under the cursor. That will never happens for an IdxRowid +- ** or Seek opcode */ +- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; ++ /* sqlite3VdbeCursorRestore() may fail if the cursor has been disturbed ++ ** since it was last positioned and an error (e.g. OOM or an IO error) ++ ** occurs while trying to reposition it. */ ++ if( rc!=SQLITE_OK ) goto abort_due_to_error; + + if( !pC->nullRow ){ + rowid = 0; /* Not needed. Only used to silence a warning. */ +@@ -91116,8 +100996,11 @@ case OP_IdxRowid: { /* out2 */ + pTabCur->nullRow = 0; + pTabCur->movetoTarget = rowid; + pTabCur->deferredMoveto = 1; ++ pTabCur->cacheStatus = CACHE_STALE; + assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); +- pTabCur->aAltMap = pOp->p4.ai; ++ assert( !pTabCur->isEphemeral ); ++ pTabCur->ub.aAltMap = pOp->p4.ai; ++ assert( !pC->isEphemeral ); + pTabCur->pAltCursor = pC; + }else{ + pOut = out2Prerelease(p, pOp); +@@ -91136,8 +101019,8 @@ case OP_IdxRowid: { /* out2 */ + ** seek operation now, without further delay. If the cursor seek has + ** already occurred, this instruction is a no-op. + */ +-case OP_FinishSeek: { +- VdbeCursor *pC; /* The P1 index cursor */ ++case OP_FinishSeek: { /* ncycle */ ++ VdbeCursor *pC; /* The P1 index cursor */ + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; +@@ -91148,7 +101031,7 @@ case OP_FinishSeek: { + break; + } + +-/* Opcode: IdxGE P1 P2 P3 P4 P5 ++/* Opcode: IdxGE P1 P2 P3 P4 * + ** Synopsis: key=r[P3@P4] + ** + ** The P4 register values beginning with P3 form an unpacked index +@@ -91159,7 +101042,7 @@ case OP_FinishSeek: { + ** If the P1 index entry is greater than or equal to the key value + ** then jump to P2. Otherwise fall through to the next instruction. + */ +-/* Opcode: IdxGT P1 P2 P3 P4 P5 ++/* Opcode: IdxGT P1 P2 P3 P4 * + ** Synopsis: key=r[P3@P4] + ** + ** The P4 register values beginning with P3 form an unpacked index +@@ -91170,7 +101053,7 @@ case OP_FinishSeek: { + ** If the P1 index entry is greater than the key value + ** then jump to P2. Otherwise fall through to the next instruction. + */ +-/* Opcode: IdxLT P1 P2 P3 P4 P5 ++/* Opcode: IdxLT P1 P2 P3 P4 * + ** Synopsis: key=r[P3@P4] + ** + ** The P4 register values beginning with P3 form an unpacked index +@@ -91181,7 +101064,7 @@ case OP_FinishSeek: { + ** If the P1 index entry is less than the key value then jump to P2. + ** Otherwise fall through to the next instruction. + */ +-/* Opcode: IdxLE P1 P2 P3 P4 P5 ++/* Opcode: IdxLE P1 P2 P3 P4 * + ** Synopsis: key=r[P3@P4] + ** + ** The P4 register values beginning with P3 form an unpacked index +@@ -91192,10 +101075,10 @@ case OP_FinishSeek: { + ** If the P1 index entry is less than or equal to the key value then jump + ** to P2. Otherwise fall through to the next instruction. + */ +-case OP_IdxLE: /* jump */ +-case OP_IdxGT: /* jump */ +-case OP_IdxLT: /* jump */ +-case OP_IdxGE: { /* jump */ ++case OP_IdxLE: /* jump, ncycle */ ++case OP_IdxGT: /* jump, ncycle */ ++case OP_IdxLT: /* jump, ncycle */ ++case OP_IdxGE: { /* jump, ncycle */ + VdbeCursor *pC; + int res; + UnpackedRecord r; +@@ -91207,7 +101090,6 @@ case OP_IdxGE: { /* jump */ + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0); + assert( pC->deferredMoveto==0 ); +- assert( pOp->p5==0 || pOp->p5==1 ); + assert( pOp->p4type==P4_INT32 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)pOp->p4.i; +@@ -91228,8 +101110,31 @@ case OP_IdxGE: { /* jump */ + } + } + #endif +- res = 0; /* Not needed. Only used to silence a warning. */ +- rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res); ++ ++ /* Inlined version of sqlite3VdbeIdxKeyCompare() */ ++ { ++ i64 nCellKey = 0; ++ BtCursor *pCur; ++ Mem m; ++ ++ assert( pC->eCurType==CURTYPE_BTREE ); ++ pCur = pC->uc.pCursor; ++ assert( sqlite3BtreeCursorIsValid(pCur) ); ++ nCellKey = sqlite3BtreePayloadSize(pCur); ++ /* nCellKey will always be between 0 and 0xffffffff because of the way ++ ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ ++ if( nCellKey<=0 || nCellKey>0x7fffffff ){ ++ rc = SQLITE_CORRUPT_BKPT; ++ goto abort_due_to_error; ++ } ++ sqlite3VdbeMemInit(&m, db, 0); ++ rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); ++ if( rc ) goto abort_due_to_error; ++ res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, &r, 0); ++ sqlite3VdbeMemReleaseMalloc(&m); ++ } ++ /* End of inlined sqlite3VdbeIdxKeyCompare() */ ++ + assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) ); + if( (pOp->opcode&1)==(OP_IdxLT&1) ){ + assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT ); +@@ -91239,7 +101144,7 @@ case OP_IdxGE: { /* jump */ + res++; + } + VdbeBranchTaken(res>0,2); +- if( rc ) goto abort_due_to_error; ++ assert( rc==SQLITE_OK ); + if( res>0 ) goto jump_to_p2; + break; + } +@@ -91250,7 +101155,7 @@ case OP_IdxGE: { /* jump */ + ** file is given by P1. + ** + ** The table being destroyed is in the main database file if P3==0. If +-** P3==1 then the table to be clear is in the auxiliary database file ++** P3==1 then the table to be destroyed is in the auxiliary database file + ** that is used to store tables create using CREATE TEMPORARY TABLE. + ** + ** If AUTOVACUUM is enabled then it is possible that another root page +@@ -91310,28 +101215,25 @@ case OP_Destroy: { /* out2 */ + ** in the database file is given by P1. But, unlike Destroy, do not + ** remove the table or index from the database file. + ** +-** The table being clear is in the main database file if P2==0. If +-** P2==1 then the table to be clear is in the auxiliary database file ++** The table being cleared is in the main database file if P2==0. If ++** P2==1 then the table to be cleared is in the auxiliary database file + ** that is used to store tables create using CREATE TEMPORARY TABLE. + ** +-** If the P3 value is non-zero, then the table referred to must be an +-** intkey table (an SQL table, not an index). In this case the row change +-** count is incremented by the number of rows in the table being cleared. +-** If P3 is greater than zero, then the value stored in register P3 is +-** also incremented by the number of rows in the table being cleared. ++** If the P3 value is non-zero, then the row change count is incremented ++** by the number of rows in the table being cleared. If P3 is greater ++** than zero, then the value stored in register P3 is also incremented ++** by the number of rows in the table being cleared. + ** + ** See also: Destroy + */ + case OP_Clear: { +- int nChange; ++ i64 nChange; + + sqlite3VdbeIncrWriteCounter(p, 0); + nChange = 0; + assert( p->readOnly==0 ); + assert( DbMaskTest(p->btreeMask, pOp->p2) ); +- rc = sqlite3BtreeClearTable( +- db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) +- ); ++ rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, (u32)pOp->p1, &nChange); + if( pOp->p3 ){ + p->nChange += nChange; + if( pOp->p3>0 ){ +@@ -91397,16 +101299,57 @@ case OP_CreateBtree: { /* out2 */ + break; + } + +-/* Opcode: SqlExec * * * P4 * ++/* Opcode: SqlExec P1 P2 * P4 * + ** + ** Run the SQL statement or statements specified in the P4 string. ++** ++** The P1 parameter is a bitmask of options: ++** ++** 0x0001 Disable Auth and Trace callbacks while the statements ++** in P4 are running. ++** ++** 0x0002 Set db->nAnalysisLimit to P2 while the statements in ++** P4 are running. ++** + */ + case OP_SqlExec: { ++ char *zErr; ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ sqlite3_xauth xAuth; ++#endif ++ u8 mTrace; ++ int savedAnalysisLimit; ++ + sqlite3VdbeIncrWriteCounter(p, 0); + db->nSqlExec++; +- rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0); ++ zErr = 0; ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ xAuth = db->xAuth; ++#endif ++ mTrace = db->mTrace; ++ savedAnalysisLimit = db->nAnalysisLimit; ++ if( pOp->p1 & 0x0001 ){ ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ db->xAuth = 0; ++#endif ++ db->mTrace = 0; ++ } ++ if( pOp->p1 & 0x0002 ){ ++ db->nAnalysisLimit = pOp->p2; ++ } ++ rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr); + db->nSqlExec--; +- if( rc ) goto abort_due_to_error; ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ db->xAuth = xAuth; ++#endif ++ db->mTrace = mTrace; ++ db->nAnalysisLimit = savedAnalysisLimit; ++ if( zErr || rc ){ ++ sqlite3VdbeError(p, "%s", zErr); ++ sqlite3_free(zErr); ++ if( rc==SQLITE_NOMEM ) goto no_mem; ++ goto abort_due_to_error; ++ } + break; + } + +@@ -91437,19 +101380,21 @@ case OP_ParseSchema: { + + iDb = pOp->p1; + assert( iDb>=0 && iDbnDb ); +- assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); ++ assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ++ || db->mallocFailed ++ || (CORRUPT_DB && (db->flags & SQLITE_NoSchemaError)!=0) ); + + #ifndef SQLITE_OMIT_ALTERTABLE + if( pOp->p4.z==0 ){ + sqlite3SchemaClear(db->aDb[iDb].pSchema); + db->mDbFlags &= ~DBFLAG_SchemaKnownOk; +- rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable); ++ rc = sqlite3InitOne(db, iDb, &p->zErrMsg, pOp->p5); + db->mDbFlags |= DBFLAG_SchemaChange; + p->expired = 0; + }else + #endif + { +- zSchema = DFLT_SCHEMA_TABLE; ++ zSchema = LEGACY_SCHEMA_TABLE; + initData.db = db; + initData.iDb = iDb; + initData.pzErrMsg = &p->zErrMsg; +@@ -91550,11 +101495,11 @@ case OP_DropTrigger: { + /* Opcode: IntegrityCk P1 P2 P3 P4 P5 + ** + ** Do an analysis of the currently open database. Store in +-** register P1 the text of an error message describing any problems. +-** If no problems are found, store a NULL in register P1. ++** register (P1+1) the text of an error message describing any problems. ++** If no problems are found, store a NULL in register (P1+1). + ** +-** The register P3 contains one less than the maximum number of allowed errors. +-** At most reg(P3) errors will be reported. ++** The register (P1) contains one less than the maximum number of allowed ++** errors. At most reg(P1) errors will be reported. + ** In other words, the analysis stops as soon as reg(P1) errors are + ** seen. Reg(P1) is updated with the number of errors remaining. + ** +@@ -91574,24 +101519,27 @@ case OP_IntegrityCk: { + Mem *pnErr; /* Register keeping track of errors remaining */ + + assert( p->bIsReader ); ++ assert( pOp->p4type==P4_INTARRAY ); + nRoot = pOp->p2; + aRoot = pOp->p4.ai; + assert( nRoot>0 ); ++ assert( aRoot!=0 ); + assert( aRoot[0]==(Pgno)nRoot ); +- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); +- pnErr = &aMem[pOp->p3]; ++ assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) ); ++ pnErr = &aMem[pOp->p1]; + assert( (pnErr->flags & MEM_Int)!=0 ); + assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); +- pIn1 = &aMem[pOp->p1]; ++ pIn1 = &aMem[pOp->p1+1]; + assert( pOp->p5nDb ); + assert( DbMaskTest(p->btreeMask, pOp->p5) ); +- z = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, +- (int)pnErr->u.i+1, &nErr); ++ rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], ++ &aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z); + sqlite3VdbeMemSetNull(pIn1); + if( nErr==0 ){ + assert( z==0 ); +- }else if( z==0 ){ +- goto no_mem; ++ }else if( rc ){ ++ sqlite3_free(z); ++ goto abort_due_to_error; + }else{ + pnErr->u.i -= nErr-1; + sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free); +@@ -91712,7 +101660,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */ + ** P1 contains the address of the memory cell that contains the first memory + ** cell in an array of values used as arguments to the sub-program. P2 + ** contains the address to jump to if the sub-program throws an IGNORE +-** exception using the RAISE() function. Register P3 contains the address ++** exception using the RAISE() function. P2 might be zero, if there is ++** no possibility that an IGNORE exception will be raised. ++** Register P3 contains the address + ** of a memory cell in this (the parent) VM that is used to allocate the + ** memory required by the sub-vdbe at runtime. + ** +@@ -91720,9 +101670,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */ + ** + ** If P5 is non-zero, then recursive program invocation is enabled. + */ +-case OP_Program: { /* jump */ ++case OP_Program: { /* jump0 */ + int nMem; /* Number of memory registers for sub-program */ +- int nByte; /* Bytes of runtime space required for sub-program */ ++ i64 nByte; /* Bytes of runtime space required for sub-program */ + Mem *pRt; /* Register to allocate runtime space */ + Mem *pMem; /* Used to iterate through memory cells */ + Mem *pEnd; /* Last memory cell in new array */ +@@ -91773,7 +101723,7 @@ case OP_Program: { /* jump */ + nByte = ROUND8(sizeof(VdbeFrame)) + + nMem * sizeof(Mem) + + pProgram->nCsr * sizeof(VdbeCursor*) +- + (pProgram->nOp + 7)/8; ++ + (7 + (i64)pProgram->nOp)/8; + pFrame = sqlite3DbMallocZero(db, nByte); + if( !pFrame ){ + goto no_mem; +@@ -91781,7 +101731,7 @@ case OP_Program: { /* jump */ + sqlite3VdbeMemRelease(pRt); + pRt->flags = MEM_Blob|MEM_Dyn; + pRt->z = (char*)pFrame; +- pRt->n = nByte; ++ pRt->n = (int)nByte; + pRt->xDel = sqlite3VdbeFrameMemDel; + + pFrame->v = p; +@@ -91795,9 +101745,6 @@ case OP_Program: { /* jump */ + pFrame->aOp = p->aOp; + pFrame->nOp = p->nOp; + pFrame->token = pProgram->token; +-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- pFrame->anExec = p->anExec; +-#endif + #ifdef SQLITE_DEBUG + pFrame->iFrameMagic = SQLITE_FRAME_MAGIC; + #endif +@@ -91834,9 +101781,6 @@ case OP_Program: { /* jump */ + memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); + p->aOp = aOp = pProgram->aOp; + p->nOp = pProgram->nOp; +-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +- p->anExec = 0; +-#endif + #ifdef SQLITE_DEBUG + /* Verify that second and subsequent executions of the same trigger do not + ** try to reuse register values from the first use. */ +@@ -91886,12 +101830,14 @@ case OP_Param: { /* out2 */ + ** statement counter is incremented (immediate foreign key constraints). + */ + case OP_FkCounter: { +- if( db->flags & SQLITE_DeferFKs ){ +- db->nDeferredImmCons += pOp->p2; +- }else if( pOp->p1 ){ ++ if( pOp->p1 ){ + db->nDeferredCons += pOp->p2; + }else{ +- p->nFkConstraint += pOp->p2; ++ if( db->flags & SQLITE_DeferFKs ){ ++ db->nDeferredImmCons += pOp->p2; ++ }else{ ++ p->nFkConstraint += pOp->p2; ++ } + } + break; + } +@@ -91976,7 +101922,7 @@ case OP_IfPos: { /* jump, in1 */ + ** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) + ** + ** This opcode performs a commonly used computation associated with +-** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3] ++** LIMIT and OFFSET processing. r[P1] holds the limit counter. r[P3] + ** holds the offset counter. The opcode computes the combined value + ** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2] + ** value computed is the total number of rows that will need to be +@@ -92091,23 +102037,35 @@ case OP_AggInverse: + case OP_AggStep: { + int n; + sqlite3_context *pCtx; ++ u64 nAlloc; + + assert( pOp->p4type==P4_FUNCDEF ); + n = pOp->p5; + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); + assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); +- pCtx = sqlite3DbMallocRawNN(db, n*sizeof(sqlite3_value*) + +- (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlite3_value*))); ++ ++ /* Allocate space for (a) the context object and (n-1) extra pointers ++ ** to append to the sqlite3_context.argv[1] array, and (b) a memory ++ ** cell in which to store the accumulation. Be careful that the memory ++ ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. ++ ** ++ ** Note: We could avoid this by using a regular memory cell from aMem[] for ++ ** the accumulator, instead of allocating one here. */ ++ nAlloc = ROUND8P( SZ_CONTEXT(n) ); ++ pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); + if( pCtx==0 ) goto no_mem; +- pCtx->pMem = 0; +- pCtx->pOut = (Mem*)&(pCtx->argv[n]); ++ pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); ++ assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); ++ + sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null); ++ pCtx->pMem = 0; + pCtx->pFunc = pOp->p4.pFunc; + pCtx->iOp = (int)(pOp - aOp); + pCtx->pVdbe = p; + pCtx->skipFlag = 0; + pCtx->isError = 0; ++ pCtx->enc = encoding; + pCtx->argc = n; + pOp->p4type = P4_FUNCCTX; + pOp->p4.pCtx = pCtx; +@@ -92142,7 +102100,7 @@ case OP_AggStep1: { + /* If this function is inside of a trigger, the register array in aMem[] + ** might change from one evaluation to the next. The next block of code + ** checks to see if the register array has changed, and if so it +- ** reinitializes the relavant parts of the sqlite3_context object */ ++ ** reinitializes the relevant parts of the sqlite3_context object */ + if( pCtx->pMem != pMem ){ + pCtx->pMem = pMem; + for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; +@@ -92237,9 +102195,7 @@ case OP_AggFinal: { + } + sqlite3VdbeChangeEncoding(pMem, encoding); + UPDATE_MAX_BLOBSIZE(pMem); +- if( sqlite3VdbeMemTooBig(pMem) ){ +- goto too_big; +- } ++ REGISTER_TRACE((int)(pMem-aMem), pMem); + break; + } + +@@ -92319,6 +102275,7 @@ case OP_JournalMode: { /* out2 */ + pPager = sqlite3BtreePager(pBt); + eOld = sqlite3PagerGetJournalMode(pPager); + if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; ++ assert( sqlite3BtreeHoldsMutex(pBt) ); + if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; + + #ifndef SQLITE_OMIT_WAL +@@ -92365,7 +102322,7 @@ case OP_JournalMode: { /* out2 */ + /* Open a transaction on the database file. Regardless of the journal + ** mode, this transaction always uses a rollback journal. + */ +- assert( sqlite3BtreeIsInTrans(pBt)==0 ); ++ assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ); + if( rc==SQLITE_OK ){ + rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); + } +@@ -92594,7 +102551,7 @@ case OP_VDestroy: { + ** P1 is a cursor number. This opcode opens a cursor to the virtual + ** table and stores that cursor in P1. + */ +-case OP_VOpen: { ++case OP_VOpen: { /* ncycle */ + VdbeCursor *pCur; + sqlite3_vtab_cursor *pVCur; + sqlite3_vtab *pVtab; +@@ -92617,7 +102574,7 @@ case OP_VOpen: { + pVCur->pVtab = pVtab; + + /* Initialize vdbe cursor object */ +- pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB); ++ pCur = allocateCursor(p, pOp->p1, 0, CURTYPE_VTAB); + if( pCur ){ + pCur->uc.pVCur = pVCur; + pVtab->nRef++; +@@ -92630,6 +102587,80 @@ case OP_VOpen: { + } + #endif /* SQLITE_OMIT_VIRTUALTABLE */ + ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++/* Opcode: VCheck P1 P2 P3 P4 * ++** ++** P4 is a pointer to a Table object that is a virtual table in schema P1 ++** that supports the xIntegrity() method. This opcode runs the xIntegrity() ++** method for that virtual table, using P3 as the integer argument. If ++** an error is reported back, the table name is prepended to the error ++** message and that message is stored in P2. If no errors are seen, ++** register P2 is set to NULL. ++*/ ++case OP_VCheck: { /* out2 */ ++ Table *pTab; ++ sqlite3_vtab *pVtab; ++ const sqlite3_module *pModule; ++ char *zErr = 0; ++ ++ pOut = &aMem[pOp->p2]; ++ sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */ ++ assert( pOp->p4type==P4_TABLEREF ); ++ pTab = pOp->p4.pTab; ++ assert( pTab!=0 ); ++ assert( pTab->nTabRef>0 ); ++ assert( IsVirtual(pTab) ); ++ if( pTab->u.vtab.p==0 ) break; ++ pVtab = pTab->u.vtab.p->pVtab; ++ assert( pVtab!=0 ); ++ pModule = pVtab->pModule; ++ assert( pModule!=0 ); ++ assert( pModule->iVersion>=4 ); ++ assert( pModule->xIntegrity!=0 ); ++ sqlite3VtabLock(pTab->u.vtab.p); ++ assert( pOp->p1>=0 && pOp->p1nDb ); ++ rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName, ++ pOp->p3, &zErr); ++ sqlite3VtabUnlock(pTab->u.vtab.p); ++ if( rc ){ ++ sqlite3_free(zErr); ++ goto abort_due_to_error; ++ } ++ if( zErr ){ ++ sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free); ++ } ++ break; ++} ++#endif /* SQLITE_OMIT_VIRTUALTABLE */ ++ ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++/* Opcode: VInitIn P1 P2 P3 * * ++** Synopsis: r[P2]=ValueList(P1,P3) ++** ++** Set register P2 to be a pointer to a ValueList object for cursor P1 ++** with cache register P3 and output register P3+1. This ValueList object ++** can be used as the first argument to sqlite3_vtab_in_first() and ++** sqlite3_vtab_in_next() to extract all of the values stored in the P1 ++** cursor. Register P3 is used to hold the values returned by ++** sqlite3_vtab_in_first() and sqlite3_vtab_in_next(). ++*/ ++case OP_VInitIn: { /* out2, ncycle */ ++ VdbeCursor *pC; /* The cursor containing the RHS values */ ++ ValueList *pRhs; /* New ValueList object to put in reg[P2] */ ++ ++ pC = p->apCsr[pOp->p1]; ++ pRhs = sqlite3_malloc64( sizeof(*pRhs) ); ++ if( pRhs==0 ) goto no_mem; ++ pRhs->pCsr = pC->uc.pCursor; ++ pRhs->pOut = &aMem[pOp->p3]; ++ pOut = out2Prerelease(p, pOp); ++ pOut->flags = MEM_Null; ++ sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3VdbeValueListFree); ++ break; ++} ++#endif /* SQLITE_OMIT_VIRTUALTABLE */ ++ ++ + #ifndef SQLITE_OMIT_VIRTUALTABLE + /* Opcode: VFilter P1 P2 P3 P4 * + ** Synopsis: iplan=r[P3] zplan='P4' +@@ -92650,7 +102681,7 @@ case OP_VOpen: { + ** + ** A jump is made to P2 if the result set after filtering would be empty. + */ +-case OP_VFilter: { /* jump */ ++case OP_VFilter: { /* jump, ncycle */ + int nArg; + int iQuery; + const sqlite3_module *pModule; +@@ -92668,6 +102699,7 @@ case OP_VFilter: { /* jump */ + pCur = p->apCsr[pOp->p1]; + assert( memIsValid(pQuery) ); + REGISTER_TRACE(pOp->p3, pQuery); ++ assert( pCur!=0 ); + assert( pCur->eCurType==CURTYPE_VTAB ); + pVCur = pCur->uc.pVCur; + pVtab = pVCur->pVtab; +@@ -92679,8 +102711,8 @@ case OP_VFilter: { /* jump */ + iQuery = (int)pQuery->u.i; + + /* Invoke the xFilter method */ +- res = 0; + apArg = p->apArg; ++ assert( nArg<=p->napArg ); + for(i = 0; iapCsr[pOp->p1]; +- assert( pCur->eCurType==CURTYPE_VTAB ); ++ assert( pCur!=0 ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); +@@ -92725,11 +102758,16 @@ case OP_VColumn: { + sqlite3VdbeMemSetNull(pDest); + break; + } ++ assert( pCur->eCurType==CURTYPE_VTAB ); + pVtab = pCur->uc.pVCur->pVtab; + pModule = pVtab->pModule; + assert( pModule->xColumn ); + memset(&sContext, 0, sizeof(sContext)); + sContext.pOut = pDest; ++ sContext.enc = encoding; ++ nullFunc.pUserData = 0; ++ nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE; ++ sContext.pFunc = &nullFunc; + assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); + if( pOp->p5 & OPFLAG_NOCHNG ){ + sqlite3VdbeMemSetNull(pDest); +@@ -92748,9 +102786,6 @@ case OP_VColumn: { + REGISTER_TRACE(pOp->p3, pDest); + UPDATE_MAX_BLOBSIZE(pDest); + +- if( sqlite3VdbeMemTooBig(pDest) ){ +- goto too_big; +- } + if( rc ) goto abort_due_to_error; + break; + } +@@ -92763,14 +102798,14 @@ case OP_VColumn: { + ** jump to instruction P2. Or, if the virtual table has reached + ** the end of its result set, then fall through to the next instruction. + */ +-case OP_VNext: { /* jump */ ++case OP_VNext: { /* jump, ncycle */ + sqlite3_vtab *pVtab; + const sqlite3_module *pModule; + int res; + VdbeCursor *pCur; + +- res = 0; + pCur = p->apCsr[pOp->p1]; ++ assert( pCur!=0 ); + assert( pCur->eCurType==CURTYPE_VTAB ); + if( pCur->nullRow ){ + break; +@@ -92866,7 +102901,7 @@ case OP_VUpdate: { + const sqlite3_module *pModule; + int nArg; + int i; +- sqlite_int64 rowid; ++ sqlite_int64 rowid = 0; + Mem **apArg; + Mem *pX; + +@@ -92888,6 +102923,7 @@ case OP_VUpdate: { + u8 vtabOnConflict = db->vtabOnConflict; + apArg = p->apArg; + pX = &aMem[pOp->p3]; ++ assert( nArg<=p->napArg ); + for(i=0; ip3]; + if( pCtx->pOut != pOut ){ + pCtx->pVdbe = p; + pCtx->pOut = pOut; ++ pCtx->enc = encoding; + for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; + } + assert( pCtx->pVdbe==p ); +@@ -93043,17 +103080,134 @@ case OP_Function: { /* group */ + if( rc ) goto abort_due_to_error; + } + +- /* Copy the result of the function into register P3 */ +- if( pOut->flags & (MEM_Str|MEM_Blob) ){ +- sqlite3VdbeChangeEncoding(pOut, encoding); +- if( sqlite3VdbeMemTooBig(pOut) ) goto too_big; +- } ++ assert( (pOut->flags&MEM_Str)==0 ++ || pOut->enc==encoding ++ || db->mallocFailed ); ++ assert( !sqlite3VdbeMemTooBig(pOut) ); + + REGISTER_TRACE(pOp->p3, pOut); + UPDATE_MAX_BLOBSIZE(pOut); + break; + } + ++/* Opcode: ClrSubtype P1 * * * * ++** Synopsis: r[P1].subtype = 0 ++** ++** Clear the subtype from register P1. ++*/ ++case OP_ClrSubtype: { /* in1 */ ++ pIn1 = &aMem[pOp->p1]; ++ pIn1->flags &= ~MEM_Subtype; ++ break; ++} ++ ++/* Opcode: GetSubtype P1 P2 * * * ++** Synopsis: r[P2] = r[P1].subtype ++** ++** Extract the subtype value from register P1 and write that subtype ++** into register P2. If P1 has no subtype, then P1 gets a NULL. ++*/ ++case OP_GetSubtype: { /* in1 out2 */ ++ pIn1 = &aMem[pOp->p1]; ++ pOut = &aMem[pOp->p2]; ++ if( pIn1->flags & MEM_Subtype ){ ++ sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype); ++ }else{ ++ sqlite3VdbeMemSetNull(pOut); ++ } ++ break; ++} ++ ++/* Opcode: SetSubtype P1 P2 * * * ++** Synopsis: r[P2].subtype = r[P1] ++** ++** Set the subtype value of register P2 to the integer from register P1. ++** If P1 is NULL, clear the subtype from p2. ++*/ ++case OP_SetSubtype: { /* in1 out2 */ ++ pIn1 = &aMem[pOp->p1]; ++ pOut = &aMem[pOp->p2]; ++ if( pIn1->flags & MEM_Null ){ ++ pOut->flags &= ~MEM_Subtype; ++ }else{ ++ assert( pIn1->flags & MEM_Int ); ++ pOut->flags |= MEM_Subtype; ++ pOut->eSubtype = (u8)(pIn1->u.i & 0xff); ++ } ++ break; ++} ++ ++/* Opcode: FilterAdd P1 * P3 P4 * ++** Synopsis: filter(P1) += key(P3@P4) ++** ++** Compute a hash on the P4 registers starting with r[P3] and ++** add that hash to the bloom filter contained in r[P1]. ++*/ ++case OP_FilterAdd: { ++ u64 h; ++ ++ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); ++ pIn1 = &aMem[pOp->p1]; ++ assert( pIn1->flags & MEM_Blob ); ++ assert( pIn1->n>0 ); ++ h = filterHash(aMem, pOp); ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ int ii; ++ for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ ++ registerTrace(ii, &aMem[ii]); ++ } ++ printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); ++ } ++#endif ++ h %= (pIn1->n*8); ++ pIn1->z[h/8] |= 1<<(h&7); ++ break; ++} ++ ++/* Opcode: Filter P1 P2 P3 P4 * ++** Synopsis: if key(P3@P4) not in filter(P1) goto P2 ++** ++** Compute a hash on the key contained in the P4 registers starting ++** with r[P3]. Check to see if that hash is found in the ++** bloom filter hosted by register P1. If it is not present then ++** maybe jump to P2. Otherwise fall through. ++** ++** False negatives are harmless. It is always safe to fall through, ++** even if the value is in the bloom filter. A false negative causes ++** more CPU cycles to be used, but it should still yield the correct ++** answer. However, an incorrect answer may well arise from a ++** false positive - if the jump is taken when it should fall through. ++*/ ++case OP_Filter: { /* jump */ ++ u64 h; ++ ++ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); ++ pIn1 = &aMem[pOp->p1]; ++ assert( (pIn1->flags & MEM_Blob)!=0 ); ++ assert( pIn1->n >= 1 ); ++ h = filterHash(aMem, pOp); ++#ifdef SQLITE_DEBUG ++ if( db->flags&SQLITE_VdbeTrace ){ ++ int ii; ++ for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ ++ registerTrace(ii, &aMem[ii]); ++ } ++ printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); ++ } ++#endif ++ h %= (pIn1->n*8); ++ if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){ ++ VdbeBranchTaken(1, 2); ++ p->aCounter[SQLITE_STMTSTATUS_FILTER_HIT]++; ++ goto jump_to_p2; ++ }else{ ++ p->aCounter[SQLITE_STMTSTATUS_FILTER_MISS]++; ++ VdbeBranchTaken(0, 2); ++ } ++ break; ++} ++ + /* Opcode: Trace P1 P2 * P4 * + ** + ** Write P4 on the statement trace output if statement tracing is +@@ -93080,7 +103234,7 @@ case OP_Function: { /* group */ + ** error is encountered. + */ + case OP_Trace: +-case OP_Init: { /* jump */ ++case OP_Init: { /* jump0 */ + int i; + #ifndef SQLITE_OMIT_TRACE + char *zTrace; +@@ -93102,7 +103256,7 @@ case OP_Init: { /* jump */ + + #ifndef SQLITE_OMIT_TRACE + if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 +- && !p->doingRerun ++ && p->minWriteFileFormat!=254 /* tag-20220401a */ + && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 + ){ + #ifndef SQLITE_OMIT_DEPRECATED +@@ -93241,14 +103395,29 @@ case OP_ReleaseReg: { + + /* Opcode: Noop * * * * * + ** +-** Do nothing. This instruction is often useful as a jump +-** destination. ++** Do nothing. Continue downward to the next opcode. + */ +-/* +-** The magic Explain opcode are only inserted when explain==2 (which +-** is to say when the EXPLAIN QUERY PLAN syntax is used.) +-** This opcode records information from the optimizer. It is the +-** the same as a no-op. This opcodesnever appears in a real VM program. ++/* Opcode: Explain P1 P2 P3 P4 * ++** ++** This is the same as OP_Noop during normal query execution. The ++** purpose of this opcode is to hold information about the query ++** plan for the purpose of EXPLAIN QUERY PLAN output. ++** ++** The P4 value is human-readable text that describes the query plan ++** element. Something like "SCAN t1" or "SEARCH t2 USING INDEX t2x1". ++** ++** The P1 value is the ID of the current element and P2 is the parent ++** element for the case of nested query plan elements. If P2 is zero ++** then this element is a top-level element. ++** ++** For loop elements, P3 is the estimated code of each invocation of this ++** element. ++** ++** As with all opcodes, the meanings of the parameters for OP_Explain ++** are subject to change from one release to the next. Applications ++** should not attempt to interpret or use any of the information ++** contained in the OP_Explain opcode. The information provided by this ++** opcode is intended for testing and debugging use only. + */ + default: { /* This is really OP_Noop, OP_Explain */ + assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); +@@ -93264,11 +103433,13 @@ default: { /* This is really OP_Noop, OP_Explain */ + *****************************************************************************/ + } + +-#ifdef VDBE_PROFILE +- { +- u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); +- if( endTime>start ) pOrigOp->cycles += endTime - start; +- pOrigOp->cnt++; ++#if defined(VDBE_PROFILE) ++ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); ++ pnCycle = 0; ++#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) ++ if( pnCycle ){ ++ *pnCycle += sqlite3Hwtime(); ++ pnCycle = 0; + } + #endif + +@@ -93292,7 +103463,7 @@ default: { /* This is really OP_Noop, OP_Explain */ + } + if( opProperty==0xff ){ + /* Never happens. This code exists to avoid a harmless linkage +- ** warning aboud sqlite3VdbeRegisterDump() being defined but not ++ ** warning about sqlite3VdbeRegisterDump() being defined but not + ** used. */ + sqlite3VdbeRegisterDump(p); + } +@@ -93305,18 +103476,37 @@ default: { /* This is really OP_Noop, OP_Explain */ + ** an error of some kind. + */ + abort_due_to_error: +- if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT; ++ if( db->mallocFailed ){ ++ rc = SQLITE_NOMEM_BKPT; ++ }else if( rc==SQLITE_IOERR_CORRUPTFS ){ ++ rc = SQLITE_CORRUPT_BKPT; ++ } + assert( rc ); ++#ifdef SQLITE_DEBUG ++ if( db->flags & SQLITE_VdbeTrace ){ ++ const char *zTrace = p->zSql; ++ if( zTrace==0 ){ ++ if( aOp[0].opcode==OP_Trace ){ ++ zTrace = aOp[0].p4.z; ++ } ++ if( zTrace==0 ) zTrace = "???"; ++ } ++ printf("ABORT-due-to-error (rc=%d): %s\n", rc, zTrace); ++ } ++#endif + if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ + sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); + } + p->rc = rc; + sqlite3SystemError(db, rc); + testcase( sqlite3GlobalConfig.xLog!=0 ); +- sqlite3_log(rc, "statement aborts at %d: [%s] %s", +- (int)(pOp - aOp), p->zSql, p->zErrMsg); +- sqlite3VdbeHalt(p); ++ sqlite3_log(rc, "statement aborts at %d: %s; [%s]", ++ (int)(pOp - aOp), p->zErrMsg, p->zSql); ++ if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p); + if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db); ++ if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){ ++ db->flags |= SQLITE_CorruptRdOnly; ++ } + rc = SQLITE_ERROR; + if( resetSchemaOnFault>0 ){ + sqlite3ResetOneSchema(db, resetSchemaOnFault-1); +@@ -93326,6 +103516,18 @@ abort_due_to_error: + ** release the mutexes on btrees that were acquired at the + ** top. */ + vdbe_return: ++#if defined(VDBE_PROFILE) ++ if( pnCycle ){ ++ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); ++ pnCycle = 0; ++ } ++#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) ++ if( pnCycle ){ ++ *pnCycle += sqlite3Hwtime(); ++ pnCycle = 0; ++ } ++#endif ++ + #ifndef SQLITE_OMIT_PROGRESS_CALLBACK + while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ + nProgressLimit += db->nProgressOps; +@@ -93337,7 +103539,9 @@ vdbe_return: + } + #endif + p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; +- sqlite3VdbeLeave(p); ++ if( DbMaskNonZero(p->lockMask) ){ ++ sqlite3VdbeLeave(p); ++ } + assert( rc!=SQLITE_OK || nExtraDelete==0 + || sqlite3_strlike("DELETE%",p->zSql,0)!=0 + ); +@@ -93432,8 +103636,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ + /* Set the value of register r[1] in the SQL statement to integer iRow. + ** This is done directly as a performance optimization + */ +- v->aMem[1].flags = MEM_Int; +- v->aMem[1].u.i = iRow; ++ sqlite3VdbeMemSetInt64(&v->aMem[1], iRow); + + /* If the statement has been run before (and is paused at the OP_ResultRow) + ** then back it up to the point where it does the OP_NotExists. This could +@@ -93448,7 +103651,10 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ + } + if( rc==SQLITE_ROW ){ + VdbeCursor *pC = v->apCsr[0]; +- u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; ++ u32 type; ++ assert( pC!=0 ); ++ assert( pC->eCurType==CURTYPE_BTREE ); ++ type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; + testcase( pC->nHdrParsed==p->iCol ); + testcase( pC->nHdrParsed==p->iCol+1 ); + if( type<12 ){ +@@ -93504,6 +103710,7 @@ SQLITE_API int sqlite3_blob_open( + char *zErr = 0; + Table *pTab; + Incrblob *pBlob = 0; ++ int iDb; + Parse sParse; + + #ifdef SQLITE_ENABLE_API_ARMOR +@@ -93513,7 +103720,7 @@ SQLITE_API int sqlite3_blob_open( + #endif + *ppBlob = 0; + #ifdef SQLITE_ENABLE_API_ARMOR +- if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ ++ if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){ + return SQLITE_MISUSE_BKPT; + } + #endif +@@ -93522,10 +103729,9 @@ SQLITE_API int sqlite3_blob_open( + sqlite3_mutex_enter(db->mutex); + + pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); +- do { +- memset(&sParse, 0, sizeof(Parse)); ++ while(1){ ++ sqlite3ParseObjectInit(&sParse,db); + if( !pBlob ) goto blob_open_out; +- sParse.db = db; + sqlite3DbFree(db, zErr); + zErr = 0; + +@@ -93539,13 +103745,21 @@ SQLITE_API int sqlite3_blob_open( + pTab = 0; + sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); + } ++ if( pTab && (pTab->tabFlags&TF_HasGenerated)!=0 ){ ++ pTab = 0; ++ sqlite3ErrorMsg(&sParse, "cannot open table with generated columns: %s", ++ zTable); ++ } + #ifndef SQLITE_OMIT_VIEW +- if( pTab && pTab->pSelect ){ ++ if( pTab && IsView(pTab) ){ + pTab = 0; + sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); + } + #endif +- if( !pTab ){ ++ if( pTab==0 ++ || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 && ++ sqlite3OpenTempDatabase(&sParse)) ++ ){ + if( sParse.zErrMsg ){ + sqlite3DbFree(db, zErr); + zErr = sParse.zErrMsg; +@@ -93556,15 +103770,11 @@ SQLITE_API int sqlite3_blob_open( + goto blob_open_out; + } + pBlob->pTab = pTab; +- pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; ++ pBlob->zDb = db->aDb[iDb].zDbSName; + + /* Now search pTab for the exact column. */ +- for(iCol=0; iColnCol; iCol++) { +- if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ +- break; +- } +- } +- if( iCol==pTab->nCol ){ ++ iCol = sqlite3ColumnIndex(pTab, zColumn); ++ if( iCol<0 ){ + sqlite3DbFree(db, zErr); + zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); + rc = SQLITE_ERROR; +@@ -93585,7 +103795,8 @@ SQLITE_API int sqlite3_blob_open( + ** key columns must be indexed. The check below will pick up this + ** case. */ + FKey *pFKey; +- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ ++ assert( IsOrdinaryTable(pTab) ); ++ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ + int j; + for(j=0; jnCol; j++){ + if( pFKey->aCol[j].iFrom==iCol ){ +@@ -93643,7 +103854,6 @@ SQLITE_API int sqlite3_blob_open( + {OP_Halt, 0, 0, 0}, /* 5 */ + }; + Vdbe *v = (Vdbe *)pBlob->pStmt; +- int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + VdbeOp *aOp; + + sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, +@@ -93701,7 +103911,9 @@ SQLITE_API int sqlite3_blob_open( + goto blob_open_out; + } + rc = blobSeekToRow(pBlob, iRow, &zErr); +- } while( (++nAttempt)=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break; ++ sqlite3ParseObjectReset(&sParse); ++ } + + blob_open_out: + if( rc==SQLITE_OK && db->mallocFailed==0 ){ +@@ -93710,9 +103922,9 @@ blob_open_out: + if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); + sqlite3DbFree(db, pBlob); + } +- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); ++ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); + sqlite3DbFree(db, zErr); +- sqlite3ParserReset(&sParse); ++ sqlite3ParseObjectReset(&sParse); + rc = sqlite3ApiExit(db, rc); + sqlite3_mutex_leave(db->mutex); + return rc; +@@ -93792,8 +104004,10 @@ static int blobReadWrite( + */ + sqlite3_int64 iKey; + iKey = sqlite3BtreeIntegerKey(p->pCsr); ++ assert( v->apCsr[0]!=0 ); ++ assert( v->apCsr[0]->eCurType==CURTYPE_BTREE ); + sqlite3VdbePreUpdateHook( +- v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1 ++ v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol + ); + } + #endif +@@ -93864,9 +104078,10 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ + rc = SQLITE_ABORT; + }else{ + char *zErr; ++ ((Vdbe*)p->pStmt)->rc = SQLITE_OK; + rc = blobSeekToRow(p, iRow, &zErr); + if( rc!=SQLITE_OK ){ +- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); ++ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); + sqlite3DbFree(db, zErr); + } + assert( rc!=SQLITE_SCHEMA ); +@@ -93969,7 +104184,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ + ** The threshold for the amount of main memory to use before flushing + ** records to a PMA is roughly the same as the limit configured for the + ** page-cache of the main database. Specifically, the threshold is set to +-** the value returned by "PRAGMA main.page_size" multipled by ++** the value returned by "PRAGMA main.page_size" multiplied by + ** that returned by "PRAGMA main.cache_size", in bytes. + ** + ** If the sorter is running in single-threaded mode, then all PMAs generated +@@ -93992,7 +104207,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ + ** + ** If there are fewer than SORTER_MAX_MERGE_COUNT PMAs in total and the + ** sorter is running in single-threaded mode, then these PMAs are merged +-** incrementally as keys are retreived from the sorter by the VDBE. The ++** incrementally as keys are retrieved from the sorter by the VDBE. The + ** MergeEngine object, described in further detail below, performs this + ** merge. + ** +@@ -94070,7 +104285,7 @@ struct SorterFile { + struct SorterList { + SorterRecord *pList; /* Linked list of records */ + u8 *aMemory; /* If non-NULL, bulk memory to hold pList */ +- int szPMA; /* Size of pList as PMA in bytes */ ++ i64 szPMA; /* Size of pList as PMA in bytes */ + }; + + /* +@@ -94155,7 +104370,7 @@ struct MergeEngine { + ** + ** Essentially, this structure contains all those fields of the VdbeSorter + ** structure for which each thread requires a separate instance. For example, +-** each thread requries its own UnpackedRecord object to unpack records in ++** each thread requeries its own UnpackedRecord object to unpack records in + ** as part of comparison operations. + ** + ** Before a background thread is launched, variable bDone is set to 0. Then, +@@ -94179,10 +104394,10 @@ typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int); + struct SortSubtask { + SQLiteThread *pThread; /* Background thread, if any */ + int bDone; /* Set if thread is finished but not joined */ ++ int nPMA; /* Number of PMAs currently in file */ + VdbeSorter *pSorter; /* Sorter that owns this sub-task */ + UnpackedRecord *pUnpacked; /* Space to unpack a record */ + SorterList list; /* List for thread to write to a PMA */ +- int nPMA; /* Number of PMAs currently in file */ + SorterCompare xCompare; /* Compare function to use */ + SorterFile file; /* Temp file for level-0 PMAs */ + SorterFile file2; /* Space for other PMAs */ +@@ -94216,9 +104431,12 @@ struct VdbeSorter { + u8 iPrev; /* Previous thread used to flush PMA */ + u8 nTask; /* Size of aTask[] array */ + u8 typeMask; +- SortSubtask aTask[1]; /* One or more subtasks */ ++ SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */ + }; + ++/* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */ ++#define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask)) ++ + #define SORTER_TYPE_INTEGER 0x01 + #define SORTER_TYPE_TEXT 0x02 + +@@ -94227,7 +104445,7 @@ struct VdbeSorter { + ** PMA, in sorted order. The next key to be read is cached in nKey/aKey. + ** aKey might point into aMap or into aBuffer. If neither of those locations + ** contain a contiguous representation of the key, then aAlloc is allocated +-** and the key is copied into aAlloc and aKey is made to poitn to aAlloc. ++** and the key is copied into aAlloc and aKey is made to point to aAlloc. + ** + ** pFd==0 at EOF. + */ +@@ -94440,13 +104658,14 @@ static int vdbePmaReadBlob( + while( nRem>0 ){ + int rc; /* vdbePmaReadBlob() return code */ + int nCopy; /* Number of bytes to copy */ +- u8 *aNext; /* Pointer to buffer to copy data from */ ++ u8 *aNext = 0; /* Pointer to buffer to copy data from */ + + nCopy = nRem; + if( nRem>p->nBuffer ) nCopy = p->nBuffer; + rc = vdbePmaReadBlob(p, nCopy, &aNext); + if( rc!=SQLITE_OK ) return rc; + assert( aNext!=p->aAlloc ); ++ assert( aNext!=0 ); + memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy); + nRem -= nCopy; + } +@@ -94819,7 +105038,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( + VdbeSorter *pSorter; /* The new sorter */ + KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */ + int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */ +- int sz; /* Size of pSorter in bytes */ ++ i64 sz; /* Size of pSorter in bytes */ + int rc = SQLITE_OK; + #if SQLITE_MAX_WORKER_THREADS==0 + # define nWorker 0 +@@ -94844,23 +105063,29 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( + } + #endif + +- assert( pCsr->pKeyInfo && pCsr->pBtx==0 ); ++ assert( pCsr->pKeyInfo ); ++ assert( !pCsr->isEphemeral ); + assert( pCsr->eCurType==CURTYPE_SORTER ); +- szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); +- sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); ++ assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) ++ < 0x7fffffff ); ++ szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField); ++ sz = SZ_VDBESORTER(nWorker+1); + + pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); + pCsr->uc.pSorter = pSorter; + if( pSorter==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ ++ Btree *pBt = db->aDb[0].pBt; + pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); + memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); + pKeyInfo->db = 0; + if( nField && nWorker==0 ){ + pKeyInfo->nKeyField = nField; + } +- pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt); ++ sqlite3BtreeEnter(pBt); ++ pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt); ++ sqlite3BtreeLeave(pBt); + pSorter->nTask = nWorker + 1; + pSorter->iPrev = (u8)(nWorker - 1); + pSorter->bUseThreads = (pSorter->nTask>1); +@@ -94954,8 +105179,9 @@ static void vdbeSorterWorkDebug(SortSubtask *pTask, const char *zEvent){ + fprintf(stderr, "%lld:%d %s\n", t, iTask, zEvent); + } + static void vdbeSorterRewindDebug(const char *zEvent){ +- i64 t; +- sqlite3OsCurrentTimeInt64(sqlite3_vfs_find(0), &t); ++ i64 t = 0; ++ sqlite3_vfs *pVfs = sqlite3_vfs_find(0); ++ if( ALWAYS(pVfs) ) sqlite3OsCurrentTimeInt64(pVfs, &t); + fprintf(stderr, "%lld:X %s\n", t, zEvent); + } + static void vdbeSorterPopulateDebug( +@@ -95055,7 +105281,7 @@ static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){ + */ + static MergeEngine *vdbeMergeEngineNew(int nReader){ + int N = 2; /* Smallest power of two >= nReader */ +- int nByte; /* Total bytes of space to allocate */ ++ i64 nByte; /* Total bytes of space to allocate */ + MergeEngine *pNew; /* Pointer to allocated object to return */ + + assert( nReader<=SORTER_MAX_MERGE_COUNT ); +@@ -95169,7 +105395,7 @@ static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){ + sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); + sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); + sqlite3OsFetch(pFd, 0, (int)nByte, &p); +- sqlite3OsUnfetch(pFd, 0, p); ++ if( p ) sqlite3OsUnfetch(pFd, 0, p); + } + } + #else +@@ -95307,6 +105533,10 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ + p->u.pNext = 0; + for(i=0; aSlot[i]; i++){ + p = vdbeSorterMerge(pTask, p, aSlot[i]); ++ /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use ++ ** | up all 64 aSlots[] with only a 64-bit address space. ++ ** v */ ++ assert( inTask-1) sub-tasks are all still busy, + ** fall back to using the final sub-task. The first (pSorter->nTask-1) +- ** sub-tasks are prefered as they use background threads - the final ++ ** sub-tasks are preferred as they use background threads - the final + ** sub-task uses the main thread. */ + for(i=0; iiPrev + i + 1) % nWorker; +@@ -95651,8 +105881,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite( + int rc = SQLITE_OK; /* Return Code */ + SorterRecord *pNew; /* New list element */ + int bFlush; /* True to flush contents of memory to PMA */ +- int nReq; /* Bytes of memory required */ +- int nPMA; /* Bytes of PMA space required */ ++ i64 nReq; /* Bytes of memory required */ ++ i64 nPMA; /* Bytes of PMA space required */ + int t; /* serial type of first record field */ + + assert( pCsr->eCurType==CURTYPE_SORTER ); +@@ -95887,6 +106117,7 @@ static int vdbeIncrMergerNew( + vdbeMergeEngineFree(pMerger); + rc = SQLITE_NOMEM_BKPT; + } ++ assert( *ppOut!=0 || rc!=SQLITE_OK ); + return rc; + } + +@@ -96076,7 +106307,7 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ + + rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); + +- /* Set up the required files for pIncr. A multi-theaded IncrMerge object ++ /* Set up the required files for pIncr. A multi-threaded IncrMerge object + ** requires two temp files to itself, whereas a single-threaded object + ** only requires a region of pTask->file2. */ + if( rc==SQLITE_OK ){ +@@ -96716,6 +106947,8 @@ static int bytecodevtabConnect( + "p5 INT," + "comment TEXT," + "subprog TEXT," ++ "nexec INT," ++ "ncycle INT," + "stmt HIDDEN" + ");", + +@@ -96730,6 +106963,9 @@ static int bytecodevtabConnect( + ");" + }; + ++ (void)argc; ++ (void)argv; ++ (void)pzErr; + rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]); + if( rc==SQLITE_OK ){ + pNew = sqlite3_malloc( sizeof(*pNew) ); +@@ -96875,7 +107111,7 @@ static int bytecodevtabColumn( + } + } + } +- i += 10; ++ i += 20; + } + } + switch( i ){ +@@ -96925,16 +107161,31 @@ static int bytecodevtabColumn( + } + break; + } +- case 10: /* tables_used.type */ ++ ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ case 9: /* nexec */ ++ sqlite3_result_int64(ctx, pOp->nExec); ++ break; ++ case 10: /* ncycle */ ++ sqlite3_result_int64(ctx, pOp->nCycle); ++ break; ++#else ++ case 9: /* nexec */ ++ case 10: /* ncycle */ ++ sqlite3_result_int(ctx, 0); ++ break; ++#endif ++ ++ case 20: /* tables_used.type */ + sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); + break; +- case 11: /* tables_used.schema */ ++ case 21: /* tables_used.schema */ + sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); + break; +- case 12: /* tables_used.name */ ++ case 22: /* tables_used.name */ + sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); + break; +- case 13: /* tables_used.wr */ ++ case 23: /* tables_used.wr */ + sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); + break; + } +@@ -96965,6 +107216,7 @@ static int bytecodevtabFilter( + bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; + bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; + int rc = SQLITE_OK; ++ (void)idxStr; + + bytecodevtabCursorClear(pCur); + pCur->iRowid = 0; +@@ -97007,7 +107259,7 @@ static int bytecodevtabBestIndex( + int rc = SQLITE_CONSTRAINT; + struct sqlite3_index_constraint *p; + bytecodevtab *pVTab = (bytecodevtab*)tab; +- int iBaseCol = pVTab->bTablesUsed ? 4 : 8; ++ int iBaseCol = pVTab->bTablesUsed ? 4 : 10; + pIdxInfo->estimatedCost = (double)100; + pIdxInfo->estimatedRows = 100; + pIdxInfo->idxNum = 0; +@@ -97054,7 +107306,8 @@ static sqlite3_module bytecodevtabModule = { + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, +- /* xShadowName */ 0 ++ /* xShadowName */ 0, ++ /* xIntegrity */ 0 + }; + + +@@ -97144,7 +107397,6 @@ struct MemJournal { + int nChunkSize; /* In-memory chunk-size */ + + int nSpill; /* Bytes of data before flushing */ +- int nSize; /* Bytes of data currently in memory */ + FileChunk *pFirst; /* Head of in-memory chunk-list */ + FilePoint endpoint; /* Pointer to the end of the file */ + FilePoint readpoint; /* Pointer to the end of the last xRead() */ +@@ -97205,14 +107457,13 @@ static int memjrnlRead( + /* + ** Free the list of FileChunk structures headed at MemJournal.pFirst. + */ +-static void memjrnlFreeChunks(MemJournal *p){ ++static void memjrnlFreeChunks(FileChunk *pFirst){ + FileChunk *pIter; + FileChunk *pNext; +- for(pIter=p->pFirst; pIter; pIter=pNext){ ++ for(pIter=pFirst; pIter; pIter=pNext){ + pNext = pIter->pNext; + sqlite3_free(pIter); + } +- p->pFirst = 0; + } + + /* +@@ -97239,7 +107490,7 @@ static int memjrnlCreateFile(MemJournal *p){ + } + if( rc==SQLITE_OK ){ + /* No error has occurred. Free the in-memory buffers. */ +- memjrnlFreeChunks(©); ++ memjrnlFreeChunks(copy.pFirst); + } + } + if( rc!=SQLITE_OK ){ +@@ -97254,6 +107505,9 @@ static int memjrnlCreateFile(MemJournal *p){ + } + + ++/* Forward reference */ ++static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size); ++ + /* + ** Write data to the file. + */ +@@ -97284,22 +107538,20 @@ static int memjrnlWrite( + ** the in-memory journal is being used by a connection using the + ** atomic-write optimization. In this case the first 28 bytes of the + ** journal file may be written as part of committing the transaction. */ +- assert( iOfst==p->endpoint.iOffset || iOfst==0 ); +-#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ +- || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) ++ assert( iOfst<=p->endpoint.iOffset ); ++ if( iOfst>0 && iOfst!=p->endpoint.iOffset ){ ++ memjrnlTruncate(pJfd, iOfst); ++ } + if( iOfst==0 && p->pFirst ){ + assert( p->nChunkSize>iAmt ); + memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); +- }else +-#else +- assert( iOfst>0 || p->pFirst==0 ); +-#endif +- { ++ }else{ + while( nWrite>0 ){ + FileChunk *pChunk = p->endpoint.pChunk; + int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize); + int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset); + ++ assert( pChunk!=0 || iChunkOffset==0 ); + if( iChunkOffset==0 ){ + /* New chunk is required to extend the file. */ + FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize)); +@@ -97314,15 +107566,15 @@ static int memjrnlWrite( + assert( !p->pFirst ); + p->pFirst = pNew; + } +- p->endpoint.pChunk = pNew; ++ pChunk = p->endpoint.pChunk = pNew; + } + +- memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace); ++ assert( pChunk!=0 ); ++ memcpy((u8*)pChunk->zChunk + iChunkOffset, zWrite, iSpace); + zWrite += iSpace; + nWrite -= iSpace; + p->endpoint.iOffset += iSpace; + } +- p->nSize = iAmt + iOfst; + } + } + +@@ -97330,19 +107582,29 @@ static int memjrnlWrite( + } + + /* +-** Truncate the file. +-** +-** If the journal file is already on disk, truncate it there. Or, if it +-** is still in main memory but is being truncated to zero bytes in size, +-** ignore ++** Truncate the in-memory file. + */ + static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ + MemJournal *p = (MemJournal *)pJfd; +- if( ALWAYS(size==0) ){ +- memjrnlFreeChunks(p); +- p->nSize = 0; +- p->endpoint.pChunk = 0; +- p->endpoint.iOffset = 0; ++ assert( p->endpoint.pChunk==0 || p->endpoint.pChunk->pNext==0 ); ++ if( sizeendpoint.iOffset ){ ++ FileChunk *pIter = 0; ++ if( size==0 ){ ++ memjrnlFreeChunks(p->pFirst); ++ p->pFirst = 0; ++ }else{ ++ i64 iOff = p->nChunkSize; ++ for(pIter=p->pFirst; ALWAYS(pIter) && iOffpNext){ ++ iOff += p->nChunkSize; ++ } ++ if( ALWAYS(pIter) ){ ++ memjrnlFreeChunks(pIter->pNext); ++ pIter->pNext = 0; ++ } ++ } ++ ++ p->endpoint.pChunk = pIter; ++ p->endpoint.iOffset = size; + p->readpoint.pChunk = 0; + p->readpoint.iOffset = 0; + } +@@ -97354,7 +107616,7 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ + */ + static int memjrnlClose(sqlite3_file *pJfd){ + MemJournal *p = (MemJournal *)pJfd; +- memjrnlFreeChunks(p); ++ memjrnlFreeChunks(p->pFirst); + return SQLITE_OK; + } + +@@ -97424,6 +107686,8 @@ SQLITE_PRIVATE int sqlite3JournalOpen( + ){ + MemJournal *p = (MemJournal*)pJfd; + ++ assert( zName || nSpill<0 || (flags & SQLITE_OPEN_EXCLUSIVE) ); ++ + /* Zero the file-handle object. If nSpill was passed zero, initialize + ** it using the sqlite3OsOpen() function of the underlying VFS. In this + ** case none of the code in this module is executed as a result of calls +@@ -97528,7 +107792,7 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){ + ** Walk all expressions linked into the list of Window objects passed + ** as the second argument. + */ +-static int walkWindowList(Walker *pWalker, Window *pList){ ++static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){ + Window *pWin; + for(pWin=pList; pWin; pWin=pWin->pNextWin){ + int rc; +@@ -97538,15 +107802,11 @@ static int walkWindowList(Walker *pWalker, Window *pList){ + if( rc ) return WRC_Abort; + rc = sqlite3WalkExpr(pWalker, pWin->pFilter); + if( rc ) return WRC_Abort; +- +- /* The next two are purely for calls to sqlite3RenameExprUnmap() +- ** within sqlite3WindowOffsetExpr(). Because of constraints imposed +- ** by sqlite3WindowOffsetExpr(), they can never fail. The results do +- ** not matter anyhow. */ + rc = sqlite3WalkExpr(pWalker, pWin->pStart); +- if( NEVER(rc) ) return WRC_Abort; ++ if( rc ) return WRC_Abort; + rc = sqlite3WalkExpr(pWalker, pWin->pEnd); +- if( NEVER(rc) ) return WRC_Abort; ++ if( rc ) return WRC_Abort; ++ if( bOneOnly ) break; + } + return WRC_Continue; + } +@@ -97571,7 +107831,7 @@ static int walkWindowList(Walker *pWalker, Window *pList){ + ** The return value from this routine is WRC_Abort to abandon the tree walk + ** and WRC_Continue to continue. + */ +-static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ ++SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3WalkExprNN(Walker *pWalker, Expr *pExpr){ + int rc; + testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); + testcase( ExprHasProperty(pExpr, EP_Reduced) ); +@@ -97580,12 +107840,14 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ + if( rc ) return rc & WRC_Abort; + if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ + assert( pExpr->x.pList==0 || pExpr->pRight==0 ); +- if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; ++ if( pExpr->pLeft && sqlite3WalkExprNN(pWalker, pExpr->pLeft) ){ ++ return WRC_Abort; ++ } + if( pExpr->pRight ){ + assert( !ExprHasProperty(pExpr, EP_WinFunc) ); + pExpr = pExpr->pRight; + continue; +- }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ }else if( ExprUseXSelect(pExpr) ){ + assert( !ExprHasProperty(pExpr, EP_WinFunc) ); + if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; + }else{ +@@ -97594,7 +107856,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ + } + #ifndef SQLITE_OMIT_WINDOWFUNC + if( ExprHasProperty(pExpr, EP_WinFunc) ){ +- if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort; ++ if( walkWindowList(pWalker, pExpr->y.pWin, 1) ) return WRC_Abort; + } + #endif + } +@@ -97604,7 +107866,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ + return WRC_Continue; + } + SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ +- return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue; ++ return pExpr ? sqlite3WalkExprNN(pWalker,pExpr) : WRC_Continue; + } + + /* +@@ -97622,6 +107884,16 @@ SQLITE_PRIVATE int sqlite3WalkExprList(Walker *pWalker, ExprList *p){ + return WRC_Continue; + } + ++/* ++** This is a no-op callback for Walker->xSelectCallback2. If this ++** callback is set, then the Select->pWinDefn list is traversed. ++*/ ++SQLITE_PRIVATE void sqlite3WalkWinDefnDummyCallback(Walker *pWalker, Select *p){ ++ UNUSED_PARAMETER(pWalker); ++ UNUSED_PARAMETER(p); ++ /* No-op */ ++} ++ + /* + ** Walk all expressions associated with SELECT statement p. Do + ** not invoke the SELECT callback on p, but do (of course) invoke +@@ -97635,13 +107907,18 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ + if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort; + if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort; + if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort; +-#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE) +- { +- Parse *pParse = pWalker->pParse; +- if( pParse && IN_RENAME_OBJECT ){ ++#if !defined(SQLITE_OMIT_WINDOWFUNC) ++ if( p->pWinDefn ){ ++ Parse *pParse; ++ if( pWalker->xSelectCallback2==sqlite3WalkWinDefnDummyCallback ++ || ((pParse = pWalker->pParse)!=0 && IN_RENAME_OBJECT) ++#ifndef SQLITE_OMIT_CTE ++ || pWalker->xSelectCallback2==sqlite3SelectPopWith ++#endif ++ ){ + /* The following may return WRC_Abort if there are unresolvable + ** symbols (e.g. a table that does not exist) in a window definition. */ +- int rc = walkWindowList(pWalker, p->pWinDefn); ++ int rc = walkWindowList(pWalker, p->pWinDefn, 0); + return rc; + } + } +@@ -97659,12 +107936,14 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ + SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ + SrcList *pSrc; + int i; +- struct SrcList_item *pItem; ++ SrcItem *pItem; + + pSrc = p->pSrc; +- if( pSrc ){ ++ if( ALWAYS(pSrc) ){ + for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ +- if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){ ++ if( pItem->fg.isSubquery ++ && sqlite3WalkSelect(pWalker, pItem->u4.pSubq->pSelect) ++ ){ + return WRC_Abort; + } + if( pItem->fg.isTabFunc +@@ -97715,7 +107994,7 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){ + } + + /* Increase the walkerDepth when entering a subquery, and +-** descrease when leaving the subquery. ++** decrease when leaving the subquery. + */ + SQLITE_PRIVATE int sqlite3WalkerDepthIncrease(Walker *pWalker, Select *pSelect){ + UNUSED_PARAMETER(pSelect); +@@ -97825,7 +108104,6 @@ static void resolveAlias( + ExprList *pEList, /* A result set */ + int iCol, /* A column in the result set. 0..pEList->nExpr-1 */ + Expr *pExpr, /* Transform this into an alias to the result set */ +- const char *zType, /* "GROUP" or "ORDER" or "" */ + int nSubquery /* Number of subqueries that the label is moving */ + ){ + Expr *pOrig; /* The iCol-th column of the result set */ +@@ -97835,74 +108113,63 @@ static void resolveAlias( + assert( iCol>=0 && iColnExpr ); + pOrig = pEList->a[iCol].pExpr; + assert( pOrig!=0 ); ++ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); ++ if( pExpr->pAggInfo ) return; + db = pParse->db; + pDup = sqlite3ExprDup(db, pOrig, 0); +- if( pDup!=0 ){ +- if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery); ++ if( db->mallocFailed ){ ++ sqlite3ExprDelete(db, pDup); ++ pDup = 0; ++ }else{ ++ Expr temp; ++ incrAggFunctionDepth(pDup, nSubquery); + if( pExpr->op==TK_COLLATE ){ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); + } +- +- /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This +- ** prevents ExprDelete() from deleting the Expr structure itself, +- ** allowing it to be repopulated by the memcpy() on the following line. +- ** The pExpr->u.zToken might point into memory that will be freed by the +- ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to +- ** make a copy of the token before doing the sqlite3DbFree(). +- */ +- ExprSetProperty(pExpr, EP_Static); +- sqlite3ExprDelete(db, pExpr); +- memcpy(pExpr, pDup, sizeof(*pExpr)); +- if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){ +- assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 ); +- pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken); +- pExpr->flags |= EP_MemToken; +- } ++ memcpy(&temp, pDup, sizeof(Expr)); ++ memcpy(pDup, pExpr, sizeof(Expr)); ++ memcpy(pExpr, &temp, sizeof(Expr)); + if( ExprHasProperty(pExpr, EP_WinFunc) ){ +- if( pExpr->y.pWin!=0 ){ ++ if( ALWAYS(pExpr->y.pWin!=0) ){ + pExpr->y.pWin->pOwner = pExpr; +- }else{ +- assert( db->mallocFailed ); + } + } +- sqlite3DbFree(db, pDup); ++ sqlite3ExprDeferredDelete(pParse, pDup); + } +- ExprSetProperty(pExpr, EP_Alias); + } + +- + /* +-** Return TRUE if the name zCol occurs anywhere in the USING clause. ++** Subqueries store the original database, table and column names for their ++** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN", ++** and mark the expression-list item by setting ExprList.a[].fg.eEName ++** to ENAME_TAB. + ** +-** Return FALSE if the USING clause is NULL or if it does not contain +-** zCol. +-*/ +-static int nameInUsingClause(IdList *pUsing, const char *zCol){ +- if( pUsing ){ +- int k; +- for(k=0; knId; k++){ +- if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; +- } +- } +- return 0; +-} +- +-/* +-** Subqueries stores the original database, table and column names for their +-** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". +-** Check to see if the zSpan given to this routine matches the zDb, zTab, +-** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will +-** match anything. ++** Check to see if the zSpan/eEName of the expression-list item passed to this ++** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are ++** NULL then those fields will match anything. Return true if there is a match, ++** or false otherwise. ++** ++** SF_NestedFrom subqueries also store an entry for the implicit rowid (or ++** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID, ++** and setting zSpan to "DATABASE.TABLE.". This type of pItem ++** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid) ++** is set to 1 if there is this kind of match. + */ + SQLITE_PRIVATE int sqlite3MatchEName( + const struct ExprList_item *pItem, + const char *zCol, + const char *zTab, +- const char *zDb ++ const char *zDb, ++ int *pbRowid + ){ + int n; + const char *zSpan; +- if( pItem->eEName!=ENAME_TAB ) return 0; ++ int eEName = pItem->fg.eEName; ++ if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){ ++ return 0; ++ } ++ assert( pbRowid==0 || *pbRowid==0 ); + zSpan = pItem->zEName; + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} + if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ +@@ -97914,9 +108181,11 @@ SQLITE_PRIVATE int sqlite3MatchEName( + return 0; + } + zSpan += n+1; +- if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ +- return 0; ++ if( zCol ){ ++ if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0; ++ if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0; + } ++ if( eEName==ENAME_ROWID ) *pbRowid = 1; + return 1; + } + +@@ -97946,8 +108215,10 @@ SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){ + Table *pExTab; + + n = pExpr->iColumn; ++ assert( ExprUseYTab(pExpr) ); + pExTab = pExpr->y.pTab; + assert( pExTab!=0 ); ++ assert( n < pExTab->nCol ); + if( (pExTab->tabFlags & TF_HasGenerated)!=0 + && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0 + ){ +@@ -97962,6 +108233,55 @@ SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){ + } + } + ++/* ++** Create a new expression term for the column specified by pMatch and ++** iColumn. Append this new expression term to the FULL JOIN Match set ++** in *ppList. Create a new *ppList if this is the first term in the ++** set. ++*/ ++static void extendFJMatch( ++ Parse *pParse, /* Parsing context */ ++ ExprList **ppList, /* ExprList to extend */ ++ SrcItem *pMatch, /* Source table containing the column */ ++ i16 iColumn /* The column number */ ++){ ++ Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); ++ if( pNew ){ ++ pNew->iTable = pMatch->iCursor; ++ pNew->iColumn = iColumn; ++ pNew->y.pTab = pMatch->pSTab; ++ assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ); ++ ExprSetProperty(pNew, EP_CanBeNull); ++ *ppList = sqlite3ExprListAppend(pParse, *ppList, pNew); ++ } ++} ++ ++/* ++** Return TRUE (non-zero) if zTab is a valid name for the schema table pTab. ++*/ ++static SQLITE_NOINLINE int isValidSchemaTableName( ++ const char *zTab, /* Name as it appears in the SQL */ ++ Table *pTab, /* The schema table we are trying to match */ ++ const char *zDb /* non-NULL if a database qualifier is present */ ++){ ++ const char *zLegacy; ++ assert( pTab!=0 ); ++ assert( pTab->tnum==1 ); ++ if( sqlite3StrNICmp(zTab, "sqlite_", 7)!=0 ) return 0; ++ zLegacy = pTab->zName; ++ if( strcmp(zLegacy+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){ ++ if( sqlite3StrICmp(zTab+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){ ++ return 1; ++ } ++ if( zDb==0 ) return 0; ++ if( sqlite3StrICmp(zTab+7, &LEGACY_SCHEMA_TABLE[7])==0 ) return 1; ++ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1; ++ }else{ ++ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1; ++ } ++ return 0; ++} ++ + /* + ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up + ** that name in the set of source tables in pSrcList and make the pExpr +@@ -97993,25 +108313,27 @@ static int lookupName( + Parse *pParse, /* The parsing context */ + const char *zDb, /* Name of the database containing table, or NULL */ + const char *zTab, /* Name of table containing column, or NULL */ +- const char *zCol, /* Name of the column. */ ++ const Expr *pRight, /* Name of the column. */ + NameContext *pNC, /* The name context used to resolve the name */ + Expr *pExpr /* Make this EXPR node point to the selected column */ + ){ + int i, j; /* Loop counters */ + int cnt = 0; /* Number of matching column names */ +- int cntTab = 0; /* Number of matching table names */ ++ int cntTab = 0; /* Number of potential "rowid" matches */ + int nSubquery = 0; /* How many levels of subquery */ + sqlite3 *db = pParse->db; /* The database connection */ +- struct SrcList_item *pItem; /* Use for looping over pSrcList items */ +- struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ ++ SrcItem *pItem; /* Use for looping over pSrcList items */ ++ SrcItem *pMatch = 0; /* The matching pSrcList item */ + NameContext *pTopNC = pNC; /* First namecontext in the list */ + Schema *pSchema = 0; /* Schema of the expression */ + int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ +- Table *pTab = 0; /* Table hold the row */ +- Column *pCol; /* A column of pTab */ ++ Table *pTab = 0; /* Table holding the row */ ++ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ ++ const char *zCol = pRight->u.zToken; + + assert( pNC ); /* the name context cannot be NULL. */ + assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ ++ assert( zDb==0 || zTab!=0 ); + assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); + + /* Initialize the node to no-match */ +@@ -98056,65 +108378,166 @@ static int lookupName( + + if( pSrcList ){ + for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ +- u8 hCol; +- pTab = pItem->pTab; ++ pTab = pItem->pSTab; + assert( pTab!=0 && pTab->zName!=0 ); +- assert( pTab->nCol>0 ); +- if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ ++ assert( pTab->nCol>0 || pParse->nErr ); ++ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem)); ++ if( pItem->fg.isNestedFrom ){ ++ /* In this case, pItem is a subquery that has been formed from a ++ ** parenthesized subset of the FROM clause terms. Example: ++ ** .... FROM t1 LEFT JOIN (t2 RIGHT JOIN t3 USING(x)) USING(y) ... ++ ** \_________________________/ ++ ** This pItem -------------^ ++ */ + int hit = 0; +- pEList = pItem->pSelect->pEList; ++ Select *pSel; ++ assert( pItem->fg.isSubquery ); ++ assert( pItem->u4.pSubq!=0 ); ++ pSel = pItem->u4.pSubq->pSelect; ++ assert( pSel!=0 ); ++ pEList = pSel->pEList; ++ assert( pEList!=0 ); ++ assert( pEList->nExpr==pTab->nCol ); + for(j=0; jnExpr; j++){ +- if( sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ ++ int bRowid = 0; /* True if possible rowid match */ ++ if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){ ++ continue; ++ } ++ if( bRowid==0 ){ ++ if( cnt>0 ){ ++ if( pItem->fg.isUsing==0 ++ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 ++ ){ ++ /* Two or more tables have the same column name which is ++ ** not joined by USING. This is an error. Signal as much ++ ** by clearing pFJMatch and letting cnt go above 1. */ ++ sqlite3ExprListDelete(db, pFJMatch); ++ pFJMatch = 0; ++ }else ++ if( (pItem->fg.jointype & JT_RIGHT)==0 ){ ++ /* An INNER or LEFT JOIN. Use the left-most table */ ++ continue; ++ }else ++ if( (pItem->fg.jointype & JT_LEFT)==0 ){ ++ /* A RIGHT JOIN. Use the right-most table */ ++ cnt = 0; ++ sqlite3ExprListDelete(db, pFJMatch); ++ pFJMatch = 0; ++ }else{ ++ /* For a FULL JOIN, we must construct a coalesce() func */ ++ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); ++ } ++ } + cnt++; +- cntTab = 2; +- pMatch = pItem; +- pExpr->iColumn = j; + hit = 1; ++ }else if( cnt>0 ){ ++ /* This is a potential rowid match, but there has already been ++ ** a real match found. So this can be ignored. */ ++ continue; + } ++ cntTab++; ++ pMatch = pItem; ++ pExpr->iColumn = j; ++ pEList->a[j].fg.bUsed = 1; ++ ++ /* rowid cannot be part of a USING clause - assert() this. */ ++ assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 ); ++ if( pEList->a[j].fg.bUsingTerm ) break; + } + if( hit || zTab==0 ) continue; + } +- if( zDb && pTab->pSchema!=pSchema ){ +- continue; +- } ++ assert( zDb==0 || zTab!=0 ); + if( zTab ){ +- const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; +- assert( zTabName!=0 ); +- if( sqlite3StrICmp(zTabName, zTab)!=0 ){ +- continue; ++ if( zDb ){ ++ if( pTab->pSchema!=pSchema ) continue; ++ if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue; ++ } ++ if( pItem->zAlias!=0 ){ ++ if( sqlite3StrICmp(zTab, pItem->zAlias)!=0 ){ ++ continue; ++ } ++ }else if( sqlite3StrICmp(zTab, pTab->zName)!=0 ){ ++ if( pTab->tnum!=1 ) continue; ++ if( !isValidSchemaTableName(zTab, pTab, zDb) ) continue; + } ++ assert( ExprUseYTab(pExpr) ); + if( IN_RENAME_OBJECT && pItem->zAlias ){ + sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab); + } + } +- if( 0==(cntTab++) ){ ++ j = sqlite3ColumnIndex(pTab, zCol); ++ if( j>=0 ){ ++ if( cnt>0 ){ ++ if( pItem->fg.isUsing==0 ++ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 ++ ){ ++ /* Two or more tables have the same column name which is ++ ** not joined by USING. This is an error. Signal as much ++ ** by clearing pFJMatch and letting cnt go above 1. */ ++ sqlite3ExprListDelete(db, pFJMatch); ++ pFJMatch = 0; ++ }else ++ if( (pItem->fg.jointype & JT_RIGHT)==0 ){ ++ /* An INNER or LEFT JOIN. Use the left-most table */ ++ continue; ++ }else ++ if( (pItem->fg.jointype & JT_LEFT)==0 ){ ++ /* A RIGHT JOIN. Use the right-most table */ ++ cnt = 0; ++ sqlite3ExprListDelete(db, pFJMatch); ++ pFJMatch = 0; ++ }else{ ++ /* For a FULL JOIN, we must construct a coalesce() func */ ++ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); ++ } ++ } ++ cnt++; + pMatch = pItem; ++ /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ ++ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; ++ if( pItem->fg.isNestedFrom ){ ++ sqlite3SrcItemColumnUsed(pItem, j); ++ } + } +- hCol = sqlite3StrIHash(zCol); +- for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ +- if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ +- /* If there has been exactly one prior match and this match +- ** is for the right-hand table of a NATURAL JOIN or is in a +- ** USING clause, then skip this match. +- */ +- if( cnt==1 ){ +- if( pItem->fg.jointype & JT_NATURAL ) continue; +- if( nameInUsingClause(pItem->pUsing, zCol) ) continue; +- } +- cnt++; ++ if( 0==cnt && VisibleRowid(pTab) ){ ++ /* pTab is a potential ROWID match. Keep track of it and match ++ ** the ROWID later if that seems appropriate. (Search for "cntTab" ++ ** to find related code.) Only allow a ROWID match if there is ++ ** a single ROWID match candidate. ++ */ ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match ++ ** if there is a single VIEW candidate or if there is a single ++ ** non-VIEW candidate plus multiple VIEW candidates. In other ++ ** words non-VIEW candidate terms take precedence over VIEWs. ++ */ ++ if( cntTab==0 ++ || (cntTab==1 ++ && pMatch!=0 ++ && ALWAYS(pMatch->pSTab!=0) ++ && (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0 ++ && (pTab->tabFlags & TF_Ephemeral)==0) ++ ){ ++ cntTab = 1; + pMatch = pItem; +- /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ +- pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; +- break; ++ }else{ ++ cntTab++; + } ++#else ++ /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is ++ ** simpler since we require exactly one candidate, which will ++ ** always be a non-VIEW ++ */ ++ cntTab++; ++ pMatch = pItem; ++#endif + } + } + if( pMatch ){ + pExpr->iTable = pMatch->iCursor; +- pExpr->y.pTab = pMatch->pTab; +- /* RIGHT JOIN not (yet) supported */ +- assert( (pMatch->fg.jointype & JT_RIGHT)==0 ); +- if( (pMatch->fg.jointype & JT_LEFT)!=0 ){ ++ assert( ExprUseYTab(pExpr) ); ++ pExpr->y.pTab = pMatch->pSTab; ++ if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){ + ExprSetProperty(pExpr, EP_CanBeNull); + } + pSchema = pExpr->y.pTab->pSchema; +@@ -98124,28 +108547,38 @@ static int lookupName( + #if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) + /* If we have not already resolved the name, then maybe + ** it is a new.* or old.* trigger argument reference. Or +- ** maybe it is an excluded.* from an upsert. ++ ** maybe it is an excluded.* from an upsert. Or maybe it is ++ ** a reference in the RETURNING clause to a table being modified. + */ +- if( zDb==0 && zTab!=0 && cntTab==0 ){ ++ if( cnt==0 && zDb==0 ){ + pTab = 0; + #ifndef SQLITE_OMIT_TRIGGER + if( pParse->pTriggerTab!=0 ){ + int op = pParse->eTriggerOp; + assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); +- if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ ++ if( pParse->bReturning ){ ++ if( (pNC->ncFlags & NC_UBaseReg)!=0 ++ && ALWAYS(zTab==0 ++ || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0 ++ || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0)) ++ ){ ++ pExpr->iTable = op!=TK_DELETE; ++ pTab = pParse->pTriggerTab; ++ } ++ }else if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){ + pExpr->iTable = 1; + pTab = pParse->pTriggerTab; +- }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ ++ }else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){ + pExpr->iTable = 0; + pTab = pParse->pTriggerTab; + } + } + #endif /* SQLITE_OMIT_TRIGGER */ + #ifndef SQLITE_OMIT_UPSERT +- if( (pNC->ncFlags & NC_UUpsert)!=0 ){ ++ if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){ + Upsert *pUpsert = pNC->uNC.pUpsert; + if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){ +- pTab = pUpsert->pUpsertSrc->a[0].pTab; ++ pTab = pUpsert->pUpsertSrc->a[0].pSTab; + pExpr->iTable = EXCLUDED_TABLE_NUMBER; + } + } +@@ -98153,26 +108586,25 @@ static int lookupName( + + if( pTab ){ + int iCol; +- u8 hCol = sqlite3StrIHash(zCol); + pSchema = pTab->pSchema; + cntTab++; +- for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ +- if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ +- if( iCol==pTab->iPKey ){ +- iCol = -1; +- } +- break; ++ iCol = sqlite3ColumnIndex(pTab, zCol); ++ if( iCol>=0 ){ ++ if( pTab->iPKey==iCol ) iCol = -1; ++ }else{ ++ if( sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){ ++ iCol = -1; ++ }else{ ++ iCol = pTab->nCol; + } + } +- if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){ +- /* IMP: R-51414-32910 */ +- iCol = -1; +- } + if( iColnCol ){ + cnt++; ++ pMatch = 0; + #ifndef SQLITE_OMIT_UPSERT + if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){ + testcase( iCol==(-1) ); ++ assert( ExprUseYTab(pExpr) ); + if( IN_RENAME_OBJECT ){ + pExpr->iColumn = iCol; + pExpr->y.pTab = pTab; +@@ -98181,27 +108613,35 @@ static int lookupName( + pExpr->iTable = pNC->uNC.pUpsert->regData + + sqlite3TableColumnToStorage(pTab, iCol); + eNewExprOp = TK_REGISTER; +- ExprSetProperty(pExpr, EP_Alias); + } + }else + #endif /* SQLITE_OMIT_UPSERT */ + { +-#ifndef SQLITE_OMIT_TRIGGER +- if( iCol<0 ){ +- pExpr->affExpr = SQLITE_AFF_INTEGER; +- }else if( pExpr->iTable==0 ){ +- testcase( iCol==31 ); +- testcase( iCol==32 ); +- pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<y.pTab = pTab; +- pExpr->iColumn = (i16)iCol; +- eNewExprOp = TK_TRIGGER; ++ if( pParse->bReturning ){ ++ eNewExprOp = TK_REGISTER; ++ pExpr->op2 = TK_COLUMN; ++ pExpr->iColumn = iCol; ++ pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable + ++ sqlite3TableColumnToStorage(pTab, iCol) + 1; ++ }else{ ++ pExpr->iColumn = (i16)iCol; ++ eNewExprOp = TK_TRIGGER; ++#ifndef SQLITE_OMIT_TRIGGER ++ if( iCol<0 ){ ++ pExpr->affExpr = SQLITE_AFF_INTEGER; ++ }else if( pExpr->iTable==0 ){ ++ testcase( iCol==31 ); ++ testcase( iCol==32 ); ++ pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<=1 + && pMatch + && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 + && sqlite3IsRowid(zCol) +- && VisibleRowid(pMatch->pTab) ++ && ALWAYS(VisibleRowid(pMatch->pSTab) || pMatch->fg.isNestedFrom) + ){ +- cnt = 1; +- pExpr->iColumn = -1; ++ cnt = cntTab; ++#if SQLITE_ALLOW_ROWID_IN_VIEW+0==2 ++ if( pMatch->pSTab!=0 && IsView(pMatch->pSTab) ){ ++ eNewExprOp = TK_NULL; ++ } ++#endif ++ if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; + pExpr->affExpr = SQLITE_AFF_INTEGER; + } + +@@ -98241,21 +108686,21 @@ static int lookupName( + ** is supported for backwards compatibility only. Hence, we issue a warning + ** on sqlite3_log() whenever the capability is used. + */ +- if( (pNC->ncFlags & NC_UEList)!=0 +- && cnt==0 ++ if( cnt==0 ++ && (pNC->ncFlags & NC_UEList)!=0 + && zTab==0 + ){ + pEList = pNC->uNC.pEList; + assert( pEList!=0 ); + for(j=0; jnExpr; j++){ + char *zAs = pEList->a[j].zEName; +- if( pEList->a[j].eEName==ENAME_NAME ++ if( pEList->a[j].fg.eEName==ENAME_NAME + && sqlite3_stricmp(zAs, zCol)==0 + ){ + Expr *pOrig; + assert( pExpr->pLeft==0 && pExpr->pRight==0 ); +- assert( pExpr->x.pList==0 ); +- assert( pExpr->x.pSelect==0 ); ++ assert( ExprUseXList(pExpr)==0 || pExpr->x.pList==0 ); ++ assert( ExprUseXSelect(pExpr)==0 || pExpr->x.pSelect==0 ); + pOrig = pEList->a[j].pExpr; + if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ + sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); +@@ -98271,7 +108716,7 @@ static int lookupName( + sqlite3ErrorMsg(pParse, "row value misused"); + return WRC_Abort; + } +- resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); ++ resolveAlias(pParse, pEList, j, pExpr, nSubquery); + cnt = 1; + pMatch = 0; + assert( zTab==0 && zDb==0 ); +@@ -98327,7 +108772,7 @@ static int lookupName( + sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); + #endif + pExpr->op = TK_STRING; +- pExpr->y.pTab = 0; ++ memset(&pExpr->y, 0, sizeof(pExpr->y)); + return WRC_Prune; + } + if( sqlite3ExprIdToTrueFalse(pExpr) ){ +@@ -98336,21 +108781,63 @@ static int lookupName( + } + + /* +- ** cnt==0 means there was not match. cnt>1 means there were two or +- ** more matches. Either way, we have an error. ++ ** cnt==0 means there was not match. ++ ** cnt>1 means there were two or more matches. ++ ** ++ ** cnt==0 is always an error. cnt>1 is often an error, but might ++ ** be multiple matches for a NATURAL LEFT JOIN or a LEFT JOIN USING. + */ ++ assert( pFJMatch==0 || cnt>0 ); ++ assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); + if( cnt!=1 ){ + const char *zErr; ++ if( pFJMatch ){ ++ if( pFJMatch->nExpr==cnt-1 ){ ++ if( ExprHasProperty(pExpr,EP_Leaf) ){ ++ ExprClearProperty(pExpr,EP_Leaf); ++ }else{ ++ sqlite3ExprDelete(db, pExpr->pLeft); ++ pExpr->pLeft = 0; ++ sqlite3ExprDelete(db, pExpr->pRight); ++ pExpr->pRight = 0; ++ } ++ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); ++ pExpr->op = TK_FUNCTION; ++ pExpr->u.zToken = "coalesce"; ++ pExpr->x.pList = pFJMatch; ++ cnt = 1; ++ goto lookupname_end; ++ }else{ ++ sqlite3ExprListDelete(db, pFJMatch); ++ pFJMatch = 0; ++ } ++ } + zErr = cnt==0 ? "no such column" : "ambiguous column name"; + if( zDb ){ + sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); + }else if( zTab ){ + sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); ++ }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){ ++ sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a" ++ " string literal in single-quotes?", ++ zErr, zCol); + }else{ + sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); + } ++ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); + pParse->checkSchema = 1; +- pTopNC->nErr++; ++ pTopNC->nNcErr++; ++ eNewExprOp = TK_NULL; ++ } ++ assert( pFJMatch==0 ); ++ ++ /* Remove all substructure from pExpr */ ++ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ ++ sqlite3ExprDelete(db, pExpr->pLeft); ++ pExpr->pLeft = 0; ++ sqlite3ExprDelete(db, pExpr->pRight); ++ pExpr->pRight = 0; ++ ExprSetProperty(pExpr, EP_Leaf); + } + + /* If a column from a table in pSrcList is referenced, then record +@@ -98367,24 +108854,25 @@ static int lookupName( + ** If a generated column is referenced, set bits for every column + ** of the table. + */ +- if( pExpr->iColumn>=0 && pMatch!=0 ){ +- pMatch->colUsed |= sqlite3ExprColUsed(pExpr); ++ if( pMatch ){ ++ if( pExpr->iColumn>=0 ){ ++ pMatch->colUsed |= sqlite3ExprColUsed(pExpr); ++ }else{ ++ pMatch->fg.rowidUsed = 1; ++ } + } + +- /* Clean up and return +- */ +- sqlite3ExprDelete(db, pExpr->pLeft); +- pExpr->pLeft = 0; +- sqlite3ExprDelete(db, pExpr->pRight); +- pExpr->pRight = 0; + pExpr->op = eNewExprOp; +- ExprSetProperty(pExpr, EP_Leaf); + lookupname_end: + if( cnt==1 ){ + assert( pNC!=0 ); +- if( !ExprHasProperty(pExpr, EP_Alias) ){ ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ if( pParse->db->xAuth ++ && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER) ++ ){ + sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); + } ++#endif + /* Increment the nRef value on all name contexts from TopNC up to + ** the point where the name matched. */ + for(;;){ +@@ -98406,8 +108894,10 @@ lookupname_end: + SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ + Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); + if( p ){ +- struct SrcList_item *pItem = &pSrc->a[iSrc]; +- Table *pTab = p->y.pTab = pItem->pTab; ++ SrcItem *pItem = &pSrc->a[iSrc]; ++ Table *pTab; ++ assert( ExprUseYTab(p) ); ++ pTab = p->y.pTab = pItem->pSTab; + p->iTable = pItem->iCursor; + if( p->y.pTab->iPKey==iCol ){ + p->iColumn = -1; +@@ -98449,7 +108939,8 @@ static void notValidImpl( + Parse *pParse, /* Leave error message here */ + NameContext *pNC, /* The name context */ + const char *zMsg, /* Type of error */ +- Expr *pExpr /* Invalidate this expression on error */ ++ Expr *pExpr, /* Invalidate this expression on error */ ++ Expr *pError /* Associate error with this expression */ + ){ + const char *zIn = "partial index WHERE clauses"; + if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions"; +@@ -98461,10 +108952,11 @@ static void notValidImpl( + #endif + sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn); + if( pExpr ) pExpr->op = TK_NULL; ++ sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); + } +-#define sqlite3ResolveNotValid(P,N,M,X,E) \ ++#define sqlite3ResolveNotValid(P,N,M,X,E,R) \ + assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \ +- if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E); ++ if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R); + + /* + ** Expression p should encode a floating point value between 1.0 and 0.0. +@@ -98474,6 +108966,7 @@ static void notValidImpl( + static int exprProbability(Expr *p){ + double r = -1.0; + if( p->op!=TK_FLOAT ) return -1; ++ assert( !ExprHasProperty(p, EP_IntValue) ); + sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8); + assert( r>=0.0 ); + if( r>1.0 ) return -1; +@@ -98518,17 +109011,87 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + */ + case TK_ROW: { + SrcList *pSrcList = pNC->pSrcList; +- struct SrcList_item *pItem; ++ SrcItem *pItem; + assert( pSrcList && pSrcList->nSrc>=1 ); + pItem = pSrcList->a; + pExpr->op = TK_COLUMN; +- pExpr->y.pTab = pItem->pTab; ++ assert( ExprUseYTab(pExpr) ); ++ pExpr->y.pTab = pItem->pSTab; + pExpr->iTable = pItem->iCursor; + pExpr->iColumn--; + pExpr->affExpr = SQLITE_AFF_INTEGER; + break; + } + ++ /* An optimization: Attempt to convert ++ ** ++ ** "expr IS NOT NULL" --> "TRUE" ++ ** "expr IS NULL" --> "FALSE" ++ ** ++ ** if we can prove that "expr" is never NULL. Call this the ++ ** "NOT NULL strength reduction optimization". ++ ** ++ ** If this optimization occurs, also restore the NameContext ref-counts ++ ** to the state they where in before the "column" LHS expression was ++ ** resolved. This prevents "column" from being counted as having been ++ ** referenced, which might prevent a SELECT from being erroneously ++ ** marked as correlated. ++ ** ++ ** 2024-03-28: Beware of aggregates. A bare column of aggregated table ++ ** can still evaluate to NULL even though it is marked as NOT NULL. ++ ** Example: ++ ** ++ ** CREATE TABLE t1(a INT NOT NULL); ++ ** SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1; ++ ** ++ ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized ++ ** here because at the time this case is hit, we do not yet know whether ++ ** or not t1 is being aggregated. We have to assume the worst and omit ++ ** the optimization. The only time it is safe to apply this optimization ++ ** is within the WHERE clause. ++ */ ++ case TK_NOTNULL: ++ case TK_ISNULL: { ++ int anRef[8]; ++ NameContext *p; ++ int i; ++ for(i=0, p=pNC; p && ipNext, i++){ ++ anRef[i] = p->nRef; ++ } ++ sqlite3WalkExpr(pWalker, pExpr->pLeft); ++ if( IN_RENAME_OBJECT ) return WRC_Prune; ++ if( sqlite3ExprCanBeNull(pExpr->pLeft) ){ ++ /* The expression can be NULL. So the optimization does not apply */ ++ return WRC_Prune; ++ } ++ ++ for(i=0, p=pNC; p; p=p->pNext, i++){ ++ if( (p->ncFlags & NC_Where)==0 ){ ++ return WRC_Prune; /* Not in a WHERE clause. Unsafe to optimize. */ ++ } ++ } ++ testcase( ExprHasProperty(pExpr, EP_OuterON) ); ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x80000 ){ ++ sqlite3DebugPrintf( ++ "NOT NULL strength reduction converts the following to %d:\n", ++ pExpr->op==TK_NOTNULL ++ ); ++ sqlite3ShowExpr(pExpr); ++ } ++#endif /* TREETRACE_ENABLED */ ++ pExpr->u.iValue = (pExpr->op==TK_NOTNULL); ++ pExpr->flags |= EP_IntValue; ++ pExpr->op = TK_INTEGER; ++ for(i=0, p=pNC; p && ipNext, i++){ ++ p->nRef = anRef[i]; ++ } ++ sqlite3ExprDelete(pParse->db, pExpr->pLeft); ++ pExpr->pLeft = 0; ++ return WRC_Prune; ++ } ++ + /* A column name: ID + ** Or table name and column name: ID.ID + ** Or a database, table and column: ID.ID.ID +@@ -98539,7 +109102,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + */ + case TK_ID: + case TK_DOT: { +- const char *zColumn; + const char *zTable; + const char *zDb; + Expr *pRight; +@@ -98547,41 +109109,43 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_ID ){ + zDb = 0; + zTable = 0; +- zColumn = pExpr->u.zToken; ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ pRight = pExpr; + }else{ + Expr *pLeft = pExpr->pLeft; + testcase( pNC->ncFlags & NC_IdxExpr ); + testcase( pNC->ncFlags & NC_GenCol ); + sqlite3ResolveNotValid(pParse, pNC, "the \".\" operator", +- NC_IdxExpr|NC_GenCol, 0); ++ NC_IdxExpr|NC_GenCol, 0, pExpr); + pRight = pExpr->pRight; + if( pRight->op==TK_ID ){ + zDb = 0; + }else{ + assert( pRight->op==TK_DOT ); ++ assert( !ExprHasProperty(pRight, EP_IntValue) ); + zDb = pLeft->u.zToken; + pLeft = pRight->pLeft; + pRight = pRight->pRight; + } ++ assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); + zTable = pLeft->u.zToken; +- zColumn = pRight->u.zToken; ++ assert( ExprUseYTab(pExpr) ); + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); + sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); + } + } +- return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); ++ return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr); + } + + /* Resolve function names + */ + case TK_FUNCTION: { +- ExprList *pList = pExpr->x.pList; /* The argument list */ +- int n = pList ? pList->nExpr : 0; /* Number of arguments */ ++ ExprList *pList; /* The argument list */ ++ int n; /* Number of arguments */ + int no_such_func = 0; /* True if no such function exists */ + int wrong_num_args = 0; /* True if wrong number of arguments */ + int is_agg = 0; /* True if is an aggregate function */ +- int nId; /* Number of characters in function name */ + const char *zId; /* The function name. */ + FuncDef *pDef; /* Information about the function */ + u8 enc = ENC(pParse->db); /* The database encoding */ +@@ -98589,9 +109153,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + #ifndef SQLITE_OMIT_WINDOWFUNC + Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); + #endif +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); ++ assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); ++ assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER ); ++ pList = pExpr->x.pList; ++ n = pList ? pList->nExpr : 0; + zId = pExpr->u.zToken; +- nId = sqlite3Strlen30(zId); + pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); + if( pDef==0 ){ + pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0); +@@ -98608,9 +109174,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + pExpr->iTable = exprProbability(pList->a[1].pExpr); + if( pExpr->iTable<0 ){ + sqlite3ErrorMsg(pParse, +- "second argument to likelihood() must be a " +- "constant between 0.0 and 1.0"); +- pNC->nErr++; ++ "second argument to %#T() must be a " ++ "constant between 0.0 and 1.0", pExpr); ++ pNC->nNcErr++; + } + }else{ + /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is +@@ -98630,15 +109196,33 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + int auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0); + if( auth!=SQLITE_OK ){ + if( auth==SQLITE_DENY ){ +- sqlite3ErrorMsg(pParse, "not authorized to use function: %s", +- pDef->zName); +- pNC->nErr++; ++ sqlite3ErrorMsg(pParse, "not authorized to use function: %#T", ++ pExpr); ++ pNC->nNcErr++; + } + pExpr->op = TK_NULL; + return WRC_Prune; + } + } + #endif ++ ++ /* If the function may call sqlite3_value_subtype(), then set the ++ ** EP_SubtArg flag on all of its argument expressions. This prevents ++ ** where.c from replacing the expression with a value read from an ++ ** index on the same expression, which will not have the correct ++ ** subtype. Also set the flag if the function expression itself is ++ ** an EP_SubtArg expression. In this case subtypes are required as ++ ** the function may return a value with a subtype back to its ++ ** caller using sqlite3_result_value(). */ ++ if( (pDef->funcFlags & SQLITE_SUBTYPE) ++ || ExprHasProperty(pExpr, EP_SubtArg) ++ ){ ++ int ii; ++ for(ii=0; iia[ii].pExpr, EP_SubtArg); ++ } ++ } ++ + if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){ + /* For the purposes of the EP_ConstFunc flag, date and time + ** functions and other functions that change slowly are considered +@@ -98652,13 +109236,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + ** sqlite_version() that might change over time cannot be used + ** in an index or generated column. Curiously, they can be used + ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all +- ** all this. */ ++ ** allow this. */ + sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions", +- NC_IdxExpr|NC_PartIdx|NC_GenCol, 0); ++ NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr); + }else{ + assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */ + pExpr->op2 = pNC->ncFlags & NC_SelfRef; +- if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL); + } + if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0 + && pParse->nested==0 +@@ -98667,13 +109250,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + /* Internal-use-only functions are disallowed unless the + ** SQL is being compiled using sqlite3NestedParse() or + ** the SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test-control has be +- ** used to activate internal functionsn for testing purposes */ ++ ** used to activate internal functions for testing purposes */ + no_such_func = 1; + pDef = 0; + }else + if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 + && !IN_RENAME_OBJECT + ){ ++ if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL); + sqlite3ExprFunctionUsable(pParse, pExpr, pDef); + } + } +@@ -98686,9 +109270,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + ); + if( pDef && pDef->xValue==0 && pWin ){ + sqlite3ErrorMsg(pParse, +- "%.*s() may not be used as a window function", nId, zId ++ "%#T() may not be used as a window function", pExpr + ); +- pNC->nErr++; ++ pNC->nNcErr++; + }else if( + (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) + || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin) +@@ -98700,14 +109284,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + }else{ + zType = "aggregate"; + } +- sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId); +- pNC->nErr++; ++ sqlite3ErrorMsg(pParse, "misuse of %s function %#T()",zType,pExpr); ++ pNC->nNcErr++; + is_agg = 0; + } + #else + if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){ +- sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId); +- pNC->nErr++; ++ sqlite3ErrorMsg(pParse,"misuse of aggregate function %#T()",pExpr); ++ pNC->nNcErr++; + is_agg = 0; + } + #endif +@@ -98716,22 +109300,26 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + && pParse->explain==0 + #endif + ){ +- sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); +- pNC->nErr++; ++ sqlite3ErrorMsg(pParse, "no such function: %#T", pExpr); ++ pNC->nNcErr++; + }else if( wrong_num_args ){ +- sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", +- nId, zId); +- pNC->nErr++; ++ sqlite3ErrorMsg(pParse,"wrong number of arguments to function %#T()", ++ pExpr); ++ pNC->nNcErr++; + } + #ifndef SQLITE_OMIT_WINDOWFUNC + else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){ + sqlite3ErrorMsg(pParse, +- "FILTER may not be used with non-aggregate %.*s()", +- nId, zId ++ "FILTER may not be used with non-aggregate %#T()", ++ pExpr + ); +- pNC->nErr++; ++ pNC->nNcErr++; + } + #endif ++ else if( is_agg==0 && pExpr->pLeft ){ ++ sqlite3ExprOrderByAggregateError(pParse, pExpr); ++ pNC->nNcErr++; ++ } + if( is_agg ){ + /* Window functions may not be arguments of aggregate functions. + ** Or arguments of other window functions. But aggregate functions +@@ -98743,19 +109331,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + #endif + } + } +-#ifndef SQLITE_OMIT_WINDOWFUNC +- else if( ExprHasProperty(pExpr, EP_WinFunc) ){ ++ else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){ + is_agg = 1; + } +-#endif + sqlite3WalkExprList(pWalker, pList); + if( is_agg ){ ++ if( pExpr->pLeft ){ ++ assert( pExpr->pLeft->op==TK_ORDER ); ++ assert( ExprUseXList(pExpr->pLeft) ); ++ sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); ++ } + #ifndef SQLITE_OMIT_WINDOWFUNC +- if( pWin ){ ++ if( pWin && pParse->nErr==0 ){ + Select *pSel = pNC->pWinSelect; +- assert( pWin==pExpr->y.pWin ); ++ assert( ExprUseYWin(pExpr) && pWin==pExpr->y.pWin ); + if( IN_RENAME_OBJECT==0 ){ + sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef); ++ if( pParse->db->mallocFailed ) break; + } + sqlite3WalkExprList(pWalker, pWin->pPartition); + sqlite3WalkExprList(pWalker, pWin->pOrderBy); +@@ -98765,7 +109357,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + }else + #endif /* SQLITE_OMIT_WINDOWFUNC */ + { +- NameContext *pNC2 = pNC; ++ NameContext *pNC2; /* For looping up thru outer contexts */ + pExpr->op = TK_AGG_FUNCTION; + pExpr->op2 = 0; + #ifndef SQLITE_OMIT_WINDOWFUNC +@@ -98773,16 +109365,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); + } + #endif +- while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ +- pExpr->op2++; ++ pNC2 = pNC; ++ while( pNC2 ++ && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 ++ ){ ++ pExpr->op2 += (1 + pNC2->nNestedSelect); + pNC2 = pNC2->pNext; + } + assert( pDef!=0 || IN_RENAME_OBJECT ); + if( pNC2 && pDef ){ ++ pExpr->op2 += pNC2->nNestedSelect; + assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); ++ assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg ); + testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); +- pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); +- ++ testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 ); ++ pNC2->ncFlags |= NC_HasAgg ++ | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER) ++ & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER)); + } + } + pNC->ncFlags |= savedAllowFlags; +@@ -98798,20 +109397,24 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + #endif + case TK_IN: { + testcase( pExpr->op==TK_IN ); +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + int nRef = pNC->nRef; + testcase( pNC->ncFlags & NC_IsCheck ); + testcase( pNC->ncFlags & NC_PartIdx ); + testcase( pNC->ncFlags & NC_IdxExpr ); + testcase( pNC->ncFlags & NC_GenCol ); +- sqlite3ResolveNotValid(pParse, pNC, "subqueries", +- NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); +- sqlite3WalkSelect(pWalker, pExpr->x.pSelect); ++ assert( pExpr->x.pSelect ); ++ if( pNC->ncFlags & NC_SelfRef ){ ++ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); ++ }else{ ++ sqlite3WalkSelect(pWalker, pExpr->x.pSelect); ++ } + assert( pNC->nRef>=nRef ); + if( nRef!=pNC->nRef ){ + ExprSetProperty(pExpr, EP_VarSelect); +- pNC->ncFlags |= NC_VarSelect; ++ pExpr->x.pSelect->selFlags |= SF_Correlated; + } ++ pNC->ncFlags |= NC_Subquery; + } + break; + } +@@ -98821,7 +109424,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + testcase( pNC->ncFlags & NC_IdxExpr ); + testcase( pNC->ncFlags & NC_GenCol ); + sqlite3ResolveNotValid(pParse, pNC, "parameters", +- NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); ++ NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr, pExpr); + break; + } + case TK_IS: +@@ -98830,7 +109433,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + assert( !ExprHasProperty(pExpr, EP_Reduced) ); + /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE", + ** and "x IS NOT FALSE". */ +- if( pRight && pRight->op==TK_ID ){ ++ if( ALWAYS(pRight) && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){ + int rc = resolveExprStep(pWalker, pRight); + if( rc==WRC_Abort ) return WRC_Abort; + if( pRight->op==TK_TRUEFALSE ){ +@@ -98853,6 +109456,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + assert( pExpr->pLeft!=0 ); + nLeft = sqlite3ExprVectorSize(pExpr->pLeft); + if( pExpr->op==TK_BETWEEN ){ ++ assert( ExprUseXList(pExpr) ); + nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[0].pExpr); + if( nRight==nLeft ){ + nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[1].pExpr); +@@ -98872,11 +109476,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ + testcase( pExpr->op==TK_ISNOT ); + testcase( pExpr->op==TK_BETWEEN ); + sqlite3ErrorMsg(pParse, "row value misused"); ++ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); + } + break; + } + } +- return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; ++ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); ++ return pParse->nErr ? WRC_Abort : WRC_Continue; + } + + /* +@@ -98901,9 +109507,11 @@ static int resolveAsName( + UNUSED_PARAMETER(pParse); + + if( pE->op==TK_ID ){ +- char *zCol = pE->u.zToken; ++ const char *zCol; ++ assert( !ExprHasProperty(pE, EP_IntValue) ); ++ zCol = pE->u.zToken; + for(i=0; inExpr; i++){ +- if( pEList->a[i].eEName==ENAME_NAME ++ if( pEList->a[i].fg.eEName==ENAME_NAME + && sqlite3_stricmp(pEList->a[i].zEName, zCol)==0 + ){ + return i+1; +@@ -98943,7 +109551,7 @@ static int resolveOrderByTermToExprList( + int rc; /* Return code from subprocedures */ + u8 savedSuppErr; /* Saved value of db->suppressErr */ + +- assert( sqlite3ExprIsInteger(pE, &i)==0 ); ++ assert( sqlite3ExprIsInteger(pE, &i, 0)==0 ); + pEList = pSelect->pEList; + + /* Resolve all names in the ORDER BY term expression +@@ -98952,11 +109560,11 @@ static int resolveOrderByTermToExprList( + nc.pParse = pParse; + nc.pSrcList = pSelect->pSrc; + nc.uNC.pEList = pEList; +- nc.ncFlags = NC_AllowAgg|NC_UEList; +- nc.nErr = 0; ++ nc.ncFlags = NC_AllowAgg|NC_UEList|NC_NoSelect; ++ nc.nNcErr = 0; + db = pParse->db; + savedSuppErr = db->suppressErr; +- if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1; ++ db->suppressErr = 1; + rc = sqlite3ResolveExprNames(&nc, pE); + db->suppressErr = savedSuppErr; + if( rc ) return 0; +@@ -98982,11 +109590,13 @@ static void resolveOutOfRangeError( + Parse *pParse, /* The error context into which to write the error */ + const char *zType, /* "ORDER" or "GROUP" */ + int i, /* The index (1-based) of the term out of range */ +- int mx /* Largest permissible value of i */ ++ int mx, /* Largest permissible value of i */ ++ Expr *pError /* Associate the error with the expression */ + ){ + sqlite3ErrorMsg(pParse, + "%r %s BY term out of range - should be " + "between 1 and %d", i, zType, mx); ++ sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); + } + + /* +@@ -99022,7 +109632,7 @@ static int resolveCompoundOrderBy( + return 1; + } + for(i=0; inExpr; i++){ +- pOrderBy->a[i].done = 0; ++ pOrderBy->a[i].fg.done = 0; + } + pSelect->pNext = 0; + while( pSelect->pPrior ){ +@@ -99037,11 +109647,12 @@ static int resolveCompoundOrderBy( + for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ + int iCol = -1; + Expr *pE, *pDup; +- if( pItem->done ) continue; ++ if( pItem->fg.done ) continue; + pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); +- if( sqlite3ExprIsInteger(pE, &iCol) ){ ++ if( NEVER(pE==0) ) continue; ++ if( sqlite3ExprIsInteger(pE, &iCol, 0) ){ + if( iCol<=0 || iCol>pEList->nExpr ){ +- resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); ++ resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr, pE); + return 1; + } + }else{ +@@ -99054,29 +109665,24 @@ static int resolveCompoundOrderBy( + ** Once the comparisons are finished, the duplicate expression + ** is deleted. + ** +- ** Or, if this is running as part of an ALTER TABLE operation, +- ** resolve the symbols in the actual expression, not a duplicate. +- ** And, if one of the comparisons is successful, leave the expression +- ** as is instead of transforming it to an integer as in the usual +- ** case. This allows the code in alter.c to modify column +- ** refererences within the ORDER BY expression as required. */ +- if( IN_RENAME_OBJECT ){ +- pDup = pE; +- }else{ +- pDup = sqlite3ExprDup(db, pE, 0); +- } ++ ** If this is running as part of an ALTER TABLE operation and ++ ** the symbols resolve successfully, also resolve the symbols in the ++ ** actual expression. This allows the code in alter.c to modify ++ ** column references within the ORDER BY expression as required. */ ++ pDup = sqlite3ExprDup(db, pE, 0); + if( !db->mallocFailed ){ + assert(pDup); + iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); ++ if( IN_RENAME_OBJECT && iCol>0 ){ ++ resolveOrderByTermToExprList(pParse, pSelect, pE); ++ } + } +- if( !IN_RENAME_OBJECT ){ +- sqlite3ExprDelete(db, pDup); +- } ++ sqlite3ExprDelete(db, pDup); + } + } + if( iCol>0 ){ + /* Convert the ORDER BY term into an integer column number iCol, +- ** taking care to preserve the COLLATE clause if it exists */ ++ ** taking care to preserve the COLLATE clause if it exists. */ + if( !IN_RENAME_OBJECT ){ + Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); + if( pNew==0 ) return 1; +@@ -99094,7 +109700,7 @@ static int resolveCompoundOrderBy( + sqlite3ExprDelete(db, pE); + pItem->u.x.iOrderByCol = (u16)iCol; + } +- pItem->done = 1; ++ pItem->fg.done = 1; + }else{ + moreToDo = 1; + } +@@ -99102,7 +109708,7 @@ static int resolveCompoundOrderBy( + pSelect = pSelect->pNext; + } + for(i=0; inExpr; i++){ +- if( pOrderBy->a[i].done==0 ){ ++ if( pOrderBy->a[i].fg.done==0 ){ + sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any " + "column in the result set", i+1); + return 1; +@@ -99142,11 +109748,10 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( + for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ + if( pItem->u.x.iOrderByCol ){ + if( pItem->u.x.iOrderByCol>pEList->nExpr ){ +- resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); ++ resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr, 0); + return 1; + } +- resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, +- zType,0); ++ resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0); + } + } + return 0; +@@ -99212,12 +109817,13 @@ static int resolveOrderGroupBy( + Parse *pParse; /* Parsing context */ + int nResult; /* Number of terms in the result set */ + +- if( pOrderBy==0 ) return 0; ++ assert( pOrderBy!=0 ); + nResult = pSelect->pEList->nExpr; + pParse = pNC->pParse; + for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ + Expr *pE = pItem->pExpr; + Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); ++ if( NEVER(pE2==0) ) continue; + if( zType[0]!='G' ){ + iCol = resolveAsName(pParse, pSelect->pEList, pE2); + if( iCol>0 ){ +@@ -99229,12 +109835,12 @@ static int resolveOrderGroupBy( + continue; + } + } +- if( sqlite3ExprIsInteger(pE2, &iCol) ){ ++ if( sqlite3ExprIsInteger(pE2, &iCol, 0) ){ + /* The ORDER BY term is an integer constant. Again, set the column + ** number so that sqlite3ResolveOrderGroupBy() will convert the + ** order-by term to a copy of the result-set expression */ + if( iCol<1 || iCol>0xffff ){ +- resolveOutOfRangeError(pParse, zType, i+1, nResult); ++ resolveOutOfRangeError(pParse, zType, i+1, nResult, pE2); + return 1; + } + pItem->u.x.iOrderByCol = (u16)iCol; +@@ -99248,7 +109854,7 @@ static int resolveOrderGroupBy( + } + for(j=0; jpEList->nExpr; j++){ + if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ +- /* Since this expresion is being changed into a reference ++ /* Since this expression is being changed into a reference + ** to an identical expression in the result set, remove all Window + ** objects belonging to the expression from the Select.pWin list. */ + windowRemoveExprFromSelect(pSelect, pE); +@@ -99292,7 +109898,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + */ + if( (p->selFlags & SF_Expanded)==0 ){ + sqlite3SelectPrep(pParse, p, pOuterNC); +- return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune; ++ return pParse->nErr ? WRC_Abort : WRC_Prune; + } + + isCompound = p->pPrior!=0; +@@ -99320,39 +109926,51 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + ** moves the pOrderBy down to the sub-query. It will be moved back + ** after the names have been resolved. */ + if( p->selFlags & SF_Converted ){ +- Select *pSub = p->pSrc->a[0].pSelect; ++ Select *pSub; ++ assert( p->pSrc->a[0].fg.isSubquery ); ++ assert( p->pSrc->a[0].u4.pSubq!=0 ); ++ pSub = p->pSrc->a[0].u4.pSubq->pSelect; ++ assert( pSub!=0 ); + assert( p->pSrc->nSrc==1 && p->pOrderBy ); + assert( pSub->pPrior && pSub->pOrderBy==0 ); + pSub->pOrderBy = p->pOrderBy; + p->pOrderBy = 0; + } + +- /* Recursively resolve names in all subqueries ++ /* Recursively resolve names in all subqueries in the FROM clause + */ ++ if( pOuterNC ) pOuterNC->nNestedSelect++; + for(i=0; ipSrc->nSrc; i++){ +- struct SrcList_item *pItem = &p->pSrc->a[i]; +- if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ +- NameContext *pNC; /* Used to iterate name contexts */ +- int nRef = 0; /* Refcount for pOuterNC and outer contexts */ ++ SrcItem *pItem = &p->pSrc->a[i]; ++ assert( pItem->zName!=0 ++ || pItem->fg.isSubquery ); /* Test of tag-20240424-1*/ ++ if( pItem->fg.isSubquery ++ && (pItem->u4.pSubq->pSelect->selFlags & SF_Resolved)==0 ++ ){ ++ int nRef = pOuterNC ? pOuterNC->nRef : 0; + const char *zSavedContext = pParse->zAuthContext; + +- /* Count the total number of references to pOuterNC and all of its +- ** parent contexts. After resolving references to expressions in +- ** pItem->pSelect, check if this value has changed. If so, then +- ** SELECT statement pItem->pSelect must be correlated. Set the +- ** pItem->fg.isCorrelated flag if this is the case. */ +- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef; +- + if( pItem->zName ) pParse->zAuthContext = pItem->zName; +- sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); ++ sqlite3ResolveSelectNames(pParse, pItem->u4.pSubq->pSelect, pOuterNC); + pParse->zAuthContext = zSavedContext; +- if( pParse->nErr || db->mallocFailed ) return WRC_Abort; ++ if( pParse->nErr ) return WRC_Abort; ++ assert( db->mallocFailed==0 ); + +- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef; +- assert( pItem->fg.isCorrelated==0 && nRef<=0 ); +- pItem->fg.isCorrelated = (nRef!=0); ++ /* If the number of references to the outer context changed when ++ ** expressions in the sub-select were resolved, the sub-select ++ ** is correlated. It is not required to check the refcount on any ++ ** but the innermost outer context object, as lookupName() increments ++ ** the refcount on all contexts between the current one and the ++ ** context containing the column when it resolves a name. */ ++ if( pOuterNC ){ ++ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef ); ++ pItem->fg.isCorrelated = (pOuterNC->nRef>nRef); ++ } + } + } ++ if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){ ++ pOuterNC->nNestedSelect--; ++ } + + /* Set up the local name-context to pass to sqlite3ResolveExprNames() to + ** resolve the result-set expression list. +@@ -99372,18 +109990,12 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + pGroupBy = p->pGroupBy; + if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){ + assert( NC_MinMaxAgg==SF_MinMaxAgg ); +- p->selFlags |= SF_Aggregate | (sNC.ncFlags&NC_MinMaxAgg); ++ assert( NC_OrderAgg==SF_OrderByReqd ); ++ p->selFlags |= SF_Aggregate | (sNC.ncFlags&(NC_MinMaxAgg|NC_OrderAgg)); + }else{ + sNC.ncFlags &= ~NC_AllowAgg; + } + +- /* If a HAVING clause is present, then there must be a GROUP BY clause. +- */ +- if( p->pHaving && !pGroupBy ){ +- sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); +- return WRC_Abort; +- } +- + /* Add the output column list to the name-context before parsing the + ** other expressions in the SELECT statement. This is so that + ** expressions in the WHERE clause (etc.) can refer to expressions by +@@ -99392,15 +110004,23 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + ** Minor point: If this is the case, then the expression will be + ** re-evaluated for each reference to it. + */ +- assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 ); ++ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 ); + sNC.uNC.pEList = p->pEList; + sNC.ncFlags |= NC_UEList; +- if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; ++ if( p->pHaving ){ ++ if( (p->selFlags & SF_Aggregate)==0 ){ ++ sqlite3ErrorMsg(pParse, "HAVING clause on a non-aggregate query"); ++ return WRC_Abort; ++ } ++ if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; ++ } ++ sNC.ncFlags |= NC_Where; + if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; ++ sNC.ncFlags &= ~NC_Where; + + /* Resolve names in table-valued-function arguments */ + for(i=0; ipSrc->nSrc; i++){ +- struct SrcList_item *pItem = &p->pSrc->a[i]; ++ SrcItem *pItem = &p->pSrc->a[i]; + if( pItem->fg.isTabFunc + && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg) + ){ +@@ -99408,6 +110028,19 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + } + } + ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ if( IN_RENAME_OBJECT ){ ++ Window *pWin; ++ for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ ++ if( sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) ++ || sqlite3ResolveExprListNames(&sNC, pWin->pPartition) ++ ){ ++ return WRC_Abort; ++ } ++ } ++ } ++#endif ++ + /* The ORDER BY and GROUP BY clauses may not refer to terms in + ** outer queries + */ +@@ -99420,7 +110053,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + ** These integers will be replaced by copies of the corresponding result + ** set expressions by the call to resolveOrderGroupBy() below. */ + if( p->selFlags & SF_Converted ){ +- Select *pSub = p->pSrc->a[0].pSelect; ++ Select *pSub; ++ assert( p->pSrc->a[0].fg.isSubquery ); ++ pSub = p->pSrc->a[0].u4.pSubq->pSelect; ++ assert( pSub!=0 ); + p->pOrderBy = pSub->pOrderBy; + pSub->pOrderBy = 0; + } +@@ -99435,7 +110071,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + ** is not detected until much later, and so we need to go ahead and + ** resolve those symbols on the incorrect ORDER BY for consistency. + */ +- if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ ++ if( p->pOrderBy!=0 ++ && isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ + && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") + ){ + return WRC_Abort; +@@ -99463,19 +110100,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ + } + } + +-#ifndef SQLITE_OMIT_WINDOWFUNC +- if( IN_RENAME_OBJECT ){ +- Window *pWin; +- for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ +- if( sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) +- || sqlite3ResolveExprListNames(&sNC, pWin->pPartition) +- ){ +- return WRC_Abort; +- } +- } +- } +-#endif +- + /* If this is part of a compound SELECT, check that it has the right + ** number of expressions in the select list. */ + if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){ +@@ -99555,11 +110179,11 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( + Walker w; + + if( pExpr==0 ) return SQLITE_OK; +- savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); +- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); ++ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); ++ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + w.pParse = pNC->pParse; + w.xExprCallback = resolveExprStep; +- w.xSelectCallback = resolveSelectStep; ++ w.xSelectCallback = (pNC->ncFlags & NC_NoSelect) ? 0 : resolveSelectStep; + w.xSelectCallback2 = 0; + w.u.pNC = pNC; + #if SQLITE_MAX_EXPR_DEPTH>0 +@@ -99568,7 +110192,8 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( + return SQLITE_ERROR; + } + #endif +- sqlite3WalkExpr(&w, pExpr); ++ assert( pExpr!=0 ); ++ sqlite3WalkExprNN(&w, pExpr); + #if SQLITE_MAX_EXPR_DEPTH>0 + w.pParse->nHeight -= pExpr->nHeight; + #endif +@@ -99578,13 +110203,16 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( + testcase( pNC->ncFlags & NC_HasWin ); + ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); + pNC->ncFlags |= savedHasAgg; +- return pNC->nErr>0 || w.pParse->nErr>0; ++ return pNC->nNcErr>0 || w.pParse->nErr>0; + } + + /* + ** Resolve all names for all expression in an expression list. This is + ** just like sqlite3ResolveExprNames() except that it works for an expression + ** list rather than a single expression. ++** ++** The return value is SQLITE_OK (0) for success or SQLITE_ERROR (1) for a ++** failure. + */ + SQLITE_PRIVATE int sqlite3ResolveExprListNames( + NameContext *pNC, /* Namespace to resolve expressions in. */ +@@ -99593,24 +110221,24 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames( + int i; + int savedHasAgg = 0; + Walker w; +- if( pList==0 ) return WRC_Continue; ++ if( pList==0 ) return SQLITE_OK; + w.pParse = pNC->pParse; + w.xExprCallback = resolveExprStep; + w.xSelectCallback = resolveSelectStep; + w.xSelectCallback2 = 0; + w.u.pNC = pNC; +- savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); +- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); ++ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); ++ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + for(i=0; inExpr; i++){ + Expr *pExpr = pList->a[i].pExpr; + if( pExpr==0 ) continue; + #if SQLITE_MAX_EXPR_DEPTH>0 + w.pParse->nHeight += pExpr->nHeight; + if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){ +- return WRC_Abort; ++ return SQLITE_ERROR; + } + #endif +- sqlite3WalkExpr(&w, pExpr); ++ sqlite3WalkExprNN(&w, pExpr); + #if SQLITE_MAX_EXPR_DEPTH>0 + w.pParse->nHeight -= pExpr->nHeight; + #endif +@@ -99618,20 +110246,21 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames( + assert( EP_Win==NC_HasWin ); + testcase( pNC->ncFlags & NC_HasAgg ); + testcase( pNC->ncFlags & NC_HasWin ); +- if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){ ++ if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg) ){ + ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); +- savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); +- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); ++ savedHasAgg |= pNC->ncFlags & ++ (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); ++ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); + } +- if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort; ++ if( w.pParse->nErr>0 ) return SQLITE_ERROR; + } + pNC->ncFlags |= savedHasAgg; +- return WRC_Continue; ++ return SQLITE_OK; + } + + /* + ** Resolve all names in all expressions of a SELECT and in all +-** decendents of the SELECT, including compounds off of p->pPrior, ++** descendants of the SELECT, including compounds off of p->pPrior, + ** subqueries in expressions, and subqueries used as FROM clause + ** terms. + ** +@@ -99682,20 +110311,22 @@ SQLITE_PRIVATE int sqlite3ResolveSelfReference( + Expr *pExpr, /* Expression to resolve. May be NULL. */ + ExprList *pList /* Expression list to resolve. May be NULL. */ + ){ +- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ ++ SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */ + NameContext sNC; /* Name context for pParse->pNewTable */ + int rc; ++ u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */ + + assert( type==0 || pTab!=0 ); + assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr + || type==NC_GenCol || pTab==0 ); + memset(&sNC, 0, sizeof(sNC)); +- memset(&sSrc, 0, sizeof(sSrc)); ++ pSrc = (SrcList*)srcSpace; ++ memset(pSrc, 0, SZ_SRCLIST_1); + if( pTab ){ +- sSrc.nSrc = 1; +- sSrc.a[0].zName = pTab->zName; +- sSrc.a[0].pTab = pTab; +- sSrc.a[0].iCursor = -1; ++ pSrc->nSrc = 1; ++ pSrc->a[0].zName = pTab->zName; ++ pSrc->a[0].pSTab = pTab; ++ pSrc->a[0].iCursor = -1; + if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ + /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP + ** schema elements */ +@@ -99703,7 +110334,7 @@ SQLITE_PRIVATE int sqlite3ResolveSelfReference( + } + } + sNC.pParse = pParse; +- sNC.pSrcList = &sSrc; ++ sNC.pSrcList = pSrc; + sNC.ncFlags = type | NC_IsDDL; + if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; + if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); +@@ -99735,9 +110366,9 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); + /* + ** Return the affinity character for a single column of a table. + */ +-SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){ +- assert( iColnCol ); +- return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER; ++SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table *pTab, int iCol){ ++ if( iCol<0 || NEVER(iCol>=pTab->nCol) ) return SQLITE_AFF_INTEGER; ++ return pTab->aCol[iCol].affinity; + } + + /* +@@ -99758,41 +110389,126 @@ SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){ + */ + SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ + int op; +- while( ExprHasProperty(pExpr, EP_Skip) ){ +- assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); +- pExpr = pExpr->pLeft; +- assert( pExpr!=0 ); +- } + op = pExpr->op; +- if( op==TK_SELECT ){ +- assert( pExpr->flags&EP_xIsSelect ); +- assert( pExpr->x.pSelect!=0 ); +- assert( pExpr->x.pSelect->pEList!=0 ); +- assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 ); +- return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); +- } +- if( op==TK_REGISTER ) op = pExpr->op2; ++ while( 1 /* exit-by-break */ ){ ++ if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){ ++ assert( ExprUseYTab(pExpr) ); ++ assert( pExpr->y.pTab!=0 ); ++ return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); ++ } ++ if( op==TK_SELECT ){ ++ assert( ExprUseXSelect(pExpr) ); ++ assert( pExpr->x.pSelect!=0 ); ++ assert( pExpr->x.pSelect->pEList!=0 ); ++ assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 ); ++ return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); ++ } + #ifndef SQLITE_OMIT_CAST +- if( op==TK_CAST ){ +- assert( !ExprHasProperty(pExpr, EP_IntValue) ); +- return sqlite3AffinityType(pExpr->u.zToken, 0); +- } ++ if( op==TK_CAST ){ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ return sqlite3AffinityType(pExpr->u.zToken, 0); ++ } + #endif +- if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->y.pTab ){ +- return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); +- } +- if( op==TK_SELECT_COLUMN ){ +- assert( pExpr->pLeft->flags&EP_xIsSelect ); +- return sqlite3ExprAffinity( +- pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr +- ); +- } +- if( op==TK_VECTOR ){ +- return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); ++ if( op==TK_SELECT_COLUMN ){ ++ assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) ); ++ assert( pExpr->iColumn < pExpr->iTable ); ++ assert( pExpr->iColumn >= 0 ); ++ assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr ); ++ return sqlite3ExprAffinity( ++ pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr ++ ); ++ } ++ if( op==TK_VECTOR ++ || (op==TK_FUNCTION && pExpr->affExpr==SQLITE_AFF_DEFER) ++ ){ ++ assert( ExprUseXList(pExpr) ); ++ return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); ++ } ++ if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){ ++ assert( pExpr->op==TK_COLLATE ++ || pExpr->op==TK_IF_NULL_ROW ++ || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) ); ++ pExpr = pExpr->pLeft; ++ op = pExpr->op; ++ continue; ++ } ++ if( op!=TK_REGISTER ) break; ++ op = pExpr->op2; ++ if( NEVER( op==TK_REGISTER ) ) break; + } + return pExpr->affExpr; + } + ++/* ++** Make a guess at all the possible datatypes of the result that could ++** be returned by an expression. Return a bitmask indicating the answer: ++** ++** 0x01 Numeric ++** 0x02 Text ++** 0x04 Blob ++** ++** If the expression must return NULL, then 0x00 is returned. ++*/ ++SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr){ ++ while( pExpr ){ ++ switch( pExpr->op ){ ++ case TK_COLLATE: ++ case TK_IF_NULL_ROW: ++ case TK_UPLUS: { ++ pExpr = pExpr->pLeft; ++ break; ++ } ++ case TK_NULL: { ++ pExpr = 0; ++ break; ++ } ++ case TK_STRING: { ++ return 0x02; ++ } ++ case TK_BLOB: { ++ return 0x04; ++ } ++ case TK_CONCAT: { ++ return 0x06; ++ } ++ case TK_VARIABLE: ++ case TK_AGG_FUNCTION: ++ case TK_FUNCTION: { ++ return 0x07; ++ } ++ case TK_COLUMN: ++ case TK_AGG_COLUMN: ++ case TK_SELECT: ++ case TK_CAST: ++ case TK_SELECT_COLUMN: ++ case TK_VECTOR: { ++ int aff = sqlite3ExprAffinity(pExpr); ++ if( aff>=SQLITE_AFF_NUMERIC ) return 0x05; ++ if( aff==SQLITE_AFF_TEXT ) return 0x06; ++ return 0x07; ++ } ++ case TK_CASE: { ++ int res = 0; ++ int ii; ++ ExprList *pList = pExpr->x.pList; ++ assert( ExprUseXList(pExpr) && pList!=0 ); ++ assert( pList->nExpr > 0); ++ for(ii=1; iinExpr; ii+=2){ ++ res |= sqlite3ExprDataType(pList->a[ii].pExpr); ++ } ++ if( pList->nExpr % 2 ){ ++ res |= sqlite3ExprDataType(pList->a[pList->nExpr-1].pExpr); ++ } ++ return res; ++ } ++ default: { ++ return 0x01; ++ } ++ } /* End of switch(op) */ ++ } /* End of while(pExpr) */ ++ return 0x00; ++} ++ + /* + ** Set the collating sequence for expression pExpr to be the collating + ** sequence named by pToken. Return a pointer to a new Expr node that +@@ -99802,7 +110518,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ + ** and the pExpr parameter is returned unchanged. + */ + SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken( +- Parse *pParse, /* Parsing context */ ++ const Parse *pParse, /* Parsing context */ + Expr *pExpr, /* Add the "COLLATE" clause to this expression */ + const Token *pCollName, /* Name of collating sequence */ + int dequote /* True to dequote pCollName */ +@@ -99817,7 +110533,11 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken( + } + return pExpr; + } +-SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){ ++SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString( ++ const Parse *pParse, /* Parsing context */ ++ Expr *pExpr, /* Add the "COLLATE" clause to this expression */ ++ const char *zC /* The collating sequence name */ ++){ + Token s; + assert( zC!=0 ); + sqlite3TokenInit(&s, (char*)zC); +@@ -99829,7 +110549,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con + */ + SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ + while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){ +- assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); ++ assert( pExpr->op==TK_COLLATE ); + pExpr = pExpr->pLeft; + } + return pExpr; +@@ -99843,13 +110563,14 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ + SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){ + while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){ + if( ExprHasProperty(pExpr, EP_Unlikely) ){ +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); ++ assert( ExprUseXList(pExpr) ); + assert( pExpr->x.pList->nExpr>0 ); + assert( pExpr->op==TK_FUNCTION ); + pExpr = pExpr->x.pList->a[0].pExpr; +- }else{ +- assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); ++ }else if( pExpr->op==TK_COLLATE ){ + pExpr = pExpr->pLeft; ++ }else{ ++ break; + } + } + return pExpr; +@@ -99876,14 +110597,14 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ + while( p ){ + int op = p->op; + if( op==TK_REGISTER ) op = p->op2; +- if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER) +- && p->y.pTab!=0 ++ if( (op==TK_AGG_COLUMN && p->y.pTab!=0) ++ || op==TK_COLUMN || op==TK_TRIGGER + ){ +- /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally +- ** a TK_COLUMN but was previously evaluated and cached in a register */ +- int j = p->iColumn; +- if( j>=0 ){ +- const char *zColl = p->y.pTab->aCol[j].zColl; ++ int j; ++ assert( ExprUseYTab(p) ); ++ assert( p->y.pTab!=0 ); ++ if( (j = p->iColumn)>=0 ){ ++ const char *zColl = sqlite3ColumnColl(&p->y.pTab->aCol[j]); + pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); + } + break; +@@ -99892,11 +110613,15 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ + p = p->pLeft; + continue; + } +- if( op==TK_VECTOR ){ ++ if( op==TK_VECTOR ++ || (op==TK_FUNCTION && p->affExpr==SQLITE_AFF_DEFER) ++ ){ ++ assert( ExprUseXList(p) ); + p = p->x.pList->a[0].pExpr; + continue; + } + if( op==TK_COLLATE ){ ++ assert( !ExprHasProperty(p, EP_IntValue) ); + pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); + break; + } +@@ -99906,13 +110631,10 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ + }else{ + Expr *pNext = p->pRight; + /* The Expr.x union is never used at the same time as Expr.pRight */ +- assert( p->x.pList==0 || p->pRight==0 ); +- if( p->x.pList!=0 +- && !db->mallocFailed +- && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) +- ){ ++ assert( !ExprUseXList(p) || p->x.pList==0 || p->pRight==0 ); ++ if( ExprUseXList(p) && p->x.pList!=0 && !db->mallocFailed ){ + int i; +- for(i=0; ALWAYS(ix.pList->nExpr); i++){ ++ for(i=0; ix.pList->nExpr; i++){ + if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ + pNext = p->x.pList->a[i].pExpr; + break; +@@ -99934,7 +110656,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ + /* + ** Return the collation sequence for the expression pExpr. If + ** there is no defined collating sequence, return a pointer to the +-** defautl collation sequence. ++** default collation sequence. + ** + ** See also: sqlite3ExprCollSeq() + ** +@@ -99993,7 +110715,7 @@ static char comparisonAffinity(const Expr *pExpr){ + aff = sqlite3ExprAffinity(pExpr->pLeft); + if( pExpr->pRight ){ + aff = sqlite3CompareAffinity(pExpr->pRight, aff); +- }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ }else if( ExprUseXSelect(pExpr) ){ + aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); + }else if( aff==0 ){ + aff = SQLITE_AFF_BLOB; +@@ -100064,7 +110786,7 @@ SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq( + return pColl; + } + +-/* Expresssion p is a comparison operator. Return a collation sequence ++/* Expression p is a comparison operator. Return a collation sequence + ** appropriate for the comparison operator. + ** + ** This is normally just a wrapper around sqlite3BinaryCompareCollSeq(). +@@ -100106,7 +110828,7 @@ static int codeCompare( + p5 = binaryCompareP5(pLeft, pRight, jumpIfNull); + addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1, + (void*)p4, P4_COLLSEQ); +- sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5); ++ sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5); + return addr; + } + +@@ -100119,7 +110841,7 @@ static int codeCompare( + ** But a TK_SELECT might be either a vector or a scalar. It is only + ** considered a vector if it has two or more result columns. + */ +-SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr){ ++SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr){ + return sqlite3ExprVectorSize(pExpr)>1; + } + +@@ -100129,12 +110851,14 @@ SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr){ + ** is a sub-select, return the number of columns in the sub-select. For + ** any other type of expression, return 1. + */ +-SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){ ++SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr){ + u8 op = pExpr->op; + if( op==TK_REGISTER ) op = pExpr->op2; + if( op==TK_VECTOR ){ ++ assert( ExprUseXList(pExpr) ); + return pExpr->x.pList->nExpr; + }else if( op==TK_SELECT ){ ++ assert( ExprUseXSelect(pExpr) ); + return pExpr->x.pSelect->pEList->nExpr; + }else{ + return 1; +@@ -100157,12 +110881,14 @@ SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){ + ** been positioned. + */ + SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ +- assert( iop==TK_ERROR ); + if( sqlite3ExprIsVector(pVector) ){ + assert( pVector->op2==0 || pVector->op==TK_REGISTER ); + if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){ ++ assert( ExprUseXSelect(pVector) ); + return pVector->x.pSelect->pEList->a[i].pExpr; + }else{ ++ assert( ExprUseXList(pVector) ); + return pVector->x.pList->a[i].pExpr; + } + } +@@ -100193,11 +110919,12 @@ SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ + SQLITE_PRIVATE Expr *sqlite3ExprForVectorField( + Parse *pParse, /* Parsing context */ + Expr *pVector, /* The vector. List of expressions or a sub-SELECT */ +- int iField /* Which column of the vector to return */ ++ int iField, /* Which column of the vector to return */ ++ int nField /* Total number of columns in the vector */ + ){ + Expr *pRet; + if( pVector->op==TK_SELECT ){ +- assert( pVector->flags & EP_xIsSelect ); ++ assert( ExprUseXSelect(pVector) ); + /* The TK_SELECT_COLUMN Expr node: + ** + ** pLeft: pVector containing TK_SELECT. Not deleted. +@@ -100216,14 +110943,24 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField( + */ + pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0); + if( pRet ){ ++ ExprSetProperty(pRet, EP_FullSize); ++ pRet->iTable = nField; + pRet->iColumn = iField; + pRet->pLeft = pVector; + } +- assert( pRet==0 || pRet->iTable==0 ); + }else{ +- if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; ++ if( pVector->op==TK_VECTOR ){ ++ Expr **ppVector; ++ assert( ExprUseXList(pVector) ); ++ ppVector = &pVector->x.pList->a[iField].pExpr; ++ pVector = *ppVector; ++ if( IN_RENAME_OBJECT ){ ++ /* This must be a vector UPDATE inside a trigger */ ++ *ppVector = 0; ++ return pVector; ++ } ++ } + pRet = sqlite3ExprDup(pParse->db, pVector, 0); +- sqlite3RenameTokenRemap(pParse, pRet, pVector); + } + return pRet; + } +@@ -100273,17 +111010,22 @@ static int exprVectorRegister( + int *pRegFree /* OUT: Temp register to free */ + ){ + u8 op = pVector->op; +- assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT ); ++ assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT || op==TK_ERROR ); + if( op==TK_REGISTER ){ + *ppExpr = sqlite3VectorFieldSubexpr(pVector, iField); + return pVector->iTable+iField; + } + if( op==TK_SELECT ){ ++ assert( ExprUseXSelect(pVector) ); + *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; + return regSelect+iField; + } +- *ppExpr = pVector->x.pList->a[iField].pExpr; +- return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); ++ if( op==TK_VECTOR ){ ++ assert( ExprUseXList(pVector) ); ++ *ppExpr = pVector->x.pList->a[iField].pExpr; ++ return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); ++ } ++ return 0; + } + + /* +@@ -100312,6 +111054,7 @@ static void codeVectorCompare( + int regLeft = 0; + int regRight = 0; + u8 opx = op; ++ int addrCmp = 0; + int addrDone = sqlite3VdbeMakeLabel(pParse); + int isCommuted = ExprHasProperty(pExpr,EP_Commuted); + +@@ -100331,21 +111074,24 @@ static void codeVectorCompare( + assert( p5==0 || pExpr->op!=op ); + assert( p5==SQLITE_NULLEQ || pExpr->op==op ); + +- p5 |= SQLITE_STOREP2; +- if( opx==TK_LE ) opx = TK_LT; +- if( opx==TK_GE ) opx = TK_GT; ++ if( op==TK_LE ) opx = TK_LT; ++ if( op==TK_GE ) opx = TK_GT; ++ if( op==TK_NE ) opx = TK_EQ; + + regLeft = exprCodeSubselect(pParse, pLeft); + regRight = exprCodeSubselect(pParse, pRight); + ++ sqlite3VdbeAddOp2(v, OP_Integer, 1, dest); + for(i=0; 1 /*Loop exits by "break"*/; i++){ + int regFree1 = 0, regFree2 = 0; +- Expr *pL, *pR; ++ Expr *pL = 0, *pR = 0; + int r1, r2; + assert( i>=0 && i0 +@@ -100403,14 +111155,14 @@ SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse *pParse, int nHeight){ + ** to by pnHeight, the second parameter, then set *pnHeight to that + ** value. + */ +-static void heightOfExpr(Expr *p, int *pnHeight){ ++static void heightOfExpr(const Expr *p, int *pnHeight){ + if( p ){ + if( p->nHeight>*pnHeight ){ + *pnHeight = p->nHeight; + } + } + } +-static void heightOfExprList(ExprList *p, int *pnHeight){ ++static void heightOfExprList(const ExprList *p, int *pnHeight){ + if( p ){ + int i; + for(i=0; inExpr; i++){ +@@ -100418,8 +111170,8 @@ static void heightOfExprList(ExprList *p, int *pnHeight){ + } + } + } +-static void heightOfSelect(Select *pSelect, int *pnHeight){ +- Select *p; ++static void heightOfSelect(const Select *pSelect, int *pnHeight){ ++ const Select *p; + for(p=pSelect; p; p=p->pPrior){ + heightOfExpr(p->pWhere, pnHeight); + heightOfExpr(p->pHaving, pnHeight); +@@ -100441,10 +111193,11 @@ static void heightOfSelect(Select *pSelect, int *pnHeight){ + ** if appropriate. + */ + static void exprSetHeight(Expr *p){ +- int nHeight = 0; +- heightOfExpr(p->pLeft, &nHeight); +- heightOfExpr(p->pRight, &nHeight); +- if( ExprHasProperty(p, EP_xIsSelect) ){ ++ int nHeight = p->pLeft ? p->pLeft->nHeight : 0; ++ if( NEVER(p->pRight) && p->pRight->nHeight>nHeight ){ ++ nHeight = p->pRight->nHeight; ++ } ++ if( ExprUseXSelect(p) ){ + heightOfSelect(p->x.pSelect, &nHeight); + }else if( p->x.pList ){ + heightOfExprList(p->x.pList, &nHeight); +@@ -100471,7 +111224,7 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ + ** Return the maximum height of any expression tree referenced + ** by the select statement passed as an argument. + */ +-SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){ ++SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *p){ + int nHeight = 0; + heightOfSelect(p, &nHeight); + return nHeight; +@@ -100482,13 +111235,23 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){ + ** Expr.flags. + */ + SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ +- if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){ ++ if( pParse->nErr ) return; ++ if( p && ExprUseXList(p) && p->x.pList ){ + p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList); + } + } + #define exprSetHeight(y) + #endif /* SQLITE_MAX_EXPR_DEPTH>0 */ + ++/* ++** Set the error offset for an Expr node, if possible. ++*/ ++SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){ ++ if( pExpr==0 ) return; ++ if( NEVER(ExprUseWJoin(pExpr)) ) return; ++ pExpr->w.iOfst = iOfst; ++} ++ + /* + ** This routine is the core allocator for Expr nodes. + ** +@@ -100503,11 +111266,12 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ + ** appear to be quoted. If the quotes were of the form "..." (double-quotes) + ** then the EP_DblQuoted flag is set on the expression node. + ** +-** Special case: If op==TK_INTEGER and pToken points to a string that +-** can be translated into a 32-bit integer, then the token is not +-** stored in u.zToken. Instead, the integer values is written +-** into u.iValue and the EP_IntValue flag is set. No extra storage ++** Special case (tag-20240227-a): If op==TK_INTEGER and pToken points to ++** a string that can be translated into a 32-bit integer, then the token is ++** not stored in u.zToken. Instead, the integer values is written ++** into u.iValue and the EP_IntValue flag is set. No extra storage + ** is allocated to hold the integer text and the dequote flag is ignored. ++** See also tag-20240227-b. + */ + SQLITE_PRIVATE Expr *sqlite3ExprAlloc( + sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */ +@@ -100523,7 +111287,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( + if( pToken ){ + if( op!=TK_INTEGER || pToken->z==0 + || sqlite3GetInt32(pToken->z, &iValue)==0 ){ +- nExtra = pToken->n+1; ++ nExtra = pToken->n+1; /* tag-20240227-a */ + assert( iValue>=0 ); + } + } +@@ -100585,15 +111349,26 @@ SQLITE_PRIVATE void sqlite3ExprAttachSubtrees( + sqlite3ExprDelete(db, pLeft); + sqlite3ExprDelete(db, pRight); + }else{ ++ assert( ExprUseXList(pRoot) ); ++ assert( pRoot->x.pSelect==0 ); + if( pRight ){ + pRoot->pRight = pRight; + pRoot->flags |= EP_Propagate & pRight->flags; ++#if SQLITE_MAX_EXPR_DEPTH>0 ++ pRoot->nHeight = pRight->nHeight+1; ++ }else{ ++ pRoot->nHeight = 1; ++#endif + } + if( pLeft ){ + pRoot->pLeft = pLeft; + pRoot->flags |= EP_Propagate & pLeft->flags; ++#if SQLITE_MAX_EXPR_DEPTH>0 ++ if( pLeft->nHeight>=pRoot->nHeight ){ ++ pRoot->nHeight = pLeft->nHeight+1; ++ } ++#endif + } +- exprSetHeight(pRoot); + } + } + +@@ -100640,14 +111415,71 @@ SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, Select *pS + } + } + ++/* ++** Expression list pEList is a list of vector values. This function ++** converts the contents of pEList to a VALUES(...) Select statement ++** returning 1 row for each element of the list. For example, the ++** expression list: ++** ++** ( (1,2), (3,4) (5,6) ) ++** ++** is translated to the equivalent of: ++** ++** VALUES(1,2), (3,4), (5,6) ++** ++** Each of the vector values in pEList must contain exactly nElem terms. ++** If a list element that is not a vector or does not contain nElem terms, ++** an error message is left in pParse. ++** ++** This is used as part of processing IN(...) expressions with a list ++** of vectors on the RHS. e.g. "... IN ((1,2), (3,4), (5,6))". ++*/ ++SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse *pParse, int nElem, ExprList *pEList){ ++ int ii; ++ Select *pRet = 0; ++ assert( nElem>1 ); ++ for(ii=0; iinExpr; ii++){ ++ Select *pSel; ++ Expr *pExpr = pEList->a[ii].pExpr; ++ int nExprElem; ++ if( pExpr->op==TK_VECTOR ){ ++ assert( ExprUseXList(pExpr) ); ++ nExprElem = pExpr->x.pList->nExpr; ++ }else{ ++ nExprElem = 1; ++ } ++ if( nExprElem!=nElem ){ ++ sqlite3ErrorMsg(pParse, "IN(...) element has %d term%s - expected %d", ++ nExprElem, nExprElem>1?"s":"", nElem ++ ); ++ break; ++ } ++ assert( ExprUseXList(pExpr) ); ++ pSel = sqlite3SelectNew(pParse, pExpr->x.pList, 0, 0, 0, 0, 0, SF_Values,0); ++ pExpr->x.pList = 0; ++ if( pSel ){ ++ if( pRet ){ ++ pSel->op = TK_ALL; ++ pSel->pPrior = pRet; ++ } ++ pRet = pSel; ++ } ++ } ++ ++ if( pRet && pRet->pPrior ){ ++ pRet->selFlags |= SF_MultiValue; ++ } ++ sqlite3ExprListDelete(pParse->db, pEList); ++ return pRet; ++} + + /* + ** Join two expressions using an AND operator. If either expression is + ** NULL, then just return the other expression. + ** +-** If one side or the other of the AND is known to be false, then instead +-** of returning an AND expression, just return a constant expression with +-** a value of false. ++** If one side or the other of the AND is known to be false, and neither side ++** is part of an ON clause, then instead of returning an AND expression, ++** just return a constant expression with a value of false. + */ + SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ + sqlite3 *db = pParse->db; +@@ -100655,14 +111487,17 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ + return pRight; + }else if( pRight==0 ){ + return pLeft; +- }else if( (ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight)) +- && !IN_RENAME_OBJECT +- ){ +- sqlite3ExprDelete(db, pLeft); +- sqlite3ExprDelete(db, pRight); +- return sqlite3Expr(db, TK_INTEGER, "0"); + }else{ +- return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); ++ u32 f = pLeft->flags | pRight->flags; ++ if( (f&(EP_OuterON|EP_InnerON|EP_IsFalse|EP_HasFunc))==EP_IsFalse ++ && !IN_RENAME_OBJECT ++ ){ ++ sqlite3ExprDeferredDelete(pParse, pLeft); ++ sqlite3ExprDeferredDelete(pParse, pRight); ++ return sqlite3Expr(db, TK_INTEGER, "0"); ++ }else{ ++ return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); ++ } + } + } + +@@ -100673,7 +111508,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ + SQLITE_PRIVATE Expr *sqlite3ExprFunction( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* Argument list */ +- Token *pToken, /* Name of the function */ ++ const Token *pToken, /* Name of the function */ + int eDistinct /* SF_Distinct or SF_ALL or 0 */ + ){ + Expr *pNew; +@@ -100684,17 +111519,83 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction( + sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ + return 0; + } +- if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ ++ assert( !ExprHasProperty(pNew, EP_InnerON|EP_OuterON) ); ++ pNew->w.iOfst = (int)(pToken->z - pParse->zTail); ++ if( pList ++ && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ++ && !pParse->nested ++ ){ + sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken); + } + pNew->x.pList = pList; + ExprSetProperty(pNew, EP_HasFunc); +- assert( !ExprHasProperty(pNew, EP_xIsSelect) ); ++ assert( ExprUseXList(pNew) ); + sqlite3ExprSetHeightAndFlags(pParse, pNew); + if( eDistinct==SF_Distinct ) ExprSetProperty(pNew, EP_Distinct); + return pNew; + } + ++/* ++** Report an error when attempting to use an ORDER BY clause within ++** the arguments of a non-aggregate function. ++*/ ++SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse *pParse, Expr *p){ ++ sqlite3ErrorMsg(pParse, ++ "ORDER BY may not be used with non-aggregate %#T()", p ++ ); ++} ++ ++/* ++** Attach an ORDER BY clause to a function call. ++** ++** functionname( arguments ORDER BY sortlist ) ++** \_____________________/ \______/ ++** pExpr pOrderBy ++** ++** The ORDER BY clause is inserted into a new Expr node of type TK_ORDER ++** and added to the Expr.pLeft field of the parent TK_FUNCTION node. ++*/ ++SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy( ++ Parse *pParse, /* Parsing context */ ++ Expr *pExpr, /* The function call to which ORDER BY is to be added */ ++ ExprList *pOrderBy /* The ORDER BY clause to add */ ++){ ++ Expr *pOB; ++ sqlite3 *db = pParse->db; ++ if( NEVER(pOrderBy==0) ){ ++ assert( db->mallocFailed ); ++ return; ++ } ++ if( pExpr==0 ){ ++ assert( db->mallocFailed ); ++ sqlite3ExprListDelete(db, pOrderBy); ++ return; ++ } ++ assert( pExpr->op==TK_FUNCTION ); ++ assert( pExpr->pLeft==0 ); ++ assert( ExprUseXList(pExpr) ); ++ if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){ ++ /* Ignore ORDER BY on zero-argument aggregates */ ++ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy); ++ return; ++ } ++ if( IsWindowFunc(pExpr) ){ ++ sqlite3ExprOrderByAggregateError(pParse, pExpr); ++ sqlite3ExprListDelete(db, pOrderBy); ++ return; ++ } ++ ++ pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0); ++ if( pOB==0 ){ ++ sqlite3ExprListDelete(db, pOrderBy); ++ return; ++ } ++ pOB->x.pList = pOrderBy; ++ assert( ExprUseXList(pOB) ); ++ pExpr->pLeft = pOB; ++ ExprSetProperty(pOB, EP_FullSize); ++} ++ + /* + ** Check to see if a function is usable according to current access + ** rules: +@@ -100708,8 +111609,8 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction( + */ + SQLITE_PRIVATE void sqlite3ExprFunctionUsable( + Parse *pParse, /* Parsing and code generating context */ +- Expr *pExpr, /* The function invocation */ +- FuncDef *pDef /* The function being invoked */ ++ const Expr *pExpr, /* The function invocation */ ++ const FuncDef *pDef /* The function being invoked */ + ){ + assert( !IN_RENAME_OBJECT ); + assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); +@@ -100724,7 +111625,7 @@ SQLITE_PRIVATE void sqlite3ExprFunctionUsable( + ** SQLITE_DBCONFIG_TRUSTED_SCHEMA is off (meaning + ** that the schema is possibly tainted). + */ +- sqlite3ErrorMsg(pParse, "unsafe use of %s()", pDef->zName); ++ sqlite3ErrorMsg(pParse, "unsafe use of %#T()", pExpr); + } + } + } +@@ -100780,6 +111681,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n + if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ + sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", + db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); ++ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); + return; + } + x = (ynVar)i; +@@ -100807,6 +111709,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n + pExpr->iColumn = x; + if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ + sqlite3ErrorMsg(pParse, "too many SQL variables"); ++ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); + } + } + +@@ -100815,27 +111718,27 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n + */ + static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ + assert( p!=0 ); +- /* Sanity check: Assert that the IntValue is non-negative if it exists */ +- assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); +- +- assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed ); +- assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced) +- || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) ); ++ assert( db!=0 ); ++exprDeleteRestart: ++ assert( !ExprUseUValue(p) || p->u.iValue>=0 ); ++ assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); ++ assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); ++ assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); + #ifdef SQLITE_DEBUG + if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ + assert( p->pLeft==0 ); + assert( p->pRight==0 ); +- assert( p->x.pSelect==0 ); ++ assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); ++ assert( !ExprUseXList(p) || p->x.pList==0 ); + } + #endif + if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ + /* The Expr.x union is never used at the same time as Expr.pRight */ +- assert( p->x.pList==0 || p->pRight==0 ); +- if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); ++ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); + if( p->pRight ){ + assert( !ExprHasProperty(p, EP_WinFunc) ); + sqlite3ExprDeleteNN(db, p->pRight); +- }else if( ExprHasProperty(p, EP_xIsSelect) ){ ++ }else if( ExprUseXSelect(p) ){ + assert( !ExprHasProperty(p, EP_WinFunc) ); + sqlite3SelectDelete(db, p->x.pSelect); + }else{ +@@ -100846,15 +111749,57 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ + } + #endif + } ++ if( p->pLeft && p->op!=TK_SELECT_COLUMN ){ ++ Expr *pLeft = p->pLeft; ++ if( !ExprHasProperty(p, EP_Static) ++ && !ExprHasProperty(pLeft, EP_Static) ++ ){ ++ /* Avoid unnecessary recursion on unary operators */ ++ sqlite3DbNNFreeNN(db, p); ++ p = pLeft; ++ goto exprDeleteRestart; ++ }else{ ++ sqlite3ExprDeleteNN(db, pLeft); ++ } ++ } + } +- if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); + if( !ExprHasProperty(p, EP_Static) ){ +- sqlite3DbFreeNN(db, p); ++ sqlite3DbNNFreeNN(db, p); + } + } + SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ + if( p ) sqlite3ExprDeleteNN(db, p); + } ++SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){ ++ if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p); ++} ++ ++/* ++** Clear both elements of an OnOrUsing object ++*/ ++SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ ++ if( p==0 ){ ++ /* Nothing to clear */ ++ }else if( p->pOn ){ ++ sqlite3ExprDeleteNN(db, p->pOn); ++ }else if( p->pUsing ){ ++ sqlite3IdListDelete(db, p->pUsing); ++ } ++} ++ ++/* ++** Arrange to cause pExpr to be deleted when the pParse is deleted. ++** This is similar to sqlite3ExprDelete() except that the delete is ++** deferred until the pParse is deleted. ++** ++** The pExpr might be deleted immediately on an OOM error. ++** ++** Return 0 if the delete was successfully deferred. Return non-zero ++** if the delete happened immediately because of an OOM. ++*/ ++SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ ++ return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); ++} + + /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the + ** expression. +@@ -100873,7 +111818,7 @@ SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ + ** passed as the first argument. This is always one of EXPR_FULLSIZE, + ** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. + */ +-static int exprStructSize(Expr *p){ ++static int exprStructSize(const Expr *p){ + if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE; + if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE; + return EXPR_FULLSIZE; +@@ -100913,21 +111858,16 @@ static int exprStructSize(Expr *p){ + ** of dupedExprStructSize() contain multiple assert() statements that attempt + ** to enforce this constraint. + */ +-static int dupedExprStructSize(Expr *p, int flags){ ++static int dupedExprStructSize(const Expr *p, int flags){ + int nSize; + assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ + assert( EXPR_FULLSIZE<=0xfff ); + assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 ); +- if( 0==flags || p->op==TK_SELECT_COLUMN +-#ifndef SQLITE_OMIT_WINDOWFUNC +- || ExprHasProperty(p, EP_WinFunc) +-#endif +- ){ ++ if( 0==flags || ExprHasProperty(p, EP_FullSize) ){ + nSize = EXPR_FULLSIZE; + }else{ + assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); +- assert( !ExprHasProperty(p, EP_FromJoin) ); +- assert( !ExprHasProperty(p, EP_MemToken) ); ++ assert( !ExprHasProperty(p, EP_OuterON) ); + assert( !ExprHasVVAProperty(p, EP_NoReduce) ); + if( p->pLeft || p->x.pList ){ + nSize = EXPR_REDUCEDSIZE | EP_Reduced; +@@ -100944,7 +111884,7 @@ static int dupedExprStructSize(Expr *p, int flags){ + ** of the Expr structure and a copy of the Expr.u.zToken string (if that + ** string is defined.) + */ +-static int dupedExprNodeSize(Expr *p, int flags){ ++static int dupedExprNodeSize(const Expr *p, int flags){ + int nByte = dupedExprStructSize(p, flags) & 0xfff; + if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ + nByte += sqlite3Strlen30NN(p->u.zToken)+1; +@@ -100954,55 +111894,93 @@ static int dupedExprNodeSize(Expr *p, int flags){ + + /* + ** Return the number of bytes required to create a duplicate of the +-** expression passed as the first argument. The second argument is a +-** mask containing EXPRDUP_XXX flags. ++** expression passed as the first argument. + ** + ** The value returned includes space to create a copy of the Expr struct + ** itself and the buffer referred to by Expr.u.zToken, if any. + ** +-** If the EXPRDUP_REDUCE flag is set, then the return value includes +-** space to duplicate all Expr nodes in the tree formed by Expr.pLeft +-** and Expr.pRight variables (but not for any structures pointed to or +-** descended from the Expr.x.pList or Expr.x.pSelect variables). ++** The return value includes space to duplicate all Expr nodes in the ++** tree formed by Expr.pLeft and Expr.pRight, but not any other ++** substructure such as Expr.x.pList, Expr.x.pSelect, and Expr.y.pWin. + */ +-static int dupedExprSize(Expr *p, int flags){ +- int nByte = 0; +- if( p ){ +- nByte = dupedExprNodeSize(p, flags); +- if( flags&EXPRDUP_REDUCE ){ +- nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags); +- } +- } ++static int dupedExprSize(const Expr *p){ ++ int nByte; ++ assert( p!=0 ); ++ nByte = dupedExprNodeSize(p, EXPRDUP_REDUCE); ++ if( p->pLeft ) nByte += dupedExprSize(p->pLeft); ++ if( p->pRight ) nByte += dupedExprSize(p->pRight); ++ assert( nByte==ROUND8(nByte) ); + return nByte; + } + + /* +-** This function is similar to sqlite3ExprDup(), except that if pzBuffer +-** is not NULL then *pzBuffer is assumed to point to a buffer large enough +-** to store the copy of expression p, the copies of p->u.zToken +-** (if applicable), and the copies of the p->pLeft and p->pRight expressions, +-** if any. Before returning, *pzBuffer is set to the first byte past the +-** portion of the buffer copied into by this function. ++** An EdupBuf is a memory allocation used to stored multiple Expr objects ++** together with their Expr.zToken content. This is used to help implement ++** compression while doing sqlite3ExprDup(). The top-level Expr does the ++** allocation for itself and many of its decendents, then passes an instance ++** of the structure down into exprDup() so that they decendents can have ++** access to that memory. ++*/ ++typedef struct EdupBuf EdupBuf; ++struct EdupBuf { ++ u8 *zAlloc; /* Memory space available for storage */ ++#ifdef SQLITE_DEBUG ++ u8 *zEnd; /* First byte past the end of memory */ ++#endif ++}; ++ ++/* ++** This function is similar to sqlite3ExprDup(), except that if pEdupBuf ++** is not NULL then it points to memory that can be used to store a copy ++** of the input Expr p together with its p->u.zToken (if any). pEdupBuf ++** is updated with the new buffer tail prior to returning. + */ +-static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ ++static Expr *exprDup( ++ sqlite3 *db, /* Database connection (for memory allocation) */ ++ const Expr *p, /* Expr tree to be duplicated */ ++ int dupFlags, /* EXPRDUP_REDUCE for compression. 0 if not */ ++ EdupBuf *pEdupBuf /* Preallocated storage space, or NULL */ ++){ + Expr *pNew; /* Value to return */ +- u8 *zAlloc; /* Memory space from which to build Expr object */ ++ EdupBuf sEdupBuf; /* Memory space from which to build Expr object */ + u32 staticFlag; /* EP_Static if space not obtained from malloc */ ++ int nToken = -1; /* Space needed for p->u.zToken. -1 means unknown */ + + assert( db!=0 ); + assert( p ); + assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE ); +- assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE ); ++ assert( pEdupBuf==0 || dupFlags==EXPRDUP_REDUCE ); + + /* Figure out where to write the new Expr structure. */ +- if( pzBuffer ){ +- zAlloc = *pzBuffer; ++ if( pEdupBuf ){ ++ sEdupBuf.zAlloc = pEdupBuf->zAlloc; ++#ifdef SQLITE_DEBUG ++ sEdupBuf.zEnd = pEdupBuf->zEnd; ++#endif + staticFlag = EP_Static; ++ assert( sEdupBuf.zAlloc!=0 ); ++ assert( dupFlags==EXPRDUP_REDUCE ); + }else{ +- zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags)); ++ int nAlloc; ++ if( dupFlags ){ ++ nAlloc = dupedExprSize(p); ++ }else if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ ++ nToken = sqlite3Strlen30NN(p->u.zToken)+1; ++ nAlloc = ROUND8(EXPR_FULLSIZE + nToken); ++ }else{ ++ nToken = 0; ++ nAlloc = ROUND8(EXPR_FULLSIZE); ++ } ++ assert( nAlloc==ROUND8(nAlloc) ); ++ sEdupBuf.zAlloc = sqlite3DbMallocRawNN(db, nAlloc); ++#ifdef SQLITE_DEBUG ++ sEdupBuf.zEnd = sEdupBuf.zAlloc ? sEdupBuf.zAlloc+nAlloc : 0; ++#endif ++ + staticFlag = 0; + } +- pNew = (Expr *)zAlloc; ++ pNew = (Expr *)sEdupBuf.zAlloc; ++ assert( EIGHT_BYTE_ALIGNMENT(pNew) ); + + if( pNew ){ + /* Set nNewSize to the size allocated for the structure pointed to +@@ -101011,26 +111989,31 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ + ** by the copy of the p->u.zToken string (if any). + */ + const unsigned nStructSize = dupedExprStructSize(p, dupFlags); +- const int nNewSize = nStructSize & 0xfff; +- int nToken; +- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ +- nToken = sqlite3Strlen30(p->u.zToken) + 1; +- }else{ +- nToken = 0; ++ int nNewSize = nStructSize & 0xfff; ++ if( nToken<0 ){ ++ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ ++ nToken = sqlite3Strlen30(p->u.zToken) + 1; ++ }else{ ++ nToken = 0; ++ } + } + if( dupFlags ){ ++ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= nNewSize+nToken ); + assert( ExprHasProperty(p, EP_Reduced)==0 ); +- memcpy(zAlloc, p, nNewSize); ++ memcpy(sEdupBuf.zAlloc, p, nNewSize); + }else{ + u32 nSize = (u32)exprStructSize(p); +- memcpy(zAlloc, p, nSize); ++ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= ++ (int)EXPR_FULLSIZE+nToken ); ++ memcpy(sEdupBuf.zAlloc, p, nSize); + if( nSizeflags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken); ++ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static); + pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly); + pNew->flags |= staticFlag; + ExprClearVVAProperties(pNew); +@@ -101039,44 +112022,50 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ + } + + /* Copy the p->u.zToken string, if any. */ +- if( nToken ){ +- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize]; ++ assert( nToken>=0 ); ++ if( nToken>0 ){ ++ char *zToken = pNew->u.zToken = (char*)&sEdupBuf.zAlloc[nNewSize]; + memcpy(zToken, p->u.zToken, nToken); ++ nNewSize += nToken; + } ++ sEdupBuf.zAlloc += ROUND8(nNewSize); ++ ++ if( ((p->flags|pNew->flags)&(EP_TokenOnly|EP_Leaf))==0 ){ + +- if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){ + /* Fill in the pNew->x.pSelect or pNew->x.pList member. */ +- if( ExprHasProperty(p, EP_xIsSelect) ){ ++ if( ExprUseXSelect(p) ){ + pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags); + }else{ +- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags); ++ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, ++ p->op!=TK_ORDER ? dupFlags : 0); + } +- } + +- /* Fill in pNew->pLeft and pNew->pRight. */ +- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){ +- zAlloc += dupedExprNodeSize(p, dupFlags); +- if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){ +- pNew->pLeft = p->pLeft ? +- exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0; +- pNew->pRight = p->pRight ? +- exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0; +- } + #ifndef SQLITE_OMIT_WINDOWFUNC + if( ExprHasProperty(p, EP_WinFunc) ){ + pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin); + assert( ExprHasProperty(pNew, EP_WinFunc) ); + } + #endif /* SQLITE_OMIT_WINDOWFUNC */ +- if( pzBuffer ){ +- *pzBuffer = zAlloc; +- } +- }else{ +- if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ +- if( pNew->op==TK_SELECT_COLUMN ){ ++ ++ /* Fill in pNew->pLeft and pNew->pRight. */ ++ if( dupFlags ){ ++ if( p->op==TK_SELECT_COLUMN ){ ++ pNew->pLeft = p->pLeft; ++ assert( p->pRight==0 ++ || p->pRight==p->pLeft ++ || ExprHasProperty(p->pLeft, EP_Subquery) ); ++ }else{ ++ pNew->pLeft = p->pLeft ? ++ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &sEdupBuf) : 0; ++ } ++ pNew->pRight = p->pRight ? ++ exprDup(db, p->pRight, EXPRDUP_REDUCE, &sEdupBuf) : 0; ++ }else{ ++ if( p->op==TK_SELECT_COLUMN ){ + pNew->pLeft = p->pLeft; +- assert( p->iColumn==0 || p->pRight==0 ); +- assert( p->pRight==0 || p->pRight==p->pLeft ); ++ assert( p->pRight==0 ++ || p->pRight==p->pLeft ++ || ExprHasProperty(p->pLeft, EP_Subquery) ); + }else{ + pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); + } +@@ -101084,6 +112073,8 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ + } + } + } ++ if( pEdupBuf ) memcpy(pEdupBuf, &sEdupBuf, sizeof(sEdupBuf)); ++ assert( sEdupBuf.zAlloc <= sEdupBuf.zEnd ); + return pNew; + } + +@@ -101093,10 +112084,10 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ + ** and the db->mallocFailed flag set. + */ + #ifndef SQLITE_OMIT_CTE +-static With *withDup(sqlite3 *db, With *p){ ++SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ + With *pRet = 0; + if( p ){ +- sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); ++ sqlite3_int64 nByte = SZ_WITH(p->nCte); + pRet = sqlite3DbMallocZero(db, nByte); + if( pRet ){ + int i; +@@ -101105,13 +112096,14 @@ static With *withDup(sqlite3 *db, With *p){ + pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0); + pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0); + pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName); ++ pRet->a[i].eM10d = p->a[i].eM10d; + } + } + } + return pRet; + } + #else +-# define withDup(x,y) 0 ++# define sqlite3WithDup(x,y) 0 + #endif + + #ifndef SQLITE_OMIT_WINDOWFUNC +@@ -101164,20 +112156,23 @@ static void gatherSelectWindows(Select *p){ + ** truncated version of the usual Expr structure that will be stored as + ** part of the in-memory representation of the database schema. + */ +-SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){ ++SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, const Expr *p, int flags){ + assert( flags==0 || flags==EXPRDUP_REDUCE ); + return p ? exprDup(db, p, flags, 0) : 0; + } +-SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ ++SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int flags){ + ExprList *pNew; +- struct ExprList_item *pItem, *pOldItem; ++ struct ExprList_item *pItem; ++ const struct ExprList_item *pOldItem; + int i; +- Expr *pPriorSelectCol = 0; ++ Expr *pPriorSelectColOld = 0; ++ Expr *pPriorSelectColNew = 0; + assert( db!=0 ); + if( p==0 ) return 0; + pNew = sqlite3DbMallocRawNN(db, sqlite3DbMallocSize(db, p)); + if( pNew==0 ) return 0; + pNew->nExpr = p->nExpr; ++ pNew->nAlloc = p->nAlloc; + pItem = pNew->a; + pOldItem = p->a; + for(i=0; inExpr; i++, pItem++, pOldItem++){ +@@ -101188,24 +112183,21 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags) + && pOldExpr->op==TK_SELECT_COLUMN + && (pNewExpr = pItem->pExpr)!=0 + ){ +- assert( pNewExpr->iColumn==0 || i>0 ); +- if( pNewExpr->iColumn==0 ){ +- assert( pOldExpr->pLeft==pOldExpr->pRight ); +- pPriorSelectCol = pNewExpr->pLeft = pNewExpr->pRight; ++ if( pNewExpr->pRight ){ ++ pPriorSelectColOld = pOldExpr->pRight; ++ pPriorSelectColNew = pNewExpr->pRight; ++ pNewExpr->pLeft = pNewExpr->pRight; + }else{ +- assert( i>0 ); +- assert( pItem[-1].pExpr!=0 ); +- assert( pNewExpr->iColumn==pItem[-1].pExpr->iColumn+1 ); +- assert( pPriorSelectCol==pItem[-1].pExpr->pLeft ); +- pNewExpr->pLeft = pPriorSelectCol; ++ if( pOldExpr->pLeft!=pPriorSelectColOld ){ ++ pPriorSelectColOld = pOldExpr->pLeft; ++ pPriorSelectColNew = sqlite3ExprDup(db, pPriorSelectColOld, flags); ++ pNewExpr->pRight = pPriorSelectColNew; ++ } ++ pNewExpr->pLeft = pPriorSelectColNew; + } + } + pItem->zEName = sqlite3DbStrDup(db, pOldItem->zEName); +- pItem->sortFlags = pOldItem->sortFlags; +- pItem->eEName = pOldItem->eEName; +- pItem->done = 0; +- pItem->bNulls = pOldItem->bNulls; +- pItem->bSorterRef = pOldItem->bSorterRef; ++ pItem->fg = pOldItem->fg; + pItem->u = pOldItem->u; + } + return pNew; +@@ -101219,76 +112211,88 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags) + */ + #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ + || !defined(SQLITE_OMIT_SUBQUERY) +-SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ ++SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ + SrcList *pNew; + int i; +- int nByte; + assert( db!=0 ); + if( p==0 ) return 0; +- nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); +- pNew = sqlite3DbMallocRawNN(db, nByte ); ++ pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) ); + if( pNew==0 ) return 0; + pNew->nSrc = pNew->nAlloc = p->nSrc; + for(i=0; inSrc; i++){ +- struct SrcList_item *pNewItem = &pNew->a[i]; +- struct SrcList_item *pOldItem = &p->a[i]; ++ SrcItem *pNewItem = &pNew->a[i]; ++ const SrcItem *pOldItem = &p->a[i]; + Table *pTab; +- pNewItem->pSchema = pOldItem->pSchema; +- pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); ++ pNewItem->fg = pOldItem->fg; ++ if( pOldItem->fg.isSubquery ){ ++ Subquery *pNewSubq = sqlite3DbMallocRaw(db, sizeof(Subquery)); ++ if( pNewSubq==0 ){ ++ assert( db->mallocFailed ); ++ pNewItem->fg.isSubquery = 0; ++ }else{ ++ memcpy(pNewSubq, pOldItem->u4.pSubq, sizeof(*pNewSubq)); ++ pNewSubq->pSelect = sqlite3SelectDup(db, pNewSubq->pSelect, flags); ++ if( pNewSubq->pSelect==0 ){ ++ sqlite3DbFree(db, pNewSubq); ++ pNewSubq = 0; ++ pNewItem->fg.isSubquery = 0; ++ } ++ } ++ pNewItem->u4.pSubq = pNewSubq; ++ }else if( pOldItem->fg.fixedSchema ){ ++ pNewItem->u4.pSchema = pOldItem->u4.pSchema; ++ }else{ ++ pNewItem->u4.zDatabase = sqlite3DbStrDup(db, pOldItem->u4.zDatabase); ++ } + pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); + pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); +- pNewItem->fg = pOldItem->fg; + pNewItem->iCursor = pOldItem->iCursor; +- pNewItem->addrFillSub = pOldItem->addrFillSub; +- pNewItem->regReturn = pOldItem->regReturn; + if( pNewItem->fg.isIndexedBy ){ + pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); +- } +- pNewItem->pIBIndex = pOldItem->pIBIndex; +- if( pNewItem->fg.isTabFunc ){ ++ }else if( pNewItem->fg.isTabFunc ){ + pNewItem->u1.pFuncArg = + sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); ++ }else{ ++ pNewItem->u1.nRow = pOldItem->u1.nRow; + } +- pTab = pNewItem->pTab = pOldItem->pTab; ++ pNewItem->u2 = pOldItem->u2; ++ if( pNewItem->fg.isCte ){ ++ pNewItem->u2.pCteUse->nUse++; ++ } ++ pTab = pNewItem->pSTab = pOldItem->pSTab; + if( pTab ){ + pTab->nTabRef++; + } +- pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); +- pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); +- pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); ++ if( pOldItem->fg.isUsing ){ ++ assert( pNewItem->fg.isUsing ); ++ pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing); ++ }else{ ++ pNewItem->u3.pOn = sqlite3ExprDup(db, pOldItem->u3.pOn, flags); ++ } + pNewItem->colUsed = pOldItem->colUsed; + } + return pNew; + } +-SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ ++SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ + IdList *pNew; + int i; + assert( db!=0 ); + if( p==0 ) return 0; +- pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) ); ++ pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId)); + if( pNew==0 ) return 0; + pNew->nId = p->nId; +- pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) ); +- if( pNew->a==0 ){ +- sqlite3DbFreeNN(db, pNew); +- return 0; +- } +- /* Note that because the size of the allocation for p->a[] is not +- ** necessarily a power of two, sqlite3IdListAppend() may not be called +- ** on the duplicate created by this function. */ + for(i=0; inId; i++){ + struct IdList_item *pNewItem = &pNew->a[i]; +- struct IdList_item *pOldItem = &p->a[i]; ++ const struct IdList_item *pOldItem = &p->a[i]; + pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); +- pNewItem->idx = pOldItem->idx; + } + return pNew; + } +-SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ ++SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int flags){ + Select *pRet = 0; + Select *pNext = 0; + Select **pp = &pRet; +- Select *p; ++ const Select *p; + + assert( db!=0 ); + for(p=pDup; p; p=p->pPrior){ +@@ -101306,26 +112310,33 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ + pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); + pNew->iLimit = 0; + pNew->iOffset = 0; +- pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; ++ pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral; + pNew->addrOpenEphm[0] = -1; + pNew->addrOpenEphm[1] = -1; + pNew->nSelectRow = p->nSelectRow; +- pNew->pWith = withDup(db, p->pWith); ++ pNew->pWith = sqlite3WithDup(db, p->pWith); + #ifndef SQLITE_OMIT_WINDOWFUNC + pNew->pWin = 0; + pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); + if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew); + #endif + pNew->selId = p->selId; ++ if( db->mallocFailed ){ ++ /* Any prior OOM might have left the Select object incomplete. ++ ** Delete the whole thing rather than allow an incomplete Select ++ ** to be used by the code generator. */ ++ pNew->pNext = 0; ++ sqlite3SelectDelete(db, pNew); ++ break; ++ } + *pp = pNew; + pp = &pNew->pPrior; + pNext = pNew; + } +- + return pRet; + } + #else +-SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ ++SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags){ + assert( p==0 ); + return 0; + } +@@ -101337,51 +112348,69 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ + ** initially NULL, then create a new expression list. + ** + ** The pList argument must be either NULL or a pointer to an ExprList +-** obtained from a prior call to sqlite3ExprListAppend(). This routine +-** may not be used with an ExprList obtained from sqlite3ExprListDup(). +-** Reason: This routine assumes that the number of slots in pList->a[] +-** is a power of two. That is true for sqlite3ExprListAppend() returns +-** but is not necessarily true from the return value of sqlite3ExprListDup(). ++** obtained from a prior call to sqlite3ExprListAppend(). + ** + ** If a memory allocation error occurs, the entire list is freed and + ** NULL is returned. If non-NULL is returned, then it is guaranteed + ** that the new entry was successfully appended. + */ ++static const struct ExprList_item zeroItem = {0}; ++SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendNew( ++ sqlite3 *db, /* Database handle. Used for memory allocation */ ++ Expr *pExpr /* Expression to be appended. Might be NULL */ ++){ ++ struct ExprList_item *pItem; ++ ExprList *pList; ++ ++ pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4)); ++ if( pList==0 ){ ++ sqlite3ExprDelete(db, pExpr); ++ return 0; ++ } ++ pList->nAlloc = 4; ++ pList->nExpr = 1; ++ pItem = &pList->a[0]; ++ *pItem = zeroItem; ++ pItem->pExpr = pExpr; ++ return pList; ++} ++SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendGrow( ++ sqlite3 *db, /* Database handle. Used for memory allocation */ ++ ExprList *pList, /* List to which to append. Might be NULL */ ++ Expr *pExpr /* Expression to be appended. Might be NULL */ ++){ ++ struct ExprList_item *pItem; ++ ExprList *pNew; ++ pList->nAlloc *= 2; ++ pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc)); ++ if( pNew==0 ){ ++ sqlite3ExprListDelete(db, pList); ++ sqlite3ExprDelete(db, pExpr); ++ return 0; ++ }else{ ++ pList = pNew; ++ } ++ pItem = &pList->a[pList->nExpr++]; ++ *pItem = zeroItem; ++ pItem->pExpr = pExpr; ++ return pList; ++} + SQLITE_PRIVATE ExprList *sqlite3ExprListAppend( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* List to which to append. Might be NULL */ + Expr *pExpr /* Expression to be appended. Might be NULL */ + ){ + struct ExprList_item *pItem; +- sqlite3 *db = pParse->db; +- assert( db!=0 ); + if( pList==0 ){ +- pList = sqlite3DbMallocRawNN(db, sizeof(ExprList) ); +- if( pList==0 ){ +- goto no_mem; +- } +- pList->nExpr = 0; +- }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ +- ExprList *pNew; +- pNew = sqlite3DbRealloc(db, pList, +- sizeof(*pList)+(2*(sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0])); +- if( pNew==0 ){ +- goto no_mem; +- } +- pList = pNew; ++ return sqlite3ExprListAppendNew(pParse->db,pExpr); ++ } ++ if( pList->nAllocnExpr+1 ){ ++ return sqlite3ExprListAppendGrow(pParse->db,pList,pExpr); + } + pItem = &pList->a[pList->nExpr++]; +- assert( offsetof(struct ExprList_item,zEName)==sizeof(pItem->pExpr) ); +- assert( offsetof(struct ExprList_item,pExpr)==0 ); +- memset(&pItem->zEName,0,sizeof(*pItem)-offsetof(struct ExprList_item,zEName)); ++ *pItem = zeroItem; + pItem->pExpr = pExpr; + return pList; +- +-no_mem: +- /* Avoid leaking memory if malloc has failed. */ +- sqlite3ExprDelete(db, pExpr); +- sqlite3ExprListDelete(db, pList); +- return 0; + } + + /* +@@ -101422,11 +112451,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector( + } + + for(i=0; inId; i++){ +- Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i); ++ Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i, pColumns->nId); + assert( pSubExpr!=0 || db->mallocFailed ); +- assert( pSubExpr==0 || pSubExpr->iTable==0 ); + if( pSubExpr==0 ) continue; +- pSubExpr->iTable = pColumns->nId; + pList = sqlite3ExprListAppend(pParse, pList, pSubExpr); + if( pList ){ + assert( pList->nExpr==iFirst+i+1 ); +@@ -101475,16 +112502,16 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int + ); + + pItem = &p->a[p->nExpr-1]; +- assert( pItem->bNulls==0 ); ++ assert( pItem->fg.bNulls==0 ); + if( iSortOrder==SQLITE_SO_UNDEFINED ){ + iSortOrder = SQLITE_SO_ASC; + } +- pItem->sortFlags = (u8)iSortOrder; ++ pItem->fg.sortFlags = (u8)iSortOrder; + + if( eNulls!=SQLITE_SO_UNDEFINED ){ +- pItem->bNulls = 1; ++ pItem->fg.bNulls = 1; + if( iSortOrder!=eNulls ){ +- pItem->sortFlags |= KEYINFO_ORDER_BIGNULL; ++ pItem->fg.sortFlags |= KEYINFO_ORDER_BIGNULL; + } + } + } +@@ -101500,7 +112527,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int + SQLITE_PRIVATE void sqlite3ExprListSetName( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* List to which to add the span. */ +- Token *pName, /* Name to be added */ ++ const Token *pName, /* Name to be added */ + int dequote /* True to cause the name to be dequoted */ + ){ + assert( pList!=0 || pParse->db->mallocFailed!=0 ); +@@ -101510,7 +112537,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName( + assert( pList->nExpr>0 ); + pItem = &pList->a[pList->nExpr-1]; + assert( pItem->zEName==0 ); +- assert( pItem->eEName==ENAME_NAME ); ++ assert( pItem->fg.eEName==ENAME_NAME ); + pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); + if( dequote ){ + /* If dequote==0, then pName->z does not point to part of a DDL +@@ -101518,7 +112545,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName( + ** to the token-map. */ + sqlite3Dequote(pItem->zEName); + if( IN_RENAME_OBJECT ){ +- sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); ++ sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName); + } + } + } +@@ -101545,7 +112572,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetSpan( + assert( pList->nExpr>0 ); + if( pItem->zEName==0 ){ + pItem->zEName = sqlite3DbSpanDup(db, zStart, zEnd); +- pItem->eEName = ENAME_SPAN; ++ pItem->fg.eEName = ENAME_SPAN; + } + } + } +@@ -101574,16 +112601,20 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){ + int i = pList->nExpr; + struct ExprList_item *pItem = pList->a; + assert( pList->nExpr>0 ); ++ assert( db!=0 ); + do{ + sqlite3ExprDelete(db, pItem->pExpr); +- sqlite3DbFree(db, pItem->zEName); ++ if( pItem->zEName ) sqlite3DbNNFreeNN(db, pItem->zEName); + pItem++; + }while( --i>0 ); +- sqlite3DbFreeNN(db, pList); ++ sqlite3DbNNFreeNN(db, pList); + } + SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ + if( pList ) exprListDeleteNN(db, pList); + } ++SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){ ++ if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList); ++} + + /* + ** Return the bitwise-OR of all Expr.flags fields in the given +@@ -101637,7 +112668,7 @@ SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char *zIn){ + SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ + u32 v; + assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); +- if( !ExprHasProperty(pExpr, EP_Quoted) ++ if( !ExprHasProperty(pExpr, EP_Quoted|EP_IntValue) + && (v = sqlite3IsTrueOrFalse(pExpr->u.zToken))!=0 + ){ + pExpr->op = TK_TRUEFALSE; +@@ -101652,8 +112683,9 @@ SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ + ** and 0 if it is FALSE. + */ + SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){ +- pExpr = sqlite3ExprSkipCollate((Expr*)pExpr); ++ pExpr = sqlite3ExprSkipCollateAndLikely((Expr*)pExpr); + assert( pExpr->op==TK_TRUEFALSE ); ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( sqlite3StrICmp(pExpr->u.zToken,"true")==0 + || sqlite3StrICmp(pExpr->u.zToken,"false")==0 ); + return pExpr->u.zToken[4]==0; +@@ -101686,6 +112718,54 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ + return pExpr; + } + ++/* ++** pExpr is a TK_FUNCTION node. Try to determine whether or not the ++** function is a constant function. A function is constant if all of ++** the following are true: ++** ++** (1) It is a scalar function (not an aggregate or window function) ++** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG ++** property. ++** (3) All of its arguments are constants ++** ++** This routine sets pWalker->eCode to 0 if pExpr is not a constant. ++** It makes no changes to pWalker->eCode if pExpr is constant. In ++** every case, it returns WRC_Abort. ++** ++** Called as a service subroutine from exprNodeIsConstant(). ++*/ ++static SQLITE_NOINLINE int exprNodeIsConstantFunction( ++ Walker *pWalker, ++ Expr *pExpr ++){ ++ int n; /* Number of arguments */ ++ ExprList *pList; /* List of arguments */ ++ FuncDef *pDef; /* The function */ ++ sqlite3 *db; /* The database */ ++ ++ assert( pExpr->op==TK_FUNCTION ); ++ if( ExprHasProperty(pExpr, EP_TokenOnly) ++ || (pList = pExpr->x.pList)==0 ++ ){; ++ n = 0; ++ }else{ ++ n = pList->nExpr; ++ sqlite3WalkExprList(pWalker, pList); ++ if( pWalker->eCode==0 ) return WRC_Abort; ++ } ++ db = pWalker->pParse->db; ++ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); ++ if( pDef==0 ++ || pDef->xFinalize!=0 ++ || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 ++ || ExprHasProperty(pExpr, EP_WinFunc) ++ ){ ++ pWalker->eCode = 0; ++ return WRC_Abort; ++ } ++ return WRC_Prune; ++} ++ + + /* + ** These routines are Walker callbacks used to check expressions to +@@ -101714,11 +112794,12 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ + ** malformed schema error. + */ + static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ ++ assert( pWalker->eCode>0 ); + + /* If pWalker->eCode is 2 then any term of the expression that comes from +- ** the ON or USING clauses of a left join disqualifies the expression ++ ** the ON or USING clauses of an outer join disqualifies the expression + ** from being considered constant. */ +- if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){ ++ if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){ + pWalker->eCode = 0; + return WRC_Abort; + } +@@ -101733,6 +112814,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ + ){ + if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL); + return WRC_Continue; ++ }else if( pWalker->pParse ){ ++ return exprNodeIsConstantFunction(pWalker, pExpr); + }else{ + pWalker->eCode = 0; + return WRC_Abort; +@@ -101761,9 +112844,11 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ + case TK_IF_NULL_ROW: + case TK_REGISTER: + case TK_DOT: ++ case TK_RAISE: + testcase( pExpr->op==TK_REGISTER ); + testcase( pExpr->op==TK_IF_NULL_ROW ); + testcase( pExpr->op==TK_DOT ); ++ testcase( pExpr->op==TK_RAISE ); + pWalker->eCode = 0; + return WRC_Abort; + case TK_VARIABLE: +@@ -101785,15 +112870,15 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ + return WRC_Continue; + } + } +-static int exprIsConst(Expr *p, int initFlag, int iCur){ ++static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ + Walker w; + w.eCode = initFlag; ++ w.pParse = pParse; + w.xExprCallback = exprNodeIsConstant; + w.xSelectCallback = sqlite3SelectWalkFail; + #ifdef SQLITE_DEBUG + w.xSelectCallback2 = sqlite3SelectWalkAssert2; + #endif +- w.u.iCur = iCur; + sqlite3WalkExpr(&w, p); + return w.eCode; + } +@@ -101805,9 +112890,15 @@ static int exprIsConst(Expr *p, int initFlag, int iCur){ + ** For the purposes of this function, a double-quoted string (ex: "abc") + ** is considered a variable but a single-quoted string (ex: 'abc') is + ** a constant. ++** ++** The pParse parameter may be NULL. But if it is NULL, there is no way ++** to determine if function calls are constant or not, and hence all ++** function calls will be considered to be non-constant. If pParse is ++** not NULL, then a function call might be constant, depending on the ++** function and on its parameters. + */ +-SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ +- return exprIsConst(p, 1, 0); ++SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse *pParse, Expr *p){ ++ return exprIsConst(pParse, p, 1); + } + + /* +@@ -101823,8 +112914,24 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ + ** can be added to the pParse->pConstExpr list and evaluated once when + ** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce(). + */ +-SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ +- return exprIsConst(p, 2, 0); ++static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ ++ return exprIsConst(pParse, p, 2); ++} ++ ++/* ++** This routine examines sub-SELECT statements as an expression is being ++** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered ++** constant as long as they are uncorrelated - meaning that they do not ++** contain any terms from outer contexts. ++*/ ++static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ ++ assert( pSelect!=0 ); ++ assert( pWalker->eCode==3 || pWalker->eCode==0 ); ++ if( (pSelect->selFlags & SF_Correlated)!=0 ){ ++ pWalker->eCode = 0; ++ return WRC_Abort; ++ } ++ return WRC_Prune; + } + + /* +@@ -101832,9 +112939,103 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ + ** for any single row of the table with cursor iCur. In other words, the + ** expression must not refer to any non-deterministic function nor any + ** table other than iCur. ++** ++** Consider uncorrelated subqueries to be constants if the bAllowSubq ++** parameter is true. ++*/ ++static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){ ++ Walker w; ++ w.eCode = 3; ++ w.pParse = 0; ++ w.xExprCallback = exprNodeIsConstant; ++ if( bAllowSubq ){ ++ w.xSelectCallback = exprSelectWalkTableConstant; ++ }else{ ++ w.xSelectCallback = sqlite3SelectWalkFail; ++#ifdef SQLITE_DEBUG ++ w.xSelectCallback2 = sqlite3SelectWalkAssert2; ++#endif ++ } ++ w.u.iCur = iCur; ++ sqlite3WalkExpr(&w, p); ++ return w.eCode; ++} ++ ++/* ++** Check pExpr to see if it is an constraint on the single data source ++** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr ++** constrains pSrc but does not depend on any other tables or data ++** sources anywhere else in the query. Return true (non-zero) if pExpr ++** is a constraint on pSrc only. ++** ++** This is an optimization. False negatives will perhaps cause slower ++** queries, but false positives will yield incorrect answers. So when in ++** doubt, return 0. ++** ++** To be an single-source constraint, the following must be true: ++** ++** (1) pExpr cannot refer to any table other than pSrc->iCursor. ++** ++** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is ++** true and the subquery is non-correlated ++** ++** (2b) pExpr cannot use non-deterministic functions. ++** ++** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ++** (Is there some way to relax this constraint?) ++** ++** (4) If pSrc is the right operand of a LEFT JOIN, then... ++** (4a) pExpr must come from an ON clause.. ++** (4b) and specifically the ON clause associated with the LEFT JOIN. ++** ++** (5) If pSrc is the right operand of a LEFT JOIN or the left ++** operand of a RIGHT JOIN, then pExpr must be from the WHERE ++** clause, not an ON clause. ++** ++** (6) Either: ++** ++** (6a) pExpr does not originate in an ON or USING clause, or ++** ++** (6b) The ON or USING clause from which pExpr is derived is ++** not to the left of a RIGHT JOIN (or FULL JOIN). ++** ++** Without this restriction, accepting pExpr as a single-table ++** constraint might move the the ON/USING filter expression ++** from the left side of a RIGHT JOIN over to the right side, ++** which leads to incorrect answers. See also restriction (9) ++** on push-down. + */ +-SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ +- return exprIsConst(p, 3, iCur); ++SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( ++ Expr *pExpr, /* The constraint */ ++ const SrcList *pSrcList, /* Complete FROM clause */ ++ int iSrc, /* Which element of pSrcList to use */ ++ int bAllowSubq /* Allow non-correlated subqueries */ ++){ ++ const SrcItem *pSrc = &pSrcList->a[iSrc]; ++ if( pSrc->fg.jointype & JT_LTORJ ){ ++ return 0; /* rule (3) */ ++ } ++ if( pSrc->fg.jointype & JT_LEFT ){ ++ if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ ++ if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ ++ }else{ ++ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ ++ } ++ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) /* (6a) */ ++ && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (6b) */ ++ ){ ++ int jj; ++ for(jj=0; jjw.iJoin==pSrcList->a[jj].iCursor ){ ++ if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){ ++ return 0; /* restriction (6) */ ++ } ++ break; ++ } ++ } ++ } ++ /* Rules (1), (2a), and (2b) handled by the following: */ ++ return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq); + } + + +@@ -101858,7 +113059,7 @@ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){ + } + + /* Check if pExpr is a sub-select. If so, consider it variable. */ +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + pWalker->eCode = 0; + return WRC_Abort; + } +@@ -101919,7 +113120,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprLi + */ + SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){ + assert( isInit==0 || isInit==1 ); +- return exprIsConst(p, 4+isInit, 0); ++ return exprIsConst(0, p, 4+isInit); + } + + #ifdef SQLITE_ENABLE_CURSOR_HINTS +@@ -101945,8 +113146,12 @@ SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr *p){ + ** to fit in a 32-bit integer, return 1 and put the value of the integer + ** in *pValue. If the expression is not an integer or if it is too big + ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. ++** ++** If the pParse pointer is provided, then allow the expression p to be ++** a parameter (TK_VARIABLE) that is bound to an integer. ++** But if pParse is NULL, then p must be a pure integer literal. + */ +-SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ ++SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue, Parse *pParse){ + int rc = 0; + if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */ + +@@ -101961,18 +113166,38 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ + } + switch( p->op ){ + case TK_UPLUS: { +- rc = sqlite3ExprIsInteger(p->pLeft, pValue); ++ rc = sqlite3ExprIsInteger(p->pLeft, pValue, 0); + break; + } + case TK_UMINUS: { +- int v; +- if( sqlite3ExprIsInteger(p->pLeft, &v) ){ +- assert( v!=(-2147483647-1) ); ++ int v = 0; ++ if( sqlite3ExprIsInteger(p->pLeft, &v, 0) ){ ++ assert( ((unsigned int)v)!=0x80000000 ); + *pValue = -v; + rc = 1; + } + break; + } ++ case TK_VARIABLE: { ++ sqlite3_value *pVal; ++ if( pParse==0 ) break; ++ if( NEVER(pParse->pVdbe==0) ) break; ++ if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) break; ++ sqlite3VdbeSetVarmask(pParse->pVdbe, p->iColumn); ++ pVal = sqlite3VdbeGetBoundValue(pParse->pReprepare, p->iColumn, ++ SQLITE_AFF_BLOB); ++ if( pVal ){ ++ if( sqlite3_value_type(pVal)==SQLITE_INTEGER ){ ++ sqlite3_int64 vv = sqlite3_value_int64(pVal); ++ if( vv == (vv & 0x7fffffff) ){ /* non-negative numbers only */ ++ *pValue = (int)vv; ++ rc = 1; ++ } ++ } ++ sqlite3ValueFree(pVal); ++ } ++ break; ++ } + default: break; + } + return rc; +@@ -101994,8 +113219,10 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ + */ + SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ + u8 op; ++ assert( p!=0 ); + while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ + p = p->pLeft; ++ assert( p!=0 ); + } + op = p->op; + if( op==TK_REGISTER ) op = p->op2; +@@ -102006,10 +113233,15 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ + case TK_BLOB: + return 0; + case TK_COLUMN: +- return ExprHasProperty(p, EP_CanBeNull) || +- p->y.pTab==0 || /* Reference to column of index on expression */ +- (p->iColumn>=0 +- && ALWAYS(p->y.pTab->aCol!=0) /* Defense against OOM problems */ ++ assert( ExprUseYTab(p) ); ++ return ExprHasProperty(p, EP_CanBeNull) ++ || NEVER(p->y.pTab==0) /* Reference to column of index on expr */ ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ || (p->iColumn==XN_ROWID && IsView(p->y.pTab)) ++#endif ++ || (p->iColumn>=0 ++ && p->y.pTab->aCol!=0 /* Possible due to prior error */ ++ && ALWAYS(p->iColumny.pTab->nCol) + && p->y.pTab->aCol[p->iColumn].notNull==0); + default: + return 1; +@@ -102069,21 +113301,36 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ + return 0; + } + ++/* ++** Return a pointer to a buffer containing a usable rowid alias for table ++** pTab. An alias is usable if there is not an explicit user-defined column ++** of the same name. ++*/ ++SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab){ ++ const char *azOpt[] = {"_ROWID_", "ROWID", "OID"}; ++ int ii; ++ assert( VisibleRowid(pTab) ); ++ for(ii=0; iix.pSelect; + if( p->pPrior ) return 0; /* Not a compound SELECT */ +@@ -102098,10 +113345,10 @@ static Select *isCandidateForInOpt(Expr *pX){ + pSrc = p->pSrc; + assert( pSrc!=0 ); + if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */ +- if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */ +- pTab = pSrc->a[0].pTab; ++ if( pSrc->a[0].fg.isSubquery) return 0;/* FROM is not a subquery or view */ ++ pTab = pSrc->a[0].pSTab; + assert( pTab!=0 ); +- assert( pTab->pSelect==0 ); /* FROM clause is not a view */ ++ assert( !IsView(pTab) ); /* FROM clause is not a view */ + if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ + pEList = p->pEList; + assert( pEList!=0 ); +@@ -102139,13 +113386,13 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){ + ** The argument is an IN operator with a list (not a subquery) on the + ** right-hand side. Return TRUE if that list is constant. + */ +-static int sqlite3InRhsIsConstant(Expr *pIn){ ++static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){ + Expr *pLHS; + int res; + assert( !ExprHasProperty(pIn, EP_xIsSelect) ); + pLHS = pIn->pLeft; + pIn->pLeft = 0; +- res = sqlite3ExprIsConstant(pIn); ++ res = sqlite3ExprIsConstant(pParse, pIn); + pIn->pLeft = pLHS; + return res; + } +@@ -102161,7 +113408,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ + ** all members of the RHS set, skipping duplicates. + ** + ** A cursor is opened on the b-tree object that is the RHS of the IN operator +-** and pX->iTable is set to the index of that cursor. ++** and the *piTab parameter is set to the index of that cursor. + ** + ** The returned value of this function indicates the b-tree type, as follows: + ** +@@ -102169,7 +113416,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ + ** IN_INDEX_INDEX_ASC - The cursor was opened on an ascending index. + ** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index. + ** IN_INDEX_EPH - The cursor was opened on a specially created and +-** populated epheremal table. ++** populated ephemeral table. + ** IN_INDEX_NOOP - No cursor was allocated. The IN operator must be + ** implemented as a sequence of comparisons. + ** +@@ -102181,7 +113428,10 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ + ** If the RHS of the IN operator is a list or a more complex subquery, then + ** an ephemeral table might need to be generated from the RHS and then + ** pX->iTable made to point to the ephemeral table instead of an +-** existing table. ++** existing table. In this case, the creation and initialization of the ++** ephemeral table might be put inside of a subroutine, the EP_Subrtn flag ++** will be set on pX and the pX->y.sub fields will be set to show where ++** the subroutine is coded. + ** + ** The inFlags parameter must contain, at a minimum, one of the bits + ** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP but not both. If inFlags contains +@@ -102191,12 +113441,12 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ + ** + ** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate + ** through the set members) then the b-tree must not contain duplicates. +-** An epheremal table will be created unless the selected columns are guaranteed ++** An ephemeral table will be created unless the selected columns are guaranteed + ** to be unique - either because it is an INTEGER PRIMARY KEY or due to + ** a UNIQUE constraint or index. + ** + ** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used +-** for fast set membership tests) then an epheremal table must ++** for fast set membership tests) then an ephemeral table must + ** be used unless is a single INTEGER PRIMARY KEY column or an + ** index can be found with the specified as its left-most. + ** +@@ -102242,19 +113492,20 @@ SQLITE_PRIVATE int sqlite3FindInIndex( + ){ + Select *p; /* SELECT to the right of IN operator */ + int eType = 0; /* Type of RHS table. IN_INDEX_* */ +- int iTab = pParse->nTab++; /* Cursor of the RHS table */ ++ int iTab; /* Cursor of the RHS table */ + int mustBeUnique; /* True if RHS must be unique */ + Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ + + assert( pX->op==TK_IN ); + mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; ++ iTab = pParse->nTab++; + + /* If the RHS of this IN(...) operator is a SELECT, and if it matters + ** whether or not the SELECT result contains NULL values, check whether + ** or not NULL is actually possible (it may not be, for example, due + ** to NOT NULL constraints in the schema). If no NULL values are possible, + ** set prRhsHasNull to 0 before continuing. */ +- if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){ ++ if( prRhsHasNull && ExprUseXSelect(pX) ){ + int i; + ExprList *pEList = pX->x.pSelect->pEList; + for(i=0; inExpr; i++){ +@@ -102278,11 +113529,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex( + assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ + assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ + assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ +- pTab = p->pSrc->a[0].pTab; ++ pTab = p->pSrc->a[0].pSTab; + + /* Code an OP_Transaction and OP_TableLock for . */ + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); +- assert( iDb>=0 && iDb=0 && iDbtnum, 0, pTab->zName); + +@@ -102355,7 +113606,6 @@ SQLITE_PRIVATE int sqlite3FindInIndex( + CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); + int j; + +- assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr ); + for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; + assert( pIdx->azColl[j] ); +@@ -102371,6 +113621,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( + if( aiMap ) aiMap[i] = j; + } + ++ assert( nExpr>0 && nExprx.pList->nExpr<=2) ++ && ExprUseXList(pX) ++ && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2) + ){ ++ pParse->nTab--; /* Back out the allocation of the unused cursor */ ++ iTab = -1; /* Cursor is not allocated */ + eType = IN_INDEX_NOOP; + } + +@@ -102455,14 +113708,14 @@ SQLITE_PRIVATE int sqlite3FindInIndex( + ** It is the responsibility of the caller to ensure that the returned + ** string is eventually freed using sqlite3DbFree(). + */ +-static char *exprINAffinity(Parse *pParse, Expr *pExpr){ ++static char *exprINAffinity(Parse *pParse, const Expr *pExpr){ + Expr *pLeft = pExpr->pLeft; + int nVal = sqlite3ExprVectorSize(pLeft); +- Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; ++ Select *pSelect = ExprUseXSelect(pExpr) ? pExpr->x.pSelect : 0; + char *zRet; + + assert( pExpr->op==TK_IN ); +- zRet = sqlite3DbMallocRaw(pParse->db, nVal+1); ++ zRet = sqlite3DbMallocRaw(pParse->db, 1+(i64)nVal); + if( zRet ){ + int i; + for(i=0; iflags & EP_xIsSelect ){ ++ if( ExprUseXSelect(pExpr) ){ + sqlite3SubselectError(pParse, pExpr->x.pSelect->pEList->nExpr, 1); + }else + #endif +@@ -102517,6 +113770,50 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ + } + } + ++#ifndef SQLITE_OMIT_SUBQUERY ++/* ++** Scan all previously generated bytecode looking for an OP_BeginSubrtn ++** that is compatible with pExpr. If found, add the y.sub values ++** to pExpr and return true. If not found, return false. ++*/ ++static int findCompatibleInRhsSubrtn( ++ Parse *pParse, /* Parsing context */ ++ Expr *pExpr, /* IN operator with RHS that we want to reuse */ ++ SubrtnSig *pNewSig /* Signature for the IN operator */ ++){ ++ VdbeOp *pOp, *pEnd; ++ SubrtnSig *pSig; ++ Vdbe *v; ++ ++ if( pNewSig==0 ) return 0; ++ if( (pParse->mSubrtnSig & (1<<(pNewSig->selId&7)))==0 ) return 0; ++ assert( pExpr->op==TK_IN ); ++ assert( !ExprUseYSub(pExpr) ); ++ assert( ExprUseXSelect(pExpr) ); ++ assert( pExpr->x.pSelect!=0 ); ++ assert( (pExpr->x.pSelect->selFlags & SF_All)==0 ); ++ v = pParse->pVdbe; ++ assert( v!=0 ); ++ pOp = sqlite3VdbeGetOp(v, 1); ++ pEnd = sqlite3VdbeGetLastOp(v); ++ for(; pOpp4type!=P4_SUBRTNSIG ) continue; ++ assert( pOp->opcode==OP_BeginSubrtn ); ++ pSig = pOp->p4.pSubrtnSig; ++ assert( pSig!=0 ); ++ if( !pSig->bComplete ) continue; ++ if( pNewSig->selId!=pSig->selId ) continue; ++ if( strcmp(pNewSig->zAff,pSig->zAff)!=0 ) continue; ++ pExpr->y.sub.iAddr = pSig->iAddr; ++ pExpr->y.sub.regReturn = pSig->regReturn; ++ pExpr->iTable = pSig->iTable; ++ ExprSetProperty(pExpr, EP_Subrtn); ++ return 1; ++ } ++ return 0; ++} ++#endif /* SQLITE_OMIT_SUBQUERY */ ++ + #ifndef SQLITE_OMIT_SUBQUERY + /* + ** Generate code that will construct an ephemeral table containing all terms +@@ -102527,7 +113824,7 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ + ** x IN (SELECT a FROM b) -- IN operator with subquery on the right + ** + ** The pExpr parameter is the IN operator. The cursor number for the +-** constructed ephermeral table is returned. The first time the ephemeral ++** constructed ephemeral table is returned. The first time the ephemeral + ** table is computed, the cursor number is also stored in pExpr->iTable, + ** however the cursor number returned might not be the same, as it might + ** have been duplicated using OP_OpenDup. +@@ -102551,6 +113848,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + KeyInfo *pKeyInfo = 0; /* Key information */ + int nVal; /* Size of vector pLeft */ + Vdbe *v; /* The prepared statement under construction */ ++ SubrtnSig *pSig = 0; /* Signature for this subroutine */ + + v = pParse->pVdbe; + assert( v!=0 ); +@@ -102566,31 +113864,60 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + ** and reuse it many names. + */ + if( !ExprHasProperty(pExpr, EP_VarSelect) && pParse->iSelfTab==0 ){ +- /* Reuse of the RHS is allowed */ +- /* If this routine has already been coded, but the previous code +- ** might not have been invoked yet, so invoke it now as a subroutine. ++ /* Reuse of the RHS is allowed ++ ** ++ ** Compute a signature for the RHS of the IN operator to facility ++ ** finding and reusing prior instances of the same IN operator. + */ +- if( ExprHasProperty(pExpr, EP_Subrtn) ){ ++ assert( !ExprUseXSelect(pExpr) || pExpr->x.pSelect!=0 ); ++ if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_All)==0 ){ ++ pSig = sqlite3DbMallocRawNN(pParse->db, sizeof(pSig[0])); ++ if( pSig ){ ++ pSig->selId = pExpr->x.pSelect->selId; ++ pSig->zAff = exprINAffinity(pParse, pExpr); ++ } ++ } ++ ++ /* Check to see if there is a prior materialization of the RHS of ++ ** this IN operator. If there is, then make use of that prior ++ ** materialization rather than recomputing it. ++ */ ++ if( ExprHasProperty(pExpr, EP_Subrtn) ++ || findCompatibleInRhsSubrtn(pParse, pExpr, pSig) ++ ){ + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", + pExpr->x.pSelect->selId)); + } ++ assert( ExprUseYSub(pExpr) ); + sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr); ++ assert( iTab!=pExpr->iTable ); + sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); + sqlite3VdbeJumpHere(v, addrOnce); ++ if( pSig ){ ++ sqlite3DbFree(pParse->db, pSig->zAff); ++ sqlite3DbFree(pParse->db, pSig); ++ } + return; + } + + /* Begin coding the subroutine */ ++ assert( !ExprUseYWin(pExpr) ); + ExprSetProperty(pExpr, EP_Subrtn); + assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); + pExpr->y.sub.regReturn = ++pParse->nMem; + pExpr->y.sub.iAddr = +- sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; +- VdbeComment((v, "return address")); +- ++ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; ++ if( pSig ){ ++ pSig->bComplete = 0; ++ pSig->iAddr = pExpr->y.sub.iAddr; ++ pSig->regReturn = pExpr->y.sub.regReturn; ++ pSig->iTable = iTab; ++ pParse->mSubrtnSig = 1 << (pSig->selId&7); ++ sqlite3VdbeChangeP4(v, -1, (const char*)pSig, P4_SUBRTNSIG); ++ } + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + } + +@@ -102604,7 +113931,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + pExpr->iTable = iTab; + addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal); + #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId)); + }else{ + VdbeComment((v, "RHS of IN operator")); +@@ -102612,7 +113939,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + #endif + pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1); + +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + /* Case 1: expr IN (SELECT ...) + ** + ** Generate code to write the results of the select into the temporary +@@ -102627,19 +113954,39 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + /* If the LHS and RHS of the IN operator do not match, that + ** error will have been caught long before we reach this point. */ + if( ALWAYS(pEList->nExpr==nVal) ){ ++ Select *pCopy; + SelectDest dest; + int i; ++ int rc; ++ int addrBloom = 0; + sqlite3SelectDestInit(&dest, SRT_Set, iTab); + dest.zAffSdst = exprINAffinity(pParse, pExpr); + pSelect->iLimit = 0; ++ if( addrOnce && OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ ++ int regBloom = ++pParse->nMem; ++ addrBloom = sqlite3VdbeAddOp2(v, OP_Blob, 10000, regBloom); ++ VdbeComment((v, "Bloom filter")); ++ dest.iSDParm2 = regBloom; ++ } + testcase( pSelect->selFlags & SF_Distinct ); + testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ +- if( sqlite3Select(pParse, pSelect, &dest) ){ +- sqlite3DbFree(pParse->db, dest.zAffSdst); ++ pCopy = sqlite3SelectDup(pParse->db, pSelect, 0); ++ rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest); ++ sqlite3SelectDelete(pParse->db, pCopy); ++ sqlite3DbFree(pParse->db, dest.zAffSdst); ++ if( addrBloom ){ ++ /* Remember that location of the Bloom filter in the P3 operand ++ ** of the OP_Once that began this subroutine. tag-202407032019 */ ++ sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2; ++ if( dest.iSDParm2==0 ){ ++ /* If the Bloom filter won't actually be used, keep it small */ ++ sqlite3VdbeGetOp(v, addrBloom)->p1 = 10; ++ } ++ } ++ if( rc ){ + sqlite3KeyInfoUnref(pKeyInfo); + return; + } +- sqlite3DbFree(pParse->db, dest.zAffSdst); + assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ + assert( pEList!=0 ); + assert( pEList->nExpr>0 ); +@@ -102686,7 +114033,8 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + ** this code only executes once. Because for a non-constant + ** expression we need to rerun this code each time. + */ +- if( addrOnce && !sqlite3ExprIsConstant(pE2) ){ ++ if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){ ++ sqlite3VdbeChangeToNoop(v, addrOnce-1); + sqlite3VdbeChangeToNoop(v, addrOnce); + ExprClearProperty(pExpr, EP_Subrtn); + addrOnce = 0; +@@ -102700,14 +114048,20 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + sqlite3ReleaseTempReg(pParse, r1); + sqlite3ReleaseTempReg(pParse, r2); + } ++ if( pSig ) pSig->bComplete = 1; + if( pKeyInfo ){ + sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); + } + if( addrOnce ){ ++ sqlite3VdbeAddOp1(v, OP_NullRow, iTab); + sqlite3VdbeJumpHere(v, addrOnce); + /* Subroutine return */ +- sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); +- sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); ++ assert( ExprUseYSub(pExpr) ); ++ assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn ++ || pParse->nErr ); ++ sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, ++ pExpr->y.sub.iAddr, 1); ++ VdbeCoverage(v); + sqlite3ClearTempRegCache(pParse); + } + } +@@ -102735,15 +114089,37 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + SelectDest dest; /* How to deal with SELECT result */ + int nReg; /* Registers to allocate */ + Expr *pLimit; /* New limit expression */ ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ int addrExplain; /* Address of OP_Explain instruction */ ++#endif + + Vdbe *v = pParse->pVdbe; + assert( v!=0 ); ++ if( pParse->nErr ) return 0; + testcase( pExpr->op==TK_EXISTS ); + testcase( pExpr->op==TK_SELECT ); + assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); +- assert( ExprHasProperty(pExpr, EP_xIsSelect) ); ++ assert( ExprUseXSelect(pExpr) ); + pSel = pExpr->x.pSelect; + ++ /* If this routine has already been coded, then invoke it as a ++ ** subroutine. */ ++ if( ExprHasProperty(pExpr, EP_Subrtn) ){ ++ ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); ++ assert( ExprUseYSub(pExpr) ); ++ sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, ++ pExpr->y.sub.iAddr); ++ return pExpr->iTable; ++ } ++ ++ /* Begin coding the subroutine */ ++ assert( !ExprUseYWin(pExpr) ); ++ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); ++ ExprSetProperty(pExpr, EP_Subrtn); ++ pExpr->y.sub.regReturn = ++pParse->nMem; ++ pExpr->y.sub.iAddr = ++ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; ++ + /* The evaluation of the EXISTS/SELECT must be repeated every time it + ** is encountered if any of the following is true: + ** +@@ -102755,22 +114131,6 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + ** save the results, and reuse the same result on subsequent invocations. + */ + if( !ExprHasProperty(pExpr, EP_VarSelect) ){ +- /* If this routine has already been coded, then invoke it as a +- ** subroutine. */ +- if( ExprHasProperty(pExpr, EP_Subrtn) ){ +- ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); +- sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, +- pExpr->y.sub.iAddr); +- return pExpr->iTable; +- } +- +- /* Begin coding the subroutine */ +- ExprSetProperty(pExpr, EP_Subrtn); +- pExpr->y.sub.regReturn = ++pParse->nMem; +- pExpr->y.sub.iAddr = +- sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; +- VdbeComment((v, "return address")); +- + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + } + +@@ -102784,8 +114144,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + ** In both cases, the query is augmented with "LIMIT 1". Any + ** preexisting limit is discarded in place of the new LIMIT 1. + */ +- ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY %d", ++ ExplainQueryPlan2(addrExplain, (pParse, 1, "%sSCALAR SUBQUERY %d", + addrOnce?"":"CORRELATED ", pSel->selId)); ++ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, -1); + nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; + sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); + pParse->nMem += nReg; +@@ -102810,7 +114171,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + pLimit = sqlite3PExpr(pParse, TK_NE, + sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit); + } +- sqlite3ExprDelete(db, pSel->pLimit->pLeft); ++ sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft); + pSel->pLimit->pLeft = pLimit; + }else{ + /* If there is no pre-existing limit add a limit of 1 */ +@@ -102819,19 +114180,25 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + } + pSel->iLimit = 0; + if( sqlite3Select(pParse, pSel, &dest) ){ ++ pExpr->op2 = pExpr->op; ++ pExpr->op = TK_ERROR; + return 0; + } + pExpr->iTable = rReg = dest.iSDParm; + ExprSetVVAProperty(pExpr, EP_NoReduce); + if( addrOnce ){ + sqlite3VdbeJumpHere(v, addrOnce); +- +- /* Subroutine return */ +- sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); +- sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); +- sqlite3ClearTempRegCache(pParse); + } ++ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); + ++ /* Subroutine return */ ++ assert( ExprUseYSub(pExpr) ); ++ assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn ++ || pParse->nErr ); ++ sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, ++ pExpr->y.sub.iAddr, 1); ++ VdbeCoverage(v); ++ sqlite3ClearTempRegCache(pParse); + return rReg; + } + #endif /* SQLITE_OMIT_SUBQUERY */ +@@ -102845,7 +114212,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + */ + SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ + int nVector = sqlite3ExprVectorSize(pIn->pLeft); +- if( (pIn->flags & EP_xIsSelect) ){ ++ if( ExprUseXSelect(pIn) && !pParse->db->mallocFailed ){ + if( nVector!=pIn->x.pSelect->pEList->nExpr ){ + sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector); + return 1; +@@ -102914,9 +114281,7 @@ static void sqlite3ExprCodeIN( + if( sqlite3ExprCheckIN(pParse, pExpr) ) return; + zAff = exprINAffinity(pParse, pExpr); + nVector = sqlite3ExprVectorSize(pExpr->pLeft); +- aiMap = (int*)sqlite3DbMallocZero( +- pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 +- ); ++ aiMap = (int*)sqlite3DbMallocZero(pParse->db, nVector*sizeof(int)); + if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error; + + /* Attempt to compute the RHS. After this step, if anything other than +@@ -102979,13 +114344,15 @@ static void sqlite3ExprCodeIN( + ** This is step (1) in the in-operator.md optimized algorithm. + */ + if( eType==IN_INDEX_NOOP ){ +- ExprList *pList = pExpr->x.pList; +- CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); ++ ExprList *pList; ++ CollSeq *pColl; + int labelOk = sqlite3VdbeMakeLabel(pParse); + int r2, regToFree; + int regCkNull = 0; + int ii; +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); ++ assert( ExprUseXList(pExpr) ); ++ pList = pExpr->x.pList; ++ pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); + if( destIfNull!=destIfFalse ){ + regCkNull = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); +@@ -103033,9 +114400,9 @@ static void sqlite3ExprCodeIN( + }else{ + destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse); + } +- if( pParse->nErr ) goto sqlite3ExprCodeIN_finished; + for(i=0; ipLeft, i); ++ if( pParse->nErr ) goto sqlite3ExprCodeIN_oom_error; + if( sqlite3ExprCanBeNull(p) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2); + VdbeCoverage(v); +@@ -103057,6 +114424,15 @@ static void sqlite3ExprCodeIN( + sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector); + if( destIfFalse==destIfNull ){ + /* Combine Step 3 and Step 5 into a single opcode */ ++ if( ExprHasProperty(pExpr, EP_Subrtn) ){ ++ const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); ++ assert( pOp->opcode==OP_Once || pParse->nErr ); ++ if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */ ++ assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ); ++ sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, ++ rLhs, nVector); VdbeCoverage(v); ++ } ++ } + sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, + rLhs, nVector); VdbeCoverage(v); + goto sqlite3ExprCodeIN_finished; +@@ -103173,11 +114549,12 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ + c = sqlite3DecOrHexToI64(z, &value); + if( (c==3 && !negFlag) || (c==2) || (negFlag && value==SMALLEST_INT64)){ + #ifdef SQLITE_OMIT_FLOATING_POINT +- sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); ++ sqlite3ErrorMsg(pParse, "oversized integer: %s%#T", negFlag?"-":"",pExpr); + #else + #ifndef SQLITE_OMIT_HEX_INTEGER + if( sqlite3_strnicmp(z,"0x",2)==0 ){ +- sqlite3ErrorMsg(pParse, "hex literal too big: %s%s", negFlag?"-":"",z); ++ sqlite3ErrorMsg(pParse, "hex literal too big: %s%#T", ++ negFlag?"-":"",pExpr); + }else + #endif + { +@@ -103221,12 +114598,14 @@ SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn( + ** and store the result in register regOut + */ + SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn( +- Parse *pParse, +- Column *pCol, +- int regOut ++ Parse *pParse, /* Parsing context */ ++ Table *pTab, /* Table containing the generated column */ ++ Column *pCol, /* The generated column */ ++ int regOut /* Put the result in this register */ + ){ + int iAddr; + Vdbe *v = pParse->pVdbe; ++ int nErr = pParse->nErr; + assert( v!=0 ); + assert( pParse->iSelfTab!=0 ); + if( pParse->iSelfTab>0 ){ +@@ -103234,11 +114613,12 @@ SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn( + }else{ + iAddr = 0; + } +- sqlite3ExprCodeCopy(pParse, pCol->pDflt, regOut); ++ sqlite3ExprCodeCopy(pParse, sqlite3ColumnExpr(pTab,pCol), regOut); + if( pCol->affinity>=SQLITE_AFF_TEXT ){ + sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1); + } + if( iAddr ) sqlite3VdbeJumpHere(v, iAddr); ++ if( pParse->nErr>nErr ) pParse->db->errByteOffset = -1; + } + #endif /* SQLITE_OMIT_GENERATED_COLUMNS */ + +@@ -103254,12 +114634,11 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable( + ){ + Column *pCol; + assert( v!=0 ); +- if( pTab==0 ){ +- sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut); +- return; +- } ++ assert( pTab!=0 ); ++ assert( iCol!=XN_EXPR ); + if( iCol<0 || iCol==pTab->iPKey ){ + sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); ++ VdbeComment((v, "%s.rowid", pTab->zName)); + }else{ + int op; + int x; +@@ -103270,12 +114649,13 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable( + }else if( (pCol = &pTab->aCol[iCol])->colFlags & COLFLAG_VIRTUAL ){ + Parse *pParse = sqlite3VdbeParser(v); + if( pCol->colFlags & COLFLAG_BUSY ){ +- sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pCol->zName); ++ sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", ++ pCol->zCnName); + }else{ + int savedSelfTab = pParse->iSelfTab; + pCol->colFlags |= COLFLAG_BUSY; + pParse->iSelfTab = iTabCur+1; +- sqlite3ExprCodeGeneratedColumn(pParse, pCol, regOut); ++ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, regOut); + pParse->iSelfTab = savedSelfTab; + pCol->colFlags &= ~COLFLAG_BUSY; + } +@@ -103311,10 +114691,13 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn( + u8 p5 /* P5 value for OP_Column + FLAGS */ + ){ + assert( pParse->pVdbe!=0 ); ++ assert( (p5 & (OPFLAG_NOCHNG|OPFLAG_TYPEOFARG|OPFLAG_LENGTHARG))==p5 ); ++ assert( IsVirtual(pTab) || (p5 & OPFLAG_NOCHNG)==0 ); + sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pTab, iTable, iColumn, iReg); + if( p5 ){ +- VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe,-1); ++ VdbeOp *pOp = sqlite3VdbeGetLastOp(pParse->pVdbe); + if( pOp->opcode==OP_Column ) pOp->p5 = p5; ++ if( pOp->opcode==OP_VColumn ) pOp->p5 = (p5 & OPFLAG_NOCHNG); + } + return iReg; + } +@@ -103332,17 +114715,22 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int n + ** register iReg. The caller must ensure that iReg already contains + ** the correct value for the expression. + */ +-static void exprToRegister(Expr *pExpr, int iReg){ ++SQLITE_PRIVATE void sqlite3ExprToRegister(Expr *pExpr, int iReg){ + Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); +- p->op2 = p->op; +- p->op = TK_REGISTER; +- p->iTable = iReg; +- ExprClearProperty(p, EP_Skip); ++ if( NEVER(p==0) ) return; ++ if( p->op==TK_REGISTER ){ ++ assert( p->iTable==iReg ); ++ }else{ ++ p->op2 = p->op; ++ p->op = TK_REGISTER; ++ p->iTable = iReg; ++ ExprClearProperty(p, EP_Skip); ++ } + } + + /* + ** Evaluate an expression (either a vector or a scalar expression) and store +-** the result in continguous temporary registers. Return the index of ++** the result in contiguous temporary registers. Return the index of + ** the first register used to store the result. + ** + ** If the returned result register is a temporary scalar, then also write +@@ -103367,6 +114755,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ + int i; + iResult = pParse->nMem+1; + pParse->nMem += nResult; ++ assert( ExprUseXList(p) ); + for(i=0; ix.pList->a[i].pExpr, i+iResult); + } +@@ -103380,8 +114769,8 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ + ** so that a subsequent copy will not be merged into this one. + */ + static void setDoNotMergeFlagOnCopy(Vdbe *v){ +- if( sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){ +- sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */ ++ if( sqlite3VdbeGetLastOp(v)->opcode==OP_Copy ){ ++ sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergeable */ + } + } + +@@ -103427,7 +114816,17 @@ static int exprCodeInlineFunction( + caseExpr.x.pList = pFarg; + return sqlite3ExprCodeTarget(pParse, &caseExpr, target); + } +- ++#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC ++ case INLINEFUNC_sqlite_offset: { ++ Expr *pArg = pFarg->a[0].pExpr; ++ if( pArg->op==TK_COLUMN && pArg->iTable>=0 ){ ++ sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); ++ }else{ ++ sqlite3VdbeAddOp2(v, OP_Null, 0, target); ++ } ++ break; ++ } ++#endif + default: { + /* The UNLIKELY() function is a no-op. The result is the value + ** of the first argument. +@@ -103441,6 +114840,7 @@ static int exprCodeInlineFunction( + ** Test-only SQL functions that are only usable if enabled + ** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS + */ ++#if !defined(SQLITE_UNTESTABLE) + case INLINEFUNC_expr_compare: { + /* Compare two expressions using sqlite3ExprCompare() */ + assert( nFarg==2 ); +@@ -103460,13 +114860,13 @@ static int exprCodeInlineFunction( + } + + case INLINEFUNC_implies_nonnull_row: { +- /* REsult of sqlite3ExprImpliesNonNullRow() */ ++ /* Result of sqlite3ExprImpliesNonNullRow() */ + Expr *pA1; + assert( nFarg==2 ); + pA1 = pFarg->a[1].pExpr; + if( pA1->op==TK_COLUMN ){ + sqlite3VdbeAddOp2(v, OP_Integer, +- sqlite3ExprImpliesNonNullRow(pFarg->a[0].pExpr,pA1->iTable), ++ sqlite3ExprImpliesNonNullRow(pFarg->a[0].pExpr,pA1->iTable,1), + target); + }else{ + sqlite3VdbeAddOp2(v, OP_Null, 0, target); +@@ -103474,25 +114874,184 @@ static int exprCodeInlineFunction( + break; + } + +-#ifdef SQLITE_DEBUG + case INLINEFUNC_affinity: { + /* The AFFINITY() function evaluates to a string that describes + ** the type affinity of the argument. This is used for testing of + ** the SQLite type logic. + */ +- const char *azAff[] = { "blob", "text", "numeric", "integer", "real" }; ++ const char *azAff[] = { "blob", "text", "numeric", "integer", ++ "real", "flexnum" }; + char aff; + assert( nFarg==1 ); + aff = sqlite3ExprAffinity(pFarg->a[0].pExpr); ++ assert( aff<=SQLITE_AFF_NONE ++ || (aff>=SQLITE_AFF_BLOB && aff<=SQLITE_AFF_FLEXNUM) ); + sqlite3VdbeLoadString(v, target, + (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]); + break; + } +-#endif ++#endif /* !defined(SQLITE_UNTESTABLE) */ + } + return target; + } + ++/* ++** Expression Node callback for sqlite3ExprCanReturnSubtype(). ++** ++** Only a function call is able to return a subtype. So if the node ++** is not a function call, return WRC_Prune immediately. ++** ++** A function call is able to return a subtype if it has the ++** SQLITE_RESULT_SUBTYPE property. ++** ++** Assume that every function is able to pass-through a subtype from ++** one of its argument (using sqlite3_result_value()). Most functions ++** are not this way, but we don't have a mechanism to distinguish those ++** that are from those that are not, so assume they all work this way. ++** That means that if one of its arguments is another function and that ++** other function is able to return a subtype, then this function is ++** able to return a subtype. ++*/ ++static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ ++ int n; ++ FuncDef *pDef; ++ sqlite3 *db; ++ if( pExpr->op!=TK_FUNCTION ){ ++ return WRC_Prune; ++ } ++ assert( ExprUseXList(pExpr) ); ++ db = pWalker->pParse->db; ++ n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; ++ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); ++ if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ ++ pWalker->eCode = 1; ++ return WRC_Prune; ++ } ++ return WRC_Continue; ++} ++ ++/* ++** Return TRUE if expression pExpr is able to return a subtype. ++** ++** A TRUE return does not guarantee that a subtype will be returned. ++** It only indicates that a subtype return is possible. False positives ++** are acceptable as they only disable an optimization. False negatives, ++** on the other hand, can lead to incorrect answers. ++*/ ++static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ ++ Walker w; ++ memset(&w, 0, sizeof(w)); ++ w.pParse = pParse; ++ w.xExprCallback = exprNodeCanReturnSubtype; ++ sqlite3WalkExpr(&w, pExpr); ++ return w.eCode; ++} ++ ++ ++/* ++** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr. ++** If it is, then resolve the expression by reading from the index and ++** return the register into which the value has been read. If pExpr is ++** not an indexed expression, then return negative. ++*/ ++static SQLITE_NOINLINE int sqlite3IndexedExprLookup( ++ Parse *pParse, /* The parsing context */ ++ Expr *pExpr, /* The expression to potentially bypass */ ++ int target /* Where to store the result of the expression */ ++){ ++ IndexedExpr *p; ++ Vdbe *v; ++ for(p=pParse->pIdxEpr; p; p=p->pIENext){ ++ u8 exprAff; ++ int iDataCur = p->iDataCur; ++ if( iDataCur<0 ) continue; ++ if( pParse->iSelfTab ){ ++ if( p->iDataCur!=pParse->iSelfTab-1 ) continue; ++ iDataCur = -1; ++ } ++ if( sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue; ++ assert( p->aff>=SQLITE_AFF_BLOB && p->aff<=SQLITE_AFF_NUMERIC ); ++ exprAff = sqlite3ExprAffinity(pExpr); ++ if( (exprAff<=SQLITE_AFF_BLOB && p->aff!=SQLITE_AFF_BLOB) ++ || (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT) ++ || (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC) ++ ){ ++ /* Affinity mismatch on a generated column */ ++ continue; ++ } ++ ++ ++ /* Functions that might set a subtype should not be replaced by the ++ ** value taken from an expression index if they are themselves an ++ ** argument to another scalar function or aggregate. ++ ** https://sqlite.org/forum/forumpost/68d284c86b082c3e */ ++ if( ExprHasProperty(pExpr, EP_SubtArg) ++ && sqlite3ExprCanReturnSubtype(pParse, pExpr) ++ ){ ++ continue; ++ } ++ ++ v = pParse->pVdbe; ++ assert( v!=0 ); ++ if( p->bMaybeNullRow ){ ++ /* If the index is on a NULL row due to an outer join, then we ++ ** cannot extract the value from the index. The value must be ++ ** computed using the original expression. */ ++ int addr = sqlite3VdbeCurrentAddr(v); ++ sqlite3VdbeAddOp3(v, OP_IfNullRow, p->iIdxCur, addr+3, target); ++ VdbeCoverage(v); ++ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target); ++ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol)); ++ sqlite3VdbeGoto(v, 0); ++ p = pParse->pIdxEpr; ++ pParse->pIdxEpr = 0; ++ sqlite3ExprCode(pParse, pExpr, target); ++ pParse->pIdxEpr = p; ++ sqlite3VdbeJumpHere(v, addr+2); ++ }else{ ++ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target); ++ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol)); ++ } ++ return target; ++ } ++ return -1; /* Not found */ ++} ++ ++ ++/* ++** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This ++** function checks the Parse.pIdxPartExpr list to see if this column ++** can be replaced with a constant value. If so, it generates code to ++** put the constant value in a register (ideally, but not necessarily, ++** register iTarget) and returns the register number. ++** ++** Or, if the TK_COLUMN cannot be replaced by a constant, zero is ++** returned. ++*/ ++static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){ ++ IndexedExpr *p; ++ for(p=pParse->pIdxPartExpr; p; p=p->pIENext){ ++ if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){ ++ Vdbe *v = pParse->pVdbe; ++ int addr = 0; ++ int ret; ++ ++ if( p->bMaybeNullRow ){ ++ addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur); ++ } ++ ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget); ++ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0, ++ (const char*)&p->aff, 1); ++ if( addr ){ ++ sqlite3VdbeJumpHere(v, addr); ++ sqlite3VdbeChangeP3(v, addr, ret); ++ } ++ return ret; ++ } ++ } ++ return 0; ++} ++ + + /* + ** Generate code into the current Vdbe to evaluate the given +@@ -103521,33 +115080,58 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) + expr_code_doover: + if( pExpr==0 ){ + op = TK_NULL; ++ }else if( pParse->pIdxEpr!=0 ++ && !ExprHasProperty(pExpr, EP_Leaf) ++ && (r1 = sqlite3IndexedExprLookup(pParse, pExpr, target))>=0 ++ ){ ++ return r1; + }else{ + assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); + op = pExpr->op; + } ++ assert( op!=TK_ORDER ); + switch( op ){ + case TK_AGG_COLUMN: { + AggInfo *pAggInfo = pExpr->pAggInfo; + struct AggInfo_col *pCol; + assert( pAggInfo!=0 ); +- assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); ++ assert( pExpr->iAgg>=0 ); ++ if( pExpr->iAgg>=pAggInfo->nColumn ){ ++ /* Happens when the left table of a RIGHT JOIN is null and ++ ** is using an expression index */ ++ sqlite3VdbeAddOp2(v, OP_Null, 0, target); ++#ifdef SQLITE_VDBE_COVERAGE ++ /* Verify that the OP_Null above is exercised by tests ++ ** tag-20230325-2 */ ++ sqlite3VdbeAddOp3(v, OP_NotNull, target, 1, 20230325); ++ VdbeCoverageNeverTaken(v); ++#endif ++ break; ++ } + pCol = &pAggInfo->aCol[pExpr->iAgg]; + if( !pAggInfo->directMode ){ +- assert( pCol->iMem>0 ); +- return pCol->iMem; ++ return AggInfoColumnReg(pAggInfo, pExpr->iAgg); + }else if( pAggInfo->useSortingIdx ){ + Table *pTab = pCol->pTab; + sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, + pCol->iSorterColumn, target); +- if( pCol->iColumn<0 ){ ++ if( pTab==0 ){ ++ /* No comment added */ ++ }else if( pCol->iColumn<0 ){ + VdbeComment((v,"%s.rowid",pTab->zName)); + }else{ +- VdbeComment((v,"%s.%s",pTab->zName,pTab->aCol[pCol->iColumn].zName)); ++ VdbeComment((v,"%s.%s", ++ pTab->zName, pTab->aCol[pCol->iColumn].zCnName)); + if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){ + sqlite3VdbeAddOp1(v, OP_RealAffinity, target); + } + } + return target; ++ }else if( pExpr->y.pTab==0 ){ ++ /* This case happens when the argument to an aggregate function ++ ** is rewritten by aggregateConvertIndexedExprRefToColumn() */ ++ sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, pExpr->iColumn, target); ++ return target; + } + /* Otherwise, fall thru into the TK_COLUMN case */ + /* no break */ deliberate_fall_through +@@ -103558,19 +115142,17 @@ expr_code_doover: + if( ExprHasProperty(pExpr, EP_FixedCol) ){ + /* This COLUMN expression is really a constant due to WHERE clause + ** constraints, and that constant is coded by the pExpr->pLeft +- ** expresssion. However, make sure the constant has the correct ++ ** expression. However, make sure the constant has the correct + ** datatype by applying the Affinity of the table column to the + ** constant. + */ + int aff; + iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target); +- if( pExpr->y.pTab ){ +- aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); +- }else{ +- aff = pExpr->affExpr; +- } ++ assert( ExprUseYTab(pExpr) ); ++ assert( pExpr->y.pTab!=0 ); ++ aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); + if( aff>SQLITE_AFF_BLOB ){ +- static const char zAff[] = "B\000C\000D\000E"; ++ static const char zAff[] = "B\000C\000D\000E\000F"; + assert( SQLITE_AFF_BLOB=='A' ); + assert( SQLITE_AFF_TEXT=='B' ); + sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0, +@@ -103587,9 +115169,11 @@ expr_code_doover: + ** immediately prior to the first column. + */ + Column *pCol; +- Table *pTab = pExpr->y.pTab; ++ Table *pTab; + int iSrc; + int iCol = pExpr->iColumn; ++ assert( ExprUseYTab(pExpr) ); ++ pTab = pExpr->y.pTab; + assert( pTab!=0 ); + assert( iCol>=XN_ROWID ); + assert( iColnCol ); +@@ -103603,12 +115187,12 @@ expr_code_doover: + if( pCol->colFlags & COLFLAG_GENERATED ){ + if( pCol->colFlags & COLFLAG_BUSY ){ + sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", +- pCol->zName); ++ pCol->zCnName); + return 0; + } + pCol->colFlags |= COLFLAG_BUSY; + if( pCol->colFlags & COLFLAG_NOTAVAIL ){ +- sqlite3ExprCodeGeneratedColumn(pParse, pCol, iSrc); ++ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, iSrc); + } + pCol->colFlags &= ~(COLFLAG_BUSY|COLFLAG_NOTAVAIL); + return iSrc; +@@ -103627,12 +115211,16 @@ expr_code_doover: + iTab = pParse->iSelfTab - 1; + } + } ++ else if( pParse->pIdxPartExpr ++ && 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target)) ++ ){ ++ return r1; ++ } ++ assert( ExprUseYTab(pExpr) ); ++ assert( pExpr->y.pTab!=0 ); + iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab, + pExpr->iColumn, iTab, target, + pExpr->op2); +- if( pExpr->y.pTab==0 && pExpr->affExpr==SQLITE_AFF_REAL ){ +- sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); +- } + return iReg; + } + case TK_INTEGER: { +@@ -103655,12 +115243,18 @@ expr_code_doover: + sqlite3VdbeLoadString(v, target, pExpr->u.zToken); + return target; + } ++ case TK_NULLS: { ++ /* Set a range of registers to NULL. pExpr->y.nReg registers starting ++ ** with target */ ++ sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1); ++ return target; ++ } + default: { + /* Make NULL the default case so that if a bug causes an illegal + ** Expr node to be passed into this function, it will be handled + ** sanely and not crash. But keep the assert() to bring the problem + ** to the attention of the developers. */ +- assert( op==TK_NULL ); ++ assert( op==TK_NULL || op==TK_ERROR || pParse->db->mallocFailed ); + sqlite3VdbeAddOp2(v, OP_Null, 0, target); + return target; + } +@@ -103685,12 +115279,6 @@ expr_code_doover: + assert( pExpr->u.zToken!=0 ); + assert( pExpr->u.zToken[0]!=0 ); + sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); +- if( pExpr->u.zToken[1]!=0 ){ +- const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn); +- assert( pExpr->u.zToken[0]=='?' || (z && !strcmp(pExpr->u.zToken, z)) ); +- pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */ +- sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC); +- } + return target; + } + case TK_REGISTER: { +@@ -103699,11 +115287,9 @@ expr_code_doover: + #ifndef SQLITE_OMIT_CAST + case TK_CAST: { + /* Expressions of the form: CAST(pLeft AS token) */ +- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); +- if( inReg!=target ){ +- sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); +- inReg = target; +- } ++ sqlite3ExprCode(pParse, pExpr->pLeft, target); ++ assert( inReg==target ); ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3VdbeAddOp2(v, OP_Cast, target, + sqlite3AffinityType(pExpr->u.zToken, 0)); + return inReg; +@@ -103726,8 +115312,9 @@ expr_code_doover: + }else{ + r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); +- codeCompare(pParse, pLeft, pExpr->pRight, op, +- r1, r2, inReg, SQLITE_STOREP2 | p5, ++ sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg); ++ codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2, ++ sqlite3VdbeCurrentAddr(v)+2, p5, + ExprHasProperty(pExpr,EP_Commuted)); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); +@@ -103735,6 +115322,11 @@ expr_code_doover: + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); ++ if( p5==SQLITE_NULLEQ ){ ++ sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg); ++ }else{ ++ sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2); ++ } + testcase( regFree1==0 ); + testcase( regFree2==0 ); + } +@@ -103837,9 +115429,9 @@ expr_code_doover: + || NEVER(pExpr->iAgg>=pInfo->nFunc) + ){ + assert( !ExprHasProperty(pExpr, EP_IntValue) ); +- sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken); ++ sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr); + }else{ +- return pInfo->aFunc[pExpr->iAgg].iMem; ++ return AggInfoFuncReg(pInfo, pExpr->iAgg); + } + break; + } +@@ -103860,13 +115452,15 @@ expr_code_doover: + } + #endif + +- if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){ ++ if( ConstFactorOk(pParse) ++ && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ++ ){ + /* SQL functions can be expensive. So try to avoid running them + ** multiple times if we know they always give the same result */ + return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); + } +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + assert( !ExprHasProperty(pExpr, EP_TokenOnly) ); ++ assert( ExprUseXList(pExpr) ); + pFarg = pExpr->x.pList; + nFarg = pFarg ? pFarg->nExpr : 0; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); +@@ -103878,10 +115472,10 @@ expr_code_doover: + } + #endif + if( pDef==0 || pDef->xFinalize!=0 ){ +- sqlite3ErrorMsg(pParse, "unknown function: %s()", zId); ++ sqlite3ErrorMsg(pParse, "unknown function: %#T()", pExpr); + break; + } +- if( pDef->funcFlags & SQLITE_FUNC_INLINE ){ ++ if( (pDef->funcFlags & SQLITE_FUNC_INLINE)!=0 && ALWAYS(pFarg!=0) ){ + assert( (pDef->funcFlags & SQLITE_FUNC_UNSAFE)==0 ); + assert( (pDef->funcFlags & SQLITE_FUNC_DIRECT)==0 ); + return exprCodeInlineFunction(pParse, pFarg, +@@ -103891,7 +115485,7 @@ expr_code_doover: + } + + for(i=0; ia[i].pExpr) ){ ++ if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){ + testcase( i==31 ); + constMask |= MASKBIT32(i); + } +@@ -103907,10 +115501,10 @@ expr_code_doover: + r1 = sqlite3GetTempRange(pParse, nFarg); + } + +- /* For length() and typeof() functions with a column argument, ++ /* For length() and typeof() and octet_length() functions, + ** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG +- ** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data +- ** loading. ++ ** or OPFLAG_TYPEOFARG or OPFLAG_BYTELENARG respectively, to avoid ++ ** unnecessary data loading. + */ + if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){ + u8 exprOp; +@@ -103920,14 +115514,16 @@ expr_code_doover: + if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){ + assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG ); + assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG ); +- testcase( pDef->funcFlags & OPFLAG_LENGTHARG ); +- pFarg->a[0].pExpr->op2 = +- pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG); ++ assert( SQLITE_FUNC_BYTELEN==OPFLAG_BYTELENARG ); ++ assert( (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG)==OPFLAG_BYTELENARG ); ++ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_LENGTHARG ); ++ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_TYPEOFARG ); ++ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_BYTELENARG); ++ pFarg->a[0].pExpr->op2 = pDef->funcFlags & OPFLAG_BYTELENARG; + } + } + +- sqlite3ExprCodeExprList(pParse, pFarg, r1, 0, +- SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR); ++ sqlite3ExprCodeExprList(pParse, pFarg, r1, 0, SQLITE_ECEL_FACTOR); + }else{ + r1 = 0; + } +@@ -103954,20 +115550,8 @@ expr_code_doover: + if( !pColl ) pColl = db->pDfltColl; + sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); + } +-#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC +- if( pDef->funcFlags & SQLITE_FUNC_OFFSET ){ +- Expr *pArg = pFarg->a[0].pExpr; +- if( pArg->op==TK_COLUMN ){ +- sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); +- }else{ +- sqlite3VdbeAddOp2(v, OP_Null, 0, target); +- } +- }else +-#endif +- { +- sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, +- pDef, pExpr->op2); +- } ++ sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, ++ pDef, pExpr->op2); + if( nFarg ){ + if( constMask==0 ){ + sqlite3ReleaseTempRange(pParse, r1, nFarg); +@@ -103985,7 +115569,10 @@ expr_code_doover: + testcase( op==TK_SELECT ); + if( pParse->db->mallocFailed ){ + return 0; +- }else if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ ++ }else if( op==TK_SELECT ++ && ALWAYS( ExprUseXSelect(pExpr) ) ++ && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ++ ){ + sqlite3SubselectError(pParse, nCol, 1); + }else{ + return sqlite3CodeSubselect(pParse, pExpr); +@@ -103994,17 +115581,18 @@ expr_code_doover: + } + case TK_SELECT_COLUMN: { + int n; +- if( pExpr->pLeft->iTable==0 ){ +- pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft); ++ Expr *pLeft = pExpr->pLeft; ++ if( pLeft->iTable==0 || pParse->withinRJSubrtn > pLeft->op2 ){ ++ pLeft->iTable = sqlite3CodeSubselect(pParse, pLeft); ++ pLeft->op2 = pParse->withinRJSubrtn; + } +- assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); +- if( pExpr->iTable!=0 +- && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft)) +- ){ ++ assert( pLeft->op==TK_SELECT || pLeft->op==TK_ERROR ); ++ n = sqlite3ExprVectorSize(pLeft); ++ if( pExpr->iTable!=n ){ + sqlite3ErrorMsg(pParse, "%d columns assigned %d values", + pExpr->iTable, n); + } +- return pExpr->pLeft->iTable + pExpr->iColumn; ++ return pLeft->iTable + pExpr->iColumn; + } + case TK_IN: { + int destIfFalse = sqlite3VdbeMakeLabel(pParse); +@@ -104035,8 +115623,24 @@ expr_code_doover: + exprCodeBetween(pParse, pExpr, target, 0, 0); + return target; + } ++ case TK_COLLATE: { ++ if( !ExprHasProperty(pExpr, EP_Collate) ){ ++ /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called ++ ** "SOFT-COLLATE" that is added to constraints that are pushed down ++ ** from outer queries into sub-queries by the WHERE-clause push-down ++ ** optimization. Clear subtypes as subtypes may not cross a subquery ++ ** boundary. ++ */ ++ assert( pExpr->pLeft ); ++ sqlite3ExprCode(pParse, pExpr->pLeft, target); ++ sqlite3VdbeAddOp1(v, OP_ClrSubtype, target); ++ return target; ++ }else{ ++ pExpr = pExpr->pLeft; ++ goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */ ++ } ++ } + case TK_SPAN: +- case TK_COLLATE: + case TK_UPLUS: { + pExpr = pExpr->pLeft; + goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */ +@@ -104068,9 +115672,14 @@ expr_code_doover: + ** p1==1 -> old.a p1==4 -> new.a + ** p1==2 -> old.b p1==5 -> new.b + */ +- Table *pTab = pExpr->y.pTab; +- int iCol = pExpr->iColumn; +- int p1 = pExpr->iTable * (pTab->nCol+1) + 1 ++ Table *pTab; ++ int iCol; ++ int p1; ++ ++ assert( ExprUseYTab(pExpr) ); ++ pTab = pExpr->y.pTab; ++ iCol = pExpr->iColumn; ++ p1 = pExpr->iTable * (pTab->nCol+1) + 1 + + sqlite3TableColumnToStorage(pTab, iCol); + + assert( pExpr->iTable==0 || pExpr->iTable==1 ); +@@ -104081,7 +115690,7 @@ expr_code_doover: + sqlite3VdbeAddOp2(v, OP_Param, p1, target); + VdbeComment((v, "r[%d]=%s.%s", target, + (pExpr->iTable ? "new" : "old"), +- (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zName) ++ (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zCnName) + )); + + #ifndef SQLITE_OMIT_FLOATING_POINT +@@ -104111,16 +115720,34 @@ expr_code_doover: + case TK_IF_NULL_ROW: { + int addrINR; + u8 okConstFactor = pParse->okConstFactor; +- addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable); +- /* Temporarily disable factoring of constant expressions, since +- ** even though expressions may appear to be constant, they are not +- ** really constant because they originate from the right-hand side +- ** of a LEFT JOIN. */ +- pParse->okConstFactor = 0; +- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); ++ AggInfo *pAggInfo = pExpr->pAggInfo; ++ if( pAggInfo ){ ++ assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); ++ if( !pAggInfo->directMode ){ ++ inReg = AggInfoColumnReg(pAggInfo, pExpr->iAgg); ++ break; ++ } ++ if( pExpr->pAggInfo->useSortingIdx ){ ++ sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, ++ pAggInfo->aCol[pExpr->iAgg].iSorterColumn, ++ target); ++ inReg = target; ++ break; ++ } ++ } ++ addrINR = sqlite3VdbeAddOp3(v, OP_IfNullRow, pExpr->iTable, 0, target); ++ /* The OP_IfNullRow opcode above can overwrite the result register with ++ ** NULL. So we have to ensure that the result register is not a value ++ ** that is suppose to be a constant. Two defenses are needed: ++ ** (1) Temporarily disable factoring of constant expressions ++ ** (2) Make sure the computed value really is stored in register ++ ** "target" and not someplace else. ++ */ ++ pParse->okConstFactor = 0; /* note (1) above */ ++ sqlite3ExprCode(pParse, pExpr->pLeft, target); ++ assert( target==inReg ); + pParse->okConstFactor = okConstFactor; + sqlite3VdbeJumpHere(v, addrINR); +- sqlite3VdbeChangeP3(v, addrINR, inReg); + break; + } + +@@ -104158,7 +115785,7 @@ expr_code_doover: + Expr *pDel = 0; + sqlite3 *db = pParse->db; + +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList ); ++ assert( ExprUseXList(pExpr) && pExpr->x.pList!=0 ); + assert(pExpr->x.pList->nExpr > 0); + pEList = pExpr->x.pList; + aListelem = pEList->a; +@@ -104171,7 +115798,7 @@ expr_code_doover: + break; + } + testcase( pX->op==TK_COLUMN ); +- exprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); ++ sqlite3ExprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); + testcase( regFree1==0 ); + memset(&opCompare, 0, sizeof(opCompare)); + opCompare.op = TK_EQ; +@@ -104225,15 +115852,14 @@ expr_code_doover: + } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + if( pExpr->affExpr==OE_Ignore ){ +- sqlite3VdbeAddOp4( +- v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); ++ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, OE_Ignore); + VdbeCoverage(v); + }else{ +- sqlite3HaltConstraint(pParse, ++ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); ++ sqlite3VdbeAddOp3(v, OP_Halt, + pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR, +- pExpr->affExpr, pExpr->u.zToken, 0, 0); ++ pExpr->affExpr, r1); + } +- + break; + } + #endif +@@ -104252,9 +115878,9 @@ expr_code_doover: + ** once. If no functions are involved, then factor the code out and put it at + ** the end of the prepared statement in the initialization section. + ** +-** If regDest>=0 then the result is always stored in that register and the ++** If regDest>0 then the result is always stored in that register and the + ** result is not reusable. If regDest<0 then this routine is free to +-** store the value whereever it wants. The register where the expression ++** store the value wherever it wants. The register where the expression + ** is stored is returned. When regDest<0, two identical expressions might + ** code to the same register, if they do not contain function calls and hence + ** are factored out into the initialization section at the end of the +@@ -104267,12 +115893,15 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( + ){ + ExprList *p; + assert( ConstFactorOk(pParse) ); ++ assert( regDest!=0 ); + p = pParse->pConstExpr; + if( regDest<0 && p ){ + struct ExprList_item *pItem; + int i; + for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){ +- if( pItem->reusable && sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 ){ ++ if( pItem->fg.reusable ++ && sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 ++ ){ + return pItem->u.iConstExprReg; + } + } +@@ -104295,7 +115924,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( + p = sqlite3ExprListAppend(pParse, p, pExpr); + if( p ){ + struct ExprList_item *pItem = &p->a[p->nExpr-1]; +- pItem->reusable = regDest<0; ++ pItem->fg.reusable = regDest<0; + if( regDest<0 ) regDest = ++pParse->nMem; + pItem->u.iConstExprReg = regDest; + } +@@ -104304,6 +115933,25 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( + return regDest; + } + ++/* ++** Make arrangements to invoke OP_Null on a range of registers ++** during initialization. ++*/ ++SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange( ++ Parse *pParse, /* Parsing context */ ++ int iReg, /* First register to set to NULL */ ++ int nReg /* Number of sequential registers to NULL out */ ++){ ++ u8 okConstFactor = pParse->okConstFactor; ++ Expr t; ++ memset(&t, 0, sizeof(t)); ++ t.op = TK_NULLS; ++ t.y.nReg = nReg; ++ pParse->okConstFactor = 1; ++ sqlite3ExprCodeRunJustOnce(pParse, &t, iReg); ++ pParse->okConstFactor = okConstFactor; ++} ++ + /* + ** Generate code to evaluate an expression and store the results + ** into a register. Return the register number where the results +@@ -104321,8 +115969,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ + int r2; + pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); + if( ConstFactorOk(pParse) ++ && ALWAYS(pExpr!=0) + && pExpr->op!=TK_REGISTER +- && sqlite3ExprIsConstantNotJoin(pExpr) ++ && sqlite3ExprIsConstantNotJoin(pParse, pExpr) + ){ + *pReg = 0; + r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); +@@ -104354,7 +116003,11 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ + inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); + if( inReg!=target ){ + u8 op; +- if( ExprHasProperty(pExpr,EP_Subquery) ){ ++ Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr); ++ testcase( pX!=pExpr ); ++ if( ALWAYS(pX) ++ && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER) ++ ){ + op = OP_Copy; + }else{ + op = OP_SCopy; +@@ -104382,7 +116035,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){ + ** might choose to code the expression at initialization time. + */ + SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ +- if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){ ++ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ + sqlite3ExprCodeRunJustOnce(pParse, pExpr, target); + }else{ + sqlite3ExprCodeCopy(pParse, pExpr, target); +@@ -104428,7 +116081,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( + for(pItem=pList->a, i=0; ipExpr; + #ifdef SQLITE_ENABLE_SORTER_REFERENCES +- if( pItem->bSorterRef ){ ++ if( pItem->fg.bSorterRef ){ + i--; + n--; + }else +@@ -104441,7 +116094,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( + sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); + } + }else if( (flags & SQLITE_ECEL_FACTOR)!=0 +- && sqlite3ExprIsConstantNotJoin(pExpr) ++ && sqlite3ExprIsConstantNotJoin(pParse,pExpr) + ){ + sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i); + }else{ +@@ -104449,7 +116102,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( + if( inReg!=target+i ){ + VdbeOp *pOp; + if( copyOp==OP_Copy +- && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy ++ && (pOp=sqlite3VdbeGetLastOp(v))->opcode==OP_Copy + && pOp->p1+pOp->p3+1==inReg + && pOp->p2+pOp->p3+1==target+i + && pOp->p5==0 /* The do-not-merge flag must be clear */ +@@ -104502,7 +116155,7 @@ static void exprCodeBetween( + memset(&compRight, 0, sizeof(Expr)); + memset(&exprAnd, 0, sizeof(Expr)); + +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); ++ assert( ExprUseXList(pExpr) ); + pDel = sqlite3ExprDup(db, pExpr->pLeft, 0); + if( db->mallocFailed==0 ){ + exprAnd.op = TK_AND; +@@ -104514,7 +116167,7 @@ static void exprCodeBetween( + compRight.op = TK_LE; + compRight.pLeft = pDel; + compRight.pRight = pExpr->x.pList->a[1].pExpr; +- exprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); ++ sqlite3ExprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); + if( xJump ){ + xJump(pParse, &exprAnd, dest, jumpIfNull); + }else{ +@@ -104522,8 +116175,8 @@ static void exprCodeBetween( + ** so that the sqlite3ExprCodeTarget() routine will not attempt to move + ** it into the Parse.pConstExpr list. We should use a new bit for this, + ** for clarity, but we are out of bits in the Expr.flags field so we +- ** have to reuse the EP_FromJoin bit. Bummer. */ +- pDel->flags |= EP_FromJoin; ++ ** have to reuse the EP_OuterON bit. Bummer. */ ++ pDel->flags |= EP_OuterON; + sqlite3ExprCodeTarget(pParse, &exprAnd, dest); + } + sqlite3ReleaseTempReg(pParse, regFree1); +@@ -104648,10 +116301,11 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int + assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); + assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); ++ assert( regFree1==0 || regFree1==r1 ); ++ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1); + sqlite3VdbeAddOp2(v, op, r1, dest); + VdbeCoverageIf(v, op==TK_ISNULL); + VdbeCoverageIf(v, op==TK_NOTNULL); +- testcase( regFree1==0 ); + break; + } + case TK_BETWEEN: { +@@ -104822,10 +116476,11 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int + case TK_ISNULL: + case TK_NOTNULL: { + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); ++ assert( regFree1==0 || regFree1==r1 ); ++ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1); + sqlite3VdbeAddOp2(v, op, r1, dest); + testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL); + testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL); +- testcase( regFree1==0 ); + break; + } + case TK_BETWEEN: { +@@ -104891,12 +116546,23 @@ SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int dest,i + ** same as that currently bound to variable pVar, non-zero is returned. + ** Otherwise, if the values are not the same or if pExpr is not a simple + ** SQL value, zero is returned. ++** ++** If the SQLITE_EnableQPSG flag is set on the database connection, then ++** this routine always returns false. + */ +-static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ +- int res = 0; ++static SQLITE_NOINLINE int exprCompareVariable( ++ const Parse *pParse, ++ const Expr *pVar, ++ const Expr *pExpr ++){ ++ int res = 2; + int iVar; + sqlite3_value *pL, *pR = 0; + ++ if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){ ++ return 0; ++ } ++ if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2; + sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR); + if( pR ){ + iVar = pVar->iColumn; +@@ -104906,12 +116572,11 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ + if( sqlite3_value_type(pL)==SQLITE_TEXT ){ + sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */ + } +- res = 0==sqlite3MemCompare(pL, pR, 0); ++ res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0; + } + sqlite3ValueFree(pR); + sqlite3ValueFree(pL); + } +- + return res; + } + +@@ -104937,20 +116602,23 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ + ** just might result in some slightly slower code. But returning + ** an incorrect 0 or 1 could lead to a malfunction. + ** +-** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in +-** pParse->pReprepare can be matched against literals in pB. The +-** pParse->pVdbe->expmask bitmask is updated for each variable referenced. +-** If pParse is NULL (the normal case) then any TK_VARIABLE term in +-** Argument pParse should normally be NULL. If it is not NULL and pA or +-** pB causes a return value of 2. ++** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE ++** terms in pA with bindings in pParse->pReprepare can be matched against ++** literals in pB. The pParse->pVdbe->expmask bitmask is updated for ++** each variable referenced. + */ +-SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ ++SQLITE_PRIVATE int sqlite3ExprCompare( ++ const Parse *pParse, ++ const Expr *pA, ++ const Expr *pB, ++ int iTab ++){ + u32 combinedFlags; + if( pA==0 || pB==0 ){ + return pB==pA ? 0 : 2; + } +- if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){ +- return 0; ++ if( pParse && pA->op==TK_VARIABLE ){ ++ return exprCompareVariable(pParse, pA, pB); + } + combinedFlags = pA->flags | pB->flags; + if( combinedFlags & EP_IntValue ){ +@@ -104966,9 +116634,17 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa + if( pB->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA,pB->pLeft,iTab)<2 ){ + return 1; + } +- return 2; ++ if( pA->op==TK_AGG_COLUMN && pB->op==TK_COLUMN ++ && pB->iTable<0 && pA->iTable==iTab ++ ){ ++ /* fall through */ ++ }else{ ++ return 2; ++ } + } +- if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ ++ assert( !ExprHasProperty(pA, EP_IntValue) ); ++ assert( !ExprHasProperty(pB, EP_IntValue) ); ++ if( pA->u.zToken ){ + if( pA->op==TK_FUNCTION || pA->op==TK_AGG_FUNCTION ){ + if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; + #ifndef SQLITE_OMIT_WINDOWFUNC +@@ -104986,7 +116662,12 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa + return 0; + }else if( pA->op==TK_COLLATE ){ + if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; +- }else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ ++ }else ++ if( pB->u.zToken!=0 ++ && pA->op!=TK_COLUMN ++ && pA->op!=TK_AGG_COLUMN ++ && strcmp(pA->u.zToken,pB->u.zToken)!=0 ++ ){ + return 2; + } + } +@@ -105028,7 +116709,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa + ** Two NULL pointers are considered to be the same. But a NULL pointer + ** always differs from a non-NULL pointer. + */ +-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ ++SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){ + int i; + if( pA==0 && pB==0 ) return 0; + if( pA==0 || pB==0 ) return 1; +@@ -105037,7 +116718,7 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ + int res; + Expr *pExprA = pA->a[i].pExpr; + Expr *pExprB = pB->a[i].pExpr; +- if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1; ++ if( pA->a[i].fg.sortFlags!=pB->a[i].fg.sortFlags ) return 1; + if( (res = sqlite3ExprCompare(0, pExprA, pExprB, iTab)) ) return res; + } + return 0; +@@ -105047,10 +116728,10 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ + ** Like sqlite3ExprCompare() except COLLATE operators at the top-level + ** are ignored. + */ +-SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ ++SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ + return sqlite3ExprCompare(0, +- sqlite3ExprSkipCollateAndLikely(pA), +- sqlite3ExprSkipCollateAndLikely(pB), ++ sqlite3ExprSkipCollate(pA), ++ sqlite3ExprSkipCollate(pB), + iTab); + } + +@@ -105061,9 +116742,9 @@ SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ + ** non-NULL if pNN is not NULL + */ + static int exprImpliesNotNull( +- Parse *pParse, /* Parsing context */ +- Expr *p, /* The expression to be checked */ +- Expr *pNN, /* The expression that is NOT NULL */ ++ const Parse *pParse,/* Parsing context */ ++ const Expr *p, /* The expression to be checked */ ++ const Expr *pNN, /* The expression that is NOT NULL */ + int iTab, /* Table being evaluated */ + int seenNot /* Return true only if p can be any non-NULL value */ + ){ +@@ -105075,12 +116756,13 @@ static int exprImpliesNotNull( + switch( p->op ){ + case TK_IN: { + if( seenNot && ExprHasProperty(p, EP_xIsSelect) ) return 0; +- assert( ExprHasProperty(p,EP_xIsSelect) +- || (p->x.pList!=0 && p->x.pList->nExpr>0) ); ++ assert( ExprUseXSelect(p) || (p->x.pList!=0 && p->x.pList->nExpr>0) ); + return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); + } + case TK_BETWEEN: { +- ExprList *pList = p->x.pList; ++ ExprList *pList; ++ assert( ExprUseXList(p) ); ++ pList = p->x.pList; + assert( pList!=0 ); + assert( pList->nExpr==2 ); + if( seenNot ) return 0; +@@ -105131,18 +116813,70 @@ static int exprImpliesNotNull( + return 0; + } + ++/* ++** Return true if the boolean value of the expression is always either ++** FALSE or NULL. ++*/ ++static int sqlite3ExprIsNotTrue(Expr *pExpr){ ++ int v; ++ if( pExpr->op==TK_NULL ) return 1; ++ if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1; ++ v = 1; ++ if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1; ++ return 0; ++} ++ ++/* ++** Return true if the expression is one of the following: ++** ++** CASE WHEN x THEN y END ++** CASE WHEN x THEN y ELSE NULL END ++** CASE WHEN x THEN y ELSE false END ++** iif(x,y) ++** iif(x,y,NULL) ++** iif(x,y,false) ++*/ ++static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){ ++ ExprList *pList; ++ if( pExpr->op==TK_FUNCTION ){ ++ const char *z = pExpr->u.zToken; ++ FuncDef *pDef; ++ if( (z[0]!='i' && z[0]!='I') ) return 0; ++ if( pExpr->x.pList==0 ) return 0; ++ pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0); ++#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION ++ if( pDef==0 ) return 0; ++#else ++ if( NEVER(pDef==0) ) return 0; ++#endif ++ if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0; ++ if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0; ++ }else if( pExpr->op==TK_CASE ){ ++ if( pExpr->pLeft!=0 ) return 0; ++ }else{ ++ return 0; ++ } ++ pList = pExpr->x.pList; ++ assert( pList!=0 ); ++ if( pList->nExpr==2 ) return 1; ++ if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1; ++ return 0; ++} ++ + /* + ** Return true if we can prove the pE2 will always be true if pE1 is + ** true. Return false if we cannot complete the proof or if pE2 might + ** be false. Examples: + ** +-** pE1: x==5 pE2: x==5 Result: true +-** pE1: x>0 pE2: x==5 Result: false +-** pE1: x=21 pE2: x=21 OR y=43 Result: true +-** pE1: x!=123 pE2: x IS NOT NULL Result: true +-** pE1: x!=?1 pE2: x IS NOT NULL Result: true +-** pE1: x IS NULL pE2: x IS NOT NULL Result: false +-** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false ++** pE1: x==5 pE2: x==5 Result: true ++** pE1: x>0 pE2: x==5 Result: false ++** pE1: x=21 pE2: x=21 OR y=43 Result: true ++** pE1: x!=123 pE2: x IS NOT NULL Result: true ++** pE1: x!=?1 pE2: x IS NOT NULL Result: true ++** pE1: x IS NULL pE2: x IS NOT NULL Result: false ++** pE1: x IS ?2 pE2: x IS NOT NULL Result: false ++** pE1: iif(x,y) pE2: x Result: true ++** PE1: iif(x,y,0) pE2: x Result: true + ** + ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has + ** Expr.iTable<0 then assume a table number given by iTab. +@@ -105156,7 +116890,12 @@ static int exprImpliesNotNull( + ** improvement. Returning false might cause a performance reduction, but + ** it will always give the correct answer and is hence always safe. + */ +-SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){ ++SQLITE_PRIVATE int sqlite3ExprImpliesExpr( ++ const Parse *pParse, ++ const Expr *pE1, ++ const Expr *pE2, ++ int iTab ++){ + if( sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){ + return 1; + } +@@ -105171,14 +116910,35 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, i + ){ + return 1; + } ++ if( sqlite3ExprIsIIF(pParse->db, pE1) ){ ++ return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab); ++ } + return 0; + } + ++/* This is a helper function to impliesNotNullRow(). In this routine, ++** set pWalker->eCode to one only if *both* of the input expressions ++** separately have the implies-not-null-row property. ++*/ ++static void bothImplyNotNullRow(Walker *pWalker, Expr *pE1, Expr *pE2){ ++ if( pWalker->eCode==0 ){ ++ sqlite3WalkExpr(pWalker, pE1); ++ if( pWalker->eCode ){ ++ pWalker->eCode = 0; ++ sqlite3WalkExpr(pWalker, pE2); ++ } ++ } ++} ++ + /* + ** This is the Expr node callback for sqlite3ExprImpliesNonNullRow(). + ** If the expression node requires that the table at pWalker->iCur + ** have one or more non-NULL column, then set pWalker->eCode to 1 and abort. + ** ++** pWalker->mWFlags is non-zero if this inquiry is being undertaking on ++** behalf of a RIGHT JOIN (or FULL JOIN). That makes a difference when ++** evaluating terms in the ON clause of an inner join. ++** + ** This routine controls an optimization. False positives (setting + ** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives + ** (never setting pWalker->eCode) is a harmless missed optimization. +@@ -105186,29 +116946,34 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, i + static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + testcase( pExpr->op==TK_AGG_COLUMN ); + testcase( pExpr->op==TK_AGG_FUNCTION ); +- if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune; ++ if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune; ++ if( ExprHasProperty(pExpr, EP_InnerON) && pWalker->mWFlags ){ ++ /* If iCur is used in an inner-join ON clause to the left of a ++ ** RIGHT JOIN, that does *not* mean that the table must be non-null. ++ ** But it is difficult to check for that condition precisely. ++ ** To keep things simple, any use of iCur from any inner-join is ++ ** ignored while attempting to simplify a RIGHT JOIN. */ ++ return WRC_Prune; ++ } + switch( pExpr->op ){ + case TK_ISNOT: + case TK_ISNULL: + case TK_NOTNULL: + case TK_IS: +- case TK_OR: + case TK_VECTOR: +- case TK_CASE: +- case TK_IN: + case TK_FUNCTION: + case TK_TRUTH: ++ case TK_CASE: + testcase( pExpr->op==TK_ISNOT ); + testcase( pExpr->op==TK_ISNULL ); + testcase( pExpr->op==TK_NOTNULL ); + testcase( pExpr->op==TK_IS ); +- testcase( pExpr->op==TK_OR ); + testcase( pExpr->op==TK_VECTOR ); +- testcase( pExpr->op==TK_CASE ); +- testcase( pExpr->op==TK_IN ); + testcase( pExpr->op==TK_FUNCTION ); + testcase( pExpr->op==TK_TRUTH ); ++ testcase( pExpr->op==TK_CASE ); + return WRC_Prune; ++ + case TK_COLUMN: + if( pWalker->u.iCur==pExpr->iTable ){ + pWalker->eCode = 1; +@@ -105216,21 +116981,38 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + } + return WRC_Prune; + ++ case TK_OR: + case TK_AND: +- if( pWalker->eCode==0 ){ ++ /* Both sides of an AND or OR must separately imply non-null-row. ++ ** Consider these cases: ++ ** 1. NOT (x AND y) ++ ** 2. x OR y ++ ** If only one of x or y is non-null-row, then the overall expression ++ ** can be true if the other arm is false (case 1) or true (case 2). ++ */ ++ testcase( pExpr->op==TK_OR ); ++ testcase( pExpr->op==TK_AND ); ++ bothImplyNotNullRow(pWalker, pExpr->pLeft, pExpr->pRight); ++ return WRC_Prune; ++ ++ case TK_IN: ++ /* Beware of "x NOT IN ()" and "x NOT IN (SELECT 1 WHERE false)", ++ ** both of which can be true. But apart from these cases, if ++ ** the left-hand side of the IN is NULL then the IN itself will be ++ ** NULL. */ ++ if( ExprUseXList(pExpr) && ALWAYS(pExpr->x.pList->nExpr>0) ){ + sqlite3WalkExpr(pWalker, pExpr->pLeft); +- if( pWalker->eCode ){ +- pWalker->eCode = 0; +- sqlite3WalkExpr(pWalker, pExpr->pRight); +- } + } + return WRC_Prune; + + case TK_BETWEEN: +- if( sqlite3WalkExpr(pWalker, pExpr->pLeft)==WRC_Abort ){ +- assert( pWalker->eCode ); +- return WRC_Abort; +- } ++ /* In "x NOT BETWEEN y AND z" either x must be non-null-row or else ++ ** both y and z must be non-null row */ ++ assert( ExprUseXList(pExpr) ); ++ assert( pExpr->x.pList->nExpr==2 ); ++ sqlite3WalkExpr(pWalker, pExpr->pLeft); ++ bothImplyNotNullRow(pWalker, pExpr->x.pList->a[0].pExpr, ++ pExpr->x.pList->a[1].pExpr); + return WRC_Prune; + + /* Virtual tables are allowed to use constraints like x=NULL. So +@@ -105252,10 +117034,14 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + testcase( pExpr->op==TK_GE ); + /* The y.pTab=0 assignment in wherecode.c always happens after the + ** impliesNotNullRow() test */ +- if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) +- && IsVirtual(pLeft->y.pTab)) +- || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) +- && IsVirtual(pRight->y.pTab)) ++ assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) ); ++ assert( pRight->op!=TK_COLUMN || ExprUseYTab(pRight) ); ++ if( (pLeft->op==TK_COLUMN ++ && ALWAYS(pLeft->y.pTab!=0) ++ && IsVirtual(pLeft->y.pTab)) ++ || (pRight->op==TK_COLUMN ++ && ALWAYS(pRight->y.pTab!=0) ++ && IsVirtual(pRight->y.pTab)) + ){ + return WRC_Prune; + } +@@ -105279,8 +117065,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + ** False positives are not allowed, however. A false positive may result + ** in an incorrect answer. + ** +-** Terms of p that are marked with EP_FromJoin (and hence that come from +-** the ON or USING clauses of LEFT JOINS) are excluded from the analysis. ++** Terms of p that are marked with EP_OuterON (and hence that come from ++** the ON or USING clauses of OUTER JOINS) are excluded from the analysis. + ** + ** This routine is used to check if a LEFT JOIN can be converted into + ** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE +@@ -105288,7 +117074,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + ** be non-NULL, then the LEFT JOIN can be safely converted into an + ** ordinary join. + */ +-SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ ++SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab, int isRJ){ + Walker w; + p = sqlite3ExprSkipCollateAndLikely(p); + if( p==0 ) return 0; +@@ -105296,7 +117082,7 @@ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ + p = p->pLeft; + }else{ + while( p->op==TK_AND ){ +- if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab) ) return 1; ++ if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab, isRJ) ) return 1; + p = p->pRight; + } + } +@@ -105304,6 +117090,7 @@ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ + w.xSelectCallback = 0; + w.xSelectCallback2 = 0; + w.eCode = 0; ++ w.mWFlags = isRJ!=0; + w.u.iCur = iTab; + sqlite3WalkExpr(&w, p); + return w.eCode; +@@ -105364,88 +117151,132 @@ SQLITE_PRIVATE int sqlite3ExprCoveredByIndex( + } + + +-/* +-** An instance of the following structure is used by the tree walker +-** to count references to table columns in the arguments of an +-** aggregate function, in order to implement the +-** sqlite3FunctionThisSrc() routine. +-*/ +-struct SrcCount { +- SrcList *pSrc; /* One particular FROM clause in a nested query */ +- int iSrcInner; /* Smallest cursor number in this context */ +- int nThis; /* Number of references to columns in pSrcList */ +- int nOther; /* Number of references to columns in other FROM clauses */ ++/* Structure used to pass information throughout the Walker in order to ++** implement sqlite3ReferencesSrcList(). ++*/ ++struct RefSrcList { ++ sqlite3 *db; /* Database connection used for sqlite3DbRealloc() */ ++ SrcList *pRef; /* Looking for references to these tables */ ++ i64 nExclude; /* Number of tables to exclude from the search */ ++ int *aiExclude; /* Cursor IDs for tables to exclude from the search */ + }; + + /* +-** xSelect callback for sqlite3FunctionUsesThisSrc(). If this is the first +-** SELECT with a FROM clause encountered during this iteration, set +-** SrcCount.iSrcInner to the cursor number of the leftmost object in +-** the FROM cause. ++** Walker SELECT callbacks for sqlite3ReferencesSrcList(). ++** ++** When entering a new subquery on the pExpr argument, add all FROM clause ++** entries for that subquery to the exclude list. ++** ++** When leaving the subquery, remove those entries from the exclude list. + */ +-static int selectSrcCount(Walker *pWalker, Select *pSel){ +- struct SrcCount *p = pWalker->u.pSrcCount; +- if( p->iSrcInner==0x7FFFFFFF && ALWAYS(pSel->pSrc) && pSel->pSrc->nSrc ){ +- pWalker->u.pSrcCount->iSrcInner = pSel->pSrc->a[0].iCursor; ++static int selectRefEnter(Walker *pWalker, Select *pSelect){ ++ struct RefSrcList *p = pWalker->u.pRefSrcList; ++ SrcList *pSrc = pSelect->pSrc; ++ i64 i, j; ++ int *piNew; ++ if( pSrc->nSrc==0 ) return WRC_Continue; ++ j = p->nExclude; ++ p->nExclude += pSrc->nSrc; ++ piNew = sqlite3DbRealloc(p->db, p->aiExclude, p->nExclude*sizeof(int)); ++ if( piNew==0 ){ ++ p->nExclude = 0; ++ return WRC_Abort; ++ }else{ ++ p->aiExclude = piNew; ++ } ++ for(i=0; inSrc; i++, j++){ ++ p->aiExclude[j] = pSrc->a[i].iCursor; + } + return WRC_Continue; + } ++static void selectRefLeave(Walker *pWalker, Select *pSelect){ ++ struct RefSrcList *p = pWalker->u.pRefSrcList; ++ SrcList *pSrc = pSelect->pSrc; ++ if( p->nExclude ){ ++ assert( p->nExclude>=pSrc->nSrc ); ++ p->nExclude -= pSrc->nSrc; ++ } ++} + +-/* +-** Count the number of references to columns. ++/* This is the Walker EXPR callback for sqlite3ReferencesSrcList(). ++** ++** Set the 0x01 bit of pWalker->eCode if there is a reference to any ++** of the tables shown in RefSrcList.pRef. ++** ++** Set the 0x02 bit of pWalker->eCode if there is a reference to a ++** table is in neither RefSrcList.pRef nor RefSrcList.aiExclude. + */ +-static int exprSrcCount(Walker *pWalker, Expr *pExpr){ +- /* There was once a NEVER() on the second term on the grounds that +- ** sqlite3FunctionUsesThisSrc() was always called before +- ** sqlite3ExprAnalyzeAggregates() and so the TK_COLUMNs have not yet +- ** been converted into TK_AGG_COLUMN. But this is no longer true due +- ** to window functions - sqlite3WindowRewrite() may now indirectly call +- ** FunctionUsesThisSrc() when creating a new sub-select. */ +- if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){ ++static int exprRefToSrcList(Walker *pWalker, Expr *pExpr){ ++ if( pExpr->op==TK_COLUMN ++ || pExpr->op==TK_AGG_COLUMN ++ ){ + int i; +- struct SrcCount *p = pWalker->u.pSrcCount; +- SrcList *pSrc = p->pSrc; ++ struct RefSrcList *p = pWalker->u.pRefSrcList; ++ SrcList *pSrc = p->pRef; + int nSrc = pSrc ? pSrc->nSrc : 0; + for(i=0; iiTable==pSrc->a[i].iCursor ) break; ++ if( pExpr->iTable==pSrc->a[i].iCursor ){ ++ pWalker->eCode |= 1; ++ return WRC_Continue; ++ } + } +- if( inThis++; +- }else if( pExpr->iTableiSrcInner ){ +- /* In a well-formed parse tree (no name resolution errors), +- ** TK_COLUMN nodes with smaller Expr.iTable values are in an +- ** outer context. Those are the only ones to count as "other" */ +- p->nOther++; ++ for(i=0; inExclude && p->aiExclude[i]!=pExpr->iTable; i++){} ++ if( i>=p->nExclude ){ ++ pWalker->eCode |= 2; + } + } + return WRC_Continue; + } + + /* +-** Determine if any of the arguments to the pExpr Function reference +-** pSrcList. Return true if they do. Also return true if the function +-** has no arguments or has only constant arguments. Return false if pExpr +-** references columns but not columns of tables found in pSrcList. ++** Check to see if pExpr references any tables in pSrcList. ++** Possible return values: ++** ++** 1 pExpr does references a table in pSrcList. ++** ++** 0 pExpr references some table that is not defined in either ++** pSrcList or in subqueries of pExpr itself. ++** ++** -1 pExpr only references no tables at all, or it only ++** references tables defined in subqueries of pExpr itself. ++** ++** As currently used, pExpr is always an aggregate function call. That ++** fact is exploited for efficiency. + */ +-SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ ++SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){ + Walker w; +- struct SrcCount cnt; +- assert( pExpr->op==TK_AGG_FUNCTION ); ++ struct RefSrcList x; ++ assert( pParse->db!=0 ); + memset(&w, 0, sizeof(w)); +- w.xExprCallback = exprSrcCount; +- w.xSelectCallback = selectSrcCount; +- w.u.pSrcCount = &cnt; +- cnt.pSrc = pSrcList; +- cnt.iSrcInner = (pSrcList&&pSrcList->nSrc)?pSrcList->a[0].iCursor:0x7FFFFFFF; +- cnt.nThis = 0; +- cnt.nOther = 0; ++ memset(&x, 0, sizeof(x)); ++ w.xExprCallback = exprRefToSrcList; ++ w.xSelectCallback = selectRefEnter; ++ w.xSelectCallback2 = selectRefLeave; ++ w.u.pRefSrcList = &x; ++ x.db = pParse->db; ++ x.pRef = pSrcList; ++ assert( pExpr->op==TK_AGG_FUNCTION ); ++ assert( ExprUseXList(pExpr) ); + sqlite3WalkExprList(&w, pExpr->x.pList); ++ if( pExpr->pLeft ){ ++ assert( pExpr->pLeft->op==TK_ORDER ); ++ assert( ExprUseXList(pExpr->pLeft) ); ++ assert( pExpr->pLeft->x.pList!=0 ); ++ sqlite3WalkExprList(&w, pExpr->pLeft->x.pList); ++ } + #ifndef SQLITE_OMIT_WINDOWFUNC + if( ExprHasProperty(pExpr, EP_WinFunc) ){ + sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter); + } + #endif +- return cnt.nThis>0 || cnt.nOther==0; ++ if( x.aiExclude ) sqlite3DbNNFreeNN(pParse->db, x.aiExclude); ++ if( w.eCode & 0x01 ){ ++ return 1; ++ }else if( w.eCode ){ ++ return 0; ++ }else{ ++ return -1; ++ } + } + + /* +@@ -105456,10 +117287,8 @@ SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ + ** it does, make a copy. This is done because the pExpr argument is + ** subject to change. + ** +-** The copy is stored on pParse->pConstExpr with a register number of 0. +-** This will cause the expression to be deleted automatically when the +-** Parse object is destroyed, but the zero register number means that it +-** will not generate any code in the preamble. ++** The copy is scheduled for deletion using the sqlite3ExprDeferredDelete() ++** which builds on the sqlite3ParserAddCleanup() mechanism. + */ + static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ + if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced)) +@@ -105469,25 +117298,24 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ + int iAgg = pExpr->iAgg; + Parse *pParse = pWalker->pParse; + sqlite3 *db = pParse->db; +- assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION ); +- if( pExpr->op==TK_AGG_COLUMN ){ +- assert( iAgg>=0 && iAggnColumn ); +- if( pAggInfo->aCol[iAgg].pCExpr==pExpr ){ ++ assert( iAgg>=0 ); ++ if( pExpr->op!=TK_AGG_FUNCTION ){ ++ if( iAggnColumn ++ && pAggInfo->aCol[iAgg].pCExpr==pExpr ++ ){ + pExpr = sqlite3ExprDup(db, pExpr, 0); +- if( pExpr ){ ++ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ + pAggInfo->aCol[iAgg].pCExpr = pExpr; +- pParse->pConstExpr = +- sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + } + } + }else{ +- assert( iAgg>=0 && iAggnFunc ); +- if( pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ ++ assert( pExpr->op==TK_AGG_FUNCTION ); ++ if( ALWAYS(iAggnFunc) ++ && pAggInfo->aFunc[iAgg].pFExpr==pExpr ++ ){ + pExpr = sqlite3ExprDup(db, pExpr, 0); +- if( pExpr ){ ++ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ + pAggInfo->aFunc[iAgg].pFExpr = pExpr; +- pParse->pConstExpr = +- sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + } + } + } +@@ -105538,6 +117366,81 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){ + return i; + } + ++/* ++** Search the AggInfo object for an aCol[] entry that has iTable and iColumn. ++** Return the index in aCol[] of the entry that describes that column. ++** ++** If no prior entry is found, create a new one and return -1. The ++** new column will have an index of pAggInfo->nColumn-1. ++*/ ++static void findOrCreateAggInfoColumn( ++ Parse *pParse, /* Parsing context */ ++ AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */ ++ Expr *pExpr /* Expr describing the column to find or insert */ ++){ ++ struct AggInfo_col *pCol; ++ int k; ++ int mxTerm = pParse->db->aLimit[SQLITE_LIMIT_COLUMN]; ++ ++ assert( mxTerm <= SMXV(i16) ); ++ assert( pAggInfo->iFirstReg==0 ); ++ pCol = pAggInfo->aCol; ++ for(k=0; knColumn; k++, pCol++){ ++ if( pCol->pCExpr==pExpr ) return; ++ if( pCol->iTable==pExpr->iTable ++ && pCol->iColumn==pExpr->iColumn ++ && pExpr->op!=TK_IF_NULL_ROW ++ ){ ++ goto fix_up_expr; ++ } ++ } ++ k = addAggInfoColumn(pParse->db, pAggInfo); ++ if( k<0 ){ ++ /* OOM on resize */ ++ assert( pParse->db->mallocFailed ); ++ return; ++ } ++ if( k>mxTerm ){ ++ sqlite3ErrorMsg(pParse, "more than %d aggregate terms", mxTerm); ++ k = mxTerm; ++ } ++ pCol = &pAggInfo->aCol[k]; ++ assert( ExprUseYTab(pExpr) ); ++ pCol->pTab = pExpr->y.pTab; ++ pCol->iTable = pExpr->iTable; ++ pCol->iColumn = pExpr->iColumn; ++ pCol->iSorterColumn = -1; ++ pCol->pCExpr = pExpr; ++ if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){ ++ int j, n; ++ ExprList *pGB = pAggInfo->pGroupBy; ++ struct ExprList_item *pTerm = pGB->a; ++ n = pGB->nExpr; ++ for(j=0; jpExpr; ++ if( pE->op==TK_COLUMN ++ && pE->iTable==pExpr->iTable ++ && pE->iColumn==pExpr->iColumn ++ ){ ++ pCol->iSorterColumn = j; ++ break; ++ } ++ } ++ } ++ if( pCol->iSorterColumn<0 ){ ++ pCol->iSorterColumn = pAggInfo->nSortingColumn++; ++ } ++fix_up_expr: ++ ExprSetVVAProperty(pExpr, EP_NoReduce); ++ assert( pExpr->pAggInfo==0 || pExpr->pAggInfo==pAggInfo ); ++ pExpr->pAggInfo = pAggInfo; ++ if( pExpr->op==TK_COLUMN ){ ++ pExpr->op = TK_AGG_COLUMN; ++ } ++ assert( k <= SMXV(pExpr->iAgg) ); ++ pExpr->iAgg = (i16)k; ++} ++ + /* + ** This is the xExprCallback for a tree walker. It is used to + ** implement sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates +@@ -105551,104 +117454,130 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ + AggInfo *pAggInfo = pNC->uNC.pAggInfo; + + assert( pNC->ncFlags & NC_UAggInfo ); ++ assert( pAggInfo->iFirstReg==0 ); + switch( pExpr->op ){ ++ default: { ++ IndexedExpr *pIEpr; ++ Expr tmp; ++ assert( pParse->iSelfTab==0 ); ++ if( (pNC->ncFlags & NC_InAggFunc)==0 ) break; ++ if( pParse->pIdxEpr==0 ) break; ++ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){ ++ int iDataCur = pIEpr->iDataCur; ++ if( iDataCur<0 ) continue; ++ if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break; ++ } ++ if( pIEpr==0 ) break; ++ if( NEVER(!ExprUseYTab(pExpr)) ) break; ++ for(i=0; inSrc; i++){ ++ if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break; ++ } ++ if( i>=pSrcList->nSrc ) break; ++ if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */ ++ if( pParse->nErr ){ return WRC_Abort; } ++ ++ /* If we reach this point, it means that expression pExpr can be ++ ** translated into a reference to an index column as described by ++ ** pIEpr. ++ */ ++ memset(&tmp, 0, sizeof(tmp)); ++ tmp.op = TK_AGG_COLUMN; ++ tmp.iTable = pIEpr->iIdxCur; ++ tmp.iColumn = pIEpr->iIdxCol; ++ findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp); ++ if( pParse->nErr ){ return WRC_Abort; } ++ assert( pAggInfo->aCol!=0 ); ++ assert( tmp.iAggnColumn ); ++ pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr; ++ pExpr->pAggInfo = pAggInfo; ++ pExpr->iAgg = tmp.iAgg; ++ return WRC_Prune; ++ } ++ case TK_IF_NULL_ROW: + case TK_AGG_COLUMN: + case TK_COLUMN: { + testcase( pExpr->op==TK_AGG_COLUMN ); + testcase( pExpr->op==TK_COLUMN ); ++ testcase( pExpr->op==TK_IF_NULL_ROW ); + /* Check to see if the column is in one of the tables in the FROM + ** clause of the aggregate query */ + if( ALWAYS(pSrcList!=0) ){ +- struct SrcList_item *pItem = pSrcList->a; ++ SrcItem *pItem = pSrcList->a; + for(i=0; inSrc; i++, pItem++){ +- struct AggInfo_col *pCol; + assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); + if( pExpr->iTable==pItem->iCursor ){ +- /* If we reach this point, it means that pExpr refers to a table +- ** that is in the FROM clause of the aggregate query. +- ** +- ** Make an entry for the column in pAggInfo->aCol[] if there +- ** is not an entry there already. +- */ +- int k; +- pCol = pAggInfo->aCol; +- for(k=0; knColumn; k++, pCol++){ +- if( pCol->iTable==pExpr->iTable && +- pCol->iColumn==pExpr->iColumn ){ +- break; +- } +- } +- if( (k>=pAggInfo->nColumn) +- && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0 +- ){ +- pCol = &pAggInfo->aCol[k]; +- pCol->pTab = pExpr->y.pTab; +- pCol->iTable = pExpr->iTable; +- pCol->iColumn = pExpr->iColumn; +- pCol->iMem = ++pParse->nMem; +- pCol->iSorterColumn = -1; +- pCol->pCExpr = pExpr; +- if( pAggInfo->pGroupBy ){ +- int j, n; +- ExprList *pGB = pAggInfo->pGroupBy; +- struct ExprList_item *pTerm = pGB->a; +- n = pGB->nExpr; +- for(j=0; jpExpr; +- if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable && +- pE->iColumn==pExpr->iColumn ){ +- pCol->iSorterColumn = j; +- break; +- } +- } +- } +- if( pCol->iSorterColumn<0 ){ +- pCol->iSorterColumn = pAggInfo->nSortingColumn++; +- } +- } +- /* There is now an entry for pExpr in pAggInfo->aCol[] (either +- ** because it was there before or because we just created it). +- ** Convert the pExpr to be a TK_AGG_COLUMN referring to that +- ** pAggInfo->aCol[] entry. +- */ +- ExprSetVVAProperty(pExpr, EP_NoReduce); +- pExpr->pAggInfo = pAggInfo; +- pExpr->op = TK_AGG_COLUMN; +- pExpr->iAgg = (i16)k; ++ findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr); + break; + } /* endif pExpr->iTable==pItem->iCursor */ + } /* end loop over pSrcList */ + } +- return WRC_Prune; ++ return WRC_Continue; + } + case TK_AGG_FUNCTION: { + if( (pNC->ncFlags & NC_InAggFunc)==0 + && pWalker->walkerDepth==pExpr->op2 ++ && pExpr->pAggInfo==0 + ){ + /* Check to see if pExpr is a duplicate of another aggregate + ** function that is already in the pAggInfo structure + */ + struct AggInfo_func *pItem = pAggInfo->aFunc; ++ int mxTerm = pParse->db->aLimit[SQLITE_LIMIT_COLUMN]; ++ assert( mxTerm <= SMXV(i16) ); + for(i=0; inFunc; i++, pItem++){ ++ if( NEVER(pItem->pFExpr==pExpr) ) break; + if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ + break; + } + } +- if( i>=pAggInfo->nFunc ){ ++ if( i>mxTerm ){ ++ sqlite3ErrorMsg(pParse, "more than %d aggregate terms", mxTerm); ++ i = mxTerm; ++ assert( inFunc ); ++ }else if( i>=pAggInfo->nFunc ){ + /* pExpr is original. Make a new entry in pAggInfo->aFunc[] + */ + u8 enc = ENC(pParse->db); + i = addAggInfoFunc(pParse->db, pAggInfo); + if( i>=0 ){ ++ int nArg; + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + pItem = &pAggInfo->aFunc[i]; + pItem->pFExpr = pExpr; +- pItem->iMem = ++pParse->nMem; +- assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ assert( ExprUseUToken(pExpr) ); ++ nArg = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; + pItem->pFunc = sqlite3FindFunction(pParse->db, +- pExpr->u.zToken, +- pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); +- if( pExpr->flags & EP_Distinct ){ ++ pExpr->u.zToken, nArg, enc, 0); ++ assert( pItem->bOBUnique==0 ); ++ if( pExpr->pLeft ++ && (pItem->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)==0 ++ ){ ++ /* The NEEDCOLL test above causes any ORDER BY clause on ++ ** aggregate min() or max() to be ignored. */ ++ ExprList *pOBList; ++ assert( nArg>0 ); ++ assert( pExpr->pLeft->op==TK_ORDER ); ++ assert( ExprUseXList(pExpr->pLeft) ); ++ pItem->iOBTab = pParse->nTab++; ++ pOBList = pExpr->pLeft->x.pList; ++ assert( pOBList->nExpr>0 ); ++ assert( pItem->bOBUnique==0 ); ++ if( pOBList->nExpr==1 ++ && nArg==1 ++ && sqlite3ExprCompare(0,pOBList->a[0].pExpr, ++ pExpr->x.pList->a[0].pExpr,0)==0 ++ ){ ++ pItem->bOBPayload = 0; ++ pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct); ++ }else{ ++ pItem->bOBPayload = 1; ++ } ++ pItem->bUseSubtype = ++ (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0; ++ }else{ ++ pItem->iOBTab = -1; ++ } ++ if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){ + pItem->iDistinct = pParse->nTab++; + }else{ + pItem->iDistinct = -1; +@@ -105659,6 +117588,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ + */ + assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); + ExprSetVVAProperty(pExpr, EP_NoReduce); ++ assert( i <= SMXV(pExpr->iAgg) ); + pExpr->iAgg = (i16)i; + pExpr->pAggInfo = pAggInfo; + return WRC_Prune; +@@ -105772,6 +117702,37 @@ SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){ + pParse->nRangeReg = 0; + } + ++/* ++** Make sure sufficient registers have been allocated so that ++** iReg is a valid register number. ++*/ ++SQLITE_PRIVATE void sqlite3TouchRegister(Parse *pParse, int iReg){ ++ if( pParse->nMemnMem = iReg; ++} ++ ++#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) ++/* ++** Return the latest reusable register in the set of all registers. ++** The value returned is no less than iMin. If any register iMin or ++** greater is in permanent use, then return one more than that last ++** permanent register. ++*/ ++SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse *pParse, int iMin){ ++ const ExprList *pList = pParse->pConstExpr; ++ if( pList ){ ++ int i; ++ for(i=0; inExpr; i++){ ++ if( pList->a[i].u.iConstExprReg>=iMin ){ ++ iMin = pList->a[i].u.iConstExprReg + 1; ++ } ++ } ++ } ++ pParse->nTempReg = 0; ++ pParse->nRangeReg = 0; ++ return iMin; ++} ++#endif /* SQLITE_ENABLE_STAT4 || SQLITE_DEBUG */ ++ + /* + ** Validate that no temporary register falls within the range of + ** iFirst..iLast, inclusive. This routine is only call from within assert() +@@ -105791,6 +117752,14 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ + return 0; + } + } ++ if( pParse->pConstExpr ){ ++ ExprList *pList = pParse->pConstExpr; ++ for(i=0; inExpr; i++){ ++ int iReg = pList->a[i].u.iConstExprReg; ++ if( iReg==0 ) continue; ++ if( iReg>=iFirst && iReg<=iLast ) return 0; ++ } ++ } + return 1; + } + #endif /* SQLITE_DEBUG */ +@@ -105830,6 +117799,7 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ + static int isAlterableTable(Parse *pParse, Table *pTab){ + if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) + #ifndef SQLITE_OMIT_VIRTUALTABLE ++ || (pTab->tabFlags & TF_Eponymous)!=0 + || ( (pTab->tabFlags & TF_Shadow)!=0 + && sqlite3ReadOnlyShadowTables(pParse->db) + ) +@@ -105848,25 +117818,56 @@ static int isAlterableTable(Parse *pParse, Table *pTab){ + ** statement to ensure that the operation has not rendered any schema + ** objects unusable. + */ +-static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ ++static void renameTestSchema( ++ Parse *pParse, /* Parse context */ ++ const char *zDb, /* Name of db to verify schema of */ ++ int bTemp, /* True if this is the temp db */ ++ const char *zWhen, /* "when" part of error message */ ++ int bNoDQS /* Do not allow DQS in the schema */ ++){ ++ pParse->colNamesSet = 1; + sqlite3NestedParse(pParse, + "SELECT 1 " +- "FROM \"%w\"." DFLT_SCHEMA_TABLE " " ++ "FROM \"%w\"." LEGACY_SCHEMA_TABLE " " + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" +- " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ", ++ " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ", + zDb, +- zDb, bTemp ++ zDb, bTemp, zWhen, bNoDQS + ); + + if( bTemp==0 ){ + sqlite3NestedParse(pParse, + "SELECT 1 " +- "FROM temp." DFLT_SCHEMA_TABLE " " ++ "FROM temp." LEGACY_SCHEMA_TABLE " " + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" + " AND sql NOT LIKE 'create virtual%%'" +- " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ", +- zDb ++ " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ", ++ zDb, zWhen, bNoDQS ++ ); ++ } ++} ++ ++/* ++** Generate VM code to replace any double-quoted strings (but not double-quoted ++** identifiers) within the "sql" column of the sqlite_schema table in ++** database zDb with their single-quoted equivalents. If argument bTemp is ++** not true, similarly update all SQL statements in the sqlite_schema table ++** of the temp db. ++*/ ++static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){ ++ sqlite3NestedParse(pParse, ++ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE ++ " SET sql = sqlite_rename_quotefix(%Q, sql)" ++ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" ++ " AND sql NOT LIKE 'create virtual%%'" , zDb, zDb ++ ); ++ if( bTemp==0 ){ ++ sqlite3NestedParse(pParse, ++ "UPDATE temp." LEGACY_SCHEMA_TABLE ++ " SET sql = sqlite_rename_quotefix('temp', sql)" ++ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" ++ " AND sql NOT LIKE 'create virtual%%'" + ); + } + } +@@ -105875,12 +117876,12 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ + ** Generate code to reload the schema for database iDb. And, if iDb!=1, for + ** the temp database as well. + */ +-static void renameReloadSchema(Parse *pParse, int iDb){ ++static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){ + Vdbe *v = pParse->pVdbe; + if( v ){ + sqlite3ChangeCookie(pParse, iDb); +- sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0); +- if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); ++ sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5); ++ if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5); + } + } + +@@ -105902,9 +117903,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( + const char *zTabName; /* Original name of the table */ + Vdbe *v; + VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ +- u32 savedDbFlags; /* Saved value of db->mDbFlags */ + +- savedDbFlags = db->mDbFlags; + if( NEVER(db->mallocFailed) ) goto exit_rename_table; + assert( pSrc->nSrc==1 ); + assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); +@@ -105913,7 +117912,6 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( + if( !pTab ) goto exit_rename_table; + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + zDb = db->aDb[iDb].zDbSName; +- db->mDbFlags |= DBFLAG_PreferBuiltin; + + /* Get a NULL terminated version of the new table name. */ + zName = sqlite3NameFromToken(db, pName); +@@ -105942,7 +117940,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( + } + + #ifndef SQLITE_OMIT_VIEW +- if( pTab->pSelect ){ ++ if( IsView(pTab) ){ + sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); + goto exit_rename_table; + } +@@ -105984,7 +117982,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( + /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in + ** the schema to use the new table name. */ + sqlite3NestedParse(pParse, +- "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " ++ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " + "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) " + "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)" + "AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" +@@ -105994,7 +117992,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( + /* Update the tbl_name and name columns of the sqlite_schema table + ** as required. */ + sqlite3NestedParse(pParse, +- "UPDATE %Q." DFLT_SCHEMA_TABLE " SET " ++ "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET " + "tbl_name = %Q, " + "name = CASE " + "WHEN type='table' THEN %Q " +@@ -106029,7 +118027,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( + "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " + "tbl_name = " + "CASE WHEN tbl_name=%Q COLLATE nocase AND " +- " sqlite_rename_test(%Q, sql, type, name, 1) " ++ " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) " + "THEN %Q ELSE tbl_name END " + "WHERE type IN ('view', 'trigger')" + , zDb, zTabName, zName, zTabName, zDb, zName); +@@ -106048,13 +118046,12 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( + } + #endif + +- renameReloadSchema(pParse, iDb); +- renameTestSchema(pParse, zDb, iDb==1); ++ renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); ++ renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); + + exit_rename_table: + sqlite3SrcListDelete(db, pSrc); + sqlite3DbFree(db, zName); +- db->mDbFlags = savedDbFlags; + } + + /* +@@ -106095,7 +118092,9 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ + int r1; /* Temporary registers */ + + db = pParse->db; +- if( pParse->nErr || db->mallocFailed ) return; ++ assert( db->pParse==pParse ); ++ if( pParse->nErr ) return; ++ assert( db->mallocFailed==0 ); + pNew = pParse->pNewTable; + assert( pNew ); + +@@ -106104,7 +118103,7 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ + zDb = db->aDb[iDb].zDbSName; + zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ + pCol = &pNew->aCol[pNew->nCol-1]; +- pDflt = pCol->pDflt; ++ pDflt = sqlite3ColumnExpr(pNew, pCol); + pTab = sqlite3FindTable(db, zTab, zDb); + assert( pTab ); + +@@ -106138,7 +118137,8 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ + if( pDflt && pDflt->pLeft->op==TK_NULL ){ + pDflt = 0; + } +- if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ ++ assert( IsOrdinaryTable(pNew) ); ++ if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){ + sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, + "Cannot add a REFERENCES column with non-NULL default value"); + } +@@ -106175,28 +118175,30 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ + zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); + if( zCol ){ + char *zEnd = &zCol[pColDef->n-1]; +- u32 savedDbFlags = db->mDbFlags; + while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ + *zEnd-- = '\0'; + } +- db->mDbFlags |= DBFLAG_PreferBuiltin; ++ /* substr() operations on characters, but addColOffset is in bytes. So we ++ ** have to use printf() to translate between these units: */ ++ assert( IsOrdinaryTable(pTab) ); ++ assert( IsOrdinaryTable(pNew) ); + sqlite3NestedParse(pParse, +- "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " +- "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " ++ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " ++ "sql = printf('%%.%ds, ',sql) || %Q" ++ " || substr(sql,1+length(printf('%%.%ds',sql))) " + "WHERE type = 'table' AND name = %Q", +- zDb, pNew->addColOffset, zCol, pNew->addColOffset+1, ++ zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset, + zTab + ); + sqlite3DbFree(db, zCol); +- db->mDbFlags = savedDbFlags; + } + +- /* Make sure the schema version is at least 3. But do not upgrade +- ** from less than 3 to 4, as that will corrupt any preexisting DESC +- ** index. +- */ + v = sqlite3GetVdbe(pParse); + if( v ){ ++ /* Make sure the schema version is at least 3. But do not upgrade ++ ** from less than 3 to 4, as that will corrupt any preexisting DESC ++ ** index. ++ */ + r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); + sqlite3VdbeUsesBtree(v, iDb); +@@ -106205,10 +118207,30 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); + sqlite3ReleaseTempReg(pParse, r1); +- } + +- /* Reload the table definition */ +- renameReloadSchema(pParse, iDb); ++ /* Reload the table definition */ ++ renameReloadSchema(pParse, iDb, INITFLAG_AlterAdd); ++ ++ /* Verify that constraints are still satisfied */ ++ if( pNew->pCheck!=0 ++ || (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0) ++ || (pTab->tabFlags & TF_Strict)!=0 ++ ){ ++ sqlite3NestedParse(pParse, ++ "SELECT CASE WHEN quick_check GLOB 'CHECK*'" ++ " THEN raise(ABORT,'CHECK constraint failed')" ++ " WHEN quick_check GLOB 'non-* value in*'" ++ " THEN raise(ABORT,'type mismatch on DEFAULT')" ++ " ELSE raise(ABORT,'NOT NULL constraint failed')" ++ " END" ++ " FROM pragma_quick_check(%Q,%Q)" ++ " WHERE quick_check GLOB 'CHECK*'" ++ " OR quick_check GLOB 'NULL*'" ++ " OR quick_check GLOB 'non-* value in*'", ++ zTab, zDb ++ ); ++ } ++ } + } + + /* +@@ -106249,7 +118271,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ + #endif + + /* Make sure this is not an attempt to ALTER a view. */ +- if( pTab->pSelect ){ ++ if( IsView(pTab) ){ + sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); + goto exit_begin_add_column; + } +@@ -106258,7 +118280,8 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ + } + + sqlite3MayAbort(pParse); +- assert( pTab->addColOffset>0 ); ++ assert( IsOrdinaryTable(pTab) ); ++ assert( pTab->u.tab.addColOffset>0 ); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + + /* Put a copy of the Table struct in Parse.pNewTable for the +@@ -106276,23 +118299,23 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ + assert( pNew->nCol>0 ); + nAlloc = (((pNew->nCol-1)/8)*8)+8; + assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); +- pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc); ++ pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*(u32)nAlloc); + pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); + if( !pNew->aCol || !pNew->zName ){ + assert( db->mallocFailed ); + goto exit_begin_add_column; + } +- memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); ++ memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol); + for(i=0; inCol; i++){ + Column *pCol = &pNew->aCol[i]; +- pCol->zName = sqlite3DbStrDup(db, pCol->zName); +- pCol->hName = sqlite3StrIHash(pCol->zName); +- pCol->zColl = 0; +- pCol->pDflt = 0; ++ pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName); ++ pCol->hName = sqlite3StrIHash(pCol->zCnName); + } ++ assert( IsOrdinaryTable(pNew) ); ++ pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0); + pNew->pSchema = db->aDb[iDb].pSchema; +- pNew->addColOffset = pTab->addColOffset; +- pNew->nTabRef = 1; ++ pNew->u.tab.addColOffset = pTab->u.tab.addColOffset; ++ assert( pNew->nTabRef==1 ); + + exit_begin_add_column: + sqlite3SrcListDelete(db, pSrc); +@@ -106308,10 +118331,10 @@ exit_begin_add_column: + ** Or, if pTab is not a view or virtual table, zero is returned. + */ + #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) +-static int isRealTable(Parse *pParse, Table *pTab){ ++static int isRealTable(Parse *pParse, Table *pTab, int bDrop){ + const char *zType = 0; + #ifndef SQLITE_OMIT_VIEW +- if( pTab->pSelect ){ ++ if( IsView(pTab) ){ + zType = "view"; + } + #endif +@@ -106321,15 +118344,16 @@ static int isRealTable(Parse *pParse, Table *pTab){ + } + #endif + if( zType ){ +- sqlite3ErrorMsg( +- pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName ++ sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"", ++ (bDrop ? "drop column from" : "rename columns of"), ++ zType, pTab->zName + ); + return 1; + } + return 0; + } + #else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ +-# define isRealTable(x,y) (0) ++# define isRealTable(x,y,z) (0) + #endif + + /* +@@ -106358,7 +118382,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( + + /* Cannot alter a system table */ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; +- if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column; ++ if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column; + + /* Which schema holds the table to be altered */ + iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); +@@ -106376,14 +118400,16 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( + ** altered. Set iCol to be the index of the column being renamed */ + zOld = sqlite3NameFromToken(db, pOld); + if( !zOld ) goto exit_rename_column; +- for(iCol=0; iColnCol; iCol++){ +- if( 0==sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break; +- } +- if( iCol==pTab->nCol ){ +- sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld); ++ iCol = sqlite3ColumnIndex(pTab, zOld); ++ if( iCol<0 ){ ++ sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld); + goto exit_rename_column; + } + ++ /* Ensure the schema contains no double-quoted strings */ ++ renameTestSchema(pParse, zDb, iSchema==1, "", 0); ++ renameFixQuotes(pParse, zDb, iSchema==1); ++ + /* Do the rename operation using a recursive UPDATE statement that + ** uses the sqlite_rename_column() SQL function to compute the new + ** CREATE statement text for the sqlite_schema table. +@@ -106394,26 +118420,25 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( + assert( pNew->n>0 ); + bQuote = sqlite3Isquote(pNew->z[0]); + sqlite3NestedParse(pParse, +- "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " ++ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " + "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) " + "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' " +- " AND (type != 'index' OR tbl_name = %Q)" +- " AND sql NOT LIKE 'create virtual%%'", ++ " AND (type != 'index' OR tbl_name = %Q)", + zDb, + zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1, + pTab->zName + ); + + sqlite3NestedParse(pParse, +- "UPDATE temp." DFLT_SCHEMA_TABLE " SET " ++ "UPDATE temp." LEGACY_SCHEMA_TABLE " SET " + "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) " + "WHERE type IN ('trigger', 'view')", + zDb, pTab->zName, iCol, zNew, bQuote + ); + + /* Drop and reload the database schema. */ +- renameReloadSchema(pParse, iSchema); +- renameTestSchema(pParse, zDb, iSchema==1); ++ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); ++ renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1); + + exit_rename_column: + sqlite3SrcListDelete(db, pSrc); +@@ -106440,7 +118465,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( + ** the parse tree. + */ + struct RenameToken { +- void *p; /* Parse tree element created by token t */ ++ const void *p; /* Parse tree element created by token t */ + Token t; /* The token that created parse tree element p */ + RenameToken *pNext; /* Next is a list of all RenameToken objects */ + }; +@@ -106482,16 +118507,19 @@ struct RenameCtx { + ** Technically, as x no longer points into a valid object or to the byte + ** following a valid object, it may not be used in comparison operations. + */ +-static void renameTokenCheckAll(Parse *pParse, void *pPtr){ +- if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){ +- RenameToken *p; +- u8 i = 0; ++static void renameTokenCheckAll(Parse *pParse, const void *pPtr){ ++ assert( pParse==pParse->db->pParse ); ++ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); ++ if( pParse->nErr==0 ){ ++ const RenameToken *p; ++ u32 i = 1; + for(p=pParse->pRename; p; p=p->pNext){ + if( p->p ){ + assert( p->p!=pPtr ); +- i += *(u8*)(p->p); ++ i += *(u8*)(p->p) | 1; + } + } ++ assert( i>0 ); + } + } + #else +@@ -106510,7 +118538,11 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){ + ** with tail recursion in tokenExpr() routine, for a small performance + ** improvement. + */ +-SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ ++SQLITE_PRIVATE const void *sqlite3RenameTokenMap( ++ Parse *pParse, ++ const void *pPtr, ++ const Token *pToken ++){ + RenameToken *pNew; + assert( pPtr || pParse->db->mallocFailed ); + renameTokenCheckAll(pParse, pPtr); +@@ -106532,7 +118564,7 @@ SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pTo + ** with parse tree element pFrom. This function remaps the associated token + ** to parse tree element pTo. + */ +-SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ ++SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){ + RenameToken *p; + renameTokenCheckAll(pParse, pTo); + for(p=pParse->pRename; p; p=p->pNext){ +@@ -106548,7 +118580,10 @@ SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFro + */ + static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ + Parse *pParse = pWalker->pParse; +- sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr); ++ sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr); ++ if( ExprUseYTab(pExpr) ){ ++ sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab); ++ } + return WRC_Continue; + } + +@@ -106559,15 +118594,31 @@ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ + static void renameWalkWith(Walker *pWalker, Select *pSelect){ + With *pWith = pSelect->pWith; + if( pWith ){ ++ Parse *pParse = pWalker->pParse; + int i; ++ With *pCopy = 0; ++ assert( pWith->nCte>0 ); ++ if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){ ++ /* Push a copy of the With object onto the with-stack. We use a copy ++ ** here as the original will be expanded and resolved (flags SF_Expanded ++ ** and SF_Resolved) below. And the parser code that uses the with-stack ++ ** fails if the Select objects on it have already been expanded and ++ ** resolved. */ ++ pCopy = sqlite3WithDup(pParse->db, pWith); ++ pCopy = sqlite3WithPush(pParse, pCopy, 1); ++ } + for(i=0; inCte; i++){ + Select *p = pWith->a[i].pSelect; + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); +- sNC.pParse = pWalker->pParse; +- sqlite3SelectPrep(sNC.pParse, p, &sNC); ++ sNC.pParse = pParse; ++ if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC); ++ if( sNC.pParse->db->mallocFailed ) return; + sqlite3WalkSelect(pWalker, p); +- sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); ++ sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); ++ } ++ if( pCopy && pParse->pWith==pCopy ){ ++ pParse->pWith = pCopy->pOuter; + } + } + } +@@ -106577,13 +118628,12 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){ + */ + static void unmapColumnIdlistNames( + Parse *pParse, +- IdList *pIdList ++ const IdList *pIdList + ){ +- if( pIdList ){ +- int ii; +- for(ii=0; iinId; ii++){ +- sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName); +- } ++ int ii; ++ assert( pIdList!=0 ); ++ for(ii=0; iinId; ii++){ ++ sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); + } + } + +@@ -106594,11 +118644,15 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ + Parse *pParse = pWalker->pParse; + int i; + if( pParse->nErr ) return WRC_Abort; +- if( NEVER(p->selFlags & SF_View) ) return WRC_Prune; ++ testcase( p->selFlags & SF_View ); ++ testcase( p->selFlags & SF_CopyCte ); ++ if( p->selFlags & (SF_View|SF_CopyCte) ){ ++ return WRC_Prune; ++ } + if( ALWAYS(p->pEList) ){ + ExprList *pList = p->pEList; + for(i=0; inExpr; i++){ +- if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){ ++ if( pList->a[i].zEName && pList->a[i].fg.eEName==ENAME_NAME ){ + sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); + } + } +@@ -106607,8 +118661,11 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ + SrcList *pSrc = p->pSrc; + for(i=0; inSrc; i++){ + sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); +- if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; +- unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); ++ if( pSrc->a[i].fg.isUsing==0 ){ ++ sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn); ++ }else{ ++ unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing); ++ } + } + } + +@@ -106644,7 +118701,7 @@ SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){ + sWalker.xExprCallback = renameUnmapExprCb; + sqlite3WalkExprList(&sWalker, pEList); + for(i=0; inExpr; i++){ +- if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) ){ ++ if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) ){ + sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName); + } + } +@@ -106665,23 +118722,35 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){ + + /* + ** Search the Parse object passed as the first argument for a RenameToken +-** object associated with parse tree element pPtr. If found, remove it +-** from the Parse object and add it to the list maintained by the +-** RenameCtx object passed as the second argument. ++** object associated with parse tree element pPtr. If found, return a pointer ++** to it. Otherwise, return NULL. ++** ++** If the second argument passed to this function is not NULL and a matching ++** RenameToken object is found, remove it from the Parse object and add it to ++** the list maintained by the RenameCtx object. + */ +-static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ ++static RenameToken *renameTokenFind( ++ Parse *pParse, ++ struct RenameCtx *pCtx, ++ const void *pPtr ++){ + RenameToken **pp; +- assert( pPtr!=0 ); ++ if( NEVER(pPtr==0) ){ ++ return 0; ++ } + for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ + if( (*pp)->p==pPtr ){ + RenameToken *pToken = *pp; +- *pp = pToken->pNext; +- pToken->pNext = pCtx->pList; +- pCtx->pList = pToken; +- pCtx->nList++; +- break; ++ if( pCtx ){ ++ *pp = pToken->pNext; ++ pToken->pNext = pCtx->pList; ++ pCtx->pList = pToken; ++ pCtx->nList++; ++ } ++ return pToken; + } + } ++ return 0; + } + + /* +@@ -106690,7 +118759,11 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ + ** descend into sub-select statements. + */ + static int renameColumnSelectCb(Walker *pWalker, Select *p){ +- if( p->selFlags & SF_View ) return WRC_Prune; ++ if( p->selFlags & (SF_View|SF_CopyCte) ){ ++ testcase( p->selFlags & SF_View ); ++ testcase( p->selFlags & SF_CopyCte ); ++ return WRC_Prune; ++ } + renameWalkWith(pWalker, p); + return WRC_Continue; + } +@@ -106713,6 +118786,7 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ + renameTokenFind(pWalker->pParse, p, (void*)pExpr); + }else if( pExpr->op==TK_COLUMN + && pExpr->iColumn==p->iCol ++ && ALWAYS(ExprUseYTab(pExpr)) + && p->pTab==pExpr->y.pTab + ){ + renameTokenFind(pWalker->pParse, p, (void*)pExpr); +@@ -106744,7 +118818,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ + } + + /* +-** An error occured while parsing or otherwise processing a database ++** An error occurred while parsing or otherwise processing a database + ** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an + ** ALTER TABLE RENAME COLUMN program. The error message emitted by the + ** sub-routine is currently stored in pParse->zErrMsg. This function +@@ -106752,7 +118826,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ + */ + static void renameColumnParseError( + sqlite3_context *pCtx, +- int bPost, ++ const char *zWhen, + sqlite3_value *pType, + sqlite3_value *pObject, + Parse *pParse +@@ -106761,12 +118835,12 @@ static void renameColumnParseError( + const char *zN = (const char*)sqlite3_value_text(pObject); + char *zErr; + +- zErr = sqlite3_mprintf("error in %s %s%s: %s", +- zT, zN, (bPost ? " after rename" : ""), ++ zErr = sqlite3MPrintf(pParse->db, "error in %s %s%s%s: %s", ++ zT, zN, (zWhen[0] ? " " : ""), zWhen, + pParse->zErrMsg + ); + sqlite3_result_error(pCtx, zErr, -1); +- sqlite3_free(zErr); ++ sqlite3DbFree(pParse->db, zErr); + } + + /* +@@ -106778,18 +118852,18 @@ static void renameColumnParseError( + static void renameColumnElistNames( + Parse *pParse, + RenameCtx *pCtx, +- ExprList *pEList, ++ const ExprList *pEList, + const char *zOld + ){ + if( pEList ){ + int i; + for(i=0; inExpr; i++){ +- char *zName = pEList->a[i].zEName; +- if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) ++ const char *zName = pEList->a[i].zEName; ++ if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) + && ALWAYS(zName!=0) + && 0==sqlite3_stricmp(zName, zOld) + ){ +- renameTokenFind(pParse, pCtx, (void*)zName); ++ renameTokenFind(pParse, pCtx, (const void*)zName); + } + } + } +@@ -106803,15 +118877,15 @@ static void renameColumnElistNames( + static void renameColumnIdlistNames( + Parse *pParse, + RenameCtx *pCtx, +- IdList *pIdList, ++ const IdList *pIdList, + const char *zOld + ){ + if( pIdList ){ + int i; + for(i=0; inId; i++){ +- char *zName = pIdList->a[i].zName; ++ const char *zName = pIdList->a[i].zName; + if( 0==sqlite3_stricmp(zName, zOld) ){ +- renameTokenFind(pParse, pCtx, (void*)zName); ++ renameTokenFind(pParse, pCtx, (const void*)zName); + } + } + } +@@ -106830,24 +118904,33 @@ static int renameParseSql( + int bTemp /* True if SQL is from temp schema */ + ){ + int rc; +- char *zErr = 0; ++ u64 flags; + +- db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); +- +- /* Parse the SQL statement passed as the first argument. If no error +- ** occurs and the parse does not result in a new table, index or +- ** trigger object, the database must be corrupt. */ +- memset(p, 0, sizeof(Parse)); ++ sqlite3ParseObjectInit(p, db); ++ if( zSql==0 ){ ++ return SQLITE_NOMEM; ++ } ++ if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ ++ return SQLITE_CORRUPT_BKPT; ++ } ++ if( bTemp ){ ++ db->init.iDb = 1; ++ }else{ ++ int iDb = sqlite3FindDbName(db, zDb); ++ assert( iDb>=0 && iDb<=0xff ); ++ db->init.iDb = (u8)iDb; ++ } + p->eParseMode = PARSE_MODE_RENAME; + p->db = db; + p->nQueryLoop = 1; +- rc = sqlite3RunParser(p, zSql, &zErr); +- assert( p->zErrMsg==0 ); +- assert( rc!=SQLITE_OK || zErr==0 ); +- p->zErrMsg = zErr; ++ flags = db->flags; ++ testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 ); ++ db->flags |= SQLITE_Comments; ++ rc = sqlite3RunParser(p, zSql); ++ db->flags = flags; + if( db->mallocFailed ) rc = SQLITE_NOMEM; + if( rc==SQLITE_OK +- && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0 ++ && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) + ){ + rc = SQLITE_CORRUPT_BKPT; + } +@@ -106884,54 +118967,82 @@ static int renameEditSql( + const char *zNew, /* New token text */ + int bQuote /* True to always quote token */ + ){ +- int nNew = sqlite3Strlen30(zNew); +- int nSql = sqlite3Strlen30(zSql); ++ i64 nNew = sqlite3Strlen30(zNew); ++ i64 nSql = sqlite3Strlen30(zSql); + sqlite3 *db = sqlite3_context_db_handle(pCtx); + int rc = SQLITE_OK; +- char *zQuot; ++ char *zQuot = 0; + char *zOut; +- int nQuot; +- +- /* Set zQuot to point to a buffer containing a quoted copy of the +- ** identifier zNew. If the corresponding identifier in the original +- ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to +- ** point to zQuot so that all substitutions are made using the +- ** quoted version of the new column name. */ +- zQuot = sqlite3MPrintf(db, "\"%w\"", zNew); +- if( zQuot==0 ){ +- return SQLITE_NOMEM; ++ i64 nQuot = 0; ++ char *zBuf1 = 0; ++ char *zBuf2 = 0; ++ ++ if( zNew ){ ++ /* Set zQuot to point to a buffer containing a quoted copy of the ++ ** identifier zNew. If the corresponding identifier in the original ++ ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to ++ ** point to zQuot so that all substitutions are made using the ++ ** quoted version of the new column name. */ ++ zQuot = sqlite3MPrintf(db, "\"%w\" ", zNew); ++ if( zQuot==0 ){ ++ return SQLITE_NOMEM; ++ }else{ ++ nQuot = sqlite3Strlen30(zQuot)-1; ++ } ++ ++ assert( nQuot>=nNew && nSql>=0 && nNew>=0 ); ++ zOut = sqlite3DbMallocZero(db, (u64)nSql + pRename->nList*(u64)nQuot + 1); + }else{ +- nQuot = sqlite3Strlen30(zQuot); +- } +- if( bQuote ){ +- zNew = zQuot; +- nNew = nQuot; ++ assert( nSql>0 ); ++ zOut = (char*)sqlite3DbMallocZero(db, (2*(u64)nSql + 1) * 3); ++ if( zOut ){ ++ zBuf1 = &zOut[nSql*2+1]; ++ zBuf2 = &zOut[nSql*4+2]; ++ } + } + + /* At this point pRename->pList contains a list of RenameToken objects + ** corresponding to all tokens in the input SQL that must be replaced +- ** with the new column name. All that remains is to construct and +- ** return the edited SQL string. */ +- assert( nQuot>=nNew ); +- zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); ++ ** with the new column name, or with single-quoted versions of themselves. ++ ** All that remains is to construct and return the edited SQL string. */ + if( zOut ){ +- int nOut = nSql; +- memcpy(zOut, zSql, nSql); ++ i64 nOut = nSql; ++ assert( nSql>0 ); ++ memcpy(zOut, zSql, (size_t)nSql); + while( pRename->pList ){ + int iOff; /* Offset of token to replace in zOut */ ++ i64 nReplace; ++ const char *zReplace; + RenameToken *pBest = renameColumnTokenNext(pRename); + +- u32 nReplace; +- const char *zReplace; +- if( sqlite3IsIdChar(*pBest->t.z) ){ +- nReplace = nNew; +- zReplace = zNew; ++ if( zNew ){ ++ if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){ ++ nReplace = nNew; ++ zReplace = zNew; ++ }else{ ++ nReplace = nQuot; ++ zReplace = zQuot; ++ if( pBest->t.z[pBest->t.n]=='"' ) nReplace++; ++ } + }else{ +- nReplace = nQuot; +- zReplace = zQuot; ++ /* Dequote the double-quoted token. Then requote it again, this time ++ ** using single quotes. If the character immediately following the ++ ** original token within the input SQL was a single quote ('), then ++ ** add another space after the new, single-quoted version of the ++ ** token. This is so that (SELECT "string"'alias') maps to ++ ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ ++ memcpy(zBuf1, pBest->t.z, pBest->t.n); ++ zBuf1[pBest->t.n] = 0; ++ sqlite3Dequote(zBuf1); ++ assert( nSql < 0x15555554 /* otherwise malloc would have failed */ ); ++ sqlite3_snprintf((int)(nSql*2), zBuf2, "%Q%s", zBuf1, ++ pBest->t.z[pBest->t.n]=='\'' ? " " : "" ++ ); ++ zReplace = zBuf2; ++ nReplace = sqlite3Strlen30(zReplace); + } + +- iOff = pBest->t.z - zSql; ++ iOff = (int)(pBest->t.z - zSql); + if( pBest->t.n!=nReplace ){ + memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], + nOut - (iOff + pBest->t.n) +@@ -106953,6 +119064,20 @@ static int renameEditSql( + return rc; + } + ++/* ++** Set all pEList->a[].fg.eEName fields in the expression-list to val. ++*/ ++static void renameSetENames(ExprList *pEList, int val){ ++ assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN ); ++ if( pEList ){ ++ int i; ++ for(i=0; inExpr; i++){ ++ assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); ++ pEList->a[i].fg.eEName = val&0x3; ++ } ++ } ++} ++ + /* + ** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming + ** it was read from the schema of database zDb. Return SQLITE_OK if +@@ -106976,7 +119101,7 @@ static int renameResolveTrigger(Parse *pParse){ + /* ALWAYS() because if the table of the trigger does not exist, the + ** error would have been hit before this point */ + if( ALWAYS(pParse->pTriggerTab) ){ +- rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab); ++ rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab)!=0; + } + + /* Resolve symbols in WHEN clause */ +@@ -106992,18 +119117,46 @@ static int renameResolveTrigger(Parse *pParse){ + if( rc==SQLITE_OK && pStep->zTarget ){ + SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); + if( pSrc ){ +- int i; +- for(i=0; inSrc && rc==SQLITE_OK; i++){ +- struct SrcList_item *p = &pSrc->a[i]; +- p->pTab = sqlite3LocateTableItem(pParse, 0, p); +- p->iCursor = pParse->nTab++; +- if( p->pTab==0 ){ +- rc = SQLITE_ERROR; +- }else{ +- p->pTab->nTabRef++; +- rc = sqlite3ViewGetColumnNames(pParse, p->pTab); ++ Select *pSel = sqlite3SelectNew( ++ pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0 ++ ); ++ if( pSel==0 ){ ++ pStep->pExprList = 0; ++ pSrc = 0; ++ rc = SQLITE_NOMEM; ++ }else{ ++ /* pStep->pExprList contains an expression-list used for an UPDATE ++ ** statement. So the a[].zEName values are the RHS of the ++ ** "= " clauses of the UPDATE statement. So, before ++ ** running SelectPrep(), change all the eEName values in ++ ** pStep->pExprList to ENAME_SPAN (from their current value of ++ ** ENAME_NAME). This is to prevent any ids in ON() clauses that are ++ ** part of pSrc from being incorrectly resolved against the ++ ** a[].zEName values as if they were column aliases. */ ++ renameSetENames(pStep->pExprList, ENAME_SPAN); ++ sqlite3SelectPrep(pParse, pSel, 0); ++ renameSetENames(pStep->pExprList, ENAME_NAME); ++ rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; ++ assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); ++ assert( pSrc==pSel->pSrc ); ++ if( pStep->pExprList ) pSel->pEList = 0; ++ pSel->pSrc = 0; ++ sqlite3SelectDelete(db, pSel); ++ } ++ if( pStep->pFrom ){ ++ int i; ++ for(i=0; ipFrom->nSrc && rc==SQLITE_OK; i++){ ++ SrcItem *p = &pStep->pFrom->a[i]; ++ if( p->fg.isSubquery ){ ++ assert( p->u4.pSubq!=0 ); ++ sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0); ++ } + } + } ++ ++ if( db->mallocFailed ){ ++ rc = SQLITE_NOMEM; ++ } + sNC.pSrcList = pSrc; + if( rc==SQLITE_OK && pStep->pWhere ){ + rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); +@@ -107012,9 +119165,8 @@ static int renameResolveTrigger(Parse *pParse){ + rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); + } + assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); +- if( pStep->pUpsert ){ ++ if( pStep->pUpsert && rc==SQLITE_OK ){ + Upsert *pUpsert = pStep->pUpsert; +- assert( rc==SQLITE_OK ); + pUpsert->pUpsertSrc = pSrc; + sNC.uNC.pUpsert = pUpsert; + sNC.ncFlags = NC_UUpsert; +@@ -107063,6 +119215,16 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ + sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere); + sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere); + } ++ if( pStep->pFrom ){ ++ int i; ++ SrcList *pFrom = pStep->pFrom; ++ for(i=0; inSrc; i++){ ++ if( pFrom->a[i].fg.isSubquery ){ ++ assert( pFrom->a[i].u4.pSubq!=0 ); ++ sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect); ++ } ++ } ++ } + } + } + +@@ -107084,13 +119246,13 @@ static void renameParseCleanup(Parse *pParse){ + sqlite3DeleteTrigger(db, pParse->pNewTrigger); + sqlite3DbFree(db, pParse->zErrMsg); + renameTokenFree(db, pParse->pRename); +- sqlite3ParserReset(pParse); ++ sqlite3ParseObjectReset(pParse); + } + + /* + ** SQL function: + ** +-** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld) ++** sqlite_rename_column(SQL,TYPE,OBJ,DB,TABLE,COL,NEWNAME,QUOTE,TEMP) + ** + ** 0. zSql: SQL statement to rewrite + ** 1. type: Type of object ("table", "view" etc.) +@@ -107108,7 +119270,8 @@ static void renameParseCleanup(Parse *pParse){ + ** + ** This function is used internally by the ALTER TABLE RENAME COLUMN command. + ** It is only accessible to SQL created using sqlite3NestedParse(). It is +-** not reachable from ordinary SQL passed into sqlite3_prepare(). ++** not reachable from ordinary SQL passed into sqlite3_prepare() unless the ++** SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test setting is enabled. + */ + static void renameColumnFunc( + sqlite3_context *context, +@@ -107146,7 +119309,7 @@ static void renameColumnFunc( + sqlite3BtreeLeaveAll(db); + return; + } +- zOld = pTab->aCol[iCol].zName; ++ zOld = pTab->aCol[iCol].zCnName; + memset(&sCtx, 0, sizeof(sCtx)); + sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); + +@@ -107165,9 +119328,9 @@ static void renameColumnFunc( + sCtx.pTab = pTab; + if( rc!=SQLITE_OK ) goto renameColumnFunc_done; + if( sParse.pNewTable ){ +- Select *pSelect = sParse.pNewTable->pSelect; +- if( pSelect ){ +- pSelect->selFlags &= ~SF_View; ++ if( IsView(sParse.pNewTable) ){ ++ Select *pSelect = sParse.pNewTable->u.view.pSelect; ++ pSelect->selFlags &= ~(u32)SF_View; + sParse.rc = SQLITE_OK; + sqlite3SelectPrep(&sParse, pSelect, 0); + rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); +@@ -107175,16 +119338,17 @@ static void renameColumnFunc( + sqlite3WalkSelect(&sWalker, pSelect); + } + if( rc!=SQLITE_OK ) goto renameColumnFunc_done; +- }else{ ++ }else if( IsOrdinaryTable(sParse.pNewTable) ){ + /* A regular table */ + int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); + FKey *pFKey; +- assert( sParse.pNewTable->pSelect==0 ); + sCtx.pTab = sParse.pNewTable; + if( bFKOnly==0 ){ +- renameTokenFind( +- &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName +- ); ++ if( iColnCol ){ ++ renameTokenFind( ++ &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zCnName ++ ); ++ } + if( sCtx.iCol<0 ){ + renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey); + } +@@ -107195,14 +119359,17 @@ static void renameColumnFunc( + for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ + sqlite3WalkExprList(&sWalker, pIdx->aColExpr); + } +- } + #ifndef SQLITE_OMIT_GENERATED_COLUMNS +- for(i=0; inCol; i++){ +- sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); +- } ++ for(i=0; inCol; i++){ ++ Expr *pExpr = sqlite3ColumnExpr(sParse.pNewTable, ++ &sParse.pNewTable->aCol[i]); ++ sqlite3WalkExpr(&sWalker, pExpr); ++ } + #endif ++ } + +- for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ ++ assert( IsOrdinaryTable(sParse.pNewTable) ); ++ for(pFKey=sParse.pNewTable->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ + for(i=0; inCol; i++){ + if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ + renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); +@@ -107253,8 +119420,10 @@ static void renameColumnFunc( + + renameColumnFunc_done: + if( rc!=SQLITE_OK ){ +- if( sParse.zErrMsg ){ +- renameColumnParseError(context, 0, argv[1], argv[2], &sParse); ++ if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){ ++ sqlite3_result_value(context, argv[0]); ++ }else if( sParse.zErrMsg ){ ++ renameColumnParseError(context, "", argv[1], argv[2], &sParse); + }else{ + sqlite3_result_error_code(context, rc); + } +@@ -107273,7 +119442,10 @@ renameColumnFunc_done: + */ + static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ + RenameCtx *p = pWalker->u.pRename; +- if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){ ++ if( pExpr->op==TK_COLUMN ++ && ALWAYS(ExprUseYTab(pExpr)) ++ && p->pTab==pExpr->y.pTab ++ ){ + renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab); + } + return WRC_Continue; +@@ -107286,14 +119458,18 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ + int i; + RenameCtx *p = pWalker->u.pRename; + SrcList *pSrc = pSelect->pSrc; +- if( pSelect->selFlags & SF_View ) return WRC_Prune; +- if( pSrc==0 ){ ++ if( pSelect->selFlags & (SF_View|SF_CopyCte) ){ ++ testcase( pSelect->selFlags & SF_View ); ++ testcase( pSelect->selFlags & SF_CopyCte ); ++ return WRC_Prune; ++ } ++ if( NEVER(pSrc==0) ){ + assert( pWalker->pParse->db->mallocFailed ); + return WRC_Abort; + } + for(i=0; inSrc; i++){ +- struct SrcList_item *pItem = &pSrc->a[i]; +- if( pItem->pTab==p->pTab ){ ++ SrcItem *pItem = &pSrc->a[i]; ++ if( pItem->pSTab==p->pTab ){ + renameTokenFind(pWalker->pParse, p, pItem->zName); + } + } +@@ -107364,28 +119540,31 @@ static void renameTableFunc( + if( sParse.pNewTable ){ + Table *pTab = sParse.pNewTable; + +- if( pTab->pSelect ){ ++ if( IsView(pTab) ){ + if( isLegacy==0 ){ +- Select *pSelect = pTab->pSelect; ++ Select *pSelect = pTab->u.view.pSelect; + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = &sParse; + + assert( pSelect->selFlags & SF_View ); +- pSelect->selFlags &= ~SF_View; +- sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); ++ pSelect->selFlags &= ~(u32)SF_View; ++ sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); + if( sParse.nErr ){ + rc = sParse.rc; + }else{ +- sqlite3WalkSelect(&sWalker, pTab->pSelect); ++ sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); + } + } + }else{ + /* Modify any FK definitions to point to the new table. */ + #ifndef SQLITE_OMIT_FOREIGN_KEY +- if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){ ++ if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys)) ++ && !IsVirtual(pTab) ++ ){ + FKey *pFKey; +- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ ++ assert( IsOrdinaryTable(pTab) ); ++ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ + if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ + renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo); + } +@@ -107431,6 +119610,15 @@ static void renameTableFunc( + if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ + renameTokenFind(&sParse, &sCtx, pStep->zTarget); + } ++ if( pStep->pFrom ){ ++ int i; ++ for(i=0; ipFrom->nSrc; i++){ ++ SrcItem *pItem = &pStep->pFrom->a[i]; ++ if( 0==sqlite3_stricmp(pItem->zName, zOld) ){ ++ renameTokenFind(&sParse, &sCtx, pItem->zName); ++ } ++ } ++ } + } + } + } +@@ -107442,8 +119630,10 @@ static void renameTableFunc( + rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote); + } + if( rc!=SQLITE_OK ){ +- if( sParse.zErrMsg ){ +- renameColumnParseError(context, 0, argv[1], argv[2], &sParse); ++ if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){ ++ sqlite3_result_value(context, argv[3]); ++ }else if( sParse.zErrMsg ){ ++ renameColumnParseError(context, "", argv[1], argv[2], &sParse); + }else{ + sqlite3_result_error_code(context, rc); + } +@@ -107460,7 +119650,131 @@ static void renameTableFunc( + return; + } + +-/* ++static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){ ++ if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){ ++ renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr); ++ } ++ return WRC_Continue; ++} ++ ++/* SQL function: sqlite_rename_quotefix(DB,SQL) ++** ++** Rewrite the DDL statement "SQL" so that any string literals that use ++** double-quotes use single quotes instead. ++** ++** Two arguments must be passed: ++** ++** 0: Database name ("main", "temp" etc.). ++** 1: SQL statement to edit. ++** ++** The returned value is the modified SQL statement. For example, given ++** the database schema: ++** ++** CREATE TABLE t1(a, b, c); ++** ++** SELECT sqlite_rename_quotefix('main', ++** 'CREATE VIEW v1 AS SELECT "a", "string" FROM t1' ++** ); ++** ++** returns the string: ++** ++** CREATE VIEW v1 AS SELECT "a", 'string' FROM t1 ++** ++** If there is a error in the input SQL, then raise an error, except ++** if PRAGMA writable_schema=ON, then just return the input string ++** unmodified following an error. ++*/ ++static void renameQuotefixFunc( ++ sqlite3_context *context, ++ int NotUsed, ++ sqlite3_value **argv ++){ ++ sqlite3 *db = sqlite3_context_db_handle(context); ++ char const *zDb = (const char*)sqlite3_value_text(argv[0]); ++ char const *zInput = (const char*)sqlite3_value_text(argv[1]); ++ ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ sqlite3_xauth xAuth = db->xAuth; ++ db->xAuth = 0; ++#endif ++ ++ sqlite3BtreeEnterAll(db); ++ ++ UNUSED_PARAMETER(NotUsed); ++ if( zDb && zInput ){ ++ int rc; ++ Parse sParse; ++ rc = renameParseSql(&sParse, zDb, db, zInput, 0); ++ ++ if( rc==SQLITE_OK ){ ++ RenameCtx sCtx; ++ Walker sWalker; ++ ++ /* Walker to find tokens that need to be replaced. */ ++ memset(&sCtx, 0, sizeof(RenameCtx)); ++ memset(&sWalker, 0, sizeof(Walker)); ++ sWalker.pParse = &sParse; ++ sWalker.xExprCallback = renameQuotefixExprCb; ++ sWalker.xSelectCallback = renameColumnSelectCb; ++ sWalker.u.pRename = &sCtx; ++ ++ if( sParse.pNewTable ){ ++ if( IsView(sParse.pNewTable) ){ ++ Select *pSelect = sParse.pNewTable->u.view.pSelect; ++ pSelect->selFlags &= ~(u32)SF_View; ++ sParse.rc = SQLITE_OK; ++ sqlite3SelectPrep(&sParse, pSelect, 0); ++ rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); ++ if( rc==SQLITE_OK ){ ++ sqlite3WalkSelect(&sWalker, pSelect); ++ } ++ }else{ ++ int i; ++ sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); ++#ifndef SQLITE_OMIT_GENERATED_COLUMNS ++ for(i=0; inCol; i++){ ++ sqlite3WalkExpr(&sWalker, ++ sqlite3ColumnExpr(sParse.pNewTable, ++ &sParse.pNewTable->aCol[i])); ++ } ++#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ ++ } ++ }else if( sParse.pNewIndex ){ ++ sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); ++ sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); ++ }else{ ++#ifndef SQLITE_OMIT_TRIGGER ++ rc = renameResolveTrigger(&sParse); ++ if( rc==SQLITE_OK ){ ++ renameWalkTrigger(&sWalker, sParse.pNewTrigger); ++ } ++#endif /* SQLITE_OMIT_TRIGGER */ ++ } ++ ++ if( rc==SQLITE_OK ){ ++ rc = renameEditSql(context, &sCtx, zInput, 0, 0); ++ } ++ renameTokenFree(db, sCtx.pList); ++ } ++ if( rc!=SQLITE_OK ){ ++ if( sqlite3WritableSchema(db) && rc==SQLITE_ERROR ){ ++ sqlite3_result_value(context, argv[1]); ++ }else{ ++ sqlite3_result_error_code(context, rc); ++ } ++ } ++ renameParseCleanup(&sParse); ++ } ++ ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ db->xAuth = xAuth; ++#endif ++ ++ sqlite3BtreeLeaveAll(db); ++} ++ ++/* Function: sqlite_rename_test(DB,SQL,TYPE,NAME,ISTEMP,WHEN,DQS) ++** + ** An SQL user function that checks that there are no parse or symbol + ** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement. + ** After an ALTER TABLE .. RENAME operation is performed and the schema +@@ -107472,12 +119786,16 @@ static void renameTableFunc( + ** 2: Object type ("view", "table", "trigger" or "index"). + ** 3: Object name. + ** 4: True if object is from temp schema. ++** 5: "when" part of error message. ++** 6: True to disable the DQS quirk when parsing SQL. + ** +-** Unless it finds an error, this function normally returns NULL. However, it +-** returns integer value 1 if: ++** The return value is computed as follows: + ** +-** * the SQL argument creates a trigger, and +-** * the table that the trigger is attached to is in database zDb. ++** A. If an error is seen and not in PRAGMA writable_schema=ON mode, ++** then raise the error. ++** B. Else if a trigger is created and the the table that the trigger is ++** attached to is in database zDb, then return 1. ++** C. Otherwise return NULL. + */ + static void renameTableTest( + sqlite3_context *context, +@@ -107489,6 +119807,8 @@ static void renameTableTest( + char const *zInput = (const char*)sqlite3_value_text(argv[1]); + int bTemp = sqlite3_value_int(argv[4]); + int isLegacy = (db->flags & SQLITE_LegacyAlter); ++ char const *zWhen = (const char*)sqlite3_value_text(argv[5]); ++ int bNoDQS = sqlite3_value_int(argv[6]); + + #ifndef SQLITE_OMIT_AUTHORIZATION + sqlite3_xauth xAuth = db->xAuth; +@@ -107496,16 +119816,20 @@ static void renameTableTest( + #endif + + UNUSED_PARAMETER(NotUsed); ++ + if( zDb && zInput ){ + int rc; + Parse sParse; ++ u64 flags = db->flags; ++ if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); + rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); ++ db->flags = flags; + if( rc==SQLITE_OK ){ +- if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ ++ if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = &sParse; +- sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); ++ sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC); + if( sParse.nErr ) rc = sParse.rc; + } + +@@ -107516,13 +119840,17 @@ static void renameTableTest( + if( rc==SQLITE_OK ){ + int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema); + int i2 = sqlite3FindDbName(db, zDb); +- if( i1==i2 ) sqlite3_result_int(context, 1); ++ if( i1==i2 ){ ++ /* Handle output case B */ ++ sqlite3_result_int(context, 1); ++ } + } + } + } + +- if( rc!=SQLITE_OK ){ +- renameColumnParseError(context, 1, argv[2], argv[3], &sParse); ++ if( rc!=SQLITE_OK && zWhen && !sqlite3WritableSchema(db) ){ ++ /* Output case A */ ++ renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse); + } + renameParseCleanup(&sParse); + } +@@ -107532,14 +119860,237 @@ static void renameTableTest( + #endif + } + ++/* ++** The implementation of internal UDF sqlite_drop_column(). ++** ++** Arguments: ++** ++** argv[0]: An integer - the index of the schema containing the table ++** argv[1]: CREATE TABLE statement to modify. ++** argv[2]: An integer - the index of the column to remove. ++** ++** The value returned is a string containing the CREATE TABLE statement ++** with column argv[2] removed. ++*/ ++static void dropColumnFunc( ++ sqlite3_context *context, ++ int NotUsed, ++ sqlite3_value **argv ++){ ++ sqlite3 *db = sqlite3_context_db_handle(context); ++ int iSchema = sqlite3_value_int(argv[0]); ++ const char *zSql = (const char*)sqlite3_value_text(argv[1]); ++ int iCol = sqlite3_value_int(argv[2]); ++ const char *zDb = db->aDb[iSchema].zDbSName; ++ int rc; ++ Parse sParse; ++ RenameToken *pCol; ++ Table *pTab; ++ const char *zEnd; ++ char *zNew = 0; ++ ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ sqlite3_xauth xAuth = db->xAuth; ++ db->xAuth = 0; ++#endif ++ ++ UNUSED_PARAMETER(NotUsed); ++ rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); ++ if( rc!=SQLITE_OK ) goto drop_column_done; ++ pTab = sParse.pNewTable; ++ if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ ++ /* This can happen if the sqlite_schema table is corrupt */ ++ rc = SQLITE_CORRUPT_BKPT; ++ goto drop_column_done; ++ } ++ ++ pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); ++ if( iColnCol-1 ){ ++ RenameToken *pEnd; ++ pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName); ++ zEnd = (const char*)pEnd->t.z; ++ }else{ ++ assert( IsOrdinaryTable(pTab) ); ++ zEnd = (const char*)&zSql[pTab->u.tab.addColOffset]; ++ while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--; ++ } ++ ++ zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd); ++ sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT); ++ sqlite3_free(zNew); ++ ++drop_column_done: ++ renameParseCleanup(&sParse); ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ db->xAuth = xAuth; ++#endif ++ if( rc!=SQLITE_OK ){ ++ sqlite3_result_error_code(context, rc); ++ } ++} ++ ++/* ++** This function is called by the parser upon parsing an ++** ++** ALTER TABLE pSrc DROP COLUMN pName ++** ++** statement. Argument pSrc contains the possibly qualified name of the ++** table being edited, and token pName the name of the column to drop. ++*/ ++SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){ ++ sqlite3 *db = pParse->db; /* Database handle */ ++ Table *pTab; /* Table to modify */ ++ int iDb; /* Index of db containing pTab in aDb[] */ ++ const char *zDb; /* Database containing pTab ("main" etc.) */ ++ char *zCol = 0; /* Name of column to drop */ ++ int iCol; /* Index of column zCol in pTab->aCol[] */ ++ ++ /* Look up the table being altered. */ ++ assert( pParse->pNewTable==0 ); ++ assert( sqlite3BtreeHoldsAllMutexes(db) ); ++ if( NEVER(db->mallocFailed) ) goto exit_drop_column; ++ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); ++ if( !pTab ) goto exit_drop_column; ++ ++ /* Make sure this is not an attempt to ALTER a view, virtual table or ++ ** system table. */ ++ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column; ++ if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column; ++ ++ /* Find the index of the column being dropped. */ ++ zCol = sqlite3NameFromToken(db, pName); ++ if( zCol==0 ){ ++ assert( db->mallocFailed ); ++ goto exit_drop_column; ++ } ++ iCol = sqlite3ColumnIndex(pTab, zCol); ++ if( iCol<0 ){ ++ sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pName); ++ goto exit_drop_column; ++ } ++ ++ /* Do not allow the user to drop a PRIMARY KEY column or a column ++ ** constrained by a UNIQUE constraint. */ ++ if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){ ++ sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"", ++ (pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE", ++ zCol ++ ); ++ goto exit_drop_column; ++ } ++ ++ /* Do not allow the number of columns to go to zero */ ++ if( pTab->nCol<=1 ){ ++ sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol); ++ goto exit_drop_column; ++ } ++ ++ /* Edit the sqlite_schema table */ ++ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); ++ assert( iDb>=0 ); ++ zDb = db->aDb[iDb].zDbSName; ++#ifndef SQLITE_OMIT_AUTHORIZATION ++ /* Invoke the authorization callback. */ ++ if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ ++ goto exit_drop_column; ++ } ++#endif ++ renameTestSchema(pParse, zDb, iDb==1, "", 0); ++ renameFixQuotes(pParse, zDb, iDb==1); ++ sqlite3NestedParse(pParse, ++ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " ++ "sql = sqlite_drop_column(%d, sql, %d) " ++ "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" ++ , zDb, iDb, iCol, pTab->zName ++ ); ++ ++ /* Drop and reload the database schema. */ ++ renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); ++ renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1); ++ ++ /* Edit rows of table on disk */ ++ if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ ++ int i; ++ int addr; ++ int reg; ++ int regRec; ++ Index *pPk = 0; ++ int nField = 0; /* Number of non-virtual columns after drop */ ++ int iCur; ++ Vdbe *v = sqlite3GetVdbe(pParse); ++ iCur = pParse->nTab++; ++ sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); ++ addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); ++ reg = ++pParse->nMem; ++ if( HasRowid(pTab) ){ ++ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg); ++ pParse->nMem += pTab->nCol; ++ }else{ ++ pPk = sqlite3PrimaryKeyIndex(pTab); ++ pParse->nMem += pPk->nColumn; ++ for(i=0; inKeyCol; i++){ ++ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, reg+i+1); ++ } ++ nField = pPk->nKeyCol; ++ } ++ regRec = ++pParse->nMem; ++ for(i=0; inCol; i++){ ++ if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ ++ int regOut; ++ if( pPk ){ ++ int iPos = sqlite3TableColumnToIndex(pPk, i); ++ int iColPos = sqlite3TableColumnToIndex(pPk, iCol); ++ if( iPosnKeyCol ) continue; ++ regOut = reg+1+iPos-(iPos>iColPos); ++ }else{ ++ regOut = reg+1+nField; ++ } ++ if( i==pTab->iPKey ){ ++ sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); ++ }else{ ++ char aff = pTab->aCol[i].affinity; ++ if( aff==SQLITE_AFF_REAL ){ ++ pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC; ++ } ++ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); ++ pTab->aCol[i].affinity = aff; ++ } ++ nField++; ++ } ++ } ++ if( nField==0 ){ ++ /* dbsqlfuzz 5f09e7bcc78b4954d06bf9f2400d7715f48d1fef */ ++ pParse->nMem++; ++ sqlite3VdbeAddOp2(v, OP_Null, 0, reg+1); ++ nField = 1; ++ } ++ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec); ++ if( pPk ){ ++ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol); ++ }else{ ++ sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg); ++ } ++ sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); ++ ++ sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v); ++ sqlite3VdbeJumpHere(v, addr); ++ } ++ ++exit_drop_column: ++ sqlite3DbFree(db, zCol); ++ sqlite3SrcListDelete(db, pSrc); ++} ++ + /* + ** Register built-in functions used to help implement ALTER TABLE + */ + SQLITE_PRIVATE void sqlite3AlterFunctions(void){ + static FuncDef aAlterTableFuncs[] = { +- INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), +- INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), +- INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest), ++ INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), ++ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), ++ INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), ++ INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), ++ INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), + }; + sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); + } +@@ -107764,7 +120315,8 @@ static void openStatTable( + sqlite3NestedParse(pParse, + "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols + ); +- aRoot[i] = (u32)pParse->regRoot; ++ assert( pParse->isCreate || pParse->nErr ); ++ aRoot[i] = (u32)pParse->u1.cr.regRoot; + aCreateTbl[i] = OPFLAG_P2ISREG; + } + }else{ +@@ -107813,9 +120365,9 @@ static void openStatTable( + typedef struct StatAccum StatAccum; + typedef struct StatSample StatSample; + struct StatSample { +- tRowcnt *anEq; /* sqlite_stat4.nEq */ + tRowcnt *anDLt; /* sqlite_stat4.nDLt */ + #ifdef SQLITE_ENABLE_STAT4 ++ tRowcnt *anEq; /* sqlite_stat4.nEq */ + tRowcnt *anLt; /* sqlite_stat4.nLt */ + union { + i64 iRowid; /* Rowid in main table of the key */ +@@ -107955,7 +120507,7 @@ static void statInit( + int nCol; /* Number of columns in index being sampled */ + int nKeyCol; /* Number of key columns */ + int nColUp; /* nCol rounded up for alignment */ +- int n; /* Bytes of space to allocate */ ++ i64 n; /* Bytes of space to allocate */ + sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */ + #ifdef SQLITE_ENABLE_STAT4 + /* Maximum number of samples. 0 if STAT4 data is not collected */ +@@ -107973,16 +120525,15 @@ static void statInit( + + /* Allocate the space required for the StatAccum object */ + n = sizeof(*p) +- + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ +- + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ ++ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ + #ifdef SQLITE_ENABLE_STAT4 ++ n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */ + if( mxSample ){ + n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ + + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ + + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); + } + #endif +- db = sqlite3_context_db_handle(context); + p = sqlite3DbMallocZero(db, n); + if( p==0 ){ + sqlite3_result_error_nomem(context); +@@ -107992,14 +120543,14 @@ static void statInit( + p->db = db; + p->nEst = sqlite3_value_int64(argv[2]); + p->nRow = 0; +- p->nLimit = sqlite3_value_int64(argv[3]); ++ p->nLimit = sqlite3_value_int(argv[3]); + p->nCol = nCol; + p->nKeyCol = nKeyCol; + p->nSkipAhead = 0; + p->current.anDLt = (tRowcnt*)&p[1]; +- p->current.anEq = &p->current.anDLt[nColUp]; + + #ifdef SQLITE_ENABLE_STAT4 ++ p->current.anEq = &p->current.anDLt[nColUp]; + p->mxSample = p->nLimit==0 ? mxSample : 0; + if( mxSample ){ + u8 *pSpace; /* Allocated space not yet assigned */ +@@ -108266,7 +120817,9 @@ static void statPush( + + if( p->nRow==0 ){ + /* This is the first call to this function. Do initialization. */ ++#ifdef SQLITE_ENABLE_STAT4 + for(i=0; inCol; i++) p->current.anEq[i] = 1; ++#endif + }else{ + /* Second and subsequent calls get processed here */ + #ifdef SQLITE_ENABLE_STAT4 +@@ -108275,15 +120828,17 @@ static void statPush( + + /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply + ** to the current row of the index. */ ++#ifdef SQLITE_ENABLE_STAT4 + for(i=0; icurrent.anEq[i]++; + } ++#endif + for(i=iChng; inCol; i++){ + p->current.anDLt[i]++; + #ifdef SQLITE_ENABLE_STAT4 + if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; +-#endif + p->current.anEq[i] = 1; ++#endif + } + } + +@@ -108397,32 +120952,31 @@ static void statGet( + ** * "WHERE a=? AND b=?" matches 2 rows. + ** + ** If D is the count of distinct values and K is the total number of +- ** rows, then each estimate is computed as: ++ ** rows, then each estimate is usually computed as: + ** + ** I = (K+D-1)/D ++ ** ++ ** In other words, I is K/D rounded up to the next whole integer. ++ ** However, if I is between 1.0 and 1.1 (in other words if I is ++ ** close to 1.0 but just a little larger) then do not round up but ++ ** instead keep the I value at 1.0. + */ +- char *z; +- int i; +- +- char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 ); +- if( zRet==0 ){ +- sqlite3_result_error_nomem(context); +- return; +- } ++ sqlite3_str sStat; /* Text of the constructed "stat" line */ ++ int i; /* Loop counter */ + +- sqlite3_snprintf(24, zRet, "%llu", ++ sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100); ++ sqlite3_str_appendf(&sStat, "%llu", + p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); +- z = zRet + sqlite3Strlen30(zRet); + for(i=0; inKeyCol; i++){ + u64 nDistinct = p->current.anDLt[i] + 1; + u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; +- sqlite3_snprintf(24, z, " %llu", iVal); +- z += sqlite3Strlen30(z); +- assert( p->current.anEq[i] ); ++ if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; ++ sqlite3_str_appendf(&sStat, " %llu", iVal); ++#ifdef SQLITE_ENABLE_STAT4 ++ assert( p->current.anEq[i] || p->nRow==0 ); ++#endif + } +- assert( z[0]=='\0' && z>zRet ); +- +- sqlite3_result_text(context, zRet, -1, sqlite3_free); ++ sqlite3ResultStrAccum(context, &sStat); + } + #ifdef SQLITE_ENABLE_STAT4 + else if( eCall==STAT_GET_ROWID ){ +@@ -108441,6 +120995,8 @@ static void statGet( + } + }else{ + tRowcnt *aCnt = 0; ++ sqlite3_str sStat; ++ int i; + + assert( p->iGetnSample ); + switch( eCall ){ +@@ -108452,23 +121008,12 @@ static void statGet( + break; + } + } +- +- { +- char *zRet = sqlite3MallocZero(p->nCol * 25); +- if( zRet==0 ){ +- sqlite3_result_error_nomem(context); +- }else{ +- int i; +- char *z = zRet; +- for(i=0; inCol; i++){ +- sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]); +- z += sqlite3Strlen30(z); +- } +- assert( z[0]=='\0' && z>zRet ); +- z[-1] = '\0'; +- sqlite3_result_text(context, zRet, -1, sqlite3_free); +- } ++ sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100); ++ for(i=0; inCol; i++){ ++ sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]); + } ++ if( sStat.nChar ) sStat.nChar--; ++ sqlite3ResultStrAccum(context, &sStat); + } + #endif /* SQLITE_ENABLE_STAT4 */ + #ifndef SQLITE_DEBUG +@@ -108515,9 +121060,10 @@ static void analyzeVdbeCommentIndexWithColumnName( + if( NEVER(i==XN_ROWID) ){ + VdbeComment((v,"%s.rowid",pIdx->zName)); + }else if( i==XN_EXPR ){ ++ assert( pIdx->bHasExpr ); + VdbeComment((v,"%s.expr(%d)",pIdx->zName, k)); + }else{ +- VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zName)); ++ VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName)); + } + } + #else +@@ -108555,16 +121101,20 @@ static void analyzeOneTable( + int regIdxname = iMem++; /* Register containing index name */ + int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ + int regPrev = iMem; /* MUST BE LAST (see below) */ ++#ifdef SQLITE_ENABLE_STAT4 ++ int doOnce = 1; /* Flag for a one-time computation */ ++#endif + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK + Table *pStat1 = 0; + #endif + +- pParse->nMem = MAX(pParse->nMem, iMem); ++ sqlite3TouchRegister(pParse, iMem); ++ assert( sqlite3NoTempsInRange(pParse, regNewRowid, iMem) ); + v = sqlite3GetVdbe(pParse); + if( v==0 || NEVER(pTab==0) ){ + return; + } +- if( pTab->tnum==0 ){ ++ if( !IsOrdinaryTable(pTab) ){ + /* Do not gather statistics on views or virtual tables */ + return; + } +@@ -108591,7 +121141,7 @@ static void analyzeOneTable( + memcpy(pStat1->zName, "sqlite_stat1", 13); + pStat1->nCol = 3; + pStat1->iPKey = -1; +- sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNBLOB); ++ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNAMIC); + } + #endif + +@@ -108608,7 +121158,7 @@ static void analyzeOneTable( + + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int nCol; /* Number of columns in pIdx. "N" */ +- int addrRewind; /* Address of "OP_Rewind iIdxCur" */ ++ int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */ + int addrNextRow; /* Address of "next_row:" */ + const char *zIdxName; /* Name of the index */ + int nColTest; /* Number of columns to test for changes */ +@@ -108632,9 +121182,14 @@ static void analyzeOneTable( + /* + ** Pseudo-code for loop that calls stat_push(): + ** +- ** Rewind csr +- ** if eof(csr) goto end_of_scan; + ** regChng = 0 ++ ** Rewind csr ++ ** if eof(csr){ ++ ** stat_init() with count = 0; ++ ** goto end_of_scan; ++ ** } ++ ** count() ++ ** stat_init() + ** goto chng_addr_0; + ** + ** next_row: +@@ -108665,7 +121220,7 @@ static void analyzeOneTable( + ** the regPrev array and a trailing rowid (the rowid slot is required + ** when building a record to insert into the sample column of + ** the sqlite_stat4 table. */ +- pParse->nMem = MAX(pParse->nMem, regPrev+nColTest); ++ sqlite3TouchRegister(pParse, regPrev+nColTest); + + /* Open a read-only cursor on the index being analyzed. */ + assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); +@@ -108673,41 +121228,36 @@ static void analyzeOneTable( + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); + VdbeComment((v, "%s", pIdx->zName)); + +- /* Invoke the stat_init() function. The arguments are: ++ /* Implementation of the following: + ** ++ ** regChng = 0 ++ ** Rewind csr ++ ** if eof(csr){ ++ ** stat_init() with count = 0; ++ ** goto end_of_scan; ++ ** } ++ ** count() ++ ** stat_init() ++ ** goto chng_addr_0; ++ */ ++ assert( regTemp2==regStat+4 ); ++ sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); ++ ++ /* Arguments to stat_init(): + ** (1) the number of columns in the index including the rowid + ** (or for a WITHOUT ROWID table, the number of PK columns), + ** (2) the number of columns in the key without the rowid/pk +- ** (3) estimated number of rows in the index, +- */ ++ ** (3) estimated number of rows in the index. */ + sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); + assert( regRowid==regStat+2 ); + sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); +-#ifdef SQLITE_ENABLE_STAT4 +- if( OptimizationEnabled(db, SQLITE_Stat4) ){ +- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp); +- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); +- VdbeCoverage(v); +- }else +-#endif +- { +- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); +- VdbeCoverage(v); +- sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1); +- } +- assert( regTemp2==regStat+4 ); +- sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); ++ sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, ++ OptimizationDisabled(db, SQLITE_Stat4)); + sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, + &statInitFuncdef, 0); ++ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); ++ VdbeCoverage(v); + +- /* Implementation of the following: +- ** +- ** Rewind csr +- ** if eof(csr) goto end_of_scan; +- ** regChng = 0 +- ** goto next_push_0; +- ** +- */ + sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); + addrNextRow = sqlite3VdbeCurrentAddr(v); + +@@ -108814,6 +121364,12 @@ static void analyzeOneTable( + } + + /* Add the entry to the stat1 table. */ ++ if( pIdx->pPartIdxWhere ){ ++ /* Partial indexes might get a zero-entry in sqlite_stat1. But ++ ** an empty table is omitted from sqlite_stat1. */ ++ sqlite3VdbeJumpHere(v, addrGotoEnd); ++ addrGotoEnd = 0; ++ } + callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); + assert( "BBB"[0]==SQLITE_AFF_TEXT ); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); +@@ -108837,7 +121393,42 @@ static void analyzeOneTable( + int addrIsNull; + u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; + +- pParse->nMem = MAX(pParse->nMem, regCol+nCol); ++ /* No STAT4 data is generated if the number of rows is zero */ ++ if( addrGotoEnd==0 ){ ++ sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER); ++ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); ++ VdbeCoverage(v); ++ } ++ ++ if( doOnce ){ ++ int mxCol = nCol; ++ Index *pX; ++ ++ /* Compute the maximum number of columns in any index */ ++ for(pX=pTab->pIndex; pX; pX=pX->pNext){ ++ int nColX; /* Number of columns in pX */ ++ if( !HasRowid(pTab) && IsPrimaryKeyIndex(pX) ){ ++ nColX = pX->nKeyCol; ++ }else{ ++ nColX = pX->nColumn; ++ } ++ if( nColX>mxCol ) mxCol = nColX; ++ } ++ ++ /* Allocate space to compute results for the largest index */ ++ sqlite3TouchRegister(pParse, regCol+mxCol); ++ doOnce = 0; ++#ifdef SQLITE_DEBUG ++ /* Verify that the call to sqlite3ClearTempRegCache() below ++ ** really is needed. ++ ** https://sqlite.org/forum/forumpost/83cb4a95a0 (2023-03-25) ++ */ ++ testcase( !sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); ++#endif ++ sqlite3ClearTempRegCache(pParse); /* tag-20230325-1 */ ++ assert( sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); ++ } ++ assert( sqlite3NoTempsInRange(pParse, regEq, regCol+nCol) ); + + addrNext = sqlite3VdbeCurrentAddr(v); + callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid); +@@ -108861,7 +121452,7 @@ static void analyzeOneTable( + #endif /* SQLITE_ENABLE_STAT4 */ + + /* End of analysis */ +- sqlite3VdbeJumpHere(v, addrRewind); ++ if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd); + } + + +@@ -108918,6 +121509,11 @@ static void analyzeDatabase(Parse *pParse, int iDb){ + for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ + Table *pTab = (Table*)sqliteHashData(k); + analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab); ++#ifdef SQLITE_ENABLE_STAT4 ++ iMem = sqlite3FirstAvailableRegister(pParse, iMem); ++#else ++ assert( iMem==sqlite3FirstAvailableRegister(pParse,iMem) ); ++#endif + } + loadAnalysis(pParse, iDb); + } +@@ -109158,6 +121754,8 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ + ** and its contents. + */ + SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ ++ assert( db!=0 ); ++ assert( pIdx!=0 ); + #ifdef SQLITE_ENABLE_STAT4 + if( pIdx->aSample ){ + int j; +@@ -109167,7 +121765,7 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ + } + sqlite3DbFree(db, pIdx->aSample); + } +- if( db && db->pnBytesFreed==0 ){ ++ if( db->pnBytesFreed==0 ){ + pIdx->nSample = 0; + pIdx->aSample = 0; + } +@@ -109290,12 +121888,13 @@ static int loadStatTbl( + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + int nIdxCol = 1; /* Number of columns in stat4 records */ + +- char *zIndex; /* Index name */ +- Index *pIdx; /* Pointer to the index object */ +- int nSample; /* Number of samples */ +- int nByte; /* Bytes of space required */ +- int i; /* Bytes of space required */ +- tRowcnt *pSpace; ++ char *zIndex; /* Index name */ ++ Index *pIdx; /* Pointer to the index object */ ++ int nSample; /* Number of samples */ ++ i64 nByte; /* Bytes of space required */ ++ i64 i; /* Bytes of space required */ ++ tRowcnt *pSpace; /* Available allocated memory space */ ++ u8 *pPtr; /* Available memory as a u8 for easier manipulation */ + + zIndex = (char *)sqlite3_column_text(pStmt, 0); + if( zIndex==0 ) continue; +@@ -109303,6 +121902,10 @@ static int loadStatTbl( + pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); + assert( pIdx==0 || pIdx->nSample==0 ); + if( pIdx==0 ) continue; ++ if( pIdx->aSample!=0 ){ ++ /* The same index appears in sqlite_stat4 under multiple names */ ++ continue; ++ } + assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 ); + if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ + nIdxCol = pIdx->nKeyCol; +@@ -109310,7 +121913,8 @@ static int loadStatTbl( + nIdxCol = pIdx->nColumn; + } + pIdx->nSampleCol = nIdxCol; +- nByte = sizeof(IndexSample) * nSample; ++ pIdx->mxSample = nSample; ++ nByte = ROUND8(sizeof(IndexSample) * nSample); + nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; + nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ + +@@ -109319,8 +121923,12 @@ static int loadStatTbl( + sqlite3_finalize(pStmt); + return SQLITE_NOMEM_BKPT; + } +- pSpace = (tRowcnt*)&pIdx->aSample[nSample]; ++ pPtr = (u8*)pIdx->aSample; ++ pPtr += ROUND8(nSample*sizeof(pIdx->aSample[0])); ++ pSpace = (tRowcnt*)pPtr; ++ assert( EIGHT_BYTE_ALIGNMENT( pSpace ) ); + pIdx->aAvgEq = pSpace; pSpace += nIdxCol; ++ pIdx->pTable->tabFlags |= TF_HasStat4; + for(i=0; iaSample[i].anEq = pSpace; pSpace += nIdxCol; + pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol; +@@ -109348,6 +121956,11 @@ static int loadStatTbl( + if( zIndex==0 ) continue; + pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); + if( pIdx==0 ) continue; ++ if( pIdx->nSample>=pIdx->mxSample ){ ++ /* Too many slots used because the same index appears in ++ ** sqlite_stat4 using multiple names */ ++ continue; ++ } + /* This next condition is true if data has already been loaded from + ** the sqlite_stat4 table. */ + nCol = pIdx->nSampleCol; +@@ -109360,14 +121973,15 @@ static int loadStatTbl( + decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0); + decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0); + +- /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer. ++ /* Take a copy of the sample. Add 8 extra 0x00 bytes the end of the buffer. + ** This is in case the sample record is corrupted. In that case, the + ** sqlite3VdbeRecordCompare() may read up to two varints past the + ** end of the allocated buffer before it realizes it is dealing with +- ** a corrupt record. Adding the two 0x00 bytes prevents this from causing ++ ** a corrupt record. Or it might try to read a large integer from the ++ ** buffer. In any case, eight 0x00 bytes prevents this from causing + ** a buffer overread. */ + pSample->n = sqlite3_column_bytes(pStmt, 4); +- pSample->p = sqlite3DbMallocZero(db, pSample->n + 2); ++ pSample->p = sqlite3DbMallocZero(db, pSample->n + 8); + if( pSample->p==0 ){ + sqlite3_finalize(pStmt); + return SQLITE_NOMEM_BKPT; +@@ -109388,11 +122002,15 @@ static int loadStatTbl( + */ + static int loadStat4(sqlite3 *db, const char *zDb){ + int rc = SQLITE_OK; /* Result codes from subroutines */ ++ const Table *pStat4; + + assert( db->lookaside.bDisable ); +- if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){ ++ if( OptimizationEnabled(db, SQLITE_Stat4) ++ && (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 ++ && IsOrdinaryTable(pStat4) ++ ){ + rc = loadStatTbl(db, +- "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", ++ "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase", + "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", + zDb + ); +@@ -109427,6 +122045,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ + char *zSql; + int rc = SQLITE_OK; + Schema *pSchema = db->aDb[iDb].pSchema; ++ const Table *pStat1; + + assert( iDb>=0 && iDbnDb ); + assert( db->aDb[iDb].pBt!=0 ); +@@ -109449,7 +122068,9 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ + /* Load new statistics out of the sqlite_stat1 table */ + sInfo.db = db; + sInfo.zDatabase = db->aDb[iDb].zDbSName; +- if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){ ++ if( (pStat1 = sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)) ++ && IsOrdinaryTable(pStat1) ++ ){ + zSql = sqlite3MPrintf(db, + "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); + if( zSql==0 ){ +@@ -109579,7 +122200,7 @@ static void attachFunc( + char *zErr = 0; + unsigned int flags; + Db *aNew; /* New array of Db pointers */ +- Db *pNew; /* Db object for the newly attached database */ ++ Db *pNew = 0; /* Db object for the newly attached database */ + char *zErrDyn = 0; + sqlite3_vfs *pVfs; + +@@ -109589,7 +122210,7 @@ static void attachFunc( + if( zFile==0 ) zFile = ""; + if( zName==0 ) zName = ""; + +-#ifdef SQLITE_ENABLE_DESERIALIZE ++#ifndef SQLITE_OMIT_DESERIALIZE + # define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb) + #else + # define REOPEN_AS_MEMDB(db) (0) +@@ -109599,13 +122220,26 @@ static void attachFunc( + /* This is not a real ATTACH. Instead, this routine is being called + ** from sqlite3_deserialize() to close database db->init.iDb and + ** reopen it as a MemDB */ ++ Btree *pNewBt = 0; + pVfs = sqlite3_vfs_find("memdb"); + if( pVfs==0 ) return; +- pNew = &db->aDb[db->init.iDb]; +- if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt); +- pNew->pBt = 0; +- pNew->pSchema = 0; +- rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); ++ rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB); ++ if( rc==SQLITE_OK ){ ++ Schema *pNewSchema = sqlite3SchemaGet(db, pNewBt); ++ if( pNewSchema ){ ++ /* Both the Btree and the new Schema were allocated successfully. ++ ** Close the old db and update the aDb[] slot with the new memdb ++ ** values. */ ++ pNew = &db->aDb[db->init.iDb]; ++ if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt); ++ pNew->pBt = pNewBt; ++ pNew->pSchema = pNewSchema; ++ }else{ ++ sqlite3BtreeClose(pNewBt); ++ rc = SQLITE_NOMEM; ++ } ++ } ++ if( rc ) goto attach_error; + }else{ + /* This is a real ATTACH + ** +@@ -109637,7 +122271,7 @@ static void attachFunc( + if( aNew==0 ) return; + memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); + }else{ +- aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); ++ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(1+(i64)db->nDb)); + if( aNew==0 ) return; + } + db->aDb = aNew; +@@ -109656,6 +122290,12 @@ static void attachFunc( + sqlite3_free(zErr); + return; + } ++ if( (db->flags & SQLITE_AttachWrite)==0 ){ ++ flags &= ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE); ++ flags |= SQLITE_OPEN_READONLY; ++ }else if( (db->flags & SQLITE_AttachCreate)==0 ){ ++ flags &= ~SQLITE_OPEN_CREATE; ++ } + assert( pVfs ); + flags |= SQLITE_OPEN_MAIN_DB; + rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); +@@ -109702,23 +122342,21 @@ static void attachFunc( + sqlite3BtreeEnterAll(db); + db->init.iDb = 0; + db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ if( db->setlkFlags & SQLITE_SETLK_BLOCK_ON_CONNECT ){ ++ int val = 1; ++ sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pNew->pBt)); ++ sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, &val); ++ } ++#endif + if( !REOPEN_AS_MEMDB(db) ){ + rc = sqlite3Init(db, &zErrDyn); + } + sqlite3BtreeLeaveAll(db); + assert( zErrDyn==0 || rc!=SQLITE_OK ); + } +-#ifdef SQLITE_USER_AUTHENTICATION +- if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){ +- u8 newAuth = 0; +- rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); +- if( newAuthauth.authLevel ){ +- rc = SQLITE_AUTH_USER; +- } +- } +-#endif + if( rc ){ +- if( !REOPEN_AS_MEMDB(db) ){ ++ if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){ + int iDb = db->nDb - 1; + assert( iDb>=2 ); + if( db->aDb[iDb].pBt ){ +@@ -109787,7 +122425,9 @@ static void detachFunc( + sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName); + goto detach_error; + } +- if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){ ++ if( sqlite3BtreeTxnState(pDb->pBt)!=SQLITE_TXN_NONE ++ || sqlite3BtreeIsInBackup(pDb->pBt) ++ ){ + sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); + goto detach_error; + } +@@ -109833,22 +122473,25 @@ static void codeAttach( + sqlite3* db = pParse->db; + int regArgs; + ++ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto attach_end; ++ + if( pParse->nErr ) goto attach_end; + memset(&sName, 0, sizeof(NameContext)); + sName.pParse = pParse; + + if( +- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || +- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || +- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) ++ SQLITE_OK!=resolveAttachExpr(&sName, pFilename) || ++ SQLITE_OK!=resolveAttachExpr(&sName, pDbname) || ++ SQLITE_OK!=resolveAttachExpr(&sName, pKey) + ){ + goto attach_end; + } + + #ifndef SQLITE_OMIT_AUTHORIZATION +- if( pAuthArg ){ ++ if( ALWAYS(pAuthArg) ){ + char *zAuthArg; + if( pAuthArg->op==TK_STRING ){ ++ assert( !ExprHasProperty(pAuthArg, EP_IntValue) ); + zAuthArg = pAuthArg->u.zToken; + }else{ + zAuthArg = 0; +@@ -109925,6 +122568,70 @@ SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *p + } + #endif /* SQLITE_OMIT_ATTACH */ + ++/* ++** Expression callback used by sqlite3FixAAAA() routines. ++*/ ++static int fixExprCb(Walker *p, Expr *pExpr){ ++ DbFixer *pFix = p->u.pFix; ++ if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL); ++ if( pExpr->op==TK_VARIABLE ){ ++ if( pFix->pParse->db->init.busy ){ ++ pExpr->op = TK_NULL; ++ }else{ ++ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType); ++ return WRC_Abort; ++ } ++ } ++ return WRC_Continue; ++} ++ ++/* ++** Select callback used by sqlite3FixAAAA() routines. ++*/ ++static int fixSelectCb(Walker *p, Select *pSelect){ ++ DbFixer *pFix = p->u.pFix; ++ int i; ++ SrcItem *pItem; ++ sqlite3 *db = pFix->pParse->db; ++ int iDb = sqlite3FindDbName(db, pFix->zDb); ++ SrcList *pList = pSelect->pSrc; ++ ++ if( NEVER(pList==0) ) return WRC_Continue; ++ for(i=0, pItem=pList->a; inSrc; i++, pItem++){ ++ if( pFix->bTemp==0 && pItem->fg.isSubquery==0 ){ ++ if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ ++ if( iDb!=sqlite3FindDbName(db, pItem->u4.zDatabase) ){ ++ sqlite3ErrorMsg(pFix->pParse, ++ "%s %T cannot reference objects in database %s", ++ pFix->zType, pFix->pName, pItem->u4.zDatabase); ++ return WRC_Abort; ++ } ++ sqlite3DbFree(db, pItem->u4.zDatabase); ++ pItem->fg.notCte = 1; ++ pItem->fg.hadSchema = 1; ++ } ++ pItem->u4.pSchema = pFix->pSchema; ++ pItem->fg.fromDDL = 1; ++ pItem->fg.fixedSchema = 1; ++ } ++#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) ++ if( pList->a[i].fg.isUsing==0 ++ && sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn) ++ ){ ++ return WRC_Abort; ++ } ++#endif ++ } ++ if( pSelect->pWith ){ ++ for(i=0; ipWith->nCte; i++){ ++ if( sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){ ++ return WRC_Abort; ++ } ++ } ++ } ++ return WRC_Continue; ++} ++ + /* + ** Initialize a DbFixer structure. This routine must be called prior + ** to passing the structure to one of the sqliteFixAAAA() routines below. +@@ -109936,9 +122643,7 @@ SQLITE_PRIVATE void sqlite3FixInit( + const char *zType, /* "view", "trigger", or "index" */ + const Token *pName /* Name of the view, trigger, or index */ + ){ +- sqlite3 *db; +- +- db = pParse->db; ++ sqlite3 *db = pParse->db; + assert( db->nDb>iDb ); + pFix->pParse = pParse; + pFix->zDb = db->aDb[iDb].zDbSName; +@@ -109946,6 +122651,13 @@ SQLITE_PRIVATE void sqlite3FixInit( + pFix->zType = zType; + pFix->pName = pName; + pFix->bTemp = (iDb==1); ++ pFix->w.pParse = pParse; ++ pFix->w.xExprCallback = fixExprCb; ++ pFix->w.xSelectCallback = fixSelectCb; ++ pFix->w.xSelectCallback2 = sqlite3WalkWinDefnDummyCallback; ++ pFix->w.walkerDepth = 0; ++ pFix->w.eCode = 0; ++ pFix->w.u.pFix = pFix; + } + + /* +@@ -109966,115 +122678,27 @@ SQLITE_PRIVATE int sqlite3FixSrcList( + DbFixer *pFix, /* Context of the fixation */ + SrcList *pList /* The Source list to check and modify */ + ){ +- int i; +- struct SrcList_item *pItem; +- sqlite3 *db = pFix->pParse->db; +- int iDb = sqlite3FindDbName(db, pFix->zDb); +- +- if( NEVER(pList==0) ) return 0; +- +- for(i=0, pItem=pList->a; inSrc; i++, pItem++){ +- if( pFix->bTemp==0 ){ +- if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){ +- sqlite3ErrorMsg(pFix->pParse, +- "%s %T cannot reference objects in database %s", +- pFix->zType, pFix->pName, pItem->zDatabase); +- return 1; +- } +- sqlite3DbFree(db, pItem->zDatabase); +- pItem->zDatabase = 0; +- pItem->pSchema = pFix->pSchema; +- pItem->fg.fromDDL = 1; +- } +-#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) +- if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1; +- if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1; +-#endif +- if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){ +- return 1; +- } ++ int res = 0; ++ if( pList ){ ++ Select s; ++ memset(&s, 0, sizeof(s)); ++ s.pSrc = pList; ++ res = sqlite3WalkSelect(&pFix->w, &s); + } +- return 0; ++ return res; + } + #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) + SQLITE_PRIVATE int sqlite3FixSelect( + DbFixer *pFix, /* Context of the fixation */ + Select *pSelect /* The SELECT statement to be fixed to one database */ + ){ +- while( pSelect ){ +- if( sqlite3FixExprList(pFix, pSelect->pEList) ){ +- return 1; +- } +- if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){ +- return 1; +- } +- if( sqlite3FixExpr(pFix, pSelect->pWhere) ){ +- return 1; +- } +- if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){ +- return 1; +- } +- if( sqlite3FixExpr(pFix, pSelect->pHaving) ){ +- return 1; +- } +- if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){ +- return 1; +- } +- if( sqlite3FixExpr(pFix, pSelect->pLimit) ){ +- return 1; +- } +- if( pSelect->pWith ){ +- int i; +- for(i=0; ipWith->nCte; i++){ +- if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){ +- return 1; +- } +- } +- } +- pSelect = pSelect->pPrior; +- } +- return 0; ++ return sqlite3WalkSelect(&pFix->w, pSelect); + } + SQLITE_PRIVATE int sqlite3FixExpr( + DbFixer *pFix, /* Context of the fixation */ + Expr *pExpr /* The expression to be fixed to one database */ + ){ +- while( pExpr ){ +- if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL); +- if( pExpr->op==TK_VARIABLE ){ +- if( pFix->pParse->db->init.busy ){ +- pExpr->op = TK_NULL; +- }else{ +- sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType); +- return 1; +- } +- } +- if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break; +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ +- if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1; +- }else{ +- if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1; +- } +- if( sqlite3FixExpr(pFix, pExpr->pRight) ){ +- return 1; +- } +- pExpr = pExpr->pLeft; +- } +- return 0; +-} +-SQLITE_PRIVATE int sqlite3FixExprList( +- DbFixer *pFix, /* Context of the fixation */ +- ExprList *pList /* The expression to be fixed to one database */ +-){ +- int i; +- struct ExprList_item *pItem; +- if( pList==0 ) return 0; +- for(i=0, pItem=pList->a; inExpr; i++, pItem++){ +- if( sqlite3FixExpr(pFix, pItem->pExpr) ){ +- return 1; +- } +- } +- return 0; ++ return sqlite3WalkExpr(&pFix->w, pExpr); + } + #endif + +@@ -110084,32 +122708,30 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep( + TriggerStep *pStep /* The trigger step be fixed to one database */ + ){ + while( pStep ){ +- if( sqlite3FixSelect(pFix, pStep->pSelect) ){ +- return 1; +- } +- if( sqlite3FixExpr(pFix, pStep->pWhere) ){ +- return 1; +- } +- if( sqlite3FixExprList(pFix, pStep->pExprList) ){ +- return 1; +- } +- if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){ ++ if( sqlite3WalkSelect(&pFix->w, pStep->pSelect) ++ || sqlite3WalkExpr(&pFix->w, pStep->pWhere) ++ || sqlite3WalkExprList(&pFix->w, pStep->pExprList) ++ || sqlite3FixSrcList(pFix, pStep->pFrom) ++ ){ + return 1; + } + #ifndef SQLITE_OMIT_UPSERT +- if( pStep->pUpsert ){ +- Upsert *pUp = pStep->pUpsert; +- if( sqlite3FixExprList(pFix, pUp->pUpsertTarget) +- || sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere) +- || sqlite3FixExprList(pFix, pUp->pUpsertSet) +- || sqlite3FixExpr(pFix, pUp->pUpsertWhere) +- ){ +- return 1; ++ { ++ Upsert *pUp; ++ for(pUp=pStep->pUpsert; pUp; pUp=pUp->pNextUpsert){ ++ if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget) ++ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere) ++ || sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet) ++ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere) ++ ){ ++ return 1; ++ } + } + } + #endif + pStep = pStep->pNext; + } ++ + return 0; + } + #endif +@@ -110230,11 +122852,7 @@ SQLITE_PRIVATE int sqlite3AuthReadCol( + int rc; /* Auth callback return code */ + + if( db->init.busy ) return SQLITE_OK; +- rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext +-#ifdef SQLITE_USER_AUTHENTICATION +- ,db->auth.zAuthUser +-#endif +- ); ++ rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext); + if( rc==SQLITE_DENY ){ + char *z = sqlite3_mprintf("%s.%s", zTab, zCol); + if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z); +@@ -110261,7 +122879,6 @@ SQLITE_PRIVATE void sqlite3AuthRead( + Schema *pSchema, /* The schema of the expression */ + SrcList *pTabList /* All table that pExpr might refer to */ + ){ +- sqlite3 *db = pParse->db; + Table *pTab = 0; /* The table being read */ + const char *zCol; /* Name of the column of the table */ + int iSrc; /* Index in pTabList->a[] of table being read */ +@@ -110269,8 +122886,8 @@ SQLITE_PRIVATE void sqlite3AuthRead( + int iCol; /* Index of column in table */ + + assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); +- assert( !IN_RENAME_OBJECT || db->xAuth==0 ); +- if( db->xAuth==0 ) return; ++ assert( !IN_RENAME_OBJECT ); ++ assert( pParse->db->xAuth!=0 ); + iDb = sqlite3SchemaToIndex(pParse->db, pSchema); + if( iDb<0 ){ + /* An attempt to read a column out of a subquery or other +@@ -110282,26 +122899,26 @@ SQLITE_PRIVATE void sqlite3AuthRead( + pTab = pParse->pTriggerTab; + }else{ + assert( pTabList ); +- for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ ++ for(iSrc=0; iSrcnSrc; iSrc++){ + if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ +- pTab = pTabList->a[iSrc].pTab; ++ pTab = pTabList->a[iSrc].pSTab; + break; + } + } + } + iCol = pExpr->iColumn; +- if( NEVER(pTab==0) ) return; ++ if( pTab==0 ) return; + + if( iCol>=0 ){ + assert( iColnCol ); +- zCol = pTab->aCol[iCol].zName; ++ zCol = pTab->aCol[iCol].zCnName; + }else if( pTab->iPKey>=0 ){ + assert( pTab->iPKeynCol ); +- zCol = pTab->aCol[pTab->iPKey].zName; ++ zCol = pTab->aCol[pTab->iPKey].zCnName; + }else{ + zCol = "ROWID"; + } +- assert( iDb>=0 && iDbnDb ); ++ assert( iDb>=0 && iDbdb->nDb ); + if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ + pExpr->op = TK_NULL; + } +@@ -110323,15 +122940,11 @@ SQLITE_PRIVATE int sqlite3AuthCheck( + sqlite3 *db = pParse->db; + int rc; + +- /* Don't do any authorization checks if the database is initialising ++ /* Don't do any authorization checks if the database is initializing + ** or if the parser is being invoked from within sqlite3_declare_vtab. + */ + assert( !IN_RENAME_OBJECT || db->xAuth==0 ); +- if( db->init.busy || IN_SPECIAL_PARSE ){ +- return SQLITE_OK; +- } +- +- if( db->xAuth==0 ){ ++ if( db->xAuth==0 || db->init.busy || IN_SPECIAL_PARSE ){ + return SQLITE_OK; + } + +@@ -110346,11 +122959,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck( + testcase( zArg3==0 ); + testcase( pParse->zAuthContext==0 ); + +- rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext +-#ifdef SQLITE_USER_AUTHENTICATION +- ,db->auth.zAuthUser +-#endif +- ); ++ rc = db->xAuth(db->pAuthArg,code,zArg1,zArg2,zArg3,pParse->zAuthContext); + if( rc==SQLITE_DENY ){ + sqlite3ErrorMsg(pParse, "not authorized"); + pParse->rc = SQLITE_AUTH; +@@ -110440,21 +123049,20 @@ struct TableLock { + ** code to make the lock occur is generated by a later call to + ** codeTableLocks() which occurs during sqlite3FinishCoding(). + */ +-SQLITE_PRIVATE void sqlite3TableLock( ++static SQLITE_NOINLINE void lockTable( + Parse *pParse, /* Parsing context */ + int iDb, /* Index of the database containing the table to lock */ + Pgno iTab, /* Root page number of the table to be locked */ + u8 isWriteLock, /* True for a write lock */ + const char *zName /* Name of the table to be locked */ + ){ +- Parse *pToplevel = sqlite3ParseToplevel(pParse); ++ Parse *pToplevel; + int i; + int nBytes; + TableLock *p; + assert( iDb>=0 ); + +- if( iDb==1 ) return; +- if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; ++ pToplevel = sqlite3ParseToplevel(pParse); + for(i=0; inTableLock; i++){ + p = &pToplevel->aTableLock[i]; + if( p->iDb==iDb && p->iTab==iTab ){ +@@ -110463,6 +123071,7 @@ SQLITE_PRIVATE void sqlite3TableLock( + } + } + ++ assert( pToplevel->nTableLock < 0x7fff0000 ); + nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); + pToplevel->aTableLock = + sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); +@@ -110477,6 +123086,17 @@ SQLITE_PRIVATE void sqlite3TableLock( + sqlite3OomFault(pToplevel->db); + } + } ++SQLITE_PRIVATE void sqlite3TableLock( ++ Parse *pParse, /* Parsing context */ ++ int iDb, /* Index of the database containing the table to lock */ ++ Pgno iTab, /* Root page number of the table to be locked */ ++ u8 isWriteLock, /* True for a write lock */ ++ const char *zName /* Name of the table to be locked */ ++){ ++ if( iDb==1 ) return; ++ if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; ++ lockTable(pParse, iDb, iTab, isWriteLock, zName); ++} + + /* + ** Code an OP_TableLock instruction for each table locked by the +@@ -110484,10 +123104,8 @@ SQLITE_PRIVATE void sqlite3TableLock( + */ + static void codeTableLocks(Parse *pParse){ + int i; +- Vdbe *pVdbe; +- +- pVdbe = sqlite3GetVdbe(pParse); +- assert( pVdbe!=0 ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */ ++ Vdbe *pVdbe = pParse->pVdbe; ++ assert( pVdbe!=0 ); + + for(i=0; inTableLock; i++){ + TableLock *p = &pParse->aTableLock[i]; +@@ -110526,34 +123144,56 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask m){ + SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ + sqlite3 *db; + Vdbe *v; ++ int iDb, i; + + assert( pParse->pToplevel==0 ); + db = pParse->db; ++ assert( db->pParse==pParse ); + if( pParse->nested ) return; +- if( db->mallocFailed || pParse->nErr ){ +- if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR; ++ if( pParse->nErr ){ ++ if( db->mallocFailed ) pParse->rc = SQLITE_NOMEM; + return; + } ++ assert( db->mallocFailed==0 ); + + /* Begin by generating some termination code at the end of the + ** vdbe program + */ +- v = sqlite3GetVdbe(pParse); ++ v = pParse->pVdbe; ++ if( v==0 ){ ++ if( db->init.busy ){ ++ pParse->rc = SQLITE_DONE; ++ return; ++ } ++ v = sqlite3GetVdbe(pParse); ++ if( v==0 ) pParse->rc = SQLITE_ERROR; ++ } + assert( !pParse->isMultiWrite + || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); + if( v ){ +- sqlite3VdbeAddOp0(v, OP_Halt); +- +-#if SQLITE_USER_AUTHENTICATION +- if( pParse->nTableLock>0 && db->init.busy==0 ){ +- sqlite3UserAuthInit(db); +- if( db->auth.authLevelrc = SQLITE_AUTH_USER; +- return; ++ if( pParse->bReturning ){ ++ Returning *pReturning; ++ int addrRewind; ++ int reg; ++ ++ assert( !pParse->isCreate ); ++ pReturning = pParse->u1.d.pReturning; ++ if( pReturning->nRetCol ){ ++ sqlite3VdbeAddOp0(v, OP_FkCheck); ++ addrRewind = ++ sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur); ++ VdbeCoverage(v); ++ reg = pReturning->iRetReg; ++ for(i=0; inRetCol; i++){ ++ sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i); ++ } ++ sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i); ++ sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1); ++ VdbeCoverage(v); ++ sqlite3VdbeJumpHere(v, addrRewind); + } + } +-#endif ++ sqlite3VdbeAddOp0(v, OP_Halt); + + /* The cookie mask contains one bit for each database file open. + ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are +@@ -110561,73 +123201,75 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ + ** transaction on each used database and to verify the schema cookie + ** on each used database. + */ +- if( db->mallocFailed==0 +- && (DbMaskNonZero(pParse->cookieMask) || pParse->pConstExpr) +- ){ +- int iDb, i; +- assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); +- sqlite3VdbeJumpHere(v, 0); +- for(iDb=0; iDbnDb; iDb++){ +- Schema *pSchema; +- if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue; +- sqlite3VdbeUsesBtree(v, iDb); +- pSchema = db->aDb[iDb].pSchema; +- sqlite3VdbeAddOp4Int(v, +- OP_Transaction, /* Opcode */ +- iDb, /* P1 */ +- DbMaskTest(pParse->writeMask,iDb), /* P2 */ +- pSchema->schema_cookie, /* P3 */ +- pSchema->iGeneration /* P4 */ +- ); +- if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1); +- VdbeComment((v, +- "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); +- } ++ assert( pParse->nErr>0 || sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); ++ sqlite3VdbeJumpHere(v, 0); ++ assert( db->nDb>0 ); ++ iDb = 0; ++ do{ ++ Schema *pSchema; ++ if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue; ++ sqlite3VdbeUsesBtree(v, iDb); ++ pSchema = db->aDb[iDb].pSchema; ++ sqlite3VdbeAddOp4Int(v, ++ OP_Transaction, /* Opcode */ ++ iDb, /* P1 */ ++ DbMaskTest(pParse->writeMask,iDb), /* P2 */ ++ pSchema->schema_cookie, /* P3 */ ++ pSchema->iGeneration /* P4 */ ++ ); ++ if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1); ++ VdbeComment((v, ++ "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); ++ }while( ++iDbnDb ); + #ifndef SQLITE_OMIT_VIRTUALTABLE +- for(i=0; inVtabLock; i++){ +- char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); +- sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); +- } +- pParse->nVtabLock = 0; ++ for(i=0; inVtabLock; i++){ ++ char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); ++ sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); ++ } ++ pParse->nVtabLock = 0; + #endif + +- /* Once all the cookies have been verified and transactions opened, +- ** obtain the required table-locks. This is a no-op unless the +- ** shared-cache feature is enabled. +- */ +- codeTableLocks(pParse); ++#ifndef SQLITE_OMIT_SHARED_CACHE ++ /* Once all the cookies have been verified and transactions opened, ++ ** obtain the required table-locks. This is a no-op unless the ++ ** shared-cache feature is enabled. ++ */ ++ if( pParse->nTableLock ) codeTableLocks(pParse); ++#endif + +- /* Initialize any AUTOINCREMENT data structures required. +- */ +- sqlite3AutoincrementBegin(pParse); ++ /* Initialize any AUTOINCREMENT data structures required. ++ */ ++ if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse); + +- /* Code constant expressions that where factored out of inner loops. +- ** +- ** The pConstExpr list might also contain expressions that we simply +- ** want to keep around until the Parse object is deleted. Such +- ** expressions have iConstExprReg==0. Do not generate code for +- ** those expressions, of course. +- */ +- if( pParse->pConstExpr ){ +- ExprList *pEL = pParse->pConstExpr; +- pParse->okConstFactor = 0; +- for(i=0; inExpr; i++){ +- int iReg = pEL->a[i].u.iConstExprReg; +- if( iReg>0 ){ +- sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg); +- } +- } ++ /* Code constant expressions that were factored out of inner loops. ++ */ ++ if( pParse->pConstExpr ){ ++ ExprList *pEL = pParse->pConstExpr; ++ pParse->okConstFactor = 0; ++ for(i=0; inExpr; i++){ ++ assert( pEL->a[i].u.iConstExprReg>0 ); ++ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg); + } ++ } + +- /* Finally, jump back to the beginning of the executable code. */ +- sqlite3VdbeGoto(v, 1); ++ if( pParse->bReturning ){ ++ Returning *pRet; ++ assert( !pParse->isCreate ); ++ pRet = pParse->u1.d.pReturning; ++ if( pRet->nRetCol ){ ++ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol); ++ } + } +- } + ++ /* Finally, jump back to the beginning of the executable code. */ ++ sqlite3VdbeGoto(v, 1); ++ } + + /* Get the VDBE program ready for execution + */ +- if( v && pParse->nErr==0 && !db->mallocFailed ){ ++ assert( v!=0 || pParse->nErr ); ++ assert( db->mallocFailed==0 || pParse->nErr ); ++ if( pParse->nErr==0 ){ + /* A minimum of one cursor is required if autoincrement is used + * See ticket [a696379c1f08866] */ + assert( pParse->pAinc==0 || pParse->nTab>0 ); +@@ -110641,23 +123283,25 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ + /* + ** Run the parser and code generator recursively in order to generate + ** code for the SQL statement given onto the end of the pParse context +-** currently under construction. When the parser is run recursively +-** this way, the final OP_Halt is not appended and other initialization +-** and finalization steps are omitted because those are handling by the +-** outermost parser. ++** currently under construction. Notes: + ** +-** Not everything is nestable. This facility is designed to permit +-** INSERT, UPDATE, and DELETE operations against the schema table. Use +-** care if you decide to try to use this routine for some other purposes. ++** * The final OP_Halt is not appended and other initialization ++** and finalization steps are omitted because those are handling by the ++** outermost parser. ++** ++** * Built-in SQL functions always take precedence over application-defined ++** SQL functions. In other words, it is not possible to override a ++** built-in function. + */ + SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ + va_list ap; + char *zSql; +- char *zErrMsg = 0; + sqlite3 *db = pParse->db; ++ u32 savedDbFlags = db->mDbFlags; + char saveBuf[PARSE_TAIL_SZ]; + + if( pParse->nErr ) return; ++ if( pParse->eParseMode ) return; + assert( pParse->nested<10 ); /* Nesting should only be of limited depth */ + va_start(ap, zFormat); + zSql = sqlite3VMPrintf(db, zFormat, ap); +@@ -110673,23 +123317,14 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ + pParse->nested++; + memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ); + memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); +- sqlite3RunParser(pParse, zSql, &zErrMsg); +- sqlite3DbFree(db, zErrMsg); ++ db->mDbFlags |= DBFLAG_PreferBuiltin; ++ sqlite3RunParser(pParse, zSql); ++ db->mDbFlags = savedDbFlags; + sqlite3DbFree(db, zSql); + memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); + pParse->nested--; + } + +-#if SQLITE_USER_AUTHENTICATION +-/* +-** Return TRUE if zTable is the name of the system table that stores the +-** list of users and their access credentials. +-*/ +-SQLITE_PRIVATE int sqlite3UserAuthTable(const char *zTable){ +- return sqlite3_stricmp(zTable, "sqlite_user")==0; +-} +-#endif +- + /* + ** Locate the in-memory structure that describes a particular database + ** table given the name of that table and (optionally) the name of the +@@ -110708,13 +123343,6 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha + + /* All mutexes are required for schema access. Make sure we hold them. */ + assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); +-#if SQLITE_USER_AUTHENTICATION +- /* Only the admin user is allowed to know that the sqlite_user table +- ** exists */ +- if( db->auth.authLevelnDb; i++){ + if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break; +@@ -110731,17 +123359,17 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha + p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); + if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ + if( i==1 ){ +- if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 +- || sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 +- || sqlite3StrICmp(zName+7, &DFLT_SCHEMA_TABLE[7])==0 ++ if( sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ++ || sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ++ || sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 + ){ + p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, +- DFLT_TEMP_SCHEMA_TABLE); ++ LEGACY_TEMP_SCHEMA_TABLE); + } + }else{ +- if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ ++ if( sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ + p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, +- DFLT_SCHEMA_TABLE); ++ LEGACY_SCHEMA_TABLE); + } + } + } +@@ -110759,11 +123387,11 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha + if( p ) break; + } + if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ +- if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ +- p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, DFLT_SCHEMA_TABLE); +- }else if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 ){ ++ if( sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ ++ p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, LEGACY_SCHEMA_TABLE); ++ }else if( sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){ + p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, +- DFLT_TEMP_SCHEMA_TABLE); ++ LEGACY_TEMP_SCHEMA_TABLE); + } + } + } +@@ -110803,19 +123431,20 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( + /* If zName is the not the name of a table in the schema created using + ** CREATE, then check to see if it is the name of an virtual table that + ** can be an eponymous virtual table. */ +- if( pParse->disableVtab==0 ){ ++ if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){ + Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); + if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ + pMod = sqlite3PragmaVtabRegister(db, zName); + } + if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ ++ testcase( pMod->pEpoTab==0 ); + return pMod->pEpoTab; + } + } + #endif + if( flags & LOCATE_NOERR ) return 0; + pParse->checkSchema = 1; +- }else if( IsVirtual(p) && pParse->disableVtab ){ ++ }else if( IsVirtual(p) && (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)!=0 ){ + p = 0; + } + +@@ -110826,6 +123455,8 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( + }else{ + sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); + } ++ }else{ ++ assert( HasRowid(p) || p->iPKey<0 ); + } + + return p; +@@ -110843,19 +123474,35 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( + SQLITE_PRIVATE Table *sqlite3LocateTableItem( + Parse *pParse, + u32 flags, +- struct SrcList_item *p ++ SrcItem *p + ){ + const char *zDb; +- assert( p->pSchema==0 || p->zDatabase==0 ); +- if( p->pSchema ){ +- int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); ++ if( p->fg.fixedSchema ){ ++ int iDb = sqlite3SchemaToIndex(pParse->db, p->u4.pSchema); + zDb = pParse->db->aDb[iDb].zDbSName; + }else{ +- zDb = p->zDatabase; ++ assert( !p->fg.isSubquery ); ++ zDb = p->u4.zDatabase; + } + return sqlite3LocateTable(pParse, flags, p->zName, zDb); + } + ++/* ++** Return the preferred table name for system tables. Translate legacy ++** names into the new preferred names, as appropriate. ++*/ ++SQLITE_PRIVATE const char *sqlite3PreferredTableName(const char *zName){ ++ if( sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ ++ if( sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 ){ ++ return PREFERRED_SCHEMA_TABLE; ++ } ++ if( sqlite3StrICmp(zName+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){ ++ return PREFERRED_TEMP_SCHEMA_TABLE; ++ } ++ } ++ return zName; ++} ++ + /* + ** Locate the in-memory structure that describes + ** a particular index given the name of that index +@@ -111020,6 +123667,84 @@ SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){ + db->mDbFlags &= ~DBFLAG_SchemaChange; + } + ++/* ++** Set the expression associated with a column. This is usually ++** the DEFAULT value, but might also be the expression that computes ++** the value for a generated column. ++*/ ++SQLITE_PRIVATE void sqlite3ColumnSetExpr( ++ Parse *pParse, /* Parsing context */ ++ Table *pTab, /* The table containing the column */ ++ Column *pCol, /* The column to receive the new DEFAULT expression */ ++ Expr *pExpr /* The new default expression */ ++){ ++ ExprList *pList; ++ assert( IsOrdinaryTable(pTab) ); ++ pList = pTab->u.tab.pDfltList; ++ if( pCol->iDflt==0 ++ || NEVER(pList==0) ++ || NEVER(pList->nExpriDflt) ++ ){ ++ pCol->iDflt = pList==0 ? 1 : pList->nExpr+1; ++ pTab->u.tab.pDfltList = sqlite3ExprListAppend(pParse, pList, pExpr); ++ }else{ ++ sqlite3ExprDelete(pParse->db, pList->a[pCol->iDflt-1].pExpr); ++ pList->a[pCol->iDflt-1].pExpr = pExpr; ++ } ++} ++ ++/* ++** Return the expression associated with a column. The expression might be ++** the DEFAULT clause or the AS clause of a generated column. ++** Return NULL if the column has no associated expression. ++*/ ++SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){ ++ if( pCol->iDflt==0 ) return 0; ++ if( !IsOrdinaryTable(pTab) ) return 0; ++ if( NEVER(pTab->u.tab.pDfltList==0) ) return 0; ++ if( NEVER(pTab->u.tab.pDfltList->nExpriDflt) ) return 0; ++ return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; ++} ++ ++/* ++** Set the collating sequence name for a column. ++*/ ++SQLITE_PRIVATE void sqlite3ColumnSetColl( ++ sqlite3 *db, ++ Column *pCol, ++ const char *zColl ++){ ++ i64 nColl; ++ i64 n; ++ char *zNew; ++ assert( zColl!=0 ); ++ n = sqlite3Strlen30(pCol->zCnName) + 1; ++ if( pCol->colFlags & COLFLAG_HASTYPE ){ ++ n += sqlite3Strlen30(pCol->zCnName+n) + 1; ++ } ++ nColl = sqlite3Strlen30(zColl) + 1; ++ zNew = sqlite3DbRealloc(db, pCol->zCnName, nColl+n); ++ if( zNew ){ ++ pCol->zCnName = zNew; ++ memcpy(pCol->zCnName + n, zColl, nColl); ++ pCol->colFlags |= COLFLAG_HASCOLL; ++ } ++} ++ ++/* ++** Return the collating sequence name for a column ++*/ ++SQLITE_PRIVATE const char *sqlite3ColumnColl(Column *pCol){ ++ const char *z; ++ if( (pCol->colFlags & COLFLAG_HASCOLL)==0 ) return 0; ++ z = pCol->zCnName; ++ while( *z ){ z++; } ++ if( pCol->colFlags & COLFLAG_HASTYPE ){ ++ do{ z++; }while( *z ); ++ } ++ return z+1; ++} ++ + /* + ** Delete memory allocated for the column names of a table or view (the + ** Table.aCol[] array). +@@ -111028,14 +123753,23 @@ SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){ + int i; + Column *pCol; + assert( pTable!=0 ); ++ assert( db!=0 ); + if( (pCol = pTable->aCol)!=0 ){ + for(i=0; inCol; i++, pCol++){ +- assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) ); +- sqlite3DbFree(db, pCol->zName); +- sqlite3ExprDelete(db, pCol->pDflt); +- sqlite3DbFree(db, pCol->zColl); ++ assert( pCol->zCnName==0 || pCol->hName==sqlite3StrIHash(pCol->zCnName) ); ++ sqlite3DbFree(db, pCol->zCnName); ++ } ++ sqlite3DbNNFreeNN(db, pTable->aCol); ++ if( IsOrdinaryTable(pTable) ){ ++ sqlite3ExprListDelete(db, pTable->u.tab.pDfltList); ++ } ++ if( db->pnBytesFreed==0 ){ ++ pTable->aCol = 0; ++ pTable->nCol = 0; ++ if( IsOrdinaryTable(pTable) ){ ++ pTable->u.tab.pDfltList = 0; ++ } + } +- sqlite3DbFree(db, pTable->aCol); + } + } + +@@ -111066,7 +123800,8 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ + ** a Table object that was going to be marked ephemeral. So do not check + ** that no lookaside memory is used in this case either. */ + int nLookaside = 0; +- if( db && !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){ ++ assert( db!=0 ); ++ if( !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){ + nLookaside = sqlite3LookasideUsed(db, 0); + } + #endif +@@ -111076,7 +123811,7 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ + pNext = pIndex->pNext; + assert( pIndex->pSchema==pTable->pSchema + || (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) ); +- if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){ ++ if( db->pnBytesFreed==0 && !IsVirtual(pTable) ){ + char *zName = pIndex->zName; + TESTONLY ( Index *pOld = ) sqlite3HashInsert( + &pIndex->pSchema->idxHash, zName, 0 +@@ -111087,19 +123822,25 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ + sqlite3FreeIndex(db, pIndex); + } + +- /* Delete any foreign keys attached to this table. */ +- sqlite3FkDelete(db, pTable); ++ if( IsOrdinaryTable(pTable) ){ ++ sqlite3FkDelete(db, pTable); ++ } ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ else if( IsVirtual(pTable) ){ ++ sqlite3VtabClear(db, pTable); ++ } ++#endif ++ else{ ++ assert( IsView(pTable) ); ++ sqlite3SelectDelete(db, pTable->u.view.pSelect); ++ } + + /* Delete the Table structure itself. + */ + sqlite3DeleteColumnNames(db, pTable); + sqlite3DbFree(db, pTable->zName); + sqlite3DbFree(db, pTable->zColAff); +- sqlite3SelectDelete(db, pTable->pSelect); + sqlite3ExprListDelete(db, pTable->pCheck); +-#ifndef SQLITE_OMIT_VIRTUALTABLE +- sqlite3VtabClear(db, pTable); +-#endif + sqlite3DbFree(db, pTable); + + /* Verify that no lookaside memory was used by schema tables */ +@@ -111107,10 +123848,14 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ + } + SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ + /* Do not delete the table until the reference count reaches zero. */ ++ assert( db!=0 ); + if( !pTable ) return; +- if( ((!db || db->pnBytesFreed==0) && (--pTable->nTabRef)>0) ) return; ++ if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return; + deleteTable(db, pTable); + } ++SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){ ++ sqlite3DeleteTable(db, (Table*)pTable); ++} + + + /* +@@ -111145,10 +123890,10 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char + ** are not \000 terminated and are not persistent. The returned string + ** is \000 terminated and is persistent. + */ +-SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ ++SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, const Token *pName){ + char *zName; + if( pName ){ +- zName = sqlite3DbStrNDup(db, (char*)pName->z, pName->n); ++ zName = sqlite3DbStrNDup(db, (const char*)pName->z, pName->n); + sqlite3Dequote(zName); + }else{ + zName = 0; +@@ -111162,7 +123907,7 @@ SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ + */ + SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *p, int iDb){ + Vdbe *v = sqlite3GetVdbe(p); +- sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, DFLT_SCHEMA_TABLE); ++ sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, LEGACY_SCHEMA_TABLE); + sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, SCHEMA_ROOT, iDb, 5); + if( p->nTab==0 ){ + p->nTab = 1; +@@ -111242,7 +123987,7 @@ SQLITE_PRIVATE int sqlite3TwoPartName( + return -1; + } + }else{ +- assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT ++ assert( db->init.iDb==0 || db->init.busy || IN_SPECIAL_PARSE + || (db->mDbFlags & DBFLAG_Vacuum)!=0); + iDb = db->init.iDb; + *pUnqual = pName1; +@@ -111325,10 +124070,16 @@ SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table *pTab){ + ** find the (first) offset of that column in index pIdx. Or return -1 + ** if column iCol is not used in index pIdx. + */ +-SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){ ++SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){ + int i; ++ i16 iCol16; ++ assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN ); ++ assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 ); ++ iCol16 = iCol; + for(i=0; inColumn; i++){ +- if( iCol==pIdx->aiColumn[i] ) return i; ++ if( iCol16==pIdx->aiColumn[i] ){ ++ return i; ++ } + } + return -1; + } +@@ -111411,6 +124162,23 @@ SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table *pTab, i16 iCol){ + } + #endif + ++/* ++** Insert a single OP_JournalMode query opcode in order to force the ++** prepared statement to return false for sqlite3_stmt_readonly(). This ++** is used by CREATE TABLE IF NOT EXISTS and similar if the table already ++** exists, so that the prepared statement for CREATE TABLE IF NOT EXISTS ++** will return false for sqlite3_stmt_readonly() even if that statement ++** is a read-only no-op. ++*/ ++static void sqlite3ForceNotReadOnly(Parse *pParse){ ++ int iReg = ++pParse->nMem; ++ Vdbe *v = sqlite3GetVdbe(pParse); ++ if( v ){ ++ sqlite3VdbeAddOp3(v, OP_JournalMode, 0, iReg, PAGER_JOURNALMODE_QUERY); ++ sqlite3VdbeUsesBtree(v, 0); ++ } ++} ++ + /* + ** Begin constructing a new table representation in memory. This is + ** the first of several action routines that get called in response +@@ -111506,10 +124274,12 @@ SQLITE_PRIVATE void sqlite3StartTable( + pTable = sqlite3FindTable(db, zName, zDb); + if( pTable ){ + if( !noErr ){ +- sqlite3ErrorMsg(pParse, "table %T already exists", pName); ++ sqlite3ErrorMsg(pParse, "%s %T already exists", ++ (IsView(pTable)? "view" : "table"), pName); + }else{ + assert( !db->init.busy || CORRUPT_DB ); + sqlite3CodeVerifySchema(pParse, iDb); ++ sqlite3ForceNotReadOnly(pParse); + } + goto begin_table_error; + } +@@ -111538,17 +124308,6 @@ SQLITE_PRIVATE void sqlite3StartTable( + assert( pParse->pNewTable==0 ); + pParse->pNewTable = pTable; + +- /* If this is the magic sqlite_sequence table used by autoincrement, +- ** then record a pointer to this table in the main database structure +- ** so that INSERT can find the table easily. +- */ +-#ifndef SQLITE_OMIT_AUTOINCREMENT +- if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ +- assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); +- pTable->pSchema->pSeqTab = pTable; +- } +-#endif +- + /* Begin generating the code that will insert the table record into + ** the schema table. Note in particular that we must go ahead + ** and allocate the record number for the table entry now. Before any +@@ -111574,8 +124333,9 @@ SQLITE_PRIVATE void sqlite3StartTable( + /* If the file format and encoding in the database have not been set, + ** set them now. + */ +- reg1 = pParse->regRowid = ++pParse->nMem; +- reg2 = pParse->regRoot = ++pParse->nMem; ++ assert( pParse->isCreate ); ++ reg1 = pParse->u1.cr.regRowid = ++pParse->nMem; ++ reg2 = pParse->u1.cr.regRoot = ++pParse->nMem; + reg3 = ++pParse->nMem; + sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT); + sqlite3VdbeUsesBtree(v, iDb); +@@ -111590,8 +124350,8 @@ SQLITE_PRIVATE void sqlite3StartTable( + ** The record created does not contain anything yet. It will be replaced + ** by the real entry in code generated at sqlite3EndTable(). + ** +- ** The rowid for the new entry is left in register pParse->regRowid. +- ** The root page number of the new table is left in reg pParse->regRoot. ++ ** The rowid for the new entry is left in register pParse->u1.cr.regRowid. ++ ** The root page of the new table is left in reg pParse->u1.cr.regRoot. + ** The rowid and root page number values are needed by the code that + ** sqlite3EndTable will generate. + */ +@@ -111601,7 +124361,8 @@ SQLITE_PRIVATE void sqlite3StartTable( + }else + #endif + { +- pParse->addrCrTab = ++ assert( !pParse->bReturning ); ++ pParse->u1.cr.addrCrTab = + sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY); + } + sqlite3OpenSchemaTable(pParse, iDb); +@@ -111617,6 +124378,7 @@ SQLITE_PRIVATE void sqlite3StartTable( + + /* If an error occurs, we jump here */ + begin_table_error: ++ pParse->checkSchema = 1; + sqlite3DbFree(db, zName); + return; + } +@@ -111626,14 +124388,85 @@ begin_table_error: + */ + #if SQLITE_ENABLE_HIDDEN_COLUMNS + SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ +- if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){ ++ if( sqlite3_strnicmp(pCol->zCnName, "__hidden__", 10)==0 ){ + pCol->colFlags |= COLFLAG_HIDDEN; ++ if( pTab ) pTab->tabFlags |= TF_HasHidden; + }else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){ + pTab->tabFlags |= TF_OOOHidden; + } + } + #endif + ++/* ++** Clean up the data structures associated with the RETURNING clause. ++*/ ++static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){ ++ Returning *pRet = (Returning*)pArg; ++ Hash *pHash; ++ pHash = &(db->aDb[1].pSchema->trigHash); ++ sqlite3HashInsert(pHash, pRet->zName, 0); ++ sqlite3ExprListDelete(db, pRet->pReturnEL); ++ sqlite3DbFree(db, pRet); ++} ++ ++/* ++** Add the RETURNING clause to the parse currently underway. ++** ++** This routine creates a special TEMP trigger that will fire for each row ++** of the DML statement. That TEMP trigger contains a single SELECT ++** statement with a result set that is the argument of the RETURNING clause. ++** The trigger has the Trigger.bReturning flag and an opcode of ++** TK_RETURNING instead of TK_SELECT, so that the trigger code generator ++** knows to handle it specially. The TEMP trigger is automatically ++** removed at the end of the parse. ++** ++** When this routine is called, we do not yet know if the RETURNING clause ++** is attached to a DELETE, INSERT, or UPDATE, so construct it as a ++** RETURNING trigger instead. It will then be converted into the appropriate ++** type on the first call to sqlite3TriggersExist(). ++*/ ++SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){ ++ Returning *pRet; ++ Hash *pHash; ++ sqlite3 *db = pParse->db; ++ if( pParse->pNewTrigger ){ ++ sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger"); ++ }else{ ++ assert( pParse->bReturning==0 || pParse->ifNotExists ); ++ } ++ pParse->bReturning = 1; ++ pRet = sqlite3DbMallocZero(db, sizeof(*pRet)); ++ if( pRet==0 ){ ++ sqlite3ExprListDelete(db, pList); ++ return; ++ } ++ assert( !pParse->isCreate ); ++ pParse->u1.d.pReturning = pRet; ++ pRet->pParse = pParse; ++ pRet->pReturnEL = pList; ++ sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet); ++ testcase( pParse->earlyCleanup ); ++ if( db->mallocFailed ) return; ++ sqlite3_snprintf(sizeof(pRet->zName), pRet->zName, ++ "sqlite_returning_%p", pParse); ++ pRet->retTrig.zName = pRet->zName; ++ pRet->retTrig.op = TK_RETURNING; ++ pRet->retTrig.tr_tm = TRIGGER_AFTER; ++ pRet->retTrig.bReturning = 1; ++ pRet->retTrig.pSchema = db->aDb[1].pSchema; ++ pRet->retTrig.pTabSchema = db->aDb[1].pSchema; ++ pRet->retTrig.step_list = &pRet->retTStep; ++ pRet->retTStep.op = TK_RETURNING; ++ pRet->retTStep.pTrig = &pRet->retTrig; ++ pRet->retTStep.pExprList = pList; ++ pHash = &(db->aDb[1].pSchema->trigHash); ++ assert( sqlite3HashFind(pHash, pRet->zName)==0 ++ || pParse->nErr || pParse->ifNotExists ); ++ if( sqlite3HashInsert(pHash, pRet->zName, &pRet->retTrig) ++ ==&pRet->retTrig ){ ++ sqlite3OomFault(db); ++ } ++} + + /* + ** Add a new column to the table currently being constructed. +@@ -111643,67 +124476,112 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ + ** first to get things going. Then this routine is called for each + ** column. + */ +-SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ ++SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ + Table *p; + int i; + char *z; + char *zType; + Column *pCol; + sqlite3 *db = pParse->db; ++ Column *aNew; ++ u8 eType = COLTYPE_CUSTOM; ++ u8 szEst = 1; ++ char affinity = SQLITE_AFF_BLOB; ++ + if( (p = pParse->pNewTable)==0 ) return; + if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){ + sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName); + return; + } +- z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2); ++ if( !IN_RENAME_OBJECT ) sqlite3DequoteToken(&sName); ++ ++ /* Because keywords GENERATE ALWAYS can be converted into identifiers ++ ** by the parser, we can sometimes end up with a typename that ends ++ ** with "generated always". Check for this case and omit the surplus ++ ** text. */ ++ if( sType.n>=16 ++ && sqlite3_strnicmp(sType.z+(sType.n-6),"always",6)==0 ++ ){ ++ sType.n -= 6; ++ while( ALWAYS(sType.n>0) && sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; ++ if( sType.n>=9 ++ && sqlite3_strnicmp(sType.z+(sType.n-9),"generated",9)==0 ++ ){ ++ sType.n -= 9; ++ while( sType.n>0 && sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; ++ } ++ } ++ ++ /* Check for standard typenames. For standard typenames we will ++ ** set the Column.eType field rather than storing the typename after ++ ** the column name, in order to save space. */ ++ if( sType.n>=3 ){ ++ sqlite3DequoteToken(&sType); ++ for(i=0; i0) ); + if( z==0 ) return; +- if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName); +- memcpy(z, pName->z, pName->n); +- z[pName->n] = 0; ++ if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, &sName); ++ memcpy(z, sName.z, sName.n); ++ z[sName.n] = 0; + sqlite3Dequote(z); +- for(i=0; inCol; i++){ +- if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){ +- sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); +- sqlite3DbFree(db, z); +- return; +- } ++ if( p->nCol && sqlite3ColumnIndex(p, z)>=0 ){ ++ sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); ++ sqlite3DbFree(db, z); ++ return; + } +- if( (p->nCol & 0x7)==0 ){ +- Column *aNew; +- aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0])); +- if( aNew==0 ){ +- sqlite3DbFree(db, z); +- return; +- } +- p->aCol = aNew; ++ aNew = sqlite3DbRealloc(db,p->aCol,((i64)p->nCol+1)*sizeof(p->aCol[0])); ++ if( aNew==0 ){ ++ sqlite3DbFree(db, z); ++ return; + } ++ p->aCol = aNew; + pCol = &p->aCol[p->nCol]; + memset(pCol, 0, sizeof(p->aCol[0])); +- pCol->zName = z; ++ pCol->zCnName = z; + pCol->hName = sqlite3StrIHash(z); + sqlite3ColumnPropertiesFromName(p, pCol); + +- if( pType->n==0 ){ ++ if( sType.n==0 ){ + /* If there is no type specified, columns have the default affinity + ** 'BLOB' with a default size of 4 bytes. */ +- pCol->affinity = SQLITE_AFF_BLOB; +- pCol->szEst = 1; ++ pCol->affinity = affinity; ++ pCol->eCType = eType; ++ pCol->szEst = szEst; + #ifdef SQLITE_ENABLE_SORTER_REFERENCES +- if( 4>=sqlite3GlobalConfig.szSorterRef ){ +- pCol->colFlags |= COLFLAG_SORTERREF; ++ if( affinity==SQLITE_AFF_BLOB ){ ++ if( 4>=sqlite3GlobalConfig.szSorterRef ){ ++ pCol->colFlags |= COLFLAG_SORTERREF; ++ } + } + #endif + }else{ + zType = z + sqlite3Strlen30(z) + 1; +- memcpy(zType, pType->z, pType->n); +- zType[pType->n] = 0; ++ memcpy(zType, sType.z, sType.n); ++ zType[sType.n] = 0; + sqlite3Dequote(zType); + pCol->affinity = sqlite3AffinityType(zType, pCol); + pCol->colFlags |= COLFLAG_HASTYPE; + } ++ if( p->nCol<=0xff ){ ++ u8 h = pCol->hName % sizeof(p->aHx); ++ p->aHx[h] = p->nCol; ++ } + p->nCol++; + p->nNVCol++; +- pParse->constraintName.n = 0; ++ assert( pParse->isCreate ); ++ pParse->u1.cr.constraintName.n = 0; + } + + /* +@@ -111766,7 +124644,8 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){ + + assert( zIn!=0 ); + while( zIn[0] ){ +- h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff]; ++ u8 x = *(u8*)zIn; ++ h = (h<<8) + sqlite3UpperToLower[x]; + zIn++; + if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */ + aff = SQLITE_AFF_TEXT; +@@ -111840,7 +124719,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue( + Parse *pParse, /* Parsing context */ + Expr *pExpr, /* The parsed expression of the default value */ + const char *zStart, /* Start of the default value text */ +- const char *zEnd /* First character past end of defaut value text */ ++ const char *zEnd /* First character past end of default value text */ + ){ + Table *p; + Column *pCol; +@@ -111851,7 +124730,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue( + pCol = &(p->aCol[p->nCol-1]); + if( !sqlite3ExprIsConstantOrFunction(pExpr, isInit) ){ + sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", +- pCol->zName); ++ pCol->zCnName); + #ifndef SQLITE_OMIT_GENERATED_COLUMNS + }else if( pCol->colFlags & COLFLAG_GENERATED ){ + testcase( pCol->colFlags & COLFLAG_VIRTUAL ); +@@ -111862,15 +124741,15 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue( + /* A copy of pExpr is used instead of the original, as pExpr contains + ** tokens that point to volatile memory. + */ +- Expr x; +- sqlite3ExprDelete(db, pCol->pDflt); ++ Expr x, *pDfltExpr; + memset(&x, 0, sizeof(x)); + x.op = TK_SPAN; + x.u.zToken = sqlite3DbSpanDup(db, zStart, zEnd); + x.pLeft = pExpr; + x.flags = EP_Skip; +- pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); ++ pDfltExpr = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); + sqlite3DbFree(db, x.u.zToken); ++ sqlite3ColumnSetExpr(pParse, p, pCol, pDfltExpr); + } + } + if( IN_RENAME_OBJECT ){ +@@ -111966,20 +124845,18 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( + assert( pCExpr!=0 ); + sqlite3StringToId(pCExpr); + if( pCExpr->op==TK_ID ){ +- const char *zCName = pCExpr->u.zToken; +- for(iCol=0; iColnCol; iCol++){ +- if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){ +- pCol = &pTab->aCol[iCol]; +- makeColumnPartOfPrimaryKey(pParse, pCol); +- break; +- } ++ assert( !ExprHasProperty(pCExpr, EP_IntValue) ); ++ iCol = sqlite3ColumnIndex(pTab, pCExpr->u.zToken); ++ if( iCol>=0 ){ ++ pCol = &pTab->aCol[iCol]; ++ makeColumnPartOfPrimaryKey(pParse, pCol); + } + } + } + } + if( nTerm==1 + && pCol +- && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0 ++ && pCol->eCType==COLTYPE_INTEGER + && sortOrder!=SQLITE_SO_DESC + ){ + if( IN_RENAME_OBJECT && pList ){ +@@ -111990,7 +124867,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( + pTab->keyConf = (u8)onError; + assert( autoInc==0 || autoInc==1 ); + pTab->tabFlags |= autoInc*TF_Autoincrement; +- if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags; ++ if( pList ) pParse->iPkSortOrder = pList->a[0].fg.sortFlags; + (void)sqlite3HasExplicitNulls(pParse, pList); + }else if( autoInc ){ + #ifndef SQLITE_OMIT_AUTOINCREMENT +@@ -112012,8 +124889,10 @@ primary_key_exit: + ** Add a new CHECK constraint to the table currently under construction. + */ + SQLITE_PRIVATE void sqlite3AddCheckConstraint( +- Parse *pParse, /* Parsing context */ +- Expr *pCheckExpr /* The check expression */ ++ Parse *pParse, /* Parsing context */ ++ Expr *pCheckExpr, /* The check expression */ ++ const char *zStart, /* Opening "(" */ ++ const char *zEnd /* Closing ")" */ + ){ + #ifndef SQLITE_OMIT_CHECK + Table *pTab = pParse->pNewTable; +@@ -112022,8 +124901,17 @@ SQLITE_PRIVATE void sqlite3AddCheckConstraint( + && !sqlite3BtreeIsReadonly(db->aDb[db->init.iDb].pBt) + ){ + pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr); +- if( pParse->constraintName.n ){ +- sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1); ++ assert( pParse->isCreate ); ++ if( pParse->u1.cr.constraintName.n ){ ++ sqlite3ExprListSetName(pParse, pTab->pCheck, ++ &pParse->u1.cr.constraintName, 1); ++ }else{ ++ Token t; ++ for(zStart++; sqlite3Isspace(zStart[0]); zStart++){} ++ while( sqlite3Isspace(zEnd[-1]) ){ zEnd--; } ++ t.z = zStart; ++ t.n = (int)(zEnd - t.z); ++ sqlite3ExprListSetName(pParse, pTab->pCheck, &t, 1); + } + }else + #endif +@@ -112042,7 +124930,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ + char *zColl; /* Dequoted name of collation sequence */ + sqlite3 *db; + +- if( (p = pParse->pNewTable)==0 ) return; ++ if( (p = pParse->pNewTable)==0 || IN_RENAME_OBJECT ) return; + i = p->nCol-1; + db = pParse->db; + zColl = sqlite3NameFromToken(db, pToken); +@@ -112050,8 +124938,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ + + if( sqlite3LocateCollSeq(pParse, zColl) ){ + Index *pIdx; +- sqlite3DbFree(db, p->aCol[i].zColl); +- p->aCol[i].zColl = zColl; ++ sqlite3ColumnSetColl(db, &p->aCol[i], zColl); + + /* If the column is declared as " PRIMARY KEY COLLATE ", + ** then an index may have been created on this column before the +@@ -112060,12 +124947,11 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ + for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->nKeyCol==1 ); + if( pIdx->aiColumn[0]==i ){ +- pIdx->azColl[0] = p->aCol[i].zColl; ++ pIdx->azColl[0] = sqlite3ColumnColl(&p->aCol[i]); + } + } +- }else{ +- sqlite3DbFree(db, zColl); + } ++ sqlite3DbFree(db, zColl); + } + + /* Change the most recently parsed column to be a GENERATED ALWAYS AS +@@ -112085,7 +124971,7 @@ SQLITE_PRIVATE void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType + sqlite3ErrorMsg(pParse, "virtual tables cannot use computed columns"); + goto generated_done; + } +- if( pCol->pDflt ) goto generated_error; ++ if( pCol->iDflt>0 ) goto generated_error; + if( pType ){ + if( pType->n==7 && sqlite3StrNICmp("virtual",pType->z,7)==0 ){ + /* no-op */ +@@ -112103,13 +124989,21 @@ SQLITE_PRIVATE void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType + if( pCol->colFlags & COLFLAG_PRIMKEY ){ + makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */ + } +- pCol->pDflt = pExpr; ++ if( ALWAYS(pExpr) && pExpr->op==TK_ID ){ ++ /* The value of a generated column needs to be a real expression, not ++ ** just a reference to another column, in order for covering index ++ ** optimizations to work correctly. So if the value is not an expression, ++ ** turn it into one by adding a unary "+" operator. */ ++ pExpr = sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0); ++ } ++ if( pExpr && pExpr->op!=TK_RAISE ) pExpr->affExpr = pCol->affinity; ++ sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr); + pExpr = 0; + goto generated_done; + + generated_error: + sqlite3ErrorMsg(pParse, "error in generated column \"%s\"", +- pCol->zName); ++ pCol->zCnName); + generated_done: + sqlite3ExprDelete(pParse->db, pExpr); + #else +@@ -112171,7 +125065,7 @@ static int identLength(const char *z){ + ** to the specified offset in the buffer and updates *pIdx to refer + ** to the first byte after the last byte written before returning. + ** +-** If the string zSignedIdent consists entirely of alpha-numeric ++** If the string zSignedIdent consists entirely of alphanumeric + ** characters, does not begin with a digit and is not an SQL keyword, + ** then it is copied to the output buffer exactly as it is. Otherwise, + ** it is quoted using double-quotes. +@@ -112205,13 +125099,14 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){ + ** from sqliteMalloc() and must be freed by the calling function. + */ + static char *createTableStmt(sqlite3 *db, Table *p){ +- int i, k, n; ++ int i, k, len; ++ i64 n; + char *zStmt; + char *zSep, *zSep2, *zEnd; + Column *pCol; + n = 0; + for(pCol = p->aCol, i=0; inCol; i++, pCol++){ +- n += identLength(pCol->zName) + 5; ++ n += identLength(pCol->zCnName) + 5; + } + n += identLength(p->zName); + if( n<50 ){ +@@ -112229,8 +125124,9 @@ static char *createTableStmt(sqlite3 *db, Table *p){ + sqlite3OomFault(db); + return 0; + } +- sqlite3_snprintf(n, zStmt, "CREATE TABLE "); +- k = sqlite3Strlen30(zStmt); ++ assert( n>14 && n<=0x7fffffff ); ++ memcpy(zStmt, "CREATE TABLE ", 13); ++ k = 13; + identPut(zStmt, &k, p->zName); + zStmt[k++] = '('; + for(pCol=p->aCol, i=0; inCol; i++, pCol++){ +@@ -112239,15 +125135,18 @@ static char *createTableStmt(sqlite3 *db, Table *p){ + /* SQLITE_AFF_TEXT */ " TEXT", + /* SQLITE_AFF_NUMERIC */ " NUM", + /* SQLITE_AFF_INTEGER */ " INT", +- /* SQLITE_AFF_REAL */ " REAL" ++ /* SQLITE_AFF_REAL */ " REAL", ++ /* SQLITE_AFF_FLEXNUM */ " NUM", + }; +- int len; + const char *zType; + +- sqlite3_snprintf(n-k, &zStmt[k], zSep); +- k += sqlite3Strlen30(&zStmt[k]); ++ len = sqlite3Strlen30(zSep); ++ assert( k+lenzName); ++ identPut(zStmt, &k, pCol->zCnName); ++ assert( kaffinity-SQLITE_AFF_BLOB >= 0 ); + assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) ); + testcase( pCol->affinity==SQLITE_AFF_BLOB ); +@@ -112255,16 +125154,21 @@ static char *createTableStmt(sqlite3 *db, Table *p){ + testcase( pCol->affinity==SQLITE_AFF_NUMERIC ); + testcase( pCol->affinity==SQLITE_AFF_INTEGER ); + testcase( pCol->affinity==SQLITE_AFF_REAL ); ++ testcase( pCol->affinity==SQLITE_AFF_FLEXNUM ); + + zType = azType[pCol->affinity - SQLITE_AFF_BLOB]; + len = sqlite3Strlen30(zType); + assert( pCol->affinity==SQLITE_AFF_BLOB ++ || pCol->affinity==SQLITE_AFF_FLEXNUM + || pCol->affinity==sqlite3AffinityType(zType, 0) ); ++ assert( k+lennColumn>=N ) return SQLITE_OK; ++ db = pParse->db; ++ assert( N>0 ); ++ assert( N <= SQLITE_MAX_COLUMN*2 /* tag-20250221-1 */ ); ++ testcase( N==2*pParse->db->aLimit[SQLITE_LIMIT_COLUMN] ); + assert( pIdx->isResized==0 ); +- nByte = (sizeof(char*) + sizeof(i16) + 1)*N; ++ nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*(u64)N; + zExtra = sqlite3DbMallocZero(db, nByte); + if( zExtra==0 ) return SQLITE_NOMEM_BKPT; + memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); + pIdx->azColl = (const char**)zExtra; + zExtra += sizeof(char*)*N; ++ memcpy(zExtra, pIdx->aiRowLogEst, sizeof(LogEst)*(pIdx->nKeyCol+1)); ++ pIdx->aiRowLogEst = (LogEst*)zExtra; ++ zExtra += sizeof(LogEst)*N; + memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn); + pIdx->aiColumn = (i16*)zExtra; + zExtra += sizeof(i16)*N; + memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn); + pIdx->aSortOrder = (u8*)zExtra; +- pIdx->nColumn = N; ++ pIdx->nColumn = (u16)N; /* See tag-20250221-1 above for proof of safety */ + pIdx->isResized = 1; + return SQLITE_OK; + } +@@ -112317,7 +125229,7 @@ static void estimateIndexWidth(Index *pIdx){ + for(i=0; inColumn; i++){ + i16 x = pIdx->aiColumn[i]; + assert( xpTable->nCol ); +- wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst; ++ wIndex += x<0 ? 1 : aCol[x].szEst; + } + pIdx->szIdxRow = sqlite3LogEst(wIndex*4); + } +@@ -112328,7 +125240,6 @@ static void estimateIndexWidth(Index *pIdx){ + */ + static int hasColumn(const i16 *aiCol, int nCol, int x){ + while( nCol-- > 0 ){ +- assert( aiCol[0]>=0 ); + if( x==*(aiCol++) ){ + return 1; + } +@@ -112373,7 +125284,8 @@ static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){ + /* Recompute the colNotIdxed field of the Index. + ** + ** colNotIdxed is a bitmask that has a 0 bit representing each indexed +-** columns that are within the first 63 columns of the table. The ++** columns that are within the first 63 columns of the table and a 1 for ++** all other bits (all columns that are not in the index). The + ** high-order bit of colNotIdxed is always 1. All unindexed columns + ** of the table have a 1. + ** +@@ -112401,7 +125313,7 @@ static void recomputeColumnsNotIndexed(Index *pIdx){ + } + } + pIdx->colNotIdxed = ~m; +- assert( (pIdx->colNotIdxed>>63)==1 ); ++ assert( (pIdx->colNotIdxed>>63)==1 ); /* See note-20221022-a */ + } + + /* +@@ -112441,7 +125353,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ + */ + if( !db->init.imposterTable ){ + for(i=0; inCol; i++){ +- if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){ ++ if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ++ && (pTab->aCol[i].notNull==OE_None) ++ ){ + pTab->aCol[i].notNull = OE_Abort; + } + } +@@ -112451,9 +125365,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ + /* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY + ** into BTREE_BLOBKEY. + */ +- if( pParse->addrCrTab ){ ++ assert( !pParse->bReturning ); ++ if( pParse->u1.cr.addrCrTab ){ + assert( v ); +- sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY); ++ sqlite3VdbeChangeP3(v, pParse->u1.cr.addrCrTab, BTREE_BLOBKEY); + } + + /* Locate the PRIMARY KEY index. Or, if this table was originally +@@ -112462,19 +125377,26 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ + if( pTab->iPKey>=0 ){ + ExprList *pList; + Token ipkToken; +- sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName); ++ sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zCnName); + pList = sqlite3ExprListAppend(pParse, 0, + sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0)); +- if( pList==0 ) return; ++ if( pList==0 ){ ++ pTab->tabFlags &= ~TF_WithoutRowid; ++ return; ++ } + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey); + } +- pList->a[0].sortFlags = pParse->iPkSortOrder; ++ pList->a[0].fg.sortFlags = pParse->iPkSortOrder; + assert( pParse->pNewTable==pTab ); + pTab->iPKey = -1; + sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0, + SQLITE_IDXTYPE_PRIMARYKEY); +- if( db->mallocFailed || pParse->nErr ) return; ++ if( pParse->nErr ){ ++ pTab->tabFlags &= ~TF_WithoutRowid; ++ return; ++ } ++ assert( db->mallocFailed==0 ); + pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk->nKeyCol==1 ); + }else{ +@@ -112532,14 +125454,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ + pIdx->nColumn = pIdx->nKeyCol; + continue; + } +- if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return; ++ if( resizeIndexObject(pParse, pIdx, pIdx->nKeyCol+n) ) return; + for(i=0, j=pIdx->nKeyCol; inKeyCol, pPk, i) ){ + testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ); + pIdx->aiColumn[j] = pPk->aiColumn[i]; + pIdx->azColl[j] = pPk->azColl[i]; + if( pPk->aSortOrder[i] ){ +- /* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */ ++ /* See ticket https://sqlite.org/src/info/bba7b69f9849b5bf */ + pIdx->bAscKeyBug = 1; + } + j++; +@@ -112556,7 +125478,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ + if( !hasColumn(pPk->aiColumn, nPk, i) + && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++; + } +- if( resizeIndexObject(db, pPk, nPk+nExtra) ) return; ++ if( resizeIndexObject(pParse, pPk, nPk+nExtra) ) return; + for(i=0, j=nPk; inCol; i++){ + if( !hasColumn(pPk->aiColumn, j, i) + && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 +@@ -112586,7 +125508,7 @@ SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char * + nName = sqlite3Strlen30(pTab->zName); + if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0; + if( zName[nName]!='_' ) return 0; +- pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); ++ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); + if( pMod==0 ) return 0; + if( pMod->pModule->iVersion<3 ) return 0; + if( pMod->pModule->xShadowName==0 ) return 0; +@@ -112594,6 +125516,41 @@ SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char * + } + #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ + ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++/* ++** Table pTab is a virtual table. If it the virtual table implementation ++** exists and has an xShadowName method, then loop over all other ordinary ++** tables within the same schema looking for shadow tables of pTab, and mark ++** any shadow tables seen using the TF_Shadow flag. ++*/ ++SQLITE_PRIVATE void sqlite3MarkAllShadowTablesOf(sqlite3 *db, Table *pTab){ ++ int nName; /* Length of pTab->zName */ ++ Module *pMod; /* Module for the virtual table */ ++ HashElem *k; /* For looping through the symbol table */ ++ ++ assert( IsVirtual(pTab) ); ++ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); ++ if( pMod==0 ) return; ++ if( NEVER(pMod->pModule==0) ) return; ++ if( pMod->pModule->iVersion<3 ) return; ++ if( pMod->pModule->xShadowName==0 ) return; ++ assert( pTab->zName!=0 ); ++ nName = sqlite3Strlen30(pTab->zName); ++ for(k=sqliteHashFirst(&pTab->pSchema->tblHash); k; k=sqliteHashNext(k)){ ++ Table *pOther = sqliteHashData(k); ++ assert( pOther->zName!=0 ); ++ if( !IsOrdinaryTable(pOther) ) continue; ++ if( pOther->tabFlags & TF_Shadow ) continue; ++ if( sqlite3StrNICmp(pOther->zName, pTab->zName, nName)==0 ++ && pOther->zName[nName]=='_' ++ && pMod->pModule->xShadowName(pOther->zName+nName+1) ++ ){ ++ pOther->tabFlags |= TF_Shadow; ++ } ++ } ++} ++#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ ++ + #ifndef SQLITE_OMIT_VIRTUALTABLE + /* + ** Return true if zName is a shadow table name in the current database +@@ -112625,6 +125582,7 @@ SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ + ** not pass them into code generator routines by mistake. + */ + static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){ ++ (void)pWalker; + ExprSetVVAProperty(pExpr, EP_Immutable); + return WRC_Continue; + } +@@ -112667,7 +125625,7 @@ SQLITE_PRIVATE void sqlite3EndTable( + Parse *pParse, /* Parse context */ + Token *pCons, /* The ',' token after the last column defn. */ + Token *pEnd, /* The ')' before options in the CREATE TABLE */ +- u8 tabOpts, /* Extra table options. Usually 0. */ ++ u32 tabOpts, /* Extra table options. Usually 0. */ + Select *pSelect /* Select from a "CREATE ... AS SELECT" */ + ){ + Table *p; /* The new table */ +@@ -112678,7 +125636,6 @@ SQLITE_PRIVATE void sqlite3EndTable( + if( pEnd==0 && pSelect==0 ){ + return; + } +- assert( !db->mallocFailed ); + p = pParse->pNewTable; + if( p==0 ) return; + +@@ -112696,7 +125653,7 @@ SQLITE_PRIVATE void sqlite3EndTable( + ** table itself. So mark it read-only. + */ + if( db->init.busy ){ +- if( pSelect ){ ++ if( pSelect || (!IsOrdinaryTable(p) && db->init.newTnum) ){ + sqlite3ErrorMsg(pParse, ""); + return; + } +@@ -112704,6 +125661,44 @@ SQLITE_PRIVATE void sqlite3EndTable( + if( p->tnum==1 ) p->tabFlags |= TF_Readonly; + } + ++ /* Special processing for tables that include the STRICT keyword: ++ ** ++ ** * Do not allow custom column datatypes. Every column must have ++ ** a datatype that is one of INT, INTEGER, REAL, TEXT, or BLOB. ++ ** ++ ** * If a PRIMARY KEY is defined, other than the INTEGER PRIMARY KEY, ++ ** then all columns of the PRIMARY KEY must have a NOT NULL ++ ** constraint. ++ */ ++ if( tabOpts & TF_Strict ){ ++ int ii; ++ p->tabFlags |= TF_Strict; ++ for(ii=0; iinCol; ii++){ ++ Column *pCol = &p->aCol[ii]; ++ if( pCol->eCType==COLTYPE_CUSTOM ){ ++ if( pCol->colFlags & COLFLAG_HASTYPE ){ ++ sqlite3ErrorMsg(pParse, ++ "unknown datatype for %s.%s: \"%s\"", ++ p->zName, pCol->zCnName, sqlite3ColumnType(pCol, "") ++ ); ++ }else{ ++ sqlite3ErrorMsg(pParse, "missing datatype for %s.%s", ++ p->zName, pCol->zCnName); ++ } ++ return; ++ }else if( pCol->eCType==COLTYPE_ANY ){ ++ pCol->affinity = SQLITE_AFF_BLOB; ++ } ++ if( (pCol->colFlags & COLFLAG_PRIMKEY)!=0 ++ && p->iPKey!=ii ++ && pCol->notNull == OE_None ++ ){ ++ pCol->notNull = OE_Abort; ++ p->tabFlags |= TF_HasNotNull; ++ } ++ } ++ } ++ + assert( (p->tabFlags & TF_HasPrimaryKey)==0 + || p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 ); + assert( (p->tabFlags & TF_HasPrimaryKey)!=0 +@@ -112748,7 +125743,7 @@ SQLITE_PRIVATE void sqlite3EndTable( + for(ii=0; iinCol; ii++){ + u32 colFlags = p->aCol[ii].colFlags; + if( (colFlags & COLFLAG_GENERATED)!=0 ){ +- Expr *pX = p->aCol[ii].pDflt; ++ Expr *pX = sqlite3ColumnExpr(p, &p->aCol[ii]); + testcase( colFlags & COLFLAG_VIRTUAL ); + testcase( colFlags & COLFLAG_STORED ); + if( sqlite3ResolveSelfReference(pParse, p, NC_GenCol, pX, 0) ){ +@@ -112758,8 +125753,8 @@ SQLITE_PRIVATE void sqlite3EndTable( + ** tree that have been allocated from lookaside memory, which is + ** illegal in a schema and will lead to errors or heap corruption + ** when the database connection closes. */ +- sqlite3ExprDelete(db, pX); +- p->aCol[ii].pDflt = sqlite3ExprAlloc(db, TK_NULL, 0, 0); ++ sqlite3ColumnSetExpr(pParse, p, &p->aCol[ii], ++ sqlite3ExprAlloc(db, TK_NULL, 0, 0)); + } + }else{ + nNG++; +@@ -112799,7 +125794,7 @@ SQLITE_PRIVATE void sqlite3EndTable( + /* + ** Initialize zType for the new view or table. + */ +- if( p->pSelect==0 ){ ++ if( IsOrdinaryTable(p) ){ + /* A regular table */ + zType = "table"; + zType2 = "TABLE"; +@@ -112813,7 +125808,7 @@ SQLITE_PRIVATE void sqlite3EndTable( + + /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT + ** statement to populate the new table. The root-page number for the +- ** new table is in register pParse->regRoot. ++ ** new table is in register pParse->u1.cr.regRoot. + ** + ** Once the SELECT has been coded by sqlite3Select(), it is in a + ** suitable state to query for the column names and types to be used +@@ -112832,15 +125827,21 @@ SQLITE_PRIVATE void sqlite3EndTable( + int regRowid; /* Rowid of the next row to insert */ + int addrInsLoop; /* Top of the loop for inserting rows */ + Table *pSelTab; /* A table that describes the SELECT results */ ++ int iCsr; /* Write cursor on the new table */ + ++ if( IN_SPECIAL_PARSE ){ ++ pParse->rc = SQLITE_ERROR; ++ pParse->nErr++; ++ return; ++ } ++ iCsr = pParse->nTab++; + regYield = ++pParse->nMem; + regRec = ++pParse->nMem; + regRowid = ++pParse->nMem; +- assert(pParse->nTab==1); + sqlite3MayAbort(pParse); +- sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); ++ assert( pParse->isCreate ); ++ sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->u1.cr.regRoot, iDb); + sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG); +- pParse->nTab = 2; + addrTop = sqlite3VdbeCurrentAddr(v) + 1; + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); + if( pParse->nErr ) return; +@@ -112861,11 +125862,11 @@ SQLITE_PRIVATE void sqlite3EndTable( + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec); + sqlite3TableAffinity(v, p, 0); +- sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid); +- sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid); ++ sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid); ++ sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid); + sqlite3VdbeGoto(v, addrInsLoop); + sqlite3VdbeJumpHere(v, addrInsLoop); +- sqlite3VdbeAddOp1(v, OP_Close, 1); ++ sqlite3VdbeAddOp1(v, OP_Close, iCsr); + } + + /* Compute the complete text of the CREATE statement */ +@@ -112884,17 +125885,18 @@ SQLITE_PRIVATE void sqlite3EndTable( + ** schema table. We just need to update that slot with all + ** the information we've collected. + */ ++ assert( pParse->isCreate ); + sqlite3NestedParse(pParse, +- "UPDATE %Q." DFLT_SCHEMA_TABLE ++ "UPDATE %Q." LEGACY_SCHEMA_TABLE + " SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q" + " WHERE rowid=#%d", + db->aDb[iDb].zDbSName, + zType, + p->zName, + p->zName, +- pParse->regRoot, ++ pParse->u1.cr.regRoot, + zStmt, +- pParse->regRowid ++ pParse->u1.cr.regRowid + ); + sqlite3DbFree(db, zStmt); + sqlite3ChangeCookie(pParse, iDb); +@@ -112903,7 +125905,7 @@ SQLITE_PRIVATE void sqlite3EndTable( + /* Check to see if we need to create an sqlite_sequence table for + ** keeping track of autoincrement keys. + */ +- if( (p->tabFlags & TF_Autoincrement)!=0 ){ ++ if( (p->tabFlags & TF_Autoincrement)!=0 && !IN_SPECIAL_PARSE ){ + Db *pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + if( pDb->pSchema->pSeqTab==0 ){ +@@ -112917,7 +125919,15 @@ SQLITE_PRIVATE void sqlite3EndTable( + + /* Reparse everything to update our internal data structures */ + sqlite3VdbeAddParseSchemaOp(v, iDb, +- sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName)); ++ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0); ++ ++ /* Test for cycles in generated columns and illegal expressions ++ ** in CHECK constraints and in DEFAULT clauses. */ ++ if( p->tabFlags & TF_HasGenerated ){ ++ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, ++ sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", ++ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); ++ } + } + + /* Add the table to the in-memory representation of the database. +@@ -112926,6 +125936,7 @@ SQLITE_PRIVATE void sqlite3EndTable( + Table *pOld; + Schema *pSchema = p->pSchema; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); ++ assert( HasRowid(p) || p->iPKey<0 ); + pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p); + if( pOld ){ + assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ +@@ -112935,19 +125946,27 @@ SQLITE_PRIVATE void sqlite3EndTable( + pParse->pNewTable = 0; + db->mDbFlags |= DBFLAG_SchemaChange; + +-#ifndef SQLITE_OMIT_ALTERTABLE +- if( !p->pSelect ){ +- const char *zName = (const char *)pParse->sNameToken.z; +- int nName; +- assert( !pSelect && pCons && pEnd ); +- if( pCons->z==0 ){ +- pCons = pEnd; +- } +- nName = (int)((const char *)pCons->z - zName); +- p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName); ++ /* If this is the magic sqlite_sequence table used by autoincrement, ++ ** then record a pointer to this table in the main database structure ++ ** so that INSERT can find the table easily. */ ++ assert( !pParse->nested ); ++#ifndef SQLITE_OMIT_AUTOINCREMENT ++ if( strcmp(p->zName, "sqlite_sequence")==0 ){ ++ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); ++ p->pSchema->pSeqTab = p; + } + #endif + } ++ ++#ifndef SQLITE_OMIT_ALTERTABLE ++ if( !pSelect && IsOrdinaryTable(p) ){ ++ assert( pCons && pEnd ); ++ if( pCons->z==0 ){ ++ pCons = pEnd; ++ } ++ p->u.tab.addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z); ++ } ++#endif + } + + #ifndef SQLITE_OMIT_VIEW +@@ -112980,6 +125999,19 @@ SQLITE_PRIVATE void sqlite3CreateView( + sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); + p = pParse->pNewTable; + if( p==0 || pParse->nErr ) goto create_view_fail; ++ ++ /* Legacy versions of SQLite allowed the use of the magic "rowid" column ++ ** on a view, even though views do not have rowids. The following flag ++ ** setting fixes this problem. But the fix can be disabled by compiling ++ ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that ++ ** depend upon the old buggy behavior. The ability can also be toggled ++ ** using sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW,...) */ ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */ ++#else ++ p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */ ++#endif ++ + sqlite3TwoPartName(pParse, pName1, pName2, &pName); + iDb = sqlite3SchemaToIndex(db, p->pSchema); + sqlite3FixInit(&sFix, pParse, iDb, "view", pName); +@@ -112992,12 +126024,13 @@ SQLITE_PRIVATE void sqlite3CreateView( + */ + pSelect->selFlags |= SF_View; + if( IN_RENAME_OBJECT ){ +- p->pSelect = pSelect; ++ p->u.view.pSelect = pSelect; + pSelect = 0; + }else{ +- p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); ++ p->u.view.pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + } + p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE); ++ p->eTabType = TABTYP_VIEW; + if( db->mallocFailed ) goto create_view_fail; + + /* Locate the end of the CREATE VIEW statement. Make sEnd point to +@@ -113032,14 +126065,14 @@ create_view_fail: + #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) + /* + ** The Table structure pTable is really a VIEW. Fill in the names of +-** the columns of the view in the pTable structure. Return the number +-** of errors. If an error is seen leave an error message in pParse->zErrMsg. ++** the columns of the view in the pTable structure. Return non-zero if ++** there are errors. If an error is seen an error message is left ++** in pParse->zErrMsg. + */ +-SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ ++static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){ + Table *pSelTab; /* A fake table from which we get the result set */ + Select *pSel; /* Copy of the SELECT that implements the view */ + int nErr = 0; /* Number of errors encountered */ +- int n; /* Temporarily holds the number of cursors assigned */ + sqlite3 *db = pParse->db; /* Database connection for malloc errors */ + #ifndef SQLITE_OMIT_VIRTUALTABLE + int rc; +@@ -113051,20 +126084,20 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + assert( pTable ); + + #ifndef SQLITE_OMIT_VIRTUALTABLE +- db->nSchemaLock++; +- rc = sqlite3VtabCallConnect(pParse, pTable); +- db->nSchemaLock--; +- if( rc ){ +- return 1; ++ if( IsVirtual(pTable) ){ ++ db->nSchemaLock++; ++ rc = sqlite3VtabCallConnect(pParse, pTable); ++ db->nSchemaLock--; ++ return rc; + } +- if( IsVirtual(pTable) ) return 0; + #endif + + #ifndef SQLITE_OMIT_VIEW + /* A positive nCol means the columns names for this view are +- ** already known. ++ ** already known. This routine is not called unless either the ++ ** table is virtual or nCol is zero. + */ +- if( pTable->nCol>0 ) return 0; ++ assert( pTable->nCol<=0 ); + + /* A negative nCol is a special marker meaning that we are currently + ** trying to compute the column names. If we enter this routine with +@@ -113094,12 +126127,13 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + ** to be permanent. So the computation is done on a copy of the SELECT + ** statement that defines the view. + */ +- assert( pTable->pSelect ); +- pSel = sqlite3SelectDup(db, pTable->pSelect, 0); ++ assert( IsView(pTable) ); ++ pSel = sqlite3SelectDup(db, pTable->u.view.pSelect, 0); + if( pSel ){ + u8 eParseMode = pParse->eParseMode; ++ int nTab = pParse->nTab; ++ int nSelect = pParse->nSelect; + pParse->eParseMode = PARSE_MODE_NORMAL; +- n = pParse->nTab; + sqlite3SrcListAssignCursors(pParse, pSel->pSrc); + pTable->nCol = -1; + DisableLookaside; +@@ -113111,7 +126145,8 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + #else + pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE); + #endif +- pParse->nTab = n; ++ pParse->nTab = nTab; ++ pParse->nSelect = nSelect; + if( pSelTab==0 ){ + pTable->nCol = 0; + nErr++; +@@ -113124,12 +126159,11 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + */ + sqlite3ColumnsFromExprList(pParse, pTable->pCheck, + &pTable->nCol, &pTable->aCol); +- if( db->mallocFailed==0 +- && pParse->nErr==0 ++ if( pParse->nErr==0 + && pTable->nCol==pSel->pEList->nExpr + ){ +- sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel, +- SQLITE_AFF_NONE); ++ assert( db->mallocFailed==0 ); ++ sqlite3SubqueryColumnTypes(pParse, pTable, pSel, SQLITE_AFF_NONE); + } + }else{ + /* CREATE VIEW name AS... without an argument list. Construct +@@ -113138,6 +126172,7 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + assert( pTable->aCol==0 ); + pTable->nCol = pSelTab->nCol; + pTable->aCol = pSelTab->aCol; ++ pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT); + pSelTab->nCol = 0; + pSelTab->aCol = 0; + assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); +@@ -113153,11 +126188,14 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ + pTable->pSchema->schemaFlags |= DB_UnresetViews; + if( db->mallocFailed ){ + sqlite3DeleteColumnNames(db, pTable); +- pTable->aCol = 0; +- pTable->nCol = 0; + } + #endif /* SQLITE_OMIT_VIEW */ +- return nErr; ++ return nErr + pParse->nErr; ++} ++SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ ++ assert( pTable!=0 ); ++ if( !IsVirtual(pTable) && pTable->nCol>0 ) return 0; ++ return viewGetColumnNames(pParse, pTable); + } + #endif /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ + +@@ -113171,10 +126209,8 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ + if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; + for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ + Table *pTab = sqliteHashData(i); +- if( pTab->pSelect ){ ++ if( IsView(pTab) ){ + sqlite3DeleteColumnNames(db, pTab); +- pTab->aCol = 0; +- pTab->nCol = 0; + } + } + DbClearProperty(db, idx, DB_UnresetViews); +@@ -113248,7 +126284,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ + ** token for additional information. + */ + sqlite3NestedParse(pParse, +- "UPDATE %Q." DFLT_SCHEMA_TABLE ++ "UPDATE %Q." LEGACY_SCHEMA_TABLE + " SET rootpage=%d WHERE #%d AND rootpage=#%d", + pParse->db->aDb[iDb].zDbSName, iTable, r1, r1); + #endif +@@ -113383,7 +126419,7 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in + ** database. + */ + sqlite3NestedParse(pParse, +- "DELETE FROM %Q." DFLT_SCHEMA_TABLE ++ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE + " WHERE tbl_name=%Q and type!='trigger'", + pDb->zDbSName, pTab->zName); + if( !isView && !IsVirtual(pTab) ){ +@@ -113411,6 +126447,7 @@ SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db){ + if( (db->flags & SQLITE_Defensive)!=0 + && db->pVtabCtx==0 + && db->nVdbeExec==0 ++ && !sqlite3VtabInSync(db) + ){ + return 1; + } +@@ -113430,6 +126467,9 @@ static int tableMayNotBeDropped(sqlite3 *db, Table *pTab){ + if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){ + return 1; + } ++ if( pTab->tabFlags & TF_Eponymous ){ ++ return 1; ++ } + return 0; + } + +@@ -113448,6 +126488,8 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, + } + assert( pParse->nErr==0 ); + assert( pName->nSrc==1 ); ++ assert( pName->a[0].fg.fixedSchema==0 ); ++ assert( pName->a[0].fg.isSubquery==0 ); + if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; + if( noErr ) db->suppressErr++; + assert( isView==0 || isView==LOCATE_VIEW ); +@@ -113455,7 +126497,10 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, + if( noErr ) db->suppressErr--; + + if( pTab==0 ){ +- if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); ++ if( noErr ){ ++ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); ++ sqlite3ForceNotReadOnly(pParse); ++ } + goto exit_drop_table; + } + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); +@@ -113511,11 +126556,11 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, + /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used + ** on a table. + */ +- if( isView && pTab->pSelect==0 ){ ++ if( isView && !IsView(pTab) ){ + sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); + goto exit_drop_table; + } +- if( !isView && pTab->pSelect ){ ++ if( !isView && IsView(pTab) ){ + sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); + goto exit_drop_table; + } +@@ -113566,7 +126611,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( + FKey *pFKey = 0; + FKey *pNextTo; + Table *p = pParse->pNewTable; +- int nByte; ++ i64 nByte; + int i; + int nCol; + char *z; +@@ -113579,7 +126624,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( + if( pToCol && pToCol->nExpr!=1 ){ + sqlite3ErrorMsg(pParse, "foreign key on %s" + " should reference only one column of table %T", +- p->aCol[iCol].zName, pTo); ++ p->aCol[iCol].zCnName, pTo); + goto fk_end; + } + nCol = 1; +@@ -113591,7 +126636,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( + }else{ + nCol = pFromCol->nExpr; + } +- nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1; ++ nByte = SZ_FKEY(nCol) + pTo->n + 1; + if( pToCol ){ + for(i=0; inExpr; i++){ + nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; +@@ -113602,7 +126647,8 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( + goto fk_end; + } + pFKey->pFrom = p; +- pFKey->pNextFrom = p->pFKey; ++ assert( IsOrdinaryTable(p) ); ++ pFKey->pNextFrom = p->u.tab.pFKey; + z = (char*)&pFKey->aCol[nCol]; + pFKey->zTo = z; + if( IN_RENAME_OBJECT ){ +@@ -113619,7 +126665,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( + for(i=0; inCol; j++){ +- if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zEName)==0 ){ ++ if( sqlite3StrICmp(p->aCol[j].zCnName, pFromCol->a[i].zEName)==0 ){ + pFKey->aCol[i].iFrom = j; + break; + } +@@ -113667,7 +126713,8 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( + + /* Link the foreign key to the table as the last step. + */ +- p->pFKey = pFKey; ++ assert( IsOrdinaryTable(p) ); ++ p->u.tab.pFKey = pFKey; + pFKey = 0; + + fk_end: +@@ -113688,7 +126735,9 @@ SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ + #ifndef SQLITE_OMIT_FOREIGN_KEY + Table *pTab; + FKey *pFKey; +- if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; ++ if( (pTab = pParse->pNewTable)==0 ) return; ++ if( NEVER(!IsOrdinaryTable(pTab)) ) return; ++ if( (pFKey = pTab->u.tab.pFKey)==0 ) return; + assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */ + pFKey->isDeferred = (u8)isDeferred; + #endif +@@ -113738,7 +126787,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ + tnum = pIndex->tnum; + } + pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); +- assert( pKey!=0 || db->mallocFailed || pParse->nErr ); ++ assert( pKey!=0 || pParse->nErr ); + + /* Open the sorter cursor if we are to use one. */ + iSorter = pParse->nTab++; +@@ -113789,7 +126838,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ + ** not work for UNIQUE constraint indexes on WITHOUT ROWID tables + ** with DESC primary keys, since those indexes have there keys in + ** a different order from the main table. +- ** See ticket: https://www.sqlite.org/src/info/bba7b69f9849b5bf ++ ** See ticket: https://sqlite.org/src/info/bba7b69f9849b5bf + */ + sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx); + } +@@ -113813,13 +126862,14 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ + */ + SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( + sqlite3 *db, /* Database connection */ +- i16 nCol, /* Total number of columns in the index */ ++ int nCol, /* Total number of columns in the index */ + int nExtra, /* Number of bytes of extra space to alloc */ + char **ppExtra /* Pointer to the "extra" space */ + ){ + Index *p; /* Allocated index object */ +- int nByte; /* Bytes of space for Index object + arrays */ ++ i64 nByte; /* Bytes of space for Index object + arrays */ + ++ assert( nCol <= 2*db->aLimit[SQLITE_LIMIT_COLUMN] ); + nByte = ROUND8(sizeof(Index)) + /* Index structure */ + ROUND8(sizeof(char*)*nCol) + /* Index.azColl */ + ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */ +@@ -113832,8 +126882,9 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( + p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1); + p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol; + p->aSortOrder = (u8*)pExtra; +- p->nColumn = nCol; +- p->nKeyCol = nCol - 1; ++ assert( nCol>0 ); ++ p->nColumn = (u16)nCol; ++ p->nKeyCol = (u16)(nCol - 1); + *ppExtra = ((char*)p) + nByte; + } + return p; +@@ -113848,8 +126899,8 @@ SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){ + if( pList ){ + int i; + for(i=0; inExpr; i++){ +- if( pList->a[i].bNulls ){ +- u8 sf = pList->a[i].sortFlags; ++ if( pList->a[i].fg.bNulls ){ ++ u8 sf = pList->a[i].fg.sortFlags; + sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s", + (sf==0 || sf==3) ? "FIRST" : "LAST" + ); +@@ -113902,9 +126953,11 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + char *zExtra = 0; /* Extra space after the Index object */ + Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ + +- if( db->mallocFailed || pParse->nErr>0 ){ ++ assert( db->pParse==pParse ); ++ if( pParse->nErr ){ + goto exit_create_index; + } ++ assert( db->mallocFailed==0 ); + if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){ + goto exit_create_index; + } +@@ -113932,7 +126985,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + #ifndef SQLITE_OMIT_TEMPDB + /* If the index name was unqualified, check if the table + ** is a temp table. If so, set the database to 1. Do not do this +- ** if initialising a database schema. ++ ** if initializing a database schema. + */ + if( !db->init.busy ){ + pTab = sqlite3SrcListLookup(pParse, pTblName); +@@ -113968,19 +127021,15 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + pDb = &db->aDb[iDb]; + + assert( pTab!=0 ); +- assert( pParse->nErr==0 ); + if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 + && db->init.busy==0 + && pTblName!=0 +-#if SQLITE_USER_AUTHENTICATION +- && sqlite3UserAuthTable(pTab->zName)==0 +-#endif + ){ + sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); + goto exit_create_index; + } + #ifndef SQLITE_OMIT_VIEW +- if( pTab->pSelect ){ ++ if( IsView(pTab) ){ + sqlite3ErrorMsg(pParse, "views may not be indexed"); + goto exit_create_index; + } +@@ -114014,7 +127063,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + } + if( !IN_RENAME_OBJECT ){ + if( !db->init.busy ){ +- if( sqlite3FindTable(db, zName, 0)!=0 ){ ++ if( sqlite3FindTable(db, zName, pDb->zDbSName)!=0 ){ + sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); + goto exit_create_index; + } +@@ -114025,6 +127074,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); ++ sqlite3ForceNotReadOnly(pParse); + } + goto exit_create_index; + } +@@ -114070,7 +127120,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + Token prevCol; + Column *pCol = &pTab->aCol[pTab->nCol-1]; + pCol->colFlags |= COLFLAG_UNIQUE; +- sqlite3TokenInit(&prevCol, pCol->zName); ++ sqlite3TokenInit(&prevCol, pCol->zCnName); + pList = sqlite3ExprListAppend(pParse, 0, + sqlite3ExprAlloc(db, TK_ID, &prevCol, 0)); + if( pList==0 ) goto exit_create_index; +@@ -114088,6 +127138,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + Expr *pExpr = pList->a[i].pExpr; + assert( pExpr!=0 ); + if( pExpr->op==TK_COLLATE ){ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken)); + } + } +@@ -114165,6 +127216,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + j = XN_EXPR; + pIndex->aiColumn[i] = XN_EXPR; + pIndex->uniqNotNull = 0; ++ pIndex->bHasExpr = 1; + }else{ + j = pCExpr->iColumn; + assert( j<=0x7fff ); +@@ -114176,6 +127228,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + } + if( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ){ + pIndex->bHasVCol = 1; ++ pIndex->bHasExpr = 1; + } + } + pIndex->aiColumn[i] = (i16)j; +@@ -114183,6 +127236,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + zColl = 0; + if( pListItem->pExpr->op==TK_COLLATE ){ + int nColl; ++ assert( !ExprHasProperty(pListItem->pExpr, EP_IntValue) ); + zColl = pListItem->pExpr->u.zToken; + nColl = sqlite3Strlen30(zColl) + 1; + assert( nExtra>=nColl ); +@@ -114191,14 +127245,14 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + zExtra += nColl; + nExtra -= nColl; + }else if( j>=0 ){ +- zColl = pTab->aCol[j].zColl; ++ zColl = sqlite3ColumnColl(&pTab->aCol[j]); + } + if( !zColl ) zColl = sqlite3StrBINARY; + if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){ + goto exit_create_index; + } + pIndex->azColl[i] = zColl; +- requestedSortOrder = pListItem->sortFlags & sortOrderMask; ++ requestedSortOrder = pListItem->fg.sortFlags & sortOrderMask; + pIndex->aSortOrder[i] = (u8)requestedSortOrder; + } + +@@ -114389,13 +127443,13 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + /* Add an entry in sqlite_schema for this index + */ + sqlite3NestedParse(pParse, +- "INSERT INTO %Q." DFLT_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", +- db->aDb[iDb].zDbSName, +- pIndex->zName, +- pTab->zName, +- iMem, +- zStmt +- ); ++ "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", ++ db->aDb[iDb].zDbSName, ++ pIndex->zName, ++ pTab->zName, ++ iMem, ++ zStmt ++ ); + sqlite3DbFree(db, zStmt); + + /* Fill the index with data and reparse the schema. Code an OP_Expire +@@ -114405,7 +127459,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + sqlite3RefillIndex(pParse, pIndex, iMem); + sqlite3ChangeCookie(pParse, iDb); + sqlite3VdbeAddParseSchemaOp(v, iDb, +- sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); ++ sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), 0); + sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); + } + +@@ -114426,8 +127480,12 @@ SQLITE_PRIVATE void sqlite3CreateIndex( + /* Clean up before exiting */ + exit_create_index: + if( pIndex ) sqlite3FreeIndex(db, pIndex); +- if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */ +- Index **ppFrom = &pTab->pIndex; ++ if( pTab ){ ++ /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list. ++ ** The list was already ordered when this routine was entered, so at this ++ ** point at most a single index (the newly added index) will be out of ++ ** order. So we have to reorder at most one index. */ ++ Index **ppFrom; + Index *pThis; + for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ + Index *pNext; +@@ -114440,6 +127498,16 @@ exit_create_index: + } + break; + } ++#ifdef SQLITE_DEBUG ++ /* Verify that all REPLACE indexes really are now at the end ++ ** of the index list. In other words, no other index type ever ++ ** comes after a REPLACE index on the list. */ ++ for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){ ++ assert( pThis->onError!=OE_Replace ++ || pThis->pNext==0 ++ || pThis->pNext->onError==OE_Replace ); ++ } ++#endif + } + sqlite3ExprDelete(db, pPIWhere); + sqlite3ExprListDelete(db, pList); +@@ -114491,7 +127559,7 @@ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ + if( x<99 ){ + pIdx->pTable->nRowLogEst = x = 99; + } +- if( pIdx->pPartIdxWhere!=0 ) x -= 10; assert( 10==sqlite3LogEst(2) ); ++ if( pIdx->pPartIdxWhere!=0 ){ x -= 10; assert( 10==sqlite3LogEst(2) ); } + a[0] = x; + + /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is +@@ -114515,20 +127583,23 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists + sqlite3 *db = pParse->db; + int iDb; + +- assert( pParse->nErr==0 ); /* Never called with prior errors */ + if( db->mallocFailed ){ + goto exit_drop_index; + } ++ assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ + assert( pName->nSrc==1 ); ++ assert( pName->a[0].fg.fixedSchema==0 ); ++ assert( pName->a[0].fg.isSubquery==0 ); + if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ + goto exit_drop_index; + } +- pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); ++ pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].u4.zDatabase); + if( pIndex==0 ){ + if( !ifExists ){ +- sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); ++ sqlite3ErrorMsg(pParse, "no such index: %S", pName->a); + }else{ +- sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); ++ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); ++ sqlite3ForceNotReadOnly(pParse); + } + pParse->checkSchema = 1; + goto exit_drop_index; +@@ -114548,7 +127619,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists + if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ + goto exit_drop_index; + } +- if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; ++ if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; + if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ + goto exit_drop_index; + } +@@ -114560,7 +127631,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists + if( v ){ + sqlite3BeginWriteOperation(pParse, 1, iDb); + sqlite3NestedParse(pParse, +- "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='index'", ++ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'", + db->aDb[iDb].zDbSName, pIndex->zName + ); + sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); +@@ -114624,20 +127695,18 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token * + sqlite3 *db = pParse->db; + int i; + if( pList==0 ){ +- pList = sqlite3DbMallocZero(db, sizeof(IdList) ); ++ pList = sqlite3DbMallocZero(db, SZ_IDLIST(1)); + if( pList==0 ) return 0; ++ }else{ ++ IdList *pNew; ++ pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1)); ++ if( pNew==0 ){ ++ sqlite3IdListDelete(db, pList); ++ return 0; ++ } ++ pList = pNew; + } +- pList->a = sqlite3ArrayAllocate( +- db, +- pList->a, +- sizeof(pList->a[0]), +- &pList->nId, +- &i +- ); +- if( i<0 ){ +- sqlite3IdListDelete(db, pList); +- return 0; +- } ++ i = pList->nId++; + pList->a[i].zName = sqlite3NameFromToken(db, pToken); + if( IN_RENAME_OBJECT && pList->a[i].zName ){ + sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken); +@@ -114650,12 +127719,12 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token * + */ + SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ + int i; ++ assert( db!=0 ); + if( pList==0 ) return; + for(i=0; inId; i++){ + sqlite3DbFree(db, pList->a[i].zName); + } +- sqlite3DbFree(db, pList->a); +- sqlite3DbFreeNN(db, pList); ++ sqlite3DbNNFreeNN(db, pList); + } + + /* +@@ -114664,7 +127733,7 @@ SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ + */ + SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){ + int i; +- if( pList==0 ) return -1; ++ assert( pList!=0 ); + for(i=0; inId; i++){ + if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; + } +@@ -114729,8 +127798,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( + return 0; + } + if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; +- pNew = sqlite3DbRealloc(db, pSrc, +- sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); ++ pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc)); + if( pNew==0 ){ + assert( db->mallocFailed ); + return 0; +@@ -114798,14 +127866,14 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( + Token *pTable, /* Table to append */ + Token *pDatabase /* Database of the table */ + ){ +- struct SrcList_item *pItem; ++ SrcItem *pItem; + sqlite3 *db; + assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ + assert( pParse!=0 ); + assert( pParse->db!=0 ); + db = pParse->db; + if( pList==0 ){ +- pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); ++ pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1)); + if( pList==0 ) return 0; + pList->nAlloc = 1; + pList->nSrc = 1; +@@ -114824,12 +127892,14 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( + if( pDatabase && pDatabase->z==0 ){ + pDatabase = 0; + } ++ assert( pItem->fg.fixedSchema==0 ); ++ assert( pItem->fg.isSubquery==0 ); + if( pDatabase ){ + pItem->zName = sqlite3NameFromToken(db, pDatabase); +- pItem->zDatabase = sqlite3NameFromToken(db, pTable); ++ pItem->u4.zDatabase = sqlite3NameFromToken(db, pTable); + }else{ + pItem->zName = sqlite3NameFromToken(db, pTable); +- pItem->zDatabase = 0; ++ pItem->u4.zDatabase = 0; + } + return pList; + } +@@ -114839,40 +127909,130 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( + */ + SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ + int i; +- struct SrcList_item *pItem; +- assert(pList || pParse->db->mallocFailed ); +- if( pList ){ ++ SrcItem *pItem; ++ assert( pList || pParse->db->mallocFailed ); ++ if( ALWAYS(pList) ){ + for(i=0, pItem=pList->a; inSrc; i++, pItem++){ +- if( pItem->iCursor>=0 ) break; ++ if( pItem->iCursor>=0 ) continue; + pItem->iCursor = pParse->nTab++; +- if( pItem->pSelect ){ +- sqlite3SrcListAssignCursors(pParse, pItem->pSelect->pSrc); ++ if( pItem->fg.isSubquery ){ ++ assert( pItem->u4.pSubq!=0 ); ++ assert( pItem->u4.pSubq->pSelect!=0 ); ++ assert( pItem->u4.pSubq->pSelect->pSrc!=0 ); ++ sqlite3SrcListAssignCursors(pParse, pItem->u4.pSubq->pSelect->pSrc); + } + } + } + } + ++/* ++** Delete a Subquery object and its substructure. ++*/ ++SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3 *db, Subquery *pSubq){ ++ assert( pSubq!=0 && pSubq->pSelect!=0 ); ++ sqlite3SelectDelete(db, pSubq->pSelect); ++ sqlite3DbFree(db, pSubq); ++} ++ ++/* ++** Remove a Subquery from a SrcItem. Return the associated Select object. ++** The returned Select becomes the responsibility of the caller. ++*/ ++SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3 *db, SrcItem *pItem){ ++ Select *pSel; ++ assert( pItem!=0 ); ++ assert( pItem->fg.isSubquery ); ++ pSel = pItem->u4.pSubq->pSelect; ++ sqlite3DbFree(db, pItem->u4.pSubq); ++ pItem->u4.pSubq = 0; ++ pItem->fg.isSubquery = 0; ++ return pSel; ++} ++ + /* + ** Delete an entire SrcList including all its substructure. + */ + SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ + int i; +- struct SrcList_item *pItem; ++ SrcItem *pItem; ++ assert( db!=0 ); + if( pList==0 ) return; + for(pItem=pList->a, i=0; inSrc; i++, pItem++){ +- sqlite3DbFree(db, pItem->zDatabase); +- sqlite3DbFree(db, pItem->zName); +- sqlite3DbFree(db, pItem->zAlias); ++ ++ /* Check invariants on SrcItem */ ++ assert( !pItem->fg.isIndexedBy || !pItem->fg.isTabFunc ); ++ assert( !pItem->fg.isCte || !pItem->fg.isIndexedBy ); ++ assert( !pItem->fg.fixedSchema || !pItem->fg.isSubquery ); ++ assert( !pItem->fg.isSubquery || (pItem->u4.pSubq!=0 && ++ pItem->u4.pSubq->pSelect!=0) ); ++ ++ if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName); ++ if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias); ++ if( pItem->fg.isSubquery ){ ++ sqlite3SubqueryDelete(db, pItem->u4.pSubq); ++ }else if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ ++ sqlite3DbNNFreeNN(db, pItem->u4.zDatabase); ++ } + if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy); + if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); +- sqlite3DeleteTable(db, pItem->pTab); +- sqlite3SelectDelete(db, pItem->pSelect); +- sqlite3ExprDelete(db, pItem->pOn); +- sqlite3IdListDelete(db, pItem->pUsing); ++ sqlite3DeleteTable(db, pItem->pSTab); ++ if( pItem->fg.isUsing ){ ++ sqlite3IdListDelete(db, pItem->u3.pUsing); ++ }else if( pItem->u3.pOn ){ ++ sqlite3ExprDelete(db, pItem->u3.pOn); ++ } + } +- sqlite3DbFreeNN(db, pList); ++ sqlite3DbNNFreeNN(db, pList); + } + ++/* ++** Attach a Subquery object to pItem->uv.pSubq. Set the ++** pSelect value but leave all the other values initialized ++** to zero. ++** ++** A copy of the Select object is made if dupSelect is true, and the ++** SrcItem takes responsibility for deleting the copy. If dupSelect is ++** false, ownership of the Select passes to the SrcItem. Either way, ++** the SrcItem will take responsibility for deleting the Select. ++** ++** When dupSelect is zero, that means the Select might get deleted right ++** away if there is an OOM error. Beware. ++** ++** Return non-zero on success. Return zero on an OOM error. ++*/ ++SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery( ++ Parse *pParse, /* Parsing context */ ++ SrcItem *pItem, /* Item to which the subquery is to be attached */ ++ Select *pSelect, /* The subquery SELECT. Must be non-NULL */ ++ int dupSelect /* If true, attach a copy of pSelect, not pSelect itself.*/ ++){ ++ Subquery *p; ++ assert( pSelect!=0 ); ++ assert( pItem->fg.isSubquery==0 ); ++ if( pItem->fg.fixedSchema ){ ++ pItem->u4.pSchema = 0; ++ pItem->fg.fixedSchema = 0; ++ }else if( pItem->u4.zDatabase!=0 ){ ++ sqlite3DbFree(pParse->db, pItem->u4.zDatabase); ++ pItem->u4.zDatabase = 0; ++ } ++ if( dupSelect ){ ++ pSelect = sqlite3SelectDup(pParse->db, pSelect, 0); ++ if( pSelect==0 ) return 0; ++ } ++ p = pItem->u4.pSubq = sqlite3DbMallocRawNN(pParse->db, sizeof(Subquery)); ++ if( p==0 ){ ++ sqlite3SelectDelete(pParse->db, pSelect); ++ return 0; ++ } ++ pItem->fg.isSubquery = 1; ++ p->pSelect = pSelect; ++ assert( offsetof(Subquery, pSelect)==0 ); ++ memset(((char*)p)+sizeof(p->pSelect), 0, sizeof(*p)-sizeof(p->pSelect)); ++ return 1; ++} ++ ++ + /* + ** This routine is called by the parser to add a new term to the + ** end of a growing FROM clause. The "p" parameter is the part of +@@ -114896,14 +128056,13 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( + Token *pDatabase, /* Name of the database containing pTable */ + Token *pAlias, /* The right-hand side of the AS subexpression */ + Select *pSubquery, /* A subquery used in place of a table name */ +- Expr *pOn, /* The ON clause of a join */ +- IdList *pUsing /* The USING clause of a join */ ++ OnOrUsing *pOnUsing /* Either the ON clause or the USING clause */ + ){ +- struct SrcList_item *pItem; ++ SrcItem *pItem; + sqlite3 *db = pParse->db; +- if( !p && (pOn || pUsing) ){ ++ if( !p && pOnUsing!=0 && (pOnUsing->pOn || pOnUsing->pUsing) ){ + sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", +- (pOn ? "ON" : "USING") ++ (pOnUsing->pOn ? "ON" : "USING") + ); + goto append_from_error; + } +@@ -114923,15 +128082,29 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( + if( pAlias->n ){ + pItem->zAlias = sqlite3NameFromToken(db, pAlias); + } +- pItem->pSelect = pSubquery; +- pItem->pOn = pOn; +- pItem->pUsing = pUsing; ++ assert( pSubquery==0 || pDatabase==0 ); ++ if( pSubquery ){ ++ if( sqlite3SrcItemAttachSubquery(pParse, pItem, pSubquery, 0) ){ ++ if( pSubquery->selFlags & SF_NestedFrom ){ ++ pItem->fg.isNestedFrom = 1; ++ } ++ } ++ } ++ assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 ); ++ assert( pItem->fg.isUsing==0 ); ++ if( pOnUsing==0 ){ ++ pItem->u3.pOn = 0; ++ }else if( pOnUsing->pUsing ){ ++ pItem->fg.isUsing = 1; ++ pItem->u3.pUsing = pOnUsing->pUsing; ++ }else{ ++ pItem->u3.pOn = pOnUsing->pOn; ++ } + return p; + +- append_from_error: ++append_from_error: + assert( p==0 ); +- sqlite3ExprDelete(db, pOn); +- sqlite3IdListDelete(db, pUsing); ++ sqlite3ClearOnOrUsing(db, pOnUsing); + sqlite3SelectDelete(db, pSubquery); + return 0; + } +@@ -114943,7 +128116,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( + SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ + assert( pIndexedBy!=0 ); + if( p && pIndexedBy->n>0 ){ +- struct SrcList_item *pItem; ++ SrcItem *pItem; + assert( p->nSrc>0 ); + pItem = &p->a[p->nSrc-1]; + assert( pItem->fg.notIndexed==0 ); +@@ -114956,6 +128129,7 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI + }else{ + pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy); + pItem->fg.isIndexedBy = 1; ++ assert( pItem->fg.isCte==0 ); /* No collision on union u2 */ + } + } + } +@@ -114973,8 +128147,9 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, Src + sqlite3SrcListDelete(pParse->db, p2); + }else{ + p1 = pNew; +- memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item)); ++ memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem)); + sqlite3DbFree(pParse->db, p2); ++ p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype); + } + } + return p1; +@@ -114986,7 +128161,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, Src + */ + SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){ + if( p ){ +- struct SrcList_item *pItem = &p->a[p->nSrc-1]; ++ SrcItem *pItem = &p->a[p->nSrc-1]; + assert( pItem->fg.notIndexed==0 ); + assert( pItem->fg.isIndexedBy==0 ); + assert( pItem->fg.isTabFunc==0 ); +@@ -115011,14 +128186,34 @@ SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList * + ** The operator is "natural cross join". The A and B operands are stored + ** in p->a[0] and p->a[1], respectively. The parser initially stores the + ** operator with A. This routine shifts that operator over to B. ++** ++** Additional changes: ++** ++** * All tables to the left of the right-most RIGHT JOIN are tagged with ++** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the ++** code generator can easily tell that the table is part of ++** the left operand of at least one RIGHT JOIN. + */ +-SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){ +- if( p ){ +- int i; +- for(i=p->nSrc-1; i>0; i--){ +- p->a[i].fg.jointype = p->a[i-1].fg.jointype; +- } ++SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse *pParse, SrcList *p){ ++ (void)pParse; ++ if( p && p->nSrc>1 ){ ++ int i = p->nSrc-1; ++ u8 allFlags = 0; ++ do{ ++ allFlags |= p->a[i].fg.jointype = p->a[i-1].fg.jointype; ++ }while( (--i)>0 ); + p->a[0].fg.jointype = 0; ++ ++ /* All terms to the left of a RIGHT JOIN should be tagged with the ++ ** JT_LTORJ flags */ ++ if( allFlags & JT_RIGHT ){ ++ for(i=p->nSrc-1; ALWAYS(i>0) && (p->a[i].fg.jointype&JT_RIGHT)==0; i--){} ++ i--; ++ assert( i>=0 ); ++ do{ ++ p->a[i].fg.jointype |= JT_LTORJ; ++ }while( (--i)>=0 ); ++ } + } + } + +@@ -115040,7 +128235,16 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){ + if( !v ) return; + if( type!=TK_DEFERRED ){ + for(i=0; inDb; i++){ +- sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); ++ int eTxnType; ++ Btree *pBt = db->aDb[i].pBt; ++ if( pBt && sqlite3BtreeIsReadonly(pBt) ){ ++ eTxnType = 0; /* Read txn */ ++ }else if( type==TK_EXCLUSIVE ){ ++ eTxnType = 2; /* Exclusive txn */ ++ }else{ ++ eTxnType = 1; /* Write txn */ ++ } ++ sqlite3VdbeAddOp2(v, OP_Transaction, i, eTxnType); + sqlite3VdbeUsesBtree(v, i); + } + } +@@ -115129,13 +128333,11 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){ + ** will occur at the end of the top-level VDBE and will be generated + ** later, by sqlite3FinishCoding(). + */ +-SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ +- Parse *pToplevel = sqlite3ParseToplevel(pParse); +- +- assert( iDb>=0 && iDbdb->nDb ); +- assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 ); +- assert( iDbdb, iDb, 0) ); ++static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){ ++ assert( iDb>=0 && iDbdb->nDb ); ++ assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 ); ++ assert( iDbdb, iDb, 0) ); + if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){ + DbMaskSet(pToplevel->cookieMask, iDb); + if( !OMIT_TEMPDB && iDb==1 ){ +@@ -115143,6 +128345,10 @@ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ + } + } + } ++SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ ++ sqlite3CodeVerifySchemaAtToplevel(sqlite3ParseToplevel(pParse), iDb); ++} ++ + + /* + ** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each +@@ -115174,7 +128380,7 @@ SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb) + */ + SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); +- sqlite3CodeVerifySchema(pParse, iDb); ++ sqlite3CodeVerifySchemaAtToplevel(pToplevel, iDb); + DbMaskSet(pToplevel->writeMask, iDb); + pToplevel->isMultiWrite |= setStatement; + } +@@ -115225,7 +128431,9 @@ SQLITE_PRIVATE void sqlite3HaltConstraint( + i8 p4type, /* P4_STATIC or P4_TRANSIENT */ + u8 p5Errmsg /* P5_ErrMsg type */ + ){ +- Vdbe *v = sqlite3GetVdbe(pParse); ++ Vdbe *v; ++ assert( pParse->pVdbe!=0 ); ++ v = sqlite3GetVdbe(pParse); + assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested ); + if( onError==OE_Abort ){ + sqlite3MayAbort(pParse); +@@ -115255,7 +128463,7 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint( + for(j=0; jnKeyCol; j++){ + char *zCol; + assert( pIdx->aiColumn[j]>=0 ); +- zCol = pTab->aCol[pIdx->aiColumn[j]].zName; ++ zCol = pTab->aCol[pIdx->aiColumn[j]].zCnName; + if( j ) sqlite3_str_append(&errMsg, ", ", 2); + sqlite3_str_appendall(&errMsg, pTab->zName); + sqlite3_str_append(&errMsg, ".", 1); +@@ -115282,7 +128490,7 @@ SQLITE_PRIVATE void sqlite3RowidConstraint( + int rc; + if( pTab->iPKey>=0 ){ + zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName, +- pTab->aCol[pTab->iPKey].zName); ++ pTab->aCol[pTab->iPKey].zCnName); + rc = SQLITE_CONSTRAINT_PRIMARYKEY; + }else{ + zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName); +@@ -115405,7 +128613,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ + if( iDb<0 ) return; + z = sqlite3NameFromToken(db, pObjName); + if( z==0 ) return; +- zDb = db->aDb[iDb].zDbSName; ++ zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; + pTab = sqlite3FindTable(db, z, zDb); + if( pTab ){ + reindexTable(pParse, pTab, 0); +@@ -115415,6 +128623,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ + pIndex = sqlite3FindIndex(db, z, zDb); + sqlite3DbFree(db, z); + if( pIndex ){ ++ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema); + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3RefillIndex(pParse, pIndex, -1); + return; +@@ -115470,24 +128679,76 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ + } + + #ifndef SQLITE_OMIT_CTE ++/* ++** Create a new CTE object ++*/ ++SQLITE_PRIVATE Cte *sqlite3CteNew( ++ Parse *pParse, /* Parsing context */ ++ Token *pName, /* Name of the common-table */ ++ ExprList *pArglist, /* Optional column name list for the table */ ++ Select *pQuery, /* Query used to initialize the table */ ++ u8 eM10d /* The MATERIALIZED flag */ ++){ ++ Cte *pNew; ++ sqlite3 *db = pParse->db; ++ ++ pNew = sqlite3DbMallocZero(db, sizeof(*pNew)); ++ assert( pNew!=0 || db->mallocFailed ); ++ ++ if( db->mallocFailed ){ ++ sqlite3ExprListDelete(db, pArglist); ++ sqlite3SelectDelete(db, pQuery); ++ }else{ ++ pNew->pSelect = pQuery; ++ pNew->pCols = pArglist; ++ pNew->zName = sqlite3NameFromToken(pParse->db, pName); ++ pNew->eM10d = eM10d; ++ } ++ return pNew; ++} ++ ++/* ++** Clear information from a Cte object, but do not deallocate storage ++** for the object itself. ++*/ ++static void cteClear(sqlite3 *db, Cte *pCte){ ++ assert( pCte!=0 ); ++ sqlite3ExprListDelete(db, pCte->pCols); ++ sqlite3SelectDelete(db, pCte->pSelect); ++ sqlite3DbFree(db, pCte->zName); ++} ++ ++/* ++** Free the contents of the CTE object passed as the second argument. ++*/ ++SQLITE_PRIVATE void sqlite3CteDelete(sqlite3 *db, Cte *pCte){ ++ assert( pCte!=0 ); ++ cteClear(db, pCte); ++ sqlite3DbFree(db, pCte); ++} ++ + /* + ** This routine is invoked once per CTE by the parser while parsing a +-** WITH clause. ++** WITH clause. The CTE described by the third argument is added to ++** the WITH clause of the second argument. If the second argument is ++** NULL, then a new WITH argument is created. + */ + SQLITE_PRIVATE With *sqlite3WithAdd( + Parse *pParse, /* Parsing context */ + With *pWith, /* Existing WITH clause, or NULL */ +- Token *pName, /* Name of the common-table */ +- ExprList *pArglist, /* Optional column name list for the table */ +- Select *pQuery /* Query used to initialize the table */ ++ Cte *pCte /* CTE to add to the WITH clause */ + ){ + sqlite3 *db = pParse->db; + With *pNew; + char *zName; + ++ if( pCte==0 ){ ++ return pWith; ++ } ++ + /* Check that the CTE name is unique within this WITH clause. If + ** not, store an error in the Parse structure. */ +- zName = sqlite3NameFromToken(pParse->db, pName); ++ zName = pCte->zName; + if( zName && pWith ){ + int i; + for(i=0; inCte; i++){ +@@ -115498,24 +128759,18 @@ SQLITE_PRIVATE With *sqlite3WithAdd( + } + + if( pWith ){ +- sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); +- pNew = sqlite3DbRealloc(db, pWith, nByte); ++ pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1)); + }else{ +- pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); ++ pNew = sqlite3DbMallocZero(db, SZ_WITH(1)); + } + assert( (pNew!=0 && zName!=0) || db->mallocFailed ); + + if( db->mallocFailed ){ +- sqlite3ExprListDelete(db, pArglist); +- sqlite3SelectDelete(db, pQuery); +- sqlite3DbFree(db, zName); ++ sqlite3CteDelete(db, pCte); + pNew = pWith; + }else{ +- pNew->a[pNew->nCte].pSelect = pQuery; +- pNew->a[pNew->nCte].pCols = pArglist; +- pNew->a[pNew->nCte].zName = zName; +- pNew->a[pNew->nCte].zCteErr = 0; +- pNew->nCte++; ++ pNew->a[pNew->nCte++] = *pCte; ++ sqlite3DbFree(db, pCte); + } + + return pNew; +@@ -115528,14 +128783,14 @@ SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){ + if( pWith ){ + int i; + for(i=0; inCte; i++){ +- struct Cte *pCte = &pWith->a[i]; +- sqlite3ExprListDelete(db, pCte->pCols); +- sqlite3SelectDelete(db, pCte->pSelect); +- sqlite3DbFree(db, pCte->zName); ++ cteClear(db, &pWith->a[i]); + } + sqlite3DbFree(db, pWith); + } + } ++SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){ ++ sqlite3WithDelete(db, (With*)pWith); ++} + #endif /* !defined(SQLITE_OMIT_CTE) */ + + /************** End of build.c ***********************************************/ +@@ -115727,6 +128982,7 @@ SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){ + ** strings is BINARY. + */ + db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0); ++ sqlite3ExpirePreparedStatements(db, 1); + } + + /* +@@ -115843,12 +129099,18 @@ static int matchQuality( + u8 enc /* Desired text encoding */ + ){ + int match; +- assert( p->nArg>=-1 ); ++ assert( p->nArg>=(-4) && p->nArg!=(-2) ); ++ assert( nArg>=(-2) ); + + /* Wrong number of arguments means "no match" */ + if( p->nArg!=nArg ){ +- if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH; ++ if( nArg==(-2) ) return p->xSFunc==0 ? 0 : FUNC_PERFECT_MATCH; + if( p->nArg>=0 ) return 0; ++ /* Special p->nArg values available to built-in functions only: ++ ** -3 1 or more arguments required ++ ** -4 2 or more arguments required ++ */ ++ if( p->nArg<(-2) && nArg<(-2-p->nArg) ) return 0; + } + + /* Give a better score to a function with a specific number of arguments +@@ -115879,6 +129141,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch( + ){ + FuncDef *p; + for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ ++ assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); + if( sqlite3StrICmp(p->zName, zFunc)==0 ){ + return p; + } +@@ -115899,7 +129162,7 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs( + const char *zName = aDef[i].zName; + int nName = sqlite3Strlen30(zName); + int h = SQLITE_FUNC_HASH(zName[0], nName); +- assert( zName[0]>='a' && zName[0]<='z' ); ++ assert( aDef[i].funcFlags & SQLITE_FUNC_BUILTIN ); + pOther = sqlite3FunctionSearch(h, zName); + if( pOther ){ + assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] ); +@@ -116031,19 +129294,21 @@ SQLITE_PRIVATE void sqlite3SchemaClear(void *p){ + Hash temp2; + HashElem *pElem; + Schema *pSchema = (Schema *)p; ++ sqlite3 xdb; + ++ memset(&xdb, 0, sizeof(xdb)); + temp1 = pSchema->tblHash; + temp2 = pSchema->trigHash; + sqlite3HashInit(&pSchema->trigHash); + sqlite3HashClear(&pSchema->idxHash); + for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ +- sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem)); ++ sqlite3DeleteTrigger(&xdb, (Trigger*)sqliteHashData(pElem)); + } + sqlite3HashClear(&temp2); + sqlite3HashInit(&pSchema->tblHash); + for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ + Table *pTab = sqliteHashData(pElem); +- sqlite3DeleteTable(0, pTab); ++ sqlite3DeleteTable(&xdb, pTab); + } + sqlite3HashClear(&temp1); + sqlite3HashClear(&pSchema->fkeyHash); +@@ -116105,26 +129370,37 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ + ** + ** The following fields are initialized appropriate in pSrc: + ** +-** pSrc->a[0].pTab Pointer to the Table object +-** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one ++** pSrc->a[0].spTab Pointer to the Table object ++** pSrc->a[0].u2.pIBIndex Pointer to the INDEXED BY index, if there is one + ** + */ + SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ +- struct SrcList_item *pItem = pSrc->a; ++ SrcItem *pItem = pSrc->a; + Table *pTab; + assert( pItem && pSrc->nSrc>=1 ); + pTab = sqlite3LocateTableItem(pParse, 0, pItem); +- sqlite3DeleteTable(pParse->db, pItem->pTab); +- pItem->pTab = pTab; ++ if( pItem->pSTab ) sqlite3DeleteTable(pParse->db, pItem->pSTab); ++ pItem->pSTab = pTab; ++ pItem->fg.notCte = 1; + if( pTab ){ + pTab->nTabRef++; +- } +- if( sqlite3IndexedByLookup(pParse, pItem) ){ +- pTab = 0; ++ if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){ ++ pTab = 0; ++ } + } + return pTab; + } + ++/* Generate byte-code that will report the number of rows modified ++** by a DELETE, INSERT, or UPDATE statement. ++*/ ++SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *zColName){ ++ sqlite3VdbeAddOp0(v, OP_FkCheck); ++ sqlite3VdbeAddOp2(v, OP_ResultRow, regCounter, 1); ++ sqlite3VdbeSetNumCols(v, 1); ++ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, SQLITE_STATIC); ++} ++ + /* Return true if table pTab is read-only. + ** + ** A table is read-only if any of the following are true: +@@ -116132,18 +129408,43 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ + ** 1) It is a virtual table and no implementation of the xUpdate method + ** has been provided + ** +-** 2) It is a system table (i.e. sqlite_schema), this call is not ++** 2) A trigger is currently being coded and the table is a virtual table ++** that is SQLITE_VTAB_DIRECTONLY or if PRAGMA trusted_schema=OFF and ++** the table is not SQLITE_VTAB_INNOCUOUS. ++** ++** 3) It is a system table (i.e. sqlite_schema), this call is not + ** part of a nested parse and writable_schema pragma has not + ** been specified + ** +-** 3) The table is a shadow table, the database connection is in ++** 4) The table is a shadow table, the database connection is in + ** defensive mode, and the current sqlite3_prepare() + ** is for a top-level SQL statement. + */ ++static int vtabIsReadOnly(Parse *pParse, Table *pTab){ ++ assert( IsVirtual(pTab) ); ++ if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ ++ return 1; ++ } ++ ++ /* Within triggers: ++ ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY ++ ** virtual tables ++ ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS ++ ** virtual tables if PRAGMA trusted_schema=ON. ++ */ ++ if( pParse->pToplevel!=0 ++ && pTab->u.vtab.p->eVtabRisk > ++ ((pParse->db->flags & SQLITE_TrustedSchema)!=0) ++ ){ ++ sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", ++ pTab->zName); ++ } ++ return 0; ++} + static int tabIsReadOnly(Parse *pParse, Table *pTab){ + sqlite3 *db; + if( IsVirtual(pTab) ){ +- return sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0; ++ return vtabIsReadOnly(pParse, pTab); + } + if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0; + db = pParse->db; +@@ -116155,17 +129456,21 @@ static int tabIsReadOnly(Parse *pParse, Table *pTab){ + } + + /* +-** Check to make sure the given table is writable. If it is not +-** writable, generate an error message and return 1. If it is +-** writable return 0; ++** Check to make sure the given table is writable. ++** ++** If pTab is not writable -> generate an error message and return 1. ++** If pTab is writable but other errors have occurred -> return 1. ++** If pTab is writable and no prior errors -> return 0; + */ +-SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ ++SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, Trigger *pTrigger){ + if( tabIsReadOnly(pParse, pTab) ){ + sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); + return 1; + } + #ifndef SQLITE_OMIT_VIEW +- if( !viewOk && pTab->pSelect ){ ++ if( IsView(pTab) ++ && (pTrigger==0 || (pTrigger->bReturning && pTrigger->pNext==0)) ++ ){ + sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); + return 1; + } +@@ -116198,9 +129503,10 @@ SQLITE_PRIVATE void sqlite3MaterializeView( + if( pFrom ){ + assert( pFrom->nSrc==1 ); + pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); +- pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); +- assert( pFrom->a[0].pOn==0 ); +- assert( pFrom->a[0].pUsing==0 ); ++ assert( pFrom->a[0].fg.fixedSchema==0 && pFrom->a[0].fg.isSubquery==0 ); ++ pFrom->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); ++ assert( pFrom->a[0].fg.isUsing==0 ); ++ assert( pFrom->a[0].u3.pOn==0 ); + } + pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, + SF_IncludeHidden, pLimit); +@@ -116230,7 +129536,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( + sqlite3 *db = pParse->db; + Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */ + Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */ +- ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */ ++ ExprList *pEList = NULL; /* Expression list containing only pSelectRowid*/ + SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */ + Select *pSelect = NULL; /* Complete SELECT tree */ + Table *pTab; +@@ -116260,7 +129566,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( + ** ); + */ + +- pTab = pSrc->a[0].pTab; ++ pTab = pSrc->a[0].pSTab; + if( HasRowid(pTab) ){ + pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0); + pEList = sqlite3ExprListAppend( +@@ -116268,14 +129574,20 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( + ); + }else{ + Index *pPk = sqlite3PrimaryKeyIndex(pTab); ++ assert( pPk!=0 ); ++ assert( pPk->nKeyCol>=1 ); + if( pPk->nKeyCol==1 ){ +- const char *zName = pTab->aCol[pPk->aiColumn[0]].zName; ++ const char *zName; ++ assert( pPk->aiColumn[0]>=0 && pPk->aiColumn[0]nCol ); ++ zName = pTab->aCol[pPk->aiColumn[0]].zCnName; + pLhs = sqlite3Expr(db, TK_ID, zName); + pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName)); + }else{ + int i; + for(i=0; inKeyCol; i++){ +- Expr *p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName); ++ Expr *p; ++ assert( pPk->aiColumn[i]>=0 && pPk->aiColumn[i]nCol ); ++ p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName); + pEList = sqlite3ExprListAppend(pParse, pEList, p); + } + pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); +@@ -116287,17 +129599,24 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( + + /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree + ** and the SELECT subtree. */ +- pSrc->a[0].pTab = 0; +- pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0); +- pSrc->a[0].pTab = pTab; +- pSrc->a[0].pIBIndex = 0; ++ pSrc->a[0].pSTab = 0; ++ pSelectSrc = sqlite3SrcListDup(db, pSrc, 0); ++ pSrc->a[0].pSTab = pTab; ++ if( pSrc->a[0].fg.isIndexedBy ){ ++ assert( pSrc->a[0].fg.isCte==0 ); ++ pSrc->a[0].u2.pIBIndex = 0; ++ pSrc->a[0].fg.isIndexedBy = 0; ++ sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy); ++ }else if( pSrc->a[0].fg.isCte ){ ++ pSrc->a[0].u2.pCteUse->nUse++; ++ } + + /* generate the SELECT expression tree. */ + pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0, + pOrderBy,0,pLimit + ); + +- /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ ++ /* now generate the new WHERE rowid IN clause for the DELETE/UPDATE */ + pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0); + sqlite3PExprAddSelect(pParse, pInClause, pSelect); + return pInClause; +@@ -116357,12 +129676,13 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + + memset(&sContext, 0, sizeof(sContext)); + db = pParse->db; +- if( pParse->nErr || db->mallocFailed ){ ++ assert( db->pParse==pParse ); ++ if( pParse->nErr ){ + goto delete_from_cleanup; + } ++ assert( db->mallocFailed==0 ); + assert( pTabList->nSrc==1 ); + +- + /* Locate the table which we want to delete. This table has to be + ** put in an SrcList structure because some of the subroutines we + ** will be calling are designed to work with multiple tables and expect +@@ -116376,7 +129696,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + */ + #ifndef SQLITE_OMIT_TRIGGER + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); +- isView = pTab->pSelect!=0; ++ isView = IsView(pTab); + #else + # define pTrigger 0 + # define isView 0 +@@ -116387,6 +129707,14 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + # define isView 0 + #endif + ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x10000 ){ ++ sqlite3TreeViewLine(0, "In sqlite3Delete() at %s:%d", __FILE__, __LINE__); ++ sqlite3TreeViewDelete(pParse->pWith, pTabList, pWhere, ++ pOrderBy, pLimit, pTrigger); ++ } ++#endif ++ + #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + if( !isView ){ + pWhere = sqlite3LimitWhere( +@@ -116403,7 +129731,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + goto delete_from_cleanup; + } + +- if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ ++ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ + goto delete_from_cleanup; + } + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); +@@ -116468,6 +129796,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + if( (db->flags & SQLITE_CountRows)!=0 + && !pParse->nested + && !pParse->pTriggerTab ++ && !pParse->bReturning + ){ + memCnt = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); +@@ -116501,18 +129830,22 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + } + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->pSchema==pTab->pSchema ); +- sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); ++ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ ++ sqlite3VdbeAddOp3(v, OP_Clear, pIdx->tnum, iDb, memCnt ? memCnt : -1); ++ }else{ ++ sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); ++ } + } + }else + #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ + { +- u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE; +- if( sNC.ncFlags & NC_VarSelect ) bComplex = 1; ++ u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; ++ if( sNC.ncFlags & NC_Subquery ) bComplex = 1; + wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); + if( HasRowid(pTab) ){ + /* For a rowid table, initialize the RowSet to an empty set */ + pPk = 0; +- nPk = 1; ++ assert( nPk==1 ); + iRowSet = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); + }else{ +@@ -116536,12 +129869,16 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + ** ONEPASS_SINGLE: One-pass approach - at most one row deleted. + ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted. + */ +- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); ++ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1); + if( pWInfo==0 ) goto delete_from_cleanup; + eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); +- assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF ); ++ assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF ++ || OptimizationDisabled(db, SQLITE_OnePass) ); + if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse); ++ if( sqlite3WhereUsesDeferredSeek(pWInfo) ){ ++ sqlite3VdbeAddOp1(v, OP_FinishSeek, iTabCur); ++ } + + /* Keep track of the number of rows to be deleted */ + if( memCnt ){ +@@ -116576,6 +129913,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0; + if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0; + if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen); ++ addrBypass = sqlite3VdbeMakeLabel(pParse); + }else{ + if( pPk ){ + /* Add the PK key for this row to the temporary table */ +@@ -116589,13 +129927,6 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + nKey = 1; /* OP_DeferredSeek always uses a single rowid */ + sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); + } +- } +- +- /* If this DELETE cannot use the ONEPASS strategy, this is the +- ** end of the WHERE loop */ +- if( eOnePass!=ONEPASS_OFF ){ +- addrBypass = sqlite3VdbeMakeLabel(pParse); +- }else{ + sqlite3WhereEnd(pWInfo); + } + +@@ -116625,7 +129956,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + if( eOnePass!=ONEPASS_OFF ){ + assert( nKey==nPk ); /* OP_Found will use an unpacked key */ + if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ +- assert( pPk!=0 || pTab->pSelect!=0 ); ++ assert( pPk!=0 || IsView(pTab) ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); + VdbeCoverage(v); + } +@@ -116692,9 +130023,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( + ** invoke the callback function. + */ + if( memCnt ){ +- sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1); +- sqlite3VdbeSetNumCols(v, 1); +- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); ++ sqlite3CodeChangeCount(v, memCnt, "rows deleted"); + } + + delete_from_cleanup: +@@ -116705,7 +130034,7 @@ delete_from_cleanup: + sqlite3ExprListDelete(db, pOrderBy); + sqlite3ExprDelete(db, pLimit); + #endif +- sqlite3DbFree(db, aToOpen); ++ if( aToOpen ) sqlite3DbNNFreeNN(db, aToOpen); + return; + } + /* Make sure "isView" and other macros defined above are undefined. Otherwise +@@ -116859,7 +130188,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( + ** the update-hook is not invoked for rows removed by REPLACE, but the + ** pre-update-hook is. + */ +- if( pTab->pSelect==0 ){ ++ if( !IsView(pTab) ){ + u8 p5 = 0; + sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); + sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); +@@ -116882,9 +130211,11 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( + sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0); + + /* Invoke AFTER DELETE trigger programs. */ +- sqlite3CodeRowTrigger(pParse, pTrigger, +- TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel +- ); ++ if( pTrigger ){ ++ sqlite3CodeRowTrigger(pParse, pTrigger, ++ TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel ++ ); ++ } + + /* Jump here if the row had already been deleted before any BEFORE + ** trigger programs were invoked. Or if a trigger program throws a +@@ -117016,20 +130347,18 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey( + continue; + } + sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); +- /* If the column affinity is REAL but the number is an integer, then it +- ** might be stored in the table as an integer (using a compact +- ** representation) then converted to REAL by an OP_RealAffinity opcode. +- ** But we are getting ready to store this value back into an index, where +- ** it should be converted by to INTEGER again. So omit the OP_RealAffinity +- ** opcode if it is present */ +- sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); ++ if( pIdx->aiColumn[j]>=0 ){ ++ /* If the column affinity is REAL but the number is an integer, then it ++ ** might be stored in the table as an integer (using a compact ++ ** representation) then converted to REAL by an OP_RealAffinity opcode. ++ ** But we are getting ready to store this value back into an index, where ++ ** it should be converted by to INTEGER again. So omit the ++ ** OP_RealAffinity opcode if it is present */ ++ sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); ++ } + } + if( regOut ){ + sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); +- if( pIdx->pTable->pSelect ){ +- const char *zAff = sqlite3IndexAffinityStr(pParse->db, pIdx); +- sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); +- } + } + sqlite3ReleaseTempRange(pParse, regBase, nCol); + return regBase; +@@ -117147,6 +130476,18 @@ static void typeofFunc( + sqlite3_result_text(context, azType[i], -1, SQLITE_STATIC); + } + ++/* subtype(X) ++** ++** Return the subtype of X ++*/ ++static void subtypeFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ UNUSED_PARAMETER(argc); ++ sqlite3_result_int(context, sqlite3_value_subtype(argv[0])); ++} + + /* + ** Implementation of the length() function +@@ -117187,6 +130528,42 @@ static void lengthFunc( + } + } + ++/* ++** Implementation of the octet_length() function ++*/ ++static void bytelengthFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ assert( argc==1 ); ++ UNUSED_PARAMETER(argc); ++ switch( sqlite3_value_type(argv[0]) ){ ++ case SQLITE_BLOB: { ++ sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); ++ break; ++ } ++ case SQLITE_INTEGER: ++ case SQLITE_FLOAT: { ++ i64 m = sqlite3_context_db_handle(context)->enc<=SQLITE_UTF8 ? 1 : 2; ++ sqlite3_result_int64(context, sqlite3_value_bytes(argv[0])*m); ++ break; ++ } ++ case SQLITE_TEXT: { ++ if( sqlite3_value_encoding(argv[0])<=SQLITE_UTF8 ){ ++ sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); ++ }else{ ++ sqlite3_result_int(context, sqlite3_value_bytes16(argv[0])); ++ } ++ break; ++ } ++ default: { ++ sqlite3_result_null(context); ++ break; ++ } ++ } ++} ++ + /* + ** Implementation of the abs() function. + ** +@@ -117308,7 +130685,7 @@ endInstrOOM: + } + + /* +-** Implementation of the printf() function. ++** Implementation of the printf() (a.k.a. format()) SQL function. + */ + static void printfFunc( + sqlite3_context *context, +@@ -117356,16 +130733,10 @@ static void substrFunc( + int len; + int p0type; + i64 p1, p2; +- int negP2 = 0; + + assert( argc==3 || argc==2 ); +- if( sqlite3_value_type(argv[1])==SQLITE_NULL +- || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) +- ){ +- return; +- } + p0type = sqlite3_value_type(argv[0]); +- p1 = sqlite3_value_int(argv[1]); ++ p1 = sqlite3_value_int64(argv[1]); + if( p0type==SQLITE_BLOB ){ + len = sqlite3_value_bytes(argv[0]); + z = sqlite3_value_blob(argv[0]); +@@ -117381,28 +130752,31 @@ static void substrFunc( + } + } + } +-#ifdef SQLITE_SUBSTR_COMPATIBILITY +- /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as +- ** as substr(X,1,N) - it returns the first N characters of X. This +- ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] +- ** from 2009-02-02 for compatibility of applications that exploited the +- ** old buggy behavior. */ +- if( p1==0 ) p1 = 1; /* */ +-#endif + if( argc==3 ){ +- p2 = sqlite3_value_int(argv[2]); +- if( p2<0 ){ +- p2 = -p2; +- negP2 = 1; +- } ++ p2 = sqlite3_value_int64(argv[2]); ++ if( p2==0 && sqlite3_value_type(argv[2])==SQLITE_NULL ) return; + }else{ + p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH]; + } ++ if( p1==0 ){ ++#ifdef SQLITE_SUBSTR_COMPATIBILITY ++ /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as ++ ** as substr(X,1,N) - it returns the first N characters of X. This ++ ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] ++ ** from 2009-02-02 for compatibility of applications that exploited the ++ ** old buggy behavior. */ ++ p1 = 1; /* */ ++#endif ++ if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return; ++ } + if( p1<0 ){ + p1 += len; + if( p1<0 ){ +- p2 += p1; +- if( p2<0 ) p2 = 0; ++ if( p2<0 ){ ++ p2 = 0; ++ }else{ ++ p2 += p1; ++ } + p1 = 0; + } + }else if( p1>0 ){ +@@ -117410,12 +130784,13 @@ static void substrFunc( + }else if( p2>0 ){ + p2--; + } +- if( negP2 ){ +- p1 -= p2; +- if( p1<0 ){ +- p2 += p1; +- p1 = 0; ++ if( p2<0 ){ ++ if( p2<-p1 ){ ++ p2 = p1; ++ }else{ ++ p2 = -p2; + } ++ p1 -= p2; + } + assert( p1>=0 && p2>=0 ); + if( p0type!=SQLITE_BLOB ){ +@@ -117429,9 +130804,11 @@ static void substrFunc( + sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT, + SQLITE_UTF8); + }else{ +- if( p1+p2>len ){ ++ if( p1>=len ){ ++ p1 = p2 = 0; ++ }else if( p2>len-p1 ){ + p2 = len-p1; +- if( p2<0 ) p2 = 0; ++ assert( p2>0 ); + } + sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT); + } +@@ -117442,13 +130819,13 @@ static void substrFunc( + */ + #ifndef SQLITE_OMIT_FLOATING_POINT + static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ +- int n = 0; ++ i64 n = 0; + double r; + char *zBuf; + assert( argc==1 || argc==2 ); + if( argc==2 ){ + if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; +- n = sqlite3_value_int(argv[1]); ++ n = sqlite3_value_int64(argv[1]); + if( n>30 ) n = 30; + if( n<0 ) n = 0; + } +@@ -117463,7 +130840,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ + }else if( n==0 ){ + r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5))); + }else{ +- zBuf = sqlite3_mprintf("%.*f",n,r); ++ zBuf = sqlite3_mprintf("%!.*f",(int)n,r); + if( zBuf==0 ){ + sqlite3_result_error_nomem(context); + return; +@@ -117621,9 +130998,9 @@ static void last_insert_rowid( + /* + ** Implementation of the changes() SQL function. + ** +-** IMP: R-62073-11209 The changes() SQL function is a wrapper +-** around the sqlite3_changes() C/C++ function and hence follows the same +-** rules for counting changes. ++** IMP: R-32760-32347 The changes() SQL function is a wrapper ++** around the sqlite3_changes64() C/C++ function and hence follows the ++** same rules for counting changes. + */ + static void changes( + sqlite3_context *context, +@@ -117632,12 +131009,12 @@ static void changes( + ){ + sqlite3 *db = sqlite3_context_db_handle(context); + UNUSED_PARAMETER2(NotUsed, NotUsed2); +- sqlite3_result_int(context, sqlite3_changes(db)); ++ sqlite3_result_int64(context, sqlite3_changes64(db)); + } + + /* + ** Implementation of the total_changes() SQL function. The return value is +-** the same as the sqlite3_total_changes() API function. ++** the same as the sqlite3_total_changes64() API function. + */ + static void total_changes( + sqlite3_context *context, +@@ -117646,9 +131023,9 @@ static void total_changes( + ){ + sqlite3 *db = sqlite3_context_db_handle(context); + UNUSED_PARAMETER2(NotUsed, NotUsed2); +- /* IMP: R-52756-41993 This function is a wrapper around the +- ** sqlite3_total_changes() C/C++ interface. */ +- sqlite3_result_int(context, sqlite3_total_changes(db)); ++ /* IMP: R-11217-42568 This function is a wrapper around the ++ ** sqlite3_total_changes64() C/C++ interface. */ ++ sqlite3_result_int64(context, sqlite3_total_changes64(db)); + } + + /* +@@ -117663,7 +131040,7 @@ struct compareInfo { + + /* + ** For LIKE and GLOB matching on EBCDIC machines, assume that every +-** character is exactly one byte in size. Also, provde the Utf8Read() ++** character is exactly one byte in size. Also, provide the Utf8Read() + ** macro for fast reading of the next character in the common case where + ** the next character is ASCII. + */ +@@ -117744,7 +131121,8 @@ static int patternCompare( + /* Skip over multiple "*" characters in the pattern. If there + ** are also "?" characters, skip those as well, but consume a + ** single character of the input string for each "?" skipped */ +- while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){ ++ while( (c=Utf8Read(zPattern)) == matchAll ++ || (c == matchOne && matchOne!=0) ){ + if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){ + return SQLITE_NOWILDCARDMATCH; + } +@@ -117777,7 +131155,7 @@ static int patternCompare( + ** c but in the other case and search the input string for either + ** c or cx. + */ +- if( c<=0x80 ){ ++ if( c<0x80 ){ + char zStop[3]; + int bMatch; + if( noCase ){ +@@ -117860,7 +131238,13 @@ static int patternCompare( + ** non-zero if there is no match. + */ + SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){ +- return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '['); ++ if( zString==0 ){ ++ return zGlobPattern!=0; ++ }else if( zGlobPattern==0 ){ ++ return 1; ++ }else { ++ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '['); ++ } + } + + /* +@@ -117868,7 +131252,13 @@ SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){ + ** a miss - like strcmp(). + */ + SQLITE_API int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){ +- return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc); ++ if( zStr==0 ){ ++ return zPattern!=0; ++ }else if( zPattern==0 ){ ++ return 1; ++ }else{ ++ return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc); ++ } + } + + /* +@@ -117883,7 +131273,7 @@ SQLITE_API int sqlite3_like_count = 0; + + /* + ** Implementation of the like() SQL function. This function implements +-** the build-in LIKE operator. The first argument to the function is the ++** the built-in LIKE operator. The first argument to the function is the + ** pattern and the second argument is the string. So, the SQL statements: + ** + ** A LIKE B +@@ -118076,39 +131466,42 @@ static const char hexdigits[] = { + }; + + /* +-** Implementation of the QUOTE() function. This function takes a single +-** argument. If the argument is numeric, the return value is the same as +-** the argument. If the argument is NULL, the return value is the string +-** "NULL". Otherwise, the argument is enclosed in single quotes with +-** single-quote escapes. ++** Append to pStr text that is the SQL literal representation of the ++** value contained in pValue. + */ +-static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ +- assert( argc==1 ); +- UNUSED_PARAMETER(argc); +- switch( sqlite3_value_type(argv[0]) ){ ++SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int bEscape){ ++ /* As currently implemented, the string must be initially empty. ++ ** we might relax this requirement in the future, but that will ++ ** require enhancements to the implementation. */ ++ assert( pStr!=0 && pStr->nChar==0 ); ++ ++ switch( sqlite3_value_type(pValue) ){ + case SQLITE_FLOAT: { + double r1, r2; +- char zBuf[50]; +- r1 = sqlite3_value_double(argv[0]); +- sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1); +- sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8); +- if( r1!=r2 ){ +- sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1); ++ const char *zVal; ++ r1 = sqlite3_value_double(pValue); ++ sqlite3_str_appendf(pStr, "%!0.15g", r1); ++ zVal = sqlite3_str_value(pStr); ++ if( zVal ){ ++ sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); ++ if( r1!=r2 ){ ++ sqlite3_str_reset(pStr); ++ sqlite3_str_appendf(pStr, "%!0.20e", r1); ++ } + } +- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + break; + } + case SQLITE_INTEGER: { +- sqlite3_result_value(context, argv[0]); ++ sqlite3_str_appendf(pStr, "%lld", sqlite3_value_int64(pValue)); + break; + } + case SQLITE_BLOB: { +- char *zText = 0; +- char const *zBlob = sqlite3_value_blob(argv[0]); +- int nBlob = sqlite3_value_bytes(argv[0]); +- assert( zBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */ +- zText = (char *)contextMalloc(context, (2*(i64)nBlob)+4); +- if( zText ){ ++ char const *zBlob = sqlite3_value_blob(pValue); ++ i64 nBlob = sqlite3_value_bytes(pValue); ++ assert( zBlob==sqlite3_value_blob(pValue) ); /* No encoding change */ ++ sqlite3StrAccumEnlarge(pStr, nBlob*2 + 4); ++ if( pStr->accError==0 ){ ++ char *zText = pStr->zText; + int i; + for(i=0; i>4)&0x0F]; +@@ -118118,39 +131511,148 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ + zText[(nBlob*2)+3] = '\0'; + zText[0] = 'X'; + zText[1] = '\''; +- sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); +- sqlite3_free(zText); ++ pStr->nChar = nBlob*2 + 3; + } + break; + } + case SQLITE_TEXT: { +- int i,j; +- u64 n; +- const unsigned char *zArg = sqlite3_value_text(argv[0]); +- char *z; +- +- if( zArg==0 ) return; +- for(i=0, n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; } +- z = contextMalloc(context, ((i64)i)+((i64)n)+3); +- if( z ){ +- z[0] = '\''; +- for(i=0, j=1; zArg[i]; i++){ +- z[j++] = zArg[i]; +- if( zArg[i]=='\'' ){ +- z[j++] = '\''; +- } +- } +- z[j++] = '\''; +- z[j] = 0; +- sqlite3_result_text(context, z, j, sqlite3_free); +- } ++ const unsigned char *zArg = sqlite3_value_text(pValue); ++ sqlite3_str_appendf(pStr, bEscape ? "%#Q" : "%Q", zArg); + break; + } + default: { +- assert( sqlite3_value_type(argv[0])==SQLITE_NULL ); +- sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC); ++ assert( sqlite3_value_type(pValue)==SQLITE_NULL ); ++ sqlite3_str_append(pStr, "NULL", 4); ++ break; ++ } ++ } ++} ++ ++/* ++** Return true if z[] begins with N hexadecimal digits, and write ++** a decoding of those digits into *pVal. Or return false if any ++** one of the first N characters in z[] is not a hexadecimal digit. ++*/ ++static int isNHex(const char *z, int N, u32 *pVal){ ++ int i; ++ int v = 0; ++ for(i=0; i0 ){ ++ memmove(&zOut[j], &zIn[i], n); ++ j += n; ++ i += n; ++ } ++ if( zIn[i+1]=='\\' ){ ++ i += 2; ++ zOut[j++] = '\\'; ++ }else if( sqlite3Isxdigit(zIn[i+1]) ){ ++ if( !isNHex(&zIn[i+1], 4, &v) ) goto unistr_error; ++ i += 5; ++ j += sqlite3AppendOneUtf8Character(&zOut[j], v); ++ }else if( zIn[i+1]=='+' ){ ++ if( !isNHex(&zIn[i+2], 6, &v) ) goto unistr_error; ++ i += 8; ++ j += sqlite3AppendOneUtf8Character(&zOut[j], v); ++ }else if( zIn[i+1]=='u' ){ ++ if( !isNHex(&zIn[i+2], 4, &v) ) goto unistr_error; ++ i += 6; ++ j += sqlite3AppendOneUtf8Character(&zOut[j], v); ++ }else if( zIn[i+1]=='U' ){ ++ if( !isNHex(&zIn[i+2], 8, &v) ) goto unistr_error; ++ i += 10; ++ j += sqlite3AppendOneUtf8Character(&zOut[j], v); ++ }else{ ++ goto unistr_error; ++ } ++ } ++ zOut[j] = 0; ++ sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8); ++ return; ++ ++unistr_error: ++ sqlite3_free(zOut); ++ sqlite3_result_error(context, "invalid Unicode escape", -1); ++ return; ++} ++ ++ ++/* ++** Implementation of the QUOTE() function. ++** ++** The quote(X) function returns the text of an SQL literal which is the ++** value of its argument suitable for inclusion into an SQL statement. ++** Strings are surrounded by single-quotes with escapes on interior quotes ++** as needed. BLOBs are encoded as hexadecimal literals. Strings with ++** embedded NUL characters cannot be represented as string literals in SQL ++** and hence the returned string literal is truncated prior to the first NUL. ++** ++** If sqlite3_user_data() is non-zero, then the UNISTR_QUOTE() function is ++** implemented instead. The difference is that UNISTR_QUOTE() uses the ++** UNISTR() function to escape control characters. ++*/ ++static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ ++ sqlite3_str str; ++ sqlite3 *db = sqlite3_context_db_handle(context); ++ assert( argc==1 ); ++ UNUSED_PARAMETER(argc); ++ sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); ++ sqlite3QuoteValue(&str,argv[0],SQLITE_PTR_TO_INT(sqlite3_user_data(context))); ++ sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar, ++ SQLITE_DYNAMIC); ++ if( str.accError!=SQLITE_OK ){ ++ sqlite3_result_null(context); ++ sqlite3_result_error_code(context, str.accError); + } + } + +@@ -118207,6 +131709,7 @@ static void charFunc( + *zOut++ = 0x80 + (u8)(c & 0x3F); + } \ + } ++ *zOut = 0; + sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8); + } + +@@ -118235,10 +131738,101 @@ static void hexFunc( + *(z++) = hexdigits[c&0xf]; + } + *z = 0; +- sqlite3_result_text(context, zHex, n*2, sqlite3_free); ++ sqlite3_result_text64(context, zHex, (u64)(z-zHex), ++ sqlite3_free, SQLITE_UTF8); + } + } + ++/* ++** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr ++** contains character ch, or 0 if it does not. ++*/ ++static int strContainsChar(const u8 *zStr, int nStr, u32 ch){ ++ const u8 *zEnd = &zStr[nStr]; ++ const u8 *z = zStr; ++ while( z0 ){ +- azChar = contextMalloc(context, ((i64)nChar)*(sizeof(char*)+1)); ++ azChar = contextMalloc(context, ++ ((i64)nChar)*(sizeof(char*)+sizeof(unsigned))); + if( azChar==0 ){ + return; + } +- aLen = (unsigned char*)&azChar[nChar]; ++ aLen = (unsigned*)&azChar[nChar]; + for(z=zCharSet, nChar=0; *z; nChar++){ + azChar[nChar] = (unsigned char *)z; + SQLITE_SKIP_UTF8(z); +- aLen[nChar] = (u8)(z - azChar[nChar]); ++ aLen[nChar] = (unsigned)(z - azChar[nChar]); + } + } + } +@@ -118410,7 +132005,7 @@ static void trimFunc( + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(context)); + if( flags & 1 ){ + while( nIn>0 ){ +- int len = 0; ++ unsigned int len = 0; + for(i=0; i0 ){ +- int len = 0; ++ unsigned int len = 0; + for(i=0; i0 && nSep>0 ){ ++ memcpy(&z[j], zSep, nSep); ++ j += nSep; ++ } ++ memcpy(&z[j], v, k); ++ j += k; ++ } ++ } ++ } ++ z[j] = 0; ++ assert( j<=n ); ++ sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8); ++} ++ ++/* ++** The CONCAT(...) function. Generate a string result that is the ++** concatentation of all non-null arguments. ++*/ ++static void concatFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ concatFuncCore(context, argc, argv, 0, ""); ++} ++ ++/* ++** The CONCAT_WS(separator, ...) function. ++** ++** Generate a string that is the concatenation of 2nd through the Nth ++** argument. Use the first argument (which must be non-NULL) as the ++** separator. ++*/ ++static void concatwsFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ int nSep = sqlite3_value_bytes(argv[0]); ++ const char *zSep = (const char*)sqlite3_value_text(argv[0]); ++ if( zSep==0 ) return; ++ concatFuncCore(context, argc-1, argv+1, nSep, zSep); ++} ++ + + #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + /* + ** The "unknown" function is automatically substituted in place of + ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN +-** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used. ++** when the SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION compile-time option is used. + ** When the "sqlite3" command-line shell is built using this functionality, + ** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries + ** involving application-defined functions to be examined in a generic +@@ -118455,6 +132125,9 @@ static void unknownFunc( + sqlite3_value **argv + ){ + /* no-op */ ++ (void)context; ++ (void)argc; ++ (void)argv; + } + #endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/ + +@@ -118556,13 +132229,68 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){ + */ + typedef struct SumCtx SumCtx; + struct SumCtx { +- double rSum; /* Floating point sum */ +- i64 iSum; /* Integer sum */ ++ double rSum; /* Running sum as as a double */ ++ double rErr; /* Error term for Kahan-Babushka-Neumaier summation */ ++ i64 iSum; /* Running sum as a signed integer */ + i64 cnt; /* Number of elements summed */ +- u8 overflow; /* True if integer overflow seen */ +- u8 approx; /* True if non-integer value was input to the sum */ ++ u8 approx; /* True if any non-integer value was input to the sum */ ++ u8 ovrfl; /* Integer overflow seen */ + }; + ++/* ++** Do one step of the Kahan-Babushka-Neumaier summation. ++** ++** https://en.wikipedia.org/wiki/Kahan_summation_algorithm ++** ++** Variables are marked "volatile" to defeat c89 x86 floating point ++** optimizations can mess up this algorithm. ++*/ ++static void kahanBabuskaNeumaierStep( ++ volatile SumCtx *pSum, ++ volatile double r ++){ ++ volatile double s = pSum->rSum; ++ volatile double t = s + r; ++ if( fabs(s) > fabs(r) ){ ++ pSum->rErr += (s - t) + r; ++ }else{ ++ pSum->rErr += (r - t) + s; ++ } ++ pSum->rSum = t; ++} ++ ++/* ++** Add a (possibly large) integer to the running sum. ++*/ ++static void kahanBabuskaNeumaierStepInt64(volatile SumCtx *pSum, i64 iVal){ ++ if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){ ++ i64 iBig, iSm; ++ iSm = iVal % 16384; ++ iBig = iVal - iSm; ++ kahanBabuskaNeumaierStep(pSum, iBig); ++ kahanBabuskaNeumaierStep(pSum, iSm); ++ }else{ ++ kahanBabuskaNeumaierStep(pSum, (double)iVal); ++ } ++} ++ ++/* ++** Initialize the Kahan-Babaska-Neumaier sum from a 64-bit integer ++*/ ++static void kahanBabuskaNeumaierInit( ++ volatile SumCtx *p, ++ i64 iVal ++){ ++ if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){ ++ i64 iSm = iVal % 16384; ++ p->rSum = (double)(iVal - iSm); ++ p->rErr = (double)iSm; ++ }else{ ++ p->rSum = (double)iVal; ++ p->rErr = 0.0; ++ } ++} ++ + /* + ** Routines used to compute the sum, average, and total. + ** +@@ -118570,7 +132298,7 @@ struct SumCtx { + ** that it returns NULL if it sums over no inputs. TOTAL returns + ** 0.0 in that case. In addition, TOTAL always returns a float where + ** SUM might return an integer if it never encounters a floating point +-** value. TOTAL never fails, but SUM might through an exception if ++** value. TOTAL never fails, but SUM might throw an exception if + ** it overflows an integer. + */ + static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ +@@ -118582,15 +132310,29 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ + type = sqlite3_value_numeric_type(argv[0]); + if( p && type!=SQLITE_NULL ){ + p->cnt++; +- if( type==SQLITE_INTEGER ){ +- i64 v = sqlite3_value_int64(argv[0]); +- p->rSum += v; +- if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ +- p->approx = p->overflow = 1; ++ if( p->approx==0 ){ ++ if( type!=SQLITE_INTEGER ){ ++ kahanBabuskaNeumaierInit(p, p->iSum); ++ p->approx = 1; ++ kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0])); ++ }else{ ++ i64 x = p->iSum; ++ if( sqlite3AddInt64(&x, sqlite3_value_int64(argv[0]))==0 ){ ++ p->iSum = x; ++ }else{ ++ p->ovrfl = 1; ++ kahanBabuskaNeumaierInit(p, p->iSum); ++ p->approx = 1; ++ kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0])); ++ } + } + }else{ +- p->rSum += sqlite3_value_double(argv[0]); +- p->approx = 1; ++ if( type==SQLITE_INTEGER ){ ++ kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0])); ++ }else{ ++ p->ovrfl = 0; ++ kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0])); ++ } + } + } + } +@@ -118607,13 +132349,21 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){ + if( ALWAYS(p) && type!=SQLITE_NULL ){ + assert( p->cnt>0 ); + p->cnt--; +- assert( type==SQLITE_INTEGER || p->approx ); +- if( type==SQLITE_INTEGER && p->approx==0 ){ +- i64 v = sqlite3_value_int64(argv[0]); +- p->rSum -= v; +- p->iSum -= v; ++ if( !p->approx ){ ++ if( sqlite3SubInt64(&p->iSum, sqlite3_value_int64(argv[0])) ){ ++ p->ovrfl = 1; ++ p->approx = 1; ++ } ++ }else if( type==SQLITE_INTEGER ){ ++ i64 iVal = sqlite3_value_int64(argv[0]); ++ if( iVal!=SMALLEST_INT64 ){ ++ kahanBabuskaNeumaierStepInt64(p, -iVal); ++ }else{ ++ kahanBabuskaNeumaierStepInt64(p, LARGEST_INT64); ++ kahanBabuskaNeumaierStepInt64(p, 1); ++ } + }else{ +- p->rSum -= sqlite3_value_double(argv[0]); ++ kahanBabuskaNeumaierStep(p, -sqlite3_value_double(argv[0])); + } + } + } +@@ -118624,10 +132374,14 @@ static void sumFinalize(sqlite3_context *context){ + SumCtx *p; + p = sqlite3_aggregate_context(context, 0); + if( p && p->cnt>0 ){ +- if( p->overflow ){ +- sqlite3_result_error(context,"integer overflow",-1); +- }else if( p->approx ){ +- sqlite3_result_double(context, p->rSum); ++ if( p->approx ){ ++ if( p->ovrfl ){ ++ sqlite3_result_error(context,"integer overflow",-1); ++ }else if( !sqlite3IsOverflow(p->rErr) ){ ++ sqlite3_result_double(context, p->rSum+p->rErr); ++ }else{ ++ sqlite3_result_double(context, p->rSum); ++ } + }else{ + sqlite3_result_int64(context, p->iSum); + } +@@ -118637,14 +132391,29 @@ static void avgFinalize(sqlite3_context *context){ + SumCtx *p; + p = sqlite3_aggregate_context(context, 0); + if( p && p->cnt>0 ){ +- sqlite3_result_double(context, p->rSum/(double)p->cnt); ++ double r; ++ if( p->approx ){ ++ r = p->rSum; ++ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; ++ }else{ ++ r = (double)(p->iSum); ++ } ++ sqlite3_result_double(context, r/(double)p->cnt); + } + } + static void totalFinalize(sqlite3_context *context){ + SumCtx *p; ++ double r = 0.0; + p = sqlite3_aggregate_context(context, 0); +- /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ +- sqlite3_result_double(context, p ? p->rSum : (double)0); ++ if( p ){ ++ if( p->approx ){ ++ r = p->rSum; ++ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; ++ }else{ ++ r = (double)(p->iSum); ++ } ++ } ++ sqlite3_result_double(context, r); + } + + /* +@@ -118763,97 +132532,174 @@ static void minMaxFinalize(sqlite3_context *context){ + + /* + ** group_concat(EXPR, ?SEPARATOR?) ++** string_agg(EXPR, SEPARATOR) ++** ++** Content is accumulated in GroupConcatCtx.str with the SEPARATOR ++** coming before the EXPR value, except for the first entry which ++** omits the SEPARATOR. ++** ++** It is tragic that the SEPARATOR goes before the EXPR string. The ++** groupConcatInverse() implementation would have been easier if the ++** SEPARATOR were appended after EXPR. And the order is undocumented, ++** so we could change it, in theory. But the old behavior has been ++** around for so long that we dare not, for fear of breaking something. + */ ++typedef struct { ++ StrAccum str; /* The accumulated concatenation */ ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ int nAccum; /* Number of strings presently concatenated */ ++ int nFirstSepLength; /* Used to detect separator length change */ ++ /* If pnSepLengths!=0, refs an array of inter-string separator lengths, ++ ** stored as actually incorporated into presently accumulated result. ++ ** (Hence, its slots in use number nAccum-1 between method calls.) ++ ** If pnSepLengths==0, nFirstSepLength is the length used throughout. ++ */ ++ int *pnSepLengths; ++#endif ++} GroupConcatCtx; ++ + static void groupConcatStep( + sqlite3_context *context, + int argc, + sqlite3_value **argv + ){ + const char *zVal; +- StrAccum *pAccum; ++ GroupConcatCtx *pGCC; + const char *zSep; + int nVal, nSep; + assert( argc==1 || argc==2 ); + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; +- pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum)); +- +- if( pAccum ){ ++ pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); ++ if( pGCC ){ + sqlite3 *db = sqlite3_context_db_handle(context); +- int firstTerm = pAccum->mxAlloc==0; +- pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; +- if( !firstTerm ){ +- if( argc==2 ){ +- zSep = (char*)sqlite3_value_text(argv[1]); +- nSep = sqlite3_value_bytes(argv[1]); +- }else{ +- zSep = ","; +- nSep = 1; ++ int firstTerm = pGCC->str.mxAlloc==0; ++ pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; ++ if( argc==1 ){ ++ if( !firstTerm ){ ++ sqlite3_str_appendchar(&pGCC->str, 1, ','); + } +- if( zSep ) sqlite3_str_append(pAccum, zSep, nSep); ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ else{ ++ pGCC->nFirstSepLength = 1; ++ } ++#endif ++ }else if( !firstTerm ){ ++ zSep = (char*)sqlite3_value_text(argv[1]); ++ nSep = sqlite3_value_bytes(argv[1]); ++ if( zSep ){ ++ sqlite3_str_append(&pGCC->str, zSep, nSep); ++ } ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ else{ ++ nSep = 0; ++ } ++ if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){ ++ int *pnsl = pGCC->pnSepLengths; ++ if( pnsl == 0 ){ ++ /* First separator length variation seen, start tracking them. */ ++ pnsl = (int*)sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int)); ++ if( pnsl!=0 ){ ++ int i = 0, nA = pGCC->nAccum-1; ++ while( inFirstSepLength; ++ } ++ }else{ ++ pnsl = (int*)sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int)); ++ } ++ if( pnsl!=0 ){ ++ if( ALWAYS(pGCC->nAccum>0) ){ ++ pnsl[pGCC->nAccum-1] = nSep; ++ } ++ pGCC->pnSepLengths = pnsl; ++ }else{ ++ sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM); ++ } ++ } ++#endif + } ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ else{ ++ pGCC->nFirstSepLength = sqlite3_value_bytes(argv[1]); ++ } ++ pGCC->nAccum += 1; ++#endif + zVal = (char*)sqlite3_value_text(argv[0]); + nVal = sqlite3_value_bytes(argv[0]); +- if( zVal ) sqlite3_str_append(pAccum, zVal, nVal); ++ if( zVal ) sqlite3_str_append(&pGCC->str, zVal, nVal); + } + } ++ + #ifndef SQLITE_OMIT_WINDOWFUNC + static void groupConcatInverse( + sqlite3_context *context, + int argc, + sqlite3_value **argv + ){ +- int n; +- StrAccum *pAccum; ++ GroupConcatCtx *pGCC; + assert( argc==1 || argc==2 ); ++ (void)argc; /* Suppress unused parameter warning */ + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; +- pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum)); +- /* pAccum is always non-NULL since groupConcatStep() will have always +- ** run frist to initialize it */ +- if( ALWAYS(pAccum) ){ +- n = sqlite3_value_bytes(argv[0]); +- if( argc==2 ){ +- n += sqlite3_value_bytes(argv[1]); ++ pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); ++ /* pGCC is always non-NULL since groupConcatStep() will have always ++ ** run first to initialize it */ ++ if( ALWAYS(pGCC) ){ ++ int nVS; /* Number of characters to remove */ ++ /* Must call sqlite3_value_text() to convert the argument into text prior ++ ** to invoking sqlite3_value_bytes(), in case the text encoding is UTF16 */ ++ (void)sqlite3_value_text(argv[0]); ++ nVS = sqlite3_value_bytes(argv[0]); ++ pGCC->nAccum -= 1; ++ if( pGCC->pnSepLengths!=0 ){ ++ assert(pGCC->nAccum >= 0); ++ if( pGCC->nAccum>0 ){ ++ nVS += *pGCC->pnSepLengths; ++ memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1, ++ (pGCC->nAccum-1)*sizeof(int)); ++ } + }else{ +- n++; ++ /* If removing single accumulated string, harmlessly over-do. */ ++ nVS += pGCC->nFirstSepLength; + } +- if( n>=(int)pAccum->nChar ){ +- pAccum->nChar = 0; ++ if( nVS>=(int)pGCC->str.nChar ){ ++ pGCC->str.nChar = 0; + }else{ +- pAccum->nChar -= n; +- memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar); ++ pGCC->str.nChar -= nVS; ++ memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar); ++ } ++ if( pGCC->str.nChar==0 ){ ++ pGCC->str.mxAlloc = 0; ++ sqlite3_free(pGCC->pnSepLengths); ++ pGCC->pnSepLengths = 0; + } +- if( pAccum->nChar==0 ) pAccum->mxAlloc = 0; + } + } + #else + # define groupConcatInverse 0 + #endif /* SQLITE_OMIT_WINDOWFUNC */ + static void groupConcatFinalize(sqlite3_context *context){ +- StrAccum *pAccum; +- pAccum = sqlite3_aggregate_context(context, 0); +- if( pAccum ){ +- if( pAccum->accError==SQLITE_TOOBIG ){ +- sqlite3_result_error_toobig(context); +- }else if( pAccum->accError==SQLITE_NOMEM ){ +- sqlite3_result_error_nomem(context); +- }else{ +- sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1, +- sqlite3_free); +- } ++ GroupConcatCtx *pGCC ++ = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); ++ if( pGCC ){ ++ sqlite3ResultStrAccum(context, &pGCC->str); ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ sqlite3_free(pGCC->pnSepLengths); ++#endif + } + } + #ifndef SQLITE_OMIT_WINDOWFUNC + static void groupConcatValue(sqlite3_context *context){ +- sqlite3_str *pAccum; +- pAccum = (sqlite3_str*)sqlite3_aggregate_context(context, 0); +- if( pAccum ){ ++ GroupConcatCtx *pGCC ++ = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); ++ if( pGCC ){ ++ StrAccum *pAccum = &pGCC->str; + if( pAccum->accError==SQLITE_TOOBIG ){ + sqlite3_result_error_toobig(context); + }else if( pAccum->accError==SQLITE_NOMEM ){ + sqlite3_result_error_nomem(context); ++ }else if( pGCC->nAccum>0 && pAccum->nChar==0 ){ ++ sqlite3_result_text(context, "", 1, SQLITE_STATIC); + }else{ + const char *zText = sqlite3_str_value(pAccum); +- sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); ++ sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); + } + } + } +@@ -118880,8 +132726,10 @@ SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){ + ** sensitive. + */ + SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ ++ FuncDef *pDef; + struct compareInfo *pInfo; + int flags; ++ int nArg; + if( caseSensitive ){ + pInfo = (struct compareInfo*)&likeInfoAlt; + flags = SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE; +@@ -118889,10 +132737,13 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) + pInfo = (struct compareInfo*)&likeInfoNorm; + flags = SQLITE_FUNC_LIKE; + } +- 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); +- sqlite3FindFunction(db, "like", 2, SQLITE_UTF8, 0)->funcFlags |= flags; +- sqlite3FindFunction(db, "like", 3, SQLITE_UTF8, 0)->funcFlags |= flags; ++ for(nArg=2; nArg<=3; nArg++){ ++ sqlite3CreateFunc(db, "like", nArg, SQLITE_UTF8, pInfo, likeFunc, ++ 0, 0, 0, 0, 0); ++ pDef = sqlite3FindFunction(db, "like", nArg, SQLITE_UTF8, 0); ++ pDef->funcFlags |= flags; ++ pDef->funcFlags &= ~SQLITE_FUNC_UNSAFE; ++ } + } + + /* +@@ -118915,11 +132766,14 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) + SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ + FuncDef *pDef; + int nExpr; +- if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){ ++ assert( pExpr!=0 ); ++ assert( pExpr->op==TK_FUNCTION ); ++ assert( ExprUseXList(pExpr) ); ++ if( !pExpr->x.pList ){ + return 0; + } +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + nExpr = pExpr->x.pList->nExpr; ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0); + #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + if( pDef==0 ) return 0; +@@ -118943,6 +132797,7 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas + Expr *pEscape = pExpr->x.pList->a[2].pExpr; + char *zEscape; + if( pEscape->op!=TK_STRING ) return 0; ++ assert( !ExprHasProperty(pEscape, EP_IntValue) ); + zEscape = pEscape->u.zToken; + if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; + if( zEscape[0]==aWc[0] ) return 0; +@@ -118954,6 +132809,326 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas + return 1; + } + ++/* Mathematical Constants */ ++#ifndef M_PI ++# define M_PI 3.141592653589793238462643383279502884 ++#endif ++#ifndef M_LN10 ++# define M_LN10 2.302585092994045684017991454684364208 ++#endif ++#ifndef M_LN2 ++# define M_LN2 0.693147180559945309417232121458176568 ++#endif ++ ++ ++/* Extra math functions that require linking with -lm ++*/ ++#ifdef SQLITE_ENABLE_MATH_FUNCTIONS ++/* ++** Implementation SQL functions: ++** ++** ceil(X) ++** ceiling(X) ++** floor(X) ++** ++** The sqlite3_user_data() pointer is a pointer to the libm implementation ++** of the underlying C function. ++*/ ++static void ceilingFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ assert( argc==1 ); ++ switch( sqlite3_value_numeric_type(argv[0]) ){ ++ case SQLITE_INTEGER: { ++ sqlite3_result_int64(context, sqlite3_value_int64(argv[0])); ++ break; ++ } ++ case SQLITE_FLOAT: { ++ double (*x)(double) = (double(*)(double))sqlite3_user_data(context); ++ sqlite3_result_double(context, x(sqlite3_value_double(argv[0]))); ++ break; ++ } ++ default: { ++ break; ++ } ++ } ++} ++ ++/* ++** On some systems, ceil() and floor() are intrinsic function. You are ++** unable to take a pointer to these functions. Hence, we here wrap them ++** in our own actual functions. ++*/ ++static double xCeil(double x){ return ceil(x); } ++static double xFloor(double x){ return floor(x); } ++ ++/* ++** Some systems do not have log2() and log10() in their standard math ++** libraries. ++*/ ++#if defined(HAVE_LOG10) && HAVE_LOG10==0 ++# define log10(X) (0.4342944819032517867*log(X)) ++#endif ++#if defined(HAVE_LOG2) && HAVE_LOG2==0 ++# define log2(X) (1.442695040888963456*log(X)) ++#endif ++ ++ ++/* ++** Implementation of SQL functions: ++** ++** ln(X) - natural logarithm ++** log(X) - log X base 10 ++** log10(X) - log X base 10 ++** log(B,X) - log X base B ++*/ ++static void logFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ double x, b, ans; ++ assert( argc==1 || argc==2 ); ++ switch( sqlite3_value_numeric_type(argv[0]) ){ ++ case SQLITE_INTEGER: ++ case SQLITE_FLOAT: ++ x = sqlite3_value_double(argv[0]); ++ if( x<=0.0 ) return; ++ break; ++ default: ++ return; ++ } ++ if( argc==2 ){ ++ switch( sqlite3_value_numeric_type(argv[0]) ){ ++ case SQLITE_INTEGER: ++ case SQLITE_FLOAT: ++ b = log(x); ++ if( b<=0.0 ) return; ++ x = sqlite3_value_double(argv[1]); ++ if( x<=0.0 ) return; ++ break; ++ default: ++ return; ++ } ++ ans = log(x)/b; ++ }else{ ++ switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){ ++ case 1: ++ ans = log10(x); ++ break; ++ case 2: ++ ans = log2(x); ++ break; ++ default: ++ ans = log(x); ++ break; ++ } ++ } ++ sqlite3_result_double(context, ans); ++} ++ ++/* ++** Functions to converts degrees to radians and radians to degrees. ++*/ ++static double degToRad(double x){ return x*(M_PI/180.0); } ++static double radToDeg(double x){ return x*(180.0/M_PI); } ++ ++/* ++** Implementation of 1-argument SQL math functions: ++** ++** exp(X) - Compute e to the X-th power ++*/ ++static void math1Func( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ int type0; ++ double v0, ans; ++ double (*x)(double); ++ assert( argc==1 ); ++ type0 = sqlite3_value_numeric_type(argv[0]); ++ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; ++ v0 = sqlite3_value_double(argv[0]); ++ x = (double(*)(double))sqlite3_user_data(context); ++ ans = x(v0); ++ sqlite3_result_double(context, ans); ++} ++ ++/* ++** Implementation of 2-argument SQL math functions: ++** ++** power(X,Y) - Compute X to the Y-th power ++*/ ++static void math2Func( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ int type0, type1; ++ double v0, v1, ans; ++ double (*x)(double,double); ++ assert( argc==2 ); ++ type0 = sqlite3_value_numeric_type(argv[0]); ++ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; ++ type1 = sqlite3_value_numeric_type(argv[1]); ++ if( type1!=SQLITE_INTEGER && type1!=SQLITE_FLOAT ) return; ++ v0 = sqlite3_value_double(argv[0]); ++ v1 = sqlite3_value_double(argv[1]); ++ x = (double(*)(double,double))sqlite3_user_data(context); ++ ans = x(v0, v1); ++ sqlite3_result_double(context, ans); ++} ++ ++/* ++** Implementation of 0-argument pi() function. ++*/ ++static void piFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ assert( argc==0 ); ++ (void)argv; ++ sqlite3_result_double(context, M_PI); ++} ++ ++#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ ++ ++/* ++** Implementation of sign(X) function. ++*/ ++static void signFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ int type0; ++ double x; ++ UNUSED_PARAMETER(argc); ++ assert( argc==1 ); ++ type0 = sqlite3_value_numeric_type(argv[0]); ++ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; ++ x = sqlite3_value_double(argv[0]); ++ sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); ++} ++ ++#ifdef SQLITE_DEBUG ++/* ++** Implementation of fpdecode(x,y,z) function. ++** ++** x is a real number that is to be decoded. y is the precision. ++** z is the maximum real precision. Return a string that shows the ++** results of the sqlite3FpDecode() function. ++** ++** Used for testing and debugging only, specifically testing and debugging ++** of the sqlite3FpDecode() function. This SQL function does not appear ++** in production builds. This function is not an API and is subject to ++** modification or removal in future versions of SQLite. ++*/ ++static void fpdecodeFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ FpDecode s; ++ double x; ++ int y, z; ++ char zBuf[100]; ++ UNUSED_PARAMETER(argc); ++ assert( argc==3 ); ++ x = sqlite3_value_double(argv[0]); ++ y = sqlite3_value_int(argv[1]); ++ z = sqlite3_value_int(argv[2]); ++ if( z<=0 ) z = 1; ++ sqlite3FpDecode(&s, x, y, z); ++ if( s.isSpecial==2 ){ ++ sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); ++ }else{ ++ sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); ++ } ++ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); ++} ++#endif /* SQLITE_DEBUG */ ++ ++#ifdef SQLITE_DEBUG ++/* ++** Implementation of parseuri(uri,flags) function. ++** ++** Required Arguments: ++** "uri" The URI to parse. ++** "flags" Bitmask of flags, as if to sqlite3_open_v2(). ++** ++** Additional arguments beyond the first two make calls to ++** sqlite3_uri_key() for integers and sqlite3_uri_parameter for ++** anything else. ++** ++** The result is a string showing the results of calling sqlite3ParseUri(). ++** ++** Used for testing and debugging only, specifically testing and debugging ++** of the sqlite3ParseUri() function. This SQL function does not appear ++** in production builds. This function is not an API and is subject to ++** modification or removal in future versions of SQLite. ++*/ ++static void parseuriFunc( ++ sqlite3_context *ctx, ++ int argc, ++ sqlite3_value **argv ++){ ++ sqlite3_str *pResult; ++ const char *zVfs; ++ const char *zUri; ++ unsigned int flgs; ++ int rc; ++ sqlite3_vfs *pVfs = 0; ++ char *zFile = 0; ++ char *zErr = 0; ++ ++ if( argc<2 ) return; ++ pVfs = sqlite3_vfs_find(0); ++ assert( pVfs ); ++ zVfs = pVfs->zName; ++ zUri = (const char*)sqlite3_value_text(argv[0]); ++ if( zUri==0 ) return; ++ flgs = (unsigned int)sqlite3_value_int(argv[1]); ++ rc = sqlite3ParseUri(zVfs, zUri, &flgs, &pVfs, &zFile, &zErr); ++ pResult = sqlite3_str_new(0); ++ if( pResult ){ ++ int i; ++ sqlite3_str_appendf(pResult, "rc=%d", rc); ++ sqlite3_str_appendf(pResult, ", flags=0x%x", flgs); ++ sqlite3_str_appendf(pResult, ", vfs=%Q", pVfs ? pVfs->zName: 0); ++ sqlite3_str_appendf(pResult, ", err=%Q", zErr); ++ sqlite3_str_appendf(pResult, ", file=%Q", zFile); ++ if( zFile ){ ++ const char *z = zFile; ++ z += sqlite3Strlen30(z)+1; ++ while( z[0] ){ ++ sqlite3_str_appendf(pResult, ", %Q", z); ++ z += sqlite3Strlen30(z)+1; ++ } ++ for(i=2; iu.pHash){ + int n = sqlite3Strlen30(p->zName); + int h = p->zName[0] + n; ++ assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); + printf(" %s(%d)", p->zName, h); + } + printf("\n"); +@@ -119316,7 +133542,9 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex( + */ + if( pParent->iPKey>=0 ){ + if( !zKey ) return 0; +- if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; ++ if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zCnName, zKey) ){ ++ return 0; ++ } + } + }else if( paiCol ){ + assert( nCol>1 ); +@@ -119358,11 +133586,11 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex( + /* If the index uses a collation sequence that is different from + ** the default collation sequence for the column, this index is + ** unusable. Bail out early in this case. */ +- zDfltColl = pParent->aCol[iCol].zColl; ++ zDfltColl = sqlite3ColumnColl(&pParent->aCol[iCol]); + if( !zDfltColl ) zDfltColl = sqlite3StrBINARY; + if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; + +- zIdxCol = pParent->aCol[iCol].zName; ++ zIdxCol = pParent->aCol[iCol].zCnName; + for(j=0; jaCol[j].zCol, zIdxCol)==0 ){ + if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; +@@ -119489,7 +133717,6 @@ static void fkLookupParent( + }else{ + int nCol = pFKey->nCol; + int regTemp = sqlite3GetTempRange(pParse, nCol); +- int regRec = sqlite3GetTempReg(pParse); + + sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); +@@ -119529,11 +133756,10 @@ static void fkLookupParent( + sqlite3VdbeGoto(v, iOk); + } + +- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec, ++ sqlite3VdbeAddOp4(v, OP_Affinity, regTemp, nCol, 0, + sqlite3IndexAffinityStr(pParse->db,pIdx), nCol); +- sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v); +- +- sqlite3ReleaseTempReg(pParse, regRec); ++ sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regTemp, nCol); ++ VdbeCoverage(v); + sqlite3ReleaseTempRange(pParse, regTemp, nCol); + } + } +@@ -119586,7 +133812,7 @@ static Expr *exprTableRegister( + pCol = &pTab->aCol[iCol]; + pExpr->iTable = regBase + sqlite3TableColumnToStorage(pTab,iCol) + 1; + pExpr->affExpr = pCol->affinity; +- zColl = pCol->zColl; ++ zColl = sqlite3ColumnColl(pCol); + if( zColl==0 ) zColl = db->pDfltColl->zName; + pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl); + }else{ +@@ -119609,6 +133835,7 @@ static Expr *exprTableColumn( + ){ + Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0); + if( pExpr ){ ++ assert( ExprUseYTab(pExpr) ); + pExpr->y.pTab = pTab; + pExpr->iTable = iCursor; + pExpr->iColumn = iCol; +@@ -119634,14 +133861,10 @@ static Expr *exprTableColumn( + ** Operation | FK type | Action taken + ** -------------------------------------------------------------------------- + ** DELETE immediate Increment the "immediate constraint counter". +-** Or, if the ON (UPDATE|DELETE) action is RESTRICT, +-** throw a "FOREIGN KEY constraint failed" exception. + ** + ** INSERT immediate Decrement the "immediate constraint counter". + ** + ** DELETE deferred Increment the "deferred constraint counter". +-** Or, if the ON (UPDATE|DELETE) action is RESTRICT, +-** throw a "FOREIGN KEY constraint failed" exception. + ** + ** INSERT deferred Decrement the "deferred constraint counter". + ** +@@ -119695,7 +133918,7 @@ static void fkScanChildren( + pLeft = exprTableRegister(pParse, pTab, regData, iCol); + iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; + assert( iCol>=0 ); +- zCol = pFKey->pFrom->aCol[iCol].zName; ++ zCol = pFKey->pFrom->aCol[iCol].zCnName; + pRight = sqlite3Expr(db, TK_ID, zCol); + pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); + pWhere = sqlite3ExprAnd(pParse, pWhere, pEq); +@@ -119730,7 +133953,7 @@ static void fkScanChildren( + i16 iCol = pIdx->aiColumn[i]; + assert( iCol>=0 ); + pLeft = exprTableRegister(pParse, pTab, regData, iCol); +- pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zName); ++ pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zCnName); + pEq = sqlite3PExpr(pParse, TK_IS, pLeft, pRight); + pAll = sqlite3ExprAnd(pParse, pAll, pEq); + } +@@ -119749,7 +133972,7 @@ static void fkScanChildren( + ** clause. For each row found, increment either the deferred or immediate + ** foreign key constraint counter. */ + if( pParse->nErr==0 ){ +- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); ++ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0); + sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); + if( pWInfo ){ + sqlite3WhereEnd(pWInfo); +@@ -119800,6 +134023,25 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ + } + } + ++/* ++** Clear the apTrigger[] cache of CASCADE triggers for all foreign keys ++** in a particular database. This needs to happen when the schema ++** changes. ++*/ ++SQLITE_PRIVATE void sqlite3FkClearTriggerCache(sqlite3 *db, int iDb){ ++ HashElem *k; ++ Hash *pHash = &db->aDb[iDb].pSchema->tblHash; ++ for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k)){ ++ Table *pTab = sqliteHashData(k); ++ FKey *pFKey; ++ if( !IsOrdinaryTable(pTab) ) continue; ++ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ ++ fkTriggerDelete(db, pFKey->apTrigger[0]); pFKey->apTrigger[0] = 0; ++ fkTriggerDelete(db, pFKey->apTrigger[1]); pFKey->apTrigger[1] = 0; ++ } ++ } ++} ++ + /* + ** This function is called to generate code that runs when table pTab is + ** being dropped from the database. The SrcList passed as the second argument +@@ -119819,12 +134061,12 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ + */ + SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ + sqlite3 *db = pParse->db; +- if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) ){ ++ if( (db->flags&SQLITE_ForeignKeys) && IsOrdinaryTable(pTab) ){ + int iSkip = 0; + Vdbe *v = sqlite3GetVdbe(pParse); + + assert( v ); /* VDBE has already been allocated */ +- assert( pTab->pSelect==0 ); /* Not a view */ ++ assert( IsOrdinaryTable(pTab) ); + if( sqlite3FkReferences(pTab)==0 ){ + /* Search for a deferred foreign key constraint for which this table + ** is the child table. If one cannot be found, return without +@@ -119832,7 +134074,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa + ** the entire DELETE if there are no outstanding deferred constraints + ** when this statement is run. */ + FKey *p; +- for(p=pTab->pFKey; p; p=p->pNextFrom){ ++ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ + if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break; + } + if( !p ) return; +@@ -119921,7 +134163,7 @@ static int fkParentIsModified( + if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){ + Column *pCol = &pTab->aCol[iKey]; + if( zKey ){ +- if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1; ++ if( 0==sqlite3StrICmp(pCol->zCnName, zKey) ) return 1; + }else if( pCol->colFlags & COLFLAG_PRIMKEY ){ + return 1; + } +@@ -119943,6 +134185,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){ + if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull) + || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull) + ){ ++ assert( (pTop->db->flags & SQLITE_FkNoAction)==0 ); + return 1; + } + } +@@ -119988,13 +134231,14 @@ SQLITE_PRIVATE void sqlite3FkCheck( + + /* If foreign-keys are disabled, this function is a no-op. */ + if( (db->flags&SQLITE_ForeignKeys)==0 ) return; ++ if( !IsOrdinaryTable(pTab) ) return; + + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zDb = db->aDb[iDb].zDbSName; + + /* Loop through all the foreign key constraints for which pTab is the + ** child table (the table that the foreign key definition is part of). */ +- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ ++ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ + Table *pTo; /* Parent table of foreign key pFKey */ + Index *pIdx = 0; /* Index on key columns in pTo */ + int *aiFree = 0; +@@ -120061,7 +134305,7 @@ SQLITE_PRIVATE void sqlite3FkCheck( + ** values read from the parent table are NULL. */ + if( db->xAuth ){ + int rcauth; +- char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; ++ char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zCnName; + rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); + bIgnore = (rcauth==SQLITE_IGNORE); + } +@@ -120125,10 +134369,10 @@ SQLITE_PRIVATE void sqlite3FkCheck( + ** child table as a SrcList for sqlite3WhereBegin() */ + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + if( pSrc ){ +- struct SrcList_item *pItem = pSrc->a; +- pItem->pTab = pFKey->pFrom; ++ SrcItem *pItem = pSrc->a; ++ pItem->pSTab = pFKey->pFrom; + pItem->zName = pFKey->pFrom->zName; +- pItem->pTab->nTabRef++; ++ pItem->pSTab->nTabRef++; + pItem->iCursor = pParse->nTab++; + + if( regNew!=0 ){ +@@ -120136,6 +134380,8 @@ SQLITE_PRIVATE void sqlite3FkCheck( + } + if( regOld!=0 ){ + int eAction = pFKey->aAction[aChange!=0]; ++ if( (db->flags & SQLITE_FkNoAction) ) eAction = OE_None; ++ + fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); + /* If this is a deferred FK constraint, or a CASCADE or SET NULL + ** action applies, then any foreign key violations caused by +@@ -120176,10 +134422,10 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask( + Table *pTab /* Table being modified */ + ){ + u32 mask = 0; +- if( pParse->db->flags&SQLITE_ForeignKeys ){ ++ if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ + FKey *p; + int i; +- for(p=pTab->pFKey; p; p=p->pNextFrom){ ++ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ + for(i=0; inCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); + } + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ +@@ -120213,7 +134459,9 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask( + ** + ** For an UPDATE, this function returns 2 if: + ** +-** * There are any FKs for which pTab is the child and the parent table, or ++** * There are any FKs for which pTab is the child and the parent table ++** and any FK processing at all is required (even of a different FK), or ++** + ** * the UPDATE modifies one or more parent keys for which the action is + ** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL). + ** +@@ -120225,36 +134473,41 @@ SQLITE_PRIVATE int sqlite3FkRequired( + int *aChange, /* Non-NULL for UPDATE operations */ + int chngRowid /* True for UPDATE that affects rowid */ + ){ +- int eRet = 0; +- if( pParse->db->flags&SQLITE_ForeignKeys ){ ++ int eRet = 1; /* Value to return if bHaveFK is true */ ++ int bHaveFK = 0; /* If FK processing is required */ ++ if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ + if( !aChange ){ + /* A DELETE operation. Foreign key processing is required if the + ** table in question is either the child or parent table for any + ** foreign key constraint. */ +- eRet = (sqlite3FkReferences(pTab) || pTab->pFKey); ++ bHaveFK = (sqlite3FkReferences(pTab) || pTab->u.tab.pFKey); + }else{ + /* This is an UPDATE. Foreign key processing is only required if the + ** operation modifies one or more child or parent key columns. */ + FKey *p; + + /* Check if any child key columns are being modified. */ +- for(p=pTab->pFKey; p; p=p->pNextFrom){ +- if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) return 2; ++ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ + if( fkChildIsModified(pTab, p, aChange, chngRowid) ){ +- eRet = 1; ++ if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2; ++ bHaveFK = 1; + } + } + + /* Check if any parent key columns are being modified. */ + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + if( fkParentIsModified(pTab, p, aChange, chngRowid) ){ +- if( p->aAction[1]!=OE_None ) return 2; +- eRet = 1; ++ if( (pParse->db->flags & SQLITE_FkNoAction)==0 ++ && p->aAction[1]!=OE_None ++ ){ ++ return 2; ++ } ++ bHaveFK = 1; + } + } + } + } +- return eRet; ++ return bHaveFK ? eRet : 0; + } + + /* +@@ -120266,9 +134519,9 @@ SQLITE_PRIVATE int sqlite3FkRequired( + ** + ** It returns a pointer to a Trigger structure containing a trigger + ** equivalent to the ON UPDATE or ON DELETE action specified by pFKey. +-** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is +-** returned (these actions require no special handling by the triggers +-** sub-system, code for them is created by fkScanChildren()). ++** If the action is "NO ACTION" then a NULL pointer is returned (these actions ++** require no special handling by the triggers sub-system, code for them is ++** created by fkScanChildren()). + ** + ** For example, if pFKey is the foreign key and pTab is table "p" in + ** the following schema: +@@ -120298,6 +134551,7 @@ static Trigger *fkActionTrigger( + int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */ + + action = pFKey->aAction[iAction]; ++ if( (db->flags & SQLITE_FkNoAction) ) action = OE_None; + if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){ + return 0; + } +@@ -120331,8 +134585,8 @@ static Trigger *fkActionTrigger( + assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKeynCol) ); + assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); + sqlite3TokenInit(&tToCol, +- pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName); +- sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName); ++ pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zCnName); ++ sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zCnName); + + /* Create the expression "OLD.zToCol = zFromCol". It is important + ** that the "OLD.zToCol" term is on the LHS of the = operator, so +@@ -120377,7 +134631,7 @@ static Trigger *fkActionTrigger( + testcase( pCol->colFlags & COLFLAG_STORED ); + pDflt = 0; + }else{ +- pDflt = pCol->pDflt; ++ pDflt = sqlite3ColumnExpr(pFKey->pFrom, pCol); + } + if( pDflt ){ + pNew = sqlite3ExprDup(db, pDflt, 0); +@@ -120397,18 +134651,25 @@ static Trigger *fkActionTrigger( + nFrom = sqlite3Strlen30(zFrom); + + if( action==OE_Restrict ){ +- Token tFrom; ++ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); ++ SrcList *pSrc; + Expr *pRaise; + +- tFrom.z = zFrom; +- tFrom.n = nFrom; +- pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); ++ pRaise = sqlite3Expr(db, TK_STRING, "FOREIGN KEY constraint failed"), ++ pRaise = sqlite3PExpr(pParse, TK_RAISE, pRaise, 0); + if( pRaise ){ + pRaise->affExpr = OE_Abort; + } ++ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); ++ if( pSrc ){ ++ assert( pSrc->nSrc==1 ); ++ pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom); ++ assert( pSrc->a[0].fg.fixedSchema==0 && pSrc->a[0].fg.isSubquery==0 ); ++ pSrc->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); ++ } + pSelect = sqlite3SelectNew(pParse, + sqlite3ExprListAppend(pParse, 0, pRaise), +- sqlite3SrcListAppend(pParse, 0, &tFrom, 0), ++ pSrc, + pWhere, + 0, 0, 0, 0, 0 + ); +@@ -120514,18 +134775,18 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){ + FKey *pFKey; /* Iterator variable */ + FKey *pNext; /* Copy of pFKey->pNextFrom */ + +- assert( db==0 || IsVirtual(pTab) +- || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); +- for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ ++ assert( IsOrdinaryTable(pTab) ); ++ assert( db!=0 ); ++ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pNext){ ++ assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); + + /* Remove the FK from the fkeyHash hash table. */ +- if( !db || db->pnBytesFreed==0 ){ ++ if( db->pnBytesFreed==0 ){ + if( pFKey->pPrevTo ){ + pFKey->pPrevTo->pNextTo = pFKey->pNextTo; + }else{ +- void *p = (void *)pFKey->pNextTo; +- const char *z = (p ? pFKey->pNextTo->zTo : pFKey->zTo); +- sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, p); ++ const char *z = (pFKey->pNextTo ? pFKey->pNextTo->zTo : pFKey->zTo); ++ sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, pFKey->pNextTo); + } + if( pFKey->pNextTo ){ + pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; +@@ -120585,17 +134846,20 @@ SQLITE_PRIVATE void sqlite3OpenTable( + ){ + Vdbe *v; + assert( !IsVirtual(pTab) ); +- v = sqlite3GetVdbe(pParse); ++ assert( pParse->pVdbe!=0 ); ++ v = pParse->pVdbe; + assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); +- sqlite3TableLock(pParse, iDb, pTab->tnum, +- (opcode==OP_OpenWrite)?1:0, pTab->zName); ++ if( !pParse->db->noSharedCache ){ ++ sqlite3TableLock(pParse, iDb, pTab->tnum, ++ (opcode==OP_OpenWrite)?1:0, pTab->zName); ++ } + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nNVCol); + VdbeComment((v, "%s", pTab->zName)); + }else{ + Index *pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); +- assert( pPk->tnum==pTab->tnum ); ++ assert( pPk->tnum==pTab->tnum || CORRUPT_DB ); + sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); + VdbeComment((v, "%s", pTab->zName)); +@@ -120622,85 +134886,139 @@ SQLITE_PRIVATE void sqlite3OpenTable( + ** is managed along with the rest of the Index structure. It will be + ** released when sqlite3DeleteIndex() is called. + */ +-SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ ++static SQLITE_NOINLINE const char *computeIndexAffStr(sqlite3 *db, Index *pIdx){ ++ /* The first time a column affinity string for a particular index is ++ ** required, it is allocated and populated here. It is then stored as ++ ** a member of the Index structure for subsequent use. ++ ** ++ ** The column affinity string will eventually be deleted by ++ ** sqliteDeleteIndex() when the Index structure itself is cleaned ++ ** up. ++ */ ++ int n; ++ Table *pTab = pIdx->pTable; ++ pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1); + if( !pIdx->zColAff ){ +- /* The first time a column affinity string for a particular index is +- ** required, it is allocated and populated here. It is then stored as +- ** a member of the Index structure for subsequent use. +- ** +- ** The column affinity string will eventually be deleted by +- ** sqliteDeleteIndex() when the Index structure itself is cleaned +- ** up. +- */ +- int n; +- Table *pTab = pIdx->pTable; +- pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1); +- if( !pIdx->zColAff ){ +- sqlite3OomFault(db); +- return 0; ++ sqlite3OomFault(db); ++ return 0; ++ } ++ for(n=0; nnColumn; n++){ ++ i16 x = pIdx->aiColumn[n]; ++ char aff; ++ if( x>=0 ){ ++ aff = pTab->aCol[x].affinity; ++ }else if( x==XN_ROWID ){ ++ aff = SQLITE_AFF_INTEGER; ++ }else{ ++ assert( x==XN_EXPR ); ++ assert( pIdx->bHasExpr ); ++ assert( pIdx->aColExpr!=0 ); ++ aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); + } +- for(n=0; nnColumn; n++){ +- i16 x = pIdx->aiColumn[n]; +- char aff; +- if( x>=0 ){ +- aff = pTab->aCol[x].affinity; +- }else if( x==XN_ROWID ){ +- aff = SQLITE_AFF_INTEGER; +- }else{ +- assert( x==XN_EXPR ); +- assert( pIdx->aColExpr!=0 ); +- aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); ++ if( affSQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC; ++ pIdx->zColAff[n] = aff; ++ } ++ pIdx->zColAff[n] = 0; ++ return pIdx->zColAff; ++} ++SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ ++ if( !pIdx->zColAff ) return computeIndexAffStr(db, pIdx); ++ return pIdx->zColAff; ++} ++ ++ ++/* ++** Compute an affinity string for a table. Space is obtained ++** from sqlite3DbMalloc(). The caller is responsible for freeing ++** the space when done. ++*/ ++SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3 *db, const Table *pTab){ ++ char *zColAff; ++ zColAff = (char *)sqlite3DbMallocRaw(db, pTab->nCol+1); ++ if( zColAff ){ ++ int i, j; ++ for(i=j=0; inCol; i++){ ++ if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ ++ zColAff[j++] = pTab->aCol[i].affinity; + } +- if( affSQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC; +- pIdx->zColAff[n] = aff; + } +- pIdx->zColAff[n] = 0; ++ do{ ++ zColAff[j--] = 0; ++ }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB ); + } +- +- return pIdx->zColAff; ++ return zColAff; + } + + /* ++** Make changes to the evolving bytecode to do affinity transformations ++** of values that are about to be gathered into a row for table pTab. ++** ++** For ordinary (legacy, non-strict) tables: ++** ----------------------------------------- ++** + ** Compute the affinity string for table pTab, if it has not already been + ** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities. + ** +-** If the affinity exists (if it is no entirely SQLITE_AFF_BLOB values) and +-** if iReg>0 then code an OP_Affinity opcode that will set the affinities +-** for register iReg and following. Or if affinities exists and iReg==0, ++** If the affinity string is empty (because it was all SQLITE_AFF_BLOB entries ++** which were then optimized out) then this routine becomes a no-op. ++** ++** Otherwise if iReg>0 then code an OP_Affinity opcode that will set the ++** affinities for register iReg and following. Or if iReg==0, + ** then just set the P4 operand of the previous opcode (which should be + ** an OP_MakeRecord) to the affinity string. + ** + ** A column affinity string has one character per column: + ** +-** Character Column affinity +-** ------------------------------ +-** 'A' BLOB +-** 'B' TEXT +-** 'C' NUMERIC +-** 'D' INTEGER +-** 'E' REAL ++** Character Column affinity ++** --------- --------------- ++** 'A' BLOB ++** 'B' TEXT ++** 'C' NUMERIC ++** 'D' INTEGER ++** 'E' REAL ++** ++** For STRICT tables: ++** ------------------ ++** ++** Generate an appropriate OP_TypeCheck opcode that will verify the ++** datatypes against the column definitions in pTab. If iReg==0, that ++** means an OP_MakeRecord opcode has already been generated and should be ++** the last opcode generated. The new OP_TypeCheck needs to be inserted ++** before the OP_MakeRecord. The new OP_TypeCheck should use the same ++** register set as the OP_MakeRecord. If iReg>0 then register iReg is ++** the first of a series of registers that will form the new record. ++** Apply the type checking to that array of registers. + */ + SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ +- int i, j; +- char *zColAff = pTab->zColAff; ++ int i; ++ char *zColAff; ++ if( pTab->tabFlags & TF_Strict ){ ++ if( iReg==0 ){ ++ /* Move the previous opcode (which should be OP_MakeRecord) forward ++ ** by one slot and insert a new OP_TypeCheck where the current ++ ** OP_MakeRecord is found */ ++ VdbeOp *pPrev; ++ sqlite3VdbeAppendP4(v, pTab, P4_TABLE); ++ pPrev = sqlite3VdbeGetLastOp(v); ++ assert( pPrev!=0 ); ++ assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed ); ++ pPrev->opcode = OP_TypeCheck; ++ sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3); ++ }else{ ++ /* Insert an isolated OP_Typecheck */ ++ sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol); ++ sqlite3VdbeAppendP4(v, pTab, P4_TABLE); ++ } ++ return; ++ } ++ zColAff = pTab->zColAff; + if( zColAff==0 ){ +- sqlite3 *db = sqlite3VdbeDb(v); +- zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1); ++ zColAff = sqlite3TableAffinityStr(0, pTab); + if( !zColAff ){ +- sqlite3OomFault(db); ++ sqlite3OomFault(sqlite3VdbeDb(v)); + return; + } +- +- for(i=j=0; inCol; i++){ +- assert( pTab->aCol[i].affinity!=0 ); +- if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ +- zColAff[j++] = pTab->aCol[i].affinity; +- } +- } +- do{ +- zColAff[j--] = 0; +- }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB ); + pTab->zColAff = zColAff; + } + assert( zColAff!=0 ); +@@ -120709,6 +135027,8 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ + if( iReg ){ + sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); + }else{ ++ assert( sqlite3VdbeGetLastOp(v)->opcode==OP_MakeRecord ++ || sqlite3VdbeDb(v)->mallocFailed ); + sqlite3VdbeChangeP4(v, -1, zColAff, i); + } + } +@@ -120792,24 +135112,30 @@ SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns( + ** that appropriate affinity has been applied to the regular columns + */ + sqlite3TableAffinity(pParse->pVdbe, pTab, iRegStore); +- if( (pTab->tabFlags & TF_HasStored)!=0 +- && (pOp = sqlite3VdbeGetOp(pParse->pVdbe,-1))->opcode==OP_Affinity +- ){ +- /* Change the OP_Affinity argument to '@' (NONE) for all stored +- ** columns. '@' is the no-op affinity and those columns have not +- ** yet been computed. */ +- int ii, jj; +- char *zP4 = pOp->p4.z; +- assert( zP4!=0 ); +- assert( pOp->p4type==P4_DYNAMIC ); +- for(ii=jj=0; zP4[jj]; ii++){ +- if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ +- continue; +- } +- if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ +- zP4[jj] = SQLITE_AFF_NONE; ++ if( (pTab->tabFlags & TF_HasStored)!=0 ){ ++ pOp = sqlite3VdbeGetLastOp(pParse->pVdbe); ++ if( pOp->opcode==OP_Affinity ){ ++ /* Change the OP_Affinity argument to '@' (NONE) for all stored ++ ** columns. '@' is the no-op affinity and those columns have not ++ ** yet been computed. */ ++ int ii, jj; ++ char *zP4 = pOp->p4.z; ++ assert( zP4!=0 ); ++ assert( pOp->p4type==P4_DYNAMIC ); ++ for(ii=jj=0; zP4[jj]; ii++){ ++ if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ ++ continue; ++ } ++ if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ ++ zP4[jj] = SQLITE_AFF_NONE; ++ } ++ jj++; + } +- jj++; ++ }else if( pOp->opcode==OP_TypeCheck ){ ++ /* If an OP_TypeCheck was generated because the table is STRICT, ++ ** then set the P3 operand to indicate that generated columns should ++ ** not be checked */ ++ pOp->p3 = 1; + } + } + +@@ -120845,7 +135171,7 @@ SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns( + int x; + pCol->colFlags |= COLFLAG_BUSY; + w.eCode = 0; +- sqlite3WalkExpr(&w, pCol->pDflt); ++ sqlite3WalkExpr(&w, sqlite3ColumnExpr(pTab, pCol)); + pCol->colFlags &= ~COLFLAG_BUSY; + if( w.eCode & COLFLAG_NOTAVAIL ){ + pRedo = pCol; +@@ -120854,13 +135180,13 @@ SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns( + eProgress = 1; + assert( pCol->colFlags & COLFLAG_GENERATED ); + x = sqlite3TableColumnToStorage(pTab, i) + iRegStore; +- sqlite3ExprCodeGeneratedColumn(pParse, pCol, x); ++ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, x); + pCol->colFlags &= ~COLFLAG_NOTAVAIL; + } + } + }while( pRedo && eProgress ); + if( pRedo ){ +- sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zName); ++ sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zCnName); + } + pParse->iSelfTab = 0; + } +@@ -120910,7 +135236,7 @@ static int autoIncBegin( + ** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */ + if( pSeqTab==0 + || !HasRowid(pSeqTab) +- || IsVirtual(pSeqTab) ++ || NEVER(IsVirtual(pSeqTab)) + || pSeqTab->nCol!=2 + ){ + pParse->nErr++; +@@ -120922,7 +135248,9 @@ static int autoIncBegin( + while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } + if( pInfo==0 ){ + pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo)); +- if( pInfo==0 ) return 0; ++ sqlite3ParserAddCleanup(pToplevel, sqlite3DbFree, pInfo); ++ testcase( pParse->earlyCleanup ); ++ if( pParse->db->mallocFailed ) return 0; + pInfo->pNext = pToplevel->pAinc; + pToplevel->pAinc = pInfo; + pInfo->pTab = pTab; +@@ -121063,6 +135391,210 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ + # define autoIncStep(A,B,C) + #endif /* SQLITE_OMIT_AUTOINCREMENT */ + ++/* ++** If argument pVal is a Select object returned by an sqlite3MultiValues() ++** that was able to use the co-routine optimization, finish coding the ++** co-routine. ++*/ ++SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ ++ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ ++ SrcItem *pItem = &pVal->pSrc->a[0]; ++ assert( (pItem->fg.isSubquery && pItem->u4.pSubq!=0) || pParse->nErr ); ++ if( pItem->fg.isSubquery ){ ++ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->u4.pSubq->regReturn); ++ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->u4.pSubq->addrFillSub - 1); ++ } ++ } ++} ++ ++/* ++** Return true if all expressions in the expression-list passed as the ++** only argument are constant. ++*/ ++static int exprListIsConstant(Parse *pParse, ExprList *pRow){ ++ int ii; ++ for(ii=0; iinExpr; ii++){ ++ if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0; ++ } ++ return 1; ++} ++ ++/* ++** Return true if all expressions in the expression-list passed as the ++** only argument are both constant and have no affinity. ++*/ ++static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){ ++ int ii; ++ if( exprListIsConstant(pParse,pRow)==0 ) return 0; ++ for(ii=0; iinExpr; ii++){ ++ Expr *pExpr = pRow->a[ii].pExpr; ++ assert( pExpr->op!=TK_RAISE ); ++ assert( pExpr->affExpr==0 ); ++ if( 0!=sqlite3ExprAffinity(pExpr) ) return 0; ++ } ++ return 1; ++ ++} ++ ++/* ++** This function is called by the parser for the second and subsequent ++** rows of a multi-row VALUES clause. Argument pLeft is the part of ++** the VALUES clause already parsed, argument pRow is the vector of values ++** for the new row. The Select object returned represents the complete ++** VALUES clause, including the new row. ++** ++** There are two ways in which this may be achieved - by incremental ++** coding of a co-routine (the "co-routine" method) or by returning a ++** Select object equivalent to the following (the "UNION ALL" method): ++** ++** "pLeft UNION ALL SELECT pRow" ++** ++** If the VALUES clause contains a lot of rows, this compound Select ++** object may consume a lot of memory. ++** ++** When the co-routine method is used, each row that will be returned ++** by the VALUES clause is coded into part of a co-routine as it is ++** passed to this function. The returned Select object is equivalent to: ++** ++** SELECT * FROM ( ++** Select object to read co-routine ++** ) ++** ++** The co-routine method is used in most cases. Exceptions are: ++** ++** a) If the current statement has a WITH clause. This is to avoid ++** statements like: ++** ++** WITH cte AS ( VALUES('x'), ('y') ... ) ++** SELECT * FROM cte AS a, cte AS b; ++** ++** This will not work, as the co-routine uses a hard-coded register ++** for its OP_Yield instructions, and so it is not possible for two ++** cursors to iterate through it concurrently. ++** ++** b) The schema is currently being parsed (i.e. the VALUES clause is part ++** of a schema item like a VIEW or TRIGGER). In this case there is no VM ++** being generated when parsing is taking place, and so generating ++** a co-routine is not possible. ++** ++** c) There are non-constant expressions in the VALUES clause (e.g. ++** the VALUES clause is part of a correlated sub-query). ++** ++** d) One or more of the values in the first row of the VALUES clause ++** has an affinity (i.e. is a CAST expression). This causes problems ++** because the complex rules SQLite uses (see function ++** sqlite3SubqueryColumnTypes() in select.c) to determine the effective ++** affinity of such a column for all rows require access to all values in ++** the column simultaneously. ++*/ ++SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){ ++ ++ if( pParse->bHasWith /* condition (a) above */ ++ || pParse->db->init.busy /* condition (b) above */ ++ || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */ ++ || (pLeft->pSrc->nSrc==0 && ++ exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */ ++ || IN_SPECIAL_PARSE ++ ){ ++ /* The co-routine method cannot be used. Fall back to UNION ALL. */ ++ Select *pSelect = 0; ++ int f = SF_Values | SF_MultiValue; ++ if( pLeft->pSrc->nSrc ){ ++ sqlite3MultiValuesEnd(pParse, pLeft); ++ f = SF_Values; ++ }else if( pLeft->pPrior ){ ++ /* In this case set the SF_MultiValue flag only if it was set on pLeft */ ++ f = (f & pLeft->selFlags); ++ } ++ pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); ++ pLeft->selFlags &= ~(u32)SF_MultiValue; ++ if( pSelect ){ ++ pSelect->op = TK_ALL; ++ pSelect->pPrior = pLeft; ++ pLeft = pSelect; ++ } ++ }else{ ++ SrcItem *p = 0; /* SrcItem that reads from co-routine */ ++ ++ if( pLeft->pSrc->nSrc==0 ){ ++ /* Co-routine has not yet been started and the special Select object ++ ** that accesses the co-routine has not yet been created. This block ++ ** does both those things. */ ++ Vdbe *v = sqlite3GetVdbe(pParse); ++ Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0); ++ ++ /* Ensure the database schema has been read. This is to ensure we have ++ ** the correct text encoding. */ ++ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){ ++ sqlite3ReadSchema(pParse); ++ } ++ ++ if( pRet ){ ++ SelectDest dest; ++ Subquery *pSubq; ++ pRet->pSrc->nSrc = 1; ++ pRet->pPrior = pLeft->pPrior; ++ pRet->op = pLeft->op; ++ if( pRet->pPrior ) pRet->selFlags |= SF_Values; ++ pLeft->pPrior = 0; ++ pLeft->op = TK_SELECT; ++ assert( pLeft->pNext==0 ); ++ assert( pRet->pNext==0 ); ++ p = &pRet->pSrc->a[0]; ++ p->fg.viaCoroutine = 1; ++ p->iCursor = -1; ++ assert( !p->fg.isIndexedBy && !p->fg.isTabFunc ); ++ p->u1.nRow = 2; ++ if( sqlite3SrcItemAttachSubquery(pParse, p, pLeft, 0) ){ ++ pSubq = p->u4.pSubq; ++ pSubq->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; ++ pSubq->regReturn = ++pParse->nMem; ++ sqlite3VdbeAddOp3(v, OP_InitCoroutine, ++ pSubq->regReturn, 0, pSubq->addrFillSub); ++ sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); ++ ++ /* Allocate registers for the output of the co-routine. Do so so ++ ** that there are two unused registers immediately before those ++ ** used by the co-routine. This allows the code in sqlite3Insert() ++ ** to use these registers directly, instead of copying the output ++ ** of the co-routine to a separate array for processing. */ ++ dest.iSdst = pParse->nMem + 3; ++ dest.nSdst = pLeft->pEList->nExpr; ++ pParse->nMem += 2 + dest.nSdst; ++ ++ pLeft->selFlags |= SF_MultiValue; ++ sqlite3Select(pParse, pLeft, &dest); ++ pSubq->regResult = dest.iSdst; ++ assert( pParse->nErr || dest.iSdst>0 ); ++ } ++ pLeft = pRet; ++ } ++ }else{ ++ p = &pLeft->pSrc->a[0]; ++ assert( !p->fg.isTabFunc && !p->fg.isIndexedBy ); ++ p->u1.nRow++; ++ } ++ ++ if( pParse->nErr==0 ){ ++ Subquery *pSubq; ++ assert( p!=0 ); ++ assert( p->fg.isSubquery ); ++ pSubq = p->u4.pSubq; ++ assert( pSubq!=0 ); ++ assert( pSubq->pSelect!=0 ); ++ assert( pSubq->pSelect->pEList!=0 ); ++ if( pSubq->pSelect->pEList->nExpr!=pRow->nExpr ){ ++ sqlite3SelectWrongNumTermsError(pParse, pSubq->pSelect); ++ }else{ ++ sqlite3ExprCodeExprList(pParse, pRow, pSubq->regResult, 0, 0); ++ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, pSubq->regReturn); ++ } ++ } ++ sqlite3ExprListDelete(pParse->db, pRow); ++ } ++ ++ return pLeft; ++} + + /* Forward declaration */ + static int xferOptimization( +@@ -121209,6 +135741,7 @@ SQLITE_PRIVATE void sqlite3Insert( + int regRowid; /* registers holding insert rowid */ + int regData; /* register holding first column to insert */ + int *aRegIdx = 0; /* One register allocated to each index */ ++ int *aTabColMap = 0; /* Mapping from pTab columns to pCol entries */ + + #ifndef SQLITE_OMIT_TRIGGER + int isView; /* True if attempting to insert into a view */ +@@ -121217,9 +135750,11 @@ SQLITE_PRIVATE void sqlite3Insert( + #endif + + db = pParse->db; +- if( pParse->nErr || db->mallocFailed ){ ++ assert( db->pParse==pParse ); ++ if( pParse->nErr ){ + goto insert_cleanup; + } ++ assert( db->mallocFailed==0 ); + dest.iSDParm = 0; /* Suppress a harmless compiler warning */ + + /* If the Select object is really just a simple VALUES() list with a +@@ -121253,7 +135788,7 @@ SQLITE_PRIVATE void sqlite3Insert( + */ + #ifndef SQLITE_OMIT_TRIGGER + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); +- isView = pTab->pSelect!=0; ++ isView = IsView(pTab); + #else + # define pTrigger 0 + # define tmask 0 +@@ -121265,6 +135800,14 @@ SQLITE_PRIVATE void sqlite3Insert( + #endif + assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); + ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x10000 ){ ++ sqlite3TreeViewLine(0, "In sqlite3Insert() at %s:%d", __FILE__, __LINE__); ++ sqlite3TreeViewInsert(pParse->pWith, pTabList, pColumn, pSelect, pList, ++ onError, pUpsert, pTrigger); ++ } ++#endif ++ + /* If pTab is really a view, make sure it has been initialized. + ** ViewGetColumnNames() is a no-op if pTab is not a view. + */ +@@ -121274,7 +135817,7 @@ SQLITE_PRIVATE void sqlite3Insert( + + /* Cannot insert into a read-only table. + */ +- if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ ++ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ + goto insert_cleanup; + } + +@@ -121295,7 +135838,11 @@ SQLITE_PRIVATE void sqlite3Insert( + ** + ** This is the 2nd template. + */ +- if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ ++ if( pColumn==0 ++ && pSelect!=0 ++ && pTrigger==0 ++ && xferOptimization(pParse, pTab, pSelect, onError, iDb) ++ ){ + assert( !pTrigger ); + assert( pList==0 ); + goto insert_end; +@@ -121339,35 +135886,31 @@ SQLITE_PRIVATE void sqlite3Insert( + */ + bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0; + if( pColumn ){ ++ aTabColMap = sqlite3DbMallocZero(db, pTab->nCol*sizeof(int)); ++ if( aTabColMap==0 ) goto insert_cleanup; + for(i=0; inId; i++){ +- pColumn->a[i].idx = -1; +- } +- for(i=0; inId; i++){ +- for(j=0; jnCol; j++){ +- if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ +- pColumn->a[i].idx = j; +- if( i!=j ) bIdListInOrder = 0; +- if( j==pTab->iPKey ){ +- ipkColumn = i; assert( !withoutRowid ); +- } ++ j = sqlite3ColumnIndex(pTab, pColumn->a[i].zName); ++ if( j>=0 ){ ++ if( aTabColMap[j]==0 ) aTabColMap[j] = i+1; ++ if( i!=j ) bIdListInOrder = 0; ++ if( j==pTab->iPKey ){ ++ ipkColumn = i; assert( !withoutRowid ); ++ } + #ifndef SQLITE_OMIT_GENERATED_COLUMNS +- if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ +- sqlite3ErrorMsg(pParse, +- "cannot INSERT into generated column \"%s\"", +- pTab->aCol[j].zName); +- goto insert_cleanup; +- } +-#endif +- break; ++ if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ ++ sqlite3ErrorMsg(pParse, ++ "cannot INSERT into generated column \"%s\"", ++ pTab->aCol[j].zCnName); ++ goto insert_cleanup; + } +- } +- if( j>=pTab->nCol ){ ++#endif ++ }else{ + if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){ + ipkColumn = i; + bIdListInOrder = 0; + }else{ + sqlite3ErrorMsg(pParse, "table %S has no column named %s", +- pTabList, 0, pColumn->a[i].zName); ++ pTabList->a, pColumn->a[i].zName); + pParse->checkSchema = 1; + goto insert_cleanup; + } +@@ -121383,23 +135926,45 @@ SQLITE_PRIVATE void sqlite3Insert( + if( pSelect ){ + /* Data is coming from a SELECT or from a multi-row VALUES clause. + ** Generate a co-routine to run the SELECT. */ +- int regYield; /* Register holding co-routine entry-point */ +- int addrTop; /* Top of the co-routine */ + int rc; /* Result code */ + +- regYield = ++pParse->nMem; +- addrTop = sqlite3VdbeCurrentAddr(v) + 1; +- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); +- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); +- dest.iSdst = bIdListInOrder ? regData : 0; +- dest.nSdst = pTab->nCol; +- rc = sqlite3Select(pParse, pSelect, &dest); +- regFromSelect = dest.iSdst; +- if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; +- sqlite3VdbeEndCoroutine(v, regYield); +- sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ +- assert( pSelect->pEList ); +- nColumn = pSelect->pEList->nExpr; ++ if( pSelect->pSrc->nSrc==1 ++ && pSelect->pSrc->a[0].fg.viaCoroutine ++ && pSelect->pPrior==0 ++ ){ ++ SrcItem *pItem = &pSelect->pSrc->a[0]; ++ Subquery *pSubq; ++ assert( pItem->fg.isSubquery ); ++ pSubq = pItem->u4.pSubq; ++ dest.iSDParm = pSubq->regReturn; ++ regFromSelect = pSubq->regResult; ++ assert( pSubq->pSelect!=0 ); ++ assert( pSubq->pSelect->pEList!=0 ); ++ nColumn = pSubq->pSelect->pEList->nExpr; ++ ExplainQueryPlan((pParse, 0, "SCAN %S", pItem)); ++ if( bIdListInOrder && nColumn==pTab->nCol ){ ++ regData = regFromSelect; ++ regRowid = regData - 1; ++ regIns = regRowid - (IsVirtual(pTab) ? 1 : 0); ++ } ++ }else{ ++ int addrTop; /* Top of the co-routine */ ++ int regYield = ++pParse->nMem; ++ addrTop = sqlite3VdbeCurrentAddr(v) + 1; ++ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); ++ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); ++ dest.iSdst = bIdListInOrder ? regData : 0; ++ dest.nSdst = pTab->nCol; ++ rc = sqlite3Select(pParse, pSelect, &dest); ++ regFromSelect = dest.iSdst; ++ assert( db->pParse==pParse ); ++ if( rc || pParse->nErr ) goto insert_cleanup; ++ assert( db->mallocFailed==0 ); ++ sqlite3VdbeEndCoroutine(v, regYield); ++ sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ ++ assert( pSelect->pEList ); ++ nColumn = pSelect->pEList->nExpr; ++ } + + /* Set useTempTable to TRUE if the result of the SELECT statement + ** should be written into a temporary table (template 4). Set to +@@ -121480,19 +136045,24 @@ SQLITE_PRIVATE void sqlite3Insert( + } + } + #endif +- } + +- /* Make sure the number of columns in the source data matches the number +- ** of columns to be inserted into the table. +- */ +- for(i=0; inCol; i++){ +- if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++; +- } +- if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){ +- sqlite3ErrorMsg(pParse, +- "table %S has %d columns but %d values were supplied", +- pTabList, 0, pTab->nCol-nHidden, nColumn); +- goto insert_cleanup; ++ /* Make sure the number of columns in the source data matches the number ++ ** of columns to be inserted into the table. ++ */ ++ assert( TF_HasHidden==COLFLAG_HIDDEN ); ++ assert( TF_HasGenerated==COLFLAG_GENERATED ); ++ assert( COLFLAG_NOINSERT==(COLFLAG_GENERATED|COLFLAG_HIDDEN) ); ++ if( (pTab->tabFlags & (TF_HasGenerated|TF_HasHidden))!=0 ){ ++ for(i=0; inCol; i++){ ++ if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++; ++ } ++ } ++ if( nColumn!=(pTab->nCol-nHidden) ){ ++ sqlite3ErrorMsg(pParse, ++ "table %S has %d columns but %d values were supplied", ++ pTabList->a, pTab->nCol-nHidden, nColumn); ++ goto insert_cleanup; ++ } + } + if( pColumn!=0 && nColumn!=pColumn->nId ){ + sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); +@@ -121504,6 +136074,7 @@ SQLITE_PRIVATE void sqlite3Insert( + if( (db->flags & SQLITE_CountRows)!=0 + && !pParse->nested + && !pParse->pTriggerTab ++ && !pParse->bReturning + ){ + regRowCount = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); +@@ -121527,12 +136098,13 @@ SQLITE_PRIVATE void sqlite3Insert( + } + #ifndef SQLITE_OMIT_UPSERT + if( pUpsert ){ ++ Upsert *pNx; + if( IsVirtual(pTab) ){ + sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", + pTab->zName); + goto insert_cleanup; + } +- if( pTab->pSelect ){ ++ if( IsView(pTab) ){ + sqlite3ErrorMsg(pParse, "cannot UPSERT a view"); + goto insert_cleanup; + } +@@ -121540,13 +136112,19 @@ SQLITE_PRIVATE void sqlite3Insert( + goto insert_cleanup; + } + pTabList->a[0].iCursor = iDataCur; +- pUpsert->pUpsertSrc = pTabList; +- pUpsert->regData = regData; +- pUpsert->iDataCur = iDataCur; +- pUpsert->iIdxCur = iIdxCur; +- if( pUpsert->pUpsertTarget ){ +- sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert); +- } ++ pNx = pUpsert; ++ do{ ++ pNx->pUpsertSrc = pTabList; ++ pNx->regData = regData; ++ pNx->iDataCur = iDataCur; ++ pNx->iIdxCur = iIdxCur; ++ if( pNx->pUpsertTarget ){ ++ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){ ++ goto insert_cleanup; ++ } ++ } ++ pNx = pNx->pNextUpsert; ++ }while( pNx!=0 ); + } + #endif + +@@ -121624,23 +136202,30 @@ SQLITE_PRIVATE void sqlite3Insert( + continue; + }else if( pColumn==0 ){ + /* Hidden columns that are not explicitly named in the INSERT +- ** get there default value */ +- sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); ++ ** get their default value */ ++ sqlite3ExprCodeFactorable(pParse, ++ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), ++ iRegStore); + continue; + } + } + if( pColumn ){ +- for(j=0; jnId && pColumn->a[j].idx!=i; j++){} +- if( j>=pColumn->nId ){ ++ j = aTabColMap[i]; ++ assert( j>=0 && j<=pColumn->nId ); ++ if( j==0 ){ + /* A column not named in the insert column list gets its + ** default value */ +- sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); ++ sqlite3ExprCodeFactorable(pParse, ++ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), ++ iRegStore); + continue; + } +- k = j; ++ k = j - 1; + }else if( nColumn==0 ){ + /* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */ +- sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); ++ sqlite3ExprCodeFactorable(pParse, ++ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), ++ iRegStore); + continue; + }else{ + k = i - nHidden; +@@ -121653,7 +136238,12 @@ SQLITE_PRIVATE void sqlite3Insert( + sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+k, iRegStore); + } + }else{ +- sqlite3ExprCode(pParse, pList->a[k].pExpr, iRegStore); ++ Expr *pX = pList->a[k].pExpr; ++ int y = sqlite3ExprCodeTarget(pParse, pX, iRegStore); ++ if( y!=iRegStore ){ ++ sqlite3VdbeAddOp2(v, ++ ExprHasProperty(pX, EP_Subquery) ? OP_Copy : OP_SCopy, y, iRegStore); ++ } + } + } + +@@ -121687,13 +136277,8 @@ SQLITE_PRIVATE void sqlite3Insert( + sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v); + } + +- /* Cannot have triggers on a virtual table. If it were possible, +- ** this block would have to account for hidden column. +- */ +- assert( !IsVirtual(pTab) ); +- + /* Copy the new data already generated. */ +- assert( pTab->nNVCol>0 ); ++ assert( pTab->nNVCol>0 || pParse->nErr>0 ); + sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1); + + #ifndef SQLITE_OMIT_GENERATED_COLUMNS +@@ -121790,12 +136375,14 @@ SQLITE_PRIVATE void sqlite3Insert( + }else + #endif + { +- int isReplace; /* Set to true if constraints may cause a replace */ ++ int isReplace = 0;/* Set to true if constraints may cause a replace */ + int bUseSeek; /* True to use OPFLAG_SEEKRESULT */ + sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, + regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert + ); +- sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); ++ if( db->flags & SQLITE_ForeignKeys ){ ++ sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); ++ } + + /* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE + ** constraints or (b) there are no triggers and this table is not a +@@ -121810,6 +136397,13 @@ SQLITE_PRIVATE void sqlite3Insert( + regIns, aRegIdx, 0, appendFlag, bUseSeek + ); + } ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ }else if( pParse->bReturning ){ ++ /* If there is a RETURNING clause, populate the rowid register with ++ ** constant value -1, in case one or more of the returned expressions ++ ** refer to the "rowid" of the view. */ ++ sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid); ++#endif + } + + /* Update the count of rows that are inserted +@@ -121846,7 +136440,9 @@ SQLITE_PRIVATE void sqlite3Insert( + sqlite3VdbeJumpHere(v, addrInsTop); + } + ++#ifndef SQLITE_OMIT_XFER_OPT + insert_end: ++#endif /* SQLITE_OMIT_XFER_OPT */ + /* Update the sqlite_sequence table by storing the content of the + ** maximum rowid counter values recorded while inserting into + ** autoincrement tables. +@@ -121861,9 +136457,7 @@ insert_end: + ** invoke the callback function. + */ + if( regRowCount ){ +- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); +- sqlite3VdbeSetNumCols(v, 1); +- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); ++ sqlite3CodeChangeCount(v, regRowCount, "rows inserted"); + } + + insert_cleanup: +@@ -121871,8 +136465,11 @@ insert_cleanup: + sqlite3ExprListDelete(db, pList); + sqlite3UpsertDelete(db, pUpsert); + sqlite3SelectDelete(db, pSelect); +- sqlite3IdListDelete(db, pColumn); +- sqlite3DbFree(db, aRegIdx); ++ if( pColumn ){ ++ sqlite3IdListDelete(db, pColumn); ++ sqlite3DbFree(db, aTabColMap); ++ } ++ if( aRegIdx ) sqlite3DbNNFreeNN(db, aRegIdx); + } + + /* Make sure "isView" and other macros defined above are undefined. Otherwise +@@ -121898,7 +136495,7 @@ insert_cleanup: + /* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn(). + * Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this + ** expression node references any of the +-** columns that are being modifed by an UPDATE statement. ++** columns that are being modified by an UPDATE statement. + */ + static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_COLUMN ){ +@@ -121951,6 +136548,70 @@ SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn( + return w.eCode!=0; + } + ++/* ++** The sqlite3GenerateConstraintChecks() routine usually wants to visit ++** the indexes of a table in the order provided in the Table->pIndex list. ++** However, sometimes (rarely - when there is an upsert) it wants to visit ++** the indexes in a different order. The following data structures accomplish ++** this. ++** ++** The IndexIterator object is used to walk through all of the indexes ++** of a table in either Index.pNext order, or in some other order established ++** by an array of IndexListTerm objects. ++*/ ++typedef struct IndexListTerm IndexListTerm; ++typedef struct IndexIterator IndexIterator; ++struct IndexIterator { ++ int eType; /* 0 for Index.pNext list. 1 for an array of IndexListTerm */ ++ int i; /* Index of the current item from the list */ ++ union { ++ struct { /* Use this object for eType==0: A Index.pNext list */ ++ Index *pIdx; /* The current Index */ ++ } lx; ++ struct { /* Use this object for eType==1; Array of IndexListTerm */ ++ int nIdx; /* Size of the array */ ++ IndexListTerm *aIdx; /* Array of IndexListTerms */ ++ } ax; ++ } u; ++}; ++ ++/* When IndexIterator.eType==1, then each index is an array of instances ++** of the following object ++*/ ++struct IndexListTerm { ++ Index *p; /* The index */ ++ int ix; /* Which entry in the original Table.pIndex list is this index*/ ++}; ++ ++/* Return the first index on the list */ ++static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){ ++ assert( pIter->i==0 ); ++ if( pIter->eType ){ ++ *pIx = pIter->u.ax.aIdx[0].ix; ++ return pIter->u.ax.aIdx[0].p; ++ }else{ ++ *pIx = 0; ++ return pIter->u.lx.pIdx; ++ } ++} ++ ++/* Return the next index from the list. Return NULL when out of indexes */ ++static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){ ++ if( pIter->eType ){ ++ int i = ++pIter->i; ++ if( i>=pIter->u.ax.nIdx ){ ++ *pIx = i; ++ return 0; ++ } ++ *pIx = pIter->u.ax.aIdx[i].ix; ++ return pIter->u.ax.aIdx[i].p; ++ }else{ ++ ++(*pIx); ++ pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext; ++ return pIter->u.lx.pIdx; ++ } ++} ++ + /* + ** Generate code to do constraint checks prior to an INSERT or an UPDATE + ** on table pTab. +@@ -122057,9 +136718,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + int *aiChng, /* column i is unchanged if aiChng[i]<0 */ + Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */ + ){ +- Vdbe *v; /* VDBE under constrution */ ++ Vdbe *v; /* VDBE under construction */ + Index *pIdx; /* Pointer to one of the indices */ +- Index *pPk = 0; /* The PRIMARY KEY index */ ++ Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */ + sqlite3 *db; /* Database connection */ + int i; /* loop counter */ + int ix; /* Index loop counter */ +@@ -122067,11 +136728,11 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + int onError; /* Conflict resolution strategy */ + int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ + int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ +- Index *pUpIdx = 0; /* Index to which to apply the upsert */ +- u8 isUpdate; /* True if this is an UPDATE operation */ ++ Upsert *pUpsertClause = 0; /* The specific ON CONFLICT clause for pIdx */ ++ u8 isUpdate; /* True if this is an UPDATE operation */ + u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */ +- int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */ +- int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */ ++ int upsertIpkReturn = 0; /* Address of Goto at end of IPK uniqueness check */ ++ int upsertIpkDelay = 0; /* Address of Goto to bypass initial IPK check */ + int ipkTop = 0; /* Top of the IPK uniqueness check */ + int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */ + /* Variables associated with retesting uniqueness constraints after +@@ -122081,12 +136742,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */ + Trigger *pTrigger; /* List of DELETE triggers on the table pTab */ + int nReplaceTrig = 0; /* Number of replace triggers coded */ ++ IndexIterator sIdxIter; /* Index iterator */ + + isUpdate = regOldData!=0; + db = pParse->db; +- v = sqlite3GetVdbe(pParse); ++ v = pParse->pVdbe; + assert( v!=0 ); +- assert( pTab->pSelect==0 ); /* This table is not a VIEW */ ++ assert( !IsView(pTab) ); /* This table is not a VIEW */ + nCol = pTab->nCol; + + /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for +@@ -122137,7 +136799,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + } + if( onError==OE_Replace ){ + if( b2ndPass /* REPLACE becomes ABORT on the 2nd pass */ +- || pCol->pDflt==0 /* REPLACE is ABORT if no DEFAULT value */ ++ || pCol->iDflt==0 /* REPLACE is ABORT if no DEFAULT value */ + ){ + testcase( pCol->colFlags & COLFLAG_VIRTUAL ); + testcase( pCol->colFlags & COLFLAG_STORED ); +@@ -122159,7 +136821,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + VdbeCoverage(v); + assert( (pCol->colFlags & COLFLAG_GENERATED)==0 ); + nSeenReplace++; +- sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg); ++ sqlite3ExprCodeCopy(pParse, ++ sqlite3ColumnExpr(pTab, pCol), iReg); + sqlite3VdbeJumpHere(v, addr1); + break; + } +@@ -122169,7 +136832,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + case OE_Rollback: + case OE_Fail: { + char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, +- pCol->zName); ++ pCol->zCnName); ++ testcase( zMsg==0 && db->mallocFailed==0 ); + sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, + onError, iReg); + sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); +@@ -122238,7 +136902,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + sqlite3VdbeGoto(v, ignoreDest); + }else{ + char *zName = pCheck->a[i].zEName; +- if( zName==0 ) zName = pTab->zName; ++ assert( zName!=0 || pParse->db->mallocFailed ); + if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-26383-51744 */ + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK, + onError, zName, P4_TRANSIENT, +@@ -122263,7 +136927,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + ** could happen in any order, but they are grouped up front for + ** convenience. + ** +- ** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43 ++ ** 2018-08-14: Ticket https://sqlite.org/src/info/908f001483982c43 + ** The order of constraints used to have OE_Update as (2) and OE_Abort + ** and so forth as (1). But apparently PostgreSQL checks the OE_Update + ** constraint before any others, so it had to be moved. +@@ -122278,19 +136942,63 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + ** list of indexes attached to a table puts all OE_Replace indexes last + ** in the list. See sqlite3CreateIndex() for where that happens. + */ +- ++ sIdxIter.eType = 0; ++ sIdxIter.i = 0; ++ sIdxIter.u.ax.aIdx = 0; /* Silence harmless compiler warning */ ++ sIdxIter.u.lx.pIdx = pTab->pIndex; + if( pUpsert ){ + if( pUpsert->pUpsertTarget==0 ){ +- /* An ON CONFLICT DO NOTHING clause, without a constraint-target. +- ** Make all unique constraint resolution be OE_Ignore */ +- assert( pUpsert->pUpsertSet==0 ); +- overrideError = OE_Ignore; +- pUpsert = 0; +- }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){ +- /* If the constraint-target uniqueness check must be run first. +- ** Jump to that uniqueness check now */ +- upsertJump = sqlite3VdbeAddOp0(v, OP_Goto); +- VdbeComment((v, "UPSERT constraint goes first")); ++ /* There is just on ON CONFLICT clause and it has no constraint-target */ ++ assert( pUpsert->pNextUpsert==0 ); ++ if( pUpsert->isDoUpdate==0 ){ ++ /* A single ON CONFLICT DO NOTHING clause, without a constraint-target. ++ ** Make all unique constraint resolution be OE_Ignore */ ++ overrideError = OE_Ignore; ++ pUpsert = 0; ++ }else{ ++ /* A single ON CONFLICT DO UPDATE. Make all resolutions OE_Update */ ++ overrideError = OE_Update; ++ } ++ }else if( pTab->pIndex!=0 ){ ++ /* Otherwise, we'll need to run the IndexListTerm array version of the ++ ** iterator to ensure that all of the ON CONFLICT conditions are ++ ** checked first and in order. */ ++ int nIdx, jj; ++ u64 nByte; ++ Upsert *pTerm; ++ u8 *bUsed; ++ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ ++ assert( aRegIdx[nIdx]>0 ); ++ } ++ sIdxIter.eType = 1; ++ sIdxIter.u.ax.nIdx = nIdx; ++ nByte = (sizeof(IndexListTerm)+1)*nIdx + nIdx; ++ sIdxIter.u.ax.aIdx = sqlite3DbMallocZero(db, nByte); ++ if( sIdxIter.u.ax.aIdx==0 ) return; /* OOM */ ++ bUsed = (u8*)&sIdxIter.u.ax.aIdx[nIdx]; ++ pUpsert->pToFree = sIdxIter.u.ax.aIdx; ++ for(i=0, pTerm=pUpsert; pTerm; pTerm=pTerm->pNextUpsert){ ++ if( pTerm->pUpsertTarget==0 ) break; ++ if( pTerm->pUpsertIdx==0 ) continue; /* Skip ON CONFLICT for the IPK */ ++ jj = 0; ++ pIdx = pTab->pIndex; ++ while( ALWAYS(pIdx!=0) && pIdx!=pTerm->pUpsertIdx ){ ++ pIdx = pIdx->pNext; ++ jj++; ++ } ++ if( bUsed[jj] ) continue; /* Duplicate ON CONFLICT clause ignored */ ++ bUsed[jj] = 1; ++ sIdxIter.u.ax.aIdx[i].p = pIdx; ++ sIdxIter.u.ax.aIdx[i].ix = jj; ++ i++; ++ } ++ for(jj=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, jj++){ ++ if( bUsed[jj] ) continue; ++ sIdxIter.u.ax.aIdx[i].p = pIdx; ++ sIdxIter.u.ax.aIdx[i].ix = jj; ++ i++; ++ } ++ assert( i==nIdx ); + } + } + +@@ -122353,11 +137061,20 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + } + + /* figure out whether or not upsert applies in this case */ +- if( pUpsert && pUpsert->pUpsertIdx==0 ){ +- if( pUpsert->pUpsertSet==0 ){ +- onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ +- }else{ +- onError = OE_Update; /* DO UPDATE */ ++ if( pUpsert ){ ++ pUpsertClause = sqlite3UpsertOfIndex(pUpsert,0); ++ if( pUpsertClause!=0 ){ ++ if( pUpsertClause->isDoUpdate==0 ){ ++ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ ++ }else{ ++ onError = OE_Update; /* DO UPDATE */ ++ } ++ } ++ if( pUpsertClause!=pUpsert ){ ++ /* The first ON CONFLICT clause has a conflict target other than ++ ** the IPK. We have to jump ahead to that first ON CONFLICT clause ++ ** and then come back here and deal with the IPK afterwards */ ++ upsertIpkDelay = sqlite3VdbeAddOp0(v, OP_Goto); + } + } + +@@ -122367,8 +137084,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + ** the UNIQUE constraints have run. + */ + if( onError==OE_Replace /* IPK rule is REPLACE */ +- && onError!=overrideError /* Rules for other contraints are different */ ++ && onError!=overrideError /* Rules for other constraints are different */ + && pTab->pIndex /* There exist other constraints */ ++ && !upsertIpkDelay /* IPK check already deferred by UPSERT */ + ){ + ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1; + VdbeComment((v, "defer IPK REPLACE until last")); +@@ -122464,7 +137182,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + } + } + sqlite3VdbeResolveLabel(v, addrRowidOk); +- if( ipkTop ){ ++ if( pUpsert && pUpsertClause!=pUpsert ){ ++ upsertIpkReturn = sqlite3VdbeAddOp0(v, OP_Goto); ++ }else if( ipkTop ){ + ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeJumpHere(v, ipkTop-1); + } +@@ -122477,23 +137197,25 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + ** This loop also handles the case of the PRIMARY KEY index for a + ** WITHOUT ROWID table. + */ +- for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ +- int regIdx; /* Range of registers hold conent for pIdx */ ++ for(pIdx = indexIteratorFirst(&sIdxIter, &ix); ++ pIdx; ++ pIdx = indexIteratorNext(&sIdxIter, &ix) ++ ){ ++ int regIdx; /* Range of registers holding content for pIdx */ + int regR; /* Range of registers holding conflicting PK */ + int iThisCur; /* Cursor for this UNIQUE index */ + int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ + int addrConflictCk; /* First opcode in the conflict check logic */ + + if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ +- if( pUpIdx==pIdx ){ +- addrUniqueOk = upsertJump+1; +- upsertBypass = sqlite3VdbeGoto(v, 0); +- VdbeComment((v, "Skip upsert subroutine")); +- sqlite3VdbeJumpHere(v, upsertJump); +- }else{ +- addrUniqueOk = sqlite3VdbeMakeLabel(pParse); ++ if( pUpsert ){ ++ pUpsertClause = sqlite3UpsertOfIndex(pUpsert, pIdx); ++ if( upsertIpkDelay && pUpsertClause==pUpsert ){ ++ sqlite3VdbeJumpHere(v, upsertIpkDelay); ++ } + } +- if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ ++ addrUniqueOk = sqlite3VdbeMakeLabel(pParse); ++ if( bAffinityDone==0 ){ + sqlite3TableAffinity(v, pTab, regNewData+1); + bAffinityDone = 1; + } +@@ -122530,7 +137252,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + testcase( sqlite3TableColumnToStorage(pTab, iField)!=iField ); + x = sqlite3TableColumnToStorage(pTab, iField) + regNewData + 1; + sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i); +- VdbeComment((v, "%s", pTab->aCol[iField].zName)); ++ VdbeComment((v, "%s", pTab->aCol[iField].zCnName)); + } + } + sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); +@@ -122564,8 +137286,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + } + + /* Figure out if the upsert clause applies to this index */ +- if( pUpIdx==pIdx ){ +- if( pUpsert->pUpsertSet==0 ){ ++ if( pUpsertClause ){ ++ if( pUpsertClause->isDoUpdate==0 ){ + onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ + }else{ + onError = OE_Update; /* DO UPDATE */ +@@ -122582,6 +137304,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + ** This is not possible for ENABLE_PREUPDATE_HOOK builds, as the row + ** must be explicitly deleted in order to ensure any pre-update hook + ** is invoked. */ ++ assert( IsOrdinaryTable(pTab) ); + #ifndef SQLITE_ENABLE_PREUPDATE_HOOK + if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ + && pPk==pIdx /* Condition 2 */ +@@ -122589,7 +137312,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ + 0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) + && ( 0==(db->flags&SQLITE_ForeignKeys) || /* Condition 5 */ +- (0==pTab->pFKey && 0==sqlite3FkReferences(pTab))) ++ (0==pTab->u.tab.pFKey && 0==sqlite3FkReferences(pTab))) + ){ + sqlite3VdbeResolveLabel(v, addrUniqueOk); + continue; +@@ -122603,7 +137326,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + regIdx, pIdx->nKeyCol); VdbeCoverage(v); + + /* Generate code to handle collisions */ +- regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); ++ regR = pIdx==pPk ? regIdx : sqlite3GetTempRange(pParse, nPkField); + if( isUpdate || onError==OE_Replace ){ + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); +@@ -122624,13 +137347,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + x = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); + sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); + VdbeComment((v, "%s.%s", pTab->zName, +- pTab->aCol[pPk->aiColumn[i]].zName)); ++ pTab->aCol[pPk->aiColumn[i]].zCnName)); + } + } + if( isUpdate ){ + /* If currently processing the PRIMARY KEY of a WITHOUT ROWID + ** table, only conflict if the new PRIMARY KEY values are actually +- ** different from the old. ++ ** different from the old. See TH3 withoutrowid04.test. + ** + ** For a UNIQUE index, only conflict if the PRIMARY KEY values + ** of the matched index row are different from the original PRIMARY +@@ -122688,7 +137411,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + + assert( onError==OE_Replace ); + nConflictCk = sqlite3VdbeCurrentAddr(v) - addrConflictCk; +- assert( nConflictCk>0 ); ++ assert( nConflictCk>0 || db->mallocFailed ); ++ testcase( nConflictCk<=0 ); + testcase( nConflictCk>1 ); + if( regTrigCnt ){ + sqlite3MultiWrite(pParse); +@@ -122755,19 +137479,23 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( + break; + } + } +- if( pUpIdx==pIdx ){ +- sqlite3VdbeGoto(v, upsertJump+1); +- sqlite3VdbeJumpHere(v, upsertBypass); +- }else{ +- sqlite3VdbeResolveLabel(v, addrUniqueOk); +- } ++ sqlite3VdbeResolveLabel(v, addrUniqueOk); + if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField); ++ if( pUpsertClause ++ && upsertIpkReturn ++ && sqlite3UpsertNextIsIPK(pUpsertClause) ++ ){ ++ sqlite3VdbeGoto(v, upsertIpkDelay+1); ++ sqlite3VdbeJumpHere(v, upsertIpkReturn); ++ upsertIpkReturn = 0; ++ } + } + + /* If the IPK constraint is a REPLACE, run it last */ + if( ipkTop ){ + sqlite3VdbeGoto(v, ipkTop); + VdbeComment((v, "Do IPK REPLACE")); ++ assert( ipkBottom>0 ); + sqlite3VdbeJumpHere(v, ipkBottom); + } + +@@ -122820,13 +137548,39 @@ SQLITE_PRIVATE void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){ + if( pTab->pSchema->file_format<2 ) return; + + for(i=pTab->nCol-1; i>0; i--){ +- if( pTab->aCol[i].pDflt!=0 ) break; ++ if( pTab->aCol[i].iDflt!=0 ) break; + if( pTab->aCol[i].colFlags & COLFLAG_PRIMKEY ) break; + } + sqlite3VdbeChangeP5(v, i+1); + } + #endif + ++/* ++** Table pTab is a WITHOUT ROWID table that is being written to. The cursor ++** number is iCur, and register regData contains the new record for the ++** PK index. This function adds code to invoke the pre-update hook, ++** if one is registered. ++*/ ++#ifdef SQLITE_ENABLE_PREUPDATE_HOOK ++static void codeWithoutRowidPreupdate( ++ Parse *pParse, /* Parse context */ ++ Table *pTab, /* Table being updated */ ++ int iCur, /* Cursor number for table */ ++ int regData /* Data containing new record */ ++){ ++ Vdbe *v = pParse->pVdbe; ++ int r = sqlite3GetTempReg(pParse); ++ assert( !HasRowid(pTab) ); ++ assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB ); ++ sqlite3VdbeAddOp2(v, OP_Integer, 0, r); ++ sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE); ++ sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); ++ sqlite3ReleaseTempReg(pParse, r); ++} ++#else ++# define codeWithoutRowidPreupdate(a,b,c,d) ++#endif ++ + /* + ** This routine generates code to finish the INSERT or UPDATE operation + ** that was started by a prior call to sqlite3GenerateConstraintChecks. +@@ -122857,9 +137611,9 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( + || update_flags==(OPFLAG_ISUPDATE|OPFLAG_SAVEPOSITION) + ); + +- v = sqlite3GetVdbe(pParse); ++ v = pParse->pVdbe; + assert( v!=0 ); +- assert( pTab->pSelect==0 ); /* This table is not a VIEW */ ++ assert( !IsView(pTab) ); /* This table is not a VIEW */ + for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + /* All REPLACE indexes are at the end of the list */ + assert( pIdx->onError!=OE_Replace +@@ -122872,20 +137626,11 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( + } + pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0); + if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ +- assert( pParse->nested==0 ); + pik_flags |= OPFLAG_NCHANGE; + pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); +-#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + if( update_flags==0 ){ +- int r = sqlite3GetTempReg(pParse); +- sqlite3VdbeAddOp2(v, OP_Integer, 0, r); +- sqlite3VdbeAddOp4(v, OP_Insert, +- iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE +- ); +- sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); +- sqlite3ReleaseTempReg(pParse, r); ++ codeWithoutRowidPreupdate(pParse, pTab, iIdxCur+i, aRegIdx[i]); + } +-#endif + } + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i], + aRegIdx[i]+1, +@@ -122951,29 +137696,32 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices( + + assert( op==OP_OpenRead || op==OP_OpenWrite ); + assert( op==OP_OpenWrite || p5==0 ); ++ assert( piDataCur!=0 ); ++ assert( piIdxCur!=0 ); + if( IsVirtual(pTab) ){ + /* This routine is a no-op for virtual tables. Leave the output +- ** variables *piDataCur and *piIdxCur uninitialized so that valgrind +- ** can detect if they are used by mistake in the caller. */ ++ ** variables *piDataCur and *piIdxCur set to illegal cursor numbers ++ ** for improved error detection. */ ++ *piDataCur = *piIdxCur = -999; + return 0; + } + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); +- v = sqlite3GetVdbe(pParse); ++ v = pParse->pVdbe; + assert( v!=0 ); + if( iBase<0 ) iBase = pParse->nTab; + iDataCur = iBase++; +- if( piDataCur ) *piDataCur = iDataCur; ++ *piDataCur = iDataCur; + if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){ + sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op); +- }else{ ++ }else if( pParse->db->noSharedCache==0 ){ + sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); + } +- if( piIdxCur ) *piIdxCur = iBase; ++ *piIdxCur = iBase; + for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + int iIdxCur = iBase++; + assert( pIdx->pSchema==pTab->pSchema ); + if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ +- if( piDataCur ) *piDataCur = iIdxCur; ++ *piDataCur = iIdxCur; + p5 = 0; + } + if( aToOpen==0 || aToOpen[i+1] ){ +@@ -123083,7 +137831,7 @@ static int xferOptimization( + ExprList *pEList; /* The result set of the SELECT */ + Table *pSrc; /* The table in the FROM clause of SELECT */ + Index *pSrcIdx, *pDestIdx; /* Source and destination indices */ +- struct SrcList_item *pItem; /* An element of pSelect->pSrc */ ++ SrcItem *pItem; /* An element of pSelect->pSrc */ + int i; /* Loop counter */ + int iDbSrc; /* The database of pSrc */ + int iSrc, iDest; /* Cursors from source and destination */ +@@ -123095,18 +137843,13 @@ static int xferOptimization( + int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ + int regData, regRowid; /* Registers holding data and rowid */ + +- if( pSelect==0 ){ +- return 0; /* Must be of the form INSERT INTO ... SELECT ... */ +- } ++ assert( pSelect!=0 ); + if( pParse->pWith || pSelect->pWith ){ + /* Do not attempt to process this query if there are an WITH clauses + ** attached to it. Proceeding may generate a false "no such table: xxx" + ** error if pSelect reads from a CTE named "xxx". */ + return 0; + } +- if( sqlite3TriggerList(pParse, pDest) ){ +- return 0; /* tab1 must not have triggers */ +- } + #ifndef SQLITE_OMIT_VIRTUALTABLE + if( IsVirtual(pDest) ){ + return 0; /* tab1 must not be a virtual table */ +@@ -123120,7 +137863,7 @@ static int xferOptimization( + if( pSelect->pSrc->nSrc!=1 ){ + return 0; /* FROM clause must have exactly one term */ + } +- if( pSelect->pSrc->a[0].pSelect ){ ++ if( pSelect->pSrc->a[0].fg.isSubquery ){ + return 0; /* FROM clause cannot contain a subquery */ + } + if( pSelect->pWhere ){ +@@ -123169,13 +137912,8 @@ static int xferOptimization( + if( HasRowid(pDest)!=HasRowid(pSrc) ){ + return 0; /* source and destination must both be WITHOUT ROWID or not */ + } +-#ifndef SQLITE_OMIT_VIRTUALTABLE +- if( IsVirtual(pSrc) ){ +- return 0; /* tab2 must not be a virtual table */ +- } +-#endif +- if( pSrc->pSelect ){ +- return 0; /* tab2 may not be a view */ ++ if( !IsOrdinaryTable(pSrc) ){ ++ return 0; /* tab2 may not be a view or virtual table */ + } + if( pDest->nCol!=pSrc->nCol ){ + return 0; /* Number of columns must be the same in tab1 and tab2 */ +@@ -123183,6 +137921,9 @@ static int xferOptimization( + if( pDest->iPKey!=pSrc->iPKey ){ + return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ + } ++ if( (pDest->tabFlags & TF_Strict)!=0 && (pSrc->tabFlags & TF_Strict)==0 ){ ++ return 0; /* Cannot feed from a non-strict into a strict table */ ++ } + for(i=0; inCol; i++){ + Column *pDestCol = &pDest->aCol[i]; + Column *pSrcCol = &pSrc->aCol[i]; +@@ -123219,7 +137960,9 @@ static int xferOptimization( + ** This requirement could be relaxed for VIRTUAL columns, I suppose. + */ + if( (pDestCol->colFlags & COLFLAG_GENERATED)!=0 ){ +- if( sqlite3ExprCompare(0, pSrcCol->pDflt, pDestCol->pDflt, -1)!=0 ){ ++ if( sqlite3ExprCompare(0, ++ sqlite3ColumnExpr(pSrc, pSrcCol), ++ sqlite3ColumnExpr(pDest, pDestCol), -1)!=0 ){ + testcase( pDestCol->colFlags & COLFLAG_VIRTUAL ); + testcase( pDestCol->colFlags & COLFLAG_STORED ); + return 0; /* Different generator expressions */ +@@ -123229,7 +137972,8 @@ static int xferOptimization( + if( pDestCol->affinity!=pSrcCol->affinity ){ + return 0; /* Affinity must be the same on all columns */ + } +- if( sqlite3_stricmp(pDestCol->zColl, pSrcCol->zColl)!=0 ){ ++ if( sqlite3_stricmp(sqlite3ColumnColl(pDestCol), ++ sqlite3ColumnColl(pSrcCol))!=0 ){ + return 0; /* Collating sequence must be the same on all columns */ + } + if( pDestCol->notNull && !pSrcCol->notNull ){ +@@ -123237,11 +137981,15 @@ static int xferOptimization( + } + /* Default values for second and subsequent columns need to match. */ + if( (pDestCol->colFlags & COLFLAG_GENERATED)==0 && i>0 ){ +- assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN ); +- assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN ); +- if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0) +- || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken, +- pSrcCol->pDflt->u.zToken)!=0) ++ Expr *pDestExpr = sqlite3ColumnExpr(pDest, pDestCol); ++ Expr *pSrcExpr = sqlite3ColumnExpr(pSrc, pSrcCol); ++ assert( pDestExpr==0 || pDestExpr->op==TK_SPAN ); ++ assert( pDestExpr==0 || !ExprHasProperty(pDestExpr, EP_IntValue) ); ++ assert( pSrcExpr==0 || pSrcExpr->op==TK_SPAN ); ++ assert( pSrcExpr==0 || !ExprHasProperty(pSrcExpr, EP_IntValue) ); ++ if( (pDestExpr==0)!=(pSrcExpr==0) ++ || (pDestExpr!=0 && strcmp(pDestExpr->u.zToken, ++ pSrcExpr->u.zToken)!=0) + ){ + return 0; /* Default values must be the same for all columns */ + } +@@ -123266,19 +138014,23 @@ static int xferOptimization( + } + } + #ifndef SQLITE_OMIT_CHECK +- if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){ ++ if( pDest->pCheck ++ && (db->mDbFlags & DBFLAG_Vacuum)==0 ++ && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ++ ){ + return 0; /* Tables have different CHECK constraints. Ticket #2252 */ + } + #endif + #ifndef SQLITE_OMIT_FOREIGN_KEY +- /* Disallow the transfer optimization if the destination table constains ++ /* Disallow the transfer optimization if the destination table contains + ** any foreign key constraints. This is more restrictive than necessary. + ** But the main beneficiary of the transfer optimization is the VACUUM + ** command, and the VACUUM command disables foreign key constraints. So + ** the extra complication to make this rule less restrictive is probably + ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] + */ +- if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ ++ assert( IsOrdinaryTable(pDest) ); ++ if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->u.tab.pFKey!=0 ){ + return 0; + } + #endif +@@ -123300,6 +138052,7 @@ static int xferOptimization( + iDest = pParse->nTab++; + regAutoinc = autoIncBegin(pParse, iDbDest, pDest); + regData = sqlite3GetTempReg(pParse); ++ sqlite3VdbeAddOp2(v, OP_Null, 0, regData); + regRowid = sqlite3GetTempReg(pParse); + sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); + assert( HasRowid(pDest) || destHasUniqueIdx ); +@@ -123335,11 +138088,13 @@ static int xferOptimization( + emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); + if( pDest->iPKey>=0 ){ + addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); +- sqlite3VdbeVerifyAbortable(v, onError); +- addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); +- VdbeCoverage(v); +- sqlite3RowidConstraint(pParse, onError, pDest); +- sqlite3VdbeJumpHere(v, addr2); ++ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ ++ sqlite3VdbeVerifyAbortable(v, onError); ++ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); ++ VdbeCoverage(v); ++ sqlite3RowidConstraint(pParse, onError, pDest); ++ sqlite3VdbeJumpHere(v, addr2); ++ } + autoIncStep(pParse, regAutoinc, regRowid); + }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){ + addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); +@@ -123347,16 +138102,28 @@ static int xferOptimization( + addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); + assert( (pDest->tabFlags & TF_Autoincrement)==0 ); + } ++ + if( db->mDbFlags & DBFLAG_Vacuum ){ + sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); +- insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT; ++ insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT; + }else{ +- insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND; ++ insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT; ++ } ++#ifdef SQLITE_ENABLE_PREUPDATE_HOOK ++ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ ++ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); ++ insFlags &= ~OPFLAG_PREFORMAT; ++ }else ++#endif ++ { ++ sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid); ++ } ++ sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); ++ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ ++ sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE); + } +- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); +- sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid, +- (char*)pDest, P4_TABLE); + sqlite3VdbeChangeP5(v, insFlags); ++ + sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); + sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); +@@ -123398,13 +138165,22 @@ static int xferOptimization( + if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break; + } + if( i==pSrcIdx->nColumn ){ +- idxInsFlags = OPFLAG_USESEEKRESULT; ++ idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT; + sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); ++ sqlite3VdbeAddOp2(v, OP_RowCell, iDest, iSrc); + } + }else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ + idxInsFlags |= OPFLAG_NCHANGE; + } +- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); ++ if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){ ++ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); ++ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ++ && !HasRowid(pDest) ++ && IsPrimaryKeyIndex(pDestIdx) ++ ){ ++ codeWithoutRowidPreupdate(pParse, pDest, iDest, regData); ++ } ++ } + sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData); + sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND); + sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); +@@ -123926,10 +138702,43 @@ struct sqlite3_api_routines { + const char *(*filename_journal)(const char*); + const char *(*filename_wal)(const char*); + /* Version 3.32.0 and later */ +- char *(*create_filename)(const char*,const char*,const char*, ++ const char *(*create_filename)(const char*,const char*,const char*, + int,const char**); +- void (*free_filename)(char*); ++ void (*free_filename)(const char*); + sqlite3_file *(*database_file_object)(const char*); ++ /* Version 3.34.0 and later */ ++ int (*txn_state)(sqlite3*,const char*); ++ /* Version 3.36.1 and later */ ++ sqlite3_int64 (*changes64)(sqlite3*); ++ sqlite3_int64 (*total_changes64)(sqlite3*); ++ /* Version 3.37.0 and later */ ++ int (*autovacuum_pages)(sqlite3*, ++ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), ++ void*, void(*)(void*)); ++ /* Version 3.38.0 and later */ ++ int (*error_offset)(sqlite3*); ++ int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**); ++ int (*vtab_distinct)(sqlite3_index_info*); ++ int (*vtab_in)(sqlite3_index_info*,int,int); ++ int (*vtab_in_first)(sqlite3_value*,sqlite3_value**); ++ int (*vtab_in_next)(sqlite3_value*,sqlite3_value**); ++ /* Version 3.39.0 and later */ ++ int (*deserialize)(sqlite3*,const char*,unsigned char*, ++ sqlite3_int64,sqlite3_int64,unsigned); ++ unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*, ++ unsigned int); ++ const char *(*db_name)(sqlite3*,int); ++ /* Version 3.40.0 and later */ ++ int (*value_encoding)(sqlite3_value*); ++ /* Version 3.41.0 and later */ ++ int (*is_interrupted)(sqlite3*); ++ /* Version 3.43.0 and later */ ++ int (*stmt_explain)(sqlite3_stmt*,int); ++ /* Version 3.44.0 and later */ ++ void *(*get_clientdata)(sqlite3*,const char*); ++ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); ++ /* Version 3.50.0 and later */ ++ int (*setlk_timeout)(sqlite3*,int,int); + }; + + /* +@@ -124234,6 +139043,37 @@ typedef int (*sqlite3_loadext_entry)( + #define sqlite3_create_filename sqlite3_api->create_filename + #define sqlite3_free_filename sqlite3_api->free_filename + #define sqlite3_database_file_object sqlite3_api->database_file_object ++/* Version 3.34.0 and later */ ++#define sqlite3_txn_state sqlite3_api->txn_state ++/* Version 3.36.1 and later */ ++#define sqlite3_changes64 sqlite3_api->changes64 ++#define sqlite3_total_changes64 sqlite3_api->total_changes64 ++/* Version 3.37.0 and later */ ++#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages ++/* Version 3.38.0 and later */ ++#define sqlite3_error_offset sqlite3_api->error_offset ++#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value ++#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct ++#define sqlite3_vtab_in sqlite3_api->vtab_in ++#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first ++#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next ++/* Version 3.39.0 and later */ ++#ifndef SQLITE_OMIT_DESERIALIZE ++#define sqlite3_deserialize sqlite3_api->deserialize ++#define sqlite3_serialize sqlite3_api->serialize ++#endif ++#define sqlite3_db_name sqlite3_api->db_name ++/* Version 3.40.0 and later */ ++#define sqlite3_value_encoding sqlite3_api->value_encoding ++/* Version 3.41.0 and later */ ++#define sqlite3_is_interrupted sqlite3_api->is_interrupted ++/* Version 3.43.0 and later */ ++#define sqlite3_stmt_explain sqlite3_api->stmt_explain ++/* Version 3.44.0 and later */ ++#define sqlite3_get_clientdata sqlite3_api->get_clientdata ++#define sqlite3_set_clientdata sqlite3_api->set_clientdata ++/* Version 3.50.0 and later */ ++#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout + #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ + + #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) +@@ -124716,6 +139556,48 @@ static const sqlite3_api_routines sqlite3Apis = { + sqlite3_create_filename, + sqlite3_free_filename, + sqlite3_database_file_object, ++ /* Version 3.34.0 and later */ ++ sqlite3_txn_state, ++ /* Version 3.36.1 and later */ ++ sqlite3_changes64, ++ sqlite3_total_changes64, ++ /* Version 3.37.0 and later */ ++ sqlite3_autovacuum_pages, ++ /* Version 3.38.0 and later */ ++ sqlite3_error_offset, ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ sqlite3_vtab_rhs_value, ++ sqlite3_vtab_distinct, ++ sqlite3_vtab_in, ++ sqlite3_vtab_in_first, ++ sqlite3_vtab_in_next, ++#else ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++#endif ++ /* Version 3.39.0 and later */ ++#ifndef SQLITE_OMIT_DESERIALIZE ++ sqlite3_deserialize, ++ sqlite3_serialize, ++#else ++ 0, ++ 0, ++#endif ++ sqlite3_db_name, ++ /* Version 3.40.0 and later */ ++ sqlite3_value_encoding, ++ /* Version 3.41.0 and later */ ++ sqlite3_is_interrupted, ++ /* Version 3.43.0 and later */ ++ sqlite3_stmt_explain, ++ /* Version 3.44.0 and later */ ++ sqlite3_get_clientdata, ++ sqlite3_set_clientdata, ++ /* Version 3.50.0 and later */ ++ sqlite3_setlk_timeout + }; + + /* True if x is the directory separator character +@@ -124751,7 +139633,7 @@ static int sqlite3LoadExtension( + const char *zEntry; + char *zAltEntry = 0; + void **aHandle; +- u64 nMsg = 300 + sqlite3Strlen30(zFile); ++ u64 nMsg = strlen(zFile); + int ii; + int rc; + +@@ -124785,26 +139667,32 @@ static int sqlite3LoadExtension( + + zEntry = zProc ? zProc : "sqlite3_extension_init"; + ++ /* tag-20210611-1. Some dlopen() implementations will segfault if given ++ ** an oversize filename. Most filesystems have a pathname limit of 4K, ++ ** so limit the extension filename length to about twice that. ++ ** https://sqlite.org/forum/forumpost/08a0d6d9bf ++ ** ++ ** Later (2023-03-25): Save an extra 6 bytes for the filename suffix. ++ ** See https://sqlite.org/forum/forumpost/24083b579d. ++ */ ++ if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; ++ ++ /* Do not allow sqlite3_load_extension() to link to a copy of the ++ ** running application, by passing in an empty filename. */ ++ if( nMsg==0 ) goto extension_not_found; ++ + handle = sqlite3OsDlOpen(pVfs, zFile); + #if SQLITE_OS_UNIX || SQLITE_OS_WIN + for(ii=0; iiaExtension[db->nExtension++] = handle; + return SQLITE_OK; ++ ++extension_not_found: ++ if( pzErrMsg ){ ++ nMsg += 300; ++ *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); ++ if( zErrmsg ){ ++ assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ ++ sqlite3_snprintf((int)nMsg, zErrmsg, ++ "unable to open shared library [%.*s]", SQLITE_MAX_PATHLEN, zFile); ++ sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); ++ } ++ } ++ return SQLITE_ERROR; + } + SQLITE_API int sqlite3_load_extension( + sqlite3 *db, /* Load the extension into this database connection */ +@@ -124911,6 +139813,9 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){ + ** default so as not to open security holes in older applications. + */ + SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; ++#endif + sqlite3_mutex_enter(db->mutex); + if( onoff ){ + db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc; +@@ -124960,6 +139865,9 @@ SQLITE_API int sqlite3_auto_extension( + void (*xInit)(void) + ){ + int rc = SQLITE_OK; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( xInit==0 ) return SQLITE_MISUSE_BKPT; ++#endif + #ifndef SQLITE_OMIT_AUTOINIT + rc = sqlite3_initialize(); + if( rc ){ +@@ -125012,6 +139920,9 @@ SQLITE_API int sqlite3_cancel_auto_extension( + int i; + int n = 0; + wsdAutoextInit; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( xInit==0 ) return 0; ++#endif + sqlite3_mutex_enter(mutex); + for(i=(int)wsdAutoext.nExt-1; i>=0; i--){ + if( wsdAutoext.aExt[i]==xInit ){ +@@ -125168,13 +140079,14 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ + #define PragTyp_SOFT_HEAP_LIMIT 35 + #define PragTyp_SYNCHRONOUS 36 + #define PragTyp_TABLE_INFO 37 +-#define PragTyp_TEMP_STORE 38 +-#define PragTyp_TEMP_STORE_DIRECTORY 39 +-#define PragTyp_THREADS 40 +-#define PragTyp_WAL_AUTOCHECKPOINT 41 +-#define PragTyp_WAL_CHECKPOINT 42 +-#define PragTyp_LOCK_STATUS 43 +-#define PragTyp_STATS 44 ++#define PragTyp_TABLE_LIST 38 ++#define PragTyp_TEMP_STORE 39 ++#define PragTyp_TEMP_STORE_DIRECTORY 40 ++#define PragTyp_THREADS 41 ++#define PragTyp_WAL_AUTOCHECKPOINT 42 ++#define PragTyp_WAL_CHECKPOINT 43 ++#define PragTyp_LOCK_STATUS 44 ++#define PragTyp_STATS 45 + + /* Property flags associated with various pragma. */ + #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ +@@ -125207,45 +140119,51 @@ static const char *const pragCName[] = { + /* 13 */ "pk", + /* 14 */ "hidden", + /* table_info reuses 8 */ +- /* 15 */ "seqno", /* Used by: index_xinfo */ +- /* 16 */ "cid", +- /* 17 */ "name", +- /* 18 */ "desc", +- /* 19 */ "coll", +- /* 20 */ "key", +- /* 21 */ "name", /* Used by: function_list */ +- /* 22 */ "builtin", ++ /* 15 */ "name", /* Used by: function_list */ ++ /* 16 */ "builtin", ++ /* 17 */ "type", ++ /* 18 */ "enc", ++ /* 19 */ "narg", ++ /* 20 */ "flags", ++ /* 21 */ "schema", /* Used by: table_list */ ++ /* 22 */ "name", + /* 23 */ "type", +- /* 24 */ "enc", +- /* 25 */ "narg", +- /* 26 */ "flags", +- /* 27 */ "tbl", /* Used by: stats */ +- /* 28 */ "idx", +- /* 29 */ "wdth", +- /* 30 */ "hght", +- /* 31 */ "flgs", +- /* 32 */ "seq", /* Used by: index_list */ +- /* 33 */ "name", +- /* 34 */ "unique", +- /* 35 */ "origin", +- /* 36 */ "partial", +- /* 37 */ "table", /* Used by: foreign_key_check */ +- /* 38 */ "rowid", +- /* 39 */ "parent", +- /* 40 */ "fkid", +- /* index_info reuses 15 */ +- /* 41 */ "seq", /* Used by: database_list */ +- /* 42 */ "name", +- /* 43 */ "file", +- /* 44 */ "busy", /* Used by: wal_checkpoint */ +- /* 45 */ "log", +- /* 46 */ "checkpointed", +- /* collation_list reuses 32 */ +- /* 47 */ "database", /* Used by: lock_status */ +- /* 48 */ "status", +- /* 49 */ "cache_size", /* Used by: default_cache_size */ ++ /* 24 */ "ncol", ++ /* 25 */ "wr", ++ /* 26 */ "strict", ++ /* 27 */ "seqno", /* Used by: index_xinfo */ ++ /* 28 */ "cid", ++ /* 29 */ "name", ++ /* 30 */ "desc", ++ /* 31 */ "coll", ++ /* 32 */ "key", ++ /* 33 */ "seq", /* Used by: index_list */ ++ /* 34 */ "name", ++ /* 35 */ "unique", ++ /* 36 */ "origin", ++ /* 37 */ "partial", ++ /* 38 */ "tbl", /* Used by: stats */ ++ /* 39 */ "idx", ++ /* 40 */ "wdth", ++ /* 41 */ "hght", ++ /* 42 */ "flgs", ++ /* 43 */ "table", /* Used by: foreign_key_check */ ++ /* 44 */ "rowid", ++ /* 45 */ "parent", ++ /* 46 */ "fkid", ++ /* 47 */ "busy", /* Used by: wal_checkpoint */ ++ /* 48 */ "log", ++ /* 49 */ "checkpointed", ++ /* 50 */ "seq", /* Used by: database_list */ ++ /* 51 */ "name", ++ /* 52 */ "file", ++ /* index_info reuses 27 */ ++ /* 53 */ "database", /* Used by: lock_status */ ++ /* 54 */ "status", ++ /* collation_list reuses 33 */ ++ /* 55 */ "cache_size", /* Used by: default_cache_size */ + /* module_list pragma_list reuses 9 */ +- /* 50 */ "timeout", /* Used by: busy_timeout */ ++ /* 56 */ "timeout", /* Used by: busy_timeout */ + }; + + /* Definitions of all built-in pragmas */ +@@ -125296,7 +140214,7 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "busy_timeout", + /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, + /* ePragFlg: */ PragFlg_Result0, +- /* ColNames: */ 50, 1, ++ /* ColNames: */ 56, 1, + /* iArg: */ 0 }, + #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) + {/* zName: */ "cache_size", +@@ -125335,7 +140253,7 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "collation_list", + /* ePragTyp: */ PragTyp_COLLATION_LIST, + /* ePragFlg: */ PragFlg_Result0, +- /* ColNames: */ 32, 2, ++ /* ColNames: */ 33, 2, + /* iArg: */ 0 }, + #endif + #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) +@@ -125369,15 +140287,15 @@ static const PragmaName aPragmaName[] = { + #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) + {/* zName: */ "database_list", + /* ePragTyp: */ PragTyp_DATABASE_LIST, +- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, +- /* ColNames: */ 41, 3, ++ /* ePragFlg: */ PragFlg_Result0, ++ /* ColNames: */ 50, 3, + /* iArg: */ 0 }, + #endif + #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) + {/* zName: */ "default_cache_size", + /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, +- /* ColNames: */ 49, 1, ++ /* ColNames: */ 55, 1, + /* iArg: */ 0 }, + #endif + #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) +@@ -125407,7 +140325,7 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "foreign_key_check", + /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, +- /* ColNames: */ 37, 4, ++ /* ColNames: */ 43, 4, + /* iArg: */ 0 }, + #endif + #if !defined(SQLITE_OMIT_FOREIGN_KEY) +@@ -125450,7 +140368,7 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "function_list", + /* ePragTyp: */ PragTyp_FUNCTION_LIST, + /* ePragFlg: */ PragFlg_Result0, +- /* ColNames: */ 21, 6, ++ /* ColNames: */ 15, 6, + /* iArg: */ 0 }, + #endif + #endif +@@ -125479,23 +140397,23 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "index_info", + /* ePragTyp: */ PragTyp_INDEX_INFO, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, +- /* ColNames: */ 15, 3, ++ /* ColNames: */ 27, 3, + /* iArg: */ 0 }, + {/* zName: */ "index_list", + /* ePragTyp: */ PragTyp_INDEX_LIST, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, +- /* ColNames: */ 32, 5, ++ /* ColNames: */ 33, 5, + /* iArg: */ 0 }, + {/* zName: */ "index_xinfo", + /* ePragTyp: */ PragTyp_INDEX_INFO, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, +- /* ColNames: */ 15, 6, ++ /* ColNames: */ 27, 6, + /* iArg: */ 1 }, + #endif + #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) + {/* zName: */ "integrity_check", + /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, +- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, ++ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, + /* ColNames: */ 0, 0, + /* iArg: */ 0 }, + #endif +@@ -125529,7 +140447,7 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "lock_status", + /* ePragTyp: */ PragTyp_LOCK_STATUS, + /* ePragFlg: */ PragFlg_Result0, +- /* ColNames: */ 47, 2, ++ /* ColNames: */ 53, 2, + /* iArg: */ 0 }, + #endif + #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) +@@ -125603,7 +140521,7 @@ static const PragmaName aPragmaName[] = { + #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) + {/* zName: */ "quick_check", + /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, +- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, ++ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, + /* ColNames: */ 0, 0, + /* iArg: */ 0 }, + #endif +@@ -125668,7 +140586,7 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "stats", + /* ePragTyp: */ PragTyp_STATS, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, +- /* ColNames: */ 27, 5, ++ /* ColNames: */ 38, 5, + /* iArg: */ 0 }, + #endif + #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) +@@ -125684,6 +140602,11 @@ static const PragmaName aPragmaName[] = { + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, + /* ColNames: */ 8, 6, + /* iArg: */ 0 }, ++ {/* zName: */ "table_list", ++ /* ePragTyp: */ PragTyp_TABLE_LIST, ++ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, ++ /* ColNames: */ 21, 6, ++ /* iArg: */ 0 }, + {/* zName: */ "table_xinfo", + /* ePragTyp: */ PragTyp_TABLE_INFO, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, +@@ -125759,7 +140682,7 @@ static const PragmaName aPragmaName[] = { + {/* zName: */ "wal_checkpoint", + /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, + /* ePragFlg: */ PragFlg_NeedSchema, +- /* ColNames: */ 44, 3, ++ /* ColNames: */ 47, 3, + /* iArg: */ 0 }, + #endif + #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) +@@ -125770,11 +140693,39 @@ static const PragmaName aPragmaName[] = { + /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, + #endif + }; +-/* Number of pragmas: 67 on by default, 77 total. */ ++/* Number of pragmas: 68 on by default, 78 total. */ + + /************** End of pragma.h **********************************************/ + /************** Continuing where we left off in pragma.c *********************/ + ++/* ++** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands ++** will be run with an analysis_limit set to the lessor of the value of ++** the following macro or to the actual analysis_limit if it is non-zero, ++** in order to prevent PRAGMA optimize from running for too long. ++** ++** The value of 2000 is chosen empirically so that the worst-case run-time ++** for PRAGMA optimize does not exceed 100 milliseconds against a variety ++** of test databases on a RaspberryPI-4 compiled using -Os and without ++** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of ++** this paragraph, "worst-case" means that ANALYZE ends up being ++** run on every table in the database. The worst case typically only ++** happens if PRAGMA optimize is run on a database file for which ANALYZE ++** has not been previously run and the 0x10000 flag is included so that ++** all tables are analyzed. The usual case for PRAGMA optimize is that ++** no ANALYZE commands will be run at all, or if any ANALYZE happens it ++** will be against a single table, so that expected timing for PRAGMA ++** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000 ++** flag or less than 100 microseconds without the 0x10000 flag. ++** ++** An analysis limit of 2000 is almost always sufficient for the query ++** planner to fully characterize an index. The additional accuracy from ++** a larger analysis is not usually helpful. ++*/ ++#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT ++# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000 ++#endif ++ + /* + ** Interpret the given string as a safety level. Return 0 for OFF, + ** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or +@@ -125876,7 +140827,9 @@ static int getTempStore(const char *z){ + static int invalidateTempStorage(Parse *pParse){ + sqlite3 *db = pParse->db; + if( db->aDb[1].pBt!=0 ){ +- if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){ ++ if( !db->autoCommit ++ || sqlite3BtreeTxnState(db->aDb[1].pBt)!=SQLITE_TXN_NONE ++ ){ + sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " + "from within a transaction"); + return SQLITE_ERROR; +@@ -126050,15 +141003,16 @@ static void pragmaFunclistLine( + int isBuiltin, /* True if this is a built-in function */ + int showInternFuncs /* True if showing internal functions */ + ){ ++ u32 mask = ++ SQLITE_DETERMINISTIC | ++ SQLITE_DIRECTONLY | ++ SQLITE_SUBTYPE | ++ SQLITE_INNOCUOUS | ++ SQLITE_FUNC_INTERNAL ++ ; ++ if( showInternFuncs ) mask = 0xffffffff; + for(; p; p=p->pNext){ + const char *zType; +- static const u32 mask = +- SQLITE_DETERMINISTIC | +- SQLITE_DIRECTONLY | +- SQLITE_SUBTYPE | +- SQLITE_INNOCUOUS | +- SQLITE_FUNC_INTERNAL +- ; + static const char *azEnc[] = { 0, "utf8", "utf16le", "utf16be" }; + + assert( SQLITE_FUNC_ENCMASK==0x3 ); +@@ -126210,7 +141164,11 @@ SQLITE_PRIVATE void sqlite3Pragma( + + /* Locate the pragma in the lookup table */ + pPragma = pragmaLocate(zLeft); +- if( pPragma==0 ) goto pragma_out; ++ if( pPragma==0 ){ ++ /* IMP: R-43042-22504 No error messages are generated if an ++ ** unknown pragma is issued. */ ++ goto pragma_out; ++ } + + /* Make sure the database schema is loaded if the pragma requires that */ + if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){ +@@ -126546,7 +141504,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + */ + #ifndef SQLITE_OMIT_AUTOVACUUM + case PragTyp_INCREMENTAL_VACUUM: { +- int iLimit, addr; ++ int iLimit = 0, addr; + if( zRight==0 || !sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){ + iLimit = 0x7fffffff; + } +@@ -126592,7 +141550,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** + ** The first form reports the current local setting for the + ** page cache spill size. The second form turns cache spill on +- ** or off. When turnning cache spill on, the size is set to the ++ ** or off. When turning cache spill on, the size is set to the + ** current cache_size. The third form sets a spill size that + ** may be different form the cache size. + ** If N is positive then that is the +@@ -126703,6 +141661,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** + */ + case PragTyp_TEMP_STORE_DIRECTORY: { ++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + if( !zRight ){ + returnSingleText(v, sqlite3_temp_directory); + }else{ +@@ -126712,6 +141671,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); + if( rc!=SQLITE_OK || res==0 ){ + sqlite3ErrorMsg(pParse, "not a writable directory"); ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + goto pragma_out; + } + } +@@ -126729,6 +141689,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + } + #endif /* SQLITE_OMIT_WSD */ + } ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + break; + } + +@@ -126747,6 +141708,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** + */ + case PragTyp_DATA_STORE_DIRECTORY: { ++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + if( !zRight ){ + returnSingleText(v, sqlite3_data_directory); + }else{ +@@ -126756,6 +141718,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); + if( rc!=SQLITE_OK || res==0 ){ + sqlite3ErrorMsg(pParse, "not a writable directory"); ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + goto pragma_out; + } + } +@@ -126767,6 +141730,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + } + #endif /* SQLITE_OMIT_WSD */ + } ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); + break; + } + #endif +@@ -126848,18 +141812,27 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** in auto-commit mode. */ + mask &= ~(SQLITE_ForeignKeys); + } +-#if SQLITE_USER_AUTHENTICATION +- if( db->auth.authLevel==UAUTH_User ){ +- /* Do not allow non-admin users to modify the schema arbitrarily */ +- mask &= ~(SQLITE_WriteSchema); +- } +-#endif + + if( sqlite3GetBoolean(zRight, 0) ){ +- db->flags |= mask; ++ if( (mask & SQLITE_WriteSchema)==0 ++ || (db->flags & SQLITE_Defensive)==0 ++ ){ ++ db->flags |= mask; ++ } + }else{ + db->flags &= ~mask; +- if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0; ++ if( mask==SQLITE_DeferFKs ){ ++ db->nDeferredImmCons = 0; ++ db->nDeferredCons = 0; ++ } ++ if( (mask & SQLITE_WriteSchema)!=0 ++ && sqlite3_stricmp(zRight, "reset")==0 ++ ){ ++ /* IMP: R-60817-01178 If the argument is "RESET" then schema ++ ** writing is disabled (as with "PRAGMA writable_schema=OFF") and, ++ ** in addition, the schema is reloaded. */ ++ sqlite3ResetAllSchemasOfConnection(db); ++ } + } + + /* Many of the flag-pragmas modify the code generated by the SQL +@@ -126900,6 +141873,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + sqlite3ViewGetColumnNames(pParse, pTab); + for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ + int isHidden = 0; ++ const Expr *pColExpr; + if( pCol->colFlags & COLFLAG_NOINSERT ){ + if( pPragma->iArg==0 ){ + nHidden++; +@@ -126920,13 +141894,16 @@ SQLITE_PRIVATE void sqlite3Pragma( + }else{ + for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} + } +- assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN || isHidden>=2 ); ++ pColExpr = sqlite3ColumnExpr(pTab,pCol); ++ assert( pColExpr==0 || pColExpr->op==TK_SPAN || isHidden>=2 ); ++ assert( pColExpr==0 || !ExprHasProperty(pColExpr, EP_IntValue) ++ || isHidden>=2 ); + sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi", + i-nHidden, +- pCol->zName, ++ pCol->zCnName, + sqlite3ColumnType(pCol,""), + pCol->notNull ? 1 : 0, +- pCol->pDflt && isHidden<2 ? pCol->pDflt->u.zToken : 0, ++ (isHidden>=2 || pColExpr==0) ? 0 : pColExpr->u.zToken, + k, + isHidden); + } +@@ -126934,6 +141911,86 @@ SQLITE_PRIVATE void sqlite3Pragma( + } + break; + ++ /* ++ ** PRAGMA table_list ++ ** ++ ** Return a single row for each table, virtual table, or view in the ++ ** entire schema. ++ ** ++ ** schema: Name of attached database hold this table ++ ** name: Name of the table itself ++ ** type: "table", "view", "virtual", "shadow" ++ ** ncol: Number of columns ++ ** wr: True for a WITHOUT ROWID table ++ ** strict: True for a STRICT table ++ */ ++ case PragTyp_TABLE_LIST: { ++ int ii; ++ pParse->nMem = 6; ++ sqlite3CodeVerifyNamedSchema(pParse, zDb); ++ for(ii=0; iinDb; ii++){ ++ HashElem *k; ++ Hash *pHash; ++ int initNCol; ++ if( zDb && sqlite3_stricmp(zDb, db->aDb[ii].zDbSName)!=0 ) continue; ++ ++ /* Ensure that the Table.nCol field is initialized for all views ++ ** and virtual tables. Each time we initialize a Table.nCol value ++ ** for a table, that can potentially disrupt the hash table, so restart ++ ** the initialization scan. ++ */ ++ pHash = &db->aDb[ii].pSchema->tblHash; ++ initNCol = sqliteHashCount(pHash); ++ while( initNCol-- ){ ++ for(k=sqliteHashFirst(pHash); 1; k=sqliteHashNext(k) ){ ++ Table *pTab; ++ if( k==0 ){ initNCol = 0; break; } ++ pTab = sqliteHashData(k); ++ if( pTab->nCol==0 ){ ++ char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); ++ if( zSql ){ ++ sqlite3_stmt *pDummy = 0; ++ (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG, ++ &pDummy, 0); ++ (void)sqlite3_finalize(pDummy); ++ sqlite3DbFree(db, zSql); ++ } ++ if( db->mallocFailed ){ ++ sqlite3ErrorMsg(db->pParse, "out of memory"); ++ db->pParse->rc = SQLITE_NOMEM_BKPT; ++ } ++ pHash = &db->aDb[ii].pSchema->tblHash; ++ break; ++ } ++ } ++ } ++ ++ for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k) ){ ++ Table *pTab = sqliteHashData(k); ++ const char *zType; ++ if( zRight && sqlite3_stricmp(zRight, pTab->zName)!=0 ) continue; ++ if( IsView(pTab) ){ ++ zType = "view"; ++ }else if( IsVirtual(pTab) ){ ++ zType = "virtual"; ++ }else if( pTab->tabFlags & TF_Shadow ){ ++ zType = "shadow"; ++ }else{ ++ zType = "table"; ++ } ++ sqlite3VdbeMultiLoad(v, 1, "sssiii", ++ db->aDb[ii].zDbSName, ++ sqlite3PreferredTableName(pTab->zName), ++ zType, ++ pTab->nCol, ++ (pTab->tabFlags & TF_WithoutRowid)!=0, ++ (pTab->tabFlags & TF_Strict)!=0 ++ ); ++ } ++ } ++ } ++ break; ++ + #ifdef SQLITE_DEBUG + case PragTyp_STATS: { + Index *pIdx; +@@ -126943,7 +142000,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){ + Table *pTab = sqliteHashData(i); + sqlite3VdbeMultiLoad(v, 1, "ssiii", +- pTab->zName, ++ sqlite3PreferredTableName(pTab->zName), + 0, + pTab->szTabRow, + pTab->nRowLogEst, +@@ -126993,7 +142050,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + for(i=0; iaiColumn[i]; + sqlite3VdbeMultiLoad(v, 1, "iisX", i, cnum, +- cnum<0 ? 0 : pTab->aCol[cnum].zName); ++ cnum<0 ? 0 : pTab->aCol[cnum].zCnName); + if( pPragma->iArg ){ + sqlite3VdbeMultiLoad(v, 4, "isiX", + pIdx->aSortOrder[i], +@@ -127062,11 +142119,13 @@ SQLITE_PRIVATE void sqlite3Pragma( + pParse->nMem = 6; + for(i=0; iu.pHash ){ ++ assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); + pragmaFunclistLine(v, p, 1, showInternFunc); + } + } + for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){ + p = (FuncDef*)sqliteHashData(j); ++ assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); + pragmaFunclistLine(v, p, 0, showInternFunc); + } + } +@@ -127100,8 +142159,8 @@ SQLITE_PRIVATE void sqlite3Pragma( + FKey *pFK; + Table *pTab; + pTab = sqlite3FindTable(db, zRight, zDb); +- if( pTab ){ +- pFK = pTab->pFKey; ++ if( pTab && IsOrdinaryTable(pTab) ){ ++ pFK = pTab->u.tab.pFKey; + if( pFK ){ + int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); + int i = 0; +@@ -127114,7 +142173,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + i, + j, + pFK->zTo, +- pTab->aCol[pFK->aCol[j].iFrom].zName, ++ pTab->aCol[pFK->aCol[j].iFrom].zCnName, + pFK->aCol[j].zCol, + actionName(pFK->aAction[1]), /* ON UPDATE */ + actionName(pFK->aAction[0]), /* ON DELETE */ +@@ -127141,7 +142200,6 @@ SQLITE_PRIVATE void sqlite3Pragma( + HashElem *k; /* Loop counter: Next table in schema */ + int x; /* result variable */ + int regResult; /* 3 registers to hold a result row */ +- int regKey; /* Register to hold key for checking the FK */ + int regRow; /* Registers to hold a row from pTab */ + int addrTop; /* Top of a loop checking foreign keys */ + int addrOk; /* Jump here if the key is OK */ +@@ -127149,7 +142207,6 @@ SQLITE_PRIVATE void sqlite3Pragma( + + regResult = pParse->nMem+1; + pParse->nMem += 4; +- regKey = ++pParse->nMem; + regRow = ++pParse->nMem; + k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); + while( k ){ +@@ -127160,15 +142217,16 @@ SQLITE_PRIVATE void sqlite3Pragma( + pTab = (Table*)sqliteHashData(k); + k = sqliteHashNext(k); + } +- if( pTab==0 || pTab->pFKey==0 ) continue; ++ if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue; + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zDb = db->aDb[iDb].zDbSName; + sqlite3CodeVerifySchema(pParse, iDb); + sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); +- if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; ++ sqlite3TouchRegister(pParse, pTab->nCol+regRow); + sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); + sqlite3VdbeLoadString(v, regResult, pTab->zName); +- for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ ++ assert( IsOrdinaryTable(pTab) ); ++ for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ + pParent = sqlite3FindTable(db, pFK->zTo, zDb); + if( pParent==0 ) continue; + pIdx = 0; +@@ -127190,13 +142248,14 @@ SQLITE_PRIVATE void sqlite3Pragma( + if( pFK ) break; + if( pParse->nTabnTab = i; + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v); +- for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ ++ assert( IsOrdinaryTable(pTab) ); ++ for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ + pParent = sqlite3FindTable(db, pFK->zTo, zDb); + pIdx = 0; + aiCols = 0; + if( pParent ){ + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); +- assert( x==0 ); ++ assert( x==0 || db->mallocFailed ); + } + addrOk = sqlite3VdbeMakeLabel(pParse); + +@@ -127204,6 +142263,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** regRow..regRow+n. If any of the child key values are NULL, this + ** row cannot cause an FK violation. Jump directly to addrOk in + ** this case. */ ++ sqlite3TouchRegister(pParse, regRow + pFK->nCol); + for(j=0; jnCol; j++){ + int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; + sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); +@@ -127213,15 +142273,15 @@ SQLITE_PRIVATE void sqlite3Pragma( + /* Generate code to query the parent index for a matching parent + ** key. If a match is found, jump to addrOk. */ + if( pIdx ){ +- sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey, ++ sqlite3VdbeAddOp4(v, OP_Affinity, regRow, pFK->nCol, 0, + sqlite3IndexAffinityStr(db,pIdx), pFK->nCol); +- sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); ++ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regRow, pFK->nCol); + VdbeCoverage(v); + }else if( pParent ){ + int jmp = sqlite3VdbeCurrentAddr(v)+2; + sqlite3VdbeAddOp3(v, OP_SeekRowid, i, jmp, regRow); VdbeCoverage(v); + sqlite3VdbeGoto(v, addrOk); +- assert( pFK->nCol==1 ); ++ assert( pFK->nCol==1 || db->mallocFailed ); + } + + /* Generate code to report an FK violation to the caller. */ +@@ -127270,9 +142330,9 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** The "quick_check" is reduced version of + ** integrity_check designed to detect most database corruption + ** without the overhead of cross-checking indexes. Quick_check +- ** is linear time wherease integrity_check is O(NlogN). ++ ** is linear time whereas integrity_check is O(NlogN). + ** +- ** The maximum nubmer of errors is 100 by default. A different default ++ ** The maximum number of errors is 100 by default. A different default + ** can be specified using a numeric parameter N. + ** + ** Or, the parameter N can be the name of a table. In that case, only +@@ -127309,7 +142369,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + /* Set the maximum error count */ + mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; + if( zRight ){ +- if( sqlite3GetInt32(zRight, &mxErr) ){ ++ if( sqlite3GetInt32(pValue->z, &mxErr) ){ + if( mxErr<=0 ){ + mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; + } +@@ -127326,12 +142386,12 @@ SQLITE_PRIVATE void sqlite3Pragma( + Hash *pTbls; /* Set of all tables in the schema */ + int *aRoot; /* Array of root page numbers of all btrees */ + int cnt = 0; /* Number of entries in aRoot[] */ +- int mxIdx = 0; /* Maximum number of indexes for any table */ + + if( OMIT_TEMPDB && i==1 ) continue; + if( iDb>=0 && i!=iDb ) continue; + + sqlite3CodeVerifySchema(pParse, i); ++ pParse->okConstFactor = 0; /* tag-20230327-1 */ + + /* Do an integrity check of the B-Tree + ** +@@ -127347,7 +142407,6 @@ SQLITE_PRIVATE void sqlite3Pragma( + if( pObjTab && pObjTab!=pTab ) continue; + if( HasRowid(pTab) ) cnt++; + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } +- if( nIdx>mxIdx ) mxIdx = nIdx; + } + if( cnt==0 ) continue; + if( pObjTab ) cnt++; +@@ -127367,12 +142426,13 @@ SQLITE_PRIVATE void sqlite3Pragma( + aRoot[0] = cnt; + + /* Make sure sufficient number of registers have been allocated */ +- pParse->nMem = MAX( pParse->nMem, 8+mxIdx ); ++ sqlite3TouchRegister(pParse, 8+cnt); ++ sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); + sqlite3ClearTempRegCache(pParse); + + /* Do the b-tree integrity checks */ +- sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY); +- sqlite3VdbeChangeP5(v, (u8)i); ++ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); ++ sqlite3VdbeChangeP5(v, (u16)i); + addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); + sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, + sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName), +@@ -127381,19 +142441,59 @@ SQLITE_PRIVATE void sqlite3Pragma( + integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, addr); + ++ /* Check that the indexes all have the right number of rows */ ++ cnt = pObjTab ? 1 : 0; ++ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); ++ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ ++ int iTab = 0; ++ Table *pTab = sqliteHashData(x); ++ Index *pIdx; ++ if( pObjTab && pObjTab!=pTab ) continue; ++ if( HasRowid(pTab) ){ ++ iTab = cnt++; ++ }else{ ++ iTab = cnt; ++ for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){ ++ if( IsPrimaryKeyIndex(pIdx) ) break; ++ iTab++; ++ } ++ } ++ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ ++ if( pIdx->pPartIdxWhere==0 ){ ++ addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab); ++ VdbeCoverageNeverNull(v); ++ sqlite3VdbeLoadString(v, 4, pIdx->zName); ++ sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); ++ integrityCheckResultRow(v); ++ sqlite3VdbeJumpHere(v, addr); ++ } ++ cnt++; ++ } ++ } ++ + /* Make sure all the indices are constructed correctly. + */ + for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ + Table *pTab = sqliteHashData(x); + Index *pIdx, *pPk; +- Index *pPrior = 0; ++ Index *pPrior = 0; /* Previous index */ + int loopTop; + int iDataCur, iIdxCur; + int r1 = -1; ++ int bStrict; /* True for a STRICT table */ ++ int r2; /* Previous key for WITHOUT ROWID tables */ ++ int mxCol; /* Maximum non-virtual column number */ + +- if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ + if( pObjTab && pObjTab!=pTab ) continue; +- pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); ++ if( !IsOrdinaryTable(pTab) ) continue; ++ if( isQuick || HasRowid(pTab) ){ ++ pPk = 0; ++ r2 = 0; ++ }else{ ++ pPk = sqlite3PrimaryKeyIndex(pTab); ++ r2 = sqlite3GetTempRange(pParse, pPk->nKeyCol); ++ sqlite3VdbeAddOp3(v, OP_Null, 1, r2, r2+pPk->nKeyCol-1); ++ } + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, + 1, 0, &iDataCur, &iIdxCur); + /* reg[7] counts the number of entries in the table. +@@ -127407,27 +142507,181 @@ SQLITE_PRIVATE void sqlite3Pragma( + assert( sqlite3NoTempsInRange(pParse,1,7+j) ); + sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v); + loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1); ++ ++ /* Fetch the right-most column from the table. This will cause ++ ** the entire record header to be parsed and sanity checked. It ++ ** will also prepopulate the cursor column cache that is used ++ ** by the OP_IsType code, so it is a required step. ++ */ ++ assert( !IsVirtual(pTab) ); ++ if( HasRowid(pTab) ){ ++ mxCol = -1; ++ for(j=0; jnCol; j++){ ++ if( (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)==0 ) mxCol++; ++ } ++ if( mxCol==pTab->iPKey ) mxCol--; ++ }else{ ++ /* COLFLAG_VIRTUAL columns are not included in the WITHOUT ROWID ++ ** PK index column-count, so there is no need to account for them ++ ** in this case. */ ++ mxCol = sqlite3PrimaryKeyIndex(pTab)->nColumn-1; ++ } ++ if( mxCol>=0 ){ ++ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, mxCol, 3); ++ sqlite3VdbeTypeofColumn(v, 3); ++ } ++ + if( !isQuick ){ +- /* Sanity check on record header decoding */ +- sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3); +- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); ++ if( pPk ){ ++ /* Verify WITHOUT ROWID keys are in ascending order */ ++ int a1; ++ char *zErr; ++ a1 = sqlite3VdbeAddOp4Int(v, OP_IdxGT, iDataCur, 0,r2,pPk->nKeyCol); ++ VdbeCoverage(v); ++ sqlite3VdbeAddOp1(v, OP_IsNull, r2); VdbeCoverage(v); ++ zErr = sqlite3MPrintf(db, ++ "row not in PRIMARY KEY order for %s", ++ pTab->zName); ++ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); ++ integrityCheckResultRow(v); ++ sqlite3VdbeJumpHere(v, a1); ++ sqlite3VdbeJumpHere(v, a1+1); ++ for(j=0; jnKeyCol; j++){ ++ sqlite3ExprCodeLoadIndexColumn(pParse, pPk, iDataCur, j, r2+j); ++ } ++ } + } +- /* Verify that all NOT NULL columns really are NOT NULL */ ++ /* Verify datatypes for all columns: ++ ** ++ ** (1) NOT NULL columns may not contain a NULL ++ ** (2) Datatype must be exact for non-ANY columns in STRICT tables ++ ** (3) Datatype for TEXT columns in non-STRICT tables must be ++ ** NULL, TEXT, or BLOB. ++ ** (4) Datatype for numeric columns in non-STRICT tables must not ++ ** be a TEXT value that can be losslessly converted to numeric. ++ */ ++ bStrict = (pTab->tabFlags & TF_Strict)!=0; + for(j=0; jnCol; j++){ + char *zErr; +- int jmp2; ++ Column *pCol = pTab->aCol + j; /* The column to be checked */ ++ int labelError; /* Jump here to report an error */ ++ int labelOk; /* Jump here if all looks ok */ ++ int p1, p3, p4; /* Operands to the OP_IsType opcode */ ++ int doTypeCheck; /* Check datatypes (besides NOT NULL) */ ++ + if( j==pTab->iPKey ) continue; +- if( pTab->aCol[j].notNull==0 ) continue; +- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); +- if( sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){ +- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); +- } +- jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); +- zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, +- pTab->aCol[j].zName); +- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); ++ if( bStrict ){ ++ doTypeCheck = pCol->eCType>COLTYPE_ANY; ++ }else{ ++ doTypeCheck = pCol->affinity>SQLITE_AFF_BLOB; ++ } ++ if( pCol->notNull==0 && !doTypeCheck ) continue; ++ ++ /* Compute the operands that will be needed for OP_IsType */ ++ p4 = SQLITE_NULL; ++ if( pCol->colFlags & COLFLAG_VIRTUAL ){ ++ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); ++ p1 = -1; ++ p3 = 3; ++ }else{ ++ if( pCol->iDflt ){ ++ sqlite3_value *pDfltValue = 0; ++ sqlite3ValueFromExpr(db, sqlite3ColumnExpr(pTab,pCol), ENC(db), ++ pCol->affinity, &pDfltValue); ++ if( pDfltValue ){ ++ p4 = sqlite3_value_type(pDfltValue); ++ sqlite3ValueFree(pDfltValue); ++ } ++ } ++ p1 = iDataCur; ++ if( !HasRowid(pTab) ){ ++ testcase( j!=sqlite3TableColumnToStorage(pTab, j) ); ++ p3 = sqlite3TableColumnToIndex(sqlite3PrimaryKeyIndex(pTab), j); ++ }else{ ++ p3 = sqlite3TableColumnToStorage(pTab,j); ++ testcase( p3!=j); ++ } ++ } ++ ++ labelError = sqlite3VdbeMakeLabel(pParse); ++ labelOk = sqlite3VdbeMakeLabel(pParse); ++ if( pCol->notNull ){ ++ /* (1) NOT NULL columns may not contain a NULL */ ++ int jmp3; ++ int jmp2 = sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); ++ VdbeCoverage(v); ++ if( p1<0 ){ ++ sqlite3VdbeChangeP5(v, 0x0f); /* INT, REAL, TEXT, or BLOB */ ++ jmp3 = jmp2; ++ }else{ ++ sqlite3VdbeChangeP5(v, 0x0d); /* INT, TEXT, or BLOB */ ++ /* OP_IsType does not detect NaN values in the database file ++ ** which should be treated as a NULL. So if the header type ++ ** is REAL, we have to load the actual data using OP_Column ++ ** to reliably determine if the value is a NULL. */ ++ sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3); ++ sqlite3ColumnDefault(v, pTab, j, 3); ++ jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk); ++ VdbeCoverage(v); ++ } ++ zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, ++ pCol->zCnName); ++ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); ++ if( doTypeCheck ){ ++ sqlite3VdbeGoto(v, labelError); ++ sqlite3VdbeJumpHere(v, jmp2); ++ sqlite3VdbeJumpHere(v, jmp3); ++ }else{ ++ /* VDBE byte code will fall thru */ ++ } ++ } ++ if( bStrict && doTypeCheck ){ ++ /* (2) Datatype must be exact for non-ANY columns in STRICT tables*/ ++ static unsigned char aStdTypeMask[] = { ++ 0x1f, /* ANY */ ++ 0x18, /* BLOB */ ++ 0x11, /* INT */ ++ 0x11, /* INTEGER */ ++ 0x13, /* REAL */ ++ 0x14 /* TEXT */ ++ }; ++ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); ++ assert( pCol->eCType>=1 && pCol->eCType<=sizeof(aStdTypeMask) ); ++ sqlite3VdbeChangeP5(v, aStdTypeMask[pCol->eCType-1]); ++ VdbeCoverage(v); ++ zErr = sqlite3MPrintf(db, "non-%s value in %s.%s", ++ sqlite3StdType[pCol->eCType-1], ++ pTab->zName, pTab->aCol[j].zCnName); ++ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); ++ }else if( !bStrict && pCol->affinity==SQLITE_AFF_TEXT ){ ++ /* (3) Datatype for TEXT columns in non-STRICT tables must be ++ ** NULL, TEXT, or BLOB. */ ++ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); ++ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */ ++ VdbeCoverage(v); ++ zErr = sqlite3MPrintf(db, "NUMERIC value in %s.%s", ++ pTab->zName, pTab->aCol[j].zCnName); ++ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); ++ }else if( !bStrict && pCol->affinity>=SQLITE_AFF_NUMERIC ){ ++ /* (4) Datatype for numeric columns in non-STRICT tables must not ++ ** be a TEXT value that can be converted to numeric. */ ++ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); ++ sqlite3VdbeChangeP5(v, 0x1b); /* NULL, INT, FLOAT, or BLOB */ ++ VdbeCoverage(v); ++ if( p1>=0 ){ ++ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); ++ } ++ sqlite3VdbeAddOp4(v, OP_Affinity, 3, 1, 0, "C", P4_STATIC); ++ sqlite3VdbeAddOp4Int(v, OP_IsType, -1, labelOk, 3, p4); ++ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */ ++ VdbeCoverage(v); ++ zErr = sqlite3MPrintf(db, "TEXT value in %s.%s", ++ pTab->zName, pTab->aCol[j].zCnName); ++ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); ++ } ++ sqlite3VdbeResolveLabel(v, labelError); + integrityCheckResultRow(v); +- sqlite3VdbeJumpHere(v, jmp2); ++ sqlite3VdbeResolveLabel(v, labelOk); + } + /* Verify CHECK constraints */ + if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ +@@ -127456,7 +142710,8 @@ SQLITE_PRIVATE void sqlite3Pragma( + if( !isQuick ){ /* Omit the remaining tests for quick_check */ + /* Validate index entries for the current row */ + for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ +- int jmp2, jmp3, jmp4, jmp5; ++ int jmp2, jmp3, jmp4, jmp5, label6; ++ int kk; + int ckUniq = sqlite3VdbeMakeLabel(pParse); + if( pPk==pIdx ) continue; + r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, +@@ -127474,13 +142729,49 @@ SQLITE_PRIVATE void sqlite3Pragma( + sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); + jmp4 = integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, jmp2); ++ ++ /* The OP_IdxRowid opcode is an optimized version of OP_Column ++ ** that extracts the rowid off the end of the index record. ++ ** But it only works correctly if index record does not have ++ ** any extra bytes at the end. Verify that this is the case. */ ++ if( HasRowid(pTab) ){ ++ int jmp7; ++ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur+j, 3); ++ jmp7 = sqlite3VdbeAddOp3(v, OP_Eq, 3, 0, r1+pIdx->nColumn-1); ++ VdbeCoverageNeverNull(v); ++ sqlite3VdbeLoadString(v, 3, ++ "rowid not at end-of-record for row "); ++ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); ++ sqlite3VdbeLoadString(v, 4, " of index "); ++ sqlite3VdbeGoto(v, jmp5-1); ++ sqlite3VdbeJumpHere(v, jmp7); ++ } ++ ++ /* Any indexed columns with non-BINARY collations must still hold ++ ** the exact same text value as the table. */ ++ label6 = 0; ++ for(kk=0; kknKeyCol; kk++){ ++ if( pIdx->azColl[kk]==sqlite3StrBINARY ) continue; ++ if( label6==0 ) label6 = sqlite3VdbeMakeLabel(pParse); ++ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur+j, kk, 3); ++ sqlite3VdbeAddOp3(v, OP_Ne, 3, label6, r1+kk); VdbeCoverage(v); ++ } ++ if( label6 ){ ++ int jmp6 = sqlite3VdbeAddOp0(v, OP_Goto); ++ sqlite3VdbeResolveLabel(v, label6); ++ sqlite3VdbeLoadString(v, 3, "row "); ++ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); ++ sqlite3VdbeLoadString(v, 4, " values differ from index "); ++ sqlite3VdbeGoto(v, jmp5-1); ++ sqlite3VdbeJumpHere(v, jmp6); ++ } ++ + /* For UNIQUE indexes, verify that only one entry exists with the + ** current key. The entry is unique if (1) any column is NULL + ** or (2) the next entry has a different key */ + if( IsUniqueIndex(pIdx) ){ + int uniqOk = sqlite3VdbeMakeLabel(pParse); + int jmp6; +- int kk; + for(kk=0; kknKeyCol; kk++){ + int iCol = pIdx->aiColumn[kk]; + assert( iCol!=XN_ROWID && iColnCol ); +@@ -127503,20 +142794,43 @@ SQLITE_PRIVATE void sqlite3Pragma( + } + sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); + sqlite3VdbeJumpHere(v, loopTop-1); +- if( !isQuick ){ +- sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); +- for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ +- if( pPk==pIdx ) continue; +- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); +- addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v); +- sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); +- sqlite3VdbeLoadString(v, 4, pIdx->zName); +- sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); +- integrityCheckResultRow(v); +- sqlite3VdbeJumpHere(v, addr); +- } ++ if( pPk ){ ++ assert( !isQuick ); ++ sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); + } + } ++ ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ /* Second pass to invoke the xIntegrity method on all virtual ++ ** tables. ++ */ ++ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ ++ Table *pTab = sqliteHashData(x); ++ sqlite3_vtab *pVTab; ++ int a1; ++ if( pObjTab && pObjTab!=pTab ) continue; ++ if( IsOrdinaryTable(pTab) ) continue; ++ if( !IsVirtual(pTab) ) continue; ++ if( pTab->nCol<=0 ){ ++ const char *zMod = pTab->u.vtab.azArg[0]; ++ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue; ++ } ++ sqlite3ViewGetColumnNames(pParse, pTab); ++ if( pTab->u.vtab.p==0 ) continue; ++ pVTab = pTab->u.vtab.p->pVtab; ++ if( NEVER(pVTab==0) ) continue; ++ if( NEVER(pVTab->pModule==0) ) continue; ++ if( pVTab->pModule->iVersion<4 ) continue; ++ if( pVTab->pModule->xIntegrity==0 ) continue; ++ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); ++ pTab->nTabRef++; ++ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); ++ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); ++ integrityCheckResultRow(v); ++ sqlite3VdbeJumpHere(v, a1); ++ continue; ++ } ++#endif + } + { + static const int iLn = VDBE_OFFSET_LINENO(2); +@@ -127665,6 +142979,11 @@ SQLITE_PRIVATE void sqlite3Pragma( + aOp[1].p2 = iCookie; + aOp[1].p3 = sqlite3Atoi(zRight); + aOp[1].p5 = 1; ++ if( iCookie==BTREE_SCHEMA_VERSION && (db->flags & SQLITE_Defensive)!=0 ){ ++ /* Do not allow the use of PRAGMA schema_version=VALUE in defensive ++ ** mode. Change the OP_SetCookie opcode into a no-op. */ ++ aOp[1].opcode = OP_Noop; ++ } + }else{ + /* Read the specified cookie value */ + static const VdbeOpList readCookie[] = { +@@ -127712,7 +143031,7 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** Checkpoint the database. + */ + case PragTyp_WAL_CHECKPOINT: { +- int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); ++ int iBt = (pId2->z?iDb:SQLITE_MAX_DB); + int eMode = SQLITE_CHECKPOINT_PASSIVE; + if( zRight ){ + if( sqlite3StrICmp(zRight, "full")==0 ){ +@@ -127775,44 +143094,63 @@ SQLITE_PRIVATE void sqlite3Pragma( + ** + ** The optional argument is a bitmask of optimizations to perform: + ** +- ** 0x0001 Debugging mode. Do not actually perform any optimizations +- ** but instead return one line of text for each optimization +- ** that would have been done. Off by default. ++ ** 0x00001 Debugging mode. Do not actually perform any optimizations ++ ** but instead return one line of text for each optimization ++ ** that would have been done. Off by default. + ** +- ** 0x0002 Run ANALYZE on tables that might benefit. On by default. +- ** See below for additional information. ++ ** 0x00002 Run ANALYZE on tables that might benefit. On by default. ++ ** See below for additional information. + ** +- ** 0x0004 (Not yet implemented) Record usage and performance +- ** information from the current session in the +- ** database file so that it will be available to "optimize" +- ** pragmas run by future database connections. ++ ** 0x00010 Run all ANALYZE operations using an analysis_limit that ++ ** is the lessor of the current analysis_limit and the ++ ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option. ++ ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is ++ ** currently (2024-02-19) set to 2000, which is such that ++ ** the worst case run-time for PRAGMA optimize on a 100MB ++ ** database will usually be less than 100 milliseconds on ++ ** a RaspberryPI-4 class machine. On by default. + ** +- ** 0x0008 (Not yet implemented) Create indexes that might have +- ** been helpful to recent queries ++ ** 0x10000 Look at tables to see if they need to be reanalyzed ++ ** due to growth or shrinkage even if they have not been ++ ** queried during the current connection. Off by default. + ** +- ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all +- ** of the optimizations listed above except Debug Mode, including new +- ** optimizations that have not yet been invented. If new optimizations are +- ** ever added that should be off by default, those off-by-default +- ** optimizations will have bitmasks of 0x10000 or larger. ++ ** The default MASK is and always shall be 0x0fffe. In the current ++ ** implementation, the default mask only covers the 0x00002 optimization, ++ ** though additional optimizations that are covered by 0x0fffe might be ++ ** added in the future. Optimizations that are off by default and must ++ ** be explicitly requested have masks of 0x10000 or greater. + ** + ** DETERMINATION OF WHEN TO RUN ANALYZE + ** + ** In the current implementation, a table is analyzed if only if all of + ** the following are true: + ** +- ** (1) MASK bit 0x02 is set. ++ ** (1) MASK bit 0x00002 is set. ++ ** ++ ** (2) The table is an ordinary table, not a virtual table or view. + ** +- ** (2) The query planner used sqlite_stat1-style statistics for one or +- ** more indexes of the table at some point during the lifetime of +- ** the current connection. ++ ** (3) The table name does not begin with "sqlite_". + ** +- ** (3) One or more indexes of the table are currently unanalyzed OR +- ** the number of rows in the table has increased by 25 times or more +- ** since the last time ANALYZE was run. ++ ** (4) One or more of the following is true: ++ ** (4a) The 0x10000 MASK bit is set. ++ ** (4b) One or more indexes on the table lacks an entry ++ ** in the sqlite_stat1 table. ++ ** (4c) The query planner used sqlite_stat1-style statistics for one ++ ** or more indexes of the table at some point during the lifetime ++ ** of the current connection. ++ ** ++ ** (5) One or more of the following is true: ++ ** (5a) One or more indexes on the table lacks an entry ++ ** in the sqlite_stat1 table. (Same as 4a) ++ ** (5b) The number of rows in the table has increased or decreased by ++ ** 10-fold. In other words, the current size of the table is ++ ** 10 times larger than the size in sqlite_stat1 or else the ++ ** current size is less than 1/10th the size in sqlite_stat1. + ** + ** The rules for when tables are analyzed are likely to change in +- ** future releases. ++ ** future releases. Future versions of SQLite might accept a string ++ ** literal argument to this pragma that contains a mnemonic description ++ ** of the options rather than a bitmap. + */ + case PragTyp_OPTIMIZE: { + int iDbLast; /* Loop termination point for the schema loop */ +@@ -127821,9 +143159,13 @@ SQLITE_PRIVATE void sqlite3Pragma( + Schema *pSchema; /* The current schema */ + Table *pTab; /* A table in the schema */ + Index *pIdx; /* An index of the table */ +- LogEst szThreshold; /* Size threshold above which reanalysis is needd */ ++ LogEst szThreshold; /* Size threshold above which reanalysis needed */ + char *zSubSql; /* SQL statement for the OP_SqlExec opcode */ + u32 opMask; /* Mask of operations to perform */ ++ int nLimit; /* Analysis limit to use */ ++ int nCheck = 0; /* Number of tables to be optimized */ ++ int nBtree = 0; /* Number of btrees to scan */ ++ int nIndex; /* Number of indexes on the current table */ + + if( zRight ){ + opMask = (u32)sqlite3Atoi(zRight); +@@ -127831,6 +143173,14 @@ SQLITE_PRIVATE void sqlite3Pragma( + }else{ + opMask = 0xfffe; + } ++ if( (opMask & 0x10)==0 ){ ++ nLimit = 0; ++ }else if( db->nAnalysisLimit>0 ++ && db->nAnalysisLimitnTab++; + for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){ + if( iDb==1 ) continue; +@@ -127839,23 +143189,61 @@ SQLITE_PRIVATE void sqlite3Pragma( + for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ + pTab = (Table*)sqliteHashData(k); + +- /* If table pTab has not been used in a way that would benefit from +- ** having analysis statistics during the current session, then skip it. +- ** This also has the effect of skipping virtual tables and views */ +- if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue; ++ /* This only works for ordinary tables */ ++ if( !IsOrdinaryTable(pTab) ) continue; + +- /* Reanalyze if the table is 25 times larger than the last analysis */ +- szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 ); ++ /* Do not scan system tables */ ++ if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue; ++ ++ /* Find the size of the table as last recorded in sqlite_stat1. ++ ** If any index is unanalyzed, then the threshold is -1 to ++ ** indicate a new, unanalyzed index ++ */ ++ szThreshold = pTab->nRowLogEst; ++ nIndex = 0; + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ ++ nIndex++; + if( !pIdx->hasStat1 ){ +- szThreshold = 0; /* Always analyze if any index lacks statistics */ +- break; ++ szThreshold = -1; /* Always analyze if any index lacks statistics */ + } + } +- if( szThreshold ){ +- sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); +- sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur, +- sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold); ++ ++ /* If table pTab has not been used in a way that would benefit from ++ ** having analysis statistics during the current session, then skip it, ++ ** unless the 0x10000 MASK bit is set. */ ++ if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){ ++ /* Check for size change if stat1 has been used for a query */ ++ }else if( opMask & 0x10000 ){ ++ /* Check for size change if 0x10000 is set */ ++ }else if( pTab->pIndex!=0 && szThreshold<0 ){ ++ /* Do analysis if unanalyzed indexes exists */ ++ }else{ ++ /* Otherwise, we can skip this table */ ++ continue; ++ } ++ ++ nCheck++; ++ if( nCheck==2 ){ ++ /* If ANALYZE might be invoked two or more times, hold a write ++ ** transaction for efficiency */ ++ sqlite3BeginWriteOperation(pParse, 0, iDb); ++ } ++ nBtree += nIndex+1; ++ ++ /* Reanalyze if the table is 10 times larger or smaller than ++ ** the last analysis. Unconditional reanalysis if there are ++ ** unanalyzed indexes. */ ++ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); ++ if( szThreshold>=0 ){ ++ const LogEst iRange = 33; /* 10x size change */ ++ sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur, ++ sqlite3VdbeCurrentAddr(v)+2+(opMask&1), ++ szThreshold>=iRange ? szThreshold-iRange : -1, ++ szThreshold+iRange); ++ VdbeCoverage(v); ++ }else{ ++ sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur, ++ sqlite3VdbeCurrentAddr(v)+2+(opMask&1)); + VdbeCoverage(v); + } + zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"", +@@ -127865,11 +143253,27 @@ SQLITE_PRIVATE void sqlite3Pragma( + sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC); + sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1); + }else{ +- sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC); ++ sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0, ++ zSubSql, P4_DYNAMIC); + } + } + } + sqlite3VdbeAddOp0(v, OP_Expire); ++ ++ /* In a schema with a large number of tables and indexes, scale back ++ ** the analysis_limit to avoid excess run-time in the worst case. ++ */ ++ if( !db->mallocFailed && nLimit>0 && nBtree>100 ){ ++ int iAddr, iEnd; ++ VdbeOp *aOp; ++ nLimit = 100*nLimit/nBtree; ++ if( nLimit<100 ) nLimit = 100; ++ aOp = sqlite3VdbeGetOp(v, 0); ++ iEnd = sqlite3VdbeCurrentAddr(v); ++ for(iAddr=0; iAddr=0 + ){ + db->nAnalysisLimit = (int)(N&0x7fffffff); + } +- returnSingleInt(v, db->nAnalysisLimit); ++ returnSingleInt(v, db->nAnalysisLimit); /* IMP: R-57594-65522 */ + break; + } + +@@ -128133,9 +143537,9 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + seen[0] = 0; + seen[1] = 0; + for(i=0; inConstraint; i++, pConstraint++){ +- if( pConstraint->usable==0 ) continue; +- if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; + if( pConstraint->iColumn < pTab->iHidden ) continue; ++ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; ++ if( pConstraint->usable==0 ) return SQLITE_CONSTRAINT; + j = pConstraint->iColumn - pTab->iHidden; + assert( j < 2 ); + seen[j] = i+1; +@@ -128148,12 +143552,13 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + j = seen[0]-1; + pIdxInfo->aConstraintUsage[j].argvIndex = 1; + pIdxInfo->aConstraintUsage[j].omit = 1; +- if( seen[1]==0 ) return SQLITE_OK; + pIdxInfo->estimatedCost = (double)20; + pIdxInfo->estimatedRows = 20; +- j = seen[1]-1; +- pIdxInfo->aConstraintUsage[j].argvIndex = 2; +- pIdxInfo->aConstraintUsage[j].omit = 1; ++ if( seen[1] ){ ++ j = seen[1]-1; ++ pIdxInfo->aConstraintUsage[j].argvIndex = 2; ++ pIdxInfo->aConstraintUsage[j].omit = 1; ++ } + return SQLITE_OK; + } + +@@ -128173,6 +143578,7 @@ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ + int i; + sqlite3_finalize(pCsr->pPragma); + pCsr->pPragma = 0; ++ pCsr->iRowid = 0; + for(i=0; iazArg); i++){ + sqlite3_free(pCsr->azArg[i]); + pCsr->azArg[i] = 0; +@@ -128313,7 +143719,8 @@ static const sqlite3_module pragmaVtabModule = { + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ +- 0 /* xShadowName */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + + /* +@@ -128360,7 +143767,7 @@ SQLITE_PRIVATE Module *sqlite3PragmaVtabRegister(sqlite3 *db, const char *zName) + */ + static void corruptSchema( + InitData *pData, /* Initialization context */ +- const char *zObj, /* Object being parsed at the point of error */ ++ char **azObj, /* Type and name of object being parsed */ + const char *zExtra /* Error information */ + ){ + sqlite3 *db = pData->db; +@@ -128368,14 +143775,23 @@ static void corruptSchema( + pData->rc = SQLITE_NOMEM_BKPT; + }else if( pData->pzErrMsg[0]!=0 ){ + /* A error message has already been generated. Do not overwrite it */ +- }else if( pData->mInitFlags & INITFLAG_AlterTable ){ +- *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra); ++ }else if( pData->mInitFlags & (INITFLAG_AlterMask) ){ ++ static const char *azAlterType[] = { ++ "rename", ++ "drop column", ++ "add column" ++ }; ++ *pData->pzErrMsg = sqlite3MPrintf(db, ++ "error in %s %s after %s: %s", azObj[0], azObj[1], ++ azAlterType[(pData->mInitFlags&INITFLAG_AlterMask)-1], ++ zExtra ++ ); + pData->rc = SQLITE_ERROR; + }else if( db->flags & SQLITE_WriteSchema ){ + pData->rc = SQLITE_CORRUPT_BKPT; + }else{ + char *z; +- if( zObj==0 ) zObj = "?"; ++ const char *zObj = azObj[1] ? azObj[1] : "?"; + z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj); + if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); + *pData->pzErrMsg = z; +@@ -128431,21 +143847,28 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char + UNUSED_PARAMETER2(NotUsed, argc); + assert( sqlite3_mutex_held(db->mutex) ); + db->mDbFlags |= DBFLAG_EncodingFixed; ++ if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ + pData->nInitRow++; + if( db->mallocFailed ){ +- corruptSchema(pData, argv[1], 0); ++ corruptSchema(pData, argv, 0); + return 1; + } + + assert( iDb>=0 && iDbnDb ); +- if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ + if( argv[3]==0 ){ +- corruptSchema(pData, argv[1], 0); +- }else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){ ++ corruptSchema(pData, argv, 0); ++ }else if( argv[4] ++ && 'c'==sqlite3UpperToLower[(unsigned char)argv[4][0]] ++ && 'r'==sqlite3UpperToLower[(unsigned char)argv[4][1]] ){ + /* Call the parser to process a CREATE TABLE, INDEX or VIEW. + ** But because db->init.busy is set to 1, no VDBE code is generated + ** or executed. All the parser does is build the internal data + ** structures that describe the table, index, or view. ++ ** ++ ** No other valid SQL statement, other than the variable CREATE statements, ++ ** can begin with the letters "C" and "R". Thus, it is not possible run ++ ** any other kind of statement while parsing the schema, even a corrupt ++ ** schema. + */ + int rc; + u8 saved_iDb = db->init.iDb; +@@ -128458,11 +143881,11 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char + || (db->init.newTnum>pData->mxPage && pData->mxPage>0) + ){ + if( sqlite3Config.bExtraSchemaChecks ){ +- corruptSchema(pData, argv[1], "invalid rootpage"); ++ corruptSchema(pData, argv, "invalid rootpage"); + } + } + db->init.orphanTrigger = 0; +- db->init.azInit = argv; ++ db->init.azInit = (const char**)argv; + pStmt = 0; + TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); + rc = db->errCode; +@@ -128477,13 +143900,14 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char + if( rc==SQLITE_NOMEM ){ + sqlite3OomFault(db); + }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){ +- corruptSchema(pData, argv[1], sqlite3_errmsg(db)); ++ corruptSchema(pData, argv, sqlite3_errmsg(db)); + } + } + } ++ db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */ + sqlite3_finalize(pStmt); + }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){ +- corruptSchema(pData, argv[1], 0); ++ corruptSchema(pData, argv, 0); + }else{ + /* If the SQL column is blank it means this is an index that + ** was created to be the PRIMARY KEY or to fulfill a UNIQUE +@@ -128494,7 +143918,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char + Index *pIndex; + pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); + if( pIndex==0 ){ +- corruptSchema(pData, argv[1], "orphan index"); ++ corruptSchema(pData, argv, "orphan index"); + }else + if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0 + || pIndex->tnum<2 +@@ -128502,7 +143926,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char + || sqlite3IndexHasDuplicateRootPage(pIndex) + ){ + if( sqlite3Config.bExtraSchemaChecks ){ +- corruptSchema(pData, argv[1], "invalid rootpage"); ++ corruptSchema(pData, argv, "invalid rootpage"); + } + } + } +@@ -128579,7 +144003,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed before this function returns. */ + sqlite3BtreeEnter(pDb->pBt); +- if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ ++ if( sqlite3BtreeTxnState(pDb->pBt)==SQLITE_TXN_NONE ){ + rc = sqlite3BtreeBeginTrans(pDb->pBt, 0, 0); + if( rc!=SQLITE_OK ){ + sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc)); +@@ -128705,18 +144129,22 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl + } + #endif + } ++ assert( pDb == &(db->aDb[iDb]) ); + if( db->mallocFailed ){ + rc = SQLITE_NOMEM_BKPT; + sqlite3ResetAllSchemasOfConnection(db); +- } +- if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ +- /* Black magic: If the SQLITE_NoSchemaError flag is set, then consider +- ** the schema loaded, even if errors occurred. In this situation the +- ** current sqlite3_prepare() operation will fail, but the following one +- ** will attempt to compile the supplied statement against whatever subset +- ** of the schema was loaded before the error occurred. The primary +- ** purpose of this is to allow access to the sqlite_schema table +- ** even when its contents have been corrupted. ++ pDb = &db->aDb[iDb]; ++ }else ++ if( rc==SQLITE_OK || ((db->flags&SQLITE_NoSchemaError) && rc!=SQLITE_NOMEM)){ ++ /* Hack: If the SQLITE_NoSchemaError flag is set, then consider ++ ** the schema loaded, even if errors (other than OOM) occurred. In ++ ** this situation the current sqlite3_prepare() operation will fail, ++ ** but the following one will attempt to compile the supplied statement ++ ** against whatever subset of the schema was loaded before the error ++ ** occurred. ++ ** ++ ** The primary purpose of this is to allow access to the sqlite_schema ++ ** table even when its contents have been corrupted. + */ + DbSetProperty(db, iDb, DB_SchemaLoaded); + rc = SQLITE_OK; +@@ -128822,10 +144250,11 @@ static void schemaIsValid(Parse *pParse){ + /* If there is not already a read-only (or read-write) transaction opened + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed immediately after reading the meta-value. */ +- if( !sqlite3BtreeIsInReadTrans(pBt) ){ ++ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_NONE ){ + rc = sqlite3BtreeBeginTrans(pBt, 0, 0); + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ + sqlite3OomFault(db); ++ pParse->rc = SQLITE_NOMEM; + } + if( rc!=SQLITE_OK ) return; + openedTransaction = 1; +@@ -128837,8 +144266,8 @@ static void schemaIsValid(Parse *pParse){ + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ ++ if( DbHasProperty(db, iDb, DB_SchemaLoaded) ) pParse->rc = SQLITE_SCHEMA; + sqlite3ResetOneSchema(db, iDb); +- pParse->rc = SQLITE_SCHEMA; + } + + /* Close the transaction, if one was opened. */ +@@ -128883,35 +144312,115 @@ SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ + } + + /* +-** Deallocate a single AggInfo object ++** Free all memory allocations in the pParse object + */ +-static void agginfoFree(sqlite3 *db, AggInfo *p){ +- sqlite3DbFree(db, p->aCol); +- sqlite3DbFree(db, p->aFunc); +- sqlite3DbFree(db, p); ++SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){ ++ sqlite3 *db = pParse->db; ++ assert( db!=0 ); ++ assert( db->pParse==pParse ); ++ assert( pParse->nested==0 ); ++#ifndef SQLITE_OMIT_SHARED_CACHE ++ if( pParse->aTableLock ) sqlite3DbNNFreeNN(db, pParse->aTableLock); ++#endif ++ while( pParse->pCleanup ){ ++ ParseCleanup *pCleanup = pParse->pCleanup; ++ pParse->pCleanup = pCleanup->pNext; ++ pCleanup->xCleanup(db, pCleanup->pPtr); ++ sqlite3DbNNFreeNN(db, pCleanup); ++ } ++ if( pParse->aLabel ) sqlite3DbNNFreeNN(db, pParse->aLabel); ++ if( pParse->pConstExpr ){ ++ sqlite3ExprListDelete(db, pParse->pConstExpr); ++ } ++ assert( db->lookaside.bDisable >= pParse->disableLookaside ); ++ db->lookaside.bDisable -= pParse->disableLookaside; ++ db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; ++ assert( pParse->db->pParse==pParse ); ++ db->pParse = pParse->pOuterParse; + } + + /* +-** Free all memory allocations in the pParse object +-*/ +-SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){ +- sqlite3 *db = pParse->db; +- AggInfo *pThis = pParse->pAggList; +- while( pThis ){ +- AggInfo *pNext = pThis->pNext; +- agginfoFree(db, pThis); +- pThis = pNext; +- } +- sqlite3DbFree(db, pParse->aLabel); +- sqlite3ExprListDelete(db, pParse->pConstExpr); +- if( db ){ +- assert( db->lookaside.bDisable >= pParse->disableLookaside ); +- db->lookaside.bDisable -= pParse->disableLookaside; +- db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; ++** Add a new cleanup operation to a Parser. The cleanup should happen when ++** the parser object is destroyed. But, beware: the cleanup might happen ++** immediately. ++** ++** Use this mechanism for uncommon cleanups. There is a higher setup ++** cost for this mechanism (an extra malloc), so it should not be used ++** for common cleanups that happen on most calls. But for less ++** common cleanups, we save a single NULL-pointer comparison in ++** sqlite3ParseObjectReset(), which reduces the total CPU cycle count. ++** ++** If a memory allocation error occurs, then the cleanup happens immediately. ++** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the ++** pParse->earlyCleanup flag is set in that case. Calling code show verify ++** that test cases exist for which this happens, to guard against possible ++** use-after-free errors following an OOM. The preferred way to do this is ++** to immediately follow the call to this routine with: ++** ++** testcase( pParse->earlyCleanup ); ++** ++** This routine returns a copy of its pPtr input (the third parameter) ++** except if an early cleanup occurs, in which case it returns NULL. So ++** another way to check for early cleanup is to check the return value. ++** Or, stop using the pPtr parameter with this call and use only its ++** return value thereafter. Something like this: ++** ++** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj); ++*/ ++SQLITE_PRIVATE void *sqlite3ParserAddCleanup( ++ Parse *pParse, /* Destroy when this Parser finishes */ ++ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ ++ void *pPtr /* Pointer to object to be cleaned up */ ++){ ++ ParseCleanup *pCleanup; ++ if( sqlite3FaultSim(300) ){ ++ pCleanup = 0; ++ sqlite3OomFault(pParse->db); ++ }else{ ++ pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); ++ } ++ if( pCleanup ){ ++ pCleanup->pNext = pParse->pCleanup; ++ pParse->pCleanup = pCleanup; ++ pCleanup->pPtr = pPtr; ++ pCleanup->xCleanup = xCleanup; ++ }else{ ++ xCleanup(pParse->db, pPtr); ++ pPtr = 0; ++#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) ++ pParse->earlyCleanup = 1; ++#endif + } +- pParse->disableLookaside = 0; ++ return pPtr; + } + ++/* ++** Turn bulk memory into a valid Parse object and link that Parse object ++** into database connection db. ++** ++** Call sqlite3ParseObjectReset() to undo this operation. ++** ++** Caution: Do not confuse this routine with sqlite3ParseObjectInit() which ++** is generated by Lemon. ++*/ ++SQLITE_PRIVATE void sqlite3ParseObjectInit(Parse *pParse, sqlite3 *db){ ++ memset(PARSE_HDR(pParse), 0, PARSE_HDR_SZ); ++ memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); ++ assert( db->pParse!=pParse ); ++ pParse->pOuterParse = db->pParse; ++ db->pParse = pParse; ++ pParse->db = db; ++ if( db->mallocFailed ) sqlite3ErrorMsg(pParse, "out of memory"); ++} ++ ++/* ++** Maximum number of times that we will try again to prepare a statement ++** that returns SQLITE_ERROR_RETRY. ++*/ ++#ifndef SQLITE_MAX_PREPARE_RETRY ++# define SQLITE_MAX_PREPARE_RETRY 25 ++#endif ++ + /* + ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. + */ +@@ -128924,16 +144433,28 @@ static int sqlite3Prepare( + sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ + const char **pzTail /* OUT: End of parsed string */ + ){ +- char *zErrMsg = 0; /* Error message */ + int rc = SQLITE_OK; /* Result code */ + int i; /* Loop counter */ + Parse sParse; /* Parsing context */ + +- memset(&sParse, 0, PARSE_HDR_SZ); ++ /* sqlite3ParseObjectInit(&sParse, db); // inlined for performance */ ++ memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ); + memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); +- sParse.pReprepare = pReprepare; ++ sParse.pOuterParse = db->pParse; ++ db->pParse = &sParse; ++ sParse.db = db; ++ if( pReprepare ){ ++ sParse.pReprepare = pReprepare; ++ sParse.explain = sqlite3_stmt_isexplain((sqlite3_stmt*)pReprepare); ++ }else{ ++ assert( sParse.pReprepare==0 ); ++ } + assert( ppStmt && *ppStmt==0 ); +- /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ ++ if( db->mallocFailed ){ ++ sqlite3ErrorMsg(&sParse, "out of memory"); ++ db->errCode = rc = SQLITE_NOMEM; ++ goto end_prepare; ++ } + assert( sqlite3_mutex_held(db->mutex) ); + + /* For a long-term use prepared statement avoid the use of +@@ -128943,7 +144464,7 @@ static int sqlite3Prepare( + sParse.disableLookaside++; + DisableLookaside; + } +- sParse.disableVtab = (prepFlags & SQLITE_PREPARE_NO_VTAB)!=0; ++ sParse.prepFlags = prepFlags & 0xff; + + /* Check to verify that it is possible to get a read lock on all + ** database schemas. The inability to get a read lock indicates that +@@ -128984,9 +144505,10 @@ static int sqlite3Prepare( + } + } + +- sqlite3VtabUnlockList(db); ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ if( db->pDisconnect ) sqlite3VtabUnlockList(db); ++#endif + +- sParse.db = db; + if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ + char *zSqlCopy; + int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; +@@ -128999,23 +144521,17 @@ static int sqlite3Prepare( + } + zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes); + if( zSqlCopy ){ +- sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg); ++ sqlite3RunParser(&sParse, zSqlCopy); + sParse.zTail = &zSql[sParse.zTail-zSqlCopy]; + sqlite3DbFree(db, zSqlCopy); + }else{ + sParse.zTail = &zSql[nBytes]; + } + }else{ +- sqlite3RunParser(&sParse, zSql, &zErrMsg); ++ sqlite3RunParser(&sParse, zSql); + } + assert( 0==sParse.nQueryLoop ); + +- if( sParse.rc==SQLITE_DONE ){ +- sParse.rc = SQLITE_OK; +- } +- if( sParse.checkSchema ){ +- schemaIsValid(&sParse); +- } + if( pzTail ){ + *pzTail = sParse.zTail; + } +@@ -129025,21 +144541,30 @@ static int sqlite3Prepare( + } + if( db->mallocFailed ){ + sParse.rc = SQLITE_NOMEM_BKPT; ++ sParse.checkSchema = 0; + } +- rc = sParse.rc; +- if( rc!=SQLITE_OK ){ +- if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe); +- assert(!(*ppStmt)); ++ if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){ ++ if( sParse.checkSchema && db->init.busy==0 ){ ++ schemaIsValid(&sParse); ++ } ++ if( sParse.pVdbe ){ ++ sqlite3VdbeFinalize(sParse.pVdbe); ++ } ++ assert( 0==(*ppStmt) ); ++ rc = sParse.rc; ++ if( sParse.zErrMsg ){ ++ sqlite3ErrorWithMsg(db, rc, "%s", sParse.zErrMsg); ++ sqlite3DbFree(db, sParse.zErrMsg); ++ }else{ ++ sqlite3Error(db, rc); ++ } + }else{ ++ assert( sParse.zErrMsg==0 ); + *ppStmt = (sqlite3_stmt*)sParse.pVdbe; ++ rc = SQLITE_OK; ++ sqlite3ErrorClear(db); + } + +- if( zErrMsg ){ +- sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg); +- sqlite3DbFree(db, zErrMsg); +- }else{ +- sqlite3Error(db, rc); +- } + + /* Delete any TriggerPrg structures allocated while parsing this statement. */ + while( sParse.pTriggerPrg ){ +@@ -129050,7 +144575,7 @@ static int sqlite3Prepare( + + end_prepare: + +- sqlite3ParserReset(&sParse); ++ sqlite3ParseObjectReset(&sParse); + return rc; + } + static int sqlite3LockAndPrepare( +@@ -129080,12 +144605,15 @@ static int sqlite3LockAndPrepare( + ** reset is considered a permanent error. */ + rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); + assert( rc==SQLITE_OK || *ppStmt==0 ); +- }while( rc==SQLITE_ERROR_RETRY ++ if( rc==SQLITE_OK || db->mallocFailed ) break; ++ }while( (rc==SQLITE_ERROR_RETRY && (cnt++)errMask)==rc ); ++ db->busyHandler.nBusy = 0; + sqlite3_mutex_leave(db->mutex); ++ assert( rc==SQLITE_OK || (*ppStmt)==0 ); + return rc; + } + +@@ -129218,12 +144746,24 @@ static int sqlite3Prepare16( + if( !sqlite3SafetyCheckOk(db)||zSql==0 ){ + return SQLITE_MISUSE_BKPT; + } ++ ++ /* Make sure nBytes is non-negative and correct. It should be the ++ ** number of bytes until the end of the input buffer or until the first ++ ** U+0000 character. If the input nBytes is odd, convert it into ++ ** an even number. If the input nBytes is negative, then the input ++ ** must be terminated by at least one U+0000 character */ + if( nBytes>=0 ){ + int sz; + const char *z = (const char*)zSql; + for(sz=0; szmutex); + zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); + if( zSql8 ){ +@@ -129237,7 +144777,7 @@ static int sqlite3Prepare16( + ** the same number of characters into the UTF-16 string. + */ + int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8)); +- *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed); ++ *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, nBytes, chars_parsed); + } + sqlite3DbFree(db, zSql8); + rc = sqlite3ApiExit(db, rc); +@@ -129320,7 +144860,7 @@ SQLITE_API int sqlite3_prepare16_v3( + */ + typedef struct DistinctCtx DistinctCtx; + struct DistinctCtx { +- u8 isTnct; /* True if the DISTINCT keyword is present */ ++ u8 isTnct; /* 0: Not distinct. 1: DISTICT 2: DISTINCT and ORDER BY */ + u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */ + int tabTnct; /* Ephemeral table used for DISTINCT processing */ + int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */ +@@ -129364,6 +144904,10 @@ struct SortCtx { + } aDefer[4]; + #endif + struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */ ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ int addrPush; /* First instruction to push data into sorter */ ++ int addrPushEnd; /* Last instruction that pushes data into sorter */ ++#endif + }; + #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ + +@@ -129375,6 +144919,7 @@ struct SortCtx { + ** If bFree==0, Leave the first Select object unfreed + */ + static void clearSelect(sqlite3 *db, Select *p, int bFree){ ++ assert( db!=0 ); + while( p ){ + Select *pPrior = p->pPrior; + sqlite3ExprListDelete(db, p->pEList); +@@ -129384,13 +144929,17 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){ + sqlite3ExprDelete(db, p->pHaving); + sqlite3ExprListDelete(db, p->pOrderBy); + sqlite3ExprDelete(db, p->pLimit); ++ if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); + #ifndef SQLITE_OMIT_WINDOWFUNC + if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){ + sqlite3WindowListDelete(db, p->pWinDefn); + } ++ while( p->pWin ){ ++ assert( p->pWin->ppThis==&p->pWin ); ++ sqlite3WindowUnlinkFromSelect(p->pWin); ++ } + #endif +- if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); +- if( bFree ) sqlite3DbFreeNN(db, p); ++ if( bFree ) sqlite3DbNNFreeNN(db, p); + p = pPrior; + bFree = 1; + } +@@ -129444,7 +144993,7 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( + pNew->addrOpenEphm[0] = -1; + pNew->addrOpenEphm[1] = -1; + pNew->nSelectRow = 0; +- if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc)); ++ if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); + pNew->pSrc = pSrc; + pNew->pWhere = pWhere; + pNew->pGroupBy = pGroupBy; +@@ -129474,6 +145023,9 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( + SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ + if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1); + } ++SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){ ++ if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1); ++} + + /* + ** Return a pointer to the right-most SELECT statement in a compound. +@@ -129499,6 +145051,52 @@ static Select *findRightmost(Select *p){ + ** + ** If an illegal or unsupported join type is seen, then still return + ** a join type, but put an error in the pParse structure. ++** ++** These are the valid join types: ++** ++** ++** pA pB pC Return Value ++** ------- ----- ----- ------------ ++** CROSS - - JT_CROSS ++** INNER - - JT_INNER ++** LEFT - - JT_LEFT|JT_OUTER ++** LEFT OUTER - JT_LEFT|JT_OUTER ++** RIGHT - - JT_RIGHT|JT_OUTER ++** RIGHT OUTER - JT_RIGHT|JT_OUTER ++** FULL - - JT_LEFT|JT_RIGHT|JT_OUTER ++** FULL OUTER - JT_LEFT|JT_RIGHT|JT_OUTER ++** NATURAL INNER - JT_NATURAL|JT_INNER ++** NATURAL LEFT - JT_NATURAL|JT_LEFT|JT_OUTER ++** NATURAL LEFT OUTER JT_NATURAL|JT_LEFT|JT_OUTER ++** NATURAL RIGHT - JT_NATURAL|JT_RIGHT|JT_OUTER ++** NATURAL RIGHT OUTER JT_NATURAL|JT_RIGHT|JT_OUTER ++** NATURAL FULL - JT_NATURAL|JT_LEFT|JT_RIGHT ++** NATURAL FULL OUTER JT_NATRUAL|JT_LEFT|JT_RIGHT ++** ++** To preserve historical compatibly, SQLite also accepts a variety ++** of other non-standard and in many cases nonsensical join types. ++** This routine makes as much sense at it can from the nonsense join ++** type and returns a result. Examples of accepted nonsense join types ++** include but are not limited to: ++** ++** INNER CROSS JOIN -> same as JOIN ++** NATURAL CROSS JOIN -> same as NATURAL JOIN ++** OUTER LEFT JOIN -> same as LEFT JOIN ++** LEFT NATURAL JOIN -> same as NATURAL LEFT JOIN ++** LEFT RIGHT JOIN -> same as FULL JOIN ++** RIGHT OUTER FULL JOIN -> same as FULL JOIN ++** CROSS CROSS CROSS JOIN -> same as JOIN ++** ++** The only restrictions on the join type name are: ++** ++** * "INNER" cannot appear together with "OUTER", "LEFT", "RIGHT", ++** or "FULL". ++** ++** * "CROSS" cannot appear together with "OUTER", "LEFT", "RIGHT, ++** or "FULL". ++** ++** * If "OUTER" is present then there must also be one of ++** "LEFT", "RIGHT", or "FULL" + */ + SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ + int jointype = 0; +@@ -129511,13 +145109,13 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p + u8 nChar; /* Length of the keyword in characters */ + u8 code; /* Join type mask */ + } aKeyword[] = { +- /* natural */ { 0, 7, JT_NATURAL }, +- /* left */ { 6, 4, JT_LEFT|JT_OUTER }, +- /* outer */ { 10, 5, JT_OUTER }, +- /* right */ { 14, 5, JT_RIGHT|JT_OUTER }, +- /* full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, +- /* inner */ { 23, 5, JT_INNER }, +- /* cross */ { 28, 5, JT_INNER|JT_CROSS }, ++ /* (0) natural */ { 0, 7, JT_NATURAL }, ++ /* (1) left */ { 6, 4, JT_LEFT|JT_OUTER }, ++ /* (2) outer */ { 10, 5, JT_OUTER }, ++ /* (3) right */ { 14, 5, JT_RIGHT|JT_OUTER }, ++ /* (4) full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, ++ /* (5) inner */ { 23, 5, JT_INNER }, ++ /* (6) cross */ { 28, 5, JT_INNER|JT_CROSS }, + }; + int i, j; + apAll[0] = pA; +@@ -129540,18 +145138,15 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p + } + if( + (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || +- (jointype & JT_ERROR)!=0 ++ (jointype & JT_ERROR)!=0 || ++ (jointype & (JT_OUTER|JT_LEFT|JT_RIGHT))==JT_OUTER + ){ +- const char *zSp = " "; +- assert( pB!=0 ); +- if( pC==0 ){ zSp++; } +- sqlite3ErrorMsg(pParse, "unknown or unsupported join type: " +- "%T %T%s%T", pA, pB, zSp, pC); +- jointype = JT_INNER; +- }else if( (jointype & JT_OUTER)!=0 +- && (jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ){ +- sqlite3ErrorMsg(pParse, +- "RIGHT and FULL OUTER JOINs are not currently supported"); ++ const char *zSp1 = " "; ++ const char *zSp2 = " "; ++ if( pB==0 ){ zSp1++; } ++ if( pC==0 ){ zSp2++; } ++ sqlite3ErrorMsg(pParse, "unknown join type: " ++ "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC); + jointype = JT_INNER; + } + return jointype; +@@ -129561,19 +145156,61 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p + ** Return the index of a column in a table. Return -1 if the column + ** is not contained in the table. + */ +-static int columnIndex(Table *pTab, const char *zCol){ ++SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol){ + int i; +- u8 h = sqlite3StrIHash(zCol); +- Column *pCol; +- for(pCol=pTab->aCol, i=0; inCol; pCol++, i++){ +- if( pCol->hName==h && sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; ++ u8 h; ++ const Column *aCol; ++ int nCol; ++ ++ h = sqlite3StrIHash(zCol); ++ aCol = pTab->aCol; ++ nCol = pTab->nCol; ++ ++ /* See if the aHx gives us a lucky match */ ++ i = pTab->aHx[h % sizeof(pTab->aHx)]; ++ assert( i=nCol ) break; + } + return -1; + } + + /* +-** Search the first N tables in pSrc, from left to right, looking for a +-** table that has a column named zCol. ++** Mark a subquery result column as having been used. ++*/ ++SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){ ++ assert( pItem!=0 ); ++ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); ++ if( pItem->fg.isNestedFrom ){ ++ ExprList *pResults; ++ assert( pItem->fg.isSubquery ); ++ assert( pItem->u4.pSubq!=0 ); ++ assert( pItem->u4.pSubq->pSelect!=0 ); ++ pResults = pItem->u4.pSubq->pSelect->pEList; ++ assert( pResults!=0 ); ++ assert( iCol>=0 && iColnExpr ); ++ pResults->a[iCol].fg.bUsed = 1; ++ } ++} ++ ++/* ++** Search the tables iStart..iEnd (inclusive) in pSrc, looking for a ++** table that has a column named zCol. The search is left-to-right. ++** The first match found is returned. + ** + ** When found, set *piTab and *piCol to the table index and column index + ** of the matching column and return TRUE. +@@ -129582,22 +145219,27 @@ static int columnIndex(Table *pTab, const char *zCol){ + */ + static int tableAndColumnIndex( + SrcList *pSrc, /* Array of tables to search */ +- int N, /* Number of tables in pSrc->a[] to search */ ++ int iStart, /* First member of pSrc->a[] to check */ ++ int iEnd, /* Last member of pSrc->a[] to check */ + const char *zCol, /* Name of the column we are looking for */ + int *piTab, /* Write index of pSrc->a[] here */ + int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */ +- int bIgnoreHidden /* True to ignore hidden columns */ ++ int bIgnoreHidden /* Ignore hidden columns */ + ){ + int i; /* For looping over tables in pSrc */ + int iCol; /* Index of column matching zCol */ + ++ assert( iEndnSrc ); ++ assert( iStart>=0 ); + assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ +- for(i=0; ia[i].pTab, zCol); ++ ++ for(i=iStart; i<=iEnd; i++){ ++ iCol = sqlite3ColumnIndex(pSrc->a[i].pSTab, zCol); + if( iCol>=0 +- && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0) ++ && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pSTab->aCol[iCol])==0) + ){ + if( piTab ){ ++ sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol); + *piTab = i; + *piCol = iCol; + } +@@ -129608,63 +145250,19 @@ static int tableAndColumnIndex( + } + + /* +-** This function is used to add terms implied by JOIN syntax to the +-** WHERE clause expression of a SELECT statement. The new term, which +-** is ANDed with the existing WHERE clause, is of the form: +-** +-** (tab1.col1 = tab2.col2) +-** +-** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the +-** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is +-** column iColRight of tab2. +-*/ +-static void addWhereTerm( +- Parse *pParse, /* Parsing context */ +- SrcList *pSrc, /* List of tables in FROM clause */ +- int iLeft, /* Index of first table to join in pSrc */ +- int iColLeft, /* Index of column in first table */ +- int iRight, /* Index of second table in pSrc */ +- int iColRight, /* Index of column in second table */ +- int isOuterJoin, /* True if this is an OUTER join */ +- Expr **ppWhere /* IN/OUT: The WHERE clause to add to */ +-){ +- sqlite3 *db = pParse->db; +- Expr *pE1; +- Expr *pE2; +- Expr *pEq; +- +- assert( iLeftnSrc>iRight ); +- assert( pSrc->a[iLeft].pTab ); +- assert( pSrc->a[iRight].pTab ); +- +- pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); +- pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); +- +- pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2); +- if( pEq && isOuterJoin ){ +- ExprSetProperty(pEq, EP_FromJoin); +- assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); +- ExprSetVVAProperty(pEq, EP_NoReduce); +- pEq->iRightJoinTable = (i16)pE2->iTable; +- } +- *ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq); +-} +- +-/* +-** Set the EP_FromJoin property on all terms of the given expression. +-** And set the Expr.iRightJoinTable to iTable for every term in the ++** Set the EP_OuterON property on all terms of the given expression. ++** And set the Expr.w.iJoin to iTable for every term in the + ** expression. + ** +-** The EP_FromJoin property is used on terms of an expression to tell +-** the LEFT OUTER JOIN processing logic that this term is part of the ++** The EP_OuterON property is used on terms of an expression to tell ++** the OUTER JOIN processing logic that this term is part of the + ** join restriction specified in the ON or USING clause and not a part + ** of the more general WHERE clause. These terms are moved over to the + ** WHERE clause during join processing but we need to remember that they + ** originated in the ON or USING clause. + ** +-** The Expr.iRightJoinTable tells the WHERE clause processing that the +-** expression depends on table iRightJoinTable even if that table is not ++** The Expr.w.iJoin tells the WHERE clause processing that the ++** expression depends on table w.iJoin even if that table is not + ** explicitly mentioned in the expression. That information is needed + ** for cases like this: + ** +@@ -129677,114 +145275,132 @@ static void addWhereTerm( + ** after the t1 loop and rows with t1.x!=5 will never appear in + ** the output, which is incorrect. + */ +-SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable){ ++SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){ ++ assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON ); + while( p ){ +- ExprSetProperty(p, EP_FromJoin); ++ ExprSetProperty(p, joinFlag); + assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); + ExprSetVVAProperty(p, EP_NoReduce); +- p->iRightJoinTable = (i16)iTable; +- if( p->op==TK_FUNCTION && p->x.pList ){ +- int i; +- for(i=0; ix.pList->nExpr; i++){ +- sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable); ++ p->w.iJoin = iTable; ++ if( p->op==TK_FUNCTION ){ ++ assert( ExprUseXList(p) ); ++ if( p->x.pList ){ ++ int i; ++ for(i=0; ix.pList->nExpr; i++){ ++ sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag); ++ } + } + } +- sqlite3SetJoinExpr(p->pLeft, iTable); ++ sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag); + p = p->pRight; + } + } + +-/* Undo the work of sqlite3SetJoinExpr(). In the expression p, convert every +-** term that is marked with EP_FromJoin and iRightJoinTable==iTable into +-** an ordinary term that omits the EP_FromJoin mark. ++/* Undo the work of sqlite3SetJoinExpr(). This is used when a LEFT JOIN ++** is simplified into an ordinary JOIN, and when an ON expression is ++** "pushed down" into the WHERE clause of a subquery. ++** ++** Convert every term that is marked with EP_OuterON and w.iJoin==iTable into ++** an ordinary term that omits the EP_OuterON mark. Or if iTable<0, then ++** just clear every EP_OuterON and EP_InnerON mark from the expression tree. + ** +-** This happens when a LEFT JOIN is simplified into an ordinary JOIN. ++** If nullable is true, that means that Expr p might evaluate to NULL even ++** if it is a reference to a NOT NULL column. This can happen, for example, ++** if the table that p references is on the left side of a RIGHT JOIN. ++** If nullable is true, then take care to not remove the EP_CanBeNull bit. ++** See forum thread https://sqlite.org/forum/forumpost/b40696f50145d21c + */ +-static void unsetJoinExpr(Expr *p, int iTable){ ++static void unsetJoinExpr(Expr *p, int iTable, int nullable){ + while( p ){ +- if( ExprHasProperty(p, EP_FromJoin) +- && (iTable<0 || p->iRightJoinTable==iTable) ){ +- ExprClearProperty(p, EP_FromJoin); ++ if( iTable<0 || (ExprHasProperty(p, EP_OuterON) && p->w.iJoin==iTable) ){ ++ ExprClearProperty(p, EP_OuterON|EP_InnerON); ++ if( iTable>=0 ) ExprSetProperty(p, EP_InnerON); + } +- if( p->op==TK_FUNCTION && p->x.pList ){ +- int i; +- for(i=0; ix.pList->nExpr; i++){ +- unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); ++ if( p->op==TK_COLUMN && p->iTable==iTable && !nullable ){ ++ ExprClearProperty(p, EP_CanBeNull); ++ } ++ if( p->op==TK_FUNCTION ){ ++ assert( ExprUseXList(p) ); ++ assert( p->pLeft==0 ); ++ if( p->x.pList ){ ++ int i; ++ for(i=0; ix.pList->nExpr; i++){ ++ unsetJoinExpr(p->x.pList->a[i].pExpr, iTable, nullable); ++ } + } + } +- unsetJoinExpr(p->pLeft, iTable); ++ unsetJoinExpr(p->pLeft, iTable, nullable); + p = p->pRight; + } + } + + /* + ** This routine processes the join information for a SELECT statement. +-** ON and USING clauses are converted into extra terms of the WHERE clause. +-** NATURAL joins also create extra WHERE clause terms. ++** ++** * A NATURAL join is converted into a USING join. After that, we ++** do not need to be concerned with NATURAL joins and we only have ++** think about USING joins. ++** ++** * ON and USING clauses result in extra terms being added to the ++** WHERE clause to enforce the specified constraints. The extra ++** WHERE clause terms will be tagged with EP_OuterON or ++** EP_InnerON so that we know that they originated in ON/USING. + ** + ** The terms of a FROM clause are contained in the Select.pSrc structure. + ** The left most table is the first entry in Select.pSrc. The right-most + ** table is the last entry. The join operator is held in the entry to +-** the left. Thus entry 0 contains the join operator for the join between ++** the right. Thus entry 1 contains the join operator for the join between + ** entries 0 and 1. Any ON or USING clauses associated with the join are +-** also attached to the left entry. ++** also attached to the right entry. + ** + ** This routine returns the number of errors encountered. + */ +-static int sqliteProcessJoin(Parse *pParse, Select *p){ ++static int sqlite3ProcessJoin(Parse *pParse, Select *p){ + SrcList *pSrc; /* All tables in the FROM clause */ + int i, j; /* Loop counters */ +- struct SrcList_item *pLeft; /* Left table being joined */ +- struct SrcList_item *pRight; /* Right table being joined */ ++ SrcItem *pLeft; /* Left table being joined */ ++ SrcItem *pRight; /* Right table being joined */ + + pSrc = p->pSrc; + pLeft = &pSrc->a[0]; + pRight = &pLeft[1]; + for(i=0; inSrc-1; i++, pRight++, pLeft++){ +- Table *pRightTab = pRight->pTab; +- int isOuter; ++ Table *pRightTab = pRight->pSTab; ++ u32 joinType; + +- if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; +- isOuter = (pRight->fg.jointype & JT_OUTER)!=0; ++ if( NEVER(pLeft->pSTab==0 || pRightTab==0) ) continue; ++ joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; + +- /* When the NATURAL keyword is present, add WHERE clause terms for +- ** every column that the two tables have in common. ++ /* If this is a NATURAL join, synthesize an appropriate USING clause ++ ** to specify which columns should be joined. + */ + if( pRight->fg.jointype & JT_NATURAL ){ +- if( pRight->pOn || pRight->pUsing ){ ++ IdList *pUsing = 0; ++ if( pRight->fg.isUsing || pRight->u3.pOn ){ + sqlite3ErrorMsg(pParse, "a NATURAL join may not have " + "an ON or USING clause", 0); + return 1; + } + for(j=0; jnCol; j++){ + char *zName; /* Name of column in the right table */ +- int iLeft; /* Matching left table */ +- int iLeftCol; /* Matching column in the left table */ + + if( IsHiddenColumn(&pRightTab->aCol[j]) ) continue; +- zName = pRightTab->aCol[j].zName; +- if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 1) ){ +- addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j, +- isOuter, &p->pWhere); ++ zName = pRightTab->aCol[j].zCnName; ++ if( tableAndColumnIndex(pSrc, 0, i, zName, 0, 0, 1) ){ ++ pUsing = sqlite3IdListAppend(pParse, pUsing, 0); ++ if( pUsing ){ ++ assert( pUsing->nId>0 ); ++ assert( pUsing->a[pUsing->nId-1].zName==0 ); ++ pUsing->a[pUsing->nId-1].zName = sqlite3DbStrDup(pParse->db, zName); ++ } + } + } +- } +- +- /* Disallow both ON and USING clauses in the same join +- */ +- if( pRight->pOn && pRight->pUsing ){ +- sqlite3ErrorMsg(pParse, "cannot have both ON and USING " +- "clauses in the same join"); +- return 1; +- } +- +- /* Add the ON clause to the end of the WHERE clause, connected by +- ** an AND operator. +- */ +- if( pRight->pOn ){ +- if( isOuter ) sqlite3SetJoinExpr(pRight->pOn, pRight->iCursor); +- p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->pOn); +- pRight->pOn = 0; ++ if( pUsing ){ ++ pRight->fg.isUsing = 1; ++ pRight->fg.isSynthUsing = 1; ++ pRight->u3.pUsing = pUsing; ++ } ++ if( pParse->nErr ) return 1; + } + + /* Create extra terms on the WHERE clause for each column named +@@ -129794,27 +145410,96 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ + ** Report an error if any column mentioned in the USING clause is + ** not contained in both tables to be joined. + */ +- if( pRight->pUsing ){ +- IdList *pList = pRight->pUsing; ++ if( pRight->fg.isUsing ){ ++ IdList *pList = pRight->u3.pUsing; ++ sqlite3 *db = pParse->db; ++ assert( pList!=0 ); + for(j=0; jnId; j++){ + char *zName; /* Name of the term in the USING clause */ + int iLeft; /* Table on the left with matching column name */ + int iLeftCol; /* Column number of matching column on the left */ + int iRightCol; /* Column number of matching column on the right */ ++ Expr *pE1; /* Reference to the column on the LEFT of the join */ ++ Expr *pE2; /* Reference to the column on the RIGHT of the join */ ++ Expr *pEq; /* Equality constraint. pE1 == pE2 */ + + zName = pList->a[j].zName; +- iRightCol = columnIndex(pRightTab, zName); ++ iRightCol = sqlite3ColumnIndex(pRightTab, zName); + if( iRightCol<0 +- || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0) ++ || tableAndColumnIndex(pSrc, 0, i, zName, &iLeft, &iLeftCol, ++ pRight->fg.isSynthUsing)==0 + ){ + sqlite3ErrorMsg(pParse, "cannot join using column %s - column " + "not present in both tables", zName); + return 1; + } +- addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol, +- isOuter, &p->pWhere); ++ pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); ++ sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); ++ if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 && pParse->nErr==0 ){ ++ /* This branch runs if the query contains one or more RIGHT or FULL ++ ** JOINs. If only a single table on the left side of this join ++ ** contains the zName column, then this branch is a no-op. ++ ** But if there are two or more tables on the left side ++ ** of the join, construct a coalesce() function that gathers all ++ ** such tables. Raise an error if more than one of those references ++ ** to zName is not also within a prior USING clause. ++ ** ++ ** We really ought to raise an error if there are two or more ++ ** non-USING references to zName on the left of an INNER or LEFT ++ ** JOIN. But older versions of SQLite do not do that, so we avoid ++ ** adding a new error so as to not break legacy applications. ++ */ ++ ExprList *pFuncArgs = 0; /* Arguments to the coalesce() */ ++ static const Token tkCoalesce = { "coalesce", 8 }; ++ assert( pE1!=0 ); ++ ExprSetProperty(pE1, EP_CanBeNull); ++ while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol, ++ pRight->fg.isSynthUsing)!=0 ){ ++ if( pSrc->a[iLeft].fg.isUsing==0 ++ || sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0 ++ ){ ++ sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()", ++ zName); ++ break; ++ } ++ pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1); ++ pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); ++ sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); ++ } ++ if( pFuncArgs ){ ++ pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1); ++ pE1 = sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0); ++ if( pE1 ){ ++ pE1->affExpr = SQLITE_AFF_DEFER; ++ } ++ } ++ }else if( (pSrc->a[i+1].fg.jointype & JT_LEFT)!=0 && pParse->nErr==0 ){ ++ assert( pE1!=0 ); ++ ExprSetProperty(pE1, EP_CanBeNull); ++ } ++ pE2 = sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol); ++ sqlite3SrcItemColumnUsed(pRight, iRightCol); ++ pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2); ++ assert( pE2!=0 || pEq==0 ); ++ if( pEq ){ ++ ExprSetProperty(pEq, joinType); ++ assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); ++ ExprSetVVAProperty(pEq, EP_NoReduce); ++ pEq->w.iJoin = pE2->iTable; ++ } ++ p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pEq); + } + } ++ ++ /* Add the ON clause to the end of the WHERE clause, connected by ++ ** an AND operator. ++ */ ++ else if( pRight->u3.pOn ){ ++ sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType); ++ p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn); ++ pRight->u3.pOn = 0; ++ pRight->fg.isOn = 1; ++ } + } + return 0; + } +@@ -129908,14 +145593,18 @@ static void pushOntoSorter( + ** (2) All output columns are included in the sort record. In that + ** case regData==regOrigData. + ** (3) Some output columns are omitted from the sort record due to +- ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the ++ ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the + ** SQLITE_ECEL_OMITREF optimization, or due to the +- ** SortCtx.pDeferredRowLoad optimiation. In any of these cases ++ ** SortCtx.pDeferredRowLoad optimization. In any of these cases + ** regOrigData is 0 to prevent this routine from trying to copy + ** values that might not yet exist. + */ + assert( nData==1 || regData==regOrigData || regOrigData==0 ); + ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ pSort->addrPush = sqlite3VdbeCurrentAddr(v); ++#endif ++ + if( nPrefixReg ){ + assert( nPrefixReg==nExpr+bSeq ); + regBase = regData - nPrefixReg; +@@ -129962,7 +145651,7 @@ static void pushOntoSorter( + testcase( pKI->nAllField > pKI->nKeyField+2 ); + pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat, + pKI->nAllField-pKI->nKeyField-1); +- pOp = 0; /* Ensure pOp not used after sqltie3VdbeAddOp3() */ ++ pOp = 0; /* Ensure pOp not used after sqlite3VdbeAddOp3() */ + addrJmp = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); + pSort->labelBkOut = sqlite3VdbeMakeLabel(pParse); +@@ -130016,6 +145705,9 @@ static void pushOntoSorter( + sqlite3VdbeChangeP2(v, iSkip, + pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v)); + } ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ pSort->addrPushEnd = sqlite3VdbeCurrentAddr(v)-1; ++#endif + } + + /* +@@ -130033,31 +145725,157 @@ static void codeOffset( + } + + /* +-** Add code that will check to make sure the N registers starting at iMem +-** form a distinct entry. iTab is a sorting index that holds previously +-** seen combinations of the N values. A new entry is made in iTab +-** if the current N values are new. ++** Add code that will check to make sure the array of registers starting at ++** iMem form a distinct entry. This is used by both "SELECT DISTINCT ..." and ++** distinct aggregates ("SELECT count(DISTINCT ) ..."). Three strategies ++** are available. Which is used depends on the value of parameter eTnctType, ++** as follows: + ** +-** A jump to addrRepeat is made and the N+1 values are popped from the +-** stack if the top N elements are not distinct. +-*/ +-static void codeDistinct( ++** WHERE_DISTINCT_UNORDERED/WHERE_DISTINCT_NOOP: ++** Build an ephemeral table that contains all entries seen before and ++** skip entries which have been seen before. ++** ++** Parameter iTab is the cursor number of an ephemeral table that must ++** be opened before the VM code generated by this routine is executed. ++** The ephemeral cursor table is queried for a record identical to the ++** record formed by the current array of registers. If one is found, ++** jump to VM address addrRepeat. Otherwise, insert a new record into ++** the ephemeral cursor and proceed. ++** ++** The returned value in this case is a copy of parameter iTab. ++** ++** WHERE_DISTINCT_ORDERED: ++** In this case rows are being delivered sorted order. The ephemeral ++** table is not required. Instead, the current set of values ++** is compared against previous row. If they match, the new row ++** is not distinct and control jumps to VM address addrRepeat. Otherwise, ++** the VM program proceeds with processing the new row. ++** ++** The returned value in this case is the register number of the first ++** in an array of registers used to store the previous result row so that ++** it can be compared to the next. The caller must ensure that this ++** register is initialized to NULL. (The fixDistinctOpenEph() routine ++** will take care of this initialization.) ++** ++** WHERE_DISTINCT_UNIQUE: ++** In this case it has already been determined that the rows are distinct. ++** No special action is required. The return value is zero. ++** ++** Parameter pEList is the list of expressions used to generated the ++** contents of each row. It is used by this routine to determine (a) ++** how many elements there are in the array of registers and (b) the ++** collation sequences that should be used for the comparisons if ++** eTnctType is WHERE_DISTINCT_ORDERED. ++*/ ++static int codeDistinct( + Parse *pParse, /* Parsing and code generating context */ ++ int eTnctType, /* WHERE_DISTINCT_* value */ + int iTab, /* A sorting index used to test for distinctness */ + int addrRepeat, /* Jump to here if not distinct */ +- int N, /* Number of elements */ +- int iMem /* First element */ ++ ExprList *pEList, /* Expression for each element */ ++ int regElem /* First element */ + ){ +- Vdbe *v; +- int r1; ++ int iRet = 0; ++ int nResultCol = pEList->nExpr; ++ Vdbe *v = pParse->pVdbe; + +- v = pParse->pVdbe; +- r1 = sqlite3GetTempReg(pParse); +- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v); +- sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); +- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, iMem, N); +- sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); +- sqlite3ReleaseTempReg(pParse, r1); ++ switch( eTnctType ){ ++ case WHERE_DISTINCT_ORDERED: { ++ int i; ++ int iJump; /* Jump destination */ ++ int regPrev; /* Previous row content */ ++ ++ /* Allocate space for the previous row */ ++ iRet = regPrev = pParse->nMem+1; ++ pParse->nMem += nResultCol; ++ ++ iJump = sqlite3VdbeCurrentAddr(v) + nResultCol; ++ for(i=0; ia[i].pExpr); ++ if( idb->mallocFailed ); ++ sqlite3VdbeAddOp3(v, OP_Copy, regElem, regPrev, nResultCol-1); ++ break; ++ } ++ ++ case WHERE_DISTINCT_UNIQUE: { ++ /* nothing to do */ ++ break; ++ } ++ ++ default: { ++ int r1 = sqlite3GetTempReg(pParse); ++ sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, regElem, nResultCol); ++ VdbeCoverage(v); ++ sqlite3VdbeAddOp3(v, OP_MakeRecord, regElem, nResultCol, r1); ++ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, regElem, nResultCol); ++ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); ++ sqlite3ReleaseTempReg(pParse, r1); ++ iRet = iTab; ++ break; ++ } ++ } ++ ++ return iRet; ++} ++ ++/* ++** This routine runs after codeDistinct(). It makes necessary ++** adjustments to the OP_OpenEphemeral opcode that the codeDistinct() ++** routine made use of. This processing must be done separately since ++** sometimes codeDistinct is called before the OP_OpenEphemeral is actually ++** laid down. ++** ++** WHERE_DISTINCT_NOOP: ++** WHERE_DISTINCT_UNORDERED: ++** ++** No adjustments necessary. This function is a no-op. ++** ++** WHERE_DISTINCT_UNIQUE: ++** ++** The ephemeral table is not needed. So change the ++** OP_OpenEphemeral opcode into an OP_Noop. ++** ++** WHERE_DISTINCT_ORDERED: ++** ++** The ephemeral table is not needed. But we do need register ++** iVal to be initialized to NULL. So change the OP_OpenEphemeral ++** into an OP_Null on the iVal register. ++*/ ++static void fixDistinctOpenEph( ++ Parse *pParse, /* Parsing and code generating context */ ++ int eTnctType, /* WHERE_DISTINCT_* value */ ++ int iVal, /* Value returned by codeDistinct() */ ++ int iOpenEphAddr /* Address of OP_OpenEphemeral instruction for iTab */ ++){ ++ if( pParse->nErr==0 ++ && (eTnctType==WHERE_DISTINCT_UNIQUE || eTnctType==WHERE_DISTINCT_ORDERED) ++ ){ ++ Vdbe *v = pParse->pVdbe; ++ sqlite3VdbeChangeToNoop(v, iOpenEphAddr); ++ if( sqlite3VdbeGetOp(v, iOpenEphAddr+1)->opcode==OP_Explain ){ ++ sqlite3VdbeChangeToNoop(v, iOpenEphAddr+1); ++ } ++ if( eTnctType==WHERE_DISTINCT_ORDERED ){ ++ /* Change the OP_OpenEphemeral to an OP_Null that sets the MEM_Cleared ++ ** bit on the first register of the previous value. This will cause the ++ ** OP_Ne added in codeDistinct() to always fail on the first iteration of ++ ** the loop even if the first row is all NULLs. */ ++ VdbeOp *pOp = sqlite3VdbeGetOp(v, iOpenEphAddr); ++ pOp->opcode = OP_Null; ++ pOp->p1 = 1; ++ pOp->p2 = iVal; ++ } ++ } + } + + #ifdef SQLITE_ENABLE_SORTER_REFERENCES +@@ -130077,7 +145895,7 @@ static void codeDistinct( + ** retrieved directly from table t1. If the values are very large, this + ** can be more efficient than storing them directly in the sorter records. + ** +-** The ExprList_item.bSorterRef flag is set for each expression in pEList ++** The ExprList_item.fg.bSorterRef flag is set for each expression in pEList + ** for which the sorter-reference optimization should be enabled. + ** Additionally, the pSort->aDefer[] array is populated with entries + ** for all cursors required to evaluate all selected expressions. Finally. +@@ -130098,9 +145916,13 @@ static void selectExprDefer( + struct ExprList_item *pItem = &pEList->a[i]; + if( pItem->u.x.iOrderByCol==0 ){ + Expr *pExpr = pItem->pExpr; +- Table *pTab = pExpr->y.pTab; +- if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab) +- && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF) ++ Table *pTab; ++ if( pExpr->op==TK_COLUMN ++ && pExpr->iColumn>=0 ++ && ALWAYS( ExprUseYTab(pExpr) ) ++ && (pTab = pExpr->y.pTab)!=0 ++ && IsOrdinaryTable(pTab) ++ && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)!=0 + ){ + int j; + for(j=0; jiTable = pExpr->iTable; ++ assert( ExprUseYTab(pNew) ); + pNew->y.pTab = pExpr->y.pTab; + pNew->iColumn = pPk ? pPk->aiColumn[k] : -1; + pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew); +@@ -130132,7 +145955,7 @@ static void selectExprDefer( + nDefer++; + } + } +- pItem->bSorterRef = 1; ++ pItem->fg.bSorterRef = 1; + } + } + } +@@ -130263,7 +146086,7 @@ static void selectInnerLoop( + for(i=0; inExpr; i++){ + if( pEList->a[i].u.x.iOrderByCol>0 + #ifdef SQLITE_ENABLE_SORTER_REFERENCES +- || pEList->a[i].bSorterRef ++ || pEList->a[i].fg.bSorterRef + #endif + ){ + nResultCol--; +@@ -130305,59 +146128,11 @@ static void selectInnerLoop( + ** part of the result. + */ + if( hasDistinct ){ +- switch( pDistinct->eTnctType ){ +- case WHERE_DISTINCT_ORDERED: { +- VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ +- int iJump; /* Jump destination */ +- int regPrev; /* Previous row content */ +- +- /* Allocate space for the previous row */ +- regPrev = pParse->nMem+1; +- pParse->nMem += nResultCol; +- +- /* Change the OP_OpenEphemeral coded earlier to an OP_Null +- ** sets the MEM_Cleared bit on the first register of the +- ** previous value. This will cause the OP_Ne below to always +- ** fail on the first iteration of the loop even if the first +- ** row is all NULLs. +- */ +- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); +- pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct); +- pOp->opcode = OP_Null; +- pOp->p1 = 1; +- pOp->p2 = regPrev; +- pOp = 0; /* Ensure pOp is not used after sqlite3VdbeAddOp() */ +- +- iJump = sqlite3VdbeCurrentAddr(v) + nResultCol; +- for(i=0; ipEList->a[i].pExpr); +- if( idb->mallocFailed ); +- sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1); +- break; +- } +- +- case WHERE_DISTINCT_UNIQUE: { +- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); +- break; +- } +- +- default: { +- assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED ); +- codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, +- regResult); +- break; +- } +- } ++ int eType = pDistinct->eTnctType; ++ int iTab = pDistinct->tabTnct; ++ assert( nResultCol==p->pEList->nExpr ); ++ iTab = codeDistinct(pParse, eType, iTab, iContinue, p->pEList, regResult); ++ fixDistinctOpenEph(pParse, eType, iTab, pDistinct->addrTnct); + if( pSort==0 ){ + codeOffset(v, p->iOffset, iContinue); + } +@@ -130399,6 +146174,16 @@ static void selectInnerLoop( + testcase( eDest==SRT_Fifo ); + testcase( eDest==SRT_DistFifo ); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg); ++#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG) ++ /* A destination of SRT_Table and a non-zero iSDParm2 parameter means ++ ** that this is an "UPDATE ... FROM" on a virtual table or view. In this ++ ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC. ++ ** This does not affect operation in any way - it just allows MakeRecord ++ ** to process OPFLAG_NOCHANGE values without an assert() failing. */ ++ if( eDest==SRT_Table && pDest->iSDParm2 ){ ++ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); ++ } ++#endif + #ifndef SQLITE_OMIT_CTE + if( eDest==SRT_DistFifo ){ + /* If the destination is DistFifo, then cursor (iParm+1) is open +@@ -130464,12 +146249,18 @@ static void selectInnerLoop( + ** case the order does matter */ + pushOntoSorter( + pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); ++ pDest->iSDParm2 = 0; /* Signal that any Bloom filter is unpopulated */ + }else{ + int r1 = sqlite3GetTempReg(pParse); + assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol ); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol, + r1, pDest->zAffSdst, nResultCol); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); ++ if( pDest->iSDParm2 ){ ++ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pDest->iSDParm2, 0, ++ regResult, nResultCol); ++ ExplainQueryPlan((pParse, 0, "CREATE BLOOM FILTER")); ++ } + sqlite3ReleaseTempReg(pParse, r1); + } + break; +@@ -130593,8 +146384,8 @@ static void selectInnerLoop( + ** X extra columns. + */ + SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ +- int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*); +- KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra); ++ int nExtra = (N+X)*(sizeof(CollSeq*)+1); ++ KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra); + if( p ){ + p->aSortFlags = (u8*)&p->aColl[N+X]; + p->nKeyField = (u16)N; +@@ -130602,9 +146393,9 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ + p->enc = ENC(db); + p->db = db; + p->nRef = 1; +- memset(&p[1], 0, nExtra); ++ memset(p->aColl, 0, nExtra); + }else{ +- sqlite3OomFault(db); ++ return (KeyInfo*)sqlite3OomFault(db); + } + return p; + } +@@ -130614,9 +146405,10 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ + */ + SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){ + if( p ){ ++ assert( p->db!=0 ); + assert( p->nRef>0 ); + p->nRef--; +- if( p->nRef==0 ) sqlite3DbFreeNN(p->db, p); ++ if( p->nRef==0 ) sqlite3DbNNFreeNN(p->db, p); + } + } + +@@ -130673,7 +146465,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList( + assert( sqlite3KeyInfoIsWriteable(pInfo) ); + for(i=iStart, pItem=pList->a+iStart; iaColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr); +- pInfo->aSortFlags[i-iStart] = pItem->sortFlags; ++ pInfo->aSortFlags[i-iStart] = pItem->fg.sortFlags; + } + } + return pInfo; +@@ -130682,7 +146474,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList( + /* + ** Name of the connection operator, used for error messages. + */ +-static const char *selectOpName(int id){ ++SQLITE_PRIVATE const char *sqlite3SelectOpName(int id){ + char *z; + switch( id ){ + case TK_ALL: z = "UNION ALL"; break; +@@ -130755,6 +146547,23 @@ static void generateSortTail( + int bSeq; /* True if sorter record includes seq. no. */ + int nRefKey = 0; + struct ExprList_item *aOutEx = p->pEList->a; ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ int addrExplain; /* Address of OP_Explain instruction */ ++#endif ++ ++ nKey = pOrderBy->nExpr - pSort->nOBSat; ++ if( pSort->nOBSat==0 || nKey==1 ){ ++ ExplainQueryPlan2(addrExplain, (pParse, 0, ++ "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat?"LAST TERM OF ":"" ++ )); ++ }else{ ++ ExplainQueryPlan2(addrExplain, (pParse, 0, ++ "USE TEMP B-TREE FOR LAST %d TERMS OF ORDER BY", nKey ++ )); ++ } ++ sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd); ++ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush); ++ + + assert( addrBreak<0 ); + if( pSort->labelBkOut ){ +@@ -130775,6 +146584,9 @@ static void generateSortTail( + + iTab = pSort->iECursor; + if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){ ++ if( eDest==SRT_Mem && p->iOffset ){ ++ sqlite3VdbeAddOp2(v, OP_Null, 0, pDest->iSdst); ++ } + regRowid = 0; + regRow = pDest->iSdst; + }else{ +@@ -130786,7 +146598,6 @@ static void generateSortTail( + regRow = sqlite3GetTempRange(pParse, nColumn); + } + } +- nKey = pOrderBy->nExpr - pSort->nOBSat; + if( pSort->sortFlags & SORTFLAG_UseSorter ){ + int regSortOut = ++pParse->nMem; + iSortTab = pParse->nTab++; +@@ -130798,7 +146609,7 @@ static void generateSortTail( + if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce); + addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); + VdbeCoverage(v); +- codeOffset(v, p->iOffset, addrContinue); ++ assert( p->iLimit==0 && p->iOffset==0 ); + sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab); + bSeq = 0; + }else{ +@@ -130806,10 +146617,13 @@ static void generateSortTail( + codeOffset(v, p->iOffset, addrContinue); + iSortTab = iTab; + bSeq = 1; ++ if( p->iOffset>0 ){ ++ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); ++ } + } + for(i=0, iCol=nKey+bSeq-1; i=0; i--){ + #ifdef SQLITE_ENABLE_SORTER_REFERENCES +- if( aOutEx[i].bSorterRef ){ ++ if( aOutEx[i].fg.bSorterRef ){ + sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i); + }else + #endif +@@ -130861,6 +146675,7 @@ static void generateSortTail( + VdbeComment((v, "%s", aOutEx[i].zEName)); + } + } ++ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); + switch( eDest ){ + case SRT_Table: + case SRT_EphemTab: { +@@ -130922,6 +146737,7 @@ static void generateSortTail( + }else{ + sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v); + } ++ sqlite3VdbeScanStatusRange(v, addrExplain, sqlite3VdbeCurrentAddr(v)-1, -1); + if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn); + sqlite3VdbeResolveLabel(v, addrBreak); + } +@@ -130930,9 +146746,6 @@ static void generateSortTail( + ** Return a pointer to a string containing the 'declaration type' of the + ** expression pExpr. The string may be treated as static by the caller. + ** +-** Also try to estimate the size of the returned value and return that +-** result in *pEstWidth. +-** + ** The declaration type is the exact datatype definition extracted from the + ** original CREATE TABLE statement if the expression is a column. The + ** declaration type for a ROWID field is INTEGER. Exactly when an expression +@@ -130989,8 +146802,12 @@ static const char *columnTypeImpl( + SrcList *pTabList = pNC->pSrcList; + for(j=0;jnSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++); + if( jnSrc ){ +- pTab = pTabList->a[j].pTab; +- pS = pTabList->a[j].pSelect; ++ pTab = pTabList->a[j].pSTab; ++ if( pTabList->a[j].fg.isSubquery ){ ++ pS = pTabList->a[j].u4.pSubq->pSelect; ++ }else{ ++ pS = 0; ++ } + }else{ + pNC = pNC->pNext; + } +@@ -131017,13 +146834,15 @@ static const char *columnTypeImpl( + break; + } + +- assert( pTab && pExpr->y.pTab==pTab ); ++ assert( pTab && ExprUseYTab(pExpr) && pExpr->y.pTab==pTab ); + if( pS ){ + /* The "table" is actually a sub-select or a view in the FROM clause + ** of the SELECT statement. Return the declaration type and origin + ** data for the result-set column of the sub-select. + */ +- if( iCol>=0 && iColpEList->nExpr ){ ++ if( iColpEList->nExpr ++ && (!ViewCanHaveRowid || iCol>=0) ++ ){ + /* If iCol is less than zero, then the expression requests the + ** rowid of the sub-select or view. This expression is legal (see + ** test case misc2.2.2) - it always evaluates to NULL. +@@ -131045,7 +146864,7 @@ static const char *columnTypeImpl( + zType = "INTEGER"; + zOrigCol = "rowid"; + }else{ +- zOrigCol = pTab->aCol[iCol].zName; ++ zOrigCol = pTab->aCol[iCol].zCnName; + zType = sqlite3ColumnType(&pTab->aCol[iCol],0); + } + zOrigTab = pTab->zName; +@@ -131071,9 +146890,11 @@ static const char *columnTypeImpl( + ** statement. + */ + NameContext sNC; +- Select *pS = pExpr->x.pSelect; +- Expr *p = pS->pEList->a[0].pExpr; +- assert( ExprHasProperty(pExpr, EP_xIsSelect) ); ++ Select *pS; ++ Expr *p; ++ assert( ExprUseXSelect(pExpr) ); ++ pS = pExpr->x.pSelect; ++ p = pS->pEList->a[0].pExpr; + sNC.pSrcList = pS->pSrc; + sNC.pNext = pNC; + sNC.pParse = pNC->pParse; +@@ -131165,7 +146986,7 @@ static void generateColumnTypes( + ** then the result column name with the table name + ** prefix, ex: TABLE.COLUMN. Otherwise use zSpan. + */ +-static void generateColumnNames( ++SQLITE_PRIVATE void sqlite3GenerateColumnNames( + Parse *pParse, /* Parser context */ + Select *pSelect /* Generate column names for this SELECT statement */ + ){ +@@ -131178,17 +146999,10 @@ static void generateColumnNames( + int fullName; /* TABLE.COLUMN if no AS clause and is a direct table ref */ + int srcName; /* COLUMN or TABLE.COLUMN if no AS clause and is direct */ + +-#ifndef SQLITE_OMIT_EXPLAIN +- /* If this is an EXPLAIN, skip this step */ +- if( pParse->explain ){ +- return; +- } +-#endif +- + if( pParse->colNamesSet ) return; + /* Column names are determined by the left-most term of a compound select */ + while( pSelect->pPrior ) pSelect = pSelect->pPrior; +- SELECTTRACE(1,pParse,pSelect,("generating column names\n")); ++ TREETRACE(0x80,pParse,pSelect,("generating column names\n")); + pTabList = pSelect->pSrc; + pEList = pSelect->pEList; + assert( v!=0 ); +@@ -131202,8 +147016,9 @@ static void generateColumnNames( + + assert( p!=0 ); + assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */ +- assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */ +- if( pEList->a[i].zEName && pEList->a[i].eEName==ENAME_NAME ){ ++ assert( p->op!=TK_COLUMN ++ || (ExprUseYTab(p) && p->y.pTab!=0) ); /* Covering idx not yet coded */ ++ if( pEList->a[i].zEName && pEList->a[i].fg.eEName==ENAME_NAME ){ + /* An AS clause always takes first priority */ + char *zName = pEList->a[i].zEName; + sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); +@@ -131217,7 +147032,7 @@ static void generateColumnNames( + if( iCol<0 ){ + zCol = "rowid"; + }else{ +- zCol = pTab->aCol[iCol].zName; ++ zCol = pTab->aCol[iCol].zCnName; + } + if( fullName ){ + char *zName = 0; +@@ -131255,7 +147070,7 @@ static void generateColumnNames( + ** and will break if those assumptions changes. Hence, use extreme caution + ** when modifying this routine to avoid breaking legacy. + ** +-** See Also: generateColumnNames() ++** See Also: sqlite3GenerateColumnNames() + */ + SQLITE_PRIVATE int sqlite3ColumnsFromExprList( + Parse *pParse, /* Parsing context */ +@@ -131271,13 +147086,14 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( + char *zName; /* Column name */ + int nName; /* Size of name in zName[] */ + Hash ht; /* Hash table of column names */ ++ Table *pTab; + + sqlite3HashInit(&ht); + if( pEList ){ + nCol = pEList->nExpr; + aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); + testcase( aCol==0 ); +- if( nCol>32767 ) nCol = 32767; ++ if( NEVER(nCol>32767) ) nCol = 32767; + }else{ + nCol = 0; + aCol = 0; +@@ -131286,30 +147102,34 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( + *pnCol = nCol; + *paCol = aCol; + +- for(i=0, pCol=aCol; imallocFailed; i++, pCol++){ ++ for(i=0, pCol=aCol; inErr; i++, pCol++){ ++ struct ExprList_item *pX = &pEList->a[i]; ++ struct ExprList_item *pCollide; + /* Get an appropriate name for the column + */ +- if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ ++ if( (zName = pX->zEName)!=0 && pX->fg.eEName==ENAME_NAME ){ + /* If the column contains an "AS " phrase, use as the name */ + }else{ +- Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); +- while( pColExpr->op==TK_DOT ){ ++ Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pX->pExpr); ++ while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ + pColExpr = pColExpr->pRight; + assert( pColExpr!=0 ); + } +- if( pColExpr->op==TK_COLUMN ){ ++ if( pColExpr->op==TK_COLUMN ++ && ALWAYS( ExprUseYTab(pColExpr) ) ++ && ALWAYS( pColExpr->y.pTab!=0 ) ++ ){ + /* For columns use the column name name */ + int iCol = pColExpr->iColumn; +- Table *pTab = pColExpr->y.pTab; +- assert( pTab!=0 ); ++ pTab = pColExpr->y.pTab; + if( iCol<0 ) iCol = pTab->iPKey; +- zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; ++ zName = iCol>=0 ? pTab->aCol[iCol].zCnName : "rowid"; + }else if( pColExpr->op==TK_ID ){ + assert( !ExprHasProperty(pColExpr, EP_IntValue) ); + zName = pColExpr->u.zToken; + }else{ + /* Use the original text of the column expression as its name */ +- zName = pEList->a[i].zEName; ++ assert( zName==pX->zEName ); /* pointer comparison intended */ + } + } + if( zName && !sqlite3IsTrueOrFalse(zName) ){ +@@ -131322,87 +147142,139 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( + ** append an integer to the name so that it becomes unique. + */ + cnt = 0; +- while( zName && sqlite3HashFind(&ht, zName)!=0 ){ ++ while( zName && (pCollide = sqlite3HashFind(&ht, zName))!=0 ){ ++ if( pCollide->fg.bUsingTerm ){ ++ pCol->colFlags |= COLFLAG_NOEXPAND; ++ } + nName = sqlite3Strlen30(zName); + if( nName>0 ){ + for(j=nName-1; j>0 && sqlite3Isdigit(zName[j]); j--){} + if( zName[j]==':' ) nName = j; + } + zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); +- if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt); ++ sqlite3ProgressCheck(pParse); ++ if( cnt>3 ){ ++ sqlite3_randomness(sizeof(cnt), &cnt); ++ } + } +- pCol->zName = zName; ++ pCol->zCnName = zName; + pCol->hName = sqlite3StrIHash(zName); ++ if( pX->fg.bNoExpand ){ ++ pCol->colFlags |= COLFLAG_NOEXPAND; ++ } + sqlite3ColumnPropertiesFromName(0, pCol); +- if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){ ++ if( zName && sqlite3HashInsert(&ht, zName, pX)==pX ){ + sqlite3OomFault(db); + } + } + sqlite3HashClear(&ht); +- if( db->mallocFailed ){ ++ if( pParse->nErr ){ + for(j=0; jrc; + } + return SQLITE_OK; + } + + /* +-** Add type and collation information to a column list based on +-** a SELECT statement. +-** +-** The column list presumably came from selectColumnNamesFromExprList(). +-** The column list has only names, not types or collations. This +-** routine goes through and adds the types and collations. ++** pTab is a transient Table object that represents a subquery of some ++** kind (maybe a parenthesized subquery in the FROM clause of a larger ++** query, or a VIEW, or a CTE). This routine computes type information ++** for that Table object based on the Select object that implements the ++** subquery. For the purposes of this routine, "type information" means: + ** +-** This routine requires that all identifiers in the SELECT +-** statement be resolved. ++** * The datatype name, as it might appear in a CREATE TABLE statement ++** * Which collating sequence to use for the column ++** * The affinity of the column + */ +-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation( +- Parse *pParse, /* Parsing contexts */ +- Table *pTab, /* Add column type information to this table */ +- Select *pSelect, /* SELECT used to determine types and collations */ +- char aff /* Default affinity for columns */ ++SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( ++ Parse *pParse, /* Parsing contexts */ ++ Table *pTab, /* Add column type information to this table */ ++ Select *pSelect, /* SELECT used to determine types and collations */ ++ char aff /* Default affinity. */ + ){ + sqlite3 *db = pParse->db; +- NameContext sNC; + Column *pCol; + CollSeq *pColl; +- int i; ++ int i,j; + Expr *p; + struct ExprList_item *a; ++ NameContext sNC; + + assert( pSelect!=0 ); + assert( (pSelect->selFlags & SF_Resolved)!=0 ); +- assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed ); +- if( db->mallocFailed ) return; ++ assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); ++ assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); ++ if( db->mallocFailed || IN_RENAME_OBJECT ) return; ++ while( pSelect->pPrior ) pSelect = pSelect->pPrior; ++ a = pSelect->pEList->a; + memset(&sNC, 0, sizeof(sNC)); + sNC.pSrcList = pSelect->pSrc; +- a = pSelect->pEList->a; + for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ + const char *zType; +- int n, m; ++ i64 n; ++ int m = 0; ++ Select *pS2 = pSelect; ++ pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); + p = a[i].pExpr; +- zType = columnType(&sNC, p, 0, 0, 0); + /* pCol->szEst = ... // Column size est for SELECT tables never used */ + pCol->affinity = sqlite3ExprAffinity(p); ++ while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){ ++ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); ++ pS2 = pS2->pNext; ++ pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr); ++ } ++ if( pCol->affinity<=SQLITE_AFF_NONE ){ ++ pCol->affinity = aff; ++ } ++ if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){ ++ for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){ ++ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); ++ } ++ if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ ++ pCol->affinity = SQLITE_AFF_BLOB; ++ }else ++ if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){ ++ pCol->affinity = SQLITE_AFF_BLOB; ++ } ++ if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){ ++ pCol->affinity = SQLITE_AFF_FLEXNUM; ++ } ++ } ++ zType = columnType(&sNC, p, 0, 0, 0); ++ if( zType==0 || pCol->affinity!=sqlite3AffinityType(zType, 0) ){ ++ if( pCol->affinity==SQLITE_AFF_NUMERIC ++ || pCol->affinity==SQLITE_AFF_FLEXNUM ++ ){ ++ zType = "NUM"; ++ }else{ ++ zType = 0; ++ for(j=1; jaffinity ){ ++ zType = sqlite3StdType[j]; ++ break; ++ } ++ } ++ } ++ } + if( zType ){ +- m = sqlite3Strlen30(zType); +- n = sqlite3Strlen30(pCol->zName); +- pCol->zName = sqlite3DbReallocOrFree(db, pCol->zName, n+m+2); +- if( pCol->zName ){ +- memcpy(&pCol->zName[n+1], zType, m+1); ++ const i64 k = sqlite3Strlen30(zType); ++ n = sqlite3Strlen30(pCol->zCnName); ++ pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2); ++ pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); ++ if( pCol->zCnName ){ ++ memcpy(&pCol->zCnName[n+1], zType, k+1); + pCol->colFlags |= COLFLAG_HASTYPE; + } + } +- if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff; + pColl = sqlite3ExprCollSeq(pParse, p); +- if( pColl && pCol->zColl==0 ){ +- pCol->zColl = sqlite3DbStrDup(db, pColl->zName); ++ if( pColl ){ ++ assert( pTab->pIndex==0 ); ++ sqlite3ColumnSetColl(db, pCol, pColl->zName); + } + } + pTab->szTabRow = 1; /* Any non-zero value works */ +@@ -131432,7 +147304,7 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, c + pTab->zName = 0; + pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); + sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); +- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff); ++ sqlite3SubqueryColumnTypes(pParse, pTab, pSelect, aff); + pTab->iPKey = -1; + if( db->mallocFailed ){ + sqlite3DeleteTable(db, pTab); +@@ -131502,7 +147374,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ + p->iLimit = iLimit = ++pParse->nMem; + v = sqlite3GetVdbe(pParse); + assert( v!=0 ); +- if( sqlite3ExprIsInteger(pLimit->pLeft, &n) ){ ++ if( sqlite3ExprIsInteger(pLimit->pLeft, &n, pParse) ){ + sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); + VdbeComment((v, "LIMIT counter")); + if( n==0 ){ +@@ -131566,7 +147438,7 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ + */ + static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ + ExprList *pOrderBy = p->pOrderBy; +- int nOrderBy = p->pOrderBy->nExpr; ++ int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0; + sqlite3 *db = pParse->db; + KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); + if( pRet ){ +@@ -131586,7 +147458,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ + } + assert( sqlite3KeyInfoIsWriteable(pRet) ); + pRet->aColl[i] = pColl; +- pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags; ++ pRet->aSortFlags[i] = pOrderBy->a[i].fg.sortFlags; + } + } + +@@ -131638,7 +147510,8 @@ static void generateWithRecursiveQuery( + SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */ + int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */ + Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ +- Select *pSetup = p->pPrior; /* The setup query */ ++ Select *pSetup; /* The setup query */ ++ Select *pFirstRec; /* Left-most recursive term */ + int addrTop; /* Top of the loop */ + int addrCont, addrBreak; /* CONTINUE and BREAK addresses */ + int iCurrent = 0; /* The Current table */ +@@ -131646,7 +147519,7 @@ static void generateWithRecursiveQuery( + int iQueue; /* The Queue table */ + int iDistinct = 0; /* To ensure unique results if UNION */ + int eDest = SRT_Fifo; /* How to write to Queue */ +- SelectDest destQueue; /* SelectDest targetting the Queue table */ ++ SelectDest destQueue; /* SelectDest targeting the Queue table */ + int i; /* Loop counter */ + int rc; /* Result code */ + ExprList *pOrderBy; /* The ORDER BY clause */ +@@ -131714,7 +147587,24 @@ static void generateWithRecursiveQuery( + /* Detach the ORDER BY clause from the compound SELECT */ + p->pOrderBy = 0; + ++ /* Figure out how many elements of the compound SELECT are part of the ++ ** recursive query. Make sure no recursive elements use aggregate ++ ** functions. Mark the recursive elements as UNION ALL even if they ++ ** are really UNION because the distinctness will be enforced by the ++ ** iDistinct table. pFirstRec is left pointing to the left-most ++ ** recursive term of the CTE. ++ */ ++ for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){ ++ if( pFirstRec->selFlags & SF_Aggregate ){ ++ sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); ++ goto end_of_recursive_query; ++ } ++ pFirstRec->op = TK_ALL; ++ if( (pFirstRec->pPrior->selFlags & SF_Recursive)==0 ) break; ++ } ++ + /* Store the results of the setup-query in Queue. */ ++ pSetup = pFirstRec->pPrior; + pSetup->pNext = 0; + ExplainQueryPlan((pParse, 1, "SETUP")); + rc = sqlite3Select(pParse, pSetup, &destQueue); +@@ -131747,15 +147637,11 @@ static void generateWithRecursiveQuery( + /* Execute the recursive SELECT taking the single row in Current as + ** the value for the recursive-table. Store the results in the Queue. + */ +- if( p->selFlags & SF_Aggregate ){ +- sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); +- }else{ +- p->pPrior = 0; +- ExplainQueryPlan((pParse, 1, "RECURSIVE STEP")); +- sqlite3Select(pParse, p, &destQueue); +- assert( p->pPrior==0 ); +- p->pPrior = pSetup; +- } ++ pFirstRec->pPrior = 0; ++ ExplainQueryPlan((pParse, 1, "RECURSIVE STEP")); ++ sqlite3Select(pParse, p, &destQueue); ++ assert( pFirstRec->pPrior==0 ); ++ pFirstRec->pPrior = pSetup; + + /* Keep running the loop until the Queue is empty */ + sqlite3VdbeGoto(v, addrTop); +@@ -131790,7 +147676,7 @@ static int multiSelectOrderBy( + ** The "LIMIT of exactly 1" case of condition (1) comes about when a VALUES + ** clause occurs within scalar expression (ex: "SELECT (VALUES(1),(2),(3))"). + ** The sqlite3CodeSubselect will have added the LIMIT 1 clause in tht case. +-** Since the limit is exactly 1, we only need to evalutes the left-most VALUES. ++** Since the limit is exactly 1, we only need to evaluate the left-most VALUES. + */ + static int multiSelectValues( + Parse *pParse, /* Parsing context */ +@@ -131824,6 +147710,16 @@ static int multiSelectValues( + return rc; + } + ++/* ++** Return true if the SELECT statement which is known to be the recursive ++** part of a recursive CTE still has its anchor terms attached. If the ++** anchor terms have already been removed, then return false. ++*/ ++static int hasAnchor(Select *p){ ++ while( p && (p->selFlags & SF_Recursive)!=0 ){ p = p->pPrior; } ++ return p!=0; ++} ++ + /* + ** This routine is called to process a compound query form from + ** two or more separate queries using UNION, UNION ALL, EXCEPT, or +@@ -131876,12 +147772,8 @@ static int multiSelect( + db = pParse->db; + pPrior = p->pPrior; + dest = *pDest; +- if( pPrior->pOrderBy || pPrior->pLimit ){ +- sqlite3ErrorMsg(pParse,"%s clause should come after %s not before", +- pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op)); +- rc = 1; +- goto multi_select_end; +- } ++ assert( pPrior->pOrderBy==0 ); ++ assert( pPrior->pLimit==0 ); + + v = sqlite3GetVdbe(pParse); + assert( v!=0 ); /* The VDBE already created by calling function */ +@@ -131909,7 +147801,7 @@ static int multiSelect( + assert( p->pEList->nExpr==pPrior->pEList->nExpr ); + + #ifndef SQLITE_OMIT_CTE +- if( p->selFlags & SF_Recursive ){ ++ if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){ + generateWithRecursiveQuery(pParse, p, &dest); + }else + #endif +@@ -131932,13 +147824,14 @@ static int multiSelect( + switch( p->op ){ + case TK_ALL: { + int addr = 0; +- int nLimit; ++ int nLimit = 0; /* Initialize to suppress harmless compiler warning */ + assert( !pPrior->pLimit ); + pPrior->iLimit = p->iLimit; + pPrior->iOffset = p->iOffset; + pPrior->pLimit = p->pLimit; ++ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); + rc = sqlite3Select(pParse, pPrior, &dest); +- p->pLimit = 0; ++ pPrior->pLimit = 0; + if( rc ){ + goto multi_select_end; + } +@@ -131954,13 +147847,14 @@ static int multiSelect( + } + } + ExplainQueryPlan((pParse, 1, "UNION ALL")); ++ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); + rc = sqlite3Select(pParse, p, &dest); + testcase( rc!=SQLITE_OK ); + pDelete = p->pPrior; + p->pPrior = pPrior; + p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); +- if( pPrior->pLimit +- && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit) ++ if( p->pLimit ++ && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) + && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) + ){ + p->nSelectRow = sqlite3LogEst((u64)nLimit); +@@ -132001,10 +147895,12 @@ static int multiSelect( + assert( p->pEList ); + } + ++ + /* Code the SELECT statements to our left + */ + assert( !pPrior->pOrderBy ); + sqlite3SelectDestInit(&uniondest, priorOp, unionTab); ++ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); + rc = sqlite3Select(pParse, pPrior, &uniondest); + if( rc ){ + goto multi_select_end; +@@ -132023,7 +147919,8 @@ static int multiSelect( + p->pLimit = 0; + uniondest.eDest = op; + ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", +- selectOpName(p->op))); ++ sqlite3SelectOpName(p->op))); ++ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); + rc = sqlite3Select(pParse, p, &uniondest); + testcase( rc!=SQLITE_OK ); + assert( p->pOrderBy==0 ); +@@ -132084,6 +147981,7 @@ static int multiSelect( + /* Code the SELECTs to our left into temporary table "tab1". + */ + sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); ++ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n")); + rc = sqlite3Select(pParse, pPrior, &intersectdest); + if( rc ){ + goto multi_select_end; +@@ -132099,7 +147997,8 @@ static int multiSelect( + p->pLimit = 0; + intersectdest.iSDParm = tab2; + ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", +- selectOpName(p->op))); ++ sqlite3SelectOpName(p->op))); ++ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n")); + rc = sqlite3Select(pParse, p, &intersectdest); + testcase( rc!=SQLITE_OK ); + pDelete = p->pPrior; +@@ -132160,6 +148059,7 @@ static int multiSelect( + int nCol; /* Number of columns in result set */ + + assert( p->pNext==0 ); ++ assert( p->pEList!=0 ); + nCol = p->pEList->nExpr; + pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); + if( !pKeyInfo ){ +@@ -132194,7 +148094,10 @@ static int multiSelect( + multi_select_end: + pDest->iSdst = dest.iSdst; + pDest->nSdst = dest.nSdst; +- sqlite3SelectDelete(db, pDelete); ++ pDest->iSDParm2 = dest.iSDParm2; ++ if( pDelete ){ ++ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete); ++ } + return rc; + } + #endif /* SQLITE_OMIT_COMPOUND_SELECT */ +@@ -132208,13 +148111,14 @@ SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){ + sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); + }else{ + sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" +- " do not have the same number of result columns", selectOpName(p->op)); ++ " do not have the same number of result columns", ++ sqlite3SelectOpName(p->op)); + } + } + + /* + ** Code an output subroutine for a coroutine implementation of a +-** SELECT statment. ++** SELECT statement. + ** + ** The data to be output is contained in pIn->iSdst. There are + ** pIn->nSdst columns to be output. pDest is where the output should +@@ -132295,6 +148199,11 @@ static int generateOutputSubroutine( + r1, pDest->zAffSdst, pIn->nSdst); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pDest->iSDParm, r1, + pIn->iSdst, pIn->nSdst); ++ if( pDest->iSDParm2>0 ){ ++ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pDest->iSDParm2, 0, ++ pIn->iSdst, pIn->nSdst); ++ ExplainQueryPlan((pParse, 0, "CREATE BLOOM FILTER")); ++ } + sqlite3ReleaseTempReg(pParse, r1); + break; + } +@@ -132305,10 +148214,8 @@ static int generateOutputSubroutine( + ** if it is the RHS of a row-value IN operator. + */ + case SRT_Mem: { +- if( pParse->nErr==0 ){ +- testcase( pIn->nSdst>1 ); +- sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst); +- } ++ testcase( pIn->nSdst>1 ); ++ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst); + /* The LIMIT clause will jump out of the loop for us */ + break; + } +@@ -132438,7 +148345,7 @@ static int generateOutputSubroutine( + ** + ** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not + ** actually called using Gosub and they do not Return. EofA and EofB loop +-** until all data is exhausted then jump to the "end" labe. AltB, AeqB, ++** until all data is exhausted then jump to the "end" label. AltB, AeqB, + ** and AgtB jump to either L2 or to one of EofA or EofB. + */ + #ifndef SQLITE_OMIT_COMPOUND_SELECT +@@ -132449,6 +148356,8 @@ static int multiSelectOrderBy( + ){ + int i, j; /* Loop counters */ + Select *pPrior; /* Another SELECT immediately to our left */ ++ Select *pSplit; /* Left-most SELECT in the right-hand group */ ++ int nSelect; /* Number of SELECT statements in the compound */ + Vdbe *v; /* Generate code to this VDBE */ + SelectDest destA; /* Destination for coroutine A */ + SelectDest destB; /* Destination for coroutine B */ +@@ -132473,7 +148382,7 @@ static int multiSelectOrderBy( + int savedOffset; /* Saved value of p->iOffset */ + int labelCmpr; /* Label for the start of the merge algorithm */ + int labelEnd; /* Label for the end of the overall SELECT stmt */ +- int addr1; /* Jump instructions that get retargetted */ ++ int addr1; /* Jump instructions that get retargeted */ + int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ + KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ + KeyInfo *pKeyMerge; /* Comparison information for merging rows */ +@@ -132494,8 +148403,7 @@ static int multiSelectOrderBy( + /* Patch up the ORDER BY clause + */ + op = p->op; +- pPrior = p->pPrior; +- assert( pPrior->pOrderBy==0 ); ++ assert( p->pPrior->pOrderBy==0 ); + pOrderBy = p->pOrderBy; + assert( pOrderBy ); + nOrderBy = pOrderBy->nExpr; +@@ -132508,6 +148416,7 @@ static int multiSelectOrderBy( + for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ + struct ExprList_item *pItem; + for(j=0, pItem=pOrderBy->a; ju.x.iOrderByCol>0 ); + if( pItem->u.x.iOrderByCol==i ) break; + } +@@ -132534,6 +148443,7 @@ static int multiSelectOrderBy( + struct ExprList_item *pItem; + aPermute[0] = nOrderBy; + for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ ++ assert( pItem!=0 ); + assert( pItem->u.x.iOrderByCol>0 ); + assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); + aPermute[i] = pItem->u.x.iOrderByCol - 1; +@@ -132543,11 +148453,6 @@ static int multiSelectOrderBy( + pKeyMerge = 0; + } + +- /* Reattach the ORDER BY clause to the query. +- */ +- p->pOrderBy = pOrderBy; +- pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0); +- + /* Allocate a range of temporary registers and the KeyInfo needed + ** for the logic that removes duplicate result rows when the + ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). +@@ -132572,12 +148477,30 @@ static int multiSelectOrderBy( + + /* Separate the left and the right query from one another + */ +- p->pPrior = 0; ++ nSelect = 1; ++ if( (op==TK_ALL || op==TK_UNION) ++ && OptimizationEnabled(db, SQLITE_BalancedMerge) ++ ){ ++ for(pSplit=p; pSplit->pPrior!=0 && pSplit->op==op; pSplit=pSplit->pPrior){ ++ nSelect++; ++ assert( pSplit->pPrior->pNext==pSplit ); ++ } ++ } ++ if( nSelect<=3 ){ ++ pSplit = p; ++ }else{ ++ pSplit = p; ++ for(i=2; ipPrior; } ++ } ++ pPrior = pSplit->pPrior; ++ assert( pPrior!=0 ); ++ pSplit->pPrior = 0; + pPrior->pNext = 0; ++ assert( p->pOrderBy == pOrderBy ); ++ assert( pOrderBy!=0 || db->mallocFailed ); ++ pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0); + sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); +- if( pPrior->pPrior==0 ){ +- sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); +- } ++ sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); + + /* Compute the limit registers */ + computeLimitRegisters(pParse, p, labelEnd); +@@ -132600,7 +148523,7 @@ static int multiSelectOrderBy( + sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA); + sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB); + +- ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op))); ++ ExplainQueryPlan((pParse, 1, "MERGE (%s)", sqlite3SelectOpName(p->op))); + + /* Generate a coroutine to evaluate the SELECT statement to the + ** left of the compound operator - the "A" select. +@@ -132726,13 +148649,15 @@ static int multiSelectOrderBy( + */ + sqlite3VdbeResolveLabel(v, labelEnd); + +- /* Reassembly the compound query so that it will be freed correctly +- ** by the calling function */ +- if( p->pPrior ){ +- sqlite3SelectDelete(db, p->pPrior); ++ /* Make arrangements to free the 2nd and subsequent arms of the compound ++ ** after the parse has finished */ ++ if( pSplit->pPrior ){ ++ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior); + } +- p->pPrior = pPrior; +- pPrior->pNext = p; ++ pSplit->pPrior = pPrior; ++ pPrior->pNext = pSplit; ++ sqlite3ExprListDelete(db, pPrior->pOrderBy); ++ pPrior->pOrderBy = 0; + + /*** TBD: Insert subroutine calls to close cursors on incomplete + **** subqueries ****/ +@@ -132748,13 +148673,42 @@ static int multiSelectOrderBy( + ** + ** All references to columns in table iTable are to be replaced by corresponding + ** expressions in pEList. ++** ++** ## About "isOuterJoin": ++** ++** The isOuterJoin column indicates that the replacement will occur into a ++** position in the parent that NULL-able due to an OUTER JOIN. Either the ++** target slot in the parent is the right operand of a LEFT JOIN, or one of ++** the left operands of a RIGHT JOIN. In either case, we need to potentially ++** bypass the substituted expression with OP_IfNullRow. ++** ++** Suppose the original expression is an integer constant. Even though the table ++** has the nullRow flag set, because the expression is an integer constant, ++** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode ++** that checks to see if the nullRow flag is set on the table. If the nullRow ++** flag is set, then the value in the register is set to NULL and the original ++** expression is bypassed. If the nullRow flag is not set, then the original ++** expression runs to populate the register. ++** ++** Example where this is needed: ++** ++** CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT); ++** CREATE TABLE t2(x INT UNIQUE); ++** ++** SELECT a,b,m,x FROM t1 LEFT JOIN (SELECT 59 AS m,x FROM t2) ON b=x; ++** ++** When the subquery on the right side of the LEFT JOIN is flattened, we ++** have to add OP_IfNullRow in front of the OP_Integer that implements the ++** "m" value of the subquery so that a NULL will be loaded instead of 59 ++** when processing a non-matched row of the left. + */ + typedef struct SubstContext { + Parse *pParse; /* The parsing context */ + int iTable; /* Replace references to this table */ + int iNewTable; /* New table number */ +- int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ ++ int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ + ExprList *pEList; /* Replacement expressions */ ++ ExprList *pCList; /* Collation sequences for replacement expr */ + } SubstContext; + + /* Forward Declarations */ +@@ -132779,58 +148733,81 @@ static Expr *substExpr( + Expr *pExpr /* Expr in which substitution occurs */ + ){ + if( pExpr==0 ) return 0; +- if( ExprHasProperty(pExpr, EP_FromJoin) +- && pExpr->iRightJoinTable==pSubst->iTable ++ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ++ && pExpr->w.iJoin==pSubst->iTable + ){ +- pExpr->iRightJoinTable = pSubst->iNewTable; ++ testcase( ExprHasProperty(pExpr, EP_InnerON) ); ++ pExpr->w.iJoin = pSubst->iNewTable; + } + if( pExpr->op==TK_COLUMN + && pExpr->iTable==pSubst->iTable + && !ExprHasProperty(pExpr, EP_FixedCol) + ){ ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + if( pExpr->iColumn<0 ){ + pExpr->op = TK_NULL; +- }else{ ++ }else ++#endif ++ { + Expr *pNew; +- Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; ++ int iColumn; ++ Expr *pCopy; + Expr ifNullRow; +- assert( pSubst->pEList!=0 && pExpr->iColumnpEList->nExpr ); ++ iColumn = pExpr->iColumn; ++ assert( iColumn>=0 ); ++ assert( pSubst->pEList!=0 && iColumnpEList->nExpr ); + assert( pExpr->pRight==0 ); ++ pCopy = pSubst->pEList->a[iColumn].pExpr; + if( sqlite3ExprIsVector(pCopy) ){ + sqlite3VectorErrorMsg(pSubst->pParse, pCopy); + }else{ + sqlite3 *db = pSubst->pParse->db; +- if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ ++ if( pSubst->isOuterJoin ++ && (pCopy->op!=TK_COLUMN || pCopy->iTable!=pSubst->iNewTable) ++ ){ + memset(&ifNullRow, 0, sizeof(ifNullRow)); + ifNullRow.op = TK_IF_NULL_ROW; + ifNullRow.pLeft = pCopy; + ifNullRow.iTable = pSubst->iNewTable; +- ifNullRow.flags = EP_Skip; ++ ifNullRow.iColumn = -99; ++ ifNullRow.flags = EP_IfNullRow; + pCopy = &ifNullRow; + } + testcase( ExprHasProperty(pCopy, EP_Subquery) ); + pNew = sqlite3ExprDup(db, pCopy, 0); +- if( pNew && pSubst->isLeftJoin ){ ++ if( db->mallocFailed ){ ++ sqlite3ExprDelete(db, pNew); ++ return pExpr; ++ } ++ if( pSubst->isOuterJoin ){ + ExprSetProperty(pNew, EP_CanBeNull); + } +- if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){ +- pNew->iRightJoinTable = pExpr->iRightJoinTable; +- ExprSetProperty(pNew, EP_FromJoin); ++ if( pNew->op==TK_TRUEFALSE ){ ++ pNew->u.iValue = sqlite3ExprTruthValue(pNew); ++ pNew->op = TK_INTEGER; ++ ExprSetProperty(pNew, EP_IntValue); + } +- sqlite3ExprDelete(db, pExpr); +- pExpr = pNew; + + /* Ensure that the expression now has an implicit collation sequence, + ** just as it did when it was a column of a view or sub-query. */ +- if( pExpr ){ +- if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){ +- CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr); +- pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr, ++ { ++ CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew); ++ CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, ++ pSubst->pCList->a[iColumn].pExpr ++ ); ++ if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){ ++ pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew, + (pColl ? pColl->zName : "BINARY") + ); + } +- ExprClearProperty(pExpr, EP_Collate); + } ++ ExprClearProperty(pNew, EP_Collate); ++ if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ ++ sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, ++ pExpr->flags & (EP_OuterON|EP_InnerON)); ++ } ++ sqlite3ExprDelete(db, pExpr); ++ pExpr = pNew; + } + } + }else{ +@@ -132839,7 +148816,7 @@ static Expr *substExpr( + } + pExpr->pLeft = substExpr(pSubst, pExpr->pLeft); + pExpr->pRight = substExpr(pSubst, pExpr->pRight); +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + substSelect(pSubst, pExpr->x.pSelect, 1); + }else{ + substExprList(pSubst, pExpr->x.pList); +@@ -132871,7 +148848,7 @@ static void substSelect( + int doPrior /* Do substitutes on p->pPrior too */ + ){ + SrcList *pSrc; +- struct SrcList_item *pItem; ++ SrcItem *pItem; + int i; + if( !p ) return; + do{ +@@ -132883,7 +148860,9 @@ static void substSelect( + pSrc = p->pSrc; + assert( pSrc!=0 ); + for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ +- substSelect(pSubst, pItem->pSelect, 1); ++ if( pItem->fg.isSubquery ){ ++ substSelect(pSubst, pItem->u4.pSubq->pSelect, 1); ++ } + if( pItem->fg.isTabFunc ){ + substExprList(pSubst, pItem->u1.pFuncArg); + } +@@ -132901,7 +148880,7 @@ static void substSelect( + ** pSrcItem->colUsed mask. + */ + static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){ +- struct SrcList_item *pItem; ++ SrcItem *pItem; + if( pExpr->op!=TK_COLUMN ) return WRC_Continue; + pItem = pWalker->u.pSrcItem; + if( pItem->iCursor!=pExpr->iTable ) return WRC_Continue; +@@ -132911,10 +148890,10 @@ static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){ + } + static void recomputeColumnsUsed( + Select *pSelect, /* The complete SELECT statement */ +- struct SrcList_item *pSrcItem /* Which FROM clause item to recompute */ ++ SrcItem *pSrcItem /* Which FROM clause item to recompute */ + ){ + Walker w; +- if( NEVER(pSrcItem->pTab==0) ) return; ++ if( NEVER(pSrcItem->pSTab==0) ) return; + memset(&w, 0, sizeof(w)); + w.xExprCallback = recomputeColumnsUsedExpr; + w.xSelectCallback = sqlite3SelectWalkNoop; +@@ -132924,6 +148903,145 @@ static void recomputeColumnsUsed( + } + #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ + ++#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) ++/* ++** Assign new cursor numbers to each of the items in pSrc. For each ++** new cursor number assigned, set an entry in the aCsrMap[] array ++** to map the old cursor number to the new: ++** ++** aCsrMap[iOld+1] = iNew; ++** ++** The array is guaranteed by the caller to be large enough for all ++** existing cursor numbers in pSrc. aCsrMap[0] is the array size. ++** ++** If pSrc contains any sub-selects, call this routine recursively ++** on the FROM clause of each such sub-select, with iExcept set to -1. ++*/ ++static void srclistRenumberCursors( ++ Parse *pParse, /* Parse context */ ++ int *aCsrMap, /* Array to store cursor mappings in */ ++ SrcList *pSrc, /* FROM clause to renumber */ ++ int iExcept /* FROM clause item to skip */ ++){ ++ int i; ++ SrcItem *pItem; ++ for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){ ++ if( i!=iExcept ){ ++ Select *p; ++ assert( pItem->iCursor < aCsrMap[0] ); ++ if( !pItem->fg.isRecursive || aCsrMap[pItem->iCursor+1]==0 ){ ++ aCsrMap[pItem->iCursor+1] = pParse->nTab++; ++ } ++ pItem->iCursor = aCsrMap[pItem->iCursor+1]; ++ if( pItem->fg.isSubquery ){ ++ for(p=pItem->u4.pSubq->pSelect; p; p=p->pPrior){ ++ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1); ++ } ++ } ++ } ++ } ++} ++ ++/* ++** *piCursor is a cursor number. Change it if it needs to be mapped. ++*/ ++static void renumberCursorDoMapping(Walker *pWalker, int *piCursor){ ++ int *aCsrMap = pWalker->u.aiCol; ++ int iCsr = *piCursor; ++ if( iCsr < aCsrMap[0] && aCsrMap[iCsr+1]>0 ){ ++ *piCursor = aCsrMap[iCsr+1]; ++ } ++} ++ ++/* ++** Expression walker callback used by renumberCursors() to update ++** Expr objects to match newly assigned cursor numbers. ++*/ ++static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ ++ int op = pExpr->op; ++ if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){ ++ renumberCursorDoMapping(pWalker, &pExpr->iTable); ++ } ++ if( ExprHasProperty(pExpr, EP_OuterON) ){ ++ renumberCursorDoMapping(pWalker, &pExpr->w.iJoin); ++ } ++ return WRC_Continue; ++} ++ ++/* ++** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc) ++** of the SELECT statement passed as the second argument, and to each ++** cursor in the FROM clause of any FROM clause sub-selects, recursively. ++** Except, do not assign a new cursor number to the iExcept'th element in ++** the FROM clause of (*p). Update all expressions and other references ++** to refer to the new cursor numbers. ++** ++** Argument aCsrMap is an array that may be used for temporary working ++** space. Two guarantees are made by the caller: ++** ++** * the array is larger than the largest cursor number used within the ++** select statement passed as an argument, and ++** ++** * the array entries for all cursor numbers that do *not* appear in ++** FROM clauses of the select statement as described above are ++** initialized to zero. ++*/ ++static void renumberCursors( ++ Parse *pParse, /* Parse context */ ++ Select *p, /* Select to renumber cursors within */ ++ int iExcept, /* FROM clause item to skip */ ++ int *aCsrMap /* Working space */ ++){ ++ Walker w; ++ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept); ++ memset(&w, 0, sizeof(w)); ++ w.u.aiCol = aCsrMap; ++ w.xExprCallback = renumberCursorsCb; ++ w.xSelectCallback = sqlite3SelectWalkNoop; ++ sqlite3WalkSelect(&w, p); ++} ++#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ ++ ++/* ++** If pSel is not part of a compound SELECT, return a pointer to its ++** expression list. Otherwise, return a pointer to the expression list ++** of the leftmost SELECT in the compound. ++*/ ++static ExprList *findLeftmostExprlist(Select *pSel){ ++ while( pSel->pPrior ){ ++ pSel = pSel->pPrior; ++ } ++ return pSel->pEList; ++} ++ ++/* ++** Return true if any of the result-set columns in the compound query ++** have incompatible affinities on one or more arms of the compound. ++*/ ++static int compoundHasDifferentAffinities(Select *p){ ++ int ii; ++ ExprList *pList; ++ assert( p!=0 ); ++ assert( p->pEList!=0 ); ++ assert( p->pPrior!=0 ); ++ pList = p->pEList; ++ for(ii=0; iinExpr; ii++){ ++ char aff; ++ Select *pSub1; ++ assert( pList->a[ii].pExpr!=0 ); ++ aff = sqlite3ExprAffinity(pList->a[ii].pExpr); ++ for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){ ++ assert( pSub1->pEList!=0 ); ++ assert( pSub1->pEList->nExpr>ii ); ++ assert( pSub1->pEList->a[ii].pExpr!=0 ); ++ if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){ ++ return 1; ++ } ++ } ++ } ++ return 0; ++} ++ + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) + /* + ** This routine attempts to flatten subqueries as a performance optimization. +@@ -132965,11 +149083,13 @@ static void recomputeColumnsUsed( + ** from 2015-02-09.) + ** + ** (3) If the subquery is the right operand of a LEFT JOIN then +-** (3a) the subquery may not be a join and +-** (3b) the FROM clause of the subquery may not contain a virtual +-** table and +-** (3c) the outer query may not be an aggregate. ++** (3a) the subquery may not be a join ++** (**) Was (3b): "the FROM clause of the subquery may not contain ++** a virtual table" ++** (**) Was: "The outer query may not have a GROUP BY." This case ++** is now managed correctly + ** (3d) the outer query may not be DISTINCT. ++** See also (26) for restrictions on RIGHT JOIN. + ** + ** (4) The subquery can not be DISTINCT. + ** +@@ -132990,7 +149110,7 @@ static void recomputeColumnsUsed( + ** (9) If the subquery uses LIMIT then the outer query may not be aggregate. + ** + ** (**) Restriction (10) was removed from the code on 2005-02-05 but we +-** accidently carried the comment forward until 2014-09-15. Original ++** accidentally carried the comment forward until 2014-09-15. Original + ** constraint: "If the subquery is aggregate then the outer query + ** may not use LIMIT." + ** +@@ -133018,9 +149138,14 @@ static void recomputeColumnsUsed( + ** (17c) every term within the subquery compound must have a FROM clause + ** (17d) the outer query may not be + ** (17d1) aggregate, or +-** (17d2) DISTINCT, or +-** (17d3) a join. +-** (17e) the subquery may not contain window functions ++** (17d2) DISTINCT ++** (17e) the subquery may not contain window functions, and ++** (17f) the subquery must not be the RHS of a LEFT JOIN. ++** (17g) either the subquery is the first element of the outer ++** query or there are no RIGHT or FULL JOINs in any arm ++** of the subquery. (This is a duplicate of condition (27b).) ++** (17h) The corresponding result set expressions in all arms of the ++** compound must have the same affinity. + ** + ** The parent and sub-query may contain WHERE clauses. Subject to + ** rules (11), (13) and (14), they may also contain ORDER BY, +@@ -133036,8 +149161,8 @@ static void recomputeColumnsUsed( + ** syntax error and return a detailed message. + ** + ** (18) If the sub-query is a compound select, then all terms of the +-** ORDER BY clause of the parent must be simple references to +-** columns of the sub-query. ++** ORDER BY clause of the parent must be copies of a term returned ++** by the parent query. + ** + ** (19) If the subquery uses LIMIT then the outer query may not + ** have a WHERE clause. +@@ -133053,9 +149178,8 @@ static void recomputeColumnsUsed( + ** + ** (22) The subquery may not be a recursive CTE. + ** +-** (**) Subsumed into restriction (17d3). Was: If the outer query is +-** a recursive CTE, then the sub-query may not be a compound query. +-** This restriction is because transforming the ++** (23) If the outer query is a recursive CTE, then the sub-query may not be ++** a compound query. This restriction is because transforming the + ** parent to a compound query confuses the code that handles + ** recursive queries in multiSelect(). + ** +@@ -133069,6 +149193,18 @@ static void recomputeColumnsUsed( + ** function in the select list or ORDER BY clause, flattening + ** is not attempted. + ** ++** (26) The subquery may not be the right operand of a RIGHT JOIN. ++** See also (3) for restrictions on LEFT JOIN. ++** ++** (27) The subquery may not contain a FULL or RIGHT JOIN unless it ++** is the first element of the parent query. Two subcases: ++** (27a) the subquery is not a compound query. ++** (27b) the subquery is a compound query and the RIGHT JOIN occurs ++** in any arm of the compound query. (See also (17g).) ++** ++** (28) The subquery is not a MATERIALIZED CTE. (This is handled ++** in the caller before ever reaching this routine.) ++** + ** + ** In this routine, the "p" parameter is a pointer to the outer query. + ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query +@@ -133094,12 +149230,13 @@ static int flattenSubquery( + SrcList *pSubSrc; /* The FROM clause of the subquery */ + int iParent; /* VDBE cursor number of the pSub result set temp table */ + int iNewParent = -1;/* Replacement table for iParent */ +- int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ ++ int isOuterJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ + int i; /* Loop counter */ + Expr *pWhere; /* The WHERE clause */ +- struct SrcList_item *pSubitem; /* The subquery */ ++ SrcItem *pSubitem; /* The subquery */ + sqlite3 *db = pParse->db; + Walker w; /* Walker to persist agginfo data */ ++ int *aCsrMap = 0; + + /* Check to see if flattening is permitted. Return 0 if not. + */ +@@ -133110,7 +149247,8 @@ static int flattenSubquery( + assert( pSrc && iFrom>=0 && iFromnSrc ); + pSubitem = &pSrc->a[iFrom]; + iParent = pSubitem->iCursor; +- pSub = pSubitem->pSelect; ++ assert( pSubitem->fg.isSubquery ); ++ pSub = pSubitem->u4.pSubq->pSelect; + assert( pSub!=0 ); + + #ifndef SQLITE_OMIT_WINDOWFUNC +@@ -133159,32 +149297,26 @@ static int flattenSubquery( + ** + ** which is not at all the same thing. + ** +- ** If the subquery is the right operand of a LEFT JOIN, then the outer +- ** query cannot be an aggregate. (3c) This is an artifact of the way +- ** aggregates are processed - there is no mechanism to determine if +- ** the LEFT JOIN table should be all-NULL. +- ** + ** See also tickets #306, #350, and #3300. + */ +- if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){ +- isLeftJoin = 1; +- if( pSubSrc->nSrc>1 /* (3a) */ +- || isAgg /* (3b) */ +- || IsVirtual(pSubSrc->a[0].pTab) /* (3c) */ +- || (p->selFlags & SF_Distinct)!=0 /* (3d) */ ++ if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){ ++ if( pSubSrc->nSrc>1 /* (3a) */ ++ /**** || IsVirtual(pSubSrc->a[0].pSTab) (3b)-omitted */ ++ || (p->selFlags & SF_Distinct)!=0 /* (3d) */ ++ || (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */ + ){ + return 0; + } ++ isOuterJoin = 1; + } +-#ifdef SQLITE_EXTRA_IFNULLROW +- else if( iFrom>0 && !isAgg ){ +- /* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for +- ** every reference to any result column from subquery in a join, even +- ** though they are not necessary. This will stress-test the OP_IfNullRow +- ** opcode. */ +- isLeftJoin = -1; ++ ++ assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */ ++ if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ ++ return 0; /* Restriction (27a) */ + } +-#endif ++ ++ /* Condition (28) is blocked by the caller */ ++ assert( !pSubitem->fg.isCte || pSubitem->u2.pCteUse->eM10d!=M10d_Yes ); + + /* Restriction (17): If the sub-query is a compound SELECT, then it must + ** use only the UNION ALL operator. And none of the simple select queries +@@ -133192,16 +149324,18 @@ static int flattenSubquery( + ** queries. + */ + if( pSub->pPrior ){ ++ int ii; + if( pSub->pOrderBy ){ + return 0; /* Restriction (20) */ + } +- if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){ +- return 0; /* (17d1), (17d2), or (17d3) */ ++ if( isAgg || (p->selFlags & SF_Distinct)!=0 || isOuterJoin>0 ){ ++ return 0; /* (17d1), (17d2), or (17f) */ + } + for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ + testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); + testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); + assert( pSub->pSrc!=0 ); ++ assert( (pSub->selFlags & SF_Recursive)==0 ); + assert( pSub->pEList->nExpr==pSub1->pEList->nExpr ); + if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 /* (17b) */ + || (pSub1->pPrior && pSub1->op!=TK_ALL) /* (17a) */ +@@ -133212,28 +149346,38 @@ static int flattenSubquery( + ){ + return 0; + } ++ if( iFrom>0 && (pSub1->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ ++ /* Without this restriction, the JT_LTORJ flag would end up being ++ ** omitted on left-hand tables of the right join that is being ++ ** flattened. */ ++ return 0; /* Restrictions (17g), (27b) */ ++ } + testcase( pSub1->pSrc->nSrc>1 ); + } + + /* Restriction (18). */ + if( p->pOrderBy ){ +- int ii; + for(ii=0; iipOrderBy->nExpr; ii++){ + if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; + } + } +- } + +- /* Ex-restriction (23): +- ** The only way that the recursive part of a CTE can contain a compound +- ** subquery is for the subquery to be one term of a join. But if the +- ** subquery is a join, then the flattening has already been stopped by +- ** restriction (17d3) +- */ +- assert( (p->selFlags & SF_Recursive)==0 || pSub->pPrior==0 ); ++ /* Restriction (23) */ ++ if( (p->selFlags & SF_Recursive) ) return 0; ++ ++ /* Restriction (17h) */ ++ if( compoundHasDifferentAffinities(pSub) ) return 0; ++ ++ if( pSrc->nSrc>1 ){ ++ if( pParse->nSelect>500 ) return 0; ++ if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0; ++ aCsrMap = sqlite3DbMallocZero(db, ((i64)pParse->nTab+1)*sizeof(int)); ++ if( aCsrMap ) aCsrMap[0] = pParse->nTab; ++ } ++ } + + /***** If we reach this point, flattening is permitted. *****/ +- SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n", ++ TREETRACE(0x4,pParse,p,("flatten %u.%p from term %d\n", + pSub->selId, pSub, iFrom)); + + /* Authorize the subquery */ +@@ -133242,6 +149386,21 @@ static int flattenSubquery( + testcase( i==SQLITE_DENY ); + pParse->zAuthContext = zSavedAuthContext; + ++ /* Delete the transient structures associated with the subquery */ ++ ++ if( ALWAYS(pSubitem->fg.isSubquery) ){ ++ pSub1 = sqlite3SubqueryDetach(db, pSubitem); ++ }else{ ++ pSub1 = 0; ++ } ++ assert( pSubitem->fg.isSubquery==0 ); ++ assert( pSubitem->fg.fixedSchema==0 ); ++ sqlite3DbFree(db, pSubitem->zName); ++ sqlite3DbFree(db, pSubitem->zAlias); ++ pSubitem->zName = 0; ++ pSubitem->zAlias = 0; ++ assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 ); ++ + /* If the sub-query is a compound SELECT statement, then (by restrictions + ** 17 and 18 above) it must be a UNION ALL and the parent query must + ** be of the form: +@@ -133280,43 +149439,40 @@ static int flattenSubquery( + ExprList *pOrderBy = p->pOrderBy; + Expr *pLimit = p->pLimit; + Select *pPrior = p->pPrior; ++ Table *pItemTab = pSubitem->pSTab; ++ pSubitem->pSTab = 0; + p->pOrderBy = 0; +- p->pSrc = 0; + p->pPrior = 0; + p->pLimit = 0; + pNew = sqlite3SelectDup(db, p, 0); + p->pLimit = pLimit; + p->pOrderBy = pOrderBy; +- p->pSrc = pSrc; + p->op = TK_ALL; ++ pSubitem->pSTab = pItemTab; + if( pNew==0 ){ + p->pPrior = pPrior; + }else{ ++ pNew->selId = ++pParse->nSelect; ++ if( aCsrMap && ALWAYS(db->mallocFailed==0) ){ ++ renumberCursors(pParse, pNew, iFrom, aCsrMap); ++ } + pNew->pPrior = pPrior; + if( pPrior ) pPrior->pNext = pNew; + pNew->pNext = p; + p->pPrior = pNew; +- SELECTTRACE(2,pParse,p,("compound-subquery flattener" ++ TREETRACE(0x4,pParse,p,("compound-subquery flattener" + " creates %u as peer\n",pNew->selId)); + } +- if( db->mallocFailed ) return 1; ++ assert( pSubitem->fg.isSubquery==0 ); ++ } ++ sqlite3DbFree(db, aCsrMap); ++ if( db->mallocFailed ){ ++ assert( pSubitem->fg.fixedSchema==0 ); ++ assert( pSubitem->fg.isSubquery==0 ); ++ assert( pSubitem->u4.zDatabase==0 ); ++ sqlite3SrcItemAttachSubquery(pParse, pSubitem, pSub1, 0); ++ return 1; + } +- +- /* Begin flattening the iFrom-th entry of the FROM clause +- ** in the outer query. +- */ +- pSub = pSub1 = pSubitem->pSelect; +- +- /* Delete the transient table structure associated with the +- ** subquery +- */ +- sqlite3DbFree(db, pSubitem->zDatabase); +- sqlite3DbFree(db, pSubitem->zName); +- sqlite3DbFree(db, pSubitem->zAlias); +- pSubitem->zDatabase = 0; +- pSubitem->zName = 0; +- pSubitem->zAlias = 0; +- pSubitem->pSelect = 0; + + /* Defer deleting the Table object associated with the + ** subquery until code generation is +@@ -133325,16 +149481,16 @@ static int flattenSubquery( + ** + ** pSubitem->pTab is always non-NULL by test restrictions and tests above. + */ +- if( ALWAYS(pSubitem->pTab!=0) ){ +- Table *pTabToDel = pSubitem->pTab; ++ if( ALWAYS(pSubitem->pSTab!=0) ){ ++ Table *pTabToDel = pSubitem->pSTab; + if( pTabToDel->nTabRef==1 ){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); +- pTabToDel->pNextZombie = pToplevel->pZombieTab; +- pToplevel->pZombieTab = pTabToDel; ++ sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel); ++ testcase( pToplevel->earlyCleanup ); + }else{ + pTabToDel->nTabRef--; + } +- pSubitem->pTab = 0; ++ pSubitem->pSTab = 0; + } + + /* The following loop runs once for each term in a compound-subquery +@@ -133350,22 +149506,18 @@ static int flattenSubquery( + ** those references with expressions that resolve to the subquery FROM + ** elements we are now copying in. + */ ++ pSub = pSub1; + for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){ + int nSubSrc; + u8 jointype = 0; ++ u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ; + assert( pSub!=0 ); + pSubSrc = pSub->pSrc; /* FROM clause of subquery */ + nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */ + pSrc = pParent->pSrc; /* FROM clause of the outer query */ + +- if( pSrc ){ +- assert( pParent==p ); /* First time through the loop */ +- jointype = pSubitem->fg.jointype; +- }else{ +- assert( pParent!=p ); /* 2nd and subsequent times through the loop */ +- pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); +- if( pSrc==0 ) break; +- pParent->pSrc = pSrc; ++ if( pParent==p ){ ++ jointype = pSubitem->fg.jointype; /* First time through the loop */ + } + + /* The subquery uses a single slot of the FROM clause of the outer +@@ -133392,14 +149544,20 @@ static int flattenSubquery( + /* Transfer the FROM clause terms from the subquery into the + ** outer query. + */ ++ iNewParent = pSubSrc->a[0].iCursor; + for(i=0; ia[i+iFrom].pUsing); +- assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); +- pSrc->a[i+iFrom] = pSubSrc->a[i]; +- iNewParent = pSubSrc->a[i].iCursor; ++ SrcItem *pItem = &pSrc->a[i+iFrom]; ++ assert( pItem->fg.isTabFunc==0 ); ++ assert( pItem->fg.isSubquery ++ || pItem->fg.fixedSchema ++ || pItem->u4.zDatabase==0 ); ++ if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); ++ *pItem = pSubSrc->a[i]; ++ pItem->fg.jointype |= ltorj; + memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); + } +- pSrc->a[iFrom].fg.jointype = jointype; ++ pSrc->a[iFrom].fg.jointype &= JT_LTORJ; ++ pSrc->a[iFrom].fg.jointype |= jointype | ltorj; + + /* Now begin substituting subquery result set expressions for + ** references to the iParent in the outer query. +@@ -133418,7 +149576,7 @@ static int flattenSubquery( + ** ORDER BY column expression is identical to the iOrderByCol'th + ** expression returned by SELECT statement pSub. Since these values + ** do not necessarily correspond to columns in SELECT statement pParent, +- ** zero them before transfering the ORDER BY clause. ++ ** zero them before transferring the ORDER BY clause. + ** + ** Not doing this may cause an error if a subsequent call to this + ** function attempts to flatten a compound sub-query into pParent +@@ -133434,8 +149592,9 @@ static int flattenSubquery( + } + pWhere = pSub->pWhere; + pSub->pWhere = 0; +- if( isLeftJoin>0 ){ +- sqlite3SetJoinExpr(pWhere, iNewParent); ++ if( isOuterJoin>0 ){ ++ assert( pSubSrc->nSrc==1 ); ++ sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); + } + if( pWhere ){ + if( pParent->pWhere ){ +@@ -133449,8 +149608,9 @@ static int flattenSubquery( + x.pParse = pParse; + x.iTable = iParent; + x.iNewTable = iNewParent; +- x.isLeftJoin = isLeftJoin; ++ x.isOuterJoin = isOuterJoin; + x.pEList = pSub->pEList; ++ x.pCList = findLeftmostExprlist(pSub); + substSelect(&x, pParent, 0); + } + +@@ -133470,23 +149630,22 @@ static int flattenSubquery( + pSub->pLimit = 0; + } + +- /* Recompute the SrcList_item.colUsed masks for the flattened ++ /* Recompute the SrcItem.colUsed masks for the flattened + ** tables. */ + for(i=0; ia[i+iFrom]); + } + } + +- /* Finially, delete what is left of the subquery and return +- ** success. ++ /* Finally, delete what is left of the subquery and return success. + */ + sqlite3AggInfoPersistWalkerInit(&w, pParse); + sqlite3WalkSelect(&w,pSub1); + sqlite3SelectDelete(db, pSub1); + +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x100 ){ +- SELECTTRACE(0x100,pParse,p,("After flattening:\n")); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x4 ){ ++ TREETRACE(0x4,pParse,p,("After flattening:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif +@@ -133502,14 +149661,18 @@ static int flattenSubquery( + typedef struct WhereConst WhereConst; + struct WhereConst { + Parse *pParse; /* Parsing context */ ++ u8 *pOomFault; /* Pointer to pParse->db->mallocFailed */ + int nConst; /* Number for COLUMN=CONSTANT terms */ + int nChng; /* Number of times a constant is propagated */ ++ int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ ++ u32 mExcludeOn; /* Which ON expressions to exclude from considertion. ++ ** Either EP_OuterON or EP_InnerON|EP_OuterON */ + Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ + }; + + /* + ** Add a new entry to the pConst object. Except, do not add duplicate +-** pColumn entires. Also, do not add if doing so would not be appropriate. ++** pColumn entries. Also, do not add if doing so would not be appropriate. + ** + ** The caller guarantees the pColumn is a column and pValue is a constant. + ** This routine has to do some additional checks before completing the +@@ -133523,7 +149686,7 @@ static void constInsert( + ){ + int i; + assert( pColumn->op==TK_COLUMN ); +- assert( sqlite3ExprIsConstant(pValue) ); ++ assert( sqlite3ExprIsConstant(pConst->pParse, pValue) ); + + if( ExprHasProperty(pColumn, EP_FixedCol) ) return; + if( sqlite3ExprAffinity(pValue)!=0 ) return; +@@ -133542,6 +149705,10 @@ static void constInsert( + return; /* Already present. Return without doing anything. */ + } + } ++ assert( SQLITE_AFF_NONEbHasAffBlob = 1; ++ } + + pConst->nConst++; + pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, +@@ -133562,8 +149729,12 @@ static void constInsert( + */ + static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ + Expr *pRight, *pLeft; +- if( pExpr==0 ) return; +- if( ExprHasProperty(pExpr, EP_FromJoin) ) return; ++ if( NEVER(pExpr==0) ) return; ++ if( ExprHasProperty(pExpr, pConst->mExcludeOn) ){ ++ testcase( ExprHasProperty(pExpr, EP_OuterON) ); ++ testcase( ExprHasProperty(pExpr, EP_InnerON) ); ++ return; ++ } + if( pExpr->op==TK_AND ){ + findConstInWhere(pConst, pExpr->pRight); + findConstInWhere(pConst, pExpr->pLeft); +@@ -133574,46 +149745,94 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ + pLeft = pExpr->pLeft; + assert( pRight!=0 ); + assert( pLeft!=0 ); +- if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){ ++ if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){ + constInsert(pConst,pRight,pLeft,pExpr); + } +- if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){ ++ if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){ + constInsert(pConst,pLeft,pRight,pExpr); + } + } + + /* +-** This is a Walker expression callback. pExpr is a candidate expression +-** to be replaced by a value. If pExpr is equivalent to one of the +-** columns named in pWalker->u.pConst, then overwrite it with its +-** corresponding value. ++** This is a helper function for Walker callback propagateConstantExprRewrite(). ++** ++** Argument pExpr is a candidate expression to be replaced by a value. If ++** pExpr is equivalent to one of the columns named in pWalker->u.pConst, ++** then overwrite it with the corresponding value. Except, do not do so ++** if argument bIgnoreAffBlob is non-zero and the affinity of pExpr ++** is SQLITE_AFF_BLOB. + */ +-static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ ++static int propagateConstantExprRewriteOne( ++ WhereConst *pConst, ++ Expr *pExpr, ++ int bIgnoreAffBlob ++){ + int i; +- WhereConst *pConst; ++ if( pConst->pOomFault[0] ) return WRC_Prune; + if( pExpr->op!=TK_COLUMN ) return WRC_Continue; +- if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ ++ if( ExprHasProperty(pExpr, EP_FixedCol|pConst->mExcludeOn) ){ + testcase( ExprHasProperty(pExpr, EP_FixedCol) ); +- testcase( ExprHasProperty(pExpr, EP_FromJoin) ); ++ testcase( ExprHasProperty(pExpr, EP_OuterON) ); ++ testcase( ExprHasProperty(pExpr, EP_InnerON) ); + return WRC_Continue; + } +- pConst = pWalker->u.pConst; + for(i=0; inConst; i++){ + Expr *pColumn = pConst->apExpr[i*2]; + if( pColumn==pExpr ) continue; + if( pColumn->iTable!=pExpr->iTable ) continue; + if( pColumn->iColumn!=pExpr->iColumn ) continue; ++ assert( SQLITE_AFF_NONEnChng++; + ExprClearProperty(pExpr, EP_Leaf); + ExprSetProperty(pExpr, EP_FixedCol); + assert( pExpr->pLeft==0 ); + pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); ++ if( pConst->pParse->db->mallocFailed ) return WRC_Prune; + break; + } + return WRC_Prune; + } + ++/* ++** This is a Walker expression callback. pExpr is a node from the WHERE ++** clause of a SELECT statement. This function examines pExpr to see if ++** any substitutions based on the contents of pWalker->u.pConst should ++** be made to pExpr or its immediate children. ++** ++** A substitution is made if: ++** ++** + pExpr is a column with an affinity other than BLOB that matches ++** one of the columns in pWalker->u.pConst, or ++** ++** + pExpr is a binary comparison operator (=, <=, >=, <, >) that ++** uses an affinity other than TEXT and one of its immediate ++** children is a column that matches one of the columns in ++** pWalker->u.pConst. ++*/ ++static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ ++ WhereConst *pConst = pWalker->u.pConst; ++ assert( TK_GT==TK_EQ+1 ); ++ assert( TK_LE==TK_EQ+2 ); ++ assert( TK_LT==TK_EQ+3 ); ++ assert( TK_GE==TK_EQ+4 ); ++ if( pConst->bHasAffBlob ){ ++ if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) ++ || pExpr->op==TK_IS ++ ){ ++ propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); ++ if( pConst->pOomFault[0] ) return WRC_Prune; ++ if( sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ ++ propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); ++ } ++ } ++ } ++ return propagateConstantExprRewriteOne(pConst, pExpr, pConst->bHasAffBlob); ++} ++ + /* + ** The WHERE-clause constant propagation optimization. + ** +@@ -133641,7 +149860,7 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ + ** SELECT * FROM t1 WHERE a=123 AND b=123; + ** + ** The two SELECT statements above should return different answers. b=a +-** is alway true because the comparison uses numeric affinity, but b=123 ++** is always true because the comparison uses numeric affinity, but b=123 + ** is false because it uses text affinity and '0123' is not the same as '123'. + ** To work around this, the expression tree is not actually changed from + ** "b=a" to "b=123" but rather the "a" in "b=a" is tagged with EP_FixedCol +@@ -133649,6 +149868,21 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ + ** routines know to generate the constant "123" instead of looking up the + ** column value. Also, to avoid collation problems, this optimization is + ** only attempted if the "a=123" term uses the default BINARY collation. ++** ++** 2021-05-25 forum post 6a06202608: Another troublesome case is... ++** ++** CREATE TABLE t1(x); ++** INSERT INTO t1 VALUES(10.0); ++** SELECT 1 FROM t1 WHERE x=10 AND x LIKE 10; ++** ++** The query should return no rows, because the t1.x value is '10.0' not '10' ++** and '10.0' is not LIKE '10'. But if we are not careful, the first WHERE ++** term "x=10" will cause the second WHERE term to become "10 LIKE 10", ++** resulting in a false positive. To avoid this, constant propagation for ++** columns with BLOB affinity is only allowed if the constant is used with ++** operators ==, <=, <, >=, >, or IS in a way that will cause the correct ++** type conversions to occur. See logic associated with the bHasAffBlob flag ++** for details. + */ + static int propagateConstants( + Parse *pParse, /* The parsing context */ +@@ -133658,10 +149892,23 @@ static int propagateConstants( + Walker w; + int nChng = 0; + x.pParse = pParse; ++ x.pOomFault = &pParse->db->mallocFailed; + do{ + x.nConst = 0; + x.nChng = 0; + x.apExpr = 0; ++ x.bHasAffBlob = 0; ++ if( ALWAYS(p->pSrc!=0) ++ && p->pSrc->nSrc>0 ++ && (p->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ++ ){ ++ /* Do not propagate constants on any ON clause if there is a ++ ** RIGHT JOIN anywhere in the query */ ++ x.mExcludeOn = EP_InnerON | EP_OuterON; ++ }else{ ++ /* Do not propagate constants through the ON clause of a LEFT JOIN */ ++ x.mExcludeOn = EP_OuterON; ++ } + findConstInWhere(&x, p->pWhere); + if( x.nConst ){ + memset(&w, 0, sizeof(w)); +@@ -133679,6 +149926,35 @@ static int propagateConstants( + return nChng; + } + ++#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) ++# if !defined(SQLITE_OMIT_WINDOWFUNC) ++/* ++** This function is called to determine whether or not it is safe to ++** push WHERE clause expression pExpr down to FROM clause sub-query ++** pSubq, which contains at least one window function. Return 1 ++** if it is safe and the expression should be pushed down, or 0 ++** otherwise. ++** ++** It is only safe to push the expression down if it consists only ++** of constants and copies of expressions that appear in the PARTITION ++** BY clause of all window function used by the sub-query. It is safe ++** to filter out entire partitions, but not rows within partitions, as ++** this may change the results of the window functions. ++** ++** At the time this function is called it is guaranteed that ++** ++** * the sub-query uses only one distinct window frame, and ++** * that the window frame has a PARTITION BY clause. ++*/ ++static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ++ assert( pSubq->pWin->pPartition ); ++ assert( (pSubq->selFlags & SF_MultiPart)==0 ); ++ assert( pSubq->pPrior==0 ); ++ return sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition); ++} ++# endif /* SQLITE_OMIT_WINDOWFUNC */ ++#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ ++ + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) + /* + ** Make copies of relevant WHERE clause terms of the outer query into +@@ -133694,6 +149970,19 @@ static int propagateConstants( + ** The hope is that the terms added to the inner query will make it more + ** efficient. + ** ++** NAME AMBIGUITY ++** ++** This optimization is called the "WHERE-clause push-down optimization" ++** or sometimes the "predicate push-down optimization". ++** ++** Do not confuse this optimization with another unrelated optimization ++** with a similar name: The "MySQL push-down optimization" causes WHERE ++** clause terms that can be evaluated using only the index and without ++** reference to the table are run first, so that if they are false, ++** unnecessary table seeks are avoided. ++** ++** RULES ++** + ** Do not attempt this optimization if: + ** + ** (1) (** This restriction was removed on 2017-09-29. We used to +@@ -133726,9 +150015,51 @@ static int propagateConstants( + ** But if the (b2=2) term were to be pushed down into the bb subquery, + ** then the (1,1,NULL) row would be suppressed. + ** +-** (6) The inner query features one or more window-functions (since +-** changes to the WHERE clause of the inner query could change the +-** window over which window functions are calculated). ++** (6) Window functions make things tricky as changes to the WHERE clause ++** of the inner query could change the window over which window ++** functions are calculated. Therefore, do not attempt the optimization ++** if: ++** ++** (6a) The inner query uses multiple incompatible window partitions. ++** ++** (6b) The inner query is a compound and uses window-functions. ++** ++** (6c) The WHERE clause does not consist entirely of constants and ++** copies of expressions found in the PARTITION BY clause of ++** all window-functions used by the sub-query. It is safe to ++** filter out entire partitions, as this does not change the ++** window over which any window-function is calculated. ++** ++** (7) The inner query is a Common Table Expression (CTE) that should ++** be materialized. (This restriction is implemented in the calling ++** routine.) ++** ++** (8) If the subquery is a compound that uses UNION, INTERSECT, ++** or EXCEPT, then all of the result set columns for all arms of ++** the compound must use the BINARY collating sequence. ++** ++** (9) All three of the following are true: ++** ++** (9a) The WHERE clause expression originates in the ON or USING clause ++** of a join (either an INNER or an OUTER join), and ++** ++** (9b) The subquery is to the right of the ON/USING clause ++** ++** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING ++** clause and the subquery. ++** ++** Without this restriction, the WHERE-clause push-down optimization ++** might move the ON/USING filter expression from the left side of a ++** RIGHT JOIN over to the right side, which leads to incorrect answers. ++** See also restriction (6) in sqlite3ExprIsSingleTableConstraint(). ++** ++** (10) The inner query is not the right-hand table of a RIGHT JOIN. ++** ++** (11) The subquery is not a VALUES clause ++** ++** (12) The WHERE clause is not "rowid ISNULL" or the equivalent. This ++** case only comes up if SQLite is compiled using ++** SQLITE_ALLOW_ROWID_IN_VIEW. + ** + ** Return 0 if no changes are made and non-zero if one or more WHERE clause + ** terms are duplicated into the subquery. +@@ -133737,20 +150068,56 @@ static int pushDownWhereTerms( + Parse *pParse, /* Parse context (for malloc() and error reporting) */ + Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ + Expr *pWhere, /* The WHERE clause of the outer query */ +- int iCursor, /* Cursor number of the subquery */ +- int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */ ++ SrcList *pSrcList, /* The complete from clause of the outer query */ ++ int iSrc /* Which FROM clause term to try to push into */ + ){ + Expr *pNew; ++ SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */ + int nChng = 0; +- Select *pSel; ++ pSrc = &pSrcList->a[iSrc]; + if( pWhere==0 ) return 0; +- if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */ ++ if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ){ ++ return 0; /* restrictions (2) and (11) */ ++ } ++ if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ){ ++ return 0; /* restrictions (10) */ ++ } + ++ if( pSubq->pPrior ){ ++ Select *pSel; ++ int notUnionAll = 0; ++ for(pSel=pSubq; pSel; pSel=pSel->pPrior){ ++ u8 op = pSel->op; ++ assert( op==TK_ALL || op==TK_SELECT ++ || op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT ); ++ if( op!=TK_ALL && op!=TK_SELECT ){ ++ notUnionAll = 1; ++ } + #ifndef SQLITE_OMIT_WINDOWFUNC +- for(pSel=pSubq; pSel; pSel=pSel->pPrior){ +- if( pSel->pWin ) return 0; /* restriction (6) */ +- } ++ if( pSel->pWin ) return 0; /* restriction (6b) */ + #endif ++ } ++ if( notUnionAll ){ ++ /* If any of the compound arms are connected using UNION, INTERSECT, ++ ** or EXCEPT, then we must ensure that none of the columns use a ++ ** non-BINARY collating sequence. */ ++ for(pSel=pSubq; pSel; pSel=pSel->pPrior){ ++ int ii; ++ const ExprList *pList = pSel->pEList; ++ assert( pList!=0 ); ++ for(ii=0; iinExpr; ii++){ ++ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[ii].pExpr); ++ if( !sqlite3IsBinary(pColl) ){ ++ return 0; /* Restriction (8) */ ++ } ++ } ++ } ++ } ++ }else{ ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0; ++#endif ++ } + + #ifdef SQLITE_DEBUG + /* Only the first term of a compound can have a WITH clause. But make +@@ -133769,31 +150136,75 @@ static int pushDownWhereTerms( + return 0; /* restriction (3) */ + } + while( pWhere->op==TK_AND ){ +- nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, +- iCursor, isLeftJoin); ++ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc); + pWhere = pWhere->pLeft; + } ++ ++#if 0 /* These checks now done by sqlite3ExprIsSingleTableConstraint() */ ++ if( ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) /* (9a) */ ++ && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (9c) */ ++ ){ ++ int jj; ++ for(jj=0; jjw.iJoin==pSrcList->a[jj].iCursor ){ ++ /* If we reach this point, both (9a) and (9b) are satisfied. ++ ** The following loop checks (9c): ++ */ ++ for(jj++; jja[jj].fg.jointype & JT_RIGHT)!=0 ){ ++ return 0; /* restriction (9) */ ++ } ++ } ++ } ++ } ++ } + if( isLeftJoin +- && (ExprHasProperty(pWhere,EP_FromJoin)==0 +- || pWhere->iRightJoinTable!=iCursor) ++ && (ExprHasProperty(pWhere,EP_OuterON)==0 ++ || pWhere->w.iJoin!=iCursor) + ){ + return 0; /* restriction (4) */ + } +- if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){ ++ if( ExprHasProperty(pWhere,EP_OuterON) ++ && pWhere->w.iJoin!=iCursor ++ ){ + return 0; /* restriction (5) */ + } +- if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ ++#endif ++ ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ if( ViewCanHaveRowid && (pWhere->op==TK_ISNULL || pWhere->op==TK_NOTNULL) ){ ++ Expr *pLeft = pWhere->pLeft; ++ if( ALWAYS(pLeft) ++ && pLeft->op==TK_COLUMN ++ && pLeft->iColumn < 0 ++ ){ ++ return 0; /* Restriction (12) */ ++ } ++ } ++#endif ++ ++ if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){ + nChng++; ++ pSubq->selFlags |= SF_PushDown; + while( pSubq ){ + SubstContext x; + pNew = sqlite3ExprDup(pParse->db, pWhere, 0); +- unsetJoinExpr(pNew, -1); ++ unsetJoinExpr(pNew, -1, 1); + x.pParse = pParse; +- x.iTable = iCursor; +- x.iNewTable = iCursor; +- x.isLeftJoin = 0; ++ x.iTable = pSrc->iCursor; ++ x.iNewTable = pSrc->iCursor; ++ x.isOuterJoin = 0; + x.pEList = pSubq->pEList; ++ x.pCList = findLeftmostExprlist(pSubq); + pNew = substExpr(&x, pNew); ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){ ++ /* Restriction 6c has prevented push-down in this case */ ++ sqlite3ExprDelete(pParse->db, pNew); ++ nChng--; ++ break; ++ } ++#endif + if( pSubq->selFlags & SF_Aggregate ){ + pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew); + }else{ +@@ -133806,6 +150217,78 @@ static int pushDownWhereTerms( + } + #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ + ++/* ++** Check to see if a subquery contains result-set columns that are ++** never used. If it does, change the value of those result-set columns ++** to NULL so that they do not cause unnecessary work to compute. ++** ++** Return the number of column that were changed to NULL. ++*/ ++static int disableUnusedSubqueryResultColumns(SrcItem *pItem){ ++ int nCol; ++ Select *pSub; /* The subquery to be simplified */ ++ Select *pX; /* For looping over compound elements of pSub */ ++ Table *pTab; /* The table that describes the subquery */ ++ int j; /* Column number */ ++ int nChng = 0; /* Number of columns converted to NULL */ ++ Bitmask colUsed; /* Columns that may not be NULLed out */ ++ ++ assert( pItem!=0 ); ++ if( pItem->fg.isCorrelated || pItem->fg.isCte ){ ++ return 0; ++ } ++ assert( pItem->pSTab!=0 ); ++ pTab = pItem->pSTab; ++ assert( pItem->fg.isSubquery ); ++ pSub = pItem->u4.pSubq->pSelect; ++ assert( pSub->pEList->nExpr==pTab->nCol ); ++ for(pX=pSub; pX; pX=pX->pPrior){ ++ if( (pX->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){ ++ testcase( pX->selFlags & SF_Distinct ); ++ testcase( pX->selFlags & SF_Aggregate ); ++ return 0; ++ } ++ if( pX->pPrior && pX->op!=TK_ALL ){ ++ /* This optimization does not work for compound subqueries that ++ ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */ ++ return 0; ++ } ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ if( pX->pWin ){ ++ /* This optimization does not work for subqueries that use window ++ ** functions. */ ++ return 0; ++ } ++#endif ++ } ++ colUsed = pItem->colUsed; ++ if( pSub->pOrderBy ){ ++ ExprList *pList = pSub->pOrderBy; ++ for(j=0; jnExpr; j++){ ++ u16 iCol = pList->a[j].u.x.iOrderByCol; ++ if( iCol>0 ){ ++ iCol--; ++ colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); ++ } ++ } ++ } ++ nCol = pTab->nCol; ++ for(j=0; jpPrior) { ++ Expr *pY = pX->pEList->a[j].pExpr; ++ if( pY->op==TK_NULL ) continue; ++ pY->op = TK_NULL; ++ ExprClearProperty(pY, EP_Skip|EP_Unlikely); ++ pX->selFlags |= SF_PushDown; ++ nChng++; ++ } ++ } ++ return nChng; ++} ++ ++ + /* + ** The pFunc is the only aggregate function in the query. Check to see + ** if the query is a candidate for the min/max optimization. +@@ -133824,7 +150307,7 @@ static int pushDownWhereTerms( + */ + static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ + int eRet = WHERE_ORDERBY_NORMAL; /* Return value */ +- ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */ ++ ExprList *pEList; /* Arguments to agg function */ + const char *zFunc; /* Name of aggregate function pFunc */ + ExprList *pOrderBy; + u8 sortFlags = 0; +@@ -133832,9 +150315,16 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ + assert( *ppMinMax==0 ); + assert( pFunc->op==TK_AGG_FUNCTION ); + assert( !IsWindowFunc(pFunc) ); +- if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){ ++ assert( ExprUseXList(pFunc) ); ++ pEList = pFunc->x.pList; ++ if( pEList==0 ++ || pEList->nExpr!=1 ++ || ExprHasProperty(pFunc, EP_WinFunc) ++ || OptimizationDisabled(db, SQLITE_MinMaxOpt) ++ ){ + return eRet; + } ++ assert( !ExprHasProperty(pFunc, EP_IntValue) ); + zFunc = pFunc->u.zToken; + if( sqlite3StrICmp(zFunc, "min")==0 ){ + eRet = WHERE_ORDERBY_MIN; +@@ -133849,7 +150339,7 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ + } + *ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0); + assert( pOrderBy!=0 || db->mallocFailed ); +- if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags; ++ if( pOrderBy ) pOrderBy->a[0].fg.sortFlags = sortFlags; + return eRet; + } + +@@ -133862,7 +150352,13 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ + ** + ** where table is a database table, not a sub-select or view. If the query + ** does match this pattern, then a pointer to the Table object representing +-** is returned. Otherwise, 0 is returned. ++** is returned. Otherwise, NULL is returned. ++** ++** This routine checks to see if it is safe to use the count optimization. ++** A correct answer is still obtained (though perhaps more slowly) if ++** this routine returns NULL when it could have returned a table pointer. ++** But returning the pointer when NULL should have been returned can ++** result in incorrect answers and/or crashes. So, when in doubt, return NULL. + */ + static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ + Table *pTab; +@@ -133870,19 +150366,27 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ + + assert( !p->pGroupBy ); + +- if( p->pWhere || p->pEList->nExpr!=1 +- || p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect ++ if( p->pWhere ++ || p->pEList->nExpr!=1 ++ || p->pSrc->nSrc!=1 ++ || p->pSrc->a[0].fg.isSubquery ++ || pAggInfo->nFunc!=1 ++ || p->pHaving + ){ + return 0; + } +- pTab = p->pSrc->a[0].pTab; ++ pTab = p->pSrc->a[0].pSTab; ++ assert( pTab!=0 ); ++ assert( !IsView(pTab) ); ++ if( !IsOrdinaryTable(pTab) ) return 0; + pExpr = p->pEList->a[0].pExpr; +- assert( pTab && !pTab->pSelect && pExpr ); +- +- if( IsVirtual(pTab) ) return 0; ++ assert( pExpr!=0 ); + if( pExpr->op!=TK_AGG_FUNCTION ) return 0; +- if( NEVER(pAggInfo->nFunc==0) ) return 0; ++ if( pExpr->pAggInfo!=pAggInfo ) return 0; + if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0; ++ assert( pAggInfo->aFunc[0].pFExpr==pExpr ); ++ testcase( ExprHasProperty(pExpr, EP_Distinct) ); ++ testcase( ExprHasProperty(pExpr, EP_WinFunc) ); + if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0; + + return pTab; +@@ -133895,24 +150399,27 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ + ** SQLITE_ERROR and leave an error in pParse. Otherwise, populate + ** pFrom->pIndex and return SQLITE_OK. + */ +-SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){ +- if( pFrom->pTab && pFrom->fg.isIndexedBy ){ +- Table *pTab = pFrom->pTab; +- char *zIndexedBy = pFrom->u1.zIndexedBy; +- Index *pIdx; +- for(pIdx=pTab->pIndex; +- pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy); +- pIdx=pIdx->pNext +- ); +- if( !pIdx ){ +- sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0); +- pParse->checkSchema = 1; +- return SQLITE_ERROR; +- } +- pFrom->pIBIndex = pIdx; ++SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ ++ Table *pTab = pFrom->pSTab; ++ char *zIndexedBy = pFrom->u1.zIndexedBy; ++ Index *pIdx; ++ assert( pTab!=0 ); ++ assert( pFrom->fg.isIndexedBy!=0 ); ++ ++ for(pIdx=pTab->pIndex; ++ pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy); ++ pIdx=pIdx->pNext ++ ); ++ if( !pIdx ){ ++ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0); ++ pParse->checkSchema = 1; ++ return SQLITE_ERROR; + } ++ assert( pFrom->fg.isCte==0 ); ++ pFrom->u2.pIBIndex = pIdx; + return SQLITE_OK; + } ++ + /* + ** Detect compound SELECT statements that use an ORDER BY clause with + ** an alternative collating sequence. +@@ -133928,7 +150435,7 @@ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pF + ** above that generates the code for a compound SELECT with an ORDER BY clause + ** uses a merge algorithm that requires the same collating sequence on the + ** result columns as on the ORDER BY clause. See ticket +-** http://www.sqlite.org/src/info/6709574d2a ++** http://sqlite.org/src/info/6709574d2a + ** + ** This transformation is only needed for EXCEPT, INTERSECT, and UNION. + ** The UNION ALL operator works fine with multiSelectOrderBy() even when +@@ -133969,8 +150476,12 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ + pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); + if( pNew==0 ) return WRC_Abort; + memset(&dummy, 0, sizeof(dummy)); +- pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0); +- if( pNewSrc==0 ) return WRC_Abort; ++ pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); ++ assert( pNewSrc!=0 || pParse->nErr ); ++ if( pParse->nErr ){ ++ sqlite3SrcListDelete(db, pNewSrc); ++ return WRC_Abort; ++ } + *pNew = *p; + p->pSrc = pNewSrc; + p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0)); +@@ -133985,7 +150496,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ + #ifndef SQLITE_OMIT_WINDOWFUNC + p->pWinDefn = 0; + #endif +- p->selFlags &= ~SF_Compound; ++ p->selFlags &= ~(u32)SF_Compound; + assert( (p->selFlags & SF_Converted)==0 ); + p->selFlags |= SF_Converted; + assert( pNew->pPrior!=0 ); +@@ -133999,7 +150510,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ + ** arguments. If it does, leave an error message in pParse and return + ** non-zero, since pFrom is not allowed to be a table-valued function. + */ +-static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){ ++static int cannotBeFunction(Parse *pParse, SrcItem *pFrom){ + if( pFrom->fg.isTabFunc ){ + sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName); + return 1; +@@ -134020,21 +150531,22 @@ static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){ + */ + static struct Cte *searchWith( + With *pWith, /* Current innermost WITH clause */ +- struct SrcList_item *pItem, /* FROM clause element to resolve */ ++ SrcItem *pItem, /* FROM clause element to resolve */ + With **ppContext /* OUT: WITH clause return value belongs to */ + ){ +- const char *zName; +- if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){ +- With *p; +- for(p=pWith; p; p=p->pOuter){ +- int i; +- for(i=0; inCte; i++){ +- if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){ +- *ppContext = p; +- return &p->a[i]; +- } ++ const char *zName = pItem->zName; ++ With *p; ++ assert( pItem->fg.fixedSchema || pItem->u4.zDatabase==0 ); ++ assert( zName!=0 ); ++ for(p=pWith; p; p=p->pOuter){ ++ int i; ++ for(i=0; inCte; i++){ ++ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){ ++ *ppContext = p; ++ return &p->a[i]; + } + } ++ if( p->bView ) break; + } + return 0; + } +@@ -134044,58 +150556,92 @@ static struct Cte *searchWith( + ** + ** This routine pushes the WITH clause passed as the second argument + ** onto the top of the stack. If argument bFree is true, then this +-** WITH clause will never be popped from the stack. In this case it +-** should be freed along with the Parse object. In other cases, when ++** WITH clause will never be popped from the stack but should instead ++** be freed along with the Parse object. In other cases, when + ** bFree==0, the With object will be freed along with the SELECT + ** statement with which it is associated. ++** ++** This routine returns a copy of pWith. Or, if bFree is true and ++** the pWith object is destroyed immediately due to an OOM condition, ++** then this routine return NULL. ++** ++** If bFree is true, do not continue to use the pWith pointer after ++** calling this routine, Instead, use only the return value. + */ +-SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ +- assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) ); ++SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ + if( pWith ){ +- assert( pParse->pWith!=pWith ); +- pWith->pOuter = pParse->pWith; +- pParse->pWith = pWith; +- if( bFree ) pParse->pWithToFree = pWith; ++ if( bFree ){ ++ pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric, ++ pWith); ++ if( pWith==0 ) return 0; ++ } ++ if( pParse->nErr==0 ){ ++ assert( pParse->pWith!=pWith ); ++ pWith->pOuter = pParse->pWith; ++ pParse->pWith = pWith; ++ } + } ++ return pWith; + } + + /* + ** This function checks if argument pFrom refers to a CTE declared by +-** a WITH clause on the stack currently maintained by the parser. And, +-** if currently processing a CTE expression, if it is a recursive +-** reference to the current CTE. ++** a WITH clause on the stack currently maintained by the parser (on the ++** pParse->pWith linked list). And if currently processing a CTE ++** CTE expression, through routine checks to see if the reference is ++** a recursive reference to the CTE. + ** +-** If pFrom falls into either of the two categories above, pFrom->pTab +-** and other fields are populated accordingly. The caller should check +-** (pFrom->pTab!=0) to determine whether or not a successful match +-** was found. ++** If pFrom matches a CTE according to either of these two above, pFrom->pTab ++** and other fields are populated accordingly. + ** +-** Whether or not a match is found, SQLITE_OK is returned if no error +-** occurs. If an error does occur, an error message is stored in the +-** parser and some error code other than SQLITE_OK returned. ++** Return 0 if no match is found. ++** Return 1 if a match is found. ++** Return 2 if an error condition is detected. + */ +-static int withExpand( +- Walker *pWalker, +- struct SrcList_item *pFrom ++static int resolveFromTermToCte( ++ Parse *pParse, /* The parsing context */ ++ Walker *pWalker, /* Current tree walker */ ++ SrcItem *pFrom /* The FROM clause term to check */ + ){ +- Parse *pParse = pWalker->pParse; +- sqlite3 *db = pParse->db; +- struct Cte *pCte; /* Matched CTE (or NULL if no match) */ +- With *pWith; /* WITH clause that pCte belongs to */ ++ Cte *pCte; /* Matched CTE (or NULL if no match) */ ++ With *pWith; /* The matching WITH */ + +- assert( pFrom->pTab==0 ); ++ assert( pFrom->pSTab==0 ); ++ if( pParse->pWith==0 ){ ++ /* There are no WITH clauses in the stack. No match is possible */ ++ return 0; ++ } + if( pParse->nErr ){ +- return SQLITE_ERROR; ++ /* Prior errors might have left pParse->pWith in a goofy state, so ++ ** go no further. */ ++ return 0; ++ } ++ assert( pFrom->fg.hadSchema==0 || pFrom->fg.notCte!=0 ); ++ if( pFrom->fg.fixedSchema==0 && pFrom->u4.zDatabase!=0 ){ ++ /* The FROM term contains a schema qualifier (ex: main.t1) and so ++ ** it cannot possibly be a CTE reference. */ ++ return 0; ++ } ++ if( pFrom->fg.notCte ){ ++ /* The FROM term is specifically excluded from matching a CTE. ++ ** (1) It is part of a trigger that used to have zDatabase but had ++ ** zDatabase removed by sqlite3FixTriggerStep(). ++ ** (2) This is the first term in the FROM clause of an UPDATE. ++ */ ++ return 0; + } +- + pCte = searchWith(pParse->pWith, pFrom, &pWith); + if( pCte ){ ++ sqlite3 *db = pParse->db; + Table *pTab; + ExprList *pEList; + Select *pSel; + Select *pLeft; /* Left-most SELECT statement */ ++ Select *pRecTerm; /* Left-most recursive term */ + int bMayRecursive; /* True if compound joined by UNION [ALL] */ + With *pSavedWith; /* Initial value of pParse->pWith */ ++ int iRecTab = -1; /* Cursor for recursive table */ ++ CteUse *pCteUse; + + /* If pCte->zCteErr is non-NULL at this point, then this is an illegal + ** recursive reference to CTE pCte. Leave an error in pParse and return +@@ -134103,63 +150649,100 @@ static int withExpand( + ** In this case, proceed. */ + if( pCte->zCteErr ){ + sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName); +- return SQLITE_ERROR; ++ return 2; + } +- if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR; ++ if( cannotBeFunction(pParse, pFrom) ) return 2; + +- assert( pFrom->pTab==0 ); +- pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); +- if( pTab==0 ) return WRC_Abort; ++ assert( pFrom->pSTab==0 ); ++ pTab = sqlite3DbMallocZero(db, sizeof(Table)); ++ if( pTab==0 ) return 2; ++ pCteUse = pCte->pUse; ++ if( pCteUse==0 ){ ++ pCte->pUse = pCteUse = sqlite3DbMallocZero(db, sizeof(pCteUse[0])); ++ if( pCteUse==0 ++ || sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCteUse)==0 ++ ){ ++ sqlite3DbFree(db, pTab); ++ return 2; ++ } ++ pCteUse->eM10d = pCte->eM10d; ++ } ++ pFrom->pSTab = pTab; + pTab->nTabRef = 1; + pTab->zName = sqlite3DbStrDup(db, pCte->zName); + pTab->iPKey = -1; + pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); + pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; +- pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); +- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; +- assert( pFrom->pSelect ); ++ sqlite3SrcItemAttachSubquery(pParse, pFrom, pCte->pSelect, 1); ++ if( db->mallocFailed ) return 2; ++ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); ++ pSel = pFrom->u4.pSubq->pSelect; ++ assert( pSel!=0 ); ++ pSel->selFlags |= SF_CopyCte; ++ if( pFrom->fg.isIndexedBy ){ ++ sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy); ++ return 2; ++ } ++ assert( !pFrom->fg.isIndexedBy ); ++ pFrom->fg.isCte = 1; ++ pFrom->u2.pCteUse = pCteUse; ++ pCteUse->nUse++; + + /* Check if this is a recursive CTE. */ +- pSel = pFrom->pSelect; ++ pRecTerm = pSel; + bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION ); +- if( bMayRecursive ){ ++ while( bMayRecursive && pRecTerm->op==pSel->op ){ + int i; +- SrcList *pSrc = pFrom->pSelect->pSrc; ++ SrcList *pSrc = pRecTerm->pSrc; ++ assert( pRecTerm->pPrior!=0 ); + for(i=0; inSrc; i++){ +- struct SrcList_item *pItem = &pSrc->a[i]; +- if( pItem->zDatabase==0 +- && pItem->zName!=0 ++ SrcItem *pItem = &pSrc->a[i]; ++ if( pItem->zName!=0 ++ && !pItem->fg.hadSchema ++ && ALWAYS( !pItem->fg.isSubquery ) ++ && (pItem->fg.fixedSchema || pItem->u4.zDatabase==0) + && 0==sqlite3StrICmp(pItem->zName, pCte->zName) +- ){ +- pItem->pTab = pTab; +- pItem->fg.isRecursive = 1; ++ ){ ++ pItem->pSTab = pTab; + pTab->nTabRef++; +- pSel->selFlags |= SF_Recursive; ++ pItem->fg.isRecursive = 1; ++ if( pRecTerm->selFlags & SF_Recursive ){ ++ sqlite3ErrorMsg(pParse, ++ "multiple references to recursive table: %s", pCte->zName ++ ); ++ return 2; ++ } ++ pRecTerm->selFlags |= SF_Recursive; ++ if( iRecTab<0 ) iRecTab = pParse->nTab++; ++ pItem->iCursor = iRecTab; + } + } ++ if( (pRecTerm->selFlags & SF_Recursive)==0 ) break; ++ pRecTerm = pRecTerm->pPrior; + } + +- /* Only one recursive reference is permitted. */ +- if( pTab->nTabRef>2 ){ +- sqlite3ErrorMsg( +- pParse, "multiple references to recursive table: %s", pCte->zName +- ); +- return SQLITE_ERROR; +- } +- assert( pTab->nTabRef==1 || +- ((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 )); +- + pCte->zCteErr = "circular reference: %s"; + pSavedWith = pParse->pWith; + pParse->pWith = pWith; +- if( bMayRecursive ){ +- Select *pPrior = pSel->pPrior; +- assert( pPrior->pWith==0 ); +- pPrior->pWith = pSel->pWith; +- sqlite3WalkSelect(pWalker, pPrior); +- pPrior->pWith = 0; ++ if( pSel->selFlags & SF_Recursive ){ ++ int rc; ++ assert( pRecTerm!=0 ); ++ assert( (pRecTerm->selFlags & SF_Recursive)==0 ); ++ assert( pRecTerm->pNext!=0 ); ++ assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 ); ++ assert( pRecTerm->pWith==0 ); ++ pRecTerm->pWith = pSel->pWith; ++ rc = sqlite3WalkSelect(pWalker, pRecTerm); ++ pRecTerm->pWith = 0; ++ if( rc ){ ++ pParse->pWith = pSavedWith; ++ return 2; ++ } + }else{ +- sqlite3WalkSelect(pWalker, pSel); ++ if( sqlite3WalkSelect(pWalker, pSel) ){ ++ pParse->pWith = pSavedWith; ++ return 2; ++ } + } + pParse->pWith = pWith; + +@@ -134171,7 +150754,7 @@ static int withExpand( + pCte->zName, pEList->nExpr, pCte->pCols->nExpr + ); + pParse->pWith = pSavedWith; +- return SQLITE_ERROR; ++ return 2; + } + pEList = pCte->pCols; + } +@@ -134187,9 +150770,9 @@ static int withExpand( + } + pCte->zCteErr = 0; + pParse->pWith = pSavedWith; ++ return 1; /* Success */ + } +- +- return SQLITE_OK; ++ return 0; /* No match */ + } + #endif + +@@ -134202,7 +150785,7 @@ static int withExpand( + ** sqlite3SelectExpand() when walking a SELECT tree to resolve table + ** names and other FROM clause elements. + */ +-static void selectPopWith(Walker *pWalker, Select *p){ ++SQLITE_PRIVATE void sqlite3SelectPopWith(Walker *pWalker, Select *p){ + Parse *pParse = pWalker->pParse; + if( OK_IF_ALWAYS_TRUE(pParse->pWith) && p->pPrior==0 ){ + With *pWith = findRightmost(p)->pWith; +@@ -134212,39 +150795,72 @@ static void selectPopWith(Walker *pWalker, Select *p){ + } + } + } +-#else +-#define selectPopWith 0 + #endif + + /* +-** The SrcList_item structure passed as the second argument represents a ++** The SrcItem structure passed as the second argument represents a + ** sub-query in the FROM clause of a SELECT statement. This function +-** allocates and populates the SrcList_item.pTab object. If successful, ++** allocates and populates the SrcItem.pTab object. If successful, + ** SQLITE_OK is returned. Otherwise, if an OOM error is encountered, + ** SQLITE_NOMEM. + */ +-SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){ +- Select *pSel = pFrom->pSelect; ++SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ ++ Select *pSel; + Table *pTab; + ++ assert( pFrom->fg.isSubquery ); ++ assert( pFrom->u4.pSubq!=0 ); ++ pSel = pFrom->u4.pSubq->pSelect; + assert( pSel ); +- pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); ++ pFrom->pSTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); + if( pTab==0 ) return SQLITE_NOMEM; + pTab->nTabRef = 1; + if( pFrom->zAlias ){ + pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias); + }else{ +- pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId); ++ pTab->zName = sqlite3MPrintf(pParse->db, "%!S", pFrom); + } + while( pSel->pPrior ){ pSel = pSel->pPrior; } + sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); + pTab->iPKey = -1; ++ pTab->eTabType = TABTYP_VIEW; + pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); +- pTab->tabFlags |= TF_Ephemeral; +- ++#ifndef SQLITE_ALLOW_ROWID_IN_VIEW ++ /* The usual case - do not allow ROWID on a subquery */ ++ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; ++#else ++ /* Legacy compatibility mode */ ++ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid; ++#endif + return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; + } + ++ ++/* ++** Check the N SrcItem objects to the right of pBase. (N might be zero!) ++** If any of those SrcItem objects have a USING clause containing zName ++** then return true. ++** ++** If N is zero, or none of the N SrcItem objects to the right of pBase ++** contains a USING clause, or if none of the USING clauses contain zName, ++** then return false. ++*/ ++static int inAnyUsingClause( ++ const char *zName, /* Name we are looking for */ ++ SrcItem *pBase, /* The base SrcItem. Looking at pBase[1] and following */ ++ int N /* How many SrcItems to check */ ++){ ++ while( N>0 ){ ++ N--; ++ pBase++; ++ if( pBase->fg.isUsing==0 ) continue; ++ if( NEVER(pBase->u3.pUsing==0) ) continue; ++ if( sqlite3IdListIndex(pBase->u3.pUsing, zName)>=0 ) return 1; ++ } ++ return 0; ++} ++ ++ + /* + ** This routine is a Walker callback for "expanding" a SELECT statement. + ** "Expanding" means to do the following: +@@ -134271,10 +150887,10 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFr + */ + static int selectExpander(Walker *pWalker, Select *p){ + Parse *pParse = pWalker->pParse; +- int i, j, k; ++ int i, j, k, rc; + SrcList *pTabList; + ExprList *pEList; +- struct SrcList_item *pFrom; ++ SrcItem *pFrom; + sqlite3 *db = pParse->db; + Expr *pE, *pRight, *pExpr; + u16 selFlags = p->selFlags; +@@ -134294,6 +150910,15 @@ static int selectExpander(Walker *pWalker, Select *p){ + } + pTabList = p->pSrc; + pEList = p->pEList; ++ if( pParse->pWith && (p->selFlags & SF_View) ){ ++ if( p->pWith==0 ){ ++ p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) ); ++ if( p->pWith==0 ){ ++ return WRC_Abort; ++ } ++ } ++ p->pWith->bView = 1; ++ } + sqlite3WithPush(pParse, p->pWith, 0); + + /* Make sure cursor numbers have been assigned to all entries in +@@ -134307,31 +150932,35 @@ static int selectExpander(Walker *pWalker, Select *p){ + */ + for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ + Table *pTab; +- assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 ); +- if( pFrom->pTab ) continue; ++ assert( pFrom->fg.isRecursive==0 || pFrom->pSTab!=0 ); ++ if( pFrom->pSTab ) continue; + assert( pFrom->fg.isRecursive==0 ); +-#ifndef SQLITE_OMIT_CTE +- if( withExpand(pWalker, pFrom) ) return WRC_Abort; +- if( pFrom->pTab ) {} else +-#endif + if( pFrom->zName==0 ){ + #ifndef SQLITE_OMIT_SUBQUERY +- Select *pSel = pFrom->pSelect; ++ Select *pSel; ++ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq!=0 ); ++ pSel = pFrom->u4.pSubq->pSelect; + /* A sub-query in the FROM clause of a SELECT */ + assert( pSel!=0 ); +- assert( pFrom->pTab==0 ); ++ assert( pFrom->pSTab==0 ); + if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; + if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort; ++#endif ++#ifndef SQLITE_OMIT_CTE ++ }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){ ++ if( rc>1 ) return WRC_Abort; ++ pTab = pFrom->pSTab; ++ assert( pTab!=0 ); + #endif + }else{ + /* An ordinary table or view name in the FROM clause */ +- assert( pFrom->pTab==0 ); +- pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); ++ assert( pFrom->pSTab==0 ); ++ pFrom->pSTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); + if( pTab==0 ) return WRC_Abort; + if( pTab->nTabRef>=0xffff ){ + sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535", + pTab->zName); +- pFrom->pTab = 0; ++ pFrom->pSTab = 0; + return WRC_Abort; + } + pTab->nTabRef++; +@@ -134339,30 +150968,37 @@ static int selectExpander(Walker *pWalker, Select *p){ + return WRC_Abort; + } + #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) +- if( IsVirtual(pTab) || pTab->pSelect ){ ++ if( !IsOrdinaryTable(pTab) ){ + i16 nCol; + u8 eCodeOrig = pWalker->eCode; + if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; +- assert( pFrom->pSelect==0 ); +- if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){ +- sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", +- pTab->zName); ++ assert( pFrom->fg.isSubquery==0 ); ++ if( IsView(pTab) ){ ++ if( (db->flags & SQLITE_EnableView)==0 ++ && pTab->pSchema!=db->aDb[1].pSchema ++ ){ ++ sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", ++ pTab->zName); ++ } ++ sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1); + } + #ifndef SQLITE_OMIT_VIRTUALTABLE +- if( IsVirtual(pTab) ++ else if( ALWAYS(IsVirtual(pTab)) + && pFrom->fg.fromDDL +- && ALWAYS(pTab->pVTable!=0) +- && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) ++ && ALWAYS(pTab->u.vtab.p!=0) ++ && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) + ){ + sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", + pTab->zName); + } ++ assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 ); + #endif +- pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0); + nCol = pTab->nCol; + pTab->nCol = -1; + pWalker->eCode = 1; /* Turn on Select.selId renumbering */ +- sqlite3WalkSelect(pWalker, pFrom->pSelect); ++ if( pFrom->fg.isSubquery ){ ++ sqlite3WalkSelect(pWalker, pFrom->u4.pSubq->pSelect); ++ } + pWalker->eCode = eCodeOrig; + pTab->nCol = nCol; + } +@@ -134370,14 +151006,15 @@ static int selectExpander(Walker *pWalker, Select *p){ + } + + /* Locate the index named by the INDEXED BY clause, if any. */ +- if( sqlite3IndexedByLookup(pParse, pFrom) ){ ++ if( pFrom->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pFrom) ){ + return WRC_Abort; + } + } + + /* Process NATURAL keywords, and ON and USING clauses of joins. + */ +- if( pParse->nErr || db->mallocFailed || sqliteProcessJoin(pParse, p) ){ ++ assert( db->mallocFailed==0 || pParse->nErr!=0 ); ++ if( pParse->nErr || sqlite3ProcessJoin(pParse, p) ){ + return WRC_Abort; + } + +@@ -134425,7 +151062,7 @@ static int selectExpander(Walker *pWalker, Select *p){ + pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); + if( pNew ){ + pNew->a[pNew->nExpr-1].zEName = a[k].zEName; +- pNew->a[pNew->nExpr-1].eEName = a[k].eEName; ++ pNew->a[pNew->nExpr-1].fg.eEName = a[k].fg.eEName; + a[k].zEName = 0; + } + a[k].pExpr = 0; +@@ -134434,102 +151071,177 @@ static int selectExpander(Walker *pWalker, Select *p){ + ** expanded. */ + int tableSeen = 0; /* Set to 1 when TABLE matches */ + char *zTName = 0; /* text of name of TABLE */ ++ int iErrOfst; + if( pE->op==TK_DOT ){ ++ assert( (selFlags & SF_NestedFrom)==0 ); + assert( pE->pLeft!=0 ); + assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); + zTName = pE->pLeft->u.zToken; ++ assert( ExprUseWOfst(pE->pLeft) ); ++ iErrOfst = pE->pRight->w.iOfst; ++ }else{ ++ assert( ExprUseWOfst(pE) ); ++ iErrOfst = pE->w.iOfst; + } + for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ +- Table *pTab = pFrom->pTab; +- Select *pSub = pFrom->pSelect; +- char *zTabName = pFrom->zAlias; +- const char *zSchemaName = 0; +- int iDb; +- if( zTabName==0 ){ ++ int nAdd; /* Number of cols including rowid */ ++ Table *pTab = pFrom->pSTab; /* Table for this data source */ ++ ExprList *pNestedFrom; /* Result-set of a nested FROM clause */ ++ char *zTabName; /* AS name for this data source */ ++ const char *zSchemaName = 0; /* Schema name for this data source */ ++ int iDb; /* Schema index for this data src */ ++ IdList *pUsing; /* USING clause for pFrom[1] */ ++ ++ if( (zTabName = pFrom->zAlias)==0 ){ + zTabName = pTab->zName; + } + if( db->mallocFailed ) break; +- if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ +- pSub = 0; ++ assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom) ); ++ if( pFrom->fg.isNestedFrom ){ ++ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); ++ assert( pFrom->u4.pSubq->pSelect!=0 ); ++ pNestedFrom = pFrom->u4.pSubq->pSelect->pEList; ++ assert( pNestedFrom!=0 ); ++ assert( pNestedFrom->nExpr==pTab->nCol ); ++ assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid ); ++ }else{ + if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ + continue; + } ++ pNestedFrom = 0; + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*"; + } +- for(j=0; jnCol; j++){ +- char *zName = pTab->aCol[j].zName; +- char *zColname; /* The computed column name */ +- char *zToFree; /* Malloced string that needs to be freed */ +- Token sColname; /* Computed column name as a token */ +- +- assert( zName ); +- if( zTName && pSub +- && sqlite3MatchEName(&pSub->pEList->a[j], 0, zTName, 0)==0 +- ){ +- continue; ++ if( i+1nSrc ++ && pFrom[1].fg.isUsing ++ && (selFlags & SF_NestedFrom)!=0 ++ ){ ++ int ii; ++ pUsing = pFrom[1].u3.pUsing; ++ for(ii=0; iinId; ii++){ ++ const char *zUName = pUsing->a[ii].zName; ++ pRight = sqlite3Expr(db, TK_ID, zUName); ++ sqlite3ExprSetErrorOffset(pRight, iErrOfst); ++ pNew = sqlite3ExprListAppend(pParse, pNew, pRight); ++ if( pNew ){ ++ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; ++ assert( pX->zEName==0 ); ++ pX->zEName = sqlite3MPrintf(db,"..%s", zUName); ++ pX->fg.eEName = ENAME_TAB; ++ pX->fg.bUsingTerm = 1; ++ } + } ++ }else{ ++ pUsing = 0; ++ } + +- /* If a column is marked as 'hidden', omit it from the expanded +- ** result-set list unless the SELECT has the SF_IncludeHidden +- ** bit set. +- */ +- if( (p->selFlags & SF_IncludeHidden)==0 +- && IsHiddenColumn(&pTab->aCol[j]) +- ){ +- continue; +- } +- tableSeen = 1; ++ nAdd = pTab->nCol; ++ if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++; ++ for(j=0; jnCol ){ ++ zName = sqlite3RowidAlias(pTab); ++ if( zName==0 ) continue; ++ }else{ ++ zName = pTab->aCol[j].zCnName; + +- if( i>0 && zTName==0 ){ +- if( (pFrom->fg.jointype & JT_NATURAL)!=0 +- && tableAndColumnIndex(pTabList, i, zName, 0, 0, 1) ++ /* If pTab is actually an SF_NestedFrom sub-select, do not ++ ** expand any ENAME_ROWID columns. */ ++ if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){ ++ continue; ++ } ++ ++ if( zTName ++ && pNestedFrom ++ && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0 ++ ){ ++ continue; ++ } ++ ++ /* If a column is marked as 'hidden', omit it from the expanded ++ ** result-set list unless the SELECT has the SF_IncludeHidden ++ ** bit set. ++ */ ++ if( (p->selFlags & SF_IncludeHidden)==0 ++ && IsHiddenColumn(&pTab->aCol[j]) ++ ){ ++ continue; ++ } ++ if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 ++ && zTName==0 ++ && (selFlags & (SF_NestedFrom))==0 + ){ +- /* In a NATURAL join, omit the join columns from the +- ** table to the right of the join */ + continue; + } +- if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ ++ } ++ assert( zName ); ++ tableSeen = 1; ++ ++ if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){ ++ if( pFrom->fg.isUsing ++ && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0 ++ ){ + /* In a join with a USING clause, omit columns in the + ** using clause from the table on the right. */ + continue; + } + } + pRight = sqlite3Expr(db, TK_ID, zName); +- zColname = zName; +- zToFree = 0; +- if( longNames || pTabList->nSrc>1 ){ ++ if( (pTabList->nSrc>1 ++ && ( (pFrom->fg.jointype & JT_LTORJ)==0 ++ || (selFlags & SF_NestedFrom)!=0 ++ || !inAnyUsingClause(zName,pFrom,pTabList->nSrc-i-1) ++ ) ++ ) ++ || IN_RENAME_OBJECT ++ ){ + Expr *pLeft; + pLeft = sqlite3Expr(db, TK_ID, zTabName); + pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); ++ if( IN_RENAME_OBJECT && pE->pLeft ){ ++ sqlite3RenameTokenRemap(pParse, pLeft, pE->pLeft); ++ } + if( zSchemaName ){ + pLeft = sqlite3Expr(db, TK_ID, zSchemaName); + pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr); + } +- if( longNames ){ +- zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); +- zToFree = zColname; +- } + }else{ + pExpr = pRight; + } ++ sqlite3ExprSetErrorOffset(pExpr, iErrOfst); + pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); +- sqlite3TokenInit(&sColname, zColname); +- sqlite3ExprListSetName(pParse, pNew, &sColname, 0); +- if( pNew && (p->selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ +- struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; +- sqlite3DbFree(db, pX->zEName); +- if( pSub ){ +- pX->zEName = sqlite3DbStrDup(db, pSub->pEList->a[j].zEName); ++ if( pNew==0 ){ ++ break; /* OOM */ ++ } ++ pX = &pNew->a[pNew->nExpr-1]; ++ assert( pX->zEName==0 ); ++ if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ ++ if( pNestedFrom && (!ViewCanHaveRowid || jnExpr) ){ ++ assert( jnExpr ); ++ pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName); + testcase( pX->zEName==0 ); + }else{ + pX->zEName = sqlite3MPrintf(db, "%s.%s.%s", +- zSchemaName, zTabName, zColname); ++ zSchemaName, zTabName, zName); + testcase( pX->zEName==0 ); + } +- pX->eEName = ENAME_TAB; ++ pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB); ++ if( (pFrom->fg.isUsing ++ && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0) ++ || (pUsing && sqlite3IdListIndex(pUsing, zName)>=0) ++ || (jnCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)) ++ ){ ++ pX->fg.bNoExpand = 1; ++ } ++ }else if( longNames ){ ++ pX->zEName = sqlite3MPrintf(db, "%s.%s", zTabName, zName); ++ pX->fg.eEName = ENAME_NAME; ++ }else{ ++ pX->zEName = sqlite3DbStrDup(db, zName); ++ pX->fg.eEName = ENAME_NAME; + } +- sqlite3DbFree(db, zToFree); + } + } + if( !tableSeen ){ +@@ -134553,6 +151265,12 @@ static int selectExpander(Walker *pWalker, Select *p){ + p->selFlags |= SF_ComplexResult; + } + } ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x8 ){ ++ TREETRACE(0x8,pParse,p,("After result-set wildcard expansion:\n")); ++ sqlite3TreeViewSelect(0, p, 0); ++ } ++#endif + return WRC_Continue; + } + +@@ -134589,7 +151307,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ + sqlite3WalkSelect(&w, pSelect); + } + w.xSelectCallback = selectExpander; +- w.xSelectCallback2 = selectPopWith; ++ w.xSelectCallback2 = sqlite3SelectPopWith; + w.eCode = 0; + sqlite3WalkSelect(&w, pSelect); + } +@@ -134600,37 +151318,33 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ + ** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo() + ** interface. + ** +-** For each FROM-clause subquery, add Column.zType and Column.zColl +-** information to the Table structure that represents the result set +-** of that subquery. ++** For each FROM-clause subquery, add Column.zType, Column.zColl, and ++** Column.affinity information to the Table structure that represents ++** the result set of that subquery. + ** + ** The Table structure that represents the result set was constructed +-** by selectExpander() but the type and collation information was omitted +-** at that point because identifiers had not yet been resolved. This +-** routine is called after identifier resolution. ++** by selectExpander() but the type and collation and affinity information ++** was omitted at that point because identifiers had not yet been resolved. ++** This routine is called after identifier resolution. + */ + static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ + Parse *pParse; + int i; + SrcList *pTabList; +- struct SrcList_item *pFrom; ++ SrcItem *pFrom; + +- assert( p->selFlags & SF_Resolved ); + if( p->selFlags & SF_HasTypeInfo ) return; + p->selFlags |= SF_HasTypeInfo; + pParse = pWalker->pParse; ++ assert( (p->selFlags & SF_Resolved) ); + pTabList = p->pSrc; + for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ +- Table *pTab = pFrom->pTab; ++ Table *pTab = pFrom->pSTab; + assert( pTab!=0 ); +- if( (pTab->tabFlags & TF_Ephemeral)!=0 ){ ++ if( (pTab->tabFlags & TF_Ephemeral)!=0 && pFrom->fg.isSubquery ){ + /* A sub-query in the FROM clause of a SELECT */ +- Select *pSel = pFrom->pSelect; +- if( pSel ){ +- while( pSel->pPrior ) pSel = pSel->pPrior; +- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel, +- SQLITE_AFF_NONE); +- } ++ Select *pSel = pFrom->u4.pSubq->pSelect; ++ sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE); + } + } + } +@@ -134674,15 +151388,196 @@ SQLITE_PRIVATE void sqlite3SelectPrep( + NameContext *pOuterNC /* Name context for container */ + ){ + assert( p!=0 || pParse->db->mallocFailed ); ++ assert( pParse->db->pParse==pParse ); + if( pParse->db->mallocFailed ) return; + if( p->selFlags & SF_HasTypeInfo ) return; + sqlite3SelectExpand(pParse, p); +- if( pParse->nErr || pParse->db->mallocFailed ) return; ++ if( pParse->nErr ) return; + sqlite3ResolveSelectNames(pParse, p, pOuterNC); +- if( pParse->nErr || pParse->db->mallocFailed ) return; ++ if( pParse->nErr ) return; + sqlite3SelectAddTypeInfo(pParse, p); + } + ++#if TREETRACE_ENABLED ++/* ++** Display all information about an AggInfo object ++*/ ++static void printAggInfo(AggInfo *pAggInfo){ ++ int ii; ++ sqlite3DebugPrintf("AggInfo %d/%p:\n", ++ pAggInfo->selId, pAggInfo); ++ for(ii=0; iinColumn; ii++){ ++ struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; ++ sqlite3DebugPrintf( ++ "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d" ++ " iSorterColumn=%d %s\n", ++ ii, pCol->pTab ? pCol->pTab->zName : "NULL", ++ pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii, ++ pCol->iSorterColumn, ++ ii>=pAggInfo->nAccumulator ? "" : " Accumulator"); ++ sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); ++ } ++ for(ii=0; iinFunc; ii++){ ++ sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", ++ ii, pAggInfo->iFirstReg+pAggInfo->nColumn+ii); ++ sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0); ++ } ++} ++#endif /* TREETRACE_ENABLED */ ++ ++/* ++** Analyze the arguments to aggregate functions. Create new pAggInfo->aCol[] ++** entries for columns that are arguments to aggregate functions but which ++** are not otherwise used. ++** ++** The aCol[] entries in AggInfo prior to nAccumulator are columns that ++** are referenced outside of aggregate functions. These might be columns ++** that are part of the GROUP by clause, for example. Other database engines ++** would throw an error if there is a column reference that is not in the ++** GROUP BY clause and that is not part of an aggregate function argument. ++** But SQLite allows this. ++** ++** The aCol[] entries beginning with the aCol[nAccumulator] and following ++** are column references that are used exclusively as arguments to ++** aggregate functions. This routine is responsible for computing ++** (or recomputing) those aCol[] entries. ++*/ ++static void analyzeAggFuncArgs( ++ AggInfo *pAggInfo, ++ NameContext *pNC ++){ ++ int i; ++ assert( pAggInfo!=0 ); ++ assert( pAggInfo->iFirstReg==0 ); ++ pNC->ncFlags |= NC_InAggFunc; ++ for(i=0; inFunc; i++){ ++ Expr *pExpr = pAggInfo->aFunc[i].pFExpr; ++ assert( pExpr->op==TK_FUNCTION || pExpr->op==TK_AGG_FUNCTION ); ++ assert( ExprUseXList(pExpr) ); ++ sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList); ++ if( pExpr->pLeft ){ ++ assert( pExpr->pLeft->op==TK_ORDER ); ++ assert( ExprUseXList(pExpr->pLeft) ); ++ sqlite3ExprAnalyzeAggList(pNC, pExpr->pLeft->x.pList); ++ } ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ assert( !IsWindowFunc(pExpr) ); ++ if( ExprHasProperty(pExpr, EP_WinFunc) ){ ++ sqlite3ExprAnalyzeAggregates(pNC, pExpr->y.pWin->pFilter); ++ } ++#endif ++ } ++ pNC->ncFlags &= ~NC_InAggFunc; ++} ++ ++/* ++** An index on expressions is being used in the inner loop of an ++** aggregate query with a GROUP BY clause. This routine attempts ++** to adjust the AggInfo object to take advantage of index and to ++** perhaps use the index as a covering index. ++** ++*/ ++static void optimizeAggregateUseOfIndexedExpr( ++ Parse *pParse, /* Parsing context */ ++ Select *pSelect, /* The SELECT statement being processed */ ++ AggInfo *pAggInfo, /* The aggregate info */ ++ NameContext *pNC /* Name context used to resolve agg-func args */ ++){ ++ assert( pAggInfo->iFirstReg==0 ); ++ assert( pSelect!=0 ); ++ assert( pSelect->pGroupBy!=0 ); ++ pAggInfo->nColumn = pAggInfo->nAccumulator; ++ if( ALWAYS(pAggInfo->nSortingColumn>0) ){ ++ int mx = pSelect->pGroupBy->nExpr - 1; ++ int j, k; ++ for(j=0; jnColumn; j++){ ++ k = pAggInfo->aCol[j].iSorterColumn; ++ if( k>mx ) mx = k; ++ } ++ pAggInfo->nSortingColumn = mx+1; ++ } ++ analyzeAggFuncArgs(pAggInfo, pNC); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x20 ){ ++ IndexedExpr *pIEpr; ++ TREETRACE(0x20, pParse, pSelect, ++ ("AggInfo (possibly) adjusted for Indexed Exprs\n")); ++ sqlite3TreeViewSelect(0, pSelect, 0); ++ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){ ++ printf("data-cursor=%d index={%d,%d}\n", ++ pIEpr->iDataCur, pIEpr->iIdxCur, pIEpr->iIdxCol); ++ sqlite3TreeViewExpr(0, pIEpr->pExpr, 0); ++ } ++ printAggInfo(pAggInfo); ++ } ++#else ++ UNUSED_PARAMETER(pSelect); ++ UNUSED_PARAMETER(pParse); ++#endif ++} ++ ++/* ++** Walker callback for aggregateConvertIndexedExprRefToColumn(). ++*/ ++static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){ ++ AggInfo *pAggInfo; ++ struct AggInfo_col *pCol; ++ UNUSED_PARAMETER(pWalker); ++ if( pExpr->pAggInfo==0 ) return WRC_Continue; ++ if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue; ++ if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue; ++ if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue; ++ pAggInfo = pExpr->pAggInfo; ++ if( NEVER(pExpr->iAgg>=pAggInfo->nColumn) ) return WRC_Continue; ++ assert( pExpr->iAgg>=0 ); ++ pCol = &pAggInfo->aCol[pExpr->iAgg]; ++ pExpr->op = TK_AGG_COLUMN; ++ pExpr->iTable = pCol->iTable; ++ pExpr->iColumn = pCol->iColumn; ++ ExprClearProperty(pExpr, EP_Skip|EP_Collate|EP_Unlikely); ++ return WRC_Prune; ++} ++ ++/* ++** Convert every pAggInfo->aFunc[].pExpr such that any node within ++** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN ++** opcode. ++*/ ++static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){ ++ int i; ++ Walker w; ++ memset(&w, 0, sizeof(w)); ++ w.xExprCallback = aggregateIdxEprRefToColCallback; ++ for(i=0; inFunc; i++){ ++ sqlite3WalkExpr(&w, pAggInfo->aFunc[i].pFExpr); ++ } ++} ++ ++ ++/* ++** Allocate a block of registers so that there is one register for each ++** pAggInfo->aCol[] and pAggInfo->aFunc[] entry in pAggInfo. The first ++** register in this block is stored in pAggInfo->iFirstReg. ++** ++** This routine may only be called once for each AggInfo object. Prior ++** to calling this routine: ++** ++** * The aCol[] and aFunc[] arrays may be modified ++** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used ++** ++** After calling this routine: ++** ++** * The aCol[] and aFunc[] arrays are fixed ++** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used ++** ++*/ ++static void assignAggregateRegisters(Parse *pParse, AggInfo *pAggInfo){ ++ assert( pAggInfo!=0 ); ++ assert( pAggInfo->iFirstReg==0 ); ++ pAggInfo->iFirstReg = pParse->nMem + 1; ++ pParse->nMem += pAggInfo->nColumn + pAggInfo->nFunc; ++} ++ + /* + ** Reset the aggregate accumulator. + ** +@@ -134696,35 +151591,58 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ + int i; + struct AggInfo_func *pFunc; + int nReg = pAggInfo->nFunc + pAggInfo->nColumn; ++ assert( pAggInfo->iFirstReg>0 ); ++ assert( pParse->db->pParse==pParse ); ++ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); + if( nReg==0 ) return; +- if( pParse->nErr || pParse->db->mallocFailed ) return; +-#ifdef SQLITE_DEBUG +- /* Verify that all AggInfo registers are within the range specified by +- ** AggInfo.mnReg..AggInfo.mxReg */ +- assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); +- for(i=0; inColumn; i++){ +- assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg +- && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); +- } +- for(i=0; inFunc; i++){ +- assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg +- && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); +- } +-#endif +- sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); ++ if( pParse->nErr ) return; ++ sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->iFirstReg, ++ pAggInfo->iFirstReg+nReg-1); + for(pFunc=pAggInfo->aFunc, i=0; inFunc; i++, pFunc++){ + if( pFunc->iDistinct>=0 ){ + Expr *pE = pFunc->pFExpr; +- assert( !ExprHasProperty(pE, EP_xIsSelect) ); ++ assert( ExprUseXList(pE) ); + if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ + sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " + "argument"); + pFunc->iDistinct = -1; + }else{ + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0); +- sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, +- (char*)pKeyInfo, P4_KEYINFO); ++ pFunc->iDistAddr = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, ++ pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO); ++ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(DISTINCT)", ++ pFunc->pFunc->zName)); ++ } ++ } ++ if( pFunc->iOBTab>=0 ){ ++ ExprList *pOBList; ++ KeyInfo *pKeyInfo; ++ int nExtra = 0; ++ assert( pFunc->pFExpr->pLeft!=0 ); ++ assert( pFunc->pFExpr->pLeft->op==TK_ORDER ); ++ assert( ExprUseXList(pFunc->pFExpr->pLeft) ); ++ assert( pFunc->pFunc!=0 ); ++ pOBList = pFunc->pFExpr->pLeft->x.pList; ++ if( !pFunc->bOBUnique ){ ++ nExtra++; /* One extra column for the OP_Sequence */ ++ } ++ if( pFunc->bOBPayload ){ ++ /* extra columns for the function arguments */ ++ assert( ExprUseXList(pFunc->pFExpr) ); ++ nExtra += pFunc->pFExpr->x.pList->nExpr; ++ } ++ if( pFunc->bUseSubtype ){ ++ nExtra += pFunc->pFExpr->x.pList->nExpr; ++ } ++ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra); ++ if( !pFunc->bOBUnique && pParse->nErr==0 ){ ++ pKeyInfo->nKeyField++; + } ++ sqlite3VdbeAddOp4(v, OP_OpenEphemeral, ++ pFunc->iOBTab, pOBList->nExpr+nExtra, 0, ++ (char*)pKeyInfo, P4_KEYINFO); ++ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)", ++ pFunc->pFunc->zName)); + } + } + } +@@ -134738,24 +151656,82 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ + int i; + struct AggInfo_func *pF; + for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ +- ExprList *pList = pF->pFExpr->x.pList; +- assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); +- sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); ++ ExprList *pList; ++ assert( ExprUseXList(pF->pFExpr) ); ++ if( pParse->nErr ) return; ++ pList = pF->pFExpr->x.pList; ++ if( pF->iOBTab>=0 ){ ++ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs ++ ** were stored in emphermal table pF->iOBTab. Here, we extract those ++ ** inputs (in ORDER BY order) and make all calls to OP_AggStep ++ ** before doing the OP_AggFinal call. */ ++ int iTop; /* Start of loop for extracting columns */ ++ int nArg; /* Number of columns to extract */ ++ int nKey; /* Key columns to be skipped */ ++ int regAgg; /* Extract into this array */ ++ int j; /* Loop counter */ ++ ++ assert( pF->pFunc!=0 ); ++ nArg = pList->nExpr; ++ regAgg = sqlite3GetTempRange(pParse, nArg); ++ ++ if( pF->bOBPayload==0 ){ ++ nKey = 0; ++ }else{ ++ assert( pF->pFExpr->pLeft!=0 ); ++ assert( ExprUseXList(pF->pFExpr->pLeft) ); ++ assert( pF->pFExpr->pLeft->x.pList!=0 ); ++ nKey = pF->pFExpr->pLeft->x.pList->nExpr; ++ if( ALWAYS(!pF->bOBUnique) ) nKey++; ++ } ++ iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v); ++ for(j=nArg-1; j>=0; j--){ ++ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j); ++ } ++ if( pF->bUseSubtype ){ ++ int regSubtype = sqlite3GetTempReg(pParse); ++ int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0); ++ for(j=nArg-1; j>=0; j--){ ++ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype); ++ sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j); ++ } ++ sqlite3ReleaseTempReg(pParse, regSubtype); ++ } ++ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); ++ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); ++ sqlite3VdbeChangeP5(v, (u16)nArg); ++ sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); ++ sqlite3VdbeJumpHere(v, iTop); ++ sqlite3ReleaseTempRange(pParse, regAgg, nArg); ++ } ++ sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), ++ pList ? pList->nExpr : 0); + sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); + } + } + +- + /* +-** Update the accumulator memory cells for an aggregate based on +-** the current cursor position. ++** Generate code that will update the accumulator memory cells for an ++** aggregate based on the current cursor position. + ** + ** If regAcc is non-zero and there are no min() or max() aggregates + ** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator + ** registers if register regAcc contains 0. The caller will take care + ** of setting and clearing regAcc. ++** ++** For an ORDER BY aggregate, the actual accumulator memory cell update ++** is deferred until after all input rows have been received, so that they ++** can be run in the requested order. In that case, instead of invoking ++** OP_AggStep to update the accumulator, just add the arguments that would ++** have been passed into OP_AggStep into the sorting ephemeral table ++** (along with the appropriate sort key). + */ +-static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ ++static void updateAccumulator( ++ Parse *pParse, ++ int regAcc, ++ AggInfo *pAggInfo, ++ int eDistinctType ++){ + Vdbe *v = pParse->pVdbe; + int i; + int regHit = 0; +@@ -134763,14 +151739,20 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ + struct AggInfo_func *pF; + struct AggInfo_col *pC; + ++ assert( pAggInfo->iFirstReg>0 ); ++ if( pParse->nErr ) return; + pAggInfo->directMode = 1; + for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ + int nArg; + int addrNext = 0; + int regAgg; +- ExprList *pList = pF->pFExpr->x.pList; +- assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); ++ int regAggSz = 0; ++ int regDistinct = 0; ++ ExprList *pList; ++ assert( ExprUseXList(pF->pFExpr) ); + assert( !IsWindowFunc(pF->pFExpr) ); ++ assert( pF->pFunc!=0 ); ++ pList = pF->pFExpr->x.pList; + if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ + Expr *pFilter = pF->pFExpr->y.pWin->pFilter; + if( pAggInfo->nAccumulator +@@ -134793,43 +151775,100 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ + addrNext = sqlite3VdbeMakeLabel(pParse); + sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL); + } +- if( pList ){ ++ if( pF->iOBTab>=0 ){ ++ /* Instead of invoking AggStep, we must push the arguments that would ++ ** have been passed to AggStep onto the sorting table. */ ++ int jj; /* Registered used so far in building the record */ ++ ExprList *pOBList; /* The ORDER BY clause */ ++ assert( pList!=0 ); ++ nArg = pList->nExpr; ++ assert( nArg>0 ); ++ assert( pF->pFExpr->pLeft!=0 ); ++ assert( pF->pFExpr->pLeft->op==TK_ORDER ); ++ assert( ExprUseXList(pF->pFExpr->pLeft) ); ++ pOBList = pF->pFExpr->pLeft->x.pList; ++ assert( pOBList!=0 ); ++ assert( pOBList->nExpr>0 ); ++ regAggSz = pOBList->nExpr; ++ if( !pF->bOBUnique ){ ++ regAggSz++; /* One register for OP_Sequence */ ++ } ++ if( pF->bOBPayload ){ ++ regAggSz += nArg; ++ } ++ if( pF->bUseSubtype ){ ++ regAggSz += nArg; ++ } ++ regAggSz++; /* One extra register to hold result of MakeRecord */ ++ regAgg = sqlite3GetTempRange(pParse, regAggSz); ++ regDistinct = regAgg; ++ sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP); ++ jj = pOBList->nExpr; ++ if( !pF->bOBUnique ){ ++ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj); ++ jj++; ++ } ++ if( pF->bOBPayload ){ ++ regDistinct = regAgg+jj; ++ sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP); ++ jj += nArg; ++ } ++ if( pF->bUseSubtype ){ ++ int kk; ++ int regBase = pF->bOBPayload ? regDistinct : regAgg; ++ for(kk=0; kknExpr; + regAgg = sqlite3GetTempRange(pParse, nArg); ++ regDistinct = regAgg; + sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP); + }else{ + nArg = 0; + regAgg = 0; + } +- if( pF->iDistinct>=0 ){ ++ if( pF->iDistinct>=0 && pList ){ + if( addrNext==0 ){ + addrNext = sqlite3VdbeMakeLabel(pParse); + } +- testcase( nArg==0 ); /* Error condition */ +- testcase( nArg>1 ); /* Also an error */ +- codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); ++ pF->iDistinct = codeDistinct(pParse, eDistinctType, ++ pF->iDistinct, addrNext, pList, regDistinct); + } +- if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ +- CollSeq *pColl = 0; +- struct ExprList_item *pItem; +- int j; +- assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */ +- for(j=0, pItem=pList->a; !pColl && jpExpr); +- } +- if( !pColl ){ +- pColl = pParse->db->pDfltColl; ++ if( pF->iOBTab>=0 ){ ++ /* Insert a new record into the ORDER BY table */ ++ sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1, ++ regAgg+regAggSz-1); ++ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1, ++ regAgg, regAggSz-1); ++ sqlite3ReleaseTempRange(pParse, regAgg, regAggSz); ++ }else{ ++ /* Invoke the AggStep function */ ++ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ ++ CollSeq *pColl = 0; ++ struct ExprList_item *pItem; ++ int j; ++ assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */ ++ for(j=0, pItem=pList->a; !pColl && jpExpr); ++ } ++ if( !pColl ){ ++ pColl = pParse->db->pDfltColl; ++ } ++ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; ++ sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, ++ (char *)pColl, P4_COLLSEQ); + } +- if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; +- sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ); ++ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); ++ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); ++ sqlite3VdbeChangeP5(v, (u16)nArg); ++ sqlite3ReleaseTempRange(pParse, regAgg, nArg); + } +- sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem); +- sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); +- sqlite3VdbeChangeP5(v, (u8)nArg); +- sqlite3ReleaseTempRange(pParse, regAgg, nArg); + if( addrNext ){ + sqlite3VdbeResolveLabel(v, addrNext); + } ++ if( pParse->nErr ) return; + } + if( regHit==0 && pAggInfo->nAccumulator ){ + regHit = regAcc; +@@ -134838,7 +151877,8 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ + addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v); + } + for(i=0, pC=pAggInfo->aCol; inAccumulator; i++, pC++){ +- sqlite3ExprCode(pParse, pC->pCExpr, pC->iMem); ++ sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i)); ++ if( pParse->nErr ) return; + } + + pAggInfo->directMode = 0; +@@ -134859,7 +151899,7 @@ static void explainSimpleCount( + ){ + if( pParse->explain==2 ){ + int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx))); +- sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s", ++ sqlite3VdbeExplain(pParse, 0, "SCAN %s%s%s", + pTab->zName, + bCover ? " USING COVERING INDEX " : "", + bCover ? pIdx->zName : "" +@@ -134884,7 +151924,17 @@ static void explainSimpleCount( + static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op!=TK_AND ){ + Select *pS = pWalker->u.pSelect; +- if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){ ++ /* This routine is called before the HAVING clause of the current ++ ** SELECT is analyzed for aggregates. So if pExpr->pAggInfo is set ++ ** here, it indicates that the expression is a correlated reference to a ++ ** column from an outer aggregate query, or an aggregate function that ++ ** belongs to an outer query. Do not move the expression to the WHERE ++ ** clause in this obscure case, as doing so may corrupt the outer Select ++ ** statements AggInfo structure. */ ++ if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ++ && ExprAlwaysFalse(pExpr)==0 ++ && pExpr->pAggInfo==0 ++ ){ + sqlite3 *db = pWalker->pParse->db; + Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1"); + if( pNew ){ +@@ -134922,42 +151972,50 @@ static void havingToWhere(Parse *pParse, Select *p){ + sWalker.xExprCallback = havingToWhereExprCb; + sWalker.u.pSelect = p; + sqlite3WalkExpr(&sWalker, p->pHaving); +-#if SELECTTRACE_ENABLED +- if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){ +- SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); ++#if TREETRACE_ENABLED ++ if( sWalker.eCode && (sqlite3TreeTrace & 0x100)!=0 ){ ++ TREETRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif + } + + /* +-** Check to see if the pThis entry of pTabList is a self-join of a prior view. +-** If it is, then return the SrcList_item for the prior view. If it is not, +-** then return 0. ++** Check to see if the pThis entry of pTabList is a self-join of another view. ++** Search FROM-clause entries in the range of iFirst..iEnd, including iFirst ++** but stopping before iEnd. ++** ++** If pThis is a self-join, then return the SrcItem for the first other ++** instance of that view found. If pThis is not a self-join then return 0. + */ +-static struct SrcList_item *isSelfJoinView( ++static SrcItem *isSelfJoinView( + SrcList *pTabList, /* Search for self-joins in this FROM clause */ +- struct SrcList_item *pThis /* Search for prior reference to this subquery */ ++ SrcItem *pThis, /* Search for prior reference to this subquery */ ++ int iFirst, int iEnd /* Range of FROM-clause entries to search. */ + ){ +- struct SrcList_item *pItem; +- for(pItem = pTabList->a; pItemfg.isSubquery ); ++ pSel = pThis->u4.pSubq->pSelect; ++ assert( pSel!=0 ); ++ if( pSel->selFlags & SF_PushDown ) return 0; ++ while( iFirstpSelect==0 ) continue; ++ pItem = &pTabList->a[iFirst++]; ++ if( !pItem->fg.isSubquery ) continue; + if( pItem->fg.viaCoroutine ) continue; + if( pItem->zName==0 ) continue; +- assert( pItem->pTab!=0 ); +- assert( pThis->pTab!=0 ); +- if( pItem->pTab->pSchema!=pThis->pTab->pSchema ) continue; ++ assert( pItem->pSTab!=0 ); ++ assert( pThis->pSTab!=0 ); ++ if( pItem->pSTab->pSchema!=pThis->pSTab->pSchema ) continue; + if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; +- pS1 = pItem->pSelect; +- if( pItem->pTab->pSchema==0 && pThis->pSelect->selId!=pS1->selId ){ ++ pS1 = pItem->u4.pSubq->pSelect; ++ if( pItem->pSTab->pSchema==0 && pSel->selId!=pS1->selId ){ + /* The query flattener left two different CTE tables with identical + ** names in the same FROM clause. */ + continue; + } +- if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1) +- || sqlite3ExprCompare(0, pThis->pSelect->pHaving, pS1->pHaving, -1) +- ){ ++ if( pS1->selFlags & SF_PushDown ){ + /* The view was modified by some other optimization such as + ** pushDownWhereTerms() */ + continue; +@@ -134967,7 +152025,16 @@ static struct SrcList_item *isSelfJoinView( + return 0; + } + +-#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION ++/* ++** Deallocate a single AggInfo object ++*/ ++static void agginfoFree(sqlite3 *db, void *pArg){ ++ AggInfo *p = (AggInfo*)pArg; ++ sqlite3DbFree(db, p->aCol); ++ sqlite3DbFree(db, p->aFunc); ++ sqlite3DbFreeNN(db, p); ++} ++ + /* + ** Attempt to transform a query of the form + ** +@@ -134984,6 +152051,7 @@ static struct SrcList_item *isSelfJoinView( + ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries + ** * The outer query is a simple count(*) with no WHERE clause or other + ** extraneous syntax. ++** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10) + ** + ** Return TRUE if the optimization is undertaken. + */ +@@ -134992,23 +152060,36 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ + Expr *pExpr; + Expr *pCount; + sqlite3 *db; ++ SrcItem *pFrom; + if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */ + if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ + if( p->pWhere ) return 0; ++ if( p->pHaving ) return 0; + if( p->pGroupBy ) return 0; ++ if( p->pOrderBy ) return 0; + pExpr = p->pEList->a[0].pExpr; + if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */ ++ assert( ExprUseUToken(pExpr) ); + if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */ ++ assert( ExprUseXList(pExpr) ); + if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */ + if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */ +- pSub = p->pSrc->a[0].pSelect; +- if( pSub==0 ) return 0; /* The FROM is a subquery */ +- if( pSub->pPrior==0 ) return 0; /* Must be a compound ry */ ++ if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */ ++ pFrom = p->pSrc->a; ++ if( pFrom->fg.isSubquery==0 ) return 0; /* FROM is a subquery */ ++ pSub = pFrom->u4.pSubq->pSelect; ++ if( pSub->pPrior==0 ) return 0; /* Must be a compound */ ++ if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ + do{ + if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ + if( pSub->pWhere ) return 0; /* No WHERE clause */ + if( pSub->pLimit ) return 0; /* No LIMIT clause */ +- if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ ++ if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){ ++ testcase( pSub->selFlags & SF_Aggregate ); ++ testcase( pSub->selFlags & SF_Distinct ); ++ return 0; /* Not an aggregate nor DISTINCT */ ++ } ++ assert( pSub->pHaving==0 ); /* Due to the previous */ + pSub = pSub->pPrior; /* Repeat over compound */ + }while( pSub ); + +@@ -135017,19 +152098,18 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ + db = pParse->db; + pCount = pExpr; + pExpr = 0; +- pSub = p->pSrc->a[0].pSelect; +- p->pSrc->a[0].pSelect = 0; ++ pSub = sqlite3SubqueryDetach(db, pFrom); + sqlite3SrcListDelete(db, p->pSrc); +- p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); ++ p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); + while( pSub ){ + Expr *pTerm; + pPrior = pSub->pPrior; + pSub->pPrior = 0; + pSub->pNext = 0; + pSub->selFlags |= SF_Aggregate; +- pSub->selFlags &= ~SF_Compound; ++ pSub->selFlags &= ~(u32)SF_Compound; + pSub->nSelectRow = 0; +- sqlite3ExprListDelete(db, pSub->pEList); ++ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList); + pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount; + pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm); + pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0); +@@ -135042,20 +152122,104 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ + pSub = pPrior; + } + p->pEList->a[0].pExpr = pExpr; +- p->selFlags &= ~SF_Aggregate; ++ p->selFlags &= ~(u32)SF_Aggregate; + +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x400 ){ +- SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x200 ){ ++ TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif + return 1; + } +-#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ + + /* +-** Generate code for the SELECT statement given in the p argument. ++** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same ++** as pSrcItem but has the same alias as p0, then return true. ++** Otherwise return false. ++*/ ++static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ ++ int i; ++ for(i=0; inSrc; i++){ ++ SrcItem *p1 = &pSrc->a[i]; ++ if( p1==p0 ) continue; ++ if( p0->pSTab==p1->pSTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ ++ return 1; ++ } ++ if( p1->fg.isSubquery ++ && (p1->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 ++ && sameSrcAlias(p0, p1->u4.pSubq->pSelect->pSrc) ++ ){ ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++/* ++** Return TRUE (non-zero) if the i-th entry in the pTabList SrcList can ++** be implemented as a co-routine. The i-th entry is guaranteed to be ++** a subquery. ++** ++** The subquery is implemented as a co-routine if all of the following are ++** true: ++** ++** (1) The subquery will likely be implemented in the outer loop of ++** the query. This will be the case if any one of the following ++** conditions hold: ++** (a) The subquery is the only term in the FROM clause ++** (b) The subquery is the left-most term and a CROSS JOIN or similar ++** requires it to be the outer loop ++** (c) All of the following are true: ++** (i) The subquery is the left-most subquery in the FROM clause ++** (ii) There is nothing that would prevent the subquery from ++** being used as the outer loop if the sqlite3WhereBegin() ++** routine nominates it to that position. ++** (iii) The query is not a UPDATE ... FROM ++** (2) The subquery is not a CTE that should be materialized because ++** (a) the AS MATERIALIZED keyword is used, or ++** (b) the CTE is used multiple times and does not have the ++** NOT MATERIALIZED keyword ++** (3) The subquery is not part of a left operand for a RIGHT JOIN ++** (4) The SQLITE_Coroutine optimization disable flag is not set ++** (5) The subquery is not self-joined ++*/ ++static int fromClauseTermCanBeCoroutine( ++ Parse *pParse, /* Parsing context */ ++ SrcList *pTabList, /* FROM clause */ ++ int i, /* Which term of the FROM clause holds the subquery */ ++ int selFlags /* Flags on the SELECT statement */ ++){ ++ SrcItem *pItem = &pTabList->a[i]; ++ if( pItem->fg.isCte ){ ++ const CteUse *pCteUse = pItem->u2.pCteUse; ++ if( pCteUse->eM10d==M10d_Yes ) return 0; /* (2a) */ ++ if( pCteUse->nUse>=2 && pCteUse->eM10d!=M10d_No ) return 0; /* (2b) */ ++ } ++ if( pTabList->a[0].fg.jointype & JT_LTORJ ) return 0; /* (3) */ ++ if( OptimizationDisabled(pParse->db, SQLITE_Coroutines) ) return 0; /* (4) */ ++ if( isSelfJoinView(pTabList, pItem, i+1, pTabList->nSrc)!=0 ){ ++ return 0; /* (5) */ ++ } ++ if( i==0 ){ ++ if( pTabList->nSrc==1 ) return 1; /* (1a) */ ++ if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */ ++ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ ++ return 1; ++ } ++ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ ++ while( 1 /*exit-by-break*/ ){ ++ if( pItem->fg.jointype & (JT_OUTER|JT_CROSS) ) return 0; /* (1c-ii) */ ++ if( i==0 ) break; ++ i--; ++ pItem--; ++ if( pItem->fg.isSubquery ) return 0; /* (1c-i) */ ++ } ++ return 1; ++} ++ ++/* ++** Generate byte-code for the SELECT statement given in the p argument. + ** + ** The results are returned according to the SelectDest structure. + ** See comments in sqliteInt.h for further information. +@@ -135066,6 +152230,40 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ + ** + ** This routine does NOT free the Select structure passed in. The + ** calling function needs to do that. ++** ++** This is a long function. The following is an outline of the processing ++** steps, with tags referencing various milestones: ++** ++** * Resolve names and similar preparation tag-select-0100 ++** * Scan of the FROM clause tag-select-0200 ++** + OUTER JOIN strength reduction tag-select-0220 ++** + Sub-query ORDER BY removal tag-select-0230 ++** + Query flattening tag-select-0240 ++** * Separate subroutine for compound-SELECT tag-select-0300 ++** * WHERE-clause constant propagation tag-select-0330 ++** * Count()-of-VIEW optimization tag-select-0350 ++** * Scan of the FROM clause again tag-select-0400 ++** + Authorize unreferenced tables tag-select-0410 ++** + Predicate push-down optimization tag-select-0420 ++** + Omit unused subquery columns optimization tag-select-0440 ++** + Generate code to implement subqueries tag-select-0480 ++** - Co-routines tag-select-0482 ++** - Reuse previously computed CTE tag-select-0484 ++** - REuse previously computed VIEW tag-select-0486 ++** - Materialize a VIEW or CTE tag-select-0488 ++** * DISTINCT ORDER BY -> GROUP BY optimization tag-select-0500 ++** * Set up for ORDER BY tag-select-0600 ++** * Create output table tag-select-0630 ++** * Prepare registers for LIMIT tag-select-0650 ++** * Setup for DISTINCT tag-select-0680 ++** * Generate code for non-aggregate and non-GROUP BY tag-select-0700 ++** * Generate code for aggregate and/or GROUP BY tag-select-0800 ++** + GROUP BY queries tag-select-0810 ++** + non-GROUP BY queries tag-select-0820 ++** - Special case of count() w/o GROUP BY tag-select-0821 ++** - General case of non-GROUP BY aggregates tag-select-0822 ++** * Sort results, as needed tag-select-0900 ++** * Internal self-checks tag-select-1000 + */ + SQLITE_PRIVATE int sqlite3Select( + Parse *pParse, /* The parser context */ +@@ -135091,77 +152289,100 @@ SQLITE_PRIVATE int sqlite3Select( + u8 minMaxFlag; /* Flag for min/max queries */ + + db = pParse->db; ++ assert( pParse==db->pParse ); + v = sqlite3GetVdbe(pParse); +- if( p==0 || db->mallocFailed || pParse->nErr ){ ++ if( p==0 || pParse->nErr ){ + return 1; + } ++ assert( db->mallocFailed==0 ); + if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; +-#if SELECTTRACE_ENABLED +- SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); +- if( sqlite3_unsupported_selecttrace & 0x100 ){ +- sqlite3TreeViewSelect(0, p, 0); ++#if TREETRACE_ENABLED ++ TREETRACE(0x1,pParse,p, ("begin processing:\n", pParse->addrExplain)); ++ if( sqlite3TreeTrace & 0x10000 ){ ++ if( (sqlite3TreeTrace & 0x10001)==0x10000 ){ ++ sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d", ++ __FILE__, __LINE__); ++ } ++ sqlite3ShowSelect(p); + } + #endif + ++ /* tag-select-0100 */ + assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); + assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); + assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); + assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue ); +- if( IgnorableOrderby(pDest) ){ +- assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || +- pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || +- pDest->eDest==SRT_Queue || pDest->eDest==SRT_DistFifo || +- pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_Fifo); +- /* If ORDER BY makes no difference in the output then neither does +- ** DISTINCT so it can be removed too. */ +- sqlite3ExprListDelete(db, p->pOrderBy); +- p->pOrderBy = 0; +- p->selFlags &= ~SF_Distinct; ++ if( IgnorableDistinct(pDest) ){ ++ assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || ++ pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || ++ pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); ++ /* All of these destinations are also able to ignore the ORDER BY clause */ ++ if( p->pOrderBy ){ ++#if TREETRACE_ENABLED ++ TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); ++ if( sqlite3TreeTrace & 0x800 ){ ++ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); ++ } ++#endif ++ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, ++ p->pOrderBy); ++ testcase( pParse->earlyCleanup ); ++ p->pOrderBy = 0; ++ } ++ p->selFlags &= ~(u32)SF_Distinct; + p->selFlags |= SF_NoopOrderBy; + } + sqlite3SelectPrep(pParse, p, 0); +- if( pParse->nErr || db->mallocFailed ){ ++ if( pParse->nErr ){ + goto select_end; + } ++ assert( db->mallocFailed==0 ); + assert( p->pEList!=0 ); +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x104 ){ +- SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x10 ){ ++ TREETRACE(0x10,pParse,p, ("after name resolution:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif + +- /* If the SF_UpdateFrom flag is set, then this function is being called ++ /* If the SF_UFSrcCheck flag is set, then this function is being called + ** as part of populating the temp table for an UPDATE...FROM statement. + ** In this case, it is an error if the target object (pSrc->a[0]) name +- ** or alias is duplicated within FROM clause (pSrc->a[1..n]). */ +- if( p->selFlags & SF_UpdateFrom ){ +- struct SrcList_item *p0 = &p->pSrc->a[0]; +- for(i=1; ipSrc->nSrc; i++){ +- struct SrcList_item *p1 = &p->pSrc->a[i]; +- if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ +- sqlite3ErrorMsg(pParse, +- "target object/alias may not appear in FROM clause: %s", +- p0->zAlias ? p0->zAlias : p0->pTab->zName +- ); +- goto select_end; +- } ++ ** or alias is duplicated within FROM clause (pSrc->a[1..n]). ++ ** ++ ** Postgres disallows this case too. The reason is that some other ++ ** systems handle this case differently, and not all the same way, ++ ** which is just confusing. To avoid this, we follow PG's lead and ++ ** disallow it altogether. */ ++ if( p->selFlags & SF_UFSrcCheck ){ ++ SrcItem *p0 = &p->pSrc->a[0]; ++ if( sameSrcAlias(p0, p->pSrc) ){ ++ sqlite3ErrorMsg(pParse, ++ "target object/alias may not appear in FROM clause: %s", ++ p0->zAlias ? p0->zAlias : p0->pSTab->zName ++ ); ++ goto select_end; + } ++ ++ /* Clear the SF_UFSrcCheck flag. The check has already been performed, ++ ** and leaving this flag set can cause errors if a compound sub-query ++ ** in p->pSrc is flattened into this query and this function called ++ ** again as part of compound SELECT processing. */ ++ p->selFlags &= ~(u32)SF_UFSrcCheck; + } + + if( pDest->eDest==SRT_Output ){ +- generateColumnNames(pParse, p); ++ sqlite3GenerateColumnNames(pParse, p); + } + + #ifndef SQLITE_OMIT_WINDOWFUNC +- rc = sqlite3WindowRewrite(pParse, p); +- if( rc ){ +- assert( db->mallocFailed || pParse->nErr>0 ); ++ if( sqlite3WindowRewrite(pParse, p) ){ ++ assert( pParse->nErr ); + goto select_end; + } +-#if SELECTTRACE_ENABLED +- if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){ +- SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); ++#if TREETRACE_ENABLED ++ if( p->pWin && (sqlite3TreeTrace & 0x40)!=0 ){ ++ TREETRACE(0x40,pParse,p, ("after window rewrite:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif +@@ -135173,32 +152394,72 @@ SQLITE_PRIVATE int sqlite3Select( + + /* Try to do various optimizations (flattening subqueries, and strength + ** reduction of join operators) in the FROM clause up into the main query ++ ** tag-select-0200 + */ + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) + for(i=0; !p->pPrior && inSrc; i++){ +- struct SrcList_item *pItem = &pTabList->a[i]; +- Select *pSub = pItem->pSelect; +- Table *pTab = pItem->pTab; ++ SrcItem *pItem = &pTabList->a[i]; ++ Select *pSub = pItem->fg.isSubquery ? pItem->u4.pSubq->pSelect : 0; ++ Table *pTab = pItem->pSTab; + + /* The expander should have already created transient Table objects + ** even for FROM clause elements such as subqueries that do not correspond + ** to a real table */ + assert( pTab!=0 ); + +- /* Convert LEFT JOIN into JOIN if there are terms of the right table +- ** of the LEFT JOIN used in the WHERE clause. ++ /* Try to simplify joins: ++ ** ++ ** LEFT JOIN -> JOIN ++ ** RIGHT JOIN -> JOIN ++ ** FULL JOIN -> RIGHT JOIN ++ ** ++ ** If terms of the i-th table are used in the WHERE clause in such a ++ ** way that the i-th table cannot be the NULL row of a join, then ++ ** perform the appropriate simplification. This is called ++ ** "OUTER JOIN strength reduction" in the SQLite documentation. ++ ** tag-select-0220 + */ +- if( (pItem->fg.jointype & JT_LEFT)!=0 +- && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor) ++ if( (pItem->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ++ && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor, ++ pItem->fg.jointype & JT_LTORJ) + && OptimizationEnabled(db, SQLITE_SimplifyJoin) + ){ +- SELECTTRACE(0x100,pParse,p, +- ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); +- pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); +- unsetJoinExpr(p->pWhere, pItem->iCursor); ++ if( pItem->fg.jointype & JT_LEFT ){ ++ if( pItem->fg.jointype & JT_RIGHT ){ ++ TREETRACE(0x1000,pParse,p, ++ ("FULL-JOIN simplifies to RIGHT-JOIN on term %d\n",i)); ++ pItem->fg.jointype &= ~JT_LEFT; ++ }else{ ++ TREETRACE(0x1000,pParse,p, ++ ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); ++ pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); ++ unsetJoinExpr(p->pWhere, pItem->iCursor, 0); ++ } ++ } ++ if( pItem->fg.jointype & JT_LTORJ ){ ++ for(j=i+1; jnSrc; j++){ ++ SrcItem *pI2 = &pTabList->a[j]; ++ if( pI2->fg.jointype & JT_RIGHT ){ ++ if( pI2->fg.jointype & JT_LEFT ){ ++ TREETRACE(0x1000,pParse,p, ++ ("FULL-JOIN simplifies to LEFT-JOIN on term %d\n",j)); ++ pI2->fg.jointype &= ~JT_RIGHT; ++ }else{ ++ TREETRACE(0x1000,pParse,p, ++ ("RIGHT-JOIN simplifies to JOIN on term %d\n",j)); ++ pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER); ++ unsetJoinExpr(p->pWhere, pI2->iCursor, 1); ++ } ++ } ++ } ++ for(j=pTabList->nSrc-1; j>=0; j--){ ++ pTabList->a[j].fg.jointype &= ~JT_LTORJ; ++ if( pTabList->a[j].fg.jointype & JT_RIGHT ) break; ++ } ++ } + } + +- /* No futher action if this term of the FROM clause is no a subquery */ ++ /* No further action if this term of the FROM clause is not a subquery */ + if( pSub==0 ) continue; + + /* Catch mismatch in the declared columns of a view and the number of +@@ -135209,6 +152470,14 @@ SQLITE_PRIVATE int sqlite3Select( + goto select_end; + } + ++ /* Do not attempt the usual optimizations (flattening and ORDER BY ++ ** elimination) on a MATERIALIZED common table expression because ++ ** a MATERIALIZED common table expression is an optimization fence. ++ */ ++ if( pItem->fg.isCte && pItem->u2.pCteUse->eM10d==M10d_Yes ){ ++ continue; ++ } ++ + /* Do not try to flatten an aggregate subquery. + ** + ** Flattening an aggregate subquery is only possible if the outer query +@@ -135219,6 +152488,46 @@ SQLITE_PRIVATE int sqlite3Select( + if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; + assert( pSub->pGroupBy==0 ); + ++ /* tag-select-0230: ++ ** If a FROM-clause subquery has an ORDER BY clause that is not ++ ** really doing anything, then delete it now so that it does not ++ ** interfere with query flattening. See the discussion at ++ ** https://sqlite.org/forum/forumpost/2d76f2bcf65d256a ++ ** ++ ** Beware of these cases where the ORDER BY clause may not be safely ++ ** omitted: ++ ** ++ ** (1) There is also a LIMIT clause ++ ** (2) The subquery was added to help with window-function ++ ** processing ++ ** (3) The subquery is in the FROM clause of an UPDATE ++ ** (4) The outer query uses an aggregate function other than ++ ** the built-in count(), min(), or max(). ++ ** (5) The ORDER BY isn't going to accomplish anything because ++ ** one of: ++ ** (a) The outer query has a different ORDER BY clause ++ ** (b) The subquery is part of a join ++ ** See forum post 062d576715d277c8 ++ ** (6) The subquery is not a recursive CTE. ORDER BY has a different ++ ** meaning for recursive CTEs and this optimization does not ++ ** apply. ++ ** ++ ** Also retain the ORDER BY if the OmitOrderBy optimization is disabled. ++ */ ++ if( pSub->pOrderBy!=0 ++ && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */ ++ && pSub->pLimit==0 /* Condition (1) */ ++ && (pSub->selFlags & (SF_OrderByReqd|SF_Recursive))==0 /* (2) and (6) */ ++ && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ ++ && OptimizationEnabled(db, SQLITE_OmitOrderBy) ++ ){ ++ TREETRACE(0x800,pParse,p, ++ ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); ++ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, ++ pSub->pOrderBy); ++ pSub->pOrderBy = 0; ++ } ++ + /* If the outer query contains a "complex" result set (that is, + ** if the result set of the outer query uses functions or subqueries) + ** and if the subquery contains an ORDER BY clause and if +@@ -135241,11 +152550,12 @@ SQLITE_PRIVATE int sqlite3Select( + && i==0 + && (p->selFlags & SF_ComplexResult)!=0 + && (pTabList->nSrc==1 +- || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) ++ || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) + ){ + continue; + } + ++ /* tag-select-0240 */ + if( flattenSubquery(pParse, p, i, isAgg) ){ + if( pParse->nErr ) goto select_end; + /* This subquery can be absorbed into its parent. */ +@@ -135261,13 +152571,13 @@ SQLITE_PRIVATE int sqlite3Select( + + #ifndef SQLITE_OMIT_COMPOUND_SELECT + /* Handle compound SELECT statements using the separate multiSelect() +- ** procedure. ++ ** procedure. tag-select-0300 + */ + if( p->pPrior ){ + rc = multiSelect(pParse, p, pDest); +-#if SELECTTRACE_ENABLED +- SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); +- if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ ++#if TREETRACE_ENABLED ++ TREETRACE(0x400,pParse,p,("end compound-select processing\n")); ++ if( (sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){ + sqlite3TreeViewSelect(0, p, 0); + } + #endif +@@ -135277,47 +152587,53 @@ SQLITE_PRIVATE int sqlite3Select( + #endif + + /* Do the WHERE-clause constant propagation optimization if this is +- ** a join. No need to speed time on this operation for non-join queries ++ ** a join. No need to spend time on this operation for non-join queries + ** as the equivalent optimization will be handled by query planner in +- ** sqlite3WhereBegin(). ++ ** sqlite3WhereBegin(). tag-select-0330 + */ +- if( pTabList->nSrc>1 ++ if( p->pWhere!=0 ++ && p->pWhere->op==TK_AND + && OptimizationEnabled(db, SQLITE_PropagateConst) + && propagateConstants(pParse, p) + ){ +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x100 ){ +- SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x2000 ){ ++ TREETRACE(0x2000,pParse,p,("After constant propagation:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif + }else{ +- SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n")); ++ TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n")); + } + +-#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION ++ /* tag-select-0350 */ + if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView) + && countOfViewOptimization(pParse, p) + ){ + if( db->mallocFailed ) goto select_end; +- pEList = p->pEList; + pTabList = p->pSrc; + } +-#endif + +- /* For each term in the FROM clause, do two things: +- ** (1) Authorized unreferenced tables +- ** (2) Generate code for all sub-queries ++ /* Loop over all terms in the FROM clause and do two things for each term: ++ ** ++ ** (1) Authorize unreferenced tables ++ ** (2) Generate code for all sub-queries ++ ** ++ ** tag-select-0400 + */ + for(i=0; inSrc; i++){ +- struct SrcList_item *pItem = &pTabList->a[i]; ++ SrcItem *pItem = &pTabList->a[i]; ++ SrcItem *pPrior; + SelectDest dest; ++ Subquery *pSubq; + Select *pSub; + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) + const char *zSavedAuthContext; + #endif + +- /* Issue SQLITE_READ authorizations with a fake column name for any ++ /* Authorized unreferenced tables. tag-select-0410 ++ ** ++ ** Issue SQLITE_READ authorizations with a fake column name for any + ** tables that are referenced but from which no values are extracted. + ** Examples of where these kinds of null SQLITE_READ authorizations + ** would occur: +@@ -135334,28 +152650,28 @@ SQLITE_PRIVATE int sqlite3Select( + ** string for the fake column name seems safer. + */ + if( pItem->colUsed==0 && pItem->zName!=0 ){ +- sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase); ++ const char *zDb; ++ if( pItem->fg.fixedSchema ){ ++ int iDb = sqlite3SchemaToIndex(pParse->db, pItem->u4.pSchema); ++ zDb = db->aDb[iDb].zDbSName; ++ }else if( pItem->fg.isSubquery ){ ++ zDb = 0; ++ }else{ ++ zDb = pItem->u4.zDatabase; ++ } ++ sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", zDb); + } + + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) + /* Generate code for all sub-queries in the FROM clause + */ +- pSub = pItem->pSelect; +- if( pSub==0 ) continue; ++ if( pItem->fg.isSubquery==0 ) continue; ++ pSubq = pItem->u4.pSubq; ++ assert( pSubq!=0 ); ++ pSub = pSubq->pSelect; + +- /* The code for a subquery should only be generated once, though it is +- ** technically harmless for it to be generated multiple times. The +- ** following assert() will detect if something changes to cause +- ** the same subquery to be coded multiple times, as a signal to the +- ** developers to try to optimize the situation. +- ** +- ** Update 2019-07-24: +- ** See ticket https://sqlite.org/src/tktview/c52b09c7f38903b1311cec40. +- ** The dbsqlfuzz fuzzer found a case where the same subquery gets +- ** coded twice. So this assert() now becomes a testcase(). It should +- ** be very rare, though. +- */ +- testcase( pItem->addrFillSub!=0 ); ++ /* The code for a subquery should only be generated once. */ ++ if( pSubq->addrFillSub!=0 ) continue; + + /* Increment Parse.nHeight by the height of the largest expression + ** tree referred to by this, the parent select. The child select +@@ -135368,96 +152684,133 @@ SQLITE_PRIVATE int sqlite3Select( + + /* Make copies of constant WHERE-clause terms in the outer query down + ** inside the subquery. This can help the subquery to run more efficiently. ++ ** This is the "predicate push-down optimization". tag-select-0420 + */ + if( OptimizationEnabled(db, SQLITE_PushDown) +- && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, +- (pItem->fg.jointype & JT_OUTER)!=0) ++ && (pItem->fg.isCte==0 ++ || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) ++ && pushDownWhereTerms(pParse, pSub, p->pWhere, pTabList, i) + ){ +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x100 ){ +- SELECTTRACE(0x100,pParse,p, ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x4000 ){ ++ TREETRACE(0x4000,pParse,p, + ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); + sqlite3TreeViewSelect(0, p, 0); + } + #endif ++ assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); + }else{ +- SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); ++ TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n")); ++ } ++ ++ /* Convert unused result columns of the subquery into simple NULL ++ ** expressions, to avoid unneeded searching and computation. ++ ** tag-select-0440 ++ */ ++ if( OptimizationEnabled(db, SQLITE_NullUnusedCols) ++ && disableUnusedSubqueryResultColumns(pItem) ++ ){ ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x4000 ){ ++ TREETRACE(0x4000,pParse,p, ++ ("Change unused result columns to NULL for subquery %d:\n", ++ pSub->selId)); ++ sqlite3TreeViewSelect(0, p, 0); ++ } ++#endif + } + + zSavedAuthContext = pParse->zAuthContext; + pParse->zAuthContext = pItem->zName; + +- /* Generate code to implement the subquery +- ** +- ** The subquery is implemented as a co-routine if the subquery is +- ** guaranteed to be the outer loop (so that it does not need to be +- ** computed more than once) +- ** +- ** TODO: Are there other reasons beside (1) to use a co-routine +- ** implementation? ++ /* Generate byte-code to implement the subquery tag-select-0480 + */ +- if( i==0 +- && (pTabList->nSrc==1 +- || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */ +- ){ ++ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){ + /* Implement a co-routine that will return a single row of the result +- ** set on each invocation. ++ ** set on each invocation. tag-select-0482 + */ + int addrTop = sqlite3VdbeCurrentAddr(v)+1; + +- pItem->regReturn = ++pParse->nMem; +- sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); +- VdbeComment((v, "%s", pItem->pTab->zName)); +- pItem->addrFillSub = addrTop; +- sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); +- ExplainQueryPlan((pParse, 1, "CO-ROUTINE %u", pSub->selId)); ++ pSubq->regReturn = ++pParse->nMem; ++ sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, addrTop); ++ VdbeComment((v, "%!S", pItem)); ++ pSubq->addrFillSub = addrTop; ++ sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); ++ ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); + sqlite3Select(pParse, pSub, &dest); +- pItem->pTab->nRowLogEst = pSub->nSelectRow; ++ pItem->pSTab->nRowLogEst = pSub->nSelectRow; + pItem->fg.viaCoroutine = 1; +- pItem->regResult = dest.iSdst; +- sqlite3VdbeEndCoroutine(v, pItem->regReturn); ++ pSubq->regResult = dest.iSdst; ++ sqlite3VdbeEndCoroutine(v, pSubq->regReturn); ++ VdbeComment((v, "end %!S", pItem)); + sqlite3VdbeJumpHere(v, addrTop-1); + sqlite3ClearTempRegCache(pParse); ++ }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){ ++ /* This is a CTE for which materialization code has already been ++ ** generated. Invoke the subroutine to compute the materialization, ++ ** then make the pItem->iCursor be a copy of the ephemeral table that ++ ** holds the result of the materialization. tag-select-0484 */ ++ CteUse *pCteUse = pItem->u2.pCteUse; ++ sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); ++ if( pItem->iCursor!=pCteUse->iCur ){ ++ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur); ++ VdbeComment((v, "%!S", pItem)); ++ } ++ pSub->nSelectRow = pCteUse->nRowEst; ++ }else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){ ++ /* This view has already been materialized by a prior entry in ++ ** this same FROM clause. Reuse it. tag-select-0486 */ ++ Subquery *pPriorSubq; ++ assert( pPrior->fg.isSubquery ); ++ pPriorSubq = pPrior->u4.pSubq; ++ assert( pPriorSubq!=0 ); ++ if( pPriorSubq->addrFillSub ){ ++ sqlite3VdbeAddOp2(v, OP_Gosub, pPriorSubq->regReturn, ++ pPriorSubq->addrFillSub); ++ } ++ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); ++ pSub->nSelectRow = pPriorSubq->pSelect->nSelectRow; + }else{ +- /* Generate a subroutine that will fill an ephemeral table with +- ** the content of this subquery. pItem->addrFillSub will point +- ** to the address of the generated subroutine. pItem->regReturn +- ** is a register allocated to hold the subroutine return address +- */ ++ /* Materialize the view. If the view is not correlated, generate a ++ ** subroutine to do the materialization so that subsequent uses of ++ ** the same view can reuse the materialization. tag-select-0488 */ + int topAddr; + int onceAddr = 0; +- int retAddr; +- struct SrcList_item *pPrior; ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ int addrExplain; ++#endif + +- testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ +- pItem->regReturn = ++pParse->nMem; +- topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); +- pItem->addrFillSub = topAddr+1; ++ pSubq->regReturn = ++pParse->nMem; ++ topAddr = sqlite3VdbeAddOp0(v, OP_Goto); ++ pSubq->addrFillSub = topAddr+1; ++ pItem->fg.isMaterialized = 1; + if( pItem->fg.isCorrelated==0 ){ + /* If the subquery is not correlated and if we are not inside of + ** a trigger, then we only need to compute the value of the subquery + ** once. */ + onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); +- VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); ++ VdbeComment((v, "materialize %!S", pItem)); + }else{ +- VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); ++ VdbeNoopComment((v, "materialize %!S", pItem)); + } +- pPrior = isSelfJoinView(pTabList, pItem); +- if( pPrior ){ +- sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); +- assert( pPrior->pSelect!=0 ); +- pSub->nSelectRow = pPrior->pSelect->nSelectRow; +- }else{ +- sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); +- ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId)); +- sqlite3Select(pParse, pSub, &dest); +- } +- pItem->pTab->nRowLogEst = pSub->nSelectRow; ++ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); ++ ++ ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); ++ sqlite3Select(pParse, pSub, &dest); ++ pItem->pSTab->nRowLogEst = pSub->nSelectRow; + if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); +- retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); +- VdbeComment((v, "end %s", pItem->pTab->zName)); +- sqlite3VdbeChangeP1(v, topAddr, retAddr); ++ sqlite3VdbeAddOp2(v, OP_Return, pSubq->regReturn, topAddr+1); ++ VdbeComment((v, "end %!S", pItem)); ++ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); ++ sqlite3VdbeJumpHere(v, topAddr); + sqlite3ClearTempRegCache(pParse); ++ if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ ++ CteUse *pCteUse = pItem->u2.pCteUse; ++ pCteUse->addrM9e = pSubq->addrFillSub; ++ pCteUse->regRtn = pSubq->regReturn; ++ pCteUse->iCur = pItem->iCursor; ++ pCteUse->nRowEst = pSub->nSelectRow; ++ } + } + if( db->mallocFailed ) goto select_end; + pParse->nHeight -= sqlite3SelectExprHeight(p); +@@ -135473,14 +152826,16 @@ SQLITE_PRIVATE int sqlite3Select( + pHaving = p->pHaving; + sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; + +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x400 ){ +- SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x8000 ){ ++ TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif + +- /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and ++ /* tag-select-0500 ++ ** ++ ** If the query is DISTINCT with an ORDER BY but is not an aggregate, and + ** if the select-list is the same as the ORDER BY list, then this query + ** can be rewritten as a GROUP BY. In other words, this: + ** +@@ -135497,21 +152852,28 @@ SQLITE_PRIVATE int sqlite3Select( + */ + if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct + && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 ++ && OptimizationEnabled(db, SQLITE_GroupByOrder) + #ifndef SQLITE_OMIT_WINDOWFUNC + && p->pWin==0 + #endif + ){ +- p->selFlags &= ~SF_Distinct; ++ p->selFlags &= ~(u32)SF_Distinct; + pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); ++ if( pGroupBy ){ ++ for(i=0; inExpr; i++){ ++ pGroupBy->a[i].u.x.iOrderByCol = i+1; ++ } ++ } + p->selFlags |= SF_Aggregate; + /* Notice that even thought SF_Distinct has been cleared from p->selFlags, + ** the sDistinct.isTnct is still set. Hence, isTnct represents the + ** original setting of the SF_Distinct flag, not the current setting */ + assert( sDistinct.isTnct ); ++ sDistinct.isTnct = 2; + +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x400 ){ +- SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x20000 ){ ++ TREETRACE(0x20000,pParse,p,("Transform DISTINCT into GROUP BY:\n")); + sqlite3TreeViewSelect(0, p, 0); + } + #endif +@@ -135523,7 +152885,7 @@ SQLITE_PRIVATE int sqlite3Select( + ** If that is the case, then the OP_OpenEphemeral instruction will be + ** changed to an OP_Noop once we figure out that the sorting index is + ** not needed. The sSort.addrSortIndex variable is used to facilitate +- ** that change. ++ ** that change. tag-select-0600 + */ + if( sSort.pOrderBy ){ + KeyInfo *pKeyInfo; +@@ -135540,24 +152902,37 @@ SQLITE_PRIVATE int sqlite3Select( + } + + /* If the output is destined for a temporary table, open that table. ++ ** tag-select-0630 + */ + if( pDest->eDest==SRT_EphemTab ){ + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); ++ if( p->selFlags & SF_NestedFrom ){ ++ /* Delete or NULL-out result columns that will never be used */ ++ int ii; ++ for(ii=pEList->nExpr-1; ii>0 && pEList->a[ii].fg.bUsed==0; ii--){ ++ sqlite3ExprDelete(db, pEList->a[ii].pExpr); ++ sqlite3DbFree(db, pEList->a[ii].zEName); ++ pEList->nExpr--; ++ } ++ for(ii=0; iinExpr; ii++){ ++ if( pEList->a[ii].fg.bUsed==0 ) pEList->a[ii].pExpr->op = TK_NULL; ++ } ++ } + } + +- /* Set the limiter. ++ /* Set the limiter. tag-select-0650 + */ + iEnd = sqlite3VdbeMakeLabel(pParse); + if( (p->selFlags & SF_FixedLimit)==0 ){ + p->nSelectRow = 320; /* 4 billion rows */ + } +- computeLimitRegisters(pParse, p, iEnd); ++ if( p->pLimit ) computeLimitRegisters(pParse, p, iEnd); + if( p->iLimit==0 && sSort.addrSortIndex>=0 ){ + sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen); + sSort.sortFlags |= SORTFLAG_UseSorter; + } + +- /* Open an ephemeral index to use for the distinct set. ++ /* Open an ephemeral index to use for the distinct set. tag-select-0680 + */ + if( p->selFlags & SF_Distinct ){ + sDistinct.tabTnct = pParse->nTab++; +@@ -135572,7 +152947,7 @@ SQLITE_PRIVATE int sqlite3Select( + } + + if( !isAgg && pGroupBy==0 ){ +- /* No aggregate functions and no GROUP BY clause */ ++ /* No aggregate functions and no GROUP BY clause. tag-select-0700 */ + u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0) + | (p->selFlags & SF_FixedLimit); + #ifndef SQLITE_OMIT_WINDOWFUNC +@@ -135585,12 +152960,18 @@ SQLITE_PRIVATE int sqlite3Select( + + + /* Begin the database scan. */ +- SELECTTRACE(1,pParse,p,("WhereBegin\n")); ++ TREETRACE(0x2,pParse,p,("WhereBegin\n")); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, +- p->pEList, wctrlFlags, p->nSelectRow); ++ p->pEList, p, wctrlFlags, p->nSelectRow); + if( pWInfo==0 ) goto select_end; + if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ + p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo); ++ if( pDest->eDest<=SRT_DistQueue && pDest->eDest>=SRT_DistFifo ){ ++ /* TUNING: For a UNION CTE, because UNION is implies DISTINCT, ++ ** reduce the estimated output row count by 8 (LogEst 30). ++ ** Search for tag-20250414a to see other cases */ ++ p->nSelectRow -= 30; ++ } + } + if( sDistinct.isTnct && sqlite3WhereIsDistinct(pWInfo) ){ + sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo); +@@ -135602,6 +152983,7 @@ SQLITE_PRIVATE int sqlite3Select( + sSort.pOrderBy = 0; + } + } ++ TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); + + /* If sorting index that was created by a prior OP_OpenEphemeral + ** instruction ended up not being needed, then change the OP_OpenEphemeral +@@ -135640,11 +153022,12 @@ SQLITE_PRIVATE int sqlite3Select( + + /* End the database scan loop. + */ ++ TREETRACE(0x2,pParse,p,("WhereEnd\n")); + sqlite3WhereEnd(pWInfo); + } + }else{ +- /* This case when there exist aggregate functions or a GROUP BY clause +- ** or both */ ++ /* This case is for when there exist aggregate functions or a GROUP BY ++ ** clause or both. tag-select-0800 */ + NameContext sNC; /* Name context for processing aggregate information */ + int iAMem; /* First Mem address for storing current GROUP BY */ + int iBMem; /* First Mem address for previous GROUP BY */ +@@ -135690,8 +153073,9 @@ SQLITE_PRIVATE int sqlite3Select( + ** ORDER BY to maximize the chances of rows being delivered in an + ** order that makes the ORDER BY redundant. */ + for(ii=0; iinExpr; ii++){ +- u8 sortFlags = sSort.pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_DESC; +- pGroupBy->a[ii].sortFlags = sortFlags; ++ u8 sortFlags; ++ sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; ++ pGroupBy->a[ii].fg.sortFlags = sortFlags; + } + if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ + orderByGrp = 1; +@@ -135710,18 +153094,22 @@ SQLITE_PRIVATE int sqlite3Select( + ** SELECT statement. + */ + pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); +- if( pAggInfo==0 ){ ++ if( pAggInfo ){ ++ sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo); ++ testcase( pParse->earlyCleanup ); ++ } ++ if( db->mallocFailed ){ + goto select_end; + } +- pAggInfo->pNext = pParse->pAggList; +- pParse->pAggList = pAggInfo; + pAggInfo->selId = p->selId; ++#ifdef SQLITE_DEBUG ++ pAggInfo->pSelect = p; ++#endif + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + sNC.pSrcList = pTabList; + sNC.uNC.pAggInfo = pAggInfo; + VVA_ONLY( sNC.ncFlags = NC_UAggInfo; ) +- pAggInfo->mnReg = pParse->nMem+1; + pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; + pAggInfo->pGroupBy = pGroupBy; + sqlite3ExprAnalyzeAggList(&sNC, pEList); +@@ -135742,46 +153130,27 @@ SQLITE_PRIVATE int sqlite3Select( + }else{ + minMaxFlag = WHERE_ORDERBY_NORMAL; + } +- for(i=0; inFunc; i++){ +- Expr *pExpr = pAggInfo->aFunc[i].pFExpr; +- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); +- sNC.ncFlags |= NC_InAggFunc; +- sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList); +-#ifndef SQLITE_OMIT_WINDOWFUNC +- assert( !IsWindowFunc(pExpr) ); +- if( ExprHasProperty(pExpr, EP_WinFunc) ){ +- sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter); +- } +-#endif +- sNC.ncFlags &= ~NC_InAggFunc; +- } +- pAggInfo->mxReg = pParse->nMem; ++ analyzeAggFuncArgs(pAggInfo, &sNC); + if( db->mallocFailed ) goto select_end; +-#if SELECTTRACE_ENABLED +- if( sqlite3_unsupported_selecttrace & 0x400 ){ +- int ii; +- SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x20 ){ ++ TREETRACE(0x20,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); + sqlite3TreeViewSelect(0, p, 0); +- for(ii=0; iinColumn; ii++){ +- sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", +- ii, pAggInfo->aCol[ii].iMem); +- sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); +- } +- for(ii=0; iinFunc; ii++){ +- sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", +- ii, pAggInfo->aFunc[ii].iMem); +- sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0); ++ if( minMaxFlag ){ ++ sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag); ++ sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY"); + } ++ printAggInfo(pAggInfo); + } + #endif + + + /* Processing for aggregates with GROUP BY is very different and +- ** much more complex than aggregates without a GROUP BY. ++ ** much more complex than aggregates without a GROUP BY. tag-select-0810 + */ + if( pGroupBy ){ + KeyInfo *pKeyInfo; /* Keying information for the group by clause */ +- int addr1; /* A-vs-B comparision jump */ ++ int addr1; /* A-vs-B comparison jump */ + int addrOutputRow; /* Start of subroutine that outputs a result row */ + int regOutputRow; /* Return address register for output subroutine */ + int addrSetAbort; /* Set the abort flag and return */ +@@ -135789,6 +153158,22 @@ SQLITE_PRIVATE int sqlite3Select( + int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */ + int addrReset; /* Subroutine for resetting the accumulator */ + int regReset; /* Return address register for reset subroutine */ ++ ExprList *pDistinct = 0; ++ u16 distFlag = 0; ++ int eDist = WHERE_DISTINCT_NOOP; ++ ++ if( pAggInfo->nFunc==1 ++ && pAggInfo->aFunc[0].iDistinct>=0 ++ && ALWAYS(pAggInfo->aFunc[0].pFExpr!=0) ++ && ALWAYS(ExprUseXList(pAggInfo->aFunc[0].pFExpr)) ++ && pAggInfo->aFunc[0].pFExpr->x.pList!=0 ++ ){ ++ Expr *pExpr = pAggInfo->aFunc[0].pFExpr->x.pList->a[0].pExpr; ++ pExpr = sqlite3ExprDup(db, pExpr, 0); ++ pDistinct = sqlite3ExprListDup(db, pGroupBy, 0); ++ pDistinct = sqlite3ExprListAppend(pParse, pDistinct, pExpr); ++ distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; ++ } + + /* If there is a GROUP BY clause we might need a sorting index to + ** implement it. Allocate that sorting index now. If it turns out +@@ -135817,6 +153202,7 @@ SQLITE_PRIVATE int sqlite3Select( + sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag); + VdbeComment((v, "clear abort flag")); + sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1); ++ sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr); + + /* Begin a loop that will extract all source rows in GROUP BY order. + ** This might involve two separate loops with an OP_Sort in between, or +@@ -135824,11 +153210,21 @@ SQLITE_PRIVATE int sqlite3Select( + ** in the right order to begin with. + */ + sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); +- SELECTTRACE(1,pParse,p,("WhereBegin\n")); +- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, +- WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 ++ TREETRACE(0x2,pParse,p,("WhereBegin\n")); ++ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct, ++ p, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY) ++ | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0 + ); +- if( pWInfo==0 ) goto select_end; ++ if( pWInfo==0 ){ ++ sqlite3ExprListDelete(db, pDistinct); ++ goto select_end; ++ } ++ if( pParse->pIdxEpr ){ ++ optimizeAggregateUseOfIndexedExpr(pParse, p, pAggInfo, &sNC); ++ } ++ assignAggregateRegisters(pParse, pAggInfo); ++ eDist = sqlite3WhereIsDistinct(pWInfo); ++ TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); + if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){ + /* The optimizer is able to deliver rows in group by order so + ** we do not have to sort. The OP_OpenEphemeral table will be +@@ -135846,9 +153242,13 @@ SQLITE_PRIVATE int sqlite3Select( + int nCol; + int nGroupBy; + +- explainTempTable(pParse, ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ int addrExp; /* Address of OP_Explain instruction */ ++#endif ++ ExplainQueryPlan2(addrExp, (pParse, 0, "USE TEMP B-TREE FOR %s", + (sDistinct.isTnct && (p->selFlags&SF_Distinct)==0) ? +- "DISTINCT" : "GROUP BY"); ++ "DISTINCT" : "GROUP BY" ++ )); + + groupBySort = 1; + nGroupBy = pGroupBy->nExpr; +@@ -135863,27 +153263,50 @@ SQLITE_PRIVATE int sqlite3Select( + regBase = sqlite3GetTempRange(pParse, nCol); + sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0); + j = nGroupBy; ++ pAggInfo->directMode = 1; + for(i=0; inColumn; i++){ + struct AggInfo_col *pCol = &pAggInfo->aCol[i]; + if( pCol->iSorterColumn>=j ){ +- int r1 = j + regBase; +- sqlite3ExprCodeGetColumnOfTable(v, +- pCol->pTab, pCol->iTable, pCol->iColumn, r1); ++ sqlite3ExprCode(pParse, pCol->pCExpr, j + regBase); + j++; + } + } ++ pAggInfo->directMode = 0; + regRecord = sqlite3GetTempReg(pParse); ++ sqlite3VdbeScanStatusCounters(v, addrExp, 0, sqlite3VdbeCurrentAddr(v)); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord); + sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); ++ sqlite3VdbeScanStatusRange(v, addrExp, sqlite3VdbeCurrentAddr(v)-2, -1); + sqlite3ReleaseTempReg(pParse, regRecord); + sqlite3ReleaseTempRange(pParse, regBase, nCol); ++ TREETRACE(0x2,pParse,p,("WhereEnd\n")); + sqlite3WhereEnd(pWInfo); + pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; + sortOut = sqlite3GetTempReg(pParse); ++ sqlite3VdbeScanStatusCounters(v, addrExp, sqlite3VdbeCurrentAddr(v), 0); + sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol); + sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd); + VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); + pAggInfo->useSortingIdx = 1; ++ sqlite3VdbeScanStatusRange(v, addrExp, -1, sortPTab); ++ sqlite3VdbeScanStatusRange(v, addrExp, -1, pAggInfo->sortingIdx); ++ } ++ ++ /* If there are entries in pAgggInfo->aFunc[] that contain subexpressions ++ ** that are indexed (and that were previously identified and tagged ++ ** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions ++ ** must now be converted into a TK_AGG_COLUMN node so that the value ++ ** is correctly pulled from the index rather than being recomputed. */ ++ if( pParse->pIdxEpr ){ ++ aggregateConvertIndexedExprRefToColumn(pAggInfo); ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x20 ){ ++ TREETRACE(0x20, pParse, p, ++ ("AggInfo function expressions converted to reference index\n")); ++ sqlite3TreeViewSelect(0, p, 0); ++ printAggInfo(pAggInfo); ++ } ++#endif + } + + /* If the index or temporary table used by the GROUP BY sort +@@ -135911,12 +153334,29 @@ SQLITE_PRIVATE int sqlite3Select( + sortOut, sortPTab); + } + for(j=0; jnExpr; j++){ ++ int iOrderByCol = pGroupBy->a[j].u.x.iOrderByCol; ++ + if( groupBySort ){ + sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); + }else{ + pAggInfo->directMode = 1; + sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); + } ++ ++ if( iOrderByCol ){ ++ Expr *pX = p->pEList->a[iOrderByCol-1].pExpr; ++ Expr *pBase = sqlite3ExprSkipCollateAndLikely(pX); ++ while( ALWAYS(pBase!=0) && pBase->op==TK_IF_NULL_ROW ){ ++ pX = pBase->pLeft; ++ pBase = sqlite3ExprSkipCollateAndLikely(pX); ++ } ++ if( ALWAYS(pBase!=0) ++ && pBase->op!=TK_AGG_COLUMN ++ && pBase->op!=TK_REGISTER ++ ){ ++ sqlite3ExprToRegister(pX, iAMem+j); ++ } ++ } + } + sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, + (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); +@@ -135932,9 +153372,9 @@ SQLITE_PRIVATE int sqlite3Select( + ** and resets the aggregate accumulator registers in preparation + ** for the next GROUP BY batch. + */ +- sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); + sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow); + VdbeComment((v, "output one row")); ++ sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); + sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v); + VdbeComment((v, "check abort flag")); + sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); +@@ -135944,19 +153384,21 @@ SQLITE_PRIVATE int sqlite3Select( + ** the current row + */ + sqlite3VdbeJumpHere(v, addr1); +- updateAccumulator(pParse, iUseFlag, pAggInfo); ++ updateAccumulator(pParse, iUseFlag, pAggInfo, eDist); + sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); + VdbeComment((v, "indicate data in accumulator")); + + /* End of the loop + */ + if( groupBySort ){ +- sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop); ++ sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop); + VdbeCoverage(v); + }else{ ++ TREETRACE(0x2,pParse,p,("WhereEnd\n")); + sqlite3WhereEnd(pWInfo); + sqlite3VdbeChangeToNoop(v, addrSortingIdx); + } ++ sqlite3ExprListDelete(db, pDistinct); + + /* Output the final row of result + */ +@@ -136000,11 +153442,18 @@ SQLITE_PRIVATE int sqlite3Select( + VdbeComment((v, "indicate accumulator empty")); + sqlite3VdbeAddOp1(v, OP_Return, regReset); + ++ if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){ ++ struct AggInfo_func *pF = &pAggInfo->aFunc[0]; ++ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); ++ } + } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ + else { ++ /* Aggregate functions without GROUP BY. tag-select-0820 */ + Table *pTab; + if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){ +- /* If isSimpleCount() returns a pointer to a Table structure, then ++ /* tag-select-0821 ++ ** ++ ** If isSimpleCount() returns a pointer to a Table structure, then + ** the SQL statement is of the form: + ** + ** SELECT count(*) FROM +@@ -136058,12 +153507,17 @@ SQLITE_PRIVATE int sqlite3Select( + if( pKeyInfo ){ + sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); + } +- sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); ++ assignAggregateRegisters(pParse, pAggInfo); ++ sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0)); + sqlite3VdbeAddOp1(v, OP_Close, iCsr); + explainSimpleCount(pParse, pTab, pBest); + }else{ ++ /* The general case of an aggregate query without GROUP BY ++ ** tag-select-0822 */ + int regAcc = 0; /* "populate accumulators" flag */ +- int addrSkip; ++ ExprList *pDistinct = 0; ++ u16 distFlag = 0; ++ int eDist; + + /* If there are accumulator registers but no min() or max() functions + ** without FILTER clauses, allocate register regAcc. Register regAcc +@@ -136087,7 +153541,12 @@ SQLITE_PRIVATE int sqlite3Select( + regAcc = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc); + } ++ }else if( pAggInfo->nFunc==1 && pAggInfo->aFunc[0].iDistinct>=0 ){ ++ assert( ExprUseXList(pAggInfo->aFunc[0].pFExpr) ); ++ pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList; ++ distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; + } ++ assignAggregateRegisters(pParse, pAggInfo); + + /* This case runs if the aggregate has no GROUP BY clause. The + ** processing is much simpler since there is only a single row +@@ -136104,18 +153563,27 @@ SQLITE_PRIVATE int sqlite3Select( + assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 ); + assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 ); + +- SELECTTRACE(1,pParse,p,("WhereBegin\n")); ++ TREETRACE(0x2,pParse,p,("WhereBegin\n")); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, +- 0, minMaxFlag, 0); ++ pDistinct, p, minMaxFlag|distFlag, 0); + if( pWInfo==0 ){ + goto select_end; + } +- updateAccumulator(pParse, regAcc, pAggInfo); ++ TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); ++ eDist = sqlite3WhereIsDistinct(pWInfo); ++ updateAccumulator(pParse, regAcc, pAggInfo, eDist); ++ if( eDist!=WHERE_DISTINCT_NOOP ){ ++ struct AggInfo_func *pF = pAggInfo->aFunc; ++ if( pF ){ ++ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); ++ } ++ } ++ + if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc); +- addrSkip = sqlite3WhereOrderByLimitOptLabel(pWInfo); +- if( addrSkip!=sqlite3WhereContinueLabel(pWInfo) ){ +- sqlite3VdbeGoto(v, addrSkip); ++ if( minMaxFlag ){ ++ sqlite3WhereMinMaxOptEarlyOut(v, pWInfo); + } ++ TREETRACE(0x2,pParse,p,("WhereEnd\n")); + sqlite3WhereEnd(pWInfo); + finalizeAggFunctions(pParse, pAggInfo); + } +@@ -136134,11 +153602,9 @@ SQLITE_PRIVATE int sqlite3Select( + } + + /* If there is an ORDER BY clause, then we need to sort the results +- ** and send them to the callback one by one. ++ ** and send them to the callback one by one. tag-select-0900 + */ + if( sSort.pOrderBy ){ +- explainTempTable(pParse, +- sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY"); + assert( p->pEList==pEList ); + generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest); + } +@@ -136155,29 +153621,36 @@ SQLITE_PRIVATE int sqlite3Select( + ** successful coding of the SELECT. + */ + select_end: ++ assert( db->mallocFailed==0 || db->mallocFailed==1 ); ++ assert( db->mallocFailed==0 || pParse->nErr!=0 ); + sqlite3ExprListDelete(db, pMinMaxOrderBy); + #ifdef SQLITE_DEBUG ++ /* Internal self-checks. tag-select-1000 */ + if( pAggInfo && !db->mallocFailed ){ ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x20 ){ ++ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); ++ printAggInfo(pAggInfo); ++ } ++#endif + for(i=0; inColumn; i++){ + Expr *pExpr = pAggInfo->aCol[i].pCExpr; +- assert( pExpr!=0 || db->mallocFailed ); + if( pExpr==0 ) continue; + assert( pExpr->pAggInfo==pAggInfo ); + assert( pExpr->iAgg==i ); + } + for(i=0; inFunc; i++){ + Expr *pExpr = pAggInfo->aFunc[i].pFExpr; +- assert( pExpr!=0 || db->mallocFailed ); +- if( pExpr==0 ) continue; ++ assert( pExpr!=0 ); + assert( pExpr->pAggInfo==pAggInfo ); + assert( pExpr->iAgg==i ); + } + } + #endif + +-#if SELECTTRACE_ENABLED +- SELECTTRACE(0x1,pParse,p,("end processing\n")); +- if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ ++#if TREETRACE_ENABLED ++ TREETRACE(0x1,pParse,p,("end processing\n")); ++ if( (sqlite3TreeTrace & 0x40000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ + sqlite3TreeViewSelect(0, p, 0); + } + #endif +@@ -136438,28 +153911,49 @@ SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerS + ** pTab as well as the triggers lised in pTab->pTrigger. + */ + SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ +- Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; +- Trigger *pList = 0; /* List of triggers to return */ +- +- if( pParse->disableTriggers ){ +- return 0; ++ Schema *pTmpSchema; /* Schema of the pTab table */ ++ Trigger *pList; /* List of triggers to return */ ++ HashElem *p; /* Loop variable for TEMP triggers */ ++ ++ assert( pParse->disableTriggers==0 ); ++ pTmpSchema = pParse->db->aDb[1].pSchema; ++ p = sqliteHashFirst(&pTmpSchema->trigHash); ++ pList = pTab->pTrigger; ++ while( p ){ ++ Trigger *pTrig = (Trigger *)sqliteHashData(p); ++ if( pTrig->pTabSchema==pTab->pSchema ++ && pTrig->table ++ && 0==sqlite3StrICmp(pTrig->table, pTab->zName) ++ && (pTrig->pTabSchema!=pTmpSchema || pTrig->bReturning) ++ ){ ++ pTrig->pNext = pList; ++ pList = pTrig; ++ }else if( pTrig->op==TK_RETURNING ){ ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ assert( pParse->db->pVtabCtx==0 ); ++#endif ++ assert( pParse->bReturning ); ++ assert( !pParse->isCreate ); ++ assert( &(pParse->u1.d.pReturning->retTrig) == pTrig ); ++ pTrig->table = pTab->zName; ++ pTrig->pTabSchema = pTab->pSchema; ++ pTrig->pNext = pList; ++ pList = pTrig; ++ } ++ p = sqliteHashNext(p); + } +- +- if( pTmpSchema!=pTab->pSchema ){ +- HashElem *p; +- assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); +- for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ +- Trigger *pTrig = (Trigger *)sqliteHashData(p); +- if( pTrig->pTabSchema==pTab->pSchema +- && 0==sqlite3StrICmp(pTrig->table, pTab->zName) +- ){ +- pTrig->pNext = (pList ? pList : pTab->pTrigger); +- pList = pTrig; +- } ++#if 0 ++ if( pList ){ ++ Trigger *pX; ++ printf("Triggers for %s:", pTab->zName); ++ for(pX=pList; pX; pX=pX->pNext){ ++ printf(" %s", pX->zName); + } ++ printf("\n"); ++ fflush(stdout); + } +- +- return (pList ? pList : pTab->pTrigger); ++#endif ++ return pList; + } + + /* +@@ -136522,8 +154016,10 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( + ** name on pTableName if we are reparsing out of the schema table + */ + if( db->init.busy && iDb!=1 ){ +- sqlite3DbFree(db, pTableName->a[0].zDatabase); +- pTableName->a[0].zDatabase = 0; ++ assert( pTableName->a[0].fg.fixedSchema==0 ); ++ assert( pTableName->a[0].fg.isSubquery==0 ); ++ sqlite3DbFree(db, pTableName->a[0].u4.zDatabase); ++ pTableName->a[0].u4.zDatabase = 0; + } + + /* If the trigger name was unqualified, and the table is a temp table, +@@ -136547,22 +154043,15 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( + pTab = sqlite3SrcListLookup(pParse, pTableName); + if( !pTab ){ + /* The table does not exist. */ +- if( db->init.iDb==1 ){ +- /* Ticket #3810. +- ** Normally, whenever a table is dropped, all associated triggers are +- ** dropped too. But if a TEMP trigger is created on a non-TEMP table +- ** and the table is dropped by a different database connection, the +- ** trigger is not visible to the database connection that does the +- ** drop so the trigger cannot be dropped. This results in an +- ** "orphaned trigger" - a trigger whose associated table is missing. +- */ +- db->init.orphanTrigger = 1; +- } +- goto trigger_cleanup; ++ goto trigger_orphan_error; + } + if( IsVirtual(pTab) ){ + sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); +- goto trigger_cleanup; ++ goto trigger_orphan_error; ++ } ++ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){ ++ sqlite3ErrorMsg(pParse, "cannot create triggers on shadow tables"); ++ goto trigger_orphan_error; + } + + /* Check that the trigger name is not reserved and that no trigger of the +@@ -136583,6 +154072,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); ++ VVA_ONLY( pParse->ifNotExists = 1; ) + } + goto trigger_cleanup; + } +@@ -136597,15 +154087,15 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( + /* INSTEAD of triggers are only for views and views only support INSTEAD + ** of triggers. + */ +- if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ ++ if( IsView(pTab) && tr_tm!=TK_INSTEAD ){ + sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", +- (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); +- goto trigger_cleanup; ++ (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName->a); ++ goto trigger_orphan_error; + } +- if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ ++ if( !IsView(pTab) && tr_tm==TK_INSTEAD ){ + sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" +- " trigger on table: %S", pTableName, 0); +- goto trigger_cleanup; ++ " trigger on table: %S", pTableName->a); ++ goto trigger_orphan_error; + } + + #ifndef SQLITE_OMIT_AUTHORIZATION +@@ -136665,6 +154155,23 @@ trigger_cleanup: + }else{ + assert( pParse->pNewTrigger==pTrigger ); + } ++ return; ++ ++trigger_orphan_error: ++ if( db->init.iDb==1 ){ ++ /* Ticket #3810. ++ ** Normally, whenever a table is dropped, all associated triggers are ++ ** dropped too. But if a TEMP trigger is created on a non-TEMP table ++ ** and the table is dropped by a different database connection, the ++ ** trigger is not visible to the database connection that does the ++ ** drop so the trigger cannot be dropped. This results in an ++ ** "orphaned trigger" - a trigger whose associated table is missing. ++ ** ++ ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df ++ */ ++ db->init.orphanTrigger = 1; ++ } ++ goto trigger_cleanup; + } + + /* +@@ -136715,6 +154222,23 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( + Vdbe *v; + char *z; + ++ /* If this is a new CREATE TABLE statement, and if shadow tables ++ ** are read-only, and the trigger makes a change to a shadow table, ++ ** then raise an error - do not allow the trigger to be created. */ ++ if( sqlite3ReadOnlyShadowTables(db) ){ ++ TriggerStep *pStep; ++ for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){ ++ if( pStep->zTarget!=0 ++ && sqlite3ShadowTableName(db, pStep->zTarget) ++ ){ ++ sqlite3ErrorMsg(pParse, ++ "trigger \"%s\" may not write to shadow table \"%s\"", ++ pTrig->zName, pStep->zTarget); ++ goto triggerfinish_cleanup; ++ } ++ } ++ } ++ + /* Make an entry in the sqlite_schema table */ + v = sqlite3GetVdbe(pParse); + if( v==0 ) goto triggerfinish_cleanup; +@@ -136722,14 +154246,14 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( + z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); + testcase( z==0 ); + sqlite3NestedParse(pParse, +- "INSERT INTO %Q." DFLT_SCHEMA_TABLE ++ "INSERT INTO %Q." LEGACY_SCHEMA_TABLE + " VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", + db->aDb[iDb].zDbSName, zName, + pTrig->table, z); + sqlite3DbFree(db, z); + sqlite3ChangeCookie(pParse, iDb); + sqlite3VdbeAddParseSchemaOp(v, iDb, +- sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName)); ++ sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName), 0); + } + + if( db->init.busy ){ +@@ -136807,6 +154331,7 @@ static TriggerStep *triggerStepAllocate( + sqlite3 *db = pParse->db; + TriggerStep *pTriggerStep; + ++ if( pParse->nErr ) return 0; + pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); + if( pTriggerStep ){ + char *z = (char*)&pTriggerStep[1]; +@@ -136877,7 +154402,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep( + SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep( + Parse *pParse, /* Parser */ + Token *pTableName, /* Name of the table to be updated */ +- SrcList *pFrom, ++ SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */ + ExprList *pEList, /* The SET clause: list of column and new values */ + Expr *pWhere, /* The WHERE clause */ + u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ +@@ -136942,7 +154467,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep( + ** Recursively delete a Trigger structure + */ + SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){ +- if( pTrigger==0 ) return; ++ if( pTrigger==0 || pTrigger->bReturning ) return; + sqlite3DeleteTriggerStep(db, pTrigger->step_list); + sqlite3DbFree(db, pTrigger->zName); + sqlite3DbFree(db, pTrigger->table); +@@ -136972,7 +154497,8 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) + } + + assert( pName->nSrc==1 ); +- zDb = pName->a[0].zDatabase; ++ assert( pName->a[0].fg.fixedSchema==0 && pName->a[0].fg.isSubquery==0 ); ++ zDb = pName->a[0].u4.zDatabase; + zName = pName->a[0].zName; + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); + for(i=OMIT_TEMPDB; inDb; i++){ +@@ -136984,7 +154510,7 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) + } + if( !pTrigger ){ + if( !noErr ){ +- sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); ++ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, zDb); + } +@@ -137036,7 +154562,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ + */ + if( (v = sqlite3GetVdbe(pParse))!=0 ){ + sqlite3NestedParse(pParse, +- "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", ++ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", + db->aDb[iDb].zDbSName, pTrigger->zName + ); + sqlite3ChangeCookie(pParse, iDb); +@@ -137090,13 +154616,22 @@ static int checkColumnOverlap(IdList *pIdList, ExprList *pEList){ + return 0; + } + ++/* ++** Return true if any TEMP triggers exist ++*/ ++static int tempTriggersExist(sqlite3 *db){ ++ if( NEVER(db->aDb[1].pSchema==0) ) return 0; ++ if( sqliteHashFirst(&db->aDb[1].pSchema->trigHash)==0 ) return 0; ++ return 1; ++} ++ + /* + ** Return a list of all triggers on table pTab if there exists at least + ** one trigger that must be fired when an operation of type 'op' is + ** performed on the table, and, if that operation is an UPDATE, if at + ** least one of the columns in pChanges is being modified. + */ +-SQLITE_PRIVATE Trigger *sqlite3TriggersExist( ++static SQLITE_NOINLINE Trigger *triggersReallyExist( + Parse *pParse, /* Parse context */ + Table *pTab, /* The table the contains the triggers */ + int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ +@@ -137107,20 +154642,74 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist( + Trigger *pList = 0; + Trigger *p; + +- if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){ +- pList = sqlite3TriggerList(pParse, pTab); +- } +- assert( pList==0 || IsVirtual(pTab)==0 ); +- for(p=pList; p; p=p->pNext){ +- if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ +- mask |= p->tr_tm; ++ pList = sqlite3TriggerList(pParse, pTab); ++ assert( pList==0 || IsVirtual(pTab)==0 ++ || (pList->bReturning && pList->pNext==0) ); ++ if( pList!=0 ){ ++ p = pList; ++ if( (pParse->db->flags & SQLITE_EnableTrigger)==0 ++ && pTab->pTrigger!=0 ++ ){ ++ /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that ++ ** only TEMP triggers are allowed. Truncate the pList so that it ++ ** includes only TEMP triggers */ ++ if( pList==pTab->pTrigger ){ ++ pList = 0; ++ goto exit_triggers_exist; ++ } ++ while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext; ++ p->pNext = 0; ++ p = pList; + } ++ do{ ++ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ ++ mask |= p->tr_tm; ++ }else if( p->op==TK_RETURNING ){ ++ /* The first time a RETURNING trigger is seen, the "op" value tells ++ ** us what time of trigger it should be. */ ++ assert( sqlite3IsToplevel(pParse) ); ++ p->op = op; ++ if( IsVirtual(pTab) ){ ++ if( op!=TK_INSERT ){ ++ sqlite3ErrorMsg(pParse, ++ "%s RETURNING is not available on virtual tables", ++ op==TK_DELETE ? "DELETE" : "UPDATE"); ++ } ++ p->tr_tm = TRIGGER_BEFORE; ++ }else{ ++ p->tr_tm = TRIGGER_AFTER; ++ } ++ mask |= p->tr_tm; ++ }else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE ++ && sqlite3IsToplevel(pParse) ){ ++ /* Also fire a RETURNING trigger for an UPSERT */ ++ mask |= p->tr_tm; ++ } ++ p = p->pNext; ++ }while( p ); + } ++exit_triggers_exist: + if( pMask ){ + *pMask = mask; + } + return (mask ? pList : 0); + } ++SQLITE_PRIVATE Trigger *sqlite3TriggersExist( ++ Parse *pParse, /* Parse context */ ++ Table *pTab, /* The table the contains the triggers */ ++ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ ++ ExprList *pChanges, /* Columns that change in an UPDATE statement */ ++ int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ++){ ++ assert( pTab!=0 ); ++ if( (pTab->pTrigger==0 && !tempTriggersExist(pParse->db)) ++ || pParse->disableTriggers ++ ){ ++ if( pMask ) *pMask = 0; ++ return 0; ++ } ++ return triggersReallyExist(pParse,pTab,op,pChanges,pMask); ++} + + /* + ** Convert the pStep->zTarget string into a SrcList and return a pointer +@@ -137146,10 +154735,20 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc( + Schema *pSchema = pStep->pTrig->pSchema; + pSrc->a[0].zName = zName; + if( pSchema!=db->aDb[1].pSchema ){ +- pSrc->a[0].pSchema = pSchema; ++ assert( pSrc->a[0].fg.fixedSchema || pSrc->a[0].u4.zDatabase==0 ); ++ pSrc->a[0].u4.pSchema = pSchema; ++ pSrc->a[0].fg.fixedSchema = 1; + } + if( pStep->pFrom ){ + SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0); ++ if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){ ++ Select *pSubquery; ++ Token as; ++ pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0); ++ as.n = 0; ++ as.z = 0; ++ pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); ++ } + pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup); + } + }else{ +@@ -137158,6 +154757,224 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc( + return pSrc; + } + ++/* ++** Return true if the pExpr term from the RETURNING clause argument ++** list is of the form "*". Raise an error if the terms if of the ++** form "table.*". ++*/ ++static int isAsteriskTerm( ++ Parse *pParse, /* Parsing context */ ++ Expr *pTerm /* A term in the RETURNING clause */ ++){ ++ assert( pTerm!=0 ); ++ if( pTerm->op==TK_ASTERISK ) return 1; ++ if( pTerm->op!=TK_DOT ) return 0; ++ assert( pTerm->pRight!=0 ); ++ assert( pTerm->pLeft!=0 ); ++ if( pTerm->pRight->op!=TK_ASTERISK ) return 0; ++ sqlite3ErrorMsg(pParse, "RETURNING may not use \"TABLE.*\" wildcards"); ++ return 1; ++} ++ ++/* The input list pList is the list of result set terms from a RETURNING ++** clause. The table that we are returning from is pTab. ++** ++** This routine makes a copy of the pList, and at the same time expands ++** any "*" wildcards to be the complete set of columns from pTab. ++*/ ++static ExprList *sqlite3ExpandReturning( ++ Parse *pParse, /* Parsing context */ ++ ExprList *pList, /* The arguments to RETURNING */ ++ Table *pTab /* The table being updated */ ++){ ++ ExprList *pNew = 0; ++ sqlite3 *db = pParse->db; ++ int i; ++ ++ for(i=0; inExpr; i++){ ++ Expr *pOldExpr = pList->a[i].pExpr; ++ if( NEVER(pOldExpr==0) ) continue; ++ if( isAsteriskTerm(pParse, pOldExpr) ){ ++ int jj; ++ for(jj=0; jjnCol; jj++){ ++ Expr *pNewExpr; ++ if( IsHiddenColumn(pTab->aCol+jj) ) continue; ++ pNewExpr = sqlite3Expr(db, TK_ID, pTab->aCol[jj].zCnName); ++ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr); ++ if( !db->mallocFailed ){ ++ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; ++ pItem->zEName = sqlite3DbStrDup(db, pTab->aCol[jj].zCnName); ++ pItem->fg.eEName = ENAME_NAME; ++ } ++ } ++ }else{ ++ Expr *pNewExpr = sqlite3ExprDup(db, pOldExpr, 0); ++ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr); ++ if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){ ++ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; ++ pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName); ++ pItem->fg.eEName = pList->a[i].fg.eEName; ++ } ++ } ++ } ++ return pNew; ++} ++ ++/* If the Expr node is a subquery or an EXISTS operator or an IN operator that ++** uses a subquery, and if the subquery is SF_Correlated, then mark the ++** expression as EP_VarSelect. ++*/ ++static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ ++ UNUSED_PARAMETER(NotUsed); ++ if( ExprUseXSelect(pExpr) ++ && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 ++ ){ ++ testcase( ExprHasProperty(pExpr, EP_VarSelect) ); ++ ExprSetProperty(pExpr, EP_VarSelect); ++ } ++ return WRC_Continue; ++} ++ ++ ++/* ++** If the SELECT references the table pWalker->u.pTab, then do two things: ++** ++** (1) Mark the SELECT as as SF_Correlated. ++** (2) Set pWalker->eCode to non-zero so that the caller will know ++** that (1) has happened. ++*/ ++static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ ++ int i; ++ SrcList *pSrc; ++ assert( pSelect!=0 ); ++ pSrc = pSelect->pSrc; ++ assert( pSrc!=0 ); ++ for(i=0; inSrc; i++){ ++ if( pSrc->a[i].pSTab==pWalker->u.pTab ){ ++ testcase( pSelect->selFlags & SF_Correlated ); ++ pSelect->selFlags |= SF_Correlated; ++ pWalker->eCode = 1; ++ break; ++ } ++ } ++ return WRC_Continue; ++} ++ ++/* ++** Scan the expression list that is the argument to RETURNING looking ++** for subqueries that depend on the table which is being modified in the ++** statement that is hosting the RETURNING clause (pTab). Mark all such ++** subqueries as SF_Correlated. If the subqueries are part of an ++** expression, mark the expression as EP_VarSelect. ++** ++** https://sqlite.org/forum/forumpost/2c83569ce8945d39 ++*/ ++static void sqlite3ProcessReturningSubqueries( ++ ExprList *pEList, ++ Table *pTab ++){ ++ Walker w; ++ memset(&w, 0, sizeof(w)); ++ w.xExprCallback = sqlite3ExprWalkNoop; ++ w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; ++ w.u.pTab = pTab; ++ sqlite3WalkExprList(&w, pEList); ++ if( w.eCode ){ ++ w.xExprCallback = sqlite3ReturningSubqueryVarSelect; ++ w.xSelectCallback = sqlite3SelectWalkNoop; ++ sqlite3WalkExprList(&w, pEList); ++ } ++} ++ ++/* ++** Generate code for the RETURNING trigger. Unlike other triggers ++** that invoke a subprogram in the bytecode, the code for RETURNING ++** is generated in-line. ++*/ ++static void codeReturningTrigger( ++ Parse *pParse, /* Parse context */ ++ Trigger *pTrigger, /* The trigger step that defines the RETURNING */ ++ Table *pTab, /* The table to code triggers from */ ++ int regIn /* The first in an array of registers */ ++){ ++ Vdbe *v = pParse->pVdbe; ++ sqlite3 *db = pParse->db; ++ ExprList *pNew; ++ Returning *pReturning; ++ Select sSelect; ++ SrcList *pFrom; ++ u8 fromSpace[SZ_SRCLIST_1]; ++ ++ assert( v!=0 ); ++ if( !pParse->bReturning ){ ++ /* This RETURNING trigger must be for a different statement as ++ ** this statement lacks a RETURNING clause. */ ++ return; ++ } ++ assert( db->pParse==pParse ); ++ assert( !pParse->isCreate ); ++ pReturning = pParse->u1.d.pReturning; ++ if( pTrigger != &(pReturning->retTrig) ){ ++ /* This RETURNING trigger is for a different statement */ ++ return; ++ } ++ memset(&sSelect, 0, sizeof(sSelect)); ++ pFrom = (SrcList*)fromSpace; ++ memset(pFrom, 0, SZ_SRCLIST_1); ++ sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); ++ sSelect.pSrc = pFrom; ++ pFrom->nSrc = 1; ++ pFrom->a[0].pSTab = pTab; ++ pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */ ++ pFrom->a[0].iCursor = -1; ++ sqlite3SelectPrep(pParse, &sSelect, 0); ++ if( pParse->nErr==0 ){ ++ assert( db->mallocFailed==0 ); ++ sqlite3GenerateColumnNames(pParse, &sSelect); ++ } ++ sqlite3ExprListDelete(db, sSelect.pEList); ++ pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab); ++ if( pParse->nErr==0 ){ ++ NameContext sNC; ++ memset(&sNC, 0, sizeof(sNC)); ++ if( pReturning->nRetCol==0 ){ ++ pReturning->nRetCol = pNew->nExpr; ++ pReturning->iRetCur = pParse->nTab++; ++ } ++ sNC.pParse = pParse; ++ sNC.uNC.iBaseReg = regIn; ++ sNC.ncFlags = NC_UBaseReg; ++ pParse->eTriggerOp = pTrigger->op; ++ pParse->pTriggerTab = pTab; ++ if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK ++ && ALWAYS(!db->mallocFailed) ++ ){ ++ int i; ++ int nCol = pNew->nExpr; ++ int reg = pParse->nMem+1; ++ sqlite3ProcessReturningSubqueries(pNew, pTab); ++ pParse->nMem += nCol+2; ++ pReturning->iRetReg = reg; ++ for(i=0; ia[i].pExpr; ++ assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ ++ sqlite3ExprCodeFactorable(pParse, pCol, reg+i); ++ if( sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ ++ sqlite3VdbeAddOp1(v, OP_RealAffinity, reg+i); ++ } ++ } ++ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i); ++ sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1); ++ sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1); ++ } ++ } ++ sqlite3ExprListDelete(db, pNew); ++ pParse->eTriggerOp = 0; ++ pParse->pTriggerTab = 0; ++} ++ ++ ++ + /* + ** Generate VDBE code for the statements inside the body of a single + ** trigger. +@@ -137207,6 +155024,7 @@ static int codeTriggerProgram( + sqlite3ExprDup(db, pStep->pWhere, 0), + pParse->eOrconf, 0, 0, 0 + ); ++ sqlite3VdbeAddOp0(v, OP_ResetCount); + break; + } + case TK_INSERT: { +@@ -137217,6 +155035,7 @@ static int codeTriggerProgram( + pParse->eOrconf, + sqlite3UpsertDup(db, pStep->pUpsert) + ); ++ sqlite3VdbeAddOp0(v, OP_ResetCount); + break; + } + case TK_DELETE: { +@@ -137224,6 +155043,7 @@ static int codeTriggerProgram( + sqlite3TriggerStepSrc(pParse, pStep), + sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0 + ); ++ sqlite3VdbeAddOp0(v, OP_ResetCount); + break; + } + default: assert( pStep->op==TK_SELECT ); { +@@ -137235,9 +155055,6 @@ static int codeTriggerProgram( + break; + } + } +- if( pStep->op!=TK_SELECT ){ +- sqlite3VdbeAddOp0(v, OP_ResetCount); +- } + } + + return 0; +@@ -137295,8 +155112,8 @@ static TriggerPrg *codeRowTrigger( + Vdbe *v; /* Temporary VM */ + NameContext sNC; /* Name context for sub-vdbe */ + SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ +- Parse *pSubParse; /* Parse context for sub-vdbe */ + int iEndTrigger = 0; /* Label to jump to if WHEN is false */ ++ Parse sSubParse; /* Parse context for sub-vdbe */ + + assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); + assert( pTop->pVdbe ); +@@ -137318,19 +155135,19 @@ static TriggerPrg *codeRowTrigger( + + /* Allocate and populate a new Parse context to use for coding the + ** trigger sub-program. */ +- pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); +- if( !pSubParse ) return 0; ++ sqlite3ParseObjectInit(&sSubParse, db); + memset(&sNC, 0, sizeof(sNC)); +- sNC.pParse = pSubParse; +- pSubParse->db = db; +- pSubParse->pTriggerTab = pTab; +- pSubParse->pToplevel = pTop; +- pSubParse->zAuthContext = pTrigger->zName; +- pSubParse->eTriggerOp = pTrigger->op; +- pSubParse->nQueryLoop = pParse->nQueryLoop; +- pSubParse->disableVtab = pParse->disableVtab; +- +- v = sqlite3GetVdbe(pSubParse); ++ sNC.pParse = &sSubParse; ++ sSubParse.pTriggerTab = pTab; ++ sSubParse.pToplevel = pTop; ++ sSubParse.zAuthContext = pTrigger->zName; ++ sSubParse.eTriggerOp = pTrigger->op; ++ sSubParse.nQueryLoop = pParse->nQueryLoop; ++ sSubParse.prepFlags = pParse->prepFlags; ++ sSubParse.oldmask = 0; ++ sSubParse.newmask = 0; ++ ++ v = sqlite3GetVdbe(&sSubParse); + if( v ){ + VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", + pTrigger->zName, onErrorText(orconf), +@@ -137353,17 +155170,17 @@ static TriggerPrg *codeRowTrigger( + ** OP_Halt inserted at the end of the program. */ + if( pTrigger->pWhen ){ + pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0); +- if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) +- && db->mallocFailed==0 ++ if( db->mallocFailed==0 ++ && SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) + ){ +- iEndTrigger = sqlite3VdbeMakeLabel(pSubParse); +- sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); ++ iEndTrigger = sqlite3VdbeMakeLabel(&sSubParse); ++ sqlite3ExprIfFalse(&sSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); + } + sqlite3ExprDelete(db, pWhen); + } + + /* Code the trigger program into the sub-vdbe. */ +- codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); ++ codeTriggerProgram(&sSubParse, pTrigger->step_list, orconf); + + /* Insert an OP_Halt at the end of the sub-program. */ + if( iEndTrigger ){ +@@ -137371,24 +155188,24 @@ static TriggerPrg *codeRowTrigger( + } + sqlite3VdbeAddOp0(v, OP_Halt); + VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); ++ transferParseError(pParse, &sSubParse); + +- transferParseError(pParse, pSubParse); +- if( db->mallocFailed==0 && pParse->nErr==0 ){ ++ if( pParse->nErr==0 ){ ++ assert( db->mallocFailed==0 ); + pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); + } +- pProgram->nMem = pSubParse->nMem; +- pProgram->nCsr = pSubParse->nTab; ++ pProgram->nMem = sSubParse.nMem; ++ pProgram->nCsr = sSubParse.nTab; + pProgram->token = (void *)pTrigger; +- pPrg->aColmask[0] = pSubParse->oldmask; +- pPrg->aColmask[1] = pSubParse->newmask; ++ pPrg->aColmask[0] = sSubParse.oldmask; ++ pPrg->aColmask[1] = sSubParse.newmask; + sqlite3VdbeDelete(v); ++ }else{ ++ transferParseError(pParse, &sSubParse); + } + +- assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); +- assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); +- sqlite3ParserReset(pSubParse); +- sqlite3StackFree(db, pSubParse); +- ++ assert( !sSubParse.pTriggerPrg && !sSubParse.nMaxArg ); ++ sqlite3ParseObjectReset(&sSubParse); + return pPrg; + } + +@@ -137421,6 +155238,7 @@ static TriggerPrg *getRowTrigger( + /* If an existing TriggerPrg could not be located, create a new one. */ + if( !pPrg ){ + pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); ++ pParse->db->errByteOffset = -1; + } + + return pPrg; +@@ -137443,7 +155261,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( + Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ + TriggerPrg *pPrg; + pPrg = getRowTrigger(pParse, p, pTab, orconf); +- assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); ++ assert( pPrg || pParse->nErr ); + + /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program + ** is a pointer to the sub-vdbe containing the trigger program. */ +@@ -137460,7 +155278,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( + ** invocation is disallowed if (a) the sub-program is really a trigger, + ** not a foreign key action, and (b) the flag to enable recursive triggers + ** is clear. */ +- sqlite3VdbeChangeP5(v, (u8)bRecursive); ++ sqlite3VdbeChangeP5(v, (u16)bRecursive); + } + } + +@@ -137486,7 +155304,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( + ** ... ... + ** reg+N OLD.* value of right-most column of pTab + ** reg+N+1 NEW.rowid +-** reg+N+2 OLD.* value of left-most column of pTab ++** reg+N+2 NEW.* value of left-most column of pTab + ** ... ... + ** reg+N+N+1 NEW.* value of right-most column of pTab + ** +@@ -137531,12 +155349,20 @@ SQLITE_PRIVATE void sqlite3CodeRowTrigger( + assert( p->pSchema==p->pTabSchema + || p->pSchema==pParse->db->aDb[1].pSchema ); + +- /* Determine whether we should code this trigger */ +- if( p->op==op ++ /* Determine whether we should code this trigger. One of two choices: ++ ** 1. The trigger is an exact match to the current DML statement ++ ** 2. This is a RETURNING trigger for INSERT but we are currently ++ ** doing the UPDATE part of an UPSERT. ++ */ ++ if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE)) + && p->tr_tm==tr_tm + && checkColumnOverlap(p->pColumns, pChanges) + ){ +- sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); ++ if( !p->bReturning ){ ++ sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); ++ }else if( sqlite3IsToplevel(pParse) ){ ++ codeReturningTrigger(pParse, p, pTab, reg); ++ } + } + } + } +@@ -137580,14 +155406,22 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask( + Trigger *p; + + assert( isNew==1 || isNew==0 ); ++ if( IsView(pTab) ){ ++ return 0xffffffff; ++ } + for(p=pTrigger; p; p=p->pNext){ +- if( p->op==op && (tr_tm&p->tr_tm) ++ if( p->op==op ++ && (tr_tm&p->tr_tm) + && checkColumnOverlap(p->pColumns,pChanges) + ){ +- TriggerPrg *pPrg; +- pPrg = getRowTrigger(pParse, p, pTab, orconf); +- if( pPrg ){ +- mask |= pPrg->aColmask[isNew]; ++ if( p->bReturning ){ ++ mask = 0xffffffff; ++ }else{ ++ TriggerPrg *pPrg; ++ pPrg = getRowTrigger(pParse, p, pTab, orconf); ++ if( pPrg ){ ++ mask |= pPrg->aColmask[isNew]; ++ } + } + } + } +@@ -137660,21 +155494,25 @@ static void updateVirtualTable( + ** it has been converted into REAL. + */ + SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ ++ Column *pCol; + assert( pTab!=0 ); +- if( !pTab->pSelect ){ ++ assert( pTab->nCol>i ); ++ pCol = &pTab->aCol[i]; ++ if( pCol->iDflt ){ + sqlite3_value *pValue = 0; + u8 enc = ENC(sqlite3VdbeDb(v)); +- Column *pCol = &pTab->aCol[i]; +- VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); ++ assert( !IsView(pTab) ); ++ VdbeComment((v, "%s.%s", pTab->zName, pCol->zCnName)); + assert( inCol ); +- sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc, ++ sqlite3ValueFromExpr(sqlite3VdbeDb(v), ++ sqlite3ColumnExpr(pTab,pCol), enc, + pCol->affinity, &pValue); + if( pValue ){ + sqlite3VdbeAppendP4(v, pValue, P4_MEM); + } + } + #ifndef SQLITE_OMIT_FLOATING_POINT +- if( pTab->aCol[i].affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ ++ if( pCol->affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ + sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); + } + #endif +@@ -137799,7 +155637,7 @@ static void updateFromSelect( + Expr *pLimit2 = 0; + ExprList *pOrderBy2 = 0; + sqlite3 *db = pParse->db; +- Table *pTab = pTabList->a[0].pTab; ++ Table *pTab = pTabList->a[0].pSTab; + SrcList *pSrc; + Expr *pWhere2; + int eDest; +@@ -137821,9 +155659,10 @@ static void updateFromSelect( + + assert( pTabList->nSrc>1 ); + if( pSrc ){ ++ assert( pSrc->a[0].fg.notCte ); + pSrc->a[0].iCursor = -1; +- pSrc->a[0].pTab->nTabRef--; +- pSrc->a[0].pTab = 0; ++ pSrc->a[0].pSTab->nTabRef--; ++ pSrc->a[0].pSTab = 0; + } + if( pPk ){ + for(i=0; inKeyCol; i++){ +@@ -137835,8 +155674,8 @@ static void updateFromSelect( + #endif + pList = sqlite3ExprListAppend(pParse, pList, pNew); + } +- eDest = SRT_Upfrom; +- }else if( pTab->pSelect ){ ++ eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom; ++ }else if( IsView(pTab) ){ + for(i=0; inCol; i++){ + pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); + } +@@ -137850,7 +155689,8 @@ static void updateFromSelect( + } + #endif + } +- if( ALWAYS(pChanges) ){ ++ assert( pChanges!=0 || pParse->db->mallocFailed ); ++ if( pChanges ){ + for(i=0; inExpr; i++){ + pList = sqlite3ExprListAppend(pParse, pList, + sqlite3ExprDup(db, pChanges->a[i].pExpr, 0) +@@ -137858,8 +155698,10 @@ static void updateFromSelect( + } + } + pSelect = sqlite3SelectNew(pParse, pList, +- pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UpdateFrom|SF_IncludeHidden, pLimit2 ++ pSrc, pWhere2, pGrp, 0, pOrderBy2, ++ SF_UFSrcCheck|SF_IncludeHidden|SF_UpdateFrom, pLimit2 + ); ++ if( pSelect ) pSelect->selFlags |= SF_OrderByReqd; + sqlite3SelectDestInit(&dest, eDest, iEph); + dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1); + sqlite3Select(pParse, pSelect, &dest); +@@ -137944,9 +155786,11 @@ SQLITE_PRIVATE void sqlite3Update( + + memset(&sContext, 0, sizeof(sContext)); + db = pParse->db; +- if( pParse->nErr || db->mallocFailed ){ ++ assert( db->pParse==pParse ); ++ if( pParse->nErr ){ + goto update_cleanup; + } ++ assert( db->mallocFailed==0 ); + + /* Locate the table which we want to update. + */ +@@ -137959,7 +155803,7 @@ SQLITE_PRIVATE void sqlite3Update( + */ + #ifndef SQLITE_OMIT_TRIGGER + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask); +- isView = pTab->pSelect!=0; ++ isView = IsView(pTab); + assert( pTrigger || tmask==0 ); + #else + # define pTrigger 0 +@@ -137971,6 +155815,14 @@ SQLITE_PRIVATE void sqlite3Update( + # define isView 0 + #endif + ++#if TREETRACE_ENABLED ++ if( sqlite3TreeTrace & 0x10000 ){ ++ sqlite3TreeViewLine(0, "In sqlite3Update() at %s:%d", __FILE__, __LINE__); ++ sqlite3TreeViewUpdate(pParse->pWith, pTabList, pChanges, pWhere, ++ onError, pOrderBy, pLimit, pUpsert, pTrigger); ++ } ++#endif ++ + /* If there was a FROM clause, set nChangeFrom to the number of expressions + ** in the change-list. Otherwise, set it to 0. There cannot be a FROM + ** clause if this function is being called to generate code for part of +@@ -137991,7 +155843,7 @@ SQLITE_PRIVATE void sqlite3Update( + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto update_cleanup; + } +- if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ ++ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ + goto update_cleanup; + } + +@@ -138053,30 +155905,27 @@ SQLITE_PRIVATE void sqlite3Update( + if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ + goto update_cleanup; + } +- for(j=0; jnCol; j++){ +- if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zEName)==0 ){ +- if( j==pTab->iPKey ){ +- chngRowid = 1; +- pRowidExpr = pChanges->a[i].pExpr; +- iRowidExpr = i; +- }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ +- chngPk = 1; +- } ++ j = sqlite3ColumnIndex(pTab, pChanges->a[i].zEName); ++ if( j>=0 ){ ++ if( j==pTab->iPKey ){ ++ chngRowid = 1; ++ pRowidExpr = pChanges->a[i].pExpr; ++ iRowidExpr = i; ++ }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ ++ chngPk = 1; ++ } + #ifndef SQLITE_OMIT_GENERATED_COLUMNS +- else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){ +- testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ); +- testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); +- sqlite3ErrorMsg(pParse, +- "cannot UPDATE generated column \"%s\"", +- pTab->aCol[j].zName); +- goto update_cleanup; +- } +-#endif +- aXRef[j] = i; +- break; ++ else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){ ++ testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ); ++ testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); ++ sqlite3ErrorMsg(pParse, ++ "cannot UPDATE generated column \"%s\"", ++ pTab->aCol[j].zCnName); ++ goto update_cleanup; + } +- } +- if( j>=pTab->nCol ){ ++#endif ++ aXRef[j] = i; ++ }else{ + if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zEName) ){ + j = -1; + chngRowid = 1; +@@ -138092,7 +155941,7 @@ SQLITE_PRIVATE void sqlite3Update( + { + int rc; + rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, +- j<0 ? "ROWID" : pTab->aCol[j].zName, ++ j<0 ? "ROWID" : pTab->aCol[j].zCnName, + db->aDb[iDb].zDbSName); + if( rc==SQLITE_DENY ){ + goto update_cleanup; +@@ -138124,8 +155973,10 @@ SQLITE_PRIVATE void sqlite3Update( + for(i=0; inCol; i++){ + if( aXRef[i]>=0 ) continue; + if( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ) continue; +- if( sqlite3ExprReferencesUpdatedColumn(pTab->aCol[i].pDflt, +- aXRef, chngRowid) ){ ++ if( sqlite3ExprReferencesUpdatedColumn( ++ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), ++ aXRef, chngRowid) ++ ){ + aXRef[i] = 99999; + bProgress = 1; + } +@@ -138244,6 +156095,7 @@ SQLITE_PRIVATE void sqlite3Update( + if( (db->flags&SQLITE_CountRows)!=0 + && !pParse->pTriggerTab + && !pParse->nested ++ && !pParse->bReturning + && pUpsert==0 + ){ + regRowCount = ++pParse->nMem; +@@ -138252,6 +156104,8 @@ SQLITE_PRIVATE void sqlite3Update( + + if( nChangeFrom==0 && HasRowid(pTab) ){ + sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); ++ iEph = pParse->nTab++; ++ addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet); + }else{ + assert( pPk!=0 || HasRowid(pTab) ); + nPk = pPk ? pPk->nKeyCol : 0; +@@ -138302,15 +156156,25 @@ SQLITE_PRIVATE void sqlite3Update( + /* Begin the database scan. + ** + ** Do not consider a single-pass strategy for a multi-row update if +- ** there are any triggers or foreign keys to process, or rows may +- ** be deleted as a result of REPLACE conflict handling. Any of these +- ** things might disturb a cursor being used to scan through the table +- ** or index, causing a single-pass approach to malfunction. */ +- flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE; +- if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ ++ ** there is anything that might disrupt the cursor being used to do ++ ** the UPDATE: ++ ** (1) This is a nested UPDATE ++ ** (2) There are triggers ++ ** (3) There are FOREIGN KEY constraints ++ ** (4) There are REPLACE conflict handlers ++ ** (5) There are subqueries in the WHERE clause ++ */ ++ flags = WHERE_ONEPASS_DESIRED; ++ if( !pParse->nested ++ && !pTrigger ++ && !hasFK ++ && !chngKey ++ && !bReplace ++ && (pWhere==0 || !ExprHasProperty(pWhere, EP_Subquery)) ++ ){ + flags |= WHERE_ONEPASS_MULTIROW; + } +- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur); ++ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); + if( pWInfo==0 ) goto update_cleanup; + + /* A one-pass strategy that might update more than one row may not +@@ -138343,9 +156207,10 @@ SQLITE_PRIVATE void sqlite3Update( + ** leave it in register regOldRowid. */ + sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); + if( eOnePass==ONEPASS_OFF ){ +- /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ + aRegIdx[nAllIdx] = ++pParse->nMem; +- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); ++ sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid); ++ }else{ ++ if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen); + } + }else{ + /* Read the PK of the current row into an array of registers. In +@@ -138377,6 +156242,8 @@ SQLITE_PRIVATE void sqlite3Update( + + if( !isView ){ + int addrOnce = 0; ++ int iNotUsed1 = 0; ++ int iNotUsed2 = 0; + + /* Open every index that needs updating. */ + if( eOnePass!=ONEPASS_OFF ){ +@@ -138388,7 +156255,7 @@ SQLITE_PRIVATE void sqlite3Update( + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + } + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, +- aToOpen, 0, 0); ++ aToOpen, &iNotUsed1, &iNotUsed2); + if( addrOnce ){ + sqlite3VdbeJumpHereOrPopInst(v, addrOnce); + } +@@ -138396,7 +156263,12 @@ SQLITE_PRIVATE void sqlite3Update( + + /* Top of the update loop */ + if( eOnePass!=ONEPASS_OFF ){ +- if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){ ++ if( aiCurOnePass[0]!=iDataCur ++ && aiCurOnePass[1]!=iDataCur ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ && !isView ++#endif ++ ){ + assert( pPk ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey); + VdbeCoverage(v); +@@ -138433,8 +156305,9 @@ SQLITE_PRIVATE void sqlite3Update( + VdbeCoverage(v); + } + }else{ +- labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak, +- regOldRowid); ++ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); ++ labelContinue = sqlite3VdbeMakeLabel(pParse); ++ addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); + VdbeCoverage(v); +@@ -138477,6 +156350,9 @@ SQLITE_PRIVATE void sqlite3Update( + } + } + if( chngRowid==0 && pPk==0 ){ ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ if( isView ) sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); ++#endif + sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); + } + } +@@ -138600,7 +156476,7 @@ SQLITE_PRIVATE void sqlite3Update( + }else{ + sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid); + } +- VdbeCoverageNeverTaken(v); ++ VdbeCoverage(v); + } + + /* Do FK constraint checks. */ +@@ -138673,8 +156549,10 @@ SQLITE_PRIVATE void sqlite3Update( + sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); + } + +- sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, +- TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); ++ if( pTrigger ){ ++ sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, ++ TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); ++ } + + /* Repeat the above with the next record to be updated, until + ** all record selected by the WHERE clause have been updated. +@@ -138684,11 +156562,9 @@ SQLITE_PRIVATE void sqlite3Update( + }else if( eOnePass==ONEPASS_MULTI ){ + sqlite3VdbeResolveLabel(v, labelContinue); + sqlite3WhereEnd(pWInfo); +- }else if( pPk || nChangeFrom ){ ++ }else{ + sqlite3VdbeResolveLabel(v, labelContinue); + sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); +- }else{ +- sqlite3VdbeGoto(v, labelContinue); + } + sqlite3VdbeResolveLabel(v, labelBreak); + +@@ -138705,9 +156581,7 @@ SQLITE_PRIVATE void sqlite3Update( + ** that information. + */ + if( regRowCount ){ +- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); +- sqlite3VdbeSetNumCols(v, 1); +- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); ++ sqlite3CodeChangeCount(v, regRowCount, "rows updated"); + } + + update_cleanup: +@@ -138773,7 +156647,7 @@ static void updateVirtualTable( + int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */ + int regArg; /* First register in VUpdate arg array */ + int regRec; /* Register in which to assemble record */ +- int regRowid; /* Register for ephem table rowid */ ++ int regRowid; /* Register for ephemeral table rowid */ + int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */ + int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */ + int eOnePass; /* True to use onepass strategy */ +@@ -138788,12 +156662,26 @@ static void updateVirtualTable( + regArg = pParse->nMem + 1; + pParse->nMem += nArg; + if( pSrc->nSrc>1 ){ ++ Index *pPk = 0; + Expr *pRow; + ExprList *pList; +- if( pRowid ){ +- pRow = sqlite3ExprDup(db, pRowid, 0); ++ if( HasRowid(pTab) ){ ++ if( pRowid ){ ++ pRow = sqlite3ExprDup(db, pRowid, 0); ++ }else{ ++ pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0); ++ } + }else{ +- pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0); ++ i16 iPk; /* PRIMARY KEY column */ ++ pPk = sqlite3PrimaryKeyIndex(pTab); ++ assert( pPk!=0 ); ++ assert( pPk->nKeyCol==1 ); ++ iPk = pPk->aiColumn[0]; ++ if( aXRef[iPk]>=0 ){ ++ pRow = sqlite3ExprDup(db, pChanges->a[aXRef[iPk]].pExpr, 0); ++ }else{ ++ pRow = exprRowColumn(pParse, iPk); ++ } + } + pList = sqlite3ExprListAppend(pParse, 0, pRow); + +@@ -138803,11 +156691,13 @@ static void updateVirtualTable( + sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0) + ); + }else{ +- pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); ++ Expr *pRowExpr = exprRowColumn(pParse, i); ++ if( pRowExpr ) pRowExpr->op2 = OPFLAG_NOCHNG; ++ pList = sqlite3ExprListAppend(pParse, pList, pRowExpr); + } + } + +- updateFromSelect(pParse, ephemTab, 0, pList, pSrc, pWhere, 0, 0); ++ updateFromSelect(pParse, ephemTab, pPk, pList, pSrc, pWhere, 0, 0); + sqlite3ExprListDelete(db, pList); + eOnePass = ONEPASS_OFF; + }else{ +@@ -138815,7 +156705,9 @@ static void updateVirtualTable( + regRowid = ++pParse->nMem; + + /* Start scanning the virtual table */ +- pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0); ++ pWInfo = sqlite3WhereBegin( ++ pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0 ++ ); + if( pWInfo==0 ) return; + + /* Populate the argument registers. */ +@@ -138878,7 +156770,7 @@ static void updateVirtualTable( + sqlite3WhereEnd(pWInfo); + } + +- /* Begin scannning through the ephemeral table. */ ++ /* Begin scanning through the ephemeral table. */ + addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v); + + /* Extract arguments from the current row of the ephemeral table and +@@ -138926,15 +156818,22 @@ static void updateVirtualTable( + /* + ** Free a list of Upsert objects + */ +-SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){ +- if( p ){ ++static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){ ++ do{ ++ Upsert *pNext = p->pNextUpsert; + sqlite3ExprListDelete(db, p->pUpsertTarget); + sqlite3ExprDelete(db, p->pUpsertTargetWhere); + sqlite3ExprListDelete(db, p->pUpsertSet); + sqlite3ExprDelete(db, p->pUpsertWhere); ++ sqlite3DbFree(db, p->pToFree); + sqlite3DbFree(db, p); +- } ++ p = pNext; ++ }while( p ); + } ++SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){ ++ if( p ) upsertDelete(db, p); ++} ++ + + /* + ** Duplicate an Upsert object. +@@ -138945,7 +156844,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){ + sqlite3ExprListDup(db, p->pUpsertTarget, 0), + sqlite3ExprDup(db, p->pUpsertTargetWhere, 0), + sqlite3ExprListDup(db, p->pUpsertSet, 0), +- sqlite3ExprDup(db, p->pUpsertWhere, 0) ++ sqlite3ExprDup(db, p->pUpsertWhere, 0), ++ sqlite3UpsertDup(db, p->pNextUpsert) + ); + } + +@@ -138957,22 +156857,25 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew( + ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */ + Expr *pTargetWhere, /* Optional WHERE clause on the target */ + ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */ +- Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */ ++ Expr *pWhere, /* WHERE clause for the ON CONFLICT UPDATE */ ++ Upsert *pNext /* Next ON CONFLICT clause in the list */ + ){ + Upsert *pNew; +- pNew = sqlite3DbMallocRaw(db, sizeof(Upsert)); ++ pNew = sqlite3DbMallocZero(db, sizeof(Upsert)); + if( pNew==0 ){ + sqlite3ExprListDelete(db, pTarget); + sqlite3ExprDelete(db, pTargetWhere); + sqlite3ExprListDelete(db, pSet); + sqlite3ExprDelete(db, pWhere); ++ sqlite3UpsertDelete(db, pNext); + return 0; + }else{ + pNew->pUpsertTarget = pTarget; + pNew->pUpsertTargetWhere = pTargetWhere; + pNew->pUpsertSet = pSet; + pNew->pUpsertWhere = pWhere; +- pNew->pUpsertIdx = 0; ++ pNew->isDoUpdate = pSet!=0; ++ pNew->pNextUpsert = pNext; + } + return pNew; + } +@@ -138987,7 +156890,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew( + SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( + Parse *pParse, /* The parsing context */ + SrcList *pTabList, /* Table into which we are inserting */ +- Upsert *pUpsert /* The ON CONFLICT clauses */ ++ Upsert *pUpsert, /* The ON CONFLICT clauses */ ++ Upsert *pAll /* Complete list of all ON CONFLICT clauses */ + ){ + Table *pTab; /* That table into which we are inserting */ + int rc; /* Result code */ +@@ -138997,9 +156901,10 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( + Expr *pTerm; /* One term of the conflict-target clause */ + NameContext sNC; /* Context for resolving symbolic names */ + Expr sCol[2]; /* Index column converted into an Expr */ ++ int nClause = 0; /* Counter of ON CONFLICT clauses */ + + assert( pTabList->nSrc==1 ); +- assert( pTabList->a[0].pTab!=0 ); ++ assert( pTabList->a[0].pSTab!=0 ); + assert( pUpsert!=0 ); + assert( pUpsert->pUpsertTarget!=0 ); + +@@ -139010,87 +156915,144 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + sNC.pSrcList = pTabList; +- rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); +- if( rc ) return rc; +- rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); +- if( rc ) return rc; ++ for(; pUpsert && pUpsert->pUpsertTarget; ++ pUpsert=pUpsert->pNextUpsert, nClause++){ ++ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); ++ if( rc ) return rc; ++ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); ++ if( rc ) return rc; + +- /* Check to see if the conflict target matches the rowid. */ +- pTab = pTabList->a[0].pTab; +- pTarget = pUpsert->pUpsertTarget; +- iCursor = pTabList->a[0].iCursor; +- if( HasRowid(pTab) +- && pTarget->nExpr==1 +- && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN +- && pTerm->iColumn==XN_ROWID +- ){ +- /* The conflict-target is the rowid of the primary table */ +- assert( pUpsert->pUpsertIdx==0 ); +- return SQLITE_OK; +- } ++ /* Check to see if the conflict target matches the rowid. */ ++ pTab = pTabList->a[0].pSTab; ++ pTarget = pUpsert->pUpsertTarget; ++ iCursor = pTabList->a[0].iCursor; ++ if( HasRowid(pTab) ++ && pTarget->nExpr==1 ++ && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN ++ && pTerm->iColumn==XN_ROWID ++ ){ ++ /* The conflict-target is the rowid of the primary table */ ++ assert( pUpsert->pUpsertIdx==0 ); ++ continue; ++ } + +- /* Initialize sCol[0..1] to be an expression parse tree for a +- ** single column of an index. The sCol[0] node will be the TK_COLLATE +- ** operator and sCol[1] will be the TK_COLUMN operator. Code below +- ** will populate the specific collation and column number values +- ** prior to comparing against the conflict-target expression. +- */ +- memset(sCol, 0, sizeof(sCol)); +- sCol[0].op = TK_COLLATE; +- sCol[0].pLeft = &sCol[1]; +- sCol[1].op = TK_COLUMN; +- sCol[1].iTable = pTabList->a[0].iCursor; ++ /* Initialize sCol[0..1] to be an expression parse tree for a ++ ** single column of an index. The sCol[0] node will be the TK_COLLATE ++ ** operator and sCol[1] will be the TK_COLUMN operator. Code below ++ ** will populate the specific collation and column number values ++ ** prior to comparing against the conflict-target expression. ++ */ ++ memset(sCol, 0, sizeof(sCol)); ++ sCol[0].op = TK_COLLATE; ++ sCol[0].pLeft = &sCol[1]; ++ sCol[1].op = TK_COLUMN; ++ sCol[1].iTable = pTabList->a[0].iCursor; + +- /* Check for matches against other indexes */ +- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ +- int ii, jj, nn; +- if( !IsUniqueIndex(pIdx) ) continue; +- if( pTarget->nExpr!=pIdx->nKeyCol ) continue; +- if( pIdx->pPartIdxWhere ){ +- if( pUpsert->pUpsertTargetWhere==0 ) continue; +- if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, +- pIdx->pPartIdxWhere, iCursor)!=0 ){ +- continue; ++ /* Check for matches against other indexes */ ++ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ ++ int ii, jj, nn; ++ if( !IsUniqueIndex(pIdx) ) continue; ++ if( pTarget->nExpr!=pIdx->nKeyCol ) continue; ++ if( pIdx->pPartIdxWhere ){ ++ if( pUpsert->pUpsertTargetWhere==0 ) continue; ++ if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, ++ pIdx->pPartIdxWhere, iCursor)!=0 ){ ++ continue; ++ } + } +- } +- nn = pIdx->nKeyCol; +- for(ii=0; iiazColl[ii]; +- if( pIdx->aiColumn[ii]==XN_EXPR ){ +- assert( pIdx->aColExpr!=0 ); +- assert( pIdx->aColExpr->nExpr>ii ); +- pExpr = pIdx->aColExpr->a[ii].pExpr; +- if( pExpr->op!=TK_COLLATE ){ +- sCol[0].pLeft = pExpr; ++ nn = pIdx->nKeyCol; ++ for(ii=0; iiazColl[ii]; ++ if( pIdx->aiColumn[ii]==XN_EXPR ){ ++ assert( pIdx->aColExpr!=0 ); ++ assert( pIdx->aColExpr->nExpr>ii ); ++ assert( pIdx->bHasExpr ); ++ pExpr = pIdx->aColExpr->a[ii].pExpr; ++ if( pExpr->op!=TK_COLLATE ){ ++ sCol[0].pLeft = pExpr; ++ pExpr = &sCol[0]; ++ } ++ }else{ ++ sCol[0].pLeft = &sCol[1]; ++ sCol[1].iColumn = pIdx->aiColumn[ii]; + pExpr = &sCol[0]; + } +- }else{ +- sCol[0].pLeft = &sCol[1]; +- sCol[1].iColumn = pIdx->aiColumn[ii]; +- pExpr = &sCol[0]; +- } +- for(jj=0; jja[jj].pExpr, pExpr,iCursor)<2 ){ +- break; /* Column ii of the index matches column jj of target */ ++ for(jj=0; jja[jj].pExpr,pExpr,iCursor)<2 ){ ++ break; /* Column ii of the index matches column jj of target */ ++ } ++ } ++ if( jj>=nn ){ ++ /* The target contains no match for column jj of the index */ ++ break; + } + } +- if( jj>=nn ){ +- /* The target contains no match for column jj of the index */ +- break; ++ if( iipUpsertIdx = pIdx; ++ if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){ ++ /* Really this should be an error. The isDup ON CONFLICT clause will ++ ** never fire. But this problem was not discovered until three years ++ ** after multi-CONFLICT upsert was added, and so we silently ignore ++ ** the problem to prevent breaking applications that might actually ++ ** have redundant ON CONFLICT clauses. */ ++ pUpsert->isDup = 1; ++ } ++ break; + } +- if( iipUpsertIdx==0 ){ ++ char zWhich[16]; ++ if( nClause==0 && pUpsert->pNextUpsert==0 ){ ++ zWhich[0] = 0; ++ }else{ ++ sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1); ++ } ++ sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any " ++ "PRIMARY KEY or UNIQUE constraint", zWhich); ++ return SQLITE_ERROR; + } +- pUpsert->pUpsertIdx = pIdx; +- return SQLITE_OK; + } +- sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any " +- "PRIMARY KEY or UNIQUE constraint"); +- return SQLITE_ERROR; ++ return SQLITE_OK; ++} ++ ++/* ++** Return true if pUpsert is the last ON CONFLICT clause with a ++** conflict target, or if pUpsert is followed by another ON CONFLICT ++** clause that targets the INTEGER PRIMARY KEY. ++*/ ++SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){ ++ Upsert *pNext; ++ if( NEVER(pUpsert==0) ) return 0; ++ pNext = pUpsert->pNextUpsert; ++ while( 1 /*exit-by-return*/ ){ ++ if( pNext==0 ) return 1; ++ if( pNext->pUpsertTarget==0 ) return 1; ++ if( pNext->pUpsertIdx==0 ) return 1; ++ if( !pNext->isDup ) return 0; ++ pNext = pNext->pNextUpsert; ++ } ++ return 0; ++} ++ ++/* ++** Given the list of ON CONFLICT clauses described by pUpsert, and ++** a particular index pIdx, return a pointer to the particular ON CONFLICT ++** clause that applies to the index. Or, if the index is not subject to ++** any ON CONFLICT clause, return NULL. ++*/ ++SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){ ++ while( ++ pUpsert ++ && pUpsert->pUpsertTarget!=0 ++ && pUpsert->pUpsertIdx!=pIdx ++ ){ ++ pUpsert = pUpsert->pNextUpsert; ++ } ++ return pUpsert; + } + + /* +@@ -139114,11 +157076,13 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate( + SrcList *pSrc; /* FROM clause for the UPDATE */ + int iDataCur; + int i; ++ Upsert *pTop = pUpsert; + + assert( v!=0 ); + assert( pUpsert!=0 ); +- VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); + iDataCur = pUpsert->iDataCur; ++ pUpsert = sqlite3UpsertOfIndex(pTop, pIdx); ++ VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); + if( pIdx && iCur!=iDataCur ){ + if( HasRowid(pTab) ){ + int regRowid = sqlite3GetTempReg(pParse); +@@ -139137,7 +157101,7 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate( + k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); + sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i); + VdbeComment((v, "%s.%s", pIdx->zName, +- pTab->aCol[pPk->aiColumn[i]].zName)); ++ pTab->aCol[pPk->aiColumn[i]].zCnName)); + } + sqlite3VdbeVerifyAbortable(v, OE_Abort); + i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk); +@@ -139148,19 +157112,17 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate( + sqlite3VdbeJumpHere(v, i); + } + } +- /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So +- ** we have to make a copy before passing it down into sqlite3Update() */ +- pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0); ++ /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does. ++ ** So we have to make a copy before passing it down into sqlite3Update() */ ++ pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0); + /* excluded.* columns of type REAL need to be converted to a hard real */ + for(i=0; inCol; i++){ + if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ +- sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i); ++ sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i); + } + } +- sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet, +- pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert); +- pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */ +- pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */ ++ sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0), ++ sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert); + VdbeNoopComment((v, "End DO UPDATE of UPSERT")); + } + +@@ -139286,7 +157248,7 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){ + #else + /* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments + ** to VACUUM are silently ignored. This is a back-out of a bug fix that +- ** occurred on 2016-08-19 (https://www.sqlite.org/src/info/083f9e6270). ++ ** occurred on 2016-08-19 (https://sqlite.org/src/info/083f9e6270). + ** The buggy behavior is required for binary compatibility with some + ** legacy applications. */ + iDb = sqlite3FindDb(pParse->db, pNm); +@@ -139321,8 +157283,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + Btree *pTemp; /* The temporary database we vacuum into */ + u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ + u64 saved_flags; /* Saved value of db->flags */ +- int saved_nChange; /* Saved value of db->nChange */ +- int saved_nTotalChange; /* Saved value of db->nTotalChange */ ++ i64 saved_nChange; /* Saved value of db->nChange */ ++ i64 saved_nTotalChange; /* Saved value of db->nTotalChange */ + u32 saved_openFlags; /* Saved value of db->openFlags */ + u8 saved_mTrace; /* Saved trace settings */ + Db *pDb = 0; /* Database to detach at end of vacuum */ +@@ -139331,6 +157293,10 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + int nDb; /* Number of attached databases */ + const char *zDbMain; /* Schema name of database to vacuum */ + const char *zOut; /* Name of output file */ ++ u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */ ++ u64 iRandom; /* Random value used for zDbVacuum[] */ ++ char zDbVacuum[42]; /* Name of the ATTACH-ed database used for vacuum */ ++ + + if( !db->autoCommit ){ + sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); +@@ -139361,7 +157327,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + saved_nChange = db->nChange; + saved_nTotalChange = db->nTotalChange; + saved_mTrace = db->mTrace; +- db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; ++ db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments; + db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; + db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder + | SQLITE_Defensive | SQLITE_CountRows); +@@ -139371,27 +157337,29 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + pMain = db->aDb[iDb].pBt; + isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); + +- /* Attach the temporary database as 'vacuum_db'. The synchronous pragma ++ /* Attach the temporary database as 'vacuum_XXXXXX'. The synchronous pragma + ** can be set to 'off' for this file, as it is not recovered if a crash + ** occurs anyway. The integrity of the database is maintained by a + ** (possibly synchronous) transaction opened on the main database before + ** sqlite3BtreeCopyFile() is called. + ** +- ** An optimisation would be to use a non-journaled pager. +- ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but ++ ** An optimization would be to use a non-journaled pager. ++ ** (Later:) I tried setting "PRAGMA vacuum_XXXXXX.journal_mode=OFF" but + ** that actually made the VACUUM run slower. Very little journalling + ** actually occurs when doing a vacuum since the vacuum_db is initially + ** empty. Only the journal header is written. Apparently it takes more + ** time to parse and run the PRAGMA to turn journalling off than it does + ** to write the journal header file. + */ ++ sqlite3_randomness(sizeof(iRandom),&iRandom); ++ sqlite3_snprintf(sizeof(zDbVacuum), zDbVacuum, "vacuum_%016llx", iRandom); + nDb = db->nDb; +- rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut); ++ rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS %s", zOut, zDbVacuum); + db->openFlags = saved_openFlags; + if( rc!=SQLITE_OK ) goto end_of_vacuum; + assert( (db->nDb-1)==nDb ); + pDb = &db->aDb[nDb]; +- assert( strcmp(pDb->zDbSName,"vacuum_db")==0 ); ++ assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); + pTemp = pDb->pBt; + if( pOut ){ + sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); +@@ -139402,12 +157370,17 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + goto end_of_vacuum; + } + db->mDbFlags |= DBFLAG_VacuumInto; ++ ++ /* For a VACUUM INTO, the pager-flags are set to the same values as ++ ** they are for the database being vacuumed, except that PAGER_CACHESPILL ++ ** is always set. */ ++ pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); + } + nRes = sqlite3BtreeGetRequestedReserve(pMain); + + sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); + sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); +- sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); ++ sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); + + /* Begin a transaction and take an exclusive lock on the main database + ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below, +@@ -139420,7 +157393,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + + /* Do not attempt to change the page size for a WAL database */ + if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) +- ==PAGER_JOURNALMODE_WAL ){ ++ ==PAGER_JOURNALMODE_WAL ++ && pOut==0 ++ ){ + db->nextPagesize = 0; + } + +@@ -139461,11 +157436,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + ** the contents to the temporary database. + */ + rc = execSqlF(db, pzErrMsg, +- "SELECT'INSERT INTO vacuum_db.'||quote(name)" ++ "SELECT'INSERT INTO %s.'||quote(name)" + "||' SELECT*FROM\"%w\".'||quote(name)" +- "FROM vacuum_db.sqlite_schema " ++ "FROM %s.sqlite_schema " + "WHERE type='table'AND coalesce(rootpage,1)>0", +- zDbMain ++ zDbVacuum, zDbMain, zDbVacuum + ); + assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); + db->mDbFlags &= ~DBFLAG_Vacuum; +@@ -139477,11 +157452,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + ** from the schema table. + */ + rc = execSqlF(db, pzErrMsg, +- "INSERT INTO vacuum_db.sqlite_schema" ++ "INSERT INTO %s.sqlite_schema" + " SELECT*FROM \"%w\".sqlite_schema" + " WHERE type IN('view','trigger')" + " OR(type='table'AND rootpage=0)", +- zDbMain ++ zDbVacuum, zDbMain + ); + if( rc ) goto end_of_vacuum; + +@@ -139509,8 +157484,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( + BTREE_APPLICATION_ID, 0, /* Preserve the application id */ + }; + +- assert( 1==sqlite3BtreeIsInTrans(pTemp) ); +- assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) ); ++ assert( SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pTemp) ); ++ assert( pOut!=0 || SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pMain) ); + + /* Copy Btree meta values */ + for(i=0; ipVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); ++ for(pVtab=pTab->u.vtab.p; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); + return pVtab; + } + +@@ -139782,36 +157758,40 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){ + + assert( db ); + assert( pVTab->nRef>0 ); +- assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE ); ++ assert( db->eOpenState==SQLITE_STATE_OPEN ++ || db->eOpenState==SQLITE_STATE_ZOMBIE ); + + pVTab->nRef--; + if( pVTab->nRef==0 ){ + sqlite3_vtab *p = pVTab->pVtab; +- sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod); + if( p ){ + p->pModule->xDisconnect(p); + } ++ sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod); + sqlite3DbFree(db, pVTab); + } + } + + /* + ** Table p is a virtual table. This function moves all elements in the +-** p->pVTable list to the sqlite3.pDisconnect lists of their associated ++** p->u.vtab.p list to the sqlite3.pDisconnect lists of their associated + ** database connections to be disconnected at the next opportunity. + ** Except, if argument db is not NULL, then the entry associated with +-** connection db is left in the p->pVTable list. ++** connection db is left in the p->u.vtab.p list. + */ + static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ + VTable *pRet = 0; +- VTable *pVTable = p->pVTable; +- p->pVTable = 0; ++ VTable *pVTable; ++ ++ assert( IsVirtual(p) ); ++ pVTable = p->u.vtab.p; ++ p->u.vtab.p = 0; + + /* Assert that the mutex (if any) associated with the BtShared database + ** that contains table p is held by the caller. See header comments + ** above function sqlite3VtabUnlockList() for an explanation of why + ** this makes it safe to access the sqlite3.pDisconnect list of any +- ** database connection that may have an entry in the p->pVTable list. ++ ** database connection that may have an entry in the p->u.vtab.p list. + */ + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); + +@@ -139821,7 +157801,7 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ + assert( db2 ); + if( db2==db ){ + pRet = pVTable; +- p->pVTable = pRet; ++ p->u.vtab.p = pRet; + pRet->pNext = 0; + }else{ + pVTable->pNext = db2->pDisconnect; +@@ -139849,7 +157829,7 @@ SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){ + assert( sqlite3BtreeHoldsAllMutexes(db) ); + assert( sqlite3_mutex_held(db->mutex) ); + +- for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){ ++ for(ppVTab=&p->u.vtab.p; *ppVTab; ppVTab=&(*ppVTab)->pNext){ + if( (*ppVTab)->db==db ){ + VTable *pVTab = *ppVTab; + *ppVTab = pVTab->pNext; +@@ -139888,7 +157868,6 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){ + + if( p ){ + db->pDisconnect = 0; +- sqlite3ExpirePreparedStatements(db, 0); + do { + VTable *pNext = p->pNext; + sqlite3VtabUnlock(p); +@@ -139912,37 +157891,42 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){ + ** database connection. + */ + SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){ +- if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); +- if( p->azModuleArg ){ ++ assert( IsVirtual(p) ); ++ assert( db!=0 ); ++ if( db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); ++ if( p->u.vtab.azArg ){ + int i; +- for(i=0; inModuleArg; i++){ +- if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]); ++ for(i=0; iu.vtab.nArg; i++){ ++ if( i!=1 ) sqlite3DbFree(db, p->u.vtab.azArg[i]); + } +- sqlite3DbFree(db, p->azModuleArg); ++ sqlite3DbFree(db, p->u.vtab.azArg); + } + } + + /* +-** Add a new module argument to pTable->azModuleArg[]. ++** Add a new module argument to pTable->u.vtab.azArg[]. + ** The string is not copied - the pointer is stored. The + ** string will be freed automatically when the table is + ** deleted. + */ + static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){ +- sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg); ++ sqlite3_int64 nBytes; + char **azModuleArg; + sqlite3 *db = pParse->db; +- if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ ++ ++ assert( IsVirtual(pTable) ); ++ nBytes = sizeof(char *)*(2+pTable->u.vtab.nArg); ++ if( pTable->u.vtab.nArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ + sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName); + } +- azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); ++ azModuleArg = sqlite3DbRealloc(db, pTable->u.vtab.azArg, nBytes); + if( azModuleArg==0 ){ + sqlite3DbFree(db, zArg); + }else{ +- int i = pTable->nModuleArg++; ++ int i = pTable->u.vtab.nArg++; + azModuleArg[i] = zArg; + azModuleArg[i+1] = 0; +- pTable->azModuleArg = azModuleArg; ++ pTable->u.vtab.azArg = azModuleArg; + } + } + +@@ -139965,10 +157949,11 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( + pTable = pParse->pNewTable; + if( pTable==0 ) return; + assert( 0==pTable->pIndex ); ++ pTable->eTabType = TABTYP_VTAB; + + db = pParse->db; + +- assert( pTable->nModuleArg==0 ); ++ assert( pTable->u.vtab.nArg==0 ); + addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName)); + addModuleArgument(pParse, pTable, 0); + addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName)); +@@ -139985,11 +157970,11 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( + ** sqlite_schema table, has already been made by sqlite3StartTable(). + ** The second call, to obtain permission to create the table, is made now. + */ +- if( pTable->azModuleArg ){ ++ if( pTable->u.vtab.azArg ){ + int iDb = sqlite3SchemaToIndex(db, pTable->pSchema); + assert( iDb>=0 ); /* The database the table is being created in */ + sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, +- pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName); ++ pTable->u.vtab.azArg[0], pParse->db->aDb[iDb].zDbSName); + } + #endif + } +@@ -140017,9 +158002,10 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ + sqlite3 *db = pParse->db; /* The database connection */ + + if( pTab==0 ) return; ++ assert( IsVirtual(pTab) ); + addArgumentToVtab(pParse); + pParse->sArg.z = 0; +- if( pTab->nModuleArg<1 ) return; ++ if( pTab->u.vtab.nArg<1 ) return; + + /* If the CREATE VIRTUAL TABLE statement is being entered for the + ** first time (in other words if the virtual table is actually being +@@ -140046,44 +158032,41 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ + ** schema table. We just need to update that slot with all + ** the information we've collected. + ** +- ** The VM register number pParse->regRowid holds the rowid of an +- ** entry in the sqlite_schema table tht was created for this vtab ++ ** The VM register number pParse->u1.cr.regRowid holds the rowid of an ++ ** entry in the sqlite_schema table that was created for this vtab + ** by sqlite3StartTable(). + */ + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); ++ assert( pParse->isCreate ); + sqlite3NestedParse(pParse, +- "UPDATE %Q." DFLT_SCHEMA_TABLE " " ++ "UPDATE %Q." LEGACY_SCHEMA_TABLE " " + "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " + "WHERE rowid=#%d", + db->aDb[iDb].zDbSName, + pTab->zName, + pTab->zName, + zStmt, +- pParse->regRowid ++ pParse->u1.cr.regRowid + ); + v = sqlite3GetVdbe(pParse); + sqlite3ChangeCookie(pParse, iDb); + + sqlite3VdbeAddOp0(v, OP_Expire); + zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt); +- sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); ++ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0); + sqlite3DbFree(db, zStmt); + + iReg = ++pParse->nMem; + sqlite3VdbeLoadString(v, iReg, pTab->zName); + sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg); +- } +- +- /* If we are rereading the sqlite_schema table create the in-memory +- ** record of the table. The xConnect() method is not called until +- ** the first time the virtual table is used in an SQL statement. This +- ** allows a schema that contains virtual tables to be loaded before +- ** the required virtual table implementations are registered. */ +- else { ++ }else{ ++ /* If we are rereading the sqlite_schema table create the in-memory ++ ** record of the table. */ + Table *pOld; + Schema *pSchema = pTab->pSchema; + const char *zName = pTab->zName; +- assert( sqlite3SchemaMutexHeld(db, 0, pSchema) ); ++ assert( zName!=0 ); ++ sqlite3MarkAllShadowTablesOf(db, pTab); + pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab); + if( pOld ){ + sqlite3OomFault(db); +@@ -140134,13 +158117,16 @@ static int vtabCallConstructor( + VtabCtx sCtx; + VTable *pVTable; + int rc; +- const char *const*azArg = (const char *const*)pTab->azModuleArg; +- int nArg = pTab->nModuleArg; ++ const char *const*azArg; ++ int nArg = pTab->u.vtab.nArg; + char *zErr = 0; + char *zModuleName; + int iDb; + VtabCtx *pCtx; + ++ assert( IsVirtual(pTab) ); ++ azArg = (const char *const*)pTab->u.vtab.azArg; ++ + /* Check that the virtual-table is not already being initialized */ + for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){ + if( pCtx->pTab==pTab ){ +@@ -140167,7 +158153,7 @@ static int vtabCallConstructor( + pVTable->eVtabRisk = SQLITE_VTABRISK_Normal; + + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); +- pTab->azModuleArg[1] = db->aDb[iDb].zDbSName; ++ pTab->u.vtab.azArg[1] = db->aDb[iDb].zDbSName; + + /* Invoke the virtual table constructor */ + assert( &db->pVtabCtx ); +@@ -140177,7 +158163,11 @@ static int vtabCallConstructor( + sCtx.pPrior = db->pVtabCtx; + sCtx.bDeclared = 0; + db->pVtabCtx = &sCtx; ++ pTab->nTabRef++; + rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); ++ assert( pTab!=0 ); ++ assert( pTab->nTabRef>1 || rc!=SQLITE_OK ); ++ sqlite3DeleteTable(db, pTab); + db->pVtabCtx = sCtx.pPrior; + if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); + assert( sCtx.pTab==pTab ); +@@ -140199,19 +158189,19 @@ static int vtabCallConstructor( + pVTable->nRef = 1; + if( sCtx.bDeclared==0 ){ + const char *zFormat = "vtable constructor did not declare schema: %s"; +- *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); ++ *pzErr = sqlite3MPrintf(db, zFormat, zModuleName); + sqlite3VtabUnlock(pVTable); + rc = SQLITE_ERROR; + }else{ + int iCol; + u16 oooHidden = 0; + /* If everything went according to plan, link the new VTable structure +- ** into the linked list headed by pTab->pVTable. Then loop through the ++ ** into the linked list headed by pTab->u.vtab.p. Then loop through the + ** columns of the table to see if any of them contain the token "hidden". + ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from + ** the type string. */ +- pVTable->pNext = pTab->pVTable; +- pTab->pVTable = pVTable; ++ pVTable->pNext = pTab->u.vtab.p; ++ pTab->u.vtab.p = pVTable; + + for(iCol=0; iColnCol; iCol++){ + char *zType = sqlite3ColumnType(&pTab->aCol[iCol], ""); +@@ -140237,6 +158227,7 @@ static int vtabCallConstructor( + zType[i-1] = '\0'; + } + pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN; ++ pTab->tabFlags |= TF_HasHidden; + oooHidden = TF_OOOHidden; + }else{ + pTab->tabFlags |= oooHidden; +@@ -140263,16 +158254,17 @@ SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ + int rc; + + assert( pTab ); +- if( !IsVirtual(pTab) || sqlite3GetVTable(db, pTab) ){ ++ assert( IsVirtual(pTab) ); ++ if( sqlite3GetVTable(db, pTab) ){ + return SQLITE_OK; + } + + /* Locate the required virtual table module */ +- zMod = pTab->azModuleArg[0]; ++ zMod = pTab->u.vtab.azArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); + + if( !pMod ){ +- const char *zModule = pTab->azModuleArg[0]; ++ const char *zModule = pTab->u.vtab.azArg[0]; + sqlite3ErrorMsg(pParse, "no such module: %s", zModule); + rc = SQLITE_ERROR; + }else{ +@@ -140335,10 +158327,10 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, + const char *zMod; + + pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); +- assert( pTab && IsVirtual(pTab) && !pTab->pVTable ); ++ assert( pTab && IsVirtual(pTab) && !pTab->u.vtab.p ); + + /* Locate the required virtual table module */ +- zMod = pTab->azModuleArg[0]; ++ zMod = pTab->u.vtab.azArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); + + /* If the module has been registered and includes a Create method, +@@ -140373,39 +158365,67 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ + VtabCtx *pCtx; + int rc = SQLITE_OK; + Table *pTab; +- char *zErr = 0; + Parse sParse; ++ int initBusy; ++ int i; ++ const unsigned char *z; ++ static const u8 aKeyword[] = { TK_CREATE, TK_TABLE, 0 }; + + #ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ + return SQLITE_MISUSE_BKPT; + } + #endif ++ ++ /* Verify that the first two keywords in the CREATE TABLE statement ++ ** really are "CREATE" and "TABLE". If this is not the case, then ++ ** sqlite3_declare_vtab() is being misused. ++ */ ++ z = (const unsigned char*)zCreateTable; ++ for(i=0; aKeyword[i]; i++){ ++ int tokenType = 0; ++ do{ ++ z += sqlite3GetToken(z, &tokenType); ++ }while( tokenType==TK_SPACE || tokenType==TK_COMMENT ); ++ if( tokenType!=aKeyword[i] ){ ++ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); ++ return SQLITE_ERROR; ++ } ++ } ++ + sqlite3_mutex_enter(db->mutex); + pCtx = db->pVtabCtx; + if( !pCtx || pCtx->bDeclared ){ +- sqlite3Error(db, SQLITE_MISUSE); ++ sqlite3Error(db, SQLITE_MISUSE_BKPT); + sqlite3_mutex_leave(db->mutex); + return SQLITE_MISUSE_BKPT; + } ++ + pTab = pCtx->pTab; + assert( IsVirtual(pTab) ); + +- memset(&sParse, 0, sizeof(sParse)); ++ sqlite3ParseObjectInit(&sParse, db); + sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; +- sParse.db = db; ++ sParse.disableTriggers = 1; ++ /* We should never be able to reach this point while loading the ++ ** schema. Nevertheless, defend against that (turn off db->init.busy) ++ ** in case a bug arises. */ ++ assert( db->init.busy==0 ); ++ initBusy = db->init.busy; ++ db->init.busy = 0; + sParse.nQueryLoop = 1; +- if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) +- && sParse.pNewTable +- && !db->mallocFailed +- && !sParse.pNewTable->pSelect +- && !IsVirtual(sParse.pNewTable) +- ){ ++ if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) ){ ++ assert( sParse.pNewTable!=0 ); ++ assert( !db->mallocFailed ); ++ assert( IsOrdinaryTable(sParse.pNewTable) ); ++ assert( sParse.zErrMsg==0 ); + if( !pTab->aCol ){ + Table *pNew = sParse.pNewTable; + Index *pIdx; + pTab->aCol = pNew->aCol; +- pTab->nCol = pNew->nCol; ++ assert( IsOrdinaryTable(pNew) ); ++ sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); ++ pTab->nNVCol = pTab->nCol = pNew->nCol; + pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); + pNew->nCol = 0; + pNew->aCol = 0; +@@ -140429,8 +158449,9 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ + } + pCtx->bDeclared = 1; + }else{ +- sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); +- sqlite3DbFree(db, zErr); ++ sqlite3ErrorWithMsg(db, SQLITE_ERROR, ++ (sParse.zErrMsg ? "%s" : 0), sParse.zErrMsg); ++ sqlite3DbFree(db, sParse.zErrMsg); + rc = SQLITE_ERROR; + } + sParse.eParseMode = PARSE_MODE_NORMAL; +@@ -140439,7 +158460,8 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ + sqlite3VdbeFinalize(sParse.pVdbe); + } + sqlite3DeleteTable(db, sParse.pNewTable); +- sqlite3ParserReset(&sParse); ++ sqlite3ParseObjectReset(&sParse); ++ db->init.busy = initBusy; + + assert( (rc&0xff)==rc ); + rc = sqlite3ApiExit(db, rc); +@@ -140459,10 +158481,13 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab + Table *pTab; + + pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); +- if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){ ++ if( ALWAYS(pTab!=0) ++ && ALWAYS(IsVirtual(pTab)) ++ && ALWAYS(pTab->u.vtab.p!=0) ++ ){ + VTable *p; + int (*xDestroy)(sqlite3_vtab *); +- for(p=pTab->pVTable; p; p=p->pNext){ ++ for(p=pTab->u.vtab.p; p; p=p->pNext){ + assert( p->pVtab ); + if( p->pVtab->nRef>0 ){ + return SQLITE_LOCKED; +@@ -140476,9 +158501,9 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab + rc = xDestroy(p->pVtab); + /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ + if( rc==SQLITE_OK ){ +- assert( pTab->pVTable==p && p->pNext==0 ); ++ assert( pTab->u.vtab.p==p && p->pNext==0 ); + p->pVtab = 0; +- pTab->pVTable = 0; ++ pTab->u.vtab.p = 0; + sqlite3VtabUnlock(p); + } + sqlite3DeleteTable(db, pTab); +@@ -140653,7 +158678,10 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ + break; + } + if( xMethod && pVTab->iSavepoint>iSavepoint ){ ++ u64 savedFlags = (db->flags & SQLITE_Defensive); ++ db->flags &= ~(u64)SQLITE_Defensive; + rc = xMethod(pVTab->pVtab, iSavepoint); ++ db->flags |= savedFlags; + } + sqlite3VtabUnlock(pVTab); + } +@@ -140692,8 +158720,9 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction( + /* Check to see the left operand is a column in a virtual table */ + if( NEVER(pExpr==0) ) return pDef; + if( pExpr->op!=TK_COLUMN ) return pDef; ++ assert( ExprUseYTab(pExpr) ); + pTab = pExpr->y.pTab; +- if( pTab==0 ) return pDef; ++ if( NEVER(pTab==0) ) return pDef; + if( !IsVirtual(pTab) ) return pDef; + pVtab = sqlite3GetVTable(db, pTab)->pVtab; + assert( pVtab!=0 ); +@@ -140766,12 +158795,13 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ + /* + ** Check to see if virtual table module pMod can be have an eponymous + ** virtual table instance. If it can, create one if one does not already +-** exist. Return non-zero if the eponymous virtual table instance exists +-** when this routine returns, and return zero if it does not exist. ++** exist. Return non-zero if either the eponymous virtual table instance ++** exists when this routine returns or if an attempt to create it failed ++** and an error message was left in pParse. + ** + ** An eponymous virtual table instance is one that is named after its + ** module, and more importantly, does not require a CREATE VIRTUAL TABLE +-** statement in order to come into existance. Eponymous virtual table ++** statement in order to come into existence. Eponymous virtual table + ** instances always exist. They cannot be DROP-ed. + ** + ** Any virtual table module for which xConnect and xCreate are the same +@@ -140794,9 +158824,11 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ + } + pMod->pEpoTab = pTab; + pTab->nTabRef = 1; ++ pTab->eTabType = TABTYP_VTAB; + pTab->pSchema = db->aDb[0].pSchema; +- assert( pTab->nModuleArg==0 ); ++ assert( pTab->u.vtab.nArg==0 ); + pTab->iPKey = -1; ++ pTab->tabFlags |= TF_Eponymous; + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); + addModuleArgument(pParse, pTab, 0); + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); +@@ -140805,7 +158837,6 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ + sqlite3ErrorMsg(pParse, "%s", zErr); + sqlite3DbFree(db, zErr); + sqlite3VtabEponymousTableClear(db, pMod); +- return 0; + } + return 1; + } +@@ -140879,6 +158910,10 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ + p->pVTable->eVtabRisk = SQLITE_VTABRISK_High; + break; + } ++ case SQLITE_VTAB_USES_ALL_SCHEMAS: { ++ p->pVTable->bAllSchemas = 1; ++ break; ++ } + default: { + rc = SQLITE_MISUSE_BKPT; + break; +@@ -140937,19 +158972,6 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ + #ifndef SQLITE_WHEREINT_H + #define SQLITE_WHEREINT_H + +-/* +-** Trace output macros +-*/ +-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) +-/***/ extern int sqlite3WhereTrace; +-#endif +-#if defined(SQLITE_DEBUG) \ +- && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) +-# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X +-# define WHERETRACE_ENABLED 1 +-#else +-# define WHERETRACE(K,X) +-#endif + + /* Forward references + */ +@@ -140965,6 +158987,28 @@ typedef struct WhereLoopBuilder WhereLoopBuilder; + typedef struct WhereScan WhereScan; + typedef struct WhereOrCost WhereOrCost; + typedef struct WhereOrSet WhereOrSet; ++typedef struct WhereMemBlock WhereMemBlock; ++typedef struct WhereRightJoin WhereRightJoin; ++ ++/* ++** This object is a header on a block of allocated memory that will be ++** automatically freed when its WInfo object is destructed. ++*/ ++struct WhereMemBlock { ++ WhereMemBlock *pNext; /* Next block in the chain */ ++ u64 sz; /* Bytes of space */ ++}; ++ ++/* ++** Extra information attached to a WhereLevel that is a RIGHT JOIN. ++*/ ++struct WhereRightJoin { ++ int iMatch; /* Cursor used to determine prior matched rows */ ++ int regBloom; /* Bloom filter for iRJMatch */ ++ int regReturn; /* Return register for the interior subroutine */ ++ int addrSubrtn; /* Starting address for the interior subroutine */ ++ int endSubrtn; /* The last opcode in the interior subroutine */ ++}; + + /* + ** This object contains information needed to implement a single nested +@@ -140997,6 +159041,8 @@ struct WhereLevel { + u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */ + int addrLikeRep; /* LIKE range processing address */ + #endif ++ int regFilter; /* Bloom filter */ ++ WhereRightJoin *pRJ; /* Extra information for RIGHT JOIN */ + u8 iFrom; /* Which entry in the FROM clause */ + u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ + int p1, p2; /* Operands of the opcode used to end the loop */ +@@ -141007,11 +159053,11 @@ struct WhereLevel { + int iCur; /* The VDBE cursor used by this IN operator */ + int addrInTop; /* Top of the IN loop */ + int iBase; /* Base register of multi-key index record */ +- int nPrefix; /* Number of prior entires in the key */ ++ int nPrefix; /* Number of prior entries in the key */ + u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ + } *aInLoop; /* Information about each nested IN operator */ + } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */ +- Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ ++ Index *pCoveringIdx; /* Possible covering index for WHERE_MULTI_OR */ + } u; + struct WhereLoop *pWLoop; /* The selected WhereLoop object */ + Bitmask notReady; /* FROM entries not usable at this level */ +@@ -141052,13 +159098,17 @@ struct WhereLoop { + u16 nTop; /* Size of TOP vector */ + u16 nDistinctCol; /* Index columns used to sort for DISTINCT */ + Index *pIndex; /* Index used, or NULL */ ++ ExprList *pOrderBy; /* ORDER BY clause if this is really a subquery */ + } btree; + struct { /* Information for virtual tables */ + int idxNum; /* Index number */ +- u8 needFree; /* True if sqlite3_free(idxStr) is needed */ ++ u32 needFree : 1; /* True if sqlite3_free(idxStr) is needed */ ++ u32 bOmitOffset : 1; /* True to let virtual table handle offset */ ++ u32 bIdxNumHex : 1; /* Show idxNum as hex in EXPLAIN QUERY PLAN */ + i8 isOrdered; /* True if satisfies ORDER BY */ + u16 omitMask; /* Terms that may be omitted */ + char *idxStr; /* Index identifier string */ ++ u32 mHandleIn; /* Terms to handle as IN(...) instead of == */ + } vtab; + } u; + u32 wsFlags; /* WHERE_* flags describing the plan */ +@@ -141067,6 +159117,10 @@ struct WhereLoop { + /**** whereLoopXfer() copies fields above ***********************/ + # define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot) + u16 nLSlot; /* Number of slots allocated for aLTerm[] */ ++#ifdef WHERETRACE_ENABLED ++ LogEst rStarDelta; /* Cost delta due to star-schema heuristic. Not ++ ** initialized unless pWInfo->bStarUsed */ ++#endif + WhereTerm **aLTerm; /* WhereTerms used */ + WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */ + WhereTerm *aLTermSpace[3]; /* Initial aLTerm[] space */ +@@ -141115,7 +159169,7 @@ struct WherePath { + Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */ + LogEst nRow; /* Estimated number of rows generated by this path */ + LogEst rCost; /* Total cost of this path */ +- LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */ ++ LogEst rUnsort; /* Total cost of this path ignoring sorting costs */ + i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */ + WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */ + }; +@@ -141181,9 +159235,11 @@ struct WhereTerm { + u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */ + int iParent; /* Disable pWC->a[iParent] when this term disabled */ + int leftCursor; /* Cursor number of X in "X " */ +- int iField; /* Field in (?,?,?) IN (SELECT...) vector */ + union { +- int leftColumn; /* Column number of X in "X " */ ++ struct { ++ int leftColumn; /* Column number of X in "X " */ ++ int iField; /* Field in (?,?,?) IN (SELECT...) vector */ ++ } x; /* Opcode other than OP_OR or OP_AND */ + WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ + WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ + } u; +@@ -141200,12 +159256,8 @@ struct WhereTerm { + #define TERM_COPIED 0x0008 /* Has a child */ + #define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */ + #define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */ +-#define TERM_OR_OK 0x0040 /* Used during OR-clause processing */ +-#ifdef SQLITE_ENABLE_STAT4 +-# define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */ +-#else +-# define TERM_VNULL 0x0000 /* Disabled if not using stat4 */ +-#endif ++#define TERM_OK 0x0040 /* Used during OR-clause processing */ ++#define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */ + #define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */ + #define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */ + #define TERM_LIKE 0x0400 /* The original LIKE operator */ +@@ -141217,6 +159269,7 @@ struct WhereTerm { + #else + # define TERM_HIGHTRUTH 0 /* Only used with STAT4 */ + #endif ++#define TERM_SLICE 0x8000 /* One slice of a row-value/vector comparison */ + + /* + ** An instance of the WhereScan object is used as an iterator for locating +@@ -141227,11 +159280,11 @@ struct WhereScan { + WhereClause *pWC; /* WhereClause currently being scanned */ + const char *zCollName; /* Required collating sequence, if not NULL */ + Expr *pIdxExpr; /* Search for this index expression */ +- char idxaff; /* Must match this affinity, if zCollName!=NULL */ +- unsigned char nEquiv; /* Number of entries in aEquiv[] */ +- unsigned char iEquiv; /* Next unused slot in aEquiv[] */ +- u32 opMask; /* Acceptable operators */ + int k; /* Resume scanning at this->pWC->a[this->k] */ ++ u32 opMask; /* Acceptable operators */ ++ char idxaff; /* Must match this affinity, if zCollName!=NULL */ ++ unsigned char iEquiv; /* Current slot in aiCur[] and aiColumn[] */ ++ unsigned char nEquiv; /* Number of entries in aiCur[] and aiColumn[] */ + int aiCur[11]; /* Cursors in the equivalence class */ + i16 aiColumn[11]; /* Corresponding column number in the eq-class */ + }; +@@ -141255,7 +159308,8 @@ struct WhereClause { + u8 hasOr; /* True if any a[].eOperator is WO_OR */ + int nTerm; /* Number of terms */ + int nSlot; /* Number of entries in a[] */ +- WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ ++ int nBase; /* Number of terms through the last non-Virtual */ ++ WhereTerm *a; /* Each a[] describes a term of the WHERE clause */ + #if defined(SQLITE_SMALL_STACK) + WhereTerm aStatic[1]; /* Initial static space for a[] */ + #else +@@ -141285,7 +159339,7 @@ struct WhereAndInfo { + ** between VDBE cursor numbers and bits of the bitmasks in WhereTerm. + ** + ** The VDBE cursor numbers are small integers contained in +-** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE ++** SrcItem.iCursor and Expr.iTable fields. For any given WHERE + ** clause, the cursor numbers might not begin with 0 and they might + ** contain gaps in the numbering sequence. But we want to make maximum + ** use of the bits in our bitmasks. This structure provides a mapping +@@ -141312,11 +159366,6 @@ struct WhereMaskSet { + int ix[BMS]; /* Cursor assigned to each bit */ + }; + +-/* +-** Initialize a WhereMaskSet object +-*/ +-#define initMaskSet(P) (P)->n=0 +- + /* + ** This object is a convenience wrapper holding all information needed + ** to construct WhereLoop objects for a particular query. +@@ -141324,7 +159373,6 @@ struct WhereMaskSet { + struct WhereLoopBuilder { + WhereInfo *pWInfo; /* Information about this WHERE */ + WhereClause *pWC; /* WHERE clause terms */ +- ExprList *pOrderBy; /* ORDER BY clause */ + WhereLoop *pNew; /* Template WhereLoop */ + WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ + #ifdef SQLITE_ENABLE_STAT4 +@@ -141362,20 +159410,6 @@ struct WhereLoopBuilder { + # define SQLITE_QUERY_PLANNER_LIMIT_INCR 1000 + #endif + +-/* +-** Each instance of this object records a change to a single node +-** in an expression tree to cause that node to point to a column +-** of an index rather than an expression or a virtual column. All +-** such transformations need to be undone at the end of WHERE clause +-** processing. +-*/ +-typedef struct WhereExprMod WhereExprMod; +-struct WhereExprMod { +- WhereExprMod *pNext; /* Next translation on a list of them all */ +- Expr *pExpr; /* The Expr node that was transformed */ +- Expr orig; /* Original value of the Expr node */ +-}; +- + /* + ** The WHERE clause processing routine has two halves. The + ** first part does the start of the WHERE loop and the second +@@ -141391,7 +159425,10 @@ struct WhereInfo { + SrcList *pTabList; /* List of tables in the join */ + ExprList *pOrderBy; /* The ORDER BY clause or NULL */ + ExprList *pResultSet; /* Result set of the query */ ++#if WHERETRACE_ENABLED + Expr *pWhere; /* The complete WHERE clause */ ++#endif ++ Select *pSelect; /* The entire SELECT statement containing WHERE */ + int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ + int iContinue; /* Jump here to continue with next record */ + int iBreak; /* Jump here to break out of the loop */ +@@ -141405,17 +159442,28 @@ struct WhereInfo { + unsigned bDeferredSeek :1; /* Uses OP_DeferredSeek */ + unsigned untestedTerms :1; /* Not all WHERE terms resolved by outer loop */ + unsigned bOrderedInnerLoop:1;/* True if only the inner-most loop is ordered */ +- unsigned sorted :1; /* True if really sorted (not just grouped) */ ++ unsigned sorted :1; /* True if really sorted (not just grouped) */ ++ unsigned bStarDone :1; /* True if check for star-query is complete */ ++ unsigned bStarUsed :1; /* True if star-query heuristic is used */ + LogEst nRowOut; /* Estimated number of output rows */ ++#ifdef WHERETRACE_ENABLED ++ LogEst rTotalCost; /* Total cost of the solution */ ++#endif + int iTop; /* The very beginning of the WHERE loop */ ++ int iEndWhere; /* End of the WHERE clause itself */ + WhereLoop *pLoops; /* List of all WhereLoop objects */ +- WhereExprMod *pExprMods; /* Expression modifications */ ++ WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ + Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ + WhereClause sWC; /* Decomposition of the WHERE clause */ + WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ +- WhereLevel a[1]; /* Information about each nest loop in WHERE */ ++ WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */ + }; + ++/* ++** The size (in bytes) of a WhereInfo object that holds N WhereLevels. ++*/ ++#define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel)) ++ + /* + ** Private interfaces - callable only by other where.c routines. + ** +@@ -141425,7 +159473,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); + #ifdef WHERETRACE_ENABLED + SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC); + SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm); +-SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC); ++SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC); + #endif + SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm( + WhereClause *pWC, /* The WHERE clause to be searched */ +@@ -141435,6 +159483,8 @@ SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm( + u32 op, /* Mask of WO_xx values describing operator */ + Index *pIdx /* Must be compatible with this index, if not NULL */ + ); ++SQLITE_PRIVATE void *sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte); ++SQLITE_PRIVATE void *sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte); + + /* wherecode.c: */ + #ifndef SQLITE_OMIT_EXPLAIN +@@ -141444,8 +159494,22 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( + WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ + u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ + ); ++SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( ++ const Parse *pParse, /* Parse context */ ++ const WhereInfo *pWInfo, /* WHERE clause */ ++ const WhereLevel *pLevel /* Bloom filter on this level */ ++); ++SQLITE_PRIVATE void sqlite3WhereAddExplainText( ++ Parse *pParse, /* Parse context */ ++ int addr, ++ SrcList *pTabList, /* Table list this loop refers to */ ++ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ ++ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ++); + #else + # define sqlite3WhereExplainOneScan(u,v,w,x) 0 ++# define sqlite3WhereExplainBloomFilter(u,v,w) 0 ++# define sqlite3WhereAddExplainText(u,v,w,x,y) + #endif /* SQLITE_OMIT_EXPLAIN */ + #ifdef SQLITE_ENABLE_STMT_SCANSTATUS + SQLITE_PRIVATE void sqlite3WhereAddScanStatus( +@@ -141465,16 +159529,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + WhereLevel *pLevel, /* The current level pointer */ + Bitmask notReady /* Which tables are currently available */ + ); ++SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( ++ WhereInfo *pWInfo, ++ int iLevel, ++ WhereLevel *pLevel ++); + + /* whereexpr.c: */ + SQLITE_PRIVATE void sqlite3WhereClauseInit(WhereClause*,WhereInfo*); + SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause*); + SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause*,Expr*,u8); ++SQLITE_PRIVATE void sqlite3WhereAddLimit(WhereClause*, Select*); + SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*); + SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*); + SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); + SQLITE_PRIVATE void sqlite3WhereExprAnalyze(SrcList*, WhereClause*); +-SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*); ++SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); + + + +@@ -141506,8 +159576,9 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC + #define WO_AND 0x0400 /* Two or more AND-connected terms */ + #define WO_EQUIV 0x0800 /* Of the form A==B, both columns */ + #define WO_NOOP 0x1000 /* This term does not restrict search space */ ++#define WO_ROWVAL 0x2000 /* A row-value term */ + +-#define WO_ALL 0x1fff /* Mask of all possible WO_* values */ ++#define WO_ALL 0x3fff /* Mask of all possible WO_* values */ + #define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */ + + /* +@@ -141536,6 +159607,14 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC + #define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */ + #define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */ + #define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */ ++#define WHERE_IN_SEEKSCAN 0x00100000 /* Seek-scan optimization for IN */ ++#define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */ ++#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ ++#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ ++#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ ++#define WHERE_COROUTINE 0x02000000 /* Implemented by co-routine. ++ ** NB: False-negatives are possible */ ++#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */ + + #endif /* !defined(SQLITE_WHEREINT_H) */ + +@@ -141551,7 +159630,7 @@ static const char *explainIndexColumnName(Index *pIdx, int i){ + i = pIdx->aiColumn[i]; + if( i==XN_EXPR ) return ""; + if( i==XN_ROWID ) return "rowid"; +- return pIdx->pTable->aCol[i].zName; ++ return pIdx->pTable->aCol[i].zCnName; + } + + /* +@@ -141632,54 +159711,46 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){ + } + + /* +-** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN +-** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was +-** defined at compile-time. If it is not a no-op, a single OP_Explain opcode +-** is added to the output to describe the table scan strategy in pLevel. +-** +-** If an OP_Explain opcode is added to the VM, its address is returned. +-** Otherwise, if no OP_Explain is coded, zero is returned. ++** This function sets the P4 value of an existing OP_Explain opcode to ++** text describing the loop in pLevel. If the OP_Explain opcode already has ++** a P4 value, it is freed before it is overwritten. + */ +-SQLITE_PRIVATE int sqlite3WhereExplainOneScan( ++SQLITE_PRIVATE void sqlite3WhereAddExplainText( + Parse *pParse, /* Parse context */ ++ int addr, /* Address of OP_Explain opcode */ + SrcList *pTabList, /* Table list this loop refers to */ + WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ + u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ + ){ +- int ret = 0; +-#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS) +- if( sqlite3ParseToplevel(pParse)->explain==2 ) ++#if !defined(SQLITE_DEBUG) ++ if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) + #endif + { +- struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; +- Vdbe *v = pParse->pVdbe; /* VM being constructed */ ++ VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr); ++ ++ SrcItem *pItem = &pTabList->a[pLevel->iFrom]; + sqlite3 *db = pParse->db; /* Database handle */ + int isSearch; /* True for a SEARCH. False for SCAN. */ + WhereLoop *pLoop; /* The controlling WhereLoop object */ + u32 flags; /* Flags that describe this loop */ ++#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) + char *zMsg; /* Text to add to EQP output */ ++#endif + StrAccum str; /* EQP output string */ + char zBuf[100]; /* Initial space for EQP output string */ + ++ if( db->mallocFailed ) return; ++ + pLoop = pLevel->pWLoop; + flags = pLoop->wsFlags; +- if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0; + + isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 + || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) + || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); + + sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); +- sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN"); +- if( pItem->pSelect ){ +- sqlite3_str_appendf(&str, " SUBQUERY %u", pItem->pSelect->selId); +- }else{ +- sqlite3_str_appendf(&str, " TABLE %s", pItem->zName); +- } +- +- if( pItem->zAlias ){ +- sqlite3_str_appendf(&str, " AS %s", pItem->zAlias); +- } ++ str.printfFlags = SQLITE_PRINTF_INTERNAL; ++ sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem); + if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){ + const char *zFmt = 0; + Index *pIdx; +@@ -141687,7 +159758,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( + assert( pLoop->u.btree.pIndex!=0 ); + pIdx = pLoop->u.btree.pIndex; + assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) ); +- if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){ ++ if( !HasRowid(pItem->pSTab) && IsPrimaryKeyIndex(pIdx) ){ + if( isSearch ){ + zFmt = "PRIMARY KEY"; + } +@@ -141695,7 +159766,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( + zFmt = "AUTOMATIC PARTIAL COVERING INDEX"; + }else if( flags & WHERE_AUTO_INDEX ){ + zFmt = "AUTOMATIC COVERING INDEX"; +- }else if( flags & WHERE_IDX_ONLY ){ ++ }else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ + zFmt = "COVERING INDEX %s"; + }else{ + zFmt = "INDEX %s"; +@@ -141706,26 +159777,39 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( + explainIndexRange(&str, pLoop); + } + }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){ +- const char *zRangeOp; ++ char cRangeOp; ++#if 0 /* Better output, but breaks many tests */ ++ const Table *pTab = pItem->pTab; ++ const char *zRowid = pTab->iPKey>=0 ? pTab->aCol[pTab->iPKey].zCnName: ++ "rowid"; ++#else ++ const char *zRowid = "rowid"; ++#endif ++ sqlite3_str_appendf(&str, " USING INTEGER PRIMARY KEY (%s", zRowid); + if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){ +- zRangeOp = "="; ++ cRangeOp = '='; + }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ +- zRangeOp = ">? AND rowid<"; ++ sqlite3_str_appendf(&str, ">? AND %s", zRowid); ++ cRangeOp = '<'; + }else if( flags&WHERE_BTM_LIMIT ){ +- zRangeOp = ">"; ++ cRangeOp = '>'; + }else{ + assert( flags&WHERE_TOP_LIMIT); +- zRangeOp = "<"; ++ cRangeOp = '<'; + } +- sqlite3_str_appendf(&str, +- " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp); ++ sqlite3_str_appendf(&str, "%c?)", cRangeOp); + } + #ifndef SQLITE_OMIT_VIRTUALTABLE + else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ +- sqlite3_str_appendf(&str, " VIRTUAL TABLE INDEX %d:%s", ++ sqlite3_str_appendall(&str, " VIRTUAL TABLE INDEX "); ++ sqlite3_str_appendf(&str, ++ pLoop->u.vtab.bIdxNumHex ? "0x%x:%s" : "%d:%s", + pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr); + } + #endif ++ if( pItem->fg.jointype & JT_LEFT ){ ++ sqlite3_str_appendf(&str, " LEFT-JOIN"); ++ } + #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS + if( pLoop->nOut>=10 ){ + sqlite3_str_appendf(&str, " (~%llu rows)", +@@ -141734,13 +159818,105 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( + sqlite3_str_append(&str, " (~1 row)", 9); + } + #endif ++#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) + zMsg = sqlite3StrAccumFinish(&str); + sqlite3ExplainBreakpoint("",zMsg); +- ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), +- pParse->addrExplain, 0, zMsg,P4_DYNAMIC); ++#endif ++ ++ assert( pOp->opcode==OP_Explain ); ++ assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 ); ++ sqlite3DbFree(db, pOp->p4.z); ++ pOp->p4type = P4_DYNAMIC; ++ pOp->p4.z = sqlite3StrAccumFinish(&str); ++ } ++} ++ ++ ++/* ++** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN ++** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG ++** was defined at compile-time. If it is not a no-op, a single OP_Explain ++** opcode is added to the output to describe the table scan strategy in pLevel. ++** ++** If an OP_Explain opcode is added to the VM, its address is returned. ++** Otherwise, if no OP_Explain is coded, zero is returned. ++*/ ++SQLITE_PRIVATE int sqlite3WhereExplainOneScan( ++ Parse *pParse, /* Parse context */ ++ SrcList *pTabList, /* Table list this loop refers to */ ++ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ ++ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ++){ ++ int ret = 0; ++#if !defined(SQLITE_DEBUG) ++ if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) ++#endif ++ { ++ if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0 ++ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ++ ){ ++ Vdbe *v = pParse->pVdbe; ++ int addr = sqlite3VdbeCurrentAddr(v); ++ ret = sqlite3VdbeAddOp3( ++ v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun ++ ); ++ sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags); ++ } + } + return ret; + } ++ ++/* ++** Add a single OP_Explain opcode that describes a Bloom filter. ++** ++** Or if not processing EXPLAIN QUERY PLAN and not in a SQLITE_DEBUG and/or ++** SQLITE_ENABLE_STMT_SCANSTATUS build, then OP_Explain opcodes are not ++** required and this routine is a no-op. ++** ++** If an OP_Explain opcode is added to the VM, its address is returned. ++** Otherwise, if no OP_Explain is coded, zero is returned. ++*/ ++SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( ++ const Parse *pParse, /* Parse context */ ++ const WhereInfo *pWInfo, /* WHERE clause */ ++ const WhereLevel *pLevel /* Bloom filter on this level */ ++){ ++ int ret = 0; ++ SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom]; ++ Vdbe *v = pParse->pVdbe; /* VM being constructed */ ++ sqlite3 *db = pParse->db; /* Database handle */ ++ char *zMsg; /* Text to add to EQP output */ ++ int i; /* Loop counter */ ++ WhereLoop *pLoop; /* The where loop */ ++ StrAccum str; /* EQP output string */ ++ char zBuf[100]; /* Initial space for EQP output string */ ++ ++ sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); ++ str.printfFlags = SQLITE_PRINTF_INTERNAL; ++ sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem); ++ pLoop = pLevel->pWLoop; ++ if( pLoop->wsFlags & WHERE_IPK ){ ++ const Table *pTab = pItem->pSTab; ++ if( pTab->iPKey>=0 ){ ++ sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName); ++ }else{ ++ sqlite3_str_appendf(&str, "rowid=?"); ++ } ++ }else{ ++ for(i=pLoop->nSkip; iu.btree.nEq; i++){ ++ const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i); ++ if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5); ++ sqlite3_str_appendf(&str, "%s=?", z); ++ } ++ } ++ sqlite3_str_append(&str, ")", 1); ++ zMsg = sqlite3StrAccumFinish(&str); ++ ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), ++ pParse->addrExplain, 0, zMsg,P4_DYNAMIC); ++ ++ sqlite3VdbeScanStatus(v, sqlite3VdbeCurrentAddr(v)-1, 0, 0, 0, 0); ++ return ret; ++} + #endif /* SQLITE_OMIT_EXPLAIN */ + + #ifdef SQLITE_ENABLE_STMT_SCANSTATUS +@@ -141759,16 +159935,40 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus( + WhereLevel *pLvl, /* Level to add scanstatus() entry for */ + int addrExplain /* Address of OP_Explain (or 0) */ + ){ +- const char *zObj = 0; +- WhereLoop *pLoop = pLvl->pWLoop; +- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ +- zObj = pLoop->u.btree.pIndex->zName; +- }else{ +- zObj = pSrclist->a[pLvl->iFrom].zName; ++ if( IS_STMT_SCANSTATUS( sqlite3VdbeDb(v) ) ){ ++ const char *zObj = 0; ++ WhereLoop *pLoop = pLvl->pWLoop; ++ int wsFlags = pLoop->wsFlags; ++ int viaCoroutine = 0; ++ ++ if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ ++ zObj = pLoop->u.btree.pIndex->zName; ++ }else{ ++ zObj = pSrclist->a[pLvl->iFrom].zName; ++ viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine; ++ } ++ sqlite3VdbeScanStatus( ++ v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj ++ ); ++ ++ if( viaCoroutine==0 ){ ++ if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){ ++ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur); ++ } ++ if( wsFlags & WHERE_INDEXED ){ ++ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); ++ } ++ }else{ ++ int addr; ++ VdbeOp *pOp; ++ assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); ++ addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; ++ pOp = sqlite3VdbeGetOp(v, addr-1); ++ assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); ++ assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); ++ sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); ++ } + } +- sqlite3VdbeScanStatus( +- v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj +- ); + } + #endif + +@@ -141819,7 +160019,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ + int nLoop = 0; + assert( pTerm!=0 ); + while( (pTerm->wtFlags & TERM_CODED)==0 +- && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) ++ && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_OuterON)) + && (pLevel->notReady & pTerm->prereqAll)==0 + ){ + if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){ +@@ -141827,6 +160027,12 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ + }else{ + pTerm->wtFlags |= TERM_CODED; + } ++#ifdef WHERETRACE_ENABLED ++ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){ ++ sqlite3DebugPrintf("DISABLE-"); ++ sqlite3WhereTermPrint(pTerm, (int)(pTerm - (pTerm->pWC->a))); ++ } ++#endif + if( pTerm->iParent<0 ) break; + pTerm = &pTerm->pWC->a[pTerm->iParent]; + assert( pTerm!=0 ); +@@ -141900,11 +160106,44 @@ static void updateRangeAffinityStr( + } + } + ++/* ++** The pOrderBy->a[].u.x.iOrderByCol values might be incorrect because ++** columns might have been rearranged in the result set. This routine ++** fixes them up. ++** ++** pEList is the new result set. The pEList->a[].u.x.iOrderByCol values ++** contain the *old* locations of each expression. This is a temporary ++** use of u.x.iOrderByCol, not its intended use. The caller must reset ++** u.x.iOrderByCol back to zero for all entries in pEList before the ++** caller returns. ++** ++** This routine changes pOrderBy->a[].u.x.iOrderByCol values from ++** pEList->a[N].u.x.iOrderByCol into N+1. (The "+1" is because of the 1-based ++** indexing used by iOrderByCol.) Or if no match, iOrderByCol is set to zero. ++*/ ++static void adjustOrderByCol(ExprList *pOrderBy, ExprList *pEList){ ++ int i, j; ++ if( pOrderBy==0 ) return; ++ for(i=0; inExpr; i++){ ++ int t = pOrderBy->a[i].u.x.iOrderByCol; ++ if( t==0 ) continue; ++ for(j=0; jnExpr; j++){ ++ if( pEList->a[j].u.x.iOrderByCol==t ){ ++ pOrderBy->a[i].u.x.iOrderByCol = j+1; ++ break; ++ } ++ } ++ if( j>=pEList->nExpr ){ ++ pOrderBy->a[i].u.x.iOrderByCol = 0; ++ } ++ } ++} ++ + + /* + ** pX is an expression of the form: (vector) IN (SELECT ...) + ** In other words, it is a vector IN operator with a SELECT clause on the +-** LHS. But not all terms in the vector are indexable and the terms might ++** RHS. But not all terms in the vector are indexable and the terms might + ** not be in the correct order for indexing. + ** + ** This routine makes a copy of the input pX expression and then adjusts +@@ -141937,66 +160176,214 @@ static Expr *removeUnindexableInClauseTerms( + Expr *pX /* The IN expression to be reduced */ + ){ + sqlite3 *db = pParse->db; ++ Select *pSelect; /* Pointer to the SELECT on the RHS */ + Expr *pNew; + pNew = sqlite3ExprDup(db, pX, 0); + if( db->mallocFailed==0 ){ +- ExprList *pOrigRhs = pNew->x.pSelect->pEList; /* Original unmodified RHS */ +- ExprList *pOrigLhs = pNew->pLeft->x.pList; /* Original unmodified LHS */ +- ExprList *pRhs = 0; /* New RHS after modifications */ +- ExprList *pLhs = 0; /* New LHS after mods */ +- int i; /* Loop counter */ +- Select *pSelect; /* Pointer to the SELECT on the RHS */ ++ for(pSelect=pNew->x.pSelect; pSelect; pSelect=pSelect->pPrior){ ++ ExprList *pOrigRhs; /* Original unmodified RHS */ ++ ExprList *pOrigLhs = 0; /* Original unmodified LHS */ ++ ExprList *pRhs = 0; /* New RHS after modifications */ ++ ExprList *pLhs = 0; /* New LHS after mods */ ++ int i; /* Loop counter */ ++ ++ assert( ExprUseXSelect(pNew) ); ++ pOrigRhs = pSelect->pEList; ++ assert( pNew->pLeft!=0 ); ++ assert( ExprUseXList(pNew->pLeft) ); ++ if( pSelect==pNew->x.pSelect ){ ++ pOrigLhs = pNew->pLeft->x.pList; ++ } ++ for(i=iEq; inLTerm; i++){ ++ if( pLoop->aLTerm[i]->pExpr==pX ){ ++ int iField; ++ assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 ); ++ iField = pLoop->aLTerm[i]->u.x.iField - 1; ++ if( NEVER(pOrigRhs->a[iField].pExpr==0) ){ ++ continue; /* Duplicate PK column */ ++ } ++ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); ++ pOrigRhs->a[iField].pExpr = 0; ++ if( pRhs ) pRhs->a[pRhs->nExpr-1].u.x.iOrderByCol = iField+1; ++ if( pOrigLhs ){ ++ assert( pOrigLhs->a[iField].pExpr!=0 ); ++ pLhs = sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr); ++ pOrigLhs->a[iField].pExpr = 0; ++ } ++ } ++ } ++ sqlite3ExprListDelete(db, pOrigRhs); ++ if( pOrigLhs ){ ++ sqlite3ExprListDelete(db, pOrigLhs); ++ pNew->pLeft->x.pList = pLhs; ++ } ++ pSelect->pEList = pRhs; ++ pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */ ++ if( pLhs && pLhs->nExpr==1 ){ ++ /* Take care here not to generate a TK_VECTOR containing only a ++ ** single value. Since the parser never creates such a vector, some ++ ** of the subroutines do not handle this case. */ ++ Expr *p = pLhs->a[0].pExpr; ++ pLhs->a[0].pExpr = 0; ++ sqlite3ExprDelete(db, pNew->pLeft); ++ pNew->pLeft = p; ++ } + +- for(i=iEq; inLTerm; i++){ +- if( pLoop->aLTerm[i]->pExpr==pX ){ +- int iField = pLoop->aLTerm[i]->iField - 1; +- if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ +- pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); +- pOrigRhs->a[iField].pExpr = 0; +- assert( pOrigLhs->a[iField].pExpr!=0 ); +- pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr); +- pOrigLhs->a[iField].pExpr = 0; +- } +- } +- sqlite3ExprListDelete(db, pOrigRhs); +- sqlite3ExprListDelete(db, pOrigLhs); +- pNew->pLeft->x.pList = pLhs; +- pNew->x.pSelect->pEList = pRhs; +- if( pLhs && pLhs->nExpr==1 ){ +- /* Take care here not to generate a TK_VECTOR containing only a +- ** single value. Since the parser never creates such a vector, some +- ** of the subroutines do not handle this case. */ +- Expr *p = pLhs->a[0].pExpr; +- pLhs->a[0].pExpr = 0; +- sqlite3ExprDelete(db, pNew->pLeft); +- pNew->pLeft = p; +- } +- pSelect = pNew->x.pSelect; +- if( pSelect->pOrderBy ){ +- /* If the SELECT statement has an ORDER BY clause, zero the +- ** iOrderByCol variables. These are set to non-zero when an +- ** ORDER BY term exactly matches one of the terms of the +- ** result-set. Since the result-set of the SELECT statement may +- ** have been modified or reordered, these variables are no longer +- ** set correctly. Since setting them is just an optimization, +- ** it's easiest just to zero them here. */ +- ExprList *pOrderBy = pSelect->pOrderBy; +- for(i=0; inExpr; i++){ +- pOrderBy->a[i].u.x.iOrderByCol = 0; ++ /* If either the ORDER BY clause or the GROUP BY clause contains ++ ** references to result-set columns, those references might now be ++ ** obsolete. So fix them up. ++ */ ++ assert( pRhs!=0 || db->mallocFailed ); ++ if( pRhs ){ ++ adjustOrderByCol(pSelect->pOrderBy, pRhs); ++ adjustOrderByCol(pSelect->pGroupBy, pRhs); ++ for(i=0; inExpr; i++) pRhs->a[i].u.x.iOrderByCol = 0; + } +- } + + #if 0 +- printf("For indexing, change the IN expr:\n"); +- sqlite3TreeViewExpr(0, pX, 0); +- printf("Into:\n"); +- sqlite3TreeViewExpr(0, pNew, 0); ++ printf("For indexing, change the IN expr:\n"); ++ sqlite3TreeViewExpr(0, pX, 0); ++ printf("Into:\n"); ++ sqlite3TreeViewExpr(0, pNew, 0); + #endif ++ } + } + return pNew; + } + + ++#ifndef SQLITE_OMIT_SUBQUERY ++/* ++** Generate code for a single X IN (....) term of the WHERE clause. ++** ++** This is a special-case of codeEqualityTerm() that works for IN operators ++** only. It is broken out into a subroutine because this case is ++** uncommon and by splitting it off into a subroutine, the common case ++** runs faster. ++** ++** The current value for the constraint is left in register iTarget. ++** This routine sets up a loop that will iterate over all values of X. ++*/ ++static SQLITE_NOINLINE void codeINTerm( ++ Parse *pParse, /* The parsing context */ ++ WhereTerm *pTerm, /* The term of the WHERE clause to be coded */ ++ WhereLevel *pLevel, /* The level of the FROM clause we are working on */ ++ int iEq, /* Index of the equality term within this level */ ++ int bRev, /* True for reverse-order IN operations */ ++ int iTarget /* Attempt to leave results in this register */ ++){ ++ Expr *pX = pTerm->pExpr; ++ int eType = IN_INDEX_NOOP; ++ int iTab; ++ struct InLoop *pIn; ++ WhereLoop *pLoop = pLevel->pWLoop; ++ Vdbe *v = pParse->pVdbe; ++ int i; ++ int nEq = 0; ++ int *aiMap = 0; ++ ++ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ++ && pLoop->u.btree.pIndex!=0 ++ && pLoop->u.btree.pIndex->aSortOrder[iEq] ++ ){ ++ testcase( iEq==0 ); ++ testcase( bRev ); ++ bRev = !bRev; ++ } ++ assert( pX->op==TK_IN ); ++ ++ for(i=0; iaLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){ ++ disableTerm(pLevel, pTerm); ++ return; ++ } ++ } ++ for(i=iEq; inLTerm; i++){ ++ assert( pLoop->aLTerm[i]!=0 ); ++ if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; ++ } ++ ++ iTab = 0; ++ if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){ ++ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); ++ }else{ ++ sqlite3 *db = pParse->db; ++ Expr *pXMod = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); ++ if( !db->mallocFailed ){ ++ aiMap = (int*)sqlite3DbMallocZero(db, sizeof(int)*nEq); ++ eType = sqlite3FindInIndex(pParse, pXMod, IN_INDEX_LOOP, 0, aiMap, &iTab); ++ } ++ sqlite3ExprDelete(db, pXMod); ++ } ++ ++ if( eType==IN_INDEX_INDEX_DESC ){ ++ testcase( bRev ); ++ bRev = !bRev; ++ } ++ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); ++ VdbeCoverageIf(v, bRev); ++ VdbeCoverageIf(v, !bRev); ++ ++ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); ++ pLoop->wsFlags |= WHERE_IN_ABLE; ++ if( pLevel->u.in.nIn==0 ){ ++ pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); ++ } ++ if( iEq>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 ){ ++ pLoop->wsFlags |= WHERE_IN_EARLYOUT; ++ } ++ ++ i = pLevel->u.in.nIn; ++ pLevel->u.in.nIn += nEq; ++ pLevel->u.in.aInLoop = ++ sqlite3WhereRealloc(pTerm->pWC->pWInfo, ++ pLevel->u.in.aInLoop, ++ sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); ++ pIn = pLevel->u.in.aInLoop; ++ if( pIn ){ ++ int iMap = 0; /* Index in aiMap[] */ ++ pIn += i; ++ for(i=iEq; inLTerm; i++){ ++ if( pLoop->aLTerm[i]->pExpr==pX ){ ++ int iOut = iTarget + i - iEq; ++ if( eType==IN_INDEX_ROWID ){ ++ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); ++ }else{ ++ int iCol = aiMap ? aiMap[iMap++] : 0; ++ pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut); ++ } ++ sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v); ++ if( i==iEq ){ ++ pIn->iCur = iTab; ++ pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; ++ if( iEq>0 ){ ++ pIn->iBase = iTarget - i; ++ pIn->nPrefix = i; ++ }else{ ++ pIn->nPrefix = 0; ++ } ++ }else{ ++ pIn->eEndLoopOp = OP_Noop; ++ } ++ pIn++; ++ } ++ } ++ testcase( iEq>0 ++ && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 ++ && (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ); ++ if( iEq>0 ++ && (pLoop->wsFlags & (WHERE_IN_SEEKSCAN|WHERE_VIRTUALTABLE))==0 ++ ){ ++ sqlite3VdbeAddOp3(v, OP_SeekHit, pLevel->iIdxCur, 0, iEq); ++ } ++ }else{ ++ pLevel->u.in.nIn = 0; ++ } ++ sqlite3DbFree(pParse->db, aiMap); ++} ++#endif ++ ++ + /* + ** Generate code for a single equality term of the WHERE clause. An equality + ** term can be either X=expr or X IN (...). pTerm is the term to be +@@ -142021,7 +160408,6 @@ static int codeEqualityTerm( + int iTarget /* Attempt to leave results in this register */ + ){ + Expr *pX = pTerm->pExpr; +- Vdbe *v = pParse->pVdbe; + int iReg; /* Register holding results */ + + assert( pLevel->pWLoop->aLTerm[iEq]==pTerm ); +@@ -142030,111 +160416,30 @@ static int codeEqualityTerm( + iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); + }else if( pX->op==TK_ISNULL ){ + iReg = iTarget; +- sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); ++ sqlite3VdbeAddOp2(pParse->pVdbe, OP_Null, 0, iReg); + #ifndef SQLITE_OMIT_SUBQUERY + }else{ +- int eType = IN_INDEX_NOOP; +- int iTab; +- struct InLoop *pIn; +- WhereLoop *pLoop = pLevel->pWLoop; +- int i; +- int nEq = 0; +- int *aiMap = 0; +- +- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 +- && pLoop->u.btree.pIndex!=0 +- && pLoop->u.btree.pIndex->aSortOrder[iEq] +- ){ +- testcase( iEq==0 ); +- testcase( bRev ); +- bRev = !bRev; +- } + assert( pX->op==TK_IN ); + iReg = iTarget; +- +- for(i=0; iaLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){ +- disableTerm(pLevel, pTerm); +- return iTarget; +- } +- } +- for(i=iEq;inLTerm; i++){ +- assert( pLoop->aLTerm[i]!=0 ); +- if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; +- } +- +- iTab = 0; +- if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ +- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); +- }else{ +- sqlite3 *db = pParse->db; +- pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); +- +- if( !db->mallocFailed ){ +- aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); +- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); +- pTerm->pExpr->iTable = iTab; +- } +- sqlite3ExprDelete(db, pX); +- pX = pTerm->pExpr; +- } +- +- if( eType==IN_INDEX_INDEX_DESC ){ +- testcase( bRev ); +- bRev = !bRev; +- } +- sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); +- VdbeCoverageIf(v, bRev); +- VdbeCoverageIf(v, !bRev); +- assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); +- +- pLoop->wsFlags |= WHERE_IN_ABLE; +- if( pLevel->u.in.nIn==0 ){ +- pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); +- } +- +- i = pLevel->u.in.nIn; +- pLevel->u.in.nIn += nEq; +- pLevel->u.in.aInLoop = +- sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop, +- sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); +- pIn = pLevel->u.in.aInLoop; +- if( pIn ){ +- int iMap = 0; /* Index in aiMap[] */ +- pIn += i; +- for(i=iEq;inLTerm; i++){ +- if( pLoop->aLTerm[i]->pExpr==pX ){ +- int iOut = iReg + i - iEq; +- if( eType==IN_INDEX_ROWID ){ +- pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); +- }else{ +- int iCol = aiMap ? aiMap[iMap++] : 0; +- pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut); +- } +- sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v); +- if( i==iEq ){ +- pIn->iCur = iTab; +- pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; +- if( iEq>0 ){ +- pIn->iBase = iReg - i; +- pIn->nPrefix = i; +- pLoop->wsFlags |= WHERE_IN_EARLYOUT; +- }else{ +- pIn->nPrefix = 0; +- } +- }else{ +- pIn->eEndLoopOp = OP_Noop; +- } +- pIn++; +- } +- } +- }else{ +- pLevel->u.in.nIn = 0; +- } +- sqlite3DbFree(pParse->db, aiMap); ++ codeINTerm(pParse, pTerm, pLevel, iEq, bRev, iTarget); + #endif + } +- disableTerm(pLevel, pTerm); ++ ++ /* As an optimization, try to disable the WHERE clause term that is ++ ** driving the index as it will always be true. The correct answer is ++ ** obtained regardless, but we might get the answer with fewer CPU cycles ++ ** by omitting the term. ++ ** ++ ** But do not disable the term unless we are certain that the term is ++ ** not a transitive constraint. For an example of where that does not ++ ** work, see https://sqlite.org/forum/forumpost/eb8613976a (2021-05-04) ++ */ ++ if( (pLevel->pWLoop->wsFlags & WHERE_TRANSCONS)==0 ++ || (pTerm->eOperator & WO_EQUIV)==0 ++ ){ ++ disableTerm(pLevel, pTerm); ++ } ++ + return iReg; + } + +@@ -142212,7 +160517,7 @@ static int codeAllEqualityTerms( + /* Figure out how many memory cells we will need then allocate them. + */ + regBase = pParse->nMem + 1; +- nReg = pLoop->u.btree.nEq + nExtraReg; ++ nReg = nEq + nExtraReg; + pParse->nMem += nReg; + + zAff = sqlite3DbStrDup(pParse->db,sqlite3IndexAffinityStr(pParse->db,pIdx)); +@@ -142220,11 +160525,13 @@ static int codeAllEqualityTerms( + + if( nSkip ){ + int iIdxCur = pLevel->iIdxCur; ++ sqlite3VdbeAddOp3(v, OP_Null, 0, regBase, regBase+nSkip-1); + sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur); + VdbeCoverageIf(v, bRev==0); + VdbeCoverageIf(v, bRev!=0); + VdbeComment((v, "begin skip-scan on %s", pIdx->zName)); + j = sqlite3VdbeAddOp0(v, OP_Goto); ++ assert( pLevel->addrSkip==0 ); + pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT), + iIdxCur, 0, regBase, nSkip); + VdbeCoverageIf(v, bRev==0); +@@ -142254,7 +160561,7 @@ static int codeAllEqualityTerms( + sqlite3ReleaseTempReg(pParse, regBase); + regBase = r1; + }else{ +- sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); ++ sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j); + } + } + if( pTerm->eOperator & WO_IN ){ +@@ -142271,7 +160578,8 @@ static int codeAllEqualityTerms( + sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); + VdbeCoverage(v); + } +- if( zAff ){ ++ if( pParse->nErr==0 ){ ++ assert( pParse->db->mallocFailed==0 ); + if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ + zAff[j] = SQLITE_AFF_BLOB; + } +@@ -142311,7 +160619,7 @@ static void whereLikeOptimizationStringFixup( + if( pTerm->wtFlags & TERM_LIKEOPT ){ + VdbeOp *pOp; + assert( pLevel->iLikeRepCntr>0 ); +- pOp = sqlite3VdbeGetOp(v, -1); ++ pOp = sqlite3VdbeGetLastOp(v); + assert( pOp!=0 ); + assert( pOp->opcode==OP_String8 + || pTerm->pWC->pWInfo->pParse->db->mallocFailed ); +@@ -142398,18 +160706,19 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){ + ** 2) transform the expression node to a TK_REGISTER node that reads + ** from the newly populated register. + ** +-** Also, if the node is a TK_COLUMN that does access the table idenified ++** Also, if the node is a TK_COLUMN that does access the table identified + ** by pCCurHint.iTabCur, and an index is being used (which we will + ** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into + ** an access of the index rather than the original table. + */ + static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ + int rc = WRC_Continue; ++ int reg; + struct CCurHint *pHint = pWalker->u.pCCurHint; + if( pExpr->op==TK_COLUMN ){ + if( pExpr->iTable!=pHint->iTabCur ){ +- int reg = ++pWalker->pParse->nMem; /* Register for column value */ +- sqlite3ExprCode(pWalker->pParse, pExpr, reg); ++ reg = ++pWalker->pParse->nMem; /* Register for column value */ ++ reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); + pExpr->op = TK_REGISTER; + pExpr->iTable = reg; + }else if( pHint->pIdx!=0 ){ +@@ -142417,15 +160726,15 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ + pExpr->iColumn = sqlite3TableColumnToIndex(pHint->pIdx, pExpr->iColumn); + assert( pExpr->iColumn>=0 ); + } +- }else if( pExpr->op==TK_AGG_FUNCTION ){ +- /* An aggregate function in the WHERE clause of a query means this must +- ** be a correlated sub-query, and expression pExpr is an aggregate from +- ** the parent context. Do not walk the function arguments in this case. +- ** +- ** todo: It should be possible to replace this node with a TK_REGISTER +- ** expression, as the result of the expression must be stored in a +- ** register at this point. The same holds for TK_AGG_COLUMN nodes. */ ++ }else if( pExpr->pAggInfo ){ + rc = WRC_Prune; ++ reg = ++pWalker->pParse->nMem; /* Register for column value */ ++ reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); ++ pExpr->op = TK_REGISTER; ++ pExpr->iTable = reg; ++ }else if( pExpr->op==TK_TRUEFALSE ){ ++ /* Do not walk disabled expressions. tag-20230504-1 */ ++ return WRC_Prune; + } + return rc; + } +@@ -142434,7 +160743,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ + ** Insert an OP_CursorHint instruction if it is appropriate to do so. + */ + static void codeCursorHint( +- struct SrcList_item *pTabItem, /* FROM clause item */ ++ SrcItem *pTabItem, /* FROM clause item */ + WhereInfo *pWInfo, /* The where clause */ + WhereLevel *pLevel, /* Which loop to provide hints for */ + WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */ +@@ -142461,7 +160770,7 @@ static void codeCursorHint( + sWalker.pParse = pParse; + sWalker.u.pCCurHint = &sHint; + pWC = &pWInfo->sWC; +- for(i=0; inTerm; i++){ ++ for(i=0; inBase; i++){ + pTerm = &pWC->a[i]; + if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; + if( pTerm->prereqAll & pLevel->notReady ) continue; +@@ -142490,8 +160799,8 @@ static void codeCursorHint( + */ + if( pTabItem->fg.jointype & JT_LEFT ){ + Expr *pExpr = pTerm->pExpr; +- if( !ExprHasProperty(pExpr, EP_FromJoin) +- || pExpr->iRightJoinTable!=pTabItem->iCursor ++ if( !ExprHasProperty(pExpr, EP_OuterON) ++ || pExpr->w.iJoin!=pTabItem->iCursor + ){ + sWalker.eCode = 0; + sWalker.xExprCallback = codeCursorHintIsOrFunction; +@@ -142499,7 +160808,7 @@ static void codeCursorHint( + if( sWalker.eCode ) continue; + } + }else{ +- if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue; ++ if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) continue; + } + + /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize +@@ -142527,7 +160836,7 @@ static void codeCursorHint( + } + if( pExpr!=0 ){ + sWalker.xExprCallback = codeCursorHintFixExpr; +- sqlite3WalkExpr(&sWalker, pExpr); ++ if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr); + sqlite3VdbeAddOp4(v, OP_CursorHint, + (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0, + (const char*)pExpr, P4_EXPR); +@@ -142547,13 +160856,21 @@ static void codeCursorHint( + ** + ** OP_DeferredSeek $iCur $iRowid + ** ++** Which causes a seek on $iCur to the row with rowid $iRowid. ++** + ** However, if the scan currently being coded is a branch of an OR-loop and +-** the statement currently being coded is a SELECT, then P3 of OP_DeferredSeek +-** is set to iIdxCur and P4 is set to point to an array of integers +-** containing one entry for each column of the table cursor iCur is open +-** on. For each table column, if the column is the i'th column of the +-** index, then the corresponding array entry is set to (i+1). If the column +-** does not appear in the index at all, the array entry is set to 0. ++** the statement currently being coded is a SELECT, then additional information ++** is added that might allow OP_Column to omit the seek and instead do its ++** lookup on the index, thus avoiding an expensive seek operation. To ++** enable this optimization, the P3 of OP_DeferredSeek is set to iIdxCur ++** and P4 is set to an array of integers containing one entry for each column ++** in the table. For each table column, if the column is the i'th ++** column of the index, then the corresponding array entry is set to (i+1). ++** If the column does not appear in the index at all, the array entry is set ++** to 0. The OP_Column opcode can check this array to see if the column it ++** wants is in the index and if it is, it will substitute the index cursor ++** and column number and continue with those new values, rather than seeking ++** the table cursor. + */ + static void codeDeferredSeek( + WhereInfo *pWInfo, /* Where clause context */ +@@ -142569,7 +160886,7 @@ static void codeDeferredSeek( + + pWInfo->bDeferredSeek = 1; + sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); +- if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) ++ if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) + && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) + ){ + int i; +@@ -142603,7 +160920,7 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ + assert( nReg>0 ); + if( p && sqlite3ExprIsVector(p) ){ + #ifndef SQLITE_OMIT_SUBQUERY +- if( (p->flags & EP_xIsSelect) ){ ++ if( ExprUseXSelect(p) ){ + Vdbe *v = pParse->pVdbe; + int iSelect; + assert( p->op==TK_SELECT ); +@@ -142613,154 +160930,20 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ + #endif + { + int i; +- ExprList *pList = p->x.pList; ++ const ExprList *pList; ++ assert( ExprUseXList(p) ); ++ pList = p->x.pList; + assert( nReg<=pList->nExpr ); + for(i=0; ia[i].pExpr, iReg+i); + } + } + }else{ +- assert( nReg==1 ); ++ assert( nReg==1 || pParse->nErr ); + sqlite3ExprCode(pParse, p, iReg); + } + } + +-/* An instance of the IdxExprTrans object carries information about a +-** mapping from an expression on table columns into a column in an index +-** down through the Walker. +-*/ +-typedef struct IdxExprTrans { +- Expr *pIdxExpr; /* The index expression */ +- int iTabCur; /* The cursor of the corresponding table */ +- int iIdxCur; /* The cursor for the index */ +- int iIdxCol; /* The column for the index */ +- int iTabCol; /* The column for the table */ +- WhereInfo *pWInfo; /* Complete WHERE clause information */ +- sqlite3 *db; /* Database connection (for malloc()) */ +-} IdxExprTrans; +- +-/* +-** Preserve pExpr on the WhereETrans list of the WhereInfo. +-*/ +-static void preserveExpr(IdxExprTrans *pTrans, Expr *pExpr){ +- WhereExprMod *pNew; +- pNew = sqlite3DbMallocRaw(pTrans->db, sizeof(*pNew)); +- if( pNew==0 ) return; +- pNew->pNext = pTrans->pWInfo->pExprMods; +- pTrans->pWInfo->pExprMods = pNew; +- pNew->pExpr = pExpr; +- memcpy(&pNew->orig, pExpr, sizeof(*pExpr)); +-} +- +-/* The walker node callback used to transform matching expressions into +-** a reference to an index column for an index on an expression. +-** +-** If pExpr matches, then transform it into a reference to the index column +-** that contains the value of pExpr. +-*/ +-static int whereIndexExprTransNode(Walker *p, Expr *pExpr){ +- IdxExprTrans *pX = p->u.pIdxTrans; +- if( sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){ +- preserveExpr(pX, pExpr); +- pExpr->affExpr = sqlite3ExprAffinity(pExpr); +- pExpr->op = TK_COLUMN; +- pExpr->iTable = pX->iIdxCur; +- pExpr->iColumn = pX->iIdxCol; +- pExpr->y.pTab = 0; +- testcase( ExprHasProperty(pExpr, EP_Skip) ); +- testcase( ExprHasProperty(pExpr, EP_Unlikely) ); +- ExprClearProperty(pExpr, EP_Skip|EP_Unlikely); +- return WRC_Prune; +- }else{ +- return WRC_Continue; +- } +-} +- +-#ifndef SQLITE_OMIT_GENERATED_COLUMNS +-/* A walker node callback that translates a column reference to a table +-** into a corresponding column reference of an index. +-*/ +-static int whereIndexExprTransColumn(Walker *p, Expr *pExpr){ +- if( pExpr->op==TK_COLUMN ){ +- IdxExprTrans *pX = p->u.pIdxTrans; +- if( pExpr->iTable==pX->iTabCur && pExpr->iColumn==pX->iTabCol ){ +- assert( pExpr->y.pTab!=0 ); +- preserveExpr(pX, pExpr); +- pExpr->affExpr = sqlite3TableColumnAffinity(pExpr->y.pTab,pExpr->iColumn); +- pExpr->iTable = pX->iIdxCur; +- pExpr->iColumn = pX->iIdxCol; +- pExpr->y.pTab = 0; +- } +- } +- return WRC_Continue; +-} +-#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ +- +-/* +-** For an indexes on expression X, locate every instance of expression X +-** in pExpr and change that subexpression into a reference to the appropriate +-** column of the index. +-** +-** 2019-10-24: Updated to also translate references to a VIRTUAL column in +-** the table into references to the corresponding (stored) column of the +-** index. +-*/ +-static void whereIndexExprTrans( +- Index *pIdx, /* The Index */ +- int iTabCur, /* Cursor of the table that is being indexed */ +- int iIdxCur, /* Cursor of the index itself */ +- WhereInfo *pWInfo /* Transform expressions in this WHERE clause */ +-){ +- int iIdxCol; /* Column number of the index */ +- ExprList *aColExpr; /* Expressions that are indexed */ +- Table *pTab; +- Walker w; +- IdxExprTrans x; +- aColExpr = pIdx->aColExpr; +- if( aColExpr==0 && !pIdx->bHasVCol ){ +- /* The index does not reference any expressions or virtual columns +- ** so no translations are needed. */ +- return; +- } +- pTab = pIdx->pTable; +- memset(&w, 0, sizeof(w)); +- w.u.pIdxTrans = &x; +- x.iTabCur = iTabCur; +- x.iIdxCur = iIdxCur; +- x.pWInfo = pWInfo; +- x.db = pWInfo->pParse->db; +- for(iIdxCol=0; iIdxColnColumn; iIdxCol++){ +- i16 iRef = pIdx->aiColumn[iIdxCol]; +- if( iRef==XN_EXPR ){ +- assert( aColExpr->a[iIdxCol].pExpr!=0 ); +- x.pIdxExpr = aColExpr->a[iIdxCol].pExpr; +- if( sqlite3ExprIsConstant(x.pIdxExpr) ) continue; +- w.xExprCallback = whereIndexExprTransNode; +-#ifndef SQLITE_OMIT_GENERATED_COLUMNS +- }else if( iRef>=0 +- && (pTab->aCol[iRef].colFlags & COLFLAG_VIRTUAL)!=0 +- && (pTab->aCol[iRef].zColl==0 +- || sqlite3StrICmp(pTab->aCol[iRef].zColl, sqlite3StrBINARY)==0) +- ){ +- /* Check to see if there are direct references to generated columns +- ** that are contained in the index. Pulling the generated column +- ** out of the index is an optimization only - the main table is always +- ** available if the index cannot be used. To avoid unnecessary +- ** complication, omit this optimization if the collating sequence for +- ** the column is non-standard */ +- x.iTabCol = iRef; +- w.xExprCallback = whereIndexExprTransColumn; +-#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ +- }else{ +- continue; +- } +- x.iIdxCol = iIdxCol; +- sqlite3WalkExpr(&w, pWInfo->pWhere); +- sqlite3WalkExprList(&w, pWInfo->pOrderBy); +- sqlite3WalkExprList(&w, pWInfo->pResultSet); +- } +-} +- + /* + ** The pTruth expression is always true because it is the WHERE clause + ** a partial index that is driving a query loop. Look through all of the +@@ -142789,6 +160972,91 @@ static void whereApplyPartialIndexConstraints( + } + } + ++/* ++** This routine is called right after An OP_Filter has been generated and ++** before the corresponding index search has been performed. This routine ++** checks to see if there are additional Bloom filters in inner loops that ++** can be checked prior to doing the index lookup. If there are available ++** inner-loop Bloom filters, then evaluate those filters now, before the ++** index lookup. The idea is that a Bloom filter check is way faster than ++** an index lookup, and the Bloom filter might return false, meaning that ++** the index lookup can be skipped. ++** ++** We know that an inner loop uses a Bloom filter because it has the ++** WhereLevel.regFilter set. If an inner-loop Bloom filter is checked, ++** then clear the WhereLevel.regFilter value to prevent the Bloom filter ++** from being checked a second time when the inner loop is evaluated. ++*/ ++static SQLITE_NOINLINE void filterPullDown( ++ Parse *pParse, /* Parsing context */ ++ WhereInfo *pWInfo, /* Complete information about the WHERE clause */ ++ int iLevel, /* Which level of pWInfo->a[] should be coded */ ++ int addrNxt, /* Jump here to bypass inner loops */ ++ Bitmask notReady /* Loops that are not ready */ ++){ ++ while( ++iLevel < pWInfo->nLevel ){ ++ WhereLevel *pLevel = &pWInfo->a[iLevel]; ++ WhereLoop *pLoop = pLevel->pWLoop; ++ if( pLevel->regFilter==0 ) continue; ++ if( pLevel->pWLoop->nSkip ) continue; ++ /* ,--- Because sqlite3ConstructBloomFilter() has will not have set ++ ** vvvvv--' pLevel->regFilter if this were true. */ ++ if( NEVER(pLoop->prereq & notReady) ) continue; ++ assert( pLevel->addrBrk==0 ); ++ pLevel->addrBrk = addrNxt; ++ if( pLoop->wsFlags & WHERE_IPK ){ ++ WhereTerm *pTerm = pLoop->aLTerm[0]; ++ int regRowid; ++ assert( pTerm!=0 ); ++ assert( pTerm->pExpr!=0 ); ++ testcase( pTerm->wtFlags & TERM_VIRTUAL ); ++ regRowid = sqlite3GetTempReg(pParse); ++ regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, regRowid); ++ sqlite3VdbeAddOp2(pParse->pVdbe, OP_MustBeInt, regRowid, addrNxt); ++ VdbeCoverage(pParse->pVdbe); ++ sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, ++ addrNxt, regRowid, 1); ++ VdbeCoverage(pParse->pVdbe); ++ }else{ ++ u16 nEq = pLoop->u.btree.nEq; ++ int r1; ++ char *zStartAff; ++ ++ assert( pLoop->wsFlags & WHERE_INDEXED ); ++ assert( (pLoop->wsFlags & WHERE_COLUMN_IN)==0 ); ++ r1 = codeAllEqualityTerms(pParse,pLevel,0,0,&zStartAff); ++ codeApplyAffinity(pParse, r1, nEq, zStartAff); ++ sqlite3DbFree(pParse->db, zStartAff); ++ sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, ++ addrNxt, r1, nEq); ++ VdbeCoverage(pParse->pVdbe); ++ } ++ pLevel->regFilter = 0; ++ pLevel->addrBrk = 0; ++ } ++} ++ ++/* ++** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...) ++** operator. Return true if level pLoop is guaranteed to visit only one ++** row for each key generated for the index. ++*/ ++static int whereLoopIsOneRow(WhereLoop *pLoop){ ++ if( pLoop->u.btree.pIndex->onError ++ && pLoop->nSkip==0 ++ && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol ++ ){ ++ int ii; ++ for(ii=0; iiu.btree.nEq; ii++){ ++ if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){ ++ return 0; ++ } ++ } ++ return 1; ++ } ++ return 0; ++} ++ + /* + ** Generate code for the start of the iLevel-th loop in the WHERE clause + ** implementation described by pWInfo. +@@ -142809,7 +161077,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + WhereClause *pWC; /* Decomposition of the entire WHERE clause */ + WhereTerm *pTerm; /* A WHERE clause term */ + sqlite3 *db; /* Database connection */ +- struct SrcList_item *pTabItem; /* FROM clause term being coded */ ++ SrcItem *pTabItem; /* FROM clause term being coded */ + int addrBrk; /* Jump here to break out of the loop */ + int addrHalt; /* addrBrk for the outermost loop */ + int addrCont; /* Jump here to continue with next cycle */ +@@ -142825,14 +161093,17 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + iCur = pTabItem->iCursor; + pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); + bRev = (pWInfo->revMask>>iLevel)&1; +- VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); +-#if WHERETRACE_ENABLED /* 0x20800 */ +- if( sqlite3WhereTrace & 0x800 ){ ++ VdbeModuleComment((v, "Begin WHERE-loop%d: %s", ++ iLevel, pTabItem->pSTab->zName)); ++#if WHERETRACE_ENABLED /* 0x4001 */ ++ if( sqlite3WhereTrace & 0x1 ){ + sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n", + iLevel, pWInfo->nLevel, (u64)notReady, pLevel->iFrom); +- sqlite3WhereLoopPrint(pLoop, pWC); ++ if( sqlite3WhereTrace & 0x1000 ){ ++ sqlite3WhereLoopPrint(pLoop, pWC); ++ } + } +- if( sqlite3WhereTrace & 0x20000 ){ ++ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){ + if( iLevel==0 ){ + sqlite3DebugPrintf("WHERE clause being coded:\n"); + sqlite3TreeViewExpr(0, pWInfo->pWhere, 0); +@@ -142859,27 +161130,34 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** initialize a memory cell that records if this table matches any + ** row of the left table of the join. + */ +- assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) ++ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) + || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 + ); + if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ + pLevel->iLeftJoin = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); +- VdbeComment((v, "init LEFT JOIN no-match flag")); ++ VdbeComment((v, "init LEFT JOIN match flag")); + } + + /* Compute a safe address to jump to if we discover that the table for + ** this loop is empty and can never contribute content. */ +- for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){} ++ for(j=iLevel; j>0; j--){ ++ if( pWInfo->a[j].iLeftJoin ) break; ++ if( pWInfo->a[j].pRJ ) break; ++ } + addrHalt = pWInfo->a[j].addrBrk; + + /* Special case of a FROM clause subquery implemented as a co-routine */ + if( pTabItem->fg.viaCoroutine ){ +- int regYield = pTabItem->regReturn; +- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); ++ int regYield; ++ Subquery *pSubq; ++ assert( pTabItem->fg.isSubquery && pTabItem->u4.pSubq!=0 ); ++ pSubq = pTabItem->u4.pSubq; ++ regYield = pSubq->regReturn; ++ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); + pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); + VdbeCoverage(v); +- VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); ++ VdbeComment((v, "next row of %s", pTabItem->pSTab->zName)); + pLevel->op = OP_Goto; + }else + +@@ -142891,7 +161169,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + int iReg; /* P3 Value for OP_VFilter */ + int addrNotFound; + int nConstraint = pLoop->nLTerm; +- int iIn; /* Counter for IN constraints */ + + iReg = sqlite3GetTempRange(pParse, nConstraint+2); + addrNotFound = pLevel->addrBrk; +@@ -142900,57 +161177,94 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + pTerm = pLoop->aLTerm[j]; + if( NEVER(pTerm==0) ) continue; + if( pTerm->eOperator & WO_IN ){ +- codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); +- addrNotFound = pLevel->addrNxt; ++ if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){ ++ int iTab = pParse->nTab++; ++ int iCache = ++pParse->nMem; ++ sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab); ++ sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache); ++ }else{ ++ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); ++ addrNotFound = pLevel->addrNxt; ++ } + }else{ + Expr *pRight = pTerm->pExpr->pRight; + codeExprOrVector(pParse, pRight, iTarget, 1); ++ if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ++ && pLoop->u.vtab.bOmitOffset ++ ){ ++ assert( pTerm->eOperator==WO_AUX ); ++ assert( pWInfo->pSelect!=0 ); ++ assert( pWInfo->pSelect->iOffset>0 ); ++ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pSelect->iOffset); ++ VdbeComment((v,"Zero OFFSET counter")); ++ } + } + } + sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); + sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1); ++ /* The instruction immediately prior to OP_VFilter must be an OP_Integer ++ ** that sets the "argc" value for xVFilter. This is necessary for ++ ** resolveP2() to work correctly. See tag-20250207a. */ + sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, + pLoop->u.vtab.idxStr, + pLoop->u.vtab.needFree ? P4_DYNAMIC : P4_STATIC); + VdbeCoverage(v); + pLoop->u.vtab.needFree = 0; ++ /* An OOM inside of AddOp4(OP_VFilter) instruction above might have freed ++ ** the u.vtab.idxStr. NULL it out to prevent a use-after-free */ ++ if( db->mallocFailed ) pLoop->u.vtab.idxStr = 0; + pLevel->p1 = iCur; + pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext; + pLevel->p2 = sqlite3VdbeCurrentAddr(v); +- iIn = pLevel->u.in.nIn; +- for(j=nConstraint-1; j>=0; j--){ ++ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); ++ ++ for(j=0; jaLTerm[j]; +- if( (pTerm->eOperator & WO_IN)!=0 ) iIn--; + if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){ + disableTerm(pLevel, pTerm); +- }else if( (pTerm->eOperator & WO_IN)!=0 +- && sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 ++ continue; ++ } ++ if( (pTerm->eOperator & WO_IN)!=0 ++ && (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0 ++ && !db->mallocFailed + ){ + Expr *pCompare; /* The comparison operator */ + Expr *pRight; /* RHS of the comparison */ + VdbeOp *pOp; /* Opcode to access the value of the IN constraint */ ++ int iIn; /* IN loop corresponding to the j-th constraint */ + + /* Reload the constraint value into reg[iReg+j+2]. The same value + ** was loaded into the same register prior to the OP_VFilter, but + ** the xFilter implementation might have changed the datatype or +- ** encoding of the value in the register, so it *must* be reloaded. */ +- assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed ); +- if( !db->mallocFailed ){ +- assert( iIn>=0 && iInu.in.nIn ); ++ ** encoding of the value in the register, so it *must* be reloaded. ++ */ ++ for(iIn=0; ALWAYS(iInu.in.nIn); iIn++){ + pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[iIn].addrInTop); +- assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid ); +- assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 ); +- assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 ); +- testcase( pOp->opcode==OP_Rowid ); +- sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); ++ if( (pOp->opcode==OP_Column && pOp->p3==iReg+j+2) ++ || (pOp->opcode==OP_Rowid && pOp->p2==iReg+j+2) ++ ){ ++ testcase( pOp->opcode==OP_Rowid ); ++ sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); ++ break; ++ } + } + + /* Generate code that will continue to the next row if +- ** the IN constraint is not satisfied */ ++ ** the IN constraint is not satisfied ++ */ + pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0); +- assert( pCompare!=0 || db->mallocFailed ); +- if( pCompare ){ +- pCompare->pLeft = pTerm->pExpr->pLeft; ++ if( !db->mallocFailed ){ ++ int iFld = pTerm->u.x.iField; ++ Expr *pLeft = pTerm->pExpr->pLeft; ++ assert( pLeft!=0 ); ++ if( iFld>0 ){ ++ assert( pLeft->op==TK_VECTOR ); ++ assert( ExprUseXList(pLeft) ); ++ assert( iFld<=pLeft->x.pList->nExpr ); ++ pCompare->pLeft = pLeft->x.pList->a[iFld-1].pExpr; ++ }else{ ++ pCompare->pLeft = pLeft; ++ } + pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0); + if( pRight ){ + pRight->iTable = iReg+j+2; +@@ -142959,11 +161273,11 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ); + } + pCompare->pLeft = 0; +- sqlite3ExprDelete(db, pCompare); + } ++ sqlite3ExprDelete(db, pCompare); + } + } +- assert( iIn==0 || db->mallocFailed ); ++ + /* These registers need to be preserved in case there is an IN operator + ** loop. So we could deallocate the registers here (and potentially + ** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems +@@ -142991,12 +161305,17 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); + if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg); + addrNxt = pLevel->addrNxt; ++ if( pLevel->regFilter ){ ++ sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); ++ VdbeCoverage(v); ++ sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, ++ iRowidReg, 1); ++ VdbeCoverage(v); ++ filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); ++ } + sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); + VdbeCoverage(v); + pLevel->op = OP_Noop; +- if( (pTerm->prereqAll & pLevel->notReady)==0 ){ +- pTerm->wtFlags |= TERM_CODED; +- } + }else if( (pLoop->wsFlags & WHERE_IPK)!=0 + && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 + ){ +@@ -143034,7 +161353,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + }; + assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */ + assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */ +- assert( TK_GE==TK_GT+3 ); /* ... is correcct. */ ++ assert( TK_GE==TK_GT+3 ); /* ... is correct. */ + + assert( (pStart->wtFlags & TERM_VNULL)==0 ); + testcase( pStart->wtFlags & TERM_VIRTUAL ); +@@ -143172,6 +161491,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */ + int omitTable; /* True if we use the index only */ + int regBignull = 0; /* big-null flag register */ ++ int addrSeekScan = 0; /* Opcode of the OP_SeekScan, if any */ + + pIdx = pLoop->u.btree.pIndex; + iIdxCur = pLevel->iIdxCur; +@@ -143243,14 +161563,18 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** a forward order scan on a descending index, interchange the + ** start and end terms (pRangeStart and pRangeEnd). + */ +- if( (nEqnKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) +- || (bRev && pIdx->nKeyCol==nEq) +- ){ ++ if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) ){ + SWAP(WhereTerm *, pRangeEnd, pRangeStart); + SWAP(u8, bSeekPastNull, bStopAtNull); + SWAP(u8, nBtm, nTop); + } + ++ if( iLevel>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){ ++ /* In case OP_SeekScan is used, ensure that the index cursor does not ++ ** point to a valid row for the first iteration of this loop. */ ++ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); ++ } ++ + /* Generate code to evaluate all constraint terms using == or IN + ** and store the values of those terms in an array of registers + ** starting at regBase. +@@ -143310,16 +161634,38 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** above has already left the cursor sitting on the correct row, + ** so no further seeking is needed */ + }else{ +- if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){ +- sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur); +- } + if( regBignull ){ + sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull); + VdbeComment((v, "NULL-scan pass ctr")); + } ++ if( pLevel->regFilter ){ ++ sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, ++ regBase, nEq); ++ VdbeCoverage(v); ++ filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); ++ } + + op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; + assert( op!=0 ); ++ if( (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 && op==OP_SeekGE ){ ++ assert( regBignull==0 ); ++ /* TUNING: The OP_SeekScan opcode seeks to reduce the number ++ ** of expensive seek operations by replacing a single seek with ++ ** 1 or more step operations. The question is, how many steps ++ ** should we try before giving up and going with a seek. The cost ++ ** of a seek is proportional to the logarithm of the of the number ++ ** of entries in the tree, so basing the number of steps to try ++ ** on the estimated number of rows in the btree seems like a good ++ ** guess. */ ++ addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan, ++ (pIdx->aiRowLogEst[0]+9)/10); ++ if( pRangeStart || pRangeEnd ){ ++ sqlite3VdbeChangeP5(v, 1); ++ sqlite3VdbeChangeP2(v, addrSeekScan, sqlite3VdbeCurrentAddr(v)+1); ++ addrSeekScan = 0; ++ } ++ VdbeCoverage(v); ++ } + sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); + VdbeCoverage(v); + VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind ); +@@ -143351,8 +161697,10 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** range (if any). + */ + nConstraint = nEq; ++ assert( pLevel->p2==0 ); + if( pRangeEnd ){ + Expr *pRight = pRangeEnd->pExpr->pRight; ++ assert( addrSeekScan==0 ); + codeExprOrVector(pParse, pRight, regBase+nEq, nTop); + whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); + if( (pRangeEnd->wtFlags & TERM_VNULL)==0 +@@ -143382,8 +161730,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + } + nConstraint++; + } +- sqlite3DbFree(db, zStartAff); +- sqlite3DbFree(db, zEndAff); ++ if( zStartAff ) sqlite3DbNNFreeNN(db, zStartAff); ++ if( zEndAff ) sqlite3DbNNFreeNN(db, zEndAff); + + /* Top of the loop body */ + pLevel->p2 = sqlite3VdbeCurrentAddr(v); +@@ -143402,6 +161750,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE ); + testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT ); + testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); ++ if( addrSeekScan ) sqlite3VdbeJumpHere(v, addrSeekScan); + } + if( regBignull ){ + /* During a NULL-scan, check to see if we have reached the end of +@@ -143421,27 +161770,17 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); + } + +- if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){ +- sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1); ++ if( (pLoop->wsFlags & WHERE_IN_EARLYOUT)!=0 ){ ++ sqlite3VdbeAddOp3(v, OP_SeekHit, iIdxCur, nEq, nEq); + } + + /* Seek the table cursor, if required */ + omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 +- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; ++ && (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0; + if( omitTable ){ + /* pIdx is a covering index. No need to access the main table. */ + }else if( HasRowid(pIdx->pTable) ){ +- if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE) +- || ( (pWInfo->wctrlFlags & WHERE_SEEK_UNIQ_TABLE)!=0 +- && (pWInfo->eOnePass==ONEPASS_SINGLE || pLoop->nLTerm==0) ) +- ){ +- iRowidReg = ++pParse->nMem; +- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); +- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg); +- VdbeCoverage(v); +- }else{ +- codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur); +- } ++ codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur); + }else if( iCur!=iIdxCur ){ + Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); + iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol); +@@ -143454,35 +161793,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + } + + if( pLevel->iLeftJoin==0 ){ +- /* If pIdx is an index on one or more expressions, then look through +- ** all the expressions in pWInfo and try to transform matching expressions +- ** into reference to index columns. Also attempt to translate references +- ** to virtual columns in the table into references to (stored) columns +- ** of the index. +- ** +- ** Do not do this for the RHS of a LEFT JOIN. This is because the +- ** expression may be evaluated after OP_NullRow has been executed on +- ** the cursor. In this case it is important to do the full evaluation, +- ** as the result of the expression may not be NULL, even if all table +- ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a +- ** +- ** Also, do not do this when processing one index an a multi-index +- ** OR clause, since the transformation will become invalid once we +- ** move forward to the next index. +- ** https://sqlite.org/src/info/4e8e4857d32d401f +- */ +- if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ +- whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); +- } +- + /* If a partial index is driving the loop, try to eliminate WHERE clause + ** terms from the query that must be true due to the WHERE clause of +- ** the partial index. ++ ** the partial index. This optimization does not work on an outer join, ++ ** as shown by: + ** +- ** 2019-11-02 ticket 623eff57e76d45f6: This optimization does not work +- ** for a LEFT JOIN. ++ ** 2019-11-02 ticket 623eff57e76d45f6 (LEFT JOIN) ++ ** 2025-05-29 forum post 7dee41d32506c4ae (RIGHT JOIN) + */ +- if( pIdx->pPartIdxWhere ){ ++ if( pIdx->pPartIdxWhere && pLevel->pRJ==0 ){ + whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC); + } + }else{ +@@ -143490,11 +161809,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + /* The following assert() is not a requirement, merely an observation: + ** The OR-optimization doesn't work for the right hand table of + ** a LEFT JOIN: */ +- assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ); ++ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); + } + + /* Record the instruction used to terminate the loop. */ +- if( pLoop->wsFlags & WHERE_ONEROW ){ ++ if( (pLoop->wsFlags & WHERE_ONEROW) ++ || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop)) ++ ){ + pLevel->op = OP_Noop; + }else if( bRev ){ + pLevel->op = OP_Prev; +@@ -143568,9 +161889,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + int iRetInit; /* Address of regReturn init */ + int untestedTerms = 0; /* Some terms not completely tested */ + int ii; /* Loop counter */ +- u16 wctrlFlags; /* Flags for sub-WHERE clause */ + Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ +- Table *pTab = pTabItem->pTab; ++ Table *pTab = pTabItem->pSTab; + + pTerm = pLoop->aLTerm[0]; + assert( pTerm!=0 ); +@@ -143586,10 +161906,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + */ + if( pWInfo->nLevel>1 ){ + int nNotReady; /* The number of notReady tables */ +- struct SrcList_item *origSrc; /* Original list of tables */ ++ SrcItem *origSrc; /* Original list of tables */ + nNotReady = pWInfo->nLevel - iLevel - 1; +- pOrTab = sqlite3StackAllocRaw(db, +- sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); ++ pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1)); + if( pOrTab==0 ) return notReady; + pOrTab->nAlloc = (u8)(nNotReady + 1); + pOrTab->nSrc = pOrTab->nAlloc; +@@ -143629,7 +161948,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); + + /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y +- ** Then for every term xN, evaluate as the subexpression: xN AND z ++ ** Then for every term xN, evaluate as the subexpression: xN AND y + ** That way, terms in y that are factored into the disjunction will + ** be picked up by the recursive calls to sqlite3WhereBegin() below. + ** +@@ -143640,7 +161959,21 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** + ** This optimization also only applies if the (x1 OR x2 OR ...) term + ** is not contained in the ON clause of a LEFT JOIN. +- ** See ticket http://www.sqlite.org/src/info/f2369304e4 ++ ** See ticket http://sqlite.org/src/info/f2369304e4 ++ ** ++ ** 2022-02-04: Do not push down slices of a row-value comparison. ++ ** In other words, "w" or "y" may not be a slice of a vector. Otherwise, ++ ** the initialization of the right-hand operand of the vector comparison ++ ** might not occur, or might occur only in an OR branch that is not ++ ** taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1. ++ ** ++ ** 2022-03-03: Do not push down expressions that involve subqueries. ++ ** The subquery might get coded as a subroutine. Any table-references ++ ** in the subquery might be resolved to index-references for the index on ++ ** the OR branch in which the subroutine is coded. But if the subroutine ++ ** is invoked from a different OR branch that uses a different index, such ++ ** index-references will not work. tag-20220303a ++ ** https://sqlite.org/forum/forumpost/36937b197273d403 + */ + if( pWC->nTerm>1 ){ + int iTerm; +@@ -143649,9 +161982,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + if( &pWC->a[iTerm] == pTerm ) continue; + testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL ); + testcase( pWC->a[iTerm].wtFlags & TERM_CODED ); +- if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue; ++ testcase( pWC->a[iTerm].wtFlags & TERM_SLICE ); ++ if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED|TERM_SLICE))!=0 ){ ++ continue; ++ } + if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; +- testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); ++ if( ExprHasProperty(pExpr, EP_Subquery) ) continue; /* tag-20220303a */ + pExpr = sqlite3ExprDup(db, pExpr, 0); + pAndExpr = sqlite3ExprAnd(pParse, pAndExpr, pExpr); + } +@@ -143659,7 +161995,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + /* The extra 0x10000 bit on the opcode is masked off and does not + ** become part of the new Expr.op. However, it does make the + ** op==TK_AND comparison inside of sqlite3PExpr() false, and this +- ** prevents sqlite3PExpr() from implementing AND short-circuit ++ ** prevents sqlite3PExpr() from applying the AND short-circuit + ** optimization, which we do not want here. */ + pAndExpr = sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr); + } +@@ -143669,27 +162005,32 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** eliminating duplicates from other WHERE clauses, the action for each + ** sub-WHERE clause is to to invoke the main loop body as a subroutine. + */ +- wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE); + ExplainQueryPlan((pParse, 1, "MULTI-INDEX OR")); + for(ii=0; iinTerm; ii++){ + WhereTerm *pOrTerm = &pOrWc->a[ii]; + if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ + WhereInfo *pSubWInfo; /* Info for single OR-term scan */ + Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ ++ Expr *pDelete; /* Local copy of OR clause term */ + int jmp1 = 0; /* Address of jump operation */ + testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0 +- && !ExprHasProperty(pOrExpr, EP_FromJoin) ++ && !ExprHasProperty(pOrExpr, EP_OuterON) + ); /* See TH3 vtab25.400 and ticket 614b25314c766238 */ ++ pDelete = pOrExpr = sqlite3ExprDup(db, pOrExpr, 0); ++ if( db->mallocFailed ){ ++ sqlite3ExprDelete(db, pDelete); ++ continue; ++ } + if( pAndExpr ){ + pAndExpr->pLeft = pOrExpr; + pOrExpr = pAndExpr; + } + /* Loop through table entries that match term pOrTerm. */ + ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); +- WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); +- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, +- wctrlFlags, iCovCur); +- assert( pSubWInfo || pParse->nErr || db->mallocFailed ); ++ WHERETRACE(0xffffffff, ("Subplan for OR-clause:\n")); ++ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0, ++ WHERE_OR_SUBCLAUSE, iCovCur); ++ assert( pSubWInfo || pParse->nErr ); + if( pSubWInfo ){ + WhereLoop *pSubLoop; + int addrExplain = sqlite3WhereExplainOneScan( +@@ -143786,15 +162127,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + }else{ + pCov = 0; + } ++ if( sqlite3WhereUsesDeferredSeek(pSubWInfo) ){ ++ pWInfo->bDeferredSeek = 1; ++ } + + /* Finish the loop through table entries that match term pOrTerm. */ + sqlite3WhereEnd(pSubWInfo); + ExplainQueryPlanPop(pParse); + } ++ sqlite3ExprDelete(db, pDelete); + } + } + ExplainQueryPlanPop(pParse); +- pLevel->u.pCovidx = pCov; ++ assert( pLevel->pWLoop==pLoop ); ++ assert( (pLoop->wsFlags & WHERE_MULTI_OR)!=0 ); ++ assert( (pLoop->wsFlags & WHERE_IN_ABLE)==0 ); ++ pLevel->u.pCoveringIdx = pCov; + if( pCov ) pLevel->iIdxCur = iCovCur; + if( pAndExpr ){ + pAndExpr->pLeft = 0; +@@ -143804,7 +162152,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + sqlite3VdbeGoto(v, pLevel->addrBrk); + sqlite3VdbeResolveLabel(v, iLoopBody); + +- if( pWInfo->nLevel>1 ){ sqlite3StackFree(db, pOrTab); } ++ /* Set the P2 operand of the OP_Return opcode that will end the current ++ ** loop to point to this spot, which is the top of the next containing ++ ** loop. The byte-code formatter will use that P2 value as a hint to ++ ** indent everything in between the this point and the final OP_Return. ++ ** See tag-20220407a in vdbe.c and shell.c */ ++ assert( pLevel->op==OP_Return ); ++ pLevel->p2 = sqlite3VdbeCurrentAddr(v); ++ ++ if( pWInfo->nLevel>1 ){ sqlite3DbFreeNN(db, pOrTab); } + if( !untestedTerms ) disableTerm(pLevel, pTerm); + }else + #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ +@@ -143848,6 +162204,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** iLoop==3: Code all remaining expressions. + ** + ** An effort is made to skip unnecessary iterations of the loop. ++ ** ++ ** This optimization of causing simple query restrictions to occur before ++ ** more complex one is call the "push-down" optimization in MySQL. Here ++ ** in SQLite, the name is "MySQL push-down", since there is also another ++ ** totally unrelated optimization called "WHERE-clause push-down". ++ ** Sometimes the qualifier is omitted, resulting in an ambiguity, so beware. + */ + iLoop = (pIdx ? 1 : 2); + do{ +@@ -143866,10 +162228,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + } + pE = pTerm->pExpr; + assert( pE!=0 ); +- if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){ +- continue; ++ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ ++ if( !ExprHasProperty(pE,EP_OuterON|EP_InnerON) ){ ++ /* Defer processing WHERE clause constraints until after outer ++ ** join processing. tag-20220513a */ ++ continue; ++ }else if( (pTabItem->fg.jointype & JT_LEFT)==JT_LEFT ++ && !ExprHasProperty(pE,EP_OuterON) ){ ++ continue; ++ }else{ ++ Bitmask m = sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin); ++ if( m & pLevel->notReady ){ ++ /* An ON clause that is not ripe */ ++ continue; ++ } ++ } + } +- + if( iLoop==1 && !sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){ + iNext = 2; + continue; +@@ -143896,12 +162270,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + } + #endif + } +-#ifdef WHERETRACE_ENABLED /* 0xffff */ ++#ifdef WHERETRACE_ENABLED /* 0xffffffff */ + if( sqlite3WhereTrace ){ + VdbeNoopComment((v, "WhereTerm[%d] (%p) priority=%d", + pWC->nTerm-j, pTerm, iLoop)); + } +- if( sqlite3WhereTrace & 0x800 ){ ++ if( sqlite3WhereTrace & 0x4000 ){ + sqlite3DebugPrintf("Coding auxiliary constraint:\n"); + sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); + } +@@ -143921,29 +162295,30 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** then we cannot use the "t1.a=t2.b" constraint, but we can code + ** the implied "t1.a=123" constraint. + */ +- for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ ++ for(pTerm=pWC->a, j=pWC->nBase; j>0; j--, pTerm++){ + Expr *pE, sEAlt; + WhereTerm *pAlt; + if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; + if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; + if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; + if( pTerm->leftCursor!=iCur ) continue; +- if( pTabItem->fg.jointype & JT_LEFT ) continue; ++ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue; + pE = pTerm->pExpr; +-#ifdef WHERETRACE_ENABLED /* 0x800 */ +- if( sqlite3WhereTrace & 0x800 ){ ++#ifdef WHERETRACE_ENABLED /* 0x4001 */ ++ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){ + sqlite3DebugPrintf("Coding transitive constraint:\n"); + sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); + } + #endif +- assert( !ExprHasProperty(pE, EP_FromJoin) ); ++ assert( !ExprHasProperty(pE, EP_OuterON) ); + assert( (pTerm->prereqRight & pLevel->notReady)!=0 ); +- pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.leftColumn, notReady, ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.x.leftColumn, notReady, + WO_EQ|WO_IN|WO_IS, 0); + if( pAlt==0 ) continue; + if( pAlt->wtFlags & (TERM_CODED) ) continue; + if( (pAlt->eOperator & WO_IN) +- && (pAlt->pExpr->flags & EP_xIsSelect) ++ && ExprUseXSelect(pAlt->pExpr) + && (pAlt->pExpr->x.pSelect->pEList->nExpr>1) + ){ + continue; +@@ -143955,6 +162330,48 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + sEAlt = *pAlt->pExpr; + sEAlt.pLeft = pE->pLeft; + sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL); ++ pAlt->wtFlags |= TERM_CODED; ++ } ++ ++ /* For a RIGHT OUTER JOIN, record the fact that the current row has ++ ** been matched at least once. ++ */ ++ if( pLevel->pRJ ){ ++ Table *pTab; ++ int nPk; ++ int r; ++ int jmp1 = 0; ++ WhereRightJoin *pRJ = pLevel->pRJ; ++ ++ /* pTab is the right-hand table of the RIGHT JOIN. Generate code that ++ ** will record that the current row of that table has been matched at ++ ** least once. This is accomplished by storing the PK for the row in ++ ** both the iMatch index and the regBloom Bloom filter. ++ */ ++ pTab = pWInfo->pTabList->a[pLevel->iFrom].pSTab; ++ if( HasRowid(pTab) ){ ++ r = sqlite3GetTempRange(pParse, 2); ++ sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1); ++ nPk = 1; ++ }else{ ++ int iPk; ++ Index *pPk = sqlite3PrimaryKeyIndex(pTab); ++ nPk = pPk->nKeyCol; ++ r = sqlite3GetTempRange(pParse, nPk+1); ++ for(iPk=0; iPkaiColumn[iPk]; ++ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+1+iPk); ++ } ++ } ++ jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, 0, r+1, nPk); ++ VdbeCoverage(v); ++ VdbeComment((v, "match against %s", pTab->zName)); ++ sqlite3VdbeAddOp3(v, OP_MakeRecord, r+1, nPk, r); ++ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pRJ->iMatch, r, r+1, nPk); ++ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pRJ->regBloom, 0, r+1, nPk); ++ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); ++ sqlite3VdbeJumpHere(v, jmp1); ++ sqlite3ReleaseTempRange(pParse, r, nPk+1); + } + + /* For a LEFT OUTER JOIN, generate code that will record the fact that +@@ -143964,7 +162381,31 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + pLevel->addrFirst = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); + VdbeComment((v, "record LEFT JOIN hit")); +- for(pTerm=pWC->a, j=0; jnTerm; j++, pTerm++){ ++ if( pLevel->pRJ==0 ){ ++ goto code_outer_join_constraints; /* WHERE clause constraints */ ++ } ++ } ++ ++ if( pLevel->pRJ ){ ++ /* Create a subroutine used to process all interior loops and code ++ ** of the RIGHT JOIN. During normal operation, the subroutine will ++ ** be in-line with the rest of the code. But at the end, a separate ++ ** loop will run that invokes this subroutine for unmatched rows ++ ** of pTab, with all tables to left begin set to NULL. ++ */ ++ WhereRightJoin *pRJ = pLevel->pRJ; ++ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn); ++ pRJ->addrSubrtn = sqlite3VdbeCurrentAddr(v); ++ assert( pParse->withinRJSubrtn < 255 ); ++ pParse->withinRJSubrtn++; ++ ++ /* WHERE clause constraints must be deferred until after outer join ++ ** row elimination has completed, since WHERE clause constraints apply ++ ** to the results of the OUTER JOIN. The following loop generates the ++ ** appropriate WHERE clause constraint checks. tag-20220513a. ++ */ ++ code_outer_join_constraints: ++ for(pTerm=pWC->a, j=0; jnBase; j++, pTerm++){ + testcase( pTerm->wtFlags & TERM_VIRTUAL ); + testcase( pTerm->wtFlags & TERM_CODED ); + if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; +@@ -143972,19 +162413,20 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + assert( pWInfo->untestedTerms ); + continue; + } ++ if( pTabItem->fg.jointype & JT_LTORJ ) continue; + assert( pTerm->pExpr ); + sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); + pTerm->wtFlags |= TERM_CODED; + } + } + +-#if WHERETRACE_ENABLED /* 0x20800 */ +- if( sqlite3WhereTrace & 0x20000 ){ ++#if WHERETRACE_ENABLED /* 0x4001 */ ++ if( sqlite3WhereTrace & 0x4000 ){ + sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n", + iLevel); + sqlite3WhereClausePrint(pWC); + } +- if( sqlite3WhereTrace & 0x800 ){ ++ if( sqlite3WhereTrace & 0x1 ){ + sqlite3DebugPrintf("End Coding level %d: notReady=%llx\n", + iLevel, (u64)pLevel->notReady); + } +@@ -143992,6 +162434,111 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + return pLevel->notReady; + } + ++/* ++** Generate the code for the loop that finds all non-matched terms ++** for a RIGHT JOIN. ++*/ ++SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( ++ WhereInfo *pWInfo, ++ int iLevel, ++ WhereLevel *pLevel ++){ ++ Parse *pParse = pWInfo->pParse; ++ Vdbe *v = pParse->pVdbe; ++ WhereRightJoin *pRJ = pLevel->pRJ; ++ Expr *pSubWhere = 0; ++ WhereClause *pWC = &pWInfo->sWC; ++ WhereInfo *pSubWInfo; ++ WhereLoop *pLoop = pLevel->pWLoop; ++ SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; ++ SrcList *pFrom; ++ u8 fromSpace[SZ_SRCLIST_1]; ++ Bitmask mAll = 0; ++ int k; ++ ++ ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); ++ sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, ++ pRJ->regReturn); ++ for(k=0; ka[k].pWLoop->iTab == pWInfo->a[k].iFrom ); ++ pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; ++ mAll |= pWInfo->a[k].pWLoop->maskSelf; ++ if( pRight->fg.viaCoroutine ){ ++ Subquery *pSubq; ++ assert( pRight->fg.isSubquery && pRight->u4.pSubq!=0 ); ++ pSubq = pRight->u4.pSubq; ++ assert( pSubq->pSelect!=0 && pSubq->pSelect->pEList!=0 ); ++ sqlite3VdbeAddOp3( ++ v, OP_Null, 0, pSubq->regResult, ++ pSubq->regResult + pSubq->pSelect->pEList->nExpr-1 ++ ); ++ } ++ sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); ++ iIdxCur = pWInfo->a[k].iIdxCur; ++ if( iIdxCur ){ ++ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); ++ } ++ } ++ if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ ++ mAll |= pLoop->maskSelf; ++ for(k=0; knTerm; k++){ ++ WhereTerm *pTerm = &pWC->a[k]; ++ if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0 ++ && pTerm->eOperator!=WO_ROWVAL ++ ){ ++ break; ++ } ++ if( pTerm->prereqAll & ~mAll ) continue; ++ if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; ++ pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, ++ sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); ++ } ++ } ++ pFrom = (SrcList*)fromSpace; ++ pFrom->nSrc = 1; ++ pFrom->nAlloc = 1; ++ memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem)); ++ pFrom->a[0].fg.jointype = 0; ++ assert( pParse->withinRJSubrtn < 100 ); ++ pParse->withinRJSubrtn++; ++ pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0, ++ WHERE_RIGHT_JOIN, 0); ++ if( pSubWInfo ){ ++ int iCur = pLevel->iTabCur; ++ int r = ++pParse->nMem; ++ int nPk; ++ int jmp; ++ int addrCont = sqlite3WhereContinueLabel(pSubWInfo); ++ Table *pTab = pTabItem->pSTab; ++ if( HasRowid(pTab) ){ ++ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r); ++ nPk = 1; ++ }else{ ++ int iPk; ++ Index *pPk = sqlite3PrimaryKeyIndex(pTab); ++ nPk = pPk->nKeyCol; ++ pParse->nMem += nPk - 1; ++ for(iPk=0; iPkaiColumn[iPk]; ++ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk); ++ } ++ } ++ jmp = sqlite3VdbeAddOp4Int(v, OP_Filter, pRJ->regBloom, 0, r, nPk); ++ VdbeCoverage(v); ++ sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, addrCont, r, nPk); ++ VdbeCoverage(v); ++ sqlite3VdbeJumpHere(v, jmp); ++ sqlite3VdbeAddOp2(v, OP_Gosub, pRJ->regReturn, pRJ->addrSubrtn); ++ sqlite3WhereEnd(pSubWInfo); ++ } ++ sqlite3ExprDelete(pParse->db, pSubWhere); ++ ExplainQueryPlanPop(pParse); ++ assert( pParse->withinRJSubrtn>0 ); ++ pParse->withinRJSubrtn--; ++} ++ + /************** End of wherecode.c *******************************************/ + /************** Begin file whereexpr.c ***************************************/ + /* +@@ -144009,7 +162556,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + ** the WHERE clause of SQL statements. + ** + ** This file was originally part of where.c but was split out to improve +-** readability and editabiliity. This file contains utility routines for ++** readability and editability. This file contains utility routines for + ** analyzing Expr objects in the WHERE clause. + */ + /* #include "sqliteInt.h" */ +@@ -144060,7 +162607,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ + if( pWC->nTerm>=pWC->nSlot ){ + WhereTerm *pOld = pWC->a; + sqlite3 *db = pWC->pWInfo->pParse->db; +- pWC->a = sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 ); ++ pWC->a = sqlite3WhereMalloc(pWC->pWInfo, sizeof(pWC->a[0])*pWC->nSlot*2 ); + if( pWC->a==0 ){ + if( wtFlags & TERM_DYNAMIC ){ + sqlite3ExprDelete(db, p); +@@ -144069,12 +162616,10 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ + return 0; + } + memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm); +- if( pOld!=pWC->aStatic ){ +- sqlite3DbFree(db, pOld); +- } +- pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); ++ pWC->nSlot = pWC->nSlot*2; + } + pTerm = &pWC->a[idx = pWC->nTerm++]; ++ if( (wtFlags & TERM_VIRTUAL)==0 ) pWC->nBase = pWC->nTerm; + if( p && ExprHasProperty(p, EP_Unlikely) ){ + pTerm->truthProb = sqlite3LogEst(p->iTable) - 270; + }else{ +@@ -144099,7 +162644,12 @@ static int allowedOp(int op){ + assert( TK_LT>TK_EQ && TK_LTTK_EQ && TK_LE=TK_EQ && op<=TK_GE) || op==TK_ISNULL || op==TK_IS; ++ assert( TK_INTK_GE ) return 0; ++ if( op>=TK_EQ ) return 1; ++ return op==TK_IN || op==TK_ISNULL || op==TK_IS; + } + + /* +@@ -144132,15 +162682,16 @@ static u16 exprCommute(Parse *pParse, Expr *pExpr){ + static u16 operatorMask(int op){ + u16 c; + assert( allowedOp(op) ); +- if( op==TK_IN ){ ++ if( op>=TK_EQ ){ ++ assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); ++ c = (u16)(WO_EQ<<(op-TK_EQ)); ++ }else if( op==TK_IN ){ + c = WO_IN; + }else if( op==TK_ISNULL ){ + c = WO_ISNULL; +- }else if( op==TK_IS ){ +- c = WO_IS; + }else{ +- assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); +- c = (u16)(WO_EQ<<(op-TK_EQ)); ++ assert( op==TK_IS ); ++ c = WO_IS; + } + assert( op!=TK_ISNULL || c==WO_ISNULL ); + assert( op!=TK_IN || c==WO_IN ); +@@ -144191,6 +162742,7 @@ static int isLikeOrGlob( + #ifdef SQLITE_EBCDIC + if( *pnoCase ) return 0; + #endif ++ assert( ExprUseXList(pExpr) ); + pList = pExpr->x.pList; + pLeft = pList->a[1].pExpr; + +@@ -144206,15 +162758,32 @@ static int isLikeOrGlob( + sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); + assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); + }else if( op==TK_STRING ){ +- z = (u8*)pRight->u.zToken; ++ assert( !ExprHasProperty(pRight, EP_IntValue) ); ++ z = (u8*)pRight->u.zToken; + } + if( z ){ +- +- /* Count the number of prefix characters prior to the first wildcard */ ++ /* Count the number of prefix bytes prior to the first wildcard, ++ ** U+fffd character, or malformed utf-8. If the underlying database ++ ** has a UTF16LE encoding, then only consider ASCII characters. Note that ++ ** the encoding of z[] is UTF8 - we are dealing with only UTF8 here in this ++ ** code, but the database engine itself might be processing content using a ++ ** different encoding. */ + cnt = 0; + while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ + cnt++; +- if( c==wc[3] && z[cnt]!=0 ) cnt++; ++ if( c==wc[3] && z[cnt]>0 && z[cnt]<0x80 ){ ++ cnt++; ++ }else if( c>=0x80 ){ ++ const u8 *z2 = z+cnt-1; ++ if( c==0xff || sqlite3Utf8Read(&z2)==0xfffd /* bad utf-8 */ ++ || ENC(db)==SQLITE_UTF16LE ++ ){ ++ cnt--; ++ break; ++ }else{ ++ cnt = (int)(z2-z); ++ } ++ } + } + + /* The optimization is possible only if (1) the pattern does not begin +@@ -144225,17 +162794,19 @@ static int isLikeOrGlob( + ** range search. The third is because the caller assumes that the pattern + ** consists of at least one character after all escapes have been + ** removed. */ +- if( cnt!=0 && 255!=(u8)z[cnt-1] && (cnt>1 || z[0]!=wc[3]) ){ ++ if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && ALWAYS(255!=(u8)z[cnt-1]) ){ + Expr *pPrefix; + + /* A "complete" match if the pattern ends with "*" or "%" */ +- *pisComplete = c==wc[0] && z[cnt+1]==0; ++ *pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE; + + /* Get the pattern prefix. Remove all escapes from the prefix. */ + pPrefix = sqlite3Expr(db, TK_STRING, (char*)z); + if( pPrefix ){ + int iFrom, iTo; +- char *zNew = pPrefix->u.zToken; ++ char *zNew; ++ assert( !ExprHasProperty(pPrefix, EP_IntValue) ); ++ zNew = pPrefix->u.zToken; + zNew[cnt] = 0; + for(iFrom=iTo=0; iFromop!=TK_COLUMN + || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT +- || IsVirtual(pLeft->y.pTab) /* Value might be numeric */ ++ || (ALWAYS( ExprUseYTab(pLeft) ) ++ && ALWAYS(pLeft->y.pTab) ++ && IsVirtual(pLeft->y.pTab)) /* Might be numeric */ + ){ + int isNum; + double rDummy; +@@ -144287,6 +162860,7 @@ static int isLikeOrGlob( + if( op==TK_VARIABLE ){ + Vdbe *v = pParse->pVdbe; + sqlite3VdbeSetVarmask(v, pRight->iColumn); ++ assert( !ExprHasProperty(pRight, EP_IntValue) ); + if( *pisComplete && pRight->u.zToken[1] ){ + /* If the rhs of the LIKE expression is a variable, and the current + ** value of the variable means there is no need to invoke the LIKE +@@ -144360,6 +162934,7 @@ static int isAuxiliaryVtabOperator( + Expr *pCol; /* Column reference */ + int i; + ++ assert( ExprUseXList(pExpr) ); + pList = pExpr->x.pList; + if( pList==0 || pList->nExpr!=2 ){ + return 0; +@@ -144373,9 +162948,10 @@ static int isAuxiliaryVtabOperator( + ** MATCH(expression,vtab_column) + */ + pCol = pList->a[1].pExpr; +- testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); ++ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); + if( ExprIsVtab(pCol) ){ + for(i=0; iu.zToken, aOp[i].zOp)==0 ){ + *peOp2 = aOp[i].eOp2; + *ppRight = pList->a[0].pExpr; +@@ -144396,7 +162972,8 @@ static int isAuxiliaryVtabOperator( + ** with function names in an arbitrary case. + */ + pCol = pList->a[0].pExpr; +- testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); ++ assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) ); ++ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); + if( ExprIsVtab(pCol) ){ + sqlite3_vtab *pVtab; + sqlite3_module *pMod; +@@ -144405,6 +162982,7 @@ static int isAuxiliaryVtabOperator( + pVtab = sqlite3GetVTable(db, pCol->y.pTab)->pVtab; + assert( pVtab!=0 ); + assert( pVtab->pModule!=0 ); ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); + pMod = (sqlite3_module *)pVtab->pModule; + if( pMod->xFindFunction!=0 ){ + i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed); +@@ -144416,15 +162994,23 @@ static int isAuxiliaryVtabOperator( + } + } + } ++ }else if( pExpr->op>=TK_EQ ){ ++ /* Comparison operators are a common case. Save a few comparisons for ++ ** that common case by terminating early. */ ++ assert( TK_NE < TK_EQ ); ++ assert( TK_ISNOT < TK_EQ ); ++ assert( TK_NOTNULL < TK_EQ ); ++ return 0; + }else if( pExpr->op==TK_NE || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL ){ + int res = 0; + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pRight; +- testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); ++ assert( pLeft->op!=TK_COLUMN || (ExprUseYTab(pLeft) && pLeft->y.pTab!=0) ); + if( ExprIsVtab(pLeft) ){ + res++; + } +- testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); ++ assert( pRight==0 || pRight->op!=TK_COLUMN ++ || (ExprUseYTab(pRight) && pRight->y.pTab!=0) ); + if( pRight && ExprIsVtab(pRight) ){ + res++; + SWAP(Expr*, pLeft, pRight); +@@ -144445,9 +163031,9 @@ static int isAuxiliaryVtabOperator( + ** a join, then transfer the appropriate markings over to derived. + */ + static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ +- if( pDerived ){ +- pDerived->flags |= pBase->flags & EP_FromJoin; +- pDerived->iRightJoinTable = pBase->iRightJoinTable; ++ if( pDerived && ExprHasProperty(pBase, EP_OuterON|EP_InnerON) ){ ++ pDerived->flags |= pBase->flags & (EP_OuterON|EP_InnerON); ++ pDerived->w.iJoin = pBase->w.iJoin; + } + } + +@@ -144507,6 +163093,7 @@ static void whereCombineDisjuncts( + int op; /* Operator for the combined expression */ + int idxNew; /* Index in pWC of the next virtual term */ + ++ if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; + if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; + if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; + if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp +@@ -144675,6 +163262,7 @@ static void exprAnalyzeOrTerm( + pOrTerm->u.pAndInfo = pAndInfo; + pOrTerm->wtFlags |= TERM_ANDINFO; + pOrTerm->eOperator = WO_AND; ++ pOrTerm->leftCursor = -1; + pAndWC = &pAndInfo->wc; + memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic)); + sqlite3WhereClauseInit(pAndWC, pWC->pWInfo); +@@ -144717,11 +163305,10 @@ static void exprAnalyzeOrTerm( + ** empty. + */ + pOrInfo->indexable = indexable; ++ pTerm->eOperator = WO_OR; ++ pTerm->leftCursor = -1; + if( indexable ){ +- pTerm->eOperator = WO_OR; + pWC->hasOr = 1; +- }else{ +- pTerm->eOperator = WO_OR; + } + + /* For a two-way OR, attempt to implementation case 2. +@@ -144776,7 +163363,7 @@ static void exprAnalyzeOrTerm( + pOrTerm = pOrWc->a; + for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ + assert( pOrTerm->eOperator & WO_EQ ); +- pOrTerm->wtFlags &= ~TERM_OR_OK; ++ pOrTerm->wtFlags &= ~TERM_OK; + if( pOrTerm->leftCursor==iCursor ){ + /* This is the 2-bit case and we are on the second iteration and + ** current term is from the first iteration. So skip this term. */ +@@ -144787,14 +163374,15 @@ static void exprAnalyzeOrTerm( + pOrTerm->leftCursor))==0 ){ + /* This term must be of the form t1.a==t2.b where t2 is in the + ** chngToIN set but t1 is not. This term will be either preceded +- ** or follwed by an inverted copy (t2.b==t1.a). Skip this term ++ ** or followed by an inverted copy (t2.b==t1.a). Skip this term + ** and use its inversion. */ + testcase( pOrTerm->wtFlags & TERM_COPIED ); + testcase( pOrTerm->wtFlags & TERM_VIRTUAL ); + assert( pOrTerm->wtFlags & (TERM_COPIED|TERM_VIRTUAL) ); + continue; + } +- iColumn = pOrTerm->u.leftColumn; ++ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ iColumn = pOrTerm->u.x.leftColumn; + iCursor = pOrTerm->leftCursor; + pLeft = pOrTerm->pExpr->pLeft; + break; +@@ -144814,9 +163402,10 @@ static void exprAnalyzeOrTerm( + okToChngToIN = 1; + for(; i>=0 && okToChngToIN; i--, pOrTerm++){ + assert( pOrTerm->eOperator & WO_EQ ); ++ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); + if( pOrTerm->leftCursor!=iCursor ){ +- pOrTerm->wtFlags &= ~TERM_OR_OK; +- }else if( pOrTerm->u.leftColumn!=iColumn || (iColumn==XN_EXPR ++ pOrTerm->wtFlags &= ~TERM_OK; ++ }else if( pOrTerm->u.x.leftColumn!=iColumn || (iColumn==XN_EXPR + && sqlite3ExprCompare(pParse, pOrTerm->pExpr->pLeft, pLeft, -1) + )){ + okToChngToIN = 0; +@@ -144831,7 +163420,7 @@ static void exprAnalyzeOrTerm( + if( affRight!=0 && affRight!=affLeft ){ + okToChngToIN = 0; + }else{ +- pOrTerm->wtFlags |= TERM_OR_OK; ++ pOrTerm->wtFlags |= TERM_OK; + } + } + } +@@ -144848,10 +163437,11 @@ static void exprAnalyzeOrTerm( + Expr *pNew; /* The complete IN operator */ + + for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ +- if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; ++ if( (pOrTerm->wtFlags & TERM_OK)==0 ) continue; + assert( pOrTerm->eOperator & WO_EQ ); ++ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); + assert( pOrTerm->leftCursor==iCursor ); +- assert( pOrTerm->u.leftColumn==iColumn ); ++ assert( pOrTerm->u.x.leftColumn==iColumn ); + pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); + pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup); + pLeft = pOrTerm->pExpr->pLeft; +@@ -144862,12 +163452,12 @@ static void exprAnalyzeOrTerm( + if( pNew ){ + int idxNew; + transferJoinMarkings(pNew, pExpr); +- assert( !ExprHasProperty(pNew, EP_xIsSelect) ); ++ assert( ExprUseXList(pNew) ); + pNew->x.pList = pList; + idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); + testcase( idxNew==0 ); + exprAnalyze(pSrc, pWC, idxNew); +- /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */ ++ /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where reused */ + markTermAsChild(pWC, idxNew, idxTerm); + }else{ + sqlite3ExprListDelete(db, pList); +@@ -144884,30 +163474,42 @@ static void exprAnalyzeOrTerm( + ** 1. The SQLITE_Transitive optimization must be enabled + ** 2. Must be either an == or an IS operator + ** 3. Not originating in the ON clause of an OUTER JOIN +-** 4. The affinities of A and B must be compatible +-** 5a. Both operands use the same collating sequence OR +-** 5b. The overall collating sequence is BINARY ++** 4. The operator is not IS or else the query does not contain RIGHT JOIN ++** 5. The affinities of A and B must be compatible ++** 6a. Both operands use the same collating sequence OR ++** 6b. The overall collating sequence is BINARY + ** If this routine returns TRUE, that means that the RHS can be substituted + ** for the LHS anyplace else in the WHERE clause where the LHS column occurs. + ** This is an optimization. No harm comes from returning 0. But if 1 is + ** returned when it should not be, then incorrect answers might result. + */ +-static int termIsEquivalence(Parse *pParse, Expr *pExpr){ ++static int termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){ + char aff1, aff2; + CollSeq *pColl; +- if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; +- if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; +- if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0; ++ if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; /* (1) */ ++ if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; /* (2) */ ++ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* (3) */ ++ assert( pSrc!=0 ); ++ if( pExpr->op==TK_IS ++ && pSrc->nSrc ++ && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ++ ){ ++ return 0; /* (4) */ ++ } + aff1 = sqlite3ExprAffinity(pExpr->pLeft); + aff2 = sqlite3ExprAffinity(pExpr->pRight); + if( aff1!=aff2 + && (!sqlite3IsNumericAffinity(aff1) || !sqlite3IsNumericAffinity(aff2)) + ){ +- return 0; ++ return 0; /* (5) */ + } + pColl = sqlite3ExprCompareCollSeq(pParse, pExpr); +- if( sqlite3IsBinary(pColl) ) return 1; +- return sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight); ++ if( !sqlite3IsBinary(pColl) ++ && !sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight) ++ ){ ++ return 0; /* (6) */ ++ } ++ return 1; + } + + /* +@@ -144927,8 +163529,12 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ + if( ALWAYS(pSrc!=0) ){ + int i; + for(i=0; inSrc; i++){ +- mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); +- mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn); ++ if( pSrc->a[i].fg.isSubquery ){ ++ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].u4.pSubq->pSelect); ++ } ++ if( pSrc->a[i].fg.isUsing==0 ){ ++ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn); ++ } + if( pSrc->a[i].fg.isTabFunc ){ + mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); + } +@@ -144954,35 +163560,40 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ + */ + static SQLITE_NOINLINE int exprMightBeIndexed2( + SrcList *pFrom, /* The FROM clause */ +- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ + int *aiCurCol, /* Write the referenced table cursor and column here */ +- Expr *pExpr /* An operand of a comparison operator */ ++ Expr *pExpr, /* An operand of a comparison operator */ ++ int j /* Start looking with the j-th pFrom entry */ + ){ + Index *pIdx; + int i; + int iCur; +- for(i=0; mPrereq>1; i++, mPrereq>>=1){} +- iCur = pFrom->a[i].iCursor; +- for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ +- if( pIdx->aColExpr==0 ) continue; +- for(i=0; inKeyCol; i++){ +- if( pIdx->aiColumn[i]!=XN_EXPR ) continue; +- if( sqlite3ExprCompareSkip(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){ +- aiCurCol[0] = iCur; +- aiCurCol[1] = XN_EXPR; +- return 1; ++ do{ ++ iCur = pFrom->a[j].iCursor; ++ for(pIdx=pFrom->a[j].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){ ++ if( pIdx->aColExpr==0 ) continue; ++ for(i=0; inKeyCol; i++){ ++ if( pIdx->aiColumn[i]!=XN_EXPR ) continue; ++ assert( pIdx->bHasExpr ); ++ if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0 ++ && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr) ++ ){ ++ aiCurCol[0] = iCur; ++ aiCurCol[1] = XN_EXPR; ++ return 1; ++ } + } + } +- } ++ }while( ++j < pFrom->nSrc ); + return 0; + } + static int exprMightBeIndexed( + SrcList *pFrom, /* The FROM clause */ +- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ + int *aiCurCol, /* Write the referenced table cursor & column here */ + Expr *pExpr, /* An operand of a comparison operator */ + int op /* The specific comparison operator */ + ){ ++ int i; ++ + /* If this expression is a vector to the left or right of a + ** inequality constraint (>, <, >= or <=), perform the processing + ** on the first element of the vector. */ +@@ -144990,6 +163601,7 @@ static int exprMightBeIndexed( + assert( TK_ISop==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){ ++ assert( ExprUseXList(pExpr) ); + pExpr = pExpr->x.pList->a[0].pExpr; + } + +@@ -144998,11 +163610,19 @@ static int exprMightBeIndexed( + aiCurCol[1] = pExpr->iColumn; + return 1; + } +- if( mPrereq==0 ) return 0; /* No table references */ +- if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */ +- return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr); ++ ++ for(i=0; inSrc; i++){ ++ Index *pIdx; ++ for(pIdx=pFrom->a[i].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){ ++ if( pIdx->aColExpr ){ ++ return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i); ++ } ++ } ++ } ++ return 0; + } + ++ + /* + ** The input to this routine is an WhereTerm structure with only the + ** "pExpr" field filled in. The job of this routine is to analyze the +@@ -145030,8 +163650,8 @@ static void exprAnalyze( + WhereTerm *pTerm; /* The term to be analyzed */ + WhereMaskSet *pMaskSet; /* Set of table index masks */ + Expr *pExpr; /* The expression to be analyzed */ +- Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ +- Bitmask prereqAll; /* Prerequesites of pExpr */ ++ Bitmask prereqLeft; /* Prerequisites of the pExpr->pLeft */ ++ Bitmask prereqAll; /* Prerequisites of pExpr */ + Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ + Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ + int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ +@@ -145045,36 +163665,67 @@ static void exprAnalyze( + if( db->mallocFailed ){ + return; + } ++ assert( pWC->nTerm > idxTerm ); + pTerm = &pWC->a[idxTerm]; + pMaskSet = &pWInfo->sMaskSet; + pExpr = pTerm->pExpr; ++ assert( pExpr!=0 ); /* Because malloc() has not failed */ + assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); ++ pMaskSet->bVarSelect = 0; + prereqLeft = sqlite3WhereExprUsage(pMaskSet, pExpr->pLeft); + op = pExpr->op; + if( op==TK_IN ){ + assert( pExpr->pRight==0 ); + if( sqlite3ExprCheckIN(pParse, pExpr) ) return; +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect); + }else{ + pTerm->prereqRight = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList); + } +- }else if( op==TK_ISNULL ){ +- pTerm->prereqRight = 0; ++ prereqAll = prereqLeft | pTerm->prereqRight; + }else{ + pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight); ++ if( pExpr->pLeft==0 ++ || ExprHasProperty(pExpr, EP_xIsSelect|EP_IfNullRow) ++ || pExpr->x.pList!=0 ++ ){ ++ prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr); ++ }else{ ++ prereqAll = prereqLeft | pTerm->prereqRight; ++ } + } +- pMaskSet->bVarSelect = 0; +- prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr); + if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT; +- if( ExprHasProperty(pExpr, EP_FromJoin) ){ +- Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); +- prereqAll |= x; +- extraRight = x-1; /* ON clause terms may not be used with an index +- ** on left table of a LEFT JOIN. Ticket #3015 */ +- if( (prereqAll>>1)>=x ){ +- sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); +- return; ++ ++#ifdef SQLITE_DEBUG ++ if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){ ++ printf("\n*** Incorrect prereqAll computed for:\n"); ++ sqlite3TreeViewExpr(0,pExpr,0); ++ assert( 0 ); ++ } ++#endif ++ ++ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){ ++ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin); ++ if( ExprHasProperty(pExpr, EP_OuterON) ){ ++ prereqAll |= x; ++ extraRight = x-1; /* ON clause terms may not be used with an index ++ ** on left table of a LEFT JOIN. Ticket #3015 */ ++ if( (prereqAll>>1)>=x ){ ++ sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); ++ return; ++ } ++ }else if( (prereqAll>>1)>=x ){ ++ /* The ON clause of an INNER JOIN references a table to its right. ++ ** Most other SQL database engines raise an error. But SQLite versions ++ ** 3.0 through 3.38 just put the ON clause constraint into the WHERE ++ ** clause and carried on. Beginning with 3.39, raise an error only ++ ** if there is a RIGHT or FULL JOIN in the query. This makes SQLite ++ ** more like other systems, and also preserves legacy. */ ++ if( ALWAYS(pSrc->nSrc>0) && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ ++ sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); ++ return; ++ } ++ ExprClearProperty(pExpr, EP_InnerON); + } + } + pTerm->prereqAll = prereqAll; +@@ -145087,25 +163738,28 @@ static void exprAnalyze( + Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); + u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; + +- if( pTerm->iField>0 ){ ++ if( pTerm->u.x.iField>0 ){ + assert( op==TK_IN ); + assert( pLeft->op==TK_VECTOR ); +- pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr; ++ assert( ExprUseXList(pLeft) ); ++ pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr; + } + +- if( exprMightBeIndexed(pSrc, prereqLeft, aiCurCol, pLeft, op) ){ ++ if( exprMightBeIndexed(pSrc, aiCurCol, pLeft, op) ){ + pTerm->leftCursor = aiCurCol[0]; +- pTerm->u.leftColumn = aiCurCol[1]; ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ pTerm->u.x.leftColumn = aiCurCol[1]; + pTerm->eOperator = operatorMask(op) & opMask; + } + if( op==TK_IS ) pTerm->wtFlags |= TERM_IS; + if( pRight +- && exprMightBeIndexed(pSrc, pTerm->prereqRight, aiCurCol, pRight, op) ++ && exprMightBeIndexed(pSrc, aiCurCol, pRight, op) ++ && !ExprHasProperty(pRight, EP_FixedCol) + ){ + WhereTerm *pNew; + Expr *pDup; + u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ +- assert( pTerm->iField==0 ); ++ assert( pTerm->u.x.iField==0 ); + if( pTerm->leftCursor>=0 ){ + int idxNew; + pDup = sqlite3ExprDup(db, pExpr, 0); +@@ -145120,8 +163774,8 @@ static void exprAnalyze( + if( op==TK_IS ) pNew->wtFlags |= TERM_IS; + pTerm = &pWC->a[idxTerm]; + pTerm->wtFlags |= TERM_COPIED; +- +- if( termIsEquivalence(pParse, pDup) ){ ++ assert( pWInfo->pTabList!=0 ); ++ if( termIsEquivalence(pParse, pDup, pWInfo->pTabList) ){ + pTerm->eOperator |= WO_EQUIV; + eExtraOp = WO_EQUIV; + } +@@ -145131,11 +163785,23 @@ static void exprAnalyze( + } + pNew->wtFlags |= exprCommute(pParse, pDup); + pNew->leftCursor = aiCurCol[0]; +- pNew->u.leftColumn = aiCurCol[1]; ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ pNew->u.x.leftColumn = aiCurCol[1]; + testcase( (prereqLeft | extraRight) != prereqLeft ); + pNew->prereqRight = prereqLeft | extraRight; + pNew->prereqAll = prereqAll; + pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; ++ }else ++ if( op==TK_ISNULL ++ && !ExprHasProperty(pExpr,EP_OuterON) ++ && 0==sqlite3ExprCanBeNull(pLeft) ++ ){ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */ ++ pExpr->u.zToken = "false"; ++ ExprSetProperty(pExpr, EP_IsFalse); ++ pTerm->prereqAll = 0; ++ pTerm->eOperator = 0; + } + } + +@@ -145156,9 +163822,11 @@ static void exprAnalyze( + ** BETWEEN term is skipped. + */ + else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ +- ExprList *pList = pExpr->x.pList; ++ ExprList *pList; + int i; + static const u8 ops[] = {TK_GE, TK_LE}; ++ assert( ExprUseXList(pExpr) ); ++ pList = pExpr->x.pList; + assert( pList!=0 ); + assert( pList->nExpr==2 ); + for(i=0; i<2; i++){ +@@ -145187,6 +163855,42 @@ static void exprAnalyze( + pTerm = &pWC->a[idxTerm]; + } + #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ ++ /* The form "x IS NOT NULL" can sometimes be evaluated more efficiently ++ ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a ++ ** virtual term of that form. ++ ** ++ ** The virtual term must be tagged with TERM_VNULL. ++ */ ++ else if( pExpr->op==TK_NOTNULL ){ ++ if( pExpr->pLeft->op==TK_COLUMN ++ && pExpr->pLeft->iColumn>=0 ++ && !ExprHasProperty(pExpr, EP_OuterON) ++ ){ ++ Expr *pNewExpr; ++ Expr *pLeft = pExpr->pLeft; ++ int idxNew; ++ WhereTerm *pNewTerm; ++ ++ pNewExpr = sqlite3PExpr(pParse, TK_GT, ++ sqlite3ExprDup(db, pLeft, 0), ++ sqlite3ExprAlloc(db, TK_NULL, 0, 0)); ++ ++ idxNew = whereClauseInsert(pWC, pNewExpr, ++ TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); ++ if( idxNew ){ ++ pNewTerm = &pWC->a[idxNew]; ++ pNewTerm->prereqRight = 0; ++ pNewTerm->leftCursor = pLeft->iTable; ++ pNewTerm->u.x.leftColumn = pLeft->iColumn; ++ pNewTerm->eOperator = WO_GT; ++ markTermAsChild(pWC, idxNew, idxTerm); ++ pTerm = &pWC->a[idxTerm]; ++ pTerm->wtFlags |= TERM_COPIED; ++ pNewTerm->prereqAll = pTerm->prereqAll; ++ } ++ } ++ } ++ + + #ifndef SQLITE_OMIT_LIKE_OPTIMIZATION + /* Add constraints to reduce the search space on a LIKE or GLOB +@@ -145202,7 +163906,8 @@ static void exprAnalyze( + ** bound is made all lowercase so that the bounds also work when comparing + ** BLOBs. + */ +- if( pWC->op==TK_AND ++ else if( pExpr->op==TK_FUNCTION ++ && pWC->op==TK_AND + && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) + ){ + Expr *pLeft; /* LHS of LIKE/GLOB operator */ +@@ -145214,8 +163919,12 @@ static void exprAnalyze( + const char *zCollSeqName; /* Name of collating sequence */ + const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC; + ++ assert( ExprUseXList(pExpr) ); + pLeft = pExpr->x.pList->a[1].pExpr; + pStr2 = sqlite3ExprDup(db, pStr1, 0); ++ assert( pStr1==0 || !ExprHasProperty(pStr1, EP_IntValue) ); ++ assert( pStr2==0 || !ExprHasProperty(pStr2, EP_IntValue) ); ++ + + /* Convert the lower bound to upper-case and the upper bound to + ** lower-case (upper-case is less than lower-case in ASCII) so that +@@ -145232,9 +163941,8 @@ static void exprAnalyze( + } + + if( !db->mallocFailed ){ +- u8 c, *pC; /* Last character before the first wildcard */ ++ u8 *pC; /* Last character before the first wildcard */ + pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; +- c = *pC; + if( noCase ){ + /* The point is to increment the last character before the first + ** wildcard. But if we increment '@', that will push it into the +@@ -145242,10 +163950,17 @@ static void exprAnalyze( + ** inequality. To avoid this, make sure to also run the full + ** LIKE on all candidate expressions by clearing the isComplete flag + */ +- if( c=='A'-1 ) isComplete = 0; +- c = sqlite3UpperToLower[c]; ++ if( *pC=='A'-1 ) isComplete = 0; ++ *pC = sqlite3UpperToLower[*pC]; ++ } ++ ++ /* Increment the value of the last utf8 character in the prefix. */ ++ while( *pC==0xBF && pC>(u8*)pStr2->u.zToken ){ ++ *pC = 0x80; ++ pC--; + } +- *pC = c + 1; ++ assert( *pC!=0xFF ); /* isLikeOrGlob() guarantees this */ ++ (*pC)++; + } + zCollSeqName = noCase ? "NOCASE" : sqlite3StrBINARY; + pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); +@@ -145255,7 +163970,6 @@ static void exprAnalyze( + transferJoinMarkings(pNewExpr1, pExpr); + idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags); + testcase( idxNew1==0 ); +- exprAnalyze(pSrc, pWC, idxNew1); + pNewExpr2 = sqlite3ExprDup(db, pLeft, 0); + pNewExpr2 = sqlite3PExpr(pParse, TK_LT, + sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName), +@@ -145263,6 +163977,7 @@ static void exprAnalyze( + transferJoinMarkings(pNewExpr2, pExpr); + idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags); + testcase( idxNew2==0 ); ++ exprAnalyze(pSrc, pWC, idxNew1); + exprAnalyze(pSrc, pWC, idxNew2); + pTerm = &pWC->a[idxTerm]; + if( isComplete ){ +@@ -145272,147 +163987,114 @@ static void exprAnalyze( + } + #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ + +-#ifndef SQLITE_OMIT_VIRTUALTABLE +- /* Add a WO_AUX auxiliary term to the constraint set if the +- ** current expression is of the form "column OP expr" where OP +- ** is an operator that gets passed into virtual tables but which is +- ** not normally optimized for ordinary tables. In other words, OP +- ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL. +- ** This information is used by the xBestIndex methods of +- ** virtual tables. The native query optimizer does not attempt +- ** to do anything with MATCH functions. +- */ +- if( pWC->op==TK_AND ){ +- Expr *pRight = 0, *pLeft = 0; +- int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight); +- while( res-- > 0 ){ +- int idxNew; +- WhereTerm *pNewTerm; +- Bitmask prereqColumn, prereqExpr; +- +- prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight); +- prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft); +- if( (prereqExpr & prereqColumn)==0 ){ +- Expr *pNewExpr; +- pNewExpr = sqlite3PExpr(pParse, TK_MATCH, +- 0, sqlite3ExprDup(db, pRight, 0)); +- if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){ +- ExprSetProperty(pNewExpr, EP_FromJoin); +- pNewExpr->iRightJoinTable = pExpr->iRightJoinTable; +- } +- idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); +- testcase( idxNew==0 ); +- pNewTerm = &pWC->a[idxNew]; +- pNewTerm->prereqRight = prereqExpr; +- pNewTerm->leftCursor = pLeft->iTable; +- pNewTerm->u.leftColumn = pLeft->iColumn; +- pNewTerm->eOperator = WO_AUX; +- pNewTerm->eMatchOp = eOp2; +- markTermAsChild(pWC, idxNew, idxTerm); +- pTerm = &pWC->a[idxTerm]; +- pTerm->wtFlags |= TERM_COPIED; +- pNewTerm->prereqAll = pTerm->prereqAll; +- } +- SWAP(Expr*, pLeft, pRight); +- } +- } +-#endif /* SQLITE_OMIT_VIRTUALTABLE */ +- + /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create + ** new terms for each component comparison - "a = ?" and "b = ?". The + ** new terms completely replace the original vector comparison, which is + ** no longer used. + ** + ** This is only required if at least one side of the comparison operation +- ** is not a sub-select. */ +- if( pWC->op==TK_AND +- && (pExpr->op==TK_EQ || pExpr->op==TK_IS) +- && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1 +- && sqlite3ExprVectorSize(pExpr->pRight)==nLeft +- && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 +- || (pExpr->pRight->flags & EP_xIsSelect)==0) ++ ** is not a sub-select. ++ ** ++ ** tag-20220128a ++ */ ++ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS) ++ && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1 ++ && sqlite3ExprVectorSize(pExpr->pRight)==nLeft ++ && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 ++ || (pExpr->pRight->flags & EP_xIsSelect)==0) ++ && pWC->op==TK_AND + ){ + int i; + for(i=0; ipLeft, i); +- Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i); ++ Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, nLeft); ++ Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i, nLeft); + + pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight); + transferJoinMarkings(pNew, pExpr); +- idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); ++ idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_SLICE); + exprAnalyze(pSrc, pWC, idxNew); + } + pTerm = &pWC->a[idxTerm]; + pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */ +- pTerm->eOperator = 0; ++ pTerm->eOperator = WO_ROWVAL; + } + + /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create + ** a virtual term for each vector component. The expression object + ** used by each such virtual term is pExpr (the full vector IN(...) +- ** expression). The WhereTerm.iField variable identifies the index within ++ ** expression). The WhereTerm.u.x.iField variable identifies the index within + ** the vector on the LHS that the virtual term represents. + ** + ** This only works if the RHS is a simple SELECT (not a compound) that does + ** not use window functions. + */ +- if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0 ++ else if( pExpr->op==TK_IN ++ && pTerm->u.x.iField==0 + && pExpr->pLeft->op==TK_VECTOR +- && pExpr->x.pSelect->pPrior==0 ++ && ALWAYS( ExprUseXSelect(pExpr) ) ++ && (pExpr->x.pSelect->pPrior==0 || (pExpr->x.pSelect->selFlags & SF_Values)) + #ifndef SQLITE_OMIT_WINDOWFUNC + && pExpr->x.pSelect->pWin==0 + #endif ++ && pWC->op==TK_AND + ){ + int i; + for(i=0; ipLeft); i++){ + int idxNew; +- idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL); +- pWC->a[idxNew].iField = i+1; ++ idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL|TERM_SLICE); ++ pWC->a[idxNew].u.x.iField = i+1; + exprAnalyze(pSrc, pWC, idxNew); + markTermAsChild(pWC, idxNew, idxTerm); + } + } + +-#ifdef SQLITE_ENABLE_STAT4 +- /* When sqlite_stat4 histogram data is available an operator of the +- ** form "x IS NOT NULL" can sometimes be evaluated more efficiently +- ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a +- ** virtual term of that form. +- ** +- ** Note that the virtual term must be tagged with TERM_VNULL. ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ /* Add a WO_AUX auxiliary term to the constraint set if the ++ ** current expression is of the form "column OP expr" where OP ++ ** is an operator that gets passed into virtual tables but which is ++ ** not normally optimized for ordinary tables. In other words, OP ++ ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL. ++ ** This information is used by the xBestIndex methods of ++ ** virtual tables. The native query optimizer does not attempt ++ ** to do anything with MATCH functions. + */ +- if( pExpr->op==TK_NOTNULL +- && pExpr->pLeft->op==TK_COLUMN +- && pExpr->pLeft->iColumn>=0 +- && !ExprHasProperty(pExpr, EP_FromJoin) +- && OptimizationEnabled(db, SQLITE_Stat4) +- ){ +- Expr *pNewExpr; +- Expr *pLeft = pExpr->pLeft; +- int idxNew; +- WhereTerm *pNewTerm; +- +- pNewExpr = sqlite3PExpr(pParse, TK_GT, +- sqlite3ExprDup(db, pLeft, 0), +- sqlite3ExprAlloc(db, TK_NULL, 0, 0)); +- +- idxNew = whereClauseInsert(pWC, pNewExpr, +- TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); +- if( idxNew ){ +- pNewTerm = &pWC->a[idxNew]; +- pNewTerm->prereqRight = 0; +- pNewTerm->leftCursor = pLeft->iTable; +- pNewTerm->u.leftColumn = pLeft->iColumn; +- pNewTerm->eOperator = WO_GT; +- markTermAsChild(pWC, idxNew, idxTerm); +- pTerm = &pWC->a[idxTerm]; +- pTerm->wtFlags |= TERM_COPIED; +- pNewTerm->prereqAll = pTerm->prereqAll; ++ else if( pWC->op==TK_AND ){ ++ Expr *pRight = 0, *pLeft = 0; ++ int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight); ++ while( res-- > 0 ){ ++ int idxNew; ++ WhereTerm *pNewTerm; ++ Bitmask prereqColumn, prereqExpr; ++ ++ prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight); ++ prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft); ++ if( (prereqExpr & prereqColumn)==0 ){ ++ Expr *pNewExpr; ++ pNewExpr = sqlite3PExpr(pParse, TK_MATCH, ++ 0, sqlite3ExprDup(db, pRight, 0)); ++ if( ExprHasProperty(pExpr, EP_OuterON) && pNewExpr ){ ++ ExprSetProperty(pNewExpr, EP_OuterON); ++ pNewExpr->w.iJoin = pExpr->w.iJoin; ++ } ++ idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); ++ testcase( idxNew==0 ); ++ pNewTerm = &pWC->a[idxNew]; ++ pNewTerm->prereqRight = prereqExpr; ++ pNewTerm->leftCursor = pLeft->iTable; ++ pNewTerm->u.x.leftColumn = pLeft->iColumn; ++ pNewTerm->eOperator = WO_AUX; ++ pNewTerm->eMatchOp = eOp2; ++ markTermAsChild(pWC, idxNew, idxTerm); ++ pTerm = &pWC->a[idxTerm]; ++ pTerm->wtFlags |= TERM_COPIED; ++ pNewTerm->prereqAll = pTerm->prereqAll; ++ } ++ SWAP(Expr*, pLeft, pRight); + } + } +-#endif /* SQLITE_ENABLE_STAT4 */ ++#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + /* Prevent ON clause terms of a LEFT JOIN from being used to drive + ** an index for tables to the left of the join. +@@ -145447,6 +164129,7 @@ static void exprAnalyze( + SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ + Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); + pWC->op = op; ++ assert( pE2!=0 || pExpr==0 ); + if( pE2==0 ) return; + if( pE2->op!=op ){ + whereClauseInsert(pWC, pExpr, 0); +@@ -145456,6 +164139,123 @@ SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ + } + } + ++/* ++** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or ++** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the ++** where-clause passed as the first argument. The value for the term ++** is found in register iReg. ++** ++** In the common case where the value is a simple integer ++** (example: "LIMIT 5 OFFSET 10") then the expression codes as a ++** TK_INTEGER so that it will be available to sqlite3_vtab_rhs_value(). ++** If not, then it codes as a TK_REGISTER expression. ++*/ ++static void whereAddLimitExpr( ++ WhereClause *pWC, /* Add the constraint to this WHERE clause */ ++ int iReg, /* Register that will hold value of the limit/offset */ ++ Expr *pExpr, /* Expression that defines the limit/offset */ ++ int iCsr, /* Cursor to which the constraint applies */ ++ int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */ ++){ ++ Parse *pParse = pWC->pWInfo->pParse; ++ sqlite3 *db = pParse->db; ++ Expr *pNew; ++ int iVal = 0; ++ ++ if( sqlite3ExprIsInteger(pExpr, &iVal, pParse) && iVal>=0 ){ ++ Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); ++ if( pVal==0 ) return; ++ ExprSetProperty(pVal, EP_IntValue); ++ pVal->u.iValue = iVal; ++ pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); ++ }else{ ++ Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0); ++ if( pVal==0 ) return; ++ pVal->iTable = iReg; ++ pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); ++ } ++ if( pNew ){ ++ WhereTerm *pTerm; ++ int idx; ++ idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL); ++ pTerm = &pWC->a[idx]; ++ pTerm->leftCursor = iCsr; ++ pTerm->eOperator = WO_AUX; ++ pTerm->eMatchOp = eMatchOp; ++ } ++} ++ ++/* ++** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the ++** SELECT statement passed as the second argument. These terms are only ++** added if: ++** ++** 1. The SELECT statement has a LIMIT clause, and ++** 2. The SELECT statement is not an aggregate or DISTINCT query, and ++** 3. The SELECT statement has exactly one object in its from clause, and ++** that object is a virtual table, and ++** 4. There are no terms in the WHERE clause that will not be passed ++** to the virtual table xBestIndex method. ++** 5. The ORDER BY clause, if any, will be made available to the xBestIndex ++** method. ++** ++** LIMIT and OFFSET terms are ignored by most of the planner code. They ++** exist only so that they may be passed to the xBestIndex method of the ++** single virtual table in the FROM clause of the SELECT. ++*/ ++SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ ++ assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */ ++ if( p->pGroupBy==0 ++ && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ ++ && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pSTab)) /* 3 */ ++ ){ ++ ExprList *pOrderBy = p->pOrderBy; ++ int iCsr = p->pSrc->a[0].iCursor; ++ int ii; ++ ++ /* Check condition (4). Return early if it is not met. */ ++ for(ii=0; iinTerm; ii++){ ++ if( pWC->a[ii].wtFlags & TERM_CODED ){ ++ /* This term is a vector operation that has been decomposed into ++ ** other, subsequent terms. It can be ignored. See tag-20220128a */ ++ assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); ++ assert( pWC->a[ii].eOperator==WO_ROWVAL ); ++ continue; ++ } ++ if( pWC->a[ii].nChild ){ ++ /* If this term has child terms, then they are also part of the ++ ** pWC->a[] array. So this term can be ignored, as a LIMIT clause ++ ** will only be added if each of the child terms passes the ++ ** (leftCursor==iCsr) test below. */ ++ continue; ++ } ++ if( pWC->a[ii].leftCursor!=iCsr ) return; ++ if( pWC->a[ii].prereqRight!=0 ) return; ++ } ++ ++ /* Check condition (5). Return early if it is not met. */ ++ if( pOrderBy ){ ++ for(ii=0; iinExpr; ii++){ ++ Expr *pExpr = pOrderBy->a[ii].pExpr; ++ if( pExpr->op!=TK_COLUMN ) return; ++ if( pExpr->iTable!=iCsr ) return; ++ if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; ++ } ++ } ++ ++ /* All conditions are met. Add the terms to the where-clause object. */ ++ assert( p->pLimit->op==TK_LIMIT ); ++ if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){ ++ whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, ++ iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); ++ } ++ if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){ ++ whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, ++ iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); ++ } ++ } ++} ++ + /* + ** Initialize a preallocated WhereClause structure. + */ +@@ -145467,6 +164267,7 @@ SQLITE_PRIVATE void sqlite3WhereClauseInit( + pWC->hasOr = 0; + pWC->pOuter = 0; + pWC->nTerm = 0; ++ pWC->nBase = 0; + pWC->nSlot = ArraySize(pWC->aStatic); + pWC->a = pWC->aStatic; + } +@@ -145477,22 +164278,36 @@ SQLITE_PRIVATE void sqlite3WhereClauseInit( + ** sqlite3WhereClauseInit(). + */ + SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){ +- int i; +- WhereTerm *a; + sqlite3 *db = pWC->pWInfo->pParse->db; +- for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){ +- if( a->wtFlags & TERM_DYNAMIC ){ +- sqlite3ExprDelete(db, a->pExpr); ++ assert( pWC->nTerm>=pWC->nBase ); ++ if( pWC->nTerm>0 ){ ++ WhereTerm *a = pWC->a; ++ WhereTerm *aLast = &pWC->a[pWC->nTerm-1]; ++#ifdef SQLITE_DEBUG ++ int i; ++ /* Verify that every term past pWC->nBase is virtual */ ++ for(i=pWC->nBase; inTerm; i++){ ++ assert( (pWC->a[i].wtFlags & TERM_VIRTUAL)!=0 ); + } +- if( a->wtFlags & TERM_ORINFO ){ +- whereOrInfoDelete(db, a->u.pOrInfo); +- }else if( a->wtFlags & TERM_ANDINFO ){ +- whereAndInfoDelete(db, a->u.pAndInfo); ++#endif ++ while(1){ ++ assert( a->eMatchOp==0 || a->eOperator==WO_AUX ); ++ if( a->wtFlags & TERM_DYNAMIC ){ ++ sqlite3ExprDelete(db, a->pExpr); ++ } ++ if( a->wtFlags & (TERM_ORINFO|TERM_ANDINFO) ){ ++ if( a->wtFlags & TERM_ORINFO ){ ++ assert( (a->wtFlags & TERM_ANDINFO)==0 ); ++ whereOrInfoDelete(db, a->u.pOrInfo); ++ }else{ ++ assert( (a->wtFlags & TERM_ANDINFO)!=0 ); ++ whereAndInfoDelete(db, a->u.pAndInfo); ++ } ++ } ++ if( a==aLast ) break; ++ a++; + } + } +- if( pWC->a!=pWC->aStatic ){ +- sqlite3DbFree(db, pWC->a); +- } + } + + +@@ -145500,28 +164315,52 @@ SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){ + ** These routines walk (recursively) an expression tree and generate + ** a bitmask indicating which tables are used in that expression + ** tree. ++** ++** sqlite3WhereExprUsage(MaskSet, Expr) -> ++** ++** Return a Bitmask of all tables referenced by Expr. Expr can be ++** be NULL, in which case 0 is returned. ++** ++** sqlite3WhereExprUsageNN(MaskSet, Expr) -> ++** ++** Same as sqlite3WhereExprUsage() except that Expr must not be ++** NULL. The "NN" suffix on the name stands for "Not Null". ++** ++** sqlite3WhereExprListUsage(MaskSet, ExprList) -> ++** ++** Return a Bitmask of all tables referenced by every expression ++** in the expression list ExprList. ExprList can be NULL, in which ++** case 0 is returned. ++** ++** sqlite3WhereExprUsageFull(MaskSet, ExprList) -> ++** ++** Internal use only. Called only by sqlite3WhereExprUsageNN() for ++** complex expressions that require pushing register values onto ++** the stack. Many calls to sqlite3WhereExprUsageNN() do not need ++** the more complex analysis done by this routine. Hence, the ++** computations done by this routine are broken out into a separate ++** "no-inline" function to avoid the stack push overhead in the ++** common case where it is not needed. + */ +-SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ ++static SQLITE_NOINLINE Bitmask sqlite3WhereExprUsageFull( ++ WhereMaskSet *pMaskSet, ++ Expr *p ++){ + Bitmask mask; +- if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ +- return sqlite3WhereGetMask(pMaskSet, p->iTable); +- }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ +- assert( p->op!=TK_IF_NULL_ROW ); +- return 0; +- } + mask = (p->op==TK_IF_NULL_ROW) ? sqlite3WhereGetMask(pMaskSet, p->iTable) : 0; + if( p->pLeft ) mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pLeft); + if( p->pRight ){ + mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pRight); + assert( p->x.pList==0 ); +- }else if( ExprHasProperty(p, EP_xIsSelect) ){ ++ }else if( ExprUseXSelect(p) ){ + if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1; + mask |= exprSelectUsage(pMaskSet, p->x.pSelect); + }else if( p->x.pList ){ + mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList); + } + #ifndef SQLITE_OMIT_WINDOWFUNC +- if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && p->y.pWin ){ ++ if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && ExprUseYWin(p) ){ ++ assert( p->y.pWin!=0 ); + mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition); + mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy); + mask |= sqlite3WhereExprUsage(pMaskSet, p->y.pWin->pFilter); +@@ -145529,6 +164368,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ + #endif + return mask; + } ++SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ ++ if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ ++ return sqlite3WhereGetMask(pMaskSet, p->iTable); ++ }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ ++ assert( p->op!=TK_IF_NULL_ROW ); ++ return 0; ++ } ++ return sqlite3WhereExprUsageFull(pMaskSet, p); ++} + SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ + return p ? sqlite3WhereExprUsageNN(pMaskSet,p) : 0; + } +@@ -145571,7 +164419,7 @@ SQLITE_PRIVATE void sqlite3WhereExprAnalyze( + */ + SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( + Parse *pParse, /* Parsing context */ +- struct SrcList_item *pItem, /* The FROM clause term to process */ ++ SrcItem *pItem, /* The FROM clause term to process */ + WhereClause *pWC /* Xfer function arguments to here */ + ){ + Table *pTab; +@@ -145580,12 +164428,13 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( + Expr *pColRef; + Expr *pTerm; + if( pItem->fg.isTabFunc==0 ) return; +- pTab = pItem->pTab; ++ pTab = pItem->pSTab; + assert( pTab!=0 ); + pArgs = pItem->u1.pFuncArg; + if( pArgs==0 ) return; + for(j=k=0; jnExpr; j++){ + Expr *pRhs; ++ u32 joinType; + while( knCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;} + if( k>=pTab->nCol ){ + sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d", +@@ -145596,13 +164445,21 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( + if( pColRef==0 ) return; + pColRef->iTable = pItem->iCursor; + pColRef->iColumn = k++; ++ assert( ExprUseYTab(pColRef) ); + pColRef->y.pTab = pTab; ++ pItem->colUsed |= sqlite3ExprColUsed(pColRef); + pRhs = sqlite3PExpr(pParse, TK_UPLUS, + sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); + pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); +- if( pItem->fg.jointype & JT_LEFT ){ +- sqlite3SetJoinExpr(pTerm, pItem->iCursor); ++ if( pItem->fg.jointype & (JT_LEFT|JT_RIGHT) ){ ++ testcase( pItem->fg.jointype & JT_LEFT ); /* testtag-20230227a */ ++ testcase( pItem->fg.jointype & JT_RIGHT ); /* testtag-20230227b */ ++ joinType = EP_OuterON; ++ }else{ ++ testcase( pItem->fg.jointype & JT_LTORJ ); /* testtag-20230227c */ ++ joinType = EP_InnerON; + } ++ sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType); + whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); + } + } +@@ -145641,19 +164498,24 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( + */ + typedef struct HiddenIndexInfo HiddenIndexInfo; + struct HiddenIndexInfo { +- WhereClause *pWC; /* The Where clause being analyzed */ +- Parse *pParse; /* The parsing context */ ++ WhereClause *pWC; /* The Where clause being analyzed */ ++ Parse *pParse; /* The parsing context */ ++ int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ ++ u32 mIn; /* Mask of terms that are IN (...) */ ++ u32 mHandleIn; /* Terms that vtab will handle as IN (...) */ ++ sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST ++ ** Extra space is allocated to hold up ++ ** to nTerm such values */ + }; + ++/* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as ++** many as N constraints */ ++#define SZ_HIDDENINDEXINFO(N) \ ++ (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*)) ++ + /* Forward declaration of methods */ + static int whereLoopResize(sqlite3*, WhereLoop*, int); + +-/* Test variable that can be set to enable WHERE tracing */ +-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) +-/***/ int sqlite3WhereTrace = 0; +-#endif +- +- + /* + ** Return the estimated number of output rows from a WHERE clause + */ +@@ -145678,7 +164540,7 @@ SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){ + ** block sorting is required. + */ + SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){ +- return pWInfo->nOBSat; ++ return pWInfo->nOBSat<0 ? 0 : pWInfo->nOBSat; + } + + /* +@@ -145713,7 +164575,33 @@ SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){ + } + pInner = &pWInfo->a[pWInfo->nLevel-1]; + assert( pInner->addrNxt!=0 ); +- return pInner->addrNxt; ++ return pInner->pRJ ? pWInfo->iContinue : pInner->addrNxt; ++} ++ ++/* ++** While generating code for the min/max optimization, after handling ++** the aggregate-step call to min() or max(), check to see if any ++** additional looping is required. If the output order is such that ++** we are certain that the correct answer has already been found, then ++** code an OP_Goto to by pass subsequent processing. ++** ++** Any extra OP_Goto that is coded here is an optimization. The ++** correct answer should be obtained regardless. This OP_Goto just ++** makes the answer appear faster. ++*/ ++SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe *v, WhereInfo *pWInfo){ ++ WhereLevel *pInner; ++ int i; ++ if( !pWInfo->bOrderedInnerLoop ) return; ++ if( pWInfo->nOBSat==0 ) return; ++ for(i=pWInfo->nLevel-1; i>=0; i--){ ++ pInner = &pWInfo->a[i]; ++ if( (pInner->pWLoop->wsFlags & WHERE_COLUMN_IN)!=0 ){ ++ sqlite3VdbeGoto(v, pInner->addrNxt); ++ return; ++ } ++ } ++ sqlite3VdbeGoto(v, pWInfo->iBreak); + } + + /* +@@ -145825,7 +164713,12 @@ whereOrInsert_done: + SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ + int i; + assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); +- for(i=0; in; i++){ ++ assert( pMaskSet->n>0 || pMaskSet->ix[0]<0 ); ++ assert( iCursor>=-1 ); ++ if( pMaskSet->ix[0]==iCursor ){ ++ return 1; ++ } ++ for(i=1; in; i++){ + if( pMaskSet->ix[i]==iCursor ){ + return MASKBIT(i); + } +@@ -145833,6 +164726,30 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ + return 0; + } + ++/* Allocate memory that is automatically freed when pWInfo is freed. ++*/ ++SQLITE_PRIVATE void *sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte){ ++ WhereMemBlock *pBlock; ++ pBlock = sqlite3DbMallocRawNN(pWInfo->pParse->db, nByte+sizeof(*pBlock)); ++ if( pBlock ){ ++ pBlock->pNext = pWInfo->pMemToFree; ++ pBlock->sz = nByte; ++ pWInfo->pMemToFree = pBlock; ++ pBlock++; ++ } ++ return (void*)pBlock; ++} ++SQLITE_PRIVATE void *sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte){ ++ void *pNew = sqlite3WhereMalloc(pWInfo, nByte); ++ if( pNew && pOld ){ ++ WhereMemBlock *pOldBlk = (WhereMemBlock*)pOld; ++ pOldBlk--; ++ assert( pOldBlk->szsz); ++ } ++ return pNew; ++} ++ + /* + ** Create a new mask for cursor iCursor. + ** +@@ -145846,6 +164763,54 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){ + pMaskSet->ix[pMaskSet->n++] = iCursor; + } + ++/* ++** If the right-hand branch of the expression is a TK_COLUMN, then return ++** a pointer to the right-hand branch. Otherwise, return NULL. ++*/ ++static Expr *whereRightSubexprIsColumn(Expr *p){ ++ p = sqlite3ExprSkipCollateAndLikely(p->pRight); ++ if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ ++ return p; ++ } ++ return 0; ++} ++ ++/* ++** Term pTerm is guaranteed to be a WO_IN term. It may be a component term ++** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)". ++** This function checks to see if the term is compatible with an index ++** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so, ++** it returns a pointer to the name of the collation sequence (e.g. "BINARY" ++** or "NOCASE") used by the comparison in pTerm. If it is not compatible ++** with affinity idxaff, NULL is returned. ++*/ ++static SQLITE_NOINLINE const char *indexInAffinityOk( ++ Parse *pParse, ++ WhereTerm *pTerm, ++ u8 idxaff ++){ ++ Expr *pX = pTerm->pExpr; ++ Expr inexpr; ++ ++ assert( pTerm->eOperator & WO_IN ); ++ ++ if( sqlite3ExprIsVector(pX->pLeft) ){ ++ int iField = pTerm->u.x.iField - 1; ++ inexpr.flags = 0; ++ inexpr.op = TK_EQ; ++ inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr; ++ assert( ExprUseXSelect(pX) ); ++ inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr; ++ pX = &inexpr; ++ } ++ ++ if( sqlite3IndexAffinityOk(pX, idxaff) ){ ++ CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX); ++ return pRet ? pRet->zName : sqlite3StrBINARY; ++ } ++ return 0; ++} ++ + /* + ** Advance to the next WhereTerm that matches according to the criteria + ** established when the pScan object was initialized by whereScanInit(). +@@ -145865,19 +164830,20 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ + iColumn = pScan->aiColumn[pScan->iEquiv-1]; + iCur = pScan->aiCur[pScan->iEquiv-1]; + assert( pWC!=0 ); ++ assert( iCur>=0 ); + do{ + for(pTerm=pWC->a+k; knTerm; k++, pTerm++){ ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 || pTerm->leftCursor<0 ); + if( pTerm->leftCursor==iCur +- && pTerm->u.leftColumn==iColumn ++ && pTerm->u.x.leftColumn==iColumn + && (iColumn!=XN_EXPR + || sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, + pScan->pIdxExpr,iCur)==0) +- && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) ++ && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_OuterON)) + ){ + if( (pTerm->eOperator & WO_EQUIV)!=0 + && pScan->nEquivaiCur) +- && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op +- ==TK_COLUMN ++ && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0 + ){ + int j; + for(j=0; jnEquiv; j++){ +@@ -145895,21 +164861,30 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ + if( (pTerm->eOperator & pScan->opMask)!=0 ){ + /* Verify the affinity and collating sequence match */ + if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ +- CollSeq *pColl; ++ const char *zCollName; + Parse *pParse = pWC->pWInfo->pParse; + pX = pTerm->pExpr; +- if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ +- continue; ++ ++ if( (pTerm->eOperator & WO_IN) ){ ++ zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff); ++ if( !zCollName ) continue; ++ }else{ ++ CollSeq *pColl; ++ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ ++ continue; ++ } ++ assert(pX->pLeft); ++ pColl = sqlite3ExprCompareCollSeq(pParse, pX); ++ zCollName = pColl ? pColl->zName : sqlite3StrBINARY; + } +- assert(pX->pLeft); +- pColl = sqlite3ExprCompareCollSeq(pParse, pX); +- if( pColl==0 ) pColl = pParse->db->pDfltColl; +- if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){ ++ ++ if( sqlite3StrICmp(zCollName, pScan->zCollName) ){ + continue; + } + } + if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 +- && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN ++ && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0)) ++ && pX->op==TK_COLUMN + && pX->iTable==pScan->aiCur[0] + && pX->iColumn==pScan->aiColumn[0] + ){ +@@ -145918,6 +164893,18 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ + } + pScan->pWC = pWC; + pScan->k = k+1; ++#ifdef WHERETRACE_ENABLED ++ if( sqlite3WhereTrace & 0x20000 ){ ++ int ii; ++ sqlite3DebugPrintf("SCAN-TERM %p: nEquiv=%d", ++ pTerm, pScan->nEquiv); ++ for(ii=0; iinEquiv; ii++){ ++ sqlite3DebugPrintf(" {%d:%d}", ++ pScan->aiCur[ii], pScan->aiColumn[ii]); ++ } ++ sqlite3DebugPrintf("\n"); ++ } ++#endif + return pTerm; + } + } +@@ -145984,16 +164971,16 @@ static WhereTerm *whereScanInit( + if( pIdx ){ + int j = iColumn; + iColumn = pIdx->aiColumn[j]; +- if( iColumn==XN_EXPR ){ +- pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; +- pScan->zCollName = pIdx->azColl[j]; +- pScan->aiColumn[0] = XN_EXPR; +- return whereScanInitIndexExpr(pScan); +- }else if( iColumn==pIdx->pTable->iPKey ){ ++ if( iColumn==pIdx->pTable->iPKey ){ + iColumn = XN_ROWID; + }else if( iColumn>=0 ){ + pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; + pScan->zCollName = pIdx->azColl[j]; ++ }else if( iColumn==XN_EXPR ){ ++ pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; ++ pScan->zCollName = pIdx->azColl[j]; ++ pScan->aiColumn[0] = XN_EXPR; ++ return whereScanInitIndexExpr(pScan); + } + }else if( iColumn==XN_EXPR ){ + return 0; +@@ -146073,7 +165060,8 @@ static int findIndexCol( + + for(i=0; inExpr; i++){ + Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); +- if( p->op==TK_COLUMN ++ if( ALWAYS(p!=0) ++ && (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) + && p->iColumn==pIdx->aiColumn[iCol] + && p->iTable==iBase + ){ +@@ -146129,7 +165117,7 @@ static int isDistinctRedundant( + ** clause is redundant. */ + if( pTabList->nSrc!=1 ) return 0; + iBase = pTabList->a[0].iCursor; +- pTab = pTabList->a[0].pTab; ++ pTab = pTabList->a[0].pSTab; + + /* If any of the expressions is an IPK column on table iBase, then return + ** true. Note: The (p->iTable==iBase) part of this test may be false if the +@@ -146137,7 +165125,9 @@ static int isDistinctRedundant( + */ + for(i=0; inExpr; i++){ + Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); +- if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; ++ if( NEVER(p==0) ) continue; ++ if( p->op!=TK_COLUMN && p->op!=TK_AGG_COLUMN ) continue; ++ if( p->iTable==iBase && p->iColumn<0 ) return 1; + } + + /* Loop through all indices on the table, checking each to see if it makes +@@ -146155,6 +165145,7 @@ static int isDistinctRedundant( + */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( !IsUniqueIndex(pIdx) ) continue; ++ if( pIdx->pPartIdxWhere ) continue; + for(i=0; inKeyCol; i++){ + if( 0==sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx) ){ + if( findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0 ) break; +@@ -146201,22 +165192,39 @@ static void translateColumnToCopy( + VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart); + int iEnd = sqlite3VdbeCurrentAddr(v); + if( pParse->db->mallocFailed ) return; ++#ifdef SQLITE_DEBUG ++ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ ++ printf("CHECKING for column-to-copy on cursor %d for %d..%d\n", ++ iTabCur, iStart, iEnd); ++ } ++#endif + for(; iStartp1!=iTabCur ) continue; + if( pOp->opcode==OP_Column ){ ++#ifdef SQLITE_DEBUG ++ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ ++ printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart); ++ } ++#endif + pOp->opcode = OP_Copy; + pOp->p1 = pOp->p2 + iRegister; + pOp->p2 = pOp->p3; + pOp->p3 = 0; ++ pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ + }else if( pOp->opcode==OP_Rowid ){ +- if( iAutoidxCur ){ +- pOp->opcode = OP_Sequence; +- pOp->p1 = iAutoidxCur; +- }else{ ++#ifdef SQLITE_DEBUG ++ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ ++ printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart); ++ } ++#endif ++ pOp->opcode = OP_Sequence; ++ pOp->p1 = iAutoidxCur; ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ if( iAutoidxCur==0 ){ + pOp->opcode = OP_Null; +- pOp->p1 = 0; + pOp->p3 = 0; + } ++#endif + } + } + } +@@ -146228,16 +165236,22 @@ static void translateColumnToCopy( + ** are no-ops. + */ + #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) +-static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ ++static void whereTraceIndexInfoInputs( ++ sqlite3_index_info *p, /* The IndexInfo object */ ++ Table *pTab /* The TABLE that is the virtual table */ ++){ + int i; +- if( !sqlite3WhereTrace ) return; ++ if( (sqlite3WhereTrace & 0x10)==0 ) return; ++ sqlite3DebugPrintf("sqlite3_index_info inputs for %s:\n", pTab->zName); + for(i=0; inConstraint; i++){ +- sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n", ++ sqlite3DebugPrintf( ++ " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", + i, + p->aConstraint[i].iColumn, + p->aConstraint[i].iTermOffset, + p->aConstraint[i].op, +- p->aConstraint[i].usable); ++ p->aConstraint[i].usable, ++ sqlite3_vtab_collation(p,i)); + } + for(i=0; inOrderBy; i++){ + sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", +@@ -146246,9 +165260,13 @@ static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ + p->aOrderBy[i].desc); + } + } +-static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ ++static void whereTraceIndexInfoOutputs( ++ sqlite3_index_info *p, /* The IndexInfo object */ ++ Table *pTab /* The TABLE that is the virtual table */ ++){ + int i; +- if( !sqlite3WhereTrace ) return; ++ if( (sqlite3WhereTrace & 0x10)==0 ) return; ++ sqlite3DebugPrintf("sqlite3_index_info outputs for %s:\n", pTab->zName); + for(i=0; inConstraint; i++){ + sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", + i, +@@ -146262,10 +165280,86 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ + sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); + } + #else +-#define whereTraceIndexInfoInputs(A) +-#define whereTraceIndexInfoOutputs(A) ++#define whereTraceIndexInfoInputs(A,B) ++#define whereTraceIndexInfoOutputs(A,B) + #endif + ++/* ++** We know that pSrc is an operand of an outer join. Return true if ++** pTerm is a constraint that is compatible with that join. ++** ++** pTerm must be EP_OuterON if pSrc is the right operand of an ++** outer join. pTerm can be either EP_OuterON or EP_InnerON if pSrc ++** is the left operand of a RIGHT join. ++** ++** See https://sqlite.org/forum/forumpost/206d99a16dd9212f ++** for an example of a WHERE clause constraints that may not be used on ++** the right table of a RIGHT JOIN because the constraint implies a ++** not-NULL condition on the left table of the RIGHT JOIN. ++*/ ++static int constraintCompatibleWithOuterJoin( ++ const WhereTerm *pTerm, /* WHERE clause term to check */ ++ const SrcItem *pSrc /* Table we are trying to access */ ++){ ++ assert( (pSrc->fg.jointype&(JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ); /* By caller */ ++ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); ++ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); ++ testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) ++ testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); ++ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ++ || pTerm->pExpr->w.iJoin != pSrc->iCursor ++ ){ ++ return 0; ++ } ++ if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 ++ && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON)) ++ ){ ++ return 0; ++ } ++ return 1; ++} ++ ++#ifndef SQLITE_OMIT_AUTOMATIC_INDEX ++/* ++** Return true if column iCol of table pTab seem like it might be a ++** good column to use as part of a query-time index. ++** ++** Current algorithm (subject to improvement!): ++** ++** 1. If iCol is already the left-most column of some other index, ++** then return false. ++** ++** 2. If iCol is part of an existing index that has an aiRowLogEst of ++** more than 20, then return false. ++** ++** 3. If no disqualifying conditions above are found, return true. ++** ++** 2025-01-03: I experimented with a new rule that returns false if the ++** the datatype of the column is "BOOLEAN". This did not improve ++** performance on any queries at hand, but it did burn CPU cycles, so the ++** idea was not committed. ++*/ ++static SQLITE_NOINLINE int columnIsGoodIndexCandidate( ++ const Table *pTab, ++ int iCol ++){ ++ const Index *pIdx; ++ for(pIdx = pTab->pIndex; pIdx!=0; pIdx=pIdx->pNext){ ++ int j; ++ for(j=0; jnKeyCol; j++){ ++ if( pIdx->aiColumn[j]==iCol ){ ++ if( j==0 ) return 0; ++ if( pIdx->hasStat1 && pIdx->aiRowLogEst[j+1]>20 ) return 0; ++ break; ++ } ++ } ++ } ++ return 1; ++} ++#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ ++ ++ ++ + #ifndef SQLITE_OMIT_AUTOMATIC_INDEX + /* + ** Return TRUE if the WHERE clause term pTerm is of a form where it +@@ -146273,43 +165367,94 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ + ** index existed. + */ + static int termCanDriveIndex( +- WhereTerm *pTerm, /* WHERE clause term to check */ +- struct SrcList_item *pSrc, /* Table we are trying to access */ +- Bitmask notReady /* Tables in outer loops of the join */ ++ const WhereTerm *pTerm, /* WHERE clause term to check */ ++ const SrcItem *pSrc, /* Table we are trying to access */ ++ const Bitmask notReady /* Tables in outer loops of the join */ + ){ + char aff; ++ int leftCol; ++ + if( pTerm->leftCursor!=pSrc->iCursor ) return 0; + if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; +- if( (pSrc->fg.jointype & JT_LEFT) +- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) +- && (pTerm->eOperator & WO_IS) ++ assert( (pSrc->fg.jointype & JT_RIGHT)==0 ); ++ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ++ && !constraintCompatibleWithOuterJoin(pTerm,pSrc) + ){ +- /* Cannot use an IS term from the WHERE clause as an index driver for +- ** the RHS of a LEFT JOIN. Such a term can only be used if it is from +- ** the ON clause. */ +- return 0; ++ return 0; /* See https://sqlite.org/forum/forumpost/51e6959f61 */ + } + if( (pTerm->prereqRight & notReady)!=0 ) return 0; +- if( pTerm->u.leftColumn<0 ) return 0; +- aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ leftCol = pTerm->u.x.leftColumn; ++ if( leftCol<0 ) return 0; ++ aff = pSrc->pSTab->aCol[leftCol].affinity; + if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; + testcase( pTerm->pExpr->op==TK_IS ); +- return 1; ++ return columnIsGoodIndexCandidate(pSrc->pSTab, leftCol); + } + #endif + + + #ifndef SQLITE_OMIT_AUTOMATIC_INDEX ++ ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++/* ++** Argument pIdx represents an automatic index that the current statement ++** will create and populate. Add an OP_Explain with text of the form: ++** ++** CREATE AUTOMATIC INDEX ON
        () [WHERE ] ++** ++** This is only required if sqlite3_stmt_scanstatus() is enabled, to ++** associate an SQLITE_SCANSTAT_NCYCLE and SQLITE_SCANSTAT_NLOOP ++** values with. In order to avoid breaking legacy code and test cases, ++** the OP_Explain is not added if this is an EXPLAIN QUERY PLAN command. ++*/ ++static void explainAutomaticIndex( ++ Parse *pParse, ++ Index *pIdx, /* Automatic index to explain */ ++ int bPartial, /* True if pIdx is a partial index */ ++ int *pAddrExplain /* OUT: Address of OP_Explain */ ++){ ++ if( IS_STMT_SCANSTATUS(pParse->db) && pParse->explain!=2 ){ ++ Table *pTab = pIdx->pTable; ++ const char *zSep = ""; ++ char *zText = 0; ++ int ii = 0; ++ sqlite3_str *pStr = sqlite3_str_new(pParse->db); ++ sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName); ++ assert( pIdx->nColumn>1 ); ++ assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID || !HasRowid(pTab) ); ++ for(ii=0; ii<(pIdx->nColumn-1); ii++){ ++ const char *zName = 0; ++ int iCol = pIdx->aiColumn[ii]; ++ ++ zName = pTab->aCol[iCol].zCnName; ++ sqlite3_str_appendf(pStr, "%s%s", zSep, zName); ++ zSep = ", "; ++ } ++ zText = sqlite3_str_finish(pStr); ++ if( zText==0 ){ ++ sqlite3OomFault(pParse->db); ++ }else{ ++ *pAddrExplain = sqlite3VdbeExplain( ++ pParse, 0, "%s)%s", zText, (bPartial ? " WHERE " : "") ++ ); ++ sqlite3_free(zText); ++ } ++ } ++} ++#else ++# define explainAutomaticIndex(a,b,c,d) ++#endif ++ + /* + ** Generate code to construct the Index object for an automatic index + ** and to set up the WhereLevel object pLevel so that the code generator + ** makes use of the automatic index. + */ +-static void constructAutomaticIndex( ++static SQLITE_NOINLINE void constructAutomaticIndex( + Parse *pParse, /* The parsing context */ + WhereClause *pWC, /* The WHERE clause */ +- struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ +- Bitmask notReady, /* Mask of cursors that are not available */ ++ const Bitmask notReady, /* Mask of cursors that are not available */ + WhereLevel *pLevel /* Write new index here */ + ){ + int nKeyCol; /* Number of columns in the constructed index */ +@@ -146329,12 +165474,17 @@ static void constructAutomaticIndex( + char *zNotUsed; /* Extra space on the end of pIdx */ + Bitmask idxCols; /* Bitmap of columns used for indexing */ + Bitmask extraCols; /* Bitmap of additional columns */ +- u8 sentWarning = 0; /* True if a warnning has been issued */ ++ u8 sentWarning = 0; /* True if a warning has been issued */ ++ u8 useBloomFilter = 0; /* True to also add a Bloom filter */ + Expr *pPartial = 0; /* Partial Index Expression */ + int iContinue = 0; /* Jump here to skip excluded rows */ +- struct SrcList_item *pTabItem; /* FROM clause term being indexed */ ++ SrcList *pTabList; /* The complete FROM clause */ ++ SrcItem *pSrc; /* The FROM clause term to get the next index */ + int addrCounter = 0; /* Address where integer counter is initialized */ + int regBase; /* Array of registers where record is assembled */ ++#ifdef SQLITE_ENABLE_STMT_SCANSTATUS ++ int addrExp = 0; /* Address of OP_Explain */ ++#endif + + /* Generate code to skip over the creation and initialization of the + ** transient index on 2nd and subsequent iterations of the loop. */ +@@ -146345,31 +165495,35 @@ static void constructAutomaticIndex( + /* Count the number of columns that will be added to the index + ** and used to match WHERE clause constraints */ + nKeyCol = 0; +- pTable = pSrc->pTab; ++ pTabList = pWC->pWInfo->pTabList; ++ pSrc = &pTabList->a[pLevel->iFrom]; ++ pTable = pSrc->pSTab; + pWCEnd = &pWC->a[pWC->nTerm]; + pLoop = pLevel->pWLoop; + idxCols = 0; + for(pTerm=pWC->a; pTermpExpr; +- assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ +- || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ +- || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ +- if( pLoop->prereq==0 +- && (pTerm->wtFlags & TERM_VIRTUAL)==0 +- && !ExprHasProperty(pExpr, EP_FromJoin) +- && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){ ++ /* Make the automatic index a partial index if there are terms in the ++ ** WHERE clause (or the ON clause of a LEFT join) that constrain which ++ ** rows of the target table (pSrc) that can be used. */ ++ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 ++ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0) ++ ){ + pPartial = sqlite3ExprAnd(pParse, pPartial, + sqlite3ExprDup(pParse->db, pExpr, 0)); + } + if( termCanDriveIndex(pTerm, pSrc, notReady) ){ +- int iCol = pTerm->u.leftColumn; +- Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); ++ int iCol; ++ Bitmask cMask; ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ iCol = pTerm->u.x.leftColumn; ++ cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); + testcase( iCol==BMS ); + testcase( iCol==BMS-1 ); + if( !sentWarning ){ + sqlite3_log(SQLITE_WARNING_AUTOINDEX, + "automatic index on %s(%s)", pTable->zName, +- pTable->aCol[iCol].zName); ++ pTable->aCol[iCol].zCnName); + sentWarning = 1; + } + if( (idxCols & cMask)==0 ){ +@@ -146381,7 +165535,7 @@ static void constructAutomaticIndex( + } + } + } +- assert( nKeyCol>0 ); ++ assert( nKeyCol>0 || pParse->db->mallocFailed ); + pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol; + pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED + | WHERE_AUTO_INDEX; +@@ -146394,7 +165548,24 @@ static void constructAutomaticIndex( + ** original table changes and the index and table cannot both be used + ** if they go out of sync. + */ +- extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); ++ if( IsView(pTable) ){ ++ extraCols = ALLBITS & ~idxCols; ++ }else{ ++ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); ++ } ++ if( !HasRowid(pTable) ){ ++ /* For WITHOUT ROWID tables, ensure that all PRIMARY KEY columns are ++ ** either in the idxCols mask or in the extraCols mask */ ++ for(i=0; inCol; i++){ ++ if( (pTable->aCol[i].colFlags & COLFLAG_PRIMKEY)==0 ) continue; ++ if( i>=BMS-1 ){ ++ extraCols |= MASKBIT(BMS-1); ++ break; ++ } ++ if( idxCols & MASKBIT(i) ) continue; ++ extraCols |= MASKBIT(i); ++ } ++ } + mxBitCol = MIN(BMS-1,pTable->nCol); + testcase( pTable->nCol==BMS-1 ); + testcase( pTable->nCol==BMS-2 ); +@@ -146406,7 +165577,10 @@ static void constructAutomaticIndex( + } + + /* Construct the Index object to describe this index */ +- pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed); ++ assert( nKeyCol <= pTable->nCol + MAX(0, pTable->nCol - BMS + 1) ); ++ /* ^-- This guarantees that the number of index columns will fit in the u16 */ ++ pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable), ++ 0, &zNotUsed); + if( pIdx==0 ) goto end_auto_index_create; + pLoop->u.btree.pIndex = pIdx; + pIdx->zName = "auto-index"; +@@ -146415,18 +165589,31 @@ static void constructAutomaticIndex( + idxCols = 0; + for(pTerm=pWC->a; pTermu.leftColumn; +- Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); ++ int iCol; ++ Bitmask cMask; ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ iCol = pTerm->u.x.leftColumn; ++ cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); + testcase( iCol==BMS-1 ); + testcase( iCol==BMS ); + if( (idxCols & cMask)==0 ){ + Expr *pX = pTerm->pExpr; + idxCols |= cMask; +- pIdx->aiColumn[n] = pTerm->u.leftColumn; ++ pIdx->aiColumn[n] = pTerm->u.x.leftColumn; + pColl = sqlite3ExprCompareCollSeq(pParse, pX); + assert( pColl!=0 || pParse->nErr>0 ); /* TH3 collate01.800 */ + pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY; + n++; ++ if( ALWAYS(pX->pLeft!=0) ++ && sqlite3ExprAffinity(pX->pLeft)!=SQLITE_AFF_TEXT ++ ){ ++ /* TUNING: only use a Bloom filter on an automatic index ++ ** if one or more key columns has the ability to hold numeric ++ ** values, since strings all have the same hash in the Bloom ++ ** filter implementation and hence a Bloom filter on a text column ++ ** is not usually helpful. */ ++ useBloomFilter = 1; ++ } + } + } + } +@@ -146449,25 +165636,38 @@ static void constructAutomaticIndex( + } + } + assert( n==nKeyCol ); +- pIdx->aiColumn[n] = XN_ROWID; +- pIdx->azColl[n] = sqlite3StrBINARY; ++ if( HasRowid(pTable) ){ ++ pIdx->aiColumn[n] = XN_ROWID; ++ pIdx->azColl[n] = sqlite3StrBINARY; ++ } + + /* Create the automatic index */ ++ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp); + assert( pLevel->iIdxCur>=0 ); + pLevel->iIdxCur = pParse->nTab++; + sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); + VdbeComment((v, "for %s", pTable->zName)); ++ if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) && useBloomFilter ){ ++ sqlite3WhereExplainBloomFilter(pParse, pWC->pWInfo, pLevel); ++ pLevel->regFilter = ++pParse->nMem; ++ sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); ++ } + + /* Fill the automatic index with content */ +- pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; +- if( pTabItem->fg.viaCoroutine ){ +- int regYield = pTabItem->regReturn; ++ assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] ); ++ if( pSrc->fg.viaCoroutine ){ ++ int regYield; ++ Subquery *pSubq; ++ assert( pSrc->fg.isSubquery ); ++ pSubq = pSrc->u4.pSubq; ++ assert( pSubq!=0 ); ++ regYield = pSubq->regReturn; + addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); +- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); ++ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); + addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); + VdbeCoverage(v); +- VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); ++ VdbeComment((v, "next row of %s", pSrc->pSTab->zName)); + }else{ + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); + } +@@ -146480,17 +165680,23 @@ static void constructAutomaticIndex( + regBase = sqlite3GenerateIndexKey( + pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 + ); ++ if( pLevel->regFilter ){ ++ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, ++ regBase, pLoop->u.btree.nEq); ++ } ++ sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v)); + sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); + sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); +- if( pTabItem->fg.viaCoroutine ){ ++ if( pSrc->fg.viaCoroutine ){ ++ assert( pSrc->fg.isSubquery && pSrc->u4.pSubq!=0 ); + sqlite3VdbeChangeP2(v, addrCounter, regBase+n); + testcase( pParse->db->mallocFailed ); + assert( pLevel->iIdxCur>0 ); + translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, +- pTabItem->regResult, pLevel->iIdxCur); ++ pSrc->u4.pSubq->regResult, pLevel->iIdxCur); + sqlite3VdbeGoto(v, addrTop); +- pTabItem->fg.viaCoroutine = 0; ++ pSrc->fg.viaCoroutine = 0; + }else{ + sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); + sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); +@@ -146500,28 +165706,183 @@ static void constructAutomaticIndex( + + /* Jump here when skipping the initialization */ + sqlite3VdbeJumpHere(v, addrInit); ++ sqlite3VdbeScanStatusRange(v, addrExp, addrExp, -1); + + end_auto_index_create: + sqlite3ExprDelete(pParse->db, pPartial); + } + #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ + ++/* ++** Generate bytecode that will initialize a Bloom filter that is appropriate ++** for pLevel. ++** ++** If there are inner loops within pLevel that have the WHERE_BLOOMFILTER ++** flag set, initialize a Bloomfilter for them as well. Except don't do ++** this recursive initialization if the SQLITE_BloomPulldown optimization has ++** been turned off. ++** ++** When the Bloom filter is initialized, the WHERE_BLOOMFILTER flag is cleared ++** from the loop, but the regFilter value is set to a register that implements ++** the Bloom filter. When regFilter is positive, the ++** sqlite3WhereCodeOneLoopStart() will generate code to test the Bloom filter ++** and skip the subsequence B-Tree seek if the Bloom filter indicates that ++** no matching rows exist. ++** ++** This routine may only be called if it has previously been determined that ++** the loop would benefit from a Bloom filter, and the WHERE_BLOOMFILTER bit ++** is set. ++*/ ++static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( ++ WhereInfo *pWInfo, /* The WHERE clause */ ++ int iLevel, /* Index in pWInfo->a[] that is pLevel */ ++ WhereLevel *pLevel, /* Make a Bloom filter for this FROM term */ ++ Bitmask notReady /* Loops that are not ready */ ++){ ++ int addrOnce; /* Address of opening OP_Once */ ++ int addrTop; /* Address of OP_Rewind */ ++ int addrCont; /* Jump here to skip a row */ ++ const WhereTerm *pTerm; /* For looping over WHERE clause terms */ ++ const WhereTerm *pWCEnd; /* Last WHERE clause term */ ++ Parse *pParse = pWInfo->pParse; /* Parsing context */ ++ Vdbe *v = pParse->pVdbe; /* VDBE under construction */ ++ WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */ ++ int iCur; /* Cursor for table getting the filter */ ++ IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */ ++ IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */ ++ ++ saved_pIdxEpr = pParse->pIdxEpr; ++ saved_pIdxPartExpr = pParse->pIdxPartExpr; ++ pParse->pIdxEpr = 0; ++ pParse->pIdxPartExpr = 0; ++ ++ assert( pLoop!=0 ); ++ assert( v!=0 ); ++ assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); ++ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ); ++ ++ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); ++ do{ ++ const SrcList *pTabList; ++ const SrcItem *pItem; ++ const Table *pTab; ++ u64 sz; ++ int iSrc; ++ sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); ++ addrCont = sqlite3VdbeMakeLabel(pParse); ++ iCur = pLevel->iTabCur; ++ pLevel->regFilter = ++pParse->nMem; ++ ++ /* The Bloom filter is a Blob held in a register. Initialize it ++ ** to zero-filled blob of at least 80K bits, but maybe more if the ++ ** estimated size of the table is larger. We could actually ++ ** measure the size of the table at run-time using OP_Count with ++ ** P3==1 and use that value to initialize the blob. But that makes ++ ** testing complicated. By basing the blob size on the value in the ++ ** sqlite_stat1 table, testing is much easier. ++ */ ++ pTabList = pWInfo->pTabList; ++ iSrc = pLevel->iFrom; ++ pItem = &pTabList->a[iSrc]; ++ assert( pItem!=0 ); ++ pTab = pItem->pSTab; ++ assert( pTab!=0 ); ++ sz = sqlite3LogEstToInt(pTab->nRowLogEst); ++ if( sz<10000 ){ ++ sz = 10000; ++ }else if( sz>10000000 ){ ++ sz = 10000000; ++ } ++ sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter); ++ ++ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); ++ pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; ++ for(pTerm=pWInfo->sWC.a; pTermpExpr; ++ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 ++ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0) ++ ){ ++ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); ++ } ++ } ++ if( pLoop->wsFlags & WHERE_IPK ){ ++ int r1 = sqlite3GetTempReg(pParse); ++ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); ++ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1); ++ sqlite3ReleaseTempReg(pParse, r1); ++ }else{ ++ Index *pIdx = pLoop->u.btree.pIndex; ++ int n = pLoop->u.btree.nEq; ++ int r1 = sqlite3GetTempRange(pParse, n); ++ int jj; ++ for(jj=0; jjpTable==pItem->pSTab ); ++ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj); ++ } ++ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n); ++ sqlite3ReleaseTempRange(pParse, r1, n); ++ } ++ sqlite3VdbeResolveLabel(v, addrCont); ++ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); ++ VdbeCoverage(v); ++ sqlite3VdbeJumpHere(v, addrTop); ++ pLoop->wsFlags &= ~WHERE_BLOOMFILTER; ++ if( OptimizationDisabled(pParse->db, SQLITE_BloomPulldown) ) break; ++ while( ++iLevel < pWInfo->nLevel ){ ++ const SrcItem *pTabItem; ++ pLevel = &pWInfo->a[iLevel]; ++ pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; ++ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue; ++ pLoop = pLevel->pWLoop; ++ if( NEVER(pLoop==0) ) continue; ++ if( pLoop->prereq & notReady ) continue; ++ if( (pLoop->wsFlags & (WHERE_BLOOMFILTER|WHERE_COLUMN_IN)) ++ ==WHERE_BLOOMFILTER ++ ){ ++ /* This is a candidate for bloom-filter pull-down (early evaluation). ++ ** The test that WHERE_COLUMN_IN is omitted is important, as we are ++ ** not able to do early evaluation of bloom filters that make use of ++ ** the IN operator */ ++ break; ++ } ++ } ++ }while( iLevel < pWInfo->nLevel ); ++ sqlite3VdbeJumpHere(v, addrOnce); ++ pParse->pIdxEpr = saved_pIdxEpr; ++ pParse->pIdxPartExpr = saved_pIdxPartExpr; ++} ++ ++ + #ifndef SQLITE_OMIT_VIRTUALTABLE ++/* ++** Return term iTerm of the WhereClause passed as the first argument. Terms ++** are numbered from 0 upwards, starting with the terms in pWC->a[], then ++** those in pWC->pOuter->a[] (if any), and so on. ++*/ ++static WhereTerm *termFromWhereClause(WhereClause *pWC, int iTerm){ ++ WhereClause *p; ++ for(p=pWC; p; p=p->pOuter){ ++ if( iTermnTerm ) return &p->a[iTerm]; ++ iTerm -= p->nTerm; ++ } ++ return 0; ++} ++ + /* + ** Allocate and populate an sqlite3_index_info structure. It is the + ** responsibility of the caller to eventually release the structure +-** by passing the pointer returned by this function to sqlite3_free(). ++** by passing the pointer returned by this function to freeIndexInfo(). + */ + static sqlite3_index_info *allocateIndexInfo( +- Parse *pParse, /* The parsing context */ ++ WhereInfo *pWInfo, /* The WHERE clause */ + WhereClause *pWC, /* The WHERE clause being analyzed */ + Bitmask mUnusable, /* Ignore terms with these prereqs */ +- struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */ +- ExprList *pOrderBy, /* The ORDER BY clause */ ++ SrcItem *pSrc, /* The FROM clause term that is the vtab */ + u16 *pmNoOmit /* Mask of terms not to omit */ + ){ + int i, j; + int nTerm; ++ Parse *pParse = pWInfo->pParse; + struct sqlite3_index_constraint *pIdxCons; + struct sqlite3_index_orderby *pIdxOrderBy; + struct sqlite3_index_constraint_usage *pUsage; +@@ -146530,21 +165891,44 @@ static sqlite3_index_info *allocateIndexInfo( + int nOrderBy; + sqlite3_index_info *pIdxInfo; + u16 mNoOmit = 0; ++ const Table *pTab; ++ int eDistinct = 0; ++ ExprList *pOrderBy = pWInfo->pOrderBy; ++ WhereClause *p; + +- /* Count the number of possible WHERE clause constraints referring +- ** to this virtual table */ +- for(i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++){ +- if( pTerm->leftCursor != pSrc->iCursor ) continue; +- if( pTerm->prereqRight & mUnusable ) continue; +- assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); +- testcase( pTerm->eOperator & WO_IN ); +- testcase( pTerm->eOperator & WO_ISNULL ); +- testcase( pTerm->eOperator & WO_IS ); +- testcase( pTerm->eOperator & WO_ALL ); +- if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; +- if( pTerm->wtFlags & TERM_VNULL ) continue; +- assert( pTerm->u.leftColumn>=(-1) ); +- nTerm++; ++ assert( pSrc!=0 ); ++ pTab = pSrc->pSTab; ++ assert( pTab!=0 ); ++ assert( IsVirtual(pTab) ); ++ ++ /* Find all WHERE clause constraints referring to this virtual table. ++ ** Mark each term with the TERM_OK flag. Set nTerm to the number of ++ ** terms found. ++ */ ++ for(p=pWC, nTerm=0; p; p=p->pOuter){ ++ for(i=0, pTerm=p->a; inTerm; i++, pTerm++){ ++ pTerm->wtFlags &= ~TERM_OK; ++ if( pTerm->leftCursor != pSrc->iCursor ) continue; ++ if( pTerm->prereqRight & mUnusable ) continue; ++ assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); ++ testcase( pTerm->eOperator & WO_IN ); ++ testcase( pTerm->eOperator & WO_ISNULL ); ++ testcase( pTerm->eOperator & WO_IS ); ++ testcase( pTerm->eOperator & WO_ALL ); ++ if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; ++ if( pTerm->wtFlags & TERM_VNULL ) continue; ++ ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); ++ assert( pTerm->u.x.leftColumn>=XN_ROWID ); ++ assert( pTerm->u.x.leftColumnnCol ); ++ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ++ && !constraintCompatibleWithOuterJoin(pTerm,pSrc) ++ ){ ++ continue; ++ } ++ nTerm++; ++ pTerm->wtFlags |= TERM_OK; ++ } + } + + /* If the ORDER BY clause contains only columns in the current +@@ -146556,11 +165940,49 @@ static sqlite3_index_info *allocateIndexInfo( + int n = pOrderBy->nExpr; + for(i=0; ia[i].pExpr; +- if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break; +- if( pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL ) break; ++ Expr *pE2; ++ ++ /* Skip over constant terms in the ORDER BY clause */ ++ if( sqlite3ExprIsConstant(0, pExpr) ){ ++ continue; ++ } ++ ++ /* Virtual tables are unable to deal with NULLS FIRST */ ++ if( pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) break; ++ ++ /* First case - a direct column references without a COLLATE operator */ ++ if( pExpr->op==TK_COLUMN && pExpr->iTable==pSrc->iCursor ){ ++ assert( pExpr->iColumn>=XN_ROWID && pExpr->iColumnnCol ); ++ continue; ++ } ++ ++ /* 2nd case - a column reference with a COLLATE operator. Only match ++ ** of the COLLATE operator matches the collation of the column. */ ++ if( pExpr->op==TK_COLLATE ++ && (pE2 = pExpr->pLeft)->op==TK_COLUMN ++ && pE2->iTable==pSrc->iCursor ++ ){ ++ const char *zColl; /* The collating sequence name */ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ assert( pExpr->u.zToken!=0 ); ++ assert( pE2->iColumn>=XN_ROWID && pE2->iColumnnCol ); ++ pExpr->iColumn = pE2->iColumn; ++ if( pE2->iColumn<0 ) continue; /* Collseq does not matter for rowid */ ++ zColl = sqlite3ColumnColl(&pTab->aCol[pE2->iColumn]); ++ if( zColl==0 ) zColl = sqlite3StrBINARY; ++ if( sqlite3_stricmp(pExpr->u.zToken, zColl)==0 ) continue; ++ } ++ ++ /* No matches cause a break out of the loop */ ++ break; + } +- if( i==n){ ++ if( i==n ){ + nOrderBy = n; ++ if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ ++ eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); ++ }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ ++ eDistinct = 1; ++ } + } + } + +@@ -146568,89 +165990,131 @@ static sqlite3_index_info *allocateIndexInfo( + */ + pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) + + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm +- + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) ); ++ + sizeof(*pIdxOrderBy)*nOrderBy ++ + SZ_HIDDENINDEXINFO(nTerm) ); + if( pIdxInfo==0 ){ + sqlite3ErrorMsg(pParse, "out of memory"); + return 0; + } + pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; +- pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1]; ++ pIdxCons = (struct sqlite3_index_constraint*)&pHidden->aRhs[nTerm]; + pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm]; + pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; +- pIdxInfo->nOrderBy = nOrderBy; + pIdxInfo->aConstraint = pIdxCons; + pIdxInfo->aOrderBy = pIdxOrderBy; + pIdxInfo->aConstraintUsage = pUsage; ++ pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; ++ if( HasRowid(pTab)==0 ){ ++ /* Ensure that all bits associated with PK columns are set. This is to ++ ** ensure they are available for cases like RIGHT joins or OR loops. */ ++ Index *pPk = sqlite3PrimaryKeyIndex((Table*)pTab); ++ assert( pPk!=0 ); ++ for(i=0; inKeyCol; i++){ ++ int iCol = pPk->aiColumn[i]; ++ assert( iCol>=0 ); ++ if( iCol>=BMS-1 ) iCol = BMS-1; ++ pIdxInfo->colUsed |= MASKBIT(iCol); ++ } ++ } + pHidden->pWC = pWC; + pHidden->pParse = pParse; +- for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ +- u16 op; +- if( pTerm->leftCursor != pSrc->iCursor ) continue; +- if( pTerm->prereqRight & mUnusable ) continue; +- assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); +- testcase( pTerm->eOperator & WO_IN ); +- testcase( pTerm->eOperator & WO_IS ); +- testcase( pTerm->eOperator & WO_ISNULL ); +- testcase( pTerm->eOperator & WO_ALL ); +- if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; +- if( pTerm->wtFlags & TERM_VNULL ) continue; +- +- /* tag-20191211-002: WHERE-clause constraints are not useful to the +- ** right-hand table of a LEFT JOIN. See tag-20191211-001 for the +- ** equivalent restriction for ordinary tables. */ +- if( (pSrc->fg.jointype & JT_LEFT)!=0 +- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) +- ){ +- continue; +- } +- assert( pTerm->u.leftColumn>=(-1) ); +- pIdxCons[j].iColumn = pTerm->u.leftColumn; +- pIdxCons[j].iTermOffset = i; +- op = pTerm->eOperator & WO_ALL; +- if( op==WO_IN ) op = WO_EQ; +- if( op==WO_AUX ){ +- pIdxCons[j].op = pTerm->eMatchOp; +- }else if( op & (WO_ISNULL|WO_IS) ){ +- if( op==WO_ISNULL ){ +- pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; +- }else{ +- pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; +- } +- }else{ +- pIdxCons[j].op = (u8)op; +- /* The direct assignment in the previous line is possible only because +- ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The +- ** following asserts verify this fact. */ +- assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); +- assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); +- assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); +- assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); +- assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); +- assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) ); +- +- if( op & (WO_LT|WO_LE|WO_GT|WO_GE) +- && sqlite3ExprIsVector(pTerm->pExpr->pRight) +- ){ +- testcase( j!=i ); +- if( j<16 ) mNoOmit |= (1 << j); +- if( op==WO_LT ) pIdxCons[j].op = WO_LE; +- if( op==WO_GT ) pIdxCons[j].op = WO_GE; ++ pHidden->eDistinct = eDistinct; ++ pHidden->mIn = 0; ++ for(p=pWC, i=j=0; p; p=p->pOuter){ ++ int nLast = i+p->nTerm;; ++ for(pTerm=p->a; iwtFlags & TERM_OK)==0 ) continue; ++ pIdxCons[j].iColumn = pTerm->u.x.leftColumn; ++ pIdxCons[j].iTermOffset = i; ++ op = pTerm->eOperator & WO_ALL; ++ if( op==WO_IN ){ ++ if( (pTerm->wtFlags & TERM_SLICE)==0 ){ ++ pHidden->mIn |= SMASKBIT32(j); ++ } ++ op = WO_EQ; ++ } ++ if( op==WO_AUX ){ ++ pIdxCons[j].op = pTerm->eMatchOp; ++ }else if( op & (WO_ISNULL|WO_IS) ){ ++ if( op==WO_ISNULL ){ ++ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; ++ }else{ ++ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; ++ } ++ }else{ ++ pIdxCons[j].op = (u8)op; ++ /* The direct assignment in the previous line is possible only because ++ ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The ++ ** following asserts verify this fact. */ ++ assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); ++ assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); ++ assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); ++ assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); ++ assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); ++ assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) ); ++ ++ if( op & (WO_LT|WO_LE|WO_GT|WO_GE) ++ && sqlite3ExprIsVector(pTerm->pExpr->pRight) ++ ){ ++ testcase( j!=i ); ++ if( j<16 ) mNoOmit |= (1 << j); ++ if( op==WO_LT ) pIdxCons[j].op = WO_LE; ++ if( op==WO_GT ) pIdxCons[j].op = WO_GE; ++ } + } +- } + +- j++; ++ j++; ++ } + } ++ assert( j==nTerm ); + pIdxInfo->nConstraint = j; +- for(i=0; ia[i].pExpr; +- pIdxOrderBy[i].iColumn = pExpr->iColumn; +- pIdxOrderBy[i].desc = pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC; ++ if( sqlite3ExprIsConstant(0, pExpr) ) continue; ++ assert( pExpr->op==TK_COLUMN ++ || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN ++ && pExpr->iColumn==pExpr->pLeft->iColumn) ); ++ pIdxOrderBy[j].iColumn = pExpr->iColumn; ++ pIdxOrderBy[j].desc = pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC; ++ j++; + } ++ pIdxInfo->nOrderBy = j; + + *pmNoOmit = mNoOmit; + return pIdxInfo; + } + ++/* ++** Free and zero the sqlite3_index_info.idxStr value if needed. ++*/ ++static void freeIdxStr(sqlite3_index_info *pIdxInfo){ ++ if( pIdxInfo->needToFreeIdxStr ){ ++ sqlite3_free(pIdxInfo->idxStr); ++ pIdxInfo->idxStr = 0; ++ pIdxInfo->needToFreeIdxStr = 0; ++ } ++} ++ ++/* ++** Free an sqlite3_index_info structure allocated by allocateIndexInfo() ++** and possibly modified by xBestIndex methods. ++*/ ++static void freeIndexInfo(sqlite3 *db, sqlite3_index_info *pIdxInfo){ ++ HiddenIndexInfo *pHidden; ++ int i; ++ assert( pIdxInfo!=0 ); ++ pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; ++ assert( pHidden->pParse!=0 ); ++ assert( pHidden->pParse->db==db ); ++ for(i=0; inConstraint; i++){ ++ sqlite3ValueFree(pHidden->aRhs[i]); /* IMP: R-14553-25174 */ ++ pHidden->aRhs[i] = 0; ++ } ++ freeIdxStr(pIdxInfo); ++ sqlite3DbFree(db, pIdxInfo); ++} ++ + /* + ** The table object reference passed as the second argument to this function + ** must represent a virtual table. This function invokes the xBestIndex() +@@ -146668,12 +166132,16 @@ static sqlite3_index_info *allocateIndexInfo( + ** that this is required. + */ + static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ +- sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; + int rc; ++ sqlite3_vtab *pVtab; + +- whereTraceIndexInfoInputs(p); ++ assert( IsVirtual(pTab) ); ++ pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; ++ whereTraceIndexInfoInputs(p, pTab); ++ pParse->db->nSchemaLock++; + rc = pVtab->pModule->xBestIndex(pVtab, p); +- whereTraceIndexInfoOutputs(p); ++ pParse->db->nSchemaLock--; ++ whereTraceIndexInfoOutputs(p, pTab); + + if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ + if( rc==SQLITE_NOMEM ){ +@@ -146684,6 +166152,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ + sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); + } + } ++ if( pTab->u.vtab.p->bAllSchemas ){ ++ sqlite3VtabUsesAllSchemas(pParse); ++ } + sqlite3_free(pVtab->zErrMsg); + pVtab->zErrMsg = 0; + return rc; +@@ -146726,7 +166197,8 @@ static int whereKeyStats( + #endif + assert( pRec!=0 ); + assert( pIdx->nSample>0 ); +- assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); ++ assert( pRec->nField>0 ); ++ + + /* Do a binary search to find the first sample greater than or equal + ** to pRec. If pRec contains a single field, the set of samples to search +@@ -146772,7 +166244,12 @@ static int whereKeyStats( + ** it is extended to two fields. The duplicates that this creates do not + ** cause any problems. + */ +- nField = pRec->nField; ++ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ ++ nField = pIdx->nKeyCol; ++ }else{ ++ nField = pIdx->nColumn; ++ } ++ nField = MIN(pRec->nField, nField); + iCol = 0; + iSample = pIdx->nSample * nField; + do{ +@@ -146838,12 +166315,12 @@ static int whereKeyStats( + if( iCol>0 ){ + pRec->nField = iCol; + assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0 +- || pParse->db->mallocFailed ); ++ || pParse->db->mallocFailed || CORRUPT_DB ); + } + if( i>0 ){ + pRec->nField = nField; + assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 +- || pParse->db->mallocFailed ); ++ || pParse->db->mallocFailed || CORRUPT_DB ); + } + } + } +@@ -146860,7 +166337,7 @@ static int whereKeyStats( + ** is larger than all samples in the array. */ + tRowcnt iUpper, iGap; + if( i>=pIdx->nSample ){ +- iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); ++ iUpper = pIdx->nRowEst0; + }else{ + iUpper = aSample[i].anLt[iCol]; + } +@@ -146935,7 +166412,7 @@ SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCo + ** Value pLoop->nOut is currently set to the estimated number of rows + ** visited for scanning (a=? AND b=?). This function reduces that estimate + ** by some factor to account for the (c BETWEEN ? AND ?) expression based +-** on the stat4 data for the index. this scan will be peformed multiple ++** on the stat4 data for the index. this scan will be performed multiple + ** times (once for each (a,b) combination that matches a=?) is dealt with + ** by the caller. + ** +@@ -147016,7 +166493,7 @@ static int whereRangeSkipScanEst( + int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff)); + pLoop->nOut -= nAdjust; + *pbDone = 1; +- WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", ++ WHERETRACE(0x20, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", + nLower, nUpper, nAdjust*-1, pLoop->nOut)); + } + +@@ -147187,14 +166664,15 @@ static int whereRangeScanEst( + ** sample, then assume they are 4x more selective. This brings + ** the estimated selectivity more in line with what it would be + ** if estimated without the use of STAT4 tables. */ +- if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) ); ++ if( iLwrIdx==iUprIdx ){ nNew -= 20; } ++ assert( 20==sqlite3LogEst(4) ); + }else{ + nNew = 10; assert( 10==sqlite3LogEst(2) ); + } + if( nNewwtFlags & TERM_VNULL)==0 ); ++ assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 || pParse->nErr>0 ); + nNew = whereRangeAdjust(pLower, nOut); + nNew = whereRangeAdjust(pUpper, nNew); + +@@ -147227,7 +166705,7 @@ static int whereRangeScanEst( + if( nNewnOut>nOut ){ +- WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n", ++ WHERETRACE(0x20,("Range scan lowers nOut from %d to %d\n", + pLoop->nOut, nOut)); + } + #endif +@@ -147292,7 +166770,7 @@ static int whereEqualScanEst( + pBuilder->nRecValid = nEq; + + whereKeyStats(pParse, p, pRec, 0, a); +- WHERETRACE(0x10,("equality scan regions %s(%d): %d\n", ++ WHERETRACE(0x20,("equality scan regions %s(%d): %d\n", + p->zName, nEq-1, (int)a[1])); + *pnRow = a[1]; + +@@ -147340,9 +166818,9 @@ static int whereInScanEst( + } + + if( rc==SQLITE_OK ){ +- if( nRowEst > nRow0 ) nRowEst = nRow0; ++ if( nRowEst > (tRowcnt)nRow0 ) nRowEst = nRow0; + *pnRow = nRowEst; +- WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst)); ++ WHERETRACE(0x20,("IN row estimate: est=%d\n", nRowEst)); + } + assert( pBuilder->nRecValid==nRecValid ); + return rc; +@@ -147350,7 +166828,7 @@ static int whereInScanEst( + #endif /* SQLITE_ENABLE_STAT4 */ + + +-#ifdef WHERETRACE_ENABLED ++#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG) + /* + ** Print the content of a WhereTerm object + */ +@@ -147363,13 +166841,14 @@ SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ + memcpy(zType, "....", 5); + if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; + if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; +- if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L'; ++ if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) zType[2] = 'L'; + if( pTerm->wtFlags & TERM_CODED ) zType[3] = 'C'; + if( pTerm->eOperator & WO_SINGLE ){ ++ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", +- pTerm->leftCursor, pTerm->u.leftColumn); ++ pTerm->leftCursor, pTerm->u.x.leftColumn); + }else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){ +- sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld", ++ sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%llx", + pTerm->u.pOrInfo->indexable); + }else{ + sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor); +@@ -147383,8 +166862,8 @@ SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ + sqlite3DebugPrintf(" prob=%-3d prereq=%llx,%llx", + pTerm->truthProb, (u64)pTerm->prereqAll, (u64)pTerm->prereqRight); + } +- if( pTerm->iField ){ +- sqlite3DebugPrintf(" iField=%d", pTerm->iField); ++ if( (pTerm->eOperator & (WO_OR|WO_AND))==0 && pTerm->u.x.iField ){ ++ sqlite3DebugPrintf(" iField=%d", pTerm->u.x.iField); + } + if( pTerm->iParent>=0 ){ + sqlite3DebugPrintf(" iParent=%d", pTerm->iParent); +@@ -147393,6 +166872,9 @@ SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ + sqlite3TreeViewExpr(0, pTerm->pExpr, 0); + } + } ++SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){ ++ sqlite3WhereTermPrint(pTerm, 0); ++} + #endif + + #ifdef WHERETRACE_ENABLED +@@ -147410,17 +166892,36 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){ + #ifdef WHERETRACE_ENABLED + /* + ** Print a WhereLoop object for debugging purposes ++** ++** Format example: ++** ++** .--- Position in WHERE clause rSetup, rRun, nOut ---. ++** | | ++** | .--- selfMask nTerm ------. | ++** | | | | ++** | | .-- prereq Idx wsFlags----. | | ++** | | | Name | | | ++** | | | __|__ nEq ---. ___|__ | __|__ ++** | / \ / \ / \ | / \ / \ / \ ++** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 + */ +-SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ +- WhereInfo *pWInfo = pWC->pWInfo; +- int nb = 1+(pWInfo->pTabList->nSrc+3)/4; +- struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab; +- Table *pTab = pItem->pTab; +- Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; +- sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, +- p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); +- sqlite3DebugPrintf(" %12s", +- pItem->zAlias ? pItem->zAlias : pTab->zName); ++SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ ++ WhereInfo *pWInfo; ++ if( pWC ){ ++ pWInfo = pWC->pWInfo; ++ int nb = 1+(pWInfo->pTabList->nSrc+3)/4; ++ SrcItem *pItem = pWInfo->pTabList->a + p->iTab; ++ Table *pTab = pItem->pSTab; ++ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; ++ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, ++ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); ++ sqlite3DebugPrintf(" %12s", ++ pItem->zAlias ? pItem->zAlias : pTab->zName); ++ }else{ ++ pWInfo = 0; ++ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d", ++ p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab); ++ } + if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ + const char *zName; + if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){ +@@ -147445,18 +166946,32 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ + sqlite3_free(z); + } + if( p->wsFlags & WHERE_SKIPSCAN ){ +- sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); ++ sqlite3DebugPrintf(" f %06x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); + }else{ +- sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm); ++ sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm); + } +- sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); +- if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){ ++ if( pWInfo && pWInfo->bStarUsed && p->rStarDelta!=0 ){ ++ sqlite3DebugPrintf(" cost %d,%d,%d delta=%d\n", ++ p->rSetup, p->rRun, p->nOut, p->rStarDelta); ++ }else{ ++ sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); ++ } ++ if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){ + int i; + for(i=0; inLTerm; i++){ + sqlite3WhereTermPrint(p->aLTerm[i], i); + } + } + } ++SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){ ++ if( p ) sqlite3WhereLoopPrint(p, 0); ++} ++SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){ ++ while( p ){ ++ sqlite3ShowWhereLoop(p); ++ p = p->pNextLoop; ++ } ++} + #endif + + /* +@@ -147488,12 +167003,18 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){ + } + + /* +-** Deallocate internal memory used by a WhereLoop object ++** Deallocate internal memory used by a WhereLoop object. Leave the ++** object in an initialized state, as if it had been newly allocated. + */ + static void whereLoopClear(sqlite3 *db, WhereLoop *p){ +- if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFreeNN(db, p->aLTerm); ++ if( p->aLTerm!=p->aLTermSpace ){ ++ sqlite3DbFreeNN(db, p->aLTerm); ++ p->aLTerm = p->aLTermSpace; ++ p->nLSlot = ArraySize(p->aLTermSpace); ++ } + whereLoopClearUnion(db, p); +- whereLoopInit(p); ++ p->nLTerm = 0; ++ p->wsFlags = 0; + } + + /* +@@ -147517,8 +167038,10 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){ + */ + static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ + whereLoopClearUnion(db, pTo); +- if( whereLoopResize(db, pTo, pFrom->nLTerm) ){ +- memset(&pTo->u, 0, sizeof(pTo->u)); ++ if( pFrom->nLTerm > pTo->nLSlot ++ && whereLoopResize(db, pTo, pFrom->nLTerm) ++ ){ ++ memset(pTo, 0, WHERE_LOOP_XFER_SZ); + return SQLITE_NOMEM_BKPT; + } + memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); +@@ -147535,80 +167058,91 @@ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ + ** Delete a WhereLoop object + */ + static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ ++ assert( db!=0 ); + whereLoopClear(db, p); +- sqlite3DbFreeNN(db, p); ++ sqlite3DbNNFreeNN(db, p); + } + + /* + ** Free a WhereInfo structure + */ + static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ +- int i; + assert( pWInfo!=0 ); +- for(i=0; inLevel; i++){ +- WhereLevel *pLevel = &pWInfo->a[i]; +- if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ +- sqlite3DbFree(db, pLevel->u.in.aInLoop); +- } +- } ++ assert( db!=0 ); + sqlite3WhereClauseClear(&pWInfo->sWC); + while( pWInfo->pLoops ){ + WhereLoop *p = pWInfo->pLoops; + pWInfo->pLoops = p->pNextLoop; + whereLoopDelete(db, p); + } +- assert( pWInfo->pExprMods==0 ); +- sqlite3DbFreeNN(db, pWInfo); ++ while( pWInfo->pMemToFree ){ ++ WhereMemBlock *pNext = pWInfo->pMemToFree->pNext; ++ sqlite3DbNNFreeNN(db, pWInfo->pMemToFree); ++ pWInfo->pMemToFree = pNext; ++ } ++ sqlite3DbNNFreeNN(db, pWInfo); + } + + /* +-** Return TRUE if all of the following are true: ++** Return TRUE if X is a proper subset of Y but is of equal or less cost. ++** In other words, return true if all constraints of X are also part of Y ++** and Y has additional constraints that might speed the search that X lacks ++** but the cost of running X is not more than the cost of running Y. ++** ++** In other words, return true if the cost relationship between X and Y ++** is inverted and needs to be adjusted. ++** ++** Case 1: + ** +-** (1) X has the same or lower cost that Y +-** (2) X uses fewer WHERE clause terms than Y +-** (3) Every WHERE clause term used by X is also used by Y +-** (4) X skips at least as many columns as Y +-** (5) If X is a covering index, than Y is too ++** (1a) X and Y use the same index. ++** (1b) X has fewer == terms than Y ++** (1c) Neither X nor Y use skip-scan ++** (1d) X does not have a a greater cost than Y + ** +-** Conditions (2) and (3) mean that X is a "proper subset" of Y. +-** If X is a proper subset of Y then Y is a better choice and ought +-** to have a lower cost. This routine returns TRUE when that cost +-** relationship is inverted and needs to be adjusted. Constraint (4) +-** was added because if X uses skip-scan less than Y it still might +-** deserve a lower cost even if it is a proper subset of Y. Constraint (5) +-** was added because a covering index probably deserves to have a lower cost +-** than a non-covering index even if it is a proper subset. ++** Case 2: ++** ++** (2a) X has the same or lower cost, or returns the same or fewer rows, ++** than Y. ++** (2b) X uses fewer WHERE clause terms than Y ++** (2c) Every WHERE clause term used by X is also used by Y ++** (2d) X skips at least as many columns as Y ++** (2e) If X is a covering index, than Y is too + */ + static int whereLoopCheaperProperSubset( + const WhereLoop *pX, /* First WhereLoop to compare */ + const WhereLoop *pY /* Compare against this WhereLoop */ + ){ + int i, j; +- if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ +- return 0; /* X is not a subset of Y */ ++ if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */ ++ assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 ); ++ assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 ); ++ if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */ ++ && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */ ++ && pX->nSkip==0 && pY->nSkip==0 /* (1c) */ ++ ){ ++ return 1; /* Case 1 is true */ + } +- if( pY->nSkip > pX->nSkip ) return 0; +- if( pX->rRun >= pY->rRun ){ +- if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */ +- if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */ ++ if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ ++ return 0; /* (2b) */ + } ++ if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */ + for(i=pX->nLTerm-1; i>=0; i--){ + if( pX->aLTerm[i]==0 ) continue; + for(j=pY->nLTerm-1; j>=0; j--){ + if( pY->aLTerm[j]==pX->aLTerm[i] ) break; + } +- if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */ ++ if( j<0 ) return 0; /* (2c) */ + } + if( (pX->wsFlags&WHERE_IDX_ONLY)!=0 + && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){ +- return 0; /* Constraint (5) */ ++ return 0; /* (2e) */ + } +- return 1; /* All conditions meet */ ++ return 1; /* Case 2 is true */ + } + + /* +-** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so +-** that: ++** Try to adjust the cost and number of output rows of WhereLoop pTemplate ++** upwards or downwards so that: + ** + ** (1) pTemplate costs less than any other WhereLoops that are a proper + ** subset of pTemplate +@@ -147629,16 +167163,20 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ + /* Adjust pTemplate cost downward so that it is cheaper than its + ** subset p. */ + WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", +- pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1)); +- pTemplate->rRun = p->rRun; +- pTemplate->nOut = p->nOut - 1; ++ pTemplate->rRun, pTemplate->nOut, ++ MIN(p->rRun, pTemplate->rRun), ++ MIN(p->nOut - 1, pTemplate->nOut))); ++ pTemplate->rRun = MIN(p->rRun, pTemplate->rRun); ++ pTemplate->nOut = MIN(p->nOut - 1, pTemplate->nOut); + }else if( whereLoopCheaperProperSubset(pTemplate, p) ){ + /* Adjust pTemplate cost upward so that it is costlier than p since + ** pTemplate is a proper subset of p */ + WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", +- pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1)); +- pTemplate->rRun = p->rRun; +- pTemplate->nOut = p->nOut + 1; ++ pTemplate->rRun, pTemplate->nOut, ++ MAX(p->rRun, pTemplate->rRun), ++ MAX(p->nOut + 1, pTemplate->nOut))); ++ pTemplate->rRun = MAX(p->rRun, pTemplate->rRun); ++ pTemplate->nOut = MAX(p->nOut + 1, pTemplate->nOut); + } + } + } +@@ -147680,7 +167218,7 @@ static WhereLoop **whereLoopFindLesser( + ** rSetup. Call this SETUP-INVARIANT */ + assert( p->rSetup>=pTemplate->rSetup ); + +- /* Any loop using an appliation-defined index (or PRIMARY KEY or ++ /* Any loop using an application-defined index (or PRIMARY KEY or + ** UNIQUE constraint) with one or more == constraints is better + ** than an automatic index. Unless it is a skip-scan. */ + if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 +@@ -147707,7 +167245,7 @@ static WhereLoop **whereLoopFindLesser( + + /* If pTemplate is always better than p, then cause p to be overwritten + ** with pTemplate. pTemplate is better than p if: +- ** (1) pTemplate has no more dependences than p, and ++ ** (1) pTemplate has no more dependencies than p, and + ** (2) pTemplate has an equal or lower cost than p. + */ + if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */ +@@ -147825,7 +167363,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ + }else{ + /* We will be overwriting WhereLoop p[]. But before we do, first + ** go through the rest of the list and delete any other entries besides +- ** p[] that are also supplated by pTemplate */ ++ ** p[] that are also supplanted by pTemplate */ + WhereLoop **ppTail = &p->pNextLoop; + WhereLoop *pToDel; + while( *ppTail ){ +@@ -147893,11 +167431,11 @@ static void whereLoopOutputAdjust( + LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */ + + assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 ); +- for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){ ++ for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){ + assert( pTerm!=0 ); +- if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break; +- if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; + if( (pTerm->prereqAll & notAllowed)!=0 ) continue; ++ if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; ++ if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) continue; + for(j=pLoop->nLTerm-1; j>=0; j--){ + pX = pLoop->aLTerm[j]; + if( pX==0 ) continue; +@@ -147905,6 +167443,24 @@ static void whereLoopOutputAdjust( + if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break; + } + if( j<0 ){ ++ sqlite3ProgressCheck(pWC->pWInfo->pParse); ++ if( pLoop->maskSelf==pTerm->prereqAll ){ ++ /* If there are extra terms in the WHERE clause not used by an index ++ ** that depend only on the table being scanned, and that will tend to ++ ** cause many rows to be omitted, then mark that table as ++ ** "self-culling". ++ ** ++ ** 2022-03-24: Self-culling only applies if either the extra terms ++ ** are straight comparison operators that are non-true with NULL ++ ** operand, or if the loop is not an OUTER JOIN. ++ */ ++ if( (pTerm->eOperator & 0x3f)!=0 ++ || (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype ++ & (JT_LEFT|JT_LTORJ))==0 ++ ){ ++ pLoop->wsFlags |= WHERE_SELFCULL; ++ } ++ } + if( pTerm->truthProb<=0 ){ + /* If a truth probability is specified using the likelihood() hints, + ** then use the probability provided by the application. */ +@@ -147919,7 +167475,7 @@ static void whereLoopOutputAdjust( + Expr *pRight = pTerm->pExpr->pRight; + int k = 0; + testcase( pTerm->pExpr->op==TK_IS ); +- if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){ ++ if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){ + k = 10; + }else{ + k = 20; +@@ -147932,7 +167488,9 @@ static void whereLoopOutputAdjust( + } + } + } +- if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce; ++ if( pLoop->nOut > nRow-iReduce ){ ++ pLoop->nOut = nRow - iReduce; ++ } + } + + /* +@@ -147969,9 +167527,12 @@ static int whereRangeVectorLen( + char aff; /* Comparison affinity */ + char idxaff = 0; /* Indexed columns affinity */ + CollSeq *pColl; /* Comparison collation sequence */ +- Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; +- Expr *pRhs = pTerm->pExpr->pRight; +- if( pRhs->flags & EP_xIsSelect ){ ++ Expr *pLhs, *pRhs; ++ ++ assert( ExprUseXList(pTerm->pExpr->pLeft) ); ++ pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; ++ pRhs = pTerm->pExpr->pRight; ++ if( ExprUseXSelect(pRhs) ){ + pRhs = pRhs->x.pSelect->pEList->a[i].pExpr; + }else{ + pRhs = pRhs->x.pList->a[i].pExpr; +@@ -148002,7 +167563,7 @@ static int whereRangeVectorLen( + } + + /* +-** Adjust the cost C by the costMult facter T. This only occurs if ++** Adjust the cost C by the costMult factor T. This only occurs if + ** compiled with -DSQLITE_ENABLE_COSTMULT + */ + #ifdef SQLITE_ENABLE_COSTMULT +@@ -148025,11 +167586,11 @@ static int whereRangeVectorLen( + */ + static int whereLoopAddBtreeIndex( + WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ +- struct SrcList_item *pSrc, /* FROM clause term being analyzed */ ++ SrcItem *pSrc, /* FROM clause term being analyzed */ + Index *pProbe, /* An index on pSrc */ + LogEst nInMul /* log(Number of iterations due to IN) */ + ){ +- WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */ ++ WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyze context */ + Parse *pParse = pWInfo->pParse; /* Parsing context */ + sqlite3 *db = pParse->db; /* Database connection malloc context */ + WhereLoop *pNew; /* Template WhereLoop under construction */ +@@ -148050,10 +167611,13 @@ static int whereLoopAddBtreeIndex( + WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ + + pNew = pBuilder->pNew; +- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; +- WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n", ++ assert( db->mallocFailed==0 || pParse->nErr>0 ); ++ if( pParse->nErr ){ ++ return pParse->rc; ++ } ++ WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n", + pProbe->pTable->zName,pProbe->zName, +- pNew->u.btree.nEq, pNew->nSkip)); ++ pNew->u.btree.nEq, pNew->nSkip, pNew->rRun)); + + assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); + assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); +@@ -148063,9 +167627,13 @@ static int whereLoopAddBtreeIndex( + assert( pNew->u.btree.nBtm==0 ); + opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; + } +- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); ++ if( pProbe->bUnordered ){ ++ opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); ++ } + + assert( pNew->u.btree.nEqnColumn ); ++ assert( pNew->u.btree.nEqnKeyCol ++ || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY ); + + saved_nEq = pNew->u.btree.nEq; + saved_nBtm = pNew->u.btree.nBtm; +@@ -148099,15 +167667,11 @@ static int whereLoopAddBtreeIndex( + ** to mix with a lower range bound from some other source */ + if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; + +- /* tag-20191211-001: Do not allow constraints from the WHERE clause to +- ** be used by the right table of a LEFT JOIN. Only constraints in the +- ** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */ +- if( (pSrc->fg.jointype & JT_LEFT)!=0 +- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) ++ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ++ && !constraintCompatibleWithOuterJoin(pTerm,pSrc) + ){ + continue; + } +- + if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ + pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE; + }else{ +@@ -148118,7 +167682,11 @@ static int whereLoopAddBtreeIndex( + pNew->u.btree.nBtm = saved_nBtm; + pNew->u.btree.nTop = saved_nTop; + pNew->nLTerm = saved_nLTerm; +- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ ++ if( pNew->nLTerm>=pNew->nLSlot ++ && whereLoopResize(db, pNew, pNew->nLTerm+1) ++ ){ ++ break; /* OOM while trying to enlarge the pNew->aLTerm array */ ++ } + pNew->aLTerm[pNew->nLTerm++] = pTerm; + pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf; + +@@ -148130,9 +167698,10 @@ static int whereLoopAddBtreeIndex( + + if( eOp & WO_IN ){ + Expr *pExpr = pTerm->pExpr; +- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ++ if( ExprUseXSelect(pExpr) ){ + /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ + int i; ++ int bRedundant = 0; + nIn = 46; assert( 46==sqlite3LogEst(25) ); + + /* The expression may actually be of the form (x, y) IN (SELECT...). +@@ -148141,14 +167710,27 @@ static int whereLoopAddBtreeIndex( + ** for each such term. The following loop checks that pTerm is the + ** first such term in use, and sets nIn back to 0 if it is not. */ + for(i=0; inLTerm-1; i++){ +- if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0; ++ if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ){ ++ nIn = 0; ++ if( pNew->aLTerm[i]->u.x.iField == pTerm->u.x.iField ){ ++ /* Detect when two or more columns of an index match the same ++ ** column of a vector IN operater, and avoid adding the column ++ ** to the WhereLoop more than once. See tag-20250707-01 ++ ** in test/rowvalue.test */ ++ bRedundant = 1; ++ } ++ } ++ } ++ if( bRedundant ){ ++ pNew->nLTerm--; ++ continue; + } + }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ + /* "x IN (value, value, ...)" */ + nIn = sqlite3LogEst(pExpr->x.pList->nExpr); + } +- if( pProbe->hasStat1 ){ +- LogEst M, logK, safetyMargin; ++ if( pProbe->hasStat1 && rLogSize>=10 ){ ++ LogEst M, logK, x; + /* Let: + ** N = the total number of rows in the table + ** K = the number of entries on the RHS of the IN operator +@@ -148166,20 +167748,30 @@ static int whereLoopAddBtreeIndex( + ** a safety margin of 2 (LogEst: 10) that favors using the IN operator + ** with the index, as using an index has better worst-case behavior. + ** If we do not have real sqlite_stat1 data, always prefer to use +- ** the index. ++ ** the index. Do not bother with this optimization on very small ++ ** tables (less than 2 rows) as it is pointless in that case. + */ + M = pProbe->aiRowLogEst[saved_nEq]; + logK = estLog(nIn); +- safetyMargin = 10; /* TUNING: extra weight for indexed IN */ +- if( M + logK + safetyMargin < nIn + rLogSize ){ ++ /* TUNING v----- 10 to bias toward indexed IN */ ++ x = M + logK + 10 - (nIn + rLogSize); ++ if( x>=0 ){ + WHERETRACE(0x40, +- ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n", +- saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); +- continue; ++ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d) " ++ "prefers indexed lookup\n", ++ saved_nEq, M, logK, nIn, rLogSize, x)); ++ }else if( nInMul<2 && OptimizationEnabled(db, SQLITE_SeekScan) ){ ++ WHERETRACE(0x40, ++ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" ++ " nInMul=%d) prefers skip-scan\n", ++ saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); ++ pNew->wsFlags |= WHERE_IN_SEEKSCAN; + }else{ + WHERETRACE(0x40, +- ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n", +- saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); ++ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" ++ " nInMul=%d) prefers normal scan\n", ++ saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); ++ continue; + } + } + pNew->wsFlags |= WHERE_COLUMN_IN; +@@ -148191,47 +167783,49 @@ static int whereLoopAddBtreeIndex( + || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) + ){ + if( iCol==XN_ROWID || pProbe->uniqNotNull +- || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ) ++ || (pProbe->nKeyCol==1 && pProbe->onError && (eOp & WO_EQ)) + ){ + pNew->wsFlags |= WHERE_ONEROW; + }else{ + pNew->wsFlags |= WHERE_UNQ_WANTED; + } + } ++ if( scan.iEquiv>1 ) pNew->wsFlags |= WHERE_TRANSCONS; + }else if( eOp & WO_ISNULL ){ + pNew->wsFlags |= WHERE_COLUMN_NULL; +- }else if( eOp & (WO_GT|WO_GE) ){ +- testcase( eOp & WO_GT ); +- testcase( eOp & WO_GE ); +- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; +- pNew->u.btree.nBtm = whereRangeVectorLen( +- pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm +- ); +- pBtm = pTerm; +- pTop = 0; +- if( pTerm->wtFlags & TERM_LIKEOPT ){ +- /* Range contraints that come from the LIKE optimization are +- ** always used in pairs. */ +- pTop = &pTerm[1]; +- assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); +- assert( pTop->wtFlags & TERM_LIKEOPT ); +- assert( pTop->eOperator==WO_LT ); +- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ +- pNew->aLTerm[pNew->nLTerm++] = pTop; +- pNew->wsFlags |= WHERE_TOP_LIMIT; +- pNew->u.btree.nTop = 1; +- } +- }else{ +- assert( eOp & (WO_LT|WO_LE) ); +- testcase( eOp & WO_LT ); +- testcase( eOp & WO_LE ); +- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; +- pNew->u.btree.nTop = whereRangeVectorLen( ++ }else{ ++ int nVecLen = whereRangeVectorLen( + pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm + ); +- pTop = pTerm; +- pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? +- pNew->aLTerm[pNew->nLTerm-2] : 0; ++ if( eOp & (WO_GT|WO_GE) ){ ++ testcase( eOp & WO_GT ); ++ testcase( eOp & WO_GE ); ++ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; ++ pNew->u.btree.nBtm = nVecLen; ++ pBtm = pTerm; ++ pTop = 0; ++ if( pTerm->wtFlags & TERM_LIKEOPT ){ ++ /* Range constraints that come from the LIKE optimization are ++ ** always used in pairs. */ ++ pTop = &pTerm[1]; ++ assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); ++ assert( pTop->wtFlags & TERM_LIKEOPT ); ++ assert( pTop->eOperator==WO_LT ); ++ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ ++ pNew->aLTerm[pNew->nLTerm++] = pTop; ++ pNew->wsFlags |= WHERE_TOP_LIMIT; ++ pNew->u.btree.nTop = 1; ++ } ++ }else{ ++ assert( eOp & (WO_LT|WO_LE) ); ++ testcase( eOp & WO_LT ); ++ testcase( eOp & WO_LE ); ++ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; ++ pNew->u.btree.nTop = nVecLen; ++ pTop = pTerm; ++ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? ++ pNew->aLTerm[pNew->nLTerm-2] : 0; ++ } + } + + /* At this point pNew->nOut is set to the number of rows expected to +@@ -148259,8 +167853,8 @@ static int whereLoopAddBtreeIndex( + tRowcnt nOut = 0; + if( nInMul==0 + && pProbe->nSample +- && pNew->u.btree.nEq<=pProbe->nSampleCol +- && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)) ++ && ALWAYS(pNew->u.btree.nEq<=pProbe->nSampleCol) ++ && ((eOp & WO_IN)==0 || ExprUseXList(pTerm->pExpr)) + && OptimizationEnabled(db, SQLITE_Stat4) + ){ + Expr *pExpr = pTerm->pExpr; +@@ -148283,7 +167877,7 @@ static int whereLoopAddBtreeIndex( + && pNew->nOut+10 > pProbe->aiRowLogEst[0] + ){ + #if WHERETRACE_ENABLED /* 0x01 */ +- if( sqlite3WhereTrace & 0x01 ){ ++ if( sqlite3WhereTrace & 0x20 ){ + sqlite3DebugPrintf( + "STAT4 determines term has low selectivity:\n"); + sqlite3WhereTermPrint(pTerm, 999); +@@ -148315,14 +167909,33 @@ static int whereLoopAddBtreeIndex( + } + } + +- /* Set rCostIdx to the cost of visiting selected rows in index. Add +- ** it to pNew->rRun, which is currently set to the cost of the index +- ** seek only. Then, if this is a non-covering index, add the cost of +- ** visiting the rows in the main table. */ +- assert( pSrc->pTab->szTabRow>0 ); +- rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; +- pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx); +- if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){ ++ /* Set rCostIdx to the estimated cost of visiting selected rows in the ++ ** index. The estimate is the sum of two values: ++ ** 1. The cost of doing one search-by-key to find the first matching ++ ** entry ++ ** 2. Stepping forward in the index pNew->nOut times to find all ++ ** additional matching entries. ++ */ ++ assert( pSrc->pSTab->szTabRow>0 ); ++ if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ ++ /* The pProbe->szIdxRow is low for an IPK table since the interior ++ ** pages are small. Thus szIdxRow gives a good estimate of seek cost. ++ ** But the leaf pages are full-size, so pProbe->szIdxRow would badly ++ ** under-estimate the scanning cost. */ ++ rCostIdx = pNew->nOut + 16; ++ }else{ ++ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pSTab->szTabRow; ++ } ++ rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx); ++ ++ /* Estimate the cost of running the loop. If all data is coming ++ ** from the index, then this is just the cost of doing the index ++ ** lookup and scan. But if some data is coming out of the main table, ++ ** we also have to add in the cost of doing pNew->nOut searches to ++ ** locate the row in the main table that corresponds to the index entry. ++ */ ++ pNew->rRun = rCostIdx; ++ if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){ + pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); + } + ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult); +@@ -148341,7 +167954,12 @@ static int whereLoopAddBtreeIndex( + + if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 + && pNew->u.btree.nEqnColumn ++ && (pNew->u.btree.nEqnKeyCol || ++ pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY) + ){ ++ if( pNew->u.btree.nEq>3 ){ ++ sqlite3ProgressCheck(pParse); ++ } + whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); + } + pNew->nOut = saved_nOut; +@@ -148421,7 +168039,10 @@ static int indexMightHelpWithOrderBy( + if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; + for(ii=0; iinExpr; ii++){ + Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); +- if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ ++ if( NEVER(pExpr==0) ) continue; ++ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) ++ && pExpr->iTable==iCursor ++ ){ + if( pExpr->iColumn<0 ) return 1; + for(jj=0; jjnKeyCol; jj++){ + if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; +@@ -148443,24 +168064,48 @@ static int indexMightHelpWithOrderBy( + */ + static int whereUsablePartialIndex( + int iTab, /* The table for which we want an index */ +- int isLeft, /* True if iTab is the right table of a LEFT JOIN */ ++ u8 jointype, /* The JT_* flags on the join */ + WhereClause *pWC, /* The WHERE clause of the query */ + Expr *pWhere /* The WHERE clause from the partial index */ + ){ + int i; + WhereTerm *pTerm; +- Parse *pParse = pWC->pWInfo->pParse; ++ Parse *pParse; ++ ++ if( jointype & JT_LTORJ ) return 0; ++ pParse = pWC->pWInfo->pParse; + while( pWhere->op==TK_AND ){ +- if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0; ++ if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; + pWhere = pWhere->pRight; + } +- if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; + for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ + Expr *pExpr; + pExpr = pTerm->pExpr; +- if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) +- && (isLeft==0 || ExprHasProperty(pExpr, EP_FromJoin)) ++ if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) ++ && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) + && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) ++ && !sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, -1) ++ && (pTerm->wtFlags & TERM_VNULL)==0 ++ ){ ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++/* ++** pIdx is an index containing expressions. Check it see if any of the ++** expressions in the index match the pExpr expression. ++*/ ++static int exprIsCoveredByIndex( ++ const Expr *pExpr, ++ const Index *pIdx, ++ int iTabCur ++){ ++ int i; ++ for(i=0; inColumn; i++){ ++ if( pIdx->aiColumn[i]==XN_EXPR ++ && sqlite3ExprCompare(0, pExpr, pIdx->aColExpr->a[i].pExpr, iTabCur)==0 + ){ + return 1; + } +@@ -148468,6 +168113,223 @@ static int whereUsablePartialIndex( + return 0; + } + ++/* ++** Structure passed to the whereIsCoveringIndex Walker callback. ++*/ ++typedef struct CoveringIndexCheck CoveringIndexCheck; ++struct CoveringIndexCheck { ++ Index *pIdx; /* The index */ ++ int iTabCur; /* Cursor number for the corresponding table */ ++ u8 bExpr; /* Uses an indexed expression */ ++ u8 bUnidx; /* Uses an unindexed column not within an indexed expr */ ++}; ++ ++/* ++** Information passed in is pWalk->u.pCovIdxCk. Call it pCk. ++** ++** If the Expr node references the table with cursor pCk->iTabCur, then ++** make sure that column is covered by the index pCk->pIdx. We know that ++** all columns less than 63 (really BMS-1) are covered, so we don't need ++** to check them. But we do need to check any column at 63 or greater. ++** ++** If the index does not cover the column, then set pWalk->eCode to ++** non-zero and return WRC_Abort to stop the search. ++** ++** If this node does not disprove that the index can be a covering index, ++** then just return WRC_Continue, to continue the search. ++** ++** If pCk->pIdx contains indexed expressions and one of those expressions ++** matches pExpr, then prune the search. ++*/ ++static int whereIsCoveringIndexWalkCallback(Walker *pWalk, Expr *pExpr){ ++ int i; /* Loop counter */ ++ const Index *pIdx; /* The index of interest */ ++ const i16 *aiColumn; /* Columns contained in the index */ ++ u16 nColumn; /* Number of columns in the index */ ++ CoveringIndexCheck *pCk; /* Info about this search */ ++ ++ pCk = pWalk->u.pCovIdxCk; ++ pIdx = pCk->pIdx; ++ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) ){ ++ /* if( pExpr->iColumn<(BMS-1) && pIdx->bHasExpr==0 ) return WRC_Continue;*/ ++ if( pExpr->iTable!=pCk->iTabCur ) return WRC_Continue; ++ pIdx = pWalk->u.pCovIdxCk->pIdx; ++ aiColumn = pIdx->aiColumn; ++ nColumn = pIdx->nColumn; ++ for(i=0; iiColumn ) return WRC_Continue; ++ } ++ pCk->bUnidx = 1; ++ return WRC_Abort; ++ }else if( pIdx->bHasExpr ++ && exprIsCoveredByIndex(pExpr, pIdx, pWalk->u.pCovIdxCk->iTabCur) ){ ++ pCk->bExpr = 1; ++ return WRC_Prune; ++ } ++ return WRC_Continue; ++} ++ ++ ++/* ++** pIdx is an index that covers all of the low-number columns used by ++** pWInfo->pSelect (columns from 0 through 62) or an index that has ++** expressions terms. Hence, we cannot determine whether or not it is ++** a covering index by using the colUsed bitmasks. We have to do a search ++** to see if the index is covering. This routine does that search. ++** ++** The return value is one of these: ++** ++** 0 The index is definitely not a covering index ++** ++** WHERE_IDX_ONLY The index is definitely a covering index ++** ++** WHERE_EXPRIDX The index is likely a covering index, but it is ++** difficult to determine precisely because of the ++** expressions that are indexed. Score it as a ++** covering index, but still keep the main table open ++** just in case we need it. ++** ++** This routine is an optimization. It is always safe to return zero. ++** But returning one of the other two values when zero should have been ++** returned can lead to incorrect bytecode and assertion faults. ++*/ ++static SQLITE_NOINLINE u32 whereIsCoveringIndex( ++ WhereInfo *pWInfo, /* The WHERE clause context */ ++ Index *pIdx, /* Index that is being tested */ ++ int iTabCur /* Cursor for the table being indexed */ ++){ ++ int i, rc; ++ struct CoveringIndexCheck ck; ++ Walker w; ++ if( pWInfo->pSelect==0 ){ ++ /* We don't have access to the full query, so we cannot check to see ++ ** if pIdx is covering. Assume it is not. */ ++ return 0; ++ } ++ if( pIdx->bHasExpr==0 ){ ++ for(i=0; inColumn; i++){ ++ if( pIdx->aiColumn[i]>=BMS-1 ) break; ++ } ++ if( i>=pIdx->nColumn ){ ++ /* pIdx does not index any columns greater than 62, but we know from ++ ** colMask that columns greater than 62 are used, so this is not a ++ ** covering index */ ++ return 0; ++ } ++ } ++ ck.pIdx = pIdx; ++ ck.iTabCur = iTabCur; ++ ck.bExpr = 0; ++ ck.bUnidx = 0; ++ memset(&w, 0, sizeof(w)); ++ w.xExprCallback = whereIsCoveringIndexWalkCallback; ++ w.xSelectCallback = sqlite3SelectWalkNoop; ++ w.u.pCovIdxCk = &ck; ++ sqlite3WalkSelect(&w, pWInfo->pSelect); ++ if( ck.bUnidx ){ ++ rc = 0; ++ }else if( ck.bExpr ){ ++ rc = WHERE_EXPRIDX; ++ }else{ ++ rc = WHERE_IDX_ONLY; ++ } ++ return rc; ++} ++ ++/* ++** This is an sqlite3ParserAddCleanup() callback that is invoked to ++** free the Parse->pIdxEpr list when the Parse object is destroyed. ++*/ ++static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){ ++ IndexedExpr **pp = (IndexedExpr**)pObject; ++ while( *pp!=0 ){ ++ IndexedExpr *p = *pp; ++ *pp = p->pIENext; ++ sqlite3ExprDelete(db, p->pExpr); ++ sqlite3DbFreeNN(db, p); ++ } ++} ++ ++/* ++** This function is called for a partial index - one with a WHERE clause - in ++** two scenarios. In both cases, it determines whether or not the WHERE ++** clause on the index implies that a column of the table may be safely ++** replaced by a constant expression. For example, in the following ++** SELECT: ++** ++** CREATE INDEX i1 ON t1(b, c) WHERE a=; ++** SELECT a, b, c FROM t1 WHERE a= AND b=?; ++** ++** The "a" in the select-list may be replaced by , iff: ++** ++** (a) is a constant expression, and ++** (b) The (a=) comparison uses the BINARY collation sequence, and ++** (c) Column "a" has an affinity other than NONE or BLOB. ++** ++** If argument pItem is NULL, then pMask must not be NULL. In this case this ++** function is being called as part of determining whether or not pIdx ++** is a covering index. This function clears any bits in (*pMask) ++** corresponding to columns that may be replaced by constants as described ++** above. ++** ++** Otherwise, if pItem is not NULL, then this function is being called ++** as part of coding a loop that uses index pIdx. In this case, add entries ++** to the Parse.pIdxPartExpr list for each column that can be replaced ++** by a constant. ++*/ ++static void wherePartIdxExpr( ++ Parse *pParse, /* Parse context */ ++ Index *pIdx, /* Partial index being processed */ ++ Expr *pPart, /* WHERE clause being processed */ ++ Bitmask *pMask, /* Mask to clear bits in */ ++ int iIdxCur, /* Cursor number for index */ ++ SrcItem *pItem /* The FROM clause entry for the table */ ++){ ++ assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 ); ++ assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) ); ++ ++ if( pPart->op==TK_AND ){ ++ wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem); ++ pPart = pPart->pLeft; ++ } ++ ++ if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){ ++ Expr *pLeft = pPart->pLeft; ++ Expr *pRight = pPart->pRight; ++ u8 aff; ++ ++ if( pLeft->op!=TK_COLUMN ) return; ++ if( !sqlite3ExprIsConstant(0, pRight) ) return; ++ if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; ++ if( pLeft->iColumn<0 ) return; ++ aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; ++ if( aff>=SQLITE_AFF_TEXT ){ ++ if( pItem ){ ++ sqlite3 *db = pParse->db; ++ IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p)); ++ if( p ){ ++ int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0; ++ p->pExpr = sqlite3ExprDup(db, pRight, 0); ++ p->iDataCur = pItem->iCursor; ++ p->iIdxCur = iIdxCur; ++ p->iIdxCol = pLeft->iColumn; ++ p->bMaybeNullRow = bNullRow; ++ p->pIENext = pParse->pIdxPartExpr; ++ p->aff = aff; ++ pParse->pIdxPartExpr = p; ++ if( p->pIENext==0 ){ ++ void *pArg = (void*)&pParse->pIdxPartExpr; ++ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); ++ } ++ } ++ }else if( pLeft->iColumn<(BMS-1) ){ ++ *pMask &= ~((Bitmask)1 << pLeft->iColumn); ++ } ++ } ++ } ++} ++ ++ + /* + ** Add all WhereLoop objects for a single table of the join where the table + ** is identified by pBuilder->pNew->iTab. That table is guaranteed to be +@@ -148506,7 +168368,7 @@ static int whereUsablePartialIndex( + */ + static int whereLoopAddBtree( + WhereLoopBuilder *pBuilder, /* WHERE clause information */ +- Bitmask mPrereq /* Extra prerequesites for using this table */ ++ Bitmask mPrereq /* Extra prerequisites for using this table */ + ){ + WhereInfo *pWInfo; /* WHERE analysis context */ + Index *pProbe; /* An index we are evaluating */ +@@ -148514,13 +168376,12 @@ static int whereLoopAddBtree( + LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */ + i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */ + SrcList *pTabList; /* The FROM clause */ +- struct SrcList_item *pSrc; /* The FROM clause btree term to add */ ++ SrcItem *pSrc; /* The FROM clause btree term to add */ + WhereLoop *pNew; /* Template WhereLoop object */ + int rc = SQLITE_OK; /* Return code */ + int iSortIdx = 1; /* Index number */ + int b; /* A boolean value */ + LogEst rSize; /* number of rows in the table */ +- LogEst rLogSize; /* Logarithm of the number of rows in the table */ + WhereClause *pWC; /* The parsed WHERE clause */ + Table *pTab; /* Table being queried */ + +@@ -148528,13 +168389,14 @@ static int whereLoopAddBtree( + pWInfo = pBuilder->pWInfo; + pTabList = pWInfo->pTabList; + pSrc = pTabList->a + pNew->iTab; +- pTab = pSrc->pTab; ++ pTab = pSrc->pSTab; + pWC = pBuilder->pWC; +- assert( !IsVirtual(pSrc->pTab) ); ++ assert( !IsVirtual(pSrc->pSTab) ); + +- if( pSrc->pIBIndex ){ ++ if( pSrc->fg.isIndexedBy ){ ++ assert( pSrc->fg.isCte==0 ); + /* An INDEXED BY clause specifies a particular index to use */ +- pProbe = pSrc->pIBIndex; ++ pProbe = pSrc->u2.pIBIndex; + }else if( !HasRowid(pTab) ){ + pProbe = pTab->pIndex; + }else{ +@@ -148550,11 +168412,11 @@ static int whereLoopAddBtree( + sPk.aiRowLogEst = aiRowEstPk; + sPk.onError = OE_Replace; + sPk.pTable = pTab; +- sPk.szIdxRow = pTab->szTabRow; ++ sPk.szIdxRow = 3; /* TUNING: Interior rows of IPK table are very small */ + sPk.idxType = SQLITE_IDXTYPE_IPK; + aiRowEstPk[0] = pTab->nRowLogEst; + aiRowEstPk[1] = 0; +- pFirst = pSrc->pTab->pIndex; ++ pFirst = pSrc->pSTab->pIndex; + if( pSrc->fg.notIndexed==0 ){ + /* The real indices of the table are only considered if the + ** NOT INDEXED qualifier is omitted from the FROM clause */ +@@ -148563,22 +168425,23 @@ static int whereLoopAddBtree( + pProbe = &sPk; + } + rSize = pTab->nRowLogEst; +- rLogSize = estLog(rSize); + + #ifndef SQLITE_OMIT_AUTOMATIC_INDEX + /* Automatic indexes */ + if( !pBuilder->pOrSet /* Not part of an OR optimization */ +- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ++ && (pWInfo->wctrlFlags & (WHERE_RIGHT_JOIN|WHERE_OR_SUBCLAUSE))==0 + && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 +- && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ ++ && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */ + && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ +- && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ + && !pSrc->fg.isCorrelated /* Not a correlated subquery */ + && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ ++ && (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */ + ){ + /* Generate auto-index WhereLoops */ ++ LogEst rLogSize; /* Logarithm of the number of rows in the table */ + WhereTerm *pTerm; + WhereTerm *pWCEnd = pWC->a + pWC->nTerm; ++ rLogSize = estLog(rSize); + for(pTerm=pWC->a; rc==SQLITE_OK && pTermprereqRight & pNew->maskSelf ) continue; + if( termCanDriveIndex(pTerm, pSrc, 0) ){ +@@ -148596,10 +168459,11 @@ static int whereLoopAddBtree( + ** those objects, since there is no opportunity to add schema + ** indexes on subqueries and views. */ + pNew->rSetup = rLogSize + rSize; +- if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){ ++ if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){ + pNew->rSetup += 28; + }else{ +- pNew->rSetup -= 10; ++ pNew->rSetup -= 25; /* Greatly reduced setup cost for auto indexes ++ ** on ephemeral materializations of views */ + } + ApplyCostMultiplier(pNew->rSetup, pTab->costMult); + if( pNew->rSetup<0 ) pNew->rSetup = 0; +@@ -148620,11 +168484,10 @@ static int whereLoopAddBtree( + /* Loop over all indices. If there was an INDEXED BY clause, then only + ** consider index pProbe. */ + for(; rc==SQLITE_OK && pProbe; +- pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++ ++ pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++ + ){ +- int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0; + if( pProbe->pPartIdxWhere!=0 +- && !whereUsablePartialIndex(pSrc->iCursor, isLeft, pWC, ++ && !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC, + pProbe->pPartIdxWhere) + ){ + testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */ +@@ -148635,6 +168498,7 @@ static int whereLoopAddBtree( + pNew->u.btree.nEq = 0; + pNew->u.btree.nBtm = 0; + pNew->u.btree.nTop = 0; ++ pNew->u.btree.nDistinctCol = 0; + pNew->nSkip = 0; + pNew->nLTerm = 0; + pNew->iSortIdx = 0; +@@ -148642,6 +168506,7 @@ static int whereLoopAddBtree( + pNew->prereq = mPrereq; + pNew->nOut = rSize; + pNew->u.btree.pIndex = pProbe; ++ pNew->u.btree.pOrderBy = 0; + b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); + + /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ +@@ -148652,21 +168517,74 @@ static int whereLoopAddBtree( + + /* Full table scan */ + pNew->iSortIdx = b ? iSortIdx : 0; +- /* TUNING: Cost of full table scan is (N*3.0). */ ++ /* TUNING: Cost of full table scan is 3.0*N. The 3.0 factor is an ++ ** extra cost designed to discourage the use of full table scans, ++ ** since index lookups have better worst-case performance if our ++ ** stat guesses are wrong. Reduce the 3.0 penalty slightly ++ ** (to 2.75) if we have valid STAT4 information for the table. ++ ** At 2.75, a full table scan is preferred over using an index on ++ ** a column with just two distinct values where each value has about ++ ** an equal number of appearances. Without STAT4 data, we still want ++ ** to use an index in that case, since the constraint might be for ++ ** the scarcer of the two values, and in that case an index lookup is ++ ** better. ++ */ ++#ifdef SQLITE_ENABLE_STAT4 ++ pNew->rRun = rSize + 16 - 2*((pTab->tabFlags & TF_HasStat4)!=0); ++#else + pNew->rRun = rSize + 16; ++#endif + ApplyCostMultiplier(pNew->rRun, pTab->costMult); + whereLoopOutputAdjust(pWC, pNew, rSize); ++ if( pSrc->fg.isSubquery ){ ++ if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE; ++ pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; ++ } + rc = whereLoopInsert(pBuilder, pNew); + pNew->nOut = rSize; + if( rc ) break; + }else{ + Bitmask m; + if( pProbe->isCovering ){ +- pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; + m = 0; ++ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; + }else{ + m = pSrc->colUsed & pProbe->colNotIdxed; +- pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; ++ if( pProbe->pPartIdxWhere ){ ++ wherePartIdxExpr( ++ pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0 ++ ); ++ } ++ pNew->wsFlags = WHERE_INDEXED; ++ if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){ ++ u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor); ++ if( isCov==0 ){ ++ WHERETRACE(0x200, ++ ("-> %s is not a covering index" ++ " according to whereIsCoveringIndex()\n", pProbe->zName)); ++ assert( m!=0 ); ++ }else{ ++ m = 0; ++ pNew->wsFlags |= isCov; ++ if( isCov & WHERE_IDX_ONLY ){ ++ WHERETRACE(0x200, ++ ("-> %s is a covering expression index" ++ " according to whereIsCoveringIndex()\n", pProbe->zName)); ++ }else{ ++ assert( isCov==WHERE_EXPRIDX ); ++ WHERETRACE(0x200, ++ ("-> %s might be a covering expression index" ++ " according to whereIsCoveringIndex()\n", pProbe->zName)); ++ } ++ } ++ }else if( m==0 ++ && (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700)) ++ ){ ++ WHERETRACE(0x200, ++ ("-> %s is a covering index according to bitmasks\n", ++ pProbe->zName, m==0 ? "is" : "is not")); ++ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; ++ } + } + + /* Full scan via index */ +@@ -148717,7 +168635,14 @@ static int whereLoopAddBtree( + } + ApplyCostMultiplier(pNew->rRun, pTab->costMult); + whereLoopOutputAdjust(pWC, pNew, rSize); +- rc = whereLoopInsert(pBuilder, pNew); ++ if( (pSrc->fg.jointype & JT_RIGHT)!=0 && pProbe->aColExpr ){ ++ /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN ++ ** because the cursor used to access the index might not be ++ ** positioned to the correct row during the right-join no-match ++ ** loop. */ ++ }else{ ++ rc = whereLoopInsert(pBuilder, pNew); ++ } + pNew->nOut = rSize; + if( rc ) break; + } +@@ -148730,7 +168655,7 @@ static int whereLoopAddBtree( + ** unique index is used (making the index functionally non-unique) + ** then the sqlite_stat1 data becomes important for scoring the + ** plan */ +- pTab->tabFlags |= TF_StatsUsed; ++ pTab->tabFlags |= TF_MaybeReanalyze; + } + #ifdef SQLITE_ENABLE_STAT4 + sqlite3Stat4ProbeFree(pBuilder->pRec); +@@ -148743,6 +168668,30 @@ static int whereLoopAddBtree( + + #ifndef SQLITE_OMIT_VIRTUALTABLE + ++/* ++** Return true if pTerm is a virtual table LIMIT or OFFSET term. ++*/ ++static int isLimitTerm(WhereTerm *pTerm){ ++ assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); ++ return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT ++ && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; ++} ++ ++/* ++** Return true if the first nCons constraints in the pUsage array are ++** marked as in-use (have argvIndex>0). False otherwise. ++*/ ++static int allConstraintsUsed( ++ struct sqlite3_index_constraint_usage *aUsage, ++ int nCons ++){ ++ int ii; ++ for(ii=0; iipNew->iTab. This +@@ -148770,9 +168719,11 @@ static int whereLoopAddVirtualOne( + u16 mExclude, /* Exclude terms using these operators */ + sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ + u16 mNoOmit, /* Do not omit these constraints */ +- int *pbIn /* OUT: True if plan uses an IN(...) op */ ++ int *pbIn, /* OUT: True if plan uses an IN(...) op */ ++ int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */ + ){ + WhereClause *pWC = pBuilder->pWC; ++ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + struct sqlite3_index_constraint *pIdxCons; + struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; + int i; +@@ -148780,7 +168731,7 @@ static int whereLoopAddVirtualOne( + int rc = SQLITE_OK; + WhereLoop *pNew = pBuilder->pNew; + Parse *pParse = pBuilder->pWInfo->pParse; +- struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; ++ SrcItem *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; + int nConstraint = pIdxInfo->nConstraint; + + assert( (mUsable & mPrereq)==mPrereq ); +@@ -148791,10 +168742,11 @@ static int whereLoopAddVirtualOne( + ** arguments mUsable and mExclude. */ + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; ia[pIdxCons->iTermOffset]; ++ WhereTerm *pTerm = termFromWhereClause(pWC, pIdxCons->iTermOffset); + pIdxCons->usable = 0; + if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight + && (pTerm->eOperator & mExclude)==0 ++ && (pbRetryLimit || !isLimitTerm(pTerm)) + ){ + pIdxCons->usable = 1; + } +@@ -148809,17 +168761,18 @@ static int whereLoopAddVirtualOne( + pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; + pIdxInfo->estimatedRows = 25; + pIdxInfo->idxFlags = 0; +- pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; ++ pHidden->mHandleIn = 0; + + /* Invoke the virtual table xBestIndex() method */ +- rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); ++ rc = vtabBestIndex(pParse, pSrc->pSTab, pIdxInfo); + if( rc ){ + if( rc==SQLITE_CONSTRAINT ){ + /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means + ** that the particular combination of parameters provided is unusable. + ** Make no entries in the loop table. + */ +- WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n")); ++ WHERETRACE(0xffffffff, (" ^^^^--- non-viable plan rejected!\n")); ++ freeIdxStr(pIdxInfo); + return SQLITE_OK; + } + return rc; +@@ -148827,8 +168780,8 @@ static int whereLoopAddVirtualOne( + + mxTerm = -1; + assert( pNew->nLSlot>=nConstraint ); +- for(i=0; iaLTerm[i] = 0; +- pNew->u.vtab.omitMask = 0; ++ memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint ); ++ memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab)); + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; iiTermOffset; + if( iTerm>=nConstraint + || j<0 +- || j>=pWC->nTerm ++ || (pTerm = termFromWhereClause(pWC, j))==0 + || pNew->aLTerm[iTerm]!=0 + || pIdxCons->usable==0 + ){ +- sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); +- testcase( pIdxInfo->needToFreeIdxStr ); ++ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); ++ freeIdxStr(pIdxInfo); + return SQLITE_ERROR; + } + testcase( iTerm==nConstraint-1 ); + testcase( j==0 ); + testcase( j==pWC->nTerm-1 ); +- pTerm = &pWC->a[j]; + pNew->prereq |= pTerm->prereqRight; + assert( iTermnLSlot ); + pNew->aLTerm[iTerm] = pTerm; +@@ -148862,8 +168814,13 @@ static int whereLoopAddVirtualOne( + }else{ + testcase( i!=iTerm ); + } ++ if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){ ++ pNew->u.vtab.bOmitOffset = 1; ++ } + } +- if( (pTerm->eOperator & WO_IN)!=0 ){ ++ if( SMASKBIT32(i) & pHidden->mHandleIn ){ ++ pNew->u.vtab.mHandleIn |= MASKBIT32(iTerm); ++ }else if( (pTerm->eOperator & WO_IN)!=0 ){ + /* A virtual table that is constrained by an IN clause may not + ** consume the ORDER BY clause because (1) the order of IN terms + ** is not necessarily related to the order of output terms and +@@ -148873,6 +168830,25 @@ static int whereLoopAddVirtualOne( + pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; + *pbIn = 1; assert( (mExclude & WO_IN)==0 ); + } ++ ++ /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET ++ ** terms. And if there are any, they should follow all other terms. */ ++ assert( pbRetryLimit || !isLimitTerm(pTerm) ); ++ assert( !isLimitTerm(pTerm) || i>=nConstraint-2 ); ++ assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) ); ++ ++ if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){ ++ /* If there is an IN(...) term handled as an == (separate call to ++ ** xFilter for each value on the RHS of the IN) and a LIMIT or ++ ** OFFSET term handled as well, the plan is unusable. Similarly, ++ ** if there is a LIMIT/OFFSET and there are other unused terms, ++ ** the plan cannot be used. In these cases set variable *pbRetryLimit ++ ** to true to tell the caller to retry with LIMIT and OFFSET ++ ** disabled. */ ++ freeIdxStr(pIdxInfo); ++ *pbRetryLimit = 1; ++ return SQLITE_OK; ++ } + } + } + +@@ -148881,8 +168857,8 @@ static int whereLoopAddVirtualOne( + if( pNew->aLTerm[i]==0 ){ + /* The non-zero argvIdx values must be contiguous. Raise an + ** error if they are not */ +- sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); +- testcase( pIdxInfo->needToFreeIdxStr ); ++ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); ++ freeIdxStr(pIdxInfo); + return SQLITE_ERROR; + } + } +@@ -148893,6 +168869,7 @@ static int whereLoopAddVirtualOne( + pNew->u.vtab.idxStr = pIdxInfo->idxStr; + pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? + pIdxInfo->nOrderBy : 0); ++ pNew->u.vtab.bIdxNumHex = (pIdxInfo->idxFlags&SQLITE_INDEX_SCAN_HEX)!=0; + pNew->rSetup = 0; + pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); + pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); +@@ -148909,7 +168886,7 @@ static int whereLoopAddVirtualOne( + sqlite3_free(pNew->u.vtab.idxStr); + pNew->u.vtab.needFree = 0; + } +- WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", ++ WHERETRACE(0xffffffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", + *pbIn, (sqlite3_uint64)mPrereq, + (sqlite3_uint64)(pNew->prereq & ~mPrereq))); + +@@ -148917,11 +168894,19 @@ static int whereLoopAddVirtualOne( + } + + /* +-** If this function is invoked from within an xBestIndex() callback, it +-** returns a pointer to a buffer containing the name of the collation +-** sequence associated with element iCons of the sqlite3_index_info.aConstraint +-** array. Or, if iCons is out of range or there is no active xBestIndex +-** call, return NULL. ++** Return the collating sequence for a constraint passed into xBestIndex. ++** ++** pIdxInfo must be an sqlite3_index_info structure passed into xBestIndex. ++** This routine depends on there being a HiddenIndexInfo structure immediately ++** following the sqlite3_index_info structure. ++** ++** Return a pointer to the collation name: ++** ++** 1. If there is an explicit COLLATE operator on the constraint, return it. ++** ++** 2. Else, if the column has an alternative collation, return that. ++** ++** 3. Otherwise, return "BINARY". + */ + SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; +@@ -148929,7 +168914,7 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int + if( iCons>=0 && iConsnConstraint ){ + CollSeq *pC = 0; + int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset; +- Expr *pX = pHidden->pWC->a[iTerm].pExpr; ++ Expr *pX = termFromWhereClause(pHidden->pWC, iTerm)->pExpr; + if( pX->pLeft ){ + pC = sqlite3ExprCompareCollSeq(pHidden->pParse, pX); + } +@@ -148938,6 +168923,94 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int + return zRet; + } + ++/* ++** Return true if constraint iCons is really an IN(...) constraint, or ++** false otherwise. If iCons is an IN(...) constraint, set (if bHandle!=0) ++** or clear (if bHandle==0) the flag to handle it using an iterator. ++*/ ++SQLITE_API int sqlite3_vtab_in(sqlite3_index_info *pIdxInfo, int iCons, int bHandle){ ++ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; ++ u32 m = SMASKBIT32(iCons); ++ if( m & pHidden->mIn ){ ++ if( bHandle==0 ){ ++ pHidden->mHandleIn &= ~m; ++ }else if( bHandle>0 ){ ++ pHidden->mHandleIn |= m; ++ } ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++** This interface is callable from within the xBestIndex callback only. ++** ++** If possible, set (*ppVal) to point to an object containing the value ++** on the right-hand-side of constraint iCons. ++*/ ++SQLITE_API int sqlite3_vtab_rhs_value( ++ sqlite3_index_info *pIdxInfo, /* Copy of first argument to xBestIndex */ ++ int iCons, /* Constraint for which RHS is wanted */ ++ sqlite3_value **ppVal /* Write value extracted here */ ++){ ++ HiddenIndexInfo *pH = (HiddenIndexInfo*)&pIdxInfo[1]; ++ sqlite3_value *pVal = 0; ++ int rc = SQLITE_OK; ++ if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ ++ rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */ ++ }else{ ++ if( pH->aRhs[iCons]==0 ){ ++ WhereTerm *pTerm = termFromWhereClause( ++ pH->pWC, pIdxInfo->aConstraint[iCons].iTermOffset ++ ); ++ rc = sqlite3ValueFromExpr( ++ pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db), ++ SQLITE_AFF_BLOB, &pH->aRhs[iCons] ++ ); ++ testcase( rc!=SQLITE_OK ); ++ } ++ pVal = pH->aRhs[iCons]; ++ } ++ *ppVal = pVal; ++ ++ if( rc==SQLITE_OK && pVal==0 ){ /* IMP: R-19933-32160 */ ++ rc = SQLITE_NOTFOUND; /* IMP: R-36424-56542 */ ++ } ++ ++ return rc; ++} ++ ++/* ++** Return true if ORDER BY clause may be handled as DISTINCT. ++*/ ++SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){ ++ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; ++ assert( pHidden->eDistinct>=0 && pHidden->eDistinct<=3 ); ++ return pHidden->eDistinct; ++} ++ ++/* ++** Cause the prepared statement that is associated with a call to ++** xBestIndex to potentially use all schemas. If the statement being ++** prepared is read-only, then just start read transactions on all ++** schemas. But if this is a write operation, start writes on all ++** schemas. ++** ++** This is used by the (built-in) sqlite_dbpage virtual table. ++*/ ++SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse *pParse){ ++ int nDb = pParse->db->nDb; ++ int i; ++ for(i=0; iwriteMask) ){ ++ for(i=0; ipNew->iTab. That table is guaranteed to be a virtual table. +@@ -148972,13 +169045,14 @@ static int whereLoopAddVirtual( + WhereInfo *pWInfo; /* WHERE analysis context */ + Parse *pParse; /* The parsing context */ + WhereClause *pWC; /* The WHERE clause */ +- struct SrcList_item *pSrc; /* The FROM clause term to search */ ++ SrcItem *pSrc; /* The FROM clause term to search */ + sqlite3_index_info *p; /* Object to pass to xBestIndex() */ + int nConstraint; /* Number of constraints in p */ + int bIn; /* True if plan uses IN(...) operator */ + WhereLoop *pNew; + Bitmask mBest; /* Tables used by best possible plan */ + u16 mNoOmit; ++ int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */ + + assert( (mPrereq & mUnusable)==0 ); + pWInfo = pBuilder->pWInfo; +@@ -148986,9 +169060,8 @@ static int whereLoopAddVirtual( + pWC = pBuilder->pWC; + pNew = pBuilder->pNew; + pSrc = &pWInfo->pTabList->a[pNew->iTab]; +- assert( IsVirtual(pSrc->pTab) ); +- p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, +- &mNoOmit); ++ assert( IsVirtual(pSrc->pSTab) ); ++ p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); + if( p==0 ) return SQLITE_NOMEM_BKPT; + pNew->rSetup = 0; + pNew->wsFlags = WHERE_VIRTUALTABLE; +@@ -148996,14 +169069,22 @@ static int whereLoopAddVirtual( + pNew->u.vtab.needFree = 0; + nConstraint = p->nConstraint; + if( whereLoopResize(pParse->db, pNew, nConstraint) ){ +- sqlite3DbFree(pParse->db, p); ++ freeIndexInfo(pParse->db, p); + return SQLITE_NOMEM_BKPT; + } + + /* First call xBestIndex() with all constraints usable. */ +- WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); +- WHERETRACE(0x40, (" VirtualOne: all usable\n")); +- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); ++ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pSTab->zName)); ++ WHERETRACE(0x800, (" VirtualOne: all usable\n")); ++ rc = whereLoopAddVirtualOne( ++ pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry ++ ); ++ if( bRetry ){ ++ assert( rc==SQLITE_OK ); ++ rc = whereLoopAddVirtualOne( ++ pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0 ++ ); ++ } + + /* If the call to xBestIndex() with all terms enabled produced a plan + ** that does not require any source tables (IOW: a plan with mBest==0) +@@ -149019,9 +169100,9 @@ static int whereLoopAddVirtual( + /* If the plan produced by the earlier call uses an IN(...) term, call + ** xBestIndex again, this time with IN(...) terms disabled. */ + if( bIn ){ +- WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); ++ WHERETRACE(0x800, (" VirtualOne: all usable w/o IN\n")); + rc = whereLoopAddVirtualOne( +- pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); ++ pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0); + assert( bIn==0 ); + mBestNoIn = pNew->prereq & ~mPrereq; + if( mBestNoIn==0 ){ +@@ -149037,18 +169118,17 @@ static int whereLoopAddVirtual( + Bitmask mNext = ALLBITS; + assert( mNext>0 ); + for(i=0; ia[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq +- ); ++ int iTerm = p->aConstraint[i].iTermOffset; ++ Bitmask mThis = termFromWhereClause(pWC, iTerm)->prereqRight & ~mPrereq; + if( mThis>mPrev && mThisprereq==mPrereq ){ + seenZero = 1; + if( bIn==0 ) seenZeroNoIN = 1; +@@ -149059,9 +169139,9 @@ static int whereLoopAddVirtual( + ** that requires no source tables at all (i.e. one guaranteed to be + ** usable), make a call here with all source tables disabled */ + if( rc==SQLITE_OK && seenZero==0 ){ +- WHERETRACE(0x40, (" VirtualOne: all disabled\n")); ++ WHERETRACE(0x800, (" VirtualOne: all disabled\n")); + rc = whereLoopAddVirtualOne( +- pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); ++ pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0); + if( bIn==0 ) seenZeroNoIN = 1; + } + +@@ -149069,15 +169149,14 @@ static int whereLoopAddVirtual( + ** that requires no source tables at all and does not use an IN(...) + ** operator, make a final call to obtain one here. */ + if( rc==SQLITE_OK && seenZeroNoIN==0 ){ +- WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); ++ WHERETRACE(0x800, (" VirtualOne: all disabled and w/o IN\n")); + rc = whereLoopAddVirtualOne( +- pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); ++ pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); + } + } + +- if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); +- sqlite3DbFreeNN(pParse->db, p); +- WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); ++ freeIndexInfo(pParse->db, p); ++ WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pSTab->zName, rc)); + return rc; + } + #endif /* SQLITE_OMIT_VIRTUALTABLE */ +@@ -149100,7 +169179,7 @@ static int whereLoopAddOr( + WhereClause tempWC; + WhereLoopBuilder sSubBuild; + WhereOrSet sSum, sCur; +- struct SrcList_item *pItem; ++ SrcItem *pItem; + + pWC = pBuilder->pWC; + pWCEnd = pWC->a + pWC->nTerm; +@@ -149109,6 +169188,9 @@ static int whereLoopAddOr( + pItem = pWInfo->pTabList->a + pNew->iTab; + iCur = pItem->iCursor; + ++ /* The multi-index OR optimization does not work for RIGHT and FULL JOIN */ ++ if( pItem->fg.jointype & JT_RIGHT ) return SQLITE_OK; ++ + for(pTerm=pWC->a; pTermeOperator & WO_OR)!=0 + && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0 +@@ -149120,10 +169202,9 @@ static int whereLoopAddOr( + int i, j; + + sSubBuild = *pBuilder; +- sSubBuild.pOrderBy = 0; + sSubBuild.pOrSet = &sCur; + +- WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); ++ WHERETRACE(0x400, ("Begin processing OR-clause %p\n", pTerm)); + for(pOrTerm=pOrWC->a; pOrTermeOperator & WO_AND)!=0 ){ + sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc; +@@ -149132,6 +169213,7 @@ static int whereLoopAddOr( + tempWC.pOuter = pWC; + tempWC.op = TK_AND; + tempWC.nTerm = 1; ++ tempWC.nBase = 1; + tempWC.a = pOrTerm; + sSubBuild.pWC = &tempWC; + }else{ +@@ -149139,14 +169221,14 @@ static int whereLoopAddOr( + } + sCur.n = 0; + #ifdef WHERETRACE_ENABLED +- WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n", ++ WHERETRACE(0x400, ("OR-term %d of %p has %d subterms:\n", + (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); +- if( sqlite3WhereTrace & 0x400 ){ ++ if( sqlite3WhereTrace & 0x20000 ){ + sqlite3WhereClausePrint(sSubBuild.pWC); + } + #endif + #ifndef SQLITE_OMIT_VIRTUALTABLE +- if( IsVirtual(pItem->pTab) ){ ++ if( IsVirtual(pItem->pSTab) ){ + rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable); + }else + #endif +@@ -149156,7 +169238,7 @@ static int whereLoopAddOr( + if( rc==SQLITE_OK ){ + rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); + } +- assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0 ); ++ testcase( rc==SQLITE_NOMEM && sCur.n>0 ); + testcase( rc==SQLITE_DONE ); + if( sCur.n==0 ){ + sSum.n = 0; +@@ -149201,7 +169283,7 @@ static int whereLoopAddOr( + pNew->prereq = sSum.a[i].prereq; + rc = whereLoopInsert(pBuilder, pNew); + } +- WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm)); ++ WHERETRACE(0x400, ("End processing OR-clause %p\n", pTerm)); + } + } + return rc; +@@ -149216,33 +169298,54 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ + Bitmask mPrior = 0; + int iTab; + SrcList *pTabList = pWInfo->pTabList; +- struct SrcList_item *pItem; +- struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel]; ++ SrcItem *pItem; ++ SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; + sqlite3 *db = pWInfo->pParse->db; + int rc = SQLITE_OK; ++ int bFirstPastRJ = 0; ++ int hasRightJoin = 0; + WhereLoop *pNew; + ++ + /* Loop over the tables in the join, from left to right */ + pNew = pBuilder->pNew; +- whereLoopInit(pNew); ++ ++ /* Verify that pNew has already been initialized */ ++ assert( pNew->nLTerm==0 ); ++ assert( pNew->wsFlags==0 ); ++ assert( pNew->nLSlot>=ArraySize(pNew->aLTermSpace) ); ++ assert( pNew->aLTerm!=0 ); ++ + pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; + for(iTab=0, pItem=pTabList->a; pItemiTab = iTab; + pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; + pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); +- if( (pItem->fg.jointype & (JT_LEFT|JT_CROSS))!=0 ){ +- /* This condition is true when pItem is the FROM clause term on the +- ** right-hand-side of a LEFT or CROSS JOIN. */ +- mPrereq = mPrior; +- }else{ ++ if( bFirstPastRJ ++ || (pItem->fg.jointype & (JT_OUTER|JT_CROSS|JT_LTORJ))!=0 ++ ){ ++ /* Add prerequisites to prevent reordering of FROM clause terms ++ ** across CROSS joins and outer joins. The bFirstPastRJ boolean ++ ** prevents the right operand of a RIGHT JOIN from being swapped with ++ ** other elements even further to the right. ++ ** ++ ** The JT_LTORJ case and the hasRightJoin flag work together to ++ ** prevent FROM-clause terms from moving from the right side of ++ ** a LEFT JOIN over to the left side of that join if the LEFT JOIN ++ ** is itself on the left side of a RIGHT JOIN. ++ */ ++ if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; ++ mPrereq |= mPrior; ++ bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; ++ }else if( !hasRightJoin ){ + mPrereq = 0; + } + #ifndef SQLITE_OMIT_VIRTUALTABLE +- if( IsVirtual(pItem->pTab) ){ +- struct SrcList_item *p; ++ if( IsVirtual(pItem->pSTab) ){ ++ SrcItem *p; + for(p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS)) ){ ++ if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){ + mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); + } + } +@@ -149271,6 +169374,97 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ + return rc; + } + ++/* Implementation of the order-by-subquery optimization: ++** ++** WhereLoop pLoop, which the iLoop-th term of the nested loop, is really ++** a subquery or CTE that has an ORDER BY clause. See if any of the terms ++** in the subquery ORDER BY clause will satisfy pOrderBy from the outer ++** query. Mark off all satisfied terms (by setting bits in *pOBSat) and ++** return TRUE if they do. If not, return false. ++** ++** Example: ++** ++** CREATE TABLE t1(a,b,c, PRIMARY KEY(a,b)); ++** CREATE TABLE t2(x,y); ++** WITH t3(p,q) AS MATERIALIZED (SELECT x+y, x-y FROM t2 ORDER BY x+y) ++** SELECT * FROM t3 JOIN t1 ON a=q ORDER BY p, b; ++** ++** The CTE named "t3" comes out in the natural order of "p", so the first ++** first them of "ORDER BY p,b" is satisfied by a sequential scan of "t3" ++** and sorting only needs to occur on the second term "b". ++** ++** Limitations: ++** ++** (1) The optimization is not applied if the outer ORDER BY contains ++** a COLLATE clause. The optimization might be applied if the ++** outer ORDER BY uses NULLS FIRST, NULLS LAST, ASC, and/or DESC as ++** long as the subquery ORDER BY does the same. But if the ++** outer ORDER BY uses COLLATE, even a redundant COLLATE, the ++** optimization is bypassed. ++** ++** (2) The subquery ORDER BY terms must exactly match subquery result ++** columns, including any COLLATE annotations. This routine relies ++** on iOrderByCol to do matching between order by terms and result ++** columns, and iOrderByCol will not be set if the result column ++** and ORDER BY collations differ. ++** ++** (3) The subquery and outer ORDER BY can be in opposite directions as ++** long as the subquery is materialized. If the subquery is ++** implemented as a co-routine, the sort orders must be in the same ++** direction because there is no way to run a co-routine backwards. ++*/ ++static SQLITE_NOINLINE int wherePathMatchSubqueryOB( ++ WhereInfo *pWInfo, /* The WHERE clause */ ++ WhereLoop *pLoop, /* The nested loop term that is a subquery */ ++ int iLoop, /* Which level of the nested loop. 0==outermost */ ++ int iCur, /* Cursor used by the this loop */ ++ ExprList *pOrderBy, /* The ORDER BY clause on the whole query */ ++ Bitmask *pRevMask, /* When loops need to go in reverse order */ ++ Bitmask *pOBSat /* Which terms of pOrderBy are satisfied so far */ ++){ ++ int iOB; /* Index into pOrderBy->a[] */ ++ int jSub; /* Index into pSubOB->a[] */ ++ u8 rev = 0; /* True if iOB and jSub sort in opposite directions */ ++ u8 revIdx = 0; /* Sort direction for jSub */ ++ Expr *pOBExpr; /* Current term of outer ORDER BY */ ++ ExprList *pSubOB; /* Complete ORDER BY on the subquery */ ++ ++ pSubOB = pLoop->u.btree.pOrderBy; ++ assert( pSubOB!=0 ); ++ for(iOB=0; (MASKBIT(iOB) & *pOBSat)!=0; iOB++){} ++ for(jSub=0; jSubnExpr && iOBnExpr; jSub++, iOB++){ ++ if( pSubOB->a[jSub].u.x.iOrderByCol==0 ) break; ++ pOBExpr = pOrderBy->a[iOB].pExpr; ++ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) break; ++ if( pOBExpr->iTable!=iCur ) break; ++ if( pOBExpr->iColumn!=pSubOB->a[jSub].u.x.iOrderByCol-1 ) break; ++ if( (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){ ++ u8 sfOB = pOrderBy->a[iOB].fg.sortFlags; /* sortFlags for iOB */ ++ u8 sfSub = pSubOB->a[jSub].fg.sortFlags; /* sortFlags for jSub */ ++ if( (sfSub & KEYINFO_ORDER_BIGNULL) != (sfOB & KEYINFO_ORDER_BIGNULL) ){ ++ break; ++ } ++ revIdx = sfSub & KEYINFO_ORDER_DESC; ++ if( jSub>0 ){ ++ if( (rev^revIdx)!=(sfOB & KEYINFO_ORDER_DESC) ){ ++ break; ++ } ++ }else{ ++ rev = revIdx ^ (sfOB & KEYINFO_ORDER_DESC); ++ if( rev ){ ++ if( (pLoop->wsFlags & WHERE_COROUTINE)!=0 ){ ++ /* Cannot run a co-routine in reverse order */ ++ break; ++ } ++ *pRevMask |= MASKBIT(iLoop); ++ } ++ } ++ } ++ *pOBSat |= MASKBIT(iOB); ++ } ++ return jSub>0; ++} ++ + /* + ** Examine a WherePath (with the addition of the extra WhereLoop of the 6th + ** parameters) to see if it outputs rows in the requested ORDER BY +@@ -149367,12 +169561,12 @@ static i8 wherePathSatisfiesOrderBy( + pLoop = pLast; + } + if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ +- if( pLoop->u.vtab.isOrdered && (wctrlFlags & WHERE_DISTINCTBY)==0 ){ ++ if( pLoop->u.vtab.isOrdered ++ && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) ++ ){ + obSat = obDone; + } + break; +- }else if( wctrlFlags & WHERE_DISTINCTBY ){ +- pLoop->u.btree.nDistinctCol = 0; + } + iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; + +@@ -149384,7 +169578,8 @@ static i8 wherePathSatisfiesOrderBy( + for(i=0; ia[i].pExpr); +- if( pOBExpr->op!=TK_COLUMN ) continue; ++ if( NEVER(pOBExpr==0) ) continue; ++ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; + if( pOBExpr->iTable!=iCur ) continue; + pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, + ~ready, eqOpMask, 0); +@@ -149413,9 +169608,18 @@ static i8 wherePathSatisfiesOrderBy( + + if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){ + if( pLoop->wsFlags & WHERE_IPK ){ ++ if( pLoop->u.btree.pOrderBy ++ && OptimizationEnabled(db, SQLITE_OrderBySubq) ++ && wherePathMatchSubqueryOB(pWInfo,pLoop,iLoop,iCur, ++ pOrderBy,pRevMask, &obSat) ++ ){ ++ nColumn = 0; ++ isOrderDistinct = 0; ++ }else{ ++ nColumn = 1; ++ } + pIndex = 0; + nKeyCol = 0; +- nColumn = 1; + }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){ + return 0; + }else{ +@@ -149424,6 +169628,10 @@ static i8 wherePathSatisfiesOrderBy( + assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) ); + assert( pIndex->aiColumn[nColumn-1]==XN_ROWID + || !HasRowid(pIndex->pTable)); ++ /* All relevant terms of the index must also be non-NULL in order ++ ** for isOrderDistinct to be true. So the isOrderDistinct value ++ ** computed here might be a false positive. Corrections will be ++ ** made at tag-20210426-1 below */ + isOrderDistinct = IsUniqueIndex(pIndex) + && (pLoop->wsFlags & WHERE_SKIPSCAN)==0; + } +@@ -149491,18 +169699,22 @@ static i8 wherePathSatisfiesOrderBy( + } + + /* An unconstrained column that might be NULL means that this +- ** WhereLoop is not well-ordered ++ ** WhereLoop is not well-ordered. tag-20210426-1 + */ +- if( isOrderDistinct +- && iColumn>=0 +- && j>=pLoop->u.btree.nEq +- && pIndex->pTable->aCol[iColumn].notNull==0 +- ){ +- isOrderDistinct = 0; ++ if( isOrderDistinct ){ ++ if( iColumn>=0 ++ && j>=pLoop->u.btree.nEq ++ && pIndex->pTable->aCol[iColumn].notNull==0 ++ ){ ++ isOrderDistinct = 0; ++ } ++ if( iColumn==XN_EXPR ){ ++ isOrderDistinct = 0; ++ } + } + + /* Find the ORDER BY term that corresponds to the j-th column +- ** of the index and mark that ORDER BY term off ++ ** of the index and mark that ORDER BY term having been satisfied. + */ + isMatch = 0; + for(i=0; bOnce && ia[i].pExpr); + testcase( wctrlFlags & WHERE_GROUPBY ); + testcase( wctrlFlags & WHERE_DISTINCTBY ); ++ if( NEVER(pOBExpr==0) ) continue; + if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; + if( iColumn>=XN_ROWID ){ +- if( pOBExpr->op!=TK_COLUMN ) continue; ++ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; + if( pOBExpr->iTable!=iCur ) continue; + if( pOBExpr->iColumn!=iColumn ) continue; + }else{ +- Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr; +- if( sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){ ++ Expr *pIxExpr = pIndex->aColExpr->a[j].pExpr; ++ if( sqlite3ExprCompareSkip(pOBExpr, pIxExpr, iCur) ){ + continue; + } + } +@@ -149535,16 +169748,18 @@ static i8 wherePathSatisfiesOrderBy( + /* Make sure the sort order is compatible in an ORDER BY clause. + ** Sort order is irrelevant for a GROUP BY clause. */ + if( revSet ){ +- if( (rev ^ revIdx)!=(pOrderBy->a[i].sortFlags&KEYINFO_ORDER_DESC) ){ ++ if( (rev ^ revIdx) ++ != (pOrderBy->a[i].fg.sortFlags&KEYINFO_ORDER_DESC) ++ ){ + isMatch = 0; + } + }else{ +- rev = revIdx ^ (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC); ++ rev = revIdx ^ (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC); + if( rev ) *pRevMask |= MASKBIT(iLoop); + revSet = 1; + } + } +- if( isMatch && (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL) ){ ++ if( isMatch && (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL) ){ + if( j==pLoop->u.btree.nEq ){ + pLoop->wsFlags |= WHERE_BIGNULL_SORT; + }else{ +@@ -149581,7 +169796,7 @@ static i8 wherePathSatisfiesOrderBy( + if( MASKBIT(i) & obSat ) continue; + p = pOrderBy->a[i].pExpr; + mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); +- if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue; ++ if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue; + if( (mTerm&~orderDistinctMask)==0 ){ + obSat |= MASKBIT(i); + } +@@ -149591,7 +169806,7 @@ static i8 wherePathSatisfiesOrderBy( + if( obSat==obDone ) return (i8)nOrderBy; + if( !isOrderDistinct ){ + for(i=nOrderBy-1; i>0; i--){ +- Bitmask m = MASKBIT(i) - 1; ++ Bitmask m = ALWAYS(iwctrlFlags & WHERE_GROUPBY ); ++ assert( pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY) ); + assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); + return pWInfo->sorted; + } +@@ -149647,38 +169862,275 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ + ** order. + */ + static LogEst whereSortingCost( +- WhereInfo *pWInfo, +- LogEst nRow, +- int nOrderBy, +- int nSorted ++ WhereInfo *pWInfo, /* Query planning context */ ++ LogEst nRow, /* Estimated number of rows to sort */ ++ int nOrderBy, /* Number of ORDER BY clause terms */ ++ int nSorted /* Number of initial ORDER BY terms naturally in order */ + ){ +- /* TUNING: Estimated cost of a full external sort, where N is ++ /* Estimated cost of a full external sort, where N is + ** the number of rows to sort is: + ** +- ** cost = (3.0 * N * log(N)). ++ ** cost = (K * N * log(N)). + ** + ** Or, if the order-by clause has X terms but only the last Y + ** terms are out of order, then block-sorting will reduce the + ** sorting cost to: + ** +- ** cost = (3.0 * N * log(N)) * (Y/X) ++ ** cost = (K * N * log(N)) * (Y/X) ++ ** ++ ** The constant K is at least 2.0 but will be larger if there are a ++ ** large number of columns to be sorted, as the sorting time is ++ ** proportional to the amount of content to be sorted. The algorithm ++ ** does not currently distinguish between fat columns (BLOBs and TEXTs) ++ ** and skinny columns (INTs). It just uses the number of columns as ++ ** an approximation for the row width. + ** +- ** The (Y/X) term is implemented using stack variable rScale +- ** below. */ +- LogEst rScale, rSortCost; +- assert( nOrderBy>0 && 66==sqlite3LogEst(100) ); +- rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; +- rSortCost = nRow + rScale + 16; ++ ** And extra factor of 2.0 or 3.0 is added to the sorting cost if the sort ++ ** is built using OP_IdxInsert and OP_Sort rather than with OP_SorterInsert. ++ */ ++ LogEst rSortCost, nCol; ++ assert( pWInfo->pSelect!=0 ); ++ assert( pWInfo->pSelect->pEList!=0 ); ++ /* TUNING: sorting cost proportional to the number of output columns: */ ++ nCol = sqlite3LogEst((pWInfo->pSelect->pEList->nExpr+59)/30); ++ rSortCost = nRow + nCol; ++ if( nSorted>0 ){ ++ /* Scale the result by (Y/X) */ ++ rSortCost += sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; ++ } + + /* Multiple by log(M) where M is the number of output rows. +- ** Use the LIMIT for M if it is smaller */ +- if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimitiLimit; ++ ** Use the LIMIT for M if it is smaller. Or if this sort is for ++ ** a DISTINCT operator, M will be the number of distinct output ++ ** rows, so fudge it downwards a bit. ++ */ ++ if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 ){ ++ rSortCost += 10; /* TUNING: Extra 2.0x if using LIMIT */ ++ if( nSorted!=0 ){ ++ rSortCost += 6; /* TUNING: Extra 1.5x if also using partial sort */ ++ } ++ if( pWInfo->iLimitiLimit; ++ } ++ }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ ++ /* TUNING: In the sort for a DISTINCT operator, assume that the DISTINCT ++ ** reduces the number of output rows by a factor of 2 */ ++ if( nRow>10 ){ nRow -= 10; assert( 10==sqlite3LogEst(2) ); } + } + rSortCost += estLog(nRow); + return rSortCost; + } + ++/* ++** Compute the maximum number of paths in the solver algorithm, for ++** queries that have three or more terms in the FROM clause. Queries with ++** two or fewer FROM clause terms are handled by the caller. ++** ++** Query planning is NP-hard. We must limit the number of paths at ++** each step of the solver search algorithm to avoid exponential behavior. ++** ++** The value returned is a tuning parameter. Currently the value is: ++** ++** 18 for star queries ++** 12 otherwise ++** ++** For the purposes of this heuristic, a star-query is defined as a query ++** with a large central table that is joined using an INNER JOIN, ++** not CROSS or OUTER JOINs, against four or more smaller tables. ++** The central table is called the "fact" table. The smaller tables ++** that get joined are "dimension tables". Also, any table that is ++** self-joined cannot be a dimension table; we assume that dimension ++** tables may only be joined against fact tables. ++** ++** SIDE EFFECT: (and really the whole point of this subroutine) ++** ++** If pWInfo describes a star-query, then the cost for SCANs of dimension ++** WhereLoops is increased to be slightly larger than the cost of a SCAN ++** in the fact table. Only SCAN costs are increased. SEARCH costs are ++** unchanged. This heuristic helps keep fact tables in outer loops. Without ++** this heuristic, paths with fact tables in outer loops tend to get pruned ++** by the mxChoice limit on the number of paths, resulting in poor query ++** plans. See the starschema1.test test module for examples of queries ++** that need this heuristic to find good query plans. ++** ++** This heuristic can be completely disabled, so that no query is ++** considered a star-query, using SQLITE_TESTCTRL_OPTIMIZATION to ++** disable the SQLITE_StarQuery optimization. In the CLI, the command ++** to do that is: ".testctrl opt -starquery". ++** ++** HISTORICAL NOTES: ++** ++** This optimization was first added on 2024-05-09 by check-in 38db9b5c83d. ++** The original optimization reduced the cost and output size estimate for ++** fact tables to help them move to outer loops. But months later (as people ++** started upgrading) performance regression reports started caming in, ++** including: ++** ++** forum post b18ef983e68d06d1 (2024-12-21) ++** forum post 0025389d0860af82 (2025-01-14) ++** forum post d87570a145599033 (2025-01-17) ++** ++** To address these, the criteria for a star-query was tightened to exclude ++** cases where the fact and dimensions are separated by an outer join, and ++** the affect of star-schema detection was changed to increase the rRun cost ++** on just full table scans of dimension tables, rather than reducing costs ++** in the all access methods of the fact table. ++*/ ++static int computeMxChoice(WhereInfo *pWInfo){ ++ int nLoop = pWInfo->nLevel; /* Number of terms in the join */ ++ WhereLoop *pWLoop; /* For looping over WhereLoops */ ++ ++#ifdef SQLITE_DEBUG ++ /* The star-query detection code below makes use of the following ++ ** properties of the WhereLoop list, so verify them before ++ ** continuing: ++ ** (1) .maskSelf is the bitmask corresponding to .iTab ++ ** (2) The WhereLoop list is in ascending .iTab order ++ */ ++ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ ++ assert( pWLoop->maskSelf==MASKBIT(pWLoop->iTab) ); ++ assert( pWLoop->pNextLoop==0 || pWLoop->iTab<=pWLoop->pNextLoop->iTab ); ++ } ++#endif /* SQLITE_DEBUG */ ++ ++ if( nLoop>=5 ++ && !pWInfo->bStarDone ++ && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery) ++ ){ ++ SrcItem *aFromTabs; /* All terms of the FROM clause */ ++ int iFromIdx; /* Term of FROM clause is the candidate fact-table */ ++ Bitmask m; /* Bitmask for candidate fact-table */ ++ Bitmask mSelfJoin = 0; /* Tables that cannot be dimension tables */ ++ WhereLoop *pStart; /* Where to start searching for dimension-tables */ ++ ++ pWInfo->bStarDone = 1; /* Only do this computation once */ ++ ++ /* Look for fact tables with four or more dimensions where the ++ ** dimension tables are not separately from the fact tables by an outer ++ ** or cross join. Adjust cost weights if found. ++ */ ++ assert( !pWInfo->bStarUsed ); ++ aFromTabs = pWInfo->pTabList->a; ++ pStart = pWInfo->pLoops; ++ for(iFromIdx=0, m=1; iFromIdxfg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ ++ /* If the candidate fact-table is the right table of an outer join ++ ** restrict the search for dimension-tables to be tables to the right ++ ** of the fact-table. */ ++ if( iFromIdx+4 > nLoop ) break; /* Impossible to reach nDep>=4 */ ++ while( pStart && pStart->iTab<=iFromIdx ){ ++ pStart = pStart->pNextLoop; ++ } ++ } ++ for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ ++ if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ ++ /* Fact-tables and dimension-tables cannot be separated by an ++ ** outer join (at least for the definition of fact- and dimension- ++ ** used by this heuristic). */ ++ break; ++ } ++ if( (pWLoop->prereq & m)!=0 /* pWInfo depends on iFromIdx */ ++ && (pWLoop->maskSelf & mSeen)==0 /* pWInfo not already a dependency */ ++ && (pWLoop->maskSelf & mSelfJoin)==0 /* Not a self-join */ ++ ){ ++ if( aFromTabs[pWLoop->iTab].pSTab==pFactTab->pSTab ){ ++ mSelfJoin |= m; ++ }else{ ++ nDep++; ++ mSeen |= pWLoop->maskSelf; ++ } ++ } ++ } ++ if( nDep<=3 ) continue; ++ ++ /* If we reach this point, it means that pFactTab is a fact table ++ ** with four or more dimensions connected by inner joins. Proceed ++ ** to make cost adjustments. */ ++ ++#ifdef WHERETRACE_ENABLED ++ /* Make sure rStarDelta values are initialized */ ++ if( !pWInfo->bStarUsed ){ ++ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ ++ pWLoop->rStarDelta = 0; ++ } ++ } ++#endif ++ pWInfo->bStarUsed = 1; ++ ++ /* Compute the maximum cost of any WhereLoop for the ++ ** fact table plus one epsilon */ ++ mxRun = LOGEST_MIN; ++ for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ ++ if( pWLoop->iTabiTab>iFromIdx ) break; ++ if( pWLoop->rRun>mxRun ) mxRun = pWLoop->rRun; ++ } ++ if( ALWAYS(mxRunpNextLoop){ ++ if( (pWLoop->maskSelf & mSeen)==0 ) continue; ++ if( pWLoop->nLTerm ) continue; ++ if( pWLoop->rRuniTab; ++ sqlite3DebugPrintf( ++ "Increase SCAN cost of dimension %s(%d) of fact %s(%d) to %d\n", ++ pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab, ++ pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName, ++ iFromIdx, mxRun ++ ); ++ } ++ pWLoop->rStarDelta = mxRun - pWLoop->rRun; ++#endif /* WHERETRACE_ENABLED */ ++ pWLoop->rRun = mxRun; ++ } ++ } ++ } ++#ifdef WHERETRACE_ENABLED /* 0x80000 */ ++ if( (sqlite3WhereTrace & 0x80000)!=0 && pWInfo->bStarUsed ){ ++ sqlite3DebugPrintf("WhereLoops changed by star-query heuristic:\n"); ++ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ ++ if( pWLoop->rStarDelta ){ ++ sqlite3WhereLoopPrint(pWLoop, &pWInfo->sWC); ++ } ++ } ++ } ++#endif ++ } ++ return pWInfo->bStarUsed ? 18 : 12; ++} ++ ++/* ++** Two WhereLoop objects, pCandidate and pBaseline, are known to have the ++** same cost. Look deep into each to see if pCandidate is even slightly ++** better than pBaseline. Return false if it is, if pCandidate is is preferred. ++** Return true if pBaseline is preferred or if we cannot tell the difference. ++** ++** Result Meaning ++** -------- ---------------------------------------------------------- ++** true We cannot tell the difference in pCandidate and pBaseline ++** false pCandidate seems like a better choice than pBaseline ++*/ ++static SQLITE_NOINLINE int whereLoopIsNoBetter( ++ const WhereLoop *pCandidate, ++ const WhereLoop *pBaseline ++){ ++ if( (pCandidate->wsFlags & WHERE_INDEXED)==0 ) return 1; ++ if( (pBaseline->wsFlags & WHERE_INDEXED)==0 ) return 1; ++ if( pCandidate->u.btree.pIndex->szIdxRow < ++ pBaseline->u.btree.pIndex->szIdxRow ) return 0; ++ return 1; ++} ++ + /* + ** Given the list of WhereLoop objects at pWInfo->pLoops, this routine + ** attempts to find the lowest cost path that visits each WhereLoop +@@ -149695,13 +170147,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + int mxChoice; /* Maximum number of simultaneous paths tracked */ + int nLoop; /* Number of terms in the join */ + Parse *pParse; /* Parsing context */ +- sqlite3 *db; /* The database connection */ + int iLoop; /* Loop counter over the terms of the join */ + int ii, jj; /* Loop counters */ + int mxI = 0; /* Index of next entry to replace */ + int nOrderBy; /* Number of ORDER BY clause terms */ + LogEst mxCost = 0; /* Maximum cost of a set of paths */ +- LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */ ++ LogEst mxUnsort = 0; /* Maximum unsorted cost of a set of path */ + int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */ + WherePath *aFrom; /* All nFrom paths at the previous level */ + WherePath *aTo; /* The nTo best paths at the current level */ +@@ -149714,14 +170165,28 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + int nSpace; /* Bytes of space allocated at pSpace */ + + pParse = pWInfo->pParse; +- db = pParse->db; + nLoop = pWInfo->nLevel; +- /* TUNING: For simple queries, only the best path is tracked. +- ** For 2-way joins, the 5 best paths are followed. +- ** For joins of 3 or more tables, track the 10 best paths */ +- mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10); ++ WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d, nQueryLoop=%d)\n", ++ nRowEst, pParse->nQueryLoop)); ++ /* TUNING: mxChoice is the maximum number of possible paths to preserve ++ ** at each step. Based on the number of loops in the FROM clause: ++ ** ++ ** nLoop mxChoice ++ ** ----- -------- ++ ** 1 1 // the most common case ++ ** 2 5 ++ ** 3+ 12 or 18 // see computeMxChoice() ++ */ ++ if( nLoop<=1 ){ ++ mxChoice = 1; ++ }else if( nLoop==2 ){ ++ mxChoice = 5; ++ }else if( pParse->nErr ){ ++ mxChoice = 1; ++ }else{ ++ mxChoice = computeMxChoice(pWInfo); ++ } + assert( nLoop<=pWInfo->pTabList->nSrc ); +- WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst)); + + /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this + ** case the purpose of this call is to estimate the number of rows returned +@@ -149737,7 +170202,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + /* Allocate and initialize space for aTo, aFrom and aSortCost[] */ + nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2; + nSpace += sizeof(LogEst) * nOrderBy; +- pSpace = sqlite3DbMallocRawNN(db, nSpace); ++ pSpace = sqlite3StackAllocRawNN(pParse->db, nSpace); + if( pSpace==0 ) return SQLITE_NOMEM_BKPT; + aTo = (WherePath*)pSpace; + aFrom = aTo+mxChoice; +@@ -149786,10 +170251,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + LogEst nOut; /* Rows visited by (pFrom+pWLoop) */ + LogEst rCost; /* Cost of path (pFrom+pWLoop) */ +- LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ +- i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */ ++ LogEst rUnsort; /* Unsorted cost of (pFrom+pWLoop) */ ++ i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */ + Bitmask maskNew; /* Mask of src visited by (..) */ +- Bitmask revMask = 0; /* Mask of rev-order loops for (..) */ ++ Bitmask revMask; /* Mask of rev-order loops for (..) */ + + if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue; + if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue; +@@ -149804,11 +170269,16 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + + /* At this point, pWLoop is a candidate to be the next loop. + ** Compute its cost */ +- rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow); +- rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); ++ rUnsort = pWLoop->rRun + pFrom->nRow; ++ if( pWLoop->rSetup ){ ++ rUnsort = sqlite3LogEstAdd(pWLoop->rSetup, rUnsort); ++ } ++ rUnsort = sqlite3LogEstAdd(rUnsort, pFrom->rUnsort); + nOut = pFrom->nRow + pWLoop->nOut; + maskNew = pFrom->maskLoop | pWLoop->maskSelf; ++ isOrdered = pFrom->isOrdered; + if( isOrdered<0 ){ ++ revMask = 0; + isOrdered = wherePathSatisfiesOrderBy(pWInfo, + pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags, + iLoop, pWLoop, &revMask); +@@ -149821,19 +170291,19 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + pWInfo, nRowEst, nOrderBy, isOrdered + ); + } +- /* TUNING: Add a small extra penalty (5) to sorting as an +- ** extra encouragment to the query planner to select a plan ++ /* TUNING: Add a small extra penalty (3) to sorting as an ++ ** extra encouragement to the query planner to select a plan + ** where the rows emerge in the correct order without any sorting + ** required. */ +- rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 5; ++ rCost = sqlite3LogEstAdd(rUnsort, aSortCost[isOrdered]) + 3; + + WHERETRACE(0x002, + ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n", + aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy, +- rUnsorted, rCost)); ++ rUnsort, rCost)); + }else{ +- rCost = rUnsorted; +- rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */ ++ rCost = rUnsort; ++ rUnsort -= 2; /* TUNING: Slight bias in favor of no-sort plans */ + } + + /* Check to see if pWLoop should be added to the set of +@@ -149847,6 +170317,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range + ** of legal values for isOrdered, -1..64. + */ ++ testcase( nTo==0 ); + for(jj=0, pTo=aTo; jjmaskLoop==maskNew + && ((pTo->isOrdered^isOrdered)&0x80)==0 +@@ -149858,7 +170329,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + if( jj>=nTo ){ + /* None of the existing best-so-far paths match the candidate. */ + if( nTo>=mxChoice +- && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) ++ && (rCost>mxCost || (rCost==mxCost && rUnsort>=mxUnsort)) + ){ + /* The current candidate is no better than any of the mxChoice + ** paths currently in the best-so-far buffer. So discard +@@ -149866,7 +170337,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + #ifdef WHERETRACE_ENABLED /* 0x4 */ + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n", +- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, ++ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, + isOrdered>=0 ? isOrdered+'0' : '?'); + } + #endif +@@ -149885,7 +170356,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + #ifdef WHERETRACE_ENABLED /* 0x4 */ + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf("New %s cost=%-3d,%3d,%3d order=%c\n", +- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, ++ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, + isOrdered>=0 ? isOrdered+'0' : '?'); + } + #endif +@@ -149896,24 +170367,23 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + ** pTo or if the candidate should be skipped. + ** + ** The conditional is an expanded vector comparison equivalent to: +- ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted) ++ ** (pTo->rCost,pTo->nRow,pTo->rUnsort) <= (rCost,nOut,rUnsort) + */ +- if( pTo->rCostrCost==rCost +- && (pTo->nRownRow==nOut && pTo->rUnsorted<=rUnsorted) +- ) +- ) ++ if( (pTo->rCostrCost==rCost && pTo->nRowrCost==rCost && pTo->nRow==nOut && pTo->rUnsortrCost==rCost && pTo->nRow==nOut && pTo->rUnsort==rUnsort ++ && whereLoopIsNoBetter(pWLoop, pTo->aLoop[iLoop]) ) + ){ + #ifdef WHERETRACE_ENABLED /* 0x4 */ + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf( + "Skip %s cost=%-3d,%3d,%3d order=%c", +- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, ++ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, + isOrdered>=0 ? isOrdered+'0' : '?'); + sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n", + wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, +- pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); ++ pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); + } + #endif + /* Discard the candidate path from further consideration */ +@@ -149927,11 +170397,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf( + "Update %s cost=%-3d,%3d,%3d order=%c", +- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, ++ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, + isOrdered>=0 ? isOrdered+'0' : '?'); + sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n", + wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, +- pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); ++ pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); + } + #endif + } +@@ -149940,20 +170410,20 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + pTo->revLoop = revMask; + pTo->nRow = nOut; + pTo->rCost = rCost; +- pTo->rUnsorted = rUnsorted; ++ pTo->rUnsort = rUnsort; + pTo->isOrdered = isOrdered; + memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop); + pTo->aLoop[iLoop] = pWLoop; + if( nTo>=mxChoice ){ + mxI = 0; + mxCost = aTo[0].rCost; +- mxUnsorted = aTo[0].nRow; ++ mxUnsort = aTo[0].nRow; + for(jj=1, pTo=&aTo[1]; jjrCost>mxCost +- || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted) ++ || (pTo->rCost==mxCost && pTo->rUnsort>mxUnsort) + ){ + mxCost = pTo->rCost; +- mxUnsorted = pTo->rUnsorted; ++ mxUnsort = pTo->rUnsort; + mxI = jj; + } + } +@@ -149963,17 +170433,32 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + + #ifdef WHERETRACE_ENABLED /* >=2 */ + if( sqlite3WhereTrace & 0x02 ){ ++ LogEst rMin, rFloor = 0; ++ int nDone = 0; ++ int nProgress; + sqlite3DebugPrintf("---- after round %d ----\n", iLoop); +- for(ii=0, pTo=aTo; iirCost, pTo->nRow, +- pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); +- if( pTo->isOrdered>0 ){ +- sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); +- }else{ +- sqlite3DebugPrintf("\n"); ++ do{ ++ nProgress = 0; ++ rMin = 0x7fff; ++ for(ii=0, pTo=aTo; iirCost>rFloor && pTo->rCostrCost; ++ } ++ for(ii=0, pTo=aTo; iirCost==rMin ){ ++ sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c", ++ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, ++ pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); ++ if( pTo->isOrdered>0 ){ ++ sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); ++ }else{ ++ sqlite3DebugPrintf("\n"); ++ } ++ nDone++; ++ nProgress++; ++ } + } +- } ++ rFloor = rMin; ++ }while( nDone0 ); + } + #endif + +@@ -149986,7 +170471,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + + if( nFrom==0 ){ + sqlite3ErrorMsg(pParse, "no query solution"); +- sqlite3DbFreeNN(db, pSpace); ++ sqlite3StackFreeNN(pParse->db, pSpace); + return SQLITE_ERROR; + } + +@@ -150017,12 +170502,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + } + pWInfo->bOrderedInnerLoop = 0; + if( pWInfo->pOrderBy ){ ++ pWInfo->nOBSat = pFrom->isOrdered; + if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ + if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ + pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; + } ++ /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */ ++ assert( pWInfo->pSelect->pOrderBy==0 ++ || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr ); + }else{ +- pWInfo->nOBSat = pFrom->isOrdered; + pWInfo->revMask = pFrom->revLoop; + if( pWInfo->nOBSat<=0 ){ + pWInfo->nOBSat = 0; +@@ -150064,14 +170552,93 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + } + } + +- + pWInfo->nRowOut = pFrom->nRow; ++#ifdef WHERETRACE_ENABLED ++ pWInfo->rTotalCost = pFrom->rCost; ++#endif + + /* Free temporary memory and return success */ +- sqlite3DbFreeNN(db, pSpace); ++ sqlite3StackFreeNN(pParse->db, pSpace); + return SQLITE_OK; + } + ++/* ++** This routine implements a heuristic designed to improve query planning. ++** This routine is called in between the first and second call to ++** wherePathSolver(). Hence the name "Interstage" "Heuristic". ++** ++** The first call to wherePathSolver() (hereafter just "solver()") computes ++** the best path without regard to the order of the outputs. The second call ++** to the solver() builds upon the first call to try to find an alternative ++** path that satisfies the ORDER BY clause. ++** ++** This routine looks at the results of the first solver() run, and for ++** every FROM clause term in the resulting query plan that uses an equality ++** constraint against an index, disable other WhereLoops for that same ++** FROM clause term that would try to do a full-table scan. This prevents ++** an index search from being converted into a full-table scan in order to ++** satisfy an ORDER BY clause, since even though we might get slightly better ++** performance using the full-scan without sorting if the output size ++** estimates are very precise, we might also get severe performance ++** degradation using the full-scan if the output size estimate is too large. ++** It is better to err on the side of caution. ++** ++** Except, if the first solver() call generated a full-table scan in an outer ++** loop then stop this analysis at the first full-scan, since the second ++** solver() run might try to swap that full-scan for another in order to ++** get the output into the correct order. In other words, we allow a ++** rewrite like this: ++** ++** First Solver() Second Solver() ++** |-- SCAN t1 |-- SCAN t2 ++** |-- SEARCH t2 `-- SEARCH t1 ++** `-- SORT USING B-TREE ++** ++** The purpose of this routine is to disallow rewrites such as: ++** ++** First Solver() Second Solver() ++** |-- SEARCH t1 |-- SCAN t2 <--- bad! ++** |-- SEARCH t2 `-- SEARCH t1 ++** `-- SORT USING B-TREE ++** ++** See test cases in test/whereN.test for the real-world query that ++** originally provoked this heuristic. ++*/ ++static SQLITE_NOINLINE void whereInterstageHeuristic(WhereInfo *pWInfo){ ++ int i; ++#ifdef WHERETRACE_ENABLED ++ int once = 0; ++#endif ++ for(i=0; inLevel; i++){ ++ WhereLoop *p = pWInfo->a[i].pWLoop; ++ if( p==0 ) break; ++ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue; ++ if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){ ++ u8 iTab = p->iTab; ++ WhereLoop *pLoop; ++ for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){ ++ if( pLoop->iTab!=iTab ) continue; ++ if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){ ++ /* Auto-index and index-constrained loops allowed to remain */ ++ continue; ++ } ++#ifdef WHERETRACE_ENABLED ++ if( sqlite3WhereTrace & 0x80 ){ ++ if( once==0 ){ ++ sqlite3DebugPrintf("Loops disabled by interstage heuristic:\n"); ++ once = 1; ++ } ++ sqlite3WhereLoopPrint(pLoop, &pWInfo->sWC); ++ } ++#endif /* WHERETRACE_ENABLED */ ++ pLoop->prereq = ALLBITS; /* Prevent 2nd solver() from using this one */ ++ } ++ }else{ ++ break; ++ } ++ } ++} ++ + /* + ** Most queries use only a single table (they are not joins) and have + ** simple == constraints against indexed fields. This routine attempts +@@ -150085,7 +170652,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ + */ + static int whereShortCut(WhereLoopBuilder *pBuilder){ + WhereInfo *pWInfo; +- struct SrcList_item *pItem; ++ SrcItem *pItem; + WhereClause *pWC; + WhereTerm *pTerm; + WhereLoop *pLoop; +@@ -150093,20 +170660,26 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ + int j; + Table *pTab; + Index *pIdx; ++ WhereScan scan; + + pWInfo = pBuilder->pWInfo; + if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; + assert( pWInfo->pTabList->nSrc>=1 ); + pItem = pWInfo->pTabList->a; +- pTab = pItem->pTab; ++ pTab = pItem->pSTab; + if( IsVirtual(pTab) ) return 0; +- if( pItem->fg.isIndexedBy ) return 0; ++ if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){ ++ testcase( pItem->fg.isIndexedBy ); ++ testcase( pItem->fg.notIndexed ); ++ return 0; ++ } + iCur = pItem->iCursor; + pWC = &pWInfo->sWC; + pLoop = pBuilder->pNew; + pLoop->wsFlags = 0; + pLoop->nSkip = 0; +- pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); ++ pTerm = whereScanInit(&scan, pWC, iCur, -1, WO_EQ|WO_IS, 0); ++ while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); + if( pTerm ){ + testcase( pTerm->eOperator & WO_IS ); + pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; +@@ -150125,7 +170698,8 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ + ) continue; + opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ; + for(j=0; jnKeyCol; j++){ +- pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx); ++ pTerm = whereScanInit(&scan, pWC, iCur, j, opMask, pIdx); ++ while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); + if( pTerm==0 ) break; + testcase( pTerm->eOperator & WO_IS ); + pLoop->aLTerm[j] = pTerm; +@@ -150154,8 +170728,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ + if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ + pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + } ++ if( scan.iEquiv>1 ) pLoop->wsFlags |= WHERE_TRANSCONS; + #ifdef SQLITE_DEBUG + pLoop->cId = '0'; ++#endif ++#ifdef WHERETRACE_ENABLED ++ if( sqlite3WhereTrace & 0x02 ){ ++ sqlite3DebugPrintf("whereShortCut() used to compute solution\n"); ++ } + #endif + return 1; + } +@@ -150210,6 +170790,255 @@ static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){ + # define WHERETRACE_ALL_LOOPS(W,C) + #endif + ++/* Attempt to omit tables from a join that do not affect the result. ++** For a table to not affect the result, the following must be true: ++** ++** 1) The query must not be an aggregate. ++** 2) The table must be the RHS of a LEFT JOIN. ++** 3) Either the query must be DISTINCT, or else the ON or USING clause ++** must contain a constraint that limits the scan of the table to ++** at most a single row. ++** 4) The table must not be referenced by any part of the query apart ++** from its own USING or ON clause. ++** 5) The table must not have an inner-join ON or USING clause if there is ++** a RIGHT JOIN anywhere in the query. Otherwise the ON/USING clause ++** might move from the right side to the left side of the RIGHT JOIN. ++** Note: Due to (2), this condition can only arise if the table is ++** the right-most table of a subquery that was flattened into the ++** main query and that subquery was the right-hand operand of an ++** inner join that held an ON or USING clause. ++** 6) The ORDER BY clause has 63 or fewer terms ++** 7) The omit-noop-join optimization is enabled. ++** ++** Items (1), (6), and (7) are checked by the caller. ++** ++** For example, given: ++** ++** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); ++** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); ++** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); ++** ++** then table t2 can be omitted from the following: ++** ++** SELECT v1, v3 FROM t1 ++** LEFT JOIN t2 ON (t1.ipk=t2.ipk) ++** LEFT JOIN t3 ON (t1.ipk=t3.ipk) ++** ++** or from: ++** ++** SELECT DISTINCT v1, v3 FROM t1 ++** LEFT JOIN t2 ++** LEFT JOIN t3 ON (t1.ipk=t3.ipk) ++*/ ++static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( ++ WhereInfo *pWInfo, ++ Bitmask notReady ++){ ++ int i; ++ Bitmask tabUsed; ++ int hasRightJoin; ++ ++ /* Preconditions checked by the caller */ ++ assert( pWInfo->nLevel>=2 ); ++ assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) ); ++ ++ /* These two preconditions checked by the caller combine to guarantee ++ ** condition (1) of the header comment */ ++ assert( pWInfo->pResultSet!=0 ); ++ assert( 0==(pWInfo->wctrlFlags & WHERE_AGG_DISTINCT) ); ++ ++ tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet); ++ if( pWInfo->pOrderBy ){ ++ tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); ++ } ++ hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; ++ for(i=pWInfo->nLevel-1; i>=1; i--){ ++ WhereTerm *pTerm, *pEnd; ++ SrcItem *pItem; ++ WhereLoop *pLoop; ++ Bitmask m1; ++ pLoop = pWInfo->a[i].pWLoop; ++ pItem = &pWInfo->pTabList->a[pLoop->iTab]; ++ if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; ++ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0 ++ && (pLoop->wsFlags & WHERE_ONEROW)==0 ++ ){ ++ continue; ++ } ++ if( (tabUsed & pLoop->maskSelf)!=0 ) continue; ++ pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm; ++ for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ ++ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON) ++ || pTerm->pExpr->w.iJoin!=pItem->iCursor ++ ){ ++ break; ++ } ++ } ++ if( hasRightJoin ++ && ExprHasProperty(pTerm->pExpr, EP_InnerON) ++ && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor) ++ ){ ++ break; /* restriction (5) */ ++ } ++ } ++ if( pTerm omit unused FROM-clause term %c\n",pLoop->cId)); ++ m1 = MASKBIT(i)-1; ++ testcase( ((pWInfo->revMask>>1) & ~m1)!=0 ); ++ pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); ++ notReady &= ~pLoop->maskSelf; ++ for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ ++ pTerm->wtFlags |= TERM_CODED; ++ } ++ } ++ if( i!=pWInfo->nLevel-1 ){ ++ int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); ++ memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); ++ } ++ pWInfo->nLevel--; ++ assert( pWInfo->nLevel>0 ); ++ } ++ return notReady; ++} ++ ++/* ++** Check to see if there are any SEARCH loops that might benefit from ++** using a Bloom filter. Consider a Bloom filter if: ++** ++** (1) The SEARCH happens more than N times where N is the number ++** of rows in the table that is being considered for the Bloom ++** filter. ++** (2) Some searches are expected to find zero rows. (This is determined ++** by the WHERE_SELFCULL flag on the term.) ++** (3) Bloom-filter processing is not disabled. (Checked by the ++** caller.) ++** (4) The size of the table being searched is known by ANALYZE. ++** ++** This block of code merely checks to see if a Bloom filter would be ++** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the ++** WhereLoop. The implementation of the Bloom filter comes further ++** down where the code for each WhereLoop is generated. ++*/ ++static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( ++ const WhereInfo *pWInfo ++){ ++ int i; ++ LogEst nSearch = 0; ++ ++ assert( pWInfo->nLevel>=2 ); ++ assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) ); ++ for(i=0; inLevel; i++){ ++ WhereLoop *pLoop = pWInfo->a[i].pWLoop; ++ const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); ++ SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; ++ Table *pTab = pItem->pSTab; ++ if( (pTab->tabFlags & TF_HasStat1)==0 ) break; ++ pTab->tabFlags |= TF_MaybeReanalyze; ++ if( i>=1 ++ && (pLoop->wsFlags & reqFlags)==reqFlags ++ /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ ++ && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0) ++ ){ ++ if( nSearch > pTab->nRowLogEst ){ ++ testcase( pItem->fg.jointype & JT_LEFT ); ++ pLoop->wsFlags |= WHERE_BLOOMFILTER; ++ pLoop->wsFlags &= ~WHERE_IDX_ONLY; ++ WHERETRACE(0xffffffff, ( ++ "-> use Bloom-filter on loop %c because there are ~%.1e " ++ "lookups into %s which has only ~%.1e rows\n", ++ pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName, ++ (double)sqlite3LogEstToInt(pTab->nRowLogEst))); ++ } ++ } ++ nSearch += pLoop->nOut; ++ } ++} ++ ++/* ++** The index pIdx is used by a query and contains one or more expressions. ++** In other words pIdx is an index on an expression. iIdxCur is the cursor ++** number for the index and iDataCur is the cursor number for the corresponding ++** table. ++** ++** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for ++** each of the expressions in the index so that the expression code generator ++** will know to replace occurrences of the indexed expression with ++** references to the corresponding column of the index. ++*/ ++static SQLITE_NOINLINE void whereAddIndexedExpr( ++ Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxEpr */ ++ Index *pIdx, /* The index-on-expression that contains the expressions */ ++ int iIdxCur, /* Cursor number for pIdx */ ++ SrcItem *pTabItem /* The FROM clause entry for the table */ ++){ ++ int i; ++ IndexedExpr *p; ++ Table *pTab; ++ assert( pIdx->bHasExpr ); ++ pTab = pIdx->pTable; ++ for(i=0; inColumn; i++){ ++ Expr *pExpr; ++ int j = pIdx->aiColumn[i]; ++ if( j==XN_EXPR ){ ++ pExpr = pIdx->aColExpr->a[i].pExpr; ++ }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ ++ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); ++ }else{ ++ continue; ++ } ++ if( sqlite3ExprIsConstant(0,pExpr) ) continue; ++ p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); ++ if( p==0 ) break; ++ p->pIENext = pParse->pIdxEpr; ++#ifdef WHERETRACE_ENABLED ++ if( sqlite3WhereTrace & 0x200 ){ ++ sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i); ++ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(pExpr); ++ } ++#endif ++ p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); ++ p->iDataCur = pTabItem->iCursor; ++ p->iIdxCur = iIdxCur; ++ p->iIdxCol = i; ++ p->bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; ++ if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){ ++ p->aff = pIdx->zColAff[i]; ++ } ++#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS ++ p->zIdxName = pIdx->zName; ++#endif ++ pParse->pIdxEpr = p; ++ if( p->pIENext==0 ){ ++ void *pArg = (void*)&pParse->pIdxEpr; ++ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); ++ } ++ } ++} ++ ++/* ++** Set the reverse-scan order mask to one for all tables in the query ++** with the exception of MATERIALIZED common table expressions that have ++** their own internal ORDER BY clauses. ++** ++** This implements the PRAGMA reverse_unordered_selects=ON setting. ++** (Also SQLITE_DBCONFIG_REVERSE_SCANORDER). ++*/ ++static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){ ++ int ii; ++ for(ii=0; iipTabList->nSrc; ii++){ ++ SrcItem *pItem = &pWInfo->pTabList->a[ii]; ++ if( !pItem->fg.isCte ++ || pItem->u2.pCteUse->eM10d!=M10d_Yes ++ || NEVER(pItem->fg.isSubquery==0) ++ || pItem->u4.pSubq->pSelect->pOrderBy==0 ++ ){ ++ pWInfo->revMask |= MASKBIT(ii); ++ } ++ } ++} ++ + /* + ** Generate the beginning of the loop used for WHERE clause processing. + ** The return value is a pointer to an opaque structure that contains +@@ -150268,7 +171097,7 @@ static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){ + ** + ** OUTER JOINS + ** +-** An outer join of tables t1 and t2 is conceptally coded as follows: ++** An outer join of tables t1 and t2 is conceptually coded as follows: + ** + ** foreach row1 in t1 do + ** flag = 0 +@@ -150304,6 +171133,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + Expr *pWhere, /* The WHERE clause */ + ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ + ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ ++ Select *pSelect, /* The entire SELECT statement */ + u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ + int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number + ** If WHERE_USE_LIMIT, then the limit amount */ +@@ -150337,13 +171167,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + + /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ + testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); +- if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0; +- sWLB.pOrderBy = pOrderBy; +- +- /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via +- ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ +- if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ ++ if( pOrderBy && pOrderBy->nExpr>=BMS ){ ++ pOrderBy = 0; + wctrlFlags &= ~WHERE_WANT_DISTINCT; ++ wctrlFlags |= WHERE_KEEP_ALL_JOINS; /* Disable omit-noop-join opt */ + } + + /* The number of tables in the FROM clause is limited by the number of +@@ -150369,7 +171196,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + ** field (type Bitmask) it must be aligned on an 8-byte boundary on + ** some architectures. Hence the ROUND8() below. + */ +- nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); ++ nByteWInfo = SZ_WHEREINFO(nTabList); + pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); + if( db->mallocFailed ){ + sqlite3DbFree(db, pWInfo); +@@ -150379,7 +171206,9 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + pWInfo->pParse = pParse; + pWInfo->pTabList = pTabList; + pWInfo->pOrderBy = pOrderBy; ++#if WHERETRACE_ENABLED + pWInfo->pWhere = pWhere; ++#endif + pWInfo->pResultSet = pResultSet; + pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; + pWInfo->nLevel = nTabList; +@@ -150387,11 +171216,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + pWInfo->wctrlFlags = wctrlFlags; + pWInfo->iLimit = iAuxArg; + pWInfo->savedNQueryLoop = pParse->nQueryLoop; ++ pWInfo->pSelect = pSelect; + memset(&pWInfo->nOBSat, 0, + offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); + memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); + assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */ + pMaskSet = &pWInfo->sMaskSet; ++ pMaskSet->n = 0; ++ pMaskSet->ix[0] = -99; /* Initialize ix[0] to a value that can never be ++ ** a valid cursor number, to avoid an initial ++ ** test for pMaskSet->n==0 in sqlite3WhereGetMask() */ + sWLB.pWInfo = pWInfo; + sWLB.pWC = &pWInfo->sWC; + sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo); +@@ -150404,7 +171238,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + /* Split the WHERE clause into separate subexpressions where each + ** subexpression is separated by an AND operator. + */ +- initMaskSet(pMaskSet); + sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); + sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); + +@@ -150412,16 +171245,22 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + */ + if( nTabList==0 ){ + if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; +- if( wctrlFlags & WHERE_WANT_DISTINCT ){ ++ if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0 ++ && OptimizationEnabled(db, SQLITE_DistinctOpt) ++ ){ + pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + } +- ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); ++ if( ALWAYS(pWInfo->pSelect) ++ && (pWInfo->pSelect->selFlags & SF_MultiValue)==0 ++ ){ ++ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); ++ } + }else{ + /* Assign a bit from the bitmask to every term in the FROM clause. + ** + ** The N-th term of the FROM clause is assigned a bitmask of 1<sWC); +- if( db->mallocFailed ) goto whereBeginError; ++ if( pSelect && pSelect->pLimit ){ ++ sqlite3WhereAddLimit(&pWInfo->sWC, pSelect); ++ } ++ if( pParse->nErr ) goto whereBeginError; + +- /* Special case: WHERE terms that do not refer to any tables in the join +- ** (constant expressions). Evaluate each such term, and jump over all the +- ** generated code if the result is not true. ++ /* The False-WHERE-Term-Bypass optimization: + ** +- ** Do not do this if the expression contains non-deterministic functions +- ** that are not within a sub-select. This is not strictly required, but +- ** preserves SQLite's legacy behaviour in the following two cases: ++ ** If there are WHERE terms that are false, then no rows will be output, ++ ** so skip over all of the code generated here. + ** +- ** FROM ... WHERE random()>0; -- eval random() once per row +- ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall ++ ** Conditions: ++ ** ++ ** (1) The WHERE term must not refer to any tables in the join. ++ ** (2) The term must not come from an ON clause on the ++ ** right-hand side of a LEFT or FULL JOIN. ++ ** (3) The term must not come from an ON clause, or there must be ++ ** no RIGHT or FULL OUTER joins in pTabList. ++ ** (4) If the expression contains non-deterministic functions ++ ** that are not within a sub-select. This is not required ++ ** for correctness but rather to preserves SQLite's legacy ++ ** behaviour in the following two cases: ++ ** ++ ** WHERE random()>0; -- eval random() once per row ++ ** WHERE (SELECT random())>0; -- eval random() just once overall ++ ** ++ ** Note that the Where term need not be a constant in order for this ++ ** optimization to apply, though it does need to be constant relative to ++ ** the current subquery (condition 1). The term might include variables ++ ** from outer queries so that the value of the term changes from one ++ ** invocation of the current subquery to the next. + */ +- for(ii=0; iinTerm; ii++){ +- WhereTerm *pT = &sWLB.pWC->a[ii]; ++ for(ii=0; iinBase; ii++){ ++ WhereTerm *pT = &sWLB.pWC->a[ii]; /* A term of the WHERE clause */ ++ Expr *pX; /* The expression of pT */ + if( pT->wtFlags & TERM_VIRTUAL ) continue; +- if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ +- sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); ++ pX = pT->pExpr; ++ assert( pX!=0 ); ++ assert( pT->prereqAll!=0 || !ExprHasProperty(pX, EP_OuterON) ); ++ if( pT->prereqAll==0 /* Conditions (1) and (2) */ ++ && (nTabList==0 || exprIsDeterministic(pX)) /* Condition (4) */ ++ && !(ExprHasProperty(pX, EP_InnerON) /* Condition (3) */ ++ && (pTabList->a[0].fg.jointype & JT_LTORJ)!=0 ) ++ ){ ++ sqlite3ExprIfFalse(pParse, pX, pWInfo->iBreak, SQLITE_JUMPIFNULL); + pT->wtFlags |= TERM_CODED; + } + } + + if( wctrlFlags & WHERE_WANT_DISTINCT ){ +- if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ ++ if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ ++ /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via ++ ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ ++ wctrlFlags &= ~WHERE_WANT_DISTINCT; ++ pWInfo->wctrlFlags &= ~WHERE_WANT_DISTINCT; ++ }else if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ + /* The DISTINCT marking is pointless. Ignore it. */ + pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + }else if( pOrderBy==0 ){ +@@ -150485,13 +171355,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + + /* Construct the WhereLoop objects */ + #if defined(WHERETRACE_ENABLED) +- if( sqlite3WhereTrace & 0xffff ){ ++ if( sqlite3WhereTrace & 0xffffffff ){ + sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags); + if( wctrlFlags & WHERE_USE_LIMIT ){ + sqlite3DebugPrintf(", limit: %d", iAuxArg); + } + sqlite3DebugPrintf(")\n"); +- if( sqlite3WhereTrace & 0x100 ){ ++ if( sqlite3WhereTrace & 0x8000 ){ + Select sSelect; + memset(&sSelect, 0, sizeof(sSelect)); + sSelect.selFlags = SF_WhereBegin; +@@ -150501,10 +171371,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + sSelect.pEList = pResultSet; + sqlite3TreeViewSelect(0, &sSelect, 0); + } +- } +- if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ +- sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n"); +- sqlite3WhereClausePrint(sWLB.pWC); ++ if( sqlite3WhereTrace & 0x4000 ){ /* Display all WHERE clause terms */ ++ sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n"); ++ sqlite3WhereClausePrint(sWLB.pWC); ++ } + } + #endif + +@@ -150520,7 +171390,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + ** loops will be built using the revised truthProb values. */ + if( sWLB.bldFlags2 & SQLITE_BLDF2_2NDPASS ){ + WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC); +- WHERETRACE(0xffff, ++ WHERETRACE(0xffffffff, + ("**** Redo all loop computations due to" + " TERM_HIGHTRUTH changes ****\n")); + while( pWInfo->pLoops ){ +@@ -150537,19 +171407,34 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + wherePathSolver(pWInfo, 0); + if( db->mallocFailed ) goto whereBeginError; + if( pWInfo->pOrderBy ){ +- wherePathSolver(pWInfo, pWInfo->nRowOut+1); ++ whereInterstageHeuristic(pWInfo); ++ wherePathSolver(pWInfo, pWInfo->nRowOut<0 ? 1 : pWInfo->nRowOut+1); + if( db->mallocFailed ) goto whereBeginError; + } ++ ++ /* TUNING: Assume that a DISTINCT clause on a subquery reduces ++ ** the output size by a factor of 8 (LogEst -30). Search for ++ ** tag-20250414a to see other cases. ++ */ ++ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){ ++ WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n", ++ pWInfo->nRowOut, pWInfo->nRowOut-30)); ++ pWInfo->nRowOut -= 30; ++ } ++ + } ++ assert( pWInfo->pTabList!=0 ); + if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ +- pWInfo->revMask = ALLBITS; ++ whereReverseScanOrder(pWInfo); + } +- if( pParse->nErr || NEVER(db->mallocFailed) ){ ++ if( pParse->nErr ){ + goto whereBeginError; + } ++ assert( db->mallocFailed==0 ); + #ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace ){ +- sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); ++ sqlite3DebugPrintf("---- Solution cost=%d, nRow=%d", ++ pWInfo->rTotalCost, pWInfo->nRowOut); + if( pWInfo->nOBSat>0 ){ + sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); + } +@@ -150574,89 +171459,42 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + } + #endif + +- /* Attempt to omit tables from the join that do not affect the result. +- ** For a table to not affect the result, the following must be true: +- ** +- ** 1) The query must not be an aggregate. +- ** 2) The table must be the RHS of a LEFT JOIN. +- ** 3) Either the query must be DISTINCT, or else the ON or USING clause +- ** must contain a constraint that limits the scan of the table to +- ** at most a single row. +- ** 4) The table must not be referenced by any part of the query apart +- ** from its own USING or ON clause. +- ** +- ** For example, given: +- ** +- ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); +- ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); +- ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); +- ** +- ** then table t2 can be omitted from the following: ++ /* Attempt to omit tables from a join that do not affect the result. ++ ** See the comment on whereOmitNoopJoin() for further information. + ** +- ** SELECT v1, v3 FROM t1 +- ** LEFT JOIN t2 ON (t1.ipk=t2.ipk) +- ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) +- ** +- ** or from: +- ** +- ** SELECT DISTINCT v1, v3 FROM t1 +- ** LEFT JOIN t2 +- ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) ++ ** This query optimization is factored out into a separate "no-inline" ++ ** procedure to keep the sqlite3WhereBegin() procedure from becoming ++ ** too large. If sqlite3WhereBegin() becomes too large, that prevents ++ ** some C-compiler optimizers from in-lining the ++ ** sqlite3WhereCodeOneLoopStart() procedure, and it is important to ++ ** in-line sqlite3WhereCodeOneLoopStart() for performance reasons. + */ + notReady = ~(Bitmask)0; ++ if( pWInfo->nLevel>=2 /* Must be a join, or this opt8n is pointless */ ++ && pResultSet!=0 /* Condition (1) */ ++ && 0==(wctrlFlags & (WHERE_AGG_DISTINCT|WHERE_KEEP_ALL_JOINS)) /* (1),(6) */ ++ && OptimizationEnabled(db, SQLITE_OmitNoopJoin) /* (7) */ ++ ){ ++ notReady = whereOmitNoopJoin(pWInfo, notReady); ++ nTabList = pWInfo->nLevel; ++ assert( nTabList>0 ); ++ } ++ ++ /* Check to see if there are any SEARCH loops that might benefit from ++ ** using a Bloom filter. ++ */ + if( pWInfo->nLevel>=2 +- && pResultSet!=0 /* guarantees condition (1) above */ +- && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ++ && OptimizationEnabled(db, SQLITE_BloomFilter) + ){ +- int i; +- Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet); +- if( sWLB.pOrderBy ){ +- tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); +- } +- for(i=pWInfo->nLevel-1; i>=1; i--){ +- WhereTerm *pTerm, *pEnd; +- struct SrcList_item *pItem; +- pLoop = pWInfo->a[i].pWLoop; +- pItem = &pWInfo->pTabList->a[pLoop->iTab]; +- if( (pItem->fg.jointype & JT_LEFT)==0 ) continue; +- if( (wctrlFlags & WHERE_WANT_DISTINCT)==0 +- && (pLoop->wsFlags & WHERE_ONEROW)==0 +- ){ +- continue; +- } +- if( (tabUsed & pLoop->maskSelf)!=0 ) continue; +- pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; +- for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ +- if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin) +- || pTerm->pExpr->iRightJoinTable!=pItem->iCursor +- ){ +- break; +- } +- } +- } +- if( pTerm drop loop %c not used\n", pLoop->cId)); +- notReady &= ~pLoop->maskSelf; +- for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ +- pTerm->wtFlags |= TERM_CODED; +- } +- } +- if( i!=pWInfo->nLevel-1 ){ +- int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); +- memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); +- } +- pWInfo->nLevel--; +- nTabList--; +- } ++ whereCheckIfBloomFilterIsUseful(pWInfo); + } ++ + #if defined(WHERETRACE_ENABLED) +- if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ ++ if( sqlite3WhereTrace & 0x4000 ){ /* Display all terms of the WHERE clause */ + sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n"); + sqlite3WhereClausePrint(sWLB.pWC); + } +- WHERETRACE(0xffff,("*** Optimizer Finished ***\n")); ++ WHERETRACE(0xffffffff,("*** Optimizer Finished ***\n")); + #endif + pWInfo->pParse->nQueryLoop += pWInfo->nRowOut; + +@@ -150683,14 +171521,15 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ + int wsFlags = pWInfo->a[0].pWLoop->wsFlags; + int bOnerow = (wsFlags & WHERE_ONEROW)!=0; +- assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab) ); ++ assert( !(wsFlags&WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pSTab) ); + if( bOnerow || ( + 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) +- && !IsVirtual(pTabList->a[0].pTab) ++ && !IsVirtual(pTabList->a[0].pSTab) + && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) ++ && OptimizationEnabled(db, SQLITE_OnePass) + )){ + pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; +- if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){ ++ if( HasRowid(pTabList->a[0].pSTab) && (wsFlags & WHERE_IDX_ONLY) ){ + if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){ + bFordelete = OPFLAG_FORDELETE; + } +@@ -150705,13 +171544,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + for(ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom]; +- pTab = pTabItem->pTab; ++ pTab = pTabItem->pSTab; + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + pLoop = pLevel->pWLoop; +- if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ ++ if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){ + /* Do nothing */ + }else + #ifndef SQLITE_OMIT_VIRTUALTABLE +@@ -150723,8 +171562,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + /* noop */ + }else + #endif +- if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 +- && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ ++ if( ((pLoop->wsFlags & WHERE_IDX_ONLY)==0 ++ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0) ++ || (pTabItem->fg.jointype & (JT_LTORJ|JT_RIGHT))!=0 ++ ){ + int op = OP_OpenRead; + if( pWInfo->eOnePass!=ONEPASS_OFF ){ + op = OP_OpenWrite; +@@ -150737,6 +171578,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + if( pWInfo->eOnePass==ONEPASS_OFF + && pTab->nColtabFlags & (TF_HasGenerated|TF_WithoutRowid))==0 ++ && (pLoop->wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))==0 + ){ + /* If we know that only a prefix of the record will be used, + ** it is advantageous to reduce the "column count" field in +@@ -150748,7 +171590,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + assert( n<=pTab->nCol ); + } + #ifdef SQLITE_ENABLE_CURSOR_HINTS +- if( pLoop->u.btree.pIndex!=0 ){ ++ if( pLoop->u.btree.pIndex!=0 && (pTab->tabFlags & TF_WithoutRowid)==0 ){ + sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); + }else + #endif +@@ -150776,7 +171618,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + iIndexCur = pLevel->iTabCur; + op = 0; + }else if( pWInfo->eOnePass!=ONEPASS_OFF ){ +- Index *pJ = pTabItem->pTab->pIndex; ++ Index *pJ = pTabItem->pSTab->pIndex; + iIndexCur = iAuxArg; + assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); + while( ALWAYS(pJ) && pJ!=pIx ){ +@@ -150790,8 +171632,17 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + op = OP_ReopenIdx; + }else{ + iIndexCur = pParse->nTab++; ++ if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){ ++ whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem); ++ } ++ if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){ ++ wherePartIdxExpr( ++ pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem ++ ); ++ } + } + pLevel->iIdxCur = iIndexCur; ++ assert( pIx!=0 ); + assert( pIx->pSchema==pTab->pSchema ); + assert( iIndexCur>=0 ); + if( op ){ +@@ -150800,6 +171651,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 + && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 + && (pLoop->wsFlags & WHERE_BIGNULL_SORT)==0 ++ && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 + && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 + && pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED + ){ +@@ -150824,6 +171676,37 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + } + } + if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb); ++ if( (pTabItem->fg.jointype & JT_RIGHT)!=0 ++ && (pLevel->pRJ = sqlite3WhereMalloc(pWInfo, sizeof(WhereRightJoin)))!=0 ++ ){ ++ WhereRightJoin *pRJ = pLevel->pRJ; ++ pRJ->iMatch = pParse->nTab++; ++ pRJ->regBloom = ++pParse->nMem; ++ sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom); ++ pRJ->regReturn = ++pParse->nMem; ++ sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn); ++ assert( pTab==pTabItem->pSTab ); ++ if( HasRowid(pTab) ){ ++ KeyInfo *pInfo; ++ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1); ++ pInfo = sqlite3KeyInfoAlloc(pParse->db, 1, 0); ++ if( pInfo ){ ++ pInfo->aColl[0] = 0; ++ pInfo->aSortFlags[0] = 0; ++ sqlite3VdbeAppendP4(v, pInfo, P4_KEYINFO); ++ } ++ }else{ ++ Index *pPk = sqlite3PrimaryKeyIndex(pTab); ++ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, pPk->nKeyCol); ++ sqlite3VdbeSetP4KeyInfo(pParse, pPk); ++ } ++ pLoop->wsFlags &= ~WHERE_IDX_ONLY; ++ /* The nature of RIGHT JOIN processing is such that it messes up ++ ** the output order. So omit any ORDER BY/GROUP BY elimination ++ ** optimizations. We need to do an actual sort for RIGHT JOIN. */ ++ pWInfo->nOBSat = 0; ++ pWInfo->eDistinct = WHERE_DISTINCT_UNORDERED; ++ } + } + pWInfo->iTop = sqlite3VdbeCurrentAddr(v); + if( db->mallocFailed ) goto whereBeginError; +@@ -150835,15 +171718,36 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + for(ii=0; iinErr ) goto whereBeginError; + pLevel = &pWInfo->a[ii]; + wsFlags = pLevel->pWLoop->wsFlags; ++ pSrc = &pTabList->a[pLevel->iFrom]; ++ if( pSrc->fg.isMaterialized ){ ++ Subquery *pSubq; ++ int iOnce = 0; ++ assert( pSrc->fg.isSubquery ); ++ pSubq = pSrc->u4.pSubq; ++ if( pSrc->fg.isCorrelated==0 ){ ++ iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); ++ }else{ ++ iOnce = 0; ++ } ++ sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub); ++ VdbeComment((v, "materialize %!S", pSrc)); ++ if( iOnce ) sqlite3VdbeJumpHere(v, iOnce); ++ } ++ assert( pTabList == pWInfo->pTabList ); ++ if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ ++ if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ + #ifndef SQLITE_OMIT_AUTOMATIC_INDEX +- if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){ +- constructAutomaticIndex(pParse, &pWInfo->sWC, +- &pTabList->a[pLevel->iFrom], notReady, pLevel); ++ constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel); ++#endif ++ }else{ ++ sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); ++ } + if( db->mallocFailed ) goto whereBeginError; + } +-#endif + addrExplain = sqlite3WhereExplainOneScan( + pParse, pTabList, pLevel, wctrlFlags + ); +@@ -150857,6 +171761,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( + + /* Done. */ + VdbeModuleComment((v, "Begin WHERE-core")); ++ pWInfo->iEndWhere = sqlite3VdbeCurrentAddr(v); + return pWInfo; + + /* Jump here if malloc fails */ +@@ -150865,6 +171770,11 @@ whereBeginError: + pParse->nQueryLoop = pWInfo->savedNQueryLoop; + whereInfoFree(db, pWInfo); + } ++#ifdef WHERETRACE_ENABLED ++ /* Prevent harmless compiler warnings about debugging routines ++ ** being declared but never used */ ++ sqlite3ShowWhereLoopList(0); ++#endif /* WHERETRACE_ENABLED */ + return 0; + } + +@@ -150885,6 +171795,7 @@ whereBeginError: + ){ + if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; + sqlite3VdbePrintOp(0, pc, pOp); ++ sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */ + } + #endif + +@@ -150900,6 +171811,8 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + WhereLoop *pLoop; + SrcList *pTabList = pWInfo->pTabList; + sqlite3 *db = pParse->db; ++ int iEnd = sqlite3VdbeCurrentAddr(v); ++ int nRJ = 0; + + /* Generate loop termination code. + */ +@@ -150907,6 +171820,17 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + for(i=pWInfo->nLevel-1; i>=0; i--){ + int addr; + pLevel = &pWInfo->a[i]; ++ if( pLevel->pRJ ){ ++ /* Terminate the subroutine that forms the interior of the loop of ++ ** the RIGHT JOIN table */ ++ WhereRightJoin *pRJ = pLevel->pRJ; ++ sqlite3VdbeResolveLabel(v, pLevel->addrCont); ++ pLevel->addrCont = 0; ++ pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v); ++ sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1); ++ VdbeCoverage(v); ++ nRJ++; ++ } + pLoop = pLevel->pWLoop; + if( pLevel->op!=OP_Noop ){ + #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT +@@ -150934,7 +171858,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + } + #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ + /* The common case: Advance to the next row */ +- sqlite3VdbeResolveLabel(v, pLevel->addrCont); ++ if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont); + sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); + sqlite3VdbeChangeP5(v, pLevel->p5); + VdbeCoverage(v); +@@ -150949,18 +171873,22 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT + if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek); + #endif +- }else{ ++ }else if( pLevel->addrCont ){ + sqlite3VdbeResolveLabel(v, pLevel->addrCont); + } +- if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ ++ if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){ + struct InLoop *pIn; + int j; + sqlite3VdbeResolveLabel(v, pLevel->addrNxt); + for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ ++ assert( sqlite3VdbeGetOp(v, pIn->addrInTop+1)->opcode==OP_IsNull ++ || pParse->db->mallocFailed ); + sqlite3VdbeJumpHere(v, pIn->addrInTop+1); + if( pIn->eEndLoopOp!=OP_Noop ){ + if( pIn->nPrefix ){ +- assert( pLoop->wsFlags & WHERE_IN_EARLYOUT ); ++ int bEarlyOut = ++ (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ++ && (pLoop->wsFlags & WHERE_IN_EARLYOUT)!=0; + if( pLevel->iLeftJoin ){ + /* For LEFT JOIN queries, cursor pIn->iCur may not have been + ** opened yet. This occurs for WHERE clauses such as +@@ -150971,16 +171899,19 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + ** jump over the OP_Next or OP_Prev instruction about to + ** be coded. */ + sqlite3VdbeAddOp2(v, OP_IfNotOpen, pIn->iCur, +- sqlite3VdbeCurrentAddr(v) + 2 + +- ((pLoop->wsFlags & WHERE_VIRTUALTABLE)==0) +- ); ++ sqlite3VdbeCurrentAddr(v) + 2 + bEarlyOut); + VdbeCoverage(v); + } +- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){ ++ if( bEarlyOut ){ + sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur, + sqlite3VdbeCurrentAddr(v)+2, + pIn->iBase, pIn->nPrefix); + VdbeCoverage(v); ++ /* Retarget the OP_IsNull against the left operand of IN so ++ ** it jumps past the OP_IfNoHope. This is because the ++ ** OP_IsNull also bypasses the OP_Affinity opcode that is ++ ** required by OP_IfNoHope. */ ++ sqlite3VdbeJumpHere(v, pIn->addrInTop+1); + } + } + sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); +@@ -150992,6 +171923,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + } + } + sqlite3VdbeResolveLabel(v, pLevel->addrBrk); ++ if( pLevel->pRJ ){ ++ sqlite3VdbeAddOp3(v, OP_Return, pLevel->pRJ->regReturn, 0, 1); ++ VdbeCoverage(v); ++ } + if( pLevel->addrSkip ){ + sqlite3VdbeGoto(v, pLevel->addrSkip); + VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName)); +@@ -151010,12 +171945,27 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); + assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); + if( (ws & WHERE_IDX_ONLY)==0 ){ +- assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor ); ++ SrcItem *pSrc = &pTabList->a[pLevel->iFrom]; ++ assert( pLevel->iTabCur==pSrc->iCursor ); ++ if( pSrc->fg.viaCoroutine ){ ++ int m, n; ++ assert( pSrc->fg.isSubquery ); ++ n = pSrc->u4.pSubq->regResult; ++ assert( pSrc->pSTab!=0 ); ++ m = pSrc->pSTab->nCol; ++ sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); ++ } + sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); + } + if( (ws & WHERE_INDEXED) +- || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx) ++ || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) + ){ ++ if( ws & WHERE_MULTI_OR ){ ++ Index *pIx = pLevel->u.pCoveringIdx; ++ int iDb = sqlite3SchemaToIndex(db, pIx->pSchema); ++ sqlite3VdbeAddOp3(v, OP_ReopenIdx, pLevel->iIdxCur, pIx->tnum, iDb); ++ sqlite3VdbeSetP4KeyInfo(pParse, pIx); ++ } + sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); + } + if( pLevel->op==OP_Return ){ +@@ -151026,58 +171976,41 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + sqlite3VdbeJumpHere(v, addr); + } + VdbeModuleComment((v, "End WHERE-loop%d: %s", i, +- pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); ++ pWInfo->pTabList->a[pLevel->iFrom].pSTab->zName)); + } + +- /* The "break" point is here, just past the end of the outer loop. +- ** Set it. +- */ +- sqlite3VdbeResolveLabel(v, pWInfo->iBreak); +- + assert( pWInfo->nLevel<=pTabList->nSrc ); + for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ + int k, last; +- VdbeOp *pOp; ++ VdbeOp *pOp, *pLastOp; + Index *pIdx = 0; +- struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; +- Table *pTab = pTabItem->pTab; ++ SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; ++ Table *pTab = pTabItem->pSTab; + assert( pTab!=0 ); + pLoop = pLevel->pWLoop; + ++ /* Do RIGHT JOIN processing. Generate code that will output the ++ ** unmatched rows of the right operand of the RIGHT JOIN with ++ ** all of the columns of the left operand set to NULL. ++ */ ++ if( pLevel->pRJ ){ ++ sqlite3WhereRightJoinLoop(pWInfo, i, pLevel); ++ continue; ++ } ++ + /* For a co-routine, change all OP_Column references to the table of + ** the co-routine into OP_Copy of result contained in a register. + ** OP_Rowid becomes OP_Null. + */ + if( pTabItem->fg.viaCoroutine ){ + testcase( pParse->db->mallocFailed ); ++ assert( pTabItem->fg.isSubquery ); ++ assert( pTabItem->u4.pSubq->regResult>=0 ); + translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, +- pTabItem->regResult, 0); ++ pTabItem->u4.pSubq->regResult, 0); + continue; + } + +-#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE +- /* Close all of the cursors that were opened by sqlite3WhereBegin. +- ** Except, do not close cursors that will be reused by the OR optimization +- ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors +- ** created for the ONEPASS optimization. +- */ +- if( (pTab->tabFlags & TF_Ephemeral)==0 +- && pTab->pSelect==0 +- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 +- ){ +- int ws = pLoop->wsFlags; +- if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){ +- sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); +- } +- if( (ws & WHERE_INDEXED)!=0 +- && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 +- && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] +- ){ +- sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); +- } +- } +-#endif +- + /* If this scan uses an index, make VDBE code substitutions to read data + ** from the index instead of from the table where possible. In some cases + ** this optimization prevents the table from ever being read, which can +@@ -151092,29 +172025,63 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){ + pIdx = pLoop->u.btree.pIndex; + }else if( pLoop->wsFlags & WHERE_MULTI_OR ){ +- pIdx = pLevel->u.pCovidx; ++ pIdx = pLevel->u.pCoveringIdx; + } + if( pIdx +- && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable)) + && !db->mallocFailed + ){ +- last = sqlite3VdbeCurrentAddr(v); +- k = pLevel->addrBody; ++ if( pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable) ){ ++ last = iEnd; ++ }else{ ++ last = pWInfo->iEndWhere; ++ } ++ if( pIdx->bHasExpr ){ ++ IndexedExpr *p = pParse->pIdxEpr; ++ while( p ){ ++ if( p->iIdxCur==pLevel->iIdxCur ){ ++#ifdef WHERETRACE_ENABLED ++ if( sqlite3WhereTrace & 0x200 ){ ++ sqlite3DebugPrintf("Disable pParse->pIdxEpr term {%d,%d}\n", ++ p->iIdxCur, p->iIdxCol); ++ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(p->pExpr); ++ } ++#endif ++ p->iDataCur = -1; ++ p->iIdxCur = -1; ++ } ++ p = p->pIENext; ++ } ++ } ++ k = pLevel->addrBody + 1; + #ifdef SQLITE_DEBUG + if( db->flags & SQLITE_VdbeAddopTrace ){ +- printf("TRANSLATE opcodes in range %d..%d\n", k, last-1); ++ printf("TRANSLATE cursor %d->%d in opcode range %d..%d\n", ++ pLevel->iTabCur, pLevel->iIdxCur, k, last-1); + } ++ /* Proof that the "+1" on the k value above is safe */ ++ pOp = sqlite3VdbeGetOp(v, k - 1); ++ assert( pOp->opcode!=OP_Column || pOp->p1!=pLevel->iTabCur ); ++ assert( pOp->opcode!=OP_Rowid || pOp->p1!=pLevel->iTabCur ); ++ assert( pOp->opcode!=OP_IfNullRow || pOp->p1!=pLevel->iTabCur ); + #endif + pOp = sqlite3VdbeGetOp(v, k); +- for(; kp1!=pLevel->iTabCur ) continue; +- if( pOp->opcode==OP_Column ++ pLastOp = pOp + (last - k); ++ assert( pOp<=pLastOp ); ++ do{ ++ if( pOp->p1!=pLevel->iTabCur ){ ++ /* no-op */ ++ }else if( pOp->opcode==OP_Column + #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC + || pOp->opcode==OP_Offset + #endif + ){ + int x = pOp->p2; + assert( pIdx->pTable==pTab ); ++#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC ++ if( pOp->opcode==OP_Offset ){ ++ /* Do not need to translate the column number */ ++ }else ++#endif + if( !HasRowid(pTab) ){ + Index *pPk = sqlite3PrimaryKeyIndex(pTab); + x = pPk->aiColumn[x]; +@@ -151128,9 +172095,30 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + pOp->p2 = x; + pOp->p1 = pLevel->iIdxCur; + OpcodeRewriteTrace(db, k, pOp); ++ }else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ ++ if( pLoop->wsFlags & WHERE_IDX_ONLY ){ ++ /* An error. pLoop is supposed to be a covering index loop, ++ ** and yet the VM code refers to a column of the table that ++ ** is not part of the index. */ ++ sqlite3ErrorMsg(pParse, "internal query planner error"); ++ pParse->rc = SQLITE_INTERNAL; ++ }else{ ++ /* The WHERE_EXPRIDX flag is set by the planner when it is likely ++ ** that pLoop is a covering index loop, but it is not possible ++ ** to be 100% sure. In this case, any OP_Explain opcode ++ ** corresponding to this loop describes the index as a "COVERING ++ ** INDEX". But, pOp proves that pLoop is not actually a covering ++ ** index loop. So clear the WHERE_EXPRIDX flag and rewrite the ++ ** text that accompanies the OP_Explain opcode, if any. */ ++ pLoop->wsFlags &= ~WHERE_EXPRIDX; ++ sqlite3WhereAddExplainText(pParse, ++ pLevel->addrBody-1, ++ pTabList, ++ pLevel, ++ pWInfo->wctrlFlags ++ ); ++ } + } +- assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 +- || pWInfo->eOnePass ); + }else if( pOp->opcode==OP_Rowid ){ + pOp->p1 = pLevel->iIdxCur; + pOp->opcode = OP_IdxRowid; +@@ -151139,25 +172127,26 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + pOp->p1 = pLevel->iIdxCur; + OpcodeRewriteTrace(db, k, pOp); + } +- } ++#ifdef SQLITE_DEBUG ++ k++; ++#endif ++ }while( (++pOp)flags & SQLITE_VdbeAddopTrace ) printf("TRANSLATE complete\n"); + #endif + } + } + +- /* Undo all Expr node modifications */ +- while( pWInfo->pExprMods ){ +- WhereExprMod *p = pWInfo->pExprMods; +- pWInfo->pExprMods = p->pNext; +- memcpy(p->pExpr, &p->orig, sizeof(p->orig)); +- sqlite3DbFree(db, p); +- } ++ /* The "break" point is here, just past the end of the outer loop. ++ ** Set it. ++ */ ++ sqlite3VdbeResolveLabel(v, pWInfo->iBreak); + + /* Final cleanup + */ + pParse->nQueryLoop = pWInfo->savedNQueryLoop; + whereInfoFree(db, pWInfo); ++ pParse->withinRJSubrtn -= nRJ; + return; + } + +@@ -151282,7 +172271,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ + ** + ** These are the same built-in window functions supported by Postgres. + ** Although the behaviour of aggregate window functions (functions that +-** can be used as either aggregates or window funtions) allows them to ++** can be used as either aggregates or window functions) allows them to + ** be implemented using an API, built-in window functions are much more + ** esoteric. Additionally, some window functions (e.g. nth_value()) + ** may only be implemented by caching the entire partition in memory. +@@ -151746,7 +172735,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ } + /* Window functions that use all window interfaces: xStep, xFinal, + ** xValue, and xInverse */ + #define WINDOWFUNCALL(name,nArg,extra) { \ +- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ ++ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + name ## StepFunc, name ## FinalizeFunc, name ## ValueFunc, \ + name ## InvFunc, name ## Name, {0} \ + } +@@ -151754,7 +172743,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ } + /* Window functions that are implemented using bytecode and thus have + ** no-op routines for their methods */ + #define WINDOWFUNCNOOP(name,nArg,extra) { \ +- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ ++ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + noopStepFunc, noopValueFunc, noopValueFunc, \ + noopStepFunc, name ## Name, {0} \ + } +@@ -151763,7 +172752,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ } + ** same routine for xFinalize and xValue and which never call + ** xInverse. */ + #define WINDOWFUNCX(name,nArg,extra) { \ +- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ ++ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ + name ## StepFunc, name ## ValueFunc, name ## ValueFunc, \ + noopStepFunc, name ## Name, {0} \ + } +@@ -151812,7 +172801,7 @@ static Window *windowFind(Parse *pParse, Window *pList, const char *zName){ + ** is the Window object representing the associated OVER clause. This + ** function updates the contents of pWin as follows: + ** +-** * If the OVER clause refered to a named window (as in "max(x) OVER win"), ++** * If the OVER clause referred to a named window (as in "max(x) OVER win"), + ** search list pList for a matching WINDOW definition, and update pWin + ** accordingly. If no such WINDOW clause can be found, leave an error + ** in pParse. +@@ -151889,7 +172878,7 @@ SQLITE_PRIVATE void sqlite3WindowUpdate( + } + } + } +- pWin->pFunc = pFunc; ++ pWin->pWFunc = pFunc; + } + + /* +@@ -151950,9 +172939,11 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ + } + /* no break */ deliberate_fall_through + ++ case TK_IF_NULL_ROW: + case TK_AGG_FUNCTION: + case TK_COLUMN: { + int iCol = -1; ++ if( pParse->db->mallocFailed ) return WRC_Abort; + if( p->pSub ){ + int i; + for(i=0; ipSub->nExpr; i++){ +@@ -152062,22 +173053,24 @@ static ExprList *exprListAppendList( + int i; + int nInit = pList ? pList->nExpr : 0; + for(i=0; inExpr; i++){ +- Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); +- assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); +- if( bIntToNull && pDup ){ ++ sqlite3 *db = pParse->db; ++ Expr *pDup = sqlite3ExprDup(db, pAppend->a[i].pExpr, 0); ++ if( db->mallocFailed ){ ++ sqlite3ExprDelete(db, pDup); ++ break; ++ } ++ if( bIntToNull ){ + int iDummy; + Expr *pSub; +- for(pSub=pDup; ExprHasProperty(pSub, EP_Skip); pSub=pSub->pLeft){ +- assert( pSub ); +- } +- if( sqlite3ExprIsInteger(pSub, &iDummy) ){ ++ pSub = sqlite3ExprSkipCollateAndLikely(pDup); ++ if( sqlite3ExprIsInteger(pSub, &iDummy, 0) ){ + pSub->op = TK_NULL; + pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); + pSub->u.zToken = 0; + } + } + pList = sqlite3ExprListAppend(pParse, pList, pDup); +- if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; ++ if( pList ) pList->a[nInit+i].fg.sortFlags = pAppend->a[i].fg.sortFlags; + } + } + return pList; +@@ -152100,6 +173093,15 @@ static int sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){ + return WRC_Continue; + } + ++static int disallowAggregatesInOrderByCb(Walker *pWalker, Expr *pExpr){ ++ if( pExpr->op==TK_AGG_FUNCTION && pExpr->pAggInfo==0 ){ ++ assert( !ExprHasProperty(pExpr, EP_IntValue) ); ++ sqlite3ErrorMsg(pWalker->pParse, ++ "misuse of aggregate: %s()", pExpr->u.zToken); ++ } ++ return WRC_Continue; ++} ++ + /* + ** If the SELECT statement passed as the second argument does not invoke + ** any SQL window functions, this function is a no-op. Otherwise, it +@@ -152109,7 +173111,11 @@ static int sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){ + */ + SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ + int rc = SQLITE_OK; +- if( p->pWin && p->pPrior==0 && (p->selFlags & SF_WinRewrite)==0 ){ ++ if( p->pWin ++ && p->pPrior==0 ++ && ALWAYS((p->selFlags & SF_WinRewrite)==0) ++ && ALWAYS(!IN_RENAME_OBJECT) ++ ){ + Vdbe *v = sqlite3GetVdbe(pParse); + sqlite3 *db = pParse->db; + Select *pSub = 0; /* The subquery */ +@@ -152133,12 +173139,17 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ + } + sqlite3AggInfoPersistWalkerInit(&w, pParse); + sqlite3WalkSelect(&w, p); ++ if( (p->selFlags & SF_Aggregate)==0 ){ ++ w.xExprCallback = disallowAggregatesInOrderByCb; ++ w.xSelectCallback = 0; ++ sqlite3WalkExprList(&w, p->pOrderBy); ++ } + + p->pSrc = 0; + p->pWhere = 0; + p->pGroupBy = 0; + p->pHaving = 0; +- p->selFlags &= ~SF_Aggregate; ++ p->selFlags &= ~(u32)SF_Aggregate; + p->selFlags |= SF_WinRewrite; + + /* Create the ORDER BY clause for the sub-select. This is the concatenation +@@ -152177,8 +173188,11 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ + ** window function - one for the accumulator, another for interim + ** results. */ + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ +- ExprList *pArgs = pWin->pOwner->x.pList; +- if( pWin->pFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ ++ ExprList *pArgs; ++ assert( ExprUseXList(pWin->pOwner) ); ++ assert( pWin->pWFunc!=0 ); ++ pArgs = pWin->pOwner->x.pList; ++ if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){ + selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); + pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); + pWin->bExprArgs = 1; +@@ -152210,15 +173224,20 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ + pSub = sqlite3SelectNew( + pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0 + ); +- SELECTTRACE(1,pParse,pSub, ++ TREETRACE(0x40,pParse,pSub, + ("New window-function subquery in FROM clause of (%u/%p)\n", + p->selId, p)); + p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); +- if( p->pSrc ){ ++ assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside ++ ** of sqlite3DbMallocRawNN() called from ++ ** sqlite3SrcListAppend() */ ++ if( p->pSrc==0 ){ ++ sqlite3SelectDelete(db, pSub); ++ }else if( sqlite3SrcItemAttachSubquery(pParse, &p->pSrc->a[0], pSub, 0) ){ + Table *pTab2; +- p->pSrc->a[0].pSelect = pSub; ++ p->pSrc->a[0].fg.isCorrelated = 1; + sqlite3SrcListAssignCursors(pParse, p->pSrc); +- pSub->selFlags |= SF_Expanded; ++ pSub->selFlags |= SF_Expanded|SF_OrderByReqd; + pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE); + pSub->selFlags |= (selFlags & SF_Aggregate); + if( pTab2==0 ){ +@@ -152229,7 +173248,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ + }else{ + memcpy(pTab, pTab2, sizeof(Table)); + pTab->tabFlags |= TF_Ephemeral; +- p->pSrc->a[0].pTab = pTab; ++ p->pSrc->a[0].pSTab = pTab; + pTab = pTab2; + memset(&w, 0, sizeof(w)); + w.xExprCallback = sqlite3WindowExtraAggFuncDepth; +@@ -152237,19 +173256,16 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ + w.xSelectCallback2 = sqlite3WalkerDepthDecrease; + sqlite3WalkSelect(&w, pSub); + } +- }else{ +- sqlite3SelectDelete(db, pSub); + } + if( db->mallocFailed ) rc = SQLITE_NOMEM; +- sqlite3DbFree(db, pTab); +- } + +- if( rc ){ +- if( pParse->nErr==0 ){ +- assert( pParse->db->mallocFailed ); +- sqlite3ErrorToParser(pParse->db, SQLITE_NOMEM); +- } ++ /* Defer deleting the temporary table pTab because if an error occurred, ++ ** there could still be references to that table embedded in the ++ ** result-set or ORDER BY clause of the SELECT statement p. */ ++ sqlite3ParserAddCleanup(pParse, sqlite3DbFree, pTab); + } ++ ++ assert( rc==SQLITE_OK || pParse->nErr!=0 ); + return rc; + } + +@@ -152301,7 +173317,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){ + ** variable values in the expression tree. + */ + static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ +- if( 0==sqlite3ExprIsConstant(pExpr) ){ ++ if( 0==sqlite3ExprIsConstant(0,pExpr) ){ + if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr); + sqlite3ExprDelete(pParse->db, pExpr); + pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0); +@@ -152405,7 +173421,7 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble( + } + + /* +-** Window *pWin has just been created from a WINDOW clause. Tokne pBase ++** Window *pWin has just been created from a WINDOW clause. Token pBase + ** is the base window. Earlier windows from the same WINDOW clause are + ** stored in the linked list starting at pWin->pNextWin. This function + ** either updates *pWin according to the base specification, or else +@@ -152449,8 +173465,9 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ + if( p ){ + assert( p->op==TK_FUNCTION ); + assert( pWin ); ++ assert( ExprIsFullSize(p) ); + p->y.pWin = pWin; +- ExprSetProperty(p, EP_WinFunc); ++ ExprSetProperty(p, EP_WinFunc|EP_FullSize); + pWin->pOwner = p; + if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){ + sqlite3ErrorMsg(pParse, +@@ -152469,15 +173486,19 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ + ** SELECT, or (b) the windows already linked use a compatible window frame. + */ + SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){ +- if( pSel!=0 +- && (0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0)) +- ){ +- pWin->pNextWin = pSel->pWin; +- if( pSel->pWin ){ +- pSel->pWin->ppThis = &pWin->pNextWin; ++ if( pSel ){ ++ if( 0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){ ++ pWin->pNextWin = pSel->pWin; ++ if( pSel->pWin ){ ++ pSel->pWin->ppThis = &pWin->pNextWin; ++ } ++ pSel->pWin = pWin; ++ pWin->ppThis = &pSel->pWin; ++ }else{ ++ if( sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){ ++ pSel->selFlags |= SF_MultiPart; ++ } + } +- pSel->pWin = pWin; +- pWin->ppThis = &pSel->pWin; + } + } + +@@ -152486,7 +173507,12 @@ SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){ + ** different, or 2 if it cannot be determined if the objects are identical + ** or not. Identical window objects can be processed in a single scan. + */ +-SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, int bFilter){ ++SQLITE_PRIVATE int sqlite3WindowCompare( ++ const Parse *pParse, ++ const Window *p1, ++ const Window *p2, ++ int bFilter ++){ + int res; + if( NEVER(p1==0) || NEVER(p2==0) ) return 1; + if( p1->eFrmType!=p2->eFrmType ) return 1; +@@ -152516,10 +173542,15 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, i + ** and initialize registers and cursors used by sqlite3WindowCodeStep(). + */ + SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ +- int nEphExpr = pSelect->pSrc->a[0].pSelect->pEList->nExpr; +- Window *pMWin = pSelect->pWin; + Window *pWin; +- Vdbe *v = sqlite3GetVdbe(pParse); ++ int nEphExpr; ++ Window *pMWin; ++ Vdbe *v; ++ ++ assert( pSelect->pSrc->a[0].fg.isSubquery ); ++ nEphExpr = pSelect->pSrc->a[0].u4.pSubq->pSelect->pEList->nExpr; ++ pMWin = pSelect->pWin; ++ v = sqlite3GetVdbe(pParse); + + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, nEphExpr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr); +@@ -152549,7 +173580,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ + } + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ +- FuncDef *p = pWin->pFunc; ++ FuncDef *p = pWin->pWFunc; + if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){ + /* The inline versions of min() and max() require a single ephemeral + ** table and 3 registers. The registers are used as follows: +@@ -152558,12 +173589,15 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ + ** regApp+1: integer value used to ensure keys are unique + ** regApp+2: output of MakeRecord + */ +- ExprList *pList = pWin->pOwner->x.pList; +- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); ++ ExprList *pList; ++ KeyInfo *pKeyInfo; ++ assert( ExprUseXList(pWin->pOwner) ); ++ pList = pWin->pOwner->x.pList; ++ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); + pWin->csrApp = pParse->nTab++; + pWin->regApp = pParse->nMem+1; + pParse->nMem += 3; +- if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){ ++ if( pKeyInfo && pWin->pWFunc->zName[1]=='i' ){ + assert( pKeyInfo->aSortFlags[0]==0 ); + pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC; + } +@@ -152630,6 +173664,7 @@ static void windowCheckValue(Parse *pParse, int reg, int eCond){ + VdbeCoverageIf(v, eCond==2); + } + sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg); ++ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC); + VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */ + VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */ + VdbeCoverageNeverNullIf(v, eCond==2); +@@ -152646,7 +173681,9 @@ static void windowCheckValue(Parse *pParse, int reg, int eCond){ + ** with the object passed as the only argument to this function. + */ + static int windowArgCount(Window *pWin){ +- ExprList *pList = pWin->pOwner->x.pList; ++ const ExprList *pList; ++ assert( ExprUseXList(pWin->pOwner) ); ++ pList = pWin->pOwner->x.pList; + return (pList ? pList->nExpr : 0); + } + +@@ -152696,7 +173733,7 @@ struct WindowCsrAndReg { + ** + ** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING) + ** +-** The windows functions implmentation caches the input rows in a temp ++** The windows functions implementation caches the input rows in a temp + ** table, sorted by "a, b" (it actually populates the cache lazily, and + ** aggressively removes rows once they are no longer required, but that's + ** a mere detail). It keeps three cursors open on the temp table. One +@@ -152724,6 +173761,7 @@ struct WindowCodeArg { + int regGosub; /* Register used with OP_Gosub(addrGosub) */ + int regArg; /* First in array of accumulator registers */ + int eDelete; /* See above */ ++ int regRowid; + + WindowCsrAndReg start; + WindowCsrAndReg current; +@@ -152782,10 +173820,11 @@ static void windowAggStep( + Vdbe *v = sqlite3GetVdbe(pParse); + Window *pWin; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ +- FuncDef *pFunc = pWin->pFunc; ++ FuncDef *pFunc = pWin->pWFunc; + int regArg; + int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); + int i; ++ int addrIf = 0; + + assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED ); + +@@ -152802,6 +173841,18 @@ static void windowAggStep( + } + regArg = reg; + ++ if( pWin->pFilter ){ ++ int regTmp; ++ assert( ExprUseXList(pWin->pOwner) ); ++ assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); ++ assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); ++ regTmp = sqlite3GetTempReg(pParse); ++ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); ++ addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); ++ VdbeCoverage(v); ++ sqlite3ReleaseTempReg(pParse, regTmp); ++ } ++ + if( pMWin->regStartRowid==0 + && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) +@@ -152821,35 +173872,25 @@ static void windowAggStep( + } + sqlite3VdbeJumpHere(v, addrIsNull); + }else if( pWin->regApp ){ ++ assert( pWin->pFilter==0 ); + assert( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName + ); + assert( bInverse==0 || bInverse==1 ); + sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); + }else if( pFunc->xSFunc!=noopStepFunc ){ +- int addrIf = 0; +- if( pWin->pFilter ){ +- int regTmp; +- assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); +- assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); +- regTmp = sqlite3GetTempReg(pParse); +- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); +- addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); +- VdbeCoverage(v); +- sqlite3ReleaseTempReg(pParse, regTmp); +- } +- + if( pWin->bExprArgs ){ +- int iStart = sqlite3VdbeCurrentAddr(v); +- VdbeOp *pOp, *pEnd; ++ int iOp = sqlite3VdbeCurrentAddr(v); ++ int iEnd; + ++ assert( ExprUseXList(pWin->pOwner) ); + nArg = pWin->pOwner->x.pList->nExpr; + regArg = sqlite3GetTempRange(pParse, nArg); + sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0); + +- pEnd = sqlite3VdbeGetOp(v, -1); +- for(pOp=sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){ +- if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){ ++ for(iEnd=sqlite3VdbeCurrentAddr(v); iOpopcode==OP_Column && pOp->p1==pMWin->iEphCsr ){ + pOp->p1 = csr; + } + } +@@ -152857,18 +173898,20 @@ static void windowAggStep( + if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ + CollSeq *pColl; + assert( nArg>0 ); ++ assert( ExprUseXList(pWin->pOwner) ); + pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); + sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); + } + sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, + bInverse, regArg, pWin->regAccum); + sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); +- sqlite3VdbeChangeP5(v, (u8)nArg); ++ sqlite3VdbeChangeP5(v, (u16)nArg); + if( pWin->bExprArgs ){ + sqlite3ReleaseTempRange(pParse, regArg, nArg); + } +- if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); + } ++ ++ if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); + } + } + +@@ -152893,7 +173936,7 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + if( pMWin->regStartRowid==0 +- && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) ++ && (pWin->pWFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) + ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); +@@ -152907,12 +173950,12 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ + int nArg = windowArgCount(pWin); + if( bFin ){ + sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg); +- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); ++ sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); + sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + }else{ + sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult); +- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); ++ sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); + } + } + } +@@ -153041,7 +174084,8 @@ static void windowReturnOneRow(WindowCodeArg *p){ + Window *pWin; + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ +- FuncDef *pFunc = pWin->pFunc; ++ FuncDef *pFunc = pWin->pWFunc; ++ assert( ExprUseXList(pWin->pOwner) ); + if( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName + ){ +@@ -153112,7 +174156,7 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ + int nArg = 0; + Window *pWin; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ +- FuncDef *pFunc = pWin->pFunc; ++ FuncDef *pFunc = pWin->pWFunc; + assert( pWin->regAccum ); + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + nArg = MAX(nArg, windowArgCount(pWin)); +@@ -153142,7 +174186,7 @@ static int windowCacheFrame(Window *pMWin){ + Window *pWin; + if( pMWin->regStartRowid ) return 1; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ +- FuncDef *pFunc = pWin->pFunc; ++ FuncDef *pFunc = pWin->pWFunc; + if( (pFunc->zName==nth_valueName) + || (pFunc->zName==first_valueName) + || (pFunc->zName==leadName) +@@ -153207,7 +174251,7 @@ static void windowIfNewPeer( + ** if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl; + ** + ** A special type of arithmetic is used such that if csr1.peerVal is not +-** a numeric type (real or integer), then the result of the addition addition ++** a numeric type (real or integer), then the result of the addition + ** or subtraction is a a copy of csr1.peerVal. + */ + static void windowCodeRangeTest( +@@ -153226,10 +174270,16 @@ static void windowCodeRangeTest( + int regString = ++pParse->nMem; /* Reg. for constant value '' */ + int arith = OP_Add; /* OP_Add or OP_Subtract */ + int addrGe; /* Jump destination */ ++ int addrDone = sqlite3VdbeMakeLabel(pParse); /* Address past OP_Ge */ ++ CollSeq *pColl; ++ ++ /* Read the peer-value from each cursor into a register */ ++ windowReadPeerValues(p, csr1, reg1); ++ windowReadPeerValues(p, csr2, reg2); + + assert( op==OP_Ge || op==OP_Gt || op==OP_Le ); + assert( pOrderBy && pOrderBy->nExpr==1 ); +- if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){ ++ if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_DESC ){ + switch( op ){ + case OP_Ge: op = OP_Le; break; + case OP_Gt: op = OP_Lt; break; +@@ -153238,34 +174288,11 @@ static void windowCodeRangeTest( + arith = OP_Subtract; + } + +- /* Read the peer-value from each cursor into a register */ +- windowReadPeerValues(p, csr1, reg1); +- windowReadPeerValues(p, csr2, reg2); +- + VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl", + reg1, (arith==OP_Add ? "+" : "-"), regVal, + ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2 + )); + +- /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). +- ** This block adds (or subtracts for DESC) the numeric value in regVal +- ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), +- ** then leave reg1 as it is. In pseudo-code, this is implemented as: +- ** +- ** if( reg1>='' ) goto addrGe; +- ** reg1 = reg1 +/- regVal +- ** addrGe: +- ** +- ** Since all strings and blobs are greater-than-or-equal-to an empty string, +- ** the add/subtract is skipped for these, as required. If reg1 is a NULL, +- ** then the arithmetic is performed, but since adding or subtracting from +- ** NULL is always NULL anyway, this case is handled as required too. */ +- sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); +- addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); +- VdbeCoverage(v); +- sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); +- sqlite3VdbeJumpHere(v, addrGe); +- + /* If the BIGNULL flag is set for the ORDER BY, then it is required to + ** consider NULL values to be larger than all other values, instead of + ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this +@@ -153285,7 +174312,7 @@ static void windowCodeRangeTest( + ** Additionally, if either reg1 or reg2 are NULL but the jump to lbl is + ** not taken, control jumps over the comparison operator coded below this + ** block. */ +- if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_BIGNULL ){ ++ if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_BIGNULL ){ + /* This block runs if reg1 contains a NULL. */ + int addr = sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v); + switch( op ){ +@@ -153302,21 +174329,45 @@ static void windowCodeRangeTest( + break; + default: assert( op==OP_Lt ); /* no-op */ break; + } +- sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3); ++ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone); + + /* This block runs if reg1 is not NULL, but reg2 is. */ + sqlite3VdbeJumpHere(v, addr); +- sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v); +- if( op==OP_Gt || op==OP_Ge ){ +- sqlite3VdbeChangeP2(v, -1, sqlite3VdbeCurrentAddr(v)+1); +- } ++ sqlite3VdbeAddOp2(v, OP_IsNull, reg2, ++ (op==OP_Gt || op==OP_Ge) ? addrDone : lbl); ++ VdbeCoverage(v); ++ } ++ ++ /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). ++ ** This block adds (or subtracts for DESC) the numeric value in regVal ++ ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), ++ ** then leave reg1 as it is. In pseudo-code, this is implemented as: ++ ** ++ ** if( reg1>='' ) goto addrGe; ++ ** reg1 = reg1 +/- regVal ++ ** addrGe: ++ ** ++ ** Since all strings and blobs are greater-than-or-equal-to an empty string, ++ ** the add/subtract is skipped for these, as required. If reg1 is a NULL, ++ ** then the arithmetic is performed, but since adding or subtracting from ++ ** NULL is always NULL anyway, this case is handled as required too. */ ++ sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); ++ addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); ++ VdbeCoverage(v); ++ if( (op==OP_Ge && arith==OP_Add) || (op==OP_Le && arith==OP_Subtract) ){ ++ sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); + } ++ sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); ++ sqlite3VdbeJumpHere(v, addrGe); + + /* Compare registers reg2 and reg1, taking the jump if required. Note that + ** control skips over this test if the BIGNULL flag is set and either + ** reg1 or reg2 contain a NULL value. */ + sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); ++ pColl = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr); ++ sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); ++ sqlite3VdbeResolveLabel(v, addrDone); + + assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); + testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); +@@ -153392,16 +174443,24 @@ static int windowCodeOp( + /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING) or + ** (RANGE BETWEEN b PRECEDING AND a PRECEDING) frame, ensure the + ** start cursor does not advance past the end cursor within the +- ** temporary table. It otherwise might, if (a>b). */ ++ ** temporary table. It otherwise might, if (a>b). Also ensure that, ++ ** if the input cursor is still finding new rows, that the end ++ ** cursor does not go past it to EOF. */ + if( pMWin->eStart==pMWin->eEnd && regCountdown +- && pMWin->eFrmType==TK_RANGE && op==WINDOW_AGGINVERSE ++ && pMWin->eFrmType==TK_RANGE + ){ + int regRowid1 = sqlite3GetTempReg(pParse); + int regRowid2 = sqlite3GetTempReg(pParse); +- sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); +- sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); +- sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); +- VdbeCoverage(v); ++ if( op==WINDOW_AGGINVERSE ){ ++ sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); ++ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); ++ sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); ++ VdbeCoverage(v); ++ }else if( p->regRowid ){ ++ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid1); ++ sqlite3VdbeAddOp3(v, OP_Ge, p->regRowid, lblDone, regRowid1); ++ VdbeCoverageNeverNull(v); ++ } + sqlite3ReleaseTempReg(pParse, regRowid1); + sqlite3ReleaseTempReg(pParse, regRowid2); + assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ); +@@ -153484,7 +174543,7 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ + pNew->zName = sqlite3DbStrDup(db, p->zName); + pNew->zBase = sqlite3DbStrDup(db, p->zBase); + pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0); +- pNew->pFunc = p->pFunc; ++ pNew->pWFunc = p->pWFunc; + pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0); + pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0); + pNew->eFrmType = p->eFrmType; +@@ -153685,7 +174744,7 @@ static int windowExprGtZero(Parse *pParse, Expr *pExpr){ + ** + ** For the most part, the patterns above are adapted to support UNBOUNDED by + ** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and +-** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING". ++** CURRENT ROW by assuming that it is equivalent to "0 PRECEDING/FOLLOWING". + ** This is optimized of course - branches that will never be taken and + ** conditions that are always true are omitted from the VM code. The only + ** exceptional case is: +@@ -153890,7 +174949,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + Vdbe *v = sqlite3GetVdbe(pParse); + int csrWrite; /* Cursor used to write to eph. table */ + int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ +- int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ ++ int nInput = p->pSrc->a[0].pSTab->nCol; /* Number of cols returned by sub */ + int iInput; /* To iterate through sub cols */ + int addrNe; /* Address of OP_Ne */ + int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */ +@@ -153898,7 +174957,6 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + int addrEmpty; /* Address of OP_Rewind in flush: */ + int regNew; /* Array of registers holding new input row */ + int regRecord; /* regNew array in record form */ +- int regRowid; /* Rowid for regRecord in eph table */ + int regNewPeer = 0; /* Peer values for new row (part of regNew) */ + int regPeer = 0; /* Peer values for current row */ + int regFlushPart = 0; /* Register for "Gosub flush_partition" */ +@@ -153965,12 +175023,12 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + } + + /* Allocate registers for the array of values from the sub-query, the +- ** samve values in record form, and the rowid used to insert said record ++ ** same values in record form, and the rowid used to insert said record + ** into the ephemeral table. */ + regNew = pParse->nMem+1; + pParse->nMem += nInput; + regRecord = ++pParse->nMem; +- regRowid = ++pParse->nMem; ++ s.regRowid = ++pParse->nMem; + + /* If the window frame contains an " PRECEDING" or " FOLLOWING" + ** clause, allocate registers to store the results of evaluating each +@@ -154026,9 +175084,9 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + } + + /* Insert the new row into the ephemeral table */ +- sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid); +- sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid); +- addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid); ++ sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, s.regRowid); ++ sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, s.regRowid); ++ addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, s.regRowid); + VdbeCoverageNeverNull(v); + + /* This block is run for the first row of each partition */ +@@ -154049,8 +175107,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound */ + VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */ + windowAggFinal(&s, 0); +- sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); +- VdbeCoverageNeverTaken(v); ++ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr); + windowReturnOneRow(&s); + sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); +@@ -154062,13 +175119,10 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + } + + if( pMWin->eStart!=TK_UNBOUNDED ){ +- sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1); +- VdbeCoverageNeverTaken(v); ++ sqlite3VdbeAddOp1(v, OP_Rewind, s.start.csr); + } +- sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); +- VdbeCoverageNeverTaken(v); +- sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1); +- VdbeCoverageNeverTaken(v); ++ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr); ++ sqlite3VdbeAddOp1(v, OP_Rewind, s.end.csr); + if( regPeer && pOrderBy ){ + sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1); +@@ -154146,6 +175200,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + sqlite3VdbeJumpHere(v, addrGosubFlush); + } + ++ s.regRowid = 0; + addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); + VdbeCoverage(v); + if( pMWin->eEnd==TK_PRECEDING ){ +@@ -154208,8 +175263,11 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + + /************** End of window.c **********************************************/ + /************** Begin file parse.c *******************************************/ ++/* This file is automatically generated by Lemon from input grammar ++** source file "parse.y". ++*/ + /* +-** 2000-05-29 ++** 2001-09-15 + ** + ** The author disclaims copyright to this source code. In place of + ** a legal notice, here is a blessing: +@@ -154219,25 +175277,23 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( + ** May you share freely, never taking more than you give. + ** + ************************************************************************* +-** Driver template for the LEMON parser generator. ++** This file contains SQLite's SQL parser. + ** +-** The "lemon" program processes an LALR(1) input grammar file, then uses +-** this template to construct a parser. The "lemon" program inserts text +-** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the +-** interstitial "-" characters) contained in this template is changed into +-** the value of the %name directive from the grammar. Otherwise, the content +-** of this template is copied straight through into the generate parser +-** source file. +-** +-** The following is the concatenation of all %include directives from the +-** input grammar file: ++** The canonical source code to this file ("parse.y") is a Lemon grammar ++** file that specifies the input grammar and actions to take while parsing. ++** That input file is processed by Lemon to generate a C-language ++** implementation of a parser for the given grammar. You might be reading ++** this comment as part of the translated C-code. Edits should be made ++** to the original parse.y sources. + */ +-/* #include */ +-/* #include */ +-/************ Begin %include sections from the grammar ************************/ + + /* #include "sqliteInt.h" */ + ++/* ++** Verify that the pParse->isCreate field is set ++*/ ++#define ASSERT_IS_CREATE assert(pParse->isCreate) ++ + /* + ** Disable all error recovery processing in the parser push-down + ** automaton. +@@ -154287,6 +175343,13 @@ struct TrigEvent { int a; IdList * b; }; + + struct FrameBound { int eType; Expr *pExpr; }; + ++/* ++** Generate a syntax error ++*/ ++static void parserSyntaxError(Parse *pParse, Token *p){ ++ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p); ++} ++ + /* + ** Disable lookaside memory allocation for objects that might be + ** shared across database connections. +@@ -154294,6 +175357,10 @@ struct FrameBound { int eType; Expr *pExpr; }; + static void disableLookaside(Parse *pParse){ + sqlite3 *db = pParse->db; + pParse->disableLookaside++; ++#ifdef SQLITE_DEBUG ++ pParse->isCreate = 1; ++#endif ++ memset(&pParse->u1.cr, 0, sizeof(pParse->u1.cr)); + DisableLookaside; + } + +@@ -154327,26 +175394,54 @@ static void updateDeleteLimitError( + static void parserDoubleLinkSelect(Parse *pParse, Select *p){ + assert( p!=0 ); + if( p->pPrior ){ +- Select *pNext = 0, *pLoop; +- int mxSelect, cnt = 0; +- for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ ++ Select *pNext = 0, *pLoop = p; ++ int mxSelect, cnt = 1; ++ while(1){ + pLoop->pNext = pNext; + pLoop->selFlags |= SF_Compound; ++ pNext = pLoop; ++ pLoop = pLoop->pPrior; ++ if( pLoop==0 ) break; ++ cnt++; ++ if( pLoop->pOrderBy || pLoop->pLimit ){ ++ sqlite3ErrorMsg(pParse,"%s clause should come after %s not before", ++ pLoop->pOrderBy!=0 ? "ORDER BY" : "LIMIT", ++ sqlite3SelectOpName(pNext->op)); ++ break; ++ } + } +- if( (p->selFlags & SF_MultiValue)==0 && +- (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 && +- cnt>mxSelect ++ if( (p->selFlags & (SF_MultiValue|SF_Values))==0 ++ && (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 ++ && cnt>mxSelect + ){ + sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); + } + } + } + ++ /* Attach a With object describing the WITH clause to a Select ++ ** object describing the query for which the WITH clause is a prefix. ++ */ ++ static Select *attachWithToSelect(Parse *pParse, Select *pSelect, With *pWith){ ++ if( pSelect ){ ++ pSelect->pWith = pWith; ++ parserDoubleLinkSelect(pParse, pSelect); ++ }else{ ++ sqlite3WithDelete(pParse->db, pWith); ++ } ++ return pSelect; ++ } + +- /* Construct a new Expr object from a single identifier. Use the +- ** new Expr to populate pOut. Set the span of pOut to be the identifier +- ** that created the expression. ++ /* Memory allocator for parser stack resizing. This is a thin wrapper around ++ ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate ++ ** testing. + */ ++ static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){ ++ return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); ++ } ++ ++ ++ /* Construct a new Expr object from a single token */ + static Expr *tokenExpr(Parse *pParse, int op, Token t){ + Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); + if( p ){ +@@ -154355,17 +175450,18 @@ static void updateDeleteLimitError( + p->affExpr = 0; + p->flags = EP_Leaf; + ExprClearVVAProperties(p); +- p->iAgg = -1; ++ /* p->iAgg = -1; // Not required */ + p->pLeft = p->pRight = 0; +- p->x.pList = 0; + p->pAggInfo = 0; +- p->y.pTab = 0; ++ memset(&p->x, 0, sizeof(p->x)); ++ memset(&p->y, 0, sizeof(p->y)); + p->op2 = 0; + p->iTable = 0; + p->iColumn = 0; + p->u.zToken = (char*)&p[1]; + memcpy(p->u.zToken, t.z, t.n); + p->u.zToken[t.n] = 0; ++ p->w.iOfst = (int)(t.z - pParse->zTail); + if( sqlite3Isquote(p->u.zToken[0]) ){ + sqlite3DequoteExpr(p); + } +@@ -154418,11 +175514,197 @@ static void updateDeleteLimitError( + # error too many tokens in the grammar + #endif + /**************** End of %include directives **********************************/ +-/* These constants specify the various numeric values for terminal symbols +-** in a format understandable to "makeheaders". This section is blank unless +-** "lemon" is run with the "-m" command-line option. +-***************** Begin makeheaders token definitions *************************/ +-/**************** End makeheaders token definitions ***************************/ ++/* These constants specify the various numeric values for terminal symbols. ++***************** Begin token definitions *************************************/ ++#ifndef TK_SEMI ++#define TK_SEMI 1 ++#define TK_EXPLAIN 2 ++#define TK_QUERY 3 ++#define TK_PLAN 4 ++#define TK_BEGIN 5 ++#define TK_TRANSACTION 6 ++#define TK_DEFERRED 7 ++#define TK_IMMEDIATE 8 ++#define TK_EXCLUSIVE 9 ++#define TK_COMMIT 10 ++#define TK_END 11 ++#define TK_ROLLBACK 12 ++#define TK_SAVEPOINT 13 ++#define TK_RELEASE 14 ++#define TK_TO 15 ++#define TK_TABLE 16 ++#define TK_CREATE 17 ++#define TK_IF 18 ++#define TK_NOT 19 ++#define TK_EXISTS 20 ++#define TK_TEMP 21 ++#define TK_LP 22 ++#define TK_RP 23 ++#define TK_AS 24 ++#define TK_COMMA 25 ++#define TK_WITHOUT 26 ++#define TK_ABORT 27 ++#define TK_ACTION 28 ++#define TK_AFTER 29 ++#define TK_ANALYZE 30 ++#define TK_ASC 31 ++#define TK_ATTACH 32 ++#define TK_BEFORE 33 ++#define TK_BY 34 ++#define TK_CASCADE 35 ++#define TK_CAST 36 ++#define TK_CONFLICT 37 ++#define TK_DATABASE 38 ++#define TK_DESC 39 ++#define TK_DETACH 40 ++#define TK_EACH 41 ++#define TK_FAIL 42 ++#define TK_OR 43 ++#define TK_AND 44 ++#define TK_IS 45 ++#define TK_ISNOT 46 ++#define TK_MATCH 47 ++#define TK_LIKE_KW 48 ++#define TK_BETWEEN 49 ++#define TK_IN 50 ++#define TK_ISNULL 51 ++#define TK_NOTNULL 52 ++#define TK_NE 53 ++#define TK_EQ 54 ++#define TK_GT 55 ++#define TK_LE 56 ++#define TK_LT 57 ++#define TK_GE 58 ++#define TK_ESCAPE 59 ++#define TK_ID 60 ++#define TK_COLUMNKW 61 ++#define TK_DO 62 ++#define TK_FOR 63 ++#define TK_IGNORE 64 ++#define TK_INITIALLY 65 ++#define TK_INSTEAD 66 ++#define TK_NO 67 ++#define TK_KEY 68 ++#define TK_OF 69 ++#define TK_OFFSET 70 ++#define TK_PRAGMA 71 ++#define TK_RAISE 72 ++#define TK_RECURSIVE 73 ++#define TK_REPLACE 74 ++#define TK_RESTRICT 75 ++#define TK_ROW 76 ++#define TK_ROWS 77 ++#define TK_TRIGGER 78 ++#define TK_VACUUM 79 ++#define TK_VIEW 80 ++#define TK_VIRTUAL 81 ++#define TK_WITH 82 ++#define TK_NULLS 83 ++#define TK_FIRST 84 ++#define TK_LAST 85 ++#define TK_CURRENT 86 ++#define TK_FOLLOWING 87 ++#define TK_PARTITION 88 ++#define TK_PRECEDING 89 ++#define TK_RANGE 90 ++#define TK_UNBOUNDED 91 ++#define TK_EXCLUDE 92 ++#define TK_GROUPS 93 ++#define TK_OTHERS 94 ++#define TK_TIES 95 ++#define TK_GENERATED 96 ++#define TK_ALWAYS 97 ++#define TK_MATERIALIZED 98 ++#define TK_REINDEX 99 ++#define TK_RENAME 100 ++#define TK_CTIME_KW 101 ++#define TK_ANY 102 ++#define TK_BITAND 103 ++#define TK_BITOR 104 ++#define TK_LSHIFT 105 ++#define TK_RSHIFT 106 ++#define TK_PLUS 107 ++#define TK_MINUS 108 ++#define TK_STAR 109 ++#define TK_SLASH 110 ++#define TK_REM 111 ++#define TK_CONCAT 112 ++#define TK_PTR 113 ++#define TK_COLLATE 114 ++#define TK_BITNOT 115 ++#define TK_ON 116 ++#define TK_INDEXED 117 ++#define TK_STRING 118 ++#define TK_JOIN_KW 119 ++#define TK_CONSTRAINT 120 ++#define TK_DEFAULT 121 ++#define TK_NULL 122 ++#define TK_PRIMARY 123 ++#define TK_UNIQUE 124 ++#define TK_CHECK 125 ++#define TK_REFERENCES 126 ++#define TK_AUTOINCR 127 ++#define TK_INSERT 128 ++#define TK_DELETE 129 ++#define TK_UPDATE 130 ++#define TK_SET 131 ++#define TK_DEFERRABLE 132 ++#define TK_FOREIGN 133 ++#define TK_DROP 134 ++#define TK_UNION 135 ++#define TK_ALL 136 ++#define TK_EXCEPT 137 ++#define TK_INTERSECT 138 ++#define TK_SELECT 139 ++#define TK_VALUES 140 ++#define TK_DISTINCT 141 ++#define TK_DOT 142 ++#define TK_FROM 143 ++#define TK_JOIN 144 ++#define TK_USING 145 ++#define TK_ORDER 146 ++#define TK_GROUP 147 ++#define TK_HAVING 148 ++#define TK_LIMIT 149 ++#define TK_WHERE 150 ++#define TK_RETURNING 151 ++#define TK_INTO 152 ++#define TK_NOTHING 153 ++#define TK_FLOAT 154 ++#define TK_BLOB 155 ++#define TK_INTEGER 156 ++#define TK_VARIABLE 157 ++#define TK_CASE 158 ++#define TK_WHEN 159 ++#define TK_THEN 160 ++#define TK_ELSE 161 ++#define TK_INDEX 162 ++#define TK_ALTER 163 ++#define TK_ADD 164 ++#define TK_WINDOW 165 ++#define TK_OVER 166 ++#define TK_FILTER 167 ++#define TK_COLUMN 168 ++#define TK_AGG_FUNCTION 169 ++#define TK_AGG_COLUMN 170 ++#define TK_TRUEFALSE 171 ++#define TK_FUNCTION 172 ++#define TK_UPLUS 173 ++#define TK_UMINUS 174 ++#define TK_TRUTH 175 ++#define TK_REGISTER 176 ++#define TK_VECTOR 177 ++#define TK_SELECT_COLUMN 178 ++#define TK_IF_NULL_ROW 179 ++#define TK_ASTERISK 180 ++#define TK_SPAN 181 ++#define TK_ERROR 182 ++#define TK_QNUMBER 183 ++#define TK_SPACE 184 ++#define TK_COMMENT 185 ++#define TK_ILLEGAL 186 ++#endif ++/**************** End token definitions ***************************************/ + + /* The next sections is a series of control #defines. + ** various aspects of the generated parser. +@@ -154461,6 +175743,9 @@ static void updateDeleteLimitError( + ** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser + ** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser + ** sqlite3ParserCTX_* As sqlite3ParserARG_ except for %extra_context ++** YYREALLOC Name of the realloc() function to use ++** YYFREE Name of the free() function to use ++** YYDYNSTACK True if stack space should be extended on heap + ** YYERRORSYMBOL is the code number of the error symbol. If not + ** defined, then do no error processing. + ** YYNSTATE the combined number of states. +@@ -154474,34 +175759,39 @@ static void updateDeleteLimitError( + ** YY_NO_ACTION The yy_action[] code for no-op + ** YY_MIN_REDUCE Minimum value for reduce actions + ** YY_MAX_REDUCE Maximum value for reduce actions ++** YY_MIN_DSTRCTR Minimum symbol value that has a destructor ++** YY_MAX_DSTRCTR Maximum symbol value that has a destructor + */ + #ifndef INTERFACE + # define INTERFACE 1 + #endif + /************* Begin control #defines *****************************************/ + #define YYCODETYPE unsigned short int +-#define YYNOCODE 310 ++#define YYNOCODE 323 + #define YYACTIONTYPE unsigned short int +-#define YYWILDCARD 100 ++#define YYWILDCARD 102 + #define sqlite3ParserTOKENTYPE Token + typedef union { + int yyinit; + sqlite3ParserTOKENTYPE yy0; +- SrcList* yy47; +- u8 yy58; +- struct FrameBound yy77; +- With* yy131; +- int yy192; +- Expr* yy202; +- struct {int value; int mask;} yy207; +- struct TrigEvent yy230; +- ExprList* yy242; +- Window* yy303; +- Upsert* yy318; +- const char* yy436; +- TriggerStep* yy447; +- Select* yy539; +- IdList* yy600; ++ u32 yy9; ++ struct TrigEvent yy28; ++ With* yy125; ++ IdList* yy204; ++ struct FrameBound yy205; ++ TriggerStep* yy319; ++ const char* yy342; ++ Cte* yy361; ++ ExprList* yy402; ++ Upsert* yy403; ++ OnOrUsing yy421; ++ u8 yy444; ++ struct {int value; int mask;} yy481; ++ Window* yy483; ++ int yy502; ++ SrcList* yy563; ++ Expr* yy590; ++ Select* yy637; + } YYMINORTYPE; + #ifndef YYSTACKDEPTH + #define YYSTACKDEPTH 100 +@@ -154511,24 +175801,29 @@ typedef union { + #define sqlite3ParserARG_PARAM + #define sqlite3ParserARG_FETCH + #define sqlite3ParserARG_STORE ++#define YYREALLOC parserStackRealloc ++#define YYFREE sqlite3_free ++#define YYDYNSTACK 1 + #define sqlite3ParserCTX_SDECL Parse *pParse; + #define sqlite3ParserCTX_PDECL ,Parse *pParse + #define sqlite3ParserCTX_PARAM ,pParse + #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; + #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; + #define YYFALLBACK 1 +-#define YYNSTATE 553 +-#define YYNRULE 385 +-#define YYNRULE_WITH_ACTION 325 +-#define YYNTOKEN 181 +-#define YY_MAX_SHIFT 552 +-#define YY_MIN_SHIFTREDUCE 803 +-#define YY_MAX_SHIFTREDUCE 1187 +-#define YY_ERROR_ACTION 1188 +-#define YY_ACCEPT_ACTION 1189 +-#define YY_NO_ACTION 1190 +-#define YY_MIN_REDUCE 1191 +-#define YY_MAX_REDUCE 1575 ++#define YYNSTATE 583 ++#define YYNRULE 409 ++#define YYNRULE_WITH_ACTION 344 ++#define YYNTOKEN 187 ++#define YY_MAX_SHIFT 582 ++#define YY_MIN_SHIFTREDUCE 845 ++#define YY_MAX_SHIFTREDUCE 1253 ++#define YY_ERROR_ACTION 1254 ++#define YY_ACCEPT_ACTION 1255 ++#define YY_NO_ACTION 1256 ++#define YY_MIN_REDUCE 1257 ++#define YY_MAX_REDUCE 1665 ++#define YY_MIN_DSTRCTR 206 ++#define YY_MAX_DSTRCTR 320 + /************* End control #defines *******************************************/ + #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) + +@@ -154544,6 +175839,22 @@ typedef union { + # define yytestcase(X) + #endif + ++/* Macro to determine if stack space has the ability to grow using ++** heap memory. ++*/ ++#if YYSTACKDEPTH<=0 || YYDYNSTACK ++# define YYGROWABLESTACK 1 ++#else ++# define YYGROWABLESTACK 0 ++#endif ++ ++/* Guarantee a minimum number of initial stack slots. ++*/ ++#if YYSTACKDEPTH<=0 ++# undef YYSTACKDEPTH ++# define YYSTACKDEPTH 2 /* Need a minimum stack size */ ++#endif ++ + + /* Next are the tables used to determine what action to take based on the + ** current state and lookahead token. These tables are used to implement +@@ -154595,586 +175906,643 @@ typedef union { + ** yy_default[] Default action for each state. + ** + *********** Begin parsing tables **********************************************/ +-#define YY_ACTTAB_COUNT (1962) ++#define YY_ACTTAB_COUNT (2207) + static const YYACTIONTYPE yy_action[] = { +- /* 0 */ 546, 1222, 546, 451, 1260, 546, 1239, 546, 114, 111, +- /* 10 */ 211, 546, 1537, 546, 1260, 523, 114, 111, 211, 392, +- /* 20 */ 1232, 344, 42, 42, 42, 42, 1225, 42, 42, 71, +- /* 30 */ 71, 937, 1224, 71, 71, 71, 71, 1462, 1493, 938, +- /* 40 */ 820, 453, 6, 121, 122, 112, 1165, 1165, 1006, 1009, +- /* 50 */ 999, 999, 119, 119, 120, 120, 120, 120, 1543, 392, +- /* 60 */ 1358, 1517, 552, 2, 1193, 194, 528, 436, 143, 291, +- /* 70 */ 528, 136, 528, 371, 261, 504, 272, 385, 1273, 527, +- /* 80 */ 503, 493, 164, 121, 122, 112, 1165, 1165, 1006, 1009, +- /* 90 */ 999, 999, 119, 119, 120, 120, 120, 120, 1358, 442, +- /* 100 */ 1514, 118, 118, 118, 118, 117, 117, 116, 116, 116, +- /* 110 */ 115, 424, 266, 266, 266, 266, 1498, 358, 1500, 435, +- /* 120 */ 357, 1498, 517, 524, 1485, 543, 1114, 543, 1114, 392, +- /* 130 */ 405, 241, 208, 114, 111, 211, 98, 290, 537, 221, +- /* 140 */ 1029, 118, 118, 118, 118, 117, 117, 116, 116, 116, +- /* 150 */ 115, 424, 1142, 121, 122, 112, 1165, 1165, 1006, 1009, +- /* 160 */ 999, 999, 119, 119, 120, 120, 120, 120, 406, 428, +- /* 170 */ 117, 117, 116, 116, 116, 115, 424, 1418, 468, 123, +- /* 180 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, +- /* 190 */ 424, 116, 116, 116, 115, 424, 540, 540, 540, 392, +- /* 200 */ 505, 120, 120, 120, 120, 113, 1051, 1142, 1143, 1144, +- /* 210 */ 1051, 118, 118, 118, 118, 117, 117, 116, 116, 116, +- /* 220 */ 115, 424, 1461, 121, 122, 112, 1165, 1165, 1006, 1009, +- /* 230 */ 999, 999, 119, 119, 120, 120, 120, 120, 392, 444, +- /* 240 */ 316, 83, 463, 81, 359, 382, 1142, 80, 118, 118, +- /* 250 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 179, +- /* 260 */ 434, 424, 121, 122, 112, 1165, 1165, 1006, 1009, 999, +- /* 270 */ 999, 119, 119, 120, 120, 120, 120, 434, 433, 266, +- /* 280 */ 266, 118, 118, 118, 118, 117, 117, 116, 116, 116, +- /* 290 */ 115, 424, 543, 1109, 903, 506, 1142, 114, 111, 211, +- /* 300 */ 1431, 1142, 1143, 1144, 206, 491, 1109, 392, 449, 1109, +- /* 310 */ 545, 330, 120, 120, 120, 120, 298, 1431, 1433, 17, +- /* 320 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, +- /* 330 */ 424, 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, +- /* 340 */ 119, 119, 120, 120, 120, 120, 392, 1358, 434, 1142, +- /* 350 */ 482, 1142, 1143, 1144, 996, 996, 1007, 1010, 445, 118, +- /* 360 */ 118, 118, 118, 117, 117, 116, 116, 116, 115, 424, +- /* 370 */ 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, +- /* 380 */ 119, 120, 120, 120, 120, 1054, 1054, 465, 1431, 118, +- /* 390 */ 118, 118, 118, 117, 117, 116, 116, 116, 115, 424, +- /* 400 */ 1142, 451, 546, 1426, 1142, 1143, 1144, 233, 966, 1142, +- /* 410 */ 481, 478, 477, 171, 360, 392, 164, 407, 414, 842, +- /* 420 */ 476, 164, 185, 334, 71, 71, 1243, 1000, 118, 118, +- /* 430 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 121, +- /* 440 */ 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, 119, +- /* 450 */ 120, 120, 120, 120, 392, 1142, 1143, 1144, 835, 12, +- /* 460 */ 314, 509, 163, 356, 1142, 1143, 1144, 114, 111, 211, +- /* 470 */ 508, 290, 537, 546, 276, 180, 290, 537, 121, 122, +- /* 480 */ 112, 1165, 1165, 1006, 1009, 999, 999, 119, 119, 120, +- /* 490 */ 120, 120, 120, 345, 484, 71, 71, 118, 118, 118, +- /* 500 */ 118, 117, 117, 116, 116, 116, 115, 424, 1142, 209, +- /* 510 */ 411, 523, 1142, 1109, 1571, 378, 252, 269, 342, 487, +- /* 520 */ 337, 486, 238, 392, 513, 364, 1109, 1127, 333, 1109, +- /* 530 */ 191, 409, 286, 32, 457, 443, 118, 118, 118, 118, +- /* 540 */ 117, 117, 116, 116, 116, 115, 424, 121, 122, 112, +- /* 550 */ 1165, 1165, 1006, 1009, 999, 999, 119, 119, 120, 120, +- /* 560 */ 120, 120, 392, 1142, 1143, 1144, 987, 1142, 1143, 1144, +- /* 570 */ 1142, 233, 492, 1492, 481, 478, 477, 6, 163, 546, +- /* 580 */ 512, 546, 115, 424, 476, 5, 121, 122, 112, 1165, +- /* 590 */ 1165, 1006, 1009, 999, 999, 119, 119, 120, 120, 120, +- /* 600 */ 120, 13, 13, 13, 13, 118, 118, 118, 118, 117, +- /* 610 */ 117, 116, 116, 116, 115, 424, 403, 502, 408, 546, +- /* 620 */ 1486, 544, 1142, 892, 892, 1142, 1143, 1144, 1473, 1142, +- /* 630 */ 275, 392, 808, 809, 810, 971, 422, 422, 422, 16, +- /* 640 */ 16, 55, 55, 1242, 118, 118, 118, 118, 117, 117, +- /* 650 */ 116, 116, 116, 115, 424, 121, 122, 112, 1165, 1165, +- /* 660 */ 1006, 1009, 999, 999, 119, 119, 120, 120, 120, 120, +- /* 670 */ 392, 1189, 1, 1, 552, 2, 1193, 1142, 1143, 1144, +- /* 680 */ 194, 291, 898, 136, 1142, 1143, 1144, 897, 521, 1492, +- /* 690 */ 1273, 3, 380, 6, 121, 122, 112, 1165, 1165, 1006, +- /* 700 */ 1009, 999, 999, 119, 119, 120, 120, 120, 120, 858, +- /* 710 */ 546, 924, 546, 118, 118, 118, 118, 117, 117, 116, +- /* 720 */ 116, 116, 115, 424, 266, 266, 1092, 1569, 1142, 551, +- /* 730 */ 1569, 1193, 13, 13, 13, 13, 291, 543, 136, 392, +- /* 740 */ 485, 421, 420, 966, 344, 1273, 468, 410, 859, 279, +- /* 750 */ 140, 221, 118, 118, 118, 118, 117, 117, 116, 116, +- /* 760 */ 116, 115, 424, 121, 122, 112, 1165, 1165, 1006, 1009, +- /* 770 */ 999, 999, 119, 119, 120, 120, 120, 120, 546, 266, +- /* 780 */ 266, 428, 392, 1142, 1143, 1144, 1172, 830, 1172, 468, +- /* 790 */ 431, 145, 543, 1146, 401, 314, 439, 302, 838, 1490, +- /* 800 */ 71, 71, 412, 6, 1090, 473, 221, 100, 112, 1165, +- /* 810 */ 1165, 1006, 1009, 999, 999, 119, 119, 120, 120, 120, +- /* 820 */ 120, 118, 118, 118, 118, 117, 117, 116, 116, 116, +- /* 830 */ 115, 424, 237, 1425, 546, 451, 428, 287, 986, 546, +- /* 840 */ 236, 235, 234, 830, 97, 529, 429, 1265, 1265, 1146, +- /* 850 */ 494, 307, 430, 838, 977, 546, 71, 71, 976, 1241, +- /* 860 */ 546, 51, 51, 300, 118, 118, 118, 118, 117, 117, +- /* 870 */ 116, 116, 116, 115, 424, 194, 103, 70, 70, 266, +- /* 880 */ 266, 546, 71, 71, 266, 266, 30, 391, 344, 976, +- /* 890 */ 976, 978, 543, 528, 1109, 328, 392, 543, 495, 397, +- /* 900 */ 1470, 195, 530, 13, 13, 1358, 240, 1109, 277, 280, +- /* 910 */ 1109, 280, 304, 457, 306, 333, 392, 31, 188, 419, +- /* 920 */ 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, +- /* 930 */ 119, 120, 120, 120, 120, 142, 392, 365, 457, 986, +- /* 940 */ 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, +- /* 950 */ 119, 120, 120, 120, 120, 977, 323, 1142, 326, 976, +- /* 960 */ 121, 110, 112, 1165, 1165, 1006, 1009, 999, 999, 119, +- /* 970 */ 119, 120, 120, 120, 120, 464, 377, 1185, 118, 118, +- /* 980 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 1142, +- /* 990 */ 976, 976, 978, 305, 9, 366, 244, 362, 118, 118, +- /* 1000 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 313, +- /* 1010 */ 546, 344, 1142, 1143, 1144, 299, 290, 537, 118, 118, +- /* 1020 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 1263, +- /* 1030 */ 1263, 1163, 13, 13, 278, 421, 420, 468, 392, 923, +- /* 1040 */ 260, 260, 289, 1169, 1142, 1143, 1144, 189, 1171, 266, +- /* 1050 */ 266, 468, 390, 543, 1186, 546, 1170, 263, 144, 489, +- /* 1060 */ 922, 546, 543, 122, 112, 1165, 1165, 1006, 1009, 999, +- /* 1070 */ 999, 119, 119, 120, 120, 120, 120, 71, 71, 1142, +- /* 1080 */ 1172, 1272, 1172, 13, 13, 898, 1070, 1163, 546, 468, +- /* 1090 */ 897, 107, 538, 1491, 4, 1268, 1109, 6, 525, 1049, +- /* 1100 */ 12, 1071, 1092, 1570, 312, 455, 1570, 520, 541, 1109, +- /* 1110 */ 56, 56, 1109, 1489, 423, 1358, 1072, 6, 345, 285, +- /* 1120 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, +- /* 1130 */ 424, 425, 1271, 321, 1142, 1143, 1144, 878, 266, 266, +- /* 1140 */ 1277, 107, 538, 535, 4, 1488, 293, 879, 1211, 6, +- /* 1150 */ 210, 543, 543, 164, 294, 496, 416, 204, 541, 267, +- /* 1160 */ 267, 1214, 398, 511, 499, 204, 266, 266, 396, 531, +- /* 1170 */ 8, 986, 543, 519, 546, 922, 458, 105, 105, 543, +- /* 1180 */ 1090, 425, 266, 266, 106, 417, 425, 548, 547, 266, +- /* 1190 */ 266, 976, 518, 535, 1373, 543, 15, 15, 266, 266, +- /* 1200 */ 456, 1120, 543, 266, 266, 1070, 1372, 515, 290, 537, +- /* 1210 */ 546, 543, 514, 97, 444, 316, 543, 546, 922, 125, +- /* 1220 */ 1071, 986, 976, 976, 978, 979, 27, 105, 105, 401, +- /* 1230 */ 343, 1511, 44, 44, 106, 1072, 425, 548, 547, 57, +- /* 1240 */ 57, 976, 343, 1511, 107, 538, 546, 4, 462, 401, +- /* 1250 */ 214, 1120, 459, 297, 377, 1091, 534, 1309, 546, 539, +- /* 1260 */ 398, 541, 290, 537, 104, 244, 102, 526, 58, 58, +- /* 1270 */ 546, 199, 976, 976, 978, 979, 27, 1516, 1131, 427, +- /* 1280 */ 59, 59, 270, 237, 425, 138, 95, 375, 375, 374, +- /* 1290 */ 255, 372, 60, 60, 817, 1180, 535, 546, 273, 546, +- /* 1300 */ 1163, 1308, 389, 388, 546, 438, 546, 215, 210, 296, +- /* 1310 */ 515, 849, 546, 265, 208, 516, 1476, 295, 274, 61, +- /* 1320 */ 61, 62, 62, 308, 986, 109, 45, 45, 46, 46, +- /* 1330 */ 105, 105, 1186, 922, 47, 47, 341, 106, 546, 425, +- /* 1340 */ 548, 547, 1542, 546, 976, 867, 340, 217, 546, 937, +- /* 1350 */ 397, 107, 538, 218, 4, 156, 1163, 938, 158, 546, +- /* 1360 */ 49, 49, 1162, 546, 268, 50, 50, 546, 541, 1450, +- /* 1370 */ 63, 63, 546, 1449, 216, 976, 976, 978, 979, 27, +- /* 1380 */ 446, 64, 64, 546, 460, 65, 65, 546, 318, 14, +- /* 1390 */ 14, 425, 1305, 546, 66, 66, 1087, 546, 141, 379, +- /* 1400 */ 38, 546, 963, 535, 322, 127, 127, 546, 393, 67, +- /* 1410 */ 67, 546, 325, 290, 537, 52, 52, 515, 546, 68, +- /* 1420 */ 68, 845, 514, 69, 69, 399, 165, 857, 856, 53, +- /* 1430 */ 53, 986, 311, 151, 151, 97, 432, 105, 105, 327, +- /* 1440 */ 152, 152, 526, 1048, 106, 1048, 425, 548, 547, 1131, +- /* 1450 */ 427, 976, 1032, 270, 968, 239, 329, 243, 375, 375, +- /* 1460 */ 374, 255, 372, 940, 941, 817, 1296, 546, 220, 546, +- /* 1470 */ 107, 538, 546, 4, 546, 1256, 199, 845, 215, 1036, +- /* 1480 */ 296, 1530, 976, 976, 978, 979, 27, 541, 295, 76, +- /* 1490 */ 76, 54, 54, 980, 72, 72, 128, 128, 864, 865, +- /* 1500 */ 107, 538, 546, 4, 1047, 546, 1047, 533, 469, 546, +- /* 1510 */ 425, 546, 450, 1240, 546, 243, 546, 541, 217, 546, +- /* 1520 */ 452, 197, 535, 243, 73, 73, 156, 129, 129, 158, +- /* 1530 */ 336, 130, 130, 126, 126, 1036, 150, 150, 149, 149, +- /* 1540 */ 425, 134, 134, 317, 474, 216, 97, 239, 331, 980, +- /* 1550 */ 986, 97, 535, 346, 347, 546, 105, 105, 902, 931, +- /* 1560 */ 546, 895, 243, 106, 109, 425, 548, 547, 546, 1505, +- /* 1570 */ 976, 828, 99, 538, 139, 4, 546, 133, 133, 393, +- /* 1580 */ 986, 1317, 131, 131, 290, 537, 105, 105, 1357, 541, +- /* 1590 */ 132, 132, 1292, 106, 1303, 425, 548, 547, 75, 75, +- /* 1600 */ 976, 976, 976, 978, 979, 27, 546, 432, 896, 1289, +- /* 1610 */ 532, 109, 425, 1363, 546, 1221, 1213, 1202, 258, 546, +- /* 1620 */ 349, 546, 1201, 11, 535, 1203, 1524, 351, 77, 77, +- /* 1630 */ 376, 976, 976, 978, 979, 27, 74, 74, 353, 213, +- /* 1640 */ 301, 43, 43, 48, 48, 437, 310, 201, 303, 1350, +- /* 1650 */ 315, 355, 986, 454, 479, 1239, 339, 192, 105, 105, +- /* 1660 */ 1422, 1421, 193, 536, 205, 106, 1527, 425, 548, 547, +- /* 1670 */ 1180, 167, 976, 270, 247, 1469, 1467, 1177, 375, 375, +- /* 1680 */ 374, 255, 372, 200, 369, 817, 400, 83, 79, 82, +- /* 1690 */ 1427, 448, 177, 95, 1342, 161, 169, 1339, 215, 440, +- /* 1700 */ 296, 172, 173, 976, 976, 978, 979, 27, 295, 174, +- /* 1710 */ 175, 441, 472, 223, 1347, 383, 35, 381, 36, 461, +- /* 1720 */ 88, 1353, 181, 447, 384, 1416, 227, 467, 259, 229, +- /* 1730 */ 186, 488, 470, 324, 1250, 230, 231, 320, 217, 1204, +- /* 1740 */ 1438, 1259, 386, 1258, 413, 90, 156, 849, 1541, 158, +- /* 1750 */ 206, 415, 1540, 507, 1300, 1257, 94, 348, 1229, 1301, +- /* 1760 */ 387, 1510, 1228, 338, 1227, 216, 350, 1539, 498, 283, +- /* 1770 */ 284, 1249, 501, 1299, 352, 245, 246, 418, 1298, 354, +- /* 1780 */ 1496, 1495, 124, 10, 526, 363, 101, 1324, 253, 96, +- /* 1790 */ 510, 1210, 34, 549, 1137, 254, 256, 257, 166, 393, +- /* 1800 */ 550, 1199, 1282, 361, 290, 537, 1281, 196, 367, 368, +- /* 1810 */ 1194, 153, 1454, 137, 281, 1323, 1455, 804, 154, 426, +- /* 1820 */ 198, 155, 1453, 1452, 292, 212, 202, 432, 1402, 203, +- /* 1830 */ 271, 135, 288, 78, 1046, 1044, 960, 168, 157, 881, +- /* 1840 */ 170, 219, 309, 222, 1060, 176, 964, 159, 402, 84, +- /* 1850 */ 178, 404, 85, 86, 87, 160, 1063, 224, 394, 395, +- /* 1860 */ 225, 1059, 146, 18, 226, 319, 243, 1174, 466, 228, +- /* 1870 */ 1052, 182, 183, 37, 819, 471, 340, 232, 332, 483, +- /* 1880 */ 184, 89, 162, 19, 20, 475, 91, 480, 847, 335, +- /* 1890 */ 147, 860, 282, 92, 490, 93, 1125, 148, 1012, 1095, +- /* 1900 */ 39, 497, 1096, 40, 500, 262, 207, 264, 930, 187, +- /* 1910 */ 925, 109, 1111, 1115, 1113, 7, 1099, 242, 33, 1119, +- /* 1920 */ 21, 522, 22, 23, 24, 1118, 25, 190, 97, 26, +- /* 1930 */ 1027, 1013, 1011, 1015, 1069, 1016, 1068, 249, 248, 28, +- /* 1940 */ 41, 891, 981, 829, 108, 29, 250, 542, 251, 370, +- /* 1950 */ 373, 1133, 1132, 1190, 1190, 1190, 1190, 1190, 1190, 1190, +- /* 1960 */ 1532, 1531, ++ /* 0 */ 130, 127, 234, 282, 282, 1328, 576, 1307, 460, 289, ++ /* 10 */ 289, 576, 1622, 381, 576, 1328, 573, 576, 562, 413, ++ /* 20 */ 1300, 1542, 573, 481, 562, 524, 460, 459, 558, 82, ++ /* 30 */ 82, 983, 294, 375, 51, 51, 498, 61, 61, 984, ++ /* 40 */ 82, 82, 1577, 137, 138, 91, 7, 1228, 1228, 1063, ++ /* 50 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 413, ++ /* 60 */ 288, 288, 182, 288, 288, 481, 536, 288, 288, 130, ++ /* 70 */ 127, 234, 432, 573, 525, 562, 573, 557, 562, 1290, ++ /* 80 */ 573, 421, 562, 137, 138, 91, 559, 1228, 1228, 1063, ++ /* 90 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 296, ++ /* 100 */ 460, 398, 1249, 134, 134, 134, 134, 133, 133, 132, ++ /* 110 */ 132, 132, 131, 128, 451, 451, 1050, 1050, 1064, 1067, ++ /* 120 */ 1255, 1, 1, 582, 2, 1259, 581, 1174, 1259, 1174, ++ /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 481, 1341, ++ /* 140 */ 456, 299, 1341, 134, 134, 134, 134, 133, 133, 132, ++ /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 498, 1228, ++ /* 160 */ 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, ++ /* 170 */ 136, 1204, 862, 1281, 288, 288, 283, 288, 288, 523, ++ /* 180 */ 523, 1250, 139, 578, 7, 578, 1345, 573, 1169, 562, ++ /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 573, 547, ++ /* 200 */ 562, 1169, 245, 1541, 1169, 245, 133, 133, 132, 132, ++ /* 210 */ 132, 131, 128, 451, 302, 134, 134, 134, 134, 133, ++ /* 220 */ 133, 132, 132, 132, 131, 128, 451, 1575, 1204, 1205, ++ /* 230 */ 1204, 7, 470, 550, 455, 413, 550, 455, 130, 127, ++ /* 240 */ 234, 134, 134, 134, 134, 133, 133, 132, 132, 132, ++ /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 483, 137, ++ /* 260 */ 138, 91, 1019, 1228, 1228, 1063, 1066, 1053, 1053, 135, ++ /* 270 */ 135, 136, 136, 136, 136, 1085, 576, 1204, 132, 132, ++ /* 280 */ 132, 131, 128, 451, 93, 214, 134, 134, 134, 134, ++ /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 401, 19, ++ /* 300 */ 19, 134, 134, 134, 134, 133, 133, 132, 132, 132, ++ /* 310 */ 131, 128, 451, 1498, 426, 267, 344, 467, 332, 134, ++ /* 320 */ 134, 134, 134, 133, 133, 132, 132, 132, 131, 128, ++ /* 330 */ 451, 1281, 576, 6, 1204, 1205, 1204, 257, 576, 413, ++ /* 340 */ 511, 508, 507, 1279, 94, 1019, 464, 1204, 551, 551, ++ /* 350 */ 506, 1224, 1571, 44, 38, 51, 51, 411, 576, 413, ++ /* 360 */ 45, 51, 51, 137, 138, 91, 530, 1228, 1228, 1063, ++ /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 398, ++ /* 380 */ 1148, 82, 82, 137, 138, 91, 39, 1228, 1228, 1063, ++ /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 344, ++ /* 400 */ 44, 288, 288, 375, 1204, 1205, 1204, 209, 1204, 1224, ++ /* 410 */ 320, 567, 471, 576, 573, 576, 562, 576, 316, 264, ++ /* 420 */ 231, 46, 160, 134, 134, 134, 134, 133, 133, 132, ++ /* 430 */ 132, 132, 131, 128, 451, 303, 82, 82, 82, 82, ++ /* 440 */ 82, 82, 442, 134, 134, 134, 134, 133, 133, 132, ++ /* 450 */ 132, 132, 131, 128, 451, 1582, 544, 320, 567, 1250, ++ /* 460 */ 874, 1582, 380, 382, 413, 1204, 1205, 1204, 360, 182, ++ /* 470 */ 288, 288, 1576, 557, 1339, 557, 7, 557, 1277, 472, ++ /* 480 */ 346, 526, 531, 573, 556, 562, 439, 1511, 137, 138, ++ /* 490 */ 91, 219, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, ++ /* 500 */ 136, 136, 136, 136, 465, 1511, 1513, 532, 413, 288, ++ /* 510 */ 288, 423, 512, 288, 288, 411, 288, 288, 874, 130, ++ /* 520 */ 127, 234, 573, 1107, 562, 1204, 573, 1107, 562, 573, ++ /* 530 */ 560, 562, 137, 138, 91, 1293, 1228, 1228, 1063, 1066, ++ /* 540 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 134, 134, ++ /* 550 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, ++ /* 560 */ 493, 503, 1292, 1204, 257, 288, 288, 511, 508, 507, ++ /* 570 */ 1204, 1628, 1169, 123, 568, 275, 4, 506, 573, 1511, ++ /* 580 */ 562, 331, 1204, 1205, 1204, 1169, 548, 548, 1169, 261, ++ /* 590 */ 571, 7, 134, 134, 134, 134, 133, 133, 132, 132, ++ /* 600 */ 132, 131, 128, 451, 108, 533, 130, 127, 234, 1204, ++ /* 610 */ 448, 447, 413, 1451, 452, 983, 886, 96, 1598, 1233, ++ /* 620 */ 1204, 1205, 1204, 984, 1235, 1450, 565, 1204, 1205, 1204, ++ /* 630 */ 229, 522, 1234, 534, 1333, 1333, 137, 138, 91, 1449, ++ /* 640 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, ++ /* 650 */ 136, 136, 373, 1595, 971, 1040, 413, 1236, 418, 1236, ++ /* 660 */ 879, 121, 121, 948, 373, 1595, 1204, 1205, 1204, 122, ++ /* 670 */ 1204, 452, 577, 452, 363, 417, 1028, 882, 373, 1595, ++ /* 680 */ 137, 138, 91, 462, 1228, 1228, 1063, 1066, 1053, 1053, ++ /* 690 */ 135, 135, 136, 136, 136, 136, 134, 134, 134, 134, ++ /* 700 */ 133, 133, 132, 132, 132, 131, 128, 451, 1028, 1028, ++ /* 710 */ 1030, 1031, 35, 570, 570, 570, 197, 423, 1040, 198, ++ /* 720 */ 1204, 123, 568, 1204, 4, 320, 567, 1204, 1205, 1204, ++ /* 730 */ 40, 388, 576, 384, 882, 1029, 423, 1188, 571, 1028, ++ /* 740 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, ++ /* 750 */ 128, 451, 529, 1568, 1204, 19, 19, 1204, 575, 492, ++ /* 760 */ 413, 157, 452, 489, 1187, 1331, 1331, 5, 1204, 949, ++ /* 770 */ 431, 1028, 1028, 1030, 565, 22, 22, 1204, 1205, 1204, ++ /* 780 */ 1204, 1205, 1204, 477, 137, 138, 91, 212, 1228, 1228, ++ /* 790 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, ++ /* 800 */ 1188, 48, 111, 1040, 413, 1204, 213, 970, 1041, 121, ++ /* 810 */ 121, 1204, 1205, 1204, 1204, 1205, 1204, 122, 221, 452, ++ /* 820 */ 577, 452, 44, 487, 1028, 1204, 1205, 1204, 137, 138, ++ /* 830 */ 91, 378, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, ++ /* 840 */ 136, 136, 136, 136, 134, 134, 134, 134, 133, 133, ++ /* 850 */ 132, 132, 132, 131, 128, 451, 1028, 1028, 1030, 1031, ++ /* 860 */ 35, 461, 1204, 1205, 1204, 1569, 1040, 377, 214, 1149, ++ /* 870 */ 1657, 535, 1657, 437, 902, 320, 567, 1568, 364, 320, ++ /* 880 */ 567, 412, 329, 1029, 519, 1188, 3, 1028, 134, 134, ++ /* 890 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, ++ /* 900 */ 1659, 399, 1169, 307, 893, 307, 515, 576, 413, 214, ++ /* 910 */ 498, 944, 1024, 540, 903, 1169, 943, 392, 1169, 1028, ++ /* 920 */ 1028, 1030, 406, 298, 1204, 50, 1149, 1658, 413, 1658, ++ /* 930 */ 145, 145, 137, 138, 91, 293, 1228, 1228, 1063, 1066, ++ /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 1188, 1147, ++ /* 950 */ 514, 1568, 137, 138, 91, 1505, 1228, 1228, 1063, 1066, ++ /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 323, ++ /* 970 */ 435, 539, 111, 1506, 274, 291, 372, 517, 367, 516, ++ /* 980 */ 262, 1204, 1205, 1204, 1574, 481, 363, 576, 7, 1569, ++ /* 990 */ 1568, 377, 134, 134, 134, 134, 133, 133, 132, 132, ++ /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 576, 232, 576, ++ /* 1010 */ 19, 19, 134, 134, 134, 134, 133, 133, 132, 132, ++ /* 1020 */ 132, 131, 128, 451, 1169, 433, 576, 1207, 19, 19, ++ /* 1030 */ 19, 19, 19, 19, 1627, 576, 911, 1169, 47, 120, ++ /* 1040 */ 1169, 117, 413, 306, 498, 438, 1125, 206, 336, 19, ++ /* 1050 */ 19, 1435, 49, 449, 449, 449, 1368, 315, 81, 81, ++ /* 1060 */ 576, 304, 413, 1570, 207, 377, 137, 138, 91, 115, ++ /* 1070 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, ++ /* 1080 */ 136, 136, 576, 82, 82, 1207, 137, 138, 91, 1340, ++ /* 1090 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, ++ /* 1100 */ 136, 136, 1569, 386, 377, 82, 82, 463, 1126, 1552, ++ /* 1110 */ 333, 463, 335, 131, 128, 451, 1569, 161, 377, 16, ++ /* 1120 */ 317, 387, 428, 1127, 448, 447, 134, 134, 134, 134, ++ /* 1130 */ 133, 133, 132, 132, 132, 131, 128, 451, 1128, 576, ++ /* 1140 */ 1105, 10, 445, 267, 576, 1554, 134, 134, 134, 134, ++ /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 532, 576, ++ /* 1160 */ 922, 576, 19, 19, 576, 1573, 576, 147, 147, 7, ++ /* 1170 */ 923, 1236, 498, 1236, 576, 487, 413, 552, 285, 1224, ++ /* 1180 */ 969, 215, 82, 82, 66, 66, 1435, 67, 67, 21, ++ /* 1190 */ 21, 1110, 1110, 495, 334, 297, 413, 53, 53, 297, ++ /* 1200 */ 137, 138, 91, 119, 1228, 1228, 1063, 1066, 1053, 1053, ++ /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 1336, 1311, 446, ++ /* 1220 */ 137, 138, 91, 227, 1228, 1228, 1063, 1066, 1053, 1053, ++ /* 1230 */ 135, 135, 136, 136, 136, 136, 574, 1224, 936, 936, ++ /* 1240 */ 137, 126, 91, 141, 1228, 1228, 1063, 1066, 1053, 1053, ++ /* 1250 */ 135, 135, 136, 136, 136, 136, 533, 429, 472, 346, ++ /* 1260 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, ++ /* 1270 */ 128, 451, 576, 457, 233, 343, 1435, 403, 498, 1550, ++ /* 1280 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, ++ /* 1290 */ 128, 451, 576, 324, 576, 82, 82, 487, 576, 969, ++ /* 1300 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, ++ /* 1310 */ 128, 451, 288, 288, 546, 68, 68, 54, 54, 553, ++ /* 1320 */ 413, 69, 69, 351, 6, 573, 944, 562, 410, 409, ++ /* 1330 */ 1435, 943, 450, 545, 260, 259, 258, 576, 158, 576, ++ /* 1340 */ 413, 222, 1180, 479, 969, 138, 91, 430, 1228, 1228, ++ /* 1350 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, ++ /* 1360 */ 70, 70, 71, 71, 576, 1126, 91, 576, 1228, 1228, ++ /* 1370 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, ++ /* 1380 */ 1127, 166, 850, 851, 852, 1282, 419, 72, 72, 108, ++ /* 1390 */ 73, 73, 1310, 358, 1180, 1128, 576, 305, 576, 123, ++ /* 1400 */ 568, 494, 4, 488, 134, 134, 134, 134, 133, 133, ++ /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 55, ++ /* 1420 */ 55, 56, 56, 576, 134, 134, 134, 134, 133, 133, ++ /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 1104, 233, 1104, ++ /* 1440 */ 452, 1602, 582, 2, 1259, 576, 57, 57, 576, 321, ++ /* 1450 */ 576, 155, 565, 1435, 485, 353, 576, 356, 1341, 59, ++ /* 1460 */ 59, 576, 44, 969, 569, 419, 576, 238, 60, 60, ++ /* 1470 */ 261, 74, 74, 75, 75, 287, 231, 576, 1366, 76, ++ /* 1480 */ 76, 1040, 420, 184, 20, 20, 576, 121, 121, 77, ++ /* 1490 */ 77, 97, 218, 288, 288, 122, 125, 452, 577, 452, ++ /* 1500 */ 143, 143, 1028, 576, 520, 576, 573, 576, 562, 144, ++ /* 1510 */ 144, 474, 227, 1244, 478, 123, 568, 576, 4, 320, ++ /* 1520 */ 567, 245, 411, 576, 443, 411, 78, 78, 62, 62, ++ /* 1530 */ 79, 79, 571, 319, 1028, 1028, 1030, 1031, 35, 418, ++ /* 1540 */ 63, 63, 576, 290, 411, 9, 80, 80, 1144, 576, ++ /* 1550 */ 400, 576, 486, 455, 576, 1223, 452, 576, 325, 342, ++ /* 1560 */ 576, 111, 576, 1188, 242, 64, 64, 473, 565, 576, ++ /* 1570 */ 23, 576, 170, 170, 171, 171, 576, 87, 87, 328, ++ /* 1580 */ 65, 65, 542, 83, 83, 146, 146, 541, 123, 568, ++ /* 1590 */ 341, 4, 84, 84, 168, 168, 576, 1040, 576, 148, ++ /* 1600 */ 148, 576, 1380, 121, 121, 571, 1021, 576, 266, 576, ++ /* 1610 */ 424, 122, 576, 452, 577, 452, 576, 553, 1028, 142, ++ /* 1620 */ 142, 169, 169, 576, 162, 162, 528, 889, 371, 452, ++ /* 1630 */ 152, 152, 151, 151, 1379, 149, 149, 109, 370, 150, ++ /* 1640 */ 150, 565, 576, 480, 576, 266, 86, 86, 576, 1092, ++ /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 482, 576, 266, 466, ++ /* 1660 */ 543, 123, 568, 1616, 4, 88, 88, 85, 85, 475, ++ /* 1670 */ 1040, 52, 52, 222, 901, 900, 121, 121, 571, 1188, ++ /* 1680 */ 58, 58, 244, 1032, 122, 889, 452, 577, 452, 908, ++ /* 1690 */ 909, 1028, 300, 347, 504, 111, 263, 361, 165, 111, ++ /* 1700 */ 111, 1088, 452, 263, 974, 1153, 266, 1092, 986, 987, ++ /* 1710 */ 942, 939, 125, 125, 565, 1103, 872, 1103, 159, 941, ++ /* 1720 */ 1309, 125, 1557, 1028, 1028, 1030, 1031, 35, 542, 337, ++ /* 1730 */ 1530, 205, 1529, 541, 499, 1589, 490, 348, 1376, 352, ++ /* 1740 */ 355, 1032, 357, 1040, 359, 1324, 1308, 366, 563, 121, ++ /* 1750 */ 121, 376, 1188, 1389, 1434, 1362, 280, 122, 1374, 452, ++ /* 1760 */ 577, 452, 167, 1439, 1028, 1289, 1280, 1268, 1267, 1269, ++ /* 1770 */ 1609, 1359, 312, 313, 314, 397, 12, 237, 224, 1421, ++ /* 1780 */ 295, 1416, 1409, 1426, 339, 484, 340, 509, 1371, 1612, ++ /* 1790 */ 1372, 1425, 1244, 404, 301, 228, 1028, 1028, 1030, 1031, ++ /* 1800 */ 35, 1601, 1192, 454, 345, 1307, 292, 369, 1502, 1501, ++ /* 1810 */ 270, 396, 396, 395, 277, 393, 1370, 1369, 859, 1549, ++ /* 1820 */ 186, 123, 568, 235, 4, 1188, 391, 210, 211, 223, ++ /* 1830 */ 1547, 239, 1241, 327, 422, 96, 220, 195, 571, 180, ++ /* 1840 */ 188, 326, 468, 469, 190, 191, 502, 192, 193, 566, ++ /* 1850 */ 247, 109, 1430, 491, 199, 251, 102, 281, 402, 476, ++ /* 1860 */ 405, 1496, 452, 497, 253, 1422, 13, 1428, 14, 1427, ++ /* 1870 */ 203, 1507, 241, 500, 565, 354, 407, 92, 95, 1270, ++ /* 1880 */ 175, 254, 518, 43, 1327, 255, 1326, 1325, 436, 1518, ++ /* 1890 */ 350, 1318, 104, 229, 893, 1626, 440, 441, 1625, 408, ++ /* 1900 */ 240, 1296, 268, 1040, 310, 269, 1297, 527, 444, 121, ++ /* 1910 */ 121, 368, 1295, 1594, 1624, 311, 1394, 122, 1317, 452, ++ /* 1920 */ 577, 452, 374, 1580, 1028, 1393, 140, 553, 11, 90, ++ /* 1930 */ 568, 385, 4, 116, 318, 414, 1579, 110, 1483, 537, ++ /* 1940 */ 320, 567, 1350, 555, 42, 579, 571, 1349, 1198, 383, ++ /* 1950 */ 276, 390, 216, 389, 278, 279, 1028, 1028, 1030, 1031, ++ /* 1960 */ 35, 172, 580, 1265, 458, 1260, 415, 416, 185, 156, ++ /* 1970 */ 452, 1534, 1535, 173, 1533, 1532, 89, 308, 225, 226, ++ /* 1980 */ 846, 174, 565, 453, 217, 1188, 322, 236, 1102, 154, ++ /* 1990 */ 1100, 330, 187, 176, 1223, 243, 189, 925, 338, 246, ++ /* 2000 */ 1116, 194, 177, 425, 178, 427, 98, 196, 99, 100, ++ /* 2010 */ 101, 1040, 179, 1119, 1115, 248, 249, 121, 121, 163, ++ /* 2020 */ 24, 250, 349, 1238, 496, 122, 1108, 452, 577, 452, ++ /* 2030 */ 1192, 454, 1028, 266, 292, 200, 252, 201, 861, 396, ++ /* 2040 */ 396, 395, 277, 393, 15, 501, 859, 370, 292, 256, ++ /* 2050 */ 202, 554, 505, 396, 396, 395, 277, 393, 103, 239, ++ /* 2060 */ 859, 327, 25, 26, 1028, 1028, 1030, 1031, 35, 326, ++ /* 2070 */ 362, 510, 891, 239, 365, 327, 513, 904, 105, 309, ++ /* 2080 */ 164, 181, 27, 326, 106, 521, 107, 1185, 1069, 1155, ++ /* 2090 */ 17, 1154, 230, 1188, 284, 286, 265, 204, 125, 1171, ++ /* 2100 */ 241, 28, 978, 972, 29, 41, 1175, 1179, 175, 1173, ++ /* 2110 */ 30, 43, 31, 8, 241, 1178, 32, 1160, 208, 549, ++ /* 2120 */ 33, 111, 175, 1083, 1070, 43, 1068, 1072, 240, 113, ++ /* 2130 */ 114, 34, 561, 118, 1124, 271, 1073, 36, 18, 572, ++ /* 2140 */ 1033, 873, 240, 124, 37, 935, 272, 273, 1617, 183, ++ /* 2150 */ 153, 394, 1194, 1193, 1256, 1256, 1256, 1256, 1256, 1256, ++ /* 2160 */ 1256, 1256, 1256, 414, 1256, 1256, 1256, 1256, 320, 567, ++ /* 2170 */ 1256, 1256, 1256, 1256, 1256, 1256, 1256, 414, 1256, 1256, ++ /* 2180 */ 1256, 1256, 320, 567, 1256, 1256, 1256, 1256, 1256, 1256, ++ /* 2190 */ 1256, 1256, 458, 1256, 1256, 1256, 1256, 1256, 1256, 1256, ++ /* 2200 */ 1256, 1256, 1256, 1256, 1256, 1256, 458, + }; + static const YYCODETYPE yy_lookahead[] = { +- /* 0 */ 189, 211, 189, 189, 218, 189, 220, 189, 267, 268, +- /* 10 */ 269, 189, 210, 189, 228, 189, 267, 268, 269, 19, +- /* 20 */ 218, 189, 211, 212, 211, 212, 211, 211, 212, 211, +- /* 30 */ 212, 31, 211, 211, 212, 211, 212, 288, 300, 39, +- /* 40 */ 21, 189, 304, 43, 44, 45, 46, 47, 48, 49, +- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 225, 19, +- /* 60 */ 189, 183, 184, 185, 186, 189, 248, 263, 236, 191, +- /* 70 */ 248, 193, 248, 197, 208, 257, 262, 201, 200, 257, +- /* 80 */ 200, 257, 81, 43, 44, 45, 46, 47, 48, 49, +- /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 189, 80, +- /* 100 */ 189, 101, 102, 103, 104, 105, 106, 107, 108, 109, +- /* 110 */ 110, 111, 234, 235, 234, 235, 305, 306, 305, 118, +- /* 120 */ 307, 305, 306, 297, 298, 247, 86, 247, 88, 19, +- /* 130 */ 259, 251, 252, 267, 268, 269, 26, 136, 137, 261, +- /* 140 */ 121, 101, 102, 103, 104, 105, 106, 107, 108, 109, +- /* 150 */ 110, 111, 59, 43, 44, 45, 46, 47, 48, 49, +- /* 160 */ 50, 51, 52, 53, 54, 55, 56, 57, 259, 291, +- /* 170 */ 105, 106, 107, 108, 109, 110, 111, 158, 189, 69, +- /* 180 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +- /* 190 */ 111, 107, 108, 109, 110, 111, 205, 206, 207, 19, +- /* 200 */ 19, 54, 55, 56, 57, 58, 29, 114, 115, 116, +- /* 210 */ 33, 101, 102, 103, 104, 105, 106, 107, 108, 109, +- /* 220 */ 110, 111, 233, 43, 44, 45, 46, 47, 48, 49, +- /* 230 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 126, +- /* 240 */ 127, 148, 65, 24, 214, 200, 59, 67, 101, 102, +- /* 250 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 22, +- /* 260 */ 189, 111, 43, 44, 45, 46, 47, 48, 49, 50, +- /* 270 */ 51, 52, 53, 54, 55, 56, 57, 206, 207, 234, +- /* 280 */ 235, 101, 102, 103, 104, 105, 106, 107, 108, 109, +- /* 290 */ 110, 111, 247, 76, 107, 114, 59, 267, 268, 269, +- /* 300 */ 189, 114, 115, 116, 162, 163, 89, 19, 263, 92, +- /* 310 */ 189, 23, 54, 55, 56, 57, 189, 206, 207, 22, +- /* 320 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +- /* 330 */ 111, 43, 44, 45, 46, 47, 48, 49, 50, 51, +- /* 340 */ 52, 53, 54, 55, 56, 57, 19, 189, 277, 59, +- /* 350 */ 23, 114, 115, 116, 46, 47, 48, 49, 61, 101, +- /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, +- /* 370 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, +- /* 380 */ 53, 54, 55, 56, 57, 125, 126, 127, 277, 101, +- /* 390 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, +- /* 400 */ 59, 189, 189, 276, 114, 115, 116, 117, 73, 59, +- /* 410 */ 120, 121, 122, 72, 214, 19, 81, 259, 19, 23, +- /* 420 */ 130, 81, 72, 24, 211, 212, 221, 119, 101, 102, +- /* 430 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 43, +- /* 440 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, +- /* 450 */ 54, 55, 56, 57, 19, 114, 115, 116, 23, 208, +- /* 460 */ 125, 248, 189, 189, 114, 115, 116, 267, 268, 269, +- /* 470 */ 189, 136, 137, 189, 262, 22, 136, 137, 43, 44, +- /* 480 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, +- /* 490 */ 55, 56, 57, 189, 95, 211, 212, 101, 102, 103, +- /* 500 */ 104, 105, 106, 107, 108, 109, 110, 111, 59, 189, +- /* 510 */ 111, 189, 59, 76, 294, 295, 117, 118, 119, 120, +- /* 520 */ 121, 122, 123, 19, 87, 189, 89, 23, 129, 92, +- /* 530 */ 279, 227, 248, 22, 189, 284, 101, 102, 103, 104, +- /* 540 */ 105, 106, 107, 108, 109, 110, 111, 43, 44, 45, +- /* 550 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, +- /* 560 */ 56, 57, 19, 114, 115, 116, 23, 114, 115, 116, +- /* 570 */ 59, 117, 299, 300, 120, 121, 122, 304, 189, 189, +- /* 580 */ 143, 189, 110, 111, 130, 22, 43, 44, 45, 46, +- /* 590 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, +- /* 600 */ 57, 211, 212, 211, 212, 101, 102, 103, 104, 105, +- /* 610 */ 106, 107, 108, 109, 110, 111, 226, 189, 226, 189, +- /* 620 */ 298, 132, 59, 134, 135, 114, 115, 116, 189, 59, +- /* 630 */ 285, 19, 7, 8, 9, 23, 205, 206, 207, 211, +- /* 640 */ 212, 211, 212, 221, 101, 102, 103, 104, 105, 106, +- /* 650 */ 107, 108, 109, 110, 111, 43, 44, 45, 46, 47, +- /* 660 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, +- /* 670 */ 19, 181, 182, 183, 184, 185, 186, 114, 115, 116, +- /* 680 */ 189, 191, 133, 193, 114, 115, 116, 138, 299, 300, +- /* 690 */ 200, 22, 201, 304, 43, 44, 45, 46, 47, 48, +- /* 700 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 35, +- /* 710 */ 189, 141, 189, 101, 102, 103, 104, 105, 106, 107, +- /* 720 */ 108, 109, 110, 111, 234, 235, 22, 23, 59, 184, +- /* 730 */ 26, 186, 211, 212, 211, 212, 191, 247, 193, 19, +- /* 740 */ 66, 105, 106, 73, 189, 200, 189, 226, 74, 226, +- /* 750 */ 22, 261, 101, 102, 103, 104, 105, 106, 107, 108, +- /* 760 */ 109, 110, 111, 43, 44, 45, 46, 47, 48, 49, +- /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 189, 234, +- /* 780 */ 235, 291, 19, 114, 115, 116, 150, 59, 152, 189, +- /* 790 */ 233, 236, 247, 59, 189, 125, 126, 127, 59, 300, +- /* 800 */ 211, 212, 128, 304, 100, 19, 261, 156, 45, 46, +- /* 810 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, +- /* 820 */ 57, 101, 102, 103, 104, 105, 106, 107, 108, 109, +- /* 830 */ 110, 111, 46, 233, 189, 189, 291, 248, 99, 189, +- /* 840 */ 125, 126, 127, 115, 26, 200, 289, 230, 231, 115, +- /* 850 */ 200, 16, 189, 114, 115, 189, 211, 212, 119, 221, +- /* 860 */ 189, 211, 212, 258, 101, 102, 103, 104, 105, 106, +- /* 870 */ 107, 108, 109, 110, 111, 189, 156, 211, 212, 234, +- /* 880 */ 235, 189, 211, 212, 234, 235, 22, 201, 189, 150, +- /* 890 */ 151, 152, 247, 248, 76, 16, 19, 247, 248, 113, +- /* 900 */ 189, 24, 257, 211, 212, 189, 26, 89, 262, 223, +- /* 910 */ 92, 225, 77, 189, 79, 129, 19, 53, 226, 248, +- /* 920 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, +- /* 930 */ 53, 54, 55, 56, 57, 236, 19, 271, 189, 99, +- /* 940 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, +- /* 950 */ 53, 54, 55, 56, 57, 115, 77, 59, 79, 119, +- /* 960 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, +- /* 970 */ 53, 54, 55, 56, 57, 259, 22, 23, 101, 102, +- /* 980 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 59, +- /* 990 */ 150, 151, 152, 158, 22, 244, 24, 246, 101, 102, +- /* 1000 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 285, +- /* 1010 */ 189, 189, 114, 115, 116, 200, 136, 137, 101, 102, +- /* 1020 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 230, +- /* 1030 */ 231, 59, 211, 212, 285, 105, 106, 189, 19, 141, +- /* 1040 */ 234, 235, 239, 113, 114, 115, 116, 226, 118, 234, +- /* 1050 */ 235, 189, 249, 247, 100, 189, 126, 23, 236, 107, +- /* 1060 */ 26, 189, 247, 44, 45, 46, 47, 48, 49, 50, +- /* 1070 */ 51, 52, 53, 54, 55, 56, 57, 211, 212, 59, +- /* 1080 */ 150, 233, 152, 211, 212, 133, 12, 115, 189, 189, +- /* 1090 */ 138, 19, 20, 300, 22, 233, 76, 304, 226, 11, +- /* 1100 */ 208, 27, 22, 23, 200, 19, 26, 87, 36, 89, +- /* 1110 */ 211, 212, 92, 300, 248, 189, 42, 304, 189, 250, +- /* 1120 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +- /* 1130 */ 111, 59, 200, 233, 114, 115, 116, 63, 234, 235, +- /* 1140 */ 235, 19, 20, 71, 22, 300, 189, 73, 200, 304, +- /* 1150 */ 116, 247, 247, 81, 189, 200, 227, 26, 36, 234, +- /* 1160 */ 235, 203, 204, 143, 200, 26, 234, 235, 194, 200, +- /* 1170 */ 48, 99, 247, 66, 189, 141, 284, 105, 106, 247, +- /* 1180 */ 100, 59, 234, 235, 112, 259, 114, 115, 116, 234, +- /* 1190 */ 235, 119, 85, 71, 266, 247, 211, 212, 234, 235, +- /* 1200 */ 114, 94, 247, 234, 235, 12, 266, 85, 136, 137, +- /* 1210 */ 189, 247, 90, 26, 126, 127, 247, 189, 26, 22, +- /* 1220 */ 27, 99, 150, 151, 152, 153, 154, 105, 106, 189, +- /* 1230 */ 302, 303, 211, 212, 112, 42, 114, 115, 116, 211, +- /* 1240 */ 212, 119, 302, 303, 19, 20, 189, 22, 274, 189, +- /* 1250 */ 15, 144, 278, 189, 22, 23, 63, 189, 189, 203, +- /* 1260 */ 204, 36, 136, 137, 155, 24, 157, 143, 211, 212, +- /* 1270 */ 189, 140, 150, 151, 152, 153, 154, 0, 1, 2, +- /* 1280 */ 211, 212, 5, 46, 59, 161, 147, 10, 11, 12, +- /* 1290 */ 13, 14, 211, 212, 17, 60, 71, 189, 258, 189, +- /* 1300 */ 59, 189, 105, 106, 189, 189, 189, 30, 116, 32, +- /* 1310 */ 85, 124, 189, 251, 252, 90, 189, 40, 258, 211, +- /* 1320 */ 212, 211, 212, 189, 99, 26, 211, 212, 211, 212, +- /* 1330 */ 105, 106, 100, 141, 211, 212, 119, 112, 189, 114, +- /* 1340 */ 115, 116, 23, 189, 119, 26, 129, 70, 189, 31, +- /* 1350 */ 113, 19, 20, 24, 22, 78, 115, 39, 81, 189, +- /* 1360 */ 211, 212, 26, 189, 22, 211, 212, 189, 36, 189, +- /* 1370 */ 211, 212, 189, 189, 97, 150, 151, 152, 153, 154, +- /* 1380 */ 127, 211, 212, 189, 189, 211, 212, 189, 189, 211, +- /* 1390 */ 212, 59, 189, 189, 211, 212, 23, 189, 22, 26, +- /* 1400 */ 24, 189, 149, 71, 189, 211, 212, 189, 131, 211, +- /* 1410 */ 212, 189, 189, 136, 137, 211, 212, 85, 189, 211, +- /* 1420 */ 212, 59, 90, 211, 212, 292, 293, 118, 119, 211, +- /* 1430 */ 212, 99, 23, 211, 212, 26, 159, 105, 106, 189, +- /* 1440 */ 211, 212, 143, 150, 112, 152, 114, 115, 116, 1, +- /* 1450 */ 2, 119, 23, 5, 23, 26, 189, 26, 10, 11, +- /* 1460 */ 12, 13, 14, 83, 84, 17, 253, 189, 139, 189, +- /* 1470 */ 19, 20, 189, 22, 189, 189, 140, 115, 30, 59, +- /* 1480 */ 32, 139, 150, 151, 152, 153, 154, 36, 40, 211, +- /* 1490 */ 212, 211, 212, 59, 211, 212, 211, 212, 7, 8, +- /* 1500 */ 19, 20, 189, 22, 150, 189, 152, 231, 281, 189, +- /* 1510 */ 59, 189, 23, 189, 189, 26, 189, 36, 70, 189, +- /* 1520 */ 23, 237, 71, 26, 211, 212, 78, 211, 212, 81, +- /* 1530 */ 189, 211, 212, 211, 212, 115, 211, 212, 211, 212, +- /* 1540 */ 59, 211, 212, 23, 23, 97, 26, 26, 23, 115, +- /* 1550 */ 99, 26, 71, 189, 189, 189, 105, 106, 107, 23, +- /* 1560 */ 189, 23, 26, 112, 26, 114, 115, 116, 189, 309, +- /* 1570 */ 119, 23, 19, 20, 26, 22, 189, 211, 212, 131, +- /* 1580 */ 99, 189, 211, 212, 136, 137, 105, 106, 189, 36, +- /* 1590 */ 211, 212, 189, 112, 189, 114, 115, 116, 211, 212, +- /* 1600 */ 119, 150, 151, 152, 153, 154, 189, 159, 23, 250, +- /* 1610 */ 189, 26, 59, 189, 189, 189, 189, 189, 280, 189, +- /* 1620 */ 250, 189, 189, 238, 71, 189, 189, 250, 211, 212, +- /* 1630 */ 187, 150, 151, 152, 153, 154, 211, 212, 250, 290, +- /* 1640 */ 240, 211, 212, 211, 212, 254, 286, 209, 254, 241, +- /* 1650 */ 240, 254, 99, 286, 215, 220, 214, 244, 105, 106, +- /* 1660 */ 214, 214, 244, 273, 224, 112, 192, 114, 115, 116, +- /* 1670 */ 60, 290, 119, 5, 139, 196, 196, 38, 10, 11, +- /* 1680 */ 12, 13, 14, 238, 240, 17, 196, 148, 287, 287, +- /* 1690 */ 276, 113, 22, 147, 241, 43, 229, 241, 30, 18, +- /* 1700 */ 32, 232, 232, 150, 151, 152, 153, 154, 40, 232, +- /* 1710 */ 232, 196, 18, 195, 265, 265, 264, 241, 264, 196, +- /* 1720 */ 155, 229, 229, 241, 241, 241, 195, 62, 196, 195, +- /* 1730 */ 22, 113, 216, 196, 222, 195, 195, 282, 70, 196, +- /* 1740 */ 283, 213, 216, 213, 64, 22, 78, 124, 219, 81, +- /* 1750 */ 162, 111, 219, 142, 256, 213, 113, 255, 213, 256, +- /* 1760 */ 216, 303, 215, 213, 213, 97, 255, 213, 216, 275, +- /* 1770 */ 275, 222, 216, 256, 255, 196, 91, 82, 256, 255, +- /* 1780 */ 308, 308, 146, 22, 143, 196, 155, 260, 25, 145, +- /* 1790 */ 144, 199, 26, 198, 13, 190, 190, 6, 293, 131, +- /* 1800 */ 188, 188, 245, 244, 136, 137, 245, 243, 242, 241, +- /* 1810 */ 188, 202, 208, 217, 217, 260, 208, 4, 202, 3, +- /* 1820 */ 22, 202, 208, 208, 160, 15, 209, 159, 270, 209, +- /* 1830 */ 98, 16, 272, 208, 23, 23, 137, 148, 128, 20, +- /* 1840 */ 140, 24, 16, 142, 1, 140, 149, 128, 61, 53, +- /* 1850 */ 148, 37, 53, 53, 53, 128, 114, 34, 296, 296, +- /* 1860 */ 139, 1, 5, 22, 113, 158, 26, 75, 41, 139, +- /* 1870 */ 68, 68, 113, 24, 20, 19, 129, 123, 23, 96, +- /* 1880 */ 22, 22, 37, 22, 22, 67, 22, 67, 59, 24, +- /* 1890 */ 23, 28, 67, 147, 22, 26, 23, 23, 23, 23, +- /* 1900 */ 22, 24, 23, 22, 24, 23, 139, 23, 114, 22, +- /* 1910 */ 141, 26, 88, 75, 86, 44, 23, 34, 22, 75, +- /* 1920 */ 34, 24, 34, 34, 34, 93, 34, 26, 26, 34, +- /* 1930 */ 23, 23, 23, 23, 23, 11, 23, 22, 26, 22, +- /* 1940 */ 22, 133, 23, 23, 22, 22, 139, 26, 139, 23, +- /* 1950 */ 15, 1, 1, 310, 310, 310, 310, 310, 310, 310, +- /* 1960 */ 139, 139, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 1970 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 1980 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 1990 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2000 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2010 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2020 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2030 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2040 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2050 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2060 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2070 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2080 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2090 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2100 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2110 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2120 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2130 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, +- /* 2140 */ 310, 310, 310, ++ /* 0 */ 277, 278, 279, 241, 242, 225, 195, 227, 195, 241, ++ /* 10 */ 242, 195, 217, 221, 195, 235, 254, 195, 256, 19, ++ /* 20 */ 225, 298, 254, 195, 256, 206, 213, 214, 206, 218, ++ /* 30 */ 219, 31, 206, 195, 218, 219, 195, 218, 219, 39, ++ /* 40 */ 218, 219, 313, 43, 44, 45, 317, 47, 48, 49, ++ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 19, ++ /* 60 */ 241, 242, 195, 241, 242, 195, 255, 241, 242, 277, ++ /* 70 */ 278, 279, 234, 254, 255, 256, 254, 255, 256, 218, ++ /* 80 */ 254, 240, 256, 43, 44, 45, 264, 47, 48, 49, ++ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 271, ++ /* 100 */ 287, 22, 23, 103, 104, 105, 106, 107, 108, 109, ++ /* 110 */ 110, 111, 112, 113, 114, 114, 47, 48, 49, 50, ++ /* 120 */ 187, 188, 189, 190, 191, 192, 190, 87, 192, 89, ++ /* 130 */ 197, 19, 199, 197, 318, 199, 320, 25, 195, 206, ++ /* 140 */ 299, 271, 206, 103, 104, 105, 106, 107, 108, 109, ++ /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 195, 47, ++ /* 160 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, ++ /* 170 */ 58, 60, 21, 195, 241, 242, 215, 241, 242, 312, ++ /* 180 */ 313, 102, 70, 205, 317, 207, 242, 254, 77, 256, ++ /* 190 */ 254, 122, 256, 55, 56, 57, 58, 59, 254, 88, ++ /* 200 */ 256, 90, 269, 240, 93, 269, 107, 108, 109, 110, ++ /* 210 */ 111, 112, 113, 114, 271, 103, 104, 105, 106, 107, ++ /* 220 */ 108, 109, 110, 111, 112, 113, 114, 313, 117, 118, ++ /* 230 */ 119, 317, 81, 195, 301, 19, 195, 301, 277, 278, ++ /* 240 */ 279, 103, 104, 105, 106, 107, 108, 109, 110, 111, ++ /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 195, 43, ++ /* 260 */ 44, 45, 74, 47, 48, 49, 50, 51, 52, 53, ++ /* 270 */ 54, 55, 56, 57, 58, 124, 195, 60, 109, 110, ++ /* 280 */ 111, 112, 113, 114, 68, 195, 103, 104, 105, 106, ++ /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 208, 218, ++ /* 300 */ 219, 103, 104, 105, 106, 107, 108, 109, 110, 111, ++ /* 310 */ 112, 113, 114, 162, 233, 24, 128, 129, 130, 103, ++ /* 320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, ++ /* 330 */ 114, 195, 195, 215, 117, 118, 119, 120, 195, 19, ++ /* 340 */ 123, 124, 125, 207, 24, 74, 246, 60, 310, 311, ++ /* 350 */ 133, 60, 311, 82, 22, 218, 219, 257, 195, 19, ++ /* 360 */ 73, 218, 219, 43, 44, 45, 206, 47, 48, 49, ++ /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 22, ++ /* 380 */ 23, 218, 219, 43, 44, 45, 54, 47, 48, 49, ++ /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 128, ++ /* 400 */ 82, 241, 242, 195, 117, 118, 119, 289, 60, 118, ++ /* 410 */ 139, 140, 294, 195, 254, 195, 256, 195, 255, 259, ++ /* 420 */ 260, 73, 22, 103, 104, 105, 106, 107, 108, 109, ++ /* 430 */ 110, 111, 112, 113, 114, 206, 218, 219, 218, 219, ++ /* 440 */ 218, 219, 234, 103, 104, 105, 106, 107, 108, 109, ++ /* 450 */ 110, 111, 112, 113, 114, 318, 319, 139, 140, 102, ++ /* 460 */ 60, 318, 319, 221, 19, 117, 118, 119, 23, 195, ++ /* 470 */ 241, 242, 313, 255, 206, 255, 317, 255, 206, 129, ++ /* 480 */ 130, 206, 264, 254, 264, 256, 264, 195, 43, 44, ++ /* 490 */ 45, 151, 47, 48, 49, 50, 51, 52, 53, 54, ++ /* 500 */ 55, 56, 57, 58, 246, 213, 214, 19, 19, 241, ++ /* 510 */ 242, 195, 23, 241, 242, 257, 241, 242, 118, 277, ++ /* 520 */ 278, 279, 254, 29, 256, 60, 254, 33, 256, 254, ++ /* 530 */ 206, 256, 43, 44, 45, 218, 47, 48, 49, 50, ++ /* 540 */ 51, 52, 53, 54, 55, 56, 57, 58, 103, 104, ++ /* 550 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, ++ /* 560 */ 66, 19, 218, 60, 120, 241, 242, 123, 124, 125, ++ /* 570 */ 60, 232, 77, 19, 20, 26, 22, 133, 254, 287, ++ /* 580 */ 256, 265, 117, 118, 119, 90, 312, 313, 93, 47, ++ /* 590 */ 36, 317, 103, 104, 105, 106, 107, 108, 109, 110, ++ /* 600 */ 111, 112, 113, 114, 116, 117, 277, 278, 279, 60, ++ /* 610 */ 107, 108, 19, 276, 60, 31, 23, 152, 195, 116, ++ /* 620 */ 117, 118, 119, 39, 121, 276, 72, 117, 118, 119, ++ /* 630 */ 166, 167, 129, 145, 237, 238, 43, 44, 45, 276, ++ /* 640 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, ++ /* 650 */ 57, 58, 315, 316, 144, 101, 19, 154, 116, 156, ++ /* 660 */ 23, 107, 108, 109, 315, 316, 117, 118, 119, 115, ++ /* 670 */ 60, 117, 118, 119, 132, 200, 122, 60, 315, 316, ++ /* 680 */ 43, 44, 45, 272, 47, 48, 49, 50, 51, 52, ++ /* 690 */ 53, 54, 55, 56, 57, 58, 103, 104, 105, 106, ++ /* 700 */ 107, 108, 109, 110, 111, 112, 113, 114, 154, 155, ++ /* 710 */ 156, 157, 158, 212, 213, 214, 22, 195, 101, 22, ++ /* 720 */ 60, 19, 20, 60, 22, 139, 140, 117, 118, 119, ++ /* 730 */ 22, 251, 195, 253, 117, 118, 195, 183, 36, 122, ++ /* 740 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, ++ /* 750 */ 113, 114, 195, 195, 60, 218, 219, 60, 195, 284, ++ /* 760 */ 19, 25, 60, 288, 23, 237, 238, 22, 60, 109, ++ /* 770 */ 233, 154, 155, 156, 72, 218, 219, 117, 118, 119, ++ /* 780 */ 117, 118, 119, 116, 43, 44, 45, 265, 47, 48, ++ /* 790 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ++ /* 800 */ 183, 243, 25, 101, 19, 60, 265, 144, 23, 107, ++ /* 810 */ 108, 117, 118, 119, 117, 118, 119, 115, 151, 117, ++ /* 820 */ 118, 119, 82, 195, 122, 117, 118, 119, 43, 44, ++ /* 830 */ 45, 195, 47, 48, 49, 50, 51, 52, 53, 54, ++ /* 840 */ 55, 56, 57, 58, 103, 104, 105, 106, 107, 108, ++ /* 850 */ 109, 110, 111, 112, 113, 114, 154, 155, 156, 157, ++ /* 860 */ 158, 121, 117, 118, 119, 307, 101, 309, 195, 22, ++ /* 870 */ 23, 195, 25, 19, 35, 139, 140, 195, 24, 139, ++ /* 880 */ 140, 208, 195, 118, 109, 183, 22, 122, 103, 104, ++ /* 890 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, ++ /* 900 */ 304, 305, 77, 230, 127, 232, 67, 195, 19, 195, ++ /* 910 */ 195, 136, 23, 88, 75, 90, 141, 203, 93, 154, ++ /* 920 */ 155, 156, 208, 295, 60, 243, 22, 23, 19, 25, ++ /* 930 */ 218, 219, 43, 44, 45, 100, 47, 48, 49, 50, ++ /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 183, 102, ++ /* 950 */ 96, 195, 43, 44, 45, 240, 47, 48, 49, 50, ++ /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 134, ++ /* 970 */ 131, 146, 25, 286, 120, 121, 122, 123, 124, 125, ++ /* 980 */ 126, 117, 118, 119, 313, 195, 132, 195, 317, 307, ++ /* 990 */ 195, 309, 103, 104, 105, 106, 107, 108, 109, 110, ++ /* 1000 */ 111, 112, 113, 114, 195, 195, 102, 195, 195, 195, ++ /* 1010 */ 218, 219, 103, 104, 105, 106, 107, 108, 109, 110, ++ /* 1020 */ 111, 112, 113, 114, 77, 233, 195, 60, 218, 219, ++ /* 1030 */ 218, 219, 218, 219, 23, 195, 25, 90, 243, 159, ++ /* 1040 */ 93, 161, 19, 233, 195, 233, 23, 233, 16, 218, ++ /* 1050 */ 219, 195, 243, 212, 213, 214, 262, 263, 218, 219, ++ /* 1060 */ 195, 271, 19, 307, 233, 309, 43, 44, 45, 160, ++ /* 1070 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, ++ /* 1080 */ 57, 58, 195, 218, 219, 118, 43, 44, 45, 240, ++ /* 1090 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, ++ /* 1100 */ 57, 58, 307, 195, 309, 218, 219, 263, 12, 195, ++ /* 1110 */ 78, 267, 80, 112, 113, 114, 307, 22, 309, 24, ++ /* 1120 */ 255, 281, 266, 27, 107, 108, 103, 104, 105, 106, ++ /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 195, ++ /* 1140 */ 11, 22, 255, 24, 195, 195, 103, 104, 105, 106, ++ /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 19, 195, ++ /* 1160 */ 64, 195, 218, 219, 195, 313, 195, 218, 219, 317, ++ /* 1170 */ 74, 154, 195, 156, 195, 195, 19, 233, 23, 60, ++ /* 1180 */ 25, 24, 218, 219, 218, 219, 195, 218, 219, 218, ++ /* 1190 */ 219, 128, 129, 130, 162, 263, 19, 218, 219, 267, ++ /* 1200 */ 43, 44, 45, 160, 47, 48, 49, 50, 51, 52, ++ /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 240, 228, 255, ++ /* 1220 */ 43, 44, 45, 25, 47, 48, 49, 50, 51, 52, ++ /* 1230 */ 53, 54, 55, 56, 57, 58, 135, 118, 137, 138, ++ /* 1240 */ 43, 44, 45, 22, 47, 48, 49, 50, 51, 52, ++ /* 1250 */ 53, 54, 55, 56, 57, 58, 117, 266, 129, 130, ++ /* 1260 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, ++ /* 1270 */ 113, 114, 195, 195, 119, 295, 195, 206, 195, 195, ++ /* 1280 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, ++ /* 1290 */ 113, 114, 195, 195, 195, 218, 219, 195, 195, 144, ++ /* 1300 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, ++ /* 1310 */ 113, 114, 241, 242, 67, 218, 219, 218, 219, 146, ++ /* 1320 */ 19, 218, 219, 240, 215, 254, 136, 256, 107, 108, ++ /* 1330 */ 195, 141, 255, 86, 128, 129, 130, 195, 165, 195, ++ /* 1340 */ 19, 143, 95, 272, 25, 44, 45, 266, 47, 48, ++ /* 1350 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ++ /* 1360 */ 218, 219, 218, 219, 195, 12, 45, 195, 47, 48, ++ /* 1370 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ++ /* 1380 */ 27, 23, 7, 8, 9, 210, 211, 218, 219, 116, ++ /* 1390 */ 218, 219, 228, 16, 147, 42, 195, 295, 195, 19, ++ /* 1400 */ 20, 266, 22, 294, 103, 104, 105, 106, 107, 108, ++ /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 218, ++ /* 1420 */ 219, 218, 219, 195, 103, 104, 105, 106, 107, 108, ++ /* 1430 */ 109, 110, 111, 112, 113, 114, 195, 154, 119, 156, ++ /* 1440 */ 60, 189, 190, 191, 192, 195, 218, 219, 195, 197, ++ /* 1450 */ 195, 199, 72, 195, 19, 78, 195, 80, 206, 218, ++ /* 1460 */ 219, 195, 82, 144, 210, 211, 195, 15, 218, 219, ++ /* 1470 */ 47, 218, 219, 218, 219, 259, 260, 195, 261, 218, ++ /* 1480 */ 219, 101, 302, 303, 218, 219, 195, 107, 108, 218, ++ /* 1490 */ 219, 150, 151, 241, 242, 115, 25, 117, 118, 119, ++ /* 1500 */ 218, 219, 122, 195, 146, 195, 254, 195, 256, 218, ++ /* 1510 */ 219, 246, 25, 61, 246, 19, 20, 195, 22, 139, ++ /* 1520 */ 140, 269, 257, 195, 266, 257, 218, 219, 218, 219, ++ /* 1530 */ 218, 219, 36, 246, 154, 155, 156, 157, 158, 116, ++ /* 1540 */ 218, 219, 195, 22, 257, 49, 218, 219, 23, 195, ++ /* 1550 */ 25, 195, 117, 301, 195, 25, 60, 195, 195, 23, ++ /* 1560 */ 195, 25, 195, 183, 24, 218, 219, 130, 72, 195, ++ /* 1570 */ 22, 195, 218, 219, 218, 219, 195, 218, 219, 195, ++ /* 1580 */ 218, 219, 86, 218, 219, 218, 219, 91, 19, 20, ++ /* 1590 */ 153, 22, 218, 219, 218, 219, 195, 101, 195, 218, ++ /* 1600 */ 219, 195, 195, 107, 108, 36, 23, 195, 25, 195, ++ /* 1610 */ 62, 115, 195, 117, 118, 119, 195, 146, 122, 218, ++ /* 1620 */ 219, 218, 219, 195, 218, 219, 19, 60, 122, 60, ++ /* 1630 */ 218, 219, 218, 219, 195, 218, 219, 150, 132, 218, ++ /* 1640 */ 219, 72, 195, 23, 195, 25, 218, 219, 195, 60, ++ /* 1650 */ 154, 155, 156, 157, 158, 86, 23, 195, 25, 195, ++ /* 1660 */ 91, 19, 20, 142, 22, 218, 219, 218, 219, 130, ++ /* 1670 */ 101, 218, 219, 143, 121, 122, 107, 108, 36, 183, ++ /* 1680 */ 218, 219, 142, 60, 115, 118, 117, 118, 119, 7, ++ /* 1690 */ 8, 122, 153, 23, 23, 25, 25, 23, 23, 25, ++ /* 1700 */ 25, 23, 60, 25, 23, 98, 25, 118, 84, 85, ++ /* 1710 */ 23, 23, 25, 25, 72, 154, 23, 156, 25, 23, ++ /* 1720 */ 228, 25, 195, 154, 155, 156, 157, 158, 86, 195, ++ /* 1730 */ 195, 258, 195, 91, 291, 322, 195, 195, 195, 195, ++ /* 1740 */ 195, 118, 195, 101, 195, 195, 195, 195, 238, 107, ++ /* 1750 */ 108, 195, 183, 195, 195, 195, 290, 115, 195, 117, ++ /* 1760 */ 118, 119, 244, 195, 122, 195, 195, 195, 195, 195, ++ /* 1770 */ 195, 258, 258, 258, 258, 193, 245, 300, 216, 274, ++ /* 1780 */ 247, 270, 270, 274, 296, 296, 248, 222, 262, 198, ++ /* 1790 */ 262, 274, 61, 274, 248, 231, 154, 155, 156, 157, ++ /* 1800 */ 158, 0, 1, 2, 247, 227, 5, 221, 221, 221, ++ /* 1810 */ 142, 10, 11, 12, 13, 14, 262, 262, 17, 202, ++ /* 1820 */ 300, 19, 20, 300, 22, 183, 247, 251, 251, 245, ++ /* 1830 */ 202, 30, 38, 32, 202, 152, 151, 22, 36, 43, ++ /* 1840 */ 236, 40, 18, 202, 239, 239, 18, 239, 239, 283, ++ /* 1850 */ 201, 150, 236, 202, 236, 201, 159, 202, 248, 248, ++ /* 1860 */ 248, 248, 60, 63, 201, 275, 273, 275, 273, 275, ++ /* 1870 */ 22, 286, 71, 223, 72, 202, 223, 297, 297, 202, ++ /* 1880 */ 79, 201, 116, 82, 220, 201, 220, 220, 65, 293, ++ /* 1890 */ 292, 229, 22, 166, 127, 226, 24, 114, 226, 223, ++ /* 1900 */ 99, 222, 202, 101, 285, 92, 220, 308, 83, 107, ++ /* 1910 */ 108, 220, 220, 316, 220, 285, 268, 115, 229, 117, ++ /* 1920 */ 118, 119, 223, 321, 122, 268, 149, 146, 22, 19, ++ /* 1930 */ 20, 202, 22, 159, 282, 134, 321, 148, 280, 147, ++ /* 1940 */ 139, 140, 252, 141, 25, 204, 36, 252, 13, 251, ++ /* 1950 */ 196, 248, 250, 249, 196, 6, 154, 155, 156, 157, ++ /* 1960 */ 158, 209, 194, 194, 163, 194, 306, 306, 303, 224, ++ /* 1970 */ 60, 215, 215, 209, 215, 215, 215, 224, 216, 216, ++ /* 1980 */ 4, 209, 72, 3, 22, 183, 164, 15, 23, 16, ++ /* 1990 */ 23, 140, 152, 131, 25, 24, 143, 20, 16, 145, ++ /* 2000 */ 1, 143, 131, 62, 131, 37, 54, 152, 54, 54, ++ /* 2010 */ 54, 101, 131, 117, 1, 34, 142, 107, 108, 5, ++ /* 2020 */ 22, 116, 162, 76, 41, 115, 69, 117, 118, 119, ++ /* 2030 */ 1, 2, 122, 25, 5, 69, 142, 116, 20, 10, ++ /* 2040 */ 11, 12, 13, 14, 24, 19, 17, 132, 5, 126, ++ /* 2050 */ 22, 141, 68, 10, 11, 12, 13, 14, 22, 30, ++ /* 2060 */ 17, 32, 22, 22, 154, 155, 156, 157, 158, 40, ++ /* 2070 */ 23, 68, 60, 30, 24, 32, 97, 28, 22, 68, ++ /* 2080 */ 23, 37, 34, 40, 150, 22, 25, 23, 23, 23, ++ /* 2090 */ 22, 98, 142, 183, 23, 23, 34, 22, 25, 89, ++ /* 2100 */ 71, 34, 117, 144, 34, 22, 76, 76, 79, 87, ++ /* 2110 */ 34, 82, 34, 44, 71, 94, 34, 23, 25, 24, ++ /* 2120 */ 34, 25, 79, 23, 23, 82, 23, 23, 99, 143, ++ /* 2130 */ 143, 22, 25, 25, 23, 22, 11, 22, 22, 25, ++ /* 2140 */ 23, 23, 99, 22, 22, 136, 142, 142, 142, 25, ++ /* 2150 */ 23, 15, 1, 1, 323, 323, 323, 323, 323, 323, ++ /* 2160 */ 323, 323, 323, 134, 323, 323, 323, 323, 139, 140, ++ /* 2170 */ 323, 323, 323, 323, 323, 323, 323, 134, 323, 323, ++ /* 2180 */ 323, 323, 139, 140, 323, 323, 323, 323, 323, 323, ++ /* 2190 */ 323, 323, 163, 323, 323, 323, 323, 323, 323, 323, ++ /* 2200 */ 323, 323, 323, 323, 323, 323, 163, 323, 323, 323, ++ /* 2210 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2220 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2230 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2240 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2250 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2260 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2270 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2280 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2290 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2300 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2310 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2320 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2330 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, ++ /* 2340 */ 323, 187, 187, 187, 187, 187, 187, 187, 187, 187, ++ /* 2350 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, ++ /* 2360 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, ++ /* 2370 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, ++ /* 2380 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, ++ /* 2390 */ 187, 187, 187, 187, + }; +-#define YY_SHIFT_COUNT (552) ++#define YY_SHIFT_COUNT (582) + #define YY_SHIFT_MIN (0) +-#define YY_SHIFT_MAX (1951) ++#define YY_SHIFT_MAX (2152) + static const unsigned short int yy_shift_ofst[] = { +- /* 0 */ 1448, 1277, 1668, 1072, 1072, 340, 1122, 1225, 1332, 1481, +- /* 10 */ 1481, 1481, 335, 0, 0, 180, 897, 1481, 1481, 1481, +- /* 20 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, +- /* 30 */ 930, 930, 1020, 1020, 290, 1, 340, 340, 340, 340, +- /* 40 */ 340, 340, 40, 110, 219, 288, 327, 396, 435, 504, +- /* 50 */ 543, 612, 651, 720, 877, 897, 897, 897, 897, 897, +- /* 60 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, +- /* 70 */ 897, 897, 897, 917, 897, 1019, 763, 763, 1451, 1481, +- /* 80 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, +- /* 90 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, +- /* 100 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, +- /* 110 */ 1481, 1481, 1553, 1481, 1481, 1481, 1481, 1481, 1481, 1481, +- /* 120 */ 1481, 1481, 1481, 1481, 1481, 1481, 147, 258, 258, 258, +- /* 130 */ 258, 258, 79, 65, 84, 449, 19, 786, 449, 636, +- /* 140 */ 636, 449, 880, 880, 880, 880, 113, 142, 142, 472, +- /* 150 */ 150, 1962, 1962, 399, 399, 399, 93, 237, 341, 237, +- /* 160 */ 237, 1074, 1074, 437, 350, 704, 1080, 449, 449, 449, +- /* 170 */ 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, +- /* 180 */ 449, 449, 449, 449, 449, 449, 449, 449, 818, 818, +- /* 190 */ 449, 1088, 217, 217, 734, 734, 1124, 1126, 1962, 1962, +- /* 200 */ 1962, 739, 840, 840, 453, 454, 511, 187, 563, 570, +- /* 210 */ 898, 669, 449, 449, 449, 449, 449, 449, 449, 449, +- /* 220 */ 449, 670, 449, 449, 449, 449, 449, 449, 449, 449, +- /* 230 */ 449, 449, 449, 449, 674, 674, 674, 449, 449, 449, +- /* 240 */ 449, 1034, 449, 449, 449, 972, 1107, 449, 449, 1193, +- /* 250 */ 449, 449, 449, 449, 449, 449, 449, 449, 260, 177, +- /* 260 */ 489, 1241, 1241, 1241, 1241, 1192, 489, 489, 952, 1197, +- /* 270 */ 625, 1235, 1131, 181, 181, 1086, 1139, 1131, 1086, 1187, +- /* 280 */ 1319, 1237, 1318, 1318, 1318, 181, 1299, 1299, 1109, 1336, +- /* 290 */ 549, 1376, 1610, 1535, 1535, 1639, 1639, 1535, 1539, 1578, +- /* 300 */ 1670, 1546, 1652, 1546, 1681, 1681, 1681, 1681, 1535, 1694, +- /* 310 */ 1546, 1546, 1578, 1670, 1652, 1546, 1652, 1546, 1535, 1694, +- /* 320 */ 1565, 1665, 1535, 1694, 1708, 1535, 1694, 1535, 1694, 1708, +- /* 330 */ 1618, 1618, 1618, 1680, 1723, 1723, 1708, 1618, 1623, 1618, +- /* 340 */ 1680, 1618, 1618, 1588, 1708, 1640, 1640, 1708, 1611, 1643, +- /* 350 */ 1611, 1643, 1611, 1643, 1611, 1643, 1535, 1685, 1685, 1695, +- /* 360 */ 1695, 1636, 1641, 1761, 1535, 1631, 1636, 1644, 1646, 1546, +- /* 370 */ 1763, 1766, 1781, 1781, 1791, 1791, 1791, 1962, 1962, 1962, +- /* 380 */ 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, +- /* 390 */ 1962, 1962, 308, 835, 954, 1232, 879, 715, 728, 1373, +- /* 400 */ 864, 1329, 1253, 1409, 297, 1431, 1489, 1497, 1520, 1521, +- /* 410 */ 1525, 1362, 1309, 1491, 1217, 1420, 1429, 1536, 1380, 1538, +- /* 420 */ 1293, 1354, 1548, 1585, 1434, 1342, 1813, 1816, 1798, 1664, +- /* 430 */ 1810, 1732, 1815, 1811, 1812, 1699, 1689, 1710, 1817, 1700, +- /* 440 */ 1819, 1701, 1826, 1843, 1705, 1697, 1719, 1787, 1814, 1702, +- /* 450 */ 1796, 1799, 1800, 1801, 1727, 1742, 1823, 1721, 1860, 1857, +- /* 460 */ 1841, 1751, 1707, 1802, 1840, 1803, 1792, 1827, 1730, 1759, +- /* 470 */ 1849, 1854, 1856, 1747, 1754, 1858, 1818, 1859, 1861, 1855, +- /* 480 */ 1862, 1820, 1829, 1865, 1783, 1863, 1864, 1825, 1845, 1867, +- /* 490 */ 1746, 1872, 1873, 1874, 1875, 1869, 1876, 1878, 1877, 1879, +- /* 500 */ 1881, 1880, 1767, 1882, 1884, 1794, 1883, 1887, 1769, 1885, +- /* 510 */ 1886, 1888, 1889, 1890, 1824, 1838, 1828, 1871, 1844, 1832, +- /* 520 */ 1892, 1893, 1896, 1897, 1901, 1902, 1895, 1907, 1885, 1908, +- /* 530 */ 1909, 1910, 1911, 1912, 1913, 1915, 1924, 1917, 1918, 1919, +- /* 540 */ 1920, 1922, 1923, 1921, 1808, 1807, 1809, 1821, 1822, 1926, +- /* 550 */ 1935, 1950, 1951, ++ /* 0 */ 2029, 1801, 2043, 1380, 1380, 318, 271, 1496, 1569, 1642, ++ /* 10 */ 702, 702, 702, 740, 318, 318, 318, 318, 318, 0, ++ /* 20 */ 0, 216, 1177, 702, 702, 702, 702, 702, 702, 702, ++ /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 503, 503, ++ /* 40 */ 111, 111, 217, 287, 348, 610, 610, 736, 736, 736, ++ /* 50 */ 736, 40, 112, 320, 340, 445, 489, 593, 637, 741, ++ /* 60 */ 785, 889, 909, 1023, 1043, 1157, 1177, 1177, 1177, 1177, ++ /* 70 */ 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, ++ /* 80 */ 1177, 1177, 1177, 1177, 1197, 1177, 1301, 1321, 1321, 554, ++ /* 90 */ 1802, 1910, 702, 702, 702, 702, 702, 702, 702, 702, ++ /* 100 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, ++ /* 110 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, ++ /* 120 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, ++ /* 130 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, ++ /* 140 */ 702, 702, 138, 198, 198, 198, 198, 198, 198, 198, ++ /* 150 */ 183, 99, 169, 549, 610, 151, 542, 610, 610, 1017, ++ /* 160 */ 1017, 610, 1001, 350, 464, 464, 464, 586, 1, 1, ++ /* 170 */ 2207, 2207, 854, 854, 854, 465, 694, 694, 694, 694, ++ /* 180 */ 1096, 1096, 825, 549, 847, 904, 610, 610, 610, 610, ++ /* 190 */ 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, ++ /* 200 */ 610, 610, 610, 610, 610, 488, 947, 947, 610, 1129, ++ /* 210 */ 495, 495, 1139, 1139, 967, 967, 1173, 2207, 2207, 2207, ++ /* 220 */ 2207, 2207, 2207, 2207, 617, 765, 765, 697, 444, 708, ++ /* 230 */ 660, 745, 510, 663, 864, 610, 610, 610, 610, 610, ++ /* 240 */ 610, 610, 610, 610, 610, 188, 610, 610, 610, 610, ++ /* 250 */ 610, 610, 610, 610, 610, 610, 610, 610, 839, 839, ++ /* 260 */ 839, 610, 610, 610, 1155, 610, 610, 610, 1119, 1247, ++ /* 270 */ 610, 1353, 610, 610, 610, 610, 610, 610, 610, 610, ++ /* 280 */ 1063, 494, 1101, 291, 291, 291, 291, 1319, 1101, 1101, ++ /* 290 */ 775, 1221, 1375, 1452, 667, 1341, 1198, 1341, 1435, 1487, ++ /* 300 */ 667, 667, 1487, 667, 1198, 1435, 777, 1011, 1423, 584, ++ /* 310 */ 584, 584, 1273, 1273, 1273, 1273, 1471, 1471, 880, 1530, ++ /* 320 */ 1190, 1095, 1731, 1731, 1668, 1668, 1794, 1794, 1668, 1683, ++ /* 330 */ 1685, 1815, 1796, 1824, 1824, 1824, 1824, 1668, 1828, 1701, ++ /* 340 */ 1685, 1685, 1701, 1815, 1796, 1701, 1796, 1701, 1668, 1828, ++ /* 350 */ 1697, 1800, 1668, 1828, 1848, 1668, 1828, 1668, 1828, 1848, ++ /* 360 */ 1766, 1766, 1766, 1823, 1870, 1870, 1848, 1766, 1767, 1766, ++ /* 370 */ 1823, 1766, 1766, 1727, 1872, 1783, 1783, 1848, 1668, 1813, ++ /* 380 */ 1813, 1825, 1825, 1777, 1781, 1906, 1668, 1774, 1777, 1789, ++ /* 390 */ 1792, 1701, 1919, 1935, 1935, 1949, 1949, 1949, 2207, 2207, ++ /* 400 */ 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, ++ /* 410 */ 2207, 2207, 2207, 69, 1032, 79, 357, 1377, 1206, 400, ++ /* 420 */ 1525, 835, 332, 1540, 1437, 1539, 1536, 1548, 1583, 1620, ++ /* 430 */ 1633, 1670, 1671, 1674, 1567, 1553, 1682, 1506, 1675, 1358, ++ /* 440 */ 1607, 1589, 1678, 1681, 1624, 1687, 1688, 1283, 1561, 1693, ++ /* 450 */ 1696, 1623, 1521, 1976, 1980, 1962, 1822, 1972, 1973, 1965, ++ /* 460 */ 1967, 1851, 1840, 1862, 1969, 1969, 1971, 1853, 1977, 1854, ++ /* 470 */ 1982, 1999, 1858, 1871, 1969, 1873, 1941, 1968, 1969, 1855, ++ /* 480 */ 1952, 1954, 1955, 1956, 1881, 1896, 1981, 1874, 2013, 2014, ++ /* 490 */ 1998, 1905, 1860, 1957, 2008, 1966, 1947, 1983, 1894, 1921, ++ /* 500 */ 2020, 2018, 2026, 1915, 1923, 2028, 1984, 2036, 2040, 2047, ++ /* 510 */ 2041, 2003, 2012, 2050, 1979, 2049, 2056, 2011, 2044, 2057, ++ /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1950, ++ /* 530 */ 2071, 2072, 1985, 2062, 2075, 1959, 2073, 2067, 2070, 2076, ++ /* 540 */ 2078, 2010, 2030, 2022, 2069, 2031, 2021, 2082, 2094, 2083, ++ /* 550 */ 2095, 2093, 2096, 2086, 1986, 1987, 2100, 2073, 2101, 2103, ++ /* 560 */ 2104, 2109, 2107, 2108, 2111, 2113, 2125, 2115, 2116, 2117, ++ /* 570 */ 2118, 2121, 2122, 2114, 2009, 2004, 2005, 2006, 2124, 2127, ++ /* 580 */ 2136, 2151, 2152, + }; +-#define YY_REDUCE_COUNT (391) +-#define YY_REDUCE_MIN (-262) +-#define YY_REDUCE_MAX (1625) ++#define YY_REDUCE_COUNT (412) ++#define YY_REDUCE_MIN (-277) ++#define YY_REDUCE_MAX (1772) + static const short yy_reduce_ofst[] = { +- /* 0 */ 490, -122, 545, 645, 650, -120, -189, -187, -184, -182, +- /* 10 */ -178, -176, 45, 30, 200, -251, -134, 390, 392, 521, +- /* 20 */ 523, 213, 692, 821, 284, 589, 872, 666, 671, 866, +- /* 30 */ 71, 111, 273, 389, 686, 815, 904, 932, 948, 955, +- /* 40 */ 964, 969, -259, -259, -259, -259, -259, -259, -259, -259, +- /* 50 */ -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, +- /* 60 */ -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, +- /* 70 */ -259, -259, -259, -259, -259, -259, -259, -259, 428, 430, +- /* 80 */ 899, 985, 1021, 1028, 1057, 1069, 1081, 1108, 1110, 1115, +- /* 90 */ 1117, 1123, 1149, 1154, 1159, 1170, 1174, 1178, 1183, 1194, +- /* 100 */ 1198, 1204, 1208, 1212, 1218, 1222, 1229, 1278, 1280, 1283, +- /* 110 */ 1285, 1313, 1316, 1320, 1322, 1325, 1327, 1330, 1366, 1371, +- /* 120 */ 1379, 1387, 1417, 1425, 1430, 1432, -259, -259, -259, -259, +- /* 130 */ -259, -259, -259, -259, -259, 557, 974, -214, -174, -9, +- /* 140 */ 431, -124, 806, 925, 806, 925, 251, 928, 940, -259, +- /* 150 */ -259, -259, -259, -198, -198, -198, 127, -186, -168, 212, +- /* 160 */ 646, 617, 799, -262, 555, 220, 220, 491, 605, 1040, +- /* 170 */ 1060, 699, -11, 600, 848, 862, 345, -129, 724, -91, +- /* 180 */ 158, 749, 716, 900, 304, 822, 929, 926, 499, 793, +- /* 190 */ 322, 892, 813, 845, 958, 1056, 751, 905, 1133, 1062, +- /* 200 */ 803, -210, -185, -179, -148, -167, -89, 121, 274, 281, +- /* 210 */ 320, 336, 439, 663, 711, 957, 965, 1064, 1068, 1112, +- /* 220 */ 1116, -196, 1127, 1134, 1180, 1184, 1195, 1199, 1203, 1215, +- /* 230 */ 1223, 1250, 1267, 1286, 205, 422, 638, 1324, 1341, 1364, +- /* 240 */ 1365, 1213, 1392, 1399, 1403, 869, 1260, 1405, 1421, 1276, +- /* 250 */ 1424, 121, 1426, 1427, 1428, 1433, 1436, 1437, 1227, 1338, +- /* 260 */ 1284, 1359, 1370, 1377, 1388, 1213, 1284, 1284, 1385, 1438, +- /* 270 */ 1443, 1349, 1400, 1391, 1394, 1360, 1408, 1410, 1367, 1439, +- /* 280 */ 1440, 1435, 1442, 1446, 1447, 1397, 1413, 1418, 1390, 1444, +- /* 290 */ 1445, 1474, 1381, 1479, 1480, 1401, 1402, 1490, 1414, 1449, +- /* 300 */ 1452, 1453, 1467, 1456, 1469, 1470, 1477, 1478, 1515, 1518, +- /* 310 */ 1476, 1482, 1450, 1454, 1492, 1483, 1493, 1484, 1523, 1531, +- /* 320 */ 1457, 1455, 1532, 1534, 1516, 1537, 1540, 1543, 1541, 1526, +- /* 330 */ 1528, 1530, 1542, 1512, 1529, 1533, 1544, 1545, 1547, 1550, +- /* 340 */ 1549, 1551, 1554, 1458, 1552, 1494, 1495, 1556, 1498, 1502, +- /* 350 */ 1503, 1511, 1517, 1519, 1522, 1524, 1579, 1472, 1473, 1527, +- /* 360 */ 1555, 1557, 1559, 1558, 1589, 1560, 1561, 1564, 1566, 1568, +- /* 370 */ 1592, 1595, 1605, 1606, 1612, 1613, 1622, 1562, 1563, 1505, +- /* 380 */ 1609, 1604, 1608, 1614, 1615, 1616, 1596, 1597, 1617, 1620, +- /* 390 */ 1625, 1619, ++ /* 0 */ -67, 1252, -64, -178, -181, 160, 1071, 143, -184, 137, ++ /* 10 */ 218, 220, 222, -174, 229, 268, 272, 275, 324, -208, ++ /* 20 */ 242, -277, -39, 81, 537, 792, 810, 812, -189, 814, ++ /* 30 */ 831, 163, 865, 944, 887, 840, 964, 1077, -187, 292, ++ /* 40 */ -133, 274, 673, 558, 682, 795, 809, -238, -232, -238, ++ /* 50 */ -232, 329, 329, 329, 329, 329, 329, 329, 329, 329, ++ /* 60 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, ++ /* 70 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, ++ /* 80 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 557, ++ /* 90 */ 712, 949, 966, 969, 971, 979, 1097, 1099, 1103, 1142, ++ /* 100 */ 1144, 1169, 1172, 1201, 1203, 1228, 1241, 1250, 1253, 1255, ++ /* 110 */ 1261, 1266, 1271, 1282, 1291, 1308, 1310, 1312, 1322, 1328, ++ /* 120 */ 1347, 1354, 1356, 1359, 1362, 1365, 1367, 1374, 1376, 1381, ++ /* 130 */ 1401, 1403, 1406, 1412, 1414, 1417, 1421, 1428, 1447, 1449, ++ /* 140 */ 1453, 1462, 329, 329, 329, 329, 329, 329, 329, 329, ++ /* 150 */ 329, 329, 329, -22, -159, 475, -220, 756, 38, 501, ++ /* 160 */ 841, 714, 329, 118, 337, 349, 363, -56, 329, 329, ++ /* 170 */ 329, 329, -205, -205, -205, 687, -172, -130, -57, 790, ++ /* 180 */ 397, 528, -271, 136, 596, 596, 90, 316, 522, 541, ++ /* 190 */ -37, 715, 849, 977, 628, 856, 980, 991, 1081, 1102, ++ /* 200 */ 1135, 1083, -162, 208, 1258, 794, -86, 159, 41, 1109, ++ /* 210 */ 671, 852, 844, 932, 1175, 1254, 480, 1180, 100, 258, ++ /* 220 */ 1265, 1268, 1216, 1287, -139, 317, 344, 63, 339, 423, ++ /* 230 */ 563, 636, 676, 813, 908, 914, 950, 1078, 1084, 1098, ++ /* 240 */ 1363, 1384, 1407, 1439, 1464, 411, 1527, 1534, 1535, 1537, ++ /* 250 */ 1541, 1542, 1543, 1544, 1545, 1547, 1549, 1550, 990, 1164, ++ /* 260 */ 1492, 1551, 1552, 1556, 1217, 1558, 1559, 1560, 1473, 1413, ++ /* 270 */ 1563, 1510, 1568, 563, 1570, 1571, 1572, 1573, 1574, 1575, ++ /* 280 */ 1443, 1466, 1518, 1513, 1514, 1515, 1516, 1217, 1518, 1518, ++ /* 290 */ 1531, 1562, 1582, 1477, 1505, 1511, 1533, 1512, 1488, 1538, ++ /* 300 */ 1509, 1517, 1546, 1519, 1557, 1489, 1565, 1564, 1578, 1586, ++ /* 310 */ 1587, 1588, 1526, 1528, 1554, 1555, 1576, 1577, 1566, 1579, ++ /* 320 */ 1584, 1591, 1520, 1523, 1617, 1628, 1580, 1581, 1632, 1585, ++ /* 330 */ 1590, 1593, 1604, 1605, 1606, 1608, 1609, 1641, 1649, 1610, ++ /* 340 */ 1592, 1594, 1611, 1595, 1616, 1612, 1618, 1613, 1651, 1654, ++ /* 350 */ 1596, 1598, 1655, 1663, 1650, 1673, 1680, 1677, 1684, 1653, ++ /* 360 */ 1664, 1666, 1667, 1662, 1669, 1672, 1676, 1686, 1679, 1691, ++ /* 370 */ 1689, 1692, 1694, 1597, 1599, 1619, 1630, 1699, 1700, 1602, ++ /* 380 */ 1615, 1648, 1657, 1690, 1698, 1658, 1729, 1652, 1695, 1702, ++ /* 390 */ 1704, 1703, 1741, 1754, 1758, 1768, 1769, 1771, 1660, 1661, ++ /* 400 */ 1665, 1752, 1756, 1757, 1759, 1760, 1764, 1745, 1753, 1762, ++ /* 410 */ 1763, 1761, 1772, + }; + static const YYACTIONTYPE yy_default[] = { +- /* 0 */ 1575, 1575, 1575, 1411, 1188, 1297, 1188, 1188, 1188, 1411, +- /* 10 */ 1411, 1411, 1188, 1327, 1327, 1464, 1219, 1188, 1188, 1188, +- /* 20 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1410, 1188, 1188, +- /* 30 */ 1188, 1188, 1494, 1494, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 40 */ 1188, 1188, 1188, 1336, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 50 */ 1412, 1413, 1188, 1188, 1188, 1463, 1465, 1428, 1346, 1345, +- /* 60 */ 1344, 1343, 1446, 1314, 1341, 1334, 1338, 1406, 1407, 1405, +- /* 70 */ 1409, 1413, 1412, 1188, 1337, 1377, 1391, 1376, 1188, 1188, +- /* 80 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 90 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 100 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 110 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 120 */ 1188, 1188, 1188, 1188, 1188, 1188, 1385, 1390, 1396, 1389, +- /* 130 */ 1386, 1379, 1378, 1380, 1381, 1188, 1209, 1261, 1188, 1188, +- /* 140 */ 1188, 1188, 1482, 1481, 1188, 1188, 1219, 1371, 1370, 1382, +- /* 150 */ 1383, 1393, 1392, 1471, 1529, 1528, 1429, 1188, 1188, 1188, +- /* 160 */ 1188, 1188, 1188, 1494, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 170 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 180 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1494, 1494, +- /* 190 */ 1188, 1219, 1494, 1494, 1215, 1215, 1321, 1188, 1477, 1297, +- /* 200 */ 1288, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 210 */ 1188, 1188, 1188, 1188, 1188, 1468, 1466, 1188, 1188, 1188, +- /* 220 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 230 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 240 */ 1188, 1188, 1188, 1188, 1188, 1293, 1188, 1188, 1188, 1188, +- /* 250 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1523, 1188, 1441, +- /* 260 */ 1275, 1293, 1293, 1293, 1293, 1295, 1276, 1274, 1287, 1220, +- /* 270 */ 1195, 1567, 1294, 1316, 1316, 1564, 1340, 1294, 1564, 1236, +- /* 280 */ 1545, 1231, 1327, 1327, 1327, 1316, 1321, 1321, 1408, 1294, +- /* 290 */ 1287, 1188, 1567, 1302, 1302, 1566, 1566, 1302, 1429, 1349, +- /* 300 */ 1355, 1340, 1264, 1340, 1270, 1270, 1270, 1270, 1302, 1206, +- /* 310 */ 1340, 1340, 1349, 1355, 1264, 1340, 1264, 1340, 1302, 1206, +- /* 320 */ 1445, 1561, 1302, 1206, 1419, 1302, 1206, 1302, 1206, 1419, +- /* 330 */ 1262, 1262, 1262, 1251, 1188, 1188, 1419, 1262, 1236, 1262, +- /* 340 */ 1251, 1262, 1262, 1512, 1419, 1423, 1423, 1419, 1320, 1315, +- /* 350 */ 1320, 1315, 1320, 1315, 1320, 1315, 1302, 1504, 1504, 1330, +- /* 360 */ 1330, 1335, 1321, 1414, 1302, 1188, 1335, 1333, 1331, 1340, +- /* 370 */ 1212, 1254, 1526, 1526, 1522, 1522, 1522, 1572, 1572, 1477, +- /* 380 */ 1538, 1219, 1219, 1219, 1219, 1538, 1238, 1238, 1220, 1220, +- /* 390 */ 1219, 1538, 1188, 1188, 1188, 1188, 1188, 1188, 1533, 1188, +- /* 400 */ 1430, 1306, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 410 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 420 */ 1188, 1188, 1188, 1188, 1188, 1360, 1188, 1191, 1474, 1188, +- /* 430 */ 1188, 1472, 1188, 1188, 1188, 1188, 1188, 1188, 1307, 1188, +- /* 440 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 450 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1563, 1188, 1188, +- /* 460 */ 1188, 1188, 1188, 1188, 1444, 1443, 1188, 1188, 1304, 1188, +- /* 470 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 480 */ 1188, 1188, 1234, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 490 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 500 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1332, +- /* 510 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 520 */ 1188, 1188, 1188, 1188, 1509, 1322, 1188, 1188, 1554, 1188, +- /* 530 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, +- /* 540 */ 1188, 1188, 1188, 1549, 1278, 1362, 1188, 1361, 1365, 1188, +- /* 550 */ 1200, 1188, 1188, ++ /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254, ++ /* 10 */ 1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397, ++ /* 20 */ 1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254, ++ /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254, ++ /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254, ++ /* 60 */ 1492, 1493, 1254, 1254, 1254, 1254, 1543, 1545, 1508, 1420, ++ /* 70 */ 1419, 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, ++ /* 80 */ 1486, 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, ++ /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 140 */ 1254, 1254, 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, ++ /* 150 */ 1456, 1458, 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, ++ /* 160 */ 1254, 1254, 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, ++ /* 170 */ 1473, 1472, 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, ++ /* 180 */ 1254, 1254, 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 200 */ 1254, 1254, 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, ++ /* 210 */ 1578, 1578, 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, ++ /* 220 */ 1358, 1358, 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, ++ /* 240 */ 1546, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, ++ /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, ++ /* 280 */ 1254, 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, ++ /* 290 */ 1357, 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, ++ /* 300 */ 1423, 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, ++ /* 310 */ 1397, 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, ++ /* 320 */ 1357, 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, ++ /* 330 */ 1638, 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, ++ /* 340 */ 1638, 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, ++ /* 350 */ 1525, 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, ++ /* 360 */ 1330, 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, ++ /* 370 */ 1319, 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, ++ /* 380 */ 1588, 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, ++ /* 390 */ 1401, 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, ++ /* 400 */ 1558, 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, ++ /* 410 */ 1288, 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, ++ /* 420 */ 1254, 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 440 */ 1564, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 450 */ 1254, 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, ++ /* 460 */ 1254, 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, ++ /* 470 */ 1254, 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, ++ /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, ++ /* 490 */ 1254, 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, ++ /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 510 */ 1254, 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 530 */ 1254, 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, ++ /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 550 */ 1254, 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, ++ /* 560 */ 1254, 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, ++ /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254, ++ /* 580 */ 1266, 1254, 1254, + }; + /********** End of lemon-generated parsing tables *****************************/ + +@@ -155196,52 +176564,53 @@ static const YYACTIONTYPE yy_default[] = { + static const YYCODETYPE yyFallback[] = { + 0, /* $ => nothing */ + 0, /* SEMI => nothing */ +- 59, /* EXPLAIN => ID */ +- 59, /* QUERY => ID */ +- 59, /* PLAN => ID */ +- 59, /* BEGIN => ID */ ++ 60, /* EXPLAIN => ID */ ++ 60, /* QUERY => ID */ ++ 60, /* PLAN => ID */ ++ 60, /* BEGIN => ID */ + 0, /* TRANSACTION => nothing */ +- 59, /* DEFERRED => ID */ +- 59, /* IMMEDIATE => ID */ +- 59, /* EXCLUSIVE => ID */ ++ 60, /* DEFERRED => ID */ ++ 60, /* IMMEDIATE => ID */ ++ 60, /* EXCLUSIVE => ID */ + 0, /* COMMIT => nothing */ +- 59, /* END => ID */ +- 59, /* ROLLBACK => ID */ +- 59, /* SAVEPOINT => ID */ +- 59, /* RELEASE => ID */ ++ 60, /* END => ID */ ++ 60, /* ROLLBACK => ID */ ++ 60, /* SAVEPOINT => ID */ ++ 60, /* RELEASE => ID */ + 0, /* TO => nothing */ + 0, /* TABLE => nothing */ + 0, /* CREATE => nothing */ +- 59, /* IF => ID */ ++ 60, /* IF => ID */ + 0, /* NOT => nothing */ + 0, /* EXISTS => nothing */ +- 59, /* TEMP => ID */ ++ 60, /* TEMP => ID */ + 0, /* LP => nothing */ + 0, /* RP => nothing */ + 0, /* AS => nothing */ +- 59, /* WITHOUT => ID */ + 0, /* COMMA => nothing */ +- 59, /* ABORT => ID */ +- 59, /* ACTION => ID */ +- 59, /* AFTER => ID */ +- 59, /* ANALYZE => ID */ +- 59, /* ASC => ID */ +- 59, /* ATTACH => ID */ +- 59, /* BEFORE => ID */ +- 59, /* BY => ID */ +- 59, /* CASCADE => ID */ +- 59, /* CAST => ID */ +- 59, /* CONFLICT => ID */ +- 59, /* DATABASE => ID */ +- 59, /* DESC => ID */ +- 59, /* DETACH => ID */ +- 59, /* EACH => ID */ +- 59, /* FAIL => ID */ ++ 60, /* WITHOUT => ID */ ++ 60, /* ABORT => ID */ ++ 60, /* ACTION => ID */ ++ 60, /* AFTER => ID */ ++ 60, /* ANALYZE => ID */ ++ 60, /* ASC => ID */ ++ 60, /* ATTACH => ID */ ++ 60, /* BEFORE => ID */ ++ 60, /* BY => ID */ ++ 60, /* CASCADE => ID */ ++ 60, /* CAST => ID */ ++ 60, /* CONFLICT => ID */ ++ 60, /* DATABASE => ID */ ++ 60, /* DESC => ID */ ++ 60, /* DETACH => ID */ ++ 60, /* EACH => ID */ ++ 60, /* FAIL => ID */ + 0, /* OR => nothing */ + 0, /* AND => nothing */ + 0, /* IS => nothing */ +- 59, /* MATCH => ID */ +- 59, /* LIKE_KW => ID */ ++ 0, /* ISNOT => nothing */ ++ 60, /* MATCH => ID */ ++ 60, /* LIKE_KW => ID */ + 0, /* BETWEEN => nothing */ + 0, /* IN => nothing */ + 0, /* ISNULL => nothing */ +@@ -155254,46 +176623,47 @@ static const YYCODETYPE yyFallback[] = { + 0, /* GE => nothing */ + 0, /* ESCAPE => nothing */ + 0, /* ID => nothing */ +- 59, /* COLUMNKW => ID */ +- 59, /* DO => ID */ +- 59, /* FOR => ID */ +- 59, /* IGNORE => ID */ +- 59, /* INITIALLY => ID */ +- 59, /* INSTEAD => ID */ +- 59, /* NO => ID */ +- 59, /* KEY => ID */ +- 59, /* OF => ID */ +- 59, /* OFFSET => ID */ +- 59, /* PRAGMA => ID */ +- 59, /* RAISE => ID */ +- 59, /* RECURSIVE => ID */ +- 59, /* REPLACE => ID */ +- 59, /* RESTRICT => ID */ +- 59, /* ROW => ID */ +- 59, /* ROWS => ID */ +- 59, /* TRIGGER => ID */ +- 59, /* VACUUM => ID */ +- 59, /* VIEW => ID */ +- 59, /* VIRTUAL => ID */ +- 59, /* WITH => ID */ +- 59, /* NULLS => ID */ +- 59, /* FIRST => ID */ +- 59, /* LAST => ID */ +- 59, /* CURRENT => ID */ +- 59, /* FOLLOWING => ID */ +- 59, /* PARTITION => ID */ +- 59, /* PRECEDING => ID */ +- 59, /* RANGE => ID */ +- 59, /* UNBOUNDED => ID */ +- 59, /* EXCLUDE => ID */ +- 59, /* GROUPS => ID */ +- 59, /* OTHERS => ID */ +- 59, /* TIES => ID */ +- 59, /* GENERATED => ID */ +- 59, /* ALWAYS => ID */ +- 59, /* REINDEX => ID */ +- 59, /* RENAME => ID */ +- 59, /* CTIME_KW => ID */ ++ 60, /* COLUMNKW => ID */ ++ 60, /* DO => ID */ ++ 60, /* FOR => ID */ ++ 60, /* IGNORE => ID */ ++ 60, /* INITIALLY => ID */ ++ 60, /* INSTEAD => ID */ ++ 60, /* NO => ID */ ++ 60, /* KEY => ID */ ++ 60, /* OF => ID */ ++ 60, /* OFFSET => ID */ ++ 60, /* PRAGMA => ID */ ++ 60, /* RAISE => ID */ ++ 60, /* RECURSIVE => ID */ ++ 60, /* REPLACE => ID */ ++ 60, /* RESTRICT => ID */ ++ 60, /* ROW => ID */ ++ 60, /* ROWS => ID */ ++ 60, /* TRIGGER => ID */ ++ 60, /* VACUUM => ID */ ++ 60, /* VIEW => ID */ ++ 60, /* VIRTUAL => ID */ ++ 60, /* WITH => ID */ ++ 60, /* NULLS => ID */ ++ 60, /* FIRST => ID */ ++ 60, /* LAST => ID */ ++ 60, /* CURRENT => ID */ ++ 60, /* FOLLOWING => ID */ ++ 60, /* PARTITION => ID */ ++ 60, /* PRECEDING => ID */ ++ 60, /* RANGE => ID */ ++ 60, /* UNBOUNDED => ID */ ++ 60, /* EXCLUDE => ID */ ++ 60, /* GROUPS => ID */ ++ 60, /* OTHERS => ID */ ++ 60, /* TIES => ID */ ++ 60, /* GENERATED => ID */ ++ 60, /* ALWAYS => ID */ ++ 60, /* MATERIALIZED => ID */ ++ 60, /* REINDEX => ID */ ++ 60, /* RENAME => ID */ ++ 60, /* CTIME_KW => ID */ + 0, /* ANY => nothing */ + 0, /* BITAND => nothing */ + 0, /* BITOR => nothing */ +@@ -155305,6 +176675,7 @@ static const YYCODETYPE yyFallback[] = { + 0, /* SLASH => nothing */ + 0, /* REM => nothing */ + 0, /* CONCAT => nothing */ ++ 0, /* PTR => nothing */ + 0, /* COLLATE => nothing */ + 0, /* BITNOT => nothing */ + 0, /* ON => nothing */ +@@ -155342,6 +176713,7 @@ static const YYCODETYPE yyFallback[] = { + 0, /* HAVING => nothing */ + 0, /* LIMIT => nothing */ + 0, /* WHERE => nothing */ ++ 0, /* RETURNING => nothing */ + 0, /* INTO => nothing */ + 0, /* NOTHING => nothing */ + 0, /* FLOAT => nothing */ +@@ -155362,10 +176734,9 @@ static const YYCODETYPE yyFallback[] = { + 0, /* AGG_FUNCTION => nothing */ + 0, /* AGG_COLUMN => nothing */ + 0, /* TRUEFALSE => nothing */ +- 0, /* ISNOT => nothing */ + 0, /* FUNCTION => nothing */ +- 0, /* UMINUS => nothing */ + 0, /* UPLUS => nothing */ ++ 0, /* UMINUS => nothing */ + 0, /* TRUTH => nothing */ + 0, /* REGISTER => nothing */ + 0, /* VECTOR => nothing */ +@@ -155373,7 +176744,10 @@ static const YYCODETYPE yyFallback[] = { + 0, /* IF_NULL_ROW => nothing */ + 0, /* ASTERISK => nothing */ + 0, /* SPAN => nothing */ ++ 0, /* ERROR => nothing */ ++ 0, /* QNUMBER => nothing */ + 0, /* SPACE => nothing */ ++ 0, /* COMMENT => nothing */ + 0, /* ILLEGAL => nothing */ + }; + #endif /* YYFALLBACK */ +@@ -155415,17 +176789,13 @@ struct yyParser { + #endif + sqlite3ParserARG_SDECL /* A place to hold %extra_argument */ + sqlite3ParserCTX_SDECL /* A place to hold %extra_context */ +-#if YYSTACKDEPTH<=0 +- int yystksz; /* Current side of the stack */ +- yyStackEntry *yystack; /* The parser's stack */ +- yyStackEntry yystk0; /* First stack entry */ +-#else +- yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +- yyStackEntry *yystackEnd; /* Last entry in the stack */ +-#endif ++ yyStackEntry *yystackEnd; /* Last entry in the stack */ ++ yyStackEntry *yystack; /* The parser stack */ ++ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ + }; + typedef struct yyParser yyParser; + ++/* #include */ + #ifndef NDEBUG + /* #include */ + static FILE *yyTraceFILE = 0; +@@ -155487,8 +176857,8 @@ static const char *const yyTokenName[] = { + /* 22 */ "LP", + /* 23 */ "RP", + /* 24 */ "AS", +- /* 25 */ "WITHOUT", +- /* 26 */ "COMMA", ++ /* 25 */ "COMMA", ++ /* 26 */ "WITHOUT", + /* 27 */ "ABORT", + /* 28 */ "ACTION", + /* 29 */ "AFTER", +@@ -155508,270 +176878,283 @@ static const char *const yyTokenName[] = { + /* 43 */ "OR", + /* 44 */ "AND", + /* 45 */ "IS", +- /* 46 */ "MATCH", +- /* 47 */ "LIKE_KW", +- /* 48 */ "BETWEEN", +- /* 49 */ "IN", +- /* 50 */ "ISNULL", +- /* 51 */ "NOTNULL", +- /* 52 */ "NE", +- /* 53 */ "EQ", +- /* 54 */ "GT", +- /* 55 */ "LE", +- /* 56 */ "LT", +- /* 57 */ "GE", +- /* 58 */ "ESCAPE", +- /* 59 */ "ID", +- /* 60 */ "COLUMNKW", +- /* 61 */ "DO", +- /* 62 */ "FOR", +- /* 63 */ "IGNORE", +- /* 64 */ "INITIALLY", +- /* 65 */ "INSTEAD", +- /* 66 */ "NO", +- /* 67 */ "KEY", +- /* 68 */ "OF", +- /* 69 */ "OFFSET", +- /* 70 */ "PRAGMA", +- /* 71 */ "RAISE", +- /* 72 */ "RECURSIVE", +- /* 73 */ "REPLACE", +- /* 74 */ "RESTRICT", +- /* 75 */ "ROW", +- /* 76 */ "ROWS", +- /* 77 */ "TRIGGER", +- /* 78 */ "VACUUM", +- /* 79 */ "VIEW", +- /* 80 */ "VIRTUAL", +- /* 81 */ "WITH", +- /* 82 */ "NULLS", +- /* 83 */ "FIRST", +- /* 84 */ "LAST", +- /* 85 */ "CURRENT", +- /* 86 */ "FOLLOWING", +- /* 87 */ "PARTITION", +- /* 88 */ "PRECEDING", +- /* 89 */ "RANGE", +- /* 90 */ "UNBOUNDED", +- /* 91 */ "EXCLUDE", +- /* 92 */ "GROUPS", +- /* 93 */ "OTHERS", +- /* 94 */ "TIES", +- /* 95 */ "GENERATED", +- /* 96 */ "ALWAYS", +- /* 97 */ "REINDEX", +- /* 98 */ "RENAME", +- /* 99 */ "CTIME_KW", +- /* 100 */ "ANY", +- /* 101 */ "BITAND", +- /* 102 */ "BITOR", +- /* 103 */ "LSHIFT", +- /* 104 */ "RSHIFT", +- /* 105 */ "PLUS", +- /* 106 */ "MINUS", +- /* 107 */ "STAR", +- /* 108 */ "SLASH", +- /* 109 */ "REM", +- /* 110 */ "CONCAT", +- /* 111 */ "COLLATE", +- /* 112 */ "BITNOT", +- /* 113 */ "ON", +- /* 114 */ "INDEXED", +- /* 115 */ "STRING", +- /* 116 */ "JOIN_KW", +- /* 117 */ "CONSTRAINT", +- /* 118 */ "DEFAULT", +- /* 119 */ "NULL", +- /* 120 */ "PRIMARY", +- /* 121 */ "UNIQUE", +- /* 122 */ "CHECK", +- /* 123 */ "REFERENCES", +- /* 124 */ "AUTOINCR", +- /* 125 */ "INSERT", +- /* 126 */ "DELETE", +- /* 127 */ "UPDATE", +- /* 128 */ "SET", +- /* 129 */ "DEFERRABLE", +- /* 130 */ "FOREIGN", +- /* 131 */ "DROP", +- /* 132 */ "UNION", +- /* 133 */ "ALL", +- /* 134 */ "EXCEPT", +- /* 135 */ "INTERSECT", +- /* 136 */ "SELECT", +- /* 137 */ "VALUES", +- /* 138 */ "DISTINCT", +- /* 139 */ "DOT", +- /* 140 */ "FROM", +- /* 141 */ "JOIN", +- /* 142 */ "USING", +- /* 143 */ "ORDER", +- /* 144 */ "GROUP", +- /* 145 */ "HAVING", +- /* 146 */ "LIMIT", +- /* 147 */ "WHERE", +- /* 148 */ "INTO", +- /* 149 */ "NOTHING", +- /* 150 */ "FLOAT", +- /* 151 */ "BLOB", +- /* 152 */ "INTEGER", +- /* 153 */ "VARIABLE", +- /* 154 */ "CASE", +- /* 155 */ "WHEN", +- /* 156 */ "THEN", +- /* 157 */ "ELSE", +- /* 158 */ "INDEX", +- /* 159 */ "ALTER", +- /* 160 */ "ADD", +- /* 161 */ "WINDOW", +- /* 162 */ "OVER", +- /* 163 */ "FILTER", +- /* 164 */ "COLUMN", +- /* 165 */ "AGG_FUNCTION", +- /* 166 */ "AGG_COLUMN", +- /* 167 */ "TRUEFALSE", +- /* 168 */ "ISNOT", +- /* 169 */ "FUNCTION", +- /* 170 */ "UMINUS", +- /* 171 */ "UPLUS", +- /* 172 */ "TRUTH", +- /* 173 */ "REGISTER", +- /* 174 */ "VECTOR", +- /* 175 */ "SELECT_COLUMN", +- /* 176 */ "IF_NULL_ROW", +- /* 177 */ "ASTERISK", +- /* 178 */ "SPAN", +- /* 179 */ "SPACE", +- /* 180 */ "ILLEGAL", +- /* 181 */ "input", +- /* 182 */ "cmdlist", +- /* 183 */ "ecmd", +- /* 184 */ "cmdx", +- /* 185 */ "explain", +- /* 186 */ "cmd", +- /* 187 */ "transtype", +- /* 188 */ "trans_opt", +- /* 189 */ "nm", +- /* 190 */ "savepoint_opt", +- /* 191 */ "create_table", +- /* 192 */ "create_table_args", +- /* 193 */ "createkw", +- /* 194 */ "temp", +- /* 195 */ "ifnotexists", +- /* 196 */ "dbnm", +- /* 197 */ "columnlist", +- /* 198 */ "conslist_opt", +- /* 199 */ "table_options", +- /* 200 */ "select", +- /* 201 */ "columnname", +- /* 202 */ "carglist", +- /* 203 */ "typetoken", +- /* 204 */ "typename", +- /* 205 */ "signed", +- /* 206 */ "plus_num", +- /* 207 */ "minus_num", +- /* 208 */ "scanpt", +- /* 209 */ "scantok", +- /* 210 */ "ccons", +- /* 211 */ "term", +- /* 212 */ "expr", +- /* 213 */ "onconf", +- /* 214 */ "sortorder", +- /* 215 */ "autoinc", +- /* 216 */ "eidlist_opt", +- /* 217 */ "refargs", +- /* 218 */ "defer_subclause", +- /* 219 */ "generated", +- /* 220 */ "refarg", +- /* 221 */ "refact", +- /* 222 */ "init_deferred_pred_opt", +- /* 223 */ "conslist", +- /* 224 */ "tconscomma", +- /* 225 */ "tcons", +- /* 226 */ "sortlist", +- /* 227 */ "eidlist", +- /* 228 */ "defer_subclause_opt", +- /* 229 */ "orconf", +- /* 230 */ "resolvetype", +- /* 231 */ "raisetype", +- /* 232 */ "ifexists", +- /* 233 */ "fullname", +- /* 234 */ "selectnowith", +- /* 235 */ "oneselect", +- /* 236 */ "wqlist", +- /* 237 */ "multiselect_op", +- /* 238 */ "distinct", +- /* 239 */ "selcollist", +- /* 240 */ "from", +- /* 241 */ "where_opt", +- /* 242 */ "groupby_opt", +- /* 243 */ "having_opt", +- /* 244 */ "orderby_opt", +- /* 245 */ "limit_opt", +- /* 246 */ "window_clause", +- /* 247 */ "values", +- /* 248 */ "nexprlist", +- /* 249 */ "sclp", +- /* 250 */ "as", +- /* 251 */ "seltablist", +- /* 252 */ "stl_prefix", +- /* 253 */ "joinop", +- /* 254 */ "indexed_opt", +- /* 255 */ "on_opt", +- /* 256 */ "using_opt", +- /* 257 */ "exprlist", +- /* 258 */ "xfullname", +- /* 259 */ "idlist", +- /* 260 */ "nulls", +- /* 261 */ "with", +- /* 262 */ "setlist", +- /* 263 */ "insert_cmd", +- /* 264 */ "idlist_opt", +- /* 265 */ "upsert", +- /* 266 */ "filter_over", +- /* 267 */ "likeop", +- /* 268 */ "between_op", +- /* 269 */ "in_op", +- /* 270 */ "paren_exprlist", +- /* 271 */ "case_operand", +- /* 272 */ "case_exprlist", +- /* 273 */ "case_else", +- /* 274 */ "uniqueflag", +- /* 275 */ "collate", +- /* 276 */ "vinto", +- /* 277 */ "nmnum", +- /* 278 */ "trigger_decl", +- /* 279 */ "trigger_cmd_list", +- /* 280 */ "trigger_time", +- /* 281 */ "trigger_event", +- /* 282 */ "foreach_clause", +- /* 283 */ "when_clause", +- /* 284 */ "trigger_cmd", +- /* 285 */ "trnm", +- /* 286 */ "tridxby", +- /* 287 */ "database_kw_opt", +- /* 288 */ "key_opt", +- /* 289 */ "add_column_fullname", +- /* 290 */ "kwcolumn_opt", +- /* 291 */ "create_vtab", +- /* 292 */ "vtabarglist", +- /* 293 */ "vtabarg", +- /* 294 */ "vtabargtoken", +- /* 295 */ "lp", +- /* 296 */ "anylist", +- /* 297 */ "windowdefn_list", +- /* 298 */ "windowdefn", +- /* 299 */ "window", +- /* 300 */ "frame_opt", +- /* 301 */ "part_opt", +- /* 302 */ "filter_clause", +- /* 303 */ "over_clause", +- /* 304 */ "range_or_rows", +- /* 305 */ "frame_bound", +- /* 306 */ "frame_bound_s", +- /* 307 */ "frame_bound_e", +- /* 308 */ "frame_exclude_opt", +- /* 309 */ "frame_exclude", ++ /* 46 */ "ISNOT", ++ /* 47 */ "MATCH", ++ /* 48 */ "LIKE_KW", ++ /* 49 */ "BETWEEN", ++ /* 50 */ "IN", ++ /* 51 */ "ISNULL", ++ /* 52 */ "NOTNULL", ++ /* 53 */ "NE", ++ /* 54 */ "EQ", ++ /* 55 */ "GT", ++ /* 56 */ "LE", ++ /* 57 */ "LT", ++ /* 58 */ "GE", ++ /* 59 */ "ESCAPE", ++ /* 60 */ "ID", ++ /* 61 */ "COLUMNKW", ++ /* 62 */ "DO", ++ /* 63 */ "FOR", ++ /* 64 */ "IGNORE", ++ /* 65 */ "INITIALLY", ++ /* 66 */ "INSTEAD", ++ /* 67 */ "NO", ++ /* 68 */ "KEY", ++ /* 69 */ "OF", ++ /* 70 */ "OFFSET", ++ /* 71 */ "PRAGMA", ++ /* 72 */ "RAISE", ++ /* 73 */ "RECURSIVE", ++ /* 74 */ "REPLACE", ++ /* 75 */ "RESTRICT", ++ /* 76 */ "ROW", ++ /* 77 */ "ROWS", ++ /* 78 */ "TRIGGER", ++ /* 79 */ "VACUUM", ++ /* 80 */ "VIEW", ++ /* 81 */ "VIRTUAL", ++ /* 82 */ "WITH", ++ /* 83 */ "NULLS", ++ /* 84 */ "FIRST", ++ /* 85 */ "LAST", ++ /* 86 */ "CURRENT", ++ /* 87 */ "FOLLOWING", ++ /* 88 */ "PARTITION", ++ /* 89 */ "PRECEDING", ++ /* 90 */ "RANGE", ++ /* 91 */ "UNBOUNDED", ++ /* 92 */ "EXCLUDE", ++ /* 93 */ "GROUPS", ++ /* 94 */ "OTHERS", ++ /* 95 */ "TIES", ++ /* 96 */ "GENERATED", ++ /* 97 */ "ALWAYS", ++ /* 98 */ "MATERIALIZED", ++ /* 99 */ "REINDEX", ++ /* 100 */ "RENAME", ++ /* 101 */ "CTIME_KW", ++ /* 102 */ "ANY", ++ /* 103 */ "BITAND", ++ /* 104 */ "BITOR", ++ /* 105 */ "LSHIFT", ++ /* 106 */ "RSHIFT", ++ /* 107 */ "PLUS", ++ /* 108 */ "MINUS", ++ /* 109 */ "STAR", ++ /* 110 */ "SLASH", ++ /* 111 */ "REM", ++ /* 112 */ "CONCAT", ++ /* 113 */ "PTR", ++ /* 114 */ "COLLATE", ++ /* 115 */ "BITNOT", ++ /* 116 */ "ON", ++ /* 117 */ "INDEXED", ++ /* 118 */ "STRING", ++ /* 119 */ "JOIN_KW", ++ /* 120 */ "CONSTRAINT", ++ /* 121 */ "DEFAULT", ++ /* 122 */ "NULL", ++ /* 123 */ "PRIMARY", ++ /* 124 */ "UNIQUE", ++ /* 125 */ "CHECK", ++ /* 126 */ "REFERENCES", ++ /* 127 */ "AUTOINCR", ++ /* 128 */ "INSERT", ++ /* 129 */ "DELETE", ++ /* 130 */ "UPDATE", ++ /* 131 */ "SET", ++ /* 132 */ "DEFERRABLE", ++ /* 133 */ "FOREIGN", ++ /* 134 */ "DROP", ++ /* 135 */ "UNION", ++ /* 136 */ "ALL", ++ /* 137 */ "EXCEPT", ++ /* 138 */ "INTERSECT", ++ /* 139 */ "SELECT", ++ /* 140 */ "VALUES", ++ /* 141 */ "DISTINCT", ++ /* 142 */ "DOT", ++ /* 143 */ "FROM", ++ /* 144 */ "JOIN", ++ /* 145 */ "USING", ++ /* 146 */ "ORDER", ++ /* 147 */ "GROUP", ++ /* 148 */ "HAVING", ++ /* 149 */ "LIMIT", ++ /* 150 */ "WHERE", ++ /* 151 */ "RETURNING", ++ /* 152 */ "INTO", ++ /* 153 */ "NOTHING", ++ /* 154 */ "FLOAT", ++ /* 155 */ "BLOB", ++ /* 156 */ "INTEGER", ++ /* 157 */ "VARIABLE", ++ /* 158 */ "CASE", ++ /* 159 */ "WHEN", ++ /* 160 */ "THEN", ++ /* 161 */ "ELSE", ++ /* 162 */ "INDEX", ++ /* 163 */ "ALTER", ++ /* 164 */ "ADD", ++ /* 165 */ "WINDOW", ++ /* 166 */ "OVER", ++ /* 167 */ "FILTER", ++ /* 168 */ "COLUMN", ++ /* 169 */ "AGG_FUNCTION", ++ /* 170 */ "AGG_COLUMN", ++ /* 171 */ "TRUEFALSE", ++ /* 172 */ "FUNCTION", ++ /* 173 */ "UPLUS", ++ /* 174 */ "UMINUS", ++ /* 175 */ "TRUTH", ++ /* 176 */ "REGISTER", ++ /* 177 */ "VECTOR", ++ /* 178 */ "SELECT_COLUMN", ++ /* 179 */ "IF_NULL_ROW", ++ /* 180 */ "ASTERISK", ++ /* 181 */ "SPAN", ++ /* 182 */ "ERROR", ++ /* 183 */ "QNUMBER", ++ /* 184 */ "SPACE", ++ /* 185 */ "COMMENT", ++ /* 186 */ "ILLEGAL", ++ /* 187 */ "input", ++ /* 188 */ "cmdlist", ++ /* 189 */ "ecmd", ++ /* 190 */ "cmdx", ++ /* 191 */ "explain", ++ /* 192 */ "cmd", ++ /* 193 */ "transtype", ++ /* 194 */ "trans_opt", ++ /* 195 */ "nm", ++ /* 196 */ "savepoint_opt", ++ /* 197 */ "create_table", ++ /* 198 */ "create_table_args", ++ /* 199 */ "createkw", ++ /* 200 */ "temp", ++ /* 201 */ "ifnotexists", ++ /* 202 */ "dbnm", ++ /* 203 */ "columnlist", ++ /* 204 */ "conslist_opt", ++ /* 205 */ "table_option_set", ++ /* 206 */ "select", ++ /* 207 */ "table_option", ++ /* 208 */ "columnname", ++ /* 209 */ "carglist", ++ /* 210 */ "typetoken", ++ /* 211 */ "typename", ++ /* 212 */ "signed", ++ /* 213 */ "plus_num", ++ /* 214 */ "minus_num", ++ /* 215 */ "scanpt", ++ /* 216 */ "scantok", ++ /* 217 */ "ccons", ++ /* 218 */ "term", ++ /* 219 */ "expr", ++ /* 220 */ "onconf", ++ /* 221 */ "sortorder", ++ /* 222 */ "autoinc", ++ /* 223 */ "eidlist_opt", ++ /* 224 */ "refargs", ++ /* 225 */ "defer_subclause", ++ /* 226 */ "generated", ++ /* 227 */ "refarg", ++ /* 228 */ "refact", ++ /* 229 */ "init_deferred_pred_opt", ++ /* 230 */ "conslist", ++ /* 231 */ "tconscomma", ++ /* 232 */ "tcons", ++ /* 233 */ "sortlist", ++ /* 234 */ "eidlist", ++ /* 235 */ "defer_subclause_opt", ++ /* 236 */ "orconf", ++ /* 237 */ "resolvetype", ++ /* 238 */ "raisetype", ++ /* 239 */ "ifexists", ++ /* 240 */ "fullname", ++ /* 241 */ "selectnowith", ++ /* 242 */ "oneselect", ++ /* 243 */ "wqlist", ++ /* 244 */ "multiselect_op", ++ /* 245 */ "distinct", ++ /* 246 */ "selcollist", ++ /* 247 */ "from", ++ /* 248 */ "where_opt", ++ /* 249 */ "groupby_opt", ++ /* 250 */ "having_opt", ++ /* 251 */ "orderby_opt", ++ /* 252 */ "limit_opt", ++ /* 253 */ "window_clause", ++ /* 254 */ "values", ++ /* 255 */ "nexprlist", ++ /* 256 */ "mvalues", ++ /* 257 */ "sclp", ++ /* 258 */ "as", ++ /* 259 */ "seltablist", ++ /* 260 */ "stl_prefix", ++ /* 261 */ "joinop", ++ /* 262 */ "on_using", ++ /* 263 */ "indexed_by", ++ /* 264 */ "exprlist", ++ /* 265 */ "xfullname", ++ /* 266 */ "idlist", ++ /* 267 */ "indexed_opt", ++ /* 268 */ "nulls", ++ /* 269 */ "with", ++ /* 270 */ "where_opt_ret", ++ /* 271 */ "setlist", ++ /* 272 */ "insert_cmd", ++ /* 273 */ "idlist_opt", ++ /* 274 */ "upsert", ++ /* 275 */ "returning", ++ /* 276 */ "filter_over", ++ /* 277 */ "likeop", ++ /* 278 */ "between_op", ++ /* 279 */ "in_op", ++ /* 280 */ "paren_exprlist", ++ /* 281 */ "case_operand", ++ /* 282 */ "case_exprlist", ++ /* 283 */ "case_else", ++ /* 284 */ "uniqueflag", ++ /* 285 */ "collate", ++ /* 286 */ "vinto", ++ /* 287 */ "nmnum", ++ /* 288 */ "trigger_decl", ++ /* 289 */ "trigger_cmd_list", ++ /* 290 */ "trigger_time", ++ /* 291 */ "trigger_event", ++ /* 292 */ "foreach_clause", ++ /* 293 */ "when_clause", ++ /* 294 */ "trigger_cmd", ++ /* 295 */ "trnm", ++ /* 296 */ "tridxby", ++ /* 297 */ "database_kw_opt", ++ /* 298 */ "key_opt", ++ /* 299 */ "add_column_fullname", ++ /* 300 */ "kwcolumn_opt", ++ /* 301 */ "create_vtab", ++ /* 302 */ "vtabarglist", ++ /* 303 */ "vtabarg", ++ /* 304 */ "vtabargtoken", ++ /* 305 */ "lp", ++ /* 306 */ "anylist", ++ /* 307 */ "wqitem", ++ /* 308 */ "wqas", ++ /* 309 */ "withnm", ++ /* 310 */ "windowdefn_list", ++ /* 311 */ "windowdefn", ++ /* 312 */ "window", ++ /* 313 */ "frame_opt", ++ /* 314 */ "part_opt", ++ /* 315 */ "filter_clause", ++ /* 316 */ "over_clause", ++ /* 317 */ "range_or_rows", ++ /* 318 */ "frame_bound", ++ /* 319 */ "frame_bound_s", ++ /* 320 */ "frame_bound_e", ++ /* 321 */ "frame_exclude_opt", ++ /* 322 */ "frame_exclude", + }; + #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ + +@@ -155798,407 +177181,439 @@ static const char *const yyRuleName[] = { + /* 16 */ "ifnotexists ::= IF NOT EXISTS", + /* 17 */ "temp ::= TEMP", + /* 18 */ "temp ::=", +- /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_options", ++ /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_option_set", + /* 20 */ "create_table_args ::= AS select", +- /* 21 */ "table_options ::=", +- /* 22 */ "table_options ::= WITHOUT nm", +- /* 23 */ "columnname ::= nm typetoken", +- /* 24 */ "typetoken ::=", +- /* 25 */ "typetoken ::= typename LP signed RP", +- /* 26 */ "typetoken ::= typename LP signed COMMA signed RP", +- /* 27 */ "typename ::= typename ID|STRING", +- /* 28 */ "scanpt ::=", +- /* 29 */ "scantok ::=", +- /* 30 */ "ccons ::= CONSTRAINT nm", +- /* 31 */ "ccons ::= DEFAULT scantok term", +- /* 32 */ "ccons ::= DEFAULT LP expr RP", +- /* 33 */ "ccons ::= DEFAULT PLUS scantok term", +- /* 34 */ "ccons ::= DEFAULT MINUS scantok term", +- /* 35 */ "ccons ::= DEFAULT scantok ID|INDEXED", +- /* 36 */ "ccons ::= NOT NULL onconf", +- /* 37 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", +- /* 38 */ "ccons ::= UNIQUE onconf", +- /* 39 */ "ccons ::= CHECK LP expr RP", +- /* 40 */ "ccons ::= REFERENCES nm eidlist_opt refargs", +- /* 41 */ "ccons ::= defer_subclause", +- /* 42 */ "ccons ::= COLLATE ID|STRING", +- /* 43 */ "generated ::= LP expr RP", +- /* 44 */ "generated ::= LP expr RP ID", +- /* 45 */ "autoinc ::=", +- /* 46 */ "autoinc ::= AUTOINCR", +- /* 47 */ "refargs ::=", +- /* 48 */ "refargs ::= refargs refarg", +- /* 49 */ "refarg ::= MATCH nm", +- /* 50 */ "refarg ::= ON INSERT refact", +- /* 51 */ "refarg ::= ON DELETE refact", +- /* 52 */ "refarg ::= ON UPDATE refact", +- /* 53 */ "refact ::= SET NULL", +- /* 54 */ "refact ::= SET DEFAULT", +- /* 55 */ "refact ::= CASCADE", +- /* 56 */ "refact ::= RESTRICT", +- /* 57 */ "refact ::= NO ACTION", +- /* 58 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", +- /* 59 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", +- /* 60 */ "init_deferred_pred_opt ::=", +- /* 61 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", +- /* 62 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", +- /* 63 */ "conslist_opt ::=", +- /* 64 */ "tconscomma ::= COMMA", +- /* 65 */ "tcons ::= CONSTRAINT nm", +- /* 66 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", +- /* 67 */ "tcons ::= UNIQUE LP sortlist RP onconf", +- /* 68 */ "tcons ::= CHECK LP expr RP onconf", +- /* 69 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", +- /* 70 */ "defer_subclause_opt ::=", +- /* 71 */ "onconf ::=", +- /* 72 */ "onconf ::= ON CONFLICT resolvetype", +- /* 73 */ "orconf ::=", +- /* 74 */ "orconf ::= OR resolvetype", +- /* 75 */ "resolvetype ::= IGNORE", +- /* 76 */ "resolvetype ::= REPLACE", +- /* 77 */ "cmd ::= DROP TABLE ifexists fullname", +- /* 78 */ "ifexists ::= IF EXISTS", +- /* 79 */ "ifexists ::=", +- /* 80 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", +- /* 81 */ "cmd ::= DROP VIEW ifexists fullname", +- /* 82 */ "cmd ::= select", +- /* 83 */ "select ::= WITH wqlist selectnowith", +- /* 84 */ "select ::= WITH RECURSIVE wqlist selectnowith", +- /* 85 */ "select ::= selectnowith", +- /* 86 */ "selectnowith ::= selectnowith multiselect_op oneselect", +- /* 87 */ "multiselect_op ::= UNION", +- /* 88 */ "multiselect_op ::= UNION ALL", +- /* 89 */ "multiselect_op ::= EXCEPT|INTERSECT", +- /* 90 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", +- /* 91 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", +- /* 92 */ "values ::= VALUES LP nexprlist RP", +- /* 93 */ "values ::= values COMMA LP nexprlist RP", +- /* 94 */ "distinct ::= DISTINCT", +- /* 95 */ "distinct ::= ALL", +- /* 96 */ "distinct ::=", +- /* 97 */ "sclp ::=", +- /* 98 */ "selcollist ::= sclp scanpt expr scanpt as", +- /* 99 */ "selcollist ::= sclp scanpt STAR", +- /* 100 */ "selcollist ::= sclp scanpt nm DOT STAR", +- /* 101 */ "as ::= AS nm", +- /* 102 */ "as ::=", +- /* 103 */ "from ::=", +- /* 104 */ "from ::= FROM seltablist", +- /* 105 */ "stl_prefix ::= seltablist joinop", +- /* 106 */ "stl_prefix ::=", +- /* 107 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", +- /* 108 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", +- /* 109 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", +- /* 110 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", +- /* 111 */ "dbnm ::=", +- /* 112 */ "dbnm ::= DOT nm", +- /* 113 */ "fullname ::= nm", +- /* 114 */ "fullname ::= nm DOT nm", +- /* 115 */ "xfullname ::= nm", +- /* 116 */ "xfullname ::= nm DOT nm", +- /* 117 */ "xfullname ::= nm DOT nm AS nm", +- /* 118 */ "xfullname ::= nm AS nm", +- /* 119 */ "joinop ::= COMMA|JOIN", +- /* 120 */ "joinop ::= JOIN_KW JOIN", +- /* 121 */ "joinop ::= JOIN_KW nm JOIN", +- /* 122 */ "joinop ::= JOIN_KW nm nm JOIN", +- /* 123 */ "on_opt ::= ON expr", +- /* 124 */ "on_opt ::=", +- /* 125 */ "indexed_opt ::=", +- /* 126 */ "indexed_opt ::= INDEXED BY nm", +- /* 127 */ "indexed_opt ::= NOT INDEXED", +- /* 128 */ "using_opt ::= USING LP idlist RP", +- /* 129 */ "using_opt ::=", +- /* 130 */ "orderby_opt ::=", +- /* 131 */ "orderby_opt ::= ORDER BY sortlist", +- /* 132 */ "sortlist ::= sortlist COMMA expr sortorder nulls", +- /* 133 */ "sortlist ::= expr sortorder nulls", +- /* 134 */ "sortorder ::= ASC", +- /* 135 */ "sortorder ::= DESC", +- /* 136 */ "sortorder ::=", +- /* 137 */ "nulls ::= NULLS FIRST", +- /* 138 */ "nulls ::= NULLS LAST", +- /* 139 */ "nulls ::=", +- /* 140 */ "groupby_opt ::=", +- /* 141 */ "groupby_opt ::= GROUP BY nexprlist", +- /* 142 */ "having_opt ::=", +- /* 143 */ "having_opt ::= HAVING expr", +- /* 144 */ "limit_opt ::=", +- /* 145 */ "limit_opt ::= LIMIT expr", +- /* 146 */ "limit_opt ::= LIMIT expr OFFSET expr", +- /* 147 */ "limit_opt ::= LIMIT expr COMMA expr", +- /* 148 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt", +- /* 149 */ "where_opt ::=", +- /* 150 */ "where_opt ::= WHERE expr", +- /* 151 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt", +- /* 152 */ "setlist ::= setlist COMMA nm EQ expr", +- /* 153 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", +- /* 154 */ "setlist ::= nm EQ expr", +- /* 155 */ "setlist ::= LP idlist RP EQ expr", +- /* 156 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", +- /* 157 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES", +- /* 158 */ "upsert ::=", +- /* 159 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt", +- /* 160 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING", +- /* 161 */ "upsert ::= ON CONFLICT DO NOTHING", +- /* 162 */ "insert_cmd ::= INSERT orconf", +- /* 163 */ "insert_cmd ::= REPLACE", +- /* 164 */ "idlist_opt ::=", +- /* 165 */ "idlist_opt ::= LP idlist RP", +- /* 166 */ "idlist ::= idlist COMMA nm", +- /* 167 */ "idlist ::= nm", +- /* 168 */ "expr ::= LP expr RP", +- /* 169 */ "expr ::= ID|INDEXED", +- /* 170 */ "expr ::= JOIN_KW", +- /* 171 */ "expr ::= nm DOT nm", +- /* 172 */ "expr ::= nm DOT nm DOT nm", +- /* 173 */ "term ::= NULL|FLOAT|BLOB", +- /* 174 */ "term ::= STRING", +- /* 175 */ "term ::= INTEGER", +- /* 176 */ "expr ::= VARIABLE", +- /* 177 */ "expr ::= expr COLLATE ID|STRING", +- /* 178 */ "expr ::= CAST LP expr AS typetoken RP", +- /* 179 */ "expr ::= ID|INDEXED LP distinct exprlist RP", +- /* 180 */ "expr ::= ID|INDEXED LP STAR RP", +- /* 181 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", +- /* 182 */ "expr ::= ID|INDEXED LP STAR RP filter_over", +- /* 183 */ "term ::= CTIME_KW", +- /* 184 */ "expr ::= LP nexprlist COMMA expr RP", +- /* 185 */ "expr ::= expr AND expr", +- /* 186 */ "expr ::= expr OR expr", +- /* 187 */ "expr ::= expr LT|GT|GE|LE expr", +- /* 188 */ "expr ::= expr EQ|NE expr", +- /* 189 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", +- /* 190 */ "expr ::= expr PLUS|MINUS expr", +- /* 191 */ "expr ::= expr STAR|SLASH|REM expr", +- /* 192 */ "expr ::= expr CONCAT expr", +- /* 193 */ "likeop ::= NOT LIKE_KW|MATCH", +- /* 194 */ "expr ::= expr likeop expr", +- /* 195 */ "expr ::= expr likeop expr ESCAPE expr", +- /* 196 */ "expr ::= expr ISNULL|NOTNULL", +- /* 197 */ "expr ::= expr NOT NULL", +- /* 198 */ "expr ::= expr IS expr", +- /* 199 */ "expr ::= expr IS NOT expr", +- /* 200 */ "expr ::= NOT expr", +- /* 201 */ "expr ::= BITNOT expr", +- /* 202 */ "expr ::= PLUS|MINUS expr", +- /* 203 */ "between_op ::= BETWEEN", +- /* 204 */ "between_op ::= NOT BETWEEN", +- /* 205 */ "expr ::= expr between_op expr AND expr", +- /* 206 */ "in_op ::= IN", +- /* 207 */ "in_op ::= NOT IN", +- /* 208 */ "expr ::= expr in_op LP exprlist RP", +- /* 209 */ "expr ::= LP select RP", +- /* 210 */ "expr ::= expr in_op LP select RP", +- /* 211 */ "expr ::= expr in_op nm dbnm paren_exprlist", +- /* 212 */ "expr ::= EXISTS LP select RP", +- /* 213 */ "expr ::= CASE case_operand case_exprlist case_else END", +- /* 214 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", +- /* 215 */ "case_exprlist ::= WHEN expr THEN expr", +- /* 216 */ "case_else ::= ELSE expr", +- /* 217 */ "case_else ::=", +- /* 218 */ "case_operand ::= expr", +- /* 219 */ "case_operand ::=", +- /* 220 */ "exprlist ::=", +- /* 221 */ "nexprlist ::= nexprlist COMMA expr", +- /* 222 */ "nexprlist ::= expr", +- /* 223 */ "paren_exprlist ::=", +- /* 224 */ "paren_exprlist ::= LP exprlist RP", +- /* 225 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", +- /* 226 */ "uniqueflag ::= UNIQUE", +- /* 227 */ "uniqueflag ::=", +- /* 228 */ "eidlist_opt ::=", +- /* 229 */ "eidlist_opt ::= LP eidlist RP", +- /* 230 */ "eidlist ::= eidlist COMMA nm collate sortorder", +- /* 231 */ "eidlist ::= nm collate sortorder", +- /* 232 */ "collate ::=", +- /* 233 */ "collate ::= COLLATE ID|STRING", +- /* 234 */ "cmd ::= DROP INDEX ifexists fullname", +- /* 235 */ "cmd ::= VACUUM vinto", +- /* 236 */ "cmd ::= VACUUM nm vinto", +- /* 237 */ "vinto ::= INTO expr", +- /* 238 */ "vinto ::=", +- /* 239 */ "cmd ::= PRAGMA nm dbnm", +- /* 240 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", +- /* 241 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", +- /* 242 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", +- /* 243 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", +- /* 244 */ "plus_num ::= PLUS INTEGER|FLOAT", +- /* 245 */ "minus_num ::= MINUS INTEGER|FLOAT", +- /* 246 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", +- /* 247 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", +- /* 248 */ "trigger_time ::= BEFORE|AFTER", +- /* 249 */ "trigger_time ::= INSTEAD OF", +- /* 250 */ "trigger_time ::=", +- /* 251 */ "trigger_event ::= DELETE|INSERT", +- /* 252 */ "trigger_event ::= UPDATE", +- /* 253 */ "trigger_event ::= UPDATE OF idlist", +- /* 254 */ "when_clause ::=", +- /* 255 */ "when_clause ::= WHEN expr", +- /* 256 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", +- /* 257 */ "trigger_cmd_list ::= trigger_cmd SEMI", +- /* 258 */ "trnm ::= nm DOT nm", +- /* 259 */ "tridxby ::= INDEXED BY nm", +- /* 260 */ "tridxby ::= NOT INDEXED", +- /* 261 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", +- /* 262 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", +- /* 263 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", +- /* 264 */ "trigger_cmd ::= scanpt select scanpt", +- /* 265 */ "expr ::= RAISE LP IGNORE RP", +- /* 266 */ "expr ::= RAISE LP raisetype COMMA nm RP", +- /* 267 */ "raisetype ::= ROLLBACK", +- /* 268 */ "raisetype ::= ABORT", +- /* 269 */ "raisetype ::= FAIL", +- /* 270 */ "cmd ::= DROP TRIGGER ifexists fullname", +- /* 271 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", +- /* 272 */ "cmd ::= DETACH database_kw_opt expr", +- /* 273 */ "key_opt ::=", +- /* 274 */ "key_opt ::= KEY expr", +- /* 275 */ "cmd ::= REINDEX", +- /* 276 */ "cmd ::= REINDEX nm dbnm", +- /* 277 */ "cmd ::= ANALYZE", +- /* 278 */ "cmd ::= ANALYZE nm dbnm", +- /* 279 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", +- /* 280 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", +- /* 281 */ "add_column_fullname ::= fullname", +- /* 282 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", +- /* 283 */ "cmd ::= create_vtab", +- /* 284 */ "cmd ::= create_vtab LP vtabarglist RP", +- /* 285 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", +- /* 286 */ "vtabarg ::=", +- /* 287 */ "vtabargtoken ::= ANY", +- /* 288 */ "vtabargtoken ::= lp anylist RP", +- /* 289 */ "lp ::= LP", +- /* 290 */ "with ::= WITH wqlist", +- /* 291 */ "with ::= WITH RECURSIVE wqlist", +- /* 292 */ "wqlist ::= nm eidlist_opt AS LP select RP", +- /* 293 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", +- /* 294 */ "windowdefn_list ::= windowdefn", +- /* 295 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", +- /* 296 */ "windowdefn ::= nm AS LP window RP", +- /* 297 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", +- /* 298 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", +- /* 299 */ "window ::= ORDER BY sortlist frame_opt", +- /* 300 */ "window ::= nm ORDER BY sortlist frame_opt", +- /* 301 */ "window ::= frame_opt", +- /* 302 */ "window ::= nm frame_opt", +- /* 303 */ "frame_opt ::=", +- /* 304 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", +- /* 305 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", +- /* 306 */ "range_or_rows ::= RANGE|ROWS|GROUPS", +- /* 307 */ "frame_bound_s ::= frame_bound", +- /* 308 */ "frame_bound_s ::= UNBOUNDED PRECEDING", +- /* 309 */ "frame_bound_e ::= frame_bound", +- /* 310 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", +- /* 311 */ "frame_bound ::= expr PRECEDING|FOLLOWING", +- /* 312 */ "frame_bound ::= CURRENT ROW", +- /* 313 */ "frame_exclude_opt ::=", +- /* 314 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", +- /* 315 */ "frame_exclude ::= NO OTHERS", +- /* 316 */ "frame_exclude ::= CURRENT ROW", +- /* 317 */ "frame_exclude ::= GROUP|TIES", +- /* 318 */ "window_clause ::= WINDOW windowdefn_list", +- /* 319 */ "filter_over ::= filter_clause over_clause", +- /* 320 */ "filter_over ::= over_clause", +- /* 321 */ "filter_over ::= filter_clause", +- /* 322 */ "over_clause ::= OVER LP window RP", +- /* 323 */ "over_clause ::= OVER nm", +- /* 324 */ "filter_clause ::= FILTER LP WHERE expr RP", +- /* 325 */ "input ::= cmdlist", +- /* 326 */ "cmdlist ::= cmdlist ecmd", +- /* 327 */ "cmdlist ::= ecmd", +- /* 328 */ "ecmd ::= SEMI", +- /* 329 */ "ecmd ::= cmdx SEMI", +- /* 330 */ "ecmd ::= explain cmdx SEMI", +- /* 331 */ "trans_opt ::=", +- /* 332 */ "trans_opt ::= TRANSACTION", +- /* 333 */ "trans_opt ::= TRANSACTION nm", +- /* 334 */ "savepoint_opt ::= SAVEPOINT", +- /* 335 */ "savepoint_opt ::=", +- /* 336 */ "cmd ::= create_table create_table_args", +- /* 337 */ "columnlist ::= columnlist COMMA columnname carglist", +- /* 338 */ "columnlist ::= columnname carglist", +- /* 339 */ "nm ::= ID|INDEXED", +- /* 340 */ "nm ::= STRING", +- /* 341 */ "nm ::= JOIN_KW", +- /* 342 */ "typetoken ::= typename", +- /* 343 */ "typename ::= ID|STRING", +- /* 344 */ "signed ::= plus_num", +- /* 345 */ "signed ::= minus_num", +- /* 346 */ "carglist ::= carglist ccons", +- /* 347 */ "carglist ::=", +- /* 348 */ "ccons ::= NULL onconf", +- /* 349 */ "ccons ::= GENERATED ALWAYS AS generated", +- /* 350 */ "ccons ::= AS generated", +- /* 351 */ "conslist_opt ::= COMMA conslist", +- /* 352 */ "conslist ::= conslist tconscomma tcons", +- /* 353 */ "conslist ::= tcons", +- /* 354 */ "tconscomma ::=", +- /* 355 */ "defer_subclause_opt ::= defer_subclause", +- /* 356 */ "resolvetype ::= raisetype", +- /* 357 */ "selectnowith ::= oneselect", +- /* 358 */ "oneselect ::= values", +- /* 359 */ "sclp ::= selcollist COMMA", +- /* 360 */ "as ::= ID|STRING", +- /* 361 */ "expr ::= term", +- /* 362 */ "likeop ::= LIKE_KW|MATCH", +- /* 363 */ "exprlist ::= nexprlist", +- /* 364 */ "nmnum ::= plus_num", +- /* 365 */ "nmnum ::= nm", +- /* 366 */ "nmnum ::= ON", +- /* 367 */ "nmnum ::= DELETE", +- /* 368 */ "nmnum ::= DEFAULT", +- /* 369 */ "plus_num ::= INTEGER|FLOAT", +- /* 370 */ "foreach_clause ::=", +- /* 371 */ "foreach_clause ::= FOR EACH ROW", +- /* 372 */ "trnm ::= nm", +- /* 373 */ "tridxby ::=", +- /* 374 */ "database_kw_opt ::= DATABASE", +- /* 375 */ "database_kw_opt ::=", +- /* 376 */ "kwcolumn_opt ::=", +- /* 377 */ "kwcolumn_opt ::= COLUMNKW", +- /* 378 */ "vtabarglist ::= vtabarg", +- /* 379 */ "vtabarglist ::= vtabarglist COMMA vtabarg", +- /* 380 */ "vtabarg ::= vtabarg vtabargtoken", +- /* 381 */ "anylist ::=", +- /* 382 */ "anylist ::= anylist LP anylist RP", +- /* 383 */ "anylist ::= anylist ANY", +- /* 384 */ "with ::=", ++ /* 21 */ "table_option_set ::=", ++ /* 22 */ "table_option_set ::= table_option_set COMMA table_option", ++ /* 23 */ "table_option ::= WITHOUT nm", ++ /* 24 */ "table_option ::= nm", ++ /* 25 */ "columnname ::= nm typetoken", ++ /* 26 */ "typetoken ::=", ++ /* 27 */ "typetoken ::= typename LP signed RP", ++ /* 28 */ "typetoken ::= typename LP signed COMMA signed RP", ++ /* 29 */ "typename ::= typename ID|STRING", ++ /* 30 */ "scanpt ::=", ++ /* 31 */ "scantok ::=", ++ /* 32 */ "ccons ::= CONSTRAINT nm", ++ /* 33 */ "ccons ::= DEFAULT scantok term", ++ /* 34 */ "ccons ::= DEFAULT LP expr RP", ++ /* 35 */ "ccons ::= DEFAULT PLUS scantok term", ++ /* 36 */ "ccons ::= DEFAULT MINUS scantok term", ++ /* 37 */ "ccons ::= DEFAULT scantok ID|INDEXED", ++ /* 38 */ "ccons ::= NOT NULL onconf", ++ /* 39 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", ++ /* 40 */ "ccons ::= UNIQUE onconf", ++ /* 41 */ "ccons ::= CHECK LP expr RP", ++ /* 42 */ "ccons ::= REFERENCES nm eidlist_opt refargs", ++ /* 43 */ "ccons ::= defer_subclause", ++ /* 44 */ "ccons ::= COLLATE ID|STRING", ++ /* 45 */ "generated ::= LP expr RP", ++ /* 46 */ "generated ::= LP expr RP ID", ++ /* 47 */ "autoinc ::=", ++ /* 48 */ "autoinc ::= AUTOINCR", ++ /* 49 */ "refargs ::=", ++ /* 50 */ "refargs ::= refargs refarg", ++ /* 51 */ "refarg ::= MATCH nm", ++ /* 52 */ "refarg ::= ON INSERT refact", ++ /* 53 */ "refarg ::= ON DELETE refact", ++ /* 54 */ "refarg ::= ON UPDATE refact", ++ /* 55 */ "refact ::= SET NULL", ++ /* 56 */ "refact ::= SET DEFAULT", ++ /* 57 */ "refact ::= CASCADE", ++ /* 58 */ "refact ::= RESTRICT", ++ /* 59 */ "refact ::= NO ACTION", ++ /* 60 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", ++ /* 61 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", ++ /* 62 */ "init_deferred_pred_opt ::=", ++ /* 63 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", ++ /* 64 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", ++ /* 65 */ "conslist_opt ::=", ++ /* 66 */ "tconscomma ::= COMMA", ++ /* 67 */ "tcons ::= CONSTRAINT nm", ++ /* 68 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", ++ /* 69 */ "tcons ::= UNIQUE LP sortlist RP onconf", ++ /* 70 */ "tcons ::= CHECK LP expr RP onconf", ++ /* 71 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", ++ /* 72 */ "defer_subclause_opt ::=", ++ /* 73 */ "onconf ::=", ++ /* 74 */ "onconf ::= ON CONFLICT resolvetype", ++ /* 75 */ "orconf ::=", ++ /* 76 */ "orconf ::= OR resolvetype", ++ /* 77 */ "resolvetype ::= IGNORE", ++ /* 78 */ "resolvetype ::= REPLACE", ++ /* 79 */ "cmd ::= DROP TABLE ifexists fullname", ++ /* 80 */ "ifexists ::= IF EXISTS", ++ /* 81 */ "ifexists ::=", ++ /* 82 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", ++ /* 83 */ "cmd ::= DROP VIEW ifexists fullname", ++ /* 84 */ "cmd ::= select", ++ /* 85 */ "select ::= WITH wqlist selectnowith", ++ /* 86 */ "select ::= WITH RECURSIVE wqlist selectnowith", ++ /* 87 */ "select ::= selectnowith", ++ /* 88 */ "selectnowith ::= selectnowith multiselect_op oneselect", ++ /* 89 */ "multiselect_op ::= UNION", ++ /* 90 */ "multiselect_op ::= UNION ALL", ++ /* 91 */ "multiselect_op ::= EXCEPT|INTERSECT", ++ /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", ++ /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", ++ /* 94 */ "values ::= VALUES LP nexprlist RP", ++ /* 95 */ "oneselect ::= mvalues", ++ /* 96 */ "mvalues ::= values COMMA LP nexprlist RP", ++ /* 97 */ "mvalues ::= mvalues COMMA LP nexprlist RP", ++ /* 98 */ "distinct ::= DISTINCT", ++ /* 99 */ "distinct ::= ALL", ++ /* 100 */ "distinct ::=", ++ /* 101 */ "sclp ::=", ++ /* 102 */ "selcollist ::= sclp scanpt expr scanpt as", ++ /* 103 */ "selcollist ::= sclp scanpt STAR", ++ /* 104 */ "selcollist ::= sclp scanpt nm DOT STAR", ++ /* 105 */ "as ::= AS nm", ++ /* 106 */ "as ::=", ++ /* 107 */ "from ::=", ++ /* 108 */ "from ::= FROM seltablist", ++ /* 109 */ "stl_prefix ::= seltablist joinop", ++ /* 110 */ "stl_prefix ::=", ++ /* 111 */ "seltablist ::= stl_prefix nm dbnm as on_using", ++ /* 112 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", ++ /* 113 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", ++ /* 114 */ "seltablist ::= stl_prefix LP select RP as on_using", ++ /* 115 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", ++ /* 116 */ "dbnm ::=", ++ /* 117 */ "dbnm ::= DOT nm", ++ /* 118 */ "fullname ::= nm", ++ /* 119 */ "fullname ::= nm DOT nm", ++ /* 120 */ "xfullname ::= nm", ++ /* 121 */ "xfullname ::= nm DOT nm", ++ /* 122 */ "xfullname ::= nm DOT nm AS nm", ++ /* 123 */ "xfullname ::= nm AS nm", ++ /* 124 */ "joinop ::= COMMA|JOIN", ++ /* 125 */ "joinop ::= JOIN_KW JOIN", ++ /* 126 */ "joinop ::= JOIN_KW nm JOIN", ++ /* 127 */ "joinop ::= JOIN_KW nm nm JOIN", ++ /* 128 */ "on_using ::= ON expr", ++ /* 129 */ "on_using ::= USING LP idlist RP", ++ /* 130 */ "on_using ::=", ++ /* 131 */ "indexed_opt ::=", ++ /* 132 */ "indexed_by ::= INDEXED BY nm", ++ /* 133 */ "indexed_by ::= NOT INDEXED", ++ /* 134 */ "orderby_opt ::=", ++ /* 135 */ "orderby_opt ::= ORDER BY sortlist", ++ /* 136 */ "sortlist ::= sortlist COMMA expr sortorder nulls", ++ /* 137 */ "sortlist ::= expr sortorder nulls", ++ /* 138 */ "sortorder ::= ASC", ++ /* 139 */ "sortorder ::= DESC", ++ /* 140 */ "sortorder ::=", ++ /* 141 */ "nulls ::= NULLS FIRST", ++ /* 142 */ "nulls ::= NULLS LAST", ++ /* 143 */ "nulls ::=", ++ /* 144 */ "groupby_opt ::=", ++ /* 145 */ "groupby_opt ::= GROUP BY nexprlist", ++ /* 146 */ "having_opt ::=", ++ /* 147 */ "having_opt ::= HAVING expr", ++ /* 148 */ "limit_opt ::=", ++ /* 149 */ "limit_opt ::= LIMIT expr", ++ /* 150 */ "limit_opt ::= LIMIT expr OFFSET expr", ++ /* 151 */ "limit_opt ::= LIMIT expr COMMA expr", ++ /* 152 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", ++ /* 153 */ "where_opt ::=", ++ /* 154 */ "where_opt ::= WHERE expr", ++ /* 155 */ "where_opt_ret ::=", ++ /* 156 */ "where_opt_ret ::= WHERE expr", ++ /* 157 */ "where_opt_ret ::= RETURNING selcollist", ++ /* 158 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", ++ /* 159 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", ++ /* 160 */ "setlist ::= setlist COMMA nm EQ expr", ++ /* 161 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", ++ /* 162 */ "setlist ::= nm EQ expr", ++ /* 163 */ "setlist ::= LP idlist RP EQ expr", ++ /* 164 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", ++ /* 165 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", ++ /* 166 */ "upsert ::=", ++ /* 167 */ "upsert ::= RETURNING selcollist", ++ /* 168 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", ++ /* 169 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", ++ /* 170 */ "upsert ::= ON CONFLICT DO NOTHING returning", ++ /* 171 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", ++ /* 172 */ "returning ::= RETURNING selcollist", ++ /* 173 */ "insert_cmd ::= INSERT orconf", ++ /* 174 */ "insert_cmd ::= REPLACE", ++ /* 175 */ "idlist_opt ::=", ++ /* 176 */ "idlist_opt ::= LP idlist RP", ++ /* 177 */ "idlist ::= idlist COMMA nm", ++ /* 178 */ "idlist ::= nm", ++ /* 179 */ "expr ::= LP expr RP", ++ /* 180 */ "expr ::= ID|INDEXED|JOIN_KW", ++ /* 181 */ "expr ::= nm DOT nm", ++ /* 182 */ "expr ::= nm DOT nm DOT nm", ++ /* 183 */ "term ::= NULL|FLOAT|BLOB", ++ /* 184 */ "term ::= STRING", ++ /* 185 */ "term ::= INTEGER", ++ /* 186 */ "expr ::= VARIABLE", ++ /* 187 */ "expr ::= expr COLLATE ID|STRING", ++ /* 188 */ "expr ::= CAST LP expr AS typetoken RP", ++ /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP", ++ /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP", ++ /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", ++ /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", ++ /* 193 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over", ++ /* 194 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", ++ /* 195 */ "term ::= CTIME_KW", ++ /* 196 */ "expr ::= LP nexprlist COMMA expr RP", ++ /* 197 */ "expr ::= expr AND expr", ++ /* 198 */ "expr ::= expr OR expr", ++ /* 199 */ "expr ::= expr LT|GT|GE|LE expr", ++ /* 200 */ "expr ::= expr EQ|NE expr", ++ /* 201 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", ++ /* 202 */ "expr ::= expr PLUS|MINUS expr", ++ /* 203 */ "expr ::= expr STAR|SLASH|REM expr", ++ /* 204 */ "expr ::= expr CONCAT expr", ++ /* 205 */ "likeop ::= NOT LIKE_KW|MATCH", ++ /* 206 */ "expr ::= expr likeop expr", ++ /* 207 */ "expr ::= expr likeop expr ESCAPE expr", ++ /* 208 */ "expr ::= expr ISNULL|NOTNULL", ++ /* 209 */ "expr ::= expr NOT NULL", ++ /* 210 */ "expr ::= expr IS expr", ++ /* 211 */ "expr ::= expr IS NOT expr", ++ /* 212 */ "expr ::= expr IS NOT DISTINCT FROM expr", ++ /* 213 */ "expr ::= expr IS DISTINCT FROM expr", ++ /* 214 */ "expr ::= NOT expr", ++ /* 215 */ "expr ::= BITNOT expr", ++ /* 216 */ "expr ::= PLUS|MINUS expr", ++ /* 217 */ "expr ::= expr PTR expr", ++ /* 218 */ "between_op ::= BETWEEN", ++ /* 219 */ "between_op ::= NOT BETWEEN", ++ /* 220 */ "expr ::= expr between_op expr AND expr", ++ /* 221 */ "in_op ::= IN", ++ /* 222 */ "in_op ::= NOT IN", ++ /* 223 */ "expr ::= expr in_op LP exprlist RP", ++ /* 224 */ "expr ::= LP select RP", ++ /* 225 */ "expr ::= expr in_op LP select RP", ++ /* 226 */ "expr ::= expr in_op nm dbnm paren_exprlist", ++ /* 227 */ "expr ::= EXISTS LP select RP", ++ /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END", ++ /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", ++ /* 230 */ "case_exprlist ::= WHEN expr THEN expr", ++ /* 231 */ "case_else ::= ELSE expr", ++ /* 232 */ "case_else ::=", ++ /* 233 */ "case_operand ::=", ++ /* 234 */ "exprlist ::=", ++ /* 235 */ "nexprlist ::= nexprlist COMMA expr", ++ /* 236 */ "nexprlist ::= expr", ++ /* 237 */ "paren_exprlist ::=", ++ /* 238 */ "paren_exprlist ::= LP exprlist RP", ++ /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", ++ /* 240 */ "uniqueflag ::= UNIQUE", ++ /* 241 */ "uniqueflag ::=", ++ /* 242 */ "eidlist_opt ::=", ++ /* 243 */ "eidlist_opt ::= LP eidlist RP", ++ /* 244 */ "eidlist ::= eidlist COMMA nm collate sortorder", ++ /* 245 */ "eidlist ::= nm collate sortorder", ++ /* 246 */ "collate ::=", ++ /* 247 */ "collate ::= COLLATE ID|STRING", ++ /* 248 */ "cmd ::= DROP INDEX ifexists fullname", ++ /* 249 */ "cmd ::= VACUUM vinto", ++ /* 250 */ "cmd ::= VACUUM nm vinto", ++ /* 251 */ "vinto ::= INTO expr", ++ /* 252 */ "vinto ::=", ++ /* 253 */ "cmd ::= PRAGMA nm dbnm", ++ /* 254 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", ++ /* 255 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", ++ /* 256 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", ++ /* 257 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", ++ /* 258 */ "plus_num ::= PLUS INTEGER|FLOAT", ++ /* 259 */ "minus_num ::= MINUS INTEGER|FLOAT", ++ /* 260 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", ++ /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", ++ /* 262 */ "trigger_time ::= BEFORE|AFTER", ++ /* 263 */ "trigger_time ::= INSTEAD OF", ++ /* 264 */ "trigger_time ::=", ++ /* 265 */ "trigger_event ::= DELETE|INSERT", ++ /* 266 */ "trigger_event ::= UPDATE", ++ /* 267 */ "trigger_event ::= UPDATE OF idlist", ++ /* 268 */ "when_clause ::=", ++ /* 269 */ "when_clause ::= WHEN expr", ++ /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", ++ /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI", ++ /* 272 */ "trnm ::= nm DOT nm", ++ /* 273 */ "tridxby ::= INDEXED BY nm", ++ /* 274 */ "tridxby ::= NOT INDEXED", ++ /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", ++ /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", ++ /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", ++ /* 278 */ "trigger_cmd ::= scanpt select scanpt", ++ /* 279 */ "expr ::= RAISE LP IGNORE RP", ++ /* 280 */ "expr ::= RAISE LP raisetype COMMA expr RP", ++ /* 281 */ "raisetype ::= ROLLBACK", ++ /* 282 */ "raisetype ::= ABORT", ++ /* 283 */ "raisetype ::= FAIL", ++ /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname", ++ /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", ++ /* 286 */ "cmd ::= DETACH database_kw_opt expr", ++ /* 287 */ "key_opt ::=", ++ /* 288 */ "key_opt ::= KEY expr", ++ /* 289 */ "cmd ::= REINDEX", ++ /* 290 */ "cmd ::= REINDEX nm dbnm", ++ /* 291 */ "cmd ::= ANALYZE", ++ /* 292 */ "cmd ::= ANALYZE nm dbnm", ++ /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", ++ /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", ++ /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", ++ /* 296 */ "add_column_fullname ::= fullname", ++ /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", ++ /* 298 */ "cmd ::= create_vtab", ++ /* 299 */ "cmd ::= create_vtab LP vtabarglist RP", ++ /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", ++ /* 301 */ "vtabarg ::=", ++ /* 302 */ "vtabargtoken ::= ANY", ++ /* 303 */ "vtabargtoken ::= lp anylist RP", ++ /* 304 */ "lp ::= LP", ++ /* 305 */ "with ::= WITH wqlist", ++ /* 306 */ "with ::= WITH RECURSIVE wqlist", ++ /* 307 */ "wqas ::= AS", ++ /* 308 */ "wqas ::= AS MATERIALIZED", ++ /* 309 */ "wqas ::= AS NOT MATERIALIZED", ++ /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP", ++ /* 311 */ "withnm ::= nm", ++ /* 312 */ "wqlist ::= wqitem", ++ /* 313 */ "wqlist ::= wqlist COMMA wqitem", ++ /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", ++ /* 315 */ "windowdefn ::= nm AS LP window RP", ++ /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", ++ /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", ++ /* 318 */ "window ::= ORDER BY sortlist frame_opt", ++ /* 319 */ "window ::= nm ORDER BY sortlist frame_opt", ++ /* 320 */ "window ::= nm frame_opt", ++ /* 321 */ "frame_opt ::=", ++ /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", ++ /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", ++ /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS", ++ /* 325 */ "frame_bound_s ::= frame_bound", ++ /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING", ++ /* 327 */ "frame_bound_e ::= frame_bound", ++ /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", ++ /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING", ++ /* 330 */ "frame_bound ::= CURRENT ROW", ++ /* 331 */ "frame_exclude_opt ::=", ++ /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", ++ /* 333 */ "frame_exclude ::= NO OTHERS", ++ /* 334 */ "frame_exclude ::= CURRENT ROW", ++ /* 335 */ "frame_exclude ::= GROUP|TIES", ++ /* 336 */ "window_clause ::= WINDOW windowdefn_list", ++ /* 337 */ "filter_over ::= filter_clause over_clause", ++ /* 338 */ "filter_over ::= over_clause", ++ /* 339 */ "filter_over ::= filter_clause", ++ /* 340 */ "over_clause ::= OVER LP window RP", ++ /* 341 */ "over_clause ::= OVER nm", ++ /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP", ++ /* 343 */ "term ::= QNUMBER", ++ /* 344 */ "input ::= cmdlist", ++ /* 345 */ "cmdlist ::= cmdlist ecmd", ++ /* 346 */ "cmdlist ::= ecmd", ++ /* 347 */ "ecmd ::= SEMI", ++ /* 348 */ "ecmd ::= cmdx SEMI", ++ /* 349 */ "ecmd ::= explain cmdx SEMI", ++ /* 350 */ "trans_opt ::=", ++ /* 351 */ "trans_opt ::= TRANSACTION", ++ /* 352 */ "trans_opt ::= TRANSACTION nm", ++ /* 353 */ "savepoint_opt ::= SAVEPOINT", ++ /* 354 */ "savepoint_opt ::=", ++ /* 355 */ "cmd ::= create_table create_table_args", ++ /* 356 */ "table_option_set ::= table_option", ++ /* 357 */ "columnlist ::= columnlist COMMA columnname carglist", ++ /* 358 */ "columnlist ::= columnname carglist", ++ /* 359 */ "nm ::= ID|INDEXED|JOIN_KW", ++ /* 360 */ "nm ::= STRING", ++ /* 361 */ "typetoken ::= typename", ++ /* 362 */ "typename ::= ID|STRING", ++ /* 363 */ "signed ::= plus_num", ++ /* 364 */ "signed ::= minus_num", ++ /* 365 */ "carglist ::= carglist ccons", ++ /* 366 */ "carglist ::=", ++ /* 367 */ "ccons ::= NULL onconf", ++ /* 368 */ "ccons ::= GENERATED ALWAYS AS generated", ++ /* 369 */ "ccons ::= AS generated", ++ /* 370 */ "conslist_opt ::= COMMA conslist", ++ /* 371 */ "conslist ::= conslist tconscomma tcons", ++ /* 372 */ "conslist ::= tcons", ++ /* 373 */ "tconscomma ::=", ++ /* 374 */ "defer_subclause_opt ::= defer_subclause", ++ /* 375 */ "resolvetype ::= raisetype", ++ /* 376 */ "selectnowith ::= oneselect", ++ /* 377 */ "oneselect ::= values", ++ /* 378 */ "sclp ::= selcollist COMMA", ++ /* 379 */ "as ::= ID|STRING", ++ /* 380 */ "indexed_opt ::= indexed_by", ++ /* 381 */ "returning ::=", ++ /* 382 */ "expr ::= term", ++ /* 383 */ "likeop ::= LIKE_KW|MATCH", ++ /* 384 */ "case_operand ::= expr", ++ /* 385 */ "exprlist ::= nexprlist", ++ /* 386 */ "nmnum ::= plus_num", ++ /* 387 */ "nmnum ::= nm", ++ /* 388 */ "nmnum ::= ON", ++ /* 389 */ "nmnum ::= DELETE", ++ /* 390 */ "nmnum ::= DEFAULT", ++ /* 391 */ "plus_num ::= INTEGER|FLOAT", ++ /* 392 */ "foreach_clause ::=", ++ /* 393 */ "foreach_clause ::= FOR EACH ROW", ++ /* 394 */ "trnm ::= nm", ++ /* 395 */ "tridxby ::=", ++ /* 396 */ "database_kw_opt ::= DATABASE", ++ /* 397 */ "database_kw_opt ::=", ++ /* 398 */ "kwcolumn_opt ::=", ++ /* 399 */ "kwcolumn_opt ::= COLUMNKW", ++ /* 400 */ "vtabarglist ::= vtabarg", ++ /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg", ++ /* 402 */ "vtabarg ::= vtabarg vtabargtoken", ++ /* 403 */ "anylist ::=", ++ /* 404 */ "anylist ::= anylist LP anylist RP", ++ /* 405 */ "anylist ::= anylist ANY", ++ /* 406 */ "with ::=", ++ /* 407 */ "windowdefn_list ::= windowdefn", ++ /* 408 */ "window ::= frame_opt", + }; + #endif /* NDEBUG */ + + +-#if YYSTACKDEPTH<=0 ++#if YYGROWABLESTACK + /* + ** Try to increase the size of the parser stack. Return the number + ** of errors. Return 0 on success. + */ + static int yyGrowStack(yyParser *p){ ++ int oldSize = 1 + (int)(p->yystackEnd - p->yystack); + int newSize; + int idx; + yyStackEntry *pNew; + +- newSize = p->yystksz*2 + 100; +- idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; +- if( p->yystack==&p->yystk0 ){ +- pNew = malloc(newSize*sizeof(pNew[0])); +- if( pNew ) pNew[0] = p->yystk0; ++ newSize = oldSize*2 + 100; ++ idx = (int)(p->yytos - p->yystack); ++ if( p->yystack==p->yystk0 ){ ++ pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); ++ if( pNew==0 ) return 1; ++ memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); + }else{ +- pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); ++ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); ++ if( pNew==0 ) return 1; + } +- if( pNew ){ +- p->yystack = pNew; +- p->yytos = &p->yystack[idx]; ++ p->yystack = pNew; ++ p->yytos = &p->yystack[idx]; + #ifndef NDEBUG +- if( yyTraceFILE ){ +- fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", +- yyTracePrompt, p->yystksz, newSize); +- } +-#endif +- p->yystksz = newSize; ++ if( yyTraceFILE ){ ++ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", ++ yyTracePrompt, oldSize, newSize); + } +- return pNew==0; ++#endif ++ p->yystackEnd = &p->yystack[newSize-1]; ++ return 0; + } ++#endif /* YYGROWABLESTACK */ ++ ++#if !YYGROWABLESTACK ++/* For builds that do no have a growable stack, yyGrowStack always ++** returns an error. ++*/ ++# define yyGrowStack(X) 1 + #endif + + /* Datatype of the argument to the memory allocated passed as the +@@ -156218,24 +177633,14 @@ SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL) + #ifdef YYTRACKMAXSTACKDEPTH + yypParser->yyhwm = 0; + #endif +-#if YYSTACKDEPTH<=0 +- yypParser->yytos = NULL; +- yypParser->yystack = NULL; +- yypParser->yystksz = 0; +- if( yyGrowStack(yypParser) ){ +- yypParser->yystack = &yypParser->yystk0; +- yypParser->yystksz = 1; +- } +-#endif ++ yypParser->yystack = yypParser->yystk0; ++ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; + #ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; + #endif + yypParser->yytos = yypParser->yystack; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; +-#if YYSTACKDEPTH>0 +- yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; +-#endif + } + + #ifndef sqlite3Parser_ENGINEALWAYSONSTACK +@@ -156289,98 +177694,98 @@ static void yy_destructor( + ** inside the C code. + */ + /********* Begin destructor definitions ***************************************/ +- case 200: /* select */ +- case 234: /* selectnowith */ +- case 235: /* oneselect */ +- case 247: /* values */ ++ case 206: /* select */ ++ case 241: /* selectnowith */ ++ case 242: /* oneselect */ ++ case 254: /* values */ ++ case 256: /* mvalues */ + { +-sqlite3SelectDelete(pParse->db, (yypminor->yy539)); +-} +- break; +- case 211: /* term */ +- case 212: /* expr */ +- case 241: /* where_opt */ +- case 243: /* having_opt */ +- case 255: /* on_opt */ +- case 271: /* case_operand */ +- case 273: /* case_else */ +- case 276: /* vinto */ +- case 283: /* when_clause */ +- case 288: /* key_opt */ +- case 302: /* filter_clause */ ++sqlite3SelectDelete(pParse->db, (yypminor->yy637)); ++} ++ break; ++ case 218: /* term */ ++ case 219: /* expr */ ++ case 248: /* where_opt */ ++ case 250: /* having_opt */ ++ case 270: /* where_opt_ret */ ++ case 281: /* case_operand */ ++ case 283: /* case_else */ ++ case 286: /* vinto */ ++ case 293: /* when_clause */ ++ case 298: /* key_opt */ ++ case 315: /* filter_clause */ + { +-sqlite3ExprDelete(pParse->db, (yypminor->yy202)); +-} +- break; +- case 216: /* eidlist_opt */ +- case 226: /* sortlist */ +- case 227: /* eidlist */ +- case 239: /* selcollist */ +- case 242: /* groupby_opt */ +- case 244: /* orderby_opt */ +- case 248: /* nexprlist */ +- case 249: /* sclp */ +- case 257: /* exprlist */ +- case 262: /* setlist */ +- case 270: /* paren_exprlist */ +- case 272: /* case_exprlist */ +- case 301: /* part_opt */ ++sqlite3ExprDelete(pParse->db, (yypminor->yy590)); ++} ++ break; ++ case 223: /* eidlist_opt */ ++ case 233: /* sortlist */ ++ case 234: /* eidlist */ ++ case 246: /* selcollist */ ++ case 249: /* groupby_opt */ ++ case 251: /* orderby_opt */ ++ case 255: /* nexprlist */ ++ case 257: /* sclp */ ++ case 264: /* exprlist */ ++ case 271: /* setlist */ ++ case 280: /* paren_exprlist */ ++ case 282: /* case_exprlist */ ++ case 314: /* part_opt */ + { +-sqlite3ExprListDelete(pParse->db, (yypminor->yy242)); ++sqlite3ExprListDelete(pParse->db, (yypminor->yy402)); + } + break; +- case 233: /* fullname */ +- case 240: /* from */ +- case 251: /* seltablist */ +- case 252: /* stl_prefix */ +- case 258: /* xfullname */ ++ case 240: /* fullname */ ++ case 247: /* from */ ++ case 259: /* seltablist */ ++ case 260: /* stl_prefix */ ++ case 265: /* xfullname */ + { +-sqlite3SrcListDelete(pParse->db, (yypminor->yy47)); ++sqlite3SrcListDelete(pParse->db, (yypminor->yy563)); + } + break; +- case 236: /* wqlist */ ++ case 243: /* wqlist */ + { +-sqlite3WithDelete(pParse->db, (yypminor->yy131)); ++sqlite3WithDelete(pParse->db, (yypminor->yy125)); + } + break; +- case 246: /* window_clause */ +- case 297: /* windowdefn_list */ ++ case 253: /* window_clause */ ++ case 310: /* windowdefn_list */ + { +-sqlite3WindowListDelete(pParse->db, (yypminor->yy303)); ++sqlite3WindowListDelete(pParse->db, (yypminor->yy483)); + } + break; +- case 256: /* using_opt */ +- case 259: /* idlist */ +- case 264: /* idlist_opt */ ++ case 266: /* idlist */ ++ case 273: /* idlist_opt */ + { +-sqlite3IdListDelete(pParse->db, (yypminor->yy600)); ++sqlite3IdListDelete(pParse->db, (yypminor->yy204)); + } + break; +- case 266: /* filter_over */ +- case 298: /* windowdefn */ +- case 299: /* window */ +- case 300: /* frame_opt */ +- case 303: /* over_clause */ ++ case 276: /* filter_over */ ++ case 311: /* windowdefn */ ++ case 312: /* window */ ++ case 313: /* frame_opt */ ++ case 316: /* over_clause */ + { +-sqlite3WindowDelete(pParse->db, (yypminor->yy303)); ++sqlite3WindowDelete(pParse->db, (yypminor->yy483)); + } + break; +- case 279: /* trigger_cmd_list */ +- case 284: /* trigger_cmd */ ++ case 289: /* trigger_cmd_list */ ++ case 294: /* trigger_cmd */ + { +-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy447)); ++sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy319)); + } + break; +- case 281: /* trigger_event */ ++ case 291: /* trigger_event */ + { +-sqlite3IdListDelete(pParse->db, (yypminor->yy230).b); ++sqlite3IdListDelete(pParse->db, (yypminor->yy28).b); + } + break; +- case 305: /* frame_bound */ +- case 306: /* frame_bound_s */ +- case 307: /* frame_bound_e */ ++ case 318: /* frame_bound */ ++ case 319: /* frame_bound_s */ ++ case 320: /* frame_bound_e */ + { +-sqlite3ExprDelete(pParse->db, (yypminor->yy77).pExpr); ++sqlite3ExprDelete(pParse->db, (yypminor->yy205).pExpr); + } + break; + /********* End destructor definitions *****************************************/ +@@ -156414,9 +177819,26 @@ static void yy_pop_parser_stack(yyParser *pParser){ + */ + SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){ + yyParser *pParser = (yyParser*)p; +- while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); +-#if YYSTACKDEPTH<=0 +- if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); ++ ++ /* In-lined version of calling yy_pop_parser_stack() for each ++ ** element left in the stack */ ++ yyStackEntry *yytos = pParser->yytos; ++ while( yytos>pParser->yystack ){ ++#ifndef NDEBUG ++ if( yyTraceFILE ){ ++ fprintf(yyTraceFILE,"%sPopping %s\n", ++ yyTracePrompt, ++ yyTokenName[yytos->major]); ++ } ++#endif ++ if( yytos->major>=YY_MIN_DSTRCTR ){ ++ yy_destructor(pParser, yytos->major, &yytos->minor); ++ } ++ yytos--; ++ } ++ ++#if YYGROWABLESTACK ++ if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); + #endif + } + +@@ -156547,7 +177969,7 @@ static YYACTIONTYPE yy_find_shift_action( + #endif /* YYWILDCARD */ + return yy_default[stateno]; + }else{ +- assert( i>=0 && i=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); + return yy_action[i]; + } + }while(1); +@@ -156599,7 +178021,7 @@ static void yyStackOverflow(yyParser *yypParser){ + ** stack every overflows */ + /******** Begin %stack_overflow code ******************************************/ + +- sqlite3ErrorMsg(pParse, "parser stack overflow"); ++ sqlite3OomFault(pParse->db); + /******** End %stack_overflow code ********************************************/ + sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */ + sqlite3ParserCTX_STORE +@@ -156643,25 +178065,19 @@ static void yy_shift( + assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); + } + #endif +-#if YYSTACKDEPTH>0 +- if( yypParser->yytos>yypParser->yystackEnd ){ +- yypParser->yytos--; +- yyStackOverflow(yypParser); +- return; +- } +-#else +- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ ++ yytos = yypParser->yytos; ++ if( yytos>yypParser->yystackEnd ){ + if( yyGrowStack(yypParser) ){ + yypParser->yytos--; + yyStackOverflow(yypParser); + return; + } ++ yytos = yypParser->yytos; ++ assert( yytos <= yypParser->yystackEnd ); + } +-#endif + if( yyNewState > YY_MAX_SHIFT ){ + yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; + } +- yytos = yypParser->yytos; + yytos->stateno = yyNewState; + yytos->major = yyMajor; + yytos->minor.yy0 = yyMinor; +@@ -156671,391 +178087,415 @@ static void yy_shift( + /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side + ** of that rule */ + static const YYCODETYPE yyRuleInfoLhs[] = { +- 185, /* (0) explain ::= EXPLAIN */ +- 185, /* (1) explain ::= EXPLAIN QUERY PLAN */ +- 184, /* (2) cmdx ::= cmd */ +- 186, /* (3) cmd ::= BEGIN transtype trans_opt */ +- 187, /* (4) transtype ::= */ +- 187, /* (5) transtype ::= DEFERRED */ +- 187, /* (6) transtype ::= IMMEDIATE */ +- 187, /* (7) transtype ::= EXCLUSIVE */ +- 186, /* (8) cmd ::= COMMIT|END trans_opt */ +- 186, /* (9) cmd ::= ROLLBACK trans_opt */ +- 186, /* (10) cmd ::= SAVEPOINT nm */ +- 186, /* (11) cmd ::= RELEASE savepoint_opt nm */ +- 186, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ +- 191, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ +- 193, /* (14) createkw ::= CREATE */ +- 195, /* (15) ifnotexists ::= */ +- 195, /* (16) ifnotexists ::= IF NOT EXISTS */ +- 194, /* (17) temp ::= TEMP */ +- 194, /* (18) temp ::= */ +- 192, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ +- 192, /* (20) create_table_args ::= AS select */ +- 199, /* (21) table_options ::= */ +- 199, /* (22) table_options ::= WITHOUT nm */ +- 201, /* (23) columnname ::= nm typetoken */ +- 203, /* (24) typetoken ::= */ +- 203, /* (25) typetoken ::= typename LP signed RP */ +- 203, /* (26) typetoken ::= typename LP signed COMMA signed RP */ +- 204, /* (27) typename ::= typename ID|STRING */ +- 208, /* (28) scanpt ::= */ +- 209, /* (29) scantok ::= */ +- 210, /* (30) ccons ::= CONSTRAINT nm */ +- 210, /* (31) ccons ::= DEFAULT scantok term */ +- 210, /* (32) ccons ::= DEFAULT LP expr RP */ +- 210, /* (33) ccons ::= DEFAULT PLUS scantok term */ +- 210, /* (34) ccons ::= DEFAULT MINUS scantok term */ +- 210, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ +- 210, /* (36) ccons ::= NOT NULL onconf */ +- 210, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ +- 210, /* (38) ccons ::= UNIQUE onconf */ +- 210, /* (39) ccons ::= CHECK LP expr RP */ +- 210, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ +- 210, /* (41) ccons ::= defer_subclause */ +- 210, /* (42) ccons ::= COLLATE ID|STRING */ +- 219, /* (43) generated ::= LP expr RP */ +- 219, /* (44) generated ::= LP expr RP ID */ +- 215, /* (45) autoinc ::= */ +- 215, /* (46) autoinc ::= AUTOINCR */ +- 217, /* (47) refargs ::= */ +- 217, /* (48) refargs ::= refargs refarg */ +- 220, /* (49) refarg ::= MATCH nm */ +- 220, /* (50) refarg ::= ON INSERT refact */ +- 220, /* (51) refarg ::= ON DELETE refact */ +- 220, /* (52) refarg ::= ON UPDATE refact */ +- 221, /* (53) refact ::= SET NULL */ +- 221, /* (54) refact ::= SET DEFAULT */ +- 221, /* (55) refact ::= CASCADE */ +- 221, /* (56) refact ::= RESTRICT */ +- 221, /* (57) refact ::= NO ACTION */ +- 218, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ +- 218, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ +- 222, /* (60) init_deferred_pred_opt ::= */ +- 222, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ +- 222, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ +- 198, /* (63) conslist_opt ::= */ +- 224, /* (64) tconscomma ::= COMMA */ +- 225, /* (65) tcons ::= CONSTRAINT nm */ +- 225, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ +- 225, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ +- 225, /* (68) tcons ::= CHECK LP expr RP onconf */ +- 225, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ +- 228, /* (70) defer_subclause_opt ::= */ +- 213, /* (71) onconf ::= */ +- 213, /* (72) onconf ::= ON CONFLICT resolvetype */ +- 229, /* (73) orconf ::= */ +- 229, /* (74) orconf ::= OR resolvetype */ +- 230, /* (75) resolvetype ::= IGNORE */ +- 230, /* (76) resolvetype ::= REPLACE */ +- 186, /* (77) cmd ::= DROP TABLE ifexists fullname */ +- 232, /* (78) ifexists ::= IF EXISTS */ +- 232, /* (79) ifexists ::= */ +- 186, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ +- 186, /* (81) cmd ::= DROP VIEW ifexists fullname */ +- 186, /* (82) cmd ::= select */ +- 200, /* (83) select ::= WITH wqlist selectnowith */ +- 200, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ +- 200, /* (85) select ::= selectnowith */ +- 234, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ +- 237, /* (87) multiselect_op ::= UNION */ +- 237, /* (88) multiselect_op ::= UNION ALL */ +- 237, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ +- 235, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ +- 235, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ +- 247, /* (92) values ::= VALUES LP nexprlist RP */ +- 247, /* (93) values ::= values COMMA LP nexprlist RP */ +- 238, /* (94) distinct ::= DISTINCT */ +- 238, /* (95) distinct ::= ALL */ +- 238, /* (96) distinct ::= */ +- 249, /* (97) sclp ::= */ +- 239, /* (98) selcollist ::= sclp scanpt expr scanpt as */ +- 239, /* (99) selcollist ::= sclp scanpt STAR */ +- 239, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ +- 250, /* (101) as ::= AS nm */ +- 250, /* (102) as ::= */ +- 240, /* (103) from ::= */ +- 240, /* (104) from ::= FROM seltablist */ +- 252, /* (105) stl_prefix ::= seltablist joinop */ +- 252, /* (106) stl_prefix ::= */ +- 251, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ +- 251, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ +- 251, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ +- 251, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ +- 196, /* (111) dbnm ::= */ +- 196, /* (112) dbnm ::= DOT nm */ +- 233, /* (113) fullname ::= nm */ +- 233, /* (114) fullname ::= nm DOT nm */ +- 258, /* (115) xfullname ::= nm */ +- 258, /* (116) xfullname ::= nm DOT nm */ +- 258, /* (117) xfullname ::= nm DOT nm AS nm */ +- 258, /* (118) xfullname ::= nm AS nm */ +- 253, /* (119) joinop ::= COMMA|JOIN */ +- 253, /* (120) joinop ::= JOIN_KW JOIN */ +- 253, /* (121) joinop ::= JOIN_KW nm JOIN */ +- 253, /* (122) joinop ::= JOIN_KW nm nm JOIN */ +- 255, /* (123) on_opt ::= ON expr */ +- 255, /* (124) on_opt ::= */ +- 254, /* (125) indexed_opt ::= */ +- 254, /* (126) indexed_opt ::= INDEXED BY nm */ +- 254, /* (127) indexed_opt ::= NOT INDEXED */ +- 256, /* (128) using_opt ::= USING LP idlist RP */ +- 256, /* (129) using_opt ::= */ +- 244, /* (130) orderby_opt ::= */ +- 244, /* (131) orderby_opt ::= ORDER BY sortlist */ +- 226, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ +- 226, /* (133) sortlist ::= expr sortorder nulls */ +- 214, /* (134) sortorder ::= ASC */ +- 214, /* (135) sortorder ::= DESC */ +- 214, /* (136) sortorder ::= */ +- 260, /* (137) nulls ::= NULLS FIRST */ +- 260, /* (138) nulls ::= NULLS LAST */ +- 260, /* (139) nulls ::= */ +- 242, /* (140) groupby_opt ::= */ +- 242, /* (141) groupby_opt ::= GROUP BY nexprlist */ +- 243, /* (142) having_opt ::= */ +- 243, /* (143) having_opt ::= HAVING expr */ +- 245, /* (144) limit_opt ::= */ +- 245, /* (145) limit_opt ::= LIMIT expr */ +- 245, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ +- 245, /* (147) limit_opt ::= LIMIT expr COMMA expr */ +- 186, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ +- 241, /* (149) where_opt ::= */ +- 241, /* (150) where_opt ::= WHERE expr */ +- 186, /* (151) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt */ +- 262, /* (152) setlist ::= setlist COMMA nm EQ expr */ +- 262, /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */ +- 262, /* (154) setlist ::= nm EQ expr */ +- 262, /* (155) setlist ::= LP idlist RP EQ expr */ +- 186, /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ +- 186, /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ +- 265, /* (158) upsert ::= */ +- 265, /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ +- 265, /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ +- 265, /* (161) upsert ::= ON CONFLICT DO NOTHING */ +- 263, /* (162) insert_cmd ::= INSERT orconf */ +- 263, /* (163) insert_cmd ::= REPLACE */ +- 264, /* (164) idlist_opt ::= */ +- 264, /* (165) idlist_opt ::= LP idlist RP */ +- 259, /* (166) idlist ::= idlist COMMA nm */ +- 259, /* (167) idlist ::= nm */ +- 212, /* (168) expr ::= LP expr RP */ +- 212, /* (169) expr ::= ID|INDEXED */ +- 212, /* (170) expr ::= JOIN_KW */ +- 212, /* (171) expr ::= nm DOT nm */ +- 212, /* (172) expr ::= nm DOT nm DOT nm */ +- 211, /* (173) term ::= NULL|FLOAT|BLOB */ +- 211, /* (174) term ::= STRING */ +- 211, /* (175) term ::= INTEGER */ +- 212, /* (176) expr ::= VARIABLE */ +- 212, /* (177) expr ::= expr COLLATE ID|STRING */ +- 212, /* (178) expr ::= CAST LP expr AS typetoken RP */ +- 212, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */ +- 212, /* (180) expr ::= ID|INDEXED LP STAR RP */ +- 212, /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ +- 212, /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */ +- 211, /* (183) term ::= CTIME_KW */ +- 212, /* (184) expr ::= LP nexprlist COMMA expr RP */ +- 212, /* (185) expr ::= expr AND expr */ +- 212, /* (186) expr ::= expr OR expr */ +- 212, /* (187) expr ::= expr LT|GT|GE|LE expr */ +- 212, /* (188) expr ::= expr EQ|NE expr */ +- 212, /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ +- 212, /* (190) expr ::= expr PLUS|MINUS expr */ +- 212, /* (191) expr ::= expr STAR|SLASH|REM expr */ +- 212, /* (192) expr ::= expr CONCAT expr */ +- 267, /* (193) likeop ::= NOT LIKE_KW|MATCH */ +- 212, /* (194) expr ::= expr likeop expr */ +- 212, /* (195) expr ::= expr likeop expr ESCAPE expr */ +- 212, /* (196) expr ::= expr ISNULL|NOTNULL */ +- 212, /* (197) expr ::= expr NOT NULL */ +- 212, /* (198) expr ::= expr IS expr */ +- 212, /* (199) expr ::= expr IS NOT expr */ +- 212, /* (200) expr ::= NOT expr */ +- 212, /* (201) expr ::= BITNOT expr */ +- 212, /* (202) expr ::= PLUS|MINUS expr */ +- 268, /* (203) between_op ::= BETWEEN */ +- 268, /* (204) between_op ::= NOT BETWEEN */ +- 212, /* (205) expr ::= expr between_op expr AND expr */ +- 269, /* (206) in_op ::= IN */ +- 269, /* (207) in_op ::= NOT IN */ +- 212, /* (208) expr ::= expr in_op LP exprlist RP */ +- 212, /* (209) expr ::= LP select RP */ +- 212, /* (210) expr ::= expr in_op LP select RP */ +- 212, /* (211) expr ::= expr in_op nm dbnm paren_exprlist */ +- 212, /* (212) expr ::= EXISTS LP select RP */ +- 212, /* (213) expr ::= CASE case_operand case_exprlist case_else END */ +- 272, /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */ +- 272, /* (215) case_exprlist ::= WHEN expr THEN expr */ +- 273, /* (216) case_else ::= ELSE expr */ +- 273, /* (217) case_else ::= */ +- 271, /* (218) case_operand ::= expr */ +- 271, /* (219) case_operand ::= */ +- 257, /* (220) exprlist ::= */ +- 248, /* (221) nexprlist ::= nexprlist COMMA expr */ +- 248, /* (222) nexprlist ::= expr */ +- 270, /* (223) paren_exprlist ::= */ +- 270, /* (224) paren_exprlist ::= LP exprlist RP */ +- 186, /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ +- 274, /* (226) uniqueflag ::= UNIQUE */ +- 274, /* (227) uniqueflag ::= */ +- 216, /* (228) eidlist_opt ::= */ +- 216, /* (229) eidlist_opt ::= LP eidlist RP */ +- 227, /* (230) eidlist ::= eidlist COMMA nm collate sortorder */ +- 227, /* (231) eidlist ::= nm collate sortorder */ +- 275, /* (232) collate ::= */ +- 275, /* (233) collate ::= COLLATE ID|STRING */ +- 186, /* (234) cmd ::= DROP INDEX ifexists fullname */ +- 186, /* (235) cmd ::= VACUUM vinto */ +- 186, /* (236) cmd ::= VACUUM nm vinto */ +- 276, /* (237) vinto ::= INTO expr */ +- 276, /* (238) vinto ::= */ +- 186, /* (239) cmd ::= PRAGMA nm dbnm */ +- 186, /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */ +- 186, /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */ +- 186, /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */ +- 186, /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */ +- 206, /* (244) plus_num ::= PLUS INTEGER|FLOAT */ +- 207, /* (245) minus_num ::= MINUS INTEGER|FLOAT */ +- 186, /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ +- 278, /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ +- 280, /* (248) trigger_time ::= BEFORE|AFTER */ +- 280, /* (249) trigger_time ::= INSTEAD OF */ +- 280, /* (250) trigger_time ::= */ +- 281, /* (251) trigger_event ::= DELETE|INSERT */ +- 281, /* (252) trigger_event ::= UPDATE */ +- 281, /* (253) trigger_event ::= UPDATE OF idlist */ +- 283, /* (254) when_clause ::= */ +- 283, /* (255) when_clause ::= WHEN expr */ +- 279, /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ +- 279, /* (257) trigger_cmd_list ::= trigger_cmd SEMI */ +- 285, /* (258) trnm ::= nm DOT nm */ +- 286, /* (259) tridxby ::= INDEXED BY nm */ +- 286, /* (260) tridxby ::= NOT INDEXED */ +- 284, /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +- 284, /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ +- 284, /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +- 284, /* (264) trigger_cmd ::= scanpt select scanpt */ +- 212, /* (265) expr ::= RAISE LP IGNORE RP */ +- 212, /* (266) expr ::= RAISE LP raisetype COMMA nm RP */ +- 231, /* (267) raisetype ::= ROLLBACK */ +- 231, /* (268) raisetype ::= ABORT */ +- 231, /* (269) raisetype ::= FAIL */ +- 186, /* (270) cmd ::= DROP TRIGGER ifexists fullname */ +- 186, /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ +- 186, /* (272) cmd ::= DETACH database_kw_opt expr */ +- 288, /* (273) key_opt ::= */ +- 288, /* (274) key_opt ::= KEY expr */ +- 186, /* (275) cmd ::= REINDEX */ +- 186, /* (276) cmd ::= REINDEX nm dbnm */ +- 186, /* (277) cmd ::= ANALYZE */ +- 186, /* (278) cmd ::= ANALYZE nm dbnm */ +- 186, /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */ +- 186, /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ +- 289, /* (281) add_column_fullname ::= fullname */ +- 186, /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ +- 186, /* (283) cmd ::= create_vtab */ +- 186, /* (284) cmd ::= create_vtab LP vtabarglist RP */ +- 291, /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ +- 293, /* (286) vtabarg ::= */ +- 294, /* (287) vtabargtoken ::= ANY */ +- 294, /* (288) vtabargtoken ::= lp anylist RP */ +- 295, /* (289) lp ::= LP */ +- 261, /* (290) with ::= WITH wqlist */ +- 261, /* (291) with ::= WITH RECURSIVE wqlist */ +- 236, /* (292) wqlist ::= nm eidlist_opt AS LP select RP */ +- 236, /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ +- 297, /* (294) windowdefn_list ::= windowdefn */ +- 297, /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */ +- 298, /* (296) windowdefn ::= nm AS LP window RP */ +- 299, /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ +- 299, /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ +- 299, /* (299) window ::= ORDER BY sortlist frame_opt */ +- 299, /* (300) window ::= nm ORDER BY sortlist frame_opt */ +- 299, /* (301) window ::= frame_opt */ +- 299, /* (302) window ::= nm frame_opt */ +- 300, /* (303) frame_opt ::= */ +- 300, /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ +- 300, /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ +- 304, /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */ +- 306, /* (307) frame_bound_s ::= frame_bound */ +- 306, /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */ +- 307, /* (309) frame_bound_e ::= frame_bound */ +- 307, /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */ +- 305, /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */ +- 305, /* (312) frame_bound ::= CURRENT ROW */ +- 308, /* (313) frame_exclude_opt ::= */ +- 308, /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */ +- 309, /* (315) frame_exclude ::= NO OTHERS */ +- 309, /* (316) frame_exclude ::= CURRENT ROW */ +- 309, /* (317) frame_exclude ::= GROUP|TIES */ +- 246, /* (318) window_clause ::= WINDOW windowdefn_list */ +- 266, /* (319) filter_over ::= filter_clause over_clause */ +- 266, /* (320) filter_over ::= over_clause */ +- 266, /* (321) filter_over ::= filter_clause */ +- 303, /* (322) over_clause ::= OVER LP window RP */ +- 303, /* (323) over_clause ::= OVER nm */ +- 302, /* (324) filter_clause ::= FILTER LP WHERE expr RP */ +- 181, /* (325) input ::= cmdlist */ +- 182, /* (326) cmdlist ::= cmdlist ecmd */ +- 182, /* (327) cmdlist ::= ecmd */ +- 183, /* (328) ecmd ::= SEMI */ +- 183, /* (329) ecmd ::= cmdx SEMI */ +- 183, /* (330) ecmd ::= explain cmdx SEMI */ +- 188, /* (331) trans_opt ::= */ +- 188, /* (332) trans_opt ::= TRANSACTION */ +- 188, /* (333) trans_opt ::= TRANSACTION nm */ +- 190, /* (334) savepoint_opt ::= SAVEPOINT */ +- 190, /* (335) savepoint_opt ::= */ +- 186, /* (336) cmd ::= create_table create_table_args */ +- 197, /* (337) columnlist ::= columnlist COMMA columnname carglist */ +- 197, /* (338) columnlist ::= columnname carglist */ +- 189, /* (339) nm ::= ID|INDEXED */ +- 189, /* (340) nm ::= STRING */ +- 189, /* (341) nm ::= JOIN_KW */ +- 203, /* (342) typetoken ::= typename */ +- 204, /* (343) typename ::= ID|STRING */ +- 205, /* (344) signed ::= plus_num */ +- 205, /* (345) signed ::= minus_num */ +- 202, /* (346) carglist ::= carglist ccons */ +- 202, /* (347) carglist ::= */ +- 210, /* (348) ccons ::= NULL onconf */ +- 210, /* (349) ccons ::= GENERATED ALWAYS AS generated */ +- 210, /* (350) ccons ::= AS generated */ +- 198, /* (351) conslist_opt ::= COMMA conslist */ +- 223, /* (352) conslist ::= conslist tconscomma tcons */ +- 223, /* (353) conslist ::= tcons */ +- 224, /* (354) tconscomma ::= */ +- 228, /* (355) defer_subclause_opt ::= defer_subclause */ +- 230, /* (356) resolvetype ::= raisetype */ +- 234, /* (357) selectnowith ::= oneselect */ +- 235, /* (358) oneselect ::= values */ +- 249, /* (359) sclp ::= selcollist COMMA */ +- 250, /* (360) as ::= ID|STRING */ +- 212, /* (361) expr ::= term */ +- 267, /* (362) likeop ::= LIKE_KW|MATCH */ +- 257, /* (363) exprlist ::= nexprlist */ +- 277, /* (364) nmnum ::= plus_num */ +- 277, /* (365) nmnum ::= nm */ +- 277, /* (366) nmnum ::= ON */ +- 277, /* (367) nmnum ::= DELETE */ +- 277, /* (368) nmnum ::= DEFAULT */ +- 206, /* (369) plus_num ::= INTEGER|FLOAT */ +- 282, /* (370) foreach_clause ::= */ +- 282, /* (371) foreach_clause ::= FOR EACH ROW */ +- 285, /* (372) trnm ::= nm */ +- 286, /* (373) tridxby ::= */ +- 287, /* (374) database_kw_opt ::= DATABASE */ +- 287, /* (375) database_kw_opt ::= */ +- 290, /* (376) kwcolumn_opt ::= */ +- 290, /* (377) kwcolumn_opt ::= COLUMNKW */ +- 292, /* (378) vtabarglist ::= vtabarg */ +- 292, /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ +- 293, /* (380) vtabarg ::= vtabarg vtabargtoken */ +- 296, /* (381) anylist ::= */ +- 296, /* (382) anylist ::= anylist LP anylist RP */ +- 296, /* (383) anylist ::= anylist ANY */ +- 261, /* (384) with ::= */ ++ 191, /* (0) explain ::= EXPLAIN */ ++ 191, /* (1) explain ::= EXPLAIN QUERY PLAN */ ++ 190, /* (2) cmdx ::= cmd */ ++ 192, /* (3) cmd ::= BEGIN transtype trans_opt */ ++ 193, /* (4) transtype ::= */ ++ 193, /* (5) transtype ::= DEFERRED */ ++ 193, /* (6) transtype ::= IMMEDIATE */ ++ 193, /* (7) transtype ::= EXCLUSIVE */ ++ 192, /* (8) cmd ::= COMMIT|END trans_opt */ ++ 192, /* (9) cmd ::= ROLLBACK trans_opt */ ++ 192, /* (10) cmd ::= SAVEPOINT nm */ ++ 192, /* (11) cmd ::= RELEASE savepoint_opt nm */ ++ 192, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ ++ 197, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ ++ 199, /* (14) createkw ::= CREATE */ ++ 201, /* (15) ifnotexists ::= */ ++ 201, /* (16) ifnotexists ::= IF NOT EXISTS */ ++ 200, /* (17) temp ::= TEMP */ ++ 200, /* (18) temp ::= */ ++ 198, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ ++ 198, /* (20) create_table_args ::= AS select */ ++ 205, /* (21) table_option_set ::= */ ++ 205, /* (22) table_option_set ::= table_option_set COMMA table_option */ ++ 207, /* (23) table_option ::= WITHOUT nm */ ++ 207, /* (24) table_option ::= nm */ ++ 208, /* (25) columnname ::= nm typetoken */ ++ 210, /* (26) typetoken ::= */ ++ 210, /* (27) typetoken ::= typename LP signed RP */ ++ 210, /* (28) typetoken ::= typename LP signed COMMA signed RP */ ++ 211, /* (29) typename ::= typename ID|STRING */ ++ 215, /* (30) scanpt ::= */ ++ 216, /* (31) scantok ::= */ ++ 217, /* (32) ccons ::= CONSTRAINT nm */ ++ 217, /* (33) ccons ::= DEFAULT scantok term */ ++ 217, /* (34) ccons ::= DEFAULT LP expr RP */ ++ 217, /* (35) ccons ::= DEFAULT PLUS scantok term */ ++ 217, /* (36) ccons ::= DEFAULT MINUS scantok term */ ++ 217, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ ++ 217, /* (38) ccons ::= NOT NULL onconf */ ++ 217, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ ++ 217, /* (40) ccons ::= UNIQUE onconf */ ++ 217, /* (41) ccons ::= CHECK LP expr RP */ ++ 217, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ ++ 217, /* (43) ccons ::= defer_subclause */ ++ 217, /* (44) ccons ::= COLLATE ID|STRING */ ++ 226, /* (45) generated ::= LP expr RP */ ++ 226, /* (46) generated ::= LP expr RP ID */ ++ 222, /* (47) autoinc ::= */ ++ 222, /* (48) autoinc ::= AUTOINCR */ ++ 224, /* (49) refargs ::= */ ++ 224, /* (50) refargs ::= refargs refarg */ ++ 227, /* (51) refarg ::= MATCH nm */ ++ 227, /* (52) refarg ::= ON INSERT refact */ ++ 227, /* (53) refarg ::= ON DELETE refact */ ++ 227, /* (54) refarg ::= ON UPDATE refact */ ++ 228, /* (55) refact ::= SET NULL */ ++ 228, /* (56) refact ::= SET DEFAULT */ ++ 228, /* (57) refact ::= CASCADE */ ++ 228, /* (58) refact ::= RESTRICT */ ++ 228, /* (59) refact ::= NO ACTION */ ++ 225, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ ++ 225, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ ++ 229, /* (62) init_deferred_pred_opt ::= */ ++ 229, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ ++ 229, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ ++ 204, /* (65) conslist_opt ::= */ ++ 231, /* (66) tconscomma ::= COMMA */ ++ 232, /* (67) tcons ::= CONSTRAINT nm */ ++ 232, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ ++ 232, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ ++ 232, /* (70) tcons ::= CHECK LP expr RP onconf */ ++ 232, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ ++ 235, /* (72) defer_subclause_opt ::= */ ++ 220, /* (73) onconf ::= */ ++ 220, /* (74) onconf ::= ON CONFLICT resolvetype */ ++ 236, /* (75) orconf ::= */ ++ 236, /* (76) orconf ::= OR resolvetype */ ++ 237, /* (77) resolvetype ::= IGNORE */ ++ 237, /* (78) resolvetype ::= REPLACE */ ++ 192, /* (79) cmd ::= DROP TABLE ifexists fullname */ ++ 239, /* (80) ifexists ::= IF EXISTS */ ++ 239, /* (81) ifexists ::= */ ++ 192, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ ++ 192, /* (83) cmd ::= DROP VIEW ifexists fullname */ ++ 192, /* (84) cmd ::= select */ ++ 206, /* (85) select ::= WITH wqlist selectnowith */ ++ 206, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ ++ 206, /* (87) select ::= selectnowith */ ++ 241, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ ++ 244, /* (89) multiselect_op ::= UNION */ ++ 244, /* (90) multiselect_op ::= UNION ALL */ ++ 244, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ ++ 242, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ ++ 242, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ ++ 254, /* (94) values ::= VALUES LP nexprlist RP */ ++ 242, /* (95) oneselect ::= mvalues */ ++ 256, /* (96) mvalues ::= values COMMA LP nexprlist RP */ ++ 256, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ ++ 245, /* (98) distinct ::= DISTINCT */ ++ 245, /* (99) distinct ::= ALL */ ++ 245, /* (100) distinct ::= */ ++ 257, /* (101) sclp ::= */ ++ 246, /* (102) selcollist ::= sclp scanpt expr scanpt as */ ++ 246, /* (103) selcollist ::= sclp scanpt STAR */ ++ 246, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ ++ 258, /* (105) as ::= AS nm */ ++ 258, /* (106) as ::= */ ++ 247, /* (107) from ::= */ ++ 247, /* (108) from ::= FROM seltablist */ ++ 260, /* (109) stl_prefix ::= seltablist joinop */ ++ 260, /* (110) stl_prefix ::= */ ++ 259, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ ++ 259, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ ++ 259, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ ++ 259, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ ++ 259, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ ++ 202, /* (116) dbnm ::= */ ++ 202, /* (117) dbnm ::= DOT nm */ ++ 240, /* (118) fullname ::= nm */ ++ 240, /* (119) fullname ::= nm DOT nm */ ++ 265, /* (120) xfullname ::= nm */ ++ 265, /* (121) xfullname ::= nm DOT nm */ ++ 265, /* (122) xfullname ::= nm DOT nm AS nm */ ++ 265, /* (123) xfullname ::= nm AS nm */ ++ 261, /* (124) joinop ::= COMMA|JOIN */ ++ 261, /* (125) joinop ::= JOIN_KW JOIN */ ++ 261, /* (126) joinop ::= JOIN_KW nm JOIN */ ++ 261, /* (127) joinop ::= JOIN_KW nm nm JOIN */ ++ 262, /* (128) on_using ::= ON expr */ ++ 262, /* (129) on_using ::= USING LP idlist RP */ ++ 262, /* (130) on_using ::= */ ++ 267, /* (131) indexed_opt ::= */ ++ 263, /* (132) indexed_by ::= INDEXED BY nm */ ++ 263, /* (133) indexed_by ::= NOT INDEXED */ ++ 251, /* (134) orderby_opt ::= */ ++ 251, /* (135) orderby_opt ::= ORDER BY sortlist */ ++ 233, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ ++ 233, /* (137) sortlist ::= expr sortorder nulls */ ++ 221, /* (138) sortorder ::= ASC */ ++ 221, /* (139) sortorder ::= DESC */ ++ 221, /* (140) sortorder ::= */ ++ 268, /* (141) nulls ::= NULLS FIRST */ ++ 268, /* (142) nulls ::= NULLS LAST */ ++ 268, /* (143) nulls ::= */ ++ 249, /* (144) groupby_opt ::= */ ++ 249, /* (145) groupby_opt ::= GROUP BY nexprlist */ ++ 250, /* (146) having_opt ::= */ ++ 250, /* (147) having_opt ::= HAVING expr */ ++ 252, /* (148) limit_opt ::= */ ++ 252, /* (149) limit_opt ::= LIMIT expr */ ++ 252, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ ++ 252, /* (151) limit_opt ::= LIMIT expr COMMA expr */ ++ 192, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ ++ 248, /* (153) where_opt ::= */ ++ 248, /* (154) where_opt ::= WHERE expr */ ++ 270, /* (155) where_opt_ret ::= */ ++ 270, /* (156) where_opt_ret ::= WHERE expr */ ++ 270, /* (157) where_opt_ret ::= RETURNING selcollist */ ++ 270, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ ++ 192, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ ++ 271, /* (160) setlist ::= setlist COMMA nm EQ expr */ ++ 271, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ ++ 271, /* (162) setlist ::= nm EQ expr */ ++ 271, /* (163) setlist ::= LP idlist RP EQ expr */ ++ 192, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ ++ 192, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ ++ 274, /* (166) upsert ::= */ ++ 274, /* (167) upsert ::= RETURNING selcollist */ ++ 274, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ ++ 274, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ ++ 274, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ ++ 274, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ ++ 275, /* (172) returning ::= RETURNING selcollist */ ++ 272, /* (173) insert_cmd ::= INSERT orconf */ ++ 272, /* (174) insert_cmd ::= REPLACE */ ++ 273, /* (175) idlist_opt ::= */ ++ 273, /* (176) idlist_opt ::= LP idlist RP */ ++ 266, /* (177) idlist ::= idlist COMMA nm */ ++ 266, /* (178) idlist ::= nm */ ++ 219, /* (179) expr ::= LP expr RP */ ++ 219, /* (180) expr ::= ID|INDEXED|JOIN_KW */ ++ 219, /* (181) expr ::= nm DOT nm */ ++ 219, /* (182) expr ::= nm DOT nm DOT nm */ ++ 218, /* (183) term ::= NULL|FLOAT|BLOB */ ++ 218, /* (184) term ::= STRING */ ++ 218, /* (185) term ::= INTEGER */ ++ 219, /* (186) expr ::= VARIABLE */ ++ 219, /* (187) expr ::= expr COLLATE ID|STRING */ ++ 219, /* (188) expr ::= CAST LP expr AS typetoken RP */ ++ 219, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ ++ 219, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ ++ 219, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ ++ 219, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ ++ 219, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ ++ 219, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ ++ 218, /* (195) term ::= CTIME_KW */ ++ 219, /* (196) expr ::= LP nexprlist COMMA expr RP */ ++ 219, /* (197) expr ::= expr AND expr */ ++ 219, /* (198) expr ::= expr OR expr */ ++ 219, /* (199) expr ::= expr LT|GT|GE|LE expr */ ++ 219, /* (200) expr ::= expr EQ|NE expr */ ++ 219, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ ++ 219, /* (202) expr ::= expr PLUS|MINUS expr */ ++ 219, /* (203) expr ::= expr STAR|SLASH|REM expr */ ++ 219, /* (204) expr ::= expr CONCAT expr */ ++ 277, /* (205) likeop ::= NOT LIKE_KW|MATCH */ ++ 219, /* (206) expr ::= expr likeop expr */ ++ 219, /* (207) expr ::= expr likeop expr ESCAPE expr */ ++ 219, /* (208) expr ::= expr ISNULL|NOTNULL */ ++ 219, /* (209) expr ::= expr NOT NULL */ ++ 219, /* (210) expr ::= expr IS expr */ ++ 219, /* (211) expr ::= expr IS NOT expr */ ++ 219, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ ++ 219, /* (213) expr ::= expr IS DISTINCT FROM expr */ ++ 219, /* (214) expr ::= NOT expr */ ++ 219, /* (215) expr ::= BITNOT expr */ ++ 219, /* (216) expr ::= PLUS|MINUS expr */ ++ 219, /* (217) expr ::= expr PTR expr */ ++ 278, /* (218) between_op ::= BETWEEN */ ++ 278, /* (219) between_op ::= NOT BETWEEN */ ++ 219, /* (220) expr ::= expr between_op expr AND expr */ ++ 279, /* (221) in_op ::= IN */ ++ 279, /* (222) in_op ::= NOT IN */ ++ 219, /* (223) expr ::= expr in_op LP exprlist RP */ ++ 219, /* (224) expr ::= LP select RP */ ++ 219, /* (225) expr ::= expr in_op LP select RP */ ++ 219, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ ++ 219, /* (227) expr ::= EXISTS LP select RP */ ++ 219, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ ++ 282, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ ++ 282, /* (230) case_exprlist ::= WHEN expr THEN expr */ ++ 283, /* (231) case_else ::= ELSE expr */ ++ 283, /* (232) case_else ::= */ ++ 281, /* (233) case_operand ::= */ ++ 264, /* (234) exprlist ::= */ ++ 255, /* (235) nexprlist ::= nexprlist COMMA expr */ ++ 255, /* (236) nexprlist ::= expr */ ++ 280, /* (237) paren_exprlist ::= */ ++ 280, /* (238) paren_exprlist ::= LP exprlist RP */ ++ 192, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ ++ 284, /* (240) uniqueflag ::= UNIQUE */ ++ 284, /* (241) uniqueflag ::= */ ++ 223, /* (242) eidlist_opt ::= */ ++ 223, /* (243) eidlist_opt ::= LP eidlist RP */ ++ 234, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ ++ 234, /* (245) eidlist ::= nm collate sortorder */ ++ 285, /* (246) collate ::= */ ++ 285, /* (247) collate ::= COLLATE ID|STRING */ ++ 192, /* (248) cmd ::= DROP INDEX ifexists fullname */ ++ 192, /* (249) cmd ::= VACUUM vinto */ ++ 192, /* (250) cmd ::= VACUUM nm vinto */ ++ 286, /* (251) vinto ::= INTO expr */ ++ 286, /* (252) vinto ::= */ ++ 192, /* (253) cmd ::= PRAGMA nm dbnm */ ++ 192, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ ++ 192, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ ++ 192, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ ++ 192, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ ++ 213, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ ++ 214, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ ++ 192, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ ++ 288, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ ++ 290, /* (262) trigger_time ::= BEFORE|AFTER */ ++ 290, /* (263) trigger_time ::= INSTEAD OF */ ++ 290, /* (264) trigger_time ::= */ ++ 291, /* (265) trigger_event ::= DELETE|INSERT */ ++ 291, /* (266) trigger_event ::= UPDATE */ ++ 291, /* (267) trigger_event ::= UPDATE OF idlist */ ++ 293, /* (268) when_clause ::= */ ++ 293, /* (269) when_clause ::= WHEN expr */ ++ 289, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ ++ 289, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ ++ 295, /* (272) trnm ::= nm DOT nm */ ++ 296, /* (273) tridxby ::= INDEXED BY nm */ ++ 296, /* (274) tridxby ::= NOT INDEXED */ ++ 294, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ ++ 294, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ ++ 294, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ ++ 294, /* (278) trigger_cmd ::= scanpt select scanpt */ ++ 219, /* (279) expr ::= RAISE LP IGNORE RP */ ++ 219, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ ++ 238, /* (281) raisetype ::= ROLLBACK */ ++ 238, /* (282) raisetype ::= ABORT */ ++ 238, /* (283) raisetype ::= FAIL */ ++ 192, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ ++ 192, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ ++ 192, /* (286) cmd ::= DETACH database_kw_opt expr */ ++ 298, /* (287) key_opt ::= */ ++ 298, /* (288) key_opt ::= KEY expr */ ++ 192, /* (289) cmd ::= REINDEX */ ++ 192, /* (290) cmd ::= REINDEX nm dbnm */ ++ 192, /* (291) cmd ::= ANALYZE */ ++ 192, /* (292) cmd ::= ANALYZE nm dbnm */ ++ 192, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ ++ 192, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ ++ 192, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ ++ 299, /* (296) add_column_fullname ::= fullname */ ++ 192, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ ++ 192, /* (298) cmd ::= create_vtab */ ++ 192, /* (299) cmd ::= create_vtab LP vtabarglist RP */ ++ 301, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ ++ 303, /* (301) vtabarg ::= */ ++ 304, /* (302) vtabargtoken ::= ANY */ ++ 304, /* (303) vtabargtoken ::= lp anylist RP */ ++ 305, /* (304) lp ::= LP */ ++ 269, /* (305) with ::= WITH wqlist */ ++ 269, /* (306) with ::= WITH RECURSIVE wqlist */ ++ 308, /* (307) wqas ::= AS */ ++ 308, /* (308) wqas ::= AS MATERIALIZED */ ++ 308, /* (309) wqas ::= AS NOT MATERIALIZED */ ++ 307, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ ++ 309, /* (311) withnm ::= nm */ ++ 243, /* (312) wqlist ::= wqitem */ ++ 243, /* (313) wqlist ::= wqlist COMMA wqitem */ ++ 310, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ ++ 311, /* (315) windowdefn ::= nm AS LP window RP */ ++ 312, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ ++ 312, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ ++ 312, /* (318) window ::= ORDER BY sortlist frame_opt */ ++ 312, /* (319) window ::= nm ORDER BY sortlist frame_opt */ ++ 312, /* (320) window ::= nm frame_opt */ ++ 313, /* (321) frame_opt ::= */ ++ 313, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ ++ 313, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ ++ 317, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ ++ 319, /* (325) frame_bound_s ::= frame_bound */ ++ 319, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ ++ 320, /* (327) frame_bound_e ::= frame_bound */ ++ 320, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ ++ 318, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ ++ 318, /* (330) frame_bound ::= CURRENT ROW */ ++ 321, /* (331) frame_exclude_opt ::= */ ++ 321, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ ++ 322, /* (333) frame_exclude ::= NO OTHERS */ ++ 322, /* (334) frame_exclude ::= CURRENT ROW */ ++ 322, /* (335) frame_exclude ::= GROUP|TIES */ ++ 253, /* (336) window_clause ::= WINDOW windowdefn_list */ ++ 276, /* (337) filter_over ::= filter_clause over_clause */ ++ 276, /* (338) filter_over ::= over_clause */ ++ 276, /* (339) filter_over ::= filter_clause */ ++ 316, /* (340) over_clause ::= OVER LP window RP */ ++ 316, /* (341) over_clause ::= OVER nm */ ++ 315, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ ++ 218, /* (343) term ::= QNUMBER */ ++ 187, /* (344) input ::= cmdlist */ ++ 188, /* (345) cmdlist ::= cmdlist ecmd */ ++ 188, /* (346) cmdlist ::= ecmd */ ++ 189, /* (347) ecmd ::= SEMI */ ++ 189, /* (348) ecmd ::= cmdx SEMI */ ++ 189, /* (349) ecmd ::= explain cmdx SEMI */ ++ 194, /* (350) trans_opt ::= */ ++ 194, /* (351) trans_opt ::= TRANSACTION */ ++ 194, /* (352) trans_opt ::= TRANSACTION nm */ ++ 196, /* (353) savepoint_opt ::= SAVEPOINT */ ++ 196, /* (354) savepoint_opt ::= */ ++ 192, /* (355) cmd ::= create_table create_table_args */ ++ 205, /* (356) table_option_set ::= table_option */ ++ 203, /* (357) columnlist ::= columnlist COMMA columnname carglist */ ++ 203, /* (358) columnlist ::= columnname carglist */ ++ 195, /* (359) nm ::= ID|INDEXED|JOIN_KW */ ++ 195, /* (360) nm ::= STRING */ ++ 210, /* (361) typetoken ::= typename */ ++ 211, /* (362) typename ::= ID|STRING */ ++ 212, /* (363) signed ::= plus_num */ ++ 212, /* (364) signed ::= minus_num */ ++ 209, /* (365) carglist ::= carglist ccons */ ++ 209, /* (366) carglist ::= */ ++ 217, /* (367) ccons ::= NULL onconf */ ++ 217, /* (368) ccons ::= GENERATED ALWAYS AS generated */ ++ 217, /* (369) ccons ::= AS generated */ ++ 204, /* (370) conslist_opt ::= COMMA conslist */ ++ 230, /* (371) conslist ::= conslist tconscomma tcons */ ++ 230, /* (372) conslist ::= tcons */ ++ 231, /* (373) tconscomma ::= */ ++ 235, /* (374) defer_subclause_opt ::= defer_subclause */ ++ 237, /* (375) resolvetype ::= raisetype */ ++ 241, /* (376) selectnowith ::= oneselect */ ++ 242, /* (377) oneselect ::= values */ ++ 257, /* (378) sclp ::= selcollist COMMA */ ++ 258, /* (379) as ::= ID|STRING */ ++ 267, /* (380) indexed_opt ::= indexed_by */ ++ 275, /* (381) returning ::= */ ++ 219, /* (382) expr ::= term */ ++ 277, /* (383) likeop ::= LIKE_KW|MATCH */ ++ 281, /* (384) case_operand ::= expr */ ++ 264, /* (385) exprlist ::= nexprlist */ ++ 287, /* (386) nmnum ::= plus_num */ ++ 287, /* (387) nmnum ::= nm */ ++ 287, /* (388) nmnum ::= ON */ ++ 287, /* (389) nmnum ::= DELETE */ ++ 287, /* (390) nmnum ::= DEFAULT */ ++ 213, /* (391) plus_num ::= INTEGER|FLOAT */ ++ 292, /* (392) foreach_clause ::= */ ++ 292, /* (393) foreach_clause ::= FOR EACH ROW */ ++ 295, /* (394) trnm ::= nm */ ++ 296, /* (395) tridxby ::= */ ++ 297, /* (396) database_kw_opt ::= DATABASE */ ++ 297, /* (397) database_kw_opt ::= */ ++ 300, /* (398) kwcolumn_opt ::= */ ++ 300, /* (399) kwcolumn_opt ::= COLUMNKW */ ++ 302, /* (400) vtabarglist ::= vtabarg */ ++ 302, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ ++ 303, /* (402) vtabarg ::= vtabarg vtabargtoken */ ++ 306, /* (403) anylist ::= */ ++ 306, /* (404) anylist ::= anylist LP anylist RP */ ++ 306, /* (405) anylist ::= anylist ANY */ ++ 269, /* (406) with ::= */ ++ 310, /* (407) windowdefn_list ::= windowdefn */ ++ 312, /* (408) window ::= frame_opt */ + }; + + /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number +@@ -157080,372 +178520,396 @@ static const signed char yyRuleInfoNRhs[] = { + -3, /* (16) ifnotexists ::= IF NOT EXISTS */ + -1, /* (17) temp ::= TEMP */ + 0, /* (18) temp ::= */ +- -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ ++ -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + -2, /* (20) create_table_args ::= AS select */ +- 0, /* (21) table_options ::= */ +- -2, /* (22) table_options ::= WITHOUT nm */ +- -2, /* (23) columnname ::= nm typetoken */ +- 0, /* (24) typetoken ::= */ +- -4, /* (25) typetoken ::= typename LP signed RP */ +- -6, /* (26) typetoken ::= typename LP signed COMMA signed RP */ +- -2, /* (27) typename ::= typename ID|STRING */ +- 0, /* (28) scanpt ::= */ +- 0, /* (29) scantok ::= */ +- -2, /* (30) ccons ::= CONSTRAINT nm */ +- -3, /* (31) ccons ::= DEFAULT scantok term */ +- -4, /* (32) ccons ::= DEFAULT LP expr RP */ +- -4, /* (33) ccons ::= DEFAULT PLUS scantok term */ +- -4, /* (34) ccons ::= DEFAULT MINUS scantok term */ +- -3, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ +- -3, /* (36) ccons ::= NOT NULL onconf */ +- -5, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ +- -2, /* (38) ccons ::= UNIQUE onconf */ +- -4, /* (39) ccons ::= CHECK LP expr RP */ +- -4, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ +- -1, /* (41) ccons ::= defer_subclause */ +- -2, /* (42) ccons ::= COLLATE ID|STRING */ +- -3, /* (43) generated ::= LP expr RP */ +- -4, /* (44) generated ::= LP expr RP ID */ +- 0, /* (45) autoinc ::= */ +- -1, /* (46) autoinc ::= AUTOINCR */ +- 0, /* (47) refargs ::= */ +- -2, /* (48) refargs ::= refargs refarg */ +- -2, /* (49) refarg ::= MATCH nm */ +- -3, /* (50) refarg ::= ON INSERT refact */ +- -3, /* (51) refarg ::= ON DELETE refact */ +- -3, /* (52) refarg ::= ON UPDATE refact */ +- -2, /* (53) refact ::= SET NULL */ +- -2, /* (54) refact ::= SET DEFAULT */ +- -1, /* (55) refact ::= CASCADE */ +- -1, /* (56) refact ::= RESTRICT */ +- -2, /* (57) refact ::= NO ACTION */ +- -3, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ +- -2, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ +- 0, /* (60) init_deferred_pred_opt ::= */ +- -2, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ +- -2, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ +- 0, /* (63) conslist_opt ::= */ +- -1, /* (64) tconscomma ::= COMMA */ +- -2, /* (65) tcons ::= CONSTRAINT nm */ +- -7, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ +- -5, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ +- -5, /* (68) tcons ::= CHECK LP expr RP onconf */ +- -10, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ +- 0, /* (70) defer_subclause_opt ::= */ +- 0, /* (71) onconf ::= */ +- -3, /* (72) onconf ::= ON CONFLICT resolvetype */ +- 0, /* (73) orconf ::= */ +- -2, /* (74) orconf ::= OR resolvetype */ +- -1, /* (75) resolvetype ::= IGNORE */ +- -1, /* (76) resolvetype ::= REPLACE */ +- -4, /* (77) cmd ::= DROP TABLE ifexists fullname */ +- -2, /* (78) ifexists ::= IF EXISTS */ +- 0, /* (79) ifexists ::= */ +- -9, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ +- -4, /* (81) cmd ::= DROP VIEW ifexists fullname */ +- -1, /* (82) cmd ::= select */ +- -3, /* (83) select ::= WITH wqlist selectnowith */ +- -4, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ +- -1, /* (85) select ::= selectnowith */ +- -3, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ +- -1, /* (87) multiselect_op ::= UNION */ +- -2, /* (88) multiselect_op ::= UNION ALL */ +- -1, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ +- -9, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ +- -10, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ +- -4, /* (92) values ::= VALUES LP nexprlist RP */ +- -5, /* (93) values ::= values COMMA LP nexprlist RP */ +- -1, /* (94) distinct ::= DISTINCT */ +- -1, /* (95) distinct ::= ALL */ +- 0, /* (96) distinct ::= */ +- 0, /* (97) sclp ::= */ +- -5, /* (98) selcollist ::= sclp scanpt expr scanpt as */ +- -3, /* (99) selcollist ::= sclp scanpt STAR */ +- -5, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ +- -2, /* (101) as ::= AS nm */ +- 0, /* (102) as ::= */ +- 0, /* (103) from ::= */ +- -2, /* (104) from ::= FROM seltablist */ +- -2, /* (105) stl_prefix ::= seltablist joinop */ +- 0, /* (106) stl_prefix ::= */ +- -7, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ +- -9, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ +- -7, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ +- -7, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ +- 0, /* (111) dbnm ::= */ +- -2, /* (112) dbnm ::= DOT nm */ +- -1, /* (113) fullname ::= nm */ +- -3, /* (114) fullname ::= nm DOT nm */ +- -1, /* (115) xfullname ::= nm */ +- -3, /* (116) xfullname ::= nm DOT nm */ +- -5, /* (117) xfullname ::= nm DOT nm AS nm */ +- -3, /* (118) xfullname ::= nm AS nm */ +- -1, /* (119) joinop ::= COMMA|JOIN */ +- -2, /* (120) joinop ::= JOIN_KW JOIN */ +- -3, /* (121) joinop ::= JOIN_KW nm JOIN */ +- -4, /* (122) joinop ::= JOIN_KW nm nm JOIN */ +- -2, /* (123) on_opt ::= ON expr */ +- 0, /* (124) on_opt ::= */ +- 0, /* (125) indexed_opt ::= */ +- -3, /* (126) indexed_opt ::= INDEXED BY nm */ +- -2, /* (127) indexed_opt ::= NOT INDEXED */ +- -4, /* (128) using_opt ::= USING LP idlist RP */ +- 0, /* (129) using_opt ::= */ +- 0, /* (130) orderby_opt ::= */ +- -3, /* (131) orderby_opt ::= ORDER BY sortlist */ +- -5, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ +- -3, /* (133) sortlist ::= expr sortorder nulls */ +- -1, /* (134) sortorder ::= ASC */ +- -1, /* (135) sortorder ::= DESC */ +- 0, /* (136) sortorder ::= */ +- -2, /* (137) nulls ::= NULLS FIRST */ +- -2, /* (138) nulls ::= NULLS LAST */ +- 0, /* (139) nulls ::= */ +- 0, /* (140) groupby_opt ::= */ +- -3, /* (141) groupby_opt ::= GROUP BY nexprlist */ +- 0, /* (142) having_opt ::= */ +- -2, /* (143) having_opt ::= HAVING expr */ +- 0, /* (144) limit_opt ::= */ +- -2, /* (145) limit_opt ::= LIMIT expr */ +- -4, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ +- -4, /* (147) limit_opt ::= LIMIT expr COMMA expr */ +- -6, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ +- 0, /* (149) where_opt ::= */ +- -2, /* (150) where_opt ::= WHERE expr */ +- -9, /* (151) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt */ +- -5, /* (152) setlist ::= setlist COMMA nm EQ expr */ +- -7, /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */ +- -3, /* (154) setlist ::= nm EQ expr */ +- -5, /* (155) setlist ::= LP idlist RP EQ expr */ +- -7, /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ +- -7, /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ +- 0, /* (158) upsert ::= */ +- -11, /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ +- -8, /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ +- -4, /* (161) upsert ::= ON CONFLICT DO NOTHING */ +- -2, /* (162) insert_cmd ::= INSERT orconf */ +- -1, /* (163) insert_cmd ::= REPLACE */ +- 0, /* (164) idlist_opt ::= */ +- -3, /* (165) idlist_opt ::= LP idlist RP */ +- -3, /* (166) idlist ::= idlist COMMA nm */ +- -1, /* (167) idlist ::= nm */ +- -3, /* (168) expr ::= LP expr RP */ +- -1, /* (169) expr ::= ID|INDEXED */ +- -1, /* (170) expr ::= JOIN_KW */ +- -3, /* (171) expr ::= nm DOT nm */ +- -5, /* (172) expr ::= nm DOT nm DOT nm */ +- -1, /* (173) term ::= NULL|FLOAT|BLOB */ +- -1, /* (174) term ::= STRING */ +- -1, /* (175) term ::= INTEGER */ +- -1, /* (176) expr ::= VARIABLE */ +- -3, /* (177) expr ::= expr COLLATE ID|STRING */ +- -6, /* (178) expr ::= CAST LP expr AS typetoken RP */ +- -5, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */ +- -4, /* (180) expr ::= ID|INDEXED LP STAR RP */ +- -6, /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ +- -5, /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */ +- -1, /* (183) term ::= CTIME_KW */ +- -5, /* (184) expr ::= LP nexprlist COMMA expr RP */ +- -3, /* (185) expr ::= expr AND expr */ +- -3, /* (186) expr ::= expr OR expr */ +- -3, /* (187) expr ::= expr LT|GT|GE|LE expr */ +- -3, /* (188) expr ::= expr EQ|NE expr */ +- -3, /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ +- -3, /* (190) expr ::= expr PLUS|MINUS expr */ +- -3, /* (191) expr ::= expr STAR|SLASH|REM expr */ +- -3, /* (192) expr ::= expr CONCAT expr */ +- -2, /* (193) likeop ::= NOT LIKE_KW|MATCH */ +- -3, /* (194) expr ::= expr likeop expr */ +- -5, /* (195) expr ::= expr likeop expr ESCAPE expr */ +- -2, /* (196) expr ::= expr ISNULL|NOTNULL */ +- -3, /* (197) expr ::= expr NOT NULL */ +- -3, /* (198) expr ::= expr IS expr */ +- -4, /* (199) expr ::= expr IS NOT expr */ +- -2, /* (200) expr ::= NOT expr */ +- -2, /* (201) expr ::= BITNOT expr */ +- -2, /* (202) expr ::= PLUS|MINUS expr */ +- -1, /* (203) between_op ::= BETWEEN */ +- -2, /* (204) between_op ::= NOT BETWEEN */ +- -5, /* (205) expr ::= expr between_op expr AND expr */ +- -1, /* (206) in_op ::= IN */ +- -2, /* (207) in_op ::= NOT IN */ +- -5, /* (208) expr ::= expr in_op LP exprlist RP */ +- -3, /* (209) expr ::= LP select RP */ +- -5, /* (210) expr ::= expr in_op LP select RP */ +- -5, /* (211) expr ::= expr in_op nm dbnm paren_exprlist */ +- -4, /* (212) expr ::= EXISTS LP select RP */ +- -5, /* (213) expr ::= CASE case_operand case_exprlist case_else END */ +- -5, /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */ +- -4, /* (215) case_exprlist ::= WHEN expr THEN expr */ +- -2, /* (216) case_else ::= ELSE expr */ +- 0, /* (217) case_else ::= */ +- -1, /* (218) case_operand ::= expr */ +- 0, /* (219) case_operand ::= */ +- 0, /* (220) exprlist ::= */ +- -3, /* (221) nexprlist ::= nexprlist COMMA expr */ +- -1, /* (222) nexprlist ::= expr */ +- 0, /* (223) paren_exprlist ::= */ +- -3, /* (224) paren_exprlist ::= LP exprlist RP */ +- -12, /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ +- -1, /* (226) uniqueflag ::= UNIQUE */ +- 0, /* (227) uniqueflag ::= */ +- 0, /* (228) eidlist_opt ::= */ +- -3, /* (229) eidlist_opt ::= LP eidlist RP */ +- -5, /* (230) eidlist ::= eidlist COMMA nm collate sortorder */ +- -3, /* (231) eidlist ::= nm collate sortorder */ +- 0, /* (232) collate ::= */ +- -2, /* (233) collate ::= COLLATE ID|STRING */ +- -4, /* (234) cmd ::= DROP INDEX ifexists fullname */ +- -2, /* (235) cmd ::= VACUUM vinto */ +- -3, /* (236) cmd ::= VACUUM nm vinto */ +- -2, /* (237) vinto ::= INTO expr */ +- 0, /* (238) vinto ::= */ +- -3, /* (239) cmd ::= PRAGMA nm dbnm */ +- -5, /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */ +- -6, /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */ +- -5, /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */ +- -6, /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */ +- -2, /* (244) plus_num ::= PLUS INTEGER|FLOAT */ +- -2, /* (245) minus_num ::= MINUS INTEGER|FLOAT */ +- -5, /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ +- -11, /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ +- -1, /* (248) trigger_time ::= BEFORE|AFTER */ +- -2, /* (249) trigger_time ::= INSTEAD OF */ +- 0, /* (250) trigger_time ::= */ +- -1, /* (251) trigger_event ::= DELETE|INSERT */ +- -1, /* (252) trigger_event ::= UPDATE */ +- -3, /* (253) trigger_event ::= UPDATE OF idlist */ +- 0, /* (254) when_clause ::= */ +- -2, /* (255) when_clause ::= WHEN expr */ +- -3, /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ +- -2, /* (257) trigger_cmd_list ::= trigger_cmd SEMI */ +- -3, /* (258) trnm ::= nm DOT nm */ +- -3, /* (259) tridxby ::= INDEXED BY nm */ +- -2, /* (260) tridxby ::= NOT INDEXED */ +- -9, /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +- -8, /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ +- -6, /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +- -3, /* (264) trigger_cmd ::= scanpt select scanpt */ +- -4, /* (265) expr ::= RAISE LP IGNORE RP */ +- -6, /* (266) expr ::= RAISE LP raisetype COMMA nm RP */ +- -1, /* (267) raisetype ::= ROLLBACK */ +- -1, /* (268) raisetype ::= ABORT */ +- -1, /* (269) raisetype ::= FAIL */ +- -4, /* (270) cmd ::= DROP TRIGGER ifexists fullname */ +- -6, /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ +- -3, /* (272) cmd ::= DETACH database_kw_opt expr */ +- 0, /* (273) key_opt ::= */ +- -2, /* (274) key_opt ::= KEY expr */ +- -1, /* (275) cmd ::= REINDEX */ +- -3, /* (276) cmd ::= REINDEX nm dbnm */ +- -1, /* (277) cmd ::= ANALYZE */ +- -3, /* (278) cmd ::= ANALYZE nm dbnm */ +- -6, /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */ +- -7, /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ +- -1, /* (281) add_column_fullname ::= fullname */ +- -8, /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ +- -1, /* (283) cmd ::= create_vtab */ +- -4, /* (284) cmd ::= create_vtab LP vtabarglist RP */ +- -8, /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ +- 0, /* (286) vtabarg ::= */ +- -1, /* (287) vtabargtoken ::= ANY */ +- -3, /* (288) vtabargtoken ::= lp anylist RP */ +- -1, /* (289) lp ::= LP */ +- -2, /* (290) with ::= WITH wqlist */ +- -3, /* (291) with ::= WITH RECURSIVE wqlist */ +- -6, /* (292) wqlist ::= nm eidlist_opt AS LP select RP */ +- -8, /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ +- -1, /* (294) windowdefn_list ::= windowdefn */ +- -3, /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */ +- -5, /* (296) windowdefn ::= nm AS LP window RP */ +- -5, /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ +- -6, /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ +- -4, /* (299) window ::= ORDER BY sortlist frame_opt */ +- -5, /* (300) window ::= nm ORDER BY sortlist frame_opt */ +- -1, /* (301) window ::= frame_opt */ +- -2, /* (302) window ::= nm frame_opt */ +- 0, /* (303) frame_opt ::= */ +- -3, /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ +- -6, /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ +- -1, /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */ +- -1, /* (307) frame_bound_s ::= frame_bound */ +- -2, /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */ +- -1, /* (309) frame_bound_e ::= frame_bound */ +- -2, /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */ +- -2, /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */ +- -2, /* (312) frame_bound ::= CURRENT ROW */ +- 0, /* (313) frame_exclude_opt ::= */ +- -2, /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */ +- -2, /* (315) frame_exclude ::= NO OTHERS */ +- -2, /* (316) frame_exclude ::= CURRENT ROW */ +- -1, /* (317) frame_exclude ::= GROUP|TIES */ +- -2, /* (318) window_clause ::= WINDOW windowdefn_list */ +- -2, /* (319) filter_over ::= filter_clause over_clause */ +- -1, /* (320) filter_over ::= over_clause */ +- -1, /* (321) filter_over ::= filter_clause */ +- -4, /* (322) over_clause ::= OVER LP window RP */ +- -2, /* (323) over_clause ::= OVER nm */ +- -5, /* (324) filter_clause ::= FILTER LP WHERE expr RP */ +- -1, /* (325) input ::= cmdlist */ +- -2, /* (326) cmdlist ::= cmdlist ecmd */ +- -1, /* (327) cmdlist ::= ecmd */ +- -1, /* (328) ecmd ::= SEMI */ +- -2, /* (329) ecmd ::= cmdx SEMI */ +- -3, /* (330) ecmd ::= explain cmdx SEMI */ +- 0, /* (331) trans_opt ::= */ +- -1, /* (332) trans_opt ::= TRANSACTION */ +- -2, /* (333) trans_opt ::= TRANSACTION nm */ +- -1, /* (334) savepoint_opt ::= SAVEPOINT */ +- 0, /* (335) savepoint_opt ::= */ +- -2, /* (336) cmd ::= create_table create_table_args */ +- -4, /* (337) columnlist ::= columnlist COMMA columnname carglist */ +- -2, /* (338) columnlist ::= columnname carglist */ +- -1, /* (339) nm ::= ID|INDEXED */ +- -1, /* (340) nm ::= STRING */ +- -1, /* (341) nm ::= JOIN_KW */ +- -1, /* (342) typetoken ::= typename */ +- -1, /* (343) typename ::= ID|STRING */ +- -1, /* (344) signed ::= plus_num */ +- -1, /* (345) signed ::= minus_num */ +- -2, /* (346) carglist ::= carglist ccons */ +- 0, /* (347) carglist ::= */ +- -2, /* (348) ccons ::= NULL onconf */ +- -4, /* (349) ccons ::= GENERATED ALWAYS AS generated */ +- -2, /* (350) ccons ::= AS generated */ +- -2, /* (351) conslist_opt ::= COMMA conslist */ +- -3, /* (352) conslist ::= conslist tconscomma tcons */ +- -1, /* (353) conslist ::= tcons */ +- 0, /* (354) tconscomma ::= */ +- -1, /* (355) defer_subclause_opt ::= defer_subclause */ +- -1, /* (356) resolvetype ::= raisetype */ +- -1, /* (357) selectnowith ::= oneselect */ +- -1, /* (358) oneselect ::= values */ +- -2, /* (359) sclp ::= selcollist COMMA */ +- -1, /* (360) as ::= ID|STRING */ +- -1, /* (361) expr ::= term */ +- -1, /* (362) likeop ::= LIKE_KW|MATCH */ +- -1, /* (363) exprlist ::= nexprlist */ +- -1, /* (364) nmnum ::= plus_num */ +- -1, /* (365) nmnum ::= nm */ +- -1, /* (366) nmnum ::= ON */ +- -1, /* (367) nmnum ::= DELETE */ +- -1, /* (368) nmnum ::= DEFAULT */ +- -1, /* (369) plus_num ::= INTEGER|FLOAT */ +- 0, /* (370) foreach_clause ::= */ +- -3, /* (371) foreach_clause ::= FOR EACH ROW */ +- -1, /* (372) trnm ::= nm */ +- 0, /* (373) tridxby ::= */ +- -1, /* (374) database_kw_opt ::= DATABASE */ +- 0, /* (375) database_kw_opt ::= */ +- 0, /* (376) kwcolumn_opt ::= */ +- -1, /* (377) kwcolumn_opt ::= COLUMNKW */ +- -1, /* (378) vtabarglist ::= vtabarg */ +- -3, /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ +- -2, /* (380) vtabarg ::= vtabarg vtabargtoken */ +- 0, /* (381) anylist ::= */ +- -4, /* (382) anylist ::= anylist LP anylist RP */ +- -2, /* (383) anylist ::= anylist ANY */ +- 0, /* (384) with ::= */ ++ 0, /* (21) table_option_set ::= */ ++ -3, /* (22) table_option_set ::= table_option_set COMMA table_option */ ++ -2, /* (23) table_option ::= WITHOUT nm */ ++ -1, /* (24) table_option ::= nm */ ++ -2, /* (25) columnname ::= nm typetoken */ ++ 0, /* (26) typetoken ::= */ ++ -4, /* (27) typetoken ::= typename LP signed RP */ ++ -6, /* (28) typetoken ::= typename LP signed COMMA signed RP */ ++ -2, /* (29) typename ::= typename ID|STRING */ ++ 0, /* (30) scanpt ::= */ ++ 0, /* (31) scantok ::= */ ++ -2, /* (32) ccons ::= CONSTRAINT nm */ ++ -3, /* (33) ccons ::= DEFAULT scantok term */ ++ -4, /* (34) ccons ::= DEFAULT LP expr RP */ ++ -4, /* (35) ccons ::= DEFAULT PLUS scantok term */ ++ -4, /* (36) ccons ::= DEFAULT MINUS scantok term */ ++ -3, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ ++ -3, /* (38) ccons ::= NOT NULL onconf */ ++ -5, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ ++ -2, /* (40) ccons ::= UNIQUE onconf */ ++ -4, /* (41) ccons ::= CHECK LP expr RP */ ++ -4, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ ++ -1, /* (43) ccons ::= defer_subclause */ ++ -2, /* (44) ccons ::= COLLATE ID|STRING */ ++ -3, /* (45) generated ::= LP expr RP */ ++ -4, /* (46) generated ::= LP expr RP ID */ ++ 0, /* (47) autoinc ::= */ ++ -1, /* (48) autoinc ::= AUTOINCR */ ++ 0, /* (49) refargs ::= */ ++ -2, /* (50) refargs ::= refargs refarg */ ++ -2, /* (51) refarg ::= MATCH nm */ ++ -3, /* (52) refarg ::= ON INSERT refact */ ++ -3, /* (53) refarg ::= ON DELETE refact */ ++ -3, /* (54) refarg ::= ON UPDATE refact */ ++ -2, /* (55) refact ::= SET NULL */ ++ -2, /* (56) refact ::= SET DEFAULT */ ++ -1, /* (57) refact ::= CASCADE */ ++ -1, /* (58) refact ::= RESTRICT */ ++ -2, /* (59) refact ::= NO ACTION */ ++ -3, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ ++ -2, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ ++ 0, /* (62) init_deferred_pred_opt ::= */ ++ -2, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ ++ -2, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ ++ 0, /* (65) conslist_opt ::= */ ++ -1, /* (66) tconscomma ::= COMMA */ ++ -2, /* (67) tcons ::= CONSTRAINT nm */ ++ -7, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ ++ -5, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ ++ -5, /* (70) tcons ::= CHECK LP expr RP onconf */ ++ -10, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ ++ 0, /* (72) defer_subclause_opt ::= */ ++ 0, /* (73) onconf ::= */ ++ -3, /* (74) onconf ::= ON CONFLICT resolvetype */ ++ 0, /* (75) orconf ::= */ ++ -2, /* (76) orconf ::= OR resolvetype */ ++ -1, /* (77) resolvetype ::= IGNORE */ ++ -1, /* (78) resolvetype ::= REPLACE */ ++ -4, /* (79) cmd ::= DROP TABLE ifexists fullname */ ++ -2, /* (80) ifexists ::= IF EXISTS */ ++ 0, /* (81) ifexists ::= */ ++ -9, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ ++ -4, /* (83) cmd ::= DROP VIEW ifexists fullname */ ++ -1, /* (84) cmd ::= select */ ++ -3, /* (85) select ::= WITH wqlist selectnowith */ ++ -4, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ ++ -1, /* (87) select ::= selectnowith */ ++ -3, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ ++ -1, /* (89) multiselect_op ::= UNION */ ++ -2, /* (90) multiselect_op ::= UNION ALL */ ++ -1, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ ++ -9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ ++ -10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ ++ -4, /* (94) values ::= VALUES LP nexprlist RP */ ++ -1, /* (95) oneselect ::= mvalues */ ++ -5, /* (96) mvalues ::= values COMMA LP nexprlist RP */ ++ -5, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ ++ -1, /* (98) distinct ::= DISTINCT */ ++ -1, /* (99) distinct ::= ALL */ ++ 0, /* (100) distinct ::= */ ++ 0, /* (101) sclp ::= */ ++ -5, /* (102) selcollist ::= sclp scanpt expr scanpt as */ ++ -3, /* (103) selcollist ::= sclp scanpt STAR */ ++ -5, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ ++ -2, /* (105) as ::= AS nm */ ++ 0, /* (106) as ::= */ ++ 0, /* (107) from ::= */ ++ -2, /* (108) from ::= FROM seltablist */ ++ -2, /* (109) stl_prefix ::= seltablist joinop */ ++ 0, /* (110) stl_prefix ::= */ ++ -5, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ ++ -6, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ ++ -8, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ ++ -6, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ ++ -6, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ ++ 0, /* (116) dbnm ::= */ ++ -2, /* (117) dbnm ::= DOT nm */ ++ -1, /* (118) fullname ::= nm */ ++ -3, /* (119) fullname ::= nm DOT nm */ ++ -1, /* (120) xfullname ::= nm */ ++ -3, /* (121) xfullname ::= nm DOT nm */ ++ -5, /* (122) xfullname ::= nm DOT nm AS nm */ ++ -3, /* (123) xfullname ::= nm AS nm */ ++ -1, /* (124) joinop ::= COMMA|JOIN */ ++ -2, /* (125) joinop ::= JOIN_KW JOIN */ ++ -3, /* (126) joinop ::= JOIN_KW nm JOIN */ ++ -4, /* (127) joinop ::= JOIN_KW nm nm JOIN */ ++ -2, /* (128) on_using ::= ON expr */ ++ -4, /* (129) on_using ::= USING LP idlist RP */ ++ 0, /* (130) on_using ::= */ ++ 0, /* (131) indexed_opt ::= */ ++ -3, /* (132) indexed_by ::= INDEXED BY nm */ ++ -2, /* (133) indexed_by ::= NOT INDEXED */ ++ 0, /* (134) orderby_opt ::= */ ++ -3, /* (135) orderby_opt ::= ORDER BY sortlist */ ++ -5, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ ++ -3, /* (137) sortlist ::= expr sortorder nulls */ ++ -1, /* (138) sortorder ::= ASC */ ++ -1, /* (139) sortorder ::= DESC */ ++ 0, /* (140) sortorder ::= */ ++ -2, /* (141) nulls ::= NULLS FIRST */ ++ -2, /* (142) nulls ::= NULLS LAST */ ++ 0, /* (143) nulls ::= */ ++ 0, /* (144) groupby_opt ::= */ ++ -3, /* (145) groupby_opt ::= GROUP BY nexprlist */ ++ 0, /* (146) having_opt ::= */ ++ -2, /* (147) having_opt ::= HAVING expr */ ++ 0, /* (148) limit_opt ::= */ ++ -2, /* (149) limit_opt ::= LIMIT expr */ ++ -4, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ ++ -4, /* (151) limit_opt ::= LIMIT expr COMMA expr */ ++ -6, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ ++ 0, /* (153) where_opt ::= */ ++ -2, /* (154) where_opt ::= WHERE expr */ ++ 0, /* (155) where_opt_ret ::= */ ++ -2, /* (156) where_opt_ret ::= WHERE expr */ ++ -2, /* (157) where_opt_ret ::= RETURNING selcollist */ ++ -4, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ ++ -9, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ ++ -5, /* (160) setlist ::= setlist COMMA nm EQ expr */ ++ -7, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ ++ -3, /* (162) setlist ::= nm EQ expr */ ++ -5, /* (163) setlist ::= LP idlist RP EQ expr */ ++ -7, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ ++ -8, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ ++ 0, /* (166) upsert ::= */ ++ -2, /* (167) upsert ::= RETURNING selcollist */ ++ -12, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ ++ -9, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ ++ -5, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ ++ -8, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ ++ -2, /* (172) returning ::= RETURNING selcollist */ ++ -2, /* (173) insert_cmd ::= INSERT orconf */ ++ -1, /* (174) insert_cmd ::= REPLACE */ ++ 0, /* (175) idlist_opt ::= */ ++ -3, /* (176) idlist_opt ::= LP idlist RP */ ++ -3, /* (177) idlist ::= idlist COMMA nm */ ++ -1, /* (178) idlist ::= nm */ ++ -3, /* (179) expr ::= LP expr RP */ ++ -1, /* (180) expr ::= ID|INDEXED|JOIN_KW */ ++ -3, /* (181) expr ::= nm DOT nm */ ++ -5, /* (182) expr ::= nm DOT nm DOT nm */ ++ -1, /* (183) term ::= NULL|FLOAT|BLOB */ ++ -1, /* (184) term ::= STRING */ ++ -1, /* (185) term ::= INTEGER */ ++ -1, /* (186) expr ::= VARIABLE */ ++ -3, /* (187) expr ::= expr COLLATE ID|STRING */ ++ -6, /* (188) expr ::= CAST LP expr AS typetoken RP */ ++ -5, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ ++ -8, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ ++ -4, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ ++ -6, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ ++ -9, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ ++ -5, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ ++ -1, /* (195) term ::= CTIME_KW */ ++ -5, /* (196) expr ::= LP nexprlist COMMA expr RP */ ++ -3, /* (197) expr ::= expr AND expr */ ++ -3, /* (198) expr ::= expr OR expr */ ++ -3, /* (199) expr ::= expr LT|GT|GE|LE expr */ ++ -3, /* (200) expr ::= expr EQ|NE expr */ ++ -3, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ ++ -3, /* (202) expr ::= expr PLUS|MINUS expr */ ++ -3, /* (203) expr ::= expr STAR|SLASH|REM expr */ ++ -3, /* (204) expr ::= expr CONCAT expr */ ++ -2, /* (205) likeop ::= NOT LIKE_KW|MATCH */ ++ -3, /* (206) expr ::= expr likeop expr */ ++ -5, /* (207) expr ::= expr likeop expr ESCAPE expr */ ++ -2, /* (208) expr ::= expr ISNULL|NOTNULL */ ++ -3, /* (209) expr ::= expr NOT NULL */ ++ -3, /* (210) expr ::= expr IS expr */ ++ -4, /* (211) expr ::= expr IS NOT expr */ ++ -6, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ ++ -5, /* (213) expr ::= expr IS DISTINCT FROM expr */ ++ -2, /* (214) expr ::= NOT expr */ ++ -2, /* (215) expr ::= BITNOT expr */ ++ -2, /* (216) expr ::= PLUS|MINUS expr */ ++ -3, /* (217) expr ::= expr PTR expr */ ++ -1, /* (218) between_op ::= BETWEEN */ ++ -2, /* (219) between_op ::= NOT BETWEEN */ ++ -5, /* (220) expr ::= expr between_op expr AND expr */ ++ -1, /* (221) in_op ::= IN */ ++ -2, /* (222) in_op ::= NOT IN */ ++ -5, /* (223) expr ::= expr in_op LP exprlist RP */ ++ -3, /* (224) expr ::= LP select RP */ ++ -5, /* (225) expr ::= expr in_op LP select RP */ ++ -5, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ ++ -4, /* (227) expr ::= EXISTS LP select RP */ ++ -5, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ ++ -5, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ ++ -4, /* (230) case_exprlist ::= WHEN expr THEN expr */ ++ -2, /* (231) case_else ::= ELSE expr */ ++ 0, /* (232) case_else ::= */ ++ 0, /* (233) case_operand ::= */ ++ 0, /* (234) exprlist ::= */ ++ -3, /* (235) nexprlist ::= nexprlist COMMA expr */ ++ -1, /* (236) nexprlist ::= expr */ ++ 0, /* (237) paren_exprlist ::= */ ++ -3, /* (238) paren_exprlist ::= LP exprlist RP */ ++ -12, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ ++ -1, /* (240) uniqueflag ::= UNIQUE */ ++ 0, /* (241) uniqueflag ::= */ ++ 0, /* (242) eidlist_opt ::= */ ++ -3, /* (243) eidlist_opt ::= LP eidlist RP */ ++ -5, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ ++ -3, /* (245) eidlist ::= nm collate sortorder */ ++ 0, /* (246) collate ::= */ ++ -2, /* (247) collate ::= COLLATE ID|STRING */ ++ -4, /* (248) cmd ::= DROP INDEX ifexists fullname */ ++ -2, /* (249) cmd ::= VACUUM vinto */ ++ -3, /* (250) cmd ::= VACUUM nm vinto */ ++ -2, /* (251) vinto ::= INTO expr */ ++ 0, /* (252) vinto ::= */ ++ -3, /* (253) cmd ::= PRAGMA nm dbnm */ ++ -5, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ ++ -6, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ ++ -5, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ ++ -6, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ ++ -2, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ ++ -2, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ ++ -5, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ ++ -11, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ ++ -1, /* (262) trigger_time ::= BEFORE|AFTER */ ++ -2, /* (263) trigger_time ::= INSTEAD OF */ ++ 0, /* (264) trigger_time ::= */ ++ -1, /* (265) trigger_event ::= DELETE|INSERT */ ++ -1, /* (266) trigger_event ::= UPDATE */ ++ -3, /* (267) trigger_event ::= UPDATE OF idlist */ ++ 0, /* (268) when_clause ::= */ ++ -2, /* (269) when_clause ::= WHEN expr */ ++ -3, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ ++ -2, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ ++ -3, /* (272) trnm ::= nm DOT nm */ ++ -3, /* (273) tridxby ::= INDEXED BY nm */ ++ -2, /* (274) tridxby ::= NOT INDEXED */ ++ -9, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ ++ -8, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ ++ -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ ++ -3, /* (278) trigger_cmd ::= scanpt select scanpt */ ++ -4, /* (279) expr ::= RAISE LP IGNORE RP */ ++ -6, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ ++ -1, /* (281) raisetype ::= ROLLBACK */ ++ -1, /* (282) raisetype ::= ABORT */ ++ -1, /* (283) raisetype ::= FAIL */ ++ -4, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ ++ -6, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ ++ -3, /* (286) cmd ::= DETACH database_kw_opt expr */ ++ 0, /* (287) key_opt ::= */ ++ -2, /* (288) key_opt ::= KEY expr */ ++ -1, /* (289) cmd ::= REINDEX */ ++ -3, /* (290) cmd ::= REINDEX nm dbnm */ ++ -1, /* (291) cmd ::= ANALYZE */ ++ -3, /* (292) cmd ::= ANALYZE nm dbnm */ ++ -6, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ ++ -7, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ ++ -6, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ ++ -1, /* (296) add_column_fullname ::= fullname */ ++ -8, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ ++ -1, /* (298) cmd ::= create_vtab */ ++ -4, /* (299) cmd ::= create_vtab LP vtabarglist RP */ ++ -8, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ ++ 0, /* (301) vtabarg ::= */ ++ -1, /* (302) vtabargtoken ::= ANY */ ++ -3, /* (303) vtabargtoken ::= lp anylist RP */ ++ -1, /* (304) lp ::= LP */ ++ -2, /* (305) with ::= WITH wqlist */ ++ -3, /* (306) with ::= WITH RECURSIVE wqlist */ ++ -1, /* (307) wqas ::= AS */ ++ -2, /* (308) wqas ::= AS MATERIALIZED */ ++ -3, /* (309) wqas ::= AS NOT MATERIALIZED */ ++ -6, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ ++ -1, /* (311) withnm ::= nm */ ++ -1, /* (312) wqlist ::= wqitem */ ++ -3, /* (313) wqlist ::= wqlist COMMA wqitem */ ++ -3, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ ++ -5, /* (315) windowdefn ::= nm AS LP window RP */ ++ -5, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ ++ -6, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ ++ -4, /* (318) window ::= ORDER BY sortlist frame_opt */ ++ -5, /* (319) window ::= nm ORDER BY sortlist frame_opt */ ++ -2, /* (320) window ::= nm frame_opt */ ++ 0, /* (321) frame_opt ::= */ ++ -3, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ ++ -6, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ ++ -1, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ ++ -1, /* (325) frame_bound_s ::= frame_bound */ ++ -2, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ ++ -1, /* (327) frame_bound_e ::= frame_bound */ ++ -2, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ ++ -2, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ ++ -2, /* (330) frame_bound ::= CURRENT ROW */ ++ 0, /* (331) frame_exclude_opt ::= */ ++ -2, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ ++ -2, /* (333) frame_exclude ::= NO OTHERS */ ++ -2, /* (334) frame_exclude ::= CURRENT ROW */ ++ -1, /* (335) frame_exclude ::= GROUP|TIES */ ++ -2, /* (336) window_clause ::= WINDOW windowdefn_list */ ++ -2, /* (337) filter_over ::= filter_clause over_clause */ ++ -1, /* (338) filter_over ::= over_clause */ ++ -1, /* (339) filter_over ::= filter_clause */ ++ -4, /* (340) over_clause ::= OVER LP window RP */ ++ -2, /* (341) over_clause ::= OVER nm */ ++ -5, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ ++ -1, /* (343) term ::= QNUMBER */ ++ -1, /* (344) input ::= cmdlist */ ++ -2, /* (345) cmdlist ::= cmdlist ecmd */ ++ -1, /* (346) cmdlist ::= ecmd */ ++ -1, /* (347) ecmd ::= SEMI */ ++ -2, /* (348) ecmd ::= cmdx SEMI */ ++ -3, /* (349) ecmd ::= explain cmdx SEMI */ ++ 0, /* (350) trans_opt ::= */ ++ -1, /* (351) trans_opt ::= TRANSACTION */ ++ -2, /* (352) trans_opt ::= TRANSACTION nm */ ++ -1, /* (353) savepoint_opt ::= SAVEPOINT */ ++ 0, /* (354) savepoint_opt ::= */ ++ -2, /* (355) cmd ::= create_table create_table_args */ ++ -1, /* (356) table_option_set ::= table_option */ ++ -4, /* (357) columnlist ::= columnlist COMMA columnname carglist */ ++ -2, /* (358) columnlist ::= columnname carglist */ ++ -1, /* (359) nm ::= ID|INDEXED|JOIN_KW */ ++ -1, /* (360) nm ::= STRING */ ++ -1, /* (361) typetoken ::= typename */ ++ -1, /* (362) typename ::= ID|STRING */ ++ -1, /* (363) signed ::= plus_num */ ++ -1, /* (364) signed ::= minus_num */ ++ -2, /* (365) carglist ::= carglist ccons */ ++ 0, /* (366) carglist ::= */ ++ -2, /* (367) ccons ::= NULL onconf */ ++ -4, /* (368) ccons ::= GENERATED ALWAYS AS generated */ ++ -2, /* (369) ccons ::= AS generated */ ++ -2, /* (370) conslist_opt ::= COMMA conslist */ ++ -3, /* (371) conslist ::= conslist tconscomma tcons */ ++ -1, /* (372) conslist ::= tcons */ ++ 0, /* (373) tconscomma ::= */ ++ -1, /* (374) defer_subclause_opt ::= defer_subclause */ ++ -1, /* (375) resolvetype ::= raisetype */ ++ -1, /* (376) selectnowith ::= oneselect */ ++ -1, /* (377) oneselect ::= values */ ++ -2, /* (378) sclp ::= selcollist COMMA */ ++ -1, /* (379) as ::= ID|STRING */ ++ -1, /* (380) indexed_opt ::= indexed_by */ ++ 0, /* (381) returning ::= */ ++ -1, /* (382) expr ::= term */ ++ -1, /* (383) likeop ::= LIKE_KW|MATCH */ ++ -1, /* (384) case_operand ::= expr */ ++ -1, /* (385) exprlist ::= nexprlist */ ++ -1, /* (386) nmnum ::= plus_num */ ++ -1, /* (387) nmnum ::= nm */ ++ -1, /* (388) nmnum ::= ON */ ++ -1, /* (389) nmnum ::= DELETE */ ++ -1, /* (390) nmnum ::= DEFAULT */ ++ -1, /* (391) plus_num ::= INTEGER|FLOAT */ ++ 0, /* (392) foreach_clause ::= */ ++ -3, /* (393) foreach_clause ::= FOR EACH ROW */ ++ -1, /* (394) trnm ::= nm */ ++ 0, /* (395) tridxby ::= */ ++ -1, /* (396) database_kw_opt ::= DATABASE */ ++ 0, /* (397) database_kw_opt ::= */ ++ 0, /* (398) kwcolumn_opt ::= */ ++ -1, /* (399) kwcolumn_opt ::= COLUMNKW */ ++ -1, /* (400) vtabarglist ::= vtabarg */ ++ -3, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ ++ -2, /* (402) vtabarg ::= vtabarg vtabargtoken */ ++ 0, /* (403) anylist ::= */ ++ -4, /* (404) anylist ::= anylist LP anylist RP */ ++ -2, /* (405) anylist ::= anylist ANY */ ++ 0, /* (406) with ::= */ ++ -1, /* (407) windowdefn_list ::= windowdefn */ ++ -1, /* (408) window ::= frame_opt */ + }; + + static void yy_accept(yyParser*); /* Forward Declaration */ +@@ -157475,54 +178939,6 @@ static YYACTIONTYPE yy_reduce( + (void)yyLookahead; + (void)yyLookaheadToken; + yymsp = yypParser->yytos; +-#ifndef NDEBUG +- if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ +- yysize = yyRuleInfoNRhs[yyruleno]; +- if( yysize ){ +- fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", +- yyTracePrompt, +- yyruleno, yyRuleName[yyruleno], +- yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ +- yypParser->yyhwm++; +- assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); +- } +-#endif +-#if YYSTACKDEPTH>0 +- if( yypParser->yytos>=yypParser->yystackEnd ){ +- yyStackOverflow(yypParser); +- /* The call to yyStackOverflow() above pops the stack until it is +- ** empty, causing the main parser loop to exit. So the return value +- ** is never used and does not matter. */ +- return 0; +- } +-#else +- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ +- if( yyGrowStack(yypParser) ){ +- yyStackOverflow(yypParser); +- /* The call to yyStackOverflow() above pops the stack until it is +- ** empty, causing the main parser loop to exit. So the return value +- ** is never used and does not matter. */ +- return 0; +- } +- yymsp = yypParser->yytos; +- } +-#endif +- } + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example +@@ -157536,25 +178952,25 @@ static YYACTIONTYPE yy_reduce( + /********** Begin reduce actions **********************************************/ + YYMINORTYPE yylhsminor; + case 0: /* explain ::= EXPLAIN */ +-{ pParse->explain = 1; } ++{ if( pParse->pReprepare==0 ) pParse->explain = 1; } + break; + case 1: /* explain ::= EXPLAIN QUERY PLAN */ +-{ pParse->explain = 2; } ++{ if( pParse->pReprepare==0 ) pParse->explain = 2; } + break; + case 2: /* cmdx ::= cmd */ + { sqlite3FinishCoding(pParse); } + break; + case 3: /* cmd ::= BEGIN transtype trans_opt */ +-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy192);} ++{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy502);} + break; + case 4: /* transtype ::= */ +-{yymsp[1].minor.yy192 = TK_DEFERRED;} ++{yymsp[1].minor.yy502 = TK_DEFERRED;} + break; + case 5: /* transtype ::= DEFERRED */ + case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); + case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); +- case 306: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==306); +-{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/} ++ case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324); ++{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/} + break; + case 8: /* cmd ::= COMMIT|END trans_opt */ + case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); +@@ -157577,104 +178993,122 @@ static YYACTIONTYPE yy_reduce( + break; + case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + { +- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy192,0,0,yymsp[-2].minor.yy192); ++ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy502,0,0,yymsp[-2].minor.yy502); + } + break; + case 14: /* createkw ::= CREATE */ +-{disableLookaside(pParse);} ++{ ++ disableLookaside(pParse); ++} + break; + case 15: /* ifnotexists ::= */ + case 18: /* temp ::= */ yytestcase(yyruleno==18); +- case 21: /* table_options ::= */ yytestcase(yyruleno==21); +- case 45: /* autoinc ::= */ yytestcase(yyruleno==45); +- case 60: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==60); +- case 70: /* defer_subclause_opt ::= */ yytestcase(yyruleno==70); +- case 79: /* ifexists ::= */ yytestcase(yyruleno==79); +- case 96: /* distinct ::= */ yytestcase(yyruleno==96); +- case 232: /* collate ::= */ yytestcase(yyruleno==232); +-{yymsp[1].minor.yy192 = 0;} ++ case 47: /* autoinc ::= */ yytestcase(yyruleno==47); ++ case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); ++ case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); ++ case 81: /* ifexists ::= */ yytestcase(yyruleno==81); ++ case 100: /* distinct ::= */ yytestcase(yyruleno==100); ++ case 246: /* collate ::= */ yytestcase(yyruleno==246); ++{yymsp[1].minor.yy502 = 0;} + break; + case 16: /* ifnotexists ::= IF NOT EXISTS */ +-{yymsp[-2].minor.yy192 = 1;} ++{yymsp[-2].minor.yy502 = 1;} + break; + case 17: /* temp ::= TEMP */ +- case 46: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==46); +-{yymsp[0].minor.yy192 = 1;} ++{yymsp[0].minor.yy502 = pParse->db->init.busy==0;} + break; +- case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ ++ case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + { +- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy192,0); ++ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy9,0); + } + break; + case 20: /* create_table_args ::= AS select */ + { +- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy539); +- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy539); ++ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy637); ++ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); + } + break; +- case 22: /* table_options ::= WITHOUT nm */ ++ case 21: /* table_option_set ::= */ ++{yymsp[1].minor.yy9 = 0;} ++ break; ++ case 22: /* table_option_set ::= table_option_set COMMA table_option */ ++{yylhsminor.yy9 = yymsp[-2].minor.yy9|yymsp[0].minor.yy9;} ++ yymsp[-2].minor.yy9 = yylhsminor.yy9; ++ break; ++ case 23: /* table_option ::= WITHOUT nm */ + { + if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ +- yymsp[-1].minor.yy192 = TF_WithoutRowid | TF_NoVisibleRowid; ++ yymsp[-1].minor.yy9 = TF_WithoutRowid | TF_NoVisibleRowid; + }else{ +- yymsp[-1].minor.yy192 = 0; ++ yymsp[-1].minor.yy9 = 0; + sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); + } + } + break; +- case 23: /* columnname ::= nm typetoken */ +-{sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} ++ case 24: /* table_option ::= nm */ ++{ ++ if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ ++ yylhsminor.yy9 = TF_Strict; ++ }else{ ++ yylhsminor.yy9 = 0; ++ sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); ++ } ++} ++ yymsp[0].minor.yy9 = yylhsminor.yy9; ++ break; ++ case 25: /* columnname ::= nm typetoken */ ++{sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} + break; +- case 24: /* typetoken ::= */ +- case 63: /* conslist_opt ::= */ yytestcase(yyruleno==63); +- case 102: /* as ::= */ yytestcase(yyruleno==102); ++ case 26: /* typetoken ::= */ ++ case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65); ++ case 106: /* as ::= */ yytestcase(yyruleno==106); + {yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;} + break; +- case 25: /* typetoken ::= typename LP signed RP */ ++ case 27: /* typetoken ::= typename LP signed RP */ + { + yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z); + } + break; +- case 26: /* typetoken ::= typename LP signed COMMA signed RP */ ++ case 28: /* typetoken ::= typename LP signed COMMA signed RP */ + { + yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z); + } + break; +- case 27: /* typename ::= typename ID|STRING */ ++ case 29: /* typename ::= typename ID|STRING */ + {yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);} + break; +- case 28: /* scanpt ::= */ ++ case 30: /* scanpt ::= */ + { + assert( yyLookahead!=YYNOCODE ); +- yymsp[1].minor.yy436 = yyLookaheadToken.z; ++ yymsp[1].minor.yy342 = yyLookaheadToken.z; + } + break; +- case 29: /* scantok ::= */ ++ case 31: /* scantok ::= */ + { + assert( yyLookahead!=YYNOCODE ); + yymsp[1].minor.yy0 = yyLookaheadToken; + } + break; +- case 30: /* ccons ::= CONSTRAINT nm */ +- case 65: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==65); +-{pParse->constraintName = yymsp[0].minor.yy0;} ++ case 32: /* ccons ::= CONSTRAINT nm */ ++ case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67); ++{ASSERT_IS_CREATE; pParse->u1.cr.constraintName = yymsp[0].minor.yy0;} + break; +- case 31: /* ccons ::= DEFAULT scantok term */ +-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy202,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} ++ case 33: /* ccons ::= DEFAULT scantok term */ ++{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} + break; +- case 32: /* ccons ::= DEFAULT LP expr RP */ +-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy202,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} ++ case 34: /* ccons ::= DEFAULT LP expr RP */ ++{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} + break; +- case 33: /* ccons ::= DEFAULT PLUS scantok term */ +-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy202,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} ++ case 35: /* ccons ::= DEFAULT PLUS scantok term */ ++{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} + break; +- case 34: /* ccons ::= DEFAULT MINUS scantok term */ ++ case 36: /* ccons ::= DEFAULT MINUS scantok term */ + { +- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy202, 0); ++ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy590, 0); + sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); + } + break; +- case 35: /* ccons ::= DEFAULT scantok ID|INDEXED */ ++ case 37: /* ccons ::= DEFAULT scantok ID|INDEXED */ + { + Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0); + if( p ){ +@@ -157684,576 +179118,607 @@ static YYACTIONTYPE yy_reduce( + sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n); + } + break; +- case 36: /* ccons ::= NOT NULL onconf */ +-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy192);} ++ case 38: /* ccons ::= NOT NULL onconf */ ++{sqlite3AddNotNull(pParse, yymsp[0].minor.yy502);} + break; +- case 37: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ +-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy192,yymsp[0].minor.yy192,yymsp[-2].minor.yy192);} ++ case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ ++{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy502,yymsp[0].minor.yy502,yymsp[-2].minor.yy502);} + break; +- case 38: /* ccons ::= UNIQUE onconf */ +-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy192,0,0,0,0, ++ case 40: /* ccons ::= UNIQUE onconf */ ++{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy502,0,0,0,0, + SQLITE_IDXTYPE_UNIQUE);} + break; +- case 39: /* ccons ::= CHECK LP expr RP */ +-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy202);} ++ case 41: /* ccons ::= CHECK LP expr RP */ ++{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} + break; +- case 40: /* ccons ::= REFERENCES nm eidlist_opt refargs */ +-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy242,yymsp[0].minor.yy192);} ++ case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ ++{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy402,yymsp[0].minor.yy502);} + break; +- case 41: /* ccons ::= defer_subclause */ +-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy192);} ++ case 43: /* ccons ::= defer_subclause */ ++{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy502);} + break; +- case 42: /* ccons ::= COLLATE ID|STRING */ ++ case 44: /* ccons ::= COLLATE ID|STRING */ + {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} + break; +- case 43: /* generated ::= LP expr RP */ +-{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy202,0);} ++ case 45: /* generated ::= LP expr RP */ ++{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy590,0);} + break; +- case 44: /* generated ::= LP expr RP ID */ +-{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy202,&yymsp[0].minor.yy0);} ++ case 46: /* generated ::= LP expr RP ID */ ++{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy590,&yymsp[0].minor.yy0);} + break; +- case 47: /* refargs ::= */ +-{ yymsp[1].minor.yy192 = OE_None*0x0101; /* EV: R-19803-45884 */} ++ case 48: /* autoinc ::= AUTOINCR */ ++{yymsp[0].minor.yy502 = 1;} + break; +- case 48: /* refargs ::= refargs refarg */ +-{ yymsp[-1].minor.yy192 = (yymsp[-1].minor.yy192 & ~yymsp[0].minor.yy207.mask) | yymsp[0].minor.yy207.value; } ++ case 49: /* refargs ::= */ ++{ yymsp[1].minor.yy502 = OE_None*0x0101; /* EV: R-19803-45884 */} + break; +- case 49: /* refarg ::= MATCH nm */ +-{ yymsp[-1].minor.yy207.value = 0; yymsp[-1].minor.yy207.mask = 0x000000; } ++ case 50: /* refargs ::= refargs refarg */ ++{ yymsp[-1].minor.yy502 = (yymsp[-1].minor.yy502 & ~yymsp[0].minor.yy481.mask) | yymsp[0].minor.yy481.value; } + break; +- case 50: /* refarg ::= ON INSERT refact */ +-{ yymsp[-2].minor.yy207.value = 0; yymsp[-2].minor.yy207.mask = 0x000000; } ++ case 51: /* refarg ::= MATCH nm */ ++{ yymsp[-1].minor.yy481.value = 0; yymsp[-1].minor.yy481.mask = 0x000000; } + break; +- case 51: /* refarg ::= ON DELETE refact */ +-{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy192; yymsp[-2].minor.yy207.mask = 0x0000ff; } ++ case 52: /* refarg ::= ON INSERT refact */ ++{ yymsp[-2].minor.yy481.value = 0; yymsp[-2].minor.yy481.mask = 0x000000; } + break; +- case 52: /* refarg ::= ON UPDATE refact */ +-{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy192<<8; yymsp[-2].minor.yy207.mask = 0x00ff00; } ++ case 53: /* refarg ::= ON DELETE refact */ ++{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502; yymsp[-2].minor.yy481.mask = 0x0000ff; } + break; +- case 53: /* refact ::= SET NULL */ +-{ yymsp[-1].minor.yy192 = OE_SetNull; /* EV: R-33326-45252 */} ++ case 54: /* refarg ::= ON UPDATE refact */ ++{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502<<8; yymsp[-2].minor.yy481.mask = 0x00ff00; } + break; +- case 54: /* refact ::= SET DEFAULT */ +-{ yymsp[-1].minor.yy192 = OE_SetDflt; /* EV: R-33326-45252 */} ++ case 55: /* refact ::= SET NULL */ ++{ yymsp[-1].minor.yy502 = OE_SetNull; /* EV: R-33326-45252 */} + break; +- case 55: /* refact ::= CASCADE */ +-{ yymsp[0].minor.yy192 = OE_Cascade; /* EV: R-33326-45252 */} ++ case 56: /* refact ::= SET DEFAULT */ ++{ yymsp[-1].minor.yy502 = OE_SetDflt; /* EV: R-33326-45252 */} + break; +- case 56: /* refact ::= RESTRICT */ +-{ yymsp[0].minor.yy192 = OE_Restrict; /* EV: R-33326-45252 */} ++ case 57: /* refact ::= CASCADE */ ++{ yymsp[0].minor.yy502 = OE_Cascade; /* EV: R-33326-45252 */} + break; +- case 57: /* refact ::= NO ACTION */ +-{ yymsp[-1].minor.yy192 = OE_None; /* EV: R-33326-45252 */} ++ case 58: /* refact ::= RESTRICT */ ++{ yymsp[0].minor.yy502 = OE_Restrict; /* EV: R-33326-45252 */} + break; +- case 58: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ +-{yymsp[-2].minor.yy192 = 0;} ++ case 59: /* refact ::= NO ACTION */ ++{ yymsp[-1].minor.yy502 = OE_None; /* EV: R-33326-45252 */} + break; +- case 59: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ +- case 74: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==74); +- case 162: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==162); +-{yymsp[-1].minor.yy192 = yymsp[0].minor.yy192;} ++ case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ ++{yymsp[-2].minor.yy502 = 0;} + break; +- case 61: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ +- case 78: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==78); +- case 204: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==204); +- case 207: /* in_op ::= NOT IN */ yytestcase(yyruleno==207); +- case 233: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==233); +-{yymsp[-1].minor.yy192 = 1;} ++ case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ ++ case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); ++ case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173); ++{yymsp[-1].minor.yy502 = yymsp[0].minor.yy502;} + break; +- case 62: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ +-{yymsp[-1].minor.yy192 = 0;} ++ case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ ++ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); ++ case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219); ++ case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222); ++ case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247); ++{yymsp[-1].minor.yy502 = 1;} + break; +- case 64: /* tconscomma ::= COMMA */ +-{pParse->constraintName.n = 0;} ++ case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ ++{yymsp[-1].minor.yy502 = 0;} + break; +- case 66: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ +-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy242,yymsp[0].minor.yy192,yymsp[-2].minor.yy192,0);} ++ case 66: /* tconscomma ::= COMMA */ ++{ASSERT_IS_CREATE; pParse->u1.cr.constraintName.n = 0;} + break; +- case 67: /* tcons ::= UNIQUE LP sortlist RP onconf */ +-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy242,yymsp[0].minor.yy192,0,0,0,0, ++ case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ ++{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy402,yymsp[0].minor.yy502,yymsp[-2].minor.yy502,0);} ++ break; ++ case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ ++{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy402,yymsp[0].minor.yy502,0,0,0,0, + SQLITE_IDXTYPE_UNIQUE);} + break; +- case 68: /* tcons ::= CHECK LP expr RP onconf */ +-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy202);} ++ case 70: /* tcons ::= CHECK LP expr RP onconf */ ++{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy590,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} + break; +- case 69: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ ++ case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + { +- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy242, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy242, yymsp[-1].minor.yy192); +- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy192); ++ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy402, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[-1].minor.yy502); ++ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy502); + } + break; +- case 71: /* onconf ::= */ +- case 73: /* orconf ::= */ yytestcase(yyruleno==73); +-{yymsp[1].minor.yy192 = OE_Default;} ++ case 73: /* onconf ::= */ ++ case 75: /* orconf ::= */ yytestcase(yyruleno==75); ++{yymsp[1].minor.yy502 = OE_Default;} + break; +- case 72: /* onconf ::= ON CONFLICT resolvetype */ +-{yymsp[-2].minor.yy192 = yymsp[0].minor.yy192;} ++ case 74: /* onconf ::= ON CONFLICT resolvetype */ ++{yymsp[-2].minor.yy502 = yymsp[0].minor.yy502;} + break; +- case 75: /* resolvetype ::= IGNORE */ +-{yymsp[0].minor.yy192 = OE_Ignore;} ++ case 77: /* resolvetype ::= IGNORE */ ++{yymsp[0].minor.yy502 = OE_Ignore;} + break; +- case 76: /* resolvetype ::= REPLACE */ +- case 163: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==163); +-{yymsp[0].minor.yy192 = OE_Replace;} ++ case 78: /* resolvetype ::= REPLACE */ ++ case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174); ++{yymsp[0].minor.yy502 = OE_Replace;} + break; +- case 77: /* cmd ::= DROP TABLE ifexists fullname */ ++ case 79: /* cmd ::= DROP TABLE ifexists fullname */ + { +- sqlite3DropTable(pParse, yymsp[0].minor.yy47, 0, yymsp[-1].minor.yy192); ++ sqlite3DropTable(pParse, yymsp[0].minor.yy563, 0, yymsp[-1].minor.yy502); + } + break; +- case 80: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ ++ case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + { +- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy242, yymsp[0].minor.yy539, yymsp[-7].minor.yy192, yymsp[-5].minor.yy192); ++ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[0].minor.yy637, yymsp[-7].minor.yy502, yymsp[-5].minor.yy502); + } + break; +- case 81: /* cmd ::= DROP VIEW ifexists fullname */ ++ case 83: /* cmd ::= DROP VIEW ifexists fullname */ + { +- sqlite3DropTable(pParse, yymsp[0].minor.yy47, 1, yymsp[-1].minor.yy192); ++ sqlite3DropTable(pParse, yymsp[0].minor.yy563, 1, yymsp[-1].minor.yy502); + } + break; +- case 82: /* cmd ::= select */ ++ case 84: /* cmd ::= select */ + { + SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; +- sqlite3Select(pParse, yymsp[0].minor.yy539, &dest); +- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy539); +-} +- break; +- case 83: /* select ::= WITH wqlist selectnowith */ +-{ +- Select *p = yymsp[0].minor.yy539; +- if( p ){ +- p->pWith = yymsp[-1].minor.yy131; +- parserDoubleLinkSelect(pParse, p); +- }else{ +- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy131); ++ if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0 ++ || sqlite3ReadSchema(pParse)==SQLITE_OK ++ ){ ++ sqlite3Select(pParse, yymsp[0].minor.yy637, &dest); + } +- yymsp[-2].minor.yy539 = p; ++ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); + } + break; +- case 84: /* select ::= WITH RECURSIVE wqlist selectnowith */ +-{ +- Select *p = yymsp[0].minor.yy539; +- if( p ){ +- p->pWith = yymsp[-1].minor.yy131; +- parserDoubleLinkSelect(pParse, p); +- }else{ +- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy131); +- } +- yymsp[-3].minor.yy539 = p; +-} ++ case 85: /* select ::= WITH wqlist selectnowith */ ++{yymsp[-2].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} + break; +- case 85: /* select ::= selectnowith */ ++ case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ ++{yymsp[-3].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} ++ break; ++ case 87: /* select ::= selectnowith */ + { +- Select *p = yymsp[0].minor.yy539; ++ Select *p = yymsp[0].minor.yy637; + if( p ){ + parserDoubleLinkSelect(pParse, p); + } +- yymsp[0].minor.yy539 = p; /*A-overwrites-X*/ + } + break; +- case 86: /* selectnowith ::= selectnowith multiselect_op oneselect */ ++ case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ + { +- Select *pRhs = yymsp[0].minor.yy539; +- Select *pLhs = yymsp[-2].minor.yy539; ++ Select *pRhs = yymsp[0].minor.yy637; ++ Select *pLhs = yymsp[-2].minor.yy637; + if( pRhs && pRhs->pPrior ){ + SrcList *pFrom; + Token x; + x.n = 0; + parserDoubleLinkSelect(pParse, pRhs); +- pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); ++ pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0); + pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); + } + if( pRhs ){ +- pRhs->op = (u8)yymsp[-1].minor.yy192; ++ pRhs->op = (u8)yymsp[-1].minor.yy502; + pRhs->pPrior = pLhs; +- if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; +- pRhs->selFlags &= ~SF_MultiValue; +- if( yymsp[-1].minor.yy192!=TK_ALL ) pParse->hasCompound = 1; ++ if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue; ++ pRhs->selFlags &= ~(u32)SF_MultiValue; ++ if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1; + }else{ + sqlite3SelectDelete(pParse->db, pLhs); + } +- yymsp[-2].minor.yy539 = pRhs; ++ yymsp[-2].minor.yy637 = pRhs; + } + break; +- case 87: /* multiselect_op ::= UNION */ +- case 89: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==89); +-{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-OP*/} ++ case 89: /* multiselect_op ::= UNION */ ++ case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); ++{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-OP*/} + break; +- case 88: /* multiselect_op ::= UNION ALL */ +-{yymsp[-1].minor.yy192 = TK_ALL;} ++ case 90: /* multiselect_op ::= UNION ALL */ ++{yymsp[-1].minor.yy502 = TK_ALL;} + break; +- case 90: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ ++ case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + { +- yymsp[-8].minor.yy539 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy242,yymsp[-5].minor.yy47,yymsp[-4].minor.yy202,yymsp[-3].minor.yy242,yymsp[-2].minor.yy202,yymsp[-1].minor.yy242,yymsp[-7].minor.yy192,yymsp[0].minor.yy202); ++ yymsp[-8].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy402,yymsp[-5].minor.yy563,yymsp[-4].minor.yy590,yymsp[-3].minor.yy402,yymsp[-2].minor.yy590,yymsp[-1].minor.yy402,yymsp[-7].minor.yy502,yymsp[0].minor.yy590); + } + break; +- case 91: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ ++ case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + { +- yymsp[-9].minor.yy539 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy242,yymsp[-6].minor.yy47,yymsp[-5].minor.yy202,yymsp[-4].minor.yy242,yymsp[-3].minor.yy202,yymsp[-1].minor.yy242,yymsp[-8].minor.yy192,yymsp[0].minor.yy202); +- if( yymsp[-9].minor.yy539 ){ +- yymsp[-9].minor.yy539->pWinDefn = yymsp[-2].minor.yy303; ++ yymsp[-9].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy402,yymsp[-6].minor.yy563,yymsp[-5].minor.yy590,yymsp[-4].minor.yy402,yymsp[-3].minor.yy590,yymsp[-1].minor.yy402,yymsp[-8].minor.yy502,yymsp[0].minor.yy590); ++ if( yymsp[-9].minor.yy637 ){ ++ yymsp[-9].minor.yy637->pWinDefn = yymsp[-2].minor.yy483; + }else{ +- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy303); ++ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy483); + } + } + break; +- case 92: /* values ::= VALUES LP nexprlist RP */ ++ case 94: /* values ::= VALUES LP nexprlist RP */ + { +- yymsp[-3].minor.yy539 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy242,0,0,0,0,0,SF_Values,0); ++ yymsp[-3].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy402,0,0,0,0,0,SF_Values,0); + } + break; +- case 93: /* values ::= values COMMA LP nexprlist RP */ ++ case 95: /* oneselect ::= mvalues */ + { +- Select *pRight, *pLeft = yymsp[-4].minor.yy539; +- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy242,0,0,0,0,0,SF_Values|SF_MultiValue,0); +- if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; +- if( pRight ){ +- pRight->op = TK_ALL; +- pRight->pPrior = pLeft; +- yymsp[-4].minor.yy539 = pRight; +- }else{ +- yymsp[-4].minor.yy539 = pLeft; +- } ++ sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy637); ++} ++ break; ++ case 96: /* mvalues ::= values COMMA LP nexprlist RP */ ++ case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97); ++{ ++ yymsp[-4].minor.yy637 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy637, yymsp[-1].minor.yy402); + } + break; +- case 94: /* distinct ::= DISTINCT */ +-{yymsp[0].minor.yy192 = SF_Distinct;} ++ case 98: /* distinct ::= DISTINCT */ ++{yymsp[0].minor.yy502 = SF_Distinct;} + break; +- case 95: /* distinct ::= ALL */ +-{yymsp[0].minor.yy192 = SF_All;} ++ case 99: /* distinct ::= ALL */ ++{yymsp[0].minor.yy502 = SF_All;} + break; +- case 97: /* sclp ::= */ +- case 130: /* orderby_opt ::= */ yytestcase(yyruleno==130); +- case 140: /* groupby_opt ::= */ yytestcase(yyruleno==140); +- case 220: /* exprlist ::= */ yytestcase(yyruleno==220); +- case 223: /* paren_exprlist ::= */ yytestcase(yyruleno==223); +- case 228: /* eidlist_opt ::= */ yytestcase(yyruleno==228); +-{yymsp[1].minor.yy242 = 0;} ++ case 101: /* sclp ::= */ ++ case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134); ++ case 144: /* groupby_opt ::= */ yytestcase(yyruleno==144); ++ case 234: /* exprlist ::= */ yytestcase(yyruleno==234); ++ case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237); ++ case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242); ++{yymsp[1].minor.yy402 = 0;} + break; +- case 98: /* selcollist ::= sclp scanpt expr scanpt as */ ++ case 102: /* selcollist ::= sclp scanpt expr scanpt as */ + { +- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[-2].minor.yy202); +- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[0].minor.yy0, 1); +- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy242,yymsp[-3].minor.yy436,yymsp[-1].minor.yy436); ++ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); ++ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[0].minor.yy0, 1); ++ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy402,yymsp[-3].minor.yy342,yymsp[-1].minor.yy342); + } + break; +- case 99: /* selcollist ::= sclp scanpt STAR */ ++ case 103: /* selcollist ::= sclp scanpt STAR */ + { + Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); +- yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy242, p); ++ sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); ++ yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy402, p); + } + break; +- case 100: /* selcollist ::= sclp scanpt nm DOT STAR */ ++ case 104: /* selcollist ::= sclp scanpt nm DOT STAR */ + { +- Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); +- Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); +- Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); +- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, pDot); ++ Expr *pRight, *pLeft, *pDot; ++ pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); ++ sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); ++ pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); ++ pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); ++ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, pDot); + } + break; +- case 101: /* as ::= AS nm */ +- case 112: /* dbnm ::= DOT nm */ yytestcase(yyruleno==112); +- case 244: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==244); +- case 245: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==245); ++ case 105: /* as ::= AS nm */ ++ case 117: /* dbnm ::= DOT nm */ yytestcase(yyruleno==117); ++ case 258: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==258); ++ case 259: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==259); + {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} + break; +- case 103: /* from ::= */ +- case 106: /* stl_prefix ::= */ yytestcase(yyruleno==106); +-{yymsp[1].minor.yy47 = 0;} ++ case 107: /* from ::= */ ++ case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110); ++{yymsp[1].minor.yy563 = 0;} + break; +- case 104: /* from ::= FROM seltablist */ ++ case 108: /* from ::= FROM seltablist */ + { +- yymsp[-1].minor.yy47 = yymsp[0].minor.yy47; +- sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy47); ++ yymsp[-1].minor.yy563 = yymsp[0].minor.yy563; ++ sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy563); + } + break; +- case 105: /* stl_prefix ::= seltablist joinop */ ++ case 109: /* stl_prefix ::= seltablist joinop */ + { +- if( ALWAYS(yymsp[-1].minor.yy47 && yymsp[-1].minor.yy47->nSrc>0) ) yymsp[-1].minor.yy47->a[yymsp[-1].minor.yy47->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy192; ++ if( ALWAYS(yymsp[-1].minor.yy563 && yymsp[-1].minor.yy563->nSrc>0) ) yymsp[-1].minor.yy563->a[yymsp[-1].minor.yy563->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy502; + } + break; +- case 107: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ ++ case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */ + { +- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); +- sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy47, &yymsp[-2].minor.yy0); ++ yymsp[-4].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy563,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); + } + break; +- case 108: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ ++ case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + { +- yymsp[-8].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy47,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); +- sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy47, yymsp[-4].minor.yy242); ++ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy421); ++ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-1].minor.yy0); + } + break; +- case 109: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ ++ case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + { +- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy539,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); ++ yymsp[-7].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy563,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); ++ sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy563, yymsp[-3].minor.yy402); ++} ++ break; ++ case 114: /* seltablist ::= stl_prefix LP select RP as on_using */ ++{ ++ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy637,&yymsp[0].minor.yy421); + } + break; +- case 110: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ ++ case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ + { +- if( yymsp[-6].minor.yy47==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy202==0 && yymsp[0].minor.yy600==0 ){ +- yymsp[-6].minor.yy47 = yymsp[-4].minor.yy47; +- }else if( yymsp[-4].minor.yy47->nSrc==1 ){ +- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); +- if( yymsp[-6].minor.yy47 ){ +- struct SrcList_item *pNew = &yymsp[-6].minor.yy47->a[yymsp[-6].minor.yy47->nSrc-1]; +- struct SrcList_item *pOld = yymsp[-4].minor.yy47->a; ++ if( yymsp[-5].minor.yy563==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy421.pOn==0 && yymsp[0].minor.yy421.pUsing==0 ){ ++ yymsp[-5].minor.yy563 = yymsp[-3].minor.yy563; ++ }else if( ALWAYS(yymsp[-3].minor.yy563!=0) && yymsp[-3].minor.yy563->nSrc==1 ){ ++ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); ++ if( yymsp[-5].minor.yy563 ){ ++ SrcItem *pNew = &yymsp[-5].minor.yy563->a[yymsp[-5].minor.yy563->nSrc-1]; ++ SrcItem *pOld = yymsp[-3].minor.yy563->a; ++ assert( pOld->fg.fixedSchema==0 ); + pNew->zName = pOld->zName; +- pNew->zDatabase = pOld->zDatabase; +- pNew->pSelect = pOld->pSelect; ++ assert( pOld->fg.fixedSchema==0 ); ++ if( pOld->fg.isSubquery ){ ++ pNew->fg.isSubquery = 1; ++ pNew->u4.pSubq = pOld->u4.pSubq; ++ pOld->u4.pSubq = 0; ++ pOld->fg.isSubquery = 0; ++ assert( pNew->u4.pSubq!=0 && pNew->u4.pSubq->pSelect!=0 ); ++ if( (pNew->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 ){ ++ pNew->fg.isNestedFrom = 1; ++ } ++ }else{ ++ pNew->u4.zDatabase = pOld->u4.zDatabase; ++ pOld->u4.zDatabase = 0; ++ } + if( pOld->fg.isTabFunc ){ + pNew->u1.pFuncArg = pOld->u1.pFuncArg; + pOld->u1.pFuncArg = 0; + pOld->fg.isTabFunc = 0; + pNew->fg.isTabFunc = 1; + } +- pOld->zName = pOld->zDatabase = 0; +- pOld->pSelect = 0; ++ pOld->zName = 0; + } +- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy47); ++ sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy563); + }else{ + Select *pSubquery; +- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy47); +- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy47,0,0,0,0,SF_NestedFrom,0); +- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); ++ sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy563); ++ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy563,0,0,0,0,SF_NestedFrom,0); ++ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy421); + } + } + break; +- case 111: /* dbnm ::= */ +- case 125: /* indexed_opt ::= */ yytestcase(yyruleno==125); ++ case 116: /* dbnm ::= */ ++ case 131: /* indexed_opt ::= */ yytestcase(yyruleno==131); + {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} + break; +- case 113: /* fullname ::= nm */ ++ case 118: /* fullname ::= nm */ + { +- yylhsminor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); +- if( IN_RENAME_OBJECT && yylhsminor.yy47 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy47->a[0].zName, &yymsp[0].minor.yy0); ++ yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); ++ if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); + } +- yymsp[0].minor.yy47 = yylhsminor.yy47; ++ yymsp[0].minor.yy563 = yylhsminor.yy563; + break; +- case 114: /* fullname ::= nm DOT nm */ ++ case 119: /* fullname ::= nm DOT nm */ + { +- yylhsminor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); +- if( IN_RENAME_OBJECT && yylhsminor.yy47 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy47->a[0].zName, &yymsp[0].minor.yy0); ++ yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); ++ if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); + } +- yymsp[-2].minor.yy47 = yylhsminor.yy47; ++ yymsp[-2].minor.yy563 = yylhsminor.yy563; + break; +- case 115: /* xfullname ::= nm */ +-{yymsp[0].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} ++ case 120: /* xfullname ::= nm */ ++{yymsp[0].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} + break; +- case 116: /* xfullname ::= nm DOT nm */ +-{yymsp[-2].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} ++ case 121: /* xfullname ::= nm DOT nm */ ++{yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + break; +- case 117: /* xfullname ::= nm DOT nm AS nm */ ++ case 122: /* xfullname ::= nm DOT nm AS nm */ + { +- yymsp[-4].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ +- if( yymsp[-4].minor.yy47 ) yymsp[-4].minor.yy47->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); ++ yymsp[-4].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ ++ if( yymsp[-4].minor.yy563 ) yymsp[-4].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + } + break; +- case 118: /* xfullname ::= nm AS nm */ ++ case 123: /* xfullname ::= nm AS nm */ + { +- yymsp[-2].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ +- if( yymsp[-2].minor.yy47 ) yymsp[-2].minor.yy47->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); ++ yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ ++ if( yymsp[-2].minor.yy563 ) yymsp[-2].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + } + break; +- case 119: /* joinop ::= COMMA|JOIN */ +-{ yymsp[0].minor.yy192 = JT_INNER; } ++ case 124: /* joinop ::= COMMA|JOIN */ ++{ yymsp[0].minor.yy502 = JT_INNER; } ++ break; ++ case 125: /* joinop ::= JOIN_KW JOIN */ ++{yymsp[-1].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} + break; +- case 120: /* joinop ::= JOIN_KW JOIN */ +-{yymsp[-1].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} ++ case 126: /* joinop ::= JOIN_KW nm JOIN */ ++{yymsp[-2].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} + break; +- case 121: /* joinop ::= JOIN_KW nm JOIN */ +-{yymsp[-2].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} ++ case 127: /* joinop ::= JOIN_KW nm nm JOIN */ ++{yymsp[-3].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} + break; +- case 122: /* joinop ::= JOIN_KW nm nm JOIN */ +-{yymsp[-3].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} ++ case 128: /* on_using ::= ON expr */ ++{yymsp[-1].minor.yy421.pOn = yymsp[0].minor.yy590; yymsp[-1].minor.yy421.pUsing = 0;} + break; +- case 123: /* on_opt ::= ON expr */ +- case 143: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==143); +- case 150: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==150); +- case 216: /* case_else ::= ELSE expr */ yytestcase(yyruleno==216); +- case 237: /* vinto ::= INTO expr */ yytestcase(yyruleno==237); +-{yymsp[-1].minor.yy202 = yymsp[0].minor.yy202;} ++ case 129: /* on_using ::= USING LP idlist RP */ ++{yymsp[-3].minor.yy421.pOn = 0; yymsp[-3].minor.yy421.pUsing = yymsp[-1].minor.yy204;} + break; +- case 124: /* on_opt ::= */ +- case 142: /* having_opt ::= */ yytestcase(yyruleno==142); +- case 144: /* limit_opt ::= */ yytestcase(yyruleno==144); +- case 149: /* where_opt ::= */ yytestcase(yyruleno==149); +- case 217: /* case_else ::= */ yytestcase(yyruleno==217); +- case 219: /* case_operand ::= */ yytestcase(yyruleno==219); +- case 238: /* vinto ::= */ yytestcase(yyruleno==238); +-{yymsp[1].minor.yy202 = 0;} ++ case 130: /* on_using ::= */ ++{yymsp[1].minor.yy421.pOn = 0; yymsp[1].minor.yy421.pUsing = 0;} + break; +- case 126: /* indexed_opt ::= INDEXED BY nm */ ++ case 132: /* indexed_by ::= INDEXED BY nm */ + {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} + break; +- case 127: /* indexed_opt ::= NOT INDEXED */ ++ case 133: /* indexed_by ::= NOT INDEXED */ + {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} + break; +- case 128: /* using_opt ::= USING LP idlist RP */ +-{yymsp[-3].minor.yy600 = yymsp[-1].minor.yy600;} +- break; +- case 129: /* using_opt ::= */ +- case 164: /* idlist_opt ::= */ yytestcase(yyruleno==164); +-{yymsp[1].minor.yy600 = 0;} +- break; +- case 131: /* orderby_opt ::= ORDER BY sortlist */ +- case 141: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==141); +-{yymsp[-2].minor.yy242 = yymsp[0].minor.yy242;} ++ case 135: /* orderby_opt ::= ORDER BY sortlist */ ++ case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145); ++{yymsp[-2].minor.yy402 = yymsp[0].minor.yy402;} + break; +- case 132: /* sortlist ::= sortlist COMMA expr sortorder nulls */ ++ case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */ + { +- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202); +- sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy242,yymsp[-1].minor.yy192,yymsp[0].minor.yy192); ++ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402,yymsp[-2].minor.yy590); ++ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); + } + break; +- case 133: /* sortlist ::= expr sortorder nulls */ ++ case 137: /* sortlist ::= expr sortorder nulls */ + { +- yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy202); /*A-overwrites-Y*/ +- sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy242,yymsp[-1].minor.yy192,yymsp[0].minor.yy192); ++ yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy590); /*A-overwrites-Y*/ ++ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); + } + break; +- case 134: /* sortorder ::= ASC */ +-{yymsp[0].minor.yy192 = SQLITE_SO_ASC;} ++ case 138: /* sortorder ::= ASC */ ++{yymsp[0].minor.yy502 = SQLITE_SO_ASC;} + break; +- case 135: /* sortorder ::= DESC */ +-{yymsp[0].minor.yy192 = SQLITE_SO_DESC;} ++ case 139: /* sortorder ::= DESC */ ++{yymsp[0].minor.yy502 = SQLITE_SO_DESC;} + break; +- case 136: /* sortorder ::= */ +- case 139: /* nulls ::= */ yytestcase(yyruleno==139); +-{yymsp[1].minor.yy192 = SQLITE_SO_UNDEFINED;} ++ case 140: /* sortorder ::= */ ++ case 143: /* nulls ::= */ yytestcase(yyruleno==143); ++{yymsp[1].minor.yy502 = SQLITE_SO_UNDEFINED;} + break; +- case 137: /* nulls ::= NULLS FIRST */ +-{yymsp[-1].minor.yy192 = SQLITE_SO_ASC;} ++ case 141: /* nulls ::= NULLS FIRST */ ++{yymsp[-1].minor.yy502 = SQLITE_SO_ASC;} + break; +- case 138: /* nulls ::= NULLS LAST */ +-{yymsp[-1].minor.yy192 = SQLITE_SO_DESC;} ++ case 142: /* nulls ::= NULLS LAST */ ++{yymsp[-1].minor.yy502 = SQLITE_SO_DESC;} + break; +- case 145: /* limit_opt ::= LIMIT expr */ +-{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy202,0);} ++ case 146: /* having_opt ::= */ ++ case 148: /* limit_opt ::= */ yytestcase(yyruleno==148); ++ case 153: /* where_opt ::= */ yytestcase(yyruleno==153); ++ case 155: /* where_opt_ret ::= */ yytestcase(yyruleno==155); ++ case 232: /* case_else ::= */ yytestcase(yyruleno==232); ++ case 233: /* case_operand ::= */ yytestcase(yyruleno==233); ++ case 252: /* vinto ::= */ yytestcase(yyruleno==252); ++{yymsp[1].minor.yy590 = 0;} + break; +- case 146: /* limit_opt ::= LIMIT expr OFFSET expr */ +-{yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} ++ case 147: /* having_opt ::= HAVING expr */ ++ case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154); ++ case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156); ++ case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231); ++ case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251); ++{yymsp[-1].minor.yy590 = yymsp[0].minor.yy590;} + break; +- case 147: /* limit_opt ::= LIMIT expr COMMA expr */ +-{yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy202,yymsp[-2].minor.yy202);} ++ case 149: /* limit_opt ::= LIMIT expr */ ++{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,0);} + break; +- case 148: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ ++ case 150: /* limit_opt ::= LIMIT expr OFFSET expr */ ++{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} ++ break; ++ case 151: /* limit_opt ::= LIMIT expr COMMA expr */ ++{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,yymsp[-2].minor.yy590);} ++ break; ++ case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + { +- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy47, &yymsp[-1].minor.yy0); +- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy47,yymsp[0].minor.yy202,0,0); ++ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy563, &yymsp[-1].minor.yy0); ++ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy563,yymsp[0].minor.yy590,0,0); + } + break; +- case 151: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt */ ++ case 157: /* where_opt_ret ::= RETURNING selcollist */ ++{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-1].minor.yy590 = 0;} ++ break; ++ case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ ++{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-3].minor.yy590 = yymsp[-2].minor.yy590;} ++ break; ++ case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + { +- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy47, &yymsp[-4].minor.yy0); +- sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy242,"set list"); +- yymsp[-5].minor.yy47 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy47, yymsp[-1].minor.yy47); +- sqlite3Update(pParse,yymsp[-5].minor.yy47,yymsp[-2].minor.yy242,yymsp[0].minor.yy202,yymsp[-6].minor.yy192,0,0,0); ++ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-4].minor.yy0); ++ sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy402,"set list"); ++ if( yymsp[-1].minor.yy563 ){ ++ SrcList *pFromClause = yymsp[-1].minor.yy563; ++ if( pFromClause->nSrc>1 ){ ++ Select *pSubquery; ++ Token as; ++ pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0); ++ as.n = 0; ++ as.z = 0; ++ pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); ++ } ++ yymsp[-5].minor.yy563 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy563, pFromClause); ++ } ++ sqlite3Update(pParse,yymsp[-5].minor.yy563,yymsp[-2].minor.yy402,yymsp[0].minor.yy590,yymsp[-6].minor.yy502,0,0,0); + } + break; +- case 152: /* setlist ::= setlist COMMA nm EQ expr */ ++ case 160: /* setlist ::= setlist COMMA nm EQ expr */ + { +- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[0].minor.yy202); +- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, 1); ++ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[0].minor.yy590); ++ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, 1); + } + break; +- case 153: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ ++ case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ + { +- yymsp[-6].minor.yy242 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy242, yymsp[-3].minor.yy600, yymsp[0].minor.yy202); ++ yymsp[-6].minor.yy402 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy402, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); + } + break; +- case 154: /* setlist ::= nm EQ expr */ ++ case 162: /* setlist ::= nm EQ expr */ + { +- yylhsminor.yy242 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy202); +- sqlite3ExprListSetName(pParse, yylhsminor.yy242, &yymsp[-2].minor.yy0, 1); ++ yylhsminor.yy402 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy590); ++ sqlite3ExprListSetName(pParse, yylhsminor.yy402, &yymsp[-2].minor.yy0, 1); + } +- yymsp[-2].minor.yy242 = yylhsminor.yy242; ++ yymsp[-2].minor.yy402 = yylhsminor.yy402; + break; +- case 155: /* setlist ::= LP idlist RP EQ expr */ ++ case 163: /* setlist ::= LP idlist RP EQ expr */ + { +- yymsp[-4].minor.yy242 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy600, yymsp[0].minor.yy202); ++ yymsp[-4].minor.yy402 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); + } + break; +- case 156: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ ++ case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + { +- sqlite3Insert(pParse, yymsp[-3].minor.yy47, yymsp[-1].minor.yy539, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, yymsp[0].minor.yy318); ++ sqlite3Insert(pParse, yymsp[-3].minor.yy563, yymsp[-1].minor.yy637, yymsp[-2].minor.yy204, yymsp[-5].minor.yy502, yymsp[0].minor.yy403); + } + break; +- case 157: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ ++ case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + { +- sqlite3Insert(pParse, yymsp[-3].minor.yy47, 0, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, 0); ++ sqlite3Insert(pParse, yymsp[-4].minor.yy563, 0, yymsp[-3].minor.yy204, yymsp[-6].minor.yy502, 0); + } + break; +- case 158: /* upsert ::= */ +-{ yymsp[1].minor.yy318 = 0; } ++ case 166: /* upsert ::= */ ++{ yymsp[1].minor.yy403 = 0; } ++ break; ++ case 167: /* upsert ::= RETURNING selcollist */ ++{ yymsp[-1].minor.yy403 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy402); } + break; +- case 159: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ +-{ yymsp[-10].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy242,yymsp[-5].minor.yy202,yymsp[-1].minor.yy242,yymsp[0].minor.yy202);} ++ case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ ++{ yymsp[-11].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy402,yymsp[-6].minor.yy590,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,yymsp[0].minor.yy403);} + break; +- case 160: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ +-{ yymsp[-7].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202,0,0); } ++ case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ ++{ yymsp[-8].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy402,yymsp[-3].minor.yy590,0,0,yymsp[0].minor.yy403); } + break; +- case 161: /* upsert ::= ON CONFLICT DO NOTHING */ +-{ yymsp[-3].minor.yy318 = sqlite3UpsertNew(pParse->db,0,0,0,0); } ++ case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */ ++{ yymsp[-4].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } + break; +- case 165: /* idlist_opt ::= LP idlist RP */ +-{yymsp[-2].minor.yy600 = yymsp[-1].minor.yy600;} ++ case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ ++{ yymsp[-7].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,0);} + break; +- case 166: /* idlist ::= idlist COMMA nm */ +-{yymsp[-2].minor.yy600 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy600,&yymsp[0].minor.yy0);} ++ case 172: /* returning ::= RETURNING selcollist */ ++{sqlite3AddReturning(pParse,yymsp[0].minor.yy402);} + break; +- case 167: /* idlist ::= nm */ +-{yymsp[0].minor.yy600 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} ++ case 175: /* idlist_opt ::= */ ++{yymsp[1].minor.yy204 = 0;} + break; +- case 168: /* expr ::= LP expr RP */ +-{yymsp[-2].minor.yy202 = yymsp[-1].minor.yy202;} ++ case 176: /* idlist_opt ::= LP idlist RP */ ++{yymsp[-2].minor.yy204 = yymsp[-1].minor.yy204;} + break; +- case 169: /* expr ::= ID|INDEXED */ +- case 170: /* expr ::= JOIN_KW */ yytestcase(yyruleno==170); +-{yymsp[0].minor.yy202=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} ++ case 177: /* idlist ::= idlist COMMA nm */ ++{yymsp[-2].minor.yy204 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy204,&yymsp[0].minor.yy0);} + break; +- case 171: /* expr ::= nm DOT nm */ ++ case 178: /* idlist ::= nm */ ++{yymsp[0].minor.yy204 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} ++ break; ++ case 179: /* expr ::= LP expr RP */ ++{yymsp[-2].minor.yy590 = yymsp[-1].minor.yy590;} ++ break; ++ case 180: /* expr ::= ID|INDEXED|JOIN_KW */ ++{yymsp[0].minor.yy590=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} ++ break; ++ case 181: /* expr ::= nm DOT nm */ + { +- Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); +- Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); +- if( IN_RENAME_OBJECT ){ +- sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0); +- sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0); +- } +- yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); ++ Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); ++ Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); ++ yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + } +- yymsp[-2].minor.yy202 = yylhsminor.yy202; ++ yymsp[-2].minor.yy590 = yylhsminor.yy590; + break; +- case 172: /* expr ::= nm DOT nm DOT nm */ ++ case 182: /* expr ::= nm DOT nm DOT nm */ + { +- Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1); +- Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); +- Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); ++ Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0); ++ Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); ++ Expr *temp3 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); + Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3); + if( IN_RENAME_OBJECT ){ +- sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0); +- sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0); ++ sqlite3RenameTokenRemap(pParse, 0, temp1); + } +- yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); ++ yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + } +- yymsp[-4].minor.yy202 = yylhsminor.yy202; ++ yymsp[-4].minor.yy590 = yylhsminor.yy590; + break; +- case 173: /* term ::= NULL|FLOAT|BLOB */ +- case 174: /* term ::= STRING */ yytestcase(yyruleno==174); +-{yymsp[0].minor.yy202=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} ++ case 183: /* term ::= NULL|FLOAT|BLOB */ ++ case 184: /* term ::= STRING */ yytestcase(yyruleno==184); ++{yymsp[0].minor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} + break; +- case 175: /* term ::= INTEGER */ ++ case 185: /* term ::= INTEGER */ + { +- yylhsminor.yy202 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); ++ yylhsminor.yy590 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); ++ if( yylhsminor.yy590 ) yylhsminor.yy590->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); + } +- yymsp[0].minor.yy202 = yylhsminor.yy202; ++ yymsp[0].minor.yy590 = yylhsminor.yy590; + break; +- case 176: /* expr ::= VARIABLE */ ++ case 186: /* expr ::= VARIABLE */ + { + if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ + u32 n = yymsp[0].minor.yy0.n; +- yymsp[0].minor.yy202 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); +- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy202, n); ++ yymsp[0].minor.yy590 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); ++ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy590, n); + }else{ + /* When doing a nested parse, one can include terms in an expression + ** that look like this: #1 #2 ... These terms refer to registers +@@ -158261,359 +179726,427 @@ static YYACTIONTYPE yy_reduce( + Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ + assert( t.n>=2 ); + if( pParse->nested==0 ){ +- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); +- yymsp[0].minor.yy202 = 0; ++ parserSyntaxError(pParse, &t); ++ yymsp[0].minor.yy590 = 0; + }else{ +- yymsp[0].minor.yy202 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); +- if( yymsp[0].minor.yy202 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy202->iTable); ++ yymsp[0].minor.yy590 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); ++ if( yymsp[0].minor.yy590 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy590->iTable); + } + } + } + break; +- case 177: /* expr ::= expr COLLATE ID|STRING */ ++ case 187: /* expr ::= expr COLLATE ID|STRING */ ++{ ++ yymsp[-2].minor.yy590 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy590, &yymsp[0].minor.yy0, 1); ++} ++ break; ++ case 188: /* expr ::= CAST LP expr AS typetoken RP */ ++{ ++ yymsp[-5].minor.yy590 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); ++ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy590, yymsp[-3].minor.yy590, 0); ++} ++ break; ++ case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + { +- yymsp[-2].minor.yy202 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy202, &yymsp[0].minor.yy0, 1); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy502); + } ++ yymsp[-4].minor.yy590 = yylhsminor.yy590; + break; +- case 178: /* expr ::= CAST LP expr AS typetoken RP */ ++ case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + { +- yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); +- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy202, yymsp[-3].minor.yy202, 0); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy402, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy502); ++ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-1].minor.yy402); + } ++ yymsp[-7].minor.yy590 = yylhsminor.yy590; + break; +- case 179: /* expr ::= ID|INDEXED LP distinct exprlist RP */ ++ case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + { +- yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy192); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + } +- yymsp[-4].minor.yy202 = yylhsminor.yy202; ++ yymsp[-3].minor.yy590 = yylhsminor.yy590; + break; +- case 180: /* expr ::= ID|INDEXED LP STAR RP */ ++ case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + { +- yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy402, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy502); ++ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); + } +- yymsp[-3].minor.yy202 = yylhsminor.yy202; ++ yymsp[-5].minor.yy590 = yylhsminor.yy590; + break; +- case 181: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ ++ case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + { +- yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy242, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy192); +- sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy402, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy502); ++ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); ++ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-2].minor.yy402); + } +- yymsp[-5].minor.yy202 = yylhsminor.yy202; ++ yymsp[-8].minor.yy590 = yylhsminor.yy590; + break; +- case 182: /* expr ::= ID|INDEXED LP STAR RP filter_over */ ++ case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + { +- yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); +- sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); ++ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); + } +- yymsp[-4].minor.yy202 = yylhsminor.yy202; ++ yymsp[-4].minor.yy590 = yylhsminor.yy590; + break; +- case 183: /* term ::= CTIME_KW */ ++ case 195: /* term ::= CTIME_KW */ + { +- yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + } +- yymsp[0].minor.yy202 = yylhsminor.yy202; ++ yymsp[0].minor.yy590 = yylhsminor.yy590; + break; +- case 184: /* expr ::= LP nexprlist COMMA expr RP */ ++ case 196: /* expr ::= LP nexprlist COMMA expr RP */ + { +- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202); +- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); +- if( yymsp[-4].minor.yy202 ){ +- yymsp[-4].minor.yy202->x.pList = pList; ++ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590); ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); ++ if( yymsp[-4].minor.yy590 ){ ++ yymsp[-4].minor.yy590->x.pList = pList; + if( ALWAYS(pList->nExpr) ){ +- yymsp[-4].minor.yy202->flags |= pList->a[0].pExpr->flags & EP_Propagate; ++ yymsp[-4].minor.yy590->flags |= pList->a[0].pExpr->flags & EP_Propagate; + } + }else{ + sqlite3ExprListDelete(pParse->db, pList); + } + } + break; +- case 185: /* expr ::= expr AND expr */ +-{yymsp[-2].minor.yy202=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} ++ case 197: /* expr ::= expr AND expr */ ++{yymsp[-2].minor.yy590=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} + break; +- case 186: /* expr ::= expr OR expr */ +- case 187: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==187); +- case 188: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==188); +- case 189: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==189); +- case 190: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==190); +- case 191: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==191); +- case 192: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==192); +-{yymsp[-2].minor.yy202=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} ++ case 198: /* expr ::= expr OR expr */ ++ case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199); ++ case 200: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==200); ++ case 201: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==201); ++ case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202); ++ case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203); ++ case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204); ++{yymsp[-2].minor.yy590=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} + break; +- case 193: /* likeop ::= NOT LIKE_KW|MATCH */ ++ case 205: /* likeop ::= NOT LIKE_KW|MATCH */ + {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} + break; +- case 194: /* expr ::= expr likeop expr */ ++ case 206: /* expr ::= expr likeop expr */ + { + ExprList *pList; + int bNot = yymsp[-1].minor.yy0.n & 0x80000000; + yymsp[-1].minor.yy0.n &= 0x7fffffff; +- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy202); +- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy202); +- yymsp[-2].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); +- if( bNot ) yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy202, 0); +- if( yymsp[-2].minor.yy202 ) yymsp[-2].minor.yy202->flags |= EP_InfixFunc; ++ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy590); ++ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy590); ++ yymsp[-2].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); ++ if( bNot ) yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy590, 0); ++ if( yymsp[-2].minor.yy590 ) yymsp[-2].minor.yy590->flags |= EP_InfixFunc; + } + break; +- case 195: /* expr ::= expr likeop expr ESCAPE expr */ ++ case 207: /* expr ::= expr likeop expr ESCAPE expr */ + { + ExprList *pList; + int bNot = yymsp[-3].minor.yy0.n & 0x80000000; + yymsp[-3].minor.yy0.n &= 0x7fffffff; +- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); +- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy202); +- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202); +- yymsp[-4].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); +- if( bNot ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); +- if( yymsp[-4].minor.yy202 ) yymsp[-4].minor.yy202->flags |= EP_InfixFunc; ++ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); ++ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy590); ++ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); ++ yymsp[-4].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); ++ if( bNot ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); ++ if( yymsp[-4].minor.yy590 ) yymsp[-4].minor.yy590->flags |= EP_InfixFunc; + } + break; +- case 196: /* expr ::= expr ISNULL|NOTNULL */ +-{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy202,0);} ++ case 208: /* expr ::= expr ISNULL|NOTNULL */ ++{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy590,0);} + break; +- case 197: /* expr ::= expr NOT NULL */ +-{yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy202,0);} ++ case 209: /* expr ::= expr NOT NULL */ ++{yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy590,0);} + break; +- case 198: /* expr ::= expr IS expr */ ++ case 210: /* expr ::= expr IS expr */ + { +- yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy202,yymsp[0].minor.yy202); +- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-2].minor.yy202, TK_ISNULL); ++ yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy590,yymsp[0].minor.yy590); ++ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-2].minor.yy590, TK_ISNULL); + } + break; +- case 199: /* expr ::= expr IS NOT expr */ ++ case 211: /* expr ::= expr IS NOT expr */ + { +- yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy202,yymsp[0].minor.yy202); +- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-3].minor.yy202, TK_NOTNULL); ++ yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy590,yymsp[0].minor.yy590); ++ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-3].minor.yy590, TK_NOTNULL); + } + break; +- case 200: /* expr ::= NOT expr */ +- case 201: /* expr ::= BITNOT expr */ yytestcase(yyruleno==201); +-{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy202, 0);/*A-overwrites-B*/} ++ case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */ ++{ ++ yymsp[-5].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy590,yymsp[0].minor.yy590); ++ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-5].minor.yy590, TK_ISNULL); ++} + break; +- case 202: /* expr ::= PLUS|MINUS expr */ ++ case 213: /* expr ::= expr IS DISTINCT FROM expr */ + { +- yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy202, 0); +- /*A-overwrites-B*/ ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy590,yymsp[0].minor.yy590); ++ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-4].minor.yy590, TK_NOTNULL); + } + break; +- case 203: /* between_op ::= BETWEEN */ +- case 206: /* in_op ::= IN */ yytestcase(yyruleno==206); +-{yymsp[0].minor.yy192 = 0;} ++ case 214: /* expr ::= NOT expr */ ++ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); ++{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy590, 0);/*A-overwrites-B*/} + break; +- case 205: /* expr ::= expr between_op expr AND expr */ ++ case 216: /* expr ::= PLUS|MINUS expr */ + { +- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); +- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202); +- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy202, 0); +- if( yymsp[-4].minor.yy202 ){ +- yymsp[-4].minor.yy202->x.pList = pList; ++ Expr *p = yymsp[0].minor.yy590; ++ u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); ++ assert( TK_UPLUS>TK_PLUS ); ++ assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); ++ if( p && p->op==TK_UPLUS ){ ++ p->op = op; ++ yymsp[-1].minor.yy590 = p; ++ }else{ ++ yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, op, p, 0); ++ /*A-overwrites-B*/ ++ } ++} ++ break; ++ case 217: /* expr ::= expr PTR expr */ ++{ ++ ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy590); ++ pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy590); ++ yylhsminor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); ++} ++ yymsp[-2].minor.yy590 = yylhsminor.yy590; ++ break; ++ case 218: /* between_op ::= BETWEEN */ ++ case 221: /* in_op ::= IN */ yytestcase(yyruleno==221); ++{yymsp[0].minor.yy502 = 0;} ++ break; ++ case 220: /* expr ::= expr between_op expr AND expr */ ++{ ++ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); ++ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy590, 0); ++ if( yymsp[-4].minor.yy590 ){ ++ yymsp[-4].minor.yy590->x.pList = pList; + }else{ + sqlite3ExprListDelete(pParse->db, pList); + } +- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); ++ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + } + break; +- case 208: /* expr ::= expr in_op LP exprlist RP */ ++ case 223: /* expr ::= expr in_op LP exprlist RP */ + { +- if( yymsp[-1].minor.yy242==0 ){ ++ if( yymsp[-1].minor.yy402==0 ){ + /* Expressions of the form + ** + ** expr1 IN () + ** expr1 NOT IN () + ** +- ** simplify to constants 0 (false) and 1 (true), respectively, +- ** regardless of the value of expr1. ++ ** simplify to constants 0 (false) and 1 (true), respectively. ++ ** ++ ** Except, do not apply this optimization if expr1 contains a function ++ ** because that function might be an aggregate (we don't know yet whether ++ ** it is or not) and if it is an aggregate, that could change the meaning ++ ** of the whole query. + */ +- sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202); +- yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0"); +- }else if( yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){ +- Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr; +- yymsp[-1].minor.yy242->a[0].pExpr = 0; +- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); +- pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); +- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS); +- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); ++ Expr *pB = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy502 ? "true" : "false"); ++ if( pB ) sqlite3ExprIdToTrueFalse(pB); ++ if( !ExprHasProperty(yymsp[-4].minor.yy590, EP_HasFunc) ){ ++ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy590); ++ yymsp[-4].minor.yy590 = pB; ++ }else{ ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, yymsp[-3].minor.yy502 ? TK_OR : TK_AND, pB, yymsp[-4].minor.yy590); ++ } + }else{ +- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); +- if( yymsp[-4].minor.yy202 ){ +- yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy242; +- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202); ++ Expr *pRHS = yymsp[-1].minor.yy402->a[0].pExpr; ++ if( yymsp[-1].minor.yy402->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy590->op!=TK_VECTOR ){ ++ yymsp[-1].minor.yy402->a[0].pExpr = 0; ++ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); ++ pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy590, pRHS); ++ }else if( yymsp[-1].minor.yy402->nExpr==1 && pRHS->op==TK_SELECT ){ ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); ++ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pRHS->x.pSelect); ++ pRHS->x.pSelect = 0; ++ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); + }else{ +- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); ++ if( yymsp[-4].minor.yy590==0 ){ ++ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); ++ }else if( yymsp[-4].minor.yy590->pLeft->op==TK_VECTOR ){ ++ int nExpr = yymsp[-4].minor.yy590->pLeft->x.pList->nExpr; ++ Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy402); ++ if( pSelectRHS ){ ++ parserDoubleLinkSelect(pParse, pSelectRHS); ++ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelectRHS); ++ } ++ }else{ ++ yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy402; ++ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); ++ } + } +- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); ++ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + } + } + break; +- case 209: /* expr ::= LP select RP */ ++ case 224: /* expr ::= LP select RP */ + { +- yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); +- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy202, yymsp[-1].minor.yy539); ++ yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); ++ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy590, yymsp[-1].minor.yy637); + } + break; +- case 210: /* expr ::= expr in_op LP select RP */ ++ case 225: /* expr ::= expr in_op LP select RP */ + { +- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); +- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, yymsp[-1].minor.yy539); +- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); ++ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, yymsp[-1].minor.yy637); ++ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + } + break; +- case 211: /* expr ::= expr in_op nm dbnm paren_exprlist */ ++ case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */ + { + SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); + Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); +- if( yymsp[0].minor.yy242 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy242); +- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); +- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, pSelect); +- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); ++ if( yymsp[0].minor.yy402 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy402); ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); ++ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelect); ++ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + } + break; +- case 212: /* expr ::= EXISTS LP select RP */ ++ case 227: /* expr ::= EXISTS LP select RP */ + { + Expr *p; +- p = yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); +- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy539); ++ p = yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); ++ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy637); + } + break; +- case 213: /* expr ::= CASE case_operand case_exprlist case_else END */ ++ case 228: /* expr ::= CASE case_operand case_exprlist case_else END */ + { +- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy202, 0); +- if( yymsp[-4].minor.yy202 ){ +- yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy202 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[-1].minor.yy202) : yymsp[-2].minor.yy242; +- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202); ++ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy590, 0); ++ if( yymsp[-4].minor.yy590 ){ ++ yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy590 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590) : yymsp[-2].minor.yy402; ++ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); + }else{ +- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy242); +- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy202); ++ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy402); ++ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); + } + } + break; +- case 214: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ ++ case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + { +- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[-2].minor.yy202); +- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[0].minor.yy202); ++ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); ++ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[0].minor.yy590); + } + break; +- case 215: /* case_exprlist ::= WHEN expr THEN expr */ ++ case 230: /* case_exprlist ::= WHEN expr THEN expr */ + { +- yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); +- yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy242, yymsp[0].minor.yy202); ++ yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); ++ yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy402, yymsp[0].minor.yy590); + } + break; +- case 218: /* case_operand ::= expr */ +-{yymsp[0].minor.yy202 = yymsp[0].minor.yy202; /*A-overwrites-X*/} +- break; +- case 221: /* nexprlist ::= nexprlist COMMA expr */ +-{yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[0].minor.yy202);} ++ case 235: /* nexprlist ::= nexprlist COMMA expr */ ++{yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[0].minor.yy590);} + break; +- case 222: /* nexprlist ::= expr */ +-{yymsp[0].minor.yy242 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy202); /*A-overwrites-Y*/} ++ case 236: /* nexprlist ::= expr */ ++{yymsp[0].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy590); /*A-overwrites-Y*/} + break; +- case 224: /* paren_exprlist ::= LP exprlist RP */ +- case 229: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==229); +-{yymsp[-2].minor.yy242 = yymsp[-1].minor.yy242;} ++ case 238: /* paren_exprlist ::= LP exprlist RP */ ++ case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243); ++{yymsp[-2].minor.yy402 = yymsp[-1].minor.yy402;} + break; +- case 225: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ ++ case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + { + sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, +- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy242, yymsp[-10].minor.yy192, +- &yymsp[-11].minor.yy0, yymsp[0].minor.yy202, SQLITE_SO_ASC, yymsp[-8].minor.yy192, SQLITE_IDXTYPE_APPDEF); ++ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy402, yymsp[-10].minor.yy502, ++ &yymsp[-11].minor.yy0, yymsp[0].minor.yy590, SQLITE_SO_ASC, yymsp[-8].minor.yy502, SQLITE_IDXTYPE_APPDEF); + if( IN_RENAME_OBJECT && pParse->pNewIndex ){ + sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); + } + } + break; +- case 226: /* uniqueflag ::= UNIQUE */ +- case 268: /* raisetype ::= ABORT */ yytestcase(yyruleno==268); +-{yymsp[0].minor.yy192 = OE_Abort;} ++ case 240: /* uniqueflag ::= UNIQUE */ ++ case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282); ++{yymsp[0].minor.yy502 = OE_Abort;} + break; +- case 227: /* uniqueflag ::= */ +-{yymsp[1].minor.yy192 = OE_None;} ++ case 241: /* uniqueflag ::= */ ++{yymsp[1].minor.yy502 = OE_None;} + break; +- case 230: /* eidlist ::= eidlist COMMA nm collate sortorder */ ++ case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */ + { +- yymsp[-4].minor.yy242 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); ++ yymsp[-4].minor.yy402 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); + } + break; +- case 231: /* eidlist ::= nm collate sortorder */ ++ case 245: /* eidlist ::= nm collate sortorder */ + { +- yymsp[-2].minor.yy242 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); /*A-overwrites-Y*/ ++ yymsp[-2].minor.yy402 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); /*A-overwrites-Y*/ + } + break; +- case 234: /* cmd ::= DROP INDEX ifexists fullname */ +-{sqlite3DropIndex(pParse, yymsp[0].minor.yy47, yymsp[-1].minor.yy192);} ++ case 248: /* cmd ::= DROP INDEX ifexists fullname */ ++{sqlite3DropIndex(pParse, yymsp[0].minor.yy563, yymsp[-1].minor.yy502);} + break; +- case 235: /* cmd ::= VACUUM vinto */ +-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy202);} ++ case 249: /* cmd ::= VACUUM vinto */ ++{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy590);} + break; +- case 236: /* cmd ::= VACUUM nm vinto */ +-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy202);} ++ case 250: /* cmd ::= VACUUM nm vinto */ ++{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy590);} + break; +- case 239: /* cmd ::= PRAGMA nm dbnm */ ++ case 253: /* cmd ::= PRAGMA nm dbnm */ + {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} + break; +- case 240: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ ++ case 254: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} + break; +- case 241: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ ++ case 255: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} + break; +- case 242: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ ++ case 256: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} + break; +- case 243: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ ++ case 257: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} + break; +- case 246: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ ++ case 260: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + { + Token all; + all.z = yymsp[-3].minor.yy0.z; + all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; +- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy447, &all); ++ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy319, &all); + } + break; +- case 247: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ ++ case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + { +- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy192, yymsp[-4].minor.yy230.a, yymsp[-4].minor.yy230.b, yymsp[-2].minor.yy47, yymsp[0].minor.yy202, yymsp[-10].minor.yy192, yymsp[-8].minor.yy192); ++ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy502, yymsp[-4].minor.yy28.a, yymsp[-4].minor.yy28.b, yymsp[-2].minor.yy563, yymsp[0].minor.yy590, yymsp[-10].minor.yy502, yymsp[-8].minor.yy502); + yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ ++#ifdef SQLITE_DEBUG ++ assert( pParse->isCreate ); /* Set by createkw reduce action */ ++ pParse->isCreate = 0; /* But, should not be set for CREATE TRIGGER */ ++#endif + } + break; +- case 248: /* trigger_time ::= BEFORE|AFTER */ +-{ yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/ } ++ case 262: /* trigger_time ::= BEFORE|AFTER */ ++{ yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/ } + break; +- case 249: /* trigger_time ::= INSTEAD OF */ +-{ yymsp[-1].minor.yy192 = TK_INSTEAD;} ++ case 263: /* trigger_time ::= INSTEAD OF */ ++{ yymsp[-1].minor.yy502 = TK_INSTEAD;} + break; +- case 250: /* trigger_time ::= */ +-{ yymsp[1].minor.yy192 = TK_BEFORE; } ++ case 264: /* trigger_time ::= */ ++{ yymsp[1].minor.yy502 = TK_BEFORE; } + break; +- case 251: /* trigger_event ::= DELETE|INSERT */ +- case 252: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==252); +-{yymsp[0].minor.yy230.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy230.b = 0;} ++ case 265: /* trigger_event ::= DELETE|INSERT */ ++ case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266); ++{yymsp[0].minor.yy28.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy28.b = 0;} + break; +- case 253: /* trigger_event ::= UPDATE OF idlist */ +-{yymsp[-2].minor.yy230.a = TK_UPDATE; yymsp[-2].minor.yy230.b = yymsp[0].minor.yy600;} ++ case 267: /* trigger_event ::= UPDATE OF idlist */ ++{yymsp[-2].minor.yy28.a = TK_UPDATE; yymsp[-2].minor.yy28.b = yymsp[0].minor.yy204;} + break; +- case 254: /* when_clause ::= */ +- case 273: /* key_opt ::= */ yytestcase(yyruleno==273); +-{ yymsp[1].minor.yy202 = 0; } ++ case 268: /* when_clause ::= */ ++ case 287: /* key_opt ::= */ yytestcase(yyruleno==287); ++{ yymsp[1].minor.yy590 = 0; } + break; +- case 255: /* when_clause ::= WHEN expr */ +- case 274: /* key_opt ::= KEY expr */ yytestcase(yyruleno==274); +-{ yymsp[-1].minor.yy202 = yymsp[0].minor.yy202; } ++ case 269: /* when_clause ::= WHEN expr */ ++ case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288); ++{ yymsp[-1].minor.yy590 = yymsp[0].minor.yy590; } + break; +- case 256: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ ++ case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + { +- assert( yymsp[-2].minor.yy447!=0 ); +- yymsp[-2].minor.yy447->pLast->pNext = yymsp[-1].minor.yy447; +- yymsp[-2].minor.yy447->pLast = yymsp[-1].minor.yy447; ++ assert( yymsp[-2].minor.yy319!=0 ); ++ yymsp[-2].minor.yy319->pLast->pNext = yymsp[-1].minor.yy319; ++ yymsp[-2].minor.yy319->pLast = yymsp[-1].minor.yy319; + } + break; +- case 257: /* trigger_cmd_list ::= trigger_cmd SEMI */ ++ case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */ + { +- assert( yymsp[-1].minor.yy447!=0 ); +- yymsp[-1].minor.yy447->pLast = yymsp[-1].minor.yy447; ++ assert( yymsp[-1].minor.yy319!=0 ); ++ yymsp[-1].minor.yy319->pLast = yymsp[-1].minor.yy319; + } + break; +- case 258: /* trnm ::= nm DOT nm */ ++ case 272: /* trnm ::= nm DOT nm */ + { + yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; + sqlite3ErrorMsg(pParse, +@@ -158621,344 +180154,377 @@ static YYACTIONTYPE yy_reduce( + "statements within triggers"); + } + break; +- case 259: /* tridxby ::= INDEXED BY nm */ ++ case 273: /* tridxby ::= INDEXED BY nm */ + { + sqlite3ErrorMsg(pParse, + "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " + "within triggers"); + } + break; +- case 260: /* tridxby ::= NOT INDEXED */ ++ case 274: /* tridxby ::= NOT INDEXED */ + { + sqlite3ErrorMsg(pParse, + "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " + "within triggers"); + } + break; +- case 261: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +-{yylhsminor.yy447 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy47, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202, yymsp[-7].minor.yy192, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy436);} +- yymsp[-8].minor.yy447 = yylhsminor.yy447; ++ case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ ++{yylhsminor.yy319 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy563, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590, yymsp[-7].minor.yy502, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy342);} ++ yymsp[-8].minor.yy319 = yylhsminor.yy319; + break; +- case 262: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ ++ case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + { +- yylhsminor.yy447 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy600,yymsp[-2].minor.yy539,yymsp[-6].minor.yy192,yymsp[-1].minor.yy318,yymsp[-7].minor.yy436,yymsp[0].minor.yy436);/*yylhsminor.yy447-overwrites-yymsp[-6].minor.yy192*/ ++ yylhsminor.yy319 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy204,yymsp[-2].minor.yy637,yymsp[-6].minor.yy502,yymsp[-1].minor.yy403,yymsp[-7].minor.yy342,yymsp[0].minor.yy342);/*yylhsminor.yy319-overwrites-yymsp[-6].minor.yy502*/ + } +- yymsp[-7].minor.yy447 = yylhsminor.yy447; ++ yymsp[-7].minor.yy319 = yylhsminor.yy319; + break; +- case 263: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +-{yylhsminor.yy447 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy202, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy436);} +- yymsp[-5].minor.yy447 = yylhsminor.yy447; ++ case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ ++{yylhsminor.yy319 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy590, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy342);} ++ yymsp[-5].minor.yy319 = yylhsminor.yy319; + break; +- case 264: /* trigger_cmd ::= scanpt select scanpt */ +-{yylhsminor.yy447 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy539, yymsp[-2].minor.yy436, yymsp[0].minor.yy436); /*yylhsminor.yy447-overwrites-yymsp[-1].minor.yy539*/} +- yymsp[-2].minor.yy447 = yylhsminor.yy447; ++ case 278: /* trigger_cmd ::= scanpt select scanpt */ ++{yylhsminor.yy319 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy637, yymsp[-2].minor.yy342, yymsp[0].minor.yy342); /*yylhsminor.yy319-overwrites-yymsp[-1].minor.yy637*/} ++ yymsp[-2].minor.yy319 = yylhsminor.yy319; + break; +- case 265: /* expr ::= RAISE LP IGNORE RP */ ++ case 279: /* expr ::= RAISE LP IGNORE RP */ + { +- yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); +- if( yymsp[-3].minor.yy202 ){ +- yymsp[-3].minor.yy202->affExpr = OE_Ignore; ++ yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); ++ if( yymsp[-3].minor.yy590 ){ ++ yymsp[-3].minor.yy590->affExpr = OE_Ignore; + } + } + break; +- case 266: /* expr ::= RAISE LP raisetype COMMA nm RP */ ++ case 280: /* expr ::= RAISE LP raisetype COMMA expr RP */ + { +- yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); +- if( yymsp[-5].minor.yy202 ) { +- yymsp[-5].minor.yy202->affExpr = (char)yymsp[-3].minor.yy192; ++ yymsp[-5].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy590, 0); ++ if( yymsp[-5].minor.yy590 ) { ++ yymsp[-5].minor.yy590->affExpr = (char)yymsp[-3].minor.yy502; + } + } + break; +- case 267: /* raisetype ::= ROLLBACK */ +-{yymsp[0].minor.yy192 = OE_Rollback;} ++ case 281: /* raisetype ::= ROLLBACK */ ++{yymsp[0].minor.yy502 = OE_Rollback;} + break; +- case 269: /* raisetype ::= FAIL */ +-{yymsp[0].minor.yy192 = OE_Fail;} ++ case 283: /* raisetype ::= FAIL */ ++{yymsp[0].minor.yy502 = OE_Fail;} + break; +- case 270: /* cmd ::= DROP TRIGGER ifexists fullname */ ++ case 284: /* cmd ::= DROP TRIGGER ifexists fullname */ + { +- sqlite3DropTrigger(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy192); ++ sqlite3DropTrigger(pParse,yymsp[0].minor.yy563,yymsp[-1].minor.yy502); + } + break; +- case 271: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ ++ case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + { +- sqlite3Attach(pParse, yymsp[-3].minor.yy202, yymsp[-1].minor.yy202, yymsp[0].minor.yy202); ++ sqlite3Attach(pParse, yymsp[-3].minor.yy590, yymsp[-1].minor.yy590, yymsp[0].minor.yy590); + } + break; +- case 272: /* cmd ::= DETACH database_kw_opt expr */ ++ case 286: /* cmd ::= DETACH database_kw_opt expr */ + { +- sqlite3Detach(pParse, yymsp[0].minor.yy202); ++ sqlite3Detach(pParse, yymsp[0].minor.yy590); + } + break; +- case 275: /* cmd ::= REINDEX */ ++ case 289: /* cmd ::= REINDEX */ + {sqlite3Reindex(pParse, 0, 0);} + break; +- case 276: /* cmd ::= REINDEX nm dbnm */ ++ case 290: /* cmd ::= REINDEX nm dbnm */ + {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} + break; +- case 277: /* cmd ::= ANALYZE */ ++ case 291: /* cmd ::= ANALYZE */ + {sqlite3Analyze(pParse, 0, 0);} + break; +- case 278: /* cmd ::= ANALYZE nm dbnm */ ++ case 292: /* cmd ::= ANALYZE nm dbnm */ + {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} + break; +- case 279: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ ++ case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + { +- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy47,&yymsp[0].minor.yy0); ++ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy563,&yymsp[0].minor.yy0); + } + break; +- case 280: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ ++ case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + { + yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; + sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); + } + break; +- case 281: /* add_column_fullname ::= fullname */ ++ case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ ++{ ++ sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy563, &yymsp[0].minor.yy0); ++} ++ break; ++ case 296: /* add_column_fullname ::= fullname */ + { + disableLookaside(pParse); +- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy47); ++ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy563); + } + break; +- case 282: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ ++ case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + { +- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy47, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); ++ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy563, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + } + break; +- case 283: /* cmd ::= create_vtab */ ++ case 298: /* cmd ::= create_vtab */ + {sqlite3VtabFinishParse(pParse,0);} + break; +- case 284: /* cmd ::= create_vtab LP vtabarglist RP */ ++ case 299: /* cmd ::= create_vtab LP vtabarglist RP */ + {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} + break; +- case 285: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ ++ case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + { +- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy192); ++ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy502); + } + break; +- case 286: /* vtabarg ::= */ ++ case 301: /* vtabarg ::= */ + {sqlite3VtabArgInit(pParse);} + break; +- case 287: /* vtabargtoken ::= ANY */ +- case 288: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==288); +- case 289: /* lp ::= LP */ yytestcase(yyruleno==289); ++ case 302: /* vtabargtoken ::= ANY */ ++ case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303); ++ case 304: /* lp ::= LP */ yytestcase(yyruleno==304); + {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} + break; +- case 290: /* with ::= WITH wqlist */ +- case 291: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==291); +-{ sqlite3WithPush(pParse, yymsp[0].minor.yy131, 1); } ++ case 305: /* with ::= WITH wqlist */ ++ case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306); ++{ sqlite3WithPush(pParse, yymsp[0].minor.yy125, 1); } ++ break; ++ case 307: /* wqas ::= AS */ ++{yymsp[0].minor.yy444 = M10d_Any;} + break; +- case 292: /* wqlist ::= nm eidlist_opt AS LP select RP */ ++ case 308: /* wqas ::= AS MATERIALIZED */ ++{yymsp[-1].minor.yy444 = M10d_Yes;} ++ break; ++ case 309: /* wqas ::= AS NOT MATERIALIZED */ ++{yymsp[-2].minor.yy444 = M10d_No;} ++ break; ++ case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */ + { +- yymsp[-5].minor.yy131 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); /*A-overwrites-X*/ ++ yymsp[-5].minor.yy361 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy402, yymsp[-1].minor.yy637, yymsp[-3].minor.yy444); /*A-overwrites-X*/ + } + break; +- case 293: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ ++ case 311: /* withnm ::= nm */ ++{pParse->bHasWith = 1;} ++ break; ++ case 312: /* wqlist ::= wqitem */ + { +- yymsp[-7].minor.yy131 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy131, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); ++ yymsp[0].minor.yy125 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy361); /*A-overwrites-X*/ + } + break; +- case 294: /* windowdefn_list ::= windowdefn */ +-{ yylhsminor.yy303 = yymsp[0].minor.yy303; } +- yymsp[0].minor.yy303 = yylhsminor.yy303; +- break; +- case 295: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ ++ case 313: /* wqlist ::= wqlist COMMA wqitem */ + { +- assert( yymsp[0].minor.yy303!=0 ); +- sqlite3WindowChain(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy303); +- yymsp[0].minor.yy303->pNextWin = yymsp[-2].minor.yy303; +- yylhsminor.yy303 = yymsp[0].minor.yy303; ++ yymsp[-2].minor.yy125 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy125, yymsp[0].minor.yy361); + } +- yymsp[-2].minor.yy303 = yylhsminor.yy303; + break; +- case 296: /* windowdefn ::= nm AS LP window RP */ ++ case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + { +- if( ALWAYS(yymsp[-1].minor.yy303) ){ +- yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); +- } +- yylhsminor.yy303 = yymsp[-1].minor.yy303; ++ assert( yymsp[0].minor.yy483!=0 ); ++ sqlite3WindowChain(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy483); ++ yymsp[0].minor.yy483->pNextWin = yymsp[-2].minor.yy483; ++ yylhsminor.yy483 = yymsp[0].minor.yy483; + } +- yymsp[-4].minor.yy303 = yylhsminor.yy303; ++ yymsp[-2].minor.yy483 = yylhsminor.yy483; + break; +- case 297: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ ++ case 315: /* windowdefn ::= nm AS LP window RP */ + { +- yymsp[-4].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, 0); ++ if( ALWAYS(yymsp[-1].minor.yy483) ){ ++ yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); ++ } ++ yylhsminor.yy483 = yymsp[-1].minor.yy483; + } ++ yymsp[-4].minor.yy483 = yylhsminor.yy483; + break; +- case 298: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ ++ case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + { +- yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, &yymsp[-5].minor.yy0); ++ yymsp[-4].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, 0); + } +- yymsp[-5].minor.yy303 = yylhsminor.yy303; + break; +- case 299: /* window ::= ORDER BY sortlist frame_opt */ ++ case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + { +- yymsp[-3].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, 0); ++ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, &yymsp[-5].minor.yy0); + } ++ yymsp[-5].minor.yy483 = yylhsminor.yy483; + break; +- case 300: /* window ::= nm ORDER BY sortlist frame_opt */ ++ case 318: /* window ::= ORDER BY sortlist frame_opt */ + { +- yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0); ++ yymsp[-3].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, 0); + } +- yymsp[-4].minor.yy303 = yylhsminor.yy303; + break; +- case 301: /* window ::= frame_opt */ +- case 320: /* filter_over ::= over_clause */ yytestcase(yyruleno==320); ++ case 319: /* window ::= nm ORDER BY sortlist frame_opt */ + { +- yylhsminor.yy303 = yymsp[0].minor.yy303; ++ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0); + } +- yymsp[0].minor.yy303 = yylhsminor.yy303; ++ yymsp[-4].minor.yy483 = yylhsminor.yy483; + break; +- case 302: /* window ::= nm frame_opt */ ++ case 320: /* window ::= nm frame_opt */ + { +- yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, 0, &yymsp[-1].minor.yy0); ++ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, 0, &yymsp[-1].minor.yy0); + } +- yymsp[-1].minor.yy303 = yylhsminor.yy303; ++ yymsp[-1].minor.yy483 = yylhsminor.yy483; + break; +- case 303: /* frame_opt ::= */ ++ case 321: /* frame_opt ::= */ + { +- yymsp[1].minor.yy303 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); ++ yymsp[1].minor.yy483 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + } + break; +- case 304: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ ++ case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + { +- yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy192, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy58); ++ yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy502, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy444); + } +- yymsp[-2].minor.yy303 = yylhsminor.yy303; ++ yymsp[-2].minor.yy483 = yylhsminor.yy483; + break; +- case 305: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ ++ case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + { +- yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy192, yymsp[-3].minor.yy77.eType, yymsp[-3].minor.yy77.pExpr, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, yymsp[0].minor.yy58); ++ yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy502, yymsp[-3].minor.yy205.eType, yymsp[-3].minor.yy205.pExpr, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, yymsp[0].minor.yy444); + } +- yymsp[-5].minor.yy303 = yylhsminor.yy303; ++ yymsp[-5].minor.yy483 = yylhsminor.yy483; + break; +- case 307: /* frame_bound_s ::= frame_bound */ +- case 309: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==309); +-{yylhsminor.yy77 = yymsp[0].minor.yy77;} +- yymsp[0].minor.yy77 = yylhsminor.yy77; ++ case 325: /* frame_bound_s ::= frame_bound */ ++ case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327); ++{yylhsminor.yy205 = yymsp[0].minor.yy205;} ++ yymsp[0].minor.yy205 = yylhsminor.yy205; + break; +- case 308: /* frame_bound_s ::= UNBOUNDED PRECEDING */ +- case 310: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==310); +- case 312: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==312); +-{yylhsminor.yy77.eType = yymsp[-1].major; yylhsminor.yy77.pExpr = 0;} +- yymsp[-1].minor.yy77 = yylhsminor.yy77; ++ case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */ ++ case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328); ++ case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330); ++{yylhsminor.yy205.eType = yymsp[-1].major; yylhsminor.yy205.pExpr = 0;} ++ yymsp[-1].minor.yy205 = yylhsminor.yy205; + break; +- case 311: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +-{yylhsminor.yy77.eType = yymsp[0].major; yylhsminor.yy77.pExpr = yymsp[-1].minor.yy202;} +- yymsp[-1].minor.yy77 = yylhsminor.yy77; ++ case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */ ++{yylhsminor.yy205.eType = yymsp[0].major; yylhsminor.yy205.pExpr = yymsp[-1].minor.yy590;} ++ yymsp[-1].minor.yy205 = yylhsminor.yy205; + break; +- case 313: /* frame_exclude_opt ::= */ +-{yymsp[1].minor.yy58 = 0;} ++ case 331: /* frame_exclude_opt ::= */ ++{yymsp[1].minor.yy444 = 0;} + break; +- case 314: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +-{yymsp[-1].minor.yy58 = yymsp[0].minor.yy58;} ++ case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ ++{yymsp[-1].minor.yy444 = yymsp[0].minor.yy444;} + break; +- case 315: /* frame_exclude ::= NO OTHERS */ +- case 316: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==316); +-{yymsp[-1].minor.yy58 = yymsp[-1].major; /*A-overwrites-X*/} ++ case 333: /* frame_exclude ::= NO OTHERS */ ++ case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334); ++{yymsp[-1].minor.yy444 = yymsp[-1].major; /*A-overwrites-X*/} + break; +- case 317: /* frame_exclude ::= GROUP|TIES */ +-{yymsp[0].minor.yy58 = yymsp[0].major; /*A-overwrites-X*/} ++ case 335: /* frame_exclude ::= GROUP|TIES */ ++{yymsp[0].minor.yy444 = yymsp[0].major; /*A-overwrites-X*/} + break; +- case 318: /* window_clause ::= WINDOW windowdefn_list */ +-{ yymsp[-1].minor.yy303 = yymsp[0].minor.yy303; } ++ case 336: /* window_clause ::= WINDOW windowdefn_list */ ++{ yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; } + break; +- case 319: /* filter_over ::= filter_clause over_clause */ ++ case 337: /* filter_over ::= filter_clause over_clause */ + { +- yymsp[0].minor.yy303->pFilter = yymsp[-1].minor.yy202; +- yylhsminor.yy303 = yymsp[0].minor.yy303; ++ if( yymsp[0].minor.yy483 ){ ++ yymsp[0].minor.yy483->pFilter = yymsp[-1].minor.yy590; ++ }else{ ++ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); ++ } ++ yylhsminor.yy483 = yymsp[0].minor.yy483; + } +- yymsp[-1].minor.yy303 = yylhsminor.yy303; ++ yymsp[-1].minor.yy483 = yylhsminor.yy483; + break; +- case 321: /* filter_over ::= filter_clause */ ++ case 338: /* filter_over ::= over_clause */ + { +- yylhsminor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); +- if( yylhsminor.yy303 ){ +- yylhsminor.yy303->eFrmType = TK_FILTER; +- yylhsminor.yy303->pFilter = yymsp[0].minor.yy202; ++ yylhsminor.yy483 = yymsp[0].minor.yy483; ++} ++ yymsp[0].minor.yy483 = yylhsminor.yy483; ++ break; ++ case 339: /* filter_over ::= filter_clause */ ++{ ++ yylhsminor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); ++ if( yylhsminor.yy483 ){ ++ yylhsminor.yy483->eFrmType = TK_FILTER; ++ yylhsminor.yy483->pFilter = yymsp[0].minor.yy590; + }else{ +- sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy202); ++ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy590); + } + } +- yymsp[0].minor.yy303 = yylhsminor.yy303; ++ yymsp[0].minor.yy483 = yylhsminor.yy483; + break; +- case 322: /* over_clause ::= OVER LP window RP */ ++ case 340: /* over_clause ::= OVER LP window RP */ + { +- yymsp[-3].minor.yy303 = yymsp[-1].minor.yy303; +- assert( yymsp[-3].minor.yy303!=0 ); ++ yymsp[-3].minor.yy483 = yymsp[-1].minor.yy483; ++ assert( yymsp[-3].minor.yy483!=0 ); + } + break; +- case 323: /* over_clause ::= OVER nm */ ++ case 341: /* over_clause ::= OVER nm */ + { +- yymsp[-1].minor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); +- if( yymsp[-1].minor.yy303 ){ +- yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); ++ yymsp[-1].minor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); ++ if( yymsp[-1].minor.yy483 ){ ++ yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + } + } + break; +- case 324: /* filter_clause ::= FILTER LP WHERE expr RP */ +-{ yymsp[-4].minor.yy202 = yymsp[-1].minor.yy202; } ++ case 342: /* filter_clause ::= FILTER LP WHERE expr RP */ ++{ yymsp[-4].minor.yy590 = yymsp[-1].minor.yy590; } ++ break; ++ case 343: /* term ::= QNUMBER */ ++{ ++ yylhsminor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); ++ sqlite3DequoteNumber(pParse, yylhsminor.yy590); ++} ++ yymsp[0].minor.yy590 = yylhsminor.yy590; + break; + default: +- /* (325) input ::= cmdlist */ yytestcase(yyruleno==325); +- /* (326) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==326); +- /* (327) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=327); +- /* (328) ecmd ::= SEMI */ yytestcase(yyruleno==328); +- /* (329) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==329); +- /* (330) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=330); +- /* (331) trans_opt ::= */ yytestcase(yyruleno==331); +- /* (332) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==332); +- /* (333) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==333); +- /* (334) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==334); +- /* (335) savepoint_opt ::= */ yytestcase(yyruleno==335); +- /* (336) cmd ::= create_table create_table_args */ yytestcase(yyruleno==336); +- /* (337) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==337); +- /* (338) columnlist ::= columnname carglist */ yytestcase(yyruleno==338); +- /* (339) nm ::= ID|INDEXED */ yytestcase(yyruleno==339); +- /* (340) nm ::= STRING */ yytestcase(yyruleno==340); +- /* (341) nm ::= JOIN_KW */ yytestcase(yyruleno==341); +- /* (342) typetoken ::= typename */ yytestcase(yyruleno==342); +- /* (343) typename ::= ID|STRING */ yytestcase(yyruleno==343); +- /* (344) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=344); +- /* (345) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=345); +- /* (346) carglist ::= carglist ccons */ yytestcase(yyruleno==346); +- /* (347) carglist ::= */ yytestcase(yyruleno==347); +- /* (348) ccons ::= NULL onconf */ yytestcase(yyruleno==348); +- /* (349) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==349); +- /* (350) ccons ::= AS generated */ yytestcase(yyruleno==350); +- /* (351) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==351); +- /* (352) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==352); +- /* (353) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=353); +- /* (354) tconscomma ::= */ yytestcase(yyruleno==354); +- /* (355) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=355); +- /* (356) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=356); +- /* (357) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=357); +- /* (358) oneselect ::= values */ yytestcase(yyruleno==358); +- /* (359) sclp ::= selcollist COMMA */ yytestcase(yyruleno==359); +- /* (360) as ::= ID|STRING */ yytestcase(yyruleno==360); +- /* (361) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=361); +- /* (362) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==362); +- /* (363) exprlist ::= nexprlist */ yytestcase(yyruleno==363); +- /* (364) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); +- /* (365) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=365); +- /* (366) nmnum ::= ON */ yytestcase(yyruleno==366); +- /* (367) nmnum ::= DELETE */ yytestcase(yyruleno==367); +- /* (368) nmnum ::= DEFAULT */ yytestcase(yyruleno==368); +- /* (369) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==369); +- /* (370) foreach_clause ::= */ yytestcase(yyruleno==370); +- /* (371) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==371); +- /* (372) trnm ::= nm */ yytestcase(yyruleno==372); +- /* (373) tridxby ::= */ yytestcase(yyruleno==373); +- /* (374) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==374); +- /* (375) database_kw_opt ::= */ yytestcase(yyruleno==375); +- /* (376) kwcolumn_opt ::= */ yytestcase(yyruleno==376); +- /* (377) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==377); +- /* (378) vtabarglist ::= vtabarg */ yytestcase(yyruleno==378); +- /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==379); +- /* (380) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==380); +- /* (381) anylist ::= */ yytestcase(yyruleno==381); +- /* (382) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==382); +- /* (383) anylist ::= anylist ANY */ yytestcase(yyruleno==383); +- /* (384) with ::= */ yytestcase(yyruleno==384); ++ /* (344) input ::= cmdlist */ yytestcase(yyruleno==344); ++ /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345); ++ /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346); ++ /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347); ++ /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348); ++ /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349); ++ /* (350) trans_opt ::= */ yytestcase(yyruleno==350); ++ /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351); ++ /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352); ++ /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353); ++ /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354); ++ /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355); ++ /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356); ++ /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357); ++ /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358); ++ /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359); ++ /* (360) nm ::= STRING */ yytestcase(yyruleno==360); ++ /* (361) typetoken ::= typename */ yytestcase(yyruleno==361); ++ /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362); ++ /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); ++ /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); ++ /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365); ++ /* (366) carglist ::= */ yytestcase(yyruleno==366); ++ /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367); ++ /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368); ++ /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369); ++ /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370); ++ /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371); ++ /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372); ++ /* (373) tconscomma ::= */ yytestcase(yyruleno==373); ++ /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374); ++ /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375); ++ /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376); ++ /* (377) oneselect ::= values */ yytestcase(yyruleno==377); ++ /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378); ++ /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379); ++ /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380); ++ /* (381) returning ::= */ yytestcase(yyruleno==381); ++ /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382); ++ /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383); ++ /* (384) case_operand ::= expr */ yytestcase(yyruleno==384); ++ /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385); ++ /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386); ++ /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387); ++ /* (388) nmnum ::= ON */ yytestcase(yyruleno==388); ++ /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389); ++ /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390); ++ /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391); ++ /* (392) foreach_clause ::= */ yytestcase(yyruleno==392); ++ /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393); ++ /* (394) trnm ::= nm */ yytestcase(yyruleno==394); ++ /* (395) tridxby ::= */ yytestcase(yyruleno==395); ++ /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396); ++ /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397); ++ /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398); ++ /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399); ++ /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400); ++ /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401); ++ /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402); ++ /* (403) anylist ::= */ yytestcase(yyruleno==403); ++ /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404); ++ /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405); ++ /* (406) with ::= */ yytestcase(yyruleno==406); ++ /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407); ++ /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408); + break; + /********** End reduce actions ************************************************/ + }; +@@ -159021,7 +180587,7 @@ static void yy_syntax_error( + + UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ + if( TOKEN.z[0] ){ +- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); ++ parserSyntaxError(pParse, &TOKEN); + }else{ + sqlite3ErrorMsg(pParse, "incomplete input"); + } +@@ -159110,12 +180676,49 @@ SQLITE_PRIVATE void sqlite3Parser( + } + #endif + +- do{ ++ while(1){ /* Exit by "break" */ ++ assert( yypParser->yytos>=yypParser->yystack ); + assert( yyact==yypParser->yytos->stateno ); + yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); + if( yyact >= YY_MIN_REDUCE ){ +- yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor, +- yyminor sqlite3ParserCTX_PARAM); ++ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ ++#ifndef NDEBUG ++ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); ++ if( yyTraceFILE ){ ++ int yysize = yyRuleInfoNRhs[yyruleno]; ++ if( yysize ){ ++ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", ++ yyTracePrompt, ++ yyruleno, yyRuleName[yyruleno], ++ yyrulenoyytos[yysize].stateno); ++ }else{ ++ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", ++ yyTracePrompt, yyruleno, yyRuleName[yyruleno], ++ yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ ++ yypParser->yyhwm++; ++ assert( yypParser->yyhwm == ++ (int)(yypParser->yytos - yypParser->yystack)); ++ } ++#endif ++ if( yypParser->yytos>=yypParser->yystackEnd ){ ++ if( yyGrowStack(yypParser) ){ ++ yyStackOverflow(yypParser); ++ break; ++ } ++ } ++ } ++ yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM); + }else if( yyact <= YY_MAX_SHIFTREDUCE ){ + yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); + #ifndef YYNOERRORRECOVERY +@@ -159171,14 +180774,13 @@ SQLITE_PRIVATE void sqlite3Parser( + yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); + yymajor = YYNOCODE; + }else{ +- while( yypParser->yytos >= yypParser->yystack +- && (yyact = yy_find_reduce_action( +- yypParser->yytos->stateno, +- YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE +- ){ ++ while( yypParser->yytos > yypParser->yystack ){ ++ yyact = yy_find_reduce_action(yypParser->yytos->stateno, ++ YYERRORSYMBOL); ++ if( yyact<=YY_MAX_SHIFTREDUCE ) break; + yy_pop_parser_stack(yypParser); + } +- if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ ++ if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + #ifndef YYNOERRORRECOVERY +@@ -159228,7 +180830,7 @@ SQLITE_PRIVATE void sqlite3Parser( + break; + #endif + } +- }while( yypParser->yytos>yypParser->yystack ); ++ } + #ifndef NDEBUG + if( yyTraceFILE ){ + yyStackEntry *i; +@@ -159289,8 +180891,8 @@ SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){ + ** all of them need to be used within the switch. + */ + #define CC_X 0 /* The letter 'x', or start of BLOB literal */ +-#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */ +-#define CC_ID 2 /* unicode characters usable in IDs */ ++#define CC_KYWD0 1 /* First letter of a keyword */ ++#define CC_KYWD 2 /* Alphabetics or '_'. Usable in a keyword */ + #define CC_DIGIT 3 /* Digits */ + #define CC_DOLLAR 4 /* '$' */ + #define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */ +@@ -159315,47 +180917,49 @@ SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){ + #define CC_AND 24 /* '&' */ + #define CC_TILDA 25 /* '~' */ + #define CC_DOT 26 /* '.' */ +-#define CC_ILLEGAL 27 /* Illegal character */ +-#define CC_NUL 28 /* 0x00 */ ++#define CC_ID 27 /* unicode characters usable in IDs */ ++#define CC_ILLEGAL 28 /* Illegal character */ ++#define CC_NUL 29 /* 0x00 */ ++#define CC_BOM 30 /* First byte of UTF8 BOM: 0xEF 0xBB 0xBF */ + + static const unsigned char aiClass[] = { + #ifdef SQLITE_ASCII + /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ +-/* 0x */ 28, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27, +-/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++/* 0x */ 29, 28, 28, 28, 28, 28, 28, 28, 28, 7, 7, 28, 7, 7, 28, 28, ++/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + /* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16, + /* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6, + /* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +-/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1, ++/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 9, 28, 28, 28, 2, + /* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +-/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27, +-/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +-/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +-/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +-/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +-/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +-/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +-/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +-/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ++/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 28, 10, 28, 25, 28, ++/* 8x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++/* 9x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++/* Ax */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++/* Cx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++/* Dx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++/* Ex */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 30, ++/* Fx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27 + #endif + #ifdef SQLITE_EBCDIC + /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ +-/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27, +-/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +-/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +-/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +-/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 12, 17, 20, 10, +-/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27, +-/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 6, +-/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8, +-/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, +-/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, +-/* Ax */ 27, 25, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, +-/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27, +-/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, +-/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, +-/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, +-/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27, ++/* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28, ++/* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, ++/* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, ++/* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, ++/* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10, ++/* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28, ++/* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6, ++/* 7x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 8, 5, 5, 5, 8, 14, 8, ++/* 8x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, ++/* 9x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, ++/* Ax */ 28, 25, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28, ++/* Bx */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 9, 28, 28, 28, 28, 28, ++/* Cx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, ++/* Dx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, ++/* Ex */ 28, 28, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28, ++/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 28, 28, 28, 28, 28, 28, + #endif + }; + +@@ -159420,20 +181024,21 @@ const unsigned char ebcdicToAscii[] = { + ** is substantially reduced. This is important for embedded applications + ** on platforms with limited memory. + */ +-/* Hash score: 227 */ +-/* zKWText[] encodes 984 bytes of keyword text in 648 bytes */ ++/* Hash score: 231 */ ++/* zKWText[] encodes 1007 bytes of keyword text in 667 bytes */ + /* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */ + /* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYISNULLSAVEPOINTERSECT */ + /* IESNOTNULLIKEXCEPTRANSACTIONATURALTERAISEXCLUSIVEXISTS */ + /* CONSTRAINTOFFSETRIGGERANGENERATEDETACHAVINGLOBEGINNEREFERENCES */ + /* UNIQUERYWITHOUTERELEASEATTACHBETWEENOTHINGROUPSCASCADEFAULT */ + /* CASECOLLATECREATECURRENT_DATEIMMEDIATEJOINSERTMATCHPLANALYZE */ +-/* PRAGMABORTUPDATEVALUESVIRTUALWAYSWHENWHERECURSIVEAFTERENAMEAND */ +-/* EFERREDISTINCTAUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSS */ +-/* CURRENT_TIMESTAMPARTITIONDROPRECEDINGFAILASTFILTEREPLACEFIRST */ +-/* FOLLOWINGFROMFULLIMITIFORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */ +-/* UNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBYINITIALLYPRIMARY */ +-static const char zKWText[647] = { ++/* PRAGMATERIALIZEDEFERREDISTINCTUPDATEVALUESVIRTUALWAYSWHENWHERE */ ++/* CURSIVEABORTAFTERENAMEANDROPARTITIONAUTOINCREMENTCASTCOLUMN */ ++/* COMMITCONFLICTCROSSCURRENT_TIMESTAMPRECEDINGFAILASTFILTER */ ++/* EPLACEFIRSTFOLLOWINGFROMFULLIMITIFORDERESTRICTOTHERSOVER */ ++/* ETURNINGRIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBY */ ++/* INITIALLYPRIMARY */ ++static const char zKWText[666] = { + 'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H', + 'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G', + 'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A', +@@ -159454,86 +181059,87 @@ static const char zKWText[647] = { + 'C','R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E', + 'I','M','M','E','D','I','A','T','E','J','O','I','N','S','E','R','T','M', + 'A','T','C','H','P','L','A','N','A','L','Y','Z','E','P','R','A','G','M', +- 'A','B','O','R','T','U','P','D','A','T','E','V','A','L','U','E','S','V', +- 'I','R','T','U','A','L','W','A','Y','S','W','H','E','N','W','H','E','R', +- 'E','C','U','R','S','I','V','E','A','F','T','E','R','E','N','A','M','E', +- 'A','N','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','A', +- 'U','T','O','I','N','C','R','E','M','E','N','T','C','A','S','T','C','O', +- 'L','U','M','N','C','O','M','M','I','T','C','O','N','F','L','I','C','T', +- 'C','R','O','S','S','C','U','R','R','E','N','T','_','T','I','M','E','S', +- 'T','A','M','P','A','R','T','I','T','I','O','N','D','R','O','P','R','E', +- 'C','E','D','I','N','G','F','A','I','L','A','S','T','F','I','L','T','E', +- 'R','E','P','L','A','C','E','F','I','R','S','T','F','O','L','L','O','W', +- 'I','N','G','F','R','O','M','F','U','L','L','I','M','I','T','I','F','O', +- 'R','D','E','R','E','S','T','R','I','C','T','O','T','H','E','R','S','O', +- 'V','E','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O','W', +- 'S','U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S','I', +- 'N','G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W','B', +- 'Y','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y', ++ 'A','T','E','R','I','A','L','I','Z','E','D','E','F','E','R','R','E','D', ++ 'I','S','T','I','N','C','T','U','P','D','A','T','E','V','A','L','U','E', ++ 'S','V','I','R','T','U','A','L','W','A','Y','S','W','H','E','N','W','H', ++ 'E','R','E','C','U','R','S','I','V','E','A','B','O','R','T','A','F','T', ++ 'E','R','E','N','A','M','E','A','N','D','R','O','P','A','R','T','I','T', ++ 'I','O','N','A','U','T','O','I','N','C','R','E','M','E','N','T','C','A', ++ 'S','T','C','O','L','U','M','N','C','O','M','M','I','T','C','O','N','F', ++ 'L','I','C','T','C','R','O','S','S','C','U','R','R','E','N','T','_','T', ++ 'I','M','E','S','T','A','M','P','R','E','C','E','D','I','N','G','F','A', ++ 'I','L','A','S','T','F','I','L','T','E','R','E','P','L','A','C','E','F', ++ 'I','R','S','T','F','O','L','L','O','W','I','N','G','F','R','O','M','F', ++ 'U','L','L','I','M','I','T','I','F','O','R','D','E','R','E','S','T','R', ++ 'I','C','T','O','T','H','E','R','S','O','V','E','R','E','T','U','R','N', ++ 'I','N','G','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O', ++ 'W','S','U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S', ++ 'I','N','G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W', ++ 'B','Y','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y', + }; + /* aKWHash[i] is the hash value for the i-th keyword */ + static const unsigned char aKWHash[127] = { +- 84, 102, 132, 82, 114, 29, 0, 0, 91, 0, 85, 72, 0, +- 53, 35, 86, 15, 0, 42, 94, 54, 126, 133, 19, 0, 0, +- 138, 0, 40, 128, 0, 22, 104, 0, 9, 0, 0, 122, 80, +- 0, 78, 6, 0, 65, 99, 145, 0, 134, 112, 0, 0, 48, +- 0, 100, 24, 0, 17, 0, 27, 70, 23, 26, 5, 60, 140, +- 107, 121, 0, 73, 101, 71, 143, 61, 119, 74, 0, 49, 0, +- 11, 41, 0, 110, 0, 0, 0, 106, 10, 108, 113, 124, 14, +- 50, 123, 0, 89, 0, 18, 120, 142, 56, 129, 137, 88, 83, +- 37, 30, 125, 0, 0, 105, 51, 130, 127, 0, 34, 0, 0, +- 44, 0, 95, 38, 39, 0, 20, 45, 116, 90, ++ 84, 92, 134, 82, 105, 29, 0, 0, 94, 0, 85, 72, 0, ++ 53, 35, 86, 15, 0, 42, 97, 54, 89, 135, 19, 0, 0, ++ 140, 0, 40, 129, 0, 22, 107, 0, 9, 0, 0, 123, 80, ++ 0, 78, 6, 0, 65, 103, 147, 0, 136, 115, 0, 0, 48, ++ 0, 90, 24, 0, 17, 0, 27, 70, 23, 26, 5, 60, 142, ++ 110, 122, 0, 73, 91, 71, 145, 61, 120, 74, 0, 49, 0, ++ 11, 41, 0, 113, 0, 0, 0, 109, 10, 111, 116, 125, 14, ++ 50, 124, 0, 100, 0, 18, 121, 144, 56, 130, 139, 88, 83, ++ 37, 30, 126, 0, 0, 108, 51, 131, 128, 0, 34, 0, 0, ++ 132, 0, 98, 38, 39, 0, 20, 45, 117, 93, + }; + /* aKWNext[] forms the hash collision chain. If aKWHash[i]==0 + ** then the i-th keyword has no more hash collisions. Otherwise, + ** the next keyword with the same hash is aKWHash[i]-1. */ +-static const unsigned char aKWNext[145] = { +- 0, 0, 0, 0, 4, 0, 43, 0, 0, 103, 111, 0, 0, +- 0, 2, 0, 0, 141, 0, 0, 0, 13, 0, 0, 0, 0, +- 139, 0, 0, 118, 52, 0, 0, 135, 12, 0, 0, 62, 0, +- 136, 0, 131, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0, ++static const unsigned char aKWNext[148] = {0, ++ 0, 0, 0, 0, 4, 0, 43, 0, 0, 106, 114, 0, 0, ++ 0, 2, 0, 0, 143, 0, 0, 0, 13, 0, 0, 0, 0, ++ 141, 0, 0, 119, 52, 0, 0, 137, 12, 0, 0, 62, 0, ++ 138, 0, 133, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0, + 0, 59, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 69, 0, 0, 0, 0, 0, 144, 3, 0, 58, 0, 1, +- 75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 64, 66, +- 63, 0, 0, 0, 0, 46, 0, 16, 0, 115, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 81, 97, 0, 8, 0, 109, +- 21, 7, 67, 0, 79, 93, 117, 0, 0, 68, 0, 0, 96, +- 0, 55, 0, 76, 0, 92, 32, 33, 57, 25, 0, 98, 0, +- 0, 87, ++ 0, 69, 0, 0, 0, 0, 0, 146, 3, 0, 58, 0, 1, ++ 75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 127, 0, 104, ++ 0, 64, 66, 63, 0, 0, 0, 0, 0, 46, 0, 16, 8, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 101, 0, ++ 112, 21, 7, 67, 0, 79, 96, 118, 0, 0, 68, 0, 0, ++ 99, 44, 0, 55, 0, 76, 0, 95, 32, 33, 57, 25, 0, ++ 102, 0, 0, 87, + }; + /* aKWLen[i] is the length (in bytes) of the i-th keyword */ +-static const unsigned char aKWLen[145] = { ++static const unsigned char aKWLen[148] = {0, + 7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6, + 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7, + 6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4, + 4, 6, 11, 6, 2, 7, 5, 5, 9, 6, 10, 4, 6, + 2, 3, 7, 5, 9, 6, 6, 4, 5, 5, 10, 6, 5, + 7, 4, 5, 7, 6, 7, 7, 6, 5, 7, 3, 7, 4, +- 7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 5, 6, 6, +- 7, 6, 4, 5, 9, 5, 6, 3, 8, 8, 2, 13, 2, +- 2, 4, 6, 6, 8, 5, 17, 12, 7, 9, 4, 9, 4, +- 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6, 4, +- 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2, 2, 9, +- 3, 7, ++ 7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 12, 8, 8, ++ 2, 6, 6, 7, 6, 4, 5, 9, 5, 5, 6, 3, 4, ++ 9, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, 7, 9, ++ 4, 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6, ++ 4, 9, 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2, ++ 2, 9, 3, 7, + }; + /* aKWOffset[i] is the index into zKWText[] of the start of + ** the text for the i-th keyword. */ +-static const unsigned short int aKWOffset[145] = { ++static const unsigned short int aKWOffset[148] = {0, + 0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33, + 36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81, + 86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126, + 129, 132, 137, 142, 146, 147, 152, 156, 160, 168, 174, 181, 184, + 184, 187, 189, 195, 198, 206, 211, 216, 219, 222, 226, 236, 239, + 244, 244, 248, 252, 259, 265, 271, 277, 277, 283, 284, 288, 295, +- 299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 360, 365, 371, +- 377, 382, 388, 392, 395, 404, 408, 414, 416, 423, 424, 431, 433, +- 435, 444, 448, 454, 460, 468, 473, 473, 473, 489, 498, 501, 510, +- 513, 517, 522, 529, 534, 543, 547, 550, 555, 557, 561, 569, 575, +- 578, 583, 591, 591, 595, 604, 609, 614, 620, 623, 626, 629, 631, +- 636, 640, ++ 299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 359, 370, 377, ++ 378, 385, 391, 397, 402, 408, 412, 415, 424, 429, 433, 439, 441, ++ 444, 453, 455, 457, 466, 470, 476, 482, 490, 495, 495, 495, 511, ++ 520, 523, 527, 532, 539, 544, 553, 557, 560, 565, 567, 571, 579, ++ 585, 588, 597, 602, 610, 610, 614, 623, 628, 633, 639, 642, 645, ++ 648, 650, 655, 659, + }; + /* aKWCode[i] is the parser symbol code for the i-th keyword */ +-static const unsigned char aKWCode[145] = { ++static const unsigned char aKWCode[148] = {0, + TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE, + TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN, + TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD, +@@ -159551,18 +181157,19 @@ static const unsigned char aKWCode[145] = { + TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP, TK_CASCADE, + TK_ASC, TK_DEFAULT, TK_CASE, TK_COLLATE, TK_CREATE, + TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_MATCH, +- TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT, TK_UPDATE, +- TK_VALUES, TK_VIRTUAL, TK_ALWAYS, TK_WHEN, TK_WHERE, +- TK_RECURSIVE, TK_AFTER, TK_RENAME, TK_AND, TK_DEFERRED, +- TK_DISTINCT, TK_IS, TK_AUTOINCR, TK_TO, TK_IN, +- TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, +- TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT, TK_PARTITION, TK_DROP, +- TK_PRECEDING, TK_FAIL, TK_LAST, TK_FILTER, TK_REPLACE, +- TK_FIRST, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_LIMIT, +- TK_IF, TK_ORDER, TK_RESTRICT, TK_OTHERS, TK_OVER, +- TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, TK_ROW, TK_UNBOUNDED, +- TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_WINDOW, +- TK_DO, TK_BY, TK_INITIALLY, TK_ALL, TK_PRIMARY, ++ TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_MATERIALIZED, TK_DEFERRED, ++ TK_DISTINCT, TK_IS, TK_UPDATE, TK_VALUES, TK_VIRTUAL, ++ TK_ALWAYS, TK_WHEN, TK_WHERE, TK_RECURSIVE, TK_ABORT, ++ TK_AFTER, TK_RENAME, TK_AND, TK_DROP, TK_PARTITION, ++ TK_AUTOINCR, TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, ++ TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, ++ TK_CURRENT, TK_PRECEDING, TK_FAIL, TK_LAST, TK_FILTER, ++ TK_REPLACE, TK_FIRST, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, ++ TK_LIMIT, TK_IF, TK_ORDER, TK_RESTRICT, TK_OTHERS, ++ TK_OVER, TK_RETURNING, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, ++ TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM, ++ TK_VIEW, TK_WINDOW, TK_DO, TK_BY, TK_INITIALLY, ++ TK_ALL, TK_PRIMARY, + }; + /* Hash table decoded: + ** 0: INSERT +@@ -159586,7 +181193,7 @@ static const unsigned char aKWCode[145] = { + ** 18: TRANSACTION RIGHT + ** 19: WHEN + ** 20: SET HAVING +-** 21: IF ++** 21: MATERIALIZED IF + ** 22: ROWS + ** 23: SELECT + ** 24: +@@ -159682,7 +181289,7 @@ static const unsigned char aKWCode[145] = { + ** 114: INTERSECT UNBOUNDED + ** 115: + ** 116: +-** 117: ON ++** 117: RETURNING ON + ** 118: + ** 119: WHERE + ** 120: NO INNER +@@ -159699,183 +181306,185 @@ static const unsigned char aKWCode[145] = { + static int keywordCode(const char *z, int n, int *pType){ + int i, j; + const char *zKW; +- if( n>=2 ){ +- i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127; +- for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){ +- if( aKWLen[i]!=n ) continue; +- zKW = &zKWText[aKWOffset[i]]; ++ assert( n>=2 ); ++ i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; ++ for(i=(int)aKWHash[i]; i>0; i=aKWNext[i]){ ++ if( aKWLen[i]!=n ) continue; ++ zKW = &zKWText[aKWOffset[i]]; + #ifdef SQLITE_ASCII +- if( (z[0]&~0x20)!=zKW[0] ) continue; +- if( (z[1]&~0x20)!=zKW[1] ) continue; +- j = 2; +- while( j=2 ) keywordCode((char*)z, n, &id); + return id; + } +-#define SQLITE_N_KEYWORD 145 ++#define SQLITE_N_KEYWORD 147 + SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){ + if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR; ++ i++; + *pzName = zKWText + aKWOffset[i]; + *pnName = aKWLen[i]; + return SQLITE_OK; +@@ -159940,7 +181549,7 @@ static int getToken(const unsigned char **pz){ + int t; /* Token type to return */ + do { + z += sqlite3GetToken(z, &t); +- }while( t==TK_SPACE ); ++ }while( t==TK_SPACE || t==TK_COMMENT ); + if( t==TK_ID + || t==TK_STRING + || t==TK_JOIN_KW +@@ -160029,8 +181638,11 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ + case CC_MINUS: { + if( z[1]=='-' ){ + for(i=2; (c=z[i])!=0 && c!='\n'; i++){} +- *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ ++ *tokenType = TK_COMMENT; + return i; ++ }else if( z[1]=='>' ){ ++ *tokenType = TK_PTR; ++ return 2 + (z[2]=='>'); + } + *tokenType = TK_MINUS; + return 1; +@@ -160062,7 +181674,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ + } + for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} + if( c ) i++; +- *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ ++ *tokenType = TK_COMMENT; + return i; + } + case CC_PERCENT: { +@@ -160171,31 +181783,62 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ + testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); + testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); + testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); +- testcase( z[0]=='9' ); ++ testcase( z[0]=='9' ); testcase( z[0]=='.' ); + *tokenType = TK_INTEGER; + #ifndef SQLITE_OMIT_HEX_INTEGER + if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ +- for(i=3; sqlite3Isxdigit(z[i]); i++){} +- return i; +- } ++ for(i=3; 1; i++){ ++ if( sqlite3Isxdigit(z[i])==0 ){ ++ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ ++ *tokenType = TK_QNUMBER; ++ }else{ ++ break; ++ } ++ } ++ } ++ }else + #endif +- for(i=0; sqlite3Isdigit(z[i]); i++){} ++ { ++ for(i=0; 1; i++){ ++ if( sqlite3Isdigit(z[i])==0 ){ ++ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ ++ *tokenType = TK_QNUMBER; ++ }else{ ++ break; ++ } ++ } ++ } + #ifndef SQLITE_OMIT_FLOATING_POINT +- if( z[i]=='.' ){ +- i++; +- while( sqlite3Isdigit(z[i]) ){ i++; } +- *tokenType = TK_FLOAT; +- } +- if( (z[i]=='e' || z[i]=='E') && +- ( sqlite3Isdigit(z[i+1]) +- || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) +- ) +- ){ +- i += 2; +- while( sqlite3Isdigit(z[i]) ){ i++; } +- *tokenType = TK_FLOAT; +- } ++ if( z[i]=='.' ){ ++ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; ++ for(i++; 1; i++){ ++ if( sqlite3Isdigit(z[i])==0 ){ ++ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ ++ *tokenType = TK_QNUMBER; ++ }else{ ++ break; ++ } ++ } ++ } ++ } ++ if( (z[i]=='e' || z[i]=='E') && ++ ( sqlite3Isdigit(z[i+1]) ++ || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) ++ ) ++ ){ ++ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; ++ for(i+=2; 1; i++){ ++ if( sqlite3Isdigit(z[i])==0 ){ ++ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ ++ *tokenType = TK_QNUMBER; ++ }else{ ++ break; ++ } ++ } ++ } ++ } + #endif ++ } + while( IdChar(z[i]) ){ + *tokenType = TK_ILLEGAL; + i++; +@@ -160242,8 +181885,9 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ + if( n==0 ) *tokenType = TK_ILLEGAL; + return i; + } +- case CC_KYWD: { +- for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} ++ case CC_KYWD0: { ++ if( aiClass[z[1]]>CC_KYWD ){ i = 1; break; } ++ for(i=2; aiClass[z[i]]<=CC_KYWD; i++){} + if( IdChar(z[i]) ){ + /* This token started out using characters that can appear in keywords, + ** but z[i] is a character not allowed within keywords, so this must +@@ -160272,10 +181916,19 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ + ** SQL keywords start with the letter 'x'. Fall through */ + /* no break */ deliberate_fall_through + } ++ case CC_KYWD: + case CC_ID: { + i = 1; + break; + } ++ case CC_BOM: { ++ if( z[1]==0xbb && z[2]==0xbf ){ ++ *tokenType = TK_SPACE; ++ return 3; ++ } ++ i = 1; ++ break; ++ } + case CC_NUL: { + *tokenType = TK_ILLEGAL; + return 0; +@@ -160291,13 +181944,9 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ + } + + /* +-** Run the parser on the given SQL string. The parser structure is +-** passed in. An SQLITE_ status code is returned. If an error occurs +-** then an and attempt is made to write an error message into +-** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that +-** error message. ++** Run the parser on the given SQL string. + */ +-SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ ++SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ + int nErr = 0; /* Number of errors encountered */ + void *pEngine; /* The LEMON-generated LALR(1) parser */ + int n = 0; /* Length of the next token token */ +@@ -160305,6 +181954,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr + int lastTokenParsed = -1; /* type of the previous token */ + sqlite3 *db = pParse->db; /* The database connection */ + int mxSqlLen; /* Max length of an SQL string */ ++ Parse *pParentParse = 0; /* Outer parse context, if any */ + #ifdef sqlite3Parser_ENGINEALWAYSONSTACK + yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ + #endif +@@ -160317,7 +181967,6 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr + } + pParse->rc = SQLITE_OK; + pParse->zTail = zSql; +- assert( pzErrMsg!=0 ); + #ifdef SQLITE_DEBUG + if( db->flags & SQLITE_ParserTrace ){ + printf("parser: [[[%s]]]\n", zSql); +@@ -160340,26 +181989,31 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr + assert( pParse->pNewTrigger==0 ); + assert( pParse->nVar==0 ); + assert( pParse->pVList==0 ); +- pParse->pParentParse = db->pParse; ++ pParentParse = db->pParse; + db->pParse = pParse; + while( 1 ){ + n = sqlite3GetToken((u8*)zSql, &tokenType); + mxSqlLen -= n; + if( mxSqlLen<0 ){ + pParse->rc = SQLITE_TOOBIG; ++ pParse->nErr++; + break; + } + #ifndef SQLITE_OMIT_WINDOWFUNC + if( tokenType>=TK_WINDOW ){ + assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER + || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW ++ || tokenType==TK_QNUMBER || tokenType==TK_COMMENT + ); + #else + if( tokenType>=TK_SPACE ){ +- assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); ++ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ++ || tokenType==TK_QNUMBER || tokenType==TK_COMMENT ++ ); + #endif /* SQLITE_OMIT_WINDOWFUNC */ + if( AtomicLoad(&db->u1.isInterrupted) ){ + pParse->rc = SQLITE_INTERRUPT; ++ pParse->nErr++; + break; + } + if( tokenType==TK_SPACE ){ +@@ -160388,8 +182042,18 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr + assert( n==6 ); + tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); + #endif /* SQLITE_OMIT_WINDOWFUNC */ +- }else{ +- sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql); ++ }else if( tokenType==TK_COMMENT ++ && (db->init.busy || (db->flags & SQLITE_Comments)!=0) ++ ){ ++ /* Ignore SQL comments if either (1) we are reparsing the schema or ++ ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */ ++ zSql += n; ++ continue; ++ }else if( tokenType!=TK_QNUMBER ){ ++ Token x; ++ x.z = zSql; ++ x.n = n; ++ sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x); + break; + } + } +@@ -160417,58 +182081,32 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr + if( db->mallocFailed ){ + pParse->rc = SQLITE_NOMEM_BKPT; + } +- if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ +- pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); +- } +- assert( pzErrMsg!=0 ); +- if( pParse->zErrMsg ){ +- *pzErrMsg = pParse->zErrMsg; +- sqlite3_log(pParse->rc, "%s in \"%s\"", +- *pzErrMsg, pParse->zTail); +- pParse->zErrMsg = 0; ++ if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ ++ if( pParse->zErrMsg==0 ){ ++ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); ++ } ++ if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){ ++ sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); ++ } + nErr++; + } + pParse->zTail = zSql; +- if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){ +- sqlite3VdbeDelete(pParse->pVdbe); +- pParse->pVdbe = 0; +- } +-#ifndef SQLITE_OMIT_SHARED_CACHE +- if( pParse->nested==0 ){ +- sqlite3DbFree(db, pParse->aTableLock); +- pParse->aTableLock = 0; +- pParse->nTableLock = 0; +- } +-#endif + #ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3_free(pParse->apVtabLock); + #endif + +- if( !IN_SPECIAL_PARSE ){ ++ if( pParse->pNewTable && !IN_SPECIAL_PARSE ){ + /* If the pParse->declareVtab flag is set, do not delete any table + ** structure built up in pParse->pNewTable. The calling code (see vtab.c) + ** will take responsibility for freeing the Table structure. + */ + sqlite3DeleteTable(db, pParse->pNewTable); + } +- if( !IN_RENAME_OBJECT ){ ++ if( pParse->pNewTrigger && !IN_RENAME_OBJECT ){ + sqlite3DeleteTrigger(db, pParse->pNewTrigger); + } +- +- if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree); +- sqlite3DbFree(db, pParse->pVList); +- while( pParse->pAinc ){ +- AutoincInfo *p = pParse->pAinc; +- pParse->pAinc = p->pNext; +- sqlite3DbFreeNN(db, p); +- } +- while( pParse->pZombieTab ){ +- Table *p = pParse->pZombieTab; +- pParse->pZombieTab = p->pNextZombie; +- sqlite3DeleteTable(db, p); +- } +- db->pParse = pParse->pParentParse; +- pParse->pParentParse = 0; ++ if( pParse->pVList ) sqlite3DbNNFreeNN(db, pParse->pVList); ++ db->pParse = pParentParse; + assert( nErr==0 || pParse->rc!=SQLITE_OK ); + return nErr; + } +@@ -160517,6 +182155,7 @@ SQLITE_PRIVATE char *sqlite3Normalize( + n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); + if( NEVER(n<=0) ) break; + switch( tokenType ){ ++ case TK_COMMENT: + case TK_SPACE: { + break; + } +@@ -161040,33 +182679,20 @@ static int sqlite3TestExtInit(sqlite3 *db){ + ** Forward declarations of external module initializer functions + ** for modules that need them. + */ +-#ifdef SQLITE_ENABLE_FTS1 +-SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*); +-#endif +-#ifdef SQLITE_ENABLE_FTS2 +-SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*); +-#endif + #ifdef SQLITE_ENABLE_FTS5 + SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); + #endif +-#ifdef SQLITE_ENABLE_JSON1 +-SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*); +-#endif + #ifdef SQLITE_ENABLE_STMTVTAB + SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*); + #endif +- ++#ifdef SQLITE_EXTRA_AUTOEXT ++int SQLITE_EXTRA_AUTOEXT(sqlite3*); ++#endif + /* + ** An array of pointers to extension initializer functions for + ** built-in extensions. + */ + static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { +-#ifdef SQLITE_ENABLE_FTS1 +- sqlite3Fts1Init, +-#endif +-#ifdef SQLITE_ENABLE_FTS2 +- sqlite3Fts2Init, +-#endif + #ifdef SQLITE_ENABLE_FTS3 + sqlite3Fts3Init, + #endif +@@ -161086,8 +182712,8 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { + sqlite3DbstatRegister, + #endif + sqlite3TestExtInit, +-#ifdef SQLITE_ENABLE_JSON1 +- sqlite3Json1Init, ++#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) ++ sqlite3JsonTableFunctions, + #endif + #ifdef SQLITE_ENABLE_STMTVTAB + sqlite3StmtVtabInit, +@@ -161095,6 +182721,9 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { + #ifdef SQLITE_ENABLE_BYTECODE_VTAB + sqlite3VdbeBytecodeVtabInit, + #endif ++#ifdef SQLITE_EXTRA_AUTOEXT ++ SQLITE_EXTRA_AUTOEXT, ++#endif + }; + + #ifndef SQLITE_AMALGAMATION +@@ -161304,7 +182933,7 @@ SQLITE_API int sqlite3_initialize(void){ + sqlite3GlobalConfig.isPCacheInit = 1; + rc = sqlite3OsInit(); + } +-#ifdef SQLITE_ENABLE_DESERIALIZE ++#ifndef SQLITE_OMIT_DESERIALIZE + if( rc==SQLITE_OK ){ + rc = sqlite3MemdbInit(); + } +@@ -161312,6 +182941,14 @@ SQLITE_API int sqlite3_initialize(void){ + if( rc==SQLITE_OK ){ + sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, + sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); ++#ifdef SQLITE_EXTRA_INIT_MUTEXED ++ { ++ int SQLITE_EXTRA_INIT_MUTEXED(const char*); ++ rc = SQLITE_EXTRA_INIT_MUTEXED(0); ++ } ++#endif ++ } ++ if( rc==SQLITE_OK ){ + sqlite3MemoryBarrier(); + sqlite3GlobalConfig.isInit = 1; + #ifdef SQLITE_EXTRA_INIT +@@ -161362,7 +182999,6 @@ SQLITE_API int sqlite3_initialize(void){ + rc = SQLITE_EXTRA_INIT(0); + } + #endif +- + return rc; + } + +@@ -161432,9 +183068,21 @@ SQLITE_API int sqlite3_config(int op, ...){ + va_list ap; + int rc = SQLITE_OK; + +- /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while +- ** the SQLite library is in use. */ +- if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT; ++ /* sqlite3_config() normally returns SQLITE_MISUSE if it is invoked while ++ ** the SQLite library is in use. Except, a few selected opcodes ++ ** are allowed. ++ */ ++ if( sqlite3GlobalConfig.isInit ){ ++ static const u64 mAnytimeConfigOption = 0 ++ | MASKBIT64( SQLITE_CONFIG_LOG ) ++ | MASKBIT64( SQLITE_CONFIG_PCACHE_HDRSZ ) ++ ; ++ if( op<0 || op>63 || (MASKBIT64(op) & mAnytimeConfigOption)==0 ){ ++ return SQLITE_MISUSE_BKPT; ++ } ++ testcase( op==SQLITE_CONFIG_LOG ); ++ testcase( op==SQLITE_CONFIG_PCACHE_HDRSZ ); ++ } + + va_start(ap, op); + switch( op ){ +@@ -161503,6 +183151,7 @@ SQLITE_API int sqlite3_config(int op, ...){ + break; + } + case SQLITE_CONFIG_MEMSTATUS: { ++ assert( !sqlite3GlobalConfig.isInit ); /* Cannot change at runtime */ + /* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes + ** single argument of type int, interpreted as a boolean, which enables + ** or disables the collection of memory allocation statistics. */ +@@ -161626,8 +183275,10 @@ SQLITE_API int sqlite3_config(int op, ...){ + ** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*)); + */ + typedef void(*LOGFUNC_t)(void*,int,const char*); +- sqlite3GlobalConfig.xLog = va_arg(ap, LOGFUNC_t); +- sqlite3GlobalConfig.pLogArg = va_arg(ap, void*); ++ LOGFUNC_t xLog = va_arg(ap, LOGFUNC_t); ++ void *pLogArg = va_arg(ap, void*); ++ AtomicStore(&sqlite3GlobalConfig.xLog, xLog); ++ AtomicStore(&sqlite3GlobalConfig.pLogArg, pLogArg); + break; + } + +@@ -161641,7 +183292,8 @@ SQLITE_API int sqlite3_config(int op, ...){ + ** argument of type int. If non-zero, then URI handling is globally + ** enabled. If the parameter is zero, then URI handling is globally + ** disabled. */ +- sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); ++ int bOpenUri = va_arg(ap, int); ++ AtomicStore(&sqlite3GlobalConfig.bOpenUri, bOpenUri); + break; + } + +@@ -161719,12 +183371,24 @@ SQLITE_API int sqlite3_config(int op, ...){ + } + #endif /* SQLITE_ENABLE_SORTER_REFERENCES */ + +-#ifdef SQLITE_ENABLE_DESERIALIZE ++#ifndef SQLITE_OMIT_DESERIALIZE + case SQLITE_CONFIG_MEMDB_MAXSIZE: { + sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlite3_int64); + break; + } +-#endif /* SQLITE_ENABLE_DESERIALIZE */ ++#endif /* SQLITE_OMIT_DESERIALIZE */ ++ ++ case SQLITE_CONFIG_ROWID_IN_VIEW: { ++ int *pVal = va_arg(ap,int*); ++#ifdef SQLITE_ALLOW_ROWID_IN_VIEW ++ if( 0==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid; ++ if( 1==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = 0; ++ *pVal = (sqlite3GlobalConfig.mNoVisibleRowid==0); ++#else ++ *pVal = 0; ++#endif ++ break; ++ } + + default: { + rc = SQLITE_ERROR; +@@ -161741,17 +183405,22 @@ SQLITE_API int sqlite3_config(int op, ...){ + ** If lookaside is already active, return SQLITE_BUSY. + ** + ** The sz parameter is the number of bytes in each lookaside slot. +-** The cnt parameter is the number of slots. If pStart is NULL the +-** space for the lookaside memory is obtained from sqlite3_malloc(). +-** If pStart is not NULL then it is sz*cnt bytes of memory to use for +-** the lookaside memory. ++** The cnt parameter is the number of slots. If pBuf is NULL the ++** space for the lookaside memory is obtained from sqlite3_malloc() ++** or similar. If pBuf is not NULL then it is sz*cnt bytes of memory ++** to use for the lookaside memory. + */ +-static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ ++static int setupLookaside( ++ sqlite3 *db, /* Database connection being configured */ ++ void *pBuf, /* Memory to use for lookaside. May be NULL */ ++ int sz, /* Desired size of each lookaside memory slot */ ++ int cnt /* Number of slots to allocate */ ++){ + #ifndef SQLITE_OMIT_LOOKASIDE +- void *pStart; +- sqlite3_int64 szAlloc = sz*(sqlite3_int64)cnt; +- int nBig; /* Number of full-size slots */ +- int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */ ++ void *pStart; /* Start of the lookaside buffer */ ++ sqlite3_int64 szAlloc; /* Total space set aside for lookaside memory */ ++ int nBig; /* Number of full-size slots */ ++ int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */ + + if( sqlite3LookasideUsed(db,0)>0 ){ + return SQLITE_BUSY; +@@ -161764,17 +183433,22 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ + sqlite3_free(db->lookaside.pStart); + } + /* The size of a lookaside slot after ROUNDDOWN8 needs to be larger +- ** than a pointer to be useful. ++ ** than a pointer and small enough to fit in a u16. + */ +- sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ ++ sz = ROUNDDOWN8(sz); + if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; +- if( cnt<0 ) cnt = 0; +- if( sz==0 || cnt==0 ){ ++ if( sz>65528 ) sz = 65528; ++ /* Count must be at least 1 to be useful, but not so large as to use ++ ** more than 0x7fff0000 total bytes for lookaside. */ ++ if( cnt<1 ) cnt = 0; ++ if( sz>0 && cnt>(0x7fff0000/sz) ) cnt = 0x7fff0000/sz; ++ szAlloc = (i64)sz*(i64)cnt; ++ if( szAlloc==0 ){ + sz = 0; + pStart = 0; + }else if( pBuf==0 ){ + sqlite3BeginBenignMalloc(); +- pStart = sqlite3Malloc( szAlloc ); /* IMP: R-61949-35727 */ ++ pStart = sqlite3Malloc( szAlloc ); + sqlite3EndBenignMalloc(); + if( pStart ) szAlloc = sqlite3MallocSize(pStart); + }else{ +@@ -161783,10 +183457,10 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ + #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + if( sz>=LOOKASIDE_SMALL*3 ){ + nBig = szAlloc/(3*LOOKASIDE_SMALL+sz); +- nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL; ++ nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL; + }else if( sz>=LOOKASIDE_SMALL*2 ){ + nBig = szAlloc/(LOOKASIDE_SMALL+sz); +- nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL; ++ nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL; + }else + #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ + if( sz>0 ){ +@@ -161826,18 +183500,19 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ + db->lookaside.bMalloced = pBuf==0 ?1:0; + db->lookaside.nSlot = nBig+nSm; + }else{ +- db->lookaside.pStart = db; ++ db->lookaside.pStart = 0; + #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE + db->lookaside.pSmallInit = 0; + db->lookaside.pSmallFree = 0; +- db->lookaside.pMiddle = db; ++ db->lookaside.pMiddle = 0; + #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ +- db->lookaside.pEnd = db; ++ db->lookaside.pEnd = 0; + db->lookaside.bDisable = 1; + db->lookaside.sz = 0; + db->lookaside.bMalloced = 0; + db->lookaside.nSlot = 0; + } ++ db->lookaside.pTrueEnd = db->lookaside.pEnd; + assert( sqlite3LookasideUsed(db,0)==0 ); + #endif /* SQLITE_OMIT_LOOKASIDE */ + return SQLITE_OK; +@@ -161896,7 +183571,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){ + sqlite3BtreeEnterAll(db); + for(i=0; rc==SQLITE_OK && inDb; i++){ + Btree *pBt = db->aDb[i].pBt; +- if( pBt && sqlite3BtreeIsInTrans(pBt) ){ ++ if( pBt && sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ + Pager *pPager = sqlite3BtreePager(pBt); + rc = sqlite3PagerFlush(pPager); + if( rc==SQLITE_BUSY ){ +@@ -161916,6 +183591,11 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){ + SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ + va_list ap; + int rc; ++ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; ++#endif ++ sqlite3_mutex_enter(db->mutex); + va_start(ap, op); + switch( op ){ + case SQLITE_DBCONFIG_MAINDBNAME: { +@@ -161935,7 +183615,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ + default: { + static const struct { + int op; /* The opcode */ +- u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ ++ u64 mask; /* Mask of the bit in sqlite3.flags to set/clear */ + } aFlagOp[] = { + { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, + { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, +@@ -161954,6 +183634,11 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ + { SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML }, + { SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, SQLITE_LegacyFileFmt }, + { SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema }, ++ { SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus }, ++ { SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder }, ++ { SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, SQLITE_AttachCreate }, ++ { SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, SQLITE_AttachWrite }, ++ { SQLITE_DBCONFIG_ENABLE_COMMENTS, SQLITE_Comments }, + }; + unsigned int i; + rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ +@@ -161981,6 +183666,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ + } + } + va_end(ap); ++ sqlite3_mutex_leave(db->mutex); + return rc; + } + +@@ -162085,7 +183771,7 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3 *db, sqlite3_int64 iRowid) + /* + ** Return the number of changes in the most recent call to sqlite3_exec(). + */ +-SQLITE_API int sqlite3_changes(sqlite3 *db){ ++SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3 *db){ + #ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + (void)SQLITE_MISUSE_BKPT; +@@ -162094,11 +183780,14 @@ SQLITE_API int sqlite3_changes(sqlite3 *db){ + #endif + return db->nChange; + } ++SQLITE_API int sqlite3_changes(sqlite3 *db){ ++ return (int)sqlite3_changes64(db); ++} + + /* + ** Return the number of changes since the database handle was opened. + */ +-SQLITE_API int sqlite3_total_changes(sqlite3 *db){ ++SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3 *db){ + #ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + (void)SQLITE_MISUSE_BKPT; +@@ -162107,6 +183796,9 @@ SQLITE_API int sqlite3_total_changes(sqlite3 *db){ + #endif + return db->nTotalChange; + } ++SQLITE_API int sqlite3_total_changes(sqlite3 *db){ ++ return (int)sqlite3_total_changes64(db); ++} + + /* + ** Close all open savepoints. This function only manipulates fields of the +@@ -162131,7 +183823,9 @@ SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *db){ + ** with SQLITE_ANY as the encoding. + */ + static void functionDestroy(sqlite3 *db, FuncDef *p){ +- FuncDestructor *pDestructor = p->u.pDestructor; ++ FuncDestructor *pDestructor; ++ assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); ++ pDestructor = p->u.pDestructor; + if( pDestructor ){ + pDestructor->nRef--; + if( pDestructor->nRef==0 ){ +@@ -162233,17 +183927,55 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){ + } + #endif + ++ while( db->pDbData ){ ++ DbClientData *p = db->pDbData; ++ db->pDbData = p->pNext; ++ assert( p->pData!=0 ); ++ if( p->xDestructor ) p->xDestructor(p->pData); ++ sqlite3_free(p); ++ } ++ + /* Convert the connection into a zombie and then close it. + */ +- db->magic = SQLITE_MAGIC_ZOMBIE; ++ db->eOpenState = SQLITE_STATE_ZOMBIE; + sqlite3LeaveMutexAndCloseZombie(db); + return SQLITE_OK; + } + ++/* ++** Return the transaction state for a single databse, or the maximum ++** transaction state over all attached databases if zSchema is null. ++*/ ++SQLITE_API int sqlite3_txn_state(sqlite3 *db, const char *zSchema){ ++ int iDb, nDb; ++ int iTxn = -1; ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ){ ++ (void)SQLITE_MISUSE_BKPT; ++ return -1; ++ } ++#endif ++ sqlite3_mutex_enter(db->mutex); ++ if( zSchema ){ ++ nDb = iDb = sqlite3FindDbName(db, zSchema); ++ if( iDb<0 ) nDb--; ++ }else{ ++ iDb = 0; ++ nDb = db->nDb-1; ++ } ++ for(; iDb<=nDb; iDb++){ ++ Btree *pBt = db->aDb[iDb].pBt; ++ int x = pBt!=0 ? sqlite3BtreeTxnState(pBt) : SQLITE_TXN_NONE; ++ if( x>iTxn ) iTxn = x; ++ } ++ sqlite3_mutex_leave(db->mutex); ++ return iTxn; ++} ++ + /* + ** Two variations on the public interface for closing a database + ** connection. The sqlite3_close() version returns SQLITE_BUSY and +-** leaves the connection option if there are unfinalized prepared ++** leaves the connection open if there are unfinalized prepared + ** statements or unfinished sqlite3_backups. The sqlite3_close_v2() + ** version forces the connection to become a zombie if there are + ** unclosed resources, and arranges for deallocation when the last +@@ -162269,7 +184001,7 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ + ** or if the connection has not yet been closed by sqlite3_close_v2(), + ** then just leave the mutex and return. + */ +- if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){ ++ if( db->eOpenState!=SQLITE_STATE_ZOMBIE || connectionIsBusy(db) ){ + sqlite3_mutex_leave(db->mutex); + return; + } +@@ -162350,12 +184082,8 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ + sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ + sqlite3ValueFree(db->pErr); + sqlite3CloseExtensions(db); +-#if SQLITE_USER_AUTHENTICATION +- sqlite3_free(db->auth.zAuthUser); +- sqlite3_free(db->auth.zAuthPW); +-#endif + +- db->magic = SQLITE_MAGIC_ERROR; ++ db->eOpenState = SQLITE_STATE_ERROR; + + /* The temp-database schema is allocated differently from the other schema + ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). +@@ -162364,8 +184092,11 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ + ** structure? + */ + sqlite3DbFree(db, db->aDb[1].pSchema); ++ if( db->xAutovacDestr ){ ++ db->xAutovacDestr(db->pAutovacPagesArg); ++ } + sqlite3_mutex_leave(db->mutex); +- db->magic = SQLITE_MAGIC_CLOSED; ++ db->eOpenState = SQLITE_STATE_CLOSED; + sqlite3_mutex_free(db->mutex); + assert( sqlite3LookasideUsed(db,0)==0 ); + if( db->lookaside.bMalloced ){ +@@ -162400,7 +184131,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ + for(i=0; inDb; i++){ + Btree *p = db->aDb[i].pBt; + if( p ){ +- if( sqlite3BtreeIsInTrans(p) ){ ++ if( sqlite3BtreeTxnState(p)==SQLITE_TXN_WRITE ){ + inTrans = 1; + } + sqlite3BtreeRollback(p, tripCode, !schemaChange); +@@ -162418,7 +184149,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ + /* Any deferred constraint violations have now been resolved. */ + db->nDeferredCons = 0; + db->nDeferredImmCons = 0; +- db->flags &= ~(u64)SQLITE_DeferFKs; ++ db->flags &= ~(u64)(SQLITE_DeferFKs|SQLITE_CorruptRdOnly); + + /* If one has been configured, invoke the rollback-hook callback */ + if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ +@@ -162524,6 +184255,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ + case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break; + case SQLITE_NOTICE_RECOVER_ROLLBACK: + zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break; ++ case SQLITE_NOTICE_RBU: zName = "SQLITE_NOTICE_RBU"; break; + case SQLITE_WARNING: zName = "SQLITE_WARNING"; break; + case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break; + case SQLITE_DONE: zName = "SQLITE_DONE"; break; +@@ -162616,9 +184348,9 @@ static int sqliteDefaultBusyCallback( + void *ptr, /* Database connection */ + int count /* Number of times table has been busy */ + ){ +-#if SQLITE_OS_WIN || HAVE_USLEEP ++#if SQLITE_OS_WIN || !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP + /* This case is for systems that have support for sleeping for fractions of +- ** a second. Examples: All windows systems, unix systems with usleep() */ ++ ** a second. Examples: All windows systems, unix systems with nanosleep() */ + static const u8 delays[] = + { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; + static const u8 totals[] = +@@ -162693,6 +184425,9 @@ SQLITE_API int sqlite3_busy_handler( + db->busyHandler.pBusyArg = pArg; + db->busyHandler.nBusy = 0; + db->busyTimeout = 0; ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ db->setlkTimeout = 0; ++#endif + sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; + } +@@ -162742,18 +184477,57 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ + sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, + (void*)db); + db->busyTimeout = ms; ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ db->setlkTimeout = ms; ++#endif + }else{ + sqlite3_busy_handler(db, 0, 0); + } + return SQLITE_OK; + } + ++/* ++** Set the setlk timeout value. ++*/ ++SQLITE_API int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){ ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ int iDb; ++ int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0); ++#endif ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; ++#endif ++ if( ms<-1 ) return SQLITE_RANGE; ++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT ++ sqlite3_mutex_enter(db->mutex); ++ db->setlkTimeout = ms; ++ db->setlkFlags = flags; ++ sqlite3BtreeEnterAll(db); ++ for(iDb=0; iDbnDb; iDb++){ ++ Btree *pBt = db->aDb[iDb].pBt; ++ if( pBt ){ ++ sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); ++ sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); ++ } ++ } ++ sqlite3BtreeLeaveAll(db); ++ sqlite3_mutex_leave(db->mutex); ++#endif ++#if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT) ++ UNUSED_PARAMETER(db); ++ UNUSED_PARAMETER(flags); ++#endif ++ return SQLITE_OK; ++} ++ + /* + ** Cause any pending operation to stop at its earliest opportunity. + */ + SQLITE_API void sqlite3_interrupt(sqlite3 *db){ + #ifdef SQLITE_ENABLE_API_ARMOR +- if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ ++ if( !sqlite3SafetyCheckOk(db) ++ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) ++ ){ + (void)SQLITE_MISUSE_BKPT; + return; + } +@@ -162761,6 +184535,21 @@ SQLITE_API void sqlite3_interrupt(sqlite3 *db){ + AtomicStore(&db->u1.isInterrupted, 1); + } + ++/* ++** Return true or false depending on whether or not an interrupt is ++** pending on connection db. ++*/ ++SQLITE_API int sqlite3_is_interrupted(sqlite3 *db){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ++ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) ++ ){ ++ (void)SQLITE_MISUSE_BKPT; ++ return 0; ++ } ++#endif ++ return AtomicLoad(&db->u1.isInterrupted)!=0; ++} + + /* + ** This function is exactly the same as sqlite3_create_function(), except +@@ -162782,7 +184571,6 @@ SQLITE_PRIVATE int sqlite3CreateFunc( + FuncDestructor *pDestructor + ){ + FuncDef *p; +- int nName; + int extraFlags; + + assert( sqlite3_mutex_held(db->mutex) ); +@@ -162792,7 +184580,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc( + || ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */ + || ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */ + || (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) +- || (255<(nName = sqlite3Strlen30( zFunctionName))) ++ || (255nRef==0 ){ +- assert( rc!=SQLITE_OK ); ++ assert( rc!=SQLITE_OK || (xStep==0 && xFinal==0) ); + xDestroy(p); + sqlite3_free(pArg); + } +@@ -163062,7 +184866,7 @@ SQLITE_API int sqlite3_overload_function( + rc = sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0; + sqlite3_mutex_leave(db->mutex); + if( rc ) return SQLITE_OK; +- zCopy = sqlite3_mprintf(zName); ++ zCopy = sqlite3_mprintf("%s", zName); + if( zCopy==0 ) return SQLITE_NOMEM; + return sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8, + zCopy, sqlite3InvalidFunction, 0, 0, sqlite3_free); +@@ -163242,6 +185046,12 @@ SQLITE_API void *sqlite3_preupdate_hook( + void *pArg /* First callback argument */ + ){ + void *pRet; ++ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( db==0 ){ ++ return 0; ++ } ++#endif + sqlite3_mutex_enter(db->mutex); + pRet = db->pPreUpdateArg; + db->xPreUpdateCallback = xCallback; +@@ -163251,6 +185061,34 @@ SQLITE_API void *sqlite3_preupdate_hook( + } + #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + ++/* ++** Register a function to be invoked prior to each autovacuum that ++** determines the number of pages to vacuum. ++*/ ++SQLITE_API int sqlite3_autovacuum_pages( ++ sqlite3 *db, /* Attach the hook to this database */ ++ unsigned int (*xCallback)(void*,const char*,u32,u32,u32), ++ void *pArg, /* Argument to the function */ ++ void (*xDestructor)(void*) /* Destructor for pArg */ ++){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ){ ++ if( xDestructor ) xDestructor(pArg); ++ return SQLITE_MISUSE_BKPT; ++ } ++#endif ++ sqlite3_mutex_enter(db->mutex); ++ if( db->xAutovacDestr ){ ++ db->xAutovacDestr(db->pAutovacPagesArg); ++ } ++ db->xAutovacPages = xCallback; ++ db->pAutovacPagesArg = pArg; ++ db->xAutovacDestr = xDestructor; ++ sqlite3_mutex_leave(db->mutex); ++ return SQLITE_OK; ++} ++ ++ + #ifndef SQLITE_OMIT_WAL + /* + ** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint(). +@@ -163343,7 +185181,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( + return SQLITE_OK; + #else + int rc; /* Return code */ +- int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ ++ int iDb; /* Schema to checkpoint */ + + #ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +@@ -163360,12 +185198,14 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( + if( eModeSQLITE_CHECKPOINT_TRUNCATE ){ + /* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint + ** mode: */ +- return SQLITE_MISUSE; ++ return SQLITE_MISUSE_BKPT; + } + + sqlite3_mutex_enter(db->mutex); + if( zDb && zDb[0] ){ + iDb = sqlite3FindDbName(db, zDb); ++ }else{ ++ iDb = SQLITE_MAX_DB; /* This means process all schemas */ + } + if( iDb<0 ){ + rc = SQLITE_ERROR; +@@ -163414,7 +185254,7 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ + ** associated with the specific b-tree being checkpointed is taken by + ** this function while the checkpoint is running. + ** +-** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ++** If iDb is passed SQLITE_MAX_DB then all attached databases are + ** checkpointed. If an error is encountered it is returned immediately - + ** no attempt is made to checkpoint any remaining databases. + ** +@@ -163429,9 +185269,11 @@ SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog + assert( sqlite3_mutex_held(db->mutex) ); + assert( !pnLog || *pnLog==-1 ); + assert( !pnCkpt || *pnCkpt==-1 ); ++ testcase( iDb==SQLITE_MAX_ATTACHED ); /* See forum post a006d86f72 */ ++ testcase( iDb==SQLITE_MAX_DB ); + + for(i=0; inDb && rc==SQLITE_OK; i++){ +- if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ ++ if( i==iDb || iDb==SQLITE_MAX_DB ){ + rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); + pnLog = 0; + pnCkpt = 0; +@@ -163509,6 +185351,19 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ + return z; + } + ++/* ++** Return the byte offset of the most recent error ++*/ ++SQLITE_API int sqlite3_error_offset(sqlite3 *db){ ++ int iOffset = -1; ++ if( db && sqlite3SafetyCheckSickOrOk(db) && db->errCode ){ ++ sqlite3_mutex_enter(db->mutex); ++ iOffset = db->errByteOffset; ++ sqlite3_mutex_leave(db->mutex); ++ } ++ return iOffset; ++} ++ + #ifndef SQLITE_OMIT_UTF16 + /* + ** Return UTF-16 encoded English language explanation of the most recent +@@ -163701,8 +185556,8 @@ static const int aHardLimit[] = { + #if SQLITE_MAX_VDBE_OP<40 + # error SQLITE_MAX_VDBE_OP must be at least 40 + #endif +-#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127 +-# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127 ++#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767 ++# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767 + #endif + #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 + # error SQLITE_MAX_ATTACHED must be between 0 and 125 +@@ -163769,6 +185624,8 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ + if( newLimit>=0 ){ /* IMP: R-52476-28732 */ + if( newLimit>aHardLimit[limitId] ){ + newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ ++ }else if( newLimitaLimit[limitId] = newLimit; + } +@@ -163818,9 +185675,9 @@ SQLITE_PRIVATE int sqlite3ParseUri( + + assert( *pzErrMsg==0 ); + +- if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */ +- || sqlite3GlobalConfig.bOpenUri) /* IMP: R-51689-46548 */ +- && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */ ++ if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */ ++ || AtomicLoad(&sqlite3GlobalConfig.bOpenUri)) /* IMP: R-51689-46548 */ ++ && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */ + ){ + char *zOpt; + int eState; /* Parser state when parsing URI */ +@@ -164040,7 +185897,7 @@ SQLITE_PRIVATE int sqlite3ParseUri( + */ + static const char *uriParameter(const char *zFilename, const char *zParam){ + zFilename += sqlite3Strlen30(zFilename) + 1; +- while( zFilename[0] ){ ++ while( ALWAYS(zFilename!=0) && zFilename[0] ){ + int x = strcmp(zFilename, zParam); + zFilename += sqlite3Strlen30(zFilename) + 1; + if( x==0 ) return zFilename; +@@ -164100,8 +185957,8 @@ static int openDatabase( + ** dealt with in the previous code block. Besides these, the only + ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, + ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, +- ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask +- ** off all other flags. ++ ** SQLITE_OPEN_PRIVATECACHE, SQLITE_OPEN_EXRESCODE, and some reserved ++ ** bits. Silently mask off all other flags. + */ + flags &= ~( SQLITE_OPEN_DELETEONCLOSE | + SQLITE_OPEN_EXCLUSIVE | +@@ -164136,9 +185993,9 @@ static int openDatabase( + } + } + sqlite3_mutex_enter(db->mutex); +- db->errMask = 0xff; ++ db->errMask = (flags & SQLITE_OPEN_EXRESCODE)!=0 ? 0xffffffff : 0xff; + db->nDb = 2; +- db->magic = SQLITE_MAGIC_BUSY; ++ db->eOpenState = SQLITE_STATE_BUSY; + db->aDb = db->aDbStatic; + db->lookaside.bDisable = 1; + db->lookaside.sz = 0; +@@ -164150,11 +186007,22 @@ static int openDatabase( + db->nextAutovac = -1; + db->szMmap = sqlite3GlobalConfig.szMmap; + db->nextPagesize = 0; ++ db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */ ++#ifdef SQLITE_ENABLE_SORTER_MMAP ++ /* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map ++ ** the temporary files used to do external sorts (see code in vdbesort.c) ++ ** is disabled. It can still be used either by defining ++ ** SQLITE_ENABLE_SORTER_MMAP at compile time or by using the ++ ** SQLITE_TESTCTRL_SORTER_MMAP test-control at runtime. */ + db->nMaxSorterMmap = 0x7FFFFFFF; ++#endif + db->flags |= SQLITE_ShortColNames + | SQLITE_EnableTrigger + | SQLITE_EnableView + | SQLITE_CacheSpill ++ | SQLITE_AttachCreate ++ | SQLITE_AttachWrite ++ | SQLITE_Comments + #if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0 + | SQLITE_TrustedSchema + #endif +@@ -164170,7 +186038,7 @@ static int openDatabase( + ** 0 off off + ** + ** Legacy behavior is 3 (double-quoted string literals are allowed anywhere) +-** and so that is the default. But developers are encouranged to use ++** and so that is the default. But developers are encouraged to use + ** -DSQLITE_DQS=0 (best) or -DSQLITE_DQS=1 (second choice) if possible. + */ + #if !defined(SQLITE_DQS) +@@ -164218,6 +186086,9 @@ static int openDatabase( + #endif + #if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE) + | SQLITE_LegacyAlter ++#endif ++#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) ++ | SQLITE_StmtScanStatus + #endif + ; + sqlite3HashInit(&db->aCollSeq); +@@ -164241,6 +186112,19 @@ static int openDatabase( + goto opendb_out; + } + ++#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) ++ /* Process magic filenames ":localStorage:" and ":sessionStorage:" */ ++ if( zFilename && zFilename[0]==':' ){ ++ if( strcmp(zFilename, ":localStorage:")==0 ){ ++ zFilename = "file:local?vfs=kvvfs"; ++ flags |= SQLITE_OPEN_URI; ++ }else if( strcmp(zFilename, ":sessionStorage:")==0 ){ ++ zFilename = "file:session?vfs=kvvfs"; ++ flags |= SQLITE_OPEN_URI; ++ } ++ } ++#endif /* SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) */ ++ + /* Parse the filename/URI argument + ** + ** Only allow sensible combinations of bits in the flags argument. +@@ -164263,6 +186147,7 @@ static int openDatabase( + if( ((1<<(flags&7)) & 0x46)==0 ){ + rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ + }else{ ++ if( zFilename==0 ) zFilename = ":memory:"; + rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); + } + if( rc!=SQLITE_OK ){ +@@ -164271,6 +186156,12 @@ static int openDatabase( + sqlite3_free(zErrMsg); + goto opendb_out; + } ++ assert( db->pVfs!=0 ); ++#if SQLITE_OS_KV || defined(SQLITE_OS_KV_OPTIONAL) ++ if( sqlite3_stricmp(db->pVfs->zName, "kvvfs")==0 ){ ++ db->temp_store = 2; ++ } ++#endif + + /* Open the backend database driver */ + rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, +@@ -164298,7 +186189,7 @@ static int openDatabase( + db->aDb[1].zDbSName = "temp"; + db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; + +- db->magic = SQLITE_MAGIC_OPEN; ++ db->eOpenState = SQLITE_STATE_OPEN; + if( db->mallocFailed ){ + goto opendb_out; + } +@@ -164360,12 +186251,12 @@ opendb_out: + sqlite3_mutex_leave(db->mutex); + } + rc = sqlite3_errcode(db); +- assert( db!=0 || rc==SQLITE_NOMEM ); +- if( rc==SQLITE_NOMEM ){ ++ assert( db!=0 || (rc&0xff)==SQLITE_NOMEM ); ++ if( (rc&0xff)==SQLITE_NOMEM ){ + sqlite3_close(db); + db = 0; + }else if( rc!=SQLITE_OK ){ +- db->magic = SQLITE_MAGIC_SICK; ++ db->eOpenState = SQLITE_STATE_SICK; + } + *ppDb = db; + #ifdef SQLITE_ENABLE_SQLLOG +@@ -164376,7 +186267,7 @@ opendb_out: + } + #endif + sqlite3_free_filename(zOpen); +- return rc & 0xff; ++ return rc; + } + + +@@ -164548,6 +186439,69 @@ SQLITE_API int sqlite3_collation_needed16( + } + #endif /* SQLITE_OMIT_UTF16 */ + ++/* ++** Find existing client data. ++*/ ++SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){ ++ DbClientData *p; ++ sqlite3_mutex_enter(db->mutex); ++ for(p=db->pDbData; p; p=p->pNext){ ++ if( strcmp(p->zName, zName)==0 ){ ++ void *pResult = p->pData; ++ sqlite3_mutex_leave(db->mutex); ++ return pResult; ++ } ++ } ++ sqlite3_mutex_leave(db->mutex); ++ return 0; ++} ++ ++/* ++** Add new client data to a database connection. ++*/ ++SQLITE_API int sqlite3_set_clientdata( ++ sqlite3 *db, /* Attach client data to this connection */ ++ const char *zName, /* Name of the client data */ ++ void *pData, /* The client data itself */ ++ void (*xDestructor)(void*) /* Destructor */ ++){ ++ DbClientData *p, **pp; ++ sqlite3_mutex_enter(db->mutex); ++ pp = &db->pDbData; ++ for(p=db->pDbData; p && strcmp(p->zName,zName); p=p->pNext){ ++ pp = &p->pNext; ++ } ++ if( p ){ ++ assert( p->pData!=0 ); ++ if( p->xDestructor ) p->xDestructor(p->pData); ++ if( pData==0 ){ ++ *pp = p->pNext; ++ sqlite3_free(p); ++ sqlite3_mutex_leave(db->mutex); ++ return SQLITE_OK; ++ } ++ }else if( pData==0 ){ ++ sqlite3_mutex_leave(db->mutex); ++ return SQLITE_OK; ++ }else{ ++ size_t n = strlen(zName); ++ p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) ); ++ if( p==0 ){ ++ if( xDestructor ) xDestructor(pData); ++ sqlite3_mutex_leave(db->mutex); ++ return SQLITE_NOMEM; ++ } ++ memcpy(p->zName, zName, n+1); ++ p->pNext = db->pDbData; ++ db->pDbData = p; ++ } ++ p->pData = pData; ++ p->xDestructor = xDestructor; ++ sqlite3_mutex_leave(db->mutex); ++ return SQLITE_OK; ++} ++ ++ + #ifndef SQLITE_OMIT_DEPRECATED + /* + ** This function is now an anachronism. It used to be used to recover from a +@@ -164676,22 +186630,19 @@ SQLITE_API int sqlite3_table_column_metadata( + + /* Locate the table in question */ + pTab = sqlite3FindTable(db, zTableName, zDbName); +- if( !pTab || pTab->pSelect ){ ++ if( !pTab || IsView(pTab) ){ + pTab = 0; + goto error_out; + } + + /* Find the column for which info is requested */ + if( zColumnName==0 ){ +- /* Query for existance of table only */ ++ /* Query for existence of table only */ + }else{ +- for(iCol=0; iColnCol; iCol++){ ++ iCol = sqlite3ColumnIndex(pTab, zColumnName); ++ if( iCol>=0 ){ + pCol = &pTab->aCol[iCol]; +- if( 0==sqlite3StrICmp(pCol->zName, zColumnName) ){ +- break; +- } +- } +- if( iCol==pTab->nCol ){ ++ }else{ + if( HasRowid(pTab) && sqlite3IsRowid(zColumnName) ){ + iCol = pTab->iPKey; + pCol = iCol>=0 ? &pTab->aCol[iCol] : 0; +@@ -164714,7 +186665,7 @@ SQLITE_API int sqlite3_table_column_metadata( + */ + if( pCol ){ + zDataType = sqlite3ColumnType(pCol,0); +- zCollSeq = pCol->zColl; ++ zCollSeq = sqlite3ColumnColl(pCol); + notnull = pCol->notNull!=0; + primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0; + autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0; +@@ -164764,7 +186715,7 @@ SQLITE_API int sqlite3_sleep(int ms){ + /* This function works in milliseconds, but the underlying OsSleep() + ** API uses microseconds. Hence the 1000's. + */ +- rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000); ++ rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000); + return rc; + } + +@@ -164820,8 +186771,13 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo + sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); + } + rc = SQLITE_OK; ++ }else if( op==SQLITE_FCNTL_RESET_CACHE ){ ++ sqlite3BtreeClearCache(pBtree); ++ rc = SQLITE_OK; + }else{ ++ int nSave = db->busyHandler.nBusy; + rc = sqlite3OsFileControl(fd, op, pArg); ++ db->busyHandler.nBusy = nSave; + } + sqlite3BtreeLeave(pBtree); + } +@@ -164892,6 +186848,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){ + } + #endif + ++ /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); ++ ** ++ ** If b is true, then activate the SQLITE_FkNoAction setting. If b is ++ ** false then clear that setting. If the SQLITE_FkNoAction setting is ++ ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if ++ ** they were NO ACTION, regardless of how they are defined. ++ ** ++ ** NB: One must usually run "PRAGMA writable_schema=RESET" after ++ ** using this test-control, before it will take full effect. failing ++ ** to reset the schema can result in some unexpected behavior. ++ */ ++ case SQLITE_TESTCTRL_FK_NO_ACTION: { ++ sqlite3 *db = va_arg(ap, sqlite3*); ++ int b = va_arg(ap, int); ++ if( b ){ ++ db->flags |= SQLITE_FkNoAction; ++ }else{ ++ db->flags &= ~SQLITE_FkNoAction; ++ } ++ break; ++ } ++ + /* + ** sqlite3_test_control(BITVEC_TEST, size, program) + ** +@@ -164919,12 +186897,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){ + ** sqlite3_test_control(). + */ + case SQLITE_TESTCTRL_FAULT_INSTALL: { +- /* MSVC is picky about pulling func ptrs from va lists. +- ** http://support.microsoft.com/kb/47961 ++ /* A bug in MSVC prevents it from understanding pointers to functions ++ ** types in the second argument to va_arg(). Work around the problem ++ ** using a typedef. ++ ** http://support.microsoft.com/kb/47961 <-- dead hyperlink ++ ** Search at http://web.archive.org/ to find the 2015-03-16 archive ++ ** of the link above to see the original text. + ** sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int)); + */ +- typedef int(*TESTCALLBACKFUNC_t)(int); +- sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t); ++ typedef int(*sqlite3FaultFuncType)(int); ++ sqlite3GlobalConfig.xTestCallback = va_arg(ap, sqlite3FaultFuncType); + rc = sqlite3FaultSim(0); + break; + } +@@ -164983,6 +186965,29 @@ SQLITE_API int sqlite3_test_control(int op, ...){ + volatile int x = 0; + assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 ); + rc = x; ++#if defined(SQLITE_DEBUG) ++ /* Invoke these debugging routines so that the compiler does not ++ ** issue "defined but not used" warnings. */ ++ if( x==9999 ){ ++ sqlite3ShowExpr(0); ++ sqlite3ShowExprList(0); ++ sqlite3ShowIdList(0); ++ sqlite3ShowSrcList(0); ++ sqlite3ShowWith(0); ++ sqlite3ShowUpsert(0); ++#ifndef SQLITE_OMIT_TRIGGER ++ sqlite3ShowTriggerStep(0); ++ sqlite3ShowTriggerStepList(0); ++ sqlite3ShowTrigger(0); ++ sqlite3ShowTriggerList(0); ++#endif ++#ifndef SQLITE_OMIT_WINDOWFUNC ++ sqlite3ShowWindow(0); ++ sqlite3ShowWinFunc(0); ++#endif ++ sqlite3ShowSelect(0); ++ } ++#endif + break; + } + +@@ -165047,17 +187052,43 @@ SQLITE_API int sqlite3_test_control(int op, ...){ + */ + case SQLITE_TESTCTRL_OPTIMIZATIONS: { + sqlite3 *db = va_arg(ap, sqlite3*); +- db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff); ++ db->dbOptFlags = va_arg(ap, u32); + break; + } + +- /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); ++ /* sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, sqlite3 *db, int *N) + ** +- ** If parameter onoff is non-zero, subsequent calls to localtime() +- ** and its variants fail. If onoff is zero, undo this setting. ++ ** Write the current optimization settings into *N. A zero bit means that ++ ** the optimization is on, and a 1 bit means that the optimization is off. ++ */ ++ case SQLITE_TESTCTRL_GETOPT: { ++ sqlite3 *db = va_arg(ap, sqlite3*); ++ int *pN = va_arg(ap, int*); ++ *pN = db->dbOptFlags; ++ break; ++ } ++ ++ /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt); ++ ** ++ ** If parameter onoff is 1, subsequent calls to localtime() fail. ++ ** If 2, then invoke xAlt() instead of localtime(). If 0, normal ++ ** processing. ++ ** ++ ** xAlt arguments are void pointers, but they really want to be: ++ ** ++ ** int xAlt(const time_t*, struct tm*); ++ ** ++ ** xAlt should write results in to struct tm object of its 2nd argument ++ ** and return zero on success, or return non-zero on failure. + */ + case SQLITE_TESTCTRL_LOCALTIME_FAULT: { + sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); ++ if( sqlite3GlobalConfig.bLocaltimeFault==2 ){ ++ typedef int(*sqlite3LocaltimeType)(const void*,void*); ++ sqlite3GlobalConfig.xAltLocaltime = va_arg(ap, sqlite3LocaltimeType); ++ }else{ ++ sqlite3GlobalConfig.xAltLocaltime = 0; ++ } + break; + } + +@@ -165078,7 +187109,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){ + ** formed and never corrupt. This flag is clear by default, indicating that + ** database files might have arbitrary corruption. Setting the flag during + ** testing causes certain assert() statements in the code to be activated +- ** that demonstrat invariants on well-formed database files. ++ ** that demonstrate invariants on well-formed database files. + */ + case SQLITE_TESTCTRL_NEVER_CORRUPT: { + sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int); +@@ -165162,12 +187193,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){ + */ + case SQLITE_TESTCTRL_IMPOSTER: { + sqlite3 *db = va_arg(ap, sqlite3*); ++ int iDb; + sqlite3_mutex_enter(db->mutex); +- db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); +- db->init.busy = db->init.imposterTable = va_arg(ap,int); +- db->init.newTnum = va_arg(ap,int); +- if( db->init.busy==0 && db->init.newTnum>0 ){ +- sqlite3ResetAllSchemasOfConnection(db); ++ iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); ++ if( iDb>=0 ){ ++ db->init.iDb = iDb; ++ db->init.busy = db->init.imposterTable = va_arg(ap,int); ++ db->init.newTnum = va_arg(ap,int); ++ if( db->init.busy==0 && db->init.newTnum>0 ){ ++ sqlite3ResetAllSchemasOfConnection(db); ++ } + } + sqlite3_mutex_leave(db->mutex); + break; +@@ -165204,6 +187239,117 @@ SQLITE_API int sqlite3_test_control(int op, ...){ + sqlite3ResultIntReal(pCtx); + break; + } ++ ++ /* sqlite3_test_control(SQLITE_TESTCTRL_SEEK_COUNT, ++ ** sqlite3 *db, // Database connection ++ ** u64 *pnSeek // Write seek count here ++ ** ); ++ ** ++ ** This test-control queries the seek-counter on the "main" database ++ ** file. The seek-counter is written into *pnSeek and is then reset. ++ ** The seek-count is only available if compiled with SQLITE_DEBUG. ++ */ ++ case SQLITE_TESTCTRL_SEEK_COUNT: { ++ sqlite3 *db = va_arg(ap, sqlite3*); ++ u64 *pn = va_arg(ap, sqlite3_uint64*); ++ *pn = sqlite3BtreeSeekCount(db->aDb->pBt); ++ (void)db; /* Silence harmless unused variable warning */ ++ break; ++ } ++ ++ /* sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, op, ptr) ++ ** ++ ** "ptr" is a pointer to a u32. ++ ** ++ ** op==0 Store the current sqlite3TreeTrace in *ptr ++ ** op==1 Set sqlite3TreeTrace to the value *ptr ++ ** op==2 Store the current sqlite3WhereTrace in *ptr ++ ** op==3 Set sqlite3WhereTrace to the value *ptr ++ */ ++ case SQLITE_TESTCTRL_TRACEFLAGS: { ++ int opTrace = va_arg(ap, int); ++ u32 *ptr = va_arg(ap, u32*); ++ switch( opTrace ){ ++ case 0: *ptr = sqlite3TreeTrace; break; ++ case 1: sqlite3TreeTrace = *ptr; break; ++ case 2: *ptr = sqlite3WhereTrace; break; ++ case 3: sqlite3WhereTrace = *ptr; break; ++ } ++ break; ++ } ++ ++ /* sqlite3_test_control(SQLITE_TESTCTRL_LOGEST, ++ ** double fIn, // Input value ++ ** int *pLogEst, // sqlite3LogEstFromDouble(fIn) ++ ** u64 *pInt, // sqlite3LogEstToInt(*pLogEst) ++ ** int *pLogEst2 // sqlite3LogEst(*pInt) ++ ** ); ++ ** ++ ** Test access for the LogEst conversion routines. ++ */ ++ case SQLITE_TESTCTRL_LOGEST: { ++ double rIn = va_arg(ap, double); ++ LogEst rLogEst = sqlite3LogEstFromDouble(rIn); ++ int *pI1 = va_arg(ap,int*); ++ u64 *pU64 = va_arg(ap,u64*); ++ int *pI2 = va_arg(ap,int*); ++ *pI1 = rLogEst; ++ *pU64 = sqlite3LogEstToInt(rLogEst); ++ *pI2 = sqlite3LogEst(*pU64); ++ break; ++ } ++ ++#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) ++ /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) ++ ** ++ ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value ++ ** of the id-th tuning parameter to *piValue. If "id" is between -1 ++ ** and -SQLITE_NTUNE, then write the current value of the (-id)-th ++ ** tuning parameter into *piValue. ++ ** ++ ** Tuning parameters are for use during transient development builds, ++ ** to help find the best values for constants in the query planner. ++ ** Access tuning parameters using the Tuning(ID) macro. Set the ++ ** parameters in the CLI using ".testctrl tune ID VALUE". ++ ** ++ ** Transient use only. Tuning parameters should not be used in ++ ** checked-in code. ++ */ ++ case SQLITE_TESTCTRL_TUNE: { ++ int id = va_arg(ap, int); ++ int *piValue = va_arg(ap, int*); ++ if( id>0 && id<=SQLITE_NTUNE ){ ++ Tuning(id) = *piValue; ++ }else if( id<0 && id>=-SQLITE_NTUNE ){ ++ *piValue = Tuning(-id); ++ }else{ ++ rc = SQLITE_NOTFOUND; ++ } ++ break; ++ } ++#endif ++ ++ /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff); ++ ** ++ ** Activate or deactivate validation of JSONB that is generated from ++ ** text. Off by default, as the validation is slow. Validation is ++ ** only available if compiled using SQLITE_DEBUG. ++ ** ++ ** If onOff is initially 1, then turn it on. If onOff is initially ++ ** off, turn it off. If onOff is initially -1, then change onOff ++ ** to be the current setting. ++ */ ++ case SQLITE_TESTCTRL_JSON_SELFCHECK: { ++#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) ++ int *pOnOff = va_arg(ap, int*); ++ if( *pOnOff<0 ){ ++ *pOnOff = sqlite3Config.bJsonSelfcheck; ++ }else{ ++ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff); ++ } ++#endif ++ break; ++ } + } + va_end(ap); + #endif /* SQLITE_UNTESTABLE */ +@@ -165244,7 +187390,7 @@ static char *appendText(char *p, const char *z){ + ** Memory layout must be compatible with that generated by the pager + ** and expected by sqlite3_uri_parameter() and databaseName(). + */ +-SQLITE_API char *sqlite3_create_filename( ++SQLITE_API const char *sqlite3_create_filename( + const char *zDatabase, + const char *zJournal, + const char *zWal, +@@ -165280,10 +187426,10 @@ SQLITE_API char *sqlite3_create_filename( + ** error to call this routine with any parameter other than a pointer + ** previously obtained from sqlite3_create_filename() or a NULL pointer. + */ +-SQLITE_API void sqlite3_free_filename(char *p){ ++SQLITE_API void sqlite3_free_filename(const char *p){ + if( p==0 ) return; +- p = (char*)databaseName(p); +- sqlite3_free(p - 4); ++ p = databaseName(p); ++ sqlite3_free((char*)p - 4); + } + + +@@ -165311,7 +187457,7 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N){ + if( zFilename==0 || N<0 ) return 0; + zFilename = databaseName(zFilename); + zFilename += sqlite3Strlen30(zFilename) + 1; +- while( zFilename[0] && (N--)>0 ){ ++ while( ALWAYS(zFilename) && zFilename[0] && (N--)>0 ){ + zFilename += sqlite3Strlen30(zFilename) + 1; + zFilename += sqlite3Strlen30(zFilename) + 1; + } +@@ -165354,12 +187500,14 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64( + ** corruption. + */ + SQLITE_API const char *sqlite3_filename_database(const char *zFilename){ ++ if( zFilename==0 ) return 0; + return databaseName(zFilename); + } + SQLITE_API const char *sqlite3_filename_journal(const char *zFilename){ ++ if( zFilename==0 ) return 0; + zFilename = databaseName(zFilename); + zFilename += sqlite3Strlen30(zFilename) + 1; +- while( zFilename[0] ){ ++ while( ALWAYS(zFilename) && zFilename[0] ){ + zFilename += sqlite3Strlen30(zFilename) + 1; + zFilename += sqlite3Strlen30(zFilename) + 1; + } +@@ -165370,7 +187518,7 @@ SQLITE_API const char *sqlite3_filename_wal(const char *zFilename){ + return 0; + #else + zFilename = sqlite3_filename_journal(zFilename); +- zFilename += sqlite3Strlen30(zFilename) + 1; ++ if( zFilename ) zFilename += sqlite3Strlen30(zFilename) + 1; + return zFilename; + #endif + } +@@ -165383,6 +187531,24 @@ SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ + return iDb<0 ? 0 : db->aDb[iDb].pBt; + } + ++/* ++** Return the name of the N-th database schema. Return NULL if N is out ++** of range. ++*/ ++SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N){ ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ){ ++ (void)SQLITE_MISUSE_BKPT; ++ return 0; ++ } ++#endif ++ if( N<0 || N>=db->nDb ){ ++ return 0; ++ }else{ ++ return db->aDb[N].zDbSName; ++ } ++} ++ + /* + ** Return the filename of the database associated with a database + ** connection. +@@ -165439,8 +187605,12 @@ SQLITE_API int sqlite3_snapshot_get( + int iDb = sqlite3FindDbName(db, zDb); + if( iDb==0 || iDb>1 ){ + Btree *pBt = db->aDb[iDb].pBt; +- if( 0==sqlite3BtreeIsInTrans(pBt) ){ ++ if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){ ++ Pager *pPager = sqlite3BtreePager(pBt); ++ i64 dummy = 0; ++ sqlite3PagerSnapshotOpen(pPager, (sqlite3_snapshot*)&dummy); + rc = sqlite3BtreeBeginTrans(pBt, 0, 0); ++ sqlite3PagerSnapshotOpen(pPager, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); + } +@@ -165454,7 +187624,7 @@ SQLITE_API int sqlite3_snapshot_get( + } + + /* +-** Open a read-transaction on the snapshot idendified by pSnapshot. ++** Open a read-transaction on the snapshot identified by pSnapshot. + */ + SQLITE_API int sqlite3_snapshot_open( + sqlite3 *db, +@@ -165475,10 +187645,10 @@ SQLITE_API int sqlite3_snapshot_open( + iDb = sqlite3FindDbName(db, zDb); + if( iDb==0 || iDb>1 ){ + Btree *pBt = db->aDb[iDb].pBt; +- if( sqlite3BtreeIsInTrans(pBt)==0 ){ ++ if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ){ + Pager *pPager = sqlite3BtreePager(pBt); + int bUnlock = 0; +- if( sqlite3BtreeIsInReadTrans(pBt) ){ ++ if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_NONE ){ + if( db->nVdbeActive==0 ){ + rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot); + if( rc==SQLITE_OK ){ +@@ -165514,8 +187684,8 @@ SQLITE_API int sqlite3_snapshot_open( + */ + SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){ + int rc = SQLITE_ERROR; +- int iDb; + #ifndef SQLITE_OMIT_WAL ++ int iDb; + + #ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ +@@ -165527,7 +187697,7 @@ SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){ + iDb = sqlite3FindDbName(db, zDb); + if( iDb==0 || iDb>1 ){ + Btree *pBt = db->aDb[iDb].pBt; +- if( 0==sqlite3BtreeIsInReadTrans(pBt) ){ ++ if( SQLITE_TXN_NONE==sqlite3BtreeTxnState(pBt) ){ + rc = sqlite3BtreeBeginTrans(pBt, 0, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt)); +@@ -165561,7 +187731,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ + int nOpt; + const char **azCompileOpt; + +-#if SQLITE_ENABLE_API_ARMOR ++#ifdef SQLITE_ENABLE_API_ARMOR + if( zOptName==0 ){ + (void)SQLITE_MISUSE_BKPT; + return 0; +@@ -165756,6 +187926,9 @@ SQLITE_API int sqlite3_unlock_notify( + ){ + int rc = SQLITE_OK; + ++#ifdef SQLITE_ENABLE_API_ARMOR ++ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; ++#endif + sqlite3_mutex_enter(db->mutex); + enterMutex(); + +@@ -166026,7 +188199,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ + ** Here, array { X } means zero or more occurrences of X, adjacent in + ** memory. A "position" is an index of a token in the token stream + ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur +-** in the same logical place as the position element, and act as sentinals ++** in the same logical place as the position element, and act as sentinels + ** ending a position list array. POS_END is 0. POS_COLUMN is 1. + ** The positions numbers are not stored literally but rather as two more + ** than the difference from the prior position, or the just the position plus +@@ -166245,6 +188418,13 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ + #ifndef _FTSINT_H + #define _FTSINT_H + ++/* #include */ ++/* #include */ ++/* #include */ ++/* #include */ ++/* #include */ ++/* #include */ ++ + #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) + # define NDEBUG 1 + #endif +@@ -166646,7 +188826,7 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi + ** is used for assert() conditions that are true only if it can be + ** guranteed that the database is not corrupt. + */ +-#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) ++#ifdef SQLITE_DEBUG + SQLITE_API extern int sqlite3_fts3_may_be_corrupt; + # define assert_fts3_nc(x) assert(sqlite3_fts3_may_be_corrupt || (x)) + #else +@@ -166663,17 +188843,18 @@ SQLITE_API extern int sqlite3_fts3_may_be_corrupt; + ** Macros indicating that conditional expressions are always true or + ** false. + */ +-#ifdef SQLITE_COVERAGE_TEST +-# define ALWAYS(x) (1) +-# define NEVER(X) (0) +-#elif defined(SQLITE_DEBUG) +-# define ALWAYS(x) sqlite3Fts3Always((x)!=0) +-# define NEVER(x) sqlite3Fts3Never((x)!=0) +-SQLITE_PRIVATE int sqlite3Fts3Always(int b); +-SQLITE_PRIVATE int sqlite3Fts3Never(int b); ++#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) ++# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 ++#endif ++#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) ++# define ALWAYS(X) (1) ++# define NEVER(X) (0) ++#elif !defined(NDEBUG) ++# define ALWAYS(X) ((X)?1:(assert(0),0)) ++# define NEVER(X) ((X)?(assert(0),1):0) + #else +-# define ALWAYS(x) (x) +-# define NEVER(x) (x) ++# define ALWAYS(X) (X) ++# define NEVER(X) (X) + #endif + + /* +@@ -166713,6 +188894,19 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */ + + #define deliberate_fall_through + ++/* ++** Macros needed to provide flexible arrays in a portable way ++*/ ++#ifndef offsetof ++# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) ++#endif ++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ++# define FLEXARRAY ++#else ++# define FLEXARRAY 1 ++#endif ++ ++ + #endif /* SQLITE_AMALGAMATION */ + + #ifdef SQLITE_DEBUG +@@ -166776,6 +188970,7 @@ struct Fts3Table { + int nPgsz; /* Page size for host database */ + char *zSegmentsTbl; /* Name of %_segments table */ + sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ ++ int iSavepoint; + + /* + ** The following array of hash tables is used to buffer pending index +@@ -166816,7 +189011,7 @@ struct Fts3Table { + #endif + + #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) +- /* True to disable the incremental doclist optimization. This is controled ++ /* True to disable the incremental doclist optimization. This is controlled + ** by special insert command 'test-no-incr-doclist'. */ + int bNoIncrDoclist; + +@@ -166868,7 +189063,7 @@ struct Fts3Cursor { + + /* + ** The Fts3Cursor.eSearch member is always set to one of the following. +-** Actualy, Fts3Cursor.eSearch can be greater than or equal to ++** Actually, Fts3Cursor.eSearch can be greater than or equal to + ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index + ** of the column to be searched. For example, in + ** +@@ -166941,9 +189136,13 @@ struct Fts3Phrase { + */ + int nToken; /* Number of tokens in the phrase */ + int iColumn; /* Index of column this phrase must match */ +- Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ ++ Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */ + }; + ++/* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */ ++#define SZ_FTS3PHRASE(N) \ ++ (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken)) ++ + /* + ** A tree of these objects forms the RHS of a MATCH operator. + ** +@@ -167069,7 +189268,7 @@ struct Fts3MultiSegReader { + int nAdvance; /* How many seg-readers to advance */ + Fts3SegFilter *pFilter; /* Pointer to filter object */ + char *aBuffer; /* Buffer to merge doclists in */ +- int nBuffer; /* Allocated size of aBuffer[] in bytes */ ++ i64 nBuffer; /* Allocated size of aBuffer[] in bytes */ + + int iColFilter; /* If >=0, filter for this column */ + int bRestart; +@@ -167132,6 +189331,7 @@ SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); + SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash*); + SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db); + #endif ++SQLITE_PRIVATE void *sqlite3Fts3MallocZero(i64 nByte); + + SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer(sqlite3_tokenizer *, int, const char *, int, + sqlite3_tokenizer_cursor ** +@@ -167149,9 +189349,10 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( + SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **); + SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); + SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); ++SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor*, Fts3Expr*); + + /* fts3_tokenize_vtab.c */ +-SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *); ++SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *, void(*xDestroy)(void*)); + + /* fts3_unicode2.c (functions generated by parsing unicode text files) */ + #ifndef SQLITE_DISABLE_FTS3_UNICODE +@@ -167160,6 +189361,10 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int); + SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); + #endif + ++SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); ++ ++SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk); ++ + #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ + #endif /* _FTSINT_H */ + +@@ -167171,12 +189376,6 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); + # define SQLITE_CORE 1 + #endif + +-/* #include */ +-/* #include */ +-/* #include */ +-/* #include */ +-/* #include */ +-/* #include */ + + /* #include "fts3.h" */ + #ifndef SQLITE_CORE +@@ -167184,25 +189383,26 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); + SQLITE_EXTENSION_INIT1 + #endif + ++typedef struct Fts3HashWrapper Fts3HashWrapper; ++struct Fts3HashWrapper { ++ Fts3Hash hash; /* Hash table */ ++ int nRef; /* Number of pointers to this object */ ++}; ++ + static int fts3EvalNext(Fts3Cursor *pCsr); + static int fts3EvalStart(Fts3Cursor *pCsr); + static int fts3TermSegReaderCursor( + Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); + +-#ifndef SQLITE_AMALGAMATION +-# if defined(SQLITE_DEBUG) +-SQLITE_PRIVATE int sqlite3Fts3Always(int b) { assert( b ); return b; } +-SQLITE_PRIVATE int sqlite3Fts3Never(int b) { assert( !b ); return b; } +-# endif +-#endif +- + /* + ** This variable is set to false when running tests for which the on disk + ** structures should not be corrupt. Otherwise, true. If it is false, extra + ** assert() conditions in the fts3 code are activated - conditions that are + ** only true if it is guaranteed that the fts3 database is not corrupt. + */ ++#ifdef SQLITE_DEBUG + SQLITE_API int sqlite3_fts3_may_be_corrupt = 1; ++#endif + + /* + ** Write a 64-bit variable-length integer to memory starting at p[0]. +@@ -167515,6 +189715,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){ + + zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid"); + sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); ++ sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS); + + /* Create a list of user columns for the virtual table */ + zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); +@@ -168053,7 +190254,7 @@ static int fts3InitVtab( + sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ + char **pzErr /* Write any error message here */ + ){ +- Fts3Hash *pHash = (Fts3Hash *)pAux; ++ Fts3Hash *pHash = &((Fts3HashWrapper*)pAux)->hash; + Fts3Table *p = 0; /* Pointer to allocated vtab */ + int rc = SQLITE_OK; /* Return code */ + int i; /* Iterator variable */ +@@ -168773,7 +190974,7 @@ static int fts3ScanInteriorNode( + char *zBuffer = 0; /* Buffer to load terms into */ + i64 nAlloc = 0; /* Size of allocated buffer */ + int isFirstTerm = 1; /* True when processing first term on page */ +- sqlite3_int64 iChild; /* Block id of child node to descend to */ ++ u64 iChild; /* Block id of child node to descend to */ + int nBuffer = 0; /* Total term size */ + + /* Skip over the 'height' varint that occurs at the start of every +@@ -168789,8 +190990,8 @@ static int fts3ScanInteriorNode( + ** table, then there are always 20 bytes of zeroed padding following the + ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). + */ +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); +- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); ++ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); + if( zCsr>zEnd ){ + return FTS_CORRUPT_VTAB; + } +@@ -168843,20 +191044,20 @@ static int fts3ScanInteriorNode( + */ + cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); + if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ +- *piFirst = iChild; ++ *piFirst = (i64)iChild; + piFirst = 0; + } + + if( piLast && cmp<0 ){ +- *piLast = iChild; ++ *piLast = (i64)iChild; + piLast = 0; + } + + iChild++; + }; + +- if( piFirst ) *piFirst = iChild; +- if( piLast ) *piLast = iChild; ++ if( piFirst ) *piFirst = (i64)iChild; ++ if( piLast ) *piLast = (i64)iChild; + + finish_scan: + sqlite3_free(zBuffer); +@@ -169218,10 +191419,15 @@ static int fts3PoslistPhraseMerge( + if( *p1==POS_COLUMN ){ + p1++; + p1 += fts3GetVarint32(p1, &iCol1); ++ /* iCol1==0 indicates corruption. Column 0 does not have a POS_COLUMN ++ ** entry, so this is actually end-of-doclist. */ ++ if( iCol1==0 ) return 0; + } + if( *p2==POS_COLUMN ){ + p2++; + p2 += fts3GetVarint32(p2, &iCol2); ++ /* As above, iCol2==0 indicates corruption. */ ++ if( iCol2==0 ) return 0; + } + + while( 1 ){ +@@ -169508,7 +191714,7 @@ static int fts3DoclistOrMerge( + ** sizes of the two inputs, plus enough space for exactly one of the input + ** docids to grow. + ** +- ** A symetric argument may be made if the doclists are in descending ++ ** A symmetric argument may be made if the doclists are in descending + ** order. + */ + aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); +@@ -169763,7 +191969,7 @@ static int fts3TermSelectMerge( + ** + ** Similar padding is added in the fts3DoclistOrMerge() function. + */ +- pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1); ++ pTS->aaOutput[0] = sqlite3_malloc64((i64)nDoclist + FTS3_VARINT_MAX + 1); + pTS->anOutput[0] = nDoclist; + if( pTS->aaOutput[0] ){ + memcpy(pTS->aaOutput[0], aDoclist, nDoclist); +@@ -170462,14 +192668,20 @@ static int fts3SetHasStat(Fts3Table *p){ + */ + static int fts3BeginMethod(sqlite3_vtab *pVtab){ + Fts3Table *p = (Fts3Table*)pVtab; ++ int rc; + UNUSED_PARAMETER(pVtab); + assert( p->pSegments==0 ); + assert( p->nPendingData==0 ); + assert( p->inTransaction!=1 ); +- TESTONLY( p->inTransaction = 1 ); +- TESTONLY( p->mxSavepoint = -1; ); + p->nLeafAdd = 0; +- return fts3SetHasStat(p); ++ rc = fts3SetHasStat(p); ++#ifdef SQLITE_DEBUG ++ if( rc==SQLITE_OK ){ ++ p->inTransaction = 1; ++ p->mxSavepoint = -1; ++ } ++#endif ++ return rc; + } + + /* +@@ -170758,6 +192970,8 @@ static int fts3RenameMethod( + rc = sqlite3Fts3PendingTermsFlush(p); + } + ++ p->bIgnoreSavepoint = 1; ++ + if( p->zContentTbl==0 ){ + fts3DbExec(&rc, db, + "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", +@@ -170785,6 +192999,8 @@ static int fts3RenameMethod( + "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';", + p->zDb, p->zName, zName + ); ++ ++ p->bIgnoreSavepoint = 0; + return rc; + } + +@@ -170795,12 +193011,28 @@ static int fts3RenameMethod( + */ + static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ + int rc = SQLITE_OK; +- UNUSED_PARAMETER(iSavepoint); +- assert( ((Fts3Table *)pVtab)->inTransaction ); +- assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint ); +- TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint ); +- if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){ +- rc = fts3SyncMethod(pVtab); ++ Fts3Table *pTab = (Fts3Table*)pVtab; ++ assert( pTab->inTransaction ); ++ assert( pTab->mxSavepoint<=iSavepoint ); ++ TESTONLY( pTab->mxSavepoint = iSavepoint ); ++ ++ if( pTab->bIgnoreSavepoint==0 ){ ++ if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){ ++ char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')", ++ pTab->zDb, pTab->zName, pTab->zName ++ ); ++ if( zSql ){ ++ pTab->bIgnoreSavepoint = 1; ++ rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0); ++ pTab->bIgnoreSavepoint = 0; ++ sqlite3_free(zSql); ++ }else{ ++ rc = SQLITE_NOMEM; ++ } ++ } ++ if( rc==SQLITE_OK ){ ++ pTab->iSavepoint = iSavepoint+1; ++ } + } + return rc; + } +@@ -170811,12 +193043,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ + ** This is a no-op. + */ + static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ +- TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); +- UNUSED_PARAMETER(iSavepoint); +- UNUSED_PARAMETER(pVtab); +- assert( p->inTransaction ); +- assert( p->mxSavepoint >= iSavepoint ); +- TESTONLY( p->mxSavepoint = iSavepoint-1 ); ++ Fts3Table *pTab = (Fts3Table*)pVtab; ++ assert( pTab->inTransaction ); ++ assert( pTab->mxSavepoint >= iSavepoint ); ++ TESTONLY( pTab->mxSavepoint = iSavepoint-1 ); ++ pTab->iSavepoint = iSavepoint; + return SQLITE_OK; + } + +@@ -170826,11 +193057,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ + ** Discard the contents of the pending terms table. + */ + static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ +- Fts3Table *p = (Fts3Table*)pVtab; ++ Fts3Table *pTab = (Fts3Table*)pVtab; + UNUSED_PARAMETER(iSavepoint); +- assert( p->inTransaction ); +- TESTONLY( p->mxSavepoint = iSavepoint ); +- sqlite3Fts3PendingTermsClear(p); ++ assert( pTab->inTransaction ); ++ TESTONLY( pTab->mxSavepoint = iSavepoint ); ++ if( (iSavepoint+1)<=pTab->iSavepoint ){ ++ sqlite3Fts3PendingTermsClear(pTab); ++ } + return SQLITE_OK; + } + +@@ -170849,8 +193082,42 @@ static int fts3ShadowName(const char *zName){ + return 0; + } + ++/* ++** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual ++** table. ++*/ ++static int fts3IntegrityMethod( ++ sqlite3_vtab *pVtab, /* The virtual table to be checked */ ++ const char *zSchema, /* Name of schema in which pVtab lives */ ++ const char *zTabname, /* Name of the pVTab table */ ++ int isQuick, /* True if this is a quick_check */ ++ char **pzErr /* Write error message here */ ++){ ++ Fts3Table *p = (Fts3Table*)pVtab; ++ int rc = SQLITE_OK; ++ int bOk = 0; ++ ++ UNUSED_PARAMETER(isQuick); ++ rc = sqlite3Fts3IntegrityCheck(p, &bOk); ++ assert( rc!=SQLITE_CORRUPT_VTAB ); ++ if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ ++ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" ++ " FTS%d table %s.%s: %s", ++ p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); ++ if( *pzErr ) rc = SQLITE_OK; ++ }else if( rc==SQLITE_OK && bOk==0 ){ ++ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s", ++ p->bFts4 ? 4 : 3, zSchema, zTabname); ++ if( *pzErr==0 ) rc = SQLITE_NOMEM; ++ } ++ sqlite3Fts3SegmentsClose(p); ++ return rc; ++} ++ ++ ++ + static const sqlite3_module fts3Module = { +- /* iVersion */ 3, ++ /* iVersion */ 4, + /* xCreate */ fts3CreateMethod, + /* xConnect */ fts3ConnectMethod, + /* xBestIndex */ fts3BestIndexMethod, +@@ -170874,6 +193141,7 @@ static const sqlite3_module fts3Module = { + /* xRelease */ fts3ReleaseMethod, + /* xRollbackTo */ fts3RollbackToMethod, + /* xShadowName */ fts3ShadowName, ++ /* xIntegrity */ fts3IntegrityMethod, + }; + + /* +@@ -170882,9 +193150,12 @@ static const sqlite3_module fts3Module = { + ** allocated for the tokenizer hash table. + */ + static void hashDestroy(void *p){ +- Fts3Hash *pHash = (Fts3Hash *)p; +- sqlite3Fts3HashClear(pHash); +- sqlite3_free(pHash); ++ Fts3HashWrapper *pHash = (Fts3HashWrapper *)p; ++ pHash->nRef--; ++ if( pHash->nRef<=0 ){ ++ sqlite3Fts3HashClear(&pHash->hash); ++ sqlite3_free(pHash); ++ } + } + + /* +@@ -170914,7 +193185,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const + */ + SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ + int rc = SQLITE_OK; +- Fts3Hash *pHash = 0; ++ Fts3HashWrapper *pHash = 0; + const sqlite3_tokenizer_module *pSimple = 0; + const sqlite3_tokenizer_module *pPorter = 0; + #ifndef SQLITE_DISABLE_FTS3_UNICODE +@@ -170942,23 +193213,24 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ + sqlite3Fts3PorterTokenizerModule(&pPorter); + + /* Allocate and initialize the hash-table used to store tokenizers. */ +- pHash = sqlite3_malloc(sizeof(Fts3Hash)); ++ pHash = sqlite3_malloc(sizeof(Fts3HashWrapper)); + if( !pHash ){ + rc = SQLITE_NOMEM; + }else{ +- sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); ++ sqlite3Fts3HashInit(&pHash->hash, FTS3_HASH_STRING, 1); ++ pHash->nRef = 0; + } + + /* Load the built-in tokenizers into the hash table */ + if( rc==SQLITE_OK ){ +- if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) +- || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) ++ if( sqlite3Fts3HashInsert(&pHash->hash, "simple", 7, (void *)pSimple) ++ || sqlite3Fts3HashInsert(&pHash->hash, "porter", 7, (void *)pPorter) + + #ifndef SQLITE_DISABLE_FTS3_UNICODE +- || sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode) ++ || sqlite3Fts3HashInsert(&pHash->hash, "unicode61", 10, (void *)pUnicode) + #endif + #ifdef SQLITE_ENABLE_ICU +- || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) ++ || (pIcu && sqlite3Fts3HashInsert(&pHash->hash, "icu", 4, (void *)pIcu)) + #endif + ){ + rc = SQLITE_NOMEM; +@@ -170967,7 +193239,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ + + #ifdef SQLITE_TEST + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts3ExprInitTestInterface(db, pHash); ++ rc = sqlite3Fts3ExprInitTestInterface(db, &pHash->hash); + } + #endif + +@@ -170976,23 +193248,26 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ + ** module with sqlite. + */ + if( SQLITE_OK==rc +- && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) ++ && SQLITE_OK==(rc=sqlite3Fts3InitHashTable(db,&pHash->hash,"fts3_tokenizer")) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 2)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1)) + ){ ++ pHash->nRef++; + rc = sqlite3_create_module_v2( + db, "fts3", &fts3Module, (void *)pHash, hashDestroy + ); + if( rc==SQLITE_OK ){ ++ pHash->nRef++; + rc = sqlite3_create_module_v2( +- db, "fts4", &fts3Module, (void *)pHash, 0 ++ db, "fts4", &fts3Module, (void *)pHash, hashDestroy + ); + } + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts3InitTok(db, (void *)pHash); ++ pHash->nRef++; ++ rc = sqlite3Fts3InitTok(db, (void *)pHash, hashDestroy); + } + return rc; + } +@@ -171001,7 +193276,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ + /* An error has occurred. Delete the hash table and return the error code. */ + assert( rc!=SQLITE_OK ); + if( pHash ){ +- sqlite3Fts3HashClear(pHash); ++ sqlite3Fts3HashClear(&pHash->hash); + sqlite3_free(pHash); + } + return rc; +@@ -171170,8 +193445,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ + char *aPoslist = 0; /* Position list for deferred tokens */ + int nPoslist = 0; /* Number of bytes in aPoslist */ + int iPrev = -1; /* Token number of previous deferred token */ +- +- assert( pPhrase->doclist.bFreeList==0 ); ++ char *aFree = (pPhrase->doclist.bFreeList ? pPhrase->doclist.pList : 0); + + for(iToken=0; iTokennToken; iToken++){ + Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; +@@ -171185,6 +193459,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ + + if( pList==0 ){ + sqlite3_free(aPoslist); ++ sqlite3_free(aFree); + pPhrase->doclist.pList = 0; + pPhrase->doclist.nList = 0; + return SQLITE_OK; +@@ -171205,6 +193480,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ + nPoslist = (int)(aOut - aPoslist); + if( nPoslist==0 ){ + sqlite3_free(aPoslist); ++ sqlite3_free(aFree); + pPhrase->doclist.pList = 0; + pPhrase->doclist.nList = 0; + return SQLITE_OK; +@@ -171237,13 +193513,14 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ + nDistance = iPrev - nMaxUndeferred; + } + +- aOut = (char *)sqlite3_malloc(nPoslist+8); ++ aOut = (char *)sqlite3Fts3MallocZero(((i64)nPoslist)+FTS3_BUFFER_PADDING); + if( !aOut ){ + sqlite3_free(aPoslist); + return SQLITE_NOMEM; + } + + pPhrase->doclist.pList = aOut; ++ assert( p1 && p2 ); + if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ + pPhrase->doclist.bFreeList = 1; + pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList); +@@ -171256,6 +193533,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ + } + } + ++ if( pPhrase->doclist.pList!=aFree ) sqlite3_free(aFree); + return SQLITE_OK; + } + #endif /* SQLITE_DISABLE_FTS4_DEFERRED */ +@@ -171348,7 +193626,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev( + + assert( nDoclist>0 ); + assert( *pbEof==0 ); +- assert( p || *piDocid==0 ); ++ assert_fts3_nc( p || *piDocid==0 ); + assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); + + if( p==0 ){ +@@ -171534,7 +193812,7 @@ static int incrPhraseTokenNext( + ** + ** * does not contain any deferred tokens. + ** +-** Advance it to the next matching documnent in the database and populate ++** Advance it to the next matching document in the database and populate + ** the Fts3Doclist.pList and nList fields. + ** + ** If there is no "next" entry and no error occurs, then *pbEof is set to +@@ -171604,7 +193882,7 @@ static int fts3EvalIncrPhraseNext( + if( bEof==0 ){ + int nList = 0; + int nByte = a[p->nToken-1].nList; +- char *aDoclist = sqlite3_malloc(nByte+FTS3_BUFFER_PADDING); ++ char *aDoclist = sqlite3_malloc64((i64)nByte+FTS3_BUFFER_PADDING); + if( !aDoclist ) return SQLITE_NOMEM; + memcpy(aDoclist, a[p->nToken-1].pList, nByte+1); + memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING); +@@ -171998,16 +194276,15 @@ static int fts3EvalStart(Fts3Cursor *pCsr){ + #ifndef SQLITE_DISABLE_FTS4_DEFERRED + if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){ + Fts3TokenAndCost *aTC; +- Fts3Expr **apOr; + aTC = (Fts3TokenAndCost *)sqlite3_malloc64( + sizeof(Fts3TokenAndCost) * nToken + + sizeof(Fts3Expr *) * nOr * 2 + ); +- apOr = (Fts3Expr **)&aTC[nToken]; + + if( !aTC ){ + rc = SQLITE_NOMEM; + }else{ ++ Fts3Expr **apOr = (Fts3Expr **)&aTC[nToken]; + int ii; + Fts3TokenAndCost *pTC = aTC; + Fts3Expr **ppOr = apOr; +@@ -172088,9 +194365,9 @@ static int fts3EvalNearTrim( + ); + if( res ){ + nNew = (int)(pOut - pPhrase->doclist.pList) - 1; +- if( nNew>=0 ){ ++ assert_fts3_nc( nNew<=pPhrase->doclist.nList && nNew>0 ); ++ if( nNew>=0 && nNew<=pPhrase->doclist.nList ){ + assert( pPhrase->doclist.pList[nNew]=='\0' ); +- assert( nNew<=pPhrase->doclist.nList && nNew>0 ); + memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); + pPhrase->doclist.nList = nNew; + } +@@ -172147,9 +194424,8 @@ static void fts3EvalNextRow( + Fts3Expr *pExpr, /* Expr. to advance to next matching row */ + int *pRc /* IN/OUT: Error code */ + ){ +- if( *pRc==SQLITE_OK ){ ++ if( *pRc==SQLITE_OK && pExpr->bEof==0 ){ + int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */ +- assert( pExpr->bEof==0 ); + pExpr->bStart = 1; + + switch( pExpr->eType ){ +@@ -172213,8 +194489,8 @@ static void fts3EvalNextRow( + Fts3Expr *pRight = pExpr->pRight; + sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); + +- assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); +- assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); ++ assert_fts3_nc( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); ++ assert_fts3_nc( pRight->bStart || pLeft->iDocid==pRight->iDocid ); + + if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ + fts3EvalNextRow(pCsr, pLeft, pRc); +@@ -172322,7 +194598,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ + nTmp += p->pRight->pPhrase->doclist.nList; + } + nTmp += p->pPhrase->doclist.nList; +- aTmp = sqlite3_malloc64(nTmp*2); ++ aTmp = sqlite3_malloc64(nTmp*2 + FTS3_VARINT_MAX); + if( !aTmp ){ + *pRc = SQLITE_NOMEM; + res = 0; +@@ -172431,11 +194707,10 @@ static int fts3EvalTestExpr( + + default: { + #ifndef SQLITE_DISABLE_FTS4_DEFERRED +- if( pCsr->pDeferred +- && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred) +- ){ ++ if( pCsr->pDeferred && (pExpr->bDeferred || ( ++ pExpr->iDocid==pCsr->iPrevId && pExpr->pPhrase->doclist.pList ++ ))){ + Fts3Phrase *pPhrase = pExpr->pPhrase; +- assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 ); + if( pExpr->bDeferred ){ + fts3EvalInvalidatePoslist(pPhrase); + } +@@ -172544,7 +194819,7 @@ static int fts3EvalNext(Fts3Cursor *pCsr){ + } + + /* +-** Restart interation for expression pExpr so that the next call to ++** Restart iteration for expression pExpr so that the next call to + ** fts3EvalNext() visits the first row. Do not allow incremental + ** loading or merging of phrase doclists for this iteration. + ** +@@ -172587,6 +194862,24 @@ static void fts3EvalRestart( + } + } + ++/* ++** Expression node pExpr is an MSR phrase. This function restarts pExpr ++** so that it is a regular phrase query, not an MSR. SQLITE_OK is returned ++** if successful, or an SQLite error code otherwise. ++*/ ++SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor *pCsr, Fts3Expr *pExpr){ ++ int rc = SQLITE_OK; ++ if( pExpr->bEof==0 ){ ++ i64 iDocid = pExpr->iDocid; ++ fts3EvalRestart(pCsr, pExpr, &rc); ++ while( rc==SQLITE_OK && pExpr->iDocid!=iDocid ){ ++ fts3EvalNextRow(pCsr, pExpr, &rc); ++ if( pExpr->bEof ) rc = FTS_CORRUPT_VTAB; ++ } ++ } ++ return rc; ++} ++ + /* + ** After allocating the Fts3Expr.aMI[] array for each phrase in the + ** expression rooted at pExpr, the cursor iterates through all rows matched +@@ -172626,6 +194919,22 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr, int nCol){ + } + } + ++/* ++** This is an sqlite3Fts3ExprIterate() callback. If the Fts3Expr.aMI[] array ++** has not yet been allocated, allocate and zero it. Otherwise, just zero ++** it. ++*/ ++static int fts3AllocateMSI(Fts3Expr *pExpr, int iPhrase, void *pCtx){ ++ Fts3Table *pTab = (Fts3Table*)pCtx; ++ UNUSED_PARAMETER(iPhrase); ++ if( pExpr->aMI==0 ){ ++ pExpr->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); ++ if( pExpr->aMI==0 ) return SQLITE_NOMEM; ++ } ++ memset(pExpr->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); ++ return SQLITE_OK; ++} ++ + /* + ** Expression pExpr must be of type FTSQUERY_PHRASE. + ** +@@ -172647,7 +194956,6 @@ static int fts3EvalGatherStats( + if( pExpr->aMI==0 ){ + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + Fts3Expr *pRoot; /* Root of NEAR expression */ +- Fts3Expr *p; /* Iterator used for several purposes */ + + sqlite3_int64 iPrevId = pCsr->iPrevId; + sqlite3_int64 iDocid; +@@ -172655,7 +194963,9 @@ static int fts3EvalGatherStats( + + /* Find the root of the NEAR expression */ + pRoot = pExpr; +- while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){ ++ while( pRoot->pParent ++ && (pRoot->pParent->eType==FTSQUERY_NEAR || pRoot->bDeferred) ++ ){ + pRoot = pRoot->pParent; + } + iDocid = pRoot->iDocid; +@@ -172663,14 +194973,8 @@ static int fts3EvalGatherStats( + assert( pRoot->bStart ); + + /* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */ +- for(p=pRoot; p; p=p->pLeft){ +- Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight); +- assert( pE->aMI==0 ); +- pE->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); +- if( !pE->aMI ) return SQLITE_NOMEM; +- memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); +- } +- ++ rc = sqlite3Fts3ExprIterate(pRoot, fts3AllocateMSI, (void*)pTab); ++ if( rc!=SQLITE_OK ) return rc; + fts3EvalRestart(pCsr, pRoot, &rc); + + while( pCsr->isEof==0 && rc==SQLITE_OK ){ +@@ -172826,6 +195130,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( + u8 bTreeEof = 0; + Fts3Expr *p; /* Used to iterate from pExpr to root */ + Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */ ++ Fts3Expr *pRun; /* Closest non-deferred ancestor of pNear */ + int bMatch; + + /* Check if this phrase descends from an OR expression node. If not, +@@ -172840,22 +195145,30 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( + if( p->bEof ) bTreeEof = 1; + } + if( bOr==0 ) return SQLITE_OK; ++ pRun = pNear; ++ while( pRun->bDeferred ){ ++ assert( pRun->pParent ); ++ pRun = pRun->pParent; ++ } + + /* This is the descendent of an OR node. In this case we cannot use + ** an incremental phrase. Load the entire doclist for the phrase + ** into memory in this case. */ + if( pPhrase->bIncr ){ +- int bEofSave = pNear->bEof; +- fts3EvalRestart(pCsr, pNear, &rc); +- while( rc==SQLITE_OK && !pNear->bEof ){ +- fts3EvalNextRow(pCsr, pNear, &rc); +- if( bEofSave==0 && pNear->iDocid==iDocid ) break; ++ int bEofSave = pRun->bEof; ++ fts3EvalRestart(pCsr, pRun, &rc); ++ while( rc==SQLITE_OK && !pRun->bEof ){ ++ fts3EvalNextRow(pCsr, pRun, &rc); ++ if( bEofSave==0 && pRun->iDocid==iDocid ) break; + } + assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); ++ if( rc==SQLITE_OK && pRun->bEof!=bEofSave ){ ++ rc = FTS_CORRUPT_VTAB; ++ } + } + if( bTreeEof ){ +- while( rc==SQLITE_OK && !pNear->bEof ){ +- fts3EvalNextRow(pCsr, pNear, &rc); ++ while( rc==SQLITE_OK && !pRun->bEof ){ ++ fts3EvalNextRow(pCsr, pRun, &rc); + } + } + if( rc!=SQLITE_OK ) return rc; +@@ -172954,7 +195267,7 @@ SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ + } + #endif + +-#if !SQLITE_CORE ++#if !defined(SQLITE_CORE) + /* + ** Initialize API pointer table, if required. + */ +@@ -173274,6 +195587,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ + if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; + memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); + iCol = 0; ++ rc = SQLITE_OK; + + while( iaStat[iCol+1].nDoc++; + eState = 2; +@@ -173325,7 +195643,6 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ + } + + pCsr->iCol = 0; +- rc = SQLITE_OK; + }else{ + pCsr->isEof = 1; + } +@@ -173383,6 +195700,7 @@ static int fts3auxFilterMethod( + sqlite3Fts3SegReaderFinish(&pCsr->csr); + sqlite3_free((void *)pCsr->filter.zTerm); + sqlite3_free(pCsr->aStat); ++ sqlite3_free(pCsr->zStop); + memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); + + pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; +@@ -173517,7 +195835,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ +- 0 /* xShadowName */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + int rc; /* Return code */ + +@@ -173653,7 +195972,7 @@ static int fts3isspace(char c){ + ** zero the memory before returning a pointer to it. If unsuccessful, + ** return NULL. + */ +-static void *fts3MallocZero(sqlite3_int64 nByte){ ++SQLITE_PRIVATE void *sqlite3Fts3MallocZero(sqlite3_int64 nByte){ + void *pRet = sqlite3_malloc64(nByte); + if( pRet ) memset(pRet, 0, nByte); + return pRet; +@@ -173692,6 +196011,23 @@ SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer( + */ + static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); + ++/* ++** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis ++** is defined, search for '(' and ')' as well. Return the index of the first ++** such character in the buffer. If there is no such character, return -1. ++*/ ++static int findBarredChar(const char *z, int n){ ++ int ii; ++ for(ii=0; iiiLangid, z, i, &pCursor); ++ *pnConsumed = n; ++ rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); + if( rc==SQLITE_OK ){ + const char *zToken; + int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; +@@ -173733,8 +196062,19 @@ static int getNextToken( + + rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); + if( rc==SQLITE_OK ){ +- nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; +- pRet = (Fts3Expr *)fts3MallocZero(nByte); ++ /* Check that this tokenization did not gobble up any " characters. Or, ++ ** if enable_parenthesis is true, that it did not gobble up any ++ ** open or close parenthesis characters either. If it did, call ++ ** getNextToken() again, but pass only that part of the input buffer ++ ** up to the first such character. */ ++ int iBarred = findBarredChar(z, iEnd); ++ if( iBarred>=0 ){ ++ pModule->xClose(pCursor); ++ return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed); ++ } ++ ++ nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken; ++ pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); + if( !pRet ){ + rc = SQLITE_NOMEM; + }else{ +@@ -173743,7 +196083,7 @@ static int getNextToken( + pRet->pPhrase->nToken = 1; + pRet->pPhrase->iColumn = iCol; + pRet->pPhrase->aToken[0].n = nToken; +- pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; ++ pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1]; + memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); + + if( iEnd=0 ){ ++ *pnConsumed = iBarred; ++ } + rc = SQLITE_OK; + } + +@@ -173814,9 +196158,9 @@ static int getNextString( + Fts3Expr *p = 0; + sqlite3_tokenizer_cursor *pCursor = 0; + char *zTemp = 0; +- int nTemp = 0; ++ i64 nTemp = 0; + +- const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); ++ const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1); + int nToken = 0; + + /* The final Fts3Expr data structure, including the Fts3Phrase, +@@ -173850,10 +196194,11 @@ static int getNextString( + Fts3PhraseToken *pToken; + + p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); +- if( !p ) goto no_mem; +- + zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); +- if( !zTemp ) goto no_mem; ++ if( !zTemp || !p ){ ++ rc = SQLITE_NOMEM; ++ goto getnextstring_out; ++ } + + assert( nToken==ii ); + pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; +@@ -173868,9 +196213,6 @@ static int getNextString( + nToken = ii+1; + } + } +- +- pModule->xClose(pCursor); +- pCursor = 0; + } + + if( rc==SQLITE_DONE ){ +@@ -173878,7 +196220,10 @@ static int getNextString( + char *zBuf = 0; + + p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); +- if( !p ) goto no_mem; ++ if( !p ){ ++ rc = SQLITE_NOMEM; ++ goto getnextstring_out; ++ } + memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); + p->eType = FTSQUERY_PHRASE; + p->pPhrase = (Fts3Phrase *)&p[1]; +@@ -173886,11 +196231,9 @@ static int getNextString( + p->pPhrase->nToken = nToken; + + zBuf = (char *)&p->pPhrase->aToken[nToken]; ++ assert( nTemp==0 || zTemp ); + if( zTemp ){ + memcpy(zBuf, zTemp, nTemp); +- sqlite3_free(zTemp); +- }else{ +- assert( nTemp==0 ); + } + + for(jj=0; jjpPhrase->nToken; jj++){ +@@ -173900,17 +196243,17 @@ static int getNextString( + rc = SQLITE_OK; + } + +- *ppExpr = p; +- return rc; +-no_mem: +- ++ getnextstring_out: + if( pCursor ){ + pModule->xClose(pCursor); + } + sqlite3_free(zTemp); +- sqlite3_free(p); +- *ppExpr = 0; +- return SQLITE_NOMEM; ++ if( rc!=SQLITE_OK ){ ++ sqlite3_free(p); ++ p = 0; ++ } ++ *ppExpr = p; ++ return rc; + } + + /* +@@ -173989,7 +196332,7 @@ static int getNextNode( + if( fts3isspace(cNext) + || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 + ){ +- pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr)); ++ pRet = (Fts3Expr *)sqlite3Fts3MallocZero(sizeof(Fts3Expr)); + if( !pRet ){ + return SQLITE_NOMEM; + } +@@ -174024,6 +196367,11 @@ static int getNextNode( + if( *zInput=='(' ){ + int nConsumed = 0; + pParse->nNest++; ++#if !defined(SQLITE_MAX_EXPR_DEPTH) ++ if( pParse->nNest>1000 ) return SQLITE_ERROR; ++#elif SQLITE_MAX_EXPR_DEPTH>0 ++ if( pParse->nNest>SQLITE_MAX_EXPR_DEPTH ) return SQLITE_ERROR; ++#endif + rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed); + *pnConsumed = (int)(zInput - z) + 1 + nConsumed; + return rc; +@@ -174163,7 +196511,7 @@ static int fts3ExprParse( + && p->eType==FTSQUERY_PHRASE && pParse->isNot + ){ + /* Create an implicit NOT operator. */ +- Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); ++ Fts3Expr *pNot = sqlite3Fts3MallocZero(sizeof(Fts3Expr)); + if( !pNot ){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_NOMEM; +@@ -174184,7 +196532,7 @@ static int fts3ExprParse( + + /* The isRequirePhrase variable is set to true if a phrase or + ** an expression contained in parenthesis is required. If a +- ** binary operator (AND, OR, NOT or NEAR) is encounted when ++ ** binary operator (AND, OR, NOT or NEAR) is encountered when + ** isRequirePhrase is set, this is a syntax error. + */ + if( !isPhrase && isRequirePhrase ){ +@@ -174197,7 +196545,7 @@ static int fts3ExprParse( + /* Insert an implicit AND operator. */ + Fts3Expr *pAnd; + assert( pRet && pPrev ); +- pAnd = fts3MallocZero(sizeof(Fts3Expr)); ++ pAnd = sqlite3Fts3MallocZero(sizeof(Fts3Expr)); + if( !pAnd ){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_NOMEM; +@@ -174766,7 +197114,6 @@ static void fts3ExprTestCommon( + } + + if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ +- sqlite3Fts3ExprFree(pExpr); + sqlite3_result_error(context, "Error parsing expression", -1); + }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ + sqlite3_result_error_nomem(context); +@@ -175009,7 +197356,7 @@ static void fts3HashInsertElement( + } + + +-/* Resize the hash table so that it cantains "new_size" buckets. ++/* Resize the hash table so that it contains "new_size" buckets. + ** "new_size" must be a power of 2. The hash table might fail + ** to resize if sqliteMalloc() fails. + ** +@@ -175464,7 +197811,7 @@ static int star_oh(const char *z){ + + /* + ** If the word ends with zFrom and xCond() is true for the stem +-** of the word that preceeds the zFrom ending, then change the ++** of the word that precedes the zFrom ending, then change the + ** ending to zTo. + ** + ** The input word *pz and zFrom are both in reverse order. zTo +@@ -175829,7 +198176,7 @@ static int porterNext( + if( n>c->nAllocated ){ + char *pNew; + c->nAllocated = n+20; +- pNew = sqlite3_realloc(c->zToken, c->nAllocated); ++ pNew = sqlite3_realloc64(c->zToken, c->nAllocated); + if( !pNew ) return SQLITE_NOMEM; + c->zToken = pNew; + } +@@ -176099,11 +198446,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( + + #ifdef SQLITE_TEST + +-#if defined(INCLUDE_SQLITE_TCL_H) +-# include "sqlite_tcl.h" +-#else +-# include "tcl.h" +-#endif ++#include "tclsqlite.h" + /* #include */ + + /* +@@ -176581,7 +198924,7 @@ static int simpleNext( + if( n>c->nTokenAllocated ){ + char *pNew; + c->nTokenAllocated = n+20; +- pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated); ++ pNew = sqlite3_realloc64(c->pToken, c->nTokenAllocated); + if( !pNew ) return SQLITE_NOMEM; + c->pToken = pNew; + } +@@ -176979,7 +199322,7 @@ static int fts3tokFilterMethod( + fts3tokResetCursor(pCsr); + if( idxNum==1 ){ + const char *zByte = (const char *)sqlite3_value_text(apVal[0]); +- int nByte = sqlite3_value_bytes(apVal[0]); ++ sqlite3_int64 nByte = sqlite3_value_bytes(apVal[0]); + pCsr->zInput = sqlite3_malloc64(nByte+1); + if( pCsr->zInput==0 ){ + rc = SQLITE_NOMEM; +@@ -177053,7 +199396,7 @@ static int fts3tokRowidMethod( + ** Register the fts3tok module with database connection db. Return SQLITE_OK + ** if successful or an error code if sqlite3_create_module() fails. + */ +-SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){ ++SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){ + static const sqlite3_module fts3tok_module = { + 0, /* iVersion */ + fts3tokConnectMethod, /* xCreate */ +@@ -177078,11 +199421,14 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ +- 0 /* xShadowName */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + int rc; /* Return code */ + +- rc = sqlite3_create_module(db, "fts3tokenize", &fts3tok_module, (void*)pHash); ++ rc = sqlite3_create_module_v2( ++ db, "fts3tokenize", &fts3tok_module, (void*)pHash, xDestroy ++ ); + return rc; + } + +@@ -177741,7 +200087,7 @@ static int fts3PendingListAppendVarint( + + /* Allocate or grow the PendingList as required. */ + if( !p ){ +- p = sqlite3_malloc(sizeof(*p) + 100); ++ p = sqlite3_malloc64(sizeof(*p) + 100); + if( !p ){ + return SQLITE_NOMEM; + } +@@ -177750,14 +200096,14 @@ static int fts3PendingListAppendVarint( + p->nData = 0; + } + else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){ +- int nNew = p->nSpace * 2; +- p = sqlite3_realloc(p, sizeof(*p) + nNew); ++ i64 nNew = p->nSpace * 2; ++ p = sqlite3_realloc64(p, sizeof(*p) + nNew); + if( !p ){ + sqlite3_free(*pp); + *pp = 0; + return SQLITE_NOMEM; + } +- p->nSpace = nNew; ++ p->nSpace = (int)nNew; + p->aData = (char *)&p[1]; + } + +@@ -178314,7 +200660,7 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock( + int nByte = sqlite3_blob_bytes(p->pSegments); + *pnBlob = nByte; + if( paBlob ){ +- char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING); ++ char *aByte = sqlite3_malloc64((i64)nByte + FTS3_NODE_PADDING); + if( !aByte ){ + rc = SQLITE_NOMEM; + }else{ +@@ -178427,9 +200773,19 @@ static int fts3SegReaderNext( + char *aCopy; + PendingList *pList = (PendingList *)fts3HashData(pElem); + int nCopy = pList->nData+1; +- pReader->zTerm = (char *)fts3HashKey(pElem); +- pReader->nTerm = fts3HashKeysize(pElem); +- aCopy = (char*)sqlite3_malloc(nCopy); ++ ++ int nTerm = fts3HashKeysize(pElem); ++ if( (nTerm+1)>pReader->nTermAlloc ){ ++ sqlite3_free(pReader->zTerm); ++ pReader->zTerm = (char*)sqlite3_malloc64(((i64)nTerm+1)*2); ++ if( !pReader->zTerm ) return SQLITE_NOMEM; ++ pReader->nTermAlloc = (nTerm+1)*2; ++ } ++ memcpy(pReader->zTerm, fts3HashKey(pElem), nTerm); ++ pReader->zTerm[nTerm] = '\0'; ++ pReader->nTerm = nTerm; ++ ++ aCopy = (char*)sqlite3_malloc64(nCopy); + if( !aCopy ) return SQLITE_NOMEM; + memcpy(aCopy, pList->aData, nCopy); + pReader->nNode = pReader->nDoclist = nCopy; +@@ -178681,9 +201037,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrOvfl( + */ + SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){ + if( pReader ){ +- if( !fts3SegReaderIsPending(pReader) ){ +- sqlite3_free(pReader->zTerm); +- } ++ sqlite3_free(pReader->zTerm); + if( !fts3SegReaderIsRootOnly(pReader) ){ + sqlite3_free(pReader->aNode); + } +@@ -178718,7 +201072,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( + nExtra = nRoot + FTS3_NODE_PADDING; + } + +- pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); ++ pReader = (Fts3SegReader *)sqlite3_malloc64(sizeof(Fts3SegReader) + nExtra); + if( !pReader ){ + return SQLITE_NOMEM; + } +@@ -178810,7 +201164,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( + if( nElem==nAlloc ){ + Fts3HashElem **aElem2; + nAlloc += 16; +- aElem2 = (Fts3HashElem **)sqlite3_realloc( ++ aElem2 = (Fts3HashElem **)sqlite3_realloc64( + aElem, nAlloc*sizeof(Fts3HashElem *) + ); + if( !aElem2 ){ +@@ -178899,7 +201253,7 @@ static int fts3SegReaderCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ + if( rc==0 ){ + rc = pRhs->iIdx - pLhs->iIdx; + } +- assert( rc!=0 ); ++ assert_fts3_nc( rc!=0 ); + return rc; + } + +@@ -179095,8 +201449,8 @@ static int fts3PrefixCompress( + int nNext /* Size of buffer zNext in bytes */ + ){ + int n; +- UNUSED_PARAMETER(nNext); +- for(n=0; naData==(char *)&pTree[1] ); +- pTree->aData = (char *)sqlite3_malloc(nReq); ++ pTree->aData = (char *)sqlite3_malloc64(nReq); + if( !pTree->aData ){ + return SQLITE_NOMEM; + } +@@ -179162,7 +201516,7 @@ static int fts3NodeAddTerm( + + if( isCopyTerm ){ + if( pTree->nMalloczMalloc, nTerm*2); ++ char *zNew = sqlite3_realloc64(pTree->zMalloc, (i64)nTerm*2); + if( !zNew ){ + return SQLITE_NOMEM; + } +@@ -179188,7 +201542,7 @@ static int fts3NodeAddTerm( + ** now. Instead, the term is inserted into the parent of pTree. If pTree + ** has no parent, one is created here. + */ +- pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize); ++ pNew = (SegmentNode *)sqlite3_malloc64(sizeof(SegmentNode) + p->nNodeSize); + if( !pNew ){ + return SQLITE_NOMEM; + } +@@ -179326,7 +201680,7 @@ static int fts3SegWriterAdd( + ){ + int nPrefix; /* Size of term prefix in bytes */ + int nSuffix; /* Size of term suffix in bytes */ +- int nReq; /* Number of bytes required on leaf page */ ++ i64 nReq; /* Number of bytes required on leaf page */ + int nData; + SegmentWriter *pWriter = *ppWriter; + +@@ -179335,13 +201689,13 @@ static int fts3SegWriterAdd( + sqlite3_stmt *pStmt; + + /* Allocate the SegmentWriter structure */ +- pWriter = (SegmentWriter *)sqlite3_malloc(sizeof(SegmentWriter)); ++ pWriter = (SegmentWriter *)sqlite3_malloc64(sizeof(SegmentWriter)); + if( !pWriter ) return SQLITE_NOMEM; + memset(pWriter, 0, sizeof(SegmentWriter)); + *ppWriter = pWriter; + + /* Allocate a buffer in which to accumulate data */ +- pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize); ++ pWriter->aData = (char *)sqlite3_malloc64(p->nNodeSize); + if( !pWriter->aData ) return SQLITE_NOMEM; + pWriter->nSize = p->nNodeSize; + +@@ -179416,7 +201770,7 @@ static int fts3SegWriterAdd( + ** the buffer to make it large enough. + */ + if( nReq>pWriter->nSize ){ +- char *aNew = sqlite3_realloc(pWriter->aData, nReq); ++ char *aNew = sqlite3_realloc64(pWriter->aData, nReq); + if( !aNew ) return SQLITE_NOMEM; + pWriter->aData = aNew; + pWriter->nSize = nReq; +@@ -179441,7 +201795,7 @@ static int fts3SegWriterAdd( + */ + if( isCopyTerm ){ + if( nTerm>pWriter->nMalloc ){ +- char *zNew = sqlite3_realloc(pWriter->zMalloc, nTerm*2); ++ char *zNew = sqlite3_realloc64(pWriter->zMalloc, (i64)nTerm*2); + if( !zNew ){ + return SQLITE_NOMEM; + } +@@ -179749,18 +202103,20 @@ static void fts3ColumnFilter( + static int fts3MsrBufferData( + Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */ + char *pList, +- int nList ++ i64 nList + ){ +- if( nList>pMsr->nBuffer ){ ++ if( (nList+FTS3_NODE_PADDING)>pMsr->nBuffer ){ + char *pNew; +- pMsr->nBuffer = nList*2; +- pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer); ++ int nNew = nList*2 + FTS3_NODE_PADDING; ++ pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, nNew); + if( !pNew ) return SQLITE_NOMEM; + pMsr->aBuffer = pNew; ++ pMsr->nBuffer = nNew; + } + + assert( nList>0 ); + memcpy(pMsr->aBuffer, pList, nList); ++ memset(&pMsr->aBuffer[nList], 0, FTS3_NODE_PADDING); + return SQLITE_OK; + } + +@@ -179810,7 +202166,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( + fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp); + + if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){ +- rc = fts3MsrBufferData(pMsr, pList, nList+1); ++ rc = fts3MsrBufferData(pMsr, pList, (i64)nList+1); + if( rc!=SQLITE_OK ) return rc; + assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); + pList = pMsr->aBuffer; +@@ -179947,11 +202303,11 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){ + return SQLITE_OK; + } + +-static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){ ++static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, i64 nReq){ + if( nReq>pCsr->nBuffer ){ + char *aNew; + pCsr->nBuffer = nReq*2; +- aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); ++ aNew = sqlite3_realloc64(pCsr->aBuffer, pCsr->nBuffer); + if( !aNew ){ + return SQLITE_NOMEM; + } +@@ -180042,7 +202398,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( + ){ + pCsr->nDoclist = apSegment[0]->nDoclist; + if( fts3SegReaderIsPending(apSegment[0]) ){ +- rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); ++ rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, ++ (i64)pCsr->nDoclist); + pCsr->aDoclist = pCsr->aBuffer; + }else{ + pCsr->aDoclist = apSegment[0]->aDoclist; +@@ -180095,7 +202452,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( + + nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); + +- rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist); ++ rc = fts3GrowSegReaderBuffer(pCsr, ++ (i64)nByte+nDoclist+FTS3_NODE_PADDING); + if( rc ) return rc; + + if( isFirst ){ +@@ -180121,7 +202479,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( + fts3SegReaderSort(apSegment, nMerge, j, xCmp); + } + if( nDoclist>0 ){ +- rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING); ++ rc = fts3GrowSegReaderBuffer(pCsr, (i64)nDoclist+FTS3_NODE_PADDING); + if( rc ) return rc; + memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING); + pCsr->aDoclist = pCsr->aBuffer; +@@ -180405,7 +202763,6 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ + rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING); + if( rc==SQLITE_DONE ) rc = SQLITE_OK; + } +- sqlite3Fts3PendingTermsClear(p); + + /* Determine the auto-incr-merge setting if unknown. If enabled, + ** estimate the number of leaf blocks of content to be written +@@ -180427,6 +202784,10 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ + rc = sqlite3_reset(pStmt); + } + } ++ ++ if( rc==SQLITE_OK ){ ++ sqlite3Fts3PendingTermsClear(p); ++ } + return rc; + } + +@@ -180834,7 +203195,7 @@ struct NodeReader { + static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){ + if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){ + int nAlloc = nMin; +- char *a = (char *)sqlite3_realloc(pBlob->a, nAlloc); ++ char *a = (char *)sqlite3_realloc64(pBlob->a, nAlloc); + if( a ){ + pBlob->nAlloc = nAlloc; + pBlob->a = a; +@@ -180875,7 +203236,7 @@ static int nodeReaderNext(NodeReader *p){ + return FTS_CORRUPT_VTAB; + } + blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); +- if( rc==SQLITE_OK ){ ++ if( rc==SQLITE_OK && ALWAYS(p->term.a!=0) ){ + memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); + p->term.n = nPrefix+nSuffix; + p->iOff += nSuffix; +@@ -180983,6 +203344,8 @@ static int fts3IncrmergePush( + pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix); + } + pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix); ++ assert( nPrefix+nSuffix<=nTerm ); ++ assert( nPrefix>=0 ); + memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix); + pBlk->n += nSuffix; + +@@ -181031,7 +203394,7 @@ static int fts3IncrmergePush( + ** + ** It is assumed that the buffer associated with pNode is already large + ** enough to accommodate the new entry. The buffer associated with pPrev +-** is extended by this function if requrired. ++** is extended by this function if required. + ** + ** If an error (i.e. OOM condition) occurs, an SQLite error code is + ** returned. Otherwise, SQLITE_OK. +@@ -181056,6 +203419,8 @@ static int fts3AppendToNode( + + blobGrowBuffer(pPrev, nTerm, &rc); + if( rc!=SQLITE_OK ) return rc; ++ assert( pPrev!=0 ); ++ assert( pPrev->a!=0 ); + + nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm); + nSuffix = nTerm - nPrefix; +@@ -181105,15 +203470,20 @@ static int fts3IncrmergeAppend( + pLeaf = &pWriter->aNodeWriter[0]; + nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm); + nSuffix = nTerm - nPrefix; ++ if(nSuffix<=0 ) return FTS_CORRUPT_VTAB; + + nSpace = sqlite3Fts3VarintLen(nPrefix); + nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix; + nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist; + + /* If the current block is not empty, and if adding this term/doclist +- ** to the current block would make it larger than Fts3Table.nNodeSize +- ** bytes, write this block out to the database. */ +- if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){ ++ ** to the current block would make it larger than Fts3Table.nNodeSize bytes, ++ ** and if there is still room for another leaf page, write this block out to ++ ** the database. */ ++ if( pLeaf->block.n>0 ++ && (pLeaf->block.n + nSpace)>p->nNodeSize ++ && pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst) ++ ){ + rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n); + pWriter->nWork++; + +@@ -181269,7 +203639,11 @@ static int fts3TermCmp( + int nCmp = MIN(nLhs, nRhs); + int res; + +- res = (nCmp ? memcmp(zLhs, zRhs, nCmp) : 0); ++ if( nCmp && ALWAYS(zLhs) && ALWAYS(zRhs) ){ ++ res = memcmp(zLhs, zRhs, nCmp); ++ }else{ ++ res = 0; ++ } + if( res==0 ) res = nLhs - nRhs; + + return res; +@@ -181420,6 +203794,7 @@ static int fts3IncrmergeLoad( + + for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){ + NodeReader reader; ++ memset(&reader, 0, sizeof(reader)); + pNode = &pWriter->aNodeWriter[i]; + + if( pNode->block.a){ +@@ -181427,14 +203802,17 @@ static int fts3IncrmergeLoad( + while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader); + blobGrowBuffer(&pNode->key, reader.term.n, &rc); + if( rc==SQLITE_OK ){ +- memcpy(pNode->key.a, reader.term.a, reader.term.n); ++ assert_fts3_nc( reader.term.n>0 || reader.aNode==0 ); ++ if( reader.term.n>0 ){ ++ memcpy(pNode->key.a, reader.term.a, reader.term.n); ++ } + pNode->key.n = reader.term.n; + if( i>0 ){ + char *aBlock = 0; + int nBlock = 0; + pNode = &pWriter->aNodeWriter[i-1]; + pNode->iBlock = reader.iChild; +- rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0); ++ rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0); + blobGrowBuffer(&pNode->block, + MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc + ); +@@ -181621,7 +203999,7 @@ static int fts3RepackSegdirLevel( + if( nIdx>=nAlloc ){ + int *aNew; + nAlloc += 16; +- aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int)); ++ aNew = sqlite3_realloc64(aIdx, nAlloc*sizeof(int)); + if( !aNew ){ + rc = SQLITE_NOMEM; + break; +@@ -181910,7 +204288,7 @@ static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){ + if( aHint ){ + blobGrowBuffer(pHint, nHint, &rc); + if( rc==SQLITE_OK ){ +- memcpy(pHint->a, aHint, nHint); ++ if( ALWAYS(pHint->a!=0) ) memcpy(pHint->a, aHint, nHint); + pHint->n = nHint; + } + } +@@ -181995,7 +204373,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){ + + /* Allocate space for the cursor, filter and writer objects */ + const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter); +- pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc); ++ pWriter = (IncrmergeWriter *)sqlite3_malloc64(nAlloc); + if( !pWriter ) return SQLITE_NOMEM; + pFilter = (Fts3SegFilter *)&pWriter[1]; + pCsr = (Fts3MultiSegReader *)&pFilter[1]; +@@ -182287,7 +204665,7 @@ static u64 fts3ChecksumIndex( + int rc; + u64 cksum = 0; + +- assert( *pRc==SQLITE_OK ); ++ if( *pRc ) return 0; + + memset(&filter, 0, sizeof(filter)); + memset(&csr, 0, sizeof(csr)); +@@ -182354,7 +204732,7 @@ static u64 fts3ChecksumIndex( + ** If an error occurs (e.g. an OOM or IO error), return an SQLite error + ** code. The final value of *pbOk is undefined in this case. + */ +-static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ ++SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){ + int rc = SQLITE_OK; /* Return code */ + u64 cksum1 = 0; /* Checksum based on FTS index contents */ + u64 cksum2 = 0; /* Checksum based on %_content contents */ +@@ -182432,7 +204810,12 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ + sqlite3_finalize(pStmt); + } + +- *pbOk = (cksum1==cksum2); ++ if( rc==SQLITE_CORRUPT_VTAB ){ ++ rc = SQLITE_OK; ++ *pbOk = 0; ++ }else{ ++ *pbOk = (rc==SQLITE_OK && cksum1==cksum2); ++ } + return rc; + } + +@@ -182472,7 +204855,7 @@ static int fts3DoIntegrityCheck( + ){ + int rc; + int bOk = 0; +- rc = fts3IntegrityCheck(p, &bOk); ++ rc = sqlite3Fts3IntegrityCheck(p, &bOk); + if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB; + return rc; + } +@@ -182502,8 +204885,11 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ + rc = fts3DoIncrmerge(p, &zVal[6]); + }else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){ + rc = fts3DoAutoincrmerge(p, &zVal[10]); ++ }else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){ ++ rc = sqlite3Fts3PendingTermsFlush(p); ++ } + #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) +- }else{ ++ else{ + int v; + if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ + v = atoi(&zVal[9]); +@@ -182521,8 +204907,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ + if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v; + rc = SQLITE_OK; + } +-#endif + } ++#endif + return rc; + } + +@@ -182631,7 +205017,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList( + return SQLITE_OK; + } + +- pRet = (char *)sqlite3_malloc(p->pList->nData); ++ pRet = (char *)sqlite3_malloc64(p->pList->nData); + if( !pRet ) return SQLITE_NOMEM; + + nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy); +@@ -182651,7 +205037,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken( + int iCol /* Column that token must appear in (or -1) */ + ){ + Fts3DeferredToken *pDeferred; +- pDeferred = sqlite3_malloc(sizeof(*pDeferred)); ++ pDeferred = sqlite3_malloc64(sizeof(*pDeferred)); + if( !pDeferred ){ + return SQLITE_NOMEM; + } +@@ -182671,7 +205057,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken( + /* + ** SQLite value pRowid contains the rowid of a row that may or may not be + ** present in the FTS3 table. If it is, delete it and adjust the contents +-** of subsiduary data structures accordingly. ++** of subsidiary data structures accordingly. + */ + static int fts3DeleteByRowid( + Fts3Table *p, +@@ -182906,6 +205292,10 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ + /* #include */ + /* #include */ + ++#ifndef SQLITE_AMALGAMATION ++typedef sqlite3_int64 i64; ++#endif ++ + /* + ** Characters that may appear in the second argument to matchinfo(). + */ +@@ -182926,7 +205316,7 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ + + + /* +-** Used as an fts3ExprIterate() context when loading phrase doclists to ++** Used as an sqlite3Fts3ExprIterate() context when loading phrase doclists to + ** Fts3Expr.aDoclist[]/nDoclist. + */ + typedef struct LoadDoclistCtx LoadDoclistCtx; +@@ -182956,9 +205346,9 @@ struct SnippetIter { + struct SnippetPhrase { + int nToken; /* Number of tokens in phrase */ + char *pList; /* Pointer to start of phrase position list */ +- int iHead; /* Next value in position list */ ++ i64 iHead; /* Next value in position list */ + char *pHead; /* Position list data following iHead */ +- int iTail; /* Next value in trailing position list */ ++ i64 iTail; /* Next value in trailing position list */ + char *pTail; /* Position list data following iTail */ + }; + +@@ -182970,7 +205360,7 @@ struct SnippetFragment { + }; + + /* +-** This type is used as an fts3ExprIterate() context object while ++** This type is used as an sqlite3Fts3ExprIterate() context object while + ** accumulating the data returned by the matchinfo() function. + */ + typedef struct MatchInfo MatchInfo; +@@ -182993,9 +205383,13 @@ struct MatchinfoBuffer { + int nElem; + int bGlobal; /* Set if global data is loaded */ + char *zMatchinfo; +- u32 aMatchinfo[1]; ++ u32 aMI[FLEXARRAY]; + }; + ++/* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */ ++#define SZ_MATCHINFOBUFFER(N) \ ++ (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64)) ++ + + /* + ** The snippet() and offsets() functions both return text values. An instance +@@ -183020,14 +205414,13 @@ struct StrBuffer { + static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ + MatchinfoBuffer *pRet; + sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) +- + sizeof(MatchinfoBuffer); ++ + SZ_MATCHINFOBUFFER(1); + sqlite3_int64 nStr = strlen(zMatchinfo); + +- pRet = sqlite3_malloc64(nByte + nStr+1); ++ pRet = sqlite3Fts3MallocZero(nByte + nStr+1); + if( pRet ){ +- memset(pRet, 0, nByte); +- pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; +- pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] ++ pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet; ++ pRet->aMI[1+nElem] = pRet->aMI[0] + + sizeof(u32)*((int)nElem+1); + pRet->nElem = (int)nElem; + pRet->zMatchinfo = ((char*)pRet) + nByte; +@@ -183041,10 +205434,10 @@ static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ + static void fts3MIBufferFree(void *p){ + MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); + +- assert( (u32*)p==&pBuf->aMatchinfo[1] +- || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2] ++ assert( (u32*)p==&pBuf->aMI[1] ++ || (u32*)p==&pBuf->aMI[pBuf->nElem+2] + ); +- if( (u32*)p==&pBuf->aMatchinfo[1] ){ ++ if( (u32*)p==&pBuf->aMI[1] ){ + pBuf->aRef[1] = 0; + }else{ + pBuf->aRef[2] = 0; +@@ -183061,18 +205454,18 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){ + + if( p->aRef[1]==0 ){ + p->aRef[1] = 1; +- aOut = &p->aMatchinfo[1]; ++ aOut = &p->aMI[1]; + xRet = fts3MIBufferFree; + } + else if( p->aRef[2]==0 ){ + p->aRef[2] = 1; +- aOut = &p->aMatchinfo[p->nElem+2]; ++ aOut = &p->aMI[p->nElem+2]; + xRet = fts3MIBufferFree; + }else{ + aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); + if( aOut ){ + xRet = sqlite3_free; +- if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); ++ if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32)); + } + } + +@@ -183082,7 +205475,7 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){ + + static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ + p->bGlobal = 1; +- memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32)); ++ memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32)); + } + + /* +@@ -183123,14 +205516,14 @@ SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){ + ** After it returns, *piPos contains the value of the next element of the + ** list and *pp is advanced to the following varint. + */ +-static void fts3GetDeltaPosition(char **pp, int *piPos){ ++static void fts3GetDeltaPosition(char **pp, i64 *piPos){ + int iVal; + *pp += fts3GetVarint32(*pp, &iVal); + *piPos += (iVal-2); + } + + /* +-** Helper function for fts3ExprIterate() (see below). ++** Helper function for sqlite3Fts3ExprIterate() (see below). + */ + static int fts3ExprIterate2( + Fts3Expr *pExpr, /* Expression to iterate phrases of */ +@@ -183164,7 +205557,7 @@ static int fts3ExprIterate2( + ** Otherwise, SQLITE_OK is returned after a callback has been made for + ** all eligible phrase nodes. + */ +-static int fts3ExprIterate( ++SQLITE_PRIVATE int sqlite3Fts3ExprIterate( + Fts3Expr *pExpr, /* Expression to iterate phrases of */ + int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */ + void *pCtx /* Second argument to pass to callback */ +@@ -183173,10 +205566,9 @@ static int fts3ExprIterate( + return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx); + } + +- + /* +-** This is an fts3ExprIterate() callback used while loading the doclists +-** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also ++** This is an sqlite3Fts3ExprIterate() callback used while loading the ++** doclists for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also + ** fts3ExprLoadDoclists(). + */ + static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ +@@ -183208,9 +205600,9 @@ static int fts3ExprLoadDoclists( + int *pnToken /* OUT: Number of tokens in query */ + ){ + int rc; /* Return Code */ +- LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ ++ LoadDoclistCtx sCtx = {0,0,0}; /* Context for sqlite3Fts3ExprIterate() */ + sCtx.pCsr = pCsr; +- rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx); ++ rc = sqlite3Fts3ExprIterate(pCsr->pExpr,fts3ExprLoadDoclistsCb,(void*)&sCtx); + if( pnPhrase ) *pnPhrase = sCtx.nPhrase; + if( pnToken ) *pnToken = sCtx.nToken; + return rc; +@@ -183223,7 +205615,7 @@ static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ + } + static int fts3ExprPhraseCount(Fts3Expr *pExpr){ + int nPhrase = 0; +- (void)fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase); ++ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase); + return nPhrase; + } + +@@ -183232,10 +205624,10 @@ static int fts3ExprPhraseCount(Fts3Expr *pExpr){ + ** arguments so that it points to the first element with a value greater + ** than or equal to parameter iNext. + */ +-static void fts3SnippetAdvance(char **ppIter, int *piIter, int iNext){ ++static void fts3SnippetAdvance(char **ppIter, i64 *piIter, int iNext){ + char *pIter = *ppIter; + if( pIter ){ +- int iIter = *piIter; ++ i64 iIter = *piIter; + + while( iIternSnippet>=0 ); + pIter->iCurrent = iStart = iEnd - pIter->nSnippet + 1; + for(i=0; inPhrase; i++){ + SnippetPhrase *pPhrase = &pIter->aPhrase[i]; +@@ -183318,7 +205711,7 @@ static void fts3SnippetDetails( + SnippetPhrase *pPhrase = &pIter->aPhrase[i]; + if( pPhrase->pTail ){ + char *pCsr = pPhrase->pTail; +- int iCsr = pPhrase->iTail; ++ i64 iCsr = pPhrase->iTail; + + while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){ + int j; +@@ -183333,7 +205726,7 @@ static void fts3SnippetDetails( + } + mCover |= mPhrase; + +- for(j=0; jnToken; j++){ ++ for(j=0; jnToken && jnSnippet; j++){ + mHighlight |= (mPos>>j); + } + +@@ -183351,8 +205744,9 @@ static void fts3SnippetDetails( + } + + /* +-** This function is an fts3ExprIterate() callback used by fts3BestSnippet(). +-** Each invocation populates an element of the SnippetIter.aPhrase[] array. ++** This function is an sqlite3Fts3ExprIterate() callback used by ++** fts3BestSnippet(). Each invocation populates an element of the ++** SnippetIter.aPhrase[] array. + */ + static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ + SnippetIter *p = (SnippetIter *)ctx; +@@ -183364,7 +205758,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ + rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr); + assert( rc==SQLITE_OK || pCsr==0 ); + if( pCsr ){ +- int iFirst = 0; ++ i64 iFirst = 0; + pPhrase->pList = pCsr; + fts3GetDeltaPosition(&pCsr, &iFirst); + if( iFirst<0 ){ +@@ -183429,11 +205823,10 @@ static int fts3BestSnippet( + ** the required space using malloc(). + */ + nByte = sizeof(SnippetPhrase) * nList; +- sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc64(nByte); ++ sIter.aPhrase = (SnippetPhrase *)sqlite3Fts3MallocZero(nByte); + if( !sIter.aPhrase ){ + return SQLITE_NOMEM; + } +- memset(sIter.aPhrase, 0, nByte); + + /* Initialize the contents of the SnippetIter object. Then iterate through + ** the set of phrases in the expression to populate the aPhrase[] array. +@@ -183443,7 +205836,9 @@ static int fts3BestSnippet( + sIter.nSnippet = nSnippet; + sIter.nPhrase = nList; + sIter.iCurrent = -1; +- rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter); ++ rc = sqlite3Fts3ExprIterate( ++ pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter ++ ); + if( rc==SQLITE_OK ){ + + /* Set the *pmSeen output variable. */ +@@ -183495,7 +205890,7 @@ static int fts3StringAppend( + } + + /* If there is insufficient space allocated at StrBuffer.z, use realloc() +- ** to grow the buffer until so that it is big enough to accomadate the ++ ** to grow the buffer until so that it is big enough to accommodate the + ** appended data. + */ + if( pStr->n+nAppend+1>=pStr->nAlloc ){ +@@ -183804,10 +206199,10 @@ static int fts3ExprLHitGather( + } + + /* +-** fts3ExprIterate() callback used to collect the "global" matchinfo stats +-** for a single query. ++** sqlite3Fts3ExprIterate() callback used to collect the "global" matchinfo ++** stats for a single query. + ** +-** fts3ExprIterate() callback to load the 'global' elements of a ++** sqlite3Fts3ExprIterate() callback to load the 'global' elements of a + ** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements + ** of the matchinfo array that are constant for all rows returned by the + ** current query. +@@ -183842,7 +206237,7 @@ static int fts3ExprGlobalHitsCb( + } + + /* +-** fts3ExprIterate() callback used to collect the "local" part of the ++** sqlite3Fts3ExprIterate() callback used to collect the "local" part of the + ** FTS3_MATCHINFO_HITS array. The local stats are those elements of the + ** array that are different for each row returned by the query. + */ +@@ -183907,16 +206302,16 @@ static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ + break; + + case FTS3_MATCHINFO_LHITS: +- nVal = pInfo->nCol * pInfo->nPhrase; ++ nVal = (size_t)pInfo->nCol * pInfo->nPhrase; + break; + + case FTS3_MATCHINFO_LHITS_BM: +- nVal = pInfo->nPhrase * ((pInfo->nCol + 31) / 32); ++ nVal = (size_t)pInfo->nPhrase * ((pInfo->nCol + 31) / 32); + break; + + default: + assert( cArg==FTS3_MATCHINFO_HITS ); +- nVal = pInfo->nCol * pInfo->nPhrase * 3; ++ nVal = (size_t)pInfo->nCol * pInfo->nPhrase * 3; + break; + } + +@@ -183997,10 +206392,12 @@ static int fts3MatchinfoLcsCb( + ** position list for the next column. + */ + static int fts3LcsIteratorAdvance(LcsIterator *pIter){ +- char *pRead = pIter->pRead; ++ char *pRead; + sqlite3_int64 iRead; + int rc = 0; + ++ if( NEVER(pIter==0) ) return 1; ++ pRead = pIter->pRead; + pRead += sqlite3Fts3GetVarint(pRead, &iRead); + if( iRead==0 || iRead==1 ){ + pRead = 0; +@@ -184034,10 +206431,9 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ + /* Allocate and populate the array of LcsIterator objects. The array + ** contains one element for each matchable phrase in the query. + **/ +- aIter = sqlite3_malloc64(sizeof(LcsIterator) * pCsr->nPhrase); ++ aIter = sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase); + if( !aIter ) return SQLITE_NOMEM; +- memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); +- (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); ++ (void)sqlite3Fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); + + for(i=0; inPhrase; i++){ + LcsIterator *pIter = &aIter[i]; +@@ -184214,11 +206610,11 @@ static int fts3MatchinfoValues( + rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0); + if( rc!=SQLITE_OK ) break; + } +- rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo); ++ rc = sqlite3Fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo); + sqlite3Fts3EvalTestDeferred(pCsr, &rc); + if( rc!=SQLITE_OK ) break; + } +- (void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo); ++ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo); + break; + } + } +@@ -184428,8 +206824,8 @@ typedef struct TermOffsetCtx TermOffsetCtx; + + struct TermOffset { + char *pList; /* Position-list */ +- int iPos; /* Position just read from pList */ +- int iOff; /* Offset of this term from read positions */ ++ i64 iPos; /* Position just read from pList */ ++ i64 iOff; /* Offset of this term from read positions */ + }; + + struct TermOffsetCtx { +@@ -184441,14 +206837,14 @@ struct TermOffsetCtx { + }; + + /* +-** This function is an fts3ExprIterate() callback used by sqlite3Fts3Offsets(). ++** This function is an sqlite3Fts3ExprIterate() callback used by sqlite3Fts3Offsets(). + */ + static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ + TermOffsetCtx *p = (TermOffsetCtx *)ctx; + int nTerm; /* Number of tokens in phrase */ + int iTerm; /* For looping through nTerm phrase terms */ + char *pList; /* Pointer to position list for phrase */ +- int iPos = 0; /* First position in position-list */ ++ i64 iPos = 0; /* First position in position-list */ + int rc; + + UNUSED_PARAMETER(iPhrase); +@@ -184469,6 +206865,22 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ + return rc; + } + ++/* ++** If expression pExpr is a phrase expression that uses an MSR query, ++** restart it as a regular, non-incremental query. Return SQLITE_OK ++** if successful, or an SQLite error code otherwise. ++*/ ++static int fts3ExprRestartIfCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ ++ TermOffsetCtx *p = (TermOffsetCtx*)ctx; ++ int rc = SQLITE_OK; ++ UNUSED_PARAMETER(iPhrase); ++ if( pExpr->pPhrase && pExpr->pPhrase->bIncr ){ ++ rc = sqlite3Fts3MsrCancel(p->pCsr, pExpr); ++ pExpr->pPhrase->bIncr = 0; ++ } ++ return rc; ++} ++ + /* + ** Implementation of offsets() function. + */ +@@ -184497,7 +206909,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( + if( rc!=SQLITE_OK ) goto offsets_out; + + /* Allocate the array of TermOffset iterators. */ +- sCtx.aTerm = (TermOffset *)sqlite3_malloc64(sizeof(TermOffset)*nToken); ++ sCtx.aTerm = (TermOffset *)sqlite3Fts3MallocZero(sizeof(TermOffset)*nToken); + if( 0==sCtx.aTerm ){ + rc = SQLITE_NOMEM; + goto offsets_out; +@@ -184505,6 +206917,12 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( + sCtx.iDocid = pCsr->iPrevId; + sCtx.pCsr = pCsr; + ++ /* If a query restart will be required, do it here, rather than later of ++ ** after pointers to poslist buffers that may be invalidated by a restart ++ ** have been saved. */ ++ rc = sqlite3Fts3ExprIterate(pCsr->pExpr, fts3ExprRestartIfCb, (void*)&sCtx); ++ if( rc!=SQLITE_OK ) goto offsets_out; ++ + /* Loop through the table columns, appending offset information to + ** string-buffer res for each column. + */ +@@ -184518,13 +206936,15 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( + const char *zDoc; + int nDoc; + +- /* Initialize the contents of sCtx.aTerm[] for column iCol. There is +- ** no way that this operation can fail, so the return code from +- ** fts3ExprIterate() can be discarded. ++ /* Initialize the contents of sCtx.aTerm[] for column iCol. This ++ ** operation may fail if the database contains corrupt records. + */ + sCtx.iCol = iCol; + sCtx.iTerm = 0; +- (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx); ++ rc = sqlite3Fts3ExprIterate( ++ pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx ++ ); ++ if( rc!=SQLITE_OK ) goto offsets_out; + + /* Retreive the text stored in column iCol. If an SQL NULL is stored + ** in column iCol, jump immediately to the next iteration of the loop. +@@ -184925,6 +207345,7 @@ static int unicodeOpen( + pCsr->aInput = (const unsigned char *)aInput; + if( aInput==0 ){ + pCsr->nInput = 0; ++ pCsr->aInput = (const unsigned char*)""; + }else if( nInput<0 ){ + pCsr->nInput = (int)strlen(aInput); + }else{ +@@ -185422,7 +207843,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ + #endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */ + + /************** End of fts3_unicode2.c ***************************************/ +-/************** Begin file json1.c *******************************************/ ++/************** Begin file json.c ********************************************/ + /* + ** 2015-08-12 + ** +@@ -185435,102 +207856,295 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ + ** + ****************************************************************************** + ** +-** This SQLite extension implements JSON functions. The interface is +-** modeled after MySQL JSON functions: +-** +-** https://dev.mysql.com/doc/refman/5.7/en/json.html ++** SQLite JSON functions. ++** ++** This file began as an extension in ext/misc/json1.c in 2015. That ++** extension proved so useful that it has now been moved into the core. ++** ++** The original design stored all JSON as pure text, canonical RFC-8259. ++** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16). ++** All generated JSON text still conforms strictly to RFC-8259, but text ++** with JSON-5 extensions is accepted as input. ++** ++** Beginning with version 3.45.0 (circa 2024-01-01), these routines also ++** accept BLOB values that have JSON encoded using a binary representation ++** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk ++** format for SQLite-JSONB is completely different and incompatible with ++** PostgreSQL-JSONB. ++** ++** Decoding and interpreting JSONB is still O(N) where N is the size of ++** the input, the same as text JSON. However, the constant of proportionality ++** for JSONB is much smaller due to faster parsing. The size of each ++** element in JSONB is encoded in its header, so there is no need to search ++** for delimiters using persnickety syntax rules. JSONB seems to be about ++** 3x faster than text JSON as a result. JSONB is also tends to be slightly ++** smaller than text JSON, by 5% or 10%, but there are corner cases where ++** JSONB can be slightly larger. So you are not far mistaken to say that ++** a JSONB blob is the same size as the equivalent RFC-8259 text. ++** ++** ++** THE JSONB ENCODING: ++** ++** Every JSON element is encoded in JSONB as a header and a payload. ++** The header is between 1 and 9 bytes in size. The payload is zero ++** or more bytes. ++** ++** The lower 4 bits of the first byte of the header determines the ++** element type: ++** ++** 0: NULL ++** 1: TRUE ++** 2: FALSE ++** 3: INT -- RFC-8259 integer literal ++** 4: INT5 -- JSON5 integer literal ++** 5: FLOAT -- RFC-8259 floating point literal ++** 6: FLOAT5 -- JSON5 floating point literal ++** 7: TEXT -- Text literal acceptable to both SQL and JSON ++** 8: TEXTJ -- Text containing RFC-8259 escapes ++** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes ++** 10: TEXTRAW -- Text containing unescaped syntax characters ++** 11: ARRAY ++** 12: OBJECT ++** ++** The other three possible values (13-15) are reserved for future ++** enhancements. ++** ++** The upper 4 bits of the first byte determine the size of the header ++** and sometimes also the size of the payload. If X is the first byte ++** of the element and if X>>4 is between 0 and 11, then the payload ++** will be that many bytes in size and the header is exactly one byte ++** in size. Other four values for X>>4 (12-15) indicate that the header ++** is more than one byte in size and that the payload size is determined ++** by the remainder of the header, interpreted as a unsigned big-endian ++** integer. ++** ++** Value of X>>4 Size integer Total header size ++** ------------- -------------------- ----------------- ++** 12 1 byte (0-255) 2 ++** 13 2 byte (0-65535) 3 ++** 14 4 byte (0-4294967295) 5 ++** 15 8 byte (0-1.8e19) 9 ++** ++** The payload size need not be expressed in its minimal form. For example, ++** if the payload size is 10, the size can be expressed in any of 5 different ++** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by one 0x0a byte, ++** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by ++** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and ++** a single byte of 0x0a. The shorter forms are preferred, of course, but ++** sometimes when generating JSONB, the payload size is not known in advance ++** and it is convenient to reserve sufficient header space to cover the ++** largest possible payload size and then come back later and patch up ++** the size when it becomes known, resulting in a non-minimal encoding. ++** ++** The value (X>>4)==15 is not actually used in the current implementation ++** (as SQLite is currently unable to handle BLOBs larger than about 2GB) ++** but is included in the design to allow for future enhancements. ++** ++** The payload follows the header. NULL, TRUE, and FALSE have no payload and ++** their payload size must always be zero. The payload for INT, INT5, ++** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the ++** "..." or '...' delimiters are omitted from the various text encodings. ++** The payload for ARRAY and OBJECT is a list of additional elements that ++** are the content for the array or object. The payload for an OBJECT ++** must be an even number of elements. The first element of each pair is ++** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW. ++** ++** A valid JSONB blob consists of a single element, as described above. ++** Usually this will be an ARRAY or OBJECT element which has many more ++** elements as its content. But the overall blob is just a single element. ++** ++** Input validation for JSONB blobs simply checks that the element type ++** code is between 0 and 12 and that the total size of the element ++** (header plus payload) is the same as the size of the BLOB. If those ++** checks are true, the BLOB is assumed to be JSONB and processing continues. ++** Errors are only raised if some other miscoding is discovered during ++** processing. + ** +-** For the time being, all JSON is stored as pure text. (We might add +-** a JSONB type in the future which stores a binary encoding of JSON in +-** a BLOB, but there is no support for JSONB in the current implementation. +-** This implementation parses JSON text at 250 MB/s, so it is hard to see +-** how JSONB might improve on that.) ++** Additional information can be found in the doc/jsonb.md file of the ++** canonical SQLite source tree. + */ +-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) +-#if !defined(SQLITEINT_H) +-/* #include "sqlite3ext.h" */ +-#endif +-SQLITE_EXTENSION_INIT1 +-/* #include */ +-/* #include */ +-/* #include */ +-/* #include */ ++#ifndef SQLITE_OMIT_JSON ++/* #include "sqliteInt.h" */ + +-/* Mark a function parameter as unused, to suppress nuisance compiler +-** warnings. */ +-#ifndef UNUSED_PARAM +-# define UNUSED_PARAM(X) (void)(X) +-#endif ++/* JSONB element types ++*/ ++#define JSONB_NULL 0 /* "null" */ ++#define JSONB_TRUE 1 /* "true" */ ++#define JSONB_FALSE 2 /* "false" */ ++#define JSONB_INT 3 /* integer acceptable to JSON and SQL */ ++#define JSONB_INT5 4 /* integer in 0x000 notation */ ++#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */ ++#define JSONB_FLOAT5 6 /* float with JSON5 extensions */ ++#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */ ++#define JSONB_TEXTJ 8 /* Text with JSON escapes */ ++#define JSONB_TEXT5 9 /* Text with JSON-5 escape */ ++#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */ ++#define JSONB_ARRAY 11 /* An array */ ++#define JSONB_OBJECT 12 /* An object */ ++ ++/* Human-readable names for the JSONB values. The index for each ++** string must correspond to the JSONB_* integer above. ++*/ ++static const char * const jsonbType[] = { ++ "null", "true", "false", "integer", "integer", ++ "real", "real", "text", "text", "text", ++ "text", "array", "object", "", "", "", "" ++}; + +-#ifndef LARGEST_INT64 +-# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) +-# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) ++/* ++** Growing our own isspace() routine this way is twice as fast as ++** the library isspace() function, resulting in a 7% overall performance ++** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). ++*/ ++static const char jsonIsSpace[] = { ++#ifdef SQLITE_ASCII ++/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ ++ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */ ++ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */ + #endif +- +-#ifndef deliberate_fall_through +-# define deliberate_fall_through ++#ifdef SQLITE_EBCDIC ++/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 0 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ ++ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */ ++ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */ ++ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */ + #endif + ++}; ++#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) ++ + /* +-** Versions of isspace(), isalnum() and isdigit() to which it is safe +-** to pass signed char values. ++** The set of all space characters recognized by jsonIsspace(). ++** Useful as the second argument to strspn(). + */ +-#ifdef sqlite3Isdigit +- /* Use the SQLite core versions if this routine is part of the +- ** SQLite amalgamation */ +-# define safe_isdigit(x) sqlite3Isdigit(x) +-# define safe_isalnum(x) sqlite3Isalnum(x) +-# define safe_isxdigit(x) sqlite3Isxdigit(x) +-#else +- /* Use the standard library for separate compilation */ +-#include /* amalgamator: keep */ +-# define safe_isdigit(x) isdigit((unsigned char)(x)) +-# define safe_isalnum(x) isalnum((unsigned char)(x)) +-# define safe_isxdigit(x) isxdigit((unsigned char)(x)) ++#ifdef SQLITE_ASCII ++static const char jsonSpaces[] = "\011\012\015\040"; + #endif ++#ifdef SQLITE_EBCDIC ++static const char jsonSpaces[] = "\005\045\015\100"; ++#endif ++ + + /* +-** Growing our own isspace() routine this way is twice as fast as +-** the library isspace() function, resulting in a 7% overall performance +-** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). ++** Characters that are special to JSON. Control characters, ++** '"' and '\\' and '\''. Actually, '\'' is not special to ++** canonical JSON, but it is special in JSON-5, so we include ++** it in the set of special characters. + */ +-static const char jsonIsSpace[] = { +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +-}; +-#define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) +- +-#ifndef SQLITE_AMALGAMATION +- /* Unsigned integer types. These are already defined in the sqliteInt.h, +- ** but the definitions need to be repeated for separate compilation. */ +- typedef sqlite3_uint64 u64; +- typedef unsigned int u32; +- typedef unsigned short int u16; +- typedef unsigned char u8; ++static const char jsonIsOk[256] = { ++#ifdef SQLITE_ASCII ++/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ ++ 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 2 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 3 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 5 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ ++ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */ ++#endif ++#ifdef SQLITE_EBCDIC ++/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ ++ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 3 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 5 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, /* 7 */ ++ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */ ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */ + #endif ++}; + + /* Objects */ ++typedef struct JsonCache JsonCache; + typedef struct JsonString JsonString; +-typedef struct JsonNode JsonNode; + typedef struct JsonParse JsonParse; + ++/* ++** Magic number used for the JSON parse cache in sqlite3_get_auxdata() ++*/ ++#define JSON_CACHE_ID (-429938) /* Cache entry */ ++#define JSON_CACHE_SIZE 4 /* Max number of cache entries */ ++ ++/* ++** jsonUnescapeOneChar() returns this invalid code point if it encounters ++** a syntax error. ++*/ ++#define JSON_INVALID_CHAR 0x99999 ++ ++/* A cache mapping JSON text into JSONB blobs. ++** ++** Each cache entry is a JsonParse object with the following restrictions: ++** ++** * The bReadOnly flag must be set ++** ++** * The aBlob[] array must be owned by the JsonParse object. In other ++** words, nBlobAlloc must be non-zero. ++** ++** * eEdit and delta must be zero. ++** ++** * zJson must be an RCStr. In other words bJsonIsRCStr must be true. ++*/ ++struct JsonCache { ++ sqlite3 *db; /* Database connection */ ++ int nUsed; /* Number of active entries in the cache */ ++ JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */ ++}; ++ + /* An instance of this object represents a JSON string + ** under construction. Really, this is a generic string accumulator + ** that can be and is used to create strings other than JSON. ++** ++** If the generated string is longer than will fit into the zSpace[] buffer, ++** then it will be an RCStr string. This aids with caching of large ++** JSON strings. + */ + struct JsonString { + sqlite3_context *pCtx; /* Function context - put error messages here */ +@@ -185538,88 +208152,227 @@ struct JsonString { + u64 nAlloc; /* Bytes of storage available in zBuf[] */ + u64 nUsed; /* Bytes of zBuf[] currently used */ + u8 bStatic; /* True if zBuf is static space */ +- u8 bErr; /* True if an error has been encountered */ ++ u8 eErr; /* True if an error has been encountered */ + char zSpace[100]; /* Initial static space */ + }; + +-/* JSON type values +-*/ +-#define JSON_NULL 0 +-#define JSON_TRUE 1 +-#define JSON_FALSE 2 +-#define JSON_INT 3 +-#define JSON_REAL 4 +-#define JSON_STRING 5 +-#define JSON_ARRAY 6 +-#define JSON_OBJECT 7 ++/* Allowed values for JsonString.eErr */ ++#define JSTRING_OOM 0x01 /* Out of memory */ ++#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */ ++#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */ + +-/* The "subtype" set for JSON values */ ++/* The "subtype" set for text JSON values passed through using ++** sqlite3_result_subtype() and sqlite3_value_subtype(). ++*/ + #define JSON_SUBTYPE 74 /* Ascii for "J" */ + + /* +-** Names of the various JSON types: +-*/ +-static const char * const jsonType[] = { +- "null", "true", "false", "integer", "real", "text", "array", "object" +-}; +- +-/* Bit values for the JsonNode.jnFlag field ++** Bit values for the flags passed into various SQL function implementations ++** via the sqlite3_user_data() value. + */ +-#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ +-#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ +-#define JNODE_REMOVE 0x04 /* Do not output */ +-#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ +-#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ +-#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ +-#define JNODE_LABEL 0x40 /* Is a label of an object */ ++#define JSON_JSON 0x01 /* Result is always JSON */ ++#define JSON_SQL 0x02 /* Result is always SQL */ ++#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ ++#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ ++#define JSON_BLOB 0x08 /* Use the BLOB output format */ + + +-/* A single node of parsed JSON +-*/ +-struct JsonNode { +- u8 eType; /* One of the JSON_ type values */ +- u8 jnFlags; /* JNODE flags */ +- u32 n; /* Bytes of content, or number of sub-nodes */ +- union { +- const char *zJContent; /* Content for INT, REAL, and STRING */ +- u32 iAppend; /* More terms for ARRAY and OBJECT */ +- u32 iKey; /* Key for ARRAY objects in json_tree() */ +- u32 iReplace; /* Replacement content for JNODE_REPLACE */ +- JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */ +- } u; +-}; +- +-/* A completely parsed JSON string ++/* A parsed JSON value. Lifecycle: ++** ++** 1. JSON comes in and is parsed into a JSONB value in aBlob. The ++** original text is stored in zJson. This step is skipped if the ++** input is JSONB instead of text JSON. ++** ++** 2. The aBlob[] array is searched using the JSON path notation, if needed. ++** ++** 3. Zero or more changes are made to aBlob[] (via json_remove() or ++** json_replace() or json_patch() or similar). ++** ++** 4. New JSON text is generated from the aBlob[] for output. This step ++** is skipped if the function is one of the jsonb_* functions that ++** returns JSONB instead of text JSON. + */ + struct JsonParse { +- u32 nNode; /* Number of slots of aNode[] used */ +- u32 nAlloc; /* Number of slots of aNode[] allocated */ +- JsonNode *aNode; /* Array of nodes containing the parse */ +- const char *zJson; /* Original JSON string */ +- u32 *aUp; /* Index of parent of each node */ +- u8 oom; /* Set to true if out of memory */ +- u8 nErr; /* Number of errors seen */ +- u16 iDepth; /* Nesting depth */ ++ u8 *aBlob; /* JSONB representation of JSON value */ ++ u32 nBlob; /* Bytes of aBlob[] actually used */ ++ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ ++ char *zJson; /* Json text used for parsing */ ++ sqlite3 *db; /* The database connection to which this object belongs */ + int nJson; /* Length of the zJson string in bytes */ +- u32 iHold; /* Replace cache line with the lowest iHold value */ ++ u32 nJPRef; /* Number of references to this object */ ++ u32 iErr; /* Error location in zJson[] */ ++ u16 iDepth; /* Nesting depth */ ++ u8 nErr; /* Number of errors seen */ ++ u8 oom; /* Set to true if out of memory */ ++ u8 bJsonIsRCStr; /* True if zJson is an RCStr */ ++ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ ++ u8 bReadOnly; /* Do not modify. */ ++ /* Search and edit information. See jsonLookupStep() */ ++ u8 eEdit; /* Edit operation to apply */ ++ int delta; /* Size change due to the edit */ ++ u32 nIns; /* Number of bytes to insert */ ++ u32 iLabel; /* Location of label if search landed on an object value */ ++ u8 *aIns; /* Content to be inserted */ + }; + ++/* Allowed values for JsonParse.eEdit */ ++#define JEDIT_DEL 1 /* Delete if exists */ ++#define JEDIT_REPL 2 /* Overwrite if exists */ ++#define JEDIT_INS 3 /* Insert if not exists */ ++#define JEDIT_SET 4 /* Insert or overwrite */ ++ + /* + ** Maximum nesting depth of JSON for this implementation. + ** + ** This limit is needed to avoid a stack overflow in the recursive +-** descent parser. A depth of 2000 is far deeper than any sane JSON +-** should go. ++** descent parser. A depth of 1000 is far deeper than any sane JSON ++** should go. Historical note: This limit was 2000 prior to version 3.42.0 ++*/ ++#ifndef SQLITE_JSON_MAX_DEPTH ++# define JSON_MAX_DEPTH 1000 ++#else ++# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH ++#endif ++ ++/* ++** Allowed values for the flgs argument to jsonParseFuncArg(); + */ +-#define JSON_MAX_DEPTH 2000 ++#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ ++#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */ ++ ++/************************************************************************** ++** Forward references ++**************************************************************************/ ++static void jsonReturnStringAsBlob(JsonString*); ++static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p); ++static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*); ++static void jsonReturnParse(sqlite3_context*,JsonParse*); ++static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); ++static void jsonParseFree(JsonParse*); ++static u32 jsonbPayloadSize(const JsonParse*, u32, u32*); ++static u32 jsonUnescapeOneChar(const char*, u32, u32*); ++ ++/************************************************************************** ++** Utility routines for dealing with JsonCache objects ++**************************************************************************/ ++ ++/* ++** Free a JsonCache object. ++*/ ++static void jsonCacheDelete(JsonCache *p){ ++ int i; ++ for(i=0; inUsed; i++){ ++ jsonParseFree(p->a[i]); ++ } ++ sqlite3DbFree(p->db, p); ++} ++static void jsonCacheDeleteGeneric(void *p){ ++ jsonCacheDelete((JsonCache*)p); ++} ++ ++/* ++** Insert a new entry into the cache. If the cache is full, expel ++** the least recently used entry. Return SQLITE_OK on success or a ++** result code otherwise. ++** ++** Cache entries are stored in age order, oldest first. ++*/ ++static int jsonCacheInsert( ++ sqlite3_context *ctx, /* The SQL statement context holding the cache */ ++ JsonParse *pParse /* The parse object to be added to the cache */ ++){ ++ JsonCache *p; ++ ++ assert( pParse->zJson!=0 ); ++ assert( pParse->bJsonIsRCStr ); ++ assert( pParse->delta==0 ); ++ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); ++ if( p==0 ){ ++ sqlite3 *db = sqlite3_context_db_handle(ctx); ++ p = sqlite3DbMallocZero(db, sizeof(*p)); ++ if( p==0 ) return SQLITE_NOMEM; ++ p->db = db; ++ sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric); ++ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); ++ if( p==0 ) return SQLITE_NOMEM; ++ } ++ if( p->nUsed >= JSON_CACHE_SIZE ){ ++ jsonParseFree(p->a[0]); ++ memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0])); ++ p->nUsed = JSON_CACHE_SIZE-1; ++ } ++ assert( pParse->nBlobAlloc>0 ); ++ pParse->eEdit = 0; ++ pParse->nJPRef++; ++ pParse->bReadOnly = 1; ++ p->a[p->nUsed] = pParse; ++ p->nUsed++; ++ return SQLITE_OK; ++} ++ ++/* ++** Search for a cached translation the json text supplied by pArg. Return ++** the JsonParse object if found. Return NULL if not found. ++** ++** When a match if found, the matching entry is moved to become the ++** most-recently used entry if it isn't so already. ++** ++** The JsonParse object returned still belongs to the Cache and might ++** be deleted at any moment. If the caller wants the JsonParse to ++** linger, it needs to increment the nPJRef reference counter. ++*/ ++static JsonParse *jsonCacheSearch( ++ sqlite3_context *ctx, /* The SQL statement context holding the cache */ ++ sqlite3_value *pArg /* Function argument containing SQL text */ ++){ ++ JsonCache *p; ++ int i; ++ const char *zJson; ++ int nJson; ++ ++ if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){ ++ return 0; ++ } ++ zJson = (const char*)sqlite3_value_text(pArg); ++ if( zJson==0 ) return 0; ++ nJson = sqlite3_value_bytes(pArg); ++ ++ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); ++ if( p==0 ){ ++ return 0; ++ } ++ for(i=0; inUsed; i++){ ++ if( p->a[i]->zJson==zJson ) break; ++ } ++ if( i>=p->nUsed ){ ++ for(i=0; inUsed; i++){ ++ if( p->a[i]->nJson!=nJson ) continue; ++ if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break; ++ } ++ } ++ if( inUsed ){ ++ if( inUsed-1 ){ ++ /* Make the matching entry the most recently used entry */ ++ JsonParse *tmp = p->a[i]; ++ memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); ++ p->a[p->nUsed-1] = tmp; ++ i = p->nUsed - 1; ++ } ++ assert( p->a[i]->delta==0 ); ++ return p->a[i]; ++ }else{ ++ return 0; ++ } ++} + + /************************************************************************** + ** Utility routines for dealing with JsonString objects + **************************************************************************/ + +-/* Set the JsonString object to an empty string ++/* Turn uninitialized bulk memory into a valid JsonString object ++** holding a zero-length string. + */ +-static void jsonZero(JsonString *p){ ++static void jsonStringZero(JsonString *p){ + p->zBuf = p->zSpace; + p->nAlloc = sizeof(p->zSpace); + p->nUsed = 0; +@@ -185628,53 +208381,51 @@ static void jsonZero(JsonString *p){ + + /* Initialize the JsonString object + */ +-static void jsonInit(JsonString *p, sqlite3_context *pCtx){ ++static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){ + p->pCtx = pCtx; +- p->bErr = 0; +- jsonZero(p); ++ p->eErr = 0; ++ jsonStringZero(p); + } + +- + /* Free all allocated memory and reset the JsonString object back to its + ** initial state. + */ +-static void jsonReset(JsonString *p){ +- if( !p->bStatic ) sqlite3_free(p->zBuf); +- jsonZero(p); ++static void jsonStringReset(JsonString *p){ ++ if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf); ++ jsonStringZero(p); + } + +- + /* Report an out-of-memory (OOM) condition + */ +-static void jsonOom(JsonString *p){ +- p->bErr = 1; +- sqlite3_result_error_nomem(p->pCtx); +- jsonReset(p); ++static void jsonStringOom(JsonString *p){ ++ p->eErr |= JSTRING_OOM; ++ if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx); ++ jsonStringReset(p); + } + + /* Enlarge pJson->zBuf so that it can hold at least N more bytes. + ** Return zero on success. Return non-zero on an OOM error + */ +-static int jsonGrow(JsonString *p, u32 N){ ++static int jsonStringGrow(JsonString *p, u32 N){ + u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+10; + char *zNew; + if( p->bStatic ){ +- if( p->bErr ) return 1; +- zNew = sqlite3_malloc64(nTotal); ++ if( p->eErr ) return 1; ++ zNew = sqlite3RCStrNew(nTotal); + if( zNew==0 ){ +- jsonOom(p); ++ jsonStringOom(p); + return SQLITE_NOMEM; + } + memcpy(zNew, p->zBuf, (size_t)p->nUsed); + p->zBuf = zNew; + p->bStatic = 0; + }else{ +- zNew = sqlite3_realloc64(p->zBuf, nTotal); +- if( zNew==0 ){ +- jsonOom(p); ++ p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); ++ if( p->zBuf==0 ){ ++ p->eErr |= JSTRING_OOM; ++ jsonStringZero(p); + return SQLITE_NOMEM; + } +- p->zBuf = zNew; + } + p->nAlloc = nTotal; + return SQLITE_OK; +@@ -185682,18 +208433,40 @@ static int jsonGrow(JsonString *p, u32 N){ + + /* Append N bytes from zIn onto the end of the JsonString string. + */ +-static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ +- if( N==0 ) return; +- if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; ++static SQLITE_NOINLINE void jsonStringExpandAndAppend( ++ JsonString *p, ++ const char *zIn, ++ u32 N ++){ ++ assert( N>0 ); ++ if( jsonStringGrow(p,N) ) return; + memcpy(p->zBuf+p->nUsed, zIn, N); + p->nUsed += N; + } ++static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ ++ if( N==0 ) return; ++ if( N+p->nUsed >= p->nAlloc ){ ++ jsonStringExpandAndAppend(p,zIn,N); ++ }else{ ++ memcpy(p->zBuf+p->nUsed, zIn, N); ++ p->nUsed += N; ++ } ++} ++static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ ++ assert( N>0 ); ++ if( N+p->nUsed >= p->nAlloc ){ ++ jsonStringExpandAndAppend(p,zIn,N); ++ }else{ ++ memcpy(p->zBuf+p->nUsed, zIn, N); ++ p->nUsed += N; ++ } ++} + + /* Append formatted text (not to exceed N bytes) to the JsonString. + */ + static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ + va_list ap; +- if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return; ++ if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return; + va_start(ap, zFormat); + sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap); + va_end(ap); +@@ -185702,10 +208475,38 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ + + /* Append a single character + */ +-static void jsonAppendChar(JsonString *p, char c){ +- if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return; ++static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){ ++ if( jsonStringGrow(p,1) ) return; + p->zBuf[p->nUsed++] = c; + } ++static void jsonAppendChar(JsonString *p, char c){ ++ if( p->nUsed>=p->nAlloc ){ ++ jsonAppendCharExpand(p,c); ++ }else{ ++ p->zBuf[p->nUsed++] = c; ++ } ++} ++ ++/* Remove a single character from the end of the string ++*/ ++static void jsonStringTrimOneChar(JsonString *p){ ++ if( p->eErr==0 ){ ++ assert( p->nUsed>0 ); ++ p->nUsed--; ++ } ++} ++ ++ ++/* Make sure there is a zero terminator on p->zBuf[] ++** ++** Return true on success. Return false if an OOM prevents this ++** from happening. ++*/ ++static int jsonStringTerminate(JsonString *p){ ++ jsonAppendChar(p, 0); ++ jsonStringTrimOneChar(p); ++ return p->eErr==0; ++} + + /* Append a comma separator to the output buffer, if the previous + ** character is not '[' or '{'. +@@ -185714,68 +208515,137 @@ static void jsonAppendSeparator(JsonString *p){ + char c; + if( p->nUsed==0 ) return; + c = p->zBuf[p->nUsed-1]; +- if( c!='[' && c!='{' ) jsonAppendChar(p, ','); ++ if( c=='[' || c=='{' ) return; ++ jsonAppendChar(p, ','); ++} ++ ++/* c is a control character. Append the canonical JSON representation ++** of that control character to p. ++** ++** This routine assumes that the output buffer has already been enlarged ++** sufficiently to hold the worst-case encoding plus a nul terminator. ++*/ ++static void jsonAppendControlChar(JsonString *p, u8 c){ ++ static const char aSpecial[] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++ }; ++ assert( sizeof(aSpecial)==32 ); ++ assert( aSpecial['\b']=='b' ); ++ assert( aSpecial['\f']=='f' ); ++ assert( aSpecial['\n']=='n' ); ++ assert( aSpecial['\r']=='r' ); ++ assert( aSpecial['\t']=='t' ); ++ assert( c>=0 && cnUsed+7 <= p->nAlloc ); ++ if( aSpecial[c] ){ ++ p->zBuf[p->nUsed] = '\\'; ++ p->zBuf[p->nUsed+1] = aSpecial[c]; ++ p->nUsed += 2; ++ }else{ ++ p->zBuf[p->nUsed] = '\\'; ++ p->zBuf[p->nUsed+1] = 'u'; ++ p->zBuf[p->nUsed+2] = '0'; ++ p->zBuf[p->nUsed+3] = '0'; ++ p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4]; ++ p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf]; ++ p->nUsed += 6; ++ } + } + + /* Append the N-byte string in zIn to the end of the JsonString string +-** under construction. Enclose the string in "..." and escape +-** any double-quotes or backslash characters contained within the ++** under construction. Enclose the string in double-quotes ("...") and ++** escape any double-quotes or backslash characters contained within the + ** string. ++** ++** This routine is a high-runner. There is a measurable performance ++** increase associated with unwinding the jsonIsOk[] loop. + */ + static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ +- u32 i; +- if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return; ++ u32 k; ++ u8 c; ++ const u8 *z = (const u8*)zIn; ++ if( z==0 ) return; ++ if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return; + p->zBuf[p->nUsed++] = '"'; +- for(i=0; i=N ){ ++ while( k=N ){ ++ if( k>0 ){ ++ memcpy(&p->zBuf[p->nUsed], z, k); ++ p->nUsed += k; ++ } ++ break; ++ } ++ if( k>0 ){ ++ memcpy(&p->zBuf[p->nUsed], z, k); ++ p->nUsed += k; ++ z += k; ++ N -= k; ++ } ++ c = z[0]; + if( c=='"' || c=='\\' ){ +- json_simple_escape: +- if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return; +- p->zBuf[p->nUsed++] = '\\'; +- }else if( c<=0x1f ){ +- static const char aSpecial[] = { +- 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +- }; +- assert( sizeof(aSpecial)==32 ); +- assert( aSpecial['\b']=='b' ); +- assert( aSpecial['\f']=='f' ); +- assert( aSpecial['\n']=='n' ); +- assert( aSpecial['\r']=='r' ); +- assert( aSpecial['\t']=='t' ); +- if( aSpecial[c] ){ +- c = aSpecial[c]; +- goto json_simple_escape; +- } +- if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return; ++ if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return; + p->zBuf[p->nUsed++] = '\\'; +- p->zBuf[p->nUsed++] = 'u'; +- p->zBuf[p->nUsed++] = '0'; +- p->zBuf[p->nUsed++] = '0'; +- p->zBuf[p->nUsed++] = '0' + (c>>4); +- c = "0123456789abcdef"[c&0xf]; ++ p->zBuf[p->nUsed++] = c; ++ }else if( c=='\'' ){ ++ p->zBuf[p->nUsed++] = c; ++ }else{ ++ if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; ++ jsonAppendControlChar(p, c); + } +- p->zBuf[p->nUsed++] = c; ++ z++; ++ N--; + } + p->zBuf[p->nUsed++] = '"'; + assert( p->nUsednAlloc ); + } + + /* +-** Append a function parameter value to the JSON string under +-** construction. ++** Append an sqlite3_value (such as a function parameter) to the JSON ++** string under construction in p. + */ +-static void jsonAppendValue( ++static void jsonAppendSqlValue( + JsonString *p, /* Append to this JSON string */ + sqlite3_value *pValue /* Value to append */ + ){ + switch( sqlite3_value_type(pValue) ){ + case SQLITE_NULL: { +- jsonAppendRaw(p, "null", 4); ++ jsonAppendRawNZ(p, "null", 4); ++ break; ++ } ++ case SQLITE_FLOAT: { ++ jsonPrintf(100, p, "%!0.15g", sqlite3_value_double(pValue)); + break; + } +- case SQLITE_INTEGER: +- case SQLITE_FLOAT: { ++ case SQLITE_INTEGER: { + const char *z = (const char*)sqlite3_value_text(pValue); + u32 n = (u32)sqlite3_value_bytes(pValue); + jsonAppendRaw(p, z, n); +@@ -185792,177 +208662,125 @@ static void jsonAppendValue( + break; + } + default: { +- if( p->bErr==0 ){ ++ JsonParse px; ++ memset(&px, 0, sizeof(px)); ++ if( jsonArgIsJsonb(pValue, &px) ){ ++ jsonTranslateBlobToText(&px, 0, p); ++ }else if( p->eErr==0 ){ + sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); +- p->bErr = 2; +- jsonReset(p); ++ p->eErr = JSTRING_ERR; ++ jsonStringReset(p); + } + break; + } + } + } + +- +-/* Make the JSON in p the result of the SQL function. ++/* Make the text in p (which is probably a generated JSON text string) ++** the result of the SQL function. ++** ++** The JsonString is reset. ++** ++** If pParse and ctx are both non-NULL, then the SQL string in p is ++** loaded into the zJson field of the pParse object as a RCStr and the ++** pParse is added to the cache. + */ +-static void jsonResult(JsonString *p){ +- if( p->bErr==0 ){ +- sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, +- p->bStatic ? SQLITE_TRANSIENT : sqlite3_free, +- SQLITE_UTF8); +- jsonZero(p); ++static void jsonReturnString( ++ JsonString *p, /* String to return */ ++ JsonParse *pParse, /* JSONB source or NULL */ ++ sqlite3_context *ctx /* Where to cache */ ++){ ++ assert( (pParse!=0)==(ctx!=0) ); ++ assert( ctx==0 || ctx==p->pCtx ); ++ if( p->eErr==0 ){ ++ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); ++ if( flags & JSON_BLOB ){ ++ jsonReturnStringAsBlob(p); ++ }else if( p->bStatic ){ ++ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, ++ SQLITE_TRANSIENT, SQLITE_UTF8); ++ }else if( jsonStringTerminate(p) ){ ++ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){ ++ int rc; ++ pParse->zJson = sqlite3RCStrRef(p->zBuf); ++ pParse->nJson = p->nUsed; ++ pParse->bJsonIsRCStr = 1; ++ rc = jsonCacheInsert(ctx, pParse); ++ if( rc==SQLITE_NOMEM ){ ++ sqlite3_result_error_nomem(ctx); ++ jsonStringReset(p); ++ return; ++ } ++ } ++ sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed, ++ sqlite3RCStrUnref, ++ SQLITE_UTF8); ++ }else{ ++ sqlite3_result_error_nomem(p->pCtx); ++ } ++ }else if( p->eErr & JSTRING_OOM ){ ++ sqlite3_result_error_nomem(p->pCtx); ++ }else if( p->eErr & JSTRING_MALFORMED ){ ++ sqlite3_result_error(p->pCtx, "malformed JSON", -1); + } +- assert( p->bStatic ); ++ jsonStringReset(p); + } + + /************************************************************************** +-** Utility routines for dealing with JsonNode and JsonParse objects ++** Utility routines for dealing with JsonParse objects + **************************************************************************/ + +-/* +-** Return the number of consecutive JsonNode slots need to represent +-** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and +-** OBJECT types, the number might be larger. +-** +-** Appended elements are not counted. The value returned is the number +-** by which the JsonNode counter should increment in order to go to the +-** next peer value. +-*/ +-static u32 jsonNodeSize(JsonNode *pNode){ +- return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1; +-} +- + /* + ** Reclaim all memory allocated by a JsonParse object. But do not + ** delete the JsonParse object itself. + */ + static void jsonParseReset(JsonParse *pParse){ +- sqlite3_free(pParse->aNode); +- pParse->aNode = 0; +- pParse->nNode = 0; +- pParse->nAlloc = 0; +- sqlite3_free(pParse->aUp); +- pParse->aUp = 0; ++ assert( pParse->nJPRef<=1 ); ++ if( pParse->bJsonIsRCStr ){ ++ sqlite3RCStrUnref(pParse->zJson); ++ pParse->zJson = 0; ++ pParse->nJson = 0; ++ pParse->bJsonIsRCStr = 0; ++ } ++ if( pParse->nBlobAlloc ){ ++ sqlite3DbFree(pParse->db, pParse->aBlob); ++ pParse->aBlob = 0; ++ pParse->nBlob = 0; ++ pParse->nBlobAlloc = 0; ++ } + } + + /* +-** Free a JsonParse object that was obtained from sqlite3_malloc(). ++** Decrement the reference count on the JsonParse object. When the ++** count reaches zero, free the object. + */ + static void jsonParseFree(JsonParse *pParse){ +- jsonParseReset(pParse); +- sqlite3_free(pParse); +-} +- +-/* +-** Convert the JsonNode pNode into a pure JSON string and +-** append to pOut. Subsubstructure is also included. Return +-** the number of JsonNode objects that are encoded. +-*/ +-static void jsonRenderNode( +- JsonNode *pNode, /* The node to render */ +- JsonString *pOut, /* Write JSON here */ +- sqlite3_value **aReplace /* Replacement values */ +-){ +- if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ +- if( pNode->jnFlags & JNODE_REPLACE ){ +- jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); +- return; +- } +- pNode = pNode->u.pPatch; +- } +- switch( pNode->eType ){ +- default: { +- assert( pNode->eType==JSON_NULL ); +- jsonAppendRaw(pOut, "null", 4); +- break; +- } +- case JSON_TRUE: { +- jsonAppendRaw(pOut, "true", 4); +- break; +- } +- case JSON_FALSE: { +- jsonAppendRaw(pOut, "false", 5); +- break; +- } +- case JSON_STRING: { +- if( pNode->jnFlags & JNODE_RAW ){ +- jsonAppendString(pOut, pNode->u.zJContent, pNode->n); +- break; +- } +- /* no break */ deliberate_fall_through +- } +- case JSON_REAL: +- case JSON_INT: { +- jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); +- break; +- } +- case JSON_ARRAY: { +- u32 j = 1; +- jsonAppendChar(pOut, '['); +- for(;;){ +- while( j<=pNode->n ){ +- if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){ +- jsonAppendSeparator(pOut); +- jsonRenderNode(&pNode[j], pOut, aReplace); +- } +- j += jsonNodeSize(&pNode[j]); +- } +- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; +- pNode = &pNode[pNode->u.iAppend]; +- j = 1; +- } +- jsonAppendChar(pOut, ']'); +- break; +- } +- case JSON_OBJECT: { +- u32 j = 1; +- jsonAppendChar(pOut, '{'); +- for(;;){ +- while( j<=pNode->n ){ +- if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){ +- jsonAppendSeparator(pOut); +- jsonRenderNode(&pNode[j], pOut, aReplace); +- jsonAppendChar(pOut, ':'); +- jsonRenderNode(&pNode[j+1], pOut, aReplace); +- } +- j += 1 + jsonNodeSize(&pNode[j+1]); +- } +- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; +- pNode = &pNode[pNode->u.iAppend]; +- j = 1; +- } +- jsonAppendChar(pOut, '}'); +- break; ++ if( pParse ){ ++ if( pParse->nJPRef>1 ){ ++ pParse->nJPRef--; ++ }else{ ++ jsonParseReset(pParse); ++ sqlite3DbFree(pParse->db, pParse); + } + } + } + +-/* +-** Return a JsonNode and all its descendents as a JSON string. +-*/ +-static void jsonReturnJson( +- JsonNode *pNode, /* Node to return */ +- sqlite3_context *pCtx, /* Return value for this function */ +- sqlite3_value **aReplace /* Array of replacement values */ +-){ +- JsonString s; +- jsonInit(&s, pCtx); +- jsonRenderNode(pNode, &s, aReplace); +- jsonResult(&s); +- sqlite3_result_subtype(pCtx, JSON_SUBTYPE); +-} ++/************************************************************************** ++** Utility routines for the JSON text parser ++**************************************************************************/ + + /* + ** Translate a single byte of Hex into an integer. +-** This routine only works if h really is a valid hexadecimal +-** character: 0..9a..fA..F ++** This routine only gives a correct answer if h really is a valid hexadecimal ++** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not ++** assert() if the digit is not hex. + */ + static u8 jsonHexToInt(int h){ +- assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); ++#ifdef SQLITE_ASCII ++ h += 9*(1&(h>>6)); ++#endif + #ifdef SQLITE_EBCDIC + h += 9*(1&~(h>>4)); +-#else +- h += 9*(1&(h>>6)); + #endif + return (u8)(h & 0xf); + } +@@ -185972,10 +208790,6 @@ static u8 jsonHexToInt(int h){ + */ + static u32 jsonHexToInt4(const char *z){ + u32 v; +- assert( safe_isxdigit(z[0]) ); +- assert( safe_isxdigit(z[1]) ); +- assert( safe_isxdigit(z[2]) ); +- assert( safe_isxdigit(z[3]) ); + v = (jsonHexToInt(z[0])<<12) + + (jsonHexToInt(z[1])<<8) + + (jsonHexToInt(z[2])<<4) +@@ -185984,409 +208798,1116 @@ static u32 jsonHexToInt4(const char *z){ + } + + /* +-** Make the JsonNode the return value of the function. ++** Return true if z[] begins with 2 (or more) hexadecimal digits + */ +-static void jsonReturn( +- JsonNode *pNode, /* Node to return */ +- sqlite3_context *pCtx, /* Return value for this function */ +- sqlite3_value **aReplace /* Array of replacement values */ +-){ +- switch( pNode->eType ){ +- default: { +- assert( pNode->eType==JSON_NULL ); +- sqlite3_result_null(pCtx); +- break; +- } +- case JSON_TRUE: { +- sqlite3_result_int(pCtx, 1); +- break; +- } +- case JSON_FALSE: { +- sqlite3_result_int(pCtx, 0); +- break; +- } +- case JSON_INT: { +- sqlite3_int64 i = 0; +- const char *z = pNode->u.zJContent; +- if( z[0]=='-' ){ z++; } +- while( z[0]>='0' && z[0]<='9' ){ +- unsigned v = *(z++) - '0'; +- if( i>=LARGEST_INT64/10 ){ +- if( i>LARGEST_INT64/10 ) goto int_as_real; +- if( z[0]>='0' && z[0]<='9' ) goto int_as_real; +- if( v==9 ) goto int_as_real; +- if( v==8 ){ +- if( pNode->u.zJContent[0]=='-' ){ +- sqlite3_result_int64(pCtx, SMALLEST_INT64); +- goto int_done; +- }else{ +- goto int_as_real; ++static int jsonIs2Hex(const char *z){ ++ return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]); ++} ++ ++/* ++** Return true if z[] begins with 4 (or more) hexadecimal digits ++*/ ++static int jsonIs4Hex(const char *z){ ++ return jsonIs2Hex(z) && jsonIs2Hex(&z[2]); ++} ++ ++/* ++** Return the number of bytes of JSON5 whitespace at the beginning of ++** the input string z[]. ++** ++** JSON5 whitespace consists of any of the following characters: ++** ++** Unicode UTF-8 Name ++** U+0009 09 horizontal tab ++** U+000a 0a line feed ++** U+000b 0b vertical tab ++** U+000c 0c form feed ++** U+000d 0d carriage return ++** U+0020 20 space ++** U+00a0 c2 a0 non-breaking space ++** U+1680 e1 9a 80 ogham space mark ++** U+2000 e2 80 80 en quad ++** U+2001 e2 80 81 em quad ++** U+2002 e2 80 82 en space ++** U+2003 e2 80 83 em space ++** U+2004 e2 80 84 three-per-em space ++** U+2005 e2 80 85 four-per-em space ++** U+2006 e2 80 86 six-per-em space ++** U+2007 e2 80 87 figure space ++** U+2008 e2 80 88 punctuation space ++** U+2009 e2 80 89 thin space ++** U+200a e2 80 8a hair space ++** U+2028 e2 80 a8 line separator ++** U+2029 e2 80 a9 paragraph separator ++** U+202f e2 80 af narrow no-break space (NNBSP) ++** U+205f e2 81 9f medium mathematical space (MMSP) ++** U+3000 e3 80 80 ideographical space ++** U+FEFF ef bb bf byte order mark ++** ++** In addition, comments between '/', '*' and '*', '/' and ++** from '/', '/' to end-of-line are also considered to be whitespace. ++*/ ++static int json5Whitespace(const char *zIn){ ++ int n = 0; ++ const u8 *z = (u8*)zIn; ++ while( 1 /*exit by "goto whitespace_done"*/ ){ ++ switch( z[n] ){ ++ case 0x09: ++ case 0x0a: ++ case 0x0b: ++ case 0x0c: ++ case 0x0d: ++ case 0x20: { ++ n++; ++ break; ++ } ++ case '/': { ++ if( z[n+1]=='*' && z[n+2]!=0 ){ ++ int j; ++ for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ ++ if( z[j]==0 ) goto whitespace_done; ++ } ++ n = j+1; ++ break; ++ }else if( z[n+1]=='/' ){ ++ int j; ++ char c; ++ for(j=n+2; (c = z[j])!=0; j++){ ++ if( c=='\n' || c=='\r' ) break; ++ if( 0xe2==(u8)c && 0x80==(u8)z[j+1] ++ && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]) ++ ){ ++ j += 2; ++ break; + } + } ++ n = j; ++ if( z[n] ) n++; ++ break; + } +- i = i*10 + v; ++ goto whitespace_done; + } +- if( pNode->u.zJContent[0]=='-' ){ i = -i; } +- sqlite3_result_int64(pCtx, i); +- int_done: +- break; +- int_as_real: i=0; /* no break */ deliberate_fall_through +- } +- case JSON_REAL: { +- double r; +-#ifdef SQLITE_AMALGAMATION +- const char *z = pNode->u.zJContent; +- sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); +-#else +- r = strtod(pNode->u.zJContent, 0); +-#endif +- sqlite3_result_double(pCtx, r); +- break; +- } +- case JSON_STRING: { +-#if 0 /* Never happens because JNODE_RAW is only set by json_set(), +- ** json_insert() and json_replace() and those routines do not +- ** call jsonReturn() */ +- if( pNode->jnFlags & JNODE_RAW ){ +- sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, +- SQLITE_TRANSIENT); +- }else +-#endif +- assert( (pNode->jnFlags & JNODE_RAW)==0 ); +- if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ +- /* JSON formatted without any backslash-escapes */ +- sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, +- SQLITE_TRANSIENT); +- }else{ +- /* Translate JSON formatted string into raw text */ +- u32 i; +- u32 n = pNode->n; +- const char *z = pNode->u.zJContent; +- char *zOut; +- u32 j; +- zOut = sqlite3_malloc( n+1 ); +- if( zOut==0 ){ +- sqlite3_result_error_nomem(pCtx); ++ case 0xc2: { ++ if( z[n+1]==0xa0 ){ ++ n += 2; + break; + } +- for(i=1, j=0; i>6)); +- zOut[j++] = 0x80 | (v&0x3f); +- }else{ +- u32 vlo; +- if( (v&0xfc00)==0xd800 +- && i>18); +- zOut[j++] = 0x80 | ((v>>12)&0x3f); +- zOut[j++] = 0x80 | ((v>>6)&0x3f); +- zOut[j++] = 0x80 | (v&0x3f); +- }else{ +- zOut[j++] = 0xe0 | (v>>12); +- zOut[j++] = 0x80 | ((v>>6)&0x3f); +- zOut[j++] = 0x80 | (v&0x3f); +- } +- } +- }else{ +- if( c=='b' ){ +- c = '\b'; +- }else if( c=='f' ){ +- c = '\f'; +- }else if( c=='n' ){ +- c = '\n'; +- }else if( c=='r' ){ +- c = '\r'; +- }else if( c=='t' ){ +- c = '\t'; +- } +- zOut[j++] = c; +- } ++ goto whitespace_done; ++ } ++ case 0xe1: { ++ if( z[n+1]==0x9a && z[n+2]==0x80 ){ ++ n += 3; ++ break; ++ } ++ goto whitespace_done; ++ } ++ case 0xe2: { ++ if( z[n+1]==0x80 ){ ++ u8 c = z[n+2]; ++ if( c<0x80 ) goto whitespace_done; ++ if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){ ++ n += 3; ++ break; + } ++ }else if( z[n+1]==0x81 && z[n+2]==0x9f ){ ++ n += 3; ++ break; + } +- zOut[j] = 0; +- sqlite3_result_text(pCtx, zOut, j, sqlite3_free); ++ goto whitespace_done; ++ } ++ case 0xe3: { ++ if( z[n+1]==0x80 && z[n+2]==0x80 ){ ++ n += 3; ++ break; ++ } ++ goto whitespace_done; ++ } ++ case 0xef: { ++ if( z[n+1]==0xbb && z[n+2]==0xbf ){ ++ n += 3; ++ break; ++ } ++ goto whitespace_done; ++ } ++ default: { ++ goto whitespace_done; + } +- break; +- } +- case JSON_ARRAY: +- case JSON_OBJECT: { +- jsonReturnJson(pNode, pCtx, aReplace); +- break; + } + } ++ whitespace_done: ++ return n; + } + +-/* Forward reference */ +-static int jsonParseAddNode(JsonParse*,u32,u32,const char*); ++/* ++** Extra floating-point literals to allow in JSON. ++*/ ++static const struct NanInfName { ++ char c1; ++ char c2; ++ char n; ++ char eType; ++ char nRepl; ++ char *zMatch; ++ char *zRepl; ++} aNanInfName[] = { ++ { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" }, ++ { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" }, ++ { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" }, ++ { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" }, ++ { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" }, ++}; ++ + + /* +-** A macro to hint to the compiler that a function should not be +-** inlined. ++** Report the wrong number of arguments for json_insert(), json_replace() ++** or json_set(). + */ +-#if defined(__GNUC__) +-# define JSON_NOINLINE __attribute__((noinline)) +-#elif defined(_MSC_VER) && _MSC_VER>=1310 +-# define JSON_NOINLINE __declspec(noinline) +-#else +-# define JSON_NOINLINE +-#endif ++static void jsonWrongNumArgs( ++ sqlite3_context *pCtx, ++ const char *zFuncName ++){ ++ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", ++ zFuncName); ++ sqlite3_result_error(pCtx, zMsg, -1); ++ sqlite3_free(zMsg); ++} ++ ++/**************************************************************************** ++** Utility routines for dealing with the binary BLOB representation of JSON ++****************************************************************************/ ++ ++/* ++** Expand pParse->aBlob so that it holds at least N bytes. ++** ++** Return the number of errors. ++*/ ++static int jsonBlobExpand(JsonParse *pParse, u32 N){ ++ u8 *aNew; ++ u64 t; ++ assert( N>pParse->nBlobAlloc ); ++ if( pParse->nBlobAlloc==0 ){ ++ t = 100; ++ }else{ ++ t = pParse->nBlobAlloc*2; ++ } ++ if( tdb, pParse->aBlob, t); ++ if( aNew==0 ){ pParse->oom = 1; return 1; } ++ assert( t<0x7fffffff ); ++ pParse->aBlob = aNew; ++ pParse->nBlobAlloc = (u32)t; ++ return 0; ++} + ++/* ++** If pParse->aBlob is not previously editable (because it is taken ++** from sqlite3_value_blob(), as indicated by the fact that ++** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable ++** by making a copy into space obtained from malloc. ++** ++** Return true on success. Return false on OOM. ++*/ ++static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ ++ u8 *aOld; ++ u32 nSize; ++ assert( !pParse->bReadOnly ); ++ if( pParse->oom ) return 0; ++ if( pParse->nBlobAlloc>0 ) return 1; ++ aOld = pParse->aBlob; ++ nSize = pParse->nBlob + nExtra; ++ pParse->aBlob = 0; ++ if( jsonBlobExpand(pParse, nSize) ){ ++ return 0; ++ } ++ assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra ); ++ memcpy(pParse->aBlob, aOld, pParse->nBlob); ++ return 1; ++} + +-static JSON_NOINLINE int jsonParseAddNodeExpand( +- JsonParse *pParse, /* Append the node to this object */ +- u32 eType, /* Node type */ +- u32 n, /* Content size or sub-node count */ +- const char *zContent /* Content */ ++/* Expand pParse->aBlob and append one bytes. ++*/ ++static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte( ++ JsonParse *pParse, ++ u8 c + ){ +- u32 nNew; +- JsonNode *pNew; +- assert( pParse->nNode>=pParse->nAlloc ); +- if( pParse->oom ) return -1; +- nNew = pParse->nAlloc*2 + 10; +- pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew); +- if( pNew==0 ){ +- pParse->oom = 1; +- return -1; ++ jsonBlobExpand(pParse, pParse->nBlob+1); ++ if( pParse->oom==0 ){ ++ assert( pParse->nBlob+1<=pParse->nBlobAlloc ); ++ pParse->aBlob[pParse->nBlob++] = c; + } +- pParse->nAlloc = nNew; +- pParse->aNode = pNew; +- assert( pParse->nNodenAlloc ); +- return jsonParseAddNode(pParse, eType, n, zContent); + } + +-/* +-** Create a new JsonNode instance based on the arguments and append that +-** instance to the JsonParse. Return the index in pParse->aNode[] of the +-** new node, or -1 if a memory allocation fails. ++/* Append a single character. ++*/ ++static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ ++ if( pParse->nBlob >= pParse->nBlobAlloc ){ ++ jsonBlobExpandAndAppendOneByte(pParse, c); ++ }else{ ++ pParse->aBlob[pParse->nBlob++] = c; ++ } ++} ++ ++/* Slow version of jsonBlobAppendNode() that first resizes the ++** pParse->aBlob structure. ++*/ ++static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*); ++static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode( ++ JsonParse *pParse, ++ u8 eType, ++ u32 szPayload, ++ const void *aPayload ++){ ++ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return; ++ jsonBlobAppendNode(pParse, eType, szPayload, aPayload); ++} ++ ++ ++/* Append a node type byte together with the payload size and ++** possibly also the payload. ++** ++** If aPayload is not NULL, then it is a pointer to the payload which ++** is also appended. If aPayload is NULL, the pParse->aBlob[] array ++** is resized (if necessary) so that it is big enough to hold the ++** payload, but the payload is not appended and pParse->nBlob is left ++** pointing to where the first byte of payload will eventually be. + */ +-static int jsonParseAddNode( +- JsonParse *pParse, /* Append the node to this object */ +- u32 eType, /* Node type */ +- u32 n, /* Content size or sub-node count */ +- const char *zContent /* Content */ ++static void jsonBlobAppendNode( ++ JsonParse *pParse, /* The JsonParse object under construction */ ++ u8 eType, /* Node type. One of JSONB_* */ ++ u32 szPayload, /* Number of bytes of payload */ ++ const void *aPayload /* The payload. Might be NULL */ + ){ +- JsonNode *p; +- if( pParse->nNode>=pParse->nAlloc ){ +- return jsonParseAddNodeExpand(pParse, eType, n, zContent); ++ u8 *a; ++ if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){ ++ jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload); ++ return; + } +- p = &pParse->aNode[pParse->nNode]; +- p->eType = (u8)eType; +- p->jnFlags = 0; +- p->n = n; +- p->u.zJContent = zContent; +- return pParse->nNode++; ++ assert( pParse->aBlob!=0 ); ++ a = &pParse->aBlob[pParse->nBlob]; ++ if( szPayload<=11 ){ ++ a[0] = eType | (szPayload<<4); ++ pParse->nBlob += 1; ++ }else if( szPayload<=0xff ){ ++ a[0] = eType | 0xc0; ++ a[1] = szPayload & 0xff; ++ pParse->nBlob += 2; ++ }else if( szPayload<=0xffff ){ ++ a[0] = eType | 0xd0; ++ a[1] = (szPayload >> 8) & 0xff; ++ a[2] = szPayload & 0xff; ++ pParse->nBlob += 3; ++ }else{ ++ a[0] = eType | 0xe0; ++ a[1] = (szPayload >> 24) & 0xff; ++ a[2] = (szPayload >> 16) & 0xff; ++ a[3] = (szPayload >> 8) & 0xff; ++ a[4] = szPayload & 0xff; ++ pParse->nBlob += 5; ++ } ++ if( aPayload ){ ++ pParse->nBlob += szPayload; ++ memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload); ++ } ++} ++ ++/* Change the payload size for the node at index i to be szPayload. ++*/ ++static int jsonBlobChangePayloadSize( ++ JsonParse *pParse, ++ u32 i, ++ u32 szPayload ++){ ++ u8 *a; ++ u8 szType; ++ u8 nExtra; ++ u8 nNeeded; ++ int delta; ++ if( pParse->oom ) return 0; ++ a = &pParse->aBlob[i]; ++ szType = a[0]>>4; ++ if( szType<=11 ){ ++ nExtra = 0; ++ }else if( szType==12 ){ ++ nExtra = 1; ++ }else if( szType==13 ){ ++ nExtra = 2; ++ }else if( szType==14 ){ ++ nExtra = 4; ++ }else{ ++ nExtra = 8; ++ } ++ if( szPayload<=11 ){ ++ nNeeded = 0; ++ }else if( szPayload<=0xff ){ ++ nNeeded = 1; ++ }else if( szPayload<=0xffff ){ ++ nNeeded = 2; ++ }else{ ++ nNeeded = 4; ++ } ++ delta = nNeeded - nExtra; ++ if( delta ){ ++ u32 newSize = pParse->nBlob + delta; ++ if( delta>0 ){ ++ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){ ++ return 0; /* OOM error. Error state recorded in pParse->oom. */ ++ } ++ a = &pParse->aBlob[i]; ++ memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1)); ++ }else{ ++ memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta)); ++ } ++ pParse->nBlob = newSize; ++ } ++ if( nNeeded==0 ){ ++ a[0] = (a[0] & 0x0f) | (szPayload<<4); ++ }else if( nNeeded==1 ){ ++ a[0] = (a[0] & 0x0f) | 0xc0; ++ a[1] = szPayload & 0xff; ++ }else if( nNeeded==2 ){ ++ a[0] = (a[0] & 0x0f) | 0xd0; ++ a[1] = (szPayload >> 8) & 0xff; ++ a[2] = szPayload & 0xff; ++ }else{ ++ a[0] = (a[0] & 0x0f) | 0xe0; ++ a[1] = (szPayload >> 24) & 0xff; ++ a[2] = (szPayload >> 16) & 0xff; ++ a[3] = (szPayload >> 8) & 0xff; ++ a[4] = szPayload & 0xff; ++ } ++ return delta; + } + + /* +-** Return true if z[] begins with 4 (or more) hexadecimal digits ++** If z[0] is 'u' and is followed by exactly 4 hexadecimal character, ++** then set *pOp to JSONB_TEXTJ and return true. If not, do not make ++** any changes to *pOp and return false. + */ +-static int jsonIs4Hex(const char *z){ +- int i; +- for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0; ++static int jsonIs4HexB(const char *z, int *pOp){ ++ if( z[0]!='u' ) return 0; ++ if( !jsonIs4Hex(&z[1]) ) return 0; ++ *pOp = JSONB_TEXTJ; + return 1; + } + + /* +-** Parse a single JSON value which begins at pParse->zJson[i]. Return the +-** index of the first character past the end of the value parsed. ++** Check a single element of the JSONB in pParse for validity. ++** ++** The element to be checked starts at offset i and must end at on the ++** last byte before iEnd. ++** ++** Return 0 if everything is correct. Return the 1-based byte offset of the ++** error if a problem is detected. (In other words, if the error is at offset ++** 0, return 1). ++*/ ++static u32 jsonbValidityCheck( ++ const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */ ++ u32 i, /* Start of element as pParse->aBlob[i] */ ++ u32 iEnd, /* One more than the last byte of the element */ ++ u32 iDepth /* Current nesting depth */ ++){ ++ u32 n, sz, j, k; ++ const u8 *z; ++ u8 x; ++ if( iDepth>JSON_MAX_DEPTH ) return i+1; ++ sz = 0; ++ n = jsonbPayloadSize(pParse, i, &sz); ++ if( NEVER(n==0) ) return i+1; /* Checked by caller */ ++ if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */ ++ z = pParse->aBlob; ++ x = z[i] & 0x0f; ++ switch( x ){ ++ case JSONB_NULL: ++ case JSONB_TRUE: ++ case JSONB_FALSE: { ++ return n+sz==1 ? 0 : i+1; ++ } ++ case JSONB_INT: { ++ if( sz<1 ) return i+1; ++ j = i+n; ++ if( z[j]=='-' ){ ++ j++; ++ if( sz<2 ) return i+1; ++ } ++ k = i+n+sz; ++ while( jk ) return j+1; ++ if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1; ++ j++; ++ } ++ for(; j0 ) return j+1; ++ if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){ ++ return j+1; ++ } ++ seen = 1; ++ continue; ++ } ++ if( z[j]=='e' || z[j]=='E' ){ ++ if( seen==2 ) return j+1; ++ if( j==k-1 ) return j+1; ++ if( z[j+1]=='+' || z[j+1]=='-' ){ ++ j++; ++ if( j==k-1 ) return j+1; ++ } ++ seen = 2; ++ continue; ++ } ++ return j+1; ++ } ++ if( seen==0 ) return i+1; ++ return 0; ++ } ++ case JSONB_TEXT: { ++ j = i+n; ++ k = j+sz; ++ while( j=k ){ ++ return j+1; ++ }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){ ++ j++; ++ }else if( z[j+1]=='u' ){ ++ if( j+5>=k ) return j+1; ++ if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1; ++ j++; ++ }else if( x!=JSONB_TEXT5 ){ ++ return j+1; ++ }else{ ++ u32 c = 0; ++ u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c); ++ if( c==JSON_INVALID_CHAR ) return j+1; ++ j += szC - 1; ++ } ++ } ++ j++; ++ } ++ return 0; ++ } ++ case JSONB_TEXTRAW: { ++ return 0; ++ } ++ case JSONB_ARRAY: { ++ u32 sub; ++ j = i+n; ++ k = j+sz; ++ while( jk ) return j+1; ++ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); ++ if( sub ) return sub; ++ j += n + sz; ++ } ++ assert( j==k ); ++ return 0; ++ } ++ case JSONB_OBJECT: { ++ u32 cnt = 0; ++ u32 sub; ++ j = i+n; ++ k = j+sz; ++ while( jk ) return j+1; ++ if( (cnt & 1)==0 ){ ++ x = z[j] & 0x0f; ++ if( xJSONB_TEXTRAW ) return j+1; ++ } ++ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); ++ if( sub ) return sub; ++ cnt++; ++ j += n + sz; ++ } ++ assert( j==k ); ++ if( (cnt & 1)!=0 ) return j+1; ++ return 0; ++ } ++ default: { ++ return i+1; ++ } ++ } ++} ++ ++/* ++** Translate a single element of JSON text at pParse->zJson[i] into ++** its equivalent binary JSONB representation. Append the translation into ++** pParse->aBlob[] beginning at pParse->nBlob. The size of ++** pParse->aBlob[] is increased as necessary. + ** +-** Return negative for a syntax error. Special cases: return -2 if the +-** first non-whitespace character is '}' and return -3 if the first +-** non-whitespace character is ']'. ++** Return the index of the first character past the end of the element parsed, ++** or one of the following special result codes: ++** ++** 0 End of input ++** -1 Syntax error or OOM ++** -2 '}' seen \ ++** -3 ']' seen \___ For these returns, pParse->iErr is set to ++** -4 ',' seen / the index in zJson[] of the seen character ++** -5 ':' seen / + */ +-static int jsonParseValue(JsonParse *pParse, u32 i){ ++static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){ + char c; + u32 j; +- int iThis; ++ u32 iThis, iStart; + int x; +- JsonNode *pNode; ++ u8 t; + const char *z = pParse->zJson; +- while( safe_isspace(z[i]) ){ i++; } +- if( (c = z[i])=='{' ){ ++json_parse_restart: ++ switch( (u8)z[i] ){ ++ case '{': { + /* Parse object */ +- iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); +- if( iThis<0 ) return -1; ++ iThis = pParse->nBlob; ++ jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0); ++ if( ++pParse->iDepth > JSON_MAX_DEPTH ){ ++ pParse->iErr = i; ++ return -1; ++ } ++ iStart = pParse->nBlob; + for(j=i+1;;j++){ +- while( safe_isspace(z[j]) ){ j++; } +- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; +- x = jsonParseValue(pParse, j); +- if( x<0 ){ +- pParse->iDepth--; +- if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1; +- return -1; ++ u32 iBlob = pParse->nBlob; ++ x = jsonTranslateTextToBlob(pParse, j); ++ if( x<=0 ){ ++ int op; ++ if( x==(-2) ){ ++ j = pParse->iErr; ++ if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1; ++ break; ++ } ++ j += json5Whitespace(&z[j]); ++ op = JSONB_TEXT; ++ if( sqlite3JsonId1(z[j]) ++ || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op)) ++ ){ ++ int k = j+1; ++ while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) ++ || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op)) ++ ){ ++ k++; ++ } ++ assert( iBlob==pParse->nBlob ); ++ jsonBlobAppendNode(pParse, op, k-j, &z[j]); ++ pParse->hasNonstd = 1; ++ x = k; ++ }else{ ++ if( x!=-1 ) pParse->iErr = j; ++ return -1; ++ } + } + if( pParse->oom ) return -1; +- pNode = &pParse->aNode[pParse->nNode-1]; +- if( pNode->eType!=JSON_STRING ) return -1; +- pNode->jnFlags |= JNODE_LABEL; ++ t = pParse->aBlob[iBlob] & 0x0f; ++ if( tJSONB_TEXTRAW ){ ++ pParse->iErr = j; ++ return -1; ++ } + j = x; +- while( safe_isspace(z[j]) ){ j++; } +- if( z[j]!=':' ) return -1; +- j++; +- x = jsonParseValue(pParse, j); +- pParse->iDepth--; +- if( x<0 ) return -1; ++ if( z[j]==':' ){ ++ j++; ++ }else{ ++ if( jsonIsspace(z[j]) ){ ++ /* strspn() is not helpful here */ ++ do{ j++; }while( jsonIsspace(z[j]) ); ++ if( z[j]==':' ){ ++ j++; ++ goto parse_object_value; ++ } ++ } ++ x = jsonTranslateTextToBlob(pParse, j); ++ if( x!=(-5) ){ ++ if( x!=(-1) ) pParse->iErr = j; ++ return -1; ++ } ++ j = pParse->iErr+1; ++ } ++ parse_object_value: ++ x = jsonTranslateTextToBlob(pParse, j); ++ if( x<=0 ){ ++ if( x!=(-1) ) pParse->iErr = j; ++ return -1; ++ } + j = x; +- while( safe_isspace(z[j]) ){ j++; } +- c = z[j]; +- if( c==',' ) continue; +- if( c!='}' ) return -1; +- break; ++ if( z[j]==',' ){ ++ continue; ++ }else if( z[j]=='}' ){ ++ break; ++ }else{ ++ if( jsonIsspace(z[j]) ){ ++ j += 1 + (u32)strspn(&z[j+1], jsonSpaces); ++ if( z[j]==',' ){ ++ continue; ++ }else if( z[j]=='}' ){ ++ break; ++ } ++ } ++ x = jsonTranslateTextToBlob(pParse, j); ++ if( x==(-4) ){ ++ j = pParse->iErr; ++ continue; ++ } ++ if( x==(-2) ){ ++ j = pParse->iErr; ++ break; ++ } ++ } ++ pParse->iErr = j; ++ return -1; + } +- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; ++ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); ++ pParse->iDepth--; + return j+1; +- }else if( c=='[' ){ ++ } ++ case '[': { + /* Parse array */ +- iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); +- if( iThis<0 ) return -1; ++ iThis = pParse->nBlob; ++ assert( i<=(u32)pParse->nJson ); ++ jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); ++ iStart = pParse->nBlob; ++ if( pParse->oom ) return -1; ++ if( ++pParse->iDepth > JSON_MAX_DEPTH ){ ++ pParse->iErr = i; ++ return -1; ++ } + for(j=i+1;;j++){ +- while( safe_isspace(z[j]) ){ j++; } +- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; +- x = jsonParseValue(pParse, j); +- pParse->iDepth--; +- if( x<0 ){ +- if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1; ++ x = jsonTranslateTextToBlob(pParse, j); ++ if( x<=0 ){ ++ if( x==(-3) ){ ++ j = pParse->iErr; ++ if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1; ++ break; ++ } ++ if( x!=(-1) ) pParse->iErr = j; + return -1; + } + j = x; +- while( safe_isspace(z[j]) ){ j++; } +- c = z[j]; +- if( c==',' ) continue; +- if( c!=']' ) return -1; +- break; ++ if( z[j]==',' ){ ++ continue; ++ }else if( z[j]==']' ){ ++ break; ++ }else{ ++ if( jsonIsspace(z[j]) ){ ++ j += 1 + (u32)strspn(&z[j+1], jsonSpaces); ++ if( z[j]==',' ){ ++ continue; ++ }else if( z[j]==']' ){ ++ break; ++ } ++ } ++ x = jsonTranslateTextToBlob(pParse, j); ++ if( x==(-4) ){ ++ j = pParse->iErr; ++ continue; ++ } ++ if( x==(-3) ){ ++ j = pParse->iErr; ++ break; ++ } ++ } ++ pParse->iErr = j; ++ return -1; + } +- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; ++ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); ++ pParse->iDepth--; + return j+1; +- }else if( c=='"' ){ ++ } ++ case '\'': { ++ u8 opcode; ++ char cDelim; ++ pParse->hasNonstd = 1; ++ opcode = JSONB_TEXT; ++ goto parse_string; ++ case '"': + /* Parse string */ +- u8 jnFlags = 0; ++ opcode = JSONB_TEXT; ++ parse_string: ++ cDelim = z[i]; + j = i+1; +- for(;;){ +- c = z[j]; +- if( (c & ~0x1f)==0 ){ +- /* Control characters are not allowed in strings */ +- return -1; ++ while( 1 /*exit-by-break*/ ){ ++ if( jsonIsOk[(u8)z[j]] ){ ++ if( !jsonIsOk[(u8)z[j+1]] ){ ++ j += 1; ++ }else if( !jsonIsOk[(u8)z[j+2]] ){ ++ j += 2; ++ }else{ ++ j += 3; ++ continue; ++ } + } +- if( c=='\\' ){ ++ c = z[j]; ++ if( c==cDelim ){ ++ break; ++ }else if( c=='\\' ){ + c = z[++j]; + if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' + || c=='n' || c=='r' || c=='t' +- || (c=='u' && jsonIs4Hex(z+j+1)) ){ +- jnFlags = JNODE_ESCAPE; ++ || (c=='u' && jsonIs4Hex(&z[j+1])) ){ ++ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ; ++ }else if( c=='\'' || c=='v' || c=='\n' ++#ifdef SQLITE_BUG_COMPATIBLE_20250510 ++ || (c=='0') /* Legacy bug compatible */ ++#else ++ || (c=='0' && !sqlite3Isdigit(z[j+1])) /* Correct implementation */ ++#endif ++ || (0xe2==(u8)c && 0x80==(u8)z[j+1] ++ && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) ++ || (c=='x' && jsonIs2Hex(&z[j+1])) ){ ++ opcode = JSONB_TEXT5; ++ pParse->hasNonstd = 1; ++ }else if( c=='\r' ){ ++ if( z[j+1]=='\n' ) j++; ++ opcode = JSONB_TEXT5; ++ pParse->hasNonstd = 1; + }else{ ++ pParse->iErr = j; ++ return -1; ++ } ++ }else if( c<=0x1f ){ ++ if( c==0 ){ ++ pParse->iErr = j; + return -1; + } ++ /* Control characters are not allowed in canonical JSON string ++ ** literals, but are allowed in JSON5 string literals. */ ++ opcode = JSONB_TEXT5; ++ pParse->hasNonstd = 1; + }else if( c=='"' ){ +- break; ++ opcode = JSONB_TEXT5; + } + j++; + } +- jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); +- if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; ++ jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]); + return j+1; +- }else if( c=='n' +- && strncmp(z+i,"null",4)==0 +- && !safe_isalnum(z[i+4]) ){ +- jsonParseAddNode(pParse, JSON_NULL, 0, 0); +- return i+4; +- }else if( c=='t' +- && strncmp(z+i,"true",4)==0 +- && !safe_isalnum(z[i+4]) ){ +- jsonParseAddNode(pParse, JSON_TRUE, 0, 0); +- return i+4; +- }else if( c=='f' +- && strncmp(z+i,"false",5)==0 +- && !safe_isalnum(z[i+5]) ){ +- jsonParseAddNode(pParse, JSON_FALSE, 0, 0); +- return i+5; +- }else if( c=='-' || (c>='0' && c<='9') ){ ++ } ++ case 't': { ++ if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ ++ jsonBlobAppendOneByte(pParse, JSONB_TRUE); ++ return i+4; ++ } ++ pParse->iErr = i; ++ return -1; ++ } ++ case 'f': { ++ if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ ++ jsonBlobAppendOneByte(pParse, JSONB_FALSE); ++ return i+5; ++ } ++ pParse->iErr = i; ++ return -1; ++ } ++ case '+': { ++ u8 seenE; ++ pParse->hasNonstd = 1; ++ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ ++ goto parse_number; ++ case '.': ++ if( sqlite3Isdigit(z[i+1]) ){ ++ pParse->hasNonstd = 1; ++ t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ ++ seenE = 0; ++ goto parse_number_2; ++ } ++ pParse->iErr = i; ++ return -1; ++ case '-': ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': + /* Parse number */ +- u8 seenDP = 0; +- u8 seenE = 0; ++ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ ++ parse_number: ++ seenE = 0; + assert( '-' < '0' ); ++ assert( '+' < '0' ); ++ assert( '.' < '0' ); ++ c = z[i]; ++ + if( c<='0' ){ +- j = c=='-' ? i+1 : i; +- if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1; ++ if( c=='0' ){ ++ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ ++ assert( t==0x00 ); ++ pParse->hasNonstd = 1; ++ t = 0x01; ++ for(j=i+3; sqlite3Isxdigit(z[j]); j++){} ++ goto parse_number_finish; ++ }else if( sqlite3Isdigit(z[i+1]) ){ ++ pParse->iErr = i+1; ++ return -1; ++ } ++ }else{ ++ if( !sqlite3Isdigit(z[i+1]) ){ ++ /* JSON5 allows for "+Infinity" and "-Infinity" using exactly ++ ** that case. SQLite also allows these in any case and it allows ++ ** "+inf" and "-inf". */ ++ if( (z[i+1]=='I' || z[i+1]=='i') ++ && sqlite3StrNICmp(&z[i+1], "inf",3)==0 ++ ){ ++ pParse->hasNonstd = 1; ++ if( z[i]=='-' ){ ++ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); ++ }else{ ++ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); ++ } ++ return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); ++ } ++ if( z[i+1]=='.' ){ ++ pParse->hasNonstd = 1; ++ t |= 0x01; ++ goto parse_number_2; ++ } ++ pParse->iErr = i; ++ return -1; ++ } ++ if( z[i+1]=='0' ){ ++ if( sqlite3Isdigit(z[i+2]) ){ ++ pParse->iErr = i+1; ++ return -1; ++ }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ ++ pParse->hasNonstd = 1; ++ t |= 0x01; ++ for(j=i+4; sqlite3Isxdigit(z[j]); j++){} ++ goto parse_number_finish; ++ } ++ } ++ } + } +- j = i+1; +- for(;; j++){ ++ ++ parse_number_2: ++ for(j=i+1;; j++){ + c = z[j]; +- if( c>='0' && c<='9' ) continue; ++ if( sqlite3Isdigit(c) ) continue; + if( c=='.' ){ +- if( z[j-1]=='-' ) return -1; +- if( seenDP ) return -1; +- seenDP = 1; ++ if( (t & 0x02)!=0 ){ ++ pParse->iErr = j; ++ return -1; ++ } ++ t |= 0x02; + continue; + } + if( c=='e' || c=='E' ){ +- if( z[j-1]<'0' ) return -1; +- if( seenE ) return -1; +- seenDP = seenE = 1; ++ if( z[j-1]<'0' ){ ++ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ ++ pParse->hasNonstd = 1; ++ t |= 0x01; ++ }else{ ++ pParse->iErr = j; ++ return -1; ++ } ++ } ++ if( seenE ){ ++ pParse->iErr = j; ++ return -1; ++ } ++ t |= 0x02; ++ seenE = 1; + c = z[j+1]; + if( c=='+' || c=='-' ){ + j++; + c = z[j+1]; + } +- if( c<'0' || c>'9' ) return -1; ++ if( c<'0' || c>'9' ){ ++ pParse->iErr = j; ++ return -1; ++ } + continue; + } + break; + } +- if( z[j-1]<'0' ) return -1; +- jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, +- j - i, &z[i]); ++ if( z[j-1]<'0' ){ ++ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ ++ pParse->hasNonstd = 1; ++ t |= 0x01; ++ }else{ ++ pParse->iErr = j; ++ return -1; ++ } ++ } ++ parse_number_finish: ++ assert( JSONB_INT+0x01==JSONB_INT5 ); ++ assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 ); ++ assert( JSONB_INT+0x02==JSONB_FLOAT ); ++ if( z[i]=='+' ) i++; ++ jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]); + return j; +- }else if( c=='}' ){ ++ } ++ case '}': { ++ pParse->iErr = i; + return -2; /* End of {...} */ +- }else if( c==']' ){ ++ } ++ case ']': { ++ pParse->iErr = i; + return -3; /* End of [...] */ +- }else if( c==0 ){ ++ } ++ case ',': { ++ pParse->iErr = i; ++ return -4; /* List separator */ ++ } ++ case ':': { ++ pParse->iErr = i; ++ return -5; /* Object label/value separator */ ++ } ++ case 0: { + return 0; /* End of file */ +- }else{ ++ } ++ case 0x09: ++ case 0x0a: ++ case 0x0d: ++ case 0x20: { ++ i += 1 + (u32)strspn(&z[i+1], jsonSpaces); ++ goto json_parse_restart; ++ } ++ case 0x0b: ++ case 0x0c: ++ case '/': ++ case 0xc2: ++ case 0xe1: ++ case 0xe2: ++ case 0xe3: ++ case 0xef: { ++ j = json5Whitespace(&z[i]); ++ if( j>0 ){ ++ i += j; ++ pParse->hasNonstd = 1; ++ goto json_parse_restart; ++ } ++ pParse->iErr = i; ++ return -1; ++ } ++ case 'n': { ++ if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ ++ jsonBlobAppendOneByte(pParse, JSONB_NULL); ++ return i+4; ++ } ++ /* fall-through into the default case that checks for NaN */ ++ /* no break */ deliberate_fall_through ++ } ++ default: { ++ u32 k; ++ int nn; ++ c = z[i]; ++ for(k=0; khasNonstd = 1; ++ return i + nn; ++ } ++ pParse->iErr = i; + return -1; /* Syntax error */ + } ++ } /* End switch(z[i]) */ + } + ++ + /* + ** Parse a complete JSON string. Return 0 on success or non-zero if there +-** are any errors. If an error occurs, free all memory associated with +-** pParse. ++** are any errors. If an error occurs, free all memory held by pParse, ++** but not pParse itself. + ** +-** pParse is uninitialized when this routine is called. ++** pParse must be initialized to an empty parse object prior to calling ++** this routine. + */ +-static int jsonParse( ++static int jsonConvertTextToBlob( + JsonParse *pParse, /* Initialize and fill this JsonParse object */ +- sqlite3_context *pCtx, /* Report errors here */ +- const char *zJson /* Input JSON text to be parsed */ ++ sqlite3_context *pCtx /* Report errors here */ + ){ + int i; +- memset(pParse, 0, sizeof(*pParse)); +- if( zJson==0 ) return 1; +- pParse->zJson = zJson; +- i = jsonParseValue(pParse, 0); ++ const char *zJson = pParse->zJson; ++ i = jsonTranslateTextToBlob(pParse, 0); + if( pParse->oom ) i = -1; + if( i>0 ){ ++#ifdef SQLITE_DEBUG + assert( pParse->iDepth==0 ); +- while( safe_isspace(zJson[i]) ) i++; +- if( zJson[i] ) i = -1; ++ if( sqlite3Config.bJsonSelfcheck ){ ++ assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 ); ++ } ++#endif ++ while( jsonIsspace(zJson[i]) ) i++; ++ if( zJson[i] ){ ++ i += json5Whitespace(&zJson[i]); ++ if( zJson[i] ){ ++ if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1); ++ jsonParseReset(pParse); ++ return 1; ++ } ++ pParse->hasNonstd = 1; ++ } + } + if( i<=0 ){ + if( pCtx!=0 ){ +@@ -186402,453 +209923,1773 @@ static int jsonParse( + return 0; + } + +-/* Mark node i of pParse as being a child of iParent. Call recursively +-** to fill in all the descendants of node i. ++/* ++** The input string pStr is a well-formed JSON text string. Convert ++** this into the JSONB format and make it the return value of the ++** SQL function. + */ +-static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ +- JsonNode *pNode = &pParse->aNode[i]; +- u32 j; +- pParse->aUp[i] = iParent; +- switch( pNode->eType ){ +- case JSON_ARRAY: { +- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){ +- jsonParseFillInParentage(pParse, i+j, i); ++static void jsonReturnStringAsBlob(JsonString *pStr){ ++ JsonParse px; ++ memset(&px, 0, sizeof(px)); ++ jsonStringTerminate(pStr); ++ if( pStr->eErr ){ ++ sqlite3_result_error_nomem(pStr->pCtx); ++ return; ++ } ++ px.zJson = pStr->zBuf; ++ px.nJson = pStr->nUsed; ++ px.db = sqlite3_context_db_handle(pStr->pCtx); ++ (void)jsonTranslateTextToBlob(&px, 0); ++ if( px.oom ){ ++ sqlite3DbFree(px.db, px.aBlob); ++ sqlite3_result_error_nomem(pStr->pCtx); ++ }else{ ++ assert( px.nBlobAlloc>0 ); ++ assert( !px.bReadOnly ); ++ sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC); ++ } ++} ++ ++/* The byte at index i is a node type-code. This routine ++** determines the payload size for that node and writes that ++** payload size in to *pSz. It returns the offset from i to the ++** beginning of the payload. Return 0 on error. ++*/ ++static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ ++ u8 x; ++ u32 sz; ++ u32 n; ++ assert( i<=pParse->nBlob ); ++ x = pParse->aBlob[i]>>4; ++ if( x<=11 ){ ++ sz = x; ++ n = 1; ++ }else if( x==12 ){ ++ if( i+1>=pParse->nBlob ){ ++ *pSz = 0; ++ return 0; ++ } ++ sz = pParse->aBlob[i+1]; ++ n = 2; ++ }else if( x==13 ){ ++ if( i+2>=pParse->nBlob ){ ++ *pSz = 0; ++ return 0; ++ } ++ sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; ++ n = 3; ++ }else if( x==14 ){ ++ if( i+4>=pParse->nBlob ){ ++ *pSz = 0; ++ return 0; ++ } ++ sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + ++ (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; ++ n = 5; ++ }else{ ++ if( i+8>=pParse->nBlob ++ || pParse->aBlob[i+1]!=0 ++ || pParse->aBlob[i+2]!=0 ++ || pParse->aBlob[i+3]!=0 ++ || pParse->aBlob[i+4]!=0 ++ ){ ++ *pSz = 0; ++ return 0; ++ } ++ sz = ((u32)pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) + ++ (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; ++ n = 9; ++ } ++ if( (i64)i+sz+n > pParse->nBlob ++ && (i64)i+sz+n > pParse->nBlob-pParse->delta ++ ){ ++ *pSz = 0; ++ return 0; ++ } ++ *pSz = sz; ++ return n; ++} ++ ++ ++/* ++** Translate the binary JSONB representation of JSON beginning at ++** pParse->aBlob[i] into a JSON text string. Append the JSON ++** text onto the end of pOut. Return the index in pParse->aBlob[] ++** of the first byte past the end of the element that is translated. ++** ++** If an error is detected in the BLOB input, the pOut->eErr flag ++** might get set to JSTRING_MALFORMED. But not all BLOB input errors ++** are detected. So a malformed JSONB input might either result ++** in an error, or in incorrect JSON. ++** ++** The pOut->eErr JSTRING_OOM flag is set on a OOM. ++*/ ++static u32 jsonTranslateBlobToText( ++ const JsonParse *pParse, /* the complete parse of the JSON */ ++ u32 i, /* Start rendering at this index */ ++ JsonString *pOut /* Write JSON here */ ++){ ++ u32 sz, n, j, iEnd; ++ ++ n = jsonbPayloadSize(pParse, i, &sz); ++ if( n==0 ){ ++ pOut->eErr |= JSTRING_MALFORMED; ++ return pParse->nBlob+1; ++ } ++ switch( pParse->aBlob[i] & 0x0f ){ ++ case JSONB_NULL: { ++ jsonAppendRawNZ(pOut, "null", 4); ++ return i+1; ++ } ++ case JSONB_TRUE: { ++ jsonAppendRawNZ(pOut, "true", 4); ++ return i+1; ++ } ++ case JSONB_FALSE: { ++ jsonAppendRawNZ(pOut, "false", 5); ++ return i+1; ++ } ++ case JSONB_INT: ++ case JSONB_FLOAT: { ++ if( sz==0 ) goto malformed_jsonb; ++ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); ++ break; ++ } ++ case JSONB_INT5: { /* Integer literal in hexadecimal notation */ ++ u32 k = 2; ++ sqlite3_uint64 u = 0; ++ const char *zIn = (const char*)&pParse->aBlob[i+n]; ++ int bOverflow = 0; ++ if( sz==0 ) goto malformed_jsonb; ++ if( zIn[0]=='-' ){ ++ jsonAppendChar(pOut, '-'); ++ k++; ++ }else if( zIn[0]=='+' ){ ++ k++; ++ } ++ for(; keErr |= JSTRING_MALFORMED; ++ break; ++ }else if( (u>>60)!=0 ){ ++ bOverflow = 1; ++ }else{ ++ u = u*16 + sqlite3HexToInt(zIn[k]); ++ } ++ } ++ jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u); ++ break; ++ } ++ case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ ++ u32 k = 0; ++ const char *zIn = (const char*)&pParse->aBlob[i+n]; ++ if( sz==0 ) goto malformed_jsonb; ++ if( zIn[0]=='-' ){ ++ jsonAppendChar(pOut, '-'); ++ k++; ++ } ++ if( zIn[k]=='.' ){ ++ jsonAppendChar(pOut, '0'); ++ } ++ for(; knUsed+sz+2<=pOut->nAlloc || jsonStringGrow(pOut, sz+2)==0 ){ ++ pOut->zBuf[pOut->nUsed] = '"'; ++ memcpy(pOut->zBuf+pOut->nUsed+1,(const char*)&pParse->aBlob[i+n],sz); ++ pOut->zBuf[pOut->nUsed+sz+1] = '"'; ++ pOut->nUsed += sz+2; ++ } ++ break; ++ } ++ case JSONB_TEXT5: { ++ const char *zIn; ++ u32 k; ++ u32 sz2 = sz; ++ zIn = (const char*)&pParse->aBlob[i+n]; ++ jsonAppendChar(pOut, '"'); ++ while( sz2>0 ){ ++ for(k=0; k0 ){ ++ jsonAppendRawNZ(pOut, zIn, k); ++ if( k>=sz2 ){ ++ break; ++ } ++ zIn += k; ++ sz2 -= k; ++ } ++ if( zIn[0]=='"' ){ ++ jsonAppendRawNZ(pOut, "\\\"", 2); ++ zIn++; ++ sz2--; ++ continue; ++ } ++ if( zIn[0]<=0x1f ){ ++ if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break; ++ jsonAppendControlChar(pOut, zIn[0]); ++ zIn++; ++ sz2--; ++ continue; ++ } ++ assert( zIn[0]=='\\' ); ++ assert( sz2>=1 ); ++ if( sz2<2 ){ ++ pOut->eErr |= JSTRING_MALFORMED; ++ break; ++ } ++ switch( (u8)zIn[1] ){ ++ case '\'': ++ jsonAppendChar(pOut, '\''); ++ break; ++ case 'v': ++ jsonAppendRawNZ(pOut, "\\u0009", 6); ++ break; ++ case 'x': ++ if( sz2<4 ){ ++ pOut->eErr |= JSTRING_MALFORMED; ++ sz2 = 2; ++ break; ++ } ++ jsonAppendRawNZ(pOut, "\\u00", 4); ++ jsonAppendRawNZ(pOut, &zIn[2], 2); ++ zIn += 2; ++ sz2 -= 2; ++ break; ++ case '0': ++ jsonAppendRawNZ(pOut, "\\u0000", 6); ++ break; ++ case '\r': ++ if( sz2>2 && zIn[2]=='\n' ){ ++ zIn++; ++ sz2--; ++ } ++ break; ++ case '\n': ++ break; ++ case 0xe2: ++ /* '\' followed by either U+2028 or U+2029 is ignored as ++ ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29. ++ ** U+2029 is the same except for the last byte */ ++ if( sz2<4 ++ || 0x80!=(u8)zIn[2] ++ || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) ++ ){ ++ pOut->eErr |= JSTRING_MALFORMED; ++ sz2 = 2; ++ break; ++ } ++ zIn += 2; ++ sz2 -= 2; ++ break; ++ default: ++ jsonAppendRawNZ(pOut, zIn, 2); ++ break; ++ } ++ assert( sz2>=2 ); ++ zIn += 2; ++ sz2 -= 2; ++ } ++ jsonAppendChar(pOut, '"'); ++ break; ++ } ++ case JSONB_TEXTRAW: { ++ jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz); ++ break; ++ } ++ case JSONB_ARRAY: { ++ jsonAppendChar(pOut, '['); ++ j = i+n; ++ iEnd = j+sz; ++ while( jeErr==0 ){ ++ j = jsonTranslateBlobToText(pParse, j, pOut); ++ jsonAppendChar(pOut, ','); ++ } ++ if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; ++ if( sz>0 ) jsonStringTrimOneChar(pOut); ++ jsonAppendChar(pOut, ']'); ++ break; ++ } ++ case JSONB_OBJECT: { ++ int x = 0; ++ jsonAppendChar(pOut, '{'); ++ j = i+n; ++ iEnd = j+sz; ++ while( jeErr==0 ){ ++ j = jsonTranslateBlobToText(pParse, j, pOut); ++ jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); ++ } ++ if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; ++ if( sz>0 ) jsonStringTrimOneChar(pOut); ++ jsonAppendChar(pOut, '}'); ++ break; ++ } ++ ++ default: { ++ malformed_jsonb: ++ pOut->eErr |= JSTRING_MALFORMED; ++ break; ++ } ++ } ++ return i+n+sz; ++} ++ ++/* Context for recursion of json_pretty() ++*/ ++typedef struct JsonPretty JsonPretty; ++struct JsonPretty { ++ JsonParse *pParse; /* The BLOB being rendered */ ++ JsonString *pOut; /* Generate pretty output into this string */ ++ const char *zIndent; /* Use this text for indentation */ ++ u32 szIndent; /* Bytes in zIndent[] */ ++ u32 nIndent; /* Current level of indentation */ ++}; ++ ++/* Append indentation to the pretty JSON under construction */ ++static void jsonPrettyIndent(JsonPretty *pPretty){ ++ u32 jj; ++ for(jj=0; jjnIndent; jj++){ ++ jsonAppendRaw(pPretty->pOut, pPretty->zIndent, pPretty->szIndent); ++ } ++} ++ ++/* ++** Translate the binary JSONB representation of JSON beginning at ++** pParse->aBlob[i] into a JSON text string. Append the JSON ++** text onto the end of pOut. Return the index in pParse->aBlob[] ++** of the first byte past the end of the element that is translated. ++** ++** This is a variant of jsonTranslateBlobToText() that "pretty-prints" ++** the output. Extra whitespace is inserted to make the JSON easier ++** for humans to read. ++** ++** If an error is detected in the BLOB input, the pOut->eErr flag ++** might get set to JSTRING_MALFORMED. But not all BLOB input errors ++** are detected. So a malformed JSONB input might either result ++** in an error, or in incorrect JSON. ++** ++** The pOut->eErr JSTRING_OOM flag is set on a OOM. ++*/ ++static u32 jsonTranslateBlobToPrettyText( ++ JsonPretty *pPretty, /* Pretty-printing context */ ++ u32 i /* Start rendering at this index */ ++){ ++ u32 sz, n, j, iEnd; ++ const JsonParse *pParse = pPretty->pParse; ++ JsonString *pOut = pPretty->pOut; ++ n = jsonbPayloadSize(pParse, i, &sz); ++ if( n==0 ){ ++ pOut->eErr |= JSTRING_MALFORMED; ++ return pParse->nBlob+1; ++ } ++ switch( pParse->aBlob[i] & 0x0f ){ ++ case JSONB_ARRAY: { ++ j = i+n; ++ iEnd = j+sz; ++ jsonAppendChar(pOut, '['); ++ if( jnIndent++; ++ while( pOut->eErr==0 ){ ++ jsonPrettyIndent(pPretty); ++ j = jsonTranslateBlobToPrettyText(pPretty, j); ++ if( j>=iEnd ) break; ++ jsonAppendRawNZ(pOut, ",\n", 2); ++ } ++ jsonAppendChar(pOut, '\n'); ++ pPretty->nIndent--; ++ jsonPrettyIndent(pPretty); + } ++ jsonAppendChar(pOut, ']'); ++ i = iEnd; + break; + } +- case JSON_OBJECT: { +- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){ +- pParse->aUp[i+j] = i; +- jsonParseFillInParentage(pParse, i+j+1, i); ++ case JSONB_OBJECT: { ++ j = i+n; ++ iEnd = j+sz; ++ jsonAppendChar(pOut, '{'); ++ if( jnIndent++; ++ while( pOut->eErr==0 ){ ++ jsonPrettyIndent(pPretty); ++ j = jsonTranslateBlobToText(pParse, j, pOut); ++ if( j>iEnd ){ ++ pOut->eErr |= JSTRING_MALFORMED; ++ break; ++ } ++ jsonAppendRawNZ(pOut, ": ", 2); ++ j = jsonTranslateBlobToPrettyText(pPretty, j); ++ if( j>=iEnd ) break; ++ jsonAppendRawNZ(pOut, ",\n", 2); ++ } ++ jsonAppendChar(pOut, '\n'); ++ pPretty->nIndent--; ++ jsonPrettyIndent(pPretty); + } ++ jsonAppendChar(pOut, '}'); ++ i = iEnd; + break; + } + default: { ++ i = jsonTranslateBlobToText(pParse, i, pOut); + break; + } + } ++ return i; + } + + /* +-** Compute the parentage of all nodes in a completed parse. ++** Given that a JSONB_ARRAY object starts at offset i, return ++** the number of entries in that array. + */ +-static int jsonParseFindParents(JsonParse *pParse){ +- u32 *aUp; +- assert( pParse->aUp==0 ); +- aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); +- if( aUp==0 ){ +- pParse->oom = 1; +- return SQLITE_NOMEM; ++static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ ++ u32 n, sz, i, iEnd; ++ u32 k = 0; ++ n = jsonbPayloadSize(pParse, iRoot, &sz); ++ iEnd = iRoot+n+sz; ++ for(i=iRoot+n; n>0 && idelta. + */ +-#define JSON_CACHE_ID (-429938) /* First cache entry */ +-#define JSON_CACHE_SZ 4 /* Max number of cache entries */ ++static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ ++ u32 sz = 0; ++ u32 nBlob; ++ assert( pParse->delta!=0 ); ++ assert( pParse->nBlobAlloc >= pParse->nBlob ); ++ nBlob = pParse->nBlob; ++ pParse->nBlob = pParse->nBlobAlloc; ++ (void)jsonbPayloadSize(pParse, iRoot, &sz); ++ pParse->nBlob = nBlob; ++ sz += pParse->delta; ++ pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz); ++} + + /* +-** Obtain a complete parse of the JSON found in the first argument +-** of the argv array. Use the sqlite3_get_auxdata() cache for this +-** parse if it is available. If the cache is not available or if it +-** is no longer valid, parse the JSON again and return the new parse, +-** and also register the new parse so that it will be available for +-** future sqlite3_get_auxdata() calls. ++** If the JSONB at aIns[0..nIns-1] can be expanded (by denormalizing the ++** size field) by d bytes, then write the expansion into aOut[] and ++** return true. In this way, an overwrite happens without changing the ++** size of the JSONB, which reduces memcpy() operations and also make it ++** faster and easier to update the B-Tree entry that contains the JSONB ++** in the database. ++** ++** If the expansion of aIns[] by d bytes cannot be (easily) accomplished ++** then return false. ++** ++** The d parameter is guaranteed to be between 1 and 8. ++** ++** This routine is an optimization. A correct answer is obtained if it ++** always leaves the output unchanged and returns false. + */ +-static JsonParse *jsonParseCached( +- sqlite3_context *pCtx, +- sqlite3_value **argv, +- sqlite3_context *pErrCtx ++static int jsonBlobOverwrite( ++ u8 *aOut, /* Overwrite here */ ++ const u8 *aIns, /* New content */ ++ u32 nIns, /* Bytes of new content */ ++ u32 d /* Need to expand new content by this much */ + ){ +- const char *zJson = (const char*)sqlite3_value_text(argv[0]); +- int nJson = sqlite3_value_bytes(argv[0]); +- JsonParse *p; +- JsonParse *pMatch = 0; +- int iKey; +- int iMinKey = 0; +- u32 iMinHold = 0xffffffff; +- u32 iMaxHold = 0; +- if( zJson==0 ) return 0; +- for(iKey=0; iKey>4 ){ ++ default: { /* aIns[] header size 1 */ ++ if( ((1<nJson==nJson +- && memcmp(p->zJson,zJson,nJson)==0 +- ){ +- p->nErr = 0; +- pMatch = p; +- }else if( p->iHoldiHold; +- iMinKey = iKey; ++ case 12: { /* aIns[] header size is 2 */ ++ if( ((1<iHold>iMaxHold ){ +- iMaxHold = p->iHold; ++ case 15: { /* aIns[] header size is 9 */ ++ return 0; /* No solution */ + } + } +- if( pMatch ){ +- pMatch->nErr = 0; +- pMatch->iHold = iMaxHold+1; +- return pMatch; ++ assert( i>=2 && i<=9 && aType[i-2]!=0 ); ++ aOut[0] = (aIns[0] & 0x0f) | aType[i-2]; ++ memcpy(&aOut[i], &aIns[szHdr], nIns-szHdr); ++ szPayload = nIns - szHdr; ++ while( 1/*edit-by-break*/ ){ ++ i--; ++ aOut[i] = szPayload & 0xff; ++ if( i==1 ) break; ++ szPayload >>= 8; + } +- p = sqlite3_malloc64( sizeof(*p) + nJson + 1 ); +- if( p==0 ){ +- sqlite3_result_error_nomem(pCtx); +- return 0; ++ assert( (szPayload>>8)==0 ); ++ return 1; ++} ++ ++/* ++** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of ++** content beginning at iDel, and replacing them with nIns bytes of ++** content given by aIns. ++** ++** nDel may be zero, in which case no bytes are removed. But iDel is ++** still important as new bytes will be insert beginning at iDel. ++** ++** aIns may be zero, in which case space is created to hold nIns bytes ++** beginning at iDel, but that space is uninitialized. ++** ++** Set pParse->oom if an OOM occurs. ++*/ ++static void jsonBlobEdit( ++ JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */ ++ u32 iDel, /* First byte to be removed */ ++ u32 nDel, /* Number of bytes to remove */ ++ const u8 *aIns, /* Content to insert */ ++ u32 nIns /* Bytes of content to insert */ ++){ ++ i64 d = (i64)nIns - (i64)nDel; ++ if( d<0 && d>=(-8) && aIns!=0 ++ && jsonBlobOverwrite(&pParse->aBlob[iDel], aIns, nIns, (int)-d) ++ ){ ++ return; + } +- memset(p, 0, sizeof(*p)); +- p->zJson = (char*)&p[1]; +- memcpy((char*)p->zJson, zJson, nJson+1); +- if( jsonParse(p, pErrCtx, p->zJson) ){ +- sqlite3_free(p); +- return 0; ++ if( d!=0 ){ ++ if( pParse->nBlob + d > pParse->nBlobAlloc ){ ++ jsonBlobExpand(pParse, pParse->nBlob+d); ++ if( pParse->oom ) return; ++ } ++ memmove(&pParse->aBlob[iDel+nIns], ++ &pParse->aBlob[iDel+nDel], ++ pParse->nBlob - (iDel+nDel)); ++ pParse->nBlob += d; ++ pParse->delta += d; ++ } ++ if( nIns && aIns ){ ++ memcpy(&pParse->aBlob[iDel], aIns, nIns); ++ } ++} ++ ++/* ++** Return the number of escaped newlines to be ignored. ++** An escaped newline is a one of the following byte sequences: ++** ++** 0x5c 0x0a ++** 0x5c 0x0d ++** 0x5c 0x0d 0x0a ++** 0x5c 0xe2 0x80 0xa8 ++** 0x5c 0xe2 0x80 0xa9 ++*/ ++static u32 jsonBytesToBypass(const char *z, u32 n){ ++ u32 i = 0; ++ while( i+10 ); ++ assert( z[0]=='\\' ); ++ if( n<2 ){ ++ *piOut = JSON_INVALID_CHAR; ++ return n; ++ } ++ switch( (u8)z[1] ){ ++ case 'u': { ++ u32 v, vlo; ++ if( n<6 ){ ++ *piOut = JSON_INVALID_CHAR; ++ return n; ++ } ++ v = jsonHexToInt4(&z[2]); ++ if( (v & 0xfc00)==0xd800 ++ && n>=12 ++ && z[6]=='\\' ++ && z[7]=='u' ++ && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00 ++ ){ ++ *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000; ++ return 12; ++ }else{ ++ *piOut = v; ++ return 6; ++ } ++ } ++ case 'b': { *piOut = '\b'; return 2; } ++ case 'f': { *piOut = '\f'; return 2; } ++ case 'n': { *piOut = '\n'; return 2; } ++ case 'r': { *piOut = '\r'; return 2; } ++ case 't': { *piOut = '\t'; return 2; } ++ case 'v': { *piOut = '\v'; return 2; } ++ case '0': { ++ /* JSON5 requires that the \0 escape not be followed by a digit. ++ ** But SQLite did not enforce this restriction in versions 3.42.0 ++ ** through 3.49.2. That was a bug. But some applications might have ++ ** come to depend on that bug. Use the SQLITE_BUG_COMPATIBLE_20250510 ++ ** option to restore the old buggy behavior. */ ++#ifdef SQLITE_BUG_COMPATIBLE_20250510 ++ /* Legacy bug-compatible behavior */ ++ *piOut = 0; ++#else ++ /* Correct behavior */ ++ *piOut = (n>2 && sqlite3Isdigit(z[2])) ? JSON_INVALID_CHAR : 0; ++#endif ++ return 2; ++ } ++ case '\'': ++ case '"': ++ case '/': ++ case '\\':{ *piOut = z[1]; return 2; } ++ case 'x': { ++ if( n<4 ){ ++ *piOut = JSON_INVALID_CHAR; ++ return n; ++ } ++ *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]); ++ return 4; ++ } ++ case 0xe2: ++ case '\r': ++ case '\n': { ++ u32 nSkip = jsonBytesToBypass(z, n); ++ if( nSkip==0 ){ ++ *piOut = JSON_INVALID_CHAR; ++ return n; ++ }else if( nSkip==n ){ ++ *piOut = 0; ++ return n; ++ }else if( z[nSkip]=='\\' ){ ++ return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut); ++ }else{ ++ int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut); ++ return nSkip + sz; ++ } ++ } ++ default: { ++ *piOut = JSON_INVALID_CHAR; ++ return 2; ++ } ++ } ++} ++ ++ ++/* ++** Compare two object labels. Return 1 if they are equal and ++** 0 if they differ. ++** ++** In this version, we know that one or the other or both of the ++** two comparands contains an escape sequence. ++*/ ++static SQLITE_NOINLINE int jsonLabelCompareEscaped( ++ const char *zLeft, /* The left label */ ++ u32 nLeft, /* Size of the left label in bytes */ ++ int rawLeft, /* True if zLeft contains no escapes */ ++ const char *zRight, /* The right label */ ++ u32 nRight, /* Size of the right label in bytes */ ++ int rawRight /* True if zRight is escape-free */ ++){ ++ u32 cLeft, cRight; ++ assert( rawLeft==0 || rawRight==0 ); ++ while( 1 /*exit-by-return*/ ){ ++ if( nLeft==0 ){ ++ cLeft = 0; ++ }else if( rawLeft || zLeft[0]!='\\' ){ ++ cLeft = ((u8*)zLeft)[0]; ++ if( cLeft>=0xc0 ){ ++ int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft); ++ zLeft += sz; ++ nLeft -= sz; ++ }else{ ++ zLeft++; ++ nLeft--; ++ } ++ }else{ ++ u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft); ++ zLeft += n; ++ assert( n<=nLeft ); ++ nLeft -= n; ++ } ++ if( nRight==0 ){ ++ cRight = 0; ++ }else if( rawRight || zRight[0]!='\\' ){ ++ cRight = ((u8*)zRight)[0]; ++ if( cRight>=0xc0 ){ ++ int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight); ++ zRight += sz; ++ nRight -= sz; ++ }else{ ++ zRight++; ++ nRight--; ++ } ++ }else{ ++ u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight); ++ zRight += n; ++ assert( n<=nRight ); ++ nRight -= n; ++ } ++ if( cLeft!=cRight ) return 0; ++ if( cLeft==0 ) return 1; + } +- p->nJson = nJson; +- p->iHold = iMaxHold+1; +- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, +- (void(*)(void*))jsonParseFree); +- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); + } + + /* +-** Compare the OBJECT label at pNode against zKey,nKey. Return true on +-** a match. ++** Compare two object labels. Return 1 if they are equal and ++** 0 if they differ. Return -1 if an OOM occurs. + */ +-static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ +- if( pNode->jnFlags & JNODE_RAW ){ +- if( pNode->n!=nKey ) return 0; +- return strncmp(pNode->u.zJContent, zKey, nKey)==0; ++static int jsonLabelCompare( ++ const char *zLeft, /* The left label */ ++ u32 nLeft, /* Size of the left label in bytes */ ++ int rawLeft, /* True if zLeft contains no escapes */ ++ const char *zRight, /* The right label */ ++ u32 nRight, /* Size of the right label in bytes */ ++ int rawRight /* True if zRight is escape-free */ ++){ ++ if( rawLeft && rawRight ){ ++ /* Simpliest case: Neither label contains escapes. A simple ++ ** memcmp() is sufficient. */ ++ if( nLeft!=nRight ) return 0; ++ return memcmp(zLeft, zRight, nLeft)==0; + }else{ +- if( pNode->n!=nKey+2 ) return 0; +- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; ++ return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft, ++ zRight, nRight, rawRight); + } + } + +-/* forward declaration */ +-static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); ++/* ++** Error returns from jsonLookupStep() ++*/ ++#define JSON_LOOKUP_ERROR 0xffffffff ++#define JSON_LOOKUP_NOTFOUND 0xfffffffe ++#define JSON_LOOKUP_PATHERROR 0xfffffffd ++#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) ++ ++/* Forward declaration */ ++static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); ++ ++ ++/* This helper routine for jsonLookupStep() populates pIns with ++** binary data that is to be inserted into pParse. ++** ++** In the common case, pIns just points to pParse->aIns and pParse->nIns. ++** But if the zPath of the original edit operation includes path elements ++** that go deeper, additional substructure must be created. ++** ++** For example: ++** ++** json_insert('{}', '$.a.b.c', 123); ++** ++** The search stops at '$.a' But additional substructure must be ++** created for the ".b.c" part of the patch so that the final result ++** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with ++** the binary equivalent of {"b":{"c":123}} so that it can be inserted. ++** ++** The caller is responsible for resetting pIns when it has finished ++** using the substructure. ++*/ ++static u32 jsonCreateEditSubstructure( ++ JsonParse *pParse, /* The original JSONB that is being edited */ ++ JsonParse *pIns, /* Populate this with the blob data to insert */ ++ const char *zTail /* Tail of the path that determins substructure */ ++){ ++ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; ++ int rc; ++ memset(pIns, 0, sizeof(*pIns)); ++ pIns->db = pParse->db; ++ if( zTail[0]==0 ){ ++ /* No substructure. Just insert what is given in pParse. */ ++ pIns->aBlob = pParse->aIns; ++ pIns->nBlob = pParse->nIns; ++ rc = 0; ++ }else{ ++ /* Construct the binary substructure */ ++ pIns->nBlob = 1; ++ pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.']; ++ pIns->eEdit = pParse->eEdit; ++ pIns->nIns = pParse->nIns; ++ pIns->aIns = pParse->aIns; ++ rc = jsonLookupStep(pIns, 0, zTail, 0); ++ pParse->oom |= pIns->oom; ++ } ++ return rc; /* Error code only */ ++} + + /* +-** Search along zPath to find the node specified. Return a pointer +-** to that node, or NULL if zPath is malformed or if there is no such +-** node. ++** Search along zPath to find the Json element specified. Return an ++** index into pParse->aBlob[] for the start of that element's value. ++** ++** If the value found by this routine is the value half of label/value pair ++** within an object, then set pPath->iLabel to the start of the corresponding ++** label, before returning. ++** ++** Return one of the JSON_LOOKUP error codes if problems are seen. + ** +-** If pApnd!=0, then try to append new nodes to complete zPath if it is +-** possible to do so and if no existing node corresponds to zPath. If +-** new nodes are appended *pApnd is set to 1. ++** This routine will also modify the blob. If pParse->eEdit is one of ++** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be ++** made to the selected value. If an edit is performed, then the return ++** value does not necessarily point to the select element. If an edit ++** is performed, the return value is only useful for detecting error ++** conditions. + */ +-static JsonNode *jsonLookupStep( ++static u32 jsonLookupStep( + JsonParse *pParse, /* The JSON to search */ +- u32 iRoot, /* Begin the search at this node */ ++ u32 iRoot, /* Begin the search at this element of aBlob[] */ + const char *zPath, /* The path to search */ +- int *pApnd, /* Append nodes to complete path if not NULL */ +- const char **pzErr /* Make *pzErr point to any syntax error in zPath */ ++ u32 iLabel /* Label if iRoot is a value of in an object */ + ){ +- u32 i, j, nKey; ++ u32 i, j, k, nKey, sz, n, iEnd, rc; + const char *zKey; +- JsonNode *pRoot = &pParse->aNode[iRoot]; +- if( zPath[0]==0 ) return pRoot; +- if( pRoot->jnFlags & JNODE_REPLACE ) return 0; ++ u8 x; ++ ++ if( zPath[0]==0 ){ ++ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){ ++ n = jsonbPayloadSize(pParse, iRoot, &sz); ++ sz += n; ++ if( pParse->eEdit==JEDIT_DEL ){ ++ if( iLabel>0 ){ ++ sz += iRoot - iLabel; ++ iRoot = iLabel; ++ } ++ jsonBlobEdit(pParse, iRoot, sz, 0, 0); ++ }else if( pParse->eEdit==JEDIT_INS ){ ++ /* Already exists, so json_insert() is a no-op */ ++ }else{ ++ /* json_set() or json_replace() */ ++ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); ++ } ++ } ++ pParse->iLabel = iLabel; ++ return iRoot; ++ } + if( zPath[0]=='.' ){ +- if( pRoot->eType!=JSON_OBJECT ) return 0; ++ int rawKey = 1; ++ x = pParse->aBlob[iRoot]; + zPath++; + if( zPath[0]=='"' ){ + zKey = zPath + 1; +- for(i=1; zPath[i] && zPath[i]!='"'; i++){} ++ for(i=1; zPath[i] && zPath[i]!='"'; i++){ ++ if( zPath[i]=='\\' && zPath[i+1]!=0 ) i++; ++ } + nKey = i-1; + if( zPath[i] ){ + i++; + }else{ +- *pzErr = zPath; +- return 0; ++ return JSON_LOOKUP_PATHERROR; + } ++ testcase( nKey==0 ); ++ rawKey = memchr(zKey, '\\', nKey)==0; + }else{ + zKey = zPath; + for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} + nKey = i; +- } +- if( nKey==0 ){ +- *pzErr = zPath; +- return 0; +- } +- j = 1; +- for(;;){ +- while( j<=pRoot->n ){ +- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){ +- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr); +- } +- j++; +- j += jsonNodeSize(&pRoot[j]); ++ if( nKey==0 ){ ++ return JSON_LOOKUP_PATHERROR; ++ } ++ } ++ if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND; ++ n = jsonbPayloadSize(pParse, iRoot, &sz); ++ j = iRoot + n; /* j is the index of a label */ ++ iEnd = j+sz; ++ while( jaBlob[j] & 0x0f; ++ if( xJSONB_TEXTRAW ) return JSON_LOOKUP_ERROR; ++ n = jsonbPayloadSize(pParse, j, &sz); ++ if( n==0 ) return JSON_LOOKUP_ERROR; ++ k = j+n; /* k is the index of the label text */ ++ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR; ++ zLabel = (const char*)&pParse->aBlob[k]; ++ rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW; ++ if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){ ++ u32 v = k+sz; /* v is the index of the value */ ++ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; ++ n = jsonbPayloadSize(pParse, v, &sz); ++ if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR; ++ assert( j>0 ); ++ rc = jsonLookupStep(pParse, v, &zPath[i], j); ++ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); ++ return rc; + } +- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; +- iRoot += pRoot->u.iAppend; +- pRoot = &pParse->aNode[iRoot]; +- j = 1; +- } +- if( pApnd ){ +- u32 iStart, iLabel; +- JsonNode *pNode; +- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); +- iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); +- zPath += i; +- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); +- if( pParse->oom ) return 0; +- if( pNode ){ +- pRoot = &pParse->aNode[iRoot]; +- pRoot->u.iAppend = iStart - iRoot; +- pRoot->jnFlags |= JNODE_APPEND; +- pParse->aNode[iLabel].jnFlags |= JNODE_RAW; +- } +- return pNode; ++ j = k+sz; ++ if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; ++ n = jsonbPayloadSize(pParse, j, &sz); ++ if( n==0 ) return JSON_LOOKUP_ERROR; ++ j += n+sz; ++ } ++ if( j>iEnd ) return JSON_LOOKUP_ERROR; ++ if( pParse->eEdit>=JEDIT_INS ){ ++ u32 nIns; /* Total bytes to insert (label+value) */ ++ JsonParse v; /* BLOB encoding of the value to be inserted */ ++ JsonParse ix; /* Header of the label to be inserted */ ++ testcase( pParse->eEdit==JEDIT_INS ); ++ testcase( pParse->eEdit==JEDIT_SET ); ++ memset(&ix, 0, sizeof(ix)); ++ ix.db = pParse->db; ++ jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); ++ pParse->oom |= ix.oom; ++ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); ++ if( !JSON_LOOKUP_ISERROR(rc) ++ && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) ++ ){ ++ assert( !pParse->oom ); ++ nIns = ix.nBlob + nKey + v.nBlob; ++ jsonBlobEdit(pParse, j, 0, 0, nIns); ++ if( !pParse->oom ){ ++ assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */ ++ assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */ ++ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); ++ k = j + ix.nBlob; ++ memcpy(&pParse->aBlob[k], zKey, nKey); ++ k += nKey; ++ memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); ++ if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot); ++ } ++ } ++ jsonParseReset(&v); ++ jsonParseReset(&ix); ++ return rc; + } + }else if( zPath[0]=='[' ){ +- i = 0; +- j = 1; +- while( safe_isdigit(zPath[j]) ){ +- i = i*10 + zPath[j] - '0'; +- j++; ++ x = pParse->aBlob[iRoot] & 0x0f; ++ if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; ++ n = jsonbPayloadSize(pParse, iRoot, &sz); ++ k = 0; ++ i = 1; ++ while( sqlite3Isdigit(zPath[i]) ){ ++ k = k*10 + zPath[i] - '0'; ++ i++; + } +- if( j<2 || zPath[j]!=']' ){ ++ if( i<2 || zPath[i]!=']' ){ + if( zPath[1]=='#' ){ +- JsonNode *pBase = pRoot; +- int iBase = iRoot; +- if( pRoot->eType!=JSON_ARRAY ) return 0; +- for(;;){ +- while( j<=pBase->n ){ +- if( (pBase[j].jnFlags & JNODE_REMOVE)==0 ) i++; +- j += jsonNodeSize(&pBase[j]); +- } +- if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; +- iBase += pBase->u.iAppend; +- pBase = &pParse->aNode[iBase]; +- j = 1; +- } +- j = 2; +- if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){ +- unsigned int x = 0; +- j = 3; ++ k = jsonbArrayCount(pParse, iRoot); ++ i = 2; ++ if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ ++ unsigned int nn = 0; ++ i = 3; + do{ +- x = x*10 + zPath[j] - '0'; +- j++; +- }while( safe_isdigit(zPath[j]) ); +- if( x>i ) return 0; +- i -= x; ++ nn = nn*10 + zPath[i] - '0'; ++ i++; ++ }while( sqlite3Isdigit(zPath[i]) ); ++ if( nn>k ) return JSON_LOOKUP_NOTFOUND; ++ k -= nn; + } +- if( zPath[j]!=']' ){ +- *pzErr = zPath; +- return 0; ++ if( zPath[i]!=']' ){ ++ return JSON_LOOKUP_PATHERROR; + } + }else{ +- *pzErr = zPath; +- return 0; ++ return JSON_LOOKUP_PATHERROR; + } + } +- if( pRoot->eType!=JSON_ARRAY ) return 0; +- zPath += j + 1; +- j = 1; +- for(;;){ +- while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){ +- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--; +- j += jsonNodeSize(&pRoot[j]); ++ j = iRoot+n; ++ iEnd = j+sz; ++ while( jdelta ) jsonAfterEditSizeAdjust(pParse, iRoot); ++ return rc; + } +- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; +- iRoot += pRoot->u.iAppend; +- pRoot = &pParse->aNode[iRoot]; +- j = 1; +- } +- if( j<=pRoot->n ){ +- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr); +- } +- if( i==0 && pApnd ){ +- u32 iStart; +- JsonNode *pNode; +- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); +- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); +- if( pParse->oom ) return 0; +- if( pNode ){ +- pRoot = &pParse->aNode[iRoot]; +- pRoot->u.iAppend = iStart - iRoot; +- pRoot->jnFlags |= JNODE_APPEND; ++ k--; ++ n = jsonbPayloadSize(pParse, j, &sz); ++ if( n==0 ) return JSON_LOOKUP_ERROR; ++ j += n+sz; ++ } ++ if( j>iEnd ) return JSON_LOOKUP_ERROR; ++ if( k>0 ) return JSON_LOOKUP_NOTFOUND; ++ if( pParse->eEdit>=JEDIT_INS ){ ++ JsonParse v; ++ testcase( pParse->eEdit==JEDIT_INS ); ++ testcase( pParse->eEdit==JEDIT_SET ); ++ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); ++ if( !JSON_LOOKUP_ISERROR(rc) ++ && jsonBlobMakeEditable(pParse, v.nBlob) ++ ){ ++ assert( !pParse->oom ); ++ jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob); + } +- return pNode; ++ jsonParseReset(&v); ++ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); ++ return rc; + } + }else{ +- *pzErr = zPath; ++ return JSON_LOOKUP_PATHERROR; + } +- return 0; ++ return JSON_LOOKUP_NOTFOUND; + } + + /* +-** Append content to pParse that will complete zPath. Return a pointer +-** to the inserted node, or return NULL if the append fails. ++** Convert a JSON BLOB into text and make that text the return value ++** of an SQL function. + */ +-static JsonNode *jsonLookupAppend( +- JsonParse *pParse, /* Append content to the JSON parse */ +- const char *zPath, /* Description of content to append */ +- int *pApnd, /* Set this flag to 1 */ +- const char **pzErr /* Make this point to any syntax error */ ++static void jsonReturnTextJsonFromBlob( ++ sqlite3_context *ctx, ++ const u8 *aBlob, ++ u32 nBlob + ){ +- *pApnd = 1; +- if( zPath[0]==0 ){ +- jsonParseAddNode(pParse, JSON_NULL, 0, 0); +- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1]; +- } +- if( zPath[0]=='.' ){ +- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); +- }else if( strncmp(zPath,"[0]",3)==0 ){ +- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); +- }else{ +- return 0; +- } +- if( pParse->oom ) return 0; +- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr); ++ JsonParse x; ++ JsonString s; ++ ++ if( NEVER(aBlob==0) ) return; ++ memset(&x, 0, sizeof(x)); ++ x.aBlob = (u8*)aBlob; ++ x.nBlob = nBlob; ++ jsonStringInit(&s, ctx); ++ jsonTranslateBlobToText(&x, 0, &s); ++ jsonReturnString(&s, 0, 0); + } + ++ + /* +-** Return the text of a syntax error message on a JSON path. Space is +-** obtained from sqlite3_malloc(). ++** Return the value of the BLOB node at index i. ++** ++** If the value is a primitive, return it as an SQL value. ++** If the value is an array or object, return it as either ++** JSON text or the BLOB encoding, depending on the JSON_B flag ++** on the userdata. + */ +-static char *jsonPathSyntaxError(const char *zErr){ +- return sqlite3_mprintf("JSON path error near '%q'", zErr); ++static void jsonReturnFromBlob( ++ JsonParse *pParse, /* Complete JSON parse tree */ ++ u32 i, /* Index of the node */ ++ sqlite3_context *pCtx, /* Return value for this function */ ++ int textOnly /* return text JSON. Disregard user-data */ ++){ ++ u32 n, sz; ++ int rc; ++ sqlite3 *db = sqlite3_context_db_handle(pCtx); ++ ++ n = jsonbPayloadSize(pParse, i, &sz); ++ if( n==0 ){ ++ sqlite3_result_error(pCtx, "malformed JSON", -1); ++ return; ++ } ++ switch( pParse->aBlob[i] & 0x0f ){ ++ case JSONB_NULL: { ++ if( sz ) goto returnfromblob_malformed; ++ sqlite3_result_null(pCtx); ++ break; ++ } ++ case JSONB_TRUE: { ++ if( sz ) goto returnfromblob_malformed; ++ sqlite3_result_int(pCtx, 1); ++ break; ++ } ++ case JSONB_FALSE: { ++ if( sz ) goto returnfromblob_malformed; ++ sqlite3_result_int(pCtx, 0); ++ break; ++ } ++ case JSONB_INT5: ++ case JSONB_INT: { ++ sqlite3_int64 iRes = 0; ++ char *z; ++ int bNeg = 0; ++ char x; ++ if( sz==0 ) goto returnfromblob_malformed; ++ x = (char)pParse->aBlob[i+n]; ++ if( x=='-' ){ ++ if( sz<2 ) goto returnfromblob_malformed; ++ n++; ++ sz--; ++ bNeg = 1; ++ } ++ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); ++ if( z==0 ) goto returnfromblob_oom; ++ rc = sqlite3DecOrHexToI64(z, &iRes); ++ sqlite3DbFree(db, z); ++ if( rc==0 ){ ++ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes); ++ }else if( rc==3 && bNeg ){ ++ sqlite3_result_int64(pCtx, SMALLEST_INT64); ++ }else if( rc==1 ){ ++ goto returnfromblob_malformed; ++ }else{ ++ if( bNeg ){ n--; sz++; } ++ goto to_double; ++ } ++ break; ++ } ++ case JSONB_FLOAT5: ++ case JSONB_FLOAT: { ++ double r; ++ char *z; ++ if( sz==0 ) goto returnfromblob_malformed; ++ to_double: ++ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); ++ if( z==0 ) goto returnfromblob_oom; ++ rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); ++ sqlite3DbFree(db, z); ++ if( rc<=0 ) goto returnfromblob_malformed; ++ sqlite3_result_double(pCtx, r); ++ break; ++ } ++ case JSONB_TEXTRAW: ++ case JSONB_TEXT: { ++ sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz, ++ SQLITE_TRANSIENT); ++ break; ++ } ++ case JSONB_TEXT5: ++ case JSONB_TEXTJ: { ++ /* Translate JSON formatted string into raw text */ ++ u32 iIn, iOut; ++ const char *z; ++ char *zOut; ++ u32 nOut = sz; ++ z = (const char*)&pParse->aBlob[i+n]; ++ zOut = sqlite3DbMallocRaw(db, ((u64)nOut)+1); ++ if( zOut==0 ) goto returnfromblob_oom; ++ for(iIn=iOut=0; iIn=2 ); ++ zOut[iOut++] = (char)(0xc0 | (v>>6)); ++ zOut[iOut++] = 0x80 | (v&0x3f); ++ }else if( v<0x10000 ){ ++ assert( szEscape>=3 ); ++ zOut[iOut++] = 0xe0 | (v>>12); ++ zOut[iOut++] = 0x80 | ((v>>6)&0x3f); ++ zOut[iOut++] = 0x80 | (v&0x3f); ++ }else if( v==JSON_INVALID_CHAR ){ ++ /* Silently ignore illegal unicode */ ++ }else{ ++ assert( szEscape>=4 ); ++ zOut[iOut++] = 0xf0 | (v>>18); ++ zOut[iOut++] = 0x80 | ((v>>12)&0x3f); ++ zOut[iOut++] = 0x80 | ((v>>6)&0x3f); ++ zOut[iOut++] = 0x80 | (v&0x3f); ++ } ++ iIn += szEscape - 1; ++ }else{ ++ zOut[iOut++] = c; ++ } ++ } /* end for() */ ++ assert( iOut<=nOut ); ++ zOut[iOut] = 0; ++ sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC); ++ break; ++ } ++ case JSONB_ARRAY: ++ case JSONB_OBJECT: { ++ int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); ++ if( flags & JSON_BLOB ){ ++ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT); ++ }else{ ++ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n); ++ } ++ break; ++ } ++ default: { ++ goto returnfromblob_malformed; ++ } ++ } ++ return; ++ ++returnfromblob_oom: ++ sqlite3_result_error_nomem(pCtx); ++ return; ++ ++returnfromblob_malformed: ++ sqlite3_result_error(pCtx, "malformed JSON", -1); ++ return; + } + + /* +-** Do a node lookup using zPath. Return a pointer to the node on success. +-** Return NULL if not found or if there is an error. ++** pArg is a function argument that might be an SQL value or a JSON ++** value. Figure out what it is and encode it as a JSONB blob. ++** Return the results in pParse. + ** +-** On an error, write an error message into pCtx and increment the +-** pParse->nErr counter. ++** pParse is uninitialized upon entry. This routine will handle the ++** initialization of pParse. The result will be contained in ++** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically ++** allocated (if pParse->nBlobAlloc is greater than zero) in which case ++** the caller is responsible for freeing the space allocated to pParse->aBlob ++** when it has finished with it. Or pParse->aBlob might be a static string ++** or a value obtained from sqlite3_value_blob(pArg). + ** +-** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if +-** nodes are appended. ++** If the argument is a BLOB that is clearly not a JSONB, then this ++** function might set an error message in ctx and return non-zero. ++** It might also set an error message and return non-zero on an OOM error. + */ +-static JsonNode *jsonLookup( +- JsonParse *pParse, /* The JSON to search */ +- const char *zPath, /* The path to search */ +- int *pApnd, /* Append nodes to complete path if not NULL */ +- sqlite3_context *pCtx /* Report errors here, if not NULL */ +-){ +- const char *zErr = 0; +- JsonNode *pNode = 0; +- char *zMsg; +- +- if( zPath==0 ) return 0; +- if( zPath[0]!='$' ){ +- zErr = zPath; +- goto lookup_err; ++static int jsonFunctionArgToBlob( ++ sqlite3_context *ctx, ++ sqlite3_value *pArg, ++ JsonParse *pParse ++){ ++ int eType = sqlite3_value_type(pArg); ++ static u8 aNull[] = { 0x00 }; ++ memset(pParse, 0, sizeof(pParse[0])); ++ pParse->db = sqlite3_context_db_handle(ctx); ++ switch( eType ){ ++ default: { ++ pParse->aBlob = aNull; ++ pParse->nBlob = 1; ++ return 0; ++ } ++ case SQLITE_BLOB: { ++ if( !jsonArgIsJsonb(pArg, pParse) ){ ++ sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1); ++ return 1; ++ } ++ break; ++ } ++ case SQLITE_TEXT: { ++ const char *zJson = (const char*)sqlite3_value_text(pArg); ++ int nJson = sqlite3_value_bytes(pArg); ++ if( zJson==0 ) return 1; ++ if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){ ++ pParse->zJson = (char*)zJson; ++ pParse->nJson = nJson; ++ if( jsonConvertTextToBlob(pParse, ctx) ){ ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ sqlite3DbFree(pParse->db, pParse->aBlob); ++ memset(pParse, 0, sizeof(pParse[0])); ++ return 1; ++ } ++ }else{ ++ jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson); ++ } ++ break; ++ } ++ case SQLITE_FLOAT: { ++ double r = sqlite3_value_double(pArg); ++ if( NEVER(sqlite3IsNaN(r)) ){ ++ jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0); ++ }else{ ++ int n = sqlite3_value_bytes(pArg); ++ const char *z = (const char*)sqlite3_value_text(pArg); ++ if( z==0 ) return 1; ++ if( z[0]=='I' ){ ++ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); ++ }else if( z[0]=='-' && z[1]=='I' ){ ++ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); ++ }else{ ++ jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z); ++ } ++ } ++ break; ++ } ++ case SQLITE_INTEGER: { ++ int n = sqlite3_value_bytes(pArg); ++ const char *z = (const char*)sqlite3_value_text(pArg); ++ if( z==0 ) return 1; ++ jsonBlobAppendNode(pParse, JSONB_INT, n, z); ++ break; ++ } ++ } ++ if( pParse->oom ){ ++ sqlite3_result_error_nomem(ctx); ++ return 1; ++ }else{ ++ return 0; + } +- zPath++; +- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr); +- if( zErr==0 ) return pNode; ++} + +-lookup_err: +- pParse->nErr++; +- assert( zErr!=0 && pCtx!=0 ); +- zMsg = jsonPathSyntaxError(zErr); ++/* ++** Generate a bad path error. ++** ++** If ctx is not NULL then push the error message into ctx and return NULL. ++** If ctx is NULL, then return the text of the error message. ++*/ ++static char *jsonBadPathError( ++ sqlite3_context *ctx, /* The function call containing the error */ ++ const char *zPath /* The path with the problem */ ++){ ++ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); ++ if( ctx==0 ) return zMsg; + if( zMsg ){ +- sqlite3_result_error(pCtx, zMsg, -1); ++ sqlite3_result_error(ctx, zMsg, -1); + sqlite3_free(zMsg); + }else{ +- sqlite3_result_error_nomem(pCtx); ++ sqlite3_result_error_nomem(ctx); + } + return 0; + } + ++/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent ++** arguments come in pairs where each pair contains a JSON path and ++** content to insert or set at that patch. Do the updates ++** and return the result. ++** ++** The specific operation is determined by eEdit, which can be one ++** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. ++*/ ++static void jsonInsertIntoBlob( ++ sqlite3_context *ctx, ++ int argc, ++ sqlite3_value **argv, ++ int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ ++){ ++ int i; ++ u32 rc = 0; ++ const char *zPath = 0; ++ int flgs; ++ JsonParse *p; ++ JsonParse ax; ++ ++ assert( (argc&1)==1 ); ++ flgs = argc==1 ? 0 : JSON_EDITABLE; ++ p = jsonParseFuncArg(ctx, argv[0], flgs); ++ if( p==0 ) return; ++ for(i=1; inBlob, ax.aBlob, ax.nBlob); ++ } ++ rc = 0; ++ }else{ ++ p->eEdit = eEdit; ++ p->nIns = ax.nBlob; ++ p->aIns = ax.aBlob; ++ p->delta = 0; ++ rc = jsonLookupStep(p, 0, zPath+1, 0); ++ } ++ jsonParseReset(&ax); ++ if( rc==JSON_LOOKUP_NOTFOUND ) continue; ++ if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; ++ } ++ jsonReturnParse(ctx, p); ++ jsonParseFree(p); ++ return; ++ ++jsonInsertIntoBlob_patherror: ++ jsonParseFree(p); ++ if( rc==JSON_LOOKUP_ERROR ){ ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ }else{ ++ jsonBadPathError(ctx, zPath); ++ } ++ return; ++} ++ ++/* ++** If pArg is a blob that seems like a JSONB blob, then initialize ++** p to point to that JSONB and return TRUE. If pArg does not seem like ++** a JSONB blob, then return FALSE. ++** ++** For small BLOBs (having no more than 7 bytes of payload) a full ++** validity check is done. So for small BLOBs this routine only returns ++** true if the value is guaranteed to be a valid JSONB. For larger BLOBs ++** (8 byte or more of payload) only the size of the outermost element is ++** checked to verify that the BLOB is superficially valid JSONB. ++** ++** A full JSONB validation is done on smaller BLOBs because those BLOBs might ++** also be text JSON that has been incorrectly cast into a BLOB. ++** (See tag-20240123-a and https://sqlite.org/forum/forumpost/012136abd5) ++** If the BLOB is 9 bytes are larger, then it is not possible for the ++** superficial size check done here to pass if the input is really text ++** JSON so we do not need to look deeper in that case. ++** ++** Why we only need to do full JSONB validation for smaller BLOBs: ++** ++** The first byte of valid JSON text must be one of: '{', '[', '"', ' ', '\n', ++** '\r', '\t', '-', or a digit '0' through '9'. Of these, only a subset ++** can also be the first byte of JSONB: '{', '[', and digits '3' ++** through '9'. In every one of those cases, the payload size is 7 bytes ++** or less. So if we do full JSONB validation for every BLOB where the ++** payload is less than 7 bytes, we will never get a false positive for ++** JSONB on an input that is really text JSON. ++*/ ++static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){ ++ u32 n, sz = 0; ++ u8 c; ++ if( sqlite3_value_type(pArg)!=SQLITE_BLOB ) return 0; ++ p->aBlob = (u8*)sqlite3_value_blob(pArg); ++ p->nBlob = (u32)sqlite3_value_bytes(pArg); ++ if( p->nBlob>0 ++ && ALWAYS(p->aBlob!=0) ++ && ((c = p->aBlob[0]) & 0x0f)<=JSONB_OBJECT ++ && (n = jsonbPayloadSize(p, 0, &sz))>0 ++ && sz+n==p->nBlob ++ && ((c & 0x0f)>JSONB_FALSE || sz==0) ++ && (sz>7 ++ || (c!=0x7b && c!=0x5b && !sqlite3Isdigit(c)) ++ || jsonbValidityCheck(p, 0, p->nBlob, 1)==0) ++ ){ ++ return 1; ++ } ++ p->aBlob = 0; ++ p->nBlob = 0; ++ return 0; ++} + + /* +-** Report the wrong number of arguments for json_insert(), json_replace() +-** or json_set(). ++** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, ++** from the SQL function argument pArg. Return a pointer to the new ++** JsonParse object. ++** ++** Ownership of the new JsonParse object is passed to the caller. The ++** caller should invoke jsonParseFree() on the return value when it ++** has finished using it. ++** ++** If any errors are detected, an appropriate error messages is set ++** using sqlite3_result_error() or the equivalent and this routine ++** returns NULL. This routine also returns NULL if the pArg argument ++** is an SQL NULL value, but no error message is set in that case. This ++** is so that SQL functions that are given NULL arguments will return ++** a NULL value. + */ +-static void jsonWrongNumArgs( +- sqlite3_context *pCtx, +- const char *zFuncName ++static JsonParse *jsonParseFuncArg( ++ sqlite3_context *ctx, ++ sqlite3_value *pArg, ++ u32 flgs + ){ +- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", +- zFuncName); +- sqlite3_result_error(pCtx, zMsg, -1); +- sqlite3_free(zMsg); ++ int eType; /* Datatype of pArg */ ++ JsonParse *p = 0; /* Value to be returned */ ++ JsonParse *pFromCache = 0; /* Value taken from cache */ ++ sqlite3 *db; /* The database connection */ ++ ++ assert( ctx!=0 ); ++ eType = sqlite3_value_type(pArg); ++ if( eType==SQLITE_NULL ){ ++ return 0; ++ } ++ pFromCache = jsonCacheSearch(ctx, pArg); ++ if( pFromCache ){ ++ pFromCache->nJPRef++; ++ if( (flgs & JSON_EDITABLE)==0 ){ ++ return pFromCache; ++ } ++ } ++ db = sqlite3_context_db_handle(ctx); ++rebuild_from_cache: ++ p = sqlite3DbMallocZero(db, sizeof(*p)); ++ if( p==0 ) goto json_pfa_oom; ++ memset(p, 0, sizeof(*p)); ++ p->db = db; ++ p->nJPRef = 1; ++ if( pFromCache!=0 ){ ++ u32 nBlob = pFromCache->nBlob; ++ p->aBlob = sqlite3DbMallocRaw(db, nBlob); ++ if( p->aBlob==0 ) goto json_pfa_oom; ++ memcpy(p->aBlob, pFromCache->aBlob, nBlob); ++ p->nBlobAlloc = p->nBlob = nBlob; ++ p->hasNonstd = pFromCache->hasNonstd; ++ jsonParseFree(pFromCache); ++ return p; ++ } ++ if( eType==SQLITE_BLOB ){ ++ if( jsonArgIsJsonb(pArg,p) ){ ++ if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){ ++ goto json_pfa_oom; ++ } ++ return p; ++ } ++ /* If the blob is not valid JSONB, fall through into trying to cast ++ ** the blob into text which is then interpreted as JSON. (tag-20240123-a) ++ ** ++ ** This goes against all historical documentation about how the SQLite ++ ** JSON functions were suppose to work. From the beginning, blob was ++ ** reserved for expansion and a blob value should have raised an error. ++ ** But it did not, due to a bug. And many applications came to depend ++ ** upon this buggy behavior, especially when using the CLI and reading ++ ** JSON text using readfile(), which returns a blob. For this reason ++ ** we will continue to support the bug moving forward. ++ ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d ++ */ ++ } ++ p->zJson = (char*)sqlite3_value_text(pArg); ++ p->nJson = sqlite3_value_bytes(pArg); ++ if( db->mallocFailed ) goto json_pfa_oom; ++ if( p->nJson==0 ) goto json_pfa_malformed; ++ assert( p->zJson!=0 ); ++ if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ ++ if( flgs & JSON_KEEPERROR ){ ++ p->nErr = 1; ++ return p; ++ }else{ ++ jsonParseFree(p); ++ return 0; ++ } ++ }else{ ++ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref); ++ int rc; ++ if( !isRCStr ){ ++ char *zNew = sqlite3RCStrNew( p->nJson ); ++ if( zNew==0 ) goto json_pfa_oom; ++ memcpy(zNew, p->zJson, p->nJson); ++ p->zJson = zNew; ++ p->zJson[p->nJson] = 0; ++ }else{ ++ sqlite3RCStrRef(p->zJson); ++ } ++ p->bJsonIsRCStr = 1; ++ rc = jsonCacheInsert(ctx, p); ++ if( rc==SQLITE_NOMEM ) goto json_pfa_oom; ++ if( flgs & JSON_EDITABLE ){ ++ pFromCache = p; ++ p = 0; ++ goto rebuild_from_cache; ++ } ++ } ++ return p; ++ ++json_pfa_malformed: ++ if( flgs & JSON_KEEPERROR ){ ++ p->nErr = 1; ++ return p; ++ }else{ ++ jsonParseFree(p); ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ return 0; ++ } ++ ++json_pfa_oom: ++ jsonParseFree(pFromCache); ++ jsonParseFree(p); ++ sqlite3_result_error_nomem(ctx); ++ return 0; + } + + /* +-** Mark all NULL entries in the Object passed in as JNODE_REMOVE. ++** Make the return value of a JSON function either the raw JSONB blob ++** or make it JSON text, depending on whether the JSON_BLOB flag is ++** set on the function. + */ +-static void jsonRemoveAllNulls(JsonNode *pNode){ +- int i, n; +- assert( pNode->eType==JSON_OBJECT ); +- n = pNode->n; +- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){ +- switch( pNode[i].eType ){ +- case JSON_NULL: +- pNode[i].jnFlags |= JNODE_REMOVE; +- break; +- case JSON_OBJECT: +- jsonRemoveAllNulls(&pNode[i]); +- break; ++static void jsonReturnParse( ++ sqlite3_context *ctx, ++ JsonParse *p ++){ ++ int flgs; ++ if( p->oom ){ ++ sqlite3_result_error_nomem(ctx); ++ return; ++ } ++ flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); ++ if( flgs & JSON_BLOB ){ ++ if( p->nBlobAlloc>0 && !p->bReadOnly ){ ++ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC); ++ p->nBlobAlloc = 0; ++ }else{ ++ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT); + } ++ }else{ ++ JsonString s; ++ jsonStringInit(&s, ctx); ++ p->delta = 0; ++ jsonTranslateBlobToText(p, 0, &s); ++ jsonReturnString(&s, p, ctx); ++ sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + } + +- + /**************************************************************************** + ** SQL functions used for testing and debugging + ****************************************************************************/ + +-#ifdef SQLITE_DEBUG ++#if SQLITE_DEBUG + /* +-** The json_parse(JSON) function returns a string which describes +-** a parse of the JSON provided. Or it returns NULL if JSON is not +-** well-formed. +-*/ +-static void jsonParseFunc( +- sqlite3_context *ctx, +- int argc, +- sqlite3_value **argv +-){ +- JsonString s; /* Output string - not real JSON */ +- JsonParse x; /* The parse */ +- u32 i; +- +- assert( argc==1 ); +- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; +- jsonParseFindParents(&x); +- jsonInit(&s, ctx); +- for(i=0; iaBlob[iStart] & 0x0f; ++ u32 savedNBlob = pParse->nBlob; ++ sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, ""); ++ if( pParse->nBlobAlloc>pParse->nBlob ){ ++ pParse->nBlob = pParse->nBlobAlloc; ++ } ++ nn = n = jsonbPayloadSize(pParse, iStart, &sz); ++ if( nn==0 ) nn = 1; ++ if( sz>0 && xaBlob[iStart+i]); ++ } ++ if( n==0 ){ ++ sqlite3_str_appendf(pOut, " ERROR invalid node size\n"); ++ iStart = n==0 ? iStart+1 : iEnd; ++ continue; ++ } ++ pParse->nBlob = savedNBlob; ++ if( iStart+n+sz>iEnd ){ ++ iEnd = iStart+n+sz; ++ if( iEnd>pParse->nBlob ){ ++ if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){ ++ iEnd = pParse->nBlobAlloc; ++ }else{ ++ iEnd = pParse->nBlob; ++ } ++ } ++ } ++ sqlite3_str_appendall(pOut," <-- "); ++ switch( x ){ ++ case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break; ++ case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break; ++ case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break; ++ case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break; ++ case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break; ++ case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break; ++ case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break; ++ case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break; ++ case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break; ++ case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break; ++ case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break; ++ case JSONB_ARRAY: { ++ sqlite3_str_appendf(pOut,"array, %u bytes\n", sz); ++ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); ++ showContent = 0; ++ break; ++ } ++ case JSONB_OBJECT: { ++ sqlite3_str_appendf(pOut, "object, %u bytes\n", sz); ++ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); ++ showContent = 0; ++ break; ++ } ++ default: { ++ sqlite3_str_appendall(pOut, "ERROR: unknown node type\n"); ++ showContent = 0; ++ break; ++ } + } +- jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d", +- i, zType, x.aNode[i].n, x.aUp[i]); +- if( x.aNode[i].u.zJContent!=0 ){ +- jsonAppendRaw(&s, " ", 1); +- jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n); ++ if( showContent ){ ++ if( sz==0 && x<=JSONB_FALSE ){ ++ sqlite3_str_append(pOut, "\n", 1); ++ }else{ ++ u32 j; ++ sqlite3_str_appendall(pOut, ": \""); ++ for(j=iStart+n; jaBlob[j]; ++ if( c<0x20 || c>=0x7f ) c = '.'; ++ sqlite3_str_append(pOut, (char*)&c, 1); ++ } ++ sqlite3_str_append(pOut, "\"\n", 2); ++ } + } +- jsonAppendRaw(&s, "\n", 1); ++ iStart += n + sz; ++ } ++} ++static void jsonShowParse(JsonParse *pParse){ ++ sqlite3_str out; ++ char zBuf[1000]; ++ if( pParse==0 ){ ++ printf("NULL pointer\n"); ++ return; ++ }else{ ++ printf("nBlobAlloc = %u\n", pParse->nBlobAlloc); ++ printf("nBlob = %u\n", pParse->nBlob); ++ printf("delta = %d\n", pParse->delta); ++ if( pParse->nBlob==0 ) return; ++ printf("content (bytes 0..%u):\n", pParse->nBlob-1); + } +- jsonParseReset(&x); +- jsonResult(&s); ++ sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000); ++ jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out); ++ printf("%s", sqlite3_str_value(&out)); ++ sqlite3_str_reset(&out); + } ++#endif /* SQLITE_DEBUG */ + ++#ifdef SQLITE_DEBUG + /* +-** The json_test1(JSON) function return true (1) if the input is JSON +-** text generated by another json function. It returns (0) if the input +-** is not known to be JSON. ++** SQL function: json_parse(JSON) ++** ++** Parse JSON using jsonParseFuncArg(). Return text that is a ++** human-readable dump of the binary JSONB for the input parameter. + */ +-static void jsonTest1Func( ++static void jsonParseFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv + ){ +- UNUSED_PARAM(argc); +- sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); ++ JsonParse *p; /* The parse */ ++ sqlite3_str out; ++ ++ assert( argc>=1 ); ++ sqlite3StrAccumInit(&out, 0, 0, 0, 1000000); ++ p = jsonParseFuncArg(ctx, argv[0], 0); ++ if( p==0 ) return; ++ if( argc==1 ){ ++ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); ++ sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8); ++ }else{ ++ jsonShowParse(p); ++ } ++ jsonParseFree(p); ++ sqlite3_str_reset(&out); + } + #endif /* SQLITE_DEBUG */ + +@@ -186857,7 +211698,7 @@ static void jsonTest1Func( + ****************************************************************************/ + + /* +-** Implementation of the json_QUOTE(VALUE) function. Return a JSON value ++** Implementation of the json_quote(VALUE) function. Return a JSON value + ** corresponding to the SQL value input. Mostly this means putting + ** double-quotes around strings and returning the unquoted string "null" + ** when given a NULL input. +@@ -186868,11 +211709,11 @@ static void jsonQuoteFunc( + sqlite3_value **argv + ){ + JsonString jx; +- UNUSED_PARAM(argc); ++ UNUSED_PARAMETER(argc); + +- jsonInit(&jx, ctx); +- jsonAppendValue(&jx, argv[0]); +- jsonResult(&jx); ++ jsonStringInit(&jx, ctx); ++ jsonAppendSqlValue(&jx, argv[0]); ++ jsonReturnString(&jx, 0, 0); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + +@@ -186889,18 +211730,17 @@ static void jsonArrayFunc( + int i; + JsonString jx; + +- jsonInit(&jx, ctx); ++ jsonStringInit(&jx, ctx); + jsonAppendChar(&jx, '['); + for(i=0; inNode ); + if( argc==2 ){ + const char *zPath = (const char*)sqlite3_value_text(argv[1]); +- pNode = jsonLookup(p, zPath, 0, ctx); ++ if( zPath==0 ){ ++ jsonParseFree(p); ++ return; ++ } ++ i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); ++ if( JSON_LOOKUP_ISERROR(i) ){ ++ if( i==JSON_LOOKUP_NOTFOUND ){ ++ /* no-op */ ++ }else if( i==JSON_LOOKUP_PATHERROR ){ ++ jsonBadPathError(ctx, zPath); ++ }else{ ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ } ++ eErr = 1; ++ i = 0; ++ } + }else{ +- pNode = p->aNode; +- } +- if( pNode==0 ){ +- return; ++ i = 0; + } +- if( pNode->eType==JSON_ARRAY ){ +- assert( (pNode->jnFlags & JNODE_APPEND)==0 ); +- for(i=1; i<=pNode->n; n++){ +- i += jsonNodeSize(&pNode[i]); +- } ++ if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ ++ cnt = jsonbArrayCount(p, i); + } +- sqlite3_result_int64(ctx, n); ++ if( !eErr ) sqlite3_result_int64(ctx, cnt); ++ jsonParseFree(p); ++} ++ ++/* True if the string is all alphanumerics and underscores */ ++static int jsonAllAlphanum(const char *z, int n){ ++ int i; ++ for(i=0; i"(JSON,PATH) ++** "->>"(JSON,PATH) + ** +-** Return the element described by PATH. Return NULL if there is no +-** PATH element. If there are multiple PATHs, then return a JSON array +-** with the result from each path. Throw an error if the JSON or any PATH +-** is malformed. ++** Return the element described by PATH. Return NULL if that PATH element ++** is not found. ++** ++** If JSON_JSON is set or if more that one PATH argument is supplied then ++** always return a JSON representation of the result. If JSON_SQL is set, ++** then always return an SQL representation of the result. If neither flag ++** is present and argc==2, then return JSON for objects and arrays and SQL ++** for all other values. ++** ++** When multiple PATH arguments are supplied, the result is a JSON array ++** containing the result of each PATH. ++** ++** Abbreviated JSON path expressions are allows if JSON_ABPATH, for ++** compatibility with PG. + */ + static void jsonExtractFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv + ){ +- JsonParse *p; /* The parse */ +- JsonNode *pNode; +- const char *zPath; +- JsonString jx; +- int i; ++ JsonParse *p = 0; /* The parse */ ++ int flags; /* Flags associated with the function */ ++ int i; /* Loop counter */ ++ JsonString jx; /* String for array result */ + + if( argc<2 ) return; +- p = jsonParseCached(ctx, argv, ctx); ++ p = jsonParseFuncArg(ctx, argv[0], 0); + if( p==0 ) return; +- jsonInit(&jx, ctx); +- jsonAppendChar(&jx, '['); ++ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); ++ jsonStringInit(&jx, ctx); ++ if( argc>2 ){ ++ jsonAppendChar(&jx, '['); ++ } + for(i=1; inErr ) break; +- if( argc>2 ){ +- jsonAppendSeparator(&jx); +- if( pNode ){ +- jsonRenderNode(pNode, &jx, 0); ++ /* With a single PATH argument */ ++ const char *zPath = (const char*)sqlite3_value_text(argv[i]); ++ int nPath; ++ u32 j; ++ if( zPath==0 ) goto json_extract_error; ++ nPath = sqlite3Strlen30(zPath); ++ if( zPath[0]=='$' ){ ++ j = jsonLookupStep(p, 0, zPath+1, 0); ++ }else if( (flags & JSON_ABPATH) ){ ++ /* The -> and ->> operators accept abbreviated PATH arguments. This ++ ** is mostly for compatibility with PostgreSQL, but also for ++ ** convenience. ++ ** ++ ** NUMBER ==> $[NUMBER] // PG compatible ++ ** LABEL ==> $.LABEL // PG compatible ++ ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience ++ ** ++ ** Updated 2024-05-27: If the NUMBER is negative, then PG counts from ++ ** the right of the array. Hence for negative NUMBER: ++ ** ++ ** NUMBER ==> $[#NUMBER] // PG compatible ++ */ ++ jsonStringInit(&jx, ctx); ++ if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ ++ jsonAppendRawNZ(&jx, "[", 1); ++ if( zPath[0]=='-' ) jsonAppendRawNZ(&jx,"#",1); ++ jsonAppendRaw(&jx, zPath, nPath); ++ jsonAppendRawNZ(&jx, "]", 2); ++ }else if( jsonAllAlphanum(zPath, nPath) ){ ++ jsonAppendRawNZ(&jx, ".", 1); ++ jsonAppendRaw(&jx, zPath, nPath); ++ }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){ ++ jsonAppendRaw(&jx, zPath, nPath); ++ }else{ ++ jsonAppendRawNZ(&jx, ".\"", 2); ++ jsonAppendRaw(&jx, zPath, nPath); ++ jsonAppendRawNZ(&jx, "\"", 1); ++ } ++ jsonStringTerminate(&jx); ++ j = jsonLookupStep(p, 0, jx.zBuf, 0); ++ jsonStringReset(&jx); ++ }else{ ++ jsonBadPathError(ctx, zPath); ++ goto json_extract_error; ++ } ++ if( jnBlob ){ ++ if( argc==2 ){ ++ if( flags & JSON_JSON ){ ++ jsonStringInit(&jx, ctx); ++ jsonTranslateBlobToText(p, j, &jx); ++ jsonReturnString(&jx, 0, 0); ++ jsonStringReset(&jx); ++ assert( (flags & JSON_BLOB)==0 ); ++ sqlite3_result_subtype(ctx, JSON_SUBTYPE); ++ }else{ ++ jsonReturnFromBlob(p, j, ctx, 0); ++ if( (flags & (JSON_SQL|JSON_BLOB))==0 ++ && (p->aBlob[j]&0x0f)>=JSONB_ARRAY ++ ){ ++ sqlite3_result_subtype(ctx, JSON_SUBTYPE); ++ } ++ } ++ }else{ ++ jsonAppendSeparator(&jx); ++ jsonTranslateBlobToText(p, j, &jx); ++ } ++ }else if( j==JSON_LOOKUP_NOTFOUND ){ ++ if( argc==2 ){ ++ goto json_extract_error; /* Return NULL if not found */ + }else{ +- jsonAppendRaw(&jx, "null", 4); ++ jsonAppendSeparator(&jx); ++ jsonAppendRawNZ(&jx, "null", 4); + } +- }else if( pNode ){ +- jsonReturn(pNode, ctx, 0); ++ }else if( j==JSON_LOOKUP_ERROR ){ ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ goto json_extract_error; ++ }else{ ++ jsonBadPathError(ctx, zPath); ++ goto json_extract_error; + } + } +- if( argc>2 && i==argc ){ ++ if( argc>2 ){ + jsonAppendChar(&jx, ']'); +- jsonResult(&jx); +- sqlite3_result_subtype(ctx, JSON_SUBTYPE); ++ jsonReturnString(&jx, 0, 0); ++ if( (flags & JSON_BLOB)==0 ){ ++ sqlite3_result_subtype(ctx, JSON_SUBTYPE); ++ } + } +- jsonReset(&jx); ++json_extract_error: ++ jsonStringReset(&jx); ++ jsonParseFree(p); ++ return; + } + +-/* This is the RFC 7396 MergePatch algorithm. +-*/ +-static JsonNode *jsonMergePatch( +- JsonParse *pParse, /* The JSON parser that contains the TARGET */ +- u32 iTarget, /* Node of the TARGET in pParse */ +- JsonNode *pPatch /* The PATCH */ +-){ +- u32 i, j; +- u32 iRoot; +- JsonNode *pTarget; +- if( pPatch->eType!=JSON_OBJECT ){ +- return pPatch; +- } +- assert( iTarget>=0 && iTargetnNode ); +- pTarget = &pParse->aNode[iTarget]; +- assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); +- if( pTarget->eType!=JSON_OBJECT ){ +- jsonRemoveAllNulls(pPatch); +- return pPatch; +- } +- iRoot = iTarget; +- for(i=1; in; i += jsonNodeSize(&pPatch[i+1])+1){ +- u32 nKey; +- const char *zKey; +- assert( pPatch[i].eType==JSON_STRING ); +- assert( pPatch[i].jnFlags & JNODE_LABEL ); +- nKey = pPatch[i].n; +- zKey = pPatch[i].u.zJContent; +- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); +- for(j=1; jn; j += jsonNodeSize(&pTarget[j+1])+1 ){ +- assert( pTarget[j].eType==JSON_STRING ); +- assert( pTarget[j].jnFlags & JNODE_LABEL ); +- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); +- if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){ +- if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; +- if( pPatch[i+1].eType==JSON_NULL ){ +- pTarget[j+1].jnFlags |= JNODE_REMOVE; +- }else{ +- JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); +- if( pNew==0 ) return 0; +- pTarget = &pParse->aNode[iTarget]; +- if( pNew!=&pTarget[j+1] ){ +- pTarget[j+1].u.pPatch = pNew; +- pTarget[j+1].jnFlags |= JNODE_PATCH; +- } +- } +- break; ++/* ++** Return codes for jsonMergePatch() ++*/ ++#define JSON_MERGE_OK 0 /* Success */ ++#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */ ++#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */ ++#define JSON_MERGE_OOM 3 /* Out-of-memory condition */ ++ ++/* ++** RFC-7396 MergePatch for two JSONB blobs. ++** ++** pTarget is the target. pPatch is the patch. The target is updated ++** in place. The patch is read-only. ++** ++** The original RFC-7396 algorithm is this: ++** ++** define MergePatch(Target, Patch): ++** if Patch is an Object: ++** if Target is not an Object: ++** Target = {} # Ignore the contents and set it to an empty Object ++** for each Name/Value pair in Patch: ++** if Value is null: ++** if Name exists in Target: ++** remove the Name/Value pair from Target ++** else: ++** Target[Name] = MergePatch(Target[Name], Value) ++** return Target ++** else: ++** return Patch ++** ++** Here is an equivalent algorithm restructured to show the actual ++** implementation: ++** ++** 01 define MergePatch(Target, Patch): ++** 02 if Patch is not an Object: ++** 03 return Patch ++** 04 else: // if Patch is an Object ++** 05 if Target is not an Object: ++** 06 Target = {} ++** 07 for each Name/Value pair in Patch: ++** 08 if Name exists in Target: ++** 09 if Value is null: ++** 10 remove the Name/Value pair from Target ++** 11 else ++** 12 Target[name] = MergePatch(Target[Name], Value) ++** 13 else if Value is not NULL: ++** 14 if Value is not an Object: ++** 15 Target[name] = Value ++** 16 else: ++** 17 Target[name] = MergePatch('{}',value) ++** 18 return Target ++** | ++** ^---- Line numbers referenced in comments in the implementation ++*/ ++static int jsonMergePatch( ++ JsonParse *pTarget, /* The JSON parser that contains the TARGET */ ++ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ ++ const JsonParse *pPatch, /* The PATCH */ ++ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */ ++){ ++ u8 x; /* Type of a single node */ ++ u32 n, sz=0; /* Return values from jsonbPayloadSize() */ ++ u32 iTCursor; /* Cursor position while scanning the target object */ ++ u32 iTStart; /* First label in the target object */ ++ u32 iTEndBE; /* Original first byte past end of target, before edit */ ++ u32 iTEnd; /* Current first byte past end of target */ ++ u8 eTLabel; /* Node type of the target label */ ++ u32 iTLabel = 0; /* Index of the label */ ++ u32 nTLabel = 0; /* Header size in bytes for the target label */ ++ u32 szTLabel = 0; /* Size of the target label payload */ ++ u32 iTValue = 0; /* Index of the target value */ ++ u32 nTValue = 0; /* Header size of the target value */ ++ u32 szTValue = 0; /* Payload size for the target value */ ++ ++ u32 iPCursor; /* Cursor position while scanning the patch */ ++ u32 iPEnd; /* First byte past the end of the patch */ ++ u8 ePLabel; /* Node type of the patch label */ ++ u32 iPLabel; /* Start of patch label */ ++ u32 nPLabel; /* Size of header on the patch label */ ++ u32 szPLabel; /* Payload size of the patch label */ ++ u32 iPValue; /* Start of patch value */ ++ u32 nPValue; /* Header size for the patch value */ ++ u32 szPValue; /* Payload size of the patch value */ ++ ++ assert( iTarget>=0 && iTargetnBlob ); ++ assert( iPatch>=0 && iPatchnBlob ); ++ x = pPatch->aBlob[iPatch] & 0x0f; ++ if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */ ++ u32 szPatch; /* Total size of the patch, header+payload */ ++ u32 szTarget; /* Total size of the target, header+payload */ ++ n = jsonbPayloadSize(pPatch, iPatch, &sz); ++ szPatch = n+sz; ++ sz = 0; ++ n = jsonbPayloadSize(pTarget, iTarget, &sz); ++ szTarget = n+sz; ++ jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch); ++ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */ ++ } ++ x = pTarget->aBlob[iTarget] & 0x0f; ++ if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */ ++ n = jsonbPayloadSize(pTarget, iTarget, &sz); ++ jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0); ++ x = pTarget->aBlob[iTarget]; ++ pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT; ++ } ++ n = jsonbPayloadSize(pPatch, iPatch, &sz); ++ if( NEVER(n==0) ) return JSON_MERGE_BADPATCH; ++ iPCursor = iPatch+n; ++ iPEnd = iPCursor+sz; ++ n = jsonbPayloadSize(pTarget, iTarget, &sz); ++ if( NEVER(n==0) ) return JSON_MERGE_BADTARGET; ++ iTStart = iTarget+n; ++ iTEndBE = iTStart+sz; ++ ++ while( iPCursoraBlob[iPCursor] & 0x0f; ++ if( ePLabelJSONB_TEXTRAW ){ ++ return JSON_MERGE_BADPATCH; ++ } ++ nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel); ++ if( nPLabel==0 ) return JSON_MERGE_BADPATCH; ++ iPValue = iPCursor + nPLabel + szPLabel; ++ if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH; ++ nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue); ++ if( nPValue==0 ) return JSON_MERGE_BADPATCH; ++ iPCursor = iPValue + nPValue + szPValue; ++ if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH; ++ ++ iTCursor = iTStart; ++ iTEnd = iTEndBE + pTarget->delta; ++ while( iTCursoraBlob[iTCursor] & 0x0f; ++ if( eTLabelJSONB_TEXTRAW ){ ++ return JSON_MERGE_BADTARGET; ++ } ++ nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel); ++ if( nTLabel==0 ) return JSON_MERGE_BADTARGET; ++ iTValue = iTLabel + nTLabel + szTLabel; ++ if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET; ++ nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue); ++ if( nTValue==0 ) return JSON_MERGE_BADTARGET; ++ if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET; ++ isEqual = jsonLabelCompare( ++ (const char*)&pPatch->aBlob[iPLabel+nPLabel], ++ szPLabel, ++ (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW), ++ (const char*)&pTarget->aBlob[iTLabel+nTLabel], ++ szTLabel, ++ (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW)); ++ if( isEqual ) break; ++ iTCursor = iTValue + nTValue + szTValue; ++ } ++ x = pPatch->aBlob[iPValue] & 0x0f; ++ if( iTCursoroom) ) return JSON_MERGE_OOM; ++ }else{ ++ /* Algorithm line 12 */ ++ int rc, savedDelta = pTarget->delta; ++ pTarget->delta = 0; ++ rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue); ++ if( rc ) return rc; ++ pTarget->delta += savedDelta; ++ } ++ }else if( x>0 ){ /* Algorithm line 13 */ ++ /* No match and patch value is not NULL */ ++ u32 szNew = szPLabel+nPLabel; ++ if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */ ++ jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew); ++ if( pTarget->oom ) return JSON_MERGE_OOM; ++ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); ++ memcpy(&pTarget->aBlob[iTEnd+szNew], ++ &pPatch->aBlob[iPValue], szPValue+nPValue); ++ }else{ ++ int rc, savedDelta; ++ jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1); ++ if( pTarget->oom ) return JSON_MERGE_OOM; ++ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); ++ pTarget->aBlob[iTEnd+szNew] = 0x00; ++ savedDelta = pTarget->delta; ++ pTarget->delta = 0; ++ rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue); ++ if( rc ) return rc; ++ pTarget->delta += savedDelta; + } + } +- if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){ +- int iStart, iPatch; +- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); +- jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); +- iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0); +- if( pParse->oom ) return 0; +- jsonRemoveAllNulls(pPatch); +- pTarget = &pParse->aNode[iTarget]; +- pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; +- pParse->aNode[iRoot].u.iAppend = iStart - iRoot; +- iRoot = iStart; +- pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; +- pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; +- } + } +- return pTarget; ++ if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget); ++ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; + } + ++ + /* + ** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON + ** object that is the result of running the RFC 7396 MergePatch() algorithm +@@ -187063,25 +212137,27 @@ static void jsonPatchFunc( + int argc, + sqlite3_value **argv + ){ +- JsonParse x; /* The JSON that is being patched */ +- JsonParse y; /* The patch */ +- JsonNode *pResult; /* The result of the merge */ ++ JsonParse *pTarget; /* The TARGET */ ++ JsonParse *pPatch; /* The PATCH */ ++ int rc; /* Result code */ + +- UNUSED_PARAM(argc); +- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; +- if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){ +- jsonParseReset(&x); +- return; +- } +- pResult = jsonMergePatch(&x, 0, y.aNode); +- assert( pResult!=0 || x.oom ); +- if( pResult ){ +- jsonReturnJson(pResult, ctx, 0); +- }else{ +- sqlite3_result_error_nomem(ctx); ++ UNUSED_PARAMETER(argc); ++ assert( argc==2 ); ++ pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); ++ if( pTarget==0 ) return; ++ pPatch = jsonParseFuncArg(ctx, argv[1], 0); ++ if( pPatch ){ ++ rc = jsonMergePatch(pTarget, 0, pPatch, 0); ++ if( rc==JSON_MERGE_OK ){ ++ jsonReturnParse(ctx, pTarget); ++ }else if( rc==JSON_MERGE_OOM ){ ++ sqlite3_result_error_nomem(ctx); ++ }else{ ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ } ++ jsonParseFree(pPatch); + } +- jsonParseReset(&x); +- jsonParseReset(&y); ++ jsonParseFree(pTarget); + } + + +@@ -187105,23 +212181,23 @@ static void jsonObjectFunc( + "of arguments", -1); + return; + } +- jsonInit(&jx, ctx); ++ jsonStringInit(&jx, ctx); + jsonAppendChar(&jx, '{'); + for(i=0; i1 ? JSON_EDITABLE : 0); ++ if( p==0 ) return; ++ for(i=1; ijnFlags |= JNODE_REMOVE; +- } +- if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){ +- jsonReturnJson(x.aNode, ctx, 0); ++ if( zPath==0 ){ ++ goto json_remove_done; ++ } ++ if( zPath[0]!='$' ){ ++ goto json_remove_patherror; ++ } ++ if( zPath[1]==0 ){ ++ /* json_remove(j,'$') returns NULL */ ++ goto json_remove_done; ++ } ++ p->eEdit = JEDIT_DEL; ++ p->delta = 0; ++ rc = jsonLookupStep(p, 0, zPath+1, 0); ++ if( JSON_LOOKUP_ISERROR(rc) ){ ++ if( rc==JSON_LOOKUP_NOTFOUND ){ ++ continue; /* No-op */ ++ }else if( rc==JSON_LOOKUP_PATHERROR ){ ++ jsonBadPathError(ctx, zPath); ++ }else{ ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ } ++ goto json_remove_done; ++ } + } +-remove_done: +- jsonParseReset(&x); ++ jsonReturnParse(ctx, p); ++ jsonParseFree(p); ++ return; ++ ++json_remove_patherror: ++ jsonBadPathError(ctx, zPath); ++ ++json_remove_done: ++ jsonParseFree(p); ++ return; + } + + /* +@@ -187170,36 +212270,15 @@ static void jsonReplaceFunc( + int argc, + sqlite3_value **argv + ){ +- JsonParse x; /* The parse */ +- JsonNode *pNode; +- const char *zPath; +- u32 i; +- + if( argc<1 ) return; + if( (argc&1)==0 ) { + jsonWrongNumArgs(ctx, "replace"); + return; + } +- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; +- assert( x.nNode ); +- for(i=1; i<(u32)argc; i+=2){ +- zPath = (const char*)sqlite3_value_text(argv[i]); +- pNode = jsonLookup(&x, zPath, 0, ctx); +- if( x.nErr ) goto replace_err; +- if( pNode ){ +- pNode->jnFlags |= (u8)JNODE_REPLACE; +- pNode->u.iReplace = i + 1; +- } +- } +- if( x.aNode[0].jnFlags & JNODE_REPLACE ){ +- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); +- }else{ +- jsonReturnJson(x.aNode, ctx, argv); +- } +-replace_err: +- jsonParseReset(&x); ++ jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL); + } + ++ + /* + ** json_set(JSON, PATH, VALUE, ...) + ** +@@ -187217,49 +212296,24 @@ static void jsonSetFunc( + int argc, + sqlite3_value **argv + ){ +- JsonParse x; /* The parse */ +- JsonNode *pNode; +- const char *zPath; +- u32 i; +- int bApnd; +- int bIsSet = *(int*)sqlite3_user_data(ctx); ++ ++ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); ++ int bIsSet = (flags&JSON_ISSET)!=0; + + if( argc<1 ) return; + if( (argc&1)==0 ) { + jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); + return; + } +- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; +- assert( x.nNode ); +- for(i=1; i<(u32)argc; i+=2){ +- zPath = (const char*)sqlite3_value_text(argv[i]); +- bApnd = 0; +- pNode = jsonLookup(&x, zPath, &bApnd, ctx); +- if( x.oom ){ +- sqlite3_result_error_nomem(ctx); +- goto jsonSetDone; +- }else if( x.nErr ){ +- goto jsonSetDone; +- }else if( pNode && (bApnd || bIsSet) ){ +- pNode->jnFlags |= (u8)JNODE_REPLACE; +- pNode->u.iReplace = i + 1; +- } +- } +- if( x.aNode[0].jnFlags & JNODE_REPLACE ){ +- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); +- }else{ +- jsonReturnJson(x.aNode, ctx, argv); +- } +-jsonSetDone: +- jsonParseReset(&x); ++ jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); + } + + /* + ** json_type(JSON) + ** json_type(JSON, PATH) + ** +-** Return the top-level "type" of a JSON string. Throw an error if +-** either the JSON or PATH inputs are not well-formed. ++** Return the top-level "type" of a JSON string. json_type() raises an ++** error if either the JSON or PATH inputs are not well-formed. + */ + static void jsonTypeFunc( + sqlite3_context *ctx, +@@ -187267,27 +212321,127 @@ static void jsonTypeFunc( + sqlite3_value **argv + ){ + JsonParse *p; /* The parse */ +- const char *zPath; +- JsonNode *pNode; ++ const char *zPath = 0; ++ u32 i; + +- p = jsonParseCached(ctx, argv, ctx); ++ p = jsonParseFuncArg(ctx, argv[0], 0); + if( p==0 ) return; + if( argc==2 ){ + zPath = (const char*)sqlite3_value_text(argv[1]); +- pNode = jsonLookup(p, zPath, 0, ctx); ++ if( zPath==0 ) goto json_type_done; ++ if( zPath[0]!='$' ){ ++ jsonBadPathError(ctx, zPath); ++ goto json_type_done; ++ } ++ i = jsonLookupStep(p, 0, zPath+1, 0); ++ if( JSON_LOOKUP_ISERROR(i) ){ ++ if( i==JSON_LOOKUP_NOTFOUND ){ ++ /* no-op */ ++ }else if( i==JSON_LOOKUP_PATHERROR ){ ++ jsonBadPathError(ctx, zPath); ++ }else{ ++ sqlite3_result_error(ctx, "malformed JSON", -1); ++ } ++ goto json_type_done; ++ } + }else{ +- pNode = p->aNode; ++ i = 0; + } +- if( pNode ){ +- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC); ++ sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC); ++json_type_done: ++ jsonParseFree(p); ++} ++ ++/* ++** json_pretty(JSON) ++** json_pretty(JSON, INDENT) ++** ++** Return text that is a pretty-printed rendering of the input JSON. ++** If the argument is not valid JSON, return NULL. ++** ++** The INDENT argument is text that is used for indentation. If omitted, ++** it defaults to four spaces (the same as PostgreSQL). ++*/ ++static void jsonPrettyFunc( ++ sqlite3_context *ctx, ++ int argc, ++ sqlite3_value **argv ++){ ++ JsonString s; /* The output string */ ++ JsonPretty x; /* Pretty printing context */ ++ ++ memset(&x, 0, sizeof(x)); ++ x.pParse = jsonParseFuncArg(ctx, argv[0], 0); ++ if( x.pParse==0 ) return; ++ x.pOut = &s; ++ jsonStringInit(&s, ctx); ++ if( argc==1 || (x.zIndent = (const char*)sqlite3_value_text(argv[1]))==0 ){ ++ x.zIndent = " "; ++ x.szIndent = 4; ++ }else{ ++ x.szIndent = (u32)strlen(x.zIndent); + } ++ jsonTranslateBlobToPrettyText(&x, 0); ++ jsonReturnString(&s, 0, 0); ++ jsonParseFree(x.pParse); + } + + /* + ** json_valid(JSON) +-** +-** Return 1 if JSON is a well-formed JSON string according to RFC-7159. +-** Return 0 otherwise. ++** json_valid(JSON, FLAGS) ++** ++** Check the JSON argument to see if it is well-formed. The FLAGS argument ++** encodes the various constraints on what is meant by "well-formed": ++** ++** 0x01 Canonical RFC-8259 JSON text ++** 0x02 JSON text with optional JSON-5 extensions ++** 0x04 Superficially appears to be JSONB ++** 0x08 Strictly well-formed JSONB ++** ++** If the FLAGS argument is omitted, it defaults to 1. Useful values for ++** FLAGS include: ++** ++** 1 Strict canonical JSON text ++** 2 JSON text perhaps with JSON-5 extensions ++** 4 Superficially appears to be JSONB ++** 5 Canonical JSON text or superficial JSONB ++** 6 JSON-5 text or superficial JSONB ++** 8 Strict JSONB ++** 9 Canonical JSON text or strict JSONB ++** 10 JSON-5 text or strict JSONB ++** ++** Other flag combinations are redundant. For example, every canonical ++** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3 ++** are the same. Similarly, any input that passes a strict JSONB validation ++** will also pass the superficial validation so 12 through 15 are the same ++** as 8 through 11 respectively. ++** ++** This routine runs in linear time to validate text and when doing strict ++** JSONB validation. Superficial JSONB validation is constant time, ++** assuming the BLOB is already in memory. The performance advantage ++** of superficial JSONB validation is why that option is provided. ++** Application developers can choose to do fast superficial validation or ++** slower strict validation, according to their specific needs. ++** ++** Only the lower four bits of the FLAGS argument are currently used. ++** Higher bits are reserved for future expansion. To facilitate ++** compatibility, the current implementation raises an error if any bit ++** in FLAGS is set other than the lower four bits. ++** ++** The original circa 2015 implementation of the JSON routines in ++** SQLite only supported canonical RFC-8259 JSON text and the json_valid() ++** function only accepted one argument. That is why the default value ++** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only ++** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS ++** argument was added when the JSON routines were extended to support ++** JSON5-like extensions and binary JSONB stored in BLOBs. ++** ++** Return Values: ++** ++** * Raise an error if FLAGS is outside the range of 1 to 15. ++** * Return NULL if the input is NULL ++** * Return 1 if the input is well-formed. ++** * Return 0 if the input is not well-formed. + */ + static void jsonValidFunc( + sqlite3_context *ctx, +@@ -187295,11 +212449,121 @@ static void jsonValidFunc( + sqlite3_value **argv + ){ + JsonParse *p; /* The parse */ +- UNUSED_PARAM(argc); +- p = jsonParseCached(ctx, argv, 0); +- sqlite3_result_int(ctx, p!=0); ++ u8 flags = 1; ++ u8 res = 0; ++ if( argc==2 ){ ++ i64 f = sqlite3_value_int64(argv[1]); ++ if( f<1 || f>15 ){ ++ sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be" ++ " between 1 and 15", -1); ++ return; ++ } ++ flags = f & 0x0f; ++ } ++ switch( sqlite3_value_type(argv[0]) ){ ++ case SQLITE_NULL: { ++#ifdef SQLITE_LEGACY_JSON_VALID ++ /* Incorrect legacy behavior was to return FALSE for a NULL input */ ++ sqlite3_result_int(ctx, 0); ++#endif ++ return; ++ } ++ case SQLITE_BLOB: { ++ JsonParse py; ++ memset(&py, 0, sizeof(py)); ++ if( jsonArgIsJsonb(argv[0], &py) ){ ++ if( flags & 0x04 ){ ++ /* Superficial checking only - accomplished by the ++ ** jsonArgIsJsonb() call above. */ ++ res = 1; ++ }else if( flags & 0x08 ){ ++ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If ++ ** no errors occur, call that a "strict check". */ ++ res = 0==jsonbValidityCheck(&py, 0, py.nBlob, 1); ++ } ++ break; ++ } ++ /* Fall through into interpreting the input as text. See note ++ ** above at tag-20240123-a. */ ++ /* no break */ deliberate_fall_through ++ } ++ default: { ++ JsonParse px; ++ if( (flags & 0x3)==0 ) break; ++ memset(&px, 0, sizeof(px)); ++ ++ p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR); ++ if( p ){ ++ if( p->oom ){ ++ sqlite3_result_error_nomem(ctx); ++ }else if( p->nErr ){ ++ /* no-op */ ++ }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){ ++ res = 1; ++ } ++ jsonParseFree(p); ++ }else{ ++ sqlite3_result_error_nomem(ctx); ++ } ++ break; ++ } ++ } ++ sqlite3_result_int(ctx, res); + } + ++/* ++** json_error_position(JSON) ++** ++** If the argument is NULL, return NULL ++** ++** If the argument is BLOB, do a full validity check and return non-zero ++** if the check fails. The return value is the approximate 1-based offset ++** to the byte of the element that contains the first error. ++** ++** Otherwise interpret the argument is TEXT (even if it is numeric) and ++** return the 1-based character position for where the parser first recognized ++** that the input was not valid JSON, or return 0 if the input text looks ++** ok. JSON-5 extensions are accepted. ++*/ ++static void jsonErrorFunc( ++ sqlite3_context *ctx, ++ int argc, ++ sqlite3_value **argv ++){ ++ i64 iErrPos = 0; /* Error position to be returned */ ++ JsonParse s; ++ ++ assert( argc==1 ); ++ UNUSED_PARAMETER(argc); ++ memset(&s, 0, sizeof(s)); ++ s.db = sqlite3_context_db_handle(ctx); ++ if( jsonArgIsJsonb(argv[0], &s) ){ ++ iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1); ++ }else{ ++ s.zJson = (char*)sqlite3_value_text(argv[0]); ++ if( s.zJson==0 ) return; /* NULL input or OOM */ ++ s.nJson = sqlite3_value_bytes(argv[0]); ++ if( jsonConvertTextToBlob(&s,0) ){ ++ if( s.oom ){ ++ iErrPos = -1; ++ }else{ ++ /* Convert byte-offset s.iErr into a character offset */ ++ u32 k; ++ assert( s.zJson!=0 ); /* Because s.oom is false */ ++ for(k=0; kzBuf==0 ){ +- jsonInit(pStr, ctx); ++ jsonStringInit(pStr, ctx); + jsonAppendChar(pStr, '['); + }else if( pStr->nUsed>1 ){ + jsonAppendChar(pStr, ','); +- pStr->pCtx = ctx; + } +- jsonAppendValue(pStr, argv[0]); ++ pStr->pCtx = ctx; ++ jsonAppendSqlValue(pStr, argv[0]); + } + } + static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ + JsonString *pStr; + pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); + if( pStr ){ ++ int flags; + pStr->pCtx = ctx; + jsonAppendChar(pStr, ']'); +- if( pStr->bErr ){ +- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); +- assert( pStr->bStatic ); ++ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); ++ if( pStr->eErr ){ ++ jsonReturnString(pStr, 0, 0); ++ return; ++ }else if( flags & JSON_BLOB ){ ++ jsonReturnStringAsBlob(pStr); ++ if( isFinal ){ ++ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); ++ }else{ ++ jsonStringTrimOneChar(pStr); ++ } ++ return; + }else if( isFinal ){ + sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, +- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); ++ pStr->bStatic ? SQLITE_TRANSIENT : ++ sqlite3RCStrUnref); + pStr->bStatic = 1; + }else{ + sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); +- pStr->nUsed--; ++ jsonStringTrimOneChar(pStr); + } + }else{ + sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); +@@ -187375,20 +212650,16 @@ static void jsonGroupInverse( + char *z; + char c; + JsonString *pStr; +- UNUSED_PARAM(argc); +- UNUSED_PARAM(argv); ++ UNUSED_PARAMETER(argc); ++ UNUSED_PARAMETER(argv); + pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); + #ifdef NEVER + /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will +- ** always have been called to initalize it */ ++ ** always have been called to initialize it */ + if( NEVER(!pStr) ) return; + #endif + z = pStr->zBuf; +- for(i=1; (c = z[i])!=',' || inStr || nNest; i++){ +- if( i>=pStr->nUsed ){ +- pStr->nUsed = 1; +- return; +- } ++ for(i=1; inUsed && ((c = z[i])!=',' || inStr || nNest); i++){ + if( c=='"' ){ + inStr = !inStr; + }else if( c=='\\' ){ +@@ -187398,8 +212669,13 @@ static void jsonGroupInverse( + if( c=='}' || c==']' ) nNest--; + } + } +- pStr->nUsed -= i; +- memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); ++ if( inUsed ){ ++ pStr->nUsed -= i; ++ memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); ++ z[pStr->nUsed] = 0; ++ }else{ ++ pStr->nUsed = 1; ++ } + } + #else + # define jsonGroupInverse 0 +@@ -187419,38 +212695,52 @@ static void jsonObjectStep( + JsonString *pStr; + const char *z; + u32 n; +- UNUSED_PARAM(argc); ++ UNUSED_PARAMETER(argc); + pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); + if( pStr ){ ++ z = (const char*)sqlite3_value_text(argv[0]); ++ n = sqlite3Strlen30(z); + if( pStr->zBuf==0 ){ +- jsonInit(pStr, ctx); ++ jsonStringInit(pStr, ctx); + jsonAppendChar(pStr, '{'); +- }else if( pStr->nUsed>1 ){ ++ }else if( pStr->nUsed>1 && z!=0 ){ + jsonAppendChar(pStr, ','); +- pStr->pCtx = ctx; + } +- z = (const char*)sqlite3_value_text(argv[0]); +- n = (u32)sqlite3_value_bytes(argv[0]); +- jsonAppendString(pStr, z, n); +- jsonAppendChar(pStr, ':'); +- jsonAppendValue(pStr, argv[1]); ++ pStr->pCtx = ctx; ++ if( z!=0 ){ ++ jsonAppendString(pStr, z, n); ++ jsonAppendChar(pStr, ':'); ++ jsonAppendSqlValue(pStr, argv[1]); ++ } + } + } + static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ + JsonString *pStr; + pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); + if( pStr ){ ++ int flags; + jsonAppendChar(pStr, '}'); +- if( pStr->bErr ){ +- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); +- assert( pStr->bStatic ); ++ pStr->pCtx = ctx; ++ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); ++ if( pStr->eErr ){ ++ jsonReturnString(pStr, 0, 0); ++ return; ++ }else if( flags & JSON_BLOB ){ ++ jsonReturnStringAsBlob(pStr); ++ if( isFinal ){ ++ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); ++ }else{ ++ jsonStringTrimOneChar(pStr); ++ } ++ return; + }else if( isFinal ){ + sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, +- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); ++ pStr->bStatic ? SQLITE_TRANSIENT : ++ sqlite3RCStrUnref); + pStr->bStatic = 1; + }else{ + sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); +- pStr->nUsed--; ++ jsonStringTrimOneChar(pStr); + } + }else{ + sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); +@@ -187470,19 +212760,37 @@ static void jsonObjectFinal(sqlite3_context *ctx){ + /**************************************************************************** + ** The json_each virtual table + ****************************************************************************/ ++typedef struct JsonParent JsonParent; ++struct JsonParent { ++ u32 iHead; /* Start of object or array */ ++ u32 iValue; /* Start of the value */ ++ u32 iEnd; /* First byte past the end */ ++ u32 nPath; /* Length of path */ ++ i64 iKey; /* Key for JSONB_ARRAY */ ++}; ++ + typedef struct JsonEachCursor JsonEachCursor; + struct JsonEachCursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + u32 iRowid; /* The rowid */ +- u32 iBegin; /* The first node of the scan */ +- u32 i; /* Index in sParse.aNode[] of current row */ ++ u32 i; /* Index in sParse.aBlob[] of current row */ + u32 iEnd; /* EOF when i equals or exceeds this value */ +- u8 eType; /* Type of top-level element */ ++ u32 nRoot; /* Size of the root path in bytes */ ++ u8 eType; /* Type of the container for element i */ + u8 bRecursive; /* True for json_tree(). False for json_each() */ +- char *zJson; /* Input JSON */ +- char *zRoot; /* Path by which to filter zJson */ ++ u32 nParent; /* Current nesting depth */ ++ u32 nParentAlloc; /* Space allocated for aParent[] */ ++ JsonParent *aParent; /* Parent elements of i */ ++ sqlite3 *db; /* Database connection */ ++ JsonString path; /* Current path */ + JsonParse sParse; /* Parse of the input JSON */ + }; ++typedef struct JsonEachConnection JsonEachConnection; ++struct JsonEachConnection { ++ sqlite3_vtab base; /* Base class - must be first */ ++ sqlite3 *db; /* Database connection */ ++}; ++ + + /* Constructor for the json_each virtual table */ + static int jsonEachConnect( +@@ -187492,7 +212800,7 @@ static int jsonEachConnect( + sqlite3_vtab **ppVtab, + char **pzErr + ){ +- sqlite3_vtab *pNew; ++ JsonEachConnection *pNew; + int rc; + + /* Column numbers */ +@@ -187510,36 +212818,40 @@ static int jsonEachConnect( + #define JEACH_JSON 8 + #define JEACH_ROOT 9 + +- UNUSED_PARAM(pzErr); +- UNUSED_PARAM(argv); +- UNUSED_PARAM(argc); +- UNUSED_PARAM(pAux); ++ UNUSED_PARAMETER(pzErr); ++ UNUSED_PARAMETER(argv); ++ UNUSED_PARAMETER(argc); ++ UNUSED_PARAMETER(pAux); + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," + "json HIDDEN,root HIDDEN)"); + if( rc==SQLITE_OK ){ +- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); ++ pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew)); ++ *ppVtab = (sqlite3_vtab*)pNew; + if( pNew==0 ) return SQLITE_NOMEM; +- memset(pNew, 0, sizeof(*pNew)); + sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); ++ pNew->db = db; + } + return rc; + } + + /* destructor for json_each virtual table */ + static int jsonEachDisconnect(sqlite3_vtab *pVtab){ +- sqlite3_free(pVtab); ++ JsonEachConnection *p = (JsonEachConnection*)pVtab; ++ sqlite3DbFree(p->db, pVtab); + return SQLITE_OK; + } + + /* constructor for a JsonEachCursor object for json_each(). */ + static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ ++ JsonEachConnection *pVtab = (JsonEachConnection*)p; + JsonEachCursor *pCur; + +- UNUSED_PARAM(p); +- pCur = sqlite3_malloc( sizeof(*pCur) ); ++ UNUSED_PARAMETER(p); ++ pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur)); + if( pCur==0 ) return SQLITE_NOMEM; +- memset(pCur, 0, sizeof(*pCur)); ++ pCur->db = pVtab->db; ++ jsonStringZero(&pCur->path); + *ppCursor = &pCur->base; + return SQLITE_OK; + } +@@ -187557,22 +212869,24 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + /* Reset a JsonEachCursor back to its original state. Free any memory + ** held. */ + static void jsonEachCursorReset(JsonEachCursor *p){ +- sqlite3_free(p->zJson); +- sqlite3_free(p->zRoot); + jsonParseReset(&p->sParse); ++ jsonStringReset(&p->path); ++ sqlite3DbFree(p->db, p->aParent); + p->iRowid = 0; + p->i = 0; ++ p->aParent = 0; ++ p->nParent = 0; ++ p->nParentAlloc = 0; + p->iEnd = 0; + p->eType = 0; +- p->zJson = 0; +- p->zRoot = 0; + } + + /* Destructor for a jsonEachCursor object */ + static int jsonEachClose(sqlite3_vtab_cursor *cur){ + JsonEachCursor *p = (JsonEachCursor*)cur; + jsonEachCursorReset(p); +- sqlite3_free(cur); ++ ++ sqlite3DbFree(p->db, cur); + return SQLITE_OK; + } + +@@ -187583,167 +212897,233 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){ + return p->i >= p->iEnd; + } + ++/* ++** If the cursor is currently pointing at the label of a object entry, ++** then return the index of the value. For all other cases, return the ++** current pointer position, which is the value. ++*/ ++static int jsonSkipLabel(JsonEachCursor *p){ ++ if( p->eType==JSONB_OBJECT ){ ++ u32 sz = 0; ++ u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz); ++ return p->i + n + sz; ++ }else{ ++ return p->i; ++ } ++} ++ ++/* ++** Append the path name for the current element. ++*/ ++static void jsonAppendPathName(JsonEachCursor *p){ ++ assert( p->nParent>0 ); ++ assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT ); ++ if( p->eType==JSONB_ARRAY ){ ++ jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey); ++ }else{ ++ u32 n, sz = 0, k, i; ++ const char *z; ++ int needQuote = 0; ++ n = jsonbPayloadSize(&p->sParse, p->i, &sz); ++ k = p->i + n; ++ z = (const char*)&p->sParse.aBlob[k]; ++ if( sz==0 || !sqlite3Isalpha(z[0]) ){ ++ needQuote = 1; ++ }else{ ++ for(i=0; ipath,".\"%.*s\"", sz, z); ++ }else{ ++ jsonPrintf(sz+2,&p->path,".%.*s", sz, z); ++ } ++ } ++} ++ + /* Advance the cursor to the next element for json_tree() */ + static int jsonEachNext(sqlite3_vtab_cursor *cur){ + JsonEachCursor *p = (JsonEachCursor*)cur; ++ int rc = SQLITE_OK; + if( p->bRecursive ){ +- if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++; +- p->i++; +- p->iRowid++; +- if( p->iiEnd ){ +- u32 iUp = p->sParse.aUp[p->i]; +- JsonNode *pUp = &p->sParse.aNode[iUp]; +- p->eType = pUp->eType; +- if( pUp->eType==JSON_ARRAY ){ +- if( iUp==p->i-1 ){ +- pUp->u.iKey = 0; +- }else{ +- pUp->u.iKey++; +- } ++ u8 x; ++ u8 levelChange = 0; ++ u32 n, sz = 0; ++ u32 i = jsonSkipLabel(p); ++ x = p->sParse.aBlob[i] & 0x0f; ++ n = jsonbPayloadSize(&p->sParse, i, &sz); ++ if( x==JSONB_OBJECT || x==JSONB_ARRAY ){ ++ JsonParent *pParent; ++ if( p->nParent>=p->nParentAlloc ){ ++ JsonParent *pNew; ++ u64 nNew; ++ nNew = p->nParentAlloc*2 + 3; ++ pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew); ++ if( pNew==0 ) return SQLITE_NOMEM; ++ p->nParentAlloc = (u32)nNew; ++ p->aParent = pNew; ++ } ++ levelChange = 1; ++ pParent = &p->aParent[p->nParent]; ++ pParent->iHead = p->i; ++ pParent->iValue = i; ++ pParent->iEnd = i + n + sz; ++ pParent->iKey = -1; ++ pParent->nPath = (u32)p->path.nUsed; ++ if( p->eType && p->nParent ){ ++ jsonAppendPathName(p); ++ if( p->path.eErr ) rc = SQLITE_NOMEM; ++ } ++ p->nParent++; ++ p->i = i + n; ++ }else{ ++ p->i = i + n + sz; ++ } ++ while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ ++ p->nParent--; ++ p->path.nUsed = p->aParent[p->nParent].nPath; ++ levelChange = 1; ++ } ++ if( levelChange ){ ++ if( p->nParent>0 ){ ++ JsonParent *pParent = &p->aParent[p->nParent-1]; ++ u32 iVal = pParent->iValue; ++ p->eType = p->sParse.aBlob[iVal] & 0x0f; ++ }else{ ++ p->eType = 0; + } + } + }else{ +- switch( p->eType ){ +- case JSON_ARRAY: { +- p->i += jsonNodeSize(&p->sParse.aNode[p->i]); +- p->iRowid++; +- break; +- } +- case JSON_OBJECT: { +- p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]); +- p->iRowid++; +- break; +- } +- default: { +- p->i = p->iEnd; +- break; +- } +- } ++ u32 n, sz = 0; ++ u32 i = jsonSkipLabel(p); ++ n = jsonbPayloadSize(&p->sParse, i, &sz); ++ p->i = i + n + sz; + } +- return SQLITE_OK; ++ if( p->eType==JSONB_ARRAY && p->nParent ){ ++ p->aParent[p->nParent-1].iKey++; ++ } ++ p->iRowid++; ++ return rc; + } + +-/* Append the name of the path for element i to pStr ++/* Length of the path for rowid==0 in bRecursive mode. + */ +-static void jsonEachComputePath( +- JsonEachCursor *p, /* The cursor */ +- JsonString *pStr, /* Write the path here */ +- u32 i /* Path to this element */ +-){ +- JsonNode *pNode, *pUp; +- u32 iUp; +- if( i==0 ){ +- jsonAppendChar(pStr, '$'); +- return; +- } +- iUp = p->sParse.aUp[i]; +- jsonEachComputePath(p, pStr, iUp); +- pNode = &p->sParse.aNode[i]; +- pUp = &p->sParse.aNode[iUp]; +- if( pUp->eType==JSON_ARRAY ){ +- jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); +- }else{ +- assert( pUp->eType==JSON_OBJECT ); +- if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; +- assert( pNode->eType==JSON_STRING ); +- assert( pNode->jnFlags & JNODE_LABEL ); +- jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1); ++static int jsonEachPathLength(JsonEachCursor *p){ ++ u32 n = p->path.nUsed; ++ char *z = p->path.zBuf; ++ if( p->iRowid==0 && p->bRecursive && n>=2 ){ ++ while( n>1 ){ ++ n--; ++ if( z[n]=='[' || z[n]=='.' ){ ++ u32 x, sz = 0; ++ char cSaved = z[n]; ++ z[n] = 0; ++ assert( p->sParse.eEdit==0 ); ++ x = jsonLookupStep(&p->sParse, 0, z+1, 0); ++ z[n] = cSaved; ++ if( JSON_LOOKUP_ISERROR(x) ) continue; ++ if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break; ++ } ++ } + } ++ return n; + } + + /* Return the value of a column */ + static int jsonEachColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ +- int i /* Which column to return */ ++ int iColumn /* Which column to return */ + ){ + JsonEachCursor *p = (JsonEachCursor*)cur; +- JsonNode *pThis = &p->sParse.aNode[p->i]; +- switch( i ){ ++ switch( iColumn ){ + case JEACH_KEY: { +- if( p->i==0 ) break; +- if( p->eType==JSON_OBJECT ){ +- jsonReturn(pThis, ctx, 0); +- }else if( p->eType==JSON_ARRAY ){ +- u32 iKey; +- if( p->bRecursive ){ +- if( p->iRowid==0 ) break; +- iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; ++ if( p->nParent==0 ){ ++ u32 n, j; ++ if( p->nRoot==1 ) break; ++ j = jsonEachPathLength(p); ++ n = p->nRoot - j; ++ if( n==0 ){ ++ break; ++ }else if( p->path.zBuf[j]=='[' ){ ++ i64 x; ++ sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8); ++ sqlite3_result_int64(ctx, x); ++ }else if( p->path.zBuf[j+1]=='"' ){ ++ sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT); + }else{ +- iKey = p->iRowid; ++ sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT); + } +- sqlite3_result_int64(ctx, (sqlite3_int64)iKey); ++ break; ++ } ++ if( p->eType==JSONB_OBJECT ){ ++ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); ++ }else{ ++ assert( p->eType==JSONB_ARRAY ); ++ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); + } + break; + } + case JEACH_VALUE: { +- if( pThis->jnFlags & JNODE_LABEL ) pThis++; +- jsonReturn(pThis, ctx, 0); ++ u32 i = jsonSkipLabel(p); ++ jsonReturnFromBlob(&p->sParse, i, ctx, 1); ++ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){ ++ sqlite3_result_subtype(ctx, JSON_SUBTYPE); ++ } + break; + } + case JEACH_TYPE: { +- if( pThis->jnFlags & JNODE_LABEL ) pThis++; +- sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC); ++ u32 i = jsonSkipLabel(p); ++ u8 eType = p->sParse.aBlob[i] & 0x0f; ++ sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); + break; + } + case JEACH_ATOM: { +- if( pThis->jnFlags & JNODE_LABEL ) pThis++; +- if( pThis->eType>=JSON_ARRAY ) break; +- jsonReturn(pThis, ctx, 0); ++ u32 i = jsonSkipLabel(p); ++ if( (p->sParse.aBlob[i] & 0x0f)sParse, i, ctx, 1); ++ } + break; + } + case JEACH_ID: { +- sqlite3_result_int64(ctx, +- (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0)); ++ sqlite3_result_int64(ctx, (sqlite3_int64)p->i); + break; + } + case JEACH_PARENT: { +- if( p->i>p->iBegin && p->bRecursive ){ +- sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]); ++ if( p->nParent>0 && p->bRecursive ){ ++ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); + } + break; + } + case JEACH_FULLKEY: { +- JsonString x; +- jsonInit(&x, ctx); +- if( p->bRecursive ){ +- jsonEachComputePath(p, &x, p->i); +- }else{ +- if( p->zRoot ){ +- jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); +- }else{ +- jsonAppendChar(&x, '$'); +- } +- if( p->eType==JSON_ARRAY ){ +- jsonPrintf(30, &x, "[%d]", p->iRowid); +- }else if( p->eType==JSON_OBJECT ){ +- jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1); +- } +- } +- jsonResult(&x); ++ u64 nBase = p->path.nUsed; ++ if( p->nParent ) jsonAppendPathName(p); ++ sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, ++ SQLITE_TRANSIENT, SQLITE_UTF8); ++ p->path.nUsed = nBase; + break; + } + case JEACH_PATH: { +- if( p->bRecursive ){ +- JsonString x; +- jsonInit(&x, ctx); +- jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); +- jsonResult(&x); +- break; +- } +- /* For json_each() path and root are the same so fall through +- ** into the root case */ +- /* no break */ deliberate_fall_through ++ u32 n = jsonEachPathLength(p); ++ sqlite3_result_text64(ctx, p->path.zBuf, n, ++ SQLITE_TRANSIENT, SQLITE_UTF8); ++ break; + } + default: { +- const char *zRoot = p->zRoot; +- if( zRoot==0 ) zRoot = "$"; +- sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); ++ sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC); + break; + } + case JEACH_JSON: { +- assert( i==JEACH_JSON ); +- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); ++ if( p->sParse.zJson==0 ){ ++ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, ++ SQLITE_TRANSIENT); ++ }else{ ++ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT); ++ } + break; + } + } +@@ -187775,7 +213155,7 @@ static int jsonEachBestIndex( + /* This implementation assumes that JSON and ROOT are the last two + ** columns in the table */ + assert( JEACH_ROOT == JEACH_JSON+1 ); +- UNUSED_PARAM(tab); ++ UNUSED_PARAMETER(tab); + aIdx[0] = aIdx[1] = -1; + pConstraint = pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pConstraint++){ +@@ -187784,6 +213164,7 @@ static int jsonEachBestIndex( + if( pConstraint->iColumn < JEACH_JSON ) continue; + iCol = pConstraint->iColumn - JEACH_JSON; + assert( iCol==0 || iCol==1 ); ++ testcase( iCol==0 ); + iMask = 1 << iCol; + if( pConstraint->usable==0 ){ + unusableMask |= iMask; +@@ -187792,6 +213173,13 @@ static int jsonEachBestIndex( + idxMask |= iMask; + } + } ++ if( pIdxInfo->nOrderBy>0 ++ && pIdxInfo->aOrderBy[0].iColumn<0 ++ && pIdxInfo->aOrderBy[0].desc==0 ++ ){ ++ pIdxInfo->orderByConsumed = 1; ++ } ++ + if( (unusableMask & ~idxMask)!=0 ){ + /* If there are any unusable constraints on JSON or ROOT, then reject + ** this entire plan */ +@@ -187826,76 +213214,96 @@ static int jsonEachFilter( + int argc, sqlite3_value **argv + ){ + JsonEachCursor *p = (JsonEachCursor*)cur; +- const char *z; + const char *zRoot = 0; +- sqlite3_int64 n; ++ u32 i, n, sz; + +- UNUSED_PARAM(idxStr); +- UNUSED_PARAM(argc); ++ UNUSED_PARAMETER(idxStr); ++ UNUSED_PARAMETER(argc); + jsonEachCursorReset(p); + if( idxNum==0 ) return SQLITE_OK; +- z = (const char*)sqlite3_value_text(argv[0]); +- if( z==0 ) return SQLITE_OK; +- n = sqlite3_value_bytes(argv[0]); +- p->zJson = sqlite3_malloc64( n+1 ); +- if( p->zJson==0 ) return SQLITE_NOMEM; +- memcpy(p->zJson, z, (size_t)n+1); +- if( jsonParse(&p->sParse, 0, p->zJson) ){ +- int rc = SQLITE_NOMEM; +- if( p->sParse.oom==0 ){ +- sqlite3_free(cur->pVtab->zErrMsg); +- cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); +- if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR; +- } +- jsonEachCursorReset(p); +- return rc; +- }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){ +- jsonEachCursorReset(p); +- return SQLITE_NOMEM; ++ memset(&p->sParse, 0, sizeof(p->sParse)); ++ p->sParse.nJPRef = 1; ++ p->sParse.db = p->db; ++ if( jsonArgIsJsonb(argv[0], &p->sParse) ){ ++ /* We have JSONB */ + }else{ +- JsonNode *pNode = 0; +- if( idxNum==3 ){ +- const char *zErr = 0; +- zRoot = (const char*)sqlite3_value_text(argv[1]); +- if( zRoot==0 ) return SQLITE_OK; +- n = sqlite3_value_bytes(argv[1]); +- p->zRoot = sqlite3_malloc64( n+1 ); +- if( p->zRoot==0 ) return SQLITE_NOMEM; +- memcpy(p->zRoot, zRoot, (size_t)n+1); +- if( zRoot[0]!='$' ){ +- zErr = zRoot; +- }else{ +- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr); ++ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); ++ p->sParse.nJson = sqlite3_value_bytes(argv[0]); ++ if( p->sParse.zJson==0 ){ ++ p->i = p->iEnd = 0; ++ return SQLITE_OK; ++ } ++ if( jsonConvertTextToBlob(&p->sParse, 0) ){ ++ if( p->sParse.oom ){ ++ return SQLITE_NOMEM; + } +- if( zErr ){ ++ goto json_each_malformed_input; ++ } ++ } ++ if( idxNum==3 ){ ++ zRoot = (const char*)sqlite3_value_text(argv[1]); ++ if( zRoot==0 ) return SQLITE_OK; ++ if( zRoot[0]!='$' ){ ++ sqlite3_free(cur->pVtab->zErrMsg); ++ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); ++ jsonEachCursorReset(p); ++ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; ++ } ++ p->nRoot = sqlite3Strlen30(zRoot); ++ if( zRoot[1]==0 ){ ++ i = p->i = 0; ++ p->eType = 0; ++ }else{ ++ i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0); ++ if( JSON_LOOKUP_ISERROR(i) ){ ++ if( i==JSON_LOOKUP_NOTFOUND ){ ++ p->i = 0; ++ p->eType = 0; ++ p->iEnd = 0; ++ return SQLITE_OK; ++ } + sqlite3_free(cur->pVtab->zErrMsg); +- cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr); ++ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; +- }else if( pNode==0 ){ +- return SQLITE_OK; + } +- }else{ +- pNode = p->sParse.aNode; +- } +- p->iBegin = p->i = (int)(pNode - p->sParse.aNode); +- p->eType = pNode->eType; +- if( p->eType>=JSON_ARRAY ){ +- pNode->u.iKey = 0; +- p->iEnd = p->i + pNode->n + 1; +- if( p->bRecursive ){ +- p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType; +- if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){ +- p->i--; +- } ++ if( p->sParse.iLabel ){ ++ p->i = p->sParse.iLabel; ++ p->eType = JSONB_OBJECT; + }else{ +- p->i++; ++ p->i = i; ++ p->eType = JSONB_ARRAY; + } +- }else{ +- p->iEnd = p->i+1; + } ++ jsonAppendRaw(&p->path, zRoot, p->nRoot); ++ }else{ ++ i = p->i = 0; ++ p->eType = 0; ++ p->nRoot = 1; ++ jsonAppendRaw(&p->path, "$", 1); ++ } ++ p->nParent = 0; ++ n = jsonbPayloadSize(&p->sParse, i, &sz); ++ p->iEnd = i+n+sz; ++ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ ++ p->i = i + n; ++ p->eType = p->sParse.aBlob[i] & 0x0f; ++ p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent)); ++ if( p->aParent==0 ) return SQLITE_NOMEM; ++ p->nParent = 1; ++ p->nParentAlloc = 1; ++ p->aParent[0].iKey = 0; ++ p->aParent[0].iEnd = p->iEnd; ++ p->aParent[0].iHead = p->i; ++ p->aParent[0].iValue = i; + } + return SQLITE_OK; ++ ++json_each_malformed_input: ++ sqlite3_free(cur->pVtab->zErrMsg); ++ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); ++ jsonEachCursorReset(p); ++ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + } + + /* The methods of the json_each virtual table */ +@@ -187923,7 +213331,8 @@ static sqlite3_module jsonEachModule = { + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ +- 0 /* xShadowName */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + + /* The methods of the json_tree virtual table. */ +@@ -187951,111 +213360,98 @@ static sqlite3_module jsonTreeModule = { + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ +- 0 /* xShadowName */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + #endif /* SQLITE_OMIT_VIRTUALTABLE */ +- +-/**************************************************************************** +-** The following routines are the only publically visible identifiers in this +-** file. Call the following routines in order to register the various SQL +-** functions and the virtual table implemented by this file. +-****************************************************************************/ +- +-SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){ +- int rc = SQLITE_OK; +- unsigned int i; +- static const struct { +- const char *zName; +- int nArg; +- int flag; +- void (*xFunc)(sqlite3_context*,int,sqlite3_value**); +- } aFunc[] = { +- { "json", 1, 0, jsonRemoveFunc }, +- { "json_array", -1, 0, jsonArrayFunc }, +- { "json_array_length", 1, 0, jsonArrayLengthFunc }, +- { "json_array_length", 2, 0, jsonArrayLengthFunc }, +- { "json_extract", -1, 0, jsonExtractFunc }, +- { "json_insert", -1, 0, jsonSetFunc }, +- { "json_object", -1, 0, jsonObjectFunc }, +- { "json_patch", 2, 0, jsonPatchFunc }, +- { "json_quote", 1, 0, jsonQuoteFunc }, +- { "json_remove", -1, 0, jsonRemoveFunc }, +- { "json_replace", -1, 0, jsonReplaceFunc }, +- { "json_set", -1, 1, jsonSetFunc }, +- { "json_type", 1, 0, jsonTypeFunc }, +- { "json_type", 2, 0, jsonTypeFunc }, +- { "json_valid", 1, 0, jsonValidFunc }, +- ++#endif /* !defined(SQLITE_OMIT_JSON) */ ++ ++/* ++** Register JSON functions. ++*/ ++SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){ ++#ifndef SQLITE_OMIT_JSON ++ static FuncDef aJsonFunc[] = { ++ /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */ ++ /* | | */ ++ /* Uses cache ------, | | ,---- Returns JSONB */ ++ /* | | | | */ ++ /* Number of arguments ---, | | | | ,--- Flags */ ++ /* | | | | | | */ ++ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), ++ JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), ++ JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), ++ JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), ++ JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), ++ JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), ++ JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), ++ JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc), ++ JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc), ++ JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc), ++ JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc), ++ JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc), ++ JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc), ++ JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc), ++ JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc), ++ JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc), ++ JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc), ++ JFUNCTION(json_pretty, 1,1,0, 0,0,0, jsonPrettyFunc), ++ JFUNCTION(json_pretty, 2,1,0, 0,0,0, jsonPrettyFunc), ++ JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc), ++ JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc), ++ JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc), ++ JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc), ++ JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc), ++ JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc), ++ JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc), ++ JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc), ++ JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc), ++ JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc), ++ JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc), + #if SQLITE_DEBUG +- /* DEBUG and TESTING functions */ +- { "json_parse", 1, 0, jsonParseFunc }, +- { "json_test1", 1, 0, jsonTest1Func }, +-#endif ++ JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc), ++#endif ++ WAGGREGATE(json_group_array, 1, 0, 0, ++ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, ++ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| ++ SQLITE_DETERMINISTIC), ++ WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0, ++ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, ++ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), ++ WAGGREGATE(json_group_object, 2, 0, 0, ++ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, ++ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), ++ WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0, ++ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, ++ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| ++ SQLITE_DETERMINISTIC) + }; ++ sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); ++#endif ++} ++ ++#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) ++/* ++** Register the JSON table-valued functions ++*/ ++SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3 *db){ ++ int rc = SQLITE_OK; + static const struct { +- const char *zName; +- int nArg; +- void (*xStep)(sqlite3_context*,int,sqlite3_value**); +- void (*xFinal)(sqlite3_context*); +- void (*xValue)(sqlite3_context*); +- } aAgg[] = { +- { "json_group_array", 1, +- jsonArrayStep, jsonArrayFinal, jsonArrayValue }, +- { "json_group_object", 2, +- jsonObjectStep, jsonObjectFinal, jsonObjectValue }, +- }; +-#ifndef SQLITE_OMIT_VIRTUALTABLE +- static const struct { +- const char *zName; +- sqlite3_module *pModule; ++ const char *zName; ++ sqlite3_module *pModule; + } aMod[] = { + { "json_each", &jsonEachModule }, + { "json_tree", &jsonTreeModule }, + }; +-#endif +- static const int enc = +- SQLITE_UTF8 | +- SQLITE_DETERMINISTIC | +- SQLITE_INNOCUOUS; +- for(i=0; i */ ++ ++/* ++** If building separately, we will need some setup that is normally ++** found in sqliteInt.h ++*/ ++#if !defined(SQLITE_AMALGAMATION) + #include "sqlite3rtree.h" + typedef sqlite3_int64 i64; + typedef sqlite3_uint64 u64; +@@ -188136,6 +213538,32 @@ typedef unsigned int u32; + #if defined(NDEBUG) && defined(SQLITE_DEBUG) + # undef NDEBUG + #endif ++#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) ++# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 ++#endif ++#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) ++# define ALWAYS(X) (1) ++# define NEVER(X) (0) ++#elif !defined(NDEBUG) ++# define ALWAYS(X) ((X)?1:(assert(0),0)) ++# define NEVER(X) ((X)?(assert(0),1):0) ++#else ++# define ALWAYS(X) (X) ++# define NEVER(X) (X) ++#endif ++#ifndef offsetof ++#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) ++#endif ++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ++# define FLEXARRAY ++#else ++# define FLEXARRAY 1 ++#endif ++#endif /* !defined(SQLITE_AMALGAMATION) */ ++ ++/* Macro to check for 4-byte alignment. Only used inside of assert() */ ++#ifdef SQLITE_DEBUG ++# define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) + #endif + + /* #include */ +@@ -188194,13 +213622,16 @@ struct Rtree { + u8 nBytesPerCell; /* Bytes consumed per cell */ + u8 inWrTrans; /* True if inside write transaction */ + u8 nAux; /* # of auxiliary columns in %_rowid */ ++#ifdef SQLITE_ENABLE_GEOPOLY + u8 nAuxNotNull; /* Number of initial not-null aux columns */ ++#endif + #ifdef SQLITE_DEBUG + u8 bCorrupt; /* Shadow table corruption detected */ + #endif + int iDepth; /* Current depth of the r-tree structure */ + char *zDb; /* Name of database containing r-tree table */ + char *zName; /* Name of r-tree table */ ++ char *zNodeName; /* Name of the %_node table */ + u32 nBusy; /* Current number of users of this structure */ + i64 nRowEst; /* Estimated number of rows in this table */ + u32 nCursor; /* Number of open cursors */ +@@ -188213,7 +213644,6 @@ struct Rtree { + ** headed by the node (leaf nodes have RtreeNode.iNode==0). + */ + RtreeNode *pDeleted; +- int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */ + + /* Blob I/O on xxx_node */ + sqlite3_blob *pNodeBlob; +@@ -188449,9 +213879,13 @@ struct RtreeMatchArg { + RtreeGeomCallback cb; /* Info about the callback functions */ + int nParam; /* Number of parameters to the SQL function */ + sqlite3_value **apSqlParam; /* Original SQL parameter values */ +- RtreeDValue aParam[1]; /* Values for parameters to the SQL function */ ++ RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */ + }; + ++/* Size of an RtreeMatchArg object with N parameters */ ++#define SZ_RTREEMATCHARG(N) \ ++ (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue)) ++ + #ifndef MAX + # define MAX(x,y) ((x) < (y) ? (y) : (x)) + #endif +@@ -188476,7 +213910,12 @@ struct RtreeMatchArg { + ** it is not, make it a no-op. + */ + #ifndef SQLITE_AMALGAMATION +-# define testcase(X) ++# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) ++ unsigned int sqlite3RtreeTestcase = 0; ++# define testcase(X) if( X ){ sqlite3RtreeTestcase += __LINE__; } ++# else ++# define testcase(X) ++# endif + #endif + + /* +@@ -188505,17 +213944,23 @@ struct RtreeMatchArg { + ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined + ** at run-time. + */ +-#ifndef SQLITE_BYTEORDER +-#if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ +- defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ +- defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ +- defined(__arm__) +-# define SQLITE_BYTEORDER 1234 +-#elif defined(sparc) || defined(__ppc__) +-# define SQLITE_BYTEORDER 4321 +-#else +-# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */ +-#endif ++#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */ ++# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ ++# define SQLITE_BYTEORDER 4321 ++# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ ++# define SQLITE_BYTEORDER 1234 ++# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 ++# define SQLITE_BYTEORDER 4321 ++# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ ++ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ ++ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ ++ defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) ++# define SQLITE_BYTEORDER 1234 ++# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) ++# define SQLITE_BYTEORDER 4321 ++# else ++# define SQLITE_BYTEORDER 0 ++# endif + #endif + + +@@ -188536,7 +213981,7 @@ static int readInt16(u8 *p){ + return (p[0]<<8) + p[1]; + } + static void readCoord(u8 *p, RtreeCoord *pCoord){ +- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ ++ assert( FOUR_BYTE_ALIGNED(p) ); + #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 + pCoord->u = _byteswap_ulong(*(u32*)p); + #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 +@@ -188590,7 +214035,7 @@ static void writeInt16(u8 *p, int i){ + } + static int writeCoord(u8 *p, RtreeCoord *pCoord){ + u32 i; +- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ ++ assert( FOUR_BYTE_ALIGNED(p) ); + assert( sizeof(RtreeCoord)==4 ); + assert( sizeof(u32)==4 ); + #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 +@@ -188718,23 +214163,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ + ** Clear the Rtree.pNodeBlob object + */ + static void nodeBlobReset(Rtree *pRtree){ +- if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){ +- sqlite3_blob *pBlob = pRtree->pNodeBlob; +- pRtree->pNodeBlob = 0; +- sqlite3_blob_close(pBlob); +- } +-} +- +-/* +-** Check to see if pNode is the same as pParent or any of the parents +-** of pParent. +-*/ +-static int nodeInParentChain(const RtreeNode *pNode, const RtreeNode *pParent){ +- do{ +- if( pNode==pParent ) return 1; +- pParent = pParent->pParent; +- }while( pParent ); +- return 0; ++ sqlite3_blob *pBlob = pRtree->pNodeBlob; ++ pRtree->pNodeBlob = 0; ++ sqlite3_blob_close(pBlob); + } + + /* +@@ -188753,14 +214184,7 @@ static int nodeAcquire( + ** increase its reference count and return it. + */ + if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ +- if( pParent && !pNode->pParent ){ +- if( nodeInParentChain(pNode, pParent) ){ +- RTREE_IS_CORRUPT(pRtree); +- return SQLITE_CORRUPT_VTAB; +- } +- pParent->nRef++; +- pNode->pParent = pParent; +- }else if( pParent && pNode->pParent && pParent!=pNode->pParent ){ ++ if( pParent && ALWAYS(pParent!=pNode->pParent) ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } +@@ -188780,14 +214204,11 @@ static int nodeAcquire( + } + } + if( pRtree->pNodeBlob==0 ){ +- char *zTab = sqlite3_mprintf("%s_node", pRtree->zName); +- if( zTab==0 ) return SQLITE_NOMEM; +- rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0, ++ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName, ++ "data", iNode, 0, + &pRtree->pNodeBlob); +- sqlite3_free(zTab); + } + if( rc ){ +- nodeBlobReset(pRtree); + *ppNode = 0; + /* If unable to open an sqlite3_blob on the desired row, that can only + ** be because the shadow tables hold erroneous data. */ +@@ -188818,7 +214239,7 @@ static int nodeAcquire( + ** are the leaves, and so on. If the depth as specified on the root node + ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. + */ +- if( pNode && iNode==1 ){ ++ if( rc==SQLITE_OK && pNode && iNode==1 ){ + pRtree->iDepth = readInt16(pNode->zData); + if( pRtree->iDepth>RTREE_MAX_DEPTH ){ + rc = SQLITE_CORRUPT_VTAB; +@@ -188847,6 +214268,7 @@ static int nodeAcquire( + } + *ppNode = pNode; + }else{ ++ nodeBlobReset(pRtree); + if( pNode ){ + pRtree->nNodeRef--; + sqlite3_free(pNode); +@@ -188991,6 +214413,7 @@ static void nodeGetCoord( + int iCoord, /* Which coordinate to extract */ + RtreeCoord *pCoord /* OUT: Space to write result to */ + ){ ++ assert( iCellzData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); + } + +@@ -189180,7 +214603,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){ + sqlite3_finalize(pCsr->pReadAux); + sqlite3_free(pCsr); + pRtree->nCursor--; +- nodeBlobReset(pRtree); ++ if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){ ++ nodeBlobReset(pRtree); ++ } + return SQLITE_OK; + } + +@@ -189337,24 +214762,33 @@ static void rtreeNonleafConstraint( + assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE + || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE + || p->op==RTREE_FALSE ); +- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ ++ assert( FOUR_BYTE_ALIGNED(pCellData) ); + switch( p->op ){ + case RTREE_TRUE: return; /* Always satisfied */ + case RTREE_FALSE: break; /* Never satisfied */ ++ case RTREE_EQ: ++ RTREE_DECODE_COORD(eInt, pCellData, val); ++ /* val now holds the lower bound of the coordinate pair */ ++ if( p->u.rValue>=val ){ ++ pCellData += 4; ++ RTREE_DECODE_COORD(eInt, pCellData, val); ++ /* val now holds the upper bound of the coordinate pair */ ++ if( p->u.rValue<=val ) return; ++ } ++ break; + case RTREE_LE: + case RTREE_LT: +- case RTREE_EQ: + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the lower bound of the coordinate pair */ + if( p->u.rValue>=val ) return; +- if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */ +- /* Fall through for the RTREE_EQ case */ ++ break; + +- default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */ ++ default: + pCellData += 4; + RTREE_DECODE_COORD(eInt, pCellData, val); + /* val now holds the upper bound of the coordinate pair */ + if( p->u.rValue<=val ) return; ++ break; + } + *peWithin = NOT_WITHIN; + } +@@ -189381,7 +214815,7 @@ static void rtreeLeafConstraint( + || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE + || p->op==RTREE_FALSE ); + pCellData += 8 + p->iCoord*4; +- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ ++ assert( FOUR_BYTE_ALIGNED(pCellData) ); + RTREE_DECODE_COORD(eInt, pCellData, xN); + switch( p->op ){ + case RTREE_TRUE: return; /* Always satisfied */ +@@ -189424,11 +214858,12 @@ static int nodeRowidIndex( + */ + static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ + RtreeNode *pParent = pNode->pParent; +- if( pParent ){ ++ if( ALWAYS(pParent) ){ + return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); ++ }else{ ++ *piIndex = -1; ++ return SQLITE_OK; + } +- *piIndex = -1; +- return SQLITE_OK; + } + + /* +@@ -189551,7 +214986,8 @@ static RtreeSearchPoint *rtreeSearchPointNew( + pNew = rtreeEnqueue(pCur, rScore, iLevel); + if( pNew==0 ) return 0; + ii = (int)(pNew - pCur->aPoint) + 1; +- if( iiaNode[ii]==0 ); + pCur->aNode[ii] = pCur->aNode[0]; + }else{ +@@ -189612,7 +215048,7 @@ static void rtreeSearchPointPop(RtreeCursor *p){ + if( p->bPoint ){ + p->anQueue[p->sPoint.iLevel]--; + p->bPoint = 0; +- }else if( p->nPoint ){ ++ }else if( ALWAYS(p->nPoint) ){ + p->anQueue[p->aPoint[0].iLevel]--; + n = --p->nPoint; + p->aPoint[0] = p->aPoint[n]; +@@ -189753,8 +215189,12 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ + RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); + int rc = SQLITE_OK; + RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); +- if( rc==SQLITE_OK && p ){ +- *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); ++ if( rc==SQLITE_OK && ALWAYS(p) ){ ++ if( p->iCell>=NCELL(pNode) ){ ++ rc = SQLITE_ABORT; ++ }else{ ++ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); ++ } + } + return rc; + } +@@ -189771,7 +215211,8 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); + + if( rc ) return rc; +- if( p==0 ) return SQLITE_OK; ++ if( NEVER(p==0) ) return SQLITE_OK; ++ if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT; + if( i==0 ){ + sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); + }else if( i<=pRtree->nDim2 ){ +@@ -189869,6 +215310,8 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ + return SQLITE_OK; + } + ++SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); ++ + /* + ** Rtree virtual table module xFilter method. + */ +@@ -189898,7 +215341,8 @@ static int rtreeFilter( + i64 iNode = 0; + int eType = sqlite3_value_numeric_type(argv[0]); + if( eType==SQLITE_INTEGER +- || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid) ++ || (eType==SQLITE_FLOAT ++ && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0]))) + ){ + rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); + }else{ +@@ -189949,7 +215393,20 @@ static int rtreeFilter( + p->pInfo->nCoord = pRtree->nDim2; + p->pInfo->anQueue = pCsr->anQueue; + p->pInfo->mxLevel = pRtree->iDepth + 1; +- }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ ++ }else if( eType==SQLITE_INTEGER ){ ++ sqlite3_int64 iVal = sqlite3_value_int64(argv[ii]); ++#ifdef SQLITE_RTREE_INT_ONLY ++ p->u.rValue = iVal; ++#else ++ p->u.rValue = (double)iVal; ++ if( iVal>=((sqlite3_int64)1)<<48 ++ || iVal<=-(((sqlite3_int64)1)<<48) ++ ){ ++ if( p->op==RTREE_LT ) p->op = RTREE_LE; ++ if( p->op==RTREE_GT ) p->op = RTREE_GE; ++ } ++#endif ++ }else if( eType==SQLITE_FLOAT ){ + #ifdef SQLITE_RTREE_INT_ONLY + p->u.rValue = sqlite3_value_int64(argv[ii]); + #else +@@ -189970,8 +215427,11 @@ static int rtreeFilter( + } + if( rc==SQLITE_OK ){ + RtreeSearchPoint *pNew; ++ assert( pCsr->bPoint==0 ); /* Due to the resetCursor() call above */ + pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1)); +- if( pNew==0 ) return SQLITE_NOMEM; ++ if( NEVER(pNew==0) ){ /* Because pCsr->bPoint was FALSE */ ++ return SQLITE_NOMEM; ++ } + pNew->id = 1; + pNew->iCell = 0; + pNew->eWithin = PARTLY_WITHIN; +@@ -190048,7 +215508,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; + + if( bMatch==0 && p->usable +- && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ++ && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ + ){ + /* We have an equality constraint on the rowid. Use strategy 1. */ + int jj; +@@ -190077,11 +215537,12 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) + ){ + u8 op; ++ u8 doOmit = 1; + switch( p->op ){ +- case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; +- case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; ++ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; doOmit = 0; break; ++ case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; doOmit = 0; break; + case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; +- case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; ++ case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; doOmit = 0; break; + case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; + case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break; + default: op = 0; break; +@@ -190090,15 +215551,19 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + zIdxStr[iIdx++] = op; + zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); + pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); +- pIdxInfo->aConstraintUsage[ii].omit = 1; ++ pIdxInfo->aConstraintUsage[ii].omit = doOmit; + } + } + } + + pIdxInfo->idxNum = 2; + pIdxInfo->needToFreeIdxStr = 1; +- if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){ +- return SQLITE_NOMEM; ++ if( iIdx>0 ){ ++ pIdxInfo->idxStr = sqlite3_malloc( iIdx+1 ); ++ if( pIdxInfo->idxStr==0 ){ ++ return SQLITE_NOMEM; ++ } ++ memcpy(pIdxInfo->idxStr, zIdxStr, iIdx+1); + } + + nRow = pRtree->nRowEst >> (iIdx/2); +@@ -190109,7 +215574,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + } + + /* +-** Return the N-dimensional volumn of the cell stored in *p. ++** Return the N-dimensional volume of the cell stored in *p. + */ + static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ + RtreeDValue area = (RtreeDValue)1; +@@ -190177,31 +215642,22 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ + */ + static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ + int ii; +- int isInt = (pRtree->eCoordType==RTREE_COORD_INT32); +- for(ii=0; iinDim2; ii+=2){ +- RtreeCoord *a1 = &p1->aCoord[ii]; +- RtreeCoord *a2 = &p2->aCoord[ii]; +- if( (!isInt && (a2[0].fa1[1].f)) +- || ( isInt && (a2[0].ia1[1].i)) +- ){ +- return 0; ++ if( pRtree->eCoordType==RTREE_COORD_INT32 ){ ++ for(ii=0; iinDim2; ii+=2){ ++ RtreeCoord *a1 = &p1->aCoord[ii]; ++ RtreeCoord *a2 = &p2->aCoord[ii]; ++ if( a2[0].ia1[1].i ) return 0; ++ } ++ }else{ ++ for(ii=0; iinDim2; ii+=2){ ++ RtreeCoord *a1 = &p1->aCoord[ii]; ++ RtreeCoord *a2 = &p2->aCoord[ii]; ++ if( a2[0].fa1[1].f ) return 0; + } + } + return 1; + } + +-/* +-** Return the amount cell p would grow by if it were unioned with pCell. +-*/ +-static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){ +- RtreeDValue area; +- RtreeCell cell; +- memcpy(&cell, p, sizeof(RtreeCell)); +- area = cellArea(pRtree, &cell); +- cellUnion(pRtree, &cell, pCell); +- return (cellArea(pRtree, &cell)-area); +-} +- + static RtreeDValue cellOverlap( + Rtree *pRtree, + RtreeCell *p, +@@ -190248,38 +215704,52 @@ static int ChooseLeaf( + for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){ + int iCell; + sqlite3_int64 iBest = 0; +- ++ int bFound = 0; + RtreeDValue fMinGrowth = RTREE_ZERO; + RtreeDValue fMinArea = RTREE_ZERO; +- + int nCell = NCELL(pNode); +- RtreeCell cell; +- RtreeNode *pChild; +- +- RtreeCell *aCell = 0; ++ RtreeNode *pChild = 0; + +- /* Select the child node which will be enlarged the least if pCell +- ** is inserted into it. Resolve ties by choosing the entry with +- ** the smallest area. ++ /* First check to see if there is are any cells in pNode that completely ++ ** contains pCell. If two or more cells in pNode completely contain pCell ++ ** then pick the smallest. + */ + for(iCell=0; iCellpParent ){ + RtreeNode *pParent = p->pParent; + RtreeCell cell; + int iCell; + +- if( (++cnt)>1000 || nodeParentIndex(pRtree, p, &iCell) ){ ++ cnt++; ++ if( NEVER(cnt>100) ){ ++ RTREE_IS_CORRUPT(pRtree); ++ return SQLITE_CORRUPT_VTAB; ++ } ++ rc = nodeParentIndex(pRtree, p, &iCell); ++ if( NEVER(rc!=SQLITE_OK) ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } +@@ -190345,77 +215822,6 @@ static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){ + static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int); + + +-/* +-** Arguments aIdx, aDistance and aSpare all point to arrays of size +-** nIdx. The aIdx array contains the set of integers from 0 to +-** (nIdx-1) in no particular order. This function sorts the values +-** in aIdx according to the indexed values in aDistance. For +-** example, assuming the inputs: +-** +-** aIdx = { 0, 1, 2, 3 } +-** aDistance = { 5.0, 2.0, 7.0, 6.0 } +-** +-** this function sets the aIdx array to contain: +-** +-** aIdx = { 0, 1, 2, 3 } +-** +-** The aSpare array is used as temporary working space by the +-** sorting algorithm. +-*/ +-static void SortByDistance( +- int *aIdx, +- int nIdx, +- RtreeDValue *aDistance, +- int *aSpare +-){ +- if( nIdx>1 ){ +- int iLeft = 0; +- int iRight = 0; +- +- int nLeft = nIdx/2; +- int nRight = nIdx-nLeft; +- int *aLeft = aIdx; +- int *aRight = &aIdx[nLeft]; +- +- SortByDistance(aLeft, nLeft, aDistance, aSpare); +- SortByDistance(aRight, nRight, aDistance, aSpare); +- +- memcpy(aSpare, aLeft, sizeof(int)*nLeft); +- aLeft = aSpare; +- +- while( iLeft0 ){ + RtreeNode *pChild = nodeHashLookup(pRtree, iRowid); ++ RtreeNode *p; ++ for(p=pNode; p; p=p->pParent){ ++ if( p==pChild ) return SQLITE_CORRUPT_VTAB; ++ } + if( pChild ){ + nodeRelease(pRtree, pChild->pParent); + nodeReference(pNode); + pChild->pParent = pNode; + } + } ++ if( NEVER(pNode==0) ) return SQLITE_ERROR; + return xSetMapping(pRtree, iRowid, pNode->iNode); + } + +@@ -190690,11 +216101,12 @@ static int SplitNode( + RtreeNode *pParent = pLeft->pParent; + int iCell; + rc = nodeParentIndex(pRtree, pLeft, &iCell); +- if( rc==SQLITE_OK ){ ++ if( ALWAYS(rc==SQLITE_OK) ){ + nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); + rc = AdjustTree(pRtree, pParent, &leftbbox); ++ assert( rc==SQLITE_OK ); + } +- if( rc!=SQLITE_OK ){ ++ if( NEVER(rc!=SQLITE_OK) ){ + goto splitnode_out; + } + } +@@ -190769,7 +216181,7 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ + */ + iNode = sqlite3_column_int64(pRtree->pReadParent, 0); + for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); +- if( !pTest ){ ++ if( pTest==0 ){ + rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); + } + } +@@ -190800,6 +216212,7 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ + pParent = pNode->pParent; + pNode->pParent = 0; + rc = deleteCell(pRtree, pParent, iCell, iHeight+1); ++ testcase( rc!=SQLITE_OK ); + } + rc2 = nodeRelease(pRtree, pParent); + if( rc==SQLITE_OK ){ +@@ -190893,107 +216306,6 @@ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){ + return rc; + } + +-static int Reinsert( +- Rtree *pRtree, +- RtreeNode *pNode, +- RtreeCell *pCell, +- int iHeight +-){ +- int *aOrder; +- int *aSpare; +- RtreeCell *aCell; +- RtreeDValue *aDistance; +- int nCell; +- RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS]; +- int iDim; +- int ii; +- int rc = SQLITE_OK; +- int n; +- +- memset(aCenterCoord, 0, sizeof(RtreeDValue)*RTREE_MAX_DIMENSIONS); +- +- nCell = NCELL(pNode)+1; +- n = (nCell+1)&(~1); +- +- /* Allocate the buffers used by this operation. The allocation is +- ** relinquished before this function returns. +- */ +- aCell = (RtreeCell *)sqlite3_malloc64(n * ( +- sizeof(RtreeCell) + /* aCell array */ +- sizeof(int) + /* aOrder array */ +- sizeof(int) + /* aSpare array */ +- sizeof(RtreeDValue) /* aDistance array */ +- )); +- if( !aCell ){ +- return SQLITE_NOMEM; +- } +- aOrder = (int *)&aCell[n]; +- aSpare = (int *)&aOrder[n]; +- aDistance = (RtreeDValue *)&aSpare[n]; +- +- for(ii=0; iinDim; iDim++){ +- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]); +- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]); +- } +- } +- for(iDim=0; iDimnDim; iDim++){ +- aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2)); +- } +- +- for(ii=0; iinDim; iDim++){ +- RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) - +- DCOORD(aCell[ii].aCoord[iDim*2])); +- aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]); +- } +- } +- +- SortByDistance(aOrder, nCell, aDistance, aSpare); +- nodeZero(pRtree, pNode); +- +- for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){ +- RtreeCell *p = &aCell[aOrder[ii]]; +- nodeInsertCell(pRtree, pNode, p); +- if( p->iRowid==pCell->iRowid ){ +- if( iHeight==0 ){ +- rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); +- }else{ +- rc = parentWrite(pRtree, p->iRowid, pNode->iNode); +- } +- } +- } +- if( rc==SQLITE_OK ){ +- rc = fixBoundingBox(pRtree, pNode); +- } +- for(; rc==SQLITE_OK && iiiNode currently contains +- ** the height of the sub-tree headed by the cell. +- */ +- RtreeNode *pInsert; +- RtreeCell *p = &aCell[aOrder[ii]]; +- rc = ChooseLeaf(pRtree, p, iHeight, &pInsert); +- if( rc==SQLITE_OK ){ +- int rc2; +- rc = rtreeInsertCell(pRtree, pInsert, p, iHeight); +- rc2 = nodeRelease(pRtree, pInsert); +- if( rc==SQLITE_OK ){ +- rc = rc2; +- } +- } +- } +- +- sqlite3_free(aCell); +- return rc; +-} +- + /* + ** Insert cell pCell into node pNode. Node pNode is the head of a + ** subtree iHeight high (leaf nodes have iHeight==0). +@@ -191014,15 +216326,10 @@ static int rtreeInsertCell( + } + } + if( nodeInsertCell(pRtree, pNode, pCell) ){ +- if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){ +- rc = SplitNode(pRtree, pNode, pCell, iHeight); +- }else{ +- pRtree->iReinsertHeight = iHeight; +- rc = Reinsert(pRtree, pNode, pCell, iHeight); +- } ++ rc = SplitNode(pRtree, pNode, pCell, iHeight); + }else{ + rc = AdjustTree(pRtree, pNode, pCell); +- if( rc==SQLITE_OK ){ ++ if( ALWAYS(rc==SQLITE_OK) ){ + if( iHeight==0 ){ + rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); + }else{ +@@ -191128,7 +216435,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ + int rc2; + RtreeNode *pChild = 0; + i64 iChild = nodeGetRowid(pRtree, pRoot, 0); +- rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); ++ rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); /* tag-20210916a */ + if( rc==SQLITE_OK ){ + rc = removeNode(pRtree, pChild, pRtree->iDepth-1); + } +@@ -191261,7 +216568,7 @@ static int rtreeUpdate( + rtreeReference(pRtree); + assert(nData>=1); + +- cell.iRowid = 0; /* Used only to suppress a compiler warning */ ++ memset(&cell, 0, sizeof(cell)); + + /* Constraint handling. A write operation on an r-tree table may return + ** SQLITE_CONSTRAINT for two reasons: +@@ -191362,7 +216669,6 @@ static int rtreeUpdate( + } + if( rc==SQLITE_OK ){ + int rc2; +- pRtree->iReinsertHeight = -1; + rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); + rc2 = nodeRelease(pRtree, pLeaf); + if( rc==SQLITE_OK ){ +@@ -191392,7 +216698,7 @@ constraint: + static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ + Rtree *pRtree = (Rtree *)pVtab; + assert( pRtree->inWrTrans==0 ); +- pRtree->inWrTrans++; ++ pRtree->inWrTrans = 1; + return SQLITE_OK; + } + +@@ -191406,6 +216712,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){ + nodeBlobReset(pRtree); + return SQLITE_OK; + } ++static int rtreeRollback(sqlite3_vtab *pVtab){ ++ return rtreeEndTransaction(pVtab); ++} + + /* + ** The xRename method for rtree module virtual tables. +@@ -191463,7 +216772,7 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ + char *zSql; + sqlite3_stmt *p; + int rc; +- i64 nRow = 0; ++ i64 nRow = RTREE_MIN_ROWEST; + + rc = sqlite3_table_column_metadata( + db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0 +@@ -191480,20 +216789,10 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ + if( rc==SQLITE_OK ){ + if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0); + rc = sqlite3_finalize(p); +- }else if( rc!=SQLITE_NOMEM ){ +- rc = SQLITE_OK; +- } +- +- if( rc==SQLITE_OK ){ +- if( nRow==0 ){ +- pRtree->nRowEst = RTREE_DEFAULT_ROWEST; +- }else{ +- pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); +- } + } + sqlite3_free(zSql); + } +- ++ pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); + return rc; + } + +@@ -191513,8 +216812,11 @@ static int rtreeShadowName(const char *zName){ + return 0; + } + ++/* Forward declaration */ ++static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**); ++ + static sqlite3_module rtreeModule = { +- 3, /* iVersion */ ++ 4, /* iVersion */ + rtreeCreate, /* xCreate - create a table */ + rtreeConnect, /* xConnect - connect to an existing table */ + rtreeBestIndex, /* xBestIndex - Determine search strategy */ +@@ -191531,13 +216833,14 @@ static sqlite3_module rtreeModule = { + rtreeBeginTransaction, /* xBegin - begin transaction */ + rtreeEndTransaction, /* xSync - sync transaction */ + rtreeEndTransaction, /* xCommit - commit transaction */ +- rtreeEndTransaction, /* xRollback - rollback transaction */ ++ rtreeRollback, /* xRollback - rollback transaction */ + 0, /* xFindFunction - function overloading */ + rtreeRename, /* xRename - rename the table */ + rtreeSavepoint, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ +- rtreeShadowName /* xShadowName */ ++ rtreeShadowName, /* xShadowName */ ++ rtreeIntegrity /* xIntegrity */ + }; + + static int rtreeSqlInit( +@@ -191630,7 +216933,7 @@ static int rtreeSqlInit( + } + sqlite3_free(zSql); + } +- if( pRtree->nAux ){ ++ if( pRtree->nAux && rc!=SQLITE_NOMEM ){ + pRtree->zReadAuxSql = sqlite3_mprintf( + "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1", + zDb, zPrefix); +@@ -191643,9 +216946,12 @@ static int rtreeSqlInit( + sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix); + for(ii=0; iinAux; ii++){ + if( ii ) sqlite3_str_append(p, ",", 1); ++#ifdef SQLITE_ENABLE_GEOPOLY + if( iinAuxNotNull ){ + sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii); +- }else{ ++ }else ++#endif ++ { + sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2); + } + } +@@ -191790,22 +217096,27 @@ static int rtreeInit( + } + + sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); ++ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); ++ + + /* Allocate the sqlite3_vtab structure */ + nDb = (int)strlen(argv[1]); + nName = (int)strlen(argv[2]); +- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); ++ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8); + if( !pRtree ){ + return SQLITE_NOMEM; + } +- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); ++ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8); + pRtree->nBusy = 1; + pRtree->base.pModule = &rtreeModule; + pRtree->zDb = (char *)&pRtree[1]; + pRtree->zName = &pRtree->zDb[nDb+1]; ++ pRtree->zNodeName = &pRtree->zName[nName+1]; + pRtree->eCoordType = (u8)eCoordType; + memcpy(pRtree->zDb, argv[1], nDb); + memcpy(pRtree->zName, argv[2], nName); ++ memcpy(pRtree->zNodeName, argv[2], nName); ++ memcpy(&pRtree->zNodeName[nName], "_node", 6); + + + /* Create/Connect to the underlying relational database schema. If +@@ -191910,6 +217221,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ + tree.nDim2 = tree.nDim*2; + tree.nBytesPerCell = 8 + 8 * tree.nDim; + node.zData = (u8 *)sqlite3_value_blob(apArg[1]); ++ if( node.zData==0 ) return; + nData = sqlite3_value_bytes(apArg[1]); + if( nData<4 ) return; + if( nDatadb, pRtree->zDb, pRtree->zName, pzErr); ++ if( rc==SQLITE_OK && *pzErr ){ ++ *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z", ++ pRtree->zDb, pRtree->zName, *pzErr); ++ if( (*pzErr)==0 ) rc = SQLITE_NOMEM; ++ } ++ return rc; ++} ++ + /* + ** Usage: + ** +@@ -192455,11 +217783,7 @@ static void rtreecheck( + # define GEODEBUG(X) + #endif + +-#ifndef JSON_NULL /* The following stuff repeats things found in json1 */ +-/* +-** Versions of isspace(), isalnum() and isdigit() to which it is safe +-** to pass signed char values. +-*/ ++/* Character class routines */ + #ifdef sqlite3Isdigit + /* Use the SQLite core versions if this routine is part of the + ** SQLite amalgamation */ +@@ -192474,6 +217798,7 @@ static void rtreecheck( + # define safe_isxdigit(x) isxdigit((unsigned char)(x)) + #endif + ++#ifndef JSON_NULL /* The following stuff repeats things found in json1 */ + /* + ** Growing our own isspace() routine this way is twice as fast as + ** the library isspace() function. +@@ -192496,7 +217821,7 @@ static const char geopolyIsSpace[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; +-#define safe_isspace(x) (geopolyIsSpace[(unsigned char)x]) ++#define fast_isspace(x) (geopolyIsSpace[(unsigned char)x]) + #endif /* JSON NULL - back to original code */ + + /* Compiler and version */ +@@ -192585,7 +217910,7 @@ static void geopolySwab32(unsigned char *a){ + + /* Skip whitespace. Return the next non-whitespace character. */ + static char geopolySkipSpace(GeoParse *p){ +- while( safe_isspace(p->z[0]) ) p->z++; ++ while( fast_isspace(p->z[0]) ) p->z++; + return p->z[0]; + } + +@@ -192734,11 +218059,16 @@ static GeoPoly *geopolyFuncParam( + ){ + GeoPoly *p = 0; + int nByte; ++ testcase( pCtx==0 ); + if( sqlite3_value_type(pVal)==SQLITE_BLOB +- && (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord)) ++ && (nByte = sqlite3_value_bytes(pVal))>=(int)(4+6*sizeof(GeoCoord)) + ){ + const unsigned char *a = sqlite3_value_blob(pVal); + int nVertex; ++ if( a==0 ){ ++ if( pCtx ) sqlite3_result_error_nomem(pCtx); ++ return 0; ++ } + nVertex = (a[1]<<16) + (a[2]<<8) + a[3]; + if( (a[0]==0 || a[0]==1) + && (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte +@@ -192789,6 +218119,7 @@ static void geopolyBlobFunc( + sqlite3_value **argv + ){ + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); ++ (void)argc; + if( p ){ + sqlite3_result_blob(context, p->hdr, + 4+8*p->nVertex, SQLITE_TRANSIENT); +@@ -192808,6 +218139,7 @@ static void geopolyJsonFunc( + sqlite3_value **argv + ){ + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); ++ (void)argc; + if( p ){ + sqlite3 *db = sqlite3_context_db_handle(context); + sqlite3_str *x = sqlite3_str_new(db); +@@ -192889,6 +218221,7 @@ static void geopolyXformFunc( + double F = sqlite3_value_double(argv[6]); + GeoCoord x1, y1, x0, y0; + int ii; ++ (void)argc; + if( p ){ + for(ii=0; iinVertex; ii++){ + x0 = GeoX(p,ii); +@@ -192939,6 +218272,7 @@ static void geopolyAreaFunc( + sqlite3_value **argv + ){ + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); ++ (void)argc; + if( p ){ + sqlite3_result_double(context, geopolyArea(p)); + sqlite3_free(p); +@@ -192964,6 +218298,7 @@ static void geopolyCcwFunc( + sqlite3_value **argv + ){ + GeoPoly *p = geopolyFuncParam(context, argv[0], 0); ++ (void)argc; + if( p ){ + if( geopolyArea(p)<0.0 ){ + int ii, jj; +@@ -193018,6 +218353,7 @@ static void geopolyRegularFunc( + int n = sqlite3_value_int(argv[3]); + int i; + GeoPoly *p; ++ (void)argc; + + if( n<3 || r<=0.0 ) return; + if( n>1000 ) n = 1000; +@@ -193112,7 +218448,7 @@ static GeoPoly *geopolyBBox( + aCoord[2].f = mnY; + aCoord[3].f = mxY; + } +- }else{ ++ }else if( aCoord ){ + memset(aCoord, 0, sizeof(RtreeCoord)*4); + } + return pOut; +@@ -193127,6 +218463,7 @@ static void geopolyBBoxFunc( + sqlite3_value **argv + ){ + GeoPoly *p = geopolyBBox(context, argv[0], 0, 0); ++ (void)argc; + if( p ){ + sqlite3_result_blob(context, p->hdr, + 4+8*p->nVertex, SQLITE_TRANSIENT); +@@ -193154,6 +218491,7 @@ static void geopolyBBoxStep( + ){ + RtreeCoord a[4]; + int rc = SQLITE_OK; ++ (void)argc; + (void)geopolyBBox(context, argv[0], a, &rc); + if( rc==SQLITE_OK ){ + GeoBBox *pBBox; +@@ -193190,7 +218528,7 @@ static void geopolyBBoxFinal( + ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). + ** Returns: + ** +-** +2 x0,y0 is on the line segement ++** +2 x0,y0 is on the line segment + ** + ** +1 x0,y0 is beneath line segment + ** +@@ -193242,6 +218580,8 @@ static void geopolyContainsPointFunc( + int v = 0; + int cnt = 0; + int ii; ++ (void)argc; ++ + if( p1==0 ) return; + for(ii=0; iinVertex-1; ii++){ + v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii), +@@ -193281,6 +218621,7 @@ static void geopolyWithinFunc( + ){ + GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0); + GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0); ++ (void)argc; + if( p1 && p2 ){ + int x = geopolyOverlap(p1, p2); + if( x<0 ){ +@@ -193293,7 +218634,7 @@ static void geopolyWithinFunc( + sqlite3_free(p2); + } + +-/* Objects used by the overlap algorihm. */ ++/* Objects used by the overlap algorithm. */ + typedef struct GeoEvent GeoEvent; + typedef struct GeoSegment GeoSegment; + typedef struct GeoOverlap GeoOverlap; +@@ -193504,7 +218845,7 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ + geopolyAddSegments(p, p1, 1); + geopolyAddSegments(p, p2, 2); + pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); +- rX = pThisEvent->x==0.0 ? -1.0 : 0.0; ++ rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0; + memset(aOverlap, 0, sizeof(aOverlap)); + while( pThisEvent ){ + if( pThisEvent->x!=rX ){ +@@ -193563,11 +218904,11 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ + }else{ + /* Remove a segment */ + if( pActive==pThisEvent->pSeg ){ +- pActive = pActive->pNext; ++ pActive = ALWAYS(pActive) ? pActive->pNext : 0; + }else{ + for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){ + if( pSeg->pNext==pThisEvent->pSeg ){ +- pSeg->pNext = pSeg->pNext->pNext; ++ pSeg->pNext = ALWAYS(pSeg->pNext) ? pSeg->pNext->pNext : 0; + break; + } + } +@@ -193611,6 +218952,7 @@ static void geopolyOverlapFunc( + ){ + GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0); + GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0); ++ (void)argc; + if( p1 && p2 ){ + int x = geopolyOverlap(p1, p2); + if( x<0 ){ +@@ -193631,8 +218973,12 @@ static void geopolyDebugFunc( + int argc, + sqlite3_value **argv + ){ ++ (void)context; ++ (void)argc; + #ifdef GEOPOLY_ENABLE_DEBUG + geo_debug = sqlite3_value_int(argv[0]); ++#else ++ (void)argv; + #endif + } + +@@ -193660,26 +219006,31 @@ static int geopolyInit( + sqlite3_str *pSql; + char *zSql; + int ii; ++ (void)pAux; + + sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); ++ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); + + /* Allocate the sqlite3_vtab structure */ + nDb = strlen(argv[1]); + nName = strlen(argv[2]); +- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); ++ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8); + if( !pRtree ){ + return SQLITE_NOMEM; + } +- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); ++ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8); + pRtree->nBusy = 1; + pRtree->base.pModule = &rtreeModule; + pRtree->zDb = (char *)&pRtree[1]; + pRtree->zName = &pRtree->zDb[nDb+1]; ++ pRtree->zNodeName = &pRtree->zName[nName+1]; + pRtree->eCoordType = RTREE_COORD_REAL32; + pRtree->nDim = 2; + pRtree->nDim2 = 4; + memcpy(pRtree->zDb, argv[1], nDb); + memcpy(pRtree->zName, argv[2], nName); ++ memcpy(pRtree->zNodeName, argv[2], nName); ++ memcpy(&pRtree->zNodeName[nName], "_node", 6); + + + /* Create/Connect to the underlying relational database schema. If +@@ -193776,6 +219127,7 @@ static int geopolyFilter( + RtreeNode *pRoot = 0; + int rc = SQLITE_OK; + int iCell = 0; ++ (void)idxStr; + + rtreeReference(pRtree); + +@@ -193811,6 +219163,7 @@ static int geopolyFilter( + RtreeCoord bbox[4]; + RtreeConstraint *p; + assert( argc==1 ); ++ assert( argv[0]!=0 ); + geopolyBBox(0, argv[0], bbox, &rc); + if( rc ){ + goto geopoly_filter_end; +@@ -193901,6 +219254,7 @@ static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int iRowidTerm = -1; + int iFuncTerm = -1; + int idxNum = 0; ++ (void)tab; + + for(ii=0; iinConstraint; ii++){ + struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; +@@ -194038,6 +219392,7 @@ static int geopolyUpdate( + || !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */ + || oldRowid!=newRowid) /* Rowid change */ + ){ ++ assert( aData[2]!=0 ); + geopolyBBox(0, aData[2], cell.aCoord, &rc); + if( rc ){ + if( rc==SQLITE_ERROR ){ +@@ -194089,7 +219444,6 @@ static int geopolyUpdate( + } + if( rc==SQLITE_OK ){ + int rc2; +- pRtree->iReinsertHeight = -1; + rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); + rc2 = nodeRelease(pRtree, pLeaf); + if( rc==SQLITE_OK ){ +@@ -194120,7 +219474,7 @@ static int geopolyUpdate( + sqlite3_free(p); + nChange = 1; + } +- for(jj=1; jjnAux; jj++){ ++ for(jj=1; jjxGeom = 0; + pGeomCtx->xQueryFunc = xQueryFunc; + pGeomCtx->xDestructor = xDestructor; +@@ -194401,7 +219760,7 @@ SQLITE_API int sqlite3_rtree_query_callback( + ); + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + #ifdef _WIN32 + __declspec(dllexport) + #endif +@@ -194720,8 +220079,9 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){ + + if( U_SUCCESS(status) ){ + sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete); +- }else{ +- assert(!pExpr); ++ pExpr = sqlite3_get_auxdata(p, 0); ++ } ++ if( !pExpr ){ + icuFunctionError(p, "uregex_open", status); + return; + } +@@ -194891,7 +220251,7 @@ static void icuLoadCollation( + UCollator *pUCollator; /* ICU library collation object */ + int rc; /* Return code from sqlite3_create_collation_x() */ + +- assert(nArg==2); ++ assert(nArg==2 || nArg==3); + (void)nArg; /* Unused parameter */ + zLocale = (const char *)sqlite3_value_text(apArg[0]); + zName = (const char *)sqlite3_value_text(apArg[1]); +@@ -194906,7 +220266,39 @@ static void icuLoadCollation( + return; + } + assert(p); +- ++ if(nArg==3){ ++ const char *zOption = (const char*)sqlite3_value_text(apArg[2]); ++ static const struct { ++ const char *zName; ++ UColAttributeValue val; ++ } aStrength[] = { ++ { "PRIMARY", UCOL_PRIMARY }, ++ { "SECONDARY", UCOL_SECONDARY }, ++ { "TERTIARY", UCOL_TERTIARY }, ++ { "DEFAULT", UCOL_DEFAULT_STRENGTH }, ++ { "QUARTERNARY", UCOL_QUATERNARY }, ++ { "IDENTICAL", UCOL_IDENTICAL }, ++ }; ++ unsigned int i; ++ for(i=0; i=sizeof(aStrength)/sizeof(aStrength[0]) ){ ++ sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p)); ++ sqlite3_str_appendf(pStr, ++ "unknown collation strength \"%s\" - should be one of:", ++ zOption); ++ for(i=0; i naming scheme. ++** tables or views named using the data_ naming scheme. + ** + ** Instead of the plain data_ naming scheme, RBU database tables + ** may also be named data_, where is any sequence +@@ -195445,7 +220838,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule( + ** + ** If the target database table is a virtual table or a table that has no + ** PRIMARY KEY declaration, the data_% table must also contain a column +-** named "rbu_rowid". This column is mapped to the tables implicit primary ++** named "rbu_rowid". This column is mapped to the table's implicit primary + ** key column - "rowid". Virtual tables for which the "rowid" column does + ** not function like a primary key value cannot be updated using RBU. For + ** example, if the target db contains either of the following: +@@ -195680,7 +221073,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open( + ** the next call to sqlite3rbu_vacuum() opens a handle that starts a + ** new RBU vacuum operation. + ** +-** As with sqlite3rbu_open(), Zipvfs users should rever to the comment ++** As with sqlite3rbu_open(), Zipvfs users should refer to the comment + ** describing the sqlite3rbu_create_vfs() API function below for + ** a description of the complications associated with using RBU with + ** zipvfs databases. +@@ -195776,7 +221169,7 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu); + ** + ** If the RBU update has been completely applied, mark the RBU database + ** as fully applied. Otherwise, assuming no error has occurred, save the +-** current state of the RBU update appliation to the RBU database. ++** current state of the RBU update application to the RBU database. + ** + ** If an error has already occurred as part of an sqlite3rbu_step() + ** or sqlite3rbu_open() call, or if one occurs within this function, an +@@ -195878,6 +221271,34 @@ SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo); + + SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu); + ++/* ++** As part of applying an RBU update or performing an RBU vacuum operation, ++** the system must at one point move the *-oal file to the equivalent *-wal ++** path. Normally, it does this by invoking POSIX function rename(2) directly. ++** Except on WINCE platforms, where it uses win32 API MoveFileW(). This ++** function may be used to register a callback that the RBU module will invoke ++** instead of one of these APIs. ++** ++** If a callback is registered with an RBU handle, it invokes it instead ++** of rename(2) when it needs to move a file within the file-system. The ++** first argument passed to the xRename() callback is a copy of the second ++** argument (pArg) passed to this function. The second is the full path ++** to the file to move and the third the full path to which it should be ++** moved. The callback function should return SQLITE_OK to indicate ++** success. If an error occurs, it should return an SQLite error code. ++** In this case the RBU operation will be abandoned and the error returned ++** to the RBU user. ++** ++** Passing a NULL pointer in place of the xRename argument to this function ++** restores the default behaviour. ++*/ ++SQLITE_API void sqlite3rbu_rename_handler( ++ sqlite3rbu *pRbu, ++ void *pArg, ++ int (*xRename)(void *pArg, const char *zOld, const char *zNew) ++); ++ ++ + /* + ** Create an RBU VFS named zName that accesses the underlying file-system + ** via existing VFS zParent. Or, if the zParent parameter is passed NULL, +@@ -195962,6 +221383,13 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName); + # define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;} + #endif + ++/* ++** Name of the URI option that causes RBU to take an exclusive lock as ++** part of the incremental checkpoint operation. ++*/ ++#define RBU_EXCLUSIVE_CHECKPOINT "rbu_exclusive_checkpoint" ++ ++ + /* + ** The rbu_state table is used to save the state of a partially applied + ** update so that it can be resumed later. The table consists of integer +@@ -196044,6 +221472,7 @@ typedef unsigned int u32; + typedef unsigned short u16; + typedef unsigned char u8; + typedef sqlite3_int64 i64; ++typedef sqlite3_uint64 u64; + #endif + + /* +@@ -196180,6 +221609,27 @@ struct RbuFrame { + u32 iWalFrame; + }; + ++#ifndef UNUSED_PARAMETER ++/* ++** The following macros are used to suppress compiler warnings and to ++** make it clear to human readers when a function parameter is deliberately ++** left unused within the body of a function. This usually happens when ++** a function is called via a function pointer. For example the ++** implementation of an SQL aggregate step callback may not use the ++** parameter indicating the number of arguments passed to the aggregate, ++** if it knows that this is enforced elsewhere. ++** ++** When a function parameter is not used at all within the body of a function, ++** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. ++** However, these macros may also be used to suppress warnings related to ++** parameters that may or may not be used depending on compilation options. ++** For example those parameters only used in assert() statements. In these ++** cases the parameters are named as per the usual conventions. ++*/ ++#define UNUSED_PARAMETER(x) (void)(x) ++#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) ++#endif ++ + /* + ** RBU handle. + ** +@@ -196231,13 +221681,15 @@ struct sqlite3rbu { + int rc; /* Value returned by last rbu_step() call */ + char *zErrmsg; /* Error message if rc!=SQLITE_OK */ + int nStep; /* Rows processed for current object */ +- int nProgress; /* Rows processed for all objects */ ++ sqlite3_int64 nProgress; /* Rows processed for all objects */ + RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ + const char *zVfsName; /* Name of automatically created rbu vfs */ + rbu_file *pTargetFd; /* File handle open on target db */ + int nPagePerSector; /* Pages per sector for pTargetFd */ + i64 iOalSz; + i64 nPhaseOneStep; ++ void *pRenameArg; ++ int (*xRename)(void*, const char*, const char*); + + /* The following state variables are used as part of the incremental + ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding +@@ -196346,7 +221798,7 @@ static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){ + v = (v<<6) + c; + } + z--; +- *pLen -= z - zStart; ++ *pLen -= (int)(z - zStart); + *pz = (char*)z; + return v; + } +@@ -196531,6 +221983,7 @@ static void rbuFossilDeltaFunc( + char *aOut; + + assert( argc==2 ); ++ UNUSED_PARAMETER(argc); + + nOrig = sqlite3_value_bytes(argv[0]); + aOrig = (const char*)sqlite3_value_blob(argv[0]); +@@ -196728,6 +222181,7 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){ + if( rc!=SQLITE_ROW ){ + rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg); + pIter->zTbl = 0; ++ pIter->zDataTbl = 0; + }else{ + pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); + pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1); +@@ -197046,7 +222500,9 @@ static void rbuTableType( + assert( p->rc==SQLITE_OK ); + p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg, + sqlite3_mprintf( +- "SELECT (sql LIKE 'create virtual%%'), rootpage" ++ "SELECT " ++ " (sql COLLATE nocase BETWEEN 'CREATE VIRTUAL' AND 'CREATE VIRTUAM')," ++ " rootpage" + " FROM sqlite_schema" + " WHERE name=%Q", zTab + )); +@@ -197406,7 +222862,7 @@ static char *rbuVacuumTableStart( + ** the caller has to use an OFFSET clause to extract only the required + ** rows from the sourct table, just as it does for an RBU update operation. + */ +-char *rbuVacuumIndexStart( ++static char *rbuVacuumIndexStart( + sqlite3rbu *p, /* RBU handle */ + RbuObjIter *pIter /* RBU iterator object */ + ){ +@@ -197472,7 +222928,9 @@ char *rbuVacuumIndexStart( + zSep = ""; + for(iCol=0; iColnCol; iCol++){ + const char *zQuoted = (const char*)sqlite3_column_text(pSel, iCol); +- if( zQuoted[0]=='N' ){ ++ if( zQuoted==0 ){ ++ p->rc = SQLITE_NOMEM; ++ }else if( zQuoted[0]=='N' ){ + bFailed = 1; + break; + } +@@ -198105,13 +223563,13 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){ + else if( c==')' ){ + nParen--; + if( nParen==0 ){ +- int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; ++ int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); + pIter->aIdxCol[iIdxCol++].nSpan = nSpan; + i++; + break; + } + }else if( c==',' && nParen==1 ){ +- int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; ++ int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); + pIter->aIdxCol[iIdxCol++].nSpan = nSpan; + pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1]; + }else if( c=='"' || c=='\'' || c=='`' ){ +@@ -198577,7 +224035,7 @@ static RbuState *rbuLoadState(sqlite3rbu *p){ + break; + + case RBU_STATE_OALSZ: +- pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1); ++ pRet->iOalSz = sqlite3_column_int64(pStmt, 1); + break; + + case RBU_STATE_PHASEONESTEP: +@@ -198604,19 +224062,25 @@ static RbuState *rbuLoadState(sqlite3rbu *p){ + /* + ** Open the database handle and attach the RBU database as "rbu". If an + ** error occurs, leave an error code and message in the RBU handle. ++** ++** If argument dbMain is not NULL, then it is a database handle already ++** open on the target database. Use this handle instead of opening a new ++** one. + */ +-static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){ ++static void rbuOpenDatabase(sqlite3rbu *p, sqlite3 *dbMain, int *pbRetry){ + assert( p->rc || (p->dbMain==0 && p->dbRbu==0) ); + assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 ); ++ assert( dbMain==0 || rbuIsVacuum(p)==0 ); + + /* Open the RBU database */ + p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1); ++ p->dbMain = dbMain; + + if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){ + sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p); + if( p->zState==0 ){ + const char *zFile = sqlite3_db_filename(p->dbRbu, "main"); +- p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile); ++ p->zState = rbuMPrintf(p, "file:///%s-vacuum?modeof=%s", zFile, zFile); + } + } + +@@ -198795,6 +224259,8 @@ static void rbuFileSuffix3(const char *zBase, char *z){ + for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} + if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4); + } ++#else ++ UNUSED_PARAMETER2(zBase,z); + #endif + } + +@@ -198812,7 +224278,7 @@ static i64 rbuShmChecksum(sqlite3rbu *p){ + u32 volatile *ptr; + p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr); + if( p->rc==SQLITE_OK ){ +- iRet = ((i64)ptr[10] << 32) + ptr[11]; ++ iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]); + } + } + return iRet; +@@ -198864,11 +224330,11 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){ + ** no-ops. These locks will not be released until the connection + ** is closed. + ** +- ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL ++ ** * Attempting to xSync() the database file causes an SQLITE_NOTICE + ** error. + ** + ** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the +- ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[] ++ ** checkpoint below fails with SQLITE_NOTICE, and leaves the aFrame[] + ** array populated with a set of (frame -> page) mappings. Because the + ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy + ** data from the wal file into the database file according to the +@@ -198878,7 +224344,7 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){ + int rc2; + p->eStage = RBU_STAGE_CAPTURE; + rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0); +- if( rc2!=SQLITE_INTERNAL ) p->rc = rc2; ++ if( rc2!=SQLITE_NOTICE ) p->rc = rc2; + } + + if( p->rc==SQLITE_OK && p->nFrame>0 ){ +@@ -198924,7 +224390,7 @@ static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){ + + if( pRbu->mLock!=mReq ){ + pRbu->rc = SQLITE_BUSY; +- return SQLITE_INTERNAL; ++ return SQLITE_NOTICE_RBU; + } + + pRbu->pgsz = iAmt; +@@ -198974,17 +224440,49 @@ static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){ + p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff); + } + ++/* ++** This value is copied from the definition of ZIPVFS_CTRL_FILE_POINTER ++** in zipvfs.h. ++*/ ++#define RBU_ZIPVFS_CTRL_FILE_POINTER 230439 + + /* +-** Take an EXCLUSIVE lock on the database file. ++** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if ++** successful, or an SQLite error code otherwise. + */ +-static void rbuLockDatabase(sqlite3rbu *p){ +- sqlite3_file *pReal = p->pTargetFd->pReal; +- assert( p->rc==SQLITE_OK ); +- p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED); +- if( p->rc==SQLITE_OK ){ +- p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE); ++static int rbuLockDatabase(sqlite3 *db){ ++ int rc = SQLITE_OK; ++ sqlite3_file *fd = 0; ++ ++ sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); ++ if( fd ){ ++ sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); ++ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); ++ if( rc==SQLITE_OK ){ ++ rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE); ++ } ++ sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); ++ }else{ ++ sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); ++ } ++ ++ if( rc==SQLITE_OK && fd->pMethods ){ ++ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); ++ if( rc==SQLITE_OK ){ ++ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); ++ } + } ++ return rc; ++} ++ ++/* ++** Return true if the database handle passed as the only argument ++** was opened with the rbu_exclusive_checkpoint=1 URI parameter ++** specified. Or false otherwise. ++*/ ++static int rbuExclusiveCheckpoint(sqlite3 *db){ ++ const char *zUri = sqlite3_db_filename(db, 0); ++ return sqlite3_uri_boolean(zUri, RBU_EXCLUSIVE_CHECKPOINT, 0); + } + + #if defined(_WIN32_WCE) +@@ -199042,49 +224540,38 @@ static void rbuMoveOalFile(sqlite3rbu *p){ + ** In order to ensure that there are no database readers, an EXCLUSIVE + ** lock is obtained here before the *-oal is moved to *-wal. + */ +- rbuLockDatabase(p); +- if( p->rc==SQLITE_OK ){ +- rbuFileSuffix3(zBase, zWal); +- rbuFileSuffix3(zBase, zOal); ++ sqlite3 *dbMain = 0; ++ rbuFileSuffix3(zBase, zWal); ++ rbuFileSuffix3(zBase, zOal); + +- /* Re-open the databases. */ +- rbuObjIterFinalize(&p->objiter); +- sqlite3_close(p->dbRbu); +- sqlite3_close(p->dbMain); +- p->dbMain = 0; +- p->dbRbu = 0; ++ /* Re-open the databases. */ ++ rbuObjIterFinalize(&p->objiter); ++ sqlite3_close(p->dbRbu); ++ sqlite3_close(p->dbMain); ++ p->dbMain = 0; ++ p->dbRbu = 0; + +-#if defined(_WIN32_WCE) +- { +- LPWSTR zWideOal; +- LPWSTR zWideWal; +- +- zWideOal = rbuWinUtf8ToUnicode(zOal); +- if( zWideOal ){ +- zWideWal = rbuWinUtf8ToUnicode(zWal); +- if( zWideWal ){ +- if( MoveFileW(zWideOal, zWideWal) ){ +- p->rc = SQLITE_OK; +- }else{ +- p->rc = SQLITE_IOERR; +- } +- sqlite3_free(zWideWal); +- }else{ +- p->rc = SQLITE_IOERR_NOMEM; +- } +- sqlite3_free(zWideOal); +- }else{ +- p->rc = SQLITE_IOERR_NOMEM; +- } +- } +-#else +- p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; +-#endif ++ dbMain = rbuOpenDbhandle(p, p->zTarget, 1); ++ if( dbMain ){ ++ assert( p->rc==SQLITE_OK ); ++ p->rc = rbuLockDatabase(dbMain); ++ } + +- if( p->rc==SQLITE_OK ){ +- rbuOpenDatabase(p, 0); +- rbuSetupCheckpoint(p, 0); +- } ++ if( p->rc==SQLITE_OK ){ ++ p->rc = p->xRename(p->pRenameArg, zOal, zWal); ++ } ++ ++ if( p->rc!=SQLITE_OK ++ || rbuIsVacuum(p) ++ || rbuExclusiveCheckpoint(dbMain)==0 ++ ){ ++ sqlite3_close(dbMain); ++ dbMain = 0; ++ } ++ ++ if( p->rc==SQLITE_OK ){ ++ rbuOpenDatabase(p, dbMain, 0); ++ rbuSetupCheckpoint(p, 0); + } + } + +@@ -199358,7 +224845,7 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){ + "(%d, %Q), " + "(%d, %Q), " + "(%d, %d), " +- "(%d, %d), " ++ "(%d, %lld), " + "(%d, %lld), " + "(%d, %lld), " + "(%d, %lld), " +@@ -199658,7 +225145,8 @@ static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){ + static void rbuDeleteOalFile(sqlite3rbu *p){ + char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget); + if( zOal ){ +- sqlite3_vfs *pVfs = sqlite3_vfs_find(0); ++ sqlite3_vfs *pVfs = 0; ++ sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_VFS_POINTER, &pVfs); + assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 ); + pVfs->xDelete(pVfs, zOal, 0); + sqlite3_free(zOal); +@@ -199715,6 +225203,7 @@ static void rbuIndexCntFunc( + sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain); + + assert( nVal==1 ); ++ UNUSED_PARAMETER(nVal); + + rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg, + sqlite3_mprintf("SELECT count(*) FROM sqlite_schema " +@@ -199810,6 +225299,7 @@ static sqlite3rbu *openRbuHandle( + + /* Create the custom VFS. */ + memset(p, 0, sizeof(sqlite3rbu)); ++ sqlite3rbu_rename_handler(p, 0, 0); + rbuCreateVfs(p); + + /* Open the target, RBU and state databases */ +@@ -199835,9 +225325,9 @@ static sqlite3rbu *openRbuHandle( + ** If this is the case, it will have been checkpointed and deleted + ** when the handle was closed and a second attempt to open the + ** database may succeed. */ +- rbuOpenDatabase(p, &bRetry); ++ rbuOpenDatabase(p, 0, &bRetry); + if( bRetry ){ +- rbuOpenDatabase(p, 0); ++ rbuOpenDatabase(p, 0, 0); + } + } + +@@ -199932,6 +225422,14 @@ static sqlite3rbu *openRbuHandle( + }else if( p->eStage==RBU_STAGE_MOVE ){ + /* no-op */ + }else if( p->eStage==RBU_STAGE_CKPT ){ ++ if( !rbuIsVacuum(p) && rbuExclusiveCheckpoint(p->dbMain) ){ ++ /* If the rbu_exclusive_checkpoint=1 URI parameter was specified ++ ** and an incremental checkpoint is being resumed, attempt an ++ ** exclusive lock on the db file. If this fails, so be it. */ ++ p->eStage = RBU_STAGE_DONE; ++ rbuLockDatabase(p->dbMain); ++ p->eStage = RBU_STAGE_CKPT; ++ } + rbuSetupCheckpoint(p, pState); + }else if( p->eStage==RBU_STAGE_DONE ){ + p->rc = SQLITE_DONE; +@@ -199969,7 +225467,6 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open( + const char *zState + ){ + if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); } +- /* TODO: Check that zTarget and zRbu are non-NULL */ + return openRbuHandle(zTarget, zRbu, zState); + } + +@@ -199982,7 +225479,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( + ){ + if( zTarget==0 ){ return rbuMisuseError(); } + if( zState ){ +- int n = strlen(zState); ++ size_t n = strlen(zState); + if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ + return rbuMisuseError(); + } +@@ -200194,6 +225691,55 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){ + return rc; + } + ++/* ++** Default xRename callback for RBU. ++*/ ++static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){ ++ int rc = SQLITE_OK; ++ UNUSED_PARAMETER(pArg); ++#if defined(_WIN32_WCE) ++ { ++ LPWSTR zWideOld; ++ LPWSTR zWideNew; ++ ++ zWideOld = rbuWinUtf8ToUnicode(zOld); ++ if( zWideOld ){ ++ zWideNew = rbuWinUtf8ToUnicode(zNew); ++ if( zWideNew ){ ++ if( MoveFileW(zWideOld, zWideNew) ){ ++ rc = SQLITE_OK; ++ }else{ ++ rc = SQLITE_IOERR; ++ } ++ sqlite3_free(zWideNew); ++ }else{ ++ rc = SQLITE_IOERR_NOMEM; ++ } ++ sqlite3_free(zWideOld); ++ }else{ ++ rc = SQLITE_IOERR_NOMEM; ++ } ++ } ++#else ++ rc = rename(zOld, zNew) ? SQLITE_IOERR : SQLITE_OK; ++#endif ++ return rc; ++} ++ ++SQLITE_API void sqlite3rbu_rename_handler( ++ sqlite3rbu *pRbu, ++ void *pArg, ++ int (*xRename)(void *pArg, const char *zOld, const char *zNew) ++){ ++ if( xRename ){ ++ pRbu->xRename = xRename; ++ pRbu->pRenameArg = pArg; ++ }else{ ++ pRbu->xRename = xDefaultRename; ++ pRbu->pRenameArg = 0; ++ } ++} ++ + /************************************************************************** + ** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour + ** of a standard VFS in the following ways: +@@ -200250,7 +225796,7 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){ + ** database file are recorded. xShmLock() calls to unlock the same + ** locks are no-ops (so that once obtained, these locks are never + ** relinquished). Finally, calls to xSync() on the target database +-** file fail with SQLITE_INTERNAL errors. ++** file fail with SQLITE_NOTICE errors. + */ + + static void rbuUnlockShm(rbu_file *p){ +@@ -200359,9 +225905,12 @@ static int rbuVfsClose(sqlite3_file *pFile){ + sqlite3_free(p->zDel); + + if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ ++ const sqlite3_io_methods *pMeth = p->pReal->pMethods; + rbuMainlistRemove(p); + rbuUnlockShm(p); +- p->pReal->pMethods->xShmUnmap(p->pReal, 0); ++ if( pMeth->iVersion>1 && pMeth->xShmUnmap ){ ++ pMeth->xShmUnmap(p->pReal, 0); ++ } + } + else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){ + rbuUpdateTempSize(p, 0); +@@ -200529,7 +226078,7 @@ static int rbuVfsSync(sqlite3_file *pFile, int flags){ + rbu_file *p = (rbu_file *)pFile; + if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){ + if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ +- return SQLITE_INTERNAL; ++ return SQLITE_NOTICE_RBU; + } + return SQLITE_OK; + } +@@ -200546,7 +226095,7 @@ static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + + /* If this is an RBU vacuum operation and this is the target database, + ** pretend that it has at least one page. Otherwise, SQLite will not +- ** check for the existance of a *-wal file. rbuVfsRead() contains ++ ** check for the existence of a *-wal file. rbuVfsRead() contains + ** similar logic. */ + if( rc==SQLITE_OK && *pSize==0 + && p->pRbu && rbuIsVacuum(p->pRbu) +@@ -200680,22 +226229,24 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ + #endif + + assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); +- if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){ +- /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from +- ** taking this lock also prevents any checkpoints from occurring. +- ** todo: really, it's not clear why this might occur, as +- ** wal_autocheckpoint ought to be turned off. */ ++ if( pRbu && ( ++ pRbu->eStage==RBU_STAGE_OAL ++ || pRbu->eStage==RBU_STAGE_MOVE ++ || pRbu->eStage==RBU_STAGE_DONE ++ )){ ++ /* Prevent SQLite from taking a shm-lock on the target file when it ++ ** is supplying heap memory to the upper layer in place of *-shm ++ ** segments. */ + if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY; + }else{ + int bCapture = 0; + if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){ + bCapture = 1; + } +- + if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){ + rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); + if( bCapture && rc==SQLITE_OK ){ +- pRbu->mLock |= (1 << ofst); ++ pRbu->mLock |= ((1<pRealVfs; + rbu_file *pFd = (rbu_file *)pFile; +@@ -200842,28 +226412,14 @@ static int rbuVfsOpen( + rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0); + if( pDb ){ + if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ +- /* This call is to open a *-wal file. Intead, open the *-oal. This +- ** code ensures that the string passed to xOpen() is terminated by a +- ** pair of '\0' bytes in case the VFS attempts to extract a URI +- ** parameter from it. */ +- const char *zBase = zName; +- size_t nCopy; +- char *zCopy; ++ /* This call is to open a *-wal file. Intead, open the *-oal. */ ++ size_t nOpen; + if( rbuIsVacuum(pDb->pRbu) ){ +- zBase = sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); +- zBase = sqlite3_filename_wal(zBase); +- } +- nCopy = strlen(zBase); +- zCopy = sqlite3_malloc64(nCopy+2); +- if( zCopy ){ +- memcpy(zCopy, zBase, nCopy); +- zCopy[nCopy-3] = 'o'; +- zCopy[nCopy] = '\0'; +- zCopy[nCopy+1] = '\0'; +- zOpen = (const char*)(pFd->zDel = zCopy); +- }else{ +- rc = SQLITE_NOMEM; ++ zOpen = sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); ++ zOpen = sqlite3_filename_wal(zOpen); + } ++ nOpen = strlen(zOpen); ++ ((char*)zOpen)[nOpen-3] = 'o'; + pFd->pRbu = pDb->pRbu; + } + pDb->pWalFd = pFd; +@@ -200886,10 +226442,15 @@ static int rbuVfsOpen( + rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags); + } + if( pFd->pReal->pMethods ){ ++ const sqlite3_io_methods *pMeth = pFd->pReal->pMethods; + /* The xOpen() operation has succeeded. Set the sqlite3_file.pMethods + ** pointer and, if the file is a main database file, link it into the + ** mutex protected linked list of all such files. */ +- pFile->pMethods = &rbuvfs_io_methods; ++ if( pMeth->iVersion<2 || pMeth->xShmLock==0 ){ ++ pFile->pMethods = &rbuvfs_io_methods1; ++ }else{ ++ pFile->pMethods = &rbuvfs_io_methods; ++ } + if( flags & SQLITE_OPEN_MAIN_DB ){ + rbuMainlistAdd(pFd); + } +@@ -201040,6 +226601,9 @@ static int rbuVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ + ** No-op. + */ + static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){ ++ UNUSED_PARAMETER(pVfs); ++ UNUSED_PARAMETER(a); ++ UNUSED_PARAMETER(b); + return 0; + } + +@@ -201184,6 +226748,15 @@ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){ + #if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) + ++/* ++** The pager and btree modules arrange objects in memory so that there are ++** always approximately 200 bytes of addressable memory following each page ++** buffer. This way small buffer overreads caused by corrupt database pages ++** do not cause undefined behaviour. This module pads each page buffer ++** by the following number of bytes for the same purpose. ++*/ ++#define DBSTAT_PAGE_PADDING_BYTES 256 ++ + /* + ** Page paths: + ** +@@ -201251,9 +226824,8 @@ struct StatCell { + /* Size information for a single btree page */ + struct StatPage { + u32 iPgno; /* Page number */ +- DbPage *pPg; /* Page content */ ++ u8 *aPg; /* Page buffer from sqlite3_malloc() */ + int iCell; /* Current cell */ +- + char *zPath; /* Path to this page */ + + /* Variables populated by statDecodePage(): */ +@@ -201314,6 +226886,7 @@ static int statConnect( + StatTable *pTab = 0; + int rc = SQLITE_OK; + int iDb; ++ (void)pAux; + + if( argc>=4 ){ + Token nm; +@@ -201367,6 +226940,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int iSchema = -1; + int iName = -1; + int iAgg = -1; ++ (void)tab; + + /* Look for a valid schema=? constraint. If found, change the idxNum to + ** 1 and request the value of that constraint be sent to xFilter. And +@@ -201428,6 +227002,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + pIdxInfo->orderByConsumed = 1; + pIdxInfo->idxNum |= 0x08; + } ++ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_HEX; + + return SQLITE_OK; + } +@@ -201465,18 +227040,25 @@ static void statClearCells(StatPage *p){ + } + + static void statClearPage(StatPage *p){ ++ u8 *aPg = p->aPg; + statClearCells(p); +- sqlite3PagerUnref(p->pPg); + sqlite3_free(p->zPath); + memset(p, 0, sizeof(StatPage)); ++ p->aPg = aPg; + } + + static void statResetCsr(StatCursor *pCsr){ + int i; +- sqlite3_reset(pCsr->pStmt); ++ /* In some circumstances, specifically if an OOM has occurred, the call ++ ** to sqlite3_reset() may cause the pager to be reset (emptied). It is ++ ** important that statClearPage() is called to free any page refs before ++ ** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */ + for(i=0; iaPage); i++){ + statClearPage(&pCsr->aPage[i]); ++ sqlite3_free(pCsr->aPage[i].aPg); ++ pCsr->aPage[i].aPg = 0; + } ++ sqlite3_reset(pCsr->pStmt); + pCsr->iPage = 0; + sqlite3_free(pCsr->zPath); + pCsr->zPath = 0; +@@ -201541,7 +227123,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ + int isLeaf; + int szPage; + +- u8 *aData = sqlite3PagerGetData(p->pPg); ++ u8 *aData = p->aPg; + u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; + + p->flags = aHdr[0]; +@@ -201612,7 +227194,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ + if( nPayload>(u32)nLocal ){ + int j; + int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); +- if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){ ++ if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){ + goto statPageIsCorrupt; + } + pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); +@@ -201671,6 +227253,38 @@ static void statSizeAndOffset(StatCursor *pCsr){ + } + } + ++/* ++** Load a copy of the page data for page iPg into the buffer belonging ++** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK ++** if successful, or an SQLite error code otherwise. ++*/ ++static int statGetPage( ++ Btree *pBt, /* Load page from this b-tree */ ++ u32 iPg, /* Page number to load */ ++ StatPage *pPg /* Load page into this object */ ++){ ++ int pgsz = sqlite3BtreeGetPageSize(pBt); ++ DbPage *pDbPage = 0; ++ int rc; ++ ++ if( pPg->aPg==0 ){ ++ pPg->aPg = (u8*)sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES); ++ if( pPg->aPg==0 ){ ++ return SQLITE_NOMEM_BKPT; ++ } ++ memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES); ++ } ++ ++ rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPg, &pDbPage, 0); ++ if( rc==SQLITE_OK ){ ++ const u8 *a = sqlite3PagerGetData(pDbPage); ++ memcpy(pPg->aPg, a, pgsz); ++ sqlite3PagerUnref(pDbPage); ++ } ++ ++ return rc; ++} ++ + /* + ** Move a DBSTAT cursor to the next entry. Normally, the next + ** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0), +@@ -201689,7 +227303,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){ + pCsr->zPath = 0; + + statNextRestart: +- if( pCsr->aPage[0].pPg==0 ){ ++ if( pCsr->iPage<0 ){ + /* Start measuring space on the next btree */ + statResetCounts(pCsr); + rc = sqlite3_step(pCsr->pStmt); +@@ -201701,7 +227315,7 @@ statNextRestart: + pCsr->isEof = 1; + return sqlite3_reset(pCsr->pStmt); + } +- rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0); ++ rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]); + pCsr->aPage[0].iPgno = iRoot; + pCsr->aPage[0].iCell = 0; + if( !pCsr->isAgg ){ +@@ -201752,9 +227366,8 @@ statNextRestart: + + if( !p->iRightChildPg || p->iCell>p->nCell ){ + statClearPage(p); +- if( pCsr->iPage>0 ){ +- pCsr->iPage--; +- }else if( pCsr->isAgg ){ ++ pCsr->iPage--; ++ if( pCsr->isAgg && pCsr->iPage<0 ){ + /* label-statNext-done: When computing aggregate space usage over + ** an entire btree, this is the exit point from this function */ + return SQLITE_OK; +@@ -201773,7 +227386,7 @@ statNextRestart: + }else{ + p[1].iPgno = p->aCell[p->iCell].iChildPg; + } +- rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0); ++ rc = statGetPage(pBt, p[1].iPgno, &p[1]); + pCsr->nPage++; + p[1].iCell = 0; + if( !pCsr->isAgg ){ +@@ -201854,6 +227467,8 @@ static int statFilter( + int iArg = 0; /* Count of argv[] parameters used so far */ + int rc = SQLITE_OK; /* Result of this operation */ + const char *zName = 0; /* Only provide analysis of this table */ ++ (void)argc; ++ (void)idxStr; + + statResetCsr(pCsr); + sqlite3_finalize(pCsr->pStmt); +@@ -201903,6 +227518,7 @@ static int statFilter( + } + + if( rc==SQLITE_OK ){ ++ pCsr->iPage = -1; + rc = statNext(pCursor); + } + return rc; +@@ -201936,16 +227552,16 @@ static int statColumn( + } + break; + case 4: /* ncell */ +- sqlite3_result_int(ctx, pCsr->nCell); ++ sqlite3_result_int64(ctx, pCsr->nCell); + break; + case 5: /* payload */ +- sqlite3_result_int(ctx, pCsr->nPayload); ++ sqlite3_result_int64(ctx, pCsr->nPayload); + break; + case 6: /* unused */ +- sqlite3_result_int(ctx, pCsr->nUnused); ++ sqlite3_result_int64(ctx, pCsr->nUnused); + break; + case 7: /* mx_payload */ +- sqlite3_result_int(ctx, pCsr->nMxPayload); ++ sqlite3_result_int64(ctx, pCsr->nMxPayload); + break; + case 8: /* pgoffset */ + if( !pCsr->isAgg ){ +@@ -201953,7 +227569,7 @@ static int statColumn( + } + break; + case 9: /* pgsize */ +- sqlite3_result_int(ctx, pCsr->szPage); ++ sqlite3_result_int64(ctx, pCsr->szPage); + break; + case 10: { /* schema */ + sqlite3 *db = sqlite3_context_db_handle(ctx); +@@ -202003,7 +227619,8 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ +- 0 /* xShadowName */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); + } +@@ -202043,7 +227660,13 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; } + ** + ** The data field of sqlite_dbpage table can be updated. The new + ** value must be a BLOB which is the correct page size, otherwise the +-** update fails. Rows may not be deleted or inserted. ++** update fails. INSERT operations also work, and operate as if they ++** where REPLACE. The size of the database can be extended by INSERT-ing ++** new pages on the end. ++** ++** Rows may not be deleted. However, doing an INSERT to page number N ++** with NULL page data causes the N-th page and all subsequent pages to be ++** deleted and the database to be truncated. + */ + + /* #include "sqliteInt.h" ** Requires access to internal data structures ** */ +@@ -202066,6 +227689,8 @@ struct DbpageCursor { + struct DbpageTable { + sqlite3_vtab base; /* Base class. Must be first */ + sqlite3 *db; /* The database */ ++ int iDbTrunc; /* Database to truncate */ ++ Pgno pgnoTrunc; /* Size to truncate to */ + }; + + /* Columns */ +@@ -202074,7 +227699,6 @@ struct DbpageTable { + #define DBPAGE_COLUMN_SCHEMA 2 + + +- + /* + ** Connect to or create a dbpagevfs virtual table. + */ +@@ -202087,8 +227711,13 @@ static int dbpageConnect( + ){ + DbpageTable *pTab = 0; + int rc = SQLITE_OK; ++ (void)pAux; ++ (void)argc; ++ (void)argv; ++ (void)pzErr; + + sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); ++ sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS); + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)"); + if( rc==SQLITE_OK ){ +@@ -202125,6 +227754,7 @@ static int dbpageDisconnect(sqlite3_vtab *pVtab){ + static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int i; + int iPlan = 0; ++ (void)tab; + + /* If there is a schema= constraint, it must be honored. Report a + ** ridiculously large estimated cost if the schema= constraint is +@@ -202239,6 +227869,8 @@ static int dbpageFilter( + sqlite3 *db = pTab->db; + Btree *pBt; + ++ (void)idxStr; ++ + /* Default setting is no rows of result */ + pCsr->pgno = 1; + pCsr->mxPgno = 0; +@@ -202253,7 +227885,7 @@ static int dbpageFilter( + pCsr->iDb = 0; + } + pBt = db->aDb[pCsr->iDb].pBt; +- if( pBt==0 ) return SQLITE_OK; ++ if( NEVER(pBt==0) ) return SQLITE_OK; + pCsr->pPager = sqlite3BtreePager(pBt); + pCsr->szPage = sqlite3BtreeGetPageSize(pBt); + pCsr->mxPgno = sqlite3BtreeLastPage(pBt); +@@ -202288,12 +227920,18 @@ static int dbpageColumn( + } + case 1: { /* data */ + DbPage *pDbPage = 0; +- rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); +- if( rc==SQLITE_OK ){ +- sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage, +- SQLITE_TRANSIENT); ++ if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){ ++ /* The pending byte page. Assume it is zeroed out. Attempting to ++ ** request this page from the page is an SQLITE_CORRUPT error. */ ++ sqlite3_result_zeroblob(ctx, pCsr->szPage); ++ }else{ ++ rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); ++ if( rc==SQLITE_OK ){ ++ sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage, ++ SQLITE_TRANSIENT); ++ } ++ sqlite3PagerUnref(pDbPage); + } +- sqlite3PagerUnref(pDbPage); + break; + } + default: { /* schema */ +@@ -202302,7 +227940,7 @@ static int dbpageColumn( + break; + } + } +- return SQLITE_OK; ++ return rc; + } + + static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ +@@ -202311,6 +227949,24 @@ static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + return SQLITE_OK; + } + ++/* ++** Open write transactions. Since we do not know in advance which database ++** files will be written by the sqlite_dbpage virtual table, start a write ++** transaction on them all. ++** ++** Return SQLITE_OK if successful, or an SQLite error code otherwise. ++*/ ++static int dbpageBeginTrans(DbpageTable *pTab){ ++ sqlite3 *db = pTab->db; ++ int rc = SQLITE_OK; ++ int i; ++ for(i=0; rc==SQLITE_OK && inDb; i++){ ++ Btree *pBt = db->aDb[i].pBt; ++ if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0); ++ } ++ return rc; ++} ++ + static int dbpageUpdate( + sqlite3_vtab *pVtab, + int argc, +@@ -202322,12 +227978,13 @@ static int dbpageUpdate( + DbPage *pDbPage = 0; + int rc = SQLITE_OK; + char *zErr = 0; +- const char *zSchema; + int iDb; + Btree *pBt; + Pager *pPager; + int szPage; ++ int isInsert; + ++ (void)pRowid; + if( pTab->db->flags & SQLITE_Defensive ){ + zErr = "read-only"; + goto update_fail; +@@ -202336,19 +227993,29 @@ static int dbpageUpdate( + zErr = "cannot delete"; + goto update_fail; + } +- pgno = sqlite3_value_int(argv[0]); +- if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ +- zErr = "cannot insert"; +- goto update_fail; ++ if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ ++ pgno = (Pgno)sqlite3_value_int(argv[2]); ++ isInsert = 1; ++ }else{ ++ pgno = sqlite3_value_int(argv[0]); ++ if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ ++ zErr = "cannot insert"; ++ goto update_fail; ++ } ++ isInsert = 0; + } +- zSchema = (const char*)sqlite3_value_text(argv[4]); +- iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1; +- if( iDb<0 ){ +- zErr = "no such schema"; +- goto update_fail; ++ if( sqlite3_value_type(argv[4])==SQLITE_NULL ){ ++ iDb = 0; ++ }else{ ++ const char *zSchema = (const char*)sqlite3_value_text(argv[4]); ++ iDb = sqlite3FindDbName(pTab->db, zSchema); ++ if( iDb<0 ){ ++ zErr = "no such schema"; ++ goto update_fail; ++ } + } + pBt = pTab->db->aDb[iDb].pBt; +- if( pgno<1 || pBt==0 || pgno>(int)sqlite3BtreeLastPage(pBt) ){ ++ if( pgno<1 || NEVER(pBt==0) ){ + zErr = "bad page number"; + goto update_fail; + } +@@ -202356,50 +228023,83 @@ static int dbpageUpdate( + if( sqlite3_value_type(argv[3])!=SQLITE_BLOB + || sqlite3_value_bytes(argv[3])!=szPage + ){ +- zErr = "bad page value"; ++ if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){ ++ /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and ++ ** all subsequent pages to be deleted. */ ++ pTab->iDbTrunc = iDb; ++ pTab->pgnoTrunc = pgno-1; ++ pgno = 1; ++ }else{ ++ zErr = "bad page value"; ++ goto update_fail; ++ } ++ } ++ ++ if( dbpageBeginTrans(pTab)!=SQLITE_OK ){ ++ zErr = "failed to open transaction"; + goto update_fail; + } ++ + pPager = sqlite3BtreePager(pBt); + rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); + if( rc==SQLITE_OK ){ +- rc = sqlite3PagerWrite(pDbPage); +- if( rc==SQLITE_OK ){ +- memcpy(sqlite3PagerGetData(pDbPage), +- sqlite3_value_blob(argv[3]), +- szPage); ++ const void *pData = sqlite3_value_blob(argv[3]); ++ if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){ ++ unsigned char *aPage = sqlite3PagerGetData(pDbPage); ++ memcpy(aPage, pData, szPage); ++ pTab->pgnoTrunc = 0; + } ++ }else{ ++ pTab->pgnoTrunc = 0; + } + sqlite3PagerUnref(pDbPage); + return rc; + + update_fail: ++ pTab->pgnoTrunc = 0; + sqlite3_free(pVtab->zErrMsg); + pVtab->zErrMsg = sqlite3_mprintf("%s", zErr); + return SQLITE_ERROR; + } + +-/* Since we do not know in advance which database files will be +-** written by the sqlite_dbpage virtual table, start a write transaction +-** on them all. +-*/ + static int dbpageBegin(sqlite3_vtab *pVtab){ + DbpageTable *pTab = (DbpageTable *)pVtab; +- sqlite3 *db = pTab->db; +- int i; +- for(i=0; inDb; i++){ +- Btree *pBt = db->aDb[i].pBt; +- if( pBt ) sqlite3BtreeBeginTrans(pBt, 1, 0); ++ pTab->pgnoTrunc = 0; ++ return SQLITE_OK; ++} ++ ++/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT ++*/ ++static int dbpageSync(sqlite3_vtab *pVtab){ ++ DbpageTable *pTab = (DbpageTable *)pVtab; ++ if( pTab->pgnoTrunc>0 ){ ++ Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; ++ Pager *pPager = sqlite3BtreePager(pBt); ++ sqlite3BtreeEnter(pBt); ++ if( pTab->pgnoTruncpgnoTrunc); ++ } ++ sqlite3BtreeLeave(pBt); + } ++ pTab->pgnoTrunc = 0; + return SQLITE_OK; + } + ++/* Cancel any pending truncate. ++*/ ++static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){ ++ DbpageTable *pTab = (DbpageTable *)pVtab; ++ pTab->pgnoTrunc = 0; ++ (void)notUsed1; ++ return SQLITE_OK; ++} + + /* + ** Invoke this routine to register the "dbpage" virtual table module + */ + SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ + static sqlite3_module dbpage_module = { +- 0, /* iVersion */ ++ 2, /* iVersion */ + dbpageConnect, /* xCreate */ + dbpageConnect, /* xConnect */ + dbpageBestIndex, /* xBestIndex */ +@@ -202414,15 +228114,16 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ + dbpageRowid, /* xRowid - read data */ + dbpageUpdate, /* xUpdate */ + dbpageBegin, /* xBegin */ +- 0, /* xSync */ ++ dbpageSync, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ +- 0, /* xRollbackTo */ +- 0 /* xShadowName */ ++ dbpageRollbackTo, /* xRollbackTo */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); + } +@@ -202459,6 +228160,8 @@ typedef struct SessionInput SessionInput; + # endif + #endif + ++#define SESSIONS_ROWID "_rowid_" ++ + static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; + + typedef struct SessionHook SessionHook; +@@ -202476,12 +228179,16 @@ struct SessionHook { + struct sqlite3_session { + sqlite3 *db; /* Database handle session is attached to */ + char *zDb; /* Name of database session is attached to */ ++ int bEnableSize; /* True if changeset_size() enabled */ + int bEnable; /* True if currently recording */ + int bIndirect; /* True if all changes are indirect */ + int bAutoAttach; /* True to auto-attach tables */ ++ int bImplicitPK; /* True to handle tables with implicit PK */ + int rc; /* Non-zero if an error has occurred */ + void *pFilterCtx; /* First argument to pass to xTableFilter */ + int (*xTableFilter)(void *pCtx, const char *zTab); ++ i64 nMalloc; /* Number of bytes of data allocated */ ++ i64 nMaxChangesetSize; + sqlite3_value *pZeroBlob; /* Value containing X'' */ + sqlite3_session *pNext; /* Next session object on same db. */ + SessionTable *pTable; /* List of attached tables */ +@@ -202502,6 +228209,10 @@ struct SessionBuffer { + ** input data. Input data may be supplied either as a single large buffer + ** (e.g. sqlite3changeset_start()) or using a stream function (e.g. + ** sqlite3changeset_start_strm()). ++** ++** bNoDiscard: ++** If true, then the only time data is discarded is as a result of explicit ++** sessionDiscardData() calls. Not within every sessionInputBuffer() call. + */ + struct SessionInput { + int bNoDiscard; /* If true, do not discard in InputBuffer() */ +@@ -202524,6 +228235,7 @@ struct sqlite3_changeset_iter { + SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */ + int bPatchset; /* True if this is a patchset */ + int bInvert; /* True to invert changeset */ ++ int bSkipEmpty; /* Skip noop UPDATE changes */ + int rc; /* Iterator error code */ + sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ + char *zTab; /* Current table */ +@@ -202546,17 +228258,34 @@ struct sqlite3_changeset_iter { + ** The data associated with each hash-table entry is a structure containing + ** a subset of the initial values that the modified row contained at the + ** start of the session. Or no initial values if the row was inserted. ++** ++** pDfltStmt: ++** This is only used by the sqlite3changegroup_xxx() APIs, not by ++** regular sqlite3_session objects. It is a SELECT statement that ++** selects the default value for each table column. For example, ++** if the table is ++** ++** CREATE TABLE xx(a DEFAULT 1, b, c DEFAULT 'abc') ++** ++** then this variable is the compiled version of: ++** ++** SELECT 1, NULL, 'abc' + */ + struct SessionTable { + SessionTable *pNext; + char *zName; /* Local name of table */ +- int nCol; /* Number of columns in table zName */ ++ int nCol; /* Number of non-hidden columns */ ++ int nTotalCol; /* Number of columns including hidden */ + int bStat1; /* True if this is sqlite_stat1 */ ++ int bRowid; /* True if this table uses rowid for PK */ + const char **azCol; /* Column names */ ++ const char **azDflt; /* Default value expressions */ ++ int *aiIdx; /* Index to pass to xNew/xOld */ + u8 *abPK; /* Array of primary key flags */ + int nEntry; /* Total number of entries in hash table */ + int nChange; /* Size of apChange[] array */ + SessionChange **apChange; /* Hash table buckets */ ++ sqlite3_stmt *pDfltStmt; + }; + + /* +@@ -202723,8 +228452,10 @@ struct SessionTable { + ** this structure stored in a SessionTable.aChange[] hash table. + */ + struct SessionChange { +- int op; /* One of UPDATE, DELETE, INSERT */ +- int bIndirect; /* True if this change is "indirect" */ ++ u8 op; /* One of UPDATE, DELETE, INSERT */ ++ u8 bIndirect; /* True if this change is "indirect" */ ++ u16 nRecordField; /* Number of fields in aRecord[] */ ++ int nMaxSize; /* Max size of eventual changeset record */ + int nRecord; /* Number of bytes in buffer aRecord[] */ + u8 *aRecord; /* Buffer containing old.* record */ + SessionChange *pNext; /* For hash-table collisions */ +@@ -202749,7 +228480,7 @@ static int sessionVarintLen(int iVal){ + ** Read a varint value from aBuf[] into *piVal. Return the number of + ** bytes read. + */ +-static int sessionVarintGet(u8 *aBuf, int *piVal){ ++static int sessionVarintGet(const u8 *aBuf, int *piVal){ + return getVarint32(aBuf, *piVal); + } + +@@ -202849,7 +228580,7 @@ static int sessionSerializeValue( + + if( aBuf ){ + sessionVarintPut(&aBuf[1], n); +- if( n ) memcpy(&aBuf[nVarint + 1], z, n); ++ if( n>0 ) memcpy(&aBuf[nVarint + 1], z, n); + } + + nByte = 1 + nVarint + n; +@@ -202865,6 +228596,26 @@ static int sessionSerializeValue( + return SQLITE_OK; + } + ++/* ++** Allocate and return a pointer to a buffer nByte bytes in size. If ++** pSession is not NULL, increase the sqlite3_session.nMalloc variable ++** by the number of bytes allocated. ++*/ ++static void *sessionMalloc64(sqlite3_session *pSession, i64 nByte){ ++ void *pRet = sqlite3_malloc64(nByte); ++ if( pSession ) pSession->nMalloc += sqlite3_msize(pRet); ++ return pRet; ++} ++ ++/* ++** Free buffer pFree, which must have been allocated by an earlier ++** call to sessionMalloc64(). If pSession is not NULL, decrease the ++** sqlite3_session.nMalloc counter by the number of bytes freed. ++*/ ++static void sessionFree(sqlite3_session *pSession, void *pFree){ ++ if( pSession ) pSession->nMalloc -= sqlite3_msize(pFree); ++ sqlite3_free(pFree); ++} + + /* + ** This macro is used to calculate hash key values for data structures. In +@@ -202923,6 +228674,7 @@ static unsigned int sessionHashAppendType(unsigned int h, int eType){ + */ + static int sessionPreupdateHash( + sqlite3_session *pSession, /* Session object that owns pTab */ ++ i64 iRowid, + SessionTable *pTab, /* Session table handle */ + int bNew, /* True to hash the new.* PK */ + int *piHash, /* OUT: Hash value */ +@@ -202931,48 +228683,53 @@ static int sessionPreupdateHash( + unsigned int h = 0; /* Hash value to return */ + int i; /* Used to iterate through columns */ + +- assert( *pbNullPK==0 ); +- assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); +- for(i=0; inCol; i++){ +- if( pTab->abPK[i] ){ +- int rc; +- int eType; +- sqlite3_value *pVal; +- +- if( bNew ){ +- rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); +- }else{ +- rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); +- } +- if( rc!=SQLITE_OK ) return rc; +- +- eType = sqlite3_value_type(pVal); +- h = sessionHashAppendType(h, eType); +- if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ +- i64 iVal; +- if( eType==SQLITE_INTEGER ){ +- iVal = sqlite3_value_int64(pVal); ++ assert( pTab->nTotalCol==pSession->hook.xCount(pSession->hook.pCtx) ); ++ if( pTab->bRowid ){ ++ h = sessionHashAppendI64(h, iRowid); ++ }else{ ++ assert( *pbNullPK==0 ); ++ for(i=0; inCol; i++){ ++ if( pTab->abPK[i] ){ ++ int rc; ++ int eType; ++ sqlite3_value *pVal; ++ int iIdx = pTab->aiIdx[i]; ++ ++ if( bNew ){ ++ rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); + }else{ +- double rVal = sqlite3_value_double(pVal); +- assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); +- memcpy(&iVal, &rVal, 8); ++ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); + } +- h = sessionHashAppendI64(h, iVal); +- }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ +- const u8 *z; +- int n; +- if( eType==SQLITE_TEXT ){ +- z = (const u8 *)sqlite3_value_text(pVal); ++ if( rc!=SQLITE_OK ) return rc; ++ ++ eType = sqlite3_value_type(pVal); ++ h = sessionHashAppendType(h, eType); ++ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ ++ i64 iVal; ++ if( eType==SQLITE_INTEGER ){ ++ iVal = sqlite3_value_int64(pVal); ++ }else{ ++ double rVal = sqlite3_value_double(pVal); ++ assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); ++ memcpy(&iVal, &rVal, 8); ++ } ++ h = sessionHashAppendI64(h, iVal); ++ }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ ++ const u8 *z; ++ int n; ++ if( eType==SQLITE_TEXT ){ ++ z = (const u8 *)sqlite3_value_text(pVal); ++ }else{ ++ z = (const u8 *)sqlite3_value_blob(pVal); ++ } ++ n = sqlite3_value_bytes(pVal); ++ if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; ++ h = sessionHashAppendBlob(h, n, z); + }else{ +- z = (const u8 *)sqlite3_value_blob(pVal); ++ assert( eType==SQLITE_NULL ); ++ assert( pTab->bStat1==0 || i!=1 ); ++ *pbNullPK = 1; + } +- n = sqlite3_value_bytes(pVal); +- if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; +- h = sessionHashAppendBlob(h, n, z); +- }else{ +- assert( eType==SQLITE_NULL ); +- assert( pTab->bStat1==0 || i!=1 ); +- *pbNullPK = 1; + } + } + } +@@ -202986,9 +228743,11 @@ static int sessionPreupdateHash( + ** Return the number of bytes of space occupied by the value (including + ** the type byte). + */ +-static int sessionSerialLen(u8 *a){ +- int e = *a; ++static int sessionSerialLen(const u8 *a){ ++ int e; + int n; ++ assert( a!=0 ); ++ e = *a; + if( e==0 || e==0xFF ) return 1; + if( e==SQLITE_NULL ) return 1; + if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; +@@ -203255,6 +229014,7 @@ static int sessionMergeUpdate( + */ + static int sessionPreupdateEqual( + sqlite3_session *pSession, /* Session object that owns SessionTable */ ++ i64 iRowid, /* Rowid value if pTab->bRowid */ + SessionTable *pTab, /* Table associated with change */ + SessionChange *pChange, /* Change to compare to */ + int op /* Current pre-update operation */ +@@ -203262,6 +229022,11 @@ static int sessionPreupdateEqual( + int iCol; /* Used to iterate through columns */ + u8 *a = pChange->aRecord; /* Cursor used to scan change record */ + ++ if( pTab->bRowid ){ ++ if( a[0]!=SQLITE_INTEGER ) return 0; ++ return sessionGetI64(&a[1])==iRowid; ++ } ++ + assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); + for(iCol=0; iColnCol; iCol++){ + if( !pTab->abPK[iCol] ){ +@@ -203270,6 +229035,7 @@ static int sessionPreupdateEqual( + sqlite3_value *pVal; /* Value returned by preupdate_new/old */ + int rc; /* Error code from preupdate_new/old */ + int eType = *a++; /* Type of value from change record */ ++ int iIdx = pTab->aiIdx[iCol]; + + /* The following calls to preupdate_new() and preupdate_old() can not + ** fail. This is because they cache their return values, and by the +@@ -203278,12 +229044,13 @@ static int sessionPreupdateEqual( + ** this (that the method has already been called). */ + if( op==SQLITE_INSERT ){ + /* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */ +- rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal); ++ rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); + }else{ + /* assert( db->pPreUpdate->pUnpacked ); */ +- rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal); ++ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); + } + assert( rc==SQLITE_OK ); ++ (void)rc; /* Suppress warning about unused variable */ + if( sqlite3_value_type(pVal)!=eType ) return 0; + + /* A SessionChange object never has a NULL value in a PK column */ +@@ -203332,13 +229099,19 @@ static int sessionPreupdateEqual( + ** Growing the hash table in this case is a performance optimization only, + ** it is not required for correct operation. + */ +-static int sessionGrowHash(int bPatchset, SessionTable *pTab){ ++static int sessionGrowHash( ++ sqlite3_session *pSession, /* For memory accounting. May be NULL */ ++ int bPatchset, ++ SessionTable *pTab ++){ + if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){ + int i; + SessionChange **apNew; + sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128); + +- apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew); ++ apNew = (SessionChange**)sessionMalloc64( ++ pSession, sizeof(SessionChange*) * nNew ++ ); + if( apNew==0 ){ + if( pTab->nChange==0 ){ + return SQLITE_ERROR; +@@ -203359,7 +229132,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ + } + } + +- sqlite3_free(pTab->apChange); ++ sessionFree(pSession, pTab->apChange); + pTab->nChange = nNew; + pTab->apChange = apNew; + } +@@ -203380,26 +229153,32 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ + ** + ** For example, if the table is declared as: + ** +-** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z)); ++** CREATE TABLE tbl1(w, x DEFAULT 'abc', y, z, PRIMARY KEY(w, z)); + ** +-** Then the four output variables are populated as follows: ++** Then the five output variables are populated as follows: + ** + ** *pnCol = 4 + ** *pzTab = "tbl1" + ** *pazCol = {"w", "x", "y", "z"} ++** *pazDflt = {NULL, 'abc', NULL, NULL} + ** *pabPK = {1, 0, 0, 1} + ** + ** All returned buffers are part of the same single allocation, which must + ** be freed using sqlite3_free() by the caller + */ + static int sessionTableInfo( ++ sqlite3_session *pSession, /* For memory accounting. May be NULL */ + sqlite3 *db, /* Database connection */ + const char *zDb, /* Name of attached database (e.g. "main") */ + const char *zThis, /* Table name */ + int *pnCol, /* OUT: number of columns */ ++ int *pnTotalCol, /* OUT: number of hidden columns */ + const char **pzTab, /* OUT: Copy of zThis */ + const char ***pazCol, /* OUT: Array of column names for table */ +- u8 **pabPK /* OUT: Array of booleans - true for PK col */ ++ const char ***pazDflt, /* OUT: Array of default value expressions */ ++ int **paiIdx, /* OUT: Array of xNew/xOld indexes */ ++ u8 **pabPK, /* OUT: Array of booleans - true for PK col */ ++ int *pbRowid /* OUT: True if only PK is a rowid */ + ){ + char *zPragma; + sqlite3_stmt *pStmt; +@@ -203410,19 +229189,30 @@ static int sessionTableInfo( + int i; + u8 *pAlloc = 0; + char **azCol = 0; ++ char **azDflt = 0; + u8 *abPK = 0; ++ int *aiIdx = 0; ++ int bRowid = 0; /* Set to true to use rowid as PK */ + + assert( pazCol && pabPK ); + ++ *pazCol = 0; ++ *pabPK = 0; ++ *pnCol = 0; ++ if( pnTotalCol ) *pnTotalCol = 0; ++ if( paiIdx ) *paiIdx = 0; ++ if( pzTab ) *pzTab = 0; ++ if( pazDflt ) *pazDflt = 0; ++ + nThis = sqlite3Strlen30(zThis); + if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){ + rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0); + if( rc==SQLITE_OK ){ + /* For sqlite_stat1, pretend that (tbl,idx) is the PRIMARY KEY. */ + zPragma = sqlite3_mprintf( +- "SELECT 0, 'tbl', '', 0, '', 1 UNION ALL " +- "SELECT 1, 'idx', '', 0, '', 2 UNION ALL " +- "SELECT 2, 'stat', '', 0, '', 0" ++ "SELECT 0, 'tbl', '', 0, '', 1, 0 UNION ALL " ++ "SELECT 1, 'idx', '', 0, '', 2, 0 UNION ALL " ++ "SELECT 2, 'stat', '', 0, '', 0, 0" + ); + }else if( rc==SQLITE_ERROR ){ + zPragma = sqlite3_mprintf(""); +@@ -203430,32 +229220,47 @@ static int sessionTableInfo( + return rc; + } + }else{ +- zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); ++ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_xinfo('%q')", zDb, zThis); ++ } ++ if( !zPragma ){ ++ return SQLITE_NOMEM; + } +- if( !zPragma ) return SQLITE_NOMEM; + + rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0); + sqlite3_free(zPragma); +- if( rc!=SQLITE_OK ) return rc; ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } + + nByte = nThis + 1; ++ bRowid = (pbRowid!=0); + while( SQLITE_ROW==sqlite3_step(pStmt) ){ +- nByte += sqlite3_column_bytes(pStmt, 1); +- nDbCol++; ++ nByte += sqlite3_column_bytes(pStmt, 1); /* name */ ++ nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */ ++ if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ ++ nDbCol++; ++ } ++ if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */ + } ++ if( nDbCol==0 ) bRowid = 0; ++ nDbCol += bRowid; ++ nByte += strlen(SESSIONS_ROWID); + rc = sqlite3_reset(pStmt); + + if( rc==SQLITE_OK ){ +- nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); +- pAlloc = sqlite3_malloc64(nByte); ++ nByte += nDbCol * (sizeof(const char *)*2 +sizeof(int)+sizeof(u8) + 1 + 1); ++ pAlloc = sessionMalloc64(pSession, nByte); + if( pAlloc==0 ){ + rc = SQLITE_NOMEM; ++ }else{ ++ memset(pAlloc, 0, nByte); + } + } + if( rc==SQLITE_OK ){ + azCol = (char **)pAlloc; +- pAlloc = (u8 *)&azCol[nDbCol]; +- abPK = (u8 *)pAlloc; ++ azDflt = (char**)&azCol[nDbCol]; ++ aiIdx = (int*)&azDflt[nDbCol]; ++ abPK = (u8 *)&aiIdx[nDbCol]; + pAlloc = &abPK[nDbCol]; + if( pzTab ){ + memcpy(pAlloc, zThis, nThis+1); +@@ -203464,43 +229269,63 @@ static int sessionTableInfo( + } + + i = 0; +- while( SQLITE_ROW==sqlite3_step(pStmt) ){ +- int nName = sqlite3_column_bytes(pStmt, 1); +- const unsigned char *zName = sqlite3_column_text(pStmt, 1); +- if( zName==0 ) break; +- memcpy(pAlloc, zName, nName+1); +- azCol[i] = (char *)pAlloc; ++ if( bRowid ){ ++ size_t nName = strlen(SESSIONS_ROWID); ++ memcpy(pAlloc, SESSIONS_ROWID, nName+1); ++ azCol[i] = (char*)pAlloc; + pAlloc += nName+1; +- abPK[i] = sqlite3_column_int(pStmt, 5); ++ abPK[i] = 1; ++ aiIdx[i] = -1; + i++; + } ++ while( SQLITE_ROW==sqlite3_step(pStmt) ){ ++ if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ ++ int nName = sqlite3_column_bytes(pStmt, 1); ++ int nDflt = sqlite3_column_bytes(pStmt, 4); ++ const unsigned char *zName = sqlite3_column_text(pStmt, 1); ++ const unsigned char *zDflt = sqlite3_column_text(pStmt, 4); ++ ++ if( zName==0 ) break; ++ memcpy(pAlloc, zName, nName+1); ++ azCol[i] = (char *)pAlloc; ++ pAlloc += nName+1; ++ if( zDflt ){ ++ memcpy(pAlloc, zDflt, nDflt+1); ++ azDflt[i] = (char *)pAlloc; ++ pAlloc += nDflt+1; ++ }else{ ++ azDflt[i] = 0; ++ } ++ abPK[i] = sqlite3_column_int(pStmt, 5); ++ aiIdx[i] = sqlite3_column_int(pStmt, 0); ++ i++; ++ } ++ if( pnTotalCol ) (*pnTotalCol)++; ++ } + rc = sqlite3_reset(pStmt); +- + } + + /* If successful, populate the output variables. Otherwise, zero them and + ** free any allocation made. An error code will be returned in this case. + */ + if( rc==SQLITE_OK ){ +- *pazCol = (const char **)azCol; ++ *pazCol = (const char**)azCol; ++ if( pazDflt ) *pazDflt = (const char**)azDflt; + *pabPK = abPK; + *pnCol = nDbCol; ++ if( paiIdx ) *paiIdx = aiIdx; + }else{ +- *pazCol = 0; +- *pabPK = 0; +- *pnCol = 0; +- if( pzTab ) *pzTab = 0; +- sqlite3_free(azCol); ++ sessionFree(pSession, azCol); + } ++ if( pbRowid ) *pbRowid = bRowid; + sqlite3_finalize(pStmt); + return rc; + } + + /* +-** This function is only called from within a pre-update handler for a +-** write to table pTab, part of session pSession. If this is the first +-** write to this table, initalize the SessionTable.nCol, azCol[] and +-** abPK[] arrays accordingly. ++** This function is called to initialize the SessionTable.nCol, azCol[] ++** abPK[] and azDflt[] members of SessionTable object pTab. If these ++** fields are already initialized, this function is a no-op. + ** + ** If an error occurs, an error code is stored in sqlite3_session.rc and + ** non-zero returned. Or, if no error occurs but the table has no primary +@@ -203508,14 +229333,25 @@ static int sessionTableInfo( + ** indicate that updates on this table should be ignored. SessionTable.abPK + ** is set to NULL in this case. + */ +-static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ ++static int sessionInitTable( ++ sqlite3_session *pSession, /* Optional session handle */ ++ SessionTable *pTab, /* Table object to initialize */ ++ sqlite3 *db, /* Database handle to read schema from */ ++ const char *zDb /* Name of db - "main", "temp" etc. */ ++){ ++ int rc = SQLITE_OK; ++ + if( pTab->nCol==0 ){ + u8 *abPK; + assert( pTab->azCol==0 || pTab->abPK==0 ); +- pSession->rc = sessionTableInfo(pSession->db, pSession->zDb, +- pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK ++ sqlite3_free(pTab->azCol); ++ pTab->abPK = 0; ++ rc = sessionTableInfo(pSession, db, zDb, ++ pTab->zName, &pTab->nCol, &pTab->nTotalCol, 0, &pTab->azCol, ++ &pTab->azDflt, &pTab->aiIdx, &abPK, ++ ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0) + ); +- if( pSession->rc==SQLITE_OK ){ ++ if( rc==SQLITE_OK ){ + int i; + for(i=0; inCol; i++){ + if( abPK[i] ){ +@@ -203526,9 +229362,326 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ + if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){ + pTab->bStat1 = 1; + } ++ ++ if( pSession && pSession->bEnableSize ){ ++ pSession->nMaxChangesetSize += ( ++ 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1 ++ ); ++ } ++ } ++ } ++ ++ if( pSession ){ ++ pSession->rc = rc; ++ return (rc || pTab->abPK==0); ++ } ++ return rc; ++} ++ ++/* ++** Re-initialize table object pTab. ++*/ ++static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){ ++ int nCol = 0; ++ int nTotalCol = 0; ++ const char **azCol = 0; ++ const char **azDflt = 0; ++ int *aiIdx = 0; ++ u8 *abPK = 0; ++ int bRowid = 0; ++ ++ assert( pSession->rc==SQLITE_OK ); ++ ++ pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, ++ pTab->zName, &nCol, &nTotalCol, 0, &azCol, &azDflt, &aiIdx, &abPK, ++ (pSession->bImplicitPK ? &bRowid : 0) ++ ); ++ if( pSession->rc==SQLITE_OK ){ ++ if( pTab->nCol>nCol || pTab->bRowid!=bRowid ){ ++ pSession->rc = SQLITE_SCHEMA; ++ }else{ ++ int ii; ++ int nOldCol = pTab->nCol; ++ for(ii=0; iinCol ){ ++ if( pTab->abPK[ii]!=abPK[ii] ){ ++ pSession->rc = SQLITE_SCHEMA; ++ } ++ }else if( abPK[ii] ){ ++ pSession->rc = SQLITE_SCHEMA; ++ } ++ } ++ ++ if( pSession->rc==SQLITE_OK ){ ++ const char **a = pTab->azCol; ++ pTab->azCol = azCol; ++ pTab->nCol = nCol; ++ pTab->nTotalCol = nTotalCol; ++ pTab->azDflt = azDflt; ++ pTab->abPK = abPK; ++ pTab->aiIdx = aiIdx; ++ azCol = a; ++ } ++ if( pSession->bEnableSize ){ ++ pSession->nMaxChangesetSize += (nCol - nOldCol); ++ pSession->nMaxChangesetSize += sessionVarintLen(nCol); ++ pSession->nMaxChangesetSize -= sessionVarintLen(nOldCol); ++ } ++ } ++ } ++ ++ sqlite3_free((char*)azCol); ++ return pSession->rc; ++} ++ ++/* ++** Session-change object (*pp) contains an old.* record with fewer than ++** nCol fields. This function updates it with the default values for ++** the missing fields. ++*/ ++static void sessionUpdateOneChange( ++ sqlite3_session *pSession, /* For memory accounting */ ++ int *pRc, /* IN/OUT: Error code */ ++ SessionChange **pp, /* IN/OUT: Change object to update */ ++ int nCol, /* Number of columns now in table */ ++ sqlite3_stmt *pDflt /* SELECT */ ++){ ++ SessionChange *pOld = *pp; ++ ++ while( pOld->nRecordFieldnRecordField; ++ int eType = sqlite3_column_type(pDflt, iField); ++ switch( eType ){ ++ case SQLITE_NULL: ++ nIncr = 1; ++ break; ++ case SQLITE_INTEGER: ++ case SQLITE_FLOAT: ++ nIncr = 9; ++ break; ++ default: { ++ int n = sqlite3_column_bytes(pDflt, iField); ++ nIncr = 1 + sessionVarintLen(n) + n; ++ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); ++ break; ++ } ++ } ++ ++ nByte = nIncr + (sizeof(SessionChange) + pOld->nRecord); ++ pNew = sessionMalloc64(pSession, nByte); ++ if( pNew==0 ){ ++ *pRc = SQLITE_NOMEM; ++ return; ++ }else{ ++ memcpy(pNew, pOld, sizeof(SessionChange)); ++ pNew->aRecord = (u8*)&pNew[1]; ++ memcpy(pNew->aRecord, pOld->aRecord, pOld->nRecord); ++ pNew->aRecord[pNew->nRecord++] = (u8)eType; ++ switch( eType ){ ++ case SQLITE_INTEGER: { ++ i64 iVal = sqlite3_column_int64(pDflt, iField); ++ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal); ++ pNew->nRecord += 8; ++ break; ++ } ++ ++ case SQLITE_FLOAT: { ++ double rVal = sqlite3_column_double(pDflt, iField); ++ i64 iVal = 0; ++ memcpy(&iVal, &rVal, sizeof(rVal)); ++ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal); ++ pNew->nRecord += 8; ++ break; ++ } ++ ++ case SQLITE_TEXT: { ++ int n = sqlite3_column_bytes(pDflt, iField); ++ const char *z = (const char*)sqlite3_column_text(pDflt, iField); ++ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n); ++ memcpy(&pNew->aRecord[pNew->nRecord], z, n); ++ pNew->nRecord += n; ++ break; ++ } ++ ++ case SQLITE_BLOB: { ++ int n = sqlite3_column_bytes(pDflt, iField); ++ const u8 *z = (const u8*)sqlite3_column_blob(pDflt, iField); ++ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n); ++ memcpy(&pNew->aRecord[pNew->nRecord], z, n); ++ pNew->nRecord += n; ++ break; ++ } ++ ++ default: ++ assert( eType==SQLITE_NULL ); ++ break; ++ } ++ ++ sessionFree(pSession, pOld); ++ *pp = pOld = pNew; ++ pNew->nRecordField++; ++ pNew->nMaxSize += nIncr; ++ if( pSession ){ ++ pSession->nMaxChangesetSize += nIncr; ++ } ++ } ++ } ++} ++ ++/* ++** Ensure that there is room in the buffer to append nByte bytes of data. ++** If not, use sqlite3_realloc() to grow the buffer so that there is. ++** ++** If successful, return zero. Otherwise, if an OOM condition is encountered, ++** set *pRc to SQLITE_NOMEM and return non-zero. ++*/ ++static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){ ++#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1) ++ i64 nReq = p->nBuf + nByte; ++ if( *pRc==SQLITE_OK && nReq>p->nAlloc ){ ++ u8 *aNew; ++ i64 nNew = p->nAlloc ? p->nAlloc : 128; ++ ++ do { ++ nNew = nNew*2; ++ }while( nNewSESSION_MAX_BUFFER_SZ ){ ++ nNew = SESSION_MAX_BUFFER_SZ; ++ if( nNewaBuf, nNew); ++ if( 0==aNew ){ ++ *pRc = SQLITE_NOMEM; ++ }else{ ++ p->aBuf = aNew; ++ p->nAlloc = nNew; ++ } ++ } ++ return (*pRc!=SQLITE_OK); ++} ++ ++ ++/* ++** This function is a no-op if *pRc is other than SQLITE_OK when it is ++** called. Otherwise, append a string to the buffer. All bytes in the string ++** up to (but not including) the nul-terminator are written to the buffer. ++** ++** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before ++** returning. ++*/ ++static void sessionAppendStr( ++ SessionBuffer *p, ++ const char *zStr, ++ int *pRc ++){ ++ int nStr = sqlite3Strlen30(zStr); ++ if( 0==sessionBufferGrow(p, nStr+1, pRc) ){ ++ memcpy(&p->aBuf[p->nBuf], zStr, nStr); ++ p->nBuf += nStr; ++ p->aBuf[p->nBuf] = 0x00; ++ } ++} ++ ++/* ++** Format a string using printf() style formatting and then append it to the ++** buffer using sessionAppendString(). ++*/ ++static void sessionAppendPrintf( ++ SessionBuffer *p, /* Buffer to append to */ ++ int *pRc, ++ const char *zFmt, ++ ... ++){ ++ if( *pRc==SQLITE_OK ){ ++ char *zApp = 0; ++ va_list ap; ++ va_start(ap, zFmt); ++ zApp = sqlite3_vmprintf(zFmt, ap); ++ if( zApp==0 ){ ++ *pRc = SQLITE_NOMEM; ++ }else{ ++ sessionAppendStr(p, zApp, pRc); ++ } ++ va_end(ap); ++ sqlite3_free(zApp); ++ } ++} ++ ++/* ++** Prepare a statement against database handle db that SELECTs a single ++** row containing the default values for each column in table pTab. For ++** example, if pTab is declared as: ++** ++** CREATE TABLE pTab(a PRIMARY KEY, b DEFAULT 123, c DEFAULT 'abcd'); ++** ++** Then this function prepares and returns the SQL statement: ++** ++** SELECT NULL, 123, 'abcd'; ++*/ ++static int sessionPrepareDfltStmt( ++ sqlite3 *db, /* Database handle */ ++ SessionTable *pTab, /* Table to prepare statement for */ ++ sqlite3_stmt **ppStmt /* OUT: Statement handle */ ++){ ++ SessionBuffer sql = {0,0,0}; ++ int rc = SQLITE_OK; ++ const char *zSep = " "; ++ int ii = 0; ++ ++ *ppStmt = 0; ++ sessionAppendPrintf(&sql, &rc, "SELECT"); ++ for(ii=0; iinCol; ii++){ ++ const char *zDflt = pTab->azDflt[ii] ? pTab->azDflt[ii] : "NULL"; ++ sessionAppendPrintf(&sql, &rc, "%s%s", zSep, zDflt); ++ zSep = ", "; ++ } ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3_prepare_v2(db, (const char*)sql.aBuf, -1, ppStmt, 0); ++ } ++ sqlite3_free(sql.aBuf); ++ ++ return rc; ++} ++ ++/* ++** Table pTab has one or more existing change-records with old.* records ++** with fewer than pTab->nCol columns. This function updates all such ++** change-records with the default values for the missing columns. ++*/ ++static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){ ++ sqlite3_stmt *pStmt = 0; ++ int rc = pSession->rc; ++ ++ rc = sessionPrepareDfltStmt(pSession->db, pTab, &pStmt); ++ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ ++ int ii = 0; ++ SessionChange **pp = 0; ++ for(ii=0; iinChange; ii++){ ++ for(pp=&pTab->apChange[ii]; *pp; pp=&((*pp)->pNext)){ ++ if( (*pp)->nRecordField!=pTab->nCol ){ ++ sessionUpdateOneChange(pSession, &rc, pp, pTab->nCol, pStmt); ++ } ++ } + } + } +- return (pSession->rc || pTab->abPK==0); ++ ++ pSession->rc = rc; ++ rc = sqlite3_finalize(pStmt); ++ if( pSession->rc==SQLITE_OK ) pSession->rc = rc; ++ return pSession->rc; + } + + /* +@@ -203571,6 +229724,109 @@ static int sessionStat1Depth(void *pCtx){ + return p->hook.xDepth(p->hook.pCtx); + } + ++static int sessionUpdateMaxSize( ++ int op, ++ sqlite3_session *pSession, /* Session object pTab is attached to */ ++ SessionTable *pTab, /* Table that change applies to */ ++ SessionChange *pC /* Update pC->nMaxSize */ ++){ ++ i64 nNew = 2; ++ if( pC->op==SQLITE_INSERT ){ ++ if( pTab->bRowid ) nNew += 9; ++ if( op!=SQLITE_DELETE ){ ++ int ii; ++ for(ii=0; iinCol; ii++){ ++ sqlite3_value *p = 0; ++ pSession->hook.xNew(pSession->hook.pCtx, pTab->aiIdx[ii], &p); ++ sessionSerializeValue(0, p, &nNew); ++ } ++ } ++ }else if( op==SQLITE_DELETE ){ ++ nNew += pC->nRecord; ++ if( sqlite3_preupdate_blobwrite(pSession->db)>=0 ){ ++ nNew += pC->nRecord; ++ } ++ }else{ ++ int ii; ++ u8 *pCsr = pC->aRecord; ++ if( pTab->bRowid ){ ++ nNew += 9 + 1; ++ pCsr += 9; ++ } ++ for(ii=pTab->bRowid; iinCol; ii++){ ++ int bChanged = 1; ++ int nOld = 0; ++ int eType; ++ int iIdx = pTab->aiIdx[ii]; ++ sqlite3_value *p = 0; ++ pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); ++ if( p==0 ){ ++ return SQLITE_NOMEM; ++ } ++ ++ eType = *pCsr++; ++ switch( eType ){ ++ case SQLITE_NULL: ++ bChanged = sqlite3_value_type(p)!=SQLITE_NULL; ++ break; ++ ++ case SQLITE_FLOAT: ++ case SQLITE_INTEGER: { ++ if( eType==sqlite3_value_type(p) ){ ++ sqlite3_int64 iVal = sessionGetI64(pCsr); ++ if( eType==SQLITE_INTEGER ){ ++ bChanged = (iVal!=sqlite3_value_int64(p)); ++ }else{ ++ double dVal; ++ memcpy(&dVal, &iVal, 8); ++ bChanged = (dVal!=sqlite3_value_double(p)); ++ } ++ } ++ nOld = 8; ++ pCsr += 8; ++ break; ++ } ++ ++ default: { ++ int nByte; ++ nOld = sessionVarintGet(pCsr, &nByte); ++ pCsr += nOld; ++ nOld += nByte; ++ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); ++ if( eType==sqlite3_value_type(p) ++ && nByte==sqlite3_value_bytes(p) ++ && (nByte==0 || 0==memcmp(pCsr, sqlite3_value_blob(p), nByte)) ++ ){ ++ bChanged = 0; ++ } ++ pCsr += nByte; ++ break; ++ } ++ } ++ ++ if( bChanged && pTab->abPK[ii] ){ ++ nNew = pC->nRecord + 2; ++ break; ++ } ++ ++ if( bChanged ){ ++ nNew += 1 + nOld; ++ sessionSerializeValue(0, p, &nNew); ++ }else if( pTab->abPK[ii] ){ ++ nNew += 2 + nOld; ++ }else{ ++ nNew += 2; ++ } ++ } ++ } ++ ++ if( nNew>pC->nMaxSize ){ ++ int nIncr = nNew - pC->nMaxSize; ++ pC->nMaxSize = nNew; ++ pSession->nMaxChangesetSize += nIncr; ++ } ++ return SQLITE_OK; ++} + + /* + ** This function is only called from with a pre-update-hook reporting a +@@ -203582,28 +229838,35 @@ static int sessionStat1Depth(void *pCtx){ + */ + static void sessionPreupdateOneChange( + int op, /* One of SQLITE_UPDATE, INSERT, DELETE */ ++ i64 iRowid, + sqlite3_session *pSession, /* Session object pTab is attached to */ + SessionTable *pTab /* Table that change applies to */ + ){ + int iHash; + int bNull = 0; + int rc = SQLITE_OK; ++ int nExpect = 0; + SessionStat1Ctx stat1 = {{0,0,0,0,0},0}; + + if( pSession->rc ) return; + + /* Load table details if required */ +- if( sessionInitTable(pSession, pTab) ) return; ++ if( sessionInitTable(pSession, pTab, pSession->db, pSession->zDb) ) return; + + /* Check the number of columns in this xPreUpdate call matches the + ** number of columns in the table. */ +- if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){ ++ nExpect = pSession->hook.xCount(pSession->hook.pCtx); ++ if( pTab->nTotalColnTotalCol!=nExpect ){ + pSession->rc = SQLITE_SCHEMA; + return; + } + + /* Grow the hash table if required */ +- if( sessionGrowHash(0, pTab) ){ ++ if( sessionGrowHash(pSession, 0, pTab) ){ + pSession->rc = SQLITE_NOMEM; + return; + } +@@ -203630,21 +229893,22 @@ static void sessionPreupdateOneChange( + /* Calculate the hash-key for this change. If the primary key of the row + ** includes a NULL value, exit early. Such changes are ignored by the + ** session module. */ +- rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull); ++ rc = sessionPreupdateHash( ++ pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull ++ ); + if( rc!=SQLITE_OK ) goto error_out; + + if( bNull==0 ){ + /* Search the hash table for an existing record for this row. */ + SessionChange *pC; + for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ +- if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break; ++ if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break; + } + + if( pC==0 ){ + /* Create a new change object containing all the old values (if + ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK + ** values (if this is an INSERT). */ +- SessionChange *pChange; /* New change object */ + sqlite3_int64 nByte; /* Number of bytes to allocate */ + int i; /* Used to iterate through columns */ + +@@ -203653,30 +229917,37 @@ static void sessionPreupdateOneChange( + + /* Figure out how large an allocation is required */ + nByte = sizeof(SessionChange); +- for(i=0; inCol; i++){ ++ for(i=pTab->bRowid; inCol; i++){ ++ int iIdx = pTab->aiIdx[i]; + sqlite3_value *p = 0; + if( op!=SQLITE_INSERT ){ +- TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); +- assert( trc==SQLITE_OK ); ++ /* This may fail if the column has a non-NULL default and was added ++ ** using ALTER TABLE ADD COLUMN after this record was created. */ ++ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); + }else if( pTab->abPK[i] ){ +- TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); ++ TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx,iIdx,&p); + assert( trc==SQLITE_OK ); + } + +- /* This may fail if SQLite value p contains a utf-16 string that must +- ** be converted to utf-8 and an OOM error occurs while doing so. */ +- rc = sessionSerializeValue(0, p, &nByte); ++ if( rc==SQLITE_OK ){ ++ /* This may fail if SQLite value p contains a utf-16 string that must ++ ** be converted to utf-8 and an OOM error occurs while doing so. */ ++ rc = sessionSerializeValue(0, p, &nByte); ++ } + if( rc!=SQLITE_OK ) goto error_out; + } ++ if( pTab->bRowid ){ ++ nByte += 9; /* Size of rowid field - an integer */ ++ } + + /* Allocate the change object */ +- pChange = (SessionChange *)sqlite3_malloc64(nByte); +- if( !pChange ){ ++ pC = (SessionChange*)sessionMalloc64(pSession, nByte); ++ if( !pC ){ + rc = SQLITE_NOMEM; + goto error_out; + }else{ +- memset(pChange, 0, sizeof(SessionChange)); +- pChange->aRecord = (u8 *)&pChange[1]; ++ memset(pC, 0, sizeof(SessionChange)); ++ pC->aRecord = (u8 *)&pC[1]; + } + + /* Populate the change object. None of the preupdate_old(), +@@ -203684,24 +229955,31 @@ static void sessionPreupdateOneChange( + ** required values and encodings have already been cached in memory. + ** It is not possible for an OOM to occur in this block. */ + nByte = 0; +- for(i=0; inCol; i++){ ++ if( pTab->bRowid ){ ++ pC->aRecord[0] = SQLITE_INTEGER; ++ sessionPutI64(&pC->aRecord[1], iRowid); ++ nByte = 9; ++ } ++ for(i=pTab->bRowid; inCol; i++){ + sqlite3_value *p = 0; ++ int iIdx = pTab->aiIdx[i]; + if( op!=SQLITE_INSERT ){ +- pSession->hook.xOld(pSession->hook.pCtx, i, &p); ++ pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); + }else if( pTab->abPK[i] ){ +- pSession->hook.xNew(pSession->hook.pCtx, i, &p); ++ pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); + } +- sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); ++ sessionSerializeValue(&pC->aRecord[nByte], p, &nByte); + } + + /* Add the change to the hash-table */ + if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){ +- pChange->bIndirect = 1; ++ pC->bIndirect = 1; + } +- pChange->nRecord = nByte; +- pChange->op = op; +- pChange->pNext = pTab->apChange[iHash]; +- pTab->apChange[iHash] = pChange; ++ pC->nRecordField = pTab->nCol; ++ pC->nRecord = nByte; ++ pC->op = op; ++ pC->pNext = pTab->apChange[iHash]; ++ pTab->apChange[iHash] = pC; + + }else if( pC->bIndirect ){ + /* If the existing change is considered "indirect", but this current +@@ -203712,8 +229990,14 @@ static void sessionPreupdateOneChange( + pC->bIndirect = 0; + } + } ++ ++ assert( rc==SQLITE_OK ); ++ if( pSession->bEnableSize ){ ++ rc = sessionUpdateMaxSize(op, pSession, pTab, pC); ++ } + } + ++ + /* If an error has occurred, mark the session object as failed. */ + error_out: + if( pTab->bStat1 ){ +@@ -203746,7 +230030,11 @@ static int sessionFindTable( + ){ + rc = sqlite3session_attach(pSession, zName); + if( rc==SQLITE_OK ){ +- for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext); ++ pRet = pSession->pTable; ++ while( ALWAYS(pRet) && pRet->pNext ){ ++ pRet = pRet->pNext; ++ } ++ assert( pRet!=0 ); + assert( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) ); + } + } +@@ -203773,6 +230061,8 @@ static void xPreUpdate( + int nDb = sqlite3Strlen30(zDb); + + assert( sqlite3_mutex_held(db->mutex) ); ++ (void)iKey1; ++ (void)iKey2; + + for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){ + SessionTable *pTab; +@@ -203787,9 +230077,10 @@ static void xPreUpdate( + pSession->rc = sessionFindTable(pSession, zName, &pTab); + if( pTab ){ + assert( pSession->rc==SQLITE_OK ); +- sessionPreupdateOneChange(op, pSession, pTab); ++ assert( op==SQLITE_UPDATE || iKey1==iKey2 ); ++ sessionPreupdateOneChange(op, iKey1, pSession, pTab); + if( op==SQLITE_UPDATE ){ +- sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab); ++ sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab); + } + } + } +@@ -203828,6 +230119,7 @@ static void sessionPreupdateHooks( + typedef struct SessionDiffCtx SessionDiffCtx; + struct SessionDiffCtx { + sqlite3_stmt *pStmt; ++ int bRowid; + int nOldOff; + }; + +@@ -203836,19 +230128,20 @@ struct SessionDiffCtx { + */ + static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){ + SessionDiffCtx *p = (SessionDiffCtx*)pCtx; +- *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff); ++ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid); + return SQLITE_OK; + } + static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){ + SessionDiffCtx *p = (SessionDiffCtx*)pCtx; +- *ppVal = sqlite3_column_value(p->pStmt, iVal); ++ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid); + return SQLITE_OK; + } + static int sessionDiffCount(void *pCtx){ + SessionDiffCtx *p = (SessionDiffCtx*)pCtx; +- return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt); ++ return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid; + } + static int sessionDiffDepth(void *pCtx){ ++ (void)pCtx; + return 0; + } + +@@ -203922,17 +230215,18 @@ static char *sessionExprCompareOther( + } + + static char *sessionSelectFindNew( +- int nCol, + const char *zDb1, /* Pick rows in this db only */ + const char *zDb2, /* But not in this one */ ++ int bRowid, + const char *zTbl, /* Table name */ + const char *zExpr + ){ ++ const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*"); + char *zRet = sqlite3_mprintf( +- "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS (" ++ "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS (" + " SELECT 1 FROM \"%w\".\"%w\" WHERE %s" + ")", +- zDb1, zTbl, zDb2, zTbl, zExpr ++ zSel, zDb1, zTbl, zDb2, zTbl, zExpr + ); + return zRet; + } +@@ -203946,7 +230240,9 @@ static int sessionDiffFindNew( + char *zExpr + ){ + int rc = SQLITE_OK; +- char *zStmt = sessionSelectFindNew(pTab->nCol, zDb1, zDb2, pTab->zName,zExpr); ++ char *zStmt = sessionSelectFindNew( ++ zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr ++ ); + + if( zStmt==0 ){ + rc = SQLITE_NOMEM; +@@ -203957,8 +230253,10 @@ static int sessionDiffFindNew( + SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; + pDiffCtx->pStmt = pStmt; + pDiffCtx->nOldOff = 0; ++ pDiffCtx->bRowid = pTab->bRowid; + while( SQLITE_ROW==sqlite3_step(pStmt) ){ +- sessionPreupdateOneChange(op, pSession, pTab); ++ i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); ++ sessionPreupdateOneChange(op, iRowid, pSession, pTab); + } + rc = sqlite3_finalize(pStmt); + } +@@ -203968,6 +230266,27 @@ static int sessionDiffFindNew( + return rc; + } + ++/* ++** Return a comma-separated list of the fully-qualified (with both database ++** and table name) column names from table pTab. e.g. ++** ++** "main"."t1"."a", "main"."t1"."b", "main"."t1"."c" ++*/ ++static char *sessionAllCols( ++ const char *zDb, ++ SessionTable *pTab ++){ ++ int ii; ++ char *zRet = 0; ++ for(ii=0; iinCol; ii++){ ++ zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"", ++ zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii] ++ ); ++ if( !zRet ) break; ++ } ++ return zRet; ++} ++ + static int sessionDiffFindModified( + sqlite3_session *pSession, + SessionTable *pTab, +@@ -203982,11 +230301,13 @@ static int sessionDiffFindModified( + if( zExpr2==0 ){ + rc = SQLITE_NOMEM; + }else{ ++ char *z1 = sessionAllCols(pSession->zDb, pTab); ++ char *z2 = sessionAllCols(zFrom, pTab); + char *zStmt = sqlite3_mprintf( +- "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", +- pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 ++ "SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", ++ z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 + ); +- if( zStmt==0 ){ ++ if( zStmt==0 || z1==0 || z2==0 ){ + rc = SQLITE_NOMEM; + }else{ + sqlite3_stmt *pStmt; +@@ -203997,12 +230318,15 @@ static int sessionDiffFindModified( + pDiffCtx->pStmt = pStmt; + pDiffCtx->nOldOff = pTab->nCol; + while( SQLITE_ROW==sqlite3_step(pStmt) ){ +- sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab); ++ i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); ++ sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab); + } + rc = sqlite3_finalize(pStmt); + } +- sqlite3_free(zStmt); + } ++ sqlite3_free(zStmt); ++ sqlite3_free(z1); ++ sqlite3_free(z2); + } + + return rc; +@@ -204029,9 +230353,11 @@ SQLITE_API int sqlite3session_diff( + SessionTable *pTo; /* Table zTbl */ + + /* Locate and if necessary initialize the target table object */ ++ pSession->bAutoAttach++; + rc = sessionFindTable(pSession, zTbl, &pTo); ++ pSession->bAutoAttach--; + if( pTo==0 ) goto diff_out; +- if( sessionInitTable(pSession, pTo) ){ ++ if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){ + rc = pSession->rc; + goto diff_out; + } +@@ -204040,13 +230366,43 @@ SQLITE_API int sqlite3session_diff( + if( rc==SQLITE_OK ){ + int bHasPk = 0; + int bMismatch = 0; +- int nCol; /* Columns in zFrom.zTbl */ +- u8 *abPK; ++ int nCol = 0; /* Columns in zFrom.zTbl */ ++ int bRowid = 0; ++ u8 *abPK = 0; + const char **azCol = 0; +- rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); ++ char *zDbExists = 0; ++ ++ /* Check that database zFrom is attached. */ ++ zDbExists = sqlite3_mprintf("SELECT * FROM %Q.sqlite_schema", zFrom); ++ if( zDbExists==0 ){ ++ rc = SQLITE_NOMEM; ++ }else{ ++ sqlite3_stmt *pDbExists = 0; ++ rc = sqlite3_prepare_v2(db, zDbExists, -1, &pDbExists, 0); ++ if( rc==SQLITE_ERROR ){ ++ rc = SQLITE_OK; ++ nCol = -1; ++ } ++ sqlite3_finalize(pDbExists); ++ sqlite3_free(zDbExists); ++ } ++ ++ if( rc==SQLITE_OK && nCol==0 ){ ++ rc = sessionTableInfo(0, db, zFrom, zTbl, ++ &nCol, 0, 0, &azCol, 0, 0, &abPK, ++ pSession->bImplicitPK ? &bRowid : 0 ++ ); ++ } + if( rc==SQLITE_OK ){ + if( pTo->nCol!=nCol ){ +- bMismatch = 1; ++ if( nCol<=0 ){ ++ rc = SQLITE_SCHEMA; ++ if( pzErrMsg ){ ++ *pzErrMsg = sqlite3_mprintf("no such table: %s.%s", zFrom, zTbl); ++ } ++ }else{ ++ bMismatch = 1; ++ } + }else{ + int i; + for(i=0; iapChange[i]; p; p=pNextChange){ + pNextChange = p->pNext; +- sqlite3_free(p); ++ sessionFree(pSession, p); + } + } +- sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */ +- sqlite3_free(pTab->apChange); +- sqlite3_free(pTab); ++ sqlite3_finalize(pTab->pDfltStmt); ++ sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */ ++ sessionFree(pSession, pTab->apChange); ++ sessionFree(pSession, pTab); + } + } + +@@ -204186,9 +230543,9 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){ + + /* Delete all attached table objects. And the contents of their + ** associated hash-tables. */ +- sessionDeleteTable(pSession->pTable); ++ sessionDeleteTable(pSession, pSession->pTable); + +- /* Free the session object itself. */ ++ /* Free the session object. */ + sqlite3_free(pSession); + } + +@@ -204235,7 +230592,8 @@ SQLITE_API int sqlite3session_attach( + + if( !pTab ){ + /* Allocate new SessionTable object. */ +- pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1); ++ int nByte = sizeof(SessionTable) + nName + 1; ++ pTab = (SessionTable*)sessionMalloc64(pSession, nByte); + if( !pTab ){ + rc = SQLITE_NOMEM; + }else{ +@@ -204258,32 +230616,6 @@ SQLITE_API int sqlite3session_attach( + return rc; + } + +-/* +-** Ensure that there is room in the buffer to append nByte bytes of data. +-** If not, use sqlite3_realloc() to grow the buffer so that there is. +-** +-** If successful, return zero. Otherwise, if an OOM condition is encountered, +-** set *pRc to SQLITE_NOMEM and return non-zero. +-*/ +-static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){ +- if( *pRc==SQLITE_OK && (size_t)(p->nAlloc-p->nBuf)nAlloc ? p->nAlloc : 128; +- do { +- nNew = nNew*2; +- }while( (size_t)(nNew-p->nBuf)aBuf, nNew); +- if( 0==aNew ){ +- *pRc = SQLITE_NOMEM; +- }else{ +- p->aBuf = aNew; +- p->nAlloc = nNew; +- } +- } +- return (*pRc!=SQLITE_OK); +-} +- + /* + ** Append the value passed as the second argument to the buffer passed + ** as the first. +@@ -204352,26 +230684,6 @@ static void sessionAppendBlob( + } + } + +-/* +-** This function is a no-op if *pRc is other than SQLITE_OK when it is +-** called. Otherwise, append a string to the buffer. All bytes in the string +-** up to (but not including) the nul-terminator are written to the buffer. +-** +-** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before +-** returning. +-*/ +-static void sessionAppendStr( +- SessionBuffer *p, +- const char *zStr, +- int *pRc +-){ +- int nStr = sqlite3Strlen30(zStr); +- if( 0==sessionBufferGrow(p, nStr, pRc) ){ +- memcpy(&p->aBuf[p->nBuf], zStr, nStr); +- p->nBuf += nStr; +- } +-} +- + /* + ** This function is a no-op if *pRc is other than SQLITE_OK when it is + ** called. Otherwise, append the string representation of integer iVal +@@ -204404,17 +230716,20 @@ static void sessionAppendIdent( + const char *zStr, /* String to quote, escape and append */ + int *pRc /* IN/OUT: Error code */ + ){ +- int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1; ++ int nStr = sqlite3Strlen30(zStr)*2 + 2 + 2; + if( 0==sessionBufferGrow(p, nStr, pRc) ){ + char *zOut = (char *)&p->aBuf[p->nBuf]; + const char *zIn = zStr; + *zOut++ = '"'; +- while( *zIn ){ +- if( *zIn=='"' ) *zOut++ = '"'; +- *zOut++ = *(zIn++); ++ if( zIn!=0 ){ ++ while( *zIn ){ ++ if( *zIn=='"' ) *zOut++ = '"'; ++ *zOut++ = *(zIn++); ++ } + } + *zOut++ = '"'; + p->nBuf = (int)((u8 *)zOut - p->aBuf); ++ p->aBuf[p->nBuf] = 0x00; + } + } + +@@ -204500,6 +230815,7 @@ static int sessionAppendUpdate( + int i; /* Used to iterate through columns */ + u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */ + ++ assert( abPK!=0 ); + sessionAppendByte(pBuf, SQLITE_UPDATE, &rc); + sessionAppendByte(pBuf, p->bIndirect, &rc); + for(i=0; i FROM zDb.zTab WHERE (pk1, pk2,...) IS (?1, ?2,...) ++** ++** where is: ++** ++** 1 AND (?A OR ?1 IS ) AND ... ++** ++** for each non-pk . + */ + static int sessionSelectStmt( + sqlite3 *db, /* Database handle */ ++ int bIgnoreNoop, + const char *zDb, /* Database name */ + const char *zTab, /* Table name */ ++ int bRowid, + int nCol, /* Number of columns in table */ + const char **azCol, /* Names of table columns */ + u8 *abPK, /* PRIMARY KEY array */ +@@ -204651,8 +230975,57 @@ static int sessionSelectStmt( + ){ + int rc = SQLITE_OK; + char *zSql = 0; ++ const char *zSep = ""; + int nSql = -1; ++ int i; ++ ++ SessionBuffer cols = {0, 0, 0}; ++ SessionBuffer nooptest = {0, 0, 0}; ++ SessionBuffer pkfield = {0, 0, 0}; ++ SessionBuffer pkvar = {0, 0, 0}; ++ ++ sessionAppendStr(&nooptest, ", 1", &rc); ++ ++ if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){ ++ sessionAppendStr(&nooptest, " AND (?6 OR ?3 IS stat)", &rc); ++ sessionAppendStr(&pkfield, "tbl, idx", &rc); ++ sessionAppendStr(&pkvar, ++ "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc ++ ); ++ sessionAppendStr(&cols, "tbl, ?2, stat", &rc); ++ }else{ ++ #if 0 ++ if( bRowid ){ ++ sessionAppendStr(&cols, SESSIONS_ROWID, &rc); ++ } ++ #endif ++ for(i=0; idb; /* Source database handle */ + SessionTable *pTab; /* Used to iterate through attached tables */ +- SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */ ++ SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */ + int rc; /* Return code */ + +- assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0 ) ); ++ assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); ++ assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); + + /* Zero the output variables in case an error occurs. If this session + ** object is already in the error state (sqlite3_session.rc != SQLITE_OK), + ** this call will be a no-op. */ + if( xOutput==0 ){ ++ assert( pnChangeset!=0 && ppChangeset!=0 ); + *pnChangeset = 0; + *ppChangeset = 0; + } +@@ -204823,18 +231202,16 @@ static int sessionGenerateChangeset( + for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ + if( pTab->nEntry ){ + const char *zName = pTab->zName; +- int nCol; /* Number of columns in table */ +- u8 *abPK; /* Primary key array */ +- const char **azCol = 0; /* Table columns */ + int i; /* Used to iterate through hash buckets */ + sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ + int nRewind = buf.nBuf; /* Initial size of write buffer */ + int nNoop; /* Size of buffer after writing tbl header */ ++ int nOldCol = pTab->nCol; + + /* Check the table schema is still Ok. */ +- rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK); +- if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){ +- rc = SQLITE_SCHEMA; ++ rc = sessionReinitTable(pSession, pTab); ++ if( rc==SQLITE_OK && pTab->nCol!=nOldCol ){ ++ rc = sessionUpdateChanges(pSession, pTab); + } + + /* Write a table header */ +@@ -204842,8 +231219,9 @@ static int sessionGenerateChangeset( + + /* Build and compile a statement to execute: */ + if( rc==SQLITE_OK ){ +- rc = sessionSelectStmt( +- db, pSession->zDb, zName, nCol, azCol, abPK, &pSel); ++ rc = sessionSelectStmt(db, 0, pSession->zDb, ++ zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel ++ ); + } + + nNoop = buf.nBuf; +@@ -204851,21 +231229,22 @@ static int sessionGenerateChangeset( + SessionChange *p; /* Used to iterate through changes */ + + for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){ +- rc = sessionSelectBind(pSel, nCol, abPK, p); ++ rc = sessionSelectBind(pSel, pTab->nCol, pTab->abPK, p); + if( rc!=SQLITE_OK ) continue; + if( sqlite3_step(pSel)==SQLITE_ROW ){ + if( p->op==SQLITE_INSERT ){ + int iCol; + sessionAppendByte(&buf, SQLITE_INSERT, &rc); + sessionAppendByte(&buf, p->bIndirect, &rc); +- for(iCol=0; iColnCol; iCol++){ + sessionAppendCol(&buf, pSel, iCol, &rc); + } + }else{ +- rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK); ++ assert( pTab->abPK!=0 ); ++ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK); + } + }else if( p->op!=SQLITE_INSERT ){ +- rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK); ++ rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_reset(pSel); +@@ -204890,7 +231269,6 @@ static int sessionGenerateChangeset( + if( buf.nBuf==nNoop ){ + buf.nBuf = nRewind; + } +- sqlite3_free((char*)azCol); /* cast works around VC++ bug */ + } + } + +@@ -204922,7 +231300,14 @@ SQLITE_API int sqlite3session_changeset( + int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ + void **ppChangeset /* OUT: Buffer containing changeset */ + ){ +- return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); ++ int rc; ++ ++ if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; ++ rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); ++ assert( rc || pnChangeset==0 ++ || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize ++ ); ++ return rc; + } + + /* +@@ -204933,6 +231318,7 @@ SQLITE_API int sqlite3session_changeset_strm( + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut + ){ ++ if( xOutput==0 ) return SQLITE_MISUSE; + return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0); + } + +@@ -204944,6 +231330,7 @@ SQLITE_API int sqlite3session_patchset_strm( + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut + ){ ++ if( xOutput==0 ) return SQLITE_MISUSE; + return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0); + } + +@@ -204959,6 +231346,7 @@ SQLITE_API int sqlite3session_patchset( + int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */ + void **ppPatchset /* OUT: Buffer containing changeset */ + ){ ++ if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE; + return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset); + } + +@@ -205007,6 +231395,59 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession){ + return (ret==0); + } + ++/* ++** Return the amount of heap memory in use. ++*/ ++SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession){ ++ return pSession->nMalloc; ++} ++ ++/* ++** Configure the session object passed as the first argument. ++*/ ++SQLITE_API int sqlite3session_object_config(sqlite3_session *pSession, int op, void *pArg){ ++ int rc = SQLITE_OK; ++ switch( op ){ ++ case SQLITE_SESSION_OBJCONFIG_SIZE: { ++ int iArg = *(int*)pArg; ++ if( iArg>=0 ){ ++ if( pSession->pTable ){ ++ rc = SQLITE_MISUSE; ++ }else{ ++ pSession->bEnableSize = (iArg!=0); ++ } ++ } ++ *(int*)pArg = pSession->bEnableSize; ++ break; ++ } ++ ++ case SQLITE_SESSION_OBJCONFIG_ROWID: { ++ int iArg = *(int*)pArg; ++ if( iArg>=0 ){ ++ if( pSession->pTable ){ ++ rc = SQLITE_MISUSE; ++ }else{ ++ pSession->bImplicitPK = (iArg!=0); ++ } ++ } ++ *(int*)pArg = pSession->bImplicitPK; ++ break; ++ } ++ ++ default: ++ rc = SQLITE_MISUSE; ++ } ++ ++ return rc; ++} ++ ++/* ++** Return the maximum size of sqlite3session_changeset() output. ++*/ ++SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession){ ++ return pSession->nMaxChangesetSize; ++} ++ + /* + ** Do the work for either sqlite3changeset_start() or start_strm(). + */ +@@ -205016,7 +231457,8 @@ static int sessionChangesetStart( + void *pIn, + int nChangeset, /* Size of buffer pChangeset in bytes */ + void *pChangeset, /* Pointer to buffer containing changeset */ +- int bInvert /* True to invert changeset */ ++ int bInvert, /* True to invert changeset */ ++ int bSkipEmpty /* True to skip empty UPDATE changes */ + ){ + sqlite3_changeset_iter *pRet; /* Iterator to return */ + int nByte; /* Number of bytes to allocate for iterator */ +@@ -205037,6 +231479,7 @@ static int sessionChangesetStart( + pRet->in.pIn = pIn; + pRet->in.bEof = (xInput ? 0 : 1); + pRet->bInvert = bInvert; ++ pRet->bSkipEmpty = bSkipEmpty; + + /* Populate the output variable and return success. */ + *pp = pRet; +@@ -205051,7 +231494,7 @@ SQLITE_API int sqlite3changeset_start( + int nChangeset, /* Size of buffer pChangeset in bytes */ + void *pChangeset /* Pointer to buffer containing changeset */ + ){ +- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0); ++ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0, 0); + } + SQLITE_API int sqlite3changeset_start_v2( + sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ +@@ -205060,7 +231503,7 @@ SQLITE_API int sqlite3changeset_start_v2( + int flags + ){ + int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); +- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert); ++ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert, 0); + } + + /* +@@ -205071,7 +231514,7 @@ SQLITE_API int sqlite3changeset_start_strm( + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn + ){ +- return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0); ++ return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0, 0); + } + SQLITE_API int sqlite3changeset_start_v2_strm( + sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ +@@ -205080,7 +231523,7 @@ SQLITE_API int sqlite3changeset_start_v2_strm( + int flags + ){ + int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); +- return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert); ++ return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert, 0); + } + + /* +@@ -205088,14 +231531,15 @@ SQLITE_API int sqlite3changeset_start_v2_strm( + ** object and the buffer is full, discard some data to free up space. + */ + static void sessionDiscardData(SessionInput *pIn){ +- if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){ +- int nMove = pIn->buf.nBuf - pIn->iNext; ++ if( pIn->xInput && pIn->iCurrent>=sessions_strm_chunk_size ){ ++ int nMove = pIn->buf.nBuf - pIn->iCurrent; + assert( nMove>=0 ); + if( nMove>0 ){ +- memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iNext], nMove); ++ memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iCurrent], nMove); + } +- pIn->buf.nBuf -= pIn->iNext; +- pIn->iNext = 0; ++ pIn->buf.nBuf -= pIn->iCurrent; ++ pIn->iNext -= pIn->iCurrent; ++ pIn->iCurrent = 0; + pIn->nData = pIn->buf.nBuf; + } + } +@@ -205206,11 +231650,14 @@ static int sessionReadRecord( + SessionInput *pIn, /* Input data */ + int nCol, /* Number of values in record */ + u8 *abPK, /* Array of primary key flags, or NULL */ +- sqlite3_value **apOut /* Write values to this array */ ++ sqlite3_value **apOut, /* Write values to this array */ ++ int *pbEmpty + ){ + int i; /* Used to iterate through columns */ + int rc = SQLITE_OK; + ++ assert( pbEmpty==0 || *pbEmpty==0 ); ++ if( pbEmpty ) *pbEmpty = 1; + for(i=0; iaData[pIn->iNext++]; + assert( apOut[i]==0 ); + if( eType ){ ++ if( pbEmpty ) *pbEmpty = 0; + apOut[i] = sqlite3ValueNew(0); + if( !apOut[i] ) rc = SQLITE_NOMEM; + } +@@ -205245,15 +231693,19 @@ static int sessionReadRecord( + } + } + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ +- sqlite3_int64 v = sessionGetI64(aVal); +- if( eType==SQLITE_INTEGER ){ +- sqlite3VdbeMemSetInt64(apOut[i], v); ++ if( (pIn->nData-pIn->iNext)<8 ){ ++ rc = SQLITE_CORRUPT_BKPT; + }else{ +- double d; +- memcpy(&d, &v, 8); +- sqlite3VdbeMemSetDouble(apOut[i], d); ++ sqlite3_int64 v = sessionGetI64(aVal); ++ if( eType==SQLITE_INTEGER ){ ++ sqlite3VdbeMemSetInt64(apOut[i], v); ++ }else{ ++ double d; ++ memcpy(&d, &v, 8); ++ sqlite3VdbeMemSetDouble(apOut[i], d); ++ } ++ pIn->iNext += 8; + } +- pIn->iNext += 8; + } + } + } +@@ -205400,6 +231852,149 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){ + return (p->rc = rc); + } + ++/* ++** Advance the changeset iterator to the next change. The differences between ++** this function and sessionChangesetNext() are that ++** ++** * If pbEmpty is not NULL and the change is a no-op UPDATE (an UPDATE ++** that modifies no columns), this function sets (*pbEmpty) to 1. ++** ++** * If the iterator is configured to skip no-op UPDATEs, ++** sessionChangesetNext() does that. This function does not. ++*/ ++static int sessionChangesetNextOne( ++ sqlite3_changeset_iter *p, /* Changeset iterator */ ++ u8 **paRec, /* If non-NULL, store record pointer here */ ++ int *pnRec, /* If non-NULL, store size of record here */ ++ int *pbNew, /* If non-NULL, true if new table */ ++ int *pbEmpty ++){ ++ int i; ++ u8 op; ++ ++ assert( (paRec==0 && pnRec==0) || (paRec && pnRec) ); ++ assert( pbEmpty==0 || *pbEmpty==0 ); ++ ++ /* If the iterator is in the error-state, return immediately. */ ++ if( p->rc!=SQLITE_OK ) return p->rc; ++ ++ /* Free the current contents of p->apValue[], if any. */ ++ if( p->apValue ){ ++ for(i=0; inCol*2; i++){ ++ sqlite3ValueFree(p->apValue[i]); ++ } ++ memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); ++ } ++ ++ /* Make sure the buffer contains at least 10 bytes of input data, or all ++ ** remaining data if there are less than 10 bytes available. This is ++ ** sufficient either for the 'T' or 'P' byte and the varint that follows ++ ** it, or for the two single byte values otherwise. */ ++ p->rc = sessionInputBuffer(&p->in, 2); ++ if( p->rc!=SQLITE_OK ) return p->rc; ++ ++ p->in.iCurrent = p->in.iNext; ++ sessionDiscardData(&p->in); ++ ++ /* If the iterator is already at the end of the changeset, return DONE. */ ++ if( p->in.iNext>=p->in.nData ){ ++ return SQLITE_DONE; ++ } ++ ++ op = p->in.aData[p->in.iNext++]; ++ while( op=='T' || op=='P' ){ ++ if( pbNew ) *pbNew = 1; ++ p->bPatchset = (op=='P'); ++ if( sessionChangesetReadTblhdr(p) ) return p->rc; ++ if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; ++ p->in.iCurrent = p->in.iNext; ++ if( p->in.iNext>=p->in.nData ) return SQLITE_DONE; ++ op = p->in.aData[p->in.iNext++]; ++ } ++ ++ if( p->zTab==0 || (p->bPatchset && p->bInvert) ){ ++ /* The first record in the changeset is not a table header. Must be a ++ ** corrupt changeset. */ ++ assert( p->in.iNext==1 || p->zTab ); ++ return (p->rc = SQLITE_CORRUPT_BKPT); ++ } ++ ++ p->op = op; ++ p->bIndirect = p->in.aData[p->in.iNext++]; ++ if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ ++ return (p->rc = SQLITE_CORRUPT_BKPT); ++ } ++ ++ if( paRec ){ ++ int nVal; /* Number of values to buffer */ ++ if( p->bPatchset==0 && op==SQLITE_UPDATE ){ ++ nVal = p->nCol * 2; ++ }else if( p->bPatchset && op==SQLITE_DELETE ){ ++ nVal = 0; ++ for(i=0; inCol; i++) if( p->abPK[i] ) nVal++; ++ }else{ ++ nVal = p->nCol; ++ } ++ p->rc = sessionChangesetBufferRecord(&p->in, nVal, pnRec); ++ if( p->rc!=SQLITE_OK ) return p->rc; ++ *paRec = &p->in.aData[p->in.iNext]; ++ p->in.iNext += *pnRec; ++ }else{ ++ sqlite3_value **apOld = (p->bInvert ? &p->apValue[p->nCol] : p->apValue); ++ sqlite3_value **apNew = (p->bInvert ? p->apValue : &p->apValue[p->nCol]); ++ ++ /* If this is an UPDATE or DELETE, read the old.* record. */ ++ if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){ ++ u8 *abPK = p->bPatchset ? p->abPK : 0; ++ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld, 0); ++ if( p->rc!=SQLITE_OK ) return p->rc; ++ } ++ ++ /* If this is an INSERT or UPDATE, read the new.* record. */ ++ if( p->op!=SQLITE_DELETE ){ ++ p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew, pbEmpty); ++ if( p->rc!=SQLITE_OK ) return p->rc; ++ } ++ ++ if( (p->bPatchset || p->bInvert) && p->op==SQLITE_UPDATE ){ ++ /* If this is an UPDATE that is part of a patchset, then all PK and ++ ** modified fields are present in the new.* record. The old.* record ++ ** is currently completely empty. This block shifts the PK fields from ++ ** new.* to old.*, to accommodate the code that reads these arrays. */ ++ for(i=0; inCol; i++){ ++ assert( p->bPatchset==0 || p->apValue[i]==0 ); ++ if( p->abPK[i] ){ ++ assert( p->apValue[i]==0 ); ++ p->apValue[i] = p->apValue[i+p->nCol]; ++ if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT); ++ p->apValue[i+p->nCol] = 0; ++ } ++ } ++ }else if( p->bInvert ){ ++ if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE; ++ else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT; ++ } ++ ++ /* If this is an UPDATE that is part of a changeset, then check that ++ ** there are no fields in the old.* record that are not (a) PK fields, ++ ** or (b) also present in the new.* record. ++ ** ++ ** Such records are technically corrupt, but the rebaser was at one ++ ** point generating them. Under most circumstances this is benign, but ++ ** can cause spurious SQLITE_RANGE errors when applying the changeset. */ ++ if( p->bPatchset==0 && p->op==SQLITE_UPDATE){ ++ for(i=0; inCol; i++){ ++ if( p->abPK[i]==0 && p->apValue[i+p->nCol]==0 ){ ++ sqlite3ValueFree(p->apValue[i]); ++ p->apValue[i] = 0; ++ } ++ } ++ } ++ } ++ ++ return SQLITE_ROW; ++} ++ + /* + ** Advance the changeset iterator to the next change. + ** +@@ -205422,113 +232017,13 @@ static int sessionChangesetNext( + int *pnRec, /* If non-NULL, store size of record here */ + int *pbNew /* If non-NULL, true if new table */ + ){ +- int i; +- u8 op; +- +- assert( (paRec==0 && pnRec==0) || (paRec && pnRec) ); +- +- /* If the iterator is in the error-state, return immediately. */ +- if( p->rc!=SQLITE_OK ) return p->rc; +- +- /* Free the current contents of p->apValue[], if any. */ +- if( p->apValue ){ +- for(i=0; inCol*2; i++){ +- sqlite3ValueFree(p->apValue[i]); +- } +- memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); +- } +- +- /* Make sure the buffer contains at least 10 bytes of input data, or all +- ** remaining data if there are less than 10 bytes available. This is +- ** sufficient either for the 'T' or 'P' byte and the varint that follows +- ** it, or for the two single byte values otherwise. */ +- p->rc = sessionInputBuffer(&p->in, 2); +- if( p->rc!=SQLITE_OK ) return p->rc; +- +- /* If the iterator is already at the end of the changeset, return DONE. */ +- if( p->in.iNext>=p->in.nData ){ +- return SQLITE_DONE; +- } +- +- sessionDiscardData(&p->in); +- p->in.iCurrent = p->in.iNext; +- +- op = p->in.aData[p->in.iNext++]; +- while( op=='T' || op=='P' ){ +- if( pbNew ) *pbNew = 1; +- p->bPatchset = (op=='P'); +- if( sessionChangesetReadTblhdr(p) ) return p->rc; +- if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; +- p->in.iCurrent = p->in.iNext; +- if( p->in.iNext>=p->in.nData ) return SQLITE_DONE; +- op = p->in.aData[p->in.iNext++]; +- } +- +- if( p->zTab==0 || (p->bPatchset && p->bInvert) ){ +- /* The first record in the changeset is not a table header. Must be a +- ** corrupt changeset. */ +- assert( p->in.iNext==1 || p->zTab ); +- return (p->rc = SQLITE_CORRUPT_BKPT); +- } +- +- p->op = op; +- p->bIndirect = p->in.aData[p->in.iNext++]; +- if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ +- return (p->rc = SQLITE_CORRUPT_BKPT); +- } +- +- if( paRec ){ +- int nVal; /* Number of values to buffer */ +- if( p->bPatchset==0 && op==SQLITE_UPDATE ){ +- nVal = p->nCol * 2; +- }else if( p->bPatchset && op==SQLITE_DELETE ){ +- nVal = 0; +- for(i=0; inCol; i++) if( p->abPK[i] ) nVal++; +- }else{ +- nVal = p->nCol; +- } +- p->rc = sessionChangesetBufferRecord(&p->in, nVal, pnRec); +- if( p->rc!=SQLITE_OK ) return p->rc; +- *paRec = &p->in.aData[p->in.iNext]; +- p->in.iNext += *pnRec; +- }else{ +- sqlite3_value **apOld = (p->bInvert ? &p->apValue[p->nCol] : p->apValue); +- sqlite3_value **apNew = (p->bInvert ? p->apValue : &p->apValue[p->nCol]); +- +- /* If this is an UPDATE or DELETE, read the old.* record. */ +- if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){ +- u8 *abPK = p->bPatchset ? p->abPK : 0; +- p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld); +- if( p->rc!=SQLITE_OK ) return p->rc; +- } +- +- /* If this is an INSERT or UPDATE, read the new.* record. */ +- if( p->op!=SQLITE_DELETE ){ +- p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew); +- if( p->rc!=SQLITE_OK ) return p->rc; +- } +- +- if( (p->bPatchset || p->bInvert) && p->op==SQLITE_UPDATE ){ +- /* If this is an UPDATE that is part of a patchset, then all PK and +- ** modified fields are present in the new.* record. The old.* record +- ** is currently completely empty. This block shifts the PK fields from +- ** new.* to old.*, to accommodate the code that reads these arrays. */ +- for(i=0; inCol; i++){ +- assert( p->bPatchset==0 || p->apValue[i]==0 ); +- if( p->abPK[i] ){ +- assert( p->apValue[i]==0 ); +- p->apValue[i] = p->apValue[i+p->nCol]; +- if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT); +- p->apValue[i+p->nCol] = 0; +- } +- } +- }else if( p->bInvert ){ +- if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE; +- else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT; +- } +- } +- +- return SQLITE_ROW; ++ int bEmpty; ++ int rc; ++ do { ++ bEmpty = 0; ++ rc = sessionChangesetNextOne(p, paRec, pnRec, pbNew, &bEmpty); ++ }while( rc==SQLITE_ROW && p->bSkipEmpty && bEmpty); ++ return rc; + } + + /* +@@ -205803,9 +232298,9 @@ static int sessionChangesetInvert( + + /* Read the old.* and new.* records for the update change. */ + pInput->iNext += 2; +- rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]); ++ rc = sessionReadRecord(pInput, nCol, 0, &apVal[0], 0); + if( rc==SQLITE_OK ){ +- rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]); ++ rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol], 0); + } + + /* Write the new old.* record. Consists of the PK columns from the +@@ -205849,11 +232344,11 @@ static int sessionChangesetInvert( + } + + assert( rc==SQLITE_OK ); +- if( pnInverted ){ ++ if( pnInverted && ALWAYS(ppInverted) ){ + *pnInverted = sOut.nBuf; + *ppInverted = sOut.aBuf; + sOut.aBuf = 0; +- }else if( sOut.nBuf>0 ){ ++ }else if( sOut.nBuf>0 && ALWAYS(xOutput!=0) ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + } + +@@ -205906,16 +232401,25 @@ SQLITE_API int sqlite3changeset_invert_strm( + return rc; + } + ++ ++typedef struct SessionUpdate SessionUpdate; ++struct SessionUpdate { ++ sqlite3_stmt *pStmt; ++ u32 *aMask; ++ SessionUpdate *pNext; ++}; ++ + typedef struct SessionApplyCtx SessionApplyCtx; + struct SessionApplyCtx { + sqlite3 *db; + sqlite3_stmt *pDelete; /* DELETE statement */ +- sqlite3_stmt *pUpdate; /* UPDATE statement */ + sqlite3_stmt *pInsert; /* INSERT statement */ + sqlite3_stmt *pSelect; /* SELECT statement */ + int nCol; /* Size of azCol[] and abPK[] arrays */ + const char **azCol; /* Array of column names */ + u8 *abPK; /* Boolean array - true if column is in PK */ ++ u32 *aUpdateMask; /* Used by sessionUpdateFind */ ++ SessionUpdate *pUp; + int bStat1; /* True if table is sqlite_stat1 */ + int bDeferConstraints; /* True to defer constraints */ + int bInvertConstraints; /* Invert when iterating constraints buffer */ +@@ -205923,8 +232427,171 @@ struct SessionApplyCtx { + SessionBuffer rebase; /* Rebase information (if any) here */ + u8 bRebaseStarted; /* If table header is already in rebase */ + u8 bRebase; /* True to collect rebase information */ ++ u8 bIgnoreNoop; /* True to ignore no-op conflicts */ ++ int bRowid; + }; + ++/* Number of prepared UPDATE statements to cache. */ ++#define SESSION_UPDATE_CACHE_SZ 12 ++ ++/* ++** Find a prepared UPDATE statement suitable for the UPDATE step currently ++** being visited by the iterator. The UPDATE is of the form: ++** ++** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ? ++*/ ++static int sessionUpdateFind( ++ sqlite3_changeset_iter *pIter, ++ SessionApplyCtx *p, ++ int bPatchset, ++ sqlite3_stmt **ppStmt ++){ ++ int rc = SQLITE_OK; ++ SessionUpdate *pUp = 0; ++ int nCol = pIter->nCol; ++ int nU32 = (pIter->nCol+33)/32; ++ int ii; ++ ++ if( p->aUpdateMask==0 ){ ++ p->aUpdateMask = sqlite3_malloc(nU32*sizeof(u32)); ++ if( p->aUpdateMask==0 ){ ++ rc = SQLITE_NOMEM; ++ } ++ } ++ ++ if( rc==SQLITE_OK ){ ++ memset(p->aUpdateMask, 0, nU32*sizeof(u32)); ++ rc = SQLITE_CORRUPT; ++ for(ii=0; iinCol; ii++){ ++ if( sessionChangesetNew(pIter, ii) ){ ++ p->aUpdateMask[ii/32] |= (1<<(ii%32)); ++ rc = SQLITE_OK; ++ } ++ } ++ } ++ ++ if( rc==SQLITE_OK ){ ++ if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32)); ++ ++ if( p->pUp ){ ++ int nUp = 0; ++ SessionUpdate **pp = &p->pUp; ++ while( 1 ){ ++ nUp++; ++ if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){ ++ pUp = *pp; ++ *pp = pUp->pNext; ++ pUp->pNext = p->pUp; ++ p->pUp = pUp; ++ break; ++ } ++ ++ if( (*pp)->pNext ){ ++ pp = &(*pp)->pNext; ++ }else{ ++ if( nUp>=SESSION_UPDATE_CACHE_SZ ){ ++ sqlite3_finalize((*pp)->pStmt); ++ sqlite3_free(*pp); ++ *pp = 0; ++ } ++ break; ++ } ++ } ++ } ++ ++ if( pUp==0 ){ ++ int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32); ++ int bStat1 = (sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0); ++ pUp = (SessionUpdate*)sqlite3_malloc(nByte); ++ if( pUp==0 ){ ++ rc = SQLITE_NOMEM; ++ }else{ ++ const char *zSep = ""; ++ SessionBuffer buf; ++ ++ memset(&buf, 0, sizeof(buf)); ++ pUp->aMask = (u32*)&pUp[1]; ++ memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32)); ++ ++ sessionAppendStr(&buf, "UPDATE main.", &rc); ++ sessionAppendIdent(&buf, pIter->zTab, &rc); ++ sessionAppendStr(&buf, " SET ", &rc); ++ ++ /* Create the assignments part of the UPDATE */ ++ for(ii=0; iinCol; ii++){ ++ if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){ ++ sessionAppendStr(&buf, zSep, &rc); ++ sessionAppendIdent(&buf, p->azCol[ii], &rc); ++ sessionAppendStr(&buf, " = ?", &rc); ++ sessionAppendInteger(&buf, ii*2+1, &rc); ++ zSep = ", "; ++ } ++ } ++ ++ /* Create the WHERE clause part of the UPDATE */ ++ zSep = ""; ++ sessionAppendStr(&buf, " WHERE ", &rc); ++ for(ii=0; iinCol; ii++){ ++ if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){ ++ sessionAppendStr(&buf, zSep, &rc); ++ if( bStat1 && ii==1 ){ ++ assert( sqlite3_stricmp(p->azCol[ii], "idx")==0 ); ++ sessionAppendStr(&buf, ++ "idx IS CASE " ++ "WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL " ++ "ELSE ?4 END ", &rc ++ ); ++ }else{ ++ sessionAppendIdent(&buf, p->azCol[ii], &rc); ++ sessionAppendStr(&buf, " IS ?", &rc); ++ sessionAppendInteger(&buf, ii*2+2, &rc); ++ } ++ zSep = " AND "; ++ } ++ } ++ ++ if( rc==SQLITE_OK ){ ++ char *zSql = (char*)buf.aBuf; ++ rc = sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0); ++ } ++ ++ if( rc!=SQLITE_OK ){ ++ sqlite3_free(pUp); ++ pUp = 0; ++ }else{ ++ pUp->pNext = p->pUp; ++ p->pUp = pUp; ++ } ++ sqlite3_free(buf.aBuf); ++ } ++ } ++ } ++ ++ assert( (rc==SQLITE_OK)==(pUp!=0) ); ++ if( pUp ){ ++ *ppStmt = pUp->pStmt; ++ }else{ ++ *ppStmt = 0; ++ } ++ return rc; ++} ++ ++/* ++** Free all cached UPDATE statements. ++*/ ++static void sessionUpdateFree(SessionApplyCtx *p){ ++ SessionUpdate *pUp; ++ SessionUpdate *pNext; ++ for(pUp=p->pUp; pUp; pUp=pNext){ ++ pNext = pUp->pNext; ++ sqlite3_finalize(pUp->pStmt); ++ sqlite3_free(pUp); ++ } ++ p->pUp = 0; ++ sqlite3_free(p->aUpdateMask); ++ p->aUpdateMask = 0; ++} ++ + /* + ** Formulate a statement to DELETE a row from database db. Assuming a table + ** structure like this: +@@ -205994,103 +232661,6 @@ static int sessionDeleteRow( + return rc; + } + +-/* +-** Formulate and prepare a statement to UPDATE a row from database db. +-** Assuming a table structure like this: +-** +-** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c)); +-** +-** The UPDATE statement looks like this: +-** +-** UPDATE x SET +-** a = CASE WHEN ?2 THEN ?3 ELSE a END, +-** b = CASE WHEN ?5 THEN ?6 ELSE b END, +-** c = CASE WHEN ?8 THEN ?9 ELSE c END, +-** d = CASE WHEN ?11 THEN ?12 ELSE d END +-** WHERE a = ?1 AND c = ?7 AND (?13 OR +-** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND +-** ) +-** +-** For each column in the table, there are three variables to bind: +-** +-** ?(i*3+1) The old.* value of the column, if any. +-** ?(i*3+2) A boolean flag indicating that the value is being modified. +-** ?(i*3+3) The new.* value of the column, if any. +-** +-** Also, a boolean flag that, if set to true, causes the statement to update +-** a row even if the non-PK values do not match. This is required if the +-** conflict-handler is invoked with CHANGESET_DATA and returns +-** CHANGESET_REPLACE. This is variable "?(nCol*3+1)". +-** +-** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left +-** pointing to the prepared version of the SQL statement. +-*/ +-static int sessionUpdateRow( +- sqlite3 *db, /* Database handle */ +- const char *zTab, /* Table name */ +- SessionApplyCtx *p /* Session changeset-apply context */ +-){ +- int rc = SQLITE_OK; +- int i; +- const char *zSep = ""; +- SessionBuffer buf = {0, 0, 0}; +- +- /* Append "UPDATE tbl SET " */ +- sessionAppendStr(&buf, "UPDATE main.", &rc); +- sessionAppendIdent(&buf, zTab, &rc); +- sessionAppendStr(&buf, " SET ", &rc); +- +- /* Append the assignments */ +- for(i=0; inCol; i++){ +- sessionAppendStr(&buf, zSep, &rc); +- sessionAppendIdent(&buf, p->azCol[i], &rc); +- sessionAppendStr(&buf, " = CASE WHEN ?", &rc); +- sessionAppendInteger(&buf, i*3+2, &rc); +- sessionAppendStr(&buf, " THEN ?", &rc); +- sessionAppendInteger(&buf, i*3+3, &rc); +- sessionAppendStr(&buf, " ELSE ", &rc); +- sessionAppendIdent(&buf, p->azCol[i], &rc); +- sessionAppendStr(&buf, " END", &rc); +- zSep = ", "; +- } +- +- /* Append the PK part of the WHERE clause */ +- sessionAppendStr(&buf, " WHERE ", &rc); +- for(i=0; inCol; i++){ +- if( p->abPK[i] ){ +- sessionAppendIdent(&buf, p->azCol[i], &rc); +- sessionAppendStr(&buf, " = ?", &rc); +- sessionAppendInteger(&buf, i*3+1, &rc); +- sessionAppendStr(&buf, " AND ", &rc); +- } +- } +- +- /* Append the non-PK part of the WHERE clause */ +- sessionAppendStr(&buf, " (?", &rc); +- sessionAppendInteger(&buf, p->nCol*3+1, &rc); +- sessionAppendStr(&buf, " OR 1", &rc); +- for(i=0; inCol; i++){ +- if( !p->abPK[i] ){ +- sessionAppendStr(&buf, " AND (?", &rc); +- sessionAppendInteger(&buf, i*3+2, &rc); +- sessionAppendStr(&buf, "=0 OR ", &rc); +- sessionAppendIdent(&buf, p->azCol[i], &rc); +- sessionAppendStr(&buf, " IS ?", &rc); +- sessionAppendInteger(&buf, i*3+1, &rc); +- sessionAppendStr(&buf, ")", &rc); +- } +- } +- sessionAppendStr(&buf, ")", &rc); +- +- if( rc==SQLITE_OK ){ +- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0); +- } +- sqlite3_free(buf.aBuf); +- +- return rc; +-} +- +- + /* + ** Formulate and prepare an SQL statement to query table zTab by primary + ** key. Assuming the following table structure: +@@ -206109,8 +232679,10 @@ static int sessionSelectRow( + const char *zTab, /* Table name */ + SessionApplyCtx *p /* Session changeset-apply context */ + ){ +- return sessionSelectStmt( +- db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect); ++ /* TODO */ ++ return sessionSelectStmt(db, p->bIgnoreNoop, ++ "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect ++ ); + } + + /* +@@ -206171,17 +232743,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){ + "?3)" + ); + } +- if( rc==SQLITE_OK ){ +- rc = sessionPrepare(db, &p->pUpdate, +- "UPDATE main.sqlite_stat1 SET " +- "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, " +- "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, " +- "stat = CASE WHEN ?8 THEN ?9 ELSE stat END " +- "WHERE tbl=?1 AND idx IS " +- "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END " +- "AND (?10 OR ?8=0 OR stat IS ?7)" +- ); +- } + if( rc==SQLITE_OK ){ + rc = sessionPrepare(db, &p->pDelete, + "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS " +@@ -206247,7 +232808,7 @@ static int sessionBindRow( + + for(i=0; rc==SQLITE_OK && ipSelect; + int rc; /* Return code */ + int nCol; /* Number of columns in table */ + int op; /* Changset operation (SQLITE_UPDATE etc.) */ + const char *zDummy; /* Unused */ + ++ sqlite3_clear_bindings(pSelect); + sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0); + rc = sessionBindRow(pIter, + op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old, +- nCol, abPK, pSelect ++ nCol, p->abPK, pSelect + ); + ++ if( op!=SQLITE_DELETE && p->bIgnoreNoop ){ ++ int ii; ++ for(ii=0; rc==SQLITE_OK && iiabPK[ii]==0 ){ ++ sqlite3_value *pVal = 0; ++ sqlite3changeset_new(pIter, ii, &pVal); ++ sqlite3_bind_int(pSelect, ii+1+nCol, (pVal==0)); ++ if( pVal ) rc = sessionBindValue(pSelect, ii+1, pVal); ++ } ++ } ++ } ++ + if( rc==SQLITE_OK ){ + rc = sqlite3_step(pSelect); + if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect); +@@ -206409,16 +232982,22 @@ static int sessionConflictHandler( + + /* Bind the new.* PRIMARY KEY values to the SELECT statement. */ + if( pbReplace ){ +- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect); ++ rc = sessionSeekToRow(pIter, p); + }else{ + rc = SQLITE_OK; + } + + if( rc==SQLITE_ROW ){ + /* There exists another row with the new.* primary key. */ +- pIter->pConflict = p->pSelect; +- res = xConflict(pCtx, eType, pIter); +- pIter->pConflict = 0; ++ if( p->bIgnoreNoop ++ && sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1) ++ ){ ++ res = SQLITE_CHANGESET_OMIT; ++ }else{ ++ pIter->pConflict = p->pSelect; ++ res = xConflict(pCtx, eType, pIter); ++ pIter->pConflict = 0; ++ } + rc = sqlite3_reset(p->pSelect); + }else if( rc==SQLITE_OK ){ + if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){ +@@ -206498,7 +233077,7 @@ static int sessionApplyOneOp( + int nCol; + int rc = SQLITE_OK; + +- assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect ); ++ assert( p->pDelete && p->pInsert && p->pSelect ); + assert( p->azCol && p->abPK ); + assert( !pbReplace || *pbReplace==0 ); + +@@ -206526,7 +233105,7 @@ static int sessionApplyOneOp( + + sqlite3_step(p->pDelete); + rc = sqlite3_reset(p->pDelete); +- if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ ++ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 && p->bIgnoreNoop==0 ){ + rc = sessionConflictHandler( + SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry + ); +@@ -206538,29 +233117,28 @@ static int sessionApplyOneOp( + + }else if( op==SQLITE_UPDATE ){ + int i; ++ sqlite3_stmt *pUp = 0; ++ int bPatchset = (pbRetry==0 || pIter->bPatchset); ++ ++ rc = sessionUpdateFind(pIter, p, bPatchset, &pUp); + + /* Bind values to the UPDATE statement. */ + for(i=0; rc==SQLITE_OK && ipUpdate, i*3+2, !!pNew); +- if( pOld ){ +- rc = sessionBindValue(p->pUpdate, i*3+1, pOld); ++ if( p->abPK[i] || (bPatchset==0 && pOld) ){ ++ rc = sessionBindValue(pUp, i*2+2, pOld); + } + if( rc==SQLITE_OK && pNew ){ +- rc = sessionBindValue(p->pUpdate, i*3+3, pNew); ++ rc = sessionBindValue(pUp, i*2+1, pNew); + } + } +- if( rc==SQLITE_OK ){ +- sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset); +- } + if( rc!=SQLITE_OK ) return rc; + + /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict, + ** the result will be SQLITE_OK with 0 rows modified. */ +- sqlite3_step(p->pUpdate); +- rc = sqlite3_reset(p->pUpdate); ++ sqlite3_step(pUp); ++ rc = sqlite3_reset(pUp); + + if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ + /* A NOTFOUND or DATA error. Search the table to see if it contains +@@ -206584,7 +233162,7 @@ static int sessionApplyOneOp( + /* Check if there is a conflicting row. For sqlite_stat1, this needs + ** to be done using a SELECT, as there is no PRIMARY KEY in the + ** database schema to throw an exception if a duplicate is inserted. */ +- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect); ++ rc = sessionSeekToRow(pIter, p); + if( rc==SQLITE_ROW ){ + rc = SQLITE_CONSTRAINT; + sqlite3_reset(p->pSelect); +@@ -206692,7 +233270,7 @@ static int sessionRetryConstraints( + memset(&pApply->constraints, 0, sizeof(SessionBuffer)); + + rc = sessionChangesetStart( +- &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints ++ &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1 + ); + if( rc==SQLITE_OK ){ + size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); +@@ -206754,14 +233332,21 @@ static int sessionChangesetApply( + int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ + SessionApplyCtx sApply; /* changeset_apply() context object */ + int bPatchset; ++ u64 savedFlag = db->flags & SQLITE_FkNoAction; + + assert( xConflict!=0 ); + ++ sqlite3_mutex_enter(sqlite3_db_mutex(db)); ++ if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ ++ db->flags |= ((u64)SQLITE_FkNoAction); ++ db->aDb[0].pSchema->schema_cookie -= 32; ++ } ++ + pIter->in.bNoDiscard = 1; + memset(&sApply, 0, sizeof(sApply)); + sApply.bRebase = (ppRebase && pnRebase); + sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); +- sqlite3_mutex_enter(sqlite3_db_mutex(db)); ++ sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); + if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ + rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); + } +@@ -206783,14 +233368,13 @@ static int sessionChangesetApply( + ); + if( rc!=SQLITE_OK ) break; + ++ sessionUpdateFree(&sApply); + sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ + sqlite3_finalize(sApply.pDelete); +- sqlite3_finalize(sApply.pUpdate); + sqlite3_finalize(sApply.pInsert); + sqlite3_finalize(sApply.pSelect); + sApply.db = db; + sApply.pDelete = 0; +- sApply.pUpdate = 0; + sApply.pInsert = 0; + sApply.pSelect = 0; + sApply.nCol = 0; +@@ -206799,6 +233383,7 @@ static int sessionChangesetApply( + sApply.bStat1 = 0; + sApply.bDeferConstraints = 1; + sApply.bRebaseStarted = 0; ++ sApply.bRowid = 0; + memset(&sApply.constraints, 0, sizeof(SessionBuffer)); + + /* If an xFilter() callback was specified, invoke it now. If the +@@ -206818,8 +233403,9 @@ static int sessionChangesetApply( + int i; + + sqlite3changeset_pk(pIter, &abPK, 0); +- rc = sessionTableInfo( +- db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK ++ rc = sessionTableInfo(0, db, "main", zNew, ++ &sApply.nCol, 0, &zTab, &sApply.azCol, 0, 0, ++ &sApply.abPK, &sApply.bRowid + ); + if( rc!=SQLITE_OK ) break; + for(i=0; iflags & SQLITE_FkNoAction ); ++ db->flags &= ~((u64)SQLITE_FkNoAction); ++ db->aDb[0].pSchema->schema_cookie -= 32; ++ } + sqlite3_mutex_leave(sqlite3_db_mutex(db)); + return rc; + } +@@ -206950,13 +233546,15 @@ SQLITE_API int sqlite3changeset_apply_v2( + int flags + ){ + sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ +- int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); +- int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse); ++ int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); ++ int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1); ++ + if( rc==SQLITE_OK ){ + rc = sessionChangesetApply( + db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags + ); + } ++ + return rc; + } + +@@ -207009,7 +233607,7 @@ SQLITE_API int sqlite3changeset_apply_v2_strm( + ){ + sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ + int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); +- int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse); ++ int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1); + if( rc==SQLITE_OK ){ + rc = sessionChangesetApply( + db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags +@@ -207044,6 +233642,10 @@ struct sqlite3_changegroup { + int rc; /* Error code */ + int bPatch; /* True to accumulate patchsets */ + SessionTable *pList; /* List of tables in current patch */ ++ SessionBuffer rec; ++ ++ sqlite3 *db; /* Configured by changegroup_schema() */ ++ char *zDb; /* Configured by changegroup_schema() */ + }; + + /* +@@ -207064,6 +233666,7 @@ static int sessionChangeMerge( + ){ + SessionChange *pNew = 0; + int rc = SQLITE_OK; ++ assert( aRec!=0 ); + + if( !pExist ){ + pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec); +@@ -207230,84 +233833,242 @@ static int sessionChangeMerge( + } + + /* +-** Add all changes in the changeset traversed by the iterator passed as +-** the first argument to the changegroup hash tables. ++** Check if a changeset entry with nCol columns and the PK array passed ++** as the final argument to this function is compatible with SessionTable ++** pTab. If so, return 1. Otherwise, if they are incompatible in some way, ++** return 0. + */ +-static int sessionChangesetToHash( +- sqlite3_changeset_iter *pIter, /* Iterator to read from */ +- sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ +- int bRebase /* True if hash table is for rebasing */ ++static int sessionChangesetCheckCompat( ++ SessionTable *pTab, ++ int nCol, ++ u8 *abPK + ){ +- u8 *aRec; +- int nRec; +- int rc = SQLITE_OK; +- SessionTable *pTab = 0; +- +- while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ +- const char *zNew; +- int nCol; +- int op; +- int iHash; +- int bIndirect; +- SessionChange *pChange; +- SessionChange *pExist = 0; +- SessionChange **pp; +- +- if( pGrp->pList==0 ){ +- pGrp->bPatch = pIter->bPatchset; +- }else if( pIter->bPatchset!=pGrp->bPatch ){ +- rc = SQLITE_ERROR; +- break; ++ if( pTab->azCol && nColnCol ){ ++ int ii; ++ for(ii=0; iinCol; ii++){ ++ u8 bPK = (ii < nCol) ? abPK[ii] : 0; ++ if( pTab->abPK[ii]!=bPK ) return 0; + } ++ return 1; ++ } ++ return (pTab->nCol==nCol && 0==memcmp(abPK, pTab->abPK, nCol)); ++} + +- sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); +- if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ +- /* Search the list for a matching table */ +- int nNew = (int)strlen(zNew); +- u8 *abPK; ++static int sessionChangesetExtendRecord( ++ sqlite3_changegroup *pGrp, ++ SessionTable *pTab, ++ int nCol, ++ int op, ++ const u8 *aRec, ++ int nRec, ++ SessionBuffer *pOut ++){ ++ int rc = SQLITE_OK; ++ int ii = 0; + +- sqlite3changeset_pk(pIter, &abPK, 0); +- for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ +- if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; ++ assert( pTab->azCol ); ++ assert( nColnCol ); ++ ++ pOut->nBuf = 0; ++ if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){ ++ /* Append the missing default column values to the record. */ ++ sessionAppendBlob(pOut, aRec, nRec, &rc); ++ if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){ ++ rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt); ++ if( rc==SQLITE_OK && SQLITE_ROW!=sqlite3_step(pTab->pDfltStmt) ){ ++ rc = sqlite3_errcode(pGrp->db); + } +- if( !pTab ){ +- SessionTable **ppTab; ++ } ++ for(ii=nCol; rc==SQLITE_OK && iinCol; ii++){ ++ int eType = sqlite3_column_type(pTab->pDfltStmt, ii); ++ sessionAppendByte(pOut, eType, &rc); ++ switch( eType ){ ++ case SQLITE_FLOAT: ++ case SQLITE_INTEGER: { ++ i64 iVal; ++ if( eType==SQLITE_INTEGER ){ ++ iVal = sqlite3_column_int64(pTab->pDfltStmt, ii); ++ }else{ ++ double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii); ++ memcpy(&iVal, &rVal, sizeof(i64)); ++ } ++ if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ ++ sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); ++ pOut->nBuf += 8; ++ } ++ break; ++ } + +- pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); +- if( !pTab ){ +- rc = SQLITE_NOMEM; ++ case SQLITE_BLOB: ++ case SQLITE_TEXT: { ++ int n = sqlite3_column_bytes(pTab->pDfltStmt, ii); ++ sessionAppendVarint(pOut, n, &rc); ++ if( eType==SQLITE_TEXT ){ ++ const u8 *z = (const u8*)sqlite3_column_text(pTab->pDfltStmt, ii); ++ sessionAppendBlob(pOut, z, n, &rc); ++ }else{ ++ const u8 *z = (const u8*)sqlite3_column_blob(pTab->pDfltStmt, ii); ++ sessionAppendBlob(pOut, z, n, &rc); ++ } + break; + } +- memset(pTab, 0, sizeof(SessionTable)); +- pTab->nCol = nCol; +- pTab->abPK = (u8*)&pTab[1]; +- memcpy(pTab->abPK, abPK, nCol); +- pTab->zName = (char*)&pTab->abPK[nCol]; +- memcpy(pTab->zName, zNew, nNew+1); +- +- /* The new object must be linked on to the end of the list, not +- ** simply added to the start of it. This is to ensure that the +- ** tables within the output of sqlite3changegroup_output() are in +- ** the right order. */ +- for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext); +- *ppTab = pTab; +- }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){ +- rc = SQLITE_SCHEMA; +- break; ++ ++ default: ++ assert( eType==SQLITE_NULL ); ++ break; ++ } ++ } ++ }else if( op==SQLITE_UPDATE ){ ++ /* Append missing "undefined" entries to the old.* record. And, if this ++ ** is an UPDATE, to the new.* record as well. */ ++ int iOff = 0; ++ if( pGrp->bPatch==0 ){ ++ for(ii=0; iinCol-nCol); ii++){ ++ sessionAppendByte(pOut, 0x00, &rc); + } + } + +- if( sessionGrowHash(pIter->bPatchset, pTab) ){ +- rc = SQLITE_NOMEM; +- break; ++ sessionAppendBlob(pOut, &aRec[iOff], nRec-iOff, &rc); ++ for(ii=0; ii<(pTab->nCol-nCol); ii++){ ++ sessionAppendByte(pOut, 0x00, &rc); ++ } ++ }else{ ++ assert( op==SQLITE_DELETE && pGrp->bPatch ); ++ sessionAppendBlob(pOut, aRec, nRec, &rc); ++ } ++ ++ return rc; ++} ++ ++/* ++** Locate or create a SessionTable object that may be used to add the ++** change currently pointed to by iterator pIter to changegroup pGrp. ++** If successful, set output variable (*ppTab) to point to the table ++** object and return SQLITE_OK. Otherwise, if some error occurs, return ++** an SQLite error code and leave (*ppTab) set to NULL. ++*/ ++static int sessionChangesetFindTable( ++ sqlite3_changegroup *pGrp, ++ const char *zTab, ++ sqlite3_changeset_iter *pIter, ++ SessionTable **ppTab ++){ ++ int rc = SQLITE_OK; ++ SessionTable *pTab = 0; ++ int nTab = (int)strlen(zTab); ++ u8 *abPK = 0; ++ int nCol = 0; ++ ++ *ppTab = 0; ++ sqlite3changeset_pk(pIter, &abPK, &nCol); ++ ++ /* Search the list for an existing table */ ++ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ ++ if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; ++ } ++ ++ /* If one was not found above, create a new table now */ ++ if( !pTab ){ ++ SessionTable **ppNew; ++ ++ pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1); ++ if( !pTab ){ ++ return SQLITE_NOMEM; ++ } ++ memset(pTab, 0, sizeof(SessionTable)); ++ pTab->nCol = nCol; ++ pTab->abPK = (u8*)&pTab[1]; ++ memcpy(pTab->abPK, abPK, nCol); ++ pTab->zName = (char*)&pTab->abPK[nCol]; ++ memcpy(pTab->zName, zTab, nTab+1); ++ ++ if( pGrp->db ){ ++ pTab->nCol = 0; ++ rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); ++ if( rc ){ ++ assert( pTab->azCol==0 ); ++ sqlite3_free(pTab); ++ return rc; ++ } + } ++ ++ /* The new object must be linked on to the end of the list, not ++ ** simply added to the start of it. This is to ensure that the ++ ** tables within the output of sqlite3changegroup_output() are in ++ ** the right order. */ ++ for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext); ++ *ppNew = pTab; ++ } ++ ++ /* Check that the table is compatible. */ ++ if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ ++ rc = SQLITE_SCHEMA; ++ } ++ ++ *ppTab = pTab; ++ return rc; ++} ++ ++/* ++** Add the change currently indicated by iterator pIter to the hash table ++** belonging to changegroup pGrp. ++*/ ++static int sessionOneChangeToHash( ++ sqlite3_changegroup *pGrp, ++ sqlite3_changeset_iter *pIter, ++ int bRebase ++){ ++ int rc = SQLITE_OK; ++ int nCol = 0; ++ int op = 0; ++ int iHash = 0; ++ int bIndirect = 0; ++ SessionChange *pChange = 0; ++ SessionChange *pExist = 0; ++ SessionChange **pp = 0; ++ SessionTable *pTab = 0; ++ u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; ++ int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; ++ ++ assert( nRec>0 ); ++ ++ /* Ensure that only changesets, or only patchsets, but not a mixture ++ ** of both, are being combined. It is an error to try to combine a ++ ** changeset and a patchset. */ ++ if( pGrp->pList==0 ){ ++ pGrp->bPatch = pIter->bPatchset; ++ }else if( pIter->bPatchset!=pGrp->bPatch ){ ++ rc = SQLITE_ERROR; ++ } ++ ++ if( rc==SQLITE_OK ){ ++ const char *zTab = 0; ++ sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); ++ rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); ++ } ++ ++ if( rc==SQLITE_OK && nColnCol ){ ++ SessionBuffer *pBuf = &pGrp->rec; ++ rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); ++ aRec = pBuf->aBuf; ++ nRec = pBuf->nBuf; ++ assert( pGrp->db ); ++ } ++ ++ if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ ++ rc = SQLITE_NOMEM; ++ } ++ ++ if( rc==SQLITE_OK ){ ++ /* Search for existing entry. If found, remove it from the hash table. ++ ** Code below may link it back in. */ + iHash = sessionChangeHash( + pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange + ); +- +- /* Search for existing entry. If found, remove it from the hash table. +- ** Code below may link it back in. +- */ + for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ + int bPkOnly1 = 0; + int bPkOnly2 = 0; +@@ -207322,16 +234083,40 @@ static int sessionChangesetToHash( + break; + } + } ++ } + ++ if( rc==SQLITE_OK ){ + rc = sessionChangeMerge(pTab, bRebase, + pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange + ); +- if( rc ) break; +- if( pChange ){ +- pChange->pNext = pTab->apChange[iHash]; +- pTab->apChange[iHash] = pChange; +- pTab->nEntry++; +- } ++ } ++ if( rc==SQLITE_OK && pChange ){ ++ pChange->pNext = pTab->apChange[iHash]; ++ pTab->apChange[iHash] = pChange; ++ pTab->nEntry++; ++ } ++ ++ if( rc==SQLITE_OK ) rc = pIter->rc; ++ return rc; ++} ++ ++/* ++** Add all changes in the changeset traversed by the iterator passed as ++** the first argument to the changegroup hash tables. ++*/ ++static int sessionChangesetToHash( ++ sqlite3_changeset_iter *pIter, /* Iterator to read from */ ++ sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ ++ int bRebase /* True if hash table is for rebasing */ ++){ ++ u8 *aRec; ++ int nRec; ++ int rc = SQLITE_OK; ++ ++ pIter->in.bNoDiscard = 1; ++ while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ ++ rc = sessionOneChangeToHash(pGrp, pIter, bRebase); ++ if( rc!=SQLITE_OK ) break; + } + + if( rc==SQLITE_OK ) rc = pIter->rc; +@@ -207393,9 +234178,9 @@ static int sessionChangegroupOutput( + if( rc==SQLITE_OK ){ + if( xOutput ){ + if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf); +- }else{ ++ }else if( ppOut ){ + *ppOut = buf.aBuf; +- *pnOut = buf.nBuf; ++ if( pnOut ) *pnOut = buf.nBuf; + buf.aBuf = 0; + } + } +@@ -207420,6 +234205,31 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){ + return rc; + } + ++/* ++** Provide a database schema to the changegroup object. ++*/ ++SQLITE_API int sqlite3changegroup_schema( ++ sqlite3_changegroup *pGrp, ++ sqlite3 *db, ++ const char *zDb ++){ ++ int rc = SQLITE_OK; ++ ++ if( pGrp->pList || pGrp->db ){ ++ /* Cannot add a schema after one or more calls to sqlite3changegroup_add(), ++ ** or after sqlite3changegroup_schema() has already been called. */ ++ rc = SQLITE_MISUSE; ++ }else{ ++ pGrp->zDb = sqlite3_mprintf("%s", zDb); ++ if( pGrp->zDb==0 ){ ++ rc = SQLITE_NOMEM; ++ }else{ ++ pGrp->db = db; ++ } ++ } ++ return rc; ++} ++ + /* + ** Add the changeset currently stored in buffer pData, size nData bytes, + ** to changeset-group p. +@@ -207436,6 +234246,28 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void + return rc; + } + ++/* ++** Add a single change to a changeset-group. ++*/ ++SQLITE_API int sqlite3changegroup_add_change( ++ sqlite3_changegroup *pGrp, ++ sqlite3_changeset_iter *pIter ++){ ++ int rc = SQLITE_OK; ++ ++ if( pIter->in.iCurrent==pIter->in.iNext ++ || pIter->rc!=SQLITE_OK ++ || pIter->bInvert ++ ){ ++ /* Iterator does not point to any valid entry or is an INVERT iterator. */ ++ rc = SQLITE_ERROR; ++ }else{ ++ pIter->in.bNoDiscard = 1; ++ rc = sessionOneChangeToHash(pGrp, pIter, 0); ++ } ++ return rc; ++} ++ + /* + ** Obtain a buffer containing a changeset representing the concatenation + ** of all changesets added to the group so far. +@@ -207483,7 +234315,9 @@ SQLITE_API int sqlite3changegroup_output_strm( + */ + SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ + if( pGrp ){ +- sessionDeleteTable(pGrp->pList); ++ sqlite3_free(pGrp->zDb); ++ sessionDeleteTable(0, pGrp->pList); ++ sqlite3_free(pGrp->rec.aBuf); + sqlite3_free(pGrp); + } + } +@@ -207629,10 +234463,10 @@ static void sessionAppendPartialUpdate( + int n1 = sessionSerialLen(a1); + int n2 = sessionSerialLen(a2); + if( pIter->abPK[i] || a2[0]==0 ){ +- if( !pIter->abPK[i] ) bData = 1; ++ if( !pIter->abPK[i] && a1[0] ) bData = 1; + memcpy(pOut, a1, n1); + pOut += n1; +- }else if( a2[0]!=0xFF ){ ++ }else if( a2[0]!=0xFF && a1[0] ){ + bData = 1; + memcpy(pOut, a2, n2); + pOut += n2; +@@ -207795,7 +234629,7 @@ static int sessionRebase( + if( sOut.nBuf>0 ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + } +- }else{ ++ }else if( ppOut ){ + *ppOut = (void*)sOut.aBuf; + *pnOut = sOut.nBuf; + sOut.aBuf = 0; +@@ -207884,7 +234718,8 @@ SQLITE_API int sqlite3rebaser_rebase_strm( + */ + SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ + if( p ){ +- sessionDeleteTable(p->grp.pList); ++ sessionDeleteTable(0, p->grp.pList); ++ sqlite3_free(p->grp.rec.aBuf); + sqlite3_free(p); + } + } +@@ -207915,7 +234750,27 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){ + /************** End of sqlite3session.c **************************************/ + /************** Begin file fts5.c ********************************************/ + +- ++/* ++** This, the "fts5.c" source file, is a composite file that is itself ++** assembled from the following files: ++** ++** fts5.h ++** fts5Int.h ++** fts5parse.h <--- Generated from fts5parse.y by Lemon ++** fts5parse.c <--- Generated from fts5parse.y by Lemon ++** fts5_aux.c ++** fts5_buffer.c ++** fts5_config.c ++** fts5_expr.c ++** fts5_hash.c ++** fts5_index.c ++** fts5_main.c ++** fts5_storage.c ++** fts5_tokenize.c ++** fts5_unicode2.c ++** fts5_varint.c ++** fts5_vocab.c ++*/ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) + + #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) +@@ -207925,6 +234780,12 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){ + # undef NDEBUG + #endif + ++#ifdef HAVE_STDINT_H ++/* #include */ ++#endif ++#ifdef HAVE_INTTYPES_H ++/* #include */ ++#endif + /* + ** 2014 May 31 + ** +@@ -207982,8 +234843,8 @@ struct Fts5PhraseIter { + ** EXTENSION API FUNCTIONS + ** + ** xUserData(pFts): +-** Return a copy of the context pointer the extension function was +-** registered with. ++** Return a copy of the pUserData pointer passed to the xCreateFunction() ++** API when the extension function was registered. + ** + ** xColumnTotalSize(pFts, iCol, pnToken): + ** If parameter iCol is less than zero, set output variable *pnToken +@@ -208015,8 +234876,11 @@ struct Fts5PhraseIter { + ** created with the "columnsize=0" option. + ** + ** xColumnText: +-** This function attempts to retrieve the text of column iCol of the +-** current document. If successful, (*pz) is set to point to a buffer ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of columns in the table, SQLITE_RANGE is returned. ++** ++** Otherwise, this function attempts to retrieve the text of column iCol of ++** the current document. If successful, (*pz) is set to point to a buffer + ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes + ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, + ** if an error occurs, an SQLite error code is returned and the final values +@@ -208026,8 +234890,10 @@ struct Fts5PhraseIter { + ** Returns the number of phrases in the current query expression. + ** + ** xPhraseSize: +-** Returns the number of tokens in phrase iPhrase of the query. Phrases +-** are numbered starting from zero. ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of phrases in the current query, as returned by xPhraseCount, ++** 0 is returned. Otherwise, this function returns the number of tokens in ++** phrase iPhrase of the query. Phrases are numbered starting from zero. + ** + ** xInstCount: + ** Set *pnInst to the total number of occurrences of all phrases within +@@ -208043,12 +234909,13 @@ struct Fts5PhraseIter { + ** Query for the details of phrase match iIdx within the current row. + ** Phrase matches are numbered starting from zero, so the iIdx argument + ** should be greater than or equal to zero and smaller than the value +-** output by xInstCount(). ++** output by xInstCount(). If iIdx is less than zero or greater than ++** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. + ** +-** Usually, output parameter *piPhrase is set to the phrase number, *piCol ++** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol + ** to the column in which it occurs and *piOff the token offset of the +-** first token of the phrase. Returns SQLITE_OK if successful, or an error +-** code (i.e. SQLITE_NOMEM) if an error occurs. ++** first token of the phrase. SQLITE_OK is returned if successful, or an ++** error code (i.e. SQLITE_NOMEM) if an error occurs. + ** + ** This API can be quite slow if used with an FTS5 table created with the + ** "detail=none" or "detail=column" option. +@@ -208074,6 +234941,10 @@ struct Fts5PhraseIter { + ** Invoking Api.xUserData() returns a copy of the pointer passed as + ** the third argument to pUserData. + ** ++** If parameter iPhrase is less than zero, or greater than or equal to ++** the number of phrases in the query, as returned by xPhraseCount(), ++** this function returns SQLITE_RANGE. ++** + ** If the callback function returns any value other than SQLITE_OK, the + ** query is abandoned and the xQueryPhrase function returns immediately. + ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. +@@ -208155,6 +235026,10 @@ struct Fts5PhraseIter { + ** (i.e. if it is a contentless table), then this API always iterates + ** through an empty set (all calls to xPhraseFirst() set iCol to -1). + ** ++** In all cases, matches are visited in (column ASC, offset ASC) order. ++** i.e. all those in column 0, sorted by offset, followed by those in ++** column 1, etc. ++** + ** xPhraseNext() + ** See xPhraseFirst above. + ** +@@ -208188,9 +235063,80 @@ struct Fts5PhraseIter { + ** + ** xPhraseNextColumn() + ** See xPhraseFirstColumn above. ++** ++** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) ++** This is used to access token iToken of phrase iPhrase of the current ++** query. Before returning, output parameter *ppToken is set to point ++** to a buffer containing the requested token, and *pnToken to the ++** size of this buffer in bytes. ++** ++** If iPhrase or iToken are less than zero, or if iPhrase is greater than ++** or equal to the number of phrases in the query as reported by ++** xPhraseCount(), or if iToken is equal to or greater than the number of ++** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken ++ are both zeroed. ++** ++** The output text is not a copy of the query text that specified the ++** token. It is the output of the tokenizer module. For tokendata=1 ++** tables, this includes any embedded 0x00 and trailing data. ++** ++** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ++** This is used to access token iToken of phrase hit iIdx within the ++** current row. If iIdx is less than zero or greater than or equal to the ++** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ++** output variable (*ppToken) is set to point to a buffer containing the ++** matching document token, and (*pnToken) to the size of that buffer in ++** bytes. ++** ++** The output text is not a copy of the document text that was tokenized. ++** It is the output of the tokenizer module. For tokendata=1 tables, this ++** includes any embedded 0x00 and trailing data. ++** ++** This API may be slow in some cases if the token identified by parameters ++** iIdx and iToken matched a prefix token in the query. In most cases, the ++** first call to this API for each prefix token in the query is forced ++** to scan the portion of the full-text index that matches the prefix ++** token to collect the extra data required by this API. If the prefix ++** token matches a large number of token instances in the document set, ++** this may be a performance problem. ++** ++** If the user knows in advance that a query may use this API for a ++** prefix token, FTS5 may be configured to collect all required data as part ++** of the initial querying of the full-text index, avoiding the second scan ++** entirely. This also causes prefix queries that do not use this API to ++** run more slowly and use more memory. FTS5 may be configured in this way ++** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ++** option, or on a per-query basis using the ++** [fts5_insttoken | fts5_insttoken()] user function. ++** ++** This API can be quite slow if used with an FTS5 table created with the ++** "detail=none" or "detail=column" option. ++** ++** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of columns in the table, SQLITE_RANGE is returned. ++** ++** Otherwise, this function attempts to retrieve the locale associated ++** with column iCol of the current row. Usually, there is no associated ++** locale, and output parameters (*pzLocale) and (*pnLocale) are set ++** to NULL and 0, respectively. However, if the fts5_locale() function ++** was used to associate a locale with the value when it was inserted ++** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated ++** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) ++** is set to the size in bytes of the buffer, not including the ++** nul-terminator. ++** ++** If successful, SQLITE_OK is returned. Or, if an error occurs, an ++** SQLite error code is returned. The final value of the output parameters ++** is undefined in this case. ++** ++** xTokenize_v2: ++** Tokenize text using the tokenizer belonging to the FTS5 table. This ++** API is the same as the xTokenize() API, except that it allows a tokenizer ++** locale to be specified. + */ + struct Fts5ExtensionApi { +- int iVersion; /* Currently always set to 3 */ ++ int iVersion; /* Currently always set to 4 */ + + void *(*xUserData)(Fts5Context*); + +@@ -208225,6 +235171,22 @@ struct Fts5ExtensionApi { + + int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); + void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); ++ ++ /* Below this point are iVersion>=3 only */ ++ int (*xQueryToken)(Fts5Context*, ++ int iPhrase, int iToken, ++ const char **ppToken, int *pnToken ++ ); ++ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); ++ ++ /* Below this point are iVersion>=4 only */ ++ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); ++ int (*xTokenize_v2)(Fts5Context*, ++ const char *pText, int nText, /* Text to tokenize */ ++ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ ++ void *pCtx, /* Context passed to xToken() */ ++ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ ++ ); + }; + + /* +@@ -208245,7 +235207,7 @@ struct Fts5ExtensionApi { + ** A tokenizer instance is required to actually tokenize text. + ** + ** The first argument passed to this function is a copy of the (void*) +-** pointer provided by the application when the fts5_tokenizer object ++** pointer provided by the application when the fts5_tokenizer_v2 object + ** was registered with FTS5 (the third argument to xCreateTokenizer()). + ** The second and third arguments are an array of nul-terminated strings + ** containing the tokenizer arguments, if any, specified following the +@@ -208269,7 +235231,7 @@ struct Fts5ExtensionApi { + ** argument passed to this function is a pointer to an Fts5Tokenizer object + ** returned by an earlier call to xCreate(). + ** +-** The second argument indicates the reason that FTS5 is requesting ++** The third argument indicates the reason that FTS5 is requesting + ** tokenization of the supplied text. This is always one of the following + ** four values: + ** +@@ -208293,6 +235255,13 @@ struct Fts5ExtensionApi { + ** on a columnsize=0 database. + ** + ** ++** The sixth and seventh arguments passed to xTokenize() - pLocale and ++** nLocale - are a pointer to a buffer containing the locale to use for ++** tokenization (e.g. "en_US") and its size in bytes, respectively. The ++** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in ++** which case nLocale is always 0) to indicate that the tokenizer should ++** use its default locale. ++** + ** For each token in the input string, the supplied callback xToken() must + ** be invoked. The first argument to it should be a copy of the pointer + ** passed as the second argument to xTokenize(). The third and fourth +@@ -208316,6 +235285,30 @@ struct Fts5ExtensionApi { + ** may abandon the tokenization and return any error code other than + ** SQLITE_OK or SQLITE_DONE. + ** ++** If the tokenizer is registered using an fts5_tokenizer_v2 object, ++** then the xTokenize() method has two additional arguments - pLocale ++** and nLocale. These specify the locale that the tokenizer should use ++** for the current request. If pLocale and nLocale are both 0, then the ++** tokenizer should use its default locale. Otherwise, pLocale points to ++** an nLocale byte buffer containing the name of the locale to use as utf-8 ++** text. pLocale is not nul-terminated. ++** ++** FTS5_TOKENIZER ++** ++** There is also an fts5_tokenizer object. This is an older, deprecated, ++** version of fts5_tokenizer_v2. It is similar except that: ++** ++**
          ++**
        • There is no "iVersion" field, and ++**
        • The xTokenize() method does not take a locale argument. ++**
        ++** ++** Legacy fts5_tokenizer tokenizers must be registered using the ++** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). ++** ++** Tokenizer implementations registered using either API may be retrieved ++** using both xFindTokenizer() and xFindTokenizer_v2(). ++** + ** SYNONYM SUPPORT + ** + ** Custom tokenizers may also support synonyms. Consider a case in which a +@@ -208419,11 +235412,38 @@ struct Fts5ExtensionApi { + ** as separate queries of the FTS index are required for each synonym. + ** + ** When using methods (2) or (3), it is important that the tokenizer only +-** provide synonyms when tokenizing document text (method (2)) or query +-** text (method (3)), not both. Doing so will not cause any errors, but is ++** provide synonyms when tokenizing document text (method (3)) or query ++** text (method (2)), not both. Doing so will not cause any errors, but is + ** inefficient. + */ + typedef struct Fts5Tokenizer Fts5Tokenizer; ++typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; ++struct fts5_tokenizer_v2 { ++ int iVersion; /* Currently always 2 */ ++ ++ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); ++ void (*xDelete)(Fts5Tokenizer*); ++ int (*xTokenize)(Fts5Tokenizer*, ++ void *pCtx, ++ int flags, /* Mask of FTS5_TOKENIZE_* flags */ ++ const char *pText, int nText, ++ const char *pLocale, int nLocale, ++ int (*xToken)( ++ void *pCtx, /* Copy of 2nd argument to xTokenize() */ ++ int tflags, /* Mask of FTS5_TOKEN_* flags */ ++ const char *pToken, /* Pointer to buffer containing token */ ++ int nToken, /* Size of token in bytes */ ++ int iStart, /* Byte offset of token within input text */ ++ int iEnd /* Byte offset of end of token within input text */ ++ ) ++ ); ++}; ++ ++/* ++** New code should use the fts5_tokenizer_v2 type to define tokenizer ++** implementations. The following type is included for legacy applications ++** that still use it. ++*/ + typedef struct fts5_tokenizer fts5_tokenizer; + struct fts5_tokenizer { + int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); +@@ -208443,6 +235463,7 @@ struct fts5_tokenizer { + ); + }; + ++ + /* Flags that may be passed as the third argument to xTokenize() */ + #define FTS5_TOKENIZE_QUERY 0x0001 + #define FTS5_TOKENIZE_PREFIX 0x0002 +@@ -208462,13 +235483,13 @@ struct fts5_tokenizer { + */ + typedef struct fts5_api fts5_api; + struct fts5_api { +- int iVersion; /* Currently always set to 2 */ ++ int iVersion; /* Currently always set to 3 */ + + /* Create a new tokenizer */ + int (*xCreateTokenizer)( + fts5_api *pApi, + const char *zName, +- void *pContext, ++ void *pUserData, + fts5_tokenizer *pTokenizer, + void (*xDestroy)(void*) + ); +@@ -208477,7 +235498,7 @@ struct fts5_api { + int (*xFindTokenizer)( + fts5_api *pApi, + const char *zName, +- void **ppContext, ++ void **ppUserData, + fts5_tokenizer *pTokenizer + ); + +@@ -208485,10 +235506,29 @@ struct fts5_api { + int (*xCreateFunction)( + fts5_api *pApi, + const char *zName, +- void *pContext, ++ void *pUserData, + fts5_extension_function xFunction, + void (*xDestroy)(void*) + ); ++ ++ /* APIs below this point are only available if iVersion>=3 */ ++ ++ /* Create a new tokenizer */ ++ int (*xCreateTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void *pUserData, ++ fts5_tokenizer_v2 *pTokenizer, ++ void (*xDestroy)(void*) ++ ); ++ ++ /* Find an existing tokenizer */ ++ int (*xFindTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void **ppUserData, ++ fts5_tokenizer_v2 **ppTokenizer ++ ); + }; + + /* +@@ -208523,6 +235563,7 @@ SQLITE_EXTENSION_INIT1 + + /* #include */ + /* #include */ ++/* #include */ + + #ifndef SQLITE_AMALGAMATION + +@@ -208538,8 +235579,20 @@ typedef sqlite3_uint64 u64; + #endif + + #define testcase(x) +-#define ALWAYS(x) 1 +-#define NEVER(x) 0 ++ ++#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) ++# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 ++#endif ++#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) ++# define ALWAYS(X) (1) ++# define NEVER(X) (0) ++#elif !defined(NDEBUG) ++# define ALWAYS(X) ((X)?1:(assert(0),0)) ++# define NEVER(X) ((X)?(assert(0),1):0) ++#else ++# define ALWAYS(X) (X) ++# define NEVER(X) (X) ++#endif + + #define MIN(x,y) (((x) < (y)) ? (x) : (y)) + #define MAX(x,y) (((x) > (y)) ? (x) : (y)) +@@ -208550,6 +235603,34 @@ typedef sqlite3_uint64 u64; + # define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) + # define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) + ++/* The uptr type is an unsigned integer large enough to hold a pointer ++*/ ++#if defined(HAVE_STDINT_H) ++ typedef uintptr_t uptr; ++#elif SQLITE_PTRSIZE==4 ++ typedef u32 uptr; ++#else ++ typedef u64 uptr; ++#endif ++ ++#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC ++# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) ++#else ++# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) ++#endif ++ ++/* ++** Macros needed to provide flexible arrays in a portable way ++*/ ++#ifndef offsetof ++# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) ++#endif ++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ++# define FLEXARRAY ++#else ++# define FLEXARRAY 1 ++#endif ++ + #endif + + /* Truncate very long tokens to this many bytes. Hard limit is +@@ -208599,7 +235680,7 @@ SQLITE_API extern int sqlite3_fts5_may_be_corrupt; + ** A version of memcmp() that does not cause asan errors if one of the pointer + ** parameters is NULL and the number of bytes to compare is zero. + */ +-#define fts5Memcmp(s1, s2, n) ((n)==0 ? 0 : memcmp((s1), (s2), (n))) ++#define fts5Memcmp(s1, s2, n) ((n)<=0 ? 0 : memcmp((s1), (s2), (n))) + + /* Mark a function parameter as unused, to suppress nuisance compiler + ** warnings. */ +@@ -208622,10 +235703,11 @@ typedef struct Fts5Colset Fts5Colset; + */ + struct Fts5Colset { + int nCol; +- int aiCol[1]; ++ int aiCol[FLEXARRAY]; + }; + +- ++/* Size (int bytes) of a complete Fts5Colset object with N columns. */ ++#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2)) + + /************************************************************************** + ** Interface to code in fts5_config.c. fts5_config.c contains contains code +@@ -208633,6 +235715,18 @@ struct Fts5Colset { + */ + + typedef struct Fts5Config Fts5Config; ++typedef struct Fts5TokenizerConfig Fts5TokenizerConfig; ++ ++struct Fts5TokenizerConfig { ++ Fts5Tokenizer *pTok; ++ fts5_tokenizer_v2 *pApi2; ++ fts5_tokenizer *pApi1; ++ const char **azArg; ++ int nArg; ++ int ePattern; /* FTS_PATTERN_XXX constant */ ++ const char *pLocale; /* Current locale to use */ ++ int nLocale; /* Size of pLocale in bytes */ ++}; + + /* + ** An instance of the following structure encodes all information that can +@@ -208645,6 +235739,10 @@ typedef struct Fts5Config Fts5Config; + ** attempt to merge together. A value of 1 sets the object to use the + ** compile time default. Zero disables auto-merge altogether. + ** ++** bContentlessDelete: ++** True if the contentless_delete option was present in the CREATE ++** VIRTUAL TABLE statement. ++** + ** zContent: + ** + ** zContentRowid: +@@ -208668,9 +235766,12 @@ typedef struct Fts5Config Fts5Config; + ** + ** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex); + ** ++** bLocale: ++** Set to true if locale=1 was specified when the table was created. + */ + struct Fts5Config { + sqlite3 *db; /* Database handle */ ++ Fts5Global *pGlobal; /* Global fts5 object for handle db */ + char *zDb; /* Database holding FTS index (e.g. "main") */ + char *zName; /* Name of FTS index */ + int nCol; /* Number of columns */ +@@ -208679,16 +235780,21 @@ struct Fts5Config { + int nPrefix; /* Number of prefix indexes */ + int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ + int eContent; /* An FTS5_CONTENT value */ ++ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ ++ int bContentlessUnindexed; /* "contentless_unindexed=" option (dflt=0) */ + char *zContent; /* content table */ + char *zContentRowid; /* "content_rowid=" option value */ + int bColumnsize; /* "columnsize=" option value (dflt==1) */ ++ int bTokendata; /* "tokendata=" option value (dflt==0) */ ++ int bLocale; /* "locale=" option value (dflt==0) */ + int eDetail; /* FTS5_DETAIL_XXX value */ + char *zContentExprlist; +- Fts5Tokenizer *pTok; +- fts5_tokenizer *pTokApi; ++ Fts5TokenizerConfig t; + int bLock; /* True when table is preparing statement */ + ++ + /* Values loaded from the %_config table */ ++ int iVersion; /* fts5 file format 'version' */ + int iCookie; /* Incremented when %_config is modified */ + int pgsz; /* Approximate page size used in %_data */ + int nAutomerge; /* 'automerge' setting */ +@@ -208697,6 +235803,9 @@ struct Fts5Config { + int nHashSize; /* Bytes of memory for in-memory hash */ + char *zRank; /* Name of rank function */ + char *zRankArgs; /* Arguments to rank function */ ++ int bSecureDelete; /* 'secure-delete' */ ++ int nDeleteMerge; /* 'deletemerge' */ ++ int bPrefixInsttoken; /* 'prefix-insttoken' */ + + /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ + char **pzErrmsg; +@@ -208706,18 +235815,24 @@ struct Fts5Config { + #endif + }; + +-/* Current expected value of %_config table 'version' field */ +-#define FTS5_CURRENT_VERSION 4 ++/* Current expected value of %_config table 'version' field. And ++** the expected version if the 'secure-delete' option has ever been ++** set on the table. */ ++#define FTS5_CURRENT_VERSION 4 ++#define FTS5_CURRENT_VERSION_SECUREDELETE 5 + +-#define FTS5_CONTENT_NORMAL 0 +-#define FTS5_CONTENT_NONE 1 +-#define FTS5_CONTENT_EXTERNAL 2 +- +-#define FTS5_DETAIL_FULL 0 +-#define FTS5_DETAIL_NONE 1 +-#define FTS5_DETAIL_COLUMNS 2 ++#define FTS5_CONTENT_NORMAL 0 ++#define FTS5_CONTENT_NONE 1 ++#define FTS5_CONTENT_EXTERNAL 2 ++#define FTS5_CONTENT_UNINDEXED 3 + ++#define FTS5_DETAIL_FULL 0 ++#define FTS5_DETAIL_NONE 1 ++#define FTS5_DETAIL_COLUMNS 2 + ++#define FTS5_PATTERN_NONE 0 ++#define FTS5_PATTERN_LIKE 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */ ++#define FTS5_PATTERN_GLOB 66 /* matches SQLITE_INDEX_CONSTRAINT_GLOB */ + + static int sqlite3Fts5ConfigParse( + Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** +@@ -208744,6 +235859,8 @@ static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, i + + static int sqlite3Fts5ConfigParseRank(const char*, char**, char**); + ++static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...); ++ + /* + ** End of interface to code in fts5_config.c. + **************************************************************************/ +@@ -208774,7 +235891,7 @@ static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...); + static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...); + + #define fts5BufferZero(x) sqlite3Fts5BufferZero(x) +-#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,c) ++#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,(i64)c) + #define fts5BufferFree(a) sqlite3Fts5BufferFree(a) + #define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d) + #define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d) +@@ -208788,7 +235905,7 @@ static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...); + static void sqlite3Fts5Put32(u8*, int); + static int sqlite3Fts5Get32(const u8*); + +-#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32) ++#define FTS5_POS2COLUMN(iPos) (int)((iPos >> 32) & 0x7FFFFFFF) + #define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF) + + typedef struct Fts5PoslistReader Fts5PoslistReader; +@@ -208861,16 +235978,19 @@ struct Fts5IndexIter { + /* + ** Values used as part of the flags argument passed to IndexQuery(). + */ +-#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ +-#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ +-#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ +-#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ ++#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ ++#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ ++#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ ++#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ + + /* The following are used internally by the fts5_index.c module. They are + ** defined here only to make it easier to avoid clashes with the flags + ** above. */ +-#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 +-#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 ++#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 ++#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 ++#define FTS5INDEX_QUERY_SKIPHASH 0x0040 ++#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080 ++#define FTS5INDEX_QUERY_SCANONETERM 0x0100 + + /* + ** Create/destroy an Fts5Index object. +@@ -208935,7 +236055,21 @@ static void sqlite3Fts5IndexCloseReader(Fts5Index*); + */ + static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); + static int sqlite3Fts5IterNextScan(Fts5IndexIter*); ++static void *sqlite3Fts5StructureRef(Fts5Index*); ++static void sqlite3Fts5StructureRelease(void*); ++static int sqlite3Fts5StructureTest(Fts5Index*, void*); + ++/* ++** Used by xInstToken(): ++*/ ++static int sqlite3Fts5IterToken( ++ Fts5IndexIter *pIndexIter, ++ const char *pToken, int nToken, ++ i64 iRowid, ++ int iCol, ++ int iOff, ++ const char **ppOut, int *pnOut ++); + + /* + ** Insert or remove data to or from the index. Each time a document is +@@ -208987,7 +236121,7 @@ static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int); + /* + ** Functions called by the storage module as part of integrity-check. + */ +-static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum); ++static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum, int bUseCksum); + + /* + ** Called during virtual module initialization to register UDF +@@ -209010,6 +236144,16 @@ static int sqlite3Fts5IndexReset(Fts5Index *p); + + static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); + ++static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); ++static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); ++ ++static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); ++ ++/* Used to populate hash tables for xInstToken in detail=none/column mode. */ ++static int sqlite3Fts5IndexIterWriteTokendata( ++ Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff ++); ++ + /* + ** End of interface to code in fts5_index.c. + **************************************************************************/ +@@ -209022,7 +236166,7 @@ static int sqlite3Fts5GetVarintLen(u32 iVal); + static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*); + static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); + +-#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b) ++#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&(b)) + #define fts5GetVarint sqlite3Fts5GetVarint + + #define fts5FastGetVarint32(a, iOff, nVal) { \ +@@ -209053,19 +236197,20 @@ struct Fts5Table { + Fts5Index *pIndex; /* Full-text index */ + }; + +-static int sqlite3Fts5GetTokenizer( +- Fts5Global*, +- const char **azArg, +- int nArg, +- Fts5Tokenizer**, +- fts5_tokenizer**, +- char **pzErr +-); ++static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig); + + static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); + + static int sqlite3Fts5FlushToDisk(Fts5Table*); + ++static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); ++static void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc); ++ ++static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal); ++static int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal, ++ const char **ppText, int *pnText, const char **ppLoc, int *pnLoc ++); ++ + /* + ** End of interface to code in fts5.c. + **************************************************************************/ +@@ -209095,6 +236240,11 @@ static int sqlite3Fts5HashWrite( + */ + static void sqlite3Fts5HashClear(Fts5Hash*); + ++/* ++** Return true if the hash is empty, false otherwise. ++*/ ++static int sqlite3Fts5HashIsEmpty(Fts5Hash*); ++ + static int sqlite3Fts5HashQuery( + Fts5Hash*, /* Hash table to query */ + int nPre, +@@ -209111,11 +236261,13 @@ static void sqlite3Fts5HashScanNext(Fts5Hash*); + static int sqlite3Fts5HashScanEof(Fts5Hash*); + static void sqlite3Fts5HashScanEntry(Fts5Hash *, + const char **pzTerm, /* OUT: term (nul-terminated) */ ++ int *pnTerm, /* OUT: Size of term in bytes */ + const u8 **ppDoclist, /* OUT: pointer to doclist */ + int *pnDoclist /* OUT: size of doclist in bytes */ + ); + + ++ + /* + ** End of interface to code in fts5_hash.c. + **************************************************************************/ +@@ -209138,11 +236290,11 @@ static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); + static int sqlite3Fts5DropAll(Fts5Config*); + static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); + +-static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**); +-static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*); ++static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); ++static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*); + static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); + +-static int sqlite3Fts5StorageIntegrity(Fts5Storage *p); ++static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); + + static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); + static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); +@@ -209164,6 +236316,9 @@ static int sqlite3Fts5StorageOptimize(Fts5Storage *p); + static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge); + static int sqlite3Fts5StorageReset(Fts5Storage *p); + ++static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage*); ++static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel); ++ + /* + ** End of interface to code in fts5_storage.c. + **************************************************************************/ +@@ -209187,11 +236342,19 @@ struct Fts5Token { + /* Parse a MATCH expression. */ + static int sqlite3Fts5ExprNew( + Fts5Config *pConfig, ++ int bPhraseToAnd, + int iCol, /* Column on LHS of MATCH operator */ + const char *zExpr, + Fts5Expr **ppNew, + char **pzErr + ); ++static int sqlite3Fts5ExprPattern( ++ Fts5Config *pConfig, ++ int bGlob, ++ int iCol, ++ const char *zText, ++ Fts5Expr **pp ++); + + /* + ** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc); +@@ -209228,6 +236391,10 @@ static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); + + static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); + ++static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); ++static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*); ++static void sqlite3Fts5ExprClearTokens(Fts5Expr*); ++ + /******************************************* + ** The fts5_expr.c API above this point is used by the other hand-written + ** C code in this module. The interfaces below this point are called by +@@ -209300,6 +236467,11 @@ static int sqlite3Fts5AuxInit(fts5_api*); + */ + + static int sqlite3Fts5TokenizerInit(fts5_api*); ++static int sqlite3Fts5TokenizerPattern( ++ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), ++ Fts5Tokenizer *pTok ++); ++static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig*); + /* + ** End of interface to code in fts5_tokenizer.c. + **************************************************************************/ +@@ -209346,6 +236518,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); + #define FTS5_PLUS 14 + #define FTS5_STAR 15 + ++/* This file is automatically generated by Lemon from input grammar ++** source file "fts5parse.y". ++*/ + /* + ** 2000-05-29 + ** +@@ -209361,7 +236536,7 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); + ** + ** The "lemon" program processes an LALR(1) input grammar file, then uses + ** this template to construct a parser. The "lemon" program inserts text +-** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the ++** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the + ** interstitial "-" characters) contained in this template is changed into + ** the value of the %name directive from the grammar. Otherwise, the content + ** of this template is copied straight through into the generate parser +@@ -209370,8 +236545,6 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); + ** The following is the concatenation of all %include directives from the + ** input grammar file: + */ +-/* #include */ +-/* #include */ + /************ Begin %include sections from the grammar ************************/ + + /* #include "fts5Int.h" */ +@@ -209401,11 +236574,26 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); + #define fts5YYMALLOCARGTYPE u64 + + /**************** End of %include directives **********************************/ +-/* These constants specify the various numeric values for terminal symbols +-** in a format understandable to "makeheaders". This section is blank unless +-** "lemon" is run with the "-m" command-line option. +-***************** Begin makeheaders token definitions *************************/ +-/**************** End makeheaders token definitions ***************************/ ++/* These constants specify the various numeric values for terminal symbols. ++***************** Begin token definitions *************************************/ ++#ifndef FTS5_OR ++#define FTS5_OR 1 ++#define FTS5_AND 2 ++#define FTS5_NOT 3 ++#define FTS5_TERM 4 ++#define FTS5_COLON 5 ++#define FTS5_MINUS 6 ++#define FTS5_LCP 7 ++#define FTS5_RCP 8 ++#define FTS5_STRING 9 ++#define FTS5_LP 10 ++#define FTS5_RP 11 ++#define FTS5_CARET 12 ++#define FTS5_COMMA 13 ++#define FTS5_PLUS 14 ++#define FTS5_STAR 15 ++#endif ++/**************** End token definitions ***************************************/ + + /* The next sections is a series of control #defines. + ** various aspects of the generated parser. +@@ -209444,6 +236632,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); + ** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser + ** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser + ** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context ++** fts5YYREALLOC Name of the realloc() function to use ++** fts5YYFREE Name of the free() function to use ++** fts5YYDYNSTACK True if stack space should be extended on heap + ** fts5YYERRORSYMBOL is the code number of the error symbol. If not + ** defined, then do no error processing. + ** fts5YYNSTATE the combined number of states. +@@ -209457,6 +236648,8 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); + ** fts5YY_NO_ACTION The fts5yy_action[] code for no-op + ** fts5YY_MIN_REDUCE Minimum value for reduce actions + ** fts5YY_MAX_REDUCE Maximum value for reduce actions ++** fts5YY_MIN_DSTRCTR Minimum symbol value that has a destructor ++** fts5YY_MAX_DSTRCTR Maximum symbol value that has a destructor + */ + #ifndef INTERFACE + # define INTERFACE 1 +@@ -209483,6 +236676,9 @@ typedef union { + #define sqlite3Fts5ParserARG_PARAM ,pParse + #define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse; + #define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse; ++#define fts5YYREALLOC realloc ++#define fts5YYFREE free ++#define fts5YYDYNSTACK 0 + #define sqlite3Fts5ParserCTX_SDECL + #define sqlite3Fts5ParserCTX_PDECL + #define sqlite3Fts5ParserCTX_PARAM +@@ -209500,6 +236696,8 @@ typedef union { + #define fts5YY_NO_ACTION 82 + #define fts5YY_MIN_REDUCE 83 + #define fts5YY_MAX_REDUCE 110 ++#define fts5YY_MIN_DSTRCTR 16 ++#define fts5YY_MAX_DSTRCTR 24 + /************* End control #defines *******************************************/ + #define fts5YY_NLOOKAHEAD ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]))) + +@@ -209515,6 +236713,22 @@ typedef union { + # define fts5yytestcase(X) + #endif + ++/* Macro to determine if stack space has the ability to grow using ++** heap memory. ++*/ ++#if fts5YYSTACKDEPTH<=0 || fts5YYDYNSTACK ++# define fts5YYGROWABLESTACK 1 ++#else ++# define fts5YYGROWABLESTACK 0 ++#endif ++ ++/* Guarantee a minimum number of initial stack slots. ++*/ ++#if fts5YYSTACKDEPTH<=0 ++# undef fts5YYSTACKDEPTH ++# define fts5YYSTACKDEPTH 2 /* Need a minimum stack size */ ++#endif ++ + + /* Next are the tables used to determine what action to take based on the + ** current state and lookahead token. These tables are used to implement +@@ -209675,17 +236889,13 @@ struct fts5yyParser { + #endif + sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */ + sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */ +-#if fts5YYSTACKDEPTH<=0 +- int fts5yystksz; /* Current side of the stack */ +- fts5yyStackEntry *fts5yystack; /* The parser's stack */ +- fts5yyStackEntry fts5yystk0; /* First stack entry */ +-#else +- fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */ +- fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ +-#endif ++ fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ ++ fts5yyStackEntry *fts5yystack; /* The parser stack */ ++ fts5yyStackEntry fts5yystk0[fts5YYSTACKDEPTH]; /* Initial stack space */ + }; + typedef struct fts5yyParser fts5yyParser; + ++/* #include */ + #ifndef NDEBUG + /* #include */ + static FILE *fts5yyTraceFILE = 0; +@@ -209788,37 +236998,45 @@ static const char *const fts5yyRuleName[] = { + #endif /* NDEBUG */ + + +-#if fts5YYSTACKDEPTH<=0 ++#if fts5YYGROWABLESTACK + /* + ** Try to increase the size of the parser stack. Return the number + ** of errors. Return 0 on success. + */ + static int fts5yyGrowStack(fts5yyParser *p){ ++ int oldSize = 1 + (int)(p->fts5yystackEnd - p->fts5yystack); + int newSize; + int idx; + fts5yyStackEntry *pNew; + +- newSize = p->fts5yystksz*2 + 100; +- idx = p->fts5yytos ? (int)(p->fts5yytos - p->fts5yystack) : 0; +- if( p->fts5yystack==&p->fts5yystk0 ){ +- pNew = malloc(newSize*sizeof(pNew[0])); +- if( pNew ) pNew[0] = p->fts5yystk0; ++ newSize = oldSize*2 + 100; ++ idx = (int)(p->fts5yytos - p->fts5yystack); ++ if( p->fts5yystack==p->fts5yystk0 ){ ++ pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0])); ++ if( pNew==0 ) return 1; ++ memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0])); + }else{ +- pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0])); ++ pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0])); ++ if( pNew==0 ) return 1; + } +- if( pNew ){ +- p->fts5yystack = pNew; +- p->fts5yytos = &p->fts5yystack[idx]; ++ p->fts5yystack = pNew; ++ p->fts5yytos = &p->fts5yystack[idx]; + #ifndef NDEBUG +- if( fts5yyTraceFILE ){ +- fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", +- fts5yyTracePrompt, p->fts5yystksz, newSize); +- } +-#endif +- p->fts5yystksz = newSize; ++ if( fts5yyTraceFILE ){ ++ fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", ++ fts5yyTracePrompt, oldSize, newSize); + } +- return pNew==0; ++#endif ++ p->fts5yystackEnd = &p->fts5yystack[newSize-1]; ++ return 0; + } ++#endif /* fts5YYGROWABLESTACK */ ++ ++#if !fts5YYGROWABLESTACK ++/* For builds that do no have a growable stack, fts5yyGrowStack always ++** returns an error. ++*/ ++# define fts5yyGrowStack(X) 1 + #endif + + /* Datatype of the argument to the memory allocated passed as the +@@ -209838,24 +237056,14 @@ static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PD + #ifdef fts5YYTRACKMAXSTACKDEPTH + fts5yypParser->fts5yyhwm = 0; + #endif +-#if fts5YYSTACKDEPTH<=0 +- fts5yypParser->fts5yytos = NULL; +- fts5yypParser->fts5yystack = NULL; +- fts5yypParser->fts5yystksz = 0; +- if( fts5yyGrowStack(fts5yypParser) ){ +- fts5yypParser->fts5yystack = &fts5yypParser->fts5yystk0; +- fts5yypParser->fts5yystksz = 1; +- } +-#endif ++ fts5yypParser->fts5yystack = fts5yypParser->fts5yystk0; ++ fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; + #ifndef fts5YYNOERRORRECOVERY + fts5yypParser->fts5yyerrcnt = -1; + #endif + fts5yypParser->fts5yytos = fts5yypParser->fts5yystack; + fts5yypParser->fts5yystack[0].stateno = 0; + fts5yypParser->fts5yystack[0].major = 0; +-#if fts5YYSTACKDEPTH>0 +- fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; +-#endif + } + + #ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK +@@ -209969,9 +237177,26 @@ static void fts5yy_pop_parser_stack(fts5yyParser *pParser){ + */ + static void sqlite3Fts5ParserFinalize(void *p){ + fts5yyParser *pParser = (fts5yyParser*)p; +- while( pParser->fts5yytos>pParser->fts5yystack ) fts5yy_pop_parser_stack(pParser); +-#if fts5YYSTACKDEPTH<=0 +- if( pParser->fts5yystack!=&pParser->fts5yystk0 ) free(pParser->fts5yystack); ++ ++ /* In-lined version of calling fts5yy_pop_parser_stack() for each ++ ** element left in the stack */ ++ fts5yyStackEntry *fts5yytos = pParser->fts5yytos; ++ while( fts5yytos>pParser->fts5yystack ){ ++#ifndef NDEBUG ++ if( fts5yyTraceFILE ){ ++ fprintf(fts5yyTraceFILE,"%sPopping %s\n", ++ fts5yyTracePrompt, ++ fts5yyTokenName[fts5yytos->major]); ++ } ++#endif ++ if( fts5yytos->major>=fts5YY_MIN_DSTRCTR ){ ++ fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor); ++ } ++ fts5yytos--; ++ } ++ ++#if fts5YYGROWABLESTACK ++ if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack); + #endif + } + +@@ -210102,7 +237327,7 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action( + #endif /* fts5YYWILDCARD */ + return fts5yy_default[stateno]; + }else{ +- assert( i>=0 && i=0 && i<(int)(sizeof(fts5yy_action)/sizeof(fts5yy_action[0])) ); + return fts5yy_action[i]; + } + }while(1); +@@ -210198,25 +237423,19 @@ static void fts5yy_shift( + assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) ); + } + #endif +-#if fts5YYSTACKDEPTH>0 +- if( fts5yypParser->fts5yytos>fts5yypParser->fts5yystackEnd ){ +- fts5yypParser->fts5yytos--; +- fts5yyStackOverflow(fts5yypParser); +- return; +- } +-#else +- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz] ){ ++ fts5yytos = fts5yypParser->fts5yytos; ++ if( fts5yytos>fts5yypParser->fts5yystackEnd ){ + if( fts5yyGrowStack(fts5yypParser) ){ + fts5yypParser->fts5yytos--; + fts5yyStackOverflow(fts5yypParser); + return; + } ++ fts5yytos = fts5yypParser->fts5yytos; ++ assert( fts5yytos <= fts5yypParser->fts5yystackEnd ); + } +-#endif + if( fts5yyNewState > fts5YY_MAX_SHIFT ){ + fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE; + } +- fts5yytos = fts5yypParser->fts5yytos; + fts5yytos->stateno = fts5yyNewState; + fts5yytos->major = fts5yyMajor; + fts5yytos->minor.fts5yy0 = fts5yyMinor; +@@ -210316,54 +237535,6 @@ static fts5YYACTIONTYPE fts5yy_reduce( + (void)fts5yyLookahead; + (void)fts5yyLookaheadToken; + fts5yymsp = fts5yypParser->fts5yytos; +-#ifndef NDEBUG +- if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){ +- fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; +- if( fts5yysize ){ +- fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", +- fts5yyTracePrompt, +- fts5yyruleno, fts5yyRuleName[fts5yyruleno], +- fts5yyrulenofts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ +- fts5yypParser->fts5yyhwm++; +- assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); +- } +-#endif +-#if fts5YYSTACKDEPTH>0 +- if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ +- fts5yyStackOverflow(fts5yypParser); +- /* The call to fts5yyStackOverflow() above pops the stack until it is +- ** empty, causing the main parser loop to exit. So the return value +- ** is never used and does not matter. */ +- return 0; +- } +-#else +- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){ +- if( fts5yyGrowStack(fts5yypParser) ){ +- fts5yyStackOverflow(fts5yypParser); +- /* The call to fts5yyStackOverflow() above pops the stack until it is +- ** empty, causing the main parser loop to exit. So the return value +- ** is never used and does not matter. */ +- return 0; +- } +- fts5yymsp = fts5yypParser->fts5yytos; +- } +-#endif +- } + + switch( fts5yyruleno ){ + /* Beginning here are the reduction cases. A typical example +@@ -210666,12 +237837,49 @@ static void sqlite3Fts5Parser( + } + #endif + +- do{ ++ while(1){ /* Exit by "break" */ ++ assert( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystack ); + assert( fts5yyact==fts5yypParser->fts5yytos->stateno ); + fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact); + if( fts5yyact >= fts5YY_MIN_REDUCE ){ +- fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE,fts5yymajor, +- fts5yyminor sqlite3Fts5ParserCTX_PARAM); ++ unsigned int fts5yyruleno = fts5yyact - fts5YY_MIN_REDUCE; /* Reduce by this rule */ ++#ifndef NDEBUG ++ assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ); ++ if( fts5yyTraceFILE ){ ++ int fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; ++ if( fts5yysize ){ ++ fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", ++ fts5yyTracePrompt, ++ fts5yyruleno, fts5yyRuleName[fts5yyruleno], ++ fts5yyrulenofts5yytos[fts5yysize].stateno); ++ }else{ ++ fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s.\n", ++ fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno], ++ fts5yyrulenofts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ ++ fts5yypParser->fts5yyhwm++; ++ assert( fts5yypParser->fts5yyhwm == ++ (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); ++ } ++#endif ++ if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ ++ if( fts5yyGrowStack(fts5yypParser) ){ ++ fts5yyStackOverflow(fts5yypParser); ++ break; ++ } ++ } ++ } ++ fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM); + }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){ + fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPE)fts5yymajor,fts5yyminor); + #ifndef fts5YYNOERRORRECOVERY +@@ -210727,14 +237935,13 @@ static void sqlite3Fts5Parser( + fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion); + fts5yymajor = fts5YYNOCODE; + }else{ +- while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack +- && (fts5yyact = fts5yy_find_reduce_action( +- fts5yypParser->fts5yytos->stateno, +- fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE +- ){ ++ while( fts5yypParser->fts5yytos > fts5yypParser->fts5yystack ){ ++ fts5yyact = fts5yy_find_reduce_action(fts5yypParser->fts5yytos->stateno, ++ fts5YYERRORSYMBOL); ++ if( fts5yyact<=fts5YY_MAX_SHIFTREDUCE ) break; + fts5yy_pop_parser_stack(fts5yypParser); + } +- if( fts5yypParser->fts5yytos < fts5yypParser->fts5yystack || fts5yymajor==0 ){ ++ if( fts5yypParser->fts5yytos <= fts5yypParser->fts5yystack || fts5yymajor==0 ){ + fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion); + fts5yy_parse_failed(fts5yypParser); + #ifndef fts5YYNOERRORRECOVERY +@@ -210784,7 +237991,7 @@ static void sqlite3Fts5Parser( + break; + #endif + } +- }while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ); ++ } + #ifndef NDEBUG + if( fts5yyTraceFILE ){ + fts5yyStackEntry *i; +@@ -210926,15 +238133,19 @@ static int fts5CInstIterInit( + */ + typedef struct HighlightContext HighlightContext; + struct HighlightContext { +- CInstIter iter; /* Coalesced Instance Iterator */ +- int iPos; /* Current token offset in zIn[] */ ++ /* Constant parameters to fts5HighlightCb() */ + int iRangeStart; /* First token to include */ + int iRangeEnd; /* If non-zero, last token to include */ + const char *zOpen; /* Opening highlight */ + const char *zClose; /* Closing highlight */ + const char *zIn; /* Input text */ + int nIn; /* Size of input text in bytes */ +- int iOff; /* Current offset within zIn[] */ ++ ++ /* Variables modified by fts5HighlightCb() */ ++ CInstIter iter; /* Coalesced Instance Iterator */ ++ int iPos; /* Current token offset in zIn[] */ ++ int iOff; /* Have copied up to this offset in zIn[] */ ++ int bOpen; /* True if highlight is open */ + char *zOut; /* Output value */ + }; + +@@ -210967,8 +238178,8 @@ static int fts5HighlightCb( + int tflags, /* Mask of FTS5_TOKEN_* flags */ + const char *pToken, /* Buffer containing token */ + int nToken, /* Size of token in bytes */ +- int iStartOff, /* Start offset of token */ +- int iEndOff /* End offset of token */ ++ int iStartOff, /* Start byte offset of token */ ++ int iEndOff /* End byte offset of token */ + ){ + HighlightContext *p = (HighlightContext*)pContext; + int rc = SQLITE_OK; +@@ -210979,40 +238190,66 @@ static int fts5HighlightCb( + if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK; + iPos = p->iPos++; + +- if( p->iRangeEnd>0 ){ ++ if( p->iRangeEnd>=0 ){ + if( iPosiRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK; + if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff; + } + +- if( iPos==p->iter.iStart ){ ++ /* If the parenthesis is open, and this token is not part of the current ++ ** phrase, and the starting byte offset of this token is past the point ++ ** that has currently been copied into the output buffer, close the ++ ** parenthesis. */ ++ if( p->bOpen ++ && (iPos<=p->iter.iStart || p->iter.iStart<0) ++ && iStartOff>p->iOff ++ ){ ++ fts5HighlightAppend(&rc, p, p->zClose, -1); ++ p->bOpen = 0; ++ } ++ ++ /* If this is the start of a new phrase, and the highlight is not open: ++ ** ++ ** * copy text from the input up to the start of the phrase, and ++ ** * open the highlight. ++ */ ++ if( iPos==p->iter.iStart && p->bOpen==0 ){ + fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff); + fts5HighlightAppend(&rc, p, p->zOpen, -1); + p->iOff = iStartOff; ++ p->bOpen = 1; + } + + if( iPos==p->iter.iEnd ){ +- if( p->iRangeEnd && p->iter.iStartiRangeStart ){ ++ if( p->bOpen==0 ){ ++ assert( p->iRangeEnd>=0 ); + fts5HighlightAppend(&rc, p, p->zOpen, -1); ++ p->bOpen = 1; + } + fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); +- fts5HighlightAppend(&rc, p, p->zClose, -1); + p->iOff = iEndOff; ++ + if( rc==SQLITE_OK ){ + rc = fts5CInstIterNext(&p->iter); + } + } + +- if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){ +- fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); +- p->iOff = iEndOff; +- if( iPos>=p->iter.iStart && iPositer.iEnd ){ ++ if( iPos==p->iRangeEnd ){ ++ if( p->bOpen ){ ++ if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){ ++ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); ++ p->iOff = iEndOff; ++ } + fts5HighlightAppend(&rc, p, p->zClose, -1); ++ p->bOpen = 0; + } ++ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); ++ p->iOff = iEndOff; + } + + return rc; + } + ++ + /* + ** Implementation of highlight() function. + */ +@@ -211037,15 +238274,28 @@ static void fts5HighlightFunction( + memset(&ctx, 0, sizeof(HighlightContext)); + ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); + ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ++ ctx.iRangeEnd = -1; + rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); +- +- if( ctx.zIn ){ ++ if( rc==SQLITE_RANGE ){ ++ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); ++ rc = SQLITE_OK; ++ }else if( ctx.zIn ){ ++ const char *pLoc = 0; /* Locale of column iCol */ ++ int nLoc = 0; /* Size of pLoc in bytes */ + if( rc==SQLITE_OK ){ + rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); + } + + if( rc==SQLITE_OK ){ +- rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); ++ rc = pApi->xColumnLocale(pFts, iCol, &pLoc, &nLoc); ++ } ++ if( rc==SQLITE_OK ){ ++ rc = pApi->xTokenize_v2( ++ pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx, fts5HighlightCb ++ ); ++ } ++ if( ctx.bOpen ){ ++ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); + } + fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); + +@@ -211222,6 +238472,7 @@ static void fts5SnippetFunction( + iCol = sqlite3_value_int(apVal[0]); + ctx.zOpen = fts5ValueToText(apVal[1]); + ctx.zClose = fts5ValueToText(apVal[2]); ++ ctx.iRangeEnd = -1; + zEllips = fts5ValueToText(apVal[3]); + nToken = sqlite3_value_int(apVal[4]); + +@@ -211238,6 +238489,8 @@ static void fts5SnippetFunction( + memset(&sFinder, 0, sizeof(Fts5SFinder)); + for(i=0; ixColumnText(pFts, i, &sFinder.zDoc, &nDoc); + if( rc!=SQLITE_OK ) break; +- rc = pApi->xTokenize(pFts, +- sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb ++ rc = pApi->xColumnLocale(pFts, i, &pLoc, &nLoc); ++ if( rc!=SQLITE_OK ) break; ++ rc = pApi->xTokenize_v2(pFts, ++ sFinder.zDoc, nDoc, pLoc, nLoc, (void*)&sFinder, fts5SentenceFinderCb + ); + if( rc!=SQLITE_OK ) break; + rc = pApi->xColumnSize(pFts, i, &nDocsize); +@@ -211304,6 +238559,9 @@ static void fts5SnippetFunction( + rc = pApi->xColumnSize(pFts, iBestCol, &nColSize); + } + if( ctx.zIn ){ ++ const char *pLoc = 0; /* Locale of column iBestCol */ ++ int nLoc = 0; /* Bytes in pLoc */ ++ + if( rc==SQLITE_OK ){ + rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); + } +@@ -211322,7 +238580,15 @@ static void fts5SnippetFunction( + } + + if( rc==SQLITE_OK ){ +- rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); ++ rc = pApi->xColumnLocale(pFts, iBestCol, &pLoc, &nLoc); ++ } ++ if( rc==SQLITE_OK ){ ++ rc = pApi->xTokenize_v2( ++ pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx,fts5HighlightCb ++ ); ++ } ++ if( ctx.bOpen ){ ++ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); + } + if( ctx.iRangeEnd>=(nColSize-1) ){ + fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); +@@ -211382,7 +238648,7 @@ static int fts5Bm25GetData( + int rc = SQLITE_OK; /* Return code */ + Fts5Bm25Data *p; /* Object to return */ + +- p = pApi->xGetAuxdata(pFts, 0); ++ p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0); + if( p==0 ){ + int nPhrase; /* Number of phrases in query */ + sqlite3_int64 nRow = 0; /* Number of rows in table */ +@@ -211424,7 +238690,7 @@ static int fts5Bm25GetData( + ** under consideration. + ** + ** The problem with this is that if (N < 2*nHit), the IDF is +- ** negative. Which is undesirable. So the mimimum allowable IDF is ++ ** negative. Which is undesirable. So the minimum allowable IDF is + ** (1e-6) - roughly the same as a term that appears in just over + ** half of set of 5,000,000 documents. */ + double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); +@@ -211456,7 +238722,7 @@ static void fts5Bm25Function( + ){ + const double k1 = 1.2; /* Constant "k1" from BM25 formula */ + const double b = 0.75; /* Constant "b" from BM25 formula */ +- int rc = SQLITE_OK; /* Error code */ ++ int rc; /* Error code */ + double score = 0.0; /* SQL function return value */ + Fts5Bm25Data *pData; /* Values allocated/calculated once only */ + int i; /* Iterator variable */ +@@ -211488,23 +238754,68 @@ static void fts5Bm25Function( + D = (double)nTok; + } + +- /* Determine the BM25 score for the current row. */ +- for(i=0; rc==SQLITE_OK && inPhrase; i++){ +- score += pData->aIDF[i] * ( +- ( aFreq[i] * (k1 + 1.0) ) / +- ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) +- ); +- } +- +- /* If no error has occurred, return the calculated score. Otherwise, +- ** throw an SQL exception. */ ++ /* Determine and return the BM25 score for the current row. Or, if an ++ ** error has occurred, throw an exception. */ + if( rc==SQLITE_OK ){ ++ for(i=0; inPhrase; i++){ ++ score += pData->aIDF[i] * ( ++ ( aFreq[i] * (k1 + 1.0) ) / ++ ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) ++ ); ++ } + sqlite3_result_double(pCtx, -1.0 * score); + }else{ + sqlite3_result_error_code(pCtx, rc); + } + } + ++/* ++** Implementation of fts5_get_locale() function. ++*/ ++static void fts5GetLocaleFunction( ++ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ ++ Fts5Context *pFts, /* First arg to pass to pApi functions */ ++ sqlite3_context *pCtx, /* Context for returning result/error */ ++ int nVal, /* Number of values in apVal[] array */ ++ sqlite3_value **apVal /* Array of trailing arguments */ ++){ ++ int iCol = 0; ++ int eType = 0; ++ int rc = SQLITE_OK; ++ const char *zLocale = 0; ++ int nLocale = 0; ++ ++ /* xColumnLocale() must be available */ ++ assert( pApi->iVersion>=4 ); ++ ++ if( nVal!=1 ){ ++ const char *z = "wrong number of arguments to function fts5_get_locale()"; ++ sqlite3_result_error(pCtx, z, -1); ++ return; ++ } ++ ++ eType = sqlite3_value_numeric_type(apVal[0]); ++ if( eType!=SQLITE_INTEGER ){ ++ const char *z = "non-integer argument passed to function fts5_get_locale()"; ++ sqlite3_result_error(pCtx, z, -1); ++ return; ++ } ++ ++ iCol = sqlite3_value_int(apVal[0]); ++ if( iCol<0 || iCol>=pApi->xColumnCount(pFts) ){ ++ sqlite3_result_error_code(pCtx, SQLITE_RANGE); ++ return; ++ } ++ ++ rc = pApi->xColumnLocale(pFts, iCol, &zLocale, &nLocale); ++ if( rc!=SQLITE_OK ){ ++ sqlite3_result_error_code(pCtx, rc); ++ return; ++ } ++ ++ sqlite3_result_text(pCtx, zLocale, nLocale, SQLITE_TRANSIENT); ++} ++ + static int sqlite3Fts5AuxInit(fts5_api *pApi){ + struct Builtin { + const char *zFunc; /* Function name (nul-terminated) */ +@@ -211512,9 +238823,10 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){ + fts5_extension_function xFunc;/* Callback function */ + void (*xDestroy)(void*); /* Destructor function */ + } aBuiltin [] = { +- { "snippet", 0, fts5SnippetFunction, 0 }, +- { "highlight", 0, fts5HighlightFunction, 0 }, +- { "bm25", 0, fts5Bm25Function, 0 }, ++ { "snippet", 0, fts5SnippetFunction, 0 }, ++ { "highlight", 0, fts5HighlightFunction, 0 }, ++ { "bm25", 0, fts5Bm25Function, 0 }, ++ { "fts5_get_locale", 0, fts5GetLocaleFunction, 0 }, + }; + int rc = SQLITE_OK; /* Return code */ + int i; /* To iterate through builtin functions */ +@@ -211599,9 +238911,9 @@ static void sqlite3Fts5BufferAppendBlob( + u32 nData, + const u8 *pData + ){ +- assert_nc( *pRc || nData>=0 ); + if( nData ){ + if( fts5BufferGrow(pRc, pBuf, nData) ) return; ++ assert( pBuf->p!=0 ); + memcpy(&pBuf->p[pBuf->n], pData, nData); + pBuf->n += nData; + } +@@ -211703,13 +239015,15 @@ static int sqlite3Fts5PoslistNext64( + i64 *piOff /* IN/OUT: Current offset */ + ){ + int i = *pi; ++ assert( a!=0 || i==0 ); + if( i>=n ){ + /* EOF */ + *piOff = -1; + return 1; + }else{ + i64 iOff = *piOff; +- int iVal; ++ u32 iVal; ++ assert( a!=0 ); + fts5FastGetVarint32(a, i, iVal); + if( iVal<=1 ){ + if( iVal==0 ){ +@@ -211718,15 +239032,19 @@ static int sqlite3Fts5PoslistNext64( + } + fts5FastGetVarint32(a, i, iVal); + iOff = ((i64)iVal) << 32; ++ assert( iOff>=0 ); + fts5FastGetVarint32(a, i, iVal); + if( iVal<2 ){ + /* This is a corrupt record. So stop parsing it here. */ + *piOff = -1; + return 1; + } ++ *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); ++ }else{ ++ *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF); + } +- *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); + *pi = i; ++ assert_nc( *piOff>=iOff ); + return 0; + } + } +@@ -211765,14 +239083,16 @@ static void sqlite3Fts5PoslistSafeAppend( + i64 *piPrev, + i64 iPos + ){ +- static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; +- if( (iPos & colmask) != (*piPrev & colmask) ){ +- pBuf->p[pBuf->n++] = 1; +- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); +- *piPrev = (iPos & colmask); ++ if( iPos>=*piPrev ){ ++ static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; ++ if( (iPos & colmask) != (*piPrev & colmask) ){ ++ pBuf->p[pBuf->n++] = 1; ++ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); ++ *piPrev = (iPos & colmask); ++ } ++ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); ++ *piPrev = iPos; + } +- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); +- *piPrev = iPos; + } + + static int sqlite3Fts5PoslistWriterAppend( +@@ -211833,7 +239153,7 @@ static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){ + ** * The 52 upper and lower case ASCII characters, and + ** * The 10 integer ASCII characters. + ** * The underscore character "_" (0x5F). +-** * The unicode "subsitute" character (0x1A). ++** * The unicode "substitute" character (0x1A). + */ + static int sqlite3Fts5IsBareword(char t){ + u8 aBareword[128] = { +@@ -211959,6 +239279,8 @@ static void sqlite3Fts5TermsetFree(Fts5Termset *p){ + #define FTS5_DEFAULT_CRISISMERGE 16 + #define FTS5_DEFAULT_HASHSIZE (1024*1024) + ++#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */ ++ + /* Maximum allowed page size */ + #define FTS5_MAX_PAGE_SIZE (64*1024) + +@@ -212169,7 +239491,6 @@ static int fts5ConfigSetEnum( + ** eventually free any such error message using sqlite3_free(). + */ + static int fts5ConfigParseSpecial( +- Fts5Global *pGlobal, + Fts5Config *pConfig, /* Configuration object to update */ + const char *zCmd, /* Special command to parse */ + const char *zArg, /* Argument to parse */ +@@ -212177,6 +239498,7 @@ static int fts5ConfigParseSpecial( + ){ + int rc = SQLITE_OK; + int nCmd = (int)strlen(zCmd); ++ + if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){ + const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES; + const char *p; +@@ -212233,12 +239555,11 @@ static int fts5ConfigParseSpecial( + if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){ + const char *p = (const char*)zArg; + sqlite3_int64 nArg = strlen(zArg) + 1; +- char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg); +- char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2); +- char *pSpace = pDel; ++ char **azArg = sqlite3Fts5MallocZero(&rc, (sizeof(char*) + 2) * nArg); + +- if( azArg && pSpace ){ +- if( pConfig->pTok ){ ++ if( azArg ){ ++ char *pSpace = (char*)&azArg[nArg]; ++ if( pConfig->t.azArg ){ + *pzErr = sqlite3_mprintf("multiple tokenize=... directives"); + rc = SQLITE_ERROR; + }else{ +@@ -212261,16 +239582,14 @@ static int fts5ConfigParseSpecial( + *pzErr = sqlite3_mprintf("parse error in tokenize directive"); + rc = SQLITE_ERROR; + }else{ +- rc = sqlite3Fts5GetTokenizer(pGlobal, +- (const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi, +- pzErr +- ); ++ pConfig->t.azArg = (const char**)azArg; ++ pConfig->t.nArg = nArg; ++ azArg = 0; + } + } + } +- + sqlite3_free(azArg); +- sqlite3_free(pDel); ++ + return rc; + } + +@@ -212289,6 +239608,26 @@ static int fts5ConfigParseSpecial( + return rc; + } + ++ if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){ ++ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ ++ *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); ++ rc = SQLITE_ERROR; ++ }else{ ++ pConfig->bContentlessDelete = (zArg[0]=='1'); ++ } ++ return rc; ++ } ++ ++ if( sqlite3_strnicmp("contentless_unindexed", zCmd, nCmd)==0 ){ ++ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ ++ *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); ++ rc = SQLITE_ERROR; ++ }else{ ++ pConfig->bContentlessUnindexed = (zArg[0]=='1'); ++ } ++ return rc; ++ } ++ + if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ + if( pConfig->zContentRowid ){ + *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); +@@ -212309,6 +239648,16 @@ static int fts5ConfigParseSpecial( + return rc; + } + ++ if( sqlite3_strnicmp("locale", zCmd, nCmd)==0 ){ ++ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ ++ *pzErr = sqlite3_mprintf("malformed locale=... directive"); ++ rc = SQLITE_ERROR; ++ }else{ ++ pConfig->bLocale = (zArg[0]=='1'); ++ } ++ return rc; ++ } ++ + if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){ + const Fts5Enum aDetail[] = { + { "none", FTS5_DETAIL_NONE }, +@@ -212323,22 +239672,20 @@ static int fts5ConfigParseSpecial( + return rc; + } + ++ if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){ ++ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ ++ *pzErr = sqlite3_mprintf("malformed tokendata=... directive"); ++ rc = SQLITE_ERROR; ++ }else{ ++ pConfig->bTokendata = (zArg[0]=='1'); ++ } ++ return rc; ++ } ++ + *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); + return SQLITE_ERROR; + } + +-/* +-** Allocate an instance of the default tokenizer ("simple") at +-** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error +-** code if an error occurs. +-*/ +-static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){ +- assert( pConfig->pTok==0 && pConfig->pTokApi==0 ); +- return sqlite3Fts5GetTokenizer( +- pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0 +- ); +-} +- + /* + ** Gobble up the first bareword or quoted word from the input buffer zIn. + ** Return a pointer to the character immediately following the last in +@@ -212398,7 +239745,8 @@ static int fts5ConfigParseColumn( + Fts5Config *p, + char *zCol, + char *zArg, +- char **pzErr ++ char **pzErr, ++ int *pbUnindexed + ){ + int rc = SQLITE_OK; + if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME) +@@ -212409,6 +239757,7 @@ static int fts5ConfigParseColumn( + }else if( zArg ){ + if( 0==sqlite3_stricmp(zArg, "unindexed") ){ + p->abUnindexed[p->nCol] = 1; ++ *pbUnindexed = 1; + }else{ + *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg); + rc = SQLITE_ERROR; +@@ -212429,11 +239778,26 @@ static int fts5ConfigMakeExprlist(Fts5Config *p){ + + sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid); + if( p->eContent!=FTS5_CONTENT_NONE ){ ++ assert( p->eContent==FTS5_CONTENT_EXTERNAL ++ || p->eContent==FTS5_CONTENT_NORMAL ++ || p->eContent==FTS5_CONTENT_UNINDEXED ++ ); + for(i=0; inCol; i++){ + if( p->eContent==FTS5_CONTENT_EXTERNAL ){ + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]); +- }else{ ++ }else if( p->eContent==FTS5_CONTENT_NORMAL || p->abUnindexed[i] ){ + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i); ++ }else{ ++ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); ++ } ++ } ++ } ++ if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){ ++ for(i=0; inCol; i++){ ++ if( p->abUnindexed[i]==0 ){ ++ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i); ++ }else{ ++ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); + } + } + } +@@ -212467,16 +239831,18 @@ static int sqlite3Fts5ConfigParse( + Fts5Config *pRet; /* New object to return */ + int i; + sqlite3_int64 nByte; ++ int bUnindexed = 0; /* True if there are one or more UNINDEXED */ + + *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); + if( pRet==0 ) return SQLITE_NOMEM; + memset(pRet, 0, sizeof(Fts5Config)); ++ pRet->pGlobal = pGlobal; + pRet->db = db; + pRet->iCookie = -1; + + nByte = nArg * (sizeof(char*) + sizeof(u8)); + pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte); +- pRet->abUnindexed = (u8*)&pRet->azCol[nArg]; ++ pRet->abUnindexed = pRet->azCol ? (u8*)&pRet->azCol[nArg] : 0; + pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1); + pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1); + pRet->bColumnsize = 1; +@@ -212489,6 +239855,7 @@ static int sqlite3Fts5ConfigParse( + rc = SQLITE_ERROR; + } + ++ assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK ); + for(i=3; rc==SQLITE_OK && ipTok==0 ){ +- rc = fts5ConfigDefaultTokenizer(pGlobal, pRet); ++ /* We only allow contentless_delete=1 if the table is indeed contentless. */ ++ if( rc==SQLITE_OK ++ && pRet->bContentlessDelete ++ && pRet->eContent!=FTS5_CONTENT_NONE ++ ){ ++ *pzErr = sqlite3_mprintf( ++ "contentless_delete=1 requires a contentless table" ++ ); ++ rc = SQLITE_ERROR; ++ } ++ ++ /* We only allow contentless_delete=1 if columnsize=0 is not present. ++ ** ++ ** This restriction may be removed at some point. ++ */ ++ if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){ ++ *pzErr = sqlite3_mprintf( ++ "contentless_delete=1 is incompatible with columnsize=0" ++ ); ++ rc = SQLITE_ERROR; ++ } ++ ++ /* We only allow contentless_unindexed=1 if the table is actually a ++ ** contentless one. ++ */ ++ if( rc==SQLITE_OK ++ && pRet->bContentlessUnindexed ++ && pRet->eContent!=FTS5_CONTENT_NONE ++ ){ ++ *pzErr = sqlite3_mprintf( ++ "contentless_unindexed=1 requires a contentless table" ++ ); ++ rc = SQLITE_ERROR; + } + + /* If no zContent option was specified, fill in the default values. */ +@@ -212544,6 +239944,9 @@ static int sqlite3Fts5ConfigParse( + ); + if( pRet->eContent==FTS5_CONTENT_NORMAL ){ + zTail = "content"; ++ }else if( bUnindexed && pRet->bContentlessUnindexed ){ ++ pRet->eContent = FTS5_CONTENT_UNINDEXED; ++ zTail = "content"; + }else if( pRet->bColumnsize ){ + zTail = "docsize"; + } +@@ -212577,9 +239980,14 @@ static int sqlite3Fts5ConfigParse( + static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){ + if( pConfig ){ + int i; +- if( pConfig->pTok ){ +- pConfig->pTokApi->xDelete(pConfig->pTok); ++ if( pConfig->t.pTok ){ ++ if( pConfig->t.pApi1 ){ ++ pConfig->t.pApi1->xDelete(pConfig->t.pTok); ++ }else{ ++ pConfig->t.pApi2->xDelete(pConfig->t.pTok); ++ } + } ++ sqlite3_free((char*)pConfig->t.azArg); + sqlite3_free(pConfig->zDb); + sqlite3_free(pConfig->zName); + for(i=0; inCol; i++){ +@@ -212654,10 +240062,24 @@ static int sqlite3Fts5Tokenize( + void *pCtx, /* Context passed to xToken() */ + int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ + ){ +- if( pText==0 ) return SQLITE_OK; +- return pConfig->pTokApi->xTokenize( +- pConfig->pTok, pCtx, flags, pText, nText, xToken +- ); ++ int rc = SQLITE_OK; ++ if( pText ){ ++ if( pConfig->t.pTok==0 ){ ++ rc = sqlite3Fts5LoadTokenizer(pConfig); ++ } ++ if( rc==SQLITE_OK ){ ++ if( pConfig->t.pApi1 ){ ++ rc = pConfig->t.pApi1->xTokenize( ++ pConfig->t.pTok, pCtx, flags, pText, nText, xToken ++ ); ++ }else{ ++ rc = pConfig->t.pApi2->xTokenize(pConfig->t.pTok, pCtx, flags, ++ pText, nText, pConfig->t.pLocale, pConfig->t.nLocale, xToken ++ ); ++ } ++ } ++ } ++ return rc; + } + + /* +@@ -212823,6 +240245,18 @@ static int sqlite3Fts5ConfigSetValue( + } + } + ++ else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){ ++ int nVal = -1; ++ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ ++ nVal = sqlite3_value_int(pVal); ++ }else{ ++ *pbBadkey = 1; ++ } ++ if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE; ++ if( nVal>100 ) nVal = 0; ++ pConfig->nDeleteMerge = nVal; ++ } ++ + else if( 0==sqlite3_stricmp(zKey, "rank") ){ + const char *zIn = (const char*)sqlite3_value_text(pVal); + char *zRank; +@@ -212837,6 +240271,31 @@ static int sqlite3Fts5ConfigSetValue( + rc = SQLITE_OK; + *pbBadkey = 1; + } ++ } ++ ++ else if( 0==sqlite3_stricmp(zKey, "secure-delete") ){ ++ int bVal = -1; ++ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ ++ bVal = sqlite3_value_int(pVal); ++ } ++ if( bVal<0 ){ ++ *pbBadkey = 1; ++ }else{ ++ pConfig->bSecureDelete = (bVal ? 1 : 0); ++ } ++ } ++ ++ else if( 0==sqlite3_stricmp(zKey, "insttoken") ){ ++ int bVal = -1; ++ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ ++ bVal = sqlite3_value_int(pVal); ++ } ++ if( bVal<0 ){ ++ *pbBadkey = 1; ++ }else{ ++ pConfig->bPrefixInsttoken = (bVal ? 1 : 0); ++ } ++ + }else{ + *pbBadkey = 1; + } +@@ -212859,6 +240318,7 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ + pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE; + pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE; + pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE; ++ pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE; + + zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName); + if( zSql ){ +@@ -212881,15 +240341,17 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ + rc = sqlite3_finalize(p); + } + +- if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){ ++ if( rc==SQLITE_OK ++ && iVersion!=FTS5_CURRENT_VERSION ++ && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ++ ){ + rc = SQLITE_ERROR; +- if( pConfig->pzErrmsg ){ +- assert( 0==*pConfig->pzErrmsg ); +- *pConfig->pzErrmsg = sqlite3_mprintf( +- "invalid fts5 file format (found %d, expected %d) - run 'rebuild'", +- iVersion, FTS5_CURRENT_VERSION +- ); +- } ++ sqlite3Fts5ConfigErrmsg(pConfig, "invalid fts5 file format " ++ "(found %d, expected %d or %d) - run 'rebuild'", ++ iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE ++ ); ++ }else{ ++ pConfig->iVersion = iVersion; + } + + if( rc==SQLITE_OK ){ +@@ -212898,6 +240360,29 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ + return rc; + } + ++/* ++** Set (*pConfig->pzErrmsg) to point to an sqlite3_malloc()ed buffer ++** containing the error message created using printf() style formatting ++** string zFmt and its trailing arguments. ++*/ ++static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){ ++ va_list ap; /* ... printf arguments */ ++ char *zMsg = 0; ++ ++ va_start(ap, zFmt); ++ zMsg = sqlite3_vmprintf(zFmt, ap); ++ if( pConfig->pzErrmsg ){ ++ assert( *pConfig->pzErrmsg==0 ); ++ *pConfig->pzErrmsg = zMsg; ++ }else{ ++ sqlite3_free(zMsg); ++ } ++ ++ va_end(ap); ++} ++ ++ ++ + /* + ** 2014 May 31 + ** +@@ -212917,6 +240402,10 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ + /* #include "fts5Int.h" */ + /* #include "fts5parse.h" */ + ++#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH ++# define SQLITE_FTS5_MAX_EXPR_DEPTH 256 ++#endif ++ + /* + ** All token types in the generated fts5parse.h file are greater than 0. + */ +@@ -212950,18 +240439,28 @@ struct Fts5Expr { + + /* + ** eType: +-** Expression node type. Always one of: ++** Expression node type. Usually one of: + ** + ** FTS5_AND (nChild, apChild valid) + ** FTS5_OR (nChild, apChild valid) + ** FTS5_NOT (nChild, apChild valid) + ** FTS5_STRING (pNear valid) + ** FTS5_TERM (pNear valid) ++** ++** An expression node with eType==0 may also exist. It always matches zero ++** rows. This is created when a phrase containing no tokens is parsed. ++** e.g. "". ++** ++** iHeight: ++** Distance from this node to furthest leaf. This is always 0 for nodes ++** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one ++** greater than the largest child value. + */ + struct Fts5ExprNode { + int eType; /* Node type */ + int bEof; /* True at EOF */ + int bNomatch; /* True if entry is not a match */ ++ int iHeight; /* Distance to tree leaf nodes */ + + /* Next method for this node. */ + int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64); +@@ -212972,9 +240471,13 @@ struct Fts5ExprNode { + /* Child nodes. For a NOT node, this array always contains 2 entries. For + ** AND or OR nodes, it contains 2 or more entries. */ + int nChild; /* Number of child nodes */ +- Fts5ExprNode *apChild[1]; /* Array of child nodes */ ++ Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */ + }; + ++/* Size (in bytes) of an Fts5ExprNode object that holds up to N children */ ++#define SZ_FTS5EXPRNODE(N) \ ++ (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*)) ++ + #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) + + /* +@@ -212990,7 +240493,9 @@ struct Fts5ExprNode { + struct Fts5ExprTerm { + u8 bPrefix; /* True for a prefix term */ + u8 bFirst; /* True if token must be first in column */ +- char *zTerm; /* nul-terminated term */ ++ char *pTerm; /* Term data */ ++ int nQueryTerm; /* Effective size of term in bytes */ ++ int nFullTerm; /* Size of term in bytes incl. tokendata */ + Fts5IndexIter *pIter; /* Iterator for this term */ + Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */ + }; +@@ -213003,9 +240508,13 @@ struct Fts5ExprPhrase { + Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ + Fts5Buffer poslist; /* Current position list */ + int nTerm; /* Number of entries in aTerm[] */ +- Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */ ++ Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */ + }; + ++/* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */ ++#define SZ_FTS5EXPRPHRASE(N) \ ++ (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm)) ++ + /* + ** One or more phrases that must appear within a certain token distance of + ** each other within each matching document. +@@ -213014,9 +240523,12 @@ struct Fts5ExprNearset { + int nNear; /* NEAR parameter */ + Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ + int nPhrase; /* Number of entries in aPhrase[] array */ +- Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */ ++ Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */ + }; + ++/* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */ ++#define SZ_FTS5EXPRNEARSET(N) \ ++ (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*)) + + /* + ** Parse context. +@@ -213028,12 +240540,39 @@ struct Fts5Parse { + int nPhrase; /* Size of apPhrase array */ + Fts5ExprPhrase **apPhrase; /* Array of all phrases */ + Fts5ExprNode *pExpr; /* Result of a successful parse */ ++ int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ + }; + ++/* ++** Check that the Fts5ExprNode.iHeight variables are set correctly in ++** the expression tree passed as the only argument. ++*/ ++#ifndef NDEBUG ++static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){ ++ if( rc==SQLITE_OK ){ ++ if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){ ++ assert( p->iHeight==0 ); ++ }else{ ++ int ii; ++ int iMaxChild = 0; ++ for(ii=0; iinChild; ii++){ ++ Fts5ExprNode *pChild = p->apChild[ii]; ++ iMaxChild = MAX(iMaxChild, pChild->iHeight); ++ assert_expr_depth_ok(SQLITE_OK, pChild); ++ } ++ assert( p->iHeight==iMaxChild+1 ); ++ } ++ } ++} ++#else ++# define assert_expr_depth_ok(rc, p) ++#endif ++ + static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ + va_list ap; + va_start(ap, zFmt); + if( pParse->rc==SQLITE_OK ){ ++ assert( pParse->zErr==0 ); + pParse->zErr = sqlite3_vmprintf(zFmt, ap); + pParse->rc = SQLITE_ERROR; + } +@@ -213116,6 +240655,7 @@ static void fts5ParseFree(void *p){ sqlite3_free(p); } + + static int sqlite3Fts5ExprNew( + Fts5Config *pConfig, /* FTS5 Configuration */ ++ int bPhraseToAnd, + int iCol, + const char *zExpr, /* Expression text */ + Fts5Expr **ppNew, +@@ -213131,6 +240671,7 @@ static int sqlite3Fts5ExprNew( + *ppNew = 0; + *pzErr = 0; + memset(&sParse, 0, sizeof(sParse)); ++ sParse.bPhraseToAnd = bPhraseToAnd; + pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc); + if( pEngine==0 ){ return SQLITE_NOMEM; } + sParse.pConfig = pConfig; +@@ -213141,10 +240682,13 @@ static int sqlite3Fts5ExprNew( + }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); + sqlite3Fts5ParserFree(pEngine, fts5ParseFree); + ++ assert( sParse.pExpr || sParse.rc!=SQLITE_OK ); ++ assert_expr_depth_ok(sParse.rc, sParse.pExpr); ++ + /* If the LHS of the MATCH expression was a user column, apply the + ** implicit column-filter. */ +- if( iColnCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ +- int n = sizeof(Fts5Colset); ++ if( sParse.rc==SQLITE_OK && iColnCol ){ ++ int n = SZ_FTS5COLSET(1); + Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); + if( pColset ){ + pColset->nCol = 1; +@@ -213160,19 +240704,12 @@ static int sqlite3Fts5ExprNew( + sParse.rc = SQLITE_NOMEM; + sqlite3Fts5ParseNodeFree(sParse.pExpr); + }else{ +- if( !sParse.pExpr ){ +- const int nByte = sizeof(Fts5ExprNode); +- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&sParse.rc, nByte); +- if( pNew->pRoot ){ +- pNew->pRoot->bEof = 1; +- } +- }else{ +- pNew->pRoot = sParse.pExpr; +- } ++ pNew->pRoot = sParse.pExpr; + pNew->pIndex = 0; + pNew->pConfig = pConfig; + pNew->apExprPhrase = sParse.apPhrase; + pNew->nPhrase = sParse.nPhrase; ++ pNew->bDesc = 0; + sParse.apPhrase = 0; + } + }else{ +@@ -213180,10 +240717,103 @@ static int sqlite3Fts5ExprNew( + } + + sqlite3_free(sParse.apPhrase); +- *pzErr = sParse.zErr; ++ if( 0==*pzErr ){ ++ *pzErr = sParse.zErr; ++ }else{ ++ sqlite3_free(sParse.zErr); ++ } + return sParse.rc; + } + ++/* ++** Assuming that buffer z is at least nByte bytes in size and contains a ++** valid utf-8 string, return the number of characters in the string. ++*/ ++static int fts5ExprCountChar(const char *z, int nByte){ ++ int nRet = 0; ++ int ii; ++ for(ii=0; ii=3 ){ ++ int jj; ++ zExpr[iOut++] = '"'; ++ for(jj=iFirst; jj0 ){ ++ int bAnd = 0; ++ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ ++ bAnd = 1; ++ if( pConfig->eDetail==FTS5_DETAIL_NONE ){ ++ iCol = pConfig->nCol; ++ } ++ } ++ zExpr[iOut] = '\0'; ++ rc = sqlite3Fts5ExprNew(pConfig, bAnd, iCol, zExpr, pp,pConfig->pzErrmsg); ++ }else{ ++ *pp = 0; ++ } ++ sqlite3_free(zExpr); ++ } ++ ++ return rc; ++} ++ + /* + ** Free the expression node object passed as the only argument. + */ +@@ -213213,7 +240843,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ + Fts5Parse sParse; + memset(&sParse, 0, sizeof(sParse)); + +- if( *pp1 ){ ++ if( *pp1 && p2 ){ + Fts5Expr *p1 = *pp1; + int nPhrase = p1->nPhrase + p2->nPhrase; + +@@ -213238,7 +240868,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ + } + sqlite3_free(p2->apExprPhrase); + sqlite3_free(p2); +- }else{ ++ }else if( p2 ){ + *pp1 = p2; + } + +@@ -213254,6 +240884,7 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){ + int bRetValid = 0; + Fts5ExprTerm *p; + ++ assert( pTerm ); + assert( pTerm->pSynonym ); + assert( bDesc==0 || bDesc==1 ); + for(p=pTerm; p; p=p->pSynonym){ +@@ -213735,7 +241366,7 @@ static int fts5ExprNearInitAll( + p->pIter = 0; + } + rc = sqlite3Fts5IndexQuery( +- pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm), ++ pExpr->pIndex, p->pTerm, p->nQueryTerm, + (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | + (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), + pNear->pColset, +@@ -213891,7 +241522,7 @@ static int fts5ExprNodeTest_STRING( + } + }else{ + Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; +- if( pIter->iRowid==iLast || pIter->bEof ) continue; ++ if( pIter->iRowid==iLast ) continue; + bMatch = 0; + if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){ + return rc; +@@ -214321,8 +241952,8 @@ static int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bD + } + + /* If the iterator is not at a real match, skip forward until it is. */ +- while( pRoot->bNomatch ){ +- assert( pRoot->bEof==0 && rc==SQLITE_OK ); ++ while( pRoot->bNomatch && rc==SQLITE_OK ){ ++ assert( pRoot->bEof==0 ); + rc = fts5ExprNodeNext(p, pRoot, 0, 0); + } + return rc; +@@ -214372,7 +242003,7 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ + Fts5ExprTerm *pSyn; + Fts5ExprTerm *pNext; + Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; +- sqlite3_free(pTerm->zTerm); ++ sqlite3_free(pTerm->pTerm); + sqlite3Fts5IterClose(pTerm->pIter); + for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){ + pNext = pSyn->pSynonym; +@@ -214413,12 +242044,9 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( + Fts5ExprNearset *pRet = 0; + + if( pParse->rc==SQLITE_OK ){ +- if( pPhrase==0 ){ +- return pNear; +- } + if( pNear==0 ){ + sqlite3_int64 nByte; +- nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); ++ nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1); + pRet = sqlite3_malloc64(nByte); + if( pRet==0 ){ + pParse->rc = SQLITE_NOMEM; +@@ -214429,7 +242057,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( + int nNew = pNear->nPhrase + SZALLOC; + sqlite3_int64 nByte; + +- nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); ++ nByte = SZ_FTS5EXPRNEARSET(nNew+1); + pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); + if( pRet==0 ){ + pParse->rc = SQLITE_NOMEM; +@@ -214446,6 +242074,9 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( + }else{ + if( pRet->nPhrase>0 ){ + Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1]; ++ assert( pParse!=0 ); ++ assert( pParse->apPhrase!=0 ); ++ assert( pParse->nPhrase>=2 ); + assert( pLast==pParse->apPhrase[pParse->nPhrase-2] ); + if( pPhrase->nTerm==0 ){ + fts5ExprPhraseFree(pPhrase); +@@ -214467,6 +242098,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( + typedef struct TokenCtx TokenCtx; + struct TokenCtx { + Fts5ExprPhrase *pPhrase; ++ Fts5Config *pConfig; + int rc; + }; + +@@ -214500,8 +242132,12 @@ static int fts5ParseTokenize( + rc = SQLITE_NOMEM; + }else{ + memset(pSyn, 0, (size_t)nByte); +- pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); +- memcpy(pSyn->zTerm, pToken, nToken); ++ pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); ++ pSyn->nFullTerm = pSyn->nQueryTerm = nToken; ++ if( pCtx->pConfig->bTokendata ){ ++ pSyn->nQueryTerm = (int)strlen(pSyn->pTerm); ++ } ++ memcpy(pSyn->pTerm, pToken, nToken); + pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; + pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; + } +@@ -214512,12 +242148,12 @@ static int fts5ParseTokenize( + int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); + + pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, +- sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew ++ SZ_FTS5EXPRPHRASE(nNew+1) + ); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ +- if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase)); ++ if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1)); + pCtx->pPhrase = pPhrase = pNew; + pNew->nTerm = nNew - SZALLOC; + } +@@ -214526,7 +242162,11 @@ static int fts5ParseTokenize( + if( rc==SQLITE_OK ){ + pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; + memset(pTerm, 0, sizeof(Fts5ExprTerm)); +- pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); ++ pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); ++ pTerm->nFullTerm = pTerm->nQueryTerm = nToken; ++ if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){ ++ pTerm->nQueryTerm = (int)strlen(pTerm->pTerm); ++ } + } + } + +@@ -214561,6 +242201,20 @@ static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){ + pParse->pExpr = p; + } + ++static int parseGrowPhraseArray(Fts5Parse *pParse){ ++ if( (pParse->nPhrase % 8)==0 ){ ++ sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); ++ Fts5ExprPhrase **apNew; ++ apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); ++ if( apNew==0 ){ ++ pParse->rc = SQLITE_NOMEM; ++ return SQLITE_NOMEM; ++ } ++ pParse->apPhrase = apNew; ++ } ++ return SQLITE_OK; ++} ++ + /* + ** This function is called by the parser to process a string token. The + ** string may or may not be quoted. In any case it is tokenized and a +@@ -214579,6 +242233,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( + + memset(&sCtx, 0, sizeof(TokenCtx)); + sCtx.pPhrase = pAppend; ++ sCtx.pConfig = pConfig; + + rc = fts5ParseStringFromToken(pToken, &z); + if( rc==SQLITE_OK ){ +@@ -214596,16 +242251,9 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( + }else{ + + if( pAppend==0 ){ +- if( (pParse->nPhrase % 8)==0 ){ +- sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); +- Fts5ExprPhrase **apNew; +- apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); +- if( apNew==0 ){ +- pParse->rc = SQLITE_NOMEM; +- fts5ExprPhraseFree(sCtx.pPhrase); +- return 0; +- } +- pParse->apPhrase = apNew; ++ if( parseGrowPhraseArray(pParse) ){ ++ fts5ExprPhraseFree(sCtx.pPhrase); ++ return 0; + } + pParse->nPhrase++; + } +@@ -214613,10 +242261,11 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( + if( sCtx.pPhrase==0 ){ + /* This happens when parsing a token or quoted phrase that contains + ** no token characters at all. (e.g ... MATCH '""'). */ +- sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); ++ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1)); + }else if( sCtx.pPhrase->nTerm ){ + sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; + } ++ assert( pParse->apPhrase!=0 ); + pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; + } + +@@ -214633,30 +242282,32 @@ static int sqlite3Fts5ExprClonePhrase( + Fts5Expr **ppNew + ){ + int rc = SQLITE_OK; /* Return code */ +- Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */ ++ Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */ + Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ +- TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */ +- +- pOrig = pExpr->apExprPhrase[iPhrase]; +- pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); ++ TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */ ++ if( !pExpr || iPhrase<0 || iPhrase>=pExpr->nPhrase ){ ++ rc = SQLITE_RANGE; ++ }else{ ++ pOrig = pExpr->apExprPhrase[iPhrase]; ++ pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); ++ } + if( rc==SQLITE_OK ){ + pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, + sizeof(Fts5ExprPhrase*)); + } + if( rc==SQLITE_OK ){ +- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, +- sizeof(Fts5ExprNode)); ++ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1)); + } + if( rc==SQLITE_OK ){ + pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, +- sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); ++ SZ_FTS5EXPRNEARSET(2)); + } +- if( rc==SQLITE_OK ){ ++ if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ + Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; + if( pColsetOrig ){ + sqlite3_int64 nByte; + Fts5Colset *pColset; +- nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); ++ nByte = SZ_FTS5COLSET(pColsetOrig->nCol); + pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); + if( pColset ){ + memcpy(pColset, pColsetOrig, (size_t)nByte); +@@ -214665,29 +242316,30 @@ static int sqlite3Fts5ExprClonePhrase( + } + } + +- if( pOrig->nTerm ){ +- int i; /* Used to iterate through phrase terms */ +- for(i=0; rc==SQLITE_OK && inTerm; i++){ +- int tflags = 0; +- Fts5ExprTerm *p; +- for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ +- const char *zTerm = p->zTerm; +- rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm), +- 0, 0); +- tflags = FTS5_TOKEN_COLOCATED; +- } +- if( rc==SQLITE_OK ){ +- sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; +- sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; ++ if( rc==SQLITE_OK ){ ++ if( pOrig->nTerm ){ ++ int i; /* Used to iterate through phrase terms */ ++ sCtx.pConfig = pExpr->pConfig; ++ for(i=0; rc==SQLITE_OK && inTerm; i++){ ++ int tflags = 0; ++ Fts5ExprTerm *p; ++ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ ++ rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0); ++ tflags = FTS5_TOKEN_COLOCATED; ++ } ++ if( rc==SQLITE_OK ){ ++ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; ++ sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; ++ } + } ++ }else{ ++ /* This happens when parsing a token or quoted phrase that contains ++ ** no token characters at all. (e.g ... MATCH '""'). */ ++ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1)); + } +- }else{ +- /* This happens when parsing a token or quoted phrase that contains +- ** no token characters at all. (e.g ... MATCH '""'). */ +- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); + } + +- if( rc==SQLITE_OK ){ ++ if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ + /* All the allocations succeeded. Put the expression object together. */ + pNew->pIndex = pExpr->pIndex; + pNew->pConfig = pExpr->pConfig; +@@ -214748,7 +242400,8 @@ static void sqlite3Fts5ParseSetDistance( + ); + return; + } +- nNear = nNear * 10 + (p->p[i] - '0'); ++ if( nNear<214748363 ) nNear = nNear * 10 + (p->p[i] - '0'); ++ /* ^^^^^^^^^^^^^^^--- Prevent integer overflow */ + } + }else{ + nNear = FTS5_DEFAULT_NEARDIST; +@@ -214777,7 +242430,7 @@ static Fts5Colset *fts5ParseColset( + assert( pParse->rc==SQLITE_OK ); + assert( iCol>=0 && iColpConfig->nCol ); + +- pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); ++ pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1)); + if( pNew==0 ){ + pParse->rc = SQLITE_NOMEM; + }else{ +@@ -214812,7 +242465,7 @@ static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p + int nCol = pParse->pConfig->nCol; + + pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, +- sizeof(Fts5Colset) + sizeof(int)*nCol ++ SZ_FTS5COLSET(nCol+1) + ); + if( pRet ){ + int i; +@@ -214873,7 +242526,7 @@ static Fts5Colset *sqlite3Fts5ParseColset( + static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ + Fts5Colset *pRet; + if( pOrig ){ +- sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); ++ sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol); + pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); + if( pRet ){ + memcpy(pRet, pOrig, (size_t)nByte); +@@ -214958,9 +242611,8 @@ static void sqlite3Fts5ParseSetColset( + ){ + Fts5Colset *pFree = pColset; + if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ +- pParse->rc = SQLITE_ERROR; +- pParse->zErr = sqlite3_mprintf( +- "fts5: column queries are not supported (detail=none)" ++ sqlite3Fts5ParseError(pParse, ++ "fts5: column queries are not supported (detail=none)" + ); + }else{ + fts5ParseSetColset(pParse, pExpr, pColset, &pFree); +@@ -215001,7 +242653,11 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){ + } + } + ++/* ++** Add pSub as a child of p. ++*/ + static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ ++ int ii = p->nChild; + if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){ + int nByte = sizeof(Fts5ExprNode*) * pSub->nChild; + memcpy(&p->apChild[p->nChild], pSub->apChild, nByte); +@@ -215010,6 +242666,73 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ + }else{ + p->apChild[p->nChild++] = pSub; + } ++ for( ; iinChild; ii++){ ++ p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1); ++ } ++} ++ ++/* ++** This function is used when parsing LIKE or GLOB patterns against ++** trigram indexes that specify either detail=column or detail=none. ++** It converts a phrase: ++** ++** abc + def + ghi ++** ++** into an AND tree: ++** ++** abc AND def AND ghi ++*/ ++static Fts5ExprNode *fts5ParsePhraseToAnd( ++ Fts5Parse *pParse, ++ Fts5ExprNearset *pNear ++){ ++ int nTerm = pNear->apPhrase[0]->nTerm; ++ int ii; ++ int nByte; ++ Fts5ExprNode *pRet; ++ ++ assert( pNear->nPhrase==1 ); ++ assert( pParse->bPhraseToAnd ); ++ ++ nByte = SZ_FTS5EXPRNODE(nTerm+1); ++ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); ++ if( pRet ){ ++ pRet->eType = FTS5_AND; ++ pRet->nChild = nTerm; ++ pRet->iHeight = 1; ++ fts5ExprAssignXNext(pRet); ++ pParse->nPhrase--; ++ for(ii=0; iirc, SZ_FTS5EXPRPHRASE(1) ++ ); ++ if( pPhrase ){ ++ if( parseGrowPhraseArray(pParse) ){ ++ fts5ExprPhraseFree(pPhrase); ++ }else{ ++ Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii]; ++ Fts5ExprTerm *pTo = &pPhrase->aTerm[0]; ++ pParse->apPhrase[pParse->nPhrase++] = pPhrase; ++ pPhrase->nTerm = 1; ++ pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm); ++ pTo->nQueryTerm = p->nQueryTerm; ++ pTo->nFullTerm = p->nFullTerm; ++ pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING, ++ 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) ++ ); ++ } ++ } ++ } ++ ++ if( pParse->rc ){ ++ sqlite3Fts5ParseNodeFree(pRet); ++ pRet = 0; ++ }else{ ++ sqlite3Fts5ParseNearsetFree(pNear); ++ } ++ } ++ ++ return pRet; + } + + /* +@@ -215036,51 +242759,67 @@ static Fts5ExprNode *sqlite3Fts5ParseNode( + if( eType!=FTS5_STRING && pLeft==0 ) return pRight; + if( eType!=FTS5_STRING && pRight==0 ) return pLeft; + +- if( eType==FTS5_NOT ){ +- nChild = 2; +- }else if( eType==FTS5_AND || eType==FTS5_OR ){ +- nChild = 2; +- if( pLeft->eType==eType ) nChild += pLeft->nChild-1; +- if( pRight->eType==eType ) nChild += pRight->nChild-1; +- } ++ if( eType==FTS5_STRING ++ && pParse->bPhraseToAnd ++ && pNear->apPhrase[0]->nTerm>1 ++ ){ ++ pRet = fts5ParsePhraseToAnd(pParse, pNear); ++ }else{ ++ if( eType==FTS5_NOT ){ ++ nChild = 2; ++ }else if( eType==FTS5_AND || eType==FTS5_OR ){ ++ nChild = 2; ++ if( pLeft->eType==eType ) nChild += pLeft->nChild-1; ++ if( pRight->eType==eType ) nChild += pRight->nChild-1; ++ } + +- nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); +- pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); ++ nByte = SZ_FTS5EXPRNODE(nChild); ++ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); + +- if( pRet ){ +- pRet->eType = eType; +- pRet->pNear = pNear; +- fts5ExprAssignXNext(pRet); +- if( eType==FTS5_STRING ){ +- int iPhrase; +- for(iPhrase=0; iPhrasenPhrase; iPhrase++){ +- pNear->apPhrase[iPhrase]->pNode = pRet; +- if( pNear->apPhrase[iPhrase]->nTerm==0 ){ +- pRet->xNext = 0; +- pRet->eType = FTS5_EOF; +- } +- } +- +- if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ +- Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; +- if( pNear->nPhrase!=1 +- || pPhrase->nTerm>1 +- || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) +- ){ +- assert( pParse->rc==SQLITE_OK ); +- pParse->rc = SQLITE_ERROR; +- assert( pParse->zErr==0 ); +- pParse->zErr = sqlite3_mprintf( +- "fts5: %s queries are not supported (detail!=full)", +- pNear->nPhrase==1 ? "phrase": "NEAR" +- ); +- sqlite3_free(pRet); ++ if( pRet ){ ++ pRet->eType = eType; ++ pRet->pNear = pNear; ++ fts5ExprAssignXNext(pRet); ++ if( eType==FTS5_STRING ){ ++ int iPhrase; ++ for(iPhrase=0; iPhrasenPhrase; iPhrase++){ ++ pNear->apPhrase[iPhrase]->pNode = pRet; ++ if( pNear->apPhrase[iPhrase]->nTerm==0 ){ ++ pRet->xNext = 0; ++ pRet->eType = FTS5_EOF; ++ } ++ } ++ ++ if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ ++ Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; ++ if( pNear->nPhrase!=1 ++ || pPhrase->nTerm>1 ++ || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) ++ ){ ++ sqlite3Fts5ParseError(pParse, ++ "fts5: %s queries are not supported (detail!=full)", ++ pNear->nPhrase==1 ? "phrase": "NEAR" ++ ); ++ sqlite3Fts5ParseNodeFree(pRet); ++ pRet = 0; ++ pNear = 0; ++ assert( pLeft==0 && pRight==0 ); ++ } ++ } ++ }else{ ++ assert( pNear==0 ); ++ fts5ExprAddChildren(pRet, pLeft); ++ fts5ExprAddChildren(pRet, pRight); ++ pLeft = pRight = 0; ++ if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){ ++ sqlite3Fts5ParseError(pParse, ++ "fts5 expression tree is too large (maximum depth %d)", ++ SQLITE_FTS5_MAX_EXPR_DEPTH ++ ); ++ sqlite3Fts5ParseNodeFree(pRet); + pRet = 0; + } + } +- }else{ +- fts5ExprAddChildren(pRet, pLeft); +- fts5ExprAddChildren(pRet, pRight); + } + } + } +@@ -215115,6 +242854,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( + assert( pRight->eType==FTS5_STRING + || pRight->eType==FTS5_TERM + || pRight->eType==FTS5_EOF ++ || (pRight->eType==FTS5_AND && pParse->bPhraseToAnd) + ); + + if( pLeft->eType==FTS5_AND ){ +@@ -215128,6 +242868,8 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( + ); + + if( pRight->eType==FTS5_EOF ){ ++ assert( pParse->apPhrase!=0 ); ++ assert( pParse->nPhrase>0 ); + assert( pParse->apPhrase[pParse->nPhrase-1]==pRight->pNear->apPhrase[0] ); + sqlite3Fts5ParseNodeFree(pRight); + pRet = pLeft; +@@ -215158,6 +242900,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( + return pRet; + } + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ + sqlite3_int64 nByte = 0; + Fts5ExprTerm *p; +@@ -215165,16 +242908,17 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ + + /* Determine the maximum amount of space required. */ + for(p=pTerm; p; p=p->pSynonym){ +- nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2; ++ nByte += pTerm->nQueryTerm * 2 + 3 + 2; + } + zQuoted = sqlite3_malloc64(nByte); + + if( zQuoted ){ + int i = 0; + for(p=pTerm; p; p=p->pSynonym){ +- char *zIn = p->zTerm; ++ char *zIn = p->pTerm; ++ char *zEnd = &zIn[p->nQueryTerm]; + zQuoted[i++] = '"'; +- while( *zIn ){ ++ while( zInnTerm; iTerm++){ +- char *zTerm = pPhrase->aTerm[iTerm].zTerm; +- zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); ++ Fts5ExprTerm *p = &pPhrase->aTerm[iTerm]; ++ zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ", ++ p->nQueryTerm, p->pTerm ++ ); + if( pPhrase->aTerm[iTerm].bPrefix ){ + zRet = fts5PrintfAppend(zRet, "*"); + } +@@ -215263,6 +243009,8 @@ static char *fts5ExprPrintTcl( + if( zRet==0 ) return 0; + } + ++ }else if( pExpr->eType==0 ){ ++ zRet = sqlite3_mprintf("{}"); + }else{ + char const *zOp = 0; + int i; +@@ -215301,8 +243049,17 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){ + int iTerm; + + if( pNear->pColset ){ +- int iCol = pNear->pColset->aiCol[0]; +- zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[iCol]); ++ int ii; ++ Fts5Colset *pColset = pNear->pColset; ++ if( pColset->nCol>1 ) zRet = fts5PrintfAppend(zRet, "{"); ++ for(ii=0; iinCol; ii++){ ++ zRet = fts5PrintfAppend(zRet, "%s%s", ++ pConfig->azCol[pColset->aiCol[ii]], ii==pColset->nCol-1 ? "" : " " ++ ); ++ } ++ if( zRet ){ ++ zRet = fts5PrintfAppend(zRet, "%s : ", pColset->nCol>1 ? "}" : ""); ++ } + if( zRet==0 ) return 0; + } + +@@ -215425,7 +243182,7 @@ static void fts5ExprFunction( + + rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr); + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts5ExprNew(pConfig, pConfig->nCol, zExpr, &pExpr, &zErr); ++ rc = sqlite3Fts5ExprNew(pConfig, 0, pConfig->nCol, zExpr, &pExpr, &zErr); + } + if( rc==SQLITE_OK ){ + char *zText; +@@ -215515,12 +243272,14 @@ static void fts5ExprFold( + sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics)); + } + } ++#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */ + + /* + ** This is called during initialization to register the fts5_expr() scalar + ** UDF with the SQLite handle passed as the only argument. + */ + static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + struct Fts5ExprFunc { + const char *z; + void (*x)(sqlite3_context*,int,sqlite3_value**); +@@ -215538,6 +243297,10 @@ static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ + struct Fts5ExprFunc *p = &aFunc[i]; + rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0); + } ++#else ++ int rc = SQLITE_OK; ++ UNUSED_PARAM2(pGlobal,db); ++#endif + + /* Avoid warnings indicating that sqlite3Fts5ParserTrace() and + ** sqlite3Fts5ParserFallback() are unused */ +@@ -215588,6 +243351,15 @@ struct Fts5PoslistPopulator { + int bMiss; + }; + ++/* ++** Clear the position lists associated with all phrases in the expression ++** passed as the first argument. Argument bLive is true if the expression ++** might be pointing to a real entry, otherwise it has just been reset. ++** ++** At present this function is only used for detail=col and detail=none ++** fts5 tables. This implies that all phrases must be at most 1 token ++** in size, as phrase matches are not supported without detail=full. ++*/ + static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ + Fts5PoslistPopulator *pRet; + pRet = sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); +@@ -215597,7 +243369,7 @@ static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int b + for(i=0; inPhrase; i++){ + Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist; + Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; +- assert( pExpr->apExprPhrase[i]->nTerm==1 ); ++ assert( pExpr->apExprPhrase[i]->nTerm<=1 ); + if( bLive && + (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof) + ){ +@@ -215628,6 +243400,17 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ + return 0; + } + ++/* ++** pToken is a buffer nToken bytes in size that may or may not contain ++** an embedded 0x00 byte. If it does, return the number of bytes in ++** the buffer before the 0x00. If it does not, return nToken. ++*/ ++static int fts5QueryTerm(const char *pToken, int nToken){ ++ int ii; ++ for(ii=0; iipExpr; + int i; ++ int nQuery = nToken; ++ i64 iRowid = pExpr->pRoot->iRowid; + + UNUSED_PARAM2(iUnused1, iUnused2); + +- if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; ++ if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE; ++ if( pExpr->pConfig->bTokendata ){ ++ nQuery = fts5QueryTerm(pToken, nQuery); ++ } + if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; + for(i=0; inPhrase; i++){ +- Fts5ExprTerm *pTerm; ++ Fts5ExprTerm *pT; + if( p->aPopulator[i].bOk==0 ) continue; +- for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ +- int nTerm = (int)strlen(pTerm->zTerm); +- if( (nTerm==nToken || (nTermbPrefix)) +- && memcmp(pTerm->zTerm, pToken, nTerm)==0 ++ for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ ++ if( (pT->nQueryTerm==nQuery || (pT->nQueryTermbPrefix)) ++ && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 + ){ + int rc = sqlite3Fts5PoslistWriterAppend( + &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff + ); ++ if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){ ++ int iCol = p->iOff>>32; ++ int iTokOff = p->iOff & 0x7FFFFFFF; ++ rc = sqlite3Fts5IndexIterWriteTokendata( ++ pT->pIter, pToken, nToken, iRowid, iCol, iTokOff ++ ); ++ } + if( rc ) return rc; + break; + } +@@ -215708,6 +243502,7 @@ static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){ + pNode->iRowid = iRowid; + pNode->bEof = 0; + switch( pNode->eType ){ ++ case 0: + case FTS5_TERM: + case FTS5_STRING: + return (pNode->pNear->apPhrase[0]->poslist.n>0); +@@ -215790,6 +243585,82 @@ static int sqlite3Fts5ExprPhraseCollist( + return rc; + } + ++/* ++** Does the work of the fts5_api.xQueryToken() API method. ++*/ ++static int sqlite3Fts5ExprQueryToken( ++ Fts5Expr *pExpr, ++ int iPhrase, ++ int iToken, ++ const char **ppOut, ++ int *pnOut ++){ ++ Fts5ExprPhrase *pPhrase = 0; ++ ++ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ ++ return SQLITE_RANGE; ++ } ++ pPhrase = pExpr->apExprPhrase[iPhrase]; ++ if( iToken<0 || iToken>=pPhrase->nTerm ){ ++ return SQLITE_RANGE; ++ } ++ ++ *ppOut = pPhrase->aTerm[iToken].pTerm; ++ *pnOut = pPhrase->aTerm[iToken].nFullTerm; ++ return SQLITE_OK; ++} ++ ++/* ++** Does the work of the fts5_api.xInstToken() API method. ++*/ ++static int sqlite3Fts5ExprInstToken( ++ Fts5Expr *pExpr, ++ i64 iRowid, ++ int iPhrase, ++ int iCol, ++ int iOff, ++ int iToken, ++ const char **ppOut, ++ int *pnOut ++){ ++ Fts5ExprPhrase *pPhrase = 0; ++ Fts5ExprTerm *pTerm = 0; ++ int rc = SQLITE_OK; ++ ++ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ ++ return SQLITE_RANGE; ++ } ++ pPhrase = pExpr->apExprPhrase[iPhrase]; ++ if( iToken<0 || iToken>=pPhrase->nTerm ){ ++ return SQLITE_RANGE; ++ } ++ pTerm = &pPhrase->aTerm[iToken]; ++ if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){ ++ rc = sqlite3Fts5IterToken( ++ pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm, ++ iRowid, iCol, iOff+iToken, ppOut, pnOut ++ ); ++ }else{ ++ *ppOut = pTerm->pTerm; ++ *pnOut = pTerm->nFullTerm; ++ } ++ return rc; ++} ++ ++/* ++** Clear the token mappings for all Fts5IndexIter objects managed by ++** the expression passed as the only argument. ++*/ ++static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ ++ int ii; ++ for(ii=0; iinPhrase; ii++){ ++ Fts5ExprTerm *pT; ++ for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){ ++ sqlite3Fts5IndexIterClearTokendata(pT->pIter); ++ } ++ } ++} ++ + /* + ** 2014 August 11 + ** +@@ -215812,7 +243683,7 @@ typedef struct Fts5HashEntry Fts5HashEntry; + + /* + ** This file contains the implementation of an in-memory hash table used +-** to accumuluate "term -> doclist" content before it is flused to a level-0 ++** to accumulate "term -> doclist" content before it is flushed to a level-0 + ** segment. + */ + +@@ -215828,10 +243699,15 @@ struct Fts5Hash { + + /* + ** Each entry in the hash table is represented by an object of the +-** following type. Each object, its key (a nul-terminated string) and +-** its current data are stored in a single memory allocation. The +-** key immediately follows the object in memory. The position list +-** data immediately follows the key data in memory. ++** following type. Each object, its key, and its current data are stored ++** in a single memory allocation. The key immediately follows the object ++** in memory. The position list data immediately follows the key data ++** in memory. ++** ++** The key is Fts5HashEntry.nKey bytes in size. It consists of a single ++** byte identifying the index (either the main term index or a prefix-index), ++** followed by the term data. For example: "0token". There is no ++** nul-terminator - in this case nKey=6. + ** + ** The data that follows the key is in a similar, but not identical format + ** to the doclist data stored in the database. It is: +@@ -215864,7 +243740,7 @@ struct Fts5HashEntry { + }; + + /* +-** Eqivalent to: ++** Equivalent to: + ** + ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } + */ +@@ -215966,8 +243842,7 @@ static int fts5HashResize(Fts5Hash *pHash){ + unsigned int iHash; + Fts5HashEntry *p = apOld[i]; + apOld[i] = p->pHashNext; +- iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), +- (int)strlen(fts5EntryKey(p))); ++ iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey); + p->pHashNext = apNew[iHash]; + apNew[iHash] = p; + } +@@ -216051,7 +243926,7 @@ static int sqlite3Fts5HashWrite( + for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ + char *zKey = fts5EntryKey(p); + if( zKey[0]==bByte +- && p->nKey==nToken ++ && p->nKey==nToken+1 + && memcmp(&zKey[1], pToken, nToken)==0 + ){ + break; +@@ -216081,9 +243956,9 @@ static int sqlite3Fts5HashWrite( + zKey[0] = bByte; + memcpy(&zKey[1], pToken, nToken); + assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) ); +- p->nKey = nToken; ++ p->nKey = nToken+1; + zKey[nToken+1] = '\0'; +- p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry); ++ p->nData = nToken+1 + sizeof(Fts5HashEntry); + p->pHashNext = pHash->aSlot[iHash]; + pHash->aSlot[iHash] = p; + pHash->nEntry++; +@@ -216098,7 +243973,6 @@ static int sqlite3Fts5HashWrite( + p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1); + } + +- nIncr += p->nData; + }else{ + + /* Appending to an existing hash-entry. Check that there is enough +@@ -216131,8 +244005,9 @@ static int sqlite3Fts5HashWrite( + /* If this is a new rowid, append the 4-byte size field for the previous + ** entry, and the new rowid for this entry. */ + if( iRowid!=p->iRowid ){ ++ u64 iDiff = (u64)iRowid - (u64)p->iRowid; + fts5HashAddPoslistSize(pHash, p, 0); +- p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); ++ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iDiff); + p->iRowid = iRowid; + bNew = 1; + p->iSzPoslist = p->nData; +@@ -216148,7 +244023,7 @@ static int sqlite3Fts5HashWrite( + p->bContent = 1; + }else{ + /* Append a new column value, if necessary */ +- assert( iCol>=p->iCol ); ++ assert_nc( iCol>=p->iCol ); + if( iCol!=p->iCol ){ + if( pHash->eDetail==FTS5_DETAIL_FULL ){ + pPtr[p->nData++] = 0x01; +@@ -216200,12 +244075,17 @@ static Fts5HashEntry *fts5HashEntryMerge( + *ppOut = p1; + p1 = 0; + }else{ +- int i = 0; + char *zKey1 = fts5EntryKey(p1); + char *zKey2 = fts5EntryKey(p2); +- while( zKey1[i]==zKey2[i] ) i++; ++ int nMin = MIN(p1->nKey, p2->nKey); ++ ++ int cmp = memcmp(zKey1, zKey2, nMin); ++ if( cmp==0 ){ ++ cmp = p1->nKey - p2->nKey; ++ } ++ assert( cmp!=0 ); + +- if( ((u8)zKey1[i])>((u8)zKey2[i]) ){ ++ if( cmp>0 ){ + /* p2 is smaller */ + *ppOut = p2; + ppOut = &p2->pScanNext; +@@ -216224,10 +244104,8 @@ static Fts5HashEntry *fts5HashEntryMerge( + } + + /* +-** Extract all tokens from hash table iHash and link them into a list +-** in sorted order. The hash table is cleared before returning. It is +-** the responsibility of the caller to free the elements of the returned +-** list. ++** Link all tokens from hash table iHash into a list in sorted order. The ++** tokens are not removed from the hash table. + */ + static int fts5HashEntrySort( + Fts5Hash *pHash, +@@ -216249,7 +244127,7 @@ static int fts5HashEntrySort( + Fts5HashEntry *pIter; + for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ + if( pTerm==0 +- || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) ++ || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) + ){ + Fts5HashEntry *pEntry = pIter; + pEntry->pScanNext = 0; +@@ -216267,7 +244145,6 @@ static int fts5HashEntrySort( + pList = fts5HashEntryMerge(pList, ap[i]); + } + +- pHash->nEntry = 0; + sqlite3_free(ap); + *ppSorted = pList; + return SQLITE_OK; +@@ -216289,12 +244166,11 @@ static int sqlite3Fts5HashQuery( + + for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ + zKey = fts5EntryKey(p); +- assert( p->nKey+1==(int)strlen(zKey) ); +- if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break; ++ if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break; + } + + if( p ){ +- int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1; ++ int nHashPre = sizeof(Fts5HashEntry) + nTerm; + int nList = p->nData - nHashPre; + u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10)); + if( pRet ){ +@@ -216321,6 +244197,28 @@ static int sqlite3Fts5HashScanInit( + return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan); + } + ++#ifdef SQLITE_DEBUG ++static int fts5HashCount(Fts5Hash *pHash){ ++ int nEntry = 0; ++ int ii; ++ for(ii=0; iinSlot; ii++){ ++ Fts5HashEntry *p = 0; ++ for(p=pHash->aSlot[ii]; p; p=p->pHashNext){ ++ nEntry++; ++ } ++ } ++ return nEntry; ++} ++#endif ++ ++/* ++** Return true if the hash table is empty, false otherwise. ++*/ ++static int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){ ++ assert( pHash->nEntry==fts5HashCount(pHash) ); ++ return pHash->nEntry==0; ++} ++ + static void sqlite3Fts5HashScanNext(Fts5Hash *p){ + assert( !sqlite3Fts5HashScanEof(p) ); + p->pScan = p->pScan->pScanNext; +@@ -216333,19 +244231,22 @@ static int sqlite3Fts5HashScanEof(Fts5Hash *p){ + static void sqlite3Fts5HashScanEntry( + Fts5Hash *pHash, + const char **pzTerm, /* OUT: term (nul-terminated) */ ++ int *pnTerm, /* OUT: Size of term in bytes */ + const u8 **ppDoclist, /* OUT: pointer to doclist */ + int *pnDoclist /* OUT: size of doclist in bytes */ + ){ + Fts5HashEntry *p; + if( (p = pHash->pScan) ){ + char *zKey = fts5EntryKey(p); +- int nTerm = (int)strlen(zKey); ++ int nTerm = p->nKey; + fts5HashAddPoslistSize(pHash, p, 0); + *pzTerm = zKey; +- *ppDoclist = (const u8*)&zKey[nTerm+1]; +- *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); ++ *pnTerm = nTerm; ++ *ppDoclist = (const u8*)&zKey[nTerm]; ++ *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm); + }else{ + *pzTerm = 0; ++ *pnTerm = 0; + *ppDoclist = 0; + *pnDoclist = 0; + } +@@ -216407,6 +244308,26 @@ static void sqlite3Fts5HashScanEntry( + # error "FTS5_MAX_PREFIX_INDEXES is too large" + #endif + ++#define FTS5_MAX_LEVEL 64 ++ ++/* ++** There are two versions of the format used for the structure record: ++** ++** 1. the legacy format, that may be read by all fts5 versions, and ++** ++** 2. the V2 format, which is used by contentless_delete=1 databases. ++** ++** Both begin with a 4-byte "configuration cookie" value. Then, a legacy ++** format structure record contains a varint - the number of levels in ++** the structure. Whereas a V2 structure record contains the constant ++** 4 bytes [0xff 0x00 0x00 0x01]. This is unambiguous as the value of a ++** varint has to be at least 16256 to begin with "0xFF". And the default ++** maximum number of levels is 64. ++** ++** See below for more on structure record formats. ++*/ ++#define FTS5_STRUCTURE_V2 "\xFF\x00\x00\x01" ++ + /* + ** Details: + ** +@@ -216414,7 +244335,7 @@ static void sqlite3Fts5HashScanEntry( + ** + ** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB); + ** +-** , contains the following 5 types of records. See the comments surrounding ++** , contains the following 6 types of records. See the comments surrounding + ** the FTS5_*_ROWID macros below for a description of how %_data rowids are + ** assigned to each fo them. + ** +@@ -216423,12 +244344,12 @@ static void sqlite3Fts5HashScanEntry( + ** The set of segments that make up an index - the index structure - are + ** recorded in a single record within the %_data table. The record consists + ** of a single 32-bit configuration cookie value followed by a list of +-** SQLite varints. If the FTS table features more than one index (because +-** there are one or more prefix indexes), it is guaranteed that all share +-** the same cookie value. ++** SQLite varints. + ** +-** Immediately following the configuration cookie, the record begins with +-** three varints: ++** If the structure record is a V2 record, the configuration cookie is ++** followed by the following 4 bytes: [0xFF 0x00 0x00 0x01]. ++** ++** Next, the record continues with three varints: + ** + ** + number of levels, + ** + total number of segments on all levels, +@@ -216443,6 +244364,12 @@ static void sqlite3Fts5HashScanEntry( + ** + first leaf page number (often 1, always greater than 0) + ** + final leaf page number + ** ++** Then, for V2 structures only: ++** ++** + lower origin counter value, ++** + upper origin counter value, ++** + the number of tombstone hash pages. ++** + ** 2. The Averages Record: + ** + ** A single record within the %_data table. The data is a list of varints. +@@ -216558,6 +244485,38 @@ static void sqlite3Fts5HashScanEntry( + ** * A list of delta-encoded varints - the first rowid on each subsequent + ** child page. + ** ++** 6. Tombstone Hash Page ++** ++** These records are only ever present in contentless_delete=1 tables. ++** There are zero or more of these associated with each segment. They ++** are used to store the tombstone rowids for rows contained in the ++** associated segments. ++** ++** The set of nHashPg tombstone hash pages associated with a single ++** segment together form a single hash table containing tombstone rowids. ++** To find the page of the hash on which a key might be stored: ++** ++** iPg = (rowid % nHashPg) ++** ++** Then, within page iPg, which has nSlot slots: ++** ++** iSlot = (rowid / nHashPg) % nSlot ++** ++** Each tombstone hash page begins with an 8 byte header: ++** ++** 1-byte: Key-size (the size in bytes of each slot). Either 4 or 8. ++** 1-byte: rowid-0-tombstone flag. This flag is only valid on the ++** first tombstone hash page for each segment (iPg=0). If set, ++** the hash table contains rowid 0. If clear, it does not. ++** Rowid 0 is handled specially. ++** 2-bytes: unused. ++** 4-bytes: Big-endian integer containing number of entries on page. ++** ++** Following this are nSlot 4 or 8 byte slots (depending on the key-size ++** in the first byte of the page header). The number of slots may be ++** determined based on the size of the page record and the key-size: ++** ++** nSlot = (nByte - 8) / key-size + */ + + /* +@@ -216591,6 +244550,7 @@ static void sqlite3Fts5HashScanEntry( + + #define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno) + #define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno) ++#define FTS5_TOMBSTONE_ROWID(segid,ipg) fts5_dri(segid+(1<<16), 0, 0, ipg) + + #ifdef SQLITE_DEBUG + static int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; } +@@ -216617,6 +244577,9 @@ typedef struct Fts5SegWriter Fts5SegWriter; + typedef struct Fts5Structure Fts5Structure; + typedef struct Fts5StructureLevel Fts5StructureLevel; + typedef struct Fts5StructureSegment Fts5StructureSegment; ++typedef struct Fts5TokenDataIter Fts5TokenDataIter; ++typedef struct Fts5TokenDataMap Fts5TokenDataMap; ++typedef struct Fts5TombstoneArray Fts5TombstoneArray; + + struct Fts5Data { + u8 *p; /* Pointer to buffer containing record */ +@@ -216626,6 +244589,12 @@ struct Fts5Data { + + /* + ** One object per %_data table. ++** ++** nContentlessDelete: ++** The number of contentless delete operations since the most recent ++** call to fts5IndexFlush() or fts5IndexDiscardData(). This is tracked ++** so that extra auto-merge work can be done by fts5IndexFlush() to ++** account for the delete operations. + */ + struct Fts5Index { + Fts5Config *pConfig; /* Virtual table configuration */ +@@ -216640,19 +244609,25 @@ struct Fts5Index { + int nPendingData; /* Current bytes of pending data */ + i64 iWriteRowid; /* Rowid for current doc being written */ + int bDelete; /* Current write is a delete */ ++ int nContentlessDelete; /* Number of contentless delete ops */ ++ int nPendingRow; /* Number of INSERT in hash table */ + + /* Error state. */ + int rc; /* Current error code */ ++ int flushRc; + + /* State used by the fts5DataXXX() functions. */ + sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ + sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ + sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ + sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ +- sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=? */ ++ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ + sqlite3_stmt *pIdxSelect; ++ sqlite3_stmt *pIdxNextSelect; + int nRead; /* Total number of blocks read */ + ++ sqlite3_stmt *pDeleteFromIdx; ++ + sqlite3_stmt *pDataVersion; + i64 iStructVersion; /* data_version when pStruct read */ + Fts5Structure *pStruct; /* Current db structure (or NULL) */ +@@ -216672,11 +244647,23 @@ struct Fts5DoclistIter { + ** The contents of the "structure" record for each index are represented + ** using an Fts5Structure record in memory. Which uses instances of the + ** other Fts5StructureXXX types as components. ++** ++** nOriginCntr: ++** This value is set to non-zero for structure records created for ++** contentlessdelete=1 tables only. In that case it represents the ++** origin value to apply to the next top-level segment created. + */ + struct Fts5StructureSegment { + int iSegid; /* Segment id */ + int pgnoFirst; /* First leaf page number in segment */ + int pgnoLast; /* Last leaf page number in segment */ ++ ++ /* contentlessdelete=1 tables only: */ ++ u64 iOrigin1; ++ u64 iOrigin2; ++ int nPgTombstone; /* Number of tombstone hash table pages */ ++ u64 nEntryTombstone; /* Number of tombstone entries that "count" */ ++ u64 nEntry; /* Number of rows in this segment */ + }; + struct Fts5StructureLevel { + int nMerge; /* Number of segments in incr-merge */ +@@ -216686,11 +244673,16 @@ struct Fts5StructureLevel { + struct Fts5Structure { + int nRef; /* Object reference count */ + u64 nWriteCounter; /* Total leaves written to level 0 */ ++ u64 nOriginCntr; /* Origin value for next top-level segment */ + int nSegment; /* Total segments in this structure */ + int nLevel; /* Number of levels in this index */ +- Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */ ++ Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */ + }; + ++/* Size (in bytes) of an Fts5Structure object holding up to N levels */ ++#define SZ_FTS5STRUCTURE(N) \ ++ (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel)) ++ + /* + ** An object of type Fts5SegWriter is used to write to segments. + */ +@@ -216745,9 +244737,6 @@ struct Fts5CResult { + ** iLeafOffset: + ** Byte offset within the current leaf that is the first byte of the + ** position list data (one byte passed the position-list size field). +-** rowid field of the current entry. Usually this is the size field of the +-** position list data. The exception is if the rowid for the current entry +-** is the last thing on the leaf page. + ** + ** pLeaf: + ** Buffer containing current leaf page data. Set to NULL at EOF. +@@ -216777,6 +244766,13 @@ struct Fts5CResult { + ** + ** iTermIdx: + ** Index of current term on iTermLeafPgno. ++** ++** apTombstone/nTombstone: ++** These are used for contentless_delete=1 tables only. When the cursor ++** is first allocated, the apTombstone[] array is allocated so that it ++** is large enough for all tombstones hash pages associated with the ++** segment. The pages themselves are loaded lazily from the database as ++** they are required. + */ + struct Fts5SegIter { + Fts5StructureSegment *pSeg; /* Segment to iterate through */ +@@ -216784,7 +244780,8 @@ struct Fts5SegIter { + int iLeafPgno; /* Current leaf page number */ + Fts5Data *pLeaf; /* Current leaf data */ + Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ +- int iLeafOffset; /* Byte offset within current leaf */ ++ i64 iLeafOffset; /* Byte offset within current leaf */ ++ Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */ + + /* Next method */ + void (*xNext)(Fts5Index*, Fts5SegIter*, int*); +@@ -216811,6 +244808,19 @@ struct Fts5SegIter { + u8 bDel; /* True if the delete flag is set */ + }; + ++/* ++** Array of tombstone pages. Reference counted. ++*/ ++struct Fts5TombstoneArray { ++ int nRef; /* Number of pointers to this object */ ++ int nTombstone; ++ Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */ ++}; ++ ++/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */ ++#define SZ_FTS5TOMBSTONEARRAY(N) \ ++ (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*)) ++ + /* + ** Argument is a pointer to an Fts5Data structure that contains a + ** leaf page. +@@ -216855,9 +244865,16 @@ struct Fts5SegIter { + ** poslist: + ** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered. + ** There is no way to tell if this is populated or not. ++** ++** pColset: ++** If not NULL, points to an object containing a set of column indices. ++** Only matches that occur in one of these columns will be returned. ++** The Fts5Iter does not own the Fts5Colset object, and so it is not ++** freed when the iterator is closed - it is owned by the upper layer. + */ + struct Fts5Iter { + Fts5IndexIter base; /* Base class containing output vars */ ++ Fts5TokenDataIter *pTokenDataIter; + + Fts5Index *pIndex; /* Index that owns this iterator */ + Fts5Buffer poslist; /* Buffer containing current poslist */ +@@ -216872,9 +244889,11 @@ struct Fts5Iter { + + i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ + Fts5CResult *aFirst; /* Current merge state (see above) */ +- Fts5SegIter aSeg[1]; /* Array of segment iterators */ ++ Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */ + }; + ++/* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */ ++#define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter)) + + /* + ** An instance of the following type is used to iterate through the contents +@@ -216902,9 +244921,13 @@ struct Fts5DlidxLvl { + struct Fts5DlidxIter { + int nLvl; + int iSegid; +- Fts5DlidxLvl aLvl[1]; ++ Fts5DlidxLvl aLvl[FLEXARRAY]; + }; + ++/* Size (in bytes) of an Fts5DlidxIter object with up to N levels */ ++#define SZ_FTS5DLIDXITER(N) \ ++ (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl)) ++ + static void fts5PutU16(u8 *aOut, u16 iVal){ + aOut[0] = (iVal>>8); + aOut[1] = (iVal&0xFF); +@@ -216914,6 +244937,60 @@ static u16 fts5GetU16(const u8 *aIn){ + return ((u16)aIn[0] << 8) + aIn[1]; + } + ++/* ++** The only argument points to a buffer at least 8 bytes in size. This ++** function interprets the first 8 bytes of the buffer as a 64-bit big-endian ++** unsigned integer and returns the result. ++*/ ++static u64 fts5GetU64(u8 *a){ ++ return ((u64)a[0] << 56) ++ + ((u64)a[1] << 48) ++ + ((u64)a[2] << 40) ++ + ((u64)a[3] << 32) ++ + ((u64)a[4] << 24) ++ + ((u64)a[5] << 16) ++ + ((u64)a[6] << 8) ++ + ((u64)a[7] << 0); ++} ++ ++/* ++** The only argument points to a buffer at least 4 bytes in size. This ++** function interprets the first 4 bytes of the buffer as a 32-bit big-endian ++** unsigned integer and returns the result. ++*/ ++static u32 fts5GetU32(const u8 *a){ ++ return ((u32)a[0] << 24) ++ + ((u32)a[1] << 16) ++ + ((u32)a[2] << 8) ++ + ((u32)a[3] << 0); ++} ++ ++/* ++** Write iVal, formated as a 64-bit big-endian unsigned integer, to the ++** buffer indicated by the first argument. ++*/ ++static void fts5PutU64(u8 *a, u64 iVal){ ++ a[0] = ((iVal >> 56) & 0xFF); ++ a[1] = ((iVal >> 48) & 0xFF); ++ a[2] = ((iVal >> 40) & 0xFF); ++ a[3] = ((iVal >> 32) & 0xFF); ++ a[4] = ((iVal >> 24) & 0xFF); ++ a[5] = ((iVal >> 16) & 0xFF); ++ a[6] = ((iVal >> 8) & 0xFF); ++ a[7] = ((iVal >> 0) & 0xFF); ++} ++ ++/* ++** Write iVal, formated as a 32-bit big-endian unsigned integer, to the ++** buffer indicated by the first argument. ++*/ ++static void fts5PutU32(u8 *a, u32 iVal){ ++ a[0] = ((iVal >> 24) & 0xFF); ++ a[1] = ((iVal >> 16) & 0xFF); ++ a[2] = ((iVal >> 8) & 0xFF); ++ a[3] = ((iVal >> 0) & 0xFF); ++} ++ + /* + ** Allocate and return a buffer at least nByte bytes in size. + ** +@@ -216953,8 +245030,11 @@ static int fts5BufferCompareBlob( + ** res = *pLeft - *pRight + */ + static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ +- int nCmp = MIN(pLeft->n, pRight->n); +- int res = fts5Memcmp(pLeft->p, pRight->p, nCmp); ++ int nCmp, res; ++ nCmp = MIN(pLeft->n, pRight->n); ++ assert( nCmp<=0 || pLeft->p!=0 ); ++ assert( nCmp<=0 || pRight->p!=0 ); ++ res = fts5Memcmp(pLeft->p, pRight->p, nCmp); + return (res==0 ? (pLeft->n - pRight->n) : res); + } + +@@ -216967,11 +245047,13 @@ static int fts5LeafFirstTermOff(Fts5Data *pLeaf){ + /* + ** Close the read-only blob handle, if it is open. + */ +-static void sqlite3Fts5IndexCloseReader(Fts5Index *p){ ++static void fts5IndexCloseReader(Fts5Index *p){ + if( p->pReader ){ ++ int rc; + sqlite3_blob *pReader = p->pReader; + p->pReader = 0; +- sqlite3_blob_close(pReader); ++ rc = sqlite3_blob_close(pReader); ++ if( p->rc==SQLITE_OK ) p->rc = rc; + } + } + +@@ -216996,7 +245078,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ + assert( p->pReader==0 ); + p->pReader = pBlob; + if( rc!=SQLITE_OK ){ +- sqlite3Fts5IndexCloseReader(p); ++ fts5IndexCloseReader(p); + } + if( rc==SQLITE_ABORT ) rc = SQLITE_OK; + } +@@ -217020,11 +245102,12 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ + if( rc==SQLITE_OK ){ + u8 *aOut = 0; /* Read blob data into this buffer */ + int nByte = sqlite3_blob_bytes(p->pReader); +- sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING; ++ int szData = (sizeof(Fts5Data) + 7) & ~7; ++ sqlite3_int64 nAlloc = szData + nByte + FTS5_DATA_PADDING; + pRet = (Fts5Data*)sqlite3_malloc64(nAlloc); + if( pRet ){ + pRet->nn = nByte; +- aOut = pRet->p = (u8*)&pRet[1]; ++ aOut = pRet->p = (u8*)pRet + szData; + }else{ + rc = SQLITE_NOMEM; + } +@@ -217047,9 +245130,11 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ + } + + assert( (pRet==0)==(p->rc!=SQLITE_OK) ); ++ assert( pRet==0 || EIGHT_BYTE_ALIGNMENT( pRet->p ) ); + return pRet; + } + ++ + /* + ** Release a reference to data record returned by an earlier call to + ** fts5DataRead(). +@@ -217077,9 +245162,13 @@ static int fts5IndexPrepareStmt( + ){ + if( p->rc==SQLITE_OK ){ + if( zSql ){ +- p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, ++ int rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, + SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB, + ppStmt, 0); ++ /* If this prepare() call fails with SQLITE_ERROR, then one of the ++ ** %_idx or %_data tables has been removed or modified. Call this ++ ** corruption. */ ++ p->rc = (rc==SQLITE_ERROR ? SQLITE_CORRUPT : rc); + }else{ + p->rc = SQLITE_NOMEM; + } +@@ -217137,10 +245226,17 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){ + /* + ** Remove all records associated with segment iSegid. + */ +-static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){ ++static void fts5DataRemoveSegment(Fts5Index *p, Fts5StructureSegment *pSeg){ ++ int iSegid = pSeg->iSegid; + i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0); + i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)-1; + fts5DataDelete(p, iFirst, iLast); ++ ++ if( pSeg->nPgTombstone ){ ++ i64 iTomb1 = FTS5_TOMBSTONE_ROWID(iSegid, 0); ++ i64 iTomb2 = FTS5_TOMBSTONE_ROWID(iSegid, pSeg->nPgTombstone-1); ++ fts5DataDelete(p, iTomb1, iTomb2); ++ } + if( p->pIdxDeleter==0 ){ + Fts5Config *pConfig = p->pConfig; + fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintf( +@@ -217174,6 +245270,58 @@ static void fts5StructureRef(Fts5Structure *pStruct){ + pStruct->nRef++; + } + ++static void *sqlite3Fts5StructureRef(Fts5Index *p){ ++ fts5StructureRef(p->pStruct); ++ return (void*)p->pStruct; ++} ++static void sqlite3Fts5StructureRelease(void *p){ ++ if( p ){ ++ fts5StructureRelease((Fts5Structure*)p); ++ } ++} ++static int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){ ++ if( p->pStruct!=(Fts5Structure*)pStruct ){ ++ return SQLITE_ABORT; ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Ensure that structure object (*pp) is writable. ++** ++** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If ++** an error occurs, (*pRc) is set to an SQLite error code before returning. ++*/ ++static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ ++ Fts5Structure *p = *pp; ++ if( *pRc==SQLITE_OK && p->nRef>1 ){ ++ i64 nByte = SZ_FTS5STRUCTURE(p->nLevel); ++ Fts5Structure *pNew; ++ pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); ++ if( pNew ){ ++ int i; ++ memcpy(pNew, p, nByte); ++ for(i=0; inLevel; i++) pNew->aLevel[i].aSeg = 0; ++ for(i=0; inLevel; i++){ ++ Fts5StructureLevel *pLvl = &pNew->aLevel[i]; ++ nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg; ++ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(pRc, nByte); ++ if( pLvl->aSeg==0 ){ ++ for(i=0; inLevel; i++){ ++ sqlite3_free(pNew->aLevel[i].aSeg); ++ } ++ sqlite3_free(pNew); ++ return; ++ } ++ memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte); ++ } ++ p->nRef--; ++ pNew->nRef = 1; ++ } ++ *pp = pNew; ++ } ++} ++ + /* + ** Deserialize and return the structure record currently stored in serialized + ** form within buffer pData/nData. +@@ -217199,11 +245347,19 @@ static int fts5StructureDecode( + int nSegment = 0; + sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */ + Fts5Structure *pRet = 0; /* Structure object to return */ ++ int bStructureV2 = 0; /* True for FTS5_STRUCTURE_V2 */ ++ u64 nOriginCntr = 0; /* Largest origin value seen so far */ + + /* Grab the cookie value */ + if( piCookie ) *piCookie = sqlite3Fts5Get32(pData); + i = 4; + ++ /* Check if this is a V2 structure record. Set bStructureV2 if it is. */ ++ if( 0==memcmp(&pData[i], FTS5_STRUCTURE_V2, 4) ){ ++ i += 4; ++ bStructureV2 = 1; ++ } ++ + /* Read the total number of levels and segments from the start of the + ** structure record. */ + i += fts5GetVarint32(&pData[i], nLevel); +@@ -217213,10 +245369,7 @@ static int fts5StructureDecode( + ){ + return FTS5_CORRUPT; + } +- nByte = ( +- sizeof(Fts5Structure) + /* Main structure */ +- sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ +- ); ++ nByte = SZ_FTS5STRUCTURE(nLevel); + pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); + + if( pRet ){ +@@ -217250,9 +245403,18 @@ static int fts5StructureDecode( + rc = FTS5_CORRUPT; + break; + } ++ assert( pSeg!=0 ); + i += fts5GetVarint32(&pData[i], pSeg->iSegid); + i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); + i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); ++ if( bStructureV2 ){ ++ i += fts5GetVarint(&pData[i], &pSeg->iOrigin1); ++ i += fts5GetVarint(&pData[i], &pSeg->iOrigin2); ++ i += fts5GetVarint32(&pData[i], pSeg->nPgTombstone); ++ i += fts5GetVarint(&pData[i], &pSeg->nEntryTombstone); ++ i += fts5GetVarint(&pData[i], &pSeg->nEntry); ++ nOriginCntr = MAX(nOriginCntr, pSeg->iOrigin2); ++ } + if( pSeg->pgnoLastpgnoFirst ){ + rc = FTS5_CORRUPT; + break; +@@ -217263,6 +245425,9 @@ static int fts5StructureDecode( + } + } + if( nSegment!=0 && rc==SQLITE_OK ) rc = FTS5_CORRUPT; ++ if( bStructureV2 ){ ++ pRet->nOriginCntr = nOriginCntr+1; ++ } + + if( rc!=SQLITE_OK ){ + fts5StructureRelease(pRet); +@@ -217275,16 +245440,16 @@ static int fts5StructureDecode( + } + + /* +-** ++** Add a level to the Fts5Structure.aLevel[] array of structure object ++** (*ppStruct). + */ + static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ ++ fts5StructureMakeWritable(pRc, ppStruct); ++ assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); + if( *pRc==SQLITE_OK ){ + Fts5Structure *pStruct = *ppStruct; + int nLevel = pStruct->nLevel; +- sqlite3_int64 nByte = ( +- sizeof(Fts5Structure) + /* Main structure */ +- sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ +- ); ++ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2); + + pStruct = sqlite3_realloc64(pStruct, nByte); + if( pStruct ){ +@@ -217472,6 +245637,7 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ + Fts5Buffer buf; /* Buffer to serialize record into */ + int iLvl; /* Used to iterate through levels */ + int iCookie; /* Cookie value to store */ ++ int nHdr = (pStruct->nOriginCntr>0 ? (4+4+9+9+9) : (4+9+9)); + + assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); + memset(&buf, 0, sizeof(Fts5Buffer)); +@@ -217480,9 +245646,12 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ + iCookie = p->pConfig->iCookie; + if( iCookie<0 ) iCookie = 0; + +- if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, 4+9+9+9) ){ ++ if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, nHdr) ){ + sqlite3Fts5Put32(buf.p, iCookie); + buf.n = 4; ++ if( pStruct->nOriginCntr>0 ){ ++ fts5BufferSafeAppendBlob(&buf, FTS5_STRUCTURE_V2, 4); ++ } + fts5BufferSafeAppendVarint(&buf, pStruct->nLevel); + fts5BufferSafeAppendVarint(&buf, pStruct->nSegment); + fts5BufferSafeAppendVarint(&buf, (i64)pStruct->nWriteCounter); +@@ -217496,9 +245665,17 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ + assert( pLvl->nMerge<=pLvl->nSeg ); + + for(iSeg=0; iSegnSeg; iSeg++){ +- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid); +- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst); +- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast); ++ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iSegid); ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoFirst); ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoLast); ++ if( pStruct->nOriginCntr>0 ){ ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin1); ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin2); ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nPgTombstone); ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntryTombstone); ++ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntry); ++ } + } + } + +@@ -217641,9 +245818,9 @@ static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){ + } + + if( iOffnn ){ +- i64 iVal; ++ u64 iVal; + pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1; +- iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal); ++ iOff += fts5GetVarint(&pData->p[iOff], &iVal); + pLvl->iRowid += iVal; + pLvl->iOff = iOff; + }else{ +@@ -217736,42 +245913,25 @@ static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){ + pLvl->bEof = 1; + }else{ + u8 *a = pLvl->pData->p; +- i64 iVal; +- int iLimit; +- int ii; +- int nZero = 0; +- +- /* Currently iOff points to the first byte of a varint. This block +- ** decrements iOff until it points to the first byte of the previous +- ** varint. Taking care not to read any memory locations that occur +- ** before the buffer in memory. */ +- iLimit = (iOff>9 ? iOff-9 : 0); +- for(iOff--; iOff>iLimit; iOff--){ +- if( (a[iOff-1] & 0x80)==0 ) break; +- } +- +- fts5GetVarint(&a[iOff], (u64*)&iVal); +- pLvl->iRowid -= iVal; +- pLvl->iLeafPgno--; +- +- /* Skip backwards past any 0x00 varints. */ +- for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){ +- nZero++; +- } +- if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){ +- /* The byte immediately before the last 0x00 byte has the 0x80 bit +- ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80 +- ** bytes before a[ii]. */ +- int bZero = 0; /* True if last 0x00 counts */ +- if( (ii-8)>=pLvl->iFirstOff ){ +- int j; +- for(j=1; j<=8 && (a[ii-j] & 0x80); j++); +- bZero = (j>8); ++ ++ pLvl->iOff = 0; ++ fts5DlidxLvlNext(pLvl); ++ while( 1 ){ ++ int nZero = 0; ++ int ii = pLvl->iOff; ++ u64 delta = 0; ++ ++ while( a[ii]==0 ){ ++ nZero++; ++ ii++; + } +- if( bZero==0 ) nZero--; ++ ii += sqlite3Fts5GetVarint(&a[ii], &delta); ++ ++ if( ii>=iOff ) break; ++ pLvl->iLeafPgno += nZero+1; ++ pLvl->iRowid += delta; ++ pLvl->iOff = ii; + } +- pLvl->iLeafPgno -= nZero; +- pLvl->iOff = iOff - nZero; + } + + return pLvl->bEof; +@@ -217828,7 +245988,7 @@ static Fts5DlidxIter *fts5DlidxIterInit( + int bDone = 0; + + for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ +- sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); ++ sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1); + Fts5DlidxIter *pNew; + + pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); +@@ -217964,10 +246124,10 @@ static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ + + static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ + u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ +- int iOff = pIter->iLeafOffset; ++ i64 iOff = pIter->iLeafOffset; + + ASSERT_SZLEAF_OK(pIter->pLeaf); +- if( iOff>=pIter->pLeaf->szLeaf ){ ++ while( iOff>=pIter->pLeaf->szLeaf ){ + fts5SegIterNextPage(p, pIter); + if( pIter->pLeaf==0 ){ + if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; +@@ -217997,7 +246157,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ + */ + static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ + u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ +- int iOff = pIter->iLeafOffset; /* Offset to read at */ ++ i64 iOff = pIter->iLeafOffset; /* Offset to read at */ + int nNew; /* Bytes of new data */ + + iOff += fts5GetVarint32(&a[iOff], nNew); +@@ -218038,6 +246198,25 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ + } + } + ++/* ++** Allocate a tombstone hash page array object (pIter->pTombArray) for ++** the iterator passed as the second argument. If an OOM error occurs, ++** leave an error in the Fts5Index object. ++*/ ++static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ ++ const i64 nTomb = (i64)pIter->pSeg->nPgTombstone; ++ if( nTomb>0 ){ ++ i64 nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1); ++ Fts5TombstoneArray *pNew; ++ pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); ++ if( pNew ){ ++ pNew->nTombstone = nTomb; ++ pNew->nRef = 1; ++ pIter->pTombArray = pNew; ++ } ++ } ++} ++ + /* + ** Initialize the iterator object pIter to iterate through the entries in + ** segment pSeg. The iterator is left pointing to the first entry when +@@ -218066,16 +246245,20 @@ static void fts5SegIterInit( + fts5SegIterSetNext(p, pIter); + pIter->pSeg = pSeg; + pIter->iLeafPgno = pSeg->pgnoFirst-1; +- fts5SegIterNextPage(p, pIter); ++ do { ++ fts5SegIterNextPage(p, pIter); ++ }while( p->rc==SQLITE_OK && pIter->pLeaf && pIter->pLeaf->nn==4 ); + } + +- if( p->rc==SQLITE_OK ){ ++ if( p->rc==SQLITE_OK && pIter->pLeaf ){ + pIter->iLeafOffset = 4; ++ assert( pIter->pLeaf!=0 ); + assert_nc( pIter->pLeaf->nn>4 ); + assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); + pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; + fts5SegIterLoadTerm(p, pIter, 0); + fts5SegIterLoadNPos(p, pIter); ++ fts5SegIterAllocTombstone(p, pIter); + } + } + +@@ -218107,7 +246290,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ + + ASSERT_SZLEAF_OK(pIter->pLeaf); + while( 1 ){ +- i64 iDelta = 0; ++ u64 iDelta = 0; + + if( eDetail==FTS5_DETAIL_NONE ){ + /* todo */ +@@ -218122,7 +246305,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ + i += nPos; + } + if( i>=n ) break; +- i += fts5GetVarint(&a[i], (u64*)&iDelta); ++ i += fts5GetVarint(&a[i], &iDelta); + pIter->iRowid += iDelta; + + /* If necessary, grow the pIter->aRowidOffset[] array. */ +@@ -218173,8 +246356,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ + int iRowidOff; + iRowidOff = fts5LeafFirstRowidOff(pNew); + if( iRowidOff ){ +- pIter->pLeaf = pNew; +- pIter->iLeafOffset = iRowidOff; ++ if( iRowidOff>=pNew->szLeaf ){ ++ p->rc = FTS5_CORRUPT; ++ }else{ ++ pIter->pLeaf = pNew; ++ pIter->iLeafOffset = iRowidOff; ++ } + } + } + +@@ -218221,7 +246408,7 @@ static void fts5SegIterNext_Reverse( + if( pIter->iRowidOffset>0 ){ + u8 *a = pIter->pLeaf->p; + int iOff; +- i64 iDelta; ++ u64 iDelta; + + pIter->iRowidOffset--; + pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset]; +@@ -218230,7 +246417,7 @@ static void fts5SegIterNext_Reverse( + if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){ + iOff += pIter->nPos; + } +- fts5GetVarint(&a[iOff], (u64*)&iDelta); ++ fts5GetVarint(&a[iOff], &iDelta); + pIter->iRowid -= iDelta; + }else{ + fts5SegIterReverseNewPage(p, pIter); +@@ -218258,7 +246445,7 @@ static void fts5SegIterNext_None( + iOff = pIter->iLeafOffset; + + /* Next entry is on the next page */ +- if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ ++ while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ + fts5SegIterNextPage(p, pIter); + if( p->rc || pIter->pLeaf==0 ) return; + pIter->iRowid = 0; +@@ -218267,7 +246454,7 @@ static void fts5SegIterNext_None( + + if( iOffiEndofDoclist ){ + /* Next entry is on the current page */ +- i64 iDelta; ++ u64 iDelta; + iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta); + pIter->iLeafOffset = iOff; + pIter->iRowid += iDelta; +@@ -218282,15 +246469,16 @@ static void fts5SegIterNext_None( + }else{ + const u8 *pList = 0; + const char *zTerm = 0; ++ int nTerm = 0; + int nList; + sqlite3Fts5HashScanNext(p->pHash); +- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); ++ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); + if( pList==0 ) goto next_none_eof; + pIter->pLeaf->p = (u8*)pList; + pIter->pLeaf->nn = nList; + pIter->pLeaf->szLeaf = nList; + pIter->iEndofDoclist = nList; +- sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm); ++ sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm); + pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); + } + +@@ -218356,11 +246544,12 @@ static void fts5SegIterNext( + }else if( pIter->pSeg==0 ){ + const u8 *pList = 0; + const char *zTerm = 0; ++ int nTerm = 0; + int nList = 0; + assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); + if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ + sqlite3Fts5HashScanNext(p->pHash); +- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); ++ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); + } + if( pList==0 ){ + fts5DataRelease(pIter->pLeaf); +@@ -218370,8 +246559,7 @@ static void fts5SegIterNext( + pIter->pLeaf->nn = nList; + pIter->pLeaf->szLeaf = nList; + pIter->iEndofDoclist = nList+1; +- sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), +- (u8*)zTerm); ++ sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm); + pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); + *pbNewTerm = 1; + } +@@ -218423,14 +246611,9 @@ static void fts5SegIterNext( + }else{ + /* The following could be done by calling fts5SegIterLoadNPos(). But + ** this block is particularly performance critical, so equivalent +- ** code is inlined. +- ** +- ** Later: Switched back to fts5SegIterLoadNPos() because it supports +- ** detail=none mode. Not ideal. +- */ ++ ** code is inlined. */ + int nSz; +- assert( p->rc==SQLITE_OK ); +- assert( pIter->iLeafOffset<=pIter->pLeaf->nn ); ++ assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn ); + fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); + pIter->bDel = (nSz & 0x0001); + pIter->nPos = nSz>>1; +@@ -218456,10 +246639,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ + Fts5Data *pLast = 0; + int pgnoLast = 0; + +- if( pDlidx ){ ++ if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){ + int iSegid = pIter->pSeg->iSegid; + pgnoLast = fts5DlidxIterPgno(pDlidx); +- pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); ++ pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); + }else{ + Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ + +@@ -218486,7 +246669,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ + ** forward to find the page containing the last rowid. */ + for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){ + i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); +- Fts5Data *pNew = fts5DataRead(p, iAbs); ++ Fts5Data *pNew = fts5LeafRead(p, iAbs); + if( pNew ){ + int iRowid, bTermless; + iRowid = fts5LeafFirstRowidOff(pNew); +@@ -218517,6 +246700,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ + pIter->pLeaf = pLast; + pIter->iLeafPgno = pgnoLast; + iOff = fts5LeafFirstRowidOff(pLast); ++ if( iOff>pLast->szLeaf ){ ++ p->rc = FTS5_CORRUPT; ++ return; ++ } + iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); + pIter->iLeafOffset = iOff; + +@@ -218525,7 +246712,6 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ + }else{ + pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast); + } +- + } + + fts5SegIterReverseInitPage(p, pIter); +@@ -218577,21 +246763,20 @@ static void fts5LeafSeek( + Fts5SegIter *pIter, /* Iterator to seek */ + const u8 *pTerm, int nTerm /* Term to search for */ + ){ +- int iOff; ++ u32 iOff; + const u8 *a = pIter->pLeaf->p; +- int szLeaf = pIter->pLeaf->szLeaf; +- int n = pIter->pLeaf->nn; ++ u32 n = (u32)pIter->pLeaf->nn; + + u32 nMatch = 0; + u32 nKeep = 0; + u32 nNew = 0; + u32 iTermOff; +- int iPgidx; /* Current offset in pgidx */ ++ u32 iPgidx; /* Current offset in pgidx */ + int bEndOfPage = 0; + + assert( p->rc==SQLITE_OK ); + +- iPgidx = szLeaf; ++ iPgidx = (u32)pIter->pLeaf->szLeaf; + iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff); + iOff = iTermOff; + if( iOff>n ){ +@@ -218657,15 +246842,15 @@ static void fts5LeafSeek( + if( pIter->pLeaf==0 ) return; + a = pIter->pLeaf->p; + if( fts5LeafIsTermless(pIter->pLeaf)==0 ){ +- iPgidx = pIter->pLeaf->szLeaf; ++ iPgidx = (u32)pIter->pLeaf->szLeaf; + iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff); +- if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){ ++ if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){ + p->rc = FTS5_CORRUPT; + return; + }else{ + nKeep = 0; + iTermOff = iOff; +- n = pIter->pLeaf->nn; ++ n = (u32)pIter->pLeaf->nn; + iOff += fts5GetVarint32(&a[iOff], nNew); + break; + } +@@ -218760,7 +246945,7 @@ static void fts5SegIterSeekInit( + fts5LeafSeek(p, bGe, pIter, pTerm, nTerm); + } + +- if( p->rc==SQLITE_OK && bGe==0 ){ ++ if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){ + pIter->flags |= FTS5_SEGITER_ONETERM; + if( pIter->pLeaf ){ + if( flags & FTS5INDEX_QUERY_DESC ){ +@@ -218776,6 +246961,9 @@ static void fts5SegIterSeekInit( + } + + fts5SegIterSetNext(p, pIter); ++ if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){ ++ fts5SegIterAllocTombstone(p, pIter); ++ } + + /* Either: + ** +@@ -218792,6 +246980,79 @@ static void fts5SegIterSeekInit( + ); + } + ++ ++/* ++** SQL used by fts5SegIterNextInit() to find the page to open. ++*/ ++static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){ ++ if( p->pIdxNextSelect==0 ){ ++ Fts5Config *pConfig = p->pConfig; ++ fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf( ++ "SELECT pgno FROM '%q'.'%q_idx' WHERE " ++ "segid=? AND term>? ORDER BY term ASC LIMIT 1", ++ pConfig->zDb, pConfig->zName ++ )); ++ ++ } ++ return p->pIdxNextSelect; ++} ++ ++/* ++** This is similar to fts5SegIterSeekInit(), except that it initializes ++** the segment iterator to point to the first term following the page ++** with pToken/nToken on it. ++*/ ++static void fts5SegIterNextInit( ++ Fts5Index *p, ++ const char *pTerm, int nTerm, ++ Fts5StructureSegment *pSeg, /* Description of segment */ ++ Fts5SegIter *pIter /* Object to populate */ ++){ ++ int iPg = -1; /* Page of segment to open */ ++ int bDlidx = 0; ++ sqlite3_stmt *pSel = 0; /* SELECT to find iPg */ ++ ++ pSel = fts5IdxNextStmt(p); ++ if( pSel ){ ++ assert( p->rc==SQLITE_OK ); ++ sqlite3_bind_int(pSel, 1, pSeg->iSegid); ++ sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC); ++ ++ if( sqlite3_step(pSel)==SQLITE_ROW ){ ++ i64 val = sqlite3_column_int64(pSel, 0); ++ iPg = (int)(val>>1); ++ bDlidx = (val & 0x0001); ++ } ++ p->rc = sqlite3_reset(pSel); ++ sqlite3_bind_null(pSel, 2); ++ if( p->rc ) return; ++ } ++ ++ memset(pIter, 0, sizeof(*pIter)); ++ pIter->pSeg = pSeg; ++ pIter->flags |= FTS5_SEGITER_ONETERM; ++ if( iPg>=0 ){ ++ pIter->iLeafPgno = iPg - 1; ++ fts5SegIterNextPage(p, pIter); ++ fts5SegIterSetNext(p, pIter); ++ } ++ if( pIter->pLeaf ){ ++ const u8 *a = pIter->pLeaf->p; ++ int iTermOff = 0; ++ ++ pIter->iPgidxOff = pIter->pLeaf->szLeaf; ++ pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff); ++ pIter->iLeafOffset = iTermOff; ++ fts5SegIterLoadTerm(p, pIter, 0); ++ fts5SegIterLoadNPos(p, pIter); ++ if( bDlidx ) fts5SegIterLoadDlidx(p, pIter); ++ ++ assert( p->rc!=SQLITE_OK || ++ fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0 ++ ); ++ } ++} ++ + /* + ** Initialize the object pIter to point to term pTerm/nTerm within the + ** in-memory hash table. If there is no such term in the hash-table, the +@@ -218818,14 +247079,21 @@ static void fts5SegIterHashInit( + const u8 *pList = 0; + + p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); +- sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList); +- n = (z ? (int)strlen((const char*)z) : 0); ++ sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList); + if( pList ){ + pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); + if( pLeaf ){ + pLeaf->p = (u8*)pList; + } + } ++ ++ /* The call to sqlite3Fts5HashScanInit() causes the hash table to ++ ** fill the size field of all existing position lists. This means they ++ ** can no longer be appended to. Since the only scenario in which they ++ ** can be appended to is if the previous operation on this table was ++ ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this ++ ** possibility altogether. */ ++ p->bDelete = 0; + }else{ + p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data), + (const char*)pTerm, nTerm, (void**)&pLeaf, &nList +@@ -218856,6 +247124,37 @@ static void fts5SegIterHashInit( + fts5SegIterSetNext(p, pIter); + } + ++/* ++** Array ap[] contains n elements. Release each of these elements using ++** fts5DataRelease(). Then free the array itself using sqlite3_free(). ++*/ ++static void fts5IndexFreeArray(Fts5Data **ap, int n){ ++ if( ap ){ ++ int ii; ++ for(ii=0; iinRef--; ++ if( p->nRef<=0 ){ ++ int ii; ++ for(ii=0; iinTombstone; ii++){ ++ fts5DataRelease(p->apTombstone[ii]); ++ } ++ sqlite3_free(p); ++ } ++ } ++} ++ + /* + ** Zero the iterator passed as the only argument. + */ +@@ -218863,6 +247162,7 @@ static void fts5SegIterClear(Fts5SegIter *pIter){ + fts5BufferFree(&pIter->term); + fts5DataRelease(pIter->pLeaf); + fts5DataRelease(pIter->pNextLeaf); ++ fts5TombstoneArrayDelete(pIter->pTombArray); + fts5DlidxIterFree(pIter->pDlidx); + sqlite3_free(pIter->aRowidOffset); + memset(pIter, 0, sizeof(Fts5SegIter)); +@@ -218996,7 +247296,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ + assert_nc( i2!=0 ); + pRes->bTermEq = 1; + if( p1->iRowid==p2->iRowid ){ +- p1->bDel = p2->bDel; + return i2; + } + res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1; +@@ -219015,7 +247314,8 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ + + /* + ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. +-** It is an error if leaf iLeafPgno does not exist or contains no rowids. ++** It is an error if leaf iLeafPgno does not exist. Unless the db is ++** a 'secure-delete' db, if it contains no rowids then this is also an error. + */ + static void fts5SegIterGotoPage( + Fts5Index *p, /* FTS5 backend object */ +@@ -219030,21 +247330,23 @@ static void fts5SegIterGotoPage( + fts5DataRelease(pIter->pNextLeaf); + pIter->pNextLeaf = 0; + pIter->iLeafPgno = iLeafPgno-1; +- fts5SegIterNextPage(p, pIter); +- assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); + +- if( p->rc==SQLITE_OK ){ ++ while( p->rc==SQLITE_OK ){ + int iOff; +- u8 *a = pIter->pLeaf->p; +- int n = pIter->pLeaf->szLeaf; +- ++ fts5SegIterNextPage(p, pIter); ++ if( pIter->pLeaf==0 ) break; + iOff = fts5LeafFirstRowidOff(pIter->pLeaf); +- if( iOff<4 || iOff>=n ){ +- p->rc = FTS5_CORRUPT; +- }else{ +- iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); +- pIter->iLeafOffset = iOff; +- fts5SegIterLoadNPos(p, pIter); ++ if( iOff>0 ){ ++ u8 *a = pIter->pLeaf->p; ++ int n = pIter->pLeaf->szLeaf; ++ if( iOff<4 || iOff>=n ){ ++ p->rc = FTS5_CORRUPT; ++ }else{ ++ iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); ++ pIter->iLeafOffset = iOff; ++ fts5SegIterLoadNPos(p, pIter); ++ } ++ break; + } + } + } +@@ -219105,7 +247407,6 @@ static void fts5SegIterNextFrom( + }while( p->rc==SQLITE_OK ); + } + +- + /* + ** Free the iterator object passed as the second argument. + */ +@@ -219197,6 +247498,85 @@ static void fts5MultiIterSetEof(Fts5Iter *pIter){ + pIter->iSwitchRowid = pSeg->iRowid; + } + ++/* ++** The argument to this macro must be an Fts5Data structure containing a ++** tombstone hash page. This macro returns the key-size of the hash-page. ++*/ ++#define TOMBSTONE_KEYSIZE(pPg) (pPg->p[0]==4 ? 4 : 8) ++ ++#define TOMBSTONE_NSLOT(pPg) \ ++ ((pPg->nn > 16) ? ((pPg->nn-8) / TOMBSTONE_KEYSIZE(pPg)) : 1) ++ ++/* ++** Query a single tombstone hash table for rowid iRowid. Return true if ++** it is found or false otherwise. The tombstone hash table is one of ++** nHashTable tables. ++*/ ++static int fts5IndexTombstoneQuery( ++ Fts5Data *pHash, /* Hash table page to query */ ++ int nHashTable, /* Number of pages attached to segment */ ++ u64 iRowid /* Rowid to query hash for */ ++){ ++ const int szKey = TOMBSTONE_KEYSIZE(pHash); ++ const int nSlot = TOMBSTONE_NSLOT(pHash); ++ int iSlot = (iRowid / nHashTable) % nSlot; ++ int nCollide = nSlot; ++ ++ if( iRowid==0 ){ ++ return pHash->p[1]; ++ }else if( szKey==4 ){ ++ u32 *aSlot = (u32*)&pHash->p[8]; ++ while( aSlot[iSlot] ){ ++ if( fts5GetU32((u8*)&aSlot[iSlot])==iRowid ) return 1; ++ if( nCollide--==0 ) break; ++ iSlot = (iSlot+1)%nSlot; ++ } ++ }else{ ++ u64 *aSlot = (u64*)&pHash->p[8]; ++ while( aSlot[iSlot] ){ ++ if( fts5GetU64((u8*)&aSlot[iSlot])==iRowid ) return 1; ++ if( nCollide--==0 ) break; ++ iSlot = (iSlot+1)%nSlot; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++** Return true if the iterator passed as the only argument points ++** to an segment entry for which there is a tombstone. Return false ++** if there is no tombstone or if the iterator is already at EOF. ++*/ ++static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ ++ int iFirst = pIter->aFirst[1].iFirst; ++ Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; ++ Fts5TombstoneArray *pArray = pSeg->pTombArray; ++ ++ if( pSeg->pLeaf && pArray ){ ++ /* Figure out which page the rowid might be present on. */ ++ int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone; ++ assert( iPg>=0 ); ++ ++ /* If tombstone hash page iPg has not yet been loaded from the ++ ** database, load it now. */ ++ if( pArray->apTombstone[iPg]==0 ){ ++ pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex, ++ FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg) ++ ); ++ if( pArray->apTombstone[iPg]==0 ) return 0; ++ } ++ ++ return fts5IndexTombstoneQuery( ++ pArray->apTombstone[iPg], ++ pArray->nTombstone, ++ pSeg->iRowid ++ ); ++ } ++ ++ return 0; ++} ++ + /* + ** Move the iterator to the next entry. + ** +@@ -219234,7 +247614,9 @@ static void fts5MultiIterNext( + + fts5AssertMultiIterSetup(p, pIter); + assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf ); +- if( pIter->bSkipEmpty==0 || pSeg->nPos ){ ++ if( (pIter->bSkipEmpty==0 || pSeg->nPos) ++ && 0==fts5MultiIterIsDeleted(pIter) ++ ){ + pIter->xSetOutputs(pIter, pSeg); + return; + } +@@ -219266,7 +247648,9 @@ static void fts5MultiIterNext2( + } + fts5AssertMultiIterSetup(p, pIter); + +- }while( fts5MultiIterIsEmpty(p, pIter) ); ++ }while( (fts5MultiIterIsEmpty(p, pIter) || fts5MultiIterIsDeleted(pIter)) ++ && (p->rc==SQLITE_OK) ++ ); + } + } + +@@ -219279,12 +247663,11 @@ static Fts5Iter *fts5MultiIterAlloc( + int nSeg + ){ + Fts5Iter *pNew; +- int nSlot; /* Power of two >= nSeg */ ++ i64 nSlot; /* Power of two >= nSeg */ + + for(nSlot=2; nSlotaSeg[] */ ++ SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */ + sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ + ); + if( pNew ){ +@@ -219422,7 +247805,7 @@ static void fts5ChunkIterate( + int pgno = pSeg->iLeafPgno; + int pgnoSave = 0; + +- /* This function does notmwork with detail=none databases. */ ++ /* This function does not work with detail=none databases. */ + assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE ); + + if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){ +@@ -219435,6 +247818,9 @@ static void fts5ChunkIterate( + fts5DataRelease(pData); + if( nRem<=0 ){ + break; ++ }else if( pSeg->pSeg==0 ){ ++ p->rc = FTS5_CORRUPT; ++ return; + }else{ + pgno++; + pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno)); +@@ -219462,7 +247848,11 @@ static void fts5SegiterPoslist( + Fts5Colset *pColset, + Fts5Buffer *pBuf + ){ ++ assert( pBuf!=0 ); ++ assert( pSeg!=0 ); + if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){ ++ assert( pBuf->p!=0 ); ++ assert( pBuf->nSpace >= pBuf->n+pSeg->nPos+FTS5_DATA_ZERO_PADDING ); + memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING); + if( pColset==0 ){ + fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); +@@ -219486,66 +247876,72 @@ static void fts5SegiterPoslist( + } + + /* +-** IN/OUT parameter (*pa) points to a position list n bytes in size. If +-** the position list contains entries for column iCol, then (*pa) is set +-** to point to the sub-position-list for that column and the number of +-** bytes in it returned. Or, if the argument position list does not +-** contain any entries for column iCol, return 0. ++** Parameter pPos points to a buffer containing a position list, size nPos. ++** This function filters it according to pColset (which must be non-NULL) ++** and sets pIter->base.pData/nData to point to the new position list. ++** If memory is required for the new position list, use buffer pIter->poslist. ++** Or, if the new position list is a contiguous subset of the input, set ++** pIter->base.pData/nData to point directly to it. ++** ++** This function is a no-op if *pRc is other than SQLITE_OK when it is ++** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM ++** before returning. + */ +-static int fts5IndexExtractCol( +- const u8 **pa, /* IN/OUT: Pointer to poslist */ +- int n, /* IN: Size of poslist in bytes */ +- int iCol /* Column to extract from poslist */ +-){ +- int iCurrent = 0; /* Anything before the first 0x01 is col 0 */ +- const u8 *p = *pa; +- const u8 *pEnd = &p[n]; /* One byte past end of position list */ +- +- while( iCol>iCurrent ){ +- /* Advance pointer p until it points to pEnd or an 0x01 byte that is +- ** not part of a varint. Note that it is not possible for a negative +- ** or extremely large varint to occur within an uncorrupted position +- ** list. So the last byte of each varint may be assumed to have a clear +- ** 0x80 bit. */ +- while( *p!=0x01 ){ +- while( *p++ & 0x80 ); +- if( p>=pEnd ) return 0; +- } +- *pa = p++; +- iCurrent = *p++; +- if( iCurrent & 0x80 ){ +- p--; +- p += fts5GetVarint32(p, iCurrent); +- } +- } +- if( iCol!=iCurrent ) return 0; +- +- /* Advance pointer p until it points to pEnd or an 0x01 byte that is +- ** not part of a varint */ +- while( pnCol; i++){ +- const u8 *pSub = pPos; +- int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]); +- if( nSub ){ +- fts5BufferAppendBlob(pRc, pBuf, nSub, pSub); ++ const u8 *p = pPos; ++ const u8 *aCopy = p; ++ const u8 *pEnd = &p[nPos]; /* One byte past end of position list */ ++ int i = 0; ++ int iCurrent = 0; ++ ++ if( pColset->nCol>1 && sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){ ++ return; ++ } ++ ++ while( 1 ){ ++ while( pColset->aiCol[i]nCol ){ ++ pIter->base.pData = pIter->poslist.p; ++ pIter->base.nData = pIter->poslist.n; ++ return; ++ } ++ } ++ ++ /* Advance pointer p until it points to pEnd or an 0x01 byte that is ++ ** not part of a varint */ ++ while( paiCol[i]==iCurrent ){ ++ if( pColset->nCol==1 ){ ++ pIter->base.pData = aCopy; ++ pIter->base.nData = p-aCopy; ++ return; ++ } ++ fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy); ++ } ++ if( p>=pEnd ){ ++ pIter->base.pData = pIter->poslist.p; ++ pIter->base.nData = pIter->poslist.n; ++ return; ++ } ++ aCopy = p++; ++ iCurrent = *p++; ++ if( iCurrent & 0x80 ){ ++ p--; ++ p += fts5GetVarint32(p, iCurrent); + } + } + } ++ + } + + /* +@@ -219665,16 +248061,9 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ + /* All data is stored on the current page. Populate the output + ** variables to point into the body of the page object. */ + const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset]; +- if( pColset->nCol==1 ){ +- pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]); +- pIter->base.pData = a; +- }else{ +- int *pRc = &pIter->pIndex->rc; +- fts5BufferZero(&pIter->poslist); +- fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist); +- pIter->base.pData = pIter->poslist.p; +- pIter->base.nData = pIter->poslist.n; +- } ++ int *pRc = &pIter->pIndex->rc; ++ fts5BufferZero(&pIter->poslist); ++ fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter); + }else{ + /* The data is distributed over two or more pages. Copy it into the + ** Fts5Iter.poslist buffer and then set the output pointer to point +@@ -219687,6 +248076,7 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ + } + + static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ ++ assert( pIter!=0 || (*pRc)!=SQLITE_OK ); + if( *pRc==SQLITE_OK ){ + Fts5Config *pConfig = pIter->pIndex->pConfig; + if( pConfig->eDetail==FTS5_DETAIL_NONE ){ +@@ -219717,6 +248107,32 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ + } + } + ++/* ++** All the component segment-iterators of pIter have been set up. This ++** functions finishes setup for iterator pIter itself. ++*/ ++static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){ ++ int iIter; ++ for(iIter=pIter->nSeg-1; iIter>0; iIter--){ ++ int iEq; ++ if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){ ++ Fts5SegIter *pSeg = &pIter->aSeg[iEq]; ++ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); ++ fts5MultiIterAdvanced(p, pIter, iEq, iIter); ++ } ++ } ++ fts5MultiIterSetEof(pIter); ++ fts5AssertMultiIterSetup(p, pIter); ++ ++ if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter)) ++ || fts5MultiIterIsDeleted(pIter) ++ ){ ++ fts5MultiIterNext(p, pIter, 0, 0); ++ }else if( pIter->base.bEof==0 ){ ++ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; ++ pIter->xSetOutputs(pIter, pSeg); ++ } ++} + + /* + ** Allocate a new Fts5Iter object. +@@ -219752,13 +248168,16 @@ static void fts5MultiIterNew( + if( iLevel<0 ){ + assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); + nSeg = pStruct->nSegment; +- nSeg += (p->pHash ? 1 : 0); ++ nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH)); + }else{ + nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); + } + } + *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); +- if( pNew==0 ) return; ++ if( pNew==0 ){ ++ assert( p->rc!=SQLITE_OK ); ++ goto fts5MultiIterNew_post_check; ++ } + pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC)); + pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); + pNew->pColset = pColset; +@@ -219770,7 +248189,7 @@ static void fts5MultiIterNew( + if( p->rc==SQLITE_OK ){ + if( iLevel<0 ){ + Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; +- if( p->pHash ){ ++ if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){ + /* Add a segment iterator for the current contents of the hash table. */ + Fts5SegIter *pIter = &pNew->aSeg[iIter++]; + fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); +@@ -219795,33 +248214,20 @@ static void fts5MultiIterNew( + assert( iIter==nSeg ); + } + +- /* If the above was successful, each component iterators now points ++ /* If the above was successful, each component iterator now points + ** to the first entry in its segment. In this case initialize the + ** aFirst[] array. Or, if an error has occurred, free the iterator + ** object and set the output variable to NULL. */ + if( p->rc==SQLITE_OK ){ +- for(iIter=pNew->nSeg-1; iIter>0; iIter--){ +- int iEq; +- if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ +- Fts5SegIter *pSeg = &pNew->aSeg[iEq]; +- if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); +- fts5MultiIterAdvanced(p, pNew, iEq, iIter); +- } +- } +- fts5MultiIterSetEof(pNew); +- fts5AssertMultiIterSetup(p, pNew); +- +- if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){ +- fts5MultiIterNext(p, pNew, 0, 0); +- }else if( pNew->base.bEof==0 ){ +- Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst]; +- pNew->xSetOutputs(pNew, pSeg); +- } +- ++ fts5MultiIterFinishSetup(p, pNew); + }else{ + fts5MultiIterFree(pNew); + *ppOut = 0; + } ++ ++fts5MultiIterNew_post_check: ++ assert( (*ppOut)!=0 || p->rc!=SQLITE_OK ); ++ return; + } + + /* +@@ -219838,7 +248244,6 @@ static void fts5MultiIterNew2( + pNew = fts5MultiIterAlloc(p, 2); + if( pNew ){ + Fts5SegIter *pIter = &pNew->aSeg[1]; +- + pIter->flags = FTS5_SEGITER_ONETERM; + if( pData->szLeaf>0 ){ + pIter->pLeaf = pData; +@@ -219869,7 +248274,8 @@ static void fts5MultiIterNew2( + ** False otherwise. + */ + static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){ +- assert( p->rc ++ assert( pIter!=0 || p->rc!=SQLITE_OK ); ++ assert( p->rc!=SQLITE_OK + || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof + ); + return (p->rc || pIter->base.bEof); +@@ -219984,7 +248390,10 @@ static void fts5IndexDiscardData(Fts5Index *p){ + if( p->pHash ){ + sqlite3Fts5HashClear(p->pHash); + p->nPendingData = 0; ++ p->nPendingRow = 0; ++ p->flushRc = SQLITE_OK; + } ++ p->nContentlessDelete = 0; + } + + /* +@@ -220198,7 +248607,7 @@ static void fts5WriteDlidxAppend( + } + + if( pDlidx->bPrevValid ){ +- iVal = iRowid - pDlidx->iPrev; ++ iVal = (u64)iRowid - (u64)pDlidx->iPrev; + }else{ + i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno); + assert( pDlidx->buf.n==0 ); +@@ -220365,7 +248774,9 @@ static void fts5WriteAppendRowid( + fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid); + }else{ + assert_nc( p->rc || iRowid>pWriter->iPrevRowid ); +- fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); ++ fts5BufferAppendVarint(&p->rc, &pPage->buf, ++ (u64)iRowid - (u64)pWriter->iPrevRowid ++ ); + } + pWriter->iPrevRowid = iRowid; + pWriter->bFirstRowidInDoclist = 0; +@@ -220383,7 +248794,7 @@ static void fts5WriteAppendPoslistData( + const u8 *a = aData; + int n = nData; + +- assert( p->pConfig->pgsz>0 ); ++ assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK ); + while( p->rc==SQLITE_OK + && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz + ){ +@@ -220518,7 +248929,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ + fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); + fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); +- fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff,&pData->p[iOff]); ++ fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff]); + if( p->rc==SQLITE_OK ){ + /* Set the szLeaf field */ + fts5PutU16(&buf.p[2], (u16)buf.n); +@@ -220619,6 +249030,12 @@ static void fts5IndexMergeLevel( + + /* Read input from all segments in the input level */ + nInput = pLvl->nSeg; ++ ++ /* Set the range of origins that will go into the output segment. */ ++ if( pStruct->nOriginCntr>0 ){ ++ pSeg->iOrigin1 = pLvl->aSeg[0].iOrigin1; ++ pSeg->iOrigin2 = pLvl->aSeg[pLvl->nSeg-1].iOrigin2; ++ } + } + bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2); + +@@ -220673,12 +249090,16 @@ static void fts5IndexMergeLevel( + ** and last leaf page number at the same time. */ + fts5WriteFinish(p, &writer, &pSeg->pgnoLast); + ++ assert( pIter!=0 || p->rc!=SQLITE_OK ); + if( fts5MultiIterEof(p, pIter) ){ + int i; + + /* Remove the redundant segments from the %_data table */ ++ assert( pSeg->nEntry==0 ); + for(i=0; iaSeg[i].iSegid); ++ Fts5StructureSegment *pOld = &pLvl->aSeg[i]; ++ pSeg->nEntry += (pOld->nEntry - pOld->nEntryTombstone); ++ fts5DataRemoveSegment(p, pOld); + } + + /* Remove the redundant segments from the input level */ +@@ -220704,6 +249125,48 @@ static void fts5IndexMergeLevel( + if( pnRem ) *pnRem -= writer.nLeafWritten; + } + ++/* ++** If this is not a contentless_delete=1 table, or if the 'deletemerge' ++** configuration option is set to 0, then this function always returns -1. ++** Otherwise, it searches the structure object passed as the second argument ++** for a level suitable for merging due to having a large number of ++** tombstones in the tombstone hash. If one is found, its index is returned. ++** Otherwise, if there is no suitable level, -1. ++*/ ++static int fts5IndexFindDeleteMerge(Fts5Index *p, Fts5Structure *pStruct){ ++ Fts5Config *pConfig = p->pConfig; ++ int iRet = -1; ++ if( pConfig->bContentlessDelete && pConfig->nDeleteMerge>0 ){ ++ int ii; ++ int nBest = 0; ++ ++ for(ii=0; iinLevel; ii++){ ++ Fts5StructureLevel *pLvl = &pStruct->aLevel[ii]; ++ i64 nEntry = 0; ++ i64 nTomb = 0; ++ int iSeg; ++ for(iSeg=0; iSegnSeg; iSeg++){ ++ nEntry += pLvl->aSeg[iSeg].nEntry; ++ nTomb += pLvl->aSeg[iSeg].nEntryTombstone; ++ } ++ assert_nc( nEntry>0 || pLvl->nSeg==0 ); ++ if( nEntry>0 ){ ++ int nPercent = (nTomb * 100) / nEntry; ++ if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){ ++ iRet = ii; ++ nBest = nPercent; ++ } ++ } ++ ++ /* If pLvl is already the input level to an ongoing merge, look no ++ ** further for a merge candidate. The caller should be allowed to ++ ** continue merging from pLvl first. */ ++ if( pLvl->nMerge ) break; ++ } ++ } ++ return iRet; ++} ++ + /* + ** Do up to nPg pages of automerge work on the index. + ** +@@ -220723,14 +249186,15 @@ static int fts5IndexMerge( + int iBestLvl = 0; /* Level offering the most input segments */ + int nBest = 0; /* Number of input segments on best level */ + +- /* Set iBestLvl to the level to read input segments from. */ ++ /* Set iBestLvl to the level to read input segments from. Or to -1 if ++ ** there is no level suitable to merge segments from. */ + assert( pStruct->nLevel>0 ); + for(iLvl=0; iLvlnLevel; iLvl++){ + Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; + if( pLvl->nMerge ){ + if( pLvl->nMerge>nBest ){ + iBestLvl = iLvl; +- nBest = pLvl->nMerge; ++ nBest = nMin; + } + break; + } +@@ -220739,22 +249203,18 @@ static int fts5IndexMerge( + iBestLvl = iLvl; + } + } +- +- /* If nBest is still 0, then the index must be empty. */ +-#ifdef SQLITE_DEBUG +- for(iLvl=0; nBest==0 && iLvlnLevel; iLvl++){ +- assert( pStruct->aLevel[iLvl].nSeg==0 ); ++ if( nBestaLevel[iBestLvl].nMerge==0 ){ +- break; +- } ++ if( iBestLvl<0 ) break; + bRet = 1; + fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem); + if( p->rc==SQLITE_OK && pStruct->aLevel[iBestLvl].nMerge==0 ){ + fts5StructurePromote(p, iBestLvl+1, pStruct); + } ++ ++ if( nMin==1 ) nMin = 2; + } + *ppStruct = pStruct; + return bRet; +@@ -220773,7 +249233,7 @@ static void fts5IndexAutomerge( + Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ + int nLeaf /* Number of output leaves just written */ + ){ +- if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 ){ ++ if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 && ALWAYS((*ppStruct)!=0) ){ + Fts5Structure *pStruct = *ppStruct; + u64 nWrite; /* Initial value of write-counter */ + int nWork; /* Number of work-quanta to perform */ +@@ -220795,16 +249255,16 @@ static void fts5IndexCrisismerge( + ){ + const int nCrisis = p->pConfig->nCrisisMerge; + Fts5Structure *pStruct = *ppStruct; +- int iLvl = 0; +- +- assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 ); +- while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ +- fts5IndexMergeLevel(p, &pStruct, iLvl, 0); +- assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); +- fts5StructurePromote(p, iLvl+1, pStruct); +- iLvl++; ++ if( pStruct && pStruct->nLevel>0 ){ ++ int iLvl = 0; ++ while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ ++ fts5IndexMergeLevel(p, &pStruct, iLvl, 0); ++ assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); ++ fts5StructurePromote(p, iLvl+1, pStruct); ++ iLvl++; ++ } ++ *ppStruct = pStruct; + } +- *ppStruct = pStruct; + } + + static int fts5IndexReturn(Fts5Index *p){ +@@ -220813,6 +249273,14 @@ static int fts5IndexReturn(Fts5Index *p){ + return rc; + } + ++/* ++** Close the read-only blob handle, if it is open. ++*/ ++static void sqlite3Fts5IndexCloseReader(Fts5Index *p){ ++ fts5IndexCloseReader(p); ++ fts5IndexReturn(p); ++} ++ + typedef struct Fts5FlushCtx Fts5FlushCtx; + struct Fts5FlushCtx { + Fts5Index *pIdx; +@@ -220838,6 +249306,491 @@ static int fts5PoslistPrefix(const u8 *aBuf, int nMax){ + return ret; + } + ++/* ++** Execute the SQL statement: ++** ++** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno); ++** ++** This is used when a secure-delete operation removes the last term ++** from a segment leaf page. In that case the %_idx entry is removed ++** too. This is done to ensure that if all instances of a token are ++** removed from an fts5 database in secure-delete mode, no trace of ++** the token itself remains in the database. ++*/ ++static void fts5SecureDeleteIdxEntry( ++ Fts5Index *p, /* FTS5 backend object */ ++ int iSegid, /* Id of segment to delete entry for */ ++ int iPgno /* Page number within segment */ ++){ ++ if( iPgno!=1 ){ ++ assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE ); ++ if( p->pDeleteFromIdx==0 ){ ++ fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintf( ++ "DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)", ++ p->pConfig->zDb, p->pConfig->zName ++ )); ++ } ++ if( p->rc==SQLITE_OK ){ ++ sqlite3_bind_int(p->pDeleteFromIdx, 1, iSegid); ++ sqlite3_bind_int(p->pDeleteFromIdx, 2, iPgno); ++ sqlite3_step(p->pDeleteFromIdx); ++ p->rc = sqlite3_reset(p->pDeleteFromIdx); ++ } ++ } ++} ++ ++/* ++** This is called when a secure-delete operation removes a position-list ++** that overflows onto segment page iPgno of segment pSeg. This function ++** rewrites node iPgno, and possibly one or more of its right-hand peers, ++** to remove this portion of the position list. ++** ++** Output variable (*pbLastInDoclist) is set to true if the position-list ++** removed is followed by a new term or the end-of-segment, or false if ++** it is followed by another rowid/position list. ++*/ ++static void fts5SecureDeleteOverflow( ++ Fts5Index *p, ++ Fts5StructureSegment *pSeg, ++ int iPgno, ++ int *pbLastInDoclist ++){ ++ const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); ++ int pgno; ++ Fts5Data *pLeaf = 0; ++ assert( iPgno!=1 ); ++ ++ *pbLastInDoclist = 1; ++ for(pgno=iPgno; p->rc==SQLITE_OK && pgno<=pSeg->pgnoLast; pgno++){ ++ i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); ++ int iNext = 0; ++ u8 *aPg = 0; ++ ++ pLeaf = fts5DataRead(p, iRowid); ++ if( pLeaf==0 ) break; ++ aPg = pLeaf->p; ++ ++ iNext = fts5GetU16(&aPg[0]); ++ if( iNext!=0 ){ ++ *pbLastInDoclist = 0; ++ } ++ if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ ++ fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext); ++ } ++ ++ if( iNext==0 ){ ++ /* The page contains no terms or rowids. Replace it with an empty ++ ** page and move on to the right-hand peer. */ ++ const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04}; ++ assert_nc( bDetailNone==0 || pLeaf->nn==4 ); ++ if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty)); ++ fts5DataRelease(pLeaf); ++ pLeaf = 0; ++ }else if( bDetailNone ){ ++ break; ++ }else if( iNext>=pLeaf->szLeaf || pLeaf->nnszLeaf || iNext<4 ){ ++ p->rc = FTS5_CORRUPT; ++ break; ++ }else{ ++ int nShift = iNext - 4; ++ int nPg; ++ ++ int nIdx = 0; ++ u8 *aIdx = 0; ++ ++ /* Unless the current page footer is 0 bytes in size (in which case ++ ** the new page footer will be as well), allocate and populate a ++ ** buffer containing the new page footer. Set stack variables aIdx ++ ** and nIdx accordingly. */ ++ if( pLeaf->nn>pLeaf->szLeaf ){ ++ int iFirst = 0; ++ int i1 = pLeaf->szLeaf; ++ int i2 = 0; ++ ++ i1 += fts5GetVarint32(&aPg[i1], iFirst); ++ if( iFirstrc = FTS5_CORRUPT; ++ break; ++ } ++ aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2); ++ if( aIdx==0 ) break; ++ i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift); ++ if( i1nn ){ ++ memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1); ++ i2 += (pLeaf->nn-i1); ++ } ++ nIdx = i2; ++ } ++ ++ /* Modify the contents of buffer aPg[]. Set nPg to the new size ++ ** in bytes. The new page is always smaller than the old. */ ++ nPg = pLeaf->szLeaf - nShift; ++ memmove(&aPg[4], &aPg[4+nShift], nPg-4); ++ fts5PutU16(&aPg[2], nPg); ++ if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4); ++ if( nIdx>0 ){ ++ memcpy(&aPg[nPg], aIdx, nIdx); ++ nPg += nIdx; ++ } ++ sqlite3_free(aIdx); ++ ++ /* Write the new page to disk and exit the loop */ ++ assert( nPg>4 || fts5GetU16(aPg)==0 ); ++ fts5DataWrite(p, iRowid, aPg, nPg); ++ break; ++ } ++ } ++ fts5DataRelease(pLeaf); ++} ++ ++/* ++** Completely remove the entry that pSeg currently points to from ++** the database. ++*/ ++static void fts5DoSecureDelete( ++ Fts5Index *p, ++ Fts5SegIter *pSeg ++){ ++ const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); ++ int iSegid = pSeg->pSeg->iSegid; ++ u8 *aPg = pSeg->pLeaf->p; ++ int nPg = pSeg->pLeaf->nn; ++ int iPgIdx = pSeg->pLeaf->szLeaf; ++ ++ u64 iDelta = 0; ++ int iNextOff = 0; ++ int iOff = 0; ++ int nIdx = 0; ++ u8 *aIdx = 0; ++ int bLastInDoclist = 0; ++ int iIdx = 0; ++ int iStart = 0; ++ int iDelKeyOff = 0; /* Offset of deleted key, if any */ ++ ++ nIdx = nPg-iPgIdx; ++ aIdx = sqlite3Fts5MallocZero(&p->rc, ((i64)nIdx)+16); ++ if( p->rc ) return; ++ memcpy(aIdx, &aPg[iPgIdx], nIdx); ++ ++ /* At this point segment iterator pSeg points to the entry ++ ** this function should remove from the b-tree segment. ++ ** ++ ** In detail=full or detail=column mode, pSeg->iLeafOffset is the ++ ** offset of the first byte in the position-list for the entry to ++ ** remove. Immediately before this comes two varints that will also ++ ** need to be removed: ++ ** ++ ** + the rowid or delta rowid value for the entry, and ++ ** + the size of the position list in bytes. ++ ** ++ ** Or, in detail=none mode, there is a single varint prior to ++ ** pSeg->iLeafOffset - the rowid or delta rowid value. ++ ** ++ ** This block sets the following variables: ++ ** ++ ** iStart: ++ ** The offset of the first byte of the rowid or delta-rowid ++ ** value for the doclist entry being removed. ++ ** ++ ** iDelta: ++ ** The value of the rowid or delta-rowid value for the doclist ++ ** entry being removed. ++ ** ++ ** iNextOff: ++ ** The offset of the next entry following the position list ++ ** for the one being removed. If the position list for this ++ ** entry overflows onto the next leaf page, this value will be ++ ** greater than pLeaf->szLeaf. ++ */ ++ { ++ int iSOP; /* Start-Of-Position-list */ ++ if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){ ++ iStart = pSeg->iTermLeafOffset; ++ }else{ ++ iStart = fts5GetU16(&aPg[0]); ++ } ++ ++ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); ++ assert_nc( iSOP<=pSeg->iLeafOffset ); ++ ++ if( bDetailNone ){ ++ while( iSOPiLeafOffset ){ ++ if( aPg[iSOP]==0x00 ) iSOP++; ++ if( aPg[iSOP]==0x00 ) iSOP++; ++ iStart = iSOP; ++ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); ++ } ++ ++ iNextOff = iSOP; ++ if( iNextOffiEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; ++ if( iNextOffiEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; ++ ++ }else{ ++ int nPos = 0; ++ iSOP += fts5GetVarint32(&aPg[iSOP], nPos); ++ while( iSOPiLeafOffset ){ ++ iStart = iSOP + (nPos/2); ++ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); ++ iSOP += fts5GetVarint32(&aPg[iSOP], nPos); ++ } ++ assert_nc( iSOP==pSeg->iLeafOffset ); ++ iNextOff = pSeg->iLeafOffset + pSeg->nPos; ++ } ++ } ++ ++ iOff = iStart; ++ ++ /* If the position-list for the entry being removed flows over past ++ ** the end of this page, delete the portion of the position-list on the ++ ** next page and beyond. ++ ** ++ ** Set variable bLastInDoclist to true if this entry happens ++ ** to be the last rowid in the doclist for its term. */ ++ if( iNextOff>=iPgIdx ){ ++ int pgno = pSeg->iLeafPgno+1; ++ fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); ++ iNextOff = iPgIdx; ++ } ++ ++ if( pSeg->bDel==0 ){ ++ if( iNextOff!=iPgIdx ){ ++ /* Loop through the page-footer. If iNextOff (offset of the ++ ** entry following the one we are removing) is equal to the ++ ** offset of a key on this page, then the entry is the last ++ ** in its doclist. */ ++ int iKeyOff = 0; ++ for(iIdx=0; iIdxbDel ){ ++ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta); ++ aPg[iOff++] = 0x01; ++ }else if( bLastInDoclist==0 ){ ++ if( iNextOff!=iPgIdx ){ ++ u64 iNextDelta = 0; ++ iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta); ++ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta); ++ } ++ }else if( ++ pSeg->iLeafPgno==pSeg->iTermLeafPgno ++ && iStart==pSeg->iTermLeafOffset ++ ){ ++ /* The entry being removed was the only position list in its ++ ** doclist. Therefore the term needs to be removed as well. */ ++ int iKey = 0; ++ int iKeyOff = 0; ++ ++ /* Set iKeyOff to the offset of the term that will be removed - the ++ ** last offset in the footer that is not greater than iStart. */ ++ for(iIdx=0; iIdx(u32)iStart ) break; ++ iKeyOff += iVal; ++ } ++ assert_nc( iKey>=1 ); ++ ++ /* Set iDelKeyOff to the value of the footer entry to remove from ++ ** the page. */ ++ iDelKeyOff = iOff = iKeyOff; ++ ++ if( iNextOff!=iPgIdx ){ ++ /* This is the only position-list associated with the term, and there ++ ** is another term following it on this page. So the subsequent term ++ ** needs to be moved to replace the term associated with the entry ++ ** being removed. */ ++ int nPrefix = 0; ++ int nSuffix = 0; ++ int nPrefix2 = 0; ++ int nSuffix2 = 0; ++ ++ iDelKeyOff = iNextOff; ++ iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); ++ iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); ++ ++ if( iKey!=1 ){ ++ iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); ++ } ++ iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); ++ ++ nPrefix = MIN(nPrefix, nPrefix2); ++ nSuffix = (nPrefix2 + nSuffix2) - nPrefix; ++ ++ if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ ++ p->rc = FTS5_CORRUPT; ++ }else{ ++ if( iKey!=1 ){ ++ iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); ++ } ++ iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); ++ if( nPrefix2>pSeg->term.n ){ ++ p->rc = FTS5_CORRUPT; ++ }else if( nPrefix2>nPrefix ){ ++ memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); ++ iOff += (nPrefix2-nPrefix); ++ } ++ memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2); ++ iOff += nSuffix2; ++ iNextOff += nSuffix2; ++ } ++ } ++ }else if( iStart==4 ){ ++ int iPgno; ++ ++ assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno ); ++ /* The entry being removed may be the only position list in ++ ** its doclist. */ ++ for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ ++ Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); ++ int bEmpty = (pPg && pPg->nn==4); ++ fts5DataRelease(pPg); ++ if( bEmpty==0 ) break; ++ } ++ ++ if( iPgno==pSeg->iTermLeafPgno ){ ++ i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); ++ Fts5Data *pTerm = fts5DataRead(p, iId); ++ if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ ++ u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; ++ int nTermIdx = pTerm->nn - pTerm->szLeaf; ++ int iTermIdx = 0; ++ int iTermOff = 0; ++ ++ while( 1 ){ ++ u32 iVal = 0; ++ int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); ++ iTermOff += iVal; ++ if( (iTermIdx+nByte)>=nTermIdx ) break; ++ iTermIdx += nByte; ++ } ++ nTermIdx = iTermIdx; ++ ++ memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); ++ fts5PutU16(&pTerm->p[2], iTermOff); ++ ++ fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); ++ if( nTermIdx==0 ){ ++ fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); ++ } ++ } ++ fts5DataRelease(pTerm); ++ } ++ } ++ ++ /* Assuming no error has occurred, this block does final edits to the ++ ** leaf page before writing it back to disk. Input variables are: ++ ** ++ ** nPg: Total initial size of leaf page. ++ ** iPgIdx: Initial offset of page footer. ++ ** ++ ** iOff: Offset to move data to ++ ** iNextOff: Offset to move data from ++ */ ++ if( p->rc==SQLITE_OK ){ ++ const int nMove = nPg - iNextOff; /* Number of bytes to move */ ++ int nShift = iNextOff - iOff; /* Distance to move them */ ++ ++ int iPrevKeyOut = 0; ++ int iKeyIn = 0; ++ ++ memmove(&aPg[iOff], &aPg[iNextOff], nMove); ++ iPgIdx -= nShift; ++ nPg = iPgIdx; ++ fts5PutU16(&aPg[2], iPgIdx); ++ ++ for(iIdx=0; iIdxiOff ? nShift : 0)); ++ nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOut - iPrevKeyOut); ++ iPrevKeyOut = iKeyOut; ++ } ++ } ++ ++ if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ ++ fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); ++ } ++ ++ assert_nc( nPg>4 || fts5GetU16(aPg)==0 ); ++ fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg, nPg); ++ } ++ sqlite3_free(aIdx); ++} ++ ++/* ++** This is called as part of flushing a delete to disk in 'secure-delete' ++** mode. It edits the segments within the database described by argument ++** pStruct to remove the entries for term zTerm, rowid iRowid. ++** ++** Return SQLITE_OK if successful, or an SQLite error code if an error ++** has occurred. Any error code is also stored in the Fts5Index handle. ++*/ ++static int fts5FlushSecureDelete( ++ Fts5Index *p, ++ Fts5Structure *pStruct, ++ const char *zTerm, ++ int nTerm, ++ i64 iRowid ++){ ++ const int f = FTS5INDEX_QUERY_SKIPHASH; ++ Fts5Iter *pIter = 0; /* Used to find term instance */ ++ ++ /* If the version number has not been set to SECUREDELETE, do so now. */ ++ if( p->pConfig->iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ){ ++ Fts5Config *pConfig = p->pConfig; ++ sqlite3_stmt *pStmt = 0; ++ fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf( ++ "REPLACE INTO %Q.'%q_config' VALUES ('version', %d)", ++ pConfig->zDb, pConfig->zName, FTS5_CURRENT_VERSION_SECUREDELETE ++ )); ++ if( p->rc==SQLITE_OK ){ ++ int rc; ++ sqlite3_step(pStmt); ++ rc = sqlite3_finalize(pStmt); ++ if( p->rc==SQLITE_OK ) p->rc = rc; ++ pConfig->iCookie++; ++ pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE; ++ } ++ } ++ ++ fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); ++ if( fts5MultiIterEof(p, pIter)==0 ){ ++ i64 iThis = fts5MultiIterRowid(pIter); ++ if( iThisrc==SQLITE_OK ++ && fts5MultiIterEof(p, pIter)==0 ++ && iRowid==fts5MultiIterRowid(pIter) ++ ){ ++ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; ++ fts5DoSecureDelete(p, pSeg); ++ } ++ } ++ ++ fts5MultiIterFree(pIter); ++ return p->rc; ++} ++ ++ + /* + ** Flush the contents of in-memory hash table iHash to a new level-0 + ** segment on disk. Also update the corresponding structure record. +@@ -220854,143 +249807,199 @@ static void fts5FlushOneHash(Fts5Index *p){ + /* Obtain a reference to the index structure and allocate a new segment-id + ** for the new level-0 segment. */ + pStruct = fts5StructureRead(p); +- iSegid = fts5AllocateSegid(p, pStruct); + fts5StructureInvalidate(p); + +- if( iSegid ){ +- const int pgsz = p->pConfig->pgsz; +- int eDetail = p->pConfig->eDetail; +- Fts5StructureSegment *pSeg; /* New segment within pStruct */ +- Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ +- Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ ++ if( sqlite3Fts5HashIsEmpty(pHash)==0 ){ ++ iSegid = fts5AllocateSegid(p, pStruct); ++ if( iSegid ){ ++ const int pgsz = p->pConfig->pgsz; ++ int eDetail = p->pConfig->eDetail; ++ int bSecureDelete = p->pConfig->bSecureDelete; ++ Fts5StructureSegment *pSeg; /* New segment within pStruct */ ++ Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ ++ Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ ++ ++ Fts5SegWriter writer; ++ fts5WriteInit(p, &writer, iSegid); ++ ++ pBuf = &writer.writer.buf; ++ pPgidx = &writer.writer.pgidx; ++ ++ /* fts5WriteInit() should have initialized the buffers to (most likely) ++ ** the maximum space required. */ ++ assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) ); ++ assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) ); ++ ++ /* Begin scanning through hash table entries. This loop runs once for each ++ ** term/doclist currently stored within the hash table. */ ++ if( p->rc==SQLITE_OK ){ ++ p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); ++ } ++ while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ ++ const char *zTerm; /* Buffer containing term */ ++ int nTerm; /* Size of zTerm in bytes */ ++ const u8 *pDoclist; /* Pointer to doclist for this term */ ++ int nDoclist; /* Size of doclist in bytes */ + +- Fts5SegWriter writer; +- fts5WriteInit(p, &writer, iSegid); ++ /* Get the term and doclist for this entry. */ ++ sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist); ++ if( bSecureDelete==0 ){ ++ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); ++ if( p->rc!=SQLITE_OK ) break; ++ assert( writer.bFirstRowidInPage==0 ); ++ } + +- pBuf = &writer.writer.buf; +- pPgidx = &writer.writer.pgidx; ++ if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ ++ /* The entire doclist will fit on the current leaf. */ ++ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); ++ }else{ ++ int bTermWritten = !bSecureDelete; ++ i64 iRowid = 0; ++ i64 iPrev = 0; ++ int iOff = 0; ++ ++ /* The entire doclist will not fit on this leaf. The following ++ ** loop iterates through the poslists that make up the current ++ ** doclist. */ ++ while( p->rc==SQLITE_OK && iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ ++ iOff++; ++ continue; ++ } ++ } ++ } + +- /* fts5WriteInit() should have initialized the buffers to (most likely) +- ** the maximum space required. */ +- assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) ); +- assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) ); ++ if( p->rc==SQLITE_OK && bTermWritten==0 ){ ++ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); ++ bTermWritten = 1; ++ assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 ); ++ } + +- /* Begin scanning through hash table entries. This loop runs once for each +- ** term/doclist currently stored within the hash table. */ +- if( p->rc==SQLITE_OK ){ +- p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); +- } +- while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ +- const char *zTerm; /* Buffer containing term */ +- const u8 *pDoclist; /* Pointer to doclist for this term */ +- int nDoclist; /* Size of doclist in bytes */ +- +- /* Write the term for this entry to disk. */ +- sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); +- fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm); +- if( p->rc!=SQLITE_OK ) break; +- +- assert( writer.bFirstRowidInPage==0 ); +- if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ +- /* The entire doclist will fit on the current leaf. */ +- fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); +- }else{ +- i64 iRowid = 0; +- i64 iDelta = 0; +- int iOff = 0; +- +- /* The entire doclist will not fit on this leaf. The following +- ** loop iterates through the poslists that make up the current +- ** doclist. */ +- while( p->rc==SQLITE_OK && iOffp[0], (u16)pBuf->n); /* first rowid on page */ +- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); +- writer.bFirstRowidInPage = 0; +- fts5WriteDlidxAppend(p, &writer, iRowid); ++ if( writer.bFirstRowidInPage ){ ++ fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ ++ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); ++ writer.bFirstRowidInPage = 0; ++ fts5WriteDlidxAppend(p, &writer, iRowid); ++ }else{ ++ u64 iRowidDelta = (u64)iRowid - (u64)iPrev; ++ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowidDelta); ++ } + if( p->rc!=SQLITE_OK ) break; +- }else{ +- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); +- } +- assert( pBuf->n<=pBuf->nSpace ); ++ assert( pBuf->n<=pBuf->nSpace ); ++ iPrev = iRowid; + +- if( eDetail==FTS5_DETAIL_NONE ){ +- if( iOffp[pBuf->n++] = 0; +- iOff++; ++ if( eDetail==FTS5_DETAIL_NONE ){ + if( iOffp[pBuf->n++] = 0; + iOff++; ++ if( iOffp[pBuf->n++] = 0; ++ iOff++; ++ } ++ } ++ if( (pBuf->n + pPgidx->n)>=pgsz ){ ++ fts5WriteFlushLeaf(p, &writer); + } +- } +- if( (pBuf->n + pPgidx->n)>=pgsz ){ +- fts5WriteFlushLeaf(p, &writer); +- } +- }else{ +- int bDummy; +- int nPos; +- int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); +- nCopy += nPos; +- if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ +- /* The entire poslist will fit on the current leaf. So copy +- ** it in one go. */ +- fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); + }else{ +- /* The entire poslist will not fit on this leaf. So it needs +- ** to be broken into sections. The only qualification being +- ** that each varint must be stored contiguously. */ +- const u8 *pPoslist = &pDoclist[iOff]; +- int iPos = 0; +- while( p->rc==SQLITE_OK ){ +- int nSpace = pgsz - pBuf->n - pPgidx->n; +- int n = 0; +- if( (nCopy - iPos)<=nSpace ){ +- n = nCopy - iPos; +- }else{ +- n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); +- } +- assert( n>0 ); +- fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); +- iPos += n; +- if( (pBuf->n + pPgidx->n)>=pgsz ){ +- fts5WriteFlushLeaf(p, &writer); ++ int bDel = 0; ++ int nPos = 0; ++ int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel); ++ if( bDel && bSecureDelete ){ ++ fts5BufferAppendVarint(&p->rc, pBuf, nPos*2); ++ iOff += nCopy; ++ nCopy = nPos; ++ }else{ ++ nCopy += nPos; ++ } ++ if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ ++ /* The entire poslist will fit on the current leaf. So copy ++ ** it in one go. */ ++ fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); ++ }else{ ++ /* The entire poslist will not fit on this leaf. So it needs ++ ** to be broken into sections. The only qualification being ++ ** that each varint must be stored contiguously. */ ++ const u8 *pPoslist = &pDoclist[iOff]; ++ int iPos = 0; ++ while( p->rc==SQLITE_OK ){ ++ int nSpace = pgsz - pBuf->n - pPgidx->n; ++ int n = 0; ++ if( (nCopy - iPos)<=nSpace ){ ++ n = nCopy - iPos; ++ }else{ ++ n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); ++ } ++ assert( n>0 ); ++ fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); ++ iPos += n; ++ if( (pBuf->n + pPgidx->n)>=pgsz ){ ++ fts5WriteFlushLeaf(p, &writer); ++ } ++ if( iPos>=nCopy ) break; + } +- if( iPos>=nCopy ) break; + } ++ iOff += nCopy; + } +- iOff += nCopy; + } + } +- } + +- /* TODO2: Doclist terminator written here. */ +- /* pBuf->p[pBuf->n++] = '\0'; */ +- assert( pBuf->n<=pBuf->nSpace ); +- if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); +- } +- sqlite3Fts5HashClear(pHash); +- fts5WriteFinish(p, &writer, &pgnoLast); ++ /* TODO2: Doclist terminator written here. */ ++ /* pBuf->p[pBuf->n++] = '\0'; */ ++ assert( pBuf->n<=pBuf->nSpace ); ++ if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); ++ } ++ fts5WriteFinish(p, &writer, &pgnoLast); + +- /* Update the Fts5Structure. It is written back to the database by the +- ** fts5StructureRelease() call below. */ +- if( pStruct->nLevel==0 ){ +- fts5StructureAddLevel(&p->rc, &pStruct); +- } +- fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); +- if( p->rc==SQLITE_OK ){ +- pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; +- pSeg->iSegid = iSegid; +- pSeg->pgnoFirst = 1; +- pSeg->pgnoLast = pgnoLast; +- pStruct->nSegment++; ++ assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); ++ if( pgnoLast>0 ){ ++ /* Update the Fts5Structure. It is written back to the database by the ++ ** fts5StructureRelease() call below. */ ++ if( pStruct->nLevel==0 ){ ++ fts5StructureAddLevel(&p->rc, &pStruct); ++ } ++ fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); ++ if( p->rc==SQLITE_OK ){ ++ pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; ++ pSeg->iSegid = iSegid; ++ pSeg->pgnoFirst = 1; ++ pSeg->pgnoLast = pgnoLast; ++ if( pStruct->nOriginCntr>0 ){ ++ pSeg->iOrigin1 = pStruct->nOriginCntr; ++ pSeg->iOrigin2 = pStruct->nOriginCntr; ++ pSeg->nEntry = p->nPendingRow; ++ pStruct->nOriginCntr++; ++ } ++ pStruct->nSegment++; ++ } ++ fts5StructurePromote(p, 0, pStruct); ++ } + } +- fts5StructurePromote(p, 0, pStruct); + } + +- fts5IndexAutomerge(p, &pStruct, pgnoLast); ++ fts5IndexAutomerge(p, &pStruct, pgnoLast + p->nContentlessDelete); + fts5IndexCrisismerge(p, &pStruct); + fts5StructureWrite(p, pStruct); + fts5StructureRelease(pStruct); +@@ -221001,10 +250010,21 @@ static void fts5FlushOneHash(Fts5Index *p){ + */ + static void fts5IndexFlush(Fts5Index *p){ + /* Unless it is empty, flush the hash table to disk */ +- if( p->nPendingData ){ ++ if( p->flushRc ){ ++ p->rc = p->flushRc; ++ return; ++ } ++ if( p->nPendingData || p->nContentlessDelete ){ + assert( p->pHash ); +- p->nPendingData = 0; + fts5FlushOneHash(p); ++ if( p->rc==SQLITE_OK ){ ++ sqlite3Fts5HashClear(p->pHash); ++ p->nPendingData = 0; ++ p->nPendingRow = 0; ++ p->nContentlessDelete = 0; ++ }else if( p->nPendingData || p->nContentlessDelete ){ ++ p->flushRc = p->rc; ++ } + } + } + +@@ -221013,40 +250033,47 @@ static Fts5Structure *fts5IndexOptimizeStruct( + Fts5Structure *pStruct + ){ + Fts5Structure *pNew = 0; +- sqlite3_int64 nByte = sizeof(Fts5Structure); ++ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1); + int nSeg = pStruct->nSegment; + int i; + + /* Figure out if this structure requires optimization. A structure does + ** not require optimization if either: + ** +- ** + it consists of fewer than two segments, or +- ** + all segments are on the same level, or +- ** + all segments except one are currently inputs to a merge operation. ++ ** 1. it consists of fewer than two segments, or ++ ** 2. all segments are on the same level, or ++ ** 3. all segments except one are currently inputs to a merge operation. + ** +- ** In the first case, return NULL. In the second, increment the ref-count +- ** on *pStruct and return a copy of the pointer to it. ++ ** In the first case, if there are no tombstone hash pages, return NULL. In ++ ** the second, increment the ref-count on *pStruct and return a copy of the ++ ** pointer to it. + */ +- if( nSeg<2 ) return 0; ++ if( nSeg==0 ) return 0; + for(i=0; inLevel; i++){ + int nThis = pStruct->aLevel[i].nSeg; +- if( nThis==nSeg || (nThis==nSeg-1 && pStruct->aLevel[i].nMerge==nThis) ){ ++ int nMerge = pStruct->aLevel[i].nMerge; ++ if( nThis>0 && (nThis==nSeg || (nThis==nSeg-1 && nMerge==nThis)) ){ ++ if( nSeg==1 && nThis==1 && pStruct->aLevel[i].aSeg[0].nPgTombstone==0 ){ ++ return 0; ++ } + fts5StructureRef(pStruct); + return pStruct; + } + assert( pStruct->aLevel[i].nMerge<=nThis ); + } + +- nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel); ++ nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); ++ assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); + pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); + + if( pNew ){ + Fts5StructureLevel *pLvl; + nByte = nSeg * sizeof(Fts5StructureSegment); +- pNew->nLevel = pStruct->nLevel+1; ++ pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL); + pNew->nRef = 1; + pNew->nWriteCounter = pStruct->nWriteCounter; +- pLvl = &pNew->aLevel[pStruct->nLevel]; ++ pNew->nOriginCntr = pStruct->nOriginCntr; ++ pLvl = &pNew->aLevel[pNew->nLevel-1]; + pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte); + if( pLvl->aSeg ){ + int iLvl, iSeg; +@@ -221076,7 +250103,9 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){ + + assert( p->rc==SQLITE_OK ); + fts5IndexFlush(p); ++ assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 ); + pStruct = fts5StructureRead(p); ++ assert( p->rc!=SQLITE_OK || pStruct!=0 ); + fts5StructureInvalidate(p); + + if( pStruct ){ +@@ -221105,7 +250134,10 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){ + ** INSERT command. + */ + static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ +- Fts5Structure *pStruct = fts5StructureRead(p); ++ Fts5Structure *pStruct = 0; ++ ++ fts5IndexFlush(p); ++ pStruct = fts5StructureRead(p); + if( pStruct ){ + int nMin = p->pConfig->nUsermerge; + fts5StructureInvalidate(p); +@@ -221113,7 +250145,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ + Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct); + fts5StructureRelease(pStruct); + pStruct = pNew; +- nMin = 2; ++ nMin = 1; + nMerge = nMerge*-1; + } + if( pStruct && pStruct->nLevel ){ +@@ -221128,7 +250160,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ + + static void fts5AppendRowid( + Fts5Index *p, +- i64 iDelta, ++ u64 iDelta, + Fts5Iter *pUnused, + Fts5Buffer *pBuf + ){ +@@ -221138,7 +250170,7 @@ static void fts5AppendRowid( + + static void fts5AppendPoslist( + Fts5Index *p, +- i64 iDelta, ++ u64 iDelta, + Fts5Iter *pMulti, + Fts5Buffer *pBuf + ){ +@@ -221157,7 +250189,7 @@ static void fts5AppendPoslist( + static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ + u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; + +- assert( pIter->aPoslist ); ++ assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) ); + if( p>=pIter->aEof ){ + pIter->aPoslist = 0; + }else{ +@@ -221177,6 +250209,9 @@ static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ + } + + pIter->aPoslist = p; ++ if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){ ++ pIter->aPoslist = 0; ++ } + } + } + +@@ -221185,9 +250220,11 @@ static void fts5DoclistIterInit( + Fts5DoclistIter *pIter + ){ + memset(pIter, 0, sizeof(*pIter)); +- pIter->aPoslist = pBuf->p; +- pIter->aEof = &pBuf->p[pBuf->n]; +- fts5DoclistIterNext(pIter); ++ if( pBuf->n>0 ){ ++ pIter->aPoslist = pBuf->p; ++ pIter->aEof = &pBuf->p[pBuf->n]; ++ fts5DoclistIterNext(pIter); ++ } + } + + #if 0 +@@ -221208,10 +250245,10 @@ static void fts5MergeAppendDocid( + } + #endif + +-#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ +- assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ +- fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \ +- (iLastRowid) = (iRowid); \ ++#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ ++ assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ ++ fts5BufferSafeAppendVarint((pBuf), (u64)(iRowid) - (u64)(iLastRowid)); \ ++ (iLastRowid) = (iRowid); \ + } + + /* +@@ -221241,16 +250278,20 @@ static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){ + static void fts5MergeRowidLists( + Fts5Index *p, /* FTS5 backend object */ + Fts5Buffer *p1, /* First list to merge */ +- Fts5Buffer *p2 /* Second list to merge */ ++ int nBuf, /* Number of entries in apBuf[] */ ++ Fts5Buffer *aBuf /* Array of other lists to merge into p1 */ + ){ + int i1 = 0; + int i2 = 0; + i64 iRowid1 = 0; + i64 iRowid2 = 0; + i64 iOut = 0; +- ++ Fts5Buffer *p2 = &aBuf[0]; + Fts5Buffer out; ++ ++ (void)nBuf; + memset(&out, 0, sizeof(out)); ++ assert( nBuf==1 ); + sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n); + if( p->rc ) return; + +@@ -221277,252 +250318,665 @@ static void fts5MergeRowidLists( + fts5BufferFree(&out); + } + ++typedef struct PrefixMerger PrefixMerger; ++struct PrefixMerger { ++ Fts5DoclistIter iter; /* Doclist iterator */ ++ i64 iPos; /* For iterating through a position list */ ++ int iOff; ++ u8 *aPos; ++ PrefixMerger *pNext; /* Next in docid/poslist order */ ++}; ++ ++static void fts5PrefixMergerInsertByRowid( ++ PrefixMerger **ppHead, ++ PrefixMerger *p ++){ ++ if( p->iter.aPoslist ){ ++ PrefixMerger **pp = ppHead; ++ while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){ ++ pp = &(*pp)->pNext; ++ } ++ p->pNext = *pp; ++ *pp = p; ++ } ++} ++ ++static void fts5PrefixMergerInsertByPosition( ++ PrefixMerger **ppHead, ++ PrefixMerger *p ++){ ++ if( p->iPos>=0 ){ ++ PrefixMerger **pp = ppHead; ++ while( *pp && p->iPos>(*pp)->iPos ){ ++ pp = &(*pp)->pNext; ++ } ++ p->pNext = *pp; ++ *pp = p; ++ } ++} ++ ++ + /* +-** Buffers p1 and p2 contain doclists. This function merges the content +-** of the two doclists together and sets buffer p1 to the result before +-** returning. +-** +-** If an error occurs, an error code is left in p->rc. If an error has +-** already occurred, this function is a no-op. ++** Array aBuf[] contains nBuf doclists. These are all merged in with the ++** doclist in buffer p1. + */ + static void fts5MergePrefixLists( + Fts5Index *p, /* FTS5 backend object */ + Fts5Buffer *p1, /* First list to merge */ +- Fts5Buffer *p2 /* Second list to merge */ +-){ +- if( p2->n ){ +- i64 iLastRowid = 0; +- Fts5DoclistIter i1; +- Fts5DoclistIter i2; +- Fts5Buffer out = {0, 0, 0}; +- Fts5Buffer tmp = {0, 0, 0}; +- +- /* The maximum size of the output is equal to the sum of the two +- ** input sizes + 1 varint (9 bytes). The extra varint is because if the +- ** first rowid in one input is a large negative number, and the first in +- ** the other a non-negative number, the delta for the non-negative +- ** number will be larger on disk than the literal integer value +- ** was. +- ** +- ** Or, if the input position-lists are corrupt, then the output might +- ** include up to 2 extra 10-byte positions created by interpreting -1 +- ** (the value PoslistNext64() uses for EOF) as a position and appending +- ** it to the output. This can happen at most once for each input +- ** position-list, hence two 10 byte paddings. */ +- if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9+10+10) ) return; +- fts5DoclistIterInit(p1, &i1); +- fts5DoclistIterInit(p2, &i2); ++ int nBuf, /* Number of buffers in array aBuf[] */ ++ Fts5Buffer *aBuf /* Other lists to merge in */ ++){ ++#define fts5PrefixMergerNextPosition(p) \ ++ sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos) ++#define FTS5_MERGE_NLIST 16 ++ PrefixMerger aMerger[FTS5_MERGE_NLIST]; ++ PrefixMerger *pHead = 0; ++ int i; ++ int nOut = 0; ++ Fts5Buffer out = {0, 0, 0}; ++ Fts5Buffer tmp = {0, 0, 0}; ++ i64 iLastRowid = 0; ++ ++ /* Initialize a doclist-iterator for each input buffer. Arrange them in ++ ** a linked-list starting at pHead in ascending order of rowid. Avoid ++ ** linking any iterators already at EOF into the linked list at all. */ ++ assert( nBuf+1<=(int)(sizeof(aMerger)/sizeof(aMerger[0])) ); ++ memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1)); ++ pHead = &aMerger[nBuf]; ++ fts5DoclistIterInit(p1, &pHead->iter); ++ for(i=0; in + 9 + 10*nBuf; ++ ++ /* The maximum size of the output is equal to the sum of the ++ ** input sizes + 1 varint (9 bytes). The extra varint is because if the ++ ** first rowid in one input is a large negative number, and the first in ++ ** the other a non-negative number, the delta for the non-negative ++ ** number will be larger on disk than the literal integer value ++ ** was. ++ ** ++ ** Or, if the input position-lists are corrupt, then the output might ++ ** include up to (nBuf+1) extra 10-byte positions created by interpreting -1 ++ ** (the value PoslistNext64() uses for EOF) as a position and appending ++ ** it to the output. This can happen at most once for each input ++ ** position-list, hence (nBuf+1) 10 byte paddings. */ ++ if( sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return; ++ ++ while( pHead ){ ++ fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid); ++ ++ if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){ ++ /* Merge data from two or more poslists */ ++ i64 iPrev = 0; ++ int nTmp = FTS5_DATA_ZERO_PADDING; ++ int nMerge = 0; ++ PrefixMerger *pSave = pHead; ++ PrefixMerger *pThis = 0; ++ int nTail = 0; ++ ++ pHead = 0; ++ while( pSave && pSave->iter.iRowid==iLastRowid ){ ++ PrefixMerger *pNext = pSave->pNext; ++ pSave->iOff = 0; ++ pSave->iPos = 0; ++ pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize]; ++ fts5PrefixMergerNextPosition(pSave); ++ nTmp += pSave->iter.nPoslist + 10; ++ nMerge++; ++ fts5PrefixMergerInsertByPosition(&pHead, pSave); ++ pSave = pNext; ++ } ++ ++ if( pHead==0 || pHead->pNext==0 ){ ++ p->rc = FTS5_CORRUPT; ++ break; ++ } + +- while( 1 ){ +- if( i1.iRowidp) + (i2.aPoslist-p2->p)+9+10+10) ); +- } +- else if( i2.iRowid!=i1.iRowid ){ +- /* Copy entry from i2 */ +- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); +- fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize); +- fts5DoclistIterNext(&i2); +- if( i2.aPoslist==0 ) break; +- assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) ); ++ /* See the earlier comment in this function for an explanation of why ++ ** corrupt input position lists might cause the output to consume ++ ** at most nMerge*10 bytes of unexpected space. */ ++ if( sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){ ++ break; + } +- else{ +- /* Merge the two position lists. */ +- i64 iPos1 = 0; +- i64 iPos2 = 0; +- int iOff1 = 0; +- int iOff2 = 0; +- u8 *a1 = &i1.aPoslist[i1.nSize]; +- u8 *a2 = &i2.aPoslist[i2.nSize]; +- int nCopy; +- u8 *aCopy; +- +- i64 iPrev = 0; +- Fts5PoslistWriter writer; +- memset(&writer, 0, sizeof(writer)); +- +- /* See the earlier comment in this function for an explanation of why +- ** corrupt input position lists might cause the output to consume +- ** at most 20 bytes of unexpected space. */ +- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); +- fts5BufferZero(&tmp); +- sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist + 10 + 10); +- if( p->rc ) break; ++ fts5BufferZero(&tmp); + +- sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1); +- sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); +- assert_nc( iPos1>=0 && iPos2>=0 ); ++ pThis = pHead; ++ pHead = pThis->pNext; ++ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); ++ fts5PrefixMergerNextPosition(pThis); ++ fts5PrefixMergerInsertByPosition(&pHead, pThis); + +- if( iPos1=0 && iPos2>=0 ){ +- while( 1 ){ +- if( iPos1pNext ){ ++ pThis = pHead; ++ if( pThis->iPos!=iPrev ){ ++ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); + } ++ fts5PrefixMergerNextPosition(pThis); ++ pHead = pThis->pNext; ++ fts5PrefixMergerInsertByPosition(&pHead, pThis); ++ } + +- if( iPos1>=0 ){ +- if( iPos1!=iPrev ){ +- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1); +- } +- aCopy = &a1[iOff1]; +- nCopy = i1.nPoslist - iOff1; +- }else{ +- assert_nc( iPos2>=0 && iPos2!=iPrev ); +- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2); +- aCopy = &a2[iOff2]; +- nCopy = i2.nPoslist - iOff2; +- } +- if( nCopy>0 ){ +- fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy); +- } ++ if( pHead->iPos!=iPrev ){ ++ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos); ++ } ++ nTail = pHead->iter.nPoslist - pHead->iOff; + +- /* WRITEPOSLISTSIZE */ +- assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist ); +- assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 ); +- if( tmp.n>i1.nPoslist+i2.nPoslist ){ +- if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; +- break; ++ /* WRITEPOSLISTSIZE */ ++ assert_nc( tmp.n+nTail<=nTmp ); ++ assert( tmp.n+nTail<=nTmp+nMerge*10 ); ++ if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){ ++ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; ++ break; ++ } ++ fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2); ++ fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); ++ if( nTail>0 ){ ++ fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail); ++ } ++ ++ pHead = pSave; ++ for(i=0; iiter.aPoslist && pX->iter.iRowid==iLastRowid ){ ++ fts5DoclistIterNext(&pX->iter); ++ fts5PrefixMergerInsertByRowid(&pHead, pX); + } +- fts5BufferSafeAppendVarint(&out, tmp.n * 2); +- fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); +- fts5DoclistIterNext(&i1); +- fts5DoclistIterNext(&i2); +- assert_nc( out.n<=(p1->n+p2->n+9) ); +- if( i1.aPoslist==0 || i2.aPoslist==0 ) break; +- assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) ); + } ++ ++ }else{ ++ /* Copy poslist from pHead to output */ ++ PrefixMerger *pThis = pHead; ++ Fts5DoclistIter *pI = &pThis->iter; ++ fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize); ++ fts5DoclistIterNext(pI); ++ pHead = pThis->pNext; ++ fts5PrefixMergerInsertByRowid(&pHead, pThis); + } ++ } + +- if( i1.aPoslist ){ +- fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid); +- fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist); ++ fts5BufferFree(p1); ++ fts5BufferFree(&tmp); ++ memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); ++ *p1 = out; ++} ++ ++ ++/* ++** Iterate through a range of entries in the FTS index, invoking the xVisit ++** callback for each of them. ++** ++** Parameter pToken points to an nToken buffer containing an FTS index term ++** (i.e. a document term with the preceding 1 byte index identifier - ++** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits ++** all entries for terms that have pToken/nToken as a prefix. If bPrefix ++** is false, then only entries with pToken/nToken as the entire key are ++** visited. ++** ++** If the current table is a tokendata=1 table, then if bPrefix is true then ++** each index term is treated separately. However, if bPrefix is false, then ++** all index terms corresponding to pToken/nToken are collapsed into a single ++** term before the callback is invoked. ++** ++** The callback invoked for each entry visited is specified by paramter xVisit. ++** Each time it is invoked, it is passed a pointer to the Fts5Index object, ++** a copy of the 7th paramter to this function (pCtx) and a pointer to the ++** iterator that indicates the current entry. If the current entry is the ++** first with a new term (i.e. different from that of the previous entry, ++** including the very first term), then the final two parameters are passed ++** a pointer to the term and its size in bytes, respectively. If the current ++** entry is not the first associated with its term, these two parameters ++** are passed 0. ++** ++** If parameter pColset is not NULL, then it is used to filter entries before ++** the callback is invoked. ++*/ ++static int fts5VisitEntries( ++ Fts5Index *p, /* Fts5 index object */ ++ Fts5Colset *pColset, /* Columns filter to apply, or NULL */ ++ u8 *pToken, /* Buffer containing token */ ++ int nToken, /* Size of buffer pToken in bytes */ ++ int bPrefix, /* True for a prefix scan */ ++ void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int), ++ void *pCtx /* Passed as second argument to xVisit() */ ++){ ++ const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0) ++ | FTS5INDEX_QUERY_SKIPEMPTY ++ | FTS5INDEX_QUERY_NOOUTPUT; ++ Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ ++ int bNewTerm = 1; ++ Fts5Structure *pStruct = fts5StructureRead(p); ++ ++ fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); ++ fts5IterSetOutputCb(&p->rc, p1); ++ for( /* no-op */ ; ++ fts5MultiIterEof(p, p1)==0; ++ fts5MultiIterNext2(p, p1, &bNewTerm) ++ ){ ++ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; ++ int nNew = 0; ++ const u8 *pNew = 0; ++ ++ p1->xSetOutputs(p1, pSeg); ++ if( p->rc ) break; ++ ++ if( bNewTerm ){ ++ nNew = pSeg->term.n; ++ pNew = pSeg->term.p; ++ if( nNewrc; ++} ++ ++ ++/* ++** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an ++** array of these for each row it visits (so all iRowid fields are the same). ++** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an ++** array of these for the entire query (in which case iRowid fields may take ++** a variety of values). ++** ++** Each instance in the array indicates the iterator (and therefore term) ++** associated with position iPos of rowid iRowid. This is used by the ++** xInstToken() API. ++** ++** iRowid: ++** Rowid for the current entry. ++** ++** iPos: ++** Position of current entry within row. In the usual ((iCol<<32)+iOff) ++** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()). ++** ++** iIter: ++** If the Fts5TokenDataIter iterator that the entry is part of is ++** actually an iterator (i.e. with nIter>0, not just a container for ++** Fts5TokenDataMap structures), then this variable is an index into ++** the apIter[] array. The corresponding term is that which the iterator ++** at apIter[iIter] currently points to. ++** ++** Or, if the Fts5TokenDataIter iterator is just a container object ++** (nIter==0), then iIter is an index into the term.p[] buffer where ++** the term is stored. ++** ++** nByte: ++** In the case where iIter is an index into term.p[], this variable ++** is the size of the term in bytes. If iIter is an index into apIter[], ++** this variable is unused. ++*/ ++struct Fts5TokenDataMap { ++ i64 iRowid; /* Row this token is located in */ ++ i64 iPos; /* Position of token */ ++ int iIter; /* Iterator token was read from */ ++ int nByte; /* Length of token in bytes (or 0) */ ++}; ++ ++/* ++** An object used to supplement Fts5Iter for tokendata=1 iterators. ++** ++** This object serves two purposes. The first is as a container for an array ++** of Fts5TokenDataMap structures, which are used to find the token required ++** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and ++** aMap[] variables. ++*/ ++struct Fts5TokenDataIter { ++ int nMapAlloc; /* Allocated size of aMap[] in entries */ ++ int nMap; /* Number of valid entries in aMap[] */ ++ Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */ ++ ++ /* The following are used for prefix-queries only. */ ++ Fts5Buffer terms; ++ ++ /* The following are used for other full-token tokendata queries only. */ ++ int nIter; ++ int nIterAlloc; ++ Fts5PoslistReader *aPoslistReader; ++ int *aPoslistToIter; ++ Fts5Iter *apIter[FLEXARRAY]; ++}; ++ ++/* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */ ++#define SZ_FTS5TOKENDATAITER(N) \ ++ (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter)) ++ ++/* ++** The two input arrays - a1[] and a2[] - are in sorted order. This function ++** merges the two arrays together and writes the result to output array ++** aOut[]. aOut[] is guaranteed to be large enough to hold the result. ++** ++** Duplicate entries are copied into the output. So the size of the output ++** array is always (n1+n2) entries. ++*/ ++static void fts5TokendataMerge( ++ Fts5TokenDataMap *a1, int n1, /* Input array 1 */ ++ Fts5TokenDataMap *a2, int n2, /* Input array 2 */ ++ Fts5TokenDataMap *aOut /* Output array */ ++){ ++ int i1 = 0; ++ int i2 = 0; ++ ++ assert( n1>=0 && n2>=0 ); ++ while( i1=n2 || (i1rc==SQLITE_OK ){ ++ if( pT->nMap==pT->nMapAlloc ){ ++ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; ++ int nAlloc = nNew * sizeof(Fts5TokenDataMap); ++ Fts5TokenDataMap *aNew; ++ ++ aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc); ++ if( aNew==0 ){ ++ p->rc = SQLITE_NOMEM; ++ return; ++ } ++ ++ pT->aMap = aNew; ++ pT->nMapAlloc = nNew; ++ } ++ ++ pT->aMap[pT->nMap].iRowid = iRowid; ++ pT->aMap[pT->nMap].iPos = iPos; ++ pT->aMap[pT->nMap].iIter = iIter; ++ pT->aMap[pT->nMap].nByte = nByte; ++ pT->nMap++; ++ } ++} ++ ++/* ++** Sort the contents of the pT->aMap[] array. ++** ++** The sorting algorithm requires a malloc(). If this fails, an error code ++** is left in Fts5Index.rc before returning. ++*/ ++static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ ++ Fts5TokenDataMap *aTmp = 0; ++ int nByte = pT->nMap * sizeof(Fts5TokenDataMap); ++ ++ aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte); ++ if( aTmp ){ ++ Fts5TokenDataMap *a1 = pT->aMap; ++ Fts5TokenDataMap *a2 = aTmp; ++ i64 nHalf; ++ ++ for(nHalf=1; nHalfnMap; nHalf=nHalf*2){ ++ int i1; ++ for(i1=0; i1nMap; i1+=(nHalf*2)){ ++ int n1 = MIN(nHalf, pT->nMap-i1); ++ int n2 = MIN(nHalf, pT->nMap-i1-n1); ++ fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]); ++ } ++ SWAPVAL(Fts5TokenDataMap*, a1, a2); ++ } ++ ++ if( a1!=pT->aMap ){ ++ memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap)); ++ } ++ sqlite3_free(aTmp); ++ ++#ifdef SQLITE_DEBUG ++ { ++ int ii; ++ for(ii=1; iinMap; ii++){ ++ Fts5TokenDataMap *p1 = &pT->aMap[ii-1]; ++ Fts5TokenDataMap *p2 = &pT->aMap[ii]; ++ assert( p1->iRowidiRowid ++ || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos) ++ ); ++ } ++ } ++#endif ++ } ++} ++ ++/* ++** Delete an Fts5TokenDataIter structure and its contents. ++*/ ++static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ ++ if( pSet ){ ++ int ii; ++ for(ii=0; iinIter; ii++){ ++ fts5MultiIterFree(pSet->apIter[ii]); ++ } ++ fts5BufferFree(&pSet->terms); ++ sqlite3_free(pSet->aPoslistReader); ++ sqlite3_free(pSet->aMap); ++ sqlite3_free(pSet); ++ } ++} ++ ++ ++/* ++** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata() ++** to pass data to prefixIterSetupTokendataCb(). ++*/ ++typedef struct TokendataSetupCtx TokendataSetupCtx; ++struct TokendataSetupCtx { ++ Fts5TokenDataIter *pT; /* Object being populated with mappings */ ++ int iTermOff; /* Offset of current term in terms.p[] */ ++ int nTermByte; /* Size of current term in bytes */ ++}; ++ ++/* ++** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This ++** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each ++** position in the current position-list. It doesn't matter that some of ++** these may be out of order - they will be sorted later. ++*/ ++static void prefixIterSetupTokendataCb( ++ Fts5Index *p, ++ void *pCtx, ++ Fts5Iter *p1, ++ const u8 *pNew, ++ int nNew ++){ ++ TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx; ++ int iPosOff = 0; ++ i64 iPos = 0; ++ ++ if( pNew ){ ++ pSetup->nTermByte = nNew-1; ++ pSetup->iTermOff = pSetup->pT->terms.n; ++ fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1); ++ } ++ ++ while( 0==sqlite3Fts5PoslistNext64( ++ p1->base.pData, p1->base.nData, &iPosOff, &iPos ++ ) ){ ++ fts5TokendataIterAppendMap(p, ++ pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos ++ ); ++ } ++} ++ ++ ++/* ++** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries(). ++*/ ++typedef struct PrefixSetupCtx PrefixSetupCtx; ++struct PrefixSetupCtx { ++ void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); ++ void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); ++ i64 iLastRowid; ++ int nMerge; ++ Fts5Buffer *aBuf; ++ int nBuf; ++ Fts5Buffer doclist; ++ TokendataSetupCtx *pTokendata; ++}; ++ ++/* ++** fts5VisitEntries() callback used by fts5SetupPrefixIter() ++*/ ++static void prefixIterSetupCb( ++ Fts5Index *p, ++ void *pCtx, ++ Fts5Iter *p1, ++ const u8 *pNew, ++ int nNew ++){ ++ PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx; ++ const int nMerge = pSetup->nMerge; ++ ++ if( p1->base.nData>0 ){ ++ if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){ ++ int i; ++ for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){ ++ int i1 = i*nMerge; ++ int iStore; ++ assert( i1+nMerge<=pSetup->nBuf ); ++ for(iStore=i1; iStoreaBuf[iStore].n==0 ){ ++ fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]); ++ fts5BufferZero(&pSetup->doclist); ++ break; ++ } ++ } ++ if( iStore==i1+nMerge ){ ++ pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]); ++ for(iStore=i1; iStoreaBuf[iStore]); ++ } ++ } ++ } ++ pSetup->iLastRowid = 0; + } +- assert_nc( out.n<=(p1->n+p2->n+9) ); + +- fts5BufferSet(&p->rc, p1, out.n, out.p); +- fts5BufferFree(&tmp); +- fts5BufferFree(&out); ++ pSetup->xAppend( ++ p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist ++ ); ++ pSetup->iLastRowid = p1->base.iRowid; ++ } ++ ++ if( pSetup->pTokendata ){ ++ prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew); + } + } + + static void fts5SetupPrefixIter( + Fts5Index *p, /* Index to read from */ + int bDesc, /* True for "ORDER BY rowid DESC" */ +- const u8 *pToken, /* Buffer containing prefix to match */ ++ int iIdx, /* Index to scan for data */ ++ u8 *pToken, /* Buffer containing prefix to match */ + int nToken, /* Size of buffer pToken in bytes */ + Fts5Colset *pColset, /* Restrict matches to these columns */ +- Fts5Iter **ppIter /* OUT: New iterator */ ++ Fts5Iter **ppIter /* OUT: New iterator */ + ){ + Fts5Structure *pStruct; +- Fts5Buffer *aBuf; +- const int nBuf = 32; ++ PrefixSetupCtx s; ++ TokendataSetupCtx s2; ++ ++ memset(&s, 0, sizeof(s)); ++ memset(&s2, 0, sizeof(s2)); ++ ++ s.nMerge = 1; ++ s.iLastRowid = 0; ++ s.nBuf = 32; ++ if( iIdx==0 ++ && p->pConfig->eDetail==FTS5_DETAIL_FULL ++ && p->pConfig->bPrefixInsttoken ++ ){ ++ s.pTokendata = &s2; ++ s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1)); ++ } + +- void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*); +- void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*); + if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ +- xMerge = fts5MergeRowidLists; +- xAppend = fts5AppendRowid; ++ s.xMerge = fts5MergeRowidLists; ++ s.xAppend = fts5AppendRowid; + }else{ +- xMerge = fts5MergePrefixLists; +- xAppend = fts5AppendPoslist; ++ s.nMerge = FTS5_MERGE_NLIST-1; ++ s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ ++ s.xMerge = fts5MergePrefixLists; ++ s.xAppend = fts5AppendPoslist; + } + +- aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); ++ s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf); + pStruct = fts5StructureRead(p); ++ assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) ); + +- if( aBuf && pStruct ){ +- const int flags = FTS5INDEX_QUERY_SCAN +- | FTS5INDEX_QUERY_SKIPEMPTY +- | FTS5INDEX_QUERY_NOOUTPUT; ++ if( p->rc==SQLITE_OK ){ ++ void *pCtx = (void*)&s; + int i; +- i64 iLastRowid = 0; +- Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ + Fts5Data *pData; +- Fts5Buffer doclist; +- int bNewTerm = 1; +- +- memset(&doclist, 0, sizeof(doclist)); +- fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); +- fts5IterSetOutputCb(&p->rc, p1); +- for( /* no-op */ ; +- fts5MultiIterEof(p, p1)==0; +- fts5MultiIterNext2(p, p1, &bNewTerm) +- ){ +- Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; +- int nTerm = pSeg->term.n; +- const u8 *pTerm = pSeg->term.p; +- p1->xSetOutputs(p1, pSeg); + +- assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); +- if( bNewTerm ){ +- if( nTermbase.nData==0 ) continue; +- +- if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ +- for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ +- assert( ibase.iRowid-iLastRowid, p1, &doclist); +- iLastRowid = p1->base.iRowid; ++ /* If iIdx is non-zero, then it is the number of a prefix-index for ++ ** prefixes 1 character longer than the prefix being queried for. That ++ ** index contains all the doclists required, except for the one ++ ** corresponding to the prefix itself. That one is extracted from the ++ ** main term index here. */ ++ if( iIdx!=0 ){ ++ pToken[0] = FTS5_MAIN_PREFIX; ++ fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx); + } + +- for(i=0; irc==SQLITE_OK ){ +- xMerge(p, &doclist, &aBuf[i]); ++ s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]); ++ } ++ for(iFree=i; iFreerc!=SQLITE_OK ); + if( pData ){ + pData->p = (u8*)&pData[1]; +- pData->nn = pData->szLeaf = doclist.n; +- if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); ++ pData->nn = pData->szLeaf = s.doclist.n; ++ if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n); + fts5MultiIterNew2(p, pData, bDesc, ppIter); + } +- fts5BufferFree(&doclist); ++ ++ assert( (*ppIter)!=0 || p->rc!=SQLITE_OK ); ++ if( p->rc==SQLITE_OK && s.pTokendata ){ ++ fts5TokendataIterSortMap(p, s2.pT); ++ (*ppIter)->pTokenDataIter = s2.pT; ++ s2.pT = 0; ++ } + } + ++ fts5TokendataIterDelete(s2.pT); ++ fts5BufferFree(&s.doclist); + fts5StructureRelease(pStruct); +- sqlite3_free(aBuf); ++ sqlite3_free(s.aBuf); + } + + +@@ -221548,6 +251002,9 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ + + p->iWriteRowid = iRowid; + p->bDelete = bDelete; ++ if( bDelete==0 ){ ++ p->nPendingRow++; ++ } + return fts5IndexReturn(p); + } + +@@ -221557,7 +251014,7 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ + static int sqlite3Fts5IndexSync(Fts5Index *p){ + assert( p->rc==SQLITE_OK ); + fts5IndexFlush(p); +- sqlite3Fts5IndexCloseReader(p); ++ fts5IndexCloseReader(p); + return fts5IndexReturn(p); + } + +@@ -221568,11 +251025,10 @@ static int sqlite3Fts5IndexSync(Fts5Index *p){ + ** records must be invalidated. + */ + static int sqlite3Fts5IndexRollback(Fts5Index *p){ +- sqlite3Fts5IndexCloseReader(p); ++ fts5IndexCloseReader(p); + fts5IndexDiscardData(p); + fts5StructureInvalidate(p); +- /* assert( p->rc==SQLITE_OK ); */ +- return SQLITE_OK; ++ return fts5IndexReturn(p); + } + + /* +@@ -221581,12 +251037,17 @@ static int sqlite3Fts5IndexRollback(Fts5Index *p){ + ** and the initial version of the "averages" record (a zero-byte blob). + */ + static int sqlite3Fts5IndexReinit(Fts5Index *p){ +- Fts5Structure s; ++ Fts5Structure *pTmp; ++ u8 tmpSpace[SZ_FTS5STRUCTURE(1)]; + fts5StructureInvalidate(p); + fts5IndexDiscardData(p); +- memset(&s, 0, sizeof(Fts5Structure)); ++ pTmp = (Fts5Structure*)tmpSpace; ++ memset(pTmp, 0, SZ_FTS5STRUCTURE(1)); ++ if( p->pConfig->bContentlessDelete ){ ++ pTmp->nOriginCntr = 1; ++ } + fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); +- fts5StructureWrite(p, &s); ++ fts5StructureWrite(p, pTmp); + return fts5IndexReturn(p); + } + +@@ -221648,7 +251109,9 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){ + sqlite3_finalize(p->pIdxWriter); + sqlite3_finalize(p->pIdxDeleter); + sqlite3_finalize(p->pIdxSelect); ++ sqlite3_finalize(p->pIdxNextSelect); + sqlite3_finalize(p->pDataVersion); ++ sqlite3_finalize(p->pDeleteFromIdx); + sqlite3Fts5HashFree(p->pHash); + sqlite3_free(p->zDataTbl); + sqlite3_free(p); +@@ -221742,6 +251205,387 @@ static int sqlite3Fts5IndexWrite( + return rc; + } + ++/* ++** pToken points to a buffer of size nToken bytes containing a search ++** term, including the index number at the start, used on a tokendata=1 ++** table. This function returns true if the term in buffer pBuf matches ++** token pToken/nToken. ++*/ ++static int fts5IsTokendataPrefix( ++ Fts5Buffer *pBuf, ++ const u8 *pToken, ++ int nToken ++){ ++ return ( ++ pBuf->n>=nToken ++ && 0==memcmp(pBuf->p, pToken, nToken) ++ && (pBuf->n==nToken || pBuf->p[nToken]==0x00) ++ ); ++} ++ ++/* ++** Ensure the segment-iterator passed as the only argument points to EOF. ++*/ ++static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ ++ fts5DataRelease(pSeg->pLeaf); ++ pSeg->pLeaf = 0; ++} ++ ++static void fts5IterClose(Fts5IndexIter *pIndexIter){ ++ if( pIndexIter ){ ++ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; ++ Fts5Index *pIndex = pIter->pIndex; ++ fts5TokendataIterDelete(pIter->pTokenDataIter); ++ fts5MultiIterFree(pIter); ++ fts5IndexCloseReader(pIndex); ++ } ++} ++ ++/* ++** This function appends iterator pAppend to Fts5TokenDataIter pIn and ++** returns the result. ++*/ ++static Fts5TokenDataIter *fts5AppendTokendataIter( ++ Fts5Index *p, /* Index object (for error code) */ ++ Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */ ++ Fts5Iter *pAppend /* Append this iterator */ ++){ ++ Fts5TokenDataIter *pRet = pIn; ++ ++ if( p->rc==SQLITE_OK ){ ++ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ ++ int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; ++ int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); ++ Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); ++ ++ if( pNew==0 ){ ++ p->rc = SQLITE_NOMEM; ++ }else{ ++ if( pIn==0 ) memset(pNew, 0, nByte); ++ pRet = pNew; ++ pNew->nIterAlloc = nAlloc; ++ } ++ } ++ } ++ if( p->rc ){ ++ fts5IterClose((Fts5IndexIter*)pAppend); ++ }else{ ++ pRet->apIter[pRet->nIter++] = pAppend; ++ } ++ assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); ++ ++ return pRet; ++} ++ ++/* ++** The iterator passed as the only argument must be a tokendata=1 iterator ++** (pIter->pTokenDataIter!=0). This function sets the iterator output ++** variables (pIter->base.*) according to the contents of the current ++** row. ++*/ ++static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ ++ int ii; ++ int nHit = 0; ++ i64 iRowid = SMALLEST_INT64; ++ int iMin = 0; ++ ++ Fts5TokenDataIter *pT = pIter->pTokenDataIter; ++ ++ pIter->base.nData = 0; ++ pIter->base.pData = 0; ++ ++ for(ii=0; iinIter; ii++){ ++ Fts5Iter *p = pT->apIter[ii]; ++ if( p->base.bEof==0 ){ ++ if( nHit==0 || p->base.iRowidbase.iRowid; ++ nHit = 1; ++ pIter->base.pData = p->base.pData; ++ pIter->base.nData = p->base.nData; ++ iMin = ii; ++ }else if( p->base.iRowid==iRowid ){ ++ nHit++; ++ } ++ } ++ } ++ ++ if( nHit==0 ){ ++ pIter->base.bEof = 1; ++ }else{ ++ int eDetail = pIter->pIndex->pConfig->eDetail; ++ pIter->base.bEof = 0; ++ pIter->base.iRowid = iRowid; ++ ++ if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ ++ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1); ++ }else ++ if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ ++ int nReader = 0; ++ int nByte = 0; ++ i64 iPrev = 0; ++ ++ /* Allocate array of iterators if they are not already allocated. */ ++ if( pT->aPoslistReader==0 ){ ++ pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero( ++ &pIter->pIndex->rc, ++ pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)) ++ ); ++ if( pT->aPoslistReader==0 ) return; ++ pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter]; ++ } ++ ++ /* Populate an iterator for each poslist that will be merged */ ++ for(ii=0; iinIter; ii++){ ++ Fts5Iter *p = pT->apIter[ii]; ++ if( iRowid==p->base.iRowid ){ ++ pT->aPoslistToIter[nReader] = ii; ++ sqlite3Fts5PoslistReaderInit( ++ p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++] ++ ); ++ nByte += p->base.nData; ++ } ++ } ++ ++ /* Ensure the output buffer is large enough */ ++ if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){ ++ return; ++ } ++ ++ /* Ensure the token-mapping is large enough */ ++ if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){ ++ int nNew = (pT->nMapAlloc + nByte) * 2; ++ Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc( ++ pT->aMap, nNew*sizeof(Fts5TokenDataMap) ++ ); ++ if( aNew==0 ){ ++ pIter->pIndex->rc = SQLITE_NOMEM; ++ return; ++ } ++ pT->aMap = aNew; ++ pT->nMapAlloc = nNew; ++ } ++ ++ pIter->poslist.n = 0; ++ ++ while( 1 ){ ++ i64 iMinPos = LARGEST_INT64; ++ ++ /* Find smallest position */ ++ iMin = 0; ++ for(ii=0; iiaPoslistReader[ii]; ++ if( pReader->bEof==0 ){ ++ if( pReader->iPosiPos; ++ iMin = ii; ++ } ++ } ++ } ++ ++ /* If all readers were at EOF, break out of the loop. */ ++ if( iMinPos==LARGEST_INT64 ) break; ++ ++ sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos); ++ sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]); ++ ++ if( eDetail==FTS5_DETAIL_FULL ){ ++ pT->aMap[pT->nMap].iPos = iMinPos; ++ pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin]; ++ pT->aMap[pT->nMap].iRowid = iRowid; ++ pT->nMap++; ++ } ++ } ++ ++ pIter->base.pData = pIter->poslist.p; ++ pIter->base.nData = pIter->poslist.n; ++ } ++ } ++} ++ ++/* ++** The iterator passed as the only argument must be a tokendata=1 iterator ++** (pIter->pTokenDataIter!=0). This function advances the iterator. If ++** argument bFrom is false, then the iterator is advanced to the next ++** entry. Or, if bFrom is true, it is advanced to the first entry with ++** a rowid of iFrom or greater. ++*/ ++static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ ++ int ii; ++ Fts5TokenDataIter *pT = pIter->pTokenDataIter; ++ Fts5Index *pIndex = pIter->pIndex; ++ ++ for(ii=0; iinIter; ii++){ ++ Fts5Iter *p = pT->apIter[ii]; ++ if( p->base.bEof==0 ++ && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowidbase.bEof==0 ++ && p->base.iRowidrc==SQLITE_OK ++ ){ ++ fts5MultiIterNext(pIndex, p, 0, 0); ++ } ++ } ++ } ++ ++ if( pIndex->rc==SQLITE_OK ){ ++ fts5IterSetOutputsTokendata(pIter); ++ } ++} ++ ++/* ++** If the segment-iterator passed as the first argument is at EOF, then ++** set pIter->term to a copy of buffer pTerm. ++*/ ++static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ ++ if( pIter && pIter->aSeg[0].pLeaf==0 ){ ++ fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p); ++ } ++} ++ ++/* ++** This function sets up an iterator to use for a non-prefix query on a ++** tokendata=1 table. ++*/ ++static Fts5Iter *fts5SetupTokendataIter( ++ Fts5Index *p, /* FTS index to query */ ++ const u8 *pToken, /* Buffer containing query term */ ++ int nToken, /* Size of buffer pToken in bytes */ ++ Fts5Colset *pColset /* Colset to filter on */ ++){ ++ Fts5Iter *pRet = 0; ++ Fts5TokenDataIter *pSet = 0; ++ Fts5Structure *pStruct = 0; ++ const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN; ++ ++ Fts5Buffer bSeek = {0, 0, 0}; ++ Fts5Buffer *pSmall = 0; ++ ++ fts5IndexFlush(p); ++ pStruct = fts5StructureRead(p); ++ ++ while( p->rc==SQLITE_OK ){ ++ Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0; ++ Fts5Iter *pNew = 0; ++ Fts5SegIter *pNewIter = 0; ++ Fts5SegIter *pPrevIter = 0; ++ ++ int iLvl, iSeg, ii; ++ ++ pNew = fts5MultiIterAlloc(p, pStruct->nSegment); ++ if( pSmall ){ ++ fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p); ++ fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0"); ++ }else{ ++ fts5BufferSet(&p->rc, &bSeek, nToken, pToken); ++ } ++ if( p->rc ){ ++ fts5IterClose((Fts5IndexIter*)pNew); ++ break; ++ } ++ ++ pNewIter = &pNew->aSeg[0]; ++ pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0); ++ for(iLvl=0; iLvlnLevel; iLvl++){ ++ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ ++ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; ++ int bDone = 0; ++ ++ if( pPrevIter ){ ++ if( fts5BufferCompare(pSmall, &pPrevIter->term) ){ ++ memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter)); ++ memset(pPrevIter, 0, sizeof(Fts5SegIter)); ++ bDone = 1; ++ }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){ ++ fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter); ++ bDone = 1; ++ } ++ } ++ ++ if( bDone==0 ){ ++ fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); ++ } ++ ++ if( pPrevIter ){ ++ if( pPrevIter->pTombArray ){ ++ pNewIter->pTombArray = pPrevIter->pTombArray; ++ pNewIter->pTombArray->nRef++; ++ } ++ }else{ ++ fts5SegIterAllocTombstone(p, pNewIter); ++ } ++ ++ pNewIter++; ++ if( pPrevIter ) pPrevIter++; ++ if( p->rc ) break; ++ } ++ } ++ fts5TokendataSetTermIfEof(pPrev, pSmall); ++ ++ pNew->bSkipEmpty = 1; ++ pNew->pColset = pColset; ++ fts5IterSetOutputCb(&p->rc, pNew); ++ ++ /* Loop through all segments in the new iterator. Find the smallest ++ ** term that any segment-iterator points to. Iterator pNew will be ++ ** used for this term. Also, set any iterator that points to a term that ++ ** does not match pToken/nToken to point to EOF */ ++ pSmall = 0; ++ for(ii=0; iinSeg; ii++){ ++ Fts5SegIter *pII = &pNew->aSeg[ii]; ++ if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){ ++ fts5SegIterSetEOF(pII); ++ } ++ if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){ ++ pSmall = &pII->term; ++ } ++ } ++ ++ /* If pSmall is still NULL at this point, then the new iterator does ++ ** not point to any terms that match the query. So delete it and break ++ ** out of the loop - all required iterators have been collected. */ ++ if( pSmall==0 ){ ++ fts5IterClose((Fts5IndexIter*)pNew); ++ break; ++ } ++ ++ /* Append this iterator to the set and continue. */ ++ pSet = fts5AppendTokendataIter(p, pSet, pNew); ++ } ++ ++ if( p->rc==SQLITE_OK && pSet ){ ++ int ii; ++ for(ii=0; iinIter; ii++){ ++ Fts5Iter *pIter = pSet->apIter[ii]; ++ int iSeg; ++ for(iSeg=0; iSegnSeg; iSeg++){ ++ pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM; ++ } ++ fts5MultiIterFinishSetup(p, pIter); ++ } ++ } ++ ++ if( p->rc==SQLITE_OK ){ ++ pRet = fts5MultiIterAlloc(p, 0); ++ } ++ if( pRet ){ ++ pRet->nSeg = 0; ++ pRet->pTokenDataIter = pSet; ++ if( pSet ){ ++ fts5IterSetOutputsTokendata(pRet); ++ }else{ ++ pRet->base.bEof = 1; ++ } ++ }else{ ++ fts5TokendataIterDelete(pSet); ++ } ++ ++ fts5StructureRelease(pStruct); ++ fts5BufferFree(&bSeek); ++ return pRet; ++} ++ + /* + ** Open a new iterator to iterate though all rowid that match the + ** specified token or token prefix. +@@ -221762,7 +251606,19 @@ static int sqlite3Fts5IndexQuery( + + if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ + int iIdx = 0; /* Index to search */ +- if( nToken ) memcpy(&buf.p[1], pToken, nToken); ++ int iPrefixIdx = 0; /* +1 prefix index */ ++ int bTokendata = pConfig->bTokendata; ++ assert( buf.p!=0 ); ++ if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); ++ ++ /* The NOTOKENDATA flag is set when each token in a tokendata=1 table ++ ** should be treated individually, instead of merging all those with ++ ** a common prefix into a single entry. This is used, for example, by ++ ** queries performed as part of an integrity-check, or by the fts5vocab ++ ** module. */ ++ if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ ++ bTokendata = 0; ++ } + + /* Figure out which index to search and set iIdx accordingly. If this + ** is a prefix query for which there is no prefix index, set iIdx to +@@ -221783,11 +251639,16 @@ static int sqlite3Fts5IndexQuery( + if( flags & FTS5INDEX_QUERY_PREFIX ){ + int nChar = fts5IndexCharlen(pToken, nToken); + for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){ +- if( pConfig->aPrefix[iIdx-1]==nChar ) break; ++ int nIdxChar = pConfig->aPrefix[iIdx-1]; ++ if( nIdxChar==nChar ) break; ++ if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; + } + } + +- if( iIdx<=pConfig->nPrefix ){ ++ if( bTokendata && iIdx==0 ){ ++ buf.p[0] = FTS5_MAIN_PREFIX; ++ pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); ++ }else if( iIdx<=pConfig->nPrefix ){ + /* Straight index lookup */ + Fts5Structure *pStruct = fts5StructureRead(p); + buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); +@@ -221798,22 +251659,25 @@ static int sqlite3Fts5IndexQuery( + fts5StructureRelease(pStruct); + } + }else{ +- /* Scan multiple terms in the main index */ ++ /* Scan multiple terms in the main index for a prefix query. */ + int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; +- buf.p[0] = FTS5_MAIN_PREFIX; +- fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet); +- assert( p->rc!=SQLITE_OK || pRet->pColset==0 ); +- fts5IterSetOutputCb(&p->rc, pRet); +- if( p->rc==SQLITE_OK ){ +- Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; +- if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); ++ fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); ++ if( pRet==0 ){ ++ assert( p->rc!=SQLITE_OK ); ++ }else{ ++ assert( pRet->pColset==0 ); ++ fts5IterSetOutputCb(&p->rc, pRet); ++ if( p->rc==SQLITE_OK ){ ++ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; ++ if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); ++ } + } + } + + if( p->rc ){ +- sqlite3Fts5IterClose((Fts5IndexIter*)pRet); ++ fts5IterClose((Fts5IndexIter*)pRet); + pRet = 0; +- sqlite3Fts5IndexCloseReader(p); ++ fts5IndexCloseReader(p); + } + + *ppIter = (Fts5IndexIter*)pRet; +@@ -221831,7 +251695,12 @@ static int sqlite3Fts5IndexQuery( + static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + assert( pIter->pIndex->rc==SQLITE_OK ); +- fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); ++ if( pIter->nSeg==0 ){ ++ assert( pIter->pTokenDataIter ); ++ fts5TokendataIterNext(pIter, 0, 0); ++ }else{ ++ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); ++ } + return fts5IndexReturn(pIter->pIndex); + } + +@@ -221864,7 +251733,12 @@ static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){ + */ + static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; +- fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); ++ if( pIter->nSeg==0 ){ ++ assert( pIter->pTokenDataIter ); ++ fts5TokendataIterNext(pIter, 1, iMatch); ++ }else{ ++ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); ++ } + return fts5IndexReturn(pIter->pIndex); + } + +@@ -221874,8 +251748,180 @@ static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ + static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ + int n; + const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); ++ assert_nc( z || n<=1 ); + *pn = n-1; +- return &z[1]; ++ return (z ? &z[1] : 0); ++} ++ ++/* ++** pIter is a prefix query. This function populates pIter->pTokenDataIter ++** with an Fts5TokenDataIter object containing mappings for all rows ++** matched by the query. ++*/ ++static int fts5SetupPrefixIterTokendata( ++ Fts5Iter *pIter, ++ const char *pToken, /* Token prefix to search for */ ++ int nToken /* Size of pToken in bytes */ ++){ ++ Fts5Index *p = pIter->pIndex; ++ Fts5Buffer token = {0, 0, 0}; ++ TokendataSetupCtx ctx; ++ ++ memset(&ctx, 0, sizeof(ctx)); ++ ++ fts5BufferGrow(&p->rc, &token, nToken+1); ++ assert( token.p!=0 || p->rc!=SQLITE_OK ); ++ ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, ++ SZ_FTS5TOKENDATAITER(1)); ++ ++ if( p->rc==SQLITE_OK ){ ++ ++ /* Fill in the token prefix to search for */ ++ token.p[0] = FTS5_MAIN_PREFIX; ++ memcpy(&token.p[1], pToken, nToken); ++ token.n = nToken+1; ++ ++ fts5VisitEntries( ++ p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx ++ ); ++ ++ fts5TokendataIterSortMap(p, ctx.pT); ++ } ++ ++ if( p->rc==SQLITE_OK ){ ++ pIter->pTokenDataIter = ctx.pT; ++ }else{ ++ fts5TokendataIterDelete(ctx.pT); ++ } ++ fts5BufferFree(&token); ++ ++ return fts5IndexReturn(p); ++} ++ ++/* ++** This is used by xInstToken() to access the token at offset iOff, column ++** iCol of row iRowid. The token is returned via output variables *ppOut ++** and *pnOut. The iterator passed as the first argument must be a tokendata=1 ++** iterator (pIter->pTokenDataIter!=0). ++** ++** pToken/nToken: ++*/ ++static int sqlite3Fts5IterToken( ++ Fts5IndexIter *pIndexIter, ++ const char *pToken, int nToken, ++ i64 iRowid, ++ int iCol, ++ int iOff, ++ const char **ppOut, int *pnOut ++){ ++ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; ++ Fts5TokenDataIter *pT = pIter->pTokenDataIter; ++ i64 iPos = (((i64)iCol)<<32) + iOff; ++ Fts5TokenDataMap *aMap = 0; ++ int i1 = 0; ++ int i2 = 0; ++ int iTest = 0; ++ ++ assert( pT || (pToken && pIter->nSeg>0) ); ++ if( pT==0 ){ ++ int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken); ++ if( rc!=SQLITE_OK ) return rc; ++ pT = pIter->pTokenDataIter; ++ } ++ ++ i2 = pT->nMap; ++ aMap = pT->aMap; ++ ++ while( i2>i1 ){ ++ iTest = (i1 + i2) / 2; ++ ++ if( aMap[iTest].iRowidiRowid ){ ++ i2 = iTest; ++ }else{ ++ if( aMap[iTest].iPosiPos ){ ++ i2 = iTest; ++ }else{ ++ break; ++ } ++ } ++ } ++ ++ if( i2>i1 ){ ++ if( pIter->nSeg==0 ){ ++ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; ++ *ppOut = (const char*)pMap->aSeg[0].term.p+1; ++ *pnOut = pMap->aSeg[0].term.n-1; ++ }else{ ++ Fts5TokenDataMap *p = &aMap[iTest]; ++ *ppOut = (const char*)&pT->terms.p[p->iIter]; ++ *pnOut = aMap[iTest].nByte; ++ } ++ } ++ ++ return SQLITE_OK; ++} ++ ++/* ++** Clear any existing entries from the token-map associated with the ++** iterator passed as the only argument. ++*/ ++static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ ++ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; ++ if( pIter && pIter->pTokenDataIter ++ && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL) ++ ){ ++ pIter->pTokenDataIter->nMap = 0; ++ } ++} ++ ++/* ++** Set a token-mapping for the iterator passed as the first argument. This ++** is used in detail=column or detail=none mode when a token is requested ++** using the xInstToken() API. In this case the caller tokenizers the ++** current row and configures the token-mapping via multiple calls to this ++** function. ++*/ ++static int sqlite3Fts5IndexIterWriteTokendata( ++ Fts5IndexIter *pIndexIter, ++ const char *pToken, int nToken, ++ i64 iRowid, int iCol, int iOff ++){ ++ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; ++ Fts5TokenDataIter *pT = pIter->pTokenDataIter; ++ Fts5Index *p = pIter->pIndex; ++ i64 iPos = (((i64)iCol)<<32) + iOff; ++ ++ assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); ++ assert( pIter->pTokenDataIter || pIter->nSeg>0 ); ++ if( pIter->nSeg>0 ){ ++ /* This is a prefix term iterator. */ ++ if( pT==0 ){ ++ pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, ++ SZ_FTS5TOKENDATAITER(1)); ++ pIter->pTokenDataIter = pT; ++ } ++ if( pT ){ ++ fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); ++ fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); ++ } ++ }else{ ++ int ii; ++ for(ii=0; iinIter; ii++){ ++ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; ++ if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; ++ } ++ if( iinIter ){ ++ fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos); ++ } ++ } ++ return fts5IndexReturn(p); + } + + /* +@@ -221883,10 +251929,9 @@ static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ + */ + static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ + if( pIndexIter ){ +- Fts5Iter *pIter = (Fts5Iter*)pIndexIter; +- Fts5Index *pIndex = pIter->pIndex; +- fts5MultiIterFree(pIter); +- sqlite3Fts5IndexCloseReader(pIndex); ++ Fts5Index *pIndex = ((Fts5Iter*)pIndexIter)->pIndex; ++ fts5IterClose(pIndexIter); ++ fts5IndexReturn(pIndex); + } + } + +@@ -221968,6 +252013,347 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){ + return fts5IndexReturn(p); + } + ++/* ++** Retrieve the origin value that will be used for the segment currently ++** being accumulated in the in-memory hash table when it is flushed to ++** disk. If successful, SQLITE_OK is returned and (*piOrigin) set to ++** the queried value. Or, if an error occurs, an error code is returned ++** and the final value of (*piOrigin) is undefined. ++*/ ++static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin){ ++ Fts5Structure *pStruct; ++ pStruct = fts5StructureRead(p); ++ if( pStruct ){ ++ *piOrigin = pStruct->nOriginCntr; ++ fts5StructureRelease(pStruct); ++ } ++ return fts5IndexReturn(p); ++} ++ ++/* ++** Buffer pPg contains a page of a tombstone hash table - one of nPg pages ++** associated with the same segment. This function adds rowid iRowid to ++** the hash table. The caller is required to guarantee that there is at ++** least one free slot on the page. ++** ++** If parameter bForce is false and the hash table is deemed to be full ++** (more than half of the slots are occupied), then non-zero is returned ++** and iRowid not inserted. Or, if bForce is true or if the hash table page ++** is not full, iRowid is inserted and zero returned. ++*/ ++static int fts5IndexTombstoneAddToPage( ++ Fts5Data *pPg, ++ int bForce, ++ int nPg, ++ u64 iRowid ++){ ++ const int szKey = TOMBSTONE_KEYSIZE(pPg); ++ const int nSlot = TOMBSTONE_NSLOT(pPg); ++ const int nElem = fts5GetU32(&pPg->p[4]); ++ int iSlot = (iRowid / nPg) % nSlot; ++ int nCollide = nSlot; ++ ++ if( szKey==4 && iRowid>0xFFFFFFFF ) return 2; ++ if( iRowid==0 ){ ++ pPg->p[1] = 0x01; ++ return 0; ++ } ++ ++ if( bForce==0 && nElem>=(nSlot/2) ){ ++ return 1; ++ } ++ ++ fts5PutU32(&pPg->p[4], nElem+1); ++ if( szKey==4 ){ ++ u32 *aSlot = (u32*)&pPg->p[8]; ++ while( aSlot[iSlot] ){ ++ iSlot = (iSlot + 1) % nSlot; ++ if( nCollide--==0 ) return 0; ++ } ++ fts5PutU32((u8*)&aSlot[iSlot], (u32)iRowid); ++ }else{ ++ u64 *aSlot = (u64*)&pPg->p[8]; ++ while( aSlot[iSlot] ){ ++ iSlot = (iSlot + 1) % nSlot; ++ if( nCollide--==0 ) return 0; ++ } ++ fts5PutU64((u8*)&aSlot[iSlot], iRowid); ++ } ++ ++ return 0; ++} ++ ++/* ++** This function attempts to build a new hash containing all the keys ++** currently in the tombstone hash table for segment pSeg. The new ++** hash will be stored in the nOut buffers passed in array apOut[]. ++** All pages of the new hash use key-size szKey (4 or 8). ++** ++** Return 0 if the hash is successfully rebuilt into the nOut pages. ++** Or non-zero if it is not (because one page became overfull). In this ++** case the caller should retry with a larger nOut parameter. ++** ++** Parameter pData1 is page iPg1 of the hash table being rebuilt. ++*/ ++static int fts5IndexTombstoneRehash( ++ Fts5Index *p, ++ Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ ++ Fts5Data *pData1, /* One page of current hash - or NULL */ ++ int iPg1, /* Which page of the current hash is pData1 */ ++ int szKey, /* 4 or 8, the keysize */ ++ int nOut, /* Number of output pages */ ++ Fts5Data **apOut /* Array of output hash pages */ ++){ ++ int ii; ++ int res = 0; ++ ++ /* Initialize the headers of all the output pages */ ++ for(ii=0; iip[0] = szKey; ++ fts5PutU32(&apOut[ii]->p[4], 0); ++ } ++ ++ /* Loop through the current pages of the hash table. */ ++ for(ii=0; res==0 && iinPgTombstone; ii++){ ++ Fts5Data *pData = 0; /* Page ii of the current hash table */ ++ Fts5Data *pFree = 0; /* Free this at the end of the loop */ ++ ++ if( iPg1==ii ){ ++ pData = pData1; ++ }else{ ++ pFree = pData = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid, ii)); ++ } ++ ++ if( pData ){ ++ int szKeyIn = TOMBSTONE_KEYSIZE(pData); ++ int nSlotIn = (pData->nn - 8) / szKeyIn; ++ int iIn; ++ for(iIn=0; iInp[8]; ++ if( aSlot[iIn] ) iVal = fts5GetU32((u8*)&aSlot[iIn]); ++ }else{ ++ u64 *aSlot = (u64*)&pData->p[8]; ++ if( aSlot[iIn] ) iVal = fts5GetU64((u8*)&aSlot[iIn]); ++ } ++ ++ /* If iVal is not 0 at this point, insert it into the new hash table */ ++ if( iVal ){ ++ Fts5Data *pPg = apOut[(iVal % nOut)]; ++ res = fts5IndexTombstoneAddToPage(pPg, 0, nOut, iVal); ++ if( res ) break; ++ } ++ } ++ ++ /* If this is page 0 of the old hash, copy the rowid-0-flag from the ++ ** old hash to the new. */ ++ if( ii==0 ){ ++ apOut[0]->p[1] = pData->p[1]; ++ } ++ } ++ fts5DataRelease(pFree); ++ } ++ ++ return res; ++} ++ ++/* ++** This is called to rebuild the hash table belonging to segment pSeg. ++** If parameter pData1 is not NULL, then one page of the existing hash table ++** has already been loaded - pData1, which is page iPg1. The key-size for ++** the new hash table is szKey (4 or 8). ++** ++** If successful, the new hash table is not written to disk. Instead, ++** output parameter (*pnOut) is set to the number of pages in the new ++** hash table, and (*papOut) to point to an array of buffers containing ++** the new page data. ++** ++** If an error occurs, an error code is left in the Fts5Index object and ++** both output parameters set to 0 before returning. ++*/ ++static void fts5IndexTombstoneRebuild( ++ Fts5Index *p, ++ Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ ++ Fts5Data *pData1, /* One page of current hash - or NULL */ ++ int iPg1, /* Which page of the current hash is pData1 */ ++ int szKey, /* 4 or 8, the keysize */ ++ int *pnOut, /* OUT: Number of output pages */ ++ Fts5Data ***papOut /* OUT: Output hash pages */ ++){ ++ const int MINSLOT = 32; ++ int nSlotPerPage = MAX(MINSLOT, (p->pConfig->pgsz - 8) / szKey); ++ int nSlot = 0; /* Number of slots in each output page */ ++ int nOut = 0; ++ ++ /* Figure out how many output pages (nOut) and how many slots per ++ ** page (nSlot). There are three possibilities: ++ ** ++ ** 1. The hash table does not yet exist. In this case the new hash ++ ** table will consist of a single page with MINSLOT slots. ++ ** ++ ** 2. The hash table exists but is currently a single page. In this ++ ** case an attempt is made to grow the page to accommodate the new ++ ** entry. The page is allowed to grow up to nSlotPerPage (see above) ++ ** slots. ++ ** ++ ** 3. The hash table already consists of more than one page, or of ++ ** a single page already so large that it cannot be grown. In this ++ ** case the new hash consists of (nPg*2+1) pages of nSlotPerPage ++ ** slots each, where nPg is the current number of pages in the ++ ** hash table. ++ */ ++ if( pSeg->nPgTombstone==0 ){ ++ /* Case 1. */ ++ nOut = 1; ++ nSlot = MINSLOT; ++ }else if( pSeg->nPgTombstone==1 ){ ++ /* Case 2. */ ++ int nElem = (int)fts5GetU32(&pData1->p[4]); ++ assert( pData1 && iPg1==0 ); ++ nOut = 1; ++ nSlot = MAX(nElem*4, MINSLOT); ++ if( nSlot>nSlotPerPage ) nOut = 0; ++ } ++ if( nOut==0 ){ ++ /* Case 3. */ ++ nOut = (pSeg->nPgTombstone * 2 + 1); ++ nSlot = nSlotPerPage; ++ } ++ ++ /* Allocate the required array and output pages */ ++ while( 1 ){ ++ int res = 0; ++ int ii = 0; ++ int szPage = 0; ++ Fts5Data **apOut = 0; ++ ++ /* Allocate space for the new hash table */ ++ assert( nSlot>=MINSLOT ); ++ apOut = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data*) * nOut); ++ szPage = 8 + nSlot*szKey; ++ for(ii=0; iirc, ++ sizeof(Fts5Data)+szPage ++ ); ++ if( pNew ){ ++ pNew->nn = szPage; ++ pNew->p = (u8*)&pNew[1]; ++ apOut[ii] = pNew; ++ } ++ } ++ ++ /* Rebuild the hash table. */ ++ if( p->rc==SQLITE_OK ){ ++ res = fts5IndexTombstoneRehash(p, pSeg, pData1, iPg1, szKey, nOut, apOut); ++ } ++ if( res==0 ){ ++ if( p->rc ){ ++ fts5IndexFreeArray(apOut, nOut); ++ apOut = 0; ++ nOut = 0; ++ } ++ *pnOut = nOut; ++ *papOut = apOut; ++ break; ++ } ++ ++ /* If control flows to here, it was not possible to rebuild the hash ++ ** table. Free all buffers and then try again with more pages. */ ++ assert( p->rc==SQLITE_OK ); ++ fts5IndexFreeArray(apOut, nOut); ++ nSlot = nSlotPerPage; ++ nOut = nOut*2 + 1; ++ } ++} ++ ++ ++/* ++** Add a tombstone for rowid iRowid to segment pSeg. ++*/ ++static void fts5IndexTombstoneAdd( ++ Fts5Index *p, ++ Fts5StructureSegment *pSeg, ++ u64 iRowid ++){ ++ Fts5Data *pPg = 0; ++ int iPg = -1; ++ int szKey = 0; ++ int nHash = 0; ++ Fts5Data **apHash = 0; ++ ++ p->nContentlessDelete++; ++ ++ if( pSeg->nPgTombstone>0 ){ ++ iPg = iRowid % pSeg->nPgTombstone; ++ pPg = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg)); ++ if( pPg==0 ){ ++ assert( p->rc!=SQLITE_OK ); ++ return; ++ } ++ ++ if( 0==fts5IndexTombstoneAddToPage(pPg, 0, pSeg->nPgTombstone, iRowid) ){ ++ fts5DataWrite(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg), pPg->p, pPg->nn); ++ fts5DataRelease(pPg); ++ return; ++ } ++ } ++ ++ /* Have to rebuild the hash table. First figure out the key-size (4 or 8). */ ++ szKey = pPg ? TOMBSTONE_KEYSIZE(pPg) : 4; ++ if( iRowid>0xFFFFFFFF ) szKey = 8; ++ ++ /* Rebuild the hash table */ ++ fts5IndexTombstoneRebuild(p, pSeg, pPg, iPg, szKey, &nHash, &apHash); ++ assert( p->rc==SQLITE_OK || (nHash==0 && apHash==0) ); ++ ++ /* If all has succeeded, write the new rowid into one of the new hash ++ ** table pages, then write them all out to disk. */ ++ if( nHash ){ ++ int ii = 0; ++ fts5IndexTombstoneAddToPage(apHash[iRowid % nHash], 1, nHash, iRowid); ++ for(ii=0; iiiSegid, ii); ++ fts5DataWrite(p, iTombstoneRowid, apHash[ii]->p, apHash[ii]->nn); ++ } ++ pSeg->nPgTombstone = nHash; ++ fts5StructureWrite(p, p->pStruct); ++ } ++ ++ fts5DataRelease(pPg); ++ fts5IndexFreeArray(apHash, nHash); ++} ++ ++/* ++** Add iRowid to the tombstone list of the segment or segments that contain ++** rows from origin iOrigin. Return SQLITE_OK if successful, or an SQLite ++** error code otherwise. ++*/ ++static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid){ ++ Fts5Structure *pStruct; ++ pStruct = fts5StructureRead(p); ++ if( pStruct ){ ++ int bFound = 0; /* True after pSeg->nEntryTombstone incr. */ ++ int iLvl; ++ for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){ ++ int iSeg; ++ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ ++ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; ++ if( pSeg->iOrigin1<=(u64)iOrigin && pSeg->iOrigin2>=(u64)iOrigin ){ ++ if( bFound==0 ){ ++ pSeg->nEntryTombstone++; ++ bFound = 1; ++ } ++ fts5IndexTombstoneAdd(p, pSeg, iRowid); ++ } ++ } ++ } ++ fts5StructureRelease(pStruct); ++ } ++ return fts5IndexReturn(p); ++} + + /************************************************************************* + ************************************************************************** +@@ -222051,9 +252437,11 @@ static int fts5QueryCksum( + int eDetail = p->pConfig->eDetail; + u64 cksum = *pCksum; + Fts5IndexIter *pIter = 0; +- int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter); ++ int rc = sqlite3Fts5IndexQuery( ++ p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter ++ ); + +- while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIter) ){ ++ while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){ + i64 rowid = pIter->iRowid; + + if( eDetail==FTS5_DETAIL_NONE ){ +@@ -222073,7 +252461,7 @@ static int fts5QueryCksum( + rc = sqlite3Fts5IterNext(pIter); + } + } +- sqlite3Fts5IterClose(pIter); ++ fts5IterClose(pIter); + + *pCksum = cksum; + return rc; +@@ -222218,7 +252606,7 @@ static void fts5IndexIntegrityCheckEmpty( + } + + static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ +- int iTermOff = 0; ++ i64 iTermOff = 0; + int ii; + + Fts5Buffer buf1 = {0,0,0}; +@@ -222227,7 +252615,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ + ii = pLeaf->szLeaf; + while( iinn && p->rc==SQLITE_OK ){ + int res; +- int iOff; ++ i64 iOff; + int nIncr; + + ii += fts5GetVarint32(&pLeaf->p[ii], nIncr); +@@ -222272,6 +252660,7 @@ static void fts5IndexIntegrityCheckSegment( + Fts5StructureSegment *pSeg /* Segment to check internal consistency */ + ){ + Fts5Config *pConfig = p->pConfig; ++ int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE); + sqlite3_stmt *pStmt = 0; + int rc2; + int iIdxPrevLeaf = pSeg->pgnoFirst-1; +@@ -222307,7 +252696,19 @@ static void fts5IndexIntegrityCheckSegment( + ** is also a rowid pointer within the leaf page header, it points to a + ** location before the term. */ + if( pLeaf->nn<=pLeaf->szLeaf ){ +- p->rc = FTS5_CORRUPT; ++ ++ if( nIdxTerm==0 ++ && pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE ++ && pLeaf->nn==pLeaf->szLeaf ++ && pLeaf->nn==4 ++ ){ ++ /* special case - the very first page in a segment keeps its %_idx ++ ** entry even if all the terms are removed from it by secure-delete ++ ** operations. */ ++ }else{ ++ p->rc = FTS5_CORRUPT; ++ } ++ + }else{ + int iOff; /* Offset of first term on leaf */ + int iRowidOff; /* Offset of first rowid on leaf */ +@@ -222371,9 +252772,12 @@ static void fts5IndexIntegrityCheckSegment( + ASSERT_SZLEAF_OK(pLeaf); + if( iRowidOff>=pLeaf->szLeaf ){ + p->rc = FTS5_CORRUPT; +- }else{ ++ }else if( bSecureDelete==0 || iRowidOff>0 ){ ++ i64 iDlRowid = fts5DlidxIterRowid(pDlidx); + fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); +- if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT; ++ if( iRowidrc = FTS5_CORRUPT; ++ } + } + fts5DataRelease(pLeaf); + } +@@ -222412,12 +252816,13 @@ static void fts5IndexIntegrityCheckSegment( + ** error, or some other SQLite error code if another error (e.g. OOM) + ** occurs. + */ +-static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ ++static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){ + int eDetail = p->pConfig->eDetail; + u64 cksum2 = 0; /* Checksum based on contents of indexes */ + Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ + Fts5Iter *pIter; /* Used to iterate through entire index */ + Fts5Structure *pStruct; /* Index structure */ ++ int iLvl, iSeg; + + #ifdef SQLITE_DEBUG + /* Used by extra internal tests only run if NDEBUG is not defined */ +@@ -222428,15 +252833,16 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ + + /* Load the FTS index structure */ + pStruct = fts5StructureRead(p); ++ if( pStruct==0 ){ ++ assert( p->rc!=SQLITE_OK ); ++ return fts5IndexReturn(p); ++ } + + /* Check that the internal nodes of each segment match the leaves */ +- if( pStruct ){ +- int iLvl, iSeg; +- for(iLvl=0; iLvlnLevel; iLvl++){ +- for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ +- Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; +- fts5IndexIntegrityCheckSegment(p, pSeg); +- } ++ for(iLvl=0; iLvlnLevel; iLvl++){ ++ for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ ++ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; ++ fts5IndexIntegrityCheckSegment(p, pSeg); + } + } + +@@ -222465,6 +252871,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ + + /* If this is a new term, query for it. Update cksum3 with the results. */ + fts5TestTerm(p, &term, z, n, cksum2, &cksum3); ++ if( p->rc ) break; + + if( eDetail==FTS5_DETAIL_NONE ){ + if( 0==fts5MultiIterIsEmpty(p, pIter) ){ +@@ -222473,6 +252880,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ + }else{ + poslist.n = 0; + fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); ++ fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0"); + while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ + int iCol = FTS5_POS2COLUMN(iPos); + int iTokOff = FTS5_POS2OFFSET(iPos); +@@ -222483,7 +252891,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ + fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3); + + fts5MultiIterFree(pIter); +- if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT; ++ if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT; + + fts5StructureRelease(pStruct); + #ifdef SQLITE_DEBUG +@@ -222499,12 +252907,14 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ + ** function only. + */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + /* + ** Decode a segment-data rowid from the %_data table. This function is + ** the opposite of macro FTS5_SEGMENT_ROWID(). + */ + static void fts5DecodeRowid( + i64 iRowid, /* Rowid from %_data table */ ++ int *pbTombstone, /* OUT: Tombstone hash flag */ + int *piSegid, /* OUT: Segment id */ + int *pbDlidx, /* OUT: Dlidx flag */ + int *piHeight, /* OUT: Height */ +@@ -222520,11 +252930,16 @@ static void fts5DecodeRowid( + iRowid >>= FTS5_DATA_DLI_B; + + *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); ++ iRowid >>= FTS5_DATA_ID_B; ++ ++ *pbTombstone = (int)(iRowid & 0x0001); + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ +- int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */ +- fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno); ++ int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */ ++ fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); + + if( iSegid==0 ){ + if( iKey==FTS5_AVERAGES_ROWID ){ +@@ -222534,12 +252949,16 @@ static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ + } + } + else{ +- sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%ssegid=%d h=%d pgno=%d}", +- bDlidx ? "dlidx " : "", iSegid, iHeight, iPgno ++ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%s%ssegid=%d h=%d pgno=%d}", ++ bDlidx ? "dlidx " : "", ++ bTomb ? "tombstone " : "", ++ iSegid, iHeight, iPgno + ); + } + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + static void fts5DebugStructure( + int *pRc, /* IN/OUT: error code */ + Fts5Buffer *pBuf, +@@ -222554,14 +252973,22 @@ static void fts5DebugStructure( + ); + for(iSeg=0; iSegnSeg; iSeg++){ + Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; +- sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d}", ++ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d", + pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast + ); ++ if( pSeg->iOrigin1>0 ){ ++ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " origin=%lld..%lld", ++ pSeg->iOrigin1, pSeg->iOrigin2 ++ ); ++ } ++ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); + } + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); + } + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + /* + ** This is part of the fts5_decode() debugging aid. + ** +@@ -222586,7 +253013,9 @@ static void fts5DecodeStructure( + fts5DebugStructure(pRc, pBuf, p); + fts5StructureRelease(p); + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + /* + ** This is part of the fts5_decode() debugging aid. + ** +@@ -222609,7 +253038,9 @@ static void fts5DecodeAverages( + zSpace = " "; + } + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + /* + ** Buffer (a/n) is assumed to contain a list of serialized varints. Read + ** each varint and append its string representation to buffer pBuf. Return +@@ -222626,7 +253057,9 @@ static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ + } + return iOff; + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + /* + ** The start of buffer (a/n) contains the start of a doclist. The doclist + ** may or may not finish within the buffer. This function appends a text +@@ -222659,7 +253092,9 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ + + return iOff; + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + /* + ** This function is part of the fts5_decode() debugging function. It is + ** only ever used with detail=none tables. +@@ -222700,7 +253135,27 @@ static void fts5DecodeRowidList( + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); + } + } ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ ++ ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) ++static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){ ++ int ii; ++ fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1); ++ if( *pRc==SQLITE_OK ){ ++ for(ii=0; iin; ii++){ ++ if( pTerm->p[ii]==0x00 ){ ++ pBuf->p[pBuf->n++] = '\\'; ++ pBuf->p[pBuf->n++] = '0'; ++ }else{ ++ pBuf->p[pBuf->n++] = pTerm->p[ii]; ++ } ++ } ++ pBuf->p[pBuf->n] = 0x00; ++ } ++} ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + /* + ** The implementation of user-defined scalar function fts5_decode(). + */ +@@ -222711,6 +253166,7 @@ static void fts5DecodeFunction( + ){ + i64 iRowid; /* Rowid for record being decoded */ + int iSegid,iHeight,iPgno,bDlidx;/* Rowid components */ ++ int bTomb; + const u8 *aBlob; int n; /* Record to decode */ + u8 *a = 0; + Fts5Buffer s; /* Build up text to return here */ +@@ -222728,12 +253184,12 @@ static void fts5DecodeFunction( + ** buffer overreads even if the record is corrupt. */ + n = sqlite3_value_bytes(apVal[1]); + aBlob = sqlite3_value_blob(apVal[1]); +- nSpace = n + FTS5_DATA_ZERO_PADDING; ++ nSpace = ((i64)n) + FTS5_DATA_ZERO_PADDING; + a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace); + if( a==0 ) goto decode_out; + if( n>0 ) memcpy(a, aBlob, n); + +- fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno); ++ fts5DecodeRowid(iRowid, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); + + fts5DebugRowid(&rc, &s, iRowid); + if( bDlidx ){ +@@ -222752,6 +253208,28 @@ static void fts5DecodeFunction( + " %d(%lld)", lvl.iLeafPgno, lvl.iRowid + ); + } ++ }else if( bTomb ){ ++ u32 nElem = fts5GetU32(&a[4]); ++ int szKey = (aBlob[0]==4 || aBlob[0]==8) ? aBlob[0] : 8; ++ int nSlot = (n - 8) / szKey; ++ int ii; ++ sqlite3Fts5BufferAppendPrintf(&rc, &s, " nElem=%d", (int)nElem); ++ if( aBlob[1] ){ ++ sqlite3Fts5BufferAppendPrintf(&rc, &s, " 0"); ++ } ++ for(ii=0; iiestimatedCost = (double)100; ++ pIdxInfo->estimatedRows = 100; ++ pIdxInfo->idxNum = 0; ++ for(i=0, p=pIdxInfo->aConstraint; inConstraint; i++, p++){ ++ if( p->usable==0 ) continue; ++ if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==11 ){ ++ rc = SQLITE_OK; ++ pIdxInfo->aConstraintUsage[i].omit = 1; ++ pIdxInfo->aConstraintUsage[i].argvIndex = 1; ++ break; ++ } ++ } ++ return rc; ++} ++ ++/* ++** This method is the destructor for bytecodevtab objects. ++*/ ++static int fts5structDisconnectMethod(sqlite3_vtab *pVtab){ ++ Fts5StructVtab *p = (Fts5StructVtab*)pVtab; ++ sqlite3_free(p); ++ return SQLITE_OK; ++} ++ ++/* ++** Constructor for a new bytecodevtab_cursor object. ++*/ ++static int fts5structOpenMethod(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){ ++ int rc = SQLITE_OK; ++ Fts5StructVcsr *pNew = 0; ++ ++ pNew = sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); ++ *ppCsr = (sqlite3_vtab_cursor*)pNew; ++ ++ return SQLITE_OK; ++} ++ ++/* ++** Destructor for a bytecodevtab_cursor. ++*/ ++static int fts5structCloseMethod(sqlite3_vtab_cursor *cur){ ++ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; ++ fts5StructureRelease(pCsr->pStruct); ++ sqlite3_free(pCsr); ++ return SQLITE_OK; ++} ++ ++ ++/* ++** Advance a bytecodevtab_cursor to its next row of output. ++*/ ++static int fts5structNextMethod(sqlite3_vtab_cursor *cur){ ++ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; ++ Fts5Structure *p = pCsr->pStruct; ++ ++ assert( pCsr->pStruct ); ++ pCsr->iSeg++; ++ pCsr->iRowid++; ++ while( pCsr->iLevelnLevel && pCsr->iSeg>=p->aLevel[pCsr->iLevel].nSeg ){ ++ pCsr->iLevel++; ++ pCsr->iSeg = 0; ++ } ++ if( pCsr->iLevel>=p->nLevel ){ ++ fts5StructureRelease(pCsr->pStruct); ++ pCsr->pStruct = 0; ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Return TRUE if the cursor has been moved off of the last ++** row of output. ++*/ ++static int fts5structEofMethod(sqlite3_vtab_cursor *cur){ ++ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; ++ return pCsr->pStruct==0; ++} ++ ++static int fts5structRowidMethod( ++ sqlite3_vtab_cursor *cur, ++ sqlite_int64 *piRowid ++){ ++ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; ++ *piRowid = pCsr->iRowid; ++ return SQLITE_OK; ++} ++ ++/* ++** Return values of columns for the row at which the bytecodevtab_cursor ++** is currently pointing. ++*/ ++static int fts5structColumnMethod( ++ sqlite3_vtab_cursor *cur, /* The cursor */ ++ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ ++ int i /* Which column to return */ ++){ ++ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; ++ Fts5Structure *p = pCsr->pStruct; ++ Fts5StructureSegment *pSeg = &p->aLevel[pCsr->iLevel].aSeg[pCsr->iSeg]; ++ ++ switch( i ){ ++ case 0: /* level */ ++ sqlite3_result_int(ctx, pCsr->iLevel); ++ break; ++ case 1: /* segment */ ++ sqlite3_result_int(ctx, pCsr->iSeg); ++ break; ++ case 2: /* merge */ ++ sqlite3_result_int(ctx, pCsr->iSeg < p->aLevel[pCsr->iLevel].nMerge); ++ break; ++ case 3: /* segid */ ++ sqlite3_result_int(ctx, pSeg->iSegid); ++ break; ++ case 4: /* leaf1 */ ++ sqlite3_result_int(ctx, pSeg->pgnoFirst); ++ break; ++ case 5: /* leaf2 */ ++ sqlite3_result_int(ctx, pSeg->pgnoLast); ++ break; ++ case 6: /* origin1 */ ++ sqlite3_result_int64(ctx, pSeg->iOrigin1); ++ break; ++ case 7: /* origin2 */ ++ sqlite3_result_int64(ctx, pSeg->iOrigin2); ++ break; ++ case 8: /* npgtombstone */ ++ sqlite3_result_int(ctx, pSeg->nPgTombstone); ++ break; ++ case 9: /* nentrytombstone */ ++ sqlite3_result_int64(ctx, pSeg->nEntryTombstone); ++ break; ++ case 10: /* nentry */ ++ sqlite3_result_int64(ctx, pSeg->nEntry); ++ break; ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Initialize a cursor. ++** ++** idxNum==0 means show all subprograms ++** idxNum==1 means show only the main bytecode and omit subprograms. ++*/ ++static int fts5structFilterMethod( ++ sqlite3_vtab_cursor *pVtabCursor, ++ int idxNum, const char *idxStr, ++ int argc, sqlite3_value **argv ++){ ++ Fts5StructVcsr *pCsr = (Fts5StructVcsr *)pVtabCursor; ++ int rc = SQLITE_OK; ++ ++ const u8 *aBlob = 0; ++ int nBlob = 0; ++ ++ assert( argc==1 ); ++ fts5StructureRelease(pCsr->pStruct); ++ pCsr->pStruct = 0; ++ ++ nBlob = sqlite3_value_bytes(argv[0]); ++ aBlob = (const u8*)sqlite3_value_blob(argv[0]); ++ rc = fts5StructureDecode(aBlob, nBlob, 0, &pCsr->pStruct); ++ if( rc==SQLITE_OK ){ ++ pCsr->iLevel = 0; ++ pCsr->iRowid = 0; ++ pCsr->iSeg = -1; ++ rc = fts5structNextMethod(pVtabCursor); ++ } ++ ++ return rc; ++} ++ ++#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + + /* + ** This is called as part of registering the FTS5 module with database +@@ -222953,6 +253663,7 @@ static void fts5RowidFunction( + ** SQLite error code is returned instead. + */ + static int sqlite3Fts5IndexInit(sqlite3 *db){ ++#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) + int rc = sqlite3_create_function( + db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0 + ); +@@ -222969,7 +253680,42 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){ + db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0 + ); + } ++ ++ if( rc==SQLITE_OK ){ ++ static const sqlite3_module fts5structure_module = { ++ 0, /* iVersion */ ++ 0, /* xCreate */ ++ fts5structConnectMethod, /* xConnect */ ++ fts5structBestIndexMethod, /* xBestIndex */ ++ fts5structDisconnectMethod, /* xDisconnect */ ++ 0, /* xDestroy */ ++ fts5structOpenMethod, /* xOpen */ ++ fts5structCloseMethod, /* xClose */ ++ fts5structFilterMethod, /* xFilter */ ++ fts5structNextMethod, /* xNext */ ++ fts5structEofMethod, /* xEof */ ++ fts5structColumnMethod, /* xColumn */ ++ fts5structRowidMethod, /* xRowid */ ++ 0, /* xUpdate */ ++ 0, /* xBegin */ ++ 0, /* xSync */ ++ 0, /* xCommit */ ++ 0, /* xRollback */ ++ 0, /* xFindFunction */ ++ 0, /* xRename */ ++ 0, /* xSavepoint */ ++ 0, /* xRelease */ ++ 0, /* xRollbackTo */ ++ 0, /* xShadowName */ ++ 0 /* xIntegrity */ ++ }; ++ rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0); ++ } + return rc; ++#else ++ return SQLITE_OK; ++ UNUSED_PARAM(db); ++#endif + } + + +@@ -223005,7 +253751,9 @@ static int sqlite3Fts5IndexReset(Fts5Index *p){ + ** assert() conditions in the fts5 code are activated - conditions that are + ** only true if it is guaranteed that the fts5 database is not corrupt. + */ ++#ifdef SQLITE_DEBUG + SQLITE_API int sqlite3_fts5_may_be_corrupt = 1; ++#endif + + + typedef struct Fts5Auxdata Fts5Auxdata; +@@ -223064,8 +253812,18 @@ struct Fts5Global { + Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ + Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ + Fts5Cursor *pCsr; /* First in list of all open cursors */ ++ u32 aLocaleHdr[4]; + }; + ++/* ++** Size of header on fts5_locale() values. And macro to access a buffer ++** containing a copy of the header from an Fts5Config pointer. ++*/ ++#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) ++#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr)) ++ ++#define FTS5_INSTTOKEN_SUBTYPE 73 ++ + /* + ** Each auxiliary function registered with the FTS5 module is represented + ** by an object of the following type. All such objects are stored as part +@@ -223084,11 +253842,28 @@ struct Fts5Auxiliary { + ** Each tokenizer module registered with the FTS5 module is represented + ** by an object of the following type. All such objects are stored as part + ** of the Fts5Global.pTok list. ++** ++** bV2Native: ++** True if the tokenizer was registered using xCreateTokenizer_v2(), false ++** for xCreateTokenizer(). If this variable is true, then x2 is populated ++** with the routines as supplied by the caller and x1 contains synthesized ++** wrapper routines. In this case the user-data pointer passed to ++** x1.xCreate should be a pointer to the Fts5TokenizerModule structure, ++** not a copy of pUserData. ++** ++** Of course, if bV2Native is false, then x1 contains the real routines and ++** x2 the synthesized ones. In this case a pointer to the Fts5TokenizerModule ++** object should be passed to x2.xCreate. ++** ++** The synthesized wrapper routines are necessary for xFindTokenizer(_v2) ++** calls. + */ + struct Fts5TokenizerModule { + char *zName; /* Name of tokenizer */ + void *pUserData; /* User pointer passed to xCreate() */ +- fts5_tokenizer x; /* Tokenizer functions */ ++ int bV2Native; /* True if v2 native tokenizer */ ++ fts5_tokenizer x1; /* Tokenizer functions */ ++ fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ + void (*xDestroy)(void*); /* Destructor function */ + Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ + }; +@@ -223098,6 +253873,8 @@ struct Fts5FullTable { + Fts5Storage *pStorage; /* Document store */ + Fts5Global *pGlobal; /* Global (connection wide) data */ + Fts5Cursor *pSortCsr; /* Sort data from this cursor */ ++ int iSavepoint; /* Successful xSavepoint()+1 */ ++ + #ifdef SQLITE_DEBUG + struct Fts5TransactionState ts; + #endif +@@ -223122,9 +253899,11 @@ struct Fts5Sorter { + i64 iRowid; /* Current rowid */ + const u8 *aPoslist; /* Position lists for current row */ + int nIdx; /* Number of entries in aIdx[] */ +- int aIdx[1]; /* Offsets into aPoslist for current row */ ++ int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */ + }; + ++/* Size (int bytes) of an Fts5Sorter object with N indexes */ ++#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64)) + + /* + ** Virtual-table cursor object. +@@ -223174,7 +253953,7 @@ struct Fts5Cursor { + Fts5Auxiliary *pAux; /* Currently executing extension function */ + Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ + +- /* Cache used by auxiliary functions xInst() and xInstCount() */ ++ /* Cache used by auxiliary API functions xInst() and xInstCount() */ + Fts5PoslistReader *aInstIter; /* One for each phrase */ + int nInstAlloc; /* Size of aInst[] array (entries / 3) */ + int nInstCount; /* Number of phrase instances */ +@@ -223241,7 +254020,7 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ + break; + + case FTS5_SYNC: +- assert( p->ts.eState==1 ); ++ assert( p->ts.eState==1 || p->ts.eState==2 ); + p->ts.eState = 2; + break; + +@@ -223256,21 +254035,21 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ + break; + + case FTS5_SAVEPOINT: +- assert( p->ts.eState==1 ); ++ assert( p->ts.eState>=1 ); + assert( iSavepoint>=0 ); + assert( iSavepoint>=p->ts.iSavepoint ); + p->ts.iSavepoint = iSavepoint; + break; + + case FTS5_RELEASE: +- assert( p->ts.eState==1 ); ++ assert( p->ts.eState>=1 ); + assert( iSavepoint>=0 ); + assert( iSavepoint<=p->ts.iSavepoint ); + p->ts.iSavepoint = iSavepoint-1; + break; + + case FTS5_ROLLBACKTO: +- assert( p->ts.eState==1 ); ++ assert( p->ts.eState>=1 ); + assert( iSavepoint>=-1 ); + /* The following assert() can fail if another vtab strikes an error + ** within an xSavepoint() call then SQLite calls xRollbackTo() - without +@@ -223285,10 +254064,16 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ + #endif + + /* +-** Return true if pTab is a contentless table. ++** Return true if pTab is a contentless table. If parameter bIncludeUnindexed ++** is true, this includes contentless tables that store UNINDEXED columns ++** only. + */ +-static int fts5IsContentless(Fts5FullTable *pTab){ +- return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE; ++static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){ ++ int eContent = pTab->p.pConfig->eContent; ++ return ( ++ eContent==FTS5_CONTENT_NONE ++ || (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED) ++ ); + } + + /* +@@ -223356,8 +254141,12 @@ static int fts5InitVtab( + assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 ); + } + if( rc==SQLITE_OK ){ ++ pConfig->pzErrmsg = pzErr; + pTab->p.pConfig = pConfig; + pTab->pGlobal = pGlobal; ++ if( bCreate || sqlite3Fts5TokenizerPreload(&pConfig->t) ){ ++ rc = sqlite3Fts5LoadTokenizer(pConfig); ++ } + } + + /* Open the index sub-system */ +@@ -223379,13 +254168,17 @@ static int fts5InitVtab( + + /* Load the initial configuration */ + if( rc==SQLITE_OK ){ +- assert( pConfig->pzErrmsg==0 ); +- pConfig->pzErrmsg = pzErr; +- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); +- sqlite3Fts5IndexRollback(pTab->p.pIndex); +- pConfig->pzErrmsg = 0; ++ rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie-1); ++ } ++ ++ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ ++ rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1); ++ } ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); + } + ++ if( pConfig ) pConfig->pzErrmsg = 0; + if( rc!=SQLITE_OK ){ + fts5FreeVtab(pTab); + pTab = 0; +@@ -223447,6 +254240,23 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ + #endif + } + ++static int fts5UsePatternMatch( ++ Fts5Config *pConfig, ++ struct sqlite3_index_constraint *p ++){ ++ assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB ); ++ assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE ); ++ if( pConfig->t.ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){ ++ return 1; ++ } ++ if( pConfig->t.ePattern==FTS5_PATTERN_LIKE ++ && (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB) ++ ){ ++ return 1; ++ } ++ return 0; ++} ++ + /* + ** Implementation of the xBestIndex method for FTS5 tables. Within the + ** WHERE constraint, it searches for the following: +@@ -223476,7 +254286,9 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ + ** + ** Match against table column: "m" + ** Match against rank column: "r" +-** Match against other column: "" ++** Match against other column: "M" ++** LIKE against other column: "L" ++** GLOB against other column: "G" + ** Equality constraint against the rowid: "=" + ** A < or <= against the rowid: "<" + ** A > or >= against the rowid: ">" +@@ -223484,10 +254296,10 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ + ** This function ensures that there is at most one "r" or "=". And that if + ** there exists an "=" then there is no "<" or ">". + ** +-** Costs are assigned as follows: ++** If an unusable MATCH operator is present in the WHERE clause, then ++** SQLITE_CONSTRAINT is returned. + ** +-** a) If an unusable MATCH operator is present in the WHERE clause, the +-** cost is unconditionally set to 1e50 (a really big number). ++** Costs are assigned as follows: + ** + ** a) If a MATCH operator is present, the cost depends on the other + ** constraints also present. As follows: +@@ -223520,7 +254332,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ + int bSeenEq = 0; + int bSeenGt = 0; + int bSeenLt = 0; +- int bSeenMatch = 0; ++ int nSeenMatch = 0; + int bSeenRank = 0; + + +@@ -223537,7 +254349,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ + return SQLITE_ERROR; + } + +- idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1); ++ idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1); + if( idxStr==0 ) return SQLITE_NOMEM; + pInfo->idxStr = idxStr; + pInfo->needToFreeIdxStr = 1; +@@ -223551,35 +254363,38 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ + /* A MATCH operator or equivalent */ + if( p->usable==0 || iCol<0 ){ + /* As there exists an unusable MATCH constraint this is an +- ** unusable plan. Set a prohibitively high cost. */ +- pInfo->estimatedCost = 1e50; +- assert( iIdxStr < pInfo->nConstraint*6 + 1 ); ++ ** unusable plan. Return SQLITE_CONSTRAINT. */ + idxStr[iIdxStr] = 0; +- return SQLITE_OK; ++ return SQLITE_CONSTRAINT; + }else{ + if( iCol==nCol+1 ){ + if( bSeenRank ) continue; + idxStr[iIdxStr++] = 'r'; + bSeenRank = 1; + }else{ +- bSeenMatch = 1; +- idxStr[iIdxStr++] = 'm'; +- if( iColaConstraintUsage[i].argvIndex = ++iCons; + pInfo->aConstraintUsage[i].omit = 1; + } +- } +- else if( p->usable && bSeenEq==0 +- && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 +- ){ +- idxStr[iIdxStr++] = '='; +- bSeenEq = 1; +- pInfo->aConstraintUsage[i].argvIndex = ++iCons; ++ }else if( p->usable ){ ++ if( iCol>=0 && iColop==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB ); ++ idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G'; ++ sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol); ++ idxStr += strlen(&idxStr[iIdxStr]); ++ pInfo->aConstraintUsage[i].argvIndex = ++iCons; ++ assert( idxStr[iIdxStr]=='\0' ); ++ nSeenMatch++; ++ }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){ ++ idxStr[iIdxStr++] = '='; ++ bSeenEq = 1; ++ pInfo->aConstraintUsage[i].argvIndex = ++iCons; ++ } + } + } + +@@ -223605,12 +254420,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ + } + idxStr[iIdxStr] = '\0'; + +- /* Set idxFlags flags for the ORDER BY clause */ ++ /* Set idxFlags flags for the ORDER BY clause ++ ** ++ ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC". ++ */ + if( pInfo->nOrderBy==1 ){ + int iSort = pInfo->aOrderBy[0].iColumn; +- if( iSort==(pConfig->nCol+1) && bSeenMatch ){ ++ if( iSort==(pConfig->nCol+1) && nSeenMatch>0 ){ + idxFlags |= FTS5_BI_ORDER_RANK; +- }else if( iSort==-1 ){ ++ }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){ + idxFlags |= FTS5_BI_ORDER_ROWID; + } + if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){ +@@ -223623,14 +254441,17 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ + + /* Calculate the estimated cost based on the flags set in idxFlags. */ + if( bSeenEq ){ +- pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0; +- if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo); ++ pInfo->estimatedCost = nSeenMatch ? 1000.0 : 10.0; ++ if( nSeenMatch==0 ) fts5SetUniqueFlag(pInfo); + }else if( bSeenLt && bSeenGt ){ +- pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0; ++ pInfo->estimatedCost = nSeenMatch ? 5000.0 : 250000.0; + }else if( bSeenLt || bSeenGt ){ +- pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0; ++ pInfo->estimatedCost = nSeenMatch ? 7500.0 : 750000.0; + }else{ +- pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0; ++ pInfo->estimatedCost = nSeenMatch ? 10000.0 : 1000000.0; ++ } ++ for(i=1; iestimatedCost *= 0.4; + } + + pInfo->idxNum = idxFlags; +@@ -223762,7 +254583,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){ + rc = sqlite3_step(pSorter->pStmt); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; +- CsrFlagSet(pCsr, FTS5CSR_EOF); ++ CsrFlagSet(pCsr, FTS5CSR_EOF|FTS5CSR_REQUIRE_CONTENT); + }else if( rc==SQLITE_ROW ){ + const u8 *a; + const u8 *aBlob; +@@ -223862,6 +254683,16 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ + ); + assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) ); + ++ /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table, ++ ** clear any token mappings accumulated at the fts5_index.c level. In ++ ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH, ++ ** we need to retain the mappings for the entire query. */ ++ if( pCsr->ePlan==FTS5_PLAN_MATCH ++ && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata ++ ){ ++ sqlite3Fts5ExprClearTokens(pCsr->pExpr); ++ } ++ + if( pCsr->ePlan<3 ){ + int bSkip = 0; + if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; +@@ -223896,6 +254727,7 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ + } + }else{ + rc = SQLITE_OK; ++ CsrFlagSet(pCsr, FTS5CSR_REQUIRE_DOCSIZE); + } + break; + } +@@ -223925,7 +254757,7 @@ static int fts5PrepareStatement( + rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, + SQLITE_PREPARE_PERSISTENT, &pRet, 0); + if( rc!=SQLITE_OK ){ +- *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); ++ sqlite3Fts5ConfigErrmsg(pConfig, "%s", sqlite3_errmsg(pConfig->db)); + } + sqlite3_free(zSql); + } +@@ -223949,7 +254781,7 @@ static int fts5CursorFirstSorted( + const char *zRankArgs = pCsr->zRankArgs; + + nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); +- nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); ++ nByte = SZ_FTS5SORTER(nPhrase); + pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); + if( pSorter==0 ) return SQLITE_NOMEM; + memset(pSorter, 0, (size_t)nByte); +@@ -224149,6 +254981,145 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){ + return iDefault; + } + ++/* ++** Set the error message on the virtual table passed as the first argument. ++*/ ++static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ ++ va_list ap; /* ... printf arguments */ ++ va_start(ap, zFormat); ++ sqlite3_free(p->p.base.zErrMsg); ++ p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); ++ va_end(ap); ++} ++ ++/* ++** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale ++** specified by pLocale/nLocale. The buffer indicated by pLocale must remain ++** valid until after the final call to sqlite3Fts5Tokenize() that will use ++** the locale. ++*/ ++static void sqlite3Fts5SetLocale( ++ Fts5Config *pConfig, ++ const char *zLocale, ++ int nLocale ++){ ++ Fts5TokenizerConfig *pT = &pConfig->t; ++ pT->pLocale = zLocale; ++ pT->nLocale = nLocale; ++} ++ ++/* ++** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale(). ++*/ ++static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ ++ sqlite3Fts5SetLocale(pConfig, 0, 0); ++} ++ ++/* ++** Return true if the value passed as the only argument is an ++** fts5_locale() value. ++*/ ++static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){ ++ int ret = 0; ++ if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ ++ /* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case. ++ ** If the blob was created using zeroblob(), then sqlite3_value_blob() ++ ** may call malloc(). If this malloc() fails, then the values returned ++ ** by both value_blob() and value_bytes() will be 0. If value_bytes() were ++ ** called first, then the NULL pointer returned by value_blob() might ++ ** be dereferenced. */ ++ const u8 *pBlob = sqlite3_value_blob(pVal); ++ int nBlob = sqlite3_value_bytes(pVal); ++ if( nBlob>FTS5_LOCALE_HDR_SIZE ++ && 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig), FTS5_LOCALE_HDR_SIZE) ++ ){ ++ ret = 1; ++ } ++ } ++ return ret; ++} ++ ++/* ++** Value pVal is guaranteed to be an fts5_locale() value, according to ++** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale ++** from the value and returns them separately. ++** ++** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set ++** to point to buffers containing the text and locale, as utf-8, ++** respectively. In this case output parameters (*pnText) and (*pnLoc) are ++** set to the sizes in bytes of these two buffers. ++** ++** Or, if an error occurs, then an SQLite error code is returned. The final ++** value of the four output parameters is undefined in this case. ++*/ ++static int sqlite3Fts5DecodeLocaleValue( ++ sqlite3_value *pVal, ++ const char **ppText, ++ int *pnText, ++ const char **ppLoc, ++ int *pnLoc ++){ ++ const char *p = sqlite3_value_blob(pVal); ++ int n = sqlite3_value_bytes(pVal); ++ int nLoc = 0; ++ ++ assert( sqlite3_value_type(pVal)==SQLITE_BLOB ); ++ assert( n>FTS5_LOCALE_HDR_SIZE ); ++ ++ for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){ ++ if( nLoc==(n-1) ){ ++ return SQLITE_MISMATCH; ++ } ++ } ++ *ppLoc = &p[FTS5_LOCALE_HDR_SIZE]; ++ *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE; ++ ++ *ppText = &p[nLoc+1]; ++ *pnText = n - nLoc - 1; ++ return SQLITE_OK; ++} ++ ++/* ++** Argument pVal is the text of a full-text search expression. It may or ++** may not have been wrapped by fts5_locale(). This function extracts ++** the text of the expression, and sets output variable (*pzText) to ++** point to a nul-terminated buffer containing the expression. ++** ++** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called ++** to set the tokenizer to use the specified locale. ++** ++** If output variable (*pbFreeAndReset) is set to true, then the caller ++** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer ++** locale, and (b) call sqlite3_free() to free (*pzText). ++*/ ++static int fts5ExtractExprText( ++ Fts5Config *pConfig, /* Fts5 configuration */ ++ sqlite3_value *pVal, /* Value to extract expression text from */ ++ char **pzText, /* OUT: nul-terminated buffer of text */ ++ int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ ++){ ++ int rc = SQLITE_OK; ++ ++ if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ ++ const char *pText = 0; ++ int nText = 0; ++ const char *pLoc = 0; ++ int nLoc = 0; ++ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); ++ *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText); ++ if( rc==SQLITE_OK ){ ++ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); ++ } ++ *pbFreeAndReset = 1; ++ }else{ ++ *pzText = (char*)sqlite3_value_text(pVal); ++ *pbFreeAndReset = 0; ++ } ++ ++ return rc; ++} ++ ++ + /* + ** This is the xFilter interface for the virtual table. See + ** the virtual table xFilter method documentation for additional +@@ -224179,17 +255150,12 @@ static int fts5FilterMethod( + sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ + int iCol; /* Column on LHS of MATCH operator */ + char **pzErrmsg = pConfig->pzErrmsg; ++ int bPrefixInsttoken = pConfig->bPrefixInsttoken; + int i; + int iIdxStr = 0; + Fts5Expr *pExpr = 0; + +- if( pConfig->bLock ){ +- pTab->p.base.zErrMsg = sqlite3_mprintf( +- "recursively defined fts5 content table" +- ); +- return SQLITE_ERROR; +- } +- ++ assert( pConfig->bLock==0 ); + if( pCsr->ePlan ){ + fts5FreeCursorComponents(pCsr); + memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); +@@ -224212,36 +255178,65 @@ static int fts5FilterMethod( + case 'r': + pRank = apVal[i]; + break; +- case 'm': { +- const char *zText = (const char*)sqlite3_value_text(apVal[i]); +- if( zText==0 ) zText = ""; ++ case 'M': { ++ char *zText = 0; ++ int bFreeAndReset = 0; ++ int bInternal = 0; + +- if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){ +- iCol = 0; +- do{ +- iCol = iCol*10 + (idxStr[iIdxStr]-'0'); +- iIdxStr++; +- }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); +- }else{ +- iCol = pConfig->nCol; ++ rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); ++ if( rc!=SQLITE_OK ) goto filter_out; ++ if( zText==0 ) zText = ""; ++ if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){ ++ pConfig->bPrefixInsttoken = 1; + } + ++ iCol = 0; ++ do{ ++ iCol = iCol*10 + (idxStr[iIdxStr]-'0'); ++ iIdxStr++; ++ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); ++ + if( zText[0]=='*' ){ + /* The user has issued a query of the form "MATCH '*...'". This + ** indicates that the MATCH expression is not a full text query, + ** but a request for an internal parameter. */ + rc = fts5SpecialMatch(pTab, pCsr, &zText[1]); +- goto filter_out; ++ bInternal = 1; + }else{ + char **pzErr = &pTab->p.base.zErrMsg; +- rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr); ++ rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); + pExpr = 0; + } +- if( rc!=SQLITE_OK ) goto filter_out; + } + ++ if( bFreeAndReset ){ ++ sqlite3_free(zText); ++ sqlite3Fts5ClearLocale(pConfig); ++ } ++ ++ if( bInternal || rc!=SQLITE_OK ) goto filter_out; ++ ++ break; ++ } ++ case 'L': ++ case 'G': { ++ int bGlob = (idxStr[iIdxStr-1]=='G'); ++ const char *zText = (const char*)sqlite3_value_text(apVal[i]); ++ iCol = 0; ++ do{ ++ iCol = iCol*10 + (idxStr[iIdxStr]-'0'); ++ iIdxStr++; ++ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); ++ if( zText ){ ++ rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr); ++ } ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); ++ pExpr = 0; ++ } ++ if( rc!=SQLITE_OK ) goto filter_out; + break; + } + case '=': +@@ -224273,6 +255268,9 @@ static int fts5FilterMethod( + pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64); + } + ++ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); ++ if( rc!=SQLITE_OK ) goto filter_out; ++ + if( pTab->pSortCsr ){ + /* If pSortCsr is non-NULL, then this call is being made as part of + ** processing for a "... MATCH ORDER BY rank" query (ePlan is +@@ -224295,6 +255293,7 @@ static int fts5FilterMethod( + pCsr->pExpr = pTab->pSortCsr->pExpr; + rc = fts5CursorFirst(pTab, pCsr, bDesc); + }else if( pCsr->pExpr ){ ++ assert( rc==SQLITE_OK ); + rc = fts5CursorParseRank(pConfig, pCsr, pRank); + if( rc==SQLITE_OK ){ + if( bOrderByRank ){ +@@ -224306,9 +255305,7 @@ static int fts5FilterMethod( + } + } + }else if( pConfig->zContent==0 ){ +- *pConfig->pzErrmsg = sqlite3_mprintf( +- "%s: table does not support scanning", pConfig->zName +- ); ++ fts5SetVtabError(pTab,"%s: table does not support scanning",pConfig->zName); + rc = SQLITE_ERROR; + }else{ + /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup +@@ -224318,7 +255315,8 @@ static int fts5FilterMethod( + pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg + ); + if( rc==SQLITE_OK ){ +- if( pCsr->ePlan==FTS5_PLAN_ROWID ){ ++ if( pRowidEq!=0 ){ ++ assert( pCsr->ePlan==FTS5_PLAN_ROWID ); + sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq); + }else{ + sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid); +@@ -224331,6 +255329,7 @@ static int fts5FilterMethod( + filter_out: + sqlite3Fts5ExprFree(pExpr); + pConfig->pzErrmsg = pzErrmsg; ++ pConfig->bPrefixInsttoken = bPrefixInsttoken; + return rc; + } + +@@ -224350,9 +255349,13 @@ static i64 fts5CursorRowid(Fts5Cursor *pCsr){ + assert( pCsr->ePlan==FTS5_PLAN_MATCH + || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH + || pCsr->ePlan==FTS5_PLAN_SOURCE ++ || pCsr->ePlan==FTS5_PLAN_SCAN ++ || pCsr->ePlan==FTS5_PLAN_ROWID + ); + if( pCsr->pSorter ){ + return pCsr->pSorter->iRowid; ++ }else if( pCsr->ePlan>=FTS5_PLAN_SCAN ){ ++ return sqlite3_column_int64(pCsr->pStmt, 0); + }else{ + return sqlite3Fts5ExprRowid(pCsr->pExpr); + } +@@ -224369,25 +255372,16 @@ static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + int ePlan = pCsr->ePlan; + + assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); +- switch( ePlan ){ +- case FTS5_PLAN_SPECIAL: +- *pRowid = 0; +- break; +- +- case FTS5_PLAN_SOURCE: +- case FTS5_PLAN_MATCH: +- case FTS5_PLAN_SORTED_MATCH: +- *pRowid = fts5CursorRowid(pCsr); +- break; +- +- default: +- *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); +- break; ++ if( ePlan==FTS5_PLAN_SPECIAL ){ ++ *pRowid = 0; ++ }else{ ++ *pRowid = fts5CursorRowid(pCsr); + } + + return SQLITE_OK; + } + ++ + /* + ** If the cursor requires seeking (bSeekRequired flag is set), seek it. + ** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise. +@@ -224424,8 +255418,13 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ + rc = sqlite3_reset(pCsr->pStmt); + if( rc==SQLITE_OK ){ + rc = FTS5_CORRUPT; ++ fts5SetVtabError((Fts5FullTable*)pTab, ++ "fts5: missing row %lld from content table %s", ++ fts5CursorRowid(pCsr), ++ pTab->pConfig->zContent ++ ); + }else if( pTab->pConfig->pzErrmsg ){ +- *pTab->pConfig->pzErrmsg = sqlite3_mprintf( ++ fts5SetVtabError((Fts5FullTable*)pTab, + "%s", sqlite3_errmsg(pTab->pConfig->db) + ); + } +@@ -224434,14 +255433,6 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ + return rc; + } + +-static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ +- va_list ap; /* ... printf arguments */ +- va_start(ap, zFormat); +- assert( p->p.base.zErrMsg==0 ); +- p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); +- va_end(ap); +-} +- + /* + ** This function is called to handle an FTS INSERT command. In other words, + ** an INSERT statement of the form: +@@ -224465,6 +255456,7 @@ static int fts5SpecialInsert( + Fts5Config *pConfig = pTab->p.pConfig; + int rc = SQLITE_OK; + int bError = 0; ++ int bLoadConfig = 0; + + if( 0==sqlite3_stricmp("delete-all", zCmd) ){ + if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ +@@ -224476,8 +255468,9 @@ static int fts5SpecialInsert( + }else{ + rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); + } ++ bLoadConfig = 1; + }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ +- if( pConfig->eContent==FTS5_CONTENT_NONE ){ ++ if( fts5IsContentless(pTab, 1) ){ + fts5SetVtabError(pTab, + "'rebuild' may not be used with a contentless fts5 table" + ); +@@ -224485,19 +255478,26 @@ static int fts5SpecialInsert( + }else{ + rc = sqlite3Fts5StorageRebuild(pTab->pStorage); + } ++ bLoadConfig = 1; + }else if( 0==sqlite3_stricmp("optimize", zCmd) ){ + rc = sqlite3Fts5StorageOptimize(pTab->pStorage); + }else if( 0==sqlite3_stricmp("merge", zCmd) ){ + int nMerge = sqlite3_value_int(pVal); + rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge); + }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){ +- rc = sqlite3Fts5StorageIntegrity(pTab->pStorage); ++ int iArg = sqlite3_value_int(pVal); ++ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, iArg); + #ifdef SQLITE_DEBUG + }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){ + pConfig->bPrefixIndex = sqlite3_value_int(pVal); + #endif ++ }else if( 0==sqlite3_stricmp("flush", zCmd) ){ ++ rc = sqlite3Fts5FlushToDisk(&pTab->p); + }else{ +- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); ++ rc = sqlite3Fts5FlushToDisk(&pTab->p); ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); ++ } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError); + } +@@ -224509,6 +255509,12 @@ static int fts5SpecialInsert( + } + } + } ++ ++ if( rc==SQLITE_OK && bLoadConfig ){ ++ pTab->p.pConfig->iCookie--; ++ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); ++ } ++ + return rc; + } + +@@ -224520,7 +255526,7 @@ static int fts5SpecialDelete( + int eType1 = sqlite3_value_type(apVal[1]); + if( eType1==SQLITE_INTEGER ){ + sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); +- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]); ++ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2], 0); + } + return rc; + } +@@ -224533,7 +255539,7 @@ static void fts5StorageInsert( + ){ + int rc = *pRc; + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid); ++ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid); + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); +@@ -224541,6 +255547,67 @@ static void fts5StorageInsert( + *pRc = rc; + } + ++/* ++** ++** This function is called when the user attempts an UPDATE on a contentless ++** table. Parameter bRowidModified is true if the UPDATE statement modifies ++** the rowid value. Parameter apVal[] contains the new values for each user ++** defined column of the fts5 table. pConfig is the configuration object of the ++** table being updated (guaranteed to be contentless). The contentless_delete=1 ++** and contentless_unindexed=1 options may or may not be set. ++** ++** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite ++** error code if it cannot. In this case an error message is also loaded into ++** pConfig. Output parameter (*pbContent) is set to true if the caller should ++** update the %_content table only - not the FTS index or any other shadow ++** table. This occurs when an UPDATE modifies only UNINDEXED columns of the ++** table. ++** ++** An UPDATE may proceed if: ++** ++** * The only columns modified are UNINDEXED columns, or ++** ++** * The contentless_delete=1 option was specified and all of the indexed ++** columns (not a subset) have been modified. ++*/ ++static int fts5ContentlessUpdate( ++ Fts5Config *pConfig, ++ sqlite3_value **apVal, ++ int bRowidModified, ++ int *pbContent ++){ ++ int ii; ++ int bSeenIndex = 0; /* Have seen modified indexed column */ ++ int bSeenIndexNC = 0; /* Have seen unmodified indexed column */ ++ int rc = SQLITE_OK; ++ ++ for(ii=0; iinCol; ii++){ ++ if( pConfig->abUnindexed[ii]==0 ){ ++ if( sqlite3_value_nochange(apVal[ii]) ){ ++ bSeenIndexNC++; ++ }else{ ++ bSeenIndex++; ++ } ++ } ++ } ++ ++ if( bSeenIndex==0 && bRowidModified==0 ){ ++ *pbContent = 1; ++ }else{ ++ if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){ ++ rc = SQLITE_ERROR; ++ sqlite3Fts5ConfigErrmsg(pConfig, ++ (pConfig->bContentlessDelete ? ++ "%s a subset of columns on fts5 contentless-delete table: %s" : ++ "%s contentless fts5 table: %s") ++ , "cannot UPDATE", pConfig->zName ++ ); ++ } ++ } ++ ++ return rc; ++} ++ + /* + ** This function is the implementation of the xUpdate callback used by + ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be +@@ -224567,7 +255634,7 @@ static int fts5UpdateMethod( + int rc = SQLITE_OK; /* Return code */ + + /* A transaction must be open when this is called. */ +- assert( pTab->ts.eState==1 ); ++ assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); + + assert( pVtab->zErrMsg==0 ); + assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); +@@ -224575,6 +255642,11 @@ static int fts5UpdateMethod( + || sqlite3_value_type(apVal[0])==SQLITE_NULL + ); + assert( pTab->p.pConfig->pzErrmsg==0 ); ++ if( pConfig->pgsz==0 ){ ++ rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie); ++ if( rc!=SQLITE_OK ) return rc; ++ } ++ + pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; + + /* Put any active cursors into REQUIRE_SEEK state. */ +@@ -224589,7 +255661,14 @@ static int fts5UpdateMethod( + if( pConfig->eContent!=FTS5_CONTENT_NORMAL + && 0==sqlite3_stricmp("delete", z) + ){ +- rc = fts5SpecialDelete(pTab, apVal); ++ if( pConfig->bContentlessDelete ){ ++ fts5SetVtabError(pTab, ++ "'delete' may not be used with a contentless_delete=1 table" ++ ); ++ rc = SQLITE_ERROR; ++ }else{ ++ rc = fts5SpecialDelete(pTab, apVal); ++ } + }else{ + rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); + } +@@ -224606,74 +255685,111 @@ static int fts5UpdateMethod( + ** Cases 3 and 4 may violate the rowid constraint. + */ + int eConflict = SQLITE_ABORT; +- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ ++ if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){ + eConflict = sqlite3_vtab_on_conflict(pConfig->db); + } + + assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); + assert( nArg!=1 || eType0==SQLITE_INTEGER ); + +- /* Filter out attempts to run UPDATE or DELETE on contentless tables. +- ** This is not suported. */ +- if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){ +- pTab->p.base.zErrMsg = sqlite3_mprintf( +- "cannot %s contentless fts5 table: %s", +- (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName +- ); +- rc = SQLITE_ERROR; +- } +- + /* DELETE */ +- else if( nArg==1 ){ +- i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ +- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); ++ if( nArg==1 ){ ++ /* It is only possible to DELETE from a contentless table if the ++ ** contentless_delete=1 flag is set. */ ++ if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){ ++ fts5SetVtabError(pTab, ++ "cannot DELETE from contentless fts5 table: %s", pConfig->zName ++ ); ++ rc = SQLITE_ERROR; ++ }else{ ++ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ ++ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); ++ } + } + + /* INSERT or UPDATE */ + else{ + int eType1 = sqlite3_value_numeric_type(apVal[1]); + +- if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){ +- rc = SQLITE_MISMATCH; ++ /* It is an error to write an fts5_locale() value to a table without ++ ** the locale=1 option. */ ++ if( pConfig->bLocale==0 ){ ++ int ii; ++ for(ii=0; iinCol; ii++){ ++ sqlite3_value *pVal = apVal[ii+2]; ++ if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ ++ fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); ++ rc = SQLITE_MISMATCH; ++ goto update_out; ++ } ++ } + } + +- else if( eType0!=SQLITE_INTEGER ){ +- /* If this is a REPLACE, first remove the current entry (if any) */ ++ if( eType0!=SQLITE_INTEGER ){ ++ /* An INSERT statement. If the conflict-mode is REPLACE, first remove ++ ** the current entry (if any). */ + if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ + i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ +- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); ++ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); + } + fts5StorageInsert(&rc, pTab, apVal, pRowid); + } + + /* UPDATE */ + else{ ++ Fts5Storage *pStorage = pTab->pStorage; + i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ + i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ +- if( eType1==SQLITE_INTEGER && iOld!=iNew ){ ++ int bContent = 0; /* Content only update */ ++ ++ /* If this is a contentless table (including contentless_unindexed=1 ++ ** tables), check if the UPDATE may proceed. */ ++ if( fts5IsContentless(pTab, 1) ){ ++ rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent); ++ if( rc!=SQLITE_OK ) goto update_out; ++ } ++ ++ if( eType1!=SQLITE_INTEGER ){ ++ rc = SQLITE_MISMATCH; ++ }else if( iOld!=iNew ){ ++ assert( bContent==0 ); + if( eConflict==SQLITE_REPLACE ){ +- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); ++ rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); ++ rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0); + } + fts5StorageInsert(&rc, pTab, apVal, pRowid); + }else{ +- rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); ++ rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid); ++ } + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); ++ rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0); + } + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); ++ rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid); + } + } ++ }else if( bContent ){ ++ /* This occurs when an UPDATE on a contentless table affects *only* ++ ** UNINDEXED columns. This is a no-op for contentless_unindexed=0 ++ ** tables, or a write to the %_content table only for =1 tables. */ ++ assert( fts5IsContentless(pTab, 1) ); ++ rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid); ++ } + }else{ +- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); ++ rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); + fts5StorageInsert(&rc, pTab, apVal, pRowid); + } ++ sqlite3Fts5StorageReleaseDeleteRow(pStorage); + } + } + } + ++ update_out: + pTab->p.pConfig->pzErrmsg = 0; + return rc; + } +@@ -224686,8 +255802,7 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; + fts5CheckTransactionState(pTab, FTS5_SYNC, 0); + pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; +- fts5TripCursors(pTab); +- rc = sqlite3Fts5StorageSync(pTab->pStorage); ++ rc = sqlite3Fts5FlushToDisk(&pTab->p); + pTab->p.pConfig->pzErrmsg = 0; + return rc; + } +@@ -224696,9 +255811,11 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ + ** Implementation of xBegin() method. + */ + static int fts5BeginMethod(sqlite3_vtab *pVtab){ +- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); +- fts5NewTransaction((Fts5FullTable*)pVtab); +- return SQLITE_OK; ++ int rc = fts5NewTransaction((Fts5FullTable*)pVtab); ++ if( rc==SQLITE_OK ){ ++ fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); ++ } ++ return rc; + } + + /* +@@ -224721,6 +255838,7 @@ static int fts5RollbackMethod(sqlite3_vtab *pVtab){ + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; + fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); + rc = sqlite3Fts5StorageRollback(pTab->pStorage); ++ pTab->p.pConfig->pgsz = 0; + return rc; + } + +@@ -224752,17 +255870,40 @@ static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ + return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); + } + +-static int fts5ApiTokenize( ++/* ++** Implementation of xTokenize_v2() API. ++*/ ++static int fts5ApiTokenize_v2( + Fts5Context *pCtx, + const char *pText, int nText, ++ const char *pLoc, int nLoc, + void *pUserData, + int (*xToken)(void*, int, const char*, int, int, int) + ){ + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); +- return sqlite3Fts5Tokenize( +- pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken ++ int rc = SQLITE_OK; ++ ++ sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc); ++ rc = sqlite3Fts5Tokenize(pTab->pConfig, ++ FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken + ); ++ sqlite3Fts5SetLocale(pTab->pConfig, 0, 0); ++ ++ return rc; ++} ++ ++/* ++** Implementation of xTokenize() API. This is just xTokenize_v2() with NULL/0 ++** passed as the locale. ++*/ ++static int fts5ApiTokenize( ++ Fts5Context *pCtx, ++ const char *pText, int nText, ++ void *pUserData, ++ int (*xToken)(void*, int, const char*, int, int, int) ++){ ++ return fts5ApiTokenize_v2(pCtx, pText, nText, 0, 0, pUserData, xToken); + } + + static int fts5ApiPhraseCount(Fts5Context *pCtx){ +@@ -224775,6 +255916,49 @@ static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ + return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); + } + ++/* ++** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This ++** function extracts the text value of column iCol of the current row. ++** Additionally, if there is an associated locale, it invokes ++** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller ++** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point ++** after this function returns. ++** ++** If successful, (*ppText) is set to point to a buffer containing the text ++** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that ++** buffer in bytes. It is not guaranteed to be nul-terminated. If an error ++** occurs, an SQLite error code is returned. The final values of the two ++** output parameters are undefined in this case. ++*/ ++static int fts5TextFromStmt( ++ Fts5Config *pConfig, ++ sqlite3_stmt *pStmt, ++ int iCol, ++ const char **ppText, ++ int *pnText ++){ ++ sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1); ++ const char *pLoc = 0; ++ int nLoc = 0; ++ int rc = SQLITE_OK; ++ ++ if( pConfig->bLocale ++ && pConfig->eContent==FTS5_CONTENT_EXTERNAL ++ && sqlite3Fts5IsLocaleValue(pConfig, pVal) ++ ){ ++ rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc); ++ }else{ ++ *ppText = (const char*)sqlite3_value_text(pVal); ++ *pnText = sqlite3_value_bytes(pVal); ++ if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){ ++ pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol); ++ nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol); ++ } ++ } ++ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); ++ return rc; ++} ++ + static int fts5ApiColumnText( + Fts5Context *pCtx, + int iCol, +@@ -224783,46 +255967,69 @@ static int fts5ApiColumnText( + ){ + int rc = SQLITE_OK; + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; +- if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) +- || pCsr->ePlan==FTS5_PLAN_SPECIAL +- ){ ++ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); ++ ++ assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); ++ if( iCol<0 || iCol>=pTab->pConfig->nCol ){ ++ rc = SQLITE_RANGE; ++ }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){ + *pz = 0; + *pn = 0; + }else{ + rc = fts5SeekCursor(pCsr, 0); + if( rc==SQLITE_OK ){ +- *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); +- *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); ++ rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn); ++ sqlite3Fts5ClearLocale(pTab->pConfig); + } + } + return rc; + } + ++/* ++** This is called by various API functions - xInst, xPhraseFirst, ++** xPhraseFirstColumn etc. - to obtain the position list for phrase iPhrase ++** of the current row. This function works for both detail=full tables (in ++** which case the position-list was read from the fts index) or for other ++** detail= modes if the row content is available. ++*/ + static int fts5CsrPoslist( +- Fts5Cursor *pCsr, +- int iPhrase, +- const u8 **pa, +- int *pn ++ Fts5Cursor *pCsr, /* Fts5 cursor object */ ++ int iPhrase, /* Phrase to find position list for */ ++ const u8 **pa, /* OUT: Pointer to position list buffer */ ++ int *pn /* OUT: Size of (*pa) in bytes */ + ){ + Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; + int rc = SQLITE_OK; + int bLive = (pCsr->pSorter==0); + +- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ +- ++ if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ ++ rc = SQLITE_RANGE; ++ }else if( pConfig->eDetail!=FTS5_DETAIL_FULL ++ && fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) ++ ){ ++ *pa = 0; ++ *pn = 0; ++ return SQLITE_OK; ++ }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ + if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ + Fts5PoslistPopulator *aPopulator; + int i; ++ + aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); + if( aPopulator==0 ) rc = SQLITE_NOMEM; ++ if( rc==SQLITE_OK ){ ++ rc = fts5SeekCursor(pCsr, 0); ++ } + for(i=0; inCol && rc==SQLITE_OK; i++){ +- int n; const char *z; +- rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n); ++ const char *z = 0; ++ int n = 0; ++ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5ExprPopulatePoslists( + pConfig, pCsr->pExpr, aPopulator, i, z, n + ); + } ++ sqlite3Fts5ClearLocale(pConfig); + } + sqlite3_free(aPopulator); + +@@ -224833,13 +256040,18 @@ static int fts5CsrPoslist( + CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST); + } + +- if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ +- Fts5Sorter *pSorter = pCsr->pSorter; +- int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); +- *pn = pSorter->aIdx[iPhrase] - i1; +- *pa = &pSorter->aPoslist[i1]; ++ if( rc==SQLITE_OK ){ ++ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ ++ Fts5Sorter *pSorter = pCsr->pSorter; ++ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); ++ *pn = pSorter->aIdx[iPhrase] - i1; ++ *pa = &pSorter->aPoslist[i1]; ++ }else{ ++ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); ++ } + }else{ +- *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); ++ *pa = 0; ++ *pn = 0; + } + + return rc; +@@ -224892,13 +256104,15 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ + + nInst++; + if( nInst>=pCsr->nInstAlloc ){ +- pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; ++ int nNewSize = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; + aInst = (int*)sqlite3_realloc64( +- pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 ++ pCsr->aInst, nNewSize*sizeof(int)*3 + ); + if( aInst ){ + pCsr->aInst = aInst; ++ pCsr->nInstAlloc = nNewSize; + }else{ ++ nInst--; + rc = SQLITE_NOMEM; + break; + } +@@ -224908,7 +256122,8 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ + aInst[0] = iBest; + aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); + aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); +- if( aInst[1]<0 || aInst[1]>=nCol ){ ++ assert( aInst[1]>=0 ); ++ if( aInst[1]>=nCol ){ + rc = FTS5_CORRUPT; + break; + } +@@ -224946,12 +256161,6 @@ static int fts5ApiInst( + ){ + if( iIdx<0 || iIdx>=pCsr->nInstCount ){ + rc = SQLITE_RANGE; +-#if 0 +- }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){ +- *piPhrase = pCsr->aInst[iIdx*3]; +- *piCol = pCsr->aInst[iIdx*3 + 2]; +- *piOff = -1; +-#endif + }else{ + *piPhrase = pCsr->aInst[iIdx*3]; + *piCol = pCsr->aInst[iIdx*3 + 1]; +@@ -224992,7 +256201,7 @@ static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ + if( pConfig->bColumnsize ){ + i64 iRowid = fts5CursorRowid(pCsr); + rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); +- }else if( pConfig->zContent==0 ){ ++ }else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ + int i; + for(i=0; inCol; i++){ + if( pConfig->abUnindexed[i]==0 ){ +@@ -225001,17 +256210,19 @@ static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ + } + }else{ + int i; ++ rc = fts5SeekCursor(pCsr, 0); + for(i=0; rc==SQLITE_OK && inCol; i++){ + if( pConfig->abUnindexed[i]==0 ){ +- const char *z; int n; +- void *p = (void*)(&pCsr->aColumnSize[i]); ++ const char *z = 0; ++ int n = 0; + pCsr->aColumnSize[i] = 0; +- rc = fts5ApiColumnText(pCtx, i, &z, &n); ++ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts5Tokenize( +- pConfig, FTS5_TOKENIZE_AUX, z, n, p, fts5ColumnSizeCb ++ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, ++ z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb + ); + } ++ sqlite3Fts5ClearLocale(pConfig); + } + } + } +@@ -225091,11 +256302,10 @@ static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ + } + + static void fts5ApiPhraseNext( +- Fts5Context *pUnused, ++ Fts5Context *pCtx, + Fts5PhraseIter *pIter, + int *piCol, int *piOff + ){ +- UNUSED_PARAM(pUnused); + if( pIter->a>=pIter->b ){ + *piCol = -1; + *piOff = -1; +@@ -225103,8 +256313,12 @@ static void fts5ApiPhraseNext( + int iVal; + pIter->a += fts5GetVarint32(pIter->a, iVal); + if( iVal==1 ){ ++ /* Avoid returning a (*piCol) value that is too large for the table, ++ ** even if the position-list is corrupt. The caller might not be ++ ** expecting it. */ ++ int nCol = ((Fts5Table*)(((Fts5Cursor*)pCtx)->base.pVtab))->pConfig->nCol; + pIter->a += fts5GetVarint32(pIter->a, iVal); +- *piCol = iVal; ++ *piCol = (iVal>=nCol ? nCol-1 : iVal); + *piOff = 0; + pIter->a += fts5GetVarint32(pIter->a, iVal); + } +@@ -225122,7 +256336,8 @@ static int fts5ApiPhraseFirst( + int n; + int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); + if( rc==SQLITE_OK ){ +- pIter->b = &pIter->a[n]; ++ assert( pIter->a || n==0 ); ++ pIter->b = (pIter->a ? &pIter->a[n] : 0); + *piCol = 0; + *piOff = 0; + fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); +@@ -225181,7 +256396,8 @@ static int fts5ApiPhraseFirstColumn( + rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); + } + if( rc==SQLITE_OK ){ +- pIter->b = &pIter->a[n]; ++ assert( pIter->a || n==0 ); ++ pIter->b = (pIter->a ? &pIter->a[n] : 0); + *piCol = 0; + fts5ApiPhraseNextColumn(pCtx, pIter, piCol); + } +@@ -225189,7 +256405,8 @@ static int fts5ApiPhraseFirstColumn( + int n; + rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); + if( rc==SQLITE_OK ){ +- pIter->b = &pIter->a[n]; ++ assert( pIter->a || n==0 ); ++ pIter->b = (pIter->a ? &pIter->a[n] : 0); + if( n<=0 ){ + *piCol = -1; + }else if( pIter->a[0]==0x01 ){ +@@ -225203,13 +256420,96 @@ static int fts5ApiPhraseFirstColumn( + return rc; + } + ++/* ++** xQueryToken() API implemenetation. ++*/ ++static int fts5ApiQueryToken( ++ Fts5Context* pCtx, ++ int iPhrase, ++ int iToken, ++ const char **ppOut, ++ int *pnOut ++){ ++ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; ++ return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut); ++} ++ ++/* ++** xInstToken() API implemenetation. ++*/ ++static int fts5ApiInstToken( ++ Fts5Context *pCtx, ++ int iIdx, ++ int iToken, ++ const char **ppOut, int *pnOut ++){ ++ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; ++ int rc = SQLITE_OK; ++ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 ++ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ++ ){ ++ if( iIdx<0 || iIdx>=pCsr->nInstCount ){ ++ rc = SQLITE_RANGE; ++ }else{ ++ int iPhrase = pCsr->aInst[iIdx*3]; ++ int iCol = pCsr->aInst[iIdx*3 + 1]; ++ int iOff = pCsr->aInst[iIdx*3 + 2]; ++ i64 iRowid = fts5CursorRowid(pCsr); ++ rc = sqlite3Fts5ExprInstToken( ++ pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut ++ ); ++ } ++ } ++ return rc; ++} ++ + + static int fts5ApiQueryPhrase(Fts5Context*, int, void*, + int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) + ); + ++/* ++** The xColumnLocale() API. ++*/ ++static int fts5ApiColumnLocale( ++ Fts5Context *pCtx, ++ int iCol, ++ const char **pzLocale, ++ int *pnLocale ++){ ++ int rc = SQLITE_OK; ++ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; ++ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; ++ ++ *pzLocale = 0; ++ *pnLocale = 0; ++ ++ assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); ++ if( iCol<0 || iCol>=pConfig->nCol ){ ++ rc = SQLITE_RANGE; ++ }else if( ++ pConfig->abUnindexed[iCol]==0 ++ && 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) ++ && pConfig->bLocale ++ ){ ++ rc = fts5SeekCursor(pCsr, 0); ++ if( rc==SQLITE_OK ){ ++ const char *zDummy = 0; ++ int nDummy = 0; ++ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy); ++ if( rc==SQLITE_OK ){ ++ *pzLocale = pConfig->t.pLocale; ++ *pnLocale = pConfig->t.nLocale; ++ } ++ sqlite3Fts5ClearLocale(pConfig); ++ } ++ } ++ ++ return rc; ++} ++ + static const Fts5ExtensionApi sFts5Api = { +- 2, /* iVersion */ ++ 4, /* iVersion */ + fts5ApiUserData, + fts5ApiColumnCount, + fts5ApiRowCount, +@@ -225229,6 +256529,10 @@ static const Fts5ExtensionApi sFts5Api = { + fts5ApiPhraseNext, + fts5ApiPhraseFirstColumn, + fts5ApiPhraseNextColumn, ++ fts5ApiQueryToken, ++ fts5ApiInstToken, ++ fts5ApiColumnLocale, ++ fts5ApiTokenize_v2 + }; + + /* +@@ -225279,6 +256583,7 @@ static void fts5ApiInvoke( + sqlite3_value **argv + ){ + assert( pCsr->pAux==0 ); ++ assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); + pCsr->pAux = pAux; + pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); + pCsr->pAux = 0; +@@ -225292,6 +256597,21 @@ static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){ + return pCsr; + } + ++/* ++** Parameter zFmt is a printf() style formatting string. This function ++** formats it using the trailing arguments and returns the result as ++** an error message to the context passed as the first argument. ++*/ ++static void fts5ResultError(sqlite3_context *pCtx, const char *zFmt, ...){ ++ char *zErr = 0; ++ va_list ap; ++ va_start(ap, zFmt); ++ zErr = sqlite3_vmprintf(zFmt, ap); ++ sqlite3_result_error(pCtx, zErr, -1); ++ sqlite3_free(zErr); ++ va_end(ap); ++} ++ + static void fts5ApiCallback( + sqlite3_context *context, + int argc, +@@ -225307,12 +256627,13 @@ static void fts5ApiCallback( + iCsrId = sqlite3_value_int64(argv[0]); + + pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId); +- if( pCsr==0 || pCsr->ePlan==0 ){ +- char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId); +- sqlite3_result_error(context, zErr, -1); +- sqlite3_free(zErr); ++ if( pCsr==0 || (pCsr->ePlan==0 || pCsr->ePlan==FTS5_PLAN_SPECIAL) ){ ++ fts5ResultError(context, "no such cursor: %lld", iCsrId); + }else{ ++ sqlite3_vtab *pTab = pCsr->base.pVtab; + fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]); ++ sqlite3_free(pTab->zErrMsg); ++ pTab->zErrMsg = 0; + } + } + +@@ -225430,8 +256751,8 @@ static int fts5ColumnMethod( + ** auxiliary function. */ + sqlite3_result_int64(pCtx, pCsr->iCsrId); + }else if( iCol==pConfig->nCol+1 ){ +- + /* The value of the "rank" column. */ ++ + if( pCsr->ePlan==FTS5_PLAN_SOURCE ){ + fts5PoslistBlob(pCtx, pCsr); + }else if( +@@ -225442,14 +256763,32 @@ static int fts5ColumnMethod( + fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); + } + } +- }else if( !fts5IsContentless(pTab) ){ +- pConfig->pzErrmsg = &pTab->p.base.zErrMsg; +- rc = fts5SeekCursor(pCsr, 1); +- if( rc==SQLITE_OK ){ +- sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); ++ }else{ ++ if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){ ++ pConfig->pzErrmsg = &pTab->p.base.zErrMsg; ++ rc = fts5SeekCursor(pCsr, 1); ++ if( rc==SQLITE_OK ){ ++ sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); ++ if( pConfig->bLocale ++ && pConfig->eContent==FTS5_CONTENT_EXTERNAL ++ && sqlite3Fts5IsLocaleValue(pConfig, pVal) ++ ){ ++ const char *z = 0; ++ int n = 0; ++ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n); ++ if( rc==SQLITE_OK ){ ++ sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT); ++ } ++ sqlite3Fts5ClearLocale(pConfig); ++ }else{ ++ sqlite3_result_value(pCtx, pVal); ++ } ++ } ++ ++ pConfig->pzErrmsg = 0; + } +- pConfig->pzErrmsg = 0; + } ++ + return rc; + } + +@@ -225487,8 +256826,10 @@ static int fts5RenameMethod( + sqlite3_vtab *pVtab, /* Virtual table handle */ + const char *zName /* New name of table */ + ){ ++ int rc; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; +- return sqlite3Fts5StorageRename(pTab->pStorage, zName); ++ rc = sqlite3Fts5StorageRename(pTab->pStorage, zName); ++ return rc; + } + + static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ +@@ -225502,9 +256843,15 @@ static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ + ** Flush the contents of the pending-terms table to disk. + */ + static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ +- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ +- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint); +- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); ++ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; ++ int rc = SQLITE_OK; ++ ++ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); ++ rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); ++ if( rc==SQLITE_OK ){ ++ pTab->iSavepoint = iSavepoint+1; ++ } ++ return rc; + } + + /* +@@ -225513,9 +256860,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ + ** This is a no-op. + */ + static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ +- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ +- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint); +- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); ++ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; ++ int rc = SQLITE_OK; ++ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); ++ if( (iSavepoint+1)iSavepoint ){ ++ rc = sqlite3Fts5FlushToDisk(&pTab->p); ++ if( rc==SQLITE_OK ){ ++ pTab->iSavepoint = iSavepoint; ++ } ++ } ++ return rc; + } + + /* +@@ -225525,10 +256879,14 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ + */ + static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; +- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ ++ int rc = SQLITE_OK; + fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); + fts5TripCursors(pTab); +- return sqlite3Fts5StorageRollback(pTab->pStorage); ++ if( (iSavepoint+1)<=pTab->iSavepoint ){ ++ pTab->p.pConfig->pgsz = 0; ++ rc = sqlite3Fts5StorageRollback(pTab->pStorage); ++ } ++ return rc; + } + + /* +@@ -225570,47 +256928,210 @@ static int fts5CreateAux( + } + + /* +-** Register a new tokenizer. This is the implementation of the +-** fts5_api.xCreateTokenizer() method. ++** This function is used by xCreateTokenizer_v2() and xCreateTokenizer(). ++** It allocates and partially populates a new Fts5TokenizerModule object. ++** The new object is already linked into the Fts5Global context before ++** returning. ++** ++** If successful, SQLITE_OK is returned and a pointer to the new ++** Fts5TokenizerModule object returned via output parameter (*ppNew). All ++** that is required is for the caller to fill in the methods in ++** Fts5TokenizerModule.x1 and x2, and to set Fts5TokenizerModule.bV2Native ++** as appropriate. ++** ++** If an error occurs, an SQLite error code is returned and the final value ++** of (*ppNew) undefined. + */ +-static int fts5CreateTokenizer( +- fts5_api *pApi, /* Global context (one per db handle) */ ++static int fts5NewTokenizerModule( ++ Fts5Global *pGlobal, /* Global context (one per db handle) */ + const char *zName, /* Name of new function */ + void *pUserData, /* User data for aux. function */ +- fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ +- void(*xDestroy)(void*) /* Destructor for pUserData */ ++ void(*xDestroy)(void*), /* Destructor for pUserData */ ++ Fts5TokenizerModule **ppNew + ){ +- Fts5Global *pGlobal = (Fts5Global*)pApi; +- Fts5TokenizerModule *pNew; +- sqlite3_int64 nName; /* Size of zName and its \0 terminator */ +- sqlite3_int64 nByte; /* Bytes of space to allocate */ + int rc = SQLITE_OK; ++ Fts5TokenizerModule *pNew; ++ sqlite3_int64 nName; /* Size of zName and its \0 terminator */ ++ sqlite3_int64 nByte; /* Bytes of space to allocate */ + + nName = strlen(zName) + 1; + nByte = sizeof(Fts5TokenizerModule) + nName; +- pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte); ++ *ppNew = pNew = (Fts5TokenizerModule*)sqlite3Fts5MallocZero(&rc, nByte); + if( pNew ){ +- memset(pNew, 0, (size_t)nByte); + pNew->zName = (char*)&pNew[1]; + memcpy(pNew->zName, zName, nName); + pNew->pUserData = pUserData; +- pNew->x = *pTokenizer; + pNew->xDestroy = xDestroy; + pNew->pNext = pGlobal->pTok; + pGlobal->pTok = pNew; + if( pNew->pNext==0 ){ + pGlobal->pDfltTok = pNew; + } ++ } ++ ++ return rc; ++} ++ ++/* ++** An instance of this type is used as the Fts5Tokenizer object for ++** wrapper tokenizers - those that provide access to a v1 tokenizer via ++** the fts5_tokenizer_v2 API, and those that provide access to a v2 tokenizer ++** via the fts5_tokenizer API. ++*/ ++typedef struct Fts5VtoVTokenizer Fts5VtoVTokenizer; ++struct Fts5VtoVTokenizer { ++ int bV2Native; /* True if v2 native tokenizer */ ++ fts5_tokenizer x1; /* Tokenizer functions */ ++ fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ ++ Fts5Tokenizer *pReal; ++}; ++ ++/* ++** Create a wrapper tokenizer. The context argument pCtx points to the ++** Fts5TokenizerModule object. ++*/ ++static int fts5VtoVCreate( ++ void *pCtx, ++ const char **azArg, ++ int nArg, ++ Fts5Tokenizer **ppOut ++){ ++ Fts5TokenizerModule *pMod = (Fts5TokenizerModule*)pCtx; ++ Fts5VtoVTokenizer *pNew = 0; ++ int rc = SQLITE_OK; ++ ++ pNew = (Fts5VtoVTokenizer*)sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); ++ if( rc==SQLITE_OK ){ ++ pNew->x1 = pMod->x1; ++ pNew->x2 = pMod->x2; ++ pNew->bV2Native = pMod->bV2Native; ++ if( pMod->bV2Native ){ ++ rc = pMod->x2.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); ++ }else{ ++ rc = pMod->x1.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); ++ } ++ if( rc!=SQLITE_OK ){ ++ sqlite3_free(pNew); ++ pNew = 0; ++ } ++ } ++ ++ *ppOut = (Fts5Tokenizer*)pNew; ++ return rc; ++} ++ ++/* ++** Delete an Fts5VtoVTokenizer wrapper tokenizer. ++*/ ++static void fts5VtoVDelete(Fts5Tokenizer *pTok){ ++ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; ++ if( p ){ ++ if( p->bV2Native ){ ++ p->x2.xDelete(p->pReal); ++ }else{ ++ p->x1.xDelete(p->pReal); ++ } ++ sqlite3_free(p); ++ } ++} ++ ++ ++/* ++** xTokenizer method for a wrapper tokenizer that offers the v1 interface ++** (no support for locales). ++*/ ++static int fts5V1toV2Tokenize( ++ Fts5Tokenizer *pTok, ++ void *pCtx, int flags, ++ const char *pText, int nText, ++ int (*xToken)(void*, int, const char*, int, int, int) ++){ ++ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; ++ assert( p->bV2Native ); ++ return p->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken); ++} ++ ++/* ++** xTokenizer method for a wrapper tokenizer that offers the v2 interface ++** (with locale support). ++*/ ++static int fts5V2toV1Tokenize( ++ Fts5Tokenizer *pTok, ++ void *pCtx, int flags, ++ const char *pText, int nText, ++ const char *pLocale, int nLocale, ++ int (*xToken)(void*, int, const char*, int, int, int) ++){ ++ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; ++ assert( p->bV2Native==0 ); ++ UNUSED_PARAM2(pLocale,nLocale); ++ return p->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken); ++} ++ ++/* ++** Register a new tokenizer. This is the implementation of the ++** fts5_api.xCreateTokenizer_v2() method. ++*/ ++static int fts5CreateTokenizer_v2( ++ fts5_api *pApi, /* Global context (one per db handle) */ ++ const char *zName, /* Name of new function */ ++ void *pUserData, /* User data for aux. function */ ++ fts5_tokenizer_v2 *pTokenizer, /* Tokenizer implementation */ ++ void(*xDestroy)(void*) /* Destructor for pUserData */ ++){ ++ Fts5Global *pGlobal = (Fts5Global*)pApi; ++ int rc = SQLITE_OK; ++ ++ if( pTokenizer->iVersion>2 ){ ++ rc = SQLITE_ERROR; + }else{ +- rc = SQLITE_NOMEM; ++ Fts5TokenizerModule *pNew = 0; ++ rc = fts5NewTokenizerModule(pGlobal, zName, pUserData, xDestroy, &pNew); ++ if( pNew ){ ++ pNew->x2 = *pTokenizer; ++ pNew->bV2Native = 1; ++ pNew->x1.xCreate = fts5VtoVCreate; ++ pNew->x1.xTokenize = fts5V1toV2Tokenize; ++ pNew->x1.xDelete = fts5VtoVDelete; ++ } + } + + return rc; + } + ++/* ++** The fts5_api.xCreateTokenizer() method. ++*/ ++static int fts5CreateTokenizer( ++ fts5_api *pApi, /* Global context (one per db handle) */ ++ const char *zName, /* Name of new function */ ++ void *pUserData, /* User data for aux. function */ ++ fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ ++ void(*xDestroy)(void*) /* Destructor for pUserData */ ++){ ++ Fts5TokenizerModule *pNew = 0; ++ int rc = SQLITE_OK; ++ ++ rc = fts5NewTokenizerModule( ++ (Fts5Global*)pApi, zName, pUserData, xDestroy, &pNew ++ ); ++ if( pNew ){ ++ pNew->x1 = *pTokenizer; ++ pNew->x2.xCreate = fts5VtoVCreate; ++ pNew->x2.xTokenize = fts5V2toV1Tokenize; ++ pNew->x2.xDelete = fts5VtoVDelete; ++ } ++ return rc; ++} ++ ++/* ++** Search the global context passed as the first argument for a tokenizer ++** module named zName. If found, return a pointer to the Fts5TokenizerModule ++** object. Otherwise, return NULL. ++*/ + static Fts5TokenizerModule *fts5LocateTokenizer( +- Fts5Global *pGlobal, +- const char *zName ++ Fts5Global *pGlobal, /* Global (one per db handle) object */ ++ const char *zName /* Name of tokenizer module to find */ + ){ + Fts5TokenizerModule *pMod = 0; + +@@ -225625,6 +257146,36 @@ static Fts5TokenizerModule *fts5LocateTokenizer( + return pMod; + } + ++/* ++** Find a tokenizer. This is the implementation of the ++** fts5_api.xFindTokenizer_v2() method. ++*/ ++static int fts5FindTokenizer_v2( ++ fts5_api *pApi, /* Global context (one per db handle) */ ++ const char *zName, /* Name of tokenizer */ ++ void **ppUserData, ++ fts5_tokenizer_v2 **ppTokenizer /* Populate this object */ ++){ ++ int rc = SQLITE_OK; ++ Fts5TokenizerModule *pMod; ++ ++ pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); ++ if( pMod ){ ++ if( pMod->bV2Native ){ ++ *ppUserData = pMod->pUserData; ++ }else{ ++ *ppUserData = (void*)pMod; ++ } ++ *ppTokenizer = &pMod->x2; ++ }else{ ++ *ppTokenizer = 0; ++ *ppUserData = 0; ++ rc = SQLITE_ERROR; ++ } ++ ++ return rc; ++} ++ + /* + ** Find a tokenizer. This is the implementation of the + ** fts5_api.xFindTokenizer() method. +@@ -225640,48 +257191,75 @@ static int fts5FindTokenizer( + + pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); + if( pMod ){ +- *pTokenizer = pMod->x; +- *ppUserData = pMod->pUserData; ++ if( pMod->bV2Native==0 ){ ++ *ppUserData = pMod->pUserData; ++ }else{ ++ *ppUserData = (void*)pMod; ++ } ++ *pTokenizer = pMod->x1; + }else{ +- memset(pTokenizer, 0, sizeof(fts5_tokenizer)); ++ memset(pTokenizer, 0, sizeof(*pTokenizer)); ++ *ppUserData = 0; + rc = SQLITE_ERROR; + } + + return rc; + } + +-static int sqlite3Fts5GetTokenizer( +- Fts5Global *pGlobal, +- const char **azArg, +- int nArg, +- Fts5Tokenizer **ppTok, +- fts5_tokenizer **ppTokApi, +- char **pzErr +-){ +- Fts5TokenizerModule *pMod; ++/* ++** Attempt to instantiate the tokenizer. ++*/ ++static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig){ ++ const char **azArg = pConfig->t.azArg; ++ const int nArg = pConfig->t.nArg; ++ Fts5TokenizerModule *pMod = 0; + int rc = SQLITE_OK; + +- pMod = fts5LocateTokenizer(pGlobal, nArg==0 ? 0 : azArg[0]); ++ pMod = fts5LocateTokenizer(pConfig->pGlobal, nArg==0 ? 0 : azArg[0]); + if( pMod==0 ){ + assert( nArg>0 ); + rc = SQLITE_ERROR; +- *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]); ++ sqlite3Fts5ConfigErrmsg(pConfig, "no such tokenizer: %s", azArg[0]); + }else{ +- rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok); +- *ppTokApi = &pMod->x; +- if( rc!=SQLITE_OK && pzErr ){ +- *pzErr = sqlite3_mprintf("error in tokenizer constructor"); ++ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**) = 0; ++ if( pMod->bV2Native ){ ++ xCreate = pMod->x2.xCreate; ++ pConfig->t.pApi2 = &pMod->x2; ++ }else{ ++ pConfig->t.pApi1 = &pMod->x1; ++ xCreate = pMod->x1.xCreate; ++ } ++ ++ rc = xCreate(pMod->pUserData, ++ (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->t.pTok ++ ); ++ ++ if( rc!=SQLITE_OK ){ ++ if( rc!=SQLITE_NOMEM ){ ++ sqlite3Fts5ConfigErrmsg(pConfig, "error in tokenizer constructor"); ++ } ++ }else if( pMod->bV2Native==0 ){ ++ pConfig->t.ePattern = sqlite3Fts5TokenizerPattern( ++ pMod->x1.xCreate, pConfig->t.pTok ++ ); + } + } + + if( rc!=SQLITE_OK ){ +- *ppTokApi = 0; +- *ppTok = 0; ++ pConfig->t.pApi1 = 0; ++ pConfig->t.pApi2 = 0; ++ pConfig->t.pTok = 0; + } + + return rc; + } + ++ ++/* ++** xDestroy callback passed to sqlite3_create_module(). This is invoked ++** when the db handle is being closed. Free memory associated with ++** tokenizers and aux functions registered with this db handle. ++*/ + static void fts5ModuleDestroy(void *pCtx){ + Fts5TokenizerModule *pTok, *pNextTok; + Fts5Auxiliary *pAux, *pNextAux; +@@ -225702,6 +257280,10 @@ static void fts5ModuleDestroy(void *pCtx){ + sqlite3_free(pGlobal); + } + ++/* ++** Implementation of the fts5() function used by clients to obtain the ++** API pointer. ++*/ + static void fts5Fts5Func( + sqlite3_context *pCtx, /* Function call context */ + int nArg, /* Number of args */ +@@ -225725,7 +257307,82 @@ static void fts5SourceIdFunc( + ){ + assert( nArg==0 ); + UNUSED_PARAM2(nArg, apUnused); +- sqlite3_result_text(pCtx, "fts5: 2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f", -1, SQLITE_TRANSIENT); ++ sqlite3_result_text(pCtx, "fts5: 2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3", -1, SQLITE_TRANSIENT); ++} ++ ++/* ++** Implementation of fts5_locale(LOCALE, TEXT) function. ++** ++** If parameter LOCALE is NULL, or a zero-length string, then a copy of ++** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as ++** text, and the value returned is a blob consisting of: ++** ++** * The 4 bytes 0x00, 0xE0, 0xB2, 0xEb (FTS5_LOCALE_HEADER). ++** * The LOCALE, as utf-8 text, followed by ++** * 0x00, followed by ++** * The TEXT, as utf-8 text. ++** ++** There is no final nul-terminator following the TEXT value. ++*/ ++static void fts5LocaleFunc( ++ sqlite3_context *pCtx, /* Function call context */ ++ int nArg, /* Number of args */ ++ sqlite3_value **apArg /* Function arguments */ ++){ ++ const char *zLocale = 0; ++ int nLocale = 0; ++ const char *zText = 0; ++ int nText = 0; ++ ++ assert( nArg==2 ); ++ UNUSED_PARAM(nArg); ++ ++ zLocale = (const char*)sqlite3_value_text(apArg[0]); ++ nLocale = sqlite3_value_bytes(apArg[0]); ++ ++ zText = (const char*)sqlite3_value_text(apArg[1]); ++ nText = sqlite3_value_bytes(apArg[1]); ++ ++ if( zLocale==0 || zLocale[0]=='\0' ){ ++ sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT); ++ }else{ ++ Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx); ++ u8 *pBlob = 0; ++ u8 *pCsr = 0; ++ int nBlob = 0; ++ ++ nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText; ++ pBlob = (u8*)sqlite3_malloc(nBlob); ++ if( pBlob==0 ){ ++ sqlite3_result_error_nomem(pCtx); ++ return; ++ } ++ ++ pCsr = pBlob; ++ memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE); ++ pCsr += FTS5_LOCALE_HDR_SIZE; ++ memcpy(pCsr, zLocale, nLocale); ++ pCsr += nLocale; ++ (*pCsr++) = 0x00; ++ if( zText ) memcpy(pCsr, zText, nText); ++ assert( &pCsr[nText]==&pBlob[nBlob] ); ++ ++ sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); ++ } ++} ++ ++/* ++** Implementation of fts5_insttoken() function. ++*/ ++static void fts5InsttokenFunc( ++ sqlite3_context *pCtx, /* Function call context */ ++ int nArg, /* Number of args */ ++ sqlite3_value **apArg /* Function arguments */ ++){ ++ assert( nArg==1 ); ++ (void)nArg; ++ sqlite3_result_value(pCtx, apArg[0]); ++ sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE); + } + + /* +@@ -225743,9 +257400,47 @@ static int fts5ShadowName(const char *zName){ + return 0; + } + ++/* ++** Run an integrity check on the FTS5 data structures. Return a string ++** if anything is found amiss. Return a NULL pointer if everything is ++** OK. ++*/ ++static int fts5IntegrityMethod( ++ sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */ ++ const char *zSchema, /* Name of schema in which this table lives */ ++ const char *zTabname, /* Name of the table itself */ ++ int isQuick, /* True if this is a quick-check */ ++ char **pzErr /* Write error message here */ ++){ ++ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; ++ int rc; ++ ++ assert( pzErr!=0 && *pzErr==0 ); ++ UNUSED_PARAM(isQuick); ++ assert( pTab->p.pConfig->pzErrmsg==0 ); ++ pTab->p.pConfig->pzErrmsg = pzErr; ++ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0); ++ if( *pzErr==0 && rc!=SQLITE_OK ){ ++ if( (rc&0xff)==SQLITE_CORRUPT ){ ++ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s", ++ zSchema, zTabname); ++ rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM; ++ }else{ ++ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" ++ " FTS5 table %s.%s: %s", ++ zSchema, zTabname, sqlite3_errstr(rc)); ++ } ++ } ++ ++ sqlite3Fts5IndexCloseReader(pTab->p.pIndex); ++ pTab->p.pConfig->pzErrmsg = 0; ++ ++ return rc; ++} ++ + static int fts5Init(sqlite3 *db){ + static const sqlite3_module fts5Mod = { +- /* iVersion */ 3, ++ /* iVersion */ 4, + /* xCreate */ fts5CreateMethod, + /* xConnect */ fts5ConnectMethod, + /* xBestIndex */ fts5BestIndexMethod, +@@ -225768,7 +257463,8 @@ static int fts5Init(sqlite3 *db){ + /* xSavepoint */ fts5SavepointMethod, + /* xRelease */ fts5ReleaseMethod, + /* xRollbackTo */ fts5RollbackToMethod, +- /* xShadowName */ fts5ShadowName ++ /* xShadowName */ fts5ShadowName, ++ /* xIntegrity */ fts5IntegrityMethod + }; + + int rc; +@@ -225781,10 +257477,22 @@ static int fts5Init(sqlite3 *db){ + void *p = (void*)pGlobal; + memset(pGlobal, 0, sizeof(Fts5Global)); + pGlobal->db = db; +- pGlobal->api.iVersion = 2; ++ pGlobal->api.iVersion = 3; + pGlobal->api.xCreateFunction = fts5CreateAux; + pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; + pGlobal->api.xFindTokenizer = fts5FindTokenizer; ++ pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; ++ pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; ++ ++ /* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector. ++ ** The constants below were generated randomly. */ ++ sqlite3_randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr); ++ pGlobal->aLocaleHdr[0] ^= 0xF924976D; ++ pGlobal->aLocaleHdr[1] ^= 0x16596E13; ++ pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA; ++ pGlobal->aLocaleHdr[3] ^= 0x9B03A67F; ++ assert( sizeof(pGlobal->aLocaleHdr)==16 ); ++ + rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); + if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); + if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); +@@ -225798,7 +257506,23 @@ static int fts5Init(sqlite3 *db){ + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function( +- db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0 ++ db, "fts5_source_id", 0, ++ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS, ++ p, fts5SourceIdFunc, 0, 0 ++ ); ++ } ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3_create_function( ++ db, "fts5_locale", 2, ++ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE, ++ p, fts5LocaleFunc, 0, 0 ++ ); ++ } ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3_create_function( ++ db, "fts5_insttoken", 1, ++ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, ++ p, fts5InsttokenFunc, 0, 0 + ); + } + } +@@ -225808,8 +257532,8 @@ static int fts5Init(sqlite3 *db){ + ** its entry point to enable the matchinfo() demo. */ + #ifdef SQLITE_FTS5_ENABLE_TEST_MI + if( rc==SQLITE_OK ){ +- extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*); +- rc = sqlite3Fts5TestRegisterMatchinfo(db); ++ extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*); ++ rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api); + } + #endif + +@@ -225875,13 +257599,40 @@ SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3 *db){ + + /* #include "fts5Int.h" */ + ++/* ++** pSavedRow: ++** SQL statement FTS5_STMT_LOOKUP2 is a copy of FTS5_STMT_LOOKUP, it ++** does a by-rowid lookup to retrieve a single row from the %_content ++** table or equivalent external-content table/view. ++** ++** However, FTS5_STMT_LOOKUP2 is only used when retrieving the original ++** values for a row being UPDATEd. In that case, the SQL statement is ++** not reset and pSavedRow is set to point at it. This is so that the ++** insert operation that follows the delete may access the original ++** row values for any new values for which sqlite3_value_nochange() returns ++** true. i.e. if the user executes: ++** ++** CREATE VIRTUAL TABLE ft USING fts5(a, b, c, locale=1); ++** ... ++** UPDATE fts SET a=?, b=? WHERE rowid=?; ++** ++** then the value passed to the xUpdate() method of this table as the ++** new.c value is an sqlite3_value_nochange() value. So in this case it ++** must be read from the saved row stored in Fts5Storage.pSavedRow. ++** ++** This is necessary - using sqlite3_value_nochange() instead of just having ++** SQLite pass the original value back via xUpdate() - so as not to discard ++** any locale information associated with such values. ++** ++*/ + struct Fts5Storage { + Fts5Config *pConfig; + Fts5Index *pIndex; + int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ + i64 nTotalRow; /* Total number of rows in FTS table */ + i64 *aTotalSize; /* Total sizes of each column */ +- sqlite3_stmt *aStmt[11]; ++ sqlite3_stmt *pSavedRow; ++ sqlite3_stmt *aStmt[12]; + }; + + +@@ -225895,14 +257646,15 @@ struct Fts5Storage { + # error "FTS5_STMT_LOOKUP mismatch" + #endif + +-#define FTS5_STMT_INSERT_CONTENT 3 +-#define FTS5_STMT_REPLACE_CONTENT 4 +-#define FTS5_STMT_DELETE_CONTENT 5 +-#define FTS5_STMT_REPLACE_DOCSIZE 6 +-#define FTS5_STMT_DELETE_DOCSIZE 7 +-#define FTS5_STMT_LOOKUP_DOCSIZE 8 +-#define FTS5_STMT_REPLACE_CONFIG 9 +-#define FTS5_STMT_SCAN 10 ++#define FTS5_STMT_LOOKUP2 3 ++#define FTS5_STMT_INSERT_CONTENT 4 ++#define FTS5_STMT_REPLACE_CONTENT 5 ++#define FTS5_STMT_DELETE_CONTENT 6 ++#define FTS5_STMT_REPLACE_DOCSIZE 7 ++#define FTS5_STMT_DELETE_DOCSIZE 8 ++#define FTS5_STMT_LOOKUP_DOCSIZE 9 ++#define FTS5_STMT_REPLACE_CONFIG 10 ++#define FTS5_STMT_SCAN 11 + + /* + ** Prepare the two insert statements - Fts5Storage.pInsertContent and +@@ -225932,14 +257684,15 @@ static int fts5StorageGetStmt( + "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC", + "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC", + "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */ ++ "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP2 */ + + "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ + "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ + "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */ +- "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */ ++ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */ + "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */ + +- "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ ++ "SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ + + "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */ + "SELECT %s FROM %s AS T", /* SCAN */ +@@ -225947,6 +257700,8 @@ static int fts5StorageGetStmt( + Fts5Config *pC = p->pConfig; + char *zSql = 0; + ++ assert( ArraySize(azStmt)==ArraySize(p->aStmt) ); ++ + switch( eStmt ){ + case FTS5_STMT_SCAN: + zSql = sqlite3_mprintf(azStmt[eStmt], +@@ -225963,6 +257718,7 @@ static int fts5StorageGetStmt( + break; + + case FTS5_STMT_LOOKUP: ++ case FTS5_STMT_LOOKUP2: + zSql = sqlite3_mprintf(azStmt[eStmt], + pC->zContentExprlist, pC->zContent, pC->zContentRowid + ); +@@ -225970,23 +257726,51 @@ static int fts5StorageGetStmt( + + case FTS5_STMT_INSERT_CONTENT: + case FTS5_STMT_REPLACE_CONTENT: { +- int nCol = pC->nCol + 1; +- char *zBind; ++ char *zBind = 0; + int i; + +- zBind = sqlite3_malloc64(1 + nCol*2); +- if( zBind ){ +- for(i=0; ieContent==FTS5_CONTENT_NORMAL ++ || pC->eContent==FTS5_CONTENT_UNINDEXED ++ ); ++ ++ /* Add bindings for the "c*" columns - those that store the actual ++ ** table content. If eContent==NORMAL, then there is one binding ++ ** for each column. Or, if eContent==UNINDEXED, then there are only ++ ** bindings for the UNINDEXED columns. */ ++ for(i=0; rc==SQLITE_OK && i<(pC->nCol+1); i++){ ++ if( !i || pC->eContent==FTS5_CONTENT_NORMAL || pC->abUnindexed[i-1] ){ ++ zBind = sqlite3Fts5Mprintf(&rc, "%z%s?%d", zBind, zBind?",":"",i+1); ++ } ++ } ++ ++ /* Add bindings for any "l*" columns. Only non-UNINDEXED columns ++ ** require these. */ ++ if( pC->bLocale && pC->eContent==FTS5_CONTENT_NORMAL ){ ++ for(i=0; rc==SQLITE_OK && inCol; i++){ ++ if( pC->abUnindexed[i]==0 ){ ++ zBind = sqlite3Fts5Mprintf(&rc, "%z,?%d", zBind, pC->nCol+i+2); ++ } + } +- zBind[i*2-1] = '\0'; +- zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind); +- sqlite3_free(zBind); + } ++ ++ zSql = sqlite3Fts5Mprintf(&rc, azStmt[eStmt], pC->zDb, pC->zName,zBind); ++ sqlite3_free(zBind); + break; + } + ++ case FTS5_STMT_REPLACE_DOCSIZE: ++ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, ++ (pC->bContentlessDelete ? ",?" : "") ++ ); ++ break; ++ ++ case FTS5_STMT_LOOKUP_DOCSIZE: ++ zSql = sqlite3_mprintf(azStmt[eStmt], ++ (pC->bContentlessDelete ? ",origin" : ""), ++ pC->zDb, pC->zName ++ ); ++ break; ++ + default: + zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName); + break; +@@ -225996,7 +257780,7 @@ static int fts5StorageGetStmt( + rc = SQLITE_NOMEM; + }else{ + int f = SQLITE_PREPARE_PERSISTENT; +- if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB; ++ if( eStmt>FTS5_STMT_LOOKUP2 ) f |= SQLITE_PREPARE_NO_VTAB; + p->pConfig->bLock++; + rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0); + p->pConfig->bLock--; +@@ -226004,6 +257788,11 @@ static int fts5StorageGetStmt( + if( rc!=SQLITE_OK && pzErrMsg ){ + *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); + } ++ if( rc==SQLITE_ERROR && eStmt>FTS5_STMT_LOOKUP2 && eStmtpIndex = pIndex; + + if( bCreate ){ +- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ ++ if( pConfig->eContent==FTS5_CONTENT_NORMAL ++ || pConfig->eContent==FTS5_CONTENT_UNINDEXED ++ ){ + int nDefn = 32 + pConfig->nCol*10; +- char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); ++ char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); + if( zDefn==0 ){ + rc = SQLITE_NOMEM; + }else{ +@@ -226167,8 +257958,20 @@ static int sqlite3Fts5StorageOpen( + sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); + iOff = (int)strlen(zDefn); + for(i=0; inCol; i++){ +- sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); +- iOff += (int)strlen(&zDefn[iOff]); ++ if( pConfig->eContent==FTS5_CONTENT_NORMAL ++ || pConfig->abUnindexed[i] ++ ){ ++ sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); ++ iOff += (int)strlen(&zDefn[iOff]); ++ } ++ } ++ if( pConfig->bLocale ){ ++ for(i=0; inCol; i++){ ++ if( pConfig->abUnindexed[i]==0 ){ ++ sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); ++ iOff += (int)strlen(&zDefn[iOff]); ++ } ++ } + } + rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); + } +@@ -226176,9 +257979,11 @@ static int sqlite3Fts5StorageOpen( + } + + if( rc==SQLITE_OK && pConfig->bColumnsize ){ +- rc = sqlite3Fts5CreateTable( +- pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr +- ); ++ const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB"; ++ if( pConfig->bContentlessDelete ){ ++ zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER"; ++ } ++ rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr); + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5CreateTable( +@@ -226243,60 +258048,193 @@ static int fts5StorageInsertCallback( + return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); + } + ++/* ++** This function is used as part of an UPDATE statement that modifies the ++** rowid of a row. In that case, this function is called first to set ++** Fts5Storage.pSavedRow to point to a statement that may be used to ++** access the original values of the row being deleted - iDel. ++** ++** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ++** It is not considered an error if row iDel does not exist. In this case ++** pSavedRow is not set and SQLITE_OK returned. ++*/ ++static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){ ++ int rc = SQLITE_OK; ++ sqlite3_stmt *pSeek = 0; ++ ++ assert( p->pSavedRow==0 ); ++ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+1, &pSeek, 0); ++ if( rc==SQLITE_OK ){ ++ sqlite3_bind_int64(pSeek, 1, iDel); ++ if( sqlite3_step(pSeek)!=SQLITE_ROW ){ ++ rc = sqlite3_reset(pSeek); ++ }else{ ++ p->pSavedRow = pSeek; ++ } ++ } ++ ++ return rc; ++} ++ + /* + ** If a row with rowid iDel is present in the %_content table, add the + ** delete-markers to the FTS index necessary to delete it. Do not actually + ** remove the %_content row at this time though. ++** ++** If parameter bSaveRow is true, then Fts5Storage.pSavedRow is left ++** pointing to a statement (FTS5_STMT_LOOKUP2) that may be used to access ++** the original values of the row being deleted. This is used by UPDATE ++** statements. + */ + static int fts5StorageDeleteFromIndex( + Fts5Storage *p, + i64 iDel, +- sqlite3_value **apVal ++ sqlite3_value **apVal, ++ int bSaveRow /* True to set pSavedRow */ + ){ + Fts5Config *pConfig = p->pConfig; + sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ +- int rc; /* Return code */ ++ int rc = SQLITE_OK; /* Return code */ + int rc2; /* sqlite3_reset() return code */ + int iCol; + Fts5InsertCtx ctx; + ++ assert( bSaveRow==0 || apVal==0 ); ++ assert( bSaveRow==0 || bSaveRow==1 ); ++ assert( FTS5_STMT_LOOKUP2==FTS5_STMT_LOOKUP+1 ); ++ + if( apVal==0 ){ +- rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0); +- if( rc!=SQLITE_OK ) return rc; +- sqlite3_bind_int64(pSeek, 1, iDel); +- if( sqlite3_step(pSeek)!=SQLITE_ROW ){ +- return sqlite3_reset(pSeek); ++ if( p->pSavedRow && bSaveRow ){ ++ pSeek = p->pSavedRow; ++ p->pSavedRow = 0; ++ }else{ ++ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+bSaveRow, &pSeek, 0); ++ if( rc!=SQLITE_OK ) return rc; ++ sqlite3_bind_int64(pSeek, 1, iDel); ++ if( sqlite3_step(pSeek)!=SQLITE_ROW ){ ++ return sqlite3_reset(pSeek); ++ } + } + } + + ctx.pStorage = p; + ctx.iCol = -1; +- rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); + for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ + if( pConfig->abUnindexed[iCol-1]==0 ){ +- const char *zText; +- int nText; ++ sqlite3_value *pVal = 0; ++ sqlite3_value *pFree = 0; ++ const char *pText = 0; ++ int nText = 0; ++ const char *pLoc = 0; ++ int nLoc = 0; ++ ++ assert( pSeek==0 || apVal==0 ); ++ assert( pSeek!=0 || apVal!=0 ); + if( pSeek ){ +- zText = (const char*)sqlite3_column_text(pSeek, iCol); +- nText = sqlite3_column_bytes(pSeek, iCol); ++ pVal = sqlite3_column_value(pSeek, iCol); + }else{ +- zText = (const char*)sqlite3_value_text(apVal[iCol-1]); +- nText = sqlite3_value_bytes(apVal[iCol-1]); ++ pVal = apVal[iCol-1]; + } +- ctx.szCol = 0; +- rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, +- zText, nText, (void*)&ctx, fts5StorageInsertCallback +- ); +- p->aTotalSize[iCol-1] -= (i64)ctx.szCol; ++ ++ if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ ++ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); ++ }else{ ++ if( sqlite3_value_type(pVal)!=SQLITE_TEXT ){ ++ /* Make a copy of the value to work with. This is because the call ++ ** to sqlite3_value_text() below forces the type of the value to ++ ** SQLITE_TEXT, and we may need to use it again later. */ ++ pFree = pVal = sqlite3_value_dup(pVal); ++ if( pVal==0 ){ ++ rc = SQLITE_NOMEM; ++ } ++ } ++ if( rc==SQLITE_OK ){ ++ pText = (const char*)sqlite3_value_text(pVal); ++ nText = sqlite3_value_bytes(pVal); ++ if( pConfig->bLocale && pSeek ){ ++ pLoc = (const char*)sqlite3_column_text(pSeek, iCol+pConfig->nCol); ++ nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol); ++ } ++ } ++ } ++ ++ if( rc==SQLITE_OK ){ ++ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); ++ ctx.szCol = 0; ++ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, ++ pText, nText, (void*)&ctx, fts5StorageInsertCallback ++ ); ++ p->aTotalSize[iCol-1] -= (i64)ctx.szCol; ++ if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ ++ rc = FTS5_CORRUPT; ++ } ++ sqlite3Fts5ClearLocale(pConfig); ++ } ++ sqlite3_value_free(pFree); + } + } +- p->nTotalRow--; ++ if( rc==SQLITE_OK && p->nTotalRow<1 ){ ++ rc = FTS5_CORRUPT; ++ }else{ ++ p->nTotalRow--; ++ } + +- rc2 = sqlite3_reset(pSeek); +- if( rc==SQLITE_OK ) rc = rc2; ++ if( rc==SQLITE_OK && bSaveRow ){ ++ assert( p->pSavedRow==0 ); ++ p->pSavedRow = pSeek; ++ }else{ ++ rc2 = sqlite3_reset(pSeek); ++ if( rc==SQLITE_OK ) rc = rc2; ++ } + return rc; + } + ++/* ++** Reset any saved statement pSavedRow. Zero pSavedRow as well. This ++** should be called by the xUpdate() method of the fts5 table before ++** returning from any operation that may have set Fts5Storage.pSavedRow. ++*/ ++static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){ ++ assert( pStorage->pSavedRow==0 ++ || pStorage->pSavedRow==pStorage->aStmt[FTS5_STMT_LOOKUP2] ++ ); ++ sqlite3_reset(pStorage->pSavedRow); ++ pStorage->pSavedRow = 0; ++} ++ ++/* ++** This function is called to process a DELETE on a contentless_delete=1 ++** table. It adds the tombstone required to delete the entry with rowid ++** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs, ++** an SQLite error code. ++*/ ++static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){ ++ i64 iOrigin = 0; ++ sqlite3_stmt *pLookup = 0; ++ int rc = SQLITE_OK; ++ ++ assert( p->pConfig->bContentlessDelete ); ++ assert( p->pConfig->eContent==FTS5_CONTENT_NONE ++ || p->pConfig->eContent==FTS5_CONTENT_UNINDEXED ++ ); ++ ++ /* Look up the origin of the document in the %_docsize table. Store ++ ** this in stack variable iOrigin. */ ++ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); ++ if( rc==SQLITE_OK ){ ++ sqlite3_bind_int64(pLookup, 1, iDel); ++ if( SQLITE_ROW==sqlite3_step(pLookup) ){ ++ iOrigin = sqlite3_column_int64(pLookup, 1); ++ } ++ rc = sqlite3_reset(pLookup); ++ } ++ ++ if( rc==SQLITE_OK && iOrigin!=0 ){ ++ rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel); ++ } ++ ++ return rc; ++} + + /* + ** Insert a record into the %_docsize table. Specifically, do: +@@ -226317,6 +258255,13 @@ static int fts5StorageInsertDocsize( + rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int64(pReplace, 1, iRowid); ++ if( p->pConfig->bContentlessDelete ){ ++ i64 iOrigin = 0; ++ rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin); ++ sqlite3_bind_int64(pReplace, 3, iOrigin); ++ } ++ } ++ if( rc==SQLITE_OK ){ + sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); + sqlite3_step(pReplace); + rc = sqlite3_reset(pReplace); +@@ -226374,7 +258319,12 @@ static int fts5StorageSaveTotals(Fts5Storage *p){ + /* + ** Remove a row from the FTS table. + */ +-static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){ ++static int sqlite3Fts5StorageDelete( ++ Fts5Storage *p, /* Storage object */ ++ i64 iDel, /* Rowid to delete from table */ ++ sqlite3_value **apVal, /* Optional - values to remove from index */ ++ int bSaveRow /* If true, set pSavedRow for deleted row */ ++){ + Fts5Config *pConfig = p->pConfig; + int rc; + sqlite3_stmt *pDel = 0; +@@ -226384,7 +258334,21 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap + + /* Delete the index records */ + if( rc==SQLITE_OK ){ +- rc = fts5StorageDeleteFromIndex(p, iDel, apVal); ++ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); ++ } ++ ++ if( rc==SQLITE_OK ){ ++ if( p->pConfig->bContentlessDelete ){ ++ rc = fts5StorageContentlessDelete(p, iDel); ++ if( rc==SQLITE_OK ++ && bSaveRow ++ && p->pConfig->eContent==FTS5_CONTENT_UNINDEXED ++ ){ ++ rc = sqlite3Fts5StorageFindDeleteRow(p, iDel); ++ } ++ }else{ ++ rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); ++ } + } + + /* Delete the %_docsize record */ +@@ -226398,7 +258362,9 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap + } + + /* Delete the %_content record */ +- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ ++ if( pConfig->eContent==FTS5_CONTENT_NORMAL ++ || pConfig->eContent==FTS5_CONTENT_UNINDEXED ++ ){ + if( rc==SQLITE_OK ){ + rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); + } +@@ -226430,8 +258396,13 @@ static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ + ); + if( rc==SQLITE_OK && pConfig->bColumnsize ){ + rc = fts5ExecPrintf(pConfig->db, 0, +- "DELETE FROM %Q.'%q_docsize';", +- pConfig->zDb, pConfig->zName ++ "DELETE FROM %Q.'%q_docsize';", pConfig->zDb, pConfig->zName ++ ); ++ } ++ ++ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ ++ rc = fts5ExecPrintf(pConfig->db, 0, ++ "DELETE FROM %Q.'%q_content';", pConfig->zDb, pConfig->zName + ); + } + +@@ -226461,7 +258432,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ + } + + if( rc==SQLITE_OK ){ +- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); ++ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg); + } + + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){ +@@ -226472,14 +258443,36 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ + for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ + ctx.szCol = 0; + if( pConfig->abUnindexed[ctx.iCol]==0 ){ +- const char *zText = (const char*)sqlite3_column_text(pScan, ctx.iCol+1); +- int nText = sqlite3_column_bytes(pScan, ctx.iCol+1); +- rc = sqlite3Fts5Tokenize(pConfig, +- FTS5_TOKENIZE_DOCUMENT, +- zText, nText, +- (void*)&ctx, +- fts5StorageInsertCallback +- ); ++ int nText = 0; /* Size of pText in bytes */ ++ const char *pText = 0; /* Pointer to buffer containing text value */ ++ int nLoc = 0; /* Size of pLoc in bytes */ ++ const char *pLoc = 0; /* Pointer to buffer containing text value */ ++ ++ sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); ++ if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ++ && sqlite3Fts5IsLocaleValue(pConfig, pVal) ++ ){ ++ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); ++ }else{ ++ pText = (const char*)sqlite3_value_text(pVal); ++ nText = sqlite3_value_bytes(pVal); ++ if( pConfig->bLocale ){ ++ int iCol = ctx.iCol + 1 + pConfig->nCol; ++ pLoc = (const char*)sqlite3_column_text(pScan, iCol); ++ nLoc = sqlite3_column_bytes(pScan, iCol); ++ } ++ } ++ ++ if( rc==SQLITE_OK ){ ++ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); ++ rc = sqlite3Fts5Tokenize(pConfig, ++ FTS5_TOKENIZE_DOCUMENT, ++ pText, nText, ++ (void*)&ctx, ++ fts5StorageInsertCallback ++ ); ++ sqlite3Fts5ClearLocale(pConfig); ++ } + } + sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); + p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; +@@ -226545,6 +258538,7 @@ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ + */ + static int sqlite3Fts5StorageContentInsert( + Fts5Storage *p, ++ int bReplace, /* True to use REPLACE instead of INSERT */ + sqlite3_value **apVal, + i64 *piRowid + ){ +@@ -226552,7 +258546,9 @@ static int sqlite3Fts5StorageContentInsert( + int rc = SQLITE_OK; + + /* Insert the new row into the %_content table. */ +- if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ ++ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ++ && pConfig->eContent!=FTS5_CONTENT_UNINDEXED ++ ){ + if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ + *piRowid = sqlite3_value_int64(apVal[1]); + }else{ +@@ -226561,9 +258557,52 @@ static int sqlite3Fts5StorageContentInsert( + }else{ + sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ + int i; /* Counter variable */ +- rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); +- for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ +- rc = sqlite3_bind_value(pInsert, i, apVal[i]); ++ ++ assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT ); ++ assert( bReplace==0 || bReplace==1 ); ++ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT+bReplace, &pInsert, 0); ++ if( pInsert ) sqlite3_clear_bindings(pInsert); ++ ++ /* Bind the rowid value */ ++ sqlite3_bind_value(pInsert, 1, apVal[1]); ++ ++ /* Loop through values for user-defined columns. i=2 is the leftmost ++ ** user-defined column. As is column 1 of pSavedRow. */ ++ for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ ++ int bUnindexed = pConfig->abUnindexed[i-2]; ++ if( pConfig->eContent==FTS5_CONTENT_NORMAL || bUnindexed ){ ++ sqlite3_value *pVal = apVal[i]; ++ ++ if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ ++ /* This is an UPDATE statement, and user-defined column (i-2) was not ++ ** modified. Retrieve the value from Fts5Storage.pSavedRow. */ ++ pVal = sqlite3_column_value(p->pSavedRow, i-1); ++ if( pConfig->bLocale && bUnindexed==0 ){ ++ sqlite3_bind_value(pInsert, pConfig->nCol + i, ++ sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1) ++ ); ++ } ++ }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ ++ const char *pText = 0; ++ const char *pLoc = 0; ++ int nText = 0; ++ int nLoc = 0; ++ assert( pConfig->bLocale ); ++ ++ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); ++ if( rc==SQLITE_OK ){ ++ sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); ++ if( bUnindexed==0 ){ ++ int iLoc = pConfig->nCol + i; ++ sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT); ++ } ++ } ++ ++ continue; ++ } ++ ++ rc = sqlite3_bind_value(pInsert, i, pVal); ++ } + } + if( rc==SQLITE_OK ){ + sqlite3_step(pInsert); +@@ -226598,14 +258637,38 @@ static int sqlite3Fts5StorageIndexInsert( + for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ + ctx.szCol = 0; + if( pConfig->abUnindexed[ctx.iCol]==0 ){ +- const char *zText = (const char*)sqlite3_value_text(apVal[ctx.iCol+2]); +- int nText = sqlite3_value_bytes(apVal[ctx.iCol+2]); +- rc = sqlite3Fts5Tokenize(pConfig, +- FTS5_TOKENIZE_DOCUMENT, +- zText, nText, +- (void*)&ctx, +- fts5StorageInsertCallback +- ); ++ int nText = 0; /* Size of pText in bytes */ ++ const char *pText = 0; /* Pointer to buffer containing text value */ ++ int nLoc = 0; /* Size of pText in bytes */ ++ const char *pLoc = 0; /* Pointer to buffer containing text value */ ++ ++ sqlite3_value *pVal = apVal[ctx.iCol+2]; ++ if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ ++ pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); ++ if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ ++ int iCol = ctx.iCol + 1 + pConfig->nCol; ++ pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol); ++ nLoc = sqlite3_column_bytes(p->pSavedRow, iCol); ++ } ++ }else{ ++ pVal = apVal[ctx.iCol+2]; ++ } ++ ++ if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ ++ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); ++ }else{ ++ pText = (const char*)sqlite3_value_text(pVal); ++ nText = sqlite3_value_bytes(pVal); ++ } ++ ++ if( rc==SQLITE_OK ){ ++ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); ++ rc = sqlite3Fts5Tokenize(pConfig, ++ FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, ++ fts5StorageInsertCallback ++ ); ++ sqlite3Fts5ClearLocale(pConfig); ++ } + } + sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); + p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; +@@ -226733,13 +258796,14 @@ static int fts5StorageIntegrityCallback( + ** some other SQLite error code if an error occurs while attempting to + ** determine this. + */ +-static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ ++static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){ + Fts5Config *pConfig = p->pConfig; +- int rc; /* Return code */ ++ int rc = SQLITE_OK; /* Return code */ + int *aColSize; /* Array of size pConfig->nCol */ + i64 *aTotalSize; /* Array of size pConfig->nCol */ + Fts5IntegrityCtx ctx; + sqlite3_stmt *pScan; ++ int bUseCksum; + + memset(&ctx, 0, sizeof(Fts5IntegrityCtx)); + ctx.pConfig = p->pConfig; +@@ -226748,83 +258812,120 @@ static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ + aColSize = (int*)&aTotalSize[pConfig->nCol]; + memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol); + +- /* Generate the expected index checksum based on the contents of the +- ** %_content table. This block stores the checksum in ctx.cksum. */ +- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); +- if( rc==SQLITE_OK ){ +- int rc2; +- while( SQLITE_ROW==sqlite3_step(pScan) ){ +- int i; +- ctx.iRowid = sqlite3_column_int64(pScan, 0); +- ctx.szCol = 0; +- if( pConfig->bColumnsize ){ +- rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); +- } +- if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ +- rc = sqlite3Fts5TermsetNew(&ctx.pTermset); +- } +- for(i=0; rc==SQLITE_OK && inCol; i++){ +- if( pConfig->abUnindexed[i] ) continue; +- ctx.iCol = i; ++ bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL ++ || (pConfig->eContent==FTS5_CONTENT_EXTERNAL && iArg) ++ ); ++ if( bUseCksum ){ ++ /* Generate the expected index checksum based on the contents of the ++ ** %_content table. This block stores the checksum in ctx.cksum. */ ++ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); ++ if( rc==SQLITE_OK ){ ++ int rc2; ++ while( SQLITE_ROW==sqlite3_step(pScan) ){ ++ int i; ++ ctx.iRowid = sqlite3_column_int64(pScan, 0); + ctx.szCol = 0; +- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ +- rc = sqlite3Fts5TermsetNew(&ctx.pTermset); +- } +- if( rc==SQLITE_OK ){ +- const char *zText = (const char*)sqlite3_column_text(pScan, i+1); +- int nText = sqlite3_column_bytes(pScan, i+1); +- rc = sqlite3Fts5Tokenize(pConfig, +- FTS5_TOKENIZE_DOCUMENT, +- zText, nText, +- (void*)&ctx, +- fts5StorageIntegrityCallback +- ); ++ if( pConfig->bColumnsize ){ ++ rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); + } +- if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ +- rc = FTS5_CORRUPT; ++ if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ ++ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); + } +- aTotalSize[i] += ctx.szCol; +- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ +- sqlite3Fts5TermsetFree(ctx.pTermset); +- ctx.pTermset = 0; ++ for(i=0; rc==SQLITE_OK && inCol; i++){ ++ if( pConfig->abUnindexed[i]==0 ){ ++ const char *pText = 0; ++ int nText = 0; ++ const char *pLoc = 0; ++ int nLoc = 0; ++ sqlite3_value *pVal = sqlite3_column_value(pScan, i+1); ++ ++ if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ++ && sqlite3Fts5IsLocaleValue(pConfig, pVal) ++ ){ ++ rc = sqlite3Fts5DecodeLocaleValue( ++ pVal, &pText, &nText, &pLoc, &nLoc ++ ); ++ }else{ ++ if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ ++ int iCol = i + 1 + pConfig->nCol; ++ pLoc = (const char*)sqlite3_column_text(pScan, iCol); ++ nLoc = sqlite3_column_bytes(pScan, iCol); ++ } ++ pText = (const char*)sqlite3_value_text(pVal); ++ nText = sqlite3_value_bytes(pVal); ++ } ++ ++ ctx.iCol = i; ++ ctx.szCol = 0; ++ ++ if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ ++ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); ++ } ++ ++ if( rc==SQLITE_OK ){ ++ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); ++ rc = sqlite3Fts5Tokenize(pConfig, ++ FTS5_TOKENIZE_DOCUMENT, ++ pText, nText, ++ (void*)&ctx, ++ fts5StorageIntegrityCallback ++ ); ++ sqlite3Fts5ClearLocale(pConfig); ++ } ++ ++ /* If this is not a columnsize=0 database, check that the number ++ ** of tokens in the value matches the aColSize[] value read from ++ ** the %_docsize table. */ ++ if( rc==SQLITE_OK ++ && pConfig->bColumnsize ++ && ctx.szCol!=aColSize[i] ++ ){ ++ rc = FTS5_CORRUPT; ++ } ++ aTotalSize[i] += ctx.szCol; ++ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ ++ sqlite3Fts5TermsetFree(ctx.pTermset); ++ ctx.pTermset = 0; ++ } ++ } + } +- } +- sqlite3Fts5TermsetFree(ctx.pTermset); +- ctx.pTermset = 0; ++ sqlite3Fts5TermsetFree(ctx.pTermset); ++ ctx.pTermset = 0; + +- if( rc!=SQLITE_OK ) break; ++ if( rc!=SQLITE_OK ) break; ++ } ++ rc2 = sqlite3_reset(pScan); ++ if( rc==SQLITE_OK ) rc = rc2; + } +- rc2 = sqlite3_reset(pScan); +- if( rc==SQLITE_OK ) rc = rc2; +- } + +- /* Test that the "totals" (sometimes called "averages") record looks Ok */ +- if( rc==SQLITE_OK ){ +- int i; +- rc = fts5StorageLoadTotals(p, 0); +- for(i=0; rc==SQLITE_OK && inCol; i++){ +- if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT; ++ /* Test that the "totals" (sometimes called "averages") record looks Ok */ ++ if( rc==SQLITE_OK ){ ++ int i; ++ rc = fts5StorageLoadTotals(p, 0); ++ for(i=0; rc==SQLITE_OK && inCol; i++){ ++ if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT; ++ } + } +- } + +- /* Check that the %_docsize and %_content tables contain the expected +- ** number of rows. */ +- if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ +- i64 nRow = 0; +- rc = fts5StorageCount(p, "content", &nRow); +- if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; +- } +- if( rc==SQLITE_OK && pConfig->bColumnsize ){ +- i64 nRow = 0; +- rc = fts5StorageCount(p, "docsize", &nRow); +- if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; ++ /* Check that the %_docsize and %_content tables contain the expected ++ ** number of rows. */ ++ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ ++ i64 nRow = 0; ++ rc = fts5StorageCount(p, "content", &nRow); ++ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; ++ } ++ if( rc==SQLITE_OK && pConfig->bColumnsize ){ ++ i64 nRow = 0; ++ rc = fts5StorageCount(p, "docsize", &nRow); ++ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; ++ } + } + + /* Pass the expected checksum down to the FTS index module. It will + ** verify, amongst other things, that it matches the checksum generated by + ** inspecting the index itself. */ + if( rc==SQLITE_OK ){ +- rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum); ++ rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum, bUseCksum); + } + + sqlite3_free(aTotalSize); +@@ -226904,8 +259005,9 @@ static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ + + assert( p->pConfig->bColumnsize ); + rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); +- if( rc==SQLITE_OK ){ ++ if( pLookup ){ + int bCorrupt = 1; ++ assert( rc==SQLITE_OK ); + sqlite3_bind_int64(pLookup, 1, iRowid); + if( SQLITE_ROW==sqlite3_step(pLookup) ){ + const u8 *aBlob = sqlite3_column_blob(pLookup, 0); +@@ -226918,6 +259020,8 @@ static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ + if( bCorrupt && rc==SQLITE_OK ){ + rc = FTS5_CORRUPT; + } ++ }else{ ++ assert( rc!=SQLITE_OK ); + } + + return rc; +@@ -226963,7 +259067,9 @@ static int sqlite3Fts5StorageSync(Fts5Storage *p){ + i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db); + if( p->bTotalsValid ){ + rc = fts5StorageSaveTotals(p); +- p->bTotalsValid = 0; ++ if( rc==SQLITE_OK ){ ++ p->bTotalsValid = 0; ++ } + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5IndexSync(p->pIndex); +@@ -227206,7 +259312,7 @@ static const unsigned char sqlite3Utf8Trans1[] = { + c = *(zIn++); \ + if( c>=0xc0 ){ \ + c = sqlite3Utf8Trans1[c-0xc0]; \ +- while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ ++ while( zIn=0xc0 ){ \ ++ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \ ++ } \ ++} ++ + typedef struct Unicode61Tokenizer Unicode61Tokenizer; + struct Unicode61Tokenizer { + unsigned char aTokenChar[128]; /* ASCII range token characters */ +@@ -227388,7 +259500,6 @@ static int fts5UnicodeCreate( + zCat = azArg[i+1]; + } + } +- + if( rc==SQLITE_OK ){ + rc = unicodeSetCategories(p, zCat); + } +@@ -227418,7 +259529,6 @@ static int fts5UnicodeCreate( + rc = SQLITE_ERROR; + } + } +- + }else{ + rc = SQLITE_NOMEM; + } +@@ -227557,7 +259667,7 @@ static int fts5UnicodeTokenize( + + typedef struct PorterTokenizer PorterTokenizer; + struct PorterTokenizer { +- fts5_tokenizer tokenizer; /* Parent tokenizer module */ ++ fts5_tokenizer_v2 tokenizer_v2; /* Parent tokenizer module */ + Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */ + char aBuf[FTS5_PORTER_MAX_TOKEN + 64]; + }; +@@ -227569,7 +259679,7 @@ static void fts5PorterDelete(Fts5Tokenizer *pTok){ + if( pTok ){ + PorterTokenizer *p = (PorterTokenizer*)pTok; + if( p->pTokenizer ){ +- p->tokenizer.xDelete(p->pTokenizer); ++ p->tokenizer_v2.xDelete(p->pTokenizer); + } + sqlite3_free(p); + } +@@ -227588,6 +259698,7 @@ static int fts5PorterCreate( + PorterTokenizer *pRet; + void *pUserdata = 0; + const char *zBase = "unicode61"; ++ fts5_tokenizer_v2 *pV2 = 0; + + if( nArg>0 ){ + zBase = azArg[0]; +@@ -227596,14 +259707,15 @@ static int fts5PorterCreate( + pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer)); + if( pRet ){ + memset(pRet, 0, sizeof(PorterTokenizer)); +- rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer); ++ rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2); + }else{ + rc = SQLITE_NOMEM; + } + if( rc==SQLITE_OK ){ + int nArg2 = (nArg>0 ? nArg-1 : 0); +- const char **azArg2 = (nArg2 ? &azArg[1] : 0); +- rc = pRet->tokenizer.xCreate(pUserdata, azArg2, nArg2, &pRet->pTokenizer); ++ const char **az2 = (nArg2 ? &azArg[1] : 0); ++ memcpy(&pRet->tokenizer_v2, pV2, sizeof(fts5_tokenizer_v2)); ++ rc = pRet->tokenizer_v2.xCreate(pUserdata, az2, nArg2, &pRet->pTokenizer); + } + + if( rc!=SQLITE_OK ){ +@@ -228254,6 +260366,7 @@ static int fts5PorterTokenize( + void *pCtx, + int flags, + const char *pText, int nText, ++ const char *pLoc, int nLoc, + int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) + ){ + PorterTokenizer *p = (PorterTokenizer*)pTokenizer; +@@ -228261,11 +260374,194 @@ static int fts5PorterTokenize( + sCtx.xToken = xToken; + sCtx.pCtx = pCtx; + sCtx.aBuf = p->aBuf; +- return p->tokenizer.xTokenize( +- p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb ++ return p->tokenizer_v2.xTokenize( ++ p->pTokenizer, (void*)&sCtx, flags, pText, nText, pLoc, nLoc, fts5PorterCb + ); + } + ++/************************************************************************** ++** Start of trigram implementation. ++*/ ++typedef struct TrigramTokenizer TrigramTokenizer; ++struct TrigramTokenizer { ++ int bFold; /* True to fold to lower-case */ ++ int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */ ++}; ++ ++/* ++** Free a trigram tokenizer. ++*/ ++static void fts5TriDelete(Fts5Tokenizer *p){ ++ sqlite3_free(p); ++} ++ ++/* ++** Allocate a trigram tokenizer. ++*/ ++static int fts5TriCreate( ++ void *pUnused, ++ const char **azArg, ++ int nArg, ++ Fts5Tokenizer **ppOut ++){ ++ int rc = SQLITE_OK; ++ TrigramTokenizer *pNew = 0; ++ UNUSED_PARAM(pUnused); ++ if( nArg%2 ){ ++ rc = SQLITE_ERROR; ++ }else{ ++ int i; ++ pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); ++ if( pNew==0 ){ ++ rc = SQLITE_NOMEM; ++ }else{ ++ pNew->bFold = 1; ++ pNew->iFoldParam = 0; ++ ++ for(i=0; rc==SQLITE_OK && ibFold = (zArg[0]=='0'); ++ } ++ }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ ++ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ ++ rc = SQLITE_ERROR; ++ }else{ ++ pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; ++ } ++ }else{ ++ rc = SQLITE_ERROR; ++ } ++ } ++ ++ if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ ++ rc = SQLITE_ERROR; ++ } ++ ++ if( rc!=SQLITE_OK ){ ++ fts5TriDelete((Fts5Tokenizer*)pNew); ++ pNew = 0; ++ } ++ } ++ } ++ *ppOut = (Fts5Tokenizer*)pNew; ++ return rc; ++} ++ ++/* ++** Trigram tokenizer tokenize routine. ++*/ ++static int fts5TriTokenize( ++ Fts5Tokenizer *pTok, ++ void *pCtx, ++ int unusedFlags, ++ const char *pText, int nText, ++ int (*xToken)(void*, int, const char*, int, int, int) ++){ ++ TrigramTokenizer *p = (TrigramTokenizer*)pTok; ++ int rc = SQLITE_OK; ++ char aBuf[32]; ++ char *zOut = aBuf; ++ int ii; ++ const unsigned char *zIn = (const unsigned char*)pText; ++ const unsigned char *zEof = (zIn ? &zIn[nText] : 0); ++ u32 iCode = 0; ++ int aStart[3]; /* Input offset of each character in aBuf[] */ ++ ++ UNUSED_PARAM(unusedFlags); ++ ++ /* Populate aBuf[] with the characters for the first trigram. */ ++ for(ii=0; ii<3; ii++){ ++ do { ++ aStart[ii] = zIn - (const unsigned char*)pText; ++ if( zIn>=zEof ) return SQLITE_OK; ++ READ_UTF8(zIn, zEof, iCode); ++ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); ++ }while( iCode==0 ); ++ WRITE_UTF8(zOut, iCode); ++ } ++ ++ /* At the start of each iteration of this loop: ++ ** ++ ** aBuf: Contains 3 characters. The 3 characters of the next trigram. ++ ** zOut: Points to the byte following the last character in aBuf. ++ ** aStart[3]: Contains the byte offset in the input text corresponding ++ ** to the start of each of the three characters in the buffer. ++ */ ++ assert( zIn<=zEof ); ++ while( 1 ){ ++ int iNext; /* Start of character following current tri */ ++ const char *z1; ++ ++ /* Read characters from the input up until the first non-diacritic */ ++ do { ++ iNext = zIn - (const unsigned char*)pText; ++ if( zIn>=zEof ){ ++ iCode = 0; ++ break; ++ } ++ READ_UTF8(zIn, zEof, iCode); ++ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); ++ }while( iCode==0 ); ++ ++ /* Pass the current trigram back to fts5 */ ++ rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); ++ if( iCode==0 || rc!=SQLITE_OK ) break; ++ ++ /* Remove the first character from buffer aBuf[]. Append the character ++ ** with codepoint iCode. */ ++ z1 = aBuf; ++ FTS5_SKIP_UTF8(z1); ++ memmove(aBuf, z1, zOut - z1); ++ zOut -= (z1 - aBuf); ++ WRITE_UTF8(zOut, iCode); ++ ++ /* Update the aStart[] array */ ++ aStart[0] = aStart[1]; ++ aStart[1] = aStart[2]; ++ aStart[2] = iNext; ++ } ++ ++ return rc; ++} ++ ++/* ++** Argument xCreate is a pointer to a constructor function for a tokenizer. ++** pTok is a tokenizer previously created using the same method. This function ++** returns one of FTS5_PATTERN_NONE, FTS5_PATTERN_LIKE or FTS5_PATTERN_GLOB ++** indicating the style of pattern matching that the tokenizer can support. ++** In practice, this is: ++** ++** "trigram" tokenizer, case_sensitive=1 - FTS5_PATTERN_GLOB ++** "trigram" tokenizer, case_sensitive=0 (the default) - FTS5_PATTERN_LIKE ++** all other tokenizers - FTS5_PATTERN_NONE ++*/ ++static int sqlite3Fts5TokenizerPattern( ++ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), ++ Fts5Tokenizer *pTok ++){ ++ if( xCreate==fts5TriCreate ){ ++ TrigramTokenizer *p = (TrigramTokenizer*)pTok; ++ if( p->iFoldParam==0 ){ ++ return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; ++ } ++ } ++ return FTS5_PATTERN_NONE; ++} ++ ++/* ++** Return true if the tokenizer described by p->azArg[] is the trigram ++** tokenizer. This tokenizer needs to be loaded before xBestIndex is ++** called for the first time in order to correctly handle LIKE/GLOB. ++*/ ++static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig *p){ ++ return (p->nArg>=1 && 0==sqlite3_stricmp(p->azArg[0], "trigram")); ++} ++ ++ + /* + ** Register all built-in tokenizers with FTS5. + */ +@@ -228276,7 +260572,7 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ + } aBuiltin[] = { + { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, + { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, +- { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }}, ++ { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}}, + }; + + int rc = SQLITE_OK; /* Return code */ +@@ -228290,7 +260586,20 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ + 0 + ); + } +- ++ if( rc==SQLITE_OK ){ ++ fts5_tokenizer_v2 sPorter = { ++ 2, ++ fts5PorterCreate, ++ fts5PorterDelete, ++ fts5PorterTokenize ++ }; ++ rc = pApi->xCreateTokenizer_v2(pApi, ++ "porter", ++ (void*)pApi, ++ &sPorter, ++ 0 ++ ); ++ } + return rc; + } + +@@ -228660,6 +260969,9 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){ + default: return 1; } + break; + ++ ++ default: ++ return 1; + } + return 0; + } +@@ -229069,6 +261381,7 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){ + } + iTbl++; + } ++ aAscii[0] = 0; /* 0x00 is never a token character */ + } + + /* +@@ -229478,9 +261791,11 @@ struct Fts5VocabCursor { + + int bEof; /* True if this cursor is at EOF */ + Fts5IndexIter *pIter; /* Term/rowid iterator object */ ++ void *pStruct; /* From sqlite3Fts5StructureRef() */ + + int nLeTerm; /* Size of zLeTerm in bytes */ + char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ ++ int colUsed; /* Copy of sqlite3_index_info.colUsed */ + + /* These are used by 'col' tables only */ + int iCol; +@@ -229507,9 +261822,11 @@ struct Fts5VocabCursor { + /* + ** Bits for the mask used as the idxNum value by xBestIndex/xFilter. + */ +-#define FTS5_VOCAB_TERM_EQ 0x01 +-#define FTS5_VOCAB_TERM_GE 0x02 +-#define FTS5_VOCAB_TERM_LE 0x04 ++#define FTS5_VOCAB_TERM_EQ 0x0100 ++#define FTS5_VOCAB_TERM_GE 0x0200 ++#define FTS5_VOCAB_TERM_LE 0x0400 ++ ++#define FTS5_VOCAB_COLUSED_MASK 0xFF + + + /* +@@ -229607,12 +261924,12 @@ static int fts5VocabInitVtab( + *pzErr = sqlite3_mprintf("wrong number of vtable arguments"); + rc = SQLITE_ERROR; + }else{ +- int nByte; /* Bytes of space to allocate */ ++ i64 nByte; /* Bytes of space to allocate */ + const char *zDb = bDb ? argv[3] : argv[1]; + const char *zTab = bDb ? argv[4] : argv[3]; + const char *zType = bDb ? argv[5] : argv[4]; +- int nDb = (int)strlen(zDb)+1; +- int nTab = (int)strlen(zTab)+1; ++ i64 nDb = strlen(zDb)+1; ++ i64 nTab = strlen(zTab)+1; + int eType = 0; + + rc = fts5VocabTableType(zType, pzErr, &eType); +@@ -229686,11 +262003,13 @@ static int fts5VocabBestIndexMethod( + int iTermEq = -1; + int iTermGe = -1; + int iTermLe = -1; +- int idxNum = 0; ++ int idxNum = (int)pInfo->colUsed; + int nArg = 0; + + UNUSED_PARAM(pUnused); + ++ assert( (pInfo->colUsed & FTS5_VOCAB_COLUSED_MASK)==pInfo->colUsed ); ++ + for(i=0; inConstraint; i++){ + struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; + if( p->usable==0 ) continue; +@@ -229782,7 +262101,7 @@ static int fts5VocabOpenMethod( + if( rc==SQLITE_OK ){ + pVTab->zErrMsg = sqlite3_mprintf( + "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl +- ); ++ ); + rc = SQLITE_ERROR; + } + }else{ +@@ -229791,7 +262110,7 @@ static int fts5VocabOpenMethod( + } + + if( rc==SQLITE_OK ){ +- int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); ++ i64 nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); + pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte); + } + +@@ -229811,6 +262130,8 @@ static int fts5VocabOpenMethod( + static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ + pCsr->rowid = 0; + sqlite3Fts5IterClose(pCsr->pIter); ++ sqlite3Fts5StructureRelease(pCsr->pStruct); ++ pCsr->pStruct = 0; + pCsr->pIter = 0; + sqlite3_free(pCsr->zLeTerm); + pCsr->nLeTerm = -1; +@@ -229888,9 +262209,11 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ + static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ + Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; + Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; +- int rc = SQLITE_OK; + int nCol = pCsr->pFts5->pConfig->nCol; ++ int rc; + ++ rc = sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct); ++ if( rc!=SQLITE_OK ) return rc; + pCsr->rowid++; + + if( pTab->eType==FTS5_VOCAB_INSTANCE ){ +@@ -229938,9 +262261,19 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ + + switch( pTab->eType ){ + case FTS5_VOCAB_ROW: +- if( eDetail==FTS5_DETAIL_FULL ){ +- while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ +- pCsr->aCnt[0]++; ++ /* Do not bother counting the number of instances if the "cnt" ++ ** column is not being read (according to colUsed). */ ++ if( eDetail==FTS5_DETAIL_FULL && (pCsr->colUsed & 0x04) ){ ++ while( iPosaCnt[] */ ++ pCsr->aCnt[0]++; ++ } + } + } + pCsr->aDoc[0]++; +@@ -230038,11 +262371,12 @@ static int fts5VocabFilterMethod( + if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++]; + if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++]; + if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++]; ++ pCsr->colUsed = (idxNum & FTS5_VOCAB_COLUSED_MASK); + + if( pEq ){ + zTerm = (const char *)sqlite3_value_text(pEq); + nTerm = sqlite3_value_bytes(pEq); +- f = 0; ++ f = FTS5INDEX_QUERY_NOTOKENDATA; + }else{ + if( pGe ){ + zTerm = (const char *)sqlite3_value_text(pGe); +@@ -230064,6 +262398,9 @@ static int fts5VocabFilterMethod( + if( rc==SQLITE_OK ){ + Fts5Index *pIndex = pCsr->pFts5->pIndex; + rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); ++ if( rc==SQLITE_OK ){ ++ pCsr->pStruct = sqlite3Fts5StructureRef(pIndex); ++ } + } + if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ + rc = fts5VocabInstanceNewTerm(pCsr); +@@ -230193,7 +262530,8 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, +- /* xShadowName */ 0 ++ /* xShadowName */ 0, ++ /* xIntegrity */ 0 + }; + void *p = (void*)pGlobal; + +@@ -230201,7 +262539,7 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ + } + + +- ++/* Here ends the fts5.c composite file. */ + #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ + + /************** End of fts5.c ************************************************/ +@@ -230238,6 +262576,16 @@ SQLITE_EXTENSION_INIT1 + + #ifndef SQLITE_OMIT_VIRTUALTABLE + ++ ++#define STMT_NUM_INTEGER_COLUMN 10 ++typedef struct StmtRow StmtRow; ++struct StmtRow { ++ sqlite3_int64 iRowid; /* Rowid value */ ++ char *zSql; /* column "sql" */ ++ int aCol[STMT_NUM_INTEGER_COLUMN+1]; /* all other column values */ ++ StmtRow *pNext; /* Next row to return */ ++}; ++ + /* stmt_vtab is a subclass of sqlite3_vtab which will + ** serve as the underlying representation of a stmt virtual table + */ +@@ -230255,8 +262603,7 @@ typedef struct stmt_cursor stmt_cursor; + struct stmt_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlite3 *db; /* Database connection for this cursor */ +- sqlite3_stmt *pStmt; /* Statement cursor is currently pointing at */ +- sqlite3_int64 iRowid; /* The rowid */ ++ StmtRow *pRow; /* Current row */ + }; + + /* +@@ -230296,11 +262643,15 @@ static int stmtConnect( + #define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */ + + ++ (void)pAux; ++ (void)argc; ++ (void)argv; ++ (void)pzErr; + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep," + "reprep,run,mem)"); + if( rc==SQLITE_OK ){ +- pNew = sqlite3_malloc( sizeof(*pNew) ); ++ pNew = sqlite3_malloc64( sizeof(*pNew) ); + *ppVtab = (sqlite3_vtab*)pNew; + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); +@@ -230322,7 +262673,7 @@ static int stmtDisconnect(sqlite3_vtab *pVtab){ + */ + static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + stmt_cursor *pCur; +- pCur = sqlite3_malloc( sizeof(*pCur) ); ++ pCur = sqlite3_malloc64( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + pCur->db = ((stmt_vtab*)p)->db; +@@ -230330,10 +262681,21 @@ static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + return SQLITE_OK; + } + ++static void stmtCsrReset(stmt_cursor *pCur){ ++ StmtRow *pRow = 0; ++ StmtRow *pNext = 0; ++ for(pRow=pCur->pRow; pRow; pRow=pNext){ ++ pNext = pRow->pNext; ++ sqlite3_free(pRow); ++ } ++ pCur->pRow = 0; ++} ++ + /* + ** Destructor for a stmt_cursor. + */ + static int stmtClose(sqlite3_vtab_cursor *cur){ ++ stmtCsrReset((stmt_cursor*)cur); + sqlite3_free(cur); + return SQLITE_OK; + } +@@ -230344,8 +262706,9 @@ static int stmtClose(sqlite3_vtab_cursor *cur){ + */ + static int stmtNext(sqlite3_vtab_cursor *cur){ + stmt_cursor *pCur = (stmt_cursor*)cur; +- pCur->iRowid++; +- pCur->pStmt = sqlite3_next_stmt(pCur->db, pCur->pStmt); ++ StmtRow *pNext = pCur->pRow->pNext; ++ sqlite3_free(pCur->pRow); ++ pCur->pRow = pNext; + return SQLITE_OK; + } + +@@ -230359,39 +262722,11 @@ static int stmtColumn( + int i /* Which column to return */ + ){ + stmt_cursor *pCur = (stmt_cursor*)cur; +- switch( i ){ +- case STMT_COLUMN_SQL: { +- sqlite3_result_text(ctx, sqlite3_sql(pCur->pStmt), -1, SQLITE_TRANSIENT); +- break; +- } +- case STMT_COLUMN_NCOL: { +- sqlite3_result_int(ctx, sqlite3_column_count(pCur->pStmt)); +- break; +- } +- case STMT_COLUMN_RO: { +- sqlite3_result_int(ctx, sqlite3_stmt_readonly(pCur->pStmt)); +- break; +- } +- case STMT_COLUMN_BUSY: { +- sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt)); +- break; +- } +- default: { +- assert( i==STMT_COLUMN_MEM ); +- i = SQLITE_STMTSTATUS_MEMUSED + +- STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; +- /* Fall thru */ +- } +- case STMT_COLUMN_NSCAN: +- case STMT_COLUMN_NSORT: +- case STMT_COLUMN_NAIDX: +- case STMT_COLUMN_NSTEP: +- case STMT_COLUMN_REPREP: +- case STMT_COLUMN_RUN: { +- sqlite3_result_int(ctx, sqlite3_stmt_status(pCur->pStmt, +- i-STMT_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0)); +- break; +- } ++ StmtRow *pRow = pCur->pRow; ++ if( i==STMT_COLUMN_SQL ){ ++ sqlite3_result_text(ctx, pRow->zSql, -1, SQLITE_TRANSIENT); ++ }else{ ++ sqlite3_result_int(ctx, pRow->aCol[i]); + } + return SQLITE_OK; + } +@@ -230402,7 +262737,7 @@ static int stmtColumn( + */ + static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + stmt_cursor *pCur = (stmt_cursor*)cur; +- *pRowid = pCur->iRowid; ++ *pRowid = pCur->pRow->iRowid; + return SQLITE_OK; + } + +@@ -230412,7 +262747,7 @@ static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + */ + static int stmtEof(sqlite3_vtab_cursor *cur){ + stmt_cursor *pCur = (stmt_cursor*)cur; +- return pCur->pStmt==0; ++ return pCur->pRow==0; + } + + /* +@@ -230427,9 +262762,57 @@ static int stmtFilter( + int argc, sqlite3_value **argv + ){ + stmt_cursor *pCur = (stmt_cursor *)pVtabCursor; +- pCur->pStmt = 0; +- pCur->iRowid = 0; +- return stmtNext(pVtabCursor); ++ sqlite3_stmt *p = 0; ++ sqlite3_int64 iRowid = 1; ++ StmtRow **ppRow = 0; ++ ++ (void)idxNum; ++ (void)idxStr; ++ (void)argc; ++ (void)argv; ++ stmtCsrReset(pCur); ++ ppRow = &pCur->pRow; ++ for(p=sqlite3_next_stmt(pCur->db, 0); p; p=sqlite3_next_stmt(pCur->db, p)){ ++ const char *zSql = sqlite3_sql(p); ++ sqlite3_int64 nSql = zSql ? strlen(zSql)+1 : 0; ++ StmtRow *pNew = (StmtRow*)sqlite3_malloc64(sizeof(StmtRow) + nSql); ++ ++ if( pNew==0 ) return SQLITE_NOMEM; ++ memset(pNew, 0, sizeof(StmtRow)); ++ if( zSql ){ ++ pNew->zSql = (char*)&pNew[1]; ++ memcpy(pNew->zSql, zSql, nSql); ++ } ++ pNew->aCol[STMT_COLUMN_NCOL] = sqlite3_column_count(p); ++ pNew->aCol[STMT_COLUMN_RO] = sqlite3_stmt_readonly(p); ++ pNew->aCol[STMT_COLUMN_BUSY] = sqlite3_stmt_busy(p); ++ pNew->aCol[STMT_COLUMN_NSCAN] = sqlite3_stmt_status( ++ p, SQLITE_STMTSTATUS_FULLSCAN_STEP, 0 ++ ); ++ pNew->aCol[STMT_COLUMN_NSORT] = sqlite3_stmt_status( ++ p, SQLITE_STMTSTATUS_SORT, 0 ++ ); ++ pNew->aCol[STMT_COLUMN_NAIDX] = sqlite3_stmt_status( ++ p, SQLITE_STMTSTATUS_AUTOINDEX, 0 ++ ); ++ pNew->aCol[STMT_COLUMN_NSTEP] = sqlite3_stmt_status( ++ p, SQLITE_STMTSTATUS_VM_STEP, 0 ++ ); ++ pNew->aCol[STMT_COLUMN_REPREP] = sqlite3_stmt_status( ++ p, SQLITE_STMTSTATUS_REPREPARE, 0 ++ ); ++ pNew->aCol[STMT_COLUMN_RUN] = sqlite3_stmt_status( ++ p, SQLITE_STMTSTATUS_RUN, 0 ++ ); ++ pNew->aCol[STMT_COLUMN_MEM] = sqlite3_stmt_status( ++ p, SQLITE_STMTSTATUS_MEMUSED, 0 ++ ); ++ pNew->iRowid = iRowid++; ++ *ppRow = pNew; ++ ppRow = &pNew->pNext; ++ } ++ ++ return SQLITE_OK; + } + + /* +@@ -230442,6 +262825,7 @@ static int stmtBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo + ){ ++ (void)tab; + pIdxInfo->estimatedCost = (double)500; + pIdxInfo->estimatedRows = 500; + return SQLITE_OK; +@@ -230476,6 +262860,7 @@ static sqlite3_module stmtModule = { + 0, /* xRelease */ + 0, /* xRollbackTo */ + 0, /* xShadowName */ ++ 0 /* xIntegrity */ + }; + + #endif /* SQLITE_OMIT_VIRTUALTABLE */ +@@ -230508,10 +262893,7 @@ SQLITE_API int sqlite3_stmt_init( + #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ + + /************** End of stmt.c ************************************************/ +-#if __LINE__!=230511 +-#undef SQLITE_SOURCE_ID +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0alt2" +-#endif + /* Return the source-id for this library */ + SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } ++#endif /* SQLITE_AMALGAMATION */ + /************************** End of sqlite3.c ******************************/ +diff --git a/src/third_party/sqlite3.h b/src/third_party/sqlite3.h +index 910b687a..c2ed7503 100644 +--- a/src/third_party/sqlite3.h ++++ b/src/third_party/sqlite3.h +@@ -43,7 +43,30 @@ extern "C" { + + + /* +-** Provide the ability to override linkage features of the interface. ++** Facilitate override of interface linkage and calling conventions. ++** Be aware that these macros may not be used within this particular ++** translation of the amalgamation and its associated header file. ++** ++** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the ++** compiler that the target identifier should have external linkage. ++** ++** The SQLITE_CDECL macro is used to set the calling convention for ++** public functions that accept a variable number of arguments. ++** ++** The SQLITE_APICALL macro is used to set the calling convention for ++** public functions that accept a fixed number of arguments. ++** ++** The SQLITE_STDCALL macro is no longer used and is now deprecated. ++** ++** The SQLITE_CALLBACK macro is used to set the calling convention for ++** function pointers. ++** ++** The SQLITE_SYSAPI macro is used to set the calling convention for ++** functions provided by the operating system. ++** ++** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and ++** SQLITE_SYSAPI macros are used only when building for environments ++** that require non-default calling conventions. + */ + #ifndef SQLITE_EXTERN + # define SQLITE_EXTERN extern +@@ -110,7 +133,7 @@ extern "C" { + ** + ** Since [version 3.6.18] ([dateof:3.6.18]), + ** SQLite source code has been stored in the +-** Fossil configuration management ++** Fossil configuration management + ** system. ^The SQLITE_SOURCE_ID macro evaluates to + ** a string which identifies a particular check-in of SQLite + ** within its configuration management system. ^The SQLITE_SOURCE_ID +@@ -123,9 +146,9 @@ extern "C" { + ** [sqlite3_libversion_number()], [sqlite3_sourceid()], + ** [sqlite_version()] and [sqlite_source_id()]. + */ +-#define SQLITE_VERSION "3.33.0" +-#define SQLITE_VERSION_NUMBER 3033000 +-#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f" ++#define SQLITE_VERSION "3.50.4" ++#define SQLITE_VERSION_NUMBER 3050004 ++#define SQLITE_SOURCE_ID "2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3" + + /* + ** CAPI3REF: Run-Time Library Version Numbers +@@ -397,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); + ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. + **
      • The application must not modify the SQL statement text passed into + ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ++**
      • The application must not dereference the arrays or string pointers ++** passed as the 3rd and 4th callback parameters after it returns. + ** + */ + SQLITE_API int sqlite3_exec( +@@ -504,6 +529,8 @@ SQLITE_API int sqlite3_exec( + #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) + #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) + #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) ++#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) ++#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) + #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) + #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) + #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) +@@ -536,12 +563,14 @@ SQLITE_API int sqlite3_exec( + #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) + #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) + #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) ++#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) + #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) + #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) ++#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) + #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) + #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) + #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) +-#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) ++#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ + + /* + ** CAPI3REF: Flags For File Open Operations +@@ -549,6 +578,19 @@ SQLITE_API int sqlite3_exec( + ** These bit values are intended for use in the + ** 3rd parameter to the [sqlite3_open_v2()] interface and + ** in the 4th parameter to the [sqlite3_vfs.xOpen] method. ++** ++** Only those flags marked as "Ok for sqlite3_open_v2()" may be ++** used as the third argument to the [sqlite3_open_v2()] interface. ++** The other flags have historically been ignored by sqlite3_open_v2(), ++** though future versions of SQLite might change so that an error is ++** raised if any of the disallowed bits are passed into sqlite3_open_v2(). ++** Applications should not depend on the historical behavior. ++** ++** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into ++** [sqlite3_open_v2()] does *not* cause the underlying database file ++** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into ++** [sqlite3_open_v2()] has historically be a no-op and might become an ++** error in future versions of SQLite. + */ + #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ + #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ +@@ -571,6 +613,7 @@ SQLITE_API int sqlite3_exec( + #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ + #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ + #define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlite3_open_v2() */ ++#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */ + + /* Reserved: 0x00F00000 */ + /* Legacy compatibility: */ +@@ -609,6 +652,13 @@ SQLITE_API int sqlite3_exec( + ** filesystem supports doing multiple write operations atomically when those + ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and + ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. ++** ++** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read ++** from the database file in amounts that are not a multiple of the ++** page size and that do not begin at a page boundary. Without this ++** property, SQLite is careful to only do full-page reads and write ++** on aligned pages, with the one exception that it will do a sub-page ++** read of the first page to access the database header. + */ + #define SQLITE_IOCAP_ATOMIC 0x00000001 + #define SQLITE_IOCAP_ATOMIC512 0x00000002 +@@ -625,19 +675,24 @@ SQLITE_API int sqlite3_exec( + #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 + #define SQLITE_IOCAP_IMMUTABLE 0x00002000 + #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 ++#define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 + + /* + ** CAPI3REF: File Locking Levels + ** + ** SQLite uses one of these integer values as the second + ** argument to calls it makes to the xLock() and xUnlock() methods +-** of an [sqlite3_io_methods] object. ++** of an [sqlite3_io_methods] object. These values are ordered from ++** lest restrictive to most restrictive. ++** ++** The argument to xLock() is always SHARED or higher. The argument to ++** xUnlock is either SHARED or NONE. + */ +-#define SQLITE_LOCK_NONE 0 +-#define SQLITE_LOCK_SHARED 1 +-#define SQLITE_LOCK_RESERVED 2 +-#define SQLITE_LOCK_PENDING 3 +-#define SQLITE_LOCK_EXCLUSIVE 4 ++#define SQLITE_LOCK_NONE 0 /* xUnlock() only */ ++#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */ ++#define SQLITE_LOCK_RESERVED 2 /* xLock() only */ ++#define SQLITE_LOCK_PENDING 3 /* xLock() only */ ++#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */ + + /* + ** CAPI3REF: Synchronization Type Flags +@@ -715,11 +770,18 @@ struct sqlite3_file { + **
      • [SQLITE_LOCK_PENDING], or + **
      • [SQLITE_LOCK_EXCLUSIVE]. + ** +-** xLock() increases the lock. xUnlock() decreases the lock. ++** xLock() upgrades the database file lock. In other words, xLock() moves the ++** database file lock in the direction NONE toward EXCLUSIVE. The argument to ++** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ++** SQLITE_LOCK_NONE. If the database file lock is already at or above the ++** requested lock, then the call to xLock() is a no-op. ++** xUnlock() downgrades the database file lock to either SHARED or NONE. ++** If the lock is already at or below the requested lock state, then the call ++** to xUnlock() is a no-op. + ** The xCheckReservedLock() method checks whether any database connection, + ** either in this process or in some other process, is holding a RESERVED, +-** PENDING, or EXCLUSIVE lock on the file. It returns true +-** if such a lock exists and false otherwise. ++** PENDING, or EXCLUSIVE lock on the file. It returns, via its output ++** pointer parameter, true if such a lock exists and false otherwise. + ** + ** The xFileControl() method is a generic interface that allows custom + ** VFS implementations to directly control an open file using the +@@ -760,6 +822,7 @@ struct sqlite3_file { + **
      • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] + **
      • [SQLITE_IOCAP_IMMUTABLE] + **
      • [SQLITE_IOCAP_BATCH_ATOMIC] ++**
      • [SQLITE_IOCAP_SUBPAGE_READ] + ** + ** + ** The SQLITE_IOCAP_ATOMIC property means that all writes of +@@ -820,9 +883,8 @@ struct sqlite3_io_methods { + ** opcode causes the xFileControl method to write the current state of + ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], + ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) +-** into an integer that the pArg argument points to. This capability +-** is used during testing and is only available when the SQLITE_TEST +-** compile-time option is used. ++** into an integer that the pArg argument points to. ++** This capability is only available if SQLite is compiled with [SQLITE_DEBUG]. + ** + **
      • [[SQLITE_FCNTL_SIZE_HINT]] + ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS +@@ -1038,6 +1100,11 @@ struct sqlite3_io_methods { + ** pointed to by the pArg argument. This capability is used during testing + ** and only needs to be supported when SQLITE_TEST is defined. + ** ++**
      • [[SQLITE_FCNTL_NULL_IO]] ++** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor ++** or file handle for the [sqlite3_file] object such that it will no longer ++** read or write to the database file. ++** + **
      • [[SQLITE_FCNTL_WAL_BLOCK]] + ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might + ** be advantageous to block on the next WAL lock if the lock is not immediately +@@ -1096,6 +1163,12 @@ struct sqlite3_io_methods { + ** the value that M is to be set to. Before returning, the 32-bit signed + ** integer is overwritten with the previous value of M. + ** ++**
      • [[SQLITE_FCNTL_BLOCK_ON_CONNECT]] ++** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the ++** VFS to block when taking a SHARED lock to connect to a wal mode database. ++** This is used to implement the functionality associated with ++** SQLITE_SETLK_BLOCK_ON_CONNECT. ++** + **
      • [[SQLITE_FCNTL_DATA_VERSION]] + ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to + ** a database file. The argument is a pointer to a 32-bit unsigned integer. +@@ -1126,6 +1199,28 @@ struct sqlite3_io_methods { + ** in wal mode after the client has finished copying pages from the wal + ** file to the database file, but before the *-shm file is updated to + ** record the fact that the pages have been checkpointed. ++** ++**
      • [[SQLITE_FCNTL_EXTERNAL_READER]] ++** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect ++** whether or not there is a database client in another process with a wal-mode ++** transaction open on the database or not. It is only available on unix.The ++** (void*) argument passed with this file-control should be a pointer to a ++** value of type (int). The integer value is set to 1 if the database is a wal ++** mode database and there exists at least one client in another process that ++** currently has an SQL transaction open on the database. It is set to 0 if ++** the database is not a wal-mode db, or if there is no such connection in any ++** other process. This opcode cannot be used to detect transactions opened ++** by clients within the current process, only within other processes. ++** ++**
      • [[SQLITE_FCNTL_CKSM_FILE]] ++** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the ++** [checksum VFS shim] only. ++** ++**
      • [[SQLITE_FCNTL_RESET_CACHE]] ++** If there is currently no transaction open on the database, and the ++** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control ++** purges the contents of the in-memory page cache. If there is an open ++** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. + ** + */ + #define SQLITE_FCNTL_LOCKSTATE 1 +@@ -1166,6 +1261,11 @@ struct sqlite3_io_methods { + #define SQLITE_FCNTL_CKPT_DONE 37 + #define SQLITE_FCNTL_RESERVE_BYTES 38 + #define SQLITE_FCNTL_CKPT_START 39 ++#define SQLITE_FCNTL_EXTERNAL_READER 40 ++#define SQLITE_FCNTL_CKSM_FILE 41 ++#define SQLITE_FCNTL_RESET_CACHE 42 ++#define SQLITE_FCNTL_NULL_IO 43 ++#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 + + /* deprecated names */ + #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE +@@ -1195,6 +1295,26 @@ typedef struct sqlite3_mutex sqlite3_mutex; + */ + typedef struct sqlite3_api_routines sqlite3_api_routines; + ++/* ++** CAPI3REF: File Name ++** ++** Type [sqlite3_filename] is used by SQLite to pass filenames to the ++** xOpen method of a [VFS]. It may be cast to (const char*) and treated ++** as a normal, nul-terminated, UTF-8 buffer containing the filename, but ++** may also be passed to special APIs such as: ++** ++**
          ++**
        • sqlite3_filename_database() ++**
        • sqlite3_filename_journal() ++**
        • sqlite3_filename_wal() ++**
        • sqlite3_uri_parameter() ++**
        • sqlite3_uri_boolean() ++**
        • sqlite3_uri_int64() ++**
        • sqlite3_uri_key() ++**
        ++*/ ++typedef const char *sqlite3_filename; ++ + /* + ** CAPI3REF: OS Interface Object + ** +@@ -1373,7 +1493,7 @@ struct sqlite3_vfs { + sqlite3_vfs *pNext; /* Next registered VFS */ + const char *zName; /* Name of this virtual file system */ + void *pAppData; /* Pointer to application-specific data */ +- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, ++ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*, + int flags, int *pOutFlags); + int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); + int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); +@@ -1560,20 +1680,23 @@ SQLITE_API int sqlite3_os_end(void); + ** must ensure that no other SQLite interfaces are invoked by other + ** threads while sqlite3_config() is running. + ** +-** The sqlite3_config() interface +-** may only be invoked prior to library initialization using +-** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. +-** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +-** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. +-** Note, however, that ^sqlite3_config() can be called as part of the +-** implementation of an application-defined [sqlite3_os_init()]. +-** + ** The first argument to sqlite3_config() is an integer + ** [configuration option] that determines + ** what property of SQLite is to be configured. Subsequent arguments + ** vary depending on the [configuration option] + ** in the first argument. + ** ++** For most configuration options, the sqlite3_config() interface ++** may only be invoked prior to library initialization using ++** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. ++** The exceptional configuration options that may be invoked at any time ++** are called "anytime configuration options". ++** ^If sqlite3_config() is called after [sqlite3_initialize()] and before ++** [sqlite3_shutdown()] with a first argument that is not an anytime ++** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. ++** Note, however, that ^sqlite3_config() can be called as part of the ++** implementation of an application-defined [sqlite3_os_init()]. ++** + ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. + ** ^If the option is unknown or SQLite is unable to set the option + ** then this routine returns a non-zero [error code]. +@@ -1681,6 +1804,23 @@ struct sqlite3_mem_methods { + ** These constants are the available integer configuration options that + ** can be passed as the first argument to the [sqlite3_config()] interface. + ** ++** Most of the configuration options for sqlite3_config() ++** will only work if invoked prior to [sqlite3_initialize()] or after ++** [sqlite3_shutdown()]. The few exceptions to this rule are called ++** "anytime configuration options". ++** ^Calling [sqlite3_config()] with a first argument that is not an ++** anytime configuration option in between calls to [sqlite3_initialize()] and ++** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE. ++** ++** The set of anytime configuration options can change (by insertions ++** and/or deletions) from one release of SQLite to the next. ++** As of SQLite version 3.42.0, the complete set of anytime configuration ++** options is: ++**
          ++**
        • SQLITE_CONFIG_LOG ++**
        • SQLITE_CONFIG_PCACHE_HDRSZ ++**
        ++** + ** New configuration options may be added in future releases of SQLite. + ** Existing configuration options might be discontinued. Applications + ** should check the return code from [sqlite3_config()] to make sure that +@@ -1856,13 +1996,16 @@ struct sqlite3_mem_methods { + ** + ** [[SQLITE_CONFIG_LOOKASIDE]]
        SQLITE_CONFIG_LOOKASIDE
        + **
        ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine +-** the default size of lookaside memory on each [database connection]. ++** the default size of [lookaside memory] on each [database connection]. + ** The first argument is the +-** size of each lookaside buffer slot and the second is the number of +-** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE +-** sets the default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] +-** option to [sqlite3_db_config()] can be used to change the lookaside +-** configuration on individual connections.)^
        ++** size of each lookaside buffer slot ("sz") and the second is the number of ++** slots allocated to each database connection ("cnt").)^ ++** ^(SQLITE_CONFIG_LOOKASIDE sets the default lookaside size. ++** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can ++** be used to change the lookaside configuration on individual connections.)^ ++** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the ++** default lookaside configuration at compile-time. ++** + ** + ** [[SQLITE_CONFIG_PCACHE2]]
        SQLITE_CONFIG_PCACHE2
        + **
        ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is +@@ -2011,7 +2154,7 @@ struct sqlite3_mem_methods { + ** is stored in each sorted record and the required column values loaded + ** from the database as records are returned in sorted order. The default + ** value for this option is to never use this optimization. Specifying a +-** negative value for this option restores the default behaviour. ++** negative value for this option restores the default behavior. + ** This option is only available if SQLite is compiled with the + ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. + ** +@@ -2025,30 +2168,46 @@ struct sqlite3_mem_methods { + ** configuration setting is never used, then the default maximum is determined + ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that + ** compile-time option is not set, then the default maximum is 1073741824. ++** ++** [[SQLITE_CONFIG_ROWID_IN_VIEW]] ++**
        SQLITE_CONFIG_ROWID_IN_VIEW ++**
        The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability ++** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is ++** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability ++** defaults to on. This configuration option queries the current setting or ++** changes the setting to off or on. The argument is a pointer to an integer. ++** If that integer initially holds a value of 1, then the ability for VIEWs to ++** have ROWIDs is activated. If the integer initially holds zero, then the ++** ability is deactivated. Any other initial value for the integer leaves the ++** setting unchanged. After changes, if any, the integer is written with ++** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite ++** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and ++** recommended case) then the integer is always filled with zero, regardless ++** if its initial value. + ** + */ +-#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +-#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +-#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +-#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +-#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +-#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ +-#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +-#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +-#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +-#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +-#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +-/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +-#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +-#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +-#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +-#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +-#define SQLITE_CONFIG_URI 17 /* int */ +-#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +-#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ ++#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ ++#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ ++#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ ++#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ ++#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ ++#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ ++#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ ++#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ ++#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ ++#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ ++#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ ++/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ ++#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ ++#define SQLITE_CONFIG_PCACHE 14 /* no-op */ ++#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ ++#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ ++#define SQLITE_CONFIG_URI 17 /* int */ ++#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ ++#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ + #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ +-#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ +-#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ ++#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ ++#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ + #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ + #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ + #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ +@@ -2056,12 +2215,21 @@ struct sqlite3_mem_methods { + #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ + #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ + #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ ++#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ + + /* + ** CAPI3REF: Database Connection Configuration Options + ** + ** These constants are the available integer configuration options that +-** can be passed as the second argument to the [sqlite3_db_config()] interface. ++** can be passed as the second parameter to the [sqlite3_db_config()] interface. ++** ++** The [sqlite3_db_config()] interface is a var-args functions. It takes a ++** variable number of parameters, though always at least two. The number of ++** parameters passed into sqlite3_db_config() depends on which of these ++** constants is given as the second parameter. This documentation page ++** refers to parameters beyond the second as "arguments". Thus, when this ++** page says "the N-th argument" it means "the N-th parameter past the ++** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()". + ** + ** New configuration options may be added in future releases of SQLite. + ** Existing configuration options might be discontinued. Applications +@@ -2073,31 +2241,57 @@ struct sqlite3_mem_methods { + **
        + ** [[SQLITE_DBCONFIG_LOOKASIDE]] + **
        SQLITE_DBCONFIG_LOOKASIDE
        +-**
        ^This option takes three additional arguments that determine the +-** [lookaside memory allocator] configuration for the [database connection]. +-** ^The first argument (the third parameter to [sqlite3_db_config()] is a ++**
        The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the ++** configuration of the [lookaside memory allocator] within a database ++** connection. ++** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are not ++** in the [DBCONFIG arguments|usual format]. ++** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two, ++** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE ++** should have a total of five parameters. ++**
          ++**
        1. The first argument ("buf") is a + ** pointer to a memory buffer to use for lookaside memory. +-** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb +-** may be NULL in which case SQLite will allocate the +-** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the +-** size of each lookaside buffer slot. ^The third argument is the number of +-** slots. The size of the buffer in the first argument must be greater than +-** or equal to the product of the second and third arguments. The buffer +-** must be aligned to an 8-byte boundary. ^If the second argument to +-** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally +-** rounded down to the next smaller multiple of 8. ^(The lookaside memory ++** The first argument may be NULL in which case SQLite will allocate the ++** lookaside buffer itself using [sqlite3_malloc()]. ++**

        2. The second argument ("sz") is the ++** size of each lookaside buffer slot. Lookaside is disabled if "sz" ++** is less than 8. The "sz" argument should be a multiple of 8 less than ++** 65536. If "sz" does not meet this constraint, it is reduced in size until ++** it does. ++**

        3. The third argument ("cnt") is the number of slots. Lookaside is disabled ++** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so ++** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" ++** parameter is usually chosen so that the product of "sz" and "cnt" is less ++** than 1,000,000. ++**

        ++**

        If the "buf" argument is not NULL, then it must ++** point to a memory buffer with a size that is greater than ++** or equal to the product of "sz" and "cnt". ++** The buffer must be aligned to an 8-byte boundary. ++** The lookaside memory + ** configuration for a database connection can only be changed when that + ** connection is not currently using lookaside memory, or in other words +-** when the "current value" returned by +-** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. ++** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero. + ** Any attempt to change the lookaside memory configuration when lookaside + ** memory is in use leaves the configuration unchanged and returns +-** [SQLITE_BUSY].)^

        ++** [SQLITE_BUSY]. ++** If the "buf" argument is NULL and an attempt ++** to allocate memory based on "sz" and "cnt" fails, then ++** lookaside is silently disabled. ++**

        ++** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the ++** default lookaside configuration at initialization. The ++** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside ++** configuration at compile-time. Typical values for lookaside are 1200 for ++** "sz" and 40 to 100 for "cnt". ++**

        + ** + ** [[SQLITE_DBCONFIG_ENABLE_FKEY]] + **
        SQLITE_DBCONFIG_ENABLE_FKEY
        + **
        ^This option is used to enable or disable the enforcement of +-** [foreign key constraints]. There should be two additional arguments. ++** [foreign key constraints]. This is the same setting that is ++** enabled or disabled by the [PRAGMA foreign_keys] statement. + ** The first argument is an integer which is 0 to disable FK enforcement, + ** positive to enable FK enforcement or negative to leave FK enforcement + ** unchanged. The second parameter is a pointer to an integer into which +@@ -2114,25 +2308,37 @@ struct sqlite3_mem_methods { + ** The second parameter is a pointer to an integer into which + ** is written 0 or 1 to indicate whether triggers are disabled or enabled + ** following this call. The second parameter may be a NULL pointer, in +-** which case the trigger setting is not reported back.
        ++** which case the trigger setting is not reported back. ++** ++**

        Originally this option disabled all triggers. ^(However, since ++** SQLite version 3.35.0, TEMP triggers are still allowed even if ++** this option is off. So, in other words, this option now only disables ++** triggers in the main database schema or in the schemas of [ATTACH]-ed ++** databases.)^ + ** + ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] + **

        SQLITE_DBCONFIG_ENABLE_VIEW
        + **
        ^This option is used to enable or disable [CREATE VIEW | views]. +-** There should be two additional arguments. ++** There must be two additional arguments. + ** The first argument is an integer which is 0 to disable views, + ** positive to enable views or negative to leave the setting unchanged. + ** The second parameter is a pointer to an integer into which + ** is written 0 or 1 to indicate whether views are disabled or enabled + ** following this call. The second parameter may be a NULL pointer, in +-** which case the view setting is not reported back.
        ++** which case the view setting is not reported back. ++** ++**

        Originally this option disabled all views. ^(However, since ++** SQLite version 3.35.0, TEMP views are still allowed even if ++** this option is off. So, in other words, this option now only disables ++** views in the main database schema or in the schemas of ATTACH-ed ++** databases.)^ + ** + ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] + **

        SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
        + **
        ^This option is used to enable or disable the + ** [fts3_tokenizer()] function which is part of the + ** [FTS3] full-text search engine extension. +-** There should be two additional arguments. ++** There must be two additional arguments. + ** The first argument is an integer which is 0 to disable fts3_tokenizer() or + ** positive to enable fts3_tokenizer() or negative to leave the setting + ** unchanged. +@@ -2147,7 +2353,7 @@ struct sqlite3_mem_methods { + ** interface independently of the [load_extension()] SQL function. + ** The [sqlite3_enable_load_extension()] API enables or disables both the + ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. +-** There should be two additional arguments. ++** There must be two additional arguments. + ** When the first argument to this interface is 1, then only the C-API is + ** enabled and the SQL function remains disabled. If the first argument to + ** this interface is 0, then both the C-API and the SQL function are disabled. +@@ -2161,23 +2367,30 @@ struct sqlite3_mem_methods { + ** + ** [[SQLITE_DBCONFIG_MAINDBNAME]]
        SQLITE_DBCONFIG_MAINDBNAME
        + **
        ^This option is used to change the name of the "main" database +-** schema. ^The sole argument is a pointer to a constant UTF8 string +-** which will become the new schema name in place of "main". ^SQLite +-** does not make a copy of the new main schema name string, so the application +-** must ensure that the argument passed into this DBCONFIG option is unchanged +-** until after the database connection closes. ++** schema. This option does not follow the ++** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. ++** This option takes exactly one additional argument so that the ++** [sqlite3_db_config()] call has a total of three parameters. The ++** extra argument must be a pointer to a constant UTF8 string which ++** will become the new schema name in place of "main". ^SQLite does ++** not make a copy of the new main schema name string, so the application ++** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME ++** is unchanged until after the database connection closes. + **
        + ** + ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] + **
        SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
        +-**
        Usually, when a database in wal mode is closed or detached from a +-** database handle, SQLite checks if this will mean that there are now no +-** connections at all to the database. If so, it performs a checkpoint +-** operation before closing the connection. This option may be used to +-** override this behaviour. The first parameter passed to this operation +-** is an integer - positive to disable checkpoints-on-close, or zero (the +-** default) to enable them, and negative to leave the setting unchanged. +-** The second parameter is a pointer to an integer ++**
        Usually, when a database in [WAL mode] is closed or detached from a ++** database handle, SQLite checks if if there are other connections to the ++** same database, and if there are no other database connection (if the ++** connection being closed is the last open connection to the database), ++** then SQLite performs a [checkpoint] before closing the connection and ++** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can ++** be used to override that behavior. The first argument passed to this ++** operation (the third parameter to [sqlite3_db_config()]) is an integer ++** which is positive to disable checkpoints-on-close, or zero (the default) ++** to enable them, and negative to leave the setting unchanged. ++** The second argument (the fourth parameter) is a pointer to an integer + ** into which is written 0 or 1 to indicate whether checkpoints-on-close + ** have been disabled - 0 if they are not disabled, 1 if they are. + **
        +@@ -2227,8 +2440,12 @@ struct sqlite3_mem_methods { + **
      • sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); + ** + ** Because resetting a database is destructive and irreversible, the +-** process requires the use of this obscure API and multiple steps to help +-** ensure that it does not happen by accident. ++** process requires the use of this obscure API and multiple steps to ++** help ensure that it does not happen by accident. Because this ++** feature must be capable of resetting corrupt databases, and ++** shutting down virtual tables may require access to that corrupt ++** storage, the library must abandon any installed virtual tables ++** without calling their xDestroy() methods. + ** + ** [[SQLITE_DBCONFIG_DEFENSIVE]]
        SQLITE_DBCONFIG_DEFENSIVE
        + **
        The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the +@@ -2239,6 +2456,7 @@ struct sqlite3_mem_methods { + **
          + **
        • The [PRAGMA writable_schema=ON] statement. + **
        • The [PRAGMA journal_mode=OFF] statement. ++**
        • The [PRAGMA schema_version=N] statement. + **
        • Writes to the [sqlite_dbpage] virtual table. + **
        • Direct writes to [shadow tables]. + **
        +@@ -2266,7 +2484,7 @@ struct sqlite3_mem_methods { + **
        + ** + ** [[SQLITE_DBCONFIG_DQS_DML]] +-**
        SQLITE_DBCONFIG_DQS_DML ++**
        SQLITE_DBCONFIG_DQS_DML
        + **
        The SQLITE_DBCONFIG_DQS_DML option activates or deactivates + ** the legacy [double-quoted string literal] misfeature for DML statements + ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The +@@ -2275,7 +2493,7 @@ struct sqlite3_mem_methods { + **
        + ** + ** [[SQLITE_DBCONFIG_DQS_DDL]] +-**
        SQLITE_DBCONFIG_DQS_DDL ++**
        SQLITE_DBCONFIG_DQS_DDL
        + **
        The SQLITE_DBCONFIG_DQS option activates or deactivates + ** the legacy [double-quoted string literal] misfeature for DDL statements, + ** such as CREATE TABLE and CREATE INDEX. The +@@ -2284,7 +2502,7 @@ struct sqlite3_mem_methods { + **
        + ** + ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] +-**
        SQLITE_DBCONFIG_TRUSTED_SCHEMA ++**
        SQLITE_DBCONFIG_TRUSTED_SCHEMA
        + **
        The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to + ** assume that database schemas are untainted by malicious content. + ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite +@@ -2304,7 +2522,7 @@ struct sqlite3_mem_methods { + **
        + ** + ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] +-**
        SQLITE_DBCONFIG_LEGACY_FILE_FORMAT ++**
        SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
        + **
        The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates + ** the legacy file format flag. When activated, this flag causes all newly + ** created database file to have a schema format version number (the 4-byte +@@ -2313,7 +2531,7 @@ struct sqlite3_mem_methods { + ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, + ** newly created databases are generally not understandable by SQLite versions + ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there +-** is now scarcely any need to generated database files that are compatible ++** is now scarcely any need to generate database files that are compatible + ** all the way back to version 3.0.0, and so this setting is of little + ** practical use, but is provided so that SQLite can continue to claim the + ** ability to generate new database files that are compatible with version +@@ -2322,9 +2540,110 @@ struct sqlite3_mem_methods { + ** the [VACUUM] command will fail with an obscure error when attempting to + ** process a table with generated columns and a descending index. This is + ** not considered a bug since SQLite versions 3.3.0 and earlier do not support +-** either generated columns or decending indexes. ++** either generated columns or descending indexes. ++**
        ++** ++** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] ++**
        SQLITE_DBCONFIG_STMT_SCANSTATUS
        ++**
        The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in ++** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears ++** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() ++** statistics. For statistics to be collected, the flag must be set on ++** the database handle both when the SQL statement is prepared and when it ++** is stepped. The flag is set (collection of statistics is enabled) ++** by default.

        This option takes two arguments: an integer and a pointer to ++** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the statement scanstatus option. If the second argument ++** is not NULL, then the value of the statement scanstatus setting after ++** processing the first argument is written into the integer that the second ++** argument points to. ++**

        ++** ++** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] ++**
        SQLITE_DBCONFIG_REVERSE_SCANORDER
        ++**
        The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order ++** in which tables and indexes are scanned so that the scans start at the end ++** and work toward the beginning rather than starting at the beginning and ++** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the ++** same as setting [PRAGMA reverse_unordered_selects].

        This option takes ++** two arguments which are an integer and a pointer to an integer. The first ++** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ++** reverse scan order flag, respectively. If the second argument is not NULL, ++** then 0 or 1 is written into the integer that the second argument points to ++** depending on if the reverse scan order flag is set after processing the ++** first argument. ++**

        ++** ++** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] ++**
        SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
        ++**
        The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables ++** the ability of the [ATTACH DATABASE] SQL command to create a new database ++** file if the database filed named in the ATTACH command does not already ++** exist. This ability of ATTACH to create a new database is enabled by ++** default. Applications can disable or reenable the ability for ATTACH to ++** create new database files using this DBCONFIG option.

        ++** This option takes two arguments which are an integer and a pointer ++** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the attach-create flag, respectively. If the second ++** argument is not NULL, then 0 or 1 is written into the integer that the ++** second argument points to depending on if the attach-create flag is set ++** after processing the first argument. + **

        ++** ++** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] ++**
        SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
        ++**
        The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the ++** ability of the [ATTACH DATABASE] SQL command to open a database for writing. ++** This capability is enabled by default. Applications can disable or ++** reenable this capability using the current DBCONFIG option. If the ++** the this capability is disabled, the [ATTACH] command will still work, ++** but the database will be opened read-only. If this option is disabled, ++** then the ability to create a new database using [ATTACH] is also disabled, ++** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] ++** option.

        ++** This option takes two arguments which are an integer and a pointer ++** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the ability to ATTACH another database for writing, ++** respectively. If the second argument is not NULL, then 0 or 1 is written ++** into the integer to which the second argument points, depending on whether ++** the ability to ATTACH a read/write database is enabled or disabled ++** after processing the first argument. ++**

        ++** ++** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] ++**
        SQLITE_DBCONFIG_ENABLE_COMMENTS
        ++**
        The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the ++** ability to include comments in SQL text. Comments are enabled by default. ++** An application can disable or reenable comments in SQL text using this ++** DBCONFIG option.

        ++** This option takes two arguments which are an integer and a pointer ++** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ++** leave unchanged the ability to use comments in SQL text, ++** respectively. If the second argument is not NULL, then 0 or 1 is written ++** into the integer that the second argument points to depending on if ++** comments are allowed in SQL text after processing the first argument. ++**

        ++** + ** ++** ++** [[DBCONFIG arguments]]

        Arguments To SQLITE_DBCONFIG Options

        ++** ++**

        Most of the SQLITE_DBCONFIG options take two arguments, so that the ++** overall call to [sqlite3_db_config()] has a total of four parameters. ++** The first argument (the third parameter to sqlite3_db_config()) is a integer. ++** The second argument is a pointer to an integer. If the first argument is 1, ++** then the option becomes enabled. If the first integer argument is 0, then the ++** option is disabled. If the first argument is -1, then the option setting ++** is unchanged. The second argument, the pointer to an integer, may be NULL. ++** If the second argument is not NULL, then a value of 0 or 1 is written into ++** the integer to which the second argument points, depending on whether the ++** setting is disabled or enabled after applying any changes specified by ++** the first argument. ++** ++**

        While most SQLITE_DBCONFIG options use the argument format ++** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] ++** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the ++** documentation of those exceptional options for details. + */ + #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ + #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +@@ -2344,7 +2663,12 @@ struct sqlite3_mem_methods { + #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ + #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ + #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ +-#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */ ++#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ ++#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ ++#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ ++#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ ++#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ ++#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ + + /* + ** CAPI3REF: Enable Or Disable Extended Result Codes +@@ -2432,11 +2756,18 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); + ** CAPI3REF: Count The Number Of Rows Modified + ** METHOD: sqlite3 + ** +-** ^This function returns the number of rows modified, inserted or ++** ^These functions return the number of rows modified, inserted or + ** deleted by the most recently completed INSERT, UPDATE or DELETE + ** statement on the database connection specified by the only parameter. +-** ^Executing any other type of SQL statement does not modify the value +-** returned by this function. ++** The two functions are identical except for the type of the return value ++** and that if the number of rows modified by the most recent INSERT, UPDATE, ++** or DELETE is greater than the maximum value supported by type "int", then ++** the return value of sqlite3_changes() is undefined. ^Executing any other ++** type of SQL statement does not modify the value returned by these functions. ++** For the purposes of this interface, a CREATE TABLE AS SELECT statement ++** does not count as an INSERT, UPDATE or DELETE statement and hence the rows ++** added to the new table by the CREATE TABLE AS SELECT statement are not ++** counted. + ** + ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are + ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], +@@ -2485,16 +2816,21 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); + ** + */ + SQLITE_API int sqlite3_changes(sqlite3*); ++SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*); + + /* + ** CAPI3REF: Total Number Of Rows Modified + ** METHOD: sqlite3 + ** +-** ^This function returns the total number of rows inserted, modified or ++** ^These functions return the total number of rows inserted, modified or + ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed + ** since the database connection was opened, including those executed as +-** part of trigger programs. ^Executing any other type of SQL statement +-** does not affect the value returned by sqlite3_total_changes(). ++** part of trigger programs. The two functions are identical except for the ++** type of the return value and that if the number of rows modified by the ++** connection exceeds the maximum value supported by type "int", then ++** the return value of sqlite3_total_changes() is undefined. ^Executing ++** any other type of SQL statement does not affect the value returned by ++** sqlite3_total_changes(). + ** + ** ^Changes made as part of [foreign key actions] are included in the + ** count, but those made as part of REPLACE constraint resolution are +@@ -2522,6 +2858,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); + ** + */ + SQLITE_API int sqlite3_total_changes(sqlite3*); ++SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); + + /* + ** CAPI3REF: Interrupt A Long-Running Query +@@ -2557,8 +2894,13 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); + ** ^A call to sqlite3_interrupt(D) that occurs when there are no running + ** SQL statements is a no-op and has no effect on SQL statements + ** that are started after the sqlite3_interrupt() call returns. ++** ++** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether ++** or not an interrupt is currently in effect for [database connection] D. ++** It returns 1 if an interrupt is currently in effect, or 0 otherwise. + */ + SQLITE_API void sqlite3_interrupt(sqlite3*); ++SQLITE_API int sqlite3_is_interrupted(sqlite3*); + + /* + ** CAPI3REF: Determine If An SQL Statement Is Complete +@@ -2680,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*); + */ + SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); + ++/* ++** CAPI3REF: Set the Setlk Timeout ++** METHOD: sqlite3 ++** ++** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If ++** the VFS supports blocking locks, it sets the timeout in ms used by ++** eligible locks taken on wal mode databases by the specified database ++** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does ++** not support blocking locks, this function is a no-op. ++** ++** Passing 0 to this function disables blocking locks altogether. Passing ++** -1 to this function requests that the VFS blocks for a long time - ++** indefinitely if possible. The results of passing any other negative value ++** are undefined. ++** ++** Internally, each SQLite database handle store two timeout values - the ++** busy-timeout (used for rollback mode databases, or if the VFS does not ++** support blocking locks) and the setlk-timeout (used for blocking locks ++** on wal-mode databases). The sqlite3_busy_timeout() method sets both ++** values, this function sets only the setlk-timeout value. Therefore, ++** to configure separate busy-timeout and setlk-timeout values for a single ++** database handle, call sqlite3_busy_timeout() followed by this function. ++** ++** Whenever the number of connections to a wal mode database falls from ++** 1 to 0, the last connection takes an exclusive lock on the database, ++** then checkpoints and deletes the wal file. While it is doing this, any ++** new connection that tries to read from the database fails with an ++** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is ++** passed to this API, the new connection blocks until the exclusive lock ++** has been released. ++*/ ++SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); ++ ++/* ++** CAPI3REF: Flags for sqlite3_setlk_timeout() ++*/ ++#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 ++ + /* + ** CAPI3REF: Convenience Routines For Running Queries + ** METHOD: sqlite3 +@@ -3105,8 +3485,8 @@ SQLITE_API int sqlite3_set_authorizer( + #define SQLITE_RECURSIVE 33 /* NULL NULL */ + + /* +-** CAPI3REF: Tracing And Profiling Functions +-** METHOD: sqlite3 ++** CAPI3REF: Deprecated Tracing And Profiling Functions ++** DEPRECATED + ** + ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface + ** instead of the routines described here. +@@ -3176,8 +3556,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, + **

        ^An SQLITE_TRACE_PROFILE callback provides approximately the same + ** information as is provided by the [sqlite3_profile()] callback. + ** ^The P argument is a pointer to the [prepared statement] and the +-** X argument points to a 64-bit integer which is the estimated of +-** the number of nanosecond that the prepared statement took to run. ++** X argument points to a 64-bit integer which is approximately ++** the number of nanoseconds that the prepared statement took to run. + ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. + ** + ** [[SQLITE_TRACE_ROW]]
        SQLITE_TRACE_ROW
        +@@ -3209,8 +3589,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, + ** M argument should be the bitwise OR-ed combination of + ** zero or more [SQLITE_TRACE] constants. + ** +-** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides +-** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). ++** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P) ++** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or ++** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each ++** database connection may have at most one trace callback. + ** + ** ^The X callback is invoked whenever any of the events identified by + ** mask M occur. ^The integer return value from the callback is currently +@@ -3240,7 +3622,7 @@ SQLITE_API int sqlite3_trace_v2( + ** + ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback + ** function X to be invoked periodically during long running calls to +-** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for ++** [sqlite3_step()] and [sqlite3_prepare()] and similar for + ** database connection D. An example use for this + ** interface is to keep a GUI updated during a large query. + ** +@@ -3265,6 +3647,13 @@ SQLITE_API int sqlite3_trace_v2( + ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their + ** database connections for the meaning of "modify" in this paragraph. + ** ++** The progress handler callback would originally only be invoked from the ++** bytecode engine. It still might be invoked during [sqlite3_prepare()] ++** and similar because those routines might force a reparse of the schema ++** which involves running the bytecode engine. However, beginning with ++** SQLite version 3.41.0, the progress handler callback might also be ++** invoked directly from [sqlite3_prepare()] while analyzing and generating ++** code for complex queries. + */ + SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + +@@ -3301,13 +3690,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + ** + **
        + ** ^(
        [SQLITE_OPEN_READONLY]
        +-**
        The database is opened in read-only mode. If the database does not +-** already exist, an error is returned.
        )^ ++**
        The database is opened in read-only mode. If the database does ++** not already exist, an error is returned.
        )^ + ** + ** ^(
        [SQLITE_OPEN_READWRITE]
        +-**
        The database is opened for reading and writing if possible, or reading +-** only if the file is write protected by the operating system. In either +-** case the database must already exist, otherwise an error is returned.
        )^ ++**
        The database is opened for reading and writing if possible, or ++** reading only if the file is write protected by the operating ++** system. In either case the database must already exist, otherwise ++** an error is returned. For historical reasons, if opening in ++** read-write mode fails due to OS-level permissions, an attempt is ++** made to open it in read-only mode. [sqlite3_db_readonly()] can be ++** used to determine whether the database is actually ++** read-write.
        )^ + ** + ** ^(
        [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
        + **
        The database is opened for reading and writing, and is created if +@@ -3345,20 +3739,39 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + **
        The database is opened [shared cache] enabled, overriding + ** the default shared cache setting provided by + ** [sqlite3_enable_shared_cache()].)^ ++** The [use of shared cache mode is discouraged] and hence shared cache ++** capabilities may be omitted from many builds of SQLite. In such cases, ++** this option is a no-op. + ** + ** ^(
        [SQLITE_OPEN_PRIVATECACHE]
        + **
        The database is opened [shared cache] disabled, overriding + ** the default shared cache setting provided by + ** [sqlite3_enable_shared_cache()].)^ + ** ++** [[OPEN_EXRESCODE]] ^(
        [SQLITE_OPEN_EXRESCODE]
        ++**
        The database connection comes up in "extended result code mode". ++** In other words, the database behaves as if ++** [sqlite3_extended_result_codes(db,1)] were called on the database ++** connection as soon as the connection is created. In addition to setting ++** the extended result code mode, this flag also causes [sqlite3_open_v2()] ++** to return an extended result code.
        ++** + ** [[OPEN_NOFOLLOW]] ^(
        [SQLITE_OPEN_NOFOLLOW]
        +-**
        The database filename is not allowed to be a symbolic link
        ++**
        The database filename is not allowed to contain a symbolic link
        + **
        )^ + ** + ** If the 3rd parameter to sqlite3_open_v2() is not one of the + ** required combinations shown above optionally combined with other + ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] +-** then the behavior is undefined. ++** then the behavior is undefined. Historic versions of SQLite ++** have silently ignored surplus bits in the flags parameter to ++** sqlite3_open_v2(), however that behavior might not be carried through ++** into future versions of SQLite and so applications should not rely ++** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op ++** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause ++** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE ++** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not ++** by sqlite3_open_v2(). + ** + ** ^The fourth parameter to sqlite3_open_v2() is the name of the + ** [sqlite3_vfs] object that defines the operating system interface that +@@ -3498,6 +3911,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + ** that uses dot-files in place of posix advisory locking. + **
      • file:data.db?mode=readonly + ** An error. "readonly" is not a valid option for the "mode" parameter. ++** Use "ro" instead: "file:data.db?mode=ro". + **
        + ** + ** ^URI hexadecimal escape sequences (%HH) are supported within the path and +@@ -3547,7 +3961,7 @@ SQLITE_API int sqlite3_open_v2( + ** as F) must be one of: + **
          + **
        • A database filename pointer created by the SQLite core and +-** passed into the xOpen() method of a VFS implemention, or ++** passed into the xOpen() method of a VFS implementation, or + **
        • A filename obtained from [sqlite3_db_filename()], or + **
        • A new filename constructed using [sqlite3_create_filename()]. + **
        +@@ -3602,10 +4016,10 @@ SQLITE_API int sqlite3_open_v2( + ** + ** See the [URI filename] documentation for additional information. + */ +-SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); +-SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +-SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); +-SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); ++SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam); ++SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault); ++SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64); ++SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N); + + /* + ** CAPI3REF: Translate filenames +@@ -3634,9 +4048,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); + ** return value from [sqlite3_db_filename()], then the result is + ** undefined and is likely a memory access violation. + */ +-SQLITE_API const char *sqlite3_filename_database(const char*); +-SQLITE_API const char *sqlite3_filename_journal(const char*); +-SQLITE_API const char *sqlite3_filename_wal(const char*); ++SQLITE_API const char *sqlite3_filename_database(sqlite3_filename); ++SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename); ++SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename); + + /* + ** CAPI3REF: Database File Corresponding To A Journal +@@ -3660,12 +4074,12 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); + /* + ** CAPI3REF: Create and Destroy VFS Filenames + ** +-** These interfces are provided for use by [VFS shim] implementations and ++** These interfaces are provided for use by [VFS shim] implementations and + ** are not useful outside of that context. + ** + ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of + ** database filename D with corresponding journal file J and WAL file W and +-** with N URI parameters key/values pairs in the array P. The result from ++** an array P of N URI Key/Value pairs. The result from + ** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that + ** is safe to pass to routines like: + **
          +@@ -3696,20 +4110,20 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); + ** If the Y parameter to sqlite3_free_filename(Y) is anything other + ** than a NULL pointer or a pointer previously acquired from + ** sqlite3_create_filename(), then bad things such as heap +-** corruption or segfaults may occur. The value Y should be ++** corruption or segfaults may occur. The value Y should not be + ** used again after sqlite3_free_filename(Y) has been called. This means + ** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y, + ** then the corresponding [sqlite3_module.xClose() method should also be + ** invoked prior to calling sqlite3_free_filename(Y). + */ +-SQLITE_API char *sqlite3_create_filename( ++SQLITE_API sqlite3_filename sqlite3_create_filename( + const char *zDatabase, + const char *zJournal, + const char *zWal, + int nParam, + const char **azParam + ); +-SQLITE_API void sqlite3_free_filename(char*); ++SQLITE_API void sqlite3_free_filename(sqlite3_filename); + + /* + ** CAPI3REF: Error Codes And Messages +@@ -3728,27 +4142,38 @@ SQLITE_API void sqlite3_free_filename(char*); + ** sqlite3_extended_errcode() might change with each API call. + ** Except, there are some interfaces that are guaranteed to never + ** change the value of the error code. The error-code preserving +-** interfaces are: ++** interfaces include the following: + ** + **
            + **
          • sqlite3_errcode() + **
          • sqlite3_extended_errcode() + **
          • sqlite3_errmsg() + **
          • sqlite3_errmsg16() ++**
          • sqlite3_error_offset() + **
          + ** + ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language +-** text that describes the error, as either UTF-8 or UTF-16 respectively. ++** text that describes the error, as either UTF-8 or UTF-16 respectively, ++** or NULL if no error message is available. ++** (See how SQLite handles [invalid UTF] for exceptions to this rule.) + ** ^(Memory to hold the error message string is managed internally. + ** The application does not need to worry about freeing the result. + ** However, the error string might be overwritten or deallocated by + ** subsequent calls to other SQLite interface functions.)^ + ** +-** ^The sqlite3_errstr() interface returns the English-language text +-** that describes the [result code], as UTF-8. ++** ^The sqlite3_errstr(E) interface returns the English-language text ++** that describes the [result code] E, as UTF-8, or NULL if E is not an ++** result code for which a text error message is available. + ** ^(Memory to hold the error message string is managed internally + ** and must not be freed by the application)^. + ** ++** ^If the most recent error references a specific token in the input ++** SQL, the sqlite3_error_offset() interface returns the byte offset ++** of the start of that token. ^The byte offset returned by ++** sqlite3_error_offset() assumes that the input SQL is UTF8. ++** ^If the most recent error does not reference a specific token in the input ++** SQL, then the sqlite3_error_offset() function returns -1. ++** + ** When the serialized [threading mode] is in use, it might be the + ** case that a second error occurs on a separate thread in between + ** the time of the first error and the call to these interfaces. +@@ -3768,6 +4193,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); + SQLITE_API const char *sqlite3_errmsg(sqlite3*); + SQLITE_API const void *sqlite3_errmsg16(sqlite3*); + SQLITE_API const char *sqlite3_errstr(int); ++SQLITE_API int sqlite3_error_offset(sqlite3 *db); + + /* + ** CAPI3REF: Prepared Statement Object +@@ -3939,11 +4365,22 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); + **
          The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler + ** to return an error (error code SQLITE_ERROR) if the statement uses + ** any virtual tables. ++** ++** [[SQLITE_PREPARE_DONT_LOG]]
          SQLITE_PREPARE_DONT_LOG
          ++**
          The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler ++** errors from being sent to the error log defined by ++** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test ++** compiles to see if some SQL syntax is well-formed, without generating ++** messages on the global error log when it is not. If the test compile ++** fails, the sqlite3_prepare_v3() call returns the same error indications ++** with or without this flag; it just omits the call to [sqlite3_log()] that ++** logs the error. + ** + */ + #define SQLITE_PREPARE_PERSISTENT 0x01 + #define SQLITE_PREPARE_NORMALIZE 0x02 + #define SQLITE_PREPARE_NO_VTAB 0x04 ++#define SQLITE_PREPARE_DONT_LOG 0x10 + + /* + ** CAPI3REF: Compiling An SQL Statement +@@ -3976,13 +4413,17 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); + ** and sqlite3_prepare16_v3() use UTF-16. + ** + ** ^If the nByte argument is negative, then zSql is read up to the +-** first zero terminator. ^If nByte is positive, then it is the +-** number of bytes read from zSql. ^If nByte is zero, then no prepared ++** first zero terminator. ^If nByte is positive, then it is the maximum ++** number of bytes read from zSql. When nByte is positive, zSql is read ++** up to the first zero terminator or until the nByte bytes have been read, ++** whichever comes first. ^If nByte is zero, then no prepared + ** statement is generated. + ** If the caller knows that the supplied string is nul-terminated, then + ** there is a small performance advantage to passing an nByte parameter that + ** is the number of bytes in the input string including + ** the nul-terminator. ++** Note that nByte measure the length of the input in bytes, not ++** characters, even for the UTF-16 interfaces. + ** + ** ^If pzTail is not NULL then *pzTail is made to point to the first byte + ** past the end of the first SQL statement in zSql. These routines only +@@ -4125,12 +4566,17 @@ SQLITE_API int sqlite3_prepare16_v3( + ** are managed by SQLite and are automatically freed when the prepared + ** statement is finalized. + ** ^The string returned by sqlite3_expanded_sql(P), on the other hand, +-** is obtained from [sqlite3_malloc()] and must be free by the application ++** is obtained from [sqlite3_malloc()] and must be freed by the application + ** by passing it to [sqlite3_free()]. ++** ++** ^The sqlite3_normalized_sql() interface is only available if ++** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. + */ + SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); + SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); ++#ifdef SQLITE_ENABLE_NORMALIZE + SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); ++#endif + + /* + ** CAPI3REF: Determine If An SQL Statement Writes The Database +@@ -4165,6 +4611,19 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); + ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and + ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so + ** sqlite3_stmt_readonly() returns false for those commands. ++** ++** ^This routine returns false if there is any possibility that the ++** statement might change the database file. ^A false return does ++** not guarantee that the statement will change the database file. ++** ^For example, an UPDATE statement might have a WHERE clause that ++** makes it a no-op, but the sqlite3_stmt_readonly() result would still ++** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a ++** read-only no-op if the table already exists, but ++** sqlite3_stmt_readonly() still returns false for such a statement. ++** ++** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] ++** statement, then sqlite3_stmt_readonly(X) returns the same value as ++** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. + */ + SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); + +@@ -4180,6 +4639,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); + */ + SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); + ++/* ++** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement ++** METHOD: sqlite3_stmt ++** ++** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN ++** setting for [prepared statement] S. If E is zero, then S becomes ++** a normal prepared statement. If E is 1, then S behaves as if ++** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if ++** its SQL text began with "[EXPLAIN QUERY PLAN]". ++** ++** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared. ++** SQLite tries to avoid a reprepare, but a reprepare might be necessary ++** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode. ++** ++** Because of the potential need to reprepare, a call to ++** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be ++** reprepared because it was created using [sqlite3_prepare()] instead of ++** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and ++** hence has no saved SQL text with which to reprepare. ++** ++** Changing the explain setting for a prepared statement does not change ++** the original SQL text for the statement. Hence, if the SQL text originally ++** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) ++** is called to convert the statement into an ordinary statement, the EXPLAIN ++** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) ++** output, even though the statement now acts like a normal SQL statement. ++** ++** This routine returns SQLITE_OK if the explain mode is successfully ++** changed, or an error code if the explain mode could not be changed. ++** The explain mode cannot be changed while a statement is active. ++** Hence, it is good practice to call [sqlite3_reset(S)] ++** immediately prior to calling sqlite3_stmt_explain(S,E). ++*/ ++SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode); ++ + /* + ** CAPI3REF: Determine If A Prepared Statement Has Been Reset + ** METHOD: sqlite3_stmt +@@ -4233,6 +4727,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + ** + ** ^The sqlite3_value objects that are passed as parameters into the + ** implementation of [application-defined SQL functions] are protected. ++** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()] ++** are protected. + ** ^The sqlite3_value object returned by + ** [sqlite3_column_value()] is unprotected. + ** Unprotected sqlite3_value objects may only be used as arguments +@@ -4264,7 +4760,7 @@ typedef struct sqlite3_context sqlite3_context; + ** METHOD: sqlite3_stmt + ** + ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, +-** literals may be replaced by a [parameter] that matches one of following ++** literals may be replaced by a [parameter] that matches one of the following + ** templates: + ** + **
            +@@ -4309,7 +4805,7 @@ typedef struct sqlite3_context sqlite3_context; + ** + ** [[byte-order determination rules]] ^The byte-order of + ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) +-** found in first character, which is removed, or in the absence of a BOM ++** found in the first character, which is removed, or in the absence of a BOM + ** the byte order is the native byte order of the host + ** machine for sqlite3_bind_text16() or the byte order specified in + ** the 6th parameter for sqlite3_bind_text64().)^ +@@ -4329,23 +4825,27 @@ typedef struct sqlite3_context sqlite3_context; + ** or sqlite3_bind_text16() or sqlite3_bind_text64() then + ** that parameter must be the byte offset + ** where the NUL terminator would occur assuming the string were NUL +-** terminated. If any NUL characters occurs at byte offsets less than ++** terminated. If any NUL characters occur at byte offsets less than + ** the value of the fourth parameter then the resulting string value will + ** contain embedded NULs. The result of expressions involving strings + ** with embedded NULs is undefined. + ** +-** ^The fifth argument to the BLOB and string binding interfaces +-** is a destructor used to dispose of the BLOB or +-** string after SQLite has finished with it. ^The destructor is called +-** to dispose of the BLOB or string even if the call to the bind API fails, +-** except the destructor is not called if the third parameter is a NULL +-** pointer or the fourth parameter is negative. +-** ^If the fifth argument is +-** the special value [SQLITE_STATIC], then SQLite assumes that the +-** information is in static, unmanaged space and does not need to be freed. +-** ^If the fifth argument has the value [SQLITE_TRANSIENT], then +-** SQLite makes its own private copy of the data immediately, before +-** the sqlite3_bind_*() routine returns. ++** ^The fifth argument to the BLOB and string binding interfaces controls ++** or indicates the lifetime of the object referenced by the third parameter. ++** These three options exist: ++** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished ++** with it may be passed. ^It is called to dispose of the BLOB or string even ++** if the call to the bind API fails, except the destructor is not called if ++** the third parameter is a NULL pointer or the fourth parameter is negative. ++** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that ++** the application remains responsible for disposing of the object. ^In this ++** case, the object and the provided pointer to it must remain valid until ++** either the prepared statement is finalized or the same SQL parameter is ++** bound to something else, whichever occurs sooner. ++** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the ++** object is to be copied prior to the return from sqlite3_bind_*(). ^The ++** object and pointer to it must remain valid until then. ^SQLite will then ++** manage the lifetime of its private copy. + ** + ** ^The sixth argument to sqlite3_bind_text64() must be one of + ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] +@@ -4537,7 +5037,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); + ** METHOD: sqlite3_stmt + ** + ** ^These routines provide a means to determine the database, table, and +-** table column that is the origin of a particular result column in ++** table column that is the origin of a particular result column in a + ** [SELECT] statement. + ** ^The name of the database or table or column can be returned as + ** either a UTF-8 or UTF-16 string. ^The _database_ routines return +@@ -4675,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); + ** other than [SQLITE_ROW] before any subsequent invocation of + ** sqlite3_step(). Failure to reset the prepared statement using + ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from +-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], ++** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), + ** sqlite3_step() began + ** calling [sqlite3_reset()] automatically in this circumstance rather + ** than returning [SQLITE_MISUSE]. This is not considered a compatibility +@@ -4850,6 +5350,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + ** even empty strings, are always zero-terminated. ^The return + ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. + ** ++** ^Strings returned by sqlite3_column_text16() always have the endianness ++** which is native to the platform, regardless of the text encoding set ++** for the database. ++** + ** Warning: ^The object returned by [sqlite3_column_value()] is an + ** [unprotected sqlite3_value] object. In a multithreaded environment, + ** an unprotected sqlite3_value object may only be used safely with +@@ -4863,7 +5367,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + ** [application-defined SQL functions] or [virtual tables], not within + ** top-level application code. + ** +-** The these routines may attempt to convert the datatype of the result. ++** These routines may attempt to convert the datatype of the result. + ** ^For example, if the internal representation is FLOAT and a text result + ** is requested, [sqlite3_snprintf()] is used internally to perform the + ** conversion automatically. ^(The following table details the conversions +@@ -4888,7 +5392,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + **
        TEXT BLOB No change + **
        BLOB INTEGER [CAST] to INTEGER + **
        BLOB FLOAT [CAST] to REAL +-**
        BLOB TEXT Add a zero terminator if needed ++**
        BLOB TEXT [CAST] to TEXT, ensure zero terminator + **
        + **
    )^ + ** +@@ -5012,20 +5516,33 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); + ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S + ** back to the beginning of its program. + ** +-** ^If the most recent call to [sqlite3_step(S)] for the +-** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +-** or if [sqlite3_step(S)] has never before been called on S, +-** then [sqlite3_reset(S)] returns [SQLITE_OK]. ++** ^The return code from [sqlite3_reset(S)] indicates whether or not ++** the previous evaluation of prepared statement S completed successfully. ++** ^If [sqlite3_step(S)] has never before been called on S or if ++** [sqlite3_step(S)] has not been called since the previous call ++** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return ++** [SQLITE_OK]. + ** + ** ^If the most recent call to [sqlite3_step(S)] for the + ** [prepared statement] S indicated an error, then + ** [sqlite3_reset(S)] returns an appropriate [error code]. ++** ^The [sqlite3_reset(S)] interface might also return an [error code] ++** if there were no prior errors but the process of resetting ++** the prepared statement caused a new error. ^For example, if an ++** [INSERT] statement with a [RETURNING] clause is only stepped one time, ++** that one call to [sqlite3_step(S)] might return SQLITE_ROW but ++** the overall statement might still fail and the [sqlite3_reset(S)] call ++** might return SQLITE_BUSY if locking constraints prevent the ++** database change from committing. Therefore, it is important that ++** applications check the return code from [sqlite3_reset(S)] even if ++** no prior call to [sqlite3_step(S)] indicated a problem. + ** + ** ^The [sqlite3_reset(S)] interface does not change the values + ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. + */ + SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + ++ + /* + ** CAPI3REF: Create Or Redefine SQL Functions + ** KEYWORDS: {function creation routines} +@@ -5087,17 +5604,15 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, + ** index expressions, or the WHERE clause of partial indexes. + ** +-** + ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for + ** all application-defined SQL functions that do not need to be +-** used inside of triggers, view, CHECK constraints, or other elements of +-** the database schema. This flags is especially recommended for SQL ++** used inside of triggers, views, CHECK constraints, or other elements of ++** the database schema. This flag is especially recommended for SQL + ** functions that have side effects or reveal internal application state. + ** Without this flag, an attacker might be able to modify the schema of + ** a database file to include invocations of the function with parameters + ** chosen by the attacker, which the application will then execute when + ** the database file is opened and read. +-** + ** + ** ^(The fifth parameter is an arbitrary pointer. The implementation of the + ** function can gain access to this pointer using [sqlite3_user_data()].)^ +@@ -5123,7 +5638,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + ** [user-defined window functions|available here]. + ** + ** ^(If the final parameter to sqlite3_create_function_v2() or +-** sqlite3_create_window_function() is not NULL, then it is destructor for ++** sqlite3_create_window_function() is not NULL, then it is the destructor for + ** the application data pointer. The destructor is invoked when the function + ** is deleted, either by being overloaded or when the database connection + ** closes.)^ ^The destructor is also invoked if the call to +@@ -5233,10 +5748,21 @@ SQLITE_API int sqlite3_create_window_function( + ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in + ** schema structures such as [CHECK constraints], [DEFAULT clauses], + ** [expression indexes], [partial indexes], or [generated columns]. +-** The SQLITE_DIRECTONLY flags is a security feature which is recommended +-** for all [application-defined SQL functions], and especially for functions +-** that have side-effects or that could potentially leak sensitive +-** information. ++**

    ++** The SQLITE_DIRECTONLY flag is recommended for any ++** [application-defined SQL function] ++** that has side-effects or that could potentially leak sensitive information. ++** This will prevent attacks in which an application is tricked ++** into using a database file that has had its schema surreptitiously ++** modified to invoke the application-defined function in ways that are ++** harmful. ++**

    ++** Some people say it is good practice to set SQLITE_DIRECTONLY on all ++** [application-defined SQL functions], regardless of whether or not they ++** are security sensitive, as doing so prevents those functions from being used ++** inside of the database schema, and thus ensures that the database ++** can be inspected and modified using generic tools (such as the [CLI]) ++** that do not have access to the application-defined functions. + ** + ** + ** [[SQLITE_INNOCUOUS]]

    SQLITE_INNOCUOUS
    +@@ -5263,13 +5789,36 @@ SQLITE_API int sqlite3_create_window_function( + **
    + ** + ** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    +-** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call ++** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call + ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. +-** Specifying this flag makes no difference for scalar or aggregate user +-** functions. However, if it is not specified for a user-defined window +-** function, then any sub-types belonging to arguments passed to the window +-** function may be discarded before the window function is called (i.e. +-** sqlite3_value_subtype() will always return 0). ++** This flag instructs SQLite to omit some corner-case optimizations that ++** might disrupt the operation of the [sqlite3_value_subtype()] function, ++** causing it to return zero rather than the correct subtype(). ++** All SQL functions that invoke [sqlite3_value_subtype()] should have this ++** property. If the SQLITE_SUBTYPE property is omitted, then the return ++** value from [sqlite3_value_subtype()] might sometimes be zero even though ++** a non-zero subtype was specified by the function argument expression. ++** ++** [[SQLITE_RESULT_SUBTYPE]]
    SQLITE_RESULT_SUBTYPE
    ++** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call ++** [sqlite3_result_subtype()] to cause a sub-type to be associated with its ++** result. ++** Every function that invokes [sqlite3_result_subtype()] should have this ++** property. If it does not, then the call to [sqlite3_result_subtype()] ++** might become a no-op if the function is used as term in an ++** [expression index]. On the other hand, SQL functions that never invoke ++** [sqlite3_result_subtype()] should avoid setting this property, as the ++** purpose of this property is to disable certain optimizations that are ++** incompatible with subtypes. ++** ++** [[SQLITE_SELFORDER1]]
    SQLITE_SELFORDER1
    ++** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate ++** that internally orders the values provided to the first argument. The ++** ordered-set aggregate SQL notation with a single ORDER BY term can be ++** used to invoke this function. If the ordered-set aggregate notation is ++** used on a function that lacks this flag, then an error is raised. Note ++** that the ordered-set aggregate syntax is only available if SQLite is ++** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. + **
    + **
    + */ +@@ -5277,6 +5826,8 @@ SQLITE_API int sqlite3_create_window_function( + #define SQLITE_DIRECTONLY 0x000080000 + #define SQLITE_SUBTYPE 0x000100000 + #define SQLITE_INNOCUOUS 0x000200000 ++#define SQLITE_RESULT_SUBTYPE 0x001000000 ++#define SQLITE_SELFORDER1 0x002000000 + + /* + ** CAPI3REF: Deprecated Functions +@@ -5442,6 +5993,28 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); + SQLITE_API int sqlite3_value_nochange(sqlite3_value*); + SQLITE_API int sqlite3_value_frombind(sqlite3_value*); + ++/* ++** CAPI3REF: Report the internal text encoding state of an sqlite3_value object ++** METHOD: sqlite3_value ++** ++** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], ++** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding ++** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) ++** returns something other than SQLITE_TEXT, then the return value from ++** sqlite3_value_encoding(X) is meaningless. ^Calls to ++** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)], ++** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or ++** [sqlite3_value_bytes16(X)] might change the encoding of the value X and ++** thus change the return from subsequent calls to sqlite3_value_encoding(X). ++** ++** This routine is intended for used by applications that test and validate ++** the SQLite implementation. This routine is inquiring about the opaque ++** internal state of an [sqlite3_value] object. Ordinary applications should ++** not need to know what the internal state of an sqlite3_value object is and ++** hence should not need to use this interface. ++*/ ++SQLITE_API int sqlite3_value_encoding(sqlite3_value*); ++ + /* + ** CAPI3REF: Finding The Subtype Of SQL Values + ** METHOD: sqlite3_value +@@ -5451,6 +6024,12 @@ SQLITE_API int sqlite3_value_frombind(sqlite3_value*); + ** information can be used to pass a limited amount of context from + ** one SQL function to another. Use the [sqlite3_result_subtype()] + ** routine to set the subtype for the return value of an SQL function. ++** ++** Every [application-defined SQL function] that invokes this interface ++** should include the [SQLITE_SUBTYPE] property in the text ++** encoding argument when the function is [sqlite3_create_function|registered]. ++** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() ++** might return zero instead of the upstream subtype in some corner cases. + */ + SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); + +@@ -5459,10 +6038,11 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); + ** METHOD: sqlite3_value + ** + ** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value] +-** object D and returns a pointer to that copy. ^The [sqlite3_value] returned ++** object V and returns a pointer to that copy. ^The [sqlite3_value] returned + ** is a [protected sqlite3_value] object even if the input is not. + ** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a +-** memory allocation fails. ++** memory allocation fails. ^If V is a [pointer value], then the result ++** of sqlite3_value_dup(V) is a NULL value. + ** + ** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object + ** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer +@@ -5493,10 +6073,10 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*); + ** + ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer + ** when first called if N is less than or equal to zero or if a memory +-** allocate error occurs. ++** allocation error occurs. + ** + ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is +-** determined by the N parameter on first successful call. Changing the ++** determined by the N parameter on the first successful call. Changing the + ** value of N in any subsequent call to sqlite3_aggregate_context() within + ** the same aggregate function instance will not resize the memory + ** allocation.)^ Within the xFinal callback, it is customary to set +@@ -5548,48 +6128,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + ** METHOD: sqlite3_context + ** + ** These functions may be used by (non-aggregate) SQL functions to +-** associate metadata with argument values. If the same value is passed to +-** multiple invocations of the same SQL function during query execution, under +-** some circumstances the associated metadata may be preserved. An example +-** of where this might be useful is in a regular-expression matching +-** function. The compiled version of the regular expression can be stored as +-** metadata associated with the pattern string. ++** associate auxiliary data with argument values. If the same argument ++** value is passed to multiple invocations of the same SQL function during ++** query execution, under some circumstances the associated auxiliary data ++** might be preserved. An example of where this might be useful is in a ++** regular-expression matching function. The compiled version of the regular ++** expression can be stored as auxiliary data associated with the pattern string. + ** Then as long as the pattern string remains the same, + ** the compiled regular expression can be reused on multiple + ** invocations of the same function. + ** +-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata ++** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data + ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument + ** value to the application-defined function. ^N is zero for the left-most +-** function argument. ^If there is no metadata ++** function argument. ^If there is no auxiliary data + ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface + ** returns a NULL pointer. + ** +-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th +-** argument of the application-defined function. ^Subsequent ++** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the ++** N-th argument of the application-defined function. ^Subsequent + ** calls to sqlite3_get_auxdata(C,N) return P from the most recent +-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or +-** NULL if the metadata has been discarded. ++** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or ++** NULL if the auxiliary data has been discarded. + ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, + ** SQLite will invoke the destructor function X with parameter P exactly +-** once, when the metadata is discarded. +-** SQLite is free to discard the metadata at any time, including:
      ++** once, when the auxiliary data is discarded. ++** SQLite is free to discard the auxiliary data at any time, including:
        + **
      • ^(when the corresponding function parameter changes)^, or + **
      • ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the + ** SQL statement)^, or + **
      • ^(when sqlite3_set_auxdata() is invoked again on the same + ** parameter)^, or + **
      • ^(during the original sqlite3_set_auxdata() call when a memory +-** allocation error occurs.)^
      ++** allocation error occurs.)^ ++**
    • ^(during the original sqlite3_set_auxdata() call if the function ++** is evaluated during query planning instead of during query execution, ++** as sometimes happens with [SQLITE_ENABLE_STAT4].)^
    + ** +-** Note the last bullet in particular. The destructor X in ++** Note the last two bullets in particular. The destructor X in + ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the + ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() + ** should be called near the end of the function implementation and the + ** function implementation should not make any use of P after +-** sqlite3_set_auxdata() has been called. +-** +-** ^(In practice, metadata is preserved between function calls for ++** sqlite3_set_auxdata() has been called. Furthermore, a call to ++** sqlite3_get_auxdata() that occurs immediately after a corresponding call ++** to sqlite3_set_auxdata() might still return NULL if an out-of-memory ++** condition occurred during the sqlite3_set_auxdata() call or if the ++** function is being evaluated during query planning rather than during ++** query execution. ++** ++** ^(In practice, auxiliary data is preserved between function calls for + ** function parameters that are compile-time constants, including literal + ** values and [parameters] and expressions composed from the same.)^ + ** +@@ -5599,10 +6187,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + ** + ** These routines must be called from the same thread in which + ** the SQL function is running. ++** ++** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()]. + */ + SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); + SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); + ++/* ++** CAPI3REF: Database Connection Client Data ++** METHOD: sqlite3 ++** ++** These functions are used to associate one or more named pointers ++** with a [database connection]. ++** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P ++** to be attached to [database connection] D using name N. Subsequent ++** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P ++** or a NULL pointer if there were no prior calls to ++** sqlite3_set_clientdata() with the same values of D and N. ++** Names are compared using strcmp() and are thus case sensitive. ++** ++** If P and X are both non-NULL, then the destructor X is invoked with ++** argument P on the first of the following occurrences: ++**
      ++**
    • An out-of-memory error occurs during the call to ++** sqlite3_set_clientdata() which attempts to register pointer P. ++**
    • A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made ++** with the same D and N parameters. ++**
    • The database connection closes. SQLite does not make any guarantees ++** about the order in which destructors are called, only that all ++** destructors will be called exactly once at some point during the ++** database connection closing process. ++**
    ++** ++** SQLite does not do anything with client data other than invoke ++** destructors on the client data at the appropriate time. The intended ++** use for client data is to provide a mechanism for wrapper libraries ++** to store additional information about an SQLite database connection. ++** ++** There is no limit (other than available memory) on the number of different ++** client data pointers (with different names) that can be attached to a ++** single database connection. However, the implementation is optimized ++** for the case of having only one or two different client data names. ++** Applications and wrapper libraries are discouraged from using more than ++** one client data name each. ++** ++** There is no way to enumerate the client data pointers ++** associated with a database connection. The N parameter can be thought ++** of as a secret key such that only code that knows the secret key is able ++** to access the associated data. ++** ++** Security Warning: These interfaces should not be exposed in scripting ++** languages or in other circumstances where it might be possible for an ++** attacker to invoke them. Any agent that can invoke these interfaces ++** can probably also take control of the process. ++** ++** Database connection client data is only available for SQLite ++** version 3.44.0 ([dateof:3.44.0]) and later. ++** ++** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. ++*/ ++SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*); ++SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*)); + + /* + ** CAPI3REF: Constants Defining Special Destructor Behavior +@@ -5698,15 +6343,16 @@ typedef void (*sqlite3_destructor_type)(void*); + ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. + ** ^SQLite takes the text result from the application from + ** the 2nd parameter of the sqlite3_result_text* interfaces. +-** ^If the 3rd parameter to the sqlite3_result_text* interfaces +-** is negative, then SQLite takes result text from the 2nd parameter +-** through the first zero character. ++** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces ++** other than sqlite3_result_text64() is negative, then SQLite computes ++** the string length itself by searching the 2nd parameter for the first ++** zero character. + ** ^If the 3rd parameter to the sqlite3_result_text* interfaces + ** is non-negative, then as many bytes (not characters) of the text + ** pointed to by the 2nd parameter are taken as the application-defined + ** function result. If the 3rd parameter is non-negative, then it + ** must be the byte offset into the string where the NUL terminator would +-** appear if the string where NUL terminated. If any NUL characters occur ++** appear if the string were NUL terminated. If any NUL characters occur + ** in the string at a byte offset that is less than the value of the 3rd + ** parameter, then the resulting string will contain embedded NULs and the + ** result of expressions operating on strings with embedded NULs is undefined. +@@ -5764,7 +6410,7 @@ typedef void (*sqlite3_destructor_type)(void*); + ** string and preferably a string literal. The sqlite3_result_pointer() + ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. + ** +-** If these routines are called from within the different thread ++** If these routines are called from within a different thread + ** than the one containing the application-defined function that received + ** the [sqlite3_context] pointer, the results are undefined. + */ +@@ -5803,6 +6449,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); + ** higher order bits are discarded. + ** The number of subtype bytes preserved by SQLite might increase + ** in future releases of SQLite. ++** ++** Every [application-defined SQL function] that invokes this interface ++** should include the [SQLITE_RESULT_SUBTYPE] property in its ++** text encoding argument when the SQL function is ++** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] ++** property is omitted from the function that invokes sqlite3_result_subtype(), ++** then in some cases the sqlite3_result_subtype() might fail to set ++** the result subtype. ++** ++** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any ++** SQL function that invokes the sqlite3_result_subtype() interface ++** and that does not have the SQLITE_RESULT_SUBTYPE property will raise ++** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 ++** by default. + */ + SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); + +@@ -5974,6 +6634,13 @@ SQLITE_API void sqlite3_activate_cerod( + ** of the default VFS is not implemented correctly, or not implemented at + ** all, then the behavior of sqlite3_sleep() may deviate from the description + ** in the previous paragraphs. ++** ++** If a negative argument is passed to sqlite3_sleep() the results vary by ++** VFS and operating system. Some system treat a negative argument as an ++** instruction to sleep forever. Others understand it to mean do not sleep ++** at all. ^In SQLite version 3.42.0 and later, a negative ++** argument passed into sqlite3_sleep() is changed to zero before it is relayed ++** down into the xSleep method of the VFS. + */ + SQLITE_API int sqlite3_sleep(int); + +@@ -6144,6 +6811,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); + */ + SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + ++/* ++** CAPI3REF: Return The Schema Name For A Database Connection ++** METHOD: sqlite3 ++** ++** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name ++** for the N-th database on database connection D, or a NULL pointer if N is ++** out of range. An N value of 0 means the main database file. An N of 1 is ++** the "temp" schema. Larger values of N correspond to various ATTACH-ed ++** databases. ++** ++** Space to hold the string that is returned by sqlite3_db_name() is managed ++** by SQLite itself. The string might be deallocated by any operation that ++** changes the schema, including [ATTACH] or [DETACH] or calls to ++** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that ++** occur on a different thread. Applications that need to ++** remember the string long-term should make their own copy. Applications that ++** are accessing the same database connection simultaneously on multiple ++** threads should mutex-protect calls to this API and should make their own ++** private copy of the result prior to releasing the mutex. ++*/ ++SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N); ++ + /* + ** CAPI3REF: Return The Filename For A Database Connection + ** METHOD: sqlite3 +@@ -6174,7 +6863,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + **
  • [sqlite3_filename_wal()] + ** + */ +-SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); ++SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName); + + /* + ** CAPI3REF: Determine if a database is read-only +@@ -6186,6 +6875,57 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); + */ + SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); + ++/* ++** CAPI3REF: Determine the transaction state of a database ++** METHOD: sqlite3 ++** ++** ^The sqlite3_txn_state(D,S) interface returns the current ++** [transaction state] of schema S in database connection D. ^If S is NULL, ++** then the highest transaction state of any schema on database connection D ++** is returned. Transaction states are (in order of lowest to highest): ++**
      ++**
    1. SQLITE_TXN_NONE ++**
    2. SQLITE_TXN_READ ++**
    3. SQLITE_TXN_WRITE ++**
    ++** ^If the S argument to sqlite3_txn_state(D,S) is not the name of ++** a valid schema, then -1 is returned. ++*/ ++SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); ++ ++/* ++** CAPI3REF: Allowed return values from sqlite3_txn_state() ++** KEYWORDS: {transaction state} ++** ++** These constants define the current transaction state of a database file. ++** ^The [sqlite3_txn_state(D,S)] interface returns one of these ++** constants in order to describe the transaction state of schema S ++** in [database connection] D. ++** ++**
    ++** [[SQLITE_TXN_NONE]]
    SQLITE_TXN_NONE
    ++**
    The SQLITE_TXN_NONE state means that no transaction is currently ++** pending.
    ++** ++** [[SQLITE_TXN_READ]]
    SQLITE_TXN_READ
    ++**
    The SQLITE_TXN_READ state means that the database is currently ++** in a read transaction. Content has been read from the database file ++** but nothing in the database file has changed. The transaction state ++** will be advanced to SQLITE_TXN_WRITE if any changes occur and there are ++** no other conflicting concurrent write transactions. The transaction ++** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or ++** [COMMIT].
    ++** ++** [[SQLITE_TXN_WRITE]]
    SQLITE_TXN_WRITE
    ++**
    The SQLITE_TXN_WRITE state means that the database is currently ++** in a write transaction. Content has been written to the database file ++** but has not yet committed. The transaction state will change to ++** SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].
    ++*/ ++#define SQLITE_TXN_NONE 0 ++#define SQLITE_TXN_READ 1 ++#define SQLITE_TXN_WRITE 2 ++ + /* + ** CAPI3REF: Find the next prepared statement + ** METHOD: sqlite3 +@@ -6252,6 +6992,72 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); + SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); + SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + ++/* ++** CAPI3REF: Autovacuum Compaction Amount Callback ++** METHOD: sqlite3 ++** ++** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback ++** function C that is invoked prior to each autovacuum of the database ++** file. ^The callback is passed a copy of the generic data pointer (P), ++** the schema-name of the attached database that is being autovacuumed, ++** the size of the database file in pages, the number of free pages, ++** and the number of bytes per page, respectively. The callback should ++** return the number of free pages that should be removed by the ++** autovacuum. ^If the callback returns zero, then no autovacuum happens. ++** ^If the value returned is greater than or equal to the number of ++** free pages, then a complete autovacuum happens. ++** ++**

    ^If there are multiple ATTACH-ed database files that are being ++** modified as part of a transaction commit, then the autovacuum pages ++** callback is invoked separately for each file. ++** ++**

    The callback is not reentrant. The callback function should ++** not attempt to invoke any other SQLite interface. If it does, bad ++** things may happen, including segmentation faults and corrupt database ++** files. The callback function should be a simple function that ++** does some arithmetic on its input parameters and returns a result. ++** ++** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional ++** destructor for the P parameter. ^If X is not NULL, then X(P) is ++** invoked whenever the database connection closes or when the callback ++** is overwritten by another invocation of sqlite3_autovacuum_pages(). ++** ++**

    ^There is only one autovacuum pages callback per database connection. ++** ^Each call to the sqlite3_autovacuum_pages() interface overrides all ++** previous invocations for that database connection. ^If the callback ++** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, ++** then the autovacuum steps callback is canceled. The return value ++** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might ++** be some other error code if something goes wrong. The current ++** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other ++** return codes might be added in future releases. ++** ++**

    If no autovacuum pages callback is specified (the usual case) or ++** a NULL pointer is provided for the callback, ++** then the default behavior is to vacuum all free pages. So, in other ++** words, the default behavior is the same as if the callback function ++** were something like this: ++** ++**

    ++**     unsigned int demonstration_autovac_pages_callback(
    ++**       void *pClientData,
    ++**       const char *zSchema,
    ++**       unsigned int nDbPage,
    ++**       unsigned int nFreePage,
    ++**       unsigned int nBytePerPage
    ++**     ){
    ++**       return nFreePage;
    ++**     }
    ++** 
    ++*/ ++SQLITE_API int sqlite3_autovacuum_pages( ++ sqlite3 *db, ++ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), ++ void*, ++ void(*)(void*) ++); ++ ++ + /* + ** CAPI3REF: Data Change Notification Callbacks + ** METHOD: sqlite3 +@@ -6265,6 +7071,8 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + ** + ** ^The second argument is a pointer to the function to invoke when a + ** row is updated, inserted or deleted in a rowid table. ++** ^The update hook is disabled by invoking sqlite3_update_hook() ++** with a NULL pointer as the second parameter. + ** ^The first argument to the callback is a copy of the third argument + ** to sqlite3_update_hook(). + ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], +@@ -6286,6 +7094,12 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + ** The exceptions defined in this paragraph might change in a future + ** release of SQLite. + ** ++** Whether the update hook is invoked before or after the ++** corresponding change is currently unspecified and may differ ++** depending on the type of change. Do not rely on the order of the ++** hook call with regards to the final result of the operation which ++** triggers the hook. ++** + ** The update hook implementation must not do anything that will modify + ** the database connection that invoked the update hook. Any actions + ** to modify the database connection must be deferred until after the +@@ -6315,6 +7129,11 @@ SQLITE_API void *sqlite3_update_hook( + ** to the same database. Sharing is enabled if the argument is true + ** and disabled if the argument is false.)^ + ** ++** This interface is omitted if SQLite is compiled with ++** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE] ++** compile-time option is recommended because the ++** [use of shared cache mode is discouraged]. ++** + ** ^Cache sharing is enabled and disabled for an entire process. + ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). + ** In prior versions of SQLite, +@@ -6382,7 +7201,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); + ** CAPI3REF: Impose A Limit On Heap Size + ** + ** These interfaces impose limits on the amount of heap memory that will be +-** by all database connections within a single process. ++** used by all database connections within a single process. + ** + ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the + ** soft limit on the amount of heap memory that may be allocated by SQLite. +@@ -6413,7 +7232,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); + ** ^The soft heap limit may not be greater than the hard heap limit. + ** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N) + ** is invoked with a value of N that is greater than the hard heap limit, +-** the the soft heap limit is set to the value of the hard heap limit. ++** the soft heap limit is set to the value of the hard heap limit. + ** ^The soft heap limit is automatically enabled whenever the hard heap + ** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and + ** the soft heap limit is outside the range of 1..N, then the soft heap +@@ -6440,7 +7259,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); + ** )^ + ** + ** The circumstances under which SQLite will enforce the heap limits may +-** changes in future releases of SQLite. ++** change in future releases of SQLite. + */ + SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); + SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N); +@@ -6555,8 +7374,8 @@ SQLITE_API int sqlite3_table_column_metadata( + ** ^The entry point is zProc. + ** ^(zProc may be 0, in which case SQLite will try to come up with an + ** entry point name on its own. It first tries "sqlite3_extension_init". +-** If that does not work, it constructs a name "sqlite3_X_init" where the +-** X is consists of the lower-case equivalent of all ASCII alphabetic ++** If that does not work, it constructs a name "sqlite3_X_init" where ++** X consists of the lower-case equivalent of all ASCII alphabetic + ** characters in the filename from the last "/" to the first following + ** "." and omitting any initial "lib".)^ + ** ^The sqlite3_load_extension() interface returns +@@ -6627,7 +7446,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); + ** ^(Even though the function prototype shows that xEntryPoint() takes + ** no arguments and returns void, SQLite invokes xEntryPoint() with three + ** arguments and expects an integer result as if the signature of the +-** entry point where as follows: ++** entry point were as follows: + ** + **
    + **    int xEntryPoint(
    +@@ -6674,15 +7493,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
    + */
    + SQLITE_API void sqlite3_reset_auto_extension(void);
    + 
    +-/*
    +-** The interface to the virtual-table mechanism is currently considered
    +-** to be experimental.  The interface might change in incompatible ways.
    +-** If this is a problem for you, do not use the interface at this time.
    +-**
    +-** When the virtual-table mechanism stabilizes, we will declare the
    +-** interface fixed, support it indefinitely, and remove this comment.
    +-*/
    +-
    + /*
    + ** Structures used by the virtual table interface
    + */
    +@@ -6743,6 +7553,10 @@ struct sqlite3_module {
    +   /* The methods above are in versions 1 and 2 of the sqlite_module object.
    +   ** Those below are for version 3 and greater. */
    +   int (*xShadowName)(const char*);
    ++  /* The methods above are in versions 1 through 3 of the sqlite_module object.
    ++  ** Those below are for version 4 and greater. */
    ++  int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
    ++                    const char *zTabName, int mFlags, char **pzErr);
    + };
    + 
    + /*
    +@@ -6796,15 +7610,15 @@ struct sqlite3_module {
    + ** virtual table and might not be checked again by the byte code.)^ ^(The
    + ** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
    + ** is left in its default setting of false, the constraint will always be
    +-** checked separately in byte code.  If the omit flag is change to true, then
    ++** checked separately in byte code.  If the omit flag is changed to true, then
    + ** the constraint may or may not be checked in byte code.  In other words,
    + ** when the omit flag is true there is no guarantee that the constraint will
    + ** not be checked again using byte code.)^
    + **
    +-** ^The idxNum and idxPtr values are recorded and passed into the
    ++** ^The idxNum and idxStr values are recorded and passed into the
    + ** [xFilter] method.
    +-** ^[sqlite3_free()] is used to free idxPtr if and only if
    +-** needToFreeIdxPtr is true.
    ++** ^[sqlite3_free()] is used to free idxStr if and only if
    ++** needToFreeIdxStr is true.
    + **
    + ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
    + ** the correct order to satisfy the ORDER BY clause so that no separate
    +@@ -6820,9 +7634,11 @@ struct sqlite3_module {
    + ** will be returned by the strategy.
    + **
    + ** The xBestIndex method may optionally populate the idxFlags field with a
    +-** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
    +-** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
    +-** assumes that the strategy may visit at most one row.
    ++** mask of SQLITE_INDEX_SCAN_* flags. One such flag is
    ++** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN]
    ++** output to show the idxNum as hex instead of as decimal.  Another flag is
    ++** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will
    ++** return at most one row.
    + **
    + ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
    + ** SQLite also assumes that if a call to the xUpdate() method is made as
    +@@ -6886,31 +7702,65 @@ struct sqlite3_index_info {
    + ** [sqlite3_index_info].idxFlags field to some combination of
    + ** these bits.
    + */
    +-#define SQLITE_INDEX_SCAN_UNIQUE      1     /* Scan visits at most 1 row */
    ++#define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */
    ++#define SQLITE_INDEX_SCAN_HEX    0x00000002 /* Display idxNum as hex */
    ++                                            /* in EXPLAIN QUERY PLAN */
    + 
    + /*
    + ** CAPI3REF: Virtual Table Constraint Operator Codes
    + **
    + ** These macros define the allowed values for the
    + ** [sqlite3_index_info].aConstraint[].op field.  Each value represents
    +-** an operator that is part of a constraint term in the wHERE clause of
    ++** an operator that is part of a constraint term in the WHERE clause of
    + ** a query that uses a [virtual table].
    +-*/
    +-#define SQLITE_INDEX_CONSTRAINT_EQ         2
    +-#define SQLITE_INDEX_CONSTRAINT_GT         4
    +-#define SQLITE_INDEX_CONSTRAINT_LE         8
    +-#define SQLITE_INDEX_CONSTRAINT_LT        16
    +-#define SQLITE_INDEX_CONSTRAINT_GE        32
    +-#define SQLITE_INDEX_CONSTRAINT_MATCH     64
    +-#define SQLITE_INDEX_CONSTRAINT_LIKE      65
    +-#define SQLITE_INDEX_CONSTRAINT_GLOB      66
    +-#define SQLITE_INDEX_CONSTRAINT_REGEXP    67
    +-#define SQLITE_INDEX_CONSTRAINT_NE        68
    +-#define SQLITE_INDEX_CONSTRAINT_ISNOT     69
    +-#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
    +-#define SQLITE_INDEX_CONSTRAINT_ISNULL    71
    +-#define SQLITE_INDEX_CONSTRAINT_IS        72
    +-#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
    ++**
    ++** ^The left-hand operand of the operator is given by the corresponding
    ++** aConstraint[].iColumn field.  ^An iColumn of -1 indicates the left-hand
    ++** operand is the rowid.
    ++** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
    ++** operators have no left-hand operand, and so for those operators the
    ++** corresponding aConstraint[].iColumn is meaningless and should not be
    ++** used.
    ++**
    ++** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
    ++** value 255 are reserved to represent functions that are overloaded
    ++** by the [xFindFunction|xFindFunction method] of the virtual table
    ++** implementation.
    ++**
    ++** The right-hand operands for each constraint might be accessible using
    ++** the [sqlite3_vtab_rhs_value()] interface.  Usually the right-hand
    ++** operand is only available if it appears as a single constant literal
    ++** in the input SQL.  If the right-hand operand is another column or an
    ++** expression (even a constant expression) or a parameter, then the
    ++** sqlite3_vtab_rhs_value() probably will not be able to extract it.
    ++** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
    ++** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
    ++** and hence calls to sqlite3_vtab_rhs_value() for those operators will
    ++** always return SQLITE_NOTFOUND.
    ++**
    ++** The collating sequence to be used for comparison can be found using
    ++** the [sqlite3_vtab_collation()] interface.  For most real-world virtual
    ++** tables, the collating sequence of constraints does not matter (for example
    ++** because the constraints are numeric) and so the sqlite3_vtab_collation()
    ++** interface is not commonly needed.
    ++*/
    ++#define SQLITE_INDEX_CONSTRAINT_EQ          2
    ++#define SQLITE_INDEX_CONSTRAINT_GT          4
    ++#define SQLITE_INDEX_CONSTRAINT_LE          8
    ++#define SQLITE_INDEX_CONSTRAINT_LT         16
    ++#define SQLITE_INDEX_CONSTRAINT_GE         32
    ++#define SQLITE_INDEX_CONSTRAINT_MATCH      64
    ++#define SQLITE_INDEX_CONSTRAINT_LIKE       65
    ++#define SQLITE_INDEX_CONSTRAINT_GLOB       66
    ++#define SQLITE_INDEX_CONSTRAINT_REGEXP     67
    ++#define SQLITE_INDEX_CONSTRAINT_NE         68
    ++#define SQLITE_INDEX_CONSTRAINT_ISNOT      69
    ++#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL  70
    ++#define SQLITE_INDEX_CONSTRAINT_ISNULL     71
    ++#define SQLITE_INDEX_CONSTRAINT_IS         72
    ++#define SQLITE_INDEX_CONSTRAINT_LIMIT      73
    ++#define SQLITE_INDEX_CONSTRAINT_OFFSET     74
    ++#define SQLITE_INDEX_CONSTRAINT_FUNCTION  150
    + 
    + /*
    + ** CAPI3REF: Register A Virtual Table Implementation
    +@@ -6927,7 +7777,7 @@ struct sqlite3_index_info {
    + ** the implementation of the [virtual table module].   ^The fourth
    + ** parameter is an arbitrary client data pointer that is passed through
    + ** into the [xCreate] and [xConnect] methods of the virtual table module
    +-** when a new virtual table is be being created or reinitialized.
    ++** when a new virtual table is being created or reinitialized.
    + **
    + ** ^The sqlite3_create_module_v2() interface has a fifth parameter which
    + ** is a pointer to a destructor for the pClientData.  ^SQLite will
    +@@ -6939,7 +7789,7 @@ struct sqlite3_index_info {
    + ** destructor.
    + **
    + ** ^If the third parameter (the pointer to the sqlite3_module object) is
    +-** NULL then no new module is create and any existing modules with the
    ++** NULL then no new module is created and any existing modules with the
    + ** same name are dropped.
    + **
    + ** See also: [sqlite3_drop_modules()]
    +@@ -7051,16 +7901,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
    + */
    + SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
    + 
    +-/*
    +-** The interface to the virtual-table mechanism defined above (back up
    +-** to a comment remarkably similar to this one) is currently considered
    +-** to be experimental.  The interface might change in incompatible ways.
    +-** If this is a problem for you, do not use the interface at this time.
    +-**
    +-** When the virtual-table mechanism stabilizes, we will declare the
    +-** interface fixed, support it indefinitely, and remove this comment.
    +-*/
    +-
    + /*
    + ** CAPI3REF: A Handle To An Open BLOB
    + ** KEYWORDS: {BLOB handle} {BLOB handles}
    +@@ -7102,7 +7942,7 @@ typedef struct sqlite3_blob sqlite3_blob;
    + ** in *ppBlob. Otherwise an [error code] is returned and, unless the error
    + ** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
    + ** the API is not misused, it is always safe to call [sqlite3_blob_close()]
    +-** on *ppBlob after this function it returns.
    ++** on *ppBlob after this function returns.
    + **
    + ** This function fails with SQLITE_ERROR if any of the following are true:
    + ** 
      +@@ -7208,7 +8048,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); + ** code is returned and the transaction rolled back. + ** + ** Calling this function with an argument that is not a NULL pointer or an +-** open blob handle results in undefined behaviour. ^Calling this routine ++** open blob handle results in undefined behavior. ^Calling this routine + ** with a null pointer (such as would be returned by a failed call to + ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function + ** is passed a valid open blob handle, the values returned by the +@@ -7222,7 +8062,7 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); + ** + ** ^Returns the size in bytes of the BLOB accessible via the + ** successfully opened [BLOB handle] in its only argument. ^The +-** incremental blob I/O routines can only read or overwriting existing ++** incremental blob I/O routines can only read or overwrite existing + ** blob content; they cannot change the size of a blob. + ** + ** This routine only works on a [BLOB handle] which has been created +@@ -7372,7 +8212,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + ** ^The sqlite3_mutex_alloc() routine allocates a new + ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() + ** routine returns NULL if it is unable to allocate the requested +-** mutex. The argument to sqlite3_mutex_alloc() must one of these ++** mutex. The argument to sqlite3_mutex_alloc() must be one of these + ** integer constants: + ** + **
        +@@ -7435,18 +8275,20 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + ** + ** ^(Some systems (for example, Windows 95) do not support the operation + ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() +-** will always return SQLITE_BUSY. The SQLite core only ever uses +-** sqlite3_mutex_try() as an optimization so this is acceptable +-** behavior.)^ ++** will always return SQLITE_BUSY. In most cases the SQLite core only uses ++** sqlite3_mutex_try() as an optimization, so this is acceptable ++** behavior. The exceptions are unix builds that set the ++** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working ++** sqlite3_mutex_try() is required.)^ + ** + ** ^The sqlite3_mutex_leave() routine exits a mutex that was + ** previously entered by the same thread. The behavior + ** is undefined if the mutex is not currently entered by the + ** calling thread or is not currently allocated. + ** +-** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or +-** sqlite3_mutex_leave() is a NULL pointer, then all three routines +-** behave as no-ops. ++** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), ++** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, ++** then any of the four routines behaves as a no-op. + ** + ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. + */ +@@ -7603,7 +8445,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); + ** CAPI3REF: Retrieve the mutex for a database connection + ** METHOD: sqlite3 + ** +-** ^This interface returns a pointer the [sqlite3_mutex] object that ++** ^This interface returns a pointer to the [sqlite3_mutex] object that + ** serializes access to the [database connection] given in the argument + ** when the [threading mode] is Serialized. + ** ^If the [threading mode] is Single-thread or Multi-thread then this +@@ -7688,6 +8530,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); + #define SQLITE_TESTCTRL_PRNG_SAVE 5 + #define SQLITE_TESTCTRL_PRNG_RESTORE 6 + #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ ++#define SQLITE_TESTCTRL_FK_NO_ACTION 7 + #define SQLITE_TESTCTRL_BITVEC_TEST 8 + #define SQLITE_TESTCTRL_FAULT_INSTALL 9 + #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 +@@ -7695,8 +8538,10 @@ SQLITE_API int sqlite3_test_control(int op, ...); + #define SQLITE_TESTCTRL_ASSERT 12 + #define SQLITE_TESTCTRL_ALWAYS 13 + #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ ++#define SQLITE_TESTCTRL_JSON_SELFCHECK 14 + #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 + #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ ++#define SQLITE_TESTCTRL_GETOPT 16 + #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ + #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 + #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +@@ -7712,20 +8557,25 @@ SQLITE_API int sqlite3_test_control(int op, ...); + #define SQLITE_TESTCTRL_RESULT_INTREAL 27 + #define SQLITE_TESTCTRL_PRNG_SEED 28 + #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 +-#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ ++#define SQLITE_TESTCTRL_SEEK_COUNT 30 ++#define SQLITE_TESTCTRL_TRACEFLAGS 31 ++#define SQLITE_TESTCTRL_TUNE 32 ++#define SQLITE_TESTCTRL_LOGEST 33 ++#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ ++#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ + + /* + ** CAPI3REF: SQL Keyword Checking + ** + ** These routines provide access to the set of SQL language keywords +-** recognized by SQLite. Applications can uses these routines to determine ++** recognized by SQLite. Applications can use these routines to determine + ** whether or not a specific identifier needs to be escaped (for example, + ** by enclosing in double-quotes) so as not to confuse the parser. + ** + ** The sqlite3_keyword_count() interface returns the number of distinct + ** keywords understood by SQLite. + ** +-** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and ++** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and + ** makes *Z point to that keyword expressed as UTF8 and writes the number + ** of bytes in the keyword into *L. The string that *Z points to is not + ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns +@@ -7886,7 +8736,7 @@ SQLITE_API void sqlite3_str_reset(sqlite3_str*); + ** content of the dynamic string under construction in X. The value + ** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X + ** and might be freed or altered by any subsequent method on the same +-** [sqlite3_str] object. Applications must not used the pointer returned ++** [sqlite3_str] object. Applications must not use the pointer returned by + ** [sqlite3_str_value(X)] after any subsequent method call on the same + ** object. ^Applications may change the content of the string returned + ** by [sqlite3_str_value(X)] as long as they do not write into any bytes +@@ -7972,7 +8822,7 @@ SQLITE_API int sqlite3_status64( + ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] + ** buffer and where forced to overflow to [sqlite3_malloc()]. The + ** returned value includes allocations that overflowed because they +-** where too large (they were larger than the "sz" parameter to ++** were too large (they were larger than the "sz" parameter to + ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because + ** no space was left in the page cache.)^ + ** +@@ -8056,28 +8906,29 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
        SQLITE_DBSTATUS_LOOKASIDE_HIT
        + **
        This parameter returns the number of malloc attempts that were + ** satisfied using lookaside memory. Only the high-water value is meaningful; +-** the current value is always zero.)^ ++** the current value is always zero.
        )^ + ** + ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] + ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
        +-**
        This parameter returns the number malloc attempts that might have ++**
        This parameter returns the number of malloc attempts that might have + ** been satisfied using lookaside memory but failed due to the amount of + ** memory requested being larger than the lookaside slot size. + ** Only the high-water value is meaningful; +-** the current value is always zero.)^ ++** the current value is always zero.
        )^ + ** + ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] + ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
        +-**
        This parameter returns the number malloc attempts that might have ++**
        This parameter returns the number of malloc attempts that might have + ** been satisfied using lookaside memory but failed due to all lookaside + ** memory already being in use. + ** Only the high-water value is meaningful; +-** the current value is always zero.)^ ++** the current value is always zero.
        )^ + ** + ** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
        SQLITE_DBSTATUS_CACHE_USED
        + **
        This parameter returns the approximate number of bytes of heap + ** memory used by all pager caches associated with the database connection.)^ + ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ++**
        + ** + ** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]] + ** ^(
        SQLITE_DBSTATUS_CACHE_USED_SHARED
        +@@ -8086,10 +8937,10 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** memory used by that pager cache is divided evenly between the attached + ** connections.)^ In other words, if none of the pager caches associated + ** with the database connection are shared, this request returns the same +-** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are ++** value as DBSTATUS_CACHE_USED. Or, if one or more of the pager caches are + ** shared, the value returned by this call will be smaller than that returned + ** by DBSTATUS_CACHE_USED. ^The highwater mark associated with +-** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. ++** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. + ** + ** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
        SQLITE_DBSTATUS_SCHEMA_USED
        + **
        This parameter returns the approximate number of bytes of heap +@@ -8099,6 +8950,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** schema memory is shared with other database connections due to + ** [shared cache mode] being enabled. + ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. ++**
        + ** + ** [[SQLITE_DBSTATUS_STMT_USED]] ^(
        SQLITE_DBSTATUS_STMT_USED
        + **
        This parameter returns the approximate number of bytes of heap +@@ -8135,7 +8987,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r + ** been written to disk in the middle of a transaction due to the page + ** cache overflowing. Transactions are more efficient if they are written + ** to disk all at once. When pages spill mid-transaction, that introduces +-** additional overhead. This parameter can be used help identify ++** additional overhead. This parameter can be used to help identify + ** inefficiencies that can be resolved by increasing the cache size. + **
        + ** +@@ -8206,13 +9058,13 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + ** [[SQLITE_STMTSTATUS_SORT]]
        SQLITE_STMTSTATUS_SORT
        + **
        ^This is the number of sort operations that have occurred. + ** A non-zero value in this counter may indicate an opportunity to +-** improvement performance through careful use of indices.
        ++** improve performance through careful use of indices. + ** + ** [[SQLITE_STMTSTATUS_AUTOINDEX]]
        SQLITE_STMTSTATUS_AUTOINDEX
        + **
        ^This is the number of rows inserted into transient indices that + ** were created automatically in order to help joins run faster. + ** A non-zero value in this counter may indicate an opportunity to +-** improvement performance by adding permanent indices that do not ++** improve performance by adding permanent indices that do not + ** need to be reinitialized each time the statement is run.
        + ** + ** [[SQLITE_STMTSTATUS_VM_STEP]]
        SQLITE_STMTSTATUS_VM_STEP
        +@@ -8221,19 +9073,29 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + ** to 2147483647. The number of virtual machine operations can be + ** used as a proxy for the total work done by the prepared statement. + ** If the number of virtual machine operations exceeds 2147483647 +-** then the value returned by this statement status code is undefined. ++** then the value returned by this statement status code is undefined. + ** + ** [[SQLITE_STMTSTATUS_REPREPARE]]
        SQLITE_STMTSTATUS_REPREPARE
        + **
        ^This is the number of times that the prepare statement has been + ** automatically regenerated due to schema changes or changes to +-** [bound parameters] that might affect the query plan. ++** [bound parameters] that might affect the query plan.
        + ** + ** [[SQLITE_STMTSTATUS_RUN]]
        SQLITE_STMTSTATUS_RUN
        + **
        ^This is the number of times that the prepared statement has + ** been run. A single "run" for the purposes of this counter is one + ** or more calls to [sqlite3_step()] followed by a call to [sqlite3_reset()]. + ** The counter is incremented on the first [sqlite3_step()] call of each +-** cycle. ++** cycle.
        ++** ++** [[SQLITE_STMTSTATUS_FILTER_MISS]] ++** [[SQLITE_STMTSTATUS_FILTER HIT]] ++**
        SQLITE_STMTSTATUS_FILTER_HIT
        ++** SQLITE_STMTSTATUS_FILTER_MISS
        ++**
        ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join ++** step was bypassed because a Bloom filter returned not-found. The ++** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of ++** times that the Bloom filter returned a find, and thus the join step ++** had to be processed as normal.
        + ** + ** [[SQLITE_STMTSTATUS_MEMUSED]]
        SQLITE_STMTSTATUS_MEMUSED
        + **
        ^This is the approximate number of bytes of heap memory +@@ -8249,6 +9111,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + #define SQLITE_STMTSTATUS_VM_STEP 4 + #define SQLITE_STMTSTATUS_REPREPARE 5 + #define SQLITE_STMTSTATUS_RUN 6 ++#define SQLITE_STMTSTATUS_FILTER_MISS 7 ++#define SQLITE_STMTSTATUS_FILTER_HIT 8 + #define SQLITE_STMTSTATUS_MEMUSED 99 + + /* +@@ -8336,9 +9200,9 @@ struct sqlite3_pcache_page { + ** SQLite will typically create one cache instance for each open database file, + ** though this is not guaranteed. ^The + ** first parameter, szPage, is the size in bytes of the pages that must +-** be allocated by the cache. ^szPage will always a power of two. ^The ++** be allocated by the cache. ^szPage will always be a power of two. ^The + ** second parameter szExtra is a number of bytes of extra storage +-** associated with each page cache entry. ^The szExtra parameter will ++** associated with each page cache entry. ^The szExtra parameter will be + ** a number less than 250. SQLite will use the + ** extra szExtra bytes on each page to store metadata about the underlying + ** database page on disk. The value passed into szExtra depends +@@ -8346,17 +9210,17 @@ struct sqlite3_pcache_page { + ** ^The third argument to xCreate(), bPurgeable, is true if the cache being + ** created will be used to cache database pages of a file stored on disk, or + ** false if it is used for an in-memory database. The cache implementation +-** does not have to do anything special based with the value of bPurgeable; ++** does not have to do anything special based upon the value of bPurgeable; + ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will + ** never invoke xUnpin() except to deliberately delete a page. + ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to + ** false will always have the "discard" flag set to true. +-** ^Hence, a cache created with bPurgeable false will ++** ^Hence, a cache created with bPurgeable set to false will + ** never contain any unpinned pages. + ** + ** [[the xCachesize() page cache method]] + ** ^(The xCachesize() method may be called at any time by SQLite to set the +-** suggested maximum cache-size (number of pages stored by) the cache ++** suggested maximum cache-size (number of pages stored) for the cache + ** instance passed as the first argument. This is the value configured using + ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable + ** parameter, the implementation is not required to do anything with this +@@ -8383,12 +9247,12 @@ struct sqlite3_pcache_page { + ** implementation must return a pointer to the page buffer with its content + ** intact. If the requested page is not already in the cache, then the + ** cache implementation should use the value of the createFlag +-** parameter to help it determined what action to take: ++** parameter to help it determine what action to take: + ** + ** + ** -+**
        SQLITE_DBCONFIG_DQS_DML
        - **
        The SQLITE_DBCONFIG_DQS_DML option activates or deactivates - ** the legacy [double-quoted string literal] misfeature for DML statements - ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The -@@ -3323,7 +2812,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_DQS_DDL]] --**
        SQLITE_DBCONFIG_DQS_DDL -+**
        SQLITE_DBCONFIG_DQS_DDL
        - **
        The SQLITE_DBCONFIG_DQS option activates or deactivates - ** the legacy [double-quoted string literal] misfeature for DDL statements, - ** such as CREATE TABLE and CREATE INDEX. The -@@ -3332,7 +2821,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] --**
        SQLITE_DBCONFIG_TRUSTED_SCHEMA -+**
        SQLITE_DBCONFIG_TRUSTED_SCHEMA
        - **
        The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to - ** assume that database schemas are untainted by malicious content. - ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite -@@ -3352,7 +2841,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] --**
        SQLITE_DBCONFIG_LEGACY_FILE_FORMAT -+**
        SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
        - **
        The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates - ** the legacy file format flag. When activated, this flag causes all newly - ** created database file to have a schema format version number (the 4-byte -@@ -3361,7 +2850,7 @@ struct sqlite3_mem_methods { - ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, - ** newly created databases are generally not understandable by SQLite versions - ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there --** is now scarcely any need to generated database files that are compatible -+** is now scarcely any need to generate database files that are compatible - ** all the way back to version 3.0.0, and so this setting is of little - ** practical use, but is provided so that SQLite can continue to claim the - ** ability to generate new database files that are compatible with version -@@ -3370,9 +2859,110 @@ struct sqlite3_mem_methods { - ** the [VACUUM] command will fail with an obscure error when attempting to - ** process a table with generated columns and a descending index. This is - ** not considered a bug since SQLite versions 3.3.0 and earlier do not support --** either generated columns or decending indexes. -+** either generated columns or descending indexes. -+**
        -+** -+** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] -+**
        SQLITE_DBCONFIG_STMT_SCANSTATUS
        -+**
        The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in -+** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears -+** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() -+** statistics. For statistics to be collected, the flag must be set on -+** the database handle both when the SQL statement is prepared and when it -+** is stepped. The flag is set (collection of statistics is enabled) -+** by default.

        This option takes two arguments: an integer and a pointer to -+** an integer.. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the statement scanstatus option. If the second argument -+** is not NULL, then the value of the statement scanstatus setting after -+** processing the first argument is written into the integer that the second -+** argument points to. -+**

        -+** -+** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] -+**
        SQLITE_DBCONFIG_REVERSE_SCANORDER
        -+**
        The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order -+** in which tables and indexes are scanned so that the scans start at the end -+** and work toward the beginning rather than starting at the beginning and -+** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the -+** same as setting [PRAGMA reverse_unordered_selects].

        This option takes -+** two arguments which are an integer and a pointer to an integer. The first -+** argument is 1, 0, or -1 to enable, disable, or leave unchanged the -+** reverse scan order flag, respectively. If the second argument is not NULL, -+** then 0 or 1 is written into the integer that the second argument points to -+** depending on if the reverse scan order flag is set after processing the -+** first argument. -+**

        -+** -+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] -+**
        SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
        -+**
        The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables -+** the ability of the [ATTACH DATABASE] SQL command to create a new database -+** file if the database filed named in the ATTACH command does not already -+** exist. This ability of ATTACH to create a new database is enabled by -+** default. Applications can disable or reenable the ability for ATTACH to -+** create new database files using this DBCONFIG option.

        -+** This option takes two arguments which are an integer and a pointer -+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the attach-create flag, respectively. If the second -+** argument is not NULL, then 0 or 1 is written into the integer that the -+** second argument points to depending on if the attach-create flag is set -+** after processing the first argument. -+**

        -+** -+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] -+**
        SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
        -+**
        The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the -+** ability of the [ATTACH DATABASE] SQL command to open a database for writing. -+** This capability is enabled by default. Applications can disable or -+** reenable this capability using the current DBCONFIG option. If the -+** the this capability is disabled, the [ATTACH] command will still work, -+** but the database will be opened read-only. If this option is disabled, -+** then the ability to create a new database using [ATTACH] is also disabled, -+** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] -+** option.

        -+** This option takes two arguments which are an integer and a pointer -+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the ability to ATTACH another database for writing, -+** respectively. If the second argument is not NULL, then 0 or 1 is written -+** into the integer to which the second argument points, depending on whether -+** the ability to ATTACH a read/write database is enabled or disabled -+** after processing the first argument. -+**

        -+** -+** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] -+**
        SQLITE_DBCONFIG_ENABLE_COMMENTS
        -+**
        The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the -+** ability to include comments in SQL text. Comments are enabled by default. -+** An application can disable or reenable comments in SQL text using this -+** DBCONFIG option.

        -+** This option takes two arguments which are an integer and a pointer -+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the ability to use comments in SQL text, -+** respectively. If the second argument is not NULL, then 0 or 1 is written -+** into the integer that the second argument points to depending on if -+** comments are allowed in SQL text after processing the first argument. - **

        -+** - ** -+** -+** [[DBCONFIG arguments]]

        Arguments To SQLITE_DBCONFIG Options

        -+** -+**

        Most of the SQLITE_DBCONFIG options take two arguments, so that the -+** overall call to [sqlite3_db_config()] has a total of four parameters. -+** The first argument (the third parameter to sqlite3_db_config()) is a integer. -+** The second argument is a pointer to an integer. If the first argument is 1, -+** then the option becomes enabled. If the first integer argument is 0, then the -+** option is disabled. If the first argument is -1, then the option setting -+** is unchanged. The second argument, the pointer to an integer, may be NULL. -+** If the second argument is not NULL, then a value of 0 or 1 is written into -+** the integer to which the second argument points, depending on whether the -+** setting is disabled or enabled after applying any changes specified by -+** the first argument. -+** -+**

        While most SQLITE_DBCONFIG options use the argument format -+** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] -+** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the -+** documentation of those exceptional options for details. - */ - #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ - #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ -@@ -3392,7 +2982,12 @@ struct sqlite3_mem_methods { - #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ - #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ - #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ --#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */ -+#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ -+#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ -+#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ -+#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ -+#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ -+#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ - - /* - ** CAPI3REF: Enable Or Disable Extended Result Codes -@@ -3480,11 +3075,18 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); - ** CAPI3REF: Count The Number Of Rows Modified - ** METHOD: sqlite3 - ** --** ^This function returns the number of rows modified, inserted or -+** ^These functions return the number of rows modified, inserted or - ** deleted by the most recently completed INSERT, UPDATE or DELETE - ** statement on the database connection specified by the only parameter. --** ^Executing any other type of SQL statement does not modify the value --** returned by this function. -+** The two functions are identical except for the type of the return value -+** and that if the number of rows modified by the most recent INSERT, UPDATE, -+** or DELETE is greater than the maximum value supported by type "int", then -+** the return value of sqlite3_changes() is undefined. ^Executing any other -+** type of SQL statement does not modify the value returned by these functions. -+** For the purposes of this interface, a CREATE TABLE AS SELECT statement -+** does not count as an INSERT, UPDATE or DELETE statement and hence the rows -+** added to the new table by the CREATE TABLE AS SELECT statement are not -+** counted. - ** - ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are - ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], -@@ -3533,16 +3135,21 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); - ** - */ - SQLITE_API int sqlite3_changes(sqlite3*); -+SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*); - - /* - ** CAPI3REF: Total Number Of Rows Modified - ** METHOD: sqlite3 - ** --** ^This function returns the total number of rows inserted, modified or -+** ^These functions return the total number of rows inserted, modified or - ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed - ** since the database connection was opened, including those executed as --** part of trigger programs. ^Executing any other type of SQL statement --** does not affect the value returned by sqlite3_total_changes(). -+** part of trigger programs. The two functions are identical except for the -+** type of the return value and that if the number of rows modified by the -+** connection exceeds the maximum value supported by type "int", then -+** the return value of sqlite3_total_changes() is undefined. ^Executing -+** any other type of SQL statement does not affect the value returned by -+** sqlite3_total_changes(). - ** - ** ^Changes made as part of [foreign key actions] are included in the - ** count, but those made as part of REPLACE constraint resolution are -@@ -3570,6 +3177,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); - ** - */ - SQLITE_API int sqlite3_total_changes(sqlite3*); -+SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); - - /* - ** CAPI3REF: Interrupt A Long-Running Query -@@ -3605,8 +3213,13 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); - ** ^A call to sqlite3_interrupt(D) that occurs when there are no running - ** SQL statements is a no-op and has no effect on SQL statements - ** that are started after the sqlite3_interrupt() call returns. -+** -+** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether -+** or not an interrupt is currently in effect for [database connection] D. -+** It returns 1 if an interrupt is currently in effect, or 0 otherwise. - */ - SQLITE_API void sqlite3_interrupt(sqlite3*); -+SQLITE_API int sqlite3_is_interrupted(sqlite3*); - - /* - ** CAPI3REF: Determine If An SQL Statement Is Complete -@@ -3728,6 +3341,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*); - */ - SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); - -+/* -+** CAPI3REF: Set the Setlk Timeout -+** METHOD: sqlite3 -+** -+** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If -+** the VFS supports blocking locks, it sets the timeout in ms used by -+** eligible locks taken on wal mode databases by the specified database -+** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does -+** not support blocking locks, this function is a no-op. -+** -+** Passing 0 to this function disables blocking locks altogether. Passing -+** -1 to this function requests that the VFS blocks for a long time - -+** indefinitely if possible. The results of passing any other negative value -+** are undefined. -+** -+** Internally, each SQLite database handle store two timeout values - the -+** busy-timeout (used for rollback mode databases, or if the VFS does not -+** support blocking locks) and the setlk-timeout (used for blocking locks -+** on wal-mode databases). The sqlite3_busy_timeout() method sets both -+** values, this function sets only the setlk-timeout value. Therefore, -+** to configure separate busy-timeout and setlk-timeout values for a single -+** database handle, call sqlite3_busy_timeout() followed by this function. -+** -+** Whenever the number of connections to a wal mode database falls from -+** 1 to 0, the last connection takes an exclusive lock on the database, -+** then checkpoints and deletes the wal file. While it is doing this, any -+** new connection that tries to read from the database fails with an -+** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is -+** passed to this API, the new connection blocks until the exclusive lock -+** has been released. -+*/ -+SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); -+ -+/* -+** CAPI3REF: Flags for sqlite3_setlk_timeout() -+*/ -+#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 -+ - /* - ** CAPI3REF: Convenience Routines For Running Queries - ** METHOD: sqlite3 -@@ -4153,8 +3804,8 @@ SQLITE_API int sqlite3_set_authorizer( - #define SQLITE_RECURSIVE 33 /* NULL NULL */ - - /* --** CAPI3REF: Tracing And Profiling Functions --** METHOD: sqlite3 -+** CAPI3REF: Deprecated Tracing And Profiling Functions -+** DEPRECATED - ** - ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface - ** instead of the routines described here. -@@ -4224,8 +3875,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, - **

        ^An SQLITE_TRACE_PROFILE callback provides approximately the same - ** information as is provided by the [sqlite3_profile()] callback. - ** ^The P argument is a pointer to the [prepared statement] and the --** X argument points to a 64-bit integer which is the estimated of --** the number of nanosecond that the prepared statement took to run. -+** X argument points to a 64-bit integer which is approximately -+** the number of nanoseconds that the prepared statement took to run. - ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. - ** - ** [[SQLITE_TRACE_ROW]]
        SQLITE_TRACE_ROW
        -@@ -4257,8 +3908,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, - ** M argument should be the bitwise OR-ed combination of - ** zero or more [SQLITE_TRACE] constants. - ** --** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides --** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). -+** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P) -+** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or -+** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each -+** database connection may have at most one trace callback. - ** - ** ^The X callback is invoked whenever any of the events identified by - ** mask M occur. ^The integer return value from the callback is currently -@@ -4288,7 +3941,7 @@ SQLITE_API int sqlite3_trace_v2( - ** - ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback - ** function X to be invoked periodically during long running calls to --** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for -+** [sqlite3_step()] and [sqlite3_prepare()] and similar for - ** database connection D. An example use for this - ** interface is to keep a GUI updated during a large query. - ** -@@ -4313,6 +3966,13 @@ SQLITE_API int sqlite3_trace_v2( - ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their - ** database connections for the meaning of "modify" in this paragraph. - ** -+** The progress handler callback would originally only be invoked from the -+** bytecode engine. It still might be invoked during [sqlite3_prepare()] -+** and similar because those routines might force a reparse of the schema -+** which involves running the bytecode engine. However, beginning with -+** SQLite version 3.41.0, the progress handler callback might also be -+** invoked directly from [sqlite3_prepare()] while analyzing and generating -+** code for complex queries. - */ - SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - -@@ -4349,13 +4009,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - ** - **
        - ** ^(
        [SQLITE_OPEN_READONLY]
        --**
        The database is opened in read-only mode. If the database does not --** already exist, an error is returned.
        )^ -+**
        The database is opened in read-only mode. If the database does -+** not already exist, an error is returned.
        )^ - ** - ** ^(
        [SQLITE_OPEN_READWRITE]
        --**
        The database is opened for reading and writing if possible, or reading --** only if the file is write protected by the operating system. In either --** case the database must already exist, otherwise an error is returned.
        )^ -+**
        The database is opened for reading and writing if possible, or -+** reading only if the file is write protected by the operating -+** system. In either case the database must already exist, otherwise -+** an error is returned. For historical reasons, if opening in -+** read-write mode fails due to OS-level permissions, an attempt is -+** made to open it in read-only mode. [sqlite3_db_readonly()] can be -+** used to determine whether the database is actually -+** read-write.
        )^ - ** - ** ^(
        [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
        - **
        The database is opened for reading and writing, and is created if -@@ -4393,20 +4058,39 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - **
        The database is opened [shared cache] enabled, overriding - ** the default shared cache setting provided by - ** [sqlite3_enable_shared_cache()].)^ -+** The [use of shared cache mode is discouraged] and hence shared cache -+** capabilities may be omitted from many builds of SQLite. In such cases, -+** this option is a no-op. - ** - ** ^(
        [SQLITE_OPEN_PRIVATECACHE]
        - **
        The database is opened [shared cache] disabled, overriding - ** the default shared cache setting provided by - ** [sqlite3_enable_shared_cache()].)^ - ** -+** [[OPEN_EXRESCODE]] ^(
        [SQLITE_OPEN_EXRESCODE]
        -+**
        The database connection comes up in "extended result code mode". -+** In other words, the database behaves as if -+** [sqlite3_extended_result_codes(db,1)] were called on the database -+** connection as soon as the connection is created. In addition to setting -+** the extended result code mode, this flag also causes [sqlite3_open_v2()] -+** to return an extended result code.
        -+** - ** [[OPEN_NOFOLLOW]] ^(
        [SQLITE_OPEN_NOFOLLOW]
        --**
        The database filename is not allowed to be a symbolic link
        -+**
        The database filename is not allowed to contain a symbolic link
        - **
        )^ - ** - ** If the 3rd parameter to sqlite3_open_v2() is not one of the - ** required combinations shown above optionally combined with other - ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] --** then the behavior is undefined. -+** then the behavior is undefined. Historic versions of SQLite -+** have silently ignored surplus bits in the flags parameter to -+** sqlite3_open_v2(), however that behavior might not be carried through -+** into future versions of SQLite and so applications should not rely -+** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op -+** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause -+** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE -+** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not -+** by sqlite3_open_v2(). - ** - ** ^The fourth parameter to sqlite3_open_v2() is the name of the - ** [sqlite3_vfs] object that defines the operating system interface that -@@ -4546,6 +4230,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - ** that uses dot-files in place of posix advisory locking. - **
        createFlag Behavior when page is not already in cache + **
        0 Do not allocate a new page. Return NULL. +-**
        1 Allocate a new page if it easy and convenient to do so. ++**
        1 Allocate a new page if it is easy and convenient to do so. + ** Otherwise return NULL. + **
        2 Make every effort to allocate a new page. Only return + ** NULL if allocating a new page is effectively impossible. +@@ -8405,7 +9269,7 @@ struct sqlite3_pcache_page { + ** as its second argument. If the third parameter, discard, is non-zero, + ** then the page must be evicted from the cache. + ** ^If the discard parameter is +-** zero, then the page may be discarded or retained at the discretion of ++** zero, then the page may be discarded or retained at the discretion of the + ** page cache implementation. ^The page cache implementation + ** may choose to evict unpinned pages at any time. + ** +@@ -8423,7 +9287,7 @@ struct sqlite3_pcache_page { + ** When SQLite calls the xTruncate() method, the cache must discard all + ** existing cache entries with page numbers (keys) greater than or equal + ** to the value of the iLimit parameter passed to xTruncate(). If any +-** of these pages are pinned, they are implicitly unpinned, meaning that ++** of these pages are pinned, they become implicitly unpinned, meaning that + ** they can be safely discarded. + ** + ** [[the xDestroy() page cache method]] +@@ -8603,7 +9467,7 @@ typedef struct sqlite3_backup sqlite3_backup; + ** external process or via a database connection other than the one being + ** used by the backup operation, then the backup will be automatically + ** restarted by the next call to sqlite3_backup_step(). ^If the source +-** database is modified by the using the same database connection as is used ++** database is modified by using the same database connection as is used + ** by the backup operation, then the backup database is automatically + ** updated at the same time. + ** +@@ -8620,7 +9484,7 @@ typedef struct sqlite3_backup sqlite3_backup; + ** and may not be used following a call to sqlite3_backup_finish(). + ** + ** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no +-** sqlite3_backup_step() errors occurred, regardless or whether or not ++** sqlite3_backup_step() errors occurred, regardless of whether or not + ** sqlite3_backup_step() completed. + ** ^If an out-of-memory condition or IO error occurred during any prior + ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then +@@ -8660,7 +9524,7 @@ typedef struct sqlite3_backup sqlite3_backup; + ** if the application incorrectly accesses the destination [database connection] + ** and so no error code is reported, but the operations may malfunction + ** nevertheless. Use of the destination database connection while a +-** backup is in progress might also also cause a mutex deadlock. ++** backup is in progress might also cause a mutex deadlock. + ** + ** If running in [shared cache mode], the application must + ** guarantee that the shared cache used by the destination database +@@ -8675,6 +9539,16 @@ typedef struct sqlite3_backup sqlite3_backup; + ** APIs are not strictly speaking threadsafe. If they are invoked at the + ** same time as another thread is invoking sqlite3_backup_step() it is + ** possible that they return invalid values. ++** ++** Alternatives To Using The Backup API ++** ++** Other techniques for safely creating a consistent backup of an SQLite ++** database include: ++** ++**
          ++**
        • The [VACUUM INTO] command. ++**
        • The [sqlite3_rsync] utility program. ++**
        + */ + SQLITE_API sqlite3_backup *sqlite3_backup_init( + sqlite3 *pDest, /* Destination database handle */ +@@ -8712,7 +9586,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); + ** application receives an SQLITE_LOCKED error, it may call the + ** sqlite3_unlock_notify() method with the blocked connection handle as + ** the first argument to register for a callback that will be invoked +-** when the blocking connections current transaction is concluded. ^The ++** when the blocking connection's current transaction is concluded. ^The + ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] + ** call that concludes the blocking connection's transaction. + ** +@@ -8732,7 +9606,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); + ** blocked connection already has a registered unlock-notify callback, + ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is + ** called with a NULL pointer as its second argument, then any existing +-** unlock-notify callback is canceled. ^The blocked connections ++** unlock-notify callback is canceled. ^The blocked connection's + ** unlock-notify callback may also be canceled by closing the blocked + ** connection using [sqlite3_close()]. + ** +@@ -8912,8 +9786,9 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); + ** + ** A single database handle may have at most a single write-ahead log callback + ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any +-** previously registered write-ahead log callback. ^Note that the +-** [sqlite3_wal_autocheckpoint()] interface and the ++** previously registered write-ahead log callback. ^The return value is ++** a copy of the third parameter from the previous call, if any, or 0. ++** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the + ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will + ** overwrite any prior [sqlite3_wal_hook()] settings. + */ +@@ -9087,7 +9962,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( + */ + #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ + #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ +-#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ ++#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */ + #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ + + /* +@@ -9129,7 +10004,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + ** support constraints. In this configuration (which is the default) if + ** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire + ** statement is rolled back as if [ON CONFLICT | OR ABORT] had been +-** specified as part of the users SQL statement, regardless of the actual ++** specified as part of the user's SQL statement, regardless of the actual + ** ON CONFLICT mode specified. + ** + ** If X is non-zero, then the virtual table implementation guarantees +@@ -9155,7 +10030,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + ** [[SQLITE_VTAB_DIRECTONLY]]
        SQLITE_VTAB_DIRECTONLY
        + **
        Calls of the form + ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the +-** the [xConnect] or [xCreate] methods of a [virtual table] implmentation ++** the [xConnect] or [xCreate] methods of a [virtual table] implementation + ** prohibits that virtual table from being used from within triggers and + ** views. + **
        +@@ -9163,18 +10038,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + ** [[SQLITE_VTAB_INNOCUOUS]]
        SQLITE_VTAB_INNOCUOUS
        + **
        Calls of the form + ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the +-** the [xConnect] or [xCreate] methods of a [virtual table] implmentation ++** [xConnect] or [xCreate] methods of a [virtual table] implementation + ** identify that virtual table as being safe to use from within triggers + ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the + ** virtual table can do no serious harm even if it is controlled by a + ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS + ** flag unless absolutely necessary. + **
        ++** ++** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]
        SQLITE_VTAB_USES_ALL_SCHEMAS
        ++**
        Calls of the form ++** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the ++** the [xConnect] or [xCreate] methods of a [virtual table] implementation ++** instruct the query planner to begin at least a read transaction on ++** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the ++** virtual table is used. ++**
        + ** + */ + #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 + #define SQLITE_VTAB_INNOCUOUS 2 + #define SQLITE_VTAB_DIRECTONLY 3 ++#define SQLITE_VTAB_USES_ALL_SCHEMAS 4 + + /* + ** CAPI3REF: Determine The Virtual Table Conflict Policy +@@ -9192,10 +10077,11 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + ** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE + ** + ** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn] +-** method of a [virtual table], then it returns true if and only if the ++** method of a [virtual table], then it might return true if the + ** column is being fetched as part of an UPDATE operation during which the +-** column value will not change. Applications might use this to substitute +-** a return value that is less expensive to compute and that the corresponding ++** column value will not change. The virtual table implementation can use ++** this hint as permission to substitute a return value that is less ++** expensive to compute and that the corresponding + ** [xUpdate] method understands as a "no-change" value. + ** + ** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that +@@ -9204,31 +10090,314 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + ** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces]. + ** In that case, [sqlite3_value_nochange(X)] will return true for the + ** same column in the [xUpdate] method. ++** ++** The sqlite3_vtab_nochange() routine is an optimization. Virtual table ++** implementations should continue to give a correct answer even if the ++** sqlite3_vtab_nochange() interface were to always return false. In the ++** current implementation, the sqlite3_vtab_nochange() interface does always ++** returns false for the enhanced [UPDATE FROM] statement. + */ + SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*); + + /* + ** CAPI3REF: Determine The Collation For a Virtual Table Constraint ++** METHOD: sqlite3_index_info + ** + ** This function may only be called from within a call to the [xBestIndex] +-** method of a [virtual table]. ++** method of a [virtual table]. This function returns a pointer to a string ++** that is the name of the appropriate collation sequence to use for text ++** comparisons on the constraint identified by its arguments. + ** +-** The first argument must be the sqlite3_index_info object that is the +-** first parameter to the xBestIndex() method. The second argument must be +-** an index into the aConstraint[] array belonging to the sqlite3_index_info +-** structure passed to xBestIndex. This function returns a pointer to a buffer +-** containing the name of the collation sequence for the corresponding +-** constraint. ++** The first argument must be the pointer to the [sqlite3_index_info] object ++** that is the first parameter to the xBestIndex() method. The second argument ++** must be an index into the aConstraint[] array belonging to the ++** sqlite3_index_info structure passed to xBestIndex. ++** ++** Important: ++** The first parameter must be the same pointer that is passed into the ++** xBestMethod() method. The first parameter may not be a pointer to a ++** different [sqlite3_index_info] object, even an exact copy. ++** ++** The return value is computed as follows: ++** ++**
          ++**
        1. If the constraint comes from a WHERE clause expression that contains ++** a [COLLATE operator], then the name of the collation specified by ++** that COLLATE operator is returned. ++**

        2. If there is no COLLATE operator, but the column that is the subject ++** of the constraint specifies an alternative collating sequence via ++** a [COLLATE clause] on the column definition within the CREATE TABLE ++** statement that was passed into [sqlite3_declare_vtab()], then the ++** name of that alternative collating sequence is returned. ++**

        3. Otherwise, "BINARY" is returned. ++**

        + */ +-SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ++SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ++ ++/* ++** CAPI3REF: Determine if a virtual table query is DISTINCT ++** METHOD: sqlite3_index_info ++** ++** This API may only be used from within an [xBestIndex|xBestIndex method] ++** of a [virtual table] implementation. The result of calling this ++** interface from outside of xBestIndex() is undefined and probably harmful. ++** ++** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and ++** 3. The integer returned by sqlite3_vtab_distinct() ++** gives the virtual table additional information about how the query ++** planner wants the output to be ordered. As long as the virtual table ++** can meet the ordering requirements of the query planner, it may set ++** the "orderByConsumed" flag. ++** ++**
        1. ++** ^If the sqlite3_vtab_distinct() interface returns 0, that means ++** that the query planner needs the virtual table to return all rows in the ++** sort order defined by the "nOrderBy" and "aOrderBy" fields of the ++** [sqlite3_index_info] object. This is the default expectation. If the ++** virtual table outputs all rows in sorted order, then it is always safe for ++** the xBestIndex method to set the "orderByConsumed" flag, regardless of ++** the return value from sqlite3_vtab_distinct(). ++**

        2. ++** ^(If the sqlite3_vtab_distinct() interface returns 1, that means ++** that the query planner does not need the rows to be returned in sorted order ++** as long as all rows with the same values in all columns identified by the ++** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ++** is doing a GROUP BY. ++**

        3. ++** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ++** that the query planner does not need the rows returned in any particular ++** order, as long as rows with the same values in all columns identified ++** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows ++** contain the same values for all columns identified by "colUsed", all but ++** one such row may optionally be omitted from the result.)^ ++** The virtual table is not required to omit rows that are duplicates ++** over the "colUsed" columns, but if the virtual table can do that without ++** too much extra effort, it could potentially help the query to run faster. ++** This mode is used for a DISTINCT query. ++**

        4. ++** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the ++** virtual table must return rows in the order defined by "aOrderBy" as ++** if the sqlite3_vtab_distinct() interface had returned 0. However if ++** two or more rows in the result have the same values for all columns ++** identified by "colUsed", then all but one such row may optionally be ++** omitted.)^ Like when the return value is 2, the virtual table ++** is not required to omit rows that are duplicates over the "colUsed" ++** columns, but if the virtual table can do that without ++** too much extra effort, it could potentially help the query to run faster. ++** This mode is used for queries ++** that have both DISTINCT and ORDER BY clauses. ++**

        ++** ++**

        The following table summarizes the conditions under which the ++** virtual table is allowed to set the "orderByConsumed" flag based on ++** the value returned by sqlite3_vtab_distinct(). This table is a ++** restatement of the previous four paragraphs: ++** ++** ++** ++**
        sqlite3_vtab_distinct() return value ++** Rows are returned in aOrderBy order ++** Rows with the same value in all aOrderBy columns are adjacent ++** Duplicates over all colUsed columns may be omitted ++**
        0yesyesno ++**
        1noyesno ++**
        2noyesyes ++**
        3yesyesyes ++**
        ++** ++** ^For the purposes of comparing virtual table output values to see if the ++** values are the same value for sorting purposes, two NULL values are considered ++** to be the same. In other words, the comparison operator is "IS" ++** (or "IS NOT DISTINCT FROM") and not "==". ++** ++** If a virtual table implementation is unable to meet the requirements ++** specified above, then it must not set the "orderByConsumed" flag in the ++** [sqlite3_index_info] object or an incorrect answer may result. ++** ++** ^A virtual table implementation is always free to return rows in any order ++** it wants, as long as the "orderByConsumed" flag is not set. ^When the ++** "orderByConsumed" flag is unset, the query planner will add extra ++** [bytecode] to ensure that the final results returned by the SQL query are ++** ordered correctly. The use of the "orderByConsumed" flag and the ++** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful ++** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed" ++** flag might help queries against a virtual table to run faster. Being ++** overly aggressive and setting the "orderByConsumed" flag when it is not ++** valid to do so, on the other hand, might cause SQLite to return incorrect ++** results. ++*/ ++SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); ++ ++/* ++** CAPI3REF: Identify and handle IN constraints in xBestIndex ++** ++** This interface may only be used from within an ++** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. ++** The result of invoking this interface from any other context is ++** undefined and probably harmful. ++** ++** ^(A constraint on a virtual table of the form ++** "[IN operator|column IN (...)]" is ++** communicated to the xBestIndex method as a ++** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ++** this constraint, it must set the corresponding ++** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under ++** the usual mode of handling IN operators, SQLite generates [bytecode] ++** that invokes the [xFilter|xFilter() method] once for each value ++** on the right-hand side of the IN operator.)^ Thus the virtual table ++** only sees a single value from the right-hand side of the IN operator ++** at a time. ++** ++** In some cases, however, it would be advantageous for the virtual ++** table to see all values on the right-hand of the IN operator all at ++** once. The sqlite3_vtab_in() interfaces facilitates this in two ways: ++** ++**

          ++**
        1. ++** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero) ++** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint ++** is an [IN operator] that can be processed all at once. ^In other words, ++** sqlite3_vtab_in() with -1 in the third argument is a mechanism ++** by which the virtual table can ask SQLite if all-at-once processing ++** of the IN operator is even possible. ++** ++**

        2. ++** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates ++** to SQLite that the virtual table does or does not want to process ++** the IN operator all-at-once, respectively. ^Thus when the third ++** parameter (F) is non-negative, this interface is the mechanism by ++** which the virtual table tells SQLite how it wants to process the ++** IN operator. ++**

        ++** ++** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times ++** within the same xBestIndex method call. ^For any given P,N pair, ++** the return value from sqlite3_vtab_in(P,N,F) will always be the same ++** within the same xBestIndex call. ^If the interface returns true ++** (non-zero), that means that the constraint is an IN operator ++** that can be processed all-at-once. ^If the constraint is not an IN ++** operator or cannot be processed all-at-once, then the interface returns ++** false. ++** ++** ^(All-at-once processing of the IN operator is selected if both of the ++** following conditions are met: ++** ++**
          ++**
        1. The P->aConstraintUsage[N].argvIndex value is set to a positive ++** integer. This is how the virtual table tells SQLite that it wants to ++** use the N-th constraint. ++** ++**

        2. The last call to sqlite3_vtab_in(P,N,F) for which F was ++** non-negative had F>=1. ++**

        )^ ++** ++** ^If either or both of the conditions above are false, then SQLite uses ++** the traditional one-at-a-time processing strategy for the IN constraint. ++** ^If both conditions are true, then the argvIndex-th parameter to the ++** xFilter method will be an [sqlite3_value] that appears to be NULL, ++** but which can be passed to [sqlite3_vtab_in_first()] and ++** [sqlite3_vtab_in_next()] to find all values on the right-hand side ++** of the IN constraint. ++*/ ++SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); ++ ++/* ++** CAPI3REF: Find all elements on the right-hand side of an IN constraint. ++** ++** These interfaces are only useful from within the ++** [xFilter|xFilter() method] of a [virtual table] implementation. ++** The result of invoking these interfaces from any other context ++** is undefined and probably harmful. ++** ++** The X parameter in a call to sqlite3_vtab_in_first(X,P) or ++** sqlite3_vtab_in_next(X,P) should be one of the parameters to the ++** xFilter method which invokes these routines, and specifically ++** a parameter that was previously selected for all-at-once IN constraint ++** processing using the [sqlite3_vtab_in()] interface in the ++** [xBestIndex|xBestIndex method]. ^(If the X parameter is not ++** an xFilter argument that was selected for all-at-once IN constraint ++** processing, then these routines return [SQLITE_ERROR].)^ ++** ++** ^(Use these routines to access all values on the right-hand side ++** of the IN constraint using code like the following: ++** ++**
        ++**    for(rc=sqlite3_vtab_in_first(pList, &pVal);
        ++**        rc==SQLITE_OK && pVal;
        ++**        rc=sqlite3_vtab_in_next(pList, &pVal)
        ++**    ){
        ++**      // do something with pVal
        ++**    }
        ++**    if( rc!=SQLITE_OK ){
        ++**      // an error has occurred
        ++**    }
        ++** 
        )^ ++** ++** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P) ++** routines return SQLITE_OK and set *P to point to the first or next value ++** on the RHS of the IN constraint. ^If there are no more values on the ++** right hand side of the IN constraint, then *P is set to NULL and these ++** routines return [SQLITE_DONE]. ^The return value might be ++** some other value, such as SQLITE_NOMEM, in the event of a malfunction. ++** ++** The *ppOut values returned by these routines are only valid until the ++** next call to either of these routines or until the end of the xFilter ++** method from which these routines were called. If the virtual table ++** implementation needs to retain the *ppOut values for longer, it must make ++** copies. The *ppOut values are [protected sqlite3_value|protected]. ++*/ ++SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); ++SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); ++ ++/* ++** CAPI3REF: Constraint values in xBestIndex() ++** METHOD: sqlite3_index_info ++** ++** This API may only be used from within the [xBestIndex|xBestIndex method] ++** of a [virtual table] implementation. The result of calling this interface ++** from outside of an xBestIndex method are undefined and probably harmful. ++** ++** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within ++** the [xBestIndex] method of a [virtual table] implementation, with P being ++** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and ++** J being a 0-based index into P->aConstraint[], then this routine ++** attempts to set *V to the value of the right-hand operand of ++** that constraint if the right-hand operand is known. ^If the ++** right-hand operand is not known, then *V is set to a NULL pointer. ++** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if ++** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) ++** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th ++** constraint is not available. ^The sqlite3_vtab_rhs_value() interface ++** can return a result code other than SQLITE_OK or SQLITE_NOTFOUND if ++** something goes wrong. ++** ++** The sqlite3_vtab_rhs_value() interface is usually only successful if ++** the right-hand operand of a constraint is a literal value in the original ++** SQL statement. If the right-hand operand is an expression or a reference ++** to some other column or a [host parameter], then sqlite3_vtab_rhs_value() ++** will probably return [SQLITE_NOTFOUND]. ++** ++** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and ++** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such ++** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ ++** ++** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value ++** and remains valid for the duration of the xBestIndex method call. ++** ^When xBestIndex returns, the sqlite3_value object returned by ++** sqlite3_vtab_rhs_value() is automatically deallocated. ++** ++** The "_rhs_" in the name of this routine is an abbreviation for ++** "Right-Hand Side". ++*/ ++SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal); + + /* + ** CAPI3REF: Conflict resolution modes + ** KEYWORDS: {conflict resolution mode} + ** + ** These constants are returned by [sqlite3_vtab_on_conflict()] to +-** inform a [virtual table] implementation what the [ON CONFLICT] mode +-** is for the SQL statement being evaluated. ++** inform a [virtual table] implementation of the [ON CONFLICT] mode ++** for the SQL statement being evaluated. + ** + ** Note that the [SQLITE_IGNORE] constant is also used as a potential + ** return value from the [sqlite3_set_authorizer()] callback and that +@@ -9252,6 +10421,10 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + ** managed by the prepared statement S and will be automatically freed when + ** S is finalized. + ** ++** Not all values are available for all query elements. When a value is ++** not available, the output variable is set to -1 if the value is numeric, ++** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). ++** + **
        + ** [[SQLITE_SCANSTAT_NLOOP]]
        SQLITE_SCANSTAT_NLOOP
        + **
        ^The [sqlite3_int64] variable pointed to by the V parameter will be +@@ -9264,27 +10437,39 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + ** [[SQLITE_SCANSTAT_EST]]
        SQLITE_SCANSTAT_EST
        + **
        ^The "double" variable pointed to by the V parameter will be set to the + ** query planner's estimate for the average number of rows output from each +-** iteration of the X-th loop. If the query planner's estimates was accurate, ++** iteration of the X-th loop. If the query planner's estimate was accurate, + ** then this value will approximate the quotient NVISIT/NLOOP and the + ** product of this value for all prior loops with the same SELECTID will +-** be the NLOOP value for the current loop. ++** be the NLOOP value for the current loop.
        + ** + ** [[SQLITE_SCANSTAT_NAME]]
        SQLITE_SCANSTAT_NAME
        + **
        ^The "const char *" variable pointed to by the V parameter will be set + ** to a zero-terminated UTF-8 string containing the name of the index or table +-** used for the X-th loop. ++** used for the X-th loop.
        + ** + ** [[SQLITE_SCANSTAT_EXPLAIN]]
        SQLITE_SCANSTAT_EXPLAIN
        + **
        ^The "const char *" variable pointed to by the V parameter will be set + ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] +-** description for the X-th loop. ++** description for the X-th loop.
        + ** +-** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECT
        ++** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECTID
        + **
        ^The "int" variable pointed to by the V parameter will be set to the +-** "select-id" for the X-th loop. The select-id identifies which query or +-** subquery the loop is part of. The main query has a select-id of zero. +-** The select-id is the same value as is output in the first column +-** of an [EXPLAIN QUERY PLAN] query. ++** id for the X-th query plan element. The id value is unique within the ++** statement. The select-id is the same value as is output in the first ++** column of an [EXPLAIN QUERY PLAN] query.
        ++** ++** [[SQLITE_SCANSTAT_PARENTID]]
        SQLITE_SCANSTAT_PARENTID
        ++**
        The "int" variable pointed to by the V parameter will be set to the ++** id of the parent of the current query element, if applicable, or ++** to zero if the query element has no parent. This is the same value as ++** returned in the second column of an [EXPLAIN QUERY PLAN] query.
        ++** ++** [[SQLITE_SCANSTAT_NCYCLE]]
        SQLITE_SCANSTAT_NCYCLE
        ++**
        The sqlite3_int64 output value is set to the number of cycles, ++** according to the processor time-stamp counter, that elapsed while the ++** query element was being processed. This value is not available for ++** all query elements - if it is unavailable the output variable is ++** set to -1.
        + **
        + */ + #define SQLITE_SCANSTAT_NLOOP 0 +@@ -9293,12 +10478,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + #define SQLITE_SCANSTAT_NAME 3 + #define SQLITE_SCANSTAT_EXPLAIN 4 + #define SQLITE_SCANSTAT_SELECTID 5 ++#define SQLITE_SCANSTAT_PARENTID 6 ++#define SQLITE_SCANSTAT_NCYCLE 7 + + /* + ** CAPI3REF: Prepared Statement Scan Status + ** METHOD: sqlite3_stmt + ** +-** This interface returns information about the predicted and measured ++** These interfaces return information about the predicted and measured + ** performance for pStmt. Advanced applications can use this + ** interface to compare the predicted and the measured performance and + ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. +@@ -9309,19 +10496,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ + ** + ** The "iScanStatusOp" parameter determines which status information to return. + ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior +-** of this interface is undefined. +-** ^The requested measurement is written into a variable pointed to by +-** the "pOut" parameter. +-** Parameter "idx" identifies the specific loop to retrieve statistics for. +-** Loops are numbered starting from zero. ^If idx is out of range - less than +-** zero or greater than or equal to the total number of loops used to implement +-** the statement - a non-zero value is returned and the variable that pOut +-** points to is unchanged. +-** +-** ^Statistics might not be available for all loops in all statements. ^In cases +-** where there exist loops with no available statistics, this function behaves +-** as if the loop did not exist - it returns non-zero and leave the variable +-** that pOut points to unchanged. ++** of this interface is undefined. ^The requested measurement is written into ++** a variable pointed to by the "pOut" parameter. ++** ++** The "flags" parameter must be passed a mask of flags. At present only ++** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX ++** is specified, then status information is available for all elements ++** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If ++** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements ++** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of ++** the EXPLAIN QUERY PLAN output) are available. Invoking API ++** sqlite3_stmt_scanstatus() is equivalent to calling ++** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. ++** ++** Parameter "idx" identifies the specific query element to retrieve statistics ++** for. Query elements are numbered starting from zero. A value of -1 may ++** retrieve statistics for the entire query. ^If idx is out of range ++** - less than -1 or greater than or equal to the total number of query ++** elements used to implement the statement - a non-zero value is returned and ++** the variable that pOut points to is unchanged. + ** + ** See also: [sqlite3_stmt_scanstatus_reset()] + */ +@@ -9331,6 +10524,19 @@ SQLITE_API int sqlite3_stmt_scanstatus( + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + void *pOut /* Result written here */ + ); ++SQLITE_API int sqlite3_stmt_scanstatus_v2( ++ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ ++ int idx, /* Index of loop to report on */ ++ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ ++ int flags, /* Mask of flags defined below */ ++ void *pOut /* Result written here */ ++); ++ ++/* ++** CAPI3REF: Prepared Statement Scan Status ++** KEYWORDS: {scan status flags} ++*/ ++#define SQLITE_SCANSTAT_COMPLEX 0x0001 + + /* + ** CAPI3REF: Zero Scan-Status Counters +@@ -9345,9 +10551,10 @@ SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); + + /* + ** CAPI3REF: Flush caches to disk mid-transaction ++** METHOD: sqlite3 + ** + ** ^If a write-transaction is open on [database connection] D when the +-** [sqlite3_db_cacheflush(D)] interface invoked, any dirty ++** [sqlite3_db_cacheflush(D)] interface is invoked, any dirty + ** pages in the pager-cache that are not currently in use are written out + ** to disk. A dirty page may be in use if a database cursor created by an + ** active SQL statement is reading from it, or if it is page 1 of a database +@@ -9377,6 +10584,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); + + /* + ** CAPI3REF: The pre-update hook. ++** METHOD: sqlite3 + ** + ** ^These interfaces are only available if SQLite is compiled using the + ** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option. +@@ -9417,7 +10625,11 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); + ** seventh parameter is the final rowid value of the row being inserted + ** or updated. The value of the seventh parameter passed to the callback + ** function is not defined for operations on WITHOUT ROWID tables, or for +-** INSERT operations on rowid tables. ++** DELETE operations on rowid tables. ++** ++** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from ++** the previous call on the same [database connection] D, or NULL for ++** the first call on D. + ** + ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], + ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces +@@ -9455,6 +10667,15 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); + ** triggers; or 2 for changes resulting from triggers called by top-level + ** triggers; and so forth. + ** ++** When the [sqlite3_blob_write()] API is used to update a blob column, ++** the pre-update hook is invoked with SQLITE_DELETE, because ++** the new values are not yet available. In this case, when a ++** callback made with op==SQLITE_DELETE is actually a write using the ++** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ++** the index of the column being written. In other cases, where the ++** pre-update hook is being invoked for some other reason, including a ++** regular DELETE, sqlite3_preupdate_blobwrite() returns -1. ++** + ** See also: [sqlite3_update_hook()] + */ + #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) +@@ -9475,10 +10696,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); + SQLITE_API int sqlite3_preupdate_count(sqlite3 *); + SQLITE_API int sqlite3_preupdate_depth(sqlite3 *); + SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); ++SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *); + #endif + + /* + ** CAPI3REF: Low-level system error code ++** METHOD: sqlite3 + ** + ** ^Attempt to return the underlying operating system error code or error + ** number that caused the most recent I/O error or failure to open a file. +@@ -9525,6 +10748,14 @@ typedef struct sqlite3_snapshot { + ** If there is not already a read-transaction open on schema S when + ** this function is called, one is opened automatically. + ** ++** If a read-transaction is opened by this function, then it is guaranteed ++** that the returned snapshot object may not be invalidated by a database ++** writer or checkpointer until after the read-transaction is closed. This ++** is not guaranteed if a read-transaction is already open when this ++** function is called. In that case, any subsequent write or checkpoint ++** operation on the database may invalidate the returned snapshot handle, ++** even while the read-transaction remains open. ++** + ** The following must be true for this function to succeed. If any of + ** the following statements are false when sqlite3_snapshot_get() is + ** called, SQLITE_ERROR is returned. The final value of *P is undefined +@@ -9682,15 +10913,16 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c + /* + ** CAPI3REF: Serialize a database + ** +-** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory +-** that is a serialization of the S database on [database connection] D. ++** The sqlite3_serialize(D,S,P,F) interface returns a pointer to ++** memory that is a serialization of the S database on ++** [database connection] D. If S is a NULL pointer, the main database is used. + ** If P is not a NULL pointer, then the size of the database in bytes + ** is written into *P. + ** + ** For an ordinary on-disk database file, the serialization is just a + ** copy of the disk file. For an in-memory database or a "TEMP" database, + ** the serialization is the same sequence of bytes which would be written +-** to disk if that database where backed up to disk. ++** to disk if that database were backed up to disk. + ** + ** The usual case is that sqlite3_serialize() copies the serialization of + ** the database into memory obtained from [sqlite3_malloc64()] and returns +@@ -9699,7 +10931,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c + ** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations + ** are made, and the sqlite3_serialize() function will return a pointer + ** to the contiguous memory representation of the database that SQLite +-** is currently using for that database, or NULL if the no such contiguous ++** is currently using for that database, or NULL if no such contiguous + ** memory representation of the database exists. A contiguous memory + ** representation of the database will usually only exist if there has + ** been a prior call to [sqlite3_deserialize(D,S,...)] with the same +@@ -9708,12 +10940,19 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c + ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy + ** of the database exists. + ** ++** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, ++** the returned buffer content will remain accessible and unchanged ++** until either the next write operation on the connection or when ++** the connection is closed, and applications must not modify the ++** buffer. If the bit had been clear, the returned buffer will not ++** be accessed by SQLite after the call. ++** + ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the + ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory + ** allocation error occurs. + ** +-** This interface is only available if SQLite is compiled with the +-** [SQLITE_ENABLE_DESERIALIZE] option. ++** This interface is omitted if SQLite is compiled with the ++** [SQLITE_OMIT_DESERIALIZE] option. + */ + SQLITE_API unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ +@@ -9756,22 +10995,36 @@ SQLITE_API unsigned char *sqlite3_serialize( + ** SQLite will try to increase the buffer size using sqlite3_realloc64() + ** if writes on the database cause it to grow larger than M bytes. + ** ++** Applications must not modify the buffer P or invalidate it before ++** the database connection D is closed. ++** + ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the + ** database is currently in a read transaction or is involved in a backup + ** operation. + ** ++** It is not possible to deserialize into the TEMP database. If the ++** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ++** function returns SQLITE_ERROR. ++** ++** The deserialized database should not be in [WAL mode]. If the database ++** is in WAL mode, then any attempt to use the database file will result ++** in an [SQLITE_CANTOPEN] error. The application can set the ++** [file format version numbers] (bytes 18 and 19) of the input database P ++** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the ++** database file into rollback mode and work around this limitation. ++** + ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the + ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then + ** [sqlite3_free()] is invoked on argument P prior to returning. + ** +-** This interface is only available if SQLite is compiled with the +-** [SQLITE_ENABLE_DESERIALIZE] option. ++** This interface is omitted if SQLite is compiled with the ++** [SQLITE_OMIT_DESERIALIZE] option. + */ + SQLITE_API int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ +- sqlite3_int64 szDb, /* Number bytes in the deserialization */ ++ sqlite3_int64 szDb, /* Number of bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ + ); +@@ -9779,7 +11032,7 @@ SQLITE_API int sqlite3_deserialize( + /* + ** CAPI3REF: Flags for sqlite3_deserialize() + ** +-** The following are allowed values for 6th argument (the F argument) to ++** The following are allowed values for the 6th argument (the F argument) to + ** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. + ** + ** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization +@@ -9809,10 +11062,21 @@ SQLITE_API int sqlite3_deserialize( + # undef double + #endif + ++#if defined(__wasi__) ++# undef SQLITE_WASI ++# define SQLITE_WASI 1 ++# ifndef SQLITE_OMIT_LOAD_EXTENSION ++# define SQLITE_OMIT_LOAD_EXTENSION ++# endif ++# ifndef SQLITE_THREADSAFE ++# define SQLITE_THREADSAFE 0 ++# endif ++#endif ++ + #ifdef __cplusplus + } /* End of the 'extern "C"' block */ + #endif +-#endif /* SQLITE3_H */ ++/* #endif for SQLITE3_H will be added by mksqlite3.tcl */ + + /******** Begin file sqlite3rtree.h *********/ + /* +@@ -10014,6 +11278,51 @@ SQLITE_API int sqlite3session_create( + */ + SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); + ++/* ++** CAPI3REF: Configure a Session Object ++** METHOD: sqlite3_session ++** ++** This method is used to configure a session object after it has been ++** created. At present the only valid values for the second parameter are ++** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ++** ++*/ ++SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); ++ ++/* ++** CAPI3REF: Options for sqlite3session_object_config ++** ++** The following values may passed as the the 2nd parameter to ++** sqlite3session_object_config(). ++** ++**
        SQLITE_SESSION_OBJCONFIG_SIZE
        ++** This option is used to set, clear or query the flag that enables ++** the [sqlite3session_changeset_size()] API. Because it imposes some ++** computational overhead, this API is disabled by default. Argument ++** pArg must point to a value of type (int). If the value is initially ++** 0, then the sqlite3session_changeset_size() API is disabled. If it ++** is greater than 0, then the same API is enabled. Or, if the initial ++** value is less than zero, no change is made. In all cases the (int) ++** variable is set to 1 if the sqlite3session_changeset_size() API is ++** enabled following the current call, or 0 otherwise. ++** ++** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ++** the first table has been attached to the session object. ++** ++**
        SQLITE_SESSION_OBJCONFIG_ROWID
        ++** This option is used to set, clear or query the flag that enables ++** collection of data for tables with no explicit PRIMARY KEY. ++** ++** Normally, tables with no explicit PRIMARY KEY are simply ignored ++** by the sessions module. However, if this flag is set, it behaves ++** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted ++** as their leftmost columns. ++** ++** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ++** the first table has been attached to the session object. ++*/ ++#define SQLITE_SESSION_OBJCONFIG_SIZE 1 ++#define SQLITE_SESSION_OBJCONFIG_ROWID 2 + + /* + ** CAPI3REF: Enable Or Disable A Session Object +@@ -10248,9 +11557,10 @@ SQLITE_API void sqlite3session_table_filter( + ** is inserted while a session object is enabled, then later deleted while + ** the same session object is disabled, no INSERT record will appear in the + ** changeset, even though the delete took place while the session was disabled. +-** Or, if one field of a row is updated while a session is disabled, and +-** another field of the same row is updated while the session is enabled, the +-** resulting changeset will contain an UPDATE change that updates both fields. ++** Or, if one field of a row is updated while a session is enabled, and ++** then another field of the same row is updated while the session is disabled, ++** the resulting changeset will contain an UPDATE change that updates both ++** fields. + */ + SQLITE_API int sqlite3session_changeset( + sqlite3_session *pSession, /* Session object */ +@@ -10258,6 +11568,22 @@ SQLITE_API int sqlite3session_changeset( + void **ppChangeset /* OUT: Buffer containing changeset */ + ); + ++/* ++** CAPI3REF: Return An Upper-limit For The Size Of The Changeset ++** METHOD: sqlite3_session ++** ++** By default, this function always returns 0. For it to return ++** a useful result, the sqlite3_session object must have been configured ++** to enable this API using sqlite3session_object_config() with the ++** SQLITE_SESSION_OBJCONFIG_SIZE verb. ++** ++** When enabled, this function returns an upper limit, in bytes, for the size ++** of the changeset that might be produced if sqlite3session_changeset() were ++** called. The final changeset size might be equal to or smaller than the ++** size in bytes returned by this function. ++*/ ++SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession); ++ + /* + ** CAPI3REF: Load The Difference Between Tables Into A Session + ** METHOD: sqlite3_session +@@ -10306,8 +11632,9 @@ SQLITE_API int sqlite3session_changeset( + ** database zFrom the contents of the two compatible tables would be + ** identical. + ** +-** It an error if database zFrom does not exist or does not contain the +-** required compatible table. ++** Unless the call to this function is a no-op as described above, it is an ++** error if database zFrom does not exist or does not contain the required ++** compatible table. + ** + ** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite + ** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg +@@ -10375,6 +11702,14 @@ SQLITE_API int sqlite3session_patchset( + */ + SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession); + ++/* ++** CAPI3REF: Query for the amount of heap memory used by a session object. ++** ++** This API returns the total amount of heap memory in bytes currently ++** used by the session object passed as the only argument. ++*/ ++SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession); ++ + /* + ** CAPI3REF: Create An Iterator To Traverse A Changeset + ** CONSTRUCTOR: sqlite3_changeset_iter +@@ -10434,7 +11769,7 @@ SQLITE_API int sqlite3changeset_start_v2( + ** The following flags may passed via the 4th parameter to + ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: + ** +-**
        SQLITE_CHANGESETAPPLY_INVERT
        ++**
        SQLITE_CHANGESETSTART_INVERT
        + ** Invert the changeset while iterating through it. This is equivalent to + ** inverting a changeset using sqlite3changeset_invert() before applying it. + ** It is an error to specify this flag with a patchset. +@@ -10477,18 +11812,23 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); + ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this + ** is not the case, this function returns [SQLITE_MISUSE]. + ** +-** If argument pzTab is not NULL, then *pzTab is set to point to a +-** nul-terminated utf-8 encoded string containing the name of the table +-** affected by the current change. The buffer remains valid until either +-** sqlite3changeset_next() is called on the iterator or until the +-** conflict-handler function returns. If pnCol is not NULL, then *pnCol is +-** set to the number of columns in the table affected by the change. If +-** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ++** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three ++** outputs are set through these pointers: ++** ++** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], ++** depending on the type of change that the iterator currently points to; ++** ++** *pnCol is set to the number of columns in the table affected by the change; and ++** ++** *pzTab is set to point to a nul-terminated utf-8 encoded string containing ++** the name of the table affected by the current change. The buffer remains ++** valid until either sqlite3changeset_next() is called on the iterator ++** or until the conflict-handler function returns. ++** ++** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change + ** is an indirect change, or false (0) otherwise. See the documentation for + ** [sqlite3session_indirect()] for a description of direct and indirect +-** changes. Finally, if pOp is not NULL, then *pOp is set to one of +-** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the +-** type of change that the iterator currently points to. ++** changes. + ** + ** If no error occurs, SQLITE_OK is returned. If an error does occur, an + ** SQLite error code is returned. The values of the output variables may not +@@ -10744,7 +12084,6 @@ SQLITE_API int sqlite3changeset_concat( + void **ppOut /* OUT: Buffer containing output changeset */ + ); + +- + /* + ** CAPI3REF: Changegroup Handle + ** +@@ -10791,6 +12130,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; + */ + SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); + ++/* ++** CAPI3REF: Add a Schema to a Changegroup ++** METHOD: sqlite3_changegroup_schema ++** ++** This method may be used to optionally enforce the rule that the changesets ++** added to the changegroup handle must match the schema of database zDb ++** ("main", "temp", or the name of an attached database). If ++** sqlite3changegroup_add() is called to add a changeset that is not compatible ++** with the configured schema, SQLITE_SCHEMA is returned and the changegroup ++** object is left in an undefined state. ++** ++** A changeset schema is considered compatible with the database schema in ++** the same way as for sqlite3changeset_apply(). Specifically, for each ++** table in the changeset, there exists a database table with: ++** ++**
          ++**
        • The name identified by the changeset, and ++**
        • at least as many columns as recorded in the changeset, and ++**
        • the primary key columns in the same position as recorded in ++** the changeset. ++**
        ++** ++** The output of the changegroup object always has the same schema as the ++** database nominated using this function. In cases where changesets passed ++** to sqlite3changegroup_add() have fewer columns than the corresponding table ++** in the database schema, these are filled in using the default column ++** values from the database schema. This makes it possible to combined ++** changesets that have different numbers of columns for a single table ++** within a changegroup, provided that they are otherwise compatible. ++*/ ++SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); ++ + /* + ** CAPI3REF: Add A Changeset To A Changegroup + ** METHOD: sqlite3_changegroup +@@ -10859,16 +12230,45 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); + ** If the new changeset contains changes to a table that is already present + ** in the changegroup, then the number of columns and the position of the + ** primary key columns for the table must be consistent. If this is not the +-** case, this function fails with SQLITE_SCHEMA. If the input changeset +-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is +-** returned. Or, if an out-of-memory condition occurs during processing, this +-** function returns SQLITE_NOMEM. In all cases, if an error occurs the state +-** of the final contents of the changegroup is undefined. ++** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup ++** object has been configured with a database schema using the ++** sqlite3changegroup_schema() API, then it is possible to combine changesets ++** with different numbers of columns for a single table, provided that ++** they are otherwise compatible. + ** +-** If no error occurs, SQLITE_OK is returned. ++** If the input changeset appears to be corrupt and the corruption is ++** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ++** occurs during processing, this function returns SQLITE_NOMEM. ++** ++** In all cases, if an error occurs the state of the final contents of the ++** changegroup is undefined. If no error occurs, SQLITE_OK is returned. + */ + SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); + ++/* ++** CAPI3REF: Add A Single Change To A Changegroup ++** METHOD: sqlite3_changegroup ++** ++** This function adds the single change currently indicated by the iterator ++** passed as the second argument to the changegroup object. The rules for ++** adding the change are just as described for [sqlite3changegroup_add()]. ++** ++** If the change is successfully added to the changegroup, SQLITE_OK is ++** returned. Otherwise, an SQLite error code is returned. ++** ++** The iterator must point to a valid entry when this function is called. ++** If it does not, SQLITE_ERROR is returned and no change is added to the ++** changegroup. Additionally, the iterator must not have been opened with ++** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also ++** returned. ++*/ ++SQLITE_API int sqlite3changegroup_add_change( ++ sqlite3_changegroup*, ++ sqlite3_changeset_iter* ++); ++ ++ ++ + /* + ** CAPI3REF: Obtain A Composite Changeset From A Changegroup + ** METHOD: sqlite3_changegroup +@@ -11117,9 +12517,30 @@ SQLITE_API int sqlite3changeset_apply_v2( + ** Invert the changeset before applying it. This is equivalent to inverting + ** a changeset using sqlite3changeset_invert() before applying it. It is + ** an error to specify this flag with a patchset. ++** ++**
        SQLITE_CHANGESETAPPLY_IGNORENOOP
        ++** Do not invoke the conflict handler callback for any changes that ++** would not actually modify the database even if they were applied. ++** Specifically, this means that the conflict handler is not invoked ++** for: ++**
          ++**
        • a delete change if the row being deleted cannot be found, ++**
        • an update change if the modified fields are already set to ++** their new values in the conflicting row, or ++**
        • an insert change if all fields of the conflicting row match ++** the row being inserted. ++**
        ++** ++**
        SQLITE_CHANGESETAPPLY_FKNOACTION
        ++** If this flag it set, then all foreign key constraints in the target ++** database behave as if they were declared with "ON UPDATE NO ACTION ON ++** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL ++** or SET DEFAULT. + */ + #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 + #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 ++#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 ++#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 + + /* + ** CAPI3REF: Constants Passed To The Conflict Handler +@@ -11652,8 +13073,8 @@ struct Fts5PhraseIter { + ** EXTENSION API FUNCTIONS + ** + ** xUserData(pFts): +-** Return a copy of the context pointer the extension function was +-** registered with. ++** Return a copy of the pUserData pointer passed to the xCreateFunction() ++** API when the extension function was registered. + ** + ** xColumnTotalSize(pFts, iCol, pnToken): + ** If parameter iCol is less than zero, set output variable *pnToken +@@ -11685,8 +13106,11 @@ struct Fts5PhraseIter { + ** created with the "columnsize=0" option. + ** + ** xColumnText: +-** This function attempts to retrieve the text of column iCol of the +-** current document. If successful, (*pz) is set to point to a buffer ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of columns in the table, SQLITE_RANGE is returned. ++** ++** Otherwise, this function attempts to retrieve the text of column iCol of ++** the current document. If successful, (*pz) is set to point to a buffer + ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes + ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, + ** if an error occurs, an SQLite error code is returned and the final values +@@ -11696,8 +13120,10 @@ struct Fts5PhraseIter { + ** Returns the number of phrases in the current query expression. + ** + ** xPhraseSize: +-** Returns the number of tokens in phrase iPhrase of the query. Phrases +-** are numbered starting from zero. ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of phrases in the current query, as returned by xPhraseCount, ++** 0 is returned. Otherwise, this function returns the number of tokens in ++** phrase iPhrase of the query. Phrases are numbered starting from zero. + ** + ** xInstCount: + ** Set *pnInst to the total number of occurrences of all phrases within +@@ -11713,12 +13139,13 @@ struct Fts5PhraseIter { + ** Query for the details of phrase match iIdx within the current row. + ** Phrase matches are numbered starting from zero, so the iIdx argument + ** should be greater than or equal to zero and smaller than the value +-** output by xInstCount(). ++** output by xInstCount(). If iIdx is less than zero or greater than ++** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. + ** +-** Usually, output parameter *piPhrase is set to the phrase number, *piCol ++** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol + ** to the column in which it occurs and *piOff the token offset of the +-** first token of the phrase. Returns SQLITE_OK if successful, or an error +-** code (i.e. SQLITE_NOMEM) if an error occurs. ++** first token of the phrase. SQLITE_OK is returned if successful, or an ++** error code (i.e. SQLITE_NOMEM) if an error occurs. + ** + ** This API can be quite slow if used with an FTS5 table created with the + ** "detail=none" or "detail=column" option. +@@ -11744,6 +13171,10 @@ struct Fts5PhraseIter { + ** Invoking Api.xUserData() returns a copy of the pointer passed as + ** the third argument to pUserData. + ** ++** If parameter iPhrase is less than zero, or greater than or equal to ++** the number of phrases in the query, as returned by xPhraseCount(), ++** this function returns SQLITE_RANGE. ++** + ** If the callback function returns any value other than SQLITE_OK, the + ** query is abandoned and the xQueryPhrase function returns immediately. + ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. +@@ -11825,6 +13256,10 @@ struct Fts5PhraseIter { + ** (i.e. if it is a contentless table), then this API always iterates + ** through an empty set (all calls to xPhraseFirst() set iCol to -1). + ** ++** In all cases, matches are visited in (column ASC, offset ASC) order. ++** i.e. all those in column 0, sorted by offset, followed by those in ++** column 1, etc. ++** + ** xPhraseNext() + ** See xPhraseFirst above. + ** +@@ -11858,9 +13293,80 @@ struct Fts5PhraseIter { + ** + ** xPhraseNextColumn() + ** See xPhraseFirstColumn above. ++** ++** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) ++** This is used to access token iToken of phrase iPhrase of the current ++** query. Before returning, output parameter *ppToken is set to point ++** to a buffer containing the requested token, and *pnToken to the ++** size of this buffer in bytes. ++** ++** If iPhrase or iToken are less than zero, or if iPhrase is greater than ++** or equal to the number of phrases in the query as reported by ++** xPhraseCount(), or if iToken is equal to or greater than the number of ++** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken ++ are both zeroed. ++** ++** The output text is not a copy of the query text that specified the ++** token. It is the output of the tokenizer module. For tokendata=1 ++** tables, this includes any embedded 0x00 and trailing data. ++** ++** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ++** This is used to access token iToken of phrase hit iIdx within the ++** current row. If iIdx is less than zero or greater than or equal to the ++** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ++** output variable (*ppToken) is set to point to a buffer containing the ++** matching document token, and (*pnToken) to the size of that buffer in ++** bytes. ++** ++** The output text is not a copy of the document text that was tokenized. ++** It is the output of the tokenizer module. For tokendata=1 tables, this ++** includes any embedded 0x00 and trailing data. ++** ++** This API may be slow in some cases if the token identified by parameters ++** iIdx and iToken matched a prefix token in the query. In most cases, the ++** first call to this API for each prefix token in the query is forced ++** to scan the portion of the full-text index that matches the prefix ++** token to collect the extra data required by this API. If the prefix ++** token matches a large number of token instances in the document set, ++** this may be a performance problem. ++** ++** If the user knows in advance that a query may use this API for a ++** prefix token, FTS5 may be configured to collect all required data as part ++** of the initial querying of the full-text index, avoiding the second scan ++** entirely. This also causes prefix queries that do not use this API to ++** run more slowly and use more memory. FTS5 may be configured in this way ++** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ++** option, or on a per-query basis using the ++** [fts5_insttoken | fts5_insttoken()] user function. ++** ++** This API can be quite slow if used with an FTS5 table created with the ++** "detail=none" or "detail=column" option. ++** ++** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ++** If parameter iCol is less than zero, or greater than or equal to the ++** number of columns in the table, SQLITE_RANGE is returned. ++** ++** Otherwise, this function attempts to retrieve the locale associated ++** with column iCol of the current row. Usually, there is no associated ++** locale, and output parameters (*pzLocale) and (*pnLocale) are set ++** to NULL and 0, respectively. However, if the fts5_locale() function ++** was used to associate a locale with the value when it was inserted ++** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated ++** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) ++** is set to the size in bytes of the buffer, not including the ++** nul-terminator. ++** ++** If successful, SQLITE_OK is returned. Or, if an error occurs, an ++** SQLite error code is returned. The final value of the output parameters ++** is undefined in this case. ++** ++** xTokenize_v2: ++** Tokenize text using the tokenizer belonging to the FTS5 table. This ++** API is the same as the xTokenize() API, except that it allows a tokenizer ++** locale to be specified. + */ + struct Fts5ExtensionApi { +- int iVersion; /* Currently always set to 3 */ ++ int iVersion; /* Currently always set to 4 */ + + void *(*xUserData)(Fts5Context*); + +@@ -11895,6 +13401,22 @@ struct Fts5ExtensionApi { + + int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); + void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); ++ ++ /* Below this point are iVersion>=3 only */ ++ int (*xQueryToken)(Fts5Context*, ++ int iPhrase, int iToken, ++ const char **ppToken, int *pnToken ++ ); ++ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); ++ ++ /* Below this point are iVersion>=4 only */ ++ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); ++ int (*xTokenize_v2)(Fts5Context*, ++ const char *pText, int nText, /* Text to tokenize */ ++ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ ++ void *pCtx, /* Context passed to xToken() */ ++ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ ++ ); + }; + + /* +@@ -11915,7 +13437,7 @@ struct Fts5ExtensionApi { + ** A tokenizer instance is required to actually tokenize text. + ** + ** The first argument passed to this function is a copy of the (void*) +-** pointer provided by the application when the fts5_tokenizer object ++** pointer provided by the application when the fts5_tokenizer_v2 object + ** was registered with FTS5 (the third argument to xCreateTokenizer()). + ** The second and third arguments are an array of nul-terminated strings + ** containing the tokenizer arguments, if any, specified following the +@@ -11939,7 +13461,7 @@ struct Fts5ExtensionApi { + ** argument passed to this function is a pointer to an Fts5Tokenizer object + ** returned by an earlier call to xCreate(). + ** +-** The second argument indicates the reason that FTS5 is requesting ++** The third argument indicates the reason that FTS5 is requesting + ** tokenization of the supplied text. This is always one of the following + ** four values: + ** +@@ -11963,6 +13485,13 @@ struct Fts5ExtensionApi { + ** on a columnsize=0 database. + ** + ** ++** The sixth and seventh arguments passed to xTokenize() - pLocale and ++** nLocale - are a pointer to a buffer containing the locale to use for ++** tokenization (e.g. "en_US") and its size in bytes, respectively. The ++** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in ++** which case nLocale is always 0) to indicate that the tokenizer should ++** use its default locale. ++** + ** For each token in the input string, the supplied callback xToken() must + ** be invoked. The first argument to it should be a copy of the pointer + ** passed as the second argument to xTokenize(). The third and fourth +@@ -11986,6 +13515,30 @@ struct Fts5ExtensionApi { + ** may abandon the tokenization and return any error code other than + ** SQLITE_OK or SQLITE_DONE. + ** ++** If the tokenizer is registered using an fts5_tokenizer_v2 object, ++** then the xTokenize() method has two additional arguments - pLocale ++** and nLocale. These specify the locale that the tokenizer should use ++** for the current request. If pLocale and nLocale are both 0, then the ++** tokenizer should use its default locale. Otherwise, pLocale points to ++** an nLocale byte buffer containing the name of the locale to use as utf-8 ++** text. pLocale is not nul-terminated. ++** ++** FTS5_TOKENIZER ++** ++** There is also an fts5_tokenizer object. This is an older, deprecated, ++** version of fts5_tokenizer_v2. It is similar except that: ++** ++**
          ++**
        • There is no "iVersion" field, and ++**
        • The xTokenize() method does not take a locale argument. ++**
        ++** ++** Legacy fts5_tokenizer tokenizers must be registered using the ++** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). ++** ++** Tokenizer implementations registered using either API may be retrieved ++** using both xFindTokenizer() and xFindTokenizer_v2(). ++** + ** SYNONYM SUPPORT + ** + ** Custom tokenizers may also support synonyms. Consider a case in which a +@@ -12089,11 +13642,38 @@ struct Fts5ExtensionApi { + ** as separate queries of the FTS index are required for each synonym. + ** + ** When using methods (2) or (3), it is important that the tokenizer only +-** provide synonyms when tokenizing document text (method (2)) or query +-** text (method (3)), not both. Doing so will not cause any errors, but is ++** provide synonyms when tokenizing document text (method (3)) or query ++** text (method (2)), not both. Doing so will not cause any errors, but is + ** inefficient. + */ + typedef struct Fts5Tokenizer Fts5Tokenizer; ++typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; ++struct fts5_tokenizer_v2 { ++ int iVersion; /* Currently always 2 */ ++ ++ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); ++ void (*xDelete)(Fts5Tokenizer*); ++ int (*xTokenize)(Fts5Tokenizer*, ++ void *pCtx, ++ int flags, /* Mask of FTS5_TOKENIZE_* flags */ ++ const char *pText, int nText, ++ const char *pLocale, int nLocale, ++ int (*xToken)( ++ void *pCtx, /* Copy of 2nd argument to xTokenize() */ ++ int tflags, /* Mask of FTS5_TOKEN_* flags */ ++ const char *pToken, /* Pointer to buffer containing token */ ++ int nToken, /* Size of token in bytes */ ++ int iStart, /* Byte offset of token within input text */ ++ int iEnd /* Byte offset of end of token within input text */ ++ ) ++ ); ++}; ++ ++/* ++** New code should use the fts5_tokenizer_v2 type to define tokenizer ++** implementations. The following type is included for legacy applications ++** that still use it. ++*/ + typedef struct fts5_tokenizer fts5_tokenizer; + struct fts5_tokenizer { + int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); +@@ -12113,6 +13693,7 @@ struct fts5_tokenizer { + ); + }; + ++ + /* Flags that may be passed as the third argument to xTokenize() */ + #define FTS5_TOKENIZE_QUERY 0x0001 + #define FTS5_TOKENIZE_PREFIX 0x0002 +@@ -12132,13 +13713,13 @@ struct fts5_tokenizer { + */ + typedef struct fts5_api fts5_api; + struct fts5_api { +- int iVersion; /* Currently always set to 2 */ ++ int iVersion; /* Currently always set to 3 */ + + /* Create a new tokenizer */ + int (*xCreateTokenizer)( + fts5_api *pApi, + const char *zName, +- void *pContext, ++ void *pUserData, + fts5_tokenizer *pTokenizer, + void (*xDestroy)(void*) + ); +@@ -12147,7 +13728,7 @@ struct fts5_api { + int (*xFindTokenizer)( + fts5_api *pApi, + const char *zName, +- void **ppContext, ++ void **ppUserData, + fts5_tokenizer *pTokenizer + ); + +@@ -12155,10 +13736,29 @@ struct fts5_api { + int (*xCreateFunction)( + fts5_api *pApi, + const char *zName, +- void *pContext, ++ void *pUserData, + fts5_extension_function xFunction, + void (*xDestroy)(void*) + ); ++ ++ /* APIs below this point are only available if iVersion>=3 */ ++ ++ /* Create a new tokenizer */ ++ int (*xCreateTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void *pUserData, ++ fts5_tokenizer_v2 *pTokenizer, ++ void (*xDestroy)(void*) ++ ); ++ ++ /* Find an existing tokenizer */ ++ int (*xFindTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void **ppUserData, ++ fts5_tokenizer_v2 **ppTokenizer ++ ); + }; + + /* +@@ -12172,3 +13772,4 @@ struct fts5_api { + #endif /* _FTS5_H */ + + /******** End of fts5.h *********/ ++#endif /* SQLITE3_H */ +-- +2.34.1 + + +From cafd5f8fae3b859b7f8c29feb03ea075c7221497 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 28 Sep 2025 18:23:54 +0200 +Subject: [PATCH 159/173] Format code + +Use clang-format to format all code files +--- + src/civetweb.c | 70 +++++++++++++++++++--------------- + src/handle_form.inl | 15 ++++---- + src/mod_lua.inl | 3 +- + src/third_party/civetweb_lua.h | 6 +-- + 4 files changed, 51 insertions(+), 43 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 2758a784..78345fb5 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -161,7 +161,7 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check"); + * file system. + * NO_FILES only disables the automatic mapping between URLs and local + * file names. +- * NO_FILESYSTEM = do not access any file at all. Useful for embedded ++ * NO_FILESYSTEMS = do not access any file at all. Useful for embedded + * devices without file system. Logging to files in not available + * (use callbacks instead) and API functions like mg_send_file are not + * available. +@@ -2253,7 +2253,7 @@ static const struct mg_option config_options[] = { + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, + #endif +- {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"}, ++ {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"}, + {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, + {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, +@@ -4235,26 +4235,29 @@ send_cors_header(struct mg_connection *conn) + conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; + const char *cors_meth_cfg = + conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; +- const char *cors_repl_asterisk_with_orig_cfg = +- conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; +- +- if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr && cors_repl_asterisk_with_orig_cfg && *cors_repl_asterisk_with_orig_cfg) { +- int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); +- ++ const char *cors_repl_asterisk_with_orig_cfg = ++ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; ++ ++ if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr ++ && cors_repl_asterisk_with_orig_cfg ++ && *cors_repl_asterisk_with_orig_cfg) { ++ int cors_repl_asterisk_with_orig = ++ mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); ++ + /* Cross-origin resource sharing (CORS), see + * http://www.html5rocks.com/en/tutorials/cors/, + * http://www.html5rocks.com/static/images/cors_server_flowchart.png + * CORS preflight is not supported for files. */ + if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') { + mg_response_header_add(conn, +- "Access-Control-Allow-Origin", +- origin_hdr, +- -1); ++ "Access-Control-Allow-Origin", ++ origin_hdr, ++ -1); + } else { + mg_response_header_add(conn, +- "Access-Control-Allow-Origin", +- cors_orig_cfg, +- -1); ++ "Access-Control-Allow-Origin", ++ cors_orig_cfg, ++ -1); + } + } + +@@ -15100,7 +15103,7 @@ handle_request(struct mg_connection *conn) + } + return; + } +- ++ + + /* 1.3. decode url (if config says so) */ + if (should_decode_url(conn)) { +@@ -15128,11 +15131,10 @@ handle_request(struct mg_connection *conn) + } + remove_dot_segments(tmp); + ri->local_uri = tmp; +- #if !defined(NO_FILES) /* Only compute if later code can actually use it */ +- /* Cache URI length once; recompute only if the buffer changes later. */ +- uri_len = (int)strlen(ri->local_uri); +- #endif +- ++#if !defined(NO_FILES) /* Only compute if later code can actually use it */ ++ /* Cache URI length once; recompute only if the buffer changes later. */ ++ uri_len = (int)strlen(ri->local_uri); ++#endif + + + /* step 1. completed, the url is known now */ +@@ -15186,18 +15188,20 @@ handle_request(struct mg_connection *conn) + const char *cors_acrm = get_header(ri->http_headers, + ri->num_headers, + "Access-Control-Request-Method"); +- const char *cors_repl_asterisk_with_orig_cfg = +- conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; +- ++ const char *cors_repl_asterisk_with_orig_cfg = ++ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; ++ + /* Todo: check if cors_origin is in cors_orig_cfg. + * Or, let the client check this. */ + + if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) + && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) + && (cors_origin != NULL) && (cors_acrm != NULL) +- && (cors_repl_asterisk_with_orig_cfg != NULL) && (*cors_repl_asterisk_with_orig_cfg != 0)) { +- int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); +- ++ && (cors_repl_asterisk_with_orig_cfg != NULL) ++ && (*cors_repl_asterisk_with_orig_cfg != 0)) { ++ int cors_repl_asterisk_with_orig = ++ mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); ++ + /* This is a valid CORS preflight, and the server is configured + * to handle it automatically. */ + const char *cors_acrh = +@@ -15218,7 +15222,10 @@ handle_request(struct mg_connection *conn) + "Content-Length: 0\r\n" + "Connection: %s\r\n", + date, +- (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, ++ (cors_repl_asterisk_with_orig == 0 ++ && cors_orig_cfg[0] == '*') ++ ? cors_origin ++ : cors_orig_cfg, + ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), + suggest_connection_header(conn)); + +@@ -15610,7 +15617,7 @@ handle_request(struct mg_connection *conn) + + /* 12. Directory uris should end with a slash */ + if (file.stat.is_directory && (uri_len > 0) +- && (ri->local_uri[uri_len - 1] != '/')) { ++ && (ri->local_uri[uri_len - 1] != '/')) { + + + /* Path + server root */ +@@ -15640,7 +15647,8 @@ handle_request(struct mg_connection *conn) + len++; + } + +- /* Append with size of space left for query string + null terminator */ ++ /* Append with size of space left for query string + null ++ * terminator */ + size_t max_append = buflen - len - 1; + strncat(new_path, ri->query_string, max_append); + } +@@ -18907,12 +18915,12 @@ get_uri_type(const char *uri) + for (i = 0; uri[i] != 0; i++) { + /* Check for CRLF injection attempts */ + if (uri[i] == '%') { +- if (uri[i+1] == '0' && (uri[i+2] == 'd' || uri[i+2] == 'D')) { ++ if (uri[i + 1] == '0' && (uri[i + 2] == 'd' || uri[i + 2] == 'D')) { + /* Found %0d (CR) */ + DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri); + return 0; + } +- if (uri[i+1] == '0' && (uri[i+2] == 'a' || uri[i+2] == 'A')) { ++ if (uri[i + 1] == '0' && (uri[i + 2] == 'a' || uri[i + 2] == 'A')) { + /* Found %0a (LF) */ + DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri); + return 0; +diff --git a/src/handle_form.inl b/src/handle_form.inl +index cdff2a13..bc76f7d3 100644 +--- a/src/handle_form.inl ++++ b/src/handle_form.inl +@@ -109,8 +109,8 @@ url_encoded_field_get( + return MG_FORM_FIELD_STORAGE_ABORT; + } + +- key_dec_len = mg_url_decode( +- key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); ++ key_dec_len = ++ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); + + if (*value_len >= 2 && value[*value_len - 2] == '%') + *value_len -= 2; +@@ -146,7 +146,8 @@ unencoded_field_get(const struct mg_connection *conn, + int key_dec_len; + (void)conn; + +- key_dec_len = mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); ++ key_dec_len = ++ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); + if (key_dec_len < 0) { + return MG_FORM_FIELD_STORAGE_ABORT; + } +@@ -488,14 +489,14 @@ mg_handle_form_request(struct mg_connection *conn, + vallen = (ptrdiff_t)strlen(val); + end_of_key_value_pair_found = all_data_read; + if ((buf + buf_fill) > (val + vallen)) { +- /* Avoid DoS attacks by having a zero byte in the middle of +- * a request that is supposed to be URL encoded. Since this +- * request is certainly invalid, according to the protocol ++ /* Avoid DoS attacks by having a zero byte in the middle ++ * of a request that is supposed to be URL encoded. ++ * Since this request is certainly invalid, according to ++ * the protocol + * specification, stop processing it. Fixes #1348 */ + abort_read = 1; + break; + } +- + } + + if (field_storage == MG_FORM_FIELD_STORAGE_GET) { +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index f2e40237..e8279099 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -2700,7 +2700,8 @@ civetweb_open_lua_libs(lua_State *L) + luaL_openlibs(L); + #else + // In Lua 5.5 and later has become a macro +- extern void (luaL_openselectedlibs) (lua_State *L, int load, int preload); ++ extern void( ++ luaL_openselectedlibs)(lua_State * L, int load, int preload); + luaL_openselectedlibs(L, ~0, 0); + #endif + } +diff --git a/src/third_party/civetweb_lua.h b/src/third_party/civetweb_lua.h +index 15a76c1d..8c59aaa1 100644 +--- a/src/third_party/civetweb_lua.h ++++ b/src/third_party/civetweb_lua.h +@@ -49,7 +49,7 @@ extern "C" { + #define LUA_ERRGCMM 999 /* not supported */ + #define mg_lua_load(a, b, c, d, e) lua_load(a, b, c, d) + #define lua_rawlen lua_objlen +-#define mg_lua_newstate(a, b) \ ++#define mg_lua_newstate(a, b) \ + luaL_newstate() /* Must use luaL_newstate() for 64 bit target */ + #define lua_pushinteger lua_pushnumber + #define luaL_newlib(L, t) \ +@@ -62,9 +62,7 @@ extern "C" { + } + #define luaL_setfuncs(L, r, u) lua_register(L, r->name, r->func) + +-#elif LUA_VERSION_NUM == 502 || \ +- LUA_VERSION_NUM == 503 || \ +- LUA_VERSION_NUM == 504 ++#elif LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504 + /* Lua 5.2 - 5.4 detected */ + #define mg_lua_load lua_load + #define mg_lua_newstate lua_newstate +-- +2.34.1 + + +From 3784be9d7c01b03b027408fee4d6d5d8b89136e6 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Mon, 29 Sep 2025 22:00:26 +0200 +Subject: [PATCH 160/173] AppVeyor: Add Visual Studio 2022 + +add compiler version settings for VS2022 +add verstion setting for VS2026 (preview) +--- + appveyor.yml | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/appveyor.yml b/appveyor.yml +index ad121e4e..b40e514f 100644 +--- a/appveyor.yml ++++ b/appveyor.yml +@@ -341,6 +341,8 @@ before_build: + - if "%compiler_version%"=="19" (set "vs_version=14" & set "vs_year=2015") + - if "%compiler_version%"=="20" (set "vs_version=15" & set "vs_year=2017") + - if "%compiler_version%"=="21" (set "vs_version=16" & set "vs_year=2019") ++ - if "%compiler_version%"=="22" (set "vs_version=17" & set "vs_year=2022") ++ - if "%compiler_version%"=="23" (set "vs_version=18" & set "vs_year=2026") + - if "%compiler_name%"=="msvc" (set "generator=Visual Studio %vs_version% %vs_year%") + - set "arch_arg= " + - if "%compiler_name%"=="msvc" ( +-- +2.34.1 + + +From b6ef58f4c4c7fbe90fd1065bccf45b143345f1a6 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Tue, 30 Sep 2025 23:16:38 +0200 +Subject: [PATCH 161/173] Do not scan third party projects + +Third party projects need to be taken from their original repositories. +--- + .github/workflows/codeql.yml | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 22fad79d..0dc7ce4a 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -14,10 +14,14 @@ name: "CodeQL" + on: + push: + branches: [ "master" ] ++ paths-ignore: ++ - src/third_party/** + schedule: + - cron: '0 0 * * *' + pull_request: + branches: '*' ++ paths-ignore: ++ - src/third_party/** + + jobs: + analyze: +-- +2.34.1 + + +From 52fb72ed2318c6233b7112a80cdd9cb94e3e7735 Mon Sep 17 00:00:00 2001 +From: DL6ER +Date: Thu, 2 Oct 2025 18:10:29 +0200 +Subject: [PATCH 162/173] Allow setting SSL cipher list also with MbedTLS + +Signed-off-by: DL6ER +--- + src/civetweb.c | 3 +- + src/mod_mbedtls.inl | 76 +++++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 76 insertions(+), 3 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 78345fb5..f9978fd1 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -16805,7 +16805,8 @@ mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) + return 0; + } + +- return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE]) ++ return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE], ++ dom_ctx->config[SSL_CIPHER_LIST]) + == 0 + ? 1 + : 0; +diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl +index 03d189ea..8256b3a4 100644 +--- a/src/mod_mbedtls.inl ++++ b/src/mod_mbedtls.inl +@@ -33,7 +33,7 @@ typedef struct { + + + /* public api */ +-int mbed_sslctx_init(SSL_CTX *ctx, const char *crt); ++int mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist); + void mbed_sslctx_uninit(SSL_CTX *ctx); + void mbed_ssl_close(mbedtls_ssl_context *ssl); + int mbed_ssl_accept(mbedtls_ssl_context **ssl, +@@ -43,6 +43,9 @@ int mbed_ssl_accept(mbedtls_ssl_context **ssl, + int mbed_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, int len); + int mbed_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, int len); + ++/* Set the ciphersuites to be used by mbedtls using a comma-separated string */ ++int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list); ++ + static void mbed_debug(void *context, + int level, + const char *file, +@@ -52,7 +55,7 @@ static int mbed_ssl_handshake(mbedtls_ssl_context *ssl); + + + int +-mbed_sslctx_init(SSL_CTX *ctx, const char *crt) ++mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + { + mbedtls_ssl_config *conf; + int rc; +@@ -154,6 +157,14 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + DEBUG_TRACE("TLS cannot set certificate and private key (%i)", rc); + return -1; + } ++ ++ /* Set ciphersuites if specified */ ++ if (cipherlist != NULL && ++ cipherlist[0] != '\0' && ++ mbed_sslctx_set_ciphersuites(conf, cipherlist) != 0) { ++ DEBUG_TRACE("Failed to set ciphersuites: no valid ciphersuites are found in the list"); ++ return -1; ++ } + return 0; + } + +@@ -279,4 +290,65 @@ mbed_debug(void *user_param, + str); + } + ++/** ++ * @brief Sets the list of allowed ciphersuites for an mbedTLS SSL configuration. ++ * ++ * Parses a comma-separated list of ciphersuite names, converts them to their ++ * corresponding mbedTLS ciphersuite IDs, and configures the SSL context to use ++ * only those ciphersuites. ++ * ++ * @param conf Pointer to the mbedTLS SSL configuration structure. ++ * @param cipher_list Comma-separated string of ciphersuite names. ++ * @return 0 on success, ++ * -1 if conf or cipher_list is NULL, ++ * -2 if no valid ciphersuites are found in the list. ++ * ++ * @note The ciphersuite ID array is static and must remain valid after the function returns, ++ * as mbedtls_ssl_conf_ciphersuites() does not copy the array. ++ */ ++int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list) { ++ if (conf == NULL || cipher_list == NULL) { ++ return -1; ++ } ++ ++ // The array for ciphersuite IDs must remain valid after this function ++ // returns as mbedtls_ssl_conf_ciphersuites() does not copy the array, ++ // but only stores the pointer. We do not allow more than 64 cipher ++ // suites for simplicity. ++ static int ciphersuites[64] = { 0 }; ++ size_t count = 0; ++ ++ char buf[1024]; ++ strncpy(buf, cipher_list, sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ ++ char *token = strtok(buf, ","); ++ while (token && count < 63) { ++ // Remove leading/trailing whitespace ++ while (*token == ' ' || *token == '\t') token++; ++ char *end = token + strlen(token) - 1; ++ while (end > token && (*end == ' ' || *end == '\t')) { ++ *end = '\0'; ++ end--; ++ } ++ const mbedtls_ssl_ciphersuite_t *ciphersuite = mbedtls_ssl_ciphersuite_from_string(token); ++ if (ciphersuite != NULL) { ++ const int id = mbedtls_ssl_ciphersuite_get_id(ciphersuite); ++ DEBUG_TRACE("Adding ciphersuite '%s' (ID %d)", token, id); ++ ciphersuites[count++] = id; ++ } ++ token = strtok(NULL, ","); ++ } ++ ciphersuites[count] = 0; ++ ++ if (count == 0) { ++ DEBUG_TRACE("No valid ciphersuites found"); ++ return -2; // No valid ciphersuites found ++ } ++ ++ // Set the ciphersuites ++ mbedtls_ssl_conf_ciphersuites(conf, ciphersuites); ++ return 0; ++} ++ + #endif /* USE_MBEDTLS */ +-- +2.34.1 + + +From 27415007b1c538296b31d6672cf09d2261a84c0d Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sat, 11 Oct 2025 10:03:17 +0000 +Subject: [PATCH 163/173] Bump github/codeql-action from 3 to 4 + +Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. +- [Release notes](https://github.com/github/codeql-action/releases) +- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) +- [Commits](https://github.com/github/codeql-action/compare/v3...v4) + +--- +updated-dependencies: +- dependency-name: github/codeql-action + dependency-version: '4' + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/codeql.yml | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 0dc7ce4a..96f98bbb 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -55,7 +55,7 @@ jobs: + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL +- uses: github/codeql-action/init@v3 ++ uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. +@@ -82,7 +82,7 @@ jobs: + make build WITH_ALL=1 + + - name: Perform CodeQL Analysis +- uses: github/codeql-action/analyze@v3 ++ uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" + upload: false +@@ -111,7 +111,7 @@ jobs: + output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + + - name: Upload CodeQL results to code scanning +- uses: github/codeql-action/upload-sarif@v3 ++ uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: ${{ steps.step1.outputs.sarif-output }} + category: "/language:${{matrix.language}}" +-- +2.34.1 + + +From 26131e0b6d0100afa81f811563b4936fe35ca538 Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sat, 25 Oct 2025 10:02:46 +0000 +Subject: [PATCH 164/173] Bump actions/upload-artifact from 4 to 5 + +Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. +- [Release notes](https://github.com/actions/upload-artifact/releases) +- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) + +--- +updated-dependencies: +- dependency-name: actions/upload-artifact + dependency-version: '5' + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/cifuzz.yml | 2 +- + .github/workflows/codeql.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml +index d62ff855..7644d847 100644 +--- a/.github/workflows/cifuzz.yml ++++ b/.github/workflows/cifuzz.yml +@@ -18,7 +18,7 @@ jobs: + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash +- uses: actions/upload-artifact@v4 ++ uses: actions/upload-artifact@v5 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 96f98bbb..31d0c4bc 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -118,7 +118,7 @@ jobs: + + - name: Upload CodeQL results as an artifact + if: success() || failure() +- uses: actions/upload-artifact@v4 ++ uses: actions/upload-artifact@v5 + with: + name: codeql-results + path: ${{ steps.step1.outputs.sarif-output }} +-- +2.34.1 + + +From 9bbef31027c1c7bccddae122abcd92a5ec8e0375 Mon Sep 17 00:00:00 2001 +From: James +Date: Tue, 4 Nov 2025 09:21:32 +0000 +Subject: [PATCH 165/173] add server address to reqinfo + +--- + include/civetweb.h | 1 + + src/civetweb.c | 4 ++++ + 2 files changed, 5 insertions(+) + +diff --git a/include/civetweb.h b/include/civetweb.h +index 2665d646..f585df25 100644 +--- a/include/civetweb.h ++++ b/include/civetweb.h +@@ -164,6 +164,7 @@ struct mg_request_info { + const char *remote_user; /* Authenticated user, or NULL if no auth + used */ + char remote_addr[48]; /* Client's IP address as a string. */ ++ char server_addr[48]; /* Server's IP address as a string. */ + + long long content_length; /* Length (in bytes) of the request body, + can be -1 if no length was given. */ +diff --git a/src/civetweb.c b/src/civetweb.c +index f9978fd1..a399ce45 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -20364,6 +20364,10 @@ worker_thread_run(struct mg_connection *conn) + sockaddr_to_string(conn->request_info.remote_addr, + sizeof(conn->request_info.remote_addr), + &conn->client.rsa); ++ ++ sockaddr_to_string(conn->request_info.server_addr, ++ sizeof(conn->request_info.server_addr), ++ &conn->client.lsa); + + DEBUG_TRACE("Incoming %sconnection from %s", + (conn->client.is_ssl ? "SSL " : ""), +-- +2.34.1 + + +From 389197bb68d86d242734a373c8f099d186c5a03f Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sat, 22 Nov 2025 10:03:21 +0000 +Subject: [PATCH 166/173] Bump actions/checkout from 5 to 6 + +Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. +- [Release notes](https://github.com/actions/checkout/releases) +- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) +- [Commits](https://github.com/actions/checkout/compare/v5...v6) + +--- +updated-dependencies: +- dependency-name: actions/checkout + dependency-version: '6' + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/cibuild.yml | 2 +- + .github/workflows/codeql.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml +index 04773117..44f05cc4 100644 +--- a/.github/workflows/cibuild.yml ++++ b/.github/workflows/cibuild.yml +@@ -392,7 +392,7 @@ jobs: + + steps: + - name: Checkout code +- uses: actions/checkout@v5 ++ uses: actions/checkout@v6 + + - name: Export number of CPUs + run: | +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 31d0c4bc..13986994 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -49,7 +49,7 @@ jobs: + + steps: + - name: Checkout repository +- uses: actions/checkout@v5 ++ uses: actions/checkout@v6 + with: + submodules: recursive + +-- +2.34.1 + + +From 34cffad63c44b811781c78472a965fbe0f06451e Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sat, 13 Dec 2025 10:03:40 +0000 +Subject: [PATCH 167/173] Bump actions/upload-artifact from 5 to 6 + +Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. +- [Release notes](https://github.com/actions/upload-artifact/releases) +- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6) + +--- +updated-dependencies: +- dependency-name: actions/upload-artifact + dependency-version: '6' + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/cifuzz.yml | 2 +- + .github/workflows/codeql.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml +index 7644d847..ab1416e0 100644 +--- a/.github/workflows/cifuzz.yml ++++ b/.github/workflows/cifuzz.yml +@@ -18,7 +18,7 @@ jobs: + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash +- uses: actions/upload-artifact@v5 ++ uses: actions/upload-artifact@v6 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index 31d0c4bc..6cb5602b 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -118,7 +118,7 @@ jobs: + + - name: Upload CodeQL results as an artifact + if: success() || failure() +- uses: actions/upload-artifact@v5 ++ uses: actions/upload-artifact@v6 + with: + name: codeql-results + path: ${{ steps.step1.outputs.sarif-output }} +-- +2.34.1 + + +From 1bd545f4eb3a6cf6e4c872ee576ba5a32cfce48e Mon Sep 17 00:00:00 2001 +From: Dominik +Date: Sat, 13 Dec 2025 13:37:07 +0100 +Subject: [PATCH 168/173] Implement changes for MbedTLS 4.0.0 + +Signed-off-by: Dominik +--- + src/mod_mbedtls.inl | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl +index 8256b3a4..6883d48a 100644 +--- a/src/mod_mbedtls.inl ++++ b/src/mod_mbedtls.inl +@@ -1,8 +1,10 @@ + #if defined(USE_MBEDTLS) // USE_MBEDTLS used with NO_SSL + +-#include "mbedtls/ctr_drbg.h" + #include "mbedtls/debug.h" ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + #include "mbedtls/entropy.h" ++#include "mbedtls/ctr_drbg.h" ++#endif + #include "mbedtls/error.h" + + #if MBEDTLS_VERSION_NUMBER >= 0x03000000 +@@ -26,8 +28,10 @@ typedef mbedtls_ssl_context SSL; + typedef struct { + mbedtls_ssl_config conf; /* SSL configuration */ + mbedtls_x509_crt cert; /* Certificate */ ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_context ctr; /* Counter random generator state */ + mbedtls_entropy_context entropy; /* Entropy context */ ++#endif + mbedtls_pk_context pkey; /* Private key */ + } SSL_CTX; + +@@ -65,7 +69,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + } + + DEBUG_TRACE("%s", "Initializing MbedTLS SSL"); ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_entropy_init(&ctx->entropy); ++#endif + + conf = &ctx->conf; + mbedtls_ssl_config_init(conf); +@@ -88,7 +94,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + + /* Initialize TLS key and cert */ + mbedtls_pk_init(&ctx->pkey); ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_init(&ctx->ctr); ++#endif + mbedtls_x509_crt_init(&ctx->cert); + + #ifdef MBEDTLS_PSA_CRYPTO_C +@@ -104,6 +112,7 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + } + #endif + ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + rc = mbedtls_ctr_drbg_seed(&ctx->ctr, + mbedtls_entropy_func, + &ctx->entropy, +@@ -113,8 +122,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + DEBUG_TRACE("TLS random seed failed (%i)", rc); + return -1; + } ++#endif + +-#if MBEDTLS_VERSION_NUMBER >= 0x03000000 ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000 + // mbedtls_pk_parse_keyfile() has changed in mbedTLS 3.0. You now need + // to pass a properly seeded, cryptographically secure RNG when calling + // these functions. It is used for blinding, a countermeasure against +@@ -145,7 +155,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + return -1; + } + ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &ctx->ctr); ++#endif + + /* Set auth mode if peer cert should be verified */ + mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); +@@ -172,10 +184,12 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + void + mbed_sslctx_uninit(SSL_CTX *ctx) + { ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_free(&ctx->ctr); ++ mbedtls_entropy_free(&ctx->entropy); ++#endif + mbedtls_pk_free(&ctx->pkey); + mbedtls_x509_crt_free(&ctx->cert); +- mbedtls_entropy_free(&ctx->entropy); + mbedtls_ssl_config_free(&ctx->conf); + } + +-- +2.34.1 + + +From 2013003792054aedb4791a8d19c6e96e4d9f6cb9 Mon Sep 17 00:00:00 2001 +From: Andreas Ziegler <15275159+aeolio@users.noreply.github.com> +Date: Thu, 26 Feb 2026 07:32:34 +0100 +Subject: [PATCH 169/173] Fix missing lua_websock_data declaration w/o + USE_WEBSOCKET +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If civetweb.c is compiled with USE_WEBSOCKET undefined, a compiler warning +is raised: + +In file included from [...]/pihole-ftl-6.5/src/webserver/civetweb/civetweb.c:13436: +[...]/pihole-ftl-6.5/src/webserver/civetweb/mod_lua.inl:2852:32: warning: ‘struct lua_websock_data’ declared inside parameter list will not be visible outside of this definition or declaration + 2852 | struct lua_websock_data *ws_conn_list, + | ^~~~~~~~~~~~~~~~ + +The reason for this warning is a missing declaration of struct +lua_websock_data, which is omitted due to USE_WEBSOCKET being undefined. + +For safety reasons, the code related to this parameter in function +prepare_lua_environment() should also be omitted, if USE_WEBSOCKET is +undefined. + +Fix missing declaration of lua_websock_data and disable code related to +unused function parameter in prepare_lua_environment(). + +Signed-off-by: Andreas Ziegler <15275159+aeolio@users.noreply.github.com> +--- + src/mod_lua.inl | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index e8279099..e6596b1c 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -1977,6 +1977,9 @@ struct lua_websock_data { + struct mg_connection *conn[MAX_WORKER_THREADS]; + pthread_mutex_t ws_mutex; + }; ++#else ++/* used in parameter list of prepare_lua_environment() */ ++struct lua_websock_data; + #endif + + +@@ -2886,11 +2889,13 @@ prepare_lua_environment(struct mg_context *ctx, + #endif + + /* Store context in the registry */ ++#if defined(USE_WEBSOCKET) + if (ctx != NULL) { + lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); + lua_pushlightuserdata(L, (void *)ctx); + lua_settable(L, LUA_REGISTRYINDEX); + } ++#endif /* USE_WEBSOCKET */ + if (ws_conn_list != NULL) { + lua_pushlightuserdata(L, (void *)&lua_regkey_connlist); + lua_pushlightuserdata(L, (void *)ws_conn_list); +-- +2.34.1 + + +From 3c0fb6ad582224b91959caf79a25dd98389c084d Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Mon, 30 Mar 2026 23:40:14 +0200 +Subject: [PATCH 170/173] Quote path handling for service installation + +--- + src/main.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/main.c b/src/main.c +index 9d3a0ea7..be859feb 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -2796,8 +2796,9 @@ manage_service(int action) + show_error(); + } else if (action == ID_INSTALL_SERVICE) { + path[sizeof(path) - 1] = 0; +- GetModuleFileName(NULL, path, sizeof(path) - 1); +- strncat(path, " ", sizeof(path) - 1 - strlen(path)); ++ path[0]='"'; ++ GetModuleFileName(NULL, path+1, sizeof(path) - 3); ++ strncat(path, "\" ", sizeof(path) - 2 - strlen(path)); + strncat(path, service_magic_argument, sizeof(path) - 1 - strlen(path)); + hService = CreateService(hSCM, + service_name, +-- +2.34.1 + + +From d5f655053274997734d9785cb5db03af020fa750 Mon Sep 17 00:00:00 2001 +From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> +Date: Sat, 4 Apr 2026 10:02:33 +0000 +Subject: [PATCH 171/173] Bump actions/upload-artifact from 6 to 7 + +Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7. +- [Release notes](https://github.com/actions/upload-artifact/releases) +- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7) + +--- +updated-dependencies: +- dependency-name: actions/upload-artifact + dependency-version: '7' + dependency-type: direct:production + update-type: version-update:semver-major +... + +Signed-off-by: dependabot[bot] +--- + .github/workflows/cifuzz.yml | 2 +- + .github/workflows/codeql.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml +index ab1416e0..8691823e 100644 +--- a/.github/workflows/cifuzz.yml ++++ b/.github/workflows/cifuzz.yml +@@ -18,7 +18,7 @@ jobs: + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash +- uses: actions/upload-artifact@v6 ++ uses: actions/upload-artifact@v7 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +index cd794a66..280d15ea 100644 +--- a/.github/workflows/codeql.yml ++++ b/.github/workflows/codeql.yml +@@ -118,7 +118,7 @@ jobs: + + - name: Upload CodeQL results as an artifact + if: success() || failure() +- uses: actions/upload-artifact@v6 ++ uses: actions/upload-artifact@v7 + with: + name: codeql-results + path: ${{ steps.step1.outputs.sarif-output }} +-- +2.34.1 + + +From d110e8731c6aa0fcd04504deb449bdb773cfb9da Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 19 Apr 2026 16:39:09 +0200 +Subject: [PATCH 172/173] Fix memory allocation condition for websocket payload + +--- + src/civetweb.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index a399ce45..28b040a7 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -13559,9 +13559,10 @@ read_websocket(struct mg_connection *conn, + if ((header_len > 0) && (body_len >= header_len)) { + /* Allocate space to hold websocket payload */ + unsigned char *data = mem; ++ size_t required_len = (size_t)data_len + 4; + +- if ((size_t)data_len > (size_t)sizeof(mem)) { +- data = (unsigned char *)mg_malloc_ctx((size_t)data_len, ++ if (required_len > sizeof(mem)) { ++ data = (unsigned char *)mg_malloc_ctx(required_len, + conn->phys_ctx); + if (data == NULL) { + /* Allocation failed, exit the loop and then close the +-- +2.34.1 + + +From 588860e30721bf5453b0440c390865a8e85dcae5 Mon Sep 17 00:00:00 2001 +From: bel2125 +Date: Sun, 19 Apr 2026 17:04:45 +0200 +Subject: [PATCH 173/173] Refactor request handling: don't allow chunked + encoding and content length + +--- + src/civetweb.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index 28b040a7..f6a60e21 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -19158,7 +19158,7 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) + static int + get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) + { +- const char *cl; ++ const char *h_zip, *h_chunk, *h_len; + + conn->connection_type = + CONNECTION_TYPE_REQUEST; /* request (valid of not) */ +@@ -19193,20 +19193,23 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) + } + + #if USE_ZLIB +- if (((cl = get_header(conn->request_info.http_headers, ++ if (((h_zip = get_header(conn->request_info.http_headers, + conn->request_info.num_headers, + "Accept-Encoding")) + != NULL) +- && strstr(cl, "gzip")) { ++ && strstr(h_zip, "gzip")) { + conn->accept_gzip = 1; + } + #endif +- if (((cl = get_header(conn->request_info.http_headers, ++ h_chunk = get_header(conn->request_info.http_headers, + conn->request_info.num_headers, +- "Transfer-Encoding")) +- != NULL) ++ "Transfer-Encoding"); ++ h_len = get_header(conn->request_info.http_headers, ++ conn->request_info.num_headers, ++ "Content-Length"); ++ if (h_chunk != NULL) + && mg_strcasecmp(cl, "identity")) { +- if (mg_strcasecmp(cl, "chunked")) { ++ if ((0!=mg_strcasecmp(cl, "chunked")) || (h_len!=NULL)) { + mg_snprintf(conn, + NULL, /* No truncation check for ebuf */ + ebuf, +@@ -19218,10 +19221,7 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) + } + conn->is_chunked = 1; + conn->content_len = 0; /* not yet read */ +- } else if ((cl = get_header(conn->request_info.http_headers, +- conn->request_info.num_headers, +- "Content-Length")) +- != NULL) { ++ } else if (h_len != NULL) { + /* Request has content length set */ + char *endptr = NULL; + conn->content_len = strtoll(cl, &endptr, 10); +-- +2.34.1 + From 2a312ed0da91c182de0b3150b0dd5ec4061e69b6 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 16:06:13 +0200 Subject: [PATCH 45/54] [cmake] force-enable builtin_civetweb since 1.16 version misses several important CVE fixes --- cmake/modules/RootBuildOptions.cmake | 2 +- cmake/modules/SearchInstalledSoftware.cmake | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/modules/RootBuildOptions.cmake b/cmake/modules/RootBuildOptions.cmake index f983c82a631e0..b1bb09479ae10 100644 --- a/cmake/modules/RootBuildOptions.cmake +++ b/cmake/modules/RootBuildOptions.cmake @@ -85,7 +85,7 @@ ROOT_BUILD_OPTION(builtin_cfitsio OFF "Build CFITSIO internally (requires networ ROOT_BUILD_OPTION(builtin_clang ON "Build bundled copy of Clang") ROOT_BUILD_OPTION(builtin_cling ON "Build bundled copy of Cling. Only build with an external cling if you know what you are doing: associating ROOT commits with cling commits is tricky.") MARK_AS_ADVANCED(builtin_cling) -ROOT_BUILD_OPTION(builtin_civetweb ON "Use civetweb distributed with ROOT") +ROOT_BUILD_OPTION(builtin_civetweb ON "Build civetweb internally (requires network). Recommended, since sytem-wide versions older than 1.17 might be unstable.") ROOT_BUILD_OPTION(builtin_fftw3 OFF "Build FFTW3 internally (requires network) [GPL]") ROOT_BUILD_OPTION(builtin_freetype OFF "Build bundled copy of freetype") ROOT_BUILD_OPTION(builtin_ftgl OFF "Build bundled copy of FTGL") diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 7367b8f7a401d..637dc95c7b7e6 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -532,6 +532,10 @@ if(http AND NOT builtin_civetweb) message(STATUS "Looking for civetweb") if(fail-on-missing) find_package(civetweb 1.15 REQUIRED) + if(civetweb_FOUND AND civetweb_VERSION VERSION_LESS 1.17) + message(STATUS "found civetweb does not include some CVE security fixes. Switching on builtin_civetweb option") + set(builtin_civetweb ON CACHE BOOL "Enabled because civetweb version too old" FORCE) + endif() else() find_package(civetweb 1.15 QUIET) if(civetweb_FOUND) From f2fe2af83de016e1f40e007f3861a8e2a543e8fc Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 17:10:56 +0200 Subject: [PATCH 46/54] fix patches --- .../{v1_17.patch => 0001-v117patches.patch} | 20754 +++++----------- ...02-only-include-header-if-necessary.patch} | 6 +- builtins/civetweb/CMakeLists.txt | 5 +- 3 files changed, 6535 insertions(+), 14230 deletions(-) rename builtins/civetweb/{v1_17.patch => 0001-v117patches.patch} (95%) rename builtins/civetweb/{0001-only-include-engine-when-needed.patch => 0002-only-include-header-if-necessary.patch} (77%) diff --git a/builtins/civetweb/v1_17.patch b/builtins/civetweb/0001-v117patches.patch similarity index 95% rename from builtins/civetweb/v1_17.patch rename to builtins/civetweb/0001-v117patches.patch index ecad2b3d4cd7d..2a45bd2368bf4 100644 --- a/builtins/civetweb/v1_17.patch +++ b/builtins/civetweb/0001-v117patches.patch @@ -1,7791 +1,254 @@ -From 2bdeaed9f982abc6d551932363a2ae9e2e8dd9ef Mon Sep 17 00:00:00 2001 -From: Lorenzo Canepa -Date: Sat, 19 Feb 2022 19:59:06 +0100 -Subject: [PATCH 001/173] should fix *** CID 349582: Null pointer dereferences - (REVERSE_INULL) +From ae04f545031956aafac23d60f30d924d97f49efe Mon Sep 17 00:00:00 2001 +From: ferdymercury +Date: Mon, 27 Apr 2026 17:05:02 +0200 +Subject: [PATCH 1/2] v117patches -Signed-off-by: Lorenzo Canepa --- - src/civetweb.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 2c10a06f..f1c25b3c 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -12711,11 +12711,14 @@ dav_lock_file(struct mg_connection *conn, const char *path) - int i; - uint64_t LOCK_DURATION_NS = - (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000; -- struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock; -+ struct twebdav_lock *dav_lock = NULL; - -- if (!path || !conn->dom_ctx || !conn->request_info.remote_user) { -+ if (!path || !conn -+ || !conn->dom_ctx || !conn->request_info.remote_user || !conn->phys_ctx) { - return; - } -+ -+ dav_lock = conn->phys_ctx->webdav_lock; - mg_get_request_link(conn, link_buf, sizeof(link_buf)); - - /* const char *refresh = mg_get_header(conn, "If"); */ --- -2.34.1 - - -From 49332567ab616f106d18f8c34e19be51281cf8cf Mon Sep 17 00:00:00 2001 -From: Niklas Fiekas -Date: Tue, 11 Apr 2023 09:04:44 +0200 -Subject: [PATCH 002/173] Fix -Wmissing-field-initializers in C++ code - ---- - src/CivetServer.cpp | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp -index fafd6e18..2bcf5c31 100644 ---- a/src/CivetServer.cpp -+++ b/src/CivetServer.cpp -@@ -406,12 +406,12 @@ CivetServer::CivetServer(const char **options, - userCloseHandler = NULL; - } - callbacks.connection_close = closeHandler; -- struct mg_init_data mg_start_init_data = {0}; -+ struct mg_init_data mg_start_init_data = {}; - mg_start_init_data.callbacks = &callbacks; - mg_start_init_data.user_data = this; - mg_start_init_data.configuration_options = options; - -- struct mg_error_data mg_start_error_data = {0}; -+ struct mg_error_data mg_start_error_data = {}; - char errtxtbuf[256] = {0}; - mg_start_error_data.text = errtxtbuf; - mg_start_error_data.text_buffer_size = sizeof(errtxtbuf); -@@ -450,12 +450,12 @@ CivetServer::CivetServer(const std::vector &options, - } - pointers.back() = NULL; - -- struct mg_init_data mg_start_init_data = {0}; -+ struct mg_init_data mg_start_init_data = {}; - mg_start_init_data.callbacks = &callbacks; - mg_start_init_data.user_data = this; - mg_start_init_data.configuration_options = &pointers[0]; - -- struct mg_error_data mg_start_error_data = {0}; -+ struct mg_error_data mg_start_error_data = {}; - char errtxtbuf[256] = {0}; - mg_start_error_data.text = errtxtbuf; - mg_start_error_data.text_buffer_size = sizeof(errtxtbuf); --- -2.34.1 - - -From 23095a8d89fd15e9211c0b9e0023cda38c46b418 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Sat, 15 Apr 2023 12:11:16 +0200 -Subject: [PATCH 003/173] Fix compilation of USE_LUA without USE_TIMERS - -Signed-off-by: DL6ER ---- - src/mod_lua.inl | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index f1073e55..d280e897 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -2905,8 +2905,8 @@ prepare_lua_environment(struct mg_context *ctx, - || (lua_env_type == LUA_ENV_TYPE_BACKGROUND)) { - reg_function(L, "set_timeout", lwebsocket_set_timeout); - reg_function(L, "set_interval", lwebsocket_set_interval); --#endif - } -+#endif - - reg_conn_function(L, "get_mime_type", lsp_get_mime_type, conn); - reg_conn_function(L, "get_option", lsp_get_option, conn); --- -2.34.1 - - -From 5a9aec4f86289523960812ea3569856e57e6220e Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 16 Apr 2023 11:57:45 +0200 -Subject: [PATCH 004/173] Update version number to V1.17 - ---- - RELEASE_NOTES.md | 13 +++++++++++++ - VisualStudio/civetweb_lua/civetweb_lua.vcxproj | 7 ++++--- - include/civetweb.h | 4 ++-- - 3 files changed, 19 insertions(+), 5 deletions(-) - -diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md -index 354061ee..0bdd1c5a 100644 ---- a/RELEASE_NOTES.md -+++ b/RELEASE_NOTES.md -@@ -1,3 +1,13 @@ -+Release Notes v1.17 (work in progress) -+=== -+### Objectives: *to be defined* -+ -+Changes -+------- -+ -+- Update version number -+ -+ - Release Notes v1.16 - === - ### Objectives: *bug fixes, documentation, WebDAV* -@@ -21,6 +31,9 @@ Changes - - Remove Conan support - - Update version number - -+Note: A precompiled 32-bit executables for Windows is no longer provided, but only a 64 bit version. -+The source code itself still supports 32-bit platforms. -+ - Known Issues - ----- - -diff --git a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj -index 7eb6fd9f..58e7fc7c 100644 ---- a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj -+++ b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj -@@ -110,7 +110,8 @@ - true - - -- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.14 --set-product-version 1.14 -+ -+ - - - -@@ -159,7 +160,7 @@ - true - - -- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 -+ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 - - - -@@ -180,7 +181,7 @@ - true - - -- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 -+ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 - - - -diff --git a/include/civetweb.h b/include/civetweb.h -index 167c6a76..78149722 100644 ---- a/include/civetweb.h -+++ b/include/civetweb.h -@@ -23,9 +23,9 @@ - #ifndef CIVETWEB_HEADER_INCLUDED - #define CIVETWEB_HEADER_INCLUDED - --#define CIVETWEB_VERSION "1.16" -+#define CIVETWEB_VERSION "1.17" - #define CIVETWEB_VERSION_MAJOR (1) --#define CIVETWEB_VERSION_MINOR (16) -+#define CIVETWEB_VERSION_MINOR (17) - #define CIVETWEB_VERSION_PATCH (0) - - #ifndef CIVETWEB_API --- -2.34.1 - - -From c4bb956f20ee1ddbbdb5704d5c59da6ac753cad1 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 16 Apr 2023 12:03:57 +0200 -Subject: [PATCH 005/173] Add a cast for Interlocked functions, required only - for C++ in Windows - -See #1151 ---- - src/civetweb.c | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 9e321edf..93bbed9f 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -1125,7 +1125,15 @@ mg_atomic_inc(volatile ptrdiff_t *addr) - #if defined(_WIN64) && !defined(NO_ATOMICS) - ret = InterlockedIncrement64(addr); - #elif defined(_WIN32) && !defined(NO_ATOMICS) -+#ifdef __cplusplus -+ /* For C++ the Microsoft Visual Studio compiler can not decide what -+ * overloaded function prototpye in the SDC corresponds to "ptrdiff_t". */ -+ static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch"); -+ static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch"); -+ ret = InterlockedIncrement((LONG *)addr); -+#else - ret = InterlockedIncrement(addr); -+#endif - #elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) -@@ -1148,7 +1156,14 @@ mg_atomic_dec(volatile ptrdiff_t *addr) - #if defined(_WIN64) && !defined(NO_ATOMICS) - ret = InterlockedDecrement64(addr); - #elif defined(_WIN32) && !defined(NO_ATOMICS) -+#ifdef __cplusplus -+ /* see mg_atomic_inc */ -+ static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch"); -+ static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch"); -+ ret = InterlockedDecrement((LONG *)addr); -+#else - ret = InterlockedDecrement(addr); -+#endif - #elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) --- -2.34.1 - - -From 180db0fcafb40393db16224a971eb48841973fec Mon Sep 17 00:00:00 2001 -From: Niklas Fiekas -Date: Fri, 21 Apr 2023 08:34:44 +0200 -Subject: [PATCH 006/173] Fix mg_get_cookie() parameter description - ---- - docs/api/mg_get_cookie.md | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/docs/api/mg_get_cookie.md b/docs/api/mg_get_cookie.md -index a738bc07..637caeee 100644 ---- a/docs/api/mg_get_cookie.md -+++ b/docs/api/mg_get_cookie.md -@@ -6,8 +6,8 @@ - - | Parameter | Type | Description | - | :--- | :--- | :--- | --|**`cookie`**|`const char *`|The cookie name| --|**`var_name`**|`const char *`|The variable name| -+|**`cookie`**|`const char *`|The unparsed cookie header| -+|**`var_name`**|`const char *`|The cookie name| - |**`buf`**|`char *`|The buffer where to store the contents of the cookie| - |**`buf_len`**|`size_t`|The length of the cookie buffer, including the terminating NUL| - --- -2.34.1 - - -From 5d2faa55481b8299d6f16c131cfc1a1ec06e9786 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 23 Apr 2023 22:13:00 +0200 -Subject: [PATCH 007/173] Format code after merge - ---- - src/civetweb.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 71ed388f..f99d7039 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -12825,8 +12825,8 @@ dav_lock_file(struct mg_connection *conn, const char *path) - (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000; - struct twebdav_lock *dav_lock = NULL; - -- if (!path || !conn -- || !conn->dom_ctx || !conn->request_info.remote_user || !conn->phys_ctx) { -+ if (!path || !conn || !conn->dom_ctx || !conn->request_info.remote_user -+ || !conn->phys_ctx) { - return; - } - --- -2.34.1 - - -From 59391a97fea8cdb442150c4784fdc3b7edbb190f Mon Sep 17 00:00:00 2001 -From: Biswapriyo Nath -Date: Fri, 28 Apr 2023 19:16:24 +0530 -Subject: [PATCH 008/173] Fix compiler error with clang - -This fixes the following -error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes] ---- - src/main.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/main.c b/src/main.c -index 66510d0f..f056a954 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -2474,7 +2474,7 @@ show_settings_dialog(void) - - - static void --change_password_file() -+change_password_file(void) - { - /* Parameter for size/format tuning of the dialog */ - short HEIGHT = 15; -@@ -2685,7 +2685,7 @@ sysinfo_reload(struct dlg_proc_param *prm) - - - int --show_system_info() -+show_system_info(void) - { - /* Parameter for size/format tuning of the dialog */ - short HEIGHT = 15; --- -2.34.1 - - -From ac00b3bec380665d25fa70a755bb8c1de4ad3db1 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 30 Apr 2023 09:07:18 +0200 -Subject: [PATCH 009/173] Avoid Memory Sanitizer warning - -Fixes #1154 ---- - src/civetweb.c | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index f99d7039..5be49462 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -2325,7 +2325,7 @@ STOP_FLAG_IS_TWO(stop_flag_t *f) - static void - STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v) - { -- stop_flag_t sf; -+ stop_flag_t sf = 0; - do { - sf = mg_atomic_compare_and_swap(f, *f, v); - } while (sf != v); -@@ -9528,9 +9528,8 @@ connect_socket( - /* Data for poll */ - struct mg_pollfd pfd[1]; - int pollres; -- int ms_wait = 10000; /* 10 second timeout */ -- stop_flag_t nonstop; -- STOP_FLAG_ASSIGN(&nonstop, 0); -+ int ms_wait = 10000; /* 10 second timeout */ -+ stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */ - - /* For a non-blocking socket, the connect sequence is: - * 1) call connect (will not block) --- -2.34.1 - - -From 2b33a24c54c847ca9da2031adafa7cb8e3298065 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Mon, 1 May 2023 21:53:55 +0200 -Subject: [PATCH 010/173] Merge with 2023 update branch - ---- - src/civetweb.c | 90 +++++++++++++++++++++++++------------------------- - 1 file changed, 45 insertions(+), 45 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 5be49462..fdea2db4 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -1,4 +1,4 @@ --/* Copyright (c) 2013-2021 the Civetweb developers -+/* Copyright (c) 2013-2023 the Civetweb developers - * Copyright (c) 2004-2013 Sergey Lyubka - * - * Permission is hereby granted, free of charge, to any person obtaining a copy -@@ -51,8 +51,8 @@ - #if !defined(_CRT_SECURE_NO_WARNINGS) - #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ - #endif --#if !defined(_WIN32_WINNT) /* defined for tdm-gcc so we can use getnameinfo */ --#define _WIN32_WINNT 0x0502 -+#if !defined(_WIN32_WINNT) /* Minimum API version */ -+#define _WIN32_WINNT 0x0601 - #endif - #else - #if !defined(_GNU_SOURCE) -@@ -525,10 +525,10 @@ mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, - - - #if defined(_WIN32) /* WINDOWS include block */ --#include /* *alloc( */ --#include /* *alloc( */ --#include /* struct timespec */ --#include -+#include -+#include /* *alloc( */ -+#include /* *alloc( */ -+#include /* struct timespec */ - #include /* DTL add for SO_EXCLUSIVE */ - #include - -@@ -1322,13 +1322,13 @@ mg_malloc_ex(size_t size, - #endif - - if (data) { -+ uintptr_t *tmp = (uintptr_t *)data; - ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size); - mg_atomic_max(&mstat->maxMemUsed, mmem); -- - mg_atomic_inc(&mstat->blockCount); -- ((uintptr_t *)data)[0] = size; -- ((uintptr_t *)data)[1] = (uintptr_t)mstat; -- memory = (void *)(((char *)data) + 2 * sizeof(uintptr_t)); -+ tmp[0] = size; -+ tmp[1] = (uintptr_t)mstat; -+ memory = (void *)&tmp[2]; - } - - #if defined(MEMORY_DEBUGGING) -@@ -22315,44 +22315,44 @@ mg_get_connection_info(const struct mg_context *ctx, - return (int)connection_info_length; - } - -+ - #if 0 --/* Get handler information. It can be printed or stored by the caller. -- * Return the size of available information. */ -+/* Get handler information. Not fully implemented. Is it required? */ - CIVETWEB_API int - mg_get_handler_info(struct mg_context *ctx, -- char *buffer, -- int buflen) -+ char *buffer, -+ int buflen) - { -- int handler_info_len = 0; -- struct mg_handler_info *tmp_rh; -- mg_lock_context(ctx); -- -- for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { -- -- if (buflen > handler_info_len+ tmp_rh->uri_len) { -- memcpy(buffer+handler_info_len, tmp_rh->uri, tmp_rh->uri_len); -- } -- handler_info_len += tmp_rh->uri_len; -- -- switch (tmp_rh->handler_type) { -- case REQUEST_HANDLER: -- (void)tmp_rh->handler; -- break; -- case WEBSOCKET_HANDLER: -- (void)tmp_rh->connect_handler; -- (void) tmp_rh->ready_handler; -- (void) tmp_rh->data_handler; -- (void) tmp_rh->close_handler; -- break; -- case AUTH_HANDLER: -- (void) tmp_rh->auth_handler; -- break; -- } -- (void)cbdata; -- } -- -- mg_unlock_context(ctx); -- return handler_info_len; -+ int handler_info_len = 0; -+ struct mg_handler_info *tmp_rh; -+ mg_lock_context(ctx); -+ -+ for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { -+ -+ if (buflen > handler_info_len + tmp_rh->uri_len) { -+ memcpy(buffer + handler_info_len, tmp_rh->uri, tmp_rh->uri_len); -+ } -+ handler_info_len += tmp_rh->uri_len; -+ -+ switch (tmp_rh->handler_type) { -+ case REQUEST_HANDLER: -+ (void)tmp_rh->handler; -+ break; -+ case WEBSOCKET_HANDLER: -+ (void)tmp_rh->connect_handler; -+ (void)tmp_rh->ready_handler; -+ (void)tmp_rh->data_handler; -+ (void)tmp_rh->close_handler; -+ break; -+ case AUTH_HANDLER: -+ (void)tmp_rh->auth_handler; -+ break; -+ } -+ (void)cbdata; -+ } -+ -+ mg_unlock_context(ctx); -+ return handler_info_len; - } - #endif - #endif --- -2.34.1 - - -From 62781b775c5338cffd5f41bb29758f5efde6c116 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Mon, 1 May 2023 22:01:13 +0200 -Subject: [PATCH 011/173] Use 64 bit timer for 64 bit Windows - ---- - src/timer.inl | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/src/timer.inl b/src/timer.inl -index 9b8d5539..39d68dfc 100644 ---- a/src/timer.inl -+++ b/src/timer.inl -@@ -39,13 +39,16 @@ TIMER_API double - timer_getcurrenttime(struct mg_context *ctx) - { - #if defined(_WIN32) -+ uint64_t now_tick64 = 0; -+#if defined(_WIN64) -+ now_tick64 = GetTickCount64(); -+#else - /* GetTickCount returns milliseconds since system start as - * unsigned 32 bit value. It will wrap around every 49.7 days. - * We need to use a 64 bit counter (will wrap in 500 mio. years), - * by adding the 32 bit difference since the last call to a - * 64 bit counter. This algorithm will only work, if this - * function is called at least once every 7 weeks. */ -- uint64_t now_tick64 = 0; - DWORD now_tick = GetTickCount(); - - if (ctx->timers) { -@@ -55,6 +58,7 @@ timer_getcurrenttime(struct mg_context *ctx) - ctx->timers->last_tick = now_tick; - pthread_mutex_unlock(&ctx->timers->mutex); - } -+#endif - return (double)now_tick64 * 1.0E-3; - #else - struct timespec now_ts; --- -2.34.1 - - -From 68479acb5fb8db378d9a5aefcb84d9c3c0403400 Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Fri, 5 May 2023 13:08:49 -0700 -Subject: [PATCH 012/173] fallback_document_root and fallback_websocket_root - initial implementation - ---- - docs/UserManual.md | 16 +++++++ - examples/https/civetweb.conf | 3 ++ - src/civetweb.c | 85 +++++++++++++++++++++--------------- - src/main.c | 3 ++ - src/mod_lua.inl | 8 ++++ - test/page3.ssjs | 2 + - unittest/private.c | 2 + - 7 files changed, 85 insertions(+), 34 deletions(-) - -diff --git a/docs/UserManual.md b/docs/UserManual.md -index df9b3685..358895f6 100644 ---- a/docs/UserManual.md -+++ b/docs/UserManual.md -@@ -296,6 +296,15 @@ The current directory is commonly referenced as dot (`.`). - It is recommended to use an absolute path for document\_root, in order to - avoid accidentally serving the wrong directory. - -+### fallback\_document\_root `.` -+An optional second directory to check for a file to serve, if the requested -+filename was not found in the document\_root directory. -+This can be useful in cases where an app ships with a read-only HTML content -+directory as part of its install, but you nevertheless want to allow the user -+to customize the served content by placing modified or additional files into -+a writable directory, where they will take precedence over their read-only -+counterparts, on a per-file basis. -+ - ### enable\_auth\_domain\_check `yes` - When using absolute URLs, verify the host is identical to the authentication\_domain. - If enabled, requests to absolute URLs will only be processed -@@ -806,6 +815,11 @@ be used for websockets as well. Since websockets use a different URL scheme - websockets may also be served from a different directory. By default, - the document\_root is used as websocket\_root as well. - -+### fallback\_websocket\_root -+An optional second directory to check for websocket-files that were -+not found in the websocket\_root directory. (See the documentation for -+fallback\_root for details) -+ - ### websocket\_timeout\_ms - Timeout for network read and network write operations for websockets, WS(S), - in milliseconds. If this value is not set, the value of request\_timeout\_ms -@@ -969,6 +983,7 @@ mg (table): - mg.onerror(msg) -- error handler, can be overridden - mg.auth_domain -- a string that holds the HTTP authentication domain - mg.document_root -- a string that holds the document root directory -+ mg.fallback_document_root -- a string that holds an optional second document root directory - mg.lua_type -- a string that holds the lua script type - mg.system -- a string that holds the operating system name - mg.version -- a string that holds CivetWeb version -@@ -1028,6 +1043,7 @@ If websocket and timers support is enabled then the following is also available: - mg.set_timeout(fn,delay,[interval]) -- call function after delay at an interval - mg.set_interval(fn,delay,[interval]) -- call function after delay at an interval - mg.websocket_root -- a string that holds the websocket root -+ mg.fallback_websocket_root -- a string that holds an optional second websocket root - - connect (function): - -diff --git a/examples/https/civetweb.conf b/examples/https/civetweb.conf -index c1ecc642..552a565f 100644 ---- a/examples/https/civetweb.conf -+++ b/examples/https/civetweb.conf -@@ -36,6 +36,9 @@ listening_ports 80r,443s - #document_root tdb - #authentication_domain mydomain.com - -+# Optional fallback document root, checked for file-paths not found in document_root -+#fallback_document_root tdb_fallback -+ - # Set the a certificate - ssl_certificate ../../resources/cert/server.pem - -diff --git a/src/civetweb.c b/src/civetweb.c -index fdea2db4..064aca29 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -1967,6 +1967,7 @@ enum { - - /* Once for each domain */ - DOCUMENT_ROOT, -+ FALLBACK_DOCUMENT_ROOT, - - ACCESS_LOG_FILE, - ERROR_LOG_FILE, -@@ -2048,6 +2049,7 @@ enum { - - #if defined(USE_WEBSOCKET) - WEBSOCKET_ROOT, -+ FALLBACK_WEBSOCKET_ROOT, - #endif - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - LUA_WEBSOCKET_EXTENSIONS, -@@ -2111,6 +2113,7 @@ static const struct mg_option config_options[] = { - - /* Once for each domain */ - {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, -+ {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, - - {"access_log_file", MG_CONFIG_TYPE_FILE, NULL}, - {"error_log_file", MG_CONFIG_TYPE_FILE, NULL}, -@@ -2209,6 +2212,7 @@ static const struct mg_option config_options[] = { - - #if defined(USE_WEBSOCKET) - {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, -+ {"fallback_websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, - #endif - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, -@@ -7650,7 +7654,8 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ - - #if !defined(NO_FILES) - const char *uri = conn->request_info.local_uri; -- const char *root = conn->dom_ctx->config[DOCUMENT_ROOT]; -+ const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT], conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT], NULL}; -+ int fileExists = 0; - const char *rewrite; - struct vec a, b; - ptrdiff_t match_len; -@@ -7685,7 +7690,8 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ - *is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET); - #if !defined(NO_FILES) - if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) { -- root = conn->dom_ctx->config[WEBSOCKET_ROOT]; -+ roots[0] = conn->dom_ctx->config[WEBSOCKET_ROOT]; -+ roots[1] = conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]; - } - #endif /* !NO_FILES */ - #else /* USE_WEBSOCKET */ -@@ -7702,51 +7708,59 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ - - #if !defined(NO_FILES) - /* Step 5: If there is no root directory, don't look for files. */ -- /* Note that root == NULL is a regular use case here. This occurs, -+ /* Note that roots[0] == NULL is a regular use case here. This occurs, - * if all requests are handled by callbacks, so the WEBSOCKET_ROOT - * config is not required. */ -- if (root == NULL) { -+ if (roots[0] == NULL) { - /* all file related outputs have already been set to 0, just return - */ - return; - } - -- /* Step 6: Determine the local file path from the root path and the -- * request uri. */ -- /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift -- * part of the path one byte on the right. */ -- truncated = 0; -- mg_snprintf( -- conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri); -+ for (int i=0; roots[i] != NULL; i++) -+ { -+ /* Step 6: Determine the local file path from the root path and the -+ * request uri. */ -+ /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift -+ * part of the path one byte on the right. */ -+ truncated = 0; -+ mg_snprintf( -+ conn, &truncated, filename, filename_buf_len - 1, "%s%s", roots[i], uri); - -- if (truncated) { -- goto interpret_cleanup; -- } -+ if (truncated) { -+ goto interpret_cleanup; -+ } - -- /* Step 7: URI rewriting */ -- rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN]; -- while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { -- if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { -- mg_snprintf(conn, -- &truncated, -- filename, -- filename_buf_len - 1, -- "%.*s%s", -- (int)b.len, -- b.ptr, -- uri + match_len); -- break; -+ /* Step 7: URI rewriting */ -+ rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN]; -+ while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { -+ if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { -+ mg_snprintf(conn, -+ &truncated, -+ filename, -+ filename_buf_len - 1, -+ "%.*s%s", -+ (int)b.len, -+ b.ptr, -+ uri + match_len); -+ break; -+ } - } -- } - -- if (truncated) { -- goto interpret_cleanup; -+ if (truncated) { -+ goto interpret_cleanup; -+ } -+ -+ /* Step 8: Check if the file exists at the server */ -+ /* Local file path and name, corresponding to requested URI -+ * is now stored in "filename" variable. */ -+ if (mg_stat(conn, filename, filestat)) { -+ fileExists = 1; -+ break; -+ } - } - -- /* Step 8: Check if the file exists at the server */ -- /* Local file path and name, corresponding to requested URI -- * is now stored in "filename" variable. */ -- if (mg_stat(conn, filename, filestat)) { -+ if (fileExists) { - int uri_len = (int)strlen(uri); - int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/'); - -@@ -11404,6 +11418,9 @@ prepare_cgi_environment(struct mg_connection *conn, - addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); - addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); - addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); -+ if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { -+ addenv(env, "FALLBACK_DOCUMENT_ROOT=%s", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); -+ } - addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version()); - - /* Prepare the environment block */ -diff --git a/src/main.c b/src/main.c -index f056a954..f4d408e2 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -1142,6 +1142,7 @@ sanitize_options(const char *options[] /* server options */, - int ok = 1; - /* Make sure we have absolute paths for files and directories */ - set_absolute_path(options, "document_root", arg0); -+ set_absolute_path(options, "fallback_document_root", arg0); - set_absolute_path(options, "put_delete_auth_file", arg0); - set_absolute_path(options, "cgi_interpreter", arg0); - set_absolute_path(options, "access_log_file", arg0); -@@ -1155,6 +1156,8 @@ sanitize_options(const char *options[] /* server options */, - /* Make extra verification for certain options */ - if (!verify_existence(options, "document_root", 1)) - ok = 0; -+ if (!verify_existence(options, "fallback_document_root", 1)) -+ ok = 0; - if (!verify_existence(options, "cgi_interpreter", 0)) - ok = 0; - if (!verify_existence(options, "ssl_certificate", 0)) -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index d280e897..ec7611c0 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -2935,6 +2935,9 @@ prepare_lua_environment(struct mg_context *ctx, - - if ((conn != NULL) && (conn->dom_ctx != NULL)) { - reg_string(L, "document_root", conn->dom_ctx->config[DOCUMENT_ROOT]); -+ if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { -+ reg_string(L, "fallback_document_root", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); -+ } - reg_string(L, - "auth_domain", - conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); -@@ -2943,6 +2946,11 @@ prepare_lua_environment(struct mg_context *ctx, - reg_string(L, - "websocket_root", - conn->dom_ctx->config[WEBSOCKET_ROOT]); -+ if (conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]) { -+ reg_string(L, -+ "fallback_websocket_root", -+ conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]); -+ } - } else { - reg_string(L, - "websocket_root", -diff --git a/test/page3.ssjs b/test/page3.ssjs -index 71e55e34..24537998 100644 ---- a/test/page3.ssjs -+++ b/test/page3.ssjs -@@ -19,6 +19,7 @@ opts = [ - "extra_mime_types", - "listening_ports", - "document_root", -+"fallback_document_root", - "ssl_certificate", - "num_threads", - "run_as_user", -@@ -32,6 +33,7 @@ opts = [ - "lua_server_page_pattern", - "_experimental_duktape_script_pattern", - "websocket_root", -+"fallback_websocket_root", - "lua_websocket_pattern", - "access_control_allow_origin", - "error_pages", -diff --git a/unittest/private.c b/unittest/private.c -index 8e85d71e..8ea35452 100644 ---- a/unittest/private.c -+++ b/unittest/private.c -@@ -1621,6 +1621,7 @@ START_TEST(test_config_options) - ck_assert_str_eq("extra_mime_types", config_options[EXTRA_MIME_TYPES].name); - ck_assert_str_eq("listening_ports", config_options[LISTENING_PORTS].name); - ck_assert_str_eq("document_root", config_options[DOCUMENT_ROOT].name); -+ ck_assert_str_eq("fallback_document_root", config_options[FALLBACK_DOCUMENT_ROOT].name); - ck_assert_str_eq("ssl_certificate", config_options[SSL_CERTIFICATE].name); - ck_assert_str_eq("ssl_certificate_chain", - config_options[SSL_CERTIFICATE_CHAIN].name); -@@ -1672,6 +1673,7 @@ START_TEST(test_config_options) - #endif - #if defined(USE_WEBSOCKET) - ck_assert_str_eq("websocket_root", config_options[WEBSOCKET_ROOT].name); -+ ck_assert_str_eq("fallback_websocket_root", config_options[FALLBACK_WEBSOCKET_ROOT].name); - #endif - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - ck_assert_str_eq("lua_websocket_pattern", --- -2.34.1 - - -From c7fb4dab4a9f1dca91558b5a0867975daeeefdb1 Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Fri, 5 May 2023 18:46:44 -0700 -Subject: [PATCH 013/173] Speed up mg_stop() by using a socket-pair to cause - all mg_poll() calls to return immediately on server shutdown - ---- - src/civetweb.c | 147 ++++++++++++++++++++++++++++++++++++++++++------- - 1 file changed, 128 insertions(+), 19 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index fdea2db4..7575cba8 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -2438,6 +2438,9 @@ struct mg_context { - int lua_bg_log_available; /* Use Lua background state for access log */ - #endif - -+ int user_shutdown_notification_socket; /* mg_stop() will close this socket... */ -+ int thread_shutdown_notification_socket; /* to cause poll() in all threads to return immediately */ -+ - /* Server nonce */ - pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers, - * ssl_cert_last_mtime, nonce_count, and -@@ -6143,12 +6146,15 @@ push_inner(struct mg_context *ctx, - mg_sleep(5); - } else { - /* For sockets, wait for the socket using poll */ -- struct mg_pollfd pfd[1]; -+ struct mg_pollfd pfd[2]; - int pollres; - - pfd[0].fd = sock; - pfd[0].events = POLLOUT; -- pollres = mg_poll(pfd, 1, (int)(ms_wait), &(ctx->stop_flag)); -+ -+ pfd[1].fd = ctx->thread_shutdown_notification_socket; -+ pfd[1].events = POLLIN; -+ pollres = mg_poll(pfd, 2, (int)(ms_wait), &(ctx->stop_flag)); - if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) { - return -2; - } -@@ -6256,7 +6262,7 @@ pull_inner(FILE *fp, - - #if defined(USE_MBEDTLS) - } else if (conn->ssl != NULL) { -- struct mg_pollfd pfd[1]; -+ struct mg_pollfd pfd[2]; - int to_read; - int pollres; - -@@ -6274,10 +6280,13 @@ pull_inner(FILE *fp, - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; - -+ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[1].events = POLLIN; -+ - to_read = len; - - pollres = mg_poll(pfd, -- 1, -+ 2, - (int)(timeout * 1000.0), - &(conn->phys_ctx->stop_flag)); - -@@ -6312,7 +6321,7 @@ pull_inner(FILE *fp, - #elif !defined(NO_SSL) - } else if (conn->ssl != NULL) { - int ssl_pending; -- struct mg_pollfd pfd[1]; -+ struct mg_pollfd pfd[2]; - int pollres; - - if ((ssl_pending = SSL_pending(conn->ssl)) > 0) { -@@ -6326,8 +6335,11 @@ pull_inner(FILE *fp, - } else { - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; -+ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[1].events = POLLIN; -+ - pollres = mg_poll(pfd, -- 1, -+ 2, - (int)(timeout * 1000.0), - &(conn->phys_ctx->stop_flag)); - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { -@@ -6365,13 +6377,17 @@ pull_inner(FILE *fp, - #endif - - } else { -- struct mg_pollfd pfd[1]; -+ struct mg_pollfd pfd[2]; - int pollres; - - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; -+ -+ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[1].events = POLLIN; -+ - pollres = mg_poll(pfd, -- 1, -+ 2, - (int)(timeout * 1000.0), - &(conn->phys_ctx->stop_flag)); - if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) { -@@ -9526,7 +9542,7 @@ connect_socket( - #endif - - /* Data for poll */ -- struct mg_pollfd pfd[1]; -+ struct mg_pollfd pfd[2]; - int pollres; - int ms_wait = 10000; /* 10 second timeout */ - stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */ -@@ -9538,7 +9554,11 @@ connect_socket( - */ - pfd[0].fd = *sock; - pfd[0].events = POLLOUT; -- pollres = mg_poll(pfd, 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop); -+ -+ pfd[1].fd = ctx ? ctx->thread_shutdown_notification_socket : -1; -+ pfd[1].events = POLLIN; -+ -+ pollres = mg_poll(pfd, ctx ? 2 : 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop); - - if (pollres != 1) { - /* Not connected */ -@@ -16028,9 +16048,13 @@ set_ports_option(struct mg_context *phys_ctx) - continue; - } - -+ /* The +2 below includes the original +1 (for the socket we're about to add), -+ * plus another +1 for the thread_shutdown_notification_socket that we'll -+ * also want to poll() on so that mg_stop() can return quickly -+ */ - if ((pfd = (struct mg_pollfd *) - mg_realloc_ctx(phys_ctx->listening_socket_fds, -- (phys_ctx->num_listening_sockets + 1) -+ (phys_ctx->num_listening_sockets + 2) - * sizeof(phys_ctx->listening_socket_fds[0]), - phys_ctx)) - == NULL) { -@@ -16552,15 +16576,18 @@ sslize(struct mg_connection *conn, - /* Need to retry the function call "later". - * See https://linux.die.net/man/3/ssl_get_error - * This is typical for non-blocking sockets. */ -- struct mg_pollfd pfd; -+ struct mg_pollfd pfd[2]; - int pollres; -- pfd.fd = conn->client.sock; -- pfd.events = ((err == SSL_ERROR_WANT_CONNECT) -- || (err == SSL_ERROR_WANT_WRITE)) -- ? POLLOUT -- : POLLIN; -+ pfd[0].fd = conn->client.sock; -+ pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT) -+ || (err == SSL_ERROR_WANT_WRITE)) -+ ? POLLOUT -+ : POLLIN; -+ -+ pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[1].events = POLLIN; - pollres = -- mg_poll(&pfd, 1, 50, &(conn->phys_ctx->stop_flag)); -+ mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag)); - if (pollres < 0) { - /* Break if error occurred (-1) - * or server shutdown (-2) */ -@@ -20144,8 +20171,14 @@ master_thread_run(struct mg_context *ctx) - pfd[i].events = POLLIN; - } - -+ /* We listen on this socket just so that mg_stop() can cause mg_poll() to return ASAP. -+ * Don't worry, we did allocate an extra slot at the end of listening_socket_fds[] just to hold this -+ */ -+ pfd[ctx->num_listening_sockets].fd = ctx->thread_shutdown_notification_socket; -+ pfd[ctx->num_listening_sockets].events = POLLIN; -+ - if (mg_poll(pfd, -- ctx->num_listening_sockets, -+ ctx->num_listening_sockets+1, // +1 for the thread_shutdown_notification_socket - SOCKET_TIMEOUT_QUANTUM, - &(ctx->stop_flag)) - > 0) { -@@ -20303,6 +20336,14 @@ free_context(struct mg_context *ctx) - (void)pthread_mutex_destroy(&ctx->lua_bg_mutex); - #endif - -+ /* Deallocate shutdown-triggering socket-pair */ -+ if (ctx->user_shutdown_notification_socket >= 0) { -+ closesocket(ctx->user_shutdown_notification_socket); -+ } -+ if (ctx->thread_shutdown_notification_socket >= 0) { -+ closesocket(ctx->thread_shutdown_notification_socket); -+ } -+ - /* Deallocate config parameters */ - for (i = 0; i < NUM_OPTIONS; i++) { - if (ctx->dd.config[i] != NULL) { -@@ -20379,6 +20420,10 @@ mg_stop(struct mg_context *ctx) - /* Set stop flag, so all threads know they have to exit. */ - STOP_FLAG_ASSIGN(&ctx->stop_flag, 1); - -+ /* Closing this socket will cause mg_poll() in all the I/O threads to return immediately */ -+ closesocket(ctx->user_shutdown_notification_socket); -+ ctx->user_shutdown_notification_socket = -1; /* to avoid calling closesocket() again in free_context() */ -+ - /* Join timer thread */ - #if defined(USE_TIMERS) - timers_exit(ctx); -@@ -20476,6 +20521,64 @@ legacy_init(const char **options) - } - } - -+/* we'll assume it's only Windows that doesn't have socketpair() available */ -+#if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32) -+# define HAVE_SOCKETPAIR 1 -+#endif -+ -+static int -+mg_socketpair(int * sockA, -+ int * sockB) -+{ -+ int temp[2] = {-1, -1}; -+ -+ /** Default to unallocated */ -+ *sockA = -1; -+ *sockB = -1; -+ -+#if defined(HAVE_SOCKETPAIR) -+ int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, temp); -+ if (ret == 0) { -+ *sockA = temp[0]; -+ *sockB = temp[1]; -+ } -+ return ret; -+#else -+ /** No socketpair() call is available, so we'll have to roll our own implementation */ -+ int asock = socket(PF_INET, SOCK_STREAM, 0); -+ if (asock >= 0) { -+ struct sockaddr_in addr; -+ struct sockaddr * pa = (struct sockaddr *) &addr; -+ socklen_t addrLen = sizeof(addr); -+ -+ memset(&addr, 0, sizeof(addr)); -+ addr.sin_family = AF_INET; -+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); -+ addr.sin_port = 0; -+ -+ if ((bind(asock, pa, sizeof(addr)) == 0) -+ &&(getsockname(asock, pa, &addrLen) == 0) -+ &&(listen(asock, 1) == 0)) { -+ temp[0] = socket(PF_INET, SOCK_STREAM, 0); -+ if ((temp[0] >= 0)&&(connect(temp[0], pa, sizeof(addr)) == 0)) { -+ temp[1] = accept(asock, pa, &addrLen); -+ if (temp[1] >= 0) { -+ closesocket(asock); -+ *sockA = temp[0]; -+ *sockB = temp[1]; -+ return 0; /* success! */ -+ } -+ } -+ } -+ } -+ -+ /* Cleanup */ -+ if (asock >= 0) closesocket(asock); -+ if (temp[0] >= 0) closesocket(temp[0]); -+ if (temp[1] >= 0) closesocket(temp[1]); -+ return -1; /* fail! */ -+#endif -+} - - CIVETWEB_API struct mg_context * - mg_start2(struct mg_init_data *init, struct mg_error_data *error) -@@ -20559,6 +20662,12 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - #if defined(USE_LUA) - ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr)); - #endif -+ -+ /** mg_stop() will close the user_shutdown_notification_socket, and that will cause poll() -+ * to return immediately in the master-thread, so that mg_stop() can also return immediately. -+ */ -+ ok &= (0 == mg_socketpair(&ctx->user_shutdown_notification_socket, &ctx->thread_shutdown_notification_socket)); -+ - if (!ok) { - unsigned error_id = (unsigned)ERRNO; - const char *err_msg = --- -2.34.1 - - -From 91dae7a6755babd4e102207ba63d7dc92c0df905 Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Fri, 5 May 2023 21:51:01 -0700 -Subject: [PATCH 014/173] Added set_close_on_exec() calls inside - mg_socketpair(), to avoid potential issues with child processes holding the - socketpair open via implicitly duplicated file descriptors - ---- - src/civetweb.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 7575cba8..baaa017f 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -20541,6 +20541,8 @@ mg_socketpair(int * sockA, - if (ret == 0) { - *sockA = temp[0]; - *sockB = temp[1]; -+ set_close_on_exec(*sockA, NULL, NULL); -+ set_close_on_exec(*sockB, NULL, NULL); - } - return ret; - #else -@@ -20566,6 +20568,8 @@ mg_socketpair(int * sockA, - closesocket(asock); - *sockA = temp[0]; - *sockB = temp[1]; -+ set_close_on_exec(*sockA, NULL, NULL); -+ set_close_on_exec(*sockB, NULL, NULL); - return 0; /* success! */ - } - } --- -2.34.1 - - -From 9859676ac9ace1db7e4add044493d306312e7d5b Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Tue, 9 May 2023 18:00:55 -0700 -Subject: [PATCH 015/173] Updated substitute_index_file() to look in the - fallback-root, if no index.html substitution can be found in the regular - document-root - ---- - src/civetweb.c | 40 +++++++++++++++++++++++++++++++++++++++- - 1 file changed, 39 insertions(+), 1 deletion(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 064aca29..6ac2e3b4 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -7590,7 +7590,7 @@ extention_matches_template_text( - * Return 1 if index file has been found, 0 if not found. - * If the file is found, it's stats is returned in stp. */ - static int --substitute_index_file(struct mg_connection *conn, -+substitute_index_file_aux(struct mg_connection *conn, - char *path, - size_t path_len, - struct mg_file_stat *filestat) -@@ -7634,6 +7634,44 @@ substitute_index_file(struct mg_connection *conn, - - return found; - } -+ -+/* Same as above, except if the first try fails and a fallback-root is configured, we'll try there also */ -+static int -+substitute_index_file(struct mg_connection *conn, -+ char *path, -+ size_t path_len, -+ struct mg_file_stat *filestat) -+{ -+ int ret = substitute_index_file_aux(conn, path, path_len, filestat); -+ if (ret == 0) { -+ const char * root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT]; -+ const char * fallback_root_prefix = conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]; -+ if ((root_prefix)&&(fallback_root_prefix)) { -+ const size_t root_prefix_len = strlen(root_prefix); -+ if ((strncmp(path, root_prefix, root_prefix_len) == 0)) { -+ const size_t fallback_root_prefix_len = strlen(fallback_root_prefix); -+ const char * sub_path = path+root_prefix_len; -+ while(*sub_path == '/') sub_path++; -+ const size_t sub_path_len = strlen(sub_path); -+ -+ char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid side effects if we fail */ -+ if (((fallback_root_prefix_len + 1 + sub_path_len + 1) < sizeof(scratch_path))) { -+ /* The concatenations below are all safe because we pre-verified string lengths above */ -+ strcpy(scratch_path, fallback_root_prefix); -+ char * nul = strchr(scratch_path, '\0'); -+ if ((nul > scratch_path)&&(*(nul-1) != '/')) {*nul++ = '/'; *nul = '\0';} -+ strcat(scratch_path, sub_path); -+ if (substitute_index_file_aux(conn, scratch_path, sizeof(scratch_path), filestat)) { -+ mg_strlcpy(path, scratch_path, path_len); -+ return 1; -+ } -+ } -+ } -+ } -+ } -+ return ret; -+} -+ - #endif - - --- -2.34.1 - - -From 220a55ecaeddae5df23872ebf26b20ff9f32c644 Mon Sep 17 00:00:00 2001 -From: Mingjie Shen -Date: Sat, 13 May 2023 16:05:54 -0400 -Subject: [PATCH 016/173] Fix offset used before range check - -These uses of offset 'i_pat' should follow the range check. ---- - src/match.inl | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/match.inl b/src/match.inl -index a5011f57..9c2765d7 100644 ---- a/src/match.inl -+++ b/src/match.inl -@@ -47,8 +47,8 @@ mg_match_impl(const char *pat, - /* Advance as long as there are ? */ - i_pat++; - i_str++; -- } while ((pat[i_pat] == '?') && (str[i_str] != '\0') -- && (str[i_str] != '/') && (i_pat < pat_len)); -+ } while ((i_pat < pat_len) && (pat[i_pat] == '?') -+ && (str[i_str] != '\0') && (str[i_str] != '/')); - - /* If we have a match context, add the substring we just found */ - if (mcx) { -@@ -72,7 +72,7 @@ mg_match_impl(const char *pat, - ptrdiff_t ret; - - i_pat++; -- if ((pat[i_pat] == '*') && (i_pat < pat_len)) { -+ if ((i_pat < pat_len) && (pat[i_pat] == '*')) { - /* Pattern ** matches all */ - i_pat++; - len = strlen(str + i_str); --- -2.34.1 - - -From 4f89995634b9a116857604e32ac71d7070a39dae Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Sat, 13 May 2023 18:40:46 -0700 -Subject: [PATCH 017/173] Added demand-spawning of threads, and - prespawn_threads config argument - ---- - docs/Embedding.md | 11 +++- - docs/UserManual.md | 17 ++++-- - resources/civetweb.conf | 1 + - src/civetweb.c | 125 +++++++++++++++++++++++++++++----------- - test/page3.ssjs | 1 + - unittest/private.c | 1 + - 6 files changed, 114 insertions(+), 42 deletions(-) - -diff --git a/docs/Embedding.md b/docs/Embedding.md -index 0a0fa336..0291ff92 100644 ---- a/docs/Embedding.md -+++ b/docs/Embedding.md -@@ -213,10 +213,15 @@ about web server instance: - - When `mg_start()` returns, all initialization is guaranteed to be complete - (e.g. listening ports are opened, SSL is initialized, etc). `mg_start()` starts --some threads: a master thread, that accepts new connections, and several --worker threads, that process accepted connections. The number of worker threads --is configurable via `num_threads` configuration option. That number puts a -+some threads: a master thread, that accepts new connections, and optionally some -+worker threads, that process accepted connections. The maximum number of worker -+threads is configurable via `num_threads` configuration option. That number puts a - limit on number of simultaneous requests that can be handled by CivetWeb. -+ -+The number of worker threads to be pre-spawned at startup is specified via the -+'prespawn_threads' configuration option; worker threads that are not pre-spawned -+will instead be demand-created the first time they are needed. -+ - If you embed CivetWeb into a program that uses SSL outside CivetWeb as well, - you may need to initialize SSL before calling `mg_start()`, and set the pre- - processor define `SSL_ALREADY_INITIALIZED`. This is not required if SSL is -diff --git a/docs/UserManual.md b/docs/UserManual.md -index 358895f6..e90ceef4 100644 ---- a/docs/UserManual.md -+++ b/docs/UserManual.md -@@ -558,8 +558,8 @@ The configuration value is approximate, the real limit might be a few bytes off. - The minimum is 1024 (1 kB). - - ### num\_threads `50` --Number of worker threads. CivetWeb handles each incoming connection in a --separate thread. Therefore, the value of this option is effectively the number -+Maximum number of worker threads allowed. CivetWeb handles each incoming connection -+in a separate thread. Therefore, the value of this option is effectively the number - of concurrent HTTP connections CivetWeb can handle. - - If there are more simultaneous requests (connection attempts), they are queued. -@@ -572,6 +572,15 @@ In case the clients are web browsers, it is recommended to use `num_threads` of - at least 5, since browsers often establish multiple connections to load a single - web page, including all linked documents (CSS, JavaScript, images, ...). - -+### prespawn\_threads '0' -+Number of worker threads that should be pre-spawned by mg_start(). Defaults to -+0, meaning no worker threads will be pre-spawned at startup; rather, worker threads -+will be spawned when a new connection comes in and there aren't currently any -+idle worker threads available to handle it (if we haven't already reached the -+maximum worker-thread count as specified by num_threads). If this value is -+specified less than zero, or greater than the value of num_threads, it will be -+treated as if it was specified to be equal to the value of num_threads. -+ - ### listen\_backlog `200` - Maximum number of connections waiting to be accepted by the server operating system. - Internally, this parameter is passed to the "listen" socket/system call. -@@ -876,8 +885,8 @@ All port, socket, process and thread specific parameters are per server: - `enable_http2`, `enable_keep_alive`, `enable_websocket_ping_pong`, - `keep_alive_timeout_ms`, `linger_timeout_ms`, `listen_backlog`, - `listening_ports`, `lua_background_script`, `lua_background_script_params`, --`max_request_size`, `num_threads`, `request_timeout_ms`, `run_as_user`, --`tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`. -+`max_request_size`, `num_threads`, 'prespawn_threads', `request_timeout_ms`, -+`run_as_user`, `tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`. - - All other options can be set per domain. In particular - `authentication_domain`, `document_root` and (for HTTPS) `ssl_certificate` -diff --git a/resources/civetweb.conf b/resources/civetweb.conf -index c5675481..293f27b5 100644 ---- a/resources/civetweb.conf -+++ b/resources/civetweb.conf -@@ -26,6 +26,7 @@ listening_ports 8080 - # extra_mime_types - # ssl_certificate - # num_threads 50 -+# prespawn_threads 0 - # run_as_user - # url_rewrite_patterns - # hide_files_patterns -diff --git a/src/civetweb.c b/src/civetweb.c -index 064aca29..88883aee 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -1934,6 +1934,7 @@ enum { - /* Once for each server */ - LISTENING_PORTS, - NUM_THREADS, -+ PRESPAWN_THREADS, - RUN_AS_USER, - CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the - * socket option typedef TCP_NODELAY. */ -@@ -2081,6 +2082,7 @@ static const struct mg_option config_options[] = { - /* Once for each server */ - {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"}, - {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"}, -+ {"prespawn_threads", MG_CONFIG_TYPE_NUMBER, "0"}, - {"run_as_user", MG_CONFIG_TYPE_STRING, NULL}, - {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"}, - {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"}, -@@ -2394,7 +2396,14 @@ struct mg_context { - - pthread_t masterthreadid; /* The master thread ID */ - unsigned int -- cfg_worker_threads; /* The number of configured worker threads. */ -+ cfg_max_worker_threads; /* How many worker-threads we are allowed to create, total */ -+ -+ unsigned int -+ spawned_worker_threads; /* How many worker-threads currently exist (modified by master thread) */ -+ unsigned int -+ idle_worker_thread_count; /* How many worker-threads are currently sitting around with nothing to do */ -+ /* Access to this value MUST be synchronized by thread_mutex */ -+ - pthread_t *worker_threadids; /* The worker thread IDs */ - unsigned long starter_thread_idx; /* thread index which called mg_start */ - -@@ -17997,7 +18006,7 @@ mg_close_connection(struct mg_connection *conn) - * timeouts, we will just wait a few seconds in mg_join_thread. */ - - /* join worker thread */ -- for (i = 0; i < conn->phys_ctx->cfg_worker_threads; i++) { -+ for (i = 0; i < conn->phys_ctx->spawned_worker_threads; i++) { - mg_join_thread(conn->phys_ctx->worker_threadids[i]); - } - } -@@ -19207,7 +19216,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options, - /* Now upgrade to ws/wss client context */ - conn->phys_ctx->user_data = user_data; - conn->phys_ctx->context_type = CONTEXT_WS_CLIENT; -- conn->phys_ctx->cfg_worker_threads = 1; /* one worker thread */ -+ conn->phys_ctx->cfg_max_worker_threads = 1; /* one worker thread */ -+ conn->phys_ctx->spawned_worker_threads = 1; /* one worker thread */ - - /* Start a thread to read the websocket client connection - * This thread will automatically stop when mg_disconnect is -@@ -19216,7 +19226,7 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options, - thread_data, - conn->phys_ctx->worker_threadids) - != 0) { -- conn->phys_ctx->cfg_worker_threads = 0; -+ conn->phys_ctx->spawned_worker_threads = 0; - mg_free(thread_data); - mg_close_connection(conn); - conn = NULL; -@@ -19587,6 +19597,7 @@ process_new_connection(struct mg_connection *conn) - #endif - } - -+static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads); /* forward declaration */ - - #if defined(ALTERNATIVE_QUEUE) - -@@ -19595,8 +19606,10 @@ produce_socket(struct mg_context *ctx, const struct socket *sp) - { - unsigned int i; - -+ (void)mg_start_worker_thread(ctx, 1); /* will start a worker-thread only if there aren't currently any idle worker-threads */ -+ - while (!ctx->stop_flag) { -- for (i = 0; i < ctx->cfg_worker_threads; i++) { -+ for (i = 0; i < ctx->spawned_worker_threads; i++) { - /* find a free worker slot and signal it */ - if (ctx->client_socks[i].in_use == 2) { - (void)pthread_mutex_lock(&ctx->thread_mutex); -@@ -19621,10 +19634,13 @@ produce_socket(struct mg_context *ctx, const struct socket *sp) - - - static int --consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) -+consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented) - { - DEBUG_TRACE("%s", "going idle"); - (void)pthread_mutex_lock(&ctx->thread_mutex); -+ if (counter_was_preincremented == 0) { /* first call only: the master-thread pre-incremented this before he spawned us */ -+ ctx->idle_worker_thread_count++; -+ } - ctx->client_socks[thread_index].in_use = 2; - (void)pthread_mutex_unlock(&ctx->thread_mutex); - -@@ -19641,6 +19657,7 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) - } - return 0; - } -+ ctx->idle_worker_thread_count--; - (void)pthread_mutex_unlock(&ctx->thread_mutex); - if (sp->in_use == 1) { - DEBUG_TRACE("grabbed socket %d, going busy", sp->sock); -@@ -19655,12 +19672,15 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) - - /* Worker threads take accepted socket from the queue */ - static int --consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) -+consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented) - { - (void)thread_index; - -- (void)pthread_mutex_lock(&ctx->thread_mutex); - DEBUG_TRACE("%s", "going idle"); -+ (void)pthread_mutex_lock(&ctx->thread_mutex); -+ if (counter_was_preincremented == 0) { /* first call only: the master-thread pre-incremented this before he spawned us */ -+ ctx->idle_worker_thread_count++; -+ } - - /* If the queue is empty, wait. We're idle at this point. */ - while ((ctx->sq_head == ctx->sq_tail) -@@ -19684,6 +19704,8 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) - } - - (void)pthread_cond_signal(&ctx->sq_empty); -+ -+ ctx->idle_worker_thread_count--; - (void)pthread_mutex_unlock(&ctx->thread_mutex); - - return STOP_FLAG_IS_ZERO(&ctx->stop_flag); -@@ -19730,6 +19752,8 @@ produce_socket(struct mg_context *ctx, const struct socket *sp) - - (void)pthread_cond_signal(&ctx->sq_full); - (void)pthread_mutex_unlock(&ctx->thread_mutex); -+ -+ (void)mg_start_worker_thread(ctx, 1); /* will start a worker-thread only if there aren't currently any idle worker-threads */ - } - #endif /* ALTERNATIVE_QUEUE */ - -@@ -19740,6 +19764,7 @@ worker_thread_run(struct mg_connection *conn) - struct mg_context *ctx = conn->phys_ctx; - int thread_index; - struct mg_workerTLS tls; -+ int first_call_to_consume_socket = 1; - - mg_set_thread_name("worker"); - -@@ -19765,7 +19790,7 @@ worker_thread_run(struct mg_connection *conn) - /* Connection structure has been pre-allocated */ - thread_index = (int)(conn - ctx->worker_connections); - if ((thread_index < 0) -- || ((unsigned)thread_index >= (unsigned)ctx->cfg_worker_threads)) { -+ || ((unsigned)thread_index >= (unsigned)ctx->cfg_max_worker_threads)) { - mg_cry_ctx_internal(ctx, - "Internal error: Invalid worker index %i", - thread_index); -@@ -19806,7 +19831,8 @@ worker_thread_run(struct mg_connection *conn) - /* Call consume_socket() even when ctx->stop_flag > 0, to let it - * signal sq_empty condvar to wake up the master waiting in - * produce_socket() */ -- while (consume_socket(ctx, &conn->client, thread_index)) { -+ while (consume_socket(ctx, &conn->client, thread_index, first_call_to_consume_socket)) { -+ first_call_to_consume_socket = 0; - - /* New connections must start with new protocol negotiation */ - tls.alpn_proto = NULL; -@@ -20188,7 +20214,7 @@ master_thread_run(struct mg_context *ctx) - - /* Wakeup workers that are waiting for connections to handle. */ - #if defined(ALTERNATIVE_QUEUE) -- for (i = 0; i < ctx->cfg_worker_threads; i++) { -+ for (i = 0; i < ctx->spawned_worker_threads; i++) { - event_signal(ctx->client_wait_events[i]); - } - #else -@@ -20198,7 +20224,7 @@ master_thread_run(struct mg_context *ctx) - #endif - - /* Join all worker threads to avoid leaking threads. */ -- workerthreadcount = ctx->cfg_worker_threads; -+ workerthreadcount = ctx->spawned_worker_threads; - for (i = 0; i < workerthreadcount; i++) { - if (ctx->worker_threadids[i] != 0) { - mg_join_thread(ctx->worker_threadids[i]); -@@ -20302,7 +20328,7 @@ free_context(struct mg_context *ctx) - #if defined(ALTERNATIVE_QUEUE) - mg_free(ctx->client_socks); - if (ctx->client_wait_events != NULL) { -- for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) { -+ for (i = 0; (unsigned)i < ctx->spawned_worker_threads; i++) { - event_destroy(ctx->client_wait_events[i]); - } - mg_free(ctx->client_wait_events); -@@ -20494,12 +20520,41 @@ legacy_init(const char **options) - } - - -+static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads) { -+ const unsigned int i = ctx->spawned_worker_threads; -+ if (i >= ctx->cfg_max_worker_threads) { -+ return -1; /* Oops, we hit our worker-thread limit! No more worker threads, ever! */ -+ } -+ -+ (void)pthread_mutex_lock(&ctx->thread_mutex); -+ if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) { -+ (void)pthread_mutex_unlock(&ctx->thread_mutex); -+ return -2; /* There are idle threads available, so no need to spawn a new worker thread now */ -+ } -+ ctx->idle_worker_thread_count++; /* we do this here to avoid a race condition while the thread is starting up */ -+ (void)pthread_mutex_unlock(&ctx->thread_mutex); -+ -+ ctx->worker_connections[i].phys_ctx = ctx; -+ int ret = mg_start_thread_with_id(worker_thread, -+ &ctx->worker_connections[i], -+ &ctx->worker_threadids[i]); -+ if (ret == 0) { -+ ctx->spawned_worker_threads++; /* note that we've filled another slot in the table */ -+ DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads); -+ } else { -+ (void)pthread_mutex_lock(&ctx->thread_mutex); -+ ctx->idle_worker_thread_count--; /* whoops, roll-back on error */ -+ (void)pthread_mutex_unlock(&ctx->thread_mutex); -+ } -+ return ret; -+} -+ - CIVETWEB_API struct mg_context * - mg_start2(struct mg_init_data *init, struct mg_error_data *error) - { - struct mg_context *ctx; - const char *name, *value, *default_value; -- int idx, ok, workerthreadcount; -+ int idx, ok, prespawnthreadcount, workerthreadcount; - unsigned int i; - int itmp; - void (*exit_callback)(const struct mg_context *ctx) = 0; -@@ -20742,7 +20797,12 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - #endif - - /* Worker thread count option */ -- workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]); -+ workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]); -+ prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]); -+ -+ if ((prespawnthreadcount < 0)||(prespawnthreadcount > workerthreadcount)) { -+ prespawnthreadcount = workerthreadcount; /* can't prespawn more than all of them! */ -+ } - - if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) { - if (workerthreadcount <= 0) { -@@ -21007,8 +21067,8 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - return NULL; - } - -- ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount)); -- ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_worker_threads, -+ ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount)); -+ ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(pthread_t), - ctx); - -@@ -21019,7 +21079,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; - error->code_sub = -- (unsigned)ctx->cfg_worker_threads * (unsigned)sizeof(pthread_t); -+ (unsigned)ctx->cfg_max_worker_threads * (unsigned)sizeof(pthread_t); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ - error->text, -@@ -21033,7 +21093,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - return NULL; - } - ctx->worker_connections = -- (struct mg_connection *)mg_calloc_ctx(ctx->cfg_worker_threads, -+ (struct mg_connection *)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(struct mg_connection), - ctx); - if (ctx->worker_connections == NULL) { -@@ -21043,7 +21103,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; -- error->code_sub = (unsigned)ctx->cfg_worker_threads -+ error->code_sub = (unsigned)ctx->cfg_max_worker_threads - * (unsigned)sizeof(struct mg_connection); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ -@@ -21060,7 +21120,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - - #if defined(ALTERNATIVE_QUEUE) - ctx->client_wait_events = -- (void **)mg_calloc_ctx(ctx->cfg_worker_threads, -+ (void **)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(ctx->client_wait_events[0]), - ctx); - if (ctx->client_wait_events == NULL) { -@@ -21070,7 +21130,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; -- error->code_sub = (unsigned)ctx->cfg_worker_threads -+ error->code_sub = (unsigned)ctx->cfg_max_worker_threads - * (unsigned)sizeof(ctx->client_wait_events[0]); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ -@@ -21086,7 +21146,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - } - - ctx->client_socks = -- (struct socket *)mg_calloc_ctx(ctx->cfg_worker_threads, -+ (struct socket *)mg_calloc_ctx(ctx->cfg_max_worker_threads, - sizeof(ctx->client_socks[0]), - ctx); - if (ctx->client_socks == NULL) { -@@ -21097,7 +21157,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - - if (error != NULL) { - error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; -- error->code_sub = (unsigned)ctx->cfg_worker_threads -+ error->code_sub = (unsigned)ctx->cfg_max_worker_threads - * (unsigned)sizeof(ctx->client_socks[0]); - mg_snprintf(NULL, - NULL, /* No truncation check for error buffers */ -@@ -21112,7 +21172,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - return NULL; - } - -- for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) { -+ for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) { - ctx->client_wait_events[i] = event_create(); - if (ctx->client_wait_events[i] == 0) { - const char *err_msg = "Error creating worker event %i"; -@@ -21176,23 +21236,18 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - ctx->context_type = CONTEXT_SERVER; /* server context */ - - /* Start worker threads */ -- for (i = 0; i < ctx->cfg_worker_threads; i++) { -+ for (int i = 0; i < prespawnthreadcount; i++) { - /* worker_thread sets up the other fields */ -- ctx->worker_connections[i].phys_ctx = ctx; -- if (mg_start_thread_with_id(worker_thread, -- &ctx->worker_connections[i], -- &ctx->worker_threadids[i]) -- != 0) { -- -+ if (mg_start_worker_thread(ctx, 0) != 0) { - long error_no = (long)ERRNO; - - /* thread was not created */ -- if (i > 0) { -+ if (ctx->spawned_worker_threads > 0) { - /* If the second, third, ... thread cannot be created, set a - * warning, but keep running. */ - mg_cry_ctx_internal(ctx, - "Cannot start worker thread %i: error %ld", -- i + 1, -+ ctx->spawned_worker_threads + 1, - error_no); - - /* If the server initialization should stop here, all -@@ -22122,7 +22177,7 @@ mg_get_connection_info(const struct mg_context *ctx, - return 0; - } - -- if ((unsigned)idx >= ctx->cfg_worker_threads) { -+ if ((unsigned)idx >= ctx->cfg_max_worker_threads) { - /* Out of range */ - return 0; - } -diff --git a/test/page3.ssjs b/test/page3.ssjs -index 24537998..6e4f62f9 100644 ---- a/test/page3.ssjs -+++ b/test/page3.ssjs -@@ -22,6 +22,7 @@ opts = [ - "fallback_document_root", - "ssl_certificate", - "num_threads", -+"prespawn_threads", - "run_as_user", - "url_rewrite_patterns", - "hide_files_patterns", -diff --git a/unittest/private.c b/unittest/private.c -index 8ea35452..f9d6015c 100644 ---- a/unittest/private.c -+++ b/unittest/private.c -@@ -1626,6 +1626,7 @@ START_TEST(test_config_options) - ck_assert_str_eq("ssl_certificate_chain", - config_options[SSL_CERTIFICATE_CHAIN].name); - ck_assert_str_eq("num_threads", config_options[NUM_THREADS].name); -+ ck_assert_str_eq("prespawn_threads", config_options[PRESPAWN_THREADS].name); - ck_assert_str_eq("run_as_user", config_options[RUN_AS_USER].name); - ck_assert_str_eq("url_rewrite_patterns", - config_options[URL_REWRITE_PATTERN].name); --- -2.34.1 - - -From 3e0761c14ddba5597e8adae08e70ac9ea88c1555 Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Sat, 13 May 2023 18:56:57 -0700 -Subject: [PATCH 018/173] Fixed a compiler warning - ---- - src/civetweb.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 88883aee..fdbb16e3 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -21236,7 +21236,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - ctx->context_type = CONTEXT_SERVER; /* server context */ - - /* Start worker threads */ -- for (int i = 0; i < prespawnthreadcount; i++) { -+ for (i = 0; (int)i < prespawnthreadcount; i++) { - /* worker_thread sets up the other fields */ - if (mg_start_worker_thread(ctx, 0) != 0) { - long error_no = (long)ERRNO; --- -2.34.1 - - -From a588b5ffe7b67a71f7cba4006700b12eaf3af25d Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Sat, 13 May 2023 22:34:43 -0700 -Subject: [PATCH 019/173] Fix mg_start_worker_thread() to take the number of - socket-connections already enqueued into account when deciding whether or not - to spawn a new thread - ---- - src/civetweb.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index fdbb16e3..cb902dba 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -20527,7 +20527,7 @@ static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_th - } - - (void)pthread_mutex_lock(&ctx->thread_mutex); -- if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) { -+ if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > (unsigned)(ctx->sq_head-ctx->sq_tail))) { - (void)pthread_mutex_unlock(&ctx->thread_mutex); - return -2; /* There are idle threads available, so no need to spawn a new worker thread now */ - } --- -2.34.1 - - -From 0891e955d129810dccc7682dd374db09ffffff58 Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Sat, 13 May 2023 22:44:04 -0700 -Subject: [PATCH 020/173] Fix for ALTERNATIVE_QUEUE mode - ---- - src/civetweb.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index cb902dba..5287afdf 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -20527,7 +20527,11 @@ static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_th - } - - (void)pthread_mutex_lock(&ctx->thread_mutex); -+#if defined(ALTERNATIVE_QUEUE) -+ if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) { -+#else - if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > (unsigned)(ctx->sq_head-ctx->sq_tail))) { -+#endif - (void)pthread_mutex_unlock(&ctx->thread_mutex); - return -2; /* There are idle threads available, so no need to spawn a new worker thread now */ - } --- -2.34.1 - - -From c1cf4be2d3a1c118c64a815672e215af0772ffbe Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 14 May 2023 10:59:26 +0200 -Subject: [PATCH 021/173] Use lowercase "windows.h" instead of "Windows.h" - -Fixes #1165 - -While the Microsoft documentation uses "Windows.h" some other source files already use "windows.h". ---- - src/civetweb.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 064aca29..1de1531f 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -525,7 +525,7 @@ mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, - - - #if defined(_WIN32) /* WINDOWS include block */ --#include -+#include - #include /* *alloc( */ - #include /* *alloc( */ - #include /* struct timespec */ --- -2.34.1 - - -From 8db42679a4b52b2ebcc10cf24da3777b369d84e5 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 14 May 2023 11:27:59 +0200 -Subject: [PATCH 022/173] Format code after merge - ---- - src/civetweb.c | 158 ++++++++++++++++++++++++++++----------------- - src/match.inl | 2 +- - src/mod_lua.inl | 8 ++- - unittest/private.c | 6 +- - 4 files changed, 108 insertions(+), 66 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index c82d7371..49d77f8e 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -525,10 +525,10 @@ mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, - - - #if defined(_WIN32) /* WINDOWS include block */ -+#include /* *alloc( */ -+#include /* *alloc( */ -+#include /* struct timespec */ - #include --#include /* *alloc( */ --#include /* *alloc( */ --#include /* struct timespec */ - #include /* DTL add for SO_EXCLUSIVE */ - #include - -@@ -2442,8 +2442,10 @@ struct mg_context { - int lua_bg_log_available; /* Use Lua background state for access log */ - #endif - -- int user_shutdown_notification_socket; /* mg_stop() will close this socket... */ -- int thread_shutdown_notification_socket; /* to cause poll() in all threads to return immediately */ -+ int user_shutdown_notification_socket; /* mg_stop() will close this -+ socket... */ -+ int thread_shutdown_notification_socket; /* to cause poll() in all threads -+ to return immediately */ - - /* Server nonce */ - pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers, -@@ -7607,9 +7609,9 @@ extention_matches_template_text( - * If the file is found, it's stats is returned in stp. */ - static int - substitute_index_file_aux(struct mg_connection *conn, -- char *path, -- size_t path_len, -- struct mg_file_stat *filestat) -+ char *path, -+ size_t path_len, -+ struct mg_file_stat *filestat) - { - const char *list = conn->dom_ctx->config[INDEX_FILES]; - struct vec filename_vec; -@@ -7651,7 +7653,8 @@ substitute_index_file_aux(struct mg_connection *conn, - return found; - } - --/* Same as above, except if the first try fails and a fallback-root is configured, we'll try there also */ -+/* Same as above, except if the first try fails and a fallback-root is -+ * configured, we'll try there also */ - static int - substitute_index_file(struct mg_connection *conn, - char *path, -@@ -7660,24 +7663,36 @@ substitute_index_file(struct mg_connection *conn, - { - int ret = substitute_index_file_aux(conn, path, path_len, filestat); - if (ret == 0) { -- const char * root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT]; -- const char * fallback_root_prefix = conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]; -- if ((root_prefix)&&(fallback_root_prefix)) { -+ const char *root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT]; -+ const char *fallback_root_prefix = -+ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]; -+ if ((root_prefix) && (fallback_root_prefix)) { - const size_t root_prefix_len = strlen(root_prefix); - if ((strncmp(path, root_prefix, root_prefix_len) == 0)) { -- const size_t fallback_root_prefix_len = strlen(fallback_root_prefix); -- const char * sub_path = path+root_prefix_len; -- while(*sub_path == '/') sub_path++; -+ const size_t fallback_root_prefix_len = -+ strlen(fallback_root_prefix); -+ const char *sub_path = path + root_prefix_len; -+ while (*sub_path == '/') -+ sub_path++; - const size_t sub_path_len = strlen(sub_path); - -- char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid side effects if we fail */ -- if (((fallback_root_prefix_len + 1 + sub_path_len + 1) < sizeof(scratch_path))) { -- /* The concatenations below are all safe because we pre-verified string lengths above */ -+ char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid -+ side effects if we fail */ -+ if (((fallback_root_prefix_len + 1 + sub_path_len + 1) -+ < sizeof(scratch_path))) { -+ /* The concatenations below are all safe because we -+ * pre-verified string lengths above */ - strcpy(scratch_path, fallback_root_prefix); -- char * nul = strchr(scratch_path, '\0'); -- if ((nul > scratch_path)&&(*(nul-1) != '/')) {*nul++ = '/'; *nul = '\0';} -+ char *nul = strchr(scratch_path, '\0'); -+ if ((nul > scratch_path) && (*(nul - 1) != '/')) { -+ *nul++ = '/'; -+ *nul = '\0'; -+ } - strcat(scratch_path, sub_path); -- if (substitute_index_file_aux(conn, scratch_path, sizeof(scratch_path), filestat)) { -+ if (substitute_index_file_aux(conn, -+ scratch_path, -+ sizeof(scratch_path), -+ filestat)) { - mg_strlcpy(path, scratch_path, path_len); - return 1; - } -@@ -7708,7 +7723,9 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ - - #if !defined(NO_FILES) - const char *uri = conn->request_info.local_uri; -- const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT], conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT], NULL}; -+ const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT], -+ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT], -+ NULL}; - int fileExists = 0; - const char *rewrite; - struct vec a, b; -@@ -7771,15 +7788,19 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */ - return; - } - -- for (int i=0; roots[i] != NULL; i++) -- { -+ for (int i = 0; roots[i] != NULL; i++) { - /* Step 6: Determine the local file path from the root path and the - * request uri. */ - /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift - * part of the path one byte on the right. */ - truncated = 0; -- mg_snprintf( -- conn, &truncated, filename, filename_buf_len - 1, "%s%s", roots[i], uri); -+ mg_snprintf(conn, -+ &truncated, -+ filename, -+ filename_buf_len - 1, -+ "%s%s", -+ roots[i], -+ uri); - - if (truncated) { - goto interpret_cleanup; -@@ -9610,7 +9631,10 @@ connect_socket( - pfd[1].fd = ctx ? ctx->thread_shutdown_notification_socket : -1; - pfd[1].events = POLLIN; - -- pollres = mg_poll(pfd, ctx ? 2 : 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop); -+ pollres = mg_poll(pfd, -+ ctx ? 2 : 1, -+ ms_wait, -+ ctx ? &(ctx->stop_flag) : &nonstop); - - if (pollres != 1) { - /* Not connected */ -@@ -11477,7 +11501,9 @@ prepare_cgi_environment(struct mg_connection *conn, - addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); - addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]); - if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { -- addenv(env, "FALLBACK_DOCUMENT_ROOT=%s", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); -+ addenv(env, -+ "FALLBACK_DOCUMENT_ROOT=%s", -+ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); - } - addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version()); - -@@ -16103,9 +16129,10 @@ set_ports_option(struct mg_context *phys_ctx) - continue; - } - -- /* The +2 below includes the original +1 (for the socket we're about to add), -- * plus another +1 for the thread_shutdown_notification_socket that we'll -- * also want to poll() on so that mg_stop() can return quickly -+ /* The +2 below includes the original +1 (for the socket we're about to -+ * add), plus another +1 for the thread_shutdown_notification_socket -+ * that we'll also want to poll() on so that mg_stop() can return -+ * quickly - */ - if ((pfd = (struct mg_pollfd *) - mg_realloc_ctx(phys_ctx->listening_socket_fds, -@@ -16635,14 +16662,14 @@ sslize(struct mg_connection *conn, - int pollres; - pfd[0].fd = conn->client.sock; - pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT) -- || (err == SSL_ERROR_WANT_WRITE)) -- ? POLLOUT -- : POLLIN; -+ || (err == SSL_ERROR_WANT_WRITE)) -+ ? POLLOUT -+ : POLLIN; - -- pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[1].fd = -+ conn->phys_ctx->thread_shutdown_notification_socket; - pfd[1].events = POLLIN; -- pollres = -- mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag)); -+ pollres = mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag)); - if (pollres < 0) { - /* Break if error occurred (-1) - * or server shutdown (-2) */ -@@ -20226,14 +20253,17 @@ master_thread_run(struct mg_context *ctx) - pfd[i].events = POLLIN; - } - -- /* We listen on this socket just so that mg_stop() can cause mg_poll() to return ASAP. -- * Don't worry, we did allocate an extra slot at the end of listening_socket_fds[] just to hold this -+ /* We listen on this socket just so that mg_stop() can cause mg_poll() -+ * to return ASAP. Don't worry, we did allocate an extra slot at the end -+ * of listening_socket_fds[] just to hold this - */ -- pfd[ctx->num_listening_sockets].fd = ctx->thread_shutdown_notification_socket; -+ pfd[ctx->num_listening_sockets].fd = -+ ctx->thread_shutdown_notification_socket; - pfd[ctx->num_listening_sockets].events = POLLIN; - - if (mg_poll(pfd, -- ctx->num_listening_sockets+1, // +1 for the thread_shutdown_notification_socket -+ ctx->num_listening_sockets -+ + 1, // +1 for the thread_shutdown_notification_socket - SOCKET_TIMEOUT_QUANTUM, - &(ctx->stop_flag)) - > 0) { -@@ -20475,9 +20505,11 @@ mg_stop(struct mg_context *ctx) - /* Set stop flag, so all threads know they have to exit. */ - STOP_FLAG_ASSIGN(&ctx->stop_flag, 1); - -- /* Closing this socket will cause mg_poll() in all the I/O threads to return immediately */ -+ /* Closing this socket will cause mg_poll() in all the I/O threads to return -+ * immediately */ - closesocket(ctx->user_shutdown_notification_socket); -- ctx->user_shutdown_notification_socket = -1; /* to avoid calling closesocket() again in free_context() */ -+ ctx->user_shutdown_notification_socket = -+ -1; /* to avoid calling closesocket() again in free_context() */ - - /* Join timer thread */ - #if defined(USE_TIMERS) -@@ -20578,12 +20610,11 @@ legacy_init(const char **options) - - /* we'll assume it's only Windows that doesn't have socketpair() available */ - #if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32) --# define HAVE_SOCKETPAIR 1 -+#define HAVE_SOCKETPAIR 1 - #endif - - static int --mg_socketpair(int * sockA, -- int * sockB) -+mg_socketpair(int *sockA, int *sockB) - { - int temp[2] = {-1, -1}; - -@@ -20601,11 +20632,12 @@ mg_socketpair(int * sockA, - } - return ret; - #else -- /** No socketpair() call is available, so we'll have to roll our own implementation */ -+ /** No socketpair() call is available, so we'll have to roll our own -+ * implementation */ - int asock = socket(PF_INET, SOCK_STREAM, 0); - if (asock >= 0) { - struct sockaddr_in addr; -- struct sockaddr * pa = (struct sockaddr *) &addr; -+ struct sockaddr *pa = (struct sockaddr *)&addr; - socklen_t addrLen = sizeof(addr); - - memset(&addr, 0, sizeof(addr)); -@@ -20614,10 +20646,10 @@ mg_socketpair(int * sockA, - addr.sin_port = 0; - - if ((bind(asock, pa, sizeof(addr)) == 0) -- &&(getsockname(asock, pa, &addrLen) == 0) -- &&(listen(asock, 1) == 0)) { -+ && (getsockname(asock, pa, &addrLen) == 0) -+ && (listen(asock, 1) == 0)) { - temp[0] = socket(PF_INET, SOCK_STREAM, 0); -- if ((temp[0] >= 0)&&(connect(temp[0], pa, sizeof(addr)) == 0)) { -+ if ((temp[0] >= 0) && (connect(temp[0], pa, sizeof(addr)) == 0)) { - temp[1] = accept(asock, pa, &addrLen); - if (temp[1] >= 0) { - closesocket(asock); -@@ -20625,17 +20657,20 @@ mg_socketpair(int * sockA, - *sockB = temp[1]; - set_close_on_exec(*sockA, NULL, NULL); - set_close_on_exec(*sockB, NULL, NULL); -- return 0; /* success! */ -+ return 0; /* success! */ - } - } - } - } - - /* Cleanup */ -- if (asock >= 0) closesocket(asock); -- if (temp[0] >= 0) closesocket(temp[0]); -- if (temp[1] >= 0) closesocket(temp[1]); -- return -1; /* fail! */ -+ if (asock >= 0) -+ closesocket(asock); -+ if (temp[0] >= 0) -+ closesocket(temp[0]); -+ if (temp[1] >= 0) -+ closesocket(temp[1]); -+ return -1; /* fail! */ - #endif - } - -@@ -20722,10 +20757,13 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr)); - #endif - -- /** mg_stop() will close the user_shutdown_notification_socket, and that will cause poll() -- * to return immediately in the master-thread, so that mg_stop() can also return immediately. -- */ -- ok &= (0 == mg_socketpair(&ctx->user_shutdown_notification_socket, &ctx->thread_shutdown_notification_socket)); -+ /** mg_stop() will close the user_shutdown_notification_socket, and that -+ * will cause poll() to return immediately in the master-thread, so that -+ * mg_stop() can also return immediately. -+ */ -+ ok &= (0 -+ == mg_socketpair(&ctx->user_shutdown_notification_socket, -+ &ctx->thread_shutdown_notification_socket)); - - if (!ok) { - unsigned error_id = (unsigned)ERRNO; -diff --git a/src/match.inl b/src/match.inl -index 9c2765d7..34ee00ef 100644 ---- a/src/match.inl -+++ b/src/match.inl -@@ -48,7 +48,7 @@ mg_match_impl(const char *pat, - i_pat++; - i_str++; - } while ((i_pat < pat_len) && (pat[i_pat] == '?') -- && (str[i_str] != '\0') && (str[i_str] != '/')); -+ && (str[i_str] != '\0') && (str[i_str] != '/')); - - /* If we have a match context, add the substring we just found */ - if (mcx) { -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index ec7611c0..f1070d58 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -2936,7 +2936,9 @@ prepare_lua_environment(struct mg_context *ctx, - if ((conn != NULL) && (conn->dom_ctx != NULL)) { - reg_string(L, "document_root", conn->dom_ctx->config[DOCUMENT_ROOT]); - if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { -- reg_string(L, "fallback_document_root", conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); -+ reg_string(L, -+ "fallback_document_root", -+ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); - } - reg_string(L, - "auth_domain", -@@ -2948,8 +2950,8 @@ prepare_lua_environment(struct mg_context *ctx, - conn->dom_ctx->config[WEBSOCKET_ROOT]); - if (conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]) { - reg_string(L, -- "fallback_websocket_root", -- conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]); -+ "fallback_websocket_root", -+ conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]); - } - } else { - reg_string(L, -diff --git a/unittest/private.c b/unittest/private.c -index 8ea35452..9af3939b 100644 ---- a/unittest/private.c -+++ b/unittest/private.c -@@ -1621,7 +1621,8 @@ START_TEST(test_config_options) - ck_assert_str_eq("extra_mime_types", config_options[EXTRA_MIME_TYPES].name); - ck_assert_str_eq("listening_ports", config_options[LISTENING_PORTS].name); - ck_assert_str_eq("document_root", config_options[DOCUMENT_ROOT].name); -- ck_assert_str_eq("fallback_document_root", config_options[FALLBACK_DOCUMENT_ROOT].name); -+ ck_assert_str_eq("fallback_document_root", -+ config_options[FALLBACK_DOCUMENT_ROOT].name); - ck_assert_str_eq("ssl_certificate", config_options[SSL_CERTIFICATE].name); - ck_assert_str_eq("ssl_certificate_chain", - config_options[SSL_CERTIFICATE_CHAIN].name); -@@ -1673,7 +1674,8 @@ START_TEST(test_config_options) - #endif - #if defined(USE_WEBSOCKET) - ck_assert_str_eq("websocket_root", config_options[WEBSOCKET_ROOT].name); -- ck_assert_str_eq("fallback_websocket_root", config_options[FALLBACK_WEBSOCKET_ROOT].name); -+ ck_assert_str_eq("fallback_websocket_root", -+ config_options[FALLBACK_WEBSOCKET_ROOT].name); - #endif - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - ck_assert_str_eq("lua_websocket_pattern", --- -2.34.1 - - -From de57e79919920b1027a3c9dd650ba3f042d9087e Mon Sep 17 00:00:00 2001 -From: Sergey Linev -Date: Fri, 26 May 2023 08:50:24 +0200 -Subject: [PATCH 023/173] Add optional support of - Access-Control-Allow-Credentials header - -Can be set with Access-Control-Allow-Origin header ---- - src/civetweb.c | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 60c99fa4..cc68b402 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -2059,6 +2059,7 @@ enum { - ACCESS_CONTROL_ALLOW_ORIGIN, - ACCESS_CONTROL_ALLOW_METHODS, - ACCESS_CONTROL_ALLOW_HEADERS, -+ ACCESS_CONTROL_ALLOW_CREDENTIALS, - ERROR_PAGES, - #if !defined(NO_CACHING) - STATIC_FILE_MAX_AGE, -@@ -2222,6 +2223,7 @@ static const struct mg_option config_options[] = { - {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, -+ {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""}, - {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL}, - #if !defined(NO_CACHING) - {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"}, -@@ -4195,6 +4197,18 @@ send_cors_header(struct mg_connection *conn) - cors_orig_cfg, - -1); - } -+ -+ const char *cors_cred_cfg = -+ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; -+ if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) { -+ /* Cross-origin resource sharing (CORS), see -+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials */ -+ mg_response_header_add(conn, -+ "Access-Control-Allow-Credentials", -+ cors_cred_cfg, -+ -1); -+ } -+ - } - - --- -2.34.1 - - -From 4523dd5bae7101f02ac5bdc883c44a7815ca4880 Mon Sep 17 00:00:00 2001 -From: Sergey Linev -Date: Fri, 26 May 2023 10:40:03 +0200 -Subject: [PATCH 024/173] Also send allowed headers and methods in - send_cors_header - ---- - src/civetweb.c | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index cc68b402..901c5419 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -4209,6 +4209,24 @@ send_cors_header(struct mg_connection *conn) - -1); - } - -+ const char *cors_hdr_cfg = -+ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS]; -+ if (cors_hdr_cfg && *cors_hdr_cfg) { -+ mg_response_header_add(conn, -+ "Access-Control-Allow-Headers", -+ cors_hdr_cfg, -+ -1); -+ } -+ -+ const char *cors_meth_cfg = -+ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; -+ if (cors_meth_cfg && *cors_meth_cfg) { -+ mg_response_header_add(conn, -+ "Access-Control-Allow-Methods", -+ cors_meth_cfg, -+ -1); -+ } -+ - } - - --- -2.34.1 - - -From 4442d645a0c14a3c392aeeff19364d4200868a35 Mon Sep 17 00:00:00 2001 -From: Sergey Linev -Date: Fri, 26 May 2023 10:41:56 +0200 -Subject: [PATCH 025/173] Add Access-Control-Allow-Credentials header in normal - replay - -Only when really configured ---- - src/civetweb.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 901c5419..58032d6d 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -15024,6 +15024,13 @@ handle_request(struct mg_connection *conn) - ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), - suggest_connection_header(conn)); - -+ const char *cors_cred_cfg = -+ conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; -+ if (cors_cred_cfg && *cors_cred_cfg) -+ mg_printf(conn, -+ "Access-Control-Allow-Credentials: %s\r\n", -+ cors_cred_cfg); -+ - if (cors_acrh != NULL) { - /* CORS request is asking for additional headers */ - const char *cors_hdr_cfg = --- -2.34.1 - - -From d04032cc0ba695ac39c2f5517e8961251426a77a Mon Sep 17 00:00:00 2001 -From: Sergey Linev -Date: Fri, 26 May 2023 10:54:54 +0200 -Subject: [PATCH 026/173] Add CORS Access-Control-Expose-Headers - -Can be used together with Access-Control-Allow-Credentials -Allows to access 'unsafe' headers from http reply by client -See: - -https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers ---- - src/civetweb.c | 24 ++++++++++++++++++++++-- - 1 file changed, 22 insertions(+), 2 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 58032d6d..306f2e37 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -2059,6 +2059,7 @@ enum { - ACCESS_CONTROL_ALLOW_ORIGIN, - ACCESS_CONTROL_ALLOW_METHODS, - ACCESS_CONTROL_ALLOW_HEADERS, -+ ACCESS_CONTROL_EXPOSE_HEADERS, - ACCESS_CONTROL_ALLOW_CREDENTIALS, - ERROR_PAGES, - #if !defined(NO_CACHING) -@@ -2223,6 +2224,7 @@ static const struct mg_option config_options[] = { - {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, -+ {"access_control_expose_headers", MG_CONFIG_TYPE_STRING, ""}, - {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""}, - {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL}, - #if !defined(NO_CACHING) -@@ -4218,6 +4220,15 @@ send_cors_header(struct mg_connection *conn) - -1); - } - -+ const char *cors_exphdr_cfg = -+ conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; -+ if (cors_exphdr_cfg && *cors_exphdr_cfg) { -+ mg_response_header_add(conn, -+ "Access-Control-Expose-Headers", -+ cors_exphdr_cfg, -+ -1); -+ } -+ - const char *cors_meth_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; - if (cors_meth_cfg && *cors_meth_cfg) { -@@ -15026,12 +15037,21 @@ handle_request(struct mg_connection *conn) - - const char *cors_cred_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS]; -- if (cors_cred_cfg && *cors_cred_cfg) -+ if (cors_cred_cfg && *cors_cred_cfg) { - mg_printf(conn, - "Access-Control-Allow-Credentials: %s\r\n", - cors_cred_cfg); -+ } -+ -+ const char *cors_exphdr_cfg = -+ conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; -+ if (cors_exphdr_cfg && *cors_exphdr_cfg) { -+ mg_printf(conn, -+ "Access-Control-Expose-Headers: %s\r\n", -+ cors_exphdr_cfg); -+ } - -- if (cors_acrh != NULL) { -+ if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) { - /* CORS request is asking for additional headers */ - const char *cors_hdr_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS]; --- -2.34.1 - - -From d7ae5df016e227ec462cf166e28edf6857d166ad Mon Sep 17 00:00:00 2001 -From: liangmingyuan -Date: Mon, 14 Aug 2023 09:25:50 +0800 -Subject: [PATCH 027/173] Fix the wrong report of http header transferring - timeout. - -For now, the connection is not closed at first keep_alive_timeout_ms -interval, if the whole header can not be read in the second interval -at one time, then return failed. -However, the http header can be transferred in two packages. -Especially in https, there will be one byte exchanged before the -real data transferring. This can lead to wrong header transferred -timeout when two request are sended more than a keep_alive_timeout_ms -interval. ---- - src/civetweb.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 306f2e37..afb03295 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -11328,11 +11328,11 @@ read_message(FILE *fp, - request_len = get_http_header_len(buf, *nread); - } - -- if ((request_len == 0) && (request_timeout >= 0)) { -+ if ((n <= 0) && (request_timeout >= 0)) { - if (mg_difftimespec(&last_action_time, &(conn->req_time)) - > request_timeout) { - /* Timeout */ -- return -1; -+ return -3; - } - } - } -@@ -18816,7 +18816,7 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) - ebuf, - ebuf_len, - "%s", -- "Malformed message"); -+ conn->request_len == -3 ? "Request timeout" : "Malformed message"); - *err = 400; - } else { - /* Server did not recv anything -> just close the connection */ --- -2.34.1 - - -From 2624754dc09d75ee3764964911019d44e5c63f27 Mon Sep 17 00:00:00 2001 -From: Jeremy Friesner -Date: Thu, 17 Aug 2023 14:35:11 -0700 -Subject: [PATCH 028/173] Added optional tag for ports in civetweb - ---- - src/civetweb.c | 53 +++++++++++++++++++++++++++++++++++++++----------- - 1 file changed, 42 insertions(+), 11 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 306f2e37..c7f28117 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -1921,6 +1921,7 @@ struct socket { - unsigned char is_ssl; /* Is port SSL-ed */ - unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL - * port */ -+ unsigned char is_optional; /* Shouldn't cause us to exit if we can't bind to it */ - unsigned char in_use; /* 0: invalid, 1: valid, 2: free */ - }; - -@@ -15689,7 +15690,7 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version) - unsigned int a, b, c, d; - unsigned port; - unsigned long portUL; -- int ch, len; -+ int len; - const char *cb; - char *endptr; - #if defined(USE_IPV6) -@@ -15842,14 +15843,31 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version) - } - - /* sscanf and the option splitting code ensure the following condition -- * Make sure the port is valid and vector ends with the port, 's' or 'r' */ -- if ((len > 0) && is_valid_port(port) -- && (((size_t)len == vec->len) || (((size_t)len + 1) == vec->len))) { -- /* Next character after the port number */ -- ch = ((size_t)len < vec->len) ? vec->ptr[len] : '\0'; -- so->is_ssl = (ch == 's'); -- so->ssl_redir = (ch == 'r'); -- if ((ch == '\0') || (ch == 's') || (ch == 'r')) { -+ * Make sure the port is valid and vector ends with the port, 'o', 's', or 'r' */ -+ if ((len > 0) && (is_valid_port(port))) { -+ int bad_suffix = 0; -+ -+ /* Parse any suffix character(s) after the port number */ -+ for (size_t i=len; ilen; i++) -+ { -+ unsigned char * opt = NULL; -+ switch(vec->ptr[i]) -+ { -+ case 'o': opt = &so->is_optional; break; -+ case 'r': opt = &so->ssl_redir; break; -+ case 's': opt = &so->is_ssl; break; -+ default: /* empty */ break; -+ } -+ -+ if ((opt)&&(*opt == 0)) *opt = 1; -+ else -+ { -+ bad_suffix = 1; -+ break; -+ } -+ } -+ -+ if ((bad_suffix == 0)&&((so->is_ssl == 0)||(so->ssl_redir == 0))) { - return 1; - } - } -@@ -15904,8 +15922,11 @@ is_ssl_port_used(const char *ports) - char prevIsNumber = 0; - - for (i = 0; i < portslen; i++) { -- if (prevIsNumber && (ports[i] == 's' || ports[i] == 'r')) { -- return 1; -+ if (prevIsNumber) { -+ int suffixCharIdx = (ports[i] == 'o') ? (i+1) : i; /* allow "os" and "or" suffixes */ -+ if (ports[suffixCharIdx] == 's' || ports[suffixCharIdx] == 'r') { -+ return 1; -+ } - } - if (ports[i] >= '0' && ports[i] <= '9') { - prevIsNumber = 1; -@@ -16088,6 +16109,9 @@ set_ports_option(struct mg_context *phys_ctx) - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; -+ if (so.is_optional) { -+ portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */ -+ } - continue; - } - } -@@ -16104,6 +16128,9 @@ set_ports_option(struct mg_context *phys_ctx) - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; -+ if (so.is_optional) { -+ portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */ -+ } - continue; - } - } -@@ -16120,6 +16147,9 @@ set_ports_option(struct mg_context *phys_ctx) - strerror(errno)); - closesocket(so.sock); - so.sock = INVALID_SOCKET; -+ if (so.is_optional) { -+ portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */ -+ } - continue; - } - } -@@ -20190,6 +20220,7 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx) - set_close_on_exec(so.sock, NULL, ctx); - so.is_ssl = listener->is_ssl; - so.ssl_redir = listener->ssl_redir; -+ so.is_optional = listener->is_optional; - if (getsockname(so.sock, &so.lsa.sa, &len) != 0) { - mg_cry_ctx_internal(ctx, - "%s: getsockname() failed: %s", --- -2.34.1 - - -From ebd2f1f99a45468d27c1557dfa757b778a874ea3 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Tue, 22 Aug 2023 16:10:14 +0300 -Subject: [PATCH 029/173] Fix GCC compilation - -On GNUC use the GCC extension "##__VA_ARGS__" ---- - src/civetweb.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 306f2e37..964c3d2f 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -230,8 +230,13 @@ static void DEBUG_TRACE_FUNC(const char *func, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(3, 4); - -+#if defined(__GNUC__) -+#define DEBUG_TRACE(fmt, ...) \ -+ DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, ##__VA_ARGS__) -+#else - #define DEBUG_TRACE(fmt, ...) \ - DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) -+#endif - - #define NEED_DEBUG_TRACE_FUNC - #if !defined(DEBUG_TRACE_STREAM) --- -2.34.1 - - -From 3549bfbbf198d45aa7e8ba65c52b311ef16c8f23 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Tue, 22 Aug 2023 16:23:35 +0300 -Subject: [PATCH 030/173] OpenSSL 3 support - -add flag to select OpenSSL 3 at build time ---- - .travis.yml | 2 ++ - CMakeLists.txt | 2 +- - Makefile | 3 +++ - appveyor.yml | 3 +++ - docs/Building.md | 2 +- - 5 files changed, 10 insertions(+), 2 deletions(-) - -diff --git a/.travis.yml b/.travis.yml -index 8dabd5b4..077fbd89 100644 ---- a/.travis.yml -+++ b/.travis.yml -@@ -82,6 +82,7 @@ before_script: - -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} - -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} - -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} - -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} - -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} - -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} -@@ -107,6 +108,7 @@ before_script: - -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} - -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} - -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} - -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} - -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} - -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} -diff --git a/CMakeLists.txt b/CMakeLists.txt -index c5368c08..7a48123e 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -227,7 +227,7 @@ message(STATUS "Compile for OpenSSL 1.0 API - ${CIVETWEB_SSL_OPENSSL_API_1_0}") - option(CIVETWEB_SSL_OPENSSL_API_1_1 "Use the OpenSSL 1.1 API" ON) - message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}") - --# OpenSSL 1.1 API -+# OpenSSL 3.0 API - option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF) - message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}") - -diff --git a/Makefile b/Makefile -index 31954e47..076d8739 100644 ---- a/Makefile -+++ b/Makefile -@@ -100,6 +100,8 @@ else ifdef WITH_OPENSSL_API_1_0 - CFLAGS += -DOPENSSL_API_1_0 - else ifdef WITH_OPENSSL_API_1_1 - CFLAGS += -DOPENSSL_API_1_1 -+else ifdef WITH_OPENSSL_API_3_0 -+ CFLAGS += -DOPENSSL_API_3_0 - else - #Use OpenSSL 1.1 API version as default - CFLAGS += -DOPENSSL_API_1_1 -@@ -298,6 +300,7 @@ help: - @echo " WITH_MBEDTLS=1 build with mbedTLS support." - @echo " WITH_OPENSSL_API_1_0=1 build with OpenSSL 1.0.x support." - @echo " WITH_OPENSSL_API_1_1=1 build with OpenSSL 1.1.x support." -+ @echo " WITH_OPENSSL_API_3_0=1 build with OpenSSL 3.0.x support." - @echo " NO_SSL=1 build without SSL support. Build will not need libcrypto/libssl." - @echo " NO_CGI=1 build without CGI support." - @echo " NO_CACHING=1 disable caching. Send no-cache/no-store headers." -diff --git a/appveyor.yml b/appveyor.yml -index 41cde75f..6008c5ee 100644 ---- a/appveyor.yml -+++ b/appveyor.yml -@@ -440,6 +440,7 @@ before_build: - -DCIVETWEB_CXX_STANDARD=%cxx_standard% - -DCIVETWEB_SSL_OPENSSL_API_1_0=NO - -DCIVETWEB_SSL_OPENSSL_API_1_1=YES -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO - "%source_path%" - - cmake - -G "%generator%" %arch_arg% -@@ -461,6 +462,8 @@ before_build: - -DCIVETWEB_CXX_STANDARD=%cxx_standard% - -DCIVETWEB_SSL_OPENSSL_API_1_0=NO - -DCIVETWEB_SSL_OPENSSL_API_1_1=YES -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO -+ - "%source_path%" - - powershell Push-AppveyorArtifact CMakeCache.txt - - cd "%source_path%" -diff --git a/docs/Building.md b/docs/Building.md -index ed30bd77..c6f92add 100644 ---- a/docs/Building.md -+++ b/docs/Building.md -@@ -179,7 +179,7 @@ make build COPT="-DNDEBUG -DNO_CGI" - | `SSL_ALREADY_INITIALIZED` | do not initialize libcrypto | - | `OPENSSL_API_1_0` | Use OpenSSL V1.0.x interface | - | `OPENSSL_API_1_1` | Use OpenSSL V1.1.x interface | --| `OPENSSL_API_3_0` | Use OpenSSL V3.x interface | -+| `OPENSSL_API_3_0` | Use OpenSSL V3.0.x interface | - | `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_*) | - | | | - | `BUILD_DATE` | define as a string to be used as build id instead of __DATE__ | --- -2.34.1 - - -From 633bb32c4aea7cf3843e2bac40e6d889f92116c5 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Wed, 23 Aug 2023 12:42:35 +0300 -Subject: [PATCH 031/173] more conditional compilation - -standard manner using __VA_OPT__ ---- - src/civetweb.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 964c3d2f..9adededb 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -230,7 +230,10 @@ static void DEBUG_TRACE_FUNC(const char *func, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(3, 4); - --#if defined(__GNUC__) -+#if defined(__cplusplus) && (__cplusplus >= 202002L) -+#define DEBUG_TRACE(fmt, ...) \ -+ DEBUG_TRACE_FUNC(__func__, __LINE__, fmt __VA_OPT__(, ) __VA_ARGS__) -+#elif defined(__GNUC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) - #define DEBUG_TRACE(fmt, ...) \ - DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, ##__VA_ARGS__) - #else --- -2.34.1 - - -From eeddc01ac226b0720a9bbefa6b664a5d4ae004a8 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Tue, 29 Aug 2023 23:19:20 +0300 -Subject: [PATCH 032/173] add missing message format - -Alternative fix for build issue in debug mode with GCC. ---- - src/civetweb.c | 8 -------- - src/http2.inl | 4 ++-- - 2 files changed, 2 insertions(+), 10 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 601ace48..df161c0b 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -230,16 +230,8 @@ static void DEBUG_TRACE_FUNC(const char *func, - PRINTF_FORMAT_STRING(const char *fmt), - ...) PRINTF_ARGS(3, 4); - --#if defined(__cplusplus) && (__cplusplus >= 202002L) --#define DEBUG_TRACE(fmt, ...) \ -- DEBUG_TRACE_FUNC(__func__, __LINE__, fmt __VA_OPT__(, ) __VA_ARGS__) --#elif defined(__GNUC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) --#define DEBUG_TRACE(fmt, ...) \ -- DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, ##__VA_ARGS__) --#else - #define DEBUG_TRACE(fmt, ...) \ - DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) --#endif - - #define NEED_DEBUG_TRACE_FUNC - #if !defined(DEBUG_TRACE_STREAM) -diff --git a/src/http2.inl b/src/http2.inl -index cea695ed..a4bfd8d0 100644 ---- a/src/http2.inl -+++ b/src/http2.inl -@@ -1442,7 +1442,7 @@ handle_http2(struct mg_connection *conn) - hpack_decode(buf, &i, (int)bytes_read, conn->phys_ctx); - CHECK_LEAK_HDR_ALLOC(key); - if (!key) { -- DEBUG_TRACE("HTTP2 key decoding error"); -+ DEBUG_TRACE("%s", "HTTP2 key decoding error"); - goto clean_http2; - } - } else if (/*(idx >= 15) &&*/ (idx <= 61)) { -@@ -1509,7 +1509,7 @@ handle_http2(struct mg_connection *conn) - conn->phys_ctx); /* leak? */ - CHECK_LEAK_HDR_ALLOC(val); - if (!val) { -- DEBUG_TRACE("HTTP2 value decoding error"); -+ DEBUG_TRACE("%s", "HTTP2 value decoding error"); - mg_free((void *)key); - goto clean_http2; - } --- -2.34.1 - - -From 07f002e49c8437dbd7b49d44a3f972952d3a5da1 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Tue, 29 Aug 2023 22:17:50 +0300 -Subject: [PATCH 033/173] Create Dockerfile - ---- - Dockerfile | 35 +++++++++++++++++++++++++++++++++++ - 1 file changed, 35 insertions(+) - create mode 100644 Dockerfile - -diff --git a/Dockerfile b/Dockerfile -new file mode 100644 -index 00000000..ac87841c ---- /dev/null -+++ b/Dockerfile -@@ -0,0 +1,35 @@ -+#################################################### -+# build stage -+#################################################### -+ -+FROM alpine:3.18 AS build -+RUN apk update && \ -+ apk add --no-cache \ -+ build-base zlib-dev -+ -+WORKDIR /civetweb -+COPY src ./src/ -+COPY include ./include/ -+COPY Makefile ./ -+COPY resources ./resources/ -+COPY *.md ./ -+ -+RUN make build && \ -+ make WITH_ALL=1 && \ -+ make install PREFIX=/app -+ -+#################################################### -+# image stage -+#################################################### -+ -+FROM alpine:3.18 -+RUN apk update && \ -+ apk add --no-cache \ -+ libstdc++ zlib -+ -+RUN addgroup -S civetweb && adduser -S civetweb -G civetweb -+USER civetweb -+ -+COPY --chown=civetweb:civetweb --from=build /app/ /app/ -+ -+ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] --- -2.34.1 - - -From 6653ce89f6307d37fab28ee3861f88e4d0a591e6 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Wed, 30 Aug 2023 00:41:40 +0300 -Subject: [PATCH 034/173] Docker documentation - ---- - Dockerfile | 32 +++++++++++++---- - README.md | 1 + - docs/Docker.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 122 insertions(+), 7 deletions(-) - create mode 100644 docs/Docker.md - -diff --git a/Dockerfile b/Dockerfile -index ac87841c..3bb7540a 100644 ---- a/Dockerfile -+++ b/Dockerfile -@@ -1,35 +1,53 @@ --#################################################### --# build stage --#################################################### -+# --------------------------------------------------- -+# Build Stage -+# --------------------------------------------------- - -+# Use Alpine Linux 3.18 as the base image for the build stage - FROM alpine:3.18 AS build -+ -+# Update package list and install build dependencies - RUN apk update && \ - apk add --no-cache \ - build-base zlib-dev -- -+ -+# Set the working directory inside the container - WORKDIR /civetweb -+ -+# Copy source code and other necessary files into the container - COPY src ./src/ - COPY include ./include/ - COPY Makefile ./ - COPY resources ./resources/ - COPY *.md ./ - -+# Build Civetweb with all features and install it into /app directory - RUN make build && \ - make WITH_ALL=1 && \ - make install PREFIX=/app - --#################################################### --# image stage --#################################################### -+# --------------------------------------------------- -+# Image Stage -+# --------------------------------------------------- - -+# Use Alpine Linux 3.18 as the base image for the final stage - FROM alpine:3.18 -+ -+# Update package list and install runtime dependencies - RUN apk update && \ - apk add --no-cache \ - libstdc++ zlib - -+# Create a non-root user and group for running Civetweb - RUN addgroup -S civetweb && adduser -S civetweb -G civetweb -+ -+# Switch to the non-root user - USER civetweb - -+# Copy the built application from the build stage into this stage - COPY --chown=civetweb:civetweb --from=build /app/ /app/ - -+# Expose port 8080 for the application -+EXPOSE 8080 -+ -+# Set the entry point for the container - ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] -diff --git a/README.md b/README.md -index 1d1c82b8..2c9e40a2 100644 ---- a/README.md -+++ b/README.md -@@ -75,6 +75,7 @@ Quick start documentation - - [docs/Building.md](https://github.com/civetweb/civetweb/blob/master/docs/Building.md) - Building the Server (quick start guide) - - [docs/Embedding.md](https://github.com/civetweb/civetweb/blob/master/docs/Embedding.md) - Embedding (how to add HTTP support to an existing application) - - [docs/OpenSSL.md](https://github.com/civetweb/civetweb/blob/master/docs/OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. -+- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use civetweb in a Docker container. - - [API documentation](https://github.com/civetweb/civetweb/tree/master/docs/api) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). - - [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md) - Release Notes - - [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - Security Policy -diff --git a/docs/Docker.md b/docs/Docker.md -new file mode 100644 -index 00000000..b21c3f47 ---- /dev/null -+++ b/docs/Docker.md -@@ -0,0 +1,96 @@ -+# Running Civetweb in Docker -+ -+## Overview -+ -+This guide explains how to build and run Civetweb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. -+ -+## Prerequisites -+ -+- Docker installed on your machine -+- Basic understanding of Docker commands -+ -+## Dockerfile Explained -+ -+### Build Stage -+ -+The build stage uses Alpine Linux 3.18 and installs the necessary build tools and libraries. -+ -+```Dockerfile -+FROM alpine:3.18 AS build -+RUN apk update && \ -+ apk add --no-cache \ -+ build-base zlib-dev -+``` -+ -+The source code and other necessary files are copied into the `/civetweb` directory in the container. -+ -+```Dockerfile -+WORKDIR /civetweb -+COPY src ./src/ -+COPY include ./include/ -+COPY Makefile ./ -+COPY resources ./resources/ -+COPY *.md ./ -+``` -+ -+The Civetweb server is then built and installed into the `/app` directory. -+ -+```Dockerfile -+RUN make build && \ -+ make WITH_ALL=1 && \ -+ make install PREFIX=/app -+``` -+ -+### Image Stage -+ -+The image stage also uses Alpine Linux 3.18 but installs only the runtime dependencies. -+ -+```Dockerfile -+FROM alpine:3.18 -+RUN apk update && \ -+ apk add --no-cache \ -+ libstdc++ zlib -+``` -+ -+A non-root user `civetweb` is created for security reasons. -+ -+```Dockerfile -+RUN addgroup -S civetweb && adduser -S civetweb -G civetweb -+USER civetweb -+``` -+ -+The built application from the build stage is copied into this stage. -+ -+```Dockerfile -+COPY --chown=civetweb:civetweb --from=build /app/ /app/ -+``` -+ -+The container will listen on port 8080 at runtime. -+ -+```Dockerfile -+EXPOSE 8080 -+``` -+ -+Finally, the entry point for the container is set. -+ -+```Dockerfile -+ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] -+``` -+ -+## Build and Run -+ -+To build the Docker image, run: -+ -+```bash -+docker build -t civetweb:latest . -+``` -+ -+To run the container, execute: -+ -+```bash -+docker run -p 8080:8080 civetweb:latest -+``` -+ -+## Conclusion -+ -+This Dockerfile provides a secure and efficient way to build and run Civetweb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. --- -2.34.1 - - -From 26a22d3e31727c2f86093610aaa677c9d48b1df1 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Wed, 30 Aug 2023 00:49:12 +0300 -Subject: [PATCH 035/173] fix typo - ---- - docs/Docker.md | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/docs/Docker.md b/docs/Docker.md -index b21c3f47..ac0fadc9 100644 ---- a/docs/Docker.md -+++ b/docs/Docker.md -@@ -1,8 +1,8 @@ --# Running Civetweb in Docker -+# Running CivetWeb in Docker - - ## Overview - --This guide explains how to build and run Civetweb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. -+This guide explains how to build and run CivetWeb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. - - ## Prerequisites - -@@ -93,4 +93,4 @@ docker run -p 8080:8080 civetweb:latest - - ## Conclusion - --This Dockerfile provides a secure and efficient way to build and run Civetweb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. -+This Dockerfile provides a secure and efficient way to build and run CivetWeb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. --- -2.34.1 - - -From a08666e1424be313c5f0df135fb82025d8650390 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Wed, 30 Aug 2023 00:51:39 +0300 -Subject: [PATCH 036/173] Update README.md - ---- - docs/README.md | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/docs/README.md b/docs/README.md -index 54ed7128..76e57732 100644 ---- a/docs/README.md -+++ b/docs/README.md -@@ -43,6 +43,7 @@ Documentation - - [Building.md](Building.md) - Building the Server (quick start guide) - - [Embedding.md](Embedding.md) - Embedding (how to add HTTP support to an existing application) - - [OpenSSL.md](OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. -+- [Docker.md](Docker.md) - Use CivetWeb in a Docker container. - - [APIReference.md](APIReference.md) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). - - [Authors](https://github.com/civetweb/civetweb/blob/master/CREDITS.md) --- -2.34.1 - - -From 06f7ac416cfffee2111fc3633f99b8cb0d359a34 Mon Sep 17 00:00:00 2001 -From: Hansi P -Date: Wed, 30 Aug 2023 00:53:04 +0300 -Subject: [PATCH 037/173] fix typo - ---- - README.md | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/README.md b/README.md -index 2c9e40a2..d0624ab5 100644 ---- a/README.md -+++ b/README.md -@@ -75,7 +75,7 @@ Quick start documentation - - [docs/Building.md](https://github.com/civetweb/civetweb/blob/master/docs/Building.md) - Building the Server (quick start guide) - - [docs/Embedding.md](https://github.com/civetweb/civetweb/blob/master/docs/Embedding.md) - Embedding (how to add HTTP support to an existing application) - - [docs/OpenSSL.md](https://github.com/civetweb/civetweb/blob/master/docs/OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. --- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use civetweb in a Docker container. -+- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use CivetWeb in a Docker container. - - [API documentation](https://github.com/civetweb/civetweb/tree/master/docs/api) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). - - [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md) - Release Notes - - [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - Security Policy --- -2.34.1 - - -From 3c62c8314720c7975ae68d343d6bfe66e6c9802b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Tomasz=20K=C5=82oczko?= -Date: Sat, 9 Sep 2023 11:27:58 +0000 -Subject: [PATCH 038/173] install pkgconfig files in libdir -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -pkgconfig files are arch dependend files so they needs to be installed -in libdir. - -Signed-off-by: Tomasz Kłoczko ---- - CMakeLists.txt | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 7a48123e..ae00462a 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -629,13 +629,13 @@ configure_file( - install( - FILES - "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" -- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" -+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" - ) - - install( - FILES - "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-cpp.pc" -- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" -+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" - ) - - write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake --- -2.34.1 - - -From ee7ea1bb767c769e15bfa25266fd92145f7a05ab Mon Sep 17 00:00:00 2001 -From: Evan Fedorov -Date: Sun, 1 Oct 2023 19:17:30 +0300 -Subject: [PATCH 039/173] fix minor typo in docs/api/mg_request_info.md - -Added missing underscore symbol to `remote_addr`. ---- - docs/api/mg_request_info.md | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/docs/api/mg_request_info.md b/docs/api/mg_request_info.md -index 0f9d720d..b7e0b6e1 100644 ---- a/docs/api/mg_request_info.md -+++ b/docs/api/mg_request_info.md -@@ -14,7 +14,7 @@ - |**`http_version`**|`const char *`| The HTTP version as mentioned in the client request. This can be "1.0", "1.1", etc. | - |**`query_string`**|`const char *`| The HTTP query string, defined as URL part after the first '?' character, not including '?'. NULL if there is no '?'. | - |**`remote_user`**|`const char *`| The name of the authenticated remote user, or NULL if no authentication was used. Only used for HTTP (digest) authentication, not for cookie based authentication. | --|**`remote addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address. Example: "127.0.0.1" | -+|**`remote_addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address. Example: "127.0.0.1" | - |~~`remote_ip`~~|`long`| *Deprecated. Use* `remote_addr` *instead* | - |**`content_length`**|`long long`| The content length of the request body. This value can be -1 if no content length was provided. The request may still have body data, but the server cannot determine the length until all data has arrived (e.g. when the client closes the connection, or the final chunk of a chunked request has been received). | - |**`remote_port`**|`int`| The port number at the client side (an integer number between 1 and 65535). | --- -2.34.1 - - -From e443671abc1e55474ac60ee43a692570c02d8108 Mon Sep 17 00:00:00 2001 -From: Brian -Date: Wed, 11 Oct 2023 20:05:37 -0400 -Subject: [PATCH 040/173] Add CodeQL Workflow for Code Security Analysis - -Add CodeQL Workflow for Code Security Analysis - -This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats. - -We added a new CodeQL workflow file (.github/workflows/codeql.yml) that -- Runs on every push and pull request to the main branch. -- Excludes queries with a high false positive rate or low-severity findings. -- Does not display results for third-party code, focusing only on our own codebase. - -Testing: -To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code. - -Deployment: -Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps: -1. Under the repository name, click on the Security tab. -2. In the left sidebar, click Code scanning alerts. - -Additional Information: -- You can further customize the workflow to adapt to your specific needs by modifying the workflow file. -- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation. - -Signed-off-by: Brian ---- - .github/workflows/codeql-buildscript.sh | 3 + - .github/workflows/codeql.yml | 123 ++++++++++++++++++++++++ - 2 files changed, 126 insertions(+) - create mode 100644 .github/workflows/codeql-buildscript.sh - create mode 100644 .github/workflows/codeql.yml - -diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh -new file mode 100644 -index 00000000..366db622 ---- /dev/null -+++ b/.github/workflows/codeql-buildscript.sh -@@ -0,0 +1,3 @@ -+#!/usr/bin/env bash -+ -+make build WITH_ALL=1 -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -new file mode 100644 -index 00000000..eaa9599b ---- /dev/null -+++ b/.github/workflows/codeql.yml -@@ -0,0 +1,123 @@ -+# For most projects, this workflow file will not need changing; you simply need -+# to commit it to your repository. -+# -+# You may wish to alter this file to override the set of languages analyzed, -+# or to provide custom queries or build logic. -+# -+# ******** NOTE ******** -+# We have attempted to detect the languages in your repository. Please check -+# the `language` matrix defined below to confirm you have the correct set of -+# supported CodeQL languages. -+# -+name: "CodeQL" -+ -+on: -+ push: -+ branches: [ "main", "master" ] -+ pull_request: -+ # The branches below must be a subset of the branches above -+ branches: [ "main", "master" ] -+ schedule: -+ - cron: '28 21 * * 0' -+ -+jobs: -+ analyze: -+ name: Analyze -+ # Runner size impacts CodeQL analysis time. To learn more, please see: -+ # - https://gh.io/recommended-hardware-resources-for-running-codeql -+ # - https://gh.io/supported-runners-and-hardware-resources -+ # - https://gh.io/using-larger-runners -+ # Consider using larger runners for possible analysis time improvements. -+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }} -+ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} -+ permissions: -+ actions: read -+ contents: read -+ security-events: write -+ -+ strategy: -+ fail-fast: false -+ matrix: -+ language: [ 'cpp' ] -+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] -+ # Use only 'java' to analyze code written in Java, Kotlin or both -+ # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both -+ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support -+ -+ steps: -+ - name: Checkout repository -+ uses: actions/checkout@v3 -+ with: -+ submodules: recursive -+ -+ # Initializes the CodeQL tools for scanning. -+ - name: Initialize CodeQL -+ uses: github/codeql-action/init@v2 -+ with: -+ languages: ${{ matrix.language }} -+ # If you wish to specify custom queries, you can do so here or in a config file. -+ # By default, queries listed here will override any specified in a config file. -+ # Prefix the list here with "+" to use these queries and those in the config file. -+ -+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs -+ # queries: security-extended,security-and-quality -+ queries: security-and-quality -+ -+ -+ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). -+ # If this step fails, then you should remove it and run the build manually (see below) -+ #- name: Autobuild -+ # uses: github/codeql-action/autobuild@v2 -+ -+ # ℹ️ Command-line programs to run using the OS shell. -+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun -+ -+ # If the Autobuild fails above, remove it and uncomment the following three lines. -+ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. -+ -+ - run: | -+ ./.github/workflows/codeql-buildscript.sh -+ -+ - name: Perform CodeQL Analysis -+ uses: github/codeql-action/analyze@v2 -+ with: -+ category: "/language:${{matrix.language}}" -+ upload: false -+ id: step1 -+ -+ # Filter out rules with low severity or high false positve rate -+ # Also filter out warnings in third-party code -+ - name: Filter out unwanted errors and warnings -+ uses: advanced-security/filter-sarif@v1 -+ with: -+ patterns: | -+ -**:cpp/path-injection -+ -**:cpp/world-writable-file-creation -+ -**:cpp/poorly-documented-function -+ -**:cpp/potentially-dangerous-function -+ -**:cpp/use-of-goto -+ -**:cpp/integer-multiplication-cast-to-long -+ -**:cpp/comparison-with-wider-type -+ -**:cpp/leap-year/* -+ -**:cpp/ambiguously-signed-bit-field -+ -**:cpp/suspicious-pointer-scaling -+ -**:cpp/suspicious-pointer-scaling-void -+ -**:cpp/unsigned-comparison-zero -+ -**/third*party/** -+ -**/3rd*party/** -+ -**/external/** -+ input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif -+ output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif -+ -+ - name: Upload SARIF -+ uses: github/codeql-action/upload-sarif@v2 -+ with: -+ sarif_file: ${{ steps.step1.outputs.sarif-output }} -+ category: "/language:${{matrix.language}}" -+ -+ - name: Archive CodeQL results -+ uses: actions/upload-artifact@v3 -+ with: -+ name: codeql-results -+ path: ${{ steps.step1.outputs.sarif-output }} -+ retention-days: 5 -\ No newline at end of file --- -2.34.1 - - -From a5c11a43f68a66aeac34de0d8848315e6bc2b877 Mon Sep 17 00:00:00 2001 -From: "E. van Putten" -Date: Thu, 12 Oct 2023 13:58:02 +0200 -Subject: [PATCH 041/173] Self-signed certificate key size from 1024 to 2048 - -The current self-signed certificate doesn't work. It will cause an SSL error (EE_KEY_TOO_SMALL) when loading the PEM file. ---- - docs/OpenSSL.md | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/docs/OpenSSL.md b/docs/OpenSSL.md -index 9822e9a4..f37c6b38 100644 ---- a/docs/OpenSSL.md -+++ b/docs/OpenSSL.md -@@ -46,7 +46,7 @@ One can use the following steps in Windows (in Linux replace "copy" by "cp" - and "type" by "cat"): - -
        --  openssl genrsa -des3 -out server.key 1024
        -+  openssl genrsa -des3 -out server.key 2048
        - 
        -   openssl req -new -key server.key -out server.csr
        - 
        --- 
        -2.34.1
        -
        -
        -From 01d1034aa00d4348ab3f8fc3c23fcff79f465115 Mon Sep 17 00:00:00 2001
        -From: HypoYoung 
        -Date: Wed, 18 Oct 2023 20:43:32 +0800
        -Subject: [PATCH 042/173] cmake compile support mbedtls
        -
        ----
        - CMakeLists.txt     |  5 +++++
        - src/CMakeLists.txt | 25 ++++++++++++++++---------
        - 2 files changed, 21 insertions(+), 9 deletions(-)
        -
        -diff --git a/CMakeLists.txt b/CMakeLists.txt
        -index ae00462a..a435a975 100644
        ---- a/CMakeLists.txt
        -+++ b/CMakeLists.txt
        -@@ -231,6 +231,9 @@ message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}")
        - option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF)
        - message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}")
        - 
        -+option(CIVETWEB_ENABLE_MBEDTLS "Use the MbedTls" OFF)
        -+message(STATUS "SSL support - ${CIVETWEB_ENABLE_MBEDTLS}")
        -+
        - # Dynamically load or link the SSL libraries
        - cmake_dependent_option(
        -   CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING "Dynamically loads the SSL library rather than linking it" ON
        -@@ -523,6 +526,8 @@ if (CIVETWEB_ENABLE_MEMORY_DEBUGGING)
        - endif()
        - if (NOT CIVETWEB_ENABLE_SSL)
        -   add_definitions(-DNO_SSL)
        -+elseif (CIVETWEB_ENABLE_MBEDTLS)
        -+  add_definitions(-DUSE_MBEDTLS)
        - elseif (NOT CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        -   add_definitions(-DNO_SSL_DL)
        - else()
        -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
        -index 43f3a77e..47a9b0c8 100644
        ---- a/src/CMakeLists.txt
        -+++ b/src/CMakeLists.txt
        -@@ -47,16 +47,23 @@ endif()
        - 
        - # We need to link OpenSSL if not dynamically loading
        - if (CIVETWEB_ENABLE_SSL)
        --  if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        --    find_package(LibDl)
        --    if (LIBDL_FOUND)
        --      target_link_libraries(civetweb-c-library -ldl)
        --    endif()
        -+  if (CIVETWEB_ENBALE_MBEDTLS)
        -+    find_package(MbedTLS)
        -+    include_directories(${MbedTLS_INCLUDE_DIR})
        -+    message(STATUS "MbedTLS include directory: {MbedTLS_INCLUDE_DIR}")
        -+    target_link_libraries(civetweb-c-library ${MbedTLS_LIBRARIES})
        -   else()
        --    find_package(OpenSSL)
        --    include_directories(${OPENSSL_INCLUDE_DIR})
        --    message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
        --    target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
        -+    if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        -+      find_package(LibDl)
        -+      if (LIBDL_FOUND)
        -+        target_link_libraries(civetweb-c-library -ldl)
        -+      endif()
        -+    else()
        -+      find_package(OpenSSL)
        -+      include_directories(${OPENSSL_INCLUDE_DIR})
        -+      message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
        -+      target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
        -+    endif()
        -   endif()
        - endif()
        - 
        --- 
        -2.34.1
        -
        -
        -From b70fe7454719dd0635363836b2fde8dc7609eb75 Mon Sep 17 00:00:00 2001
        -From: Brian 
        -Date: Wed, 18 Oct 2023 16:53:01 -0400
        -Subject: [PATCH 043/173] Add CodeQL Workflow for Code Security Analysis
        -
        -Add CodeQL Workflow for Code Security Analysis
        -
        -This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.
        -
        -We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
        -- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
        -- Runs daily.
        -- Excludes queries with a high false positive rate or low-severity findings.
        -- Does not display results for git submodules, focusing only on our own codebase.
        -
        -Testing:
        -To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.
        -
        -Deployment:
        -Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
        -1. Under the repository name, click on the Security tab.
        -2. In the left sidebar, click Code scanning alerts.
        -
        -Additional Information:
        -- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
        -- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).
        -
        -Signed-off-by: Brian 
        ----
        - .github/workflows/codeql.yml | 23 +++++++++++++----------
        - 1 file changed, 13 insertions(+), 10 deletions(-)
        -
        -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
        -index eaa9599b..3ef873fc 100644
        ---- a/.github/workflows/codeql.yml
        -+++ b/.github/workflows/codeql.yml
        -@@ -14,11 +14,10 @@ name: "CodeQL"
        - on:
        -   push:
        -     branches: [ "main", "master" ]
        --  pull_request:
        --    # The branches below must be a subset of the branches above
        --    branches: [ "main", "master" ]
        -   schedule:
        --    - cron: '28 21 * * 0'
        -+    - cron: '0 0 * * *'
        -+  pull_request:
        -+    branches: '*'
        - 
        - jobs:
        -   analyze:
        -@@ -103,21 +102,25 @@ jobs:
        -           -**:cpp/suspicious-pointer-scaling
        -           -**:cpp/suspicious-pointer-scaling-void
        -           -**:cpp/unsigned-comparison-zero
        --          -**/third*party/**
        --          -**/3rd*party/**
        --          -**/external/**
        -+          -**/cmake*/Modules/**
        -         input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
        -         output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
        - 
        --    - name: Upload SARIF
        -+    - name: Upload CodeQL results to code scanning
        -       uses: github/codeql-action/upload-sarif@v2
        -       with:
        -         sarif_file: ${{ steps.step1.outputs.sarif-output }}
        -         category: "/language:${{matrix.language}}"
        - 
        --    - name: Archive CodeQL results
        -+    - name: Upload CodeQL results as an artifact
        -+      if: success() || failure()
        -       uses: actions/upload-artifact@v3
        -       with:
        -         name: codeql-results
        -         path: ${{ steps.step1.outputs.sarif-output }}
        --        retention-days: 5
        -\ No newline at end of file
        -+        retention-days: 5
        -+
        -+    - name: Fail if an error is found
        -+      run: |
        -+        ./.github/workflows/fail_on_error.py \
        -+          ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
        --- 
        -2.34.1
        -
        -
        -From 22dbe1c2f067d75e6d7de28ac9a91b0718bc6c0b Mon Sep 17 00:00:00 2001
        -From: Brian 
        -Date: Fri, 20 Oct 2023 01:03:35 -0400
        -Subject: [PATCH 044/173] Add CodeQL Workflow for Code Security Analysis
        -
        -Add CodeQL Workflow for Code Security Analysis
        -
        -This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.
        -
        -We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
        -- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
        -- Runs daily.
        -- Excludes queries with a high false positive rate or low-severity findings.
        -- Does not display results for git submodules, focusing only on our own codebase.
        -
        -Testing:
        -To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.
        -
        -Deployment:
        -Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
        -1. Under the repository name, click on the Security tab.
        -2. In the left sidebar, click Code scanning alerts.
        -
        -Additional Information:
        -- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
        -- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).
        -
        -Signed-off-by: Brian 
        ----
        - .github/workflows/fail_on_error.py | 34 ++++++++++++++++++++++++++++++
        - 1 file changed, 34 insertions(+)
        - create mode 100755 .github/workflows/fail_on_error.py
        -
        -diff --git a/.github/workflows/fail_on_error.py b/.github/workflows/fail_on_error.py
        -new file mode 100755
        -index 00000000..29791742
        ---- /dev/null
        -+++ b/.github/workflows/fail_on_error.py
        -@@ -0,0 +1,34 @@
        -+#!/usr/bin/env python3
        -+
        -+import json
        -+import sys
        -+
        -+# Return whether SARIF file contains error-level results
        -+def codeql_sarif_contain_error(filename):
        -+    with open(filename, 'r') as f:
        -+        s = json.load(f)
        -+
        -+    for run in s.get('runs', []):
        -+        rules_metadata = run['tool']['driver']['rules']
        -+        if not rules_metadata:
        -+            rules_metadata = run['tool']['extensions'][0]['rules']
        -+
        -+        for res in run.get('results', []):
        -+            if 'ruleIndex' in res:
        -+                rule_index = res['ruleIndex']
        -+            elif 'rule' in res and 'index' in res['rule']:
        -+                rule_index = res['rule']['index']
        -+            else:
        -+                continue
        -+            try:
        -+                rule_level = rules_metadata[rule_index]['defaultConfiguration']['level']
        -+            except IndexError as e:
        -+                print(e, rule_index, len(rules_metadata))
        -+            else:
        -+                if rule_level == 'error':
        -+                    return True
        -+    return False
        -+
        -+if __name__ == "__main__":
        -+    if codeql_sarif_contain_error(sys.argv[1]):
        -+        sys.exit(1)
        --- 
        -2.34.1
        -
        -
        -From 9279eb8f6d980a123b543de9e160649e73873cdf Mon Sep 17 00:00:00 2001
        -From: Brian 
        -Date: Sun, 29 Oct 2023 15:29:20 -0400
        -Subject: [PATCH 045/173] Add CodeQL Workflow for Code Security Analysis
        -
        -Add CodeQL Workflow for Code Security Analysis
        -
        -This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats.
        -
        -We added a new CodeQL workflow file (.github/workflows/codeql.yml) that
        -- Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience).
        -- Runs daily.
        -- Excludes queries with a high false positive rate or low-severity findings.
        -- Does not display results for git submodules, focusing only on our own codebase.
        -
        -Testing:
        -To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code.
        -
        -Deployment:
        -Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps:
        -1. Under the repository name, click on the Security tab.
        -2. In the left sidebar, click Code scanning alerts.
        -
        -Additional Information:
        -- You can further customize the workflow to adapt to your specific needs by modifying the workflow file.
        -- For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/).
        -
        -Signed-off-by: Brian 
        ----
        - .github/workflows/codeql.yml | 4 ++--
        - 1 file changed, 2 insertions(+), 2 deletions(-)
        -
        -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
        -index 3ef873fc..81649227 100644
        ---- a/.github/workflows/codeql.yml
        -+++ b/.github/workflows/codeql.yml
        -@@ -12,8 +12,8 @@
        - name: "CodeQL"
        - 
        - on:
        --  push:
        --    branches: [ "main", "master" ]
        -+  # push:
        -+  #   branches: [ "main", "master" ]
        -   schedule:
        -     - cron: '0 0 * * *'
        -   pull_request:
        --- 
        -2.34.1
        -
        -
        -From 2d7d597486025feb37faec7826cee7f2c28a7827 Mon Sep 17 00:00:00 2001
        -From: DL6ER 
        -Date: Tue, 31 Oct 2023 08:47:45 +0100
        -Subject: [PATCH 046/173] Truly allow all non-control characters in URIs
        -
        -Signed-off-by: DL6ER 
        ----
        - src/civetweb.c | 5 +++--
        - 1 file changed, 3 insertions(+), 2 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index df161c0b..7e869887 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -10843,8 +10843,9 @@ static int
        - skip_to_end_of_word_and_terminate(char **ppw, int eol)
        - {
        - 	/* Forward until a space is found - use isgraph here */
        -+	/* Extended ASCII characters are also treated as word characters. */
        - 	/* See http://www.cplusplus.com/reference/cctype/ */
        --	while (isgraph((unsigned char)**ppw)) {
        -+	while ((unsigned char)**ppw > 127 || isgraph((unsigned char)**ppw)) {
        - 		(*ppw)++;
        - 	}
        - 
        -@@ -18639,7 +18640,7 @@ get_uri_type(const char *uri)
        - 	 * and % encoded symbols.
        - 	 */
        - 	for (i = 0; uri[i] != 0; i++) {
        --		if (uri[i] < 33) {
        -+		if ((unsigned char)uri[i] < 33) {
        - 			/* control characters and spaces are invalid */
        - 			return 0;
        - 		}
        --- 
        -2.34.1
        -
        -
        -From 8a15283648c5fdba0120544e41759a40fd39b42e Mon Sep 17 00:00:00 2001
        -From: DL6ER 
        -Date: Tue, 31 Oct 2023 08:42:58 +0100
        -Subject: [PATCH 047/173] struct mbedtls_ssl_context->state is only available
        - using the macro MBEDTLS_PRIVATE(state)
        -
        -Signed-off-by: DL6ER 
        ----
        - src/mod_mbedtls.inl | 8 ++++++++
        - 1 file changed, 8 insertions(+)
        -
        -diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl
        -index 822fc2e0..cada215e 100644
        ---- a/src/mod_mbedtls.inl
        -+++ b/src/mod_mbedtls.inl
        -@@ -188,7 +188,11 @@ mbed_ssl_accept(mbedtls_ssl_context **ssl,
        - 		return -1;
        - 	}
        - 
        -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
        -+	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->MBEDTLS_PRIVATE(state));
        -+#else
        - 	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->state);
        -+#endif
        - 	return 0;
        - }
        - 
        -@@ -214,7 +218,11 @@ mbed_ssl_handshake(mbedtls_ssl_context *ssl)
        - 		}
        - 	}
        - 
        -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
        -+	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->MBEDTLS_PRIVATE(state));
        -+#else
        - 	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->state);
        -+#endif
        - 	return rc;
        - }
        - 
        --- 
        -2.34.1
        -
        -
        -From 3afcd237e509a6700c449056dcc5b9092af31e6a Mon Sep 17 00:00:00 2001
        -From: Fabrice Fontaine 
        -Date: Mon, 6 Nov 2023 21:38:02 +0100
        -Subject: [PATCH 048/173] resources/Makefile.in-lua: fix build with Lua 5.1
        -
        -Fix the following build failure with Lua 5.1 raised since version 1.16
        -and
        -https://github.com/civetweb/civetweb/commit/82ba5a04c9a1ca980d8dfe00c18e086017e8ab25:
        -
        -/home/buildroot/autobuild/instance-1/output-1/host/opt/ext-toolchain/bin/../lib/gcc/sh4-buildroot-linux-gnu/13.2.0/../../../../sh4-buildroot-linux-gnu/bin/ld: out/src/third_party/lua_struct.o: in function `luaopen_struct':
        -lua_struct.c:(.text+0xce4): undefined reference to `luaL_newlib'
        -
        -Fixes:
        - - http://autobuild.buildroot.org/results/7459b504e52f473c5830c0f3c7bffd037f6e1770
        -
        -Signed-off-by: Fabrice Fontaine 
        ----
        - resources/Makefile.in-lua | 2 ++
        - 1 file changed, 2 insertions(+)
        -
        -diff --git a/resources/Makefile.in-lua b/resources/Makefile.in-lua
        -index d3038f66..45f80181 100644
        ---- a/resources/Makefile.in-lua
        -+++ b/resources/Makefile.in-lua
        -@@ -150,6 +150,7 @@ CFLAGS += -DUSE_LUA_FILE_SYSTEM
        - #SOURCE_DIRS = $(LFS_DIR)
        - 
        - 
        -+ifneq ($(WITH_LUA_VERSION), 501)
        - LXX_DIR = src/third_party
        - LXX_SOURCE_FILES = lua_struct.c
        - LXX_SOURCES = $(addprefix $(LXX_DIR)/, $(LXX_SOURCE_FILES))
        -@@ -159,6 +160,7 @@ OBJECTS += $(LXX_OBJECTS)
        - CFLAGS += $(LXX_CFLAGS)
        - CFLAGS += -DUSE_LUA_STRUCT
        - #SOURCE_DIRS = $(LXX_DIR)
        -+endif
        - 
        - 
        - ifneq ($(WITH_LUA_VERSION), 501)
        --- 
        -2.34.1
        -
        -
        -From 6b1aed0962db94cde10da8a769be7db6b461cdc6 Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Sun, 19 Nov 2023 13:51:06 +0100
        -Subject: [PATCH 049/173] Format code
        -
        ----
        - src/civetweb.c      | 204 +++++++++++++++++++++++++++-----------------
        - src/mod_mbedtls.inl |   8 +-
        - 2 files changed, 130 insertions(+), 82 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 7e869887..c8df640a 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -1921,8 +1921,9 @@ struct socket {
        - 	unsigned char is_ssl;    /* Is port SSL-ed */
        - 	unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
        - 	                          * port */
        --	unsigned char is_optional; /* Shouldn't cause us to exit if we can't bind to it */
        --	unsigned char in_use;    /* 0: invalid, 1: valid, 2: free */
        -+	unsigned char
        -+	    is_optional; /* Shouldn't cause us to exit if we can't bind to it */
        -+	unsigned char in_use; /* 0: invalid, 1: valid, 2: free */
        - };
        - 
        - 
        -@@ -2399,17 +2400,18 @@ struct mg_context {
        - 	stop_flag_t stop_flag;        /* Should we stop event loop */
        - 	pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
        - 
        --	pthread_t masterthreadid; /* The master thread ID */
        --	unsigned int
        --	    cfg_max_worker_threads;  /* How many worker-threads we are allowed to create, total */
        -+	pthread_t masterthreadid;            /* The master thread ID */
        -+	unsigned int cfg_max_worker_threads; /* How many worker-threads we are
        -+	                                        allowed to create, total */
        - 
        -+	unsigned int spawned_worker_threads; /* How many worker-threads currently
        -+	                                        exist (modified by master thread) */
        - 	unsigned int
        --	    spawned_worker_threads;  /* How many worker-threads currently exist (modified by master thread) */
        --	unsigned int
        --	    idle_worker_thread_count; /* How many worker-threads are currently sitting around with nothing to do */
        --	                              /* Access to this value MUST be synchronized by thread_mutex */
        -+	    idle_worker_thread_count; /* How many worker-threads are currently
        -+	                                 sitting around with nothing to do */
        -+	/* Access to this value MUST be synchronized by thread_mutex */
        - 
        --	pthread_t *worker_threadids; /* The worker thread IDs */
        -+	pthread_t *worker_threadids;      /* The worker thread IDs */
        - 	unsigned long starter_thread_idx; /* thread index which called mg_start */
        - 
        - 	/* Connection to thread dispatching */
        -@@ -4205,7 +4207,8 @@ send_cors_header(struct mg_connection *conn)
        - 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        - 	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
        - 		/* Cross-origin resource sharing (CORS), see
        --		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials */
        -+		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
        -+		 */
        - 		mg_response_header_add(conn,
        - 		                       "Access-Control-Allow-Credentials",
        - 		                       cors_cred_cfg,
        -@@ -4215,30 +4218,29 @@ send_cors_header(struct mg_connection *conn)
        - 	const char *cors_hdr_cfg =
        - 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
        - 	if (cors_hdr_cfg && *cors_hdr_cfg) {
        --	   mg_response_header_add(conn,
        --	                          "Access-Control-Allow-Headers",
        --	                          cors_hdr_cfg,
        --	                          -1);
        -+		mg_response_header_add(conn,
        -+		                       "Access-Control-Allow-Headers",
        -+		                       cors_hdr_cfg,
        -+		                       -1);
        - 	}
        - 
        - 	const char *cors_exphdr_cfg =
        --	      conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        - 	if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        --	   mg_response_header_add(conn,
        --	                          "Access-Control-Expose-Headers",
        --	                          cors_exphdr_cfg,
        --	                          -1);
        -+		mg_response_header_add(conn,
        -+		                       "Access-Control-Expose-Headers",
        -+		                       cors_exphdr_cfg,
        -+		                       -1);
        - 	}
        - 
        - 	const char *cors_meth_cfg =
        --	      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
        - 	if (cors_meth_cfg && *cors_meth_cfg) {
        --	   mg_response_header_add(conn,
        --	                          "Access-Control-Allow-Methods",
        --	                          cors_meth_cfg,
        --	                          -1);
        -+		mg_response_header_add(conn,
        -+		                       "Access-Control-Allow-Methods",
        -+		                       cors_meth_cfg,
        -+		                       -1);
        - 	}
        --
        - }
        - 
        - 
        -@@ -15038,19 +15040,19 @@ handle_request(struct mg_connection *conn)
        - 			          suggest_connection_header(conn));
        - 
        - 			const char *cors_cred_cfg =
        --			      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        -+			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        - 			if (cors_cred_cfg && *cors_cred_cfg) {
        --			   mg_printf(conn,
        --			             "Access-Control-Allow-Credentials: %s\r\n",
        --			             cors_cred_cfg);
        -+				mg_printf(conn,
        -+				          "Access-Control-Allow-Credentials: %s\r\n",
        -+				          cors_cred_cfg);
        - 			}
        - 
        - 			const char *cors_exphdr_cfg =
        --			      conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        -+			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        - 			if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        --			   mg_printf(conn,
        --			             "Access-Control-Expose-Headers: %s\r\n",
        --			             cors_exphdr_cfg);
        -+				mg_printf(conn,
        -+				          "Access-Control-Expose-Headers: %s\r\n",
        -+				          cors_exphdr_cfg);
        - 			}
        - 
        - 			if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) {
        -@@ -15844,31 +15846,37 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
        - 	}
        - 
        - 	/* sscanf and the option splitting code ensure the following condition
        --	 * Make sure the port is valid and vector ends with the port, 'o', 's', or 'r' */
        -+	 * Make sure the port is valid and vector ends with the port, 'o', 's', or
        -+	 * 'r' */
        - 	if ((len > 0) && (is_valid_port(port))) {
        - 		int bad_suffix = 0;
        - 
        - 		/* Parse any suffix character(s) after the port number */
        --		for (size_t i=len; ilen; i++)
        --		{
        --			unsigned char * opt = NULL;
        --			switch(vec->ptr[i])
        --			{
        --				case 'o': opt = &so->is_optional; break;
        --				case 'r': opt = &so->ssl_redir;   break;
        --				case 's': opt = &so->is_ssl;      break;
        --				default:  /* empty */             break;
        -+		for (size_t i = len; i < vec->len; i++) {
        -+			unsigned char *opt = NULL;
        -+			switch (vec->ptr[i]) {
        -+			case 'o':
        -+				opt = &so->is_optional;
        -+				break;
        -+			case 'r':
        -+				opt = &so->ssl_redir;
        -+				break;
        -+			case 's':
        -+				opt = &so->is_ssl;
        -+				break;
        -+			default: /* empty */
        -+				break;
        - 			}
        - 
        --			if ((opt)&&(*opt == 0)) *opt = 1;
        --			else
        --			{
        -+			if ((opt) && (*opt == 0))
        -+				*opt = 1;
        -+			else {
        - 				bad_suffix = 1;
        - 				break;
        - 			}
        - 		}
        - 
        --		if ((bad_suffix == 0)&&((so->is_ssl == 0)||(so->ssl_redir == 0))) {
        -+		if ((bad_suffix == 0) && ((so->is_ssl == 0) || (so->ssl_redir == 0))) {
        - 			return 1;
        - 		}
        - 	}
        -@@ -15924,8 +15932,11 @@ is_ssl_port_used(const char *ports)
        - 
        - 		for (i = 0; i < portslen; i++) {
        - 			if (prevIsNumber) {
        --				int suffixCharIdx = (ports[i] == 'o') ? (i+1) : i;  /* allow "os" and "or" suffixes */
        --				if (ports[suffixCharIdx] == 's' || ports[suffixCharIdx] == 'r') {
        -+				int suffixCharIdx = (ports[i] == 'o')
        -+				                        ? (i + 1)
        -+				                        : i; /* allow "os" and "or" suffixes */
        -+				if (ports[suffixCharIdx] == 's'
        -+				    || ports[suffixCharIdx] == 'r') {
        - 					return 1;
        - 				}
        - 			}
        -@@ -16111,7 +16122,8 @@ set_ports_option(struct mg_context *phys_ctx)
        - 				closesocket(so.sock);
        - 				so.sock = INVALID_SOCKET;
        - 				if (so.is_optional) {
        --					portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */
        -+					portsOk++; /* it's okay if we couldn't bind, this port is
        -+					              optional anyway */
        - 				}
        - 				continue;
        - 			}
        -@@ -16130,7 +16142,8 @@ set_ports_option(struct mg_context *phys_ctx)
        - 				closesocket(so.sock);
        - 				so.sock = INVALID_SOCKET;
        - 				if (so.is_optional) {
        --					portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */
        -+					portsOk++; /* it's okay if we couldn't bind, this port is
        -+					              optional anyway */
        - 				}
        - 				continue;
        - 			}
        -@@ -16149,7 +16162,8 @@ set_ports_option(struct mg_context *phys_ctx)
        - 				closesocket(so.sock);
        - 				so.sock = INVALID_SOCKET;
        - 				if (so.is_optional) {
        --					portsOk++; /* it's okay if we couldn't bind, this port is optional anyway */
        -+					portsOk++; /* it's okay if we couldn't bind, this port is
        -+					              optional anyway */
        - 				}
        - 				continue;
        - 			}
        -@@ -18847,7 +18861,8 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        - 			            ebuf,
        - 			            ebuf_len,
        - 			            "%s",
        --			            conn->request_len == -3 ? "Request timeout" : "Malformed message");
        -+			            conn->request_len == -3 ? "Request timeout"
        -+			                                    : "Malformed message");
        - 			*err = 400;
        - 		} else {
        - 			/* Server did not recv anything -> just close the connection */
        -@@ -19779,7 +19794,9 @@ process_new_connection(struct mg_connection *conn)
        - #endif
        - }
        - 
        --static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads);  /* forward declaration */
        -+static int
        -+mg_start_worker_thread(struct mg_context *ctx,
        -+                       int only_if_no_idle_threads); /* forward declaration */
        - 
        - #if defined(ALTERNATIVE_QUEUE)
        - 
        -@@ -19788,7 +19805,9 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        - {
        - 	unsigned int i;
        - 
        --	(void)mg_start_worker_thread(ctx, 1);  /* will start a worker-thread only if there aren't currently any idle worker-threads */
        -+	(void)mg_start_worker_thread(
        -+	    ctx, 1); /* will start a worker-thread only if there aren't currently
        -+	                any idle worker-threads */
        - 
        - 	while (!ctx->stop_flag) {
        - 		for (i = 0; i < ctx->spawned_worker_threads; i++) {
        -@@ -19816,11 +19835,16 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        - 
        - 
        - static int
        --consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented)
        -+consume_socket(struct mg_context *ctx,
        -+               struct socket *sp,
        -+               int thread_index,
        -+               int counter_was_preincremented)
        - {
        - 	DEBUG_TRACE("%s", "going idle");
        - 	(void)pthread_mutex_lock(&ctx->thread_mutex);
        --	if (counter_was_preincremented == 0) {  /* first call only: the master-thread pre-incremented this before he spawned us */
        -+	if (counter_was_preincremented
        -+	    == 0) { /* first call only: the master-thread pre-incremented this
        -+		           before he spawned us */
        - 		ctx->idle_worker_thread_count++;
        - 	}
        - 	ctx->client_socks[thread_index].in_use = 2;
        -@@ -19854,13 +19878,18 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int
        - 
        - /* Worker threads take accepted socket from the queue */
        - static int
        --consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented)
        -+consume_socket(struct mg_context *ctx,
        -+               struct socket *sp,
        -+               int thread_index,
        -+               int counter_was_preincremented)
        - {
        - 	(void)thread_index;
        - 
        - 	DEBUG_TRACE("%s", "going idle");
        - 	(void)pthread_mutex_lock(&ctx->thread_mutex);
        --	if (counter_was_preincremented == 0) {  /* first call only: the master-thread pre-incremented this before he spawned us */
        -+	if (counter_was_preincremented
        -+	    == 0) { /* first call only: the master-thread pre-incremented this
        -+		           before he spawned us */
        - 		ctx->idle_worker_thread_count++;
        - 	}
        - 
        -@@ -19935,7 +19964,9 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        - 	(void)pthread_cond_signal(&ctx->sq_full);
        - 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        - 
        --	(void)mg_start_worker_thread(ctx, 1);  /* will start a worker-thread only if there aren't currently any idle worker-threads */
        -+	(void)mg_start_worker_thread(
        -+	    ctx, 1); /* will start a worker-thread only if there aren't currently
        -+	                any idle worker-threads */
        - }
        - #endif /* ALTERNATIVE_QUEUE */
        - 
        -@@ -20013,7 +20044,8 @@ worker_thread_run(struct mg_connection *conn)
        - 	/* Call consume_socket() even when ctx->stop_flag > 0, to let it
        - 	 * signal sq_empty condvar to wake up the master waiting in
        - 	 * produce_socket() */
        --	while (consume_socket(ctx, &conn->client, thread_index, first_call_to_consume_socket)) {
        -+	while (consume_socket(
        -+	    ctx, &conn->client, thread_index, first_call_to_consume_socket)) {
        - 		first_call_to_consume_socket = 0;
        - 
        - 		/* New connections must start with new protocol negotiation */
        -@@ -20791,34 +20823,43 @@ mg_socketpair(int *sockA, int *sockB)
        - #endif
        - }
        - 
        --static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads) {
        -+static int
        -+mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
        -+{
        - 	const unsigned int i = ctx->spawned_worker_threads;
        - 	if (i >= ctx->cfg_max_worker_threads) {
        --		return -1;  /* Oops, we hit our worker-thread limit!  No more worker threads, ever! */
        -+		return -1; /* Oops, we hit our worker-thread limit!  No more worker
        -+		              threads, ever! */
        - 	}
        - 
        - 	(void)pthread_mutex_lock(&ctx->thread_mutex);
        - #if defined(ALTERNATIVE_QUEUE)
        --	if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > 0)) {
        -+	if ((only_if_no_idle_threads) && (ctx->idle_worker_thread_count > 0)) {
        - #else
        --	if ((only_if_no_idle_threads)&&(ctx->idle_worker_thread_count > (unsigned)(ctx->sq_head-ctx->sq_tail))) {
        -+	if ((only_if_no_idle_threads)
        -+	    && (ctx->idle_worker_thread_count
        -+	        > (unsigned)(ctx->sq_head - ctx->sq_tail))) {
        - #endif
        - 		(void)pthread_mutex_unlock(&ctx->thread_mutex);
        --		return -2;  /* There are idle threads available, so no need to spawn a new worker thread now */
        -+		return -2; /* There are idle threads available, so no need to spawn a
        -+		              new worker thread now */
        - 	}
        --	ctx->idle_worker_thread_count++;  /* we do this here to avoid a race condition while the thread is starting up */
        -+	ctx->idle_worker_thread_count++; /* we do this here to avoid a race
        -+	                                    condition while the thread is starting
        -+	                                    up */
        - 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        - 
        - 	ctx->worker_connections[i].phys_ctx = ctx;
        - 	int ret = mg_start_thread_with_id(worker_thread,
        --	                            &ctx->worker_connections[i],
        --	                            &ctx->worker_threadids[i]);
        -+	                                  &ctx->worker_connections[i],
        -+	                                  &ctx->worker_threadids[i]);
        - 	if (ret == 0) {
        --		ctx->spawned_worker_threads++;  /* note that we've filled another slot in the table */
        -+		ctx->spawned_worker_threads++; /* note that we've filled another slot in
        -+		                                  the table */
        - 		DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads);
        - 	} else {
        - 		(void)pthread_mutex_lock(&ctx->thread_mutex);
        --		ctx->idle_worker_thread_count--;  /* whoops, roll-back on error */
        -+		ctx->idle_worker_thread_count--; /* whoops, roll-back on error */
        - 		(void)pthread_mutex_unlock(&ctx->thread_mutex);
        - 	}
        - 	return ret;
        -@@ -21081,11 +21122,13 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - #endif
        - 
        - 	/* Worker thread count option */
        --	workerthreadcount   = atoi(ctx->dd.config[NUM_THREADS]);
        -+	workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
        - 	prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]);
        - 
        --	if ((prespawnthreadcount < 0)||(prespawnthreadcount > workerthreadcount)) {
        --		prespawnthreadcount = workerthreadcount;  /* can't prespawn more than all of them! */
        -+	if ((prespawnthreadcount < 0)
        -+	    || (prespawnthreadcount > workerthreadcount)) {
        -+		prespawnthreadcount =
        -+		    workerthreadcount; /* can't prespawn more than all of them! */
        - 	}
        - 
        - 	if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) {
        -@@ -21352,9 +21395,10 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 	}
        - 
        - 	ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount));
        --	ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
        --	                                                   sizeof(pthread_t),
        --	                                                   ctx);
        -+	ctx->worker_threadids =
        -+	    (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
        -+	                               sizeof(pthread_t),
        -+	                               ctx);
        - 
        - 	if (ctx->worker_threadids == NULL) {
        - 		const char *err_msg = "Not enough memory for worker thread ID array";
        -@@ -21362,8 +21406,8 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 
        - 		if (error != NULL) {
        - 			error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
        --			error->code_sub =
        --			    (unsigned)ctx->cfg_max_worker_threads * (unsigned)sizeof(pthread_t);
        -+			error->code_sub = (unsigned)ctx->cfg_max_worker_threads
        -+			                  * (unsigned)sizeof(pthread_t);
        - 			mg_snprintf(NULL,
        - 			            NULL, /* No truncation check for error buffers */
        - 			            error->text,
        -diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl
        -index cada215e..c0333b21 100644
        ---- a/src/mod_mbedtls.inl
        -+++ b/src/mod_mbedtls.inl
        -@@ -189,7 +189,9 @@ mbed_ssl_accept(mbedtls_ssl_context **ssl,
        - 	}
        - 
        - #if MBEDTLS_VERSION_NUMBER >= 0x03000000
        --	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->MBEDTLS_PRIVATE(state));
        -+	DEBUG_TRACE("TLS connection %p accepted, state: %d",
        -+	            ssl,
        -+	            (*ssl)->MBEDTLS_PRIVATE(state));
        - #else
        - 	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->state);
        - #endif
        -@@ -219,7 +221,9 @@ mbed_ssl_handshake(mbedtls_ssl_context *ssl)
        - 	}
        - 
        - #if MBEDTLS_VERSION_NUMBER >= 0x03000000
        --	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->MBEDTLS_PRIVATE(state));
        -+	DEBUG_TRACE("TLS handshake rc: %d, state: %d",
        -+	            rc,
        -+	            ssl->MBEDTLS_PRIVATE(state));
        - #else
        - 	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->state);
        - #endif
        --- 
        -2.34.1
        -
        -
        -From 56ba821ed207c475cf81676f19d707aba46e9f4d Mon Sep 17 00:00:00 2001
        -From: The-EG <>
        -Date: Sun, 19 Nov 2023 14:07:36 +0100
        -Subject: [PATCH 050/173] Better error handling in Lua scripts
        -
        -(Merge code change from #1213)
        ----
        - src/mod_lua.inl | 7 ++++++-
        - 1 file changed, 6 insertions(+), 1 deletion(-)
        -
        -diff --git a/src/mod_lua.inl b/src/mod_lua.inl
        -index f1070d58..9a2164c3 100644
        ---- a/src/mod_lua.inl
        -+++ b/src/mod_lua.inl
        -@@ -3068,9 +3068,14 @@ mg_exec_lua_script(struct mg_connection *conn,
        - 		}
        - 
        - 		if (luaL_loadfile(L, path) != 0) {
        -+			mg_send_http_error(conn, 500, "Lua error:\r\n");
        - 			lua_error_handler(L);
        - 		} else {
        --			lua_pcall(L, 0, 0, -2);
        -+			int call_status = lua_pcall(L, 0, 0, 0);
        -+			if (call_status != 0) {
        -+				mg_send_http_error(conn, 500, "Lua error:\r\n");
        -+				lua_error_handler(L);
        -+			}
        - 		}
        - 		DEBUG_TRACE("Close Lua environment %p", L);
        - 		lua_close(L);
        --- 
        -2.34.1
        -
        -
        -From 05b95ea4f97387faec1d2d1f0b769aaa715ca2d9 Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Sun, 19 Nov 2023 19:31:54 +0100
        -Subject: [PATCH 051/173] Add shutdown sockets only for server
        -
        -Test possible fix for #1214
        ----
        - src/civetweb.c | 18 ++++++++++--------
        - 1 file changed, 10 insertions(+), 8 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index c8df640a..b45a264c 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -9672,8 +9672,9 @@ connect_socket(
        - 		/* Data for poll */
        - 		struct mg_pollfd pfd[2];
        - 		int pollres;
        --		int ms_wait = 10000;     /* 10 second timeout */
        --		stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */
        -+		int ms_wait = 10000;       /* 10 second timeout */
        -+		stop_flag_t nonstop = 0;   /* STOP_FLAG_ASSIGN(&nonstop, 0); */
        -+		unsigned int num_sock = 1; /* use one or two sockets */
        - 
        - 		/* For a non-blocking socket, the connect sequence is:
        - 		 * 1) call connect (will not block)
        -@@ -9683,13 +9684,14 @@ connect_socket(
        - 		pfd[0].fd = *sock;
        - 		pfd[0].events = POLLOUT;
        - 
        --		pfd[1].fd = ctx ? ctx->thread_shutdown_notification_socket : -1;
        --		pfd[1].events = POLLIN;
        -+		if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
        -+			pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
        -+			pfd[num_sock].events = POLLIN;
        -+			num_sock++;
        -+		}
        - 
        --		pollres = mg_poll(pfd,
        --		                  ctx ? 2 : 1,
        --		                  ms_wait,
        --		                  ctx ? &(ctx->stop_flag) : &nonstop);
        -+		pollres =
        -+		    mg_poll(pfd, num_sock, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
        - 
        - 		if (pollres != 1) {
        - 			/* Not connected */
        --- 
        -2.34.1
        -
        -
        -From 9da7ba333fef54db11cadab2a92ef78575672d1a Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Sun, 19 Nov 2023 19:33:54 +0100
        -Subject: [PATCH 052/173] Appveyor build issue: Remove empty line in appveyor
        - file
        -
        ----
        - appveyor.yml   |  1 -
        - src/civetweb.c | 16 +++++++++++-----
        - 2 files changed, 11 insertions(+), 6 deletions(-)
        -
        -diff --git a/appveyor.yml b/appveyor.yml
        -index 6008c5ee..fb936d1f 100644
        ---- a/appveyor.yml
        -+++ b/appveyor.yml
        -@@ -463,7 +463,6 @@ before_build:
        -     -DCIVETWEB_SSL_OPENSSL_API_1_0=NO
        -     -DCIVETWEB_SSL_OPENSSL_API_1_1=YES
        -     -DCIVETWEB_SSL_OPENSSL_API_3_0=NO
        --
        -     "%source_path%"
        -   - powershell Push-AppveyorArtifact CMakeCache.txt
        -   - cd "%source_path%"
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index b45a264c..34b4f661 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -4179,6 +4179,8 @@ send_additional_header(struct mg_connection *conn)
        - 	}
        - #endif
        - 
        -+	// Content-Security-Policy
        -+
        - 	if (header && header[0]) {
        - 		mg_response_header_add_lines(conn, header);
        - 	}
        -@@ -7724,21 +7726,25 @@ substitute_index_file(struct mg_connection *conn,
        - 		if ((root_prefix) && (fallback_root_prefix)) {
        - 			const size_t root_prefix_len = strlen(root_prefix);
        - 			if ((strncmp(path, root_prefix, root_prefix_len) == 0)) {
        -+				char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
        -+				                                  side effects if we fail */
        -+				size_t sub_path_len;
        -+
        - 				const size_t fallback_root_prefix_len =
        - 				    strlen(fallback_root_prefix);
        - 				const char *sub_path = path + root_prefix_len;
        --				while (*sub_path == '/')
        -+				while (*sub_path == '/') {
        - 					sub_path++;
        --				const size_t sub_path_len = strlen(sub_path);
        -+				}
        -+				sub_path_len = strlen(sub_path);
        - 
        --				char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
        --				                                     side effects if we fail */
        - 				if (((fallback_root_prefix_len + 1 + sub_path_len + 1)
        - 				     < sizeof(scratch_path))) {
        - 					/* The concatenations below are all safe because we
        - 					 * pre-verified string lengths above */
        -+					char *nul;
        - 					strcpy(scratch_path, fallback_root_prefix);
        --					char *nul = strchr(scratch_path, '\0');
        -+					nul = strchr(scratch_path, '\0');
        - 					if ((nul > scratch_path) && (*(nul - 1) != '/')) {
        - 						*nul++ = '/';
        - 						*nul = '\0';
        --- 
        -2.34.1
        -
        -
        -From a4d305373d2bf884292d31c5ee28050f5d9eedad Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Wed, 22 Nov 2023 07:39:01 +0100
        -Subject: [PATCH 053/173] Fixes for old coding standard
        -
        ----
        - src/civetweb.c | 31 +++++++++++++++++--------------
        - 1 file changed, 17 insertions(+), 14 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 34b4f661..0e80e77a 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -4193,6 +4193,14 @@ send_cors_header(struct mg_connection *conn)
        - 	const char *origin_hdr = mg_get_header(conn, "Origin");
        - 	const char *cors_orig_cfg =
        - 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
        -+	const char *cors_cred_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        -+	const char *cors_hdr_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
        -+	const char *cors_exphdr_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        -+	const char *cors_meth_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
        - 
        - 	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
        - 		/* Cross-origin resource sharing (CORS), see
        -@@ -4205,8 +4213,6 @@ send_cors_header(struct mg_connection *conn)
        - 		                       -1);
        - 	}
        - 
        --	const char *cors_cred_cfg =
        --	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        - 	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
        - 		/* Cross-origin resource sharing (CORS), see
        - 		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
        -@@ -4217,8 +4223,6 @@ send_cors_header(struct mg_connection *conn)
        - 		                       -1);
        - 	}
        - 
        --	const char *cors_hdr_cfg =
        --	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
        - 	if (cors_hdr_cfg && *cors_hdr_cfg) {
        - 		mg_response_header_add(conn,
        - 		                       "Access-Control-Allow-Headers",
        -@@ -4226,8 +4230,6 @@ send_cors_header(struct mg_connection *conn)
        - 		                       -1);
        - 	}
        - 
        --	const char *cors_exphdr_cfg =
        --	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        - 	if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        - 		mg_response_header_add(conn,
        - 		                       "Access-Control-Expose-Headers",
        -@@ -4235,8 +4237,6 @@ send_cors_header(struct mg_connection *conn)
        - 		                       -1);
        - 	}
        - 
        --	const char *cors_meth_cfg =
        --	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
        - 	if (cors_meth_cfg && *cors_meth_cfg) {
        - 		mg_response_header_add(conn,
        - 		                       "Access-Control-Allow-Methods",
        -@@ -7793,6 +7793,7 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        - 	ptrdiff_t match_len;
        - 	char gz_path[UTF8_PATH_MAX];
        - 	int truncated;
        -+	int i;
        - #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
        - 	char *tmp_str;
        - 	size_t tmp_str_len, sep_pos;
        -@@ -7849,7 +7850,7 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        - 		return;
        - 	}
        - 
        --	for (int i = 0; roots[i] != NULL; i++) {
        -+	for (i = 0; roots[i] != NULL; i++) {
        - 		/* Step 6: Determine the local file path from the root path and the
        - 		 * request uri. */
        - 		/* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
        -@@ -15033,6 +15034,10 @@ handle_request(struct mg_connection *conn)
        - 			    get_header(ri->http_headers,
        - 			               ri->num_headers,
        - 			               "Access-Control-Request-Headers");
        -+			const char *cors_cred_cfg =
        -+			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        -+			const char *cors_exphdr_cfg =
        -+			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        - 
        - 			gmt_time_string(date, sizeof(date), &curtime);
        - 			mg_printf(conn,
        -@@ -15047,16 +15052,12 @@ handle_request(struct mg_connection *conn)
        - 			          ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
        - 			          suggest_connection_header(conn));
        - 
        --			const char *cors_cred_cfg =
        --			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        - 			if (cors_cred_cfg && *cors_cred_cfg) {
        - 				mg_printf(conn,
        - 				          "Access-Control-Allow-Credentials: %s\r\n",
        - 				          cors_cred_cfg);
        - 			}
        - 
        --			const char *cors_exphdr_cfg =
        --			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        - 			if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        - 				mg_printf(conn,
        - 				          "Access-Control-Expose-Headers: %s\r\n",
        -@@ -20774,6 +20775,7 @@ static int
        - mg_socketpair(int *sockA, int *sockB)
        - {
        - 	int temp[2] = {-1, -1};
        -+	int asock = -1;
        - 
        - 	/** Default to unallocated */
        - 	*sockA = -1;
        -@@ -20787,11 +20789,12 @@ mg_socketpair(int *sockA, int *sockB)
        - 		set_close_on_exec(*sockA, NULL, NULL);
        - 		set_close_on_exec(*sockB, NULL, NULL);
        - 	}
        -+	(void)asock; /* not used */
        - 	return ret;
        - #else
        - 	/** No socketpair() call is available, so we'll have to roll our own
        - 	 * implementation */
        --	int asock = socket(PF_INET, SOCK_STREAM, 0);
        -+	asock = socket(PF_INET, SOCK_STREAM, 0);
        - 	if (asock >= 0) {
        - 		struct sockaddr_in addr;
        - 		struct sockaddr *pa = (struct sockaddr *)&addr;
        --- 
        -2.34.1
        -
        -
        -From 3da9aeaed53c2acd15445b7ba898fe91a989d795 Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Mon, 4 Dec 2023 08:32:52 +0100
        -Subject: [PATCH 054/173] Add wasm mime type
        -
        -Fixes #1217
        ----
        - src/civetweb.c | 1 +
        - 1 file changed, 1 insertion(+)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 0e80e77a..4cb8f485 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -8406,6 +8406,7 @@ static const struct {
        -     {".pdf", 4, "application/pdf"},
        -     {".ps", 3, "application/postscript"},
        -     {".rtf", 4, "application/rtf"},
        -+    {".wasm", 5, "application/wasm"},
        -     {".xhtml", 6, "application/xhtml+xml"},
        -     {".xsl", 4, "application/xml"},
        -     {".xslt", 5, "application/xml"},
        --- 
        -2.34.1
        -
        -
        -From 08b7241f4bce808c3a932a5fddc1181acee5c0c1 Mon Sep 17 00:00:00 2001
        -From: Sergey Linev 
        -Date: Mon, 11 Dec 2023 10:57:43 +0100
        -Subject: [PATCH 055/173] [cmake] let enable X DOM sockets
        -
        -Missing in cmake
        ----
        - CMakeLists.txt | 12 ++++++++++--
        - 1 file changed, 10 insertions(+), 2 deletions(-)
        -
        -diff --git a/CMakeLists.txt b/CMakeLists.txt
        -index a435a975..44cebaec 100644
        ---- a/CMakeLists.txt
        -+++ b/CMakeLists.txt
        -@@ -88,6 +88,11 @@ message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}")
        - option(CIVETWEB_ENABLE_WEBSOCKETS "Enable websockets connections" OFF)
        - message(STATUS "Websockets support - ${CIVETWEB_ENABLE_WEBSOCKETS}")
        - 
        -+# X DOM sockets support
        -+option(CIVETWEB_ENABLE_X_DOM_SOCKET "Enable X DOM sockets support" OFF)
        -+message(STATUS "X DOM sockets support - ${CIVETWEB_ENABLE_X_DOM_SOCKET}")
        -+
        -+
        - # Server statistics support
        - option(CIVETWEB_ENABLE_SERVER_STATS "Enable server statistics" OFF)
        - message(STATUS "Server statistics support - ${CIVETWEB_ENABLE_SERVER_STATS}")
        -@@ -497,6 +502,9 @@ endif()
        - if (CIVETWEB_ENABLE_WEBSOCKETS)
        -   add_definitions(-DUSE_WEBSOCKET)
        - endif()
        -+if (CIVETWEB_ENABLE_X_DOM_SOCKET)
        -+   add_definitions(-DUSE_X_DOM_SOCKET)
        -+endif()
        - if (CIVETWEB_ENABLE_SERVER_STATS)
        -   add_definitions(-DUSE_SERVER_STATS)
        - endif()
        -@@ -632,13 +640,13 @@ configure_file(
        - )
        - 
        - install(
        --  FILES 
        -+  FILES
        -     "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc"
        -     DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
        - )
        - 
        - install(
        --  FILES 
        -+  FILES
        -     "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-cpp.pc"
        -     DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
        - )
        --- 
        -2.34.1
        -
        -
        -From 19b70adc70ca66cc862f3fd065e354af64b7b011 Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Sat, 30 Dec 2023 11:18:38 +0100
        -Subject: [PATCH 056/173] websocket client "magic" key should be random
        -
        -Fixes #1220
        ----
        - CREDITS.md               |  1 +
        - src/civetweb.c           | 21 ++++++++++++++++++++-
        - unittest/public_server.c |  2 +-
        - 3 files changed, 22 insertions(+), 2 deletions(-)
        -
        -diff --git a/CREDITS.md b/CREDITS.md
        -index fc98b637..f1e1d150 100644
        ---- a/CREDITS.md
        -+++ b/CREDITS.md
        -@@ -225,6 +225,7 @@
        - * Torben Jonas
        - * Uilian Ries
        - * Ulrich Hertlein
        -+* videofan3d
        - * Walt Steverson
        - * wangli28
        - * webxer
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 4cb8f485..be4f4cde 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -19243,6 +19243,24 @@ websocket_client_thread(void *data)
        - #endif
        - 
        - 
        -+#if defined(USE_WEBSOCKET)
        -+static void
        -+generate_websocket_magic(char *magic25)
        -+{
        -+	uint64_t rnd;
        -+	unsigned char buffer[2 * sizeof(rnd)];
        -+
        -+	rnd = get_random();
        -+	memcpy(buffer, &rnd, sizeof(rnd));
        -+	rnd = get_random();
        -+	memcpy(buffer + sizeof(rnd), &rnd, sizeof(rnd));
        -+
        -+	size_t dst_len = 24 + 1;
        -+	mg_base64_encode(buffer, sizeof(buffer), magic25, &dst_len);
        -+}
        -+#endif
        -+
        -+
        - static struct mg_connection *
        - mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        -                                  int use_ssl,
        -@@ -19259,7 +19277,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        - 
        - #if defined(USE_WEBSOCKET)
        - 	struct websocket_client_thread_data *thread_data;
        --	static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
        -+	char magic[32];
        -+	generate_websocket_magic(magic);
        - 
        - 	const char *host = client_options->host;
        - 	int i;
        -diff --git a/unittest/public_server.c b/unittest/public_server.c
        -index 02bf5255..42625e66 100644
        ---- a/unittest/public_server.c
        -+++ b/unittest/public_server.c
        -@@ -1316,7 +1316,7 @@ START_TEST(test_request_handlers)
        - 	char cmd_buf[1024];
        - 	char *cgi_env_opt;
        - 
        --	const char *server_host = "test.domain";
        -+	const char *server_host = "localhost"; //"test.domain";
        - 
        - 	mark_point();
        - 
        --- 
        -2.34.1
        -
        -
        -From 3771053e48c2d48b9c1f956d7f70ad4d77275f3c Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Sat, 30 Dec 2023 21:54:42 +0100
        -Subject: [PATCH 057/173] Some compilers might have an issue with declarations
        - inside "for"
        -
        -Fixes #1228
        ----
        - src/civetweb.c | 3 ++-
        - 1 file changed, 2 insertions(+), 1 deletion(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index be4f4cde..7f15670e 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -15860,9 +15860,10 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
        - 	 * 'r' */
        - 	if ((len > 0) && (is_valid_port(port))) {
        - 		int bad_suffix = 0;
        -+		size_t i;
        - 
        - 		/* Parse any suffix character(s) after the port number */
        --		for (size_t i = len; i < vec->len; i++) {
        -+		for (i = len; i < vec->len; i++) {
        - 			unsigned char *opt = NULL;
        - 			switch (vec->ptr[i]) {
        - 			case 'o':
        --- 
        -2.34.1
        -
        -
        -From b59be51aac17ab2dc07e840fa9d82f532718b2a8 Mon Sep 17 00:00:00 2001
        -From: Egor Konovalov 
        -Date: Mon, 12 Feb 2024 13:56:05 +0000
        -Subject: [PATCH 058/173] Fix typo in /docs/UserManual.md
        -
        ----
        - docs/UserManual.md | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        -diff --git a/docs/UserManual.md b/docs/UserManual.md
        -index e90ceef4..13011fbb 100644
        ---- a/docs/UserManual.md
        -+++ b/docs/UserManual.md
        -@@ -180,7 +180,7 @@ This option can be specified multiple times. All specified header lines will be
        - ### allow\_index\_script\_resource `no`
        - Index scripts (like `index.cgi` or `index.lua`) may have script handled resources.
        - 
        --It this feature is activated, that /some/path/file.ext might be handled by:
        -+If this feature is activated, then /some/path/file.ext might be handled by:
        -   1. /some/path/file.ext (with PATH\_INFO='/', if ext = cgi)
        -   2. /some/path/index.lua with mg.request\_info.path\_info='/file.ext'
        -   3. /some/path/index.cgi with PATH\_INFO='/file.ext'
        --- 
        -2.34.1
        -
        -
        -From 8d7c857ebee4b101a700cce51bd7a30d5f8cf8fd Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Tue, 27 Feb 2024 21:08:32 +0100
        -Subject: [PATCH 059/173] Remove script. See #1231
        -
        ----
        - .github/workflows/fail_on_error.py | 36 ++----------------------------
        - 1 file changed, 2 insertions(+), 34 deletions(-)
        -
        -diff --git a/.github/workflows/fail_on_error.py b/.github/workflows/fail_on_error.py
        -index 29791742..f0a06ad6 100755
        ---- a/.github/workflows/fail_on_error.py
        -+++ b/.github/workflows/fail_on_error.py
        -@@ -1,34 +1,2 @@
        --#!/usr/bin/env python3
        --
        --import json
        --import sys
        --
        --# Return whether SARIF file contains error-level results
        --def codeql_sarif_contain_error(filename):
        --    with open(filename, 'r') as f:
        --        s = json.load(f)
        --
        --    for run in s.get('runs', []):
        --        rules_metadata = run['tool']['driver']['rules']
        --        if not rules_metadata:
        --            rules_metadata = run['tool']['extensions'][0]['rules']
        --
        --        for res in run.get('results', []):
        --            if 'ruleIndex' in res:
        --                rule_index = res['ruleIndex']
        --            elif 'rule' in res and 'index' in res['rule']:
        --                rule_index = res['rule']['index']
        --            else:
        --                continue
        --            try:
        --                rule_level = rules_metadata[rule_index]['defaultConfiguration']['level']
        --            except IndexError as e:
        --                print(e, rule_index, len(rules_metadata))
        --            else:
        --                if rule_level == 'error':
        --                    return True
        --    return False
        --
        --if __name__ == "__main__":
        --    if codeql_sarif_contain_error(sys.argv[1]):
        --        sys.exit(1)
        -+# content removed, because it floods my email inbox.
        -+# see https://github.com/civetweb/civetweb/issues/1231#issuecomment-1967503931
        --- 
        -2.34.1
        -
        -
        -From 4f4593ca9c54fcc841005c4d93add0ecd2c7591f Mon Sep 17 00:00:00 2001
        -From: Yiheng Cao <65160922+Crispy-fried-chicken@users.noreply.github.com>
        -Date: Wed, 28 Feb 2024 15:15:06 +0800
        -Subject: [PATCH 060/173] fix Integer overflow
        -
        ----
        - src/third_party/lua_struct.c | 10 ++++++----
        - 1 file changed, 6 insertions(+), 4 deletions(-)
        -
        -diff --git a/src/third_party/lua_struct.c b/src/third_party/lua_struct.c
        -index 17cd1a7d..505e7df2 100644
        ---- a/src/third_party/lua_struct.c
        -+++ b/src/third_party/lua_struct.c
        -@@ -88,12 +88,14 @@ typedef struct Header {
        - } Header;
        - 
        - 
        --static int getnum (const char **fmt, int df) {
        -+static int getnum (lua_State *L, const char **fmt, int df) {
        -   if (!isdigit(**fmt))  /* no number? */
        -     return df;  /* return default value */
        -   else {
        -     int a = 0;
        -     do {
        -+      if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
        -+        luaL_error(L, "integral size overflow");
        -       a = a*10 + *((*fmt)++) - '0';
        -     } while (isdigit(**fmt));
        -     return a;
        -@@ -114,9 +116,9 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
        -     case 'f':  return sizeof(float);
        -     case 'd':  return sizeof(double);
        -     case 'x': return 1;
        --    case 'c': return getnum(fmt, 1);
        -+    case 'c': return getnum(L, fmt, 1);
        -     case 'i': case 'I': {
        --      int sz = getnum(fmt, sizeof(int));
        -+      int sz = getnum(L, fmt, sizeof(int));
        -       if (sz > MAXINTSIZE)
        -         luaL_error(L, "integral size %d is larger than limit of %d",
        -                        sz, MAXINTSIZE);
        -@@ -149,7 +151,7 @@ static void controloptions (lua_State *L, int opt, const char **fmt,
        -     case '>': h->endian = BIG; return;
        -     case '<': h->endian = LITTLE; return;
        -     case '!': {
        --      int a = getnum(fmt, MAXALIGN);
        -+      int a = getnum(L, fmt, MAXALIGN);
        -       if (!isp2(a))
        -         luaL_error(L, "alignment %d is not a power of 2", a);
        -       h->align = a;
        --- 
        -2.34.1
        -
        -
        -From 95ef692f3ce6657778935cf8629bb03bf89b5bfc Mon Sep 17 00:00:00 2001
        -From: Yiheng Cao <65160922+Crispy-fried-chicken@users.noreply.github.com>
        -Date: Wed, 28 Feb 2024 15:21:15 +0800
        -Subject: [PATCH 061/173] fix the negation overflow
        -
        ----
        - src/third_party/lua-5.2.4/src/ldebug.c | 7 ++++---
        - 1 file changed, 4 insertions(+), 3 deletions(-)
        -
        -diff --git a/src/third_party/lua-5.2.4/src/ldebug.c b/src/third_party/lua-5.2.4/src/ldebug.c
        -index 96118461..1a30433b 100644
        ---- a/src/third_party/lua-5.2.4/src/ldebug.c
        -+++ b/src/third_party/lua-5.2.4/src/ldebug.c
        -@@ -116,10 +116,11 @@ static const char *upvalname (Proto *p, int uv) {
        - 
        - static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
        -   int nparams = clLvalue(ci->func)->p->numparams;
        --  if (n >= ci->u.l.base - ci->func - nparams)
        -+  int nvararg  = ci->u.l.base - ci->func - nparams;
        -+  if (n < -nvararg)
        -     return NULL;  /* no such vararg */
        -   else {
        --    *pos = ci->func + nparams + n;
        -+    *pos = ci->func + nparams - n;
        -     return "(*vararg)";  /* generic name for any vararg */
        -   }
        - }
        -@@ -131,7 +132,7 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
        -   StkId base;
        -   if (isLua(ci)) {
        -     if (n < 0)  /* access to vararg values? */
        --      return findvararg(ci, -n, pos);
        -+      return findvararg(ci, n, pos);
        -     else {
        -       base = ci->u.l.base;
        -       name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
        --- 
        -2.34.1
        -
        -
        -From bad817953013d6bb110fa4684fe82ece70d696cb Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Sun, 24 Mar 2024 17:40:05 +0100
        -Subject: [PATCH 062/173] Add MIME type for .mjs extension (#1252)
        -
        ----
        - src/civetweb.c | 1 +
        - 1 file changed, 1 insertion(+)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 7f15670e..2b6ae416 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -8402,6 +8402,7 @@ static const struct {
        -     {".iso", 4, "application/octet-stream"},
        -     {".js", 3, "application/javascript"},
        -     {".json", 5, "application/json"},
        -+    {".mjs", 4, "application/javascript"},
        -     {".msi", 4, "application/octet-stream"},
        -     {".pdf", 4, "application/pdf"},
        -     {".ps", 3, "application/postscript"},
        --- 
        -2.34.1
        -
        -
        -From a31495b9029608c00097a23498a963dd519f3f06 Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Sun, 24 Mar 2024 17:41:39 +0100
        -Subject: [PATCH 063/173] Update copyright year
        -
        ----
        - include/civetweb.h | 2 +-
        - src/civetweb.c     | 2 +-
        - 2 files changed, 2 insertions(+), 2 deletions(-)
        -
        -diff --git a/include/civetweb.h b/include/civetweb.h
        -index 78149722..5ae6a0c7 100644
        ---- a/include/civetweb.h
        -+++ b/include/civetweb.h
        -@@ -1,4 +1,4 @@
        --/* Copyright (c) 2013-2021 the Civetweb developers
        -+/* Copyright (c) 2013-2024 the Civetweb developers
        -  * Copyright (c) 2004-2013 Sergey Lyubka
        -  *
        -  * Permission is hereby granted, free of charge, to any person obtaining a copy
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 2b6ae416..e2336bd8 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -1,4 +1,4 @@
        --/* Copyright (c) 2013-2023 the Civetweb developers
        -+/* Copyright (c) 2013-2024 the Civetweb developers
        -  * Copyright (c) 2004-2013 Sergey Lyubka
        -  *
        -  * Permission is hereby granted, free of charge, to any person obtaining a copy
        --- 
        -2.34.1
        -
        -
        -From d6c9d63cb55ae61321d3da2bece9bf5875badac1 Mon Sep 17 00:00:00 2001
        -From: =?UTF-8?q?Tim=20=C4=8Cas?= 
        -Date: Wed, 3 Apr 2024 13:10:10 +0200
        -Subject: [PATCH 064/173] Fix building with MbedTLS
        -
        ----
        - src/CMakeLists.txt | 4 ++--
        - 1 file changed, 2 insertions(+), 2 deletions(-)
        -
        -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
        -index 47a9b0c8..28c4e8cc 100644
        ---- a/src/CMakeLists.txt
        -+++ b/src/CMakeLists.txt
        -@@ -47,10 +47,10 @@ endif()
        - 
        - # We need to link OpenSSL if not dynamically loading
        - if (CIVETWEB_ENABLE_SSL)
        --  if (CIVETWEB_ENBALE_MBEDTLS)
        -+  if (CIVETWEB_ENABLE_MBEDTLS)
        -     find_package(MbedTLS)
        -     include_directories(${MbedTLS_INCLUDE_DIR})
        --    message(STATUS "MbedTLS include directory: {MbedTLS_INCLUDE_DIR}")
        -+    message(STATUS "MbedTLS include directory: ${MbedTLS_INCLUDE_DIR}")
        -     target_link_libraries(civetweb-c-library ${MbedTLS_LIBRARIES})
        -   else()
        -     if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        --- 
        -2.34.1
        -
        -
        -From 8a0d7c87fe090b63ce64e8eb13783c917bef3fdb Mon Sep 17 00:00:00 2001
        -From: drew-wells <58978640+drew-wells@users.noreply.github.com>
        -Date: Mon, 22 Apr 2024 15:11:44 +0100
        -Subject: [PATCH 065/173] Fix for slow CGI
        -
        -Don't use fread() to read from CGI scripts because of possible buffering.
        ----
        - src/civetweb.c | 8 ++++----
        - 1 file changed, 4 insertions(+), 4 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index e2336bd8..5258fee1 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -6472,7 +6472,7 @@ pull_inner(FILE *fp,
        - 		}
        - 	}
        - 
        --	if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        -+	if (conn != NULL && !STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        - 		return -2;
        - 	}
        - 
        -@@ -6496,7 +6496,7 @@ pull_inner(FILE *fp,
        - 			/* See https://www.chilkatsoft.com/p/p_299.asp */
        - 			return -2;
        - 		} else {
        --			DEBUG_TRACE("recv() failed, error %d", err);
        -+			DEBUG_TRACE("read()/recv() failed, error %d", err);
        - 			return -2;
        - 		}
        - #else
        -@@ -6518,7 +6518,7 @@ pull_inner(FILE *fp,
        - 			 * (see signal(7)).
        - 			 * => stay in the while loop */
        - 		} else {
        --			DEBUG_TRACE("recv() failed, error %d", err);
        -+			DEBUG_TRACE("read()/recv() failed, error %d", err);
        - 			return -2;
        - 		}
        - #endif
        -@@ -10322,7 +10322,7 @@ send_file_data(struct mg_connection *conn,
        - 
        - 				/* Read from file, exit the loop on error */
        - 				if ((num_read =
        --				         (int)fread(buf, 1, (size_t)to_read, filep->access.fp))
        -+				         pull_inner(filep->access.fp, NULL, buf, to_read, /* unused */ 0.0))
        - 				    <= 0) {
        - 					break;
        - 				}
        --- 
        -2.34.1
        -
        -
        -From b95b7468f3ca1a7ba5b78a2fdcb72e683f5fb947 Mon Sep 17 00:00:00 2001
        -From: Turiiya <34311583+ttytm@users.noreply.github.com>
        -Date: Fri, 31 May 2024 04:41:13 +0200
        -Subject: [PATCH 066/173] Disable `mg_` reminders when NDEBUG is defined
        -
        ----
        - src/civetweb.c | 2 ++
        - 1 file changed, 2 insertions(+)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 5258fee1..d99e1f50 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -1551,11 +1551,13 @@ static void mg_snprintf(const struct mg_connection *conn,
        - #if defined(vsnprintf)
        - #undef vsnprintf
        - #endif
        -+#if !defined(NDEBUG)
        - #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
        - #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
        - #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
        - #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
        - #define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
        -+#endif
        - #if defined(_WIN32)
        - /* vsnprintf must not be used in any system,
        -  * but this define only works well for Windows. */
        --- 
        -2.34.1
        -
        -
        -From f0c6cd0bf849a34c5d3c567d48e23be4bcbf936e Mon Sep 17 00:00:00 2001
        -From: morgonf <96179229+morgonf@users.noreply.github.com>
        -Date: Tue, 11 Jun 2024 16:36:24 +0300
        -Subject: [PATCH 067/173] Update CMakeLists.txt
        -
        -To correctly fill in the keyword fields "Version" in the pkg-config files section, you must specify the value of the "VERSION" option when calling the project() command
        ----
        - CMakeLists.txt | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        -diff --git a/CMakeLists.txt b/CMakeLists.txt
        -index 44cebaec..172b1ed5 100644
        ---- a/CMakeLists.txt
        -+++ b/CMakeLists.txt
        -@@ -5,7 +5,7 @@ cmake_policy(SET CMP0054 NEW)
        - cmake_policy(SET CMP0057 NEW)
        - 
        - # Set up the project
        --project (civetweb)
        -+project (civetweb VERSION 1.16.0)
        - 
        - # Detect the platform reliably
        - if(ZEPHYR_BASE)
        --- 
        -2.34.1
        -
        -
        -From 45d4af05a03c643dcd1ec0a88c5e36a89a9683f2 Mon Sep 17 00:00:00 2001
        -From: Jamie St Martin 
        -Date: Wed, 12 Jun 2024 11:11:06 -0700
        -Subject: [PATCH 068/173] Update CIVETWEB_VERSION to 1.16.0 in CMakeLists.txt
        -
        -Our call to `find_package(civetweb 1.16.0 EXACT CONFIG REQUIRED)` is failing with version 1.16 of civetweb installed via vcpkg, because this variable is set incorrectly and therefore `civetweb-config-version.cmake` contains the wrong version.
        ----
        - CMakeLists.txt | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        -diff --git a/CMakeLists.txt b/CMakeLists.txt
        -index 44cebaec..691bdbfa 100644
        ---- a/CMakeLists.txt
        -+++ b/CMakeLists.txt
        -@@ -37,7 +37,7 @@ include(AddCXXCompilerFlag)
        - include(DetermineTargetArchitecture)
        - include(CMakeDependentOption)
        - 
        --set(CIVETWEB_VERSION "1.15.0" CACHE STRING "The version of the civetweb library")
        -+set(CIVETWEB_VERSION "1.16.0" CACHE STRING "The version of the civetweb library")
        - string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CIVETWEB_VERSION_MATCH "${CIVETWEB_VERSION}")
        - if ("${CIVETWEB_VERSION_MATCH}" STREQUAL "")
        -   message(FATAL_ERROR "Must specify a semantic version: major.minor.patch")
        --- 
        -2.34.1
        -
        -
        -From 8e82e3b78f2214d58de49e6e3b7c30c1095800db Mon Sep 17 00:00:00 2001
        -From: Turiiya <34311583+ttytm@users.noreply.github.com>
        -Date: Sun, 16 Jun 2024 19:01:07 +0200
        -Subject: [PATCH 069/173] Rename ah struct to auth_header
        -
        ----
        - src/civetweb.c | 76 +++++++++++++++++++++++++-------------------------
        - 1 file changed, 38 insertions(+), 38 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 5258fee1..be688e4f 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -8710,7 +8710,7 @@ open_auth_file(struct mg_connection *conn,
        - 
        - 
        - /* Parsed Authorization header */
        --struct ah {
        -+struct auth_header {
        - 	char *user;
        - 	int type;             /* 1 = basic, 2 = digest */
        - 	char *plain_password; /* Basic only */
        -@@ -8718,32 +8718,32 @@ struct ah {
        - };
        - 
        - 
        --/* Return 1 on success. Always initializes the ah structure. */
        -+/* Return 1 on success. Always initializes the auth_header structure. */
        - static int
        - parse_auth_header(struct mg_connection *conn,
        -                   char *buf,
        -                   size_t buf_size,
        --                  struct ah *ah)
        -+                  struct auth_header *auth_header)
        - {
        - 	char *name, *value, *s;
        --	const char *auth_header;
        -+	const char *ah;
        - 	uint64_t nonce;
        - 
        --	if (!ah || !conn) {
        -+	if (!auth_header || !conn) {
        - 		return 0;
        - 	}
        - 
        --	(void)memset(ah, 0, sizeof(*ah));
        --	auth_header = mg_get_header(conn, "Authorization");
        -+	(void)memset(auth_header, 0, sizeof(*auth_header));
        -+	ah = mg_get_header(conn, "Authorization");
        - 
        --	if (auth_header == NULL) {
        -+	if (ah == NULL) {
        - 		/* No Authorization header at all */
        - 		return 0;
        - 	}
        --	if (0 == mg_strncasecmp(auth_header, "Basic ", 6)) {
        -+	if (0 == mg_strncasecmp(ah, "Basic ", 6)) {
        - 		/* Basic Auth (we never asked for this, but some client may send it) */
        - 		char *split;
        --		const char *userpw_b64 = auth_header + 6;
        -+		const char *userpw_b64 = ah + 6;
        - 		size_t userpw_b64_len = strlen(userpw_b64);
        - 		size_t buf_len_r = buf_size;
        - 		if (mg_base64_decode(
        -@@ -8760,15 +8760,15 @@ parse_auth_header(struct mg_connection *conn,
        - 		*split = 0;
        - 
        - 		/* User name is before ':', Password is after ':'  */
        --		ah->user = buf;
        --		ah->type = 1;
        --		ah->plain_password = split + 1;
        -+		auth_header->user = buf;
        -+		auth_header->type = 1;
        -+		auth_header->plain_password = split + 1;
        - 
        - 		return 1;
        - 
        --	} else if (0 == mg_strncasecmp(auth_header, "Digest ", 7)) {
        -+	} else if (0 == mg_strncasecmp(ah, "Digest ", 7)) {
        - 		/* Digest Auth ... implemented below */
        --		ah->type = 2;
        -+		auth_header->type = 2;
        - 
        - 	} else {
        - 		/* Unknown or invalid Auth method */
        -@@ -8776,7 +8776,7 @@ parse_auth_header(struct mg_connection *conn,
        - 	}
        - 
        - 	/* Make modifiable copy of the auth header */
        --	(void)mg_strlcpy(buf, auth_header + 7, buf_size);
        -+	(void)mg_strlcpy(buf, ah + 7, buf_size);
        - 	s = buf;
        - 
        - 	/* Parse authorization header */
        -@@ -8803,29 +8803,29 @@ parse_auth_header(struct mg_connection *conn,
        - 		}
        - 
        - 		if (!strcmp(name, "username")) {
        --			ah->user = value;
        -+			auth_header->user = value;
        - 		} else if (!strcmp(name, "cnonce")) {
        --			ah->cnonce = value;
        -+			auth_header->cnonce = value;
        - 		} else if (!strcmp(name, "response")) {
        --			ah->response = value;
        -+			auth_header->response = value;
        - 		} else if (!strcmp(name, "uri")) {
        --			ah->uri = value;
        -+			auth_header->uri = value;
        - 		} else if (!strcmp(name, "qop")) {
        --			ah->qop = value;
        -+			auth_header->qop = value;
        - 		} else if (!strcmp(name, "nc")) {
        --			ah->nc = value;
        -+			auth_header->nc = value;
        - 		} else if (!strcmp(name, "nonce")) {
        --			ah->nonce = value;
        -+			auth_header->nonce = value;
        - 		}
        - 	}
        - 
        - #if !defined(NO_NONCE_CHECK)
        - 	/* Read the nonce from the response. */
        --	if (ah->nonce == NULL) {
        -+	if (auth_header->nonce == NULL) {
        - 		return 0;
        - 	}
        - 	s = NULL;
        --	nonce = strtoull(ah->nonce, &s, 10);
        -+	nonce = strtoull(auth_header->nonce, &s, 10);
        - 	if ((s == NULL) || (*s != 0)) {
        - 		return 0;
        - 	}
        -@@ -8856,7 +8856,7 @@ parse_auth_header(struct mg_connection *conn,
        - 	(void)nonce;
        - #endif
        - 
        --	return (ah->user != NULL);
        -+	return (auth_header->user != NULL);
        - }
        - 
        - 
        -@@ -8888,7 +8888,7 @@ mg_fgets(char *buf, size_t size, struct mg_file *filep)
        - #if !defined(NO_FILESYSTEMS)
        - struct read_auth_file_struct {
        - 	struct mg_connection *conn;
        --	struct ah ah;
        -+	struct auth_header auth_header;
        - 	const char *domain;
        - 	char buf[256 + 256 + 40];
        - 	const char *f_user;
        -@@ -8989,9 +8989,9 @@ read_auth_file(struct mg_file *filep,
        - 		*(char *)(workdata->f_ha1) = 0;
        - 		(workdata->f_ha1)++;
        - 
        --		if (!strcmp(workdata->ah.user, workdata->f_user)
        -+		if (!strcmp(workdata->auth_header.user, workdata->f_user)
        - 		    && !strcmp(workdata->domain, workdata->f_domain)) {
        --			switch (workdata->ah.type) {
        -+			switch (workdata->auth_header.type) {
        - 			case 1: /* Basic */
        - 			{
        - 				char md5[33];
        -@@ -9000,7 +9000,7 @@ read_auth_file(struct mg_file *filep,
        - 				       ":",
        - 				       workdata->domain,
        - 				       ":",
        --				       workdata->ah.plain_password,
        -+				       workdata->auth_header.plain_password,
        - 				       NULL);
        - 				return 0 == memcmp(workdata->f_ha1, md5, 33);
        - 			}
        -@@ -9008,12 +9008,12 @@ read_auth_file(struct mg_file *filep,
        - 				return check_password_digest(
        - 				    workdata->conn->request_info.request_method,
        - 				    workdata->f_ha1,
        --				    workdata->ah.uri,
        --				    workdata->ah.nonce,
        --				    workdata->ah.nc,
        --				    workdata->ah.cnonce,
        --				    workdata->ah.qop,
        --				    workdata->ah.response);
        -+				    workdata->auth_header.uri,
        -+				    workdata->auth_header.nonce,
        -+				    workdata->auth_header.nc,
        -+				    workdata->auth_header.cnonce,
        -+				    workdata->auth_header.qop,
        -+				    workdata->auth_header.response);
        - 			default: /* None/Other/Unknown */
        - 				return 0;
        - 			}
        -@@ -9038,13 +9038,13 @@ authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
        - 	memset(&workdata, 0, sizeof(workdata));
        - 	workdata.conn = conn;
        - 
        --	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
        -+	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.auth_header)) {
        - 		return 0;
        - 	}
        - 
        - 	/* CGI needs it as REMOTE_USER */
        - 	conn->request_info.remote_user =
        --	    mg_strdup_ctx(workdata.ah.user, conn->phys_ctx);
        -+	    mg_strdup_ctx(workdata.auth_header.user, conn->phys_ctx);
        - 
        - 	if (realm) {
        - 		workdata.domain = realm;
        --- 
        -2.34.1
        -
        -
        -From e5cfc072cf21c45d786f45ddc3bc397706ec36b3 Mon Sep 17 00:00:00 2001
        -From: Martin Mahringer 
        -Date: Fri, 5 Jul 2024 14:01:11 +0200
        -Subject: [PATCH 070/173] Only poll thread_shutdown_notification_socket for
        - server context
        -
        -The socket thread_shutdown_notification_socket must only be used for
        -context type CONTEXT_SERVER.
        ----
        - src/civetweb.c | 54 +++++++++++++++++++++++++++++++++++---------------
        - 1 file changed, 38 insertions(+), 16 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 5258fee1..46c1545c 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -6211,13 +6211,18 @@ push_inner(struct mg_context *ctx,
        - 			/* For sockets, wait for the socket using poll */
        - 			struct mg_pollfd pfd[2];
        - 			int pollres;
        -+			unsigned int num_sock = 1;
        - 
        - 			pfd[0].fd = sock;
        - 			pfd[0].events = POLLOUT;
        - 
        --			pfd[1].fd = ctx->thread_shutdown_notification_socket;
        --			pfd[1].events = POLLIN;
        --			pollres = mg_poll(pfd, 2, (int)(ms_wait), &(ctx->stop_flag));
        -+			if (ctx->context_type == CONTEXT_SERVER) {
        -+				pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
        -+				pfd[num_sock].events = POLLIN;
        -+				num_sock++;
        -+			}
        -+
        -+			pollres = mg_poll(pfd, num_sock, (int)(ms_wait), &(ctx->stop_flag));
        - 			if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
        - 				return -2;
        - 			}
        -@@ -6328,6 +6333,7 @@ pull_inner(FILE *fp,
        - 		struct mg_pollfd pfd[2];
        - 		int to_read;
        - 		int pollres;
        -+		unsigned int num_sock = 1;
        - 
        - 		to_read = mbedtls_ssl_get_bytes_avail(conn->ssl);
        - 
        -@@ -6343,13 +6349,16 @@ pull_inner(FILE *fp,
        - 			pfd[0].fd = conn->client.sock;
        - 			pfd[0].events = POLLIN;
        - 
        --			pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
        --			pfd[1].events = POLLIN;
        -+			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+				pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
        -+				pfd[num_sock].events = POLLIN;
        -+				num_sock++;
        -+			}
        - 
        - 			to_read = len;
        - 
        - 			pollres = mg_poll(pfd,
        --			                  2,
        -+			                  num_sock,
        - 			                  (int)(timeout * 1000.0),
        - 			                  &(conn->phys_ctx->stop_flag));
        - 
        -@@ -6386,6 +6395,7 @@ pull_inner(FILE *fp,
        - 		int ssl_pending;
        - 		struct mg_pollfd pfd[2];
        - 		int pollres;
        -+		unsigned int num_sock = 1;
        - 
        - 		if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
        - 			/* We already know there is no more data buffered in conn->buf
        -@@ -6398,11 +6408,15 @@ pull_inner(FILE *fp,
        - 		} else {
        - 			pfd[0].fd = conn->client.sock;
        - 			pfd[0].events = POLLIN;
        --			pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
        --			pfd[1].events = POLLIN;
        -+
        -+			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+				pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
        -+				pfd[num_sock].events = POLLIN;
        -+				num_sock++;
        -+			}
        - 
        - 			pollres = mg_poll(pfd,
        --			                  2,
        -+			                  num_sock,
        - 			                  (int)(timeout * 1000.0),
        - 			                  &(conn->phys_ctx->stop_flag));
        - 			if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        -@@ -6442,15 +6456,19 @@ pull_inner(FILE *fp,
        - 	} else {
        - 		struct mg_pollfd pfd[2];
        - 		int pollres;
        -+		unsigned int num_sock = 1;
        - 
        - 		pfd[0].fd = conn->client.sock;
        - 		pfd[0].events = POLLIN;
        - 
        --		pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
        --		pfd[1].events = POLLIN;
        -+		if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+			pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
        -+			pfd[num_sock].events = POLLIN;
        -+			num_sock++;
        -+		}
        - 
        - 		pollres = mg_poll(pfd,
        --		                  2,
        -+		                  num_sock,
        - 		                  (int)(timeout * 1000.0),
        - 		                  &(conn->phys_ctx->stop_flag));
        - 		if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        -@@ -16785,16 +16803,20 @@ sslize(struct mg_connection *conn,
        - 					 * This is typical for non-blocking sockets. */
        - 					struct mg_pollfd pfd[2];
        - 					int pollres;
        -+					unsigned int num_sock = 1;
        - 					pfd[0].fd = conn->client.sock;
        - 					pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT)
        - 					                 || (err == SSL_ERROR_WANT_WRITE))
        - 					                    ? POLLOUT
        - 					                    : POLLIN;
        - 
        --					pfd[1].fd =
        --					    conn->phys_ctx->thread_shutdown_notification_socket;
        --					pfd[1].events = POLLIN;
        --					pollres = mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag));
        -+					if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+						pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket;
        -+						pfd[num_sock].events = POLLIN;
        -+						num_sock++;
        -+					}
        -+
        -+					pollres = mg_poll(pfd, num_sock, 50, &(conn->phys_ctx->stop_flag));
        - 					if (pollres < 0) {
        - 						/* Break if error occurred (-1)
        - 						 * or server shutdown (-2) */
        --- 
        -2.34.1
        -
        -
        -From b8773bda25100339fb8ea58e4061936ef75c930a Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Thu, 11 Jul 2024 21:39:48 +0200
        -Subject: [PATCH 071/173] Remove donation links
        -
        ----
        - docs/README.md   |  3 ---
        - test/donate.html | 32 --------------------------------
        - 2 files changed, 35 deletions(-)
        - delete mode 100644 test/donate.html
        -
        -diff --git a/docs/README.md b/docs/README.md
        -index 76e57732..d3fb0e5d 100644
        ---- a/docs/README.md
        -+++ b/docs/README.md
        -@@ -31,9 +31,6 @@ Source releases can be found on GitHub
        - 
        - A security policy can be found in [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md).
        - 
        --CivetWeb is free of charge, however, donations for maintenance are welcome:
        --[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=88ZLXZ6U77GJU)
        --
        - 
        - Documentation
        - ---------------
        -diff --git a/test/donate.html b/test/donate.html
        -deleted file mode 100644
        -index da6e3978..00000000
        ---- a/test/donate.html
        -+++ /dev/null
        -@@ -1,32 +0,0 @@
        --
        --
        --
        --
        --
        --CivetWeb web server maintenance (#523):
        --
        --

        --

        -- -- -- -- --
        --

        -- --

        -- --donate -- --

        -- --

        -- --donate -- --

        -- -- -- -- -- --- -2.34.1 - - -From 51f8443b632ddd1b126c3d182e8587f56a728f61 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Thu, 11 Jul 2024 21:40:52 +0200 -Subject: [PATCH 072/173] Format source after merge - ---- - src/civetweb.c | 24 +++++++++++++++++------- - 1 file changed, 17 insertions(+), 7 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index e8e60bf5..9e098045 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -6350,7 +6350,8 @@ pull_inner(FILE *fp, - pfd[0].events = POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { -- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[num_sock].fd = -+ conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } -@@ -6410,7 +6411,8 @@ pull_inner(FILE *fp, - pfd[0].events = POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { -- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[num_sock].fd = -+ conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } -@@ -6462,7 +6464,8 @@ pull_inner(FILE *fp, - pfd[0].events = POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { -- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[num_sock].fd = -+ conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } -@@ -10339,8 +10342,11 @@ send_file_data(struct mg_connection *conn, - } - - /* Read from file, exit the loop on error */ -- if ((num_read = -- pull_inner(filep->access.fp, NULL, buf, to_read, /* unused */ 0.0)) -+ if ((num_read = pull_inner(filep->access.fp, -+ NULL, -+ buf, -+ to_read, -+ /* unused */ 0.0)) - <= 0) { - break; - } -@@ -16811,12 +16817,16 @@ sslize(struct mg_connection *conn, - : POLLIN; - - if (conn->phys_ctx->context_type == CONTEXT_SERVER) { -- pfd[num_sock].fd = conn->phys_ctx->thread_shutdown_notification_socket; -+ pfd[num_sock].fd = -+ conn->phys_ctx->thread_shutdown_notification_socket; - pfd[num_sock].events = POLLIN; - num_sock++; - } - -- pollres = mg_poll(pfd, num_sock, 50, &(conn->phys_ctx->stop_flag)); -+ pollres = mg_poll(pfd, -+ num_sock, -+ 50, -+ &(conn->phys_ctx->stop_flag)); - if (pollres < 0) { - /* Break if error occurred (-1) - * or server shutdown (-2) */ --- -2.34.1 - - -From 715da022e9851141c0a510ce6b2db8d2a8654d09 Mon Sep 17 00:00:00 2001 -From: yafiyogi <1446544+yafiyogi@users.noreply.github.com> -Date: Sat, 20 Jul 2024 15:12:08 +0100 -Subject: [PATCH 073/173] Add cmake option CIVETWEB_ENABLE_HTTP2 to allow HTTP2 - builds. - ---- - CMakeLists.txt | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 07abf861..45a17adc 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -80,6 +80,10 @@ message(STATUS "Disable caching support - ${CIVETWEB_DISABLE_CACHING}") - option(CIVETWEB_ENABLE_CXX "Enables the C++ wrapper library" OFF) - message(STATUS "C++ wrappers - ${CIVETWEB_ENABLE_CXX}") - -+# HTTP2 Support -+option(CIVETWEB_ENABLE_HTTP2 "Enables HTTP2 support" OFF) -+message(STATUS "Use HTPP2 - ${CIVETWEB_ENABLE_HTTP2}") -+ - # IP Version 6 - option(CIVETWEB_ENABLE_IPV6 "Enables the IP version 6 support" ON) - message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}") -@@ -496,6 +500,10 @@ if (${CMAKE_BUILD_TYPE} MATCHES "[Dd]ebug") - add_definitions(-O0) - add_definitions(-g) - endif() -+if (CIVETWEB_ENABLE_HTTP2) -+ add_definitions(-DUSE_HTTP2) -+ add_definitions(-Wno-gnu-zero-variadic-macro-arguments) -+endif() - if (CIVETWEB_ENABLE_IPV6) - add_definitions(-DUSE_IPV6) - endif() -@@ -699,4 +707,3 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_PACKAGE_DEPENDS}") - - # Finalize CPack settings - include(CPack) -- --- -2.34.1 - - -From 1a5c587add95d257fbfca98c90e03930bc563256 Mon Sep 17 00:00:00 2001 -From: yafiyogi <1446544+yafiyogi@users.noreply.github.com> -Date: Mon, 22 Jul 2024 11:14:04 +0100 -Subject: [PATCH 074/173] Remove - 'add_definitions(-Wno-gnu-zero-variadic-macro-arguments)'. - ---- - CMakeLists.txt | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 45a17adc..e1aa002b 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -502,7 +502,6 @@ if (${CMAKE_BUILD_TYPE} MATCHES "[Dd]ebug") - endif() - if (CIVETWEB_ENABLE_HTTP2) - add_definitions(-DUSE_HTTP2) -- add_definitions(-Wno-gnu-zero-variadic-macro-arguments) - endif() - if (CIVETWEB_ENABLE_IPV6) - add_definitions(-DUSE_IPV6) --- -2.34.1 - - -From 0b50decfa0a5864e60d2793f6c487c5d754411a6 Mon Sep 17 00:00:00 2001 -From: tim -Date: Tue, 6 Aug 2024 02:25:30 +0100 -Subject: [PATCH 075/173] [#1276] add test cases for multipart form data - ---- - unittest/public_server.c | 569 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 569 insertions(+) - -diff --git a/unittest/public_server.c b/unittest/public_server.c -index 42625e66..efca8a16 100644 ---- a/unittest/public_server.c -+++ b/unittest/public_server.c -@@ -2922,6 +2922,575 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -+ /* Handle form: "POST multipart/form-data" without trailing CRLF*/ -+ /* -+ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ */ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2366); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" with epilogue*/ -+ /* -+ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ * -+ * epilogue := discard-text -+ * -+ * discard-text := *(*text CRLF) *text -+ * -+ * text := -+ */ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n" -+ "epilogue\r\n" -+ "epilogue\r\n" -+ "\r\n" -+ "epilogue\r\n" -+ "\r\n" -+ "\r\n" -+ "\r\n" -+ "epilogue\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2366); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" with preamble*/ -+ /* -+ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ * -+ * preamble := discard-text -+ * -+ * discard-text := *(*text CRLF) *text -+ * -+ * text := -+ */ -+ multipart_body = -+ "\r\n" -+ "\r\npreamble" -+ "\r\npreamble" -+ "\r\npreamble" -+ "\r\n" -+ "\r\npreamble" -+ "\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2366); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ -+ /* Handle form: "POST multipart/form-data" with transport padding*/ -+ /* -+ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ * -+ * transport-padding := *LWSP-char -+ * -+ * LWSP-char := SPACE / HTAB -+ */ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388 \r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2366); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); - - /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ - client_conn = --- -2.34.1 - - -From ee8e08cc4a364f3f0645b5c380223031447d0e25 Mon Sep 17 00:00:00 2001 -From: tim -Date: Thu, 8 Aug 2024 02:36:33 +0100 -Subject: [PATCH 076/173] [#1276] mutipart form data - -- Add some tests -- Add fixes for preamble, epilogue, transport padding, boundary lines ---- - src/civetweb.c | 6 +- - src/handle_form.inl | 78 +++++-- - unittest/public_server.c | 460 ++++++++++++++++++++++++++++++--------- - 3 files changed, 416 insertions(+), 128 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 9e098045..bf44a0f7 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -10979,7 +10979,7 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]) - } - - /* here *dp is either 0 or '\n' */ -- /* in any case, we have a new header */ -+ /* in any case, we have found a complete header */ - num_headers = i + 1; - - if (*dp) { -@@ -10988,9 +10988,11 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]) - *buf = dp; - - if ((dp[0] == '\r') || (dp[0] == '\n')) { -- /* This is the end of the header */ -+ /* We've had CRLF twice in a row -+ * This is the end of the headers */ - break; - } -+ /* continue within the loop, find the next header */ - } else { - *buf = dp; - break; -diff --git a/src/handle_form.inl b/src/handle_form.inl -index be477a05..b22f17ef 100644 ---- a/src/handle_form.inl -+++ b/src/handle_form.inl -@@ -162,14 +162,17 @@ search_boundary(const char *buf, - const char *boundary, - size_t boundary_len) - { -- /* We must do a binary search here, not a string search, since the buffer -- * may contain '\x00' bytes, if binary data is transferred. */ -- int clen = (int)buf_len - (int)boundary_len - 4; -+ char *boundary_start = "\r\n--"; -+ size_t boundary_start_len = strlen(boundary_start); -+ -+ /* We must do a binary search here, not a string search, since the -+ * buffer may contain '\x00' bytes, if binary data is transferred. */ -+ int clen = (int)buf_len - (int)boundary_len - boundary_start_len; - int i; - - for (i = 0; i <= clen; i++) { -- if (!memcmp(buf + i, "\r\n--", 4)) { -- if (!memcmp(buf + i + 4, boundary, boundary_len)) { -+ if (!memcmp(buf + i, boundary_start, boundary_start_len)) { -+ if (!memcmp(buf + i + boundary_start_len, boundary, boundary_len)) { - return buf + i; - } - } -@@ -624,6 +627,7 @@ mg_handle_form_request(struct mg_connection *conn, - } - - /* Copy boundary string to variable "boundary" */ -+ /* fbeg is pointer to start of value of boundary */ - fbeg = content_type + bl + 9; - bl = strlen(fbeg); - boundary = (char *)mg_malloc(bl + 1); -@@ -701,43 +705,71 @@ mg_handle_form_request(struct mg_connection *conn, - return -1; - } - -+ /* @see https://www.rfc-editor.org/rfc/rfc2046.html#section-5.1.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ */ -+ - if (part_no == 0) { -- int d = 0; -- while ((d < buf_fill) && (buf[d] != '-')) { -- d++; -+ size_t preamble_length = 0; -+ /* skip over the preamble until we find a complete boundary */ -+ /* +2 for the -- preceding the boundary */ -+ while ((preamble_length < buf_fill - bl) -+ && strncmp(buf + preamble_length + 2, boundary, bl)) { -+ preamble_length++; - } -- if ((d > 0) && (buf[d] == '-')) { -- memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d); -- buf_fill -= d; -+ /* reset the start of buf to remove the preamble */ -+ if (0 == strncmp(buf + preamble_length + 2, boundary, bl)) { -+ memmove(buf, -+ buf + preamble_length, -+ (unsigned)buf_fill - (unsigned)preamble_length); -+ buf_fill -= preamble_length; - buf[buf_fill] = 0; - } - } - -- if (buf[0] != '-' || buf[1] != '-') { -+ /* either it starts with a boundary -+ * or we couldn't find a boundary at all in the body -+ * or we didn't have a terminating boundary */ -+ if (buf_fill < (bl + 2) || strncmp(buf, "--", 2) -+ || strncmp(buf + 2, boundary, bl)) { - /* Malformed request */ - mg_free(boundary); - return -1; - } -- if (0 != strncmp(buf + 2, boundary, bl)) { -- /* Malformed request */ -- mg_free(boundary); -- return -1; -+ -+ /* skip the -- */ -+ char *boundary_start = buf + 2; -+ size_t transport_padding = 0; -+ while (boundary_start[bl + transport_padding] == ' ' -+ || boundary_start[bl + transport_padding] == '\t') { -+ transport_padding++; - } -- if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') { -- /* Every part must end with \r\n, if there is another part. -- * The end of the request has an extra -- */ -- if (((size_t)buf_fill != (size_t)(bl + 6)) -- || (strncmp(buf + bl + 2, "--\r\n", 4))) { -+ char *boundary_end = boundary_start + bl + transport_padding; -+ -+ /* after the transport padding, if the boundary isn't -+ * immediately followed by a \r\n then it is either... */ -+ if (strncmp(boundary_end, "\r\n", 2)) -+ { -+ /* ...the final boundary, and it is followed by --, (in which -+ * case it's the end of the request) or it's a malformed -+ * request */ -+ if (strncmp(boundary_end, "--", 2)) { - /* Malformed request */ - mg_free(boundary); - return -1; - } -- /* End of the request */ -+ /* Ingore any epilogue here */ - break; - } - -+ /* skip the \r\n */ -+ hbuf = boundary_end + 2; - /* Next, we need to get the part header: Read until \r\n\r\n */ -- hbuf = buf + bl + 4; - hend = strstr(hbuf, "\r\n\r\n"); - if (!hend) { - /* Malformed request */ -diff --git a/unittest/public_server.c b/unittest/public_server.c -index efca8a16..2d244297 100644 ---- a/unittest/public_server.c -+++ b/unittest/public_server.c -@@ -57,6 +57,79 @@ - #define SLEEP_AFTER_MG_STOP (5) - - -+#ifdef HEXDUMP -+#else -+void -+hexDumpth(const char *desc, const void *addr, const int len, int perLine) -+{ -+ // Silently ignore silly per-line values. -+ -+ if (perLine < 4 || perLine > 64) -+ perLine = 16; -+ -+ int i; -+ unsigned char buff[perLine + 1]; -+ const unsigned char *pc = (const unsigned char *)addr; -+ -+ // Output description if given. -+ -+ if (desc != NULL) -+ fprintf(stderr, "%s:\n", desc); -+ -+ // Length checks. -+ -+ if (len == 0) { -+ fprintf(stderr, " ZERO LENGTH\n"); -+ return; -+ } -+ if (len < 0) { -+ fprintf(stderr, " NEGATIVE LENGTH: %d\n", len); -+ return; -+ } -+ -+ // Process every byte in the data. -+ -+ for (i = 0; i < len; i++) { -+ // Multiple of perLine means new or first line (with line offset). -+ -+ if ((i % perLine) == 0) { -+ // Only print previous-line ASCII buffer for lines beyond first. -+ -+ if (i != 0) -+ fprintf(stderr, " %s\n", buff); -+ -+ // Output the offset of current line. -+ -+ fprintf(stderr, " %04x ", i); -+ } -+ -+ // Now the hex code for the specific character. -+ -+ fprintf(stderr, " %02x", pc[i]); -+ -+ // And buffer a printable ASCII character for later. -+ -+ if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better. -+ buff[i % perLine] = '.'; -+ else -+ buff[i % perLine] = pc[i]; -+ buff[(i % perLine) + 1] = '\0'; -+ } -+ -+ // Pad out last line if not exactly perLine characters. -+ -+ while ((i % perLine) != 0) { -+ fprintf(stderr, " "); -+ i++; -+ } -+ -+ // And print the final ASCII buffer. -+ -+ fprintf(stderr, " %s\n", buff); -+} -+#define HEXDUMP -+#endif -+ - /* Try to communicate with an external http server. */ - static const char * - get_external_server_ip(void) -@@ -2608,6 +2681,36 @@ FormGet(struct mg_connection *conn, void *cbdata) - } - - -+static int -+FormError(struct mg_connection *conn, void *cbdata) -+{ -+ const struct mg_request_info *req_info = mg_get_request_info(conn); -+ int ret; -+ struct mg_form_data_handler fdh = {NULL, NULL, NULL, NULL}; -+ -+ (void)cbdata; -+ -+ ck_assert(req_info != NULL); -+ -+ mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n"); -+ fdh.user_data = (void *)&g_field_found_return; -+ -+ /* Call the form handler */ -+ g_field_step = 0; -+ g_field_found_return = MG_FORM_FIELD_STORAGE_GET; -+ ret = mg_handle_form_request(conn, &fdh); -+ g_field_found_return = -888; -+ ck_assert_int_eq(ret, -1); -+ ck_assert_int_eq(g_field_step, 0); -+ mg_printf(conn, "%i\r\n", ret); -+ g_field_step = 1000; -+ -+ mark_point(); -+ -+ return 1; -+} -+ -+ - static int - FormStore(struct mg_connection *conn, - void *cbdata, -@@ -2727,6 +2830,7 @@ START_TEST(test_handle_form) - ck_assert_str_eq(opt, "8884"); - - mg_set_request_handler(ctx, "/handle_form", FormGet, NULL); -+ mg_set_request_handler(ctx, "/handle_form_error", FormError, NULL); - mg_set_request_handler(ctx, "/handle_form_store", FormStore1, NULL); - mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, NULL); - -@@ -2797,6 +2901,23 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -+ /* -+ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ * -+ * preamble := discard-text -+ * epilogue := discard-text -+ * -+ * discard-text := *(*text CRLF) *text -+ * -+ * text := -+ */ -+ - /* Handle form: "POST multipart/form-data" */ - multipart_body = - "--multipart-form-data-boundary--see-RFC-2388\r\n" -@@ -2922,16 +3043,102 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -+ -+ /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ -+ /* use the most universal possible (no edge cases) body*/ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "%s", -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Transfer-Encoding: chunked\r\n" -+ "\r\n"); -+ -+ ck_assert(client_conn != NULL); -+ -+ body_len = strlen(multipart_body); -+ chunk_len = 1; -+ body_sent = 0; -+ while (body_len > body_sent) { -+ if (chunk_len > (body_len - body_sent)) { -+ chunk_len = body_len - body_sent; -+ } -+ ck_assert_int_gt((int)chunk_len, 0); -+ mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); -+ mg_write(client_conn, multipart_body + body_sent, chunk_len); -+ mg_printf(client_conn, "\r\n"); -+ body_sent += chunk_len; -+ chunk_len = (chunk_len % 40) + 1; -+ } -+ mg_printf(client_conn, "0\r\n\r\n"); -+ -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" with chunked transfer -+ * encoding, using a quoted boundary string */ -+ client_conn = mg_download( -+ "localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "%s", -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=\"multipart-form-data-boundary--see-RFC-2388\"\r\n" -+ "Transfer-Encoding: chunked\r\n" -+ "\r\n"); -+ -+ ck_assert(client_conn != NULL); -+ -+ body_len = strlen(multipart_body); -+ chunk_len = 1; -+ body_sent = 0; -+ while (body_len > body_sent) { -+ if (chunk_len > (body_len - body_sent)) { -+ chunk_len = body_len - body_sent; -+ } -+ ck_assert_int_gt((int)chunk_len, 0); -+ mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); -+ mg_write(client_conn, multipart_body + body_sent, chunk_len); -+ mg_printf(client_conn, "\r\n"); -+ body_sent += chunk_len; -+ chunk_len = (chunk_len % 40) + 1; -+ } -+ mg_printf(client_conn, "0\r\n\r\n"); -+ -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ - /* Handle form: "POST multipart/form-data" without trailing CRLF*/ -- /* -- * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -- * -- * multipart-body := [preamble CRLF] -- * dash-boundary transport-padding CRLF -- * body-part *encapsulation -- * close-delimiter transport-padding -- * [CRLF epilogue] -- */ - multipart_body = - "--multipart-form-data-boundary--see-RFC-2388\r\n" - "Content-Disposition: form-data; name=\"textin\"\r\n" -@@ -3057,21 +3264,6 @@ START_TEST(test_handle_form) - mg_close_connection(client_conn); - - /* Handle form: "POST multipart/form-data" with epilogue*/ -- /* -- * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -- * -- * multipart-body := [preamble CRLF] -- * dash-boundary transport-padding CRLF -- * body-part *encapsulation -- * close-delimiter transport-padding -- * [CRLF epilogue] -- * -- * epilogue := discard-text -- * -- * discard-text := *(*text CRLF) *text -- * -- * text := -- */ - multipart_body = - "--multipart-form-data-boundary--see-RFC-2388\r\n" - "Content-Disposition: form-data; name=\"textin\"\r\n" -@@ -3164,16 +3356,16 @@ START_TEST(test_handle_form) - "\r\n" - "Text area default text.\r\n" - "--multipart-form-data-boundary--see-RFC-2388--\r\n" -- "epilogue\r\n" -- "epilogue\r\n" -- "\r\n" -- "epilogue\r\n" -- "\r\n" -- "\r\n" -- "\r\n" -- "epilogue\r\n"; -+ "epilogue\r\n" -+ "epilogue\r\n" -+ "\r\n" -+ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" -+ "\r\n" -+ "\r\n" -+ "\r\n" -+ "epilogue\r\n"; - body_len = strlen(multipart_body); -- ck_assert_uint_eq(body_len, 2366); /* not required */ -+ ck_assert_uint_eq(body_len, 2453); /* not required */ - - client_conn = - mg_download("localhost", -@@ -3228,6 +3420,9 @@ START_TEST(test_handle_form) - "\r\n" - "\r\npreamble" - "\r\n" -+ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" -+ "\r\n" -+ "\r\n\t\t\t \t\t\t" - "\r\n" - "\r\n" - "--multipart-form-data-boundary--see-RFC-2388\r\n" -@@ -3322,7 +3517,7 @@ START_TEST(test_handle_form) - "Text area default text.\r\n" - "--multipart-form-data-boundary--see-RFC-2388--\r\n"; - body_len = strlen(multipart_body); -- ck_assert_uint_eq(body_len, 2366); /* not required */ -+ ck_assert_uint_eq(body_len, 2478); /* not required */ - - client_conn = - mg_download("localhost", -@@ -3353,21 +3548,7 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -- - /* Handle form: "POST multipart/form-data" with transport padding*/ -- /* -- * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -- * -- * multipart-body := [preamble CRLF] -- * dash-boundary transport-padding CRLF -- * body-part *encapsulation -- * close-delimiter transport-padding -- * [CRLF epilogue] -- * -- * transport-padding := *LWSP-char -- * -- * LWSP-char := SPACE / HTAB -- */ - multipart_body = - "--multipart-form-data-boundary--see-RFC-2388 \r\n" - "Content-Disposition: form-data; name=\"textin\"\r\n" -@@ -3461,7 +3642,7 @@ START_TEST(test_handle_form) - "Text area default text.\r\n" - "--multipart-form-data-boundary--see-RFC-2388--\r\n"; - body_len = strlen(multipart_body); -- ck_assert_uint_eq(body_len, 2366); /* not required */ -+ ck_assert_uint_eq(body_len, 2382); /* not required */ - - client_conn = - mg_download("localhost", -@@ -3492,40 +3673,125 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -- /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ -+ /* Handle form: "POST multipart/form-data" with custom name fields in the -+ * Content-Disposition */ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; " -+ "custom1name=\"1\"; " -+ "custom2name=\"2\"; " -+ "custom3name=\"3\"; " -+ "custom4name=\"4\"; " -+ "name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2439); /* not required */ -+ - client_conn = - mg_download("localhost", - 8884, - 0, - ebuf, - sizeof(ebuf), -- "%s", - "POST /handle_form HTTP/1.1\r\n" - "Host: localhost:8884\r\n" - "Connection: close\r\n" - "Content-Type: multipart/form-data; " - "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -- "Transfer-Encoding: chunked\r\n" -- "\r\n"); -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); - - ck_assert(client_conn != NULL); -- -- body_len = strlen(multipart_body); -- chunk_len = 1; -- body_sent = 0; -- while (body_len > body_sent) { -- if (chunk_len > (body_len - body_sent)) { -- chunk_len = body_len - body_sent; -- } -- ck_assert_int_gt((int)chunk_len, 0); -- mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); -- mg_write(client_conn, multipart_body + body_sent, chunk_len); -- mg_printf(client_conn, "\r\n"); -- body_sent += chunk_len; -- chunk_len = (chunk_len % 40) + 1; -- } -- mg_printf(client_conn, "0\r\n\r\n"); -- - for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { - test_sleep(1); - if (g_field_step == 1000) { -@@ -3538,41 +3804,29 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -- /* Handle form: "POST multipart/form-data" with chunked transfer -- * encoding, using a quoted boundary string */ -- client_conn = mg_download( -- "localhost", -- 8884, -- 0, -- ebuf, -- sizeof(ebuf), -- "%s", -- "POST /handle_form HTTP/1.1\r\n" -- "Host: localhost:8884\r\n" -- "Connection: close\r\n" -- "Content-Type: multipart/form-data; " -- "boundary=\"multipart-form-data-boundary--see-RFC-2388\"\r\n" -- "Transfer-Encoding: chunked\r\n" -- "\r\n"); -- -- ck_assert(client_conn != NULL); -- -+ /* Handle form error cases */ -+ /* Handle form: "POST multipart/form-data" empty body */ -+ multipart_body = ""; - body_len = strlen(multipart_body); -- chunk_len = 1; -- body_sent = 0; -- while (body_len > body_sent) { -- if (chunk_len > (body_len - body_sent)) { -- chunk_len = body_len - body_sent; -- } -- ck_assert_int_gt((int)chunk_len, 0); -- mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len); -- mg_write(client_conn, multipart_body + body_sent, chunk_len); -- mg_printf(client_conn, "\r\n"); -- body_sent += chunk_len; -- chunk_len = (chunk_len % 40) + 1; -- } -- mg_printf(client_conn, "0\r\n\r\n"); -+ ck_assert_uint_eq(body_len, 0); /* not required */ - -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form_error HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); - for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { - test_sleep(1); - if (g_field_step == 1000) { --- -2.34.1 - - -From 644d3cdeaa8939e661cd26c9005b901130f44f2a Mon Sep 17 00:00:00 2001 -From: tim -Date: Thu, 8 Aug 2024 23:28:30 +0100 -Subject: [PATCH 077/173] [#1276] remove debug method - ---- - unittest/public_server.c | 73 ---------------------------------------- - 1 file changed, 73 deletions(-) - -diff --git a/unittest/public_server.c b/unittest/public_server.c -index 2d244297..75663dac 100644 ---- a/unittest/public_server.c -+++ b/unittest/public_server.c -@@ -57,79 +57,6 @@ - #define SLEEP_AFTER_MG_STOP (5) - - --#ifdef HEXDUMP --#else --void --hexDumpth(const char *desc, const void *addr, const int len, int perLine) --{ -- // Silently ignore silly per-line values. -- -- if (perLine < 4 || perLine > 64) -- perLine = 16; -- -- int i; -- unsigned char buff[perLine + 1]; -- const unsigned char *pc = (const unsigned char *)addr; -- -- // Output description if given. -- -- if (desc != NULL) -- fprintf(stderr, "%s:\n", desc); -- -- // Length checks. -- -- if (len == 0) { -- fprintf(stderr, " ZERO LENGTH\n"); -- return; -- } -- if (len < 0) { -- fprintf(stderr, " NEGATIVE LENGTH: %d\n", len); -- return; -- } -- -- // Process every byte in the data. -- -- for (i = 0; i < len; i++) { -- // Multiple of perLine means new or first line (with line offset). -- -- if ((i % perLine) == 0) { -- // Only print previous-line ASCII buffer for lines beyond first. -- -- if (i != 0) -- fprintf(stderr, " %s\n", buff); -- -- // Output the offset of current line. -- -- fprintf(stderr, " %04x ", i); -- } -- -- // Now the hex code for the specific character. -- -- fprintf(stderr, " %02x", pc[i]); -- -- // And buffer a printable ASCII character for later. -- -- if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better. -- buff[i % perLine] = '.'; -- else -- buff[i % perLine] = pc[i]; -- buff[(i % perLine) + 1] = '\0'; -- } -- -- // Pad out last line if not exactly perLine characters. -- -- while ((i % perLine) != 0) { -- fprintf(stderr, " "); -- i++; -- } -- -- // And print the final ASCII buffer. -- -- fprintf(stderr, " %s\n", buff); --} --#define HEXDUMP --#endif -- - /* Try to communicate with an external http server. */ - static const char * - get_external_server_ip(void) --- -2.34.1 - - -From c8bdc706b69f3276e381334c0ab6421204df08e4 Mon Sep 17 00:00:00 2001 -From: tim -Date: Thu, 8 Aug 2024 23:37:15 +0100 -Subject: [PATCH 078/173] [#1276] limit preamble, add test case - ---- - src/handle_form.inl | 14 ++++--- - unittest/public_server.c | 80 ++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 89 insertions(+), 5 deletions(-) - -diff --git a/src/handle_form.inl b/src/handle_form.inl -index b22f17ef..4de8d4f5 100644 ---- a/src/handle_form.inl -+++ b/src/handle_form.inl -@@ -716,9 +716,11 @@ mg_handle_form_request(struct mg_connection *conn, - - if (part_no == 0) { - size_t preamble_length = 0; -- /* skip over the preamble until we find a complete boundary */ -+ /* skip over the preamble until we find a complete boundary -+ * limit the preamble length to prevent abuse */ - /* +2 for the -- preceding the boundary */ -- while ((preamble_length < buf_fill - bl) -+ while (preamble_length < 1024 -+ && (preamble_length < buf_fill - bl) - && strncmp(buf + preamble_length + 2, boundary, bl)) { - preamble_length++; - } -@@ -732,9 +734,11 @@ mg_handle_form_request(struct mg_connection *conn, - } - } - -- /* either it starts with a boundary -- * or we couldn't find a boundary at all in the body -- * or we didn't have a terminating boundary */ -+ /* either it starts with a boundary and it's fine, or it's malformed -+ * because: -+ * - the preamble was longer than accepted -+ * - couldn't find a boundary at all in the body -+ * - didn't have a terminating boundary */ - if (buf_fill < (bl + 2) || strncmp(buf, "--", 2) - || strncmp(buf + 2, boundary, bl)) { - /* Malformed request */ -diff --git a/unittest/public_server.c b/unittest/public_server.c -index 75663dac..6e0f71c7 100644 ---- a/unittest/public_server.c -+++ b/unittest/public_server.c -@@ -3766,6 +3766,86 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -+ /* Handle form: "POST multipart/form-data" very long preamble */ -+ multipart_body = -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n"; -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 1768); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form_error HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); - - /* Now test form_store */ - --- -2.34.1 - - -From 1c5cce5b61ce05565ed0a776292db6a6d77596c2 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Sat, 10 Aug 2024 12:58:15 +0200 -Subject: [PATCH 079/173] Respect NO_DLOPEN also in Lua code - -Signed-off-by: DL6ER ---- - src/mod_lua.inl | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index 9a2164c3..ea17776f 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -3679,7 +3679,7 @@ lua_init_optional_libraries(void) - lua_shared_init(); - - /* UUID library */ --#if !defined(_WIN32) -+#if !defined(_WIN32) && !defined(NO_DLOPEN) - lib_handle_uuid = dlopen("libuuid.so", RTLD_LAZY); - pf_uuid_generate.p = - (lib_handle_uuid ? dlsym(lib_handle_uuid, "uuid_generate") : 0); -@@ -3693,7 +3693,7 @@ static void - lua_exit_optional_libraries(void) - { - /* UUID library */ --#if !defined(_WIN32) -+#if !defined(_WIN32) && !defined(NO_DLOPEN) - if (lib_handle_uuid) { - dlclose(lib_handle_uuid); - } --- -2.34.1 - - -From b1251ad6ccb3f25e2cb107582cdf755dc305f5ff Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Sat, 10 Aug 2024 13:04:10 +0200 -Subject: [PATCH 080/173] Send correct HTTP error code when serving an error - page. Currently, this is broken and even 404 errors are served with HTTP 200 - OK while they clearly shouldn't. - -Signed-off-by: DL6ER ---- - src/mod_lua.inl | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index 9a2164c3..1988a4fd 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -641,7 +641,10 @@ run_lsp_kepler(struct mg_connection *conn, - /* Only send a HTML header, if this is the top level page. - * If this page is included by some mg.include calls, do not add a - * header. */ -- mg_printf(conn, "HTTP/1.1 200 OK\r\n"); -+ if(conn->status_code < 0) -+ mg_printf(conn, "HTTP/1.1 200 OK\r\n"); -+ else -+ mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(conn, conn->status_code)); - send_no_cache_header(conn); - send_additional_header(conn); - mg_printf(conn, --- -2.34.1 - - -From 179eefe8427f449d2c147015aa28e2012315777c Mon Sep 17 00:00:00 2001 -From: leyuskckiran1510 -Date: Mon, 12 Aug 2024 22:24:37 +0545 -Subject: [PATCH 081/173] docs: fix typo on mg_form_data_handler api docs - ---- - docs/api/mg_form_data_handler.md | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/docs/api/mg_form_data_handler.md b/docs/api/mg_form_data_handler.md -index 368d7786..f9d1f43a 100644 ---- a/docs/api/mg_form_data_handler.md -+++ b/docs/api/mg_form_data_handler.md -@@ -9,7 +9,7 @@ - |**`field_found`**|**`int field_found( const char *key, const char *filename, char *path, size_t pathlen, void *user_data )`**;| - ||The callback function `field_found()` is called when a new field has been found. The return value of this callback is used to define how the field should be processed. The parameters contain the following information:| - ||**`key`** - The name of the field as it was named with the `name` tag in the HTML source.| --||**`filename`** - The name of the file to upload. Please not that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.| -+||**`filename`** - The name of the file to upload. Please note that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.| - ||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.| - ||**`pathlen`** - The length of the buffer where the output path can be stored.| - ||**`user_data`** - A pointer to the value of the field `user_data` of the structure `struct mg_form_data_handler`.| --- -2.34.1 - - -From 9ebf36b3e8aa2b5c9166437ca0c9f35e8863d2e3 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Thu, 15 Aug 2024 21:33:26 +0200 -Subject: [PATCH 082/173] Fix comparison of integer expressions of different - signedness warnings in mg_handle_form_request (GCC -Wsign-compare) - -Signed-off-by: DL6ER ---- - src/handle_form.inl | 32 ++++++++++++++++---------------- - 1 file changed, 16 insertions(+), 16 deletions(-) - -diff --git a/src/handle_form.inl b/src/handle_form.inl -index 4de8d4f5..a7b7fc10 100644 ---- a/src/handle_form.inl -+++ b/src/handle_form.inl -@@ -188,7 +188,7 @@ mg_handle_form_request(struct mg_connection *conn, - char path[512]; - char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */ - int field_storage; -- int buf_fill = 0; -+ size_t buf_fill = 0; - int r; - int field_count = 0; - struct mg_file fstore = STRUCT_FILE_INITIALIZER; -@@ -397,10 +397,10 @@ mg_handle_form_request(struct mg_connection *conn, - int end_of_key_value_pair_found = 0; - int get_block; - -- if ((size_t)buf_fill < (sizeof(buf) - 1)) { -+ if (buf_fill < (sizeof(buf) - 1)) { - -- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; -- r = mg_read(conn, buf + (size_t)buf_fill, to_read); -+ size_t to_read = sizeof(buf) - 1 - buf_fill; -+ r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { - /* read error */ - return -1; -@@ -529,11 +529,11 @@ mg_handle_form_request(struct mg_connection *conn, - buf + (size_t)used, - sizeof(buf) - (size_t)used); - next = buf; -- buf_fill -= (int)used; -- if ((size_t)buf_fill < (sizeof(buf) - 1)) { -+ buf_fill -= used; -+ if (buf_fill < (sizeof(buf) - 1)) { - -- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; -- r = mg_read(conn, buf + (size_t)buf_fill, to_read); -+ size_t to_read = sizeof(buf) - 1 - buf_fill; -+ r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { - #if !defined(NO_FILESYSTEMS) - /* read error */ -@@ -592,7 +592,7 @@ mg_handle_form_request(struct mg_connection *conn, - /* Proceed to next entry */ - used = next - buf; - memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); -- buf_fill -= (int)used; -+ buf_fill -= used; - } - - return field_count; -@@ -682,12 +682,12 @@ mg_handle_form_request(struct mg_connection *conn, - for (part_no = 0;; part_no++) { - size_t towrite, fnlen, n; - int get_block; -- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; -+ size_t to_read = sizeof(buf) - 1 - buf_fill; - - /* Unused without filesystems */ - (void)n; - -- r = mg_read(conn, buf + (size_t)buf_fill, to_read); -+ r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { - /* read error */ - mg_free(boundary); -@@ -1001,12 +1001,12 @@ mg_handle_form_request(struct mg_connection *conn, - #endif /* NO_FILESYSTEMS */ - - memmove(buf, hend + towrite, bl + 4); -- buf_fill = (int)(bl + 4); -+ buf_fill = bl + 4; - hend = buf; - - /* Read new data */ -- to_read = sizeof(buf) - 1 - (size_t)buf_fill; -- r = mg_read(conn, buf + (size_t)buf_fill, to_read); -+ to_read = sizeof(buf) - 1 - buf_fill; -+ r = mg_read(conn, buf + buf_fill, to_read); - if ((r < 0) || ((r == 0) && all_data_read)) { - #if !defined(NO_FILESYSTEMS) - /* read error */ -@@ -1025,7 +1025,7 @@ mg_handle_form_request(struct mg_connection *conn, - /* buf_fill is at least 8 here */ - - /* Find boundary */ -- next = search_boundary(buf, (size_t)buf_fill, boundary, bl); -+ next = search_boundary(buf, buf_fill, boundary, bl); - - if (!next && (r == 0)) { - /* incomplete request */ -@@ -1100,7 +1100,7 @@ mg_handle_form_request(struct mg_connection *conn, - if (next) { - used = next - buf + 2; - memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); -- buf_fill -= (int)used; -+ buf_fill -= used; - } else { - buf_fill = 0; - } --- -2.34.1 - - -From b527020950de516b2a45cfed8660744445d1aee5 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Fri, 16 Aug 2024 18:57:35 +0200 -Subject: [PATCH 083/173] Add missing PSA (Platform Security Architecture) - cryptography API initialization - -It is mandatory when PSA is used which is, in turn, mandatory when using TLS 1.3 (`MBEDTLS_SSL_PROTO_TLS1_3`). When not being initialized, the server will finish startup but any encrypted traffic will cause errors deep inside the mbedTLS library (return code `0x6c00 == MBEDTLS_ERR_SSL_INTERNAL_ERROR`). - -Signed-off-by: DL6ER ---- - src/mod_mbedtls.inl | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl -index c0333b21..93f1bb29 100644 ---- a/src/mod_mbedtls.inl -+++ b/src/mod_mbedtls.inl -@@ -88,6 +88,18 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) - mbedtls_ctr_drbg_init(&ctx->ctr); - mbedtls_x509_crt_init(&ctx->cert); - -+#ifdef MBEDTLS_PSA_CRYPTO_C -+ /* Initialize PSA crypto (mandatory with TLS 1.3) -+ * This must be done before calling any other PSA Crypto -+ * functions or they will fail with PSA_ERROR_BAD_STATE -+ */ -+ const psa_status_t status = psa_crypto_init(); -+ if (status != PSA_SUCCESS) { -+ DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n", (int) status); -+ return -1; -+ } -+#endif -+ - rc = mbedtls_ctr_drbg_seed(&ctx->ctr, - mbedtls_entropy_func, - &ctx->entropy, --- -2.34.1 - - -From 616830868fe52bf37ee1141d8f349ed3ba24ab01 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Sun, 18 Aug 2024 07:21:34 +0200 -Subject: [PATCH 084/173] Fix header generation for Lua Server Pages (Kepler - syntax). - -The currently used code has send_no_cache_header() and send_additional_header() but NOT actually a call to mg_response_header_send(). When NO_RESPONSE_BUFFERING is not set (default), this merely adds the additional headers to the buffer but does not send them. As a final call to mg_response_header_send() was indeed missing here, they are never sent causing, e.g., headers added through the "additional_headers" config string to be ignored for Kepler LSP. Similarly, CORS and cache-control headers were missing in this case. - -Signed-off-by: DL6ER ---- - src/mod_lua.inl | 22 +++++++++++++--------- - 1 file changed, 13 insertions(+), 9 deletions(-) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index 1988a4fd..94f413a7 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -641,17 +641,21 @@ run_lsp_kepler(struct mg_connection *conn, - /* Only send a HTML header, if this is the top level page. - * If this page is included by some mg.include calls, do not add a - * header. */ -- if(conn->status_code < 0) -- mg_printf(conn, "HTTP/1.1 200 OK\r\n"); -- else -- mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(conn, conn->status_code)); -+ -+ /* Initialize a new HTTP response, either with some-predefined -+ * status code (e.g. 404 if this is called from an error -+ * handler) or with 200 OK */ -+ mg_response_header_start(conn, conn->status_code > 0 ? conn->status_code : 200); -+ -+ /* Add additional headers */ - send_no_cache_header(conn); - send_additional_header(conn); -- mg_printf(conn, -- "Date: %s\r\n" -- "Connection: close\r\n" -- "Content-Type: text/html; charset=utf-8\r\n\r\n", -- date); -+ -+ /* Add content type */ -+ mg_response_header_add(conn, "Content-Type", "text/html; charset=utf-8", -1); -+ -+ /* Send the HTTP response (status and all headers) */ -+ mg_response_header_send(conn); - } - - data.begin = p; --- -2.34.1 - - -From 7fbfe8e32524a7ec590d98d824fe06f2da185c64 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 09:55:20 +0200 -Subject: [PATCH 085/173] Add devcontainer - -Signed-off-by: yubiuser ---- - .devcontainer/Dockerfile | 12 ++++++++++++ - .devcontainer/devcontainer.json | 16 ++++++++++++++++ - 2 files changed, 28 insertions(+) + .devcontainer/Dockerfile | 13 + + .devcontainer/devcontainer.json | 16 + + .github/dependabot.yml | 10 + + .github/workflows/cibuild.yml | 539 + + .github/workflows/cifuzz.yml | 2 +- + .github/workflows/codeql-analysis.yml | 66 - + .github/workflows/codeql.yml | 130 + + .github/workflows/fail_on_error.py | 2 + + .gitignore | 1 + + .travis.yml | 521 +- + CMakeLists.txt | 49 +- + CREDITS.md | 2 + + Dockerfile | 53 + + LICENSE.md | 2 +- + Makefile | 16 +- + Makefile.osx | 2 +- + README.md | 23 +- + RELEASE_NOTES.md | 13 + + SECURITY.md | 10 +- + .../civetweb_lua/civetweb_lua.vcxproj | 7 +- + appveyor.yml | 75 +- + docs/Building.md | 5 +- + docs/Docker.md | 96 + + docs/Embedding.md | 11 +- + docs/OpenSSL.md | 5 +- + docs/README.md | 4 +- + docs/UserManual.md | 46 +- + docs/api/mg_form_data_handler.md | 14 +- + docs/api/mg_get_cookie.md | 4 +- + docs/api/mg_request_info.md | 2 +- + docs/api/mg_server_port.md | 4 +- + docs/gnutls.md | 19 + + examples/https/civetweb.conf | 3 + + examples/linux_ws_server_cpp/CMakeLists.txt | 4 +- + format.bat | 1 + + fuzztest/fuzzmain.c | 62 +- + include/civetweb.h | 19 +- + resources/Makefile.in-lua | 17 +- + resources/civetweb.conf | 1 + + src/CMakeLists.txt | 33 +- + src/CivetServer.cpp | 8 +- + src/civetweb.c | 1280 +- + src/handle_form.inl | 160 +- + src/http2.inl | 4 +- + src/main.c | 14 +- + src/match.inl | 6 +- + src/mod_gnutls.inl | 240 + + src/mod_lua.inl | 119 +- + src/mod_lua_shared.inl | 2 +- + src/mod_mbedtls.inl | 121 +- + src/third_party/civetweb_lua.h | 16 +- + src/third_party/lfs.c | 2 +- + src/third_party/lua-5.2.4/src/ldebug.c | 7 +- + src/third_party/lua-5.5.0-beta/Makefile | 106 + + src/third_party/lua-5.5.0-beta/README | 6 + + .../doc/OSIApproved_100X125.png | Bin 0 -> 12127 bytes + .../lua-5.5.0-beta/doc/contents.html | 679 + + src/third_party/lua-5.5.0-beta/doc/index.css | 21 + + src/third_party/lua-5.5.0-beta/doc/logo.gif | Bin 0 -> 9893 bytes + src/third_party/lua-5.5.0-beta/doc/lua.1 | 155 + + src/third_party/lua-5.5.0-beta/doc/lua.css | 162 + + src/third_party/lua-5.5.0-beta/doc/luac.1 | 118 + + src/third_party/lua-5.5.0-beta/doc/manual.css | 21 + + .../lua-5.5.0-beta/doc/manual.html | 12398 +++ + .../lua-5.5.0-beta/doc/readme.html | 333 + + src/third_party/lua-5.5.0-beta/src/Makefile | 229 + + src/third_party/lua-5.5.0-beta/src/lapi.c | 1467 + + src/third_party/lua-5.5.0-beta/src/lapi.h | 65 + + src/third_party/lua-5.5.0-beta/src/lauxlib.c | 1198 + + src/third_party/lua-5.5.0-beta/src/lauxlib.h | 268 + + src/third_party/lua-5.5.0-beta/src/lbaselib.c | 558 + + src/third_party/lua-5.5.0-beta/src/lcode.c | 1911 + + src/third_party/lua-5.5.0-beta/src/lcode.h | 103 + + src/third_party/lua-5.5.0-beta/src/lcorolib.c | 223 + + src/third_party/lua-5.5.0-beta/src/lctype.c | 64 + + src/third_party/lua-5.5.0-beta/src/lctype.h | 101 + + src/third_party/lua-5.5.0-beta/src/ldblib.c | 477 + + src/third_party/lua-5.5.0-beta/src/ldebug.c | 971 + + src/third_party/lua-5.5.0-beta/src/ldebug.h | 64 + + src/third_party/lua-5.5.0-beta/src/ldo.c | 1131 + + src/third_party/lua-5.5.0-beta/src/ldo.h | 98 + + src/third_party/lua-5.5.0-beta/src/ldump.c | 303 + + src/third_party/lua-5.5.0-beta/src/lfunc.c | 315 + + src/third_party/lua-5.5.0-beta/src/lfunc.h | 65 + + src/third_party/lua-5.5.0-beta/src/lgc.c | 1803 + + src/third_party/lua-5.5.0-beta/src/lgc.h | 268 + + src/third_party/lua-5.5.0-beta/src/linit.c | 63 + + src/third_party/lua-5.5.0-beta/src/liolib.c | 841 + + src/third_party/lua-5.5.0-beta/src/ljumptab.h | 112 + + src/third_party/lua-5.5.0-beta/src/llex.c | 604 + + src/third_party/lua-5.5.0-beta/src/llex.h | 93 + + src/third_party/lua-5.5.0-beta/src/llimits.h | 315 + + src/third_party/lua-5.5.0-beta/src/lmathlib.c | 752 + + src/third_party/lua-5.5.0-beta/src/lmem.c | 215 + + src/third_party/lua-5.5.0-beta/src/lmem.h | 96 + + src/third_party/lua-5.5.0-beta/src/loadlib.c | 743 + + src/third_party/lua-5.5.0-beta/src/lobject.c | 717 + + src/third_party/lua-5.5.0-beta/src/lobject.h | 854 + + src/third_party/lua-5.5.0-beta/src/lopcodes.c | 138 + + src/third_party/lua-5.5.0-beta/src/lopcodes.h | 429 + + src/third_party/lua-5.5.0-beta/src/lopnames.h | 103 + + src/third_party/lua-5.5.0-beta/src/loslib.c | 432 + + src/third_party/lua-5.5.0-beta/src/lparser.c | 2091 + + src/third_party/lua-5.5.0-beta/src/lparser.h | 191 + + src/third_party/lua-5.5.0-beta/src/lprefix.h | 45 + + src/third_party/lua-5.5.0-beta/src/lstate.c | 420 + + src/third_party/lua-5.5.0-beta/src/lstate.h | 455 + + src/third_party/lua-5.5.0-beta/src/lstring.c | 359 + + src/third_party/lua-5.5.0-beta/src/lstring.h | 73 + + src/third_party/lua-5.5.0-beta/src/lstrlib.c | 1883 + + src/third_party/lua-5.5.0-beta/src/ltable.c | 1325 + + src/third_party/lua-5.5.0-beta/src/ltable.h | 184 + + src/third_party/lua-5.5.0-beta/src/ltablib.c | 426 + + src/third_party/lua-5.5.0-beta/src/ltm.c | 276 + + src/third_party/lua-5.5.0-beta/src/ltm.h | 104 + + src/third_party/lua-5.5.0-beta/src/lua.c | 761 + + src/third_party/lua-5.5.0-beta/src/lua.h | 554 + + src/third_party/lua-5.5.0-beta/src/lua.hpp | 10 + + src/third_party/lua-5.5.0-beta/src/luac.c | 724 + + src/third_party/lua-5.5.0-beta/src/luaconf.h | 827 + + src/third_party/lua-5.5.0-beta/src/lualib.h | 65 + + src/third_party/lua-5.5.0-beta/src/lundump.c | 423 + + src/third_party/lua-5.5.0-beta/src/lundump.h | 40 + + src/third_party/lua-5.5.0-beta/src/lutf8lib.c | 291 + + src/third_party/lua-5.5.0-beta/src/lvm.c | 1926 + + src/third_party/lua-5.5.0-beta/src/lvm.h | 136 + + src/third_party/lua-5.5.0-beta/src/lzio.c | 89 + + src/third_party/lua-5.5.0-beta/src/lzio.h | 67 + + src/third_party/lua_struct.c | 10 +- + src/third_party/sqlite3.c | 77464 +++++++++++----- + src/third_party/sqlite3.h | 2397 +- + src/timer.inl | 6 +- + test/donate.html | 32 - + test/page3.ssjs | 3 + + test/page4.lp | 4 +- + test/page4kepler.lp | 4 +- + unittest/CMakeLists.txt | 4 + + unittest/private.c | 5 + + unittest/public_server.c | 859 +- + unittest/timertest.c | 28 +- + 140 files changed, 106511 insertions(+), 24119 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json + create mode 100644 .github/dependabot.yml + create mode 100644 .github/workflows/cibuild.yml + delete mode 100644 .github/workflows/codeql-analysis.yml + create mode 100644 .github/workflows/codeql.yml + create mode 100755 .github/workflows/fail_on_error.py + create mode 100644 Dockerfile + create mode 100644 docs/Docker.md + create mode 100644 docs/gnutls.md + create mode 100644 src/mod_gnutls.inl + create mode 100644 src/third_party/lua-5.5.0-beta/Makefile + create mode 100644 src/third_party/lua-5.5.0-beta/README + create mode 100644 src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png + create mode 100644 src/third_party/lua-5.5.0-beta/doc/contents.html + create mode 100644 src/third_party/lua-5.5.0-beta/doc/index.css + create mode 100644 src/third_party/lua-5.5.0-beta/doc/logo.gif + create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.1 + create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.css + create mode 100644 src/third_party/lua-5.5.0-beta/doc/luac.1 + create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.css + create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.html + create mode 100644 src/third_party/lua-5.5.0-beta/doc/readme.html + create mode 100644 src/third_party/lua-5.5.0-beta/src/Makefile + create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lbaselib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lcorolib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldblib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ldump.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/linit.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/liolib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ljumptab.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/llimits.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lmathlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/loadlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lopnames.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/loslib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lprefix.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lstrlib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltablib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.hpp + create mode 100644 src/third_party/lua-5.5.0-beta/src/luac.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/luaconf.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lualib.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lutf8lib.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.h + create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.c + create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.h + delete mode 100644 test/donate.html diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 -index 00000000..71bc1462 +index 00000000..81fdf3e9 --- /dev/null +++ b/.devcontainer/Dockerfile -@@ -0,0 +1,12 @@ +@@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1 +ARG ubuntu_version=24.04 + +FROM ubuntu:${ubuntu_version} +RUN apt-get update && \ -+ apt-get install -y \ ++ apt-get install --no-install-recommends -y \ + build-essential \ + ca-certificates \ + cmake \ ++ clang \ + git \ + nano\ + openssh-server @@ -7813,27 +276,28 @@ index 00000000..b3030fdb + + } \ No newline at end of file --- -2.34.1 - - -From 67d91b1e4f4dff2eb21fff7296533494fed47127 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 10:29:46 +0200 -Subject: [PATCH 086/173] Add linux build - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 332 ++++++++++++++++++++++++++++++++++ - 1 file changed, 332 insertions(+) - create mode 100644 .github/workflows/cibuild.yml - +diff --git a/.github/dependabot.yml b/.github/dependabot.yml +new file mode 100644 +index 00000000..c20f0118 +--- /dev/null ++++ b/.github/dependabot.yml +@@ -0,0 +1,10 @@ ++version: 2 ++updates: ++- package-ecosystem: github-actions ++ directory: "/" ++ schedule: ++ interval: weekly ++ day: saturday ++ time: "10:00" ++ open-pull-requests-limit: 10 ++ target-branch: master diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml new file mode 100644 -index 00000000..3dc5fe81 +index 00000000..44f05cc4 --- /dev/null +++ b/.github/workflows/cibuild.yml -@@ -0,0 +1,332 @@ +@@ -0,0 +1,539 @@ +name: CI build + +on: @@ -7843,8 +307,9 @@ index 00000000..3dc5fe81 + types: [published] + workflow_dispatch: +jobs: -+ build: ++ build-and-test: + runs-on: ${{ matrix.os }} ++ name: ${{ matrix.env.NAME }} + strategy: + fail-fast: true + matrix: @@ -7852,8 +317,7 @@ index 00000000..3dc5fe81 + - os: ubuntu-latest + compiler: clang + env: -+ idx: 1 -+ N: Clang-Linux-Minimal-Debug ++ NAME: Clang-Linux-Minimal-Debug + BUILD_TYPE: Debug + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO @@ -7873,16 +337,16 @@ index 00000000..3dc5fe81 + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES -+ ++ RUN_UNITTEST: 1 ++ + - os: ubuntu-latest + compiler: clang + env: -+ idx: 3 -+ N: Clang-Linux-Default-Release ++ NAME: Clang-Linux-Default-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES -+ OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -7898,16 +362,16 @@ index 00000000..3dc5fe81 + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES -+ ++ RUN_UNITTEST: 1 ++ + - os: ubuntu-latest + compiler: gcc + env: -+ idx: 5 -+ N: GCC-Linux-Complete-NoLua-Release ++ NAME: GCC-Linux-Complete-NoLua-Release + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES -+ OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -7924,16 +388,15 @@ index 00000000..3dc5fe81 + NO_CACHING: YES + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 -+ ++ + - os: ubuntu-latest + compiler: clang + env: -+ idx: 6 -+ N: CLANG-AnyVersion-Linux-Coverage ++ NAME: CLANG-AnyVersion-Linux-Coverage + BUILD_TYPE: Coverage + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES -+ OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -7950,12 +413,11 @@ index 00000000..3dc5fe81 + NO_CACHING: NO + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 -+ ++ + - os: ubuntu-latest + compiler: clang + env: -+ idx: 9 -+ N: Clang-Linux-Default-Shared ++ NAME: Clang-Linux-Default-Shared + BUILD_TYPE: Debug + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO @@ -7975,16 +437,16 @@ index 00000000..3dc5fe81 + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES -+ ++ RUN_UNITTEST: 1 ++ + - os: ubuntu-latest + compiler: gcc + env: -+ idx: 15 -+ N: GCCLinuxDefault_RelWithDebInfo ++ NAME: GCCLinuxDefault_RelWithDebInfo + BUILD_TYPE: RelWithDebInfo + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES -+ OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -7999,16 +461,16 @@ index 00000000..3dc5fe81 + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES -+ ++ RUN_UNITTEST: 1 ++ + - os: ubuntu-latest + compiler: gcc + env: -+ idx: 16 -+ N: GCCLinuxDefault_MinSizeRel ++ NAME: GCCLinuxDefault_MinSizeRel + BUILD_TYPE: MinSizeRel + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES -+ OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -8023,16 +485,16 @@ index 00000000..3dc5fe81 + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES -+ ++ RUN_UNITTEST: 1 ++ + - os: ubuntu-latest + compiler: gcc + env: -+ idx: 17 -+ N: GCCLinuxDefault_None ++ NAME: GCCLinuxDefault_None + BUILD_TYPE: None + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES -+ OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -8047,16 +509,16 @@ index 00000000..3dc5fe81 + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES -+ ++ RUN_UNITTEST: 1 ++ + - os: ubuntu-latest + compiler: gcc + env: -+ idx: 20 -+ N: GCCLinuxDefault_xenial ++ NAME: GCCLinuxDefault_xenial + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES -+ OPENSSL_1_1: NO ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -8071,12 +533,12 @@ index 00000000..3dc5fe81 + ENABLE_DUKTAPE: NO + NO_CACHING: NO + ALLOW_WARNINGS: YES -+ ++ RUN_UNITTEST: 1 ++ + - os: ubuntu-latest + compiler: gcc + env: -+ idx: 23 -+ N: GCCLinuxDefault_focal ++ NAME: GCCLinuxDefault + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES + OPENSSL_1_0: NO @@ -8096,13 +558,38 @@ index 00000000..3dc5fe81 + NO_CACHING: NO + ALLOW_WARNINGS: YES + RUN_UNITTEST: 1 ++ ++ - os: ubuntu-latest ++ compiler: gcc ++ env: ++ NAME: GCCLinuxDefault_OpenSSL_3_0 ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: NO ++ OpenSSL_3_0: YES ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: NO ++ ENABLE_WEBSOCKETS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ RUN_UNITTEST: 1 + -+# Remove Lua build, until someone knows how to fix the CMake files -+ # - os: ubuntu-latest ++ # Disable Lua build, until someone knows how to fix the CMake files ++ # see https://github.com/civetweb/civetweb/issues/543 ++ # - os: ubuntu-lastest + # compiler: clang + # env: -+ # idx: 99 -+ # N: Clang-Linux-Complete-WithLua-Debug ++ # NAME: Clang-Linux-Complete-WithLua-Debug + # BUILD_TYPE: Debug + # ENABLE_SSL_DYNAMIC_LOADING: YES + # OPENSSL_1_0: NO @@ -8122,159 +609,12 @@ index 00000000..3dc5fe81 + # ENABLE_DUKTAPE: NO + # NO_CACHING: YES + # ALLOW_WARNINGS: YES ++ # RUN_UNITTEST: 1 + -+ -+ steps: -+ - name: Checkout code -+ uses: actions/checkout@v4.1.7 -+ -+ - name: Install clang on Linux -+ if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' -+ run: | -+ sudo apt-get install -y clang -+ sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 -+ sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 -+ -+ - name: Build -+ run: | -+ cmake -S . -B CMakeFiles\ -+ -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ -+ -DBUILD_SHARED_LIBS=${{ matrix.env.BUILD_SHARED }}\ -+ -DCIVETWEB_THIRD_PARTY_DIR=../src/third-party\ -+ -DCIVETWEB_ENABLE_THIRD_PARTY_OUTPUT=YES\ -+ -DCIVETWEB_ENABLE_SSL=${{ matrix.env.ENABLE_SSL }}\ -+ -DCIVETWEB_DISABLE_CGI=${{ matrix.env.NO_CGI }}\ -+ -DCIVETWEB_SERVE_NO_FILES=${{ matrix.env.NO_FILES }}\ -+ -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${{ matrix.env.ENABLE_SSL_DYNAMIC_LOADING }}\ -+ -DCIVETWEB_SSL_OPENSSL_API_1_0=${{ matrix.env.OPENSSL_1_0 }}\ -+ -DCIVETWEB_SSL_OPENSSL_API_1_1=${{ matrix.env.OPENSSL_1_1 }}\ -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=${{ matrix.env.OPENSSL_3_0 }}\ -+ -DCIVETWEB_ENABLE_WEBSOCKETS=${{ matrix.env.ENABLE_WEBSOCKETS }}\ -+ -DCIVETWEB_ENABLE_CXX=${{ matrix.env.ENABLE_CXX }}\ -+ -DCIVETWEB_ENABLE_SERVER_STATS=${{ matrix.env.ENABLE_SERVER_STATS }}\ -+ -DCIVETWEB_ENABLE_LUA=${{ matrix.env.ENABLE_LUA }}\ -+ -DCIVETWEB_ENABLE_LUA_SHARED=${{ matrix.env.ENABLE_LUA_SHARED }}\ -+ -DCIVETWEB_ENABLE_DUKTAPE=${{ matrix.env.ENABLE_DUKTAPE }}\ -+ -DCIVETWEB_DISABLE_CACHING=${{ matrix.env.NO_CACHING }}\ -+ -DCIVETWEB_C_STANDARD=${{ matrix.env.C_STANDARD }}\ -+ -DCIVETWEB_CXX_STANDARD=${{ matrix.env.CXX_STANDARD }}\ -+ -DCIVETWEB_ALLOW_WARNINGS=${{ matrix.env.ALLOW_WARNINGS }}\ -+ -DCIVETWEB_ENABLE_IPV6=${{ matrix.env.ENABLE_IPV6 }}\ -+ ${{ env.ADDITIONAL_CMAKE_ARGS }} -+ cmake --build CMakeFiles -- -j $(nproc) -+ -+ - name: Verify -+ run: | -+ ./CMakeFiles/src/civetweb -I -\ No newline at end of file --- -2.34.1 - - -From 92904b6aea8a7453f49cbcd4fd45828921d00985 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 21:59:31 +0200 -Subject: [PATCH 087/173] MacOS builds - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 108 ++++++++++++++++++++++++++++++---- - 1 file changed, 95 insertions(+), 13 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index 3dc5fe81..c3ed4ad6 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -37,7 +37,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -- -+ - - os: ubuntu-latest - compiler: clang - env: -@@ -62,7 +62,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -- -+ - - os: ubuntu-latest - compiler: gcc - env: -@@ -88,7 +88,7 @@ jobs: - NO_CACHING: YES - ALLOW_WARNINGS: YES - RUN_UNITTEST: 1 -- -+ - - os: ubuntu-latest - compiler: clang - env: -@@ -114,7 +114,7 @@ jobs: - NO_CACHING: NO - ALLOW_WARNINGS: YES - RUN_UNITTEST: 1 -- -+ - - os: ubuntu-latest - compiler: clang - env: -@@ -139,7 +139,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -- -+ - - os: ubuntu-latest - compiler: gcc - env: -@@ -163,7 +163,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -- -+ - - os: ubuntu-latest - compiler: gcc - env: -@@ -187,7 +187,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -- -+ - - os: ubuntu-latest - compiler: gcc - env: -@@ -211,7 +211,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -- -+ - - os: ubuntu-latest - compiler: gcc - env: -@@ -235,7 +235,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -- -+ - - os: ubuntu-latest - compiler: gcc - env: -@@ -287,19 +287,93 @@ jobs: - # NO_CACHING: YES - # ALLOW_WARNINGS: YES - + - os: macos-latest + compiler: clang + env: -+ idx: 8 -+ N: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad ++ NAME: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: NO + OPENSSL_1_0: NO @@ -8299,12 +639,38 @@ index 3dc5fe81..c3ed4ad6 100644 + - os: macos-latest + compiler: clang + env: -+ idx: 11 -+ N: OSX-Package ++ NAME: OSX-Package_OpenSSL_1_1 ++ BUILD_TYPE: Release ++ ENABLE_SSL_DYNAMIC_LOADING: YES ++ OPENSSL_1_0: NO ++ OPENSSL_1_1: YES ++ ENABLE_CXX: NO ++ ENABLE_LUA_SHARED: NO ++ C_STANDARD: auto ++ CXX_STANDARD: auto ++ BUILD_SHARED: NO ++ NO_FILES: NO ++ ENABLE_SSL: YES ++ NO_CGI: NO ++ ENABLE_IPV6: YES ++ ENABLE_WEBSOCKETS: YES ++ ENABLE_SERVER_STATS: NO ++ ENABLE_LUA: NO ++ ENABLE_DUKTAPE: NO ++ NO_CACHING: NO ++ ALLOW_WARNINGS: YES ++ MACOSX_PACKAGE: 1 ++ RUN_UNITTEST: 1 ++ ++ - os: macos-latest ++ compiler: clang ++ env: ++ NAME: OSX-Package_OpenSSL_3_0 + BUILD_TYPE: Release + ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: YES ++ OPENSSL_1_0: NO + OPENSSL_1_1: NO ++ OPENSSL_3_0: YES + ENABLE_CXX: NO + ENABLE_LUA_SHARED: NO + C_STANDARD: auto @@ -8321,441 +687,81 @@ index 3dc5fe81..c3ed4ad6 100644 + NO_CACHING: NO + ALLOW_WARNINGS: YES + MACOSX_PACKAGE: 1 ++ RUN_UNITTEST: 1 + - - steps: - - name: Checkout code - uses: actions/checkout@v4.1.7 -- -+ - - name: Install clang on Linux - if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' - run: | - sudo apt-get install -y clang - sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 - sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 -+ -+ - name: Set up OpenSSL 1.1 on MacOS -+ if: matrix.os == 'macos-latest' && matrix.env.OPENSSL_1_1 == 'YES' ++ ++ steps: ++ - name: Checkout code ++ uses: actions/checkout@v6 ++ ++ - name: Export number of CPUs ++ run: | ++ if [ "$RUNNER_OS" == "Linux" ]; then ++ echo "cores=$(nproc)" >> $GITHUB_ENV ++ fi ++ if [ "$RUNNER_OS" == "macOS" ]; then ++ echo "cores=$(sysctl -n hw.logicalcpu)" >> $GITHUB_ENV ++ fi ++ ++ - name: Install clang on Linux ++ if: matrix.compiler == 'clang' && startsWith(matrix.os,'ubuntu') ++ run: | ++ sudo apt-get install --no-install-recommends -y clang ++ sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 ++ sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 ++ ++ - name: Set up OpenSSL 1.1 on modern MacOS ++ # OpenSSL 1.1 is installed by default, so we just need to set the paths ++ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_1 == 'YES' + run: | + OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) -+ LDFLAGS=-L{$OPENSSL_ROOT_DIR}/lib ++ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" + PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig - -- - name: Build ++ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib ++ + echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV + echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV + echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH + echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ -+ - name: Print tool version information -+ run: | -+ openssl version -+ cc --version -+ cmake --version -+ clang --version -+ -+ - name: Run CMake - run: | - cmake -S . -B CMakeFiles\ - -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ -@@ -325,8 +399,16 @@ jobs: - -DCIVETWEB_ALLOW_WARNINGS=${{ matrix.env.ALLOW_WARNINGS }}\ - -DCIVETWEB_ENABLE_IPV6=${{ matrix.env.ENABLE_IPV6 }}\ - ${{ env.ADDITIONAL_CMAKE_ARGS }} -- cmake --build CMakeFiles -- -j $(nproc) -+ -+ - name: Build MacOS Package -+ if: matrix.env.MACOSX_PACKAGE == 1 -+ run: | -+ make -f Makefile.osx package - -- - name: Verify -+ - name: Build executable -+ run: | -+ cmake --build CMakeFiles -- -j $(nproc) -+ -+ - name: Check executable - run: | - ./CMakeFiles/src/civetweb -I -\ No newline at end of file --- -2.34.1 - - -From c62054609add2d14f34a9dd796edfd7f46bcf222 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 22:00:28 +0200 -Subject: [PATCH 088/173] Drop i386 arch for MacOS - -Signed-off-by: yubiuser ---- - Makefile.osx | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/Makefile.osx b/Makefile.osx -index 44260e84..970579fc 100644 ---- a/Makefile.osx -+++ b/Makefile.osx -@@ -13,7 +13,7 @@ WITH_LUA = 1 - PACKAGE = Civetweb - BUILD_DIR = out - --CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch i386 -arch x86_64 -+CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch x86_64 -arch arm64 - LDFLAGS += -framework Cocoa - - DMG_DIR = $(BUILD_DIR)/dmg --- -2.34.1 - - -From d04bbfe89b88eb6706a9ec17b0e2cbbb88f06ab1 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 22:01:30 +0200 -Subject: [PATCH 089/173] Do not set XOPEN__SOURCE when USE_COCOA is set - -Signed-off-by: yubiuser ---- - src/main.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/main.c b/src/main.c -index f4d408e2..9d3a0ea7 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -41,7 +41,7 @@ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wreserved-id-macro" - #endif --#if !defined(_XOPEN_SOURCE) -+#if !defined(_XOPEN_SOURCE) && !defined(USE_COCOA) - #define _XOPEN_SOURCE 600 /* For PATH_MAX on linux */ - /* This should also be sufficient for "realpath", according to - * http://man7.org/linux/man-pages/man3/realpath.3.html, but in --- -2.34.1 - - -From 160a078eb28b767421926e0bcb6f45a93fc2d220 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 22:02:04 +0200 -Subject: [PATCH 090/173] Bump macosx-version-min to 10.6 to fix deprecation - warning - -Signed-off-by: yubiuser ---- - Makefile.osx | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/Makefile.osx b/Makefile.osx -index 970579fc..a14fdee9 100644 ---- a/Makefile.osx -+++ b/Makefile.osx -@@ -13,7 +13,7 @@ WITH_LUA = 1 - PACKAGE = Civetweb - BUILD_DIR = out - --CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch x86_64 -arch arm64 -+CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.6 -ObjC -arch x86_64 -arch arm64 - LDFLAGS += -framework Cocoa - - DMG_DIR = $(BUILD_DIR)/dmg --- -2.34.1 - - -From b43cc5be976255bcd09a18ca9103f41b5efff05e Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 22:23:44 +0200 -Subject: [PATCH 091/173] Add dependabot to keep github-actions up-to-date - -Signed-off-by: yubiuser ---- - .github/dependabot.yml | 10 ++++++++++ - 1 file changed, 10 insertions(+) - create mode 100644 .github/dependabot.yml - -diff --git a/.github/dependabot.yml b/.github/dependabot.yml -new file mode 100644 -index 00000000..c20f0118 ---- /dev/null -+++ b/.github/dependabot.yml -@@ -0,0 +1,10 @@ -+version: 2 -+updates: -+- package-ecosystem: github-actions -+ directory: "/" -+ schedule: -+ interval: weekly -+ day: saturday -+ time: "10:00" -+ open-pull-requests-limit: 10 -+ target-branch: master --- -2.34.1 - - -From 0088462e95e78374308f878bb1b6adf655c6d9e7 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 18 Aug 2024 22:34:10 +0200 -Subject: [PATCH 092/173] Fix Lua SQLite Verification Hash - -Signed-off-by: yubiuser ---- - CMakeLists.txt | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index e1aa002b..4ffd41b9 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -188,7 +188,7 @@ if (CIVETWEB_ENABLE_LUA) - mark_as_advanced(CIVETWEB_LUA_SQLITE_VERSION) - - # Lua SQLite Verification Hash -- set(CIVETWEB_LUA_SQLITE_MD5_HASH 43234ae08197dfce6da02482ed14ec92 CACHE STRING -+ set(CIVETWEB_LUA_SQLITE_MD5_HASH ff7abd4aa8bd549eb18298fb954612f8 CACHE STRING - "The hash of Lua SQLite archive to be downloaded") - set_property(CACHE CIVETWEB_LUA_SQLITE_MD5_HASH PROPERTY VALUE ${CIVETWEB_LUA_SQLITE_MD5_HASH}) - mark_as_advanced(CIVETWEB_LUA_SQLITE_MD5_HASH) --- -2.34.1 - - -From 2ff558cdde172468f5d8a77d46eb8a6b650c8717 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Tue, 20 Aug 2024 13:03:27 +0200 -Subject: [PATCH 093/173] Add clang and set --no-install-recommends - -Signed-off-by: yubiuser ---- - .devcontainer/Dockerfile | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile -index 71bc1462..81fdf3e9 100644 ---- a/.devcontainer/Dockerfile -+++ b/.devcontainer/Dockerfile -@@ -3,10 +3,11 @@ ARG ubuntu_version=24.04 - - FROM ubuntu:${ubuntu_version} - RUN apt-get update && \ -- apt-get install -y \ -+ apt-get install --no-install-recommends -y \ - build-essential \ - ca-certificates \ - cmake \ -+ clang \ - git \ - nano\ - openssh-server -\ No newline at end of file --- -2.34.1 - - -From dbcf24552d2c6bf514bf9459d0d5e59b4ccbc31c Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Tue, 20 Aug 2024 13:50:55 +0200 -Subject: [PATCH 094/173] --no-install-recommends - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index c3ed4ad6..39b0bc86 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -347,7 +347,7 @@ jobs: - - name: Install clang on Linux - if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' - run: | -- sudo apt-get install -y clang -+ sudo apt-get install --no-install-recommends -y clang - sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 - sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 - --- -2.34.1 - - -From 2a306508b5f8f83383c49cd755cafbbc6e8832c7 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Fri, 23 Aug 2024 12:55:10 +0200 -Subject: [PATCH 095/173] Run unittests - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 112 +++++++++++++++++++++++++++++++--- - .gitignore | 1 + - 2 files changed, 104 insertions(+), 9 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index 39b0bc86..7fe95108 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -37,6 +37,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - - os: ubuntu-latest - compiler: clang -@@ -62,6 +63,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - - os: ubuntu-latest - compiler: gcc -@@ -139,6 +141,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - - os: ubuntu-latest - compiler: gcc -@@ -163,6 +166,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - - os: ubuntu-latest - compiler: gcc -@@ -187,6 +191,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - - os: ubuntu-latest - compiler: gcc -@@ -211,6 +216,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - - os: ubuntu-latest - compiler: gcc -@@ -235,6 +241,7 @@ jobs: - ENABLE_DUKTAPE: NO - NO_CACHING: NO - ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - - os: ubuntu-latest - compiler: gcc -@@ -261,8 +268,9 @@ jobs: - ALLOW_WARNINGS: YES - RUN_UNITTEST: 1 - --# Remove Lua build, until someone knows how to fix the CMake files -- # - os: ubuntu-latest -+ # Disable Lua build, until someone knows how to fix the CMake files -+ # see https://github.com/civetweb/civetweb/issues/543 -+ # - os: ubuntu-lastest - # compiler: clang - # env: - # idx: 99 -@@ -286,6 +294,7 @@ jobs: - # ENABLE_DUKTAPE: NO - # NO_CACHING: YES - # ALLOW_WARNINGS: YES -+ # RUN_UNITTEST: 1 - - - os: macos-latest - compiler: clang -@@ -313,7 +322,10 @@ jobs: - ALLOW_WARNINGS: YES - RUN_UNITTEST: 1 - -- - os: macos-latest -+ # mac-os 13 is the last version of MacOS runner using x86_64 architecture -+ # mac-os 14 and later are using arm64 architecture -+ # but OpenSSL 1.0 can't compile on arm64, so we set it fixed to mac-os 13 -+ - os: macos-13 - compiler: clang - env: - idx: 11 -@@ -338,6 +350,7 @@ jobs: - NO_CACHING: NO - ALLOW_WARNINGS: YES - MACOSX_PACKAGE: 1 -+ RUN_UNITTEST: 1 - - - steps: -@@ -350,12 +363,38 @@ jobs: - sudo apt-get install --no-install-recommends -y clang - sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 - sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 ++ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV + -+ - name: Install OpenSSL 1.0 on modern MacOS -+ # Needed for recent versions of MacOS as they ship with OpenSSL 1.1 by default -+ if: matrix.os == 'macos-13' && matrix.env.OPENSSL_1_0 == 'YES' ++ - name: Install OpenSSL 3.0 on modern MacOS ++ # OpenSSL 1.1 is installed by default, so we need to install 3.0 manually ++ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_3_0 == 'YES' + run: | -+ curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz -+ tar -xzf openssl-1.0.2u.tar.gz -+ cd openssl-1.0.2u -+ ./Configure --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared shared darwin64-x86_64-cc -+ #make depend -+ make -j $(nproc) -+ sudo make install_sw -j $(nproc) -+ -+ OPENSSL_ROOT_DIR=/usr/local/ssl ++ brew install openssl@3.0 ++ ++ OPENSSL_ROOT_DIR=$(brew --prefix openssl@3.0) + LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" + PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -+ ++ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib ++ + echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV + echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV + echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH + echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ echo "DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV - -- - name: Set up OpenSSL 1.1 on MacOS -+ - name: Set up OpenSSL 1.1 on modern MacOS -+ # OpenSSL 1.1 is installed by default, so we just need to set the paths - if: matrix.os == 'macos-latest' && matrix.env.OPENSSL_1_1 == 'YES' - run: | - OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) -- LDFLAGS=-L{$OPENSSL_ROOT_DIR}/lib -+ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib - CFLAGS=-I${OPENSSL_ROOT_DIR}/include - ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" - PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -@@ -365,6 +404,44 @@ jobs: - echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH - echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV - echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ -+ - name: Install OpenSSL 1.0 on modern Linux -+ # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default -+ if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_0 == 'YES' ++ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV ++ ++ - name: Install OpenSSL 1.1 on modern Linux ++ # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default ++ if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_1 == 'YES' + run: | -+ curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz -+ tar -xzf openssl-1.0.2u.tar.gz -+ cd openssl-1.0.2u -+ ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared ++ curl -O -L https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz ++ tar -xzf openssl-1.1.1w.tar.gz ++ cd openssl-1.1.1w ++ ./config --prefix=/usr/local/ssl1.1 --openssldir=/usr/local/ssl1.1 shared + make depend -+ make -j $(nproc) -+ sudo make install_sw -j $(nproc) ++ make -j ${{ env.cores }} ++ sudo make install_sw -j ${{ env.cores }} + sudo ldconfig + -+ OPENSSL_ROOT_DIR=/usr/local/ssl ++ OPENSSL_ROOT_DIR=/usr/local/ssl1.1 + LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib + CFLAGS=-I${OPENSSL_ROOT_DIR}/include + ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" @@ -8768,45 +774,51 @@ index 39b0bc86..7fe95108 100644 + echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV + -+ - name: Install OpenSSL 1.1 on modern Linux -+ # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default -+ if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_1 == 'YES' ++ - name: Print tool version information ++ run: | ++ openssl version ++ cc --version ++ cmake --version ++ clang --version ++ ++ - name: Run CMake + run: | -+ curl -O -L https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz -+ tar -xzf openssl-1.1.1w.tar.gz -+ cd openssl-1.1.1w -+ ./config -+ make -j $(nproc) -+ sudo make install_sw -j $(nproc) -+ sudo ldconfig - - - name: Print tool version information - run: | -@@ -375,7 +452,7 @@ jobs: - - - name: Run CMake - run: | -- cmake -S . -B CMakeFiles\ + cmake -S . -B output\ - -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ - -DBUILD_SHARED_LIBS=${{ matrix.env.BUILD_SHARED }}\ - -DCIVETWEB_THIRD_PARTY_DIR=../src/third-party\ -@@ -403,12 +480,29 @@ jobs: - - name: Build MacOS Package - if: matrix.env.MACOSX_PACKAGE == 1 - run: | -- make -f Makefile.osx package -+ make -f Makefile.osx package -j $(nproc) - - - name: Build executable - run: | -- cmake --build CMakeFiles -- -j $(nproc) -+ cmake --build output -- -j $(nproc) - - - name: Check executable - run: | -- ./CMakeFiles/src/civetweb -I -\ No newline at end of file ++ -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ ++ -DBUILD_SHARED_LIBS=${{ matrix.env.BUILD_SHARED }}\ ++ -DCIVETWEB_THIRD_PARTY_DIR=../src/third-party\ ++ -DCIVETWEB_ENABLE_THIRD_PARTY_OUTPUT=YES\ ++ -DCIVETWEB_ENABLE_SSL=${{ matrix.env.ENABLE_SSL }}\ ++ -DCIVETWEB_DISABLE_CGI=${{ matrix.env.NO_CGI }}\ ++ -DCIVETWEB_SERVE_NO_FILES=${{ matrix.env.NO_FILES }}\ ++ -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${{ matrix.env.ENABLE_SSL_DYNAMIC_LOADING }}\ ++ -DCIVETWEB_SSL_OPENSSL_API_1_0=${{ matrix.env.OPENSSL_1_0 }}\ ++ -DCIVETWEB_SSL_OPENSSL_API_1_1=${{ matrix.env.OPENSSL_1_1 }}\ ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=${{ matrix.env.OPENSSL_3_0 }}\ ++ -DCIVETWEB_ENABLE_WEBSOCKETS=${{ matrix.env.ENABLE_WEBSOCKETS }}\ ++ -DCIVETWEB_ENABLE_CXX=${{ matrix.env.ENABLE_CXX }}\ ++ -DCIVETWEB_ENABLE_SERVER_STATS=${{ matrix.env.ENABLE_SERVER_STATS }}\ ++ -DCIVETWEB_ENABLE_LUA=${{ matrix.env.ENABLE_LUA }}\ ++ -DCIVETWEB_ENABLE_LUA_SHARED=${{ matrix.env.ENABLE_LUA_SHARED }}\ ++ -DCIVETWEB_ENABLE_DUKTAPE=${{ matrix.env.ENABLE_DUKTAPE }}\ ++ -DCIVETWEB_DISABLE_CACHING=${{ matrix.env.NO_CACHING }}\ ++ -DCIVETWEB_C_STANDARD=${{ matrix.env.C_STANDARD }}\ ++ -DCIVETWEB_CXX_STANDARD=${{ matrix.env.CXX_STANDARD }}\ ++ -DCIVETWEB_ALLOW_WARNINGS=${{ matrix.env.ALLOW_WARNINGS }}\ ++ -DCIVETWEB_ENABLE_IPV6=${{ matrix.env.ENABLE_IPV6 }}\ ++ ${{ env.ADDITIONAL_CMAKE_ARGS }} ++ ++ - name: Build MacOS Package ++ if: matrix.env.MACOSX_PACKAGE == 1 ++ run: | ++ make -f Makefile.osx package -j ${{ env.cores }} ++ ++ - name: Build executable ++ run: | ++ cmake --build output -- -j ${{ env.cores }} ++ ++ - name: Check executable ++ run: | + ./output/src/civetweb -I + + - name: Run unit tests @@ -8824,8 +836,237 @@ index 39b0bc86..7fe95108 100644 + # Run unit tests + gcc unittest/cgi_test.c -o output/cgi_test.cgi + cd output -+ CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test -j $(nproc) ++ CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test -j ${{ env.cores }} \ No newline at end of file +diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml +index afa5846d..8691823e 100644 +--- a/.github/workflows/cifuzz.yml ++++ b/.github/workflows/cifuzz.yml +@@ -18,7 +18,7 @@ jobs: + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash +- uses: actions/upload-artifact@v1 ++ uses: actions/upload-artifact@v7 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts +diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml +deleted file mode 100644 +index 1268ef38..00000000 +--- a/.github/workflows/codeql-analysis.yml ++++ /dev/null +@@ -1,66 +0,0 @@ +-name: "CodeQL" +- +-on: +- push: +- branches: [master] +- pull_request: +- # The branches below must be a subset of the branches above +- branches: [master] +- schedule: +- - cron: '0 19 * * 4' +- +-jobs: +- analyze: +- name: Analyze +- runs-on: ubuntu-latest +- +- strategy: +- fail-fast: false +- matrix: +- # Override automatic language detection by changing the below list +- # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] +- language: ['cpp'] +- # Learn more... +- # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection +- +- steps: +- - name: Checkout repository +- uses: actions/checkout@v2 +- with: +- # We must fetch at least the immediate parents so that if this is +- # a pull request then we can checkout the head. +- fetch-depth: 2 +- +- # If this run was triggered by a pull request event, then checkout +- # the head of the pull request instead of the merge commit. +- - run: git checkout HEAD^2 +- if: ${{ github.event_name == 'pull_request' }} +- +- # Initializes the CodeQL tools for scanning. +- - name: Initialize CodeQL +- uses: github/codeql-action/init@v1 +- with: +- languages: ${{ matrix.language }} +- # If you wish to specify custom queries, you can do so here or in a config file. +- # By default, queries listed here will override any specified in a config file. +- # Prefix the list here with "+" to use these queries and those in the config file. +- # queries: ./path/to/local/query, your-org/your-repo/queries@main +- +- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). +- # If this step fails, then you should remove it and run the build manually (see below) +- - name: Autobuild +- uses: github/codeql-action/autobuild@v1 +- +- # ℹ️ Command-line programs to run using the OS shell. +- # 📚 https://git.io/JvXDl +- +- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines +- # and modify them (or add more) to build your code if your project +- # uses a compiled language +- +- #- run: | +- # make bootstrap +- # make release +- +- - name: Perform CodeQL Analysis +- uses: github/codeql-action/analyze@v1 +diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml +new file mode 100644 +index 00000000..280d15ea +--- /dev/null ++++ b/.github/workflows/codeql.yml +@@ -0,0 +1,130 @@ ++# For most projects, this workflow file will not need changing; you simply need ++# to commit it to your repository. ++# ++# You may wish to alter this file to override the set of languages analyzed, ++# or to provide custom queries or build logic. ++# ++# ******** NOTE ******** ++# We have attempted to detect the languages in your repository. Please check ++# the `language` matrix defined below to confirm you have the correct set of ++# supported CodeQL languages. ++# ++name: "CodeQL" ++ ++on: ++ push: ++ branches: [ "master" ] ++ paths-ignore: ++ - src/third_party/** ++ schedule: ++ - cron: '0 0 * * *' ++ pull_request: ++ branches: '*' ++ paths-ignore: ++ - src/third_party/** ++ ++jobs: ++ analyze: ++ name: Analyze ++ # Runner size impacts CodeQL analysis time. To learn more, please see: ++ # - https://gh.io/recommended-hardware-resources-for-running-codeql ++ # - https://gh.io/supported-runners-and-hardware-resources ++ # - https://gh.io/using-larger-runners ++ # Consider using larger runners for possible analysis time improvements. ++ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} ++ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} ++ permissions: ++ actions: read ++ contents: read ++ security-events: write ++ ++ strategy: ++ fail-fast: false ++ matrix: ++ language: [ 'cpp' ] ++ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] ++ # Use only 'java' to analyze code written in Java, Kotlin or both ++ # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both ++ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support ++ ++ steps: ++ - name: Checkout repository ++ uses: actions/checkout@v6 ++ with: ++ submodules: recursive ++ ++ # Initializes the CodeQL tools for scanning. ++ - name: Initialize CodeQL ++ uses: github/codeql-action/init@v4 ++ with: ++ languages: ${{ matrix.language }} ++ # If you wish to specify custom queries, you can do so here or in a config file. ++ # By default, queries listed here will override any specified in a config file. ++ # Prefix the list here with "+" to use these queries and those in the config file. ++ ++ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs ++ # queries: security-extended,security-and-quality ++ queries: security-and-quality ++ ++ ++ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). ++ # If this step fails, then you should remove it and run the build manually (see below) ++ #- name: Autobuild ++ # uses: github/codeql-action/autobuild@v2 ++ ++ # ℹ️ Command-line programs to run using the OS shell. ++ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun ++ ++ # If the Autobuild fails above, remove it and uncomment the following three lines. ++ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. ++ ++ - run: | ++ make build WITH_ALL=1 ++ ++ - name: Perform CodeQL Analysis ++ uses: github/codeql-action/analyze@v4 ++ with: ++ category: "/language:${{matrix.language}}" ++ upload: false ++ id: step1 ++ ++ # Filter out rules with low severity or high false positve rate ++ # Also filter out warnings in third-party code ++ - name: Filter out unwanted errors and warnings ++ uses: advanced-security/filter-sarif@v1 ++ with: ++ patterns: | ++ -**:cpp/path-injection ++ -**:cpp/world-writable-file-creation ++ -**:cpp/poorly-documented-function ++ -**:cpp/potentially-dangerous-function ++ -**:cpp/use-of-goto ++ -**:cpp/integer-multiplication-cast-to-long ++ -**:cpp/comparison-with-wider-type ++ -**:cpp/leap-year/* ++ -**:cpp/ambiguously-signed-bit-field ++ -**:cpp/suspicious-pointer-scaling ++ -**:cpp/suspicious-pointer-scaling-void ++ -**:cpp/unsigned-comparison-zero ++ -**/cmake*/Modules/** ++ input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif ++ output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif ++ ++ - name: Upload CodeQL results to code scanning ++ uses: github/codeql-action/upload-sarif@v4 ++ with: ++ sarif_file: ${{ steps.step1.outputs.sarif-output }} ++ category: "/language:${{matrix.language}}" ++ ++ - name: Upload CodeQL results as an artifact ++ if: success() || failure() ++ uses: actions/upload-artifact@v7 ++ with: ++ name: codeql-results ++ path: ${{ steps.step1.outputs.sarif-output }} ++ retention-days: 5 ++ ++ - name: Fail if an error is found ++ run: | ++ ./.github/workflows/fail_on_error.py \ ++ ${{ steps.step1.outputs.sarif-output }}/cpp.sarif +diff --git a/.github/workflows/fail_on_error.py b/.github/workflows/fail_on_error.py +new file mode 100755 +index 00000000..f0a06ad6 +--- /dev/null ++++ b/.github/workflows/fail_on_error.py +@@ -0,0 +1,2 @@ ++# content removed, because it floods my email inbox. ++# see https://github.com/civetweb/civetweb/issues/1231#issuecomment-1967503931 diff --git a/.gitignore b/.gitignore index 8a3ae869..e5194fb8 100644 --- a/.gitignore @@ -8838,50 +1079,8 @@ index 8a3ae869..e5194fb8 100644 ################# ## Eclipse --- -2.34.1 - - -From 2775a1b860590a4a00f847073238371c718dcf9c Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Fri, 23 Aug 2024 12:57:42 +0200 -Subject: [PATCH 096/173] Revert - https://github.com/civetweb/civetweb/commit/19b70adc70ca66cc862f3fd065e354af64b7b011 - -Signed-off-by: yubiuser ---- - unittest/public_server.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/unittest/public_server.c b/unittest/public_server.c -index 6e0f71c7..93641f0d 100644 ---- a/unittest/public_server.c -+++ b/unittest/public_server.c -@@ -1316,7 +1316,7 @@ START_TEST(test_request_handlers) - char cmd_buf[1024]; - char *cgi_env_opt; - -- const char *server_host = "localhost"; //"test.domain"; -+ const char *server_host = "test.domain"; - - mark_point(); - --- -2.34.1 - - -From 8d4bcef5469073b2446b2d56c17930a206597acb Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Fri, 23 Aug 2024 13:25:02 +0200 -Subject: [PATCH 097/173] Keep only FreeBSD on Travis - -Signed-off-by: yubiuser ---- - .travis.yml | 519 +--------------------------------------------------- - 1 file changed, 1 insertion(+), 518 deletions(-) - diff --git a/.travis.yml b/.travis.yml -index 077fbd89..3d8e516b 100644 +index 8dabd5b4..3d8e516b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,60 +9,17 @@ cache: @@ -8945,7 +1144,23 @@ index 077fbd89..3d8e516b 100644 # Generate the build scripts with CMake - mkdir output - openssl version -@@ -130,16 +87,7 @@ after_failure: +@@ -82,6 +39,7 @@ before_script: + -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} + -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} + -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} + -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} + -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} + -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} +@@ -107,6 +65,7 @@ before_script: + -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} + -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} + -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} + -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} + -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} + -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} +@@ -128,16 +87,7 @@ after_failure: - if [[ -f "$COREFILE" ]]; then gdb -c "$COREFILE" example -ex "thread apply all bt" -ex "set pagination 0" -batch; fi @@ -8962,7 +1177,7 @@ index 077fbd89..3d8e516b 100644 - if [ "${RUN_UNITTEST}" == "1" ]; then CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test; fi -@@ -152,16 +100,7 @@ script: +@@ -150,16 +100,7 @@ script: fi - echo "Build and test script DONE" @@ -8979,7 +1194,7 @@ index 077fbd89..3d8e516b 100644 ######################################################################################### -@@ -173,350 +112,6 @@ after_success: +@@ -171,350 +112,6 @@ after_success: matrix: fast_finish: true include: @@ -9330,7 +1545,7 @@ index 077fbd89..3d8e516b 100644 ######################################################################################### ##### FREEBSD BUILD ###########=##################################################### ######################################################################################### -@@ -547,116 +142,4 @@ matrix: +@@ -545,116 +142,4 @@ matrix: ENABLE_DUKTAPE=NO NO_CACHING=NO ALLOW_WARNINGS=YES @@ -9437,1761 +1652,870 @@ index 077fbd89..3d8e516b 100644 -# CXX_STANDARD=auto -# BUILD_SHARED=NO -# NO_FILES=NO --# ENABLE_SSL=YES --# NO_CGI=NO --# ENABLE_IPV6=YES --# ENABLE_WEBSOCKETS=YES --# ENABLE_SERVER_STATS=YES --# ENABLE_LUA=YES --# ENABLE_LUA_SHARED=YES --# ENABLE_DUKTAPE=NO --# NO_CACHING=YES --# ALLOW_WARNINGS=YES -+ RUN_UNITTEST=1 -\ No newline at end of file --- -2.34.1 - - -From 6ab2ee088e6973d935071393d09cfd4b069b1ab5 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Fri, 23 Aug 2024 13:40:49 +0200 -Subject: [PATCH 098/173] Use wildcard matching - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 36 ++++++++++++++++++++++++----------- - 1 file changed, 25 insertions(+), 11 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index 7fe95108..9e905e87 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -358,7 +358,7 @@ jobs: - uses: actions/checkout@v4.1.7 - - - name: Install clang on Linux -- if: matrix.compiler == 'clang' && matrix.os == 'ubuntu-latest' -+ if: matrix.compiler == 'clang' && startsWith(matrix.os,'ubuntu') - run: | - sudo apt-get install --no-install-recommends -y clang - sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 -@@ -366,17 +366,17 @@ jobs: - - - name: Install OpenSSL 1.0 on modern MacOS - # Needed for recent versions of MacOS as they ship with OpenSSL 1.1 by default -- if: matrix.os == 'macos-13' && matrix.env.OPENSSL_1_0 == 'YES' -+ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_0 == 'YES' - run: | - curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz - tar -xzf openssl-1.0.2u.tar.gz - cd openssl-1.0.2u -- ./Configure --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared shared darwin64-x86_64-cc -- #make depend -+ ./Configure --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared shared darwin64-x86_64-cc -+ make depend - make -j $(nproc) - sudo make install_sw -j $(nproc) - -- OPENSSL_ROOT_DIR=/usr/local/ssl -+ OPENSSL_ROOT_DIR=/usr/local/ssl1.0 - LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib - CFLAGS=-I${OPENSSL_ROOT_DIR}/include - ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -@@ -391,7 +391,7 @@ jobs: - - - name: Set up OpenSSL 1.1 on modern MacOS - # OpenSSL 1.1 is installed by default, so we just need to set the paths -- if: matrix.os == 'macos-latest' && matrix.env.OPENSSL_1_1 == 'YES' -+ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_1 == 'YES' - run: | - OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) - LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -@@ -407,18 +407,18 @@ jobs: - - - name: Install OpenSSL 1.0 on modern Linux - # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default -- if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_0 == 'YES' -+ if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_0 == 'YES' - run: | - curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz - tar -xzf openssl-1.0.2u.tar.gz - cd openssl-1.0.2u -- ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared -+ ./config --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared - make depend - make -j $(nproc) - sudo make install_sw -j $(nproc) - sudo ldconfig - -- OPENSSL_ROOT_DIR=/usr/local/ssl -+ OPENSSL_ROOT_DIR=/usr/local/ssl1.0 - LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib - CFLAGS=-I${OPENSSL_ROOT_DIR}/include - ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -@@ -433,15 +433,29 @@ jobs: - - - name: Install OpenSSL 1.1 on modern Linux - # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default -- if: matrix.os == 'ubuntu-latest' && matrix.env.OPENSSL_1_1 == 'YES' -+ if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_1 == 'YES' - run: | - curl -O -L https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz - tar -xzf openssl-1.1.1w.tar.gz - cd openssl-1.1.1w -- ./config -+ ./config --prefix=/usr/local/ssl1.1 --openssldir=/usr/local/ssl1.1 shared -+ make depend - make -j $(nproc) - sudo make install_sw -j $(nproc) - sudo ldconfig +-# ENABLE_SSL=YES +-# NO_CGI=NO +-# ENABLE_IPV6=YES +-# ENABLE_WEBSOCKETS=YES +-# ENABLE_SERVER_STATS=YES +-# ENABLE_LUA=YES +-# ENABLE_LUA_SHARED=YES +-# ENABLE_DUKTAPE=NO +-# NO_CACHING=YES +-# ALLOW_WARNINGS=YES ++ RUN_UNITTEST=1 +\ No newline at end of file +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c5368c08..602fbe41 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,11 +1,8 @@ +-# Use at least CMake 3.3 +-cmake_minimum_required (VERSION 3.3.0) +-cmake_policy(VERSION 3.2.2) +-cmake_policy(SET CMP0054 NEW) +-cmake_policy(SET CMP0057 NEW) ++cmake_minimum_required(VERSION 3.10) ++cmake_policy(VERSION 3.10) + + # Set up the project +-project (civetweb) ++project (civetweb VERSION 1.16.0) + + # Detect the platform reliably + if(ZEPHYR_BASE) +@@ -37,7 +34,7 @@ include(AddCXXCompilerFlag) + include(DetermineTargetArchitecture) + include(CMakeDependentOption) + +-set(CIVETWEB_VERSION "1.15.0" CACHE STRING "The version of the civetweb library") ++set(CIVETWEB_VERSION "1.16.0" CACHE STRING "The version of the civetweb library") + string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CIVETWEB_VERSION_MATCH "${CIVETWEB_VERSION}") + if ("${CIVETWEB_VERSION_MATCH}" STREQUAL "") + message(FATAL_ERROR "Must specify a semantic version: major.minor.patch") +@@ -80,6 +77,10 @@ message(STATUS "Disable caching support - ${CIVETWEB_DISABLE_CACHING}") + option(CIVETWEB_ENABLE_CXX "Enables the C++ wrapper library" OFF) + message(STATUS "C++ wrappers - ${CIVETWEB_ENABLE_CXX}") + ++# HTTP2 Support ++option(CIVETWEB_ENABLE_HTTP2 "Enables HTTP2 support" OFF) ++message(STATUS "Use HTPP2 - ${CIVETWEB_ENABLE_HTTP2}") + -+ OPENSSL_ROOT_DIR=/usr/local/ssl1.1 -+ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -+ CFLAGS=-I${OPENSSL_ROOT_DIR}/include -+ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -+ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -+ -+ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV -+ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV -+ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH -+ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV -+ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ echo "LD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV - - - name: Print tool version information - run: | --- -2.34.1 - - -From 13b54b2fcfd8fee34a60c4b9dceae25da46c481d Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sun, 15 Sep 2024 07:38:44 +0000 -Subject: [PATCH 099/173] Bump github/codeql-action from 1 to 3 - -Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 3. -- [Release notes](https://github.com/github/codeql-action/releases) -- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) -- [Commits](https://github.com/github/codeql-action/compare/v1...v3) - ---- -updated-dependencies: -- dependency-name: github/codeql-action - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/codeql-analysis.yml | 6 +++--- - .github/workflows/codeql.yml | 6 +++--- - 2 files changed, 6 insertions(+), 6 deletions(-) - -diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml -index 1268ef38..58b52c82 100644 ---- a/.github/workflows/codeql-analysis.yml -+++ b/.github/workflows/codeql-analysis.yml -@@ -38,7 +38,7 @@ jobs: + # IP Version 6 + option(CIVETWEB_ENABLE_IPV6 "Enables the IP version 6 support" ON) + message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}") +@@ -88,6 +89,11 @@ message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}") + option(CIVETWEB_ENABLE_WEBSOCKETS "Enable websockets connections" OFF) + message(STATUS "Websockets support - ${CIVETWEB_ENABLE_WEBSOCKETS}") - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL -- uses: github/codeql-action/init@v1 -+ uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. -@@ -49,7 +49,7 @@ jobs: - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild -- uses: github/codeql-action/autobuild@v1 -+ uses: github/codeql-action/autobuild@v3 ++# X DOM sockets support ++option(CIVETWEB_ENABLE_X_DOM_SOCKET "Enable X DOM sockets support" OFF) ++message(STATUS "X DOM sockets support - ${CIVETWEB_ENABLE_X_DOM_SOCKET}") ++ ++ + # Server statistics support + option(CIVETWEB_ENABLE_SERVER_STATS "Enable server statistics" OFF) + message(STATUS "Server statistics support - ${CIVETWEB_ENABLE_SERVER_STATS}") +@@ -179,7 +185,7 @@ if (CIVETWEB_ENABLE_LUA) + mark_as_advanced(CIVETWEB_LUA_SQLITE_VERSION) - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl -@@ -63,4 +63,4 @@ jobs: - # make release + # Lua SQLite Verification Hash +- set(CIVETWEB_LUA_SQLITE_MD5_HASH 43234ae08197dfce6da02482ed14ec92 CACHE STRING ++ set(CIVETWEB_LUA_SQLITE_MD5_HASH ff7abd4aa8bd549eb18298fb954612f8 CACHE STRING + "The hash of Lua SQLite archive to be downloaded") + set_property(CACHE CIVETWEB_LUA_SQLITE_MD5_HASH PROPERTY VALUE ${CIVETWEB_LUA_SQLITE_MD5_HASH}) + mark_as_advanced(CIVETWEB_LUA_SQLITE_MD5_HASH) +@@ -227,10 +233,16 @@ message(STATUS "Compile for OpenSSL 1.0 API - ${CIVETWEB_SSL_OPENSSL_API_1_0}") + option(CIVETWEB_SSL_OPENSSL_API_1_1 "Use the OpenSSL 1.1 API" ON) + message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}") - - name: Perform CodeQL Analysis -- uses: github/codeql-action/analyze@v1 -+ uses: github/codeql-action/analyze@v3 -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 81649227..efa1f8ca 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -51,7 +51,7 @@ jobs: +-# OpenSSL 1.1 API ++# OpenSSL 3.0 API + option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF) + message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}") - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL -- uses: github/codeql-action/init@v2 -+ uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. -@@ -78,7 +78,7 @@ jobs: - ./.github/workflows/codeql-buildscript.sh - - - name: Perform CodeQL Analysis -- uses: github/codeql-action/analyze@v2 -+ uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" - upload: false -@@ -107,7 +107,7 @@ jobs: - output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif - - - name: Upload CodeQL results to code scanning -- uses: github/codeql-action/upload-sarif@v2 -+ uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ${{ steps.step1.outputs.sarif-output }} - category: "/language:${{matrix.language}}" --- -2.34.1 - - -From 0c237b78c35061416c9efd09e176c8f23986ff98 Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sun, 15 Sep 2024 07:38:46 +0000 -Subject: [PATCH 100/173] Bump actions/upload-artifact from 1 to 4 - -Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 1 to 4. -- [Release notes](https://github.com/actions/upload-artifact/releases) -- [Commits](https://github.com/actions/upload-artifact/compare/v1...v4) - ---- -updated-dependencies: -- dependency-name: actions/upload-artifact - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/cifuzz.yml | 2 +- - .github/workflows/codeql.yml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml -index afa5846d..d62ff855 100644 ---- a/.github/workflows/cifuzz.yml -+++ b/.github/workflows/cifuzz.yml -@@ -18,7 +18,7 @@ jobs: - fuzz-seconds: 600 - dry-run: false - - name: Upload Crash -- uses: actions/upload-artifact@v1 -+ uses: actions/upload-artifact@v4 - if: failure() && steps.build.outcome == 'success' - with: - name: artifacts -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 81649227..d2a505a3 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -114,7 +114,7 @@ jobs: ++option(CIVETWEB_ENABLE_GNUTLS "Use the GnuTls" OFF) ++message(STATUS "SSL support (GnuTLS) - ${CIVETWEB_ENABLE_GNUTLS}") ++ ++option(CIVETWEB_ENABLE_MBEDTLS "Use the MbedTls" OFF) ++message(STATUS "SSL support (MbedTLS) - ${CIVETWEB_ENABLE_MBEDTLS}") ++ + # Dynamically load or link the SSL libraries + cmake_dependent_option( + CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING "Dynamically loads the SSL library rather than linking it" ON +@@ -488,12 +500,18 @@ if (${CMAKE_BUILD_TYPE} MATCHES "[Dd]ebug") + add_definitions(-O0) + add_definitions(-g) + endif() ++if (CIVETWEB_ENABLE_HTTP2) ++ add_definitions(-DUSE_HTTP2) ++endif() + if (CIVETWEB_ENABLE_IPV6) + add_definitions(-DUSE_IPV6) + endif() + if (CIVETWEB_ENABLE_WEBSOCKETS) + add_definitions(-DUSE_WEBSOCKET) + endif() ++if (CIVETWEB_ENABLE_X_DOM_SOCKET) ++ add_definitions(-DUSE_X_DOM_SOCKET) ++endif() + if (CIVETWEB_ENABLE_SERVER_STATS) + add_definitions(-DUSE_SERVER_STATS) + endif() +@@ -523,6 +541,10 @@ if (CIVETWEB_ENABLE_MEMORY_DEBUGGING) + endif() + if (NOT CIVETWEB_ENABLE_SSL) + add_definitions(-DNO_SSL) ++elseif (CIVETWEB_ENABLE_GNUTLS) ++ add_definitions(-DUSE_GNUTLS) ++elseif (CIVETWEB_ENABLE_MBEDTLS) ++ add_definitions(-DUSE_MBEDTLS) + elseif (NOT CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING) + add_definitions(-DNO_SSL_DL) + else() +@@ -627,15 +649,15 @@ configure_file( + ) - - name: Upload CodeQL results as an artifact - if: success() || failure() -- uses: actions/upload-artifact@v3 -+ uses: actions/upload-artifact@v4 - with: - name: codeql-results - path: ${{ steps.step1.outputs.sarif-output }} --- -2.34.1 - - -From aeb03a812e96562e42c688ac6d2919537439ba38 Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sun, 15 Sep 2024 07:38:48 +0000 -Subject: [PATCH 101/173] Bump actions/checkout from 2 to 4 - -Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. -- [Release notes](https://github.com/actions/checkout/releases) -- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) -- [Commits](https://github.com/actions/checkout/compare/v2...v4) - ---- -updated-dependencies: -- dependency-name: actions/checkout - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/codeql-analysis.yml | 2 +- - .github/workflows/codeql.yml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml -index 1268ef38..d1640226 100644 ---- a/.github/workflows/codeql-analysis.yml -+++ b/.github/workflows/codeql-analysis.yml -@@ -25,7 +25,7 @@ jobs: + install( +- FILES ++ FILES + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" +- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" ++ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + ) - steps: - - name: Checkout repository -- uses: actions/checkout@v2 -+ uses: actions/checkout@v4 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 81649227..ec64d43b 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -45,7 +45,7 @@ jobs: + install( +- FILES ++ FILES + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-cpp.pc" +- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" ++ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + ) - steps: - - name: Checkout repository -- uses: actions/checkout@v3 -+ uses: actions/checkout@v4 - with: - submodules: recursive + write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake +@@ -686,4 +708,3 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_PACKAGE_DEPENDS}") --- -2.34.1 - - -From af3825fcd33c483501386432c1c51d677077e186 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Sun, 15 Sep 2024 12:29:52 +0200 -Subject: [PATCH 102/173] Remove duplicated codeql workflow - -Signed-off-by: yubiuser ---- - .github/workflows/codeql-analysis.yml | 66 ------------------------- - .github/workflows/codeql-buildscript.sh | 3 -- - .github/workflows/codeql.yml | 8 +-- - 3 files changed, 4 insertions(+), 73 deletions(-) - delete mode 100644 .github/workflows/codeql-analysis.yml - delete mode 100644 .github/workflows/codeql-buildscript.sh - -diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml -deleted file mode 100644 -index 6f033055..00000000 ---- a/.github/workflows/codeql-analysis.yml -+++ /dev/null -@@ -1,66 +0,0 @@ --name: "CodeQL" -- --on: -- push: -- branches: [master] -- pull_request: -- # The branches below must be a subset of the branches above -- branches: [master] -- schedule: -- - cron: '0 19 * * 4' -- --jobs: -- analyze: -- name: Analyze -- runs-on: ubuntu-latest -- -- strategy: -- fail-fast: false -- matrix: -- # Override automatic language detection by changing the below list -- # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] -- language: ['cpp'] -- # Learn more... -- # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection -- -- steps: -- - name: Checkout repository -- uses: actions/checkout@v4 -- with: -- # We must fetch at least the immediate parents so that if this is -- # a pull request then we can checkout the head. -- fetch-depth: 2 -- -- # If this run was triggered by a pull request event, then checkout -- # the head of the pull request instead of the merge commit. -- - run: git checkout HEAD^2 -- if: ${{ github.event_name == 'pull_request' }} -- -- # Initializes the CodeQL tools for scanning. -- - name: Initialize CodeQL -- uses: github/codeql-action/init@v3 -- with: -- languages: ${{ matrix.language }} -- # If you wish to specify custom queries, you can do so here or in a config file. -- # By default, queries listed here will override any specified in a config file. -- # Prefix the list here with "+" to use these queries and those in the config file. -- # queries: ./path/to/local/query, your-org/your-repo/queries@main -- -- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). -- # If this step fails, then you should remove it and run the build manually (see below) -- - name: Autobuild -- uses: github/codeql-action/autobuild@v3 -- -- # ℹ️ Command-line programs to run using the OS shell. -- # 📚 https://git.io/JvXDl + # Finalize CPack settings + include(CPack) - -- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines -- # and modify them (or add more) to build your code if your project -- # uses a compiled language +diff --git a/CREDITS.md b/CREDITS.md +index fc98b637..f6a2a79d 100644 +--- a/CREDITS.md ++++ b/CREDITS.md +@@ -130,6 +130,7 @@ + * Lammert Bies + * Lars Immisch + * Lawrence ++* Lev275568 + * Li Peng + * Lianghui + * Lorenzo Canepa +@@ -225,6 +226,7 @@ + * Torben Jonas + * Uilian Ries + * Ulrich Hertlein ++* videofan3d + * Walt Steverson + * wangli28 + * webxer +diff --git a/Dockerfile b/Dockerfile +new file mode 100644 +index 00000000..3bb7540a +--- /dev/null ++++ b/Dockerfile +@@ -0,0 +1,53 @@ ++# --------------------------------------------------- ++# Build Stage ++# --------------------------------------------------- ++ ++# Use Alpine Linux 3.18 as the base image for the build stage ++FROM alpine:3.18 AS build ++ ++# Update package list and install build dependencies ++RUN apk update && \ ++ apk add --no-cache \ ++ build-base zlib-dev ++ ++# Set the working directory inside the container ++WORKDIR /civetweb ++ ++# Copy source code and other necessary files into the container ++COPY src ./src/ ++COPY include ./include/ ++COPY Makefile ./ ++COPY resources ./resources/ ++COPY *.md ./ ++ ++# Build Civetweb with all features and install it into /app directory ++RUN make build && \ ++ make WITH_ALL=1 && \ ++ make install PREFIX=/app ++ ++# --------------------------------------------------- ++# Image Stage ++# --------------------------------------------------- ++ ++# Use Alpine Linux 3.18 as the base image for the final stage ++FROM alpine:3.18 ++ ++# Update package list and install runtime dependencies ++RUN apk update && \ ++ apk add --no-cache \ ++ libstdc++ zlib ++ ++# Create a non-root user and group for running Civetweb ++RUN addgroup -S civetweb && adduser -S civetweb -G civetweb ++ ++# Switch to the non-root user ++USER civetweb ++ ++# Copy the built application from the build stage into this stage ++COPY --chown=civetweb:civetweb --from=build /app/ /app/ ++ ++# Expose port 8080 for the application ++EXPOSE 8080 ++ ++# Set the entry point for the container ++ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] +diff --git a/LICENSE.md b/LICENSE.md +index a74a0fe4..db39daf4 100644 +--- a/LICENSE.md ++++ b/LICENSE.md +@@ -11,7 +11,7 @@ Civetweb License + + ### Included with all features. + +-> Copyright (c) 2013-2021 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) ++> Copyright (c) 2013-2025 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) + > + > Copyright (c) 2004-2013 Sergey Lyubka + > +diff --git a/Makefile b/Makefile +index 31954e47..7831ce39 100644 +--- a/Makefile ++++ b/Makefile +@@ -71,7 +71,11 @@ ifdef WITH_CFLAGS + CFLAGS += $(WITH_CFLAGS) + endif + +-LIBS = -lpthread -lm $(LOPT) ++LIBS = ++ifneq ($(TARGET_OS), RTEMS) ++LIBS += -lpthread ++endif ++LIBS += -lm $(LOPT) + + ifdef WITH_DEBUG + CFLAGS += -g -DDEBUG +@@ -93,6 +97,9 @@ endif + + ifdef NO_SSL + CFLAGS += -DNO_SSL ++else ifdef WITH_GNUTLS ++ CFLAGS += -DUSE_GNUTLS ++ LIBS += -lgnutls -lhogweed -lgmp -lnettle + else ifdef WITH_MBEDTLS + CFLAGS += -DUSE_MBEDTLS + LIBS += -lmbedcrypto -lmbedtls -lmbedx509 +@@ -100,6 +107,8 @@ else ifdef WITH_OPENSSL_API_1_0 + CFLAGS += -DOPENSSL_API_1_0 + else ifdef WITH_OPENSSL_API_1_1 + CFLAGS += -DOPENSSL_API_1_1 ++else ifdef WITH_OPENSSL_API_3_0 ++ CFLAGS += -DOPENSSL_API_3_0 + else + #Use OpenSSL 1.1 API version as default + CFLAGS += -DOPENSSL_API_1_1 +@@ -176,7 +185,9 @@ ifdef WITH_COMPRESSION + endif + + ifdef WITH_ZLIB ++ifneq ($(TARGET_OS), RTEMS) + LIBS += -lz ++endif + CFLAGS += -DUSE_ZLIB + endif + +@@ -295,9 +306,11 @@ help: + @echo " WITH_CPP=1 build library with c++ classes" + @echo " WITH_EXPERIMENTAL=1 build with experimental features" + @echo " WITH_DAEMONIZE=1 build with daemonize." ++ @echo " WITH_GNUTLS=1 build with GnuTLS support." + @echo " WITH_MBEDTLS=1 build with mbedTLS support." + @echo " WITH_OPENSSL_API_1_0=1 build with OpenSSL 1.0.x support." + @echo " WITH_OPENSSL_API_1_1=1 build with OpenSSL 1.1.x support." ++ @echo " WITH_OPENSSL_API_3_0=1 build with OpenSSL 3.0.x support." + @echo " NO_SSL=1 build without SSL support. Build will not need libcrypto/libssl." + @echo " NO_CGI=1 build without CGI support." + @echo " NO_CACHING=1 disable caching. Send no-cache/no-store headers." +@@ -443,4 +456,3 @@ indent: + astyle --suffix=none --style=linux --indent=spaces=4 --lineend=linux include/*.h src/*.c src/*.cpp src/*.inl examples/*/*.c examples/*/*.cpp + + .PHONY: all help build install clean lib so - -- #- run: | -- # make bootstrap -- # make release +diff --git a/Makefile.osx b/Makefile.osx +index 44260e84..a14fdee9 100644 +--- a/Makefile.osx ++++ b/Makefile.osx +@@ -13,7 +13,7 @@ WITH_LUA = 1 + PACKAGE = Civetweb + BUILD_DIR = out + +-CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch i386 -arch x86_64 ++CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.6 -ObjC -arch x86_64 -arch arm64 + LDFLAGS += -framework Cocoa + + DMG_DIR = $(BUILD_DIR)/dmg +diff --git a/README.md b/README.md +index 1d1c82b8..5ff09779 100644 +--- a/README.md ++++ b/README.md +@@ -9,18 +9,12 @@ + [![Forks](https://img.shields.io/github/forks/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/network/members) + [![Latest Release](https://img.shields.io/github/v/release/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/releases) + +-Continuous integration for Linux and macOS ([Travis CI](https://app.travis-ci.com/github/civetweb/civetweb)): - -- - name: Perform CodeQL Analysis -- uses: github/codeql-action/analyze@v3 -diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh -deleted file mode 100644 -index 366db622..00000000 ---- a/.github/workflows/codeql-buildscript.sh -+++ /dev/null -@@ -1,3 +0,0 @@ --#!/usr/bin/env bash +-[![Travis Build Status](https://api.travis-ci.com/civetweb/civetweb.svg?branch=master)](https://app.travis-ci.com/github/civetweb/civetweb) - --make build WITH_ALL=1 -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 51632e65..d9f751b4 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -12,8 +12,8 @@ - name: "CodeQL" +-Continuous integration for Windows ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): ++Continuous integration ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): - on: -- # push: -- # branches: [ "main", "master" ] -+ push: -+ branches: [ "master" ] - schedule: - - cron: '0 0 * * *' - pull_request: -@@ -27,7 +27,7 @@ jobs: - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners - # Consider using larger runners for possible analysis time improvements. -- runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }} -+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} - permissions: - actions: read -@@ -75,7 +75,7 @@ jobs: - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - - run: | -- ./.github/workflows/codeql-buildscript.sh -+ make build WITH_ALL=1 + [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/civetweb/civetweb?svg=true)](https://ci.appveyor.com/project/civetweb/civetweb/branch/master) - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 --- -2.34.1 - - -From e0cd57edd1a7b8ce1d0fd6d9a6457c86e1b266e3 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Fri, 20 Sep 2024 19:00:58 +0200 -Subject: [PATCH 103/173] Fix LUA error reporting - -Signed-off-by: DL6ER ---- - src/mod_lua.inl | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index ec5e1aeb..7bacf6f3 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -673,7 +673,9 @@ run_lsp_kepler(struct mg_connection *conn, + Test coverage check ([coveralls](https://coveralls.io/github/civetweb/civetweb), [codecov](https://codecov.io/gh/civetweb/civetweb/branch/master)) (using different tools/settings): - } else { - /* Success loading chunk. Call it. */ -- lua_pcall(L, 0, 0, 1); -+ lua_ok = lua_pcall(L, 0, 0, 0); -+ if(lua_ok != LUA_OK) -+ lua_cry(conn, lua_ok, L, "LSP", "call"); - } - return 0; - } -@@ -789,7 +791,9 @@ run_lsp_civetweb(struct mg_connection *conn, - lua_pcall(L, 1, 0, 0); - } else { - /* Success loading chunk. Call it. */ -- lua_pcall(L, 0, 0, 1); -+ lua_ok = lua_pcall(L, 0, 0, 0); -+ if(lua_ok != LUA_OK) -+ lua_cry(conn, lua_ok, L, "LSP", "call"); - } +-[![Coveralls](https://img.shields.io/coveralls/civetweb/civetweb.svg?maxAge=3600)]() +-[![Coverage Status](https://coveralls.io/repos/github/civetweb/civetweb/badge.svg?branch=master)](https://coveralls.io/github/civetweb/civetweb?branch=master) + [![codecov](https://codecov.io/gh/civetweb/civetweb/branch/master/graph/badge.svg)](https://codecov.io/gh/civetweb/civetweb) - /* Progress until after the Lua closing tag. */ --- -2.34.1 - - -From 53e305b43a7256cbee6a91bcfa3ba6e4ddb15db2 Mon Sep 17 00:00:00 2001 -From: Kacper Stasik -Date: Sun, 22 Sep 2024 22:43:57 +0200 -Subject: [PATCH 104/173] fix: missing host name for ssl - ---- - src/civetweb.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index dced1362..6d8a0c1a 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -19532,6 +19532,9 @@ mg_connect_websocket_client(const char *host, - memset(&client_options, 0, sizeof(client_options)); - client_options.host = host; - client_options.port = port; -+ if (use_ssl) { -+ client_options.host_name = host; -+ } + Static source code analysis ([Coverity](https://scan.coverity.com/projects/5784)): [![Coverity Scan Build Status](https://scan.coverity.com/projects/5784/badge.svg)](https://scan.coverity.com/projects/5784) +@@ -54,18 +48,14 @@ CivetWeb must be used with an earlier or later version (see also [here](https:// + Bugs and requests should be filed on GitHub + [https://github.com/civetweb/civetweb/issues](https://github.com/civetweb/civetweb/issues) - return mg_connect_websocket_client_impl(&client_options, - use_ssl, --- -2.34.1 - - -From b02d56d6d03996446071c7de0fc1c71473a504d3 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Mon, 23 Sep 2024 13:13:35 +0200 -Subject: [PATCH 105/173] Abort early on Lua errors to avoid sending half-done - pages to the user and call the lua_error_handler to get the error to the user - as well as to the log file - -Signed-off-by: DL6ER - -Handle stack properly and add static prototype in/for lua_error_handler() - -Signed-off-by: DL6ER ---- - src/mod_lua.inl | 23 ++++++++++++++++++++--- - 1 file changed, 20 insertions(+), 3 deletions(-) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index 7bacf6f3..df4b5bfe 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -10,6 +10,9 @@ - #include "civetweb_lua.h" - #include "civetweb_private_lua.h" +-New releases are announced on Google Groups +-[https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb) +- +-Formerly some support question and discussion threads have been at [Google groups](https://groups.google.com/d/forum/civetweb). +-Recent questions and discussions use [GitHub issues](https://github.com/civetweb/civetweb/issues). +- + Source releases can be found on GitHub + [https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases) -+/* Prototypes */ -+static int -+lua_error_handler(lua_State *L); + A very brief overview can be found on GitHub Pages + [https://civetweb.github.io/civetweb/](https://civetweb.github.io/civetweb/) - #if defined(_WIN32) - static void * -@@ -669,13 +672,19 @@ run_lsp_kepler(struct mg_connection *conn, - /* Syntax error or OOM. - * Error message is pushed on stack. */ - lua_pcall(L, 1, 0, 0); -- lua_cry(conn, lua_ok, L, "LSP", "execute"); /* XXX TODO: everywhere ! */ -+ lua_cry(conn, lua_ok, L, "LSP Kepler", "execute"); -+ lua_error_handler(L); -+ return 1; ++Note: The [Google group](https://groups.google.com/d/forum/civetweb) is no longer used but has been replaced by [GitHub issues](https://github.com/civetweb/civetweb/issues). ++ - } else { - /* Success loading chunk. Call it. */ - lua_ok = lua_pcall(L, 0, 0, 0); - if(lua_ok != LUA_OK) -- lua_cry(conn, lua_ok, L, "LSP", "call"); -+ { -+ lua_cry(conn, lua_ok, L, "LSP Kepler", "call"); -+ lua_error_handler(L); -+ return 1; -+ } - } - return 0; - } -@@ -789,11 +798,18 @@ run_lsp_civetweb(struct mg_connection *conn, - /* Syntax error or OOM. - * Error message is pushed on stack. */ - lua_pcall(L, 1, 0, 0); -+ lua_cry(conn, lua_ok, L, "LSP", "execute"); -+ lua_error_handler(L); -+ return 1; - } else { - /* Success loading chunk. Call it. */ - lua_ok = lua_pcall(L, 0, 0, 0); - if(lua_ok != LUA_OK) -+ { - lua_cry(conn, lua_ok, L, "LSP", "call"); -+ lua_error_handler(L); -+ return 1; -+ } - } + Quick start documentation + -------------------------- +@@ -75,6 +65,7 @@ Quick start documentation + - [docs/Building.md](https://github.com/civetweb/civetweb/blob/master/docs/Building.md) - Building the Server (quick start guide) + - [docs/Embedding.md](https://github.com/civetweb/civetweb/blob/master/docs/Embedding.md) - Embedding (how to add HTTP support to an existing application) + - [docs/OpenSSL.md](https://github.com/civetweb/civetweb/blob/master/docs/OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. ++- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use CivetWeb in a Docker container. + - [API documentation](https://github.com/civetweb/civetweb/tree/master/docs/api) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). + - [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md) - Release Notes + - [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - Security Policy +@@ -133,6 +124,8 @@ simplicity by a carefully selected list of features: - /* Progress until after the Lua closing tag. */ -@@ -2786,12 +2802,13 @@ lua_error_handler(lua_State *L) - lua_call(L, 2, 0); - IGNORE_UNUSED_RESULT( - luaL_dostring(L, "mg.write(debug.traceback(), '\\n')")); -+ lua_pop(L, 1); /* pop mg */ - } else { - printf("Lua error: [%s]\n", error_msg); - IGNORE_UNUSED_RESULT( - luaL_dostring(L, "print(debug.traceback(), '\\n')")); - } -- /* TODO(lsm, low): leave the stack balanced */ -+ lua_pop(L, 1); /* pop error message */ + [Mbed TLS](https://github.com/ARMmbed/mbedtls) - return 0; - } --- -2.34.1 - - -From 0f6a77d46012c5f2b22378883eeead5d3851799e Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Mon, 23 Sep 2024 20:35:54 +0200 -Subject: [PATCH 106/173] Properly generate traceback in lua_error_handler() - avoiding the first line showing the manual call to debug.traceback() itself - -Signed-off-by: DL6ER ---- - src/mod_lua.inl | 28 ++++++++++++++++++++++++---- - 1 file changed, 24 insertions(+), 4 deletions(-) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index df4b5bfe..e97a28cc 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -2796,13 +2796,33 @@ lua_error_handler(lua_State *L) ++[GNU TLS](https://gnutls.org) ++ - lua_getglobal(L, "mg"); - if (!lua_isnil(L, -1)) { -- lua_getfield(L, -1, "write"); /* call mg.write() */ -+ /* Write the error message to the error log */ -+ lua_getfield(L, -1, "write"); - lua_pushstring(L, error_msg); - lua_pushliteral(L, "\n"); -- lua_call(L, 2, 0); -- IGNORE_UNUSED_RESULT( -- luaL_dostring(L, "mg.write(debug.traceback(), '\\n')")); -+ lua_call(L, 2, 0); /* call mg.write(error_msg + \n) */ - lua_pop(L, 1); /* pop mg */ + Support + ------- +@@ -171,7 +164,7 @@ Authors + + CivetWeb was forked from the last MIT version of Mongoose in August 2013. + Since then, CivetWeb has seen many improvements from various authors +-(Copyright (c) 2013-2021 the CivetWeb developers, MIT license). ++(Copyright (c) 2013-2025 the CivetWeb developers, MIT license). + + A list of authors can be found in [CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md). + +@@ -179,7 +172,7 @@ CivetWeb is based on the [Mongoose project](https://github.com/cesanta/mongoose) + Sergey Lyubka(2004-2013) who released it under the MIT license. + However, on August 16, 2013, + [Mongoose was relicensed to a dual GPL V2 + commercial license](https://groups.google.com/forum/#!topic/mongoose-users/aafbOnHonkI) +-and CiwetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose". ++and CivetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose". + The license change and CivetWeb fork was mentioned on the Mongoose + [Wikipedia](https://en.wikipedia.org/wiki/Mongoose_(web_server)) + page as well, but it's getting deleted (and added again) there every +diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md +index 354061ee..0bdd1c5a 100644 +--- a/RELEASE_NOTES.md ++++ b/RELEASE_NOTES.md +@@ -1,3 +1,13 @@ ++Release Notes v1.17 (work in progress) ++=== ++### Objectives: *to be defined* + -+ /* Get Lua traceback */ -+ lua_getglobal(L, "debug"); -+ lua_getfield(L, -1, "traceback"); -+ lua_call(L, 0, 1); /* call debug.traceback() */ -+ lua_remove(L, -2); /* remove debug */ ++Changes ++------- + -+ /* Write the Lua traceback to the error log */ -+ lua_getglobal(L, "mg"); -+ lua_getfield(L, -1, "write"); -+ lua_pushvalue(L, -3); /* push the traceback */ ++- Update version number + -+ /* Only print the traceback if it is not empty */ -+ if (strcmp(lua_tostring(L, -1), "stack traceback:") != 0) { -+ lua_pushliteral(L, "\n"); /* append a newline */ -+ lua_call(L, 2, 0); /* call mg.write(traceback + \n) */ -+ lua_pop(L, 2); /* pop mg and traceback */ -+ } else { -+ lua_pop(L, 3); /* pop mg, traceback and error message */ -+ } + - } else { - printf("Lua error: [%s]\n", error_msg); - IGNORE_UNUSED_RESULT( --- -2.34.1 - - -From aa7de369a47903de2417e2a8bf5be1f3df2acd10 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Mon, 23 Sep 2024 21:27:36 +0200 -Subject: [PATCH 107/173] Add linux and macOS test using OpenSSL3.0 - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 85 +++++++++++++++++++++++++++++------ - unittest/public_server.c | 2 +- - 2 files changed, 72 insertions(+), 15 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index 9e905e87..815a9d84 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -16,7 +16,6 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- idx: 1 - N: Clang-Linux-Minimal-Debug - BUILD_TYPE: Debug - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -42,7 +41,6 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- idx: 3 - N: Clang-Linux-Default-Release - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -68,7 +66,6 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- idx: 5 - N: GCC-Linux-Complete-NoLua-Release - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -94,7 +91,6 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- idx: 6 - N: CLANG-AnyVersion-Linux-Coverage - BUILD_TYPE: Coverage - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -120,7 +116,6 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- idx: 9 - N: Clang-Linux-Default-Shared - BUILD_TYPE: Debug - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -146,7 +141,6 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- idx: 15 - N: GCCLinuxDefault_RelWithDebInfo - BUILD_TYPE: RelWithDebInfo - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -171,7 +165,6 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- idx: 16 - N: GCCLinuxDefault_MinSizeRel - BUILD_TYPE: MinSizeRel - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -196,7 +189,6 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- idx: 17 - N: GCCLinuxDefault_None - BUILD_TYPE: None - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -221,7 +213,6 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- idx: 20 - N: GCCLinuxDefault_xenial - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -246,7 +237,6 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- idx: 23 - N: GCCLinuxDefault_focal - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -267,13 +257,37 @@ jobs: - NO_CACHING: NO - ALLOW_WARNINGS: YES - RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ N: GCCLinuxDefault_OpenSSL_3_0 -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: NO -+ OpenSSL_3_0: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 - - # Disable Lua build, until someone knows how to fix the CMake files - # see https://github.com/civetweb/civetweb/issues/543 - # - os: ubuntu-lastest - # compiler: clang - # env: -- # idx: 99 - # N: Clang-Linux-Complete-WithLua-Debug - # BUILD_TYPE: Debug - # ENABLE_SSL_DYNAMIC_LOADING: YES -@@ -299,7 +313,6 @@ jobs: - - os: macos-latest - compiler: clang - env: -- idx: 8 - N: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: NO -@@ -328,8 +341,7 @@ jobs: - - os: macos-13 - compiler: clang - env: -- idx: 11 -- N: OSX-Package -+ N: OSX-Package_OpenSSL_1_0 - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: YES -@@ -352,6 +364,33 @@ jobs: - MACOSX_PACKAGE: 1 - RUN_UNITTEST: 1 + Release Notes v1.16 + === + ### Objectives: *bug fixes, documentation, WebDAV* +@@ -21,6 +31,9 @@ Changes + - Remove Conan support + - Update version number -+ - os: macos-latest -+ compiler: clang -+ env: -+ N: OSX-Package_OpenSSL_3_0 -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: NO -+ OPENSSL_3_0: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: YES -+ ENABLE_WEBSOCKETS: YES -+ ENABLE_SERVER_STATS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ MACOSX_PACKAGE: 1 -+ RUN_UNITTEST: 1 ++Note: A precompiled 32-bit executables for Windows is no longer provided, but only a 64 bit version. ++The source code itself still supports 32-bit platforms. + + Known Issues + ----- + +diff --git a/SECURITY.md b/SECURITY.md +index b1781aff..78c85541 100644 +--- a/SECURITY.md ++++ b/SECURITY.md +@@ -14,16 +14,12 @@ Selected, critical defects are fixed in the latest release as well. + Note that different security policies apply to different files/folders in this project: + - The core components are include/civetweb.h, src/civetweb.c and src/*.inl. These files are part of every server instance in production. Therefore they have to undergo the most intensive security tests and reviews. + - The src/main.c file is used by the standalone server. It is used in various tests in combination with the aforementioned core components. Applications embedding civetweb will not use main.c and, thus, do not suffer from any vulnerabilities therein. +-- The example folders contain different usage examples in different maintenance state. This is explained in detail in the README file there. ++- The example folders contain different usage examples in different maintenance state. Some of them skipped error handling to keep the code shorter and easier to understand. This is explained in more detail in the README file there. + - The content of all test folders (test, unittest, fuzztest) is used to test the server functionality. These tests are not designed with security in mind - on the contrary, some tests contain scripts or settings that even introduce security leaks on purpose. All tests are only meant to be run in a test environment. You should not use the content of any test folder in production. Also certificates in "resources/cert" are only meant to be used in test environments and must never be used in production. + + + ## Reporting a Vulnerability + +-Please send vulnerability reports by email to bel2125 at gmail com. +-Vulnerability with low severity can be sent directly by email. +- +-For high severity vulnerabilities, you can get an individual gpg key to encrypt your detailed description of vulnerabilities you want to report. +- +-If you do not get any response within one week, your email might have been lost (e.g., deleted as false positive by a spam filter). In this case, please open a GitHub issue. ++Ideally open a github issue for suspected vulnerabilities, even if you do not post all details there. This will help against emails getting lost. ++Detailed vulnerability reports including exploit code should be sent by email to bel2125 at gmail com. - steps: - - name: Checkout code -@@ -404,7 +443,25 @@ jobs: - echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH - echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV - echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV +diff --git a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj +index 7eb6fd9f..58e7fc7c 100644 +--- a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj ++++ b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj +@@ -110,7 +110,8 @@ + true + + +- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.14 --set-product-version 1.14 ++ ++ + + + +@@ -159,7 +160,7 @@ + true + + +- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 ++ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 + + + +@@ -180,7 +181,7 @@ + true + + +- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 ++ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 + + + +diff --git a/appveyor.yml b/appveyor.yml +index 41cde75f..b40e514f 100644 +--- a/appveyor.yml ++++ b/appveyor.yml +@@ -159,59 +159,6 @@ environment: + enable_stats: YES + configuration: Release + platform: x64 +- # Visual Studio 2010 +- - id: Full-VS2010-x86 +- compiler: msvc-16-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x86 +- # Visual Studio 2012 +- - id: Full-VS2012-x86 +- compiler: msvc-17-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x86 +- # Visual Studio 2013 +- - id: Full-VS2013-x86 +- compiler: msvc-18-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x86 +- image: Visual Studio 2013 +- - id: Full-VS2013-x64 +- compiler: msvc-18-seh +- build_shared: NO +- no_files: NO +- enable_ipv6: YES +- enable_ssl: YES +- enable_websockets: YES +- no_cgi: NO +- no_caching: NO +- enable_stats: YES +- configuration: Release +- platform: x64 +- image: Visual Studio 2013 + # Visual Studio 2015 + - id: Full-VS2015-x86 + compiler: msvc-19-seh +@@ -272,7 +219,7 @@ environment: + build_shared: NO + no_files: NO + enable_ipv6: YES +- enable_ssl: YES ++ enable_ssl: NO + enable_websockets: YES + no_cgi: NO + no_caching: NO +@@ -280,7 +227,21 @@ environment: + configuration: Release + platform: x64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 +- # Ubuntu ++ # Visual Studio 2022 ++ - id: Full-VS2022-x64 ++ compiler: msvc-22-seh ++ build_shared: NO ++ no_files: NO ++ enable_ipv6: YES ++ enable_ssl: NO ++ enable_websockets: YES ++ no_cgi: NO ++ no_caching: NO ++ enable_stats: YES ++ configuration: Release ++ platform: x64 ++ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 ++ # Ubuntu + - id: Ubuntu1604-GCC-x64 + compiler: gcc-5.1.0-posix + build_shared: NO +@@ -380,6 +341,8 @@ before_build: + - if "%compiler_version%"=="19" (set "vs_version=14" & set "vs_year=2015") + - if "%compiler_version%"=="20" (set "vs_version=15" & set "vs_year=2017") + - if "%compiler_version%"=="21" (set "vs_version=16" & set "vs_year=2019") ++ - if "%compiler_version%"=="22" (set "vs_version=17" & set "vs_year=2022") ++ - if "%compiler_version%"=="23" (set "vs_version=18" & set "vs_year=2026") + - if "%compiler_name%"=="msvc" (set "generator=Visual Studio %vs_version% %vs_year%") + - set "arch_arg= " + - if "%compiler_name%"=="msvc" ( +@@ -440,6 +403,7 @@ before_build: + -DCIVETWEB_CXX_STANDARD=%cxx_standard% + -DCIVETWEB_SSL_OPENSSL_API_1_0=NO + -DCIVETWEB_SSL_OPENSSL_API_1_1=YES ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO + "%source_path%" + - cmake + -G "%generator%" %arch_arg% +@@ -461,6 +425,7 @@ before_build: + -DCIVETWEB_CXX_STANDARD=%cxx_standard% + -DCIVETWEB_SSL_OPENSSL_API_1_0=NO + -DCIVETWEB_SSL_OPENSSL_API_1_1=YES ++ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO + "%source_path%" + - powershell Push-AppveyorArtifact CMakeCache.txt + - cd "%source_path%" +diff --git a/docs/Building.md b/docs/Building.md +index ed30bd77..b6185333 100644 +--- a/docs/Building.md ++++ b/docs/Building.md +@@ -179,8 +179,9 @@ make build COPT="-DNDEBUG -DNO_CGI" + | `SSL_ALREADY_INITIALIZED` | do not initialize libcrypto | + | `OPENSSL_API_1_0` | Use OpenSSL V1.0.x interface | + | `OPENSSL_API_1_1` | Use OpenSSL V1.1.x interface | +-| `OPENSSL_API_3_0` | Use OpenSSL V3.x interface | +-| `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_*) | ++| `OPENSSL_API_3_0` | Use OpenSSL V3.0.x interface | ++| `USE_GNUTLS` | Use GnuTLS (cannot be combined with OPENSSL_API_* or USE_MBEDTLS) | ++| `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_* or USE_GNUTLS) | + | | | + | `BUILD_DATE` | define as a string to be used as build id instead of __DATE__ | + | | | +diff --git a/docs/Docker.md b/docs/Docker.md +new file mode 100644 +index 00000000..ac0fadc9 +--- /dev/null ++++ b/docs/Docker.md +@@ -0,0 +1,96 @@ ++# Running CivetWeb in Docker + -+ - name: Install OpenSSL 3.0 on modern MacOS -+ # OpenSSL 1.1 is installed by default, so we need to install 3.0 manually -+ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_3_0 == 'YES' -+ run: | -+ brew install openssl@3.0 - -+ OPENSSL_ROOT_DIR=$(brew --prefix openssl@3.0) -+ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -+ CFLAGS=-I${OPENSSL_ROOT_DIR}/include -+ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -+ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig ++## Overview ++ ++This guide explains how to build and run CivetWeb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. ++ ++## Prerequisites ++ ++- Docker installed on your machine ++- Basic understanding of Docker commands ++ ++## Dockerfile Explained ++ ++### Build Stage ++ ++The build stage uses Alpine Linux 3.18 and installs the necessary build tools and libraries. ++ ++```Dockerfile ++FROM alpine:3.18 AS build ++RUN apk update && \ ++ apk add --no-cache \ ++ build-base zlib-dev ++``` ++ ++The source code and other necessary files are copied into the `/civetweb` directory in the container. ++ ++```Dockerfile ++WORKDIR /civetweb ++COPY src ./src/ ++COPY include ./include/ ++COPY Makefile ./ ++COPY resources ./resources/ ++COPY *.md ./ ++``` ++ ++The Civetweb server is then built and installed into the `/app` directory. ++ ++```Dockerfile ++RUN make build && \ ++ make WITH_ALL=1 && \ ++ make install PREFIX=/app ++``` ++ ++### Image Stage ++ ++The image stage also uses Alpine Linux 3.18 but installs only the runtime dependencies. ++ ++```Dockerfile ++FROM alpine:3.18 ++RUN apk update && \ ++ apk add --no-cache \ ++ libstdc++ zlib ++``` ++ ++A non-root user `civetweb` is created for security reasons. ++ ++```Dockerfile ++RUN addgroup -S civetweb && adduser -S civetweb -G civetweb ++USER civetweb ++``` ++ ++The built application from the build stage is copied into this stage. ++ ++```Dockerfile ++COPY --chown=civetweb:civetweb --from=build /app/ /app/ ++``` ++ ++The container will listen on port 8080 at runtime. ++ ++```Dockerfile ++EXPOSE 8080 ++``` ++ ++Finally, the entry point for the container is set. ++ ++```Dockerfile ++ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] ++``` ++ ++## Build and Run ++ ++To build the Docker image, run: ++ ++```bash ++docker build -t civetweb:latest . ++``` ++ ++To run the container, execute: ++ ++```bash ++docker run -p 8080:8080 civetweb:latest ++``` ++ ++## Conclusion ++ ++This Dockerfile provides a secure and efficient way to build and run CivetWeb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. +diff --git a/docs/Embedding.md b/docs/Embedding.md +index 0a0fa336..0291ff92 100644 +--- a/docs/Embedding.md ++++ b/docs/Embedding.md +@@ -213,10 +213,15 @@ about web server instance: + + When `mg_start()` returns, all initialization is guaranteed to be complete + (e.g. listening ports are opened, SSL is initialized, etc). `mg_start()` starts +-some threads: a master thread, that accepts new connections, and several +-worker threads, that process accepted connections. The number of worker threads +-is configurable via `num_threads` configuration option. That number puts a ++some threads: a master thread, that accepts new connections, and optionally some ++worker threads, that process accepted connections. The maximum number of worker ++threads is configurable via `num_threads` configuration option. That number puts a + limit on number of simultaneous requests that can be handled by CivetWeb. + -+ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV -+ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV -+ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH -+ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV -+ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV ++The number of worker threads to be pre-spawned at startup is specified via the ++'prespawn_threads' configuration option; worker threads that are not pre-spawned ++will instead be demand-created the first time they are needed. + - - name: Install OpenSSL 1.0 on modern Linux - # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default - if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_0 == 'YES' -diff --git a/unittest/public_server.c b/unittest/public_server.c -index 93641f0d..4983ee8c 100644 ---- a/unittest/public_server.c -+++ b/unittest/public_server.c -@@ -823,7 +823,7 @@ START_TEST(test_mg_server_and_client_tls) - * while Ubuntu Xenial, Ubuntu Trusty and Windows test containers at - * Travis CI do not. Maybe it is OpenSSL version specific. - */ --#if defined(OPENSSL_API_1_1) -+#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) - if (client_conn) { - /* Connect succeeds, but the connection is unusable. */ - mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n"); --- -2.34.1 - - -From 69265e7a0d0655a24f9b3e8e301d1c6e72cd51c4 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Mon, 23 Sep 2024 22:16:01 +0200 -Subject: [PATCH 108/173] Remove test for OpenSSL 1.0 - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 100 +++++++++------------------------- - 1 file changed, 25 insertions(+), 75 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index 815a9d84..60f5089f 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -44,8 +44,8 @@ jobs: - N: Clang-Linux-Default-Release - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -69,8 +69,8 @@ jobs: - N: GCC-Linux-Complete-NoLua-Release - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -94,8 +94,8 @@ jobs: - N: CLANG-AnyVersion-Linux-Coverage - BUILD_TYPE: Coverage - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -144,8 +144,8 @@ jobs: - N: GCCLinuxDefault_RelWithDebInfo - BUILD_TYPE: RelWithDebInfo - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -168,8 +168,8 @@ jobs: - N: GCCLinuxDefault_MinSizeRel - BUILD_TYPE: MinSizeRel - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -192,8 +192,8 @@ jobs: - N: GCCLinuxDefault_None - BUILD_TYPE: None - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -216,8 +216,8 @@ jobs: - N: GCCLinuxDefault_xenial - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -237,7 +237,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCCLinuxDefault_focal -+ N: GCCLinuxDefault - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -335,17 +335,14 @@ jobs: - ALLOW_WARNINGS: YES - RUN_UNITTEST: 1 - -- # mac-os 13 is the last version of MacOS runner using x86_64 architecture -- # mac-os 14 and later are using arm64 architecture -- # but OpenSSL 1.0 can't compile on arm64, so we set it fixed to mac-os 13 -- - os: macos-13 -+ - os: macos-latest - compiler: clang - env: -- N: OSX-Package_OpenSSL_1_0 -+ N: OSX-Package_OpenSSL_1_1 - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES -- OPENSSL_1_0: YES -- OPENSSL_1_1: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES - ENABLE_CXX: NO - ENABLE_LUA_SHARED: NO - C_STANDARD: auto -@@ -403,31 +400,6 @@ jobs: - sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 - sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 - -- - name: Install OpenSSL 1.0 on modern MacOS -- # Needed for recent versions of MacOS as they ship with OpenSSL 1.1 by default -- if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_0 == 'YES' -- run: | -- curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz -- tar -xzf openssl-1.0.2u.tar.gz -- cd openssl-1.0.2u -- ./Configure --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared shared darwin64-x86_64-cc -- make depend -- make -j $(nproc) -- sudo make install_sw -j $(nproc) -- -- OPENSSL_ROOT_DIR=/usr/local/ssl1.0 -- LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -- CFLAGS=-I${OPENSSL_ROOT_DIR}/include -- ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -- PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -- -- echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV -- echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV -- echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH -- echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV -- echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -- echo "DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV -- - - name: Set up OpenSSL 1.1 on modern MacOS - # OpenSSL 1.1 is installed by default, so we just need to set the paths - if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_1 == 'YES' -@@ -437,57 +409,35 @@ jobs: - CFLAGS=-I${OPENSSL_ROOT_DIR}/include - ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" - PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -+ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib - - echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV - echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV - echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH - echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV - echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV - - - name: Install OpenSSL 3.0 on modern MacOS - # OpenSSL 1.1 is installed by default, so we need to install 3.0 manually - if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_3_0 == 'YES' - run: | - brew install openssl@3.0 -- -+ - OPENSSL_ROOT_DIR=$(brew --prefix openssl@3.0) - LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib - CFLAGS=-I${OPENSSL_ROOT_DIR}/include - ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" - PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -+ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib + If you embed CivetWeb into a program that uses SSL outside CivetWeb as well, + you may need to initialize SSL before calling `mg_start()`, and set the pre- + processor define `SSL_ALREADY_INITIALIZED`. This is not required if SSL is +diff --git a/docs/OpenSSL.md b/docs/OpenSSL.md +index 9822e9a4..657a6c35 100644 +--- a/docs/OpenSSL.md ++++ b/docs/OpenSSL.md +@@ -1,5 +1,4 @@ +-Adding OpenSSL Support +-===== ++# Adding OpenSSL Support - echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV - echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV - echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH - echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV - echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -- -- - name: Install OpenSSL 1.0 on modern Linux -- # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default -- if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_0 == 'YES' -- run: | -- curl -O -L https://openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz -- tar -xzf openssl-1.0.2u.tar.gz -- cd openssl-1.0.2u -- ./config --prefix=/usr/local/ssl1.0 --openssldir=/usr/local/ssl1.0 shared -- make depend -- make -j $(nproc) -- sudo make install_sw -j $(nproc) -- sudo ldconfig -- -- OPENSSL_ROOT_DIR=/usr/local/ssl1.0 -- LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -- CFLAGS=-I${OPENSSL_ROOT_DIR}/include -- ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -- PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -- -- echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV -- echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV -- echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH -- echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV -- echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -- echo "LD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV -- -+ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV -+ - - name: Install OpenSSL 1.1 on modern Linux - # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default - if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_1 == 'YES' --- -2.34.1 - - -From 0a4a47143c835a4db7cae1b39e9994b2e38173c9 Mon Sep 17 00:00:00 2001 -From: yubiuser -Date: Mon, 23 Sep 2024 22:49:28 +0200 -Subject: [PATCH 109/173] Set name of job in github UI - -Signed-off-by: yubiuser ---- - .github/workflows/cibuild.yml | 33 +++++++++++++++++---------------- - 1 file changed, 17 insertions(+), 16 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index 60f5089f..5150cd13 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -7,8 +7,9 @@ on: - types: [published] - workflow_dispatch: - jobs: -- build: -+ build-and-test: - runs-on: ${{ matrix.os }} -+ name: ${{ matrix.env.NAME }} - strategy: - fail-fast: true - matrix: -@@ -16,7 +17,7 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- N: Clang-Linux-Minimal-Debug -+ NAME: Clang-Linux-Minimal-Debug - BUILD_TYPE: Debug - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -41,7 +42,7 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- N: Clang-Linux-Default-Release -+ NAME: Clang-Linux-Default-Release - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -66,7 +67,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCC-Linux-Complete-NoLua-Release -+ NAME: GCC-Linux-Complete-NoLua-Release - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -91,7 +92,7 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- N: CLANG-AnyVersion-Linux-Coverage -+ NAME: CLANG-AnyVersion-Linux-Coverage - BUILD_TYPE: Coverage - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -116,7 +117,7 @@ jobs: - - os: ubuntu-latest - compiler: clang - env: -- N: Clang-Linux-Default-Shared -+ NAME: Clang-Linux-Default-Shared - BUILD_TYPE: Debug - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -141,7 +142,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCCLinuxDefault_RelWithDebInfo -+ NAME: GCCLinuxDefault_RelWithDebInfo - BUILD_TYPE: RelWithDebInfo - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -165,7 +166,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCCLinuxDefault_MinSizeRel -+ NAME: GCCLinuxDefault_MinSizeRel - BUILD_TYPE: MinSizeRel - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -189,7 +190,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCCLinuxDefault_None -+ NAME: GCCLinuxDefault_None - BUILD_TYPE: None - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -213,7 +214,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCCLinuxDefault_xenial -+ NAME: GCCLinuxDefault_xenial - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -237,7 +238,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCCLinuxDefault -+ NAME: GCCLinuxDefault - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -261,7 +262,7 @@ jobs: - - os: ubuntu-latest - compiler: gcc - env: -- N: GCCLinuxDefault_OpenSSL_3_0 -+ NAME: GCCLinuxDefault_OpenSSL_3_0 - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -288,7 +289,7 @@ jobs: - # - os: ubuntu-lastest - # compiler: clang - # env: -- # N: Clang-Linux-Complete-WithLua-Debug -+ # NAME: Clang-Linux-Complete-WithLua-Debug - # BUILD_TYPE: Debug - # ENABLE_SSL_DYNAMIC_LOADING: YES - # OPENSSL_1_0: NO -@@ -313,7 +314,7 @@ jobs: - - os: macos-latest - compiler: clang - env: -- N: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad -+ NAME: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: NO - OPENSSL_1_0: NO -@@ -338,7 +339,7 @@ jobs: - - os: macos-latest - compiler: clang - env: -- N: OSX-Package_OpenSSL_1_1 -+ NAME: OSX-Package_OpenSSL_1_1 - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO -@@ -364,7 +365,7 @@ jobs: - - os: macos-latest - compiler: clang - env: -- N: OSX-Package_OpenSSL_3_0 -+ NAME: OSX-Package_OpenSSL_3_0 - BUILD_TYPE: Release - ENABLE_SSL_DYNAMIC_LOADING: YES - OPENSSL_1_0: NO --- -2.34.1 - - -From 945118e5ee71ee84df12dd7b31d3ae4909e082b9 Mon Sep 17 00:00:00 2001 -From: Chris Johns -Date: Fri, 27 Sep 2024 14:12:54 +1000 -Subject: [PATCH 110/173] rtems: Add support for RTEMS - ---- - Makefile | 9 +++++++-- - src/civetweb.c | 22 ++++++++++++++++++++-- - 2 files changed, 27 insertions(+), 4 deletions(-) - -diff --git a/Makefile b/Makefile -index 076d8739..3e396581 100644 ---- a/Makefile -+++ b/Makefile -@@ -71,7 +71,11 @@ ifdef WITH_CFLAGS - CFLAGS += $(WITH_CFLAGS) - endif + Civetweb supports *HTTPS* connections using the OpenSSL transport layer + security (TLS) library. OpenSSL is a free, open source library (see +@@ -46,7 +45,7 @@ One can use the following steps in Windows (in Linux replace "copy" by "cp" + and "type" by "cat"): --LIBS = -lpthread -lm $(LOPT) -+LIBS = -+ifneq ($(TARGET_OS), RTEMS) -+LIBS += -lpthread -+endif -+LIBS += -lm $(LOPT) +
        +-  openssl genrsa -des3 -out server.key 1024
        ++  openssl genrsa -des3 -out server.key 2048
          
        - ifdef WITH_DEBUG
        -   CFLAGS += -g -DDEBUG
        -@@ -178,7 +182,9 @@ ifdef WITH_COMPRESSION
        - endif
        +   openssl req -new -key server.key -out server.csr
          
        - ifdef WITH_ZLIB
        -+ifneq ($(TARGET_OS), RTEMS)
        -   LIBS += -lz
        -+endif
        -   CFLAGS += -DUSE_ZLIB
        - endif
        +diff --git a/docs/README.md b/docs/README.md
        +index 54ed7128..d3fb0e5d 100644
        +--- a/docs/README.md
        ++++ b/docs/README.md
        +@@ -31,9 +31,6 @@ Source releases can be found on GitHub
          
        -@@ -446,4 +452,3 @@ indent:
        - 	astyle --suffix=none --style=linux --indent=spaces=4 --lineend=linux  include/*.h src/*.c src/*.cpp src/*.inl examples/*/*.c  examples/*/*.cpp
        + A security policy can be found in [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md).
          
        - .PHONY: all help build install clean lib so
        +-CivetWeb is free of charge, however, donations for maintenance are welcome:
        +-[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=88ZLXZ6U77GJU)
         -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index dced1362..7fdf226d 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -183,6 +183,10 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
        - #error "Symbian is no longer maintained. CivetWeb no longer supports Symbian."
        - #endif /* __SYMBIAN32__ */
        - 
        -+#if defined(__rtems__)
        -+#include 
        -+#endif
        -+
        - #if defined(__ZEPHYR__)
        - #include 
        - #include 
        -@@ -885,7 +889,9 @@ typedef unsigned short int in_port_t;
        - #include 
        - #include 
        - #include 
        -+#if !defined(__rtems__)
        - #include 
        -+#endif
        - #include 
        - #include 
        - #include 
        -@@ -963,7 +969,7 @@ count_leap(int y)
        - 	return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
        - }
          
        --time_t
        -+static time_t
        - timegm(struct tm *tm)
        - {
        - 	static const unsigned short ydays[] = {
        -@@ -20786,6 +20792,8 @@ get_system_name(char **sysName)
        - 
        - 	*sysName = mg_strdup(name);
        + Documentation
        + ---------------
        +@@ -43,6 +40,7 @@ Documentation
        + - [Building.md](Building.md) - Building the Server (quick start guide)
        + - [Embedding.md](Embedding.md) - Embedding (how to add HTTP support to an existing application)
        + - [OpenSSL.md](OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL.
        ++- [Docker.md](Docker.md) - Use CivetWeb in a Docker container.
        + - [APIReference.md](APIReference.md) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)).
          
        -+#elif defined(__rtems__)
        -+	*sysName = mg_strdup("RTEMS");
        - #elif defined(__ZEPHYR__)
        - 	*sysName = mg_strdup("Zephyr OS");
        - #else
        -@@ -22079,13 +22087,23 @@ mg_get_system_info(char *buffer, int buflen)
        - 		            (unsigned)si.dwNumberOfProcessors,
        - 		            (unsigned)si.dwActiveProcessorMask);
        - 		system_info_length += mg_str_append(&buffer, end, block);
        --#elif defined(__ZEPHYR__)
        -+#elif defined(__rtems__)
        - 		mg_snprintf(NULL,
        - 		            NULL,
        - 		            block,
        - 		            sizeof(block),
        - 		            ",%s\"os\" : \"%s %s\"",
        - 		            eol,
        -+		           "RTEMS",
        -+		            rtems_version());
        -+		system_info_length += mg_str_append(&buffer, end, block);
        -+#elif defined(__ZEPHYR__)
        -+		mg_snprintf(NULL,
        -+		            NULL,
        -+		            block,
        -+		            sizeof(block),
        -+		            ",%s\"os\" : \"%s\"",
        -+		            eol,
        - 		            "Zephyr OS",
        - 		            ZEPHYR_VERSION);
        - 		system_info_length += mg_str_append(&buffer, end, block);
        --- 
        -2.34.1
        -
        -
        -From b056de7d8c626200dbf732d55c11cb2f975ce5e3 Mon Sep 17 00:00:00 2001
        -From: Egor Konovalov <73017521+egorkonovalov@users.noreply.github.com>
        -Date: Mon, 7 Oct 2024 17:37:51 +0200
        -Subject: [PATCH 111/173] Fix typo
        -
        ----
        - docs/UserManual.md | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        + [Authors](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)
         diff --git a/docs/UserManual.md b/docs/UserManual.md
        -index 13011fbb..f0a784b7 100644
        +index df9b3685..7dc48c38 100644
         --- a/docs/UserManual.md
         +++ b/docs/UserManual.md
        -@@ -514,7 +514,7 @@ The script can define callbacks to be notified when the server starts
        - or stops. Furthermore, it can be used for log filtering or formatting. 
        - The Lua state remains open until the server is stopped.
        - 
        --For a detailed descriotion of available Lua callbacks see section
        -+For a detailed description of available Lua callbacks see section
        - "Lua background script" below.
        +@@ -180,7 +180,7 @@ This option can be specified multiple times. All specified header lines will be
        + ### allow\_index\_script\_resource `no`
        + Index scripts (like `index.cgi` or `index.lua`) may have script handled resources.
          
        - ### lua\_background\_script\_params
        --- 
        -2.34.1
        -
        -
        -From b88f02b776fadd06db3a48a8923b2a32280a9107 Mon Sep 17 00:00:00 2001
        -From: yubiuser 
        -Date: Mon, 23 Sep 2024 15:33:41 +0200
        -Subject: [PATCH 112/173] Replace nproc on MacOS
        -
        -Signed-off-by: yubiuser 
        ----
        - .github/workflows/cibuild.yml | 21 +++++++++++++++------
        - 1 file changed, 15 insertions(+), 6 deletions(-)
        -
        -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml
        -index 5150cd13..d8e1cc6c 100644
        ---- a/.github/workflows/cibuild.yml
        -+++ b/.github/workflows/cibuild.yml
        -@@ -393,7 +393,16 @@ jobs:
        -     steps:
        -         - name: Checkout code
        -           uses: actions/checkout@v4.1.7
        --        
        -+
        -+        - name: Export number of CPUs
        -+          run: |
        -+            if [ "$RUNNER_OS" == "Linux" ]; then
        -+              echo "cores=$(nproc)" >> $GITHUB_ENV
        -+            fi
        -+            if [ "$RUNNER_OS" == "macOS" ]; then
        -+              echo "cores=$(sysctl -n hw.logicalcpu)" >> $GITHUB_ENV
        -+            fi
        -+            
        -         - name: Install clang on Linux
        -           if: matrix.compiler == 'clang' && startsWith(matrix.os,'ubuntu')
        -           run: |
        -@@ -448,8 +457,8 @@ jobs:
        -             cd openssl-1.1.1w
        -             ./config --prefix=/usr/local/ssl1.1 --openssldir=/usr/local/ssl1.1 shared
        -             make depend
        --            make -j $(nproc)
        --            sudo make install_sw -j $(nproc)
        -+            make -j ${{ env.cores }}
        -+            sudo make install_sw -j ${{ env.cores }}
        -             sudo ldconfig
        - 
        -             OPENSSL_ROOT_DIR=/usr/local/ssl1.1
        -@@ -502,11 +511,11 @@ jobs:
        -         - name: Build MacOS Package
        -           if: matrix.env.MACOSX_PACKAGE == 1
        -           run: |
        --            make -f Makefile.osx package -j $(nproc)
        -+            make -f Makefile.osx package -j ${{ env.cores }}
        -         
        -         - name: Build executable
        -           run: |
        --            cmake --build output -- -j $(nproc)
        -+            cmake --build output -- -j ${{ env.cores }}
        -           
        -         - name: Check executable
        -           run: |
        -@@ -527,4 +536,4 @@ jobs:
        -             # Run unit tests
        -             gcc unittest/cgi_test.c -o output/cgi_test.cgi
        -             cd output
        --            CTEST_OUTPUT_ON_FAILURE=1  CK_FORK=yes  make all test -j $(nproc)
        -\ No newline at end of file
        -+            CTEST_OUTPUT_ON_FAILURE=1  CK_FORK=yes  make all test -j ${{ env.cores }}
        -\ No newline at end of file
        --- 
        -2.34.1
        -
        -
        -From fcaa2ea2d032d23432fd2e88b99c792b32204203 Mon Sep 17 00:00:00 2001
        -From: Alejandro 
        -Date: Thu, 24 Oct 2024 16:26:18 +0200
        -Subject: [PATCH 113/173] #1147 Load version-specific dylib
        -
        ----
        - src/civetweb.c | 18 ++++++++++++++++--
        - 1 file changed, 16 insertions(+), 2 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index b3295cdd..c52c8e7a 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -907,8 +907,22 @@ typedef unsigned short int in_port_t;
        - #endif
        +-It this feature is activated, that /some/path/file.ext might be handled by:
        ++If this feature is activated, then /some/path/file.ext might be handled by:
        +   1. /some/path/file.ext (with PATH\_INFO='/', if ext = cgi)
        +   2. /some/path/index.lua with mg.request\_info.path\_info='/file.ext'
        +   3. /some/path/index.cgi with PATH\_INFO='/file.ext'
        +@@ -296,6 +296,15 @@ The current directory is commonly referenced as dot (`.`).
        + It is recommended to use an absolute path for document\_root, in order to
        + avoid accidentally serving the wrong directory.
          
        - #if defined(__MACH__) && defined(__APPLE__)
        --#define SSL_LIB "libssl.dylib"
        --#define CRYPTO_LIB "libcrypto.dylib"
        -+
        -+#if defined(OPENSSL_API_3_0)
        -+#define SSL_LIB "libssl.3.dylib"
        -+#define CRYPTO_LIB "libcrypto.3.dylib"
        -+#endif
        -+
        -+#if defined(OPENSSL_API_1_1)
        -+#define SSL_LIB "libssl.1.1.dylib"
        -+#define CRYPTO_LIB "libcrypto.1.1.dylib"
        -+#endif /* OPENSSL_API_1_1 */
        -+
        -+#if defined(OPENSSL_API_1_0)
        -+#define SSL_LIB "libssl.1.0.dylib"
        -+#define CRYPTO_LIB "libcrypto.1.0.dylib"
        -+#endif /* OPENSSL_API_1_0 */
        ++### fallback\_document\_root `.`
        ++An optional second directory to check for a file to serve, if the requested
        ++filename was not found in the document\_root directory.
        ++This can be useful in cases where an app ships with a read-only HTML content
        ++directory as part of its install, but you nevertheless want to allow the user
        ++to customize the served content by placing modified or additional files into
        ++a writable directory, where they will take precedence over their read-only
        ++counterparts, on a per-file basis.
         +
        - #else
        - #if !defined(SSL_LIB)
        - #define SSL_LIB "libssl.so"
        --- 
        -2.34.1
        -
        -
        -From e927db7979e07ca5ceb06a61889a69b733dc029d Mon Sep 17 00:00:00 2001
        -From: Niklas Fiekas 
        -Date: Sun, 17 Nov 2024 16:33:35 +0100
        -Subject: [PATCH 114/173] Bump minimum CMake version to 3.10
        -
        ----
        - CMakeLists.txt                              | 7 ++-----
        - examples/linux_ws_server_cpp/CMakeLists.txt | 2 +-
        - 2 files changed, 3 insertions(+), 6 deletions(-)
        -
        -diff --git a/CMakeLists.txt b/CMakeLists.txt
        -index 4ffd41b9..293dabac 100644
        ---- a/CMakeLists.txt
        -+++ b/CMakeLists.txt
        -@@ -1,8 +1,5 @@
        --# Use at least CMake 3.3
        --cmake_minimum_required (VERSION 3.3.0)
        --cmake_policy(VERSION 3.2.2)
        --cmake_policy(SET CMP0054 NEW)
        --cmake_policy(SET CMP0057 NEW)
        -+cmake_minimum_required(VERSION 3.10)
        -+cmake_policy(VERSION 3.10)
        - 
        - # Set up the project
        - project (civetweb VERSION 1.16.0)
        -diff --git a/examples/linux_ws_server_cpp/CMakeLists.txt b/examples/linux_ws_server_cpp/CMakeLists.txt
        -index 6a71798e..8f765ac5 100644
        ---- a/examples/linux_ws_server_cpp/CMakeLists.txt
        -+++ b/examples/linux_ws_server_cpp/CMakeLists.txt
        -@@ -1,4 +1,4 @@
        --cmake_minimum_required(VERSION 3.5.1)
        -+cmake_minimum_required(VERSION 3.10)
        - project(linux_ws_server)
        - 
        - set(TARGET_NAME ${PROJECT_NAME})
        --- 
        -2.34.1
        -
        -
        -From e0448e48d08e66260f11a5b6d22144cba6f9073d Mon Sep 17 00:00:00 2001
        -From: PythonGermany <97847597+PythonGermany@users.noreply.github.com>
        -Date: Fri, 6 Dec 2024 13:04:41 +0100
        -Subject: [PATCH 115/173] Update listening_ports documentation
        -
        -Add description for optional port configuration
        ----
        - docs/UserManual.md | 4 +++-
        - 1 file changed, 3 insertions(+), 1 deletion(-)
        -
        -diff --git a/docs/UserManual.md b/docs/UserManual.md
        -index f0a784b7..7289cfa0 100644
        ---- a/docs/UserManual.md
        -+++ b/docs/UserManual.md
        -@@ -440,7 +440,9 @@ of 1000.
        + ### enable\_auth\_domain\_check `yes`
        + When using absolute URLs, verify the host is identical to the authentication\_domain.
        + If enabled, requests to absolute URLs will only be processed
        +@@ -431,11 +440,15 @@ of 1000.
          ### listening\_ports `8080`
          Comma-separated list of ports to listen on. If the port is SSL, a
          letter `s` must be appended, for example, `80,443s` will open
         -port 80 and port 443, and connections on port 443 will be SSL-ed.
         +port 80 and port 443, and connections on port 443 will be SSL-ed. If the port
         +should be optional the letter `o` must be appended, for example with `80o,443s`
        -+starting the server will not exit if binding to port 80 is not possible.
        - For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'.
        - Redirect ports will redirect all their traffic to the first configured
        - SSL port. For example, if `listening_ports` is `80r,443s`, then all
        --- 
        -2.34.1
        -
        -
        -From 68ec0a171ae6c2a35e42b5e1290e121043b423a2 Mon Sep 17 00:00:00 2001
        -From: PythonGermany <97847597+PythonGermany@users.noreply.github.com>
        -Date: Fri, 6 Dec 2024 13:16:49 +0100
        -Subject: [PATCH 116/173] Update listening_ports documentation
        -
        ----
        - docs/UserManual.md | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        -diff --git a/docs/UserManual.md b/docs/UserManual.md
        -index 7289cfa0..f036b230 100644
        ---- a/docs/UserManual.md
        -+++ b/docs/UserManual.md
        -@@ -442,7 +442,7 @@ Comma-separated list of ports to listen on. If the port is SSL, a
        - letter `s` must be appended, for example, `80,443s` will open
        - port 80 and port 443, and connections on port 443 will be SSL-ed. If the port
        - should be optional the letter `o` must be appended, for example with `80o,443s`
        --starting the server will not exit if binding to port 80 is not possible.
         +the server will not exit if binding to port 80 is not possible during startup.
          For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'.
          Redirect ports will redirect all their traffic to the first configured
          SSL port. For example, if `listening_ports` is `80r,443s`, then all
        --- 
        -2.34.1
        -
        -
        -From 87a305c5bcd6ea463124069659ea6492d4eeb4ec Mon Sep 17 00:00:00 2001
        -From: PythonGermany <97847597+PythonGermany@users.noreply.github.com>
        -Date: Fri, 6 Dec 2024 13:59:23 +0100
        -Subject: [PATCH 117/173] Update listening_ports documentation
        -
        -Add explanation on how port redirection is configured
        ----
        - docs/UserManual.md | 2 ++
        - 1 file changed, 2 insertions(+)
        -
        -diff --git a/docs/UserManual.md b/docs/UserManual.md
        -index f0a784b7..e36db229 100644
        ---- a/docs/UserManual.md
        -+++ b/docs/UserManual.md
        -@@ -445,6 +445,8 @@ For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'.
        - Redirect ports will redirect all their traffic to the first configured
        - SSL port. For example, if `listening_ports` is `80r,443s`, then all
          HTTP traffic coming at port 80 will be redirected to HTTPS port 443.
         +For ports with redirection configured `authentication_domain` will
         +be used as host component of the redirection url.
          
          It is possible to specify an IP address to bind to. In this case,
          an IP address and a colon must be prepended to the port number.
        --- 
        -2.34.1
        -
        -
        -From cf1e77659d05c6c67a1a0f7ce076311cc56ae266 Mon Sep 17 00:00:00 2001
        -From: catch-error 
        -Date: Fri, 27 Dec 2024 19:56:19 +0100
        -Subject: [PATCH 118/173] Add GnutTLS support.
        -
        -Signed-off-by: catch-error 
        ----
        - CMakeLists.txt     |   7 +-
        - Makefile           |   4 +
        - README.md          |   2 +
        - docs/Building.md   |   5 +-
        - docs/UserManual.md |   3 +-
        - docs/gnutls.md     |  20 ++++
        - format.bat         |   1 +
        - src/CMakeLists.txt |   7 +-
        - src/civetweb.c     | 164 ++++++++++++++++++++++++++++---
        - src/mod_gnutls.inl | 240 +++++++++++++++++++++++++++++++++++++++++++++
        - 10 files changed, 433 insertions(+), 20 deletions(-)
        - create mode 100644 docs/gnutls.md
        - create mode 100644 src/mod_gnutls.inl
        -
        -diff --git a/CMakeLists.txt b/CMakeLists.txt
        -index 293dabac..602fbe41 100644
        ---- a/CMakeLists.txt
        -+++ b/CMakeLists.txt
        -@@ -237,8 +237,11 @@ message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}")
        - option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF)
        - message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}")
        +@@ -505,7 +518,7 @@ The script can define callbacks to be notified when the server starts
        + or stops. Furthermore, it can be used for log filtering or formatting. 
        + The Lua state remains open until the server is stopped.
          
        -+option(CIVETWEB_ENABLE_GNUTLS "Use the GnuTls" OFF)
        -+message(STATUS "SSL support (GnuTLS)  - ${CIVETWEB_ENABLE_GNUTLS}")
        -+
        - option(CIVETWEB_ENABLE_MBEDTLS "Use the MbedTls" OFF)
        --message(STATUS "SSL support - ${CIVETWEB_ENABLE_MBEDTLS}")
        -+message(STATUS "SSL support (MbedTLS) - ${CIVETWEB_ENABLE_MBEDTLS}")
        +-For a detailed descriotion of available Lua callbacks see section
        ++For a detailed description of available Lua callbacks see section
        + "Lua background script" below.
          
        - # Dynamically load or link the SSL libraries
        - cmake_dependent_option(
        -@@ -538,6 +541,8 @@ if (CIVETWEB_ENABLE_MEMORY_DEBUGGING)
        - endif()
        - if (NOT CIVETWEB_ENABLE_SSL)
        -   add_definitions(-DNO_SSL)
        -+elseif (CIVETWEB_ENABLE_GNUTLS)
        -+  add_definitions(-DUSE_GNUTLS)
        - elseif (CIVETWEB_ENABLE_MBEDTLS)
        -   add_definitions(-DUSE_MBEDTLS)
        - elseif (NOT CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        -diff --git a/Makefile b/Makefile
        -index 3e396581..7831ce39 100644
        ---- a/Makefile
        -+++ b/Makefile
        -@@ -97,6 +97,9 @@ endif
        + ### lua\_background\_script\_params
        +@@ -549,8 +562,8 @@ The configuration value is approximate, the real limit might be a few bytes off.
        + The minimum is 1024 (1 kB).
          
        - ifdef NO_SSL
        -   CFLAGS += -DNO_SSL
        -+else ifdef WITH_GNUTLS
        -+  CFLAGS += -DUSE_GNUTLS
        -+  LIBS += -lgnutls -lhogweed -lgmp -lnettle
        - else ifdef WITH_MBEDTLS
        -   CFLAGS += -DUSE_MBEDTLS
        -   LIBS += -lmbedcrypto -lmbedtls -lmbedx509
        -@@ -303,6 +306,7 @@ help:
        - 	@echo "   WITH_CPP=1            build library with c++ classes"
        - 	@echo "   WITH_EXPERIMENTAL=1   build with experimental features"
        - 	@echo "   WITH_DAEMONIZE=1      build with daemonize."
        -+	@echo "   WITH_GNUTLS=1         build with GnuTLS support."
        - 	@echo "   WITH_MBEDTLS=1        build with mbedTLS support."
        - 	@echo "   WITH_OPENSSL_API_1_0=1  build with OpenSSL 1.0.x support."
        - 	@echo "   WITH_OPENSSL_API_1_1=1  build with OpenSSL 1.1.x support."
        -diff --git a/README.md b/README.md
        -index d0624ab5..dae05879 100644
        ---- a/README.md
        -+++ b/README.md
        -@@ -134,6 +134,8 @@ simplicity by a carefully selected list of features:
        + ### num\_threads `50`
        +-Number of worker threads. CivetWeb handles each incoming connection in a
        +-separate thread. Therefore, the value of this option is effectively the number
        ++Maximum number of worker threads allowed. CivetWeb handles each incoming connection
        ++in a separate thread. Therefore, the value of this option is effectively the number
        + of concurrent HTTP connections CivetWeb can handle.
          
        - [Mbed TLS](https://github.com/ARMmbed/mbedtls)
        + If there are more simultaneous requests (connection attempts), they are queued.
        +@@ -563,6 +576,15 @@ In case the clients are web browsers, it is recommended to use `num_threads` of
        + at least 5, since browsers often establish multiple connections to load a single
        + web page, including all linked documents (CSS, JavaScript, images, ...).
          
        -+[GNU TLS](https://gnutls.org)
        ++### prespawn\_threads '0'
        ++Number of worker threads that should be pre-spawned by mg_start().  Defaults to
        ++0, meaning no worker threads will be pre-spawned at startup; rather, worker threads
        ++will be spawned when a new connection comes in and there aren't currently any
        ++idle worker threads available to handle it (if we haven't already reached the
        ++maximum worker-thread count as specified by num_threads).  If this value is
        ++specified less than zero, or greater than the value of num_threads, it will be
        ++treated as if it was specified to be equal to the value of num_threads.
         +
        - 
        - Support
        - -------
        -diff --git a/docs/Building.md b/docs/Building.md
        -index c6f92add..b6185333 100644
        ---- a/docs/Building.md
        -+++ b/docs/Building.md
        -@@ -179,8 +179,9 @@ make build COPT="-DNDEBUG -DNO_CGI"
        - | `SSL_ALREADY_INITIALIZED`    | do not initialize libcrypto                                         |
        - | `OPENSSL_API_1_0`            | Use OpenSSL V1.0.x interface                                        |
        - | `OPENSSL_API_1_1`            | Use OpenSSL V1.1.x interface                                        |
        --| `OPENSSL_API_3_0`            | Use OpenSSL V3.0.x interface                                          |
        --| `USE_MBEDTLS`                | Use MbedTLS (cannot be combined with OPENSSL_API_*)                 |
        -+| `OPENSSL_API_3_0`            | Use OpenSSL V3.0.x interface                                        |
        -+| `USE_GNUTLS`                 | Use GnuTLS (cannot be combined with OPENSSL_API_* or USE_MBEDTLS)   |
        -+| `USE_MBEDTLS`                | Use MbedTLS (cannot be combined with OPENSSL_API_* or USE_GNUTLS)   |
        - |                              |                                                                     |
        - | `BUILD_DATE`                 | define as a string to be used as build id instead of __DATE__       |
        - |                              |                                                                     |
        -diff --git a/docs/UserManual.md b/docs/UserManual.md
        -index ba009fec..7dc48c38 100644
        ---- a/docs/UserManual.md
        -+++ b/docs/UserManual.md
        -@@ -692,7 +692,8 @@ The OpenSSL cipher string uses different cipher names than IANA
        + ### listen\_backlog `200`
        + Maximum number of connections waiting to be accepted by the server operating system.
        + Internally, this parameter is passed to the "listen" socket/system call.
        +@@ -670,7 +692,8 @@ The OpenSSL cipher string uses different cipher names than IANA
          (see [this mapping](https://testssl.sh/openssl-iana.mapping.html)).
          
          In case CivetWeb is built with a TLS library other than OpenSSL 
        @@ -11201,14 +2525,130 @@ index ba009fec..7dc48c38 100644
          the cipher names may be different.
          
          ### ssl\_default\_verify\_paths `yes`
        +@@ -806,6 +829,11 @@ be used for websockets as well. Since websockets use a different URL scheme
        + websockets may also be served from a different directory. By default,
        + the document\_root is used as websocket\_root as well.
        + 
        ++### fallback\_websocket\_root
        ++An optional second directory to check for websocket-files that were
        ++not found in the websocket\_root directory.  (See the documentation for
        ++fallback\_root for details)
        ++
        + ### websocket\_timeout\_ms
        + Timeout for network read and network write operations for websockets, WS(S),
        + in milliseconds. If this value is not set, the value of request\_timeout\_ms
        +@@ -862,8 +890,8 @@ All port, socket, process and thread specific parameters are per server:
        + `enable_http2`, `enable_keep_alive`, `enable_websocket_ping_pong`,
        + `keep_alive_timeout_ms`, `linger_timeout_ms`, `listen_backlog`,
        + `listening_ports`, `lua_background_script`, `lua_background_script_params`,
        +-`max_request_size`, `num_threads`, `request_timeout_ms`, `run_as_user`,
        +-`tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`.
        ++`max_request_size`, `num_threads`, 'prespawn_threads', `request_timeout_ms`,
        ++`run_as_user`, `tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`.
        + 
        + All other options can be set per domain. In particular
        + `authentication_domain`, `document_root` and (for HTTPS) `ssl_certificate`
        +@@ -969,6 +997,7 @@ mg (table):
        +     mg.onerror(msg)             -- error handler, can be overridden
        +     mg.auth_domain              -- a string that holds the HTTP authentication domain
        +     mg.document_root            -- a string that holds the document root directory
        ++    mg.fallback_document_root   -- a string that holds an optional second document root directory
        +     mg.lua_type                 -- a string that holds the lua script type
        +     mg.system                   -- a string that holds the operating system name
        +     mg.version                  -- a string that holds CivetWeb version
        +@@ -1028,6 +1057,7 @@ If websocket and timers support is enabled then the following is also available:
        +     mg.set_timeout(fn,delay,[interval])  -- call function after delay at an interval
        +     mg.set_interval(fn,delay,[interval]) -- call function after delay at an interval
        +     mg.websocket_root                    -- a string that holds the websocket root
        ++    mg.fallback_websocket_root           -- a string that holds an optional second websocket root
        + 
        + connect (function):
        + 
        +diff --git a/docs/api/mg_form_data_handler.md b/docs/api/mg_form_data_handler.md
        +index 368d7786..8f477b0d 100644
        +--- a/docs/api/mg_form_data_handler.md
        ++++ b/docs/api/mg_form_data_handler.md
        +@@ -9,15 +9,15 @@
        + |**`field_found`**|**`int field_found( const char *key, const char *filename, char *path, size_t pathlen, void *user_data )`**;|
        + ||The callback function `field_found()` is called when a new field has been found. The return value of this callback is used to define how the field should be processed. The parameters contain the following information:|
        + ||**`key`** - The name of the field as it was named with the `name` tag in the HTML source.|
        +-||**`filename`** - The name of the file to upload. Please not that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.|
        +-||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.|
        ++||**`filename`** - The name of the file to upload. Please note that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.|
        ++||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `MG_FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.|
        + ||**`pathlen`** - The length of the buffer where the output path can be stored.|
        + ||**`user_data`** - A pointer to the value of the field `user_data` of the structure `struct mg_form_data_handler`.|
        + ||The callback function `field_found()` can return the following values back to Civetweb:|
        +-||**`FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field|
        +-||**`FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data|
        +-||**`FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists|
        +-||**`FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields|
        ++||**`MG_FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field|
        ++||**`MG_FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data|
        ++||**`MG_FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists|
        ++||**`MG_FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields|
        + |**`field_get`**|**`int field_get( const char *key, const char *value, size_t valuelen, void *user_data );`**|
        + ||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_GET`, Civetweb will call `field_get()` one or more times to pass back the data for this field.|
        + ||**`key`** - the name of the field being decoded, note this is only passed on the first call for file parameters (bug?)|
        +@@ -28,7 +28,7 @@
        + ||**`return` `MG_FORM_FIELD_HANDLE_NEXT`** - to skip further calls to get for this field and move on to the next field. |
        + ||**`return` `MG_FORM_FIELD_HANDLE_ABORT`** - to stop parsing this form and abandon the search for further fields. |
        + |**`field_store`**|**`int field_store( const char *path, long long file_size, void *user_data );`**|
        +-||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call|
        ++||If the callback function `field_found()` returned `MG_FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call|
        + ||**`path`** - The path on the server where the file was stored|
        + ||**`file_size`** - The size of the stored file in bytes|
        + ||**`user_data`** - The value of the field `user_data` when the callback functions were registered with a call to `mg_handle_form_request();`|
        +diff --git a/docs/api/mg_get_cookie.md b/docs/api/mg_get_cookie.md
        +index a738bc07..637caeee 100644
        +--- a/docs/api/mg_get_cookie.md
        ++++ b/docs/api/mg_get_cookie.md
        +@@ -6,8 +6,8 @@
        + 
        + | Parameter | Type | Description |
        + | :--- | :--- | :--- |
        +-|**`cookie`**|`const char *`|The cookie name|
        +-|**`var_name`**|`const char *`|The variable name|
        ++|**`cookie`**|`const char *`|The unparsed cookie header|
        ++|**`var_name`**|`const char *`|The cookie name|
        + |**`buf`**|`char *`|The buffer where to store the contents of the cookie|
        + |**`buf_len`**|`size_t`|The length of the cookie buffer, including the terminating NUL|
        + 
        +diff --git a/docs/api/mg_request_info.md b/docs/api/mg_request_info.md
        +index 0f9d720d..b7e0b6e1 100644
        +--- a/docs/api/mg_request_info.md
        ++++ b/docs/api/mg_request_info.md
        +@@ -14,7 +14,7 @@
        + |**`http_version`**|`const char *`| The HTTP version as mentioned in the client request. This can be "1.0", "1.1", etc. |
        + |**`query_string`**|`const char *`| The HTTP query string, defined as URL part after the first '?' character, not including '?'. NULL if there is no '?'. |
        + |**`remote_user`**|`const char *`| The name of the authenticated remote user, or NULL if no authentication was used. Only used for HTTP (digest) authentication, not for cookie based authentication. |
        +-|**`remote addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address.  Example: "127.0.0.1" |
        ++|**`remote_addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address.  Example: "127.0.0.1" |
        + |~~`remote_ip`~~|`long`| *Deprecated. Use* `remote_addr` *instead* |
        + |**`content_length`**|`long long`| The content length of the request body. This value can be -1 if no content length was provided. The request may still have body data, but the server cannot determine the length until all data has arrived (e.g. when the client closes the connection, or the final chunk of a chunked request has been received). |
        + |**`remote_port`**|`int`| The port number at the client side (an integer number between 1 and 65535). |
        +diff --git a/docs/api/mg_server_port.md b/docs/api/mg_server_port.md
        +index 072b2979..0159be8d 100644
        +--- a/docs/api/mg_server_port.md
        ++++ b/docs/api/mg_server_port.md
        +@@ -10,8 +10,8 @@
        + |**`port`**|`int`|The port number on which the service listens|
        + |**`is_ssl`**|`int`|**0** for `HTTP` communication, **1** for `HTTPS`|
        + |**`is_redirect`**|`int`|**1** if all requests are redirected, otherwise **0**|
        +-|**`_reserved1`**|`int`|Reserved for internal use|
        +-|**`_reserved2`**|`int`|Reserved for internal use|
        ++|**`is_optional`**|`int`|**1** if port is optional, otherwise **0**|
        ++|**`is_bound`**|`int`|**1** if the port is bound, otherwise **0**|
        + |**`_reserved3`**|`int`|Reserved for internal use|
        + |**`_reserved4`**|`int`|Reserved for internal use|
        + 
         diff --git a/docs/gnutls.md b/docs/gnutls.md
         new file mode 100644
        -index 00000000..c247850f
        +index 00000000..0c4ded53
         --- /dev/null
         +++ b/docs/gnutls.md
        -@@ -0,0 +1,20 @@
        -+#### Use GnuTLS instead of OpenSSL
        -+=====
        +@@ -0,0 +1,19 @@
        ++# Use GnuTLS instead of OpenSSL
         +
         +1 [Build libgmp](https://gmplib.org)
         +
        @@ -11227,6 +2667,32 @@ index 00000000..c247850f
         +4 Run civetweb
         + - export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH
         + - ./civetweb  -listening_ports 8443s  -ssl_certificate resources/cert/server.pem  -document_root ./test/htmldir/
        +diff --git a/examples/https/civetweb.conf b/examples/https/civetweb.conf
        +index c1ecc642..552a565f 100644
        +--- a/examples/https/civetweb.conf
        ++++ b/examples/https/civetweb.conf
        +@@ -36,6 +36,9 @@ listening_ports 80r,443s
        + #document_root tdb
        + #authentication_domain mydomain.com
        + 
        ++# Optional fallback document root, checked for file-paths not found in document_root
        ++#fallback_document_root tdb_fallback
        ++
        + # Set the a certificate
        + ssl_certificate ../../resources/cert/server.pem
        + 
        +diff --git a/examples/linux_ws_server_cpp/CMakeLists.txt b/examples/linux_ws_server_cpp/CMakeLists.txt
        +index 6a71798e..cf73fc9b 100644
        +--- a/examples/linux_ws_server_cpp/CMakeLists.txt
        ++++ b/examples/linux_ws_server_cpp/CMakeLists.txt
        +@@ -1,4 +1,6 @@
        +-cmake_minimum_required(VERSION 3.5.1)
        ++cmake_minimum_required(VERSION 3.10)
        ++cmake_policy(VERSION 3.10)
        ++
        + project(linux_ws_server)
        + 
        + set(TARGET_NAME ${PROJECT_NAME})
         diff --git a/format.bat b/format.bat
         index 203b0446..3bb94e65 100755
         --- a/format.bat
        @@ -11239,41 +2705,524 @@ index 203b0446..3bb94e65 100755
          
          clang-format -i src/third_party/civetweb_lua.h
          
        -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
        -index 28c4e8cc..8736e126 100644
        ---- a/src/CMakeLists.txt
        -+++ b/src/CMakeLists.txt
        -@@ -47,7 +47,12 @@ endif()
        +diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c
        +index e8226de2..c7e27943 100644
        +--- a/fuzztest/fuzzmain.c
        ++++ b/fuzztest/fuzzmain.c
        +@@ -45,9 +45,6 @@ unsigned short PORT_NUM_HTTP = 0; /* set dynamically */
        + 	}
        + 
        + 
        +-static uint64_t call_count = 0;
        +-
        +-
        + /********************************************************/
        + /* Init CivetWeb server ... test with mock client       */
        + /********************************************************/
        +@@ -110,6 +107,18 @@ civetweb_init(void)
        + 	atexit(civetweb_exit);
        + }
        + 
        ++int LLVMFuzzerInitialize(int *argc, char ***argv);
        ++
        ++int
        ++LLVMFuzzerInitialize(int *argc, char ***argv)
        ++{
        ++	// Silence unused args warning.
        ++	(void)(argc);
        ++	(void)(argv);
        ++
        ++	civetweb_init();
        ++	return 0;
        ++}
        + 
        + #if defined(TEST_FUZZ1)
        + static int
        +@@ -202,19 +211,12 @@ test_civetweb_client(const char *server,
        + 	return 0;
        + }
        + 
        +-
        + static int
        + LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
        + {
        + 	static char URI[1024 * 64]; /* static, to avoid stack overflow */
        + 
        +-	if (call_count == 0) {
        +-		memset(URI, 0, sizeof(URI));
        +-		civetweb_init();
        +-	}
        +-	call_count++;
        +-
        +-	if (size < sizeof(URI)) {
        ++	if (size + 1 < sizeof(URI)) {
        + 		memcpy(URI, data, size);
        + 		URI[size] = 0;
        + 	} else {
        +@@ -230,11 +232,6 @@ LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
        + static int
        + LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
        + {
        +-	if (call_count == 0) {
        +-		civetweb_init();
        +-	}
        +-	call_count++;
        +-
        + 	int r;
        + 	SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);
        + 	if (sock == -1) {
        +@@ -446,15 +443,23 @@ mock_server_init(void)
        + 	atexit(mock_server_exit);
        + }
        + 
        ++int LLVMFuzzerInitialize(int *argc, char ***argv);
        ++
        ++int
        ++LLVMFuzzerInitialize(int *argc, char ***argv)
        ++{
        ++	// Silence unused args warning.
        ++	(void)(argc);
        ++	(void)(argv);
        ++
        ++	mock_server_init();
        ++	return 0;
        ++}
        ++
        + 
        + static int
        + LLVMFuzzerTestOneInput_RESPONSE(const uint8_t *data, size_t size)
        + {
        +-	if (call_count == 0) {
        +-		mock_server_init();
        +-	}
        +-	call_count++;
        +-
        + 	if (size > sizeof(RESPONSE.data)) {
        + 		return 1;
        + 	}
        +@@ -497,21 +502,26 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
        + {
        + #if defined(TEST_FUZZ1)
        + 	/* fuzz target 1: different URI for HTTP/1 server */
        +-	return LLVMFuzzerTestOneInput_URI(data, size);
        ++	LLVMFuzzerTestOneInput_URI(data, size);
        ++	return 0;
        + #elif defined(TEST_FUZZ2)
        + 	/* fuzz target 2: different requests for HTTP/1 server */
        +-	return LLVMFuzzerTestOneInput_REQUEST(data, size);
        ++	LLVMFuzzerTestOneInput_REQUEST(data, size);
        ++	return 0;
        + #elif defined(TEST_FUZZ3)
        + 	/* fuzz target 3: different responses for HTTP/1 client */
        +-	return LLVMFuzzerTestOneInput_RESPONSE(data, size);
        ++	LLVMFuzzerTestOneInput_RESPONSE(data, size);
        ++	return 0;
        + #elif defined(TEST_FUZZ4)
        + #error "Only useful in HTTP/2 feature branch"
        + 	/* fuzz target 4: different requests for HTTP/2 server */
        +-	return LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
        ++	LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
        ++	return 0;
        + #elif defined(TEST_FUZZ5)
        + 	/* fuzz target 5: calling an internal server test function,
        + 	 *                bypassing network sockets */
        +-	return LLVMFuzzerTestOneInput_process_new_connection(data, size);
        ++	LLVMFuzzerTestOneInput_process_new_connection(data, size);
        ++	return 0;
        + #else
        + /* planned targets */
        + #error "Unknown fuzz target"
        +diff --git a/include/civetweb.h b/include/civetweb.h
        +index 167c6a76..f585df25 100644
        +--- a/include/civetweb.h
        ++++ b/include/civetweb.h
        +@@ -1,4 +1,4 @@
        +-/* Copyright (c) 2013-2021 the Civetweb developers
        ++/* Copyright (c) 2013-2024 the Civetweb developers
        +  * Copyright (c) 2004-2013 Sergey Lyubka
        +  *
        +  * Permission is hereby granted, free of charge, to any person obtaining a copy
        +@@ -23,9 +23,9 @@
        + #ifndef CIVETWEB_HEADER_INCLUDED
        + #define CIVETWEB_HEADER_INCLUDED
        + 
        +-#define CIVETWEB_VERSION "1.16"
        ++#define CIVETWEB_VERSION "1.17"
        + #define CIVETWEB_VERSION_MAJOR (1)
        +-#define CIVETWEB_VERSION_MINOR (16)
        ++#define CIVETWEB_VERSION_MINOR (17)
        + #define CIVETWEB_VERSION_PATCH (0)
        + 
        + #ifndef CIVETWEB_API
        +@@ -164,6 +164,7 @@ struct mg_request_info {
        + 	const char *remote_user;    /* Authenticated user, or NULL if no auth
        + 	                               used */
        + 	char remote_addr[48];       /* Client's IP address as a string. */
        ++	char server_addr[48];       /* Server's IP address as a string. */
        + 
        + 	long long content_length; /* Length (in bytes) of the request body,
        + 	                             can be -1 if no length was given. */
        +@@ -714,8 +715,8 @@ struct mg_server_port {
        + 	int port;        /* port number */
        + 	int is_ssl;      /* https port: 0 = no, 1 = yes */
        + 	int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
        +-	int _reserved1;
        +-	int _reserved2;
        ++	int is_optional; /* optional: 0 = no, 1 = yes */
        ++	int is_bound;    /* bound: 0 = no, 1 = yes, relevant for optional ports */
        + 	int _reserved3;
        + 	int _reserved4;
        + };
        +@@ -1208,7 +1209,7 @@ struct mg_form_data_handler {
        + 	 *   filename: Name of a file to upload, at the client computer.
        + 	 *             Only set for input fields of type "file", otherwise NULL.
        + 	 *   path: Output parameter: File name (incl. path) to store the file
        +-	 *         at the server computer. Only used if FORM_FIELD_STORAGE_STORE
        ++	 *         at the server computer. Only used if MG_FORM_FIELD_STORAGE_STORE
        + 	 *         is returned by this callback. Existing files will be
        + 	 *         overwritten.
        + 	 *   pathlen: Length of the buffer for path.
        +@@ -1216,7 +1217,7 @@ struct mg_form_data_handler {
        + 	 *
        + 	 * Return value:
        + 	 *   The callback must return the intended storage for this field
        +-	 *   (See FORM_FIELD_STORAGE_*).
        ++	 *   (See MG_FORM_FIELD_STORAGE_*).
        + 	 */
        + 	int (*field_found)(const char *key,
        + 	                   const char *filename,
        +@@ -1224,7 +1225,7 @@ struct mg_form_data_handler {
        + 	                   size_t pathlen,
        + 	                   void *user_data);
        + 
        +-	/* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
        ++	/* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_GET,
        + 	 * this callback will receive the field data.
        + 	 *
        + 	 * Parameters:
        +@@ -1241,7 +1242,7 @@ struct mg_form_data_handler {
        + 	                 size_t valuelen,
        + 	                 void *user_data);
        + 
        +-	/* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
        ++	/* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_STORE,
        + 	 * the data will be stored into a file. If the file has been written
        + 	 * successfully, this callback will be called. This callback will
        + 	 * not be called for only partially uploaded files. The
        +diff --git a/resources/Makefile.in-lua b/resources/Makefile.in-lua
        +index d3038f66..8afff851 100644
        +--- a/resources/Makefile.in-lua
        ++++ b/resources/Makefile.in-lua
        +@@ -39,7 +39,14 @@ ifeq ($(WITH_LUA_VERSION), 504)
        +   $(info Lua: Using version 5.4.3)
        +   LUA_DIR = src/third_party/lua-5.4.3/src
        +   LUA_SHARED_LIB_FLAG = -llua5.4
        +-  LUA_CFLAGS = -DLUA_COMPAT_5_2 -DLUA_VERSION_MAKEFILE=504
        ++  LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=504
        ++  LUA_VERSION_KNOWN = 1
        ++endif
        ++ifeq ($(WITH_LUA_VERSION), 505)
        ++  $(info Lua: Using version 5.5.0-beta)
        ++  LUA_DIR = src/third_party/lua-5.5.0-beta/src
        ++  LUA_SHARED_LIB_FLAG = -llua5.5
        ++  LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=505
        +   LUA_VERSION_KNOWN = 1
        + endif
        + 
        +@@ -112,6 +119,12 @@ ifeq ($(WITH_LUA_VERSION), 504)
        +     lctype.c  \
        +     lutf8lib.c
        + endif
        ++ifeq ($(WITH_LUA_VERSION), 505)
        ++    LUA_SOURCE_FILES += \
        ++    lcorolib.c  \
        ++    lctype.c  \
        ++    lutf8lib.c
        ++endif
        + 
        +   $(info Lua: using static library)
        + 
        +@@ -150,6 +163,7 @@ CFLAGS += -DUSE_LUA_FILE_SYSTEM
        + #SOURCE_DIRS = $(LFS_DIR)
        + 
        + 
        ++ifneq ($(WITH_LUA_VERSION), 501)
        + LXX_DIR = src/third_party
        + LXX_SOURCE_FILES = lua_struct.c
        + LXX_SOURCES = $(addprefix $(LXX_DIR)/, $(LXX_SOURCE_FILES))
        +@@ -159,6 +173,7 @@ OBJECTS += $(LXX_OBJECTS)
        + CFLAGS += $(LXX_CFLAGS)
        + CFLAGS += -DUSE_LUA_STRUCT
        + #SOURCE_DIRS = $(LXX_DIR)
        ++endif
        + 
        + 
        + ifneq ($(WITH_LUA_VERSION), 501)
        +diff --git a/resources/civetweb.conf b/resources/civetweb.conf
        +index c5675481..293f27b5 100644
        +--- a/resources/civetweb.conf
        ++++ b/resources/civetweb.conf
        +@@ -26,6 +26,7 @@ listening_ports 8080
        + # extra_mime_types 
        + # ssl_certificate 
        + # num_threads 50
        ++# prespawn_threads 0
        + # run_as_user 
        + # url_rewrite_patterns 
        + # hide_files_patterns 
        +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
        +index 43f3a77e..de4c2d62 100644
        +--- a/src/CMakeLists.txt
        ++++ b/src/CMakeLists.txt
        +@@ -1,3 +1,6 @@
        ++cmake_minimum_required(VERSION 3.10)
        ++cmake_policy(VERSION 3.10)
        ++
        + # The C API library
        + set( LIB_TYPE "STATIC" )
        + if (BUILD_SHARED_LIBS)
        +@@ -47,16 +50,28 @@ endif()
        + 
        + # We need to link OpenSSL if not dynamically loading
        + if (CIVETWEB_ENABLE_SSL)
        +-  if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        +-    find_package(LibDl)
        +-    if (LIBDL_FOUND)
        +-      target_link_libraries(civetweb-c-library -ldl)
        +-    endif()
        ++  if (CIVETWEB_ENABLE_GNUTLS)
        ++    find_package(GnuTLS)
        ++    include_directories(${GNUTLS_INCLUDE_DIR})
        ++    message(STATUS "GnuTLS include directory: ${GNUTLS_INCLUDE_DIR}")
        ++    target_link_libraries(civetweb-c-library ${GNUTLS_LIBRARIES})
        ++  elseif (CIVETWEB_ENABLE_MBEDTLS)
        ++    find_package(MbedTLS)
        ++    include_directories(${MbedTLS_INCLUDE_DIR})
        ++    message(STATUS "MbedTLS include directory: ${MbedTLS_INCLUDE_DIR}")
        ++    target_link_libraries(civetweb-c-library ${MbedTLS_LIBRARIES})
        +   else()
        +-    find_package(OpenSSL)
        +-    include_directories(${OPENSSL_INCLUDE_DIR})
        +-    message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
        +-    target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
        ++    if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        ++      find_package(LibDl)
        ++      if (LIBDL_FOUND)
        ++        target_link_libraries(civetweb-c-library -ldl)
        ++      endif()
        ++    else()
        ++      find_package(OpenSSL)
        ++      include_directories(${OPENSSL_INCLUDE_DIR})
        ++      message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
        ++      target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
        ++    endif()
        +   endif()
        + endif()
        + 
        +diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp
        +index fafd6e18..2bcf5c31 100644
        +--- a/src/CivetServer.cpp
        ++++ b/src/CivetServer.cpp
        +@@ -406,12 +406,12 @@ CivetServer::CivetServer(const char **options,
        + 		userCloseHandler = NULL;
        + 	}
        + 	callbacks.connection_close = closeHandler;
        +-	struct mg_init_data mg_start_init_data = {0};
        ++	struct mg_init_data mg_start_init_data = {};
        + 	mg_start_init_data.callbacks = &callbacks;
        + 	mg_start_init_data.user_data = this;
        + 	mg_start_init_data.configuration_options = options;
        + 
        +-	struct mg_error_data mg_start_error_data = {0};
        ++	struct mg_error_data mg_start_error_data = {};
        + 	char errtxtbuf[256] = {0};
        + 	mg_start_error_data.text = errtxtbuf;
        + 	mg_start_error_data.text_buffer_size = sizeof(errtxtbuf);
        +@@ -450,12 +450,12 @@ CivetServer::CivetServer(const std::vector &options,
        + 	}
        + 	pointers.back() = NULL;
        + 
        +-	struct mg_init_data mg_start_init_data = {0};
        ++	struct mg_init_data mg_start_init_data = {};
        + 	mg_start_init_data.callbacks = &callbacks;
        + 	mg_start_init_data.user_data = this;
        + 	mg_start_init_data.configuration_options = &pointers[0];
        + 
        +-	struct mg_error_data mg_start_error_data = {0};
        ++	struct mg_error_data mg_start_error_data = {};
        + 	char errtxtbuf[256] = {0};
        + 	mg_start_error_data.text = errtxtbuf;
        + 	mg_start_error_data.text_buffer_size = sizeof(errtxtbuf);
        +diff --git a/src/civetweb.c b/src/civetweb.c
        +index 9e321edf..f6a60e21 100644
        +--- a/src/civetweb.c
        ++++ b/src/civetweb.c
        +@@ -1,4 +1,4 @@
        +-/* Copyright (c) 2013-2021 the Civetweb developers
        ++/* Copyright (c) 2013-2025 the Civetweb developers
        +  * Copyright (c) 2004-2013 Sergey Lyubka
        +  *
        +  * Permission is hereby granted, free of charge, to any person obtaining a copy
        +@@ -51,8 +51,8 @@
        + #if !defined(_CRT_SECURE_NO_WARNINGS)
        + #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
        + #endif
        +-#if !defined(_WIN32_WINNT) /* defined for tdm-gcc so we can use getnameinfo */
        +-#define _WIN32_WINNT 0x0502
        ++#if !defined(_WIN32_WINNT) /* Minimum API version */
        ++#define _WIN32_WINNT 0x0601
        + #endif
        + #else
        + #if !defined(_GNU_SOURCE)
        +@@ -161,7 +161,7 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
        +  * file system.
        +  * NO_FILES only disables the automatic mapping between URLs and local
        +  * file names.
        +- * NO_FILESYSTEM = do not access any file at all. Useful for embedded
        ++ * NO_FILESYSTEMS = do not access any file at all. Useful for embedded
        +  * devices without file system. Logging to files in not available
        +  * (use callbacks instead) and API functions like mg_send_file are not
        +  * available.
        +@@ -183,6 +183,10 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
        + #error "Symbian is no longer maintained. CivetWeb no longer supports Symbian."
        + #endif /* __SYMBIAN32__ */
        + 
        ++#if defined(__rtems__)
        ++#include 
        ++#endif
        ++
        + #if defined(__ZEPHYR__)
        + #include 
        + #include 
        +@@ -235,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func,
        + 
        + #define NEED_DEBUG_TRACE_FUNC
        + #if !defined(DEBUG_TRACE_STREAM)
        +-#define DEBUG_TRACE_STREAM stdout
        ++#define DEBUG_TRACE_STREAM stderr
        + #endif
        + 
        + #else
        +@@ -885,7 +889,9 @@ typedef unsigned short int in_port_t;
        + #include 
        + #include 
        + #include 
        ++#if !defined(__rtems__)
        + #include 
        ++#endif
        + #include 
        + #include 
        + #include 
        +@@ -901,8 +907,22 @@ typedef unsigned short int in_port_t;
        + #endif
        + 
        + #if defined(__MACH__) && defined(__APPLE__)
        +-#define SSL_LIB "libssl.dylib"
        +-#define CRYPTO_LIB "libcrypto.dylib"
        ++
        ++#if defined(OPENSSL_API_3_0)
        ++#define SSL_LIB "libssl.3.dylib"
        ++#define CRYPTO_LIB "libcrypto.3.dylib"
        ++#endif
        ++
        ++#if defined(OPENSSL_API_1_1)
        ++#define SSL_LIB "libssl.1.1.dylib"
        ++#define CRYPTO_LIB "libcrypto.1.1.dylib"
        ++#endif /* OPENSSL_API_1_1 */
        ++
        ++#if defined(OPENSSL_API_1_0)
        ++#define SSL_LIB "libssl.1.0.dylib"
        ++#define CRYPTO_LIB "libcrypto.1.0.dylib"
        ++#endif /* OPENSSL_API_1_0 */
        ++
        + #else
        + #if !defined(SSL_LIB)
        + #define SSL_LIB "libssl.so"
        +@@ -963,7 +983,7 @@ count_leap(int y)
        + 	return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
        + }
        + 
        +-time_t
        ++static time_t
        + timegm(struct tm *tm)
        + {
        + 	static const unsigned short ydays[] = {
        +@@ -1125,7 +1145,15 @@ mg_atomic_inc(volatile ptrdiff_t *addr)
        + #if defined(_WIN64) && !defined(NO_ATOMICS)
        + 	ret = InterlockedIncrement64(addr);
        + #elif defined(_WIN32) && !defined(NO_ATOMICS)
        ++#ifdef __cplusplus
        ++	/* For C++ the Microsoft Visual Studio compiler can not decide what
        ++	 * overloaded function prototpye in the SDC corresponds to "ptrdiff_t". */
        ++	static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
        ++	static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
        ++	ret = InterlockedIncrement((LONG *)addr);
        ++#else
        + 	ret = InterlockedIncrement(addr);
        ++#endif
        + #elif defined(__GNUC__)                                                        \
        +     && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
        +     && !defined(NO_ATOMICS)
        +@@ -1148,7 +1176,14 @@ mg_atomic_dec(volatile ptrdiff_t *addr)
        + #if defined(_WIN64) && !defined(NO_ATOMICS)
        + 	ret = InterlockedDecrement64(addr);
        + #elif defined(_WIN32) && !defined(NO_ATOMICS)
        ++#ifdef __cplusplus
        ++	/* see mg_atomic_inc */
        ++	static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
        ++	static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
        ++	ret = InterlockedDecrement((LONG *)addr);
        ++#else
        + 	ret = InterlockedDecrement(addr);
        ++#endif
        + #elif defined(__GNUC__)                                                        \
        +     && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
        +     && !defined(NO_ATOMICS)
        +@@ -1307,13 +1342,13 @@ mg_malloc_ex(size_t size,
        + #endif
        + 
        + 	if (data) {
        ++		uintptr_t *tmp = (uintptr_t *)data;
        + 		ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size);
        + 		mg_atomic_max(&mstat->maxMemUsed, mmem);
        +-
        + 		mg_atomic_inc(&mstat->blockCount);
        +-		((uintptr_t *)data)[0] = size;
        +-		((uintptr_t *)data)[1] = (uintptr_t)mstat;
        +-		memory = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
        ++		tmp[0] = size;
        ++		tmp[1] = (uintptr_t)mstat;
        ++		memory = (void *)&tmp[2];
        + 	}
          
        - # We need to link OpenSSL if not dynamically loading
        - if (CIVETWEB_ENABLE_SSL)
        --  if (CIVETWEB_ENABLE_MBEDTLS)
        -+  if (CIVETWEB_ENABLE_GNUTLS)
        -+    find_package(GnuTLS)
        -+    include_directories(${GNUTLS_INCLUDE_DIR})
        -+    message(STATUS "GnuTLS include directory: ${GNUTLS_INCLUDE_DIR}")
        -+    target_link_libraries(civetweb-c-library ${GNUTLS_LIBRARIES})
        -+  elseif (CIVETWEB_ENABLE_MBEDTLS)
        -     find_package(MbedTLS)
        -     include_directories(${MbedTLS_INCLUDE_DIR})
        -     message(STATUS "MbedTLS include directory: ${MbedTLS_INCLUDE_DIR}")
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index c52c8e7a..814e1de0 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -1594,8 +1594,9 @@ static int mg_init_library_called = 0;
        + #if defined(MEMORY_DEBUGGING)
        +@@ -1536,11 +1571,13 @@ static void mg_snprintf(const struct mg_connection *conn,
        + #if defined(vsnprintf)
        + #undef vsnprintf
        + #endif
        ++#if !defined(NDEBUG)
        + #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
        + #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
        + #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
        + #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
        + #define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
        ++#endif
        + #if defined(_WIN32)
        + /* vsnprintf must not be used in any system,
        +  * but this define only works well for Windows. */
        +@@ -1557,8 +1594,9 @@ static int mg_init_library_called = 0;
          static int mg_openssl_initialized = 0;
          #endif
          #if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1)                     \
         -    && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)
         -#error "Please define OPENSSL_API_#_# or USE_MBEDTLS"
         +    && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)                      \
        -+	&& !defined(USE_GNUTLS)
        ++    && !defined(USE_GNUTLS)
         +#error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS"
          #endif
          #if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
          #error "Multiple OPENSSL_API versions defined"
        -@@ -1608,7 +1609,10 @@ static int mg_openssl_initialized = 0;
        +@@ -1571,7 +1609,10 @@ static int mg_openssl_initialized = 0;
          #endif
          #if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)                      \
               || defined(OPENSSL_API_3_0))                                              \
        @@ -11285,7 +3234,7 @@ index c52c8e7a..814e1de0 100644
          #error "Multiple SSL libraries defined"
          #endif
          #endif
        -@@ -1773,11 +1777,15 @@ typedef int socklen_t;
        +@@ -1736,11 +1777,15 @@ typedef int socklen_t;
          #endif
          
          
        @@ -11302,7 +3251,135 @@ index c52c8e7a..814e1de0 100644
          #elif defined(NO_SSL)
          /* no SSL */
          typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
        -@@ -2564,7 +2572,6 @@ struct mg_connection {
        +@@ -1906,7 +1951,9 @@ struct socket {
        + 	unsigned char is_ssl;    /* Is port SSL-ed */
        + 	unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
        + 	                          * port */
        +-	unsigned char in_use;    /* 0: invalid, 1: valid, 2: free */
        ++	unsigned char
        ++	    is_optional; /* Shouldn't cause us to exit if we can't bind to it */
        ++	unsigned char in_use; /* 0: invalid, 1: valid, 2: free */
        + };
        + 
        + 
        +@@ -1919,6 +1966,7 @@ enum {
        + 	/* Once for each server */
        + 	LISTENING_PORTS,
        + 	NUM_THREADS,
        ++	PRESPAWN_THREADS,
        + 	RUN_AS_USER,
        + 	CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
        + 	                     * socket option typedef TCP_NODELAY. */
        +@@ -1952,6 +2000,7 @@ enum {
        + 
        + 	/* Once for each domain */
        + 	DOCUMENT_ROOT,
        ++	FALLBACK_DOCUMENT_ROOT,
        + 
        + 	ACCESS_LOG_FILE,
        + 	ERROR_LOG_FILE,
        +@@ -2033,14 +2082,17 @@ enum {
        + 
        + #if defined(USE_WEBSOCKET)
        + 	WEBSOCKET_ROOT,
        ++	FALLBACK_WEBSOCKET_ROOT,
        + #endif
        + #if defined(USE_LUA) && defined(USE_WEBSOCKET)
        + 	LUA_WEBSOCKET_EXTENSIONS,
        + #endif
        +-
        ++	REPLACE_ASTERISK_WITH_ORIGIN,
        + 	ACCESS_CONTROL_ALLOW_ORIGIN,
        + 	ACCESS_CONTROL_ALLOW_METHODS,
        + 	ACCESS_CONTROL_ALLOW_HEADERS,
        ++	ACCESS_CONTROL_EXPOSE_HEADERS,
        ++	ACCESS_CONTROL_ALLOW_CREDENTIALS,
        + 	ERROR_PAGES,
        + #if !defined(NO_CACHING)
        + 	STATIC_FILE_MAX_AGE,
        +@@ -2064,6 +2116,7 @@ static const struct mg_option config_options[] = {
        +     /* Once for each server */
        +     {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"},
        +     {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"},
        ++    {"prespawn_threads", MG_CONFIG_TYPE_NUMBER, "0"},
        +     {"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
        +     {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"},
        +     {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"},
        +@@ -2096,6 +2149,7 @@ static const struct mg_option config_options[] = {
        + 
        +     /* Once for each domain */
        +     {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        ++    {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        + 
        +     {"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
        +     {"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
        +@@ -2194,13 +2248,17 @@ static const struct mg_option config_options[] = {
        + 
        + #if defined(USE_WEBSOCKET)
        +     {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        ++    {"fallback_websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        + #endif
        + #if defined(USE_LUA) && defined(USE_WEBSOCKET)
        +     {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
        + #endif
        ++    {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"},
        +     {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
        +     {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
        +     {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
        ++    {"access_control_expose_headers", MG_CONFIG_TYPE_STRING, ""},
        ++    {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""},
        +     {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
        + #if !defined(NO_CACHING)
        +     {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"},
        +@@ -2310,9 +2368,11 @@ STOP_FLAG_IS_TWO(stop_flag_t *f)
        + static void
        + STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v)
        + {
        +-	stop_flag_t sf;
        ++	stop_flag_t sf = 0;
        + 	do {
        +-		sf = mg_atomic_compare_and_swap(f, *f, v);
        ++		sf = mg_atomic_compare_and_swap(f,
        ++		                                __atomic_load_n(f, __ATOMIC_SEQ_CST),
        ++		                                v);
        + 	} while (sf != v);
        + }
        + 
        +@@ -2373,10 +2433,18 @@ struct mg_context {
        + 	stop_flag_t stop_flag;        /* Should we stop event loop */
        + 	pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
        + 
        +-	pthread_t masterthreadid; /* The master thread ID */
        ++	pthread_t masterthreadid;            /* The master thread ID */
        ++	unsigned int cfg_max_worker_threads; /* How many worker-threads we are
        ++	                                        allowed to create, total */
        ++
        ++	unsigned int spawned_worker_threads; /* How many worker-threads currently
        ++	                                        exist (modified by master thread) */
        + 	unsigned int
        +-	    cfg_worker_threads;      /* The number of configured worker threads. */
        +-	pthread_t *worker_threadids; /* The worker thread IDs */
        ++	    idle_worker_thread_count; /* How many worker-threads are currently
        ++	                                 sitting around with nothing to do */
        ++	/* Access to this value MUST be synchronized by thread_mutex */
        ++
        ++	pthread_t *worker_threadids;      /* The worker thread IDs */
        + 	unsigned long starter_thread_idx; /* thread index which called mg_start */
        + 
        + 	/* Connection to thread dispatching */
        +@@ -2423,6 +2491,11 @@ struct mg_context {
        + 	int lua_bg_log_available;     /* Use Lua background state for access log */
        + #endif
        + 
        ++	int user_shutdown_notification_socket;   /* mg_stop() will close this
        ++	                                            socket... */
        ++	int thread_shutdown_notification_socket; /* to cause poll() in all threads
        ++	                                            to return immediately */
        ++
        + 	/* Server nonce */
        + 	pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers,
        + 	                              * ssl_cert_last_mtime, nonce_count, and
        +@@ -2502,7 +2575,6 @@ struct mg_connection {
          	                 * versions. For the current definition, see
          	                 * mg_get_connection_info_impl */
          #endif
        @@ -11310,7 +3387,106 @@ index c52c8e7a..814e1de0 100644
          	SSL *ssl;               /* SSL descriptor */
          	struct socket client;   /* Connected client */
          	time_t conn_birth_time; /* Time (wall clock) when connection was
        -@@ -6131,7 +6138,7 @@ push_inner(struct mg_context *ctx,
        +@@ -3268,6 +3340,8 @@ mg_get_server_ports(const struct mg_context *ctx,
        + 		    ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
        + 		ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
        + 		ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
        ++		ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
        ++		ports[cnt].is_bound = ctx->listening_sockets[i].sock != INVALID_SOCKET;
        + 
        + 		if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
        + 			/* IPv4 */
        +@@ -3409,7 +3483,7 @@ mg_cry_internal_impl(const struct mg_connection *conn,
        + 	DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf);
        + 
        + 	if (!conn) {
        +-		puts(buf);
        ++		fputs(buf, stderr);
        + 		return;
        + 	}
        + 
        +@@ -4139,6 +4213,8 @@ send_additional_header(struct mg_connection *conn)
        + 	}
        + #endif
        + 
        ++	// Content-Security-Policy
        ++
        + 	if (header && header[0]) {
        + 		mg_response_header_add_lines(conn, header);
        + 	}
        +@@ -4151,15 +4227,68 @@ send_cors_header(struct mg_connection *conn)
        + 	const char *origin_hdr = mg_get_header(conn, "Origin");
        + 	const char *cors_orig_cfg =
        + 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
        ++	const char *cors_cred_cfg =
        ++	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        ++	const char *cors_hdr_cfg =
        ++	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
        ++	const char *cors_exphdr_cfg =
        ++	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        ++	const char *cors_meth_cfg =
        ++	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
        ++	const char *cors_repl_asterisk_with_orig_cfg =
        ++	    conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
        ++
        ++	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr
        ++	    && cors_repl_asterisk_with_orig_cfg
        ++	    && *cors_repl_asterisk_with_orig_cfg) {
        ++		int cors_repl_asterisk_with_orig =
        ++		    mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
        + 
        +-	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
        + 		/* Cross-origin resource sharing (CORS), see
        + 		 * http://www.html5rocks.com/en/tutorials/cors/,
        + 		 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
        + 		 * CORS preflight is not supported for files. */
        ++		if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') {
        ++			mg_response_header_add(conn,
        ++			                       "Access-Control-Allow-Origin",
        ++			                       origin_hdr,
        ++			                       -1);
        ++		} else {
        ++			mg_response_header_add(conn,
        ++			                       "Access-Control-Allow-Origin",
        ++			                       cors_orig_cfg,
        ++			                       -1);
        ++		}
        ++	}
        ++
        ++	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
        ++		/* Cross-origin resource sharing (CORS), see
        ++		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
        ++		 */
        ++		mg_response_header_add(conn,
        ++		                       "Access-Control-Allow-Credentials",
        ++		                       cors_cred_cfg,
        ++		                       -1);
        ++	}
        ++
        ++	if (cors_hdr_cfg && *cors_hdr_cfg) {
        ++		mg_response_header_add(conn,
        ++		                       "Access-Control-Allow-Headers",
        ++		                       cors_hdr_cfg,
        ++		                       -1);
        ++	}
        ++
        ++	if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        + 		mg_response_header_add(conn,
        +-		                       "Access-Control-Allow-Origin",
        +-		                       cors_orig_cfg,
        ++		                       "Access-Control-Expose-Headers",
        ++		                       cors_exphdr_cfg,
        ++		                       -1);
        ++	}
        ++
        ++	if (cors_meth_cfg && *cors_meth_cfg) {
        ++		mg_response_header_add(conn,
        ++		                       "Access-Control-Allow-Methods",
        ++		                       cors_meth_cfg,
        + 		                       -1);
        + 	}
        + }
        +@@ -6028,7 +6157,7 @@ push_inner(struct mg_context *ctx,
          		return -2;
          	}
          
        @@ -11319,15 +3495,18 @@ index c52c8e7a..814e1de0 100644
          	if (ssl) {
          		return -2;
          	}
        -@@ -6157,6 +6164,16 @@ push_inner(struct mg_context *ctx,
        +@@ -6054,6 +6183,19 @@ push_inner(struct mg_context *ctx,
          				err = 0;
          			}
          		} else
         +#elif defined(USE_GNUTLS)
         +		if (ssl != NULL) {
        -+			n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t) len);
        ++			n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t)len);
         +			if (n < 0) {
        -+				fprintf(stderr, "SSL write failed (%d): %s", n, gnutls_strerror(n));
        ++				fprintf(stderr,
        ++				        "SSL write failed (%d): %s",
        ++				        n,
        ++				        gnutls_strerror(n));
         +				return -2;
         +			} else {
         +				err = 0;
        @@ -11336,7 +3515,61 @@ index c52c8e7a..814e1de0 100644
          #elif !defined(NO_SSL)
          		if (ssl != NULL) {
          			ERR_clear_error();
        -@@ -6413,6 +6430,62 @@ pull_inner(FILE *fp,
        +@@ -6128,12 +6270,20 @@ push_inner(struct mg_context *ctx,
        + 			mg_sleep(5);
        + 		} else {
        + 			/* For sockets, wait for the socket using poll */
        +-			struct mg_pollfd pfd[1];
        ++			struct mg_pollfd pfd[2];
        + 			int pollres;
        ++			unsigned int num_sock = 1;
        + 
        + 			pfd[0].fd = sock;
        + 			pfd[0].events = POLLOUT;
        +-			pollres = mg_poll(pfd, 1, (int)(ms_wait), &(ctx->stop_flag));
        ++
        ++			if (ctx->context_type == CONTEXT_SERVER) {
        ++				pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
        ++				pfd[num_sock].events = POLLIN;
        ++				num_sock++;
        ++			}
        ++
        ++			pollres = mg_poll(pfd, num_sock, (int)(ms_wait), &(ctx->stop_flag));
        + 			if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
        + 				return -2;
        + 			}
        +@@ -6241,9 +6391,10 @@ pull_inner(FILE *fp,
        + 
        + #if defined(USE_MBEDTLS)
        + 	} else if (conn->ssl != NULL) {
        +-		struct mg_pollfd pfd[1];
        ++		struct mg_pollfd pfd[2];
        + 		int to_read;
        + 		int pollres;
        ++		unsigned int num_sock = 1;
        + 
        + 		to_read = mbedtls_ssl_get_bytes_avail(conn->ssl);
        + 
        +@@ -6259,10 +6410,17 @@ pull_inner(FILE *fp,
        + 			pfd[0].fd = conn->client.sock;
        + 			pfd[0].events = POLLIN;
        + 
        ++			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        ++				pfd[num_sock].fd =
        ++				    conn->phys_ctx->thread_shutdown_notification_socket;
        ++				pfd[num_sock].events = POLLIN;
        ++				num_sock++;
        ++			}
        ++
        + 			to_read = len;
        + 
        + 			pollres = mg_poll(pfd,
        +-			                  1,
        ++			                  num_sock,
        + 			                  (int)(timeout * 1000.0),
        + 			                  &(conn->phys_ctx->stop_flag));
        + 
        +@@ -6294,11 +6452,71 @@ pull_inner(FILE *fp,
          			nread = 0;
          		}
          
        @@ -11383,7 +3616,10 @@ index c52c8e7a..814e1de0 100644
         +		if (pollres > 0) {
         +			nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
         +			if (nread < 0) {
        -+				fprintf(stderr, "SSL read failed (%d): %s", nread, gnutls_strerror(nread));
        ++				fprintf(stderr,
        ++				        "SSL read failed (%d): %s",
        ++				        nread,
        ++				        gnutls_strerror(nread));
         +				return -2;
         +			} else {
         +				err = 0;
        @@ -11399,2362 +3635,1807 @@ index c52c8e7a..814e1de0 100644
          #elif !defined(NO_SSL)
          	} else if (conn->ssl != NULL) {
          		int ssl_pending;
        -@@ -9555,7 +9628,7 @@ connect_socket(
        - 		return 0;
        - 	}
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL)
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) && !defined(NO_SSL_DL)
        - #if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
        - 	if (use_ssl && (TLS_client_method == NULL)) {
        - 		if (error != NULL) {
        -@@ -16660,6 +16733,37 @@ mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
        - 	           : 0;
        - }
        +-		struct mg_pollfd pfd[1];
        ++		struct mg_pollfd pfd[2];
        + 		int pollres;
        ++		unsigned int num_sock = 1;
          
        -+#elif defined(USE_GNUTLS)
        -+/* Check if SSL is required.
        -+ * If so, set up ctx->ssl_ctx pointer. */
        -+static int
        -+mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
        -+{
        -+	if (!phys_ctx) {
        -+		return 0;
        -+	}
        -+
        -+	if (!dom_ctx) {
        -+		dom_ctx = &(phys_ctx->dd);
        -+	}
        -+
        -+	if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
        -+		/* No SSL port is set. No need to setup SSL. */
        -+		return 1;
        -+	}
        -+
        -+	dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
        -+	if (dom_ctx->ssl_ctx == NULL) {
        -+		fprintf(stderr, "ssl_ctx malloc failed\n");
        -+		return 0;
        -+	}
        + 		if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
        + 			/* We already know there is no more data buffered in conn->buf
        +@@ -6311,8 +6529,16 @@ pull_inner(FILE *fp,
        + 		} else {
        + 			pfd[0].fd = conn->client.sock;
        + 			pfd[0].events = POLLIN;
         +
        -+	return gtls_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
        -+	               == 0
        -+	           ? 1
        -+	           : 0;
        -+}
        ++			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        ++				pfd[num_sock].fd =
        ++				    conn->phys_ctx->thread_shutdown_notification_socket;
        ++				pfd[num_sock].events = POLLIN;
        ++				num_sock++;
        ++			}
         +
        - #elif !defined(NO_SSL)
        - 
        - static int ssl_use_pem_file(struct mg_context *phys_ctx,
        -@@ -17935,7 +18039,7 @@ uninitialize_openssl(void)
        - #endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
        - 	}
        - }
        --#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) */
        -+#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) */
        + 			pollres = mg_poll(pfd,
        +-			                  1,
        ++			                  num_sock,
        + 			                  (int)(timeout * 1000.0),
        + 			                  &(conn->phys_ctx->stop_flag));
        + 			if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        +@@ -6350,13 +6576,22 @@ pull_inner(FILE *fp,
        + #endif
          
        + 	} else {
        +-		struct mg_pollfd pfd[1];
        ++		struct mg_pollfd pfd[2];
        + 		int pollres;
        ++		unsigned int num_sock = 1;
          
        - #if !defined(NO_FILESYSTEMS)
        -@@ -18207,6 +18311,11 @@ close_connection(struct mg_connection *conn)
        - 		mbed_ssl_close(conn->ssl);
        - 		conn->ssl = NULL;
        + 		pfd[0].fd = conn->client.sock;
        + 		pfd[0].events = POLLIN;
        ++
        ++		if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        ++			pfd[num_sock].fd =
        ++			    conn->phys_ctx->thread_shutdown_notification_socket;
        ++			pfd[num_sock].events = POLLIN;
        ++			num_sock++;
        ++		}
        ++
        + 		pollres = mg_poll(pfd,
        +-		                  1,
        ++		                  num_sock,
        + 		                  (int)(timeout * 1000.0),
        + 		                  &(conn->phys_ctx->stop_flag));
        + 		if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        +@@ -6378,7 +6613,7 @@ pull_inner(FILE *fp,
        + 		}
          	}
        -+#elif defined(USE_GNUTLS)
        -+	if (conn->ssl != NULL) {
        -+		gtls_ssl_close(conn->ssl);
        -+		conn->ssl = NULL;
        -+	}
        - #elif !defined(NO_SSL)
        - 	if (conn->ssl != NULL) {
        - 		/* Run SSL_shutdown twice to ensure completely close SSL connection
        -@@ -18278,7 +18387,7 @@ mg_close_connection(struct mg_connection *conn)
          
        - 	close_connection(conn);
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - 	if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT)
        - 	     || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT))
        - 	    && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
        -@@ -18380,7 +18489,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        - 		return NULL;
        +-	if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        ++	if (conn != NULL && !STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        + 		return -2;
          	}
          
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - #if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0))                     \
        -     && !defined(NO_SSL_DL)
        - 
        -@@ -18457,7 +18566,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        - 			            error->text_buffer_size,
        - 			            "Can not create mutex");
        +@@ -6402,7 +6637,7 @@ pull_inner(FILE *fp,
        + 			/* See https://www.chilkatsoft.com/p/p_299.asp */
        + 			return -2;
        + 		} else {
        +-			DEBUG_TRACE("recv() failed, error %d", err);
        ++			DEBUG_TRACE("read()/recv() failed, error %d", err);
        + 			return -2;
        + 		}
        + #else
        +@@ -6424,7 +6659,7 @@ pull_inner(FILE *fp,
        + 			 * (see signal(7)).
        + 			 * => stay in the while loop */
        + 		} else {
        +-			DEBUG_TRACE("recv() failed, error %d", err);
        ++			DEBUG_TRACE("read()/recv() failed, error %d", err);
        + 			return -2;
          		}
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - 		SSL_CTX_free(conn->dom_ctx->ssl_ctx);
          #endif
        - 		closesocket(sock);
        -@@ -18465,7 +18574,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        - 		return NULL;
        - 	}
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - 	if (use_ssl) {
        - 		/* TODO: Check ssl_verify_peer and ssl_ca_path here.
        - 		 * SSL_CTX_set_verify call is needed to switch off server
        -@@ -20186,6 +20295,24 @@ worker_thread_run(struct mg_connection *conn)
        - 				close_connection(conn);
        - 			}
        - 
        -+#elif defined(USE_GNUTLS)
        -+			/* HTTPS connection */
        -+			if (gtls_ssl_accept(&(conn->ssl),
        -+			                    conn->dom_ctx->ssl_ctx,
        -+			                    conn->client.sock,
        -+			                    conn->phys_ctx)
        -+			    == 0) {
        -+				/* conn->dom_ctx is set in get_request */
        -+				/* process HTTPS connection */
        -+				init_connection(conn);
        -+				conn->connection_type = CONNECTION_TYPE_REQUEST;
        -+				conn->protocol_type = PROTOCOL_TYPE_HTTP1;
        -+				process_new_connection(conn);
        -+			} else {
        -+				/* make sure the connection is cleaned up on SSL failure */
        -+				close_connection(conn);
        -+			}
        +@@ -7052,6 +7287,7 @@ mg_url_decode(const char *src,
        +               int is_form_url_encoded)
        + {
        + 	int i, j, a, b;
         +
        - #elif !defined(NO_SSL)
        - 			/* HTTPS connection */
        - 			if (sslize(conn, SSL_accept, NULL)) {
        -@@ -20693,6 +20820,13 @@ free_context(struct mg_context *ctx)
        - 		ctx->dd.ssl_ctx = NULL;
        + #define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
        + 
        + 	for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
        +@@ -7064,11 +7300,15 @@ mg_url_decode(const char *src,
        + 			i += 2;
        + 		} else if (is_form_url_encoded && (src[i] == '+')) {
        + 			dst[j] = ' ';
        ++		} else if ((unsigned char)src[i] <= ' ') {
        ++			return -1; /* invalid character */
        + 		} else {
        + 			dst[j] = src[i];
        + 		}
          	}
          
        -+#elif defined(USE_GNUTLS)
        -+	if (ctx->dd.ssl_ctx != NULL) {
        -+		gtls_sslctx_uninit(ctx->dd.ssl_ctx);
        -+		mg_free(ctx->dd.ssl_ctx);
        -+		ctx->dd.ssl_ctx = NULL;
        -+	}
        ++#undef HEXTOI
         +
        - #elif !defined(NO_SSL)
        - 	/* Deallocate SSL context */
        - 	if (ctx->dd.ssl_ctx != NULL) {
        -@@ -21383,7 +21517,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 	}
        - #endif
        + 	dst[j] = '\0'; /* Null-terminate the destination */
          
        --#if defined(USE_MBEDTLS)
        -+#if defined(USE_MBEDTLS) || defined(USE_GNUTLS)
        - 	if (!mg_sslctx_init(ctx, NULL)) {
        - 		const char *err_msg = "Error initializing SSL context";
        - 		/* Fatal error - abort start. */
        -@@ -21868,7 +22002,7 @@ mg_start_domain2(struct mg_context *ctx,
        - 	new_dom->shared_lua_websockets = NULL;
        - #endif
        + 	return (i >= src_len) ? j : -1;
        +@@ -7571,10 +7811,10 @@ extention_matches_template_text(
        +  * Return 1 if index file has been found, 0 if not found.
        +  * If the file is found, it's stats is returned in stp. */
        + static int
        +-substitute_index_file(struct mg_connection *conn,
        +-                      char *path,
        +-                      size_t path_len,
        +-                      struct mg_file_stat *filestat)
        ++substitute_index_file_aux(struct mg_connection *conn,
        ++                          char *path,
        ++                          size_t path_len,
        ++                          struct mg_file_stat *filestat)
        + {
        + 	const char *list = conn->dom_ctx->config[INDEX_FILES];
        + 	struct vec filename_vec;
        +@@ -7615,6 +7855,61 @@ substitute_index_file(struct mg_connection *conn,
          
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS)
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)
        - 	if (!init_ssl_ctx(ctx, new_dom)) {
        - 		/* Init SSL failed */
        - 		if (error != NULL) {
        -@@ -21947,7 +22081,7 @@ mg_check_feature(unsigned feature)
        - #if !defined(NO_FILES)
        - 	                                    | MG_FEATURES_FILES
        - #endif
        --#if !defined(NO_SSL) || defined(USE_MBEDTLS)
        -+#if !defined(NO_SSL) || defined(USE_MBEDTLS) || defined(USE_GNUTLS)
        - 	                                    | MG_FEATURES_SSL
        - #endif
        - #if !defined(NO_CGI)
        -diff --git a/src/mod_gnutls.inl b/src/mod_gnutls.inl
        -new file mode 100644
        -index 00000000..b4ca5d47
        ---- /dev/null
        -+++ b/src/mod_gnutls.inl
        -@@ -0,0 +1,240 @@
        -+#if defined(USE_GNUTLS) // USE_GNUTLS used with NO_SSL
        -+
        -+#include 
        -+#include 
        -+
        -+typedef struct {
        -+	gnutls_session_t sess;
        -+} SSL;
        -+typedef struct {
        -+	gnutls_certificate_credentials_t cred;
        -+	gnutls_priority_t prio;
        -+} SSL_CTX;
        -+
        -+
        -+/* public api */
        -+CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt);
        -+CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx);
        -+CIVETWEB_API void gtls_ssl_close(SSL *ssl);
        -+CIVETWEB_API int gtls_ssl_accept(SSL **ssl,
        -+                    SSL_CTX *ssl_ctx,
        -+                    int sock,
        -+                    struct mg_context *phys_ctx);
        -+CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len);
        -+CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len);
        -+
        -+
        -+CIVETWEB_API int
        -+gtls_sslctx_init(SSL_CTX *ctx, const char *crt)
        -+{
        -+	int rc;
        -+
        -+	if (ctx == NULL || crt == NULL) {
        -+		return -1;
        -+	}
        -+
        -+	DEBUG_TRACE("%s", "Initializing GnuTLS SSL");
        -+
        -+	rc = gnutls_certificate_allocate_credentials(&ctx->cred);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("Failed to allocate credentials (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_priority_init(&ctx->prio, NULL, NULL);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("Failed to allocate priority cache (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_certificate_set_x509_key_file2(ctx->cred,
        -+	                                           crt,
        -+	                                           crt,
        -+	                                           GNUTLS_X509_FMT_PEM,
        -+	                                           NULL,
        -+	                                           GNUTLS_PKCS_PLAIN
        -+	                                               | GNUTLS_PKCS_NULL_PASSWORD);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("TLS parse crt/key file failed (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	return 0;
        -+
        -+failed:
        -+	gtls_sslctx_uninit(ctx);
        -+
        -+	return -1;
        -+}
        -+
        -+
        -+CIVETWEB_API void
        -+gtls_sslctx_uninit(SSL_CTX *ctx)
        -+{
        -+	if (ctx != NULL) {
        -+		gnutls_certificate_free_credentials(ctx->cred);
        -+		gnutls_priority_deinit(ctx->prio);
        -+		ctx->cred = NULL;
        -+		ctx->prio = NULL;
        -+	}
        -+}
        -+
        + 	return found;
        + }
         +
        -+CIVETWEB_API int
        -+gtls_ssl_accept(SSL **ssl,
        -+                SSL_CTX *ssl_ctx,
        -+                int sock,
        -+                struct mg_context *phys_ctx)
        ++/* Same as above, except if the first try fails and a fallback-root is
        ++ * configured, we'll try there also */
        ++static int
        ++substitute_index_file(struct mg_connection *conn,
        ++                      char *path,
        ++                      size_t path_len,
        ++                      struct mg_file_stat *filestat)
         +{
        -+	int rc;
        -+
        -+	if (ssl == NULL || ssl_ctx == NULL) {
        -+		return -1;
        -+	}
        -+
        -+	DEBUG_TRACE("TLS accept processing %p", ssl);
        -+
        -+	*ssl = (SSL *)mg_calloc_ctx(1, sizeof(SSL), phys_ctx);
        -+	if (*ssl == NULL) {
        -+		DEBUG_TRACE("Failed to allocate memory for session %zu", sizeof(SSL));
        -+		return -1;
        -+	}
        -+
        -+	rc = gnutls_init(&(*ssl)->sess, GNUTLS_SERVER);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("Failed to initialize session (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_priority_set((*ssl)->sess, ssl_ctx->prio);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("TLS set priortities failed (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_credentials_set((*ssl)->sess,
        -+	                            GNUTLS_CRD_CERTIFICATE,
        -+	                            ssl_ctx->cred);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("TLS set credentials failed (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        ++	int ret = substitute_index_file_aux(conn, path, path_len, filestat);
        ++	if (ret == 0) {
        ++		const char *root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT];
        ++		const char *fallback_root_prefix =
        ++		    conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT];
        ++		if ((root_prefix) && (fallback_root_prefix)) {
        ++			const size_t root_prefix_len = strlen(root_prefix);
        ++			if ((strncmp(path, root_prefix, root_prefix_len) == 0)) {
        ++				char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
        ++				                                  side effects if we fail */
        ++				size_t sub_path_len;
         +
        -+	gnutls_certificate_send_x509_rdn_sequence((*ssl)->sess, 1);
        -+	gnutls_certificate_server_set_request((*ssl)->sess, GNUTLS_CERT_IGNORE);
        -+	gnutls_handshake_set_timeout((*ssl)->sess,
        -+	                             GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
        -+	gnutls_transport_set_int((*ssl)->sess, sock);
        ++				const size_t fallback_root_prefix_len =
        ++				    strlen(fallback_root_prefix);
        ++				const char *sub_path = path + root_prefix_len;
        ++				while (*sub_path == '/') {
        ++					sub_path++;
        ++				}
        ++				sub_path_len = strlen(sub_path);
         +
        -+	while ((rc = gnutls_handshake((*ssl)->sess)) != GNUTLS_E_SUCCESS) {
        -+		if (gnutls_error_is_fatal(rc)) {
        -+			if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
        -+				DEBUG_TRACE("TLS fatal alert received: %s",
        -+				            gnutls_alert_get_name(
        -+				                gnutls_alert_get((*ssl)->sess)));
        -+			} else {
        -+				DEBUG_TRACE("TLS handshake failed (%d): %s",
        -+				            rc,
        -+				            gnutls_strerror(rc));
        ++				if (((fallback_root_prefix_len + 1 + sub_path_len + 1)
        ++				     < sizeof(scratch_path))) {
        ++					/* The concatenations below are all safe because we
        ++					 * pre-verified string lengths above */
        ++					char *nul;
        ++					strcpy(scratch_path, fallback_root_prefix);
        ++					nul = strchr(scratch_path, '\0');
        ++					if ((nul > scratch_path) && (*(nul - 1) != '/')) {
        ++						*nul++ = '/';
        ++						*nul = '\0';
        ++					}
        ++					strcat(scratch_path, sub_path);
        ++					if (substitute_index_file_aux(conn,
        ++					                              scratch_path,
        ++					                              sizeof(scratch_path),
        ++					                              filestat)) {
        ++						mg_strlcpy(path, scratch_path, path_len);
        ++						return 1;
        ++					}
        ++				}
         +			}
        -+
        -+			goto failed;
         +		}
         +	}
        -+
        -+	DEBUG_TRACE("TLS connection %p accepted", *ssl);
        -+
        -+	return 0;
        -+
        -+failed:
        -+	gnutls_deinit((*ssl)->sess);
        -+	mg_free(*ssl);
        -+	*ssl = NULL;
        -+
        -+	return -1;
        ++	return ret;
         +}
         +
        -+
        -+CIVETWEB_API void
        -+gtls_ssl_close(SSL *ssl)
        -+{
        -+	int rc;
        -+
        -+	if (ssl == NULL) {
        -+		return;
        -+	}
        -+
        -+	while ((rc = gnutls_bye(ssl->sess, GNUTLS_SHUT_RDWR)) != GNUTLS_E_SUCCESS) {
        -+		switch (rc) {
        -+		case GNUTLS_E_AGAIN: /* fall through */
        -+		case GNUTLS_E_INTERRUPTED:
        -+			continue;
        -+		default: /* should actually never happen */
        -+			break;
        + #endif
        + 
        + 
        +@@ -7635,12 +7930,16 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        + 
        + #if !defined(NO_FILES)
        + 	const char *uri = conn->request_info.local_uri;
        +-	const char *root = conn->dom_ctx->config[DOCUMENT_ROOT];
        ++	const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT],
        ++	                       conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT],
        ++	                       NULL};
        ++	int fileExists = 0;
        + 	const char *rewrite;
        + 	struct vec a, b;
        + 	ptrdiff_t match_len;
        + 	char gz_path[UTF8_PATH_MAX];
        + 	int truncated;
        ++	int i;
        + #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
        + 	char *tmp_str;
        + 	size_t tmp_str_len, sep_pos;
        +@@ -7670,7 +7969,8 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        + 	*is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
        + #if !defined(NO_FILES)
        + 	if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
        +-		root = conn->dom_ctx->config[WEBSOCKET_ROOT];
        ++		roots[0] = conn->dom_ctx->config[WEBSOCKET_ROOT];
        ++		roots[1] = conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT];
        + 	}
        + #endif /* !NO_FILES */
        + #else  /* USE_WEBSOCKET */
        +@@ -7687,51 +7987,63 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        + 
        + #if !defined(NO_FILES)
        + 	/* Step 5: If there is no root directory, don't look for files. */
        +-	/* Note that root == NULL is a regular use case here. This occurs,
        ++	/* Note that roots[0] == NULL is a regular use case here. This occurs,
        + 	 * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
        + 	 * config is not required. */
        +-	if (root == NULL) {
        ++	if (roots[0] == NULL) {
        + 		/* all file related outputs have already been set to 0, just return
        + 		 */
        + 		return;
        + 	}
        + 
        +-	/* Step 6: Determine the local file path from the root path and the
        +-	 * request uri. */
        +-	/* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
        +-	 * part of the path one byte on the right. */
        +-	truncated = 0;
        +-	mg_snprintf(
        +-	    conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
        ++	for (i = 0; roots[i] != NULL; i++) {
        ++		/* Step 6: Determine the local file path from the root path and the
        ++		 * request uri. */
        ++		/* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
        ++		 * part of the path one byte on the right. */
        ++		truncated = 0;
        ++		mg_snprintf(conn,
        ++		            &truncated,
        ++		            filename,
        ++		            filename_buf_len - 1,
        ++		            "%s%s",
        ++		            roots[i],
        ++		            uri);
        + 
        +-	if (truncated) {
        +-		goto interpret_cleanup;
        +-	}
        ++		if (truncated) {
        ++			goto interpret_cleanup;
         +		}
        -+	}
        -+
        -+	DEBUG_TRACE("TLS connection %p closed", ssl);
        -+	gnutls_deinit(ssl->sess);
        -+	mg_free(ssl);
        -+}
        -+
        -+
        -+CIVETWEB_API int
        -+gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len)
        -+{
        -+	ssize_t rc;
        -+
        -+	if (ssl == NULL) {
        -+		return GNUTLS_E_INVALID_SESSION;
        -+	}
        -+
        -+	while ((rc = gnutls_record_recv(ssl->sess, buf, len)) < 0) {
        -+		switch (rc) {
        -+		case GNUTLS_E_AGAIN: /* fall through */
        -+		case GNUTLS_E_INTERRUPTED:
        -+			continue;
        -+		default:
        -+			break;
        + 
        +-	/* Step 7: URI rewriting */
        +-	rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
        +-	while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
        +-		if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
        +-			mg_snprintf(conn,
        +-			            &truncated,
        +-			            filename,
        +-			            filename_buf_len - 1,
        +-			            "%.*s%s",
        +-			            (int)b.len,
        +-			            b.ptr,
        +-			            uri + match_len);
        +-			break;
        ++		/* Step 7: URI rewriting */
        ++		rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
        ++		while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
        ++			if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
        ++				mg_snprintf(conn,
        ++				            &truncated,
        ++				            filename,
        ++				            filename_buf_len - 1,
        ++				            "%.*s%s",
        ++				            (int)b.len,
        ++				            b.ptr,
        ++				            uri + match_len);
        ++				break;
        ++			}
        + 		}
        +-	}
        + 
        +-	if (truncated) {
        +-		goto interpret_cleanup;
        ++		if (truncated) {
        ++			goto interpret_cleanup;
         +		}
        -+	}
        -+	/* DEBUG_TRACE("gnutls_record_recv: %d", rc); */
        -+	return (int)rc;
        -+}
        -+
        -+
        -+CIVETWEB_API int
        -+gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len)
        -+{
        -+	ssize_t rc;
        -+
        -+	if (ssl == NULL) {
        -+		return GNUTLS_E_INVALID_SESSION;
        -+	}
         +
        -+	while ((rc = gnutls_record_send(ssl->sess, buf, len)) < 0) {
        -+		switch (rc) {
        -+		case GNUTLS_E_AGAIN: /* fall through */
        -+		case GNUTLS_E_INTERRUPTED:
        -+			continue;
        -+		default:
        ++		/* Step 8: Check if the file exists at the server */
        ++		/* Local file path and name, corresponding to requested URI
        ++		 * is now stored in "filename" variable. */
        ++		if (mg_stat(conn, filename, filestat)) {
        ++			fileExists = 1;
         +			break;
         +		}
        -+	}
        -+	/* DEBUG_TRACE("gnutls_record_send: %d", rc); */
        -+	return (int)rc;
        -+}
        -+
        -+#endif /* USE_GNUTLS */
        --- 
        -2.34.1
        -
        -
        -From e92dbe12bfe0ff449a2f5a2f1984bb2c2dfb14bc Mon Sep 17 00:00:00 2001
        -From: DL6ER 
        -Date: Sun, 12 Jan 2025 19:42:07 +0100
        -Subject: [PATCH 119/173] Expose is_optional property of listining_ports via
        - mg_get_server_ports()
        -
        -Signed-off-by: DL6ER 
        ----
        - docs/api/mg_server_port.md |  2 +-
        - include/civetweb.h         |  2 +-
        - src/civetweb.c             |  1 +
        - unittest/public_server.c   | 16 ++++++++++++++--
        - 4 files changed, 17 insertions(+), 4 deletions(-)
        -
        -diff --git a/docs/api/mg_server_port.md b/docs/api/mg_server_port.md
        -index 072b2979..01c9c42c 100644
        ---- a/docs/api/mg_server_port.md
        -+++ b/docs/api/mg_server_port.md
        -@@ -10,7 +10,7 @@
        - |**`port`**|`int`|The port number on which the service listens|
        - |**`is_ssl`**|`int`|**0** for `HTTP` communication, **1** for `HTTPS`|
        - |**`is_redirect`**|`int`|**1** if all requests are redirected, otherwise **0**|
        --|**`_reserved1`**|`int`|Reserved for internal use|
        -+|**`is_optional`**|`int`|**1** if prot is optional, otherwise **0**|
        - |**`_reserved2`**|`int`|Reserved for internal use|
        - |**`_reserved3`**|`int`|Reserved for internal use|
        - |**`_reserved4`**|`int`|Reserved for internal use|
        -diff --git a/include/civetweb.h b/include/civetweb.h
        -index 5ae6a0c7..0b72eaf3 100644
        ---- a/include/civetweb.h
        -+++ b/include/civetweb.h
        -@@ -714,7 +714,7 @@ struct mg_server_port {
        - 	int port;        /* port number */
        - 	int is_ssl;      /* https port: 0 = no, 1 = yes */
        - 	int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
        --	int _reserved1;
        -+	int is_optional; /* optional: 0 = no, 1 = yes */
        - 	int _reserved2;
        - 	int _reserved3;
        - 	int _reserved4;
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 814e1de0..9f9ef2e8 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -3337,6 +3337,7 @@ mg_get_server_ports(const struct mg_context *ctx,
        - 		    ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
        - 		ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
        - 		ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
        -+		ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
        + 	}
        + 
        +-	/* Step 8: Check if the file exists at the server */
        +-	/* Local file path and name, corresponding to requested URI
        +-	 * is now stored in "filename" variable. */
        +-	if (mg_stat(conn, filename, filestat)) {
        ++	if (fileExists) {
        + 		int uri_len = (int)strlen(uri);
        + 		int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/');
        + 
        +@@ -8227,6 +8539,9 @@ static const struct {
        +      * (http://www.iana.org/assignments/media-types)
        +      * application types */
        +     {".bin", 4, "application/octet-stream"},
        ++    {".cer", 4, "application/pkix-cert"},
        ++    {".crl", 4, "application/pkix-crl"},
        ++    {".crt", 4, "application/pkix-cert"},
        +     {".deb", 4, "application/octet-stream"},
        +     {".dmg", 4, "application/octet-stream"},
        +     {".dll", 4, "application/octet-stream"},
        +@@ -8236,10 +8551,13 @@ static const struct {
        +     {".iso", 4, "application/octet-stream"},
        +     {".js", 3, "application/javascript"},
        +     {".json", 5, "application/json"},
        ++    {".mjs", 4, "application/javascript"},
        +     {".msi", 4, "application/octet-stream"},
        ++    {".pem", 4, "application/x-pem-file"},
        +     {".pdf", 4, "application/pdf"},
        +     {".ps", 3, "application/postscript"},
        +     {".rtf", 4, "application/rtf"},
        ++    {".wasm", 5, "application/wasm"},
        +     {".xhtml", 6, "application/xhtml+xml"},
        +     {".xsl", 4, "application/xml"},
        +     {".xslt", 5, "application/xml"},
        +@@ -8542,7 +8860,7 @@ open_auth_file(struct mg_connection *conn,
        + 
        + 
        + /* Parsed Authorization header */
        +-struct ah {
        ++struct auth_header {
        + 	char *user;
        + 	int type;             /* 1 = basic, 2 = digest */
        + 	char *plain_password; /* Basic only */
        +@@ -8550,32 +8868,32 @@ struct ah {
        + };
        + 
        + 
        +-/* Return 1 on success. Always initializes the ah structure. */
        ++/* Return 1 on success. Always initializes the auth_header structure. */
        + static int
        + parse_auth_header(struct mg_connection *conn,
        +                   char *buf,
        +                   size_t buf_size,
        +-                  struct ah *ah)
        ++                  struct auth_header *auth_header)
        + {
        + 	char *name, *value, *s;
        +-	const char *auth_header;
        ++	const char *ah;
        + 	uint64_t nonce;
          
        - 		if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
        - 			/* IPv4 */
        -diff --git a/unittest/public_server.c b/unittest/public_server.c
        -index 4983ee8c..bad2b77f 100644
        ---- a/unittest/public_server.c
        -+++ b/unittest/public_server.c
        -@@ -457,10 +457,12 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound)
        - 	ck_assert_int_eq(portinfo[0].port, 0);
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[0].is_optional, 0);
        - 	ck_assert_int_eq(portinfo[1].protocol, 0);
        - 	ck_assert_int_eq(portinfo[1].port, 0);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[1].is_optional, 0);
        +-	if (!ah || !conn) {
        ++	if (!auth_header || !conn) {
        + 		return 0;
        + 	}
          
        - 	ret = mg_get_server_ports(ctx, 4, portinfo);
        - 	ck_assert_int_eq(ret, 1);
        -@@ -472,10 +474,12 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound)
        - 	ck_assert_int_eq(portinfo[0].port, 8080);
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[0].is_optional, 0);
        - 	ck_assert_int_eq(portinfo[1].protocol, 0);
        - 	ck_assert_int_eq(portinfo[1].port, 0);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[1].is_optional, 0);
        +-	(void)memset(ah, 0, sizeof(*ah));
        +-	auth_header = mg_get_header(conn, "Authorization");
        ++	(void)memset(auth_header, 0, sizeof(*auth_header));
        ++	ah = mg_get_header(conn, "Authorization");
          
        - 	test_sleep(1);
        +-	if (auth_header == NULL) {
        ++	if (ah == NULL) {
        + 		/* No Authorization header at all */
        + 		return 0;
        + 	}
        +-	if (0 == mg_strncasecmp(auth_header, "Basic ", 6)) {
        ++	if (0 == mg_strncasecmp(ah, "Basic ", 6)) {
        + 		/* Basic Auth (we never asked for this, but some client may send it) */
        + 		char *split;
        +-		const char *userpw_b64 = auth_header + 6;
        ++		const char *userpw_b64 = ah + 6;
        + 		size_t userpw_b64_len = strlen(userpw_b64);
        + 		size_t buf_len_r = buf_size;
        + 		if (mg_base64_decode(
        +@@ -8592,15 +8910,15 @@ parse_auth_header(struct mg_connection *conn,
        + 		*split = 0;
          
        -@@ -649,7 +653,7 @@ START_TEST(test_mg_start_stop_https_server)
        - 	OPTIONS[opt_idx++] = ".";
        - #endif
        - 	OPTIONS[opt_idx++] = "listening_ports";
        --	OPTIONS[opt_idx++] = "8080r,8443s";
        -+	OPTIONS[opt_idx++] = "8080r,8443os";
        - 	OPTIONS[opt_idx++] = "ssl_certificate";
        - 	OPTIONS[opt_idx++] = ssl_cert;
        + 		/* User name is before ':', Password is after ':'  */
        +-		ah->user = buf;
        +-		ah->type = 1;
        +-		ah->plain_password = split + 1;
        ++		auth_header->user = buf;
        ++		auth_header->type = 1;
        ++		auth_header->plain_password = split + 1;
          
        -@@ -674,10 +678,12 @@ START_TEST(test_mg_start_stop_https_server)
        - 	ck_assert_int_eq(portinfo[0].port, 0);
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[0].is_optional, 0);
        - 	ck_assert_int_eq(portinfo[1].protocol, 0);
        - 	ck_assert_int_eq(portinfo[1].port, 0);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[1].is_optional, 0);
        + 		return 1;
          
        - 	ret = mg_get_server_ports(ctx, 4, portinfo);
        - 	ck_assert_int_eq(ret, 2);
        -@@ -685,14 +691,17 @@ START_TEST(test_mg_start_stop_https_server)
        - 	ck_assert_int_eq(portinfo[0].port, 8080);
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 1);
        -+	ck_assert_int_eq(portinfo[0].is_optional, 0);
        - 	ck_assert_int_eq(portinfo[1].protocol, 1);
        - 	ck_assert_int_eq(portinfo[1].port, 8443);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 1);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[1].is_optional, 1);
        - 	ck_assert_int_eq(portinfo[2].protocol, 0);
        - 	ck_assert_int_eq(portinfo[2].port, 0);
        - 	ck_assert_int_eq(portinfo[2].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[2].is_redirect, 0);
        -+	ck_assert_int_eq(portinfo[2].is_optional, 0);
        +-	} else if (0 == mg_strncasecmp(auth_header, "Digest ", 7)) {
        ++	} else if (0 == mg_strncasecmp(ah, "Digest ", 7)) {
        + 		/* Digest Auth ... implemented below */
        +-		ah->type = 2;
        ++		auth_header->type = 2;
          
        - 	test_sleep(1);
        + 	} else {
        + 		/* Unknown or invalid Auth method */
        +@@ -8608,7 +8926,7 @@ parse_auth_header(struct mg_connection *conn,
        + 	}
          
        -@@ -771,7 +780,7 @@ START_TEST(test_mg_server_and_client_tls)
        - 	OPTIONS[opt_idx++] = ".";
        - #endif
        - 	OPTIONS[opt_idx++] = "listening_ports";
        --	OPTIONS[opt_idx++] = "8080r,8443s";
        -+	OPTIONS[opt_idx++] = "8080r,8443os";
        - 	OPTIONS[opt_idx++] = "ssl_certificate";
        - 	OPTIONS[opt_idx++] = server_cert;
        - 	OPTIONS[opt_idx++] = "ssl_verify_peer";
        -@@ -800,14 +809,17 @@ START_TEST(test_mg_server_and_client_tls)
        - 	ck_assert_int_eq(ports[0].port, 8080);
        - 	ck_assert_int_eq(ports[0].is_ssl, 0);
        - 	ck_assert_int_eq(ports[0].is_redirect, 1);
        -+	ck_assert_int_eq(ports[0].is_optional, 0);
        - 	ck_assert_int_eq(ports[1].protocol, 1);
        - 	ck_assert_int_eq(ports[1].port, 8443);
        - 	ck_assert_int_eq(ports[1].is_ssl, 1);
        - 	ck_assert_int_eq(ports[1].is_redirect, 0);
        -+	ck_assert_int_eq(ports[1].is_optional, 1);
        - 	ck_assert_int_eq(ports[2].protocol, 0);
        - 	ck_assert_int_eq(ports[2].port, 0);
        - 	ck_assert_int_eq(ports[2].is_ssl, 0);
        - 	ck_assert_int_eq(ports[2].is_redirect, 0);
        -+	ck_assert_int_eq(ports[2].is_optional, 0);
        + 	/* Make modifiable copy of the auth header */
        +-	(void)mg_strlcpy(buf, auth_header + 7, buf_size);
        ++	(void)mg_strlcpy(buf, ah + 7, buf_size);
        + 	s = buf;
          
        - 	test_sleep(1);
        + 	/* Parse authorization header */
        +@@ -8635,29 +8953,29 @@ parse_auth_header(struct mg_connection *conn,
        + 		}
          
        --- 
        -2.34.1
        -
        -
        -From e1d7d71e0b7973006fadad5bb06af7cc2af69021 Mon Sep 17 00:00:00 2001
        -From: phi-go 
        -Date: Thu, 23 Jan 2025 10:01:42 +0100
        -Subject: [PATCH 120/173] return 0 for TestOneInput
        -
        -libFuzzer discards inputs when returning non-zero, and so gets stuck for
        -some targets
        ----
        - fuzztest/fuzzmain.c | 15 ++++++++++-----
        - 1 file changed, 10 insertions(+), 5 deletions(-)
        -
        -diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c
        -index e8226de2..9be1bb2d 100644
        ---- a/fuzztest/fuzzmain.c
        -+++ b/fuzztest/fuzzmain.c
        -@@ -497,21 +497,26 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
        - {
        - #if defined(TEST_FUZZ1)
        - 	/* fuzz target 1: different URI for HTTP/1 server */
        --	return LLVMFuzzerTestOneInput_URI(data, size);
        -+	LLVMFuzzerTestOneInput_URI(data, size);
        -+  return 0;
        - #elif defined(TEST_FUZZ2)
        - 	/* fuzz target 2: different requests for HTTP/1 server */
        --	return LLVMFuzzerTestOneInput_REQUEST(data, size);
        -+	LLVMFuzzerTestOneInput_REQUEST(data, size);
        -+  return 0;
        - #elif defined(TEST_FUZZ3)
        - 	/* fuzz target 3: different responses for HTTP/1 client */
        --	return LLVMFuzzerTestOneInput_RESPONSE(data, size);
        -+	LLVMFuzzerTestOneInput_RESPONSE(data, size);
        -+  return 0;
        - #elif defined(TEST_FUZZ4)
        - #error "Only useful in HTTP/2 feature branch"
        - 	/* fuzz target 4: different requests for HTTP/2 server */
        --	return LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
        -+	LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
        -+  return 0;
        - #elif defined(TEST_FUZZ5)
        - 	/* fuzz target 5: calling an internal server test function,
        - 	 *                bypassing network sockets */
        --	return LLVMFuzzerTestOneInput_process_new_connection(data, size);
        -+	LLVMFuzzerTestOneInput_process_new_connection(data, size);
        -+  return 0;
        - #else
        - /* planned targets */
        - #error "Unknown fuzz target"
        --- 
        -2.34.1
        -
        -
        -From 413387fcd813cb3cc77c9d6f4f9e9fbcaad44f98 Mon Sep 17 00:00:00 2001
        -From: phi-go 
        -Date: Thu, 23 Jan 2025 10:49:03 +0100
        -Subject: [PATCH 121/173] formatting
        -
        ----
        - fuzztest/fuzzmain.c | 10 +++++-----
        - 1 file changed, 5 insertions(+), 5 deletions(-)
        -
        -diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c
        -index 9be1bb2d..89424656 100644
        ---- a/fuzztest/fuzzmain.c
        -+++ b/fuzztest/fuzzmain.c
        -@@ -498,25 +498,25 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
        - #if defined(TEST_FUZZ1)
        - 	/* fuzz target 1: different URI for HTTP/1 server */
        - 	LLVMFuzzerTestOneInput_URI(data, size);
        --  return 0;
        -+	return 0;
        - #elif defined(TEST_FUZZ2)
        - 	/* fuzz target 2: different requests for HTTP/1 server */
        - 	LLVMFuzzerTestOneInput_REQUEST(data, size);
        --  return 0;
        -+	return 0;
        - #elif defined(TEST_FUZZ3)
        - 	/* fuzz target 3: different responses for HTTP/1 client */
        - 	LLVMFuzzerTestOneInput_RESPONSE(data, size);
        --  return 0;
        -+	return 0;
        - #elif defined(TEST_FUZZ4)
        - #error "Only useful in HTTP/2 feature branch"
        - 	/* fuzz target 4: different requests for HTTP/2 server */
        - 	LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
        --  return 0;
        -+	return 0;
        - #elif defined(TEST_FUZZ5)
        - 	/* fuzz target 5: calling an internal server test function,
        - 	 *                bypassing network sockets */
        - 	LLVMFuzzerTestOneInput_process_new_connection(data, size);
        --  return 0;
        -+	return 0;
        - #else
        - /* planned targets */
        - #error "Unknown fuzz target"
        --- 
        -2.34.1
        -
        -
        -From b2c1d1bee777a13a7feb8a8d123b6552e3bd9fa7 Mon Sep 17 00:00:00 2001
        -From: phi-go 
        -Date: Thu, 23 Jan 2025 11:57:59 +0100
        -Subject: [PATCH 122/173] use LLVMFuzzerInitialize
        -
        ----
        - fuzztest/fuzzmain.c | 45 ++++++++++++++++++++++++---------------------
        - 1 file changed, 24 insertions(+), 21 deletions(-)
        -
        -diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c
        -index 89424656..df26768c 100644
        ---- a/fuzztest/fuzzmain.c
        -+++ b/fuzztest/fuzzmain.c
        -@@ -45,9 +45,6 @@ unsigned short PORT_NUM_HTTP = 0; /* set dynamically */
        + 		if (!strcmp(name, "username")) {
        +-			ah->user = value;
        ++			auth_header->user = value;
        + 		} else if (!strcmp(name, "cnonce")) {
        +-			ah->cnonce = value;
        ++			auth_header->cnonce = value;
        + 		} else if (!strcmp(name, "response")) {
        +-			ah->response = value;
        ++			auth_header->response = value;
        + 		} else if (!strcmp(name, "uri")) {
        +-			ah->uri = value;
        ++			auth_header->uri = value;
        + 		} else if (!strcmp(name, "qop")) {
        +-			ah->qop = value;
        ++			auth_header->qop = value;
        + 		} else if (!strcmp(name, "nc")) {
        +-			ah->nc = value;
        ++			auth_header->nc = value;
        + 		} else if (!strcmp(name, "nonce")) {
        +-			ah->nonce = value;
        ++			auth_header->nonce = value;
        + 		}
          	}
          
        + #if !defined(NO_NONCE_CHECK)
        + 	/* Read the nonce from the response. */
        +-	if (ah->nonce == NULL) {
        ++	if (auth_header->nonce == NULL) {
        + 		return 0;
        + 	}
        + 	s = NULL;
        +-	nonce = strtoull(ah->nonce, &s, 10);
        ++	nonce = strtoull(auth_header->nonce, &s, 10);
        + 	if ((s == NULL) || (*s != 0)) {
        + 		return 0;
        + 	}
        +@@ -8688,7 +9006,7 @@ parse_auth_header(struct mg_connection *conn,
        + 	(void)nonce;
        + #endif
          
        --static uint64_t call_count = 0;
        --
        --
        - /********************************************************/
        - /* Init CivetWeb server ... test with mock client       */
        - /********************************************************/
        -@@ -110,6 +107,17 @@ civetweb_init(void)
        - 	atexit(civetweb_exit);
        +-	return (ah->user != NULL);
        ++	return (auth_header->user != NULL);
          }
          
        -+int LLVMFuzzerInitialize(int *argc, char ***argv);
        + 
        +@@ -8720,7 +9038,7 @@ mg_fgets(char *buf, size_t size, struct mg_file *filep)
        + #if !defined(NO_FILESYSTEMS)
        + struct read_auth_file_struct {
        + 	struct mg_connection *conn;
        +-	struct ah ah;
        ++	struct auth_header auth_header;
        + 	const char *domain;
        + 	char buf[256 + 256 + 40];
        + 	const char *f_user;
        +@@ -8821,9 +9139,9 @@ read_auth_file(struct mg_file *filep,
        + 		*(char *)(workdata->f_ha1) = 0;
        + 		(workdata->f_ha1)++;
        + 
        +-		if (!strcmp(workdata->ah.user, workdata->f_user)
        ++		if (!strcmp(workdata->auth_header.user, workdata->f_user)
        + 		    && !strcmp(workdata->domain, workdata->f_domain)) {
        +-			switch (workdata->ah.type) {
        ++			switch (workdata->auth_header.type) {
        + 			case 1: /* Basic */
        + 			{
        + 				char md5[33];
        +@@ -8832,7 +9150,7 @@ read_auth_file(struct mg_file *filep,
        + 				       ":",
        + 				       workdata->domain,
        + 				       ":",
        +-				       workdata->ah.plain_password,
        ++				       workdata->auth_header.plain_password,
        + 				       NULL);
        + 				return 0 == memcmp(workdata->f_ha1, md5, 33);
        + 			}
        +@@ -8840,12 +9158,12 @@ read_auth_file(struct mg_file *filep,
        + 				return check_password_digest(
        + 				    workdata->conn->request_info.request_method,
        + 				    workdata->f_ha1,
        +-				    workdata->ah.uri,
        +-				    workdata->ah.nonce,
        +-				    workdata->ah.nc,
        +-				    workdata->ah.cnonce,
        +-				    workdata->ah.qop,
        +-				    workdata->ah.response);
        ++				    workdata->auth_header.uri,
        ++				    workdata->auth_header.nonce,
        ++				    workdata->auth_header.nc,
        ++				    workdata->auth_header.cnonce,
        ++				    workdata->auth_header.qop,
        ++				    workdata->auth_header.response);
        + 			default: /* None/Other/Unknown */
        + 				return 0;
        + 			}
        +@@ -8870,13 +9188,13 @@ authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
        + 	memset(&workdata, 0, sizeof(workdata));
        + 	workdata.conn = conn;
        + 
        +-	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
        ++	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.auth_header)) {
        + 		return 0;
        + 	}
        + 
        + 	/* CGI needs it as REMOTE_USER */
        + 	conn->request_info.remote_user =
        +-	    mg_strdup_ctx(workdata.ah.user, conn->phys_ctx);
        ++	    mg_strdup_ctx(workdata.auth_header.user, conn->phys_ctx);
        + 
        + 	if (realm) {
        + 		workdata.domain = realm;
        +@@ -9344,7 +9662,8 @@ connect_socket(
        + 		return 0;
        + 	}
        + 
        +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL)
        ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)          \
        ++    && !defined(NO_SSL_DL)
        + #if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
        + 	if (use_ssl && (TLS_client_method == NULL)) {
        + 		if (error != NULL) {
        +@@ -9511,11 +9830,11 @@ connect_socket(
        + #endif
        + 
        + 		/* Data for poll */
        +-		struct mg_pollfd pfd[1];
        ++		struct mg_pollfd pfd[2];
        + 		int pollres;
        +-		int ms_wait = 10000; /* 10 second timeout */
        +-		stop_flag_t nonstop;
        +-		STOP_FLAG_ASSIGN(&nonstop, 0);
        ++		int ms_wait = 10000;       /* 10 second timeout */
        ++		stop_flag_t nonstop = 0;   /* STOP_FLAG_ASSIGN(&nonstop, 0); */
        ++		unsigned int num_sock = 1; /* use one or two sockets */
        + 
        + 		/* For a non-blocking socket, the connect sequence is:
        + 		 * 1) call connect (will not block)
        +@@ -9524,7 +9843,15 @@ connect_socket(
        + 		 */
        + 		pfd[0].fd = *sock;
        + 		pfd[0].events = POLLOUT;
        +-		pollres = mg_poll(pfd, 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
         +
        -+int
        -+LLVMFuzzerInitialize(int *argc, char ***argv) {
        -+  // Silence unused args warning.
        -+	(void)(argc);
        -+	(void)(argv);
        ++		if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
        ++			pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
        ++			pfd[num_sock].events = POLLIN;
        ++			num_sock++;
        ++		}
         +
        -+	civetweb_init();
        -+  return 0;
        -+}
        ++		pollres =
        ++		    mg_poll(pfd, num_sock, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
          
        - #if defined(TEST_FUZZ1)
        - static int
        -@@ -202,19 +210,12 @@ test_civetweb_client(const char *server,
        - 	return 0;
        - }
        + 		if (pollres != 1) {
        + 			/* Not connected */
        +@@ -10145,8 +10472,11 @@ send_file_data(struct mg_connection *conn,
        + 				}
          
        --
        - static int
        - LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
        - {
        - 	static char URI[1024 * 64]; /* static, to avoid stack overflow */
        + 				/* Read from file, exit the loop on error */
        +-				if ((num_read =
        +-				         (int)fread(buf, 1, (size_t)to_read, filep->access.fp))
        ++				if ((num_read = pull_inner(filep->access.fp,
        ++				                           NULL,
        ++				                           buf,
        ++				                           to_read,
        ++				                           /* unused */ 0.0))
        + 				    <= 0) {
        + 					break;
        + 				}
        +@@ -10473,9 +10803,13 @@ is_not_modified(const struct mg_connection *conn,
        + 	const char *inm = mg_get_header(conn, "If-None-Match");
        + 	construct_etag(etag, sizeof(etag), filestat);
          
        --	if (call_count == 0) {
        --		memset(URI, 0, sizeof(URI));
        --		civetweb_init();
        --	}
        --	call_count++;
        --
        --	if (size < sizeof(URI)) {
        -+	if (size+1 < sizeof(URI)) {
        - 		memcpy(URI, data, size);
        - 		URI[size] = 0;
        - 	} else {
        -@@ -230,11 +231,6 @@ LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
        - static int
        - LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
        - {
        --	if (call_count == 0) {
        --		civetweb_init();
        --	}
        --	call_count++;
        --
        - 	int r;
        - 	SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);
        - 	if (sock == -1) {
        -@@ -446,15 +442,22 @@ mock_server_init(void)
        - 	atexit(mock_server_exit);
        +-	return ((inm != NULL) && !mg_strcasecmp(etag, inm))
        +-	       || ((ims != NULL)
        +-	           && (filestat->last_modified <= parse_date_string(ims)));
        ++	if (inm) {
        ++		return !mg_strcasecmp(etag, inm);
        ++	}
        ++	if (ims) {
        ++		return (filestat->last_modified <= parse_date_string(ims));
        ++	}
        ++	return 0;
          }
          
        -+int LLVMFuzzerInitialize(int *argc, char ***argv);
        -+
        -+int
        -+LLVMFuzzerInitialize(int *argc, char ***argv) {
        -+  // Silence unused args warning.
        -+	(void)(argc);
        -+	(void)(argv);
        -+
        -+	mock_server_init();
        -+  return 0;
        -+}
        -+
          
        - static int
        - LLVMFuzzerTestOneInput_RESPONSE(const uint8_t *data, size_t size)
        +@@ -10680,8 +11014,9 @@ static int
        + skip_to_end_of_word_and_terminate(char **ppw, int eol)
          {
        --	if (call_count == 0) {
        --		mock_server_init();
        --	}
        --	call_count++;
        --
        - 	if (size > sizeof(RESPONSE.data)) {
        - 		return 1;
        + 	/* Forward until a space is found - use isgraph here */
        ++	/* Extended ASCII characters are also treated as word characters. */
        + 	/* See http://www.cplusplus.com/reference/cctype/ */
        +-	while (isgraph((unsigned char)**ppw)) {
        ++	while ((unsigned char)**ppw > 127 || isgraph((unsigned char)**ppw)) {
        + 		(*ppw)++;
          	}
        --- 
        -2.34.1
        -
        -
        -From dfdb047498dd4c79aa5f7b1cc2d6fef0c5b26707 Mon Sep 17 00:00:00 2001
        -From: DL6ER 
        -Date: Mon, 10 Feb 2025 18:21:51 +0100
        -Subject: [PATCH 123/173] Expose if server ports are bound
        -
        -Signed-off-by: DL6ER 
        ----
        - docs/api/mg_server_port.md |  4 ++--
        - include/civetweb.h         |  2 +-
        - src/civetweb.c             |  1 +
        - unittest/public_server.c   | 12 ++++++++++++
        - 4 files changed, 16 insertions(+), 3 deletions(-)
        -
        -diff --git a/docs/api/mg_server_port.md b/docs/api/mg_server_port.md
        -index 01c9c42c..0159be8d 100644
        ---- a/docs/api/mg_server_port.md
        -+++ b/docs/api/mg_server_port.md
        -@@ -10,8 +10,8 @@
        - |**`port`**|`int`|The port number on which the service listens|
        - |**`is_ssl`**|`int`|**0** for `HTTP` communication, **1** for `HTTPS`|
        - |**`is_redirect`**|`int`|**1** if all requests are redirected, otherwise **0**|
        --|**`is_optional`**|`int`|**1** if prot is optional, otherwise **0**|
        --|**`_reserved2`**|`int`|Reserved for internal use|
        -+|**`is_optional`**|`int`|**1** if port is optional, otherwise **0**|
        -+|**`is_bound`**|`int`|**1** if the port is bound, otherwise **0**|
        - |**`_reserved3`**|`int`|Reserved for internal use|
        - |**`_reserved4`**|`int`|Reserved for internal use|
        - 
        -diff --git a/include/civetweb.h b/include/civetweb.h
        -index 0b72eaf3..0ce344dc 100644
        ---- a/include/civetweb.h
        -+++ b/include/civetweb.h
        -@@ -715,7 +715,7 @@ struct mg_server_port {
        - 	int is_ssl;      /* https port: 0 = no, 1 = yes */
        - 	int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
        - 	int is_optional; /* optional: 0 = no, 1 = yes */
        --	int _reserved2;
        -+	int is_bound;    /* bound: 0 = no, 1 = yes, relevant for optional ports */
        - 	int _reserved3;
        - 	int _reserved4;
        - };
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 9f9ef2e8..2ee166cf 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -3338,6 +3338,7 @@ mg_get_server_ports(const struct mg_context *ctx,
        - 		ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
        - 		ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
        - 		ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
        -+		ports[cnt].is_bound = ctx->listening_sockets[i].sock != INVALID_SOCKET;
        - 
        - 		if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
        - 			/* IPv4 */
        -diff --git a/unittest/public_server.c b/unittest/public_server.c
        -index bad2b77f..3ea5c45f 100644
        ---- a/unittest/public_server.c
        -+++ b/unittest/public_server.c
        -@@ -458,11 +458,13 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound)
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[0].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[0].is_bound, 0);
        - 	ck_assert_int_eq(portinfo[1].protocol, 0);
        - 	ck_assert_int_eq(portinfo[1].port, 0);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[1].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[1].is_bound, 0);
          
        - 	ret = mg_get_server_ports(ctx, 4, portinfo);
        - 	ck_assert_int_eq(ret, 1);
        -@@ -475,11 +477,13 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound)
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[0].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[0].is_bound, 1);
        - 	ck_assert_int_eq(portinfo[1].protocol, 0);
        - 	ck_assert_int_eq(portinfo[1].port, 0);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[1].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[1].is_bound, 0);
        +@@ -10778,7 +11113,7 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
        + 		}
          
        - 	test_sleep(1);
        + 		/* here *dp is either 0 or '\n' */
        +-		/* in any case, we have a new header */
        ++		/* in any case, we have found a complete header */
        + 		num_headers = i + 1;
          
        -@@ -679,11 +683,13 @@ START_TEST(test_mg_start_stop_https_server)
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[0].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[0].is_bound, 0);
        - 	ck_assert_int_eq(portinfo[1].protocol, 0);
        - 	ck_assert_int_eq(portinfo[1].port, 0);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[1].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[1].is_bound, 0);
        + 		if (*dp) {
        +@@ -10787,9 +11122,11 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
        + 			*buf = dp;
          
        - 	ret = mg_get_server_ports(ctx, 4, portinfo);
        - 	ck_assert_int_eq(ret, 2);
        -@@ -692,16 +698,19 @@ START_TEST(test_mg_start_stop_https_server)
        - 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[0].is_redirect, 1);
        - 	ck_assert_int_eq(portinfo[0].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[0].is_bound, 1);
        - 	ck_assert_int_eq(portinfo[1].protocol, 1);
        - 	ck_assert_int_eq(portinfo[1].port, 8443);
        - 	ck_assert_int_eq(portinfo[1].is_ssl, 1);
        - 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[1].is_optional, 1);
        -+	ck_assert_int_eq(portinfo[1].is_bound, 1);
        - 	ck_assert_int_eq(portinfo[2].protocol, 0);
        - 	ck_assert_int_eq(portinfo[2].port, 0);
        - 	ck_assert_int_eq(portinfo[2].is_ssl, 0);
        - 	ck_assert_int_eq(portinfo[2].is_redirect, 0);
        - 	ck_assert_int_eq(portinfo[2].is_optional, 0);
        -+	ck_assert_int_eq(portinfo[2].is_bound, 0);
        + 			if ((dp[0] == '\r') || (dp[0] == '\n')) {
        +-				/* This is the end of the header */
        ++				/* We've had CRLF twice in a row
        ++				 * This is the end of the headers */
        + 				break;
        + 			}
        ++			/* continue within the loop, find the next header */
        + 		} else {
        + 			*buf = dp;
        + 			break;
        +@@ -11166,11 +11503,11 @@ read_message(FILE *fp,
        + 			request_len = get_http_header_len(buf, *nread);
        + 		}
          
        - 	test_sleep(1);
        +-		if ((request_len == 0) && (request_timeout >= 0)) {
        ++		if ((n <= 0) && (request_timeout >= 0)) {
        + 			if (mg_difftimespec(&last_action_time, &(conn->req_time))
        + 			    > request_timeout) {
        + 				/* Timeout */
        +-				return -1;
        ++				return -3;
        + 			}
        + 		}
        + 	}
        +@@ -11390,6 +11727,11 @@ prepare_cgi_environment(struct mg_connection *conn,
        + 	addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
        + 	addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
        + 	addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
        ++	if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) {
        ++		addenv(env,
        ++		       "FALLBACK_DOCUMENT_ROOT=%s",
        ++		       conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]);
        ++	}
        + 	addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version());
          
        -@@ -810,16 +819,19 @@ START_TEST(test_mg_server_and_client_tls)
        - 	ck_assert_int_eq(ports[0].is_ssl, 0);
        - 	ck_assert_int_eq(ports[0].is_redirect, 1);
        - 	ck_assert_int_eq(ports[0].is_optional, 0);
        -+	ck_assert_int_eq(ports[0].is_bound, 1);
        - 	ck_assert_int_eq(ports[1].protocol, 1);
        - 	ck_assert_int_eq(ports[1].port, 8443);
        - 	ck_assert_int_eq(ports[1].is_ssl, 1);
        - 	ck_assert_int_eq(ports[1].is_redirect, 0);
        - 	ck_assert_int_eq(ports[1].is_optional, 1);
        -+	ck_assert_int_eq(ports[1].is_bound, 1);
        - 	ck_assert_int_eq(ports[2].protocol, 0);
        - 	ck_assert_int_eq(ports[2].port, 0);
        - 	ck_assert_int_eq(ports[2].is_ssl, 0);
        - 	ck_assert_int_eq(ports[2].is_redirect, 0);
        - 	ck_assert_int_eq(ports[2].is_optional, 0);
        -+	ck_assert_int_eq(ports[2].is_bound, 0);
        + 	/* Prepare the environment block */
        +@@ -12808,11 +13150,14 @@ dav_lock_file(struct mg_connection *conn, const char *path)
        + 	int i;
        + 	uint64_t LOCK_DURATION_NS =
        + 	    (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000;
        +-	struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
        ++	struct twebdav_lock *dav_lock = NULL;
          
        - 	test_sleep(1);
        +-	if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
        ++	if (!path || !conn || !conn->dom_ctx || !conn->request_info.remote_user
        ++	    || !conn->phys_ctx) {
        + 		return;
        + 	}
        ++
        ++	dav_lock = conn->phys_ctx->webdav_lock;
        + 	mg_get_request_link(conn, link_buf, sizeof(link_buf));
          
        --- 
        -2.34.1
        -
        -
        -From f4d9bf25f10bf4cea4a4d345a3afc6ce92039ef2 Mon Sep 17 00:00:00 2001
        -From: DL6ER 
        -Date: Sat, 1 Mar 2025 11:50:54 +0100
        -Subject: [PATCH 124/173] Terminate master_thread early when there are no
        - listening sockets to prevent CivetWeb from crashing
        -
        -Signed-off-by: DL6ER 
        ----
        - src/civetweb.c | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 2ee166cf..edfbfd3b 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -20542,7 +20542,7 @@ master_thread_run(struct mg_context *ctx)
        - 	unsigned int i;
        - 	unsigned int workerthreadcount;
        + 	/* const char *refresh = mg_get_header(conn, "If"); */
        +@@ -13214,9 +13559,10 @@ read_websocket(struct mg_connection *conn,
        + 		if ((header_len > 0) && (body_len >= header_len)) {
        + 			/* Allocate space to hold websocket payload */
        + 			unsigned char *data = mem;
        ++			size_t required_len = (size_t)data_len + 4;
          
        --	if (!ctx) {
        -+	if (!ctx || !ctx->listening_socket_fds) {
        +-			if ((size_t)data_len > (size_t)sizeof(mem)) {
        +-				data = (unsigned char *)mg_malloc_ctx((size_t)data_len,
        ++			if (required_len > sizeof(mem)) {
        ++				data = (unsigned char *)mg_malloc_ctx(required_len,
        + 				                                      conn->phys_ctx);
        + 				if (data == NULL) {
        + 					/* Allocation failed, exit the loop and then close the
        +@@ -14758,7 +15104,7 @@ handle_request(struct mg_connection *conn)
        + 		}
          		return;
          	}
        +-	uri_len = (int)strlen(ri->local_uri);
        ++
          
        --- 
        -2.34.1
        -
        -
        -From d92557b845252f22e91cb33e74323693040df80c Mon Sep 17 00:00:00 2001
        -From: Abra Mixabra <46976171+abra-mixabra@users.noreply.github.com>
        -Date: Sun, 2 Mar 2025 18:44:35 +0000
        -Subject: [PATCH 125/173] Add X509 certificate and CRL MIME-types
        -
        -Add the following MIME types:
        -- application/pkix-cert
        -- application/pkix-crl
        -- application/x-pem-file
        ----
        - src/civetweb.c | 4 ++++
        - 1 file changed, 4 insertions(+)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 2ee166cf..30bf430c 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -8511,6 +8511,9 @@ static const struct {
        -      * (http://www.iana.org/assignments/media-types)
        -      * application types */
        -     {".bin", 4, "application/octet-stream"},
        -+    {".cer", 4, "application/pkix-cert"},
        -+    {".crl", 4, "application/pkix-crl"},
        -+    {".crt", 4, "application/pkix-cert"},
        -     {".deb", 4, "application/octet-stream"},
        -     {".dmg", 4, "application/octet-stream"},
        -     {".dll", 4, "application/octet-stream"},
        -@@ -8522,6 +8525,7 @@ static const struct {
        -     {".json", 5, "application/json"},
        -     {".mjs", 4, "application/javascript"},
        -     {".msi", 4, "application/octet-stream"},
        -+    {".pem", 4, "application/x-pem-file"},
        -     {".pdf", 4, "application/pdf"},
        -     {".ps", 3, "application/postscript"},
        -     {".rtf", 4, "application/rtf"},
        --- 
        -2.34.1
        -
        -
        -From ae42b3a7342adb957175bb227a278c1cce4d45db Mon Sep 17 00:00:00 2001
        -From: stevenwdv 
        -Date: Thu, 6 Mar 2025 11:53:40 +0100
        -Subject: [PATCH 126/173] Use `stderr` for `DEBUG_TRACE_STREAM`
        -
        -Closes #1330
        ----
        - src/civetweb.c | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 2ee166cf..c6366547 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -239,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func,
        + 	/* 1.3. decode url (if config says so) */
        + 	if (should_decode_url(conn)) {
        +@@ -14786,6 +15132,11 @@ handle_request(struct mg_connection *conn)
        + 	}
        + 	remove_dot_segments(tmp);
        + 	ri->local_uri = tmp;
        ++#if !defined(NO_FILES) /* Only compute if later code can actually use it */
        ++	/* Cache URI length once; recompute only if the buffer changes later. */
        ++	uri_len = (int)strlen(ri->local_uri);
        ++#endif
        ++
          
        - #define NEED_DEBUG_TRACE_FUNC
        - #if !defined(DEBUG_TRACE_STREAM)
        --#define DEBUG_TRACE_STREAM stdout
        -+#define DEBUG_TRACE_STREAM stderr
        - #endif
        + 	/* step 1. completed, the url is known now */
        + 	DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
        +@@ -14838,19 +15189,30 @@ handle_request(struct mg_connection *conn)
        + 		const char *cors_acrm = get_header(ri->http_headers,
        + 		                                   ri->num_headers,
        + 		                                   "Access-Control-Request-Method");
        ++		const char *cors_repl_asterisk_with_orig_cfg =
        ++		    conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
          
        - #else
        --- 
        -2.34.1
        -
        -
        -From dc375064b36fbf6ec9d29ce7c0c5005f4d3cf543 Mon Sep 17 00:00:00 2001
        -From: stevenwdv 
        -Date: Thu, 6 Mar 2025 14:11:14 +0100
        -Subject: [PATCH 127/173] Print to stderr instead of stdout in
        - mg_cry_internal_impl
        -
        ----
        - src/civetweb.c | 2 +-
        - 1 file changed, 1 insertion(+), 1 deletion(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index c6366547..64f58ece 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -3480,7 +3480,7 @@ mg_cry_internal_impl(const struct mg_connection *conn,
        - 	DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf);
        + 		/* Todo: check if cors_origin is in cors_orig_cfg.
        + 		 * Or, let the client check this. */
          
        - 	if (!conn) {
        --		puts(buf);
        -+		fputs(buf, stderr);
        - 		return;
        - 	}
        + 		if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
        + 		    && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
        +-		    && (cors_origin != NULL) && (cors_acrm != NULL)) {
        ++		    && (cors_origin != NULL) && (cors_acrm != NULL)
        ++		    && (cors_repl_asterisk_with_orig_cfg != NULL)
        ++		    && (*cors_repl_asterisk_with_orig_cfg != 0)) {
        ++			int cors_repl_asterisk_with_orig =
        ++			    mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
        ++
        + 			/* This is a valid CORS preflight, and the server is configured
        + 			 * to handle it automatically. */
        + 			const char *cors_acrh =
        + 			    get_header(ri->http_headers,
        + 			               ri->num_headers,
        + 			               "Access-Control-Request-Headers");
        ++			const char *cors_cred_cfg =
        ++			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        ++			const char *cors_exphdr_cfg =
        ++			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
          
        --- 
        -2.34.1
        -
        -
        -From 267a58096be0bb547e158949cef321942111604f Mon Sep 17 00:00:00 2001
        -From: DL6ER 
        -Date: Mon, 10 Mar 2025 19:11:43 +0100
        -Subject: [PATCH 128/173] Tolerate socket creation error if port is optional
        -
        -When users specify `[::]:80o` as port but the system has IPv6 disabled globally, e.g., with
        -```
        -GRUB_CMDLINE_LINUX="ipv6.disable=1"
        -```
        -then CivetWeb refuses to start even though it should skip the port if it cannot bind to it. However, when the port is specified as being optional, there is no point in treating non-availability of the protocol differently than the port already being taken. In this case, this port should simply be skipped.
        -
        -Signed-off-by: DL6ER 
        ----
        - src/civetweb.c | 8 ++++++++
        - 1 file changed, 8 insertions(+)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 2ee166cf..be598023 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -16154,6 +16154,10 @@ set_ports_option(struct mg_context *phys_ctx)
        - 			mg_cry_ctx_internal(phys_ctx,
        - 			                    "cannot create socket (entry %i)",
        - 			                    portsTotal);
        -+			if (so.is_optional) {
        -+				portsOk++; /* it's okay if we couldn't create a socket,
        -+						this port is optional anyway */
        -+			}
        - 			continue;
        - 		}
        + 			gmt_time_string(date, sizeof(date), &curtime);
        + 			mg_printf(conn,
        +@@ -14861,11 +15223,26 @@ handle_request(struct mg_connection *conn)
        + 			          "Content-Length: 0\r\n"
        + 			          "Connection: %s\r\n",
        + 			          date,
        +-			          cors_orig_cfg,
        ++			          (cors_repl_asterisk_with_orig == 0
        ++			           && cors_orig_cfg[0] == '*')
        ++			              ? cors_origin
        ++			              : cors_orig_cfg,
        + 			          ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
        + 			          suggest_connection_header(conn));
          
        -@@ -16239,6 +16243,10 @@ set_ports_option(struct mg_context *phys_ctx)
        - #else
        - 			mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available");
        - 			closesocket(so.sock);
        -+			if (so.is_optional) {
        -+				portsOk++; /* it's okay if we couldn't set the socket option,
        -+				              this port is optional anyway */
        +-			if (cors_acrh != NULL) {
        ++			if (cors_cred_cfg && *cors_cred_cfg) {
        ++				mg_printf(conn,
        ++				          "Access-Control-Allow-Credentials: %s\r\n",
        ++				          cors_cred_cfg);
         +			}
        - 			so.sock = INVALID_SOCKET;
        - 			continue;
        - #endif
        --- 
        -2.34.1
        -
        -
        -From 19bd43a7e17a50c26aa26d8d95d16b2087715d5f Mon Sep 17 00:00:00 2001
        -From: dmdmdm <3187057+dmdmdm@users.noreply.github.com>
        -Date: Thu, 3 Apr 2025 10:07:59 -0400
        -Subject: [PATCH 129/173] Add prefix MG_ in a few places
        -
        ----
        - docs/api/mg_form_data_handler.md | 12 ++++++------
        - 1 file changed, 6 insertions(+), 6 deletions(-)
        -
        -diff --git a/docs/api/mg_form_data_handler.md b/docs/api/mg_form_data_handler.md
        -index f9d1f43a..8f477b0d 100644
        ---- a/docs/api/mg_form_data_handler.md
        -+++ b/docs/api/mg_form_data_handler.md
        -@@ -10,14 +10,14 @@
        - ||The callback function `field_found()` is called when a new field has been found. The return value of this callback is used to define how the field should be processed. The parameters contain the following information:|
        - ||**`key`** - The name of the field as it was named with the `name` tag in the HTML source.|
        - ||**`filename`** - The name of the file to upload. Please note that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.|
        --||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.|
        -+||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `MG_FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.|
        - ||**`pathlen`** - The length of the buffer where the output path can be stored.|
        - ||**`user_data`** - A pointer to the value of the field `user_data` of the structure `struct mg_form_data_handler`.|
        - ||The callback function `field_found()` can return the following values back to Civetweb:|
        --||**`FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field|
        --||**`FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data|
        --||**`FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists|
        --||**`FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields|
        -+||**`MG_FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field|
        -+||**`MG_FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data|
        -+||**`MG_FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists|
        -+||**`MG_FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields|
        - |**`field_get`**|**`int field_get( const char *key, const char *value, size_t valuelen, void *user_data );`**|
        - ||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_GET`, Civetweb will call `field_get()` one or more times to pass back the data for this field.|
        - ||**`key`** - the name of the field being decoded, note this is only passed on the first call for file parameters (bug?)|
        -@@ -28,7 +28,7 @@
        - ||**`return` `MG_FORM_FIELD_HANDLE_NEXT`** - to skip further calls to get for this field and move on to the next field. |
        - ||**`return` `MG_FORM_FIELD_HANDLE_ABORT`** - to stop parsing this form and abandon the search for further fields. |
        - |**`field_store`**|**`int field_store( const char *path, long long file_size, void *user_data );`**|
        --||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call|
        -+||If the callback function `field_found()` returned `MG_FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call|
        - ||**`path`** - The path on the server where the file was stored|
        - ||**`file_size`** - The size of the stored file in bytes|
        - ||**`user_data`** - The value of the field `user_data` when the callback functions were registered with a call to `mg_handle_form_request();`|
        --- 
        -2.34.1
        -
        -
        -From 547504fd12c615f9a39ac446ebafba5e1e7b2717 Mon Sep 17 00:00:00 2001
        -From: dmdmdm <3187057+dmdmdm@users.noreply.github.com>
        -Date: Mon, 7 Apr 2025 11:52:03 -0400
        -Subject: [PATCH 130/173] Added MG_ prefix in a few places
        -
        ----
        - include/civetweb.h | 8 ++++----
        - 1 file changed, 4 insertions(+), 4 deletions(-)
        -
        -diff --git a/include/civetweb.h b/include/civetweb.h
        -index 0ce344dc..2665d646 100644
        ---- a/include/civetweb.h
        -+++ b/include/civetweb.h
        -@@ -1208,7 +1208,7 @@ struct mg_form_data_handler {
        - 	 *   filename: Name of a file to upload, at the client computer.
        - 	 *             Only set for input fields of type "file", otherwise NULL.
        - 	 *   path: Output parameter: File name (incl. path) to store the file
        --	 *         at the server computer. Only used if FORM_FIELD_STORAGE_STORE
        -+	 *         at the server computer. Only used if MG_FORM_FIELD_STORAGE_STORE
        - 	 *         is returned by this callback. Existing files will be
        - 	 *         overwritten.
        - 	 *   pathlen: Length of the buffer for path.
        -@@ -1216,7 +1216,7 @@ struct mg_form_data_handler {
        - 	 *
        - 	 * Return value:
        - 	 *   The callback must return the intended storage for this field
        --	 *   (See FORM_FIELD_STORAGE_*).
        -+	 *   (See MG_FORM_FIELD_STORAGE_*).
        - 	 */
        - 	int (*field_found)(const char *key,
        - 	                   const char *filename,
        -@@ -1224,7 +1224,7 @@ struct mg_form_data_handler {
        - 	                   size_t pathlen,
        - 	                   void *user_data);
        - 
        --	/* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
        -+	/* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_GET,
        - 	 * this callback will receive the field data.
        - 	 *
        - 	 * Parameters:
        -@@ -1241,7 +1241,7 @@ struct mg_form_data_handler {
        - 	                 size_t valuelen,
        - 	                 void *user_data);
        ++
        ++			if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        ++				mg_printf(conn,
        ++				          "Access-Control-Expose-Headers: %s\r\n",
        ++				          cors_exphdr_cfg);
        ++			}
        ++
        ++			if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) {
        + 				/* CORS request is asking for additional headers */
        + 				const char *cors_hdr_cfg =
        + 				    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
        +@@ -15240,9 +15617,10 @@ handle_request(struct mg_connection *conn)
        + 	}
          
        --	/* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
        -+	/* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_STORE,
        - 	 * the data will be stored into a file. If the file has been written
        - 	 * successfully, this callback will be called. This callback will
        - 	 * not be called for only partially uploaded files. The
        --- 
        -2.34.1
        -
        -
        -From a87a7aaffd182ee5b5f158f74f5825728e162306 Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Fri, 18 Apr 2025 10:54:32 +0200
        -Subject: [PATCH 131/173] Modify STOP_FLAG_ASSING to make TSAN happy (#1333)
        -
        ----
        - CREDITS.md     | 1 +
        - src/civetweb.c | 4 +++-
        - 2 files changed, 4 insertions(+), 1 deletion(-)
        -
        -diff --git a/CREDITS.md b/CREDITS.md
        -index f1e1d150..f6a2a79d 100644
        ---- a/CREDITS.md
        -+++ b/CREDITS.md
        -@@ -130,6 +130,7 @@
        - * Lammert Bies
        - * Lars Immisch
        - * Lawrence
        -+* Lev275568
        - * Li Peng
        - * Lianghui
        - * Lorenzo Canepa
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 9e098045..e09bcf3a 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -2339,7 +2339,9 @@ STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v)
        - {
        - 	stop_flag_t sf = 0;
        - 	do {
        --		sf = mg_atomic_compare_and_swap(f, *f, v);
        -+		sf = mg_atomic_compare_and_swap(f,
        -+		                                __atomic_load_n(f, __ATOMIC_SEQ_CST),
        -+		                                v);
        - 	} while (sf != v);
        - }
        + 	/* 12. Directory uris should end with a slash */
        +-	if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
        ++	if (file.stat.is_directory && (uri_len > 0)
        + 	    && (ri->local_uri[uri_len - 1] != '/')) {
          
        --- 
        -2.34.1
        -
        -
        -From d6110adc811e2d2384de477c6b402aa7f716e94d Mon Sep 17 00:00:00 2001
        -From: bel2125 
        -Date: Fri, 18 Apr 2025 10:55:36 +0200
        -Subject: [PATCH 132/173] Auto-format code
        -
        ----
        - fuzztest/fuzzmain.c      |  16 +++---
        - src/civetweb.c           |  33 ++++++++----
        - src/handle_form.inl      |   3 +-
        - src/mod_gnutls.inl       |   6 +--
        - src/mod_lua.inl          |  24 +++++----
        - src/mod_mbedtls.inl      |   3 +-
        - unittest/public_server.c | 107 +++++++++++++++++++--------------------
        - 7 files changed, 103 insertions(+), 89 deletions(-)
        -
        -diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c
        -index df26768c..c7e27943 100644
        ---- a/fuzztest/fuzzmain.c
        -+++ b/fuzztest/fuzzmain.c
        -@@ -110,13 +110,14 @@ civetweb_init(void)
        - int LLVMFuzzerInitialize(int *argc, char ***argv);
        ++
        + 		/* Path + server root */
        + 		size_t buflen = UTF8_PATH_MAX * 2 + 2;
        + 		char *new_path;
        +@@ -15255,12 +15633,27 @@ handle_request(struct mg_connection *conn)
        + 			mg_send_http_error(conn, 500, "out or memory");
        + 		} else {
        + 			mg_get_request_link(conn, new_path, buflen - 1);
        +-			strcat(new_path, "/");
        ++
        ++			size_t len = strlen(new_path);
        ++			if (len + 1 < buflen) {
        ++				new_path[len] = '/';
        ++				new_path[len + 1] = '\0';
        ++				len++;
        ++			}
        ++
        + 			if (ri->query_string) {
        +-				/* Append ? and query string */
        +-				strcat(new_path, "?");
        +-				strcat(new_path, ri->query_string);
        ++				if (len + 1 < buflen) {
        ++					new_path[len] = '?';
        ++					new_path[len + 1] = '\0';
        ++					len++;
        ++				}
        ++
        ++				/* Append with size of space left for query string + null
        ++				 * terminator */
        ++				size_t max_append = buflen - len - 1;
        ++				strncat(new_path, ri->query_string, max_append);
        + 			}
        ++
        + 			mg_send_http_redirect(conn, new_path, 301);
        + 			mg_free(new_path);
        + 		}
        +@@ -15503,7 +15896,7 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
        + 	unsigned int a, b, c, d;
        + 	unsigned port;
        + 	unsigned long portUL;
        +-	int ch, len;
        ++	int len;
        + 	const char *cb;
        + 	char *endptr;
        + #if defined(USE_IPV6)
        +@@ -15656,14 +16049,38 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
        + 	}
          
        - int
        --LLVMFuzzerInitialize(int *argc, char ***argv) {
        --  // Silence unused args warning.
        -+LLVMFuzzerInitialize(int *argc, char ***argv)
        -+{
        -+	// Silence unused args warning.
        - 	(void)(argc);
        - 	(void)(argv);
        + 	/* sscanf and the option splitting code ensure the following condition
        +-	 * Make sure the port is valid and vector ends with the port, 's' or 'r' */
        +-	if ((len > 0) && is_valid_port(port)
        +-	    && (((size_t)len == vec->len) || (((size_t)len + 1) == vec->len))) {
        +-		/* Next character after the port number */
        +-		ch = ((size_t)len < vec->len) ? vec->ptr[len] : '\0';
        +-		so->is_ssl = (ch == 's');
        +-		so->ssl_redir = (ch == 'r');
        +-		if ((ch == '\0') || (ch == 's') || (ch == 'r')) {
        ++	 * Make sure the port is valid and vector ends with the port, 'o', 's', or
        ++	 * 'r' */
        ++	if ((len > 0) && (is_valid_port(port))) {
        ++		int bad_suffix = 0;
        ++		size_t i;
        ++
        ++		/* Parse any suffix character(s) after the port number */
        ++		for (i = len; i < vec->len; i++) {
        ++			unsigned char *opt = NULL;
        ++			switch (vec->ptr[i]) {
        ++			case 'o':
        ++				opt = &so->is_optional;
        ++				break;
        ++			case 'r':
        ++				opt = &so->ssl_redir;
        ++				break;
        ++			case 's':
        ++				opt = &so->is_ssl;
        ++				break;
        ++			default: /* empty */
        ++				break;
        ++			}
        ++
        ++			if ((opt) && (*opt == 0))
        ++				*opt = 1;
        ++			else {
        ++				bad_suffix = 1;
        ++				break;
        ++			}
        ++		}
        ++
        ++		if ((bad_suffix == 0) && ((so->is_ssl == 0) || (so->ssl_redir == 0))) {
        + 			return 1;
        + 		}
        + 	}
        +@@ -15718,8 +16135,14 @@ is_ssl_port_used(const char *ports)
        + 		char prevIsNumber = 0;
          
        - 	civetweb_init();
        --  return 0;
        -+	return 0;
        - }
        + 		for (i = 0; i < portslen; i++) {
        +-			if (prevIsNumber && (ports[i] == 's' || ports[i] == 'r')) {
        +-				return 1;
        ++			if (prevIsNumber) {
        ++				int suffixCharIdx = (ports[i] == 'o')
        ++				                        ? (i + 1)
        ++				                        : i; /* allow "os" and "or" suffixes */
        ++				if (ports[suffixCharIdx] == 's'
        ++				    || ports[suffixCharIdx] == 'r') {
        ++					return 1;
        ++				}
        + 			}
        + 			if (ports[i] >= '0' && ports[i] <= '9') {
        + 				prevIsNumber = 1;
        +@@ -15800,6 +16223,10 @@ set_ports_option(struct mg_context *phys_ctx)
        + 			mg_cry_ctx_internal(phys_ctx,
        + 			                    "cannot create socket (entry %i)",
        + 			                    portsTotal);
        ++			if (so.is_optional) {
        ++				portsOk++; /* it's okay if we couldn't create a socket,
        ++				        this port is optional anyway */
        ++			}
        + 			continue;
        + 		}
          
        - #if defined(TEST_FUZZ1)
        -@@ -215,7 +216,7 @@ LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
        - {
        - 	static char URI[1024 * 64]; /* static, to avoid stack overflow */
        +@@ -15885,6 +16312,10 @@ set_ports_option(struct mg_context *phys_ctx)
        + #else
        + 			mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available");
        + 			closesocket(so.sock);
        ++			if (so.is_optional) {
        ++				portsOk++; /* it's okay if we couldn't set the socket option,
        ++				              this port is optional anyway */
        ++			}
        + 			so.sock = INVALID_SOCKET;
        + 			continue;
        + #endif
        +@@ -15902,6 +16333,10 @@ set_ports_option(struct mg_context *phys_ctx)
        + 				                    strerror(errno));
        + 				closesocket(so.sock);
        + 				so.sock = INVALID_SOCKET;
        ++				if (so.is_optional) {
        ++					portsOk++; /* it's okay if we couldn't bind, this port is
        ++					              optional anyway */
        ++				}
        + 				continue;
        + 			}
        + 		}
        +@@ -15918,6 +16353,10 @@ set_ports_option(struct mg_context *phys_ctx)
        + 				                    strerror(errno));
        + 				closesocket(so.sock);
        + 				so.sock = INVALID_SOCKET;
        ++				if (so.is_optional) {
        ++					portsOk++; /* it's okay if we couldn't bind, this port is
        ++					              optional anyway */
        ++				}
        + 				continue;
        + 			}
        + 		}
        +@@ -15934,6 +16373,10 @@ set_ports_option(struct mg_context *phys_ctx)
        + 				                    strerror(errno));
        + 				closesocket(so.sock);
        + 				so.sock = INVALID_SOCKET;
        ++				if (so.is_optional) {
        ++					portsOk++; /* it's okay if we couldn't bind, this port is
        ++					              optional anyway */
        ++				}
        + 				continue;
        + 			}
        + 		}
        +@@ -16011,9 +16454,14 @@ set_ports_option(struct mg_context *phys_ctx)
        + 			continue;
        + 		}
          
        --	if (size+1 < sizeof(URI)) {
        -+	if (size + 1 < sizeof(URI)) {
        - 		memcpy(URI, data, size);
        - 		URI[size] = 0;
        - 	} else {
        -@@ -445,13 +446,14 @@ mock_server_init(void)
        - int LLVMFuzzerInitialize(int *argc, char ***argv);
        ++		/* The +2 below includes the original +1 (for the socket we're about to
        ++		 * add), plus another +1 for the thread_shutdown_notification_socket
        ++		 * that we'll also want to poll() on so that mg_stop() can return
        ++		 * quickly
        ++		 */
        + 		if ((pfd = (struct mg_pollfd *)
        + 		         mg_realloc_ctx(phys_ctx->listening_socket_fds,
        +-		                        (phys_ctx->num_listening_sockets + 1)
        ++		                        (phys_ctx->num_listening_sockets + 2)
        + 		                            * sizeof(phys_ctx->listening_socket_fds[0]),
        + 		                        phys_ctx))
        + 		    == NULL) {
        +@@ -16358,7 +16806,39 @@ mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
        + 		return 0;
        + 	}
          
        - int
        --LLVMFuzzerInitialize(int *argc, char ***argv) {
        --  // Silence unused args warning.
        -+LLVMFuzzerInitialize(int *argc, char ***argv)
        +-	return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
        ++	return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE],
        ++	                        dom_ctx->config[SSL_CIPHER_LIST])
        ++	               == 0
        ++	           ? 1
        ++	           : 0;
        ++}
        ++
        ++#elif defined(USE_GNUTLS)
        ++/* Check if SSL is required.
        ++ * If so, set up ctx->ssl_ctx pointer. */
        ++static int
        ++mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
         +{
        -+	// Silence unused args warning.
        - 	(void)(argc);
        - 	(void)(argv);
        - 
        - 	mock_server_init();
        --  return 0;
        -+	return 0;
        ++	if (!phys_ctx) {
        ++		return 0;
        ++	}
        ++
        ++	if (!dom_ctx) {
        ++		dom_ctx = &(phys_ctx->dd);
        ++	}
        ++
        ++	if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
        ++		/* No SSL port is set. No need to setup SSL. */
        ++		return 1;
        ++	}
        ++
        ++	dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
        ++	if (dom_ctx->ssl_ctx == NULL) {
        ++		fprintf(stderr, "ssl_ctx malloc failed\n");
        ++		return 0;
        ++	}
        ++
        ++	return gtls_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
        + 	               == 0
        + 	           ? 1
        + 	           : 0;
        +@@ -16535,15 +17015,26 @@ sslize(struct mg_connection *conn,
        + 					/* Need to retry the function call "later".
        + 					 * See https://linux.die.net/man/3/ssl_get_error
        + 					 * This is typical for non-blocking sockets. */
        +-					struct mg_pollfd pfd;
        ++					struct mg_pollfd pfd[2];
        + 					int pollres;
        +-					pfd.fd = conn->client.sock;
        +-					pfd.events = ((err == SSL_ERROR_WANT_CONNECT)
        +-					              || (err == SSL_ERROR_WANT_WRITE))
        +-					                 ? POLLOUT
        +-					                 : POLLIN;
        +-					pollres =
        +-					    mg_poll(&pfd, 1, 50, &(conn->phys_ctx->stop_flag));
        ++					unsigned int num_sock = 1;
        ++					pfd[0].fd = conn->client.sock;
        ++					pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT)
        ++					                 || (err == SSL_ERROR_WANT_WRITE))
        ++					                    ? POLLOUT
        ++					                    : POLLIN;
        ++
        ++					if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        ++						pfd[num_sock].fd =
        ++						    conn->phys_ctx->thread_shutdown_notification_socket;
        ++						pfd[num_sock].events = POLLIN;
        ++						num_sock++;
        ++					}
        ++
        ++					pollres = mg_poll(pfd,
        ++					                  num_sock,
        ++					                  50,
        ++					                  &(conn->phys_ctx->stop_flag));
        + 					if (pollres < 0) {
        + 						/* Break if error occurred (-1)
        + 						 * or server shutdown (-2) */
        +@@ -17628,7 +18119,7 @@ uninitialize_openssl(void)
        + #endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
        + 	}
          }
        +-#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) */
        ++#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) */
          
          
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 37763ff1..dc2acd81 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -1595,7 +1595,7 @@ static int mg_openssl_initialized = 0;
        - #endif
        - #if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1)                     \
        -     && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)                      \
        --	&& !defined(USE_GNUTLS)
        -+    && !defined(USE_GNUTLS)
        - #error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS"
        - #endif
        - #if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
        -@@ -6170,9 +6170,12 @@ push_inner(struct mg_context *ctx,
        - 		} else
        - #elif defined(USE_GNUTLS)
        - 		if (ssl != NULL) {
        --			n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t) len);
        -+			n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t)len);
        - 			if (n < 0) {
        --				fprintf(stderr, "SSL write failed (%d): %s", n, gnutls_strerror(n));
        -+				fprintf(stderr,
        -+				        "SSL write failed (%d): %s",
        -+				        n,
        -+				        gnutls_strerror(n));
        - 				return -2;
        - 			} else {
        - 				err = 0;
        -@@ -6477,7 +6480,10 @@ pull_inner(FILE *fp,
        - 		if (pollres > 0) {
        - 			nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
        - 			if (nread < 0) {
        --				fprintf(stderr, "SSL read failed (%d): %s", nread, gnutls_strerror(nread));
        -+				fprintf(stderr,
        -+				        "SSL read failed (%d): %s",
        -+				        nread,
        -+				        gnutls_strerror(nread));
        - 				return -2;
        - 			} else {
        - 				err = 0;
        -@@ -9632,7 +9638,8 @@ connect_socket(
        - 		return 0;
        + #if !defined(NO_FILESYSTEMS)
        +@@ -17900,6 +18391,11 @@ close_connection(struct mg_connection *conn)
        + 		mbed_ssl_close(conn->ssl);
        + 		conn->ssl = NULL;
          	}
        ++#elif defined(USE_GNUTLS)
        ++	if (conn->ssl != NULL) {
        ++		gtls_ssl_close(conn->ssl);
        ++		conn->ssl = NULL;
        ++	}
        + #elif !defined(NO_SSL)
        + 	if (conn->ssl != NULL) {
        + 		/* Run SSL_shutdown twice to ensure completely close SSL connection
        +@@ -17963,7 +18459,7 @@ mg_close_connection(struct mg_connection *conn)
        + 		 * timeouts, we will just wait a few seconds in mg_join_thread. */
          
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) && !defined(NO_SSL_DL)
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)          \
        -+    && !defined(NO_SSL_DL)
        - #if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
        - 	if (use_ssl && (TLS_client_method == NULL)) {
        - 		if (error != NULL) {
        -@@ -16158,7 +16165,7 @@ set_ports_option(struct mg_context *phys_ctx)
        - 			                    portsTotal);
        - 			if (so.is_optional) {
        - 				portsOk++; /* it's okay if we couldn't create a socket,
        --						this port is optional anyway */
        -+				        this port is optional anyway */
        - 			}
        - 			continue;
        + 		/* join worker thread */
        +-		for (i = 0; i < conn->phys_ctx->cfg_worker_threads; i++) {
        ++		for (i = 0; i < conn->phys_ctx->spawned_worker_threads; i++) {
        + 			mg_join_thread(conn->phys_ctx->worker_threadids[i]);
          		}
        -@@ -18399,7 +18406,8 @@ mg_close_connection(struct mg_connection *conn)
        + 	}
        +@@ -17971,7 +18467,8 @@ mg_close_connection(struct mg_connection *conn)
          
          	close_connection(conn);
          
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
         +#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
         +    && !defined(USE_GNUTLS) // TODO: mbedTLS client
          	if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT)
          	     || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT))
          	    && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
        -@@ -18501,7 +18509,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        +@@ -18073,7 +18570,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
          		return NULL;
          	}
          
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
         +#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
         +    && !defined(USE_GNUTLS) // TODO: mbedTLS client
          #if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0))                     \
              && !defined(NO_SSL_DL)
          
        -@@ -18578,7 +18587,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        +@@ -18150,7 +18648,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
          			            error->text_buffer_size,
          			            "Can not create mutex");
          		}
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
         +#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
         +    && !defined(USE_GNUTLS) // TODO: mbedTLS client
          		SSL_CTX_free(conn->dom_ctx->ssl_ctx);
          #endif
          		closesocket(sock);
        -@@ -18586,7 +18596,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        +@@ -18158,7 +18657,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
          		return NULL;
          	}
          
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) // TODO: mbedTLS client
        +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
         +#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
         +    && !defined(USE_GNUTLS) // TODO: mbedTLS client
          	if (use_ssl) {
          		/* TODO: Check ssl_verify_peer and ssl_ca_path here.
          		 * SSL_CTX_set_verify call is needed to switch off server
        -@@ -22257,7 +22268,7 @@ mg_get_system_info(char *buffer, int buflen)
        - 		            sizeof(block),
        - 		            ",%s\"os\" : \"%s %s\"",
        - 		            eol,
        --		           "RTEMS",
        -+		            "RTEMS",
        - 		            rtems_version());
        - 		system_info_length += mg_str_append(&buffer, end, block);
        - #elif defined(__ZEPHYR__)
        -diff --git a/src/handle_form.inl b/src/handle_form.inl
        -index a7b7fc10..14235ec9 100644
        ---- a/src/handle_form.inl
        -+++ b/src/handle_form.inl
        -@@ -757,8 +757,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 
        - 			/* after the transport padding, if the boundary isn't
        - 			 * immediately followed by a \r\n then it is either... */
        --			if (strncmp(boundary_end, "\r\n", 2))
        --			{
        -+			if (strncmp(boundary_end, "\r\n", 2)) {
        - 				/* ...the final boundary, and it is followed by --, (in which
        - 				 * case it's the end of the request) or it's a malformed
        - 				 * request */
        -diff --git a/src/mod_gnutls.inl b/src/mod_gnutls.inl
        -index b4ca5d47..f765362b 100644
        ---- a/src/mod_gnutls.inl
        -+++ b/src/mod_gnutls.inl
        -@@ -17,9 +17,9 @@ CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt);
        - CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx);
        - CIVETWEB_API void gtls_ssl_close(SSL *ssl);
        - CIVETWEB_API int gtls_ssl_accept(SSL **ssl,
        --                    SSL_CTX *ssl_ctx,
        --                    int sock,
        --                    struct mg_context *phys_ctx);
        -+                                 SSL_CTX *ssl_ctx,
        -+                                 int sock,
        -+                                 struct mg_context *phys_ctx);
        - CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len);
        - CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len);
        - 
        -diff --git a/src/mod_lua.inl b/src/mod_lua.inl
        -index e97a28cc..2a1d3a57 100644
        ---- a/src/mod_lua.inl
        -+++ b/src/mod_lua.inl
        -@@ -11,8 +11,7 @@
        - #include "civetweb_private_lua.h"
        - 
        - /* Prototypes */
        --static int
        --lua_error_handler(lua_State *L);
        -+static int lua_error_handler(lua_State *L);
        - 
        - #if defined(_WIN32)
        - static void *
        -@@ -648,14 +647,19 @@ run_lsp_kepler(struct mg_connection *conn,
        - 		/* Initialize a new HTTP response, either with some-predefined
        - 		 * status code (e.g. 404 if this is called from an error
        - 		 * handler) or with 200 OK */
        --		mg_response_header_start(conn, conn->status_code > 0 ? conn->status_code : 200);
        -+		mg_response_header_start(conn,
        -+		                         conn->status_code > 0 ? conn->status_code
        -+		                                               : 200);
        - 
        - 		/* Add additional headers */
        - 		send_no_cache_header(conn);
        - 		send_additional_header(conn);
        - 
        - 		/* Add content type */
        --		mg_response_header_add(conn, "Content-Type", "text/html; charset=utf-8", -1);
        -+		mg_response_header_add(conn,
        -+		                       "Content-Type",
        -+		                       "text/html; charset=utf-8",
        -+		                       -1);
        - 
        - 		/* Send the HTTP response (status and all headers) */
        - 		mg_response_header_send(conn);
        -@@ -679,8 +683,7 @@ run_lsp_kepler(struct mg_connection *conn,
        - 	} else {
        - 		/* Success loading chunk. Call it. */
        - 		lua_ok = lua_pcall(L, 0, 0, 0);
        --		if(lua_ok != LUA_OK)
        --		{
        -+		if (lua_ok != LUA_OK) {
        - 			lua_cry(conn, lua_ok, L, "LSP Kepler", "call");
        - 			lua_error_handler(L);
        - 			return 1;
        -@@ -804,8 +807,7 @@ run_lsp_civetweb(struct mg_connection *conn,
        - 					} else {
        - 						/* Success loading chunk. Call it. */
        - 						lua_ok = lua_pcall(L, 0, 0, 0);
        --						if(lua_ok != LUA_OK)
        --						{
        -+						if (lua_ok != LUA_OK) {
        - 							lua_cry(conn, lua_ok, L, "LSP", "call");
        - 							lua_error_handler(L);
        - 							return 1;
        -@@ -2801,7 +2803,7 @@ lua_error_handler(lua_State *L)
        - 		lua_pushstring(L, error_msg);
        - 		lua_pushliteral(L, "\n");
        - 		lua_call(L, 2, 0); /* call mg.write(error_msg + \n) */
        --		lua_pop(L, 1); /* pop mg */
        -+		lua_pop(L, 1);     /* pop mg */
        - 
        - 		/* Get Lua traceback */
        - 		lua_getglobal(L, "debug");
        -@@ -2817,8 +2819,8 @@ lua_error_handler(lua_State *L)
        - 		/* Only print the traceback if it is not empty */
        - 		if (strcmp(lua_tostring(L, -1), "stack traceback:") != 0) {
        - 			lua_pushliteral(L, "\n"); /* append a newline */
        --			lua_call(L, 2, 0); /* call mg.write(traceback + \n) */
        --			lua_pop(L, 2); /* pop mg and traceback */
        -+			lua_call(L, 2, 0);        /* call mg.write(traceback + \n) */
        -+			lua_pop(L, 2);            /* pop mg and traceback */
        - 		} else {
        - 			lua_pop(L, 3); /* pop mg, traceback and error message */
        - 		}
        -diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl
        -index 93f1bb29..03d189ea 100644
        ---- a/src/mod_mbedtls.inl
        -+++ b/src/mod_mbedtls.inl
        -@@ -95,7 +95,8 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        - 	 */
        - 	const psa_status_t status = psa_crypto_init();
        - 	if (status != PSA_SUCCESS) {
        --		DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n", (int) status);
        -+		DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n",
        -+		            (int)status);
        - 		return -1;
        - 	}
        - #endif
        -diff --git a/unittest/public_server.c b/unittest/public_server.c
        -index 3ea5c45f..27a3eb9f 100644
        ---- a/unittest/public_server.c
        -+++ b/unittest/public_server.c
        -@@ -847,7 +847,7 @@ START_TEST(test_mg_server_and_client_tls)
        - 	 * while Ubuntu Xenial, Ubuntu Trusty and Windows test containers at
        - 	 * Travis CI do not. Maybe it is OpenSSL version specific.
        - 	 */
        --#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) 
        -+#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
        - 	if (client_conn) {
        - 		/* Connect succeeds, but the connection is unusable. */
        - 		mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
        -@@ -3091,7 +3091,7 @@ START_TEST(test_handle_form)
        - 
        - 	/* Handle form: "POST multipart/form-data" without trailing CRLF*/
        - 	multipart_body =
        --		"--multipart-form-data-boundary--see-RFC-2388\r\n"
        -+	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
        - 	    "Content-Disposition: form-data; name=\"textin\"\r\n"
        - 	    "\r\n"
        - 	    "text\r\n"
        -@@ -3629,11 +3629,11 @@ START_TEST(test_handle_form)
        - 	multipart_body =
        - 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
        - 	    "Content-Disposition: form-data; "
        --		"custom1name=\"1\"; "
        --		"custom2name=\"2\"; "
        --		"custom3name=\"3\"; "
        --		"custom4name=\"4\"; "
        --		"name=\"textin\"\r\n"
        -+	    "custom1name=\"1\"; "
        -+	    "custom2name=\"2\"; "
        -+	    "custom3name=\"3\"; "
        -+	    "custom4name=\"4\"; "
        -+	    "name=\"textin\"\r\n"
        - 	    "\r\n"
        - 	    "text\r\n"
        - 	    "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n"
        -@@ -3791,53 +3791,52 @@ START_TEST(test_handle_form)
        - 	mg_close_connection(client_conn);
        - 
        - 	/* Handle form: "POST multipart/form-data" very long preamble */
        --	multipart_body =
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "preamblepreamblepreamblepreamblepreamble\r\n"
        --	    "--multipart-form-data-boundary--see-RFC-2388\r\n";
        --	    "Content-Disposition: form-data; name=\"passwordin\"\r\n"
        --	    "\r\n"
        --	    "\r\n"
        --	    "--multipart-form-data-boundary--see-RFC-2388--\r\n";
        -+	multipart_body = "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "preamblepreamblepreamblepreamblepreamble\r\n"
        -+	                 "--multipart-form-data-boundary--see-RFC-2388\r\n";
        -+	"Content-Disposition: form-data; name=\"passwordin\"\r\n"
        -+	"\r\n"
        -+	"\r\n"
        -+	"--multipart-form-data-boundary--see-RFC-2388--\r\n";
        - 
        - 	body_len = strlen(multipart_body);
        - 	ck_assert_uint_eq(body_len, 1768); /* not required */
        --- 
        -2.34.1
        -
        -
        -From 0c2dc8f5dfb2d0e2069cba9f73d92b833f243ef7 Mon Sep 17 00:00:00 2001
        -From: Tim Lebedkov 
        -Date: Thu, 1 May 2025 13:19:56 +0200
        -Subject: [PATCH 133/173] Use the Markdown syntax
        -
        ----
        - docs/OpenSSL.md | 3 +--
        - 1 file changed, 1 insertion(+), 2 deletions(-)
        -
        -diff --git a/docs/OpenSSL.md b/docs/OpenSSL.md
        -index f37c6b38..657a6c35 100644
        ---- a/docs/OpenSSL.md
        -+++ b/docs/OpenSSL.md
        -@@ -1,5 +1,4 @@
        --Adding OpenSSL Support
        --=====
        -+# Adding OpenSSL Support
        - 
        - Civetweb supports *HTTPS* connections using the OpenSSL transport layer
        - security (TLS) library. OpenSSL is a free, open source library (see
        --- 
        -2.34.1
        -
        -
        -From 7cdc05292211699d410e99d74722fba2eac9b280 Mon Sep 17 00:00:00 2001
        -From: Tim Lebedkov 
        -Date: Thu, 1 May 2025 13:21:40 +0200
        -Subject: [PATCH 134/173] Use the Markdown syntax
        -
        ----
        - docs/gnutls.md | 3 +--
        - 1 file changed, 1 insertion(+), 2 deletions(-)
        -
        -diff --git a/docs/gnutls.md b/docs/gnutls.md
        -index c247850f..0c4ded53 100644
        ---- a/docs/gnutls.md
        -+++ b/docs/gnutls.md
        -@@ -1,5 +1,4 @@
        --#### Use GnuTLS instead of OpenSSL
        --=====
        -+# Use GnuTLS instead of OpenSSL
        - 
        - 1 [Build libgmp](https://gmplib.org)
        - 
        --- 
        -2.34.1
        -
        -
        -From a91d8caf6392bd7effc85f190bb760fe1283ec3f Mon Sep 17 00:00:00 2001
        -From: yubiuser 
        -Date: Sat, 3 May 2025 21:22:23 +0200
        -Subject: [PATCH 135/173] Prevent CRLF injection attempts
        -
        -Signed-off-by: yubiuser 
        ----
        - src/civetweb.c | 13 +++++++++++++
        - 1 file changed, 13 insertions(+)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 3a87a4e5..27f9b50e 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -18858,6 +18858,19 @@ get_uri_type(const char *uri)
        +@@ -18415,7 +18915,20 @@ get_uri_type(const char *uri)
          	 * and % encoded symbols.
          	 */
          	for (i = 0; uri[i] != 0; i++) {
        +-		if (uri[i] < 33) {
         +		/* Check for CRLF injection attempts */
         +		if (uri[i] == '%') {
        -+			if (uri[i+1] == '0' && (uri[i+2] == 'd' || uri[i+2] == 'D')) {
        ++			if (uri[i + 1] == '0' && (uri[i + 2] == 'd' || uri[i + 2] == 'D')) {
         +				/* Found %0d (CR) */
         +				DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
         +				return 0;
         +			}
        -+			if (uri[i+1] == '0' && (uri[i+2] == 'a' || uri[i+2] == 'A')) {
        ++			if (uri[i + 1] == '0' && (uri[i + 2] == 'a' || uri[i + 2] == 'A')) {
         +				/* Found %0a (LF) */
         +				DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
         +				return 0;
         +			}
         +		}
        - 		if ((unsigned char)uri[i] < 33) {
        ++		if ((unsigned char)uri[i] < 33) {
          			/* control characters and spaces are invalid */
          			return 0;
        --- 
        -2.34.1
        -
        -
        -From 76e222bcb77ba8452e5da4e82ae6cecd499c25e0 Mon Sep 17 00:00:00 2001
        -From: krispybyte 
        -Date: Sat, 21 Jun 2025 23:33:50 +0300
        -Subject: [PATCH 136/173] Fix heap overflow in directory URI slash redirection
        -
        ----
        - src/civetweb.c | 23 ++++++++++++++++++-----
        - 1 file changed, 18 insertions(+), 5 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index bbc9aa8b..e969c939 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -15579,7 +15579,6 @@ handle_request(struct mg_connection *conn)
        - 	/* 12. Directory uris should end with a slash */
        - 	if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
        - 	    && (ri->local_uri[uri_len - 1] != '/')) {
        --
        - 		/* Path + server root */
        - 		size_t buflen = UTF8_PATH_MAX * 2 + 2;
        - 		char *new_path;
        -@@ -15592,12 +15591,26 @@ handle_request(struct mg_connection *conn)
        - 			mg_send_http_error(conn, 500, "out or memory");
        + 		}
        +@@ -18622,7 +19135,8 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        + 			            ebuf,
        + 			            ebuf_len,
        + 			            "%s",
        +-			            "Malformed message");
        ++			            conn->request_len == -3 ? "Request timeout"
        ++			                                    : "Malformed message");
        + 			*err = 400;
          		} else {
        - 			mg_get_request_link(conn, new_path, buflen - 1);
        --			strcat(new_path, "/");
        + 			/* Server did not recv anything -> just close the connection */
        +@@ -18644,7 +19158,7 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        + static int
        + get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        + {
        +-	const char *cl;
        ++	const char *h_zip, *h_chunk, *h_len;
        + 
        + 	conn->connection_type =
        + 	    CONNECTION_TYPE_REQUEST; /* request (valid of not) */
        +@@ -18679,20 +19193,23 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        + 	}
        + 
        + #if USE_ZLIB
        +-	if (((cl = get_header(conn->request_info.http_headers,
        ++	if (((h_zip = get_header(conn->request_info.http_headers,
        + 	                      conn->request_info.num_headers,
        + 	                      "Accept-Encoding"))
        + 	     != NULL)
        +-	    && strstr(cl, "gzip")) {
        ++	    && strstr(h_zip, "gzip")) {
        + 		conn->accept_gzip = 1;
        + 	}
        + #endif
        +-	if (((cl = get_header(conn->request_info.http_headers,
        ++   h_chunk = get_header(conn->request_info.http_headers,
        + 	                      conn->request_info.num_headers,
        +-	                      "Transfer-Encoding"))
        +-	     != NULL)
        ++	                      "Transfer-Encoding");
        ++   h_len = get_header(conn->request_info.http_headers,
        ++	                            conn->request_info.num_headers,
        ++	                            "Content-Length");
        ++	if (h_chunk != NULL)
        + 	    && mg_strcasecmp(cl, "identity")) {
        +-		if (mg_strcasecmp(cl, "chunked")) {
        ++		if ((0!=mg_strcasecmp(cl, "chunked")) || (h_len!=NULL)) {
        + 			mg_snprintf(conn,
        + 			            NULL, /* No truncation check for ebuf */
        + 			            ebuf,
        +@@ -18704,10 +19221,7 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        + 		}
        + 		conn->is_chunked = 1;
        + 		conn->content_len = 0; /* not yet read */
        +-	} else if ((cl = get_header(conn->request_info.http_headers,
        +-	                            conn->request_info.num_headers,
        +-	                            "Content-Length"))
        +-	           != NULL) {
        ++	} else if (h_len != NULL) {
        + 		/* Request has content length set */
        + 		char *endptr = NULL;
        + 		conn->content_len = strtoll(cl, &endptr, 10);
        +@@ -18993,6 +19507,24 @@ websocket_client_thread(void *data)
        + #endif
        + 
        + 
        ++#if defined(USE_WEBSOCKET)
        ++static void
        ++generate_websocket_magic(char *magic25)
        ++{
        ++	uint64_t rnd;
        ++	unsigned char buffer[2 * sizeof(rnd)];
         +
        -+			size_t len = strlen(new_path);
        -+			if (len + 1 < buflen) {
        -+				new_path[len] = '/';
        -+				new_path[len + 1] = '\0';
        -+				len += 1;
        -+			}
        ++	rnd = get_random();
        ++	memcpy(buffer, &rnd, sizeof(rnd));
        ++	rnd = get_random();
        ++	memcpy(buffer + sizeof(rnd), &rnd, sizeof(rnd));
         +
        - 			if (ri->query_string) {
        --				/* Append ? and query string */
        --				strcat(new_path, "?");
        --				strcat(new_path, ri->query_string);
        -+				if (len + 1 < buflen) {
        -+					new_path[len] = '?';
        -+					new_path[len + 1] = '\0';
        -+					len += 1;
        -+				}
        ++	size_t dst_len = 24 + 1;
        ++	mg_base64_encode(buffer, sizeof(buffer), magic25, &dst_len);
        ++}
        ++#endif
         +
        -+				/* Append with size of space left for query string + null terminator */
        -+				size_t max_append = buflen - len - 1;
        -+				strncat(new_path, ri->query_string, max_append);
        - 			}
         +
        - 			mg_send_http_redirect(conn, new_path, 301);
        - 			mg_free(new_path);
        + static struct mg_connection *
        + mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        +                                  int use_ssl,
        +@@ -19009,7 +19541,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        + 
        + #if defined(USE_WEBSOCKET)
        + 	struct websocket_client_thread_data *thread_data;
        +-	static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
        ++	char magic[32];
        ++	generate_websocket_magic(magic);
        + 
        + 	const char *host = client_options->host;
        + 	int i;
        +@@ -19173,7 +19706,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        + 	/* Now upgrade to ws/wss client context */
        + 	conn->phys_ctx->user_data = user_data;
        + 	conn->phys_ctx->context_type = CONTEXT_WS_CLIENT;
        +-	conn->phys_ctx->cfg_worker_threads = 1; /* one worker thread */
        ++	conn->phys_ctx->cfg_max_worker_threads = 1; /* one worker thread */
        ++	conn->phys_ctx->spawned_worker_threads = 1; /* one worker thread */
        + 
        + 	/* Start a thread to read the websocket client connection
        + 	 * This thread will automatically stop when mg_disconnect is
        +@@ -19182,7 +19716,7 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        + 	                            thread_data,
        + 	                            conn->phys_ctx->worker_threadids)
        + 	    != 0) {
        +-		conn->phys_ctx->cfg_worker_threads = 0;
        ++		conn->phys_ctx->spawned_worker_threads = 0;
        + 		mg_free(thread_data);
        + 		mg_close_connection(conn);
        + 		conn = NULL;
        +@@ -19224,6 +19758,9 @@ mg_connect_websocket_client(const char *host,
        + 	memset(&client_options, 0, sizeof(client_options));
        + 	client_options.host = host;
        + 	client_options.port = port;
        ++	if (use_ssl) {
        ++		client_options.host_name = host;
        ++	}
        + 
        + 	return mg_connect_websocket_client_impl(&client_options,
        + 	                                        use_ssl,
        +@@ -19553,6 +20090,9 @@ process_new_connection(struct mg_connection *conn)
        + #endif
        + }
        + 
        ++static int
        ++mg_start_worker_thread(struct mg_context *ctx,
        ++                       int only_if_no_idle_threads); /* forward declaration */
        + 
        + #if defined(ALTERNATIVE_QUEUE)
        + 
        +@@ -19561,8 +20101,12 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        + {
        + 	unsigned int i;
        + 
        ++	(void)mg_start_worker_thread(
        ++	    ctx, 1); /* will start a worker-thread only if there aren't currently
        ++	                any idle worker-threads */
        ++
        + 	while (!ctx->stop_flag) {
        +-		for (i = 0; i < ctx->cfg_worker_threads; i++) {
        ++		for (i = 0; i < ctx->spawned_worker_threads; i++) {
        + 			/* find a free worker slot and signal it */
        + 			if (ctx->client_socks[i].in_use == 2) {
        + 				(void)pthread_mutex_lock(&ctx->thread_mutex);
        +@@ -19587,10 +20131,18 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        + 
        + 
        + static int
        +-consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
        ++consume_socket(struct mg_context *ctx,
        ++               struct socket *sp,
        ++               int thread_index,
        ++               int counter_was_preincremented)
        + {
        + 	DEBUG_TRACE("%s", "going idle");
        + 	(void)pthread_mutex_lock(&ctx->thread_mutex);
        ++	if (counter_was_preincremented
        ++	    == 0) { /* first call only: the master-thread pre-incremented this
        ++		           before he spawned us */
        ++		ctx->idle_worker_thread_count++;
        ++	}
        + 	ctx->client_socks[thread_index].in_use = 2;
        + 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        + 
        +@@ -19607,6 +20159,7 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
          		}
        --- 
        -2.34.1
        -
        -
        -From d5321963b1d0bc953101de91f8588bf83db73bf5 Mon Sep 17 00:00:00 2001
        -From: krispybyte 
        -Date: Sun, 22 Jun 2025 00:23:06 +0300
        -Subject: [PATCH 137/173] Fit code style
        -
        ----
        - src/civetweb.c | 4 ++--
        - 1 file changed, 2 insertions(+), 2 deletions(-)
        -
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index e969c939..6af91f87 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -15596,14 +15596,14 @@ handle_request(struct mg_connection *conn)
        - 			if (len + 1 < buflen) {
        - 				new_path[len] = '/';
        - 				new_path[len + 1] = '\0';
        --				len += 1;
        -+				len++;
        - 			}
        + 		return 0;
        + 	}
        ++	ctx->idle_worker_thread_count--;
        + 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        + 	if (sp->in_use == 1) {
        + 		DEBUG_TRACE("grabbed socket %d, going busy", sp->sock);
        +@@ -19621,12 +20174,20 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
          
        - 			if (ri->query_string) {
        - 				if (len + 1 < buflen) {
        - 					new_path[len] = '?';
        - 					new_path[len + 1] = '\0';
        --					len += 1;
        -+					len++;
        - 				}
        + /* Worker threads take accepted socket from the queue */
        + static int
        +-consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
        ++consume_socket(struct mg_context *ctx,
        ++               struct socket *sp,
        ++               int thread_index,
        ++               int counter_was_preincremented)
        + {
        + 	(void)thread_index;
          
        - 				/* Append with size of space left for query string + null terminator */
        --- 
        -2.34.1
        -
        -
        -From e788cece2d5051c31af4d68c668bc425429eb195 Mon Sep 17 00:00:00 2001
        -From: Geeoon Chung 
        -Date: Mon, 7 Jul 2025 17:44:10 -0500
        -Subject: [PATCH 138/173] Fixed typos where CivetWeb was spelled CiwetWeb
        -
        ----
        - README.md           | 2 +-
        - test/page4.lp       | 4 ++--
        - test/page4kepler.lp | 4 ++--
        - 3 files changed, 5 insertions(+), 5 deletions(-)
        -
        -diff --git a/README.md b/README.md
        -index dae05879..c3e6f0ce 100644
        ---- a/README.md
        -+++ b/README.md
        -@@ -182,7 +182,7 @@ CivetWeb is based on the [Mongoose project](https://github.com/cesanta/mongoose)
        - Sergey Lyubka(2004-2013) who released it under the MIT license.
        - However, on August 16, 2013,
        - [Mongoose was relicensed to a dual GPL V2 + commercial license](https://groups.google.com/forum/#!topic/mongoose-users/aafbOnHonkI)
        --and CiwetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose".
        -+and CivetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose".
        - The license change and CivetWeb fork was mentioned on the Mongoose
        - [Wikipedia](https://en.wikipedia.org/wiki/Mongoose_(web_server))
        - page as well, but it's getting deleted (and added again) there every
        -diff --git a/test/page4.lp b/test/page4.lp
        -index 52374907..7a4934e3 100644
        ---- a/test/page4.lp
        -+++ b/test/page4.lp
        -@@ -22,10 +22,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr
        - 

        Tags

        +- (void)pthread_mutex_lock(&ctx->thread_mutex); + DEBUG_TRACE("%s", "going idle"); ++ (void)pthread_mutex_lock(&ctx->thread_mutex); ++ if (counter_was_preincremented ++ == 0) { /* first call only: the master-thread pre-incremented this ++ before he spawned us */ ++ ctx->idle_worker_thread_count++; ++ } - --<? greeting = 'CiwetWeb' ?>
        -+<? greeting = 'CivetWeb' ?>
        - <strong><?= greeting %></strong>
        -

        -- -+ - ==>
        + /* If the queue is empty, wait. We're idle at this point. */ + while ((ctx->sq_head == ctx->sq_tail) +@@ -19650,6 +20211,8 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index) + } -
        -diff --git a/test/page4kepler.lp b/test/page4kepler.lp -index 8a986b5d..a7054897 100644 ---- a/test/page4kepler.lp -+++ b/test/page4kepler.lp -@@ -19,10 +19,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr -

        Tags

        + (void)pthread_cond_signal(&ctx->sq_empty); ++ ++ ctx->idle_worker_thread_count--; + (void)pthread_mutex_unlock(&ctx->thread_mutex); - --<? greeting = 'CiwetWeb' ?>
        -+<? greeting = 'CivetWeb' ?>
        - <strong><?= greeting %></strong>
        -

        -- -+ - ==>
        + return STOP_FLAG_IS_ZERO(&ctx->stop_flag); +@@ -19696,6 +20259,10 @@ produce_socket(struct mg_context *ctx, const struct socket *sp) -
        --- -2.34.1 - - -From db3aebf784da75d8fd7fae8ddc5a03dc4a787ff0 Mon Sep 17 00:00:00 2001 -From: "Marcel F." -Date: Wed, 13 Aug 2025 10:46:10 +0200 -Subject: [PATCH 139/173] Add `replace_asterisk_with_origin` option for - Acces-Control-Allow-Origin - -If you have multiple allowed origins and require `allow_credentials` to be true, CORS might be a problem since it blocks all requests with Allow-Origin "*" in combination with Credentials. Therefore this feature adds a config paramter `replace_asterisk_with_origin`. If it is set to `yes` and the passed `Access-Control-Allow-Origin` is an asterisk, this will be replaced by the origin of the request in the corresponding response. ---- - src/civetweb.c | 22 ++++++++++++++++------ - 1 file changed, 16 insertions(+), 6 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index bbc9aa8b..90943bb1 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -239,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func, + (void)pthread_cond_signal(&ctx->sq_full); + (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ ++ (void)mg_start_worker_thread( ++ ctx, 1); /* will start a worker-thread only if there aren't currently ++ any idle worker-threads */ + } + #endif /* ALTERNATIVE_QUEUE */ - #define NEED_DEBUG_TRACE_FUNC - #if !defined(DEBUG_TRACE_STREAM) --#define DEBUG_TRACE_STREAM stderr -+#define DEBUG_TRACE_STREAM stdout - #endif +@@ -19706,6 +20273,7 @@ worker_thread_run(struct mg_connection *conn) + struct mg_context *ctx = conn->phys_ctx; + int thread_index; + struct mg_workerTLS tls; ++ int first_call_to_consume_socket = 1; + + mg_set_thread_name("worker"); + +@@ -19731,7 +20299,7 @@ worker_thread_run(struct mg_connection *conn) + /* Connection structure has been pre-allocated */ + thread_index = (int)(conn - ctx->worker_connections); + if ((thread_index < 0) +- || ((unsigned)thread_index >= (unsigned)ctx->cfg_worker_threads)) { ++ || ((unsigned)thread_index >= (unsigned)ctx->cfg_max_worker_threads)) { + mg_cry_ctx_internal(ctx, + "Internal error: Invalid worker index %i", + thread_index); +@@ -19772,7 +20340,9 @@ worker_thread_run(struct mg_connection *conn) + /* Call consume_socket() even when ctx->stop_flag > 0, to let it + * signal sq_empty condvar to wake up the master waiting in + * produce_socket() */ +- while (consume_socket(ctx, &conn->client, thread_index)) { ++ while (consume_socket( ++ ctx, &conn->client, thread_index, first_call_to_consume_socket)) { ++ first_call_to_consume_socket = 0; + + /* New connections must start with new protocol negotiation */ + tls.alpn_proto = NULL; +@@ -19795,6 +20365,10 @@ worker_thread_run(struct mg_connection *conn) + sockaddr_to_string(conn->request_info.remote_addr, + sizeof(conn->request_info.remote_addr), + &conn->client.rsa); ++ ++ sockaddr_to_string(conn->request_info.server_addr, ++ sizeof(conn->request_info.server_addr), ++ &conn->client.lsa); + + DEBUG_TRACE("Incoming %sconnection from %s", + (conn->client.is_ssl ? "SSL " : ""), +@@ -19822,6 +20396,24 @@ worker_thread_run(struct mg_connection *conn) + close_connection(conn); + } + ++#elif defined(USE_GNUTLS) ++ /* HTTPS connection */ ++ if (gtls_ssl_accept(&(conn->ssl), ++ conn->dom_ctx->ssl_ctx, ++ conn->client.sock, ++ conn->phys_ctx) ++ == 0) { ++ /* conn->dom_ctx is set in get_request */ ++ /* process HTTPS connection */ ++ init_connection(conn); ++ conn->connection_type = CONNECTION_TYPE_REQUEST; ++ conn->protocol_type = PROTOCOL_TYPE_HTTP1; ++ process_new_connection(conn); ++ } else { ++ /* make sure the connection is cleaned up on SSL failure */ ++ close_connection(conn); ++ } ++ + #elif !defined(NO_SSL) + /* HTTPS connection */ + if (sslize(conn, SSL_accept, NULL)) { +@@ -19979,6 +20571,7 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx) + set_close_on_exec(so.sock, NULL, ctx); + so.is_ssl = listener->is_ssl; + so.ssl_redir = listener->ssl_redir; ++ so.is_optional = listener->is_optional; + if (getsockname(so.sock, &so.lsa.sa, &len) != 0) { + mg_cry_ctx_internal(ctx, + "%s: getsockname() failed: %s", +@@ -20048,7 +20641,7 @@ master_thread_run(struct mg_context *ctx) + unsigned int i; + unsigned int workerthreadcount; + +- if (!ctx) { ++ if (!ctx || !ctx->listening_socket_fds) { + return; + } + +@@ -20127,8 +20720,17 @@ master_thread_run(struct mg_context *ctx) + pfd[i].events = POLLIN; + } + ++ /* We listen on this socket just so that mg_stop() can cause mg_poll() ++ * to return ASAP. Don't worry, we did allocate an extra slot at the end ++ * of listening_socket_fds[] just to hold this ++ */ ++ pfd[ctx->num_listening_sockets].fd = ++ ctx->thread_shutdown_notification_socket; ++ pfd[ctx->num_listening_sockets].events = POLLIN; ++ + if (mg_poll(pfd, +- ctx->num_listening_sockets, ++ ctx->num_listening_sockets ++ + 1, // +1 for the thread_shutdown_notification_socket + SOCKET_TIMEOUT_QUANTUM, + &(ctx->stop_flag)) + > 0) { +@@ -20154,7 +20756,7 @@ master_thread_run(struct mg_context *ctx) + /* Wakeup workers that are waiting for connections to handle. */ + #if defined(ALTERNATIVE_QUEUE) +- for (i = 0; i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; i < ctx->spawned_worker_threads; i++) { + event_signal(ctx->client_wait_events[i]); + } #else -@@ -2087,7 +2087,7 @@ enum { - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - LUA_WEBSOCKET_EXTENSIONS, - #endif -- -+ REPLACE_ASTERISK_WITH_ORIGIN, - ACCESS_CONTROL_ALLOW_ORIGIN, - ACCESS_CONTROL_ALLOW_METHODS, - ACCESS_CONTROL_ALLOW_HEADERS, -@@ -2146,7 +2146,6 @@ static const struct mg_option config_options[] = { - #if defined(USE_HTTP2) - {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"}, +@@ -20164,7 +20766,7 @@ master_thread_run(struct mg_context *ctx) #endif -- - /* Once for each domain */ - {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, - {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, -@@ -2253,6 +2252,7 @@ static const struct mg_option config_options[] = { - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, + + /* Join all worker threads to avoid leaking threads. */ +- workerthreadcount = ctx->cfg_worker_threads; ++ workerthreadcount = ctx->spawned_worker_threads; + for (i = 0; i < workerthreadcount; i++) { + if (ctx->worker_threadids[i] != 0) { + mg_join_thread(ctx->worker_threadids[i]); +@@ -20268,7 +20870,7 @@ free_context(struct mg_context *ctx) + #if defined(ALTERNATIVE_QUEUE) + mg_free(ctx->client_socks); + if (ctx->client_wait_events != NULL) { +- for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; (unsigned)i < ctx->spawned_worker_threads; i++) { + event_destroy(ctx->client_wait_events[i]); + } + mg_free(ctx->client_wait_events); +@@ -20286,6 +20888,14 @@ free_context(struct mg_context *ctx) + (void)pthread_mutex_destroy(&ctx->lua_bg_mutex); #endif -+ {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"}, - {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, -@@ -3482,7 +3482,7 @@ mg_cry_internal_impl(const struct mg_connection *conn, - DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf); - if (!conn) { -- fputs(buf, stderr); -+ puts(buf); - return; ++ /* Deallocate shutdown-triggering socket-pair */ ++ if (ctx->user_shutdown_notification_socket >= 0) { ++ closesocket(ctx->user_shutdown_notification_socket); ++ } ++ if (ctx->thread_shutdown_notification_socket >= 0) { ++ closesocket(ctx->thread_shutdown_notification_socket); ++ } ++ + /* Deallocate config parameters */ + for (i = 0; i < NUM_OPTIONS; i++) { + if (ctx->dd.config[i] != NULL) { +@@ -20311,6 +20921,13 @@ free_context(struct mg_context *ctx) + ctx->dd.ssl_ctx = NULL; } -@@ -4235,15 +4235,24 @@ send_cors_header(struct mg_connection *conn) - const char *cors_meth_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; ++#elif defined(USE_GNUTLS) ++ if (ctx->dd.ssl_ctx != NULL) { ++ gtls_sslctx_uninit(ctx->dd.ssl_ctx); ++ mg_free(ctx->dd.ssl_ctx); ++ ctx->dd.ssl_ctx = NULL; ++ } ++ + #elif !defined(NO_SSL) + /* Deallocate SSL context */ + if (ctx->dd.ssl_ctx != NULL) { +@@ -20362,6 +20979,12 @@ mg_stop(struct mg_context *ctx) + /* Set stop flag, so all threads know they have to exit. */ + STOP_FLAG_ASSIGN(&ctx->stop_flag, 1); -+ int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -+ - if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { - /* Cross-origin resource sharing (CORS), see - * http://www.html5rocks.com/en/tutorials/cors/, - * http://www.html5rocks.com/static/images/cors_server_flowchart.png - * CORS preflight is not supported for files. */ -- mg_response_header_add(conn, -+ if (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') { -+ mg_response_header_add(conn, -+ "Access-Control-Allow-Origin", -+ origin_hdr, -+ -1); -+ } else { -+ mg_response_header_add(conn, - "Access-Control-Allow-Origin", - cors_orig_cfg, - -1); -+ } - } ++ /* Closing this socket will cause mg_poll() in all the I/O threads to return ++ * immediately */ ++ closesocket(ctx->user_shutdown_notification_socket); ++ ctx->user_shutdown_notification_socket = ++ -1; /* to avoid calling closesocket() again in free_context() */ ++ + /* Join timer thread */ + #if defined(USE_TIMERS) + timers_exit(ctx); +@@ -20421,6 +21044,8 @@ get_system_name(char **sysName) - if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) { -@@ -15159,6 +15168,7 @@ handle_request(struct mg_connection *conn) - const char *cors_acrm = get_header(ri->http_headers, - ri->num_headers, - "Access-Control-Request-Method"); -+ int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); + *sysName = mg_strdup(name); - /* Todo: check if cors_origin is in cors_orig_cfg. - * Or, let the client check this. */ -@@ -15186,7 +15196,7 @@ handle_request(struct mg_connection *conn) - "Content-Length: 0\r\n" - "Connection: %s\r\n", - date, -- cors_orig_cfg, -+ (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, - ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), - suggest_connection_header(conn)); ++#elif defined(__rtems__) ++ *sysName = mg_strdup("RTEMS"); + #elif defined(__ZEPHYR__) + *sysName = mg_strdup("Zephyr OS"); + #else +@@ -20459,13 +21084,122 @@ legacy_init(const char **options) + } + } --- -2.34.1 - - -From d24ca7d2d29ea3948ccb659dd20b5df95ccb4abd Mon Sep 17 00:00:00 2001 -From: "Marcel F." -Date: Wed, 13 Aug 2025 10:54:03 +0200 -Subject: [PATCH 140/173] Update civetweb.c - -Update to latest master ---- - src/civetweb.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 90943bb1..d11479f2 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -239,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func, ++/* we'll assume it's only Windows that doesn't have socketpair() available */ ++#if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32) ++#define HAVE_SOCKETPAIR 1 ++#endif ++ ++static int ++mg_socketpair(int *sockA, int *sockB) ++{ ++ int temp[2] = {-1, -1}; ++ int asock = -1; ++ ++ /** Default to unallocated */ ++ *sockA = -1; ++ *sockB = -1; ++ ++#if defined(HAVE_SOCKETPAIR) ++ int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, temp); ++ if (ret == 0) { ++ *sockA = temp[0]; ++ *sockB = temp[1]; ++ set_close_on_exec(*sockA, NULL, NULL); ++ set_close_on_exec(*sockB, NULL, NULL); ++ } ++ (void)asock; /* not used */ ++ return ret; ++#else ++ /** No socketpair() call is available, so we'll have to roll our own ++ * implementation */ ++ asock = socket(PF_INET, SOCK_STREAM, 0); ++ if (asock >= 0) { ++ struct sockaddr_in addr; ++ struct sockaddr *pa = (struct sockaddr *)&addr; ++ socklen_t addrLen = sizeof(addr); ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.sin_family = AF_INET; ++ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); ++ addr.sin_port = 0; ++ ++ if ((bind(asock, pa, sizeof(addr)) == 0) ++ && (getsockname(asock, pa, &addrLen) == 0) ++ && (listen(asock, 1) == 0)) { ++ temp[0] = socket(PF_INET, SOCK_STREAM, 0); ++ if ((temp[0] >= 0) && (connect(temp[0], pa, sizeof(addr)) == 0)) { ++ temp[1] = accept(asock, pa, &addrLen); ++ if (temp[1] >= 0) { ++ closesocket(asock); ++ *sockA = temp[0]; ++ *sockB = temp[1]; ++ set_close_on_exec(*sockA, NULL, NULL); ++ set_close_on_exec(*sockB, NULL, NULL); ++ return 0; /* success! */ ++ } ++ } ++ } ++ } ++ ++ /* Cleanup */ ++ if (asock >= 0) ++ closesocket(asock); ++ if (temp[0] >= 0) ++ closesocket(temp[0]); ++ if (temp[1] >= 0) ++ closesocket(temp[1]); ++ return -1; /* fail! */ ++#endif ++} ++ ++static int ++mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads) ++{ ++ const unsigned int i = ctx->spawned_worker_threads; ++ if (i >= ctx->cfg_max_worker_threads) { ++ return -1; /* Oops, we hit our worker-thread limit! No more worker ++ threads, ever! */ ++ } ++ ++ (void)pthread_mutex_lock(&ctx->thread_mutex); ++#if defined(ALTERNATIVE_QUEUE) ++ if ((only_if_no_idle_threads) && (ctx->idle_worker_thread_count > 0)) { ++#else ++ if ((only_if_no_idle_threads) ++ && (ctx->idle_worker_thread_count ++ > (unsigned)(ctx->sq_head - ctx->sq_tail))) { ++#endif ++ (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ return -2; /* There are idle threads available, so no need to spawn a ++ new worker thread now */ ++ } ++ ctx->idle_worker_thread_count++; /* we do this here to avoid a race ++ condition while the thread is starting ++ up */ ++ (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ ++ ctx->worker_connections[i].phys_ctx = ctx; ++ int ret = mg_start_thread_with_id(worker_thread, ++ &ctx->worker_connections[i], ++ &ctx->worker_threadids[i]); ++ if (ret == 0) { ++ ctx->spawned_worker_threads++; /* note that we've filled another slot in ++ the table */ ++ DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads); ++ } else { ++ (void)pthread_mutex_lock(&ctx->thread_mutex); ++ ctx->idle_worker_thread_count--; /* whoops, roll-back on error */ ++ (void)pthread_mutex_unlock(&ctx->thread_mutex); ++ } ++ return ret; ++} - #define NEED_DEBUG_TRACE_FUNC - #if !defined(DEBUG_TRACE_STREAM) --#define DEBUG_TRACE_STREAM stdout -+#define DEBUG_TRACE_STREAM stderr + CIVETWEB_API struct mg_context * + mg_start2(struct mg_init_data *init, struct mg_error_data *error) + { + struct mg_context *ctx; + const char *name, *value, *default_value; +- int idx, ok, workerthreadcount; ++ int idx, ok, prespawnthreadcount, workerthreadcount; + unsigned int i; + int itmp; + void (*exit_callback)(const struct mg_context *ctx) = 0; +@@ -20542,6 +21276,15 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + #if defined(USE_LUA) + ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr)); #endif ++ ++ /** mg_stop() will close the user_shutdown_notification_socket, and that ++ * will cause poll() to return immediately in the master-thread, so that ++ * mg_stop() can also return immediately. ++ */ ++ ok &= (0 ++ == mg_socketpair(&ctx->user_shutdown_notification_socket, ++ &ctx->thread_shutdown_notification_socket)); ++ + if (!ok) { + unsigned error_id = (unsigned)ERRNO; + const char *err_msg = +@@ -20709,6 +21452,13 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - #else -@@ -2146,6 +2146,7 @@ static const struct mg_option config_options[] = { - #if defined(USE_HTTP2) - {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"}, - #endif + /* Worker thread count option */ + workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]); ++ prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]); + - /* Once for each domain */ - {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, - {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL}, -@@ -3482,7 +3483,7 @@ mg_cry_internal_impl(const struct mg_connection *conn, - DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf); ++ if ((prespawnthreadcount < 0) ++ || (prespawnthreadcount > workerthreadcount)) { ++ prespawnthreadcount = ++ workerthreadcount; /* can't prespawn more than all of them! */ ++ } - if (!conn) { -- puts(buf); -+ fputs(buf, stderr); - return; + if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) { + if (workerthreadcount <= 0) { +@@ -20868,7 +21618,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) } + #endif --- -2.34.1 - - -From eac68303f2473207ea67533e109450ec053e5859 Mon Sep 17 00:00:00 2001 -From: "Marcel F." -Date: Wed, 13 Aug 2025 12:38:07 +0200 -Subject: [PATCH 141/173] Fix names - ---- - src/civetweb.c | 12 ++++++------ - 1 file changed, 6 insertions(+), 6 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index d11479f2..cffc7732 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -4235,15 +4235,15 @@ send_cors_header(struct mg_connection *conn) - conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; - const char *cors_meth_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; -- -- int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); - - if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { -+ int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -+ - /* Cross-origin resource sharing (CORS), see - * http://www.html5rocks.com/en/tutorials/cors/, - * http://www.html5rocks.com/static/images/cors_server_flowchart.png - * CORS preflight is not supported for files. */ -- if (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') { -+ if (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { - mg_response_header_add(conn, - "Access-Control-Allow-Origin", - origin_hdr, -@@ -15169,14 +15169,14 @@ handle_request(struct mg_connection *conn) - const char *cors_acrm = get_header(ri->http_headers, - ri->num_headers, - "Access-Control-Request-Method"); -- int cors_replace_asterisk_with_origin = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -- - /* Todo: check if cors_origin is in cors_orig_cfg. - * Or, let the client check this. */ - - if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) - && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) - && (cors_origin != NULL) && (cors_acrm != NULL)) { -+ int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -+ - /* This is a valid CORS preflight, and the server is configured - * to handle it automatically. */ - const char *cors_acrh = -@@ -15197,7 +15197,7 @@ handle_request(struct mg_connection *conn) - "Content-Length: 0\r\n" - "Connection: %s\r\n", - date, -- (cors_replace_asterisk_with_origin == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, -+ (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, - ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), - suggest_connection_header(conn)); +-#if defined(USE_MBEDTLS) ++#if defined(USE_MBEDTLS) || defined(USE_GNUTLS) + if (!mg_sslctx_init(ctx, NULL)) { + const char *err_msg = "Error initializing SSL context"; + /* Fatal error - abort start. */ +@@ -20973,10 +21723,11 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + return NULL; + } --- -2.34.1 - - -From 1eee9d7e673763d849fddab8ca9bd172aceff9b3 Mon Sep 17 00:00:00 2001 -From: "Marcel F." -Date: Wed, 13 Aug 2025 12:39:28 +0200 -Subject: [PATCH 142/173] Fix names - ---- - src/civetweb.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index cffc7732..62c51ac3 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -4237,13 +4237,13 @@ send_cors_header(struct mg_connection *conn) - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; - - if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { -- int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -+ int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); - - /* Cross-origin resource sharing (CORS), see - * http://www.html5rocks.com/en/tutorials/cors/, - * http://www.html5rocks.com/static/images/cors_server_flowchart.png - * CORS preflight is not supported for files. */ -- if (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { -+ if (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { - mg_response_header_add(conn, - "Access-Control-Allow-Origin", - origin_hdr, -@@ -15175,7 +15175,7 @@ handle_request(struct mg_connection *conn) - if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) - && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) - && (cors_origin != NULL) && (cors_acrm != NULL)) { -- int cors_repla_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -+ int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); - - /* This is a valid CORS preflight, and the server is configured - * to handle it automatically. */ -@@ -15197,7 +15197,7 @@ handle_request(struct mg_connection *conn) - "Content-Length: 0\r\n" - "Connection: %s\r\n", - date, -- (cors_repla_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, -+ (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, - ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), - suggest_connection_header(conn)); +- ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount)); +- ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_worker_threads, +- sizeof(pthread_t), +- ctx); ++ ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount)); ++ ctx->worker_threadids = ++ (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads, ++ sizeof(pthread_t), ++ ctx); --- -2.34.1 - - -From 0530ed0f2ac1cf9eb57b2caf8779cc5447097c94 Mon Sep 17 00:00:00 2001 -From: "Marcel F." -Date: Wed, 13 Aug 2025 13:56:32 +0200 -Subject: [PATCH 143/173] Update civetweb.c - ---- - src/civetweb.c | 18 ++++++++++++------ - 1 file changed, 12 insertions(+), 6 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 62c51ac3..cb2e9a17 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -4235,15 +4235,17 @@ send_cors_header(struct mg_connection *conn) - conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; - const char *cors_meth_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; -+ const char *cors_repl_asterisk_with_orig_cfg = -+ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; - -- if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) { -- int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -+ if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr && cors_repl_asterisk_with_orig_cfg && *cors_repl_asterisk_with_orig_cfg) { -+ int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); - - /* Cross-origin resource sharing (CORS), see - * http://www.html5rocks.com/en/tutorials/cors/, - * http://www.html5rocks.com/static/images/cors_server_flowchart.png - * CORS preflight is not supported for files. */ -- if (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') { -+ if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') { - mg_response_header_add(conn, - "Access-Control-Allow-Origin", - origin_hdr, -@@ -15169,13 +15171,17 @@ handle_request(struct mg_connection *conn) - const char *cors_acrm = get_header(ri->http_headers, - ri->num_headers, - "Access-Control-Request-Method"); -+ const char *cors_repl_asterisk_with_orig_cfg = -+ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; -+ - /* Todo: check if cors_origin is in cors_orig_cfg. - * Or, let the client check this. */ + if (ctx->worker_threadids == NULL) { + const char *err_msg = "Not enough memory for worker thread ID array"; +@@ -20984,8 +21735,8 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) - && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) -- && (cors_origin != NULL) && (cors_acrm != NULL)) { -- int cors_repl_asterisk_with_orig_cfg = mg_strcasecmp(conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN], "yes"); -+ && (cors_origin != NULL) && (cors_acrm != NULL) -+ && (cors_repl_asterisk_with_orig_cfg != NULL) && (*cors_repl_asterisk_with_orig_cfg != 0)) { -+ int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); - - /* This is a valid CORS preflight, and the server is configured - * to handle it automatically. */ -@@ -15197,7 +15203,7 @@ handle_request(struct mg_connection *conn) - "Content-Length: 0\r\n" - "Connection: %s\r\n", - date, -- (cors_repl_asterisk_with_orig_cfg == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, -+ (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, - ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), - suggest_connection_header(conn)); + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; +- error->code_sub = +- (unsigned)ctx->cfg_worker_threads * (unsigned)sizeof(pthread_t); ++ error->code_sub = (unsigned)ctx->cfg_max_worker_threads ++ * (unsigned)sizeof(pthread_t); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ + error->text, +@@ -20999,7 +21750,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + return NULL; + } + ctx->worker_connections = +- (struct mg_connection *)mg_calloc_ctx(ctx->cfg_worker_threads, ++ (struct mg_connection *)mg_calloc_ctx(ctx->cfg_max_worker_threads, + sizeof(struct mg_connection), + ctx); + if (ctx->worker_connections == NULL) { +@@ -21009,7 +21760,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) --- -2.34.1 - - -From 15ada377340fdee385b27bfbe14b0bf9f6336385 Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sat, 16 Aug 2025 10:46:32 +0000 -Subject: [PATCH 144/173] Bump actions/checkout from 4 to 5 - -Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. -- [Release notes](https://github.com/actions/checkout/releases) -- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) -- [Commits](https://github.com/actions/checkout/compare/v4...v5) - ---- -updated-dependencies: -- dependency-name: actions/checkout - dependency-version: '5' - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/cibuild.yml | 2 +- - .github/workflows/codeql.yml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index d8e1cc6c..04773117 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -392,7 +392,7 @@ jobs: + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; +- error->code_sub = (unsigned)ctx->cfg_worker_threads ++ error->code_sub = (unsigned)ctx->cfg_max_worker_threads + * (unsigned)sizeof(struct mg_connection); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ +@@ -21026,7 +21777,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - steps: - - name: Checkout code -- uses: actions/checkout@v4.1.7 -+ uses: actions/checkout@v5 + #if defined(ALTERNATIVE_QUEUE) + ctx->client_wait_events = +- (void **)mg_calloc_ctx(ctx->cfg_worker_threads, ++ (void **)mg_calloc_ctx(ctx->cfg_max_worker_threads, + sizeof(ctx->client_wait_events[0]), + ctx); + if (ctx->client_wait_events == NULL) { +@@ -21036,7 +21787,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) - - name: Export number of CPUs - run: | -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index d9f751b4..22fad79d 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -45,7 +45,7 @@ jobs: + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; +- error->code_sub = (unsigned)ctx->cfg_worker_threads ++ error->code_sub = (unsigned)ctx->cfg_max_worker_threads + * (unsigned)sizeof(ctx->client_wait_events[0]); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ +@@ -21052,7 +21803,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + } - steps: - - name: Checkout repository -- uses: actions/checkout@v4 -+ uses: actions/checkout@v5 - with: - submodules: recursive + ctx->client_socks = +- (struct socket *)mg_calloc_ctx(ctx->cfg_worker_threads, ++ (struct socket *)mg_calloc_ctx(ctx->cfg_max_worker_threads, + sizeof(ctx->client_socks[0]), + ctx); + if (ctx->client_socks == NULL) { +@@ -21063,7 +21814,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) --- -2.34.1 - - -From cfe55d75cca2adc8c4077b4d1ee24441eef201b0 Mon Sep 17 00:00:00 2001 -From: Aryan Rahar -Date: Thu, 21 Aug 2025 20:10:32 +0000 -Subject: [PATCH 145/173] civetweb: avoid duplicate uri_len computations in - handle_request - -- Compute URI length once after local_uri is finalized. -- Use cached value in directory trailing-slash check. -- Skip computing in NO_FILES builds. -- No functional change. ---- - src/civetweb.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index bbc9aa8b..2e93236b 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -15079,7 +15079,7 @@ handle_request(struct mg_connection *conn) - } - return; + if (error != NULL) { + error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY; +- error->code_sub = (unsigned)ctx->cfg_worker_threads ++ error->code_sub = (unsigned)ctx->cfg_max_worker_threads + * (unsigned)sizeof(ctx->client_socks[0]); + mg_snprintf(NULL, + NULL, /* No truncation check for error buffers */ +@@ -21078,7 +21829,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + return NULL; } -- uri_len = (int)strlen(ri->local_uri); -+ - /* 1.3. decode url (if config says so) */ - if (should_decode_url(conn)) { -@@ -15107,6 +15107,12 @@ handle_request(struct mg_connection *conn) - } - remove_dot_segments(tmp); - ri->local_uri = tmp; -+ #if !defined(NO_FILES) /* Only compute if later code can actually use it */ -+ /* Cache URI length once; recompute only if the buffer changes later. */ -+ uri_len = (int)strlen(ri->local_uri); -+ #endif -+ -+ +- for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) { + ctx->client_wait_events[i] = event_create(); + if (ctx->client_wait_events[i] == 0) { + const char *err_msg = "Error creating worker event %i"; +@@ -21142,23 +21893,18 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error) + ctx->context_type = CONTEXT_SERVER; /* server context */ - /* step 1. completed, the url is known now */ - DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri); -@@ -15577,8 +15583,8 @@ handle_request(struct mg_connection *conn) - } + /* Start worker threads */ +- for (i = 0; i < ctx->cfg_worker_threads; i++) { ++ for (i = 0; (int)i < prespawnthreadcount; i++) { + /* worker_thread sets up the other fields */ +- ctx->worker_connections[i].phys_ctx = ctx; +- if (mg_start_thread_with_id(worker_thread, +- &ctx->worker_connections[i], +- &ctx->worker_threadids[i]) +- != 0) { +- ++ if (mg_start_worker_thread(ctx, 0) != 0) { + long error_no = (long)ERRNO; - /* 12. Directory uris should end with a slash */ -- if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0) -- && (ri->local_uri[uri_len - 1] != '/')) { -+ if (file.stat.is_directory && (uri_len > 0) -+ && (ri->local_uri[uri_len - 1] != '/')) { + /* thread was not created */ +- if (i > 0) { ++ if (ctx->spawned_worker_threads > 0) { + /* If the second, third, ... thread cannot be created, set a + * warning, but keep running. */ + mg_cry_ctx_internal(ctx, + "Cannot start worker thread %i: error %ld", +- i + 1, ++ ctx->spawned_worker_threads + 1, + error_no); - /* Path + server root */ - size_t buflen = UTF8_PATH_MAX * 2 + 2; --- -2.34.1 - - -From 76c9260abc25d5c04c9c14c6ecbfa80599892568 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Tue, 2 Sep 2025 10:22:22 +0200 -Subject: [PATCH 146/173] Fix: Server with multiple SSL certificates and - mg_set_request_handler() not working - -Fixes #1335 ---- - src/civetweb.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index bbc9aa8b..0756b6d1 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -22020,7 +22020,7 @@ mg_start_domain2(struct mg_context *ctx, + /* If the server initialization should stop here, all +@@ -21348,7 +22094,7 @@ mg_start_domain2(struct mg_context *ctx, } } @@ -13763,94 +5444,139 @@ index bbc9aa8b..0756b6d1 100644 new_dom->next = NULL; new_dom->nonce_count = 0; new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31); --- -2.34.1 - - -From 26aa1ecf6e4f38dfdac43a44ba486341b048ffc3 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Tue, 2 Sep 2025 11:09:31 +0200 -Subject: [PATCH 147/173] Fix If-None-Match precedence (fixes caching problems - for static content) - -Fixes #1342 ---- - src/civetweb.c | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 0756b6d1..33ab8bd7 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -10783,9 +10783,13 @@ is_not_modified(const struct mg_connection *conn, - const char *inm = mg_get_header(conn, "If-None-Match"); - construct_etag(etag, sizeof(etag), filestat); +@@ -21357,7 +22103,7 @@ mg_start_domain2(struct mg_context *ctx, + new_dom->shared_lua_websockets = NULL; + #endif -- return ((inm != NULL) && !mg_strcasecmp(etag, inm)) -- || ((ims != NULL) -- && (filestat->last_modified <= parse_date_string(ims))); -+ if (inm) { -+ return !mg_strcasecmp(etag, inm); -+ } -+ if (ims) { -+ return (filestat->last_modified <= parse_date_string(ims)); -+ } -+ return 0; - } +-#if !defined(NO_SSL) && !defined(USE_MBEDTLS) ++#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) + if (!init_ssl_ctx(ctx, new_dom)) { + /* Init SSL failed */ + if (error != NULL) { +@@ -21436,7 +22182,7 @@ mg_check_feature(unsigned feature) + #if !defined(NO_FILES) + | MG_FEATURES_FILES + #endif +-#if !defined(NO_SSL) || defined(USE_MBEDTLS) ++#if !defined(NO_SSL) || defined(USE_MBEDTLS) || defined(USE_GNUTLS) + | MG_FEATURES_SSL + #endif + #if !defined(NO_CGI) +@@ -21593,13 +22339,23 @@ mg_get_system_info(char *buffer, int buflen) + (unsigned)si.dwNumberOfProcessors, + (unsigned)si.dwActiveProcessorMask); + system_info_length += mg_str_append(&buffer, end, block); +-#elif defined(__ZEPHYR__) ++#elif defined(__rtems__) + mg_snprintf(NULL, + NULL, + block, + sizeof(block), + ",%s\"os\" : \"%s %s\"", + eol, ++ "RTEMS", ++ rtems_version()); ++ system_info_length += mg_str_append(&buffer, end, block); ++#elif defined(__ZEPHYR__) ++ mg_snprintf(NULL, ++ NULL, ++ block, ++ sizeof(block), ++ ",%s\"os\" : \"%s\"", ++ eol, + "Zephyr OS", + ZEPHYR_VERSION); + system_info_length += mg_str_append(&buffer, end, block); +@@ -22088,7 +22844,7 @@ mg_get_connection_info(const struct mg_context *ctx, + return 0; + } +- if ((unsigned)idx >= ctx->cfg_worker_threads) { ++ if ((unsigned)idx >= ctx->cfg_max_worker_threads) { + /* Out of range */ + return 0; + } +@@ -22298,44 +23054,44 @@ mg_get_connection_info(const struct mg_context *ctx, + return (int)connection_info_length; + } --- -2.34.1 - - -From 782e18903515f43bafbf2e668994e82bdfa51133 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Tue, 2 Sep 2025 14:08:41 +0200 -Subject: [PATCH 148/173] Make parsing of URL encoded forms more robust - -Reject requests that obviously violate the URL encoding. -Fixes #1348 ---- - src/civetweb.c | 7 ++++++- - src/handle_form.inl | 46 +++++++++++++++++++++++++++++++++++++-------- - 2 files changed, 44 insertions(+), 9 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 33ab8bd7..58e95c5f 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -1,4 +1,4 @@ --/* Copyright (c) 2013-2024 the Civetweb developers -+/* Copyright (c) 2013-2025 the Civetweb developers - * Copyright (c) 2004-2013 Sergey Lyubka - * - * Permission is hereby granted, free of charge, to any person obtaining a copy -@@ -7272,6 +7272,7 @@ mg_url_decode(const char *src, - int is_form_url_encoded) ++ + #if 0 +-/* Get handler information. It can be printed or stored by the caller. +- * Return the size of available information. */ ++/* Get handler information. Not fully implemented. Is it required? */ + CIVETWEB_API int + mg_get_handler_info(struct mg_context *ctx, +- char *buffer, +- int buflen) ++ char *buffer, ++ int buflen) { - int i, j, a, b; +- int handler_info_len = 0; +- struct mg_handler_info *tmp_rh; +- mg_lock_context(ctx); +- +- for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { +- +- if (buflen > handler_info_len+ tmp_rh->uri_len) { +- memcpy(buffer+handler_info_len, tmp_rh->uri, tmp_rh->uri_len); +- } +- handler_info_len += tmp_rh->uri_len; +- +- switch (tmp_rh->handler_type) { +- case REQUEST_HANDLER: +- (void)tmp_rh->handler; +- break; +- case WEBSOCKET_HANDLER: +- (void)tmp_rh->connect_handler; +- (void) tmp_rh->ready_handler; +- (void) tmp_rh->data_handler; +- (void) tmp_rh->close_handler; +- break; +- case AUTH_HANDLER: +- (void) tmp_rh->auth_handler; +- break; +- } +- (void)cbdata; +- } +- +- mg_unlock_context(ctx); +- return handler_info_len; ++ int handler_info_len = 0; ++ struct mg_handler_info *tmp_rh; ++ mg_lock_context(ctx); + - #define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W')) - - for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) { -@@ -7284,11 +7285,15 @@ mg_url_decode(const char *src, - i += 2; - } else if (is_form_url_encoded && (src[i] == '+')) { - dst[j] = ' '; -+ } else if ((unsigned char)src[i] <= ' ') { -+ return -1; /* invalid character */ - } else { - dst[j] = src[i]; - } - } - -+#undef HEXTOI ++ for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { + - dst[j] = '\0'; /* Null-terminate the destination */ - - return (i >= src_len) ? j : -1; ++ if (buflen > handler_info_len + tmp_rh->uri_len) { ++ memcpy(buffer + handler_info_len, tmp_rh->uri, tmp_rh->uri_len); ++ } ++ handler_info_len += tmp_rh->uri_len; ++ ++ switch (tmp_rh->handler_type) { ++ case REQUEST_HANDLER: ++ (void)tmp_rh->handler; ++ break; ++ case WEBSOCKET_HANDLER: ++ (void)tmp_rh->connect_handler; ++ (void)tmp_rh->ready_handler; ++ (void)tmp_rh->data_handler; ++ (void)tmp_rh->close_handler; ++ break; ++ case AUTH_HANDLER: ++ (void)tmp_rh->auth_handler; ++ break; ++ } ++ (void)cbdata; ++ } ++ ++ mg_unlock_context(ctx); ++ return handler_info_len; + } + #endif + #endif diff --git a/src/handle_form.inl b/src/handle_form.inl -index 14235ec9..cdff2a13 100644 +index be477a05..bc76f7d3 100644 --- a/src/handle_form.inl +++ b/src/handle_form.inl @@ -1,4 +1,4 @@ @@ -13890,8 +5616,8 @@ index 14235ec9..cdff2a13 100644 } - mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); -+ key_dec_len = mg_url_decode( -+ key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); ++ key_dec_len = ++ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); if (*value_len >= 2 && value[*value_len - 2] == '%') *value_len -= 2; @@ -13907,7 +5633,7 @@ index 14235ec9..cdff2a13 100644 ret = fdh->field_get(key_dec, value_dec, (size_t)value_dec_len, -@@ -136,9 +143,13 @@ unencoded_field_get(const struct mg_connection *conn, +@@ -136,9 +143,14 @@ unencoded_field_get(const struct mg_connection *conn, struct mg_form_data_handler *fdh) { char key_dec[1024]; @@ -13915,22 +5641,50 @@ index 14235ec9..cdff2a13 100644 (void)conn; - mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); -+ key_dec_len = mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); ++ key_dec_len = ++ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); + if (key_dec_len < 0) { + return MG_FORM_FIELD_STORAGE_ABORT; + } return fdh->field_get(key_dec, value, value_len, fdh->user_data); } -@@ -191,6 +202,7 @@ mg_handle_form_request(struct mg_connection *conn, - size_t buf_fill = 0; +@@ -162,14 +174,17 @@ search_boundary(const char *buf, + const char *boundary, + size_t boundary_len) + { +- /* We must do a binary search here, not a string search, since the buffer +- * may contain '\x00' bytes, if binary data is transferred. */ +- int clen = (int)buf_len - (int)boundary_len - 4; ++ char *boundary_start = "\r\n--"; ++ size_t boundary_start_len = strlen(boundary_start); ++ ++ /* We must do a binary search here, not a string search, since the ++ * buffer may contain '\x00' bytes, if binary data is transferred. */ ++ int clen = (int)buf_len - (int)boundary_len - boundary_start_len; + int i; + + for (i = 0; i <= clen; i++) { +- if (!memcmp(buf + i, "\r\n--", 4)) { +- if (!memcmp(buf + i + 4, boundary, boundary_len)) { ++ if (!memcmp(buf + i, boundary_start, boundary_start_len)) { ++ if (!memcmp(buf + i + boundary_start_len, boundary, boundary_len)) { + return buf + i; + } + } +@@ -185,9 +200,10 @@ mg_handle_form_request(struct mg_connection *conn, + char path[512]; + char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */ + int field_storage; +- int buf_fill = 0; ++ size_t buf_fill = 0; int r; int field_count = 0; + int abort_read = 0; struct mg_file fstore = STRUCT_FILE_INITIALIZER; int64_t file_size = 0; /* init here, to a avoid a false positive "uninitialized variable used" warning */ -@@ -281,6 +293,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -278,6 +294,7 @@ mg_handle_form_request(struct mg_connection *conn, conn, data, (size_t)keylen, val, (size_t *)&vallen, fdh); if (r == MG_FORM_FIELD_HANDLE_ABORT) { /* Stop request handling */ @@ -13938,7 +5692,7 @@ index 14235ec9..cdff2a13 100644 break; } if (r == MG_FORM_FIELD_HANDLE_NEXT) { -@@ -323,6 +336,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -320,6 +337,7 @@ mg_handle_form_request(struct mg_connection *conn, r = field_stored(conn, path, file_size, fdh); if (r == MG_FORM_FIELD_HANDLE_ABORT) { /* Stop request handling */ @@ -13946,7 +5700,7 @@ index 14235ec9..cdff2a13 100644 break; } -@@ -361,6 +375,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -358,6 +376,7 @@ mg_handle_form_request(struct mg_connection *conn, if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) == MG_FORM_FIELD_STORAGE_ABORT) { /* Stop parsing the request */ @@ -13954,7 +5708,7 @@ index 14235ec9..cdff2a13 100644 break; } -@@ -389,7 +404,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -386,7 +405,7 @@ mg_handle_form_request(struct mg_connection *conn, * Here we use "POST", and read the data from the request body. * The data read on the fly, so it is not required to buffer the * entire request in memory before processing it. */ @@ -13963,7 +5717,21 @@ index 14235ec9..cdff2a13 100644 const char *val; const char *next; ptrdiff_t keylen, vallen; -@@ -443,6 +458,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -394,10 +413,10 @@ mg_handle_form_request(struct mg_connection *conn, + int end_of_key_value_pair_found = 0; + int get_block; + +- if ((size_t)buf_fill < (sizeof(buf) - 1)) { ++ if (buf_fill < (sizeof(buf) - 1)) { + +- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ size_t to_read = sizeof(buf) - 1 - buf_fill; ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + /* read error */ + return -1; +@@ -440,6 +459,7 @@ mg_handle_form_request(struct mg_connection *conn, if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) == MG_FORM_FIELD_STORAGE_ABORT) { /* Stop parsing the request */ @@ -13971,23 +5739,23 @@ index 14235ec9..cdff2a13 100644 break; } -@@ -471,6 +487,15 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -468,6 +488,15 @@ mg_handle_form_request(struct mg_connection *conn, } else { vallen = (ptrdiff_t)strlen(val); end_of_key_value_pair_found = all_data_read; + if ((buf + buf_fill) > (val + vallen)) { -+ /* Avoid DoS attacks by having a zero byte in the middle of -+ * a request that is supposed to be URL encoded. Since this -+ * request is certainly invalid, according to the protocol ++ /* Avoid DoS attacks by having a zero byte in the middle ++ * of a request that is supposed to be URL encoded. ++ * Since this request is certainly invalid, according to ++ * the protocol + * specification, stop processing it. Fixes #1348 */ + abort_read = 1; + break; + } -+ } if (field_storage == MG_FORM_FIELD_STORAGE_GET) { -@@ -492,6 +517,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -489,6 +518,7 @@ mg_handle_form_request(struct mg_connection *conn, get_block++; if (r == MG_FORM_FIELD_HANDLE_ABORT) { /* Stop request handling */ @@ -13995,7 +5763,23 @@ index 14235ec9..cdff2a13 100644 break; } if (r == MG_FORM_FIELD_HANDLE_NEXT) { -@@ -560,7 +586,6 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -526,11 +556,11 @@ mg_handle_form_request(struct mg_connection *conn, + buf + (size_t)used, + sizeof(buf) - (size_t)used); + next = buf; +- buf_fill -= (int)used; +- if ((size_t)buf_fill < (sizeof(buf) - 1)) { ++ buf_fill -= used; ++ if (buf_fill < (sizeof(buf) - 1)) { + +- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ size_t to_read = sizeof(buf) - 1 - buf_fill; ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + #if !defined(NO_FILESYSTEMS) + /* read error */ +@@ -557,7 +587,6 @@ mg_handle_form_request(struct mg_connection *conn, val = buf; } } @@ -14003,7 +5787,7 @@ index 14235ec9..cdff2a13 100644 } while (!end_of_key_value_pair_found); #if !defined(NO_FILESYSTEMS) -@@ -571,6 +596,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -568,6 +597,7 @@ mg_handle_form_request(struct mg_connection *conn, r = field_stored(conn, path, file_size, fdh); if (r == MG_FORM_FIELD_HANDLE_ABORT) { /* Stop request handling */ @@ -14011,7 +5795,7 @@ index 14235ec9..cdff2a13 100644 break; } } else { -@@ -584,7 +610,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -581,7 +611,7 @@ mg_handle_form_request(struct mg_connection *conn, } #endif /* NO_FILESYSTEMS */ @@ -14020,7 +5804,132 @@ index 14235ec9..cdff2a13 100644 /* nothing more to process */ break; } -@@ -972,6 +998,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -589,7 +619,7 @@ mg_handle_form_request(struct mg_connection *conn, + /* Proceed to next entry */ + used = next - buf; + memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); +- buf_fill -= (int)used; ++ buf_fill -= used; + } + + return field_count; +@@ -624,6 +654,7 @@ mg_handle_form_request(struct mg_connection *conn, + } + + /* Copy boundary string to variable "boundary" */ ++ /* fbeg is pointer to start of value of boundary */ + fbeg = content_type + bl + 9; + bl = strlen(fbeg); + boundary = (char *)mg_malloc(bl + 1); +@@ -678,12 +709,12 @@ mg_handle_form_request(struct mg_connection *conn, + for (part_no = 0;; part_no++) { + size_t towrite, fnlen, n; + int get_block; +- size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; ++ size_t to_read = sizeof(buf) - 1 - buf_fill; + + /* Unused without filesystems */ + (void)n; + +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + /* read error */ + mg_free(boundary); +@@ -701,43 +732,74 @@ mg_handle_form_request(struct mg_connection *conn, + return -1; + } + ++ /* @see https://www.rfc-editor.org/rfc/rfc2046.html#section-5.1.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ */ ++ + if (part_no == 0) { +- int d = 0; +- while ((d < buf_fill) && (buf[d] != '-')) { +- d++; ++ size_t preamble_length = 0; ++ /* skip over the preamble until we find a complete boundary ++ * limit the preamble length to prevent abuse */ ++ /* +2 for the -- preceding the boundary */ ++ while (preamble_length < 1024 ++ && (preamble_length < buf_fill - bl) ++ && strncmp(buf + preamble_length + 2, boundary, bl)) { ++ preamble_length++; + } +- if ((d > 0) && (buf[d] == '-')) { +- memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d); +- buf_fill -= d; ++ /* reset the start of buf to remove the preamble */ ++ if (0 == strncmp(buf + preamble_length + 2, boundary, bl)) { ++ memmove(buf, ++ buf + preamble_length, ++ (unsigned)buf_fill - (unsigned)preamble_length); ++ buf_fill -= preamble_length; + buf[buf_fill] = 0; + } + } + +- if (buf[0] != '-' || buf[1] != '-') { ++ /* either it starts with a boundary and it's fine, or it's malformed ++ * because: ++ * - the preamble was longer than accepted ++ * - couldn't find a boundary at all in the body ++ * - didn't have a terminating boundary */ ++ if (buf_fill < (bl + 2) || strncmp(buf, "--", 2) ++ || strncmp(buf + 2, boundary, bl)) { + /* Malformed request */ + mg_free(boundary); + return -1; + } +- if (0 != strncmp(buf + 2, boundary, bl)) { +- /* Malformed request */ +- mg_free(boundary); +- return -1; ++ ++ /* skip the -- */ ++ char *boundary_start = buf + 2; ++ size_t transport_padding = 0; ++ while (boundary_start[bl + transport_padding] == ' ' ++ || boundary_start[bl + transport_padding] == '\t') { ++ transport_padding++; + } +- if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') { +- /* Every part must end with \r\n, if there is another part. +- * The end of the request has an extra -- */ +- if (((size_t)buf_fill != (size_t)(bl + 6)) +- || (strncmp(buf + bl + 2, "--\r\n", 4))) { ++ char *boundary_end = boundary_start + bl + transport_padding; ++ ++ /* after the transport padding, if the boundary isn't ++ * immediately followed by a \r\n then it is either... */ ++ if (strncmp(boundary_end, "\r\n", 2)) { ++ /* ...the final boundary, and it is followed by --, (in which ++ * case it's the end of the request) or it's a malformed ++ * request */ ++ if (strncmp(boundary_end, "--", 2)) { + /* Malformed request */ + mg_free(boundary); + return -1; + } +- /* End of the request */ ++ /* Ingore any epilogue here */ + break; + } + ++ /* skip the \r\n */ ++ hbuf = boundary_end + 2; + /* Next, we need to get the part header: Read until \r\n\r\n */ +- hbuf = buf + bl + 4; + hend = strstr(hbuf, "\r\n\r\n"); + if (!hend) { + /* Malformed request */ +@@ -937,6 +999,7 @@ mg_handle_form_request(struct mg_connection *conn, get_block++; if (r == MG_FORM_FIELD_HANDLE_ABORT) { /* Stop request handling */ @@ -14028,7 +5937,32 @@ index 14235ec9..cdff2a13 100644 break; } if (r == MG_FORM_FIELD_HANDLE_NEXT) { -@@ -1046,6 +1073,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -965,12 +1028,12 @@ mg_handle_form_request(struct mg_connection *conn, + #endif /* NO_FILESYSTEMS */ + + memmove(buf, hend + towrite, bl + 4); +- buf_fill = (int)(bl + 4); ++ buf_fill = bl + 4; + hend = buf; + + /* Read new data */ +- to_read = sizeof(buf) - 1 - (size_t)buf_fill; +- r = mg_read(conn, buf + (size_t)buf_fill, to_read); ++ to_read = sizeof(buf) - 1 - buf_fill; ++ r = mg_read(conn, buf + buf_fill, to_read); + if ((r < 0) || ((r == 0) && all_data_read)) { + #if !defined(NO_FILESYSTEMS) + /* read error */ +@@ -989,7 +1052,7 @@ mg_handle_form_request(struct mg_connection *conn, + /* buf_fill is at least 8 here */ + + /* Find boundary */ +- next = search_boundary(buf, (size_t)buf_fill, boundary, bl); ++ next = search_boundary(buf, buf_fill, boundary, bl); + + if (!next && (r == 0)) { + /* incomplete request */ +@@ -1011,6 +1074,7 @@ mg_handle_form_request(struct mg_connection *conn, fdh); if (r == MG_FORM_FIELD_HANDLE_ABORT) { /* Stop request handling */ @@ -14036,7 +5970,7 @@ index 14235ec9..cdff2a13 100644 break; } if (r == MG_FORM_FIELD_HANDLE_NEXT) { -@@ -1074,6 +1102,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -1039,6 +1103,7 @@ mg_handle_form_request(struct mg_connection *conn, r = field_stored(conn, path, file_size, fdh); if (r == MG_FORM_FIELD_HANDLE_ABORT) { /* Stop request handling */ @@ -14044,7 +5978,7 @@ index 14235ec9..cdff2a13 100644 break; } } else { -@@ -1092,6 +1121,7 @@ mg_handle_form_request(struct mg_connection *conn, +@@ -1057,6 +1122,7 @@ mg_handle_form_request(struct mg_connection *conn, if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT) == MG_FORM_FIELD_STORAGE_ABORT) { /* Stop parsing the request */ @@ -14052,541 +5986,464 @@ index 14235ec9..cdff2a13 100644 break; } --- -2.34.1 - - -From d8c74f7cce312036816efa832196d57dd044eb90 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Tue, 2 Sep 2025 15:38:08 +0200 -Subject: [PATCH 149/173] Update README - -Remove mentioning some old services that are no longer used/maintained ---- - README.md | 18 ++++-------------- - 1 file changed, 4 insertions(+), 14 deletions(-) - -diff --git a/README.md b/README.md -index dae05879..7165ed30 100644 ---- a/README.md -+++ b/README.md -@@ -9,18 +9,12 @@ - [![Forks](https://img.shields.io/github/forks/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/network/members) - [![Latest Release](https://img.shields.io/github/v/release/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/releases) - --Continuous integration for Linux and macOS ([Travis CI](https://app.travis-ci.com/github/civetweb/civetweb)): -- --[![Travis Build Status](https://api.travis-ci.com/civetweb/civetweb.svg?branch=master)](https://app.travis-ci.com/github/civetweb/civetweb) -- --Continuous integration for Windows ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): -+Continuous integration ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): - - [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/civetweb/civetweb?svg=true)](https://ci.appveyor.com/project/civetweb/civetweb/branch/master) +@@ -1064,7 +1130,7 @@ mg_handle_form_request(struct mg_connection *conn, + if (next) { + used = next - buf + 2; + memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); +- buf_fill -= (int)used; ++ buf_fill -= used; + } else { + buf_fill = 0; + } +diff --git a/src/http2.inl b/src/http2.inl +index cea695ed..a4bfd8d0 100644 +--- a/src/http2.inl ++++ b/src/http2.inl +@@ -1442,7 +1442,7 @@ handle_http2(struct mg_connection *conn) + hpack_decode(buf, &i, (int)bytes_read, conn->phys_ctx); + CHECK_LEAK_HDR_ALLOC(key); + if (!key) { +- DEBUG_TRACE("HTTP2 key decoding error"); ++ DEBUG_TRACE("%s", "HTTP2 key decoding error"); + goto clean_http2; + } + } else if (/*(idx >= 15) &&*/ (idx <= 61)) { +@@ -1509,7 +1509,7 @@ handle_http2(struct mg_connection *conn) + conn->phys_ctx); /* leak? */ + CHECK_LEAK_HDR_ALLOC(val); + if (!val) { +- DEBUG_TRACE("HTTP2 value decoding error"); ++ DEBUG_TRACE("%s", "HTTP2 value decoding error"); + mg_free((void *)key); + goto clean_http2; + } +diff --git a/src/main.c b/src/main.c +index 66510d0f..be859feb 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -41,7 +41,7 @@ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wreserved-id-macro" + #endif +-#if !defined(_XOPEN_SOURCE) ++#if !defined(_XOPEN_SOURCE) && !defined(USE_COCOA) + #define _XOPEN_SOURCE 600 /* For PATH_MAX on linux */ + /* This should also be sufficient for "realpath", according to + * http://man7.org/linux/man-pages/man3/realpath.3.html, but in +@@ -1142,6 +1142,7 @@ sanitize_options(const char *options[] /* server options */, + int ok = 1; + /* Make sure we have absolute paths for files and directories */ + set_absolute_path(options, "document_root", arg0); ++ set_absolute_path(options, "fallback_document_root", arg0); + set_absolute_path(options, "put_delete_auth_file", arg0); + set_absolute_path(options, "cgi_interpreter", arg0); + set_absolute_path(options, "access_log_file", arg0); +@@ -1155,6 +1156,8 @@ sanitize_options(const char *options[] /* server options */, + /* Make extra verification for certain options */ + if (!verify_existence(options, "document_root", 1)) + ok = 0; ++ if (!verify_existence(options, "fallback_document_root", 1)) ++ ok = 0; + if (!verify_existence(options, "cgi_interpreter", 0)) + ok = 0; + if (!verify_existence(options, "ssl_certificate", 0)) +@@ -2474,7 +2477,7 @@ show_settings_dialog(void) - Test coverage check ([coveralls](https://coveralls.io/github/civetweb/civetweb), [codecov](https://codecov.io/gh/civetweb/civetweb/branch/master)) (using different tools/settings): --[![Coveralls](https://img.shields.io/coveralls/civetweb/civetweb.svg?maxAge=3600)]() --[![Coverage Status](https://coveralls.io/repos/github/civetweb/civetweb/badge.svg?branch=master)](https://coveralls.io/github/civetweb/civetweb?branch=master) - [![codecov](https://codecov.io/gh/civetweb/civetweb/branch/master/graph/badge.svg)](https://codecov.io/gh/civetweb/civetweb) + static void +-change_password_file() ++change_password_file(void) + { + /* Parameter for size/format tuning of the dialog */ + short HEIGHT = 15; +@@ -2685,7 +2688,7 @@ sysinfo_reload(struct dlg_proc_param *prm) - Static source code analysis ([Coverity](https://scan.coverity.com/projects/5784)): [![Coverity Scan Build Status](https://scan.coverity.com/projects/5784/badge.svg)](https://scan.coverity.com/projects/5784) -@@ -54,18 +48,14 @@ CivetWeb must be used with an earlier or later version (see also [here](https:// - Bugs and requests should be filed on GitHub - [https://github.com/civetweb/civetweb/issues](https://github.com/civetweb/civetweb/issues) --New releases are announced on Google Groups --[https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb) -- --Formerly some support question and discussion threads have been at [Google groups](https://groups.google.com/d/forum/civetweb). --Recent questions and discussions use [GitHub issues](https://github.com/civetweb/civetweb/issues). -- - Source releases can be found on GitHub - [https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases) + int +-show_system_info() ++show_system_info(void) + { + /* Parameter for size/format tuning of the dialog */ + short HEIGHT = 15; +@@ -2793,8 +2796,9 @@ manage_service(int action) + show_error(); + } else if (action == ID_INSTALL_SERVICE) { + path[sizeof(path) - 1] = 0; +- GetModuleFileName(NULL, path, sizeof(path) - 1); +- strncat(path, " ", sizeof(path) - 1 - strlen(path)); ++ path[0]='"'; ++ GetModuleFileName(NULL, path+1, sizeof(path) - 3); ++ strncat(path, "\" ", sizeof(path) - 2 - strlen(path)); + strncat(path, service_magic_argument, sizeof(path) - 1 - strlen(path)); + hService = CreateService(hSCM, + service_name, +diff --git a/src/match.inl b/src/match.inl +index a5011f57..34ee00ef 100644 +--- a/src/match.inl ++++ b/src/match.inl +@@ -47,8 +47,8 @@ mg_match_impl(const char *pat, + /* Advance as long as there are ? */ + i_pat++; + i_str++; +- } while ((pat[i_pat] == '?') && (str[i_str] != '\0') +- && (str[i_str] != '/') && (i_pat < pat_len)); ++ } while ((i_pat < pat_len) && (pat[i_pat] == '?') ++ && (str[i_str] != '\0') && (str[i_str] != '/')); - A very brief overview can be found on GitHub Pages - [https://civetweb.github.io/civetweb/](https://civetweb.github.io/civetweb/) + /* If we have a match context, add the substring we just found */ + if (mcx) { +@@ -72,7 +72,7 @@ mg_match_impl(const char *pat, + ptrdiff_t ret; -+Note: The [Google group](https://groups.google.com/d/forum/civetweb) is no longer used but has been replaced by [GitHub issues](https://github.com/civetweb/civetweb/issues). + i_pat++; +- if ((pat[i_pat] == '*') && (i_pat < pat_len)) { ++ if ((i_pat < pat_len) && (pat[i_pat] == '*')) { + /* Pattern ** matches all */ + i_pat++; + len = strlen(str + i_str); +diff --git a/src/mod_gnutls.inl b/src/mod_gnutls.inl +new file mode 100644 +index 00000000..f765362b +--- /dev/null ++++ b/src/mod_gnutls.inl +@@ -0,0 +1,240 @@ ++#if defined(USE_GNUTLS) // USE_GNUTLS used with NO_SSL ++ ++#include ++#include ++ ++typedef struct { ++ gnutls_session_t sess; ++} SSL; ++typedef struct { ++ gnutls_certificate_credentials_t cred; ++ gnutls_priority_t prio; ++} SSL_CTX; ++ ++ ++/* public api */ ++CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt); ++CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx); ++CIVETWEB_API void gtls_ssl_close(SSL *ssl); ++CIVETWEB_API int gtls_ssl_accept(SSL **ssl, ++ SSL_CTX *ssl_ctx, ++ int sock, ++ struct mg_context *phys_ctx); ++CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len); ++CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len); ++ ++ ++CIVETWEB_API int ++gtls_sslctx_init(SSL_CTX *ctx, const char *crt) ++{ ++ int rc; ++ ++ if (ctx == NULL || crt == NULL) { ++ return -1; ++ } ++ ++ DEBUG_TRACE("%s", "Initializing GnuTLS SSL"); ++ ++ rc = gnutls_certificate_allocate_credentials(&ctx->cred); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("Failed to allocate credentials (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_priority_init(&ctx->prio, NULL, NULL); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("Failed to allocate priority cache (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_certificate_set_x509_key_file2(ctx->cred, ++ crt, ++ crt, ++ GNUTLS_X509_FMT_PEM, ++ NULL, ++ GNUTLS_PKCS_PLAIN ++ | GNUTLS_PKCS_NULL_PASSWORD); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("TLS parse crt/key file failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ return 0; ++ ++failed: ++ gtls_sslctx_uninit(ctx); ++ ++ return -1; ++} ++ ++ ++CIVETWEB_API void ++gtls_sslctx_uninit(SSL_CTX *ctx) ++{ ++ if (ctx != NULL) { ++ gnutls_certificate_free_credentials(ctx->cred); ++ gnutls_priority_deinit(ctx->prio); ++ ctx->cred = NULL; ++ ctx->prio = NULL; ++ } ++} ++ ++ ++CIVETWEB_API int ++gtls_ssl_accept(SSL **ssl, ++ SSL_CTX *ssl_ctx, ++ int sock, ++ struct mg_context *phys_ctx) ++{ ++ int rc; ++ ++ if (ssl == NULL || ssl_ctx == NULL) { ++ return -1; ++ } ++ ++ DEBUG_TRACE("TLS accept processing %p", ssl); ++ ++ *ssl = (SSL *)mg_calloc_ctx(1, sizeof(SSL), phys_ctx); ++ if (*ssl == NULL) { ++ DEBUG_TRACE("Failed to allocate memory for session %zu", sizeof(SSL)); ++ return -1; ++ } ++ ++ rc = gnutls_init(&(*ssl)->sess, GNUTLS_SERVER); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("Failed to initialize session (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_priority_set((*ssl)->sess, ssl_ctx->prio); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("TLS set priortities failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ rc = gnutls_credentials_set((*ssl)->sess, ++ GNUTLS_CRD_CERTIFICATE, ++ ssl_ctx->cred); ++ if (rc != GNUTLS_E_SUCCESS) { ++ DEBUG_TRACE("TLS set credentials failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ goto failed; ++ } ++ ++ gnutls_certificate_send_x509_rdn_sequence((*ssl)->sess, 1); ++ gnutls_certificate_server_set_request((*ssl)->sess, GNUTLS_CERT_IGNORE); ++ gnutls_handshake_set_timeout((*ssl)->sess, ++ GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); ++ gnutls_transport_set_int((*ssl)->sess, sock); ++ ++ while ((rc = gnutls_handshake((*ssl)->sess)) != GNUTLS_E_SUCCESS) { ++ if (gnutls_error_is_fatal(rc)) { ++ if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { ++ DEBUG_TRACE("TLS fatal alert received: %s", ++ gnutls_alert_get_name( ++ gnutls_alert_get((*ssl)->sess))); ++ } else { ++ DEBUG_TRACE("TLS handshake failed (%d): %s", ++ rc, ++ gnutls_strerror(rc)); ++ } ++ ++ goto failed; ++ } ++ } ++ ++ DEBUG_TRACE("TLS connection %p accepted", *ssl); ++ ++ return 0; ++ ++failed: ++ gnutls_deinit((*ssl)->sess); ++ mg_free(*ssl); ++ *ssl = NULL; ++ ++ return -1; ++} ++ ++ ++CIVETWEB_API void ++gtls_ssl_close(SSL *ssl) ++{ ++ int rc; ++ ++ if (ssl == NULL) { ++ return; ++ } ++ ++ while ((rc = gnutls_bye(ssl->sess, GNUTLS_SHUT_RDWR)) != GNUTLS_E_SUCCESS) { ++ switch (rc) { ++ case GNUTLS_E_AGAIN: /* fall through */ ++ case GNUTLS_E_INTERRUPTED: ++ continue; ++ default: /* should actually never happen */ ++ break; ++ } ++ } ++ ++ DEBUG_TRACE("TLS connection %p closed", ssl); ++ gnutls_deinit(ssl->sess); ++ mg_free(ssl); ++} ++ ++ ++CIVETWEB_API int ++gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len) ++{ ++ ssize_t rc; ++ ++ if (ssl == NULL) { ++ return GNUTLS_E_INVALID_SESSION; ++ } ++ ++ while ((rc = gnutls_record_recv(ssl->sess, buf, len)) < 0) { ++ switch (rc) { ++ case GNUTLS_E_AGAIN: /* fall through */ ++ case GNUTLS_E_INTERRUPTED: ++ continue; ++ default: ++ break; ++ } ++ } ++ /* DEBUG_TRACE("gnutls_record_recv: %d", rc); */ ++ return (int)rc; ++} ++ ++ ++CIVETWEB_API int ++gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len) ++{ ++ ssize_t rc; ++ ++ if (ssl == NULL) { ++ return GNUTLS_E_INVALID_SESSION; ++ } ++ ++ while ((rc = gnutls_record_send(ssl->sess, buf, len)) < 0) { ++ switch (rc) { ++ case GNUTLS_E_AGAIN: /* fall through */ ++ case GNUTLS_E_INTERRUPTED: ++ continue; ++ default: ++ break; ++ } ++ } ++ /* DEBUG_TRACE("gnutls_record_send: %d", rc); */ ++ return (int)rc; ++} + ++#endif /* USE_GNUTLS */ +diff --git a/src/mod_lua.inl b/src/mod_lua.inl +index f1073e55..e6596b1c 100644 +--- a/src/mod_lua.inl ++++ b/src/mod_lua.inl +@@ -10,6 +10,8 @@ + #include "civetweb_lua.h" + #include "civetweb_private_lua.h" - Quick start documentation - -------------------------- -@@ -174,7 +164,7 @@ Authors - - CivetWeb was forked from the last MIT version of Mongoose in August 2013. - Since then, CivetWeb has seen many improvements from various authors --(Copyright (c) 2013-2021 the CivetWeb developers, MIT license). -+(Copyright (c) 2013-2025 the CivetWeb developers, MIT license). - - A list of authors can be found in [CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md). - --- -2.34.1 - - -From 3e7eceddeb626f5195f7c813b788a118c20eda1e Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Tue, 2 Sep 2025 15:56:22 +0200 -Subject: [PATCH 150/173] Update SECURITY.md and LICENSE.md - ---- - LICENSE.md | 2 +- - SECURITY.md | 10 +++------- - 2 files changed, 4 insertions(+), 8 deletions(-) - -diff --git a/LICENSE.md b/LICENSE.md -index a74a0fe4..db39daf4 100644 ---- a/LICENSE.md -+++ b/LICENSE.md -@@ -11,7 +11,7 @@ Civetweb License - - ### Included with all features. - --> Copyright (c) 2013-2021 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) -+> Copyright (c) 2013-2025 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) - > - > Copyright (c) 2004-2013 Sergey Lyubka - > -diff --git a/SECURITY.md b/SECURITY.md -index b1781aff..78c85541 100644 ---- a/SECURITY.md -+++ b/SECURITY.md -@@ -14,16 +14,12 @@ Selected, critical defects are fixed in the latest release as well. - Note that different security policies apply to different files/folders in this project: - - The core components are include/civetweb.h, src/civetweb.c and src/*.inl. These files are part of every server instance in production. Therefore they have to undergo the most intensive security tests and reviews. - - The src/main.c file is used by the standalone server. It is used in various tests in combination with the aforementioned core components. Applications embedding civetweb will not use main.c and, thus, do not suffer from any vulnerabilities therein. --- The example folders contain different usage examples in different maintenance state. This is explained in detail in the README file there. -+- The example folders contain different usage examples in different maintenance state. Some of them skipped error handling to keep the code shorter and easier to understand. This is explained in more detail in the README file there. - - The content of all test folders (test, unittest, fuzztest) is used to test the server functionality. These tests are not designed with security in mind - on the contrary, some tests contain scripts or settings that even introduce security leaks on purpose. All tests are only meant to be run in a test environment. You should not use the content of any test folder in production. Also certificates in "resources/cert" are only meant to be used in test environments and must never be used in production. - - - ## Reporting a Vulnerability - --Please send vulnerability reports by email to bel2125 at gmail com. --Vulnerability with low severity can be sent directly by email. -- --For high severity vulnerabilities, you can get an individual gpg key to encrypt your detailed description of vulnerabilities you want to report. -- --If you do not get any response within one week, your email might have been lost (e.g., deleted as false positive by a spam filter). In this case, please open a GitHub issue. -+Ideally open a github issue for suspected vulnerabilities, even if you do not post all details there. This will help against emails getting lost. -+Detailed vulnerability reports including exploit code should be sent by email to bel2125 at gmail com. - --- -2.34.1 - - -From 4bd10c495faabc7789d6764c1c583142989b710c Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sat, 6 Sep 2025 12:26:11 +0200 -Subject: [PATCH 151/173] AppVeyor build: Replace obsolete Visual Studio builds - by new one - -Visual Studio 2010, 2012, 2013 are no longer supported. ---- - appveyor.yml | 69 ++++++++++++---------------------------------------- - 1 file changed, 15 insertions(+), 54 deletions(-) - -diff --git a/appveyor.yml b/appveyor.yml -index fb936d1f..e54c2d5e 100644 ---- a/appveyor.yml -+++ b/appveyor.yml -@@ -159,59 +159,6 @@ environment: - enable_stats: YES - configuration: Release - platform: x64 -- # Visual Studio 2010 -- - id: Full-VS2010-x86 -- compiler: msvc-16-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x86 -- # Visual Studio 2012 -- - id: Full-VS2012-x86 -- compiler: msvc-17-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x86 -- # Visual Studio 2013 -- - id: Full-VS2013-x86 -- compiler: msvc-18-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x86 -- image: Visual Studio 2013 -- - id: Full-VS2013-x64 -- compiler: msvc-18-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x64 -- image: Visual Studio 2013 - # Visual Studio 2015 - - id: Full-VS2015-x86 - compiler: msvc-19-seh -@@ -280,7 +227,21 @@ environment: - configuration: Release - platform: x64 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 -- # Ubuntu -+ # Visual Studio 2022 -+ - id: Full-VS2022-x64 -+ compiler: msvc-22-seh -+ build_shared: NO -+ no_files: NO -+ enable_ipv6: YES -+ enable_ssl: YES -+ enable_websockets: YES -+ no_cgi: NO -+ no_caching: NO -+ enable_stats: YES -+ configuration: Release -+ platform: x64 -+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 -+ # Ubuntu - - id: Ubuntu1604-GCC-x64 - compiler: gcc-5.1.0-posix - build_shared: NO --- -2.34.1 - - -From acaf7e3fe0f406fb12b0cb9a073938995b66cc2a Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Mon, 8 Sep 2025 14:44:33 +0200 -Subject: [PATCH 152/173] Use atomic operations in timer test - ---- - unittest/timertest.c | 22 +++++++++++----------- - 1 file changed, 11 insertions(+), 11 deletions(-) - -diff --git a/unittest/timertest.c b/unittest/timertest.c -index 270dbec7..17716d16 100644 ---- a/unittest/timertest.c -+++ b/unittest/timertest.c -@@ -51,22 +51,22 @@ static int action_dec_ret; - static int - action_dec(void *arg) - { -- int *p = (int *)arg; -- (*p)--; -+ ptrdiff_t *p = (ptrdiff_t *)arg; -+ ptrdiff_t v = mg_atomic_dec(p); - -- if (*p < -1) { -+ if (v < -1) { - ck_abort_msg("Periodic timer called too often"); - /* return 0 here would be unreachable code */ - } - -- return (*p >= -3) ? action_dec_ret : 0; -+ return (v >= -3) ? action_dec_ret : 0; - } - - - static void - action_cancel(void *arg) - { -- int *p = (int *)arg; -+ ptrdiff_t *p = (ptrdiff_t *)arg; ++/* Prototypes */ ++static int lua_error_handler(lua_State *L); - /* test convention: store cancel counter after timer counter */ - p += TIMERS_IN_TEST; -@@ -76,29 +76,29 @@ action_cancel(void *arg) - /* return 0 here would be unreachable code */ + #if defined(_WIN32) + static void * +@@ -641,14 +643,26 @@ run_lsp_kepler(struct mg_connection *conn, + /* Only send a HTML header, if this is the top level page. + * If this page is included by some mg.include calls, do not add a + * header. */ +- mg_printf(conn, "HTTP/1.1 200 OK\r\n"); ++ ++ /* Initialize a new HTTP response, either with some-predefined ++ * status code (e.g. 404 if this is called from an error ++ * handler) or with 200 OK */ ++ mg_response_header_start(conn, ++ conn->status_code > 0 ? conn->status_code ++ : 200); ++ ++ /* Add additional headers */ + send_no_cache_header(conn); + send_additional_header(conn); +- mg_printf(conn, +- "Date: %s\r\n" +- "Connection: close\r\n" +- "Content-Type: text/html; charset=utf-8\r\n\r\n", +- date); ++ ++ /* Add content type */ ++ mg_response_header_add(conn, ++ "Content-Type", ++ "text/html; charset=utf-8", ++ -1); ++ ++ /* Send the HTTP response (status and all headers) */ ++ mg_response_header_send(conn); } -- (*p)++; -+ mg_atomic_inc(p); - } - - - static int - action_dec_to_0(void *arg) - { -- int *p = (int *)arg; -- (*p)--; -+ ptrdiff_t *p = (ptrdiff_t *)arg; -+ ptrdiff_t v = mg_atomic_dec(p); + data.begin = p; +@@ -662,11 +676,18 @@ run_lsp_kepler(struct mg_connection *conn, + /* Syntax error or OOM. + * Error message is pushed on stack. */ + lua_pcall(L, 1, 0, 0); +- lua_cry(conn, lua_ok, L, "LSP", "execute"); /* XXX TODO: everywhere ! */ ++ lua_cry(conn, lua_ok, L, "LSP Kepler", "execute"); ++ lua_error_handler(L); ++ return 1; -- if (*p <= -1) { -+ if (v <= -1) { - ck_abort_msg("Periodic timer called too often"); - /* return 0 here would be unreachable code */ + } else { + /* Success loading chunk. Call it. */ +- lua_pcall(L, 0, 0, 1); ++ lua_ok = lua_pcall(L, 0, 0, 0); ++ if (lua_ok != LUA_OK) { ++ lua_cry(conn, lua_ok, L, "LSP Kepler", "call"); ++ lua_error_handler(L); ++ return 1; ++ } } - -- return (*p > 0); -+ return (v > 0); + return 0; } +@@ -780,9 +801,17 @@ run_lsp_civetweb(struct mg_connection *conn, + /* Syntax error or OOM. + * Error message is pushed on stack. */ + lua_pcall(L, 1, 0, 0); ++ lua_cry(conn, lua_ok, L, "LSP", "execute"); ++ lua_error_handler(L); ++ return 1; + } else { + /* Success loading chunk. Call it. */ +- lua_pcall(L, 0, 0, 1); ++ lua_ok = lua_pcall(L, 0, 0, 0); ++ if (lua_ok != LUA_OK) { ++ lua_cry(conn, lua_ok, L, "LSP", "call"); ++ lua_error_handler(L); ++ return 1; ++ } + } + /* Progress until after the Lua closing tag. */ +@@ -1948,6 +1977,9 @@ struct lua_websock_data { + struct mg_connection *conn[MAX_WORKER_THREADS]; + pthread_mutex_t ws_mutex; + }; ++#else ++/* used in parameter list of prepare_lua_environment() */ ++struct lua_websock_data; + #endif - START_TEST(test_timer_cyclic) - { - struct mg_context ctx; -- int c[TIMERS_IN_TEST * 2]; -+ ptrdiff_t c[TIMERS_IN_TEST * 2]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); - --- -2.34.1 - - -From 618790d4d5a2fd3b410753aa3a4060a55976ca2a Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Wed, 10 Sep 2025 00:26:36 +0200 -Subject: [PATCH 153/173] Unittest: fix alignment - ---- - unittest/timertest.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/unittest/timertest.c b/unittest/timertest.c -index 17716d16..e445421b 100644 ---- a/unittest/timertest.c -+++ b/unittest/timertest.c -@@ -97,8 +97,8 @@ action_dec_to_0(void *arg) - - START_TEST(test_timer_cyclic) - { -+ static ptrdiff_t c[TIMERS_IN_TEST * 2]; - struct mg_context ctx; -- ptrdiff_t c[TIMERS_IN_TEST * 2]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); - --- -2.34.1 - - -From 2fefc5c8a7006ea997ebb332cd435bf70381d5ce Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Thu, 11 Sep 2025 19:50:56 +0200 -Subject: [PATCH 154/173] Add Lua 5.5 support - -Signed-off-by: DL6ER ---- - resources/Makefile.in-lua | 15 +- - src/mod_lua.inl | 12 +- - src/mod_lua_shared.inl | 2 +- - src/third_party/civetweb_lua.h | 18 +- - src/third_party/lfs.c | 2 +- - src/third_party/lua-5.5.0-beta/Makefile | 106 + - src/third_party/lua-5.5.0-beta/README | 6 + - .../doc/OSIApproved_100X125.png | Bin 0 -> 12127 bytes - .../lua-5.5.0-beta/doc/contents.html | 679 + - src/third_party/lua-5.5.0-beta/doc/index.css | 21 + - src/third_party/lua-5.5.0-beta/doc/logo.gif | Bin 0 -> 9893 bytes - src/third_party/lua-5.5.0-beta/doc/lua.1 | 155 + - src/third_party/lua-5.5.0-beta/doc/lua.css | 162 + - src/third_party/lua-5.5.0-beta/doc/luac.1 | 118 + - src/third_party/lua-5.5.0-beta/doc/manual.css | 21 + - .../lua-5.5.0-beta/doc/manual.html | 12398 ++++++++++++++++ - .../lua-5.5.0-beta/doc/readme.html | 333 + - src/third_party/lua-5.5.0-beta/src/Makefile | 229 + - src/third_party/lua-5.5.0-beta/src/lapi.c | 1467 ++ - src/third_party/lua-5.5.0-beta/src/lapi.h | 65 + - src/third_party/lua-5.5.0-beta/src/lauxlib.c | 1198 ++ - src/third_party/lua-5.5.0-beta/src/lauxlib.h | 268 + - src/third_party/lua-5.5.0-beta/src/lbaselib.c | 558 + - src/third_party/lua-5.5.0-beta/src/lcode.c | 1911 +++ - src/third_party/lua-5.5.0-beta/src/lcode.h | 103 + - src/third_party/lua-5.5.0-beta/src/lcorolib.c | 223 + - src/third_party/lua-5.5.0-beta/src/lctype.c | 64 + - src/third_party/lua-5.5.0-beta/src/lctype.h | 101 + - src/third_party/lua-5.5.0-beta/src/ldblib.c | 477 + - src/third_party/lua-5.5.0-beta/src/ldebug.c | 971 ++ - src/third_party/lua-5.5.0-beta/src/ldebug.h | 64 + - src/third_party/lua-5.5.0-beta/src/ldo.c | 1131 ++ - src/third_party/lua-5.5.0-beta/src/ldo.h | 98 + - src/third_party/lua-5.5.0-beta/src/ldump.c | 303 + - src/third_party/lua-5.5.0-beta/src/lfunc.c | 315 + - src/third_party/lua-5.5.0-beta/src/lfunc.h | 65 + - src/third_party/lua-5.5.0-beta/src/lgc.c | 1803 +++ - src/third_party/lua-5.5.0-beta/src/lgc.h | 268 + - src/third_party/lua-5.5.0-beta/src/linit.c | 63 + - src/third_party/lua-5.5.0-beta/src/liolib.c | 841 ++ - src/third_party/lua-5.5.0-beta/src/ljumptab.h | 112 + - src/third_party/lua-5.5.0-beta/src/llex.c | 604 + - src/third_party/lua-5.5.0-beta/src/llex.h | 93 + - src/third_party/lua-5.5.0-beta/src/llimits.h | 315 + - src/third_party/lua-5.5.0-beta/src/lmathlib.c | 752 + - src/third_party/lua-5.5.0-beta/src/lmem.c | 215 + - src/third_party/lua-5.5.0-beta/src/lmem.h | 96 + - src/third_party/lua-5.5.0-beta/src/loadlib.c | 743 + - src/third_party/lua-5.5.0-beta/src/lobject.c | 717 + - src/third_party/lua-5.5.0-beta/src/lobject.h | 854 ++ - src/third_party/lua-5.5.0-beta/src/lopcodes.c | 138 + - src/third_party/lua-5.5.0-beta/src/lopcodes.h | 429 + - src/third_party/lua-5.5.0-beta/src/lopnames.h | 103 + - src/third_party/lua-5.5.0-beta/src/loslib.c | 432 + - src/third_party/lua-5.5.0-beta/src/lparser.c | 2091 +++ - src/third_party/lua-5.5.0-beta/src/lparser.h | 191 + - src/third_party/lua-5.5.0-beta/src/lprefix.h | 45 + - src/third_party/lua-5.5.0-beta/src/lstate.c | 420 + - src/third_party/lua-5.5.0-beta/src/lstate.h | 455 + - src/third_party/lua-5.5.0-beta/src/lstring.c | 359 + - src/third_party/lua-5.5.0-beta/src/lstring.h | 73 + - src/third_party/lua-5.5.0-beta/src/lstrlib.c | 1883 +++ - src/third_party/lua-5.5.0-beta/src/ltable.c | 1325 ++ - src/third_party/lua-5.5.0-beta/src/ltable.h | 184 + - src/third_party/lua-5.5.0-beta/src/ltablib.c | 426 + - src/third_party/lua-5.5.0-beta/src/ltm.c | 276 + - src/third_party/lua-5.5.0-beta/src/ltm.h | 104 + - src/third_party/lua-5.5.0-beta/src/lua.c | 761 + - src/third_party/lua-5.5.0-beta/src/lua.h | 554 + - src/third_party/lua-5.5.0-beta/src/lua.hpp | 10 + - src/third_party/lua-5.5.0-beta/src/luac.c | 724 + - src/third_party/lua-5.5.0-beta/src/luaconf.h | 827 ++ - src/third_party/lua-5.5.0-beta/src/lualib.h | 65 + - src/third_party/lua-5.5.0-beta/src/lundump.c | 423 + - src/third_party/lua-5.5.0-beta/src/lundump.h | 40 + - src/third_party/lua-5.5.0-beta/src/lutf8lib.c | 291 + - src/third_party/lua-5.5.0-beta/src/lvm.c | 1926 +++ - src/third_party/lua-5.5.0-beta/src/lvm.h | 136 + - src/third_party/lua-5.5.0-beta/src/lzio.c | 89 + - src/third_party/lua-5.5.0-beta/src/lzio.h | 67 + - 80 files changed, 45968 insertions(+), 14 deletions(-) - create mode 100644 src/third_party/lua-5.5.0-beta/Makefile - create mode 100644 src/third_party/lua-5.5.0-beta/README - create mode 100644 src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png - create mode 100644 src/third_party/lua-5.5.0-beta/doc/contents.html - create mode 100644 src/third_party/lua-5.5.0-beta/doc/index.css - create mode 100644 src/third_party/lua-5.5.0-beta/doc/logo.gif - create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.1 - create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.css - create mode 100644 src/third_party/lua-5.5.0-beta/doc/luac.1 - create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.css - create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.html - create mode 100644 src/third_party/lua-5.5.0-beta/doc/readme.html - create mode 100644 src/third_party/lua-5.5.0-beta/src/Makefile - create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lbaselib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lcorolib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldblib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldump.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/linit.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/liolib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ljumptab.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/llimits.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lmathlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/loadlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lopnames.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/loslib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lprefix.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstrlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltablib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.hpp - create mode 100644 src/third_party/lua-5.5.0-beta/src/luac.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/luaconf.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lualib.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lutf8lib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.h - -diff --git a/resources/Makefile.in-lua b/resources/Makefile.in-lua -index 45f80181..8afff851 100644 ---- a/resources/Makefile.in-lua -+++ b/resources/Makefile.in-lua -@@ -39,7 +39,14 @@ ifeq ($(WITH_LUA_VERSION), 504) - $(info Lua: Using version 5.4.3) - LUA_DIR = src/third_party/lua-5.4.3/src - LUA_SHARED_LIB_FLAG = -llua5.4 -- LUA_CFLAGS = -DLUA_COMPAT_5_2 -DLUA_VERSION_MAKEFILE=504 -+ LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=504 -+ LUA_VERSION_KNOWN = 1 -+endif -+ifeq ($(WITH_LUA_VERSION), 505) -+ $(info Lua: Using version 5.5.0-beta) -+ LUA_DIR = src/third_party/lua-5.5.0-beta/src -+ LUA_SHARED_LIB_FLAG = -llua5.5 -+ LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=505 - LUA_VERSION_KNOWN = 1 - endif - -@@ -112,6 +119,12 @@ ifeq ($(WITH_LUA_VERSION), 504) - lctype.c \ - lutf8lib.c - endif -+ifeq ($(WITH_LUA_VERSION), 505) -+ LUA_SOURCE_FILES += \ -+ lcorolib.c \ -+ lctype.c \ -+ lutf8lib.c -+endif - - $(info Lua: using static library) -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index 2a1d3a57..f2e40237 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -2695,8 +2695,14 @@ static void +@@ -2666,8 +2698,15 @@ static void civetweb_open_lua_libs(lua_State *L) { { @@ -14595,13 +6452,107 @@ index 2a1d3a57..f2e40237 100644 luaL_openlibs(L); +#else + // In Lua 5.5 and later has become a macro -+ extern void (luaL_openselectedlibs) (lua_State *L, int load, int preload); ++ extern void( ++ luaL_openselectedlibs)(lua_State * L, int load, int preload); + luaL_openselectedlibs(L, ~0, 0); +#endif } #if defined(USE_LUA_SQLITE3) { -@@ -3091,7 +3097,7 @@ mg_exec_lua_script(struct mg_connection *conn, +@@ -2769,18 +2808,39 @@ lua_error_handler(lua_State *L) + + lua_getglobal(L, "mg"); + if (!lua_isnil(L, -1)) { +- lua_getfield(L, -1, "write"); /* call mg.write() */ ++ /* Write the error message to the error log */ ++ lua_getfield(L, -1, "write"); + lua_pushstring(L, error_msg); + lua_pushliteral(L, "\n"); +- lua_call(L, 2, 0); +- IGNORE_UNUSED_RESULT( +- luaL_dostring(L, "mg.write(debug.traceback(), '\\n')")); ++ lua_call(L, 2, 0); /* call mg.write(error_msg + \n) */ ++ lua_pop(L, 1); /* pop mg */ ++ ++ /* Get Lua traceback */ ++ lua_getglobal(L, "debug"); ++ lua_getfield(L, -1, "traceback"); ++ lua_call(L, 0, 1); /* call debug.traceback() */ ++ lua_remove(L, -2); /* remove debug */ ++ ++ /* Write the Lua traceback to the error log */ ++ lua_getglobal(L, "mg"); ++ lua_getfield(L, -1, "write"); ++ lua_pushvalue(L, -3); /* push the traceback */ ++ ++ /* Only print the traceback if it is not empty */ ++ if (strcmp(lua_tostring(L, -1), "stack traceback:") != 0) { ++ lua_pushliteral(L, "\n"); /* append a newline */ ++ lua_call(L, 2, 0); /* call mg.write(traceback + \n) */ ++ lua_pop(L, 2); /* pop mg and traceback */ ++ } else { ++ lua_pop(L, 3); /* pop mg, traceback and error message */ ++ } ++ + } else { + printf("Lua error: [%s]\n", error_msg); + IGNORE_UNUSED_RESULT( + luaL_dostring(L, "print(debug.traceback(), '\\n')")); + } +- /* TODO(lsm, low): leave the stack balanced */ ++ lua_pop(L, 1); /* pop error message */ + + return 0; + } +@@ -2829,11 +2889,13 @@ prepare_lua_environment(struct mg_context *ctx, + #endif + + /* Store context in the registry */ ++#if defined(USE_WEBSOCKET) + if (ctx != NULL) { + lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); + lua_pushlightuserdata(L, (void *)ctx); + lua_settable(L, LUA_REGISTRYINDEX); + } ++#endif /* USE_WEBSOCKET */ + if (ws_conn_list != NULL) { + lua_pushlightuserdata(L, (void *)&lua_regkey_connlist); + lua_pushlightuserdata(L, (void *)ws_conn_list); +@@ -2905,8 +2967,8 @@ prepare_lua_environment(struct mg_context *ctx, + || (lua_env_type == LUA_ENV_TYPE_BACKGROUND)) { + reg_function(L, "set_timeout", lwebsocket_set_timeout); + reg_function(L, "set_interval", lwebsocket_set_interval); +-#endif + } ++#endif + + reg_conn_function(L, "get_mime_type", lsp_get_mime_type, conn); + reg_conn_function(L, "get_option", lsp_get_option, conn); +@@ -2935,6 +2997,11 @@ prepare_lua_environment(struct mg_context *ctx, + + if ((conn != NULL) && (conn->dom_ctx != NULL)) { + reg_string(L, "document_root", conn->dom_ctx->config[DOCUMENT_ROOT]); ++ if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) { ++ reg_string(L, ++ "fallback_document_root", ++ conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]); ++ } + reg_string(L, + "auth_domain", + conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); +@@ -2943,6 +3010,11 @@ prepare_lua_environment(struct mg_context *ctx, + reg_string(L, + "websocket_root", + conn->dom_ctx->config[WEBSOCKET_ROOT]); ++ if (conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]) { ++ reg_string(L, ++ "fallback_websocket_root", ++ conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]); ++ } + } else { + reg_string(L, + "websocket_root", +@@ -3031,7 +3103,7 @@ mg_exec_lua_script(struct mg_connection *conn, /* Execute a plain Lua script. */ if (path != NULL @@ -14610,7 +6561,23 @@ index 2a1d3a57..f2e40237 100644 != NULL) { prepare_lua_environment( conn->phys_ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE); -@@ -3207,7 +3213,7 @@ handle_lsp_request(struct mg_connection *conn, +@@ -3058,9 +3130,14 @@ mg_exec_lua_script(struct mg_connection *conn, + } + + if (luaL_loadfile(L, path) != 0) { ++ mg_send_http_error(conn, 500, "Lua error:\r\n"); + lua_error_handler(L); + } else { +- lua_pcall(L, 0, 0, -2); ++ int call_status = lua_pcall(L, 0, 0, 0); ++ if (call_status != 0) { ++ mg_send_http_error(conn, 500, "Lua error:\r\n"); ++ lua_error_handler(L); ++ } + } + DEBUG_TRACE("Close Lua environment %p", L); + lua_close(L); +@@ -3142,7 +3219,7 @@ handle_lsp_request(struct mg_connection *conn, L = ls; } else { /* We need to create a Lua state. */ @@ -14619,7 +6586,7 @@ index 2a1d3a57..f2e40237 100644 if (L == NULL) { /* We neither got a Lua state from the command line, * nor did we succeed in creating our own state. -@@ -3353,7 +3359,7 @@ lua_websocket_new(const char *script, struct mg_connection *conn) +@@ -3288,7 +3365,7 @@ lua_websocket_new(const char *script, struct mg_connection *conn) } pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr); (void)pthread_mutex_lock(&(ws->ws_mutex)); @@ -14628,6 +6595,24 @@ index 2a1d3a57..f2e40237 100644 ws->conn[0] = conn; ws->references = 1; prepare_lua_environment(conn->phys_ctx, +@@ -3664,7 +3741,7 @@ lua_init_optional_libraries(void) + lua_shared_init(); + + /* UUID library */ +-#if !defined(_WIN32) ++#if !defined(_WIN32) && !defined(NO_DLOPEN) + lib_handle_uuid = dlopen("libuuid.so", RTLD_LAZY); + pf_uuid_generate.p = + (lib_handle_uuid ? dlsym(lib_handle_uuid, "uuid_generate") : 0); +@@ -3678,7 +3755,7 @@ static void + lua_exit_optional_libraries(void) + { + /* UUID library */ +-#if !defined(_WIN32) ++#if !defined(_WIN32) && !defined(NO_DLOPEN) + if (lib_handle_uuid) { + dlclose(lib_handle_uuid); + } diff --git a/src/mod_lua_shared.inl b/src/mod_lua_shared.inl index 5f69e285..fed94cd9 100644 --- a/src/mod_lua_shared.inl @@ -14641,8 +6626,241 @@ index 5f69e285..fed94cd9 100644 lua_newtable(L_shared); lua_setglobal(L_shared, "shared"); +diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl +index 822fc2e0..6883d48a 100644 +--- a/src/mod_mbedtls.inl ++++ b/src/mod_mbedtls.inl +@@ -1,8 +1,10 @@ + #if defined(USE_MBEDTLS) // USE_MBEDTLS used with NO_SSL + +-#include "mbedtls/ctr_drbg.h" + #include "mbedtls/debug.h" ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + #include "mbedtls/entropy.h" ++#include "mbedtls/ctr_drbg.h" ++#endif + #include "mbedtls/error.h" + + #if MBEDTLS_VERSION_NUMBER >= 0x03000000 +@@ -26,14 +28,16 @@ typedef mbedtls_ssl_context SSL; + typedef struct { + mbedtls_ssl_config conf; /* SSL configuration */ + mbedtls_x509_crt cert; /* Certificate */ ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_context ctr; /* Counter random generator state */ + mbedtls_entropy_context entropy; /* Entropy context */ ++#endif + mbedtls_pk_context pkey; /* Private key */ + } SSL_CTX; + + + /* public api */ +-int mbed_sslctx_init(SSL_CTX *ctx, const char *crt); ++int mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist); + void mbed_sslctx_uninit(SSL_CTX *ctx); + void mbed_ssl_close(mbedtls_ssl_context *ssl); + int mbed_ssl_accept(mbedtls_ssl_context **ssl, +@@ -43,6 +47,9 @@ int mbed_ssl_accept(mbedtls_ssl_context **ssl, + int mbed_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, int len); + int mbed_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, int len); + ++/* Set the ciphersuites to be used by mbedtls using a comma-separated string */ ++int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list); ++ + static void mbed_debug(void *context, + int level, + const char *file, +@@ -52,7 +59,7 @@ static int mbed_ssl_handshake(mbedtls_ssl_context *ssl); + + + int +-mbed_sslctx_init(SSL_CTX *ctx, const char *crt) ++mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) + { + mbedtls_ssl_config *conf; + int rc; +@@ -62,7 +69,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + } + + DEBUG_TRACE("%s", "Initializing MbedTLS SSL"); ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_entropy_init(&ctx->entropy); ++#endif + + conf = &ctx->conf; + mbedtls_ssl_config_init(conf); +@@ -85,9 +94,25 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + + /* Initialize TLS key and cert */ + mbedtls_pk_init(&ctx->pkey); ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_init(&ctx->ctr); ++#endif + mbedtls_x509_crt_init(&ctx->cert); + ++#ifdef MBEDTLS_PSA_CRYPTO_C ++ /* Initialize PSA crypto (mandatory with TLS 1.3) ++ * This must be done before calling any other PSA Crypto ++ * functions or they will fail with PSA_ERROR_BAD_STATE ++ */ ++ const psa_status_t status = psa_crypto_init(); ++ if (status != PSA_SUCCESS) { ++ DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n", ++ (int)status); ++ return -1; ++ } ++#endif ++ ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + rc = mbedtls_ctr_drbg_seed(&ctx->ctr, + mbedtls_entropy_func, + &ctx->entropy, +@@ -97,8 +122,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + DEBUG_TRACE("TLS random seed failed (%i)", rc); + return -1; + } ++#endif + +-#if MBEDTLS_VERSION_NUMBER >= 0x03000000 ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000 + // mbedtls_pk_parse_keyfile() has changed in mbedTLS 3.0. You now need + // to pass a properly seeded, cryptographically secure RNG when calling + // these functions. It is used for blinding, a countermeasure against +@@ -129,7 +155,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + return -1; + } + ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &ctx->ctr); ++#endif + + /* Set auth mode if peer cert should be verified */ + mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); +@@ -141,6 +169,14 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + DEBUG_TRACE("TLS cannot set certificate and private key (%i)", rc); + return -1; + } ++ ++ /* Set ciphersuites if specified */ ++ if (cipherlist != NULL && ++ cipherlist[0] != '\0' && ++ mbed_sslctx_set_ciphersuites(conf, cipherlist) != 0) { ++ DEBUG_TRACE("Failed to set ciphersuites: no valid ciphersuites are found in the list"); ++ return -1; ++ } + return 0; + } + +@@ -148,10 +184,12 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) + void + mbed_sslctx_uninit(SSL_CTX *ctx) + { ++#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_free(&ctx->ctr); ++ mbedtls_entropy_free(&ctx->entropy); ++#endif + mbedtls_pk_free(&ctx->pkey); + mbedtls_x509_crt_free(&ctx->cert); +- mbedtls_entropy_free(&ctx->entropy); + mbedtls_ssl_config_free(&ctx->conf); + } + +@@ -188,7 +226,13 @@ mbed_ssl_accept(mbedtls_ssl_context **ssl, + return -1; + } + ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 ++ DEBUG_TRACE("TLS connection %p accepted, state: %d", ++ ssl, ++ (*ssl)->MBEDTLS_PRIVATE(state)); ++#else + DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->state); ++#endif + return 0; + } + +@@ -214,7 +258,13 @@ mbed_ssl_handshake(mbedtls_ssl_context *ssl) + } + } + ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 ++ DEBUG_TRACE("TLS handshake rc: %d, state: %d", ++ rc, ++ ssl->MBEDTLS_PRIVATE(state)); ++#else + DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->state); ++#endif + return rc; + } + +@@ -254,4 +304,65 @@ mbed_debug(void *user_param, + str); + } + ++/** ++ * @brief Sets the list of allowed ciphersuites for an mbedTLS SSL configuration. ++ * ++ * Parses a comma-separated list of ciphersuite names, converts them to their ++ * corresponding mbedTLS ciphersuite IDs, and configures the SSL context to use ++ * only those ciphersuites. ++ * ++ * @param conf Pointer to the mbedTLS SSL configuration structure. ++ * @param cipher_list Comma-separated string of ciphersuite names. ++ * @return 0 on success, ++ * -1 if conf or cipher_list is NULL, ++ * -2 if no valid ciphersuites are found in the list. ++ * ++ * @note The ciphersuite ID array is static and must remain valid after the function returns, ++ * as mbedtls_ssl_conf_ciphersuites() does not copy the array. ++ */ ++int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list) { ++ if (conf == NULL || cipher_list == NULL) { ++ return -1; ++ } ++ ++ // The array for ciphersuite IDs must remain valid after this function ++ // returns as mbedtls_ssl_conf_ciphersuites() does not copy the array, ++ // but only stores the pointer. We do not allow more than 64 cipher ++ // suites for simplicity. ++ static int ciphersuites[64] = { 0 }; ++ size_t count = 0; ++ ++ char buf[1024]; ++ strncpy(buf, cipher_list, sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ ++ char *token = strtok(buf, ","); ++ while (token && count < 63) { ++ // Remove leading/trailing whitespace ++ while (*token == ' ' || *token == '\t') token++; ++ char *end = token + strlen(token) - 1; ++ while (end > token && (*end == ' ' || *end == '\t')) { ++ *end = '\0'; ++ end--; ++ } ++ const mbedtls_ssl_ciphersuite_t *ciphersuite = mbedtls_ssl_ciphersuite_from_string(token); ++ if (ciphersuite != NULL) { ++ const int id = mbedtls_ssl_ciphersuite_get_id(ciphersuite); ++ DEBUG_TRACE("Adding ciphersuite '%s' (ID %d)", token, id); ++ ciphersuites[count++] = id; ++ } ++ token = strtok(NULL, ","); ++ } ++ ciphersuites[count] = 0; ++ ++ if (count == 0) { ++ DEBUG_TRACE("No valid ciphersuites found"); ++ return -2; // No valid ciphersuites found ++ } ++ ++ // Set the ciphersuites ++ mbedtls_ssl_conf_ciphersuites(conf, ciphersuites); ++ return 0; ++} ++ + #endif /* USE_MBEDTLS */ diff --git a/src/third_party/civetweb_lua.h b/src/third_party/civetweb_lua.h -index 40382022..15a76c1d 100644 +index 40382022..8c59aaa1 100644 --- a/src/third_party/civetweb_lua.h +++ b/src/third_party/civetweb_lua.h @@ -49,7 +49,7 @@ extern "C" { @@ -14650,19 +6868,17 @@ index 40382022..15a76c1d 100644 #define mg_lua_load(a, b, c, d, e) lua_load(a, b, c, d) #define lua_rawlen lua_objlen -#define lua_newstate(a, b) \ -+#define mg_lua_newstate(a, b) \ ++#define mg_lua_newstate(a, b) \ luaL_newstate() /* Must use luaL_newstate() for 64 bit target */ #define lua_pushinteger lua_pushnumber #define luaL_newlib(L, t) \ -@@ -62,17 +62,19 @@ extern "C" { +@@ -62,17 +62,17 @@ extern "C" { } #define luaL_setfuncs(L, r, u) lua_register(L, r->name, r->func) -#elif LUA_VERSION_NUM == 502 -/* Lua 5.2 detected */ -+#elif LUA_VERSION_NUM == 502 || \ -+ LUA_VERSION_NUM == 503 || \ -+ LUA_VERSION_NUM == 504 ++#elif LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504 +/* Lua 5.2 - 5.4 detected */ #define mg_lua_load lua_load +#define mg_lua_newstate lua_newstate @@ -14694,6 +6910,33 @@ index 95ab63b4..e69dfec7 100644 luaL_Stream *fh = (luaL_Stream *) luaL_checkudata(L, idx, "FILE*"); if (fh->closef == 0 || fh->f == NULL) { luaL_error(L, "%s: closed file", funcname); +diff --git a/src/third_party/lua-5.2.4/src/ldebug.c b/src/third_party/lua-5.2.4/src/ldebug.c +index 96118461..1a30433b 100644 +--- a/src/third_party/lua-5.2.4/src/ldebug.c ++++ b/src/third_party/lua-5.2.4/src/ldebug.c +@@ -116,10 +116,11 @@ static const char *upvalname (Proto *p, int uv) { + + static const char *findvararg (CallInfo *ci, int n, StkId *pos) { + int nparams = clLvalue(ci->func)->p->numparams; +- if (n >= ci->u.l.base - ci->func - nparams) ++ int nvararg = ci->u.l.base - ci->func - nparams; ++ if (n < -nvararg) + return NULL; /* no such vararg */ + else { +- *pos = ci->func + nparams + n; ++ *pos = ci->func + nparams - n; + return "(*vararg)"; /* generic name for any vararg */ + } + } +@@ -131,7 +132,7 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n, + StkId base; + if (isLua(ci)) { + if (n < 0) /* access to vararg values? */ +- return findvararg(ci, -n, pos); ++ return findvararg(ci, n, pos); + else { + base = ci->u.l.base; + name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); diff --git a/src/third_party/lua-5.5.0-beta/Makefile b/src/third_party/lua-5.5.0-beta/Makefile new file mode 100644 index 00000000..388fa17f @@ -61505,162 +53748,47 @@ index 00000000..49047c98 +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif --- -2.34.1 - - -From b20a7bc1bf70b66448bec6b68a61328766cd2e19 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Thu, 11 Sep 2025 19:57:24 +0200 -Subject: [PATCH 155/173] Set minimum CMAKE version for tests - -Signed-off-by: DL6ER ---- - examples/linux_ws_server_cpp/CMakeLists.txt | 2 ++ - src/CMakeLists.txt | 3 +++ - unittest/CMakeLists.txt | 4 ++++ - 3 files changed, 9 insertions(+) - -diff --git a/examples/linux_ws_server_cpp/CMakeLists.txt b/examples/linux_ws_server_cpp/CMakeLists.txt -index 8f765ac5..cf73fc9b 100644 ---- a/examples/linux_ws_server_cpp/CMakeLists.txt -+++ b/examples/linux_ws_server_cpp/CMakeLists.txt -@@ -1,4 +1,6 @@ - cmake_minimum_required(VERSION 3.10) -+cmake_policy(VERSION 3.10) -+ - project(linux_ws_server) - - set(TARGET_NAME ${PROJECT_NAME}) -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 8736e126..de4c2d62 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -1,3 +1,6 @@ -+cmake_minimum_required(VERSION 3.10) -+cmake_policy(VERSION 3.10) -+ - # The C API library - set( LIB_TYPE "STATIC" ) - if (BUILD_SHARED_LIBS) -diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt -index 7aa34971..6aa8f0fd 100644 ---- a/unittest/CMakeLists.txt -+++ b/unittest/CMakeLists.txt -@@ -1,3 +1,6 @@ -+cmake_minimum_required(VERSION 3.10) -+cmake_policy(VERSION 3.10) -+ - # Determine if we should print to the output - if (CIVETWEB_ENABLE_THIRD_PARTY_OUTPUT) - set(THIRD_PARTY_LOGGING 0) -@@ -30,6 +33,7 @@ DOWNLOAD_NAME "master.zip" - "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" - "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" - "-DCMAKE_INSTALL_PREFIX=" -+ "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" - LOG_DOWNLOAD ${THIRD_PARTY_LOGGING} - LOG_UPDATE ${THIRD_PARTY_LOGGING} - LOG_CONFIGURE ${THIRD_PARTY_LOGGING} --- -2.34.1 - - -From c7df0249a29338f1f245b7f293f5ab8b03f963bf Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Thu, 11 Sep 2025 20:26:10 +0200 -Subject: [PATCH 156/173] Fix timer test - -Signed-off-by: DL6ER ---- - unittest/timertest.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/unittest/timertest.c b/unittest/timertest.c -index 17716d16..425e5ad8 100644 ---- a/unittest/timertest.c -+++ b/unittest/timertest.c -@@ -158,7 +158,7 @@ END_TEST - START_TEST(test_timer_oneshot_by_callback_retval) - { - struct mg_context ctx; -- int c[TIMERS_IN_TEST * 2]; -+ ptrdiff_t c[TIMERS_IN_TEST * 2]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); - -@@ -212,7 +212,7 @@ END_TEST - START_TEST(test_timer_oneshot_by_timer_add) - { - struct mg_context ctx; -- int c[TIMERS_IN_TEST * 2]; -+ ptrdiff_t c[TIMERS_IN_TEST * 2]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); +diff --git a/src/third_party/lua_struct.c b/src/third_party/lua_struct.c +index 17cd1a7d..505e7df2 100644 +--- a/src/third_party/lua_struct.c ++++ b/src/third_party/lua_struct.c +@@ -88,12 +88,14 @@ typedef struct Header { + } Header; -@@ -266,7 +266,7 @@ END_TEST - START_TEST(test_timer_mixed) - { - struct mg_context ctx; -- int c[TIMERS_IN_TEST]; -+ ptrdiff_t c[TIMERS_IN_TEST]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); --- -2.34.1 - - -From 8543cf792126064065bca70406b8acc825445153 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 28 Sep 2025 18:12:08 +0200 -Subject: [PATCH 157/173] Appveyor: Deactivate SSL for VS2019 / VS2022 - -It seems OpenSSL is not available in the build images for VS2019 and VS2022. -Test without it until there is time to analyze and fix the problem. -OpenSSL is still tested using VS2017. ---- - appveyor.yml | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/appveyor.yml b/appveyor.yml -index e54c2d5e..ad121e4e 100644 ---- a/appveyor.yml -+++ b/appveyor.yml -@@ -219,7 +219,7 @@ environment: - build_shared: NO - no_files: NO - enable_ipv6: YES -- enable_ssl: YES -+ enable_ssl: NO - enable_websockets: YES - no_cgi: NO - no_caching: NO -@@ -233,7 +233,7 @@ environment: - build_shared: NO - no_files: NO - enable_ipv6: YES -- enable_ssl: YES -+ enable_ssl: NO - enable_websockets: YES - no_cgi: NO - no_caching: NO --- -2.34.1 - - -From 3ee51c19cc75fc10ab217022f1656784ba718355 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 28 Sep 2025 18:17:53 +0200 -Subject: [PATCH 158/173] Update sqlite3 - -Note: CivetWeb has a copy of sqlite3 for convenience. For production, please consider using -the code files from the original sources. ---- - src/third_party/sqlite3.c | 77464 +++++++++++++++++++++++++----------- - src/third_party/sqlite3.h | 2397 +- - 2 files changed, 56922 insertions(+), 22939 deletions(-) - +-static int getnum (const char **fmt, int df) { ++static int getnum (lua_State *L, const char **fmt, int df) { + if (!isdigit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + int a = 0; + do { ++ if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0'))) ++ luaL_error(L, "integral size overflow"); + a = a*10 + *((*fmt)++) - '0'; + } while (isdigit(**fmt)); + return a; +@@ -114,9 +116,9 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) { + case 'f': return sizeof(float); + case 'd': return sizeof(double); + case 'x': return 1; +- case 'c': return getnum(fmt, 1); ++ case 'c': return getnum(L, fmt, 1); + case 'i': case 'I': { +- int sz = getnum(fmt, sizeof(int)); ++ int sz = getnum(L, fmt, sizeof(int)); + if (sz > MAXINTSIZE) + luaL_error(L, "integral size %d is larger than limit of %d", + sz, MAXINTSIZE); +@@ -149,7 +151,7 @@ static void controloptions (lua_State *L, int opt, const char **fmt, + case '>': h->endian = BIG; return; + case '<': h->endian = LITTLE; return; + case '!': { +- int a = getnum(fmt, MAXALIGN); ++ int a = getnum(L, fmt, MAXALIGN); + if (!isp2(a)) + luaL_error(L, "alignment %d is not a power of 2", a); + h->align = a; diff --git a/src/third_party/sqlite3.c b/src/third_party/sqlite3.c index c4317964..26a7a43d 100644 --- a/src/third_party/sqlite3.c @@ -185804,1104 +177932,1280 @@ index 910b687a..c2ed7503 100644 void (*xDestroy)(void*) ); + -+ /* APIs below this point are only available if iVersion>=3 */ ++ /* APIs below this point are only available if iVersion>=3 */ ++ ++ /* Create a new tokenizer */ ++ int (*xCreateTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void *pUserData, ++ fts5_tokenizer_v2 *pTokenizer, ++ void (*xDestroy)(void*) ++ ); ++ ++ /* Find an existing tokenizer */ ++ int (*xFindTokenizer_v2)( ++ fts5_api *pApi, ++ const char *zName, ++ void **ppUserData, ++ fts5_tokenizer_v2 **ppTokenizer ++ ); + }; + + /* +@@ -12172,3 +13772,4 @@ struct fts5_api { + #endif /* _FTS5_H */ + + /******** End of fts5.h *********/ ++#endif /* SQLITE3_H */ +diff --git a/src/timer.inl b/src/timer.inl +index 9b8d5539..39d68dfc 100644 +--- a/src/timer.inl ++++ b/src/timer.inl +@@ -39,13 +39,16 @@ TIMER_API double + timer_getcurrenttime(struct mg_context *ctx) + { + #if defined(_WIN32) ++ uint64_t now_tick64 = 0; ++#if defined(_WIN64) ++ now_tick64 = GetTickCount64(); ++#else + /* GetTickCount returns milliseconds since system start as + * unsigned 32 bit value. It will wrap around every 49.7 days. + * We need to use a 64 bit counter (will wrap in 500 mio. years), + * by adding the 32 bit difference since the last call to a + * 64 bit counter. This algorithm will only work, if this + * function is called at least once every 7 weeks. */ +- uint64_t now_tick64 = 0; + DWORD now_tick = GetTickCount(); + + if (ctx->timers) { +@@ -55,6 +58,7 @@ timer_getcurrenttime(struct mg_context *ctx) + ctx->timers->last_tick = now_tick; + pthread_mutex_unlock(&ctx->timers->mutex); + } ++#endif + return (double)now_tick64 * 1.0E-3; + #else + struct timespec now_ts; +diff --git a/test/donate.html b/test/donate.html +deleted file mode 100644 +index da6e3978..00000000 +--- a/test/donate.html ++++ /dev/null +@@ -1,32 +0,0 @@ +- +- +- +- +- +-CivetWeb web server maintenance (#523): +- +-

        +-

        +- +- +- +- +-
        +-

        +- +-

        +- +-donate +- +-

        +- +-

        +- +-donate +- +-

        +- +- +- +- +- +diff --git a/test/page3.ssjs b/test/page3.ssjs +index 71e55e34..6e4f62f9 100644 +--- a/test/page3.ssjs ++++ b/test/page3.ssjs +@@ -19,8 +19,10 @@ opts = [ + "extra_mime_types", + "listening_ports", + "document_root", ++"fallback_document_root", + "ssl_certificate", + "num_threads", ++"prespawn_threads", + "run_as_user", + "url_rewrite_patterns", + "hide_files_patterns", +@@ -32,6 +34,7 @@ opts = [ + "lua_server_page_pattern", + "_experimental_duktape_script_pattern", + "websocket_root", ++"fallback_websocket_root", + "lua_websocket_pattern", + "access_control_allow_origin", + "error_pages", +diff --git a/test/page4.lp b/test/page4.lp +index 52374907..7a4934e3 100644 +--- a/test/page4.lp ++++ b/test/page4.lp +@@ -22,10 +22,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr +

        Tags

        + + +-<? greeting = 'CiwetWeb' ?>
        ++<? greeting = 'CivetWeb' ?>
        + <strong><?= greeting %></strong>
        +

        +- ++ + ==>
        + +
        +diff --git a/test/page4kepler.lp b/test/page4kepler.lp +index 8a986b5d..a7054897 100644 +--- a/test/page4kepler.lp ++++ b/test/page4kepler.lp +@@ -19,10 +19,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr +

        Tags

        + + +-<? greeting = 'CiwetWeb' ?>
        ++<? greeting = 'CivetWeb' ?>
        + <strong><?= greeting %></strong>
        +

        +- ++ + ==>
        + +
        +diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt +index 7aa34971..6aa8f0fd 100644 +--- a/unittest/CMakeLists.txt ++++ b/unittest/CMakeLists.txt +@@ -1,3 +1,6 @@ ++cmake_minimum_required(VERSION 3.10) ++cmake_policy(VERSION 3.10) ++ + # Determine if we should print to the output + if (CIVETWEB_ENABLE_THIRD_PARTY_OUTPUT) + set(THIRD_PARTY_LOGGING 0) +@@ -30,6 +33,7 @@ DOWNLOAD_NAME "master.zip" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" + "-DCMAKE_INSTALL_PREFIX=" ++ "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" + LOG_DOWNLOAD ${THIRD_PARTY_LOGGING} + LOG_UPDATE ${THIRD_PARTY_LOGGING} + LOG_CONFIGURE ${THIRD_PARTY_LOGGING} +diff --git a/unittest/private.c b/unittest/private.c +index 8e85d71e..7540dba6 100644 +--- a/unittest/private.c ++++ b/unittest/private.c +@@ -1621,10 +1621,13 @@ START_TEST(test_config_options) + ck_assert_str_eq("extra_mime_types", config_options[EXTRA_MIME_TYPES].name); + ck_assert_str_eq("listening_ports", config_options[LISTENING_PORTS].name); + ck_assert_str_eq("document_root", config_options[DOCUMENT_ROOT].name); ++ ck_assert_str_eq("fallback_document_root", ++ config_options[FALLBACK_DOCUMENT_ROOT].name); + ck_assert_str_eq("ssl_certificate", config_options[SSL_CERTIFICATE].name); + ck_assert_str_eq("ssl_certificate_chain", + config_options[SSL_CERTIFICATE_CHAIN].name); + ck_assert_str_eq("num_threads", config_options[NUM_THREADS].name); ++ ck_assert_str_eq("prespawn_threads", config_options[PRESPAWN_THREADS].name); + ck_assert_str_eq("run_as_user", config_options[RUN_AS_USER].name); + ck_assert_str_eq("url_rewrite_patterns", + config_options[URL_REWRITE_PATTERN].name); +@@ -1672,6 +1675,8 @@ START_TEST(test_config_options) + #endif + #if defined(USE_WEBSOCKET) + ck_assert_str_eq("websocket_root", config_options[WEBSOCKET_ROOT].name); ++ ck_assert_str_eq("fallback_websocket_root", ++ config_options[FALLBACK_WEBSOCKET_ROOT].name); + #endif + #if defined(USE_LUA) && defined(USE_WEBSOCKET) + ck_assert_str_eq("lua_websocket_pattern", +diff --git a/unittest/public_server.c b/unittest/public_server.c +index 02bf5255..27a3eb9f 100644 +--- a/unittest/public_server.c ++++ b/unittest/public_server.c +@@ -457,10 +457,14 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) + ck_assert_int_eq(portinfo[0].port, 0); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 0); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 0); ++ ck_assert_int_eq(portinfo[1].is_bound, 0); + + ret = mg_get_server_ports(ctx, 4, portinfo); + ck_assert_int_eq(ret, 1); +@@ -472,10 +476,14 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) + ck_assert_int_eq(portinfo[0].port, 8080); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 1); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 0); ++ ck_assert_int_eq(portinfo[1].is_bound, 0); + + test_sleep(1); + +@@ -649,7 +657,7 @@ START_TEST(test_mg_start_stop_https_server) + OPTIONS[opt_idx++] = "."; + #endif + OPTIONS[opt_idx++] = "listening_ports"; +- OPTIONS[opt_idx++] = "8080r,8443s"; ++ OPTIONS[opt_idx++] = "8080r,8443os"; + OPTIONS[opt_idx++] = "ssl_certificate"; + OPTIONS[opt_idx++] = ssl_cert; + +@@ -674,10 +682,14 @@ START_TEST(test_mg_start_stop_https_server) + ck_assert_int_eq(portinfo[0].port, 0); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 0); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 0); + ck_assert_int_eq(portinfo[1].protocol, 0); + ck_assert_int_eq(portinfo[1].port, 0); + ck_assert_int_eq(portinfo[1].is_ssl, 0); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 0); ++ ck_assert_int_eq(portinfo[1].is_bound, 0); + + ret = mg_get_server_ports(ctx, 4, portinfo); + ck_assert_int_eq(ret, 2); +@@ -685,14 +697,20 @@ START_TEST(test_mg_start_stop_https_server) + ck_assert_int_eq(portinfo[0].port, 8080); + ck_assert_int_eq(portinfo[0].is_ssl, 0); + ck_assert_int_eq(portinfo[0].is_redirect, 1); ++ ck_assert_int_eq(portinfo[0].is_optional, 0); ++ ck_assert_int_eq(portinfo[0].is_bound, 1); + ck_assert_int_eq(portinfo[1].protocol, 1); + ck_assert_int_eq(portinfo[1].port, 8443); + ck_assert_int_eq(portinfo[1].is_ssl, 1); + ck_assert_int_eq(portinfo[1].is_redirect, 0); ++ ck_assert_int_eq(portinfo[1].is_optional, 1); ++ ck_assert_int_eq(portinfo[1].is_bound, 1); + ck_assert_int_eq(portinfo[2].protocol, 0); + ck_assert_int_eq(portinfo[2].port, 0); + ck_assert_int_eq(portinfo[2].is_ssl, 0); + ck_assert_int_eq(portinfo[2].is_redirect, 0); ++ ck_assert_int_eq(portinfo[2].is_optional, 0); ++ ck_assert_int_eq(portinfo[2].is_bound, 0); + + test_sleep(1); + +@@ -771,7 +789,7 @@ START_TEST(test_mg_server_and_client_tls) + OPTIONS[opt_idx++] = "."; + #endif + OPTIONS[opt_idx++] = "listening_ports"; +- OPTIONS[opt_idx++] = "8080r,8443s"; ++ OPTIONS[opt_idx++] = "8080r,8443os"; + OPTIONS[opt_idx++] = "ssl_certificate"; + OPTIONS[opt_idx++] = server_cert; + OPTIONS[opt_idx++] = "ssl_verify_peer"; +@@ -800,14 +818,20 @@ START_TEST(test_mg_server_and_client_tls) + ck_assert_int_eq(ports[0].port, 8080); + ck_assert_int_eq(ports[0].is_ssl, 0); + ck_assert_int_eq(ports[0].is_redirect, 1); ++ ck_assert_int_eq(ports[0].is_optional, 0); ++ ck_assert_int_eq(ports[0].is_bound, 1); + ck_assert_int_eq(ports[1].protocol, 1); + ck_assert_int_eq(ports[1].port, 8443); + ck_assert_int_eq(ports[1].is_ssl, 1); + ck_assert_int_eq(ports[1].is_redirect, 0); ++ ck_assert_int_eq(ports[1].is_optional, 1); ++ ck_assert_int_eq(ports[1].is_bound, 1); + ck_assert_int_eq(ports[2].protocol, 0); + ck_assert_int_eq(ports[2].port, 0); + ck_assert_int_eq(ports[2].is_ssl, 0); + ck_assert_int_eq(ports[2].is_redirect, 0); ++ ck_assert_int_eq(ports[2].is_optional, 0); ++ ck_assert_int_eq(ports[2].is_bound, 0); + + test_sleep(1); + +@@ -823,7 +847,7 @@ START_TEST(test_mg_server_and_client_tls) + * while Ubuntu Xenial, Ubuntu Trusty and Windows test containers at + * Travis CI do not. Maybe it is OpenSSL version specific. + */ +-#if defined(OPENSSL_API_1_1) ++#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) + if (client_conn) { + /* Connect succeeds, but the connection is unusable. */ + mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n"); +@@ -2608,6 +2632,36 @@ FormGet(struct mg_connection *conn, void *cbdata) + } + + ++static int ++FormError(struct mg_connection *conn, void *cbdata) ++{ ++ const struct mg_request_info *req_info = mg_get_request_info(conn); ++ int ret; ++ struct mg_form_data_handler fdh = {NULL, NULL, NULL, NULL}; ++ ++ (void)cbdata; ++ ++ ck_assert(req_info != NULL); ++ ++ mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n"); ++ fdh.user_data = (void *)&g_field_found_return; ++ ++ /* Call the form handler */ ++ g_field_step = 0; ++ g_field_found_return = MG_FORM_FIELD_STORAGE_GET; ++ ret = mg_handle_form_request(conn, &fdh); ++ g_field_found_return = -888; ++ ck_assert_int_eq(ret, -1); ++ ck_assert_int_eq(g_field_step, 0); ++ mg_printf(conn, "%i\r\n", ret); ++ g_field_step = 1000; ++ ++ mark_point(); ++ ++ return 1; ++} ++ ++ + static int + FormStore(struct mg_connection *conn, + void *cbdata, +@@ -2727,6 +2781,7 @@ START_TEST(test_handle_form) + ck_assert_str_eq(opt, "8884"); + + mg_set_request_handler(ctx, "/handle_form", FormGet, NULL); ++ mg_set_request_handler(ctx, "/handle_form_error", FormError, NULL); + mg_set_request_handler(ctx, "/handle_form_store", FormStore1, NULL); + mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, NULL); + +@@ -2797,6 +2852,23 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + ++ /* ++ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ * ++ * preamble := discard-text ++ * epilogue := discard-text ++ * ++ * discard-text := *(*text CRLF) *text ++ * ++ * text := ++ */ ++ + /* Handle form: "POST multipart/form-data" */ + multipart_body = + "--multipart-form-data-boundary--see-RFC-2388\r\n" +@@ -2924,6 +2996,7 @@ START_TEST(test_handle_form) + + + /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ ++ /* use the most universal possible (no edge cases) body*/ + client_conn = + mg_download("localhost", + 8884, +@@ -3016,6 +3089,786 @@ START_TEST(test_handle_form) + ck_assert_int_eq(client_ri->status_code, 200); + mg_close_connection(client_conn); + ++ /* Handle form: "POST multipart/form-data" without trailing CRLF*/ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2366); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ ++ /* Handle form: "POST multipart/form-data" with epilogue*/ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n" ++ "epilogue\r\n" ++ "epilogue\r\n" ++ "\r\n" ++ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" ++ "\r\n" ++ "\r\n" ++ "\r\n" ++ "epilogue\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2453); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ ++ /* Handle form: "POST multipart/form-data" with preamble*/ ++ /* ++ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 ++ * ++ * multipart-body := [preamble CRLF] ++ * dash-boundary transport-padding CRLF ++ * body-part *encapsulation ++ * close-delimiter transport-padding ++ * [CRLF epilogue] ++ * ++ * preamble := discard-text ++ * ++ * discard-text := *(*text CRLF) *text ++ * ++ * text := ++ */ ++ multipart_body = ++ "\r\n" ++ "\r\npreamble" ++ "\r\npreamble" ++ "\r\npreamble" ++ "\r\n" ++ "\r\npreamble" ++ "\r\n" ++ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" ++ "\r\n" ++ "\r\n\t\t\t \t\t\t" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2478); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); ++ ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); ++ ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); ++ ++ /* Handle form: "POST multipart/form-data" with transport padding*/ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388 \r\n" ++ "Content-Disposition: form-data; name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2382); /* not required */ ++ ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); + -+ /* Create a new tokenizer */ -+ int (*xCreateTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void *pUserData, -+ fts5_tokenizer_v2 *pTokenizer, -+ void (*xDestroy)(void*) -+ ); ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); + -+ /* Find an existing tokenizer */ -+ int (*xFindTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void **ppUserData, -+ fts5_tokenizer_v2 **ppTokenizer -+ ); - }; - - /* -@@ -12172,3 +13772,4 @@ struct fts5_api { - #endif /* _FTS5_H */ - - /******** End of fts5.h *********/ -+#endif /* SQLITE3_H */ --- -2.34.1 - - -From cafd5f8fae3b859b7f8c29feb03ea075c7221497 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 28 Sep 2025 18:23:54 +0200 -Subject: [PATCH 159/173] Format code - -Use clang-format to format all code files ---- - src/civetweb.c | 70 +++++++++++++++++++--------------- - src/handle_form.inl | 15 ++++---- - src/mod_lua.inl | 3 +- - src/third_party/civetweb_lua.h | 6 +-- - 4 files changed, 51 insertions(+), 43 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 2758a784..78345fb5 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -161,7 +161,7 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check"); - * file system. - * NO_FILES only disables the automatic mapping between URLs and local - * file names. -- * NO_FILESYSTEM = do not access any file at all. Useful for embedded -+ * NO_FILESYSTEMS = do not access any file at all. Useful for embedded - * devices without file system. Logging to files in not available - * (use callbacks instead) and API functions like mg_send_file are not - * available. -@@ -2253,7 +2253,7 @@ static const struct mg_option config_options[] = { - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, - #endif -- {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"}, -+ {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"}, - {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"}, - {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"}, -@@ -4235,26 +4235,29 @@ send_cors_header(struct mg_connection *conn) - conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS]; - const char *cors_meth_cfg = - conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS]; -- const char *cors_repl_asterisk_with_orig_cfg = -- conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; -- -- if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr && cors_repl_asterisk_with_orig_cfg && *cors_repl_asterisk_with_orig_cfg) { -- int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); -- -+ const char *cors_repl_asterisk_with_orig_cfg = -+ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); + -+ if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr -+ && cors_repl_asterisk_with_orig_cfg -+ && *cors_repl_asterisk_with_orig_cfg) { -+ int cors_repl_asterisk_with_orig = -+ mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); ++ /* Handle form: "POST multipart/form-data" with custom name fields in the ++ * Content-Disposition */ ++ multipart_body = ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; " ++ "custom1name=\"1\"; " ++ "custom2name=\"2\"; " ++ "custom3name=\"3\"; " ++ "custom4name=\"4\"; " ++ "name=\"textin\"\r\n" ++ "\r\n" ++ "text\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"radio1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=radio2\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"check1\"\r\n" ++ "\r\n" ++ "val1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"numberin\"\r\n" ++ "\r\n" ++ "1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datein\"\r\n" ++ "\r\n" ++ "1.1.2016\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"colorin\"\r\n" ++ "\r\n" ++ "#80ff00\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"rangein\"\r\n" ++ "\r\n" ++ "3\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"monthin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"weekin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"timein\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimen\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"emailin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"searchin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"telin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"urlin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=filesin; filename=\r\n" ++ "Content-Type: application/octet-stream\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"selectin\"\r\n" ++ "\r\n" ++ "opt1\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Disposition: form-data; name=\"message\"\r\n" ++ "\r\n" ++ "Text area default text.\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 2439); /* not required */ + - /* Cross-origin resource sharing (CORS), see - * http://www.html5rocks.com/en/tutorials/cors/, - * http://www.html5rocks.com/static/images/cors_server_flowchart.png - * CORS preflight is not supported for files. */ - if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') { - mg_response_header_add(conn, -- "Access-Control-Allow-Origin", -- origin_hdr, -- -1); -+ "Access-Control-Allow-Origin", -+ origin_hdr, -+ -1); - } else { - mg_response_header_add(conn, -- "Access-Control-Allow-Origin", -- cors_orig_cfg, -- -1); -+ "Access-Control-Allow-Origin", -+ cors_orig_cfg, -+ -1); - } - } - -@@ -15100,7 +15103,7 @@ handle_request(struct mg_connection *conn) - } - return; - } -- ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); + - - /* 1.3. decode url (if config says so) */ - if (should_decode_url(conn)) { -@@ -15128,11 +15131,10 @@ handle_request(struct mg_connection *conn) - } - remove_dot_segments(tmp); - ri->local_uri = tmp; -- #if !defined(NO_FILES) /* Only compute if later code can actually use it */ -- /* Cache URI length once; recompute only if the buffer changes later. */ -- uri_len = (int)strlen(ri->local_uri); -- #endif -- -+#if !defined(NO_FILES) /* Only compute if later code can actually use it */ -+ /* Cache URI length once; recompute only if the buffer changes later. */ -+ uri_len = (int)strlen(ri->local_uri); -+#endif - - - /* step 1. completed, the url is known now */ -@@ -15186,18 +15188,20 @@ handle_request(struct mg_connection *conn) - const char *cors_acrm = get_header(ri->http_headers, - ri->num_headers, - "Access-Control-Request-Method"); -- const char *cors_repl_asterisk_with_orig_cfg = -- conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; -- -+ const char *cors_repl_asterisk_with_orig_cfg = -+ conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN]; ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); + - /* Todo: check if cors_origin is in cors_orig_cfg. - * Or, let the client check this. */ - - if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0) - && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0) - && (cors_origin != NULL) && (cors_acrm != NULL) -- && (cors_repl_asterisk_with_orig_cfg != NULL) && (*cors_repl_asterisk_with_orig_cfg != 0)) { -- int cors_repl_asterisk_with_orig = mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); -- -+ && (cors_repl_asterisk_with_orig_cfg != NULL) -+ && (*cors_repl_asterisk_with_orig_cfg != 0)) { -+ int cors_repl_asterisk_with_orig = -+ mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes"); ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); + - /* This is a valid CORS preflight, and the server is configured - * to handle it automatically. */ - const char *cors_acrh = -@@ -15218,7 +15222,10 @@ handle_request(struct mg_connection *conn) - "Content-Length: 0\r\n" - "Connection: %s\r\n", - date, -- (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') ? cors_origin : cors_orig_cfg, -+ (cors_repl_asterisk_with_orig == 0 -+ && cors_orig_cfg[0] == '*') -+ ? cors_origin -+ : cors_orig_cfg, - ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg), - suggest_connection_header(conn)); - -@@ -15610,7 +15617,7 @@ handle_request(struct mg_connection *conn) - - /* 12. Directory uris should end with a slash */ - if (file.stat.is_directory && (uri_len > 0) -- && (ri->local_uri[uri_len - 1] != '/')) { -+ && (ri->local_uri[uri_len - 1] != '/')) { - - - /* Path + server root */ -@@ -15640,7 +15647,8 @@ handle_request(struct mg_connection *conn) - len++; - } - -- /* Append with size of space left for query string + null terminator */ -+ /* Append with size of space left for query string + null -+ * terminator */ - size_t max_append = buflen - len - 1; - strncat(new_path, ri->query_string, max_append); - } -@@ -18907,12 +18915,12 @@ get_uri_type(const char *uri) - for (i = 0; uri[i] != 0; i++) { - /* Check for CRLF injection attempts */ - if (uri[i] == '%') { -- if (uri[i+1] == '0' && (uri[i+2] == 'd' || uri[i+2] == 'D')) { -+ if (uri[i + 1] == '0' && (uri[i + 2] == 'd' || uri[i + 2] == 'D')) { - /* Found %0d (CR) */ - DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri); - return 0; - } -- if (uri[i+1] == '0' && (uri[i+2] == 'a' || uri[i+2] == 'A')) { -+ if (uri[i + 1] == '0' && (uri[i + 2] == 'a' || uri[i + 2] == 'A')) { - /* Found %0a (LF) */ - DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri); - return 0; -diff --git a/src/handle_form.inl b/src/handle_form.inl -index cdff2a13..bc76f7d3 100644 ---- a/src/handle_form.inl -+++ b/src/handle_form.inl -@@ -109,8 +109,8 @@ url_encoded_field_get( - return MG_FORM_FIELD_STORAGE_ABORT; - } - -- key_dec_len = mg_url_decode( -- key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); -+ key_dec_len = -+ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); - - if (*value_len >= 2 && value[*value_len - 2] == '%') - *value_len -= 2; -@@ -146,7 +146,8 @@ unencoded_field_get(const struct mg_connection *conn, - int key_dec_len; - (void)conn; - -- key_dec_len = mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); -+ key_dec_len = -+ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); - if (key_dec_len < 0) { - return MG_FORM_FIELD_STORAGE_ABORT; - } -@@ -488,14 +489,14 @@ mg_handle_form_request(struct mg_connection *conn, - vallen = (ptrdiff_t)strlen(val); - end_of_key_value_pair_found = all_data_read; - if ((buf + buf_fill) > (val + vallen)) { -- /* Avoid DoS attacks by having a zero byte in the middle of -- * a request that is supposed to be URL encoded. Since this -- * request is certainly invalid, according to the protocol -+ /* Avoid DoS attacks by having a zero byte in the middle -+ * of a request that is supposed to be URL encoded. -+ * Since this request is certainly invalid, according to -+ * the protocol - * specification, stop processing it. Fixes #1348 */ - abort_read = 1; - break; - } -- - } - - if (field_storage == MG_FORM_FIELD_STORAGE_GET) { -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index f2e40237..e8279099 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -2700,7 +2700,8 @@ civetweb_open_lua_libs(lua_State *L) - luaL_openlibs(L); - #else - // In Lua 5.5 and later has become a macro -- extern void (luaL_openselectedlibs) (lua_State *L, int load, int preload); -+ extern void( -+ luaL_openselectedlibs)(lua_State * L, int load, int preload); - luaL_openselectedlibs(L, ~0, 0); - #endif - } -diff --git a/src/third_party/civetweb_lua.h b/src/third_party/civetweb_lua.h -index 15a76c1d..8c59aaa1 100644 ---- a/src/third_party/civetweb_lua.h -+++ b/src/third_party/civetweb_lua.h -@@ -49,7 +49,7 @@ extern "C" { - #define LUA_ERRGCMM 999 /* not supported */ - #define mg_lua_load(a, b, c, d, e) lua_load(a, b, c, d) - #define lua_rawlen lua_objlen --#define mg_lua_newstate(a, b) \ -+#define mg_lua_newstate(a, b) \ - luaL_newstate() /* Must use luaL_newstate() for 64 bit target */ - #define lua_pushinteger lua_pushnumber - #define luaL_newlib(L, t) \ -@@ -62,9 +62,7 @@ extern "C" { - } - #define luaL_setfuncs(L, r, u) lua_register(L, r->name, r->func) - --#elif LUA_VERSION_NUM == 502 || \ -- LUA_VERSION_NUM == 503 || \ -- LUA_VERSION_NUM == 504 -+#elif LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504 - /* Lua 5.2 - 5.4 detected */ - #define mg_lua_load lua_load - #define mg_lua_newstate lua_newstate --- -2.34.1 - - -From 3784be9d7c01b03b027408fee4d6d5d8b89136e6 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Mon, 29 Sep 2025 22:00:26 +0200 -Subject: [PATCH 160/173] AppVeyor: Add Visual Studio 2022 - -add compiler version settings for VS2022 -add verstion setting for VS2026 (preview) ---- - appveyor.yml | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/appveyor.yml b/appveyor.yml -index ad121e4e..b40e514f 100644 ---- a/appveyor.yml -+++ b/appveyor.yml -@@ -341,6 +341,8 @@ before_build: - - if "%compiler_version%"=="19" (set "vs_version=14" & set "vs_year=2015") - - if "%compiler_version%"=="20" (set "vs_version=15" & set "vs_year=2017") - - if "%compiler_version%"=="21" (set "vs_version=16" & set "vs_year=2019") -+ - if "%compiler_version%"=="22" (set "vs_version=17" & set "vs_year=2022") -+ - if "%compiler_version%"=="23" (set "vs_version=18" & set "vs_year=2026") - - if "%compiler_name%"=="msvc" (set "generator=Visual Studio %vs_version% %vs_year%") - - set "arch_arg= " - - if "%compiler_name%"=="msvc" ( --- -2.34.1 - - -From b6ef58f4c4c7fbe90fd1065bccf45b143345f1a6 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Tue, 30 Sep 2025 23:16:38 +0200 -Subject: [PATCH 161/173] Do not scan third party projects - -Third party projects need to be taken from their original repositories. ---- - .github/workflows/codeql.yml | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 22fad79d..0dc7ce4a 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -14,10 +14,14 @@ name: "CodeQL" - on: - push: - branches: [ "master" ] -+ paths-ignore: -+ - src/third_party/** - schedule: - - cron: '0 0 * * *' - pull_request: - branches: '*' -+ paths-ignore: -+ - src/third_party/** - - jobs: - analyze: --- -2.34.1 - - -From 52fb72ed2318c6233b7112a80cdd9cb94e3e7735 Mon Sep 17 00:00:00 2001 -From: DL6ER -Date: Thu, 2 Oct 2025 18:10:29 +0200 -Subject: [PATCH 162/173] Allow setting SSL cipher list also with MbedTLS - -Signed-off-by: DL6ER ---- - src/civetweb.c | 3 +- - src/mod_mbedtls.inl | 76 +++++++++++++++++++++++++++++++++++++++++++-- - 2 files changed, 76 insertions(+), 3 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 78345fb5..f9978fd1 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -16805,7 +16805,8 @@ mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx) - return 0; - } - -- return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE]) -+ return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE], -+ dom_ctx->config[SSL_CIPHER_LIST]) - == 0 - ? 1 - : 0; -diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl -index 03d189ea..8256b3a4 100644 ---- a/src/mod_mbedtls.inl -+++ b/src/mod_mbedtls.inl -@@ -33,7 +33,7 @@ typedef struct { - - - /* public api */ --int mbed_sslctx_init(SSL_CTX *ctx, const char *crt); -+int mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist); - void mbed_sslctx_uninit(SSL_CTX *ctx); - void mbed_ssl_close(mbedtls_ssl_context *ssl); - int mbed_ssl_accept(mbedtls_ssl_context **ssl, -@@ -43,6 +43,9 @@ int mbed_ssl_accept(mbedtls_ssl_context **ssl, - int mbed_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, int len); - int mbed_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, int len); - -+/* Set the ciphersuites to be used by mbedtls using a comma-separated string */ -+int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list); ++ /* Handle form error cases */ ++ /* Handle form: "POST multipart/form-data" empty body */ ++ multipart_body = ""; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 0); /* not required */ + - static void mbed_debug(void *context, - int level, - const char *file, -@@ -52,7 +55,7 @@ static int mbed_ssl_handshake(mbedtls_ssl_context *ssl); - - - int --mbed_sslctx_init(SSL_CTX *ctx, const char *crt) -+mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) - { - mbedtls_ssl_config *conf; - int rc; -@@ -154,6 +157,14 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt) - DEBUG_TRACE("TLS cannot set certificate and private key (%i)", rc); - return -1; - } ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form_error HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); + -+ /* Set ciphersuites if specified */ -+ if (cipherlist != NULL && -+ cipherlist[0] != '\0' && -+ mbed_sslctx_set_ciphersuites(conf, cipherlist) != 0) { -+ DEBUG_TRACE("Failed to set ciphersuites: no valid ciphersuites are found in the list"); -+ return -1; -+ } - return 0; - } - -@@ -279,4 +290,65 @@ mbed_debug(void *user_param, - str); - } - -+/** -+ * @brief Sets the list of allowed ciphersuites for an mbedTLS SSL configuration. -+ * -+ * Parses a comma-separated list of ciphersuite names, converts them to their -+ * corresponding mbedTLS ciphersuite IDs, and configures the SSL context to use -+ * only those ciphersuites. -+ * -+ * @param conf Pointer to the mbedTLS SSL configuration structure. -+ * @param cipher_list Comma-separated string of ciphersuite names. -+ * @return 0 on success, -+ * -1 if conf or cipher_list is NULL, -+ * -2 if no valid ciphersuites are found in the list. -+ * -+ * @note The ciphersuite ID array is static and must remain valid after the function returns, -+ * as mbedtls_ssl_conf_ciphersuites() does not copy the array. -+ */ -+int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list) { -+ if (conf == NULL || cipher_list == NULL) { -+ return -1; ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } + } ++ client_ri = mg_get_response_info(client_conn); + -+ // The array for ciphersuite IDs must remain valid after this function -+ // returns as mbedtls_ssl_conf_ciphersuites() does not copy the array, -+ // but only stores the pointer. We do not allow more than 64 cipher -+ // suites for simplicity. -+ static int ciphersuites[64] = { 0 }; -+ size_t count = 0; ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); + -+ char buf[1024]; -+ strncpy(buf, cipher_list, sizeof(buf) - 1); -+ buf[sizeof(buf) - 1] = '\0'; ++ /* Handle form: "POST multipart/form-data" very long preamble */ ++ multipart_body = "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "preamblepreamblepreamblepreamblepreamble\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388\r\n"; ++ "Content-Disposition: form-data; name=\"passwordin\"\r\n" ++ "\r\n" ++ "\r\n" ++ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; + -+ char *token = strtok(buf, ","); -+ while (token && count < 63) { -+ // Remove leading/trailing whitespace -+ while (*token == ' ' || *token == '\t') token++; -+ char *end = token + strlen(token) - 1; -+ while (end > token && (*end == ' ' || *end == '\t')) { -+ *end = '\0'; -+ end--; -+ } -+ const mbedtls_ssl_ciphersuite_t *ciphersuite = mbedtls_ssl_ciphersuite_from_string(token); -+ if (ciphersuite != NULL) { -+ const int id = mbedtls_ssl_ciphersuite_get_id(ciphersuite); -+ DEBUG_TRACE("Adding ciphersuite '%s' (ID %d)", token, id); -+ ciphersuites[count++] = id; -+ } -+ token = strtok(NULL, ","); -+ } -+ ciphersuites[count] = 0; ++ body_len = strlen(multipart_body); ++ ck_assert_uint_eq(body_len, 1768); /* not required */ + -+ if (count == 0) { -+ DEBUG_TRACE("No valid ciphersuites found"); -+ return -2; // No valid ciphersuites found -+ } ++ client_conn = ++ mg_download("localhost", ++ 8884, ++ 0, ++ ebuf, ++ sizeof(ebuf), ++ "POST /handle_form_error HTTP/1.1\r\n" ++ "Host: localhost:8884\r\n" ++ "Connection: close\r\n" ++ "Content-Type: multipart/form-data; " ++ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" ++ "Content-Length: %u\r\n" ++ "\r\n%s", ++ (unsigned int)body_len, ++ multipart_body); + -+ // Set the ciphersuites -+ mbedtls_ssl_conf_ciphersuites(conf, ciphersuites); -+ return 0; -+} ++ ck_assert(client_conn != NULL); ++ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { ++ test_sleep(1); ++ if (g_field_step == 1000) { ++ break; ++ } ++ } ++ client_ri = mg_get_response_info(client_conn); + - #endif /* USE_MBEDTLS */ --- -2.34.1 - - -From 27415007b1c538296b31d6672cf09d2261a84c0d Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sat, 11 Oct 2025 10:03:17 +0000 -Subject: [PATCH 163/173] Bump github/codeql-action from 3 to 4 - -Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. -- [Release notes](https://github.com/github/codeql-action/releases) -- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) -- [Commits](https://github.com/github/codeql-action/compare/v3...v4) - ---- -updated-dependencies: -- dependency-name: github/codeql-action - dependency-version: '4' - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/codeql.yml | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 0dc7ce4a..96f98bbb 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -55,7 +55,7 @@ jobs: - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL -- uses: github/codeql-action/init@v3 -+ uses: github/codeql-action/init@v4 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. -@@ -82,7 +82,7 @@ jobs: - make build WITH_ALL=1 - - - name: Perform CodeQL Analysis -- uses: github/codeql-action/analyze@v3 -+ uses: github/codeql-action/analyze@v4 - with: - category: "/language:${{matrix.language}}" - upload: false -@@ -111,7 +111,7 @@ jobs: - output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif - - - name: Upload CodeQL results to code scanning -- uses: github/codeql-action/upload-sarif@v3 -+ uses: github/codeql-action/upload-sarif@v4 - with: - sarif_file: ${{ steps.step1.outputs.sarif-output }} - category: "/language:${{matrix.language}}" --- -2.34.1 - - -From 26131e0b6d0100afa81f811563b4936fe35ca538 Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sat, 25 Oct 2025 10:02:46 +0000 -Subject: [PATCH 164/173] Bump actions/upload-artifact from 4 to 5 - -Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. -- [Release notes](https://github.com/actions/upload-artifact/releases) -- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) - ---- -updated-dependencies: -- dependency-name: actions/upload-artifact - dependency-version: '5' - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/cifuzz.yml | 2 +- - .github/workflows/codeql.yml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml -index d62ff855..7644d847 100644 ---- a/.github/workflows/cifuzz.yml -+++ b/.github/workflows/cifuzz.yml -@@ -18,7 +18,7 @@ jobs: - fuzz-seconds: 600 - dry-run: false - - name: Upload Crash -- uses: actions/upload-artifact@v4 -+ uses: actions/upload-artifact@v5 - if: failure() && steps.build.outcome == 'success' - with: - name: artifacts -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 96f98bbb..31d0c4bc 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -118,7 +118,7 @@ jobs: - - - name: Upload CodeQL results as an artifact - if: success() || failure() -- uses: actions/upload-artifact@v4 -+ uses: actions/upload-artifact@v5 - with: - name: codeql-results - path: ${{ steps.step1.outputs.sarif-output }} --- -2.34.1 - - -From 9bbef31027c1c7bccddae122abcd92a5ec8e0375 Mon Sep 17 00:00:00 2001 -From: James -Date: Tue, 4 Nov 2025 09:21:32 +0000 -Subject: [PATCH 165/173] add server address to reqinfo - ---- - include/civetweb.h | 1 + - src/civetweb.c | 4 ++++ - 2 files changed, 5 insertions(+) - -diff --git a/include/civetweb.h b/include/civetweb.h -index 2665d646..f585df25 100644 ---- a/include/civetweb.h -+++ b/include/civetweb.h -@@ -164,6 +164,7 @@ struct mg_request_info { - const char *remote_user; /* Authenticated user, or NULL if no auth - used */ - char remote_addr[48]; /* Client's IP address as a string. */ -+ char server_addr[48]; /* Server's IP address as a string. */ - - long long content_length; /* Length (in bytes) of the request body, - can be -1 if no length was given. */ -diff --git a/src/civetweb.c b/src/civetweb.c -index f9978fd1..a399ce45 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -20364,6 +20364,10 @@ worker_thread_run(struct mg_connection *conn) - sockaddr_to_string(conn->request_info.remote_addr, - sizeof(conn->request_info.remote_addr), - &conn->client.rsa); -+ -+ sockaddr_to_string(conn->request_info.server_addr, -+ sizeof(conn->request_info.server_addr), -+ &conn->client.lsa); - - DEBUG_TRACE("Incoming %sconnection from %s", - (conn->client.is_ssl ? "SSL " : ""), --- -2.34.1 - - -From 389197bb68d86d242734a373c8f099d186c5a03f Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sat, 22 Nov 2025 10:03:21 +0000 -Subject: [PATCH 166/173] Bump actions/checkout from 5 to 6 - -Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. -- [Release notes](https://github.com/actions/checkout/releases) -- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) -- [Commits](https://github.com/actions/checkout/compare/v5...v6) - ---- -updated-dependencies: -- dependency-name: actions/checkout - dependency-version: '6' - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/cibuild.yml | 2 +- - .github/workflows/codeql.yml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -index 04773117..44f05cc4 100644 ---- a/.github/workflows/cibuild.yml -+++ b/.github/workflows/cibuild.yml -@@ -392,7 +392,7 @@ jobs: - - steps: - - name: Checkout code -- uses: actions/checkout@v5 -+ uses: actions/checkout@v6 - - - name: Export number of CPUs - run: | -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 31d0c4bc..13986994 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -49,7 +49,7 @@ jobs: - - steps: - - name: Checkout repository -- uses: actions/checkout@v5 -+ uses: actions/checkout@v6 - with: - submodules: recursive - --- -2.34.1 - - -From 34cffad63c44b811781c78472a965fbe0f06451e Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sat, 13 Dec 2025 10:03:40 +0000 -Subject: [PATCH 167/173] Bump actions/upload-artifact from 5 to 6 - -Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. -- [Release notes](https://github.com/actions/upload-artifact/releases) -- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6) - ---- -updated-dependencies: -- dependency-name: actions/upload-artifact - dependency-version: '6' - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/cifuzz.yml | 2 +- - .github/workflows/codeql.yml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml -index 7644d847..ab1416e0 100644 ---- a/.github/workflows/cifuzz.yml -+++ b/.github/workflows/cifuzz.yml -@@ -18,7 +18,7 @@ jobs: - fuzz-seconds: 600 - dry-run: false - - name: Upload Crash -- uses: actions/upload-artifact@v5 -+ uses: actions/upload-artifact@v6 - if: failure() && steps.build.outcome == 'success' - with: - name: artifacts -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index 31d0c4bc..6cb5602b 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -118,7 +118,7 @@ jobs: - - - name: Upload CodeQL results as an artifact - if: success() || failure() -- uses: actions/upload-artifact@v5 -+ uses: actions/upload-artifact@v6 - with: - name: codeql-results - path: ${{ steps.step1.outputs.sarif-output }} --- -2.34.1 - - -From 1bd545f4eb3a6cf6e4c872ee576ba5a32cfce48e Mon Sep 17 00:00:00 2001 -From: Dominik -Date: Sat, 13 Dec 2025 13:37:07 +0100 -Subject: [PATCH 168/173] Implement changes for MbedTLS 4.0.0 - -Signed-off-by: Dominik ---- - src/mod_mbedtls.inl | 20 +++++++++++++++++--- - 1 file changed, 17 insertions(+), 3 deletions(-) - -diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl -index 8256b3a4..6883d48a 100644 ---- a/src/mod_mbedtls.inl -+++ b/src/mod_mbedtls.inl -@@ -1,8 +1,10 @@ - #if defined(USE_MBEDTLS) // USE_MBEDTLS used with NO_SSL ++ ck_assert(client_ri != NULL); ++ ck_assert_int_eq(client_ri->status_code, 200); ++ mg_close_connection(client_conn); --#include "mbedtls/ctr_drbg.h" - #include "mbedtls/debug.h" -+#if MBEDTLS_VERSION_NUMBER < 0x04000000 - #include "mbedtls/entropy.h" -+#include "mbedtls/ctr_drbg.h" -+#endif - #include "mbedtls/error.h" + /* Now test form_store */ - #if MBEDTLS_VERSION_NUMBER >= 0x03000000 -@@ -26,8 +28,10 @@ typedef mbedtls_ssl_context SSL; - typedef struct { - mbedtls_ssl_config conf; /* SSL configuration */ - mbedtls_x509_crt cert; /* Certificate */ -+#if MBEDTLS_VERSION_NUMBER < 0x04000000 - mbedtls_ctr_drbg_context ctr; /* Counter random generator state */ - mbedtls_entropy_context entropy; /* Entropy context */ -+#endif - mbedtls_pk_context pkey; /* Private key */ - } SSL_CTX; +diff --git a/unittest/timertest.c b/unittest/timertest.c +index 270dbec7..8c11b07e 100644 +--- a/unittest/timertest.c ++++ b/unittest/timertest.c +@@ -51,22 +51,22 @@ static int action_dec_ret; + static int + action_dec(void *arg) + { +- int *p = (int *)arg; +- (*p)--; ++ ptrdiff_t *p = (ptrdiff_t *)arg; ++ ptrdiff_t v = mg_atomic_dec(p); -@@ -65,7 +69,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) +- if (*p < -1) { ++ if (v < -1) { + ck_abort_msg("Periodic timer called too often"); + /* return 0 here would be unreachable code */ } - DEBUG_TRACE("%s", "Initializing MbedTLS SSL"); -+#if MBEDTLS_VERSION_NUMBER < 0x04000000 - mbedtls_entropy_init(&ctx->entropy); -+#endif - - conf = &ctx->conf; - mbedtls_ssl_config_init(conf); -@@ -88,7 +94,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) +- return (*p >= -3) ? action_dec_ret : 0; ++ return (v >= -3) ? action_dec_ret : 0; + } - /* Initialize TLS key and cert */ - mbedtls_pk_init(&ctx->pkey); -+#if MBEDTLS_VERSION_NUMBER < 0x04000000 - mbedtls_ctr_drbg_init(&ctx->ctr); -+#endif - mbedtls_x509_crt_init(&ctx->cert); - #ifdef MBEDTLS_PSA_CRYPTO_C -@@ -104,6 +112,7 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) - } - #endif + static void + action_cancel(void *arg) + { +- int *p = (int *)arg; ++ ptrdiff_t *p = (ptrdiff_t *)arg; -+#if MBEDTLS_VERSION_NUMBER < 0x04000000 - rc = mbedtls_ctr_drbg_seed(&ctx->ctr, - mbedtls_entropy_func, - &ctx->entropy, -@@ -113,8 +122,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) - DEBUG_TRACE("TLS random seed failed (%i)", rc); - return -1; + /* test convention: store cancel counter after timer counter */ + p += TIMERS_IN_TEST; +@@ -76,29 +76,29 @@ action_cancel(void *arg) + /* return 0 here would be unreachable code */ } -+#endif --#if MBEDTLS_VERSION_NUMBER >= 0x03000000 -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000 - // mbedtls_pk_parse_keyfile() has changed in mbedTLS 3.0. You now need - // to pass a properly seeded, cryptographically secure RNG when calling - // these functions. It is used for blinding, a countermeasure against -@@ -145,7 +155,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) - return -1; - } +- (*p)++; ++ mg_atomic_inc(p); + } -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000 - mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &ctx->ctr); -+#endif - /* Set auth mode if peer cert should be verified */ - mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); -@@ -172,10 +184,12 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist) - void - mbed_sslctx_uninit(SSL_CTX *ctx) + static int + action_dec_to_0(void *arg) { -+#if MBEDTLS_VERSION_NUMBER < 0x04000000 - mbedtls_ctr_drbg_free(&ctx->ctr); -+ mbedtls_entropy_free(&ctx->entropy); -+#endif - mbedtls_pk_free(&ctx->pkey); - mbedtls_x509_crt_free(&ctx->cert); -- mbedtls_entropy_free(&ctx->entropy); - mbedtls_ssl_config_free(&ctx->conf); - } +- int *p = (int *)arg; +- (*p)--; ++ ptrdiff_t *p = (ptrdiff_t *)arg; ++ ptrdiff_t v = mg_atomic_dec(p); --- -2.34.1 - - -From 2013003792054aedb4791a8d19c6e96e4d9f6cb9 Mon Sep 17 00:00:00 2001 -From: Andreas Ziegler <15275159+aeolio@users.noreply.github.com> -Date: Thu, 26 Feb 2026 07:32:34 +0100 -Subject: [PATCH 169/173] Fix missing lua_websock_data declaration w/o - USE_WEBSOCKET -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -If civetweb.c is compiled with USE_WEBSOCKET undefined, a compiler warning -is raised: - -In file included from [...]/pihole-ftl-6.5/src/webserver/civetweb/civetweb.c:13436: -[...]/pihole-ftl-6.5/src/webserver/civetweb/mod_lua.inl:2852:32: warning: ‘struct lua_websock_data’ declared inside parameter list will not be visible outside of this definition or declaration - 2852 | struct lua_websock_data *ws_conn_list, - | ^~~~~~~~~~~~~~~~ - -The reason for this warning is a missing declaration of struct -lua_websock_data, which is omitted due to USE_WEBSOCKET being undefined. - -For safety reasons, the code related to this parameter in function -prepare_lua_environment() should also be omitted, if USE_WEBSOCKET is -undefined. - -Fix missing declaration of lua_websock_data and disable code related to -unused function parameter in prepare_lua_environment(). - -Signed-off-by: Andreas Ziegler <15275159+aeolio@users.noreply.github.com> ---- - src/mod_lua.inl | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/src/mod_lua.inl b/src/mod_lua.inl -index e8279099..e6596b1c 100644 ---- a/src/mod_lua.inl -+++ b/src/mod_lua.inl -@@ -1977,6 +1977,9 @@ struct lua_websock_data { - struct mg_connection *conn[MAX_WORKER_THREADS]; - pthread_mutex_t ws_mutex; - }; -+#else -+/* used in parameter list of prepare_lua_environment() */ -+struct lua_websock_data; - #endif +- if (*p <= -1) { ++ if (v <= -1) { + ck_abort_msg("Periodic timer called too often"); + /* return 0 here would be unreachable code */ + } +- return (*p > 0); ++ return (v > 0); + } -@@ -2886,11 +2889,13 @@ prepare_lua_environment(struct mg_context *ctx, - #endif - /* Store context in the registry */ -+#if defined(USE_WEBSOCKET) - if (ctx != NULL) { - lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); - lua_pushlightuserdata(L, (void *)ctx); - lua_settable(L, LUA_REGISTRYINDEX); - } -+#endif /* USE_WEBSOCKET */ - if (ws_conn_list != NULL) { - lua_pushlightuserdata(L, (void *)&lua_regkey_connlist); - lua_pushlightuserdata(L, (void *)ws_conn_list); --- -2.34.1 - - -From 3c0fb6ad582224b91959caf79a25dd98389c084d Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Mon, 30 Mar 2026 23:40:14 +0200 -Subject: [PATCH 170/173] Quote path handling for service installation - ---- - src/main.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/main.c b/src/main.c -index 9d3a0ea7..be859feb 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -2796,8 +2796,9 @@ manage_service(int action) - show_error(); - } else if (action == ID_INSTALL_SERVICE) { - path[sizeof(path) - 1] = 0; -- GetModuleFileName(NULL, path, sizeof(path) - 1); -- strncat(path, " ", sizeof(path) - 1 - strlen(path)); -+ path[0]='"'; -+ GetModuleFileName(NULL, path+1, sizeof(path) - 3); -+ strncat(path, "\" ", sizeof(path) - 2 - strlen(path)); - strncat(path, service_magic_argument, sizeof(path) - 1 - strlen(path)); - hService = CreateService(hSCM, - service_name, --- -2.34.1 - - -From d5f655053274997734d9785cb5db03af020fa750 Mon Sep 17 00:00:00 2001 -From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> -Date: Sat, 4 Apr 2026 10:02:33 +0000 -Subject: [PATCH 171/173] Bump actions/upload-artifact from 6 to 7 - -Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7. -- [Release notes](https://github.com/actions/upload-artifact/releases) -- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7) - ---- -updated-dependencies: -- dependency-name: actions/upload-artifact - dependency-version: '7' - dependency-type: direct:production - update-type: version-update:semver-major -... - -Signed-off-by: dependabot[bot] ---- - .github/workflows/cifuzz.yml | 2 +- - .github/workflows/codeql.yml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml -index ab1416e0..8691823e 100644 ---- a/.github/workflows/cifuzz.yml -+++ b/.github/workflows/cifuzz.yml -@@ -18,7 +18,7 @@ jobs: - fuzz-seconds: 600 - dry-run: false - - name: Upload Crash -- uses: actions/upload-artifact@v6 -+ uses: actions/upload-artifact@v7 - if: failure() && steps.build.outcome == 'success' - with: - name: artifacts -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -index cd794a66..280d15ea 100644 ---- a/.github/workflows/codeql.yml -+++ b/.github/workflows/codeql.yml -@@ -118,7 +118,7 @@ jobs: + START_TEST(test_timer_cyclic) + { ++ static ptrdiff_t c[TIMERS_IN_TEST * 2]; + struct mg_context ctx; +- int c[TIMERS_IN_TEST * 2]; + memset(&ctx, 0, sizeof(ctx)); + memset(c, 0, sizeof(c)); - - name: Upload CodeQL results as an artifact - if: success() || failure() -- uses: actions/upload-artifact@v6 -+ uses: actions/upload-artifact@v7 - with: - name: codeql-results - path: ${{ steps.step1.outputs.sarif-output }} --- -2.34.1 - - -From d110e8731c6aa0fcd04504deb449bdb773cfb9da Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 19 Apr 2026 16:39:09 +0200 -Subject: [PATCH 172/173] Fix memory allocation condition for websocket payload - ---- - src/civetweb.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index a399ce45..28b040a7 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -13559,9 +13559,10 @@ read_websocket(struct mg_connection *conn, - if ((header_len > 0) && (body_len >= header_len)) { - /* Allocate space to hold websocket payload */ - unsigned char *data = mem; -+ size_t required_len = (size_t)data_len + 4; +@@ -158,7 +158,7 @@ END_TEST + START_TEST(test_timer_oneshot_by_callback_retval) + { + struct mg_context ctx; +- int c[TIMERS_IN_TEST * 2]; ++ ptrdiff_t c[TIMERS_IN_TEST * 2]; + memset(&ctx, 0, sizeof(ctx)); + memset(c, 0, sizeof(c)); -- if ((size_t)data_len > (size_t)sizeof(mem)) { -- data = (unsigned char *)mg_malloc_ctx((size_t)data_len, -+ if (required_len > sizeof(mem)) { -+ data = (unsigned char *)mg_malloc_ctx(required_len, - conn->phys_ctx); - if (data == NULL) { - /* Allocation failed, exit the loop and then close the --- -2.34.1 - - -From 588860e30721bf5453b0440c390865a8e85dcae5 Mon Sep 17 00:00:00 2001 -From: bel2125 -Date: Sun, 19 Apr 2026 17:04:45 +0200 -Subject: [PATCH 173/173] Refactor request handling: don't allow chunked - encoding and content length - ---- - src/civetweb.c | 22 +++++++++++----------- - 1 file changed, 11 insertions(+), 11 deletions(-) - -diff --git a/src/civetweb.c b/src/civetweb.c -index 28b040a7..f6a60e21 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -19158,7 +19158,7 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) - static int - get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) +@@ -212,7 +212,7 @@ END_TEST + START_TEST(test_timer_oneshot_by_timer_add) { -- const char *cl; -+ const char *h_zip, *h_chunk, *h_len; + struct mg_context ctx; +- int c[TIMERS_IN_TEST * 2]; ++ ptrdiff_t c[TIMERS_IN_TEST * 2]; + memset(&ctx, 0, sizeof(ctx)); + memset(c, 0, sizeof(c)); - conn->connection_type = - CONNECTION_TYPE_REQUEST; /* request (valid of not) */ -@@ -19193,20 +19193,23 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) - } +@@ -266,7 +266,7 @@ END_TEST + START_TEST(test_timer_mixed) + { + struct mg_context ctx; +- int c[TIMERS_IN_TEST]; ++ ptrdiff_t c[TIMERS_IN_TEST]; + memset(&ctx, 0, sizeof(ctx)); + memset(c, 0, sizeof(c)); - #if USE_ZLIB -- if (((cl = get_header(conn->request_info.http_headers, -+ if (((h_zip = get_header(conn->request_info.http_headers, - conn->request_info.num_headers, - "Accept-Encoding")) - != NULL) -- && strstr(cl, "gzip")) { -+ && strstr(h_zip, "gzip")) { - conn->accept_gzip = 1; - } - #endif -- if (((cl = get_header(conn->request_info.http_headers, -+ h_chunk = get_header(conn->request_info.http_headers, - conn->request_info.num_headers, -- "Transfer-Encoding")) -- != NULL) -+ "Transfer-Encoding"); -+ h_len = get_header(conn->request_info.http_headers, -+ conn->request_info.num_headers, -+ "Content-Length"); -+ if (h_chunk != NULL) - && mg_strcasecmp(cl, "identity")) { -- if (mg_strcasecmp(cl, "chunked")) { -+ if ((0!=mg_strcasecmp(cl, "chunked")) || (h_len!=NULL)) { - mg_snprintf(conn, - NULL, /* No truncation check for ebuf */ - ebuf, -@@ -19218,10 +19221,7 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) - } - conn->is_chunked = 1; - conn->content_len = 0; /* not yet read */ -- } else if ((cl = get_header(conn->request_info.http_headers, -- conn->request_info.num_headers, -- "Content-Length")) -- != NULL) { -+ } else if (h_len != NULL) { - /* Request has content length set */ - char *endptr = NULL; - conn->content_len = strtoll(cl, &endptr, 10); -- 2.34.1 diff --git a/builtins/civetweb/0001-only-include-engine-when-needed.patch b/builtins/civetweb/0002-only-include-header-if-necessary.patch similarity index 77% rename from builtins/civetweb/0001-only-include-engine-when-needed.patch rename to builtins/civetweb/0002-only-include-header-if-necessary.patch index 80f850596afd8..b2435527e1f4f 100644 --- a/builtins/civetweb/0001-only-include-engine-when-needed.patch +++ b/builtins/civetweb/0002-only-include-header-if-necessary.patch @@ -1,7 +1,7 @@ -From 32dd125e9bdec6844fc8885eb467fb9bf24d44fa Mon Sep 17 00:00:00 2001 +From bbcfe76a87124685c5c9430e859d045fa3f50901 Mon Sep 17 00:00:00 2001 From: ferdymercury -Date: Mon, 27 Apr 2026 15:34:11 +0200 -Subject: [PATCH] only include engine when needed +Date: Mon, 27 Apr 2026 17:07:22 +0200 +Subject: [PATCH 2/2] only include header if necessary --- src/civetweb.c | 2 ++ diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index c6fd130177646..d79799e15a64f 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -10,8 +10,9 @@ set(ROOT_CIVETWEB_VERSION 1.16) set(ROOT_CIVETWEB_HASH "f0e471c1bf4e7804a6cfb41ea9d13e7d623b2bcc7bc1e2a4dd54951a24d60285") set(ROOT_CIVETWEB_PREFIX ${CMAKE_BINARY_DIR}/builtins/CIVETWEB-prefix) set(ROOT_CIVETWEB_LIBRARY ${ROOT_CIVETWEB_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}civetweb${CMAKE_STATIC_LIBRARY_SUFFIX}) -set(ROOT_CIVETWEB_PATCH_FILE_1 ${CMAKE_CURRENT_SOURCE_DIR}/v1_17.patch) # From master branch (https://github.com/civetweb/civetweb/commit/588860e30721bf5453b0440c390865a8e85dcae5): git format-patch v1.16 --stdout > v1_17.patch -set(ROOT_CIVETWEB_PATCH_FILE_2 ${CMAKE_CURRENT_SOURCE_DIR}/0001-only-include-engine-when-needed.patch) # From https://github.com/civetweb/civetweb/pull/1389 git format-patch -1 +set(ROOT_CIVETWEB_PATCH_FILE_1 ${CMAKE_CURRENT_SOURCE_DIR}/0001-v117patches.patch) # From master branch (squash all until https://github.com/civetweb/civetweb/commit/588860e30721bf5453b0440c390865a8e85dcae5), commit +set(ROOT_CIVETWEB_PATCH_FILE_2 ${CMAKE_CURRENT_SOURCE_DIR}/0002-only-include-header-if-necessary.patch) # From https://github.com/civetweb/civetweb/pull/1389, commit +# Then call git format-patch -2 if (NOT DEFINED GIT_EXECUTABLE) set(GIT_EXECUTABLE "git") From 7c2e6c29a83379442a042046774df150bf594dfe Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 17:32:13 +0200 Subject: [PATCH 47/54] use git not patches --- builtins/civetweb/0001-v117patches.patch | 179211 --------------- ...002-only-include-header-if-necessary.patch | 26 - builtins/civetweb/CMakeLists.txt | 14 +- 3 files changed, 6 insertions(+), 179245 deletions(-) delete mode 100644 builtins/civetweb/0001-v117patches.patch delete mode 100644 builtins/civetweb/0002-only-include-header-if-necessary.patch diff --git a/builtins/civetweb/0001-v117patches.patch b/builtins/civetweb/0001-v117patches.patch deleted file mode 100644 index 2a45bd2368bf4..0000000000000 --- a/builtins/civetweb/0001-v117patches.patch +++ /dev/null @@ -1,179211 +0,0 @@ -From ae04f545031956aafac23d60f30d924d97f49efe Mon Sep 17 00:00:00 2001 -From: ferdymercury -Date: Mon, 27 Apr 2026 17:05:02 +0200 -Subject: [PATCH 1/2] v117patches - ---- - .devcontainer/Dockerfile | 13 + - .devcontainer/devcontainer.json | 16 + - .github/dependabot.yml | 10 + - .github/workflows/cibuild.yml | 539 + - .github/workflows/cifuzz.yml | 2 +- - .github/workflows/codeql-analysis.yml | 66 - - .github/workflows/codeql.yml | 130 + - .github/workflows/fail_on_error.py | 2 + - .gitignore | 1 + - .travis.yml | 521 +- - CMakeLists.txt | 49 +- - CREDITS.md | 2 + - Dockerfile | 53 + - LICENSE.md | 2 +- - Makefile | 16 +- - Makefile.osx | 2 +- - README.md | 23 +- - RELEASE_NOTES.md | 13 + - SECURITY.md | 10 +- - .../civetweb_lua/civetweb_lua.vcxproj | 7 +- - appveyor.yml | 75 +- - docs/Building.md | 5 +- - docs/Docker.md | 96 + - docs/Embedding.md | 11 +- - docs/OpenSSL.md | 5 +- - docs/README.md | 4 +- - docs/UserManual.md | 46 +- - docs/api/mg_form_data_handler.md | 14 +- - docs/api/mg_get_cookie.md | 4 +- - docs/api/mg_request_info.md | 2 +- - docs/api/mg_server_port.md | 4 +- - docs/gnutls.md | 19 + - examples/https/civetweb.conf | 3 + - examples/linux_ws_server_cpp/CMakeLists.txt | 4 +- - format.bat | 1 + - fuzztest/fuzzmain.c | 62 +- - include/civetweb.h | 19 +- - resources/Makefile.in-lua | 17 +- - resources/civetweb.conf | 1 + - src/CMakeLists.txt | 33 +- - src/CivetServer.cpp | 8 +- - src/civetweb.c | 1280 +- - src/handle_form.inl | 160 +- - src/http2.inl | 4 +- - src/main.c | 14 +- - src/match.inl | 6 +- - src/mod_gnutls.inl | 240 + - src/mod_lua.inl | 119 +- - src/mod_lua_shared.inl | 2 +- - src/mod_mbedtls.inl | 121 +- - src/third_party/civetweb_lua.h | 16 +- - src/third_party/lfs.c | 2 +- - src/third_party/lua-5.2.4/src/ldebug.c | 7 +- - src/third_party/lua-5.5.0-beta/Makefile | 106 + - src/third_party/lua-5.5.0-beta/README | 6 + - .../doc/OSIApproved_100X125.png | Bin 0 -> 12127 bytes - .../lua-5.5.0-beta/doc/contents.html | 679 + - src/third_party/lua-5.5.0-beta/doc/index.css | 21 + - src/third_party/lua-5.5.0-beta/doc/logo.gif | Bin 0 -> 9893 bytes - src/third_party/lua-5.5.0-beta/doc/lua.1 | 155 + - src/third_party/lua-5.5.0-beta/doc/lua.css | 162 + - src/third_party/lua-5.5.0-beta/doc/luac.1 | 118 + - src/third_party/lua-5.5.0-beta/doc/manual.css | 21 + - .../lua-5.5.0-beta/doc/manual.html | 12398 +++ - .../lua-5.5.0-beta/doc/readme.html | 333 + - src/third_party/lua-5.5.0-beta/src/Makefile | 229 + - src/third_party/lua-5.5.0-beta/src/lapi.c | 1467 + - src/third_party/lua-5.5.0-beta/src/lapi.h | 65 + - src/third_party/lua-5.5.0-beta/src/lauxlib.c | 1198 + - src/third_party/lua-5.5.0-beta/src/lauxlib.h | 268 + - src/third_party/lua-5.5.0-beta/src/lbaselib.c | 558 + - src/third_party/lua-5.5.0-beta/src/lcode.c | 1911 + - src/third_party/lua-5.5.0-beta/src/lcode.h | 103 + - src/third_party/lua-5.5.0-beta/src/lcorolib.c | 223 + - src/third_party/lua-5.5.0-beta/src/lctype.c | 64 + - src/third_party/lua-5.5.0-beta/src/lctype.h | 101 + - src/third_party/lua-5.5.0-beta/src/ldblib.c | 477 + - src/third_party/lua-5.5.0-beta/src/ldebug.c | 971 + - src/third_party/lua-5.5.0-beta/src/ldebug.h | 64 + - src/third_party/lua-5.5.0-beta/src/ldo.c | 1131 + - src/third_party/lua-5.5.0-beta/src/ldo.h | 98 + - src/third_party/lua-5.5.0-beta/src/ldump.c | 303 + - src/third_party/lua-5.5.0-beta/src/lfunc.c | 315 + - src/third_party/lua-5.5.0-beta/src/lfunc.h | 65 + - src/third_party/lua-5.5.0-beta/src/lgc.c | 1803 + - src/third_party/lua-5.5.0-beta/src/lgc.h | 268 + - src/third_party/lua-5.5.0-beta/src/linit.c | 63 + - src/third_party/lua-5.5.0-beta/src/liolib.c | 841 + - src/third_party/lua-5.5.0-beta/src/ljumptab.h | 112 + - src/third_party/lua-5.5.0-beta/src/llex.c | 604 + - src/third_party/lua-5.5.0-beta/src/llex.h | 93 + - src/third_party/lua-5.5.0-beta/src/llimits.h | 315 + - src/third_party/lua-5.5.0-beta/src/lmathlib.c | 752 + - src/third_party/lua-5.5.0-beta/src/lmem.c | 215 + - src/third_party/lua-5.5.0-beta/src/lmem.h | 96 + - src/third_party/lua-5.5.0-beta/src/loadlib.c | 743 + - src/third_party/lua-5.5.0-beta/src/lobject.c | 717 + - src/third_party/lua-5.5.0-beta/src/lobject.h | 854 + - src/third_party/lua-5.5.0-beta/src/lopcodes.c | 138 + - src/third_party/lua-5.5.0-beta/src/lopcodes.h | 429 + - src/third_party/lua-5.5.0-beta/src/lopnames.h | 103 + - src/third_party/lua-5.5.0-beta/src/loslib.c | 432 + - src/third_party/lua-5.5.0-beta/src/lparser.c | 2091 + - src/third_party/lua-5.5.0-beta/src/lparser.h | 191 + - src/third_party/lua-5.5.0-beta/src/lprefix.h | 45 + - src/third_party/lua-5.5.0-beta/src/lstate.c | 420 + - src/third_party/lua-5.5.0-beta/src/lstate.h | 455 + - src/third_party/lua-5.5.0-beta/src/lstring.c | 359 + - src/third_party/lua-5.5.0-beta/src/lstring.h | 73 + - src/third_party/lua-5.5.0-beta/src/lstrlib.c | 1883 + - src/third_party/lua-5.5.0-beta/src/ltable.c | 1325 + - src/third_party/lua-5.5.0-beta/src/ltable.h | 184 + - src/third_party/lua-5.5.0-beta/src/ltablib.c | 426 + - src/third_party/lua-5.5.0-beta/src/ltm.c | 276 + - src/third_party/lua-5.5.0-beta/src/ltm.h | 104 + - src/third_party/lua-5.5.0-beta/src/lua.c | 761 + - src/third_party/lua-5.5.0-beta/src/lua.h | 554 + - src/third_party/lua-5.5.0-beta/src/lua.hpp | 10 + - src/third_party/lua-5.5.0-beta/src/luac.c | 724 + - src/third_party/lua-5.5.0-beta/src/luaconf.h | 827 + - src/third_party/lua-5.5.0-beta/src/lualib.h | 65 + - src/third_party/lua-5.5.0-beta/src/lundump.c | 423 + - src/third_party/lua-5.5.0-beta/src/lundump.h | 40 + - src/third_party/lua-5.5.0-beta/src/lutf8lib.c | 291 + - src/third_party/lua-5.5.0-beta/src/lvm.c | 1926 + - src/third_party/lua-5.5.0-beta/src/lvm.h | 136 + - src/third_party/lua-5.5.0-beta/src/lzio.c | 89 + - src/third_party/lua-5.5.0-beta/src/lzio.h | 67 + - src/third_party/lua_struct.c | 10 +- - src/third_party/sqlite3.c | 77464 +++++++++++----- - src/third_party/sqlite3.h | 2397 +- - src/timer.inl | 6 +- - test/donate.html | 32 - - test/page3.ssjs | 3 + - test/page4.lp | 4 +- - test/page4kepler.lp | 4 +- - unittest/CMakeLists.txt | 4 + - unittest/private.c | 5 + - unittest/public_server.c | 859 +- - unittest/timertest.c | 28 +- - 140 files changed, 106511 insertions(+), 24119 deletions(-) - create mode 100644 .devcontainer/Dockerfile - create mode 100644 .devcontainer/devcontainer.json - create mode 100644 .github/dependabot.yml - create mode 100644 .github/workflows/cibuild.yml - delete mode 100644 .github/workflows/codeql-analysis.yml - create mode 100644 .github/workflows/codeql.yml - create mode 100755 .github/workflows/fail_on_error.py - create mode 100644 Dockerfile - create mode 100644 docs/Docker.md - create mode 100644 docs/gnutls.md - create mode 100644 src/mod_gnutls.inl - create mode 100644 src/third_party/lua-5.5.0-beta/Makefile - create mode 100644 src/third_party/lua-5.5.0-beta/README - create mode 100644 src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png - create mode 100644 src/third_party/lua-5.5.0-beta/doc/contents.html - create mode 100644 src/third_party/lua-5.5.0-beta/doc/index.css - create mode 100644 src/third_party/lua-5.5.0-beta/doc/logo.gif - create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.1 - create mode 100644 src/third_party/lua-5.5.0-beta/doc/lua.css - create mode 100644 src/third_party/lua-5.5.0-beta/doc/luac.1 - create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.css - create mode 100644 src/third_party/lua-5.5.0-beta/doc/manual.html - create mode 100644 src/third_party/lua-5.5.0-beta/doc/readme.html - create mode 100644 src/third_party/lua-5.5.0-beta/src/Makefile - create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lapi.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lauxlib.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lbaselib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lcode.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lcorolib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lctype.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldblib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldebug.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldo.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ldump.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lfunc.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lgc.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/linit.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/liolib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ljumptab.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/llex.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/llimits.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lmathlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lmem.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/loadlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lobject.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lopcodes.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lopnames.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/loslib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lparser.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lprefix.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstate.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstring.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lstrlib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltable.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltablib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/ltm.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lua.hpp - create mode 100644 src/third_party/lua-5.5.0-beta/src/luac.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/luaconf.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lualib.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lundump.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lutf8lib.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lvm.h - create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.c - create mode 100644 src/third_party/lua-5.5.0-beta/src/lzio.h - delete mode 100644 test/donate.html - -diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile -new file mode 100644 -index 00000000..81fdf3e9 ---- /dev/null -+++ b/.devcontainer/Dockerfile -@@ -0,0 +1,13 @@ -+# syntax=docker/dockerfile:1 -+ARG ubuntu_version=24.04 -+ -+FROM ubuntu:${ubuntu_version} -+RUN apt-get update && \ -+ apt-get install --no-install-recommends -y \ -+ build-essential \ -+ ca-certificates \ -+ cmake \ -+ clang \ -+ git \ -+ nano\ -+ openssh-server -\ No newline at end of file -diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json -new file mode 100644 -index 00000000..b3030fdb ---- /dev/null -+++ b/.devcontainer/devcontainer.json -@@ -0,0 +1,16 @@ -+{ -+ "name": "Civetweb", -+ "dockerFile": "Dockerfile", -+ "customizations": { -+ "vscode": { -+ "extensions": [ -+ "ms-vscode.cpptools", -+ "eamodio.gitlens" -+ ] -+ } -+ }, -+ "mounts": [ -+ "type=bind,source=/home/${localEnv:USER}/.ssh,target=/root/.ssh,readonly" -+ ] -+ -+ } -\ No newline at end of file -diff --git a/.github/dependabot.yml b/.github/dependabot.yml -new file mode 100644 -index 00000000..c20f0118 ---- /dev/null -+++ b/.github/dependabot.yml -@@ -0,0 +1,10 @@ -+version: 2 -+updates: -+- package-ecosystem: github-actions -+ directory: "/" -+ schedule: -+ interval: weekly -+ day: saturday -+ time: "10:00" -+ open-pull-requests-limit: 10 -+ target-branch: master -diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml -new file mode 100644 -index 00000000..44f05cc4 ---- /dev/null -+++ b/.github/workflows/cibuild.yml -@@ -0,0 +1,539 @@ -+name: CI build -+ -+on: -+ push: -+ pull_request: -+ release: -+ types: [published] -+ workflow_dispatch: -+jobs: -+ build-and-test: -+ runs-on: ${{ matrix.os }} -+ name: ${{ matrix.env.NAME }} -+ strategy: -+ fail-fast: true -+ matrix: -+ include: -+ - os: ubuntu-latest -+ compiler: clang -+ env: -+ NAME: Clang-Linux-Minimal-Debug -+ BUILD_TYPE: Debug -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: NO -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: YES -+ ENABLE_SSL: NO -+ NO_CGI: YES -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_SERVER_STATS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: clang -+ env: -+ NAME: Clang-Linux-Default-Release -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_SERVER_STATS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ NAME: GCC-Linux-Complete-NoLua-Release -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: YES -+ ENABLE_WEBSOCKETS: YES -+ ENABLE_SERVER_STATS: YES -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: YES -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: clang -+ env: -+ NAME: CLANG-AnyVersion-Linux-Coverage -+ BUILD_TYPE: Coverage -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: YES -+ ENABLE_WEBSOCKETS: YES -+ ENABLE_SERVER_STATS: YES -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: clang -+ env: -+ NAME: Clang-Linux-Default-Shared -+ BUILD_TYPE: Debug -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: YES -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_SERVER_STATS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ NAME: GCCLinuxDefault_RelWithDebInfo -+ BUILD_TYPE: RelWithDebInfo -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ NAME: GCCLinuxDefault_MinSizeRel -+ BUILD_TYPE: MinSizeRel -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ NAME: GCCLinuxDefault_None -+ BUILD_TYPE: None -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ NAME: GCCLinuxDefault_xenial -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ NAME: GCCLinuxDefault -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: ubuntu-latest -+ compiler: gcc -+ env: -+ NAME: GCCLinuxDefault_OpenSSL_3_0 -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: NO -+ OpenSSL_3_0: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: NO -+ ENABLE_WEBSOCKETS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ # Disable Lua build, until someone knows how to fix the CMake files -+ # see https://github.com/civetweb/civetweb/issues/543 -+ # - os: ubuntu-lastest -+ # compiler: clang -+ # env: -+ # NAME: Clang-Linux-Complete-WithLua-Debug -+ # BUILD_TYPE: Debug -+ # ENABLE_SSL_DYNAMIC_LOADING: YES -+ # OPENSSL_1_0: NO -+ # OPENSSL_1_1: YES -+ # ENABLE_CXX: NO -+ # C_STANDARD: auto -+ # CXX_STANDARD: auto -+ # BUILD_SHARED: NO -+ # NO_FILES: NO -+ # ENABLE_SSL: YES -+ # NO_CGI: NO -+ # ENABLE_IPV6: YES -+ # ENABLE_WEBSOCKETS: YES -+ # ENABLE_SERVER_STATS: YES -+ # ENABLE_LUA: YES -+ # ENABLE_LUA_SHARED: YES -+ # ENABLE_DUKTAPE: NO -+ # NO_CACHING: YES -+ # ALLOW_WARNINGS: YES -+ # RUN_UNITTEST: 1 -+ -+ - os: macos-latest -+ compiler: clang -+ env: -+ NAME: Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: NO -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: YES -+ ENABLE_WEBSOCKETS: YES -+ ENABLE_SERVER_STATS: YES -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: YES -+ ALLOW_WARNINGS: YES -+ RUN_UNITTEST: 1 -+ -+ - os: macos-latest -+ compiler: clang -+ env: -+ NAME: OSX-Package_OpenSSL_1_1 -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: YES -+ ENABLE_WEBSOCKETS: YES -+ ENABLE_SERVER_STATS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ MACOSX_PACKAGE: 1 -+ RUN_UNITTEST: 1 -+ -+ - os: macos-latest -+ compiler: clang -+ env: -+ NAME: OSX-Package_OpenSSL_3_0 -+ BUILD_TYPE: Release -+ ENABLE_SSL_DYNAMIC_LOADING: YES -+ OPENSSL_1_0: NO -+ OPENSSL_1_1: NO -+ OPENSSL_3_0: YES -+ ENABLE_CXX: NO -+ ENABLE_LUA_SHARED: NO -+ C_STANDARD: auto -+ CXX_STANDARD: auto -+ BUILD_SHARED: NO -+ NO_FILES: NO -+ ENABLE_SSL: YES -+ NO_CGI: NO -+ ENABLE_IPV6: YES -+ ENABLE_WEBSOCKETS: YES -+ ENABLE_SERVER_STATS: NO -+ ENABLE_LUA: NO -+ ENABLE_DUKTAPE: NO -+ NO_CACHING: NO -+ ALLOW_WARNINGS: YES -+ MACOSX_PACKAGE: 1 -+ RUN_UNITTEST: 1 -+ -+ -+ steps: -+ - name: Checkout code -+ uses: actions/checkout@v6 -+ -+ - name: Export number of CPUs -+ run: | -+ if [ "$RUNNER_OS" == "Linux" ]; then -+ echo "cores=$(nproc)" >> $GITHUB_ENV -+ fi -+ if [ "$RUNNER_OS" == "macOS" ]; then -+ echo "cores=$(sysctl -n hw.logicalcpu)" >> $GITHUB_ENV -+ fi -+ -+ - name: Install clang on Linux -+ if: matrix.compiler == 'clang' && startsWith(matrix.os,'ubuntu') -+ run: | -+ sudo apt-get install --no-install-recommends -y clang -+ sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 -+ sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 -+ -+ - name: Set up OpenSSL 1.1 on modern MacOS -+ # OpenSSL 1.1 is installed by default, so we just need to set the paths -+ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_1_1 == 'YES' -+ run: | -+ OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) -+ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -+ CFLAGS=-I${OPENSSL_ROOT_DIR}/include -+ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -+ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -+ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib -+ -+ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV -+ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV -+ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH -+ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV -+ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV -+ -+ - name: Install OpenSSL 3.0 on modern MacOS -+ # OpenSSL 1.1 is installed by default, so we need to install 3.0 manually -+ if: startsWith(matrix.os,'macos') && matrix.env.OPENSSL_3_0 == 'YES' -+ run: | -+ brew install openssl@3.0 -+ -+ OPENSSL_ROOT_DIR=$(brew --prefix openssl@3.0) -+ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -+ CFLAGS=-I${OPENSSL_ROOT_DIR}/include -+ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -+ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -+ DYLD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib -+ -+ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV -+ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV -+ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH -+ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV -+ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}" >> $GITHUB_ENV -+ -+ - name: Install OpenSSL 1.1 on modern Linux -+ # Needed for recent versions of Linux as they ship with OpenSSL 3.0 by default -+ if: startsWith(matrix.os,'ubuntu') && matrix.env.OPENSSL_1_1 == 'YES' -+ run: | -+ curl -O -L https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz -+ tar -xzf openssl-1.1.1w.tar.gz -+ cd openssl-1.1.1w -+ ./config --prefix=/usr/local/ssl1.1 --openssldir=/usr/local/ssl1.1 shared -+ make depend -+ make -j ${{ env.cores }} -+ sudo make install_sw -j ${{ env.cores }} -+ sudo ldconfig -+ -+ OPENSSL_ROOT_DIR=/usr/local/ssl1.1 -+ LDFLAGS=-L${OPENSSL_ROOT_DIR}/lib -+ CFLAGS=-I${OPENSSL_ROOT_DIR}/include -+ ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DMAKE_C_FLAGS=${CFLAGS}" -+ PKG_CONFIG_PATH=${OPENSSL_ROOT_DIR}/lib/pkgconfig -+ -+ echo "LDFLAGS=${LDFLAGS}" >> $GITHUB_ENV -+ echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV -+ echo "${OPENSSL_ROOT_DIR}/bin" >> $GITHUB_PATH -+ echo "ADDITIONAL_CMAKE_ARGS=${ADDITIONAL_CMAKE_ARGS}" >> $GITHUB_ENV -+ echo "PKG_CONFIG_PATH=${PKG_CONFIG_PATH}" >> $GITHUB_ENV -+ echo "LD_LIBRARY_PATH=${OPENSSL_ROOT_DIR}/lib" >> $GITHUB_ENV -+ -+ - name: Print tool version information -+ run: | -+ openssl version -+ cc --version -+ cmake --version -+ clang --version -+ -+ - name: Run CMake -+ run: | -+ cmake -S . -B output\ -+ -DCMAKE_BUILD_TYPE=${{ matrix.env.BUILD_TYPE }}\ -+ -DBUILD_SHARED_LIBS=${{ matrix.env.BUILD_SHARED }}\ -+ -DCIVETWEB_THIRD_PARTY_DIR=../src/third-party\ -+ -DCIVETWEB_ENABLE_THIRD_PARTY_OUTPUT=YES\ -+ -DCIVETWEB_ENABLE_SSL=${{ matrix.env.ENABLE_SSL }}\ -+ -DCIVETWEB_DISABLE_CGI=${{ matrix.env.NO_CGI }}\ -+ -DCIVETWEB_SERVE_NO_FILES=${{ matrix.env.NO_FILES }}\ -+ -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${{ matrix.env.ENABLE_SSL_DYNAMIC_LOADING }}\ -+ -DCIVETWEB_SSL_OPENSSL_API_1_0=${{ matrix.env.OPENSSL_1_0 }}\ -+ -DCIVETWEB_SSL_OPENSSL_API_1_1=${{ matrix.env.OPENSSL_1_1 }}\ -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=${{ matrix.env.OPENSSL_3_0 }}\ -+ -DCIVETWEB_ENABLE_WEBSOCKETS=${{ matrix.env.ENABLE_WEBSOCKETS }}\ -+ -DCIVETWEB_ENABLE_CXX=${{ matrix.env.ENABLE_CXX }}\ -+ -DCIVETWEB_ENABLE_SERVER_STATS=${{ matrix.env.ENABLE_SERVER_STATS }}\ -+ -DCIVETWEB_ENABLE_LUA=${{ matrix.env.ENABLE_LUA }}\ -+ -DCIVETWEB_ENABLE_LUA_SHARED=${{ matrix.env.ENABLE_LUA_SHARED }}\ -+ -DCIVETWEB_ENABLE_DUKTAPE=${{ matrix.env.ENABLE_DUKTAPE }}\ -+ -DCIVETWEB_DISABLE_CACHING=${{ matrix.env.NO_CACHING }}\ -+ -DCIVETWEB_C_STANDARD=${{ matrix.env.C_STANDARD }}\ -+ -DCIVETWEB_CXX_STANDARD=${{ matrix.env.CXX_STANDARD }}\ -+ -DCIVETWEB_ALLOW_WARNINGS=${{ matrix.env.ALLOW_WARNINGS }}\ -+ -DCIVETWEB_ENABLE_IPV6=${{ matrix.env.ENABLE_IPV6 }}\ -+ ${{ env.ADDITIONAL_CMAKE_ARGS }} -+ -+ - name: Build MacOS Package -+ if: matrix.env.MACOSX_PACKAGE == 1 -+ run: | -+ make -f Makefile.osx package -j ${{ env.cores }} -+ -+ - name: Build executable -+ run: | -+ cmake --build output -- -j ${{ env.cores }} -+ -+ - name: Check executable -+ run: | -+ ./output/src/civetweb -I -+ -+ - name: Run unit tests -+ if: matrix.env.RUN_UNITTEST == 1 -+ run: | -+ # kill processes that are using port 8084, which is used in the unit tests -+ # Currently, this affects linux only, where 'mono' is using this port -+ pid_8084=$(sudo lsof -t -i:8084 || true;) -+ -+ if [[ -n ${pid_8084} ]]; then -+ echo "Killing process using port 8084: ${pid_8084}" -+ sudo kill -9 ${pid_8084} -+ fi -+ -+ # Run unit tests -+ gcc unittest/cgi_test.c -o output/cgi_test.cgi -+ cd output -+ CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test -j ${{ env.cores }} -\ No newline at end of file -diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml -index afa5846d..8691823e 100644 ---- a/.github/workflows/cifuzz.yml -+++ b/.github/workflows/cifuzz.yml -@@ -18,7 +18,7 @@ jobs: - fuzz-seconds: 600 - dry-run: false - - name: Upload Crash -- uses: actions/upload-artifact@v1 -+ uses: actions/upload-artifact@v7 - if: failure() && steps.build.outcome == 'success' - with: - name: artifacts -diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml -deleted file mode 100644 -index 1268ef38..00000000 ---- a/.github/workflows/codeql-analysis.yml -+++ /dev/null -@@ -1,66 +0,0 @@ --name: "CodeQL" -- --on: -- push: -- branches: [master] -- pull_request: -- # The branches below must be a subset of the branches above -- branches: [master] -- schedule: -- - cron: '0 19 * * 4' -- --jobs: -- analyze: -- name: Analyze -- runs-on: ubuntu-latest -- -- strategy: -- fail-fast: false -- matrix: -- # Override automatic language detection by changing the below list -- # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] -- language: ['cpp'] -- # Learn more... -- # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection -- -- steps: -- - name: Checkout repository -- uses: actions/checkout@v2 -- with: -- # We must fetch at least the immediate parents so that if this is -- # a pull request then we can checkout the head. -- fetch-depth: 2 -- -- # If this run was triggered by a pull request event, then checkout -- # the head of the pull request instead of the merge commit. -- - run: git checkout HEAD^2 -- if: ${{ github.event_name == 'pull_request' }} -- -- # Initializes the CodeQL tools for scanning. -- - name: Initialize CodeQL -- uses: github/codeql-action/init@v1 -- with: -- languages: ${{ matrix.language }} -- # If you wish to specify custom queries, you can do so here or in a config file. -- # By default, queries listed here will override any specified in a config file. -- # Prefix the list here with "+" to use these queries and those in the config file. -- # queries: ./path/to/local/query, your-org/your-repo/queries@main -- -- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). -- # If this step fails, then you should remove it and run the build manually (see below) -- - name: Autobuild -- uses: github/codeql-action/autobuild@v1 -- -- # ℹ️ Command-line programs to run using the OS shell. -- # 📚 https://git.io/JvXDl -- -- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines -- # and modify them (or add more) to build your code if your project -- # uses a compiled language -- -- #- run: | -- # make bootstrap -- # make release -- -- - name: Perform CodeQL Analysis -- uses: github/codeql-action/analyze@v1 -diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml -new file mode 100644 -index 00000000..280d15ea ---- /dev/null -+++ b/.github/workflows/codeql.yml -@@ -0,0 +1,130 @@ -+# For most projects, this workflow file will not need changing; you simply need -+# to commit it to your repository. -+# -+# You may wish to alter this file to override the set of languages analyzed, -+# or to provide custom queries or build logic. -+# -+# ******** NOTE ******** -+# We have attempted to detect the languages in your repository. Please check -+# the `language` matrix defined below to confirm you have the correct set of -+# supported CodeQL languages. -+# -+name: "CodeQL" -+ -+on: -+ push: -+ branches: [ "master" ] -+ paths-ignore: -+ - src/third_party/** -+ schedule: -+ - cron: '0 0 * * *' -+ pull_request: -+ branches: '*' -+ paths-ignore: -+ - src/third_party/** -+ -+jobs: -+ analyze: -+ name: Analyze -+ # Runner size impacts CodeQL analysis time. To learn more, please see: -+ # - https://gh.io/recommended-hardware-resources-for-running-codeql -+ # - https://gh.io/supported-runners-and-hardware-resources -+ # - https://gh.io/using-larger-runners -+ # Consider using larger runners for possible analysis time improvements. -+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} -+ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} -+ permissions: -+ actions: read -+ contents: read -+ security-events: write -+ -+ strategy: -+ fail-fast: false -+ matrix: -+ language: [ 'cpp' ] -+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] -+ # Use only 'java' to analyze code written in Java, Kotlin or both -+ # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both -+ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support -+ -+ steps: -+ - name: Checkout repository -+ uses: actions/checkout@v6 -+ with: -+ submodules: recursive -+ -+ # Initializes the CodeQL tools for scanning. -+ - name: Initialize CodeQL -+ uses: github/codeql-action/init@v4 -+ with: -+ languages: ${{ matrix.language }} -+ # If you wish to specify custom queries, you can do so here or in a config file. -+ # By default, queries listed here will override any specified in a config file. -+ # Prefix the list here with "+" to use these queries and those in the config file. -+ -+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs -+ # queries: security-extended,security-and-quality -+ queries: security-and-quality -+ -+ -+ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). -+ # If this step fails, then you should remove it and run the build manually (see below) -+ #- name: Autobuild -+ # uses: github/codeql-action/autobuild@v2 -+ -+ # ℹ️ Command-line programs to run using the OS shell. -+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun -+ -+ # If the Autobuild fails above, remove it and uncomment the following three lines. -+ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. -+ -+ - run: | -+ make build WITH_ALL=1 -+ -+ - name: Perform CodeQL Analysis -+ uses: github/codeql-action/analyze@v4 -+ with: -+ category: "/language:${{matrix.language}}" -+ upload: false -+ id: step1 -+ -+ # Filter out rules with low severity or high false positve rate -+ # Also filter out warnings in third-party code -+ - name: Filter out unwanted errors and warnings -+ uses: advanced-security/filter-sarif@v1 -+ with: -+ patterns: | -+ -**:cpp/path-injection -+ -**:cpp/world-writable-file-creation -+ -**:cpp/poorly-documented-function -+ -**:cpp/potentially-dangerous-function -+ -**:cpp/use-of-goto -+ -**:cpp/integer-multiplication-cast-to-long -+ -**:cpp/comparison-with-wider-type -+ -**:cpp/leap-year/* -+ -**:cpp/ambiguously-signed-bit-field -+ -**:cpp/suspicious-pointer-scaling -+ -**:cpp/suspicious-pointer-scaling-void -+ -**:cpp/unsigned-comparison-zero -+ -**/cmake*/Modules/** -+ input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif -+ output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif -+ -+ - name: Upload CodeQL results to code scanning -+ uses: github/codeql-action/upload-sarif@v4 -+ with: -+ sarif_file: ${{ steps.step1.outputs.sarif-output }} -+ category: "/language:${{matrix.language}}" -+ -+ - name: Upload CodeQL results as an artifact -+ if: success() || failure() -+ uses: actions/upload-artifact@v7 -+ with: -+ name: codeql-results -+ path: ${{ steps.step1.outputs.sarif-output }} -+ retention-days: 5 -+ -+ - name: Fail if an error is found -+ run: | -+ ./.github/workflows/fail_on_error.py \ -+ ${{ steps.step1.outputs.sarif-output }}/cpp.sarif -diff --git a/.github/workflows/fail_on_error.py b/.github/workflows/fail_on_error.py -new file mode 100755 -index 00000000..f0a06ad6 ---- /dev/null -+++ b/.github/workflows/fail_on_error.py -@@ -0,0 +1,2 @@ -+# content removed, because it floods my email inbox. -+# see https://github.com/civetweb/civetweb/issues/1231#issuecomment-1967503931 -diff --git a/.gitignore b/.gitignore -index 8a3ae869..e5194fb8 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -27,6 +27,7 @@ log.out - /CMakeCache.txt - /CMakeFiles - /mingw-builds -+/output - - ################# - ## Eclipse -diff --git a/.travis.yml b/.travis.yml -index 8dabd5b4..3d8e516b 100644 ---- a/.travis.yml -+++ b/.travis.yml -@@ -9,60 +9,17 @@ cache: - directories: - - $HOME/third-party - --osx_image: xcode9 -- --addons: -- apt: -- packages: -- - cmake -- - openssl -- - libssl-dev -- - gdb -- sources: -- - kubuntu-backports -- -- - before_install: -- - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then -- mkdir $HOME/usr; -- export PATH="$HOME/usr/bin:$PATH"; -- wget https://cmake.org/files/v3.7/cmake-3.7.2-Linux-x86_64.sh --no-check-certificate; -- chmod +x cmake-3.7.2-Linux-x86_64.sh; -- ./cmake-3.7.2-Linux-x86_64.sh --prefix=$HOME/usr --exclude-subdir --skip-license; -- fi - - cmake --version - -- - install: -- - if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then -- PATH=~/.local/bin:${PATH}; -- pip install --user --upgrade pip; -- pip install --user cpp-coveralls; -- pip install --user codecov; -- pip install --user coverage; -- fi - - before_script: -- # Add an IPv6 config - see the corresponding Travis issue -- # https://github.com/travis-ci/travis-ci/issues/8361 -- - if [ "${ENABLE_IPV6}" == "YES" -a "${TRAVIS_OS_NAME}" == "linux" ]; then -- echo "Activating IPv6 on Travis"; -- sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; -- fi - # Check some settings of the build server (operating system, IPv6 availability, directory) - - uname -a -- - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then -- lsb_release -a; -- cat /etc/network/interfaces || true; -- fi - - ifconfig - - pwd - - ls -la -- - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then -- apt-cache search gcc | grep "GNU C compiler"; -- apt-cache search clang | grep compiler; -- fi -- - if [[ "${BUILD_TYPE}" == "OSX_OPENSSL_1_1" ]]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install openssl@1.1 ;fi - # Generate the build scripts with CMake - - mkdir output - - openssl version -@@ -82,6 +39,7 @@ before_script: - -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} - -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} - -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} - -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} - -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} - -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} -@@ -107,6 +65,7 @@ before_script: - -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING} - -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0} - -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1} -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=${OPENSSL_3_0} - -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS} - -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX} - -DCIVETWEB_ENABLE_SERVER_STATS=${ENABLE_SERVER_STATS} -@@ -128,16 +87,7 @@ after_failure: - - if [[ -f "$COREFILE" ]]; then gdb -c "$COREFILE" example -ex "thread apply all bt" -ex "set pagination 0" -batch; fi - - --# Modifications due to Travis IPv6 issues: --# https://github.com/travis-ci/travis-ci/issues/8711 --# https://github.com/travis-ci/travis-ci/issues/8361 --# DCIVETWEB_ENABLE_IPV6=${ENABLE_IPV6} or =NO -- - script: -- - if [ "${MACOSX_PACKAGE}" == "1" ]; then -- cd "${TRAVIS_BUILD_DIR}"; -- make -f Makefile.osx package; -- fi - - if [ "${RUN_UNITTEST}" == "1" ]; then - CTEST_OUTPUT_ON_FAILURE=1 CK_FORK=yes make all test; - fi -@@ -150,16 +100,7 @@ script: - fi - - echo "Build and test script DONE" - --# Coveralls options: https://github.com/eddyxu/cpp-coveralls/blob/master/README.md - after_success: -- - if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then -- echo "Preparing coverage tests"; -- echo "Creating coveralls coverage report"; -- coveralls --include src --exclude src/main.c --exclude src/third_party --include include --gcov-options '\-lp' --root .. --build-root .; -- echo "Creating codecov coverage report"; -- bash <(curl -s https://codecov.io/bash); -- echo "All coverage reports created"; -- fi - - - ######################################################################################### -@@ -171,350 +112,6 @@ after_success: - matrix: - fast_finish: true - include: -- -- --######################################################################################### --##### TRUSTY ######################################################################## --######################################################################################### -- -- - dist: trusty -- sudo: false -- os: linux -- compiler: clang -- addons: -- apt: -- sources: -- - ubuntu-toolchain-r-test -- - llvm-toolchain-precise-3.8 -- packages: -- - clang-3.8 -- env: -- idx=1 -- N=Clang3.8-Linux-Minimal-Debug -- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" -- BUILD_TYPE=Debug -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=NO -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=YES -- ENABLE_SSL=NO -- NO_CGI=YES -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_SERVER_STATS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- -- - dist: trusty -- sudo: false -- os: linux -- compiler: clang -- addons: -- apt: -- sources: -- - ubuntu-toolchain-r-test -- - llvm-toolchain-precise-3.8 -- packages: -- - clang-3.8 -- env: -- idx=3 -- N=Clang3.8-Linux-Default-Release -- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" -- BUILD_TYPE=Release -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_SERVER_STATS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- -- - dist: trusty -- sudo: required -- os: linux -- compiler: gcc -- addons: -- apt: -- sources: -- - ubuntu-toolchain-r-test -- packages: -- - g++-5 -- env: -- idx=5 -- N=GCC5-Linux-Complete-NoLua-Release -- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" -- BUILD_TYPE=Release -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=YES -- ENABLE_WEBSOCKETS=YES -- ENABLE_SERVER_STATS=YES -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=YES -- ALLOW_WARNINGS=YES -- RUN_UNITTEST=1 -- -- --######################################################################################### --##### COVERAGE ###################################################################### --######################################################################################### -- -- - os: linux -- sudo: required -- compiler: clang -- env: -- idx=6 -- N=GCCAnyVersion-Linux-Coverage -- BUILD_TYPE=Coverage -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=YES -- ENABLE_WEBSOCKETS=YES -- ENABLE_SERVER_STATS=YES -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- RUN_UNITTEST=1 -- --######################################################################################### --##### SHARED ######################################################################## --######################################################################################### -- -- - sudo: false -- os: linux -- compiler: clang -- env: -- idx=9 -- N=Clang-Linux-Default-Shared -- BUILD_TYPE=Debug -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=NO -- OPENSSL_1_1=YES -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=YES -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_SERVER_STATS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- -- --######################################################################################### --##### BUILD TYPES ################################################################### --######################################################################################### -- --# According to CMakeLists, options are: --# None Debug Release RelWithDebInfo MinSizeRel Coverage -- -- - -- os: linux -- compiler: gcc -- env: -- idx=15 -- N=GCCLinuxDefault_RelWithDebInfo -- BUILD_TYPE=RelWithDebInfo -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- -- - -- os: linux -- compiler: gcc -- env: -- idx=16 -- N=GCCLinuxDefault_MinSizeRel -- BUILD_TYPE=MinSizeRel -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- -- - -- os: linux -- compiler: gcc -- env: -- idx=17 -- N=GCCLinuxDefault_None -- BUILD_TYPE=None -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- --######################################################################################### --##### XENIAL, BIONIC, FOCAL ######################################################### --######################################################################################### -- -- - -- os: linux -- compiler: gcc -- dist: xenial -- env: -- idx=20 -- N=GCCLinuxDefault_xenial -- BUILD_TYPE=Release -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- -- - -- os: linux -- compiler: gcc -- dist: bionic -- env: -- idx=21 -- N=GCCLinuxDefault_bionic -- BUILD_TYPE=Release -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=NO -- OPENSSL_1_1=YES -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- -- - -- os: linux -- compiler: gcc -- dist: focal -- addons: -- apt: -- packages: -- - lsb-core -- env: -- idx=23 -- N=GCCLinuxDefault_focal -- BUILD_TYPE=Release -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=NO -- OPENSSL_1_1=YES -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=NO -- ENABLE_WEBSOCKETS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- RUN_UNITTEST=1 -- -- - ######################################################################################### - ##### FREEBSD BUILD ###########=##################################################### - ######################################################################################### -@@ -545,116 +142,4 @@ matrix: - ENABLE_DUKTAPE=NO - NO_CACHING=NO - ALLOW_WARNINGS=YES -- RUN_UNITTEST=1 -- -- --######################################################################################### --##### OSX BUILD ##################################################################### --######################################################################################### -- -- - -- os: osx -- sudo: required -- compiler: clang -- env: -- idx=8 -- N=Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad -- BUILD_TYPE=Release -- ENABLE_SSL_DYNAMIC_LOADING=NO -- OPENSSL_1_0=NO -- OPENSSL_1_1=YES -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=YES -- ENABLE_WEBSOCKETS=YES -- ENABLE_SERVER_STATS=YES -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=YES -- ALLOW_WARNINGS=YES -- OPENSSL_ROOT_DIR="/usr/local/opt/openssl@1.1" -- LDFLAGS="-L${OPENSSL_ROOT_DIR}/lib" -- CFLAGS="-I${OPENSSL_ROOT_DIR}/include" -- ADDITIONAL_CMAKE_ARGS="-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS} -DCMAKE_C_FLAGS=${CFLAGS}" -- PATH="${OPENSSL_ROOT_DIR}/bin:$PATH" -- DYLD_LIBRARY_PATH="${OPENSSL_ROOT_DIR}/lib:${DYLD_LIBRARY_PATH}" -- RUN_UNITTEST=1 -- -- - -- os: osx -- sudo: required -- compiler: clang -- env: -- idx=11 -- N=OSX-Package -- BUILD_TYPE=Release -- ENABLE_SSL_DYNAMIC_LOADING=YES -- OPENSSL_1_0=YES -- OPENSSL_1_1=NO -- ENABLE_CXX=NO -- ENABLE_LUA_SHARED=NO -- C_STANDARD=auto -- CXX_STANDARD=auto -- BUILD_SHARED=NO -- NO_FILES=NO -- ENABLE_SSL=YES -- NO_CGI=NO -- ENABLE_IPV6=YES -- ENABLE_WEBSOCKETS=YES -- ENABLE_SERVER_STATS=NO -- ENABLE_LUA=NO -- ENABLE_DUKTAPE=NO -- NO_CACHING=NO -- ALLOW_WARNINGS=YES -- MACOSX_PACKAGE=1 -- --######################################################################################### --######################################################################################### --##### END OF BUILD MATRIX ########################################################### --######################################################################################### --######################################################################################### -- --# Remove Lua build, until someone knows how to fix the CMake files --# --# - dist: trusty --# sudo: required --# os: linux --# compiler: clang --# addons: --# apt: --# sources: --# - ubuntu-toolchain-r-test --# - llvm-toolchain-precise-3.8 --# packages: --# - clang-3.8 --# - lua5.2 --# env: --# idx=99 --# N=Clang3.8-Linux-Complete-WithLua-Debug --# MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" --# BUILD_TYPE=Debug --# ENABLE_SSL_DYNAMIC_LOADING=YES --# OPENSSL_1_0=NO --# OPENSSL_1_1=YES --# ENABLE_CXX=NO --# ENABLE_LUA_SHARED=YES --# C_STANDARD=auto --# CXX_STANDARD=auto --# BUILD_SHARED=NO --# NO_FILES=NO --# ENABLE_SSL=YES --# NO_CGI=NO --# ENABLE_IPV6=YES --# ENABLE_WEBSOCKETS=YES --# ENABLE_SERVER_STATS=YES --# ENABLE_LUA=YES --# ENABLE_LUA_SHARED=YES --# ENABLE_DUKTAPE=NO --# NO_CACHING=YES --# ALLOW_WARNINGS=YES -+ RUN_UNITTEST=1 -\ No newline at end of file -diff --git a/CMakeLists.txt b/CMakeLists.txt -index c5368c08..602fbe41 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -1,11 +1,8 @@ --# Use at least CMake 3.3 --cmake_minimum_required (VERSION 3.3.0) --cmake_policy(VERSION 3.2.2) --cmake_policy(SET CMP0054 NEW) --cmake_policy(SET CMP0057 NEW) -+cmake_minimum_required(VERSION 3.10) -+cmake_policy(VERSION 3.10) - - # Set up the project --project (civetweb) -+project (civetweb VERSION 1.16.0) - - # Detect the platform reliably - if(ZEPHYR_BASE) -@@ -37,7 +34,7 @@ include(AddCXXCompilerFlag) - include(DetermineTargetArchitecture) - include(CMakeDependentOption) - --set(CIVETWEB_VERSION "1.15.0" CACHE STRING "The version of the civetweb library") -+set(CIVETWEB_VERSION "1.16.0" CACHE STRING "The version of the civetweb library") - string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CIVETWEB_VERSION_MATCH "${CIVETWEB_VERSION}") - if ("${CIVETWEB_VERSION_MATCH}" STREQUAL "") - message(FATAL_ERROR "Must specify a semantic version: major.minor.patch") -@@ -80,6 +77,10 @@ message(STATUS "Disable caching support - ${CIVETWEB_DISABLE_CACHING}") - option(CIVETWEB_ENABLE_CXX "Enables the C++ wrapper library" OFF) - message(STATUS "C++ wrappers - ${CIVETWEB_ENABLE_CXX}") - -+# HTTP2 Support -+option(CIVETWEB_ENABLE_HTTP2 "Enables HTTP2 support" OFF) -+message(STATUS "Use HTPP2 - ${CIVETWEB_ENABLE_HTTP2}") -+ - # IP Version 6 - option(CIVETWEB_ENABLE_IPV6 "Enables the IP version 6 support" ON) - message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}") -@@ -88,6 +89,11 @@ message(STATUS "IP Version 6 - ${CIVETWEB_ENABLE_IPV6}") - option(CIVETWEB_ENABLE_WEBSOCKETS "Enable websockets connections" OFF) - message(STATUS "Websockets support - ${CIVETWEB_ENABLE_WEBSOCKETS}") - -+# X DOM sockets support -+option(CIVETWEB_ENABLE_X_DOM_SOCKET "Enable X DOM sockets support" OFF) -+message(STATUS "X DOM sockets support - ${CIVETWEB_ENABLE_X_DOM_SOCKET}") -+ -+ - # Server statistics support - option(CIVETWEB_ENABLE_SERVER_STATS "Enable server statistics" OFF) - message(STATUS "Server statistics support - ${CIVETWEB_ENABLE_SERVER_STATS}") -@@ -179,7 +185,7 @@ if (CIVETWEB_ENABLE_LUA) - mark_as_advanced(CIVETWEB_LUA_SQLITE_VERSION) - - # Lua SQLite Verification Hash -- set(CIVETWEB_LUA_SQLITE_MD5_HASH 43234ae08197dfce6da02482ed14ec92 CACHE STRING -+ set(CIVETWEB_LUA_SQLITE_MD5_HASH ff7abd4aa8bd549eb18298fb954612f8 CACHE STRING - "The hash of Lua SQLite archive to be downloaded") - set_property(CACHE CIVETWEB_LUA_SQLITE_MD5_HASH PROPERTY VALUE ${CIVETWEB_LUA_SQLITE_MD5_HASH}) - mark_as_advanced(CIVETWEB_LUA_SQLITE_MD5_HASH) -@@ -227,10 +233,16 @@ message(STATUS "Compile for OpenSSL 1.0 API - ${CIVETWEB_SSL_OPENSSL_API_1_0}") - option(CIVETWEB_SSL_OPENSSL_API_1_1 "Use the OpenSSL 1.1 API" ON) - message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}") - --# OpenSSL 1.1 API -+# OpenSSL 3.0 API - option(CIVETWEB_SSL_OPENSSL_API_3_0 "Use the OpenSSL 3.0 API" OFF) - message(STATUS "Compile for OpenSSL 3.0 API - ${CIVETWEB_SSL_OPENSSL_API_3_0}") - -+option(CIVETWEB_ENABLE_GNUTLS "Use the GnuTls" OFF) -+message(STATUS "SSL support (GnuTLS) - ${CIVETWEB_ENABLE_GNUTLS}") -+ -+option(CIVETWEB_ENABLE_MBEDTLS "Use the MbedTls" OFF) -+message(STATUS "SSL support (MbedTLS) - ${CIVETWEB_ENABLE_MBEDTLS}") -+ - # Dynamically load or link the SSL libraries - cmake_dependent_option( - CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING "Dynamically loads the SSL library rather than linking it" ON -@@ -488,12 +500,18 @@ if (${CMAKE_BUILD_TYPE} MATCHES "[Dd]ebug") - add_definitions(-O0) - add_definitions(-g) - endif() -+if (CIVETWEB_ENABLE_HTTP2) -+ add_definitions(-DUSE_HTTP2) -+endif() - if (CIVETWEB_ENABLE_IPV6) - add_definitions(-DUSE_IPV6) - endif() - if (CIVETWEB_ENABLE_WEBSOCKETS) - add_definitions(-DUSE_WEBSOCKET) - endif() -+if (CIVETWEB_ENABLE_X_DOM_SOCKET) -+ add_definitions(-DUSE_X_DOM_SOCKET) -+endif() - if (CIVETWEB_ENABLE_SERVER_STATS) - add_definitions(-DUSE_SERVER_STATS) - endif() -@@ -523,6 +541,10 @@ if (CIVETWEB_ENABLE_MEMORY_DEBUGGING) - endif() - if (NOT CIVETWEB_ENABLE_SSL) - add_definitions(-DNO_SSL) -+elseif (CIVETWEB_ENABLE_GNUTLS) -+ add_definitions(-DUSE_GNUTLS) -+elseif (CIVETWEB_ENABLE_MBEDTLS) -+ add_definitions(-DUSE_MBEDTLS) - elseif (NOT CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING) - add_definitions(-DNO_SSL_DL) - else() -@@ -627,15 +649,15 @@ configure_file( - ) - - install( -- FILES -+ FILES - "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" -- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" -+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" - ) - - install( -- FILES -+ FILES - "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-cpp.pc" -- DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" -+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" - ) - - write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake -@@ -686,4 +708,3 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_PACKAGE_DEPENDS}") - - # Finalize CPack settings - include(CPack) -- -diff --git a/CREDITS.md b/CREDITS.md -index fc98b637..f6a2a79d 100644 ---- a/CREDITS.md -+++ b/CREDITS.md -@@ -130,6 +130,7 @@ - * Lammert Bies - * Lars Immisch - * Lawrence -+* Lev275568 - * Li Peng - * Lianghui - * Lorenzo Canepa -@@ -225,6 +226,7 @@ - * Torben Jonas - * Uilian Ries - * Ulrich Hertlein -+* videofan3d - * Walt Steverson - * wangli28 - * webxer -diff --git a/Dockerfile b/Dockerfile -new file mode 100644 -index 00000000..3bb7540a ---- /dev/null -+++ b/Dockerfile -@@ -0,0 +1,53 @@ -+# --------------------------------------------------- -+# Build Stage -+# --------------------------------------------------- -+ -+# Use Alpine Linux 3.18 as the base image for the build stage -+FROM alpine:3.18 AS build -+ -+# Update package list and install build dependencies -+RUN apk update && \ -+ apk add --no-cache \ -+ build-base zlib-dev -+ -+# Set the working directory inside the container -+WORKDIR /civetweb -+ -+# Copy source code and other necessary files into the container -+COPY src ./src/ -+COPY include ./include/ -+COPY Makefile ./ -+COPY resources ./resources/ -+COPY *.md ./ -+ -+# Build Civetweb with all features and install it into /app directory -+RUN make build && \ -+ make WITH_ALL=1 && \ -+ make install PREFIX=/app -+ -+# --------------------------------------------------- -+# Image Stage -+# --------------------------------------------------- -+ -+# Use Alpine Linux 3.18 as the base image for the final stage -+FROM alpine:3.18 -+ -+# Update package list and install runtime dependencies -+RUN apk update && \ -+ apk add --no-cache \ -+ libstdc++ zlib -+ -+# Create a non-root user and group for running Civetweb -+RUN addgroup -S civetweb && adduser -S civetweb -G civetweb -+ -+# Switch to the non-root user -+USER civetweb -+ -+# Copy the built application from the build stage into this stage -+COPY --chown=civetweb:civetweb --from=build /app/ /app/ -+ -+# Expose port 8080 for the application -+EXPOSE 8080 -+ -+# Set the entry point for the container -+ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] -diff --git a/LICENSE.md b/LICENSE.md -index a74a0fe4..db39daf4 100644 ---- a/LICENSE.md -+++ b/LICENSE.md -@@ -11,7 +11,7 @@ Civetweb License - - ### Included with all features. - --> Copyright (c) 2013-2021 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) -+> Copyright (c) 2013-2025 The CivetWeb developers ([CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)) - > - > Copyright (c) 2004-2013 Sergey Lyubka - > -diff --git a/Makefile b/Makefile -index 31954e47..7831ce39 100644 ---- a/Makefile -+++ b/Makefile -@@ -71,7 +71,11 @@ ifdef WITH_CFLAGS - CFLAGS += $(WITH_CFLAGS) - endif - --LIBS = -lpthread -lm $(LOPT) -+LIBS = -+ifneq ($(TARGET_OS), RTEMS) -+LIBS += -lpthread -+endif -+LIBS += -lm $(LOPT) - - ifdef WITH_DEBUG - CFLAGS += -g -DDEBUG -@@ -93,6 +97,9 @@ endif - - ifdef NO_SSL - CFLAGS += -DNO_SSL -+else ifdef WITH_GNUTLS -+ CFLAGS += -DUSE_GNUTLS -+ LIBS += -lgnutls -lhogweed -lgmp -lnettle - else ifdef WITH_MBEDTLS - CFLAGS += -DUSE_MBEDTLS - LIBS += -lmbedcrypto -lmbedtls -lmbedx509 -@@ -100,6 +107,8 @@ else ifdef WITH_OPENSSL_API_1_0 - CFLAGS += -DOPENSSL_API_1_0 - else ifdef WITH_OPENSSL_API_1_1 - CFLAGS += -DOPENSSL_API_1_1 -+else ifdef WITH_OPENSSL_API_3_0 -+ CFLAGS += -DOPENSSL_API_3_0 - else - #Use OpenSSL 1.1 API version as default - CFLAGS += -DOPENSSL_API_1_1 -@@ -176,7 +185,9 @@ ifdef WITH_COMPRESSION - endif - - ifdef WITH_ZLIB -+ifneq ($(TARGET_OS), RTEMS) - LIBS += -lz -+endif - CFLAGS += -DUSE_ZLIB - endif - -@@ -295,9 +306,11 @@ help: - @echo " WITH_CPP=1 build library with c++ classes" - @echo " WITH_EXPERIMENTAL=1 build with experimental features" - @echo " WITH_DAEMONIZE=1 build with daemonize." -+ @echo " WITH_GNUTLS=1 build with GnuTLS support." - @echo " WITH_MBEDTLS=1 build with mbedTLS support." - @echo " WITH_OPENSSL_API_1_0=1 build with OpenSSL 1.0.x support." - @echo " WITH_OPENSSL_API_1_1=1 build with OpenSSL 1.1.x support." -+ @echo " WITH_OPENSSL_API_3_0=1 build with OpenSSL 3.0.x support." - @echo " NO_SSL=1 build without SSL support. Build will not need libcrypto/libssl." - @echo " NO_CGI=1 build without CGI support." - @echo " NO_CACHING=1 disable caching. Send no-cache/no-store headers." -@@ -443,4 +456,3 @@ indent: - astyle --suffix=none --style=linux --indent=spaces=4 --lineend=linux include/*.h src/*.c src/*.cpp src/*.inl examples/*/*.c examples/*/*.cpp - - .PHONY: all help build install clean lib so -- -diff --git a/Makefile.osx b/Makefile.osx -index 44260e84..a14fdee9 100644 ---- a/Makefile.osx -+++ b/Makefile.osx -@@ -13,7 +13,7 @@ WITH_LUA = 1 - PACKAGE = Civetweb - BUILD_DIR = out - --CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.4 -ObjC -arch i386 -arch x86_64 -+CFLAGS += -DUSE_COCOA -DENABLE_CREATE_CONFIG_FILE -mmacosx-version-min=10.6 -ObjC -arch x86_64 -arch arm64 - LDFLAGS += -framework Cocoa - - DMG_DIR = $(BUILD_DIR)/dmg -diff --git a/README.md b/README.md -index 1d1c82b8..5ff09779 100644 ---- a/README.md -+++ b/README.md -@@ -9,18 +9,12 @@ - [![Forks](https://img.shields.io/github/forks/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/network/members) - [![Latest Release](https://img.shields.io/github/v/release/civetweb/civetweb.svg)](https://github.com/civetweb/civetweb/releases) - --Continuous integration for Linux and macOS ([Travis CI](https://app.travis-ci.com/github/civetweb/civetweb)): -- --[![Travis Build Status](https://api.travis-ci.com/civetweb/civetweb.svg?branch=master)](https://app.travis-ci.com/github/civetweb/civetweb) -- --Continuous integration for Windows ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): -+Continuous integration ([AppVeyor](https://ci.appveyor.com/project/civetweb/civetweb)): - - [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/civetweb/civetweb?svg=true)](https://ci.appveyor.com/project/civetweb/civetweb/branch/master) - - Test coverage check ([coveralls](https://coveralls.io/github/civetweb/civetweb), [codecov](https://codecov.io/gh/civetweb/civetweb/branch/master)) (using different tools/settings): - --[![Coveralls](https://img.shields.io/coveralls/civetweb/civetweb.svg?maxAge=3600)]() --[![Coverage Status](https://coveralls.io/repos/github/civetweb/civetweb/badge.svg?branch=master)](https://coveralls.io/github/civetweb/civetweb?branch=master) - [![codecov](https://codecov.io/gh/civetweb/civetweb/branch/master/graph/badge.svg)](https://codecov.io/gh/civetweb/civetweb) - - Static source code analysis ([Coverity](https://scan.coverity.com/projects/5784)): [![Coverity Scan Build Status](https://scan.coverity.com/projects/5784/badge.svg)](https://scan.coverity.com/projects/5784) -@@ -54,18 +48,14 @@ CivetWeb must be used with an earlier or later version (see also [here](https:// - Bugs and requests should be filed on GitHub - [https://github.com/civetweb/civetweb/issues](https://github.com/civetweb/civetweb/issues) - --New releases are announced on Google Groups --[https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb) -- --Formerly some support question and discussion threads have been at [Google groups](https://groups.google.com/d/forum/civetweb). --Recent questions and discussions use [GitHub issues](https://github.com/civetweb/civetweb/issues). -- - Source releases can be found on GitHub - [https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases) - - A very brief overview can be found on GitHub Pages - [https://civetweb.github.io/civetweb/](https://civetweb.github.io/civetweb/) - -+Note: The [Google group](https://groups.google.com/d/forum/civetweb) is no longer used but has been replaced by [GitHub issues](https://github.com/civetweb/civetweb/issues). -+ - - Quick start documentation - -------------------------- -@@ -75,6 +65,7 @@ Quick start documentation - - [docs/Building.md](https://github.com/civetweb/civetweb/blob/master/docs/Building.md) - Building the Server (quick start guide) - - [docs/Embedding.md](https://github.com/civetweb/civetweb/blob/master/docs/Embedding.md) - Embedding (how to add HTTP support to an existing application) - - [docs/OpenSSL.md](https://github.com/civetweb/civetweb/blob/master/docs/OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL. -+- [docs/Docker.md](https://github.com/civetweb/civetweb/blob/master/docs/Docker.md) - Use CivetWeb in a Docker container. - - [API documentation](https://github.com/civetweb/civetweb/tree/master/docs/api) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)). - - [RELEASE_NOTES.md](https://github.com/civetweb/civetweb/blob/master/RELEASE_NOTES.md) - Release Notes - - [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md) - Security Policy -@@ -133,6 +124,8 @@ simplicity by a carefully selected list of features: - - [Mbed TLS](https://github.com/ARMmbed/mbedtls) - -+[GNU TLS](https://gnutls.org) -+ - - Support - ------- -@@ -171,7 +164,7 @@ Authors - - CivetWeb was forked from the last MIT version of Mongoose in August 2013. - Since then, CivetWeb has seen many improvements from various authors --(Copyright (c) 2013-2021 the CivetWeb developers, MIT license). -+(Copyright (c) 2013-2025 the CivetWeb developers, MIT license). - - A list of authors can be found in [CREDITS.md](https://github.com/civetweb/civetweb/blob/master/CREDITS.md). - -@@ -179,7 +172,7 @@ CivetWeb is based on the [Mongoose project](https://github.com/cesanta/mongoose) - Sergey Lyubka(2004-2013) who released it under the MIT license. - However, on August 16, 2013, - [Mongoose was relicensed to a dual GPL V2 + commercial license](https://groups.google.com/forum/#!topic/mongoose-users/aafbOnHonkI) --and CiwetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose". -+and CivetWeb was created by Thomas Davis (sunsetbrew) as "the MIT fork of mongoose". - The license change and CivetWeb fork was mentioned on the Mongoose - [Wikipedia](https://en.wikipedia.org/wiki/Mongoose_(web_server)) - page as well, but it's getting deleted (and added again) there every -diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md -index 354061ee..0bdd1c5a 100644 ---- a/RELEASE_NOTES.md -+++ b/RELEASE_NOTES.md -@@ -1,3 +1,13 @@ -+Release Notes v1.17 (work in progress) -+=== -+### Objectives: *to be defined* -+ -+Changes -+------- -+ -+- Update version number -+ -+ - Release Notes v1.16 - === - ### Objectives: *bug fixes, documentation, WebDAV* -@@ -21,6 +31,9 @@ Changes - - Remove Conan support - - Update version number - -+Note: A precompiled 32-bit executables for Windows is no longer provided, but only a 64 bit version. -+The source code itself still supports 32-bit platforms. -+ - Known Issues - ----- - -diff --git a/SECURITY.md b/SECURITY.md -index b1781aff..78c85541 100644 ---- a/SECURITY.md -+++ b/SECURITY.md -@@ -14,16 +14,12 @@ Selected, critical defects are fixed in the latest release as well. - Note that different security policies apply to different files/folders in this project: - - The core components are include/civetweb.h, src/civetweb.c and src/*.inl. These files are part of every server instance in production. Therefore they have to undergo the most intensive security tests and reviews. - - The src/main.c file is used by the standalone server. It is used in various tests in combination with the aforementioned core components. Applications embedding civetweb will not use main.c and, thus, do not suffer from any vulnerabilities therein. --- The example folders contain different usage examples in different maintenance state. This is explained in detail in the README file there. -+- The example folders contain different usage examples in different maintenance state. Some of them skipped error handling to keep the code shorter and easier to understand. This is explained in more detail in the README file there. - - The content of all test folders (test, unittest, fuzztest) is used to test the server functionality. These tests are not designed with security in mind - on the contrary, some tests contain scripts or settings that even introduce security leaks on purpose. All tests are only meant to be run in a test environment. You should not use the content of any test folder in production. Also certificates in "resources/cert" are only meant to be used in test environments and must never be used in production. - - - ## Reporting a Vulnerability - --Please send vulnerability reports by email to bel2125 at gmail com. --Vulnerability with low severity can be sent directly by email. -- --For high severity vulnerabilities, you can get an individual gpg key to encrypt your detailed description of vulnerabilities you want to report. -- --If you do not get any response within one week, your email might have been lost (e.g., deleted as false positive by a spam filter). In this case, please open a GitHub issue. -+Ideally open a github issue for suspected vulnerabilities, even if you do not post all details there. This will help against emails getting lost. -+Detailed vulnerability reports including exploit code should be sent by email to bel2125 at gmail com. - -diff --git a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj -index 7eb6fd9f..58e7fc7c 100644 ---- a/VisualStudio/civetweb_lua/civetweb_lua.vcxproj -+++ b/VisualStudio/civetweb_lua/civetweb_lua.vcxproj -@@ -110,7 +110,8 @@ - true - - -- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.14 --set-product-version 1.14 -+ -+ - - - -@@ -159,7 +160,7 @@ - true - - -- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 -+ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 - - - -@@ -180,7 +181,7 @@ - true - - -- rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.15 --set-product-version 1.15 -+ rcedit-x86.exe $(OutDir)$(TargetName)$(TargetExt) --set-file-version 1.17 --set-product-version 1.17 - - - -diff --git a/appveyor.yml b/appveyor.yml -index 41cde75f..b40e514f 100644 ---- a/appveyor.yml -+++ b/appveyor.yml -@@ -159,59 +159,6 @@ environment: - enable_stats: YES - configuration: Release - platform: x64 -- # Visual Studio 2010 -- - id: Full-VS2010-x86 -- compiler: msvc-16-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x86 -- # Visual Studio 2012 -- - id: Full-VS2012-x86 -- compiler: msvc-17-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x86 -- # Visual Studio 2013 -- - id: Full-VS2013-x86 -- compiler: msvc-18-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x86 -- image: Visual Studio 2013 -- - id: Full-VS2013-x64 -- compiler: msvc-18-seh -- build_shared: NO -- no_files: NO -- enable_ipv6: YES -- enable_ssl: YES -- enable_websockets: YES -- no_cgi: NO -- no_caching: NO -- enable_stats: YES -- configuration: Release -- platform: x64 -- image: Visual Studio 2013 - # Visual Studio 2015 - - id: Full-VS2015-x86 - compiler: msvc-19-seh -@@ -272,7 +219,7 @@ environment: - build_shared: NO - no_files: NO - enable_ipv6: YES -- enable_ssl: YES -+ enable_ssl: NO - enable_websockets: YES - no_cgi: NO - no_caching: NO -@@ -280,7 +227,21 @@ environment: - configuration: Release - platform: x64 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 -- # Ubuntu -+ # Visual Studio 2022 -+ - id: Full-VS2022-x64 -+ compiler: msvc-22-seh -+ build_shared: NO -+ no_files: NO -+ enable_ipv6: YES -+ enable_ssl: NO -+ enable_websockets: YES -+ no_cgi: NO -+ no_caching: NO -+ enable_stats: YES -+ configuration: Release -+ platform: x64 -+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 -+ # Ubuntu - - id: Ubuntu1604-GCC-x64 - compiler: gcc-5.1.0-posix - build_shared: NO -@@ -380,6 +341,8 @@ before_build: - - if "%compiler_version%"=="19" (set "vs_version=14" & set "vs_year=2015") - - if "%compiler_version%"=="20" (set "vs_version=15" & set "vs_year=2017") - - if "%compiler_version%"=="21" (set "vs_version=16" & set "vs_year=2019") -+ - if "%compiler_version%"=="22" (set "vs_version=17" & set "vs_year=2022") -+ - if "%compiler_version%"=="23" (set "vs_version=18" & set "vs_year=2026") - - if "%compiler_name%"=="msvc" (set "generator=Visual Studio %vs_version% %vs_year%") - - set "arch_arg= " - - if "%compiler_name%"=="msvc" ( -@@ -440,6 +403,7 @@ before_build: - -DCIVETWEB_CXX_STANDARD=%cxx_standard% - -DCIVETWEB_SSL_OPENSSL_API_1_0=NO - -DCIVETWEB_SSL_OPENSSL_API_1_1=YES -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO - "%source_path%" - - cmake - -G "%generator%" %arch_arg% -@@ -461,6 +425,7 @@ before_build: - -DCIVETWEB_CXX_STANDARD=%cxx_standard% - -DCIVETWEB_SSL_OPENSSL_API_1_0=NO - -DCIVETWEB_SSL_OPENSSL_API_1_1=YES -+ -DCIVETWEB_SSL_OPENSSL_API_3_0=NO - "%source_path%" - - powershell Push-AppveyorArtifact CMakeCache.txt - - cd "%source_path%" -diff --git a/docs/Building.md b/docs/Building.md -index ed30bd77..b6185333 100644 ---- a/docs/Building.md -+++ b/docs/Building.md -@@ -179,8 +179,9 @@ make build COPT="-DNDEBUG -DNO_CGI" - | `SSL_ALREADY_INITIALIZED` | do not initialize libcrypto | - | `OPENSSL_API_1_0` | Use OpenSSL V1.0.x interface | - | `OPENSSL_API_1_1` | Use OpenSSL V1.1.x interface | --| `OPENSSL_API_3_0` | Use OpenSSL V3.x interface | --| `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_*) | -+| `OPENSSL_API_3_0` | Use OpenSSL V3.0.x interface | -+| `USE_GNUTLS` | Use GnuTLS (cannot be combined with OPENSSL_API_* or USE_MBEDTLS) | -+| `USE_MBEDTLS` | Use MbedTLS (cannot be combined with OPENSSL_API_* or USE_GNUTLS) | - | | | - | `BUILD_DATE` | define as a string to be used as build id instead of __DATE__ | - | | | -diff --git a/docs/Docker.md b/docs/Docker.md -new file mode 100644 -index 00000000..ac0fadc9 ---- /dev/null -+++ b/docs/Docker.md -@@ -0,0 +1,96 @@ -+# Running CivetWeb in Docker -+ -+## Overview -+ -+This guide explains how to build and run CivetWeb using a multi-stage Dockerfile. The Dockerfile uses Alpine Linux for both the build and runtime stages, making the final image lightweight. -+ -+## Prerequisites -+ -+- Docker installed on your machine -+- Basic understanding of Docker commands -+ -+## Dockerfile Explained -+ -+### Build Stage -+ -+The build stage uses Alpine Linux 3.18 and installs the necessary build tools and libraries. -+ -+```Dockerfile -+FROM alpine:3.18 AS build -+RUN apk update && \ -+ apk add --no-cache \ -+ build-base zlib-dev -+``` -+ -+The source code and other necessary files are copied into the `/civetweb` directory in the container. -+ -+```Dockerfile -+WORKDIR /civetweb -+COPY src ./src/ -+COPY include ./include/ -+COPY Makefile ./ -+COPY resources ./resources/ -+COPY *.md ./ -+``` -+ -+The Civetweb server is then built and installed into the `/app` directory. -+ -+```Dockerfile -+RUN make build && \ -+ make WITH_ALL=1 && \ -+ make install PREFIX=/app -+``` -+ -+### Image Stage -+ -+The image stage also uses Alpine Linux 3.18 but installs only the runtime dependencies. -+ -+```Dockerfile -+FROM alpine:3.18 -+RUN apk update && \ -+ apk add --no-cache \ -+ libstdc++ zlib -+``` -+ -+A non-root user `civetweb` is created for security reasons. -+ -+```Dockerfile -+RUN addgroup -S civetweb && adduser -S civetweb -G civetweb -+USER civetweb -+``` -+ -+The built application from the build stage is copied into this stage. -+ -+```Dockerfile -+COPY --chown=civetweb:civetweb --from=build /app/ /app/ -+``` -+ -+The container will listen on port 8080 at runtime. -+ -+```Dockerfile -+EXPOSE 8080 -+``` -+ -+Finally, the entry point for the container is set. -+ -+```Dockerfile -+ENTRYPOINT [ "/app/bin/civetweb", "/app/etc/civetweb.conf" ] -+``` -+ -+## Build and Run -+ -+To build the Docker image, run: -+ -+```bash -+docker build -t civetweb:latest . -+``` -+ -+To run the container, execute: -+ -+```bash -+docker run -p 8080:8080 civetweb:latest -+``` -+ -+## Conclusion -+ -+This Dockerfile provides a secure and efficient way to build and run CivetWeb. The use of multi-stage builds ensures that the final image is as small as possible. The `EXPOSE` directive informs that the application will listen on port 8080, making it easier to map ports when running the container. -diff --git a/docs/Embedding.md b/docs/Embedding.md -index 0a0fa336..0291ff92 100644 ---- a/docs/Embedding.md -+++ b/docs/Embedding.md -@@ -213,10 +213,15 @@ about web server instance: - - When `mg_start()` returns, all initialization is guaranteed to be complete - (e.g. listening ports are opened, SSL is initialized, etc). `mg_start()` starts --some threads: a master thread, that accepts new connections, and several --worker threads, that process accepted connections. The number of worker threads --is configurable via `num_threads` configuration option. That number puts a -+some threads: a master thread, that accepts new connections, and optionally some -+worker threads, that process accepted connections. The maximum number of worker -+threads is configurable via `num_threads` configuration option. That number puts a - limit on number of simultaneous requests that can be handled by CivetWeb. -+ -+The number of worker threads to be pre-spawned at startup is specified via the -+'prespawn_threads' configuration option; worker threads that are not pre-spawned -+will instead be demand-created the first time they are needed. -+ - If you embed CivetWeb into a program that uses SSL outside CivetWeb as well, - you may need to initialize SSL before calling `mg_start()`, and set the pre- - processor define `SSL_ALREADY_INITIALIZED`. This is not required if SSL is -diff --git a/docs/OpenSSL.md b/docs/OpenSSL.md -index 9822e9a4..657a6c35 100644 ---- a/docs/OpenSSL.md -+++ b/docs/OpenSSL.md -@@ -1,5 +1,4 @@ --Adding OpenSSL Support --===== -+# Adding OpenSSL Support - - Civetweb supports *HTTPS* connections using the OpenSSL transport layer - security (TLS) library. OpenSSL is a free, open source library (see -@@ -46,7 +45,7 @@ One can use the following steps in Windows (in Linux replace "copy" by "cp" - and "type" by "cat"): - -
        --  openssl genrsa -des3 -out server.key 1024
        -+  openssl genrsa -des3 -out server.key 2048
        - 
        -   openssl req -new -key server.key -out server.csr
        - 
        -diff --git a/docs/README.md b/docs/README.md
        -index 54ed7128..d3fb0e5d 100644
        ---- a/docs/README.md
        -+++ b/docs/README.md
        -@@ -31,9 +31,6 @@ Source releases can be found on GitHub
        - 
        - A security policy can be found in [SECURITY.md](https://github.com/civetweb/civetweb/blob/master/SECURITY.md).
        - 
        --CivetWeb is free of charge, however, donations for maintenance are welcome:
        --[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=88ZLXZ6U77GJU)
        --
        - 
        - Documentation
        - ---------------
        -@@ -43,6 +40,7 @@ Documentation
        - - [Building.md](Building.md) - Building the Server (quick start guide)
        - - [Embedding.md](Embedding.md) - Embedding (how to add HTTP support to an existing application)
        - - [OpenSSL.md](OpenSSL.md) - Adding HTTPS (SSL/TLS) support using OpenSSL.
        -+- [Docker.md](Docker.md) - Use CivetWeb in a Docker container.
        - - [APIReference.md](APIReference.md) - Additional documentation on the civetweb application programming interface ([civetweb.h](https://github.com/civetweb/civetweb/blob/master/include/civetweb.h)).
        - 
        - [Authors](https://github.com/civetweb/civetweb/blob/master/CREDITS.md)
        -diff --git a/docs/UserManual.md b/docs/UserManual.md
        -index df9b3685..7dc48c38 100644
        ---- a/docs/UserManual.md
        -+++ b/docs/UserManual.md
        -@@ -180,7 +180,7 @@ This option can be specified multiple times. All specified header lines will be
        - ### allow\_index\_script\_resource `no`
        - Index scripts (like `index.cgi` or `index.lua`) may have script handled resources.
        - 
        --It this feature is activated, that /some/path/file.ext might be handled by:
        -+If this feature is activated, then /some/path/file.ext might be handled by:
        -   1. /some/path/file.ext (with PATH\_INFO='/', if ext = cgi)
        -   2. /some/path/index.lua with mg.request\_info.path\_info='/file.ext'
        -   3. /some/path/index.cgi with PATH\_INFO='/file.ext'
        -@@ -296,6 +296,15 @@ The current directory is commonly referenced as dot (`.`).
        - It is recommended to use an absolute path for document\_root, in order to
        - avoid accidentally serving the wrong directory.
        - 
        -+### fallback\_document\_root `.`
        -+An optional second directory to check for a file to serve, if the requested
        -+filename was not found in the document\_root directory.
        -+This can be useful in cases where an app ships with a read-only HTML content
        -+directory as part of its install, but you nevertheless want to allow the user
        -+to customize the served content by placing modified or additional files into
        -+a writable directory, where they will take precedence over their read-only
        -+counterparts, on a per-file basis.
        -+
        - ### enable\_auth\_domain\_check `yes`
        - When using absolute URLs, verify the host is identical to the authentication\_domain.
        - If enabled, requests to absolute URLs will only be processed
        -@@ -431,11 +440,15 @@ of 1000.
        - ### listening\_ports `8080`
        - Comma-separated list of ports to listen on. If the port is SSL, a
        - letter `s` must be appended, for example, `80,443s` will open
        --port 80 and port 443, and connections on port 443 will be SSL-ed.
        -+port 80 and port 443, and connections on port 443 will be SSL-ed. If the port
        -+should be optional the letter `o` must be appended, for example with `80o,443s`
        -+the server will not exit if binding to port 80 is not possible during startup.
        - For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'.
        - Redirect ports will redirect all their traffic to the first configured
        - SSL port. For example, if `listening_ports` is `80r,443s`, then all
        - HTTP traffic coming at port 80 will be redirected to HTTPS port 443.
        -+For ports with redirection configured `authentication_domain` will
        -+be used as host component of the redirection url.
        - 
        - It is possible to specify an IP address to bind to. In this case,
        - an IP address and a colon must be prepended to the port number.
        -@@ -505,7 +518,7 @@ The script can define callbacks to be notified when the server starts
        - or stops. Furthermore, it can be used for log filtering or formatting. 
        - The Lua state remains open until the server is stopped.
        - 
        --For a detailed descriotion of available Lua callbacks see section
        -+For a detailed description of available Lua callbacks see section
        - "Lua background script" below.
        - 
        - ### lua\_background\_script\_params
        -@@ -549,8 +562,8 @@ The configuration value is approximate, the real limit might be a few bytes off.
        - The minimum is 1024 (1 kB).
        - 
        - ### num\_threads `50`
        --Number of worker threads. CivetWeb handles each incoming connection in a
        --separate thread. Therefore, the value of this option is effectively the number
        -+Maximum number of worker threads allowed. CivetWeb handles each incoming connection
        -+in a separate thread. Therefore, the value of this option is effectively the number
        - of concurrent HTTP connections CivetWeb can handle.
        - 
        - If there are more simultaneous requests (connection attempts), they are queued.
        -@@ -563,6 +576,15 @@ In case the clients are web browsers, it is recommended to use `num_threads` of
        - at least 5, since browsers often establish multiple connections to load a single
        - web page, including all linked documents (CSS, JavaScript, images, ...).
        - 
        -+### prespawn\_threads '0'
        -+Number of worker threads that should be pre-spawned by mg_start().  Defaults to
        -+0, meaning no worker threads will be pre-spawned at startup; rather, worker threads
        -+will be spawned when a new connection comes in and there aren't currently any
        -+idle worker threads available to handle it (if we haven't already reached the
        -+maximum worker-thread count as specified by num_threads).  If this value is
        -+specified less than zero, or greater than the value of num_threads, it will be
        -+treated as if it was specified to be equal to the value of num_threads.
        -+
        - ### listen\_backlog `200`
        - Maximum number of connections waiting to be accepted by the server operating system.
        - Internally, this parameter is passed to the "listen" socket/system call.
        -@@ -670,7 +692,8 @@ The OpenSSL cipher string uses different cipher names than IANA
        - (see [this mapping](https://testssl.sh/openssl-iana.mapping.html)).
        - 
        - In case CivetWeb is built with a TLS library other than OpenSSL 
        --(e.g., [mbedTLS](https://tls.mbed.org/supported-ssl-ciphersuites)), 
        -+(e.g., [mbedTLS](https://tls.mbed.org/supported-ssl-ciphersuites)
        -+or [GnuTLS](https://www.gnutls.org/manual/html_node/Supported-ciphersuites.html)), 
        - the cipher names may be different.
        - 
        - ### ssl\_default\_verify\_paths `yes`
        -@@ -806,6 +829,11 @@ be used for websockets as well. Since websockets use a different URL scheme
        - websockets may also be served from a different directory. By default,
        - the document\_root is used as websocket\_root as well.
        - 
        -+### fallback\_websocket\_root
        -+An optional second directory to check for websocket-files that were
        -+not found in the websocket\_root directory.  (See the documentation for
        -+fallback\_root for details)
        -+
        - ### websocket\_timeout\_ms
        - Timeout for network read and network write operations for websockets, WS(S),
        - in milliseconds. If this value is not set, the value of request\_timeout\_ms
        -@@ -862,8 +890,8 @@ All port, socket, process and thread specific parameters are per server:
        - `enable_http2`, `enable_keep_alive`, `enable_websocket_ping_pong`,
        - `keep_alive_timeout_ms`, `linger_timeout_ms`, `listen_backlog`,
        - `listening_ports`, `lua_background_script`, `lua_background_script_params`,
        --`max_request_size`, `num_threads`, `request_timeout_ms`, `run_as_user`,
        --`tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`.
        -+`max_request_size`, `num_threads`, 'prespawn_threads', `request_timeout_ms`,
        -+`run_as_user`, `tcp_nodelay`, `throttle`, `websocket_timeout_ms` + all options from `main.c`.
        - 
        - All other options can be set per domain. In particular
        - `authentication_domain`, `document_root` and (for HTTPS) `ssl_certificate`
        -@@ -969,6 +997,7 @@ mg (table):
        -     mg.onerror(msg)             -- error handler, can be overridden
        -     mg.auth_domain              -- a string that holds the HTTP authentication domain
        -     mg.document_root            -- a string that holds the document root directory
        -+    mg.fallback_document_root   -- a string that holds an optional second document root directory
        -     mg.lua_type                 -- a string that holds the lua script type
        -     mg.system                   -- a string that holds the operating system name
        -     mg.version                  -- a string that holds CivetWeb version
        -@@ -1028,6 +1057,7 @@ If websocket and timers support is enabled then the following is also available:
        -     mg.set_timeout(fn,delay,[interval])  -- call function after delay at an interval
        -     mg.set_interval(fn,delay,[interval]) -- call function after delay at an interval
        -     mg.websocket_root                    -- a string that holds the websocket root
        -+    mg.fallback_websocket_root           -- a string that holds an optional second websocket root
        - 
        - connect (function):
        - 
        -diff --git a/docs/api/mg_form_data_handler.md b/docs/api/mg_form_data_handler.md
        -index 368d7786..8f477b0d 100644
        ---- a/docs/api/mg_form_data_handler.md
        -+++ b/docs/api/mg_form_data_handler.md
        -@@ -9,15 +9,15 @@
        - |**`field_found`**|**`int field_found( const char *key, const char *filename, char *path, size_t pathlen, void *user_data )`**;|
        - ||The callback function `field_found()` is called when a new field has been found. The return value of this callback is used to define how the field should be processed. The parameters contain the following information:|
        - ||**`key`** - The name of the field as it was named with the `name` tag in the HTML source.|
        --||**`filename`** - The name of the file to upload. Please not that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.|
        --||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.|
        -+||**`filename`** - The name of the file to upload. Please note that this parameter is only valid when the input type was set to `file`. Otherwise this parameter has the value `NULL`.|
        -+||**`path`** - This is an output parameter used to store the full name of the file including the path to store an incoming file at the computer. This parameter must be provided by the application to Civetweb when a form field of type `file` is found. Please not that together with setting this parameter, the callback function must return `MG_FORM_FIELD_STORAGE_STORE`.i With any other return value the contents of the `path` buffer is ignored by Civetweb.|
        - ||**`pathlen`** - The length of the buffer where the output path can be stored.|
        - ||**`user_data`** - A pointer to the value of the field `user_data` of the structure `struct mg_form_data_handler`.|
        - ||The callback function `field_found()` can return the following values back to Civetweb:|
        --||**`FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field|
        --||**`FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data|
        --||**`FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists|
        --||**`FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields|
        -+||**`MG_FORM_FIELD_STORAGE_SKIP`** - Ignore the field and continue with processing the next field|
        -+||**`MG_FORM_FIELD_STORAGE_GET`** - Call the callback function `field_get()` to receive the form data|
        -+||**`MG_FORM_FIELD_STORAGE_STORE`** - Store a file as `path` and overwrite that file if it already exists|
        -+||**`MG_FORM_FIELD_STORAGE_ABORT`** - Stop parsing the request and ignore all remaining form fields|
        - |**`field_get`**|**`int field_get( const char *key, const char *value, size_t valuelen, void *user_data );`**|
        - ||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_GET`, Civetweb will call `field_get()` one or more times to pass back the data for this field.|
        - ||**`key`** - the name of the field being decoded, note this is only passed on the first call for file parameters (bug?)|
        -@@ -28,7 +28,7 @@
        - ||**`return` `MG_FORM_FIELD_HANDLE_NEXT`** - to skip further calls to get for this field and move on to the next field. |
        - ||**`return` `MG_FORM_FIELD_HANDLE_ABORT`** - to stop parsing this form and abandon the search for further fields. |
        - |**`field_store`**|**`int field_store( const char *path, long long file_size, void *user_data );`**|
        --||If the callback function `field_found()` returned `FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call|
        -+||If the callback function `field_found()` returned `MG_FORM_FIELD_STORAGE_STORE`, Civetweb will try to store the received data in a file. If writing the file is successful, the callback function `field_store()` is called. This function is only called after completion of a full upload, not if a file has only partly been uploaded. When only part of a file is received, Civetweb will delete that partly upload in the background and not inform the main application through this callback. The following parameters are provided in the function call|
        - ||**`path`** - The path on the server where the file was stored|
        - ||**`file_size`** - The size of the stored file in bytes|
        - ||**`user_data`** - The value of the field `user_data` when the callback functions were registered with a call to `mg_handle_form_request();`|
        -diff --git a/docs/api/mg_get_cookie.md b/docs/api/mg_get_cookie.md
        -index a738bc07..637caeee 100644
        ---- a/docs/api/mg_get_cookie.md
        -+++ b/docs/api/mg_get_cookie.md
        -@@ -6,8 +6,8 @@
        - 
        - | Parameter | Type | Description |
        - | :--- | :--- | :--- |
        --|**`cookie`**|`const char *`|The cookie name|
        --|**`var_name`**|`const char *`|The variable name|
        -+|**`cookie`**|`const char *`|The unparsed cookie header|
        -+|**`var_name`**|`const char *`|The cookie name|
        - |**`buf`**|`char *`|The buffer where to store the contents of the cookie|
        - |**`buf_len`**|`size_t`|The length of the cookie buffer, including the terminating NUL|
        - 
        -diff --git a/docs/api/mg_request_info.md b/docs/api/mg_request_info.md
        -index 0f9d720d..b7e0b6e1 100644
        ---- a/docs/api/mg_request_info.md
        -+++ b/docs/api/mg_request_info.md
        -@@ -14,7 +14,7 @@
        - |**`http_version`**|`const char *`| The HTTP version as mentioned in the client request. This can be "1.0", "1.1", etc. |
        - |**`query_string`**|`const char *`| The HTTP query string, defined as URL part after the first '?' character, not including '?'. NULL if there is no '?'. |
        - |**`remote_user`**|`const char *`| The name of the authenticated remote user, or NULL if no authentication was used. Only used for HTTP (digest) authentication, not for cookie based authentication. |
        --|**`remote addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address.  Example: "127.0.0.1" |
        -+|**`remote_addr`**|`char[48]`| The IP address of the remote client as a string. This can either represent an IPv4 or an IPv6 address.  Example: "127.0.0.1" |
        - |~~`remote_ip`~~|`long`| *Deprecated. Use* `remote_addr` *instead* |
        - |**`content_length`**|`long long`| The content length of the request body. This value can be -1 if no content length was provided. The request may still have body data, but the server cannot determine the length until all data has arrived (e.g. when the client closes the connection, or the final chunk of a chunked request has been received). |
        - |**`remote_port`**|`int`| The port number at the client side (an integer number between 1 and 65535). |
        -diff --git a/docs/api/mg_server_port.md b/docs/api/mg_server_port.md
        -index 072b2979..0159be8d 100644
        ---- a/docs/api/mg_server_port.md
        -+++ b/docs/api/mg_server_port.md
        -@@ -10,8 +10,8 @@
        - |**`port`**|`int`|The port number on which the service listens|
        - |**`is_ssl`**|`int`|**0** for `HTTP` communication, **1** for `HTTPS`|
        - |**`is_redirect`**|`int`|**1** if all requests are redirected, otherwise **0**|
        --|**`_reserved1`**|`int`|Reserved for internal use|
        --|**`_reserved2`**|`int`|Reserved for internal use|
        -+|**`is_optional`**|`int`|**1** if port is optional, otherwise **0**|
        -+|**`is_bound`**|`int`|**1** if the port is bound, otherwise **0**|
        - |**`_reserved3`**|`int`|Reserved for internal use|
        - |**`_reserved4`**|`int`|Reserved for internal use|
        - 
        -diff --git a/docs/gnutls.md b/docs/gnutls.md
        -new file mode 100644
        -index 00000000..0c4ded53
        ---- /dev/null
        -+++ b/docs/gnutls.md
        -@@ -0,0 +1,19 @@
        -+# Use GnuTLS instead of OpenSSL
        -+
        -+1 [Build libgmp](https://gmplib.org)
        -+
        -+ - 1.1 [Download source](https://gmplib.org/#DOWNLOAD)
        -+ - 1.2 ./configure && make && make install
        -+
        -+2 [Build libhogweed and libnettle](https://www.lysator.liu.se/~nisse/nettle/)
        -+
        -+ - 2.1 [Download source](https://ftp.gnu.org/gnu/nettle/)
        -+ - 2.2 ./configure && make && make install
        -+
        -+3 Build civetweb
        -+
        -+ - make build WITH_GNUTLS=1
        -+
        -+4 Run civetweb
        -+ - export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH
        -+ - ./civetweb  -listening_ports 8443s  -ssl_certificate resources/cert/server.pem  -document_root ./test/htmldir/
        -diff --git a/examples/https/civetweb.conf b/examples/https/civetweb.conf
        -index c1ecc642..552a565f 100644
        ---- a/examples/https/civetweb.conf
        -+++ b/examples/https/civetweb.conf
        -@@ -36,6 +36,9 @@ listening_ports 80r,443s
        - #document_root tdb
        - #authentication_domain mydomain.com
        - 
        -+# Optional fallback document root, checked for file-paths not found in document_root
        -+#fallback_document_root tdb_fallback
        -+
        - # Set the a certificate
        - ssl_certificate ../../resources/cert/server.pem
        - 
        -diff --git a/examples/linux_ws_server_cpp/CMakeLists.txt b/examples/linux_ws_server_cpp/CMakeLists.txt
        -index 6a71798e..cf73fc9b 100644
        ---- a/examples/linux_ws_server_cpp/CMakeLists.txt
        -+++ b/examples/linux_ws_server_cpp/CMakeLists.txt
        -@@ -1,4 +1,6 @@
        --cmake_minimum_required(VERSION 3.5.1)
        -+cmake_minimum_required(VERSION 3.10)
        -+cmake_policy(VERSION 3.10)
        -+
        - project(linux_ws_server)
        - 
        - set(TARGET_NAME ${PROJECT_NAME})
        -diff --git a/format.bat b/format.bat
        -index 203b0446..3bb94e65 100755
        ---- a/format.bat
        -+++ b/format.bat
        -@@ -17,6 +17,7 @@ clang-format -i src/handle_form.inl
        - clang-format -i src/response.inl
        - clang-format -i src/http2.inl
        - clang-format -i src/mod_mbedtls.inl
        -+clang-format -i src/mod_gnutls.inl
        - 
        - clang-format -i src/third_party/civetweb_lua.h
        - 
        -diff --git a/fuzztest/fuzzmain.c b/fuzztest/fuzzmain.c
        -index e8226de2..c7e27943 100644
        ---- a/fuzztest/fuzzmain.c
        -+++ b/fuzztest/fuzzmain.c
        -@@ -45,9 +45,6 @@ unsigned short PORT_NUM_HTTP = 0; /* set dynamically */
        - 	}
        - 
        - 
        --static uint64_t call_count = 0;
        --
        --
        - /********************************************************/
        - /* Init CivetWeb server ... test with mock client       */
        - /********************************************************/
        -@@ -110,6 +107,18 @@ civetweb_init(void)
        - 	atexit(civetweb_exit);
        - }
        - 
        -+int LLVMFuzzerInitialize(int *argc, char ***argv);
        -+
        -+int
        -+LLVMFuzzerInitialize(int *argc, char ***argv)
        -+{
        -+	// Silence unused args warning.
        -+	(void)(argc);
        -+	(void)(argv);
        -+
        -+	civetweb_init();
        -+	return 0;
        -+}
        - 
        - #if defined(TEST_FUZZ1)
        - static int
        -@@ -202,19 +211,12 @@ test_civetweb_client(const char *server,
        - 	return 0;
        - }
        - 
        --
        - static int
        - LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
        - {
        - 	static char URI[1024 * 64]; /* static, to avoid stack overflow */
        - 
        --	if (call_count == 0) {
        --		memset(URI, 0, sizeof(URI));
        --		civetweb_init();
        --	}
        --	call_count++;
        --
        --	if (size < sizeof(URI)) {
        -+	if (size + 1 < sizeof(URI)) {
        - 		memcpy(URI, data, size);
        - 		URI[size] = 0;
        - 	} else {
        -@@ -230,11 +232,6 @@ LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
        - static int
        - LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
        - {
        --	if (call_count == 0) {
        --		civetweb_init();
        --	}
        --	call_count++;
        --
        - 	int r;
        - 	SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);
        - 	if (sock == -1) {
        -@@ -446,15 +443,23 @@ mock_server_init(void)
        - 	atexit(mock_server_exit);
        - }
        - 
        -+int LLVMFuzzerInitialize(int *argc, char ***argv);
        -+
        -+int
        -+LLVMFuzzerInitialize(int *argc, char ***argv)
        -+{
        -+	// Silence unused args warning.
        -+	(void)(argc);
        -+	(void)(argv);
        -+
        -+	mock_server_init();
        -+	return 0;
        -+}
        -+
        - 
        - static int
        - LLVMFuzzerTestOneInput_RESPONSE(const uint8_t *data, size_t size)
        - {
        --	if (call_count == 0) {
        --		mock_server_init();
        --	}
        --	call_count++;
        --
        - 	if (size > sizeof(RESPONSE.data)) {
        - 		return 1;
        - 	}
        -@@ -497,21 +502,26 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
        - {
        - #if defined(TEST_FUZZ1)
        - 	/* fuzz target 1: different URI for HTTP/1 server */
        --	return LLVMFuzzerTestOneInput_URI(data, size);
        -+	LLVMFuzzerTestOneInput_URI(data, size);
        -+	return 0;
        - #elif defined(TEST_FUZZ2)
        - 	/* fuzz target 2: different requests for HTTP/1 server */
        --	return LLVMFuzzerTestOneInput_REQUEST(data, size);
        -+	LLVMFuzzerTestOneInput_REQUEST(data, size);
        -+	return 0;
        - #elif defined(TEST_FUZZ3)
        - 	/* fuzz target 3: different responses for HTTP/1 client */
        --	return LLVMFuzzerTestOneInput_RESPONSE(data, size);
        -+	LLVMFuzzerTestOneInput_RESPONSE(data, size);
        -+	return 0;
        - #elif defined(TEST_FUZZ4)
        - #error "Only useful in HTTP/2 feature branch"
        - 	/* fuzz target 4: different requests for HTTP/2 server */
        --	return LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
        -+	LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
        -+	return 0;
        - #elif defined(TEST_FUZZ5)
        - 	/* fuzz target 5: calling an internal server test function,
        - 	 *                bypassing network sockets */
        --	return LLVMFuzzerTestOneInput_process_new_connection(data, size);
        -+	LLVMFuzzerTestOneInput_process_new_connection(data, size);
        -+	return 0;
        - #else
        - /* planned targets */
        - #error "Unknown fuzz target"
        -diff --git a/include/civetweb.h b/include/civetweb.h
        -index 167c6a76..f585df25 100644
        ---- a/include/civetweb.h
        -+++ b/include/civetweb.h
        -@@ -1,4 +1,4 @@
        --/* Copyright (c) 2013-2021 the Civetweb developers
        -+/* Copyright (c) 2013-2024 the Civetweb developers
        -  * Copyright (c) 2004-2013 Sergey Lyubka
        -  *
        -  * Permission is hereby granted, free of charge, to any person obtaining a copy
        -@@ -23,9 +23,9 @@
        - #ifndef CIVETWEB_HEADER_INCLUDED
        - #define CIVETWEB_HEADER_INCLUDED
        - 
        --#define CIVETWEB_VERSION "1.16"
        -+#define CIVETWEB_VERSION "1.17"
        - #define CIVETWEB_VERSION_MAJOR (1)
        --#define CIVETWEB_VERSION_MINOR (16)
        -+#define CIVETWEB_VERSION_MINOR (17)
        - #define CIVETWEB_VERSION_PATCH (0)
        - 
        - #ifndef CIVETWEB_API
        -@@ -164,6 +164,7 @@ struct mg_request_info {
        - 	const char *remote_user;    /* Authenticated user, or NULL if no auth
        - 	                               used */
        - 	char remote_addr[48];       /* Client's IP address as a string. */
        -+	char server_addr[48];       /* Server's IP address as a string. */
        - 
        - 	long long content_length; /* Length (in bytes) of the request body,
        - 	                             can be -1 if no length was given. */
        -@@ -714,8 +715,8 @@ struct mg_server_port {
        - 	int port;        /* port number */
        - 	int is_ssl;      /* https port: 0 = no, 1 = yes */
        - 	int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
        --	int _reserved1;
        --	int _reserved2;
        -+	int is_optional; /* optional: 0 = no, 1 = yes */
        -+	int is_bound;    /* bound: 0 = no, 1 = yes, relevant for optional ports */
        - 	int _reserved3;
        - 	int _reserved4;
        - };
        -@@ -1208,7 +1209,7 @@ struct mg_form_data_handler {
        - 	 *   filename: Name of a file to upload, at the client computer.
        - 	 *             Only set for input fields of type "file", otherwise NULL.
        - 	 *   path: Output parameter: File name (incl. path) to store the file
        --	 *         at the server computer. Only used if FORM_FIELD_STORAGE_STORE
        -+	 *         at the server computer. Only used if MG_FORM_FIELD_STORAGE_STORE
        - 	 *         is returned by this callback. Existing files will be
        - 	 *         overwritten.
        - 	 *   pathlen: Length of the buffer for path.
        -@@ -1216,7 +1217,7 @@ struct mg_form_data_handler {
        - 	 *
        - 	 * Return value:
        - 	 *   The callback must return the intended storage for this field
        --	 *   (See FORM_FIELD_STORAGE_*).
        -+	 *   (See MG_FORM_FIELD_STORAGE_*).
        - 	 */
        - 	int (*field_found)(const char *key,
        - 	                   const char *filename,
        -@@ -1224,7 +1225,7 @@ struct mg_form_data_handler {
        - 	                   size_t pathlen,
        - 	                   void *user_data);
        - 
        --	/* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
        -+	/* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_GET,
        - 	 * this callback will receive the field data.
        - 	 *
        - 	 * Parameters:
        -@@ -1241,7 +1242,7 @@ struct mg_form_data_handler {
        - 	                 size_t valuelen,
        - 	                 void *user_data);
        - 
        --	/* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
        -+	/* If the "field_found" callback returned MG_FORM_FIELD_STORAGE_STORE,
        - 	 * the data will be stored into a file. If the file has been written
        - 	 * successfully, this callback will be called. This callback will
        - 	 * not be called for only partially uploaded files. The
        -diff --git a/resources/Makefile.in-lua b/resources/Makefile.in-lua
        -index d3038f66..8afff851 100644
        ---- a/resources/Makefile.in-lua
        -+++ b/resources/Makefile.in-lua
        -@@ -39,7 +39,14 @@ ifeq ($(WITH_LUA_VERSION), 504)
        -   $(info Lua: Using version 5.4.3)
        -   LUA_DIR = src/third_party/lua-5.4.3/src
        -   LUA_SHARED_LIB_FLAG = -llua5.4
        --  LUA_CFLAGS = -DLUA_COMPAT_5_2 -DLUA_VERSION_MAKEFILE=504
        -+  LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=504
        -+  LUA_VERSION_KNOWN = 1
        -+endif
        -+ifeq ($(WITH_LUA_VERSION), 505)
        -+  $(info Lua: Using version 5.5.0-beta)
        -+  LUA_DIR = src/third_party/lua-5.5.0-beta/src
        -+  LUA_SHARED_LIB_FLAG = -llua5.5
        -+  LUA_CFLAGS = -DLUA_VERSION_MAKEFILE=505
        -   LUA_VERSION_KNOWN = 1
        - endif
        - 
        -@@ -112,6 +119,12 @@ ifeq ($(WITH_LUA_VERSION), 504)
        -     lctype.c  \
        -     lutf8lib.c
        - endif
        -+ifeq ($(WITH_LUA_VERSION), 505)
        -+    LUA_SOURCE_FILES += \
        -+    lcorolib.c  \
        -+    lctype.c  \
        -+    lutf8lib.c
        -+endif
        - 
        -   $(info Lua: using static library)
        - 
        -@@ -150,6 +163,7 @@ CFLAGS += -DUSE_LUA_FILE_SYSTEM
        - #SOURCE_DIRS = $(LFS_DIR)
        - 
        - 
        -+ifneq ($(WITH_LUA_VERSION), 501)
        - LXX_DIR = src/third_party
        - LXX_SOURCE_FILES = lua_struct.c
        - LXX_SOURCES = $(addprefix $(LXX_DIR)/, $(LXX_SOURCE_FILES))
        -@@ -159,6 +173,7 @@ OBJECTS += $(LXX_OBJECTS)
        - CFLAGS += $(LXX_CFLAGS)
        - CFLAGS += -DUSE_LUA_STRUCT
        - #SOURCE_DIRS = $(LXX_DIR)
        -+endif
        - 
        - 
        - ifneq ($(WITH_LUA_VERSION), 501)
        -diff --git a/resources/civetweb.conf b/resources/civetweb.conf
        -index c5675481..293f27b5 100644
        ---- a/resources/civetweb.conf
        -+++ b/resources/civetweb.conf
        -@@ -26,6 +26,7 @@ listening_ports 8080
        - # extra_mime_types 
        - # ssl_certificate 
        - # num_threads 50
        -+# prespawn_threads 0
        - # run_as_user 
        - # url_rewrite_patterns 
        - # hide_files_patterns 
        -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
        -index 43f3a77e..de4c2d62 100644
        ---- a/src/CMakeLists.txt
        -+++ b/src/CMakeLists.txt
        -@@ -1,3 +1,6 @@
        -+cmake_minimum_required(VERSION 3.10)
        -+cmake_policy(VERSION 3.10)
        -+
        - # The C API library
        - set( LIB_TYPE "STATIC" )
        - if (BUILD_SHARED_LIBS)
        -@@ -47,16 +50,28 @@ endif()
        - 
        - # We need to link OpenSSL if not dynamically loading
        - if (CIVETWEB_ENABLE_SSL)
        --  if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        --    find_package(LibDl)
        --    if (LIBDL_FOUND)
        --      target_link_libraries(civetweb-c-library -ldl)
        --    endif()
        -+  if (CIVETWEB_ENABLE_GNUTLS)
        -+    find_package(GnuTLS)
        -+    include_directories(${GNUTLS_INCLUDE_DIR})
        -+    message(STATUS "GnuTLS include directory: ${GNUTLS_INCLUDE_DIR}")
        -+    target_link_libraries(civetweb-c-library ${GNUTLS_LIBRARIES})
        -+  elseif (CIVETWEB_ENABLE_MBEDTLS)
        -+    find_package(MbedTLS)
        -+    include_directories(${MbedTLS_INCLUDE_DIR})
        -+    message(STATUS "MbedTLS include directory: ${MbedTLS_INCLUDE_DIR}")
        -+    target_link_libraries(civetweb-c-library ${MbedTLS_LIBRARIES})
        -   else()
        --    find_package(OpenSSL)
        --    include_directories(${OPENSSL_INCLUDE_DIR})
        --    message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
        --    target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
        -+    if (CIVETWEB_ENABLE_SSL_DYNAMIC_LOADING)
        -+      find_package(LibDl)
        -+      if (LIBDL_FOUND)
        -+        target_link_libraries(civetweb-c-library -ldl)
        -+      endif()
        -+    else()
        -+      find_package(OpenSSL)
        -+      include_directories(${OPENSSL_INCLUDE_DIR})
        -+      message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
        -+      target_link_libraries(civetweb-c-library ${OPENSSL_LIBRARIES})
        -+    endif()
        -   endif()
        - endif()
        - 
        -diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp
        -index fafd6e18..2bcf5c31 100644
        ---- a/src/CivetServer.cpp
        -+++ b/src/CivetServer.cpp
        -@@ -406,12 +406,12 @@ CivetServer::CivetServer(const char **options,
        - 		userCloseHandler = NULL;
        - 	}
        - 	callbacks.connection_close = closeHandler;
        --	struct mg_init_data mg_start_init_data = {0};
        -+	struct mg_init_data mg_start_init_data = {};
        - 	mg_start_init_data.callbacks = &callbacks;
        - 	mg_start_init_data.user_data = this;
        - 	mg_start_init_data.configuration_options = options;
        - 
        --	struct mg_error_data mg_start_error_data = {0};
        -+	struct mg_error_data mg_start_error_data = {};
        - 	char errtxtbuf[256] = {0};
        - 	mg_start_error_data.text = errtxtbuf;
        - 	mg_start_error_data.text_buffer_size = sizeof(errtxtbuf);
        -@@ -450,12 +450,12 @@ CivetServer::CivetServer(const std::vector &options,
        - 	}
        - 	pointers.back() = NULL;
        - 
        --	struct mg_init_data mg_start_init_data = {0};
        -+	struct mg_init_data mg_start_init_data = {};
        - 	mg_start_init_data.callbacks = &callbacks;
        - 	mg_start_init_data.user_data = this;
        - 	mg_start_init_data.configuration_options = &pointers[0];
        - 
        --	struct mg_error_data mg_start_error_data = {0};
        -+	struct mg_error_data mg_start_error_data = {};
        - 	char errtxtbuf[256] = {0};
        - 	mg_start_error_data.text = errtxtbuf;
        - 	mg_start_error_data.text_buffer_size = sizeof(errtxtbuf);
        -diff --git a/src/civetweb.c b/src/civetweb.c
        -index 9e321edf..f6a60e21 100644
        ---- a/src/civetweb.c
        -+++ b/src/civetweb.c
        -@@ -1,4 +1,4 @@
        --/* Copyright (c) 2013-2021 the Civetweb developers
        -+/* Copyright (c) 2013-2025 the Civetweb developers
        -  * Copyright (c) 2004-2013 Sergey Lyubka
        -  *
        -  * Permission is hereby granted, free of charge, to any person obtaining a copy
        -@@ -51,8 +51,8 @@
        - #if !defined(_CRT_SECURE_NO_WARNINGS)
        - #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
        - #endif
        --#if !defined(_WIN32_WINNT) /* defined for tdm-gcc so we can use getnameinfo */
        --#define _WIN32_WINNT 0x0502
        -+#if !defined(_WIN32_WINNT) /* Minimum API version */
        -+#define _WIN32_WINNT 0x0601
        - #endif
        - #else
        - #if !defined(_GNU_SOURCE)
        -@@ -161,7 +161,7 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
        -  * file system.
        -  * NO_FILES only disables the automatic mapping between URLs and local
        -  * file names.
        -- * NO_FILESYSTEM = do not access any file at all. Useful for embedded
        -+ * NO_FILESYSTEMS = do not access any file at all. Useful for embedded
        -  * devices without file system. Logging to files in not available
        -  * (use callbacks instead) and API functions like mg_send_file are not
        -  * available.
        -@@ -183,6 +183,10 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
        - #error "Symbian is no longer maintained. CivetWeb no longer supports Symbian."
        - #endif /* __SYMBIAN32__ */
        - 
        -+#if defined(__rtems__)
        -+#include 
        -+#endif
        -+
        - #if defined(__ZEPHYR__)
        - #include 
        - #include 
        -@@ -235,7 +239,7 @@ static void DEBUG_TRACE_FUNC(const char *func,
        - 
        - #define NEED_DEBUG_TRACE_FUNC
        - #if !defined(DEBUG_TRACE_STREAM)
        --#define DEBUG_TRACE_STREAM stdout
        -+#define DEBUG_TRACE_STREAM stderr
        - #endif
        - 
        - #else
        -@@ -885,7 +889,9 @@ typedef unsigned short int in_port_t;
        - #include 
        - #include 
        - #include 
        -+#if !defined(__rtems__)
        - #include 
        -+#endif
        - #include 
        - #include 
        - #include 
        -@@ -901,8 +907,22 @@ typedef unsigned short int in_port_t;
        - #endif
        - 
        - #if defined(__MACH__) && defined(__APPLE__)
        --#define SSL_LIB "libssl.dylib"
        --#define CRYPTO_LIB "libcrypto.dylib"
        -+
        -+#if defined(OPENSSL_API_3_0)
        -+#define SSL_LIB "libssl.3.dylib"
        -+#define CRYPTO_LIB "libcrypto.3.dylib"
        -+#endif
        -+
        -+#if defined(OPENSSL_API_1_1)
        -+#define SSL_LIB "libssl.1.1.dylib"
        -+#define CRYPTO_LIB "libcrypto.1.1.dylib"
        -+#endif /* OPENSSL_API_1_1 */
        -+
        -+#if defined(OPENSSL_API_1_0)
        -+#define SSL_LIB "libssl.1.0.dylib"
        -+#define CRYPTO_LIB "libcrypto.1.0.dylib"
        -+#endif /* OPENSSL_API_1_0 */
        -+
        - #else
        - #if !defined(SSL_LIB)
        - #define SSL_LIB "libssl.so"
        -@@ -963,7 +983,7 @@ count_leap(int y)
        - 	return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
        - }
        - 
        --time_t
        -+static time_t
        - timegm(struct tm *tm)
        - {
        - 	static const unsigned short ydays[] = {
        -@@ -1125,7 +1145,15 @@ mg_atomic_inc(volatile ptrdiff_t *addr)
        - #if defined(_WIN64) && !defined(NO_ATOMICS)
        - 	ret = InterlockedIncrement64(addr);
        - #elif defined(_WIN32) && !defined(NO_ATOMICS)
        -+#ifdef __cplusplus
        -+	/* For C++ the Microsoft Visual Studio compiler can not decide what
        -+	 * overloaded function prototpye in the SDC corresponds to "ptrdiff_t". */
        -+	static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
        -+	static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
        -+	ret = InterlockedIncrement((LONG *)addr);
        -+#else
        - 	ret = InterlockedIncrement(addr);
        -+#endif
        - #elif defined(__GNUC__)                                                        \
        -     && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
        -     && !defined(NO_ATOMICS)
        -@@ -1148,7 +1176,14 @@ mg_atomic_dec(volatile ptrdiff_t *addr)
        - #if defined(_WIN64) && !defined(NO_ATOMICS)
        - 	ret = InterlockedDecrement64(addr);
        - #elif defined(_WIN32) && !defined(NO_ATOMICS)
        -+#ifdef __cplusplus
        -+	/* see mg_atomic_inc */
        -+	static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
        -+	static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
        -+	ret = InterlockedDecrement((LONG *)addr);
        -+#else
        - 	ret = InterlockedDecrement(addr);
        -+#endif
        - #elif defined(__GNUC__)                                                        \
        -     && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
        -     && !defined(NO_ATOMICS)
        -@@ -1307,13 +1342,13 @@ mg_malloc_ex(size_t size,
        - #endif
        - 
        - 	if (data) {
        -+		uintptr_t *tmp = (uintptr_t *)data;
        - 		ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size);
        - 		mg_atomic_max(&mstat->maxMemUsed, mmem);
        --
        - 		mg_atomic_inc(&mstat->blockCount);
        --		((uintptr_t *)data)[0] = size;
        --		((uintptr_t *)data)[1] = (uintptr_t)mstat;
        --		memory = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
        -+		tmp[0] = size;
        -+		tmp[1] = (uintptr_t)mstat;
        -+		memory = (void *)&tmp[2];
        - 	}
        - 
        - #if defined(MEMORY_DEBUGGING)
        -@@ -1536,11 +1571,13 @@ static void mg_snprintf(const struct mg_connection *conn,
        - #if defined(vsnprintf)
        - #undef vsnprintf
        - #endif
        -+#if !defined(NDEBUG)
        - #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
        - #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
        - #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
        - #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
        - #define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
        -+#endif
        - #if defined(_WIN32)
        - /* vsnprintf must not be used in any system,
        -  * but this define only works well for Windows. */
        -@@ -1557,8 +1594,9 @@ static int mg_init_library_called = 0;
        - static int mg_openssl_initialized = 0;
        - #endif
        - #if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1)                     \
        --    && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)
        --#error "Please define OPENSSL_API_#_# or USE_MBEDTLS"
        -+    && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)                      \
        -+    && !defined(USE_GNUTLS)
        -+#error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS"
        - #endif
        - #if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
        - #error "Multiple OPENSSL_API versions defined"
        -@@ -1571,7 +1609,10 @@ static int mg_openssl_initialized = 0;
        - #endif
        - #if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)                      \
        -      || defined(OPENSSL_API_3_0))                                              \
        --    && defined(USE_MBEDTLS)
        -+    && (defined(USE_MBEDTLS) || defined(USE_GNUTLS))
        -+#error "Multiple SSL libraries defined"
        -+#endif
        -+#if defined(USE_MBEDTLS) && defined(USE_GNUTLS)
        - #error "Multiple SSL libraries defined"
        - #endif
        - #endif
        -@@ -1736,11 +1777,15 @@ typedef int socklen_t;
        - #endif
        - 
        - 
        --/* SSL: mbedTLS vs. no-ssl vs. OpenSSL */
        -+/* SSL: mbedTLS vs. GnuTLS vs. no-ssl vs. OpenSSL */
        - #if defined(USE_MBEDTLS)
        - /* mbedTLS */
        - #include "mod_mbedtls.inl"
        - 
        -+#elif defined(USE_GNUTLS)
        -+/* GnuTLS */
        -+#include "mod_gnutls.inl"
        -+
        - #elif defined(NO_SSL)
        - /* no SSL */
        - typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
        -@@ -1906,7 +1951,9 @@ struct socket {
        - 	unsigned char is_ssl;    /* Is port SSL-ed */
        - 	unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
        - 	                          * port */
        --	unsigned char in_use;    /* 0: invalid, 1: valid, 2: free */
        -+	unsigned char
        -+	    is_optional; /* Shouldn't cause us to exit if we can't bind to it */
        -+	unsigned char in_use; /* 0: invalid, 1: valid, 2: free */
        - };
        - 
        - 
        -@@ -1919,6 +1966,7 @@ enum {
        - 	/* Once for each server */
        - 	LISTENING_PORTS,
        - 	NUM_THREADS,
        -+	PRESPAWN_THREADS,
        - 	RUN_AS_USER,
        - 	CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
        - 	                     * socket option typedef TCP_NODELAY. */
        -@@ -1952,6 +2000,7 @@ enum {
        - 
        - 	/* Once for each domain */
        - 	DOCUMENT_ROOT,
        -+	FALLBACK_DOCUMENT_ROOT,
        - 
        - 	ACCESS_LOG_FILE,
        - 	ERROR_LOG_FILE,
        -@@ -2033,14 +2082,17 @@ enum {
        - 
        - #if defined(USE_WEBSOCKET)
        - 	WEBSOCKET_ROOT,
        -+	FALLBACK_WEBSOCKET_ROOT,
        - #endif
        - #if defined(USE_LUA) && defined(USE_WEBSOCKET)
        - 	LUA_WEBSOCKET_EXTENSIONS,
        - #endif
        --
        -+	REPLACE_ASTERISK_WITH_ORIGIN,
        - 	ACCESS_CONTROL_ALLOW_ORIGIN,
        - 	ACCESS_CONTROL_ALLOW_METHODS,
        - 	ACCESS_CONTROL_ALLOW_HEADERS,
        -+	ACCESS_CONTROL_EXPOSE_HEADERS,
        -+	ACCESS_CONTROL_ALLOW_CREDENTIALS,
        - 	ERROR_PAGES,
        - #if !defined(NO_CACHING)
        - 	STATIC_FILE_MAX_AGE,
        -@@ -2064,6 +2116,7 @@ static const struct mg_option config_options[] = {
        -     /* Once for each server */
        -     {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"},
        -     {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"},
        -+    {"prespawn_threads", MG_CONFIG_TYPE_NUMBER, "0"},
        -     {"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
        -     {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"},
        -     {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"},
        -@@ -2096,6 +2149,7 @@ static const struct mg_option config_options[] = {
        - 
        -     /* Once for each domain */
        -     {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        -+    {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        - 
        -     {"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
        -     {"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
        -@@ -2194,13 +2248,17 @@ static const struct mg_option config_options[] = {
        - 
        - #if defined(USE_WEBSOCKET)
        -     {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        -+    {"fallback_websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
        - #endif
        - #if defined(USE_LUA) && defined(USE_WEBSOCKET)
        -     {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
        - #endif
        -+    {"replace_asterisk_with_origin", MG_CONFIG_TYPE_BOOLEAN, "no"},
        -     {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
        -     {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
        -     {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
        -+    {"access_control_expose_headers", MG_CONFIG_TYPE_STRING, ""},
        -+    {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""},
        -     {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
        - #if !defined(NO_CACHING)
        -     {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"},
        -@@ -2310,9 +2368,11 @@ STOP_FLAG_IS_TWO(stop_flag_t *f)
        - static void
        - STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v)
        - {
        --	stop_flag_t sf;
        -+	stop_flag_t sf = 0;
        - 	do {
        --		sf = mg_atomic_compare_and_swap(f, *f, v);
        -+		sf = mg_atomic_compare_and_swap(f,
        -+		                                __atomic_load_n(f, __ATOMIC_SEQ_CST),
        -+		                                v);
        - 	} while (sf != v);
        - }
        - 
        -@@ -2373,10 +2433,18 @@ struct mg_context {
        - 	stop_flag_t stop_flag;        /* Should we stop event loop */
        - 	pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
        - 
        --	pthread_t masterthreadid; /* The master thread ID */
        -+	pthread_t masterthreadid;            /* The master thread ID */
        -+	unsigned int cfg_max_worker_threads; /* How many worker-threads we are
        -+	                                        allowed to create, total */
        -+
        -+	unsigned int spawned_worker_threads; /* How many worker-threads currently
        -+	                                        exist (modified by master thread) */
        - 	unsigned int
        --	    cfg_worker_threads;      /* The number of configured worker threads. */
        --	pthread_t *worker_threadids; /* The worker thread IDs */
        -+	    idle_worker_thread_count; /* How many worker-threads are currently
        -+	                                 sitting around with nothing to do */
        -+	/* Access to this value MUST be synchronized by thread_mutex */
        -+
        -+	pthread_t *worker_threadids;      /* The worker thread IDs */
        - 	unsigned long starter_thread_idx; /* thread index which called mg_start */
        - 
        - 	/* Connection to thread dispatching */
        -@@ -2423,6 +2491,11 @@ struct mg_context {
        - 	int lua_bg_log_available;     /* Use Lua background state for access log */
        - #endif
        - 
        -+	int user_shutdown_notification_socket;   /* mg_stop() will close this
        -+	                                            socket... */
        -+	int thread_shutdown_notification_socket; /* to cause poll() in all threads
        -+	                                            to return immediately */
        -+
        - 	/* Server nonce */
        - 	pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers,
        - 	                              * ssl_cert_last_mtime, nonce_count, and
        -@@ -2502,7 +2575,6 @@ struct mg_connection {
        - 	                 * versions. For the current definition, see
        - 	                 * mg_get_connection_info_impl */
        - #endif
        --
        - 	SSL *ssl;               /* SSL descriptor */
        - 	struct socket client;   /* Connected client */
        - 	time_t conn_birth_time; /* Time (wall clock) when connection was
        -@@ -3268,6 +3340,8 @@ mg_get_server_ports(const struct mg_context *ctx,
        - 		    ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
        - 		ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
        - 		ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
        -+		ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
        -+		ports[cnt].is_bound = ctx->listening_sockets[i].sock != INVALID_SOCKET;
        - 
        - 		if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
        - 			/* IPv4 */
        -@@ -3409,7 +3483,7 @@ mg_cry_internal_impl(const struct mg_connection *conn,
        - 	DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf);
        - 
        - 	if (!conn) {
        --		puts(buf);
        -+		fputs(buf, stderr);
        - 		return;
        - 	}
        - 
        -@@ -4139,6 +4213,8 @@ send_additional_header(struct mg_connection *conn)
        - 	}
        - #endif
        - 
        -+	// Content-Security-Policy
        -+
        - 	if (header && header[0]) {
        - 		mg_response_header_add_lines(conn, header);
        - 	}
        -@@ -4151,15 +4227,68 @@ send_cors_header(struct mg_connection *conn)
        - 	const char *origin_hdr = mg_get_header(conn, "Origin");
        - 	const char *cors_orig_cfg =
        - 	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
        -+	const char *cors_cred_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        -+	const char *cors_hdr_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
        -+	const char *cors_exphdr_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        -+	const char *cors_meth_cfg =
        -+	    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
        -+	const char *cors_repl_asterisk_with_orig_cfg =
        -+	    conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
        -+
        -+	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr
        -+	    && cors_repl_asterisk_with_orig_cfg
        -+	    && *cors_repl_asterisk_with_orig_cfg) {
        -+		int cors_repl_asterisk_with_orig =
        -+		    mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
        - 
        --	if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
        - 		/* Cross-origin resource sharing (CORS), see
        - 		 * http://www.html5rocks.com/en/tutorials/cors/,
        - 		 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
        - 		 * CORS preflight is not supported for files. */
        -+		if (cors_repl_asterisk_with_orig == 0 && cors_orig_cfg[0] == '*') {
        -+			mg_response_header_add(conn,
        -+			                       "Access-Control-Allow-Origin",
        -+			                       origin_hdr,
        -+			                       -1);
        -+		} else {
        -+			mg_response_header_add(conn,
        -+			                       "Access-Control-Allow-Origin",
        -+			                       cors_orig_cfg,
        -+			                       -1);
        -+		}
        -+	}
        -+
        -+	if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
        -+		/* Cross-origin resource sharing (CORS), see
        -+		 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
        -+		 */
        -+		mg_response_header_add(conn,
        -+		                       "Access-Control-Allow-Credentials",
        -+		                       cors_cred_cfg,
        -+		                       -1);
        -+	}
        -+
        -+	if (cors_hdr_cfg && *cors_hdr_cfg) {
        -+		mg_response_header_add(conn,
        -+		                       "Access-Control-Allow-Headers",
        -+		                       cors_hdr_cfg,
        -+		                       -1);
        -+	}
        -+
        -+	if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        - 		mg_response_header_add(conn,
        --		                       "Access-Control-Allow-Origin",
        --		                       cors_orig_cfg,
        -+		                       "Access-Control-Expose-Headers",
        -+		                       cors_exphdr_cfg,
        -+		                       -1);
        -+	}
        -+
        -+	if (cors_meth_cfg && *cors_meth_cfg) {
        -+		mg_response_header_add(conn,
        -+		                       "Access-Control-Allow-Methods",
        -+		                       cors_meth_cfg,
        - 		                       -1);
        - 	}
        - }
        -@@ -6028,7 +6157,7 @@ push_inner(struct mg_context *ctx,
        - 		return -2;
        - 	}
        - 
        --#if defined(NO_SSL) && !defined(USE_MBEDTLS)
        -+#if defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)
        - 	if (ssl) {
        - 		return -2;
        - 	}
        -@@ -6054,6 +6183,19 @@ push_inner(struct mg_context *ctx,
        - 				err = 0;
        - 			}
        - 		} else
        -+#elif defined(USE_GNUTLS)
        -+		if (ssl != NULL) {
        -+			n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t)len);
        -+			if (n < 0) {
        -+				fprintf(stderr,
        -+				        "SSL write failed (%d): %s",
        -+				        n,
        -+				        gnutls_strerror(n));
        -+				return -2;
        -+			} else {
        -+				err = 0;
        -+			}
        -+		} else
        - #elif !defined(NO_SSL)
        - 		if (ssl != NULL) {
        - 			ERR_clear_error();
        -@@ -6128,12 +6270,20 @@ push_inner(struct mg_context *ctx,
        - 			mg_sleep(5);
        - 		} else {
        - 			/* For sockets, wait for the socket using poll */
        --			struct mg_pollfd pfd[1];
        -+			struct mg_pollfd pfd[2];
        - 			int pollres;
        -+			unsigned int num_sock = 1;
        - 
        - 			pfd[0].fd = sock;
        - 			pfd[0].events = POLLOUT;
        --			pollres = mg_poll(pfd, 1, (int)(ms_wait), &(ctx->stop_flag));
        -+
        -+			if (ctx->context_type == CONTEXT_SERVER) {
        -+				pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
        -+				pfd[num_sock].events = POLLIN;
        -+				num_sock++;
        -+			}
        -+
        -+			pollres = mg_poll(pfd, num_sock, (int)(ms_wait), &(ctx->stop_flag));
        - 			if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
        - 				return -2;
        - 			}
        -@@ -6241,9 +6391,10 @@ pull_inner(FILE *fp,
        - 
        - #if defined(USE_MBEDTLS)
        - 	} else if (conn->ssl != NULL) {
        --		struct mg_pollfd pfd[1];
        -+		struct mg_pollfd pfd[2];
        - 		int to_read;
        - 		int pollres;
        -+		unsigned int num_sock = 1;
        - 
        - 		to_read = mbedtls_ssl_get_bytes_avail(conn->ssl);
        - 
        -@@ -6259,10 +6410,17 @@ pull_inner(FILE *fp,
        - 			pfd[0].fd = conn->client.sock;
        - 			pfd[0].events = POLLIN;
        - 
        -+			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+				pfd[num_sock].fd =
        -+				    conn->phys_ctx->thread_shutdown_notification_socket;
        -+				pfd[num_sock].events = POLLIN;
        -+				num_sock++;
        -+			}
        -+
        - 			to_read = len;
        - 
        - 			pollres = mg_poll(pfd,
        --			                  1,
        -+			                  num_sock,
        - 			                  (int)(timeout * 1000.0),
        - 			                  &(conn->phys_ctx->stop_flag));
        - 
        -@@ -6294,11 +6452,71 @@ pull_inner(FILE *fp,
        - 			nread = 0;
        - 		}
        - 
        -+#elif defined(USE_GNUTLS)
        -+	} else if (conn->ssl != NULL) {
        -+		struct mg_pollfd pfd[2];
        -+		size_t to_read;
        -+		int pollres;
        -+		unsigned int num_sock = 1;
        -+
        -+		to_read = gnutls_record_check_pending(conn->ssl->sess);
        -+
        -+		if (to_read > 0) {
        -+			/* We already know there is no more data buffered in conn->buf
        -+			 * but there is more available in the SSL layer. So don't poll
        -+			 * conn->client.sock yet. */
        -+
        -+			pollres = 1;
        -+			if (to_read > (size_t)len)
        -+				to_read = (size_t)len;
        -+		} else {
        -+			pfd[0].fd = conn->client.sock;
        -+			pfd[0].events = POLLIN;
        -+
        -+			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+				pfd[num_sock].fd =
        -+				    conn->phys_ctx->thread_shutdown_notification_socket;
        -+				pfd[num_sock].events = POLLIN;
        -+				num_sock++;
        -+			}
        -+
        -+			to_read = (size_t)len;
        -+
        -+			pollres = mg_poll(pfd,
        -+			                  num_sock,
        -+			                  (int)(timeout * 1000.0),
        -+			                  &(conn->phys_ctx->stop_flag));
        -+
        -+			if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        -+				return -2;
        -+			}
        -+		}
        -+
        -+		if (pollres > 0) {
        -+			nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
        -+			if (nread < 0) {
        -+				fprintf(stderr,
        -+				        "SSL read failed (%d): %s",
        -+				        nread,
        -+				        gnutls_strerror(nread));
        -+				return -2;
        -+			} else {
        -+				err = 0;
        -+			}
        -+		} else if (pollres < 0) {
        -+			/* Error */
        -+			return -2;
        -+		} else {
        -+			/* pollres = 0 means timeout */
        -+			nread = 0;
        -+		}
        -+
        - #elif !defined(NO_SSL)
        - 	} else if (conn->ssl != NULL) {
        - 		int ssl_pending;
        --		struct mg_pollfd pfd[1];
        -+		struct mg_pollfd pfd[2];
        - 		int pollres;
        -+		unsigned int num_sock = 1;
        - 
        - 		if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
        - 			/* We already know there is no more data buffered in conn->buf
        -@@ -6311,8 +6529,16 @@ pull_inner(FILE *fp,
        - 		} else {
        - 			pfd[0].fd = conn->client.sock;
        - 			pfd[0].events = POLLIN;
        -+
        -+			if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+				pfd[num_sock].fd =
        -+				    conn->phys_ctx->thread_shutdown_notification_socket;
        -+				pfd[num_sock].events = POLLIN;
        -+				num_sock++;
        -+			}
        -+
        - 			pollres = mg_poll(pfd,
        --			                  1,
        -+			                  num_sock,
        - 			                  (int)(timeout * 1000.0),
        - 			                  &(conn->phys_ctx->stop_flag));
        - 			if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        -@@ -6350,13 +6576,22 @@ pull_inner(FILE *fp,
        - #endif
        - 
        - 	} else {
        --		struct mg_pollfd pfd[1];
        -+		struct mg_pollfd pfd[2];
        - 		int pollres;
        -+		unsigned int num_sock = 1;
        - 
        - 		pfd[0].fd = conn->client.sock;
        - 		pfd[0].events = POLLIN;
        -+
        -+		if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+			pfd[num_sock].fd =
        -+			    conn->phys_ctx->thread_shutdown_notification_socket;
        -+			pfd[num_sock].events = POLLIN;
        -+			num_sock++;
        -+		}
        -+
        - 		pollres = mg_poll(pfd,
        --		                  1,
        -+		                  num_sock,
        - 		                  (int)(timeout * 1000.0),
        - 		                  &(conn->phys_ctx->stop_flag));
        - 		if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        -@@ -6378,7 +6613,7 @@ pull_inner(FILE *fp,
        - 		}
        - 	}
        - 
        --	if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        -+	if (conn != NULL && !STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
        - 		return -2;
        - 	}
        - 
        -@@ -6402,7 +6637,7 @@ pull_inner(FILE *fp,
        - 			/* See https://www.chilkatsoft.com/p/p_299.asp */
        - 			return -2;
        - 		} else {
        --			DEBUG_TRACE("recv() failed, error %d", err);
        -+			DEBUG_TRACE("read()/recv() failed, error %d", err);
        - 			return -2;
        - 		}
        - #else
        -@@ -6424,7 +6659,7 @@ pull_inner(FILE *fp,
        - 			 * (see signal(7)).
        - 			 * => stay in the while loop */
        - 		} else {
        --			DEBUG_TRACE("recv() failed, error %d", err);
        -+			DEBUG_TRACE("read()/recv() failed, error %d", err);
        - 			return -2;
        - 		}
        - #endif
        -@@ -7052,6 +7287,7 @@ mg_url_decode(const char *src,
        -               int is_form_url_encoded)
        - {
        - 	int i, j, a, b;
        -+
        - #define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
        - 
        - 	for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
        -@@ -7064,11 +7300,15 @@ mg_url_decode(const char *src,
        - 			i += 2;
        - 		} else if (is_form_url_encoded && (src[i] == '+')) {
        - 			dst[j] = ' ';
        -+		} else if ((unsigned char)src[i] <= ' ') {
        -+			return -1; /* invalid character */
        - 		} else {
        - 			dst[j] = src[i];
        - 		}
        - 	}
        - 
        -+#undef HEXTOI
        -+
        - 	dst[j] = '\0'; /* Null-terminate the destination */
        - 
        - 	return (i >= src_len) ? j : -1;
        -@@ -7571,10 +7811,10 @@ extention_matches_template_text(
        -  * Return 1 if index file has been found, 0 if not found.
        -  * If the file is found, it's stats is returned in stp. */
        - static int
        --substitute_index_file(struct mg_connection *conn,
        --                      char *path,
        --                      size_t path_len,
        --                      struct mg_file_stat *filestat)
        -+substitute_index_file_aux(struct mg_connection *conn,
        -+                          char *path,
        -+                          size_t path_len,
        -+                          struct mg_file_stat *filestat)
        - {
        - 	const char *list = conn->dom_ctx->config[INDEX_FILES];
        - 	struct vec filename_vec;
        -@@ -7615,6 +7855,61 @@ substitute_index_file(struct mg_connection *conn,
        - 
        - 	return found;
        - }
        -+
        -+/* Same as above, except if the first try fails and a fallback-root is
        -+ * configured, we'll try there also */
        -+static int
        -+substitute_index_file(struct mg_connection *conn,
        -+                      char *path,
        -+                      size_t path_len,
        -+                      struct mg_file_stat *filestat)
        -+{
        -+	int ret = substitute_index_file_aux(conn, path, path_len, filestat);
        -+	if (ret == 0) {
        -+		const char *root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT];
        -+		const char *fallback_root_prefix =
        -+		    conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT];
        -+		if ((root_prefix) && (fallback_root_prefix)) {
        -+			const size_t root_prefix_len = strlen(root_prefix);
        -+			if ((strncmp(path, root_prefix, root_prefix_len) == 0)) {
        -+				char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
        -+				                                  side effects if we fail */
        -+				size_t sub_path_len;
        -+
        -+				const size_t fallback_root_prefix_len =
        -+				    strlen(fallback_root_prefix);
        -+				const char *sub_path = path + root_prefix_len;
        -+				while (*sub_path == '/') {
        -+					sub_path++;
        -+				}
        -+				sub_path_len = strlen(sub_path);
        -+
        -+				if (((fallback_root_prefix_len + 1 + sub_path_len + 1)
        -+				     < sizeof(scratch_path))) {
        -+					/* The concatenations below are all safe because we
        -+					 * pre-verified string lengths above */
        -+					char *nul;
        -+					strcpy(scratch_path, fallback_root_prefix);
        -+					nul = strchr(scratch_path, '\0');
        -+					if ((nul > scratch_path) && (*(nul - 1) != '/')) {
        -+						*nul++ = '/';
        -+						*nul = '\0';
        -+					}
        -+					strcat(scratch_path, sub_path);
        -+					if (substitute_index_file_aux(conn,
        -+					                              scratch_path,
        -+					                              sizeof(scratch_path),
        -+					                              filestat)) {
        -+						mg_strlcpy(path, scratch_path, path_len);
        -+						return 1;
        -+					}
        -+				}
        -+			}
        -+		}
        -+	}
        -+	return ret;
        -+}
        -+
        - #endif
        - 
        - 
        -@@ -7635,12 +7930,16 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        - 
        - #if !defined(NO_FILES)
        - 	const char *uri = conn->request_info.local_uri;
        --	const char *root = conn->dom_ctx->config[DOCUMENT_ROOT];
        -+	const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT],
        -+	                       conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT],
        -+	                       NULL};
        -+	int fileExists = 0;
        - 	const char *rewrite;
        - 	struct vec a, b;
        - 	ptrdiff_t match_len;
        - 	char gz_path[UTF8_PATH_MAX];
        - 	int truncated;
        -+	int i;
        - #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
        - 	char *tmp_str;
        - 	size_t tmp_str_len, sep_pos;
        -@@ -7670,7 +7969,8 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        - 	*is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
        - #if !defined(NO_FILES)
        - 	if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
        --		root = conn->dom_ctx->config[WEBSOCKET_ROOT];
        -+		roots[0] = conn->dom_ctx->config[WEBSOCKET_ROOT];
        -+		roots[1] = conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT];
        - 	}
        - #endif /* !NO_FILES */
        - #else  /* USE_WEBSOCKET */
        -@@ -7687,51 +7987,63 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
        - 
        - #if !defined(NO_FILES)
        - 	/* Step 5: If there is no root directory, don't look for files. */
        --	/* Note that root == NULL is a regular use case here. This occurs,
        -+	/* Note that roots[0] == NULL is a regular use case here. This occurs,
        - 	 * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
        - 	 * config is not required. */
        --	if (root == NULL) {
        -+	if (roots[0] == NULL) {
        - 		/* all file related outputs have already been set to 0, just return
        - 		 */
        - 		return;
        - 	}
        - 
        --	/* Step 6: Determine the local file path from the root path and the
        --	 * request uri. */
        --	/* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
        --	 * part of the path one byte on the right. */
        --	truncated = 0;
        --	mg_snprintf(
        --	    conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
        -+	for (i = 0; roots[i] != NULL; i++) {
        -+		/* Step 6: Determine the local file path from the root path and the
        -+		 * request uri. */
        -+		/* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
        -+		 * part of the path one byte on the right. */
        -+		truncated = 0;
        -+		mg_snprintf(conn,
        -+		            &truncated,
        -+		            filename,
        -+		            filename_buf_len - 1,
        -+		            "%s%s",
        -+		            roots[i],
        -+		            uri);
        - 
        --	if (truncated) {
        --		goto interpret_cleanup;
        --	}
        -+		if (truncated) {
        -+			goto interpret_cleanup;
        -+		}
        - 
        --	/* Step 7: URI rewriting */
        --	rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
        --	while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
        --		if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
        --			mg_snprintf(conn,
        --			            &truncated,
        --			            filename,
        --			            filename_buf_len - 1,
        --			            "%.*s%s",
        --			            (int)b.len,
        --			            b.ptr,
        --			            uri + match_len);
        --			break;
        -+		/* Step 7: URI rewriting */
        -+		rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
        -+		while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
        -+			if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
        -+				mg_snprintf(conn,
        -+				            &truncated,
        -+				            filename,
        -+				            filename_buf_len - 1,
        -+				            "%.*s%s",
        -+				            (int)b.len,
        -+				            b.ptr,
        -+				            uri + match_len);
        -+				break;
        -+			}
        - 		}
        --	}
        - 
        --	if (truncated) {
        --		goto interpret_cleanup;
        -+		if (truncated) {
        -+			goto interpret_cleanup;
        -+		}
        -+
        -+		/* Step 8: Check if the file exists at the server */
        -+		/* Local file path and name, corresponding to requested URI
        -+		 * is now stored in "filename" variable. */
        -+		if (mg_stat(conn, filename, filestat)) {
        -+			fileExists = 1;
        -+			break;
        -+		}
        - 	}
        - 
        --	/* Step 8: Check if the file exists at the server */
        --	/* Local file path and name, corresponding to requested URI
        --	 * is now stored in "filename" variable. */
        --	if (mg_stat(conn, filename, filestat)) {
        -+	if (fileExists) {
        - 		int uri_len = (int)strlen(uri);
        - 		int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/');
        - 
        -@@ -8227,6 +8539,9 @@ static const struct {
        -      * (http://www.iana.org/assignments/media-types)
        -      * application types */
        -     {".bin", 4, "application/octet-stream"},
        -+    {".cer", 4, "application/pkix-cert"},
        -+    {".crl", 4, "application/pkix-crl"},
        -+    {".crt", 4, "application/pkix-cert"},
        -     {".deb", 4, "application/octet-stream"},
        -     {".dmg", 4, "application/octet-stream"},
        -     {".dll", 4, "application/octet-stream"},
        -@@ -8236,10 +8551,13 @@ static const struct {
        -     {".iso", 4, "application/octet-stream"},
        -     {".js", 3, "application/javascript"},
        -     {".json", 5, "application/json"},
        -+    {".mjs", 4, "application/javascript"},
        -     {".msi", 4, "application/octet-stream"},
        -+    {".pem", 4, "application/x-pem-file"},
        -     {".pdf", 4, "application/pdf"},
        -     {".ps", 3, "application/postscript"},
        -     {".rtf", 4, "application/rtf"},
        -+    {".wasm", 5, "application/wasm"},
        -     {".xhtml", 6, "application/xhtml+xml"},
        -     {".xsl", 4, "application/xml"},
        -     {".xslt", 5, "application/xml"},
        -@@ -8542,7 +8860,7 @@ open_auth_file(struct mg_connection *conn,
        - 
        - 
        - /* Parsed Authorization header */
        --struct ah {
        -+struct auth_header {
        - 	char *user;
        - 	int type;             /* 1 = basic, 2 = digest */
        - 	char *plain_password; /* Basic only */
        -@@ -8550,32 +8868,32 @@ struct ah {
        - };
        - 
        - 
        --/* Return 1 on success. Always initializes the ah structure. */
        -+/* Return 1 on success. Always initializes the auth_header structure. */
        - static int
        - parse_auth_header(struct mg_connection *conn,
        -                   char *buf,
        -                   size_t buf_size,
        --                  struct ah *ah)
        -+                  struct auth_header *auth_header)
        - {
        - 	char *name, *value, *s;
        --	const char *auth_header;
        -+	const char *ah;
        - 	uint64_t nonce;
        - 
        --	if (!ah || !conn) {
        -+	if (!auth_header || !conn) {
        - 		return 0;
        - 	}
        - 
        --	(void)memset(ah, 0, sizeof(*ah));
        --	auth_header = mg_get_header(conn, "Authorization");
        -+	(void)memset(auth_header, 0, sizeof(*auth_header));
        -+	ah = mg_get_header(conn, "Authorization");
        - 
        --	if (auth_header == NULL) {
        -+	if (ah == NULL) {
        - 		/* No Authorization header at all */
        - 		return 0;
        - 	}
        --	if (0 == mg_strncasecmp(auth_header, "Basic ", 6)) {
        -+	if (0 == mg_strncasecmp(ah, "Basic ", 6)) {
        - 		/* Basic Auth (we never asked for this, but some client may send it) */
        - 		char *split;
        --		const char *userpw_b64 = auth_header + 6;
        -+		const char *userpw_b64 = ah + 6;
        - 		size_t userpw_b64_len = strlen(userpw_b64);
        - 		size_t buf_len_r = buf_size;
        - 		if (mg_base64_decode(
        -@@ -8592,15 +8910,15 @@ parse_auth_header(struct mg_connection *conn,
        - 		*split = 0;
        - 
        - 		/* User name is before ':', Password is after ':'  */
        --		ah->user = buf;
        --		ah->type = 1;
        --		ah->plain_password = split + 1;
        -+		auth_header->user = buf;
        -+		auth_header->type = 1;
        -+		auth_header->plain_password = split + 1;
        - 
        - 		return 1;
        - 
        --	} else if (0 == mg_strncasecmp(auth_header, "Digest ", 7)) {
        -+	} else if (0 == mg_strncasecmp(ah, "Digest ", 7)) {
        - 		/* Digest Auth ... implemented below */
        --		ah->type = 2;
        -+		auth_header->type = 2;
        - 
        - 	} else {
        - 		/* Unknown or invalid Auth method */
        -@@ -8608,7 +8926,7 @@ parse_auth_header(struct mg_connection *conn,
        - 	}
        - 
        - 	/* Make modifiable copy of the auth header */
        --	(void)mg_strlcpy(buf, auth_header + 7, buf_size);
        -+	(void)mg_strlcpy(buf, ah + 7, buf_size);
        - 	s = buf;
        - 
        - 	/* Parse authorization header */
        -@@ -8635,29 +8953,29 @@ parse_auth_header(struct mg_connection *conn,
        - 		}
        - 
        - 		if (!strcmp(name, "username")) {
        --			ah->user = value;
        -+			auth_header->user = value;
        - 		} else if (!strcmp(name, "cnonce")) {
        --			ah->cnonce = value;
        -+			auth_header->cnonce = value;
        - 		} else if (!strcmp(name, "response")) {
        --			ah->response = value;
        -+			auth_header->response = value;
        - 		} else if (!strcmp(name, "uri")) {
        --			ah->uri = value;
        -+			auth_header->uri = value;
        - 		} else if (!strcmp(name, "qop")) {
        --			ah->qop = value;
        -+			auth_header->qop = value;
        - 		} else if (!strcmp(name, "nc")) {
        --			ah->nc = value;
        -+			auth_header->nc = value;
        - 		} else if (!strcmp(name, "nonce")) {
        --			ah->nonce = value;
        -+			auth_header->nonce = value;
        - 		}
        - 	}
        - 
        - #if !defined(NO_NONCE_CHECK)
        - 	/* Read the nonce from the response. */
        --	if (ah->nonce == NULL) {
        -+	if (auth_header->nonce == NULL) {
        - 		return 0;
        - 	}
        - 	s = NULL;
        --	nonce = strtoull(ah->nonce, &s, 10);
        -+	nonce = strtoull(auth_header->nonce, &s, 10);
        - 	if ((s == NULL) || (*s != 0)) {
        - 		return 0;
        - 	}
        -@@ -8688,7 +9006,7 @@ parse_auth_header(struct mg_connection *conn,
        - 	(void)nonce;
        - #endif
        - 
        --	return (ah->user != NULL);
        -+	return (auth_header->user != NULL);
        - }
        - 
        - 
        -@@ -8720,7 +9038,7 @@ mg_fgets(char *buf, size_t size, struct mg_file *filep)
        - #if !defined(NO_FILESYSTEMS)
        - struct read_auth_file_struct {
        - 	struct mg_connection *conn;
        --	struct ah ah;
        -+	struct auth_header auth_header;
        - 	const char *domain;
        - 	char buf[256 + 256 + 40];
        - 	const char *f_user;
        -@@ -8821,9 +9139,9 @@ read_auth_file(struct mg_file *filep,
        - 		*(char *)(workdata->f_ha1) = 0;
        - 		(workdata->f_ha1)++;
        - 
        --		if (!strcmp(workdata->ah.user, workdata->f_user)
        -+		if (!strcmp(workdata->auth_header.user, workdata->f_user)
        - 		    && !strcmp(workdata->domain, workdata->f_domain)) {
        --			switch (workdata->ah.type) {
        -+			switch (workdata->auth_header.type) {
        - 			case 1: /* Basic */
        - 			{
        - 				char md5[33];
        -@@ -8832,7 +9150,7 @@ read_auth_file(struct mg_file *filep,
        - 				       ":",
        - 				       workdata->domain,
        - 				       ":",
        --				       workdata->ah.plain_password,
        -+				       workdata->auth_header.plain_password,
        - 				       NULL);
        - 				return 0 == memcmp(workdata->f_ha1, md5, 33);
        - 			}
        -@@ -8840,12 +9158,12 @@ read_auth_file(struct mg_file *filep,
        - 				return check_password_digest(
        - 				    workdata->conn->request_info.request_method,
        - 				    workdata->f_ha1,
        --				    workdata->ah.uri,
        --				    workdata->ah.nonce,
        --				    workdata->ah.nc,
        --				    workdata->ah.cnonce,
        --				    workdata->ah.qop,
        --				    workdata->ah.response);
        -+				    workdata->auth_header.uri,
        -+				    workdata->auth_header.nonce,
        -+				    workdata->auth_header.nc,
        -+				    workdata->auth_header.cnonce,
        -+				    workdata->auth_header.qop,
        -+				    workdata->auth_header.response);
        - 			default: /* None/Other/Unknown */
        - 				return 0;
        - 			}
        -@@ -8870,13 +9188,13 @@ authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
        - 	memset(&workdata, 0, sizeof(workdata));
        - 	workdata.conn = conn;
        - 
        --	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
        -+	if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.auth_header)) {
        - 		return 0;
        - 	}
        - 
        - 	/* CGI needs it as REMOTE_USER */
        - 	conn->request_info.remote_user =
        --	    mg_strdup_ctx(workdata.ah.user, conn->phys_ctx);
        -+	    mg_strdup_ctx(workdata.auth_header.user, conn->phys_ctx);
        - 
        - 	if (realm) {
        - 		workdata.domain = realm;
        -@@ -9344,7 +9662,8 @@ connect_socket(
        - 		return 0;
        - 	}
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL)
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)          \
        -+    && !defined(NO_SSL_DL)
        - #if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
        - 	if (use_ssl && (TLS_client_method == NULL)) {
        - 		if (error != NULL) {
        -@@ -9511,11 +9830,11 @@ connect_socket(
        - #endif
        - 
        - 		/* Data for poll */
        --		struct mg_pollfd pfd[1];
        -+		struct mg_pollfd pfd[2];
        - 		int pollres;
        --		int ms_wait = 10000; /* 10 second timeout */
        --		stop_flag_t nonstop;
        --		STOP_FLAG_ASSIGN(&nonstop, 0);
        -+		int ms_wait = 10000;       /* 10 second timeout */
        -+		stop_flag_t nonstop = 0;   /* STOP_FLAG_ASSIGN(&nonstop, 0); */
        -+		unsigned int num_sock = 1; /* use one or two sockets */
        - 
        - 		/* For a non-blocking socket, the connect sequence is:
        - 		 * 1) call connect (will not block)
        -@@ -9524,7 +9843,15 @@ connect_socket(
        - 		 */
        - 		pfd[0].fd = *sock;
        - 		pfd[0].events = POLLOUT;
        --		pollres = mg_poll(pfd, 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
        -+
        -+		if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
        -+			pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
        -+			pfd[num_sock].events = POLLIN;
        -+			num_sock++;
        -+		}
        -+
        -+		pollres =
        -+		    mg_poll(pfd, num_sock, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
        - 
        - 		if (pollres != 1) {
        - 			/* Not connected */
        -@@ -10145,8 +10472,11 @@ send_file_data(struct mg_connection *conn,
        - 				}
        - 
        - 				/* Read from file, exit the loop on error */
        --				if ((num_read =
        --				         (int)fread(buf, 1, (size_t)to_read, filep->access.fp))
        -+				if ((num_read = pull_inner(filep->access.fp,
        -+				                           NULL,
        -+				                           buf,
        -+				                           to_read,
        -+				                           /* unused */ 0.0))
        - 				    <= 0) {
        - 					break;
        - 				}
        -@@ -10473,9 +10803,13 @@ is_not_modified(const struct mg_connection *conn,
        - 	const char *inm = mg_get_header(conn, "If-None-Match");
        - 	construct_etag(etag, sizeof(etag), filestat);
        - 
        --	return ((inm != NULL) && !mg_strcasecmp(etag, inm))
        --	       || ((ims != NULL)
        --	           && (filestat->last_modified <= parse_date_string(ims)));
        -+	if (inm) {
        -+		return !mg_strcasecmp(etag, inm);
        -+	}
        -+	if (ims) {
        -+		return (filestat->last_modified <= parse_date_string(ims));
        -+	}
        -+	return 0;
        - }
        - 
        - 
        -@@ -10680,8 +11014,9 @@ static int
        - skip_to_end_of_word_and_terminate(char **ppw, int eol)
        - {
        - 	/* Forward until a space is found - use isgraph here */
        -+	/* Extended ASCII characters are also treated as word characters. */
        - 	/* See http://www.cplusplus.com/reference/cctype/ */
        --	while (isgraph((unsigned char)**ppw)) {
        -+	while ((unsigned char)**ppw > 127 || isgraph((unsigned char)**ppw)) {
        - 		(*ppw)++;
        - 	}
        - 
        -@@ -10778,7 +11113,7 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
        - 		}
        - 
        - 		/* here *dp is either 0 or '\n' */
        --		/* in any case, we have a new header */
        -+		/* in any case, we have found a complete header */
        - 		num_headers = i + 1;
        - 
        - 		if (*dp) {
        -@@ -10787,9 +11122,11 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
        - 			*buf = dp;
        - 
        - 			if ((dp[0] == '\r') || (dp[0] == '\n')) {
        --				/* This is the end of the header */
        -+				/* We've had CRLF twice in a row
        -+				 * This is the end of the headers */
        - 				break;
        - 			}
        -+			/* continue within the loop, find the next header */
        - 		} else {
        - 			*buf = dp;
        - 			break;
        -@@ -11166,11 +11503,11 @@ read_message(FILE *fp,
        - 			request_len = get_http_header_len(buf, *nread);
        - 		}
        - 
        --		if ((request_len == 0) && (request_timeout >= 0)) {
        -+		if ((n <= 0) && (request_timeout >= 0)) {
        - 			if (mg_difftimespec(&last_action_time, &(conn->req_time))
        - 			    > request_timeout) {
        - 				/* Timeout */
        --				return -1;
        -+				return -3;
        - 			}
        - 		}
        - 	}
        -@@ -11390,6 +11727,11 @@ prepare_cgi_environment(struct mg_connection *conn,
        - 	addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
        - 	addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
        - 	addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
        -+	if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) {
        -+		addenv(env,
        -+		       "FALLBACK_DOCUMENT_ROOT=%s",
        -+		       conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]);
        -+	}
        - 	addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version());
        - 
        - 	/* Prepare the environment block */
        -@@ -12808,11 +13150,14 @@ dav_lock_file(struct mg_connection *conn, const char *path)
        - 	int i;
        - 	uint64_t LOCK_DURATION_NS =
        - 	    (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000;
        --	struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
        -+	struct twebdav_lock *dav_lock = NULL;
        - 
        --	if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
        -+	if (!path || !conn || !conn->dom_ctx || !conn->request_info.remote_user
        -+	    || !conn->phys_ctx) {
        - 		return;
        - 	}
        -+
        -+	dav_lock = conn->phys_ctx->webdav_lock;
        - 	mg_get_request_link(conn, link_buf, sizeof(link_buf));
        - 
        - 	/* const char *refresh = mg_get_header(conn, "If"); */
        -@@ -13214,9 +13559,10 @@ read_websocket(struct mg_connection *conn,
        - 		if ((header_len > 0) && (body_len >= header_len)) {
        - 			/* Allocate space to hold websocket payload */
        - 			unsigned char *data = mem;
        -+			size_t required_len = (size_t)data_len + 4;
        - 
        --			if ((size_t)data_len > (size_t)sizeof(mem)) {
        --				data = (unsigned char *)mg_malloc_ctx((size_t)data_len,
        -+			if (required_len > sizeof(mem)) {
        -+				data = (unsigned char *)mg_malloc_ctx(required_len,
        - 				                                      conn->phys_ctx);
        - 				if (data == NULL) {
        - 					/* Allocation failed, exit the loop and then close the
        -@@ -14758,7 +15104,7 @@ handle_request(struct mg_connection *conn)
        - 		}
        - 		return;
        - 	}
        --	uri_len = (int)strlen(ri->local_uri);
        -+
        - 
        - 	/* 1.3. decode url (if config says so) */
        - 	if (should_decode_url(conn)) {
        -@@ -14786,6 +15132,11 @@ handle_request(struct mg_connection *conn)
        - 	}
        - 	remove_dot_segments(tmp);
        - 	ri->local_uri = tmp;
        -+#if !defined(NO_FILES) /* Only compute if later code can actually use it */
        -+	/* Cache URI length once; recompute only if the buffer changes later. */
        -+	uri_len = (int)strlen(ri->local_uri);
        -+#endif
        -+
        - 
        - 	/* step 1. completed, the url is known now */
        - 	DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
        -@@ -14838,19 +15189,30 @@ handle_request(struct mg_connection *conn)
        - 		const char *cors_acrm = get_header(ri->http_headers,
        - 		                                   ri->num_headers,
        - 		                                   "Access-Control-Request-Method");
        -+		const char *cors_repl_asterisk_with_orig_cfg =
        -+		    conn->dom_ctx->config[REPLACE_ASTERISK_WITH_ORIGIN];
        - 
        - 		/* Todo: check if cors_origin is in cors_orig_cfg.
        - 		 * Or, let the client check this. */
        - 
        - 		if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
        - 		    && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
        --		    && (cors_origin != NULL) && (cors_acrm != NULL)) {
        -+		    && (cors_origin != NULL) && (cors_acrm != NULL)
        -+		    && (cors_repl_asterisk_with_orig_cfg != NULL)
        -+		    && (*cors_repl_asterisk_with_orig_cfg != 0)) {
        -+			int cors_repl_asterisk_with_orig =
        -+			    mg_strcasecmp(cors_repl_asterisk_with_orig_cfg, "yes");
        -+
        - 			/* This is a valid CORS preflight, and the server is configured
        - 			 * to handle it automatically. */
        - 			const char *cors_acrh =
        - 			    get_header(ri->http_headers,
        - 			               ri->num_headers,
        - 			               "Access-Control-Request-Headers");
        -+			const char *cors_cred_cfg =
        -+			    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
        -+			const char *cors_exphdr_cfg =
        -+			    conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
        - 
        - 			gmt_time_string(date, sizeof(date), &curtime);
        - 			mg_printf(conn,
        -@@ -14861,11 +15223,26 @@ handle_request(struct mg_connection *conn)
        - 			          "Content-Length: 0\r\n"
        - 			          "Connection: %s\r\n",
        - 			          date,
        --			          cors_orig_cfg,
        -+			          (cors_repl_asterisk_with_orig == 0
        -+			           && cors_orig_cfg[0] == '*')
        -+			              ? cors_origin
        -+			              : cors_orig_cfg,
        - 			          ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
        - 			          suggest_connection_header(conn));
        - 
        --			if (cors_acrh != NULL) {
        -+			if (cors_cred_cfg && *cors_cred_cfg) {
        -+				mg_printf(conn,
        -+				          "Access-Control-Allow-Credentials: %s\r\n",
        -+				          cors_cred_cfg);
        -+			}
        -+
        -+			if (cors_exphdr_cfg && *cors_exphdr_cfg) {
        -+				mg_printf(conn,
        -+				          "Access-Control-Expose-Headers: %s\r\n",
        -+				          cors_exphdr_cfg);
        -+			}
        -+
        -+			if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) {
        - 				/* CORS request is asking for additional headers */
        - 				const char *cors_hdr_cfg =
        - 				    conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
        -@@ -15240,9 +15617,10 @@ handle_request(struct mg_connection *conn)
        - 	}
        - 
        - 	/* 12. Directory uris should end with a slash */
        --	if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
        -+	if (file.stat.is_directory && (uri_len > 0)
        - 	    && (ri->local_uri[uri_len - 1] != '/')) {
        - 
        -+
        - 		/* Path + server root */
        - 		size_t buflen = UTF8_PATH_MAX * 2 + 2;
        - 		char *new_path;
        -@@ -15255,12 +15633,27 @@ handle_request(struct mg_connection *conn)
        - 			mg_send_http_error(conn, 500, "out or memory");
        - 		} else {
        - 			mg_get_request_link(conn, new_path, buflen - 1);
        --			strcat(new_path, "/");
        -+
        -+			size_t len = strlen(new_path);
        -+			if (len + 1 < buflen) {
        -+				new_path[len] = '/';
        -+				new_path[len + 1] = '\0';
        -+				len++;
        -+			}
        -+
        - 			if (ri->query_string) {
        --				/* Append ? and query string */
        --				strcat(new_path, "?");
        --				strcat(new_path, ri->query_string);
        -+				if (len + 1 < buflen) {
        -+					new_path[len] = '?';
        -+					new_path[len + 1] = '\0';
        -+					len++;
        -+				}
        -+
        -+				/* Append with size of space left for query string + null
        -+				 * terminator */
        -+				size_t max_append = buflen - len - 1;
        -+				strncat(new_path, ri->query_string, max_append);
        - 			}
        -+
        - 			mg_send_http_redirect(conn, new_path, 301);
        - 			mg_free(new_path);
        - 		}
        -@@ -15503,7 +15896,7 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
        - 	unsigned int a, b, c, d;
        - 	unsigned port;
        - 	unsigned long portUL;
        --	int ch, len;
        -+	int len;
        - 	const char *cb;
        - 	char *endptr;
        - #if defined(USE_IPV6)
        -@@ -15656,14 +16049,38 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
        - 	}
        - 
        - 	/* sscanf and the option splitting code ensure the following condition
        --	 * Make sure the port is valid and vector ends with the port, 's' or 'r' */
        --	if ((len > 0) && is_valid_port(port)
        --	    && (((size_t)len == vec->len) || (((size_t)len + 1) == vec->len))) {
        --		/* Next character after the port number */
        --		ch = ((size_t)len < vec->len) ? vec->ptr[len] : '\0';
        --		so->is_ssl = (ch == 's');
        --		so->ssl_redir = (ch == 'r');
        --		if ((ch == '\0') || (ch == 's') || (ch == 'r')) {
        -+	 * Make sure the port is valid and vector ends with the port, 'o', 's', or
        -+	 * 'r' */
        -+	if ((len > 0) && (is_valid_port(port))) {
        -+		int bad_suffix = 0;
        -+		size_t i;
        -+
        -+		/* Parse any suffix character(s) after the port number */
        -+		for (i = len; i < vec->len; i++) {
        -+			unsigned char *opt = NULL;
        -+			switch (vec->ptr[i]) {
        -+			case 'o':
        -+				opt = &so->is_optional;
        -+				break;
        -+			case 'r':
        -+				opt = &so->ssl_redir;
        -+				break;
        -+			case 's':
        -+				opt = &so->is_ssl;
        -+				break;
        -+			default: /* empty */
        -+				break;
        -+			}
        -+
        -+			if ((opt) && (*opt == 0))
        -+				*opt = 1;
        -+			else {
        -+				bad_suffix = 1;
        -+				break;
        -+			}
        -+		}
        -+
        -+		if ((bad_suffix == 0) && ((so->is_ssl == 0) || (so->ssl_redir == 0))) {
        - 			return 1;
        - 		}
        - 	}
        -@@ -15718,8 +16135,14 @@ is_ssl_port_used(const char *ports)
        - 		char prevIsNumber = 0;
        - 
        - 		for (i = 0; i < portslen; i++) {
        --			if (prevIsNumber && (ports[i] == 's' || ports[i] == 'r')) {
        --				return 1;
        -+			if (prevIsNumber) {
        -+				int suffixCharIdx = (ports[i] == 'o')
        -+				                        ? (i + 1)
        -+				                        : i; /* allow "os" and "or" suffixes */
        -+				if (ports[suffixCharIdx] == 's'
        -+				    || ports[suffixCharIdx] == 'r') {
        -+					return 1;
        -+				}
        - 			}
        - 			if (ports[i] >= '0' && ports[i] <= '9') {
        - 				prevIsNumber = 1;
        -@@ -15800,6 +16223,10 @@ set_ports_option(struct mg_context *phys_ctx)
        - 			mg_cry_ctx_internal(phys_ctx,
        - 			                    "cannot create socket (entry %i)",
        - 			                    portsTotal);
        -+			if (so.is_optional) {
        -+				portsOk++; /* it's okay if we couldn't create a socket,
        -+				        this port is optional anyway */
        -+			}
        - 			continue;
        - 		}
        - 
        -@@ -15885,6 +16312,10 @@ set_ports_option(struct mg_context *phys_ctx)
        - #else
        - 			mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available");
        - 			closesocket(so.sock);
        -+			if (so.is_optional) {
        -+				portsOk++; /* it's okay if we couldn't set the socket option,
        -+				              this port is optional anyway */
        -+			}
        - 			so.sock = INVALID_SOCKET;
        - 			continue;
        - #endif
        -@@ -15902,6 +16333,10 @@ set_ports_option(struct mg_context *phys_ctx)
        - 				                    strerror(errno));
        - 				closesocket(so.sock);
        - 				so.sock = INVALID_SOCKET;
        -+				if (so.is_optional) {
        -+					portsOk++; /* it's okay if we couldn't bind, this port is
        -+					              optional anyway */
        -+				}
        - 				continue;
        - 			}
        - 		}
        -@@ -15918,6 +16353,10 @@ set_ports_option(struct mg_context *phys_ctx)
        - 				                    strerror(errno));
        - 				closesocket(so.sock);
        - 				so.sock = INVALID_SOCKET;
        -+				if (so.is_optional) {
        -+					portsOk++; /* it's okay if we couldn't bind, this port is
        -+					              optional anyway */
        -+				}
        - 				continue;
        - 			}
        - 		}
        -@@ -15934,6 +16373,10 @@ set_ports_option(struct mg_context *phys_ctx)
        - 				                    strerror(errno));
        - 				closesocket(so.sock);
        - 				so.sock = INVALID_SOCKET;
        -+				if (so.is_optional) {
        -+					portsOk++; /* it's okay if we couldn't bind, this port is
        -+					              optional anyway */
        -+				}
        - 				continue;
        - 			}
        - 		}
        -@@ -16011,9 +16454,14 @@ set_ports_option(struct mg_context *phys_ctx)
        - 			continue;
        - 		}
        - 
        -+		/* The +2 below includes the original +1 (for the socket we're about to
        -+		 * add), plus another +1 for the thread_shutdown_notification_socket
        -+		 * that we'll also want to poll() on so that mg_stop() can return
        -+		 * quickly
        -+		 */
        - 		if ((pfd = (struct mg_pollfd *)
        - 		         mg_realloc_ctx(phys_ctx->listening_socket_fds,
        --		                        (phys_ctx->num_listening_sockets + 1)
        -+		                        (phys_ctx->num_listening_sockets + 2)
        - 		                            * sizeof(phys_ctx->listening_socket_fds[0]),
        - 		                        phys_ctx))
        - 		    == NULL) {
        -@@ -16358,7 +16806,39 @@ mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
        - 		return 0;
        - 	}
        - 
        --	return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
        -+	return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE],
        -+	                        dom_ctx->config[SSL_CIPHER_LIST])
        -+	               == 0
        -+	           ? 1
        -+	           : 0;
        -+}
        -+
        -+#elif defined(USE_GNUTLS)
        -+/* Check if SSL is required.
        -+ * If so, set up ctx->ssl_ctx pointer. */
        -+static int
        -+mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
        -+{
        -+	if (!phys_ctx) {
        -+		return 0;
        -+	}
        -+
        -+	if (!dom_ctx) {
        -+		dom_ctx = &(phys_ctx->dd);
        -+	}
        -+
        -+	if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
        -+		/* No SSL port is set. No need to setup SSL. */
        -+		return 1;
        -+	}
        -+
        -+	dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
        -+	if (dom_ctx->ssl_ctx == NULL) {
        -+		fprintf(stderr, "ssl_ctx malloc failed\n");
        -+		return 0;
        -+	}
        -+
        -+	return gtls_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
        - 	               == 0
        - 	           ? 1
        - 	           : 0;
        -@@ -16535,15 +17015,26 @@ sslize(struct mg_connection *conn,
        - 					/* Need to retry the function call "later".
        - 					 * See https://linux.die.net/man/3/ssl_get_error
        - 					 * This is typical for non-blocking sockets. */
        --					struct mg_pollfd pfd;
        -+					struct mg_pollfd pfd[2];
        - 					int pollres;
        --					pfd.fd = conn->client.sock;
        --					pfd.events = ((err == SSL_ERROR_WANT_CONNECT)
        --					              || (err == SSL_ERROR_WANT_WRITE))
        --					                 ? POLLOUT
        --					                 : POLLIN;
        --					pollres =
        --					    mg_poll(&pfd, 1, 50, &(conn->phys_ctx->stop_flag));
        -+					unsigned int num_sock = 1;
        -+					pfd[0].fd = conn->client.sock;
        -+					pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT)
        -+					                 || (err == SSL_ERROR_WANT_WRITE))
        -+					                    ? POLLOUT
        -+					                    : POLLIN;
        -+
        -+					if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
        -+						pfd[num_sock].fd =
        -+						    conn->phys_ctx->thread_shutdown_notification_socket;
        -+						pfd[num_sock].events = POLLIN;
        -+						num_sock++;
        -+					}
        -+
        -+					pollres = mg_poll(pfd,
        -+					                  num_sock,
        -+					                  50,
        -+					                  &(conn->phys_ctx->stop_flag));
        - 					if (pollres < 0) {
        - 						/* Break if error occurred (-1)
        - 						 * or server shutdown (-2) */
        -@@ -17628,7 +18119,7 @@ uninitialize_openssl(void)
        - #endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
        - 	}
        - }
        --#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) */
        -+#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) */
        - 
        - 
        - #if !defined(NO_FILESYSTEMS)
        -@@ -17900,6 +18391,11 @@ close_connection(struct mg_connection *conn)
        - 		mbed_ssl_close(conn->ssl);
        - 		conn->ssl = NULL;
        - 	}
        -+#elif defined(USE_GNUTLS)
        -+	if (conn->ssl != NULL) {
        -+		gtls_ssl_close(conn->ssl);
        -+		conn->ssl = NULL;
        -+	}
        - #elif !defined(NO_SSL)
        - 	if (conn->ssl != NULL) {
        - 		/* Run SSL_shutdown twice to ensure completely close SSL connection
        -@@ -17963,7 +18459,7 @@ mg_close_connection(struct mg_connection *conn)
        - 		 * timeouts, we will just wait a few seconds in mg_join_thread. */
        - 
        - 		/* join worker thread */
        --		for (i = 0; i < conn->phys_ctx->cfg_worker_threads; i++) {
        -+		for (i = 0; i < conn->phys_ctx->spawned_worker_threads; i++) {
        - 			mg_join_thread(conn->phys_ctx->worker_threadids[i]);
        - 		}
        - 	}
        -@@ -17971,7 +18467,8 @@ mg_close_connection(struct mg_connection *conn)
        - 
        - 	close_connection(conn);
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
        -+    && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - 	if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT)
        - 	     || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT))
        - 	    && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
        -@@ -18073,7 +18570,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        - 		return NULL;
        - 	}
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
        -+    && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - #if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0))                     \
        -     && !defined(NO_SSL_DL)
        - 
        -@@ -18150,7 +18648,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        - 			            error->text_buffer_size,
        - 			            "Can not create mutex");
        - 		}
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
        -+    && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - 		SSL_CTX_free(conn->dom_ctx->ssl_ctx);
        - #endif
        - 		closesocket(sock);
        -@@ -18158,7 +18657,8 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
        - 		return NULL;
        - 	}
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS)                                  \
        -+    && !defined(USE_GNUTLS) // TODO: mbedTLS client
        - 	if (use_ssl) {
        - 		/* TODO: Check ssl_verify_peer and ssl_ca_path here.
        - 		 * SSL_CTX_set_verify call is needed to switch off server
        -@@ -18415,7 +18915,20 @@ get_uri_type(const char *uri)
        - 	 * and % encoded symbols.
        - 	 */
        - 	for (i = 0; uri[i] != 0; i++) {
        --		if (uri[i] < 33) {
        -+		/* Check for CRLF injection attempts */
        -+		if (uri[i] == '%') {
        -+			if (uri[i + 1] == '0' && (uri[i + 2] == 'd' || uri[i + 2] == 'D')) {
        -+				/* Found %0d (CR) */
        -+				DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
        -+				return 0;
        -+			}
        -+			if (uri[i + 1] == '0' && (uri[i + 2] == 'a' || uri[i + 2] == 'A')) {
        -+				/* Found %0a (LF) */
        -+				DEBUG_TRACE("CRLF injection attempt detected: %s\r\n", uri);
        -+				return 0;
        -+			}
        -+		}
        -+		if ((unsigned char)uri[i] < 33) {
        - 			/* control characters and spaces are invalid */
        - 			return 0;
        - 		}
        -@@ -18622,7 +19135,8 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        - 			            ebuf,
        - 			            ebuf_len,
        - 			            "%s",
        --			            "Malformed message");
        -+			            conn->request_len == -3 ? "Request timeout"
        -+			                                    : "Malformed message");
        - 			*err = 400;
        - 		} else {
        - 			/* Server did not recv anything -> just close the connection */
        -@@ -18644,7 +19158,7 @@ get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        - static int
        - get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        - {
        --	const char *cl;
        -+	const char *h_zip, *h_chunk, *h_len;
        - 
        - 	conn->connection_type =
        - 	    CONNECTION_TYPE_REQUEST; /* request (valid of not) */
        -@@ -18679,20 +19193,23 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        - 	}
        - 
        - #if USE_ZLIB
        --	if (((cl = get_header(conn->request_info.http_headers,
        -+	if (((h_zip = get_header(conn->request_info.http_headers,
        - 	                      conn->request_info.num_headers,
        - 	                      "Accept-Encoding"))
        - 	     != NULL)
        --	    && strstr(cl, "gzip")) {
        -+	    && strstr(h_zip, "gzip")) {
        - 		conn->accept_gzip = 1;
        - 	}
        - #endif
        --	if (((cl = get_header(conn->request_info.http_headers,
        -+   h_chunk = get_header(conn->request_info.http_headers,
        - 	                      conn->request_info.num_headers,
        --	                      "Transfer-Encoding"))
        --	     != NULL)
        -+	                      "Transfer-Encoding");
        -+   h_len = get_header(conn->request_info.http_headers,
        -+	                            conn->request_info.num_headers,
        -+	                            "Content-Length");
        -+	if (h_chunk != NULL)
        - 	    && mg_strcasecmp(cl, "identity")) {
        --		if (mg_strcasecmp(cl, "chunked")) {
        -+		if ((0!=mg_strcasecmp(cl, "chunked")) || (h_len!=NULL)) {
        - 			mg_snprintf(conn,
        - 			            NULL, /* No truncation check for ebuf */
        - 			            ebuf,
        -@@ -18704,10 +19221,7 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
        - 		}
        - 		conn->is_chunked = 1;
        - 		conn->content_len = 0; /* not yet read */
        --	} else if ((cl = get_header(conn->request_info.http_headers,
        --	                            conn->request_info.num_headers,
        --	                            "Content-Length"))
        --	           != NULL) {
        -+	} else if (h_len != NULL) {
        - 		/* Request has content length set */
        - 		char *endptr = NULL;
        - 		conn->content_len = strtoll(cl, &endptr, 10);
        -@@ -18993,6 +19507,24 @@ websocket_client_thread(void *data)
        - #endif
        - 
        - 
        -+#if defined(USE_WEBSOCKET)
        -+static void
        -+generate_websocket_magic(char *magic25)
        -+{
        -+	uint64_t rnd;
        -+	unsigned char buffer[2 * sizeof(rnd)];
        -+
        -+	rnd = get_random();
        -+	memcpy(buffer, &rnd, sizeof(rnd));
        -+	rnd = get_random();
        -+	memcpy(buffer + sizeof(rnd), &rnd, sizeof(rnd));
        -+
        -+	size_t dst_len = 24 + 1;
        -+	mg_base64_encode(buffer, sizeof(buffer), magic25, &dst_len);
        -+}
        -+#endif
        -+
        -+
        - static struct mg_connection *
        - mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        -                                  int use_ssl,
        -@@ -19009,7 +19541,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        - 
        - #if defined(USE_WEBSOCKET)
        - 	struct websocket_client_thread_data *thread_data;
        --	static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
        -+	char magic[32];
        -+	generate_websocket_magic(magic);
        - 
        - 	const char *host = client_options->host;
        - 	int i;
        -@@ -19173,7 +19706,8 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        - 	/* Now upgrade to ws/wss client context */
        - 	conn->phys_ctx->user_data = user_data;
        - 	conn->phys_ctx->context_type = CONTEXT_WS_CLIENT;
        --	conn->phys_ctx->cfg_worker_threads = 1; /* one worker thread */
        -+	conn->phys_ctx->cfg_max_worker_threads = 1; /* one worker thread */
        -+	conn->phys_ctx->spawned_worker_threads = 1; /* one worker thread */
        - 
        - 	/* Start a thread to read the websocket client connection
        - 	 * This thread will automatically stop when mg_disconnect is
        -@@ -19182,7 +19716,7 @@ mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
        - 	                            thread_data,
        - 	                            conn->phys_ctx->worker_threadids)
        - 	    != 0) {
        --		conn->phys_ctx->cfg_worker_threads = 0;
        -+		conn->phys_ctx->spawned_worker_threads = 0;
        - 		mg_free(thread_data);
        - 		mg_close_connection(conn);
        - 		conn = NULL;
        -@@ -19224,6 +19758,9 @@ mg_connect_websocket_client(const char *host,
        - 	memset(&client_options, 0, sizeof(client_options));
        - 	client_options.host = host;
        - 	client_options.port = port;
        -+	if (use_ssl) {
        -+		client_options.host_name = host;
        -+	}
        - 
        - 	return mg_connect_websocket_client_impl(&client_options,
        - 	                                        use_ssl,
        -@@ -19553,6 +20090,9 @@ process_new_connection(struct mg_connection *conn)
        - #endif
        - }
        - 
        -+static int
        -+mg_start_worker_thread(struct mg_context *ctx,
        -+                       int only_if_no_idle_threads); /* forward declaration */
        - 
        - #if defined(ALTERNATIVE_QUEUE)
        - 
        -@@ -19561,8 +20101,12 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        - {
        - 	unsigned int i;
        - 
        -+	(void)mg_start_worker_thread(
        -+	    ctx, 1); /* will start a worker-thread only if there aren't currently
        -+	                any idle worker-threads */
        -+
        - 	while (!ctx->stop_flag) {
        --		for (i = 0; i < ctx->cfg_worker_threads; i++) {
        -+		for (i = 0; i < ctx->spawned_worker_threads; i++) {
        - 			/* find a free worker slot and signal it */
        - 			if (ctx->client_socks[i].in_use == 2) {
        - 				(void)pthread_mutex_lock(&ctx->thread_mutex);
        -@@ -19587,10 +20131,18 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        - 
        - 
        - static int
        --consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
        -+consume_socket(struct mg_context *ctx,
        -+               struct socket *sp,
        -+               int thread_index,
        -+               int counter_was_preincremented)
        - {
        - 	DEBUG_TRACE("%s", "going idle");
        - 	(void)pthread_mutex_lock(&ctx->thread_mutex);
        -+	if (counter_was_preincremented
        -+	    == 0) { /* first call only: the master-thread pre-incremented this
        -+		           before he spawned us */
        -+		ctx->idle_worker_thread_count++;
        -+	}
        - 	ctx->client_socks[thread_index].in_use = 2;
        - 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        - 
        -@@ -19607,6 +20159,7 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
        - 		}
        - 		return 0;
        - 	}
        -+	ctx->idle_worker_thread_count--;
        - 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        - 	if (sp->in_use == 1) {
        - 		DEBUG_TRACE("grabbed socket %d, going busy", sp->sock);
        -@@ -19621,12 +20174,20 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
        - 
        - /* Worker threads take accepted socket from the queue */
        - static int
        --consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
        -+consume_socket(struct mg_context *ctx,
        -+               struct socket *sp,
        -+               int thread_index,
        -+               int counter_was_preincremented)
        - {
        - 	(void)thread_index;
        - 
        --	(void)pthread_mutex_lock(&ctx->thread_mutex);
        - 	DEBUG_TRACE("%s", "going idle");
        -+	(void)pthread_mutex_lock(&ctx->thread_mutex);
        -+	if (counter_was_preincremented
        -+	    == 0) { /* first call only: the master-thread pre-incremented this
        -+		           before he spawned us */
        -+		ctx->idle_worker_thread_count++;
        -+	}
        - 
        - 	/* If the queue is empty, wait. We're idle at this point. */
        - 	while ((ctx->sq_head == ctx->sq_tail)
        -@@ -19650,6 +20211,8 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
        - 	}
        - 
        - 	(void)pthread_cond_signal(&ctx->sq_empty);
        -+
        -+	ctx->idle_worker_thread_count--;
        - 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        - 
        - 	return STOP_FLAG_IS_ZERO(&ctx->stop_flag);
        -@@ -19696,6 +20259,10 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
        - 
        - 	(void)pthread_cond_signal(&ctx->sq_full);
        - 	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        -+
        -+	(void)mg_start_worker_thread(
        -+	    ctx, 1); /* will start a worker-thread only if there aren't currently
        -+	                any idle worker-threads */
        - }
        - #endif /* ALTERNATIVE_QUEUE */
        - 
        -@@ -19706,6 +20273,7 @@ worker_thread_run(struct mg_connection *conn)
        - 	struct mg_context *ctx = conn->phys_ctx;
        - 	int thread_index;
        - 	struct mg_workerTLS tls;
        -+	int first_call_to_consume_socket = 1;
        - 
        - 	mg_set_thread_name("worker");
        - 
        -@@ -19731,7 +20299,7 @@ worker_thread_run(struct mg_connection *conn)
        - 	/* Connection structure has been pre-allocated */
        - 	thread_index = (int)(conn - ctx->worker_connections);
        - 	if ((thread_index < 0)
        --	    || ((unsigned)thread_index >= (unsigned)ctx->cfg_worker_threads)) {
        -+	    || ((unsigned)thread_index >= (unsigned)ctx->cfg_max_worker_threads)) {
        - 		mg_cry_ctx_internal(ctx,
        - 		                    "Internal error: Invalid worker index %i",
        - 		                    thread_index);
        -@@ -19772,7 +20340,9 @@ worker_thread_run(struct mg_connection *conn)
        - 	/* Call consume_socket() even when ctx->stop_flag > 0, to let it
        - 	 * signal sq_empty condvar to wake up the master waiting in
        - 	 * produce_socket() */
        --	while (consume_socket(ctx, &conn->client, thread_index)) {
        -+	while (consume_socket(
        -+	    ctx, &conn->client, thread_index, first_call_to_consume_socket)) {
        -+		first_call_to_consume_socket = 0;
        - 
        - 		/* New connections must start with new protocol negotiation */
        - 		tls.alpn_proto = NULL;
        -@@ -19795,6 +20365,10 @@ worker_thread_run(struct mg_connection *conn)
        - 		sockaddr_to_string(conn->request_info.remote_addr,
        - 		                   sizeof(conn->request_info.remote_addr),
        - 		                   &conn->client.rsa);
        -+ 
        -+		sockaddr_to_string(conn->request_info.server_addr,
        -+		                   sizeof(conn->request_info.server_addr),
        -+		                   &conn->client.lsa);
        - 
        - 		DEBUG_TRACE("Incoming %sconnection from %s",
        - 		            (conn->client.is_ssl ? "SSL " : ""),
        -@@ -19822,6 +20396,24 @@ worker_thread_run(struct mg_connection *conn)
        - 				close_connection(conn);
        - 			}
        - 
        -+#elif defined(USE_GNUTLS)
        -+			/* HTTPS connection */
        -+			if (gtls_ssl_accept(&(conn->ssl),
        -+			                    conn->dom_ctx->ssl_ctx,
        -+			                    conn->client.sock,
        -+			                    conn->phys_ctx)
        -+			    == 0) {
        -+				/* conn->dom_ctx is set in get_request */
        -+				/* process HTTPS connection */
        -+				init_connection(conn);
        -+				conn->connection_type = CONNECTION_TYPE_REQUEST;
        -+				conn->protocol_type = PROTOCOL_TYPE_HTTP1;
        -+				process_new_connection(conn);
        -+			} else {
        -+				/* make sure the connection is cleaned up on SSL failure */
        -+				close_connection(conn);
        -+			}
        -+
        - #elif !defined(NO_SSL)
        - 			/* HTTPS connection */
        - 			if (sslize(conn, SSL_accept, NULL)) {
        -@@ -19979,6 +20571,7 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx)
        - 		set_close_on_exec(so.sock, NULL, ctx);
        - 		so.is_ssl = listener->is_ssl;
        - 		so.ssl_redir = listener->ssl_redir;
        -+		so.is_optional = listener->is_optional;
        - 		if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
        - 			mg_cry_ctx_internal(ctx,
        - 			                    "%s: getsockname() failed: %s",
        -@@ -20048,7 +20641,7 @@ master_thread_run(struct mg_context *ctx)
        - 	unsigned int i;
        - 	unsigned int workerthreadcount;
        - 
        --	if (!ctx) {
        -+	if (!ctx || !ctx->listening_socket_fds) {
        - 		return;
        - 	}
        - 
        -@@ -20127,8 +20720,17 @@ master_thread_run(struct mg_context *ctx)
        - 			pfd[i].events = POLLIN;
        - 		}
        - 
        -+		/* We listen on this socket just so that mg_stop() can cause mg_poll()
        -+		 * to return ASAP. Don't worry, we did allocate an extra slot at the end
        -+		 * of listening_socket_fds[] just to hold this
        -+		 */
        -+		pfd[ctx->num_listening_sockets].fd =
        -+		    ctx->thread_shutdown_notification_socket;
        -+		pfd[ctx->num_listening_sockets].events = POLLIN;
        -+
        - 		if (mg_poll(pfd,
        --		            ctx->num_listening_sockets,
        -+		            ctx->num_listening_sockets
        -+		                + 1, // +1 for the thread_shutdown_notification_socket
        - 		            SOCKET_TIMEOUT_QUANTUM,
        - 		            &(ctx->stop_flag))
        - 		    > 0) {
        -@@ -20154,7 +20756,7 @@ master_thread_run(struct mg_context *ctx)
        - 
        - 	/* Wakeup workers that are waiting for connections to handle. */
        - #if defined(ALTERNATIVE_QUEUE)
        --	for (i = 0; i < ctx->cfg_worker_threads; i++) {
        -+	for (i = 0; i < ctx->spawned_worker_threads; i++) {
        - 		event_signal(ctx->client_wait_events[i]);
        - 	}
        - #else
        -@@ -20164,7 +20766,7 @@ master_thread_run(struct mg_context *ctx)
        - #endif
        - 
        - 	/* Join all worker threads to avoid leaking threads. */
        --	workerthreadcount = ctx->cfg_worker_threads;
        -+	workerthreadcount = ctx->spawned_worker_threads;
        - 	for (i = 0; i < workerthreadcount; i++) {
        - 		if (ctx->worker_threadids[i] != 0) {
        - 			mg_join_thread(ctx->worker_threadids[i]);
        -@@ -20268,7 +20870,7 @@ free_context(struct mg_context *ctx)
        - #if defined(ALTERNATIVE_QUEUE)
        - 	mg_free(ctx->client_socks);
        - 	if (ctx->client_wait_events != NULL) {
        --		for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
        -+		for (i = 0; (unsigned)i < ctx->spawned_worker_threads; i++) {
        - 			event_destroy(ctx->client_wait_events[i]);
        - 		}
        - 		mg_free(ctx->client_wait_events);
        -@@ -20286,6 +20888,14 @@ free_context(struct mg_context *ctx)
        - 	(void)pthread_mutex_destroy(&ctx->lua_bg_mutex);
        - #endif
        - 
        -+	/* Deallocate shutdown-triggering socket-pair */
        -+	if (ctx->user_shutdown_notification_socket >= 0) {
        -+		closesocket(ctx->user_shutdown_notification_socket);
        -+	}
        -+	if (ctx->thread_shutdown_notification_socket >= 0) {
        -+		closesocket(ctx->thread_shutdown_notification_socket);
        -+	}
        -+
        - 	/* Deallocate config parameters */
        - 	for (i = 0; i < NUM_OPTIONS; i++) {
        - 		if (ctx->dd.config[i] != NULL) {
        -@@ -20311,6 +20921,13 @@ free_context(struct mg_context *ctx)
        - 		ctx->dd.ssl_ctx = NULL;
        - 	}
        - 
        -+#elif defined(USE_GNUTLS)
        -+	if (ctx->dd.ssl_ctx != NULL) {
        -+		gtls_sslctx_uninit(ctx->dd.ssl_ctx);
        -+		mg_free(ctx->dd.ssl_ctx);
        -+		ctx->dd.ssl_ctx = NULL;
        -+	}
        -+
        - #elif !defined(NO_SSL)
        - 	/* Deallocate SSL context */
        - 	if (ctx->dd.ssl_ctx != NULL) {
        -@@ -20362,6 +20979,12 @@ mg_stop(struct mg_context *ctx)
        - 	/* Set stop flag, so all threads know they have to exit. */
        - 	STOP_FLAG_ASSIGN(&ctx->stop_flag, 1);
        - 
        -+	/* Closing this socket will cause mg_poll() in all the I/O threads to return
        -+	 * immediately */
        -+	closesocket(ctx->user_shutdown_notification_socket);
        -+	ctx->user_shutdown_notification_socket =
        -+	    -1; /* to avoid calling closesocket() again in free_context() */
        -+
        - 	/* Join timer thread */
        - #if defined(USE_TIMERS)
        - 	timers_exit(ctx);
        -@@ -20421,6 +21044,8 @@ get_system_name(char **sysName)
        - 
        - 	*sysName = mg_strdup(name);
        - 
        -+#elif defined(__rtems__)
        -+	*sysName = mg_strdup("RTEMS");
        - #elif defined(__ZEPHYR__)
        - 	*sysName = mg_strdup("Zephyr OS");
        - #else
        -@@ -20459,13 +21084,122 @@ legacy_init(const char **options)
        - 	}
        - }
        - 
        -+/* we'll assume it's only Windows that doesn't have socketpair() available */
        -+#if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32)
        -+#define HAVE_SOCKETPAIR 1
        -+#endif
        -+
        -+static int
        -+mg_socketpair(int *sockA, int *sockB)
        -+{
        -+	int temp[2] = {-1, -1};
        -+	int asock = -1;
        -+
        -+	/** Default to unallocated */
        -+	*sockA = -1;
        -+	*sockB = -1;
        -+
        -+#if defined(HAVE_SOCKETPAIR)
        -+	int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, temp);
        -+	if (ret == 0) {
        -+		*sockA = temp[0];
        -+		*sockB = temp[1];
        -+		set_close_on_exec(*sockA, NULL, NULL);
        -+		set_close_on_exec(*sockB, NULL, NULL);
        -+	}
        -+	(void)asock; /* not used */
        -+	return ret;
        -+#else
        -+	/** No socketpair() call is available, so we'll have to roll our own
        -+	 * implementation */
        -+	asock = socket(PF_INET, SOCK_STREAM, 0);
        -+	if (asock >= 0) {
        -+		struct sockaddr_in addr;
        -+		struct sockaddr *pa = (struct sockaddr *)&addr;
        -+		socklen_t addrLen = sizeof(addr);
        -+
        -+		memset(&addr, 0, sizeof(addr));
        -+		addr.sin_family = AF_INET;
        -+		addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        -+		addr.sin_port = 0;
        -+
        -+		if ((bind(asock, pa, sizeof(addr)) == 0)
        -+		    && (getsockname(asock, pa, &addrLen) == 0)
        -+		    && (listen(asock, 1) == 0)) {
        -+			temp[0] = socket(PF_INET, SOCK_STREAM, 0);
        -+			if ((temp[0] >= 0) && (connect(temp[0], pa, sizeof(addr)) == 0)) {
        -+				temp[1] = accept(asock, pa, &addrLen);
        -+				if (temp[1] >= 0) {
        -+					closesocket(asock);
        -+					*sockA = temp[0];
        -+					*sockB = temp[1];
        -+					set_close_on_exec(*sockA, NULL, NULL);
        -+					set_close_on_exec(*sockB, NULL, NULL);
        -+					return 0; /* success! */
        -+				}
        -+			}
        -+		}
        -+	}
        -+
        -+	/* Cleanup */
        -+	if (asock >= 0)
        -+		closesocket(asock);
        -+	if (temp[0] >= 0)
        -+		closesocket(temp[0]);
        -+	if (temp[1] >= 0)
        -+		closesocket(temp[1]);
        -+	return -1; /* fail! */
        -+#endif
        -+}
        -+
        -+static int
        -+mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
        -+{
        -+	const unsigned int i = ctx->spawned_worker_threads;
        -+	if (i >= ctx->cfg_max_worker_threads) {
        -+		return -1; /* Oops, we hit our worker-thread limit!  No more worker
        -+		              threads, ever! */
        -+	}
        -+
        -+	(void)pthread_mutex_lock(&ctx->thread_mutex);
        -+#if defined(ALTERNATIVE_QUEUE)
        -+	if ((only_if_no_idle_threads) && (ctx->idle_worker_thread_count > 0)) {
        -+#else
        -+	if ((only_if_no_idle_threads)
        -+	    && (ctx->idle_worker_thread_count
        -+	        > (unsigned)(ctx->sq_head - ctx->sq_tail))) {
        -+#endif
        -+		(void)pthread_mutex_unlock(&ctx->thread_mutex);
        -+		return -2; /* There are idle threads available, so no need to spawn a
        -+		              new worker thread now */
        -+	}
        -+	ctx->idle_worker_thread_count++; /* we do this here to avoid a race
        -+	                                    condition while the thread is starting
        -+	                                    up */
        -+	(void)pthread_mutex_unlock(&ctx->thread_mutex);
        -+
        -+	ctx->worker_connections[i].phys_ctx = ctx;
        -+	int ret = mg_start_thread_with_id(worker_thread,
        -+	                                  &ctx->worker_connections[i],
        -+	                                  &ctx->worker_threadids[i]);
        -+	if (ret == 0) {
        -+		ctx->spawned_worker_threads++; /* note that we've filled another slot in
        -+		                                  the table */
        -+		DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads);
        -+	} else {
        -+		(void)pthread_mutex_lock(&ctx->thread_mutex);
        -+		ctx->idle_worker_thread_count--; /* whoops, roll-back on error */
        -+		(void)pthread_mutex_unlock(&ctx->thread_mutex);
        -+	}
        -+	return ret;
        -+}
        - 
        - CIVETWEB_API struct mg_context *
        - mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - {
        - 	struct mg_context *ctx;
        - 	const char *name, *value, *default_value;
        --	int idx, ok, workerthreadcount;
        -+	int idx, ok, prespawnthreadcount, workerthreadcount;
        - 	unsigned int i;
        - 	int itmp;
        - 	void (*exit_callback)(const struct mg_context *ctx) = 0;
        -@@ -20542,6 +21276,15 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - #if defined(USE_LUA)
        - 	ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr));
        - #endif
        -+
        -+	/** mg_stop() will close the user_shutdown_notification_socket, and that
        -+	 * will cause poll() to return immediately in the master-thread, so that
        -+	 * mg_stop() can also return immediately.
        -+	 */
        -+	ok &= (0
        -+	       == mg_socketpair(&ctx->user_shutdown_notification_socket,
        -+	                        &ctx->thread_shutdown_notification_socket));
        -+
        - 	if (!ok) {
        - 		unsigned error_id = (unsigned)ERRNO;
        - 		const char *err_msg =
        -@@ -20709,6 +21452,13 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 
        - 	/* Worker thread count option */
        - 	workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
        -+	prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]);
        -+
        -+	if ((prespawnthreadcount < 0)
        -+	    || (prespawnthreadcount > workerthreadcount)) {
        -+		prespawnthreadcount =
        -+		    workerthreadcount; /* can't prespawn more than all of them! */
        -+	}
        - 
        - 	if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) {
        - 		if (workerthreadcount <= 0) {
        -@@ -20868,7 +21618,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 	}
        - #endif
        - 
        --#if defined(USE_MBEDTLS)
        -+#if defined(USE_MBEDTLS) || defined(USE_GNUTLS)
        - 	if (!mg_sslctx_init(ctx, NULL)) {
        - 		const char *err_msg = "Error initializing SSL context";
        - 		/* Fatal error - abort start. */
        -@@ -20973,10 +21723,11 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 		return NULL;
        - 	}
        - 
        --	ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount));
        --	ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_worker_threads,
        --	                                                   sizeof(pthread_t),
        --	                                                   ctx);
        -+	ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount));
        -+	ctx->worker_threadids =
        -+	    (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
        -+	                               sizeof(pthread_t),
        -+	                               ctx);
        - 
        - 	if (ctx->worker_threadids == NULL) {
        - 		const char *err_msg = "Not enough memory for worker thread ID array";
        -@@ -20984,8 +21735,8 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 
        - 		if (error != NULL) {
        - 			error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
        --			error->code_sub =
        --			    (unsigned)ctx->cfg_worker_threads * (unsigned)sizeof(pthread_t);
        -+			error->code_sub = (unsigned)ctx->cfg_max_worker_threads
        -+			                  * (unsigned)sizeof(pthread_t);
        - 			mg_snprintf(NULL,
        - 			            NULL, /* No truncation check for error buffers */
        - 			            error->text,
        -@@ -20999,7 +21750,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 		return NULL;
        - 	}
        - 	ctx->worker_connections =
        --	    (struct mg_connection *)mg_calloc_ctx(ctx->cfg_worker_threads,
        -+	    (struct mg_connection *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
        - 	                                          sizeof(struct mg_connection),
        - 	                                          ctx);
        - 	if (ctx->worker_connections == NULL) {
        -@@ -21009,7 +21760,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 
        - 		if (error != NULL) {
        - 			error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
        --			error->code_sub = (unsigned)ctx->cfg_worker_threads
        -+			error->code_sub = (unsigned)ctx->cfg_max_worker_threads
        - 			                  * (unsigned)sizeof(struct mg_connection);
        - 			mg_snprintf(NULL,
        - 			            NULL, /* No truncation check for error buffers */
        -@@ -21026,7 +21777,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 
        - #if defined(ALTERNATIVE_QUEUE)
        - 	ctx->client_wait_events =
        --	    (void **)mg_calloc_ctx(ctx->cfg_worker_threads,
        -+	    (void **)mg_calloc_ctx(ctx->cfg_max_worker_threads,
        - 	                           sizeof(ctx->client_wait_events[0]),
        - 	                           ctx);
        - 	if (ctx->client_wait_events == NULL) {
        -@@ -21036,7 +21787,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 
        - 		if (error != NULL) {
        - 			error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
        --			error->code_sub = (unsigned)ctx->cfg_worker_threads
        -+			error->code_sub = (unsigned)ctx->cfg_max_worker_threads
        - 			                  * (unsigned)sizeof(ctx->client_wait_events[0]);
        - 			mg_snprintf(NULL,
        - 			            NULL, /* No truncation check for error buffers */
        -@@ -21052,7 +21803,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 	}
        - 
        - 	ctx->client_socks =
        --	    (struct socket *)mg_calloc_ctx(ctx->cfg_worker_threads,
        -+	    (struct socket *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
        - 	                                   sizeof(ctx->client_socks[0]),
        - 	                                   ctx);
        - 	if (ctx->client_socks == NULL) {
        -@@ -21063,7 +21814,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 
        - 		if (error != NULL) {
        - 			error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
        --			error->code_sub = (unsigned)ctx->cfg_worker_threads
        -+			error->code_sub = (unsigned)ctx->cfg_max_worker_threads
        - 			                  * (unsigned)sizeof(ctx->client_socks[0]);
        - 			mg_snprintf(NULL,
        - 			            NULL, /* No truncation check for error buffers */
        -@@ -21078,7 +21829,7 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 		return NULL;
        - 	}
        - 
        --	for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
        -+	for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) {
        - 		ctx->client_wait_events[i] = event_create();
        - 		if (ctx->client_wait_events[i] == 0) {
        - 			const char *err_msg = "Error creating worker event %i";
        -@@ -21142,23 +21893,18 @@ mg_start2(struct mg_init_data *init, struct mg_error_data *error)
        - 	ctx->context_type = CONTEXT_SERVER; /* server context */
        - 
        - 	/* Start worker threads */
        --	for (i = 0; i < ctx->cfg_worker_threads; i++) {
        -+	for (i = 0; (int)i < prespawnthreadcount; i++) {
        - 		/* worker_thread sets up the other fields */
        --		ctx->worker_connections[i].phys_ctx = ctx;
        --		if (mg_start_thread_with_id(worker_thread,
        --		                            &ctx->worker_connections[i],
        --		                            &ctx->worker_threadids[i])
        --		    != 0) {
        --
        -+		if (mg_start_worker_thread(ctx, 0) != 0) {
        - 			long error_no = (long)ERRNO;
        - 
        - 			/* thread was not created */
        --			if (i > 0) {
        -+			if (ctx->spawned_worker_threads > 0) {
        - 				/* If the second, third, ... thread cannot be created, set a
        - 				 * warning, but keep running. */
        - 				mg_cry_ctx_internal(ctx,
        - 				                    "Cannot start worker thread %i: error %ld",
        --				                    i + 1,
        -+				                    ctx->spawned_worker_threads + 1,
        - 				                    error_no);
        - 
        - 				/* If the server initialization should stop here, all
        -@@ -21348,7 +22094,7 @@ mg_start_domain2(struct mg_context *ctx,
        - 		}
        - 	}
        - 
        --	new_dom->handlers = NULL;
        -+	new_dom->handlers = ctx->dd.handlers;
        - 	new_dom->next = NULL;
        - 	new_dom->nonce_count = 0;
        - 	new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31);
        -@@ -21357,7 +22103,7 @@ mg_start_domain2(struct mg_context *ctx,
        - 	new_dom->shared_lua_websockets = NULL;
        - #endif
        - 
        --#if !defined(NO_SSL) && !defined(USE_MBEDTLS)
        -+#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)
        - 	if (!init_ssl_ctx(ctx, new_dom)) {
        - 		/* Init SSL failed */
        - 		if (error != NULL) {
        -@@ -21436,7 +22182,7 @@ mg_check_feature(unsigned feature)
        - #if !defined(NO_FILES)
        - 	                                    | MG_FEATURES_FILES
        - #endif
        --#if !defined(NO_SSL) || defined(USE_MBEDTLS)
        -+#if !defined(NO_SSL) || defined(USE_MBEDTLS) || defined(USE_GNUTLS)
        - 	                                    | MG_FEATURES_SSL
        - #endif
        - #if !defined(NO_CGI)
        -@@ -21593,13 +22339,23 @@ mg_get_system_info(char *buffer, int buflen)
        - 		            (unsigned)si.dwNumberOfProcessors,
        - 		            (unsigned)si.dwActiveProcessorMask);
        - 		system_info_length += mg_str_append(&buffer, end, block);
        --#elif defined(__ZEPHYR__)
        -+#elif defined(__rtems__)
        - 		mg_snprintf(NULL,
        - 		            NULL,
        - 		            block,
        - 		            sizeof(block),
        - 		            ",%s\"os\" : \"%s %s\"",
        - 		            eol,
        -+		            "RTEMS",
        -+		            rtems_version());
        -+		system_info_length += mg_str_append(&buffer, end, block);
        -+#elif defined(__ZEPHYR__)
        -+		mg_snprintf(NULL,
        -+		            NULL,
        -+		            block,
        -+		            sizeof(block),
        -+		            ",%s\"os\" : \"%s\"",
        -+		            eol,
        - 		            "Zephyr OS",
        - 		            ZEPHYR_VERSION);
        - 		system_info_length += mg_str_append(&buffer, end, block);
        -@@ -22088,7 +22844,7 @@ mg_get_connection_info(const struct mg_context *ctx,
        - 		return 0;
        - 	}
        - 
        --	if ((unsigned)idx >= ctx->cfg_worker_threads) {
        -+	if ((unsigned)idx >= ctx->cfg_max_worker_threads) {
        - 		/* Out of range */
        - 		return 0;
        - 	}
        -@@ -22298,44 +23054,44 @@ mg_get_connection_info(const struct mg_context *ctx,
        - 	return (int)connection_info_length;
        - }
        - 
        -+
        - #if 0
        --/* Get handler information. It can be printed or stored by the caller.
        -- * Return the size of available information. */
        -+/* Get handler information. Not fully implemented. Is it required? */
        - CIVETWEB_API int
        - mg_get_handler_info(struct mg_context *ctx,
        --                       char *buffer,
        --                       int buflen)
        -+	char *buffer,
        -+	int buflen)
        - {
        --    int handler_info_len = 0;
        --    struct mg_handler_info *tmp_rh;
        --    mg_lock_context(ctx);
        --
        --    for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
        --
        --        if (buflen > handler_info_len+ tmp_rh->uri_len) {
        --        memcpy(buffer+handler_info_len, tmp_rh->uri, tmp_rh->uri_len);
        --        }
        --        handler_info_len += tmp_rh->uri_len;
        --
        --        switch (tmp_rh->handler_type) {
        --            case REQUEST_HANDLER:
        --                (void)tmp_rh->handler;
        --            break;
        --            case WEBSOCKET_HANDLER:
        --        (void)tmp_rh->connect_handler;
        --       (void) tmp_rh->ready_handler;
        --       (void) tmp_rh->data_handler;
        --       (void) tmp_rh->close_handler;
        --            break;
        --            case AUTH_HANDLER:
        --             (void) tmp_rh->auth_handler;
        --            break;
        --        }
        --        (void)cbdata;
        --    }
        --
        --    mg_unlock_context(ctx);
        --    return handler_info_len;
        -+	int handler_info_len = 0;
        -+	struct mg_handler_info *tmp_rh;
        -+	mg_lock_context(ctx);
        -+
        -+	for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
        -+
        -+		if (buflen > handler_info_len + tmp_rh->uri_len) {
        -+			memcpy(buffer + handler_info_len, tmp_rh->uri, tmp_rh->uri_len);
        -+		}
        -+		handler_info_len += tmp_rh->uri_len;
        -+
        -+		switch (tmp_rh->handler_type) {
        -+		case REQUEST_HANDLER:
        -+			(void)tmp_rh->handler;
        -+			break;
        -+		case WEBSOCKET_HANDLER:
        -+			(void)tmp_rh->connect_handler;
        -+			(void)tmp_rh->ready_handler;
        -+			(void)tmp_rh->data_handler;
        -+			(void)tmp_rh->close_handler;
        -+			break;
        -+		case AUTH_HANDLER:
        -+			(void)tmp_rh->auth_handler;
        -+			break;
        -+		}
        -+		(void)cbdata;
        -+	}
        -+
        -+	mg_unlock_context(ctx);
        -+	return handler_info_len;
        - }
        - #endif
        - #endif
        -diff --git a/src/handle_form.inl b/src/handle_form.inl
        -index be477a05..bc76f7d3 100644
        ---- a/src/handle_form.inl
        -+++ b/src/handle_form.inl
        -@@ -1,4 +1,4 @@
        --/* Copyright (c) 2016-2021 the Civetweb developers
        -+/* Copyright (c) 2016-2025 the Civetweb developers
        -  *
        -  * Permission is hereby granted, free of charge, to any person obtaining a copy
        -  * of this software and associated documentation files (the "Software"), to deal
        -@@ -39,7 +39,7 @@ url_encoded_field_found(const struct mg_connection *conn,
        - 	    mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
        - 
        - 	if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
        --		return MG_FORM_FIELD_STORAGE_SKIP;
        -+		return MG_FORM_FIELD_STORAGE_ABORT;
        - 	}
        - 
        - 	if (filename) {
        -@@ -53,7 +53,7 @@ url_encoded_field_found(const struct mg_connection *conn,
        - 		    || (filename_dec_len < 0)) {
        - 			/* Log error message and skip this field. */
        - 			mg_cry_internal(conn, "%s: Cannot decode filename", __func__);
        --			return MG_FORM_FIELD_STORAGE_SKIP;
        -+			return MG_FORM_FIELD_STORAGE_ABORT;
        - 		}
        - 		remove_dot_segments(filename_dec);
        - 
        -@@ -95,6 +95,7 @@ url_encoded_field_get(
        -     struct mg_form_data_handler *fdh)
        - {
        - 	char key_dec[1024];
        -+	int key_dec_len;
        - 
        - 	char *value_dec = (char *)mg_malloc_ctx(*value_len + 1, conn->phys_ctx);
        - 	int value_dec_len, ret;
        -@@ -108,7 +109,8 @@ url_encoded_field_get(
        - 		return MG_FORM_FIELD_STORAGE_ABORT;
        - 	}
        - 
        --	mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
        -+	key_dec_len =
        -+	    mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
        - 
        - 	if (*value_len >= 2 && value[*value_len - 2] == '%')
        - 		*value_len -= 2;
        -@@ -117,6 +119,11 @@ url_encoded_field_get(
        - 	value_dec_len = mg_url_decode(
        - 	    value, (int)*value_len, value_dec, ((int)*value_len) + 1, 1);
        - 
        -+	if ((key_dec_len < 0) || (value_dec_len < 0)) {
        -+		mg_free(value_dec);
        -+		return MG_FORM_FIELD_STORAGE_ABORT;
        -+	}
        -+
        - 	ret = fdh->field_get(key_dec,
        - 	                     value_dec,
        - 	                     (size_t)value_dec_len,
        -@@ -136,9 +143,14 @@ unencoded_field_get(const struct mg_connection *conn,
        -                     struct mg_form_data_handler *fdh)
        - {
        - 	char key_dec[1024];
        -+	int key_dec_len;
        - 	(void)conn;
        - 
        --	mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
        -+	key_dec_len =
        -+	    mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
        -+	if (key_dec_len < 0) {
        -+		return MG_FORM_FIELD_STORAGE_ABORT;
        -+	}
        - 
        - 	return fdh->field_get(key_dec, value, value_len, fdh->user_data);
        - }
        -@@ -162,14 +174,17 @@ search_boundary(const char *buf,
        -                 const char *boundary,
        -                 size_t boundary_len)
        - {
        --	/* We must do a binary search here, not a string search, since the buffer
        --	 * may contain '\x00' bytes, if binary data is transferred. */
        --	int clen = (int)buf_len - (int)boundary_len - 4;
        -+	char *boundary_start = "\r\n--";
        -+	size_t boundary_start_len = strlen(boundary_start);
        -+
        -+	/* We must do a binary search here, not a string search, since the
        -+	 * buffer may contain '\x00' bytes, if binary data is transferred. */
        -+	int clen = (int)buf_len - (int)boundary_len - boundary_start_len;
        - 	int i;
        - 
        - 	for (i = 0; i <= clen; i++) {
        --		if (!memcmp(buf + i, "\r\n--", 4)) {
        --			if (!memcmp(buf + i + 4, boundary, boundary_len)) {
        -+		if (!memcmp(buf + i, boundary_start, boundary_start_len)) {
        -+			if (!memcmp(buf + i + boundary_start_len, boundary, boundary_len)) {
        - 				return buf + i;
        - 			}
        - 		}
        -@@ -185,9 +200,10 @@ mg_handle_form_request(struct mg_connection *conn,
        - 	char path[512];
        - 	char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */
        - 	int field_storage;
        --	int buf_fill = 0;
        -+	size_t buf_fill = 0;
        - 	int r;
        - 	int field_count = 0;
        -+	int abort_read = 0;
        - 	struct mg_file fstore = STRUCT_FILE_INITIALIZER;
        - 	int64_t file_size = 0; /* init here, to a avoid a false positive
        - 	                         "uninitialized variable used" warning */
        -@@ -278,6 +294,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 				    conn, data, (size_t)keylen, val, (size_t *)&vallen, fdh);
        - 				if (r == MG_FORM_FIELD_HANDLE_ABORT) {
        - 					/* Stop request handling */
        -+					abort_read = 1;
        - 					break;
        - 				}
        - 				if (r == MG_FORM_FIELD_HANDLE_NEXT) {
        -@@ -320,6 +337,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 							r = field_stored(conn, path, file_size, fdh);
        - 							if (r == MG_FORM_FIELD_HANDLE_ABORT) {
        - 								/* Stop request handling */
        -+								abort_read = 1;
        - 								break;
        - 							}
        - 
        -@@ -358,6 +376,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 			if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
        - 			    == MG_FORM_FIELD_STORAGE_ABORT) {
        - 				/* Stop parsing the request */
        -+				abort_read = 1;
        - 				break;
        - 			}
        - 
        -@@ -386,7 +405,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 		 * Here we use "POST", and read the data from the request body.
        - 		 * The data read on the fly, so it is not required to buffer the
        - 		 * entire request in memory before processing it. */
        --		for (;;) {
        -+		while (!abort_read) {
        - 			const char *val;
        - 			const char *next;
        - 			ptrdiff_t keylen, vallen;
        -@@ -394,10 +413,10 @@ mg_handle_form_request(struct mg_connection *conn,
        - 			int end_of_key_value_pair_found = 0;
        - 			int get_block;
        - 
        --			if ((size_t)buf_fill < (sizeof(buf) - 1)) {
        -+			if (buf_fill < (sizeof(buf) - 1)) {
        - 
        --				size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
        --				r = mg_read(conn, buf + (size_t)buf_fill, to_read);
        -+				size_t to_read = sizeof(buf) - 1 - buf_fill;
        -+				r = mg_read(conn, buf + buf_fill, to_read);
        - 				if ((r < 0) || ((r == 0) && all_data_read)) {
        - 					/* read error */
        - 					return -1;
        -@@ -440,6 +459,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 			if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
        - 			    == MG_FORM_FIELD_STORAGE_ABORT) {
        - 				/* Stop parsing the request */
        -+				abort_read = 1;
        - 				break;
        - 			}
        - 
        -@@ -468,6 +488,15 @@ mg_handle_form_request(struct mg_connection *conn,
        - 				} else {
        - 					vallen = (ptrdiff_t)strlen(val);
        - 					end_of_key_value_pair_found = all_data_read;
        -+					if ((buf + buf_fill) > (val + vallen)) {
        -+						/* Avoid DoS attacks by having a zero byte in the middle
        -+						 * of a request that is supposed to be URL encoded.
        -+						 * Since this request is certainly invalid, according to
        -+						 * the protocol
        -+						 * specification, stop processing it. Fixes #1348 */
        -+						abort_read = 1;
        -+						break;
        -+					}
        - 				}
        - 
        - 				if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
        -@@ -489,6 +518,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 					get_block++;
        - 					if (r == MG_FORM_FIELD_HANDLE_ABORT) {
        - 						/* Stop request handling */
        -+						abort_read = 1;
        - 						break;
        - 					}
        - 					if (r == MG_FORM_FIELD_HANDLE_NEXT) {
        -@@ -526,11 +556,11 @@ mg_handle_form_request(struct mg_connection *conn,
        - 					        buf + (size_t)used,
        - 					        sizeof(buf) - (size_t)used);
        - 					next = buf;
        --					buf_fill -= (int)used;
        --					if ((size_t)buf_fill < (sizeof(buf) - 1)) {
        -+					buf_fill -= used;
        -+					if (buf_fill < (sizeof(buf) - 1)) {
        - 
        --						size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
        --						r = mg_read(conn, buf + (size_t)buf_fill, to_read);
        -+						size_t to_read = sizeof(buf) - 1 - buf_fill;
        -+						r = mg_read(conn, buf + buf_fill, to_read);
        - 						if ((r < 0) || ((r == 0) && all_data_read)) {
        - #if !defined(NO_FILESYSTEMS)
        - 							/* read error */
        -@@ -557,7 +587,6 @@ mg_handle_form_request(struct mg_connection *conn,
        - 						val = buf;
        - 					}
        - 				}
        --
        - 			} while (!end_of_key_value_pair_found);
        - 
        - #if !defined(NO_FILESYSTEMS)
        -@@ -568,6 +597,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 					r = field_stored(conn, path, file_size, fdh);
        - 					if (r == MG_FORM_FIELD_HANDLE_ABORT) {
        - 						/* Stop request handling */
        -+						abort_read = 1;
        - 						break;
        - 					}
        - 				} else {
        -@@ -581,7 +611,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 			}
        - #endif /* NO_FILESYSTEMS */
        - 
        --			if (all_data_read && (buf_fill == 0)) {
        -+			if ((all_data_read && (buf_fill == 0)) || abort_read) {
        - 				/* nothing more to process */
        - 				break;
        - 			}
        -@@ -589,7 +619,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 			/* Proceed to next entry */
        - 			used = next - buf;
        - 			memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
        --			buf_fill -= (int)used;
        -+			buf_fill -= used;
        - 		}
        - 
        - 		return field_count;
        -@@ -624,6 +654,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 		}
        - 
        - 		/* Copy boundary string to variable "boundary" */
        -+		/* fbeg is pointer to start of value of boundary */
        - 		fbeg = content_type + bl + 9;
        - 		bl = strlen(fbeg);
        - 		boundary = (char *)mg_malloc(bl + 1);
        -@@ -678,12 +709,12 @@ mg_handle_form_request(struct mg_connection *conn,
        - 		for (part_no = 0;; part_no++) {
        - 			size_t towrite, fnlen, n;
        - 			int get_block;
        --			size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
        -+			size_t to_read = sizeof(buf) - 1 - buf_fill;
        - 
        - 			/* Unused without filesystems */
        - 			(void)n;
        - 
        --			r = mg_read(conn, buf + (size_t)buf_fill, to_read);
        -+			r = mg_read(conn, buf + buf_fill, to_read);
        - 			if ((r < 0) || ((r == 0) && all_data_read)) {
        - 				/* read error */
        - 				mg_free(boundary);
        -@@ -701,43 +732,74 @@ mg_handle_form_request(struct mg_connection *conn,
        - 				return -1;
        - 			}
        - 
        -+			/* @see https://www.rfc-editor.org/rfc/rfc2046.html#section-5.1.1
        -+			 *
        -+			 * multipart-body := [preamble CRLF]
        -+			 *     dash-boundary transport-padding CRLF
        -+			 *     body-part *encapsulation
        -+			 *     close-delimiter transport-padding
        -+			 *     [CRLF epilogue]
        -+			 */
        -+
        - 			if (part_no == 0) {
        --				int d = 0;
        --				while ((d < buf_fill) && (buf[d] != '-')) {
        --					d++;
        -+				size_t preamble_length = 0;
        -+				/* skip over the preamble until we find a complete boundary
        -+				 * limit the preamble length to prevent abuse */
        -+				/* +2 for the -- preceding the boundary */
        -+				while (preamble_length < 1024
        -+				       && (preamble_length < buf_fill - bl)
        -+				       && strncmp(buf + preamble_length + 2, boundary, bl)) {
        -+					preamble_length++;
        - 				}
        --				if ((d > 0) && (buf[d] == '-')) {
        --					memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d);
        --					buf_fill -= d;
        -+				/* reset the start of buf to remove the preamble */
        -+				if (0 == strncmp(buf + preamble_length + 2, boundary, bl)) {
        -+					memmove(buf,
        -+					        buf + preamble_length,
        -+					        (unsigned)buf_fill - (unsigned)preamble_length);
        -+					buf_fill -= preamble_length;
        - 					buf[buf_fill] = 0;
        - 				}
        - 			}
        - 
        --			if (buf[0] != '-' || buf[1] != '-') {
        -+			/* either it starts with a boundary and it's fine, or it's malformed
        -+			 * because:
        -+			 * - the preamble was longer than accepted
        -+			 * - couldn't find a boundary at all in the body
        -+			 * - didn't have a terminating boundary */
        -+			if (buf_fill < (bl + 2) || strncmp(buf, "--", 2)
        -+			    || strncmp(buf + 2, boundary, bl)) {
        - 				/* Malformed request */
        - 				mg_free(boundary);
        - 				return -1;
        - 			}
        --			if (0 != strncmp(buf + 2, boundary, bl)) {
        --				/* Malformed request */
        --				mg_free(boundary);
        --				return -1;
        -+
        -+			/* skip the -- */
        -+			char *boundary_start = buf + 2;
        -+			size_t transport_padding = 0;
        -+			while (boundary_start[bl + transport_padding] == ' '
        -+			       || boundary_start[bl + transport_padding] == '\t') {
        -+				transport_padding++;
        - 			}
        --			if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
        --				/* Every part must end with \r\n, if there is another part.
        --				 * The end of the request has an extra -- */
        --				if (((size_t)buf_fill != (size_t)(bl + 6))
        --				    || (strncmp(buf + bl + 2, "--\r\n", 4))) {
        -+			char *boundary_end = boundary_start + bl + transport_padding;
        -+
        -+			/* after the transport padding, if the boundary isn't
        -+			 * immediately followed by a \r\n then it is either... */
        -+			if (strncmp(boundary_end, "\r\n", 2)) {
        -+				/* ...the final boundary, and it is followed by --, (in which
        -+				 * case it's the end of the request) or it's a malformed
        -+				 * request */
        -+				if (strncmp(boundary_end, "--", 2)) {
        - 					/* Malformed request */
        - 					mg_free(boundary);
        - 					return -1;
        - 				}
        --				/* End of the request */
        -+				/* Ingore any epilogue here */
        - 				break;
        - 			}
        - 
        -+			/* skip the \r\n */
        -+			hbuf = boundary_end + 2;
        - 			/* Next, we need to get the part header: Read until \r\n\r\n */
        --			hbuf = buf + bl + 4;
        - 			hend = strstr(hbuf, "\r\n\r\n");
        - 			if (!hend) {
        - 				/* Malformed request */
        -@@ -937,6 +999,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 					get_block++;
        - 					if (r == MG_FORM_FIELD_HANDLE_ABORT) {
        - 						/* Stop request handling */
        -+						abort_read = 1;
        - 						break;
        - 					}
        - 					if (r == MG_FORM_FIELD_HANDLE_NEXT) {
        -@@ -965,12 +1028,12 @@ mg_handle_form_request(struct mg_connection *conn,
        - #endif /* NO_FILESYSTEMS */
        - 
        - 				memmove(buf, hend + towrite, bl + 4);
        --				buf_fill = (int)(bl + 4);
        -+				buf_fill = bl + 4;
        - 				hend = buf;
        - 
        - 				/* Read new data */
        --				to_read = sizeof(buf) - 1 - (size_t)buf_fill;
        --				r = mg_read(conn, buf + (size_t)buf_fill, to_read);
        -+				to_read = sizeof(buf) - 1 - buf_fill;
        -+				r = mg_read(conn, buf + buf_fill, to_read);
        - 				if ((r < 0) || ((r == 0) && all_data_read)) {
        - #if !defined(NO_FILESYSTEMS)
        - 					/* read error */
        -@@ -989,7 +1052,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 				/* buf_fill is at least 8 here */
        - 
        - 				/* Find boundary */
        --				next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
        -+				next = search_boundary(buf, buf_fill, boundary, bl);
        - 
        - 				if (!next && (r == 0)) {
        - 					/* incomplete request */
        -@@ -1011,6 +1074,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 				                        fdh);
        - 				if (r == MG_FORM_FIELD_HANDLE_ABORT) {
        - 					/* Stop request handling */
        -+					abort_read = 1;
        - 					break;
        - 				}
        - 				if (r == MG_FORM_FIELD_HANDLE_NEXT) {
        -@@ -1039,6 +1103,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 							r = field_stored(conn, path, file_size, fdh);
        - 							if (r == MG_FORM_FIELD_HANDLE_ABORT) {
        - 								/* Stop request handling */
        -+								abort_read = 1;
        - 								break;
        - 							}
        - 						} else {
        -@@ -1057,6 +1122,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 			if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
        - 			    == MG_FORM_FIELD_STORAGE_ABORT) {
        - 				/* Stop parsing the request */
        -+				abort_read = 1;
        - 				break;
        - 			}
        - 
        -@@ -1064,7 +1130,7 @@ mg_handle_form_request(struct mg_connection *conn,
        - 			if (next) {
        - 				used = next - buf + 2;
        - 				memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
        --				buf_fill -= (int)used;
        -+				buf_fill -= used;
        - 			} else {
        - 				buf_fill = 0;
        - 			}
        -diff --git a/src/http2.inl b/src/http2.inl
        -index cea695ed..a4bfd8d0 100644
        ---- a/src/http2.inl
        -+++ b/src/http2.inl
        -@@ -1442,7 +1442,7 @@ handle_http2(struct mg_connection *conn)
        - 					    hpack_decode(buf, &i, (int)bytes_read, conn->phys_ctx);
        - 					CHECK_LEAK_HDR_ALLOC(key);
        - 					if (!key) {
        --						DEBUG_TRACE("HTTP2 key decoding error");
        -+						DEBUG_TRACE("%s", "HTTP2 key decoding error");
        - 						goto clean_http2;
        - 					}
        - 				} else if (/*(idx >= 15) &&*/ (idx <= 61)) {
        -@@ -1509,7 +1509,7 @@ handle_http2(struct mg_connection *conn)
        - 					                   conn->phys_ctx); /* leak? */
        - 					CHECK_LEAK_HDR_ALLOC(val);
        - 					if (!val) {
        --						DEBUG_TRACE("HTTP2 value decoding error");
        -+						DEBUG_TRACE("%s", "HTTP2 value decoding error");
        - 						mg_free((void *)key);
        - 						goto clean_http2;
        - 					}
        -diff --git a/src/main.c b/src/main.c
        -index 66510d0f..be859feb 100644
        ---- a/src/main.c
        -+++ b/src/main.c
        -@@ -41,7 +41,7 @@
        - #pragma GCC diagnostic push
        - #pragma GCC diagnostic ignored "-Wreserved-id-macro"
        - #endif
        --#if !defined(_XOPEN_SOURCE)
        -+#if !defined(_XOPEN_SOURCE) && !defined(USE_COCOA)
        - #define _XOPEN_SOURCE 600 /* For PATH_MAX on linux */
        - /* This should also be sufficient for "realpath", according to
        -  * http://man7.org/linux/man-pages/man3/realpath.3.html, but in
        -@@ -1142,6 +1142,7 @@ sanitize_options(const char *options[] /* server options */,
        - 	int ok = 1;
        - 	/* Make sure we have absolute paths for files and directories */
        - 	set_absolute_path(options, "document_root", arg0);
        -+	set_absolute_path(options, "fallback_document_root", arg0);
        - 	set_absolute_path(options, "put_delete_auth_file", arg0);
        - 	set_absolute_path(options, "cgi_interpreter", arg0);
        - 	set_absolute_path(options, "access_log_file", arg0);
        -@@ -1155,6 +1156,8 @@ sanitize_options(const char *options[] /* server options */,
        - 	/* Make extra verification for certain options */
        - 	if (!verify_existence(options, "document_root", 1))
        - 		ok = 0;
        -+	if (!verify_existence(options, "fallback_document_root", 1))
        -+		ok = 0;
        - 	if (!verify_existence(options, "cgi_interpreter", 0))
        - 		ok = 0;
        - 	if (!verify_existence(options, "ssl_certificate", 0))
        -@@ -2474,7 +2477,7 @@ show_settings_dialog(void)
        - 
        - 
        - static void
        --change_password_file()
        -+change_password_file(void)
        - {
        - 	/* Parameter for size/format tuning of the dialog */
        - 	short HEIGHT = 15;
        -@@ -2685,7 +2688,7 @@ sysinfo_reload(struct dlg_proc_param *prm)
        - 
        - 
        - int
        --show_system_info()
        -+show_system_info(void)
        - {
        - 	/* Parameter for size/format tuning of the dialog */
        - 	short HEIGHT = 15;
        -@@ -2793,8 +2796,9 @@ manage_service(int action)
        - 		show_error();
        - 	} else if (action == ID_INSTALL_SERVICE) {
        - 		path[sizeof(path) - 1] = 0;
        --		GetModuleFileName(NULL, path, sizeof(path) - 1);
        --		strncat(path, " ", sizeof(path) - 1 - strlen(path));
        -+		path[0]='"';
        -+		GetModuleFileName(NULL, path+1, sizeof(path) - 3);
        -+		strncat(path, "\" ", sizeof(path) - 2 - strlen(path));
        - 		strncat(path, service_magic_argument, sizeof(path) - 1 - strlen(path));
        - 		hService = CreateService(hSCM,
        - 		                         service_name,
        -diff --git a/src/match.inl b/src/match.inl
        -index a5011f57..34ee00ef 100644
        ---- a/src/match.inl
        -+++ b/src/match.inl
        -@@ -47,8 +47,8 @@ mg_match_impl(const char *pat,
        - 				/* Advance as long as there are ? */
        - 				i_pat++;
        - 				i_str++;
        --			} while ((pat[i_pat] == '?') && (str[i_str] != '\0')
        --			         && (str[i_str] != '/') && (i_pat < pat_len));
        -+			} while ((i_pat < pat_len) && (pat[i_pat] == '?')
        -+			         && (str[i_str] != '\0') && (str[i_str] != '/'));
        - 
        - 			/* If we have a match context, add the substring we just found */
        - 			if (mcx) {
        -@@ -72,7 +72,7 @@ mg_match_impl(const char *pat,
        - 			ptrdiff_t ret;
        - 
        - 			i_pat++;
        --			if ((pat[i_pat] == '*') && (i_pat < pat_len)) {
        -+			if ((i_pat < pat_len) && (pat[i_pat] == '*')) {
        - 				/* Pattern ** matches all */
        - 				i_pat++;
        - 				len = strlen(str + i_str);
        -diff --git a/src/mod_gnutls.inl b/src/mod_gnutls.inl
        -new file mode 100644
        -index 00000000..f765362b
        ---- /dev/null
        -+++ b/src/mod_gnutls.inl
        -@@ -0,0 +1,240 @@
        -+#if defined(USE_GNUTLS) // USE_GNUTLS used with NO_SSL
        -+
        -+#include 
        -+#include 
        -+
        -+typedef struct {
        -+	gnutls_session_t sess;
        -+} SSL;
        -+typedef struct {
        -+	gnutls_certificate_credentials_t cred;
        -+	gnutls_priority_t prio;
        -+} SSL_CTX;
        -+
        -+
        -+/* public api */
        -+CIVETWEB_API int gtls_sslctx_init(SSL_CTX *ctx, const char *crt);
        -+CIVETWEB_API void gtls_sslctx_uninit(SSL_CTX *ctx);
        -+CIVETWEB_API void gtls_ssl_close(SSL *ssl);
        -+CIVETWEB_API int gtls_ssl_accept(SSL **ssl,
        -+                                 SSL_CTX *ssl_ctx,
        -+                                 int sock,
        -+                                 struct mg_context *phys_ctx);
        -+CIVETWEB_API int gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len);
        -+CIVETWEB_API int gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len);
        -+
        -+
        -+CIVETWEB_API int
        -+gtls_sslctx_init(SSL_CTX *ctx, const char *crt)
        -+{
        -+	int rc;
        -+
        -+	if (ctx == NULL || crt == NULL) {
        -+		return -1;
        -+	}
        -+
        -+	DEBUG_TRACE("%s", "Initializing GnuTLS SSL");
        -+
        -+	rc = gnutls_certificate_allocate_credentials(&ctx->cred);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("Failed to allocate credentials (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_priority_init(&ctx->prio, NULL, NULL);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("Failed to allocate priority cache (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_certificate_set_x509_key_file2(ctx->cred,
        -+	                                           crt,
        -+	                                           crt,
        -+	                                           GNUTLS_X509_FMT_PEM,
        -+	                                           NULL,
        -+	                                           GNUTLS_PKCS_PLAIN
        -+	                                               | GNUTLS_PKCS_NULL_PASSWORD);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("TLS parse crt/key file failed (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	return 0;
        -+
        -+failed:
        -+	gtls_sslctx_uninit(ctx);
        -+
        -+	return -1;
        -+}
        -+
        -+
        -+CIVETWEB_API void
        -+gtls_sslctx_uninit(SSL_CTX *ctx)
        -+{
        -+	if (ctx != NULL) {
        -+		gnutls_certificate_free_credentials(ctx->cred);
        -+		gnutls_priority_deinit(ctx->prio);
        -+		ctx->cred = NULL;
        -+		ctx->prio = NULL;
        -+	}
        -+}
        -+
        -+
        -+CIVETWEB_API int
        -+gtls_ssl_accept(SSL **ssl,
        -+                SSL_CTX *ssl_ctx,
        -+                int sock,
        -+                struct mg_context *phys_ctx)
        -+{
        -+	int rc;
        -+
        -+	if (ssl == NULL || ssl_ctx == NULL) {
        -+		return -1;
        -+	}
        -+
        -+	DEBUG_TRACE("TLS accept processing %p", ssl);
        -+
        -+	*ssl = (SSL *)mg_calloc_ctx(1, sizeof(SSL), phys_ctx);
        -+	if (*ssl == NULL) {
        -+		DEBUG_TRACE("Failed to allocate memory for session %zu", sizeof(SSL));
        -+		return -1;
        -+	}
        -+
        -+	rc = gnutls_init(&(*ssl)->sess, GNUTLS_SERVER);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("Failed to initialize session (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_priority_set((*ssl)->sess, ssl_ctx->prio);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("TLS set priortities failed (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	rc = gnutls_credentials_set((*ssl)->sess,
        -+	                            GNUTLS_CRD_CERTIFICATE,
        -+	                            ssl_ctx->cred);
        -+	if (rc != GNUTLS_E_SUCCESS) {
        -+		DEBUG_TRACE("TLS set credentials failed (%d): %s",
        -+		            rc,
        -+		            gnutls_strerror(rc));
        -+		goto failed;
        -+	}
        -+
        -+	gnutls_certificate_send_x509_rdn_sequence((*ssl)->sess, 1);
        -+	gnutls_certificate_server_set_request((*ssl)->sess, GNUTLS_CERT_IGNORE);
        -+	gnutls_handshake_set_timeout((*ssl)->sess,
        -+	                             GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
        -+	gnutls_transport_set_int((*ssl)->sess, sock);
        -+
        -+	while ((rc = gnutls_handshake((*ssl)->sess)) != GNUTLS_E_SUCCESS) {
        -+		if (gnutls_error_is_fatal(rc)) {
        -+			if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
        -+				DEBUG_TRACE("TLS fatal alert received: %s",
        -+				            gnutls_alert_get_name(
        -+				                gnutls_alert_get((*ssl)->sess)));
        -+			} else {
        -+				DEBUG_TRACE("TLS handshake failed (%d): %s",
        -+				            rc,
        -+				            gnutls_strerror(rc));
        -+			}
        -+
        -+			goto failed;
        -+		}
        -+	}
        -+
        -+	DEBUG_TRACE("TLS connection %p accepted", *ssl);
        -+
        -+	return 0;
        -+
        -+failed:
        -+	gnutls_deinit((*ssl)->sess);
        -+	mg_free(*ssl);
        -+	*ssl = NULL;
        -+
        -+	return -1;
        -+}
        -+
        -+
        -+CIVETWEB_API void
        -+gtls_ssl_close(SSL *ssl)
        -+{
        -+	int rc;
        -+
        -+	if (ssl == NULL) {
        -+		return;
        -+	}
        -+
        -+	while ((rc = gnutls_bye(ssl->sess, GNUTLS_SHUT_RDWR)) != GNUTLS_E_SUCCESS) {
        -+		switch (rc) {
        -+		case GNUTLS_E_AGAIN: /* fall through */
        -+		case GNUTLS_E_INTERRUPTED:
        -+			continue;
        -+		default: /* should actually never happen */
        -+			break;
        -+		}
        -+	}
        -+
        -+	DEBUG_TRACE("TLS connection %p closed", ssl);
        -+	gnutls_deinit(ssl->sess);
        -+	mg_free(ssl);
        -+}
        -+
        -+
        -+CIVETWEB_API int
        -+gtls_ssl_read(SSL *ssl, unsigned char *buf, size_t len)
        -+{
        -+	ssize_t rc;
        -+
        -+	if (ssl == NULL) {
        -+		return GNUTLS_E_INVALID_SESSION;
        -+	}
        -+
        -+	while ((rc = gnutls_record_recv(ssl->sess, buf, len)) < 0) {
        -+		switch (rc) {
        -+		case GNUTLS_E_AGAIN: /* fall through */
        -+		case GNUTLS_E_INTERRUPTED:
        -+			continue;
        -+		default:
        -+			break;
        -+		}
        -+	}
        -+	/* DEBUG_TRACE("gnutls_record_recv: %d", rc); */
        -+	return (int)rc;
        -+}
        -+
        -+
        -+CIVETWEB_API int
        -+gtls_ssl_write(SSL *ssl, const unsigned char *buf, size_t len)
        -+{
        -+	ssize_t rc;
        -+
        -+	if (ssl == NULL) {
        -+		return GNUTLS_E_INVALID_SESSION;
        -+	}
        -+
        -+	while ((rc = gnutls_record_send(ssl->sess, buf, len)) < 0) {
        -+		switch (rc) {
        -+		case GNUTLS_E_AGAIN: /* fall through */
        -+		case GNUTLS_E_INTERRUPTED:
        -+			continue;
        -+		default:
        -+			break;
        -+		}
        -+	}
        -+	/* DEBUG_TRACE("gnutls_record_send: %d", rc); */
        -+	return (int)rc;
        -+}
        -+
        -+#endif /* USE_GNUTLS */
        -diff --git a/src/mod_lua.inl b/src/mod_lua.inl
        -index f1073e55..e6596b1c 100644
        ---- a/src/mod_lua.inl
        -+++ b/src/mod_lua.inl
        -@@ -10,6 +10,8 @@
        - #include "civetweb_lua.h"
        - #include "civetweb_private_lua.h"
        - 
        -+/* Prototypes */
        -+static int lua_error_handler(lua_State *L);
        - 
        - #if defined(_WIN32)
        - static void *
        -@@ -641,14 +643,26 @@ run_lsp_kepler(struct mg_connection *conn,
        - 		/* Only send a HTML header, if this is the top level page.
        - 		 * If this page is included by some mg.include calls, do not add a
        - 		 * header. */
        --		mg_printf(conn, "HTTP/1.1 200 OK\r\n");
        -+
        -+		/* Initialize a new HTTP response, either with some-predefined
        -+		 * status code (e.g. 404 if this is called from an error
        -+		 * handler) or with 200 OK */
        -+		mg_response_header_start(conn,
        -+		                         conn->status_code > 0 ? conn->status_code
        -+		                                               : 200);
        -+
        -+		/* Add additional headers */
        - 		send_no_cache_header(conn);
        - 		send_additional_header(conn);
        --		mg_printf(conn,
        --		          "Date: %s\r\n"
        --		          "Connection: close\r\n"
        --		          "Content-Type: text/html; charset=utf-8\r\n\r\n",
        --		          date);
        -+
        -+		/* Add content type */
        -+		mg_response_header_add(conn,
        -+		                       "Content-Type",
        -+		                       "text/html; charset=utf-8",
        -+		                       -1);
        -+
        -+		/* Send the HTTP response (status and all headers) */
        -+		mg_response_header_send(conn);
        - 	}
        - 
        - 	data.begin = p;
        -@@ -662,11 +676,18 @@ run_lsp_kepler(struct mg_connection *conn,
        - 		/* Syntax error or OOM.
        - 		 * Error message is pushed on stack. */
        - 		lua_pcall(L, 1, 0, 0);
        --		lua_cry(conn, lua_ok, L, "LSP", "execute"); /* XXX TODO: everywhere ! */
        -+		lua_cry(conn, lua_ok, L, "LSP Kepler", "execute");
        -+		lua_error_handler(L);
        -+		return 1;
        - 
        - 	} else {
        - 		/* Success loading chunk. Call it. */
        --		lua_pcall(L, 0, 0, 1);
        -+		lua_ok = lua_pcall(L, 0, 0, 0);
        -+		if (lua_ok != LUA_OK) {
        -+			lua_cry(conn, lua_ok, L, "LSP Kepler", "call");
        -+			lua_error_handler(L);
        -+			return 1;
        -+		}
        - 	}
        - 	return 0;
        - }
        -@@ -780,9 +801,17 @@ run_lsp_civetweb(struct mg_connection *conn,
        - 						/* Syntax error or OOM.
        - 						 * Error message is pushed on stack. */
        - 						lua_pcall(L, 1, 0, 0);
        -+						lua_cry(conn, lua_ok, L, "LSP", "execute");
        -+						lua_error_handler(L);
        -+						return 1;
        - 					} else {
        - 						/* Success loading chunk. Call it. */
        --						lua_pcall(L, 0, 0, 1);
        -+						lua_ok = lua_pcall(L, 0, 0, 0);
        -+						if (lua_ok != LUA_OK) {
        -+							lua_cry(conn, lua_ok, L, "LSP", "call");
        -+							lua_error_handler(L);
        -+							return 1;
        -+						}
        - 					}
        - 
        - 					/* Progress until after the Lua closing tag. */
        -@@ -1948,6 +1977,9 @@ struct lua_websock_data {
        - 	struct mg_connection *conn[MAX_WORKER_THREADS];
        - 	pthread_mutex_t ws_mutex;
        - };
        -+#else
        -+/* used in parameter list of prepare_lua_environment() */
        -+struct lua_websock_data;
        - #endif
        - 
        - 
        -@@ -2666,8 +2698,15 @@ static void
        - civetweb_open_lua_libs(lua_State *L)
        - {
        - 	{
        -+#if LUA_VERSION_NUM < 505
        - 		extern void luaL_openlibs(lua_State *);
        - 		luaL_openlibs(L);
        -+#else
        -+		// In Lua 5.5 and later has become a macro
        -+		extern void(
        -+		    luaL_openselectedlibs)(lua_State * L, int load, int preload);
        -+		luaL_openselectedlibs(L, ~0, 0);
        -+#endif
        - 	}
        - #if defined(USE_LUA_SQLITE3)
        - 	{
        -@@ -2769,18 +2808,39 @@ lua_error_handler(lua_State *L)
        - 
        - 	lua_getglobal(L, "mg");
        - 	if (!lua_isnil(L, -1)) {
        --		lua_getfield(L, -1, "write"); /* call mg.write() */
        -+		/* Write the error message to the error log */
        -+		lua_getfield(L, -1, "write");
        - 		lua_pushstring(L, error_msg);
        - 		lua_pushliteral(L, "\n");
        --		lua_call(L, 2, 0);
        --		IGNORE_UNUSED_RESULT(
        --		    luaL_dostring(L, "mg.write(debug.traceback(), '\\n')"));
        -+		lua_call(L, 2, 0); /* call mg.write(error_msg + \n) */
        -+		lua_pop(L, 1);     /* pop mg */
        -+
        -+		/* Get Lua traceback */
        -+		lua_getglobal(L, "debug");
        -+		lua_getfield(L, -1, "traceback");
        -+		lua_call(L, 0, 1); /* call debug.traceback() */
        -+		lua_remove(L, -2); /* remove debug */
        -+
        -+		/* Write the Lua traceback to the error log */
        -+		lua_getglobal(L, "mg");
        -+		lua_getfield(L, -1, "write");
        -+		lua_pushvalue(L, -3); /* push the traceback */
        -+
        -+		/* Only print the traceback if it is not empty */
        -+		if (strcmp(lua_tostring(L, -1), "stack traceback:") != 0) {
        -+			lua_pushliteral(L, "\n"); /* append a newline */
        -+			lua_call(L, 2, 0);        /* call mg.write(traceback + \n) */
        -+			lua_pop(L, 2);            /* pop mg and traceback */
        -+		} else {
        -+			lua_pop(L, 3); /* pop mg, traceback and error message */
        -+		}
        -+
        - 	} else {
        - 		printf("Lua error: [%s]\n", error_msg);
        - 		IGNORE_UNUSED_RESULT(
        - 		    luaL_dostring(L, "print(debug.traceback(), '\\n')"));
        - 	}
        --	/* TODO(lsm, low): leave the stack balanced */
        -+	lua_pop(L, 1); /* pop error message */
        - 
        - 	return 0;
        - }
        -@@ -2829,11 +2889,13 @@ prepare_lua_environment(struct mg_context *ctx,
        - #endif
        - 
        - 	/* Store context in the registry */
        -+#if defined(USE_WEBSOCKET)
        - 	if (ctx != NULL) {
        - 		lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
        - 		lua_pushlightuserdata(L, (void *)ctx);
        - 		lua_settable(L, LUA_REGISTRYINDEX);
        - 	}
        -+#endif /* USE_WEBSOCKET */
        - 	if (ws_conn_list != NULL) {
        - 		lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
        - 		lua_pushlightuserdata(L, (void *)ws_conn_list);
        -@@ -2905,8 +2967,8 @@ prepare_lua_environment(struct mg_context *ctx,
        - 	    || (lua_env_type == LUA_ENV_TYPE_BACKGROUND)) {
        - 		reg_function(L, "set_timeout", lwebsocket_set_timeout);
        - 		reg_function(L, "set_interval", lwebsocket_set_interval);
        --#endif
        - 	}
        -+#endif
        - 
        - 	reg_conn_function(L, "get_mime_type", lsp_get_mime_type, conn);
        - 	reg_conn_function(L, "get_option", lsp_get_option, conn);
        -@@ -2935,6 +2997,11 @@ prepare_lua_environment(struct mg_context *ctx,
        - 
        - 	if ((conn != NULL) && (conn->dom_ctx != NULL)) {
        - 		reg_string(L, "document_root", conn->dom_ctx->config[DOCUMENT_ROOT]);
        -+		if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) {
        -+			reg_string(L,
        -+			           "fallback_document_root",
        -+			           conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]);
        -+		}
        - 		reg_string(L,
        - 		           "auth_domain",
        - 		           conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
        -@@ -2943,6 +3010,11 @@ prepare_lua_environment(struct mg_context *ctx,
        - 			reg_string(L,
        - 			           "websocket_root",
        - 			           conn->dom_ctx->config[WEBSOCKET_ROOT]);
        -+			if (conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]) {
        -+				reg_string(L,
        -+				           "fallback_websocket_root",
        -+				           conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT]);
        -+			}
        - 		} else {
        - 			reg_string(L,
        - 			           "websocket_root",
        -@@ -3031,7 +3103,7 @@ mg_exec_lua_script(struct mg_connection *conn,
        - 
        - 	/* Execute a plain Lua script. */
        - 	if (path != NULL
        --	    && (L = lua_newstate(lua_allocator, (void *)(conn->phys_ctx)))
        -+	    && (L = mg_lua_newstate(lua_allocator, (void *)(conn->phys_ctx)))
        - 	           != NULL) {
        - 		prepare_lua_environment(
        - 		    conn->phys_ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE);
        -@@ -3058,9 +3130,14 @@ mg_exec_lua_script(struct mg_connection *conn,
        - 		}
        - 
        - 		if (luaL_loadfile(L, path) != 0) {
        -+			mg_send_http_error(conn, 500, "Lua error:\r\n");
        - 			lua_error_handler(L);
        - 		} else {
        --			lua_pcall(L, 0, 0, -2);
        -+			int call_status = lua_pcall(L, 0, 0, 0);
        -+			if (call_status != 0) {
        -+				mg_send_http_error(conn, 500, "Lua error:\r\n");
        -+				lua_error_handler(L);
        -+			}
        - 		}
        - 		DEBUG_TRACE("Close Lua environment %p", L);
        - 		lua_close(L);
        -@@ -3142,7 +3219,7 @@ handle_lsp_request(struct mg_connection *conn,
        - 		L = ls;
        - 	} else {
        - 		/* We need to create a Lua state. */
        --		L = lua_newstate(lua_allocator, (void *)(conn->phys_ctx));
        -+		L = mg_lua_newstate(lua_allocator, (void *)(conn->phys_ctx));
        - 		if (L == NULL) {
        - 			/* We neither got a Lua state from the command line,
        - 			 * nor did we succeed in creating our own state.
        -@@ -3288,7 +3365,7 @@ lua_websocket_new(const char *script, struct mg_connection *conn)
        - 		}
        - 		pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr);
        - 		(void)pthread_mutex_lock(&(ws->ws_mutex));
        --		ws->state = lua_newstate(lua_allocator, (void *)(conn->phys_ctx));
        -+		ws->state = mg_lua_newstate(lua_allocator, (void *)(conn->phys_ctx));
        - 		ws->conn[0] = conn;
        - 		ws->references = 1;
        - 		prepare_lua_environment(conn->phys_ctx,
        -@@ -3664,7 +3741,7 @@ lua_init_optional_libraries(void)
        - 	lua_shared_init();
        - 
        - /* UUID library */
        --#if !defined(_WIN32)
        -+#if !defined(_WIN32) && !defined(NO_DLOPEN)
        - 	lib_handle_uuid = dlopen("libuuid.so", RTLD_LAZY);
        - 	pf_uuid_generate.p =
        - 	    (lib_handle_uuid ? dlsym(lib_handle_uuid, "uuid_generate") : 0);
        -@@ -3678,7 +3755,7 @@ static void
        - lua_exit_optional_libraries(void)
        - {
        - /* UUID library */
        --#if !defined(_WIN32)
        -+#if !defined(_WIN32) && !defined(NO_DLOPEN)
        - 	if (lib_handle_uuid) {
        - 		dlclose(lib_handle_uuid);
        - 	}
        -diff --git a/src/mod_lua_shared.inl b/src/mod_lua_shared.inl
        -index 5f69e285..fed94cd9 100644
        ---- a/src/mod_lua_shared.inl
        -+++ b/src/mod_lua_shared.inl
        -@@ -40,7 +40,7 @@ lua_shared_init(void)
        - {
        - 	/* Create a new Lua state to store all shared data.
        - 	 * In fact, this is used as a hashmap. */
        --	L_shared = lua_newstate(lua_allocator, NULL);
        -+	L_shared = mg_lua_newstate(lua_allocator, NULL);
        - 
        - 	lua_newtable(L_shared);
        - 	lua_setglobal(L_shared, "shared");
        -diff --git a/src/mod_mbedtls.inl b/src/mod_mbedtls.inl
        -index 822fc2e0..6883d48a 100644
        ---- a/src/mod_mbedtls.inl
        -+++ b/src/mod_mbedtls.inl
        -@@ -1,8 +1,10 @@
        - #if defined(USE_MBEDTLS) // USE_MBEDTLS used with NO_SSL
        - 
        --#include "mbedtls/ctr_drbg.h"
        - #include "mbedtls/debug.h"
        -+#if MBEDTLS_VERSION_NUMBER < 0x04000000
        - #include "mbedtls/entropy.h"
        -+#include "mbedtls/ctr_drbg.h"
        -+#endif
        - #include "mbedtls/error.h"
        - 
        - #if MBEDTLS_VERSION_NUMBER >= 0x03000000
        -@@ -26,14 +28,16 @@ typedef mbedtls_ssl_context SSL;
        - typedef struct {
        - 	mbedtls_ssl_config conf;         /* SSL configuration */
        - 	mbedtls_x509_crt cert;           /* Certificate */
        -+#if MBEDTLS_VERSION_NUMBER < 0x04000000
        - 	mbedtls_ctr_drbg_context ctr;    /* Counter random generator state */
        - 	mbedtls_entropy_context entropy; /* Entropy context */
        -+#endif
        - 	mbedtls_pk_context pkey;         /* Private key */
        - } SSL_CTX;
        - 
        - 
        - /* public api */
        --int mbed_sslctx_init(SSL_CTX *ctx, const char *crt);
        -+int mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist);
        - void mbed_sslctx_uninit(SSL_CTX *ctx);
        - void mbed_ssl_close(mbedtls_ssl_context *ssl);
        - int mbed_ssl_accept(mbedtls_ssl_context **ssl,
        -@@ -43,6 +47,9 @@ int mbed_ssl_accept(mbedtls_ssl_context **ssl,
        - int mbed_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, int len);
        - int mbed_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, int len);
        - 
        -+/* Set the ciphersuites to be used by mbedtls using a comma-separated string */
        -+int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list);
        -+
        - static void mbed_debug(void *context,
        -                        int level,
        -                        const char *file,
        -@@ -52,7 +59,7 @@ static int mbed_ssl_handshake(mbedtls_ssl_context *ssl);
        - 
        - 
        - int
        --mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        -+mbed_sslctx_init(SSL_CTX *ctx, const char *crt, const char *cipherlist)
        - {
        - 	mbedtls_ssl_config *conf;
        - 	int rc;
        -@@ -62,7 +69,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        - 	}
        - 
        - 	DEBUG_TRACE("%s", "Initializing MbedTLS SSL");
        -+#if MBEDTLS_VERSION_NUMBER < 0x04000000
        - 	mbedtls_entropy_init(&ctx->entropy);
        -+#endif
        - 
        - 	conf = &ctx->conf;
        - 	mbedtls_ssl_config_init(conf);
        -@@ -85,9 +94,25 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        - 
        - 	/* Initialize TLS key and cert */
        - 	mbedtls_pk_init(&ctx->pkey);
        -+#if MBEDTLS_VERSION_NUMBER < 0x04000000
        - 	mbedtls_ctr_drbg_init(&ctx->ctr);
        -+#endif
        - 	mbedtls_x509_crt_init(&ctx->cert);
        - 
        -+#ifdef MBEDTLS_PSA_CRYPTO_C
        -+	/* Initialize PSA crypto (mandatory with TLS 1.3)
        -+	 * This must be done before calling any other PSA Crypto
        -+	 * functions or they will fail with PSA_ERROR_BAD_STATE
        -+	 */
        -+	const psa_status_t status = psa_crypto_init();
        -+	if (status != PSA_SUCCESS) {
        -+		DEBUG_TRACE("Failed to initialize PSA crypto, returned %d\n",
        -+		            (int)status);
        -+		return -1;
        -+	}
        -+#endif
        -+
        -+#if MBEDTLS_VERSION_NUMBER < 0x04000000
        - 	rc = mbedtls_ctr_drbg_seed(&ctx->ctr,
        - 	                           mbedtls_entropy_func,
        - 	                           &ctx->entropy,
        -@@ -97,8 +122,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        - 		DEBUG_TRACE("TLS random seed failed (%i)", rc);
        - 		return -1;
        - 	}
        -+#endif
        - 
        --#if MBEDTLS_VERSION_NUMBER >= 0x03000000
        -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000
        - 	// mbedtls_pk_parse_keyfile() has changed in mbedTLS 3.0. You now need
        - 	// to pass a properly seeded, cryptographically secure RNG when calling
        - 	// these functions. It is used for blinding, a countermeasure against
        -@@ -129,7 +155,9 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        - 		return -1;
        - 	}
        - 
        -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x04000000
        - 	mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &ctx->ctr);
        -+#endif
        - 
        - 	/* Set auth mode if peer cert should be verified */
        - 	mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE);
        -@@ -141,6 +169,14 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        - 		DEBUG_TRACE("TLS cannot set certificate and private key (%i)", rc);
        - 		return -1;
        - 	}
        -+
        -+	/* Set ciphersuites if specified */
        -+	if (cipherlist != NULL &&
        -+	    cipherlist[0] != '\0' &&
        -+	    mbed_sslctx_set_ciphersuites(conf, cipherlist) != 0) {
        -+		DEBUG_TRACE("Failed to set ciphersuites: no valid ciphersuites are found in the list");
        -+		return -1;
        -+	}
        - 	return 0;
        - }
        - 
        -@@ -148,10 +184,12 @@ mbed_sslctx_init(SSL_CTX *ctx, const char *crt)
        - void
        - mbed_sslctx_uninit(SSL_CTX *ctx)
        - {
        -+#if MBEDTLS_VERSION_NUMBER < 0x04000000
        - 	mbedtls_ctr_drbg_free(&ctx->ctr);
        -+	mbedtls_entropy_free(&ctx->entropy);
        -+#endif
        - 	mbedtls_pk_free(&ctx->pkey);
        - 	mbedtls_x509_crt_free(&ctx->cert);
        --	mbedtls_entropy_free(&ctx->entropy);
        - 	mbedtls_ssl_config_free(&ctx->conf);
        - }
        - 
        -@@ -188,7 +226,13 @@ mbed_ssl_accept(mbedtls_ssl_context **ssl,
        - 		return -1;
        - 	}
        - 
        -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
        -+	DEBUG_TRACE("TLS connection %p accepted, state: %d",
        -+	            ssl,
        -+	            (*ssl)->MBEDTLS_PRIVATE(state));
        -+#else
        - 	DEBUG_TRACE("TLS connection %p accepted, state: %d", ssl, (*ssl)->state);
        -+#endif
        - 	return 0;
        - }
        - 
        -@@ -214,7 +258,13 @@ mbed_ssl_handshake(mbedtls_ssl_context *ssl)
        - 		}
        - 	}
        - 
        -+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
        -+	DEBUG_TRACE("TLS handshake rc: %d, state: %d",
        -+	            rc,
        -+	            ssl->MBEDTLS_PRIVATE(state));
        -+#else
        - 	DEBUG_TRACE("TLS handshake rc: %d, state: %d", rc, ssl->state);
        -+#endif
        - 	return rc;
        - }
        - 
        -@@ -254,4 +304,65 @@ mbed_debug(void *user_param,
        - 	            str);
        - }
        - 
        -+/**
        -+ * @brief Sets the list of allowed ciphersuites for an mbedTLS SSL configuration.
        -+ *
        -+ * Parses a comma-separated list of ciphersuite names, converts them to their
        -+ * corresponding mbedTLS ciphersuite IDs, and configures the SSL context to use
        -+ * only those ciphersuites.
        -+ *
        -+ * @param conf Pointer to the mbedTLS SSL configuration structure.
        -+ * @param cipher_list Comma-separated string of ciphersuite names.
        -+ * @return 0 on success,
        -+ *         -1 if conf or cipher_list is NULL,
        -+ *         -2 if no valid ciphersuites are found in the list.
        -+ *
        -+ * @note The ciphersuite ID array is static and must remain valid after the function returns,
        -+ *       as mbedtls_ssl_conf_ciphersuites() does not copy the array.
        -+ */
        -+int mbed_sslctx_set_ciphersuites(mbedtls_ssl_config *conf, const char *cipher_list) {
        -+	if (conf == NULL || cipher_list == NULL) {
        -+		return -1;
        -+	}
        -+
        -+	// The array for ciphersuite IDs must remain valid after this function
        -+	// returns as mbedtls_ssl_conf_ciphersuites() does not copy the array,
        -+	// but only stores the pointer. We do not allow more than 64 cipher
        -+	// suites for simplicity.
        -+	static int ciphersuites[64] = { 0 };
        -+	size_t count = 0;
        -+
        -+	char buf[1024];
        -+	strncpy(buf, cipher_list, sizeof(buf) - 1);
        -+	buf[sizeof(buf) - 1] = '\0';
        -+
        -+	char *token = strtok(buf, ",");
        -+	while (token && count < 63) {
        -+		// Remove leading/trailing whitespace
        -+		while (*token == ' ' || *token == '\t') token++;
        -+		char *end = token + strlen(token) - 1;
        -+		while (end > token && (*end == ' ' || *end == '\t')) {
        -+			*end = '\0';
        -+			end--;
        -+		}
        -+		const mbedtls_ssl_ciphersuite_t *ciphersuite = mbedtls_ssl_ciphersuite_from_string(token);
        -+		if (ciphersuite != NULL) {
        -+			const int id = mbedtls_ssl_ciphersuite_get_id(ciphersuite);
        -+			DEBUG_TRACE("Adding ciphersuite '%s' (ID %d)", token, id);
        -+			ciphersuites[count++] = id;
        -+		}
        -+		token = strtok(NULL, ",");
        -+	}
        -+	ciphersuites[count] = 0;
        -+
        -+	if (count == 0) {
        -+		DEBUG_TRACE("No valid ciphersuites found");
        -+		return -2; // No valid ciphersuites found
        -+	}
        -+
        -+	// Set the ciphersuites
        -+	mbedtls_ssl_conf_ciphersuites(conf, ciphersuites);
        -+	return 0;
        -+}
        -+
        - #endif /* USE_MBEDTLS */
        -diff --git a/src/third_party/civetweb_lua.h b/src/third_party/civetweb_lua.h
        -index 40382022..8c59aaa1 100644
        ---- a/src/third_party/civetweb_lua.h
        -+++ b/src/third_party/civetweb_lua.h
        -@@ -49,7 +49,7 @@ extern "C" {
        - #define LUA_ERRGCMM 999 /* not supported */
        - #define mg_lua_load(a, b, c, d, e) lua_load(a, b, c, d)
        - #define lua_rawlen lua_objlen
        --#define lua_newstate(a, b)                                                     \
        -+#define mg_lua_newstate(a, b)                                                  \
        - 	luaL_newstate() /* Must use luaL_newstate() for 64 bit target */
        - #define lua_pushinteger lua_pushnumber
        - #define luaL_newlib(L, t)                                                      \
        -@@ -62,17 +62,17 @@ extern "C" {
        - 	}
        - #define luaL_setfuncs(L, r, u) lua_register(L, r->name, r->func)
        - 
        --#elif LUA_VERSION_NUM == 502
        --/* Lua 5.2 detected */
        -+#elif LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504
        -+/* Lua 5.2 - 5.4 detected */
        - #define mg_lua_load lua_load
        -+#define mg_lua_newstate lua_newstate
        - 
        --#elif LUA_VERSION_NUM == 503
        --/* Lua 5.3 detected */
        --#define mg_lua_load lua_load
        - 
        --#elif LUA_VERSION_NUM == 504
        --/* Lua 5.4 detected */
        -+#elif LUA_VERSION_NUM == 505
        -+/* Lua 5.2 - 5.5 detected */
        - #define mg_lua_load lua_load
        -+// A third parameter "seed for the hashing of strings" has been added
        -+#define mg_lua_newstate(f, ud) lua_newstate(f, ud, luaL_makeseed(NULL))
        - 
        - #else
        - #error "Lua version not supported (yet?)"
        -diff --git a/src/third_party/lfs.c b/src/third_party/lfs.c
        -index 95ab63b4..e69dfec7 100644
        ---- a/src/third_party/lfs.c
        -+++ b/src/third_party/lfs.c
        -@@ -324,7 +324,7 @@ static FILE *check_file(lua_State * L, int idx, const char *funcname)
        -     return 0;
        -   } else
        -     return *fh;
        --#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 504
        -+#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 505
        -   luaL_Stream *fh = (luaL_Stream *) luaL_checkudata(L, idx, "FILE*");
        -   if (fh->closef == 0 || fh->f == NULL) {
        -     luaL_error(L, "%s: closed file", funcname);
        -diff --git a/src/third_party/lua-5.2.4/src/ldebug.c b/src/third_party/lua-5.2.4/src/ldebug.c
        -index 96118461..1a30433b 100644
        ---- a/src/third_party/lua-5.2.4/src/ldebug.c
        -+++ b/src/third_party/lua-5.2.4/src/ldebug.c
        -@@ -116,10 +116,11 @@ static const char *upvalname (Proto *p, int uv) {
        - 
        - static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
        -   int nparams = clLvalue(ci->func)->p->numparams;
        --  if (n >= ci->u.l.base - ci->func - nparams)
        -+  int nvararg  = ci->u.l.base - ci->func - nparams;
        -+  if (n < -nvararg)
        -     return NULL;  /* no such vararg */
        -   else {
        --    *pos = ci->func + nparams + n;
        -+    *pos = ci->func + nparams - n;
        -     return "(*vararg)";  /* generic name for any vararg */
        -   }
        - }
        -@@ -131,7 +132,7 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
        -   StkId base;
        -   if (isLua(ci)) {
        -     if (n < 0)  /* access to vararg values? */
        --      return findvararg(ci, -n, pos);
        -+      return findvararg(ci, n, pos);
        -     else {
        -       base = ci->u.l.base;
        -       name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
        -diff --git a/src/third_party/lua-5.5.0-beta/Makefile b/src/third_party/lua-5.5.0-beta/Makefile
        -new file mode 100644
        -index 00000000..388fa17f
        ---- /dev/null
        -+++ b/src/third_party/lua-5.5.0-beta/Makefile
        -@@ -0,0 +1,106 @@
        -+# Makefile for installing Lua
        -+# See doc/readme.html for installation and customization instructions.
        -+
        -+# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
        -+
        -+# Your platform. See PLATS for possible values.
        -+PLAT= guess
        -+
        -+# Where to install. The installation starts in the src and doc directories,
        -+# so take care if INSTALL_TOP is not an absolute path. See the local target.
        -+# You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with
        -+# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h.
        -+INSTALL_TOP= /usr/local
        -+INSTALL_BIN= $(INSTALL_TOP)/bin
        -+INSTALL_INC= $(INSTALL_TOP)/include
        -+INSTALL_LIB= $(INSTALL_TOP)/lib
        -+INSTALL_MAN= $(INSTALL_TOP)/man/man1
        -+INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
        -+INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
        -+
        -+# How to install. If your install program does not support "-p", then
        -+# you may have to run ranlib on the installed liblua.a.
        -+INSTALL= install -p
        -+INSTALL_EXEC= $(INSTALL) -m 0755
        -+INSTALL_DATA= $(INSTALL) -m 0644
        -+#
        -+# If you don't have "install" you can use "cp" instead.
        -+# INSTALL= cp -p
        -+# INSTALL_EXEC= $(INSTALL)
        -+# INSTALL_DATA= $(INSTALL)
        -+
        -+# Other utilities.
        -+MKDIR= mkdir -p
        -+RM= rm -f
        -+
        -+# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
        -+
        -+# Convenience platforms targets.
        -+PLATS= guess aix bsd c89 freebsd generic ios linux macosx mingw posix solaris
        -+
        -+# What to install.
        -+TO_BIN= lua luac
        -+TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp
        -+TO_LIB= liblua.a
        -+TO_MAN= lua.1 luac.1
        -+
        -+# Lua version and release.
        -+V= 5.5
        -+R= $V.0
        -+
        -+# Targets start here.
        -+all:	$(PLAT)
        -+
        -+$(PLATS) help test clean:
        -+	@cd src && $(MAKE) $@
        -+
        -+install: dummy
        -+	cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
        -+	cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
        -+	cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
        -+	cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
        -+	cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
        -+
        -+uninstall:
        -+	cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN)
        -+	cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC)
        -+	cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB)
        -+	cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN)
        -+
        -+local:
        -+	$(MAKE) install INSTALL_TOP=../install
        -+
        -+# make may get confused with install/ if it does not support .PHONY.
        -+dummy:
        -+
        -+# Echo config parameters.
        -+echo:
        -+	@cd src && $(MAKE) -s echo
        -+	@echo "PLAT= $(PLAT)"
        -+	@echo "V= $V"
        -+	@echo "R= $R"
        -+	@echo "TO_BIN= $(TO_BIN)"
        -+	@echo "TO_INC= $(TO_INC)"
        -+	@echo "TO_LIB= $(TO_LIB)"
        -+	@echo "TO_MAN= $(TO_MAN)"
        -+	@echo "INSTALL_TOP= $(INSTALL_TOP)"
        -+	@echo "INSTALL_BIN= $(INSTALL_BIN)"
        -+	@echo "INSTALL_INC= $(INSTALL_INC)"
        -+	@echo "INSTALL_LIB= $(INSTALL_LIB)"
        -+	@echo "INSTALL_MAN= $(INSTALL_MAN)"
        -+	@echo "INSTALL_LMOD= $(INSTALL_LMOD)"
        -+	@echo "INSTALL_CMOD= $(INSTALL_CMOD)"
        -+	@echo "INSTALL_EXEC= $(INSTALL_EXEC)"
        -+	@echo "INSTALL_DATA= $(INSTALL_DATA)"
        -+
        -+# Echo pkg-config data.
        -+pc:
        -+	@echo "version=$R"
        -+	@echo "prefix=$(INSTALL_TOP)"
        -+	@echo "libdir=$(INSTALL_LIB)"
        -+	@echo "includedir=$(INSTALL_INC)"
        -+
        -+# Targets that do not create files (not all makes understand .PHONY).
        -+.PHONY: all $(PLATS) help test clean install uninstall local dummy echo pc
        -+
        -+# (end of Makefile)
        -diff --git a/src/third_party/lua-5.5.0-beta/README b/src/third_party/lua-5.5.0-beta/README
        -new file mode 100644
        -index 00000000..25384b86
        ---- /dev/null
        -+++ b/src/third_party/lua-5.5.0-beta/README
        -@@ -0,0 +1,6 @@
        -+
        -+This is Lua 5.5.0 (beta), released on 28 Jun 2025.
        -+
        -+For installation instructions, license details, and
        -+further information about Lua, see doc/readme.html.
        -+
        -diff --git a/src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png b/src/third_party/lua-5.5.0-beta/doc/OSIApproved_100X125.png
        -new file mode 100644
        -index 0000000000000000000000000000000000000000..795f7a06ed5b32131e69f245f1e1e0f4fc22c5aa
        -GIT binary patch
        -literal 12127
        -zcmV-lFQCwgP)
        -zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=QHaxA-%ME|i0Edlee90Ufl-hq~{<5@}G;a!i%le3hucKDazdZ7YlV7_3{F~3u;PdnI`B(G%
        -zTKM&O*Y^8DGs~aj^2LW!q*1w`}$J`zMX#L)M70)w9L;OeQR~_dyhUh>bAAe
        -z)Sml)-{|-McVBOftSO!pl7D{2&nnaBdp?i)Ie1@4iQfxf`uVQb(K436@8hlQThxE{
        -z@1L#S{k;6Q?Z32jtv|Q>`?t5-?yCzmzAvQTx>Ua(|M+|;Ul;nVtxxZM24Mfb^Doc0
        -z8^X_9c3?cIq^K0g1%3@p~=d
        -zx$Ssvw?g;Z`5ky`3|tKK{O4cpUwz|!W`Y90#RnFwD++b2!Ux)~2i`_U!hMTco&tY;
        -z{r*pVO?0q4W$tWndUKykSP4JCm7Yc?-dK42%u;}EJ{O=w+`6!skm!J~A(c>BQH!yK
        -zKp{H~nk+r09E}cyQsQQjF{e~%4l~TX`AiGXTVsizry;|X9b;Ehqcnw4__KMnF6
        -zc`}!|lu}D8y^Jzzs+zD`TNSI$mRfG5)z(^fZM4}_&%N~8Tkn1JIpRpLZW+wu=wpmI
        -zx#{G}lTT0HFsDUJmaSN|X5EIfxq#2gtE{@(>T9gI<4&77-gUR#_t^7<1C&la<{%W`;NIAn!7AHeeMEwhT5ozZOgm0
        -z82a&fGvYP()x&Cs*BB3GD2!(C9iny0J!KuZYi@^x)HGYR=BI^ivz>#QmNQdlRcBW7
        -zW497^KXncYxLQqvX;^9BGxsUOHaJ?%+D&1sd#p^zcTnDXQ(VR*TiI=fz4EXw;K~RGDAUj8DMw%ZOaUx@R_Ev_~KY6V7n!25LY+@T%W}HvuQcl
        -zISMg!-!OL#eb&Bb=+;77<6M>i66-SD$Ta|0T1%AC`Yk9=8H2W28`EcFQm;?-29}O^
        -zQrcQ=9Aj%co=||)Xx<$;tabNJAo_Yfdw@V6B_^YNaRZFh+2#b(XIR>!EvgSP4okcJ
        -zw0Y~gbPbzxX5OhVe>K)IZTk6Ox3BmINPk1Slx#>u(W;X}@|)Ze+#pn2D?)Yn9o0Kk
        -zhQCm{@@#Hw5Ht?=VgSpr?v8DmzJq1@Up*^I>D1L3Iu`hNf(-OerAvAj#lE2>q=V~}
        -z3>Mpt^u^ic^Z3mwQ34)yc-FnV0ON&F#05WtW!Za_;@nsewyB1c{NWDbhMJhZ86
        -z!wxXQr#dtD&o6BJFOTYG4St_hbM7qvtUVI5cI@0zxo{m!+YGbrWQ^?I@Z}yAb~@+O
        -z6v5PCQ2ClBvv~7Zl^%nsGu@8yo|+9;b=mBOMSIcrfsHddkz2TVQVIwFVl$cvaWLlW
        -z*V<`Y0PEA8HAC6^f{nA%i9x^K)R?-OO5ea;wPITL>Sr_ZkUbX~G2hG_2*DV;>|o);
        -z|4ix?K~l(t|M>p=B18T@f)0jttTh+lm4nQpFg6rDKz1@eTxTuK@;%_T8o7i?y{uuu
        -z_aFf1GYfm|UM3W^HwM$UHE|sKW=29Z!cescIr6SPZOyrAKg>kE15Kb(U}ko{
        -zcpA#ZF2)a+d=QE5YD9zh-aJKj<6zJE!NNJu$W?wnpAF)M{<7n*NB5Uf__B*-oQh8n
        -z%Jj4dx7u<-%8oFydtjc~@!$y*roc^gqqIoYl0}C32O_*mgBsA)L9HgDRDH>PFQb=I
        -zyh_6XGnJ}LL0Nl_!8EyZ@d9{dddm2A;#(_b=su`Rb51+}W#?aSs4DS@qB7KT?sZGU
        -zeavfPS#jD^*^tVw4W~|x)x}(C=xS&~7!toU%l30`*b4VS(`=SYNW6~egQaz?)8VlS
        -zSX$Fs2TnjF!2lp9l&1C(Md3_zr<4b3SHd{d>Kw8NH-V^(-DlVkvS=vL5^34Ru0Bk5
        -zD0Tmt)NoeAtXI;RWkfjyx6T&F;xR^9A=+7Ni!Hi}V8kFka#I25G36IMdfU27n3L6x27|Xd0tAGZAF8uG#Xku~NSR2d;!V~?8voeMX>;Qd8%Y*lu>Syq(YWj{~2JzD{{c<_5afCz}
        -z9(Zr#^%ltH1v}XP?3d1fpkwCpG7=T4WLA(oZ|Qj4hp*OgXE2?xKzR~Cz-xm@C-Oj9
        -zG0TfdpyFrjTNqV_=zx$>+GW9%6U&Ika0zT>-HZUVwvlK{0tS*T&A{OYrmRLVv${Mq
        -zJY}SSy-}Q&DH*s;jBrKXXug5~2tEsd&5Xx%5IsbQL{V)|s=)KWueD~Z71D;Q#TCUi
        -zhyl3i29rQ(D3h9WF#xz6^Wu;kU~#zl1cIdA23M~dz<<|`gD2B6S^+P0VGR`
        -zSPn#`5*kiH*O24Jjfgh!hX9m=_m$VKt*nLtLUr*#*kNoQVAIJ2M1VQ74&lM(<~Fy(
        -z(?zOhszqSeau|b{EmfqyBHHbl!G+mJBE&lw1ty{+HhJBKt~)C5;uz$jMKlnEAu^|3
        -zm`^Z+QiNi*n1$k4DqKDQal$_642%xZwT1A|xz-VmWY8A@gNV2u+q|bdxyOcZ2V8i<
        -z*ubV+(YQ{$JN^|O;T
        -zJB0Lz&IevG9K&8yV_+;bU~9+^3@KiTpuqU;j;FyrP-}$)yD-?6+k;t}qJJ4WCP|FQ
        -zk)qcLPDXK{0hvQd#S@Y05vt>gM1VzVr|LL*`~p^xv{R;HcoSAY*
        -z(|F1-OzcG?*pUkJrv2o&H1^OO;;&n*#+0`*@?2v;#Kbs6F0bX<(+vz59AjXLI6HxV
        -zguB<(i48McjZd;YoCu)kD=BS~<#nJrN7G3F$cC`65x|Swyol}w)G-;*G#IXo125jD
        -zj_{U>3s}$%DWz+0j$u^cdvHXJB36uYXAxtBdU0I~n~5)`4cKG_Fgzzo|ADPy^{*sI
        -z>Yzr13|ccP-e%!==r+mDXKJ`HRAfG5C)j%cs9WGoNvb#E)4(s2dDL+78e_08_AMy`
        -zBUR@i4sJw(wk?`1SgHidQNRVjz3jIDJ0dRz~
        -z?ztUSXkhTcZk51O4!k5;VRxR^$-_Xaya_(xC+89pHf&b9K
        -z%s~GbP$)XcD3_`dBRs5fgDu84E&!=mg%3b}biyQ4!`gjX!ozLUR7Sn&7|sM_!YFB-
        -zz8sLwzSj60fDp6%kBlFiEWZsLXH*Fi-_#}0)Ejjrrjen5J1QZS*0RMrkn#l|nf?sK
        -z_8Zw4^tTjra)u3*;m6WUWVxk@Nm63015@P=qClvSY@5f5@hT(o
        -z7}g-;jly_Beoc?hh8e-Od9hj3?NX5i;fkx*%#ji9R)yva9f7BiJ!_4+8fecB2kx;H={=A-_Y@iP%Ul6x&-|6m6#yc
        -zM@P5|_Q!oBaLbz%AINSbL;wT*QrzV>c9$5d;ekYhPLLou{k33oK_~|qtU4pbrJs-O
        -z6$z1~rVf6RPPxe0$yL%BOdE3)tqI9yO$lD=l{1eL4CCj;?BG0Eqf
        -zHoIBbj)i4hhndHj(+?DXFv0=9o5e5q;SXIS(m-jkMxtb*N{g{T`H#75&*65|sj8l<
        -z4O*kvh$tqOs<4j^yYTUIbo`qnl-Ng{awjfg+#tnz97BUqELV6zlm@vr%EG0kVORV<
        -z6po;Zs#5XdnXU2s5h>rLJb;J5;Z6sQ1C@v)tvG~|Q-GW>ZLrHYGwJ+rHl0=DfoRCk
        -z5z(L#;mCK{w>O
        -zE>3K#B?;*Ct@Ei;k!322871`6daOVyU<{QQn030skfQw_NrYRwa%QK{?37j){=?xA
        -zci_0alvi0~;cZ&daVTPat`%lu>VGFB_tl
        -z-y>>~j-bpVQq-|-q}!cRVL8~;dvy97MLFTV=*j=-&j*n
        -zj->U@sBiFq;?mG231{@m8k!~CHHt7@YlX0>LmXzi>bR1bX5BrLYfYy0$emu;HtneG
        -zdz5_D9-!apLp{&c55zX`8L(D@N^k&p&6494IbNO6O{H*}z(+~B1?BFQ@JBI3jdHOC
        -zz5$TN+~+DUu7gx;e|Y&ZmDD$L7NaN4E+z}EYzHht4>}~5Wq}r<^)h327|uv-@GySq
        -zumK9Sgm;BTbIA+>E;ewG?=@l~>kBRBbsu*~MfNq4)k6xKs#+)Q2;5okT;p6LFlxKOL`*ZZES&IQak$9FF
        -zW`%fzczUy9aNZ{lvx2M=pA(OnbV1@rt}8CTan3s|@XWB8O3o99iN$;yD{agQrbawP
        -z98oo$@`bd^D(5ZETB*vK_v9}OX7!b2uG1Vw9E(_j1Q80VD58W4qO|IySV+=-%)>wA
        -z_$6{FM<`C{82BS2smXw+=```ES{CxHJMxYAqx
        -zavhlYB)!(sB1b@I8@RY`Y04gOxdRM78L}xmlAorK%K`6a^i3IH;1=jvb9-y<p(JFpkg{pJ$)-R^!a^tkLMVxmfzW%k0UL}lxPhCl
        -zELmMMBh6^q-9OHEM`tc0TV?LJpznF~Xy(>4_dQ>G&v(u>*iEHwQ5{y&%CijXP+%r7
        -z2bc&P2#f~`fPBCJVn9C-1U3P!z*=A(up9`O4JLbn7`szrY8KUa4a+zII0ZNn_ypj~
        -z!ll;$F9FX0Pniv7U<{B!#9!|>fa8JlfknWCK@YaT@EUjvJ$Ujxc=8Ouz)DyoqDhjG
        -z1j(4SvtJbzVE%4kEN*OQjEn)IfG7dJ23!tI%ha;sDTNeP6_8iv!&mCVSLnr)@7dvX
        -zt%OCqFF`yUCl-v;-w`F;5+Tu_%yf7ca3^rRxy0Op^)UoQfVcv<%Bc;$5-+9ul~6RP
        -zfP#vA3~!d2XF)U=BhuDSZ(|>kwy5)n1aJ@V^WgU2hMw1Y_I^O7oHoVq=6f#!elE^A
        -z)q?T^ODL@?p@SnhsfIRz-
        -z6|>42H??%9{vg-DbiUI=ye~~-o&dgXHki$OJRtsh|774^V7|Src%MQl539ge;$wG|
        -zWZWX~L6EN10W=5S4xE<=C!TD6!hKx4E`f0*@GNi$fZ;Q!o;QIB2anrrv?62g^cWOY
        -z6;R?YBH9%t5l#XW1Lt`1JpCNPtNriw?>T@Ng$Diw{?mcSfJ%UZ%6ulDSWQuN0egyg
        -z3%r!>TY_ale|yxnB%dxw7VKL#qwLY(+F)|GH(7P3)sf6ko&(%v16_7N36+OcWG{T(
        -z1=8CTrfpd#Rx)M791U$63r3dT(@+s&A=k&Ca$OL0vRecQr{nt`dntn!)V
        -zivw!|)@}uezurF&cofCFQaQJR@&ilR+r*pip`@mWUNejpw?Hw_Rg>mTdaQF*=k5WB
        -zzurFuc+RFk#vfeHxM^kVt>Ve|P&}oO(3Y_6Q|85^@wRAw^v(G8_^$OKwTo&!zy9Dv!r!Fy+MRUB*Z4Dk-SQClQVl?T!Id08`3mmp>F(
        -z6UeQG)GVy=0JkF}c~wV_-)*T7w`Ibi75GY09q~evd?x26%P@>ffWrWa{Y8}27V~ix
        -zPo9VBd1*sBH~Q=S`8hLLVkegX#fAqaA6JDZ&%?)Ad?h{-kp$7M7(y3y1=a?ZtQLb~(OcA7cXJXO-)W#I^opS9en2UPFQp|{j+E>23{g8gz`uhUGbr1)WDJ1}S$xHel$KNjXSt~%
        -zrvP;T<_1M>6AeT<(oF6ThBujrz4We<_ID9PxK2kYXYIDJhKf*jftDKG;#
        -z3RRZsO~#URyxmQ3b1xH*swA&8Hz9iA#6k>@VY9&t{q_D|45!e0D6Rag{h?@LZpOu}
        -zxJBFYPQ9lAyaxO-X(b=uwq#pIHJNeV3_`u3Y6&~91j?daF`Ay<&gA2(D6De7{T`n|
        -zaZRD!_9Ml?w+>b`o77J#mgF&%?fF?BhD4Yb7mOmv+#%EmZ>@1TIbHC;ztX{%u44
        -zRsMzk$tce4RNItHo?k^_rTd@+H}}&1W|#f-D`tave^#O_3#$y64Nx?tASd*C*SkGB
        -z7+ZlO%?5MmV5o*;Hkd6I*6G0SZJ@Wm(%~$Gb43a!q|sT&W-aVBFy`4>lnZqaw?^n$
        -zAJTore6zt^KhoyOEXw1yw06IaEx4RscCj=WATmj+O|q
        -zKq|kuyK`;lv)Q_~*?CJ8sE9QpGeujhko-^0AuL=s%3k=xy!
        -z$>!+5{!s%`TVHFd8@^(9%~doI)4x8w^`Bea%A%hGdKDn<7EL?&zR9BoBxxi;4c@z6
        -zzz|F4rrvhzK8=eT6Tm8f{!VvImZOBWWYmBdmH|+d;o{jhS+TUlyUS&Ny9vOGStMO#
        -zhaLP`{-^;-+n?KdtE9`YlT
        -z@^EvZ0LSQM*QfyzX#?!JcJ9UOrFpuQJ;udb9RO4T6jtWD`kNAwv`)MwTYzk^eTZX%
        -zeRgAZpFz>26nS~PzurH=rCWU=MQykYh{LLMWDAfKvhj$!W=ktc^`UsDY?~_s_(saJ
        -zPb_pdw;JH(k{GPX79guq*>q3LT|o9J(w*#gr7$m>asg`A6J-aM;B~ojCeo2&0ycXL
        -zPb^!2tVjXkG8yN|_fRo2t*+@0HT5;w(vE4s^>#)uZo0d(yb}FM;-Qp9S-y4g(5avu
        -z0uqnJn*bA_uf<*N2cUdvsb1dyRRiOix->ADPSC^*}-lXJVd*rdXVzQusY_B
        -zcD6j;0`M}Z@|t*W++8in^DyzKwCQ5MN?OU^`0M?{euDFuhriyx2zVXX57g2gZfl1!
        -zS83Mdm8=0G@BR@4HL^fgC_hZ8vgrKq|9
        -z|7Rvqx^Eh_wI6U(?Ux1}fICLd^ou4Hu+J$I89#emPBcS3qqI)@;IPg1&JGBz4uM)`
        -zN!_q1H(CN}9KZS#6PR>tnhATe@t=)@o$@@Yjtg&*m&wOhQ&5qgv-V;^;KS5VbP$6_
        -zvJQyIeLx7HXMGSW$3^Cj9O%!39cMO}y~wzEBAn23ft*OByEVbTME~Gns!GK?xOGdV|`EUuHpU8uu^PL{&AWZf{$Usc5
        -z4td(+@oXI8sdu8LDG>NDl!HXDUrz~E;Nf2*PL79jusPRU4gA%Pw}YG9{js5~X+Gel
        -zp>je^k2Q6m=F=1HiqYTg9uH?FEjm}GExdenmdMIR0K1X;uOhjs+izP!toB8YHH
        -zSTEDL#auGHj5lc|pG3xUf*X5XDfx+o;&i^9(vDu>J0tZSBL+nMT5d$mXd=?uulM9E
        -zhLE9wyJ?~9P-WYaZ7JuTj9Xa`)vbg@+Z$<%V_w&?tY!0PuR9_@%!kY*aNdCF>LJpW
        -zy(BE!6Qh5CF@4xFtdVxR+wCAkE9TCMbgu5v+xV={^4$<`9=-V<@vxb>#N327B)a|8
        -z4rfW|sO+*Pw=_N2G@|Pi78EujY;~lyKdbEv3~2Cn;DXIdHfO~qj(i-`0?V-OLq_6~
        -zu_W7HX?Ipz4Br~Yk^~3%piSO9?-L`v)(7`}kTP2tqP7wGwunA|Fb(!5a(
        -zEH^#agyAu+My+H`EEuQd<@Ui|o-!oqZ3^r8&0n@Wuw`^T&teHu&J_GGG^zk3($>%R
        -zSJMheE-@R-7qe*!M?KQtxVSM1oQWElBibFK<)yX}Yl;>i#75%GGnm#GYB@p@ktE^P
        -zk@>pt_6RL6wd;WX)G&-&$+Bdn^ulZ~`+(0OQwgHoF}6M5Ml3kIN|pZ3sJ(_Ez=NUf
        -zp|&jj&H=)4)9{^pXloxWFQp^qS!z53*TFi>}o@w7uS`lc~Qc@A$z1Y@VVgD;GagrQw>F2nX49D8zitc@ND_a^0!cHZ51aXNrIbtNkkIlkIPG+gJ(h8
        -zo1G+8bQT8A53CJjbyj9zZ6H=Yv-|+yh-5TL(WHWptQi^J9-(zvC&5j9I$*B?pEVoI
        -zxB8piAAxjr@?cL{c~I1Fps58|D(Tn;c0_N-=5|)nZxJX~1vM
        -z79#-EdR9Keev=KrQD%erx-0GYVl7tES_K?z7*IB~l+flr=Nv~1H7@`C=4YC>B!UTY
        -zDLGkkt?|J|%m(vOEM}wL?zt|-TXn~DFy8SbJg>Xfj>QAc-Lh29qk&1}Ue89i=E(2XfH)pd
        -zv4qOSM0mh_+|0#2k3qeO`UQ{WS-Ew|R>ovZ#$-&!WK8x}IR^C_ycNKkz@JE8^XWL$
        -zM4nfG{efSg=0QA#_C{!tLxI1^{^j!d5N-cOa;-n2mU?{vcnkHiu~T=_&M%=VD*psr
        -z159)5KNB^8b_4Kb$G%y>_cL7co21W`|B3wng0%6A+P-8gugIJEgR>`>S#@X9V$8lZATyOz4lHpomV6GhVGuajenjOc*9pLHmOJEJ^
        -zjSZIy&=+d^?+4ZkP}cx!1#CkDE7OGjw}G!Z62+gQ#(RDaski8SQ?mI#13!>!PnCV2
        -z1A3&5mjWw+hen(~X%FaY(g^zj{UZY)0n{v~m$hwI$Z__z1h7DQREL&?>44avUx}KR
        -zXK(xAAnja*wgkFEPL*~|)V5WkNWw3S0FY-KG}Aa#+0U7Sq!OEff77;2kdArs&K|x~
        -zjb<~j`q@$jDC^6CGPFgNHF-!Vk#%CqklycOU{!{&B!D(YG=a>kb>1K*sF{H|DTBTJ
        -z>|yDF9?=G!Q$G!OQ2@5319)0G#>2zxq`#L=rJHn6Q|6p92elI47R?GEmgERA#X|-}
        -zUua7w{ksK_Ofx3!D&uy?b;fJdObJ?rib$rJ`^_3?JCp0gWL+VC;%2e;ebUZM3;k|$
        -z*!iJ~b0vmqxmG3$)Sg}R14rV$)$vS+1W1ea*>;fm(g5c}PjPnge}Bgh=n%7)FFkdk
        -zMfQl+ml?*A8n!se
        -z#jO%mA?o#eah8Red2-6>qPoA;w!J95=P~&{5%oftsp6zB0}jk$RQLw)2Q2`21UOOa
        -zt+wg=H1MQ+Zxv0_-`#YOqhk#w%Y+L%br2ua;_yR5mQ118g}@7<6<286*Gp*Ulun7v
        -zY+0i@|I6he9RQpyf3`d`N)Y}o_xeB{{N+e>iG<%ziM_sAI>*Hl#xK@DG$pwByO`Ju
        -zC8*h%v;zMn=iMNdW2*SFrNFmPBa{b8@KFXF4g7~hdxJ`*0jf6Cl!I=dNf5skxI~Le
        -z4$>a(jna^IRF&Yxn%V}$lr+k9-xCeIS;9p7RTDjuqdX@MsX)xha?!R-i?@U%YMUjU
        -z=O(!>n!^oApy5YNHo8W0&T%=WRyvJO_HO~+m){QRUB6Gt^U&ob31>ekPP{{o3rT?U
        -zkhJX}0sA>Q$Cc>GuOd!9RSo1^IFp-k=v2X06ss;
        -zHkS%-_Oa@O-$fQG9)|n`c*hZ*If6iCEA+!aRS80@Rxbb(-Kc4di#sx
        -zn0$?}9Ewat=BhiSW_N#p*{;i)AzAZ#^8F-vn3GYg=NYJllGi!TIb7oNxhOJof}A&7
        -zv}dM-rt37{cQSB3iVmM5p>bS3|4Pp7L%o=5i_kaq62_m5TF7Rzqx~mK!n#pQjxRy6
        -zxA6?e@AgjpIMUhK3s95&?~%NsUvirN)s8tqOdY8AwC4#kP$j?s%tx`nXh8o?j@^bD
        -z*SJsiO%;FPlWSDT=Q&Kqdz=dj*^_nzR&p?Ox9dEra&_1UbHo})
        -zzN53n9OiY840|6ZoxS}~e(-1(KC~-u3&r&0YXQMfBq;RGIoh^!Wm^zcGZvT6b~M>9
        -zZ9d9vC+xO#5ArZaQxkTE_?jZrq|1>MX5T6F#-;LokruMsKJ0eKIjaPiJsOZ~&)A=a
        -zA_UKX)S4nwkyW^dAuovOJVV0a9@&Nf+=Ywl(ChAVM)`~s9Y@?T%zSQpXW(V@GmVg3L#5%
        -zNZyeYdp;30{#}Bi@|TLEeL-@D`6$}^+tSOQ7aQLt*EwCxj{Pz)`@Tudzvanwor7=B
        -zOS|VwmS2GyUU-W!8Iv&?lQD6_hm6Va<#6D5VF0!mW6xuk%1NjKjY`z8MXART0^2k}
        -z;U4(ou0_^^bVwzcYps4wI$^mhq*|gx11XEr0Xda{3iXA1ARwoZ&Q00UF8PX6&O>`A
        -z)h>1!MJU$Vs{?W_s=#X_>Ge}nP_(!QRcLmqmQe18Ds{<64a1(q07o^Q0pf1~1C`-*
        -zz*!RCd*yE~YFq>@f0>Ia!Lg~4b5Nszx|!V%_=;R}xHH=qFu-Hkz4eQd_}PrEN9_CA
        -zq!-Z5L5=ZLOQwIUw$CH&JWm(|`&k)s+}R8i3BCy&EP2~>w3@I|Nu2ILjWfg;7>l@$
        -zfeLWD^yg{WcL%DF`W0=TU7-D}BzsF``?+Ew^r;T3P~#QvN|I(%CO?(^pO$RflH}38
        -zw@);ppMmKO8zqC!M3H1#Wm->07s)m3cD*bK>#G8ENdDV}gPVj=2x{m2PNQ_}sT3A!
        -zu&hWZn56bQzNaP12jh5w@i}r(z4USmHUB{e?)M#Sc2m$emS!)#F;B5xNJzF2(HM){uRbpCB
        -z5)D0W2M@hmOq>0u&vW@u%*_H+QNuLxDX(jU>7BqorA<3ph{lIvgzrJs9VaB{dDU^P
        -ze~6ZKX1d4=@(}%w*gYY(&^B2cGzYdsbLy66N-Wg00NbfM3yUujP;{qWt^Jr**?ysR
        -z&UeI|>2nY3#NpfX7Xt$9U?$7?cc{auU#vM{OB{27Xsv$UQq8Hq?_h>)=2|~KA)Pf}
        -z*kpa*iow+Na5-S5W8cAYq0dXybF3tc3C*G2ArEi5^x`h9XHA#r%}G~JMpcY=$Pd)b
        -z&QwX_R+BzdI!Vq+%Jpnj-&fkdRQA
        -+
        -+
        -+Lua 5.5 Reference Manual - contents
        -+
        -+
        -+
        -+
        -+
        -+
        -+
        -+

        -+Lua -+Lua 5.5 Reference Manual -+

        -+ -+

        -+The reference manual is the official definition of the Lua language. -+
        -+For a complete introduction to Lua programming, see the book -+Programming in Lua. -+ -+

        -+ -+

        -+ -+Copyright © 2020–2025 Lua.org, PUC-Rio. -+Freely available under the terms of the -+Lua license. -+ -+ -+

        Contents

        -+ --** xLock() increases the lock. xUnlock() decreases the lock. -+** xLock() upgrades the database file lock. In other words, xLock() moves the -+** database file lock in the direction NONE toward EXCLUSIVE. The argument to -+** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never -+** SQLITE_LOCK_NONE. If the database file lock is already at or above the -+** requested lock, then the call to xLock() is a no-op. -+** xUnlock() downgrades the database file lock to either SHARED or NONE. -+** If the lock is already at or below the requested lock state, then the call -+** to xUnlock() is a no-op. - ** The xCheckReservedLock() method checks whether any database connection, - ** either in this process or in some other process, is holding a RESERVED, --** PENDING, or EXCLUSIVE lock on the file. It returns true --** if such a lock exists and false otherwise. -+** PENDING, or EXCLUSIVE lock on the file. It returns, via its output -+** pointer parameter, true if such a lock exists and false otherwise. - ** - ** The xFileControl() method is a generic interface that allows custom - ** VFS implementations to directly control an open file using the -@@ -1808,6 +1141,7 @@ struct sqlite3_file { - **
      • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] - **
      • [SQLITE_IOCAP_IMMUTABLE] - **
      • [SQLITE_IOCAP_BATCH_ATOMIC] -+**
      • [SQLITE_IOCAP_SUBPAGE_READ] - ** - ** - ** The SQLITE_IOCAP_ATOMIC property means that all writes of -@@ -1868,9 +1202,8 @@ struct sqlite3_io_methods { - ** opcode causes the xFileControl method to write the current state of - ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], - ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) --** into an integer that the pArg argument points to. This capability --** is used during testing and is only available when the SQLITE_TEST --** compile-time option is used. -+** into an integer that the pArg argument points to. -+** This capability is only available if SQLite is compiled with [SQLITE_DEBUG]. - ** - **
      • [[SQLITE_FCNTL_SIZE_HINT]] - ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS -@@ -2086,6 +1419,11 @@ struct sqlite3_io_methods { - ** pointed to by the pArg argument. This capability is used during testing - ** and only needs to be supported when SQLITE_TEST is defined. - ** -+**
      • [[SQLITE_FCNTL_NULL_IO]] -+** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor -+** or file handle for the [sqlite3_file] object such that it will no longer -+** read or write to the database file. -+** - **
      • [[SQLITE_FCNTL_WAL_BLOCK]] - ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might - ** be advantageous to block on the next WAL lock if the lock is not immediately -@@ -2144,6 +1482,12 @@ struct sqlite3_io_methods { - ** the value that M is to be set to. Before returning, the 32-bit signed - ** integer is overwritten with the previous value of M. - ** -+**
      • [[SQLITE_FCNTL_BLOCK_ON_CONNECT]] -+** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the -+** VFS to block when taking a SHARED lock to connect to a wal mode database. -+** This is used to implement the functionality associated with -+** SQLITE_SETLK_BLOCK_ON_CONNECT. -+** - **
      • [[SQLITE_FCNTL_DATA_VERSION]] - ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to - ** a database file. The argument is a pointer to a 32-bit unsigned integer. -@@ -2174,6 +1518,28 @@ struct sqlite3_io_methods { - ** in wal mode after the client has finished copying pages from the wal - ** file to the database file, but before the *-shm file is updated to - ** record the fact that the pages have been checkpointed. -+** -+**
      • [[SQLITE_FCNTL_EXTERNAL_READER]] -+** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect -+** whether or not there is a database client in another process with a wal-mode -+** transaction open on the database or not. It is only available on unix.The -+** (void*) argument passed with this file-control should be a pointer to a -+** value of type (int). The integer value is set to 1 if the database is a wal -+** mode database and there exists at least one client in another process that -+** currently has an SQL transaction open on the database. It is set to 0 if -+** the database is not a wal-mode db, or if there is no such connection in any -+** other process. This opcode cannot be used to detect transactions opened -+** by clients within the current process, only within other processes. -+** -+**
      • [[SQLITE_FCNTL_CKSM_FILE]] -+** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the -+** [checksum VFS shim] only. -+** -+**
      • [[SQLITE_FCNTL_RESET_CACHE]] -+** If there is currently no transaction open on the database, and the -+** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control -+** purges the contents of the in-memory page cache. If there is an open -+** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. - ** - */ - #define SQLITE_FCNTL_LOCKSTATE 1 -@@ -2214,6 +1580,11 @@ struct sqlite3_io_methods { - #define SQLITE_FCNTL_CKPT_DONE 37 - #define SQLITE_FCNTL_RESERVE_BYTES 38 - #define SQLITE_FCNTL_CKPT_START 39 -+#define SQLITE_FCNTL_EXTERNAL_READER 40 -+#define SQLITE_FCNTL_CKSM_FILE 41 -+#define SQLITE_FCNTL_RESET_CACHE 42 -+#define SQLITE_FCNTL_NULL_IO 43 -+#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 - - /* deprecated names */ - #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE -@@ -2243,6 +1614,26 @@ typedef struct sqlite3_mutex sqlite3_mutex; - */ - typedef struct sqlite3_api_routines sqlite3_api_routines; - -+/* -+** CAPI3REF: File Name -+** -+** Type [sqlite3_filename] is used by SQLite to pass filenames to the -+** xOpen method of a [VFS]. It may be cast to (const char*) and treated -+** as a normal, nul-terminated, UTF-8 buffer containing the filename, but -+** may also be passed to special APIs such as: -+** -+**
          -+**
        • sqlite3_filename_database() -+**
        • sqlite3_filename_journal() -+**
        • sqlite3_filename_wal() -+**
        • sqlite3_uri_parameter() -+**
        • sqlite3_uri_boolean() -+**
        • sqlite3_uri_int64() -+**
        • sqlite3_uri_key() -+**
        -+*/ -+typedef const char *sqlite3_filename; -+ - /* - ** CAPI3REF: OS Interface Object - ** -@@ -2421,7 +1812,7 @@ struct sqlite3_vfs { - sqlite3_vfs *pNext; /* Next registered VFS */ - const char *zName; /* Name of this virtual file system */ - void *pAppData; /* Pointer to application-specific data */ -- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, -+ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*, - int flags, int *pOutFlags); - int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); - int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); -@@ -2608,20 +1999,23 @@ SQLITE_API int sqlite3_os_end(void); - ** must ensure that no other SQLite interfaces are invoked by other - ** threads while sqlite3_config() is running. - ** --** The sqlite3_config() interface --** may only be invoked prior to library initialization using --** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. --** ^If sqlite3_config() is called after [sqlite3_initialize()] and before --** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. --** Note, however, that ^sqlite3_config() can be called as part of the --** implementation of an application-defined [sqlite3_os_init()]. --** - ** The first argument to sqlite3_config() is an integer - ** [configuration option] that determines - ** what property of SQLite is to be configured. Subsequent arguments - ** vary depending on the [configuration option] - ** in the first argument. - ** -+** For most configuration options, the sqlite3_config() interface -+** may only be invoked prior to library initialization using -+** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -+** The exceptional configuration options that may be invoked at any time -+** are called "anytime configuration options". -+** ^If sqlite3_config() is called after [sqlite3_initialize()] and before -+** [sqlite3_shutdown()] with a first argument that is not an anytime -+** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. -+** Note, however, that ^sqlite3_config() can be called as part of the -+** implementation of an application-defined [sqlite3_os_init()]. -+** - ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. - ** ^If the option is unknown or SQLite is unable to set the option - ** then this routine returns a non-zero [error code]. -@@ -2729,6 +2123,23 @@ struct sqlite3_mem_methods { - ** These constants are the available integer configuration options that - ** can be passed as the first argument to the [sqlite3_config()] interface. - ** -+** Most of the configuration options for sqlite3_config() -+** will only work if invoked prior to [sqlite3_initialize()] or after -+** [sqlite3_shutdown()]. The few exceptions to this rule are called -+** "anytime configuration options". -+** ^Calling [sqlite3_config()] with a first argument that is not an -+** anytime configuration option in between calls to [sqlite3_initialize()] and -+** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE. -+** -+** The set of anytime configuration options can change (by insertions -+** and/or deletions) from one release of SQLite to the next. -+** As of SQLite version 3.42.0, the complete set of anytime configuration -+** options is: -+**
          -+**
        • SQLITE_CONFIG_LOG -+**
        • SQLITE_CONFIG_PCACHE_HDRSZ -+**
        -+** - ** New configuration options may be added in future releases of SQLite. - ** Existing configuration options might be discontinued. Applications - ** should check the return code from [sqlite3_config()] to make sure that -@@ -2904,13 +2315,16 @@ struct sqlite3_mem_methods { - ** - ** [[SQLITE_CONFIG_LOOKASIDE]]
        SQLITE_CONFIG_LOOKASIDE
        - **
        ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine --** the default size of lookaside memory on each [database connection]. -+** the default size of [lookaside memory] on each [database connection]. - ** The first argument is the --** size of each lookaside buffer slot and the second is the number of --** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE --** sets the default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] --** option to [sqlite3_db_config()] can be used to change the lookaside --** configuration on individual connections.)^
        -+** size of each lookaside buffer slot ("sz") and the second is the number of -+** slots allocated to each database connection ("cnt").)^ -+** ^(SQLITE_CONFIG_LOOKASIDE sets the default lookaside size. -+** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can -+** be used to change the lookaside configuration on individual connections.)^ -+** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the -+** default lookaside configuration at compile-time. -+**
      • - ** - ** [[SQLITE_CONFIG_PCACHE2]]
        SQLITE_CONFIG_PCACHE2
        - **
        ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is -@@ -3059,7 +2473,7 @@ struct sqlite3_mem_methods { - ** is stored in each sorted record and the required column values loaded - ** from the database as records are returned in sorted order. The default - ** value for this option is to never use this optimization. Specifying a --** negative value for this option restores the default behaviour. -+** negative value for this option restores the default behavior. - ** This option is only available if SQLite is compiled with the - ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. - ** -@@ -3073,30 +2487,46 @@ struct sqlite3_mem_methods { - ** configuration setting is never used, then the default maximum is determined - ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that - ** compile-time option is not set, then the default maximum is 1073741824. -+** -+** [[SQLITE_CONFIG_ROWID_IN_VIEW]] -+**
        SQLITE_CONFIG_ROWID_IN_VIEW -+**
        The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability -+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is -+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability -+** defaults to on. This configuration option queries the current setting or -+** changes the setting to off or on. The argument is a pointer to an integer. -+** If that integer initially holds a value of 1, then the ability for VIEWs to -+** have ROWIDs is activated. If the integer initially holds zero, then the -+** ability is deactivated. Any other initial value for the integer leaves the -+** setting unchanged. After changes, if any, the integer is written with -+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite -+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and -+** recommended case) then the integer is always filled with zero, regardless -+** if its initial value. - ** - */ --#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ --#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ --#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ --#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ --#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ --#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ --#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ --#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ --#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ --#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ --#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ --/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ --#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ --#define SQLITE_CONFIG_PCACHE 14 /* no-op */ --#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ --#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ --#define SQLITE_CONFIG_URI 17 /* int */ --#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ --#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ -+#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ -+#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ -+#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ -+#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ -+#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ -+#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ -+#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ -+#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ -+#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ -+#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ -+#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ -+/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -+#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -+#define SQLITE_CONFIG_PCACHE 14 /* no-op */ -+#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ -+#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ -+#define SQLITE_CONFIG_URI 17 /* int */ -+#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ -+#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ - #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ --#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ --#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ -+#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ -+#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ - #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ - #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ - #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ -@@ -3104,12 +2534,21 @@ struct sqlite3_mem_methods { - #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ - #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ - #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ -+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ - - /* - ** CAPI3REF: Database Connection Configuration Options - ** - ** These constants are the available integer configuration options that --** can be passed as the second argument to the [sqlite3_db_config()] interface. -+** can be passed as the second parameter to the [sqlite3_db_config()] interface. -+** -+** The [sqlite3_db_config()] interface is a var-args functions. It takes a -+** variable number of parameters, though always at least two. The number of -+** parameters passed into sqlite3_db_config() depends on which of these -+** constants is given as the second parameter. This documentation page -+** refers to parameters beyond the second as "arguments". Thus, when this -+** page says "the N-th argument" it means "the N-th parameter past the -+** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()". - ** - ** New configuration options may be added in future releases of SQLite. - ** Existing configuration options might be discontinued. Applications -@@ -3121,31 +2560,57 @@ struct sqlite3_mem_methods { - **
        - ** [[SQLITE_DBCONFIG_LOOKASIDE]] - **
        SQLITE_DBCONFIG_LOOKASIDE
        --**
        ^This option takes three additional arguments that determine the --** [lookaside memory allocator] configuration for the [database connection]. --** ^The first argument (the third parameter to [sqlite3_db_config()] is a -+**
        The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the -+** configuration of the [lookaside memory allocator] within a database -+** connection. -+** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are not -+** in the [DBCONFIG arguments|usual format]. -+** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two, -+** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE -+** should have a total of five parameters. -+**
          -+**
        1. The first argument ("buf") is a - ** pointer to a memory buffer to use for lookaside memory. --** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb --** may be NULL in which case SQLite will allocate the --** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the --** size of each lookaside buffer slot. ^The third argument is the number of --** slots. The size of the buffer in the first argument must be greater than --** or equal to the product of the second and third arguments. The buffer --** must be aligned to an 8-byte boundary. ^If the second argument to --** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally --** rounded down to the next smaller multiple of 8. ^(The lookaside memory -+** The first argument may be NULL in which case SQLite will allocate the -+** lookaside buffer itself using [sqlite3_malloc()]. -+**

        2. The second argument ("sz") is the -+** size of each lookaside buffer slot. Lookaside is disabled if "sz" -+** is less than 8. The "sz" argument should be a multiple of 8 less than -+** 65536. If "sz" does not meet this constraint, it is reduced in size until -+** it does. -+**

        3. The third argument ("cnt") is the number of slots. Lookaside is disabled -+** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so -+** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" -+** parameter is usually chosen so that the product of "sz" and "cnt" is less -+** than 1,000,000. -+**

        -+**

        If the "buf" argument is not NULL, then it must -+** point to a memory buffer with a size that is greater than -+** or equal to the product of "sz" and "cnt". -+** The buffer must be aligned to an 8-byte boundary. -+** The lookaside memory - ** configuration for a database connection can only be changed when that - ** connection is not currently using lookaside memory, or in other words --** when the "current value" returned by --** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. -+** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero. - ** Any attempt to change the lookaside memory configuration when lookaside - ** memory is in use leaves the configuration unchanged and returns --** [SQLITE_BUSY].)^

        -+** [SQLITE_BUSY]. -+** If the "buf" argument is NULL and an attempt -+** to allocate memory based on "sz" and "cnt" fails, then -+** lookaside is silently disabled. -+**

        -+** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the -+** default lookaside configuration at initialization. The -+** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside -+** configuration at compile-time. Typical values for lookaside are 1200 for -+** "sz" and 40 to 100 for "cnt". -+**

        - ** - ** [[SQLITE_DBCONFIG_ENABLE_FKEY]] - **
        SQLITE_DBCONFIG_ENABLE_FKEY
        - **
        ^This option is used to enable or disable the enforcement of --** [foreign key constraints]. There should be two additional arguments. -+** [foreign key constraints]. This is the same setting that is -+** enabled or disabled by the [PRAGMA foreign_keys] statement. - ** The first argument is an integer which is 0 to disable FK enforcement, - ** positive to enable FK enforcement or negative to leave FK enforcement - ** unchanged. The second parameter is a pointer to an integer into which -@@ -3162,25 +2627,37 @@ struct sqlite3_mem_methods { - ** The second parameter is a pointer to an integer into which - ** is written 0 or 1 to indicate whether triggers are disabled or enabled - ** following this call. The second parameter may be a NULL pointer, in --** which case the trigger setting is not reported back.
        -+** which case the trigger setting is not reported back. -+** -+**

        Originally this option disabled all triggers. ^(However, since -+** SQLite version 3.35.0, TEMP triggers are still allowed even if -+** this option is off. So, in other words, this option now only disables -+** triggers in the main database schema or in the schemas of [ATTACH]-ed -+** databases.)^ - ** - ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] - **

        SQLITE_DBCONFIG_ENABLE_VIEW
        - **
        ^This option is used to enable or disable [CREATE VIEW | views]. --** There should be two additional arguments. -+** There must be two additional arguments. - ** The first argument is an integer which is 0 to disable views, - ** positive to enable views or negative to leave the setting unchanged. - ** The second parameter is a pointer to an integer into which - ** is written 0 or 1 to indicate whether views are disabled or enabled - ** following this call. The second parameter may be a NULL pointer, in --** which case the view setting is not reported back.
        -+** which case the view setting is not reported back. -+** -+**

        Originally this option disabled all views. ^(However, since -+** SQLite version 3.35.0, TEMP views are still allowed even if -+** this option is off. So, in other words, this option now only disables -+** views in the main database schema or in the schemas of ATTACH-ed -+** databases.)^ - ** - ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] - **

        SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
        - **
        ^This option is used to enable or disable the - ** [fts3_tokenizer()] function which is part of the - ** [FTS3] full-text search engine extension. --** There should be two additional arguments. -+** There must be two additional arguments. - ** The first argument is an integer which is 0 to disable fts3_tokenizer() or - ** positive to enable fts3_tokenizer() or negative to leave the setting - ** unchanged. -@@ -3195,7 +2672,7 @@ struct sqlite3_mem_methods { - ** interface independently of the [load_extension()] SQL function. - ** The [sqlite3_enable_load_extension()] API enables or disables both the - ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. --** There should be two additional arguments. -+** There must be two additional arguments. - ** When the first argument to this interface is 1, then only the C-API is - ** enabled and the SQL function remains disabled. If the first argument to - ** this interface is 0, then both the C-API and the SQL function are disabled. -@@ -3209,23 +2686,30 @@ struct sqlite3_mem_methods { - ** - ** [[SQLITE_DBCONFIG_MAINDBNAME]]
        SQLITE_DBCONFIG_MAINDBNAME
        - **
        ^This option is used to change the name of the "main" database --** schema. ^The sole argument is a pointer to a constant UTF8 string --** which will become the new schema name in place of "main". ^SQLite --** does not make a copy of the new main schema name string, so the application --** must ensure that the argument passed into this DBCONFIG option is unchanged --** until after the database connection closes. -+** schema. This option does not follow the -+** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. -+** This option takes exactly one additional argument so that the -+** [sqlite3_db_config()] call has a total of three parameters. The -+** extra argument must be a pointer to a constant UTF8 string which -+** will become the new schema name in place of "main". ^SQLite does -+** not make a copy of the new main schema name string, so the application -+** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME -+** is unchanged until after the database connection closes. - **
        - ** - ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] - **
        SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
        --**
        Usually, when a database in wal mode is closed or detached from a --** database handle, SQLite checks if this will mean that there are now no --** connections at all to the database. If so, it performs a checkpoint --** operation before closing the connection. This option may be used to --** override this behaviour. The first parameter passed to this operation --** is an integer - positive to disable checkpoints-on-close, or zero (the --** default) to enable them, and negative to leave the setting unchanged. --** The second parameter is a pointer to an integer -+**
        Usually, when a database in [WAL mode] is closed or detached from a -+** database handle, SQLite checks if if there are other connections to the -+** same database, and if there are no other database connection (if the -+** connection being closed is the last open connection to the database), -+** then SQLite performs a [checkpoint] before closing the connection and -+** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can -+** be used to override that behavior. The first argument passed to this -+** operation (the third parameter to [sqlite3_db_config()]) is an integer -+** which is positive to disable checkpoints-on-close, or zero (the default) -+** to enable them, and negative to leave the setting unchanged. -+** The second argument (the fourth parameter) is a pointer to an integer - ** into which is written 0 or 1 to indicate whether checkpoints-on-close - ** have been disabled - 0 if they are not disabled, 1 if they are. - **
        -@@ -3275,8 +2759,12 @@ struct sqlite3_mem_methods { - **
      • sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); - ** - ** Because resetting a database is destructive and irreversible, the --** process requires the use of this obscure API and multiple steps to help --** ensure that it does not happen by accident. -+** process requires the use of this obscure API and multiple steps to -+** help ensure that it does not happen by accident. Because this -+** feature must be capable of resetting corrupt databases, and -+** shutting down virtual tables may require access to that corrupt -+** storage, the library must abandon any installed virtual tables -+** without calling their xDestroy() methods. - ** - ** [[SQLITE_DBCONFIG_DEFENSIVE]]
        SQLITE_DBCONFIG_DEFENSIVE
        - **
        The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the -@@ -3287,6 +2775,7 @@ struct sqlite3_mem_methods { - **
          - **
        • The [PRAGMA writable_schema=ON] statement. - **
        • The [PRAGMA journal_mode=OFF] statement. -+**
        • The [PRAGMA schema_version=N] statement. - **
        • Writes to the [sqlite_dbpage] virtual table. - **
        • Direct writes to [shadow tables]. - **
        -@@ -3314,7 +2803,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_DQS_DML]] --**
        SQLITE_DBCONFIG_DQS_DML
      • file:data.db?mode=readonly - ** An error. "readonly" is not a valid option for the "mode" parameter. -+** Use "ro" instead: "file:data.db?mode=ro". - **
        - ** - ** ^URI hexadecimal escape sequences (%HH) are supported within the path and -@@ -4595,7 +4280,7 @@ SQLITE_API int sqlite3_open_v2( - ** as F) must be one of: - **
          - **
        • A database filename pointer created by the SQLite core and --** passed into the xOpen() method of a VFS implemention, or -+** passed into the xOpen() method of a VFS implementation, or - **
        • A filename obtained from [sqlite3_db_filename()], or - **
        • A new filename constructed using [sqlite3_create_filename()]. - **
        -@@ -4650,10 +4335,10 @@ SQLITE_API int sqlite3_open_v2( - ** - ** See the [URI filename] documentation for additional information. - */ --SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); --SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); --SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); --SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); -+SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam); -+SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault); -+SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64); -+SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N); - - /* - ** CAPI3REF: Translate filenames -@@ -4682,9 +4367,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); - ** return value from [sqlite3_db_filename()], then the result is - ** undefined and is likely a memory access violation. - */ --SQLITE_API const char *sqlite3_filename_database(const char*); --SQLITE_API const char *sqlite3_filename_journal(const char*); --SQLITE_API const char *sqlite3_filename_wal(const char*); -+SQLITE_API const char *sqlite3_filename_database(sqlite3_filename); -+SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename); -+SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename); - - /* - ** CAPI3REF: Database File Corresponding To A Journal -@@ -4708,12 +4393,12 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); - /* - ** CAPI3REF: Create and Destroy VFS Filenames - ** --** These interfces are provided for use by [VFS shim] implementations and -+** These interfaces are provided for use by [VFS shim] implementations and - ** are not useful outside of that context. - ** - ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of - ** database filename D with corresponding journal file J and WAL file W and --** with N URI parameters key/values pairs in the array P. The result from -+** an array P of N URI Key/Value pairs. The result from - ** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that - ** is safe to pass to routines like: - **
          -@@ -4744,20 +4429,20 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); - ** If the Y parameter to sqlite3_free_filename(Y) is anything other - ** than a NULL pointer or a pointer previously acquired from - ** sqlite3_create_filename(), then bad things such as heap --** corruption or segfaults may occur. The value Y should be -+** corruption or segfaults may occur. The value Y should not be - ** used again after sqlite3_free_filename(Y) has been called. This means - ** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y, - ** then the corresponding [sqlite3_module.xClose() method should also be - ** invoked prior to calling sqlite3_free_filename(Y). - */ --SQLITE_API char *sqlite3_create_filename( -+SQLITE_API sqlite3_filename sqlite3_create_filename( - const char *zDatabase, - const char *zJournal, - const char *zWal, - int nParam, - const char **azParam - ); --SQLITE_API void sqlite3_free_filename(char*); -+SQLITE_API void sqlite3_free_filename(sqlite3_filename); - - /* - ** CAPI3REF: Error Codes And Messages -@@ -4776,27 +4461,38 @@ SQLITE_API void sqlite3_free_filename(char*); - ** sqlite3_extended_errcode() might change with each API call. - ** Except, there are some interfaces that are guaranteed to never - ** change the value of the error code. The error-code preserving --** interfaces are: -+** interfaces include the following: - ** - **
            - **
          • sqlite3_errcode() - **
          • sqlite3_extended_errcode() - **
          • sqlite3_errmsg() - **
          • sqlite3_errmsg16() -+**
          • sqlite3_error_offset() - **
          - ** - ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language --** text that describes the error, as either UTF-8 or UTF-16 respectively. -+** text that describes the error, as either UTF-8 or UTF-16 respectively, -+** or NULL if no error message is available. -+** (See how SQLite handles [invalid UTF] for exceptions to this rule.) - ** ^(Memory to hold the error message string is managed internally. - ** The application does not need to worry about freeing the result. - ** However, the error string might be overwritten or deallocated by - ** subsequent calls to other SQLite interface functions.)^ - ** --** ^The sqlite3_errstr() interface returns the English-language text --** that describes the [result code], as UTF-8. -+** ^The sqlite3_errstr(E) interface returns the English-language text -+** that describes the [result code] E, as UTF-8, or NULL if E is not an -+** result code for which a text error message is available. - ** ^(Memory to hold the error message string is managed internally - ** and must not be freed by the application)^. - ** -+** ^If the most recent error references a specific token in the input -+** SQL, the sqlite3_error_offset() interface returns the byte offset -+** of the start of that token. ^The byte offset returned by -+** sqlite3_error_offset() assumes that the input SQL is UTF8. -+** ^If the most recent error does not reference a specific token in the input -+** SQL, then the sqlite3_error_offset() function returns -1. -+** - ** When the serialized [threading mode] is in use, it might be the - ** case that a second error occurs on a separate thread in between - ** the time of the first error and the call to these interfaces. -@@ -4816,6 +4512,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); - SQLITE_API const char *sqlite3_errmsg(sqlite3*); - SQLITE_API const void *sqlite3_errmsg16(sqlite3*); - SQLITE_API const char *sqlite3_errstr(int); -+SQLITE_API int sqlite3_error_offset(sqlite3 *db); - - /* - ** CAPI3REF: Prepared Statement Object -@@ -4987,11 +4684,22 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); - **
          The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler - ** to return an error (error code SQLITE_ERROR) if the statement uses - ** any virtual tables. -+** -+** [[SQLITE_PREPARE_DONT_LOG]]
          SQLITE_PREPARE_DONT_LOG
          -+**
          The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler -+** errors from being sent to the error log defined by -+** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test -+** compiles to see if some SQL syntax is well-formed, without generating -+** messages on the global error log when it is not. If the test compile -+** fails, the sqlite3_prepare_v3() call returns the same error indications -+** with or without this flag; it just omits the call to [sqlite3_log()] that -+** logs the error. - **
    - */ - #define SQLITE_PREPARE_PERSISTENT 0x01 - #define SQLITE_PREPARE_NORMALIZE 0x02 - #define SQLITE_PREPARE_NO_VTAB 0x04 -+#define SQLITE_PREPARE_DONT_LOG 0x10 - - /* - ** CAPI3REF: Compiling An SQL Statement -@@ -5024,13 +4732,17 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); - ** and sqlite3_prepare16_v3() use UTF-16. - ** - ** ^If the nByte argument is negative, then zSql is read up to the --** first zero terminator. ^If nByte is positive, then it is the --** number of bytes read from zSql. ^If nByte is zero, then no prepared -+** first zero terminator. ^If nByte is positive, then it is the maximum -+** number of bytes read from zSql. When nByte is positive, zSql is read -+** up to the first zero terminator or until the nByte bytes have been read, -+** whichever comes first. ^If nByte is zero, then no prepared - ** statement is generated. - ** If the caller knows that the supplied string is nul-terminated, then - ** there is a small performance advantage to passing an nByte parameter that - ** is the number of bytes in the input string including - ** the nul-terminator. -+** Note that nByte measure the length of the input in bytes, not -+** characters, even for the UTF-16 interfaces. - ** - ** ^If pzTail is not NULL then *pzTail is made to point to the first byte - ** past the end of the first SQL statement in zSql. These routines only -@@ -5173,12 +4885,17 @@ SQLITE_API int sqlite3_prepare16_v3( - ** are managed by SQLite and are automatically freed when the prepared - ** statement is finalized. - ** ^The string returned by sqlite3_expanded_sql(P), on the other hand, --** is obtained from [sqlite3_malloc()] and must be free by the application -+** is obtained from [sqlite3_malloc()] and must be freed by the application - ** by passing it to [sqlite3_free()]. -+** -+** ^The sqlite3_normalized_sql() interface is only available if -+** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. - */ - SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); - SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); -+#ifdef SQLITE_ENABLE_NORMALIZE - SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); -+#endif - - /* - ** CAPI3REF: Determine If An SQL Statement Writes The Database -@@ -5213,6 +4930,19 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); - ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and - ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so - ** sqlite3_stmt_readonly() returns false for those commands. -+** -+** ^This routine returns false if there is any possibility that the -+** statement might change the database file. ^A false return does -+** not guarantee that the statement will change the database file. -+** ^For example, an UPDATE statement might have a WHERE clause that -+** makes it a no-op, but the sqlite3_stmt_readonly() result would still -+** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a -+** read-only no-op if the table already exists, but -+** sqlite3_stmt_readonly() still returns false for such a statement. -+** -+** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] -+** statement, then sqlite3_stmt_readonly(X) returns the same value as -+** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. - */ - SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - -@@ -5228,6 +4958,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - */ - SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); - -+/* -+** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement -+** METHOD: sqlite3_stmt -+** -+** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN -+** setting for [prepared statement] S. If E is zero, then S becomes -+** a normal prepared statement. If E is 1, then S behaves as if -+** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if -+** its SQL text began with "[EXPLAIN QUERY PLAN]". -+** -+** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared. -+** SQLite tries to avoid a reprepare, but a reprepare might be necessary -+** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode. -+** -+** Because of the potential need to reprepare, a call to -+** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be -+** reprepared because it was created using [sqlite3_prepare()] instead of -+** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and -+** hence has no saved SQL text with which to reprepare. -+** -+** Changing the explain setting for a prepared statement does not change -+** the original SQL text for the statement. Hence, if the SQL text originally -+** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) -+** is called to convert the statement into an ordinary statement, the EXPLAIN -+** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) -+** output, even though the statement now acts like a normal SQL statement. -+** -+** This routine returns SQLITE_OK if the explain mode is successfully -+** changed, or an error code if the explain mode could not be changed. -+** The explain mode cannot be changed while a statement is active. -+** Hence, it is good practice to call [sqlite3_reset(S)] -+** immediately prior to calling sqlite3_stmt_explain(S,E). -+*/ -+SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode); -+ - /* - ** CAPI3REF: Determine If A Prepared Statement Has Been Reset - ** METHOD: sqlite3_stmt -@@ -5281,6 +5046,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); - ** - ** ^The sqlite3_value objects that are passed as parameters into the - ** implementation of [application-defined SQL functions] are protected. -+** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()] -+** are protected. - ** ^The sqlite3_value object returned by - ** [sqlite3_column_value()] is unprotected. - ** Unprotected sqlite3_value objects may only be used as arguments -@@ -5312,7 +5079,7 @@ typedef struct sqlite3_context sqlite3_context; - ** METHOD: sqlite3_stmt - ** - ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, --** literals may be replaced by a [parameter] that matches one of following -+** literals may be replaced by a [parameter] that matches one of the following - ** templates: - ** - **
      -@@ -5357,7 +5124,7 @@ typedef struct sqlite3_context sqlite3_context; - ** - ** [[byte-order determination rules]] ^The byte-order of - ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) --** found in first character, which is removed, or in the absence of a BOM -+** found in the first character, which is removed, or in the absence of a BOM - ** the byte order is the native byte order of the host - ** machine for sqlite3_bind_text16() or the byte order specified in - ** the 6th parameter for sqlite3_bind_text64().)^ -@@ -5377,23 +5144,27 @@ typedef struct sqlite3_context sqlite3_context; - ** or sqlite3_bind_text16() or sqlite3_bind_text64() then - ** that parameter must be the byte offset - ** where the NUL terminator would occur assuming the string were NUL --** terminated. If any NUL characters occurs at byte offsets less than -+** terminated. If any NUL characters occur at byte offsets less than - ** the value of the fourth parameter then the resulting string value will - ** contain embedded NULs. The result of expressions involving strings - ** with embedded NULs is undefined. - ** --** ^The fifth argument to the BLOB and string binding interfaces --** is a destructor used to dispose of the BLOB or --** string after SQLite has finished with it. ^The destructor is called --** to dispose of the BLOB or string even if the call to the bind API fails, --** except the destructor is not called if the third parameter is a NULL --** pointer or the fourth parameter is negative. --** ^If the fifth argument is --** the special value [SQLITE_STATIC], then SQLite assumes that the --** information is in static, unmanaged space and does not need to be freed. --** ^If the fifth argument has the value [SQLITE_TRANSIENT], then --** SQLite makes its own private copy of the data immediately, before --** the sqlite3_bind_*() routine returns. -+** ^The fifth argument to the BLOB and string binding interfaces controls -+** or indicates the lifetime of the object referenced by the third parameter. -+** These three options exist: -+** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished -+** with it may be passed. ^It is called to dispose of the BLOB or string even -+** if the call to the bind API fails, except the destructor is not called if -+** the third parameter is a NULL pointer or the fourth parameter is negative. -+** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that -+** the application remains responsible for disposing of the object. ^In this -+** case, the object and the provided pointer to it must remain valid until -+** either the prepared statement is finalized or the same SQL parameter is -+** bound to something else, whichever occurs sooner. -+** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the -+** object is to be copied prior to the return from sqlite3_bind_*(). ^The -+** object and pointer to it must remain valid until then. ^SQLite will then -+** manage the lifetime of its private copy. - ** - ** ^The sixth argument to sqlite3_bind_text64() must be one of - ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] -@@ -5585,7 +5356,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); - ** METHOD: sqlite3_stmt - ** - ** ^These routines provide a means to determine the database, table, and --** table column that is the origin of a particular result column in -+** table column that is the origin of a particular result column in a - ** [SELECT] statement. - ** ^The name of the database or table or column can be returned as - ** either a UTF-8 or UTF-16 string. ^The _database_ routines return -@@ -5723,7 +5494,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); - ** other than [SQLITE_ROW] before any subsequent invocation of - ** sqlite3_step(). Failure to reset the prepared statement using - ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from --** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], -+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), - ** sqlite3_step() began - ** calling [sqlite3_reset()] automatically in this circumstance rather - ** than returning [SQLITE_MISUSE]. This is not considered a compatibility -@@ -5898,6 +5669,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - ** even empty strings, are always zero-terminated. ^The return - ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. - ** -+** ^Strings returned by sqlite3_column_text16() always have the endianness -+** which is native to the platform, regardless of the text encoding set -+** for the database. -+** - ** Warning: ^The object returned by [sqlite3_column_value()] is an - ** [unprotected sqlite3_value] object. In a multithreaded environment, - ** an unprotected sqlite3_value object may only be used safely with -@@ -5911,7 +5686,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - ** [application-defined SQL functions] or [virtual tables], not within - ** top-level application code. - ** --** The these routines may attempt to convert the datatype of the result. -+** These routines may attempt to convert the datatype of the result. - ** ^For example, if the internal representation is FLOAT and a text result - ** is requested, [sqlite3_snprintf()] is used internally to perform the - ** conversion automatically. ^(The following table details the conversions -@@ -5936,7 +5711,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - ** TEXT BLOB No change - ** BLOB INTEGER [CAST] to INTEGER - ** BLOB FLOAT [CAST] to REAL --** BLOB TEXT Add a zero terminator if needed -+** BLOB TEXT [CAST] to TEXT, ensure zero terminator - ** - ** )^ - ** -@@ -6060,20 +5835,33 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); - ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S - ** back to the beginning of its program. - ** --** ^If the most recent call to [sqlite3_step(S)] for the --** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], --** or if [sqlite3_step(S)] has never before been called on S, --** then [sqlite3_reset(S)] returns [SQLITE_OK]. -+** ^The return code from [sqlite3_reset(S)] indicates whether or not -+** the previous evaluation of prepared statement S completed successfully. -+** ^If [sqlite3_step(S)] has never before been called on S or if -+** [sqlite3_step(S)] has not been called since the previous call -+** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return -+** [SQLITE_OK]. - ** - ** ^If the most recent call to [sqlite3_step(S)] for the - ** [prepared statement] S indicated an error, then - ** [sqlite3_reset(S)] returns an appropriate [error code]. -+** ^The [sqlite3_reset(S)] interface might also return an [error code] -+** if there were no prior errors but the process of resetting -+** the prepared statement caused a new error. ^For example, if an -+** [INSERT] statement with a [RETURNING] clause is only stepped one time, -+** that one call to [sqlite3_step(S)] might return SQLITE_ROW but -+** the overall statement might still fail and the [sqlite3_reset(S)] call -+** might return SQLITE_BUSY if locking constraints prevent the -+** database change from committing. Therefore, it is important that -+** applications check the return code from [sqlite3_reset(S)] even if -+** no prior call to [sqlite3_step(S)] indicated a problem. - ** - ** ^The [sqlite3_reset(S)] interface does not change the values - ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. - */ - SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - -+ - /* - ** CAPI3REF: Create Or Redefine SQL Functions - ** KEYWORDS: {function creation routines} -@@ -6135,17 +5923,15 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, - ** index expressions, or the WHERE clause of partial indexes. - ** --** - ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for - ** all application-defined SQL functions that do not need to be --** used inside of triggers, view, CHECK constraints, or other elements of --** the database schema. This flags is especially recommended for SQL -+** used inside of triggers, views, CHECK constraints, or other elements of -+** the database schema. This flag is especially recommended for SQL - ** functions that have side effects or reveal internal application state. - ** Without this flag, an attacker might be able to modify the schema of - ** a database file to include invocations of the function with parameters - ** chosen by the attacker, which the application will then execute when - ** the database file is opened and read. --** - ** - ** ^(The fifth parameter is an arbitrary pointer. The implementation of the - ** function can gain access to this pointer using [sqlite3_user_data()].)^ -@@ -6171,7 +5957,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - ** [user-defined window functions|available here]. - ** - ** ^(If the final parameter to sqlite3_create_function_v2() or --** sqlite3_create_window_function() is not NULL, then it is destructor for -+** sqlite3_create_window_function() is not NULL, then it is the destructor for - ** the application data pointer. The destructor is invoked when the function - ** is deleted, either by being overloaded or when the database connection - ** closes.)^ ^The destructor is also invoked if the call to -@@ -6281,10 +6067,21 @@ SQLITE_API int sqlite3_create_window_function( - ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in - ** schema structures such as [CHECK constraints], [DEFAULT clauses], - ** [expression indexes], [partial indexes], or [generated columns]. --** The SQLITE_DIRECTONLY flags is a security feature which is recommended --** for all [application-defined SQL functions], and especially for functions --** that have side-effects or that could potentially leak sensitive --** information. -+**

      -+** The SQLITE_DIRECTONLY flag is recommended for any -+** [application-defined SQL function] -+** that has side-effects or that could potentially leak sensitive information. -+** This will prevent attacks in which an application is tricked -+** into using a database file that has had its schema surreptitiously -+** modified to invoke the application-defined function in ways that are -+** harmful. -+**

      -+** Some people say it is good practice to set SQLITE_DIRECTONLY on all -+** [application-defined SQL functions], regardless of whether or not they -+** are security sensitive, as doing so prevents those functions from being used -+** inside of the database schema, and thus ensures that the database -+** can be inspected and modified using generic tools (such as the [CLI]) -+** that do not have access to the application-defined functions. - ** - ** - ** [[SQLITE_INNOCUOUS]]

      SQLITE_INNOCUOUS
      -@@ -6311,13 +6108,36 @@ SQLITE_API int sqlite3_create_window_function( - **
      - ** - ** [[SQLITE_SUBTYPE]]
      SQLITE_SUBTYPE
      --** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call -+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call - ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. --** Specifying this flag makes no difference for scalar or aggregate user --** functions. However, if it is not specified for a user-defined window --** function, then any sub-types belonging to arguments passed to the window --** function may be discarded before the window function is called (i.e. --** sqlite3_value_subtype() will always return 0). -+** This flag instructs SQLite to omit some corner-case optimizations that -+** might disrupt the operation of the [sqlite3_value_subtype()] function, -+** causing it to return zero rather than the correct subtype(). -+** All SQL functions that invoke [sqlite3_value_subtype()] should have this -+** property. If the SQLITE_SUBTYPE property is omitted, then the return -+** value from [sqlite3_value_subtype()] might sometimes be zero even though -+** a non-zero subtype was specified by the function argument expression. -+** -+** [[SQLITE_RESULT_SUBTYPE]]
      SQLITE_RESULT_SUBTYPE
      -+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call -+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its -+** result. -+** Every function that invokes [sqlite3_result_subtype()] should have this -+** property. If it does not, then the call to [sqlite3_result_subtype()] -+** might become a no-op if the function is used as term in an -+** [expression index]. On the other hand, SQL functions that never invoke -+** [sqlite3_result_subtype()] should avoid setting this property, as the -+** purpose of this property is to disable certain optimizations that are -+** incompatible with subtypes. -+** -+** [[SQLITE_SELFORDER1]]
      SQLITE_SELFORDER1
      -+** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate -+** that internally orders the values provided to the first argument. The -+** ordered-set aggregate SQL notation with a single ORDER BY term can be -+** used to invoke this function. If the ordered-set aggregate notation is -+** used on a function that lacks this flag, then an error is raised. Note -+** that the ordered-set aggregate syntax is only available if SQLite is -+** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. - **
      - ** - */ -@@ -6325,6 +6145,8 @@ SQLITE_API int sqlite3_create_window_function( - #define SQLITE_DIRECTONLY 0x000080000 - #define SQLITE_SUBTYPE 0x000100000 - #define SQLITE_INNOCUOUS 0x000200000 -+#define SQLITE_RESULT_SUBTYPE 0x001000000 -+#define SQLITE_SELFORDER1 0x002000000 - - /* - ** CAPI3REF: Deprecated Functions -@@ -6490,6 +6312,28 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); - SQLITE_API int sqlite3_value_nochange(sqlite3_value*); - SQLITE_API int sqlite3_value_frombind(sqlite3_value*); - -+/* -+** CAPI3REF: Report the internal text encoding state of an sqlite3_value object -+** METHOD: sqlite3_value -+** -+** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], -+** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding -+** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) -+** returns something other than SQLITE_TEXT, then the return value from -+** sqlite3_value_encoding(X) is meaningless. ^Calls to -+** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)], -+** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or -+** [sqlite3_value_bytes16(X)] might change the encoding of the value X and -+** thus change the return from subsequent calls to sqlite3_value_encoding(X). -+** -+** This routine is intended for used by applications that test and validate -+** the SQLite implementation. This routine is inquiring about the opaque -+** internal state of an [sqlite3_value] object. Ordinary applications should -+** not need to know what the internal state of an sqlite3_value object is and -+** hence should not need to use this interface. -+*/ -+SQLITE_API int sqlite3_value_encoding(sqlite3_value*); -+ - /* - ** CAPI3REF: Finding The Subtype Of SQL Values - ** METHOD: sqlite3_value -@@ -6499,6 +6343,12 @@ SQLITE_API int sqlite3_value_frombind(sqlite3_value*); - ** information can be used to pass a limited amount of context from - ** one SQL function to another. Use the [sqlite3_result_subtype()] - ** routine to set the subtype for the return value of an SQL function. -+** -+** Every [application-defined SQL function] that invokes this interface -+** should include the [SQLITE_SUBTYPE] property in the text -+** encoding argument when the function is [sqlite3_create_function|registered]. -+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() -+** might return zero instead of the upstream subtype in some corner cases. - */ - SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); - -@@ -6507,10 +6357,11 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); - ** METHOD: sqlite3_value - ** - ** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value] --** object D and returns a pointer to that copy. ^The [sqlite3_value] returned -+** object V and returns a pointer to that copy. ^The [sqlite3_value] returned - ** is a [protected sqlite3_value] object even if the input is not. - ** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a --** memory allocation fails. -+** memory allocation fails. ^If V is a [pointer value], then the result -+** of sqlite3_value_dup(V) is a NULL value. - ** - ** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object - ** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer -@@ -6541,10 +6392,10 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*); - ** - ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer - ** when first called if N is less than or equal to zero or if a memory --** allocate error occurs. -+** allocation error occurs. - ** - ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is --** determined by the N parameter on first successful call. Changing the -+** determined by the N parameter on the first successful call. Changing the - ** value of N in any subsequent call to sqlite3_aggregate_context() within - ** the same aggregate function instance will not resize the memory - ** allocation.)^ Within the xFinal callback, it is customary to set -@@ -6596,48 +6447,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - ** METHOD: sqlite3_context - ** - ** These functions may be used by (non-aggregate) SQL functions to --** associate metadata with argument values. If the same value is passed to --** multiple invocations of the same SQL function during query execution, under --** some circumstances the associated metadata may be preserved. An example --** of where this might be useful is in a regular-expression matching --** function. The compiled version of the regular expression can be stored as --** metadata associated with the pattern string. -+** associate auxiliary data with argument values. If the same argument -+** value is passed to multiple invocations of the same SQL function during -+** query execution, under some circumstances the associated auxiliary data -+** might be preserved. An example of where this might be useful is in a -+** regular-expression matching function. The compiled version of the regular -+** expression can be stored as auxiliary data associated with the pattern string. - ** Then as long as the pattern string remains the same, - ** the compiled regular expression can be reused on multiple - ** invocations of the same function. - ** --** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata -+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data - ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument - ** value to the application-defined function. ^N is zero for the left-most --** function argument. ^If there is no metadata -+** function argument. ^If there is no auxiliary data - ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface - ** returns a NULL pointer. - ** --** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th --** argument of the application-defined function. ^Subsequent -+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the -+** N-th argument of the application-defined function. ^Subsequent - ** calls to sqlite3_get_auxdata(C,N) return P from the most recent --** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or --** NULL if the metadata has been discarded. -+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or -+** NULL if the auxiliary data has been discarded. - ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, - ** SQLite will invoke the destructor function X with parameter P exactly --** once, when the metadata is discarded. --** SQLite is free to discard the metadata at any time, including:
        -+** once, when the auxiliary data is discarded. -+** SQLite is free to discard the auxiliary data at any time, including:
          - **
        • ^(when the corresponding function parameter changes)^, or - **
        • ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the - ** SQL statement)^, or - **
        • ^(when sqlite3_set_auxdata() is invoked again on the same - ** parameter)^, or - **
        • ^(during the original sqlite3_set_auxdata() call when a memory --** allocation error occurs.)^
        -+** allocation error occurs.)^ -+**
      • ^(during the original sqlite3_set_auxdata() call if the function -+** is evaluated during query planning instead of during query execution, -+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^
      - ** --** Note the last bullet in particular. The destructor X in -+** Note the last two bullets in particular. The destructor X in - ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the - ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() - ** should be called near the end of the function implementation and the - ** function implementation should not make any use of P after --** sqlite3_set_auxdata() has been called. --** --** ^(In practice, metadata is preserved between function calls for -+** sqlite3_set_auxdata() has been called. Furthermore, a call to -+** sqlite3_get_auxdata() that occurs immediately after a corresponding call -+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory -+** condition occurred during the sqlite3_set_auxdata() call or if the -+** function is being evaluated during query planning rather than during -+** query execution. -+** -+** ^(In practice, auxiliary data is preserved between function calls for - ** function parameters that are compile-time constants, including literal - ** values and [parameters] and expressions composed from the same.)^ - ** -@@ -6647,10 +6506,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - ** - ** These routines must be called from the same thread in which - ** the SQL function is running. -+** -+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()]. - */ - SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); - SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); - -+/* -+** CAPI3REF: Database Connection Client Data -+** METHOD: sqlite3 -+** -+** These functions are used to associate one or more named pointers -+** with a [database connection]. -+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P -+** to be attached to [database connection] D using name N. Subsequent -+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P -+** or a NULL pointer if there were no prior calls to -+** sqlite3_set_clientdata() with the same values of D and N. -+** Names are compared using strcmp() and are thus case sensitive. -+** -+** If P and X are both non-NULL, then the destructor X is invoked with -+** argument P on the first of the following occurrences: -+**
        -+**
      • An out-of-memory error occurs during the call to -+** sqlite3_set_clientdata() which attempts to register pointer P. -+**
      • A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made -+** with the same D and N parameters. -+**
      • The database connection closes. SQLite does not make any guarantees -+** about the order in which destructors are called, only that all -+** destructors will be called exactly once at some point during the -+** database connection closing process. -+**
      -+** -+** SQLite does not do anything with client data other than invoke -+** destructors on the client data at the appropriate time. The intended -+** use for client data is to provide a mechanism for wrapper libraries -+** to store additional information about an SQLite database connection. -+** -+** There is no limit (other than available memory) on the number of different -+** client data pointers (with different names) that can be attached to a -+** single database connection. However, the implementation is optimized -+** for the case of having only one or two different client data names. -+** Applications and wrapper libraries are discouraged from using more than -+** one client data name each. -+** -+** There is no way to enumerate the client data pointers -+** associated with a database connection. The N parameter can be thought -+** of as a secret key such that only code that knows the secret key is able -+** to access the associated data. -+** -+** Security Warning: These interfaces should not be exposed in scripting -+** languages or in other circumstances where it might be possible for an -+** attacker to invoke them. Any agent that can invoke these interfaces -+** can probably also take control of the process. -+** -+** Database connection client data is only available for SQLite -+** version 3.44.0 ([dateof:3.44.0]) and later. -+** -+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. -+*/ -+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*); -+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*)); - - /* - ** CAPI3REF: Constants Defining Special Destructor Behavior -@@ -6746,15 +6662,16 @@ typedef void (*sqlite3_destructor_type)(void*); - ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. - ** ^SQLite takes the text result from the application from - ** the 2nd parameter of the sqlite3_result_text* interfaces. --** ^If the 3rd parameter to the sqlite3_result_text* interfaces --** is negative, then SQLite takes result text from the 2nd parameter --** through the first zero character. -+** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces -+** other than sqlite3_result_text64() is negative, then SQLite computes -+** the string length itself by searching the 2nd parameter for the first -+** zero character. - ** ^If the 3rd parameter to the sqlite3_result_text* interfaces - ** is non-negative, then as many bytes (not characters) of the text - ** pointed to by the 2nd parameter are taken as the application-defined - ** function result. If the 3rd parameter is non-negative, then it - ** must be the byte offset into the string where the NUL terminator would --** appear if the string where NUL terminated. If any NUL characters occur -+** appear if the string were NUL terminated. If any NUL characters occur - ** in the string at a byte offset that is less than the value of the 3rd - ** parameter, then the resulting string will contain embedded NULs and the - ** result of expressions operating on strings with embedded NULs is undefined. -@@ -6812,7 +6729,7 @@ typedef void (*sqlite3_destructor_type)(void*); - ** string and preferably a string literal. The sqlite3_result_pointer() - ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. - ** --** If these routines are called from within the different thread -+** If these routines are called from within a different thread - ** than the one containing the application-defined function that received - ** the [sqlite3_context] pointer, the results are undefined. - */ -@@ -6851,6 +6768,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); - ** higher order bits are discarded. - ** The number of subtype bytes preserved by SQLite might increase - ** in future releases of SQLite. -+** -+** Every [application-defined SQL function] that invokes this interface -+** should include the [SQLITE_RESULT_SUBTYPE] property in its -+** text encoding argument when the SQL function is -+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] -+** property is omitted from the function that invokes sqlite3_result_subtype(), -+** then in some cases the sqlite3_result_subtype() might fail to set -+** the result subtype. -+** -+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any -+** SQL function that invokes the sqlite3_result_subtype() interface -+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise -+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 -+** by default. - */ - SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); - -@@ -7022,6 +6953,13 @@ SQLITE_API void sqlite3_activate_cerod( - ** of the default VFS is not implemented correctly, or not implemented at - ** all, then the behavior of sqlite3_sleep() may deviate from the description - ** in the previous paragraphs. -+** -+** If a negative argument is passed to sqlite3_sleep() the results vary by -+** VFS and operating system. Some system treat a negative argument as an -+** instruction to sleep forever. Others understand it to mean do not sleep -+** at all. ^In SQLite version 3.42.0 and later, a negative -+** argument passed into sqlite3_sleep() is changed to zero before it is relayed -+** down into the xSleep method of the VFS. - */ - SQLITE_API int sqlite3_sleep(int); - -@@ -7192,6 +7130,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); - */ - SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - -+/* -+** CAPI3REF: Return The Schema Name For A Database Connection -+** METHOD: sqlite3 -+** -+** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name -+** for the N-th database on database connection D, or a NULL pointer if N is -+** out of range. An N value of 0 means the main database file. An N of 1 is -+** the "temp" schema. Larger values of N correspond to various ATTACH-ed -+** databases. -+** -+** Space to hold the string that is returned by sqlite3_db_name() is managed -+** by SQLite itself. The string might be deallocated by any operation that -+** changes the schema, including [ATTACH] or [DETACH] or calls to -+** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that -+** occur on a different thread. Applications that need to -+** remember the string long-term should make their own copy. Applications that -+** are accessing the same database connection simultaneously on multiple -+** threads should mutex-protect calls to this API and should make their own -+** private copy of the result prior to releasing the mutex. -+*/ -+SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N); -+ - /* - ** CAPI3REF: Return The Filename For A Database Connection - ** METHOD: sqlite3 -@@ -7222,7 +7182,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - **
    • [sqlite3_filename_wal()] - **
    - */ --SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); -+SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName); - - /* - ** CAPI3REF: Determine if a database is read-only -@@ -7234,6 +7194,57 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); - */ - SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); - -+/* -+** CAPI3REF: Determine the transaction state of a database -+** METHOD: sqlite3 -+** -+** ^The sqlite3_txn_state(D,S) interface returns the current -+** [transaction state] of schema S in database connection D. ^If S is NULL, -+** then the highest transaction state of any schema on database connection D -+** is returned. Transaction states are (in order of lowest to highest): -+**
      -+**
    1. SQLITE_TXN_NONE -+**
    2. SQLITE_TXN_READ -+**
    3. SQLITE_TXN_WRITE -+**
    -+** ^If the S argument to sqlite3_txn_state(D,S) is not the name of -+** a valid schema, then -1 is returned. -+*/ -+SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); -+ -+/* -+** CAPI3REF: Allowed return values from sqlite3_txn_state() -+** KEYWORDS: {transaction state} -+** -+** These constants define the current transaction state of a database file. -+** ^The [sqlite3_txn_state(D,S)] interface returns one of these -+** constants in order to describe the transaction state of schema S -+** in [database connection] D. -+** -+**
    -+** [[SQLITE_TXN_NONE]]
    SQLITE_TXN_NONE
    -+**
    The SQLITE_TXN_NONE state means that no transaction is currently -+** pending.
    -+** -+** [[SQLITE_TXN_READ]]
    SQLITE_TXN_READ
    -+**
    The SQLITE_TXN_READ state means that the database is currently -+** in a read transaction. Content has been read from the database file -+** but nothing in the database file has changed. The transaction state -+** will be advanced to SQLITE_TXN_WRITE if any changes occur and there are -+** no other conflicting concurrent write transactions. The transaction -+** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or -+** [COMMIT].
    -+** -+** [[SQLITE_TXN_WRITE]]
    SQLITE_TXN_WRITE
    -+**
    The SQLITE_TXN_WRITE state means that the database is currently -+** in a write transaction. Content has been written to the database file -+** but has not yet committed. The transaction state will change to -+** SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].
    -+*/ -+#define SQLITE_TXN_NONE 0 -+#define SQLITE_TXN_READ 1 -+#define SQLITE_TXN_WRITE 2 -+ - /* - ** CAPI3REF: Find the next prepared statement - ** METHOD: sqlite3 -@@ -7300,6 +7311,72 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); - SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); - SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - -+/* -+** CAPI3REF: Autovacuum Compaction Amount Callback -+** METHOD: sqlite3 -+** -+** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback -+** function C that is invoked prior to each autovacuum of the database -+** file. ^The callback is passed a copy of the generic data pointer (P), -+** the schema-name of the attached database that is being autovacuumed, -+** the size of the database file in pages, the number of free pages, -+** and the number of bytes per page, respectively. The callback should -+** return the number of free pages that should be removed by the -+** autovacuum. ^If the callback returns zero, then no autovacuum happens. -+** ^If the value returned is greater than or equal to the number of -+** free pages, then a complete autovacuum happens. -+** -+**

    ^If there are multiple ATTACH-ed database files that are being -+** modified as part of a transaction commit, then the autovacuum pages -+** callback is invoked separately for each file. -+** -+**

    The callback is not reentrant. The callback function should -+** not attempt to invoke any other SQLite interface. If it does, bad -+** things may happen, including segmentation faults and corrupt database -+** files. The callback function should be a simple function that -+** does some arithmetic on its input parameters and returns a result. -+** -+** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional -+** destructor for the P parameter. ^If X is not NULL, then X(P) is -+** invoked whenever the database connection closes or when the callback -+** is overwritten by another invocation of sqlite3_autovacuum_pages(). -+** -+**

    ^There is only one autovacuum pages callback per database connection. -+** ^Each call to the sqlite3_autovacuum_pages() interface overrides all -+** previous invocations for that database connection. ^If the callback -+** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, -+** then the autovacuum steps callback is canceled. The return value -+** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might -+** be some other error code if something goes wrong. The current -+** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other -+** return codes might be added in future releases. -+** -+**

    If no autovacuum pages callback is specified (the usual case) or -+** a NULL pointer is provided for the callback, -+** then the default behavior is to vacuum all free pages. So, in other -+** words, the default behavior is the same as if the callback function -+** were something like this: -+** -+**

    -+**     unsigned int demonstration_autovac_pages_callback(
    -+**       void *pClientData,
    -+**       const char *zSchema,
    -+**       unsigned int nDbPage,
    -+**       unsigned int nFreePage,
    -+**       unsigned int nBytePerPage
    -+**     ){
    -+**       return nFreePage;
    -+**     }
    -+** 
    -+*/ -+SQLITE_API int sqlite3_autovacuum_pages( -+ sqlite3 *db, -+ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), -+ void*, -+ void(*)(void*) -+); -+ -+ - /* - ** CAPI3REF: Data Change Notification Callbacks - ** METHOD: sqlite3 -@@ -7313,6 +7390,8 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - ** - ** ^The second argument is a pointer to the function to invoke when a - ** row is updated, inserted or deleted in a rowid table. -+** ^The update hook is disabled by invoking sqlite3_update_hook() -+** with a NULL pointer as the second parameter. - ** ^The first argument to the callback is a copy of the third argument - ** to sqlite3_update_hook(). - ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], -@@ -7334,6 +7413,12 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - ** The exceptions defined in this paragraph might change in a future - ** release of SQLite. - ** -+** Whether the update hook is invoked before or after the -+** corresponding change is currently unspecified and may differ -+** depending on the type of change. Do not rely on the order of the -+** hook call with regards to the final result of the operation which -+** triggers the hook. -+** - ** The update hook implementation must not do anything that will modify - ** the database connection that invoked the update hook. Any actions - ** to modify the database connection must be deferred until after the -@@ -7363,6 +7448,11 @@ SQLITE_API void *sqlite3_update_hook( - ** to the same database. Sharing is enabled if the argument is true - ** and disabled if the argument is false.)^ - ** -+** This interface is omitted if SQLite is compiled with -+** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE] -+** compile-time option is recommended because the -+** [use of shared cache mode is discouraged]. -+** - ** ^Cache sharing is enabled and disabled for an entire process. - ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). - ** In prior versions of SQLite, -@@ -7430,7 +7520,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); - ** CAPI3REF: Impose A Limit On Heap Size - ** - ** These interfaces impose limits on the amount of heap memory that will be --** by all database connections within a single process. -+** used by all database connections within a single process. - ** - ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the - ** soft limit on the amount of heap memory that may be allocated by SQLite. -@@ -7461,7 +7551,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); - ** ^The soft heap limit may not be greater than the hard heap limit. - ** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N) - ** is invoked with a value of N that is greater than the hard heap limit, --** the the soft heap limit is set to the value of the hard heap limit. -+** the soft heap limit is set to the value of the hard heap limit. - ** ^The soft heap limit is automatically enabled whenever the hard heap - ** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and - ** the soft heap limit is outside the range of 1..N, then the soft heap -@@ -7488,7 +7578,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); - ** )^ - ** - ** The circumstances under which SQLite will enforce the heap limits may --** changes in future releases of SQLite. -+** change in future releases of SQLite. - */ - SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); - SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N); -@@ -7603,8 +7693,8 @@ SQLITE_API int sqlite3_table_column_metadata( - ** ^The entry point is zProc. - ** ^(zProc may be 0, in which case SQLite will try to come up with an - ** entry point name on its own. It first tries "sqlite3_extension_init". --** If that does not work, it constructs a name "sqlite3_X_init" where the --** X is consists of the lower-case equivalent of all ASCII alphabetic -+** If that does not work, it constructs a name "sqlite3_X_init" where -+** X consists of the lower-case equivalent of all ASCII alphabetic - ** characters in the filename from the last "/" to the first following - ** "." and omitting any initial "lib".)^ - ** ^The sqlite3_load_extension() interface returns -@@ -7675,7 +7765,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); - ** ^(Even though the function prototype shows that xEntryPoint() takes - ** no arguments and returns void, SQLite invokes xEntryPoint() with three - ** arguments and expects an integer result as if the signature of the --** entry point where as follows: -+** entry point were as follows: - ** - **
    - **    int xEntryPoint(
    -@@ -7722,15 +7812,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
    - */
    - SQLITE_API void sqlite3_reset_auto_extension(void);
    - 
    --/*
    --** The interface to the virtual-table mechanism is currently considered
    --** to be experimental.  The interface might change in incompatible ways.
    --** If this is a problem for you, do not use the interface at this time.
    --**
    --** When the virtual-table mechanism stabilizes, we will declare the
    --** interface fixed, support it indefinitely, and remove this comment.
    --*/
    --
    - /*
    - ** Structures used by the virtual table interface
    - */
    -@@ -7791,6 +7872,10 @@ struct sqlite3_module {
    -   /* The methods above are in versions 1 and 2 of the sqlite_module object.
    -   ** Those below are for version 3 and greater. */
    -   int (*xShadowName)(const char*);
    -+  /* The methods above are in versions 1 through 3 of the sqlite_module object.
    -+  ** Those below are for version 4 and greater. */
    -+  int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
    -+                    const char *zTabName, int mFlags, char **pzErr);
    - };
    - 
    - /*
    -@@ -7844,15 +7929,15 @@ struct sqlite3_module {
    - ** virtual table and might not be checked again by the byte code.)^ ^(The
    - ** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
    - ** is left in its default setting of false, the constraint will always be
    --** checked separately in byte code.  If the omit flag is change to true, then
    -+** checked separately in byte code.  If the omit flag is changed to true, then
    - ** the constraint may or may not be checked in byte code.  In other words,
    - ** when the omit flag is true there is no guarantee that the constraint will
    - ** not be checked again using byte code.)^
    - **
    --** ^The idxNum and idxPtr values are recorded and passed into the
    -+** ^The idxNum and idxStr values are recorded and passed into the
    - ** [xFilter] method.
    --** ^[sqlite3_free()] is used to free idxPtr if and only if
    --** needToFreeIdxPtr is true.
    -+** ^[sqlite3_free()] is used to free idxStr if and only if
    -+** needToFreeIdxStr is true.
    - **
    - ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
    - ** the correct order to satisfy the ORDER BY clause so that no separate
    -@@ -7868,9 +7953,11 @@ struct sqlite3_module {
    - ** will be returned by the strategy.
    - **
    - ** The xBestIndex method may optionally populate the idxFlags field with a
    --** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
    --** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
    --** assumes that the strategy may visit at most one row.
    -+** mask of SQLITE_INDEX_SCAN_* flags. One such flag is
    -+** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN]
    -+** output to show the idxNum as hex instead of as decimal.  Another flag is
    -+** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will
    -+** return at most one row.
    - **
    - ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
    - ** SQLite also assumes that if a call to the xUpdate() method is made as
    -@@ -7934,31 +8021,65 @@ struct sqlite3_index_info {
    - ** [sqlite3_index_info].idxFlags field to some combination of
    - ** these bits.
    - */
    --#define SQLITE_INDEX_SCAN_UNIQUE      1     /* Scan visits at most 1 row */
    -+#define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */
    -+#define SQLITE_INDEX_SCAN_HEX    0x00000002 /* Display idxNum as hex */
    -+                                            /* in EXPLAIN QUERY PLAN */
    - 
    - /*
    - ** CAPI3REF: Virtual Table Constraint Operator Codes
    - **
    - ** These macros define the allowed values for the
    - ** [sqlite3_index_info].aConstraint[].op field.  Each value represents
    --** an operator that is part of a constraint term in the wHERE clause of
    -+** an operator that is part of a constraint term in the WHERE clause of
    - ** a query that uses a [virtual table].
    --*/
    --#define SQLITE_INDEX_CONSTRAINT_EQ         2
    --#define SQLITE_INDEX_CONSTRAINT_GT         4
    --#define SQLITE_INDEX_CONSTRAINT_LE         8
    --#define SQLITE_INDEX_CONSTRAINT_LT        16
    --#define SQLITE_INDEX_CONSTRAINT_GE        32
    --#define SQLITE_INDEX_CONSTRAINT_MATCH     64
    --#define SQLITE_INDEX_CONSTRAINT_LIKE      65
    --#define SQLITE_INDEX_CONSTRAINT_GLOB      66
    --#define SQLITE_INDEX_CONSTRAINT_REGEXP    67
    --#define SQLITE_INDEX_CONSTRAINT_NE        68
    --#define SQLITE_INDEX_CONSTRAINT_ISNOT     69
    --#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
    --#define SQLITE_INDEX_CONSTRAINT_ISNULL    71
    --#define SQLITE_INDEX_CONSTRAINT_IS        72
    --#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
    -+**
    -+** ^The left-hand operand of the operator is given by the corresponding
    -+** aConstraint[].iColumn field.  ^An iColumn of -1 indicates the left-hand
    -+** operand is the rowid.
    -+** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
    -+** operators have no left-hand operand, and so for those operators the
    -+** corresponding aConstraint[].iColumn is meaningless and should not be
    -+** used.
    -+**
    -+** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
    -+** value 255 are reserved to represent functions that are overloaded
    -+** by the [xFindFunction|xFindFunction method] of the virtual table
    -+** implementation.
    -+**
    -+** The right-hand operands for each constraint might be accessible using
    -+** the [sqlite3_vtab_rhs_value()] interface.  Usually the right-hand
    -+** operand is only available if it appears as a single constant literal
    -+** in the input SQL.  If the right-hand operand is another column or an
    -+** expression (even a constant expression) or a parameter, then the
    -+** sqlite3_vtab_rhs_value() probably will not be able to extract it.
    -+** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
    -+** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
    -+** and hence calls to sqlite3_vtab_rhs_value() for those operators will
    -+** always return SQLITE_NOTFOUND.
    -+**
    -+** The collating sequence to be used for comparison can be found using
    -+** the [sqlite3_vtab_collation()] interface.  For most real-world virtual
    -+** tables, the collating sequence of constraints does not matter (for example
    -+** because the constraints are numeric) and so the sqlite3_vtab_collation()
    -+** interface is not commonly needed.
    -+*/
    -+#define SQLITE_INDEX_CONSTRAINT_EQ          2
    -+#define SQLITE_INDEX_CONSTRAINT_GT          4
    -+#define SQLITE_INDEX_CONSTRAINT_LE          8
    -+#define SQLITE_INDEX_CONSTRAINT_LT         16
    -+#define SQLITE_INDEX_CONSTRAINT_GE         32
    -+#define SQLITE_INDEX_CONSTRAINT_MATCH      64
    -+#define SQLITE_INDEX_CONSTRAINT_LIKE       65
    -+#define SQLITE_INDEX_CONSTRAINT_GLOB       66
    -+#define SQLITE_INDEX_CONSTRAINT_REGEXP     67
    -+#define SQLITE_INDEX_CONSTRAINT_NE         68
    -+#define SQLITE_INDEX_CONSTRAINT_ISNOT      69
    -+#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL  70
    -+#define SQLITE_INDEX_CONSTRAINT_ISNULL     71
    -+#define SQLITE_INDEX_CONSTRAINT_IS         72
    -+#define SQLITE_INDEX_CONSTRAINT_LIMIT      73
    -+#define SQLITE_INDEX_CONSTRAINT_OFFSET     74
    -+#define SQLITE_INDEX_CONSTRAINT_FUNCTION  150
    - 
    - /*
    - ** CAPI3REF: Register A Virtual Table Implementation
    -@@ -7975,7 +8096,7 @@ struct sqlite3_index_info {
    - ** the implementation of the [virtual table module].   ^The fourth
    - ** parameter is an arbitrary client data pointer that is passed through
    - ** into the [xCreate] and [xConnect] methods of the virtual table module
    --** when a new virtual table is be being created or reinitialized.
    -+** when a new virtual table is being created or reinitialized.
    - **
    - ** ^The sqlite3_create_module_v2() interface has a fifth parameter which
    - ** is a pointer to a destructor for the pClientData.  ^SQLite will
    -@@ -7987,7 +8108,7 @@ struct sqlite3_index_info {
    - ** destructor.
    - **
    - ** ^If the third parameter (the pointer to the sqlite3_module object) is
    --** NULL then no new module is create and any existing modules with the
    -+** NULL then no new module is created and any existing modules with the
    - ** same name are dropped.
    - **
    - ** See also: [sqlite3_drop_modules()]
    -@@ -8099,16 +8220,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
    - */
    - SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
    - 
    --/*
    --** The interface to the virtual-table mechanism defined above (back up
    --** to a comment remarkably similar to this one) is currently considered
    --** to be experimental.  The interface might change in incompatible ways.
    --** If this is a problem for you, do not use the interface at this time.
    --**
    --** When the virtual-table mechanism stabilizes, we will declare the
    --** interface fixed, support it indefinitely, and remove this comment.
    --*/
    --
    - /*
    - ** CAPI3REF: A Handle To An Open BLOB
    - ** KEYWORDS: {BLOB handle} {BLOB handles}
    -@@ -8150,7 +8261,7 @@ typedef struct sqlite3_blob sqlite3_blob;
    - ** in *ppBlob. Otherwise an [error code] is returned and, unless the error
    - ** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
    - ** the API is not misused, it is always safe to call [sqlite3_blob_close()]
    --** on *ppBlob after this function it returns.
    -+** on *ppBlob after this function returns.
    - **
    - ** This function fails with SQLITE_ERROR if any of the following are true:
    - ** 
      -@@ -8256,7 +8367,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); - ** code is returned and the transaction rolled back. - ** - ** Calling this function with an argument that is not a NULL pointer or an --** open blob handle results in undefined behaviour. ^Calling this routine -+** open blob handle results in undefined behavior. ^Calling this routine - ** with a null pointer (such as would be returned by a failed call to - ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function - ** is passed a valid open blob handle, the values returned by the -@@ -8270,7 +8381,7 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); - ** - ** ^Returns the size in bytes of the BLOB accessible via the - ** successfully opened [BLOB handle] in its only argument. ^The --** incremental blob I/O routines can only read or overwriting existing -+** incremental blob I/O routines can only read or overwrite existing - ** blob content; they cannot change the size of a blob. - ** - ** This routine only works on a [BLOB handle] which has been created -@@ -8420,7 +8531,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); - ** ^The sqlite3_mutex_alloc() routine allocates a new - ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() - ** routine returns NULL if it is unable to allocate the requested --** mutex. The argument to sqlite3_mutex_alloc() must one of these -+** mutex. The argument to sqlite3_mutex_alloc() must be one of these - ** integer constants: - ** - **
        -@@ -8483,18 +8594,20 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); - ** - ** ^(Some systems (for example, Windows 95) do not support the operation - ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() --** will always return SQLITE_BUSY. The SQLite core only ever uses --** sqlite3_mutex_try() as an optimization so this is acceptable --** behavior.)^ -+** will always return SQLITE_BUSY. In most cases the SQLite core only uses -+** sqlite3_mutex_try() as an optimization, so this is acceptable -+** behavior. The exceptions are unix builds that set the -+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working -+** sqlite3_mutex_try() is required.)^ - ** - ** ^The sqlite3_mutex_leave() routine exits a mutex that was - ** previously entered by the same thread. The behavior - ** is undefined if the mutex is not currently entered by the - ** calling thread or is not currently allocated. - ** --** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or --** sqlite3_mutex_leave() is a NULL pointer, then all three routines --** behave as no-ops. -+** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), -+** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, -+** then any of the four routines behaves as a no-op. - ** - ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. - */ -@@ -8651,7 +8764,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); - ** CAPI3REF: Retrieve the mutex for a database connection - ** METHOD: sqlite3 - ** --** ^This interface returns a pointer the [sqlite3_mutex] object that -+** ^This interface returns a pointer to the [sqlite3_mutex] object that - ** serializes access to the [database connection] given in the argument - ** when the [threading mode] is Serialized. - ** ^If the [threading mode] is Single-thread or Multi-thread then this -@@ -8736,6 +8849,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); - #define SQLITE_TESTCTRL_PRNG_SAVE 5 - #define SQLITE_TESTCTRL_PRNG_RESTORE 6 - #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ -+#define SQLITE_TESTCTRL_FK_NO_ACTION 7 - #define SQLITE_TESTCTRL_BITVEC_TEST 8 - #define SQLITE_TESTCTRL_FAULT_INSTALL 9 - #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 -@@ -8743,8 +8857,10 @@ SQLITE_API int sqlite3_test_control(int op, ...); - #define SQLITE_TESTCTRL_ASSERT 12 - #define SQLITE_TESTCTRL_ALWAYS 13 - #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ -+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14 - #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 - #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ -+#define SQLITE_TESTCTRL_GETOPT 16 - #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ - #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 - #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 -@@ -8760,20 +8876,25 @@ SQLITE_API int sqlite3_test_control(int op, ...); - #define SQLITE_TESTCTRL_RESULT_INTREAL 27 - #define SQLITE_TESTCTRL_PRNG_SEED 28 - #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 --#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ -+#define SQLITE_TESTCTRL_SEEK_COUNT 30 -+#define SQLITE_TESTCTRL_TRACEFLAGS 31 -+#define SQLITE_TESTCTRL_TUNE 32 -+#define SQLITE_TESTCTRL_LOGEST 33 -+#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ -+#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ - - /* - ** CAPI3REF: SQL Keyword Checking - ** - ** These routines provide access to the set of SQL language keywords --** recognized by SQLite. Applications can uses these routines to determine -+** recognized by SQLite. Applications can use these routines to determine - ** whether or not a specific identifier needs to be escaped (for example, - ** by enclosing in double-quotes) so as not to confuse the parser. - ** - ** The sqlite3_keyword_count() interface returns the number of distinct - ** keywords understood by SQLite. - ** --** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and -+** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and - ** makes *Z point to that keyword expressed as UTF8 and writes the number - ** of bytes in the keyword into *L. The string that *Z points to is not - ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns -@@ -8934,7 +9055,7 @@ SQLITE_API void sqlite3_str_reset(sqlite3_str*); - ** content of the dynamic string under construction in X. The value - ** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X - ** and might be freed or altered by any subsequent method on the same --** [sqlite3_str] object. Applications must not used the pointer returned -+** [sqlite3_str] object. Applications must not use the pointer returned by - ** [sqlite3_str_value(X)] after any subsequent method call on the same - ** object. ^Applications may change the content of the string returned - ** by [sqlite3_str_value(X)] as long as they do not write into any bytes -@@ -9020,7 +9141,7 @@ SQLITE_API int sqlite3_status64( - ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] - ** buffer and where forced to overflow to [sqlite3_malloc()]. The - ** returned value includes allocations that overflowed because they --** where too large (they were larger than the "sz" parameter to -+** were too large (they were larger than the "sz" parameter to - ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because - ** no space was left in the page cache.)^ - ** -@@ -9104,28 +9225,29 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
        SQLITE_DBSTATUS_LOOKASIDE_HIT
        - **
        This parameter returns the number of malloc attempts that were - ** satisfied using lookaside memory. Only the high-water value is meaningful; --** the current value is always zero.)^ -+** the current value is always zero.
        )^ - ** - ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] - ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
        --**
        This parameter returns the number malloc attempts that might have -+**
        This parameter returns the number of malloc attempts that might have - ** been satisfied using lookaside memory but failed due to the amount of - ** memory requested being larger than the lookaside slot size. - ** Only the high-water value is meaningful; --** the current value is always zero.)^ -+** the current value is always zero.
        )^ - ** - ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] - ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
        --**
        This parameter returns the number malloc attempts that might have -+**
        This parameter returns the number of malloc attempts that might have - ** been satisfied using lookaside memory but failed due to all lookaside - ** memory already being in use. - ** Only the high-water value is meaningful; --** the current value is always zero.)^ -+** the current value is always zero.
        )^ - ** - ** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
        SQLITE_DBSTATUS_CACHE_USED
        - **
        This parameter returns the approximate number of bytes of heap - ** memory used by all pager caches associated with the database connection.)^ - ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. -+**
        - ** - ** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]] - ** ^(
        SQLITE_DBSTATUS_CACHE_USED_SHARED
        -@@ -9134,10 +9256,10 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** memory used by that pager cache is divided evenly between the attached - ** connections.)^ In other words, if none of the pager caches associated - ** with the database connection are shared, this request returns the same --** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are -+** value as DBSTATUS_CACHE_USED. Or, if one or more of the pager caches are - ** shared, the value returned by this call will be smaller than that returned - ** by DBSTATUS_CACHE_USED. ^The highwater mark associated with --** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. -+** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. - ** - ** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
        SQLITE_DBSTATUS_SCHEMA_USED
        - **
        This parameter returns the approximate number of bytes of heap -@@ -9147,6 +9269,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** schema memory is shared with other database connections due to - ** [shared cache mode] being enabled. - ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. -+**
        - ** - ** [[SQLITE_DBSTATUS_STMT_USED]] ^(
        SQLITE_DBSTATUS_STMT_USED
        - **
        This parameter returns the approximate number of bytes of heap -@@ -9183,7 +9306,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** been written to disk in the middle of a transaction due to the page - ** cache overflowing. Transactions are more efficient if they are written - ** to disk all at once. When pages spill mid-transaction, that introduces --** additional overhead. This parameter can be used help identify -+** additional overhead. This parameter can be used to help identify - ** inefficiencies that can be resolved by increasing the cache size. - **
        - ** -@@ -9254,13 +9377,13 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - ** [[SQLITE_STMTSTATUS_SORT]]
        SQLITE_STMTSTATUS_SORT
        - **
        ^This is the number of sort operations that have occurred. - ** A non-zero value in this counter may indicate an opportunity to --** improvement performance through careful use of indices.
        -+** improve performance through careful use of indices. - ** - ** [[SQLITE_STMTSTATUS_AUTOINDEX]]
        SQLITE_STMTSTATUS_AUTOINDEX
        - **
        ^This is the number of rows inserted into transient indices that - ** were created automatically in order to help joins run faster. - ** A non-zero value in this counter may indicate an opportunity to --** improvement performance by adding permanent indices that do not -+** improve performance by adding permanent indices that do not - ** need to be reinitialized each time the statement is run.
        - ** - ** [[SQLITE_STMTSTATUS_VM_STEP]]
        SQLITE_STMTSTATUS_VM_STEP
        -@@ -9269,19 +9392,29 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - ** to 2147483647. The number of virtual machine operations can be - ** used as a proxy for the total work done by the prepared statement. - ** If the number of virtual machine operations exceeds 2147483647 --** then the value returned by this statement status code is undefined. -+** then the value returned by this statement status code is undefined. - ** - ** [[SQLITE_STMTSTATUS_REPREPARE]]
        SQLITE_STMTSTATUS_REPREPARE
        - **
        ^This is the number of times that the prepare statement has been - ** automatically regenerated due to schema changes or changes to --** [bound parameters] that might affect the query plan. -+** [bound parameters] that might affect the query plan.
        - ** - ** [[SQLITE_STMTSTATUS_RUN]]
        SQLITE_STMTSTATUS_RUN
        - **
        ^This is the number of times that the prepared statement has - ** been run. A single "run" for the purposes of this counter is one - ** or more calls to [sqlite3_step()] followed by a call to [sqlite3_reset()]. - ** The counter is incremented on the first [sqlite3_step()] call of each --** cycle. -+** cycle.
        -+** -+** [[SQLITE_STMTSTATUS_FILTER_MISS]] -+** [[SQLITE_STMTSTATUS_FILTER HIT]] -+**
        SQLITE_STMTSTATUS_FILTER_HIT
        -+** SQLITE_STMTSTATUS_FILTER_MISS
        -+**
        ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join -+** step was bypassed because a Bloom filter returned not-found. The -+** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of -+** times that the Bloom filter returned a find, and thus the join step -+** had to be processed as normal.
        - ** - ** [[SQLITE_STMTSTATUS_MEMUSED]]
        SQLITE_STMTSTATUS_MEMUSED
        - **
        ^This is the approximate number of bytes of heap memory -@@ -9297,6 +9430,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - #define SQLITE_STMTSTATUS_VM_STEP 4 - #define SQLITE_STMTSTATUS_REPREPARE 5 - #define SQLITE_STMTSTATUS_RUN 6 -+#define SQLITE_STMTSTATUS_FILTER_MISS 7 -+#define SQLITE_STMTSTATUS_FILTER_HIT 8 - #define SQLITE_STMTSTATUS_MEMUSED 99 - - /* -@@ -9384,9 +9519,9 @@ struct sqlite3_pcache_page { - ** SQLite will typically create one cache instance for each open database file, - ** though this is not guaranteed. ^The - ** first parameter, szPage, is the size in bytes of the pages that must --** be allocated by the cache. ^szPage will always a power of two. ^The -+** be allocated by the cache. ^szPage will always be a power of two. ^The - ** second parameter szExtra is a number of bytes of extra storage --** associated with each page cache entry. ^The szExtra parameter will -+** associated with each page cache entry. ^The szExtra parameter will be - ** a number less than 250. SQLite will use the - ** extra szExtra bytes on each page to store metadata about the underlying - ** database page on disk. The value passed into szExtra depends -@@ -9394,17 +9529,17 @@ struct sqlite3_pcache_page { - ** ^The third argument to xCreate(), bPurgeable, is true if the cache being - ** created will be used to cache database pages of a file stored on disk, or - ** false if it is used for an in-memory database. The cache implementation --** does not have to do anything special based with the value of bPurgeable; -+** does not have to do anything special based upon the value of bPurgeable; - ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will - ** never invoke xUnpin() except to deliberately delete a page. - ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to - ** false will always have the "discard" flag set to true. --** ^Hence, a cache created with bPurgeable false will -+** ^Hence, a cache created with bPurgeable set to false will - ** never contain any unpinned pages. - ** - ** [[the xCachesize() page cache method]] - ** ^(The xCachesize() method may be called at any time by SQLite to set the --** suggested maximum cache-size (number of pages stored by) the cache -+** suggested maximum cache-size (number of pages stored) for the cache - ** instance passed as the first argument. This is the value configured using - ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable - ** parameter, the implementation is not required to do anything with this -@@ -9431,12 +9566,12 @@ struct sqlite3_pcache_page { - ** implementation must return a pointer to the page buffer with its content - ** intact. If the requested page is not already in the cache, then the - ** cache implementation should use the value of the createFlag --** parameter to help it determined what action to take: -+** parameter to help it determine what action to take: - ** - ** - **
        createFlag Behavior when page is not already in cache - **
        0 Do not allocate a new page. Return NULL. --**
        1 Allocate a new page if it easy and convenient to do so. -+**
        1 Allocate a new page if it is easy and convenient to do so. - ** Otherwise return NULL. - **
        2 Make every effort to allocate a new page. Only return - ** NULL if allocating a new page is effectively impossible. -@@ -9453,7 +9588,7 @@ struct sqlite3_pcache_page { - ** as its second argument. If the third parameter, discard, is non-zero, - ** then the page must be evicted from the cache. - ** ^If the discard parameter is --** zero, then the page may be discarded or retained at the discretion of -+** zero, then the page may be discarded or retained at the discretion of the - ** page cache implementation. ^The page cache implementation - ** may choose to evict unpinned pages at any time. - ** -@@ -9471,7 +9606,7 @@ struct sqlite3_pcache_page { - ** When SQLite calls the xTruncate() method, the cache must discard all - ** existing cache entries with page numbers (keys) greater than or equal - ** to the value of the iLimit parameter passed to xTruncate(). If any --** of these pages are pinned, they are implicitly unpinned, meaning that -+** of these pages are pinned, they become implicitly unpinned, meaning that - ** they can be safely discarded. - ** - ** [[the xDestroy() page cache method]] -@@ -9651,7 +9786,7 @@ typedef struct sqlite3_backup sqlite3_backup; - ** external process or via a database connection other than the one being - ** used by the backup operation, then the backup will be automatically - ** restarted by the next call to sqlite3_backup_step(). ^If the source --** database is modified by the using the same database connection as is used -+** database is modified by using the same database connection as is used - ** by the backup operation, then the backup database is automatically - ** updated at the same time. - ** -@@ -9668,7 +9803,7 @@ typedef struct sqlite3_backup sqlite3_backup; - ** and may not be used following a call to sqlite3_backup_finish(). - ** - ** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no --** sqlite3_backup_step() errors occurred, regardless or whether or not -+** sqlite3_backup_step() errors occurred, regardless of whether or not - ** sqlite3_backup_step() completed. - ** ^If an out-of-memory condition or IO error occurred during any prior - ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then -@@ -9708,7 +9843,7 @@ typedef struct sqlite3_backup sqlite3_backup; - ** if the application incorrectly accesses the destination [database connection] - ** and so no error code is reported, but the operations may malfunction - ** nevertheless. Use of the destination database connection while a --** backup is in progress might also also cause a mutex deadlock. -+** backup is in progress might also cause a mutex deadlock. - ** - ** If running in [shared cache mode], the application must - ** guarantee that the shared cache used by the destination database -@@ -9723,6 +9858,16 @@ typedef struct sqlite3_backup sqlite3_backup; - ** APIs are not strictly speaking threadsafe. If they are invoked at the - ** same time as another thread is invoking sqlite3_backup_step() it is - ** possible that they return invalid values. -+** -+** Alternatives To Using The Backup API -+** -+** Other techniques for safely creating a consistent backup of an SQLite -+** database include: -+** -+**
          -+**
        • The [VACUUM INTO] command. -+**
        • The [sqlite3_rsync] utility program. -+**
        - */ - SQLITE_API sqlite3_backup *sqlite3_backup_init( - sqlite3 *pDest, /* Destination database handle */ -@@ -9760,7 +9905,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); - ** application receives an SQLITE_LOCKED error, it may call the - ** sqlite3_unlock_notify() method with the blocked connection handle as - ** the first argument to register for a callback that will be invoked --** when the blocking connections current transaction is concluded. ^The -+** when the blocking connection's current transaction is concluded. ^The - ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] - ** call that concludes the blocking connection's transaction. - ** -@@ -9780,7 +9925,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); - ** blocked connection already has a registered unlock-notify callback, - ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is - ** called with a NULL pointer as its second argument, then any existing --** unlock-notify callback is canceled. ^The blocked connections -+** unlock-notify callback is canceled. ^The blocked connection's - ** unlock-notify callback may also be canceled by closing the blocked - ** connection using [sqlite3_close()]. - ** -@@ -9960,8 +10105,9 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); - ** - ** A single database handle may have at most a single write-ahead log callback - ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any --** previously registered write-ahead log callback. ^Note that the --** [sqlite3_wal_autocheckpoint()] interface and the -+** previously registered write-ahead log callback. ^The return value is -+** a copy of the third parameter from the previous call, if any, or 0. -+** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the - ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will - ** overwrite any prior [sqlite3_wal_hook()] settings. - */ -@@ -10135,7 +10281,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( - */ - #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ - #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ --#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ -+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */ - #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ - - /* -@@ -10177,7 +10323,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - ** support constraints. In this configuration (which is the default) if - ** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire - ** statement is rolled back as if [ON CONFLICT | OR ABORT] had been --** specified as part of the users SQL statement, regardless of the actual -+** specified as part of the user's SQL statement, regardless of the actual - ** ON CONFLICT mode specified. - ** - ** If X is non-zero, then the virtual table implementation guarantees -@@ -10203,7 +10349,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - ** [[SQLITE_VTAB_DIRECTONLY]]
        SQLITE_VTAB_DIRECTONLY
        - **
        Calls of the form - ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the --** the [xConnect] or [xCreate] methods of a [virtual table] implmentation -+** the [xConnect] or [xCreate] methods of a [virtual table] implementation - ** prohibits that virtual table from being used from within triggers and - ** views. - **
        -@@ -10211,18 +10357,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - ** [[SQLITE_VTAB_INNOCUOUS]]
        SQLITE_VTAB_INNOCUOUS
        - **
        Calls of the form - ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the --** the [xConnect] or [xCreate] methods of a [virtual table] implmentation -+** [xConnect] or [xCreate] methods of a [virtual table] implementation - ** identify that virtual table as being safe to use from within triggers - ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the - ** virtual table can do no serious harm even if it is controlled by a - ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS - ** flag unless absolutely necessary. - **
        -+** -+** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]
        SQLITE_VTAB_USES_ALL_SCHEMAS
        -+**
        Calls of the form -+** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the -+** the [xConnect] or [xCreate] methods of a [virtual table] implementation -+** instruct the query planner to begin at least a read transaction on -+** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the -+** virtual table is used. -+**
        - ** - */ - #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 - #define SQLITE_VTAB_INNOCUOUS 2 - #define SQLITE_VTAB_DIRECTONLY 3 -+#define SQLITE_VTAB_USES_ALL_SCHEMAS 4 - - /* - ** CAPI3REF: Determine The Virtual Table Conflict Policy -@@ -10240,10 +10396,11 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); - ** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE - ** - ** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn] --** method of a [virtual table], then it returns true if and only if the -+** method of a [virtual table], then it might return true if the - ** column is being fetched as part of an UPDATE operation during which the --** column value will not change. Applications might use this to substitute --** a return value that is less expensive to compute and that the corresponding -+** column value will not change. The virtual table implementation can use -+** this hint as permission to substitute a return value that is less -+** expensive to compute and that the corresponding - ** [xUpdate] method understands as a "no-change" value. - ** - ** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that -@@ -10252,31 +10409,314 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); - ** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces]. - ** In that case, [sqlite3_value_nochange(X)] will return true for the - ** same column in the [xUpdate] method. -+** -+** The sqlite3_vtab_nochange() routine is an optimization. Virtual table -+** implementations should continue to give a correct answer even if the -+** sqlite3_vtab_nochange() interface were to always return false. In the -+** current implementation, the sqlite3_vtab_nochange() interface does always -+** returns false for the enhanced [UPDATE FROM] statement. - */ - SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*); - - /* - ** CAPI3REF: Determine The Collation For a Virtual Table Constraint -+** METHOD: sqlite3_index_info - ** - ** This function may only be called from within a call to the [xBestIndex] --** method of a [virtual table]. -+** method of a [virtual table]. This function returns a pointer to a string -+** that is the name of the appropriate collation sequence to use for text -+** comparisons on the constraint identified by its arguments. - ** --** The first argument must be the sqlite3_index_info object that is the --** first parameter to the xBestIndex() method. The second argument must be --** an index into the aConstraint[] array belonging to the sqlite3_index_info --** structure passed to xBestIndex. This function returns a pointer to a buffer --** containing the name of the collation sequence for the corresponding --** constraint. -+** The first argument must be the pointer to the [sqlite3_index_info] object -+** that is the first parameter to the xBestIndex() method. The second argument -+** must be an index into the aConstraint[] array belonging to the -+** sqlite3_index_info structure passed to xBestIndex. -+** -+** Important: -+** The first parameter must be the same pointer that is passed into the -+** xBestMethod() method. The first parameter may not be a pointer to a -+** different [sqlite3_index_info] object, even an exact copy. -+** -+** The return value is computed as follows: -+** -+**
          -+**
        1. If the constraint comes from a WHERE clause expression that contains -+** a [COLLATE operator], then the name of the collation specified by -+** that COLLATE operator is returned. -+**

        2. If there is no COLLATE operator, but the column that is the subject -+** of the constraint specifies an alternative collating sequence via -+** a [COLLATE clause] on the column definition within the CREATE TABLE -+** statement that was passed into [sqlite3_declare_vtab()], then the -+** name of that alternative collating sequence is returned. -+**

        3. Otherwise, "BINARY" is returned. -+**

        - */ --SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); -+SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); -+ -+/* -+** CAPI3REF: Determine if a virtual table query is DISTINCT -+** METHOD: sqlite3_index_info -+** -+** This API may only be used from within an [xBestIndex|xBestIndex method] -+** of a [virtual table] implementation. The result of calling this -+** interface from outside of xBestIndex() is undefined and probably harmful. -+** -+** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and -+** 3. The integer returned by sqlite3_vtab_distinct() -+** gives the virtual table additional information about how the query -+** planner wants the output to be ordered. As long as the virtual table -+** can meet the ordering requirements of the query planner, it may set -+** the "orderByConsumed" flag. -+** -+**
        1. -+** ^If the sqlite3_vtab_distinct() interface returns 0, that means -+** that the query planner needs the virtual table to return all rows in the -+** sort order defined by the "nOrderBy" and "aOrderBy" fields of the -+** [sqlite3_index_info] object. This is the default expectation. If the -+** virtual table outputs all rows in sorted order, then it is always safe for -+** the xBestIndex method to set the "orderByConsumed" flag, regardless of -+** the return value from sqlite3_vtab_distinct(). -+**

        2. -+** ^(If the sqlite3_vtab_distinct() interface returns 1, that means -+** that the query planner does not need the rows to be returned in sorted order -+** as long as all rows with the same values in all columns identified by the -+** "aOrderBy" field are adjacent.)^ This mode is used when the query planner -+** is doing a GROUP BY. -+**

        3. -+** ^(If the sqlite3_vtab_distinct() interface returns 2, that means -+** that the query planner does not need the rows returned in any particular -+** order, as long as rows with the same values in all columns identified -+** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows -+** contain the same values for all columns identified by "colUsed", all but -+** one such row may optionally be omitted from the result.)^ -+** The virtual table is not required to omit rows that are duplicates -+** over the "colUsed" columns, but if the virtual table can do that without -+** too much extra effort, it could potentially help the query to run faster. -+** This mode is used for a DISTINCT query. -+**

        4. -+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the -+** virtual table must return rows in the order defined by "aOrderBy" as -+** if the sqlite3_vtab_distinct() interface had returned 0. However if -+** two or more rows in the result have the same values for all columns -+** identified by "colUsed", then all but one such row may optionally be -+** omitted.)^ Like when the return value is 2, the virtual table -+** is not required to omit rows that are duplicates over the "colUsed" -+** columns, but if the virtual table can do that without -+** too much extra effort, it could potentially help the query to run faster. -+** This mode is used for queries -+** that have both DISTINCT and ORDER BY clauses. -+**

        -+** -+**

        The following table summarizes the conditions under which the -+** virtual table is allowed to set the "orderByConsumed" flag based on -+** the value returned by sqlite3_vtab_distinct(). This table is a -+** restatement of the previous four paragraphs: -+** -+** -+** -+**
        sqlite3_vtab_distinct() return value -+** Rows are returned in aOrderBy order -+** Rows with the same value in all aOrderBy columns are adjacent -+** Duplicates over all colUsed columns may be omitted -+**
        0yesyesno -+**
        1noyesno -+**
        2noyesyes -+**
        3yesyesyes -+**
        -+** -+** ^For the purposes of comparing virtual table output values to see if the -+** values are the same value for sorting purposes, two NULL values are considered -+** to be the same. In other words, the comparison operator is "IS" -+** (or "IS NOT DISTINCT FROM") and not "==". -+** -+** If a virtual table implementation is unable to meet the requirements -+** specified above, then it must not set the "orderByConsumed" flag in the -+** [sqlite3_index_info] object or an incorrect answer may result. -+** -+** ^A virtual table implementation is always free to return rows in any order -+** it wants, as long as the "orderByConsumed" flag is not set. ^When the -+** "orderByConsumed" flag is unset, the query planner will add extra -+** [bytecode] to ensure that the final results returned by the SQL query are -+** ordered correctly. The use of the "orderByConsumed" flag and the -+** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful -+** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed" -+** flag might help queries against a virtual table to run faster. Being -+** overly aggressive and setting the "orderByConsumed" flag when it is not -+** valid to do so, on the other hand, might cause SQLite to return incorrect -+** results. -+*/ -+SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); -+ -+/* -+** CAPI3REF: Identify and handle IN constraints in xBestIndex -+** -+** This interface may only be used from within an -+** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. -+** The result of invoking this interface from any other context is -+** undefined and probably harmful. -+** -+** ^(A constraint on a virtual table of the form -+** "[IN operator|column IN (...)]" is -+** communicated to the xBestIndex method as a -+** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use -+** this constraint, it must set the corresponding -+** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under -+** the usual mode of handling IN operators, SQLite generates [bytecode] -+** that invokes the [xFilter|xFilter() method] once for each value -+** on the right-hand side of the IN operator.)^ Thus the virtual table -+** only sees a single value from the right-hand side of the IN operator -+** at a time. -+** -+** In some cases, however, it would be advantageous for the virtual -+** table to see all values on the right-hand of the IN operator all at -+** once. The sqlite3_vtab_in() interfaces facilitates this in two ways: -+** -+**

          -+**
        1. -+** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero) -+** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint -+** is an [IN operator] that can be processed all at once. ^In other words, -+** sqlite3_vtab_in() with -1 in the third argument is a mechanism -+** by which the virtual table can ask SQLite if all-at-once processing -+** of the IN operator is even possible. -+** -+**

        2. -+** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates -+** to SQLite that the virtual table does or does not want to process -+** the IN operator all-at-once, respectively. ^Thus when the third -+** parameter (F) is non-negative, this interface is the mechanism by -+** which the virtual table tells SQLite how it wants to process the -+** IN operator. -+**

        -+** -+** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times -+** within the same xBestIndex method call. ^For any given P,N pair, -+** the return value from sqlite3_vtab_in(P,N,F) will always be the same -+** within the same xBestIndex call. ^If the interface returns true -+** (non-zero), that means that the constraint is an IN operator -+** that can be processed all-at-once. ^If the constraint is not an IN -+** operator or cannot be processed all-at-once, then the interface returns -+** false. -+** -+** ^(All-at-once processing of the IN operator is selected if both of the -+** following conditions are met: -+** -+**
          -+**
        1. The P->aConstraintUsage[N].argvIndex value is set to a positive -+** integer. This is how the virtual table tells SQLite that it wants to -+** use the N-th constraint. -+** -+**

        2. The last call to sqlite3_vtab_in(P,N,F) for which F was -+** non-negative had F>=1. -+**

        )^ -+** -+** ^If either or both of the conditions above are false, then SQLite uses -+** the traditional one-at-a-time processing strategy for the IN constraint. -+** ^If both conditions are true, then the argvIndex-th parameter to the -+** xFilter method will be an [sqlite3_value] that appears to be NULL, -+** but which can be passed to [sqlite3_vtab_in_first()] and -+** [sqlite3_vtab_in_next()] to find all values on the right-hand side -+** of the IN constraint. -+*/ -+SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); -+ -+/* -+** CAPI3REF: Find all elements on the right-hand side of an IN constraint. -+** -+** These interfaces are only useful from within the -+** [xFilter|xFilter() method] of a [virtual table] implementation. -+** The result of invoking these interfaces from any other context -+** is undefined and probably harmful. -+** -+** The X parameter in a call to sqlite3_vtab_in_first(X,P) or -+** sqlite3_vtab_in_next(X,P) should be one of the parameters to the -+** xFilter method which invokes these routines, and specifically -+** a parameter that was previously selected for all-at-once IN constraint -+** processing using the [sqlite3_vtab_in()] interface in the -+** [xBestIndex|xBestIndex method]. ^(If the X parameter is not -+** an xFilter argument that was selected for all-at-once IN constraint -+** processing, then these routines return [SQLITE_ERROR].)^ -+** -+** ^(Use these routines to access all values on the right-hand side -+** of the IN constraint using code like the following: -+** -+**
        -+**    for(rc=sqlite3_vtab_in_first(pList, &pVal);
        -+**        rc==SQLITE_OK && pVal;
        -+**        rc=sqlite3_vtab_in_next(pList, &pVal)
        -+**    ){
        -+**      // do something with pVal
        -+**    }
        -+**    if( rc!=SQLITE_OK ){
        -+**      // an error has occurred
        -+**    }
        -+** 
        )^ -+** -+** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P) -+** routines return SQLITE_OK and set *P to point to the first or next value -+** on the RHS of the IN constraint. ^If there are no more values on the -+** right hand side of the IN constraint, then *P is set to NULL and these -+** routines return [SQLITE_DONE]. ^The return value might be -+** some other value, such as SQLITE_NOMEM, in the event of a malfunction. -+** -+** The *ppOut values returned by these routines are only valid until the -+** next call to either of these routines or until the end of the xFilter -+** method from which these routines were called. If the virtual table -+** implementation needs to retain the *ppOut values for longer, it must make -+** copies. The *ppOut values are [protected sqlite3_value|protected]. -+*/ -+SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); -+SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); -+ -+/* -+** CAPI3REF: Constraint values in xBestIndex() -+** METHOD: sqlite3_index_info -+** -+** This API may only be used from within the [xBestIndex|xBestIndex method] -+** of a [virtual table] implementation. The result of calling this interface -+** from outside of an xBestIndex method are undefined and probably harmful. -+** -+** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within -+** the [xBestIndex] method of a [virtual table] implementation, with P being -+** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and -+** J being a 0-based index into P->aConstraint[], then this routine -+** attempts to set *V to the value of the right-hand operand of -+** that constraint if the right-hand operand is known. ^If the -+** right-hand operand is not known, then *V is set to a NULL pointer. -+** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if -+** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) -+** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th -+** constraint is not available. ^The sqlite3_vtab_rhs_value() interface -+** can return a result code other than SQLITE_OK or SQLITE_NOTFOUND if -+** something goes wrong. -+** -+** The sqlite3_vtab_rhs_value() interface is usually only successful if -+** the right-hand operand of a constraint is a literal value in the original -+** SQL statement. If the right-hand operand is an expression or a reference -+** to some other column or a [host parameter], then sqlite3_vtab_rhs_value() -+** will probably return [SQLITE_NOTFOUND]. -+** -+** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and -+** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such -+** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ -+** -+** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value -+** and remains valid for the duration of the xBestIndex method call. -+** ^When xBestIndex returns, the sqlite3_value object returned by -+** sqlite3_vtab_rhs_value() is automatically deallocated. -+** -+** The "_rhs_" in the name of this routine is an abbreviation for -+** "Right-Hand Side". -+*/ -+SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal); - - /* - ** CAPI3REF: Conflict resolution modes - ** KEYWORDS: {conflict resolution mode} - ** - ** These constants are returned by [sqlite3_vtab_on_conflict()] to --** inform a [virtual table] implementation what the [ON CONFLICT] mode --** is for the SQL statement being evaluated. -+** inform a [virtual table] implementation of the [ON CONFLICT] mode -+** for the SQL statement being evaluated. - ** - ** Note that the [SQLITE_IGNORE] constant is also used as a potential - ** return value from the [sqlite3_set_authorizer()] callback and that -@@ -10300,6 +10740,10 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - ** managed by the prepared statement S and will be automatically freed when - ** S is finalized. - ** -+** Not all values are available for all query elements. When a value is -+** not available, the output variable is set to -1 if the value is numeric, -+** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). -+** - **
        - ** [[SQLITE_SCANSTAT_NLOOP]]
        SQLITE_SCANSTAT_NLOOP
        - **
        ^The [sqlite3_int64] variable pointed to by the V parameter will be -@@ -10312,27 +10756,39 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - ** [[SQLITE_SCANSTAT_EST]]
        SQLITE_SCANSTAT_EST
        - **
        ^The "double" variable pointed to by the V parameter will be set to the - ** query planner's estimate for the average number of rows output from each --** iteration of the X-th loop. If the query planner's estimates was accurate, -+** iteration of the X-th loop. If the query planner's estimate was accurate, - ** then this value will approximate the quotient NVISIT/NLOOP and the - ** product of this value for all prior loops with the same SELECTID will --** be the NLOOP value for the current loop. -+** be the NLOOP value for the current loop.
        - ** - ** [[SQLITE_SCANSTAT_NAME]]
        SQLITE_SCANSTAT_NAME
        - **
        ^The "const char *" variable pointed to by the V parameter will be set - ** to a zero-terminated UTF-8 string containing the name of the index or table --** used for the X-th loop. -+** used for the X-th loop.
        - ** - ** [[SQLITE_SCANSTAT_EXPLAIN]]
        SQLITE_SCANSTAT_EXPLAIN
        - **
        ^The "const char *" variable pointed to by the V parameter will be set - ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] --** description for the X-th loop. -+** description for the X-th loop.
        - ** --** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECT
        -+** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECTID
        - **
        ^The "int" variable pointed to by the V parameter will be set to the --** "select-id" for the X-th loop. The select-id identifies which query or --** subquery the loop is part of. The main query has a select-id of zero. --** The select-id is the same value as is output in the first column --** of an [EXPLAIN QUERY PLAN] query. -+** id for the X-th query plan element. The id value is unique within the -+** statement. The select-id is the same value as is output in the first -+** column of an [EXPLAIN QUERY PLAN] query.
        -+** -+** [[SQLITE_SCANSTAT_PARENTID]]
        SQLITE_SCANSTAT_PARENTID
        -+**
        The "int" variable pointed to by the V parameter will be set to the -+** id of the parent of the current query element, if applicable, or -+** to zero if the query element has no parent. This is the same value as -+** returned in the second column of an [EXPLAIN QUERY PLAN] query.
        -+** -+** [[SQLITE_SCANSTAT_NCYCLE]]
        SQLITE_SCANSTAT_NCYCLE
        -+**
        The sqlite3_int64 output value is set to the number of cycles, -+** according to the processor time-stamp counter, that elapsed while the -+** query element was being processed. This value is not available for -+** all query elements - if it is unavailable the output variable is -+** set to -1.
        - **
        - */ - #define SQLITE_SCANSTAT_NLOOP 0 -@@ -10341,12 +10797,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - #define SQLITE_SCANSTAT_NAME 3 - #define SQLITE_SCANSTAT_EXPLAIN 4 - #define SQLITE_SCANSTAT_SELECTID 5 -+#define SQLITE_SCANSTAT_PARENTID 6 -+#define SQLITE_SCANSTAT_NCYCLE 7 - - /* - ** CAPI3REF: Prepared Statement Scan Status - ** METHOD: sqlite3_stmt - ** --** This interface returns information about the predicted and measured -+** These interfaces return information about the predicted and measured - ** performance for pStmt. Advanced applications can use this - ** interface to compare the predicted and the measured performance and - ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. -@@ -10357,19 +10815,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - ** - ** The "iScanStatusOp" parameter determines which status information to return. - ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior --** of this interface is undefined. --** ^The requested measurement is written into a variable pointed to by --** the "pOut" parameter. --** Parameter "idx" identifies the specific loop to retrieve statistics for. --** Loops are numbered starting from zero. ^If idx is out of range - less than --** zero or greater than or equal to the total number of loops used to implement --** the statement - a non-zero value is returned and the variable that pOut --** points to is unchanged. --** --** ^Statistics might not be available for all loops in all statements. ^In cases --** where there exist loops with no available statistics, this function behaves --** as if the loop did not exist - it returns non-zero and leave the variable --** that pOut points to unchanged. -+** of this interface is undefined. ^The requested measurement is written into -+** a variable pointed to by the "pOut" parameter. -+** -+** The "flags" parameter must be passed a mask of flags. At present only -+** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX -+** is specified, then status information is available for all elements -+** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If -+** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements -+** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of -+** the EXPLAIN QUERY PLAN output) are available. Invoking API -+** sqlite3_stmt_scanstatus() is equivalent to calling -+** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. -+** -+** Parameter "idx" identifies the specific query element to retrieve statistics -+** for. Query elements are numbered starting from zero. A value of -1 may -+** retrieve statistics for the entire query. ^If idx is out of range -+** - less than -1 or greater than or equal to the total number of query -+** elements used to implement the statement - a non-zero value is returned and -+** the variable that pOut points to is unchanged. - ** - ** See also: [sqlite3_stmt_scanstatus_reset()] - */ -@@ -10379,6 +10843,19 @@ SQLITE_API int sqlite3_stmt_scanstatus( - int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ - void *pOut /* Result written here */ - ); -+SQLITE_API int sqlite3_stmt_scanstatus_v2( -+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ -+ int idx, /* Index of loop to report on */ -+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ -+ int flags, /* Mask of flags defined below */ -+ void *pOut /* Result written here */ -+); -+ -+/* -+** CAPI3REF: Prepared Statement Scan Status -+** KEYWORDS: {scan status flags} -+*/ -+#define SQLITE_SCANSTAT_COMPLEX 0x0001 - - /* - ** CAPI3REF: Zero Scan-Status Counters -@@ -10393,9 +10870,10 @@ SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); - - /* - ** CAPI3REF: Flush caches to disk mid-transaction -+** METHOD: sqlite3 - ** - ** ^If a write-transaction is open on [database connection] D when the --** [sqlite3_db_cacheflush(D)] interface invoked, any dirty -+** [sqlite3_db_cacheflush(D)] interface is invoked, any dirty - ** pages in the pager-cache that are not currently in use are written out - ** to disk. A dirty page may be in use if a database cursor created by an - ** active SQL statement is reading from it, or if it is page 1 of a database -@@ -10425,6 +10903,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); - - /* - ** CAPI3REF: The pre-update hook. -+** METHOD: sqlite3 - ** - ** ^These interfaces are only available if SQLite is compiled using the - ** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option. -@@ -10465,7 +10944,11 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); - ** seventh parameter is the final rowid value of the row being inserted - ** or updated. The value of the seventh parameter passed to the callback - ** function is not defined for operations on WITHOUT ROWID tables, or for --** INSERT operations on rowid tables. -+** DELETE operations on rowid tables. -+** -+** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from -+** the previous call on the same [database connection] D, or NULL for -+** the first call on D. - ** - ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], - ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces -@@ -10503,6 +10986,15 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); - ** triggers; or 2 for changes resulting from triggers called by top-level - ** triggers; and so forth. - ** -+** When the [sqlite3_blob_write()] API is used to update a blob column, -+** the pre-update hook is invoked with SQLITE_DELETE, because -+** the new values are not yet available. In this case, when a -+** callback made with op==SQLITE_DELETE is actually a write using the -+** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns -+** the index of the column being written. In other cases, where the -+** pre-update hook is being invoked for some other reason, including a -+** regular DELETE, sqlite3_preupdate_blobwrite() returns -1. -+** - ** See also: [sqlite3_update_hook()] - */ - #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) -@@ -10523,10 +11015,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); - SQLITE_API int sqlite3_preupdate_count(sqlite3 *); - SQLITE_API int sqlite3_preupdate_depth(sqlite3 *); - SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); -+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *); - #endif - - /* - ** CAPI3REF: Low-level system error code -+** METHOD: sqlite3 - ** - ** ^Attempt to return the underlying operating system error code or error - ** number that caused the most recent I/O error or failure to open a file. -@@ -10573,6 +11067,14 @@ typedef struct sqlite3_snapshot { - ** If there is not already a read-transaction open on schema S when - ** this function is called, one is opened automatically. - ** -+** If a read-transaction is opened by this function, then it is guaranteed -+** that the returned snapshot object may not be invalidated by a database -+** writer or checkpointer until after the read-transaction is closed. This -+** is not guaranteed if a read-transaction is already open when this -+** function is called. In that case, any subsequent write or checkpoint -+** operation on the database may invalidate the returned snapshot handle, -+** even while the read-transaction remains open. -+** - ** The following must be true for this function to succeed. If any of - ** the following statements are false when sqlite3_snapshot_get() is - ** called, SQLITE_ERROR is returned. The final value of *P is undefined -@@ -10730,15 +11232,16 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c - /* - ** CAPI3REF: Serialize a database - ** --** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory --** that is a serialization of the S database on [database connection] D. -+** The sqlite3_serialize(D,S,P,F) interface returns a pointer to -+** memory that is a serialization of the S database on -+** [database connection] D. If S is a NULL pointer, the main database is used. - ** If P is not a NULL pointer, then the size of the database in bytes - ** is written into *P. - ** - ** For an ordinary on-disk database file, the serialization is just a - ** copy of the disk file. For an in-memory database or a "TEMP" database, - ** the serialization is the same sequence of bytes which would be written --** to disk if that database where backed up to disk. -+** to disk if that database were backed up to disk. - ** - ** The usual case is that sqlite3_serialize() copies the serialization of - ** the database into memory obtained from [sqlite3_malloc64()] and returns -@@ -10747,7 +11250,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c - ** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations - ** are made, and the sqlite3_serialize() function will return a pointer - ** to the contiguous memory representation of the database that SQLite --** is currently using for that database, or NULL if the no such contiguous -+** is currently using for that database, or NULL if no such contiguous - ** memory representation of the database exists. A contiguous memory - ** representation of the database will usually only exist if there has - ** been a prior call to [sqlite3_deserialize(D,S,...)] with the same -@@ -10756,12 +11259,19 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c - ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy - ** of the database exists. - ** -+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, -+** the returned buffer content will remain accessible and unchanged -+** until either the next write operation on the connection or when -+** the connection is closed, and applications must not modify the -+** buffer. If the bit had been clear, the returned buffer will not -+** be accessed by SQLite after the call. -+** - ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the - ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory - ** allocation error occurs. - ** --** This interface is only available if SQLite is compiled with the --** [SQLITE_ENABLE_DESERIALIZE] option. -+** This interface is omitted if SQLite is compiled with the -+** [SQLITE_OMIT_DESERIALIZE] option. - */ - SQLITE_API unsigned char *sqlite3_serialize( - sqlite3 *db, /* The database connection */ -@@ -10804,22 +11314,36 @@ SQLITE_API unsigned char *sqlite3_serialize( - ** SQLite will try to increase the buffer size using sqlite3_realloc64() - ** if writes on the database cause it to grow larger than M bytes. - ** -+** Applications must not modify the buffer P or invalidate it before -+** the database connection D is closed. -+** - ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the - ** database is currently in a read transaction or is involved in a backup - ** operation. - ** -+** It is not possible to deserialize into the TEMP database. If the -+** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the -+** function returns SQLITE_ERROR. -+** -+** The deserialized database should not be in [WAL mode]. If the database -+** is in WAL mode, then any attempt to use the database file will result -+** in an [SQLITE_CANTOPEN] error. The application can set the -+** [file format version numbers] (bytes 18 and 19) of the input database P -+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the -+** database file into rollback mode and work around this limitation. -+** - ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the - ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then - ** [sqlite3_free()] is invoked on argument P prior to returning. - ** --** This interface is only available if SQLite is compiled with the --** [SQLITE_ENABLE_DESERIALIZE] option. -+** This interface is omitted if SQLite is compiled with the -+** [SQLITE_OMIT_DESERIALIZE] option. - */ - SQLITE_API int sqlite3_deserialize( - sqlite3 *db, /* The database connection */ - const char *zSchema, /* Which DB to reopen with the deserialization */ - unsigned char *pData, /* The serialized database content */ -- sqlite3_int64 szDb, /* Number bytes in the deserialization */ -+ sqlite3_int64 szDb, /* Number of bytes in the deserialization */ - sqlite3_int64 szBuf, /* Total size of buffer pData[] */ - unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ - ); -@@ -10827,7 +11351,7 @@ SQLITE_API int sqlite3_deserialize( - /* - ** CAPI3REF: Flags for sqlite3_deserialize() - ** --** The following are allowed values for 6th argument (the F argument) to -+** The following are allowed values for the 6th argument (the F argument) to - ** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. - ** - ** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization -@@ -10857,10 +11381,21 @@ SQLITE_API int sqlite3_deserialize( - # undef double - #endif - -+#if defined(__wasi__) -+# undef SQLITE_WASI -+# define SQLITE_WASI 1 -+# ifndef SQLITE_OMIT_LOAD_EXTENSION -+# define SQLITE_OMIT_LOAD_EXTENSION -+# endif -+# ifndef SQLITE_THREADSAFE -+# define SQLITE_THREADSAFE 0 -+# endif -+#endif -+ - #if 0 - } /* End of the 'extern "C"' block */ - #endif --#endif /* SQLITE3_H */ -+/* #endif for SQLITE3_H will be added by mksqlite3.tcl */ - - /******** Begin file sqlite3rtree.h *********/ - /* -@@ -11062,6 +11597,51 @@ SQLITE_API int sqlite3session_create( - */ - SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); - -+/* -+** CAPI3REF: Configure a Session Object -+** METHOD: sqlite3_session -+** -+** This method is used to configure a session object after it has been -+** created. At present the only valid values for the second parameter are -+** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. -+** -+*/ -+SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); -+ -+/* -+** CAPI3REF: Options for sqlite3session_object_config -+** -+** The following values may passed as the the 2nd parameter to -+** sqlite3session_object_config(). -+** -+**
        SQLITE_SESSION_OBJCONFIG_SIZE
        -+** This option is used to set, clear or query the flag that enables -+** the [sqlite3session_changeset_size()] API. Because it imposes some -+** computational overhead, this API is disabled by default. Argument -+** pArg must point to a value of type (int). If the value is initially -+** 0, then the sqlite3session_changeset_size() API is disabled. If it -+** is greater than 0, then the same API is enabled. Or, if the initial -+** value is less than zero, no change is made. In all cases the (int) -+** variable is set to 1 if the sqlite3session_changeset_size() API is -+** enabled following the current call, or 0 otherwise. -+** -+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after -+** the first table has been attached to the session object. -+** -+**
        SQLITE_SESSION_OBJCONFIG_ROWID
        -+** This option is used to set, clear or query the flag that enables -+** collection of data for tables with no explicit PRIMARY KEY. -+** -+** Normally, tables with no explicit PRIMARY KEY are simply ignored -+** by the sessions module. However, if this flag is set, it behaves -+** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted -+** as their leftmost columns. -+** -+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after -+** the first table has been attached to the session object. -+*/ -+#define SQLITE_SESSION_OBJCONFIG_SIZE 1 -+#define SQLITE_SESSION_OBJCONFIG_ROWID 2 - - /* - ** CAPI3REF: Enable Or Disable A Session Object -@@ -11296,9 +11876,10 @@ SQLITE_API void sqlite3session_table_filter( - ** is inserted while a session object is enabled, then later deleted while - ** the same session object is disabled, no INSERT record will appear in the - ** changeset, even though the delete took place while the session was disabled. --** Or, if one field of a row is updated while a session is disabled, and --** another field of the same row is updated while the session is enabled, the --** resulting changeset will contain an UPDATE change that updates both fields. -+** Or, if one field of a row is updated while a session is enabled, and -+** then another field of the same row is updated while the session is disabled, -+** the resulting changeset will contain an UPDATE change that updates both -+** fields. - */ - SQLITE_API int sqlite3session_changeset( - sqlite3_session *pSession, /* Session object */ -@@ -11306,6 +11887,22 @@ SQLITE_API int sqlite3session_changeset( - void **ppChangeset /* OUT: Buffer containing changeset */ - ); - -+/* -+** CAPI3REF: Return An Upper-limit For The Size Of The Changeset -+** METHOD: sqlite3_session -+** -+** By default, this function always returns 0. For it to return -+** a useful result, the sqlite3_session object must have been configured -+** to enable this API using sqlite3session_object_config() with the -+** SQLITE_SESSION_OBJCONFIG_SIZE verb. -+** -+** When enabled, this function returns an upper limit, in bytes, for the size -+** of the changeset that might be produced if sqlite3session_changeset() were -+** called. The final changeset size might be equal to or smaller than the -+** size in bytes returned by this function. -+*/ -+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession); -+ - /* - ** CAPI3REF: Load The Difference Between Tables Into A Session - ** METHOD: sqlite3_session -@@ -11354,8 +11951,9 @@ SQLITE_API int sqlite3session_changeset( - ** database zFrom the contents of the two compatible tables would be - ** identical. - ** --** It an error if database zFrom does not exist or does not contain the --** required compatible table. -+** Unless the call to this function is a no-op as described above, it is an -+** error if database zFrom does not exist or does not contain the required -+** compatible table. - ** - ** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite - ** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg -@@ -11423,6 +12021,14 @@ SQLITE_API int sqlite3session_patchset( - */ - SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession); - -+/* -+** CAPI3REF: Query for the amount of heap memory used by a session object. -+** -+** This API returns the total amount of heap memory in bytes currently -+** used by the session object passed as the only argument. -+*/ -+SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession); -+ - /* - ** CAPI3REF: Create An Iterator To Traverse A Changeset - ** CONSTRUCTOR: sqlite3_changeset_iter -@@ -11482,7 +12088,7 @@ SQLITE_API int sqlite3changeset_start_v2( - ** The following flags may passed via the 4th parameter to - ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: - ** --**
        SQLITE_CHANGESETAPPLY_INVERT
        -+**
        SQLITE_CHANGESETSTART_INVERT
        - ** Invert the changeset while iterating through it. This is equivalent to - ** inverting a changeset using sqlite3changeset_invert() before applying it. - ** It is an error to specify this flag with a patchset. -@@ -11525,18 +12131,23 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); - ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this - ** is not the case, this function returns [SQLITE_MISUSE]. - ** --** If argument pzTab is not NULL, then *pzTab is set to point to a --** nul-terminated utf-8 encoded string containing the name of the table --** affected by the current change. The buffer remains valid until either --** sqlite3changeset_next() is called on the iterator or until the --** conflict-handler function returns. If pnCol is not NULL, then *pnCol is --** set to the number of columns in the table affected by the change. If --** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change -+** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three -+** outputs are set through these pointers: -+** -+** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], -+** depending on the type of change that the iterator currently points to; -+** -+** *pnCol is set to the number of columns in the table affected by the change; and -+** -+** *pzTab is set to point to a nul-terminated utf-8 encoded string containing -+** the name of the table affected by the current change. The buffer remains -+** valid until either sqlite3changeset_next() is called on the iterator -+** or until the conflict-handler function returns. -+** -+** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change - ** is an indirect change, or false (0) otherwise. See the documentation for - ** [sqlite3session_indirect()] for a description of direct and indirect --** changes. Finally, if pOp is not NULL, then *pOp is set to one of --** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the --** type of change that the iterator currently points to. -+** changes. - ** - ** If no error occurs, SQLITE_OK is returned. If an error does occur, an - ** SQLite error code is returned. The values of the output variables may not -@@ -11792,7 +12403,6 @@ SQLITE_API int sqlite3changeset_concat( - void **ppOut /* OUT: Buffer containing output changeset */ - ); - -- - /* - ** CAPI3REF: Changegroup Handle - ** -@@ -11839,6 +12449,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; - */ - SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); - -+/* -+** CAPI3REF: Add a Schema to a Changegroup -+** METHOD: sqlite3_changegroup_schema -+** -+** This method may be used to optionally enforce the rule that the changesets -+** added to the changegroup handle must match the schema of database zDb -+** ("main", "temp", or the name of an attached database). If -+** sqlite3changegroup_add() is called to add a changeset that is not compatible -+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup -+** object is left in an undefined state. -+** -+** A changeset schema is considered compatible with the database schema in -+** the same way as for sqlite3changeset_apply(). Specifically, for each -+** table in the changeset, there exists a database table with: -+** -+**
          -+**
        • The name identified by the changeset, and -+**
        • at least as many columns as recorded in the changeset, and -+**
        • the primary key columns in the same position as recorded in -+** the changeset. -+**
        -+** -+** The output of the changegroup object always has the same schema as the -+** database nominated using this function. In cases where changesets passed -+** to sqlite3changegroup_add() have fewer columns than the corresponding table -+** in the database schema, these are filled in using the default column -+** values from the database schema. This makes it possible to combined -+** changesets that have different numbers of columns for a single table -+** within a changegroup, provided that they are otherwise compatible. -+*/ -+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); -+ - /* - ** CAPI3REF: Add A Changeset To A Changegroup - ** METHOD: sqlite3_changegroup -@@ -11907,16 +12549,45 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); - ** If the new changeset contains changes to a table that is already present - ** in the changegroup, then the number of columns and the position of the - ** primary key columns for the table must be consistent. If this is not the --** case, this function fails with SQLITE_SCHEMA. If the input changeset --** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is --** returned. Or, if an out-of-memory condition occurs during processing, this --** function returns SQLITE_NOMEM. In all cases, if an error occurs the state --** of the final contents of the changegroup is undefined. -+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup -+** object has been configured with a database schema using the -+** sqlite3changegroup_schema() API, then it is possible to combine changesets -+** with different numbers of columns for a single table, provided that -+** they are otherwise compatible. - ** --** If no error occurs, SQLITE_OK is returned. -+** If the input changeset appears to be corrupt and the corruption is -+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition -+** occurs during processing, this function returns SQLITE_NOMEM. -+** -+** In all cases, if an error occurs the state of the final contents of the -+** changegroup is undefined. If no error occurs, SQLITE_OK is returned. - */ - SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); - -+/* -+** CAPI3REF: Add A Single Change To A Changegroup -+** METHOD: sqlite3_changegroup -+** -+** This function adds the single change currently indicated by the iterator -+** passed as the second argument to the changegroup object. The rules for -+** adding the change are just as described for [sqlite3changegroup_add()]. -+** -+** If the change is successfully added to the changegroup, SQLITE_OK is -+** returned. Otherwise, an SQLite error code is returned. -+** -+** The iterator must point to a valid entry when this function is called. -+** If it does not, SQLITE_ERROR is returned and no change is added to the -+** changegroup. Additionally, the iterator must not have been opened with -+** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also -+** returned. -+*/ -+SQLITE_API int sqlite3changegroup_add_change( -+ sqlite3_changegroup*, -+ sqlite3_changeset_iter* -+); -+ -+ -+ - /* - ** CAPI3REF: Obtain A Composite Changeset From A Changegroup - ** METHOD: sqlite3_changegroup -@@ -12165,9 +12836,30 @@ SQLITE_API int sqlite3changeset_apply_v2( - ** Invert the changeset before applying it. This is equivalent to inverting - ** a changeset using sqlite3changeset_invert() before applying it. It is - ** an error to specify this flag with a patchset. -+** -+**
        SQLITE_CHANGESETAPPLY_IGNORENOOP
        -+** Do not invoke the conflict handler callback for any changes that -+** would not actually modify the database even if they were applied. -+** Specifically, this means that the conflict handler is not invoked -+** for: -+**
          -+**
        • a delete change if the row being deleted cannot be found, -+**
        • an update change if the modified fields are already set to -+** their new values in the conflicting row, or -+**
        • an insert change if all fields of the conflicting row match -+** the row being inserted. -+**
        -+** -+**
        SQLITE_CHANGESETAPPLY_FKNOACTION
        -+** If this flag it set, then all foreign key constraints in the target -+** database behave as if they were declared with "ON UPDATE NO ACTION ON -+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL -+** or SET DEFAULT. - */ - #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 - #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 -+#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 -+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 - - /* - ** CAPI3REF: Constants Passed To The Conflict Handler -@@ -12700,8 +13392,8 @@ struct Fts5PhraseIter { - ** EXTENSION API FUNCTIONS - ** - ** xUserData(pFts): --** Return a copy of the context pointer the extension function was --** registered with. -+** Return a copy of the pUserData pointer passed to the xCreateFunction() -+** API when the extension function was registered. - ** - ** xColumnTotalSize(pFts, iCol, pnToken): - ** If parameter iCol is less than zero, set output variable *pnToken -@@ -12733,8 +13425,11 @@ struct Fts5PhraseIter { - ** created with the "columnsize=0" option. - ** - ** xColumnText: --** This function attempts to retrieve the text of column iCol of the --** current document. If successful, (*pz) is set to point to a buffer -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of columns in the table, SQLITE_RANGE is returned. -+** -+** Otherwise, this function attempts to retrieve the text of column iCol of -+** the current document. If successful, (*pz) is set to point to a buffer - ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes - ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, - ** if an error occurs, an SQLite error code is returned and the final values -@@ -12744,8 +13439,10 @@ struct Fts5PhraseIter { - ** Returns the number of phrases in the current query expression. - ** - ** xPhraseSize: --** Returns the number of tokens in phrase iPhrase of the query. Phrases --** are numbered starting from zero. -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of phrases in the current query, as returned by xPhraseCount, -+** 0 is returned. Otherwise, this function returns the number of tokens in -+** phrase iPhrase of the query. Phrases are numbered starting from zero. - ** - ** xInstCount: - ** Set *pnInst to the total number of occurrences of all phrases within -@@ -12761,12 +13458,13 @@ struct Fts5PhraseIter { - ** Query for the details of phrase match iIdx within the current row. - ** Phrase matches are numbered starting from zero, so the iIdx argument - ** should be greater than or equal to zero and smaller than the value --** output by xInstCount(). -+** output by xInstCount(). If iIdx is less than zero or greater than -+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. - ** --** Usually, output parameter *piPhrase is set to the phrase number, *piCol -+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol - ** to the column in which it occurs and *piOff the token offset of the --** first token of the phrase. Returns SQLITE_OK if successful, or an error --** code (i.e. SQLITE_NOMEM) if an error occurs. -+** first token of the phrase. SQLITE_OK is returned if successful, or an -+** error code (i.e. SQLITE_NOMEM) if an error occurs. - ** - ** This API can be quite slow if used with an FTS5 table created with the - ** "detail=none" or "detail=column" option. -@@ -12792,6 +13490,10 @@ struct Fts5PhraseIter { - ** Invoking Api.xUserData() returns a copy of the pointer passed as - ** the third argument to pUserData. - ** -+** If parameter iPhrase is less than zero, or greater than or equal to -+** the number of phrases in the query, as returned by xPhraseCount(), -+** this function returns SQLITE_RANGE. -+** - ** If the callback function returns any value other than SQLITE_OK, the - ** query is abandoned and the xQueryPhrase function returns immediately. - ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. -@@ -12873,6 +13575,10 @@ struct Fts5PhraseIter { - ** (i.e. if it is a contentless table), then this API always iterates - ** through an empty set (all calls to xPhraseFirst() set iCol to -1). - ** -+** In all cases, matches are visited in (column ASC, offset ASC) order. -+** i.e. all those in column 0, sorted by offset, followed by those in -+** column 1, etc. -+** - ** xPhraseNext() - ** See xPhraseFirst above. - ** -@@ -12906,9 +13612,80 @@ struct Fts5PhraseIter { - ** - ** xPhraseNextColumn() - ** See xPhraseFirstColumn above. -+** -+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) -+** This is used to access token iToken of phrase iPhrase of the current -+** query. Before returning, output parameter *ppToken is set to point -+** to a buffer containing the requested token, and *pnToken to the -+** size of this buffer in bytes. -+** -+** If iPhrase or iToken are less than zero, or if iPhrase is greater than -+** or equal to the number of phrases in the query as reported by -+** xPhraseCount(), or if iToken is equal to or greater than the number of -+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken -+ are both zeroed. -+** -+** The output text is not a copy of the query text that specified the -+** token. It is the output of the tokenizer module. For tokendata=1 -+** tables, this includes any embedded 0x00 and trailing data. -+** -+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) -+** This is used to access token iToken of phrase hit iIdx within the -+** current row. If iIdx is less than zero or greater than or equal to the -+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, -+** output variable (*ppToken) is set to point to a buffer containing the -+** matching document token, and (*pnToken) to the size of that buffer in -+** bytes. -+** -+** The output text is not a copy of the document text that was tokenized. -+** It is the output of the tokenizer module. For tokendata=1 tables, this -+** includes any embedded 0x00 and trailing data. -+** -+** This API may be slow in some cases if the token identified by parameters -+** iIdx and iToken matched a prefix token in the query. In most cases, the -+** first call to this API for each prefix token in the query is forced -+** to scan the portion of the full-text index that matches the prefix -+** token to collect the extra data required by this API. If the prefix -+** token matches a large number of token instances in the document set, -+** this may be a performance problem. -+** -+** If the user knows in advance that a query may use this API for a -+** prefix token, FTS5 may be configured to collect all required data as part -+** of the initial querying of the full-text index, avoiding the second scan -+** entirely. This also causes prefix queries that do not use this API to -+** run more slowly and use more memory. FTS5 may be configured in this way -+** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] -+** option, or on a per-query basis using the -+** [fts5_insttoken | fts5_insttoken()] user function. -+** -+** This API can be quite slow if used with an FTS5 table created with the -+** "detail=none" or "detail=column" option. -+** -+** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of columns in the table, SQLITE_RANGE is returned. -+** -+** Otherwise, this function attempts to retrieve the locale associated -+** with column iCol of the current row. Usually, there is no associated -+** locale, and output parameters (*pzLocale) and (*pnLocale) are set -+** to NULL and 0, respectively. However, if the fts5_locale() function -+** was used to associate a locale with the value when it was inserted -+** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated -+** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) -+** is set to the size in bytes of the buffer, not including the -+** nul-terminator. -+** -+** If successful, SQLITE_OK is returned. Or, if an error occurs, an -+** SQLite error code is returned. The final value of the output parameters -+** is undefined in this case. -+** -+** xTokenize_v2: -+** Tokenize text using the tokenizer belonging to the FTS5 table. This -+** API is the same as the xTokenize() API, except that it allows a tokenizer -+** locale to be specified. - */ - struct Fts5ExtensionApi { -- int iVersion; /* Currently always set to 3 */ -+ int iVersion; /* Currently always set to 4 */ - - void *(*xUserData)(Fts5Context*); - -@@ -12943,6 +13720,22 @@ struct Fts5ExtensionApi { - - int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); - void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); -+ -+ /* Below this point are iVersion>=3 only */ -+ int (*xQueryToken)(Fts5Context*, -+ int iPhrase, int iToken, -+ const char **ppToken, int *pnToken -+ ); -+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); -+ -+ /* Below this point are iVersion>=4 only */ -+ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); -+ int (*xTokenize_v2)(Fts5Context*, -+ const char *pText, int nText, /* Text to tokenize */ -+ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ -+ void *pCtx, /* Context passed to xToken() */ -+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ -+ ); - }; - - /* -@@ -12963,7 +13756,7 @@ struct Fts5ExtensionApi { - ** A tokenizer instance is required to actually tokenize text. - ** - ** The first argument passed to this function is a copy of the (void*) --** pointer provided by the application when the fts5_tokenizer object -+** pointer provided by the application when the fts5_tokenizer_v2 object - ** was registered with FTS5 (the third argument to xCreateTokenizer()). - ** The second and third arguments are an array of nul-terminated strings - ** containing the tokenizer arguments, if any, specified following the -@@ -12987,7 +13780,7 @@ struct Fts5ExtensionApi { - ** argument passed to this function is a pointer to an Fts5Tokenizer object - ** returned by an earlier call to xCreate(). - ** --** The second argument indicates the reason that FTS5 is requesting -+** The third argument indicates the reason that FTS5 is requesting - ** tokenization of the supplied text. This is always one of the following - ** four values: - ** -@@ -13011,6 +13804,13 @@ struct Fts5ExtensionApi { - ** on a columnsize=0 database. - ** - ** -+** The sixth and seventh arguments passed to xTokenize() - pLocale and -+** nLocale - are a pointer to a buffer containing the locale to use for -+** tokenization (e.g. "en_US") and its size in bytes, respectively. The -+** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in -+** which case nLocale is always 0) to indicate that the tokenizer should -+** use its default locale. -+** - ** For each token in the input string, the supplied callback xToken() must - ** be invoked. The first argument to it should be a copy of the pointer - ** passed as the second argument to xTokenize(). The third and fourth -@@ -13034,6 +13834,30 @@ struct Fts5ExtensionApi { - ** may abandon the tokenization and return any error code other than - ** SQLITE_OK or SQLITE_DONE. - ** -+** If the tokenizer is registered using an fts5_tokenizer_v2 object, -+** then the xTokenize() method has two additional arguments - pLocale -+** and nLocale. These specify the locale that the tokenizer should use -+** for the current request. If pLocale and nLocale are both 0, then the -+** tokenizer should use its default locale. Otherwise, pLocale points to -+** an nLocale byte buffer containing the name of the locale to use as utf-8 -+** text. pLocale is not nul-terminated. -+** -+** FTS5_TOKENIZER -+** -+** There is also an fts5_tokenizer object. This is an older, deprecated, -+** version of fts5_tokenizer_v2. It is similar except that: -+** -+**
          -+**
        • There is no "iVersion" field, and -+**
        • The xTokenize() method does not take a locale argument. -+**
        -+** -+** Legacy fts5_tokenizer tokenizers must be registered using the -+** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). -+** -+** Tokenizer implementations registered using either API may be retrieved -+** using both xFindTokenizer() and xFindTokenizer_v2(). -+** - ** SYNONYM SUPPORT - ** - ** Custom tokenizers may also support synonyms. Consider a case in which a -@@ -13137,11 +13961,38 @@ struct Fts5ExtensionApi { - ** as separate queries of the FTS index are required for each synonym. - ** - ** When using methods (2) or (3), it is important that the tokenizer only --** provide synonyms when tokenizing document text (method (2)) or query --** text (method (3)), not both. Doing so will not cause any errors, but is -+** provide synonyms when tokenizing document text (method (3)) or query -+** text (method (2)), not both. Doing so will not cause any errors, but is - ** inefficient. - */ - typedef struct Fts5Tokenizer Fts5Tokenizer; -+typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; -+struct fts5_tokenizer_v2 { -+ int iVersion; /* Currently always 2 */ -+ -+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); -+ void (*xDelete)(Fts5Tokenizer*); -+ int (*xTokenize)(Fts5Tokenizer*, -+ void *pCtx, -+ int flags, /* Mask of FTS5_TOKENIZE_* flags */ -+ const char *pText, int nText, -+ const char *pLocale, int nLocale, -+ int (*xToken)( -+ void *pCtx, /* Copy of 2nd argument to xTokenize() */ -+ int tflags, /* Mask of FTS5_TOKEN_* flags */ -+ const char *pToken, /* Pointer to buffer containing token */ -+ int nToken, /* Size of token in bytes */ -+ int iStart, /* Byte offset of token within input text */ -+ int iEnd /* Byte offset of end of token within input text */ -+ ) -+ ); -+}; -+ -+/* -+** New code should use the fts5_tokenizer_v2 type to define tokenizer -+** implementations. The following type is included for legacy applications -+** that still use it. -+*/ - typedef struct fts5_tokenizer fts5_tokenizer; - struct fts5_tokenizer { - int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); -@@ -13161,6 +14012,7 @@ struct fts5_tokenizer { - ); - }; - -+ - /* Flags that may be passed as the third argument to xTokenize() */ - #define FTS5_TOKENIZE_QUERY 0x0001 - #define FTS5_TOKENIZE_PREFIX 0x0002 -@@ -13180,13 +14032,13 @@ struct fts5_tokenizer { - */ - typedef struct fts5_api fts5_api; - struct fts5_api { -- int iVersion; /* Currently always set to 2 */ -+ int iVersion; /* Currently always set to 3 */ - - /* Create a new tokenizer */ - int (*xCreateTokenizer)( - fts5_api *pApi, - const char *zName, -- void *pContext, -+ void *pUserData, - fts5_tokenizer *pTokenizer, - void (*xDestroy)(void*) - ); -@@ -13195,7 +14047,7 @@ struct fts5_api { - int (*xFindTokenizer)( - fts5_api *pApi, - const char *zName, -- void **ppContext, -+ void **ppUserData, - fts5_tokenizer *pTokenizer - ); - -@@ -13203,10 +14055,29 @@ struct fts5_api { - int (*xCreateFunction)( - fts5_api *pApi, - const char *zName, -- void *pContext, -+ void *pUserData, - fts5_extension_function xFunction, - void (*xDestroy)(void*) - ); -+ -+ /* APIs below this point are only available if iVersion>=3 */ -+ -+ /* Create a new tokenizer */ -+ int (*xCreateTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void *pUserData, -+ fts5_tokenizer_v2 *pTokenizer, -+ void (*xDestroy)(void*) -+ ); -+ -+ /* Find an existing tokenizer */ -+ int (*xFindTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void **ppUserData, -+ fts5_tokenizer_v2 **ppTokenizer -+ ); - }; - - /* -@@ -13220,16 +14091,22 @@ struct fts5_api { - #endif /* _FTS5_H */ - - /******** End of fts5.h *********/ -+#endif /* SQLITE3_H */ - - /************** End of sqlite3.h *********************************************/ - /************** Continuing where we left off in sqliteInt.h ******************/ - -+/* -+** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory. -+*/ -+#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1 -+ - /* - ** Include the configuration header output by 'configure' if we're using the - ** autoconf-based build - */ - #if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) --/* #include "config.h" */ -+#include "sqlite_cfg.h" - #define SQLITECONFIG_H 1 - #endif - -@@ -13260,6 +14137,7 @@ struct fts5_api { - #ifndef SQLITE_MAX_LENGTH - # define SQLITE_MAX_LENGTH 1000000000 - #endif -+#define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ - - /* - ** This is the maximum number of -@@ -13272,14 +14150,22 @@ struct fts5_api { - ** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. - ** * Terms in the VALUES clause of an INSERT statement - ** --** The hard upper limit here is 32676. Most database people will -+** The hard upper limit here is 32767. Most database people will - ** tell you that in a well-normalized database, you usually should - ** not have more than a dozen or so columns in any table. And if - ** that is the case, there is no point in having more than a few - ** dozen values in any of the other situations described above. -+** -+** An index can only have SQLITE_MAX_COLUMN columns from the user -+** point of view, but the underlying b-tree that implements the index -+** might have up to twice as many columns in a WITHOUT ROWID table, -+** since must also store the primary key at the end. Hence the -+** column count for Index is u16 instead of i16. - */ --#ifndef SQLITE_MAX_COLUMN -+#if !defined(SQLITE_MAX_COLUMN) - # define SQLITE_MAX_COLUMN 2000 -+#elif SQLITE_MAX_COLUMN>32767 -+# error SQLITE_MAX_COLUMN may not exceed 32767 - #endif - - /* -@@ -13297,11 +14183,7 @@ struct fts5_api { - ** The maximum depth of an expression tree. This is limited to - ** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might - ** want to place more severe limits on the complexity of an --** expression. --** --** A value of 0 used to mean that the limit was not enforced. --** But that is no longer true. The limit is now strictly enforced --** at all times. -+** expression. A value of 0 means that there is no limit. - */ - #ifndef SQLITE_MAX_EXPR_DEPTH - # define SQLITE_MAX_EXPR_DEPTH 1000 -@@ -13313,7 +14195,7 @@ struct fts5_api { - ** level of recursion for each term. A stack overflow can result - ** if the number of terms is too large. In practice, most SQL - ** never has more than 3 or 4 terms. Use a value of 0 to disable --** any limit on the number of terms in a compount SELECT. -+** any limit on the number of terms in a compound SELECT. - */ - #ifndef SQLITE_MAX_COMPOUND_SELECT - # define SQLITE_MAX_COMPOUND_SELECT 500 -@@ -13329,9 +14211,13 @@ struct fts5_api { - - /* - ** The maximum number of arguments to an SQL function. -+** -+** This value has a hard upper limit of 32767 due to storage -+** constraints (it needs to fit inside a i16). We keep it -+** lower than that to prevent abuse. - */ - #ifndef SQLITE_MAX_FUNCTION_ARG --# define SQLITE_MAX_FUNCTION_ARG 127 -+# define SQLITE_MAX_FUNCTION_ARG 1000 - #endif - - /* -@@ -13428,7 +14314,7 @@ struct fts5_api { - ** max_page_count macro. - */ - #ifndef SQLITE_MAX_PAGE_COUNT --# define SQLITE_MAX_PAGE_COUNT 1073741823 -+# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */ - #endif - - /* -@@ -13463,16 +14349,18 @@ struct fts5_api { - #endif - - /* --** WAL mode depends on atomic aligned 32-bit loads and stores in a few --** places. The following macros try to make this explicit. -+** A few places in the code require atomic load/store of aligned -+** integer values. - */ - #ifndef __has_extension - # define __has_extension(x) 0 /* compatibility with non-clang compilers */ - #endif - #if GCC_VERSION>=4007000 || __has_extension(c_atomic) -+# define SQLITE_ATOMIC_INTRINSICS 1 - # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) - # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) - #else -+# define SQLITE_ATOMIC_INTRINSICS 0 - # define AtomicLoad(PTR) (*(PTR)) - # define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) - #endif -@@ -13518,15 +14406,22 @@ struct fts5_api { - #endif - - /* --** A macro to hint to the compiler that a function should not be -+** Macros to hint to the compiler that a function should or should not be - ** inlined. - */ - #if defined(__GNUC__) - # define SQLITE_NOINLINE __attribute__((noinline)) -+# define SQLITE_INLINE __attribute__((always_inline)) inline - #elif defined(_MSC_VER) && _MSC_VER>=1310 - # define SQLITE_NOINLINE __declspec(noinline) -+# define SQLITE_INLINE __forceinline - #else - # define SQLITE_NOINLINE -+# define SQLITE_INLINE -+#endif -+#if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__) -+# undef SQLITE_INLINE -+# define SQLITE_INLINE - #endif - - /* -@@ -13548,6 +14443,29 @@ struct fts5_api { - # endif - #endif - -+/* -+** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit -+** SEH support if the -DSQLITE_OMIT_SEH option is given. -+*/ -+#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) -+# define SQLITE_USE_SEH 1 -+#else -+# undef SQLITE_USE_SEH -+#endif -+ -+/* -+** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly -+** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0 -+*/ -+#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1 -+ /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */ -+# undef SQLITE_DIRECT_OVERFLOW_READ -+#else -+ /* In all other cases, enable */ -+# define SQLITE_DIRECT_OVERFLOW_READ 1 -+#endif -+ -+ - /* - ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. - ** 0 means mutexes are permanently disable and the library is never -@@ -13677,11 +14595,12 @@ struct fts5_api { - ** is significant and used at least once. On switch statements - ** where multiple cases go to the same block of code, testcase() - ** can insure that all cases are evaluated. --** - */ --#ifdef SQLITE_COVERAGE_TEST --SQLITE_PRIVATE void sqlite3Coverage(int); --# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); } -+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) -+# ifndef SQLITE_AMALGAMATION -+ extern unsigned int sqlite3CoverageCounter; -+# endif -+# define testcase(X) if( X ){ sqlite3CoverageCounter += (unsigned)__LINE__; } - #else - # define testcase(X) - #endif -@@ -13711,6 +14630,14 @@ SQLITE_PRIVATE void sqlite3Coverage(int); - # define VVA_ONLY(X) - #endif - -+/* -+** Disable ALWAYS() and NEVER() (make them pass-throughs) for coverage -+** and mutation testing -+*/ -+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) -+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 -+#endif -+ - /* - ** The ALWAYS and NEVER macros surround boolean expressions which - ** are intended to always be true or false, respectively. Such -@@ -13726,7 +14653,7 @@ SQLITE_PRIVATE void sqlite3Coverage(int); - ** be true and false so that the unreachable code they specify will - ** not be counted as untested code. - */ --#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) -+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) - # define ALWAYS(X) (1) - # define NEVER(X) (0) - #elif !defined(NDEBUG) -@@ -13737,26 +14664,6 @@ SQLITE_PRIVATE void sqlite3Coverage(int); - # define NEVER(X) (X) - #endif - --/* --** The harmless(X) macro indicates that expression X is usually false --** but can be true without causing any problems, but we don't know of --** any way to cause X to be true. --** --** In debugging and testing builds, this macro will abort if X is ever --** true. In this way, developers are alerted to a possible test case --** that causes X to be true. If a harmless macro ever fails, that is --** an opportunity to change the macro into a testcase() and add a new --** test case to the test suite. --** --** For normal production builds, harmless(X) is a no-op, since it does --** not matter whether expression X is true or false. --*/ --#ifdef SQLITE_DEBUG --# define harmless(X) assert(!(X)); --#else --# define harmless(X) --#endif -- - /* - ** Some conditionals are optimizations only. In other words, if the - ** conditionals are replaced with a constant 1 (true) or 0 (false) then -@@ -13820,6 +14727,15 @@ SQLITE_PRIVATE void sqlite3Coverage(int); - # undef SQLITE_ENABLE_EXPLAIN_COMMENTS - #endif - -+/* -+** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE -+*/ -+#if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) -+# define SQLITE_OMIT_ALTERTABLE -+#endif -+ -+#define SQLITE_DIGIT_SEPARATOR '_' -+ - /* - ** Return true (non-zero) if the input is an integer that is too large - ** to fit in 32-bits. This macro is used inside of various testcase() -@@ -13901,6 +14817,7 @@ struct HashElem { - HashElem *next, *prev; /* Next and previous elements in the table */ - void *data; /* Data associated with this element */ - const char *pKey; /* Key associated with this element */ -+ unsigned int h; /* hash for pKey */ - }; - - /* -@@ -13932,7 +14849,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - /* - ** Number of entries in a hash table - */ --/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ -+#define sqliteHashCount(H) ((H)->count) - - #endif /* SQLITE_HASH_H */ - -@@ -13964,8 +14881,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - #define TK_LP 22 - #define TK_RP 23 - #define TK_AS 24 --#define TK_WITHOUT 25 --#define TK_COMMA 26 -+#define TK_COMMA 25 -+#define TK_WITHOUT 26 - #define TK_ABORT 27 - #define TK_ACTION 28 - #define TK_AFTER 29 -@@ -13985,141 +14902,147 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - #define TK_OR 43 - #define TK_AND 44 - #define TK_IS 45 --#define TK_MATCH 46 --#define TK_LIKE_KW 47 --#define TK_BETWEEN 48 --#define TK_IN 49 --#define TK_ISNULL 50 --#define TK_NOTNULL 51 --#define TK_NE 52 --#define TK_EQ 53 --#define TK_GT 54 --#define TK_LE 55 --#define TK_LT 56 --#define TK_GE 57 --#define TK_ESCAPE 58 --#define TK_ID 59 --#define TK_COLUMNKW 60 --#define TK_DO 61 --#define TK_FOR 62 --#define TK_IGNORE 63 --#define TK_INITIALLY 64 --#define TK_INSTEAD 65 --#define TK_NO 66 --#define TK_KEY 67 --#define TK_OF 68 --#define TK_OFFSET 69 --#define TK_PRAGMA 70 --#define TK_RAISE 71 --#define TK_RECURSIVE 72 --#define TK_REPLACE 73 --#define TK_RESTRICT 74 --#define TK_ROW 75 --#define TK_ROWS 76 --#define TK_TRIGGER 77 --#define TK_VACUUM 78 --#define TK_VIEW 79 --#define TK_VIRTUAL 80 --#define TK_WITH 81 --#define TK_NULLS 82 --#define TK_FIRST 83 --#define TK_LAST 84 --#define TK_CURRENT 85 --#define TK_FOLLOWING 86 --#define TK_PARTITION 87 --#define TK_PRECEDING 88 --#define TK_RANGE 89 --#define TK_UNBOUNDED 90 --#define TK_EXCLUDE 91 --#define TK_GROUPS 92 --#define TK_OTHERS 93 --#define TK_TIES 94 --#define TK_GENERATED 95 --#define TK_ALWAYS 96 --#define TK_REINDEX 97 --#define TK_RENAME 98 --#define TK_CTIME_KW 99 --#define TK_ANY 100 --#define TK_BITAND 101 --#define TK_BITOR 102 --#define TK_LSHIFT 103 --#define TK_RSHIFT 104 --#define TK_PLUS 105 --#define TK_MINUS 106 --#define TK_STAR 107 --#define TK_SLASH 108 --#define TK_REM 109 --#define TK_CONCAT 110 --#define TK_COLLATE 111 --#define TK_BITNOT 112 --#define TK_ON 113 --#define TK_INDEXED 114 --#define TK_STRING 115 --#define TK_JOIN_KW 116 --#define TK_CONSTRAINT 117 --#define TK_DEFAULT 118 --#define TK_NULL 119 --#define TK_PRIMARY 120 --#define TK_UNIQUE 121 --#define TK_CHECK 122 --#define TK_REFERENCES 123 --#define TK_AUTOINCR 124 --#define TK_INSERT 125 --#define TK_DELETE 126 --#define TK_UPDATE 127 --#define TK_SET 128 --#define TK_DEFERRABLE 129 --#define TK_FOREIGN 130 --#define TK_DROP 131 --#define TK_UNION 132 --#define TK_ALL 133 --#define TK_EXCEPT 134 --#define TK_INTERSECT 135 --#define TK_SELECT 136 --#define TK_VALUES 137 --#define TK_DISTINCT 138 --#define TK_DOT 139 --#define TK_FROM 140 --#define TK_JOIN 141 --#define TK_USING 142 --#define TK_ORDER 143 --#define TK_GROUP 144 --#define TK_HAVING 145 --#define TK_LIMIT 146 --#define TK_WHERE 147 --#define TK_INTO 148 --#define TK_NOTHING 149 --#define TK_FLOAT 150 --#define TK_BLOB 151 --#define TK_INTEGER 152 --#define TK_VARIABLE 153 --#define TK_CASE 154 --#define TK_WHEN 155 --#define TK_THEN 156 --#define TK_ELSE 157 --#define TK_INDEX 158 --#define TK_ALTER 159 --#define TK_ADD 160 --#define TK_WINDOW 161 --#define TK_OVER 162 --#define TK_FILTER 163 --#define TK_COLUMN 164 --#define TK_AGG_FUNCTION 165 --#define TK_AGG_COLUMN 166 --#define TK_TRUEFALSE 167 --#define TK_ISNOT 168 --#define TK_FUNCTION 169 --#define TK_UMINUS 170 --#define TK_UPLUS 171 --#define TK_TRUTH 172 --#define TK_REGISTER 173 --#define TK_VECTOR 174 --#define TK_SELECT_COLUMN 175 --#define TK_IF_NULL_ROW 176 --#define TK_ASTERISK 177 --#define TK_SPAN 178 --#define TK_SPACE 179 --#define TK_ILLEGAL 180 -+#define TK_ISNOT 46 -+#define TK_MATCH 47 -+#define TK_LIKE_KW 48 -+#define TK_BETWEEN 49 -+#define TK_IN 50 -+#define TK_ISNULL 51 -+#define TK_NOTNULL 52 -+#define TK_NE 53 -+#define TK_EQ 54 -+#define TK_GT 55 -+#define TK_LE 56 -+#define TK_LT 57 -+#define TK_GE 58 -+#define TK_ESCAPE 59 -+#define TK_ID 60 -+#define TK_COLUMNKW 61 -+#define TK_DO 62 -+#define TK_FOR 63 -+#define TK_IGNORE 64 -+#define TK_INITIALLY 65 -+#define TK_INSTEAD 66 -+#define TK_NO 67 -+#define TK_KEY 68 -+#define TK_OF 69 -+#define TK_OFFSET 70 -+#define TK_PRAGMA 71 -+#define TK_RAISE 72 -+#define TK_RECURSIVE 73 -+#define TK_REPLACE 74 -+#define TK_RESTRICT 75 -+#define TK_ROW 76 -+#define TK_ROWS 77 -+#define TK_TRIGGER 78 -+#define TK_VACUUM 79 -+#define TK_VIEW 80 -+#define TK_VIRTUAL 81 -+#define TK_WITH 82 -+#define TK_NULLS 83 -+#define TK_FIRST 84 -+#define TK_LAST 85 -+#define TK_CURRENT 86 -+#define TK_FOLLOWING 87 -+#define TK_PARTITION 88 -+#define TK_PRECEDING 89 -+#define TK_RANGE 90 -+#define TK_UNBOUNDED 91 -+#define TK_EXCLUDE 92 -+#define TK_GROUPS 93 -+#define TK_OTHERS 94 -+#define TK_TIES 95 -+#define TK_GENERATED 96 -+#define TK_ALWAYS 97 -+#define TK_MATERIALIZED 98 -+#define TK_REINDEX 99 -+#define TK_RENAME 100 -+#define TK_CTIME_KW 101 -+#define TK_ANY 102 -+#define TK_BITAND 103 -+#define TK_BITOR 104 -+#define TK_LSHIFT 105 -+#define TK_RSHIFT 106 -+#define TK_PLUS 107 -+#define TK_MINUS 108 -+#define TK_STAR 109 -+#define TK_SLASH 110 -+#define TK_REM 111 -+#define TK_CONCAT 112 -+#define TK_PTR 113 -+#define TK_COLLATE 114 -+#define TK_BITNOT 115 -+#define TK_ON 116 -+#define TK_INDEXED 117 -+#define TK_STRING 118 -+#define TK_JOIN_KW 119 -+#define TK_CONSTRAINT 120 -+#define TK_DEFAULT 121 -+#define TK_NULL 122 -+#define TK_PRIMARY 123 -+#define TK_UNIQUE 124 -+#define TK_CHECK 125 -+#define TK_REFERENCES 126 -+#define TK_AUTOINCR 127 -+#define TK_INSERT 128 -+#define TK_DELETE 129 -+#define TK_UPDATE 130 -+#define TK_SET 131 -+#define TK_DEFERRABLE 132 -+#define TK_FOREIGN 133 -+#define TK_DROP 134 -+#define TK_UNION 135 -+#define TK_ALL 136 -+#define TK_EXCEPT 137 -+#define TK_INTERSECT 138 -+#define TK_SELECT 139 -+#define TK_VALUES 140 -+#define TK_DISTINCT 141 -+#define TK_DOT 142 -+#define TK_FROM 143 -+#define TK_JOIN 144 -+#define TK_USING 145 -+#define TK_ORDER 146 -+#define TK_GROUP 147 -+#define TK_HAVING 148 -+#define TK_LIMIT 149 -+#define TK_WHERE 150 -+#define TK_RETURNING 151 -+#define TK_INTO 152 -+#define TK_NOTHING 153 -+#define TK_FLOAT 154 -+#define TK_BLOB 155 -+#define TK_INTEGER 156 -+#define TK_VARIABLE 157 -+#define TK_CASE 158 -+#define TK_WHEN 159 -+#define TK_THEN 160 -+#define TK_ELSE 161 -+#define TK_INDEX 162 -+#define TK_ALTER 163 -+#define TK_ADD 164 -+#define TK_WINDOW 165 -+#define TK_OVER 166 -+#define TK_FILTER 167 -+#define TK_COLUMN 168 -+#define TK_AGG_FUNCTION 169 -+#define TK_AGG_COLUMN 170 -+#define TK_TRUEFALSE 171 -+#define TK_FUNCTION 172 -+#define TK_UPLUS 173 -+#define TK_UMINUS 174 -+#define TK_TRUTH 175 -+#define TK_REGISTER 176 -+#define TK_VECTOR 177 -+#define TK_SELECT_COLUMN 178 -+#define TK_IF_NULL_ROW 179 -+#define TK_ASTERISK 180 -+#define TK_SPAN 181 -+#define TK_ERROR 182 -+#define TK_QNUMBER 183 -+#define TK_SPACE 184 -+#define TK_COMMENT 185 -+#define TK_ILLEGAL 186 - - /************** End of parse.h ***********************************************/ - /************** Continuing where we left off in sqliteInt.h ******************/ -@@ -14128,6 +15051,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - #include - #include - #include -+#include - - /* - ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. -@@ -14148,7 +15072,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - #ifdef SQLITE_OMIT_FLOATING_POINT - # define double sqlite_int64 - # define float sqlite_int64 --# define LONGDOUBLE_TYPE sqlite_int64 -+# define fabs(X) ((X)<0?-(X):(X)) -+# define sqlite3IsOverflow(X) 0 - # ifndef SQLITE_BIG_DBL - # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) - # endif -@@ -14225,7 +15150,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - ** number of pages. A negative number N translations means that a buffer - ** of -1024*N bytes is allocated and used for as many pages as it will hold. - ** --** The default value of "20" was choosen to minimize the run-time of the -+** The default value of "20" was chosen to minimize the run-time of the - ** speedtest1 test program with options: --shrink-memory --reprepare - */ - #ifndef SQLITE_DEFAULT_PCACHE_INITSZ -@@ -14253,7 +15178,17 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - ** ourselves. - */ - #ifndef offsetof --#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) -+#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) -+#endif -+ -+/* -+** Work around C99 "flex-array" syntax for pre-C99 compilers, so as -+** to avoid complaints from -fsanitize=strict-bounds. -+*/ -+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -+# define FLEXARRAY -+#else -+# define FLEXARRAY 1 - #endif - - /* -@@ -14323,9 +15258,6 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); - # define INT8_TYPE signed char - # endif - #endif --#ifndef LONGDOUBLE_TYPE --# define LONGDOUBLE_TYPE long double --#endif - typedef sqlite_int64 i64; /* 8-byte signed integer */ - typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ - typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ -@@ -14334,6 +15266,11 @@ typedef INT16_TYPE i16; /* 2-byte signed integer */ - typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ - typedef INT8_TYPE i8; /* 1-byte signed integer */ - -+/* A bitfield type for use inside of structures. Always follow with :N where -+** N is the number of bits. -+*/ -+typedef unsigned bft; /* Bit Field Type */ -+ - /* - ** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value - ** that can be stored in a u32 without loss of data. The value -@@ -14344,15 +15281,9 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ - - /* - ** The datatype used to store estimates of the number of rows in a --** table or index. This is an unsigned integer type. For 99.9% of --** the world, a 32-bit integer is sufficient. But a 64-bit integer --** can be used at compile-time if desired. -+** table or index. - */ --#ifdef SQLITE_64BIT_STATS -- typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ --#else -- typedef u32 tRowcnt; /* 32-bit is the default */ --#endif -+typedef u64 tRowcnt; - - /* - ** Estimated quantities used for query planning are stored as 16-bit -@@ -14378,6 +15309,8 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ - ** 0.5 -> -10 0.1 -> -33 0.0625 -> -40 - */ - typedef INT16_TYPE LogEst; -+#define LOGEST_MIN (-32768) -+#define LOGEST_MAX (32767) - - /* - ** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer -@@ -14387,6 +15320,7 @@ typedef INT16_TYPE LogEst; - # define SQLITE_PTRSIZE __SIZEOF_POINTER__ - # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ - defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ -+ (defined(__APPLE__) && defined(__ppc__)) || \ - (defined(__TOS_AIX__) && !defined(__64BIT__)) - # define SQLITE_PTRSIZE 4 - # else -@@ -14412,8 +15346,31 @@ typedef INT16_TYPE LogEst; - ** the end of buffer S. This macro returns true if P points to something - ** contained within the buffer S. - */ --#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) -+#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) - -+/* -+** P is one byte past the end of a large buffer. Return true if a span of bytes -+** between S..E crosses the end of that buffer. In other words, return true -+** if the sub-buffer S..E-1 overflows the buffer whose last byte is P-1. -+** -+** S is the start of the span. E is one byte past the end of end of span. -+** -+** P -+** |-----------------| FALSE -+** |-------| -+** S E -+** -+** P -+** |-----------------| -+** |-------| TRUE -+** S E -+** -+** P -+** |-----------------| -+** |-------| FALSE -+** S E -+*/ -+#define SQLITE_OVERFLOW(P,S,E) (((uptr)(S)<(uptr)(P))&&((uptr)(E)>(uptr)(P))) - - /* - ** Macros to determine whether the machine is big or little endian, -@@ -14423,16 +15380,33 @@ typedef INT16_TYPE LogEst; - ** using C-preprocessor macros. If that is unsuccessful, or if - ** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined - ** at run-time. -+** -+** If you are building SQLite on some obscure platform for which the -+** following ifdef magic does not work, you can always include either: -+** -+** -DSQLITE_BYTEORDER=1234 -+** -+** or -+** -+** -DSQLITE_BYTEORDER=4321 -+** -+** to cause the build to work for little-endian or big-endian processors, -+** respectively. - */ --#ifndef SQLITE_BYTEORDER --# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ -+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */ -+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ -+# define SQLITE_BYTEORDER 4321 -+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ -+# define SQLITE_BYTEORDER 1234 -+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 -+# define SQLITE_BYTEORDER 4321 -+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ - defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ - defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ - defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) --# define SQLITE_BYTEORDER 1234 --# elif defined(sparc) || defined(__ppc__) || \ -- defined(__ARMEB__) || defined(__AARCH64EB__) --# define SQLITE_BYTEORDER 4321 -+# define SQLITE_BYTEORDER 1234 -+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) -+# define SQLITE_BYTEORDER 4321 - # else - # define SQLITE_BYTEORDER 0 - # endif -@@ -14465,11 +15439,30 @@ typedef INT16_TYPE LogEst; - #define LARGEST_UINT64 (0xffffffff|(((u64)0xffffffff)<<32)) - #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) - -+/* -+** Macro SMXV(n) return the maximum value that can be held in variable n, -+** assuming n is a signed integer type. UMXV(n) is similar for unsigned -+** integer types. -+*/ -+#define SMXV(n) ((((i64)1)<<(sizeof(n)*8-1))-1) -+#define UMXV(n) ((((i64)1)<<(sizeof(n)*8))-1) -+ - /* - ** Round up a number to the next larger multiple of 8. This is used - ** to force 8-byte alignment on 64-bit architectures. -+** -+** ROUND8() always does the rounding, for any argument. -+** -+** ROUND8P() assumes that the argument is already an integer number of -+** pointers in size, and so it is a no-op on systems where the pointer -+** size is 8. - */ - #define ROUND8(x) (((x)+7)&~7) -+#if SQLITE_PTRSIZE==8 -+# define ROUND8P(x) (x) -+#else -+# define ROUND8P(x) (((x)+7)&~7) -+#endif - - /* - ** Round down to the nearest multiple of 8 -@@ -14486,9 +15479,9 @@ typedef INT16_TYPE LogEst; - ** pointers. In that case, only verify 4-byte alignment. - */ - #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC --# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) -+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) - #else --# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) -+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) - #endif - - /* -@@ -14532,25 +15525,94 @@ typedef INT16_TYPE LogEst; - #endif - - /* --** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not --** the Select query generator tracing logic is turned on. -+** TREETRACE_ENABLED will be either 1 or 0 depending on whether or not -+** the Abstract Syntax Tree tracing logic is turned on. - */ --#if defined(SQLITE_ENABLE_SELECTTRACE) --# define SELECTTRACE_ENABLED 1 --#else --# define SELECTTRACE_ENABLED 0 -+#if !defined(SQLITE_AMALGAMATION) -+SQLITE_PRIVATE u32 sqlite3TreeTrace; - #endif --#if defined(SQLITE_ENABLE_SELECTTRACE) --# define SELECTTRACE_ENABLED 1 --# define SELECTTRACE(K,P,S,X) \ -- if(sqlite3_unsupported_selecttrace&(K)) \ -+#if defined(SQLITE_DEBUG) \ -+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \ -+ || defined(SQLITE_ENABLE_TREETRACE)) -+# define TREETRACE_ENABLED 1 -+# define TREETRACE(K,P,S,X) \ -+ if(sqlite3TreeTrace&(K)) \ - sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ - sqlite3DebugPrintf X - #else --# define SELECTTRACE(K,P,S,X) --# define SELECTTRACE_ENABLED 0 -+# define TREETRACE(K,P,S,X) -+# define TREETRACE_ENABLED 0 -+#endif -+ -+/* TREETRACE flag meanings: -+** -+** 0x00000001 Beginning and end of SELECT processing -+** 0x00000002 WHERE clause processing -+** 0x00000004 Query flattener -+** 0x00000008 Result-set wildcard expansion -+** 0x00000010 Query name resolution -+** 0x00000020 Aggregate analysis -+** 0x00000040 Window functions -+** 0x00000080 Generated column names -+** 0x00000100 Move HAVING terms into WHERE -+** 0x00000200 Count-of-view optimization -+** 0x00000400 Compound SELECT processing -+** 0x00000800 Drop superfluous ORDER BY -+** 0x00001000 LEFT JOIN simplifies to JOIN -+** 0x00002000 Constant propagation -+** 0x00004000 Push-down optimization -+** 0x00008000 After all FROM-clause analysis -+** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing -+** 0x00020000 Transform DISTINCT into GROUP BY -+** 0x00040000 SELECT tree dump after all code has been generated -+** 0x00080000 NOT NULL strength reduction -+*/ -+ -+/* -+** Macros for "wheretrace" -+*/ -+SQLITE_PRIVATE u32 sqlite3WhereTrace; -+#if defined(SQLITE_DEBUG) \ -+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) -+# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X -+# define WHERETRACE_ENABLED 1 -+#else -+# define WHERETRACE(K,X) - #endif - -+/* -+** Bits for the sqlite3WhereTrace mask: -+** -+** (---any--) Top-level block structure -+** 0x-------F High-level debug messages -+** 0x----FFF- More detail -+** 0xFFFF---- Low-level debug messages -+** -+** 0x00000001 Code generation -+** 0x00000002 Solver (Use 0x40000 for less detail) -+** 0x00000004 Solver costs -+** 0x00000008 WhereLoop inserts -+** -+** 0x00000010 Display sqlite3_index_info xBestIndex calls -+** 0x00000020 Range an equality scan metrics -+** 0x00000040 IN operator decisions -+** 0x00000080 WhereLoop cost adjustments -+** 0x00000100 -+** 0x00000200 Covering index decisions -+** 0x00000400 OR optimization -+** 0x00000800 Index scanner -+** 0x00001000 More details associated with code generation -+** 0x00002000 -+** 0x00004000 Show all WHERE terms at key points -+** 0x00008000 Show the full SELECT statement at key places -+** -+** 0x00010000 Show more detail when printing WHERE terms -+** 0x00020000 Show WHERE terms returned from whereScanNext() -+** 0x00040000 Solver overview messages -+** 0x00080000 Star-query heuristic -+*/ -+ -+ - /* - ** An instance of the following structure is used to store the busy-handler - ** callback for a given sqlite handle. -@@ -14569,11 +15631,25 @@ struct BusyHandler { - - /* - ** Name of table that holds the database schema. -+** -+** The PREFERRED names are used wherever possible. But LEGACY is also -+** used for backwards compatibility. -+** -+** 1. Queries can use either the PREFERRED or the LEGACY names -+** 2. The sqlite3_set_authorizer() callback uses the LEGACY name -+** 3. The PRAGMA table_list statement uses the PREFERRED name -+** -+** The LEGACY names are stored in the internal symbol hash table -+** in support of (2). Names are translated using sqlite3PreferredTableName() -+** for (3). The sqlite3FindTable() function takes care of translating -+** names for (1). -+** -+** Note that "sqlite_temp_schema" can also be called "temp.sqlite_schema". - */ --#define DFLT_SCHEMA_TABLE "sqlite_master" --#define DFLT_TEMP_SCHEMA_TABLE "sqlite_temp_master" --#define ALT_SCHEMA_TABLE "sqlite_schema" --#define ALT_TEMP_SCHEMA_TABLE "sqlite_temp_schema" -+#define LEGACY_SCHEMA_TABLE "sqlite_master" -+#define LEGACY_TEMP_SCHEMA_TABLE "sqlite_temp_master" -+#define PREFERRED_SCHEMA_TABLE "sqlite_schema" -+#define PREFERRED_TEMP_SCHEMA_TABLE "sqlite_temp_schema" - - - /* -@@ -14585,7 +15661,7 @@ struct BusyHandler { - ** The name of the schema table. The name is different for TEMP. - */ - #define SCHEMA_TABLE(x) \ -- ((!OMIT_TEMPDB)&&(x==1)?DFLT_TEMP_SCHEMA_TABLE:DFLT_SCHEMA_TABLE) -+ ((!OMIT_TEMPDB)&&(x==1)?LEGACY_TEMP_SCHEMA_TABLE:LEGACY_SCHEMA_TABLE) - - /* - ** A convenience macro that returns the number of elements in -@@ -14606,7 +15682,7 @@ struct BusyHandler { - ** pointer will work here as long as it is distinct from SQLITE_STATIC - ** and SQLITE_TRANSIENT. - */ --#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomFault) -+#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomClear) - - /* - ** When SQLITE_OMIT_WSD is defined, it means that the target platform does -@@ -14662,16 +15738,22 @@ typedef struct AutoincInfo AutoincInfo; - typedef struct Bitvec Bitvec; - typedef struct CollSeq CollSeq; - typedef struct Column Column; -+typedef struct Cte Cte; -+typedef struct CteUse CteUse; - typedef struct Db Db; -+typedef struct DbClientData DbClientData; -+typedef struct DbFixer DbFixer; - typedef struct Schema Schema; - typedef struct Expr Expr; - typedef struct ExprList ExprList; - typedef struct FKey FKey; -+typedef struct FpDecode FpDecode; - typedef struct FuncDestructor FuncDestructor; - typedef struct FuncDef FuncDef; - typedef struct FuncDefHash FuncDefHash; - typedef struct IdList IdList; - typedef struct Index Index; -+typedef struct IndexedExpr IndexedExpr; - typedef struct IndexSample IndexSample; - typedef struct KeyClass KeyClass; - typedef struct KeyInfo KeyInfo; -@@ -14679,15 +15761,21 @@ typedef struct Lookaside Lookaside; - typedef struct LookasideSlot LookasideSlot; - typedef struct Module Module; - typedef struct NameContext NameContext; -+typedef struct OnOrUsing OnOrUsing; - typedef struct Parse Parse; -+typedef struct ParseCleanup ParseCleanup; - typedef struct PreUpdate PreUpdate; - typedef struct PrintfArguments PrintfArguments; -+typedef struct RCStr RCStr; - typedef struct RenameToken RenameToken; -+typedef struct Returning Returning; - typedef struct RowSet RowSet; - typedef struct Savepoint Savepoint; - typedef struct Select Select; - typedef struct SQLiteThread SQLiteThread; - typedef struct SelectDest SelectDest; -+typedef struct Subquery Subquery; -+typedef struct SrcItem SrcItem; - typedef struct SrcList SrcList; - typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */ - typedef struct Table Table; -@@ -14728,10 +15816,12 @@ typedef struct With With; - /* - ** A bit in a Bitmask - */ --#define MASKBIT(n) (((Bitmask)1)<<(n)) --#define MASKBIT64(n) (((u64)1)<<(n)) --#define MASKBIT32(n) (((unsigned int)1)<<(n)) --#define ALLBITS ((Bitmask)-1) -+#define MASKBIT(n) (((Bitmask)1)<<(n)) -+#define MASKBIT64(n) (((u64)1)<<(n)) -+#define MASKBIT32(n) (((unsigned int)1)<<(n)) -+#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0) -+#define ALLBITS ((Bitmask)-1) -+#define TOPBIT (((Bitmask)1)<<(BMS-1)) - - /* A VList object records a mapping between parameters/variables/wildcards - ** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer -@@ -14746,6 +15836,331 @@ typedef int VList; - ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque - ** pointer types (i.e. FuncDef) defined above. - */ -+/************** Include os.h in the middle of sqliteInt.h ********************/ -+/************** Begin file os.h **********************************************/ -+/* -+** 2001 September 16 -+** -+** 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 header file (together with is companion C source-code file -+** "os.c") attempt to abstract the underlying operating system so that -+** the SQLite library will work on both POSIX and windows systems. -+** -+** This header file is #include-ed by sqliteInt.h and thus ends up -+** being included by every source file. -+*/ -+#ifndef _SQLITE_OS_H_ -+#define _SQLITE_OS_H_ -+ -+/* -+** Attempt to automatically detect the operating system and setup the -+** necessary pre-processor macros for it. -+*/ -+/************** Include os_setup.h in the middle of os.h *********************/ -+/************** Begin file os_setup.h ****************************************/ -+/* -+** 2013 November 25 -+** -+** 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 contains pre-processor directives related to operating system -+** detection and/or setup. -+*/ -+#ifndef SQLITE_OS_SETUP_H -+#define SQLITE_OS_SETUP_H -+ -+/* -+** Figure out if we are dealing with Unix, Windows, or some other operating -+** system. -+** -+** After the following block of preprocess macros, all of -+** -+** SQLITE_OS_KV -+** SQLITE_OS_OTHER -+** SQLITE_OS_UNIX -+** SQLITE_OS_WIN -+** -+** will defined to either 1 or 0. One of them will be 1. The others will be 0. -+** If none of the macros are initially defined, then select either -+** SQLITE_OS_UNIX or SQLITE_OS_WIN depending on the target platform. -+** -+** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application -+** must provide its own VFS implementation together with sqlite3_os_init() -+** and sqlite3_os_end() routines. -+*/ -+#if !defined(SQLITE_OS_KV) && !defined(SQLITE_OS_OTHER) && \ -+ !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_WIN) -+# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \ -+ defined(__MINGW32__) || defined(__BORLANDC__) -+# define SQLITE_OS_WIN 1 -+# define SQLITE_OS_UNIX 0 -+# else -+# define SQLITE_OS_WIN 0 -+# define SQLITE_OS_UNIX 1 -+# endif -+#endif -+#if SQLITE_OS_OTHER+1>1 -+# undef SQLITE_OS_KV -+# define SQLITE_OS_KV 0 -+# undef SQLITE_OS_UNIX -+# define SQLITE_OS_UNIX 0 -+# undef SQLITE_OS_WIN -+# define SQLITE_OS_WIN 0 -+#endif -+#if SQLITE_OS_KV+1>1 -+# undef SQLITE_OS_OTHER -+# define SQLITE_OS_OTHER 0 -+# undef SQLITE_OS_UNIX -+# define SQLITE_OS_UNIX 0 -+# undef SQLITE_OS_WIN -+# define SQLITE_OS_WIN 0 -+# define SQLITE_OMIT_LOAD_EXTENSION 1 -+# define SQLITE_OMIT_WAL 1 -+# define SQLITE_OMIT_DEPRECATED 1 -+# undef SQLITE_TEMP_STORE -+# define SQLITE_TEMP_STORE 3 /* Always use memory for temporary storage */ -+# define SQLITE_DQS 0 -+# define SQLITE_OMIT_SHARED_CACHE 1 -+# define SQLITE_OMIT_AUTOINIT 1 -+#endif -+#if SQLITE_OS_UNIX+1>1 -+# undef SQLITE_OS_KV -+# define SQLITE_OS_KV 0 -+# undef SQLITE_OS_OTHER -+# define SQLITE_OS_OTHER 0 -+# undef SQLITE_OS_WIN -+# define SQLITE_OS_WIN 0 -+#endif -+#if SQLITE_OS_WIN+1>1 -+# undef SQLITE_OS_KV -+# define SQLITE_OS_KV 0 -+# undef SQLITE_OS_OTHER -+# define SQLITE_OS_OTHER 0 -+# undef SQLITE_OS_UNIX -+# define SQLITE_OS_UNIX 0 -+#endif -+ -+ -+#endif /* SQLITE_OS_SETUP_H */ -+ -+/************** End of os_setup.h ********************************************/ -+/************** Continuing where we left off in os.h *************************/ -+ -+/* If the SET_FULLSYNC macro is not defined above, then make it -+** a no-op -+*/ -+#ifndef SET_FULLSYNC -+# define SET_FULLSYNC(x,y) -+#endif -+ -+/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h -+*/ -+#ifndef SQLITE_MAX_PATHLEN -+# define SQLITE_MAX_PATHLEN FILENAME_MAX -+#endif -+ -+/* Maximum number of symlinks that will be resolved while trying to -+** expand a filename in xFullPathname() in the VFS. -+*/ -+#ifndef SQLITE_MAX_SYMLINK -+# define SQLITE_MAX_SYMLINK 200 -+#endif -+ -+/* -+** The default size of a disk sector -+*/ -+#ifndef SQLITE_DEFAULT_SECTOR_SIZE -+# define SQLITE_DEFAULT_SECTOR_SIZE 4096 -+#endif -+ -+/* -+** Temporary files are named starting with this prefix followed by 16 random -+** alphanumeric characters, and no file extension. They are stored in the -+** OS's standard temporary file directory, and are deleted prior to exit. -+** If sqlite is being embedded in another program, you may wish to change the -+** prefix to reflect your program's name, so that if your program exits -+** prematurely, old temporary files can be easily identified. This can be done -+** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. -+** -+** 2006-10-31: The default prefix used to be "sqlite_". But then -+** Mcafee started using SQLite in their anti-virus product and it -+** started putting files with the "sqlite" name in the c:/temp folder. -+** This annoyed many windows users. Those users would then do a -+** Google search for "sqlite", find the telephone numbers of the -+** developers and call to wake them up at night and complain. -+** For this reason, the default name prefix is changed to be "sqlite" -+** spelled backwards. So the temp files are still identified, but -+** anybody smart enough to figure out the code is also likely smart -+** enough to know that calling the developer will not help get rid -+** of the file. -+*/ -+#ifndef SQLITE_TEMP_FILE_PREFIX -+# define SQLITE_TEMP_FILE_PREFIX "etilqs_" -+#endif -+ -+/* -+** The following values may be passed as the second argument to -+** sqlite3OsLock(). The various locks exhibit the following semantics: -+** -+** SHARED: Any number of processes may hold a SHARED lock simultaneously. -+** RESERVED: A single process may hold a RESERVED lock on a file at -+** any time. Other processes may hold and obtain new SHARED locks. -+** PENDING: A single process may hold a PENDING lock on a file at -+** any one time. Existing SHARED locks may persist, but no new -+** SHARED locks may be obtained by other processes. -+** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. -+** -+** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a -+** process that requests an EXCLUSIVE lock may actually obtain a PENDING -+** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to -+** sqlite3OsLock(). -+*/ -+#define NO_LOCK 0 -+#define SHARED_LOCK 1 -+#define RESERVED_LOCK 2 -+#define PENDING_LOCK 3 -+#define EXCLUSIVE_LOCK 4 -+ -+/* -+** File Locking Notes: (Mostly about windows but also some info for Unix) -+** -+** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because -+** those functions are not available. So we use only LockFile() and -+** UnlockFile(). -+** -+** LockFile() prevents not just writing but also reading by other processes. -+** A SHARED_LOCK is obtained by locking a single randomly-chosen -+** byte out of a specific range of bytes. The lock byte is obtained at -+** random so two separate readers can probably access the file at the -+** same time, unless they are unlucky and choose the same lock byte. -+** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. -+** There can only be one writer. A RESERVED_LOCK is obtained by locking -+** a single byte of the file that is designated as the reserved lock byte. -+** A PENDING_LOCK is obtained by locking a designated byte different from -+** the RESERVED_LOCK byte. -+** -+** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, -+** which means we can use reader/writer locks. When reader/writer locks -+** are used, the lock is placed on the same range of bytes that is used -+** for probabilistic locking in Win95/98/ME. Hence, the locking scheme -+** will support two or more Win95 readers or two or more WinNT readers. -+** But a single Win95 reader will lock out all WinNT readers and a single -+** WinNT reader will lock out all other Win95 readers. -+** -+** The following #defines specify the range of bytes used for locking. -+** SHARED_SIZE is the number of bytes available in the pool from which -+** a random byte is selected for a shared lock. The pool of bytes for -+** shared locks begins at SHARED_FIRST. -+** -+** The same locking strategy and -+** byte ranges are used for Unix. This leaves open the possibility of having -+** clients on win95, winNT, and unix all talking to the same shared file -+** and all locking correctly. To do so would require that samba (or whatever -+** tool is being used for file sharing) implements locks correctly between -+** windows and unix. I'm guessing that isn't likely to happen, but by -+** using the same locking range we are at least open to the possibility. -+** -+** Locking in windows is manditory. For this reason, we cannot store -+** actual data in the bytes used for locking. The pager never allocates -+** the pages involved in locking therefore. SHARED_SIZE is selected so -+** that all locks will fit on a single page even at the minimum page size. -+** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE -+** is set high so that we don't have to allocate an unused page except -+** for very large databases. But one should test the page skipping logic -+** by setting PENDING_BYTE low and running the entire regression suite. -+** -+** Changing the value of PENDING_BYTE results in a subtly incompatible -+** file format. Depending on how it is changed, you might not notice -+** the incompatibility right away, even running a full regression test. -+** The default location of PENDING_BYTE is the first byte past the -+** 1GB boundary. -+** -+*/ -+#ifdef SQLITE_OMIT_WSD -+# define PENDING_BYTE (0x40000000) -+#else -+# define PENDING_BYTE sqlite3PendingByte -+#endif -+#define RESERVED_BYTE (PENDING_BYTE+1) -+#define SHARED_FIRST (PENDING_BYTE+2) -+#define SHARED_SIZE 510 -+ -+/* -+** Wrapper around OS specific sqlite3_os_init() function. -+*/ -+SQLITE_PRIVATE int sqlite3OsInit(void); -+ -+/* -+** Functions for accessing sqlite3_file methods -+*/ -+SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*); -+SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); -+SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); -+SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); -+SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); -+SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); -+SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); -+SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); -+SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); -+SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); -+SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); -+#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 -+SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); -+SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); -+#ifndef SQLITE_OMIT_WAL -+SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); -+SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); -+SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); -+SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); -+#endif /* SQLITE_OMIT_WAL */ -+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **); -+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *); -+ -+ -+/* -+** Functions for accessing sqlite3_vfs methods -+*/ -+SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); -+SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); -+SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); -+SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); -+#ifndef SQLITE_OMIT_LOAD_EXTENSION -+SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); -+SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); -+SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); -+SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); -+#endif /* SQLITE_OMIT_LOAD_EXTENSION */ -+SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); -+SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); -+SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*); -+SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); -+ -+/* -+** Convenience functions for opening and closing files using -+** sqlite3_malloc() to obtain space for the file-handle structure. -+*/ -+SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); -+SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *); -+ -+#endif /* _SQLITE_OS_H_ */ -+ -+/************** End of os.h **************************************************/ -+/************** Continuing where we left off in sqliteInt.h ******************/ - /************** Include pager.h in the middle of sqliteInt.h *****************/ - /************** Begin file pager.h *******************************************/ - /* -@@ -14793,14 +16208,15 @@ typedef struct Pager Pager; - typedef struct PgHdr DbPage; - - /* --** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is -+** Page number PAGER_SJ_PGNO is never used in an SQLite database (it is - ** reserved for working around a windows/posix incompatibility). It is - ** used in the journal to signify that the remainder of the journal file - ** is devoted to storing a super-journal name - there are no more pages to - ** roll back. See comments for function writeSuperJournal() in pager.c - ** for details. - */ --#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) -+#define PAGER_SJ_PGNO_COMPUTED(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) -+#define PAGER_SJ_PGNO(x) ((x)->lckPgno) - - /* - ** Allowed values for the flags parameter to sqlite3PagerOpen(). -@@ -14832,6 +16248,22 @@ typedef struct PgHdr DbPage; - #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ - #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ - -+#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL) -+ -+/* -+** The argument to this macro is a file descriptor (type sqlite3_file*). -+** Return 0 if it is not open, or non-zero (but not 1) if it is. -+** -+** This is so that expressions can be written as: -+** -+** if( isOpen(pPager->jfd) ){ ... -+** -+** instead of -+** -+** if( pPager->jfd->pMethods ){ ... -+*/ -+#define isOpen(pFd) ((pFd)->pMethods!=0) -+ - /* - ** Flags that make up the mask passed to sqlite3PagerGet(). - */ -@@ -14965,7 +16397,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); - SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); - SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); - SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); --SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); -+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*); - SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); - SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); - -@@ -14989,6 +16421,10 @@ SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); - # define enable_simulated_io_errors() - #endif - -+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) -+SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager*); -+#endif -+ - #endif /* SQLITE_PAGER_H */ - - /************** End of pager.h ***********************************************/ -@@ -15082,16 +16518,24 @@ SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); - SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int); - SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); - SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags); --SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); --SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); -+SQLITE_PRIVATE int sqlite3BtreeTxnState(Btree*); - SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); -+ - SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); - SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); - #ifndef SQLITE_OMIT_SHARED_CACHE - SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); - #endif -+ -+/* Savepoints are named, nestable SQL transactions mostly implemented */ -+/* in vdbe.c and pager.c See https://sqlite.org/lang_savepoint.html */ - SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); - -+/* "Checkpoint" only refers to WAL. See https://sqlite.org/wal.html#ckpt */ -+#ifndef SQLITE_OMIT_WAL -+SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); -+#endif -+ - SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); - SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); - SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); -@@ -15112,7 +16556,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); - #define BTREE_BLOBKEY 2 /* Table has keys only - no data */ - - SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); --SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); -+SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, i64*); - SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor*); - SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree*, int, int); - -@@ -15172,7 +16616,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); - ** reduce network bandwidth. - ** - ** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by --** standard SQLite. The other hints are provided for extentions that use -+** standard SQLite. The other hints are provided for extensions that use - ** the SQLite parser and code generator but substitute their own storage - ** engine. - */ -@@ -15229,6 +16673,9 @@ SQLITE_PRIVATE int sqlite3BtreeCursor( - ); - SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); - SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); -+#ifdef SQLITE_DEBUG -+SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(Btree*,BtCursor*); -+#endif - SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); - SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); - #ifdef SQLITE_ENABLE_CURSOR_HINTS -@@ -15236,13 +16683,17 @@ SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...); - #endif - - SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); --SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( -+SQLITE_PRIVATE int sqlite3BtreeTableMoveto( - BtCursor*, -- UnpackedRecord *pUnKey, - i64 intKey, - int bias, - int *pRes - ); -+SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( -+ BtCursor*, -+ UnpackedRecord *pUnKey, -+ int *pRes -+); - SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*); - SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*); - SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags); -@@ -15251,6 +16702,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags); - #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ - #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ - #define BTREE_APPEND 0x08 /* Insert is likely an append */ -+#define BTREE_PREFORMAT 0x80 /* Inserted data is a preformated cell */ - - /* An instance of the BtreePayload object describes the content of a single - ** entry in either an index or table btree. -@@ -15305,15 +16757,22 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int flags); - SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor*); - SQLITE_PRIVATE void sqlite3BtreeCursorPin(BtCursor*); - SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor*); --#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); --#endif - SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); - SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); - SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); - SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); - --SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*); -+SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( -+ sqlite3 *db, /* Database connection that is running the check */ -+ Btree *p, /* The btree to be checked */ -+ Pgno *aRoot, /* An array of root pages numbers for individual trees */ -+ sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */ -+ int nRoot, /* Number of entries in aRoot[] */ -+ int mxErr, /* Stop reporting errors after this many */ -+ int *pnErr, /* OUT: Write number of errors seen to this variable */ -+ char **pzOut /* OUT: Write the error message string here */ -+); - SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); - SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*); - -@@ -15328,6 +16787,12 @@ SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask); - SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *pBt); - SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void); - -+#ifdef SQLITE_DEBUG -+SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree*); -+#else -+# define sqlite3BtreeSeekCount(X) 0 -+#endif -+ - #ifndef NDEBUG - SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); - #endif -@@ -15344,6 +16809,10 @@ SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); - SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); - #endif - -+SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64); -+ -+SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree*); -+ - /* - ** If we are not using shared cache, then there is no need to - ** use mutexes to access the BtShared structures. So make the -@@ -15425,6 +16894,20 @@ typedef struct Vdbe Vdbe; - */ - typedef struct sqlite3_value Mem; - typedef struct SubProgram SubProgram; -+typedef struct SubrtnSig SubrtnSig; -+ -+/* -+** A signature for a reusable subroutine that materializes the RHS of -+** an IN operator. -+*/ -+struct SubrtnSig { -+ int selId; /* SELECT-id for the SELECT statement on the RHS */ -+ u8 bComplete; /* True if fully coded and available for reusable */ -+ char *zAff; /* Affinity of the overall IN expression */ -+ int iTable; /* Ephemeral table generated by the subroutine */ -+ int iAddr; /* Subroutine entry address */ -+ int regReturn; /* Register used to hold return address */ -+}; - - /* - ** A single instruction of the virtual machine has an opcode -@@ -15453,22 +16936,22 @@ struct VdbeOp { - u32 *ai; /* Used when p4type is P4_INTARRAY */ - SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ - Table *pTab; /* Used when p4type is P4_TABLE */ -+ SubrtnSig *pSubrtnSig; /* Used when p4type is P4_SUBRTNSIG */ - #ifdef SQLITE_ENABLE_CURSOR_HINTS - Expr *pExpr; /* Used when p4type is P4_EXPR */ - #endif -- int (*xAdvance)(BtCursor *, int); - } p4; - #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - char *zComment; /* Comment to improve readability */ - #endif --#ifdef VDBE_PROFILE -- u32 cnt; /* Number of times this instruction was executed */ -- u64 cycles; /* Total time spent executing this instruction */ --#endif - #ifdef SQLITE_VDBE_COVERAGE - u32 iSrcLine; /* Source-code line that generated this opcode - ** with flags in the upper 8 bits */ - #endif -+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) -+ u64 nExec; -+ u64 nCycle; -+#endif - }; - typedef struct VdbeOp VdbeOp; - -@@ -15507,21 +16990,21 @@ typedef struct VdbeOpList VdbeOpList; - #define P4_COLLSEQ (-2) /* P4 is a pointer to a CollSeq structure */ - #define P4_INT32 (-3) /* P4 is a 32-bit signed integer */ - #define P4_SUBPROGRAM (-4) /* P4 is a pointer to a SubProgram structure */ --#define P4_ADVANCE (-5) /* P4 is a pointer to BtreeNext() or BtreePrev() */ --#define P4_TABLE (-6) /* P4 is a pointer to a Table structure */ -+#define P4_TABLE (-5) /* P4 is a pointer to a Table structure */ - /* Above do not own any resources. Must free those below */ --#define P4_FREE_IF_LE (-7) --#define P4_DYNAMIC (-7) /* Pointer to memory from sqliteMalloc() */ --#define P4_FUNCDEF (-8) /* P4 is a pointer to a FuncDef structure */ --#define P4_KEYINFO (-9) /* P4 is a pointer to a KeyInfo structure */ --#define P4_EXPR (-10) /* P4 is a pointer to an Expr tree */ --#define P4_MEM (-11) /* P4 is a pointer to a Mem* structure */ --#define P4_VTAB (-12) /* P4 is a pointer to an sqlite3_vtab structure */ --#define P4_REAL (-13) /* P4 is a 64-bit floating point value */ --#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */ --#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ --#define P4_FUNCCTX (-16) /* P4 is a pointer to an sqlite3_context object */ --#define P4_DYNBLOB (-17) /* Pointer to memory from sqliteMalloc() */ -+#define P4_FREE_IF_LE (-6) -+#define P4_DYNAMIC (-6) /* Pointer to memory from sqliteMalloc() */ -+#define P4_FUNCDEF (-7) /* P4 is a pointer to a FuncDef structure */ -+#define P4_KEYINFO (-8) /* P4 is a pointer to a KeyInfo structure */ -+#define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ -+#define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ -+#define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */ -+#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ -+#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ -+#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ -+#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ -+#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ -+#define P4_SUBRTNSIG (-17) /* P4 is a SubrtnSig pointer */ - - /* Error message codes for OP_Halt */ - #define P5_ConstraintNotNull 1 -@@ -15566,179 +17049,193 @@ typedef struct VdbeOpList VdbeOpList; - #define OP_Savepoint 0 - #define OP_AutoCommit 1 - #define OP_Transaction 2 --#define OP_SorterNext 3 /* jump */ --#define OP_Prev 4 /* jump */ --#define OP_Next 5 /* jump */ --#define OP_Checkpoint 6 --#define OP_JournalMode 7 --#define OP_Vacuum 8 --#define OP_VFilter 9 /* jump, synopsis: iplan=r[P3] zplan='P4' */ --#define OP_VUpdate 10 /* synopsis: data=r[P3@P2] */ --#define OP_Goto 11 /* jump */ --#define OP_Gosub 12 /* jump */ --#define OP_InitCoroutine 13 /* jump */ --#define OP_Yield 14 /* jump */ --#define OP_MustBeInt 15 /* jump */ --#define OP_Jump 16 /* jump */ --#define OP_Once 17 /* jump */ --#define OP_If 18 /* jump */ -+#define OP_Checkpoint 3 -+#define OP_JournalMode 4 -+#define OP_Vacuum 5 -+#define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */ -+#define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */ -+#define OP_Init 8 /* jump0, synopsis: Start at P2 */ -+#define OP_Goto 9 /* jump */ -+#define OP_Gosub 10 /* jump */ -+#define OP_InitCoroutine 11 /* jump0 */ -+#define OP_Yield 12 /* jump0 */ -+#define OP_MustBeInt 13 /* jump0 */ -+#define OP_Jump 14 /* jump */ -+#define OP_Once 15 /* jump */ -+#define OP_If 16 /* jump */ -+#define OP_IfNot 17 /* jump */ -+#define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */ - #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ --#define OP_IfNot 20 /* jump */ --#define OP_IfNullRow 21 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ --#define OP_SeekLT 22 /* jump, synopsis: key=r[P3@P4] */ --#define OP_SeekLE 23 /* jump, synopsis: key=r[P3@P4] */ --#define OP_SeekGE 24 /* jump, synopsis: key=r[P3@P4] */ --#define OP_SeekGT 25 /* jump, synopsis: key=r[P3@P4] */ --#define OP_IfNotOpen 26 /* jump, synopsis: if( !csr[P1] ) goto P2 */ --#define OP_IfNoHope 27 /* jump, synopsis: key=r[P3@P4] */ --#define OP_NoConflict 28 /* jump, synopsis: key=r[P3@P4] */ --#define OP_NotFound 29 /* jump, synopsis: key=r[P3@P4] */ --#define OP_Found 30 /* jump, synopsis: key=r[P3@P4] */ --#define OP_SeekRowid 31 /* jump, synopsis: intkey=r[P3] */ --#define OP_NotExists 32 /* jump, synopsis: intkey=r[P3] */ --#define OP_Last 33 /* jump */ --#define OP_IfSmaller 34 /* jump */ --#define OP_SorterSort 35 /* jump */ --#define OP_Sort 36 /* jump */ --#define OP_Rewind 37 /* jump */ --#define OP_IdxLE 38 /* jump, synopsis: key=r[P3@P4] */ --#define OP_IdxGT 39 /* jump, synopsis: key=r[P3@P4] */ --#define OP_IdxLT 40 /* jump, synopsis: key=r[P3@P4] */ --#define OP_IdxGE 41 /* jump, synopsis: key=r[P3@P4] */ --#define OP_RowSetRead 42 /* jump, synopsis: r[P3]=rowset(P1) */ -+#define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ -+#define OP_SeekLT 21 /* jump0, synopsis: key=r[P3@P4] */ -+#define OP_SeekLE 22 /* jump0, synopsis: key=r[P3@P4] */ -+#define OP_SeekGE 23 /* jump0, synopsis: key=r[P3@P4] */ -+#define OP_SeekGT 24 /* jump0, synopsis: key=r[P3@P4] */ -+#define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */ -+#define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */ -+#define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */ -+#define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */ -+#define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */ -+#define OP_SeekRowid 30 /* jump0, synopsis: intkey=r[P3] */ -+#define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */ -+#define OP_Last 32 /* jump0 */ -+#define OP_IfSizeBetween 33 /* jump */ -+#define OP_SorterSort 34 /* jump */ -+#define OP_Sort 35 /* jump */ -+#define OP_Rewind 36 /* jump0 */ -+#define OP_SorterNext 37 /* jump */ -+#define OP_Prev 38 /* jump */ -+#define OP_Next 39 /* jump */ -+#define OP_IdxLE 40 /* jump, synopsis: key=r[P3@P4] */ -+#define OP_IdxGT 41 /* jump, synopsis: key=r[P3@P4] */ -+#define OP_IdxLT 42 /* jump, synopsis: key=r[P3@P4] */ - #define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */ - #define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */ --#define OP_RowSetTest 45 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ --#define OP_Program 46 /* jump */ --#define OP_FkIfZero 47 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ --#define OP_IfPos 48 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ --#define OP_IfNotZero 49 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ --#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ --#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ --#define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ --#define OP_Eq 53 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */ --#define OP_Gt 54 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */ --#define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ --#define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ --#define OP_ElseNotEq 58 /* jump, same as TK_ESCAPE */ --#define OP_DecrJumpZero 59 /* jump, synopsis: if (--r[P1])==0 goto P2 */ --#define OP_IncrVacuum 60 /* jump */ --#define OP_VNext 61 /* jump */ --#define OP_Init 62 /* jump, synopsis: Start at P2 */ --#define OP_PureFunc 63 /* synopsis: r[P3]=func(r[P2@NP]) */ --#define OP_Function 64 /* synopsis: r[P3]=func(r[P2@NP]) */ --#define OP_Return 65 --#define OP_EndCoroutine 66 --#define OP_HaltIfNull 67 /* synopsis: if r[P3]=null halt */ --#define OP_Halt 68 --#define OP_Integer 69 /* synopsis: r[P2]=P1 */ --#define OP_Int64 70 /* synopsis: r[P2]=P4 */ --#define OP_String 71 /* synopsis: r[P2]='P4' (len=P1) */ --#define OP_Null 72 /* synopsis: r[P2..P3]=NULL */ --#define OP_SoftNull 73 /* synopsis: r[P1]=NULL */ --#define OP_Blob 74 /* synopsis: r[P2]=P4 (len=P1) */ --#define OP_Variable 75 /* synopsis: r[P2]=parameter(P1,P4) */ --#define OP_Move 76 /* synopsis: r[P2@P3]=r[P1@P3] */ --#define OP_Copy 77 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ --#define OP_SCopy 78 /* synopsis: r[P2]=r[P1] */ --#define OP_IntCopy 79 /* synopsis: r[P2]=r[P1] */ --#define OP_ResultRow 80 /* synopsis: output=r[P1@P2] */ --#define OP_CollSeq 81 --#define OP_AddImm 82 /* synopsis: r[P1]=r[P1]+P2 */ --#define OP_RealAffinity 83 --#define OP_Cast 84 /* synopsis: affinity(r[P1]) */ --#define OP_Permutation 85 --#define OP_Compare 86 /* synopsis: r[P1@P3] <-> r[P2@P3] */ --#define OP_IsTrue 87 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ --#define OP_Offset 88 /* synopsis: r[P3] = sqlite_offset(P1) */ --#define OP_Column 89 /* synopsis: r[P3]=PX */ --#define OP_Affinity 90 /* synopsis: affinity(r[P1@P2]) */ --#define OP_MakeRecord 91 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ --#define OP_Count 92 /* synopsis: r[P2]=count() */ --#define OP_ReadCookie 93 --#define OP_SetCookie 94 --#define OP_ReopenIdx 95 /* synopsis: root=P2 iDb=P3 */ --#define OP_OpenRead 96 /* synopsis: root=P2 iDb=P3 */ --#define OP_OpenWrite 97 /* synopsis: root=P2 iDb=P3 */ --#define OP_OpenDup 98 --#define OP_OpenAutoindex 99 /* synopsis: nColumn=P2 */ --#define OP_OpenEphemeral 100 /* synopsis: nColumn=P2 */ --#define OP_BitAnd 101 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ --#define OP_BitOr 102 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ --#define OP_ShiftLeft 103 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ --#define OP_Add 105 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ --#define OP_Subtract 106 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ --#define OP_Multiply 107 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ --#define OP_Divide 108 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ --#define OP_Remainder 109 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ --#define OP_Concat 110 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ --#define OP_SorterOpen 111 --#define OP_BitNot 112 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ --#define OP_SequenceTest 113 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ --#define OP_OpenPseudo 114 /* synopsis: P3 columns in r[P2] */ --#define OP_String8 115 /* same as TK_STRING, synopsis: r[P2]='P4' */ --#define OP_Close 116 --#define OP_ColumnsUsed 117 --#define OP_SeekHit 118 /* synopsis: seekHit=P2 */ --#define OP_Sequence 119 /* synopsis: r[P2]=cursor[P1].ctr++ */ --#define OP_NewRowid 120 /* synopsis: r[P2]=rowid */ --#define OP_Insert 121 /* synopsis: intkey=r[P3] data=r[P2] */ --#define OP_Delete 122 --#define OP_ResetCount 123 --#define OP_SorterCompare 124 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ --#define OP_SorterData 125 /* synopsis: r[P2]=data */ --#define OP_RowData 126 /* synopsis: r[P2]=data */ --#define OP_Rowid 127 /* synopsis: r[P2]=rowid */ --#define OP_NullRow 128 --#define OP_SeekEnd 129 --#define OP_IdxInsert 130 /* synopsis: key=r[P2] */ --#define OP_SorterInsert 131 /* synopsis: key=r[P2] */ --#define OP_IdxDelete 132 /* synopsis: key=r[P2@P3] */ --#define OP_DeferredSeek 133 /* synopsis: Move P3 to P1.rowid if needed */ --#define OP_IdxRowid 134 /* synopsis: r[P2]=rowid */ --#define OP_FinishSeek 135 --#define OP_Destroy 136 --#define OP_Clear 137 --#define OP_ResetSorter 138 --#define OP_CreateBtree 139 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ --#define OP_SqlExec 140 --#define OP_ParseSchema 141 --#define OP_LoadAnalysis 142 --#define OP_DropTable 143 --#define OP_DropIndex 144 --#define OP_DropTrigger 145 --#define OP_IntegrityCk 146 --#define OP_RowSetAdd 147 /* synopsis: rowset(P1)=r[P2] */ --#define OP_Param 148 --#define OP_FkCounter 149 /* synopsis: fkctr[P1]+=P2 */ --#define OP_Real 150 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ --#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */ --#define OP_OffsetLimit 152 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ --#define OP_AggInverse 153 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ --#define OP_AggStep 154 /* synopsis: accum=r[P3] step(r[P2@P5]) */ --#define OP_AggStep1 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ --#define OP_AggValue 156 /* synopsis: r[P3]=value N=P2 */ --#define OP_AggFinal 157 /* synopsis: accum=r[P1] N=P2 */ --#define OP_Expire 158 --#define OP_CursorLock 159 --#define OP_CursorUnlock 160 --#define OP_TableLock 161 /* synopsis: iDb=P1 root=P2 write=P3 */ --#define OP_VBegin 162 --#define OP_VCreate 163 --#define OP_VDestroy 164 --#define OP_VOpen 165 --#define OP_VColumn 166 /* synopsis: r[P3]=vcolumn(P2) */ --#define OP_VRename 167 --#define OP_Pagecount 168 --#define OP_MaxPgcnt 169 --#define OP_Trace 170 --#define OP_CursorHint 171 --#define OP_ReleaseReg 172 /* synopsis: release r[P1@P2] mask P3 */ --#define OP_Noop 173 --#define OP_Explain 174 --#define OP_Abortable 175 -+#define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */ -+#define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */ -+#define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ -+#define OP_Program 48 /* jump0 */ -+#define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ -+#define OP_IfPos 50 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ -+#define OP_IsNull 51 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ -+#define OP_NotNull 52 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ -+#define OP_Ne 53 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ -+#define OP_Eq 54 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */ -+#define OP_Gt 55 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */ -+#define OP_Le 56 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ -+#define OP_Lt 57 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ -+#define OP_ElseEq 59 /* jump, same as TK_ESCAPE */ -+#define OP_IfNotZero 60 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ -+#define OP_DecrJumpZero 61 /* jump, synopsis: if (--r[P1])==0 goto P2 */ -+#define OP_IncrVacuum 62 /* jump */ -+#define OP_VNext 63 /* jump */ -+#define OP_Filter 64 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */ -+#define OP_PureFunc 65 /* synopsis: r[P3]=func(r[P2@NP]) */ -+#define OP_Function 66 /* synopsis: r[P3]=func(r[P2@NP]) */ -+#define OP_Return 67 -+#define OP_EndCoroutine 68 -+#define OP_HaltIfNull 69 /* synopsis: if r[P3]=null halt */ -+#define OP_Halt 70 -+#define OP_Integer 71 /* synopsis: r[P2]=P1 */ -+#define OP_Int64 72 /* synopsis: r[P2]=P4 */ -+#define OP_String 73 /* synopsis: r[P2]='P4' (len=P1) */ -+#define OP_BeginSubrtn 74 /* synopsis: r[P2]=NULL */ -+#define OP_Null 75 /* synopsis: r[P2..P3]=NULL */ -+#define OP_SoftNull 76 /* synopsis: r[P1]=NULL */ -+#define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */ -+#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1) */ -+#define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */ -+#define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ -+#define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */ -+#define OP_IntCopy 82 /* synopsis: r[P2]=r[P1] */ -+#define OP_FkCheck 83 -+#define OP_ResultRow 84 /* synopsis: output=r[P1@P2] */ -+#define OP_CollSeq 85 -+#define OP_AddImm 86 /* synopsis: r[P1]=r[P1]+P2 */ -+#define OP_RealAffinity 87 -+#define OP_Cast 88 /* synopsis: affinity(r[P1]) */ -+#define OP_Permutation 89 -+#define OP_Compare 90 /* synopsis: r[P1@P3] <-> r[P2@P3] */ -+#define OP_IsTrue 91 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ -+#define OP_ZeroOrNull 92 /* synopsis: r[P2] = 0 OR NULL */ -+#define OP_Offset 93 /* synopsis: r[P3] = sqlite_offset(P1) */ -+#define OP_Column 94 /* synopsis: r[P3]=PX cursor P1 column P2 */ -+#define OP_TypeCheck 95 /* synopsis: typecheck(r[P1@P2]) */ -+#define OP_Affinity 96 /* synopsis: affinity(r[P1@P2]) */ -+#define OP_MakeRecord 97 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -+#define OP_Count 98 /* synopsis: r[P2]=count() */ -+#define OP_ReadCookie 99 -+#define OP_SetCookie 100 -+#define OP_ReopenIdx 101 /* synopsis: root=P2 iDb=P3 */ -+#define OP_OpenRead 102 /* synopsis: root=P2 iDb=P3 */ -+#define OP_BitAnd 103 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ -+#define OP_BitOr 104 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ -+#define OP_ShiftLeft 105 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ -+#define OP_Add 107 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ -+#define OP_Subtract 108 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ -+#define OP_Multiply 109 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ -+#define OP_Divide 110 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ -+#define OP_Remainder 111 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ -+#define OP_Concat 112 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ -+#define OP_OpenWrite 113 /* synopsis: root=P2 iDb=P3 */ -+#define OP_OpenDup 114 -+#define OP_BitNot 115 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ -+#define OP_OpenAutoindex 116 /* synopsis: nColumn=P2 */ -+#define OP_OpenEphemeral 117 /* synopsis: nColumn=P2 */ -+#define OP_String8 118 /* same as TK_STRING, synopsis: r[P2]='P4' */ -+#define OP_SorterOpen 119 -+#define OP_SequenceTest 120 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ -+#define OP_OpenPseudo 121 /* synopsis: P3 columns in r[P2] */ -+#define OP_Close 122 -+#define OP_ColumnsUsed 123 -+#define OP_SeekScan 124 /* synopsis: Scan-ahead up to P1 rows */ -+#define OP_SeekHit 125 /* synopsis: set P2<=seekHit<=P3 */ -+#define OP_Sequence 126 /* synopsis: r[P2]=cursor[P1].ctr++ */ -+#define OP_NewRowid 127 /* synopsis: r[P2]=rowid */ -+#define OP_Insert 128 /* synopsis: intkey=r[P3] data=r[P2] */ -+#define OP_RowCell 129 -+#define OP_Delete 130 -+#define OP_ResetCount 131 -+#define OP_SorterCompare 132 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ -+#define OP_SorterData 133 /* synopsis: r[P2]=data */ -+#define OP_RowData 134 /* synopsis: r[P2]=data */ -+#define OP_Rowid 135 /* synopsis: r[P2]=PX rowid of P1 */ -+#define OP_NullRow 136 -+#define OP_SeekEnd 137 -+#define OP_IdxInsert 138 /* synopsis: key=r[P2] */ -+#define OP_SorterInsert 139 /* synopsis: key=r[P2] */ -+#define OP_IdxDelete 140 /* synopsis: key=r[P2@P3] */ -+#define OP_DeferredSeek 141 /* synopsis: Move P3 to P1.rowid if needed */ -+#define OP_IdxRowid 142 /* synopsis: r[P2]=rowid */ -+#define OP_FinishSeek 143 -+#define OP_Destroy 144 -+#define OP_Clear 145 -+#define OP_ResetSorter 146 -+#define OP_CreateBtree 147 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ -+#define OP_SqlExec 148 -+#define OP_ParseSchema 149 -+#define OP_LoadAnalysis 150 -+#define OP_DropTable 151 -+#define OP_DropIndex 152 -+#define OP_DropTrigger 153 -+#define OP_Real 154 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -+#define OP_IntegrityCk 155 -+#define OP_RowSetAdd 156 /* synopsis: rowset(P1)=r[P2] */ -+#define OP_Param 157 -+#define OP_FkCounter 158 /* synopsis: fkctr[P1]+=P2 */ -+#define OP_MemMax 159 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -+#define OP_OffsetLimit 160 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -+#define OP_AggInverse 161 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ -+#define OP_AggStep 162 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -+#define OP_AggStep1 163 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -+#define OP_AggValue 164 /* synopsis: r[P3]=value N=P2 */ -+#define OP_AggFinal 165 /* synopsis: accum=r[P1] N=P2 */ -+#define OP_Expire 166 -+#define OP_CursorLock 167 -+#define OP_CursorUnlock 168 -+#define OP_TableLock 169 /* synopsis: iDb=P1 root=P2 write=P3 */ -+#define OP_VBegin 170 -+#define OP_VCreate 171 -+#define OP_VDestroy 172 -+#define OP_VOpen 173 -+#define OP_VCheck 174 -+#define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */ -+#define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */ -+#define OP_VRename 177 -+#define OP_Pagecount 178 -+#define OP_MaxPgcnt 179 -+#define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */ -+#define OP_GetSubtype 181 /* synopsis: r[P2] = r[P1].subtype */ -+#define OP_SetSubtype 182 /* synopsis: r[P2].subtype = r[P1] */ -+#define OP_FilterAdd 183 /* synopsis: filter(P1) += key(P3@P4) */ -+#define OP_Trace 184 -+#define OP_CursorHint 185 -+#define OP_ReleaseReg 186 /* synopsis: release r[P1@P2] mask P3 */ -+#define OP_Noop 187 -+#define OP_Explain 188 -+#define OP_Abortable 189 - - /* Properties such as "out2" or "jump" that are specified in - ** comments following the "case" for each opcode in the vdbe.c -@@ -15750,38 +17247,41 @@ typedef struct VdbeOpList VdbeOpList; - #define OPFLG_IN3 0x08 /* in3: P3 is an input */ - #define OPFLG_OUT2 0x10 /* out2: P2 is an output */ - #define OPFLG_OUT3 0x20 /* out3: P3 is an output */ -+#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */ -+#define OPFLG_JUMP0 0x80 /* jump0: P2 might be zero */ - #define OPFLG_INITIALIZER {\ --/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x10,\ --/* 8 */ 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03,\ --/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x01, 0x09, 0x09,\ --/* 24 */ 0x09, 0x09, 0x01, 0x09, 0x09, 0x09, 0x09, 0x09,\ --/* 32 */ 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\ --/* 40 */ 0x01, 0x01, 0x23, 0x26, 0x26, 0x0b, 0x01, 0x01,\ --/* 48 */ 0x03, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ --/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00,\ --/* 64 */ 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10, 0x10,\ --/* 72 */ 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\ --/* 80 */ 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x12,\ --/* 88 */ 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ --/* 96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26,\ --/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00,\ --/* 112 */ 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,\ --/* 120 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ --/* 128 */ 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10, 0x00,\ --/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ --/* 144 */ 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x10, 0x04,\ --/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ --/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ --/* 168 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ --} -- --/* The sqlite3P2Values() routine is able to run faster if it knows -+/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\ -+/* 8 */ 0x81, 0x01, 0x01, 0x81, 0x83, 0x83, 0x01, 0x01,\ -+/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\ -+/* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\ -+/* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x41, 0x41,\ -+/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ -+/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\ -+/* 56 */ 0x0b, 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x01, 0x41,\ -+/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ -+/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ -+/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\ -+/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40, 0x00,\ -+/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x40, 0x26,\ -+/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ -+/* 112 */ 0x26, 0x00, 0x40, 0x12, 0x40, 0x40, 0x10, 0x00,\ -+/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ -+/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\ -+/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ -+/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ -+/* 152 */ 0x00, 0x00, 0x10, 0x00, 0x06, 0x10, 0x00, 0x04,\ -+/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\ -+/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\ -+/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} -+ -+/* The resolve3P2Values() routine is able to run faster if it knows - ** the value of the largest JUMP opcode. The smaller the maximum - ** JUMP opcode the better, so the mkopcodeh.tcl script that - ** generated this include file strives to group all JUMP opcodes - ** together near the beginning of the list. - */ --#define SQLITE_MX_JUMP_OPCODE 62 /* Maximum JUMP opcode */ -+#define SQLITE_MX_JUMP_OPCODE 64 /* Maximum JUMP opcode */ - - /************** End of opcodes.h *********************************************/ - /************** Continuing where we left off in vdbe.h ***********************/ -@@ -15790,7 +17290,7 @@ typedef struct VdbeOpList VdbeOpList; - ** Additional non-public SQLITE_PREPARE_* flags - */ - #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ --#define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */ -+#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ - - /* - ** Prototypes for the VDBE interface. See comments on the implementation -@@ -15819,19 +17319,27 @@ SQLITE_PRIVATE void sqlite3VdbeVerifyNoResultRow(Vdbe *p); - #endif - #if defined(SQLITE_DEBUG) - SQLITE_PRIVATE void sqlite3VdbeVerifyAbortable(Vdbe *p, int); -+SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int); - #else - # define sqlite3VdbeVerifyAbortable(A,B) -+# define sqlite3VdbeNoJumpsOutsideSubrtn(A,B,C,D) - #endif - SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); - #ifndef SQLITE_OMIT_EXPLAIN --SQLITE_PRIVATE void sqlite3VdbeExplain(Parse*,u8,const char*,...); -+SQLITE_PRIVATE int sqlite3VdbeExplain(Parse*,u8,const char*,...); - SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse*); - SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*); - # define ExplainQueryPlan(P) sqlite3VdbeExplain P -+# ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+# define ExplainQueryPlan2(V,P) (V = sqlite3VdbeExplain P) -+# else -+# define ExplainQueryPlan2(V,P) ExplainQueryPlan(P) -+# endif - # define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P) - # define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P) - #else - # define ExplainQueryPlan(P) -+# define ExplainQueryPlan2(V,P) - # define ExplainQueryPlanPop(P) - # define ExplainQueryPlanParent(P) 0 - # define sqlite3ExplainBreakpoint(A,B) /*no-op*/ -@@ -15841,12 +17349,13 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char*,const char*); - #else - # define sqlite3ExplainBreakpoint(A,B) /*no-op*/ - #endif --SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); -+SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*, int, char*, u16); - SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8); - SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); - SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); - SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); - SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u16 P5); -+SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe*, int); - SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); - SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe*, int addr); - SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr); -@@ -15861,11 +17370,11 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type); - SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); - SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); - SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); -+SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe*); - SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse*); - SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); - SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*); - SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); --SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*); - SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*); - SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); - SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); -@@ -15907,13 +17416,15 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); - SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); - SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*); - -+SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val); -+ - SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); - #ifdef SQLITE_ENABLE_BYTECODE_VTAB - SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); - #endif - --/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on --** each VDBE opcode. -+/* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra -+** comments on each VDBE opcode. - ** - ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op - ** comments in VDBE programs that show key decision points in the code -@@ -15939,7 +17450,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); - ** The VdbeCoverage macros are used to set a coverage testing point - ** for VDBE branch instructions. The coverage testing points are line - ** numbers in the sqlite3.c source file. VDBE branch coverage testing --** only works with an amalagmation build. That's ok since a VDBE branch -+** only works with an amalgamation build. That's ok since a VDBE branch - ** coverage build designed for testing the test suite only. No application - ** should ever ship with VDBE branch coverage measuring turned on. - ** -@@ -15957,7 +17468,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); - ** // NULL option is not possible - ** - ** VdbeCoverageEqNe(v) // Previous OP_Jump is only interested --** // in distingishing equal and not-equal. -+** // in distinguishing equal and not-equal. - ** - ** Every VDBE branch operation must be tagged with one of the macros above. - ** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and -@@ -15967,7 +17478,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); - ** During testing, the test application will invoke - ** sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE,...) to set a callback - ** routine that is invoked as each bytecode branch is taken. The callback --** contains the sqlite3.c source line number ov the VdbeCoverage macro and -+** contains the sqlite3.c source line number of the VdbeCoverage macro and - ** flags to indicate whether or not the branch was taken. The test application - ** is responsible for keeping track of this and reporting byte-code branches - ** that are never taken. -@@ -16003,14 +17514,22 @@ SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int); - - #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - SQLITE_PRIVATE void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); -+SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(Vdbe*, int, int, int); -+SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int); - #else --# define sqlite3VdbeScanStatus(a,b,c,d,e) -+# define sqlite3VdbeScanStatus(a,b,c,d,e,f) -+# define sqlite3VdbeScanStatusRange(a,b,c,d) -+# define sqlite3VdbeScanStatusCounters(a,b,c,d) - #endif - - #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) - SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); - #endif - -+#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) -+SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr); -+#endif -+ - #endif /* SQLITE_VDBE_H */ - - /************** End of vdbe.h ************************************************/ -@@ -16059,7 +17578,7 @@ struct PgHdr { - ** private to pcache.c and should not be accessed by other modules. - ** pCache is grouped with the public elements for efficiency. - */ -- i16 nRef; /* Number of users of this page */ -+ i64 nRef; /* Number of users of this page */ - PgHdr *pDirtyNext; /* Next element in list of dirty pages */ - PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ - /* NB: pDirtyNext and pDirtyPrev are undefined if the -@@ -16140,12 +17659,12 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); - SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); - - /* Return the total number of outstanding page references */ --SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); -+SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache*); - - /* Increment the reference count of an existing page */ - SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); - --SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); -+SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr*); - - /* Return the total number of pages stored in the cache */ - SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); -@@ -16210,284 +17729,6 @@ SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache); - - /************** End of pcache.h **********************************************/ - /************** Continuing where we left off in sqliteInt.h ******************/ --/************** Include os.h in the middle of sqliteInt.h ********************/ --/************** Begin file os.h **********************************************/ --/* --** 2001 September 16 --** --** 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 header file (together with is companion C source-code file --** "os.c") attempt to abstract the underlying operating system so that --** the SQLite library will work on both POSIX and windows systems. --** --** This header file is #include-ed by sqliteInt.h and thus ends up --** being included by every source file. --*/ --#ifndef _SQLITE_OS_H_ --#define _SQLITE_OS_H_ -- --/* --** Attempt to automatically detect the operating system and setup the --** necessary pre-processor macros for it. --*/ --/************** Include os_setup.h in the middle of os.h *********************/ --/************** Begin file os_setup.h ****************************************/ --/* --** 2013 November 25 --** --** 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 contains pre-processor directives related to operating system --** detection and/or setup. --*/ --#ifndef SQLITE_OS_SETUP_H --#define SQLITE_OS_SETUP_H -- --/* --** Figure out if we are dealing with Unix, Windows, or some other operating --** system. --** --** After the following block of preprocess macros, all of SQLITE_OS_UNIX, --** SQLITE_OS_WIN, and SQLITE_OS_OTHER will defined to either 1 or 0. One of --** the three will be 1. The other two will be 0. --*/ --#if defined(SQLITE_OS_OTHER) --# if SQLITE_OS_OTHER==1 --# undef SQLITE_OS_UNIX --# define SQLITE_OS_UNIX 0 --# undef SQLITE_OS_WIN --# define SQLITE_OS_WIN 0 --# else --# undef SQLITE_OS_OTHER --# endif --#endif --#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) --# define SQLITE_OS_OTHER 0 --# ifndef SQLITE_OS_WIN --# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \ -- defined(__MINGW32__) || defined(__BORLANDC__) --# define SQLITE_OS_WIN 1 --# define SQLITE_OS_UNIX 0 --# else --# define SQLITE_OS_WIN 0 --# define SQLITE_OS_UNIX 1 --# endif --# else --# define SQLITE_OS_UNIX 0 --# endif --#else --# ifndef SQLITE_OS_WIN --# define SQLITE_OS_WIN 0 --# endif --#endif -- --#endif /* SQLITE_OS_SETUP_H */ -- --/************** End of os_setup.h ********************************************/ --/************** Continuing where we left off in os.h *************************/ -- --/* If the SET_FULLSYNC macro is not defined above, then make it --** a no-op --*/ --#ifndef SET_FULLSYNC --# define SET_FULLSYNC(x,y) --#endif -- --/* --** The default size of a disk sector --*/ --#ifndef SQLITE_DEFAULT_SECTOR_SIZE --# define SQLITE_DEFAULT_SECTOR_SIZE 4096 --#endif -- --/* --** Temporary files are named starting with this prefix followed by 16 random --** alphanumeric characters, and no file extension. They are stored in the --** OS's standard temporary file directory, and are deleted prior to exit. --** If sqlite is being embedded in another program, you may wish to change the --** prefix to reflect your program's name, so that if your program exits --** prematurely, old temporary files can be easily identified. This can be done --** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. --** --** 2006-10-31: The default prefix used to be "sqlite_". But then --** Mcafee started using SQLite in their anti-virus product and it --** started putting files with the "sqlite" name in the c:/temp folder. --** This annoyed many windows users. Those users would then do a --** Google search for "sqlite", find the telephone numbers of the --** developers and call to wake them up at night and complain. --** For this reason, the default name prefix is changed to be "sqlite" --** spelled backwards. So the temp files are still identified, but --** anybody smart enough to figure out the code is also likely smart --** enough to know that calling the developer will not help get rid --** of the file. --*/ --#ifndef SQLITE_TEMP_FILE_PREFIX --# define SQLITE_TEMP_FILE_PREFIX "etilqs_" --#endif -- --/* --** The following values may be passed as the second argument to --** sqlite3OsLock(). The various locks exhibit the following semantics: --** --** SHARED: Any number of processes may hold a SHARED lock simultaneously. --** RESERVED: A single process may hold a RESERVED lock on a file at --** any time. Other processes may hold and obtain new SHARED locks. --** PENDING: A single process may hold a PENDING lock on a file at --** any one time. Existing SHARED locks may persist, but no new --** SHARED locks may be obtained by other processes. --** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. --** --** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a --** process that requests an EXCLUSIVE lock may actually obtain a PENDING --** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to --** sqlite3OsLock(). --*/ --#define NO_LOCK 0 --#define SHARED_LOCK 1 --#define RESERVED_LOCK 2 --#define PENDING_LOCK 3 --#define EXCLUSIVE_LOCK 4 -- --/* --** File Locking Notes: (Mostly about windows but also some info for Unix) --** --** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because --** those functions are not available. So we use only LockFile() and --** UnlockFile(). --** --** LockFile() prevents not just writing but also reading by other processes. --** A SHARED_LOCK is obtained by locking a single randomly-chosen --** byte out of a specific range of bytes. The lock byte is obtained at --** random so two separate readers can probably access the file at the --** same time, unless they are unlucky and choose the same lock byte. --** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. --** There can only be one writer. A RESERVED_LOCK is obtained by locking --** a single byte of the file that is designated as the reserved lock byte. --** A PENDING_LOCK is obtained by locking a designated byte different from --** the RESERVED_LOCK byte. --** --** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, --** which means we can use reader/writer locks. When reader/writer locks --** are used, the lock is placed on the same range of bytes that is used --** for probabilistic locking in Win95/98/ME. Hence, the locking scheme --** will support two or more Win95 readers or two or more WinNT readers. --** But a single Win95 reader will lock out all WinNT readers and a single --** WinNT reader will lock out all other Win95 readers. --** --** The following #defines specify the range of bytes used for locking. --** SHARED_SIZE is the number of bytes available in the pool from which --** a random byte is selected for a shared lock. The pool of bytes for --** shared locks begins at SHARED_FIRST. --** --** The same locking strategy and --** byte ranges are used for Unix. This leaves open the possibility of having --** clients on win95, winNT, and unix all talking to the same shared file --** and all locking correctly. To do so would require that samba (or whatever --** tool is being used for file sharing) implements locks correctly between --** windows and unix. I'm guessing that isn't likely to happen, but by --** using the same locking range we are at least open to the possibility. --** --** Locking in windows is manditory. For this reason, we cannot store --** actual data in the bytes used for locking. The pager never allocates --** the pages involved in locking therefore. SHARED_SIZE is selected so --** that all locks will fit on a single page even at the minimum page size. --** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE --** is set high so that we don't have to allocate an unused page except --** for very large databases. But one should test the page skipping logic --** by setting PENDING_BYTE low and running the entire regression suite. --** --** Changing the value of PENDING_BYTE results in a subtly incompatible --** file format. Depending on how it is changed, you might not notice --** the incompatibility right away, even running a full regression test. --** The default location of PENDING_BYTE is the first byte past the --** 1GB boundary. --** --*/ --#ifdef SQLITE_OMIT_WSD --# define PENDING_BYTE (0x40000000) --#else --# define PENDING_BYTE sqlite3PendingByte --#endif --#define RESERVED_BYTE (PENDING_BYTE+1) --#define SHARED_FIRST (PENDING_BYTE+2) --#define SHARED_SIZE 510 -- --/* --** Wrapper around OS specific sqlite3_os_init() function. --*/ --SQLITE_PRIVATE int sqlite3OsInit(void); -- --/* --** Functions for accessing sqlite3_file methods --*/ --SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*); --SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); --SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); --SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); --SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); --SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); --SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); --SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); --SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); --SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); --SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); --#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 --SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); --SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); --#ifndef SQLITE_OMIT_WAL --SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); --SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); --SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); --SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); --#endif /* SQLITE_OMIT_WAL */ --SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **); --SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *); -- -- --/* --** Functions for accessing sqlite3_vfs methods --*/ --SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); --SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); --SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); --SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); --#ifndef SQLITE_OMIT_LOAD_EXTENSION --SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); --SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); --SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); --SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); --#endif /* SQLITE_OMIT_LOAD_EXTENSION */ --SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); --SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); --SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*); --SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); -- --/* --** Convenience functions for opening and closing files using --** sqlite3_malloc() to obtain space for the file-handle structure. --*/ --SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); --SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *); -- --#endif /* _SQLITE_OS_H_ */ -- --/************** End of os.h **************************************************/ --/************** Continuing where we left off in sqliteInt.h ******************/ - /************** Include mutex.h in the middle of sqliteInt.h *****************/ - /************** Begin file mutex.h *******************************************/ - /* -@@ -16576,7 +17817,7 @@ SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); - /* - ** Default synchronous levels. - ** --** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ -+** Note that (for historical reasons) the PAGER_SYNCHRONOUS_* macros differ - ** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1. - ** - ** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS -@@ -16615,7 +17856,7 @@ struct Db { - ** An instance of the following structure stores a database schema. - ** - ** Most Schema objects are associated with a Btree. The exception is --** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. -+** the Schema for the TEMP database (sqlite3.aDb[1]) which is free-standing. - ** In shared cache mode, a single Schema object can be shared by multiple - ** Btrees that refer to the same underlying BtShared object. - ** -@@ -16726,13 +17967,14 @@ struct Lookaside { - LookasideSlot *pInit; /* List of buffers not previously used */ - LookasideSlot *pFree; /* List of available buffers */ - #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE -- LookasideSlot *pSmallInit; /* List of small buffers not prediously used */ -+ LookasideSlot *pSmallInit; /* List of small buffers not previously used */ - LookasideSlot *pSmallFree; /* List of available small buffers */ - void *pMiddle; /* First byte past end of full-size buffers and - ** the first byte of LOOKASIDE_SMALL buffers */ - #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ - void *pStart; /* First byte of available memory space */ - void *pEnd; /* First byte past end of available space */ -+ void *pTrueEnd; /* True value of pEnd, when db->pnBytesFreed!=0 */ - }; - struct LookasideSlot { - LookasideSlot *pNext; /* Next buffer in the list of free buffers */ -@@ -16742,7 +17984,7 @@ struct LookasideSlot { - #define EnableLookaside db->lookaside.bDisable--;\ - db->lookaside.sz=db->lookaside.bDisable?0:db->lookaside.szTrue - --/* Size of the smaller allocations in two-size lookside */ -+/* Size of the smaller allocations in two-size lookaside */ - #ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE - # define LOOKASIDE_SMALL 0 - #else -@@ -16763,43 +18005,11 @@ struct FuncDefHash { - }; - #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) - --#ifdef SQLITE_USER_AUTHENTICATION --/* --** Information held in the "sqlite3" database connection object and used --** to manage user authentication. --*/ --typedef struct sqlite3_userauth sqlite3_userauth; --struct sqlite3_userauth { -- u8 authLevel; /* Current authentication level */ -- int nAuthPW; /* Size of the zAuthPW in bytes */ -- char *zAuthPW; /* Password used to authenticate */ -- char *zAuthUser; /* User name used to authenticate */ --}; -- --/* Allowed values for sqlite3_userauth.authLevel */ --#define UAUTH_Unknown 0 /* Authentication not yet checked */ --#define UAUTH_Fail 1 /* User authentication failed */ --#define UAUTH_User 2 /* Authenticated as a normal user */ --#define UAUTH_Admin 3 /* Authenticated as an administrator */ -- --/* Functions used only by user authorization logic */ --SQLITE_PRIVATE int sqlite3UserAuthTable(const char*); --SQLITE_PRIVATE int sqlite3UserAuthCheckLogin(sqlite3*,const char*,u8*); --SQLITE_PRIVATE void sqlite3UserAuthInit(sqlite3*); --SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); -- --#endif /* SQLITE_USER_AUTHENTICATION */ -- - /* - ** typedef for the authorization callback function. - */ --#ifdef SQLITE_USER_AUTHENTICATION -- typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, -- const char*, const char*); --#else -- typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, -- const char*); --#endif -+typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*, -+ const char*); - - #ifndef SQLITE_OMIT_DEPRECATED - /* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing -@@ -16813,6 +18023,11 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); - #endif /* SQLITE_OMIT_DEPRECATED */ - #define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */ - -+/* -+** Maximum number of sqlite3.aDb[] entries. This is the number of attached -+** databases plus 2 for "main" and "temp". -+*/ -+#define SQLITE_MAX_DB (SQLITE_MAX_ATTACHED+2) - - /* - ** Each database connection is an instance of the following structure. -@@ -16831,9 +18046,10 @@ struct sqlite3 { - u32 nSchemaLock; /* Do not reset the schema when non-zero */ - unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ - int errCode; /* Most recent error code (SQLITE_*) */ -+ int errByteOffset; /* Byte offset of error in SQL statement */ - int errMask; /* & result codes with this before returning */ - int iSysErrno; /* Errno value from last system error */ -- u16 dbOptFlags; /* Flags to enable/disable optimizations */ -+ u32 dbOptFlags; /* Flags to enable/disable optimizations */ - u8 enc; /* Text encoding */ - u8 autoCommit; /* The auto-commit flag. */ - u8 temp_store; /* 1: file 2: memory 0: default */ -@@ -16847,10 +18063,10 @@ struct sqlite3 { - u8 mTrace; /* zero or more SQLITE_TRACE flags */ - u8 noSharedCache; /* True if no shared-cache backends */ - u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ -+ u8 eOpenState; /* Current condition of the connection */ - int nextPagesize; /* Pagesize after VACUUM if >0 */ -- u32 magic; /* Magic number for detect library misuse */ -- int nChange; /* Value returned by sqlite3_changes() */ -- int nTotalChange; /* Value returned by sqlite3_total_changes() */ -+ i64 nChange; /* Value returned by sqlite3_changes() */ -+ i64 nTotalChange; /* Value returned by sqlite3_total_changes() */ - int aLimit[SQLITE_N_LIMIT]; /* Limits */ - int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ - struct sqlite3InitInfo { /* Information used during initialization */ -@@ -16860,7 +18076,7 @@ struct sqlite3 { - unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ - unsigned imposterTable : 1; /* Building an imposter table */ - unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ -- char **azInit; /* "type", "name", and "tbl_name" columns */ -+ const char **azInit; /* "type", "name", and "tbl_name" columns */ - } init; - int nVdbeActive; /* Number of VDBEs currently running */ - int nVdbeRead; /* Number of active VDBEs that read or write */ -@@ -16870,10 +18086,10 @@ struct sqlite3 { - int nExtension; /* Number of loaded extensions */ - void **aExtension; /* Array of shared library handles */ - union { -- void (*xLegacy)(void*,const char*); /* Legacy trace function */ -- int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ -+ void (*xLegacy)(void*,const char*); /* mTrace==SQLITE_TRACE_LEGACY */ -+ int (*xV2)(u32,void*,void*,void*); /* All other mTrace values */ - } trace; -- void *pTraceArg; /* Argument to the trace function */ -+ void *pTraceArg; /* Argument to the trace function */ - #ifndef SQLITE_OMIT_DEPRECATED - void (*xProfile)(void*,const char*,u64); /* Profiling function */ - void *pProfileArg; /* Argument to profile function */ -@@ -16884,6 +18100,9 @@ struct sqlite3 { - void (*xRollbackCallback)(void*); /* Invoked at every commit. */ - void *pUpdateArg; - void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); -+ void *pAutovacPagesArg; /* Client argument to autovac_pages */ -+ void (*xAutovacDestr)(void*); /* Destructor for pAutovacPAgesArg */ -+ unsigned int (*xAutovacPages)(void*,const char*,u32,u32,u32); - Parse *pParse; /* Current parse */ - #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - void *pPreUpdateArg; /* First argument to xPreUpdateCallback */ -@@ -16928,11 +18147,16 @@ struct sqlite3 { - Savepoint *pSavepoint; /* List of active savepoints */ - int nAnalysisLimit; /* Number of index rows to ANALYZE */ - int busyTimeout; /* Busy handler timeout, in msec */ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ int setlkTimeout; /* Blocking lock timeout, in msec. -1 -> inf. */ -+ int setlkFlags; /* Flags passed to setlk_timeout() */ -+#endif - int nSavepoint; /* Number of non-transaction savepoints */ - int nStatement; /* Number of nested statement-transactions */ - i64 nDeferredCons; /* Net deferred constraints this transaction. */ - i64 nDeferredImmCons; /* Net deferred immediate constraints */ - int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ -+ DbClientData *pDbData; /* sqlite3_set_clientdata() content */ - #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - /* The following variables are all protected by the STATIC_MAIN - ** mutex, not by sqlite3.mutex. They are used by code in notify.c. -@@ -16950,9 +18174,6 @@ struct sqlite3 { - void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ - sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ - #endif --#ifdef SQLITE_USER_AUTHENTICATION -- sqlite3_userauth auth; /* User authentication information */ --#endif - }; - - /* -@@ -16988,7 +18209,7 @@ struct sqlite3 { - #define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ - /* result set is empty */ - #define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */ --#define SQLITE_ReadUncommit 0x00000400 /* READ UNCOMMITTED in shared-cache */ -+#define SQLITE_StmtScanStatus 0x00000400 /* Enable stmt_scanstats() counters */ - #define SQLITE_NoCkptOnClose 0x00000800 /* No checkpoint on close()/DETACH */ - #define SQLITE_ReverseOrder 0x00001000 /* Reverse unordered SELECTs */ - #define SQLITE_RecTriggers 0x00002000 /* Enable recursive triggers */ -@@ -17013,6 +18234,12 @@ struct sqlite3 { - #define SQLITE_CountRows HI(0x00001) /* Count rows changed by INSERT, */ - /* DELETE, or UPDATE and return */ - /* the count using a callback. */ -+#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ -+#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */ -+#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */ -+#define SQLITE_AttachCreate HI(0x00010) /* ATTACH allowed to create new dbs */ -+#define SQLITE_AttachWrite HI(0x00020) /* ATTACH allowed to open for write */ -+#define SQLITE_Comments HI(0x00040) /* Enable SQL comments */ - - /* Flags used only if debugging */ - #ifdef SQLITE_DEBUG -@@ -17040,24 +18267,40 @@ struct sqlite3 { - ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to - ** selectively disable various optimizations. - */ --#define SQLITE_QueryFlattener 0x0001 /* Query flattening */ --#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */ --#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ --#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ --#define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */ --#define SQLITE_CoverIdxScan 0x0020 /* Covering index scans */ --#define SQLITE_OrderByIdxJoin 0x0040 /* ORDER BY of joins via index */ --#define SQLITE_Transitive 0x0080 /* Transitive constraints */ --#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */ --#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */ --#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */ --#define SQLITE_Stat4 0x0800 /* Use STAT4 data */ -- /* TH3 expects the Stat4 ^^^^^^ value to be 0x0800. Don't change it */ --#define SQLITE_PushDown 0x1000 /* The push-down optimization */ --#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */ --#define SQLITE_SkipScan 0x4000 /* Skip-scans */ --#define SQLITE_PropagateConst 0x8000 /* The constant propagation opt */ --#define SQLITE_AllOpts 0xffff /* All optimizations */ -+#define SQLITE_QueryFlattener 0x00000001 /* Query flattening */ -+#define SQLITE_WindowFunc 0x00000002 /* Use xInverse for window functions */ -+#define SQLITE_GroupByOrder 0x00000004 /* GROUPBY cover of ORDERBY */ -+#define SQLITE_FactorOutConst 0x00000008 /* Constant factoring */ -+#define SQLITE_DistinctOpt 0x00000010 /* DISTINCT using indexes */ -+#define SQLITE_CoverIdxScan 0x00000020 /* Covering index scans */ -+#define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */ -+#define SQLITE_Transitive 0x00000080 /* Transitive constraints */ -+#define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */ -+#define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */ -+#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */ -+#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */ -+ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */ -+#define SQLITE_PushDown 0x00001000 /* WHERE-clause push-down opt */ -+#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */ -+#define SQLITE_SkipScan 0x00004000 /* Skip-scans */ -+#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */ -+#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */ -+#define SQLITE_SeekScan 0x00020000 /* The OP_SeekScan optimization */ -+#define SQLITE_OmitOrderBy 0x00040000 /* Omit pointless ORDER BY */ -+ /* TH3 expects this value ^^^^^^^^^^ to be 0x40000. Coordinate any change */ -+#define SQLITE_BloomFilter 0x00080000 /* Use a Bloom filter on searches */ -+#define SQLITE_BloomPulldown 0x00100000 /* Run Bloom filters early */ -+#define SQLITE_BalancedMerge 0x00200000 /* Balance multi-way merges */ -+#define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */ -+#define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ -+ /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ -+#define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ -+#define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ -+#define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ -+#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ -+#define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */ -+#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */ -+#define SQLITE_AllOpts 0xffffffff /* All optimizations */ - - /* - ** Macros for testing whether or not optimizations are enabled or disabled. -@@ -17071,17 +18314,16 @@ struct sqlite3 { - */ - #define ConstFactorOk(P) ((P)->okConstFactor) - --/* --** Possible values for the sqlite.magic field. --** The numbers are obtained at random and have no special meaning, other --** than being distinct from one another. -+/* Possible values for the sqlite3.eOpenState field. -+** The numbers are randomly selected such that a minimum of three bits must -+** change to convert any number to another or to zero - */ --#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ --#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ --#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ --#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ --#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ --#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ -+#define SQLITE_STATE_OPEN 0x76 /* Database is open */ -+#define SQLITE_STATE_CLOSED 0xce /* Database is closed */ -+#define SQLITE_STATE_SICK 0xba /* Error and awaiting close */ -+#define SQLITE_STATE_BUSY 0x6d /* Database currently in use */ -+#define SQLITE_STATE_ERROR 0xd5 /* An SQLITE_MISUSE error occurred */ -+#define SQLITE_STATE_ZOMBIE 0xa7 /* Close with last statement close */ - - /* - ** Each SQL function is defined by an instance of the following -@@ -17094,7 +18336,7 @@ struct sqlite3 { - ** field is used by per-connection app-def functions. - */ - struct FuncDef { -- i8 nArg; /* Number of arguments. -1 means unlimited */ -+ i16 nArg; /* Number of arguments. -1 means unlimited */ - u32 funcFlags; /* Some combination of SQLITE_FUNC_* */ - void *pUserData; /* User data parameter */ - FuncDef *pNext; /* Next function with same name */ -@@ -17106,7 +18348,7 @@ struct FuncDef { - union { - FuncDef *pHash; /* Next with a different name but the same hash */ - FuncDestructor *pDestructor; /* Reference counted destructor function */ -- } u; -+ } u; /* pHash if SQLITE_FUNC_BUILTIN, pDestructor otherwise */ - }; - - /* -@@ -17136,13 +18378,21 @@ struct FuncDestructor { - ** are assert() statements in the code to verify this. - ** - ** Value constraints (enforced via assert()): --** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg --** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG --** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG --** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API --** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API --** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -+** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg -+** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd -+** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG -+** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG -+** SQLITE_FUNC_BYTELEN == OPFLAG_BYTELENARG -+** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API -+** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API -+** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!! - ** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API -+** -+** Note that even though SQLITE_FUNC_UNSAFE and SQLITE_INNOCUOUS have the -+** same bit value, their meanings are inverted. SQLITE_FUNC_UNSAFE is -+** used internally and if set means that the function has side effects. -+** SQLITE_INNOCUOUS is used by application code and means "not unsafe". -+** See multiple instances of tag-20230109-1. - */ - #define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ - #define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */ -@@ -17151,6 +18401,7 @@ struct FuncDestructor { - #define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ - #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ - #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ -+#define SQLITE_FUNC_BYTELEN 0x00c0 /* Built-in octet_length() function */ - #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ - /* 0x0200 -- available for reuse */ - #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ -@@ -17159,13 +18410,16 @@ struct FuncDestructor { - #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a - ** single query - might change over time */ - #define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */ --#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ -+#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */ - #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ - #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ - #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ --#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */ -+/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */ - #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ - #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ -+#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ -+/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */ -+#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ - - /* Identifier numbers for each in-line function */ - #define INLINEFUNC_coalesce 0 -@@ -17174,6 +18428,7 @@ struct FuncDestructor { - #define INLINEFUNC_expr_compare 3 - #define INLINEFUNC_affinity 4 - #define INLINEFUNC_iif 5 -+#define INLINEFUNC_sqlite_offset 6 - #define INLINEFUNC_unlikely 99 /* Default case */ - - /* -@@ -17213,6 +18468,9 @@ struct FuncDestructor { - ** a single query. The iArg is ignored. The user-data is always set - ** to a NULL pointer. The bNC parameter is not used. - ** -+** MFUNCTION(zName, nArg, xPtr, xFunc) -+** For math-library functions. xPtr is an arbitrary pointer. -+** - ** PURE_DATE(zName, nArg, iArg, bNC, xFunc) - ** Used for "pure" date/time functions, this macro is like DFUNCTION - ** except that it does set the SQLITE_FUNC_CONSTANT flags. iArg is -@@ -17225,7 +18483,7 @@ struct FuncDestructor { - ** are interpreted in the same way as the first 4 parameters to - ** FUNCTION(). - ** --** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse) -+** WAGGREGATE(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 -@@ -17240,41 +18498,56 @@ struct FuncDestructor { - ** parameter. - */ - #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ -- {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ -+ {nArg, SQLITE_FUNC_BUILTIN|\ -+ SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - 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), \ -+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } - #define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \ -- {nArg, SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ -+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } -+#define MFUNCTION(zName, nArg, xPtr, xFunc) \ -+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ -+ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } -+#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \ -+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ -+ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\ -+ ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ -+ SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} } - #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ -- {nArg, SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ -+ {nArg, SQLITE_FUNC_BUILTIN|\ -+ SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ - SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } - #define TEST_FUNC(zName, nArg, iArg, mFlags) \ -- {nArg, SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ -+ {nArg, SQLITE_FUNC_BUILTIN|\ -+ SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \ - SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ - SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} } - #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ -- {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ -+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ - 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, \ -+ {nArg, SQLITE_FUNC_BUILTIN|\ -+ SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ - (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,\ -+ {nArg, SQLITE_FUNC_BUILTIN|\ -+ SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ - 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), \ -+ {nArg, SQLITE_FUNC_BUILTIN|\ -+ SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - pArg, 0, xFunc, 0, 0, 0, #zName, } - #define LIKEFUNC(zName, nArg, arg, flags) \ -- {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ -+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ - (void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} } - #define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \ -- {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ -+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ - SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}} - #define INTERNAL_FUNCTION(zName, nArg, xFunc) \ -- {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ -+ {nArg, SQLITE_FUNC_BUILTIN|\ -+ SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ - 0, 0, xFunc, 0, 0, 0, #zName, {0} } - - -@@ -17330,19 +18603,48 @@ struct Module { - ** or equal to the table column index. It is - ** equal if and only if there are no VIRTUAL - ** columns to the left. -+** -+** Notes on zCnName: -+** The zCnName field stores the name of the column, the datatype of the -+** column, and the collating sequence for the column, in that order, all in -+** a single allocation. Each string is 0x00 terminated. The datatype -+** is only included if the COLFLAG_HASTYPE bit of colFlags is set and the -+** collating sequence name is only included if the COLFLAG_HASCOLL bit is -+** set. - */ - struct Column { -- char *zName; /* Name of this column, \000, then the type */ -- Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */ -- char *zColl; /* Collating sequence. If NULL, use the default */ -- u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ -- char affinity; /* One of the SQLITE_AFF_... values */ -- u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ -- u8 hName; /* Column name hash for faster lookup */ -- u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ -+ char *zCnName; /* Name of this column */ -+ unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */ -+ unsigned eCType :4; /* One of the standard types */ -+ char affinity; /* One of the SQLITE_AFF_... values */ -+ u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */ -+ u8 hName; /* Column name hash for faster lookup */ -+ u16 iDflt; /* 1-based index of DEFAULT. 0 means "none" */ -+ u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ - }; - --/* Allowed values for Column.colFlags: -+/* Allowed values for Column.eCType. -+** -+** Values must match entries in the global constant arrays -+** sqlite3StdTypeLen[] and sqlite3StdType[]. Each value is one more -+** than the offset into these arrays for the corresponding name. -+** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. -+*/ -+#define COLTYPE_CUSTOM 0 /* Type appended to zName */ -+#define COLTYPE_ANY 1 -+#define COLTYPE_BLOB 2 -+#define COLTYPE_INT 3 -+#define COLTYPE_INTEGER 4 -+#define COLTYPE_REAL 5 -+#define COLTYPE_TEXT 6 -+#define SQLITE_N_STDTYPE 6 /* Number of standard types */ -+ -+/* Allowed values for Column.colFlags. -+** -+** Constraints: -+** TF_HasVirtual == COLFLAG_VIRTUAL -+** TF_HasStored == COLFLAG_STORED -+** TF_HasHidden == COLFLAG_HIDDEN - */ - #define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ - #define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ -@@ -17353,6 +18655,8 @@ struct Column { - #define COLFLAG_STORED 0x0040 /* GENERATED ALWAYS AS ... STORED */ - #define COLFLAG_NOTAVAIL 0x0080 /* STORED column not yet calculated */ - #define COLFLAG_BUSY 0x0100 /* Blocks recursion on GENERATED columns */ -+#define COLFLAG_HASCOLL 0x0200 /* Has collating sequence name in zCnName */ -+#define COLFLAG_NOEXPAND 0x0400 /* Omit this column when expanding "*" */ - #define COLFLAG_GENERATED 0x0060 /* Combo: _STORED, _VIRTUAL */ - #define COLFLAG_NOINSERT 0x0062 /* Combo: _HIDDEN, _STORED, _VIRTUAL */ - -@@ -17400,6 +18704,8 @@ struct CollSeq { - #define SQLITE_AFF_NUMERIC 0x43 /* 'C' */ - #define SQLITE_AFF_INTEGER 0x44 /* 'D' */ - #define SQLITE_AFF_REAL 0x45 /* 'E' */ -+#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */ -+#define SQLITE_AFF_DEFER 0x58 /* 'X' - defer computation until later */ - - #define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) - -@@ -17418,9 +18724,7 @@ struct CollSeq { - ** operator is NULL. It is added to certain comparison operators to - ** prove that the operands are always NOT NULL. - */ --#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */ - #define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */ --#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */ - #define SQLITE_NULLEQ 0x80 /* NULL=NULL */ - #define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */ - -@@ -17472,6 +18776,7 @@ struct VTable { - sqlite3_vtab *pVtab; /* Pointer to vtab instance */ - int nRef; /* Number of pointers to this structure */ - u8 bConstraint; /* True if constraints are supported */ -+ u8 bAllSchemas; /* True if might use any attached schema */ - u8 eVtabRisk; /* Riskiness of allowing hacker access */ - int iSavepoint; /* Depth of the SAVEPOINT stack */ - VTable *pNext; /* Next in linked list (see above) */ -@@ -17484,15 +18789,13 @@ struct VTable { - #define SQLITE_VTABRISK_High 2 - - /* --** The schema for each SQL table and view is represented in memory --** by an instance of the following structure. -+** The schema for each SQL table, virtual table, and view is represented -+** in memory by an instance of the following structure. - */ - struct Table { - char *zName; /* Name of the table or view */ - Column *aCol; /* Information about each column */ - Index *pIndex; /* List of SQL indexes on this table. */ -- Select *pSelect; /* NULL for tables. Points to definition if a view. */ -- FKey *pFKey; /* Linked list of all foreign keys in this table */ - char *zColAff; /* String defining the affinity of each column */ - ExprList *pCheck; /* All CHECK constraints */ - /* ... also used as column name list in a VIEW */ -@@ -17508,17 +18811,26 @@ struct Table { - LogEst costMult; /* Cost multiplier for using this table */ - #endif - u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ --#ifndef SQLITE_OMIT_ALTERTABLE -- int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ --#endif --#ifndef SQLITE_OMIT_VIRTUALTABLE -- int nModuleArg; /* Number of arguments to the module */ -- char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */ -- VTable *pVTable; /* List of VTable objects. */ --#endif -- Trigger *pTrigger; /* List of triggers stored in pSchema */ -+ u8 eTabType; /* 0: normal, 1: virtual, 2: view */ -+ union { -+ struct { /* Used by ordinary tables: */ -+ int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ -+ FKey *pFKey; /* Linked list of all foreign keys in this table */ -+ ExprList *pDfltList; /* DEFAULT clauses on various columns. -+ ** Or the AS clause for generated columns. */ -+ } tab; -+ struct { /* Used by views: */ -+ Select *pSelect; /* View definition */ -+ } view; -+ struct { /* Used by virtual tables only: */ -+ int nArg; /* Number of arguments to the module */ -+ char **azArg; /* 0: module 1: schema 2: vtab name 3...: args */ -+ VTable *p; /* List of VTable objects. */ -+ } vtab; -+ } u; -+ Trigger *pTrigger; /* List of triggers on this object */ - Schema *pSchema; /* Schema that contains this table */ -- Table *pNextZombie; /* Next on the Parse.pZombieTab list */ -+ u8 aHx[16]; /* Column aHt[K%sizeof(aHt)] might have hash K */ - }; - - /* -@@ -17532,24 +18844,38 @@ struct Table { - ** - ** Constraints: - ** --** TF_HasVirtual == COLFLAG_Virtual --** TF_HasStored == COLFLAG_Stored --*/ --#define TF_Readonly 0x0001 /* Read-only system table */ --#define TF_Ephemeral 0x0002 /* An ephemeral table */ --#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */ --#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */ --#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */ --#define TF_HasVirtual 0x0020 /* Has one or more VIRTUAL columns */ --#define TF_HasStored 0x0040 /* Has one or more STORED columns */ --#define TF_HasGenerated 0x0060 /* Combo: HasVirtual + HasStored */ --#define TF_WithoutRowid 0x0080 /* No rowid. PRIMARY KEY is the key */ --#define TF_StatsUsed 0x0100 /* Query planner decisions affected by -- ** Index.aiRowLogEst[] values */ --#define TF_NoVisibleRowid 0x0200 /* No user-visible "rowid" column */ --#define TF_OOOHidden 0x0400 /* Out-of-Order hidden columns */ --#define TF_HasNotNull 0x0800 /* Contains NOT NULL constraints */ --#define TF_Shadow 0x1000 /* True for a shadow table */ -+** TF_HasVirtual == COLFLAG_VIRTUAL -+** TF_HasStored == COLFLAG_STORED -+** TF_HasHidden == COLFLAG_HIDDEN -+*/ -+#define TF_Readonly 0x00000001 /* Read-only system table */ -+#define TF_HasHidden 0x00000002 /* Has one or more hidden columns */ -+#define TF_HasPrimaryKey 0x00000004 /* Table has a primary key */ -+#define TF_Autoincrement 0x00000008 /* Integer primary key is autoincrement */ -+#define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */ -+#define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */ -+#define TF_HasStored 0x00000040 /* Has one or more STORED columns */ -+#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ -+#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ -+#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */ -+#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ -+#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ -+#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ -+#define TF_Shadow 0x00001000 /* True for a shadow table */ -+#define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ -+#define TF_Ephemeral 0x00004000 /* An ephemeral table */ -+#define TF_Eponymous 0x00008000 /* An eponymous virtual table */ -+#define TF_Strict 0x00010000 /* STRICT mode */ -+ -+/* -+** Allowed values for Table.eTabType -+*/ -+#define TABTYP_NORM 0 /* Ordinary table */ -+#define TABTYP_VTAB 1 /* Virtual table */ -+#define TABTYP_VIEW 2 /* A view */ -+ -+#define IsView(X) ((X)->eTabType==TABTYP_VIEW) -+#define IsOrdinaryTable(X) ((X)->eTabType==TABTYP_NORM) - - /* - ** Test to see whether or not a table is a virtual table. This is -@@ -17557,9 +18883,9 @@ struct Table { - ** table support is omitted from the build. - */ - #ifndef SQLITE_OMIT_VIRTUALTABLE --# define IsVirtual(X) ((X)->nModuleArg) -+# define IsVirtual(X) ((X)->eTabType==TABTYP_VTAB) - # define ExprIsVtab(X) \ -- ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) -+ ((X)->op==TK_COLUMN && (X)->y.pTab->eTabType==TABTYP_VTAB) - #else - # define IsVirtual(X) 0 - # define ExprIsVtab(X) 0 -@@ -17587,6 +18913,15 @@ struct Table { - #define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0) - #define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0) - -+/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is -+** available. By default, this macro is false -+*/ -+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW -+# define ViewCanHaveRowid 0 -+#else -+# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0) -+#endif -+ - /* - ** Each foreign key constraint is an instance of the following structure. - ** -@@ -17629,9 +18964,13 @@ struct FKey { - struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ - int iFrom; /* Index of column in pFrom */ - char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ -- } aCol[1]; /* One entry for each of nCol columns */ -+ } aCol[FLEXARRAY]; /* One entry for each of nCol columns */ - }; - -+/* The size (in bytes) of an FKey object holding N columns. The answer -+** does NOT include space to hold the zTo name. */ -+#define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap)) -+ - /* - ** SQLite supports many different ways to resolve a constraint - ** error. ROLLBACK processing means that a constraint violation -@@ -17646,16 +18985,22 @@ struct FKey { - ** is returned. REPLACE means that preexisting database rows that caused - ** a UNIQUE constraint violation are removed so that the new insert or - ** update can proceed. Processing continues and no error is reported. -+** UPDATE applies to insert operations only and means that the insert -+** is omitted and the DO UPDATE clause of an upsert is run instead. - ** --** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. -+** RESTRICT, SETNULL, SETDFLT, and CASCADE actions apply only to foreign keys. - ** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the - ** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign --** key is set to NULL. CASCADE means that a DELETE or UPDATE of the -+** key is set to NULL. SETDFLT means that the foreign key is set -+** to its default value. CASCADE means that a DELETE or UPDATE of the - ** referenced table row is propagated into the row that holds the - ** foreign key. - ** -+** The OE_Default value is a place holder that means to use whatever -+** conflict resolution algorithm is required from context. -+** - ** The following symbolic values are used to record which type --** of action to take. -+** of conflict resolution action to take. - */ - #define OE_None 0 /* There is no constraint to check */ - #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ -@@ -17687,9 +19032,12 @@ struct KeyInfo { - u16 nAllField; /* Total columns, including key plus others */ - sqlite3 *db; /* The database connection */ - u8 *aSortFlags; /* Sort order for each column. */ -- CollSeq *aColl[1]; /* Collating sequence for each term of the key */ -+ CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */ - }; - -+/* The size (in bytes) of a KeyInfo object with up to N fields */ -+#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*)) -+ - /* - ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. - */ -@@ -17734,6 +19082,11 @@ struct KeyInfo { - struct UnpackedRecord { - KeyInfo *pKeyInfo; /* Collation and sort-order information */ - Mem *aMem; /* Values */ -+ union { -+ char *z; /* Cache of aMem[0].z for vdbeRecordCompareString() */ -+ i64 i; /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */ -+ } u; -+ int n; /* Cache of aMem[0].n used by vdbeRecordCompareString() */ - u16 nField; /* Number of entries in apMem[] */ - i8 default_rc; /* Comparison result if keys are equal */ - u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */ -@@ -17765,10 +19118,22 @@ struct UnpackedRecord { - ** The Index.onError field determines whether or not the indexed columns - ** must be unique and what to do if they are not. When Index.onError=OE_None, - ** it means this is not a unique index. Otherwise it is a unique index --** and the value of Index.onError indicate the which conflict resolution --** algorithm to employ whenever an attempt is made to insert a non-unique -+** and the value of Index.onError indicates which conflict resolution -+** algorithm to employ when an attempt is made to insert a non-unique - ** element. - ** -+** The colNotIdxed bitmask is used in combination with SrcItem.colUsed -+** for a fast test to see if an index can serve as a covering index. -+** colNotIdxed has a 1 bit for every column of the original table that -+** is *not* available in the index. Thus the expression -+** "colUsed & colNotIdxed" will be non-zero if the index is not a -+** covering index. The most significant bit of of colNotIdxed will always -+** be true (note-20221022-a). If a column beyond the 63rd column of the -+** table is used, the "colUsed & colNotIdxed" test will always be non-zero -+** and we have to assume either that the index is not covering, or use -+** an alternative (slower) algorithm to determine whether or not -+** the index is covering. -+** - ** While parsing a CREATE TABLE or CREATE INDEX statement in order to - ** generate VDBE code (as opposed to parsing one read from an sqlite_schema - ** table as part of parsing an existing database schema), transient instances -@@ -17792,7 +19157,7 @@ struct Index { - Pgno tnum; /* DB Page containing root of this index */ - LogEst szIdxRow; /* Estimated average row size in bytes */ - u16 nKeyCol; /* Number of columns forming the key */ -- u16 nColumn; /* Number of columns stored in the index */ -+ u16 nColumn; /* Nr columns in btree. Can be 2*Table.nCol */ - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ - unsigned bUnordered:1; /* Use this index for == or IN queries only */ -@@ -17804,15 +19169,18 @@ struct Index { - unsigned bNoQuery:1; /* Do not use this index to optimize queries */ - unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ - unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ -+ unsigned bHasExpr:1; /* Index contains an expression, either a literal -+ ** expression, or a reference to a VIRTUAL column */ - #ifdef SQLITE_ENABLE_STAT4 - int nSample; /* Number of elements in aSample[] */ -+ int mxSample; /* Number of slots allocated to aSample[] */ - int nSampleCol; /* Size of IndexSample.anEq[] and so on */ - tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ - IndexSample *aSample; /* Samples of the left-most key */ - tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ - tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ - #endif -- Bitmask colNotIdxed; /* 0 for unindexed columns in pTab */ -+ Bitmask colNotIdxed; /* Unindexed columns in pTab */ - }; - - /* -@@ -17887,18 +19255,17 @@ struct AggInfo { - ** from source tables rather than from accumulators */ - u8 useSortingIdx; /* In direct mode, reference the sorting index rather - ** than the source table */ -+ u32 nSortingColumn; /* Number of columns in the sorting index */ - int sortingIdx; /* Cursor number of the sorting index */ - int sortingIdxPTab; /* Cursor number of pseudo-table */ -- int nSortingColumn; /* Number of columns in the sorting index */ -- int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ -+ int iFirstReg; /* First register in range for aCol[] and aFunc[] */ - ExprList *pGroupBy; /* The group by clause */ - struct AggInfo_col { /* For each column used in source tables */ - Table *pTab; /* Source table */ - Expr *pCExpr; /* The original expression */ - int iTable; /* Cursor number of the source table */ -- int iMem; /* Memory location that acts as accumulator */ -- i16 iColumn; /* Column number within the source table */ -- i16 iSorterColumn; /* Column number in the sorting index */ -+ int iColumn; /* Column number within the source table */ -+ int iSorterColumn; /* Column number in the sorting index */ - } *aCol; - int nColumn; /* Number of used entries in aCol[] */ - int nAccumulator; /* Number of columns that show through to the output. -@@ -17907,14 +19274,37 @@ struct AggInfo { - struct AggInfo_func { /* For each aggregate function */ - Expr *pFExpr; /* Expression encoding the function */ - FuncDef *pFunc; /* The aggregate function implementation */ -- int iMem; /* Memory location that acts as accumulator */ - int iDistinct; /* Ephemeral table used to enforce DISTINCT */ -+ int iDistAddr; /* Address of OP_OpenEphemeral */ -+ int iOBTab; /* Ephemeral table to implement ORDER BY */ -+ u8 bOBPayload; /* iOBTab has payload columns separate from key */ -+ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */ -+ u8 bUseSubtype; /* Transfer subtype info through sorter */ - } *aFunc; - int nFunc; /* Number of entries in aFunc[] */ - u32 selId; /* Select to which this AggInfo belongs */ -- AggInfo *pNext; /* Next in list of them all */ -+#ifdef SQLITE_DEBUG -+ Select *pSelect; /* SELECT statement that this AggInfo supports */ -+#endif - }; - -+/* -+** Macros to compute aCol[] and aFunc[] register numbers. -+** -+** These macros should not be used prior to the call to -+** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg. -+** The assert()s that are part of this macro verify that constraint. -+*/ -+#ifndef NDEBUG -+#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I)) -+#define AggInfoFuncReg(A,I) \ -+ (assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I)) -+#else -+#define AggInfoColumnReg(A,I) ((A)->iFirstReg+(I)) -+#define AggInfoFuncReg(A,I) \ -+ ((A)->iFirstReg+(A)->nColumn+(I)) -+#endif -+ - /* - ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. - ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater -@@ -17942,10 +19332,10 @@ typedef int ynVar; - ** tree. - ** - ** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, --** or TK_STRING), then Expr.token contains the text of the SQL literal. If --** the expression is a variable (TK_VARIABLE), then Expr.token contains the -+** or TK_STRING), then Expr.u.zToken contains the text of the SQL literal. If -+** the expression is a variable (TK_VARIABLE), then Expr.u.zToken contains the - ** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), --** then Expr.token contains the name of the function. -+** then Expr.u.zToken contains the name of the function. - ** - ** Expr.pRight and Expr.pLeft are the left and right subexpressions of a - ** binary operator. Either or both may be NULL. -@@ -17985,7 +19375,7 @@ typedef int ynVar; - ** help reduce memory requirements, sometimes an Expr object will be - ** truncated. And to reduce the number of memory allocations, sometimes - ** two or more Expr objects will be stored in a single memory allocation, --** together with Expr.zToken strings. -+** together with Expr.u.zToken strings. - ** - ** If the EP_Reduced and EP_TokenOnly flags are set when - ** an Expr object is truncated. When EP_Reduced is set, then all -@@ -18034,19 +19424,23 @@ struct Expr { - ** TK_REGISTER: register number - ** TK_TRIGGER: 1 -> new, 0 -> old - ** EP_Unlikely: 134217728 times likelihood -- ** TK_IN: ephemerial table holding RHS -+ ** TK_IN: ephemeral table holding RHS - ** TK_SELECT_COLUMN: Number of columns on the LHS - ** TK_SELECT: 1st register of result vector */ - ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. - ** TK_VARIABLE: variable number (always >= 1). - ** TK_SELECT_COLUMN: column of the result vector */ - i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ -- i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ -+ union { -+ int iJoin; /* If EP_OuterON or EP_InnerON, the right table */ -+ int iOfst; /* else: start of token from start of statement */ -+ } w; - AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ - union { - Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL - ** for a column of an index on an expression */ - Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */ -+ int nReg; /* TK_NULLS: Number of registers to NULL out */ - struct { /* TK_IN, TK_SELECT, and TK_EXISTS */ - int iAddr; /* Subroutine entry address */ - int regReturn; /* Register used to hold return address */ -@@ -18054,36 +19448,35 @@ struct Expr { - } y; - }; - --/* --** The following are the meanings of bits in the Expr.flags field. -+/* The following are the meanings of bits in the Expr.flags field. - ** Value restrictions: - ** - ** EP_Agg == NC_HasAgg == SF_HasAgg - ** EP_Win == NC_HasWin - */ --#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ --#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */ --#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */ --#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */ -+#define EP_OuterON 0x000001 /* Originates in ON/USING clause of outer join */ -+#define EP_InnerON 0x000002 /* Originates in ON/USING of an inner join */ -+#define EP_Distinct 0x000004 /* Aggregate function with DISTINCT keyword */ -+#define EP_HasFunc 0x000008 /* Contains one or more functions of any kind */ - #define EP_Agg 0x000010 /* Contains one or more aggregate functions */ --#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ --#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ --#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ --#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */ --#define EP_Commuted 0x000200 /* Comparison operator has been commuted */ --#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ --#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */ --#define EP_Skip 0x001000 /* Operator does not contribute to affinity */ --#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ --#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ -+#define EP_FixedCol 0x000020 /* TK_Column with a known fixed value */ -+#define EP_VarSelect 0x000040 /* pSelect is correlated, not constant */ -+#define EP_DblQuoted 0x000080 /* token.z was originally in "..." */ -+#define EP_InfixFunc 0x000100 /* True for an infix function: LIKE, GLOB, etc */ -+#define EP_Collate 0x000200 /* Tree contains a TK_COLLATE operator */ -+#define EP_Commuted 0x000400 /* Comparison operator has been commuted */ -+#define EP_IntValue 0x000800 /* Integer value contained in u.iValue */ -+#define EP_xIsSelect 0x001000 /* x.pSelect is valid (otherwise x.pList is) */ -+#define EP_Skip 0x002000 /* Operator does not contribute to affinity */ -+#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ - #define EP_Win 0x008000 /* Contains window functions */ --#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ -- /* 0x020000 // available for reuse */ --#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ --#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ --#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ --#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ --#define EP_Alias 0x400000 /* Is an alias for a result set column */ -+#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ -+#define EP_FullSize 0x020000 /* Expr structure must remain full sized */ -+#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */ -+#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */ -+#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ -+#define EP_CanBeNull 0x200000 /* Can be null despite NOT NULL constraint */ -+#define EP_Subquery 0x400000 /* Tree contains a TK_SELECT operator */ - #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ - #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ - #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ -@@ -18092,25 +19485,36 @@ struct Expr { - #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ - #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ - #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ -- /* 0x80000000 // Available */ -+#define EP_SubtArg 0x80000000 /* Is argument to SQLITE_SUBTYPE function */ - --/* --** The EP_Propagate mask is a set of properties that automatically propagate -+/* The EP_Propagate mask is a set of properties that automatically propagate - ** upwards into parent nodes. - */ - #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) - --/* --** These macros can be used to test, set, or clear bits in the -+/* Macros can be used to test, set, or clear bits in the - ** Expr.flags field. - */ --#define ExprHasProperty(E,P) (((E)->flags&(P))!=0) --#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) --#define ExprSetProperty(E,P) (E)->flags|=(P) --#define ExprClearProperty(E,P) (E)->flags&=~(P) --#define ExprAlwaysTrue(E) (((E)->flags&(EP_FromJoin|EP_IsTrue))==EP_IsTrue) --#define ExprAlwaysFalse(E) (((E)->flags&(EP_FromJoin|EP_IsFalse))==EP_IsFalse) -- -+#define ExprHasProperty(E,P) (((E)->flags&(u32)(P))!=0) -+#define ExprHasAllProperty(E,P) (((E)->flags&(u32)(P))==(u32)(P)) -+#define ExprSetProperty(E,P) (E)->flags|=(u32)(P) -+#define ExprClearProperty(E,P) (E)->flags&=~(u32)(P) -+#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) -+#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) -+#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0) -+ -+/* Macros used to ensure that the correct members of unions are accessed -+** in Expr. -+*/ -+#define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0) -+#define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0) -+#define ExprUseWOfst(E) (((E)->flags&(EP_InnerON|EP_OuterON))==0) -+#define ExprUseWJoin(E) (((E)->flags&(EP_InnerON|EP_OuterON))!=0) -+#define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0) -+#define ExprUseXSelect(E) (((E)->flags&EP_xIsSelect)!=0) -+#define ExprUseYTab(E) (((E)->flags&(EP_WinFunc|EP_Subrtn))==0) -+#define ExprUseYWin(E) (((E)->flags&EP_WinFunc)!=0) -+#define ExprUseYSub(E) (((E)->flags&EP_Subrtn)!=0) - - /* Flags for use with Expr.vvaFlags - */ -@@ -18182,31 +19586,45 @@ struct Expr { - */ - struct ExprList { - int nExpr; /* Number of expressions on the list */ -+ int nAlloc; /* Number of a[] slots allocated */ - struct ExprList_item { /* For each expression in the list */ - Expr *pExpr; /* The parse tree for this expression */ - char *zEName; /* Token associated with this expression */ -- u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ -- unsigned eEName :2; /* Meaning of zEName */ -- unsigned done :1; /* A flag to indicate when processing is finished */ -- unsigned reusable :1; /* Constant expression is reusable */ -- unsigned bSorterRef :1; /* Defer evaluation until after sorting */ -- unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */ -+ struct { -+ u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ -+ unsigned eEName :2; /* Meaning of zEName */ -+ unsigned done :1; /* Indicates when processing is finished */ -+ unsigned reusable :1; /* Constant expression is reusable */ -+ unsigned bSorterRef :1; /* Defer evaluation until after sorting */ -+ unsigned bNulls :1; /* True if explicit "NULLS FIRST/LAST" */ -+ unsigned bUsed :1; /* This column used in a SF_NestedFrom subquery */ -+ unsigned bUsingTerm:1; /* Term from the USING clause of a NestedFrom */ -+ unsigned bNoExpand: 1; /* Term is an auxiliary in NestedFrom and should -+ ** not be expanded by "*" in parent queries */ -+ } fg; - union { -- struct { -+ struct { /* Used by any ExprList other than Parse.pConsExpr */ - u16 iOrderByCol; /* For ORDER BY, column number in result set */ - u16 iAlias; /* Index into Parse.aAlias[] for zName */ - } x; -- int iConstExprReg; /* Register in which Expr value is cached */ -+ int iConstExprReg; /* Register in which Expr value is cached. Used only -+ ** by Parse.pConstExpr */ - } u; -- } a[1]; /* One slot for each expression in the list */ -+ } a[FLEXARRAY]; /* One slot for each expression in the list */ - }; - -+/* The size (in bytes) of an ExprList object that is big enough to hold -+** as many as N expressions. */ -+#define SZ_EXPRLIST(N) \ -+ (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item)) -+ - /* - ** Allowed values for Expr.a.eEName - */ - #define ENAME_NAME 0 /* The AS clause of a result set */ - #define ENAME_SPAN 1 /* Complete text of the result set expression */ - #define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */ -+#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */ - - /* - ** An instance of this structure can hold a simple list of identifiers, -@@ -18224,23 +19642,36 @@ struct ExprList { - ** If "a" is the k-th column of table "t", then IdList.a[0].idx==k. - */ - struct IdList { -+ int nId; /* Number of identifiers on the list */ - struct IdList_item { - char *zName; /* Name of the identifier */ -- int idx; /* Index in some Table.aCol[] of a column named zName */ -- } *a; -- int nId; /* Number of identifiers on the list */ -+ } a[FLEXARRAY]; - }; - -+/* The size (in bytes) of an IdList object that can hold up to N IDs. */ -+#define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item)) -+ - /* --** The following structure describes the FROM clause of a SELECT statement. --** Each table or subquery in the FROM clause is a separate element of --** the SrcList.a[] array. --** --** With the addition of multiple database support, the following structure --** can also be used to describe a particular table such as the table that --** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL, --** such a table must be a simple name: ID. But in SQLite, the table can --** now be identified by a database name, a dot, then the table name: ID.ID. -+** Allowed values for IdList.eType, which determines which value of the a.u4 -+** is valid. -+*/ -+#define EU4_NONE 0 /* Does not use IdList.a.u4 */ -+#define EU4_IDX 1 /* Uses IdList.a.u4.idx */ -+#define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */ -+ -+/* -+** Details of the implementation of a subquery. -+*/ -+struct Subquery { -+ Select *pSelect; /* A SELECT statement used in place of a table name */ -+ int addrFillSub; /* Address of subroutine to initialize a subquery */ -+ int regReturn; /* Register holding return address of addrFillSub */ -+ int regResult; /* Registers holding results of a co-routine */ -+}; -+ -+/* -+** The SrcItem object represents a single term in the FROM clause of a query. -+** The SrcList object is mostly an array of SrcItems. - ** - ** The jointype starts out showing the join type between the current table - ** and the next table on the list. The parser builds the list this way. -@@ -18249,53 +19680,121 @@ struct IdList { - ** - ** In the colUsed field, the high-order bit (bit 63) is set if the table - ** contains more than 63 columns and the 64-th or later column is used. --*/ --struct SrcList { -- int nSrc; /* Number of tables or subqueries in the FROM clause */ -- u32 nAlloc; /* Number of entries allocated in a[] below */ -- struct SrcList_item { -+** -+** Aggressive use of "union" helps keep the size of the object small. This -+** has been shown to boost performance, in addition to saving memory. -+** Access to union elements is gated by the following rules which should -+** always be checked, either by an if-statement or by an assert(). -+** -+** Field Only access if this is true -+** --------------- ----------------------------------- -+** u1.zIndexedBy fg.isIndexedBy -+** u1.pFuncArg fg.isTabFunc -+** u1.nRow !fg.isTabFunc && !fg.isIndexedBy -+** -+** u2.pIBIndex fg.isIndexedBy -+** u2.pCteUse fg.isCte -+** -+** u3.pOn !fg.isUsing -+** u3.pUsing fg.isUsing -+** -+** u4.zDatabase !fg.fixedSchema && !fg.isSubquery -+** u4.pSchema fg.fixedSchema -+** u4.pSubq fg.isSubquery -+** -+** See also the sqlite3SrcListDelete() routine for assert() statements that -+** check invariants on the fields of this object, especially the flags -+** inside the fg struct. -+*/ -+struct SrcItem { -+ char *zName; /* Name of the table */ -+ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ -+ Table *pSTab; /* Table object for zName. Mnemonic: Srcitem-TABle */ -+ struct { -+ u8 jointype; /* Type of join between this table and the previous */ -+ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ -+ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ -+ unsigned isSubquery :1; /* True if this term is a subquery */ -+ unsigned isTabFunc :1; /* True if table-valued-function syntax */ -+ unsigned isCorrelated :1; /* True if sub-query is correlated */ -+ unsigned isMaterialized:1; /* This is a materialized view */ -+ unsigned viaCoroutine :1; /* Implemented as a co-routine */ -+ unsigned isRecursive :1; /* True for recursive reference in WITH */ -+ unsigned fromDDL :1; /* Comes from sqlite_schema */ -+ unsigned isCte :1; /* This is a CTE */ -+ unsigned notCte :1; /* This item may not match a CTE */ -+ unsigned isUsing :1; /* u3.pUsing is valid */ -+ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ -+ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ -+ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ -+ unsigned rowidUsed :1; /* The ROWID of this table is referenced */ -+ unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */ -+ unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */ -+ } fg; -+ int iCursor; /* The VDBE cursor number used to access this table */ -+ Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ -+ union { -+ char *zIndexedBy; /* Identifier from "INDEXED BY " clause */ -+ ExprList *pFuncArg; /* Arguments to table-valued-function */ -+ u32 nRow; /* Number of rows in a VALUES clause */ -+ } u1; -+ union { -+ Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ -+ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ -+ } u2; -+ union { -+ Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ -+ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ -+ } u3; -+ union { - Schema *pSchema; /* Schema to which this item is fixed */ - char *zDatabase; /* Name of database holding this table */ -- char *zName; /* Name of the table */ -- char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ -- Table *pTab; /* An SQL table corresponding to zName */ -- Select *pSelect; /* A SELECT statement used in place of a table name */ -- int addrFillSub; /* Address of subroutine to manifest a subquery */ -- int regReturn; /* Register holding return address of addrFillSub */ -- int regResult; /* Registers holding results of a co-routine */ -- struct { -- u8 jointype; /* Type of join between this table and the previous */ -- unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ -- unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ -- unsigned isTabFunc :1; /* True if table-valued-function syntax */ -- unsigned isCorrelated :1; /* True if sub-query is correlated */ -- unsigned viaCoroutine :1; /* Implemented as a co-routine */ -- unsigned isRecursive :1; /* True for recursive reference in WITH */ -- unsigned fromDDL :1; /* Comes from sqlite_schema */ -- } fg; -- int iCursor; /* The VDBE cursor number used to access this table */ -- Expr *pOn; /* The ON clause of a join */ -- IdList *pUsing; /* The USING clause of a join */ -- Bitmask colUsed; /* Bit N (1<" clause */ -- ExprList *pFuncArg; /* Arguments to table-valued-function */ -- } u1; -- Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ -- } a[1]; /* One entry for each identifier on the list */ -+ Subquery *pSubq; /* Description of a subquery */ -+ } u4; - }; - - /* --** Permitted values of the SrcList.a.jointype field -+** The OnOrUsing object represents either an ON clause or a USING clause. -+** It can never be both at the same time, but it can be neither. -+*/ -+struct OnOrUsing { -+ Expr *pOn; /* The ON clause of a join */ -+ IdList *pUsing; /* The USING clause of a join */ -+}; -+ -+/* -+** This object represents one or more tables that are the source of -+** content for an SQL statement. For example, a single SrcList object -+** is used to hold the FROM clause of a SELECT statement. SrcList also -+** represents the target tables for DELETE, INSERT, and UPDATE statements. -+** - */ --#define JT_INNER 0x0001 /* Any kind of inner or cross join */ --#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */ --#define JT_NATURAL 0x0004 /* True for a "natural" join */ --#define JT_LEFT 0x0008 /* Left outer join */ --#define JT_RIGHT 0x0010 /* Right outer join */ --#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */ --#define JT_ERROR 0x0040 /* unknown or unsupported join type */ -+struct SrcList { -+ int nSrc; /* Number of tables or subqueries in the FROM clause */ -+ u32 nAlloc; /* Number of entries allocated in a[] below */ -+ SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */ -+}; - -+/* Size (in bytes) of a SrcList object that can hold as many as N -+** SrcItem objects. */ -+#define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem)) -+ -+/* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a -+** special case of SZ_SRCITEM(1) that comes up often. */ -+#define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem)) -+ -+/* -+** Permitted values of the SrcList.a.jointype field -+*/ -+#define JT_INNER 0x01 /* Any kind of inner or cross join */ -+#define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */ -+#define JT_NATURAL 0x04 /* True for a "natural" join */ -+#define JT_LEFT 0x08 /* Left outer join */ -+#define JT_RIGHT 0x10 /* Right outer join */ -+#define JT_OUTER 0x20 /* The "OUTER" keyword is present */ -+#define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN -+ ** Mnemonic: Left Table Of Right Join */ -+#define JT_ERROR 0x80 /* unknown or unsupported join type */ - - /* - ** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin() -@@ -18316,10 +19815,10 @@ struct SrcList { - #define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */ - #define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */ - #define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */ --#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */ -+#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */ - #define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */ --#define WHERE_SEEK_UNIQ_TABLE 0x1000 /* Do not defer seeks if unique */ -- /* 0x2000 not currently used */ -+#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */ -+#define WHERE_KEEP_ALL_JOINS 0x2000 /* Do not do the omit-noop-join opt */ - #define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */ - /* 0x8000 not currently used */ - -@@ -18358,11 +19857,13 @@ struct NameContext { - ExprList *pEList; /* Optional list of result-set columns */ - AggInfo *pAggInfo; /* Information about aggregates at this level */ - Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */ -+ int iBaseReg; /* For TK_REGISTER when parsing RETURNING */ - } uNC; - NameContext *pNext; /* Next outer name context. NULL for outermost */ - int nRef; /* Number of names resolved by this context */ -- int nErr; /* Number of errors encountered while resolving names */ -+ int nNcErr; /* Number of errors encountered while resolving names */ - int ncFlags; /* Zero or more NC_* flags defined below */ -+ u32 nNestedSelect; /* Number of nested selects using this NC */ - Select *pWinSelect; /* SELECT statement for any window functions */ - }; - -@@ -18370,29 +19871,34 @@ struct NameContext { - ** Allowed values for the NameContext, ncFlags field. - ** - ** Value constraints (all checked via assert()): --** NC_HasAgg == SF_HasAgg == EP_Agg --** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX -+** NC_HasAgg == SF_HasAgg == EP_Agg -+** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX -+** NC_OrderAgg == SF_OrderByReqd == SQLITE_FUNC_ANYORDER - ** NC_HasWin == EP_Win - ** - */ --#define NC_AllowAgg 0x00001 /* Aggregate functions are allowed here */ --#define NC_PartIdx 0x00002 /* True if resolving a partial index WHERE */ --#define NC_IsCheck 0x00004 /* True if resolving a CHECK constraint */ --#define NC_GenCol 0x00008 /* True for a GENERATED ALWAYS AS clause */ --#define NC_HasAgg 0x00010 /* One or more aggregate functions seen */ --#define NC_IdxExpr 0x00020 /* True if resolving columns of CREATE INDEX */ --#define NC_SelfRef 0x0002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ --#define NC_VarSelect 0x00040 /* A correlated subquery has been seen */ --#define NC_UEList 0x00080 /* True if uNC.pEList is used */ --#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */ --#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */ --#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */ --#define NC_Complex 0x02000 /* True if a function or subquery seen */ --#define NC_AllowWin 0x04000 /* Window functions are allowed here */ --#define NC_HasWin 0x08000 /* One or more window functions seen */ --#define NC_IsDDL 0x10000 /* Resolving names in a CREATE statement */ --#define NC_InAggFunc 0x20000 /* True if analyzing arguments to an agg func */ --#define NC_FromDDL 0x40000 /* SQL text comes from sqlite_schema */ -+#define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */ -+#define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */ -+#define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */ -+#define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */ -+#define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ -+#define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ -+#define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ -+#define NC_Subquery 0x000040 /* A subquery has been seen */ -+#define NC_UEList 0x000080 /* True if uNC.pEList is used */ -+#define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */ -+#define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ -+#define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */ -+#define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */ -+/* 0x002000 // available for reuse */ -+#define NC_AllowWin 0x004000 /* Window functions are allowed here */ -+#define NC_HasWin 0x008000 /* One or more window functions seen */ -+#define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */ -+#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */ -+#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */ -+#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */ -+#define NC_Where 0x100000 /* Processing WHERE clause of a SELECT */ -+#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ - - /* - ** An instance of the following object describes a single ON CONFLICT -@@ -18409,15 +19915,22 @@ struct NameContext { - ** WHERE clause is omitted. - */ - struct Upsert { -- ExprList *pUpsertTarget; /* Optional description of conflicting index */ -+ ExprList *pUpsertTarget; /* Optional description of conflict target */ - Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */ - ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ - Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ -- /* The fields above comprise the parse tree for the upsert clause. -- ** The fields below are used to transfer information from the INSERT -- ** processing down into the UPDATE processing while generating code. -- ** Upsert owns the memory allocated above, but not the memory below. */ -- Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */ -+ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ -+ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ -+ u8 isDup; /* True if 2nd or later with same pUpsertIdx */ -+ /* Above this point is the parse tree for the ON CONFLICT clauses. -+ ** The next group of fields stores intermediate data. */ -+ void *pToFree; /* Free memory when deleting the Upsert object */ -+ /* All fields above are owned by the Upsert object and must be freed -+ ** when the Upsert is destroyed. The fields below are used to transfer -+ ** information from the INSERT processing down into the UPDATE processing -+ ** while generating code. The fields below are owned by the INSERT -+ ** statement and will be freed by INSERT processing. */ -+ Index *pUpsertIdx; /* UNIQUE constraint specified by pUpsertTarget */ - SrcList *pUpsertSrc; /* Table to be updated */ - int regData; /* First register holding array of VALUES */ - int iDataCur; /* Index of the data cursor */ -@@ -18469,9 +19982,10 @@ struct Select { - ** "Select Flag". - ** - ** Value constraints (all checked via assert()) --** SF_HasAgg == NC_HasAgg --** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX --** SF_FixedLimit == WHERE_USE_LIMIT -+** SF_HasAgg == NC_HasAgg -+** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX -+** SF_OrderByReqd == NC_OrderAgg == SQLITE_FUNC_ANYORDER -+** SF_FixedLimit == WHERE_USE_LIMIT - */ - #define SF_Distinct 0x0000001 /* Output should be DISTINCT */ - #define SF_All 0x0000002 /* Includes the ALL keyword */ -@@ -18496,7 +20010,18 @@ struct Select { - #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ - #define SF_View 0x0200000 /* SELECT statement is a view */ - #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ --#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ -+#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ -+#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ -+#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ -+#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ -+#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ -+#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ -+#define SF_Correlated 0x20000000 /* True if references the outer context */ -+ -+/* True if SrcItem X is a subquery that has SF_NestedFrom */ -+#define IsNestedFrom(X) \ -+ ((X)->fg.isSubquery && \ -+ ((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0) - - /* - ** The results of a SELECT can be distributed in several ways, as defined -@@ -18515,9 +20040,6 @@ struct Select { - ** statements within triggers whose only purpose is - ** the side-effects of functions. - ** --** All of the above are free to ignore their ORDER BY clause. Those that --** follow must honor the ORDER BY clause. --** - ** SRT_Output Generate a row of output (using the OP_ResultRow - ** opcode) for each row in the result set. - ** -@@ -18529,7 +20051,11 @@ struct Select { - ** SRT_Set The result must be a single column. Store each - ** row of result as the key in table pDest->iSDParm. - ** Apply the affinity pDest->affSdst before storing --** results. Used to implement "IN (SELECT ...)". -+** results. if pDest->iSDParm2 is positive, then it is -+** a register holding a Bloom filter for the IN operator -+** that should be populated in addition to the -+** pDest->iSDParm table. This SRT is used to -+** implement "IN (SELECT ...)". - ** - ** SRT_EphemTab Create an temporary table pDest->iSDParm and store - ** the result there. The cursor is left open after -@@ -18574,13 +20100,18 @@ struct Select { - #define SRT_Except 2 /* Remove result from a UNION index */ - #define SRT_Exists 3 /* Store 1 if the result is not empty */ - #define SRT_Discard 4 /* Do not save the results anywhere */ --#define SRT_Fifo 5 /* Store result as data with an automatic rowid */ --#define SRT_DistFifo 6 /* Like SRT_Fifo, but unique results only */ -+#define SRT_DistFifo 5 /* Like SRT_Fifo, but unique results only */ -+#define SRT_DistQueue 6 /* Like SRT_Queue, but unique results only */ -+ -+/* The DISTINCT clause is ignored for all of the above. Not that -+** IgnorableDistinct() implies IgnorableOrderby() */ -+#define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue) -+ - #define SRT_Queue 7 /* Store result in an queue */ --#define SRT_DistQueue 8 /* Like SRT_Queue, but unique results only */ -+#define SRT_Fifo 8 /* Store result as data with an automatic rowid */ - - /* The ORDER BY clause is ignored for all of the above */ --#define IgnorableOrderby(X) ((X->eDest)<=SRT_DistQueue) -+#define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo) - - #define SRT_Output 9 /* Output each row of result */ - #define SRT_Mem 10 /* Store result in a memory cell */ -@@ -18600,7 +20131,7 @@ struct SelectDest { - int iSDParm2; /* A second parameter for the eDest disposal method */ - int iSdst; /* Base register where results are written */ - int nSdst; /* Number of registers allocated */ -- char *zAffSdst; /* Affinity used when eDest==SRT_Set */ -+ char *zAffSdst; /* Affinity used for SRT_Set */ - ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */ - }; - -@@ -18659,11 +20190,45 @@ struct TriggerPrg { - #else - typedef unsigned int yDbMask; - # define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0) --# define DbMaskZero(M) (M)=0 --# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I)) --# define DbMaskAllZero(M) (M)==0 --# define DbMaskNonZero(M) (M)!=0 -+# define DbMaskZero(M) ((M)=0) -+# define DbMaskSet(M,I) ((M)|=(((yDbMask)1)<<(I))) -+# define DbMaskAllZero(M) ((M)==0) -+# define DbMaskNonZero(M) ((M)!=0) -+#endif -+ -+/* -+** For each index X that has as one of its arguments either an expression -+** or the name of a virtual generated column, and if X is in scope such that -+** the value of the expression can simply be read from the index, then -+** there is an instance of this object on the Parse.pIdxExpr list. -+** -+** During code generation, while generating code to evaluate expressions, -+** this list is consulted and if a matching expression is found, the value -+** is read from the index rather than being recomputed. -+*/ -+struct IndexedExpr { -+ Expr *pExpr; /* The expression contained in the index */ -+ int iDataCur; /* The data cursor associated with the index */ -+ int iIdxCur; /* The index cursor */ -+ int iIdxCol; /* The index column that contains value of pExpr */ -+ u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */ -+ u8 aff; /* Affinity of the pExpr expression */ -+ IndexedExpr *pIENext; /* Next in a list of all indexed expressions */ -+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS -+ const char *zIdxName; /* Name of index, used only for bytecode comments */ - #endif -+}; -+ -+/* -+** An instance of the ParseCleanup object specifies an operation that -+** should be performed after parsing to deallocation resources obtained -+** during the parse and which are no longer needed. -+*/ -+struct ParseCleanup { -+ ParseCleanup *pNext; /* Next cleanup task */ -+ void *pPtr; /* Pointer to object to deallocate */ -+ void (*xCleanup)(sqlite3*,void*); /* Deallocation routine */ -+}; - - /* - ** An SQL parser context. A copy of this structure is passed through -@@ -18686,16 +20251,32 @@ struct Parse { - char *zErrMsg; /* An error message */ - Vdbe *pVdbe; /* An engine for executing database bytecode */ - int rc; /* Return code from execution */ -- u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */ -- u8 checkSchema; /* Causes schema cookie check after an error */ -+ LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ - u8 nested; /* Number of nested calls to the parser/code generator */ - u8 nTempReg; /* Number of temporary registers in aTempReg[] */ - u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ - u8 mayAbort; /* True if statement may throw an ABORT exception */ - u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ -- u8 okConstFactor; /* OK to factor out constants */ - u8 disableLookaside; /* Number of times lookaside has been disabled */ -- u8 disableVtab; /* Disable all virtual tables for this parse */ -+ u8 prepFlags; /* SQLITE_PREPARE_* flags */ -+ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ -+ u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */ -+ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ -+ u8 bReturning; /* Coding a RETURNING trigger */ -+ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ -+ u8 disableTriggers; /* True to disable triggers */ -+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) -+ u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ -+#endif -+#ifdef SQLITE_DEBUG -+ u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ -+ u8 isCreate; /* CREATE TABLE, INDEX, or VIEW (but not TRIGGER) -+ ** and ALTER TABLE ADD COLUMN. */ -+#endif -+ bft colNamesSet :1; /* TRUE after OP_ColumnName has been issued to pVdbe */ -+ bft bHasWith :1; /* True if statement contains WITH */ -+ bft okConstFactor :1; /* OK to factor out constants */ -+ bft checkSchema :1; /* Causes schema cookie check after an error */ - int nRangeReg; /* Size of the temporary register block */ - int iRangeReg; /* First register in temporary register block */ - int nErr; /* Number of errors seen */ -@@ -18708,13 +20289,15 @@ struct Parse { - int nLabelAlloc; /* Number of slots in aLabel */ - int *aLabel; /* Space to hold the labels */ - ExprList *pConstExpr;/* Constant expressions */ -- Token constraintName;/* Name of the constraint currently being parsed */ -+ IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */ -+ IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */ - yDbMask writeMask; /* Start a write transaction on these databases */ - yDbMask cookieMask; /* Bitmask of schema verified databases */ -- int regRowid; /* Register holding rowid of CREATE TABLE entry */ -- int regRoot; /* Register holding root page number for new objects */ -- int nMaxArg; /* Max args passed to user function by sub-program */ -+ int nMaxArg; /* Max args to xUpdate and xFilter vtab methods */ - int nSelect; /* Number of SELECT stmts. Counter for Select.selId */ -+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK -+ u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */ -+#endif - #ifndef SQLITE_OMIT_SHARED_CACHE - int nTableLock; /* Number of locks in aTableLock */ - TableLock *aTableLock; /* Required table locks for shared-cache mode */ -@@ -18722,15 +20305,8 @@ struct Parse { - AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ - Parse *pToplevel; /* Parse structure for main program (or NULL) */ - Table *pTriggerTab; /* Table triggers are being coded for */ -- Parse *pParentParse; /* Parent parser if this parser is nested */ -- AggInfo *pAggList; /* List of all AggInfo objects */ -- int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ -- u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ -- u32 oldmask; /* Mask of old.* columns referenced */ -- u32 newmask; /* Mask of new.* columns referenced */ -- u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ -- u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ -- u8 disableTriggers; /* True to disable triggers */ -+ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ -+ ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */ - - /************************************************************************** - ** Fields above must be initialized to zero. The fields that follow, -@@ -18740,7 +20316,21 @@ struct Parse { - **************************************************************************/ - - int aTempReg[8]; /* Holding area for temporary registers */ -+ Parse *pOuterParse; /* Outer Parse object when nested */ - Token sNameToken; /* Token with unqualified schema object name */ -+ u32 oldmask; /* Mask of old.* columns referenced */ -+ u32 newmask; /* Mask of new.* columns referenced */ -+ union { -+ struct { /* These fields available when isCreate is true */ -+ int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ -+ int regRowid; /* Register holding rowid of CREATE TABLE entry */ -+ int regRoot; /* Register holding root page for new objects */ -+ Token constraintName; /* Name of the constraint currently being parsed */ -+ } cr; -+ struct { /* These fields available to all other statements */ -+ Returning *pReturning; /* The RETURNING clause */ -+ } d; -+ } u1; - - /************************************************************************ - ** Above is constant between recursions. Below is reset before and after -@@ -18758,9 +20348,7 @@ struct Parse { - int nVtabLock; /* Number of virtual tables to lock */ - #endif - int nHeight; /* Expression tree height of current sub-select */ --#ifndef SQLITE_OMIT_EXPLAIN - int addrExplain; /* Address of current OP_Explain opcode */ --#endif - VList *pVList; /* Mapping between variable names and numbers */ - Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ - const char *zTail; /* All SQL text past the last semicolon parsed */ -@@ -18774,15 +20362,14 @@ struct Parse { - Token sArg; /* Complete text of a module argument */ - Table **apVtabLock; /* Pointer to virtual tables needing locking */ - #endif -- Table *pZombieTab; /* List of Table objects to delete after code gen */ -- TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ - With *pWith; /* Current WITH clause, or NULL */ -- With *pWithToFree; /* Free this WITH object at the end of the parse */ - #ifndef SQLITE_OMIT_ALTERTABLE - RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */ - #endif - }; - -+/* Allowed values for Parse.eParseMode -+*/ - #define PARSE_MODE_NORMAL 0 - #define PARSE_MODE_DECLARE_VTAB 1 - #define PARSE_MODE_RENAME 2 -@@ -18791,7 +20378,8 @@ struct Parse { - /* - ** Sizes and pointers of various parts of the Parse object. - */ --#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/ -+#define PARSE_HDR(X) (((char*)(X))+offsetof(Parse,zErrMsg)) -+#define PARSE_HDR_SZ (offsetof(Parse,aTempReg)-offsetof(Parse,zErrMsg)) /* Recursive part w/o aColCache*/ - #define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */ - #define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */ - #define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */ -@@ -18849,6 +20437,7 @@ struct AuthContext { - #define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */ - #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ - #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ -+#define OPFLAG_BYTELENARG 0xc0 /* OP_Column only for octet_length() */ - #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ - #define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ - #define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */ -@@ -18857,27 +20446,29 @@ struct AuthContext { - #define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */ - #define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */ - #define OPFLAG_NOCHNG_MAGIC 0x6d /* OP_MakeRecord: serialtype 10 is ok */ -+#define OPFLAG_PREFORMAT 0x80 /* OP_Insert uses preformatted cell */ - - /* -- * Each trigger present in the database schema is stored as an instance of -- * struct Trigger. -- * -- * Pointers to instances of struct Trigger are stored in two ways. -- * 1. In the "trigHash" hash table (part of the sqlite3* that represents the -- * database). This allows Trigger structures to be retrieved by name. -- * 2. All triggers associated with a single table form a linked list, using the -- * pNext member of struct Trigger. A pointer to the first element of the -- * linked list is stored as the "pTrigger" member of the associated -- * struct Table. -- * -- * The "step_list" member points to the first element of a linked list -- * containing the SQL statements specified as the trigger program. -- */ -+** Each trigger present in the database schema is stored as an instance of -+** struct Trigger. -+** -+** Pointers to instances of struct Trigger are stored in two ways. -+** 1. In the "trigHash" hash table (part of the sqlite3* that represents the -+** database). This allows Trigger structures to be retrieved by name. -+** 2. All triggers associated with a single table form a linked list, using the -+** pNext member of struct Trigger. A pointer to the first element of the -+** linked list is stored as the "pTrigger" member of the associated -+** struct Table. -+** -+** The "step_list" member points to the first element of a linked list -+** containing the SQL statements specified as the trigger program. -+*/ - struct Trigger { - char *zName; /* The name of the trigger */ - char *table; /* The table or view to which the trigger applies */ - u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ - u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ -+ u8 bReturning; /* This trigger implements a RETURNING clause */ - Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */ - IdList *pColumns; /* If this is an UPDATE OF trigger, - the is stored here */ -@@ -18898,52 +20489,58 @@ struct Trigger { - #define TRIGGER_AFTER 2 - - /* -- * An instance of struct TriggerStep is used to store a single SQL statement -- * that is a part of a trigger-program. -- * -- * Instances of struct TriggerStep are stored in a singly linked list (linked -- * using the "pNext" member) referenced by the "step_list" member of the -- * associated struct Trigger instance. The first element of the linked list is -- * the first step of the trigger-program. -- * -- * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or -- * "SELECT" statement. The meanings of the other members is determined by the -- * value of "op" as follows: -- * -- * (op == TK_INSERT) -- * orconf -> stores the ON CONFLICT algorithm -- * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then -- * this stores a pointer to the SELECT statement. Otherwise NULL. -- * zTarget -> Dequoted name of the table to insert into. -- * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then -- * this stores values to be inserted. Otherwise NULL. -- * pIdList -> If this is an INSERT INTO ... () VALUES ... -- * statement, then this stores the column-names to be -- * inserted into. -- * -- * (op == TK_DELETE) -- * zTarget -> Dequoted name of the table to delete from. -- * pWhere -> The WHERE clause of the DELETE statement if one is specified. -- * Otherwise NULL. -- * -- * (op == TK_UPDATE) -- * zTarget -> Dequoted name of the table to update. -- * pWhere -> The WHERE clause of the UPDATE statement if one is specified. -- * Otherwise NULL. -- * pExprList -> A list of the columns to update and the expressions to update -- * them to. See sqlite3Update() documentation of "pChanges" -- * argument. -- * -- */ -+** An instance of struct TriggerStep is used to store a single SQL statement -+** that is a part of a trigger-program. -+** -+** Instances of struct TriggerStep are stored in a singly linked list (linked -+** using the "pNext" member) referenced by the "step_list" member of the -+** associated struct Trigger instance. The first element of the linked list is -+** the first step of the trigger-program. -+** -+** The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or -+** "SELECT" statement. The meanings of the other members is determined by the -+** value of "op" as follows: -+** -+** (op == TK_INSERT) -+** orconf -> stores the ON CONFLICT algorithm -+** pSelect -> The content to be inserted - either a SELECT statement or -+** a VALUES clause. -+** zTarget -> Dequoted name of the table to insert into. -+** pIdList -> If this is an INSERT INTO ... () VALUES ... -+** statement, then this stores the column-names to be -+** inserted into. -+** pUpsert -> The ON CONFLICT clauses for an Upsert -+** -+** (op == TK_DELETE) -+** zTarget -> Dequoted name of the table to delete from. -+** pWhere -> The WHERE clause of the DELETE statement if one is specified. -+** Otherwise NULL. -+** -+** (op == TK_UPDATE) -+** zTarget -> Dequoted name of the table to update. -+** pWhere -> The WHERE clause of the UPDATE statement if one is specified. -+** Otherwise NULL. -+** pExprList -> A list of the columns to update and the expressions to update -+** them to. See sqlite3Update() documentation of "pChanges" -+** argument. -+** -+** (op == TK_SELECT) -+** pSelect -> The SELECT statement -+** -+** (op == TK_RETURNING) -+** pExprList -> The list of expressions that follow the RETURNING keyword. -+** -+*/ - struct TriggerStep { -- u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ -+ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT, -+ ** or TK_RETURNING */ - u8 orconf; /* OE_Rollback etc. */ - Trigger *pTrig; /* The trigger that this step is a part of */ - Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ - char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ - SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */ - Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ -- ExprList *pExprList; /* SET clause for UPDATE */ -+ ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */ - IdList *pIdList; /* Column names for INSERT */ - Upsert *pUpsert; /* Upsert clauses on an INSERT */ - char *zSpan; /* Original SQL text of this command */ -@@ -18952,22 +20549,21 @@ struct TriggerStep { - }; - - /* --** The following structure contains information used by the sqliteFix... --** routines as they walk the parse tree to make database references --** explicit. -+** Information about a RETURNING clause - */ --typedef struct DbFixer DbFixer; --struct DbFixer { -- Parse *pParse; /* The parsing context. Error messages written here */ -- Schema *pSchema; /* Fix items to this schema */ -- u8 bTemp; /* True for TEMP schema entries */ -- const char *zDb; /* Make sure all objects are contained in this database */ -- const char *zType; /* Type of the container - used for error messages */ -- const Token *pName; /* Name of the container - used for error messages */ -+struct Returning { -+ Parse *pParse; /* The parse that includes the RETURNING clause */ -+ ExprList *pReturnEL; /* List of expressions to return */ -+ Trigger retTrig; /* The transient trigger that implements RETURNING */ -+ TriggerStep retTStep; /* The trigger step */ -+ int iRetCur; /* Transient table holding RETURNING results */ -+ int nRetCol; /* Number of in pReturnEL after expansion */ -+ int iRetReg; /* Register array for holding a row of RETURNING */ -+ char zName[40]; /* Name of trigger: "sqlite_returning_%p" */ - }; - - /* --** An objected used to accumulate the text of a string where we -+** An object used to accumulate the text of a string where we - ** do not necessarily know how big the string will be in the end. - */ - struct sqlite3_str { -@@ -18981,10 +20577,32 @@ struct sqlite3_str { - }; - #define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */ - #define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */ --#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */ -+#define SQLITE_PRINTF_MALLOCED 0x04 /* True if zText is allocated space */ - - #define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0) - -+/* -+** The following object is the header for an "RCStr" or "reference-counted -+** string". An RCStr is passed around and used like any other char* -+** that has been dynamically allocated. The important interface -+** differences: -+** -+** 1. RCStr strings are reference counted. They are deallocated -+** when the reference count reaches zero. -+** -+** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than -+** sqlite3_free() -+** -+** 3. Make a (read-only) copy of a read-only RCStr string using -+** sqlite3RCStrRef(). -+** -+** "String" is in the name, but an RCStr object can also be used to hold -+** binary data. -+*/ -+struct RCStr { -+ u64 nRCRef; /* Number of references */ -+ /* Total structure size should be a multiple of 8 bytes for alignment */ -+}; - - /* - ** A pointer to this structure is used to communicate information -@@ -19003,7 +20621,26 @@ typedef struct { - /* - ** Allowed values for mInitFlags - */ --#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */ -+#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */ -+#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */ -+#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */ -+#define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */ -+ -+/* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled -+** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning -+** parameters are for temporary use during development, to help find -+** optimal values for parameters in the query planner. The should not -+** be used on trunk check-ins. They are a temporary mechanism available -+** for transient development builds only. -+** -+** Tuning parameters are numbered starting with 1. -+*/ -+#define SQLITE_NTUNE 6 /* Should be zero for all trunk check-ins */ -+#ifdef SQLITE_DEBUG -+# define Tuning(X) (sqlite3Config.aTune[(X)-1]) -+#else -+# define Tuning(X) 0 -+#endif - - /* - ** Structure containing global configuration data for the SQLite library. -@@ -19018,6 +20655,9 @@ struct Sqlite3Config { - u8 bUseCis; /* Use covering indices for full-scans */ - u8 bSmallMalloc; /* Avoid large memory allocations if true */ - u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ -+#ifdef SQLITE_DEBUG -+ u8 bJsonSelfcheck; /* Double-check JSON parsing */ -+#endif - int mxStrlen; /* Maximum string length */ - int neverCorrupt; /* Database is always well-formed */ - int szLookaside; /* Default lookaside buffer size */ -@@ -19059,16 +20699,26 @@ struct Sqlite3Config { - void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */ - void *pVdbeBranchArg; /* 1st argument */ - #endif --#ifdef SQLITE_ENABLE_DESERIALIZE -+#ifndef SQLITE_OMIT_DESERIALIZE - sqlite3_int64 mxMemdbSize; /* Default max memdb size */ - #endif - #ifndef SQLITE_UNTESTABLE - int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ -+#endif -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW -+ ** feature is disabled. 0 if rowids can -+ ** occur in views. */ - #endif - int bLocaltimeFault; /* True to fail localtime() calls */ -+ int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ - int iOnceResetThreshold; /* When to reset OP_Once counters */ - u32 szSorterRef; /* Min size in bytes to use sorter-refs */ - unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */ -+ /* vvvv--- must be last ---vvv */ -+#ifdef SQLITE_DEBUG -+ sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */ -+#endif - }; - - /* -@@ -19099,28 +20749,47 @@ struct Walker { - void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ - int walkerDepth; /* Number of subqueries */ - u16 eCode; /* A small processing code */ -+ u16 mWFlags; /* Use-dependent flags */ - union { /* Extra data for callback */ - NameContext *pNC; /* Naming context */ - int n; /* A counter */ - int iCur; /* A cursor number */ - SrcList *pSrcList; /* FROM clause */ -- struct SrcCount *pSrcCount; /* Counting column references */ - struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ -+ struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */ - int *aiCol; /* array of column indexes */ - struct IdxCover *pIdxCover; /* Check for index coverage */ -- 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 */ - struct WhereConst *pConst; /* WHERE clause constants */ - struct RenameCtx *pRename; /* RENAME COLUMN context */ - struct Table *pTab; /* Table of generated column */ -- struct SrcList_item *pSrcItem; /* A single FROM clause item */ -+ struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */ -+ SrcItem *pSrcItem; /* A single FROM clause item */ -+ DbFixer *pFix; /* See sqlite3FixSelect() */ -+ Mem *aMem; /* See sqlite3BtreeCursorHint() */ - } u; - }; - -+/* -+** The following structure contains information used by the sqliteFix... -+** routines as they walk the parse tree to make database references -+** explicit. -+*/ -+struct DbFixer { -+ Parse *pParse; /* The parsing context. Error messages written here */ -+ Walker w; /* Walker object */ -+ Schema *pSchema; /* Fix items to this schema */ -+ u8 bTemp; /* True for TEMP schema entries */ -+ const char *zDb; /* Make sure all objects are contained in this database */ -+ const char *zType; /* Type of the container - used for error messages */ -+ const Token *pName; /* Name of the container - used for error messages */ -+}; -+ - /* Forward declarations */ - SQLITE_PRIVATE int sqlite3WalkExpr(Walker*, Expr*); -+SQLITE_PRIVATE int sqlite3WalkExprNN(Walker*, Expr*); - SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*); - SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*); - SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*); -@@ -19130,11 +20799,18 @@ SQLITE_PRIVATE int sqlite3SelectWalkNoop(Walker*, Select*); - SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker*, Select*); - SQLITE_PRIVATE int sqlite3WalkerDepthIncrease(Walker*,Select*); - SQLITE_PRIVATE void sqlite3WalkerDepthDecrease(Walker*,Select*); -+SQLITE_PRIVATE void sqlite3WalkWinDefnDummyCallback(Walker*,Select*); - - #ifdef SQLITE_DEBUG - SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*); - #endif - -+#ifndef SQLITE_OMIT_CTE -+SQLITE_PRIVATE void sqlite3SelectPopWith(Walker*, Select*); -+#else -+# define sqlite3SelectPopWith 0 -+#endif -+ - /* - ** Return code from the parse-tree walking primitives and their - ** callbacks. -@@ -19144,20 +20820,74 @@ SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*); - #define WRC_Abort 2 /* Abandon the tree walk */ - - /* --** An instance of this structure represents a set of one or more CTEs --** (common table expressions) created by a single WITH clause. -+** A single common table expression -+*/ -+struct Cte { -+ char *zName; /* Name of this CTE */ -+ ExprList *pCols; /* List of explicit column names, or NULL */ -+ Select *pSelect; /* The definition of this CTE */ -+ const char *zCteErr; /* Error message for circular references */ -+ CteUse *pUse; /* Usage information for this CTE */ -+ u8 eM10d; /* The MATERIALIZED flag */ -+}; -+ -+/* -+** Allowed values for the materialized flag (eM10d): -+*/ -+#define M10d_Yes 0 /* AS MATERIALIZED */ -+#define M10d_Any 1 /* Not specified. Query planner's choice */ -+#define M10d_No 2 /* AS NOT MATERIALIZED */ -+ -+/* -+** An instance of the With object represents a WITH clause containing -+** one or more CTEs (common table expressions). - */ - struct With { -- int nCte; /* Number of CTEs in the WITH clause */ -- With *pOuter; /* Containing WITH clause, or NULL */ -- struct Cte { /* For each CTE in the WITH clause.... */ -- char *zName; /* Name of this CTE */ -- ExprList *pCols; /* List of explicit column names, or NULL */ -- Select *pSelect; /* The definition of this CTE */ -- const char *zCteErr; /* Error message for circular references */ -- } a[1]; -+ int nCte; /* Number of CTEs in the WITH clause */ -+ int bView; /* Belongs to the outermost Select of a view */ -+ With *pOuter; /* Containing WITH clause, or NULL */ -+ Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */ -+}; -+ -+/* The size (in bytes) of a With object that can hold as many -+** as N different CTEs. */ -+#define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte)) -+ -+/* -+** The Cte object is not guaranteed to persist for the entire duration -+** of code generation. (The query flattener or other parser tree -+** edits might delete it.) The following object records information -+** about each Common Table Expression that must be preserved for the -+** duration of the parse. -+** -+** The CteUse objects are freed using sqlite3ParserAddCleanup() rather -+** than sqlite3SelectDelete(), which is what enables them to persist -+** until the end of code generation. -+*/ -+struct CteUse { -+ int nUse; /* Number of users of this CTE */ -+ int addrM9e; /* Start of subroutine to compute materialization */ -+ int regRtn; /* Return address register for addrM9e subroutine */ -+ int iCur; /* Ephemeral table holding the materialization */ -+ LogEst nRowEst; /* Estimated number of rows in the table */ -+ u8 eM10d; /* The MATERIALIZED flag */ -+}; -+ -+ -+/* Client data associated with sqlite3_set_clientdata() and -+** sqlite3_get_clientdata(). -+*/ -+struct DbClientData { -+ DbClientData *pNext; /* Next in a linked list */ -+ void *pData; /* The data */ -+ void (*xDestructor)(void*); /* Destructor. Might be NULL */ -+ char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */ - }; - -+/* The size (in bytes) of a DbClientData object that can has a name -+** that is N bytes long, including the zero-terminator. */ -+#define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N)) -+ - #ifdef SQLITE_DEBUG - /* - ** An instance of the TreeView object is used for printing the content of -@@ -19207,7 +20937,7 @@ struct Window { - Window **ppThis; /* Pointer to this object in Select.pWin list */ - Window *pNextWin; /* Next window function belonging to this SELECT */ - Expr *pFilter; /* The FILTER expression */ -- FuncDef *pFunc; /* The function */ -+ FuncDef *pWFunc; /* The function */ - int iEphCsr; /* Partition buffer or Peer buffer */ - int regAccum; /* Accumulator */ - int regResult; /* Interim result */ -@@ -19224,6 +20954,9 @@ struct Window { - ** due to the SQLITE_SUBTYPE flag */ - }; - -+SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow); -+SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal); -+ - #ifndef SQLITE_OMIT_WINDOWFUNC - SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); - SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*); -@@ -19231,11 +20964,10 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p); - SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); - SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*); - SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin); --SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*, int); -+SQLITE_PRIVATE int sqlite3WindowCompare(const Parse*, const Window*, const Window*, int); - SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Select*); - SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int); - SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*); --SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, struct SrcList_item*); - SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*); - SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p); - SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p); -@@ -19305,15 +21037,6 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); - # define SQLITE_ENABLE_FTS3 1 - #endif - --/* --** The ctype.h header is needed for non-ASCII systems. It is also --** needed by FTS3 when FTS3 is included in the amalgamation. --*/ --#if !defined(SQLITE_ASCII) || \ -- (defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_AMALGAMATION)) --# include --#endif -- - /* - ** The following macros mimic the standard library functions toupper(), - ** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The -@@ -19328,6 +21051,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); - # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) - # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) - # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) -+# define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) -+# define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) - #else - # define sqlite3Toupper(x) toupper((unsigned char)(x)) - # define sqlite3Isspace(x) isspace((unsigned char)(x)) -@@ -19337,6 +21062,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); - # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) - # define sqlite3Tolower(x) tolower((unsigned char)(x)) - # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') -+# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') -+# define sqlite3JsonId2(x) sqlite3IsIdChar(x) - #endif - SQLITE_PRIVATE int sqlite3IsIdChar(u8); - -@@ -19364,8 +21091,9 @@ SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64); - SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64); - SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*); - SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*); --SQLITE_PRIVATE int sqlite3MallocSize(void*); --SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*); -+SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3*, void*); -+SQLITE_PRIVATE int sqlite3MallocSize(const void*); -+SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, const void*); - SQLITE_PRIVATE void *sqlite3PageMalloc(int); - SQLITE_PRIVATE void sqlite3PageFree(void*); - SQLITE_PRIVATE void sqlite3MemSetDefault(void); -@@ -19384,12 +21112,14 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void); - */ - #ifdef SQLITE_USE_ALLOCA - # define sqlite3StackAllocRaw(D,N) alloca(N) --# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N) -+# define sqlite3StackAllocRawNN(D,N) alloca(N) - # define sqlite3StackFree(D,P) -+# define sqlite3StackFreeNN(D,P) - #else - # define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N) --# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N) -+# define sqlite3StackAllocRawNN(D,N) sqlite3DbMallocRawNN(D,N) - # define sqlite3StackFree(D,P) sqlite3DbFree(D,P) -+# define sqlite3StackFreeNN(D,P) sqlite3DbFreeNN(D,P) - #endif - - /* Do not allow both MEMSYS5 and MEMSYS3 to be defined together. If they -@@ -19437,10 +21167,13 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*); - # define EXP754 (((u64)0x7ff)<<52) - # define MAN754 ((((u64)1)<<52)-1) - # define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0) -+# define IsOvfl(X) (((X)&EXP754)==EXP754) - SQLITE_PRIVATE int sqlite3IsNaN(double); -+SQLITE_PRIVATE int sqlite3IsOverflow(double); - #else --# define IsNaN(X) 0 --# define sqlite3IsNaN(X) 0 -+# define IsNaN(X) 0 -+# define sqlite3IsNaN(X) 0 -+# define sqlite3IsOVerflow(X) 0 - #endif - - /* -@@ -19453,6 +21186,20 @@ struct PrintfArguments { - sqlite3_value **apArg; /* The argument values */ - }; - -+/* -+** An instance of this object receives the decoding of a floating point -+** value into an approximate decimal representation. -+*/ -+struct FpDecode { -+ char sign; /* '+' or '-' */ -+ char isSpecial; /* 1: Infinity 2: NaN */ -+ int n; /* Significant digits in the decode */ -+ int iDP; /* Location of the decimal point */ -+ char *z; /* Start of significant digits */ -+ char zBuf[24]; /* Storage for significant digits */ -+}; -+ -+SQLITE_PRIVATE void sqlite3FpDecode(FpDecode*,double,int,int); - SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); - SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); - #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) -@@ -19463,33 +21210,75 @@ SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*); - #endif - - #if defined(SQLITE_DEBUG) -+SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView*, const char *zFormat, ...); - SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8); - SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*); - SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*); -+SQLITE_PRIVATE void sqlite3TreeViewBareIdList(TreeView*, const IdList*, const char*); -+SQLITE_PRIVATE void sqlite3TreeViewIdList(TreeView*, const IdList*, u8, const char*); -+SQLITE_PRIVATE void sqlite3TreeViewColumnList(TreeView*, const Column*, int, u8); - SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView*, const SrcList*); - SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8); - SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8); -+SQLITE_PRIVATE void sqlite3TreeViewUpsert(TreeView*, const Upsert*, u8); -+#if TREETRACE_ENABLED -+SQLITE_PRIVATE void sqlite3TreeViewDelete(const With*, const SrcList*, const Expr*, -+ const ExprList*,const Expr*, const Trigger*); -+SQLITE_PRIVATE void sqlite3TreeViewInsert(const With*, const SrcList*, -+ const IdList*, const Select*, const ExprList*, -+ int, const Upsert*, const Trigger*); -+SQLITE_PRIVATE void sqlite3TreeViewUpdate(const With*, const SrcList*, const ExprList*, -+ const Expr*, int, const ExprList*, const Expr*, -+ const Upsert*, const Trigger*); -+#endif -+#ifndef SQLITE_OMIT_TRIGGER -+SQLITE_PRIVATE void sqlite3TreeViewTriggerStep(TreeView*, const TriggerStep*, u8, u8); -+SQLITE_PRIVATE void sqlite3TreeViewTrigger(TreeView*, const Trigger*, u8, u8); -+#endif - #ifndef SQLITE_OMIT_WINDOWFUNC - SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView*, const Window*, u8); - SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8); - #endif -+SQLITE_PRIVATE void sqlite3ShowExpr(const Expr*); -+SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList*); -+SQLITE_PRIVATE void sqlite3ShowIdList(const IdList*); -+SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList*); -+SQLITE_PRIVATE void sqlite3ShowSelect(const Select*); -+SQLITE_PRIVATE void sqlite3ShowWith(const With*); -+SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert*); -+#ifndef SQLITE_OMIT_TRIGGER -+SQLITE_PRIVATE void sqlite3ShowTriggerStep(const TriggerStep*); -+SQLITE_PRIVATE void sqlite3ShowTriggerStepList(const TriggerStep*); -+SQLITE_PRIVATE void sqlite3ShowTrigger(const Trigger*); -+SQLITE_PRIVATE void sqlite3ShowTriggerList(const Trigger*); -+#endif -+#ifndef SQLITE_OMIT_WINDOWFUNC -+SQLITE_PRIVATE void sqlite3ShowWindow(const Window*); -+SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window*); -+#endif - #endif -- - - SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*); -+SQLITE_PRIVATE void sqlite3ProgressCheck(Parse*); - SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); - SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); - SQLITE_PRIVATE void sqlite3Dequote(char*); - SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); -+SQLITE_PRIVATE void sqlite3DequoteToken(Token*); -+SQLITE_PRIVATE void sqlite3DequoteNumber(Parse*, Expr*); - SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*); - SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); --SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **); -+SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*); - SQLITE_PRIVATE void sqlite3FinishCoding(Parse*); - SQLITE_PRIVATE int sqlite3GetTempReg(Parse*); - SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); - SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); - SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); - SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); -+SQLITE_PRIVATE void sqlite3TouchRegister(Parse*,int); -+#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) -+SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse*,int); -+#endif - #ifdef SQLITE_DEBUG - SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int); - #endif -@@ -19500,17 +21289,23 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); - SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*); - SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*); - SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); --SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*, int); --SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,Expr*,FuncDef*); -+SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); -+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); -+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*); -+SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); - SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); - SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); -+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*); -+SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse*, Expr*); - SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); - SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); - SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); -+SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*); - SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int); --SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); -+SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); - SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); - SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); -+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*); - SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); - SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); - SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); -@@ -19524,13 +21319,18 @@ SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*); - SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int); - SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*); - SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*); -+SQLITE_PRIVATE void sqlite3ColumnSetExpr(Parse*,Table*,Column*,Expr*); -+SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table*,Column*); -+SQLITE_PRIVATE void sqlite3ColumnSetColl(sqlite3*,Column*,const char*zColl); -+SQLITE_PRIVATE const char *sqlite3ColumnColl(Column*); - SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*); -+SQLITE_PRIVATE void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect); - SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); --SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char); -+SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char); - SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); - SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int); - SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*); --SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16); -+SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index*, int); - #ifdef SQLITE_OMIT_GENERATED_COLUMNS - # define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */ - # define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */ -@@ -19544,14 +21344,15 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table*, Column*); - #else - # define sqlite3ColumnPropertiesFromName(T,C) /* no-op */ - #endif --SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*,Token*); -+SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token,Token); - SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int); - SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int); --SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*); -+SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*); - SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*); - SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); - SQLITE_PRIVATE void sqlite3AddGenerated(Parse*,Expr*,Token*); --SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); -+SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u32,Select*); -+SQLITE_PRIVATE void sqlite3AddReturning(Parse*,ExprList*); - SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, - sqlite3_vfs**,char**,char **); - #define sqlite3CodecQueryParameters(A,B,C) 0 -@@ -19595,6 +21396,7 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask); - SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int); - SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int); - SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*); -+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*); - SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*); - #ifndef SQLITE_OMIT_AUTOINCREMENT - SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse); -@@ -19613,16 +21415,20 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); - SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); - SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); - SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); -+SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3*,Subquery*); -+SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3*,SrcItem*); -+SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery(Parse*, SrcItem*, Select*, int); - SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, -- Token*, Select*, Expr*, IdList*); -+ Token*, Select*, OnOrUsing*); - SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); - SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); --SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, struct SrcList_item *); --SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*); -+SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *); -+SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*); - SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); - SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); -+SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*); - SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); --SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); -+SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,int,int,char**); - SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, - Expr*, int, int, u8); - SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); -@@ -19630,21 +21436,25 @@ SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); - SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, - Expr*,ExprList*,u32,Expr*); - SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*); -+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*); - SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*); --SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int); -+SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*); - SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); - #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) - SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); - #endif -+SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*); - SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); - SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, - Upsert*); --SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); -+SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, -+ ExprList*,Select*,u16,int); - SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*); - SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*); - SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*); - SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*); - SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo*); -+SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*); - SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*); - SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*); - SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*); -@@ -19657,13 +21467,15 @@ SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int - SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); - SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); - SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int); -+SQLITE_PRIVATE void sqlite3ExprToRegister(Expr *pExpr, int iReg); - SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int); - #ifndef SQLITE_OMIT_GENERATED_COLUMNS --SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Column*, int); -+SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); - #endif - SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int); - SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int); - SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int); -+SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int); - SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); - SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); - SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); -@@ -19678,23 +21490,24 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*); - #define LOCATE_VIEW 0x01 - #define LOCATE_NOERR 0x02 - SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); --SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *); -+SQLITE_PRIVATE const char *sqlite3PreferredTableName(const char*); -+SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *); - SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*); - SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); - SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); - SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*,Expr*); - SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); --SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*); --SQLITE_PRIVATE int sqlite3ExprCompare(Parse*,Expr*, Expr*, int); --SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*, Expr*, int); --SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int); --SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); --SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int); -+SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, const Token*); -+SQLITE_PRIVATE int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int); -+SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*,Expr*,int); -+SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList*,const ExprList*, int); -+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); -+SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int,int); - SQLITE_PRIVATE void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); - SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); - SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); - SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); --SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); -+SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse*, Expr*, SrcList*); - SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*); - #ifndef SQLITE_UNTESTABLE - SQLITE_PRIVATE void sqlite3PrngSaveState(void); -@@ -19711,18 +21524,18 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*); - SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char*); - SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*); - SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*); --SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); --SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); -+SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse*,Expr*); - SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); - SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); --SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); -+SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int); - #ifdef SQLITE_ENABLE_CURSOR_HINTS - SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); - #endif --SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); -+SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*, Parse*); - SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); - SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); - SQLITE_PRIVATE int sqlite3IsRowid(const char*); -+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); - SQLITE_PRIVATE void sqlite3GenerateRowDelete( - Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); - SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); -@@ -19744,20 +21557,27 @@ SQLITE_PRIVATE void sqlite3MayAbort(Parse*); - SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); - SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*); - SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*); --SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int); --SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); --SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); --SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*); --SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int); -+SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,const Expr*,int); -+SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,const ExprList*,int); -+SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,const SrcList*,int); -+SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*); -+SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int); - SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); - SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); - SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); -+SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int); -+SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32); - SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); - SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); -+SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void); - SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); -+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) -+SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3*); -+#endif - SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); - SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*); - SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int); -+SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p); - - #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) - SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); -@@ -19806,7 +21626,9 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*); - #endif - - SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*); --SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr*,int); -+SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol); -+SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem*,int); -+SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr*,int,u32); - SQLITE_PRIVATE void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); - SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse*, int); - #ifndef SQLITE_OMIT_AUTHORIZATION -@@ -19828,29 +21650,25 @@ SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Tok - SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); - SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); - SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); --SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); - SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); -+ - SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); --SQLITE_PRIVATE void sqlite3Int64ToText(i64,char*); -+SQLITE_PRIVATE i64 sqlite3RealToI64(double); -+SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*); - SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); - SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); - SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); - SQLITE_PRIVATE int sqlite3Atoi(const char*); - #ifndef SQLITE_OMIT_UTF16 --SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); -+SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nByte, int nChar); - #endif - SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); - SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); -+SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); - SQLITE_PRIVATE LogEst sqlite3LogEst(u64); - SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst); --#ifndef SQLITE_OMIT_VIRTUALTABLE - SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double); --#endif --#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ -- defined(SQLITE_ENABLE_STAT4) || \ -- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) - SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst); --#endif - SQLITE_PRIVATE VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int); - SQLITE_PRIVATE const char *sqlite3VListNumToName(VList*,int); - SQLITE_PRIVATE int sqlite3VListNameToNum(VList*,const char*,int); -@@ -19882,17 +21700,22 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v); - - - SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*); -+SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3*,const Table*); - SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int); - SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2); - SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); --SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table*,int); -+SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table*,int); - SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr); -+SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr); - SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); - SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*); - SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); - SQLITE_PRIVATE void sqlite3Error(sqlite3*,int); -+SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*); - SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int); -+#if !defined(SQLITE_OMIT_BLOB_LITERAL) - SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); -+#endif - SQLITE_PRIVATE u8 sqlite3HexToInt(int h); - SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); - -@@ -19900,8 +21723,11 @@ SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); - SQLITE_PRIVATE const char *sqlite3ErrName(int); - #endif - --#ifdef SQLITE_ENABLE_DESERIALIZE -+#ifndef SQLITE_OMIT_DESERIALIZE - SQLITE_PRIVATE int sqlite3MemdbInit(void); -+SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs*); -+#else -+# define sqlite3IsMemdb(X) 0 - #endif - - SQLITE_PRIVATE const char *sqlite3ErrStr(int); -@@ -19913,14 +21739,14 @@ SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8); - SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); - SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); - SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); --SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); --SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); -+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(const Parse *pParse, Expr*, const Token*, int); -+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(const Parse*,Expr*,const char*); - SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*); - SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr*); - SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); - SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*); - SQLITE_PRIVATE int sqlite3CheckObjectName(Parse*, const char*,const char*,const char*); --SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int); -+SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, i64); - SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64); - SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64); - SQLITE_PRIVATE int sqlite3MulInt64(i64*,i64); -@@ -19933,6 +21759,7 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*); - SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8); - - SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8); -+SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); - SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8); - SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, - void(*)(void*)); -@@ -19945,16 +21772,21 @@ SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); - #ifndef SQLITE_OMIT_UTF16 - SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); - #endif --SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); -+SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, const Expr *, u8, u8, sqlite3_value **); - SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); - #ifndef SQLITE_AMALGAMATION - SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[]; - SQLITE_PRIVATE const char sqlite3StrBINARY[]; -+SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[]; -+SQLITE_PRIVATE const char sqlite3StdTypeAffinity[]; -+SQLITE_PRIVATE const char *sqlite3StdType[]; - SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; -+SQLITE_PRIVATE const unsigned char *sqlite3aLTb; -+SQLITE_PRIVATE const unsigned char *sqlite3aEQb; -+SQLITE_PRIVATE const unsigned char *sqlite3aGTb; - SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; - SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; - SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; --SQLITE_API extern u32 sqlite3_unsupported_selecttrace; - #ifndef SQLITE_OMIT_WSD - SQLITE_PRIVATE int sqlite3PendingByte; - #endif -@@ -19973,12 +21805,14 @@ SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); - SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int); - SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*); - SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); -+SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, SrcItem*); - SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); - SQLITE_PRIVATE int sqlite3MatchEName( - const struct ExprList_item*, - const char*, - const char*, -- const char* -+ const char*, -+ int* - ); - SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*); - SQLITE_PRIVATE u8 sqlite3StrIHash(const char*); -@@ -19990,8 +21824,9 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const - SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); - SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); - SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *); --SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse*, void*, Token*); --SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom); -+SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse*, SrcList*, const Token*); -+SQLITE_PRIVATE const void *sqlite3RenameTokenMap(Parse*, const void*, const Token*); -+SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, const void *pTo, const void *pFrom); - SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*); - SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*); - SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); -@@ -20013,6 +21848,7 @@ SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*); - SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*); - SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*); - SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int); -+SQLITE_PRIVATE const char *sqlite3SelectOpName(int); - SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*); - - #ifdef SQLITE_DEBUG -@@ -20027,15 +21863,25 @@ SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, - FuncDestructor *pDestructor - ); - SQLITE_PRIVATE void sqlite3NoopDestructor(void*); --SQLITE_PRIVATE void sqlite3OomFault(sqlite3*); -+SQLITE_PRIVATE void *sqlite3OomFault(sqlite3*); - SQLITE_PRIVATE void sqlite3OomClear(sqlite3*); - SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); - SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); - -+SQLITE_PRIVATE char *sqlite3RCStrRef(char*); -+SQLITE_PRIVATE void sqlite3RCStrUnref(void*); -+SQLITE_PRIVATE char *sqlite3RCStrNew(u64); -+SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64); -+ - SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); -+SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, i64); - SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); -+SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum*, u8); -+SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*); - SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int); - SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); -+SQLITE_PRIVATE void sqlite3RecordErrorByteOffset(sqlite3*,const char*); -+SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3*,const Expr*); - - SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *); - SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); -@@ -20086,7 +21932,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*); - #endif - - #ifdef SQLITE_OMIT_VIRTUALTABLE --# define sqlite3VtabClear(Y) -+# define sqlite3VtabClear(D,T) - # define sqlite3VtabSync(X,Y) SQLITE_OK - # define sqlite3VtabRollback(X) - # define sqlite3VtabCommit(X) -@@ -20123,9 +21969,11 @@ SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db); - #ifndef SQLITE_OMIT_VIRTUALTABLE - SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName); - SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3*,Table*,const char*); -+SQLITE_PRIVATE void sqlite3MarkAllShadowTablesOf(sqlite3*, Table*); - #else - # define sqlite3ShadowTableName(A,B) 0 - # define sqlite3IsShadowTableOf(A,B,C) 0 -+# define sqlite3MarkAllShadowTablesOf(A,B) - #endif - SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*); - SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*); -@@ -20138,11 +21986,15 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); - SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*); - SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); - SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); -+ - SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); -+SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse*); - SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); - SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); - SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); --SQLITE_PRIVATE void sqlite3ParserReset(Parse*); -+SQLITE_PRIVATE void sqlite3ParseObjectInit(Parse*,sqlite3*); -+SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse*); -+SQLITE_PRIVATE void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*); - #ifdef SQLITE_ENABLE_NORMALIZE - SQLITE_PRIVATE char *sqlite3Normalize(Vdbe*, const char*); - #endif -@@ -20157,23 +22009,33 @@ SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); - SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); - #endif - #ifndef SQLITE_OMIT_CTE --SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*); -+SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); -+SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); -+SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); - SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); --SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8); -+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3*,void*); -+SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); - #else --#define sqlite3WithPush(x,y,z) --#define sqlite3WithDelete(x,y) -+# define sqlite3CteNew(P,T,E,S) ((void*)0) -+# define sqlite3CteDelete(D,C) -+# define sqlite3CteWithAdd(P,W,C) ((void*)0) -+# define sqlite3WithDelete(x,y) -+# define sqlite3WithPush(x,y,z) ((void*)0) - #endif - #ifndef SQLITE_OMIT_UPSERT --SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*); -+SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); - SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*); - SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); --SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*); -+SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*); - SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); -+SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*); -+SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*); - #else --#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0) -+#define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0) - #define sqlite3UpsertDelete(x,y) --#define sqlite3UpsertDup(x,y) ((Upsert*)0) -+#define sqlite3UpsertDup(x,y) ((Upsert*)0) -+#define sqlite3UpsertOfIndex(x,y) ((Upsert*)0) -+#define sqlite3UpsertNextIsIPK(x) 0 - #endif - - -@@ -20191,6 +22053,7 @@ SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int - SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int); - SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*); - SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *); -+SQLITE_PRIVATE void sqlite3FkClearTriggerCache(sqlite3*,int); - #else - #define sqlite3FkActions(a,b,c,d,e,f) - #define sqlite3FkCheck(a,b,c,d,e,f) -@@ -20198,6 +22061,7 @@ SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *); - #define sqlite3FkOldmask(a,b) 0 - #define sqlite3FkRequired(a,b,c,d) 0 - #define sqlite3FkReferences(a) 0 -+ #define sqlite3FkClearTriggerCache(a,b) - #endif - #ifndef SQLITE_OMIT_FOREIGN_KEY - SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*); -@@ -20255,12 +22119,13 @@ SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *); - - SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); - #if SQLITE_MAX_EXPR_DEPTH>0 --SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *); -+SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *); - SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); - #else - #define sqlite3SelectExprHeight(x) 0 - #define sqlite3ExprCheckHeight(x,y) - #endif -+SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr*,int); - - SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*); - SQLITE_PRIVATE void sqlite3Put4byte(u8*, u32); -@@ -20326,8 +22191,8 @@ SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...); - */ - #ifdef SQLITE_MEMDEBUG - SQLITE_PRIVATE void sqlite3MemdebugSetType(void*,u8); --SQLITE_PRIVATE int sqlite3MemdebugHasType(void*,u8); --SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8); -+SQLITE_PRIVATE int sqlite3MemdebugHasType(const void*,u8); -+SQLITE_PRIVATE int sqlite3MemdebugNoType(const void*,u8); - #else - # define sqlite3MemdebugSetType(X,Y) /* no-op */ - # define sqlite3MemdebugHasType(X,Y) 1 -@@ -20352,22 +22217,38 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3*); - SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*); - #endif - --SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr); --SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr); -+SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr); -+SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr); - SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr*, int); --SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int); -+SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int); - SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse*, Expr*); - - #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS - SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt); - #endif - -+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) -+SQLITE_PRIVATE int sqlite3KvvfsInit(void); -+#endif -+ -+#if defined(VDBE_PROFILE) \ -+ || defined(SQLITE_PERFORMANCE_TRACE) \ -+ || defined(SQLITE_ENABLE_STMT_SCANSTATUS) -+SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void); -+#endif -+ -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+# define IS_STMT_SCANSTATUS(db) (db->flags & SQLITE_StmtScanStatus) -+#else -+# define IS_STMT_SCANSTATUS(db) 0 -+#endif -+ - #endif /* SQLITEINT_H */ - - /************** End of sqliteInt.h *******************************************/ --/************** Begin file global.c ******************************************/ -+/************** Begin file os_common.h ***************************************/ - /* --** 2008 June 13 -+** 2004 May 22 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: -@@ -20376,321 +22257,108 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt); - ** May you find forgiveness for yourself and forgive others. - ** May you share freely, never taking more than you give. - ** --************************************************************************* -+****************************************************************************** - ** --** This file contains definitions of global variables and constants. --*/ --/* #include "sqliteInt.h" */ -- --/* An array to map all upper-case characters into their corresponding --** lower-case character. -+** This file contains macros and a little bit of code that is common to -+** all of the platform-specific files (os_*.c) and is #included into those -+** files. - ** --** SQLite only considers US-ASCII (or EBCDIC) characters. We do not --** handle case conversions for the UTF character set since the tables --** involved are nearly as big or bigger than SQLite itself. -+** This file should be #included by the os_*.c files only. It is not a -+** general purpose header file. - */ --SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { --#ifdef SQLITE_ASCII -- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -- 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -- 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -- 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, -- 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, -- 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, -- 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, -- 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, -- 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, -- 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, -- 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, -- 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, -- 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, -- 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, -- 252,253,254,255 --#endif --#ifdef SQLITE_EBCDIC -- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ -- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ -- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ -- 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ -- 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ -- 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ -- 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */ -- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */ -- 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ -- 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */ -- 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ -- 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ -- 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ -- 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ -- 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ -- 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ --#endif --}; -+#ifndef _OS_COMMON_H_ -+#define _OS_COMMON_H_ - - /* --** The following 256 byte lookup table is used to support SQLites built-in --** equivalents to the following standard library functions: --** --** isspace() 0x01 --** isalpha() 0x02 --** isdigit() 0x04 --** isalnum() 0x06 --** isxdigit() 0x08 --** toupper() 0x20 --** SQLite identifier character 0x40 --** Quote character 0x80 --** --** Bit 0x20 is set if the mapped character requires translation to upper --** case. i.e. if the character is a lower-case ASCII character. --** If x is a lower-case ASCII character, then its upper-case equivalent --** is (x - 0x20). Therefore toupper() can be implemented as: --** --** (x & ~(map[x]&0x20)) --** --** The equivalent of tolower() is implemented using the sqlite3UpperToLower[] --** array. tolower() is used more often than toupper() by SQLite. --** --** Bit 0x40 is set if the character is non-alphanumeric and can be used in an --** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any --** non-ASCII UTF character. Hence the test for whether or not a character is --** part of an identifier is 0x46. --*/ --SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { -- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */ -- 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ -- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ -- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ -- 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */ -- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ -- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ -- 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ -- -- 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ -- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ -- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ -- 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ -- 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ -- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ -- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ -- 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ -- -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ -- -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ -- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ --}; -- --/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards --** compatibility for legacy applications, the URI filename capability is --** disabled by default. --** --** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled --** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. --** --** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally --** disabled. The default value may be changed by compiling with the --** SQLITE_USE_URI symbol defined. --*/ --#ifndef SQLITE_USE_URI --# define SQLITE_USE_URI 0 --#endif -- --/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the --** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if --** that compile-time option is omitted. --*/ --#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN) --# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 --#else --# if !SQLITE_ALLOW_COVERING_INDEX_SCAN --# error "Compile-time disabling of covering index scan using the\ -- -DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\ -- Contact SQLite developers if this is a problem for you, and\ -- delete this #error macro to continue with your build." --# endif --#endif -- --/* The minimum PMA size is set to this value multiplied by the database --** page size in bytes. --*/ --#ifndef SQLITE_SORTER_PMASZ --# define SQLITE_SORTER_PMASZ 250 --#endif -- --/* Statement journals spill to disk when their size exceeds the following --** threshold (in bytes). 0 means that statement journals are created and --** written to disk immediately (the default behavior for SQLite versions --** before 3.12.0). -1 means always keep the entire statement journal in --** memory. (The statement journal is also always held entirely in memory --** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this --** setting.) -+** At least two bugs have slipped in because we changed the MEMORY_DEBUG -+** macro to SQLITE_DEBUG and some older makefiles have not yet made the -+** switch. The following code should catch this problem at compile-time. - */ --#ifndef SQLITE_STMTJRNL_SPILL --# define SQLITE_STMTJRNL_SPILL (64*1024) -+#ifdef MEMORY_DEBUG -+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." - #endif - - /* --** The default lookaside-configuration, the format "SZ,N". SZ is the --** number of bytes in each lookaside slot (should be a multiple of 8) --** and N is the number of slots. The lookaside-configuration can be --** changed as start-time using sqlite3_config(SQLITE_CONFIG_LOOKASIDE) --** or at run-time for an individual database connection using --** sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE); --** --** With the two-size-lookaside enhancement, less lookaside is required. --** The default configuration of 1200,40 actually provides 30 1200-byte slots --** and 93 128-byte slots, which is more lookaside than is available --** using the older 1200,100 configuration without two-size-lookaside. -+** Macros for performance tracing. Normally turned off. Only works -+** on i486 hardware. - */ --#ifndef SQLITE_DEFAULT_LOOKASIDE --# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE --# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */ --# else --# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */ --# endif --#endif -- -+#ifdef SQLITE_PERFORMANCE_TRACE - --/* The default maximum size of an in-memory database created using --** sqlite3_deserialize() --*/ --#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE --# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 -+static sqlite_uint64 g_start; -+static sqlite_uint64 g_elapsed; -+#define TIMER_START g_start=sqlite3Hwtime() -+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start -+#define TIMER_ELAPSED g_elapsed -+#else -+#define TIMER_START -+#define TIMER_END -+#define TIMER_ELAPSED ((sqlite_uint64)0) - #endif - - /* --** The following singleton contains the global configuration for --** the SQLite library. -+** If we compile with the SQLITE_TEST macro set, then the following block -+** of code will give us the ability to simulate a disk I/O error. This -+** is used for testing the I/O recovery logic. - */ --SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { -- SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ -- 1, /* bCoreMutex */ -- SQLITE_THREADSAFE==1, /* bFullMutex */ -- SQLITE_USE_URI, /* bOpenUri */ -- SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ -- 0, /* bSmallMalloc */ -- 1, /* bExtraSchemaChecks */ -- 0x7ffffffe, /* mxStrlen */ -- 0, /* neverCorrupt */ -- SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ -- SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ -- {0,0,0,0,0,0,0,0}, /* m */ -- {0,0,0,0,0,0,0,0,0}, /* mutex */ -- {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ -- (void*)0, /* pHeap */ -- 0, /* nHeap */ -- 0, 0, /* mnHeap, mxHeap */ -- SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ -- SQLITE_MAX_MMAP_SIZE, /* mxMmap */ -- (void*)0, /* pPage */ -- 0, /* szPage */ -- SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ -- 0, /* mxParserStack */ -- 0, /* sharedCacheEnabled */ -- SQLITE_SORTER_PMASZ, /* szPma */ -- /* All the rest should always be initialized to zero */ -- 0, /* isInit */ -- 0, /* inProgress */ -- 0, /* isMutexInit */ -- 0, /* isMallocInit */ -- 0, /* isPCacheInit */ -- 0, /* nRefInitMutex */ -- 0, /* pInitMutex */ -- 0, /* xLog */ -- 0, /* pLogArg */ --#ifdef SQLITE_ENABLE_SQLLOG -- 0, /* xSqllog */ -- 0, /* pSqllogArg */ --#endif --#ifdef SQLITE_VDBE_COVERAGE -- 0, /* xVdbeBranch */ -- 0, /* pVbeBranchArg */ --#endif --#ifdef SQLITE_ENABLE_DESERIALIZE -- SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ --#endif --#ifndef SQLITE_UNTESTABLE -- 0, /* xTestCallback */ --#endif -- 0, /* bLocaltimeFault */ -- 0x7ffffffe, /* iOnceResetThreshold */ -- SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ -- 0, /* iPrngSeed */ --}; -+#if defined(SQLITE_TEST) -+SQLITE_API extern int sqlite3_io_error_hit; -+SQLITE_API extern int sqlite3_io_error_hardhit; -+SQLITE_API extern int sqlite3_io_error_pending; -+SQLITE_API extern int sqlite3_io_error_persist; -+SQLITE_API extern int sqlite3_io_error_benign; -+SQLITE_API extern int sqlite3_diskfull_pending; -+SQLITE_API extern int sqlite3_diskfull; -+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) -+#define SimulateIOError(CODE) \ -+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ -+ || sqlite3_io_error_pending-- == 1 ) \ -+ { local_ioerr(); CODE; } -+static void local_ioerr(){ -+ IOTRACE(("IOERR\n")); -+ sqlite3_io_error_hit++; -+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; -+} -+#define SimulateDiskfullError(CODE) \ -+ if( sqlite3_diskfull_pending ){ \ -+ if( sqlite3_diskfull_pending == 1 ){ \ -+ local_ioerr(); \ -+ sqlite3_diskfull = 1; \ -+ sqlite3_io_error_hit = 1; \ -+ CODE; \ -+ }else{ \ -+ sqlite3_diskfull_pending--; \ -+ } \ -+ } -+#else -+#define SimulateIOErrorBenign(X) -+#define SimulateIOError(A) -+#define SimulateDiskfullError(A) -+#endif /* defined(SQLITE_TEST) */ - - /* --** Hash table for global functions - functions common to all --** database connections. After initialization, this table is --** read-only. -+** When testing, keep a count of the number of open files. - */ --SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; -+#if defined(SQLITE_TEST) -+SQLITE_API extern int sqlite3_open_file_count; -+#define OpenCounter(X) sqlite3_open_file_count+=(X) -+#else -+#define OpenCounter(X) -+#endif /* defined(SQLITE_TEST) */ - --#ifdef VDBE_PROFILE --/* --** The following performance counter can be used in place of --** sqlite3Hwtime() for profiling. This is a no-op on standard builds. --*/ --SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt = 0; --#endif -+#endif /* !defined(_OS_COMMON_H_) */ - --/* --** The value of the "pending" byte must be 0x40000000 (1 byte past the --** 1-gibabyte boundary) in a compatible database. SQLite never uses --** the database page that contains the pending byte. It never attempts --** to read or write that page. The pending byte page is set aside --** for use by the VFS layers as space for managing file locks. --** --** During testing, it is often desirable to move the pending byte to --** a different position in the file. This allows code that has to --** deal with the pending byte to run on files that are much smaller --** than 1 GiB. The sqlite3_test_control() interface can be used to --** move the pending byte. -+/************** End of os_common.h *******************************************/ -+/************** Begin file ctime.c *******************************************/ -+/* DO NOT EDIT! -+** This file is automatically generated by the script in the canonical -+** SQLite source tree at tool/mkctimec.tcl. - ** --** IMPORTANT: Changing the pending byte to any value other than --** 0x40000000 results in an incompatible database file format! --** Changing the pending byte during operation will result in undefined --** and incorrect behavior. --*/ --#ifndef SQLITE_OMIT_WSD --SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; --#endif -- --/* --** Flags for select tracing and the ".selecttrace" macro of the CLI --*/ --SQLITE_API u32 sqlite3_unsupported_selecttrace = 0; -- --/* #include "opcodes.h" */ --/* --** Properties of opcodes. The OPFLG_INITIALIZER macro is --** created by mkopcodeh.awk during compilation. Data is obtained --** from the comments following the "case OP_xxxx:" statements in --** the vdbe.c file. -+** To modify this header, edit any of the various lists in that script -+** which specify categories of generated conditionals in this file. - */ --SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; -- --/* --** Name of the default collating sequence --*/ --SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY"; - --/************** End of global.c **********************************************/ --/************** Begin file status.c ******************************************/ - /* --** 2008 June 18 -+** 2010 February 23 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: -@@ -20701,444 +22369,1728 @@ SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY"; - ** - ************************************************************************* - ** --** This module implements the sqlite3_status() interface and related --** functionality. --*/ --/* #include "sqliteInt.h" */ --/************** Include vdbeInt.h in the middle of status.c ******************/ --/************** Begin file vdbeInt.h *****************************************/ --/* --** 2003 September 6 --** --** 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 is the header file for information that is private to the --** VDBE. This information used to all be at the top of the single --** source code file "vdbe.c". When that file became too big (over --** 6000 lines long) it was split up into several smaller files and --** this header information was factored out. --*/ --#ifndef SQLITE_VDBEINT_H --#define SQLITE_VDBEINT_H -- --/* --** The maximum number of times that a statement will try to reparse --** itself before giving up and returning SQLITE_SCHEMA. -+** This file implements routines used to report what compile-time options -+** SQLite was built with. - */ --#ifndef SQLITE_MAX_SCHEMA_RETRY --# define SQLITE_MAX_SCHEMA_RETRY 50 --#endif -+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */ - - /* --** VDBE_DISPLAY_P4 is true or false depending on whether or not the --** "explain" P4 display logic is enabled. -+** Include the configuration header output by 'configure' if we're using the -+** autoconf-based build - */ --#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ -- || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ -- || defined(SQLITE_ENABLE_BYTECODE_VTAB) --# define VDBE_DISPLAY_P4 1 --#else --# define VDBE_DISPLAY_P4 0 -+#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H) -+/* #include "sqlite_cfg.h" */ -+#define SQLITECONFIG_H 1 - #endif - --/* --** SQL is translated into a sequence of instructions to be --** executed by a virtual machine. Each instruction is an instance --** of the following structure. --*/ --typedef struct VdbeOp Op; -- --/* --** Boolean values --*/ --typedef unsigned Bool; -- --/* Opaque type used by code in vdbesort.c */ --typedef struct VdbeSorter VdbeSorter; -- --/* Elements of the linked list at Vdbe.pAuxData */ --typedef struct AuxData AuxData; -+/* These macros are provided to "stringify" the value of the define -+** for those options in which the value is meaningful. */ -+#define CTIMEOPT_VAL_(opt) #opt -+#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) - --/* Types of VDBE cursors */ --#define CURTYPE_BTREE 0 --#define CURTYPE_SORTER 1 --#define CURTYPE_VTAB 2 --#define CURTYPE_PSEUDO 3 -+/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This -+** option requires a separate macro because legal values contain a single -+** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */ -+#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2 -+#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt) -+/* #include "sqliteInt.h" */ - - /* --** A VdbeCursor is an superclass (a wrapper) for various cursor objects: -+** An array of names of all compile-time options. This array should -+** be sorted A-Z. - ** --** * A b-tree cursor --** - In the main database or in an ephemeral database --** - On either an index or a table --** * A sorter --** * A virtual table --** * A one-row "pseudotable" stored in a single register -+** This array looks large, but in a typical installation actually uses -+** only a handful of compile-time options, so most times this array is usually -+** rather short and uses little memory space. - */ --typedef struct VdbeCursor VdbeCursor; --struct VdbeCursor { -- u8 eCurType; /* One of the CURTYPE_* values above */ -- i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ -- u8 nullRow; /* True if pointing to a row with no data */ -- u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ -- u8 isTable; /* True for rowid tables. False for indexes */ --#ifdef SQLITE_DEBUG -- u8 seekOp; /* Most recent seek operation on this cursor */ -- u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ --#endif -- Bool isEphemeral:1; /* True for an ephemeral table */ -- Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ -- Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ -- Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ -- Btree *pBtx; /* Separate file holding temporary table */ -- i64 seqCount; /* Sequence counter */ -- u32 *aAltMap; /* Mapping from table to index column numbers */ -- -- /* Cached OP_Column parse information is only valid if cacheStatus matches -- ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of -- ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that -- ** the cache is out of date. */ -- u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ -- int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0 -- ** if there have been no prior seeks on the cursor. */ -- /* seekResult does not distinguish between "no seeks have ever occurred -- ** on this cursor" and "the most recent seek was an exact match". -- ** For CURTYPE_PSEUDO, seekResult is the register holding the record */ -+static const char * const sqlite3azCompileOpt[] = { - -- /* When a new VdbeCursor is allocated, only the fields above are zeroed. -- ** The fields that follow are uninitialized, and must be individually -- ** initialized prior to first use. */ -- VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ -- union { -- BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */ -- sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ -- VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ -- } uc; -- KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ -- u32 iHdrOffset; /* Offset to next unparsed byte of the header */ -- Pgno pgnoRoot; /* Root page of the open btree cursor */ -- i16 nField; /* Number of fields in the header */ -- u16 nHdrParsed; /* Number of header fields parsed so far */ -- i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ -- u32 *aOffset; /* Pointer to aType[nField] */ -- const u8 *aRow; /* Data for the current row, if all on one page */ -- u32 payloadSize; /* Total number of bytes in the record */ -- u32 szRow; /* Byte available in aRow */ --#ifdef SQLITE_ENABLE_COLUMN_USED_MASK -- u64 maskUsed; /* Mask of columns used by this cursor */ -+#ifdef SQLITE_32BIT_ROWID -+ "32BIT_ROWID", - #endif -- -- /* 2*nField extra array elements allocated for aType[], beyond the one -- ** static element declared in the structure. nField total array slots for -- ** aType[] and nField+1 array slots for aOffset[] */ -- u32 aType[1]; /* Type values record decode. MUST BE LAST */ --}; -- -- --/* --** A value for VdbeCursor.cacheStatus that means the cache is always invalid. --*/ --#define CACHE_STALE 0 -- --/* --** When a sub-program is executed (OP_Program), a structure of this type --** is allocated to store the current value of the program counter, as --** well as the current memory cell array and various other frame specific --** values stored in the Vdbe struct. When the sub-program is finished, --** these values are copied back to the Vdbe from the VdbeFrame structure, --** restoring the state of the VM to as it was before the sub-program --** began executing. --** --** The memory for a VdbeFrame object is allocated and managed by a memory --** cell in the parent (calling) frame. When the memory cell is deleted or --** overwritten, the VdbeFrame object is not freed immediately. Instead, it --** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame --** list is deleted when the VM is reset in VdbeHalt(). The reason for doing --** this instead of deleting the VdbeFrame immediately is to avoid recursive --** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the --** child frame are released. --** --** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is --** set to NULL if the currently executing frame is the main program. --*/ --typedef struct VdbeFrame VdbeFrame; --struct VdbeFrame { -- Vdbe *v; /* VM this frame belongs to */ -- VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ -- Op *aOp; /* Program instructions for parent frame */ -- i64 *anExec; /* Event counters from parent frame */ -- Mem *aMem; /* Array of memory cells for parent frame */ -- VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ -- u8 *aOnce; /* Bitmask used by OP_Once */ -- void *token; /* Copy of SubProgram.token */ -- i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ -- AuxData *pAuxData; /* Linked list of auxdata allocations */ --#if SQLITE_DEBUG -- u32 iFrameMagic; /* magic number for sanity checking */ -+#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC -+ "4_BYTE_ALIGNED_MALLOC", - #endif -- int nCursor; /* Number of entries in apCsr */ -- int pc; /* Program Counter in parent (calling) frame */ -- int nOp; /* Size of aOp array */ -- int nMem; /* Number of entries in aMem */ -- int nChildMem; /* Number of memory cells for child frame */ -- int nChildCsr; /* Number of cursors for child frame */ -- int nChange; /* Statement changes (Vdbe.nChange) */ -- int nDbChange; /* Value of db->nChange */ --}; -- --/* Magic number for sanity checking on VdbeFrame objects */ --#define SQLITE_FRAME_MAGIC 0x879fb71e -- --/* --** Return a pointer to the array of registers allocated for use --** by a VdbeFrame. --*/ --#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) -- --/* --** Internally, the vdbe manipulates nearly all SQL values as Mem --** structures. Each Mem struct may cache multiple representations (string, --** integer etc.) of the same value. --*/ --struct sqlite3_value { -- union MemValue { -- double r; /* Real value used when MEM_Real is set in flags */ -- i64 i; /* Integer value used when MEM_Int is set in flags */ -- int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ -- const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ -- FuncDef *pDef; /* Used only when flags==MEM_Agg */ -- } u; -- u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ -- u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ -- u8 eSubtype; /* Subtype for this value */ -- int n; /* Number of characters in string value, excluding '\0' */ -- char *z; /* String or BLOB value */ -- /* ShallowCopy only needs to copy the information above */ -- char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ -- int szMalloc; /* Size of the zMalloc allocation */ -- u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ -- sqlite3 *db; /* The associated database connection */ -- void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ --#ifdef SQLITE_DEBUG -- Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ -- u16 mScopyFlags; /* flags value immediately after the shallow copy */ -+#ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN -+# if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 -+ "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), -+# endif - #endif --}; -- --/* --** Size of struct Mem not including the Mem.zMalloc member or anything that --** follows. --*/ --#define MEMCELLSIZE offsetof(Mem,zMalloc) -- --/* One or more of the following flags are set to indicate the validOK --** representations of the value stored in the Mem struct. --** --** If the MEM_Null flag is set, then the value is an SQL NULL value. --** For a pointer type created using sqlite3_bind_pointer() or --** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. --** --** If the MEM_Str flag is set then Mem.z points at a string representation. --** Usually this is encoded in the same unicode encoding as the main --** database (see below for exceptions). If the MEM_Term flag is also --** set, then the string is nul terminated. The MEM_Int and MEM_Real --** flags may coexist with the MEM_Str flag. --*/ --#define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ --#define MEM_Str 0x0002 /* Value is a string */ --#define MEM_Int 0x0004 /* Value is an integer */ --#define MEM_Real 0x0008 /* Value is a real number */ --#define MEM_Blob 0x0010 /* Value is a BLOB */ --#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */ --#define MEM_AffMask 0x003f /* Mask of affinity bits */ --#define MEM_FromBind 0x0040 /* Value originates from sqlite3_bind() */ --#define MEM_Undefined 0x0080 /* Value is undefined */ --#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ --#define MEM_TypeMask 0xc1bf /* Mask of type bits */ -- -- --/* Whenever Mem contains a valid string or blob representation, one of --** the following flags must be set to determine the memory management --** policy for Mem.z. The MEM_Term flag tells us whether or not the --** string is \000 or \u0000 terminated --*/ --#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ --#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */ --#define MEM_Static 0x0800 /* Mem.z points to a static string */ --#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ --#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ --#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ --#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */ --#ifdef SQLITE_OMIT_INCRBLOB -- #undef MEM_Zero -- #define MEM_Zero 0x0000 -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ "ALLOW_ROWID_IN_VIEW", - #endif -- --/* Return TRUE if Mem X contains dynamically allocated content - anything --** that needs to be deallocated to avoid a leak. --*/ --#define VdbeMemDynamic(X) \ -- (((X)->flags&(MEM_Agg|MEM_Dyn))!=0) -- --/* --** Clear any existing type flags from a Mem and replace them with f --*/ --#define MemSetTypeFlag(p, f) \ -- ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) -- --/* --** True if Mem X is a NULL-nochng type. --*/ --#define MemNullNochng(X) \ -- (((X)->flags&MEM_TypeMask)==(MEM_Null|MEM_Zero) \ -- && (X)->n==0 && (X)->u.nZero==0) -- --/* --** Return true if a memory cell is not marked as invalid. This macro --** is for use inside assert() statements only. --*/ --#ifdef SQLITE_DEBUG --#define memIsValid(M) ((M)->flags & MEM_Undefined)==0 -+#ifdef SQLITE_ALLOW_URI_AUTHORITY -+ "ALLOW_URI_AUTHORITY", - #endif -- --/* --** Each auxiliary data pointer stored by a user defined function --** implementation calling sqlite3_set_auxdata() is stored in an instance --** of this structure. All such structures associated with a single VM --** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed --** when the VM is halted (if not before). --*/ --struct AuxData { -- int iAuxOp; /* Instruction number of OP_Function opcode */ -- int iAuxArg; /* Index of function argument. */ -- void *pAux; /* Aux data pointer */ -- void (*xDeleteAux)(void*); /* Destructor for the aux data */ -- AuxData *pNextAux; /* Next element in list */ --}; -- --/* --** The "context" argument for an installable function. A pointer to an --** instance of this structure is the first argument to the routines used --** implement the SQL functions. --** --** There is a typedef for this structure in sqlite.h. So all routines, --** even the public interface to SQLite, can use a pointer to this structure. --** But this file is the only place where the internal details of this --** structure are known. --** --** This structure is defined inside of vdbeInt.h because it uses substructures --** (Mem) which are only defined there. --*/ --struct sqlite3_context { -- Mem *pOut; /* The return value is stored here */ -- FuncDef *pFunc; /* Pointer to function information */ -- Mem *pMem; /* Memory cell used to store aggregate context */ -- Vdbe *pVdbe; /* The VM that owns this context */ -- int iOp; /* Instruction number of OP_Function */ -- int isError; /* Error code returned by the function. */ -- u8 skipFlag; /* Skip accumulator loading if true */ -- u8 argc; /* Number of arguments */ -- sqlite3_value *argv[1]; /* Argument set */ --}; -- --/* A bitfield type for use inside of structures. Always follow with :N where --** N is the number of bits. --*/ --typedef unsigned bft; /* Bit Field Type */ -- --/* The ScanStatus object holds a single value for the --** sqlite3_stmt_scanstatus() interface. --*/ --typedef struct ScanStatus ScanStatus; --struct ScanStatus { -- int addrExplain; /* OP_Explain for loop */ -- int addrLoop; /* Address of "loops" counter */ -- int addrVisit; /* Address of "rows visited" counter */ -- int iSelectID; /* The "Select-ID" for this loop */ -- LogEst nEst; /* Estimated output rows per loop */ -- char *zName; /* Name of table or index */ --}; -- --/* The DblquoteStr object holds the text of a double-quoted --** string for a prepared statement. A linked list of these objects --** is constructed during statement parsing and is held on Vdbe.pDblStr. --** When computing a normalized SQL statement for an SQL statement, that --** list is consulted for each double-quoted identifier to see if the --** identifier should really be a string literal. --*/ --typedef struct DblquoteStr DblquoteStr; --struct DblquoteStr { -- DblquoteStr *pNextStr; /* Next string literal in the list */ -- char z[8]; /* Dequoted value for the string */ --}; -- --/* --** An instance of the virtual machine. This structure contains the complete --** state of the virtual machine. --** --** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() --** is really a pointer to an instance of this structure. --*/ --struct Vdbe { -- sqlite3 *db; /* The database connection that owns this statement */ -- Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ -- Parse *pParse; /* Parsing context used to create this Vdbe */ -- ynVar nVar; /* Number of entries in aVar[] */ -- u32 magic; /* Magic number for sanity checking */ -- int nMem; /* Number of memory locations currently allocated */ -- int nCursor; /* Number of slots in apCsr[] */ -- u32 cacheCtr; /* VdbeCursor row cache generation counter */ -- int pc; /* The program counter */ -- int rc; /* Value to return */ -- int nChange; /* Number of db changes made since last reset */ -- int iStatement; /* Statement number (or 0 if has no opened stmt) */ -- i64 iCurrentTime; /* Value of julianday('now') for this statement */ -- i64 nFkConstraint; /* Number of imm. FK constraints this VM */ -- i64 nStmtDefCons; /* Number of def. constraints when stmt started */ -- i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ -- Mem *aMem; /* The memory locations */ -- Mem **apArg; /* Arguments to currently executing user function */ -- VdbeCursor **apCsr; /* One element of this array for each open cursor */ -- Mem *aVar; /* Values for the OP_Variable opcode. */ -- -- /* When allocating a new Vdbe object, all of the fields below should be -- ** initialized to zero or NULL */ -- -- Op *aOp; /* Space to hold the virtual machine's program */ -- int nOp; /* Number of instructions in the program */ -- int nOpAlloc; /* Slots allocated for aOp[] */ -- Mem *aColName; /* Column names to return */ -- Mem *pResultSet; /* Pointer to an array of results */ -- char *zErrMsg; /* Error message written here */ -- VList *pVList; /* Name of variables */ --#ifndef SQLITE_OMIT_TRACE -- i64 startTime; /* Time when query started - used for profiling */ -+#ifdef SQLITE_ATOMIC_INTRINSICS -+ "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS), - #endif --#ifdef SQLITE_DEBUG -- int rcApp; /* errcode set by sqlite3_result_error_code() */ -- u32 nWrite; /* Number of write operations that have occurred */ -+#ifdef SQLITE_BITMASK_TYPE -+ "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), - #endif -- u16 nResColumn; /* Number of columns in one row of the result set */ -- u8 errorAction; /* Recovery action to do in case of an error */ -- u8 minWriteFileFormat; /* Minimum file format for writable database files */ -- u8 prepFlags; /* SQLITE_PREPARE_* flags */ -- u8 doingRerun; /* True if rerunning after an auto-reprepare */ -- bft expired:2; /* 1: recompile VM immediately 2: when convenient */ -- bft explain:2; /* True if EXPLAIN present on SQL command */ -- bft changeCntOn:1; /* True to update the change-counter */ -- bft runOnlyOnce:1; /* Automatically expire on reset */ -+#ifdef SQLITE_BUG_COMPATIBLE_20160819 -+ "BUG_COMPATIBLE_20160819", -+#endif -+#ifdef SQLITE_BUG_COMPATIBLE_20250510 -+ "BUG_COMPATIBLE_20250510", -+#endif -+#ifdef SQLITE_CASE_SENSITIVE_LIKE -+ "CASE_SENSITIVE_LIKE", -+#endif -+#ifdef SQLITE_CHECK_PAGES -+ "CHECK_PAGES", -+#endif -+#if defined(__clang__) && defined(__clang_major__) -+ "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." -+ CTIMEOPT_VAL(__clang_minor__) "." -+ CTIMEOPT_VAL(__clang_patchlevel__), -+#elif defined(_MSC_VER) -+ "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), -+#elif defined(__GNUC__) && defined(__VERSION__) -+ "COMPILER=gcc-" __VERSION__, -+#endif -+#ifdef SQLITE_COVERAGE_TEST -+ "COVERAGE_TEST", -+#endif -+#ifdef SQLITE_DEBUG -+ "DEBUG", -+#endif -+#ifdef SQLITE_DEFAULT_AUTOMATIC_INDEX -+ "DEFAULT_AUTOMATIC_INDEX", -+#endif -+#ifdef SQLITE_DEFAULT_AUTOVACUUM -+ "DEFAULT_AUTOVACUUM", -+#endif -+#ifdef SQLITE_DEFAULT_CACHE_SIZE -+ "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE), -+#endif -+#ifdef SQLITE_DEFAULT_CKPTFULLFSYNC -+ "DEFAULT_CKPTFULLFSYNC", -+#endif -+#ifdef SQLITE_DEFAULT_FILE_FORMAT -+ "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT), -+#endif -+#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS -+ "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS), -+#endif -+#ifdef SQLITE_DEFAULT_FOREIGN_KEYS -+ "DEFAULT_FOREIGN_KEYS", -+#endif -+#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -+ "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT), -+#endif -+#ifdef SQLITE_DEFAULT_LOCKING_MODE -+ "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), -+#endif -+#ifdef SQLITE_DEFAULT_LOOKASIDE -+ "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE), -+#endif -+#ifdef SQLITE_DEFAULT_MEMSTATUS -+# if SQLITE_DEFAULT_MEMSTATUS != 1 -+ "DEFAULT_MEMSTATUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_MEMSTATUS), -+# endif -+#endif -+#ifdef SQLITE_DEFAULT_MMAP_SIZE -+ "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), -+#endif -+#ifdef SQLITE_DEFAULT_PAGE_SIZE -+ "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE), -+#endif -+#ifdef SQLITE_DEFAULT_PCACHE_INITSZ -+ "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ), -+#endif -+#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS -+ "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS), -+#endif -+#ifdef SQLITE_DEFAULT_RECURSIVE_TRIGGERS -+ "DEFAULT_RECURSIVE_TRIGGERS", -+#endif -+#ifdef SQLITE_DEFAULT_ROWEST -+ "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST), -+#endif -+#ifdef SQLITE_DEFAULT_SECTOR_SIZE -+ "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE), -+#endif -+#ifdef SQLITE_DEFAULT_SYNCHRONOUS -+ "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), -+#endif -+#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT -+ "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT), -+#endif -+#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS -+ "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), -+#endif -+#ifdef SQLITE_DEFAULT_WORKER_THREADS -+ "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS), -+#endif -+#ifdef SQLITE_DIRECT_OVERFLOW_READ -+ "DIRECT_OVERFLOW_READ", -+#endif -+#ifdef SQLITE_DISABLE_DIRSYNC -+ "DISABLE_DIRSYNC", -+#endif -+#ifdef SQLITE_DISABLE_FTS3_UNICODE -+ "DISABLE_FTS3_UNICODE", -+#endif -+#ifdef SQLITE_DISABLE_FTS4_DEFERRED -+ "DISABLE_FTS4_DEFERRED", -+#endif -+#ifdef SQLITE_DISABLE_INTRINSIC -+ "DISABLE_INTRINSIC", -+#endif -+#ifdef SQLITE_DISABLE_LFS -+ "DISABLE_LFS", -+#endif -+#ifdef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS -+ "DISABLE_PAGECACHE_OVERFLOW_STATS", -+#endif -+#ifdef SQLITE_DISABLE_SKIPAHEAD_DISTINCT -+ "DISABLE_SKIPAHEAD_DISTINCT", -+#endif -+#ifdef SQLITE_DQS -+ "DQS=" CTIMEOPT_VAL(SQLITE_DQS), -+#endif -+#ifdef SQLITE_ENABLE_8_3_NAMES -+ "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), -+#endif -+#ifdef SQLITE_ENABLE_API_ARMOR -+ "ENABLE_API_ARMOR", -+#endif -+#ifdef SQLITE_ENABLE_ATOMIC_WRITE -+ "ENABLE_ATOMIC_WRITE", -+#endif -+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE -+ "ENABLE_BATCH_ATOMIC_WRITE", -+#endif -+#ifdef SQLITE_ENABLE_BYTECODE_VTAB -+ "ENABLE_BYTECODE_VTAB", -+#endif -+#ifdef SQLITE_ENABLE_CEROD -+ "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), -+#endif -+#ifdef SQLITE_ENABLE_COLUMN_METADATA -+ "ENABLE_COLUMN_METADATA", -+#endif -+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK -+ "ENABLE_COLUMN_USED_MASK", -+#endif -+#ifdef SQLITE_ENABLE_COSTMULT -+ "ENABLE_COSTMULT", -+#endif -+#ifdef SQLITE_ENABLE_CURSOR_HINTS -+ "ENABLE_CURSOR_HINTS", -+#endif -+#ifdef SQLITE_ENABLE_DBPAGE_VTAB -+ "ENABLE_DBPAGE_VTAB", -+#endif -+#ifdef SQLITE_ENABLE_DBSTAT_VTAB -+ "ENABLE_DBSTAT_VTAB", -+#endif -+#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT -+ "ENABLE_EXPENSIVE_ASSERT", -+#endif -+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS -+ "ENABLE_EXPLAIN_COMMENTS", -+#endif -+#ifdef SQLITE_ENABLE_FTS3 -+ "ENABLE_FTS3", -+#endif -+#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS -+ "ENABLE_FTS3_PARENTHESIS", -+#endif -+#ifdef SQLITE_ENABLE_FTS3_TOKENIZER -+ "ENABLE_FTS3_TOKENIZER", -+#endif -+#ifdef SQLITE_ENABLE_FTS4 -+ "ENABLE_FTS4", -+#endif -+#ifdef SQLITE_ENABLE_FTS5 -+ "ENABLE_FTS5", -+#endif -+#ifdef SQLITE_ENABLE_GEOPOLY -+ "ENABLE_GEOPOLY", -+#endif -+#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS -+ "ENABLE_HIDDEN_COLUMNS", -+#endif -+#ifdef SQLITE_ENABLE_ICU -+ "ENABLE_ICU", -+#endif -+#ifdef SQLITE_ENABLE_IOTRACE -+ "ENABLE_IOTRACE", -+#endif -+#ifdef SQLITE_ENABLE_LOAD_EXTENSION -+ "ENABLE_LOAD_EXTENSION", -+#endif -+#ifdef SQLITE_ENABLE_LOCKING_STYLE -+ "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), -+#endif -+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS -+ "ENABLE_MATH_FUNCTIONS", -+#endif -+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -+ "ENABLE_MEMORY_MANAGEMENT", -+#endif -+#ifdef SQLITE_ENABLE_MEMSYS3 -+ "ENABLE_MEMSYS3", -+#endif -+#ifdef SQLITE_ENABLE_MEMSYS5 -+ "ENABLE_MEMSYS5", -+#endif -+#ifdef SQLITE_ENABLE_MULTIPLEX -+ "ENABLE_MULTIPLEX", -+#endif -+#ifdef SQLITE_ENABLE_NORMALIZE -+ "ENABLE_NORMALIZE", -+#endif -+#ifdef SQLITE_ENABLE_NULL_TRIM -+ "ENABLE_NULL_TRIM", -+#endif -+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC -+ "ENABLE_OFFSET_SQL_FUNC", -+#endif -+#ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES -+ "ENABLE_ORDERED_SET_AGGREGATES", -+#endif -+#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK -+ "ENABLE_OVERSIZE_CELL_CHECK", -+#endif -+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -+ "ENABLE_PREUPDATE_HOOK", -+#endif -+#ifdef SQLITE_ENABLE_QPSG -+ "ENABLE_QPSG", -+#endif -+#ifdef SQLITE_ENABLE_RBU -+ "ENABLE_RBU", -+#endif -+#ifdef SQLITE_ENABLE_RTREE -+ "ENABLE_RTREE", -+#endif -+#ifdef SQLITE_ENABLE_SESSION -+ "ENABLE_SESSION", -+#endif -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ "ENABLE_SETLK_TIMEOUT", -+#endif -+#ifdef SQLITE_ENABLE_SNAPSHOT -+ "ENABLE_SNAPSHOT", -+#endif -+#ifdef SQLITE_ENABLE_SORTER_REFERENCES -+ "ENABLE_SORTER_REFERENCES", -+#endif -+#ifdef SQLITE_ENABLE_SQLLOG -+ "ENABLE_SQLLOG", -+#endif -+#ifdef SQLITE_ENABLE_STAT4 -+ "ENABLE_STAT4", -+#endif -+#ifdef SQLITE_ENABLE_STMTVTAB -+ "ENABLE_STMTVTAB", -+#endif -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ "ENABLE_STMT_SCANSTATUS", -+#endif -+#ifdef SQLITE_ENABLE_TREETRACE -+ "ENABLE_TREETRACE", -+#endif -+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION -+ "ENABLE_UNKNOWN_SQL_FUNCTION", -+#endif -+#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY -+ "ENABLE_UNLOCK_NOTIFY", -+#endif -+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -+ "ENABLE_UPDATE_DELETE_LIMIT", -+#endif -+#ifdef SQLITE_ENABLE_URI_00_ERROR -+ "ENABLE_URI_00_ERROR", -+#endif -+#ifdef SQLITE_ENABLE_VFSTRACE -+ "ENABLE_VFSTRACE", -+#endif -+#ifdef SQLITE_ENABLE_WHERETRACE -+ "ENABLE_WHERETRACE", -+#endif -+#ifdef SQLITE_ENABLE_ZIPVFS -+ "ENABLE_ZIPVFS", -+#endif -+#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS -+ "EXPLAIN_ESTIMATED_ROWS", -+#endif -+#ifdef SQLITE_EXTRA_AUTOEXT -+ "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT), -+#endif -+#ifdef SQLITE_EXTRA_IFNULLROW -+ "EXTRA_IFNULLROW", -+#endif -+#ifdef SQLITE_EXTRA_INIT -+ "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), -+#endif -+#ifdef SQLITE_EXTRA_INIT_MUTEXED -+ "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED), -+#endif -+#ifdef SQLITE_EXTRA_SHUTDOWN -+ "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), -+#endif -+#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH -+ "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), -+#endif -+#ifdef SQLITE_FTS5_ENABLE_TEST_MI -+ "FTS5_ENABLE_TEST_MI", -+#endif -+#ifdef SQLITE_FTS5_NO_WITHOUT_ROWID -+ "FTS5_NO_WITHOUT_ROWID", -+#endif -+#if HAVE_ISNAN || SQLITE_HAVE_ISNAN -+ "HAVE_ISNAN", -+#endif -+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX -+# if SQLITE_HOMEGROWN_RECURSIVE_MUTEX != 1 -+ "HOMEGROWN_RECURSIVE_MUTEX=" CTIMEOPT_VAL(SQLITE_HOMEGROWN_RECURSIVE_MUTEX), -+# endif -+#endif -+#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS -+ "IGNORE_AFP_LOCK_ERRORS", -+#endif -+#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS -+ "IGNORE_FLOCK_LOCK_ERRORS", -+#endif -+#ifdef SQLITE_INLINE_MEMCPY -+ "INLINE_MEMCPY", -+#endif -+#ifdef SQLITE_INT64_TYPE -+ "INT64_TYPE", -+#endif -+#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX -+ "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), -+#endif -+#ifdef SQLITE_LEGACY_JSON_VALID -+ "LEGACY_JSON_VALID", -+#endif -+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS -+ "LIKE_DOESNT_MATCH_BLOBS", -+#endif -+#ifdef SQLITE_LOCK_TRACE -+ "LOCK_TRACE", -+#endif -+#ifdef SQLITE_LOG_CACHE_SPILL -+ "LOG_CACHE_SPILL", -+#endif -+#ifdef SQLITE_MALLOC_SOFT_LIMIT -+ "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT), -+#endif -+#ifdef SQLITE_MAX_ATTACHED -+ "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED), -+#endif -+#ifdef SQLITE_MAX_COLUMN -+ "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN), -+#endif -+#ifdef SQLITE_MAX_COMPOUND_SELECT -+ "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT), -+#endif -+#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE -+ "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE), -+#endif -+#ifdef SQLITE_MAX_EXPR_DEPTH -+ "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH), -+#endif -+#ifdef SQLITE_MAX_FUNCTION_ARG -+ "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG), -+#endif -+#ifdef SQLITE_MAX_LENGTH -+ "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH), -+#endif -+#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH -+ "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH), -+#endif -+#ifdef SQLITE_MAX_MEMORY -+ "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY), -+#endif -+#ifdef SQLITE_MAX_MMAP_SIZE -+ "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), -+#endif -+#ifdef SQLITE_MAX_MMAP_SIZE_ -+ "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_), -+#endif -+#ifdef SQLITE_MAX_PAGE_COUNT -+ "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT), -+#endif -+#ifdef SQLITE_MAX_PAGE_SIZE -+ "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE), -+#endif -+#ifdef SQLITE_MAX_SCHEMA_RETRY -+ "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), -+#endif -+#ifdef SQLITE_MAX_SQL_LENGTH -+ "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH), -+#endif -+#ifdef SQLITE_MAX_TRIGGER_DEPTH -+ "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH), -+#endif -+#ifdef SQLITE_MAX_VARIABLE_NUMBER -+ "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER), -+#endif -+#ifdef SQLITE_MAX_VDBE_OP -+ "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP), -+#endif -+#ifdef SQLITE_MAX_WORKER_THREADS -+ "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS), -+#endif -+#ifdef SQLITE_MEMDEBUG -+ "MEMDEBUG", -+#endif -+#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT -+ "MIXED_ENDIAN_64BIT_FLOAT", -+#endif -+#ifdef SQLITE_MMAP_READWRITE -+ "MMAP_READWRITE", -+#endif -+#ifdef SQLITE_MUTEX_NOOP -+ "MUTEX_NOOP", -+#endif -+#ifdef SQLITE_MUTEX_OMIT -+ "MUTEX_OMIT", -+#endif -+#ifdef SQLITE_MUTEX_PTHREADS -+ "MUTEX_PTHREADS", -+#endif -+#ifdef SQLITE_MUTEX_W32 -+ "MUTEX_W32", -+#endif -+#ifdef SQLITE_NEED_ERR_NAME -+ "NEED_ERR_NAME", -+#endif -+#ifdef SQLITE_NO_SYNC -+ "NO_SYNC", -+#endif -+#ifdef SQLITE_OMIT_ALTERTABLE -+ "OMIT_ALTERTABLE", -+#endif -+#ifdef SQLITE_OMIT_ANALYZE -+ "OMIT_ANALYZE", -+#endif -+#ifdef SQLITE_OMIT_ATTACH -+ "OMIT_ATTACH", -+#endif -+#ifdef SQLITE_OMIT_AUTHORIZATION -+ "OMIT_AUTHORIZATION", -+#endif -+#ifdef SQLITE_OMIT_AUTOINCREMENT -+ "OMIT_AUTOINCREMENT", -+#endif -+#ifdef SQLITE_OMIT_AUTOINIT -+ "OMIT_AUTOINIT", -+#endif -+#ifdef SQLITE_OMIT_AUTOMATIC_INDEX -+ "OMIT_AUTOMATIC_INDEX", -+#endif -+#ifdef SQLITE_OMIT_AUTORESET -+ "OMIT_AUTORESET", -+#endif -+#ifdef SQLITE_OMIT_AUTOVACUUM -+ "OMIT_AUTOVACUUM", -+#endif -+#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION -+ "OMIT_BETWEEN_OPTIMIZATION", -+#endif -+#ifdef SQLITE_OMIT_BLOB_LITERAL -+ "OMIT_BLOB_LITERAL", -+#endif -+#ifdef SQLITE_OMIT_CAST -+ "OMIT_CAST", -+#endif -+#ifdef SQLITE_OMIT_CHECK -+ "OMIT_CHECK", -+#endif -+#ifdef SQLITE_OMIT_COMPLETE -+ "OMIT_COMPLETE", -+#endif -+#ifdef SQLITE_OMIT_COMPOUND_SELECT -+ "OMIT_COMPOUND_SELECT", -+#endif -+#ifdef SQLITE_OMIT_CONFLICT_CLAUSE -+ "OMIT_CONFLICT_CLAUSE", -+#endif -+#ifdef SQLITE_OMIT_CTE -+ "OMIT_CTE", -+#endif -+#if defined(SQLITE_OMIT_DATETIME_FUNCS) || defined(SQLITE_OMIT_FLOATING_POINT) -+ "OMIT_DATETIME_FUNCS", -+#endif -+#ifdef SQLITE_OMIT_DECLTYPE -+ "OMIT_DECLTYPE", -+#endif -+#ifdef SQLITE_OMIT_DEPRECATED -+ "OMIT_DEPRECATED", -+#endif -+#ifdef SQLITE_OMIT_DESERIALIZE -+ "OMIT_DESERIALIZE", -+#endif -+#ifdef SQLITE_OMIT_DISKIO -+ "OMIT_DISKIO", -+#endif -+#ifdef SQLITE_OMIT_EXPLAIN -+ "OMIT_EXPLAIN", -+#endif -+#ifdef SQLITE_OMIT_FLAG_PRAGMAS -+ "OMIT_FLAG_PRAGMAS", -+#endif -+#ifdef SQLITE_OMIT_FLOATING_POINT -+ "OMIT_FLOATING_POINT", -+#endif -+#ifdef SQLITE_OMIT_FOREIGN_KEY -+ "OMIT_FOREIGN_KEY", -+#endif -+#ifdef SQLITE_OMIT_GET_TABLE -+ "OMIT_GET_TABLE", -+#endif -+#ifdef SQLITE_OMIT_HEX_INTEGER -+ "OMIT_HEX_INTEGER", -+#endif -+#ifdef SQLITE_OMIT_INCRBLOB -+ "OMIT_INCRBLOB", -+#endif -+#ifdef SQLITE_OMIT_INTEGRITY_CHECK -+ "OMIT_INTEGRITY_CHECK", -+#endif -+#ifdef SQLITE_OMIT_INTROSPECTION_PRAGMAS -+ "OMIT_INTROSPECTION_PRAGMAS", -+#endif -+#ifdef SQLITE_OMIT_JSON -+ "OMIT_JSON", -+#endif -+#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION -+ "OMIT_LIKE_OPTIMIZATION", -+#endif -+#ifdef SQLITE_OMIT_LOAD_EXTENSION -+ "OMIT_LOAD_EXTENSION", -+#endif -+#ifdef SQLITE_OMIT_LOCALTIME -+ "OMIT_LOCALTIME", -+#endif -+#ifdef SQLITE_OMIT_LOOKASIDE -+ "OMIT_LOOKASIDE", -+#endif -+#ifdef SQLITE_OMIT_MEMORYDB -+ "OMIT_MEMORYDB", -+#endif -+#ifdef SQLITE_OMIT_OR_OPTIMIZATION -+ "OMIT_OR_OPTIMIZATION", -+#endif -+#ifdef SQLITE_OMIT_PAGER_PRAGMAS -+ "OMIT_PAGER_PRAGMAS", -+#endif -+#ifdef SQLITE_OMIT_PARSER_TRACE -+ "OMIT_PARSER_TRACE", -+#endif -+#ifdef SQLITE_OMIT_POPEN -+ "OMIT_POPEN", -+#endif -+#ifdef SQLITE_OMIT_PRAGMA -+ "OMIT_PRAGMA", -+#endif -+#ifdef SQLITE_OMIT_PROGRESS_CALLBACK -+ "OMIT_PROGRESS_CALLBACK", -+#endif -+#ifdef SQLITE_OMIT_QUICKBALANCE -+ "OMIT_QUICKBALANCE", -+#endif -+#ifdef SQLITE_OMIT_REINDEX -+ "OMIT_REINDEX", -+#endif -+#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS -+ "OMIT_SCHEMA_PRAGMAS", -+#endif -+#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS -+ "OMIT_SCHEMA_VERSION_PRAGMAS", -+#endif -+#ifdef SQLITE_OMIT_SEH -+ "OMIT_SEH", -+#endif -+#ifdef SQLITE_OMIT_SHARED_CACHE -+ "OMIT_SHARED_CACHE", -+#endif -+#ifdef SQLITE_OMIT_SHUTDOWN_DIRECTORIES -+ "OMIT_SHUTDOWN_DIRECTORIES", -+#endif -+#ifdef SQLITE_OMIT_SUBQUERY -+ "OMIT_SUBQUERY", -+#endif -+#ifdef SQLITE_OMIT_TCL_VARIABLE -+ "OMIT_TCL_VARIABLE", -+#endif -+#ifdef SQLITE_OMIT_TEMPDB -+ "OMIT_TEMPDB", -+#endif -+#ifdef SQLITE_OMIT_TEST_CONTROL -+ "OMIT_TEST_CONTROL", -+#endif -+#ifdef SQLITE_OMIT_TRACE -+# if SQLITE_OMIT_TRACE != 1 -+ "OMIT_TRACE=" CTIMEOPT_VAL(SQLITE_OMIT_TRACE), -+# endif -+#endif -+#ifdef SQLITE_OMIT_TRIGGER -+ "OMIT_TRIGGER", -+#endif -+#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION -+ "OMIT_TRUNCATE_OPTIMIZATION", -+#endif -+#ifdef SQLITE_OMIT_UTF16 -+ "OMIT_UTF16", -+#endif -+#ifdef SQLITE_OMIT_VACUUM -+ "OMIT_VACUUM", -+#endif -+#ifdef SQLITE_OMIT_VIEW -+ "OMIT_VIEW", -+#endif -+#ifdef SQLITE_OMIT_VIRTUALTABLE -+ "OMIT_VIRTUALTABLE", -+#endif -+#ifdef SQLITE_OMIT_WAL -+ "OMIT_WAL", -+#endif -+#ifdef SQLITE_OMIT_WSD -+ "OMIT_WSD", -+#endif -+#ifdef SQLITE_OMIT_XFER_OPT -+ "OMIT_XFER_OPT", -+#endif -+#ifdef SQLITE_PERFORMANCE_TRACE -+ "PERFORMANCE_TRACE", -+#endif -+#ifdef SQLITE_POWERSAFE_OVERWRITE -+# if SQLITE_POWERSAFE_OVERWRITE != 1 -+ "POWERSAFE_OVERWRITE=" CTIMEOPT_VAL(SQLITE_POWERSAFE_OVERWRITE), -+# endif -+#endif -+#ifdef SQLITE_PREFER_PROXY_LOCKING -+ "PREFER_PROXY_LOCKING", -+#endif -+#ifdef SQLITE_PROXY_DEBUG -+ "PROXY_DEBUG", -+#endif -+#ifdef SQLITE_REVERSE_UNORDERED_SELECTS -+ "REVERSE_UNORDERED_SELECTS", -+#endif -+#ifdef SQLITE_RTREE_INT_ONLY -+ "RTREE_INT_ONLY", -+#endif -+#ifdef SQLITE_SECURE_DELETE -+ "SECURE_DELETE", -+#endif -+#ifdef SQLITE_SMALL_STACK -+ "SMALL_STACK", -+#endif -+#ifdef SQLITE_SORTER_PMASZ -+ "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ), -+#endif -+#ifdef SQLITE_SOUNDEX -+ "SOUNDEX", -+#endif -+#ifdef SQLITE_STAT4_SAMPLES -+ "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES), -+#endif -+#ifdef SQLITE_STMTJRNL_SPILL -+ "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), -+#endif -+#ifdef SQLITE_SUBSTR_COMPATIBILITY -+ "SUBSTR_COMPATIBILITY", -+#endif -+#if (!defined(SQLITE_WIN32_MALLOC) \ -+ && !defined(SQLITE_ZERO_MALLOC) \ -+ && !defined(SQLITE_MEMDEBUG) \ -+ ) || defined(SQLITE_SYSTEM_MALLOC) -+ "SYSTEM_MALLOC", -+#endif -+#ifdef SQLITE_TCL -+ "TCL", -+#endif -+#ifdef SQLITE_TEMP_STORE -+ "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), -+#endif -+#ifdef SQLITE_TEST -+ "TEST", -+#endif -+#if defined(SQLITE_THREADSAFE) -+ "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), -+#elif defined(THREADSAFE) -+ "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE), -+#else -+ "THREADSAFE=1", -+#endif -+#ifdef SQLITE_UNLINK_AFTER_CLOSE -+ "UNLINK_AFTER_CLOSE", -+#endif -+#ifdef SQLITE_UNTESTABLE -+ "UNTESTABLE", -+#endif -+#ifdef SQLITE_USE_ALLOCA -+ "USE_ALLOCA", -+#endif -+#ifdef SQLITE_USE_FCNTL_TRACE -+ "USE_FCNTL_TRACE", -+#endif -+#ifdef SQLITE_USE_URI -+ "USE_URI", -+#endif -+#ifdef SQLITE_VDBE_COVERAGE -+ "VDBE_COVERAGE", -+#endif -+#ifdef SQLITE_WIN32_MALLOC -+ "WIN32_MALLOC", -+#endif -+#ifdef SQLITE_ZERO_MALLOC -+ "ZERO_MALLOC", -+#endif -+ -+} ; -+ -+SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){ -+ *pnOpt = sizeof(sqlite3azCompileOpt) / sizeof(sqlite3azCompileOpt[0]); -+ return (const char**)sqlite3azCompileOpt; -+} -+ -+#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ -+ -+/************** End of ctime.c ***********************************************/ -+/************** Begin file global.c ******************************************/ -+/* -+** 2008 June 13 -+** -+** 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 contains definitions of global variables and constants. -+*/ -+/* #include "sqliteInt.h" */ -+ -+/* An array to map all upper-case characters into their corresponding -+** lower-case character. -+** -+** SQLite only considers US-ASCII (or EBCDIC) characters. We do not -+** handle case conversions for the UTF character set since the tables -+** involved are nearly as big or bigger than SQLite itself. -+*/ -+SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { -+#ifdef SQLITE_ASCII -+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, -+ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, -+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, -+ 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, -+ 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, -+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, -+ 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, -+ 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, -+ 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, -+ 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, -+ 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, -+ 252,253,254,255, -+#endif -+#ifdef SQLITE_EBCDIC -+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ -+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ -+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ -+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ -+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ -+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ -+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */ -+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */ -+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ -+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */ -+ 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ -+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ -+ 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ -+ 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ -+ 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ -+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ -+#endif -+/* All of the upper-to-lower conversion data is above. The following -+** 18 integers are completely unrelated. They are appended to the -+** sqlite3UpperToLower[] array to avoid UBSAN warnings. Here's what is -+** going on: -+** -+** The SQL comparison operators (<>, =, >, <=, <, and >=) are implemented -+** by invoking sqlite3MemCompare(A,B) which compares values A and B and -+** returns negative, zero, or positive if A is less then, equal to, or -+** greater than B, respectively. Then the true false results is found by -+** consulting sqlite3aLTb[opcode], sqlite3aEQb[opcode], or -+** sqlite3aGTb[opcode] depending on whether the result of compare(A,B) -+** is negative, zero, or positive, where opcode is the specific opcode. -+** The only works because the comparison opcodes are consecutive and in -+** this order: NE EQ GT LE LT GE. Various assert()s throughout the code -+** ensure that is the case. -+** -+** These elements must be appended to another array. Otherwise the -+** index (here shown as [256-OP_Ne]) would be out-of-bounds and thus -+** be undefined behavior. That's goofy, but the C-standards people thought -+** it was a good idea, so here we are. -+*/ -+/* NE EQ GT LE LT GE */ -+ 1, 0, 0, 1, 1, 0, /* aLTb[]: Use when compare(A,B) less than zero */ -+ 0, 1, 0, 1, 0, 1, /* aEQb[]: Use when compare(A,B) equals zero */ -+ 1, 0, 1, 0, 0, 1 /* aGTb[]: Use when compare(A,B) greater than zero*/ -+}; -+SQLITE_PRIVATE const unsigned char *sqlite3aLTb = &sqlite3UpperToLower[256-OP_Ne]; -+SQLITE_PRIVATE const unsigned char *sqlite3aEQb = &sqlite3UpperToLower[256+6-OP_Ne]; -+SQLITE_PRIVATE const unsigned char *sqlite3aGTb = &sqlite3UpperToLower[256+12-OP_Ne]; -+ -+/* -+** The following 256 byte lookup table is used to support SQLites built-in -+** equivalents to the following standard library functions: -+** -+** isspace() 0x01 -+** isalpha() 0x02 -+** isdigit() 0x04 -+** isalnum() 0x06 -+** isxdigit() 0x08 -+** toupper() 0x20 -+** SQLite identifier character 0x40 $, _, or non-ascii -+** Quote character 0x80 -+** -+** Bit 0x20 is set if the mapped character requires translation to upper -+** case. i.e. if the character is a lower-case ASCII character. -+** If x is a lower-case ASCII character, then its upper-case equivalent -+** is (x - 0x20). Therefore toupper() can be implemented as: -+** -+** (x & ~(map[x]&0x20)) -+** -+** The equivalent of tolower() is implemented using the sqlite3UpperToLower[] -+** array. tolower() is used more often than toupper() by SQLite. -+** -+** Bit 0x40 is set if the character is non-alphanumeric and can be used in an -+** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any -+** non-ASCII UTF character. Hence the test for whether or not a character is -+** part of an identifier is 0x46. -+*/ -+SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */ -+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ -+ 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */ -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ -+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ -+ 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ -+ -+ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ -+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ -+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ -+ 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ -+ 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ -+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ -+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ -+ 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ -+ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ -+ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ -+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ -+}; -+ -+/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards -+** compatibility for legacy applications, the URI filename capability is -+** disabled by default. -+** -+** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled -+** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. -+** -+** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally -+** disabled. The default value may be changed by compiling with the -+** SQLITE_USE_URI symbol defined. -+*/ -+#ifndef SQLITE_USE_URI -+# define SQLITE_USE_URI 0 -+#endif -+ -+/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the -+** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if -+** that compile-time option is omitted. -+*/ -+#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN) -+# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 -+#else -+# if !SQLITE_ALLOW_COVERING_INDEX_SCAN -+# error "Compile-time disabling of covering index scan using the\ -+ -DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\ -+ Contact SQLite developers if this is a problem for you, and\ -+ delete this #error macro to continue with your build." -+# endif -+#endif -+ -+/* The minimum PMA size is set to this value multiplied by the database -+** page size in bytes. -+*/ -+#ifndef SQLITE_SORTER_PMASZ -+# define SQLITE_SORTER_PMASZ 250 -+#endif -+ -+/* Statement journals spill to disk when their size exceeds the following -+** threshold (in bytes). 0 means that statement journals are created and -+** written to disk immediately (the default behavior for SQLite versions -+** before 3.12.0). -1 means always keep the entire statement journal in -+** memory. (The statement journal is also always held entirely in memory -+** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this -+** setting.) -+*/ -+#ifndef SQLITE_STMTJRNL_SPILL -+# define SQLITE_STMTJRNL_SPILL (64*1024) -+#endif -+ -+/* -+** The default lookaside-configuration, the format "SZ,N". SZ is the -+** number of bytes in each lookaside slot (should be a multiple of 8) -+** and N is the number of slots. The lookaside-configuration can be -+** changed as start-time using sqlite3_config(SQLITE_CONFIG_LOOKASIDE) -+** or at run-time for an individual database connection using -+** sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE); -+** -+** With the two-size-lookaside enhancement, less lookaside is required. -+** The default configuration of 1200,40 actually provides 30 1200-byte slots -+** and 93 128-byte slots, which is more lookaside than is available -+** using the older 1200,100 configuration without two-size-lookaside. -+*/ -+#ifndef SQLITE_DEFAULT_LOOKASIDE -+# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE -+# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */ -+# else -+# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */ -+# endif -+#endif -+ -+ -+/* The default maximum size of an in-memory database created using -+** sqlite3_deserialize() -+*/ -+#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE -+# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 -+#endif -+ -+/* -+** The following singleton contains the global configuration for -+** the SQLite library. -+*/ -+SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { -+ SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ -+ 1, /* bCoreMutex */ -+ SQLITE_THREADSAFE==1, /* bFullMutex */ -+ SQLITE_USE_URI, /* bOpenUri */ -+ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ -+ 0, /* bSmallMalloc */ -+ 1, /* bExtraSchemaChecks */ -+#ifdef SQLITE_DEBUG -+ 0, /* bJsonSelfcheck */ -+#endif -+ 0x7ffffffe, /* mxStrlen */ -+ 0, /* neverCorrupt */ -+ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ -+ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ -+ {0,0,0,0,0,0,0,0}, /* m */ -+ {0,0,0,0,0,0,0,0,0}, /* mutex */ -+ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ -+ (void*)0, /* pHeap */ -+ 0, /* nHeap */ -+ 0, 0, /* mnHeap, mxHeap */ -+ SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ -+ SQLITE_MAX_MMAP_SIZE, /* mxMmap */ -+ (void*)0, /* pPage */ -+ 0, /* szPage */ -+ SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ -+ 0, /* mxParserStack */ -+ 0, /* sharedCacheEnabled */ -+ SQLITE_SORTER_PMASZ, /* szPma */ -+ /* All the rest should always be initialized to zero */ -+ 0, /* isInit */ -+ 0, /* inProgress */ -+ 0, /* isMutexInit */ -+ 0, /* isMallocInit */ -+ 0, /* isPCacheInit */ -+ 0, /* nRefInitMutex */ -+ 0, /* pInitMutex */ -+ 0, /* xLog */ -+ 0, /* pLogArg */ -+#ifdef SQLITE_ENABLE_SQLLOG -+ 0, /* xSqllog */ -+ 0, /* pSqllogArg */ -+#endif -+#ifdef SQLITE_VDBE_COVERAGE -+ 0, /* xVdbeBranch */ -+ 0, /* pVbeBranchArg */ -+#endif -+#ifndef SQLITE_OMIT_DESERIALIZE -+ SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ -+#endif -+#ifndef SQLITE_UNTESTABLE -+ 0, /* xTestCallback */ -+#endif -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */ -+#endif -+ 0, /* bLocaltimeFault */ -+ 0, /* xAltLocaltime */ -+ 0x7ffffffe, /* iOnceResetThreshold */ -+ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ -+ 0, /* iPrngSeed */ -+#ifdef SQLITE_DEBUG -+ {0,0,0,0,0,0}, /* aTune */ -+#endif -+}; -+ -+/* -+** Hash table for global functions - functions common to all -+** database connections. After initialization, this table is -+** read-only. -+*/ -+SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; -+ -+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) -+/* -+** Counter used for coverage testing. Does not come into play for -+** release builds. -+** -+** Access to this global variable is not mutex protected. This might -+** result in TSAN warnings. But as the variable does not exist in -+** release builds, that should not be a concern. -+*/ -+SQLITE_PRIVATE unsigned int sqlite3CoverageCounter; -+#endif /* SQLITE_COVERAGE_TEST || SQLITE_DEBUG */ -+ -+#ifdef VDBE_PROFILE -+/* -+** The following performance counter can be used in place of -+** sqlite3Hwtime() for profiling. This is a no-op on standard builds. -+*/ -+SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt = 0; -+#endif -+ -+/* -+** The value of the "pending" byte must be 0x40000000 (1 byte past the -+** 1-gibabyte boundary) in a compatible database. SQLite never uses -+** the database page that contains the pending byte. It never attempts -+** to read or write that page. The pending byte page is set aside -+** for use by the VFS layers as space for managing file locks. -+** -+** During testing, it is often desirable to move the pending byte to -+** a different position in the file. This allows code that has to -+** deal with the pending byte to run on files that are much smaller -+** than 1 GiB. The sqlite3_test_control() interface can be used to -+** move the pending byte. -+** -+** IMPORTANT: Changing the pending byte to any value other than -+** 0x40000000 results in an incompatible database file format! -+** Changing the pending byte during operation will result in undefined -+** and incorrect behavior. -+*/ -+#ifndef SQLITE_OMIT_WSD -+SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; -+#endif -+ -+/* -+** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS. -+*/ -+SQLITE_PRIVATE u32 sqlite3TreeTrace = 0; -+SQLITE_PRIVATE u32 sqlite3WhereTrace = 0; -+ -+/* #include "opcodes.h" */ -+/* -+** Properties of opcodes. The OPFLG_INITIALIZER macro is -+** created by mkopcodeh.awk during compilation. Data is obtained -+** from the comments following the "case OP_xxxx:" statements in -+** the vdbe.c file. -+*/ -+SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; -+ -+/* -+** Name of the default collating sequence -+*/ -+SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY"; -+ -+/* -+** Standard typenames. These names must match the COLTYPE_* definitions. -+** Adjust the SQLITE_N_STDTYPE value if adding or removing entries. -+** -+** sqlite3StdType[] The actual names of the datatypes. -+** -+** sqlite3StdTypeLen[] The length (in bytes) of each entry -+** in sqlite3StdType[]. -+** -+** sqlite3StdTypeAffinity[] The affinity associated with each entry -+** in sqlite3StdType[]. -+*/ -+SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 }; -+SQLITE_PRIVATE const char sqlite3StdTypeAffinity[] = { -+ SQLITE_AFF_NUMERIC, -+ SQLITE_AFF_BLOB, -+ SQLITE_AFF_INTEGER, -+ SQLITE_AFF_INTEGER, -+ SQLITE_AFF_REAL, -+ SQLITE_AFF_TEXT -+}; -+SQLITE_PRIVATE const char *sqlite3StdType[] = { -+ "ANY", -+ "BLOB", -+ "INT", -+ "INTEGER", -+ "REAL", -+ "TEXT" -+}; -+ -+/************** End of global.c **********************************************/ -+/************** Begin file status.c ******************************************/ -+/* -+** 2008 June 18 -+** -+** 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 module implements the sqlite3_status() interface and related -+** functionality. -+*/ -+/* #include "sqliteInt.h" */ -+/************** Include vdbeInt.h in the middle of status.c ******************/ -+/************** Begin file vdbeInt.h *****************************************/ -+/* -+** 2003 September 6 -+** -+** 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 is the header file for information that is private to the -+** VDBE. This information used to all be at the top of the single -+** source code file "vdbe.c". When that file became too big (over -+** 6000 lines long) it was split up into several smaller files and -+** this header information was factored out. -+*/ -+#ifndef SQLITE_VDBEINT_H -+#define SQLITE_VDBEINT_H -+ -+/* -+** The maximum number of times that a statement will try to reparse -+** itself before giving up and returning SQLITE_SCHEMA. -+*/ -+#ifndef SQLITE_MAX_SCHEMA_RETRY -+# define SQLITE_MAX_SCHEMA_RETRY 50 -+#endif -+ -+/* -+** VDBE_DISPLAY_P4 is true or false depending on whether or not the -+** "explain" P4 display logic is enabled. -+*/ -+#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ -+ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ -+ || defined(SQLITE_ENABLE_BYTECODE_VTAB) -+# define VDBE_DISPLAY_P4 1 -+#else -+# define VDBE_DISPLAY_P4 0 -+#endif -+ -+/* -+** SQL is translated into a sequence of instructions to be -+** executed by a virtual machine. Each instruction is an instance -+** of the following structure. -+*/ -+typedef struct VdbeOp Op; -+ -+/* -+** Boolean values -+*/ -+typedef unsigned Bool; -+ -+/* Opaque type used by code in vdbesort.c */ -+typedef struct VdbeSorter VdbeSorter; -+ -+/* Elements of the linked list at Vdbe.pAuxData */ -+typedef struct AuxData AuxData; -+ -+/* A cache of large TEXT or BLOB values in a VdbeCursor */ -+typedef struct VdbeTxtBlbCache VdbeTxtBlbCache; -+ -+/* Types of VDBE cursors */ -+#define CURTYPE_BTREE 0 -+#define CURTYPE_SORTER 1 -+#define CURTYPE_VTAB 2 -+#define CURTYPE_PSEUDO 3 -+ -+/* -+** A VdbeCursor is an superclass (a wrapper) for various cursor objects: -+** -+** * A b-tree cursor -+** - In the main database or in an ephemeral database -+** - On either an index or a table -+** * A sorter -+** * A virtual table -+** * A one-row "pseudotable" stored in a single register -+*/ -+typedef struct VdbeCursor VdbeCursor; -+struct VdbeCursor { -+ u8 eCurType; /* One of the CURTYPE_* values above */ -+ i8 iDb; /* Index of cursor database in db->aDb[] */ -+ u8 nullRow; /* True if pointing to a row with no data */ -+ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ -+ u8 isTable; /* True for rowid tables. False for indexes */ -+#ifdef SQLITE_DEBUG -+ u8 seekOp; /* Most recent seek operation on this cursor */ -+ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ -+#endif -+ Bool isEphemeral:1; /* True for an ephemeral table */ -+ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ -+ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ -+ Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */ -+ Bool colCache:1; /* pCache pointer is initialized and non-NULL */ -+ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ -+ union { /* pBtx for isEphermeral. pAltMap otherwise */ -+ Btree *pBtx; /* Separate file holding temporary table */ -+ u32 *aAltMap; /* Mapping from table to index column numbers */ -+ } ub; -+ i64 seqCount; /* Sequence counter */ -+ -+ /* Cached OP_Column parse information is only valid if cacheStatus matches -+ ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of -+ ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that -+ ** the cache is out of date. */ -+ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ -+ int seekResult; /* Result of previous sqlite3BtreeMoveto() or 0 -+ ** if there have been no prior seeks on the cursor. */ -+ /* seekResult does not distinguish between "no seeks have ever occurred -+ ** on this cursor" and "the most recent seek was an exact match". -+ ** For CURTYPE_PSEUDO, seekResult is the register holding the record */ -+ -+ /* When a new VdbeCursor is allocated, only the fields above are zeroed. -+ ** The fields that follow are uninitialized, and must be individually -+ ** initialized prior to first use. */ -+ VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ -+ union { -+ BtCursor *pCursor; /* CURTYPE_BTREE or _PSEUDO. Btree cursor */ -+ sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ -+ VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ -+ } uc; -+ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ -+ u32 iHdrOffset; /* Offset to next unparsed byte of the header */ -+ Pgno pgnoRoot; /* Root page of the open btree cursor */ -+ i16 nField; /* Number of fields in the header */ -+ u16 nHdrParsed; /* Number of header fields parsed so far */ -+ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ -+ u32 *aOffset; /* Pointer to aType[nField] */ -+ const u8 *aRow; /* Data for the current row, if all on one page */ -+ u32 payloadSize; /* Total number of bytes in the record */ -+ u32 szRow; /* Byte available in aRow */ -+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK -+ u64 maskUsed; /* Mask of columns used by this cursor */ -+#endif -+ VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ -+ -+ /* Space is allocated for aType to hold at least 2*nField+1 entries: -+ ** nField slots for aType[] and nField+1 array slots for aOffset[] */ -+ u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */ -+}; -+ -+/* -+** The size (in bytes) of a VdbeCursor object that has an nField value of N -+** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple -+** of 8. -+*/ -+#define SZ_VDBECURSOR(N) \ -+ (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64)) -+ -+/* Return true if P is a null-only cursor -+*/ -+#define IsNullCursor(P) \ -+ ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) -+ -+/* -+** A value for VdbeCursor.cacheStatus that means the cache is always invalid. -+*/ -+#define CACHE_STALE 0 -+ -+/* -+** Large TEXT or BLOB values can be slow to load, so we want to avoid -+** loading them more than once. For that reason, large TEXT and BLOB values -+** can be stored in a cache defined by this object, and attached to the -+** VdbeCursor using the pCache field. -+*/ -+struct VdbeTxtBlbCache { -+ char *pCValue; /* A RCStr buffer to hold the value */ -+ i64 iOffset; /* File offset of the row being cached */ -+ int iCol; /* Column for which the cache is valid */ -+ u32 cacheStatus; /* Vdbe.cacheCtr value */ -+ u32 colCacheCtr; /* Column cache counter */ -+}; -+ -+/* -+** When a sub-program is executed (OP_Program), a structure of this type -+** is allocated to store the current value of the program counter, as -+** well as the current memory cell array and various other frame specific -+** values stored in the Vdbe struct. When the sub-program is finished, -+** these values are copied back to the Vdbe from the VdbeFrame structure, -+** restoring the state of the VM to as it was before the sub-program -+** began executing. -+** -+** The memory for a VdbeFrame object is allocated and managed by a memory -+** cell in the parent (calling) frame. When the memory cell is deleted or -+** overwritten, the VdbeFrame object is not freed immediately. Instead, it -+** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame -+** list is deleted when the VM is reset in VdbeHalt(). The reason for doing -+** this instead of deleting the VdbeFrame immediately is to avoid recursive -+** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the -+** child frame are released. -+** -+** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is -+** set to NULL if the currently executing frame is the main program. -+*/ -+typedef struct VdbeFrame VdbeFrame; -+struct VdbeFrame { -+ Vdbe *v; /* VM this frame belongs to */ -+ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ -+ Op *aOp; /* Program instructions for parent frame */ -+ Mem *aMem; /* Array of memory cells for parent frame */ -+ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ -+ u8 *aOnce; /* Bitmask used by OP_Once */ -+ void *token; /* Copy of SubProgram.token */ -+ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ -+ AuxData *pAuxData; /* Linked list of auxdata allocations */ -+#if SQLITE_DEBUG -+ u32 iFrameMagic; /* magic number for sanity checking */ -+#endif -+ int nCursor; /* Number of entries in apCsr */ -+ int pc; /* Program Counter in parent (calling) frame */ -+ int nOp; /* Size of aOp array */ -+ int nMem; /* Number of entries in aMem */ -+ int nChildMem; /* Number of memory cells for child frame */ -+ int nChildCsr; /* Number of cursors for child frame */ -+ i64 nChange; /* Statement changes (Vdbe.nChange) */ -+ i64 nDbChange; /* Value of db->nChange */ -+}; -+ -+/* Magic number for sanity checking on VdbeFrame objects */ -+#define SQLITE_FRAME_MAGIC 0x879fb71e -+ -+/* -+** Return a pointer to the array of registers allocated for use -+** by a VdbeFrame. -+*/ -+#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) -+ -+/* -+** Internally, the vdbe manipulates nearly all SQL values as Mem -+** structures. Each Mem struct may cache multiple representations (string, -+** integer etc.) of the same value. -+*/ -+struct sqlite3_value { -+ union MemValue { -+ double r; /* Real value used when MEM_Real is set in flags */ -+ i64 i; /* Integer value used when MEM_Int is set in flags */ -+ int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */ -+ const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */ -+ FuncDef *pDef; /* Used only when flags==MEM_Agg */ -+ } u; -+ char *z; /* String or BLOB value */ -+ int n; /* Number of characters in string value, excluding '\0' */ -+ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ -+ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ -+ u8 eSubtype; /* Subtype for this value */ -+ /* ShallowCopy only needs to copy the information above */ -+ sqlite3 *db; /* The associated database connection */ -+ int szMalloc; /* Size of the zMalloc allocation */ -+ u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ -+ char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ -+ void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ -+#ifdef SQLITE_DEBUG -+ Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ -+ u16 mScopyFlags; /* flags value immediately after the shallow copy */ -+ u8 bScopy; /* The pScopyFrom of some other Mem *might* point here */ -+#endif -+}; -+ -+/* -+** Size of struct Mem not including the Mem.zMalloc member or anything that -+** follows. -+*/ -+#define MEMCELLSIZE offsetof(Mem,db) -+ -+/* One or more of the following flags are set to indicate the -+** representations of the value stored in the Mem struct. -+** -+** * MEM_Null An SQL NULL value -+** -+** * MEM_Null|MEM_Zero An SQL NULL with the virtual table -+** UPDATE no-change flag set -+** -+** * MEM_Null|MEM_Term| An SQL NULL, but also contains a -+** MEM_Subtype pointer accessible using -+** sqlite3_value_pointer(). -+** -+** * MEM_Null|MEM_Cleared Special SQL NULL that compares non-equal -+** to other NULLs even using the IS operator. -+** -+** * MEM_Str A string, stored in Mem.z with -+** length Mem.n. Zero-terminated if -+** MEM_Term is set. This flag is -+** incompatible with MEM_Blob and -+** MEM_Null, but can appear with MEM_Int, -+** MEM_Real, and MEM_IntReal. -+** -+** * MEM_Blob A blob, stored in Mem.z length Mem.n. -+** Incompatible with MEM_Str, MEM_Null, -+** MEM_Int, MEM_Real, and MEM_IntReal. -+** -+** * MEM_Blob|MEM_Zero A blob in Mem.z of length Mem.n plus -+** MEM.u.i extra 0x00 bytes at the end. -+** -+** * MEM_Int Integer stored in Mem.u.i. -+** -+** * MEM_Real Real stored in Mem.u.r. -+** -+** * MEM_IntReal Real stored as an integer in Mem.u.i. -+** -+** If the MEM_Null flag is set, then the value is an SQL NULL value. -+** For a pointer type created using sqlite3_bind_pointer() or -+** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. -+** -+** If the MEM_Str flag is set then Mem.z points at a string representation. -+** Usually this is encoded in the same unicode encoding as the main -+** database (see below for exceptions). If the MEM_Term flag is also -+** set, then the string is nul terminated. The MEM_Int and MEM_Real -+** flags may coexist with the MEM_Str flag. -+*/ -+#define MEM_Undefined 0x0000 /* Value is undefined */ -+#define MEM_Null 0x0001 /* Value is NULL (or a pointer) */ -+#define MEM_Str 0x0002 /* Value is a string */ -+#define MEM_Int 0x0004 /* Value is an integer */ -+#define MEM_Real 0x0008 /* Value is a real number */ -+#define MEM_Blob 0x0010 /* Value is a BLOB */ -+#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */ -+#define MEM_AffMask 0x003f /* Mask of affinity bits */ -+ -+/* Extra bits that modify the meanings of the core datatypes above -+*/ -+#define MEM_FromBind 0x0040 /* Value originates from sqlite3_bind() */ -+ /* 0x0080 // Available */ -+#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ -+#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */ -+#define MEM_Zero 0x0400 /* Mem.i contains count of 0s appended to blob */ -+#define MEM_Subtype 0x0800 /* Mem.eSubtype is valid */ -+#define MEM_TypeMask 0x0dbf /* Mask of type bits */ -+ -+/* Bits that determine the storage for Mem.z for a string or blob or -+** aggregate accumulator. -+*/ -+#define MEM_Dyn 0x1000 /* Need to call Mem.xDel() on Mem.z */ -+#define MEM_Static 0x2000 /* Mem.z points to a static string */ -+#define MEM_Ephem 0x4000 /* Mem.z points to an ephemeral string */ -+#define MEM_Agg 0x8000 /* Mem.z points to an agg function context */ -+ -+/* Return TRUE if Mem X contains dynamically allocated content - anything -+** that needs to be deallocated to avoid a leak. -+*/ -+#define VdbeMemDynamic(X) \ -+ (((X)->flags&(MEM_Agg|MEM_Dyn))!=0) -+ -+/* -+** Clear any existing type flags from a Mem and replace them with f -+*/ -+#define MemSetTypeFlag(p, f) \ -+ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) -+ -+/* -+** True if Mem X is a NULL-nochng type. -+*/ -+#define MemNullNochng(X) \ -+ (((X)->flags&MEM_TypeMask)==(MEM_Null|MEM_Zero) \ -+ && (X)->n==0 && (X)->u.nZero==0) -+ -+/* -+** Return true if a memory cell has been initialized and is valid. -+** is for use inside assert() statements only. -+** -+** A Memory cell is initialized if at least one of the -+** MEM_Null, MEM_Str, MEM_Int, MEM_Real, MEM_Blob, or MEM_IntReal bits -+** is set. It is "undefined" if all those bits are zero. -+*/ -+#ifdef SQLITE_DEBUG -+#define memIsValid(M) ((M)->flags & MEM_AffMask)!=0 -+#endif -+ -+/* -+** Each auxiliary data pointer stored by a user defined function -+** implementation calling sqlite3_set_auxdata() is stored in an instance -+** of this structure. All such structures associated with a single VM -+** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed -+** when the VM is halted (if not before). -+*/ -+struct AuxData { -+ int iAuxOp; /* Instruction number of OP_Function opcode */ -+ int iAuxArg; /* Index of function argument. */ -+ void *pAux; /* Aux data pointer */ -+ void (*xDeleteAux)(void*); /* Destructor for the aux data */ -+ AuxData *pNextAux; /* Next element in list */ -+}; -+ -+/* -+** The "context" argument for an installable function. A pointer to an -+** instance of this structure is the first argument to the routines used -+** implement the SQL functions. -+** -+** There is a typedef for this structure in sqlite.h. So all routines, -+** even the public interface to SQLite, can use a pointer to this structure. -+** But this file is the only place where the internal details of this -+** structure are known. -+** -+** This structure is defined inside of vdbeInt.h because it uses substructures -+** (Mem) which are only defined there. -+*/ -+struct sqlite3_context { -+ Mem *pOut; /* The return value is stored here */ -+ FuncDef *pFunc; /* Pointer to function information */ -+ Mem *pMem; /* Memory cell used to store aggregate context */ -+ Vdbe *pVdbe; /* The VM that owns this context */ -+ int iOp; /* Instruction number of OP_Function */ -+ int isError; /* Error code returned by the function. */ -+ u8 enc; /* Encoding to use for results */ -+ u8 skipFlag; /* Skip accumulator loading if true */ -+ u16 argc; /* Number of arguments */ -+ sqlite3_value *argv[FLEXARRAY]; /* Argument set */ -+}; -+ -+/* -+** The size (in bytes) of an sqlite3_context object that holds N -+** argv[] arguments. -+*/ -+#define SZ_CONTEXT(N) \ -+ (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*)) -+ -+ -+/* The ScanStatus object holds a single value for the -+** sqlite3_stmt_scanstatus() interface. -+** -+** aAddrRange[]: -+** This array is used by ScanStatus elements associated with EQP -+** notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is -+** an array of up to 3 ranges of VM addresses for which the Vdbe.anCycle[] -+** values should be summed to calculate the NCYCLE value. Each pair of -+** integer addresses is a start and end address (both inclusive) for a range -+** instructions. A start value of 0 indicates an empty range. -+*/ -+typedef struct ScanStatus ScanStatus; -+struct ScanStatus { -+ int addrExplain; /* OP_Explain for loop */ -+ int aAddrRange[6]; -+ int addrLoop; /* Address of "loops" counter */ -+ int addrVisit; /* Address of "rows visited" counter */ -+ int iSelectID; /* The "Select-ID" for this loop */ -+ LogEst nEst; /* Estimated output rows per loop */ -+ char *zName; /* Name of table or index */ -+}; -+ -+/* The DblquoteStr object holds the text of a double-quoted -+** string for a prepared statement. A linked list of these objects -+** is constructed during statement parsing and is held on Vdbe.pDblStr. -+** When computing a normalized SQL statement for an SQL statement, that -+** list is consulted for each double-quoted identifier to see if the -+** identifier should really be a string literal. -+*/ -+typedef struct DblquoteStr DblquoteStr; -+struct DblquoteStr { -+ DblquoteStr *pNextStr; /* Next string literal in the list */ -+ char z[8]; /* Dequoted value for the string */ -+}; -+ -+/* -+** An instance of the virtual machine. This structure contains the complete -+** state of the virtual machine. -+** -+** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() -+** is really a pointer to an instance of this structure. -+*/ -+struct Vdbe { -+ sqlite3 *db; /* The database connection that owns this statement */ -+ Vdbe **ppVPrev,*pVNext; /* Linked list of VDBEs with the same Vdbe.db */ -+ Parse *pParse; /* Parsing context used to create this Vdbe */ -+ ynVar nVar; /* Number of entries in aVar[] */ -+ int nMem; /* Number of memory locations currently allocated */ -+ int nCursor; /* Number of slots in apCsr[] */ -+ u32 cacheCtr; /* VdbeCursor row cache generation counter */ -+ int pc; /* The program counter */ -+ int rc; /* Value to return */ -+ i64 nChange; /* Number of db changes made since last reset */ -+ int iStatement; /* Statement number (or 0 if has no opened stmt) */ -+ i64 iCurrentTime; /* Value of julianday('now') for this statement */ -+ i64 nFkConstraint; /* Number of imm. FK constraints this VM */ -+ i64 nStmtDefCons; /* Number of def. constraints when stmt started */ -+ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ -+ Mem *aMem; /* The memory locations */ -+ Mem **apArg; /* Arguments xUpdate and xFilter vtab methods */ -+ VdbeCursor **apCsr; /* One element of this array for each open cursor */ -+ Mem *aVar; /* Values for the OP_Variable opcode. */ -+ -+ /* When allocating a new Vdbe object, all of the fields below should be -+ ** initialized to zero or NULL */ -+ -+ Op *aOp; /* Space to hold the virtual machine's program */ -+ int nOp; /* Number of instructions in the program */ -+ int nOpAlloc; /* Slots allocated for aOp[] */ -+ Mem *aColName; /* Column names to return */ -+ Mem *pResultRow; /* Current output row */ -+ char *zErrMsg; /* Error message written here */ -+ VList *pVList; /* Name of variables */ -+#ifndef SQLITE_OMIT_TRACE -+ i64 startTime; /* Time when query started - used for profiling */ -+#endif -+#ifdef SQLITE_DEBUG -+ int rcApp; /* errcode set by sqlite3_result_error_code() */ -+ u32 nWrite; /* Number of write operations that have occurred */ -+ int napArg; /* Size of the apArg[] array */ -+#endif -+ u16 nResColumn; /* Number of columns in one row of the result set */ -+ u16 nResAlloc; /* Column slots allocated to aColName[] */ -+ u8 errorAction; /* Recovery action to do in case of an error */ -+ u8 minWriteFileFormat; /* Minimum file format for writable database files */ -+ u8 prepFlags; /* SQLITE_PREPARE_* flags */ -+ u8 eVdbeState; /* On of the VDBE_*_STATE values */ -+ bft expired:2; /* 1: recompile VM immediately 2: when convenient */ -+ bft explain:2; /* 0: normal, 1: EXPLAIN, 2: EXPLAIN QUERY PLAN */ -+ bft changeCntOn:1; /* True to update the change-counter */ - bft usesStmtJournal:1; /* True if uses a statement journal */ - bft readOnly:1; /* True for statements that do not write */ - bft bIsReader:1; /* True for statements that read */ -+ bft haveEqpOps:1; /* Bytecode supports EXPLAIN QUERY PLAN */ - yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ - yDbMask lockMask; /* Subset of btreeMask that requires a lock */ -- u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ -+ u32 aCounter[9]; /* Counters used by sqlite3_stmt_status() */ - char *zSql; /* Text of the SQL statement that generated this */ - #ifdef SQLITE_ENABLE_NORMALIZE - char *zNormSql; /* Normalization of the associated SQL statement */ -@@ -21152,20 +24104,18 @@ struct Vdbe { - SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ - AuxData *pAuxData; /* Linked list of auxdata allocations */ - #ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- i64 *anExec; /* Number of times each op has been executed */ - int nScan; /* Entries in aScan[] */ - ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */ - #endif - }; - - /* --** The following are allowed values for Vdbe.magic -+** The following are allowed values for Vdbe.eVdbeState - */ --#define VDBE_MAGIC_INIT 0x16bceaa5 /* Building a VDBE program */ --#define VDBE_MAGIC_RUN 0x2df20da3 /* VDBE is ready to execute */ --#define VDBE_MAGIC_HALT 0x319c2973 /* VDBE has completed execution */ --#define VDBE_MAGIC_RESET 0x48fa9f76 /* Reset and ready to run again */ --#define VDBE_MAGIC_DEAD 0x5606c3c8 /* The VDBE has been deallocated */ -+#define VDBE_INIT_STATE 0 /* Prepared statement under construction */ -+#define VDBE_READY_STATE 1 /* Ready to run but not yet started */ -+#define VDBE_RUN_STATE 2 /* Run in progress */ -+#define VDBE_HALT_STATE 3 /* Finished. Need reset() or finalize() */ - - /* - ** Structure used to store the context required by the -@@ -21176,30 +24126,65 @@ struct PreUpdate { - VdbeCursor *pCsr; /* Cursor to read old values from */ - int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ - u8 *aRecord; /* old.* database record */ -- KeyInfo keyinfo; -+ KeyInfo *pKeyinfo; /* Key information */ - UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ - UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ - int iNewReg; /* Register for new.* values */ -+ int iBlobWrite; /* Value returned by preupdate_blobwrite() */ - i64 iKey1; /* First key value passed to hook */ - i64 iKey2; /* Second key value passed to hook */ -+ Mem oldipk; /* Memory cell holding "old" IPK value */ - Mem *aNew; /* Array of new.* values */ -- Table *pTab; /* Schema object being upated */ -+ Table *pTab; /* Schema object being updated */ - Index *pPk; /* PK index if pTab is WITHOUT ROWID */ -+ sqlite3_value **apDflt; /* Array of default values, if required */ -+ u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */ - }; - -+/* -+** An instance of this object is used to pass an vector of values into -+** OP_VFilter, the xFilter method of a virtual table. The vector is the -+** set of values on the right-hand side of an IN constraint. -+** -+** The value as passed into xFilter is an sqlite3_value with a "pointer" -+** type, such as is generated by sqlite3_result_pointer() and read by -+** sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null -+** and a subtype of 'p'. The sqlite3_vtab_in_first() and _next() interfaces -+** know how to use this object to step through all the values in the -+** right operand of the IN constraint. -+*/ -+typedef struct ValueList ValueList; -+struct ValueList { -+ BtCursor *pCsr; /* An ephemeral table holding all values */ -+ sqlite3_value *pOut; /* Register to hold each decoded output value */ -+}; -+ -+/* Size of content associated with serial types that fit into a -+** single-byte varint. -+*/ -+#ifndef SQLITE_AMALGAMATION -+SQLITE_PRIVATE const u8 sqlite3SmallTypeSizes[]; -+#endif -+ - /* - ** Function prototypes - */ - SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); - SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); -+SQLITE_PRIVATE void sqlite3VdbeFreeCursorNN(Vdbe*,VdbeCursor*); - void sqliteVdbePopStack(Vdbe*,int); -+SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeHandleMovedCursor(VdbeCursor *p); - SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); --SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, u32*); - SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*); - SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); - SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8); --SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); --SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); -+#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT -+SQLITE_PRIVATE u64 sqlite3FloatSwap(u64 in); -+# define swapMixedEndianFloat(X) X = sqlite3FloatSwap(X) -+#else -+# define swapMixedEndianFloat(X) -+#endif -+SQLITE_PRIVATE void sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); - SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int); - - int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); -@@ -21223,7 +24208,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); - SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); - SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); - SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); --SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); -+SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); - SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); - #ifdef SQLITE_OMIT_FLOATING_POINT - # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 -@@ -21233,14 +24218,20 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); - SQLITE_PRIVATE void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*)); - SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16); - SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*); -+#ifndef SQLITE_OMIT_INCRBLOB - SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int); -+#else -+SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem*,int); -+#endif - #ifdef SQLITE_DEBUG - SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*); - #endif - SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem*); -+SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem*); - SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*); - SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8); --SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*); -+SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); -+SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem*); - SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*); - SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*); - SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem*, int ifNull); -@@ -21251,6 +24242,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem*,u8,u8); - SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*); - SQLITE_PRIVATE int sqlite3VdbeMemFromBtreeZeroOffset(BtCursor*,u32,Mem*); - SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); -+SQLITE_PRIVATE void sqlite3VdbeMemReleaseMalloc(Mem*p); - SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); - #ifndef SQLITE_OMIT_WINDOWFUNC - SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); -@@ -21268,7 +24260,8 @@ SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */ - SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */ - SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); - #ifdef SQLITE_ENABLE_PREUPDATE_HOOK --SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int); -+SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( -+ Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int); - #endif - SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); - -@@ -21281,6 +24274,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); - SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); - SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); - -+SQLITE_PRIVATE void sqlite3VdbeValueListFree(void*); -+ - #ifdef SQLITE_DEBUG - SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*); - SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*); -@@ -21510,8 +24505,9 @@ SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){ - nInit += countLookasideSlots(db->lookaside.pSmallInit); - nFree += countLookasideSlots(db->lookaside.pSmallFree); - #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ -- if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; -- return db->lookaside.nSlot - (nInit+nFree); -+ assert( db->lookaside.nSlot >= nInit+nFree ); -+ if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit); -+ return (int)(db->lookaside.nSlot - (nInit+nFree)); - } - - /* -@@ -21564,7 +24560,7 @@ SQLITE_API int sqlite3_db_status( - assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); - assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); - *pCurrent = 0; -- *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; -+ *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT]; - if( resetFlag ){ - db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; - } -@@ -21609,6 +24605,8 @@ SQLITE_API int sqlite3_db_status( - - sqlite3BtreeEnterAll(db); - db->pnBytesFreed = &nByte; -+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); -+ db->lookaside.pEnd = db->lookaside.pStart; - for(i=0; inDb; i++){ - Schema *pSchema = db->aDb[i].pSchema; - if( ALWAYS(pSchema!=0) ){ -@@ -21634,6 +24632,7 @@ SQLITE_API int sqlite3_db_status( - } - } - db->pnBytesFreed = 0; -+ db->lookaside.pEnd = db->lookaside.pTrueEnd; - sqlite3BtreeLeaveAll(db); - - *pHighwater = 0; -@@ -21651,10 +24650,12 @@ SQLITE_API int sqlite3_db_status( - int nByte = 0; /* Used to accumulate return value */ - - db->pnBytesFreed = &nByte; -- for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ -- sqlite3VdbeClearObject(db, pVdbe); -- sqlite3DbFree(db, pVdbe); -+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); -+ db->lookaside.pEnd = db->lookaside.pStart; -+ for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pVNext){ -+ sqlite3VdbeDelete(pVdbe); - } -+ db->lookaside.pEnd = db->lookaside.pTrueEnd; - db->pnBytesFreed = 0; - - *pHighwater = 0; /* IMP: R-64479-57858 */ -@@ -21675,7 +24676,7 @@ SQLITE_API int sqlite3_db_status( - case SQLITE_DBSTATUS_CACHE_MISS: - case SQLITE_DBSTATUS_CACHE_WRITE:{ - int i; -- int nRet = 0; -+ u64 nRet = 0; - assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); - assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); - -@@ -21688,7 +24689,7 @@ SQLITE_API int sqlite3_db_status( - *pHighwater = 0; /* IMP: R-42420-56072 */ - /* IMP: R-54100-20147 */ - /* IMP: R-29431-39229 */ -- *pCurrent = nRet; -+ *pCurrent = (int)nRet & 0x7fffffff; - break; - } - -@@ -21785,12 +24786,14 @@ struct DateTime { - int tz; /* Timezone offset in minutes */ - double s; /* Seconds */ - char validJD; /* True (1) if iJD is valid */ -- char rawS; /* Raw numeric value stored in s */ - char validYMD; /* True (1) if Y,M,D are valid */ - char validHMS; /* True (1) if h,m,s are valid */ -- char validTZ; /* True (1) if tz is valid */ -- char tzSet; /* Timezone was set explicitly */ -- char isError; /* An overflow has occurred */ -+ char nFloor; /* Days to implement "floor" */ -+ unsigned rawS : 1; /* Raw numeric value stored in s */ -+ unsigned isError : 1; /* An overflow has occurred */ -+ unsigned useSubsec : 1; /* Display subsecond precision */ -+ unsigned isUtc : 1; /* Time is known to be UTC */ -+ unsigned isLocal : 1; /* Time is known to be localtime */ - }; - - -@@ -21823,8 +24826,8 @@ struct DateTime { - */ - static int getDigits(const char *zDate, const char *zFormat, ...){ - /* The aMx[] array translates the 3rd character of each format -- ** spec into a max size: a b c d e f */ -- static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 }; -+ ** spec into a max size: a b c d e f */ -+ static const u16 aMx[] = { 12, 14, 24, 31, 59, 14712 }; - va_list ap; - int cnt = 0; - char nextC; -@@ -21888,6 +24891,8 @@ static int parseTimezone(const char *zDate, DateTime *p){ - sgn = +1; - }else if( c=='Z' || c=='z' ){ - zDate++; -+ p->isLocal = 0; -+ p->isUtc = 1; - goto zulu_time; - }else{ - return c!=0; -@@ -21900,7 +24905,6 @@ static int parseTimezone(const char *zDate, DateTime *p){ - p->tz = sgn*(nMn + nHr*60); - zulu_time: - while( sqlite3Isspace(*zDate) ){ zDate++; } -- p->tzSet = 1; - return *zDate!=0; - } - -@@ -21933,6 +24937,9 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ - zDate++; - } - ms /= rScale; -+ /* Truncate to avoid problems with sub-milliseconds -+ ** rounding. https://sqlite.org/forum/forumpost/766a2c9231 */ -+ if( ms>0.999 ) ms = 0.999; - } - }else{ - s = 0; -@@ -21944,7 +24951,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ - p->m = m; - p->s = s + ms; - if( parseTimezone(zDate, p) ) return 1; -- p->validTZ = (p->tz!=0)?1:0; - return 0; - } - -@@ -21983,23 +24989,48 @@ static void computeJD(DateTime *p){ - Y--; - M += 12; - } -- A = Y/100; -- B = 2 - A + (A/4); -+ A = (Y+4800)/100; -+ B = 38 - A + (A/4); - X1 = 36525*(Y+4716)/100; - X2 = 306001*(M+1)/10000; - p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); - p->validJD = 1; - if( p->validHMS ){ -- p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000); -- if( p->validTZ ){ -+ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); -+ if( p->tz ){ - p->iJD -= p->tz*60000; - p->validYMD = 0; - p->validHMS = 0; -- p->validTZ = 0; -+ p->tz = 0; -+ p->isUtc = 1; -+ p->isLocal = 0; - } - } - } - -+/* -+** Given the YYYY-MM-DD information current in p, determine if there -+** is day-of-month overflow and set nFloor to the number of days that -+** would need to be subtracted from the date in order to bring the -+** date back to the end of the month. -+*/ -+static void computeFloor(DateTime *p){ -+ assert( p->validYMD || p->isError ); -+ assert( p->D>=0 && p->D<=31 ); -+ assert( p->M>=0 && p->M<=12 ); -+ if( p->D<=28 ){ -+ p->nFloor = 0; -+ }else if( (1<M) & 0x15aa ){ -+ p->nFloor = 0; -+ }else if( p->M!=2 ){ -+ p->nFloor = (p->D==31); -+ }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){ -+ p->nFloor = p->D - 28; -+ }else{ -+ p->nFloor = p->D - 29; -+ } -+} -+ - /* - ** Parse dates of the form - ** -@@ -22038,12 +25069,16 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){ - p->Y = neg ? -Y : Y; - p->M = M; - p->D = D; -- if( p->validTZ ){ -+ computeFloor(p); -+ if( p->tz ){ - computeJD(p); - } - return 0; - } - -+ -+static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */ -+ - /* - ** Set the time to the current time reported by the VFS. - ** -@@ -22053,6 +25088,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ - p->iJD = sqlite3StmtCurrentTime(context); - if( p->iJD>0 ){ - p->validJD = 1; -+ p->isUtc = 1; -+ p->isLocal = 0; -+ clearYMD_HMS_TZ(p); - return 0; - }else{ - return 1; -@@ -22105,6 +25143,11 @@ static int parseDateOrTime( - }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ - setRawDateNumber(p, r); - return 0; -+ }else if( (sqlite3StrICmp(zDate,"subsec")==0 -+ || sqlite3StrICmp(zDate,"subsecond")==0) -+ && sqlite3NotPureFunc(context) ){ -+ p->useSubsec = 1; -+ return setDateTimeToCurrent(context, p); - } - return 1; - } -@@ -22131,7 +25174,7 @@ static int validJulianDay(sqlite3_int64 iJD){ - ** Compute the Year, Month, and Day from the julian day number. - */ - static void computeYMD(DateTime *p){ -- int Z, A, B, C, D, E, X1; -+ int Z, alpha, A, B, C, D, E, X1; - if( p->validYMD ) return; - if( !p->validJD ){ - p->Y = 2000; -@@ -22142,8 +25185,8 @@ static void computeYMD(DateTime *p){ - return; - }else{ - Z = (int)((p->iJD + 43200000)/86400000); -- A = (int)((Z - 1867216.25)/36524.25); -- A = Z + 1 + A - (A/4); -+ alpha = (int)((Z + 32044.75)/36524.25) - 52; -+ A = Z + 1 + alpha - ((alpha+100)/4) + 25; - B = A + 1524; - C = (int)((B - 122.1)/365.25); - D = (36525*(C&32767))/100; -@@ -22160,17 +25203,14 @@ static void computeYMD(DateTime *p){ - ** Compute the Hour, Minute, and Seconds from the julian day number. - */ - static void computeHMS(DateTime *p){ -- int s; -+ int day_ms, day_min; /* milliseconds, minutes into the day */ - if( p->validHMS ) return; - computeJD(p); -- s = (int)((p->iJD + 43200000) % 86400000); -- p->s = s/1000.0; -- s = (int)p->s; -- p->s -= s; -- p->h = s/3600; -- s -= p->h*3600; -- p->m = s/60; -- p->s += s - p->m*60; -+ day_ms = (int)((p->iJD + 43200000) % 86400000); -+ p->s = (day_ms % 60000)/1000.0; -+ day_min = day_ms/60000; -+ p->m = day_min % 60; -+ p->h = day_min / 60; - p->rawS = 0; - p->validHMS = 1; - } -@@ -22189,7 +25229,7 @@ static void computeYMD_HMS(DateTime *p){ - static void clearYMD_HMS_TZ(DateTime *p){ - p->validYMD = 0; - p->validHMS = 0; -- p->validTZ = 0; -+ p->tz = 0; - } - - #ifndef SQLITE_OMIT_LOCALTIME -@@ -22217,8 +25257,10 @@ static void clearYMD_HMS_TZ(DateTime *p){ - ** is available. This routine returns 0 on success and - ** non-zero on any kind of error. - ** --** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this --** routine will always fail. -+** If the sqlite3GlobalConfig.bLocaltimeFault variable is non-zero then this -+** routine will always fail. If bLocaltimeFault is nonzero and -+** sqlite3GlobalConfig.xAltLocaltime is not NULL, then xAltLocaltime() is -+** invoked in place of the OS-defined localtime() function. - ** - ** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C - ** library function localtime_r() is used to assist in the calculation of -@@ -22234,14 +25276,30 @@ static int osLocaltime(time_t *t, struct tm *pTm){ - sqlite3_mutex_enter(mutex); - pX = localtime(t); - #ifndef SQLITE_UNTESTABLE -- if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; -+ if( sqlite3GlobalConfig.bLocaltimeFault ){ -+ if( sqlite3GlobalConfig.xAltLocaltime!=0 -+ && 0==sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm) -+ ){ -+ pX = pTm; -+ }else{ -+ pX = 0; -+ } -+ } - #endif - if( pX ) *pTm = *pX; -+#if SQLITE_THREADSAFE>0 - sqlite3_mutex_leave(mutex); -+#endif - rc = pX==0; - #else - #ifndef SQLITE_UNTESTABLE -- if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; -+ if( sqlite3GlobalConfig.bLocaltimeFault ){ -+ if( sqlite3GlobalConfig.xAltLocaltime!=0 ){ -+ return sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm); -+ }else{ -+ return 1; -+ } -+ } - #endif - #if HAVE_LOCALTIME_R - rc = localtime_r(t, pTm)==0; -@@ -22256,67 +25314,56 @@ static int osLocaltime(time_t *t, struct tm *pTm){ - - #ifndef SQLITE_OMIT_LOCALTIME - /* --** Compute the difference (in milliseconds) between localtime and UTC --** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs, --** return this value and set *pRc to SQLITE_OK. --** --** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value --** is undefined in this case. -+** Assuming the input DateTime is UTC, move it to its localtime equivalent. - */ --static sqlite3_int64 localtimeOffset( -- DateTime *p, /* Date at which to calculate offset */ -- sqlite3_context *pCtx, /* Write error here if one occurs */ -- int *pRc /* OUT: Error code. SQLITE_OK or ERROR */ -+static int toLocaltime( -+ DateTime *p, /* Date at which to calculate offset */ -+ sqlite3_context *pCtx /* Write error here if one occurs */ - ){ -- DateTime x, y; - time_t t; - struct tm sLocal; -+ int iYearDiff; - - /* Initialize the contents of sLocal to avoid a compiler warning. */ - memset(&sLocal, 0, sizeof(sLocal)); - -- x = *p; -- computeYMD_HMS(&x); -- if( x.Y<1971 || x.Y>=2038 ){ -+ computeJD(p); -+ if( p->iJD<2108667600*(i64)100000 /* 1970-01-01 */ -+ || p->iJD>2130141456*(i64)100000 /* 2038-01-18 */ -+ ){ - /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only - ** works for years between 1970 and 2037. For dates outside this range, - ** SQLite attempts to map the year into an equivalent year within this - ** range, do the calculation, then map the year back. - */ -- x.Y = 2000; -- x.M = 1; -- x.D = 1; -- x.h = 0; -- x.m = 0; -- x.s = 0.0; -- } else { -- int s = (int)(x.s + 0.5); -- x.s = s; -+ DateTime x = *p; -+ computeYMD_HMS(&x); -+ iYearDiff = (2000 + x.Y%4) - x.Y; -+ x.Y += iYearDiff; -+ x.validJD = 0; -+ computeJD(&x); -+ t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); -+ }else{ -+ iYearDiff = 0; -+ t = (time_t)(p->iJD/1000 - 21086676*(i64)10000); - } -- x.tz = 0; -- x.validJD = 0; -- computeJD(&x); -- t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); - if( osLocaltime(&t, &sLocal) ){ - sqlite3_result_error(pCtx, "local time unavailable", -1); -- *pRc = SQLITE_ERROR; -- return 0; -+ return SQLITE_ERROR; - } -- y.Y = sLocal.tm_year + 1900; -- y.M = sLocal.tm_mon + 1; -- y.D = sLocal.tm_mday; -- y.h = sLocal.tm_hour; -- y.m = sLocal.tm_min; -- y.s = sLocal.tm_sec; -- y.validYMD = 1; -- y.validHMS = 1; -- y.validJD = 0; -- y.rawS = 0; -- y.validTZ = 0; -- y.isError = 0; -- computeJD(&y); -- *pRc = SQLITE_OK; -- return y.iJD - x.iJD; -+ p->Y = sLocal.tm_year + 1900 - iYearDiff; -+ p->M = sLocal.tm_mon + 1; -+ p->D = sLocal.tm_mday; -+ p->h = sLocal.tm_hour; -+ p->m = sLocal.tm_min; -+ p->s = sLocal.tm_sec + (p->iJD%1000)*0.001; -+ p->validYMD = 1; -+ p->validHMS = 1; -+ p->validJD = 0; -+ p->rawS = 0; -+ p->tz = 0; -+ p->isError = 0; -+ return SQLITE_OK; - } - #endif /* SQLITE_OMIT_LOCALTIME */ - -@@ -22329,20 +25376,38 @@ static sqlite3_int64 localtimeOffset( - ** of several units of time. - */ - static const struct { -- u8 eType; /* Transformation type code */ -- u8 nName; /* Length of th name */ -- char *zName; /* Name of the transformation */ -- double rLimit; /* Maximum NNN value for this transform */ -- double rXform; /* Constant used for this transform */ -+ u8 nName; /* Length of the name */ -+ char zName[7]; /* Name of the transformation */ -+ float rLimit; /* Maximum NNN value for this transform */ -+ float rXform; /* Constant used for this transform */ - } aXformType[] = { -- { 0, 6, "second", 464269060800.0, 1000.0 }, -- { 0, 6, "minute", 7737817680.0, 60000.0 }, -- { 0, 4, "hour", 128963628.0, 3600000.0 }, -- { 0, 3, "day", 5373485.0, 86400000.0 }, -- { 1, 5, "month", 176546.0, 2592000000.0 }, -- { 2, 4, "year", 14713.0, 31536000000.0 }, -+ /* 0 */ { 6, "second", 4.6427e+14, 1.0 }, -+ /* 1 */ { 6, "minute", 7.7379e+12, 60.0 }, -+ /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 }, -+ /* 3 */ { 3, "day", 5373485.0, 86400.0 }, -+ /* 4 */ { 5, "month", 176546.0, 2592000.0 }, -+ /* 5 */ { 4, "year", 14713.0, 31536000.0 }, - }; - -+/* -+** If the DateTime p is raw number, try to figure out if it is -+** a julian day number of a unix timestamp. Set the p value -+** appropriately. -+*/ -+static void autoAdjustDate(DateTime *p){ -+ if( !p->rawS || p->validJD ){ -+ p->rawS = 0; -+ }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ -+ && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ -+ ){ -+ double r = p->s*1000.0 + 210866760000000.0; -+ clearYMD_HMS_TZ(p); -+ p->iJD = (sqlite3_int64)(r + 0.5); -+ p->validJD = 1; -+ p->rawS = 0; -+ } -+} -+ - /* - ** Process a modifier to a date-time stamp. The modifiers are - ** as follows: -@@ -22353,14 +25418,20 @@ static const struct { - ** NNN.NNNN seconds - ** NNN months - ** NNN years -+** +/-YYYY-MM-DD HH:MM:SS.SSS -+** ceiling -+** floor - ** start of month - ** start of year - ** start of week - ** start of day - ** weekday N - ** unixepoch -+** auto - ** localtime - ** utc -+** subsec -+** subsecond - ** - ** Return 0 on success and 1 if there is any kind of error. If the error - ** is in a system call (i.e. localtime()), then an error message is written -@@ -22371,11 +25442,75 @@ static int parseModifier( - sqlite3_context *pCtx, /* Function context */ - const char *z, /* The text of the modifier */ - int n, /* Length of zMod in bytes */ -- DateTime *p /* The date/time value to be modified */ -+ DateTime *p, /* The date/time value to be modified */ -+ int idx /* Parameter index of the modifier */ - ){ - int rc = 1; - double r; - switch(sqlite3UpperToLower[(u8)z[0]] ){ -+ case 'a': { -+ /* -+ ** auto -+ ** -+ ** If rawS is available, then interpret as a julian day number, or -+ ** a unix timestamp, depending on its magnitude. -+ */ -+ if( sqlite3_stricmp(z, "auto")==0 ){ -+ if( idx>1 ) return 1; /* IMP: R-33611-57934 */ -+ autoAdjustDate(p); -+ rc = 0; -+ } -+ break; -+ } -+ case 'c': { -+ /* -+ ** ceiling -+ ** -+ ** Resolve day-of-month overflow by rolling forward into the next -+ ** month. As this is the default action, this modifier is really -+ ** a no-op that is only included for symmetry. See "floor". -+ */ -+ if( sqlite3_stricmp(z, "ceiling")==0 ){ -+ computeJD(p); -+ clearYMD_HMS_TZ(p); -+ rc = 0; -+ p->nFloor = 0; -+ } -+ break; -+ } -+ case 'f': { -+ /* -+ ** floor -+ ** -+ ** Resolve day-of-month overflow by rolling back to the end of the -+ ** previous month. -+ */ -+ if( sqlite3_stricmp(z, "floor")==0 ){ -+ computeJD(p); -+ p->iJD -= p->nFloor*86400000; -+ clearYMD_HMS_TZ(p); -+ rc = 0; -+ } -+ break; -+ } -+ case 'j': { -+ /* -+ ** julianday -+ ** -+ ** Always interpret the prior number as a julian-day value. If this -+ ** is not the first modifier, or if the prior argument is not a numeric -+ ** value in the allowed range of julian day numbers understood by -+ ** SQLite (0..5373484.5) then the result will be NULL. -+ */ -+ if( sqlite3_stricmp(z, "julianday")==0 ){ -+ if( idx>1 ) return 1; /* IMP: R-31176-64601 */ -+ if( p->validJD && p->rawS ){ -+ rc = 0; -+ p->rawS = 0; -+ } -+ } -+ break; -+ } - #ifndef SQLITE_OMIT_LOCALTIME - case 'l': { - /* localtime -@@ -22384,9 +25519,9 @@ static int parseModifier( - ** show local time. - */ - if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ -- computeJD(p); -- p->iJD += localtimeOffset(p, pCtx, &rc); -- clearYMD_HMS_TZ(p); -+ rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx); -+ p->isUtc = 0; -+ p->isLocal = 1; - } - break; - } -@@ -22399,6 +25534,7 @@ static int parseModifier( - ** seconds since 1970. Convert to a real julian day number. - */ - if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){ -+ if( idx>1 ) return 1; /* IMP: R-49255-55373 */ - r = p->s*1000.0 + 210866760000000.0; - if( r>=0.0 && r<464269060800000.0 ){ - clearYMD_HMS_TZ(p); -@@ -22410,19 +25546,33 @@ static int parseModifier( - } - #ifndef SQLITE_OMIT_LOCALTIME - else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ -- if( p->tzSet==0 ){ -- sqlite3_int64 c1; -+ if( p->isUtc==0 ){ -+ i64 iOrigJD; /* Original localtime */ -+ i64 iGuess; /* Guess at the corresponding utc time */ -+ int cnt = 0; /* Safety to prevent infinite loop */ -+ i64 iErr; /* Guess is off by this much */ -+ - computeJD(p); -- c1 = localtimeOffset(p, pCtx, &rc); -- if( rc==SQLITE_OK ){ -- p->iJD -= c1; -- clearYMD_HMS_TZ(p); -- p->iJD += c1 - localtimeOffset(p, pCtx, &rc); -- } -- p->tzSet = 1; -- }else{ -- rc = SQLITE_OK; -+ iGuess = iOrigJD = p->iJD; -+ iErr = 0; -+ do{ -+ DateTime new; -+ memset(&new, 0, sizeof(new)); -+ iGuess -= iErr; -+ new.iJD = iGuess; -+ new.validJD = 1; -+ rc = toLocaltime(&new, pCtx); -+ if( rc ) return rc; -+ computeJD(&new); -+ iErr = new.iJD - iOrigJD; -+ }while( iErr && cnt++<3 ); -+ memset(p, 0, sizeof(*p)); -+ p->iJD = iGuess; -+ p->validJD = 1; -+ p->isUtc = 1; -+ p->isLocal = 0; - } -+ rc = SQLITE_OK; - } - #endif - break; -@@ -22437,10 +25587,10 @@ static int parseModifier( - */ - if( sqlite3_strnicmp(z, "weekday ", 8)==0 - && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0 -- && (n=(int)r)==r && n>=0 && r<7 ){ -+ && r>=0.0 && r<7.0 && (n=(int)r)==r ){ - sqlite3_int64 Z; - computeYMD_HMS(p); -- p->validTZ = 0; -+ p->tz = 0; - p->validJD = 0; - computeJD(p); - Z = ((p->iJD + 129600000)/86400000) % 7; -@@ -22457,8 +25607,22 @@ static int parseModifier( - ** - ** Move the date backwards to the beginning of the current day, - ** or month or year. -+ ** -+ ** subsecond -+ ** subsec -+ ** -+ ** Show subsecond precision in the output of datetime() and -+ ** unixepoch() and strftime('%s'). - */ -- if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; -+ if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){ -+ if( sqlite3_stricmp(z, "subsec")==0 -+ || sqlite3_stricmp(z, "subsecond")==0 -+ ){ -+ p->useSubsec = 1; -+ rc = 0; -+ } -+ break; -+ } - if( !p->validJD && !p->validYMD && !p->validHMS ) break; - z += 9; - computeYMD(p); -@@ -22466,7 +25630,7 @@ static int parseModifier( - p->h = p->m = 0; - p->s = 0.0; - p->rawS = 0; -- p->validTZ = 0; -+ p->tz = 0; - p->validJD = 0; - if( sqlite3_stricmp(z,"month")==0 ){ - p->D = 1; -@@ -22494,18 +25658,74 @@ static int parseModifier( - case '9': { - double rRounder; - int i; -- for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} -+ int Y,M,D,h,m,x; -+ const char *z2 = z; -+ char z0 = z[0]; -+ for(n=1; z[n]; n++){ -+ if( z[n]==':' ) break; -+ if( sqlite3Isspace(z[n]) ) break; -+ if( z[n]=='-' ){ -+ if( n==5 && getDigits(&z[1], "40f", &Y)==1 ) break; -+ if( n==6 && getDigits(&z[1], "50f", &Y)==1 ) break; -+ } -+ } - if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){ -- rc = 1; -+ assert( rc==1 ); - break; - } -- if( z[n]==':' ){ -+ if( z[n]=='-' ){ -+ /* A modifier of the form (+|-)YYYY-MM-DD adds or subtracts the -+ ** specified number of years, months, and days. MM is limited to -+ ** the range 0-11 and DD is limited to 0-30. -+ */ -+ if( z0!='+' && z0!='-' ) break; /* Must start with +/- */ -+ if( n==5 ){ -+ if( getDigits(&z[1], "40f-20a-20d", &Y, &M, &D)!=3 ) break; -+ }else{ -+ assert( n==6 ); -+ if( getDigits(&z[1], "50f-20a-20d", &Y, &M, &D)!=3 ) break; -+ z++; -+ } -+ if( M>=12 ) break; /* M range 0..11 */ -+ if( D>=31 ) break; /* D range 0..30 */ -+ computeYMD_HMS(p); -+ p->validJD = 0; -+ if( z0=='-' ){ -+ p->Y -= Y; -+ p->M -= M; -+ D = -D; -+ }else{ -+ p->Y += Y; -+ p->M += M; -+ } -+ x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; -+ p->Y += x; -+ p->M -= x*12; -+ computeFloor(p); -+ computeJD(p); -+ p->validHMS = 0; -+ p->validYMD = 0; -+ p->iJD += (i64)D*86400000; -+ if( z[11]==0 ){ -+ rc = 0; -+ break; -+ } -+ if( sqlite3Isspace(z[11]) -+ && getDigits(&z[12], "20c:20e", &h, &m)==2 -+ ){ -+ z2 = &z[12]; -+ n = 2; -+ }else{ -+ break; -+ } -+ } -+ if( z2[n]==':' ){ - /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the - ** specified number of hours, minutes, seconds, and fractional seconds - ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be - ** omitted. - */ -- const char *z2 = z; -+ - DateTime tx; - sqlite3_int64 day; - if( !sqlite3Isdigit(*z2) ) z2++; -@@ -22515,7 +25735,7 @@ static int parseModifier( - tx.iJD -= 43200000; - day = tx.iJD/86400000; - tx.iJD -= day*86400000; -- if( z[0]=='-' ) tx.iJD = -tx.iJD; -+ if( z0=='-' ) tx.iJD = -tx.iJD; - computeJD(p); - clearYMD_HMS_TZ(p); - p->iJD += tx.iJD; -@@ -22528,39 +25748,44 @@ static int parseModifier( - z += n; - while( sqlite3Isspace(*z) ) z++; - n = sqlite3Strlen30(z); -- if( n>10 || n<3 ) break; -+ if( n<3 || n>10 ) break; - if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; - computeJD(p); -- rc = 1; -+ assert( rc==1 ); - rRounder = r<0 ? -0.5 : +0.5; -+ p->nFloor = 0; - for(i=0; i-aXformType[i].rLimit && rM += (int)r; - x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; - p->Y += x; - p->M -= x*12; -+ computeFloor(p); - p->validJD = 0; - r -= (int)r; - break; - } -- case 2: { /* Special processing to add years */ -+ case 5: { /* Special processing to add years */ - int y = (int)r; -+ assert( strcmp(aXformType[5].zName,"year")==0 ); - computeYMD_HMS(p); -+ assert( p->M>=0 && p->M<=12 ); - p->Y += y; -+ computeFloor(p); - p->validJD = 0; - r -= (int)r; - break; - } - } - computeJD(p); -- p->iJD += (sqlite3_int64)(r*aXformType[i].rXform + rRounder); -+ p->iJD += (sqlite3_int64)(r*1000.0*aXformType[i].rXform + rRounder); - rc = 0; - break; - } -@@ -22595,6 +25820,7 @@ static int isDate( - int eType; - memset(p, 0, sizeof(*p)); - if( argc==0 ){ -+ if( !sqlite3NotPureFunc(context) ) return 1; - return setDateTimeToCurrent(context, p); - } - if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT -@@ -22609,10 +25835,16 @@ static int isDate( - for(i=1; iisError || !validJulianDay(p->iJD) ) return 1; -+ if( argc==1 && p->validYMD && p->D>28 ){ -+ /* Make sure a YYYY-MM-DD is normalized. -+ ** Example: 2023-02-31 -> 2023-03-03 */ -+ assert( p->validJD ); -+ p->validYMD = 0; -+ } - return 0; - } - -@@ -22639,6 +25871,28 @@ static void juliandayFunc( - } - } - -+/* -+** unixepoch( TIMESTRING, MOD, MOD, ...) -+** -+** Return the number of seconds (including fractional seconds) since -+** the unix epoch of 1970-01-01 00:00:00 GMT. -+*/ -+static void unixepochFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ DateTime x; -+ if( isDate(context, argc, argv, &x)==0 ){ -+ computeJD(&x); -+ if( x.useSubsec ){ -+ sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0); -+ }else{ -+ sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); -+ } -+ } -+} -+ - /* - ** datetime( TIMESTRING, MOD, MOD, ...) - ** -@@ -22651,11 +25905,51 @@ static void datetimeFunc( - ){ - DateTime x; - if( isDate(context, argc, argv, &x)==0 ){ -- char zBuf[100]; -+ int Y, s, n; -+ char zBuf[32]; - computeYMD_HMS(&x); -- sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d", -- x.Y, x.M, x.D, x.h, x.m, (int)(x.s)); -- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); -+ Y = x.Y; -+ if( Y<0 ) Y = -Y; -+ zBuf[1] = '0' + (Y/1000)%10; -+ zBuf[2] = '0' + (Y/100)%10; -+ zBuf[3] = '0' + (Y/10)%10; -+ zBuf[4] = '0' + (Y)%10; -+ zBuf[5] = '-'; -+ zBuf[6] = '0' + (x.M/10)%10; -+ zBuf[7] = '0' + (x.M)%10; -+ zBuf[8] = '-'; -+ zBuf[9] = '0' + (x.D/10)%10; -+ zBuf[10] = '0' + (x.D)%10; -+ zBuf[11] = ' '; -+ zBuf[12] = '0' + (x.h/10)%10; -+ zBuf[13] = '0' + (x.h)%10; -+ zBuf[14] = ':'; -+ zBuf[15] = '0' + (x.m/10)%10; -+ zBuf[16] = '0' + (x.m)%10; -+ zBuf[17] = ':'; -+ if( x.useSubsec ){ -+ s = (int)(1000.0*x.s + 0.5); -+ zBuf[18] = '0' + (s/10000)%10; -+ zBuf[19] = '0' + (s/1000)%10; -+ zBuf[20] = '.'; -+ zBuf[21] = '0' + (s/100)%10; -+ zBuf[22] = '0' + (s/10)%10; -+ zBuf[23] = '0' + (s)%10; -+ zBuf[24] = 0; -+ n = 24; -+ }else{ -+ s = (int)x.s; -+ zBuf[18] = '0' + (s/10)%10; -+ zBuf[19] = '0' + (s)%10; -+ zBuf[20] = 0; -+ n = 20; -+ } -+ if( x.Y<0 ){ -+ zBuf[0] = '-'; -+ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); -+ }else{ -+ sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT); -+ } - } - } - -@@ -22671,10 +25965,33 @@ static void timeFunc( - ){ - DateTime x; - if( isDate(context, argc, argv, &x)==0 ){ -- char zBuf[100]; -+ int s, n; -+ char zBuf[16]; - computeHMS(&x); -- sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s); -- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); -+ zBuf[0] = '0' + (x.h/10)%10; -+ zBuf[1] = '0' + (x.h)%10; -+ zBuf[2] = ':'; -+ zBuf[3] = '0' + (x.m/10)%10; -+ zBuf[4] = '0' + (x.m)%10; -+ zBuf[5] = ':'; -+ if( x.useSubsec ){ -+ s = (int)(1000.0*x.s + 0.5); -+ zBuf[6] = '0' + (s/10000)%10; -+ zBuf[7] = '0' + (s/1000)%10; -+ zBuf[8] = '.'; -+ zBuf[9] = '0' + (s/100)%10; -+ zBuf[10] = '0' + (s/10)%10; -+ zBuf[11] = '0' + (s)%10; -+ zBuf[12] = 0; -+ n = 12; -+ }else{ -+ s = (int)x.s; -+ zBuf[6] = '0' + (s/10)%10; -+ zBuf[7] = '0' + (s)%10; -+ zBuf[8] = 0; -+ n = 8; -+ } -+ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); - } - } - -@@ -22690,29 +26007,108 @@ static void dateFunc( - ){ - DateTime x; - if( isDate(context, argc, argv, &x)==0 ){ -- char zBuf[100]; -+ int Y; -+ char zBuf[16]; - computeYMD(&x); -- sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D); -- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); -+ Y = x.Y; -+ if( Y<0 ) Y = -Y; -+ zBuf[1] = '0' + (Y/1000)%10; -+ zBuf[2] = '0' + (Y/100)%10; -+ zBuf[3] = '0' + (Y/10)%10; -+ zBuf[4] = '0' + (Y)%10; -+ zBuf[5] = '-'; -+ zBuf[6] = '0' + (x.M/10)%10; -+ zBuf[7] = '0' + (x.M)%10; -+ zBuf[8] = '-'; -+ zBuf[9] = '0' + (x.D/10)%10; -+ zBuf[10] = '0' + (x.D)%10; -+ zBuf[11] = 0; -+ if( x.Y<0 ){ -+ zBuf[0] = '-'; -+ sqlite3_result_text(context, zBuf, 11, SQLITE_TRANSIENT); -+ }else{ -+ sqlite3_result_text(context, &zBuf[1], 10, SQLITE_TRANSIENT); -+ } - } - } - -+/* -+** Compute the number of days after the most recent January 1. -+** -+** In other words, compute the zero-based day number for the -+** current year: -+** -+** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ... -+** Dec31 = 364 or 365. -+*/ -+static int daysAfterJan01(DateTime *pDate){ -+ DateTime jan01 = *pDate; -+ assert( jan01.validYMD ); -+ assert( jan01.validHMS ); -+ assert( pDate->validJD ); -+ jan01.validJD = 0; -+ jan01.M = 1; -+ jan01.D = 1; -+ computeJD(&jan01); -+ return (int)((pDate->iJD-jan01.iJD+43200000)/86400000); -+} -+ -+/* -+** Return the number of days after the most recent Monday. -+** -+** In other words, return the day of the week according -+** to this code: -+** -+** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday. -+*/ -+static int daysAfterMonday(DateTime *pDate){ -+ assert( pDate->validJD ); -+ return (int)((pDate->iJD+43200000)/86400000) % 7; -+} -+ -+/* -+** Return the number of days after the most recent Sunday. -+** -+** In other words, return the day of the week according -+** to this code: -+** -+** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday -+*/ -+static int daysAfterSunday(DateTime *pDate){ -+ assert( pDate->validJD ); -+ return (int)((pDate->iJD+129600000)/86400000) % 7; -+} -+ - /* - ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) - ** - ** Return a string described by FORMAT. Conversions as follows: - ** --** %d day of month -+** %d day of month 01-31 -+** %e day of month 1-31 - ** %f ** fractional seconds SS.SSS -+** %F ISO date. YYYY-MM-DD -+** %G ISO year corresponding to %V 0000-9999. -+** %g 2-digit ISO year corresponding to %V 00-99 - ** %H hour 00-24 --** %j day of year 000-366 -+** %k hour 0-24 (leading zero converted to space) -+** %I hour 01-12 -+** %j day of year 001-366 - ** %J ** julian day number -+** %l hour 1-12 (leading zero converted to space) - ** %m month 01-12 - ** %M minute 00-59 -+** %p "am" or "pm" -+** %P "AM" or "PM" -+** %R time as HH:MM - ** %s seconds since 1970-01-01 - ** %S seconds 00-59 --** %w day of week 0-6 sunday==0 --** %W week of year 00-53 -+** %T time as HH:MM:SS -+** %u day of week 1-7 Monday==1, Sunday==7 -+** %w day of week 0-6 Sunday==0, Monday==1 -+** %U week of year 00-53 (First Sunday is start of week 01) -+** %V week of year 01-53 (First week containing Thursday is week 01) -+** %W week of year 00-53 (First Monday is start of week 01) - ** %Y year 0000-9999 - ** %% % - */ -@@ -22722,131 +26118,161 @@ static void strftimeFunc( - sqlite3_value **argv - ){ - DateTime x; -- u64 n; - size_t i,j; -- char *z; - sqlite3 *db; - const char *zFmt; -- char zBuf[100]; -+ sqlite3_str sRes; -+ -+ - if( argc==0 ) return; - zFmt = (const char*)sqlite3_value_text(argv[0]); - if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; - db = sqlite3_context_db_handle(context); -- for(i=0, n=1; zFmt[i]; i++, n++){ -- if( zFmt[i]=='%' ){ -- switch( zFmt[i+1] ){ -- case 'd': -- case 'H': -- case 'm': -- case 'M': -- case 'S': -- case 'W': -- n++; -- /* fall thru */ -- case 'w': -- case '%': -- break; -- case 'f': -- n += 8; -- break; -- case 'j': -- n += 3; -- break; -- case 'Y': -- n += 8; -- break; -- case 's': -- case 'J': -- n += 50; -- break; -- default: -- return; /* ERROR. return a NULL */ -- } -- i++; -- } -- } -- testcase( n==sizeof(zBuf)-1 ); -- testcase( n==sizeof(zBuf) ); -- testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); -- testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ); -- if( n(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){ -- sqlite3_result_error_toobig(context); -- return; -- }else{ -- z = sqlite3DbMallocRawNN(db, (int)n); -- if( z==0 ){ -- sqlite3_result_error_nomem(context); -- return; -- } -- } -+ sqlite3StrAccumInit(&sRes, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); -+ - computeJD(&x); - computeYMD_HMS(&x); - for(i=j=0; zFmt[i]; i++){ -- if( zFmt[i]!='%' ){ -- z[j++] = zFmt[i]; -- }else{ -- i++; -- switch( zFmt[i] ){ -- case 'd': sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break; -- case 'f': { -- double s = x.s; -- if( s>59.999 ) s = 59.999; -- sqlite3_snprintf(7, &z[j],"%06.3f", s); -- j += sqlite3Strlen30(&z[j]); -- break; -- } -- case 'H': sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break; -- case 'W': /* Fall thru */ -- case 'j': { -- int nDay; /* Number of days since 1st day of year */ -- DateTime y = x; -- y.validJD = 0; -- y.M = 1; -- y.D = 1; -- computeJD(&y); -- nDay = (int)((x.iJD-y.iJD+43200000)/86400000); -- if( zFmt[i]=='W' ){ -- int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ -- wd = (int)(((x.iJD+43200000)/86400000)%7); -- sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7); -- j += 2; -- }else{ -- sqlite3_snprintf(4, &z[j],"%03d",nDay+1); -- j += 3; -- } -- break; -+ char cf; -+ if( zFmt[i]!='%' ) continue; -+ if( j59.999) ) s = 59.999; -+ sqlite3_str_appendf(&sRes, "%06.3f", s); -+ break; -+ } -+ case 'F': { -+ sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D); -+ break; -+ } -+ case 'G': /* Fall thru */ -+ case 'g': { -+ DateTime y = x; -+ assert( y.validJD ); -+ /* Move y so that it is the Thursday in the same week as x */ -+ y.iJD += (3 - daysAfterMonday(&x))*86400000; -+ y.validYMD = 0; -+ computeYMD(&y); -+ if( cf=='g' ){ -+ sqlite3_str_appendf(&sRes, "%02d", y.Y%100); -+ }else{ -+ sqlite3_str_appendf(&sRes, "%04d", y.Y); - } -- case 'J': { -- sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0); -- j+=sqlite3Strlen30(&z[j]); -- break; -+ break; -+ } -+ case 'H': -+ case 'k': { -+ sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h); -+ break; -+ } -+ case 'I': /* Fall thru */ -+ case 'l': { -+ int h = x.h; -+ if( h>12 ) h -= 12; -+ if( h==0 ) h = 12; -+ sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); -+ break; -+ } -+ case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */ -+ sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1); -+ break; -+ } -+ case 'J': { /* Julian day number. (Non-standard) */ -+ sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); -+ break; -+ } -+ case 'm': { -+ sqlite3_str_appendf(&sRes,"%02d",x.M); -+ break; -+ } -+ case 'M': { -+ sqlite3_str_appendf(&sRes,"%02d",x.m); -+ break; -+ } -+ case 'p': /* Fall thru */ -+ case 'P': { -+ if( x.h>=12 ){ -+ sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2); -+ }else{ -+ sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2); - } -- case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; -- case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; -- case 's': { -+ break; -+ } -+ case 'R': { -+ sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m); -+ break; -+ } -+ case 's': { -+ if( x.useSubsec ){ -+ sqlite3_str_appendf(&sRes,"%.3f", -+ (x.iJD - 21086676*(i64)10000000)/1000.0); -+ }else{ - i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); -- sqlite3Int64ToText(iS, &z[j]); -- j += sqlite3Strlen30(&z[j]); -- break; -- } -- case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; -- case 'w': { -- z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; -- break; -+ sqlite3_str_appendf(&sRes,"%lld",iS); - } -- case 'Y': { -- sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]); -- break; -- } -- default: z[j++] = '%'; break; -+ break; -+ } -+ case 'S': { -+ sqlite3_str_appendf(&sRes,"%02d",(int)x.s); -+ break; -+ } -+ case 'T': { -+ sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); -+ break; -+ } -+ case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */ -+ case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */ -+ char c = (char)daysAfterSunday(&x) + '0'; -+ if( c=='0' && cf=='u' ) c = '7'; -+ sqlite3_str_appendchar(&sRes, 1, c); -+ break; -+ } -+ case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */ -+ sqlite3_str_appendf(&sRes,"%02d", -+ (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7); -+ break; -+ } -+ case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */ -+ DateTime y = x; -+ /* Adjust y so that is the Thursday in the same week as x */ -+ assert( y.validJD ); -+ y.iJD += (3 - daysAfterMonday(&x))*86400000; -+ y.validYMD = 0; -+ computeYMD(&y); -+ sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1); -+ break; -+ } -+ case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */ -+ sqlite3_str_appendf(&sRes,"%02d", -+ (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7); -+ break; -+ } -+ case 'Y': { -+ sqlite3_str_appendf(&sRes,"%04d",x.Y); -+ break; -+ } -+ case '%': { -+ sqlite3_str_appendchar(&sRes, 1, '%'); -+ break; -+ } -+ default: { -+ sqlite3_str_reset(&sRes); -+ return; - } - } - } -- z[j] = 0; -- sqlite3_result_text(context, z, -1, -- z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC); -+ if( j=d2.iJD ){ -+ sign = '+'; -+ Y = d1.Y - d2.Y; -+ if( Y ){ -+ d2.Y = d1.Y; -+ d2.validJD = 0; -+ computeJD(&d2); -+ } -+ M = d1.M - d2.M; -+ if( M<0 ){ -+ Y--; -+ M += 12; -+ } -+ if( M!=0 ){ -+ d2.M = d1.M; -+ d2.validJD = 0; -+ computeJD(&d2); -+ } -+ while( d1.iJDd2.iJD ){ -+ M--; -+ if( M<0 ){ -+ M = 11; -+ Y--; -+ } -+ d2.M++; -+ if( d2.M>12 ){ -+ d2.M = 1; -+ d2.Y++; -+ } -+ d2.validJD = 0; -+ computeJD(&d2); -+ } -+ d1.iJD = d2.iJD - d1.iJD; -+ d1.iJD += (u64)1486995408 * (u64)100000; -+ } -+ clearYMD_HMS_TZ(&d1); -+ computeYMD_HMS(&d1); -+ sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); -+ sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", -+ sign, Y, M, d1.D-1, d1.h, d1.m, d1.s); -+ sqlite3ResultStrAccum(context, &sRes); -+} -+ -+ - /* - ** current_timestamp() - ** -@@ -22937,6 +26472,36 @@ static void currentTimeFunc( - } - #endif - -+#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG) -+/* -+** datedebug(...) -+** -+** This routine returns JSON that describes the internal DateTime object. -+** Used for debugging and testing only. Subject to change. -+*/ -+static void datedebugFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ DateTime x; -+ if( isDate(context, argc, argv, &x)==0 ){ -+ char *zJson; -+ zJson = sqlite3_mprintf( -+ "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d," -+ "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d," -+ "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d," -+ "isUtc:%d,isLocal:%d}", -+ x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz, -+ x.s, x.validJD, x.validYMD, x.validHMS, -+ x.nFloor, x.rawS, x.isError, x.useSubsec, -+ x.isUtc, x.isLocal); -+ sqlite3_result_text(context, zJson, -1, sqlite3_free); -+ } -+} -+#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */ -+ -+ - /* - ** This function registered all of the above C functions as SQL - ** functions. This should be the only routine in this file with -@@ -22946,10 +26511,15 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){ - static FuncDef aDateTimeFuncs[] = { - #ifndef SQLITE_OMIT_DATETIME_FUNCS - PURE_DATE(julianday, -1, 0, 0, juliandayFunc ), -+ PURE_DATE(unixepoch, -1, 0, 0, unixepochFunc ), - PURE_DATE(date, -1, 0, 0, dateFunc ), - PURE_DATE(time, -1, 0, 0, timeFunc ), - PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), - PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), -+ PURE_DATE(timediff, 2, 0, 0, timediffFunc ), -+#ifdef SQLITE_DEBUG -+ PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ), -+#endif - DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), - DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), - DFUNCTION(current_date, 0, 0, 0, cdateFunc ), -@@ -23072,9 +26642,11 @@ SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){ - } - SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file *id, int lockType){ - DO_OS_MALLOC_TEST(id); -+ assert( lockType>=SQLITE_LOCK_SHARED && lockType<=SQLITE_LOCK_EXCLUSIVE ); - return id->pMethods->xLock(id, lockType); - } - SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file *id, int lockType){ -+ assert( lockType==SQLITE_LOCK_NONE || lockType==SQLITE_LOCK_SHARED ); - return id->pMethods->xUnlock(id, lockType); - } - SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){ -@@ -23095,17 +26667,24 @@ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ - #ifdef SQLITE_TEST - if( op!=SQLITE_FCNTL_COMMIT_PHASETWO - && op!=SQLITE_FCNTL_LOCK_TIMEOUT -+ && op!=SQLITE_FCNTL_CKPT_DONE -+ && op!=SQLITE_FCNTL_CKPT_START - ){ - /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite - ** is using a regular VFS, it is called after the corresponding - ** transaction has been committed. Injecting a fault at this point -- ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM -+ ** confuses the test scripts - the COMMIT command returns SQLITE_NOMEM - ** but the transaction is committed anyway. - ** - ** The core must call OsFileControl() though, not OsFileControlHint(), - ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably - ** means the commit really has failed and an error should be returned -- ** to the user. */ -+ ** to the user. -+ ** -+ ** The CKPT_DONE and CKPT_START file-controls are write-only signals -+ ** to the cksumvfs. Their return code is meaningless and is ignored -+ ** by the SQLite core, so there is no point in simulating OOMs for them. -+ */ - DO_OS_MALLOC_TEST(id); - } - #endif -@@ -23120,6 +26699,7 @@ SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){ - return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); - } - SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ -+ if( NEVER(id->pMethods==0) ) return 0; - return id->pMethods->xDeviceCharacteristics(id); - } - #ifndef SQLITE_OMIT_WAL -@@ -23181,6 +26761,7 @@ SQLITE_PRIVATE int sqlite3OsOpen( - ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, - ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before - ** reaching the VFS. */ -+ assert( zPath || (flags & SQLITE_OPEN_EXCLUSIVE) ); - rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut); - assert( rc==SQLITE_OK || pFile->pMethods==0 ); - return rc; -@@ -23188,7 +26769,7 @@ SQLITE_PRIVATE int sqlite3OsOpen( - SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ - DO_OS_MALLOC_TEST(0); - assert( dirSync==0 || dirSync==1 ); -- return pVfs->xDelete(pVfs, zPath, dirSync); -+ return pVfs->xDelete!=0 ? pVfs->xDelete(pVfs, zPath, dirSync) : SQLITE_OK; - } - SQLITE_PRIVATE int sqlite3OsAccess( - sqlite3_vfs *pVfs, -@@ -23211,6 +26792,8 @@ SQLITE_PRIVATE int sqlite3OsFullPathname( - } - #ifndef SQLITE_OMIT_LOAD_EXTENSION - SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ -+ assert( zPath!=0 ); -+ assert( strlen(zPath)<=SQLITE_MAX_PATHLEN ); /* tag-20210611-1 */ - return pVfs->xDlOpen(pVfs, zPath); - } - SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ -@@ -23272,12 +26855,15 @@ SQLITE_PRIVATE int sqlite3OsOpenMalloc( - rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags); - if( rc!=SQLITE_OK ){ - sqlite3_free(pFile); -+ *ppFile = 0; - }else{ - *ppFile = pFile; - } - }else{ -+ *ppFile = 0; - rc = SQLITE_NOMEM_BKPT; - } -+ assert( *ppFile!=0 || rc!=SQLITE_OK ); - return rc; - } - SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *pFile){ -@@ -23708,7 +27294,7 @@ static void *sqlite3MemMalloc(int nByte){ - ** or sqlite3MemRealloc(). - ** - ** For this low-level routine, we already know that pPrior!=0 since --** cases where pPrior==0 will have been intecepted and dealt with -+** cases where pPrior==0 will have been intercepted and dealt with - ** by higher-level routines. - */ - static void sqlite3MemFree(void *pPrior){ -@@ -23796,7 +27382,7 @@ static int sqlite3MemInit(void *NotUsed){ - return SQLITE_OK; - } - len = sizeof(cpuCount); -- /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ -+ /* One usually wants to use hw.activecpu for MT decisions, but not here */ - sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); - if( cpuCount>1 ){ - /* defer MT decisions to system malloc */ -@@ -23995,7 +27581,7 @@ static void adjustStats(int iSize, int increment){ - ** This routine checks the guards at either end of the allocation and - ** if they are incorrect it asserts. - */ --static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){ -+static struct MemBlockHdr *sqlite3MemsysGetHeader(const void *pAllocation){ - struct MemBlockHdr *p; - int *pInt; - u8 *pU8; -@@ -24242,7 +27828,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){ - ** - ** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - */ --SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){ -+SQLITE_PRIVATE int sqlite3MemdebugHasType(const void *p, u8 eType){ - int rc = 1; - if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ - struct MemBlockHdr *pHdr; -@@ -24264,7 +27850,7 @@ SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){ - ** - ** assert( sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); - */ --SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){ -+SQLITE_PRIVATE int sqlite3MemdebugNoType(const void *p, u8 eType){ - int rc = 1; - if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ - struct MemBlockHdr *pHdr; -@@ -25487,8 +29073,17 @@ static void *memsys5Realloc(void *pPrior, int nBytes){ - */ - static int memsys5Roundup(int n){ - int iFullSz; -- if( n > 0x40000000 ) return 0; -- for(iFullSz=mem5.szAtom; iFullSz0x10000000 ){ -+ if( n>0x40000000 ) return 0; -+ if( n>0x20000000 ) return 0x40000000; -+ return 0x20000000; -+ } -+ for(iFullSz=mem5.szAtom*8; iFullSz=(i64)n ) return iFullSz/2; - return iFullSz; - } - -@@ -25779,7 +29374,7 @@ static void checkMutexFree(sqlite3_mutex *p){ - assert( SQLITE_MUTEX_FAST<2 ); - assert( SQLITE_MUTEX_WARNONCONTENTION<2 ); - --#if SQLITE_ENABLE_API_ARMOR -+#ifdef SQLITE_ENABLE_API_ARMOR - if( ((CheckMutex*)p)->iType<2 ) - #endif - { -@@ -25993,16 +29588,29 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ - /* - ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are - ** intended for use inside assert() statements. -+** -+** Because these routines raise false-positive alerts in TSAN, disable -+** them (make them always return 1) when compiling with TSAN. - */ - SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ -+# if defined(__has_feature) -+# if __has_feature(thread_sanitizer) -+ p = 0; -+# endif -+# endif - assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld ); - return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); - } - SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ -+# if defined(__has_feature) -+# if __has_feature(thread_sanitizer) -+ p = 0; -+# endif -+# endif - assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld ); - return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); - } --#endif -+#endif /* NDEBUG */ - - #endif /* !defined(SQLITE_MUTEX_OMIT) */ - -@@ -26254,7 +29862,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ - - /* - ** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields --** are necessary under two condidtions: (1) Debug builds and (2) using -+** are necessary under two conditions: (1) Debug builds and (2) using - ** home-grown mutexes. Encapsulate these conditions into a single #define. - */ - #if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) -@@ -26451,7 +30059,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ - */ - static void pthreadMutexFree(sqlite3_mutex *p){ - assert( p->nRef==0 ); --#if SQLITE_ENABLE_API_ARMOR -+#ifdef SQLITE_ENABLE_API_ARMOR - if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) - #endif - { -@@ -26642,205 +30250,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ - /* - ** Include code that is common to all os_*.c files - */ --/************** Include os_common.h in the middle of mutex_w32.c *************/ --/************** Begin file os_common.h ***************************************/ --/* --** 2004 May 22 --** --** 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 contains macros and a little bit of code that is common to --** all of the platform-specific files (os_*.c) and is #included into those --** files. --** --** This file should be #included by the os_*.c files only. It is not a --** general purpose header file. --*/ --#ifndef _OS_COMMON_H_ --#define _OS_COMMON_H_ -- --/* --** At least two bugs have slipped in because we changed the MEMORY_DEBUG --** macro to SQLITE_DEBUG and some older makefiles have not yet made the --** switch. The following code should catch this problem at compile-time. --*/ --#ifdef MEMORY_DEBUG --# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." --#endif -- --/* --** Macros for performance tracing. Normally turned off. Only works --** on i486 hardware. --*/ --#ifdef SQLITE_PERFORMANCE_TRACE -- --/* --** hwtime.h contains inline assembler code for implementing --** high-performance timing routines. --*/ --/************** Include hwtime.h in the middle of os_common.h ****************/ --/************** Begin file hwtime.h ******************************************/ --/* --** 2008 May 27 --** --** 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 contains inline asm code for retrieving "high-performance" --** counters for x86 and x86_64 class CPUs. --*/ --#ifndef SQLITE_HWTIME_H --#define SQLITE_HWTIME_H -- --/* --** The following routine only works on pentium-class (or newer) processors. --** It uses the RDTSC opcode to read the cycle count value out of the --** processor and returns that value. This can be used for high-res --** profiling. --*/ --#if !defined(__STRICT_ANSI__) && \ -- (defined(__GNUC__) || defined(_MSC_VER)) && \ -- (defined(i386) || defined(__i386__) || defined(_M_IX86)) -- -- #if defined(__GNUC__) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned int lo, hi; -- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); -- return (sqlite_uint64)hi << 32 | lo; -- } -- -- #elif defined(_MSC_VER) -- -- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ -- __asm { -- rdtsc -- ret ; return value at EDX:EAX -- } -- } -- -- #endif -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long val; -- __asm__ __volatile__ ("rdtsc" : "=A" (val)); -- return val; -- } -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long long retval; -- unsigned long junk; -- __asm__ __volatile__ ("\n\ -- 1: mftbu %1\n\ -- mftb %L0\n\ -- mftbu %0\n\ -- cmpw %0,%1\n\ -- bne 1b" -- : "=r" (retval), "=r" (junk)); -- return retval; -- } -- --#else -- -- /* -- ** asm() is needed for hardware timing support. Without asm(), -- ** disable the sqlite3Hwtime() routine. -- ** -- ** sqlite3Hwtime() is only used for some obscure debugging -- ** and analysis configurations, not in any deliverable, so this -- ** should not be a great loss. -- */ --SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } -- --#endif -- --#endif /* !defined(SQLITE_HWTIME_H) */ -- --/************** End of hwtime.h **********************************************/ --/************** Continuing where we left off in os_common.h ******************/ -- --static sqlite_uint64 g_start; --static sqlite_uint64 g_elapsed; --#define TIMER_START g_start=sqlite3Hwtime() --#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start --#define TIMER_ELAPSED g_elapsed --#else --#define TIMER_START --#define TIMER_END --#define TIMER_ELAPSED ((sqlite_uint64)0) --#endif -- --/* --** If we compile with the SQLITE_TEST macro set, then the following block --** of code will give us the ability to simulate a disk I/O error. This --** is used for testing the I/O recovery logic. --*/ --#if defined(SQLITE_TEST) --SQLITE_API extern int sqlite3_io_error_hit; --SQLITE_API extern int sqlite3_io_error_hardhit; --SQLITE_API extern int sqlite3_io_error_pending; --SQLITE_API extern int sqlite3_io_error_persist; --SQLITE_API extern int sqlite3_io_error_benign; --SQLITE_API extern int sqlite3_diskfull_pending; --SQLITE_API extern int sqlite3_diskfull; --#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) --#define SimulateIOError(CODE) \ -- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ -- || sqlite3_io_error_pending-- == 1 ) \ -- { local_ioerr(); CODE; } --static void local_ioerr(){ -- IOTRACE(("IOERR\n")); -- sqlite3_io_error_hit++; -- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; --} --#define SimulateDiskfullError(CODE) \ -- if( sqlite3_diskfull_pending ){ \ -- if( sqlite3_diskfull_pending == 1 ){ \ -- local_ioerr(); \ -- sqlite3_diskfull = 1; \ -- sqlite3_io_error_hit = 1; \ -- CODE; \ -- }else{ \ -- sqlite3_diskfull_pending--; \ -- } \ -- } --#else --#define SimulateIOErrorBenign(X) --#define SimulateIOError(A) --#define SimulateDiskfullError(A) --#endif /* defined(SQLITE_TEST) */ -- --/* --** When testing, keep a count of the number of open files. --*/ --#if defined(SQLITE_TEST) --SQLITE_API extern int sqlite3_open_file_count; --#define OpenCounter(X) sqlite3_open_file_count+=(X) --#else --#define OpenCounter(X) --#endif /* defined(SQLITE_TEST) */ -- --#endif /* !defined(_OS_COMMON_H_) */ -- --/************** End of os_common.h *******************************************/ --/************** Continuing where we left off in mutex_w32.c ******************/ -+/* #include "os_common.h" */ - - /* - ** Include the header file for the Windows VFS. -@@ -26871,6 +30281,8 @@ SQLITE_API extern int sqlite3_open_file_count; - - #ifdef __CYGWIN__ - # include -+# include /* amalgamator: dontcache */ -+# include /* amalgamator: dontcache */ - # include /* amalgamator: dontcache */ - #endif - -@@ -26953,7 +30365,7 @@ struct sqlite3_mutex { - CRITICAL_SECTION mutex; /* Mutex controlling the lock */ - int id; /* Mutex type */ - #ifdef SQLITE_DEBUG -- volatile int nRef; /* Number of enterances */ -+ volatile int nRef; /* Number of entrances */ - volatile DWORD owner; /* Thread holding this mutex */ - volatile LONG trace; /* True to trace changes */ - #endif -@@ -27002,7 +30414,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){ - SQLITE_MEMORY_BARRIER; - #elif defined(__GNUC__) - __sync_synchronize(); --#elif MSVC_VERSION>=1300 -+#elif MSVC_VERSION>=1400 - _ReadWriteBarrier(); - #elif defined(MemoryBarrier) - MemoryBarrier(); -@@ -27478,7 +30890,6 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){ - if( sqlite3GlobalConfig.m.xMalloc==0 ){ - sqlite3MemSetDefault(); - } -- memset(&mem0, 0, sizeof(mem0)); - mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 - || sqlite3GlobalConfig.nPage<=0 ){ -@@ -27539,6 +30950,24 @@ static void sqlite3MallocAlarm(int nByte){ - sqlite3_mutex_enter(mem0.mutex); - } - -+#ifdef SQLITE_DEBUG -+/* -+** This routine is called whenever an out-of-memory condition is seen, -+** It's only purpose to to serve as a breakpoint for gdb or similar -+** code debuggers when working on out-of-memory conditions, for example -+** caused by PRAGMA hard_heap_limit=N. -+*/ -+static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){ -+ static u64 nOomFault = 0; -+ nOomFault += n; -+ /* The assert() is never reached in a human lifetime. It is here mostly -+ ** to prevent code optimizers from optimizing out this function. */ -+ assert( (nOomFault>>32) < 0xffffffff ); -+} -+#else -+# define test_oom_breakpoint(X) /* No-op for production builds */ -+#endif -+ - /* - ** Do a memory allocation with statistics and alarms. Assume the - ** lock is already held. -@@ -27565,6 +30994,7 @@ static void mallocWithAlarm(int n, void **pp){ - if( mem0.hardLimit ){ - nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - if( nUsed >= mem0.hardLimit - nFull ){ -+ test_oom_breakpoint(1); - *pp = 0; - return; - } -@@ -27588,18 +31018,34 @@ static void mallocWithAlarm(int n, void **pp){ - *pp = p; - } - -+/* -+** Maximum size of any single memory allocation. -+** -+** This is not a limit on the total amount of memory used. This is -+** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc(). -+** -+** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 -+** This provides a 256-byte safety margin for defense against 32-bit -+** signed integer overflow bugs when computing memory allocation sizes. -+** Paranoid applications might want to reduce the maximum allocation size -+** further for an even larger safety margin. 0x3fffffff or 0x0fffffff -+** or even smaller would be reasonable upper bounds on the size of a memory -+** allocations for most applications. -+*/ -+#ifndef SQLITE_MAX_ALLOCATION_SIZE -+# define SQLITE_MAX_ALLOCATION_SIZE 2147483391 -+#endif -+#if SQLITE_MAX_ALLOCATION_SIZE>2147483391 -+# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391 -+#endif -+ - /* - ** Allocate memory. This routine is like sqlite3_malloc() except that it - ** assumes the memory subsystem has already been initialized. - */ - SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ - void *p; -- if( n==0 || n>=0x7fffff00 ){ -- /* A memory allocation of a number of bytes which is near the maximum -- ** signed integer value might cause an integer overflow inside of the -- ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving -- ** 255 bytes of overhead. SQLite itself will never use anything near -- ** this amount. The only way to reach the limit is with sqlite3_malloc() */ -+ if( n==0 || n>SQLITE_MAX_ALLOCATION_SIZE ){ - p = 0; - }else if( sqlite3GlobalConfig.bMemstat ){ - sqlite3_mutex_enter(mem0.mutex); -@@ -27634,8 +31080,8 @@ SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ - ** TRUE if p is a lookaside memory allocation from db - */ - #ifndef SQLITE_OMIT_LOOKASIDE --static int isLookaside(sqlite3 *db, void *p){ -- return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); -+static int isLookaside(sqlite3 *db, const void *p){ -+ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pTrueEnd); - } - #else - #define isLookaside(A,B) 0 -@@ -27645,32 +31091,30 @@ static int isLookaside(sqlite3 *db, void *p){ - ** Return the size of a memory allocation previously obtained from - ** sqlite3Malloc() or sqlite3_malloc(). - */ --SQLITE_PRIVATE int sqlite3MallocSize(void *p){ -+SQLITE_PRIVATE int sqlite3MallocSize(const void *p){ - assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); -- return sqlite3GlobalConfig.m.xSize(p); -+ return sqlite3GlobalConfig.m.xSize((void*)p); - } --static int lookasideMallocSize(sqlite3 *db, void *p){ -+static int lookasideMallocSize(sqlite3 *db, const void *p){ - #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - return plookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL; - #else - return db->lookaside.szTrue; - #endif - } --SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ -+SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, const void *p){ - assert( p!=0 ); - #ifdef SQLITE_DEBUG -- if( db==0 || !isLookaside(db,p) ){ -- if( db==0 ){ -- assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); -- assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); -- }else{ -- assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); -- assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); -- } -+ if( db==0 ){ -+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); -+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); -+ }else if( !isLookaside(db,p) ){ -+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); -+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - } - #endif - if( db ){ -- if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ -+ if( ((uptr)p)<(uptr)(db->lookaside.pTrueEnd) ){ - #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ - assert( sqlite3_mutex_held(db->mutex) ); -@@ -27683,7 +31127,7 @@ SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ - } - } - } -- return sqlite3GlobalConfig.m.xSize(p); -+ return sqlite3GlobalConfig.m.xSize((void*)p); - } - SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ - assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); -@@ -27726,14 +31170,11 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ - assert( db==0 || sqlite3_mutex_held(db->mutex) ); - assert( p!=0 ); - if( db ){ -- if( db->pnBytesFreed ){ -- measureAllocationSize(db, p); -- return; -- } - if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ - #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ - LookasideSlot *pBuf = (LookasideSlot*)p; -+ assert( db->pnBytesFreed==0 ); - #ifdef SQLITE_DEBUG - memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ - #endif -@@ -27744,6 +31185,7 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ - #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ - if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ - LookasideSlot *pBuf = (LookasideSlot*)p; -+ assert( db->pnBytesFreed==0 ); - #ifdef SQLITE_DEBUG - memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ - #endif -@@ -27752,6 +31194,10 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ - return; - } - } -+ if( db->pnBytesFreed ){ -+ measureAllocationSize(db, p); -+ return; -+ } - } - assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); -@@ -27759,6 +31205,43 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){ - sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - sqlite3_free(p); - } -+SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3 *db, void *p){ -+ assert( db!=0 ); -+ assert( sqlite3_mutex_held(db->mutex) ); -+ assert( p!=0 ); -+ if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){ -+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE -+ if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){ -+ LookasideSlot *pBuf = (LookasideSlot*)p; -+ assert( db->pnBytesFreed==0 ); -+#ifdef SQLITE_DEBUG -+ memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */ -+#endif -+ pBuf->pNext = db->lookaside.pSmallFree; -+ db->lookaside.pSmallFree = pBuf; -+ return; -+ } -+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ -+ if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){ -+ LookasideSlot *pBuf = (LookasideSlot*)p; -+ assert( db->pnBytesFreed==0 ); -+#ifdef SQLITE_DEBUG -+ memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */ -+#endif -+ pBuf->pNext = db->lookaside.pFree; -+ db->lookaside.pFree = pBuf; -+ return; -+ } -+ } -+ if( db->pnBytesFreed ){ -+ measureAllocationSize(db, p); -+ return; -+ } -+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); -+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); -+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP); -+ sqlite3_free(p); -+} - SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){ - assert( db==0 || sqlite3_mutex_held(db->mutex) ); - if( p ) sqlite3DbFreeNN(db, p); -@@ -27791,12 +31274,18 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ - if( nOld==nNew ){ - pNew = pOld; - }else if( sqlite3GlobalConfig.bMemstat ){ -+ sqlite3_int64 nUsed; - sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); - nDiff = nNew - nOld; -- if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= -+ if( nDiff>0 && (nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >= - mem0.alarmThreshold-nDiff ){ - sqlite3MallocAlarm(nDiff); -+ if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ -+ sqlite3_mutex_leave(mem0.mutex); -+ test_oom_breakpoint(1); -+ return 0; -+ } - } - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -@@ -28053,9 +31542,14 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){ - */ - SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){ - int n; -+#ifdef SQLITE_DEBUG -+ /* Because of the way the parser works, the span is guaranteed to contain -+ ** at least one non-space character */ -+ for(n=0; sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n]0) && sqlite3Isspace(zStart[n-1]) ) n--; -+ while( sqlite3Isspace(zStart[n-1]) ) n--; - return sqlite3DbStrNDup(db, zStart, n); - } - -@@ -28063,8 +31557,9 @@ SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const cha - ** Free any prior content in *pz and replace it with a copy of zNew. - */ - SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){ -+ char *z = sqlite3DbStrDup(db, zNew); - sqlite3DbFree(db, *pz); -- *pz = sqlite3DbStrDup(db, zNew); -+ *pz = z; - } - - /* -@@ -28072,8 +31567,15 @@ SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){ - ** has happened. This routine will set db->mallocFailed, and also - ** temporarily disable the lookaside memory allocator and interrupt - ** any running VDBEs. -+** -+** Always return a NULL pointer so that this routine can be invoked using -+** -+** return sqlite3OomFault(db); -+** -+** and thereby avoid unnecessary stack frame allocations for the overwhelmingly -+** common case where no OOM occurs. - */ --SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ -+SQLITE_PRIVATE void *sqlite3OomFault(sqlite3 *db){ - if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ - db->mallocFailed = 1; - if( db->nVdbeExec>0 ){ -@@ -28081,9 +31583,16 @@ SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){ - } - DisableLookaside; - if( db->pParse ){ -+ Parse *pParse; -+ sqlite3ErrorMsg(db->pParse, "out of memory"); - db->pParse->rc = SQLITE_NOMEM_BKPT; -+ for(pParse=db->pParse->pOuterParse; pParse; pParse = pParse->pOuterParse){ -+ pParse->nErr++; -+ pParse->rc = SQLITE_NOMEM; -+ } - } - } -+ return 0; - } - - /* -@@ -28103,12 +31612,15 @@ SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){ - } - - /* --** Take actions at the end of an API call to indicate an OOM error -+** Take actions at the end of an API call to deal with error codes. - */ --static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ -- sqlite3OomClear(db); -- sqlite3Error(db, SQLITE_NOMEM); -- return SQLITE_NOMEM_BKPT; -+static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){ -+ if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ -+ sqlite3OomClear(db); -+ sqlite3Error(db, SQLITE_NOMEM); -+ return SQLITE_NOMEM_BKPT; -+ } -+ return rc & db->errMask; - } - - /* -@@ -28130,10 +31642,10 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ - */ - assert( db!=0 ); - assert( sqlite3_mutex_held(db->mutex) ); -- if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ -- return apiOomError(db); -+ if( db->mallocFailed || rc ){ -+ return apiHandleError(db, rc); - } -- return rc & db->errMask; -+ return 0; - } - - /************** End of malloc.c **********************************************/ -@@ -28165,17 +31677,17 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ - #define etPERCENT 7 /* Percent symbol. %% */ - #define etCHARX 8 /* Characters. %c */ - /* The rest are extensions, not normally found in printf() */ --#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ --#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', -- NULL pointers replaced by SQL NULL. %Q */ --#define etTOKEN 11 /* a pointer to a Token structure */ --#define etSRCLIST 12 /* a pointer to a SrcList */ --#define etPOINTER 13 /* The %p conversion */ --#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ --#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ --#define etDECIMAL 16 /* %d or %u, but not %x, %o */ -+#define etESCAPE_q 9 /* Strings with '\'' doubled. %q */ -+#define etESCAPE_Q 10 /* Strings with '\'' doubled and enclosed in '', -+ NULL pointers replaced by SQL NULL. %Q */ -+#define etTOKEN 11 /* a pointer to a Token structure */ -+#define etSRCITEM 12 /* a pointer to a SrcItem */ -+#define etPOINTER 13 /* The %p conversion */ -+#define etESCAPE_w 14 /* %w -> Strings with '\"' doubled */ -+#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ -+#define etDECIMAL 16 /* %d or %u, but not %x, %o */ - --#define etINVALID 17 /* Any unrecognized conversion type */ -+#define etINVALID 17 /* Any unrecognized conversion type */ - - - /* -@@ -28214,9 +31726,9 @@ static const et_info fmtinfo[] = { - { 's', 0, 4, etSTRING, 0, 0 }, - { 'g', 0, 1, etGENERIC, 30, 0 }, - { 'z', 0, 4, etDYNSTRING, 0, 0 }, -- { 'q', 0, 4, etSQLESCAPE, 0, 0 }, -- { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, -- { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, -+ { 'q', 0, 4, etESCAPE_q, 0, 0 }, -+ { 'Q', 0, 4, etESCAPE_Q, 0, 0 }, -+ { 'w', 0, 4, etESCAPE_w, 0, 0 }, - { 'c', 0, 0, etCHARX, 0, 0 }, - { 'o', 8, 0, etRADIX, 0, 2 }, - { 'u', 10, 0, etDECIMAL, 0, 0 }, -@@ -28235,51 +31747,20 @@ static const et_info fmtinfo[] = { - - /* All the rest are undocumented and are for internal use only */ - { 'T', 0, 0, etTOKEN, 0, 0 }, -- { 'S', 0, 0, etSRCLIST, 0, 0 }, -+ { 'S', 0, 0, etSRCITEM, 0, 0 }, - { 'r', 10, 1, etORDINAL, 0, 0 }, - }; - --/* Floating point constants used for rounding */ --static const double arRound[] = { -- 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, -- 5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10, --}; -- --/* --** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point --** conversions will work. --*/ --#ifndef SQLITE_OMIT_FLOATING_POINT --/* --** "*val" is a double such that 0.1 <= *val < 10.0 --** Return the ascii code for the leading digit of *val, then --** multiply "*val" by 10.0 to renormalize. -+/* Notes: - ** --** Example: --** input: *val = 3.14159 --** output: *val = 1.4159 function return = '3' --** --** The counter *cnt is incremented each time. After counter exceeds --** 16 (the number of significant digits in a 64-bit float) '0' is --** always returned. --*/ --static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ -- int digit; -- LONGDOUBLE_TYPE d; -- if( (*cnt)<=0 ) return '0'; -- (*cnt)--; -- digit = (int)*val; -- d = digit; -- digit += '0'; -- *val = (*val - d)*10.0; -- return (char)digit; --} --#endif /* SQLITE_OMIT_FLOATING_POINT */ -+** %S Takes a pointer to SrcItem. Shows name or database.name -+** %!S Like %S but prefer the zName over the zAlias -+*/ - - /* - ** Set the StrAccum object to an error mode. - */ --static void setStrAccumError(StrAccum *p, u8 eError){ -+SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum *p, u8 eError){ - assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); - p->accError = eError; - if( p->mxAlloc ) sqlite3_str_reset(p); -@@ -28315,12 +31796,12 @@ static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ - char *z; - if( pAccum->accError ) return 0; - if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ -- setStrAccumError(pAccum, SQLITE_TOOBIG); -+ sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG); - return 0; - } - z = sqlite3DbMallocRaw(pAccum->db, n); - if( z==0 ){ -- setStrAccumError(pAccum, SQLITE_NOMEM); -+ sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM); - } - return z; - } -@@ -28367,18 +31848,15 @@ SQLITE_API void sqlite3_str_vappendf( - u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ - char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ - sqlite_uint64 longvalue; /* Value for integer types */ -- LONGDOUBLE_TYPE realvalue; /* Value for real types */ -+ double realvalue; /* Value for real types */ - const et_info *infop; /* Pointer to the appropriate info structure */ - char *zOut; /* Rendering buffer */ - int nOut; /* Size of the rendering buffer */ - char *zExtra = 0; /* Malloced memory used by some conversion */ --#ifndef SQLITE_OMIT_FLOATING_POINT -- int exp, e2; /* exponent of real numbers */ -- int nsd; /* Number of significant digits returned */ -- double rounder; /* Used for rounding floating point values */ -+ int exp, e2; /* exponent of real numbers */ - etByte flag_dp; /* True if decimal point should be shown */ - etByte flag_rtz; /* True if trailing zeros should be removed */ --#endif -+ - PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ - char buf[etBUFSIZE]; /* Conversion buffer */ - -@@ -28567,11 +32045,10 @@ SQLITE_API void sqlite3_str_vappendf( - v = va_arg(ap,int); - } - if( v<0 ){ -- if( v==SMALLEST_INT64 ){ -- longvalue = ((u64)1)<<63; -- }else{ -- longvalue = -v; -- } -+ testcase( v==SMALLEST_INT64 ); -+ testcase( v==(-1) ); -+ longvalue = ~v; -+ longvalue++; - prefix = '-'; - }else{ - longvalue = v; -@@ -28654,74 +32131,69 @@ SQLITE_API void sqlite3_str_vappendf( - break; - case etFLOAT: - case etEXP: -- case etGENERIC: -+ case etGENERIC: { -+ FpDecode s; -+ int iRound; -+ int j; -+ - if( bArgList ){ - realvalue = getDoubleArg(pArgList); - }else{ - realvalue = va_arg(ap,double); - } --#ifdef SQLITE_OMIT_FLOATING_POINT -- length = 0; --#else - if( precision<0 ) precision = 6; /* Set default precision */ - #ifdef SQLITE_FP_PRECISION_LIMIT - if( precision>SQLITE_FP_PRECISION_LIMIT ){ - precision = SQLITE_FP_PRECISION_LIMIT; - } - #endif -- if( realvalue<0.0 ){ -- realvalue = -realvalue; -- prefix = '-'; -- }else{ -- prefix = flag_prefix; -- } -- if( xtype==etGENERIC && precision>0 ) precision--; -- testcase( precision>0xfff ); -- idx = precision & 0xfff; -- rounder = arRound[idx%10]; -- while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } - if( xtype==etFLOAT ){ -- double rx = (double)realvalue; -- sqlite3_uint64 u; -- int ex; -- memcpy(&u, &rx, sizeof(u)); -- ex = -1023 + (int)((u>>52)&0x7ff); -- if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; -- realvalue += rounder; -- } -- /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ -- exp = 0; -- if( sqlite3IsNaN((double)realvalue) ){ -- bufpt = "NaN"; -- length = 3; -- break; -+ iRound = -precision; -+ }else if( xtype==etGENERIC ){ -+ if( precision==0 ) precision = 1; -+ iRound = precision; -+ }else{ -+ iRound = precision+1; - } -- if( realvalue>0.0 ){ -- LONGDOUBLE_TYPE scale = 1.0; -- while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} -- while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; } -- while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } -- realvalue /= scale; -- while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } -- while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } -- if( exp>350 ){ -+ sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); -+ if( s.isSpecial ){ -+ if( s.isSpecial==2 ){ -+ bufpt = flag_zeropad ? "null" : "NaN"; -+ length = sqlite3Strlen30(bufpt); -+ break; -+ }else if( flag_zeropad ){ -+ s.z[0] = '9'; -+ s.iDP = 1000; -+ s.n = 1; -+ }else{ -+ memcpy(buf, "-Inf", 5); - bufpt = buf; -- buf[0] = prefix; -- memcpy(buf+(prefix!=0),"Inf",4); -- length = 3+(prefix!=0); -+ if( s.sign=='-' ){ -+ /* no-op */ -+ }else if( flag_prefix ){ -+ buf[0] = flag_prefix; -+ }else{ -+ bufpt++; -+ } -+ length = sqlite3Strlen30(bufpt); - break; - } - } -- bufpt = buf; -+ if( s.sign=='-' ){ -+ prefix = '-'; -+ }else{ -+ prefix = flag_prefix; -+ } -+ -+ exp = s.iDP-1; -+ - /* - ** If the field type is etGENERIC, then convert to either etEXP - ** or etFLOAT, as appropriate. - */ -- if( xtype!=etFLOAT ){ -- realvalue += rounder; -- if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } -- } - if( xtype==etGENERIC ){ -+ assert( precision>0 ); -+ precision--; - flag_rtz = !flag_alternateform; - if( exp<-4 || exp>precision ){ - xtype = etEXP; -@@ -28735,29 +32207,32 @@ SQLITE_API void sqlite3_str_vappendf( - if( xtype==etEXP ){ - e2 = 0; - }else{ -- e2 = exp; -+ e2 = s.iDP - 1; - } -+ bufpt = buf; - { - i64 szBufNeeded; /* Size of a temporary buffer needed */ - szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; -+ if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; - if( szBufNeeded > etBUFSIZE ){ - bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); - if( bufpt==0 ) return; - } - } - zOut = bufpt; -- nsd = 16 + flag_altform2*10; - flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; - /* The sign in front of the number */ - if( prefix ){ - *(bufpt++) = prefix; - } - /* Digits prior to the decimal point */ -+ j = 0; - if( e2<0 ){ - *(bufpt++) = '0'; - }else{ - for(; e2>=0; e2--){ -- *(bufpt++) = et_getdigit(&realvalue,&nsd); -+ *(bufpt++) = j1 ) *(bufpt++) = ','; - } - } - /* The decimal point */ -@@ -28766,13 +32241,12 @@ SQLITE_API void sqlite3_str_vappendf( - } - /* "0" digits after the decimal point but before the first - ** significant digit of the number */ -- for(e2++; e2<0; precision--, e2++){ -- assert( precision>0 ); -+ for(e2++; e2<0 && precision>0; precision--, e2++){ - *(bufpt++) = '0'; - } - /* Significant digits after the decimal point */ - while( (precision--)>0 ){ -- *(bufpt++) = et_getdigit(&realvalue,&nsd); -+ *(bufpt++) = jcharset]; - if( exp<0 ){ - *(bufpt++) = '-'; exp = -exp; -@@ -28821,8 +32296,8 @@ SQLITE_API void sqlite3_str_vappendf( - while( nPad-- ) bufpt[i++] = '0'; - length = width; - } --#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ - break; -+ } - case etSIZE: - if( !bArgList ){ - *(va_arg(ap,int*)) = pAccum->nChar; -@@ -28850,34 +32325,29 @@ SQLITE_API void sqlite3_str_vappendf( - } - }else{ - unsigned int ch = va_arg(ap,unsigned int); -- if( ch<0x00080 ){ -- buf[0] = ch & 0xff; -- length = 1; -- }else if( ch<0x00800 ){ -- buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); -- buf[1] = 0x80 + (u8)(ch & 0x3f); -- length = 2; -- }else if( ch<0x10000 ){ -- buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); -- buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); -- buf[2] = 0x80 + (u8)(ch & 0x3f); -- length = 3; -- }else{ -- buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); -- buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); -- buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); -- buf[3] = 0x80 + (u8)(ch & 0x3f); -- length = 4; -- } -+ length = sqlite3AppendOneUtf8Character(buf, ch); - } - if( precision>1 ){ -+ i64 nPrior = 1; - width -= precision-1; - if( width>1 && !flag_leftjustify ){ - sqlite3_str_appendchar(pAccum, width-1, ' '); - width = 0; - } -- while( precision-- > 1 ){ -- sqlite3_str_append(pAccum, buf, length); -+ sqlite3_str_append(pAccum, buf, length); -+ precision--; -+ while( precision > 1 ){ -+ i64 nCopyBytes; -+ if( nPrior > precision-1 ) nPrior = precision - 1; -+ nCopyBytes = length*nPrior; -+ if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){ -+ sqlite3StrAccumEnlarge(pAccum, nCopyBytes); -+ } -+ if( pAccum->accError ) break; -+ sqlite3_str_append(pAccum, -+ &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes); -+ precision -= nPrior; -+ nPrior *= 2; - } - } - bufpt = buf; -@@ -28935,22 +32405,31 @@ SQLITE_API void sqlite3_str_vappendf( - while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; - } - break; -- case etSQLESCAPE: /* %q: Escape ' characters */ -- case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ -- case etSQLESCAPE3: { /* %w: Escape " characters */ -- int i, j, k, n, isnull; -- int needQuote; -+ case etESCAPE_q: /* %q: Escape ' characters */ -+ case etESCAPE_Q: /* %Q: Escape ' and enclose in '...' */ -+ case etESCAPE_w: { /* %w: Escape " characters */ -+ i64 i, j, k, n; -+ int needQuote = 0; - char ch; -- char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ - char *escarg; -+ char q; - - if( bArgList ){ - escarg = getTextArg(pArgList); - }else{ - escarg = va_arg(ap,char*); - } -- isnull = escarg==0; -- if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); -+ if( escarg==0 ){ -+ escarg = (xtype==etESCAPE_Q ? "NULL" : "(NULL)"); -+ }else if( xtype==etESCAPE_Q ){ -+ needQuote = 1; -+ } -+ if( xtype==etESCAPE_w ){ -+ q = '"'; -+ flag_alternateform = 0; -+ }else{ -+ q = '\''; -+ } - /* For %q, %Q, and %w, the precision is the number of bytes (or - ** characters if the ! flags is present) to use from the input. - ** Because of the extra quoting characters inserted, the number -@@ -28963,7 +32442,30 @@ SQLITE_API void sqlite3_str_vappendf( - while( (escarg[i+1]&0xc0)==0x80 ){ i++; } - } - } -- needQuote = !isnull && xtype==etSQLESCAPE2; -+ if( flag_alternateform ){ -+ /* For %#q, do unistr()-style backslash escapes for -+ ** all control characters, and for backslash itself. -+ ** For %#Q, do the same but only if there is at least -+ ** one control character. */ -+ u32 nBack = 0; -+ u32 nCtrl = 0; -+ for(k=0; ketBUFSIZE ){ - bufpt = zExtra = printfTempBuf(pAccum, n); -@@ -28972,43 +32474,97 @@ SQLITE_API void sqlite3_str_vappendf( - bufpt = buf; - } - j = 0; -- if( needQuote ) bufpt[j++] = q; -+ if( needQuote ){ -+ if( needQuote==2 ){ -+ memcpy(&bufpt[j], "unistr('", 8); -+ j += 8; -+ }else{ -+ bufpt[j++] = '\''; -+ } -+ } - k = i; -- for(i=0; i=0x10 ? '1' : '0'; -+ bufpt[j++] = "0123456789abcdef"[ch&0xf]; -+ } -+ } -+ }else{ -+ for(i=0; iprintfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; -- pToken = va_arg(ap, Token*); -- assert( bArgList==0 ); -- if( pToken && pToken->n ){ -- sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); -+ if( flag_alternateform ){ -+ /* %#T means an Expr pointer that uses Expr.u.zToken */ -+ Expr *pExpr = va_arg(ap,Expr*); -+ if( ALWAYS(pExpr) && ALWAYS(!ExprHasProperty(pExpr,EP_IntValue)) ){ -+ sqlite3_str_appendall(pAccum, (const char*)pExpr->u.zToken); -+ sqlite3RecordErrorOffsetOfExpr(pAccum->db, pExpr); -+ } -+ }else{ -+ /* %T means a Token pointer */ -+ Token *pToken = va_arg(ap, Token*); -+ assert( bArgList==0 ); -+ if( pToken && pToken->n ){ -+ sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n); -+ sqlite3RecordErrorByteOffset(pAccum->db, pToken->z); -+ } - } - length = width = 0; - break; - } -- case etSRCLIST: { -- SrcList *pSrc; -- int k; -- struct SrcList_item *pItem; -+ case etSRCITEM: { -+ SrcItem *pItem; - if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; -- pSrc = va_arg(ap, SrcList*); -- k = va_arg(ap, int); -- pItem = &pSrc->a[k]; -+ pItem = va_arg(ap, SrcItem*); - assert( bArgList==0 ); -- assert( k>=0 && knSrc ); -- if( pItem->zDatabase ){ -- sqlite3_str_appendall(pAccum, pItem->zDatabase); -- sqlite3_str_append(pAccum, ".", 1); -+ if( pItem->zAlias && !flag_altform2 ){ -+ sqlite3_str_appendall(pAccum, pItem->zAlias); -+ }else if( pItem->zName ){ -+ if( pItem->fg.fixedSchema==0 -+ && pItem->fg.isSubquery==0 -+ && pItem->u4.zDatabase!=0 -+ ){ -+ sqlite3_str_appendall(pAccum, pItem->u4.zDatabase); -+ sqlite3_str_append(pAccum, ".", 1); -+ } -+ sqlite3_str_appendall(pAccum, pItem->zName); -+ }else if( pItem->zAlias ){ -+ sqlite3_str_appendall(pAccum, pItem->zAlias); -+ }else if( ALWAYS(pItem->fg.isSubquery) ){/* Because of tag-20240424-1 */ -+ Select *pSel = pItem->u4.pSubq->pSelect; -+ assert( pSel!=0 ); -+ if( pSel->selFlags & SF_NestedFrom ){ -+ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); -+ }else if( pSel->selFlags & SF_MultiValue ){ -+ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); -+ sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", -+ pItem->u1.nRow); -+ }else{ -+ sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); -+ } - } -- sqlite3_str_appendall(pAccum, pItem->zName); - length = width = 0; - break; - } -@@ -29041,6 +32597,45 @@ SQLITE_API void sqlite3_str_vappendf( - }/* End for loop over the format string */ - } /* End of function */ - -+ -+/* -+** The z string points to the first character of a token that is -+** associated with an error. If db does not already have an error -+** byte offset recorded, try to compute the error byte offset for -+** z and set the error byte offset in db. -+*/ -+SQLITE_PRIVATE void sqlite3RecordErrorByteOffset(sqlite3 *db, const char *z){ -+ const Parse *pParse; -+ const char *zText; -+ const char *zEnd; -+ assert( z!=0 ); -+ if( NEVER(db==0) ) return; -+ if( db->errByteOffset!=(-2) ) return; -+ pParse = db->pParse; -+ if( NEVER(pParse==0) ) return; -+ zText =pParse->zTail; -+ if( NEVER(zText==0) ) return; -+ zEnd = &zText[strlen(zText)]; -+ if( SQLITE_WITHIN(z,zText,zEnd) ){ -+ db->errByteOffset = (int)(z-zText); -+ } -+} -+ -+/* -+** If pExpr has a byte offset for the start of a token, record that as -+** as the error offset. -+*/ -+SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){ -+ while( pExpr -+ && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) -+ ){ -+ pExpr = pExpr->pLeft; -+ } -+ if( pExpr==0 ) return; -+ if( ExprHasProperty(pExpr, EP_FromDDL) ) return; -+ db->errByteOffset = pExpr->w.iOfst; -+} -+ - /* - ** Enlarge the memory allocation on a StrAccum object so that it is - ** able to accept at least N more bytes of text. -@@ -29048,21 +32643,20 @@ SQLITE_API void sqlite3_str_vappendf( - ** Return the number of bytes of text that StrAccum is able to accept - ** after the attempted enlargement. The value returned might be zero. - */ --static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ -+SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, i64 N){ - char *zNew; -- assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ -+ assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */ - if( p->accError ){ - testcase(p->accError==SQLITE_TOOBIG); - testcase(p->accError==SQLITE_NOMEM); - return 0; - } - if( p->mxAlloc==0 ){ -- setStrAccumError(p, SQLITE_TOOBIG); -+ sqlite3StrAccumSetError(p, SQLITE_TOOBIG); - return p->nAlloc - p->nChar - 1; - }else{ - char *zOld = isMalloced(p) ? p->zText : 0; -- i64 szNew = p->nChar; -- szNew += N + 1; -+ i64 szNew = p->nChar + N + 1; - if( szNew+p->nChar<=p->mxAlloc ){ - /* Force exponential buffer size growth as long as it does not overflow, - ** to avoid having to call this routine too often */ -@@ -29070,7 +32664,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ - } - if( szNew > p->mxAlloc ){ - sqlite3_str_reset(p); -- setStrAccumError(p, SQLITE_TOOBIG); -+ sqlite3StrAccumSetError(p, SQLITE_TOOBIG); - return 0; - }else{ - p->nAlloc = (int)szNew; -@@ -29088,11 +32682,12 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ - p->printfFlags |= SQLITE_PRINTF_MALLOCED; - }else{ - sqlite3_str_reset(p); -- setStrAccumError(p, SQLITE_NOMEM); -+ sqlite3StrAccumSetError(p, SQLITE_NOMEM); - return 0; - } - } -- return N; -+ assert( N>=0 && N<=0x7fffffff ); -+ return (int)N; - } - - /* -@@ -29156,12 +32751,12 @@ SQLITE_API void sqlite3_str_appendall(sqlite3_str *p, const char *z){ - static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ - char *zText; - assert( p->mxAlloc>0 && !isMalloced(p) ); -- zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); -+ zText = sqlite3DbMallocRaw(p->db, 1+(u64)p->nChar ); - if( zText ){ - memcpy(zText, p->zText, p->nChar+1); - p->printfFlags |= SQLITE_PRINTF_MALLOCED; - }else{ -- setStrAccumError(p, SQLITE_NOMEM); -+ sqlite3StrAccumSetError(p, SQLITE_NOMEM); - } - p->zText = zText; - return zText; -@@ -29176,6 +32771,22 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ - return p->zText; - } - -+/* -+** Use the content of the StrAccum passed as the second argument -+** as the result of an SQL function. -+*/ -+SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context *pCtx, StrAccum *p){ -+ if( p->accError ){ -+ sqlite3_result_error_code(pCtx, p->accError); -+ sqlite3_str_reset(p); -+ }else if( isMalloced(p) ){ -+ sqlite3_result_text(pCtx, p->zText, p->nChar, SQLITE_DYNAMIC); -+ }else{ -+ sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC); -+ sqlite3_str_reset(p); -+ } -+} -+ - /* - ** This singleton is an sqlite3_str object that is returned if - ** sqlite3_malloc() fails to provide space for a real one. This -@@ -29367,14 +32978,33 @@ SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_li - return zBuf; - } - SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ -- char *z; -+ StrAccum acc; - va_list ap; -+ if( n<=0 ) return zBuf; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( zBuf==0 || zFormat==0 ) { -+ (void)SQLITE_MISUSE_BKPT; -+ if( zBuf ) zBuf[0] = 0; -+ return zBuf; -+ } -+#endif -+ sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); - va_start(ap,zFormat); -- z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); -+ sqlite3_str_vappendf(&acc, zFormat, ap); - va_end(ap); -- return z; -+ zBuf[acc.nChar] = 0; -+ return zBuf; - } - -+/* Maximum size of an sqlite3_log() message. */ -+#if defined(SQLITE_MAX_LOG_MESSAGE) -+ /* Leave the definition as supplied */ -+#elif SQLITE_PRINT_BUF_SIZE*10>10000 -+# define SQLITE_MAX_LOG_MESSAGE 10000 -+#else -+# define SQLITE_MAX_LOG_MESSAGE (SQLITE_PRINT_BUF_SIZE*10) -+#endif -+ - /* - ** This is the routine that actually formats the sqlite3_log() message. - ** We house it in a separate routine from sqlite3_log() to avoid using -@@ -29391,7 +33021,7 @@ SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - */ - static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ - StrAccum acc; /* String accumulator */ -- char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ -+ char zMsg[SQLITE_MAX_LOG_MESSAGE]; /* Complete log message */ - - sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); - sqlite3_str_vappendf(&acc, zFormat, ap); -@@ -29450,6 +33080,75 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ - va_end(ap); - } - -+ -+/***************************************************************************** -+** Reference counted string/blob storage -+*****************************************************************************/ -+ -+/* -+** Increase the reference count of the string by one. -+** -+** The input parameter is returned. -+*/ -+SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){ -+ RCStr *p = (RCStr*)z; -+ assert( p!=0 ); -+ p--; -+ p->nRCRef++; -+ return z; -+} -+ -+/* -+** Decrease the reference count by one. Free the string when the -+** reference count reaches zero. -+*/ -+SQLITE_PRIVATE void sqlite3RCStrUnref(void *z){ -+ RCStr *p = (RCStr*)z; -+ assert( p!=0 ); -+ p--; -+ assert( p->nRCRef>0 ); -+ if( p->nRCRef>=2 ){ -+ p->nRCRef--; -+ }else{ -+ sqlite3_free(p); -+ } -+} -+ -+/* -+** Create a new string that is capable of holding N bytes of text, not counting -+** the zero byte at the end. The string is uninitialized. -+** -+** The reference count is initially 1. Call sqlite3RCStrUnref() to free the -+** newly allocated string. -+** -+** This routine returns 0 on an OOM. -+*/ -+SQLITE_PRIVATE char *sqlite3RCStrNew(u64 N){ -+ RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 ); -+ if( p==0 ) return 0; -+ p->nRCRef = 1; -+ return (char*)&p[1]; -+} -+ -+/* -+** Change the size of the string so that it is able to hold N bytes. -+** The string might be reallocated, so return the new allocation. -+*/ -+SQLITE_PRIVATE char *sqlite3RCStrResize(char *z, u64 N){ -+ RCStr *p = (RCStr*)z; -+ RCStr *pNew; -+ assert( p!=0 ); -+ p--; -+ assert( p->nRCRef==1 ); -+ pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1); -+ if( pNew==0 ){ -+ sqlite3_free(p); -+ return 0; -+ }else{ -+ return (char*)&pNew[1]; -+ } -+} -+ - /************** End of printf.c **********************************************/ - /************** Begin file treeview.c ****************************************/ - /* -@@ -29478,40 +33177,44 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ - ** Add a new subitem to the tree. The moreToFollow flag indicates that this - ** is not the last item in the tree. - */ --static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){ -+static void sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){ -+ TreeView *p = *pp; - if( p==0 ){ -- p = sqlite3_malloc64( sizeof(*p) ); -- if( p==0 ) return 0; -+ *pp = p = sqlite3_malloc64( sizeof(*p) ); -+ if( p==0 ) return; - memset(p, 0, sizeof(*p)); - }else{ - p->iLevel++; - } - assert( moreToFollow==0 || moreToFollow==1 ); -- if( p->iLevelbLine) ) p->bLine[p->iLevel] = moreToFollow; -- return p; -+ if( p->iLevel<(int)sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow; - } - - /* - ** Finished with one layer of the tree - */ --static void sqlite3TreeViewPop(TreeView *p){ -+static void sqlite3TreeViewPop(TreeView **pp){ -+ TreeView *p = *pp; - if( p==0 ) return; - p->iLevel--; -- if( p->iLevel<0 ) sqlite3_free(p); -+ if( p->iLevel<0 ){ -+ sqlite3_free(p); -+ *pp = 0; -+ } - } - - /* - ** Generate a single line of output for the tree, with a prefix that contains - ** all the appropriate tree lines - */ --static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ -+SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ - va_list ap; - int i; - StrAccum acc; -- char zBuf[500]; -+ char zBuf[1000]; - sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); - if( p ){ -- for(i=0; iiLevel && ibLine)-1; i++){ -+ for(i=0; iiLevel && i<(int)sizeof(p->bLine)-1; i++){ - sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4); - } - sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); -@@ -29532,10 +33235,57 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ - ** Shorthand for starting a new tree item that consists of a single label - */ - static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){ -- p = sqlite3TreeViewPush(p, moreFollows); -+ sqlite3TreeViewPush(&p, moreFollows); - sqlite3TreeViewLine(p, "%s", zLabel); - } - -+/* -+** Show a list of Column objects in tree format. -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewColumnList( -+ TreeView *pView, -+ const Column *aCol, -+ int nCol, -+ u8 moreToFollow -+){ -+ int i; -+ sqlite3TreeViewPush(&pView, moreToFollow); -+ sqlite3TreeViewLine(pView, "COLUMNS"); -+ for(i=0; inCte>0 ){ -- pView = sqlite3TreeViewPush(pView, 1); -+ sqlite3TreeViewPush(&pView, moreToFollow); - for(i=0; inCte; i++){ - StrAccum x; - char zLine[1000]; -@@ -29565,13 +33315,20 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m - } - sqlite3_str_appendf(&x, ")"); - } -- sqlite3_str_appendf(&x, " AS"); -+ if( pCte->eM10d!=M10d_Any ){ -+ sqlite3_str_appendf(&x, " %sMATERIALIZED", -+ pCte->eM10d==M10d_No ? "NOT " : ""); -+ } -+ if( pCte->pUse ){ -+ sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse, -+ pCte->pUse->nUse); -+ } - sqlite3StrAccumFinish(&x); - sqlite3TreeViewItem(pView, zLine, inCte-1); - sqlite3TreeViewSelect(pView, pCte->pSelect, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - } - -@@ -29580,39 +33337,77 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m - */ - SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ - int i; -+ if( pSrc==0 ) return; - for(i=0; inSrc; i++){ -- const struct SrcList_item *pItem = &pSrc->a[i]; -+ const SrcItem *pItem = &pSrc->a[i]; - StrAccum x; -- char zLine[100]; -+ int n = 0; -+ char zLine[1000]; - sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); -- sqlite3_str_appendf(&x, "{%d:*}", pItem->iCursor); -- if( pItem->zDatabase ){ -- sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName); -- }else if( pItem->zName ){ -- sqlite3_str_appendf(&x, " %s", pItem->zName); -- } -- if( pItem->pTab ){ -- sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", -- pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); -- } -- if( pItem->zAlias ){ -- sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); -- } -- if( pItem->fg.jointype & JT_LEFT ){ -+ x.printfFlags |= SQLITE_PRINTF_INTERNAL; -+ sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); -+ if( pItem->pSTab ){ -+ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", -+ pItem->pSTab->zName, pItem->pSTab->nCol, pItem->pSTab, -+ pItem->colUsed, -+ pItem->fg.rowidUsed ? "+rowid" : ""); -+ } -+ if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ -+ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); -+ }else if( pItem->fg.jointype & JT_LEFT ){ - sqlite3_str_appendf(&x, " LEFT-JOIN"); -+ }else if( pItem->fg.jointype & JT_RIGHT ){ -+ sqlite3_str_appendf(&x, " RIGHT-JOIN"); -+ }else if( pItem->fg.jointype & JT_CROSS ){ -+ sqlite3_str_appendf(&x, " CROSS-JOIN"); -+ } -+ if( pItem->fg.jointype & JT_LTORJ ){ -+ sqlite3_str_appendf(&x, " LTORJ"); - } - if( pItem->fg.fromDDL ){ - sqlite3_str_appendf(&x, " DDL"); - } -+ if( pItem->fg.isCte ){ -+ static const char *aMat[] = {",MAT", "", ",NO-MAT"}; -+ sqlite3_str_appendf(&x, " CteUse=%d%s", -+ pItem->u2.pCteUse->nUse, -+ aMat[pItem->u2.pCteUse->eM10d]); -+ } -+ if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ -+ sqlite3_str_appendf(&x, " isOn"); -+ } -+ if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc"); -+ if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated"); -+ if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized"); -+ if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); -+ if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte"); -+ if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom"); -+ if( pItem->fg.fixedSchema ) sqlite3_str_appendf(&x, " fixedSchema"); -+ if( pItem->fg.hadSchema ) sqlite3_str_appendf(&x, " hadSchema"); -+ if( pItem->fg.isSubquery ) sqlite3_str_appendf(&x, " isSubquery"); -+ - sqlite3StrAccumFinish(&x); - sqlite3TreeViewItem(pView, zLine, inSrc-1); -- if( pItem->pSelect ){ -- sqlite3TreeViewSelect(pView, pItem->pSelect, 0); -+ n = 0; -+ if( pItem->fg.isSubquery ) n++; -+ if( pItem->fg.isTabFunc ) n++; -+ if( pItem->fg.isUsing ) n++; -+ if( pItem->fg.isUsing ){ -+ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); -+ } -+ if( pItem->fg.isSubquery ){ -+ assert( n==1 ); -+ if( pItem->pSTab ){ -+ Table *pTab = pItem->pSTab; -+ sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); -+ } -+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); -+ sqlite3TreeViewSelect(pView, pItem->u4.pSubq->pSelect, 0); - } - if( pItem->fg.isTabFunc ){ - sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); - } -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - } - -@@ -29626,11 +33421,11 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m - sqlite3TreeViewLine(pView, "nil-SELECT"); - return; - } -- pView = sqlite3TreeViewPush(pView, moreToFollow); -+ sqlite3TreeViewPush(&pView, moreToFollow); - if( p->pWith ){ - sqlite3TreeViewWith(pView, p->pWith, 1); - cnt = 1; -- sqlite3TreeViewPush(pView, 1); -+ sqlite3TreeViewPush(&pView, 1); - } - do{ - if( p->selFlags & SF_WhereBegin ){ -@@ -29644,12 +33439,12 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m - (int)p->nSelectRow - ); - } -- if( cnt++ ) sqlite3TreeViewPop(pView); -+ if( cnt++ ) sqlite3TreeViewPop(&pView); - if( p->pPrior ){ - n = 1000; - }else{ - n = 0; -- if( p->pSrc && p->pSrc->nSrc ) n++; -+ if( p->pSrc && p->pSrc->nSrc && p->pSrc->nAlloc ) n++; - if( p->pWhere ) n++; - if( p->pGroupBy ) n++; - if( p->pHaving ) n++; -@@ -29667,24 +33462,24 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m - #ifndef SQLITE_OMIT_WINDOWFUNC - if( p->pWin ){ - Window *pX; -- pView = sqlite3TreeViewPush(pView, (n--)>0); -+ sqlite3TreeViewPush(&pView, (n--)>0); - sqlite3TreeViewLine(pView, "window-functions"); - for(pX=p->pWin; pX; pX=pX->pNextWin){ - sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0); - } -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - #endif -- if( p->pSrc && p->pSrc->nSrc ){ -- pView = sqlite3TreeViewPush(pView, (n--)>0); -+ if( p->pSrc && p->pSrc->nSrc && p->pSrc->nAlloc ){ -+ sqlite3TreeViewPush(&pView, (n--)>0); - sqlite3TreeViewLine(pView, "FROM"); - sqlite3TreeViewSrcList(pView, p->pSrc); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - if( p->pWhere ){ - sqlite3TreeViewItem(pView, "WHERE", (n--)>0); - sqlite3TreeViewExpr(pView, p->pWhere, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - if( p->pGroupBy ){ - sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY"); -@@ -29692,7 +33487,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m - if( p->pHaving ){ - sqlite3TreeViewItem(pView, "HAVING", (n--)>0); - sqlite3TreeViewExpr(pView, p->pHaving, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - #ifndef SQLITE_OMIT_WINDOWFUNC - if( p->pWinDefn ){ -@@ -29701,7 +33496,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m - for(pX=p->pWinDefn; pX; pX=pX->pNextWin){ - sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0); - } -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - #endif - if( p->pOrderBy ){ -@@ -29711,11 +33506,11 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m - sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); - sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); - if( p->pLimit->pRight ){ -- sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); -+ sqlite3TreeViewItem(pView, "OFFSET", 0); - sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - if( p->pPrior ){ - const char *zOp = "UNION"; -@@ -29728,7 +33523,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m - } - p = p->pPrior; - }while( p!=0 ); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - - #ifndef SQLITE_OMIT_WINDOWFUNC -@@ -29744,24 +33539,24 @@ SQLITE_PRIVATE void sqlite3TreeViewBound( - switch( eBound ){ - case TK_UNBOUNDED: { - sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - break; - } - case TK_CURRENT: { - sqlite3TreeViewItem(pView, "CURRENT", moreToFollow); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - break; - } - case TK_PRECEDING: { - sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow); - sqlite3TreeViewExpr(pView, pExpr, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - break; - } - case TK_FOLLOWING: { - sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow); - sqlite3TreeViewExpr(pView, pExpr, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - break; - } - } -@@ -29774,12 +33569,14 @@ SQLITE_PRIVATE void sqlite3TreeViewBound( - */ - SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ - int nElement = 0; -+ if( pWin==0 ) return; - if( pWin->pFilter ){ - sqlite3TreeViewItem(pView, "FILTER", 1); - sqlite3TreeViewExpr(pView, pWin->pFilter, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); -+ if( pWin->eFrmType==TK_FILTER ) return; - } -- pView = sqlite3TreeViewPush(pView, more); -+ sqlite3TreeViewPush(&pView, more); - if( pWin->zName ){ - sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); - }else{ -@@ -29787,12 +33584,12 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u - } - if( pWin->zBase ) nElement++; - if( pWin->pOrderBy ) nElement++; -- if( pWin->eFrmType ) nElement++; -+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++; - if( pWin->eExclude ) nElement++; - if( pWin->zBase ){ -- sqlite3TreeViewPush(pView, (--nElement)>0); -+ sqlite3TreeViewPush(&pView, (--nElement)>0); - sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - if( pWin->pPartition ){ - sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); -@@ -29800,7 +33597,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u - if( pWin->pOrderBy ){ - sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); - } -- if( pWin->eFrmType ){ -+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){ - char zBuf[30]; - const char *zFrmType = "ROWS"; - if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; -@@ -29810,7 +33607,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u - sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); - sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); - sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - if( pWin->eExclude ){ - char zBuf[30]; -@@ -29825,11 +33622,11 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u - zExclude = zBuf; - break; - } -- sqlite3TreeViewPush(pView, 0); -+ sqlite3TreeViewPush(&pView, 0); - sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - #endif /* SQLITE_OMIT_WINDOWFUNC */ - -@@ -29838,11 +33635,12 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u - ** Generate a human-readable explanation for a Window Function object - */ - SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){ -- pView = sqlite3TreeViewPush(pView, more); -+ if( pWin==0 ) return; -+ sqlite3TreeViewPush(&pView, more); - sqlite3TreeViewLine(pView, "WINFUNC %s(%d)", -- pWin->pFunc->zName, pWin->pFunc->nArg); -+ pWin->pWFunc->zName, pWin->pWFunc->nArg); - sqlite3TreeViewWindow(pView, pWin, 0); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - #endif /* SQLITE_OMIT_WINDOWFUNC */ - -@@ -29853,19 +33651,22 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - const char *zBinOp = 0; /* Binary operator */ - const char *zUniOp = 0; /* Unary operator */ - char zFlgs[200]; -- pView = sqlite3TreeViewPush(pView, moreToFollow); -+ sqlite3TreeViewPush(&pView, moreToFollow); - if( pExpr==0 ){ - sqlite3TreeViewLine(pView, "nil"); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - return; - } -- if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ -+ if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){ - StrAccum x; - sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); - sqlite3_str_appendf(&x, " fg.af=%x.%c", - pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n'); -- if( ExprHasProperty(pExpr, EP_FromJoin) ){ -- sqlite3_str_appendf(&x, " iRJT=%d", pExpr->iRightJoinTable); -+ if( ExprHasProperty(pExpr, EP_OuterON) ){ -+ sqlite3_str_appendf(&x, " outer.iJoin=%d", pExpr->w.iJoin); -+ } -+ if( ExprHasProperty(pExpr, EP_InnerON) ){ -+ sqlite3_str_appendf(&x, " inner.iJoin=%d", pExpr->w.iJoin); - } - if( ExprHasProperty(pExpr, EP_FromDDL) ){ - sqlite3_str_appendf(&x, " DDL"); -@@ -29873,6 +33674,9 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ - sqlite3_str_appendf(&x, " IMMUTABLE"); - } -+ if( pExpr->pAggInfo!=0 ){ -+ sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg); -+ } - sqlite3StrAccumFinish(&x); - }else{ - zFlgs[0] = 0; -@@ -29895,6 +33699,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - sqlite3TreeViewLine(pView, "COLUMN(%d)%s%s", - pExpr->iColumn, zFlgs, zOp2); - }else{ -+ assert( ExprUseYTab(pExpr) ); - sqlite3TreeViewLine(pView, "{%d:%d} pTab=%p%s", - pExpr->iTable, pExpr->iColumn, - pExpr->y.pTab, zFlgs); -@@ -29914,11 +33719,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - } - #ifndef SQLITE_OMIT_FLOATING_POINT - case TK_FLOAT: { -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); - break; - } - #endif - case TK_STRING: { -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken); - break; - } -@@ -29927,17 +33734,19 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - break; - } - case TK_TRUEFALSE: { -- sqlite3TreeViewLine(pView, -- sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE"); -+ sqlite3TreeViewLine(pView,"%s%s", -+ sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE", zFlgs); - break; - } - #ifndef SQLITE_OMIT_BLOB_LITERAL - case TK_BLOB: { -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); - break; - } - #endif - case TK_VARIABLE: { -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)", - pExpr->u.zToken, pExpr->iColumn); - break; -@@ -29947,12 +33756,14 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - break; - } - case TK_ID: { -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); - break; - } - #ifndef SQLITE_OMIT_CAST - case TK_CAST: { - /* Expressions of the form: CAST(pLeft AS token) */ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken); - sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; -@@ -29995,13 +33806,15 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - }; - assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); - assert( pExpr->pRight ); -- assert( sqlite3ExprSkipCollate(pExpr->pRight)->op==TK_TRUEFALSE ); -+ assert( sqlite3ExprSkipCollateAndLikely(pExpr->pRight)->op -+ == TK_TRUEFALSE ); - x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight); - zUniOp = azOp[x]; - break; - } - - case TK_SPAN: { -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); - sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; -@@ -30013,6 +33826,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE - ** operators that appear in the original SQL always have the - ** EP_Collate bit set and appear in treeview output as just "COLLATE" */ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s", - !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "", - pExpr->u.zToken, zFlgs); -@@ -30028,13 +33842,15 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - pFarg = 0; - pWin = 0; - }else{ -+ assert( ExprUseXList(pExpr) ); - pFarg = pExpr->x.pList; - #ifndef SQLITE_OMIT_WINDOWFUNC -- pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0; -+ pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0; - #else - pWin = 0; - #endif - } -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - if( pExpr->op==TK_AGG_FUNCTION ){ - sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s agg=%d[%d]/%p", - pExpr->op2, pExpr->u.zToken, zFlgs, -@@ -30055,7 +33871,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs); - } - if( pFarg ){ -- sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0); -+ sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0); -+ if( pExpr->pLeft ){ -+ Expr *pOB = pExpr->pLeft; -+ assert( pOB->op==TK_ORDER ); -+ assert( ExprUseXList(pOB) ); -+ sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY"); -+ } - } - #ifndef SQLITE_OMIT_WINDOWFUNC - if( pWin ){ -@@ -30064,21 +33886,37 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - #endif - break; - } -+ case TK_ORDER: { -+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY"); -+ break; -+ } - #ifndef SQLITE_OMIT_SUBQUERY - case TK_EXISTS: { -+ assert( ExprUseXSelect(pExpr) ); - sqlite3TreeViewLine(pView, "EXISTS-expr flags=0x%x", pExpr->flags); - sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); - break; - } - case TK_SELECT: { -+ assert( ExprUseXSelect(pExpr) ); - sqlite3TreeViewLine(pView, "subquery-expr flags=0x%x", pExpr->flags); - sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); - break; - } - case TK_IN: { -- sqlite3TreeViewLine(pView, "IN flags=0x%x", pExpr->flags); -+ sqlite3_str *pStr = sqlite3_str_new(0); -+ char *z; -+ sqlite3_str_appendf(pStr, "IN flags=0x%x", pExpr->flags); -+ if( pExpr->iTable ) sqlite3_str_appendf(pStr, " iTable=%d",pExpr->iTable); -+ if( ExprHasProperty(pExpr, EP_Subrtn) ){ -+ sqlite3_str_appendf(pStr, " subrtn(%d,%d)", -+ pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); -+ } -+ z = sqlite3_str_finish(pStr); -+ sqlite3TreeViewLine(pView, z); -+ sqlite3_free(z); - sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); - }else{ - sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); -@@ -30099,10 +33937,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - ** Z is stored in pExpr->pList->a[1].pExpr. - */ - case TK_BETWEEN: { -- Expr *pX = pExpr->pLeft; -- Expr *pY = pExpr->x.pList->a[0].pExpr; -- Expr *pZ = pExpr->x.pList->a[1].pExpr; -- sqlite3TreeViewLine(pView, "BETWEEN"); -+ const Expr *pX, *pY, *pZ; -+ pX = pExpr->pLeft; -+ assert( ExprUseXList(pExpr) ); -+ assert( pExpr->x.pList->nExpr==2 ); -+ pY = pExpr->x.pList->a[0].pExpr; -+ pZ = pExpr->x.pList->a[1].pExpr; -+ sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs); - sqlite3TreeViewExpr(pView, pX, 1); - sqlite3TreeViewExpr(pView, pY, 1); - sqlite3TreeViewExpr(pView, pZ, 0); -@@ -30123,6 +33964,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - case TK_CASE: { - sqlite3TreeViewLine(pView, "CASE"); - sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); -+ assert( ExprUseXList(pExpr) ); - sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); - break; - } -@@ -30135,7 +33977,9 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - case OE_Fail: zType = "fail"; break; - case OE_Ignore: zType = "ignore"; break; - } -- sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ sqlite3TreeViewLine(pView, "RAISE %s", zType); -+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; - } - #endif -@@ -30147,12 +33991,16 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - } - case TK_VECTOR: { - char *z = sqlite3_mprintf("VECTOR%s",zFlgs); -+ assert( ExprUseXList(pExpr) ); - sqlite3TreeViewBareExprList(pView, pExpr->x.pList, z); - sqlite3_free(z); - break; - } - case TK_SELECT_COLUMN: { -- sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); -+ sqlite3TreeViewLine(pView, "SELECT-COLUMN %d of [0..%d]%s", -+ pExpr->iColumn, pExpr->iTable-1, -+ pExpr->pRight==pExpr->pLeft ? " (SELECT-owner)" : ""); -+ assert( ExprUseXSelect(pExpr->pLeft) ); - sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); - break; - } -@@ -30161,6 +34009,23 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - break; - } -+ case TK_ERROR: { -+ Expr tmp; -+ sqlite3TreeViewLine(pView, "ERROR"); -+ tmp = *pExpr; -+ tmp.op = pExpr->op2; -+ sqlite3TreeViewExpr(pView, &tmp, 0); -+ break; -+ } -+ case TK_ROW: { -+ if( pExpr->iColumn<=0 ){ -+ sqlite3TreeViewLine(pView, "First FROM table rowid"); -+ }else{ -+ sqlite3TreeViewLine(pView, "First FROM table column %d", -+ pExpr->iColumn-1); -+ } -+ break; -+ } - default: { - sqlite3TreeViewLine(pView, "op=%d", pExpr->op); - break; -@@ -30174,7 +34039,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m - sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs); - sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); - } -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - - -@@ -30194,25 +34059,43 @@ SQLITE_PRIVATE void sqlite3TreeViewBareExprList( - sqlite3TreeViewLine(pView, "%s", zLabel); - for(i=0; inExpr; i++){ - int j = pList->a[i].u.x.iOrderByCol; -+ u8 sortFlags = pList->a[i].fg.sortFlags; - char *zName = pList->a[i].zEName; - int moreToFollow = inExpr - 1; -- if( pList->a[i].eEName!=ENAME_NAME ) zName = 0; -- if( j || zName ){ -- sqlite3TreeViewPush(pView, moreToFollow); -+ if( j || zName || sortFlags ){ -+ sqlite3TreeViewPush(&pView, moreToFollow); - moreToFollow = 0; - sqlite3TreeViewLine(pView, 0); - if( zName ){ -- fprintf(stdout, "AS %s ", zName); -+ switch( pList->a[i].fg.eEName ){ -+ default: -+ fprintf(stdout, "AS %s ", zName); -+ break; -+ case ENAME_TAB: -+ fprintf(stdout, "TABLE-ALIAS-NAME(\"%s\") ", zName); -+ if( pList->a[i].fg.bUsed ) fprintf(stdout, "(used) "); -+ if( pList->a[i].fg.bUsingTerm ) fprintf(stdout, "(USING-term) "); -+ if( pList->a[i].fg.bNoExpand ) fprintf(stdout, "(NoExpand) "); -+ break; -+ case ENAME_SPAN: -+ fprintf(stdout, "SPAN(\"%s\") ", zName); -+ break; -+ } - } - if( j ){ -- fprintf(stdout, "iOrderByCol=%d", j); -+ fprintf(stdout, "iOrderByCol=%d ", j); -+ } -+ if( sortFlags & KEYINFO_ORDER_DESC ){ -+ fprintf(stdout, "DESC "); -+ }else if( sortFlags & KEYINFO_ORDER_BIGNULL ){ -+ fprintf(stdout, "NULLS-LAST"); - } - fprintf(stdout, "\n"); - fflush(stdout); - } - sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow); -- if( j || zName ){ -- sqlite3TreeViewPop(pView); -+ if( j || zName || sortFlags ){ -+ sqlite3TreeViewPop(&pView); - } - } - } -@@ -30223,11 +34106,368 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList( - u8 moreToFollow, - const char *zLabel - ){ -- pView = sqlite3TreeViewPush(pView, moreToFollow); -+ sqlite3TreeViewPush(&pView, moreToFollow); - sqlite3TreeViewBareExprList(pView, pList, zLabel); -- sqlite3TreeViewPop(pView); -+ sqlite3TreeViewPop(&pView); - } - -+/* -+** Generate a human-readable explanation of an id-list. -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewBareIdList( -+ TreeView *pView, -+ const IdList *pList, -+ const char *zLabel -+){ -+ if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; -+ if( pList==0 ){ -+ sqlite3TreeViewLine(pView, "%s (empty)", zLabel); -+ }else{ -+ int i; -+ sqlite3TreeViewLine(pView, "%s", zLabel); -+ for(i=0; inId; i++){ -+ char *zName = pList->a[i].zName; -+ int moreToFollow = inId - 1; -+ if( zName==0 ) zName = "(null)"; -+ sqlite3TreeViewPush(&pView, moreToFollow); -+ sqlite3TreeViewLine(pView, 0); -+ fprintf(stdout, "%s\n", zName); -+ sqlite3TreeViewPop(&pView); -+ } -+ } -+} -+SQLITE_PRIVATE void sqlite3TreeViewIdList( -+ TreeView *pView, -+ const IdList *pList, -+ u8 moreToFollow, -+ const char *zLabel -+){ -+ sqlite3TreeViewPush(&pView, moreToFollow); -+ sqlite3TreeViewBareIdList(pView, pList, zLabel); -+ sqlite3TreeViewPop(&pView); -+} -+ -+/* -+** Generate a human-readable explanation of a list of Upsert objects -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewUpsert( -+ TreeView *pView, -+ const Upsert *pUpsert, -+ u8 moreToFollow -+){ -+ if( pUpsert==0 ) return; -+ sqlite3TreeViewPush(&pView, moreToFollow); -+ while( pUpsert ){ -+ int n; -+ sqlite3TreeViewPush(&pView, pUpsert->pNextUpsert!=0 || moreToFollow); -+ sqlite3TreeViewLine(pView, "ON CONFLICT DO %s", -+ pUpsert->isDoUpdate ? "UPDATE" : "NOTHING"); -+ n = (pUpsert->pUpsertSet!=0) + (pUpsert->pUpsertWhere!=0); -+ sqlite3TreeViewExprList(pView, pUpsert->pUpsertTarget, (n--)>0, "TARGET"); -+ sqlite3TreeViewExprList(pView, pUpsert->pUpsertSet, (n--)>0, "SET"); -+ if( pUpsert->pUpsertWhere ){ -+ sqlite3TreeViewItem(pView, "WHERE", (n--)>0); -+ sqlite3TreeViewExpr(pView, pUpsert->pUpsertWhere, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ sqlite3TreeViewPop(&pView); -+ pUpsert = pUpsert->pNextUpsert; -+ } -+ sqlite3TreeViewPop(&pView); -+} -+ -+#if TREETRACE_ENABLED -+/* -+** Generate a human-readable diagram of the data structure that go -+** into generating an DELETE statement. -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewDelete( -+ const With *pWith, -+ const SrcList *pTabList, -+ const Expr *pWhere, -+ const ExprList *pOrderBy, -+ const Expr *pLimit, -+ const Trigger *pTrigger -+){ -+ int n = 0; -+ TreeView *pView = 0; -+ sqlite3TreeViewPush(&pView, 0); -+ sqlite3TreeViewLine(pView, "DELETE"); -+ if( pWith ) n++; -+ if( pTabList ) n++; -+ if( pWhere ) n++; -+ if( pOrderBy ) n++; -+ if( pLimit ) n++; -+ if( pTrigger ) n++; -+ if( pWith ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewWith(pView, pWith, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pTabList ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "FROM"); -+ sqlite3TreeViewSrcList(pView, pTabList); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pWhere ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "WHERE"); -+ sqlite3TreeViewExpr(pView, pWhere, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pOrderBy ){ -+ sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); -+ } -+ if( pLimit ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "LIMIT"); -+ sqlite3TreeViewExpr(pView, pLimit, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pTrigger ){ -+ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); -+ } -+ sqlite3TreeViewPop(&pView); -+} -+#endif /* TREETRACE_ENABLED */ -+ -+#if TREETRACE_ENABLED -+/* -+** Generate a human-readable diagram of the data structure that go -+** into generating an INSERT statement. -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewInsert( -+ const With *pWith, -+ const SrcList *pTabList, -+ const IdList *pColumnList, -+ const Select *pSelect, -+ const ExprList *pExprList, -+ int onError, -+ const Upsert *pUpsert, -+ const Trigger *pTrigger -+){ -+ TreeView *pView = 0; -+ int n = 0; -+ const char *zLabel = "INSERT"; -+ switch( onError ){ -+ case OE_Replace: zLabel = "REPLACE"; break; -+ case OE_Ignore: zLabel = "INSERT OR IGNORE"; break; -+ case OE_Rollback: zLabel = "INSERT OR ROLLBACK"; break; -+ case OE_Abort: zLabel = "INSERT OR ABORT"; break; -+ case OE_Fail: zLabel = "INSERT OR FAIL"; break; -+ } -+ sqlite3TreeViewPush(&pView, 0); -+ sqlite3TreeViewLine(pView, zLabel); -+ if( pWith ) n++; -+ if( pTabList ) n++; -+ if( pColumnList ) n++; -+ if( pSelect ) n++; -+ if( pExprList ) n++; -+ if( pUpsert ) n++; -+ if( pTrigger ) n++; -+ if( pWith ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewWith(pView, pWith, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pTabList ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "INTO"); -+ sqlite3TreeViewSrcList(pView, pTabList); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pColumnList ){ -+ sqlite3TreeViewIdList(pView, pColumnList, (--n)>0, "COLUMNS"); -+ } -+ if( pSelect ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "DATA-SOURCE"); -+ sqlite3TreeViewSelect(pView, pSelect, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pExprList ){ -+ sqlite3TreeViewExprList(pView, pExprList, (--n)>0, "VALUES"); -+ } -+ if( pUpsert ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "UPSERT"); -+ sqlite3TreeViewUpsert(pView, pUpsert, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pTrigger ){ -+ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); -+ } -+ sqlite3TreeViewPop(&pView); -+} -+#endif /* TREETRACE_ENABLED */ -+ -+#if TREETRACE_ENABLED -+/* -+** Generate a human-readable diagram of the data structure that go -+** into generating an UPDATE statement. -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewUpdate( -+ const With *pWith, -+ const SrcList *pTabList, -+ const ExprList *pChanges, -+ const Expr *pWhere, -+ int onError, -+ const ExprList *pOrderBy, -+ const Expr *pLimit, -+ const Upsert *pUpsert, -+ const Trigger *pTrigger -+){ -+ int n = 0; -+ TreeView *pView = 0; -+ const char *zLabel = "UPDATE"; -+ switch( onError ){ -+ case OE_Replace: zLabel = "UPDATE OR REPLACE"; break; -+ case OE_Ignore: zLabel = "UPDATE OR IGNORE"; break; -+ case OE_Rollback: zLabel = "UPDATE OR ROLLBACK"; break; -+ case OE_Abort: zLabel = "UPDATE OR ABORT"; break; -+ case OE_Fail: zLabel = "UPDATE OR FAIL"; break; -+ } -+ sqlite3TreeViewPush(&pView, 0); -+ sqlite3TreeViewLine(pView, zLabel); -+ if( pWith ) n++; -+ if( pTabList ) n++; -+ if( pChanges ) n++; -+ if( pWhere ) n++; -+ if( pOrderBy ) n++; -+ if( pLimit ) n++; -+ if( pUpsert ) n++; -+ if( pTrigger ) n++; -+ if( pWith ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewWith(pView, pWith, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pTabList ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "FROM"); -+ sqlite3TreeViewSrcList(pView, pTabList); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pChanges ){ -+ sqlite3TreeViewExprList(pView, pChanges, (--n)>0, "SET"); -+ } -+ if( pWhere ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "WHERE"); -+ sqlite3TreeViewExpr(pView, pWhere, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pOrderBy ){ -+ sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY"); -+ } -+ if( pLimit ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "LIMIT"); -+ sqlite3TreeViewExpr(pView, pLimit, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pUpsert ){ -+ sqlite3TreeViewPush(&pView, (--n)>0); -+ sqlite3TreeViewLine(pView, "UPSERT"); -+ sqlite3TreeViewUpsert(pView, pUpsert, 0); -+ sqlite3TreeViewPop(&pView); -+ } -+ if( pTrigger ){ -+ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1); -+ } -+ sqlite3TreeViewPop(&pView); -+} -+#endif /* TREETRACE_ENABLED */ -+ -+#ifndef SQLITE_OMIT_TRIGGER -+/* -+** Show a human-readable graph of a TriggerStep -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewTriggerStep( -+ TreeView *pView, -+ const TriggerStep *pStep, -+ u8 moreToFollow, -+ u8 showFullList -+){ -+ int cnt = 0; -+ if( pStep==0 ) return; -+ sqlite3TreeViewPush(&pView, -+ moreToFollow || (showFullList && pStep->pNext!=0)); -+ do{ -+ if( cnt++ && pStep->pNext==0 ){ -+ sqlite3TreeViewPop(&pView); -+ sqlite3TreeViewPush(&pView, 0); -+ } -+ sqlite3TreeViewLine(pView, "%s", pStep->zSpan ? pStep->zSpan : "RETURNING"); -+ }while( showFullList && (pStep = pStep->pNext)!=0 ); -+ sqlite3TreeViewPop(&pView); -+} -+ -+/* -+** Show a human-readable graph of a Trigger -+*/ -+SQLITE_PRIVATE void sqlite3TreeViewTrigger( -+ TreeView *pView, -+ const Trigger *pTrigger, -+ u8 moreToFollow, -+ u8 showFullList -+){ -+ int cnt = 0; -+ if( pTrigger==0 ) return; -+ sqlite3TreeViewPush(&pView, -+ moreToFollow || (showFullList && pTrigger->pNext!=0)); -+ do{ -+ if( cnt++ && pTrigger->pNext==0 ){ -+ sqlite3TreeViewPop(&pView); -+ sqlite3TreeViewPush(&pView, 0); -+ } -+ sqlite3TreeViewLine(pView, "TRIGGER %s", pTrigger->zName); -+ sqlite3TreeViewPush(&pView, 0); -+ sqlite3TreeViewTriggerStep(pView, pTrigger->step_list, 0, 1); -+ sqlite3TreeViewPop(&pView); -+ }while( showFullList && (pTrigger = pTrigger->pNext)!=0 ); -+ sqlite3TreeViewPop(&pView); -+} -+#endif /* SQLITE_OMIT_TRIGGER */ -+ -+ -+/* -+** These simplified versions of the tree-view routines omit unnecessary -+** parameters. These variants are intended to be used from a symbolic -+** debugger, such as "gdb", during interactive debugging sessions. -+** -+** This routines are given external linkage so that they will always be -+** accessible to the debugging, and to avoid warnings about unused -+** functions. But these routines only exist in debugging builds, so they -+** do not contaminate the interface. -+** -+** See Also: -+** -+** sqlite3ShowWhereTerm() in where.c -+*/ -+SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); } -+SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);} -+SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); } -+SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); } -+SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); } -+SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); } -+SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert *p){ sqlite3TreeViewUpsert(0,p,0); } -+#ifndef SQLITE_OMIT_TRIGGER -+SQLITE_PRIVATE void sqlite3ShowTriggerStep(const TriggerStep *p){ -+ sqlite3TreeViewTriggerStep(0,p,0,0); -+} -+SQLITE_PRIVATE void sqlite3ShowTriggerStepList(const TriggerStep *p){ -+ sqlite3TreeViewTriggerStep(0,p,0,1); -+} -+SQLITE_PRIVATE void sqlite3ShowTrigger(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,0); } -+SQLITE_PRIVATE void sqlite3ShowTriggerList(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,1);} -+#endif -+#ifndef SQLITE_OMIT_WINDOWFUNC -+SQLITE_PRIVATE void sqlite3ShowWindow(const Window *p){ sqlite3TreeViewWindow(0,p,0); } -+SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window *p){ sqlite3TreeViewWinFunc(0,p,0); } -+#endif -+ - #endif /* SQLITE_DEBUG */ - - /************** End of treeview.c ********************************************/ -@@ -30256,16 +34496,41 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList( - ** This structure is the current state of the generator. - */ - static SQLITE_WSD struct sqlite3PrngType { -- unsigned char isInit; /* True if initialized */ -- unsigned char i, j; /* State variables */ -- unsigned char s[256]; /* State variables */ -+ u32 s[16]; /* 64 bytes of chacha20 state */ -+ u8 out[64]; /* Output bytes */ -+ u8 n; /* Output bytes remaining */ - } sqlite3Prng; - -+ -+/* The RFC-7539 ChaCha20 block function -+*/ -+#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) -+#define QR(a, b, c, d) ( \ -+ a += b, d ^= a, d = ROTL(d,16), \ -+ c += d, b ^= c, b = ROTL(b,12), \ -+ a += b, d ^= a, d = ROTL(d, 8), \ -+ c += d, b ^= c, b = ROTL(b, 7)) -+static void chacha_block(u32 *out, const u32 *in){ -+ int i; -+ u32 x[16]; -+ memcpy(x, in, 64); -+ for(i=0; i<10; i++){ -+ QR(x[0], x[4], x[ 8], x[12]); -+ QR(x[1], x[5], x[ 9], x[13]); -+ QR(x[2], x[6], x[10], x[14]); -+ QR(x[3], x[7], x[11], x[15]); -+ QR(x[0], x[5], x[10], x[15]); -+ QR(x[1], x[6], x[11], x[12]); -+ QR(x[2], x[7], x[ 8], x[13]); -+ QR(x[3], x[4], x[ 9], x[14]); -+ } -+ for(i=0; i<16; i++) out[i] = x[i]+in[i]; -+} -+ - /* - ** Return N random bytes. - */ - SQLITE_API void sqlite3_randomness(int N, void *pBuf){ -- unsigned char t; - unsigned char *zBuf = pBuf; - - /* The "wsdPrng" macro will resolve to the pseudo-random number generator -@@ -30295,48 +34560,46 @@ SQLITE_API void sqlite3_randomness(int N, void *pBuf){ - - sqlite3_mutex_enter(mutex); - if( N<=0 || pBuf==0 ){ -- wsdPrng.isInit = 0; -+ wsdPrng.s[0] = 0; - sqlite3_mutex_leave(mutex); - return; - } - - /* Initialize the state of the random number generator once, -- ** the first time this routine is called. The seed value does -- ** not need to contain a lot of randomness since we are not -- ** trying to do secure encryption or anything like that... -- ** -- ** Nothing in this file or anywhere else in SQLite does any kind of -- ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random -- ** number generator) not as an encryption device. -+ ** the first time this routine is called. - */ -- if( !wsdPrng.isInit ){ -- int i; -- char k[256]; -- wsdPrng.j = 0; -- wsdPrng.i = 0; -- sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k); -- for(i=0; i<256; i++){ -- wsdPrng.s[i] = (u8)i; -- } -- for(i=0; i<256; i++){ -- wsdPrng.j += wsdPrng.s[i] + k[i]; -- t = wsdPrng.s[wsdPrng.j]; -- wsdPrng.s[wsdPrng.j] = wsdPrng.s[i]; -- wsdPrng.s[i] = t; -+ if( wsdPrng.s[0]==0 ){ -+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0); -+ static const u32 chacha20_init[] = { -+ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 -+ }; -+ memcpy(&wsdPrng.s[0], chacha20_init, 16); -+ if( NEVER(pVfs==0) ){ -+ memset(&wsdPrng.s[4], 0, 44); -+ }else{ -+ sqlite3OsRandomness(pVfs, 44, (char*)&wsdPrng.s[4]); - } -- wsdPrng.isInit = 1; -+ wsdPrng.s[15] = wsdPrng.s[12]; -+ wsdPrng.s[12] = 0; -+ wsdPrng.n = 0; - } - - assert( N>0 ); -- do{ -- wsdPrng.i++; -- t = wsdPrng.s[wsdPrng.i]; -- wsdPrng.j += t; -- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j]; -- wsdPrng.s[wsdPrng.j] = t; -- t += wsdPrng.s[wsdPrng.i]; -- *(zBuf++) = wsdPrng.s[t]; -- }while( --N ); -+ while( 1 /* exit by break */ ){ -+ if( N<=wsdPrng.n ){ -+ memcpy(zBuf, &wsdPrng.out[wsdPrng.n-N], N); -+ wsdPrng.n -= N; -+ break; -+ } -+ if( wsdPrng.n>0 ){ -+ memcpy(zBuf, wsdPrng.out, wsdPrng.n); -+ N -= wsdPrng.n; -+ zBuf += wsdPrng.n; -+ } -+ wsdPrng.s[12]++; -+ chacha_block((u32*)wsdPrng.out, wsdPrng.s); -+ wsdPrng.n = 64; -+ } - sqlite3_mutex_leave(mutex); - } - -@@ -30753,6 +35016,35 @@ static const unsigned char sqlite3Utf8Trans1[] = { - } \ - } - -+/* -+** Write a single UTF8 character whose value is v into the -+** buffer starting at zOut. zOut must be sized to hold at -+** least four bytes. Return the number of bytes needed -+** to encode the new character. -+*/ -+SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char *zOut, u32 v){ -+ if( v<0x00080 ){ -+ zOut[0] = (u8)(v & 0xff); -+ return 1; -+ } -+ if( v<0x00800 ){ -+ zOut[0] = 0xc0 + (u8)((v>>6) & 0x1f); -+ zOut[1] = 0x80 + (u8)(v & 0x3f); -+ return 2; -+ } -+ if( v<0x10000 ){ -+ zOut[0] = 0xe0 + (u8)((v>>12) & 0x0f); -+ zOut[1] = 0x80 + (u8)((v>>6) & 0x3f); -+ zOut[2] = 0x80 + (u8)(v & 0x3f); -+ return 3; -+ } -+ zOut[0] = 0xf0 + (u8)((v>>18) & 0x07); -+ zOut[1] = 0x80 + (u8)((v>>12) & 0x3f); -+ zOut[2] = 0x80 + (u8)((v>>6) & 0x3f); -+ zOut[3] = 0x80 + (u8)(v & 0x3f); -+ return 4; -+} -+ - /* - ** Translate a single UTF-8 character. Return the unicode value. - ** -@@ -30784,7 +35076,7 @@ static const unsigned char sqlite3Utf8Trans1[] = { - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = sqlite3Utf8Trans1[c-0xc0]; \ -- while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ -+ while( zIn0 ); -+ c = z[0]; -+ if( c>=0xc0 ){ -+ c = sqlite3Utf8Trans1[c-0xc0]; -+ if( n>4 ) n = 4; -+ while( i=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; -+ if( c>=0xd8 && c<0xdc && z<=zEnd && z[0]>=0xdc && z[0]<0xe0 ) z += 2; - n++; - } - return (int)(z-(unsigned char const *)zIn) -@@ -31207,20 +35532,10 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){ - #include - #endif - --/* --** Routine needed to support the testcase() macro. --*/ --#ifdef SQLITE_COVERAGE_TEST --SQLITE_PRIVATE void sqlite3Coverage(int x){ -- static unsigned dummy = 0; -- dummy += (unsigned)x; --} --#endif -- - /* - ** Calls to sqlite3FaultSim() are used to simulate a failure during testing, - ** or to bypass normal error detection during testing in order to let --** execute proceed futher downstream. -+** execute proceed further downstream. - ** - ** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The - ** sqlite3FaultSim() function only returns non-zero during testing. -@@ -31246,11 +35561,34 @@ SQLITE_PRIVATE int sqlite3FaultSim(int iTest){ - #ifndef SQLITE_OMIT_FLOATING_POINT - /* - ** Return true if the floating point value is Not a Number (NaN). -+** -+** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. -+** Otherwise, we have our own implementation that works on most systems. - */ - SQLITE_PRIVATE int sqlite3IsNaN(double x){ -+ int rc; /* The value return */ -+#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN - u64 y; - memcpy(&y,&x,sizeof(y)); -- return IsNaN(y); -+ rc = IsNaN(y); -+#else -+ rc = isnan(x); -+#endif /* HAVE_ISNAN */ -+ testcase( rc ); -+ return rc; -+} -+#endif /* SQLITE_OMIT_FLOATING_POINT */ -+ -+#ifndef SQLITE_OMIT_FLOATING_POINT -+/* -+** Return true if the floating point value is NaN or +Inf or -Inf. -+*/ -+SQLITE_PRIVATE int sqlite3IsOverflow(double x){ -+ int rc; /* The value return */ -+ u64 y; -+ memcpy(&y,&x,sizeof(y)); -+ rc = IsOvfl(y); -+ return rc; - } - #endif /* SQLITE_OMIT_FLOATING_POINT */ - -@@ -31275,8 +35613,14 @@ SQLITE_PRIVATE int sqlite3Strlen30(const char *z){ - ** the column name if and only if the COLFLAG_HASTYPE flag is set. - */ - SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){ -- if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt; -- return pCol->zName + strlen(pCol->zName) + 1; -+ if( pCol->colFlags & COLFLAG_HASTYPE ){ -+ return pCol->zCnName + strlen(pCol->zCnName) + 1; -+ }else if( pCol->eCType ){ -+ assert( pCol->eCType<=SQLITE_N_STDTYPE ); -+ return (char*)sqlite3StdType[pCol->eCType-1]; -+ }else{ -+ return zDflt; -+ } - } - - /* -@@ -31297,7 +35641,22 @@ static SQLITE_NOINLINE void sqlite3ErrorFinish(sqlite3 *db, int err_code){ - SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){ - assert( db!=0 ); - db->errCode = err_code; -- if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code); -+ if( err_code || db->pErr ){ -+ sqlite3ErrorFinish(db, err_code); -+ }else{ -+ db->errByteOffset = -1; -+ } -+} -+ -+/* -+** The equivalent of sqlite3Error(db, SQLITE_OK). Clear the error state -+** and error message. -+*/ -+SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){ -+ assert( db!=0 ); -+ db->errCode = SQLITE_OK; -+ db->errByteOffset = -1; -+ if( db->pErr ) sqlite3ValueSetNull(db->pErr); - } - - /* -@@ -31306,6 +35665,23 @@ SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){ - */ - SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){ - if( rc==SQLITE_IOERR_NOMEM ) return; -+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) -+ if( rc==SQLITE_IOERR_IN_PAGE ){ -+ int ii; -+ int iErr; -+ sqlite3BtreeEnterAll(db); -+ for(ii=0; iinDb; ii++){ -+ if( db->aDb[ii].pBt ){ -+ iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt)); -+ if( iErr ){ -+ db->iSysErrno = iErr; -+ } -+ } -+ } -+ sqlite3BtreeLeaveAll(db); -+ return; -+ } -+#endif - rc &= 0xff; - if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){ - db->iSysErrno = sqlite3OsGetLastError(db->pVfs); -@@ -31317,17 +35693,8 @@ SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){ - ** handle "db". The error code is set to "err_code". - ** - ** If it is not NULL, string zFormat specifies the format of the --** error string in the style of the printf functions: The following --** format characters are allowed: --** --** %s Insert a string --** %z A string that should be freed after use --** %d Insert an integer --** %T Insert a token --** %S Insert the first element of a SrcList --** --** zFormat and any string tokens that follow it are assumed to be --** encoded in UTF-8. -+** error string. zFormat and any string tokens that follow it are -+** assumed to be encoded in UTF-8. - ** - ** To clear the most recent error for sqlite handle "db", sqlite3Error - ** should be called with err_code set to SQLITE_OK and zFormat set -@@ -31349,15 +35716,32 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *z - } - } - -+/* -+** Check for interrupts and invoke progress callback. -+*/ -+SQLITE_PRIVATE void sqlite3ProgressCheck(Parse *p){ -+ sqlite3 *db = p->db; -+ if( AtomicLoad(&db->u1.isInterrupted) ){ -+ p->nErr++; -+ p->rc = SQLITE_INTERRUPT; -+ } -+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK -+ if( db->xProgress ){ -+ if( p->rc==SQLITE_INTERRUPT ){ -+ p->nProgressSteps = 0; -+ }else if( (++p->nProgressSteps)>=db->nProgressOps ){ -+ if( db->xProgress(db->pProgressArg) ){ -+ p->nErr++; -+ p->rc = SQLITE_INTERRUPT; -+ } -+ p->nProgressSteps = 0; -+ } -+ } -+#endif -+} -+ - /* - ** Add an error message to pParse->zErrMsg and increment pParse->nErr. --** The following formatting characters are allowed: --** --** %s Insert a string --** %z A string that should be freed after use --** %d Insert an integer --** %T Insert a token --** %S Insert the first element of a SrcList - ** - ** This function should be used to report any error that occurs while - ** compiling an SQL statement (i.e. within sqlite3_prepare()). The -@@ -31370,11 +35754,19 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ - char *zMsg; - va_list ap; - sqlite3 *db = pParse->db; -+ assert( db!=0 ); -+ assert( db->pParse==pParse || db->pParse->pToplevel==pParse ); -+ db->errByteOffset = -2; - va_start(ap, zFormat); - zMsg = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); -+ if( db->errByteOffset<-1 ) db->errByteOffset = -1; - if( db->suppressErr ){ - sqlite3DbFree(db, zMsg); -+ if( db->mallocFailed ){ -+ pParse->nErr++; -+ pParse->rc = SQLITE_NOMEM; -+ } - }else{ - pParse->nErr++; - sqlite3DbFree(db, pParse->zErrMsg); -@@ -31437,11 +35829,72 @@ SQLITE_PRIVATE void sqlite3Dequote(char *z){ - z[j] = 0; - } - SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){ -+ assert( !ExprHasProperty(p, EP_IntValue) ); - assert( sqlite3Isquote(p->u.zToken[0]) ); - p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; - sqlite3Dequote(p->u.zToken); - } - -+/* -+** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken -+** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those -+** that contain '_' characters that must be removed before further processing. -+*/ -+SQLITE_PRIVATE void sqlite3DequoteNumber(Parse *pParse, Expr *p){ -+ assert( p!=0 || pParse->db->mallocFailed ); -+ if( p ){ -+ const char *pIn = p->u.zToken; -+ char *pOut = p->u.zToken; -+ int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X')); -+ int iValue; -+ assert( p->op==TK_QNUMBER ); -+ p->op = TK_INTEGER; -+ do { -+ if( *pIn!=SQLITE_DIGIT_SEPARATOR ){ -+ *pOut++ = *pIn; -+ if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT; -+ }else{ -+ if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1]))) -+ || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1]))) -+ ){ -+ sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken); -+ } -+ } -+ }while( *pIn++ ); -+ if( bHex ) p->op = TK_INTEGER; -+ -+ /* tag-20240227-a: If after dequoting, the number is an integer that -+ ** fits in 32 bits, then it must be converted into EP_IntValue. Other -+ ** parts of the code expect this. See also tag-20240227-b. */ -+ if( p->op==TK_INTEGER && sqlite3GetInt32(p->u.zToken, &iValue) ){ -+ p->u.iValue = iValue; -+ p->flags |= EP_IntValue; -+ } -+ } -+} -+ -+/* -+** If the input token p is quoted, try to adjust the token to remove -+** the quotes. This is not always possible: -+** -+** "abc" -> abc -+** "ab""cd" -> (not possible because of the interior "") -+** -+** Remove the quotes if possible. This is a optimization. The overall -+** system should still return the correct answer even if this routine -+** is always a no-op. -+*/ -+SQLITE_PRIVATE void sqlite3DequoteToken(Token *p){ -+ unsigned int i; -+ if( p->n<2 ) return; -+ if( !sqlite3Isquote(p->z[0]) ) return; -+ for(i=1; in-1; i++){ -+ if( sqlite3Isquote(p->z[i]) ) return; -+ } -+ p->n -= 2; -+ p->z++; -+} -+ - /* - ** Generate a Token object from a string - */ -@@ -31516,43 +35969,40 @@ SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){ - return h; - } - --/* --** Compute 10 to the E-th power. Examples: E==1 results in 10. --** E==2 results in 100. E==50 results in 1.0e50. -+/* Double-Double multiplication. (x[0],x[1]) *= (y,yy) - ** --** This routine only works for values of E between 1 and 341. -+** Reference: -+** T. J. Dekker, "A Floating-Point Technique for Extending the -+** Available Precision". 1971-07-26. - */ --static LONGDOUBLE_TYPE sqlite3Pow10(int E){ --#if defined(_MSC_VER) -- static const LONGDOUBLE_TYPE x[] = { -- 1.0e+001L, -- 1.0e+002L, -- 1.0e+004L, -- 1.0e+008L, -- 1.0e+016L, -- 1.0e+032L, -- 1.0e+064L, -- 1.0e+128L, -- 1.0e+256L -- }; -- LONGDOUBLE_TYPE r = 1.0; -- int i; -- assert( E>=0 && E<=307 ); -- for(i=0; E!=0; i++, E >>=1){ -- if( E & 1 ) r *= x[i]; -- } -- return r; --#else -- LONGDOUBLE_TYPE x = 10.0; -- LONGDOUBLE_TYPE r = 1.0; -- while(1){ -- if( E & 1 ) r *= x; -- E >>= 1; -- if( E==0 ) break; -- x *= x; -- } -- return r; --#endif -+static void dekkerMul2(volatile double *x, double y, double yy){ -+ /* -+ ** The "volatile" keywords on parameter x[] and on local variables -+ ** below are needed force intermediate results to be truncated to -+ ** binary64 rather than be carried around in an extended-precision -+ ** format. The truncation is necessary for the Dekker algorithm to -+ ** work. Intel x86 floating point might omit the truncation without -+ ** the use of volatile. -+ */ -+ volatile double tx, ty, p, q, c, cc; -+ double hx, hy; -+ u64 m; -+ memcpy(&m, (void*)&x[0], 8); -+ m &= 0xfffffffffc000000LL; -+ memcpy(&hx, &m, 8); -+ tx = x[0] - hx; -+ memcpy(&m, &y, 8); -+ m &= 0xfffffffffc000000LL; -+ memcpy(&hy, &m, 8); -+ ty = y - hy; -+ p = hx*hy; -+ q = hx*ty + tx*hy; -+ c = p+q; -+ cc = p - c + q + tx*ty; -+ cc = x[0]*yy + x[1]*y + cc; -+ x[0] = c + cc; -+ x[1] = c - x[0]; -+ x[1] += cc; - } - - /* -@@ -31593,14 +36043,15 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en - const char *zEnd; - /* sign * significand * (10 ^ (esign * exponent)) */ - int sign = 1; /* sign of significand */ -- i64 s = 0; /* significand */ -+ u64 s = 0; /* significand */ - int d = 0; /* adjust exponent for shifting decimal point */ - int esign = 1; /* sign of exponent */ - int e = 0; /* exponent */ - int eValid = 1; /* True exponent is either not used or is well-formed */ -- double result; - int nDigit = 0; /* Number of digits processed */ - int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ -+ u64 s2; /* round-tripped significand */ -+ double rr[2]; - - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - *pResult = 0.0; /* Default return value, in case of an error */ -@@ -31638,7 +36089,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en - while( z=((LARGEST_INT64-9)/10) ){ -+ if( s>=((LARGEST_UINT64-9)/10) ){ - /* skip non-significant significand digits - ** (increase exponent by d to shift decimal left) */ - while( z0 ){ /*OPTIMIZATION-IF-TRUE*/ -- if( esign>0 ){ -- if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/ -- s *= 10; -- }else{ -- if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/ -- s /= 10; -- } -- e--; -- } -+ /* Try to adjust the exponent to make it smaller */ -+ while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){ -+ s *= 10; -+ e--; -+ } -+ while( e<0 && (s%10)==0 ){ -+ s /= 10; -+ e++; -+ } - -- /* adjust the sign of significand */ -- s = sign<0 ? -s : s; -+ rr[0] = (double)s; -+ assert( sizeof(s2)==sizeof(rr[0]) ); -+#ifdef SQLITE_DEBUG -+ rr[1] = 18446744073709549568.0; -+ memcpy(&s2, &rr[1], sizeof(s2)); -+ assert( s2==0x43efffffffffffffLL ); -+#endif -+ /* Largest double that can be safely converted to u64 -+ ** vvvvvvvvvvvvvvvvvvvvvv */ -+ if( rr[0]<=18446744073709549568.0 ){ -+ s2 = (u64)rr[0]; -+ rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); -+ }else{ -+ rr[1] = 0.0; -+ } -+ assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */ - -- if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/ -- result = (double)s; -- }else{ -- /* attempt to handle extremely small/large numbers better */ -- if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/ -- if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/ -- LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308); -- if( esign<0 ){ -- result = s / scale; -- result /= 1.0e+308; -- }else{ -- result = s * scale; -- result *= 1.0e+308; -- } -- }else{ assert( e>=342 ); -- if( esign<0 ){ -- result = 0.0*s; -- }else{ --#ifdef INFINITY -- result = INFINITY*s; --#else -- result = 1e308*1e308*s; /* Infinity */ --#endif -- } -- } -- }else{ -- LONGDOUBLE_TYPE scale = sqlite3Pow10(e); -- if( esign<0 ){ -- result = s / scale; -- }else{ -- result = s * scale; -- } -- } -+ if( e>0 ){ -+ while( e>=100 ){ -+ e -= 100; -+ dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); -+ } -+ while( e>=10 ){ -+ e -= 10; -+ dekkerMul2(rr, 1.0e+10, 0.0); -+ } -+ while( e>=1 ){ -+ e -= 1; -+ dekkerMul2(rr, 1.0e+01, 0.0); -+ } -+ }else{ -+ while( e<=-100 ){ -+ e += 100; -+ dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); -+ } -+ while( e<=-10 ){ -+ e += 10; -+ dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); -+ } -+ while( e<=-1 ){ -+ e += 1; -+ dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); - } - } -+ *pResult = rr[0]+rr[1]; -+ if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; -+ if( sign<0 ) *pResult = -*pResult; -+ assert( !sqlite3IsNaN(*pResult) ); - -- /* store the result */ -- *pResult = result; -- -- /* return true if number and no extra non-whitespace chracters after */ -+atof_return: -+ /* return true if number and no extra non-whitespace characters after */ - if( z==zEnd && nDigit>0 && eValid && eType>0 ){ - return eType; - }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){ -@@ -31782,11 +36230,14 @@ do_atof_calc: - #endif - - /* --** Render an signed 64-bit integer as text. Store the result in zOut[]. -+** Render an signed 64-bit integer as text. Store the result in zOut[] and -+** return the length of the string that was stored, in bytes. The value -+** returned does not include the zero terminator at the end of the output -+** string. - ** - ** The caller must ensure that zOut[] is at least 21 bytes in size. - */ --SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ -+SQLITE_PRIVATE int sqlite3Int64ToText(i64 v, char *zOut){ - int i; - u64 x; - char zTemp[22]; -@@ -31797,12 +36248,15 @@ SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ - } - i = sizeof(zTemp)-2; - zTemp[sizeof(zTemp)-1] = 0; -- do{ -- zTemp[i--] = (x%10) + '0'; -+ while( 1 /*exit-by-break*/ ){ -+ zTemp[i] = (x%10) + '0'; - x = x/10; -- }while( x ); -- if( v<0 ) zTemp[i--] = '-'; -- memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); -+ if( x==0 ) break; -+ i--; -+ }; -+ if( v<0 ) zTemp[--i] = '-'; -+ memcpy(zOut, &zTemp[i], sizeof(zTemp)-i); -+ return sizeof(zTemp)-1-i; - } - - /* -@@ -31867,6 +36321,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc - incr = 1; - }else{ - incr = 2; -+ length &= ~1; - assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - for(i=3-enc; i16 ) return 2; -+ if( z[k]!=0 ) return 1; -+ return 0; - }else - #endif /* SQLITE_OMIT_HEX_INTEGER */ - { -- return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); -+ int n = (int)(0x3fffffff&strspn(z,"+- \n\t0123456789")); -+ if( z[n] ) n++; -+ return sqlite3Atoi64(z, pOut, n, SQLITE_UTF8); - } - } - -@@ -32002,7 +36461,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ - u32 u = 0; - zNum += 2; - while( zNum[0]=='0' ) zNum++; -- for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){ -+ for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){ - u = u*16 + sqlite3HexToInt(zNum[i]); - } - if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ -@@ -32049,6 +36508,146 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ - return x; - } - -+/* -+** Decode a floating-point value into an approximate decimal -+** representation. -+** -+** If iRound<=0 then round to -iRound significant digits to the -+** the left of the decimal point, or to a maximum of mxRound total -+** significant digits. -+** -+** If iRound>0 round to min(iRound,mxRound) significant digits total. -+** -+** mxRound must be positive. -+** -+** The significant digits of the decimal representation are -+** stored in p->z[] which is a often (but not always) a pointer -+** into the middle of p->zBuf[]. There are p->n significant digits. -+** The p->z[] array is *not* zero-terminated. -+*/ -+SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ -+ int i; -+ u64 v; -+ int e, exp = 0; -+ double rr[2]; -+ -+ p->isSpecial = 0; -+ p->z = p->zBuf; -+ assert( mxRound>0 ); -+ -+ /* Convert negative numbers to positive. Deal with Infinity, 0.0, and -+ ** NaN. */ -+ if( r<0.0 ){ -+ p->sign = '-'; -+ r = -r; -+ }else if( r==0.0 ){ -+ p->sign = '+'; -+ p->n = 1; -+ p->iDP = 1; -+ p->z = "0"; -+ return; -+ }else{ -+ p->sign = '+'; -+ } -+ memcpy(&v,&r,8); -+ e = v>>52; -+ if( (e&0x7ff)==0x7ff ){ -+ p->isSpecial = 1 + (v!=0x7ff0000000000000LL); -+ p->n = 0; -+ p->iDP = 0; -+ return; -+ } -+ -+ /* Multiply r by powers of ten until it lands somewhere in between -+ ** 1.0e+19 and 1.0e+17. -+ ** -+ ** Use Dekker-style double-double computation to increase the -+ ** precision. -+ ** -+ ** The error terms on constants like 1.0e+100 computed using the -+ ** decimal extension, for example as follows: -+ ** -+ ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); -+ */ -+ rr[0] = r; -+ rr[1] = 0.0; -+ if( rr[0]>9.223372036854774784e+18 ){ -+ while( rr[0]>9.223372036854774784e+118 ){ -+ exp += 100; -+ dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); -+ } -+ while( rr[0]>9.223372036854774784e+28 ){ -+ exp += 10; -+ dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); -+ } -+ while( rr[0]>9.223372036854774784e+18 ){ -+ exp += 1; -+ dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); -+ } -+ }else{ -+ while( rr[0]<9.223372036854774784e-83 ){ -+ exp -= 100; -+ dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); -+ } -+ while( rr[0]<9.223372036854774784e+07 ){ -+ exp -= 10; -+ dekkerMul2(rr, 1.0e+10, 0.0); -+ } -+ while( rr[0]<9.22337203685477478e+17 ){ -+ exp -= 1; -+ dekkerMul2(rr, 1.0e+01, 0.0); -+ } -+ } -+ v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; -+ -+ /* Extract significant digits. */ -+ i = sizeof(p->zBuf)-1; -+ assert( v>0 ); -+ while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } -+ assert( i>=0 && izBuf)-1 ); -+ p->n = sizeof(p->zBuf) - 1 - i; -+ assert( p->n>0 ); -+ assert( p->nzBuf) ); -+ p->iDP = p->n + exp; -+ if( iRound<=0 ){ -+ iRound = p->iDP - iRound; -+ if( iRound==0 && p->zBuf[i+1]>='5' ){ -+ iRound = 1; -+ p->zBuf[i--] = '0'; -+ p->n++; -+ p->iDP++; -+ } -+ } -+ if( iRound>0 && (iRoundn || p->n>mxRound) ){ -+ char *z = &p->zBuf[i+1]; -+ if( iRound>mxRound ) iRound = mxRound; -+ p->n = iRound; -+ if( z[iRound]>='5' ){ -+ int j = iRound-1; -+ while( 1 /*exit-by-break*/ ){ -+ z[j]++; -+ if( z[j]<='9' ) break; -+ z[j] = '0'; -+ if( j==0 ){ -+ p->z[i--] = '1'; -+ p->n++; -+ p->iDP++; -+ break; -+ }else{ -+ j--; -+ } -+ } -+ } -+ } -+ p->z = &p->zBuf[i+1]; -+ assert( i+p->n < sizeof(p->zBuf) ); -+ assert( p->n>0 ); -+ while( p->z[p->n-1]=='0' ){ -+ p->n--; -+ assert( p->n>0 ); -+ } -+} -+ - /* - ** Try to convert z into an unsigned 32-bit integer. Return true on - ** success and false if there is an error. -@@ -32312,121 +36911,32 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ - ** this function assumes the single-byte case has already been handled. - */ - SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ -- u32 a,b; -+ u64 v64; -+ u8 n; - -- /* The 1-byte case. Overwhelmingly the most common. Handled inline -- ** by the getVarin32() macro */ -- a = *p; -- /* a: p0 (unmasked) */ --#ifndef getVarint32 -- if (!(a&0x80)) -- { -- /* Values between 0 and 127 */ -- *v = a; -- return 1; -- } --#endif -+ /* Assume that the single-byte case has already been handled by -+ ** the getVarint32() macro */ -+ assert( (p[0] & 0x80)!=0 ); - -- /* The 2-byte case */ -- p++; -- b = *p; -- /* b: p1 (unmasked) */ -- if (!(b&0x80)) -- { -- /* Values between 128 and 16383 */ -- a &= 0x7f; -- a = a<<7; -- *v = a | b; -+ if( (p[1] & 0x80)==0 ){ -+ /* This is the two-byte case */ -+ *v = ((p[0]&0x7f)<<7) | p[1]; - return 2; - } -- -- /* The 3-byte case */ -- p++; -- a = a<<14; -- a |= *p; -- /* a: p0<<14 | p2 (unmasked) */ -- if (!(a&0x80)) -- { -- /* Values between 16384 and 2097151 */ -- a &= (0x7f<<14)|(0x7f); -- b &= 0x7f; -- b = b<<7; -- *v = a | b; -+ if( (p[2] & 0x80)==0 ){ -+ /* This is the three-byte case */ -+ *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2]; - return 3; - } -- -- /* A 32-bit varint is used to store size information in btrees. -- ** Objects are rarely larger than 2MiB limit of a 3-byte varint. -- ** A 3-byte varint is sufficient, for example, to record the size -- ** of a 1048569-byte BLOB or string. -- ** -- ** We only unroll the first 1-, 2-, and 3- byte cases. The very -- ** rare larger cases can be handled by the slower 64-bit varint -- ** routine. -- */ --#if 1 -- { -- u64 v64; -- u8 n; -- -- n = sqlite3GetVarint(p-2, &v64); -- assert( n>3 && n<=9 ); -- if( (v64 & SQLITE_MAX_U32)!=v64 ){ -- *v = 0xffffffff; -- }else{ -- *v = (u32)v64; -- } -- return n; -- } -- --#else -- /* For following code (kept for historical record only) shows an -- ** unrolling for the 3- and 4-byte varint cases. This code is -- ** slightly faster, but it is also larger and much harder to test. -- */ -- p++; -- b = b<<14; -- b |= *p; -- /* b: p1<<14 | p3 (unmasked) */ -- if (!(b&0x80)) -- { -- /* Values between 2097152 and 268435455 */ -- b &= (0x7f<<14)|(0x7f); -- a &= (0x7f<<14)|(0x7f); -- a = a<<7; -- *v = a | b; -- return 4; -- } -- -- p++; -- a = a<<14; -- a |= *p; -- /* a: p0<<28 | p2<<14 | p4 (unmasked) */ -- if (!(a&0x80)) -- { -- /* Values between 268435456 and 34359738367 */ -- a &= SLOT_4_2_0; -- b &= SLOT_4_2_0; -- b = b<<7; -- *v = a | b; -- return 5; -- } -- -- /* We can only reach this point when reading a corrupt database -- ** file. In that case we are not in any hurry. Use the (relatively -- ** slow) general-purpose sqlite3GetVarint() routine to extract the -- ** value. */ -- { -- u64 v64; -- u8 n; -- -- p -= 4; -- n = sqlite3GetVarint(p, &v64); -- assert( n>5 && n<=9 ); -+ /* four or more bytes */ -+ n = sqlite3GetVarint(p, &v64); -+ assert( n>3 && n<=9 ); -+ if( (v64 & SQLITE_MAX_U32)!=v64 ){ -+ *v = 0xffffffff; -+ }else{ - *v = (u32)v64; -- return n; - } --#endif -+ return n; - } - - /* -@@ -32546,13 +37056,13 @@ static void logBadConnection(const char *zType){ - ** used as an argument to sqlite3_errmsg() or sqlite3_close(). - */ - SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){ -- u32 magic; -+ u8 eOpenState; - if( db==0 ){ - logBadConnection("NULL"); - return 0; - } -- magic = db->magic; -- if( magic!=SQLITE_MAGIC_OPEN ){ -+ eOpenState = db->eOpenState; -+ if( eOpenState!=SQLITE_STATE_OPEN ){ - if( sqlite3SafetyCheckSickOrOk(db) ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - logBadConnection("unopened"); -@@ -32563,11 +37073,11 @@ SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){ - } - } - SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ -- u32 magic; -- magic = db->magic; -- if( magic!=SQLITE_MAGIC_SICK && -- magic!=SQLITE_MAGIC_OPEN && -- magic!=SQLITE_MAGIC_BUSY ){ -+ u8 eOpenState; -+ eOpenState = db->eOpenState; -+ if( eOpenState!=SQLITE_STATE_SICK && -+ eOpenState!=SQLITE_STATE_OPEN && -+ eOpenState!=SQLITE_STATE_BUSY ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - logBadConnection("invalid"); - return 0; -@@ -32577,7 +37087,7 @@ SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ - } - - /* --** Attempt to add, substract, or multiply the 64-bit signed value iB against -+** Attempt to add, subtract, or multiply the 64-bit signed value iB against - ** the other 64-bit signed integer at *pA and store the result in *pA. - ** Return 0 on success. Or if the operation would have resulted in an - ** overflow, leave *pA unchanged and return 1. -@@ -32640,7 +37150,7 @@ SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){ - } - - /* --** Compute the absolute value of a 32-bit signed integer, of possible. Or -+** Compute the absolute value of a 32-bit signed integer, if possible. Or - ** if the integer has a value of -2147483648, return +2147483647 - */ - SQLITE_PRIVATE int sqlite3AbsInt32(int x){ -@@ -32732,7 +37242,6 @@ SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){ - return a[x&7] + y - 10; - } - --#ifndef SQLITE_OMIT_VIRTUALTABLE - /* - ** Convert a double into a LogEst - ** In other words, compute an approximation for 10*log2(x). -@@ -32747,16 +37256,9 @@ SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){ - e = (a>>52) - 1022; - return e*10; - } --#endif /* SQLITE_OMIT_VIRTUALTABLE */ - --#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ -- defined(SQLITE_ENABLE_STAT4) || \ -- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) - /* - ** Convert a LogEst into an integer. --** --** Note that this routine is only used when one or more of various --** non-standard compile-time options is enabled. - */ - SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){ - u64 n; -@@ -32764,17 +37266,9 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){ - x /= 10; - if( n>=5 ) n -= 2; - else if( n>=1 ) n -= 1; --#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ -- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) - if( x>60 ) return (u64)LARGEST_INT64; --#else -- /* If only SQLITE_ENABLE_STAT4 is on, then the largest input -- ** possible to this routine is 310, resulting in a maximum x of 31 */ -- assert( x<=60 ); --#endif - return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x); - } --#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */ - - /* - ** Add a new name/number pair to a VList. This might require that the -@@ -32937,12 +37431,19 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){ - */ - static unsigned int strHash(const char *z){ - unsigned int h = 0; -- unsigned char c; -- while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/ -+ while( z[0] ){ /*OPTIMIZATION-IF-TRUE*/ - /* Knuth multiplicative hashing. (Sorting & Searching, p. 510). - ** 0x9e3779b1 is 2654435761 which is the closest prime number to -- ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */ -- h += sqlite3UpperToLower[c]; -+ ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. -+ ** -+ ** Only bits 0xdf for ASCII and bits 0xbf for EBCDIC each octet are -+ ** hashed since the omitted bits determine the upper/lower case difference. -+ */ -+#ifdef SQLITE_EBCDIC -+ h += 0xbf & (unsigned char)*(z++); -+#else -+ h += 0xdf & (unsigned char)*(z++); -+#endif - h *= 0x9e3779b1; - } - return h; -@@ -32980,7 +37481,7 @@ static void insertElement( - } - - --/* Resize the hash table so that it cantains "new_size" buckets. -+/* Resize the hash table so that it contains "new_size" buckets. - ** - ** The hash table might fail to resize if sqlite3_malloc() fails or - ** if the new size is the same as the prior size. -@@ -33015,9 +37516,8 @@ static int rehash(Hash *pH, unsigned int new_size){ - pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht); - memset(new_ht, 0, new_size*sizeof(struct _ht)); - for(elem=pH->first, pH->first=0; elem; elem = next_elem){ -- unsigned int h = strHash(elem->pKey) % new_size; - next_elem = elem->next; -- insertElement(pH, &new_ht[h], elem); -+ insertElement(pH, &new_ht[elem->h % new_size], elem); - } - return 1; - } -@@ -33035,26 +37535,26 @@ static HashElem *findElementWithHash( - HashElem *elem; /* Used to loop thru the element list */ - unsigned int count; /* Number of elements left to test */ - unsigned int h; /* The computed hash */ -- static HashElem nullElement = { 0, 0, 0, 0 }; -+ static HashElem nullElement = { 0, 0, 0, 0, 0 }; - -+ h = strHash(pKey); - if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/ - struct _ht *pEntry; -- h = strHash(pKey) % pH->htsize; -- pEntry = &pH->ht[h]; -+ pEntry = &pH->ht[h % pH->htsize]; - elem = pEntry->chain; - count = pEntry->count; - }else{ -- h = 0; - elem = pH->first; - count = pH->count; - } - if( pHash ) *pHash = h; -- while( count-- ){ -+ while( count ){ - assert( elem!=0 ); -- if( sqlite3StrICmp(elem->pKey,pKey)==0 ){ -+ if( h==elem->h && sqlite3StrICmp(elem->pKey,pKey)==0 ){ - return elem; - } - elem = elem->next; -+ count--; - } - return &nullElement; - } -@@ -33062,10 +37562,9 @@ static HashElem *findElementWithHash( - /* Remove a single entry from the hash table given a pointer to that - ** element and a hash on the element's key. - */ --static void removeElementGivenHash( -+static void removeElement( - Hash *pH, /* The pH containing "elem" */ -- HashElem* elem, /* The element to be removed from the pH */ -- unsigned int h /* Hash value for the element */ -+ HashElem *elem /* The element to be removed from the pH */ - ){ - struct _ht *pEntry; - if( elem->prev ){ -@@ -33077,7 +37576,7 @@ static void removeElementGivenHash( - elem->next->prev = elem->prev; - } - if( pH->ht ){ -- pEntry = &pH->ht[h]; -+ pEntry = &pH->ht[elem->h % pH->htsize]; - if( pEntry->chain==elem ){ - pEntry->chain = elem->next; - } -@@ -33128,7 +37627,7 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ - if( elem->data ){ - void *old_data = elem->data; - if( data==0 ){ -- removeElementGivenHash(pH,elem,h); -+ removeElement(pH,elem); - }else{ - elem->data = data; - elem->pKey = pKey; -@@ -33139,15 +37638,13 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ - new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) ); - if( new_elem==0 ) return data; - new_elem->pKey = pKey; -+ new_elem->h = h; - new_elem->data = data; - pH->count++; -- if( pH->count>=10 && pH->count > 2*pH->htsize ){ -- if( rehash(pH, pH->count*2) ){ -- assert( pH->htsize>0 ); -- h = strHash(pKey) % pH->htsize; -- } -+ if( pH->count>=5 && pH->count > 2*pH->htsize ){ -+ rehash(pH, pH->count*3); - } -- insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem); -+ insertElement(pH, pH->ht ? &pH->ht[new_elem->h % pH->htsize] : 0, new_elem); - return 0; - } - -@@ -33168,185 +37665,1181 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ - /* 0 */ "Savepoint" OpHelp(""), - /* 1 */ "AutoCommit" OpHelp(""), - /* 2 */ "Transaction" OpHelp(""), -- /* 3 */ "SorterNext" OpHelp(""), -- /* 4 */ "Prev" OpHelp(""), -- /* 5 */ "Next" OpHelp(""), -- /* 6 */ "Checkpoint" OpHelp(""), -- /* 7 */ "JournalMode" OpHelp(""), -- /* 8 */ "Vacuum" OpHelp(""), -- /* 9 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), -- /* 10 */ "VUpdate" OpHelp("data=r[P3@P2]"), -- /* 11 */ "Goto" OpHelp(""), -- /* 12 */ "Gosub" OpHelp(""), -- /* 13 */ "InitCoroutine" OpHelp(""), -- /* 14 */ "Yield" OpHelp(""), -- /* 15 */ "MustBeInt" OpHelp(""), -- /* 16 */ "Jump" OpHelp(""), -- /* 17 */ "Once" OpHelp(""), -- /* 18 */ "If" OpHelp(""), -+ /* 3 */ "Checkpoint" OpHelp(""), -+ /* 4 */ "JournalMode" OpHelp(""), -+ /* 5 */ "Vacuum" OpHelp(""), -+ /* 6 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), -+ /* 7 */ "VUpdate" OpHelp("data=r[P3@P2]"), -+ /* 8 */ "Init" OpHelp("Start at P2"), -+ /* 9 */ "Goto" OpHelp(""), -+ /* 10 */ "Gosub" OpHelp(""), -+ /* 11 */ "InitCoroutine" OpHelp(""), -+ /* 12 */ "Yield" OpHelp(""), -+ /* 13 */ "MustBeInt" OpHelp(""), -+ /* 14 */ "Jump" OpHelp(""), -+ /* 15 */ "Once" OpHelp(""), -+ /* 16 */ "If" OpHelp(""), -+ /* 17 */ "IfNot" OpHelp(""), -+ /* 18 */ "IsType" OpHelp("if typeof(P1.P3) in P5 goto P2"), - /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), -- /* 20 */ "IfNot" OpHelp(""), -- /* 21 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), -- /* 22 */ "SeekLT" OpHelp("key=r[P3@P4]"), -- /* 23 */ "SeekLE" OpHelp("key=r[P3@P4]"), -- /* 24 */ "SeekGE" OpHelp("key=r[P3@P4]"), -- /* 25 */ "SeekGT" OpHelp("key=r[P3@P4]"), -- /* 26 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), -- /* 27 */ "IfNoHope" OpHelp("key=r[P3@P4]"), -- /* 28 */ "NoConflict" OpHelp("key=r[P3@P4]"), -- /* 29 */ "NotFound" OpHelp("key=r[P3@P4]"), -- /* 30 */ "Found" OpHelp("key=r[P3@P4]"), -- /* 31 */ "SeekRowid" OpHelp("intkey=r[P3]"), -- /* 32 */ "NotExists" OpHelp("intkey=r[P3]"), -- /* 33 */ "Last" OpHelp(""), -- /* 34 */ "IfSmaller" OpHelp(""), -- /* 35 */ "SorterSort" OpHelp(""), -- /* 36 */ "Sort" OpHelp(""), -- /* 37 */ "Rewind" OpHelp(""), -- /* 38 */ "IdxLE" OpHelp("key=r[P3@P4]"), -- /* 39 */ "IdxGT" OpHelp("key=r[P3@P4]"), -- /* 40 */ "IdxLT" OpHelp("key=r[P3@P4]"), -- /* 41 */ "IdxGE" OpHelp("key=r[P3@P4]"), -- /* 42 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), -+ /* 20 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"), -+ /* 21 */ "SeekLT" OpHelp("key=r[P3@P4]"), -+ /* 22 */ "SeekLE" OpHelp("key=r[P3@P4]"), -+ /* 23 */ "SeekGE" OpHelp("key=r[P3@P4]"), -+ /* 24 */ "SeekGT" OpHelp("key=r[P3@P4]"), -+ /* 25 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"), -+ /* 26 */ "IfNoHope" OpHelp("key=r[P3@P4]"), -+ /* 27 */ "NoConflict" OpHelp("key=r[P3@P4]"), -+ /* 28 */ "NotFound" OpHelp("key=r[P3@P4]"), -+ /* 29 */ "Found" OpHelp("key=r[P3@P4]"), -+ /* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"), -+ /* 31 */ "NotExists" OpHelp("intkey=r[P3]"), -+ /* 32 */ "Last" OpHelp(""), -+ /* 33 */ "IfSizeBetween" OpHelp(""), -+ /* 34 */ "SorterSort" OpHelp(""), -+ /* 35 */ "Sort" OpHelp(""), -+ /* 36 */ "Rewind" OpHelp(""), -+ /* 37 */ "SorterNext" OpHelp(""), -+ /* 38 */ "Prev" OpHelp(""), -+ /* 39 */ "Next" OpHelp(""), -+ /* 40 */ "IdxLE" OpHelp("key=r[P3@P4]"), -+ /* 41 */ "IdxGT" OpHelp("key=r[P3@P4]"), -+ /* 42 */ "IdxLT" OpHelp("key=r[P3@P4]"), - /* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), - /* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), -- /* 45 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), -- /* 46 */ "Program" OpHelp(""), -- /* 47 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), -- /* 48 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), -- /* 49 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), -- /* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), -- /* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), -- /* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), -- /* 53 */ "Eq" OpHelp("IF r[P3]==r[P1]"), -- /* 54 */ "Gt" OpHelp("IF r[P3]>r[P1]"), -- /* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"), -- /* 56 */ "Lt" OpHelp("IF r[P3]=r[P1]"), -- /* 58 */ "ElseNotEq" OpHelp(""), -- /* 59 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), -- /* 60 */ "IncrVacuum" OpHelp(""), -- /* 61 */ "VNext" OpHelp(""), -- /* 62 */ "Init" OpHelp("Start at P2"), -- /* 63 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), -- /* 64 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), -- /* 65 */ "Return" OpHelp(""), -- /* 66 */ "EndCoroutine" OpHelp(""), -- /* 67 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), -- /* 68 */ "Halt" OpHelp(""), -- /* 69 */ "Integer" OpHelp("r[P2]=P1"), -- /* 70 */ "Int64" OpHelp("r[P2]=P4"), -- /* 71 */ "String" OpHelp("r[P2]='P4' (len=P1)"), -- /* 72 */ "Null" OpHelp("r[P2..P3]=NULL"), -- /* 73 */ "SoftNull" OpHelp("r[P1]=NULL"), -- /* 74 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), -- /* 75 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), -- /* 76 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), -- /* 77 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), -- /* 78 */ "SCopy" OpHelp("r[P2]=r[P1]"), -- /* 79 */ "IntCopy" OpHelp("r[P2]=r[P1]"), -- /* 80 */ "ResultRow" OpHelp("output=r[P1@P2]"), -- /* 81 */ "CollSeq" OpHelp(""), -- /* 82 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), -- /* 83 */ "RealAffinity" OpHelp(""), -- /* 84 */ "Cast" OpHelp("affinity(r[P1])"), -- /* 85 */ "Permutation" OpHelp(""), -- /* 86 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), -- /* 87 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), -- /* 88 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), -- /* 89 */ "Column" OpHelp("r[P3]=PX"), -- /* 90 */ "Affinity" OpHelp("affinity(r[P1@P2])"), -- /* 91 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), -- /* 92 */ "Count" OpHelp("r[P2]=count()"), -- /* 93 */ "ReadCookie" OpHelp(""), -- /* 94 */ "SetCookie" OpHelp(""), -- /* 95 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), -- /* 96 */ "OpenRead" OpHelp("root=P2 iDb=P3"), -- /* 97 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), -- /* 98 */ "OpenDup" OpHelp(""), -- /* 99 */ "OpenAutoindex" OpHelp("nColumn=P2"), -- /* 100 */ "OpenEphemeral" OpHelp("nColumn=P2"), -- /* 101 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), -- /* 102 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), -- /* 103 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), -- /* 105 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), -- /* 106 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), -- /* 107 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), -- /* 108 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), -- /* 109 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), -- /* 110 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), -- /* 111 */ "SorterOpen" OpHelp(""), -- /* 112 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), -- /* 113 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), -- /* 114 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), -- /* 115 */ "String8" OpHelp("r[P2]='P4'"), -- /* 116 */ "Close" OpHelp(""), -- /* 117 */ "ColumnsUsed" OpHelp(""), -- /* 118 */ "SeekHit" OpHelp("seekHit=P2"), -- /* 119 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), -- /* 120 */ "NewRowid" OpHelp("r[P2]=rowid"), -- /* 121 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), -- /* 122 */ "Delete" OpHelp(""), -- /* 123 */ "ResetCount" OpHelp(""), -- /* 124 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), -- /* 125 */ "SorterData" OpHelp("r[P2]=data"), -- /* 126 */ "RowData" OpHelp("r[P2]=data"), -- /* 127 */ "Rowid" OpHelp("r[P2]=rowid"), -- /* 128 */ "NullRow" OpHelp(""), -- /* 129 */ "SeekEnd" OpHelp(""), -- /* 130 */ "IdxInsert" OpHelp("key=r[P2]"), -- /* 131 */ "SorterInsert" OpHelp("key=r[P2]"), -- /* 132 */ "IdxDelete" OpHelp("key=r[P2@P3]"), -- /* 133 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), -- /* 134 */ "IdxRowid" OpHelp("r[P2]=rowid"), -- /* 135 */ "FinishSeek" OpHelp(""), -- /* 136 */ "Destroy" OpHelp(""), -- /* 137 */ "Clear" OpHelp(""), -- /* 138 */ "ResetSorter" OpHelp(""), -- /* 139 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), -- /* 140 */ "SqlExec" OpHelp(""), -- /* 141 */ "ParseSchema" OpHelp(""), -- /* 142 */ "LoadAnalysis" OpHelp(""), -- /* 143 */ "DropTable" OpHelp(""), -- /* 144 */ "DropIndex" OpHelp(""), -- /* 145 */ "DropTrigger" OpHelp(""), -- /* 146 */ "IntegrityCk" OpHelp(""), -- /* 147 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), -- /* 148 */ "Param" OpHelp(""), -- /* 149 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), -- /* 150 */ "Real" OpHelp("r[P2]=P4"), -- /* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), -- /* 152 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), -- /* 153 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), -- /* 154 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), -- /* 155 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), -- /* 156 */ "AggValue" OpHelp("r[P3]=value N=P2"), -- /* 157 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), -- /* 158 */ "Expire" OpHelp(""), -- /* 159 */ "CursorLock" OpHelp(""), -- /* 160 */ "CursorUnlock" OpHelp(""), -- /* 161 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), -- /* 162 */ "VBegin" OpHelp(""), -- /* 163 */ "VCreate" OpHelp(""), -- /* 164 */ "VDestroy" OpHelp(""), -- /* 165 */ "VOpen" OpHelp(""), -- /* 166 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), -- /* 167 */ "VRename" OpHelp(""), -- /* 168 */ "Pagecount" OpHelp(""), -- /* 169 */ "MaxPgcnt" OpHelp(""), -- /* 170 */ "Trace" OpHelp(""), -- /* 171 */ "CursorHint" OpHelp(""), -- /* 172 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), -- /* 173 */ "Noop" OpHelp(""), -- /* 174 */ "Explain" OpHelp(""), -- /* 175 */ "Abortable" OpHelp(""), -+ /* 45 */ "IdxGE" OpHelp("key=r[P3@P4]"), -+ /* 46 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), -+ /* 47 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), -+ /* 48 */ "Program" OpHelp(""), -+ /* 49 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), -+ /* 50 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), -+ /* 51 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), -+ /* 52 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), -+ /* 53 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), -+ /* 54 */ "Eq" OpHelp("IF r[P3]==r[P1]"), -+ /* 55 */ "Gt" OpHelp("IF r[P3]>r[P1]"), -+ /* 56 */ "Le" OpHelp("IF r[P3]<=r[P1]"), -+ /* 57 */ "Lt" OpHelp("IF r[P3]=r[P1]"), -+ /* 59 */ "ElseEq" OpHelp(""), -+ /* 60 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), -+ /* 61 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), -+ /* 62 */ "IncrVacuum" OpHelp(""), -+ /* 63 */ "VNext" OpHelp(""), -+ /* 64 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"), -+ /* 65 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), -+ /* 66 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), -+ /* 67 */ "Return" OpHelp(""), -+ /* 68 */ "EndCoroutine" OpHelp(""), -+ /* 69 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), -+ /* 70 */ "Halt" OpHelp(""), -+ /* 71 */ "Integer" OpHelp("r[P2]=P1"), -+ /* 72 */ "Int64" OpHelp("r[P2]=P4"), -+ /* 73 */ "String" OpHelp("r[P2]='P4' (len=P1)"), -+ /* 74 */ "BeginSubrtn" OpHelp("r[P2]=NULL"), -+ /* 75 */ "Null" OpHelp("r[P2..P3]=NULL"), -+ /* 76 */ "SoftNull" OpHelp("r[P1]=NULL"), -+ /* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), -+ /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1)"), -+ /* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), -+ /* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), -+ /* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"), -+ /* 82 */ "IntCopy" OpHelp("r[P2]=r[P1]"), -+ /* 83 */ "FkCheck" OpHelp(""), -+ /* 84 */ "ResultRow" OpHelp("output=r[P1@P2]"), -+ /* 85 */ "CollSeq" OpHelp(""), -+ /* 86 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), -+ /* 87 */ "RealAffinity" OpHelp(""), -+ /* 88 */ "Cast" OpHelp("affinity(r[P1])"), -+ /* 89 */ "Permutation" OpHelp(""), -+ /* 90 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), -+ /* 91 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), -+ /* 92 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"), -+ /* 93 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), -+ /* 94 */ "Column" OpHelp("r[P3]=PX cursor P1 column P2"), -+ /* 95 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"), -+ /* 96 */ "Affinity" OpHelp("affinity(r[P1@P2])"), -+ /* 97 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), -+ /* 98 */ "Count" OpHelp("r[P2]=count()"), -+ /* 99 */ "ReadCookie" OpHelp(""), -+ /* 100 */ "SetCookie" OpHelp(""), -+ /* 101 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), -+ /* 102 */ "OpenRead" OpHelp("root=P2 iDb=P3"), -+ /* 103 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), -+ /* 104 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), -+ /* 105 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), -+ /* 107 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), -+ /* 108 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), -+ /* 109 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), -+ /* 110 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), -+ /* 111 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), -+ /* 112 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), -+ /* 113 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), -+ /* 114 */ "OpenDup" OpHelp(""), -+ /* 115 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), -+ /* 116 */ "OpenAutoindex" OpHelp("nColumn=P2"), -+ /* 117 */ "OpenEphemeral" OpHelp("nColumn=P2"), -+ /* 118 */ "String8" OpHelp("r[P2]='P4'"), -+ /* 119 */ "SorterOpen" OpHelp(""), -+ /* 120 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), -+ /* 121 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), -+ /* 122 */ "Close" OpHelp(""), -+ /* 123 */ "ColumnsUsed" OpHelp(""), -+ /* 124 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"), -+ /* 125 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"), -+ /* 126 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), -+ /* 127 */ "NewRowid" OpHelp("r[P2]=rowid"), -+ /* 128 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), -+ /* 129 */ "RowCell" OpHelp(""), -+ /* 130 */ "Delete" OpHelp(""), -+ /* 131 */ "ResetCount" OpHelp(""), -+ /* 132 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), -+ /* 133 */ "SorterData" OpHelp("r[P2]=data"), -+ /* 134 */ "RowData" OpHelp("r[P2]=data"), -+ /* 135 */ "Rowid" OpHelp("r[P2]=PX rowid of P1"), -+ /* 136 */ "NullRow" OpHelp(""), -+ /* 137 */ "SeekEnd" OpHelp(""), -+ /* 138 */ "IdxInsert" OpHelp("key=r[P2]"), -+ /* 139 */ "SorterInsert" OpHelp("key=r[P2]"), -+ /* 140 */ "IdxDelete" OpHelp("key=r[P2@P3]"), -+ /* 141 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), -+ /* 142 */ "IdxRowid" OpHelp("r[P2]=rowid"), -+ /* 143 */ "FinishSeek" OpHelp(""), -+ /* 144 */ "Destroy" OpHelp(""), -+ /* 145 */ "Clear" OpHelp(""), -+ /* 146 */ "ResetSorter" OpHelp(""), -+ /* 147 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), -+ /* 148 */ "SqlExec" OpHelp(""), -+ /* 149 */ "ParseSchema" OpHelp(""), -+ /* 150 */ "LoadAnalysis" OpHelp(""), -+ /* 151 */ "DropTable" OpHelp(""), -+ /* 152 */ "DropIndex" OpHelp(""), -+ /* 153 */ "DropTrigger" OpHelp(""), -+ /* 154 */ "Real" OpHelp("r[P2]=P4"), -+ /* 155 */ "IntegrityCk" OpHelp(""), -+ /* 156 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), -+ /* 157 */ "Param" OpHelp(""), -+ /* 158 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), -+ /* 159 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), -+ /* 160 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), -+ /* 161 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), -+ /* 162 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), -+ /* 163 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), -+ /* 164 */ "AggValue" OpHelp("r[P3]=value N=P2"), -+ /* 165 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), -+ /* 166 */ "Expire" OpHelp(""), -+ /* 167 */ "CursorLock" OpHelp(""), -+ /* 168 */ "CursorUnlock" OpHelp(""), -+ /* 169 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), -+ /* 170 */ "VBegin" OpHelp(""), -+ /* 171 */ "VCreate" OpHelp(""), -+ /* 172 */ "VDestroy" OpHelp(""), -+ /* 173 */ "VOpen" OpHelp(""), -+ /* 174 */ "VCheck" OpHelp(""), -+ /* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), -+ /* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), -+ /* 177 */ "VRename" OpHelp(""), -+ /* 178 */ "Pagecount" OpHelp(""), -+ /* 179 */ "MaxPgcnt" OpHelp(""), -+ /* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), -+ /* 181 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"), -+ /* 182 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"), -+ /* 183 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), -+ /* 184 */ "Trace" OpHelp(""), -+ /* 185 */ "CursorHint" OpHelp(""), -+ /* 186 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), -+ /* 187 */ "Noop" OpHelp(""), -+ /* 188 */ "Explain" OpHelp(""), -+ /* 189 */ "Abortable" OpHelp(""), - }; - return azName[i]; - } - #endif - - /************** End of opcodes.c *********************************************/ -+/************** Begin file os_kv.c *******************************************/ -+/* -+** 2022-09-06 -+** -+** 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 contains an experimental VFS layer that operates on a -+** Key/Value storage engine where both keys and values must be pure -+** text. -+*/ -+/* #include */ -+#if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)) -+ -+/***************************************************************************** -+** Debugging logic -+*/ -+ -+/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */ -+#if 0 -+#define SQLITE_KV_TRACE(X) printf X -+#else -+#define SQLITE_KV_TRACE(X) -+#endif -+ -+/* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */ -+#if 0 -+#define SQLITE_KV_LOG(X) printf X -+#else -+#define SQLITE_KV_LOG(X) -+#endif -+ -+ -+/* -+** Forward declaration of objects used by this VFS implementation -+*/ -+typedef struct KVVfsFile KVVfsFile; -+ -+/* A single open file. There are only two files represented by this -+** VFS - the database and the rollback journal. -+*/ -+struct KVVfsFile { -+ sqlite3_file base; /* IO methods */ -+ const char *zClass; /* Storage class */ -+ int isJournal; /* True if this is a journal file */ -+ unsigned int nJrnl; /* Space allocated for aJrnl[] */ -+ char *aJrnl; /* Journal content */ -+ int szPage; /* Last known page size */ -+ sqlite3_int64 szDb; /* Database file size. -1 means unknown */ -+ char *aData; /* Buffer to hold page data */ -+}; -+#define SQLITE_KVOS_SZ 133073 -+ -+/* -+** Methods for KVVfsFile -+*/ -+static int kvvfsClose(sqlite3_file*); -+static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); -+static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); -+static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64); -+static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64); -+static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size); -+static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size); -+static int kvvfsSyncDb(sqlite3_file*, int flags); -+static int kvvfsSyncJrnl(sqlite3_file*, int flags); -+static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize); -+static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize); -+static int kvvfsLock(sqlite3_file*, int); -+static int kvvfsUnlock(sqlite3_file*, int); -+static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut); -+static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg); -+static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg); -+static int kvvfsSectorSize(sqlite3_file*); -+static int kvvfsDeviceCharacteristics(sqlite3_file*); -+ -+/* -+** Methods for sqlite3_vfs -+*/ -+static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); -+static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); -+static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); -+static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); -+static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename); -+static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); -+static int kvvfsSleep(sqlite3_vfs*, int microseconds); -+static int kvvfsCurrentTime(sqlite3_vfs*, double*); -+static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); -+ -+static sqlite3_vfs sqlite3OsKvvfsObject = { -+ 1, /* iVersion */ -+ sizeof(KVVfsFile), /* szOsFile */ -+ 1024, /* mxPathname */ -+ 0, /* pNext */ -+ "kvvfs", /* zName */ -+ 0, /* pAppData */ -+ kvvfsOpen, /* xOpen */ -+ kvvfsDelete, /* xDelete */ -+ kvvfsAccess, /* xAccess */ -+ kvvfsFullPathname, /* xFullPathname */ -+ kvvfsDlOpen, /* xDlOpen */ -+ 0, /* xDlError */ -+ 0, /* xDlSym */ -+ 0, /* xDlClose */ -+ kvvfsRandomness, /* xRandomness */ -+ kvvfsSleep, /* xSleep */ -+ kvvfsCurrentTime, /* xCurrentTime */ -+ 0, /* xGetLastError */ -+ kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ -+}; -+ -+/* Methods for sqlite3_file objects referencing a database file -+*/ -+static sqlite3_io_methods kvvfs_db_io_methods = { -+ 1, /* iVersion */ -+ kvvfsClose, /* xClose */ -+ kvvfsReadDb, /* xRead */ -+ kvvfsWriteDb, /* xWrite */ -+ kvvfsTruncateDb, /* xTruncate */ -+ kvvfsSyncDb, /* xSync */ -+ kvvfsFileSizeDb, /* xFileSize */ -+ kvvfsLock, /* xLock */ -+ kvvfsUnlock, /* xUnlock */ -+ kvvfsCheckReservedLock, /* xCheckReservedLock */ -+ kvvfsFileControlDb, /* xFileControl */ -+ kvvfsSectorSize, /* xSectorSize */ -+ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ -+ 0, /* xShmMap */ -+ 0, /* xShmLock */ -+ 0, /* xShmBarrier */ -+ 0, /* xShmUnmap */ -+ 0, /* xFetch */ -+ 0 /* xUnfetch */ -+}; -+ -+/* Methods for sqlite3_file objects referencing a rollback journal -+*/ -+static sqlite3_io_methods kvvfs_jrnl_io_methods = { -+ 1, /* iVersion */ -+ kvvfsClose, /* xClose */ -+ kvvfsReadJrnl, /* xRead */ -+ kvvfsWriteJrnl, /* xWrite */ -+ kvvfsTruncateJrnl, /* xTruncate */ -+ kvvfsSyncJrnl, /* xSync */ -+ kvvfsFileSizeJrnl, /* xFileSize */ -+ kvvfsLock, /* xLock */ -+ kvvfsUnlock, /* xUnlock */ -+ kvvfsCheckReservedLock, /* xCheckReservedLock */ -+ kvvfsFileControlJrnl, /* xFileControl */ -+ kvvfsSectorSize, /* xSectorSize */ -+ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ -+ 0, /* xShmMap */ -+ 0, /* xShmLock */ -+ 0, /* xShmBarrier */ -+ 0, /* xShmUnmap */ -+ 0, /* xFetch */ -+ 0 /* xUnfetch */ -+}; -+ -+/****** Storage subsystem **************************************************/ -+#include -+#include -+#include -+ -+/* Forward declarations for the low-level storage engine -+*/ -+static int kvstorageWrite(const char*, const char *zKey, const char *zData); -+static int kvstorageDelete(const char*, const char *zKey); -+static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); -+#define KVSTORAGE_KEY_SZ 32 -+ -+/* Expand the key name with an appropriate prefix and put the result -+** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least -+** KVSTORAGE_KEY_SZ bytes. -+*/ -+static void kvstorageMakeKey( -+ const char *zClass, -+ const char *zKeyIn, -+ char *zKeyOut -+){ -+ sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); -+} -+ -+/* Write content into a key. zClass is the particular namespace of the -+** underlying key/value store to use - either "local" or "session". -+** -+** Both zKey and zData are zero-terminated pure text strings. -+** -+** Return the number of errors. -+*/ -+static int kvstorageWrite( -+ const char *zClass, -+ const char *zKey, -+ const char *zData -+){ -+ FILE *fd; -+ char zXKey[KVSTORAGE_KEY_SZ]; -+ kvstorageMakeKey(zClass, zKey, zXKey); -+ fd = fopen(zXKey, "wb"); -+ if( fd ){ -+ SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, -+ (int)strlen(zData), zData, -+ strlen(zData)>50 ? "..." : "")); -+ fputs(zData, fd); -+ fclose(fd); -+ return 0; -+ }else{ -+ return 1; -+ } -+} -+ -+/* Delete a key (with its corresponding data) from the key/value -+** namespace given by zClass. If the key does not previously exist, -+** this routine is a no-op. -+*/ -+static int kvstorageDelete(const char *zClass, const char *zKey){ -+ char zXKey[KVSTORAGE_KEY_SZ]; -+ kvstorageMakeKey(zClass, zKey, zXKey); -+ unlink(zXKey); -+ SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); -+ return 0; -+} -+ -+/* Read the value associated with a zKey from the key/value namespace given -+** by zClass and put the text data associated with that key in the first -+** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large -+** enough to hold it all. The value put into zBuf must always be zero -+** terminated, even if it gets truncated because nBuf is not large enough. -+** -+** Return the total number of bytes in the data, without truncation, and -+** not counting the final zero terminator. Return -1 if the key does -+** not exist. -+** -+** If nBuf<=0 then this routine simply returns the size of the data without -+** actually reading it. -+*/ -+static int kvstorageRead( -+ const char *zClass, -+ const char *zKey, -+ char *zBuf, -+ int nBuf -+){ -+ FILE *fd; -+ struct stat buf; -+ char zXKey[KVSTORAGE_KEY_SZ]; -+ kvstorageMakeKey(zClass, zKey, zXKey); -+ if( access(zXKey, R_OK)!=0 -+ || stat(zXKey, &buf)!=0 -+ || !S_ISREG(buf.st_mode) -+ ){ -+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); -+ return -1; -+ } -+ if( nBuf<=0 ){ -+ return (int)buf.st_size; -+ }else if( nBuf==1 ){ -+ zBuf[0] = 0; -+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey, -+ (int)buf.st_size)); -+ return (int)buf.st_size; -+ } -+ if( nBuf > buf.st_size + 1 ){ -+ nBuf = buf.st_size + 1; -+ } -+ fd = fopen(zXKey, "rb"); -+ if( fd==0 ){ -+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); -+ return -1; -+ }else{ -+ sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd); -+ fclose(fd); -+ zBuf[n] = 0; -+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey, -+ n, zBuf, n>50 ? "..." : "")); -+ return (int)n; -+ } -+} -+ -+/* -+** An internal level of indirection which enables us to replace the -+** kvvfs i/o methods with JavaScript implementations in WASM builds. -+** Maintenance reminder: if this struct changes in any way, the JSON -+** rendering of its structure must be updated in -+** sqlite3_wasm_enum_json(). There are no binary compatibility -+** concerns, so it does not need an iVersion member. This file is -+** necessarily always compiled together with sqlite3_wasm_enum_json(), -+** and JS code dynamically creates the mapping of members based on -+** that JSON description. -+*/ -+typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods; -+struct sqlite3_kvvfs_methods { -+ int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf); -+ int (*xWrite)(const char *zClass, const char *zKey, const char *zData); -+ int (*xDelete)(const char *zClass, const char *zKey); -+ const int nKeySize; -+}; -+ -+/* -+** This object holds the kvvfs I/O methods which may be swapped out -+** for JavaScript-side implementations in WASM builds. In such builds -+** it cannot be const, but in native builds it should be so that -+** the compiler can hopefully optimize this level of indirection out. -+** That said, kvvfs is intended primarily for use in WASM builds. -+** -+** Note that this is not explicitly flagged as static because the -+** amalgamation build will tag it with SQLITE_PRIVATE. -+*/ -+#ifndef SQLITE_WASM -+const -+#endif -+SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = { -+kvstorageRead, -+kvstorageWrite, -+kvstorageDelete, -+KVSTORAGE_KEY_SZ -+}; -+ -+/****** Utility subroutines ************************************************/ -+ -+/* -+** Encode binary into the text encoded used to persist on disk. -+** The output text is stored in aOut[], which must be at least -+** nData+1 bytes in length. -+** -+** Return the actual length of the encoded text, not counting the -+** zero terminator at the end. -+** -+** Encoding format -+** --------------- -+** -+** * Non-zero bytes are encoded as upper-case hexadecimal -+** -+** * A sequence of one or more zero-bytes that are not at the -+** beginning of the buffer are encoded as a little-endian -+** base-26 number using a..z. "a" means 0. "b" means 1, -+** "z" means 25. "ab" means 26. "ac" means 52. And so forth. -+** -+** * Because there is no overlap between the encoding characters -+** of hexadecimal and base-26 numbers, it is always clear where -+** one stops and the next begins. -+*/ -+static int kvvfsEncode(const char *aData, int nData, char *aOut){ -+ int i, j; -+ const unsigned char *a = (const unsigned char*)aData; -+ for(i=j=0; i>4]; -+ aOut[j++] = "0123456789ABCDEF"[c&0xf]; -+ }else{ -+ /* A sequence of 1 or more zeros is stored as a little-endian -+ ** base-26 number using a..z as the digits. So one zero is "b". -+ ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", -+ ** and so forth. -+ */ -+ int k; -+ for(k=1; i+k0 ){ -+ aOut[j++] = 'a'+(k%26); -+ k /= 26; -+ } -+ } -+ } -+ aOut[j] = 0; -+ return j; -+} -+ -+static const signed char kvvfsHexValue[256] = { -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 -+}; -+ -+/* -+** Decode the text encoding back to binary. The binary content is -+** written into pOut, which must be at least nOut bytes in length. -+** -+** The return value is the number of bytes actually written into aOut[]. -+*/ -+static int kvvfsDecode(const char *a, char *aOut, int nOut){ -+ int i, j; -+ int c; -+ const unsigned char *aIn = (const unsigned char*)a; -+ i = 0; -+ j = 0; -+ while( 1 ){ -+ c = kvvfsHexValue[aIn[i]]; -+ if( c<0 ){ -+ int n = 0; -+ int mult = 1; -+ c = aIn[i]; -+ if( c==0 ) break; -+ while( c>='a' && c<='z' ){ -+ n += (c - 'a')*mult; -+ mult *= 26; -+ c = aIn[++i]; -+ } -+ if( j+n>nOut ) return -1; -+ memset(&aOut[j], 0, n); -+ j += n; -+ if( c==0 || mult==1 ) break; /* progress stalled if mult==1 */ -+ }else{ -+ aOut[j] = c<<4; -+ c = kvvfsHexValue[aIn[++i]]; -+ if( c<0 ) break; -+ aOut[j++] += c; -+ i++; -+ } -+ } -+ return j; -+} -+ -+/* -+** Decode a complete journal file. Allocate space in pFile->aJrnl -+** and store the decoding there. Or leave pFile->aJrnl set to NULL -+** if an error is encountered. -+** -+** The first few characters of the text encoding will be a little-endian -+** base-26 number (digits a..z) that is the total number of bytes -+** in the decoded journal file image. This base-26 number is followed -+** by a single space, then the encoding of the journal. The space -+** separator is required to act as a terminator for the base-26 number. -+*/ -+static void kvvfsDecodeJournal( -+ KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ -+ const char *zTxt, /* Text encoding. Zero-terminated */ -+ int nTxt /* Bytes in zTxt, excluding zero terminator */ -+){ -+ unsigned int n = 0; -+ int c, i, mult; -+ i = 0; -+ mult = 1; -+ while( (c = zTxt[i++])>='a' && c<='z' ){ -+ n += (zTxt[i] - 'a')*mult; -+ mult *= 26; -+ } -+ sqlite3_free(pFile->aJrnl); -+ pFile->aJrnl = sqlite3_malloc64( n ); -+ if( pFile->aJrnl==0 ){ -+ pFile->nJrnl = 0; -+ return; -+ } -+ pFile->nJrnl = n; -+ n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); -+ if( nnJrnl ){ -+ sqlite3_free(pFile->aJrnl); -+ pFile->aJrnl = 0; -+ pFile->nJrnl = 0; -+ } -+} -+ -+/* -+** Read or write the "sz" element, containing the database file size. -+*/ -+static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ -+ char zData[50]; -+ zData[0] = 0; -+ sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1); -+ return strtoll(zData, 0, 0); -+} -+static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ -+ char zData[50]; -+ sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); -+ return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData); -+} -+ -+/****** sqlite3_io_methods methods ******************************************/ -+ -+/* -+** Close an kvvfs-file. -+*/ -+static int kvvfsClose(sqlite3_file *pProtoFile){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ -+ SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, -+ pFile->isJournal ? "journal" : "db")); -+ sqlite3_free(pFile->aJrnl); -+ sqlite3_free(pFile->aData); -+ return SQLITE_OK; -+} -+ -+/* -+** Read from the -journal file. -+*/ -+static int kvvfsReadJrnl( -+ sqlite3_file *pProtoFile, -+ void *zBuf, -+ int iAmt, -+ sqlite_int64 iOfst -+){ -+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; -+ assert( pFile->isJournal ); -+ SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); -+ if( pFile->aJrnl==0 ){ -+ int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); -+ char *aTxt; -+ if( szTxt<=4 ){ -+ return SQLITE_IOERR; -+ } -+ aTxt = sqlite3_malloc64( szTxt+1 ); -+ if( aTxt==0 ) return SQLITE_NOMEM; -+ kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); -+ kvvfsDecodeJournal(pFile, aTxt, szTxt); -+ sqlite3_free(aTxt); -+ if( pFile->aJrnl==0 ) return SQLITE_IOERR; -+ } -+ if( iOfst+iAmt>pFile->nJrnl ){ -+ return SQLITE_IOERR_SHORT_READ; -+ } -+ memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); -+ return SQLITE_OK; -+} -+ -+/* -+** Read from the database file. -+*/ -+static int kvvfsReadDb( -+ sqlite3_file *pProtoFile, -+ void *zBuf, -+ int iAmt, -+ sqlite_int64 iOfst -+){ -+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; -+ unsigned int pgno; -+ int got, n; -+ char zKey[30]; -+ char *aData = pFile->aData; -+ assert( iOfst>=0 ); -+ assert( iAmt>=0 ); -+ SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); -+ if( iOfst+iAmt>=512 ){ -+ if( (iOfst % iAmt)!=0 ){ -+ return SQLITE_IOERR_READ; -+ } -+ if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ -+ return SQLITE_IOERR_READ; -+ } -+ pFile->szPage = iAmt; -+ pgno = 1 + iOfst/iAmt; -+ }else{ -+ pgno = 1; -+ } -+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); -+ got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, -+ aData, SQLITE_KVOS_SZ-1); -+ if( got<0 ){ -+ n = 0; -+ }else{ -+ aData[got] = 0; -+ if( iOfst+iAmt<512 ){ -+ int k = iOfst+iAmt; -+ aData[k*2] = 0; -+ n = kvvfsDecode(aData, &aData[2000], SQLITE_KVOS_SZ-2000); -+ if( n>=iOfst+iAmt ){ -+ memcpy(zBuf, &aData[2000+iOfst], iAmt); -+ n = iAmt; -+ }else{ -+ n = 0; -+ } -+ }else{ -+ n = kvvfsDecode(aData, zBuf, iAmt); -+ } -+ } -+ if( nzClass, iAmt, iOfst)); -+ if( iEnd>=0x10000000 ) return SQLITE_FULL; -+ if( pFile->aJrnl==0 || pFile->nJrnlaJrnl, iEnd); -+ if( aNew==0 ){ -+ return SQLITE_IOERR_NOMEM; -+ } -+ pFile->aJrnl = aNew; -+ if( pFile->nJrnlaJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); -+ } -+ pFile->nJrnl = iEnd; -+ } -+ memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); -+ return SQLITE_OK; -+} -+ -+/* -+** Write into the database file. -+*/ -+static int kvvfsWriteDb( -+ sqlite3_file *pProtoFile, -+ const void *zBuf, -+ int iAmt, -+ sqlite_int64 iOfst -+){ -+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; -+ unsigned int pgno; -+ char zKey[30]; -+ char *aData = pFile->aData; -+ SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); -+ assert( iAmt>=512 && iAmt<=65536 ); -+ assert( (iAmt & (iAmt-1))==0 ); -+ assert( pFile->szPage<0 || pFile->szPage==iAmt ); -+ pFile->szPage = iAmt; -+ pgno = 1 + iOfst/iAmt; -+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); -+ kvvfsEncode(zBuf, iAmt, aData); -+ if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){ -+ return SQLITE_IOERR; -+ } -+ if( iOfst+iAmt > pFile->szDb ){ -+ pFile->szDb = iOfst + iAmt; -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Truncate an kvvfs-file. -+*/ -+static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); -+ assert( size==0 ); -+ sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl"); -+ sqlite3_free(pFile->aJrnl); -+ pFile->aJrnl = 0; -+ pFile->nJrnl = 0; -+ return SQLITE_OK; -+} -+static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ if( pFile->szDb>size -+ && pFile->szPage>0 -+ && (size % pFile->szPage)==0 -+ ){ -+ char zKey[50]; -+ unsigned int pgno, pgnoMax; -+ SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); -+ pgno = 1 + size/pFile->szPage; -+ pgnoMax = 2 + pFile->szDb/pFile->szPage; -+ while( pgno<=pgnoMax ){ -+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); -+ sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey); -+ pgno++; -+ } -+ pFile->szDb = size; -+ return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; -+ } -+ return SQLITE_IOERR; -+} -+ -+/* -+** Sync an kvvfs-file. -+*/ -+static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ -+ int i, n; -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ char *zOut; -+ SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); -+ if( pFile->nJrnl<=0 ){ -+ return kvvfsTruncateJrnl(pProtoFile, 0); -+ } -+ zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); -+ if( zOut==0 ){ -+ return SQLITE_IOERR_NOMEM; -+ } -+ n = pFile->nJrnl; -+ i = 0; -+ do{ -+ zOut[i++] = 'a' + (n%26); -+ n /= 26; -+ }while( n>0 ); -+ zOut[i++] = ' '; -+ kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); -+ i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut); -+ sqlite3_free(zOut); -+ return i ? SQLITE_IOERR : SQLITE_OK; -+} -+static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ -+ return SQLITE_OK; -+} -+ -+/* -+** Return the current file-size of an kvvfs-file. -+*/ -+static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); -+ *pSize = pFile->nJrnl; -+ return SQLITE_OK; -+} -+static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); -+ if( pFile->szDb>=0 ){ -+ *pSize = pFile->szDb; -+ }else{ -+ *pSize = kvvfsReadFileSize(pFile); -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Lock an kvvfs-file. -+*/ -+static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ assert( !pFile->isJournal ); -+ SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); -+ -+ if( eLock!=SQLITE_LOCK_NONE ){ -+ pFile->szDb = kvvfsReadFileSize(pFile); -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Unlock an kvvfs-file. -+*/ -+static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ assert( !pFile->isJournal ); -+ SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); -+ if( eLock==SQLITE_LOCK_NONE ){ -+ pFile->szDb = -1; -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Check if another file-handle holds a RESERVED lock on an kvvfs-file. -+*/ -+static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ -+ SQLITE_KV_LOG(("xCheckReservedLock\n")); -+ *pResOut = 0; -+ return SQLITE_OK; -+} -+ -+/* -+** File control method. For custom operations on an kvvfs-file. -+*/ -+static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){ -+ SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op)); -+ return SQLITE_NOTFOUND; -+} -+static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){ -+ SQLITE_KV_LOG(("xFileControl(%d) on database\n", op)); -+ if( op==SQLITE_FCNTL_SYNC ){ -+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; -+ int rc = SQLITE_OK; -+ SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); -+ if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ -+ rc = SQLITE_IOERR; -+ } -+ return rc; -+ } -+ return SQLITE_NOTFOUND; -+} -+ -+/* -+** Return the sector-size in bytes for an kvvfs-file. -+*/ -+static int kvvfsSectorSize(sqlite3_file *pFile){ -+ return 512; -+} -+ -+/* -+** Return the device characteristic flags supported by an kvvfs-file. -+*/ -+static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ -+ return 0; -+} -+ -+/****** sqlite3_vfs methods *************************************************/ -+ -+/* -+** Open an kvvfs file handle. -+*/ -+static int kvvfsOpen( -+ sqlite3_vfs *pProtoVfs, -+ const char *zName, -+ sqlite3_file *pProtoFile, -+ int flags, -+ int *pOutFlags -+){ -+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile; -+ if( zName==0 ) zName = ""; -+ SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); -+ if( strcmp(zName, "local")==0 -+ || strcmp(zName, "session")==0 -+ ){ -+ pFile->isJournal = 0; -+ pFile->base.pMethods = &kvvfs_db_io_methods; -+ }else -+ if( strcmp(zName, "local-journal")==0 -+ || strcmp(zName, "session-journal")==0 -+ ){ -+ pFile->isJournal = 1; -+ pFile->base.pMethods = &kvvfs_jrnl_io_methods; -+ }else{ -+ return SQLITE_CANTOPEN; -+ } -+ if( zName[0]=='s' ){ -+ pFile->zClass = "session"; -+ }else{ -+ pFile->zClass = "local"; -+ } -+ pFile->aData = sqlite3_malloc64(SQLITE_KVOS_SZ); -+ if( pFile->aData==0 ){ -+ return SQLITE_NOMEM; -+ } -+ pFile->aJrnl = 0; -+ pFile->nJrnl = 0; -+ pFile->szPage = -1; -+ pFile->szDb = -1; -+ return SQLITE_OK; -+} -+ -+/* -+** Delete the file located at zPath. If the dirSync argument is true, -+** ensure the file-system modifications are synced to disk before -+** returning. -+*/ -+static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ -+ if( strcmp(zPath, "local-journal")==0 ){ -+ sqlite3KvvfsMethods.xDelete("local", "jrnl"); -+ }else -+ if( strcmp(zPath, "session-journal")==0 ){ -+ sqlite3KvvfsMethods.xDelete("session", "jrnl"); -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Test for access permissions. Return true if the requested permission -+** is available, or false otherwise. -+*/ -+static int kvvfsAccess( -+ sqlite3_vfs *pProtoVfs, -+ const char *zPath, -+ int flags, -+ int *pResOut -+){ -+ SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); -+ if( strcmp(zPath, "local-journal")==0 ){ -+ *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0; -+ }else -+ if( strcmp(zPath, "session-journal")==0 ){ -+ *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0; -+ }else -+ if( strcmp(zPath, "local")==0 ){ -+ *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0; -+ }else -+ if( strcmp(zPath, "session")==0 ){ -+ *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0; -+ }else -+ { -+ *pResOut = 0; -+ } -+ SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); -+ return SQLITE_OK; -+} -+ -+/* -+** Populate buffer zOut with the full canonical pathname corresponding -+** to the pathname in zPath. zOut is guaranteed to point to a buffer -+** of at least (INST_MAX_PATHNAME+1) bytes. -+*/ -+static int kvvfsFullPathname( -+ sqlite3_vfs *pVfs, -+ const char *zPath, -+ int nOut, -+ char *zOut -+){ -+ size_t nPath; -+#ifdef SQLITE_OS_KV_ALWAYS_LOCAL -+ zPath = "local"; -+#endif -+ nPath = strlen(zPath); -+ SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); -+ if( nOut -+static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ -+ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; -+ struct timeval sNow; -+ (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ -+ *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; -+ return SQLITE_OK; -+} -+#endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */ -+ -+#if SQLITE_OS_KV -+/* -+** This routine is called initialize the KV-vfs as the default VFS. -+*/ -+SQLITE_API int sqlite3_os_init(void){ -+ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1); -+} -+SQLITE_API int sqlite3_os_end(void){ -+ return SQLITE_OK; -+} -+#endif /* SQLITE_OS_KV */ -+ -+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) -+SQLITE_PRIVATE int sqlite3KvvfsInit(void){ -+ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0); -+} -+#endif -+ -+/************** End of os_kv.c ***********************************************/ - /************** Begin file os_unix.c *****************************************/ - /* - ** 2004 May 22 -@@ -33372,7 +38865,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ - ** This source file is organized into divisions where the logic for various - ** subfunctions is contained within the appropriate division. PLEASE - ** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed --** in the correct division and should be clearly labeled. -+** in the correct division and should be clearly labelled. - ** - ** The layout of divisions is as follows: - ** -@@ -33422,7 +38915,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ - #endif - - /* Use pread() and pwrite() if they are available */ --#if defined(__APPLE__) -+#if defined(__APPLE__) || defined(__linux__) - # define HAVE_PREAD 1 - # define HAVE_PWRITE 1 - #endif -@@ -33437,15 +38930,16 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ - /* - ** standard include files. - */ --#include --#include -+#include /* amalgamator: keep */ -+#include /* amalgamator: keep */ - #include - #include --#include -+#include /* amalgamator: keep */ - /* #include */ --#include -+#include /* amalgamator: keep */ - #include --#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 -+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ -+ && !defined(SQLITE_WASI) - # include - #endif - -@@ -33472,7 +38966,8 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ - # if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ - (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) - # if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \ -- && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0)) -+ && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))\ -+ && (!defined(TARGET_OS_MACCATALYST) || (TARGET_OS_MACCATALYST==0)) - # undef HAVE_GETHOSTUUID - # define HAVE_GETHOSTUUID 1 - # else -@@ -33532,9 +39027,46 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ - */ - #define SQLITE_MAX_SYMLINKS 100 - -+/* -+** Remove and stub certain info for WASI (WebAssembly System -+** Interface) builds. -+*/ -+#ifdef SQLITE_WASI -+# undef HAVE_FCHMOD -+# undef HAVE_FCHOWN -+# undef HAVE_MREMAP -+# define HAVE_MREMAP 0 -+# ifndef SQLITE_DEFAULT_UNIX_VFS -+# define SQLITE_DEFAULT_UNIX_VFS "unix-dotfile" -+ /* ^^^ should SQLITE_DEFAULT_UNIX_VFS be "unix-none"? */ -+# endif -+# ifndef F_RDLCK -+# define F_RDLCK 0 -+# define F_WRLCK 1 -+# define F_UNLCK 2 -+# if __LONG_MAX == 0x7fffffffL -+# define F_GETLK 12 -+# define F_SETLK 13 -+# define F_SETLKW 14 -+# else -+# define F_GETLK 5 -+# define F_SETLK 6 -+# define F_SETLKW 7 -+# endif -+# endif -+#else /* !SQLITE_WASI */ -+# ifndef HAVE_FCHMOD -+# define HAVE_FCHMOD 1 -+# endif -+#endif /* SQLITE_WASI */ -+ -+#ifdef SQLITE_WASI -+# define osGetpid(X) (pid_t)1 -+#else - /* Always cast the getpid() return type for compatibility with - ** kernel modules in VxWorks. */ --#define osGetpid(X) (pid_t)getpid() -+# define osGetpid(X) (pid_t)getpid() -+#endif - - /* - ** Only set the lastErrno if the error code is a real error and not -@@ -33595,6 +39127,7 @@ struct unixFile { - #endif - #ifdef SQLITE_ENABLE_SETLK_TIMEOUT - unsigned iBusyTimeout; /* Wait this many millisec on locks */ -+ int bBlockOnConnect; /* True to block for SHARED locks */ - #endif - #if OS_VXWORKS - struct vxworksFileId *pId; /* Unique file ID */ -@@ -33633,7 +39166,7 @@ static pid_t randomnessPid = 0; - #define UNIXFILE_EXCL 0x01 /* Connections from one process only */ - #define UNIXFILE_RDONLY 0x02 /* Connection is read only */ - #define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ --#ifndef SQLITE_DISABLE_DIRSYNC -+#if !defined(SQLITE_DISABLE_DIRSYNC) && !defined(_AIX) - # define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ - #else - # define UNIXFILE_DIRSYNC 0x00 -@@ -33646,205 +39179,7 @@ static pid_t randomnessPid = 0; - /* - ** Include code that is common to all os_*.c files - */ --/************** Include os_common.h in the middle of os_unix.c ***************/ --/************** Begin file os_common.h ***************************************/ --/* --** 2004 May 22 --** --** 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 contains macros and a little bit of code that is common to --** all of the platform-specific files (os_*.c) and is #included into those --** files. --** --** This file should be #included by the os_*.c files only. It is not a --** general purpose header file. --*/ --#ifndef _OS_COMMON_H_ --#define _OS_COMMON_H_ -- --/* --** At least two bugs have slipped in because we changed the MEMORY_DEBUG --** macro to SQLITE_DEBUG and some older makefiles have not yet made the --** switch. The following code should catch this problem at compile-time. --*/ --#ifdef MEMORY_DEBUG --# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." --#endif -- --/* --** Macros for performance tracing. Normally turned off. Only works --** on i486 hardware. --*/ --#ifdef SQLITE_PERFORMANCE_TRACE -- --/* --** hwtime.h contains inline assembler code for implementing --** high-performance timing routines. --*/ --/************** Include hwtime.h in the middle of os_common.h ****************/ --/************** Begin file hwtime.h ******************************************/ --/* --** 2008 May 27 --** --** 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 contains inline asm code for retrieving "high-performance" --** counters for x86 and x86_64 class CPUs. --*/ --#ifndef SQLITE_HWTIME_H --#define SQLITE_HWTIME_H -- --/* --** The following routine only works on pentium-class (or newer) processors. --** It uses the RDTSC opcode to read the cycle count value out of the --** processor and returns that value. This can be used for high-res --** profiling. --*/ --#if !defined(__STRICT_ANSI__) && \ -- (defined(__GNUC__) || defined(_MSC_VER)) && \ -- (defined(i386) || defined(__i386__) || defined(_M_IX86)) -- -- #if defined(__GNUC__) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned int lo, hi; -- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); -- return (sqlite_uint64)hi << 32 | lo; -- } -- -- #elif defined(_MSC_VER) -- -- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ -- __asm { -- rdtsc -- ret ; return value at EDX:EAX -- } -- } -- -- #endif -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long val; -- __asm__ __volatile__ ("rdtsc" : "=A" (val)); -- return val; -- } -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long long retval; -- unsigned long junk; -- __asm__ __volatile__ ("\n\ -- 1: mftbu %1\n\ -- mftb %L0\n\ -- mftbu %0\n\ -- cmpw %0,%1\n\ -- bne 1b" -- : "=r" (retval), "=r" (junk)); -- return retval; -- } -- --#else -- -- /* -- ** asm() is needed for hardware timing support. Without asm(), -- ** disable the sqlite3Hwtime() routine. -- ** -- ** sqlite3Hwtime() is only used for some obscure debugging -- ** and analysis configurations, not in any deliverable, so this -- ** should not be a great loss. -- */ --SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } -- --#endif -- --#endif /* !defined(SQLITE_HWTIME_H) */ -- --/************** End of hwtime.h **********************************************/ --/************** Continuing where we left off in os_common.h ******************/ -- --static sqlite_uint64 g_start; --static sqlite_uint64 g_elapsed; --#define TIMER_START g_start=sqlite3Hwtime() --#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start --#define TIMER_ELAPSED g_elapsed --#else --#define TIMER_START --#define TIMER_END --#define TIMER_ELAPSED ((sqlite_uint64)0) --#endif -- --/* --** If we compile with the SQLITE_TEST macro set, then the following block --** of code will give us the ability to simulate a disk I/O error. This --** is used for testing the I/O recovery logic. --*/ --#if defined(SQLITE_TEST) --SQLITE_API extern int sqlite3_io_error_hit; --SQLITE_API extern int sqlite3_io_error_hardhit; --SQLITE_API extern int sqlite3_io_error_pending; --SQLITE_API extern int sqlite3_io_error_persist; --SQLITE_API extern int sqlite3_io_error_benign; --SQLITE_API extern int sqlite3_diskfull_pending; --SQLITE_API extern int sqlite3_diskfull; --#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) --#define SimulateIOError(CODE) \ -- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ -- || sqlite3_io_error_pending-- == 1 ) \ -- { local_ioerr(); CODE; } --static void local_ioerr(){ -- IOTRACE(("IOERR\n")); -- sqlite3_io_error_hit++; -- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; --} --#define SimulateDiskfullError(CODE) \ -- if( sqlite3_diskfull_pending ){ \ -- if( sqlite3_diskfull_pending == 1 ){ \ -- local_ioerr(); \ -- sqlite3_diskfull = 1; \ -- sqlite3_io_error_hit = 1; \ -- CODE; \ -- }else{ \ -- sqlite3_diskfull_pending--; \ -- } \ -- } --#else --#define SimulateIOErrorBenign(X) --#define SimulateIOError(A) --#define SimulateDiskfullError(A) --#endif /* defined(SQLITE_TEST) */ -- --/* --** When testing, keep a count of the number of open files. --*/ --#if defined(SQLITE_TEST) --SQLITE_API extern int sqlite3_open_file_count; --#define OpenCounter(X) sqlite3_open_file_count+=(X) --#else --#define OpenCounter(X) --#endif /* defined(SQLITE_TEST) */ -- --#endif /* !defined(_OS_COMMON_H_) */ -- --/************** End of os_common.h *******************************************/ --/************** Continuing where we left off in os_unix.c ********************/ -+/* #include "os_common.h" */ - - /* - ** Define various macros that are missing from some systems. -@@ -34004,7 +39339,11 @@ static struct unix_syscall { - #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\ - aSyscall[13].pCurrent) - -+#if defined(HAVE_FCHMOD) - { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, -+#else -+ { "fchmod", (sqlite3_syscall_ptr)0, 0 }, -+#endif - #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) - - #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE -@@ -34040,14 +39379,16 @@ static struct unix_syscall { - #endif - #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) - --#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 -+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ -+ && !defined(SQLITE_WASI) - { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, - #else - { "mmap", (sqlite3_syscall_ptr)0, 0 }, - #endif - #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) - --#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 -+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ -+ && !defined(SQLITE_WASI) - { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, - #else - { "munmap", (sqlite3_syscall_ptr)0, 0 }, -@@ -34112,7 +39453,7 @@ static int robustFchown(int fd, uid_t uid, gid_t gid){ - - /* - ** This is the xSetSystemCall() method of sqlite3_vfs for all of the --** "unix" VFSes. Return SQLITE_OK opon successfully updating the -+** "unix" VFSes. Return SQLITE_OK upon successfully updating the - ** system call pointer, or SQLITE_NOTFOUND if there is no configurable - ** system call named zName. - */ -@@ -34233,6 +39574,9 @@ static int robust_open(const char *z, int f, mode_t m){ - break; - } - if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; -+ if( (f & (O_EXCL|O_CREAT))==(O_EXCL|O_CREAT) ){ -+ (void)osUnlink(z); -+ } - osClose(fd); - sqlite3_log(SQLITE_WARNING, - "attempt to open \"%s\" as file descriptor %d", z, fd); -@@ -34631,7 +39975,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ - ** If you close a file descriptor that points to a file that has locks, - ** all locks on that file that are owned by the current process are - ** released. To work around this problem, each unixInodeInfo object --** maintains a count of the number of pending locks on tha inode. -+** maintains a count of the number of pending locks on the inode. - ** When an attempt is made to close an unixFile, if there are - ** other unixFile open on the same inode that are holding locks, the call - ** to close() the file descriptor is deferred until all of the locks clear. -@@ -34645,7 +39989,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ - ** not posix compliant. Under LinuxThreads, a lock created by thread - ** A cannot be modified or overridden by a different thread B. - ** Only thread A can modify the lock. Locking behavior is correct --** if the appliation uses the newer Native Posix Thread Library (NPTL) -+** if the application uses the newer Native Posix Thread Library (NPTL) - ** on linux - with NPTL a lock created by thread A can override locks - ** in thread B. But there is no way to know at compile-time which - ** threading library is being used. So there is no way to know at -@@ -34795,8 +40139,12 @@ static int unixLogErrorAtLine( - ** available, the error message will often be an empty string. Not a - ** huge problem. Incorrectly concluding that the GNU version is available - ** could lead to a segfault though. -+ ** -+ ** Forum post 3f13857fa4062301 reports that the Android SDK may use -+ ** int-type return, depending on its version. - */ --#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) -+#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \ -+ && !defined(ANDROID) && !defined(__ANDROID__) - zErr = - # endif - strerror_r(iErrno, aErr, sizeof(aErr)-1); -@@ -34847,7 +40195,7 @@ static void storeLastErrno(unixFile *pFile, int error){ - } - - /* --** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. -+** Close all file descriptors accumulated in the unixInodeInfo->pUnused list. - */ - static void closePendingFds(unixFile *pFile){ - unixInodeInfo *pInode = pFile->pInode; -@@ -35092,6 +40440,9 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ - return rc; - } - -+/* Forward declaration*/ -+static int unixSleep(sqlite3_vfs*,int); -+ - /* - ** Set a posix-advisory-lock. - ** -@@ -35121,7 +40472,7 @@ static int osSetPosixAdvisoryLock( - ** generic posix, however, there is no such API. So we simply try the - ** lock once every millisecond until either the timeout expires, or until - ** the lock is obtained. */ -- usleep(1000); -+ unixSleep(0,1000); - rc = osFcntl(h,F_SETLK,pLock); - tm--; - } -@@ -35157,7 +40508,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ - if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ - if( pInode->bProcessLock==0 ){ - struct flock lock; -- assert( pInode->nLock==0 ); -+ /* assert( pInode->nLock==0 ); <-- Not true if unix-excl READONLY used */ - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; -@@ -35170,6 +40521,13 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ - rc = 0; - } - }else{ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ if( pFile->bBlockOnConnect && pLock->l_type==F_RDLCK -+ && pLock->l_start==SHARED_FIRST && pLock->l_len==SHARED_SIZE -+ ){ -+ rc = osFcntl(pFile->h, F_SETLKW, pLock); -+ }else -+#endif - rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); - } - return rc; -@@ -35192,7 +40550,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ - ** - ** UNLOCKED -> SHARED - ** SHARED -> RESERVED --** SHARED -> (PENDING) -> EXCLUSIVE -+** SHARED -> EXCLUSIVE - ** RESERVED -> (PENDING) -> EXCLUSIVE - ** PENDING -> EXCLUSIVE - ** -@@ -35207,7 +40565,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ - ** slightly in order to be compatible with Windows95 systems simultaneously - ** accessing the same database file, in case that is ever required. - ** -- ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved -+ ** Symbols defined in os.h identify the 'pending byte' and the 'reserved - ** byte', each single bytes at well known offsets, and the 'shared byte - ** range', a range of 510 bytes at a well known offset. - ** -@@ -35215,7 +40573,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ - ** byte'. If this is successful, 'shared byte range' is read-locked - ** and the lock on the 'pending byte' released. (Legacy note: When - ** SQLite was first developed, Windows95 systems were still very common, -- ** and Widnows95 lacks a shared-lock capability. So on Windows95, a -+ ** and Windows95 lacks a shared-lock capability. So on Windows95, a - ** single randomly selected by from the 'shared byte range' is locked. - ** Windows95 is now pretty much extinct, but this work-around for the - ** lack of shared-locks on Windows95 lives on, for backwards -@@ -35225,19 +40583,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){ - ** A RESERVED lock is implemented by grabbing a write-lock on the - ** 'reserved byte'. - ** -- ** A process may only obtain a PENDING lock after it has obtained a -- ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock -- ** on the 'pending byte'. This ensures that no new SHARED locks can be -- ** obtained, but existing SHARED locks are allowed to persist. A process -- ** does not have to obtain a RESERVED lock on the way to a PENDING lock. -- ** This property is used by the algorithm for rolling back a journal file -- ** after a crash. -+ ** An EXCLUSIVE lock may only be requested after either a SHARED or -+ ** RESERVED lock is held. An EXCLUSIVE lock is implemented by obtaining -+ ** a write-lock on the entire 'shared byte range'. Since all other locks -+ ** require a read-lock on one of the bytes within this range, this ensures -+ ** that no other locks are held on the database. - ** -- ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is -- ** implemented by obtaining a write-lock on the entire 'shared byte -- ** range'. Since all other locks require a read-lock on one of the bytes -- ** within this range, this ensures that no other locks are held on the -- ** database. -+ ** If a process that holds a RESERVED lock requests an EXCLUSIVE, then -+ ** a PENDING lock is obtained first. A PENDING lock is implemented by -+ ** obtaining a write-lock on the 'pending byte'. This ensures that no new -+ ** SHARED locks can be obtained, but existing SHARED locks are allowed to -+ ** persist. If the call to this function fails to obtain the EXCLUSIVE -+ ** lock in this case, it holds the PENDING lock instead. The client may -+ ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED -+ ** locks have cleared. - */ - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; -@@ -35308,7 +40667,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ - lock.l_len = 1L; - lock.l_whence = SEEK_SET; - if( eFileLock==SHARED_LOCK -- || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLockeFileLock==RESERVED_LOCK) - ){ - lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); - lock.l_start = PENDING_BYTE; -@@ -35319,6 +40678,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){ - storeLastErrno(pFile, tErrno); - } - goto end_lock; -+ }else if( eFileLock==EXCLUSIVE_LOCK ){ -+ pFile->eFileLock = PENDING_LOCK; -+ pInode->eFileLock = PENDING_LOCK; - } - } - -@@ -35406,13 +40768,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){ - } - #endif - -- - if( rc==SQLITE_OK ){ - pFile->eFileLock = eFileLock; - pInode->eFileLock = eFileLock; -- }else if( eFileLock==EXCLUSIVE_LOCK ){ -- pFile->eFileLock = PENDING_LOCK; -- pInode->eFileLock = PENDING_LOCK; - } - - end_lock: -@@ -35692,6 +41050,7 @@ static int unixClose(sqlite3_file *id){ - } - sqlite3_mutex_leave(pInode->pLockMutex); - releaseInodeInfo(pFile); -+ assert( pFile->pShm==0 ); - rc = closeUnixFile(id); - unixLeaveMutex(); - return rc; -@@ -35771,26 +41130,22 @@ static int nolockClose(sqlite3_file *id) { - - /* - ** This routine checks if there is a RESERVED lock held on the specified --** file by this or any other process. If such a lock is held, set *pResOut --** to a non-zero value otherwise *pResOut is set to zero. The return value --** is set to SQLITE_OK unless an I/O error occurs during lock checking. --** --** In dotfile locking, either a lock exists or it does not. So in this --** variation of CheckReservedLock(), *pResOut is set to true if any lock --** is held on the file and false if the file is unlocked. -+** file by this or any other process. If the caller holds a SHARED -+** or greater lock when it is called, then it is assumed that no other -+** client may hold RESERVED. Or, if the caller holds no lock, then it -+** is assumed another client holds RESERVED if the lock-file exists. - */ - static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { -- int rc = SQLITE_OK; -- int reserved = 0; - unixFile *pFile = (unixFile*)id; -- - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - -- assert( pFile ); -- reserved = osAccess((const char*)pFile->lockingContext, 0)==0; -- OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); -- *pResOut = reserved; -- return rc; -+ if( pFile->eFileLock>=SHARED_LOCK ){ -+ *pResOut = 0; -+ }else{ -+ *pResOut = osAccess((const char*)pFile->lockingContext, 0)==0; -+ } -+ OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, 0, *pResOut)); -+ return SQLITE_OK; - } - - /* -@@ -35960,54 +41315,33 @@ static int robust_flock(int fd, int op){ - ** is set to SQLITE_OK unless an I/O error occurs during lock checking. - */ - static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ -- int rc = SQLITE_OK; -- int reserved = 0; -+#ifdef SQLITE_DEBUG - unixFile *pFile = (unixFile*)id; -+#else -+ UNUSED_PARAMETER(id); -+#endif - - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - - assert( pFile ); -+ assert( pFile->eFileLock<=SHARED_LOCK ); - -- /* Check if a thread in this process holds such a lock */ -- if( pFile->eFileLock>SHARED_LOCK ){ -- reserved = 1; -- } -- -- /* Otherwise see if some other process holds it. */ -- if( !reserved ){ -- /* attempt to get the lock */ -- int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); -- if( !lrc ){ -- /* got the lock, unlock it */ -- lrc = robust_flock(pFile->h, LOCK_UN); -- if ( lrc ) { -- int tErrno = errno; -- /* unlock failed with an error */ -- lrc = SQLITE_IOERR_UNLOCK; -- storeLastErrno(pFile, tErrno); -- rc = lrc; -- } -- } else { -- int tErrno = errno; -- reserved = 1; -- /* someone else might have it reserved */ -- lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); -- if( IS_LOCK_ERROR(lrc) ){ -- storeLastErrno(pFile, tErrno); -- rc = lrc; -- } -- } -- } -- OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); -+ /* The flock VFS only ever takes exclusive locks (see function flockLock). -+ ** Therefore, if this connection is holding any lock at all, no other -+ ** connection may be holding a RESERVED lock. So set *pResOut to 0 -+ ** in this case. -+ ** -+ ** Or, this connection may be holding no lock. In that case, set *pResOut to -+ ** 0 as well. The caller will then attempt to take an EXCLUSIVE lock on the -+ ** db in order to roll the hot journal back. If there is another connection -+ ** holding a lock, that attempt will fail and an SQLITE_BUSY returned to -+ ** the user. With other VFS, we try to avoid this, in order to allow a reader -+ ** to proceed while a writer is preparing its transaction. But that won't -+ ** work with the flock VFS - as it always takes EXCLUSIVE locks - so it is -+ ** not a problem in this case. */ -+ *pResOut = 0; - --#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS -- if( (rc & 0xff) == SQLITE_IOERR ){ -- rc = SQLITE_OK; -- reserved=1; -- } --#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ -- *pResOut = reserved; -- return rc; -+ return SQLITE_OK; - } - - /* -@@ -36597,7 +41931,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ - if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + - pInode->sharedByte, 1, 0)) ){ - int failed2 = SQLITE_OK; -- /* now attemmpt to get the exclusive lock range */ -+ /* now attempt to get the exclusive lock range */ - failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, - SHARED_SIZE, 1); - if( failed && (failed2 = afpSetLock(context->dbPath, pFile, -@@ -36646,9 +41980,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { - unixInodeInfo *pInode; - afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; - int skipShared = 0; --#ifdef SQLITE_TEST -- int h = pFile->h; --#endif - - assert( pFile ); - OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, -@@ -36664,9 +41995,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { - assert( pInode->nShared!=0 ); - if( pFile->eFileLock>SHARED_LOCK ){ - assert( pInode->eFileLock==pFile->eFileLock ); -- SimulateIOErrorBenign(1); -- SimulateIOError( h=(-1) ) -- SimulateIOErrorBenign(0); - - #ifdef SQLITE_DEBUG - /* When reducing a lock such that other processes can start -@@ -36715,9 +42043,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { - unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; - pInode->nShared--; - if( pInode->nShared==0 ){ -- SimulateIOErrorBenign(1); -- SimulateIOError( h=(-1) ) -- SimulateIOErrorBenign(0); - if( !skipShared ){ - rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); - } -@@ -36818,12 +42143,6 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){ - ** Seek to the offset passed as the second argument, then read cnt - ** bytes into pBuf. Return the number of bytes actually read. - ** --** NB: If you define USE_PREAD or USE_PREAD64, then it might also --** be necessary to define _XOPEN_SOURCE to be 500. This varies from --** one system to another. Since SQLite does not define USE_PREAD --** in any form by default, we will not attempt to define _XOPEN_SOURCE. --** See tickets #2741 and #2681. --** - ** To avoid stomping the errno value on a failed read the lastErrno value - ** is set before returning. - */ -@@ -36898,7 +42217,7 @@ static int unixRead( - #endif - - #if SQLITE_MAX_MMAP_SIZE>0 -- /* Deal with as much of this read request as possible by transfering -+ /* Deal with as much of this read request as possible by transferring - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ -@@ -36918,7 +42237,24 @@ static int unixRead( - if( got==amt ){ - return SQLITE_OK; - }else if( got<0 ){ -- /* lastErrno set by seekAndRead */ -+ /* pFile->lastErrno has been set by seekAndRead(). -+ ** Usually we return SQLITE_IOERR_READ here, though for some -+ ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The -+ ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT -+ ** prior to returning to the application by the sqlite3ApiExit() -+ ** routine. -+ */ -+ switch( pFile->lastErrno ){ -+ case ERANGE: -+ case EIO: -+#ifdef ENXIO -+ case ENXIO: -+#endif -+#ifdef EDEVERR -+ case EDEVERR: -+#endif -+ return SQLITE_IOERR_CORRUPTFS; -+ } - return SQLITE_IOERR_READ; - }else{ - storeLastErrno(pFile, 0); /* not a system error */ -@@ -37033,7 +42369,7 @@ static int unixWrite( - #endif - - #if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 -- /* Deal with as much of this write request as possible by transfering -+ /* Deal with as much of this write request as possible by transferring - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ -@@ -37155,7 +42491,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ - /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a - ** no-op. But go ahead and call fstat() to validate the file - ** descriptor as we need a method to provoke a failure during -- ** coverate testing. -+ ** coverage testing. - */ - #ifdef SQLITE_NO_SYNC - { -@@ -37477,6 +42813,9 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ - - /* Forward declaration */ - static int unixGetTempname(int nBuf, char *zBuf); -+#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) -+ static int unixFcntlExternalReader(unixFile*, int*); -+#endif - - /* - ** Information and control of an open file handle. -@@ -37499,6 +42838,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ - } - #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ - -+ case SQLITE_FCNTL_NULL_IO: { -+ osClose(pFile->h); -+ pFile->h = -1; -+ return SQLITE_OK; -+ } - case SQLITE_FCNTL_LOCKSTATE: { - *(int*)pArg = pFile->eFileLock; - return SQLITE_OK; -@@ -37545,11 +42889,23 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ - #ifdef SQLITE_ENABLE_SETLK_TIMEOUT - case SQLITE_FCNTL_LOCK_TIMEOUT: { - int iOld = pFile->iBusyTimeout; -- pFile->iBusyTimeout = *(int*)pArg; -+ int iNew = *(int*)pArg; -+#if SQLITE_ENABLE_SETLK_TIMEOUT==1 -+ pFile->iBusyTimeout = iNew<0 ? 0x7FFFFFFF : (unsigned)iNew; -+#elif SQLITE_ENABLE_SETLK_TIMEOUT==2 -+ pFile->iBusyTimeout = !!(*(int*)pArg); -+#else -+# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" -+#endif - *(int*)pArg = iOld; - return SQLITE_OK; - } --#endif -+ case SQLITE_FCNTL_BLOCK_ON_CONNECT: { -+ int iNew = *(int*)pArg; -+ pFile->bBlockOnConnect = iNew; -+ return SQLITE_OK; -+ } -+#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ - #if SQLITE_MAX_MMAP_SIZE>0 - case SQLITE_FCNTL_MMAP_SIZE: { - i64 newLimit = *(i64*)pArg; -@@ -37593,6 +42949,15 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ - return proxyFileControl(id,op,pArg); - } - #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ -+ -+ case SQLITE_FCNTL_EXTERNAL_READER: { -+#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) -+ return unixFcntlExternalReader((unixFile*)id, (int*)pArg); -+#else -+ *(int*)pArg = 0; -+ return SQLITE_OK; -+#endif -+ } - } - return SQLITE_NOTFOUND; - } -@@ -37625,6 +42990,7 @@ static void setDeviceCharacteristics(unixFile *pFd){ - if( pFd->ctrlFlags & UNIXFILE_PSOW ){ - pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; - } -+ pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ; - - pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; - } -@@ -37675,7 +43041,7 @@ static void setDeviceCharacteristics(unixFile *pFile){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* full bitset of atomics from max sector size and smaller */ -- ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | -+ (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; -@@ -37683,7 +43049,7 @@ static void setDeviceCharacteristics(unixFile *pFile){ - pFile->sectorSize = fsInfo.f_bsize; - pFile->deviceCharacteristics = - /* full bitset of atomics from max sector size and smaller */ -- ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | -+ (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | - SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind - ** so it is ordered */ - 0; -@@ -37759,7 +43125,7 @@ static int unixGetpagesize(void){ - - #endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */ - --#ifndef SQLITE_OMIT_WAL -+#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) - - /* - ** Object used to represent an shared memory buffer. -@@ -37789,6 +43155,25 @@ static int unixGetpagesize(void){ - ** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and - ** unixMutexHeld() is true when reading or writing any other field - ** in this structure. -+** -+** aLock[SQLITE_SHM_NLOCK]: -+** This array records the various locks held by clients on each of the -+** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no -+** locks are held by the process on this slot. If it is set to -1, then -+** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[] -+** value is set to a positive value, then it is the number of shared -+** locks currently held on the slot. -+** -+** aMutex[SQLITE_SHM_NLOCK]: -+** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex -+** pShmMutex is used to protect the aLock[] array and the right to -+** call fcntl() on unixShmNode.hShm to obtain or release locks. -+** -+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array -+** of mutexes - one for each locking slot. To read or write locking -+** slot aLock[iSlot], the caller must hold the corresponding mutex -+** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a -+** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held. - */ - struct unixShmNode { - unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ -@@ -37802,9 +43187,11 @@ struct unixShmNode { - char **apRegion; /* Array of mapped shared-memory regions */ - int nRef; /* Number of unixShm objects pointing to this */ - unixShm *pFirst; /* All unixShm objects pointing to this */ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK]; -+#endif -+ int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ - #ifdef SQLITE_DEBUG -- u8 exclMask; /* Mask of exclusive locks held */ -- u8 sharedMask; /* Mask of shared locks held */ - u8 nextShmId; /* Next available unixShm.id value */ - #endif - }; -@@ -37837,6 +43224,40 @@ struct unixShm { - #define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ - #define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ - -+/* -+** Use F_GETLK to check whether or not there are any readers with open -+** wal-mode transactions in other processes on database file pFile. If -+** no error occurs, return SQLITE_OK and set (*piOut) to 1 if there are -+** such transactions, or 0 otherwise. If an error occurs, return an -+** SQLite error code. The final value of *piOut is undefined in this -+** case. -+*/ -+static int unixFcntlExternalReader(unixFile *pFile, int *piOut){ -+ int rc = SQLITE_OK; -+ *piOut = 0; -+ if( pFile->pShm){ -+ unixShmNode *pShmNode = pFile->pShm->pShmNode; -+ struct flock f; -+ -+ memset(&f, 0, sizeof(f)); -+ f.l_type = F_WRLCK; -+ f.l_whence = SEEK_SET; -+ f.l_start = UNIX_SHM_BASE + 3; -+ f.l_len = SQLITE_SHM_NLOCK - 3; -+ -+ sqlite3_mutex_enter(pShmNode->pShmMutex); -+ if( osFcntl(pShmNode->hShm, F_GETLK, &f)<0 ){ -+ rc = SQLITE_IOERR_LOCK; -+ }else{ -+ *piOut = (f.l_type!=F_UNLCK); -+ } -+ sqlite3_mutex_leave(pShmNode->pShmMutex); -+ } -+ -+ return rc; -+} -+ -+ - /* - ** Apply posix advisory locks for all bytes from ofst through ofst+n-1. - ** -@@ -37853,16 +43274,35 @@ static int unixShmSystemLock( - struct flock f; /* The posix advisory locking structure */ - int rc = SQLITE_OK; /* Result code form fcntl() */ - -- /* Access to the unixShmNode object is serialized by the caller */ - pShmNode = pFile->pInode->pShmNode; -- assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); -- assert( pShmNode->nRef>0 || unixMutexHeld() ); -+ -+ /* Assert that the parameters are within expected range and that the -+ ** correct mutex or mutexes are held. */ -+ assert( pShmNode->nRef>=0 ); -+ assert( (ofst==UNIX_SHM_DMS && n==1) -+ || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK)) -+ ); -+ if( ofst==UNIX_SHM_DMS ){ -+ assert( pShmNode->nRef>0 || unixMutexHeld() ); -+ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); -+ }else{ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ int ii; -+ for(ii=ofst-UNIX_SHM_BASE; iiaMutex[ii]) ); -+ } -+#else -+ assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); -+ assert( pShmNode->nRef>0 ); -+#endif -+ } - - /* Shared locks never span more than one byte */ - assert( n==1 || lockType!=F_RDLCK ); - - /* Locks are within range */ - assert( n>=1 && n<=SQLITE_SHM_NLOCK ); -+ assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) ); - - if( pShmNode->hShm>=0 ){ - int res; -@@ -37873,7 +43313,7 @@ static int unixShmSystemLock( - f.l_len = n; - res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); - if( res==-1 ){ --#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1 - rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); - #else - rc = SQLITE_BUSY; -@@ -37881,39 +43321,28 @@ static int unixShmSystemLock( - } - } - -- /* Update the global lock state and do debug tracing */ -+ /* Do debug tracing */ - #ifdef SQLITE_DEBUG -- { u16 mask; - OSTRACE(("SHM-LOCK ")); -- mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<exclMask &= ~mask; -- pShmNode->sharedMask &= ~mask; -+ OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1)); - }else if( lockType==F_RDLCK ){ -- OSTRACE(("read-lock %d ok", ofst)); -- pShmNode->exclMask &= ~mask; -- pShmNode->sharedMask |= mask; -+ OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1)); - }else{ - assert( lockType==F_WRLCK ); -- OSTRACE(("write-lock %d ok", ofst)); -- pShmNode->exclMask |= mask; -- pShmNode->sharedMask &= ~mask; -+ OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1)); - } - }else{ - if( lockType==F_UNLCK ){ -- OSTRACE(("unlock %d failed", ofst)); -+ OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1)); - }else if( lockType==F_RDLCK ){ -- OSTRACE(("read-lock failed")); -+ OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1)); - }else{ - assert( lockType==F_WRLCK ); -- OSTRACE(("write-lock %d failed", ofst)); -+ OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1)); - } - } -- OSTRACE((" - afterwards %03x,%03x\n", -- pShmNode->sharedMask, pShmNode->exclMask)); -- } - #endif - - return rc; -@@ -37950,6 +43379,11 @@ static void unixShmPurge(unixFile *pFd){ - int i; - assert( p->pInode==pFd->pInode ); - sqlite3_mutex_free(p->pShmMutex); -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ for(i=0; iaMutex[i]); -+ } -+#endif - for(i=0; inRegion; i+=nShmPerMap){ - if( p->hShm>=0 ){ - osMunmap(p->apRegion[i], p->szRegion); -@@ -38009,7 +43443,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ - pShmNode->isUnlocked = 1; - rc = SQLITE_READONLY_CANTINIT; - }else{ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ /* Do not use a blocking lock here. If the lock cannot be obtained -+ ** immediately, it means some other connection is truncating the -+ ** *-shm file. And after it has done so, it will not release its -+ ** lock, but only downgrade it to a shared lock. So no point in -+ ** blocking here. The call below to obtain the shared DMS lock may -+ ** use a blocking lock. */ -+ int iSaveTimeout = pDbFd->iBusyTimeout; -+ pDbFd->iBusyTimeout = 0; -+#endif - rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ pDbFd->iBusyTimeout = iSaveTimeout; -+#endif - /* The first connection to attach must truncate the -shm file. We - ** truncate to 3 bytes (an arbitrary small number, less than the - ** -shm header size) rather than 0 as a system debugging aid, to -@@ -38130,6 +43577,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ - rc = SQLITE_NOMEM_BKPT; - goto shm_open_err; - } -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ { -+ int ii; -+ for(ii=0; iiaMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); -+ if( pShmNode->aMutex[ii]==0 ){ -+ rc = SQLITE_NOMEM_BKPT; -+ goto shm_open_err; -+ } -+ } -+ } -+#endif - } - - if( pInode->bProcessLock==0 ){ -@@ -38342,10 +43801,45 @@ shmpage_out: - return rc; - } - -+/* -+** Check that the pShmNode->aLock[] array comports with the locking bitmasks -+** held by each client. Return true if it does, or false otherwise. This -+** is to be used in an assert(). e.g. -+** -+** assert( assertLockingArrayOk(pShmNode) ); -+*/ -+#ifdef SQLITE_DEBUG -+static int assertLockingArrayOk(unixShmNode *pShmNode){ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ return 1; -+#else -+ unixShm *pX; -+ int aLock[SQLITE_SHM_NLOCK]; -+ -+ memset(aLock, 0, sizeof(aLock)); -+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ -+ int i; -+ for(i=0; iexclMask & (1<sharedMask & (1<=0 ); -+ aLock[i]++; -+ } -+ } -+ } -+ -+ assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); -+ return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); -+#endif -+} -+#endif -+ - /* - ** Change the lock state for a shared-memory segment. - ** --** Note that the relationship between SHAREd and EXCLUSIVE locks is a little -+** Note that the relationship between SHARED and EXCLUSIVE locks is a little - ** different here than in posix. In xShmLock(), one can go from unlocked - ** to shared and back or from unlocked to exclusive and back. But one may - ** not go from shared to exclusive or from exclusive to shared. -@@ -38357,11 +43851,17 @@ static int unixShmLock( - int flags /* What to do with the lock */ - ){ - unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ -- unixShm *p = pDbFd->pShm; /* The shared memory being locked */ -- unixShm *pX; /* For looping over all siblings */ -- unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ -+ unixShm *p; /* The shared memory being locked */ -+ unixShmNode *pShmNode; /* The underlying file iNode */ - int rc = SQLITE_OK; /* Result code */ -- u16 mask; /* Mask of locks to take or release */ -+ u16 mask = (1<<(ofst+n)) - (1<pShm; -+ if( p==0 ) return SQLITE_IOERR_SHMLOCK; -+ pShmNode = p->pShmNode; -+ if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; -+ aLock = pShmNode->aLock; - - assert( pShmNode==pDbFd->pInode->pShmNode ); - assert( pShmNode->pInode==pDbFd->pInode ); -@@ -38378,101 +43878,161 @@ static int unixShmLock( - /* Check that, if this to be a blocking lock, no locks that occur later - ** in the following list than the lock being obtained are already held: - ** -- ** 1. Checkpointer lock (ofst==1). -- ** 2. Write lock (ofst==0). -- ** 3. Read locks (ofst>=3 && ofst=3 && ofstiBusyTimeout==0 || ( -- (ofst!=2) /* not RECOVER */ -- && (ofst!=1 || (p->exclMask|p->sharedMask)==0) -- && (ofst!=0 || (p->exclMask|p->sharedMask)<3) -- && (ofst<3 || (p->exclMask|p->sharedMask)<(1<exclMask|p->sharedMask); -+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( -+ (ofst!=2 || lockMask==0) -+ && (ofst!=1 || lockMask==0 || lockMask==2) -+ && (ofst!=0 || lockMask<3) -+ && (ofst<3 || lockMask<(1<1 || mask==(1<pShmMutex); -- if( flags & SQLITE_SHM_UNLOCK ){ -- u16 allMask = 0; /* Mask of locks held by siblings */ -- -- /* See if any siblings hold this same lock */ -- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ -- if( pX==p ) continue; -- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); -- allMask |= pX->sharedMask; -- } -+ /* Check if there is any work to do. There are three cases: -+ ** -+ ** a) An unlock operation where there are locks to unlock, -+ ** b) An shared lock where the requested lock is not already held -+ ** c) An exclusive lock where the requested lock is not already held -+ ** -+ ** The SQLite core never requests an exclusive lock that it already holds. -+ ** This is assert()ed below. -+ */ -+ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK) -+ || 0==(p->exclMask & mask) -+ ); -+ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) -+ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) -+ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) -+ ){ - -- /* Unlock the system-level locks */ -- if( (mask & allMask)==0 ){ -- rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); -- }else{ -- rc = SQLITE_OK; -+ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if -+ ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any -+ ** other thread is holding this mutex, then it is either holding or about -+ ** to hold a lock exclusive to the one being requested, and we may -+ ** therefore return SQLITE_BUSY to the caller. -+ ** -+ ** Doing this prevents some deadlock scenarios. For example, thread 1 may -+ ** be a checkpointer blocked waiting on the WRITER lock. And thread 2 -+ ** may be a normal SQL client upgrading to a write transaction. In this -+ ** case thread 2 does a non-blocking request for the WRITER lock. But - -+ ** if it were to use sqlite3_mutex_enter() then it would effectively -+ ** become a (doomed) blocking request, as thread 2 would block until thread -+ ** 1 obtained WRITER and released the mutex. Since thread 2 already holds -+ ** a lock on a read-locking slot at this point, this breaks the -+ ** anti-deadlock rules (see above). */ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ int iMutex; -+ for(iMutex=ofst; iMutexaMutex[iMutex]); -+ if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes; -+ }else{ -+ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]); -+ } - } -+#else -+ sqlite3_mutex_enter(pShmNode->pShmMutex); -+#endif -+ -+ if( ALWAYS(rc==SQLITE_OK) ){ -+ if( flags & SQLITE_SHM_UNLOCK ){ -+ /* Case (a) - unlock. */ -+ int bUnlock = 1; -+ assert( (p->exclMask & p->sharedMask)==0 ); -+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); -+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); -+ -+ /* If this is a SHARED lock being unlocked, it is possible that other -+ ** clients within this process are holding the same SHARED lock. In -+ ** this case, set bUnlock to 0 so that the posix lock is not removed -+ ** from the file-descriptor below. */ -+ if( flags & SQLITE_SHM_SHARED ){ -+ assert( n==1 ); -+ assert( aLock[ofst]>=1 ); -+ if( aLock[ofst]>1 ){ -+ bUnlock = 0; -+ aLock[ofst]--; -+ p->sharedMask &= ~mask; -+ } -+ } - -- /* Undo the local locks */ -- if( rc==SQLITE_OK ){ -- p->exclMask &= ~mask; -- p->sharedMask &= ~mask; -- } -- }else if( flags & SQLITE_SHM_SHARED ){ -- u16 allShared = 0; /* Union of locks held by connections other than "p" */ -+ if( bUnlock ){ -+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); -+ if( rc==SQLITE_OK ){ -+ memset(&aLock[ofst], 0, sizeof(int)*n); -+ p->sharedMask &= ~mask; -+ p->exclMask &= ~mask; -+ } -+ } -+ }else if( flags & SQLITE_SHM_SHARED ){ -+ /* Case (b) - a shared lock. */ - -- /* Find out which shared locks are already held by sibling connections. -- ** If any sibling already holds an exclusive lock, go ahead and return -- ** SQLITE_BUSY. -- */ -- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ -- if( (pX->exclMask & mask)!=0 ){ -- rc = SQLITE_BUSY; -- break; -- } -- allShared |= pX->sharedMask; -- } -+ if( aLock[ofst]<0 ){ -+ /* An exclusive lock is held by some other connection. BUSY. */ -+ rc = SQLITE_BUSY; -+ }else if( aLock[ofst]==0 ){ -+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); -+ } - -- /* Get shared locks at the system level, if necessary */ -- if( rc==SQLITE_OK ){ -- if( (allShared & mask)==0 ){ -- rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); -+ /* Get the local shared locks */ -+ if( rc==SQLITE_OK ){ -+ p->sharedMask |= mask; -+ aLock[ofst]++; -+ } - }else{ -- rc = SQLITE_OK; -- } -- } -+ /* Case (c) - an exclusive lock. */ -+ int ii; - -- /* Get the local shared locks */ -- if( rc==SQLITE_OK ){ -- p->sharedMask |= mask; -- } -- }else{ -- /* Make sure no sibling connections hold locks that will block this -- ** lock. If any do, return SQLITE_BUSY right away. -- */ -- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ -- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ -- rc = SQLITE_BUSY; -- break; -+ assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ); -+ assert( (p->sharedMask & mask)==0 ); -+ assert( (p->exclMask & mask)==0 ); -+ -+ /* Make sure no sibling connections hold locks that will block this -+ ** lock. If any do, return SQLITE_BUSY right away. */ -+ for(ii=ofst; iiexclMask |= mask; -+ for(ii=ofst; iisharedMask & mask)==0 ); -- p->exclMask |= mask; -- } -+ /* Drop the mutexes acquired above. */ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ leave_shmnode_mutexes: -+ for(iMutex--; iMutex>=ofst; iMutex--){ -+ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]); - } -+#else -+ sqlite3_mutex_leave(pShmNode->pShmMutex); -+#endif - } -- sqlite3_mutex_leave(pShmNode->pShmMutex); -+ - OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", - p->id, osGetpid(0), p->sharedMask, p->exclMask)); - return rc; -@@ -38722,11 +44282,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ - - #if SQLITE_MAX_MMAP_SIZE>0 - if( pFd->mmapSizeMax>0 ){ -+ /* Ensure that there is always at least a 256 byte buffer of addressable -+ ** memory following the returned page. If the database is corrupt, -+ ** SQLite may overread the page slightly (in practice only a few bytes, -+ ** but 256 is safe, round, number). */ -+ const int nEofBuffer = 256; - if( pFd->pMapRegion==0 ){ - int rc = unixMapfile(pFd, -1); - if( rc!=SQLITE_OK ) return rc; - } -- if( pFd->mmapSize >= iOff+nAmt ){ -+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ - *pp = &((u8 *)pFd->pMapRegion)[iOff]; - pFd->nFetchOut++; - } -@@ -39247,25 +44812,35 @@ static int fillInUnixFile( - return rc; - } - -+/* -+** Directories to consider for temp files. -+*/ -+static const char *azTempDirs[] = { -+ 0, -+ 0, -+ "/var/tmp", -+ "/usr/tmp", -+ "/tmp", -+ "." -+}; -+ -+/* -+** Initialize first two members of azTempDirs[] array. -+*/ -+static void unixTempFileInit(void){ -+ azTempDirs[0] = getenv("SQLITE_TMPDIR"); -+ azTempDirs[1] = getenv("TMPDIR"); -+} -+ - /* - ** Return the name of a directory in which to put temporary files. - ** If no suitable temporary file directory can be found, return NULL. - */ - static const char *unixTempFileDir(void){ -- static const char *azDirs[] = { -- 0, -- 0, -- "/var/tmp", -- "/usr/tmp", -- "/tmp", -- "." -- }; - unsigned int i = 0; - struct stat buf; - const char *zDir = sqlite3_temp_directory; - -- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); -- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); - while(1){ - if( zDir!=0 - && osStat(zDir, &buf)==0 -@@ -39274,8 +44849,8 @@ static const char *unixTempFileDir(void){ - ){ - return zDir; - } -- if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break; -- zDir = azDirs[i++]; -+ if( i>=sizeof(azTempDirs)/sizeof(azTempDirs[0]) ) break; -+ zDir = azTempDirs[i++]; - } - return 0; - } -@@ -39288,6 +44863,7 @@ static const char *unixTempFileDir(void){ - static int unixGetTempname(int nBuf, char *zBuf){ - const char *zDir; - int iLimit = 0; -+ int rc = SQLITE_OK; - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this -@@ -39296,18 +44872,26 @@ static int unixGetTempname(int nBuf, char *zBuf){ - zBuf[0] = 0; - SimulateIOError( return SQLITE_IOERR ); - -+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - zDir = unixTempFileDir(); -- if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH; -- do{ -- u64 r; -- sqlite3_randomness(sizeof(r), &r); -- assert( nBuf>2 ); -- zBuf[nBuf-2] = 0; -- sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", -- zDir, r, 0); -- if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR; -- }while( osAccess(zBuf,0)==0 ); -- return SQLITE_OK; -+ if( zDir==0 ){ -+ rc = SQLITE_IOERR_GETTEMPPATH; -+ }else{ -+ do{ -+ u64 r; -+ sqlite3_randomness(sizeof(r), &r); -+ assert( nBuf>2 ); -+ zBuf[nBuf-2] = 0; -+ sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", -+ zDir, r, 0); -+ if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){ -+ rc = SQLITE_ERROR; -+ break; -+ } -+ }while( osAccess(zBuf,0)==0 ); -+ } -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); -+ return rc; - } - - #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) -@@ -39450,20 +45034,23 @@ static int findCreateFileMode( - ** - ** where NN is a decimal number. The NN naming schemes are - ** used by the test_multiplex.c module. -+ ** -+ ** In normal operation, the journal file name will always contain -+ ** a '-' character. However in 8+3 filename mode, or if a corrupt -+ ** rollback journal specifies a super-journal with a goofy name, then -+ ** the '-' might be missing or the '-' might be the first character in -+ ** the filename. In that case, just return SQLITE_OK with *pMode==0. - */ - nDb = sqlite3Strlen30(zPath) - 1; -- while( zPath[nDb]!='-' ){ -- /* In normal operation, the journal file name will always contain -- ** a '-' character. However in 8+3 filename mode, or if a corrupt -- ** rollback journal specifies a super-journal with a goofy name, then -- ** the '-' might be missing. */ -- if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK; -+ while( nDb>0 && zPath[nDb]!='.' ){ -+ if( zPath[nDb]=='-' ){ -+ memcpy(zDb, zPath, nDb); -+ zDb[nDb] = '\0'; -+ rc = getFileMode(zDb, pMode, pUid, pGid); -+ break; -+ } - nDb--; - } -- memcpy(zDb, zPath, nDb); -- zDb[nDb] = '\0'; -- -- rc = getFileMode(zDb, pMode, pUid, pGid); - }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ - *pMode = 0600; - }else if( flags & SQLITE_OPEN_URI ){ -@@ -39581,6 +45168,11 @@ static int unixOpen( - } - memset(p, 0, sizeof(unixFile)); - -+#ifdef SQLITE_ASSERT_NO_FILES -+ /* Applications that never read or write a persistent disk files */ -+ assert( zName==0 ); -+#endif -+ - if( eType==SQLITE_OPEN_MAIN_DB ){ - UnixUnusedFd *pUnused; - pUnused = findReusableFd(zName, flags); -@@ -39643,12 +45235,19 @@ static int unixOpen( - rc = SQLITE_READONLY_DIRECTORY; - }else if( errno!=EISDIR && isReadWrite ){ - /* Failed to open the file for read/write access. Try read-only. */ -+ UnixUnusedFd *pReadonly = 0; - flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); - openFlags &= ~(O_RDWR|O_CREAT); - flags |= SQLITE_OPEN_READONLY; - openFlags |= O_RDONLY; - isReadonly = 1; -- fd = robust_open(zName, openFlags, openMode); -+ pReadonly = findReusableFd(zName, flags); -+ if( pReadonly ){ -+ fd = pReadonly->fd; -+ sqlite3_free(pReadonly); -+ }else{ -+ fd = robust_open(zName, openFlags, openMode); -+ } - } - } - if( fd<0 ){ -@@ -39848,30 +45447,97 @@ static int unixAccess( - } - - /* --** -+** A pathname under construction - */ --static int mkFullPathname( -- const char *zPath, /* Input path */ -- char *zOut, /* Output buffer */ -- int nOut /* Allocated size of buffer zOut */ -+typedef struct DbPath DbPath; -+struct DbPath { -+ int rc; /* Non-zero following any error */ -+ int nSymlink; /* Number of symlinks resolved */ -+ char *zOut; /* Write the pathname here */ -+ int nOut; /* Bytes of space available to zOut[] */ -+ int nUsed; /* Bytes of zOut[] currently being used */ -+}; -+ -+/* Forward reference */ -+static void appendAllPathElements(DbPath*,const char*); -+ -+/* -+** Append a single path element to the DbPath under construction -+*/ -+static void appendOnePathElement( -+ DbPath *pPath, /* Path under construction, to which to append zName */ -+ const char *zName, /* Name to append to pPath. Not zero-terminated */ -+ int nName /* Number of significant bytes in zName */ - ){ -- int nPath = sqlite3Strlen30(zPath); -- int iOff = 0; -- if( zPath[0]!='/' ){ -- if( osGetcwd(zOut, nOut-2)==0 ){ -- return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); -+ assert( nName>0 ); -+ assert( zName!=0 ); -+ if( zName[0]=='.' ){ -+ if( nName==1 ) return; -+ if( zName[1]=='.' && nName==2 ){ -+ if( pPath->nUsed>1 ){ -+ assert( pPath->zOut[0]=='/' ); -+ while( pPath->zOut[--pPath->nUsed]!='/' ){} -+ } -+ return; - } -- iOff = sqlite3Strlen30(zOut); -- zOut[iOff++] = '/'; - } -- if( (iOff+nPath+1)>nOut ){ -- /* SQLite assumes that xFullPathname() nul-terminates the output buffer -- ** even if it returns an error. */ -- zOut[iOff] = '\0'; -- return SQLITE_CANTOPEN_BKPT; -+ if( pPath->nUsed + nName + 2 >= pPath->nOut ){ -+ pPath->rc = SQLITE_ERROR; -+ return; - } -- sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); -- return SQLITE_OK; -+ pPath->zOut[pPath->nUsed++] = '/'; -+ memcpy(&pPath->zOut[pPath->nUsed], zName, nName); -+ pPath->nUsed += nName; -+#if defined(HAVE_READLINK) && defined(HAVE_LSTAT) -+ if( pPath->rc==SQLITE_OK ){ -+ const char *zIn; -+ struct stat buf; -+ pPath->zOut[pPath->nUsed] = 0; -+ zIn = pPath->zOut; -+ if( osLstat(zIn, &buf)!=0 ){ -+ if( errno!=ENOENT ){ -+ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); -+ } -+ }else if( S_ISLNK(buf.st_mode) ){ -+ ssize_t got; -+ char zLnk[SQLITE_MAX_PATHLEN+2]; -+ if( pPath->nSymlink++ > SQLITE_MAX_SYMLINK ){ -+ pPath->rc = SQLITE_CANTOPEN_BKPT; -+ return; -+ } -+ got = osReadlink(zIn, zLnk, sizeof(zLnk)-2); -+ if( got<=0 || got>=(ssize_t)sizeof(zLnk)-2 ){ -+ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); -+ return; -+ } -+ zLnk[got] = 0; -+ if( zLnk[0]=='/' ){ -+ pPath->nUsed = 0; -+ }else{ -+ pPath->nUsed -= nName + 1; -+ } -+ appendAllPathElements(pPath, zLnk); -+ } -+ } -+#endif -+} -+ -+/* -+** Append all path elements in zPath to the DbPath under construction. -+*/ -+static void appendAllPathElements( -+ DbPath *pPath, /* Path under construction, to which to append zName */ -+ const char *zPath /* Path to append to pPath. Is zero-terminated */ -+){ -+ int i = 0; -+ int j = 0; -+ do{ -+ while( zPath[i] && zPath[i]!='/' ){ i++; } -+ if( i>j ){ -+ appendOnePathElement(pPath, &zPath[j], i-j); -+ } -+ j = i+1; -+ }while( zPath[i++] ); - } - - /* -@@ -39889,86 +45555,27 @@ static int unixFullPathname( - int nOut, /* Size of output buffer in bytes */ - char *zOut /* Output buffer */ - ){ --#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT) -- return mkFullPathname(zPath, zOut, nOut); --#else -- int rc = SQLITE_OK; -- int nByte; -- int nLink = 0; /* Number of symbolic links followed so far */ -- const char *zIn = zPath; /* Input path for each iteration of loop */ -- char *zDel = 0; -- -- assert( pVfs->mxPathname==MAX_PATHNAME ); -+ DbPath path; - UNUSED_PARAMETER(pVfs); -- -- /* It's odd to simulate an io-error here, but really this is just -- ** using the io-error infrastructure to test that SQLite handles this -- ** function failing. This function could fail if, for example, the -- ** current working directory has been unlinked. -- */ -- SimulateIOError( return SQLITE_ERROR ); -- -- do { -- -- /* Call stat() on path zIn. Set bLink to true if the path is a symbolic -- ** link, or false otherwise. */ -- int bLink = 0; -- struct stat buf; -- if( osLstat(zIn, &buf)!=0 ){ -- if( errno!=ENOENT ){ -- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); -- } -- }else{ -- bLink = S_ISLNK(buf.st_mode); -- } -- -- if( bLink ){ -- nLink++; -- if( zDel==0 ){ -- zDel = sqlite3_malloc(nOut); -- if( zDel==0 ) rc = SQLITE_NOMEM_BKPT; -- }else if( nLink>=SQLITE_MAX_SYMLINKS ){ -- rc = SQLITE_CANTOPEN_BKPT; -- } -- -- if( rc==SQLITE_OK ){ -- nByte = osReadlink(zIn, zDel, nOut-1); -- if( nByte<0 ){ -- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); -- }else{ -- if( zDel[0]!='/' ){ -- int n; -- for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); -- if( nByte+n+1>nOut ){ -- rc = SQLITE_CANTOPEN_BKPT; -- }else{ -- memmove(&zDel[n], zDel, nByte+1); -- memcpy(zDel, zIn, n); -- nByte += n; -- } -- } -- zDel[nByte] = '\0'; -- } -- } -- -- zIn = zDel; -- } -- -- assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' ); -- if( rc==SQLITE_OK && zIn!=zOut ){ -- rc = mkFullPathname(zIn, zOut, nOut); -+ path.rc = 0; -+ path.nUsed = 0; -+ path.nSymlink = 0; -+ path.nOut = nOut; -+ path.zOut = zOut; -+ if( zPath[0]!='/' ){ -+ char zPwd[SQLITE_MAX_PATHLEN+2]; -+ if( osGetcwd(zPwd, sizeof(zPwd)-2)==0 ){ -+ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); - } -- if( bLink==0 ) break; -- zIn = zOut; -- }while( rc==SQLITE_OK ); -- -- sqlite3_free(zDel); -- if( rc==SQLITE_OK && nLink ) rc = SQLITE_OK_SYMLINK; -- return rc; --#endif /* HAVE_READLINK && HAVE_LSTAT */ -+ appendAllPathElements(&path, zPwd); -+ } -+ appendAllPathElements(&path, zPath); -+ zOut[path.nUsed] = 0; -+ if( path.rc || path.nUsed<2 ) return SQLITE_CANTOPEN_BKPT; -+ if( path.nSymlink ) return SQLITE_OK_SYMLINK; -+ return SQLITE_OK; - } - -- - #ifndef SQLITE_OMIT_LOAD_EXTENSION - /* - ** Interfaces for opening a shared library, finding entry points -@@ -40082,16 +45689,22 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ - ** than the argument. - */ - static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ --#if OS_VXWORKS -+#if !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP+0 - struct timespec sp; -- - sp.tv_sec = microseconds / 1000000; - sp.tv_nsec = (microseconds % 1000000) * 1000; -+ -+ /* Almost all modern unix systems support nanosleep(). But if you are -+ ** compiling for one of the rare exceptions, you can use -+ ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if -+ ** usleep() is available) in order to bypass the use of nanosleep() */ - nanosleep(&sp, NULL); -+ - UNUSED_PARAMETER(NotUsed); - return microseconds; - #elif defined(HAVE_USLEEP) && HAVE_USLEEP -- usleep(microseconds); -+ if( microseconds>=1000000 ) sleep(microseconds/1000000); -+ if( microseconds%1000000 ) usleep(microseconds%1000000); - UNUSED_PARAMETER(NotUsed); - return microseconds; - #else -@@ -40664,7 +46277,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ - - if( nTries==1 ){ - conchModTime = buf.st_mtimespec; -- usleep(500000); /* wait 0.5 sec and try the lock again*/ -+ unixSleep(0,500000); /* wait 0.5 sec and try the lock again*/ - continue; - } - -@@ -40690,7 +46303,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ - /* don't break the lock on short read or a version mismatch */ - return SQLITE_BUSY; - } -- usleep(10000000); /* wait 10 sec and try the lock again */ -+ unixSleep(0,10000000); /* wait 10 sec and try the lock again */ - continue; - } - -@@ -41463,9 +47076,39 @@ SQLITE_API int sqlite3_os_init(void){ - - /* Register all VFSes defined in the aVfs[] array */ - for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ -+#ifdef SQLITE_DEFAULT_UNIX_VFS -+ sqlite3_vfs_register(&aVfs[i], -+ 0==strcmp(aVfs[i].zName,SQLITE_DEFAULT_UNIX_VFS)); -+#else - sqlite3_vfs_register(&aVfs[i], i==0); -+#endif - } -+#ifdef SQLITE_OS_KV_OPTIONAL -+ sqlite3KvvfsInit(); -+#endif - unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); -+ -+#ifndef SQLITE_OMIT_WAL -+ /* Validate lock assumptions */ -+ assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */ -+ assert( UNIX_SHM_BASE==120 ); /* Start of locking area */ -+ /* Locks: -+ ** WRITE UNIX_SHM_BASE 120 -+ ** CKPT UNIX_SHM_BASE+1 121 -+ ** RECOVER UNIX_SHM_BASE+2 122 -+ ** READ-0 UNIX_SHM_BASE+3 123 -+ ** READ-1 UNIX_SHM_BASE+4 124 -+ ** READ-2 UNIX_SHM_BASE+5 125 -+ ** READ-3 UNIX_SHM_BASE+6 126 -+ ** READ-4 UNIX_SHM_BASE+7 127 -+ ** DMS UNIX_SHM_BASE+8 128 -+ */ -+ assert( UNIX_SHM_DMS==128 ); /* Byte offset of the deadman-switch */ -+#endif -+ -+ /* Initialize temp file dir array. */ -+ unixTempFileInit(); -+ - return SQLITE_OK; - } - -@@ -41505,205 +47148,7 @@ SQLITE_API int sqlite3_os_end(void){ - /* - ** Include code that is common to all os_*.c files - */ --/************** Include os_common.h in the middle of os_win.c ****************/ --/************** Begin file os_common.h ***************************************/ --/* --** 2004 May 22 --** --** 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 contains macros and a little bit of code that is common to --** all of the platform-specific files (os_*.c) and is #included into those --** files. --** --** This file should be #included by the os_*.c files only. It is not a --** general purpose header file. --*/ --#ifndef _OS_COMMON_H_ --#define _OS_COMMON_H_ -- --/* --** At least two bugs have slipped in because we changed the MEMORY_DEBUG --** macro to SQLITE_DEBUG and some older makefiles have not yet made the --** switch. The following code should catch this problem at compile-time. --*/ --#ifdef MEMORY_DEBUG --# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." --#endif -- --/* --** Macros for performance tracing. Normally turned off. Only works --** on i486 hardware. --*/ --#ifdef SQLITE_PERFORMANCE_TRACE -- --/* --** hwtime.h contains inline assembler code for implementing --** high-performance timing routines. --*/ --/************** Include hwtime.h in the middle of os_common.h ****************/ --/************** Begin file hwtime.h ******************************************/ --/* --** 2008 May 27 --** --** 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 contains inline asm code for retrieving "high-performance" --** counters for x86 and x86_64 class CPUs. --*/ --#ifndef SQLITE_HWTIME_H --#define SQLITE_HWTIME_H -- --/* --** The following routine only works on pentium-class (or newer) processors. --** It uses the RDTSC opcode to read the cycle count value out of the --** processor and returns that value. This can be used for high-res --** profiling. --*/ --#if !defined(__STRICT_ANSI__) && \ -- (defined(__GNUC__) || defined(_MSC_VER)) && \ -- (defined(i386) || defined(__i386__) || defined(_M_IX86)) -- -- #if defined(__GNUC__) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned int lo, hi; -- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); -- return (sqlite_uint64)hi << 32 | lo; -- } -- -- #elif defined(_MSC_VER) -- -- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ -- __asm { -- rdtsc -- ret ; return value at EDX:EAX -- } -- } -- -- #endif -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long val; -- __asm__ __volatile__ ("rdtsc" : "=A" (val)); -- return val; -- } -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long long retval; -- unsigned long junk; -- __asm__ __volatile__ ("\n\ -- 1: mftbu %1\n\ -- mftb %L0\n\ -- mftbu %0\n\ -- cmpw %0,%1\n\ -- bne 1b" -- : "=r" (retval), "=r" (junk)); -- return retval; -- } -- --#else -- -- /* -- ** asm() is needed for hardware timing support. Without asm(), -- ** disable the sqlite3Hwtime() routine. -- ** -- ** sqlite3Hwtime() is only used for some obscure debugging -- ** and analysis configurations, not in any deliverable, so this -- ** should not be a great loss. -- */ --SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } -- --#endif -- --#endif /* !defined(SQLITE_HWTIME_H) */ -- --/************** End of hwtime.h **********************************************/ --/************** Continuing where we left off in os_common.h ******************/ -- --static sqlite_uint64 g_start; --static sqlite_uint64 g_elapsed; --#define TIMER_START g_start=sqlite3Hwtime() --#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start --#define TIMER_ELAPSED g_elapsed --#else --#define TIMER_START --#define TIMER_END --#define TIMER_ELAPSED ((sqlite_uint64)0) --#endif -- --/* --** If we compile with the SQLITE_TEST macro set, then the following block --** of code will give us the ability to simulate a disk I/O error. This --** is used for testing the I/O recovery logic. --*/ --#if defined(SQLITE_TEST) --SQLITE_API extern int sqlite3_io_error_hit; --SQLITE_API extern int sqlite3_io_error_hardhit; --SQLITE_API extern int sqlite3_io_error_pending; --SQLITE_API extern int sqlite3_io_error_persist; --SQLITE_API extern int sqlite3_io_error_benign; --SQLITE_API extern int sqlite3_diskfull_pending; --SQLITE_API extern int sqlite3_diskfull; --#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) --#define SimulateIOError(CODE) \ -- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ -- || sqlite3_io_error_pending-- == 1 ) \ -- { local_ioerr(); CODE; } --static void local_ioerr(){ -- IOTRACE(("IOERR\n")); -- sqlite3_io_error_hit++; -- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; --} --#define SimulateDiskfullError(CODE) \ -- if( sqlite3_diskfull_pending ){ \ -- if( sqlite3_diskfull_pending == 1 ){ \ -- local_ioerr(); \ -- sqlite3_diskfull = 1; \ -- sqlite3_io_error_hit = 1; \ -- CODE; \ -- }else{ \ -- sqlite3_diskfull_pending--; \ -- } \ -- } --#else --#define SimulateIOErrorBenign(X) --#define SimulateIOError(A) --#define SimulateDiskfullError(A) --#endif /* defined(SQLITE_TEST) */ -- --/* --** When testing, keep a count of the number of open files. --*/ --#if defined(SQLITE_TEST) --SQLITE_API extern int sqlite3_open_file_count; --#define OpenCounter(X) sqlite3_open_file_count+=(X) --#else --#define OpenCounter(X) --#endif /* defined(SQLITE_TEST) */ -- --#endif /* !defined(_OS_COMMON_H_) */ -- --/************** End of os_common.h *******************************************/ --/************** Continuing where we left off in os_win.c *********************/ -+/* #include "os_common.h" */ - - /* - ** Include the header file for the Windows VFS. -@@ -41972,8 +47417,18 @@ struct winFile { - sqlite3_int64 mmapSize; /* Size of mapped region */ - sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ - #endif -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ DWORD iBusyTimeout; /* Wait this many millisec on locks */ -+ int bBlockOnConnect; -+#endif - }; - -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+# define winFileBusyTimeout(pDbFd) pDbFd->iBusyTimeout -+#else -+# define winFileBusyTimeout(pDbFd) 0 -+#endif -+ - /* - ** The winVfsAppData structure is used for the pAppData member for all of the - ** Win32 VFS variants. -@@ -42292,7 +47747,7 @@ static struct win_syscall { - { "FileTimeToLocalFileTime", (SYSCALL)0, 0 }, - #endif - --#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \ -+#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(const FILETIME*, \ - LPFILETIME))aSyscall[11].pCurrent) - - #if SQLITE_OS_WINCE -@@ -42301,7 +47756,7 @@ static struct win_syscall { - { "FileTimeToSystemTime", (SYSCALL)0, 0 }, - #endif - --#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \ -+#define osFileTimeToSystemTime ((BOOL(WINAPI*)(const FILETIME*, \ - LPSYSTEMTIME))aSyscall[12].pCurrent) - - { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 }, -@@ -42407,6 +47862,12 @@ static struct win_syscall { - #define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ - LPWSTR*))aSyscall[25].pCurrent) - -+/* -+** For GetLastError(), MSDN says: -+** -+** Minimum supported client: Windows XP [desktop apps | UWP apps] -+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] -+*/ - { "GetLastError", (SYSCALL)GetLastError, 0 }, - - #define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) -@@ -42575,7 +48036,7 @@ static struct win_syscall { - { "LockFile", (SYSCALL)0, 0 }, - #endif - --#ifndef osLockFile -+#if !defined(osLockFile) && defined(SQLITE_WIN32_HAS_ANSI) - #define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[47].pCurrent) - #endif -@@ -42639,7 +48100,7 @@ static struct win_syscall { - - { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, - --#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ -+#define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \ - LPFILETIME))aSyscall[56].pCurrent) - - #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT -@@ -42648,7 +48109,7 @@ static struct win_syscall { - { "UnlockFile", (SYSCALL)0, 0 }, - #endif - --#ifndef osUnlockFile -+#if !defined(osUnlockFile) && defined(SQLITE_WIN32_HAS_ANSI) - #define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[57].pCurrent) - #endif -@@ -42689,11 +48150,13 @@ static struct win_syscall { - #define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ - DWORD,DWORD))aSyscall[62].pCurrent) - --#if !SQLITE_OS_WINRT -+/* -+** For WaitForSingleObject(), MSDN says: -+** -+** Minimum supported client: Windows XP [desktop apps | UWP apps] -+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] -+*/ - { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, --#else -- { "WaitForSingleObject", (SYSCALL)0, 0 }, --#endif - - #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ - DWORD))aSyscall[63].pCurrent) -@@ -42840,11 +48303,102 @@ static struct win_syscall { - #define osFlushViewOfFile \ - ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) - -+/* -+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() -+** to implement blocking locks with timeouts. MSDN says: -+** -+** Minimum supported client: Windows XP [desktop apps | UWP apps] -+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] -+*/ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ { "CreateEvent", (SYSCALL)CreateEvent, 0 }, -+#else -+ { "CreateEvent", (SYSCALL)0, 0 }, -+#endif -+ -+#define osCreateEvent ( \ -+ (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ -+ aSyscall[80].pCurrent \ -+) -+ -+/* -+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() -+** for the case where a timeout expires and a lock request must be -+** cancelled. -+** -+** Minimum supported client: Windows XP [desktop apps | UWP apps] -+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] -+*/ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ { "CancelIo", (SYSCALL)CancelIo, 0 }, -+#else -+ { "CancelIo", (SYSCALL)0, 0 }, -+#endif -+ -+#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent) -+ -+#if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32) -+ { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 }, -+#else -+ { "GetModuleHandleW", (SYSCALL)0, 0 }, -+#endif -+ -+#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent) -+ -+#ifndef _WIN32 -+ { "getenv", (SYSCALL)getenv, 0 }, -+#else -+ { "getenv", (SYSCALL)0, 0 }, -+#endif -+ -+#define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent) -+ -+#ifndef _WIN32 -+ { "getcwd", (SYSCALL)getcwd, 0 }, -+#else -+ { "getcwd", (SYSCALL)0, 0 }, -+#endif -+ -+#define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent) -+ -+#ifndef _WIN32 -+ { "readlink", (SYSCALL)readlink, 0 }, -+#else -+ { "readlink", (SYSCALL)0, 0 }, -+#endif -+ -+#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent) -+ -+#ifndef _WIN32 -+ { "lstat", (SYSCALL)lstat, 0 }, -+#else -+ { "lstat", (SYSCALL)0, 0 }, -+#endif -+ -+#define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent) -+ -+#ifndef _WIN32 -+ { "__errno", (SYSCALL)__errno, 0 }, -+#else -+ { "__errno", (SYSCALL)0, 0 }, -+#endif -+ -+#define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)()) -+ -+#ifndef _WIN32 -+ { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 }, -+#else -+ { "cygwin_conv_path", (SYSCALL)0, 0 }, -+#endif -+ -+#define osCygwin_conv_path ((size_t(*)(unsigned int, \ -+ const void *, void *, size_t))aSyscall[88].pCurrent) -+ - }; /* End of the overrideable system calls */ - - /* - ** This is the xSetSystemCall() method of sqlite3_vfs for all of the --** "win32" VFSes. Return SQLITE_OK opon successfully updating the -+** "win32" VFSes. Return SQLITE_OK upon successfully updating the - ** system call pointer, or SQLITE_NOTFOUND if there is no configurable - ** system call named zName. - */ -@@ -43013,6 +48567,7 @@ SQLITE_API int sqlite3_win32_reset_heap(){ - } - #endif /* SQLITE_WIN32_MALLOC */ - -+#ifdef _WIN32 - /* - ** This function outputs the specified (ANSI) string to the Win32 debugger - ** (if available). -@@ -43055,6 +48610,7 @@ SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ - } - #endif - } -+#endif /* _WIN32 */ - - /* - ** The following routine suspends the current thread for at least ms -@@ -43138,7 +48694,9 @@ SQLITE_API int sqlite3_win32_is_nt(void){ - } - return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; - #elif SQLITE_TEST -- return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; -+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2 -+ || osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 -+ ; - #else - /* - ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are -@@ -43353,6 +48911,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){ - } - #endif /* SQLITE_WIN32_MALLOC */ - -+#ifdef _WIN32 - /* - ** Convert a UTF-8 string to Microsoft Unicode. - ** -@@ -43378,6 +48937,7 @@ static LPWSTR winUtf8ToUnicode(const char *zText){ - } - return zWideText; - } -+#endif /* _WIN32 */ - - /* - ** Convert a Microsoft Unicode string to UTF-8. -@@ -43412,28 +48972,29 @@ static char *winUnicodeToUtf8(LPCWSTR zWideText){ - ** Space to hold the returned string is obtained from sqlite3_malloc(). - */ - static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){ -- int nByte; -+ int nWideChar; - LPWSTR zMbcsText; - int codepage = useAnsi ? CP_ACP : CP_OEMCP; - -- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL, -- 0)*sizeof(WCHAR); -- if( nByte==0 ){ -+ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, NULL, -+ 0); -+ if( nWideChar==0 ){ - return 0; - } -- zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) ); -+ zMbcsText = sqlite3MallocZero( nWideChar*sizeof(WCHAR) ); - if( zMbcsText==0 ){ - return 0; - } -- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText, -- nByte); -- if( nByte==0 ){ -+ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText, -+ nWideChar); -+ if( nWideChar==0 ){ - sqlite3_free(zMbcsText); - zMbcsText = 0; - } - return zMbcsText; - } - -+#ifdef _WIN32 - /* - ** Convert a Microsoft Unicode string to a multi-byte character string, - ** using the ANSI or OEM code page. -@@ -43461,6 +49022,7 @@ static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){ - } - return zText; - } -+#endif /* _WIN32 */ - - /* - ** Convert a multi-byte character string to UTF-8. -@@ -43480,6 +49042,7 @@ static char *winMbcsToUtf8(const char *zText, int useAnsi){ - return zTextUtf8; - } - -+#ifdef _WIN32 - /* - ** Convert a UTF-8 string to a multi-byte character string. - ** -@@ -43529,6 +49092,7 @@ SQLITE_API char *sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){ - #endif - return winUnicodeToUtf8(zWideText); - } -+#endif /* _WIN32 */ - - /* - ** This is a public wrapper for the winMbcsToUtf8() function. -@@ -43546,6 +49110,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){ - return winMbcsToUtf8(zText, osAreFileApisANSI()); - } - -+#ifdef _WIN32 - /* - ** This is a public wrapper for the winMbcsToUtf8() function. - */ -@@ -43603,10 +49168,12 @@ SQLITE_API int sqlite3_win32_set_directory8( - const char *zValue /* New value for directory being set or reset */ - ){ - char **ppDirectory = 0; -+ int rc; - #ifndef SQLITE_OMIT_AUTOINIT -- int rc = sqlite3_initialize(); -+ rc = sqlite3_initialize(); - if( rc ) return rc; - #endif -+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){ - ppDirectory = &sqlite3_data_directory; - }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){ -@@ -43621,14 +49188,19 @@ SQLITE_API int sqlite3_win32_set_directory8( - if( zValue && zValue[0] ){ - zCopy = sqlite3_mprintf("%s", zValue); - if ( zCopy==0 ){ -- return SQLITE_NOMEM_BKPT; -+ rc = SQLITE_NOMEM_BKPT; -+ goto set_directory8_done; - } - } - sqlite3_free(*ppDirectory); - *ppDirectory = zCopy; -- return SQLITE_OK; -+ rc = SQLITE_OK; -+ }else{ -+ rc = SQLITE_ERROR; - } -- return SQLITE_ERROR; -+set_directory8_done: -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); -+ return rc; - } - - /* -@@ -43663,6 +49235,7 @@ SQLITE_API int sqlite3_win32_set_directory( - ){ - return sqlite3_win32_set_directory16(type, zValue); - } -+#endif /* _WIN32 */ - - /* - ** The return value of winGetLastErrorMsg -@@ -44211,13 +49784,98 @@ static BOOL winLockFile( - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); -+#ifdef SQLITE_WIN32_HAS_ANSI - }else{ - return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, - numBytesHigh); -+#endif - } - #endif - } - -+/* -+** Lock a region of nByte bytes starting at offset offset of file hFile. -+** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock -+** otherwise. If nMs is greater than zero and the lock cannot be obtained -+** immediately, block for that many ms before giving up. -+** -+** This function returns SQLITE_OK if the lock is obtained successfully. If -+** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or -+** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR. -+*/ -+static int winHandleLockTimeout( -+ HANDLE hFile, -+ DWORD offset, -+ DWORD nByte, -+ int bExcl, -+ DWORD nMs -+){ -+ DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0); -+ int rc = SQLITE_OK; -+ BOOL ret; -+ -+ if( !osIsNT() ){ -+ ret = winLockFile(&hFile, flags, offset, 0, nByte, 0); -+ }else{ -+ OVERLAPPED ovlp; -+ memset(&ovlp, 0, sizeof(OVERLAPPED)); -+ ovlp.Offset = offset; -+ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ if( nMs!=0 ){ -+ flags &= ~LOCKFILE_FAIL_IMMEDIATELY; -+ } -+ ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL); -+ if( ovlp.hEvent==NULL ){ -+ return SQLITE_IOERR_LOCK; -+ } -+#endif -+ -+ ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp); -+ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was -+ ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to -+ ** LockFileEx() may fail because the request is still pending. This can -+ ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified. -+ ** -+ ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags -+ ** passed to LockFileEx(). In this case, if the operation is pending, -+ ** block indefinitely until it is finished. -+ ** -+ ** Otherwise, wait for up to nMs ms for the operation to finish. nMs -+ ** may be set to INFINITE. -+ */ -+ if( !ret && GetLastError()==ERROR_IO_PENDING ){ -+ DWORD nDelay = (nMs==0 ? INFINITE : nMs); -+ DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay); -+ if( res==WAIT_OBJECT_0 ){ -+ ret = TRUE; -+ }else if( res==WAIT_TIMEOUT ){ -+#if SQLITE_ENABLE_SETLK_TIMEOUT==1 -+ rc = SQLITE_BUSY_TIMEOUT; -+#else -+ rc = SQLITE_BUSY; -+#endif -+ }else{ -+ /* Some other error has occurred */ -+ rc = SQLITE_IOERR_LOCK; -+ } -+ -+ /* If it is still pending, cancel the LockFileEx() call. */ -+ osCancelIo(hFile); -+ } -+ -+ osCloseHandle(ovlp.hEvent); -+#endif -+ } -+ -+ if( rc==SQLITE_OK && !ret ){ -+ rc = SQLITE_BUSY; -+ } -+ return rc; -+} -+ - /* - ** Unlock a file region. - */ -@@ -44242,13 +49900,23 @@ static BOOL winUnlockFile( - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); -+#ifdef SQLITE_WIN32_HAS_ANSI - }else{ - return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, - numBytesHigh); -+#endif - } - #endif - } - -+/* -+** Remove an nByte lock starting at offset iOff from HANDLE h. -+*/ -+static int winHandleUnlock(HANDLE h, int iOff, int nByte){ -+ BOOL ret = winUnlockFile(&h, iOff, 0, nByte, 0); -+ return (ret ? SQLITE_OK : SQLITE_IOERR_UNLOCK); -+} -+ - /***************************************************************************** - ** The next group of routines implement the I/O methods specified - ** by the sqlite3_io_methods object. -@@ -44262,66 +49930,70 @@ static BOOL winUnlockFile( - #endif - - /* --** Move the current position of the file handle passed as the first --** argument to offset iOffset within the file. If successful, return 0. --** Otherwise, set pFile->lastErrno and return non-zero. -+** Seek the file handle h to offset nByte of the file. -+** -+** If successful, return SQLITE_OK. Or, if an error occurs, return an SQLite -+** error code. - */ --static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ -+static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){ -+ int rc = SQLITE_OK; /* Return value */ -+ - #if !SQLITE_OS_WINRT - LONG upperBits; /* Most sig. 32 bits of new offset */ - LONG lowerBits; /* Least sig. 32 bits of new offset */ - DWORD dwRet; /* Value returned by SetFilePointer() */ -- DWORD lastErrno; /* Value returned by GetLastError() */ -- -- OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); - - upperBits = (LONG)((iOffset>>32) & 0x7fffffff); - lowerBits = (LONG)(iOffset & 0xffffffff); - -+ dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN); -+ - /* API oddity: If successful, SetFilePointer() returns a dword - ** containing the lower 32-bits of the new file-offset. Or, if it fails, - ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, - ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine - ** whether an error has actually occurred, it is also necessary to call -- ** GetLastError(). -- */ -- dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); -- -- if( (dwRet==INVALID_SET_FILE_POINTER -- && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ -- pFile->lastErrno = lastErrno; -- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, -- "winSeekFile", pFile->zPath); -- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); -- return 1; -+ ** GetLastError(). */ -+ if( dwRet==INVALID_SET_FILE_POINTER ){ -+ DWORD lastErrno = osGetLastError(); -+ if( lastErrno!=NO_ERROR ){ -+ rc = SQLITE_IOERR_SEEK; -+ } - } -- -- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); -- return 0; - #else -- /* -- ** Same as above, except that this implementation works for WinRT. -- */ -- -+ /* This implementation works for WinRT. */ - LARGE_INTEGER x; /* The new offset */ - BOOL bRet; /* Value returned by SetFilePointerEx() */ - - x.QuadPart = iOffset; -- bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); -+ bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN); - - if(!bRet){ -- pFile->lastErrno = osGetLastError(); -- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, -- "winSeekFile", pFile->zPath); -- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); -- return 1; -+ rc = SQLITE_IOERR_SEEK; - } -- -- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); -- return 0; - #endif -+ -+ OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc))); -+ return rc; - } - -+/* -+** Move the current position of the file handle passed as the first -+** argument to offset iOffset within the file. If successful, return 0. -+** Otherwise, set pFile->lastErrno and return non-zero. -+*/ -+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ -+ int rc; -+ -+ rc = winHandleSeek(pFile->h, iOffset); -+ if( rc!=SQLITE_OK ){ -+ pFile->lastErrno = osGetLastError(); -+ winLogError(rc, pFile->lastErrno, "winSeekFile", pFile->zPath); -+ } -+ return rc; -+} -+ -+ - #if SQLITE_MAX_MMAP_SIZE>0 - /* Forward references to VFS helper methods used for memory mapped files */ - static int winMapfile(winFile*, sqlite3_int64); -@@ -44417,7 +50089,7 @@ static int winRead( - pFile->h, pBuf, amt, offset, pFile->locktype)); - - #if SQLITE_MAX_MMAP_SIZE>0 -- /* Deal with as much of this read request as possible by transfering -+ /* Deal with as much of this read request as possible by transferring - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ -@@ -44495,7 +50167,7 @@ static int winWrite( - pFile->h, pBuf, amt, offset, pFile->locktype)); - - #if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 -- /* Deal with as much of this write request as possible by transfering -+ /* Deal with as much of this write request as possible by transferring - ** data from the memory mapping using memcpy(). */ - if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ -@@ -44581,6 +50253,60 @@ static int winWrite( - return SQLITE_OK; - } - -+/* -+** Truncate the file opened by handle h to nByte bytes in size. -+*/ -+static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){ -+ int rc = SQLITE_OK; /* Return code */ -+ rc = winHandleSeek(h, nByte); -+ if( rc==SQLITE_OK ){ -+ if( 0==osSetEndOfFile(h) ){ -+ rc = SQLITE_IOERR_TRUNCATE; -+ } -+ } -+ return rc; -+} -+ -+/* -+** Determine the size in bytes of the file opened by the handle passed as -+** the first argument. -+*/ -+static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){ -+ int rc = SQLITE_OK; -+ -+#if SQLITE_OS_WINRT -+ FILE_STANDARD_INFO info; -+ BOOL b; -+ b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info)); -+ if( b ){ -+ *pnByte = info.EndOfFile.QuadPart; -+ }else{ -+ rc = SQLITE_IOERR_FSTAT; -+ } -+#else -+ DWORD upperBits = 0; -+ DWORD lowerBits = 0; -+ -+ assert( pnByte ); -+ lowerBits = osGetFileSize(h, &upperBits); -+ *pnByte = (((sqlite3_int64)upperBits)<<32) + lowerBits; -+ if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){ -+ rc = SQLITE_IOERR_FSTAT; -+ } -+#endif -+ -+ return rc; -+} -+ -+/* -+** Close the handle passed as the only argument. -+*/ -+static void winHandleClose(HANDLE h){ -+ if( h!=INVALID_HANDLE_VALUE ){ -+ osCloseHandle(h); -+ } -+} -+ - /* - ** Truncate an open file to a specified size - */ -@@ -44605,7 +50331,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ - ** all references to memory-mapped content are closed. That is doable, - ** but involves adding a few branches in the common write code path which - ** could slow down normal operations slightly. Hence, we have decided for -- ** now to simply make trancations a no-op if there are pending reads. We -+ ** now to simply make transactions a no-op if there are pending reads. We - ** can maybe revisit this decision in the future. - */ - return SQLITE_OK; -@@ -44664,7 +50390,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ - #ifdef SQLITE_TEST - /* - ** Count the number of fullsyncs and normal syncs. This is used to test --** that syncs and fullsyncs are occuring at the right times. -+** that syncs and fullsyncs are occurring at the right times. - */ - SQLITE_API int sqlite3_sync_count = 0; - SQLITE_API int sqlite3_fullsync_count = 0; -@@ -44836,8 +50562,9 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ - ** Different API routines are called depending on whether or not this - ** is Win9x or WinNT. - */ --static int winGetReadLock(winFile *pFile){ -+static int winGetReadLock(winFile *pFile, int bBlock){ - int res; -+ DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0); - OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); - if( osIsNT() ){ - #if SQLITE_OS_WINCE -@@ -44847,7 +50574,7 @@ static int winGetReadLock(winFile *pFile){ - */ - res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); - #else -- res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, -+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0, - SHARED_SIZE, 0); - #endif - } -@@ -44856,7 +50583,7 @@ static int winGetReadLock(winFile *pFile){ - int lk; - sqlite3_randomness(sizeof(lk), &lk); - pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); -- res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, -+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask, - SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); - } - #endif -@@ -44951,46 +50678,62 @@ static int winLock(sqlite3_file *id, int locktype){ - assert( locktype!=PENDING_LOCK ); - assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); - -- /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or -+ /* Lock the PENDING_LOCK byte if we need to acquire an EXCLUSIVE lock or - ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of - ** the PENDING_LOCK byte is temporary. - */ - newLocktype = pFile->locktype; -- if( pFile->locktype==NO_LOCK -- || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK) -+ if( locktype==SHARED_LOCK -+ || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) - ){ - int cnt = 3; -- while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, -- PENDING_BYTE, 0, 1, 0))==0 ){ -+ -+ /* Flags for the LockFileEx() call. This should be an exclusive lock if -+ ** this call is to obtain EXCLUSIVE, or a shared lock if this call is to -+ ** obtain SHARED. */ -+ int flags = LOCKFILE_FAIL_IMMEDIATELY; -+ if( locktype==EXCLUSIVE_LOCK ){ -+ flags |= LOCKFILE_EXCLUSIVE_LOCK; -+ } -+ while( cnt>0 ){ - /* Try 3 times to get the pending lock. This is needed to work - ** around problems caused by indexing and/or anti-virus software on - ** Windows systems. -+ ** - ** If you are using this code as a model for alternative VFSes, do not -- ** copy this retry logic. It is a hack intended for Windows only. -- */ -+ ** copy this retry logic. It is a hack intended for Windows only. */ -+ res = winLockFile(&pFile->h, flags, PENDING_BYTE, 0, 1, 0); -+ if( res ) break; -+ - lastErrno = osGetLastError(); - OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", -- pFile->h, cnt, res)); -+ pFile->h, cnt, res -+ )); -+ - if( lastErrno==ERROR_INVALID_HANDLE ){ - pFile->lastErrno = lastErrno; - rc = SQLITE_IOERR_LOCK; - OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", -- pFile->h, cnt, sqlite3ErrName(rc))); -+ pFile->h, cnt, sqlite3ErrName(rc) -+ )); - return rc; - } -- if( cnt ) sqlite3_win32_sleep(1); -+ -+ cnt--; -+ if( cnt>0 ) sqlite3_win32_sleep(1); - } - gotPendingLock = res; -- if( !res ){ -- lastErrno = osGetLastError(); -- } - } - - /* Acquire a shared lock - */ - if( locktype==SHARED_LOCK && res ){ - assert( pFile->locktype==NO_LOCK ); -- res = winGetReadLock(pFile); -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ res = winGetReadLock(pFile, pFile->bBlockOnConnect); -+#else -+ res = winGetReadLock(pFile, 0); -+#endif - if( res ){ - newLocktype = SHARED_LOCK; - }else{ -@@ -45021,14 +50764,14 @@ static int winLock(sqlite3_file *id, int locktype){ - */ - if( locktype==EXCLUSIVE_LOCK && res ){ - assert( pFile->locktype>=SHARED_LOCK ); -- res = winUnlockReadLock(pFile); -+ (void)winUnlockReadLock(pFile); - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, - SHARED_SIZE, 0); - if( res ){ - newLocktype = EXCLUSIVE_LOCK; - }else{ - lastErrno = osGetLastError(); -- winGetReadLock(pFile); -+ winGetReadLock(pFile, 0); - } - } - -@@ -45108,7 +50851,7 @@ static int winUnlock(sqlite3_file *id, int locktype){ - type = pFile->locktype; - if( type>=EXCLUSIVE_LOCK ){ - winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); -- if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ -+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile, 0) ){ - /* This should never happen. We should always be able to - ** reacquire the read lock */ - rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), -@@ -45277,6 +51020,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ - return SQLITE_OK; - } - #endif -+ case SQLITE_FCNTL_NULL_IO: { -+ (void)osCloseHandle(pFile->h); -+ pFile->h = NULL; -+ return SQLITE_OK; -+ } - case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = 0; - int rc = winGetTempname(pFile->pVfs, &zTFile); -@@ -45313,6 +51061,28 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ - return rc; - } - #endif -+ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ case SQLITE_FCNTL_LOCK_TIMEOUT: { -+ int iOld = pFile->iBusyTimeout; -+ int iNew = *(int*)pArg; -+#if SQLITE_ENABLE_SETLK_TIMEOUT==1 -+ pFile->iBusyTimeout = (iNew < 0) ? INFINITE : (DWORD)iNew; -+#elif SQLITE_ENABLE_SETLK_TIMEOUT==2 -+ pFile->iBusyTimeout = (DWORD)(!!iNew); -+#else -+# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" -+#endif -+ *(int*)pArg = iOld; -+ return SQLITE_OK; -+ } -+ case SQLITE_FCNTL_BLOCK_ON_CONNECT: { -+ int iNew = *(int*)pArg; -+ pFile->bBlockOnConnect = iNew; -+ return SQLITE_OK; -+ } -+#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ -+ - } - OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); - return SQLITE_NOTFOUND; -@@ -45338,7 +51108,7 @@ static int winSectorSize(sqlite3_file *id){ - */ - static int winDeviceCharacteristics(sqlite3_file *id){ - winFile *p = (winFile*)id; -- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | -+ return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ | - ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); - } - -@@ -45393,23 +51163,27 @@ static int winShmMutexHeld(void) { - ** - ** The following fields are read-only after the object is created: - ** --** fid - ** zFilename - ** - ** Either winShmNode.mutex must be held or winShmNode.nRef==0 and - ** winShmMutexHeld() is true when reading or writing any other field - ** in this structure. - ** -+** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate -+** the *-shm file if the DMS-locking protocol demands it, and (c) map -+** regions of the *-shm file into memory using MapViewOfFile() or -+** similar. Other locks are taken by individual clients using the -+** winShm.hShm handles. - */ - struct winShmNode { - sqlite3_mutex *mutex; /* Mutex to access this object */ - char *zFilename; /* Name of the file */ -- winFile hFile; /* File handle from winOpen */ -+ HANDLE hSharedShm; /* File handle open on zFilename */ - -+ int isUnlocked; /* DMS lock has not yet been obtained */ -+ int isReadonly; /* True if read-only */ - int szRegion; /* Size of shared-memory regions */ - int nRegion; /* Size of array apRegion */ -- u8 isReadonly; /* True if read-only */ -- u8 isUnlocked; /* True if no DMS lock held */ - - struct ShmRegion { - HANDLE hMap; /* File handle from CreateFileMapping */ -@@ -45418,7 +51192,6 @@ struct winShmNode { - DWORD lastErrno; /* The Windows errno from the last I/O error */ - - int nRef; /* Number of winShm objects pointing to this */ -- winShm *pFirst; /* All winShm objects pointing to this */ - winShmNode *pNext; /* Next in list of all winShmNode objects */ - #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) - u8 nextShmId; /* Next available winShm.id value */ -@@ -45434,23 +51207,15 @@ static winShmNode *winShmNodeList = 0; - - /* - ** Structure used internally by this VFS to record the state of an --** open shared memory connection. --** --** The following fields are initialized when this object is created and --** are read-only thereafter: --** --** winShm.pShmNode --** winShm.id --** --** All other fields are read/write. The winShm.pShmNode->mutex must be held --** while accessing any read/write fields. -+** open shared memory connection. There is one such structure for each -+** winFile open on a wal mode database. - */ - struct winShm { - winShmNode *pShmNode; /* The underlying winShmNode object */ -- winShm *pNext; /* Next winShm with the same winShmNode */ -- u8 hasMutex; /* True if holding the winShmNode mutex */ - u16 sharedMask; /* Mask of shared locks held */ - u16 exclMask; /* Mask of exclusive locks held */ -+ HANDLE hShm; /* File-handle on *-shm file. For locking. */ -+ int bReadonly; /* True if hShm is opened read-only */ - #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) - u8 id; /* Id of this connection with its winShmNode */ - #endif -@@ -45462,50 +51227,6 @@ struct winShm { - #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ - #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ - --/* --** Apply advisory locks for all n bytes beginning at ofst. --*/ --#define WINSHM_UNLCK 1 --#define WINSHM_RDLCK 2 --#define WINSHM_WRLCK 3 --static int winShmSystemLock( -- winShmNode *pFile, /* Apply locks to this open shared-memory segment */ -- int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */ -- int ofst, /* Offset to first byte to be locked/unlocked */ -- int nByte /* Number of bytes to lock or unlock */ --){ -- int rc = 0; /* Result code form Lock/UnlockFileEx() */ -- -- /* Access to the winShmNode object is serialized by the caller */ -- assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) ); -- -- OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", -- pFile->hFile.h, lockType, ofst, nByte)); -- -- /* Release/Acquire the system-level lock */ -- if( lockType==WINSHM_UNLCK ){ -- rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); -- }else{ -- /* Initialize the locking parameters */ -- DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; -- if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; -- rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); -- } -- -- if( rc!= 0 ){ -- rc = SQLITE_OK; -- }else{ -- pFile->lastErrno = osGetLastError(); -- rc = SQLITE_BUSY; -- } -- -- OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", -- pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" : -- "winLockFile", pFile->lastErrno, sqlite3ErrName(rc))); -- -- return rc; --} -- - /* Forward references to VFS methods */ - static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); - static int winDelete(sqlite3_vfs *,const char*,int); -@@ -45537,11 +51258,7 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ - osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); - UNUSED_VARIABLE_VALUE(bRc); - } -- if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ -- SimulateIOErrorBenign(1); -- winClose((sqlite3_file *)&p->hFile); -- SimulateIOErrorBenign(0); -- } -+ winHandleClose(p->hSharedShm); - if( deleteFlag ){ - SimulateIOErrorBenign(1); - sqlite3BeginBenignMalloc(); -@@ -45559,42 +51276,239 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ - } - - /* --** The DMS lock has not yet been taken on shm file pShmNode. Attempt to --** take it now. Return SQLITE_OK if successful, or an SQLite error --** code otherwise. --** --** If the DMS cannot be locked because this is a readonly_shm=1 --** connection and no other process already holds a lock, return --** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. -+** The DMS lock has not yet been taken on the shm file associated with -+** pShmNode. Take the lock. Truncate the *-shm file if required. -+** Return SQLITE_OK if successful, or an SQLite error code otherwise. - */ --static int winLockSharedMemory(winShmNode *pShmNode){ -- int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1); -+static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){ -+ HANDLE h = pShmNode->hSharedShm; -+ int rc = SQLITE_OK; - -+ assert( sqlite3_mutex_held(pShmNode->mutex) ); -+ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0); - if( rc==SQLITE_OK ){ -+ /* We have an EXCLUSIVE lock on the DMS byte. This means that this -+ ** is the first process to open the file. Truncate it to zero bytes -+ ** in this case. */ - if( pShmNode->isReadonly ){ -- pShmNode->isUnlocked = 1; -- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); -- return SQLITE_READONLY_CANTINIT; -- }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){ -- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); -- return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), -- "winLockSharedMemory", pShmNode->zFilename); -+ rc = SQLITE_READONLY_CANTINIT; -+ }else{ -+ rc = winHandleTruncate(h, 0); - } -+ -+ /* Release the EXCLUSIVE lock acquired above. */ -+ winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0); -+ }else if( (rc & 0xFF)==SQLITE_BUSY ){ -+ rc = SQLITE_OK; - } - - if( rc==SQLITE_OK ){ -- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); -+ /* Take a SHARED lock on the DMS byte. */ -+ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs); -+ if( rc==SQLITE_OK ){ -+ pShmNode->isUnlocked = 0; -+ } - } - -- return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1); -+ return rc; - } - -+ - /* --** Open the shared-memory area associated with database file pDbFd. -+** Convert a UTF-8 filename into whatever form the underlying -+** operating system wants filenames in. Space to hold the result -+** is obtained from malloc and must be freed by the calling -+** function -+** -+** On Cygwin, 3 possible input forms are accepted: -+** - If the filename starts with ":/" or ":\", -+** it is converted to UTF-16 as-is. -+** - If the filename contains '/', it is assumed to be a -+** Cygwin absolute path, it is converted to a win32 -+** absolute path in UTF-16. -+** - Otherwise it must be a filename only, the win32 filename -+** is returned in UTF-16. -+** Note: If the function cygwin_conv_path() fails, only -+** UTF-8 -> UTF-16 conversion will be done. This can only -+** happen when the file path >32k, in which case winUtf8ToUnicode() -+** will fail too. -+*/ -+static void *winConvertFromUtf8Filename(const char *zFilename){ -+ void *zConverted = 0; -+ if( osIsNT() ){ -+#ifdef __CYGWIN__ -+ int nChar; -+ LPWSTR zWideFilename; -+ -+ if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename) -+ && winIsDirSep(zFilename[2])) ){ -+ i64 nByte; -+ int convertflag = CCP_POSIX_TO_WIN_W; -+ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE; -+ nByte = (i64)osCygwin_conv_path(convertflag, -+ zFilename, 0, 0); -+ if( nByte>0 ){ -+ zConverted = sqlite3MallocZero(12+(u64)nByte); -+ if ( zConverted==0 ){ -+ return zConverted; -+ } -+ zWideFilename = zConverted; -+ /* Filenames should be prefixed, except when converted -+ * full path already starts with "\\?\". */ -+ if( osCygwin_conv_path(convertflag, zFilename, -+ zWideFilename+4, nByte)==0 ){ -+ if( (convertflag&CCP_RELATIVE) ){ -+ memmove(zWideFilename, zWideFilename+4, nByte); -+ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){ -+ memcpy(zWideFilename, L"\\\\?\\", 8); -+ }else if( zWideFilename[6]!='?' ){ -+ memmove(zWideFilename+6, zWideFilename+4, nByte); -+ memcpy(zWideFilename, L"\\\\?\\UNC", 14); -+ }else{ -+ memmove(zWideFilename, zWideFilename+4, nByte); -+ } -+ return zConverted; -+ } -+ sqlite3_free(zConverted); -+ } -+ } -+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); -+ if( nChar==0 ){ -+ return 0; -+ } -+ zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 ); -+ if( zWideFilename==0 ){ -+ return 0; -+ } -+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, -+ zWideFilename, nChar); -+ if( nChar==0 ){ -+ sqlite3_free(zWideFilename); -+ zWideFilename = 0; -+ }else if( nChar>MAX_PATH -+ && winIsDriveLetterAndColon(zFilename) -+ && winIsDirSep(zFilename[2]) ){ -+ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR)); -+ zWideFilename[2] = '\\'; -+ memcpy(zWideFilename, L"\\\\?\\", 8); -+ }else if( nChar>MAX_PATH -+ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1]) -+ && zFilename[2] != '?' ){ -+ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR)); -+ memcpy(zWideFilename, L"\\\\?\\UNC", 14); -+ } -+ zConverted = zWideFilename; -+#else -+ zConverted = winUtf8ToUnicode(zFilename); -+#endif /* __CYGWIN__ */ -+ } -+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32) -+ else{ -+ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); -+ } -+#endif -+ /* caller will handle out of memory */ -+ return zConverted; -+} -+ -+/* -+** This function is used to open a handle on a *-shm file. - ** --** When opening a new shared-memory file, if no other instances of that --** file are currently open, in this process or in other processes, then --** the file must be truncated to zero length or have its header cleared. -+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file -+** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not. -+*/ -+static int winHandleOpen( -+ const char *zUtf8, /* File to open */ -+ int *pbReadonly, /* IN/OUT: True for readonly handle */ -+ HANDLE *ph /* OUT: New HANDLE for file */ -+){ -+ int rc = SQLITE_OK; -+ void *zConverted = 0; -+ int bReadonly = *pbReadonly; -+ HANDLE h = INVALID_HANDLE_VALUE; -+ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ const DWORD flag_overlapped = FILE_FLAG_OVERLAPPED; -+#else -+ const DWORD flag_overlapped = 0; -+#endif -+ -+ /* Convert the filename to the system encoding. */ -+ zConverted = winConvertFromUtf8Filename(zUtf8); -+ if( zConverted==0 ){ -+ OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8)); -+ rc = SQLITE_IOERR_NOMEM_BKPT; -+ goto winopenfile_out; -+ } -+ -+ /* Ensure the file we are trying to open is not actually a directory. */ -+ if( winIsDir(zConverted) ){ -+ OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8)); -+ rc = SQLITE_CANTOPEN_ISDIR; -+ goto winopenfile_out; -+ } -+ -+ /* TODO: platforms. -+ ** TODO: retry-on-ioerr. -+ */ -+ if( osIsNT() ){ -+#if SQLITE_OS_WINRT -+ CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; -+ memset(&extendedParameters, 0, sizeof(extendedParameters)); -+ extendedParameters.dwSize = sizeof(extendedParameters); -+ extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; -+ extendedParameters.dwFileFlags = flag_overlapped; -+ extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; -+ h = osCreateFile2((LPCWSTR)zConverted, -+ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */ -+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ -+ OPEN_ALWAYS, /* dwCreationDisposition */ -+ &extendedParameters -+ ); -+#else -+ h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */ -+ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ -+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ -+ NULL, /* lpSecurityAttributes */ -+ OPEN_ALWAYS, /* dwCreationDisposition */ -+ FILE_ATTRIBUTE_NORMAL|flag_overlapped, -+ NULL -+ ); -+#endif -+ }else{ -+ /* Due to pre-processor directives earlier in this file, -+ ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */ -+#ifdef SQLITE_WIN32_HAS_ANSI -+ h = osCreateFileA((LPCSTR)zConverted, -+ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ -+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ -+ NULL, /* lpSecurityAttributes */ -+ OPEN_ALWAYS, /* dwCreationDisposition */ -+ FILE_ATTRIBUTE_NORMAL|flag_overlapped, -+ NULL -+ ); -+#endif -+ } -+ -+ if( h==INVALID_HANDLE_VALUE ){ -+ if( bReadonly==0 ){ -+ bReadonly = 1; -+ rc = winHandleOpen(zUtf8, &bReadonly, &h); -+ }else{ -+ rc = SQLITE_CANTOPEN_BKPT; -+ } -+ } -+ -+ winopenfile_out: -+ sqlite3_free(zConverted); -+ *pbReadonly = bReadonly; -+ *ph = h; -+ return rc; -+} -+ -+ -+/* -+** Open the shared-memory area associated with database file pDbFd. - */ - static int winOpenSharedMemory(winFile *pDbFd){ - struct winShm *p; /* The connection to be opened */ -@@ -45606,98 +51520,83 @@ static int winOpenSharedMemory(winFile *pDbFd){ - assert( pDbFd->pShm==0 ); /* Not previously opened */ - - /* Allocate space for the new sqlite3_shm object. Also speculatively -- ** allocate space for a new winShmNode and filename. -- */ -+ ** allocate space for a new winShmNode and filename. */ - p = sqlite3MallocZero( sizeof(*p) ); - if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; - nName = sqlite3Strlen30(pDbFd->zPath); -- pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); -+ pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 ); - if( pNew==0 ){ - sqlite3_free(p); - return SQLITE_IOERR_NOMEM_BKPT; - } - pNew->zFilename = (char*)&pNew[1]; -+ pNew->hSharedShm = INVALID_HANDLE_VALUE; -+ pNew->isUnlocked = 1; - sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); - sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); - -+ /* Open a file-handle on the *-shm file for this connection. This file-handle -+ ** is only used for locking. The mapping of the *-shm file is created using -+ ** the shared file handle in winShmNode.hSharedShm. */ -+ p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0); -+ rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm); -+ - /* Look to see if there is an existing winShmNode that can be used. -- ** If no matching winShmNode currently exists, create a new one. -- */ -+ ** If no matching winShmNode currently exists, then create a new one. */ - winShmEnterMutex(); - for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ - /* TBD need to come up with better match here. Perhaps -- ** use FILE_ID_BOTH_DIR_INFO Structure. -- */ -+ ** use FILE_ID_BOTH_DIR_INFO Structure. */ - if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; - } -- if( pShmNode ){ -- sqlite3_free(pNew); -- }else{ -- int inFlags = SQLITE_OPEN_WAL; -- int outFlags = 0; -- -+ if( pShmNode==0 ){ - pShmNode = pNew; -- pNew = 0; -- ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; -- pShmNode->pNext = winShmNodeList; -- winShmNodeList = pShmNode; - -+ /* Allocate a mutex for this winShmNode object, if one is required. */ - if( sqlite3GlobalConfig.bCoreMutex ){ - pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); -- if( pShmNode->mutex==0 ){ -- rc = SQLITE_IOERR_NOMEM_BKPT; -- goto shm_open_err; -- } -+ if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT; - } - -- if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ -- inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; -- }else{ -- inFlags |= SQLITE_OPEN_READONLY; -- } -- rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, -- (sqlite3_file*)&pShmNode->hFile, -- inFlags, &outFlags); -- if( rc!=SQLITE_OK ){ -- rc = winLogError(rc, osGetLastError(), "winOpenShm", -- pShmNode->zFilename); -- goto shm_open_err; -+ /* Open a file-handle to use for mappings, and for the DMS lock. */ -+ if( rc==SQLITE_OK ){ -+ HANDLE h = INVALID_HANDLE_VALUE; -+ pShmNode->isReadonly = p->bReadonly; -+ rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h); -+ pShmNode->hSharedShm = h; - } -- if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1; - -- rc = winLockSharedMemory(pShmNode); -- if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; -+ /* If successful, link the new winShmNode into the global list. If an -+ ** error occurred, free the object. */ -+ if( rc==SQLITE_OK ){ -+ pShmNode->pNext = winShmNodeList; -+ winShmNodeList = pShmNode; -+ pNew = 0; -+ }else{ -+ sqlite3_mutex_free(pShmNode->mutex); -+ if( pShmNode->hSharedShm!=INVALID_HANDLE_VALUE ){ -+ osCloseHandle(pShmNode->hSharedShm); -+ } -+ } - } - -- /* Make the new connection a child of the winShmNode */ -- p->pShmNode = pShmNode; -+ /* If no error has occurred, link the winShm object to the winShmNode and -+ ** the winShm to pDbFd. */ -+ if( rc==SQLITE_OK ){ -+ p->pShmNode = pShmNode; -+ pShmNode->nRef++; - #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) -- p->id = pShmNode->nextShmId++; -+ p->id = pShmNode->nextShmId++; - #endif -- pShmNode->nRef++; -- pDbFd->pShm = p; -- winShmLeaveMutex(); -- -- /* The reference count on pShmNode has already been incremented under -- ** the cover of the winShmEnterMutex() mutex and the pointer from the -- ** new (struct winShm) object to the pShmNode has been set. All that is -- ** left to do is to link the new object into the linked list starting -- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex -- ** mutex. -- */ -- sqlite3_mutex_enter(pShmNode->mutex); -- p->pNext = pShmNode->pFirst; -- pShmNode->pFirst = p; -- sqlite3_mutex_leave(pShmNode->mutex); -- return rc; -+ pDbFd->pShm = p; -+ }else if( p ){ -+ winHandleClose(p->hShm); -+ sqlite3_free(p); -+ } - -- /* Jump here on any error */ --shm_open_err: -- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); -- winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ -- sqlite3_free(p); -- sqlite3_free(pNew); -+ assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 ); - winShmLeaveMutex(); -+ sqlite3_free(pNew); - return rc; - } - -@@ -45712,27 +51611,19 @@ static int winShmUnmap( - winFile *pDbFd; /* Database holding shared-memory */ - winShm *p; /* The connection to be closed */ - winShmNode *pShmNode; /* The underlying shared-memory file */ -- winShm **pp; /* For looping over sibling connections */ - - pDbFd = (winFile*)fd; - p = pDbFd->pShm; - if( p==0 ) return SQLITE_OK; -- pShmNode = p->pShmNode; -- -- /* Remove connection p from the set of connections associated -- ** with pShmNode */ -- sqlite3_mutex_enter(pShmNode->mutex); -- for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} -- *pp = p->pNext; -+ if( p->hShm!=INVALID_HANDLE_VALUE ){ -+ osCloseHandle(p->hShm); -+ } - -- /* Free the connection p */ -- sqlite3_free(p); -- pDbFd->pShm = 0; -- sqlite3_mutex_leave(pShmNode->mutex); -+ pShmNode = p->pShmNode; -+ winShmEnterMutex(); - - /* If pShmNode->nRef has reached 0, then close the underlying -- ** shared-memory file, too */ -- winShmEnterMutex(); -+ ** shared-memory file, too. */ - assert( pShmNode->nRef>0 ); - pShmNode->nRef--; - if( pShmNode->nRef==0 ){ -@@ -45740,6 +51631,9 @@ static int winShmUnmap( - } - winShmLeaveMutex(); - -+ /* Free the connection p */ -+ sqlite3_free(p); -+ pDbFd->pShm = 0; - return SQLITE_OK; - } - -@@ -45754,10 +51648,13 @@ static int winShmLock( - ){ - winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ - winShm *p = pDbFd->pShm; /* The shared memory being locked */ -- winShm *pX; /* For looping over all siblings */ -- winShmNode *pShmNode = p->pShmNode; -+ winShmNode *pShmNode; - int rc = SQLITE_OK; /* Result code */ -- u16 mask; /* Mask of locks to take or release */ -+ u16 mask = (u16)((1U<<(ofst+n)) - (1U<pShmNode; -+ if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; - - assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); - assert( n>=1 ); -@@ -45767,85 +51664,81 @@ static int winShmLock( - || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); - assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); - -- mask = (u16)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex); -- if( flags & SQLITE_SHM_UNLOCK ){ -- u16 allMask = 0; /* Mask of locks held by siblings */ -- -- /* See if any siblings hold this same lock */ -- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ -- if( pX==p ) continue; -- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); -- allMask |= pX->sharedMask; -- } -+ /* Check that, if this to be a blocking lock, no locks that occur later -+ ** in the following list than the lock being obtained are already held: -+ ** -+ ** 1. Recovery lock (ofst==2). -+ ** 2. Checkpointer lock (ofst==1). -+ ** 3. Write lock (ofst==0). -+ ** 4. Read locks (ofst>=3 && ofstexclMask|p->sharedMask); -+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( -+ (ofst!=2 || lockMask==0) -+ && (ofst!=1 || lockMask==0 || lockMask==2) -+ && (ofst!=0 || lockMask<3) -+ && (ofst<3 || lockMask<(1<exclMask & mask) -+ ); -+ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) -+ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) -+ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) -+ ){ - -- /* Undo the local locks */ -- if( rc==SQLITE_OK ){ -- p->exclMask &= ~mask; -- p->sharedMask &= ~mask; -- } -- }else if( flags & SQLITE_SHM_SHARED ){ -- u16 allShared = 0; /* Union of locks held by connections other than "p" */ -+ if( flags & SQLITE_SHM_UNLOCK ){ -+ /* Case (a) - unlock. */ - -- /* Find out which shared locks are already held by sibling connections. -- ** If any sibling already holds an exclusive lock, go ahead and return -- ** SQLITE_BUSY. -- */ -- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ -- if( (pX->exclMask & mask)!=0 ){ -- rc = SQLITE_BUSY; -- break; -- } -- allShared |= pX->sharedMask; -- } -+ assert( (p->exclMask & p->sharedMask)==0 ); -+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); -+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); - -- /* Get shared locks at the system level, if necessary */ -- if( rc==SQLITE_OK ){ -- if( (allShared & mask)==0 ){ -- rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n); -- }else{ -- rc = SQLITE_OK; -- } -- } -+ rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n); - -- /* Get the local shared locks */ -- if( rc==SQLITE_OK ){ -- p->sharedMask |= mask; -- } -- }else{ -- /* Make sure no sibling connections hold locks that will block this -- ** lock. If any do, return SQLITE_BUSY right away. -- */ -- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ -- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ -- rc = SQLITE_BUSY; -- break; -+ /* If successful, also clear the bits in sharedMask/exclMask */ -+ if( rc==SQLITE_OK ){ -+ p->exclMask = (p->exclMask & ~mask); -+ p->sharedMask = (p->sharedMask & ~mask); - } -- } -- -- /* Get the exclusive locks at the system level. Then if successful -- ** also mark the local connection as being locked. -- */ -- if( rc==SQLITE_OK ){ -- rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n); -+ }else{ -+ int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0); -+ DWORD nMs = winFileBusyTimeout(pDbFd); -+ rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs); - if( rc==SQLITE_OK ){ -- assert( (p->sharedMask & mask)==0 ); -- p->exclMask |= mask; -+ if( bExcl ){ -+ p->exclMask = (p->exclMask | mask); -+ }else{ -+ p->sharedMask = (p->sharedMask | mask); -+ } - } - } - } -- sqlite3_mutex_leave(pShmNode->mutex); -- OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", -- osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, -- sqlite3ErrName(rc))); -+ -+ OSTRACE(( -+ "SHM-LOCK(%d,%d,%d) pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x," -+ " rc=%s\n", -+ ofst, n, flags, -+ osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, -+ sqlite3ErrName(rc)) -+ ); - return rc; - } - -@@ -45907,13 +51800,15 @@ static int winShmMap( - - sqlite3_mutex_enter(pShmNode->mutex); - if( pShmNode->isUnlocked ){ -- rc = winLockSharedMemory(pShmNode); -+ /* Take the DMS lock. */ -+ assert( pShmNode->nRegion==0 ); -+ rc = winLockSharedMemory(pShmNode, winFileBusyTimeout(pDbFd)); - if( rc!=SQLITE_OK ) goto shmpage_out; -- pShmNode->isUnlocked = 0; - } -- assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); - -+ assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); - if( pShmNode->nRegion<=iRegion ){ -+ HANDLE hShared = pShmNode->hSharedShm; - struct ShmRegion *apNew; /* New aRegion[] array */ - int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ - sqlite3_int64 sz; /* Current size of wal-index file */ -@@ -45924,10 +51819,9 @@ static int winShmMap( - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ -- rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); -+ rc = winHandleSize(hShared, &sz); - if( rc!=SQLITE_OK ){ -- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), -- "winShmMap1", pDbFd->zPath); -+ rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath); - goto shmpage_out; - } - -@@ -45936,19 +51830,17 @@ static int winShmMap( - ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. - ** - ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate -- ** the requested memory region. -- */ -+ ** the requested memory region. */ - if( !isWrite ) goto shmpage_out; -- rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); -+ rc = winHandleTruncate(hShared, nByte); - if( rc!=SQLITE_OK ){ -- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), -- "winShmMap2", pDbFd->zPath); -+ rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath); - goto shmpage_out; - } - } - - /* Map the requested memory region into this processes address space. */ -- apNew = (struct ShmRegion *)sqlite3_realloc64( -+ apNew = (struct ShmRegion*)sqlite3_realloc64( - pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) - ); - if( !apNew ){ -@@ -45967,18 +51859,13 @@ static int winShmMap( - void *pMap = 0; /* Mapped memory region */ - - #if SQLITE_OS_WINRT -- hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, -- NULL, protect, nByte, NULL -- ); -+ hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL); - #elif defined(SQLITE_WIN32_HAS_WIDE) -- hMap = osCreateFileMappingW(pShmNode->hFile.h, -- NULL, protect, 0, nByte, NULL -- ); -+ hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL); - #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA -- hMap = osCreateFileMappingA(pShmNode->hFile.h, -- NULL, protect, 0, nByte, NULL -- ); -+ hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL); - #endif -+ - OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", - osGetCurrentProcessId(), pShmNode->nRegion, nByte, - hMap ? "ok" : "failed")); -@@ -46021,7 +51908,9 @@ shmpage_out: - }else{ - *pp = 0; - } -- if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; -+ if( pShmNode->isReadonly && rc==SQLITE_OK ){ -+ rc = SQLITE_READONLY; -+ } - sqlite3_mutex_leave(pShmNode->mutex); - return rc; - } -@@ -46195,6 +52084,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ - - #if SQLITE_MAX_MMAP_SIZE>0 - if( pFd->mmapSizeMax>0 ){ -+ /* Ensure that there is always at least a 256 byte buffer of addressable -+ ** memory following the returned page. If the database is corrupt, -+ ** SQLite may overread the page slightly (in practice only a few bytes, -+ ** but 256 is safe, round, number). */ -+ const int nEofBuffer = 256; - if( pFd->pMapRegion==0 ){ - int rc = winMapfile(pFd, -1); - if( rc!=SQLITE_OK ){ -@@ -46203,7 +52097,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ - return rc; - } - } -- if( pFd->mmapSize >= iOff+nAmt ){ -+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ - assert( pFd->pMapRegion!=0 ); - *pp = &((u8 *)pFd->pMapRegion)[iOff]; - pFd->nFetchOut++; -@@ -46336,47 +52230,6 @@ static winVfsAppData winNolockAppData = { - ** sqlite3_vfs object. - */ - --#if defined(__CYGWIN__) --/* --** Convert a filename from whatever the underlying operating system --** supports for filenames into UTF-8. Space to hold the result is --** obtained from malloc and must be freed by the calling function. --*/ --static char *winConvertToUtf8Filename(const void *zFilename){ -- char *zConverted = 0; -- if( osIsNT() ){ -- zConverted = winUnicodeToUtf8(zFilename); -- } --#ifdef SQLITE_WIN32_HAS_ANSI -- else{ -- zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI()); -- } --#endif -- /* caller will handle out of memory */ -- return zConverted; --} --#endif -- --/* --** Convert a UTF-8 filename into whatever form the underlying --** operating system wants filenames in. Space to hold the result --** is obtained from malloc and must be freed by the calling --** function. --*/ --static void *winConvertFromUtf8Filename(const char *zFilename){ -- void *zConverted = 0; -- if( osIsNT() ){ -- zConverted = winUtf8ToUnicode(zFilename); -- } --#ifdef SQLITE_WIN32_HAS_ANSI -- else{ -- zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); -- } --#endif -- /* caller will handle out of memory */ -- return zConverted; --} -- - /* - ** This function returns non-zero if the specified UTF-8 string buffer - ** ends with a directory separator character or one was successfully -@@ -46389,7 +52242,14 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){ - if( winIsDirSep(zBuf[nLen-1]) ){ - return 1; - }else if( nLen+1mxPathname; nBuf = nMax + 2; -+ nMax = pVfs->mxPathname; -+ nBuf = 2 + (i64)nMax; - zBuf = sqlite3MallocZero( nBuf ); - if( !zBuf ){ - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); -@@ -46434,22 +52309,25 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ - */ - nDir = nMax - (nPre + 15); - assert( nDir>0 ); -- if( sqlite3_temp_directory ){ -+ if( winTempDirDefined() ){ - int nDirLen = sqlite3Strlen30(sqlite3_temp_directory); - if( nDirLen>0 ){ - if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){ - nDirLen++; - } - if( nDirLen>nDir ){ -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); - return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0); - } - sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory); - } -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - } -+ - #if defined(__CYGWIN__) -- else{ -+ else if( osGetenv!=NULL ){ - static const char *azDirs[] = { - 0, /* getenv("SQLITE_TMPDIR") */ - 0, /* getenv("TMPDIR") */ -@@ -46465,11 +52343,11 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ - unsigned int i; - const char *zDir = 0; - -- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); -- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); -- if( !azDirs[2] ) azDirs[2] = getenv("TMP"); -- if( !azDirs[3] ) azDirs[3] = getenv("TEMP"); -- if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE"); -+ if( !azDirs[0] ) azDirs[0] = osGetenv("SQLITE_TMPDIR"); -+ if( !azDirs[1] ) azDirs[1] = osGetenv("TMPDIR"); -+ if( !azDirs[2] ) azDirs[2] = osGetenv("TMP"); -+ if( !azDirs[3] ) azDirs[3] = osGetenv("TEMP"); -+ if( !azDirs[4] ) azDirs[4] = osGetenv("USERPROFILE"); - for(i=0; i>= 8; - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; -@@ -46649,7 +52498,7 @@ static int winIsDir(const void *zConverted){ - return 0; /* Invalid name? */ - } - attr = sAttrData.dwFileAttributes; --#if SQLITE_OS_WINCE==0 -+#if SQLITE_OS_WINCE==0 && defined(SQLITE_WIN32_HAS_ANSI) - }else{ - attr = osGetFileAttributesA((char*)zConverted); - #endif -@@ -46665,6 +52514,12 @@ static int winAccess( - int *pResOut /* OUT: Result */ - ); - -+/* -+** The Windows version of xAccess() accepts an extra bit in the flags -+** parameter that prevents an anti-virus retry loop. -+*/ -+#define NORETRY 0x4000 -+ - /* - ** Open a file. - */ -@@ -46689,6 +52544,7 @@ static int winOpen( - void *zConverted; /* Filename in OS encoding */ - const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ - int cnt = 0; -+ int isRO = 0; /* file is known to be accessible readonly */ - - /* If argument zPath is a NULL pointer, this function is required to open - ** a temporary file. Use this buffer to store the file name in. -@@ -46697,7 +52553,7 @@ static int winOpen( - - int rc = SQLITE_OK; /* Function Return Code */ - #if !defined(NDEBUG) || SQLITE_OS_WINCE -- int eType = flags&0xFFFFFF00; /* Type of file to open */ -+ int eType = flags&0x0FFF00; /* Type of file to open */ - #endif - - int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); -@@ -46811,7 +52667,11 @@ static int winOpen( - dwCreationDisposition = OPEN_EXISTING; - } - -- dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; -+ if( 0==sqlite3_uri_boolean(zName, "exclusive", 0) ){ -+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; -+ }else{ -+ dwShareMode = 0; -+ } - - if( isDelete ){ - #if SQLITE_OS_WINCE -@@ -46849,9 +52709,9 @@ static int winOpen( - &extendedParameters); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ -- int rc2, isRO = 0; -+ int rc2; - sqlite3BeginBenignMalloc(); -- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); -+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); - sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } -@@ -46866,9 +52726,9 @@ static int winOpen( - NULL); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ -- int rc2, isRO = 0; -+ int rc2; - sqlite3BeginBenignMalloc(); -- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); -+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); - sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } -@@ -46886,9 +52746,9 @@ static int winOpen( - NULL); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ -- int rc2, isRO = 0; -+ int rc2; - sqlite3BeginBenignMalloc(); -- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); -+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); - sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } -@@ -46903,7 +52763,7 @@ static int winOpen( - if( h==INVALID_HANDLE_VALUE ){ - sqlite3_free(zConverted); - sqlite3_free(zTmpname); -- if( isReadWrite && !isExclusive ){ -+ if( isReadWrite && isRO && !isExclusive ){ - return winOpen(pVfs, zName, id, - ((flags|SQLITE_OPEN_READONLY) & - ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), -@@ -47105,12 +52965,25 @@ static int winAccess( - int rc = 0; - DWORD lastErrno = 0; - void *zConverted; -+ int noRetry = 0; /* Do not use winRetryIoerr() */ - UNUSED_PARAMETER(pVfs); - -+ if( (flags & NORETRY)!=0 ){ -+ noRetry = 1; -+ flags &= ~NORETRY; -+ } -+ - SimulateIOError( return SQLITE_IOERR_ACCESS; ); - OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", - zFilename, flags, pResOut)); - -+ if( zFilename==0 ){ -+ *pResOut = 0; -+ OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", -+ zFilename, pResOut, *pResOut)); -+ return SQLITE_OK; -+ } -+ - zConverted = winConvertFromUtf8Filename(zFilename); - if( zConverted==0 ){ - OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); -@@ -47122,7 +52995,10 @@ static int winAccess( - memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, - GetFileExInfoStandard, -- &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} -+ &sAttrData)) -+ && !noRetry -+ && winRetryIoerr(&cnt, &lastErrno) -+ ){ /* Loop until true */} - if( rc ){ - /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file - ** as if it does not exist. -@@ -47190,6 +53066,7 @@ static BOOL winIsDriveLetterAndColon( - return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ); - } - -+#ifdef _WIN32 - /* - ** Returns non-zero if the specified path name should be used verbatim. If - ** non-zero is returned from this function, the calling function must simply -@@ -47226,20 +53103,84 @@ static BOOL winIsVerbatimPathname( - */ - return FALSE; - } -+#endif /* _WIN32 */ -+ -+#ifdef __CYGWIN__ -+/* -+** Simplify a filename into its canonical form -+** by making the following changes: -+** -+** * convert any '/' to '\' (win32) or reverse (Cygwin) -+** * removing any trailing and duplicate / (except for UNC paths) -+** * convert /./ into just / -+** -+** Changes are made in-place. Return the new name length. -+** -+** The original filename is in z[0..]. If the path is shortened, -+** no-longer used bytes will be written by '\0'. -+*/ -+static void winSimplifyName(char *z){ -+ int i, j; -+ for(i=j=0; z[i]; ++i){ -+ if( winIsDirSep(z[i]) ){ -+#if !defined(SQLITE_TEST) -+ /* Some test-cases assume that "./foo" and "foo" are different */ -+ if( z[i+1]=='.' && winIsDirSep(z[i+2]) ){ -+ ++i; -+ continue; -+ } -+#endif -+ if( !z[i+1] || (winIsDirSep(z[i+1]) && (i!=0)) ){ -+ continue; -+ } -+ z[j++] = osGetenv?'/':'\\'; -+ }else{ -+ z[j++] = z[i]; -+ } -+ } -+ while(jnOut ){ -+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer -+ ** even if it returns an error. */ -+ zOut[iOff] = '\0'; -+ return SQLITE_CANTOPEN_BKPT; -+ } -+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); -+ return SQLITE_OK; -+} -+#endif /* __CYGWIN__ */ - - /* - ** Turn a relative pathname into a full pathname. Write the full - ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname - ** bytes in size. - */ --static int winFullPathname( -+static int winFullPathnameNoMutex( - sqlite3_vfs *pVfs, /* Pointer to vfs object */ - const char *zRelative, /* Possibly relative input path */ - int nFull, /* Size of output buffer in bytes */ - char *zFull /* Output buffer */ - ){ --#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) -- DWORD nByte; -+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT -+ int nByte; - void *zConverted; - char *zOut; - #endif -@@ -47252,64 +53193,82 @@ static int winFullPathname( - zRelative++; - } - --#if defined(__CYGWIN__) - SimulateIOError( return SQLITE_ERROR ); -- UNUSED_PARAMETER(nFull); -- assert( nFull>=pVfs->mxPathname ); -- if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ -- /* -- ** NOTE: We are dealing with a relative path name and the data -- ** directory has been set. Therefore, use it as the basis -- ** for converting the relative path name to an absolute -- ** one by prepending the data directory and a slash. -- */ -- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); -- if( !zOut ){ -- return SQLITE_IOERR_NOMEM_BKPT; -- } -- if( cygwin_conv_path( -- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | -- CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ -- sqlite3_free(zOut); -- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, -- "winFullPathname1", zRelative); -- }else{ -- char *zUtf8 = winConvertToUtf8Filename(zOut); -- if( !zUtf8 ){ -- sqlite3_free(zOut); -- return SQLITE_IOERR_NOMEM_BKPT; -- } -- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", -- sqlite3_data_directory, winGetDirSep(), zUtf8); -- sqlite3_free(zUtf8); -- sqlite3_free(zOut); -- } -- }else{ -- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); -- if( !zOut ){ -- return SQLITE_IOERR_NOMEM_BKPT; -- } -- if( cygwin_conv_path( -- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), -- zRelative, zOut, pVfs->mxPathname+1)<0 ){ -- sqlite3_free(zOut); -- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, -- "winFullPathname2", zRelative); -- }else{ -- char *zUtf8 = winConvertToUtf8Filename(zOut); -- if( !zUtf8 ){ -- sqlite3_free(zOut); -- return SQLITE_IOERR_NOMEM_BKPT; -- } -- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); -- sqlite3_free(zUtf8); -- sqlite3_free(zOut); -+ -+#ifdef __CYGWIN__ -+ if( osGetcwd ){ -+ zFull[nFull-1] = '\0'; -+ if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){ -+ int rc = SQLITE_OK; -+ int nLink = 1; /* Number of symbolic links followed so far */ -+ const char *zIn = zRelative; /* Input path for each iteration of loop */ -+ char *zDel = 0; -+ struct stat buf; -+ -+ UNUSED_PARAMETER(pVfs); -+ -+ do { -+ /* Call lstat() on path zIn. Set bLink to true if the path is a symbolic -+ ** link, or false otherwise. */ -+ int bLink = 0; -+ if( osLstat && osReadlink ) { -+ if( osLstat(zIn, &buf)!=0 ){ -+ int myErrno = osErrno; -+ if( myErrno!=ENOENT ){ -+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, "lstat", zIn); -+ } -+ }else{ -+ bLink = ((buf.st_mode & 0170000) == 0120000); -+ } -+ -+ if( bLink ){ -+ if( zDel==0 ){ -+ zDel = sqlite3MallocZero(nFull); -+ if( zDel==0 ) rc = SQLITE_NOMEM; -+ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){ -+ rc = SQLITE_CANTOPEN_BKPT; -+ } -+ -+ if( rc==SQLITE_OK ){ -+ nByte = osReadlink(zIn, zDel, nFull-1); -+ if( nByte ==(DWORD)-1 ){ -+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "readlink", zIn); -+ }else{ -+ if( zDel[0]!='/' ){ -+ int n; -+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); -+ if( nByte+n+1>nFull ){ -+ rc = SQLITE_CANTOPEN_BKPT; -+ }else{ -+ memmove(&zDel[n], zDel, nByte+1); -+ memcpy(zDel, zIn, n); -+ nByte += n; -+ } -+ } -+ zDel[nByte] = '\0'; -+ } -+ } -+ -+ zIn = zDel; -+ } -+ } -+ -+ assert( rc!=SQLITE_OK || zIn!=zFull || zIn[0]=='/' ); -+ if( rc==SQLITE_OK && zIn!=zFull ){ -+ rc = mkFullPathname(zIn, zFull, nFull); -+ } -+ if( bLink==0 ) break; -+ zIn = zFull; -+ }while( rc==SQLITE_OK ); -+ -+ sqlite3_free(zDel); -+ winSimplifyName(zFull); -+ return rc; - } - } -- return SQLITE_OK; --#endif -+#endif /* __CYGWIN__ */ - --#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__) -+#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && defined(_WIN32) - SimulateIOError( return SQLITE_ERROR ); - /* WinCE has no concept of a relative pathname, or so I am told. */ - /* WinRT has no way to convert a relative path to an absolute one. */ -@@ -47328,7 +53287,8 @@ static int winFullPathname( - return SQLITE_OK; - #endif - --#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) -+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT -+#if defined(_WIN32) - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. This function could fail if, for example, the -@@ -47346,6 +53306,7 @@ static int winFullPathname( - sqlite3_data_directory, winGetDirSep(), zRelative); - return SQLITE_OK; - } -+#endif - zConverted = winConvertFromUtf8Filename(zRelative); - if( zConverted==0 ){ - return SQLITE_IOERR_NOMEM_BKPT; -@@ -47384,13 +53345,12 @@ static int winFullPathname( - return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), - "winFullPathname3", zRelative); - } -- nByte += 3; -- zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); -+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) + 3*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - sqlite3_free(zConverted); - return SQLITE_IOERR_NOMEM_BKPT; - } -- nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); -+ nByte = osGetFullPathNameA((char*)zConverted, nByte+3, zTemp, 0); - if( nByte==0 ){ - sqlite3_free(zConverted); - sqlite3_free(zTemp); -@@ -47403,7 +53363,26 @@ static int winFullPathname( - } - #endif - if( zOut ){ -+#ifdef __CYGWIN__ -+ if( memcmp(zOut, "\\\\?\\", 4) ){ -+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); -+ }else if( memcmp(zOut+4, "UNC\\", 4) ){ -+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+4); -+ }else{ -+ char *p = zOut+6; -+ *p = '\\'; -+ if( osGetcwd ){ -+ /* On Cygwin, UNC paths use forward slashes */ -+ while( *p ){ -+ if( *p=='\\' ) *p = '/'; -+ ++p; -+ } -+ } -+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+6); -+ } -+#else - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); -+#endif /* __CYGWIN__ */ - sqlite3_free(zOut); - return SQLITE_OK; - }else{ -@@ -47411,6 +53390,20 @@ static int winFullPathname( - } - #endif - } -+static int winFullPathname( -+ sqlite3_vfs *pVfs, /* Pointer to vfs object */ -+ const char *zRelative, /* Possibly relative input path */ -+ int nFull, /* Size of output buffer in bytes */ -+ char *zFull /* Output buffer */ -+){ -+ int rc; -+ MUTEX_LOGIC( sqlite3_mutex *pMutex; ) -+ MUTEX_LOGIC( pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); ) -+ sqlite3_mutex_enter(pMutex); -+ rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull); -+ sqlite3_mutex_leave(pMutex); -+ return rc; -+} - - #ifndef SQLITE_OMIT_LOAD_EXTENSION - /* -@@ -47419,25 +53412,8 @@ static int winFullPathname( - */ - static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ - HANDLE h; --#if defined(__CYGWIN__) -- int nFull = pVfs->mxPathname+1; -- char *zFull = sqlite3MallocZero( nFull ); -- void *zConverted = 0; -- if( zFull==0 ){ -- OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); -- return 0; -- } -- if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){ -- sqlite3_free(zFull); -- OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); -- return 0; -- } -- zConverted = winConvertFromUtf8Filename(zFull); -- sqlite3_free(zFull); --#else - void *zConverted = winConvertFromUtf8Filename(zFilename); - UNUSED_PARAMETER(pVfs); --#endif - if( zConverted==0 ){ - OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); - return 0; -@@ -47786,7 +53762,7 @@ SQLITE_API int sqlite3_os_init(void){ - - /* Double-check that the aSyscall[] array has been constructed - ** correctly. See ticket [bb3a86e890c8e96ab] */ -- assert( ArraySize(aSyscall)==80 ); -+ assert( ArraySize(aSyscall)==89 ); - - /* get memory map allocation granularity */ - memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); -@@ -47855,31 +53831,88 @@ SQLITE_API int sqlite3_os_end(void){ - ** sqlite3_deserialize(). - */ - /* #include "sqliteInt.h" */ --#ifdef SQLITE_ENABLE_DESERIALIZE -+#ifndef SQLITE_OMIT_DESERIALIZE - - /* - ** Forward declaration of objects used by this utility - */ - typedef struct sqlite3_vfs MemVfs; - typedef struct MemFile MemFile; -+typedef struct MemStore MemStore; - - /* Access to a lower-level VFS that (might) implement dynamic loading, - ** access to randomness, etc. - */ - #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) - --/* An open file */ --struct MemFile { -- sqlite3_file base; /* IO methods */ -+/* Storage for a memdb file. -+** -+** An memdb object can be shared or separate. Shared memdb objects can be -+** used by more than one database connection. Mutexes are used by shared -+** memdb objects to coordinate access. Separate memdb objects are only -+** connected to a single database connection and do not require additional -+** mutexes. -+** -+** Shared memdb objects have .zFName!=0 and .pMutex!=0. They are created -+** using "file:/name?vfs=memdb". The first character of the name must be -+** "/" or else the object will be a separate memdb object. All shared -+** memdb objects are stored in memdb_g.apMemStore[] in an arbitrary order. -+** -+** Separate memdb objects are created using a name that does not begin -+** with "/" or using sqlite3_deserialize(). -+** -+** Access rules for shared MemStore objects: -+** -+** * .zFName is initialized when the object is created and afterwards -+** is unchanged until the object is destroyed. So it can be accessed -+** at any time as long as we know the object is not being destroyed, -+** which means while either the SQLITE_MUTEX_STATIC_VFS1 or -+** .pMutex is held or the object is not part of memdb_g.apMemStore[]. -+** -+** * Can .pMutex can only be changed while holding the -+** SQLITE_MUTEX_STATIC_VFS1 mutex or while the object is not part -+** of memdb_g.apMemStore[]. -+** -+** * Other fields can only be changed while holding the .pMutex mutex -+** or when the .nRef is less than zero and the object is not part of -+** memdb_g.apMemStore[]. -+** -+** * The .aData pointer has the added requirement that it can can only -+** be changed (for resizing) when nMmap is zero. -+** -+*/ -+struct MemStore { - sqlite3_int64 sz; /* Size of the file */ - sqlite3_int64 szAlloc; /* Space allocated to aData */ - sqlite3_int64 szMax; /* Maximum allowed size of the file */ - unsigned char *aData; /* content of the file */ -+ sqlite3_mutex *pMutex; /* Used by shared stores only */ - int nMmap; /* Number of memory mapped pages */ - unsigned mFlags; /* Flags */ -+ int nRdLock; /* Number of readers */ -+ int nWrLock; /* Number of writers. (Always 0 or 1) */ -+ int nRef; /* Number of users of this MemStore */ -+ char *zFName; /* The filename for shared stores */ -+}; -+ -+/* An open file */ -+struct MemFile { -+ sqlite3_file base; /* IO methods */ -+ MemStore *pStore; /* The storage */ - int eLock; /* Most recent lock against this file */ - }; - -+/* -+** File-scope variables for holding the memdb files that are accessible -+** to multiple database connections in separate threads. -+** -+** Must hold SQLITE_MUTEX_STATIC_VFS1 to access any part of this object. -+*/ -+static struct MemFS { -+ int nMemStore; /* Number of shared MemStore objects */ -+ MemStore **apMemStore; /* Array of all shared MemStore objects */ -+} memdb_g; -+ - /* - ** Methods for MemFile - */ -@@ -47890,6 +53923,7 @@ static int memdbTruncate(sqlite3_file*, sqlite3_int64 size); - static int memdbSync(sqlite3_file*, int flags); - static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize); - static int memdbLock(sqlite3_file*, int); -+static int memdbUnlock(sqlite3_file*, int); - /* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */ - static int memdbFileControl(sqlite3_file*, int op, void *pArg); - /* static int memdbSectorSize(sqlite3_file*); // not used */ -@@ -47933,7 +53967,10 @@ static sqlite3_vfs memdb_vfs = { - memdbSleep, /* xSleep */ - 0, /* memdbCurrentTime, */ /* xCurrentTime */ - memdbGetLastError, /* xGetLastError */ -- memdbCurrentTimeInt64 /* xCurrentTimeInt64 */ -+ memdbCurrentTimeInt64, /* xCurrentTimeInt64 */ -+ 0, /* xSetSystemCall */ -+ 0, /* xGetSystemCall */ -+ 0, /* xNextSystemCall */ - }; - - static const sqlite3_io_methods memdb_io_methods = { -@@ -47945,7 +53982,7 @@ static const sqlite3_io_methods memdb_io_methods = { - memdbSync, /* xSync */ - memdbFileSize, /* xFileSize */ - memdbLock, /* xLock */ -- memdbLock, /* xUnlock - same as xLock in this case */ -+ memdbUnlock, /* xUnlock */ - 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */ - memdbFileControl, /* xFileControl */ - 0, /* memdbSectorSize,*/ /* xSectorSize */ -@@ -47958,17 +53995,68 @@ static const sqlite3_io_methods memdb_io_methods = { - memdbUnfetch /* xUnfetch */ - }; - -+/* -+** Enter/leave the mutex on a MemStore -+*/ -+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 -+static void memdbEnter(MemStore *p){ -+ UNUSED_PARAMETER(p); -+} -+static void memdbLeave(MemStore *p){ -+ UNUSED_PARAMETER(p); -+} -+#else -+static void memdbEnter(MemStore *p){ -+ sqlite3_mutex_enter(p->pMutex); -+} -+static void memdbLeave(MemStore *p){ -+ sqlite3_mutex_leave(p->pMutex); -+} -+#endif -+ - - - /* - ** Close an memdb-file. --** --** The pData pointer is owned by the application, so there is nothing --** to free. -+** Free the underlying MemStore object when its refcount drops to zero -+** or less. - */ - static int memdbClose(sqlite3_file *pFile){ -- MemFile *p = (MemFile *)pFile; -- if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData); -+ MemStore *p = ((MemFile*)pFile)->pStore; -+ if( p->zFName ){ -+ int i; -+#ifndef SQLITE_MUTEX_OMIT -+ sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); -+#endif -+ sqlite3_mutex_enter(pVfsMutex); -+ for(i=0; ALWAYS(inRef==1 ){ -+ memdb_g.apMemStore[i] = memdb_g.apMemStore[--memdb_g.nMemStore]; -+ if( memdb_g.nMemStore==0 ){ -+ sqlite3_free(memdb_g.apMemStore); -+ memdb_g.apMemStore = 0; -+ } -+ } -+ break; -+ } -+ } -+ sqlite3_mutex_leave(pVfsMutex); -+ }else{ -+ memdbEnter(p); -+ } -+ p->nRef--; -+ if( p->nRef<=0 ){ -+ if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){ -+ sqlite3_free(p->aData); -+ } -+ memdbLeave(p); -+ sqlite3_mutex_free(p->pMutex); -+ sqlite3_free(p); -+ }else{ -+ memdbLeave(p); -+ } - return SQLITE_OK; - } - -@@ -47981,22 +54069,25 @@ static int memdbRead( - int iAmt, - sqlite_int64 iOfst - ){ -- MemFile *p = (MemFile *)pFile; -+ MemStore *p = ((MemFile*)pFile)->pStore; -+ memdbEnter(p); - if( iOfst+iAmt>p->sz ){ - memset(zBuf, 0, iAmt); - if( iOfstsz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); -+ memdbLeave(p); - return SQLITE_IOERR_SHORT_READ; - } - memcpy(zBuf, p->aData+iOfst, iAmt); -+ memdbLeave(p); - return SQLITE_OK; - } - - /* - ** Try to enlarge the memory allocation to hold at least sz bytes - */ --static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ -+static int memdbEnlarge(MemStore *p, sqlite3_int64 newSz){ - unsigned char *pNew; -- if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ -+ if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || NEVER(p->nMmap>0) ){ - return SQLITE_FULL; - } - if( newSz>p->szMax ){ -@@ -48005,7 +54096,7 @@ static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ - newSz *= 2; - if( newSz>p->szMax ) newSz = p->szMax; - pNew = sqlite3Realloc(p->aData, newSz); -- if( pNew==0 ) return SQLITE_NOMEM; -+ if( pNew==0 ) return SQLITE_IOERR_NOMEM; - p->aData = pNew; - p->szAlloc = newSz; - return SQLITE_OK; -@@ -48020,19 +54111,27 @@ static int memdbWrite( - int iAmt, - sqlite_int64 iOfst - ){ -- MemFile *p = (MemFile *)pFile; -- if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ) return SQLITE_READONLY; -+ MemStore *p = ((MemFile*)pFile)->pStore; -+ memdbEnter(p); -+ if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ){ -+ /* Can't happen: memdbLock() will return SQLITE_READONLY before -+ ** reaching this point */ -+ memdbLeave(p); -+ return SQLITE_IOERR_WRITE; -+ } - if( iOfst+iAmt>p->sz ){ - int rc; - if( iOfst+iAmt>p->szAlloc - && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK - ){ -+ memdbLeave(p); - return rc; - } - if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz); - p->sz = iOfst+iAmt; - } - memcpy(p->aData+iOfst, z, iAmt); -+ memdbLeave(p); - return SQLITE_OK; - } - -@@ -48044,16 +54143,25 @@ static int memdbWrite( - ** the size of a file, never to increase the size. - */ - static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){ -- MemFile *p = (MemFile *)pFile; -- if( NEVER(size>p->sz) ) return SQLITE_FULL; -- p->sz = size; -- return SQLITE_OK; -+ MemStore *p = ((MemFile*)pFile)->pStore; -+ int rc = SQLITE_OK; -+ memdbEnter(p); -+ if( size>p->sz ){ -+ /* This can only happen with a corrupt wal mode db */ -+ rc = SQLITE_CORRUPT; -+ }else{ -+ p->sz = size; -+ } -+ memdbLeave(p); -+ return rc; - } - - /* - ** Sync an memdb-file. - */ - static int memdbSync(sqlite3_file *pFile, int flags){ -+ UNUSED_PARAMETER(pFile); -+ UNUSED_PARAMETER(flags); - return SQLITE_OK; - } - -@@ -48061,8 +54169,10 @@ static int memdbSync(sqlite3_file *pFile, int flags){ - ** Return the current file-size of an memdb-file. - */ - static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ -- MemFile *p = (MemFile *)pFile; -+ MemStore *p = ((MemFile*)pFile)->pStore; -+ memdbEnter(p); - *pSize = p->sz; -+ memdbLeave(p); - return SQLITE_OK; - } - -@@ -48070,19 +54180,90 @@ static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ - ** Lock an memdb-file. - */ - static int memdbLock(sqlite3_file *pFile, int eLock){ -- MemFile *p = (MemFile *)pFile; -- if( eLock>SQLITE_LOCK_SHARED -- && (p->mFlags & SQLITE_DESERIALIZE_READONLY)!=0 -- ){ -- return SQLITE_READONLY; -+ MemFile *pThis = (MemFile*)pFile; -+ MemStore *p = pThis->pStore; -+ int rc = SQLITE_OK; -+ if( eLock<=pThis->eLock ) return SQLITE_OK; -+ memdbEnter(p); -+ -+ assert( p->nWrLock==0 || p->nWrLock==1 ); -+ assert( pThis->eLock<=SQLITE_LOCK_SHARED || p->nWrLock==1 ); -+ assert( pThis->eLock==SQLITE_LOCK_NONE || p->nRdLock>=1 ); -+ -+ if( eLock>SQLITE_LOCK_SHARED && (p->mFlags & SQLITE_DESERIALIZE_READONLY) ){ -+ rc = SQLITE_READONLY; -+ }else{ -+ switch( eLock ){ -+ case SQLITE_LOCK_SHARED: { -+ assert( pThis->eLock==SQLITE_LOCK_NONE ); -+ if( p->nWrLock>0 ){ -+ rc = SQLITE_BUSY; -+ }else{ -+ p->nRdLock++; -+ } -+ break; -+ }; -+ -+ case SQLITE_LOCK_RESERVED: -+ case SQLITE_LOCK_PENDING: { -+ assert( pThis->eLock>=SQLITE_LOCK_SHARED ); -+ if( ALWAYS(pThis->eLock==SQLITE_LOCK_SHARED) ){ -+ if( p->nWrLock>0 ){ -+ rc = SQLITE_BUSY; -+ }else{ -+ p->nWrLock = 1; -+ } -+ } -+ break; -+ } -+ -+ default: { -+ assert( eLock==SQLITE_LOCK_EXCLUSIVE ); -+ assert( pThis->eLock>=SQLITE_LOCK_SHARED ); -+ if( p->nRdLock>1 ){ -+ rc = SQLITE_BUSY; -+ }else if( pThis->eLock==SQLITE_LOCK_SHARED ){ -+ p->nWrLock = 1; -+ } -+ break; -+ } -+ } - } -- p->eLock = eLock; -+ if( rc==SQLITE_OK ) pThis->eLock = eLock; -+ memdbLeave(p); -+ return rc; -+} -+ -+/* -+** Unlock an memdb-file. -+*/ -+static int memdbUnlock(sqlite3_file *pFile, int eLock){ -+ MemFile *pThis = (MemFile*)pFile; -+ MemStore *p = pThis->pStore; -+ if( eLock>=pThis->eLock ) return SQLITE_OK; -+ memdbEnter(p); -+ -+ assert( eLock==SQLITE_LOCK_SHARED || eLock==SQLITE_LOCK_NONE ); -+ if( eLock==SQLITE_LOCK_SHARED ){ -+ if( ALWAYS(pThis->eLock>SQLITE_LOCK_SHARED) ){ -+ p->nWrLock--; -+ } -+ }else{ -+ if( pThis->eLock>SQLITE_LOCK_SHARED ){ -+ p->nWrLock--; -+ } -+ p->nRdLock--; -+ } -+ -+ pThis->eLock = eLock; -+ memdbLeave(p); - return SQLITE_OK; - } - --#if 0 /* Never used because memdbAccess() always returns false */ -+#if 0 - /* --** Check if another file-handle holds a RESERVED lock on an memdb-file. -+** This interface is only used for crash recovery, which does not -+** occur on an in-memory database. - */ - static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){ - *pResOut = 0; -@@ -48090,12 +54271,14 @@ static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){ - } - #endif - -+ - /* - ** File control method. For custom operations on an memdb-file. - */ - static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ -- MemFile *p = (MemFile *)pFile; -+ MemStore *p = ((MemFile*)pFile)->pStore; - int rc = SQLITE_NOTFOUND; -+ memdbEnter(p); - if( op==SQLITE_FCNTL_VFSNAME ){ - *(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); - rc = SQLITE_OK; -@@ -48113,6 +54296,7 @@ static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ - *(sqlite3_int64*)pArg = iLimit; - rc = SQLITE_OK; - } -+ memdbLeave(p); - return rc; - } - -@@ -48129,6 +54313,7 @@ static int memdbSectorSize(sqlite3_file *pFile){ - ** Return the device characteristic flags supported by an memdb-file. - */ - static int memdbDeviceCharacteristics(sqlite3_file *pFile){ -+ UNUSED_PARAMETER(pFile); - return SQLITE_IOCAP_ATOMIC | - SQLITE_IOCAP_POWERSAFE_OVERWRITE | - SQLITE_IOCAP_SAFE_APPEND | -@@ -48142,20 +54327,26 @@ static int memdbFetch( - int iAmt, - void **pp - ){ -- MemFile *p = (MemFile *)pFile; -- if( iOfst+iAmt>p->sz ){ -+ MemStore *p = ((MemFile*)pFile)->pStore; -+ memdbEnter(p); -+ if( iOfst+iAmt>p->sz || (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)!=0 ){ - *pp = 0; - }else{ - p->nMmap++; - *pp = (void*)(p->aData + iOfst); - } -+ memdbLeave(p); - return SQLITE_OK; - } - - /* Release a memory-mapped page */ - static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ -- MemFile *p = (MemFile *)pFile; -+ MemStore *p = ((MemFile*)pFile)->pStore; -+ UNUSED_PARAMETER(iOfst); -+ UNUSED_PARAMETER(pPage); -+ memdbEnter(p); - p->nMmap--; -+ memdbLeave(p); - return SQLITE_OK; - } - -@@ -48165,20 +54356,79 @@ static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ - static int memdbOpen( - sqlite3_vfs *pVfs, - const char *zName, -- sqlite3_file *pFile, -+ sqlite3_file *pFd, - int flags, - int *pOutFlags - ){ -- MemFile *p = (MemFile*)pFile; -- if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ -- return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); -+ MemFile *pFile = (MemFile*)pFd; -+ MemStore *p = 0; -+ int szName; -+ UNUSED_PARAMETER(pVfs); -+ -+ memset(pFile, 0, sizeof(*pFile)); -+ szName = sqlite3Strlen30(zName); -+ if( szName>1 && (zName[0]=='/' || zName[0]=='\\') ){ -+ int i; -+#ifndef SQLITE_MUTEX_OMIT -+ sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); -+#endif -+ sqlite3_mutex_enter(pVfsMutex); -+ for(i=0; izFName,zName)==0 ){ -+ p = memdb_g.apMemStore[i]; -+ break; -+ } -+ } -+ if( p==0 ){ -+ MemStore **apNew; -+ p = sqlite3Malloc( sizeof(*p) + (i64)szName + 3 ); -+ if( p==0 ){ -+ sqlite3_mutex_leave(pVfsMutex); -+ return SQLITE_NOMEM; -+ } -+ apNew = sqlite3Realloc(memdb_g.apMemStore, -+ sizeof(apNew[0])*(1+(i64)memdb_g.nMemStore) ); -+ if( apNew==0 ){ -+ sqlite3_free(p); -+ sqlite3_mutex_leave(pVfsMutex); -+ return SQLITE_NOMEM; -+ } -+ apNew[memdb_g.nMemStore++] = p; -+ memdb_g.apMemStore = apNew; -+ memset(p, 0, sizeof(*p)); -+ p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE|SQLITE_DESERIALIZE_FREEONCLOSE; -+ p->szMax = sqlite3GlobalConfig.mxMemdbSize; -+ p->zFName = (char*)&p[1]; -+ memcpy(p->zFName, zName, szName+1); -+ p->pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); -+ if( p->pMutex==0 ){ -+ memdb_g.nMemStore--; -+ sqlite3_free(p); -+ sqlite3_mutex_leave(pVfsMutex); -+ return SQLITE_NOMEM; -+ } -+ p->nRef = 1; -+ memdbEnter(p); -+ }else{ -+ memdbEnter(p); -+ p->nRef++; -+ } -+ sqlite3_mutex_leave(pVfsMutex); -+ }else{ -+ p = sqlite3Malloc( sizeof(*p) ); -+ if( p==0 ){ -+ return SQLITE_NOMEM; -+ } -+ memset(p, 0, sizeof(*p)); -+ p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; -+ p->szMax = sqlite3GlobalConfig.mxMemdbSize; - } -- memset(p, 0, sizeof(*p)); -- p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; -- assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ -- *pOutFlags = flags | SQLITE_OPEN_MEMORY; -- pFile->pMethods = &memdb_io_methods; -- p->szMax = sqlite3GlobalConfig.mxMemdbSize; -+ pFile->pStore = p; -+ if( pOutFlags!=0 ){ -+ *pOutFlags = flags | SQLITE_OPEN_MEMORY; -+ } -+ pFd->pMethods = &memdb_io_methods; -+ memdbLeave(p); - return SQLITE_OK; - } - -@@ -48206,6 +54456,9 @@ static int memdbAccess( - int flags, - int *pResOut - ){ -+ UNUSED_PARAMETER(pVfs); -+ UNUSED_PARAMETER(zPath); -+ UNUSED_PARAMETER(flags); - *pResOut = 0; - return SQLITE_OK; - } -@@ -48221,6 +54474,7 @@ static int memdbFullPathname( - int nOut, - char *zOut - ){ -+ UNUSED_PARAMETER(pVfs); - sqlite3_snprintf(nOut, zOut, "%s", zPath); - return SQLITE_OK; - } -@@ -48293,9 +54547,14 @@ static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ - */ - static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){ - MemFile *p = 0; -+ MemStore *pStore; - int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p); - if( rc ) return 0; - if( p->base.pMethods!=&memdb_io_methods ) return 0; -+ pStore = p->pStore; -+ memdbEnter(pStore); -+ if( pStore->zFName!=0 ) p = 0; -+ memdbLeave(pStore); - return p; - } - -@@ -48331,12 +54590,14 @@ SQLITE_API unsigned char *sqlite3_serialize( - if( piSize ) *piSize = -1; - if( iDb<0 ) return 0; - if( p ){ -- if( piSize ) *piSize = p->sz; -+ MemStore *pStore = p->pStore; -+ assert( pStore->pMutex==0 ); -+ if( piSize ) *piSize = pStore->sz; - if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ -- pOut = p->aData; -+ pOut = pStore->aData; - }else{ -- pOut = sqlite3_malloc64( p->sz ); -- if( pOut ) memcpy(pOut, p->aData, p->sz); -+ pOut = sqlite3_malloc64( pStore->sz ); -+ if( pOut ) memcpy(pOut, pStore->aData, pStore->sz); - } - return pOut; - } -@@ -48352,6 +54613,14 @@ SQLITE_API unsigned char *sqlite3_serialize( - pOut = 0; - }else{ - sz = sqlite3_column_int64(pStmt, 0)*szPage; -+ if( sz==0 ){ -+ sqlite3_reset(pStmt); -+ sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0); -+ rc = sqlite3_step(pStmt); -+ if( rc==SQLITE_ROW ){ -+ sz = sqlite3_column_int64(pStmt, 0)*szPage; -+ } -+ } - if( piSize ) *piSize = sz; - if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ - pOut = 0; -@@ -48406,13 +54675,18 @@ SQLITE_API int sqlite3_deserialize( - sqlite3_mutex_enter(db->mutex); - if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; - iDb = sqlite3FindDbName(db, zSchema); -- if( iDb<0 ){ -+ testcase( iDb==1 ); -+ if( iDb<2 && iDb!=0 ){ - rc = SQLITE_ERROR; - goto end_deserialize; - } - zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema); -- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); -- sqlite3_free(zSql); -+ if( zSql==0 ){ -+ rc = SQLITE_NOMEM; -+ }else{ -+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); -+ sqlite3_free(zSql); -+ } - if( rc ) goto end_deserialize; - db->init.iDb = (u8)iDb; - db->init.reopenMemdb = 1; -@@ -48426,30 +54700,44 @@ SQLITE_API int sqlite3_deserialize( - if( p==0 ){ - rc = SQLITE_ERROR; - }else{ -- p->aData = pData; -- p->sz = szDb; -- p->szAlloc = szBuf; -- p->szMax = szBuf; -- if( p->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; -- } -- p->mFlags = mFlags; -+ MemStore *pStore = p->pStore; -+ pStore->aData = pData; -+ pData = 0; -+ pStore->sz = szDb; -+ pStore->szAlloc = szBuf; -+ pStore->szMax = szBuf; -+ if( pStore->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; -+ } -+ pStore->mFlags = mFlags; - rc = SQLITE_OK; - } - - end_deserialize: - sqlite3_finalize(pStmt); -+ if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){ -+ sqlite3_free(pData); -+ } - sqlite3_mutex_leave(db->mutex); - return rc; - } - -+/* -+** Return true if the VFS is the memvfs. -+*/ -+SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs *pVfs){ -+ return pVfs==&memdb_vfs; -+} -+ - /* - ** This routine is called when the extension is loaded. - ** Register the new VFS. - */ - SQLITE_PRIVATE int sqlite3MemdbInit(void){ - sqlite3_vfs *pLower = sqlite3_vfs_find(0); -- int sz = pLower->szOsFile; -+ unsigned int sz; -+ if( NEVER(pLower==0) ) return SQLITE_ERROR; -+ sz = pLower->szOsFile; - memdb_vfs.pAppData = pLower; - /* The following conditional can only be true when compiled for - ** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave -@@ -48459,7 +54747,7 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){ - memdb_vfs.szOsFile = sz; - return sqlite3_vfs_register(&memdb_vfs, 0); - } --#endif /* SQLITE_ENABLE_DESERIALIZE */ -+#endif /* SQLITE_OMIT_DESERIALIZE */ - - /************** End of memdb.c ***********************************************/ - /************** Begin file bitvec.c ******************************************/ -@@ -48532,7 +54820,7 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){ - ** no fewer collisions than the no-op *1. */ - #define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT) - --#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *)) -+#define BITVEC_NPTR ((u32)(BITVEC_USIZE/sizeof(Bitvec *))) - - - /* -@@ -48653,7 +54941,7 @@ SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){ - h = BITVEC_HASH(i++); - /* if there wasn't a hash collision, and this doesn't */ - /* completely fill the hash, then just add it without */ -- /* worring about sub-dividing and re-hashing. */ -+ /* worrying about sub-dividing and re-hashing. */ - if( !p->u.aHash[h] ){ - if (p->nSet<(BITVEC_NINT-1)) { - goto bitvec_set_end; -@@ -48681,7 +54969,9 @@ bitvec_set_rehash: - }else{ - memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); - memset(p->u.apSub, 0, sizeof(p->u.apSub)); -- p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR; -+ p->iDivisor = p->iSize/BITVEC_NPTR; -+ if( (p->iSize%BITVEC_NPTR)!=0 ) p->iDivisor++; -+ if( p->iDivisoriDivisor = BITVEC_NBIT; - rc = sqlite3BitvecSet(p, i); - for(j=0; jiSize<=BITVEC_NBIT ){ -- p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); -+ p->u.aBitmap[i/BITVEC_SZELEM] &= ~(BITVEC_TELEM)(1<<(i&(BITVEC_SZELEM-1))); - }else{ - unsigned int j; - u32 *aiValues = pBuf; -@@ -48766,7 +55056,7 @@ SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec *p){ - ** individual bits within V. - */ - #define SETBIT(V,I) V[I>>3] |= (1<<(I&7)) --#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) -+#define CLEARBIT(V,I) V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7)) - #define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 - - /* -@@ -48809,7 +55099,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ - /* Allocate the Bitvec to be tested and a linear array of - ** bits to act as the reference */ - pBitvec = sqlite3BitvecCreate( sz ); -- pV = sqlite3MallocZero( (sz+7)/8 + 1 ); -+ pV = sqlite3MallocZero( (7+(i64)sz)/8 + 1 ); - pTmpSpace = sqlite3_malloc64(BITVEC_SZ); - if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; - -@@ -48818,7 +55108,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ - sqlite3BitvecClear(0, 1, pTmpSpace); - - /* Run the program */ -- pc = 0; -+ pc = i = 0; - while( (op = aOp[pc])!=0 ){ - switch( op ){ - case 1: -@@ -48920,7 +55210,7 @@ bitvec_end: - struct PCache { - PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ - PgHdr *pSynced; /* Last synced page in dirty page list */ -- int nRefSum; /* Sum of ref counts over all pages */ -+ i64 nRefSum; /* Sum of ref counts over all pages */ - int szCache; /* Configured cache size */ - int szSpill; /* Size before spilling occurs */ - int szPage; /* Size of every page in this cache */ -@@ -48945,12 +55235,24 @@ struct PCache { - int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */ - int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */ - # define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;} -- void pcacheDump(PCache *pCache){ -- int N; -- int i, j; -- sqlite3_pcache_page *pLower; -+ static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){ - PgHdr *pPg; - unsigned char *a; -+ int j; -+ if( pLower==0 ){ -+ printf("%3d: NULL\n", i); -+ }else{ -+ pPg = (PgHdr*)pLower->pExtra; -+ printf("%3d: nRef %2lld flgs %02x data ", i, pPg->nRef, pPg->flags); -+ a = (unsigned char *)pLower->pBuf; -+ for(j=0; j<12; j++) printf("%02x", a[j]); -+ printf(" ptr %p\n", pPg); -+ } -+ } -+ static void pcacheDump(PCache *pCache){ -+ int N; -+ int i; -+ sqlite3_pcache_page *pLower; - - if( sqlite3PcacheTrace<2 ) return; - if( pCache->pCache==0 ) return; -@@ -48958,22 +55260,42 @@ struct PCache { - if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump; - for(i=1; i<=N; i++){ - pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); -- if( pLower==0 ) continue; -- pPg = (PgHdr*)pLower->pExtra; -- printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); -- a = (unsigned char *)pLower->pBuf; -- for(j=0; j<12; j++) printf("%02x", a[j]); -- printf("\n"); -- if( pPg->pPage==0 ){ -+ pcachePageTrace(i, pLower); -+ if( pLower && ((PgHdr*)pLower)->pPage==0 ){ - sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); - } - } - } -- #else -+#else - # define pcacheTrace(X) -+# define pcachePageTrace(PGNO, X) - # define pcacheDump(X) - #endif - -+/* -+** Return 1 if pPg is on the dirty list for pCache. Return 0 if not. -+** This routine runs inside of assert() statements only. -+*/ -+#if defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) -+static int pageOnDirtyList(PCache *pCache, PgHdr *pPg){ -+ PgHdr *p; -+ for(p=pCache->pDirty; p; p=p->pDirtyNext){ -+ if( p==pPg ) return 1; -+ } -+ return 0; -+} -+static int pageNotOnDirtyList(PCache *pCache, PgHdr *pPg){ -+ PgHdr *p; -+ for(p=pCache->pDirty; p; p=p->pDirtyNext){ -+ if( p==pPg ) return 0; -+ } -+ return 1; -+} -+#else -+# define pageOnDirtyList(A,B) 1 -+# define pageNotOnDirtyList(A,B) 1 -+#endif -+ - /* - ** Check invariants on a PgHdr entry. Return true if everything is OK. - ** Return false if any invariant is violated. -@@ -48992,8 +55314,13 @@ SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){ - assert( pCache!=0 ); /* Every page has an associated PCache */ - if( pPg->flags & PGHDR_CLEAN ){ - assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ -- assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */ -- assert( pCache->pDirtyTail!=pPg ); -+ assert( pageNotOnDirtyList(pCache, pPg) );/* CLEAN pages not on dirtylist */ -+ }else{ -+ assert( (pPg->flags & PGHDR_DIRTY)!=0 );/* If not CLEAN must be DIRTY */ -+ assert( pPg->pDirtyNext==0 || pPg->pDirtyNext->pDirtyPrev==pPg ); -+ assert( pPg->pDirtyPrev==0 || pPg->pDirtyPrev->pDirtyNext==pPg ); -+ assert( pPg->pDirtyPrev!=0 || pCache->pDirty==pPg ); -+ assert( pageOnDirtyList(pCache, pPg) ); - } - /* WRITEABLE pages must also be DIRTY */ - if( pPg->flags & PGHDR_WRITEABLE ){ -@@ -49122,11 +55449,14 @@ static int numberOfCachePages(PCache *p){ - ** suggested cache size is set to N. */ - return p->szCache; - }else{ -- /* IMPLEMANTATION-OF: R-59858-46238 If the argument N is negative, then the -+ i64 n; -+ /* IMPLEMENTATION-OF: R-59858-46238 If the argument N is negative, then the - ** number of cache pages is adjusted to be a number of pages that would - ** use approximately abs(N*1024) bytes of memory based on the current - ** page size. */ -- return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); -+ n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); -+ if( n>1000000000 ) n = 1000000000; -+ return (int)n; - } - } - -@@ -49264,8 +55594,9 @@ SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch( - assert( createFlag==0 || pCache->eCreate==eCreate ); - assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); - pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); -- pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno, -+ pcacheTrace(("%p.FETCH %d%s (result: %p) ",pCache,pgno, - createFlag?" create":"",pRes)); -+ pcachePageTrace(pgno, pRes); - return pRes; - } - -@@ -49350,6 +55681,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( - pPgHdr->pData = pPage->pBuf; - pPgHdr->pExtra = (void *)&pPgHdr[1]; - memset(pPgHdr->pExtra, 0, 8); -+ assert( EIGHT_BYTE_ALIGNMENT( pPgHdr->pExtra ) ); - pPgHdr->pCache = pCache; - pPgHdr->pgno = pgno; - pPgHdr->flags = PGHDR_CLEAN; -@@ -49393,6 +55725,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ - pcacheUnpin(p); - }else{ - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); -+ assert( sqlite3PcachePageSanity(p) ); - } - } - } -@@ -49436,6 +55769,7 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){ - pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno)); - assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY ); - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); -+ assert( sqlite3PcachePageSanity(p) ); - } - assert( sqlite3PcachePageSanity(p) ); - } -@@ -49498,14 +55832,24 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){ - */ - SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ - PCache *pCache = p->pCache; -+ sqlite3_pcache_page *pOther; - assert( p->nRef>0 ); - assert( newPgno>0 ); - assert( sqlite3PcachePageSanity(p) ); - pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno)); -+ pOther = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0); -+ if( pOther ){ -+ PgHdr *pXPage = (PgHdr*)pOther->pExtra; -+ assert( pXPage->nRef==0 ); -+ pXPage->nRef++; -+ pCache->nRefSum++; -+ sqlite3PcacheDrop(pXPage); -+ } - sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); - p->pgno = newPgno; - if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ - pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); -+ assert( sqlite3PcachePageSanity(p) ); - } - } - -@@ -49595,7 +55939,7 @@ static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ - } - - /* --** Sort the list of pages in accending order by pgno. Pages are -+** Sort the list of pages in ascending order by pgno. Pages are - ** connected by pDirty pointers. The pDirtyPrev pointers are - ** corrupted by this sort. - ** -@@ -49654,14 +55998,14 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){ - ** This is not the total number of pages referenced, but the sum of the - ** reference count for all pages. - */ --SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){ -+SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache *pCache){ - return pCache->nRefSum; - } - - /* - ** Return the number of references to the page supplied as an argument. - */ --SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){ -+SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr *p){ - return p->nRef; - } - -@@ -49803,12 +56147,13 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd - ** size can vary according to architecture, compile-time options, and - ** SQLite library version number. - ** --** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained --** using a separate memory allocation from the database page content. This --** seeks to overcome the "clownshoe" problem (also called "internal --** fragmentation" in academic literature) of allocating a few bytes more --** than a power of two with the memory allocator rounding up to the next --** power of two, and leaving the rounded-up space unused. -+** Historical note: It used to be that if the SQLITE_PCACHE_SEPARATE_HEADER -+** was defined, then the page content would be held in a separate memory -+** allocation from the PgHdr1. This was intended to avoid clownshoe memory -+** allocations. However, the btree layer needs a small (16-byte) overrun -+** area after the page content buffer. The header serves as that overrun -+** area. Therefore SQLITE_PCACHE_SEPARATE_HEADER was discontinued to avoid -+** any possibility of a memory error. - ** - ** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates - ** with this module. Information is passed back and forth as PgHdr1 pointers. -@@ -49834,7 +56179,7 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd - ** If N is positive, then N pages worth of memory are allocated using a single - ** sqlite3Malloc() call and that memory is used for the first N pages allocated. - ** Or if N is negative, then -1024*N bytes of memory are allocated and used --** for as many pages as can be accomodated. -+** for as many pages as can be accommodated. - ** - ** Only one of (2) or (3) can be used. Once the memory available to (2) or - ** (3) is exhausted, subsequent allocations fail over to the general-purpose -@@ -49853,30 +56198,40 @@ typedef struct PGroup PGroup; - - /* - ** Each cache entry is represented by an instance of the following --** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of --** PgHdr1.pCache->szPage bytes is allocated directly before this structure --** in memory. -+** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated -+** directly before this structure and is used to cache the page content. -+** -+** When reading a corrupt database file, it is possible that SQLite might -+** read a few bytes (no more than 16 bytes) past the end of the page buffer. -+** It will only read past the end of the page buffer, never write. This -+** object is positioned immediately after the page buffer to serve as an -+** overrun area, so that overreads are harmless. - ** --** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, -+** Variables isBulkLocal and isAnchor were once type "u8". That works, - ** but causes a 2-byte gap in the structure for most architectures (since - ** pointers must be either 4 or 8-byte aligned). As this structure is located - ** in memory directly after the associated page data, if the database is - ** corrupt, code at the b-tree layer may overread the page buffer and - ** read part of this structure before the corruption is detected. This --** can cause a valgrind error if the unitialized gap is accessed. Using u16 --** ensures there is no such gap, and therefore no bytes of unitialized memory --** in the structure. -+** can cause a valgrind error if the uninitialized gap is accessed. Using u16 -+** ensures there is no such gap, and therefore no bytes of uninitialized -+** memory in the structure. -+** -+** The pLruNext and pLruPrev pointers form a double-linked circular list -+** of all pages that are unpinned. The PGroup.lru element (which should be -+** the only element on the list with PgHdr1.isAnchor set to 1) forms the -+** beginning and the end of the list. - */ - struct PgHdr1 { -- sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ -- unsigned int iKey; /* Key value (page number) */ -- u16 isBulkLocal; /* This page from bulk local storage */ -- u16 isAnchor; /* This is the PGroup.lru element */ -- PgHdr1 *pNext; /* Next in hash table chain */ -- PCache1 *pCache; /* Cache that currently owns this page */ -- PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ -- PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ -- /* NB: pLruPrev is only valid if pLruNext!=0 */ -+ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ -+ unsigned int iKey; /* Key value (page number) */ -+ u16 isBulkLocal; /* This page from bulk local storage */ -+ u16 isAnchor; /* This is the PGroup.lru element */ -+ PgHdr1 *pNext; /* Next in hash table chain */ -+ PCache1 *pCache; /* Cache that currently owns this page */ -+ PgHdr1 *pLruNext; /* Next in circular LRU list of unpinned pages */ -+ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ -+ /* NB: pLruPrev is only valid if pLruNext!=0 */ - }; - - /* -@@ -49985,10 +56340,6 @@ static SQLITE_WSD struct PCacheGlobal { - sqlite3_mutex *mutex; /* Mutex for accessing the following: */ - PgFreeslot *pFree; /* Free page blocks */ - int nFreeSlot; /* Number of unused pcache slots */ -- /* The following value requires a mutex to change. We skip the mutex on -- ** reading because (1) most platforms read a 32-bit integer atomically and -- ** (2) even if an incorrect value is read, no great harm is done since this -- ** is really just an optimization. */ - int bUnderPressure; /* True if low on PAGECACHE memory */ - } pcache1_g; - -@@ -50036,7 +56387,7 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ - pcache1.nReserve = n>90 ? 10 : (n/10 + 1); - pcache1.pStart = pBuf; - pcache1.pFree = 0; -- pcache1.bUnderPressure = 0; -+ AtomicStore(&pcache1.bUnderPressure,0); - while( n-- ){ - p = (PgFreeslot*)pBuf; - p->pNext = pcache1.pFree; -@@ -50073,7 +56424,8 @@ static int pcache1InitBulk(PCache1 *pCache){ - do{ - PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; - pX->page.pBuf = zBulk; -- pX->page.pExtra = &pX[1]; -+ pX->page.pExtra = (u8*)pX + ROUND8(sizeof(*pX)); -+ assert( EIGHT_BYTE_ALIGNMENT( pX->page.pExtra ) ); - pX->isBulkLocal = 1; - pX->isAnchor = 0; - pX->pNext = pCache->pFree; -@@ -50103,7 +56455,7 @@ static void *pcache1Alloc(int nByte){ - if( p ){ - pcache1.pFree = pcache1.pFree->pNext; - pcache1.nFreeSlot--; -- pcache1.bUnderPressure = pcache1.nFreeSlot=0 ); - sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); - sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); -@@ -50142,7 +56494,7 @@ static void pcache1Free(void *p){ - pSlot->pNext = pcache1.pFree; - pcache1.pFree = pSlot; - pcache1.nFreeSlot++; -- pcache1.bUnderPressure = pcache1.nFreeSlotpGroup); - #endif - if( benignMalloc ){ sqlite3BeginBenignMalloc(); } --#ifdef SQLITE_PCACHE_SEPARATE_HEADER -- pPg = pcache1Alloc(pCache->szPage); -- p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); -- if( !pPg || !p ){ -- pcache1Free(pPg); -- sqlite3_free(p); -- pPg = 0; -- } --#else - pPg = pcache1Alloc(pCache->szAlloc); --#endif - if( benignMalloc ){ sqlite3EndBenignMalloc(); } - #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT - pcache1EnterMutex(pCache->pGroup); - #endif - if( pPg==0 ) return 0; --#ifndef SQLITE_PCACHE_SEPARATE_HEADER - p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; --#endif - p->page.pBuf = pPg; -- p->page.pExtra = &p[1]; -+ p->page.pExtra = (u8*)p + ROUND8(sizeof(*p)); -+ assert( EIGHT_BYTE_ALIGNMENT( p->page.pExtra ) ); - p->isBulkLocal = 0; - p->isAnchor = 0; -+ p->pLruPrev = 0; /* Initializing this saves a valgrind error */ - } - (*pCache->pnPurgeable)++; - return p; -@@ -50243,9 +56585,6 @@ static void pcache1FreePage(PgHdr1 *p){ - pCache->pFree = p; - }else{ - pcache1Free(p->page.pBuf); --#ifdef SQLITE_PCACHE_SEPARATE_HEADER -- sqlite3_free(p); --#endif - } - (*pCache->pnPurgeable)--; - } -@@ -50286,7 +56625,7 @@ SQLITE_PRIVATE void sqlite3PageFree(void *p){ - */ - static int pcache1UnderMemoryPressure(PCache1 *pCache){ - if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ -- return pcache1.bUnderPressure; -+ return AtomicLoad(&pcache1.bUnderPressure); - }else{ - return sqlite3HeapNearlyFull(); - } -@@ -50303,12 +56642,12 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){ - */ - static void pcache1ResizeHash(PCache1 *p){ - PgHdr1 **apNew; -- unsigned int nNew; -- unsigned int i; -+ u64 nNew; -+ u32 i; - - assert( sqlite3_mutex_held(p->pGroup->mutex) ); - -- nNew = p->nHash*2; -+ nNew = 2*(u64)p->nHash; - if( nNew<256 ){ - nNew = 256; - } -@@ -50531,7 +56870,7 @@ static void pcache1Destroy(sqlite3_pcache *p); - static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ - PCache1 *pCache; /* The newly created page cache */ - PGroup *pGroup; /* The group the new page cache will belong to */ -- int sz; /* Bytes of memory required to allocate the new cache */ -+ i64 sz; /* Bytes of memory required to allocate the new cache */ - - assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); - assert( szExtra < 300 ); -@@ -50580,12 +56919,18 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ - */ - static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ - PCache1 *pCache = (PCache1 *)p; -+ u32 n; -+ assert( nMax>=0 ); - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; - pcache1EnterMutex(pGroup); -- pGroup->nMaxPage += (nMax - pCache->nMax); -+ n = (u32)nMax; -+ if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){ -+ n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax; -+ } -+ pGroup->nMaxPage += (n - pCache->nMax); - pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; -- pCache->nMax = nMax; -+ pCache->nMax = n; - pCache->n90pct = pCache->nMax*9/10; - pcache1EnforceMaxPage(pCache); - pcache1LeaveMutex(pGroup); -@@ -50601,7 +56946,7 @@ static void pcache1Shrink(sqlite3_pcache *p){ - PCache1 *pCache = (PCache1*)p; - if( pCache->bPurgeable ){ - PGroup *pGroup = pCache->pGroup; -- int savedMaxPage; -+ unsigned int savedMaxPage; - pcache1EnterMutex(pGroup); - savedMaxPage = pGroup->nMaxPage; - pGroup->nMaxPage = 0; -@@ -50880,23 +57225,26 @@ static void pcache1Rekey( - PCache1 *pCache = (PCache1 *)p; - PgHdr1 *pPage = (PgHdr1 *)pPg; - PgHdr1 **pp; -- unsigned int h; -+ unsigned int hOld, hNew; - assert( pPage->iKey==iOld ); - assert( pPage->pCache==pCache ); -+ assert( iOld!=iNew ); /* The page number really is changing */ - - pcache1EnterMutex(pCache->pGroup); - -- h = iOld%pCache->nHash; -- pp = &pCache->apHash[h]; -+ assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */ -+ hOld = iOld%pCache->nHash; -+ pp = &pCache->apHash[hOld]; - while( (*pp)!=pPage ){ - pp = &(*pp)->pNext; - } - *pp = pPage->pNext; - -- h = iNew%pCache->nHash; -+ assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */ -+ hNew = iNew%pCache->nHash; - pPage->iKey = iNew; -- pPage->pNext = pCache->apHash[h]; -- pCache->apHash[h] = pPage; -+ pPage->pNext = pCache->apHash[hNew]; -+ pCache->apHash[hNew] = pPage; - if( iNew>pCache->iMaxKey ){ - pCache->iMaxKey = iNew; - } -@@ -51003,9 +57351,6 @@ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ - && p->isAnchor==0 - ){ - nFree += pcache1MemSize(p->page.pBuf); --#ifdef SQLITE_PCACHE_SEPARATE_HEADER -- nFree += sqlite3MemSize(p); --#endif - assert( PAGE_IS_UNPINNED(p) ); - pcache1PinPage(p); - pcache1RemoveFromHash(p, 1); -@@ -51086,7 +57431,7 @@ SQLITE_PRIVATE void sqlite3PcacheStats( - ** The TEST primitive includes a "batch" number. The TEST primitive - ** will only see elements that were inserted before the last change - ** in the batch number. In other words, if an INSERT occurs between --** two TESTs where the TESTs have the same batch nubmer, then the -+** two TESTs where the TESTs have the same batch number, then the - ** value added by the INSERT will not be visible to the second TEST. - ** The initial batch number is zero, so if the very first TEST contains - ** a non-zero batch number, it will see all prior INSERTs. -@@ -51618,6 +57963,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 - # define sqlite3WalFramesize(z) 0 - # define sqlite3WalFindFrame(x,y,z) 0 - # define sqlite3WalFile(x) 0 -+# undef SQLITE_USE_SEH - #else - - #define WAL_SAVEPOINT_NDATA 4 -@@ -51724,6 +58070,10 @@ SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock); - SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db); - #endif - -+#ifdef SQLITE_USE_SEH -+SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal*); -+#endif -+ - #endif /* ifndef SQLITE_OMIT_WAL */ - #endif /* SQLITE_WAL_H */ - -@@ -52009,7 +58359,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ - ** outstanding transactions have been abandoned, the pager is able to - ** transition back to OPEN state, discarding the contents of the - ** page-cache and any other in-memory state at the same time. Everything --** is reloaded from disk (and, if necessary, hot-journal rollback peformed) -+** is reloaded from disk (and, if necessary, hot-journal rollback performed) - ** when a read-transaction is next opened on the pager (transitioning - ** the pager into READER state). At that point the system has recovered - ** from the error. -@@ -52143,6 +58493,7 @@ struct PagerSavepoint { - Bitvec *pInSavepoint; /* Set of pages in this savepoint */ - Pgno nOrig; /* Original number of pages in file */ - Pgno iSubRec; /* Index of first record in sub-journal */ -+ int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */ - #ifndef SQLITE_OMIT_WAL - u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ - #endif -@@ -52337,6 +58688,7 @@ struct Pager { - u8 noLock; /* Do not lock (except in WAL mode) */ - u8 readOnly; /* True for a read-only database */ - u8 memDb; /* True to inhibit all file I/O */ -+ u8 memVfs; /* VFS-implemented memory database */ - - /************************************************************************** - ** The following block contains those class members that change during -@@ -52386,14 +58738,15 @@ struct Pager { - i16 nReserve; /* Number of unused bytes at end of each page */ - u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ - u32 sectorSize; /* Assumed sector size during rollback */ -- int pageSize; /* Number of bytes in a page */ - Pgno mxPgno; /* Maximum allowed size of the database */ -+ Pgno lckPgno; /* Page number for the locking page */ -+ i64 pageSize; /* Number of bytes in a page */ - i64 journalSizeLimit; /* Size limit for persistent journal files */ - char *zFilename; /* Name of the database file */ - char *zJournal; /* Name of the journal file */ - int (*xBusyHandler)(void*); /* Function to call when busy */ - void *pBusyHandlerArg; /* Context argument for xBusyHandler */ -- int aStat[4]; /* Total cache hits, misses, writes, spills */ -+ u32 aStat[4]; /* Total cache hits, misses, writes, spills */ - #ifdef SQLITE_TEST - int nRead; /* Database pages read */ - #endif -@@ -52405,6 +58758,9 @@ struct Pager { - Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ - char *zWal; /* File name for write-ahead log */ - #endif -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ sqlite3 *dbWal; -+#endif - }; - - /* -@@ -52494,40 +58850,33 @@ static const unsigned char aJournalMagic[] = { - # define USEFETCH(x) 0 - #endif - --/* --** The argument to this macro is a file descriptor (type sqlite3_file*). --** Return 0 if it is not open, or non-zero (but not 1) if it is. --** --** This is so that expressions can be written as: --** --** if( isOpen(pPager->jfd) ){ ... --** --** instead of --** --** if( pPager->jfd->pMethods ){ ... --*/ --#define isOpen(pFd) ((pFd)->pMethods!=0) -- - #ifdef SQLITE_DIRECT_OVERFLOW_READ - /* - ** Return true if page pgno can be read directly from the database file - ** by the b-tree layer. This is the case if: - ** --** * the database file is open, --** * there are no dirty pages in the cache, and --** * the desired page is not currently in the wal file. -+** (1) the database file is open -+** (2) the VFS for the database is able to do unaligned sub-page reads -+** (3) there are no dirty pages in the cache, and -+** (4) the desired page is not currently in the wal file. - */ - SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ -- if( pPager->fd->pMethods==0 ) return 0; -- if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; -+ assert( pPager!=0 ); -+ assert( pPager->fd!=0 ); -+ if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */ -+ if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */ - #ifndef SQLITE_OMIT_WAL - if( pPager->pWal ){ - u32 iRead = 0; -- int rc; -- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); -- return (rc==SQLITE_OK && iRead==0); -+ (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); -+ if( iRead ) return 0; /* Case (4) */ - } - #endif -+ assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); -+ if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd) -+ & SQLITE_IOCAP_SUBPAGE_READ)==0 ){ -+ return 0; /* Case (2) */ -+ } - return 1; - } - #endif -@@ -52778,6 +59127,9 @@ static int subjRequiresPage(PgHdr *pPg){ - for(i=0; inSavepoint; i++){ - p = &pPager->aSavepoint[i]; - if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){ -+ for(i=i+1; inSavepoint; i++){ -+ pPager->aSavepoint[i].bTruncateOnRelease = 0; -+ } - return 1; - } - } -@@ -53000,7 +59352,7 @@ static void checkPage(PgHdr *pPg){ - ** If an error occurs while reading from the journal file, an SQLite - ** error code is returned. - */ --static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u32 nSuper){ -+static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u64 nSuper){ - int rc; /* Return code */ - u32 len; /* Length in bytes of super-journal name */ - i64 szJ; /* Total size in bytes of journal file pJrnl */ -@@ -53194,9 +59546,32 @@ static int writeJournalHdr(Pager *pPager){ - memset(zHeader, 0, sizeof(aJournalMagic)+4); - } - -+ -+ - /* The random check-hash initializer */ -- sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); -+ if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ -+ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); -+ } -+#ifdef SQLITE_DEBUG -+ else{ -+ /* The Pager.cksumInit variable is usually randomized above to protect -+ ** against there being existing records in the journal file. This is -+ ** dangerous, as following a crash they may be mistaken for records -+ ** written by the current transaction and rolled back into the database -+ ** file, causing corruption. The following assert statements verify -+ ** that this is not required in "journal_mode=memory" mode, as in that -+ ** case the journal file is always 0 bytes in size at this point. -+ ** It is advantageous to avoid the sqlite3_randomness() call if possible -+ ** as it takes the global PRNG mutex. */ -+ i64 sz = 0; -+ sqlite3OsFileSize(pPager->jfd, &sz); -+ assert( sz==0 ); -+ assert( pPager->journalOff==journalHdrOffset(pPager) ); -+ assert( sqlite3JournalIsInMemory(pPager->jfd) ); -+ } -+#endif - put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); -+ - /* The initial database size */ - put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize); - /* The assumed sector size for this process */ -@@ -53370,13 +59745,13 @@ static int readJournalHdr( - ** journal file descriptor is advanced to the next sector boundary before - ** anything is written. The format is: - ** --** + 4 bytes: PAGER_MJ_PGNO. -+** + 4 bytes: PAGER_SJ_PGNO. - ** + N bytes: super-journal filename in utf-8. - ** + 4 bytes: N (length of super-journal name in bytes, no nul-terminator). - ** + 4 bytes: super-journal name checksum. - ** + 8 bytes: aJournalMagic[]. - ** --** The super-journal page checksum is the sum of the bytes in thesuper-journal -+** The super-journal page checksum is the sum of the bytes in the super-journal - ** name, where each byte is interpreted as a signed 8-bit integer. - ** - ** If zSuper is a NULL pointer (occurs for a single database transaction), -@@ -53418,7 +59793,7 @@ static int writeSuperJournal(Pager *pPager, const char *zSuper){ - /* Write the super-journal data to the end of the journal file. If - ** an error occurs, return the error code to the caller. - */ -- if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager)))) -+ if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_SJ_PGNO(pPager)))) - || (0 != (rc = sqlite3OsWrite(pPager->jfd, zSuper, nSuper, iHdrOff+4))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper, nSuper))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper+4, cksum))) -@@ -53429,7 +59804,7 @@ static int writeSuperJournal(Pager *pPager, const char *zSuper){ - } - pPager->journalOff += (nSuper+20); - -- /* If the pager is in peristent-journal mode, then the physical -+ /* If the pager is in persistent-journal mode, then the physical - ** journal-file may extend past the end of the super-journal name - ** and 8 bytes of magic data just written to the file. This is - ** dangerous because the code to rollback a hot-journal file -@@ -53532,6 +59907,15 @@ static void pager_unlock(Pager *pPager){ - - if( pagerUseWal(pPager) ){ - assert( !isOpen(pPager->jfd) ); -+ if( pPager->eState==PAGER_ERROR ){ -+ /* If an IO error occurs in wal.c while attempting to wrap the wal file, -+ ** then the Wal object may be holding a write-lock but no read-lock. -+ ** This call ensures that the write-lock is dropped as well. We cannot -+ ** have sqlite3WalEndReadTransaction() drop the write-lock, as it once -+ ** did, because this would break "BEGIN EXCLUSIVE" handling for -+ ** SQLITE_ENABLE_SETLK_TIMEOUT builds. */ -+ sqlite3WalEndWriteTransaction(pPager->pWal); -+ } - sqlite3WalEndReadTransaction(pPager->pWal); - pPager->eState = PAGER_OPEN; - }else if( !pPager->exclusiveMode ){ -@@ -53599,7 +59983,7 @@ static void pager_unlock(Pager *pPager){ - - /* - ** This function is called whenever an IOERR or FULL error that requires --** the pager to transition into the ERROR state may ahve occurred. -+** the pager to transition into the ERROR state may have occurred. - ** The first argument is a pointer to the pager structure, the second - ** the error-code about to be returned by a pager API function. The - ** value returned is a copy of the second argument to this function. -@@ -53760,7 +60144,7 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ - } - pPager->journalOff = 0; - }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST -- || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) -+ || (pPager->exclusiveMode && pPager->journalModetempFile); - pPager->journalOff = 0; -@@ -53840,6 +60224,9 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ - return (rc==SQLITE_OK?rc2:rc); - } - -+/* Forward reference */ -+static int pager_playback(Pager *pPager, int isHot); -+ - /* - ** Execute a rollback if a transaction is active and unlock the - ** database file. -@@ -53868,13 +60255,28 @@ static void pagerUnlockAndRollback(Pager *pPager){ - assert( pPager->eState==PAGER_READER ); - pager_end_transaction(pPager, 0, 0); - } -+ }else if( pPager->eState==PAGER_ERROR -+ && pPager->journalMode==PAGER_JOURNALMODE_MEMORY -+ && isOpen(pPager->jfd) -+ ){ -+ /* Special case for a ROLLBACK due to I/O error with an in-memory -+ ** journal: We have to rollback immediately, before the journal is -+ ** closed, because once it is closed, all content is forgotten. */ -+ int errCode = pPager->errCode; -+ u8 eLock = pPager->eLock; -+ pPager->eState = PAGER_OPEN; -+ pPager->errCode = SQLITE_OK; -+ pPager->eLock = EXCLUSIVE_LOCK; -+ pager_playback(pPager, 1); -+ pPager->errCode = errCode; -+ pPager->eLock = eLock; - } - pager_unlock(pPager); - } - - /* - ** Parameter aData must point to a buffer of pPager->pageSize bytes --** of data. Compute and return a checksum based ont the contents of the -+** of data. Compute and return a checksum based on the contents of the - ** page of data and the current value of pPager->cksumInit. - ** - ** This is not a real checksum. It is really just the sum of the -@@ -53928,7 +60330,7 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ - ** corrupted, SQLITE_DONE is returned. Data is considered corrupted in - ** two circumstances: - ** --** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or -+** * If the record page-number is illegal (0 or PAGER_SJ_PGNO), or - ** * If the record is being rolled back from the main journal file - ** and the checksum field does not match the record content. - ** -@@ -53988,7 +60390,7 @@ static int pager_playback_one_page( - ** it could cause invalid data to be written into the journal. We need to - ** detect this invalid data (with high probability) and ignore it. - */ -- if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ -+ if( pgno==0 || pgno==PAGER_SJ_PGNO(pPager) ){ - assert( !isSavepnt ); - return SQLITE_DONE; - } -@@ -54194,12 +60596,13 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ - i64 nSuperJournal; /* Size of super-journal file */ - char *zJournal; /* Pointer to one journal within MJ file */ - char *zSuperPtr; /* Space to hold super-journal filename */ -- int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ -+ char *zFree = 0; /* Free this buffer */ -+ i64 nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ - - /* Allocate space for both the pJournal and pSuper file descriptors. - ** If successful, open the super-journal file for reading. - */ -- pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); -+ pSuper = (sqlite3_file *)sqlite3MallocZero(2 * (i64)pVfs->szOsFile); - if( !pSuper ){ - rc = SQLITE_NOMEM_BKPT; - pJournal = 0; -@@ -54217,12 +60620,17 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ - */ - rc = sqlite3OsFileSize(pSuper, &nSuperJournal); - if( rc!=SQLITE_OK ) goto delsuper_out; -- nSuperPtr = pVfs->mxPathname+1; -- zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); -- if( !zSuperJournal ){ -+ nSuperPtr = 1 + (i64)pVfs->mxPathname; -+ assert( nSuperJournal>=0 && nSuperPtr>0 ); -+ zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); -+ if( !zFree ){ - rc = SQLITE_NOMEM_BKPT; - goto delsuper_out; -+ }else{ -+ assert( nSuperJournal<=0x7fffffff ); - } -+ zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; -+ zSuperJournal = &zFree[4]; - zSuperPtr = &zSuperJournal[nSuperJournal+2]; - rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); - if( rc!=SQLITE_OK ) goto delsuper_out; -@@ -54270,7 +60678,7 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ - rc = sqlite3OsDelete(pVfs, zSuper, 0); - - delsuper_out: -- sqlite3_free(zSuperJournal); -+ sqlite3_free(zFree); - if( pSuper ){ - sqlite3OsClose(pSuper); - assert( !isOpen(pJournal) ); -@@ -54304,6 +60712,8 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ - int rc = SQLITE_OK; - assert( pPager->eState!=PAGER_ERROR ); - assert( pPager->eState!=PAGER_READER ); -+ PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage)); -+ - - if( isOpen(pPager->fd) - && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) -@@ -54322,6 +60732,7 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ - memset(pTmp, 0, szPage); - testcase( (newSize-szPage) == currentSize ); - testcase( (newSize-szPage) > currentSize ); -+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &newSize); - rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); - } - if( rc==SQLITE_OK ){ -@@ -54477,7 +60888,7 @@ static int pager_playback(Pager *pPager, int isHot){ - ** for pageSize. - */ - zSuper = pPager->pTmpSpace; -- rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); -+ rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname); - if( rc==SQLITE_OK && zSuper[0] ){ - rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); - } -@@ -54544,6 +60955,9 @@ static int pager_playback(Pager *pPager, int isHot){ - goto end_playback; - } - pPager->dbSize = mxPg; -+ if( pPager->mxPgnomxPgno = mxPg; -+ } - } - - /* Copy original pages out of the journal and back into the -@@ -54608,8 +61022,12 @@ end_playback: - pPager->changeCountDone = pPager->tempFile; - - if( rc==SQLITE_OK ){ -- zSuper = pPager->pTmpSpace; -- rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); -+ /* Leave 4 bytes of space before the super-journal filename in memory. -+ ** This is because it may end up being passed to sqlite3OsOpen(), in -+ ** which case it requires 4 0x00 bytes in memory immediately before -+ ** the filename. */ -+ zSuper = &pPager->pTmpSpace[4]; -+ rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname); - testcase( rc!=SQLITE_OK ); - } - if( rc==SQLITE_OK -@@ -54625,6 +61043,8 @@ end_playback: - /* If there was a super-journal and this routine will return success, - ** see if it is possible to delete the super-journal. - */ -+ assert( zSuper==&pPager->pTmpSpace[4] ); -+ memset(pPager->pTmpSpace, 0, 4); - rc = pager_delsuper(pPager, zSuper); - testcase( rc!=SQLITE_OK ); - } -@@ -54719,6 +61139,7 @@ static int readDbPage(PgHdr *pPg){ - */ - static void pager_write_changecounter(PgHdr *pPg){ - u32 change_counter; -+ if( NEVER(pPg==0) ) return; - - /* Increment the value just read and write it back to byte 24. */ - change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; -@@ -54824,7 +61245,7 @@ static int pagerWalFrames( - assert( pPager->pWal ); - assert( pList ); - #ifdef SQLITE_DEBUG -- /* Verify that the page list is in accending order */ -+ /* Verify that the page list is in ascending order */ - for(p=pList; p && p->pDirty; p=p->pDirty){ - assert( p->pgno < p->pDirty->pgno ); - } -@@ -54955,7 +61376,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){ - #ifndef SQLITE_OMIT_WAL - /* - ** Check if the *-wal file that corresponds to the database opened by pPager --** exists if the database is not empy, or verify that the *-wal file does -+** exists if the database is not empty, or verify that the *-wal file does - ** not exist (by deleting it) if the database file is empty. - ** - ** If the database is not empty and the *-wal file exists, open the pager -@@ -55244,7 +61665,6 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){ - ** Numeric values associated with these states are OFF==1, NORMAL=2, - ** and FULL=3. - */ --#ifndef SQLITE_OMIT_PAGER_PRAGMAS - SQLITE_PRIVATE void sqlite3PagerSetFlags( - Pager *pPager, /* The pager to set safety level for */ - unsigned pgFlags /* Various flags */ -@@ -55279,7 +61699,6 @@ SQLITE_PRIVATE void sqlite3PagerSetFlags( - pPager->doNotSpill |= SPILLFLAG_OFF; - } - } --#endif - - /* - ** The following global variable is incremented whenever the library -@@ -55433,6 +61852,7 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR - pPager->pTmpSpace = pNew; - pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); - pPager->pageSize = pageSize; -+ pPager->lckPgno = (Pgno)(PENDING_BYTE/pageSize) + 1; - }else{ - sqlite3PageFree(pNew); - } -@@ -55593,8 +62013,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ - ** current database image, in pages, OR - ** - ** b) if the page content were written at this time, it would not --** be necessary to write the current content out to the sub-journal --** (as determined by function subjRequiresPage()). -+** be necessary to write the current content out to the sub-journal. - ** - ** If the condition asserted by this function were not true, and the - ** dirty page were to be discarded from the cache via the pagerStress() -@@ -55609,8 +62028,16 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ - */ - #if defined(SQLITE_DEBUG) - static void assertTruncateConstraintCb(PgHdr *pPg){ -+ Pager *pPager = pPg->pPager; - assert( pPg->flags&PGHDR_DIRTY ); -- assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); -+ if( pPg->pgno>pPager->dbSize ){ /* if (a) is false */ -+ Pgno pgno = pPg->pgno; -+ int i; -+ for(i=0; ipPager->nSavepoint; i++){ -+ PagerSavepoint *p = &pPager->aSavepoint[i]; -+ assert( p->nOrigpInSavepoint,pgno) ); -+ } -+ } - } - static void assertTruncateConstraint(Pager *pPager){ - sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); -@@ -55631,7 +62058,7 @@ static void assertTruncateConstraint(Pager *pPager){ - ** then continue writing to the database. - */ - SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ -- assert( pPager->dbSize>=nPage ); -+ assert( pPager->dbSize>=nPage || CORRUPT_DB ); - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); - pPager->dbSize = nPage; - -@@ -55704,6 +62131,7 @@ static int pagerAcquireMapPage( - return SQLITE_NOMEM_BKPT; - } - p->pExtra = (void *)&p[1]; -+ assert( EIGHT_BYTE_ALIGNMENT( p->pExtra ) ); - p->flags = PGHDR_MMAP; - p->nRef = 1; - p->pPager = pPager; -@@ -56359,11 +62787,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - int rc = SQLITE_OK; /* Return code */ - int tempFile = 0; /* True for temp files (incl. in-memory files) */ - int memDb = 0; /* True if this is an in-memory file */ --#ifdef SQLITE_ENABLE_DESERIALIZE - int memJM = 0; /* Memory journal mode */ --#else --# define memJM 0 --#endif - int readOnly = 0; /* True if this is a read-only file */ - int journalFileSize; /* Bytes to allocate for each journal fd */ - char *zPathname = 0; /* Full path to database file */ -@@ -56373,7 +62797,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ - const char *zUri = 0; /* URI args to copy */ - int nUriByte = 1; /* Number of bytes of URI args at *zUri */ -- int nUri = 0; /* Number of URI parameters */ -+ - - /* Figure out how much space is required for each journal file-handle - ** (there are two of them, the main journal and the sub-journal). */ -@@ -56400,8 +62824,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - */ - if( zFilename && zFilename[0] ){ - const char *z; -- nPathname = pVfs->mxPathname+1; -- zPathname = sqlite3DbMallocRaw(0, nPathname*2); -+ nPathname = pVfs->mxPathname + 1; -+ zPathname = sqlite3DbMallocRaw(0, 2*(i64)nPathname); - if( zPathname==0 ){ - return SQLITE_NOMEM_BKPT; - } -@@ -56421,7 +62845,6 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - while( *z ){ - z += strlen(z)+1; - z += strlen(z)+1; -- nUri++; - } - nUriByte = (int)(&z[1] - zUri); - assert( nUriByte>=1 ); -@@ -56484,18 +62907,19 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - ** specific formatting and order of the various filenames, so if the format - ** changes here, be sure to change it there as well. - */ -+ assert( SQLITE_PTRSIZE==sizeof(Pager*) ); - pPtr = (u8 *)sqlite3MallocZero( - ROUND8(sizeof(*pPager)) + /* Pager structure */ - ROUND8(pcacheSize) + /* PCache object */ - ROUND8(pVfs->szOsFile) + /* The main db file */ -- journalFileSize * 2 + /* The two journal files */ -- sizeof(pPager) + /* Space to hold a pointer */ -+ (u64)journalFileSize * 2 + /* The two journal files */ -+ SQLITE_PTRSIZE + /* Space to hold a pointer */ - 4 + /* Database prefix */ -- nPathname + 1 + /* database filename */ -- nUriByte + /* query parameters */ -- nPathname + 8 + 1 + /* Journal filename */ -+ (u64)nPathname + 1 + /* database filename */ -+ (u64)nUriByte + /* query parameters */ -+ (u64)nPathname + 8 + 1 + /* Journal filename */ - #ifndef SQLITE_OMIT_WAL -- nPathname + 4 + 1 + /* WAL filename */ -+ (u64)nPathname + 4 + 1 + /* WAL filename */ - #endif - 3 /* Terminator */ - ); -@@ -56510,7 +62934,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; - pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; - assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); -- memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); -+ memcpy(pPtr, &pPager, SQLITE_PTRSIZE); pPtr += SQLITE_PTRSIZE; - - /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ - pPtr += 4; /* Skip zero prefix */ -@@ -56552,6 +62976,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - pPager->zWal = 0; - } - #endif -+ (void)pPtr; /* Suppress warning about unused pPtr value */ - - if( nPathname ) sqlite3DbFree(0, zPathname); - pPager->pVfs = pVfs; -@@ -56563,9 +62988,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( - int fout = 0; /* VFS flags returned by xOpen() */ - rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); - assert( !memDb ); --#ifdef SQLITE_ENABLE_DESERIALIZE -- memJM = (fout&SQLITE_OPEN_MEMORY)!=0; --#endif -+ pPager->memVfs = memJM = (fout&SQLITE_OPEN_MEMORY)!=0; - readOnly = (fout&SQLITE_OPEN_READONLY)!=0; - - /* If the file was successfully opened for read/write access, -@@ -56676,18 +63099,7 @@ act_like_temp_file: - pPager->memDb = (u8)memDb; - pPager->readOnly = (u8)readOnly; - assert( useJournal || pPager->tempFile ); -- pPager->noSync = pPager->tempFile; -- if( pPager->noSync ){ -- assert( pPager->fullSync==0 ); -- assert( pPager->extraSync==0 ); -- assert( pPager->syncFlags==0 ); -- assert( pPager->walSyncFlags==0 ); -- }else{ -- pPager->fullSync = 1; -- pPager->extraSync = 0; -- pPager->syncFlags = SQLITE_SYNC_NORMAL; -- pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2); -- } -+ sqlite3PagerSetFlags(pPager, (SQLITE_DEFAULT_SYNCHRONOUS+1)|PAGER_CACHESPILL); - /* pPager->pFirst = 0; */ - /* pPager->pFirstSynced = 0; */ - /* pPager->pLast = 0; */ -@@ -56713,15 +63125,18 @@ act_like_temp_file: - - /* - ** Return the sqlite3_file for the main database given the name --** of the corresonding WAL or Journal name as passed into -+** of the corresponding WAL or Journal name as passed into - ** xOpen. - */ - SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){ - Pager *pPager; -+ const char *p; - while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ - zName--; - } -- pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); -+ p = zName - 4 - sizeof(Pager*); -+ assert( EIGHT_BYTE_ALIGNMENT(p) ); -+ pPager = *(Pager**)p; - return pPager->fd; - } - -@@ -56949,7 +63364,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ - ** may mean that the pager was in the error-state when this - ** function was called and the journal file does not exist. - */ -- if( !isOpen(pPager->jfd) ){ -+ if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ - sqlite3_vfs * const pVfs = pPager->pVfs; - int bExists; /* True if journal file exists */ - rc = sqlite3OsAccess( -@@ -57194,7 +63609,7 @@ static int getPageNormal( - if( pPg->pPager && !noContent ){ - /* In this case the pcache already contains an initialized copy of - ** the page. Return without further ado. */ -- assert( pgno!=PAGER_MJ_PGNO(pPager) ); -+ assert( pgno!=PAGER_SJ_PGNO(pPager) ); - pPager->aStat[PAGER_STAT_HIT]++; - return SQLITE_OK; - -@@ -57205,7 +63620,7 @@ static int getPageNormal( - ** (*) obsolete. Was: maximum page number is 2^31 - ** (2) Never try to fetch the locking page - */ -- if( pgno==PAGER_MJ_PGNO(pPager) ){ -+ if( pgno==PAGER_SJ_PGNO(pPager) ){ - rc = SQLITE_CORRUPT_BKPT; - goto pager_acquire_err; - } -@@ -57216,6 +63631,10 @@ static int getPageNormal( - if( !isOpen(pPager->fd) || pPager->dbSizepPager->mxPgno ){ - rc = SQLITE_FULL; -+ if( pgno<=pPager->dbSize ){ -+ sqlite3PcacheRelease(pPg); -+ pPg = 0; -+ } - goto pager_acquire_err; - } - if( noContent ){ -@@ -57351,7 +63770,20 @@ SQLITE_PRIVATE int sqlite3PagerGet( - DbPage **ppPage, /* Write a pointer to the page here */ - int flags /* PAGER_GET_XXX flags */ - ){ -+#if 0 /* Trace page fetch by setting to 1 */ -+ int rc; -+ printf("PAGE %u\n", pgno); -+ fflush(stdout); -+ rc = pPager->xGet(pPager, pgno, ppPage, flags); -+ if( rc ){ -+ printf("PAGE %u failed with 0x%02x\n", pgno, rc); -+ fflush(stdout); -+ } -+ return rc; -+#else -+ /* Normal, high-speed version of sqlite3PagerGet() */ - return pPager->xGet(pPager, pgno, ppPage, flags); -+#endif - } - - /* -@@ -57379,10 +63811,12 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ - /* - ** Release a page reference. - ** --** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be --** used if we know that the page being released is not the last page. -+** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be used -+** if we know that the page being released is not the last reference to page1. - ** The btree layer always holds page1 open until the end, so these first --** to routines can be used to release any page other than BtShared.pPage1. -+** two routines can be used to release any page other than BtShared.pPage1. -+** The assert() at tag-20230419-2 proves that this constraint is always -+** honored. - ** - ** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine - ** checks the total number of outstanding pages and if the number of -@@ -57398,7 +63832,7 @@ SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){ - sqlite3PcacheRelease(pPg); - } - /* Do not use this routine to release the last reference to page1 */ -- assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); -+ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); /* tag-20230419-2 */ - } - SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ - if( pPg ) sqlite3PagerUnrefNotNull(pPg); -@@ -57464,6 +63898,7 @@ static int pager_open_journal(Pager *pPager){ - - if( pPager->tempFile ){ - flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); -+ flags |= SQLITE_OPEN_EXCLUSIVE; - nSpill = sqlite3Config.nStmtSpill; - }else{ - flags |= SQLITE_OPEN_MAIN_JOURNAL; -@@ -57499,6 +63934,7 @@ static int pager_open_journal(Pager *pPager){ - if( rc!=SQLITE_OK ){ - sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; -+ pPager->journalOff = 0; - }else{ - assert( pPager->eState==PAGER_WRITER_LOCKED ); - pPager->eState = PAGER_WRITER_CACHEMOD; -@@ -57531,7 +63967,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory - assert( pPager->eState>=PAGER_READER && pPager->eStatesubjInMemory = (u8)subjInMemory; - -- if( ALWAYS(pPager->eState==PAGER_READER) ){ -+ if( pPager->eState==PAGER_READER ){ - assert( pPager->pInJournal==0 ); - - if( pagerUseWal(pPager) ){ -@@ -57603,7 +64039,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ - /* We should never write to the journal file the page that - ** contains the database locks. The following assert verifies - ** that we do not. */ -- assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); -+ assert( pPg->pgno!=PAGER_SJ_PGNO(pPager) ); - - assert( pPager->journalHdr<=pPager->journalOff ); - pData2 = pPg->pData; -@@ -57782,7 +64218,7 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){ - Pgno pg = pg1+ii; - PgHdr *pPage; - if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){ -- if( pg!=PAGER_MJ_PGNO(pPager) ){ -+ if( pg!=PAGER_SJ_PGNO(pPager) ){ - rc = sqlite3PagerGet(pPager, pg, &pPage, 0); - if( rc==SQLITE_OK ){ - rc = pager_write(pPage); -@@ -57945,7 +64381,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ - # define DIRECT_MODE isDirectMode - #endif - -- if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ -+ if( !pPager->changeCountDone && pPager->dbSize>0 ){ - PgHdr *pPgHdr; /* Reference to page 1 */ - - assert( !pPager->tempFile && isOpen(pPager->fd) ); -@@ -58223,6 +64659,13 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( - rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0); - if( rc==SQLITE_OK ){ - rc = pager_write_pagelist(pPager, pList); -+ if( rc==SQLITE_OK && pPager->dbSize>pPager->dbFileSize ){ -+ char *pTmp = pPager->pTmpSpace; -+ int szPage = (int)pPager->pageSize; -+ memset(pTmp, 0, szPage); -+ rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, -+ ((i64)pPager->dbSize*pPager->pageSize)-szPage); -+ } - if( rc==SQLITE_OK ){ - rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); - } -@@ -58260,7 +64703,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( - ** last page is never written out to disk, leaving the database file - ** undersized. Fix this now if it is the case. */ - if( pPager->dbSize>pPager->dbFileSize ){ -- Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); -+ Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_SJ_PGNO(pPager)); - assert( pPager->eState==PAGER_WRITER_DBMOD ); - rc = pager_truncate(pPager, nNew); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; -@@ -58431,8 +64874,8 @@ SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){ - ** used by the pager and its associated cache. - */ - SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager *pPager){ -- int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr) -- + 5*sizeof(void*); -+ int perPageSize = pPager->pageSize + pPager->nExtra -+ + (int)(sizeof(PgHdr) + 5*sizeof(void*)); - return perPageSize*sqlite3PcachePagecount(pPager->pPCache) - + sqlite3MallocSize(pPager) - + pPager->pageSize; -@@ -58457,11 +64900,11 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ - a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; - a[4] = pPager->eState; - a[5] = pPager->errCode; -- a[6] = pPager->aStat[PAGER_STAT_HIT]; -- a[7] = pPager->aStat[PAGER_STAT_MISS]; -+ a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff; -+ a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff; - a[8] = 0; /* Used to be pPager->nOvfl */ - a[9] = pPager->nRead; -- a[10] = pPager->aStat[PAGER_STAT_WRITE]; -+ a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff; - return a; - } - #endif -@@ -58477,7 +64920,7 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ - ** reset parameter is non-zero, the cache hit or miss count is zeroed before - ** returning. - */ --SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ -+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){ - - assert( eStat==SQLITE_DBSTATUS_CACHE_HIT - || eStat==SQLITE_DBSTATUS_CACHE_MISS -@@ -58501,7 +64944,7 @@ SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, i - ** Return true if this is an in-memory or temp-file backed pager. - */ - SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){ -- return pPager->tempFile; -+ return pPager->tempFile || pPager->memVfs; - } - - /* -@@ -58547,6 +64990,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ - } - aNew[ii].iSubRec = pPager->nSubRec; - aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); -+ aNew[ii].bTruncateOnRelease = 1; - if( !aNew[ii].pInSavepoint ){ - return SQLITE_NOMEM_BKPT; - } -@@ -58625,16 +65069,18 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ - } - pPager->nSavepoint = nNew; - -- /* If this is a release of the outermost savepoint, truncate -- ** the sub-journal to zero bytes in size. */ -+ /* Truncate the sub-journal so that it only includes the parts -+ ** that are still in use. */ - if( op==SAVEPOINT_RELEASE ){ -- if( nNew==0 && isOpen(pPager->sjfd) ){ -+ PagerSavepoint *pRel = &pPager->aSavepoint[nNew]; -+ if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){ - /* Only truncate if it is an in-memory sub-journal. */ - if( sqlite3JournalIsInMemory(pPager->sjfd) ){ -- rc = sqlite3OsTruncate(pPager->sjfd, 0); -+ i64 sz = (pPager->pageSize+4)*(i64)pRel->iSubRec; -+ rc = sqlite3OsTruncate(pPager->sjfd, sz); - assert( rc==SQLITE_OK ); - } -- pPager->nSubRec = 0; -+ pPager->nSubRec = pRel->iSubRec; - } - } - /* Else this is a rollback operation, playback the specified savepoint. -@@ -58682,7 +65128,11 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ - */ - SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){ - static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -- return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename; -+ if( nullIfMemDb && (pPager->memDb || sqlite3IsMemdb(pPager->pVfs)) ){ -+ return &zFake[4]; -+ }else{ -+ return pPager->zFilename; -+ } - } - - /* -@@ -58706,7 +65156,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ - ** This will be either the rollback journal or the WAL file. - */ - SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ --#if SQLITE_OMIT_WAL -+#ifdef SQLITE_OMIT_WAL - return pPager->jfd; - #else - return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd; -@@ -58818,7 +65268,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i - pPgOld = sqlite3PagerLookup(pPager, pgno); - assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB ); - if( pPgOld ){ -- if( pPgOld->nRef>1 ){ -+ if( NEVER(pPgOld->nRef>1) ){ - sqlite3PagerUnrefNotNull(pPgOld); - return SQLITE_CORRUPT_BKPT; - } -@@ -58953,12 +65403,12 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ - u8 eOld = pPager->journalMode; /* Prior journalmode */ - - /* The eMode parameter is always valid */ -- assert( eMode==PAGER_JOURNALMODE_DELETE -- || eMode==PAGER_JOURNALMODE_TRUNCATE -- || eMode==PAGER_JOURNALMODE_PERSIST -- || eMode==PAGER_JOURNALMODE_OFF -- || eMode==PAGER_JOURNALMODE_WAL -- || eMode==PAGER_JOURNALMODE_MEMORY ); -+ assert( eMode==PAGER_JOURNALMODE_DELETE /* 0 */ -+ || eMode==PAGER_JOURNALMODE_PERSIST /* 1 */ -+ || eMode==PAGER_JOURNALMODE_OFF /* 2 */ -+ || eMode==PAGER_JOURNALMODE_TRUNCATE /* 3 */ -+ || eMode==PAGER_JOURNALMODE_MEMORY /* 4 */ -+ || eMode==PAGER_JOURNALMODE_WAL /* 5 */ ); - - /* This routine is only called from the OP_JournalMode opcode, and - ** the logic there will never allow a temporary file to be changed -@@ -58982,7 +65432,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ - assert( pPager->eState!=PAGER_ERROR ); - pPager->journalMode = (u8)eMode; - -- /* When transistioning from TRUNCATE or PERSIST to any other journal -+ /* When transitioning from TRUNCATE or PERSIST to any other journal - ** mode except WAL, unless the pager is in locking_mode=exclusive mode, - ** delete the journal file. - */ -@@ -58995,7 +65445,6 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ - - assert( isOpen(pPager->fd) || pPager->exclusiveMode ); - if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ -- - /* In this case we would like to delete the journal file. If it is - ** not possible, then that is not a problem. Deleting the journal file - ** here is an optimization only. -@@ -59028,7 +65477,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ - } - assert( state==pPager->eState ); - } -- }else if( eMode==PAGER_JOURNALMODE_OFF ){ -+ }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){ - sqlite3OsClose(pPager->jfd); - } - } -@@ -59107,6 +65556,18 @@ SQLITE_PRIVATE int sqlite3PagerCheckpoint( - int *pnCkpt /* OUT: Final number of checkpointed frames */ - ){ - int rc = SQLITE_OK; -+ if( pPager->pWal==0 && pPager->journalMode==PAGER_JOURNALMODE_WAL ){ -+ /* This only happens when a database file is zero bytes in size opened and -+ ** then "PRAGMA journal_mode=WAL" is run and then sqlite3_wal_checkpoint() -+ ** is invoked without any intervening transactions. We need to start -+ ** a transaction to initialize pWal. The PRAGMA table_list statement is -+ ** used for this since it starts transactions on every database file, -+ ** including all ATTACHed databases. This seems expensive for a single -+ ** sqlite3_wal_checkpoint() call, but it happens very rarely. -+ ** https://sqlite.org/forum/forumpost/fd0f19d229156939 -+ */ -+ sqlite3_exec(db, "PRAGMA table_list",0,0,0); -+ } - if( pPager->pWal ){ - rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode, - (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler), -@@ -59138,13 +65599,15 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){ - */ - static int pagerExclusiveLock(Pager *pPager){ - int rc; /* Return code */ -+ u8 eOrigLock; /* Original lock */ - -- assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); -+ assert( pPager->eLock>=SHARED_LOCK ); -+ eOrigLock = pPager->eLock; - rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); - if( rc!=SQLITE_OK ){ - /* If the attempt to grab the exclusive lock failed, release the - ** pending lock that may have been obtained instead. */ -- pagerUnlockDb(pPager, SHARED_LOCK); -+ pagerUnlockDb(pPager, eOrigLock); - } - - return rc; -@@ -59179,6 +65642,11 @@ static int pagerOpenWal(Pager *pPager){ - pPager->fd, pPager->zWal, pPager->exclusiveMode, - pPager->journalSizeLimit, &pPager->pWal - ); -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ if( rc==SQLITE_OK ){ -+ sqlite3WalDb(pPager->pWal, pPager->dbWal); -+ } -+#endif - } - pagerFixMaplimit(pPager); - -@@ -59298,6 +65766,7 @@ SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){ - ** blocking locks are required. - */ - SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){ -+ pPager->dbWal = db; - if( pagerUseWal(pPager) ){ - sqlite3WalDb(pPager->pWal, db); - } -@@ -59397,6 +65866,12 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ - } - #endif - -+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) -+SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){ -+ return sqlite3WalSystemErrno(pPager->pWal); -+} -+#endif -+ - #endif /* SQLITE_OMIT_DISKIO */ - - /************** End of pager.c ***********************************************/ -@@ -59447,7 +65922,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ - ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). - ** - ** Immediately following the wal-header are zero or more frames. Each --** frame consists of a 24-byte frame-header followed by a bytes -+** frame consists of a 24-byte frame-header followed by bytes - ** of page data. The frame-header is six big-endian 32-bit unsigned - ** integer values, as follows: - ** -@@ -59564,7 +66039,10 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ - ** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and - ** HASHTABLE_NPAGE are selected so that together the wal-index header and - ** first index block are the same size as all other index blocks in the --** wal-index. -+** wal-index. The values are: -+** -+** HASHTABLE_NPAGE 4096 -+** HASHTABLE_NPAGE_ONE 4062 - ** - ** Each index block contains two sections, a page-mapping that contains the - ** database page number associated with each wal frame, and a hash-table -@@ -59684,7 +66162,7 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0; - ** - ** Technically, the various VFSes are free to implement these locks however - ** they see fit. However, compatibility is encouraged so that VFSes can --** interoperate. The standard implemention used on both unix and windows -+** interoperate. The standard implementation used on both unix and windows - ** is for the index number to indicate a byte offset into the - ** WalCkptInfo.aLock[] array in the wal-index header. In other words, all - ** locks are on the shm file. The WALINDEX_LOCK_OFFSET constant (which -@@ -59760,7 +66238,7 @@ struct WalIndexHdr { - ** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff) - ** for any aReadMark[] means that entry is unused. aReadMark[0] is - ** a special case; its value is never used and it exists as a place-holder --** to avoid having to offset aReadMark[] indexs by one. Readers holding -+** to avoid having to offset aReadMark[] indexes by one. Readers holding - ** WAL_READ_LOCK(0) always ignore the entire WAL and read all content - ** directly from the database. - ** -@@ -59800,6 +66278,70 @@ struct WalCkptInfo { - }; - #define READMARK_NOT_USED 0xffffffff - -+/* -+** This is a schematic view of the complete 136-byte header of the -+** wal-index file (also known as the -shm file): -+** -+** +-----------------------------+ -+** 0: | iVersion | \ -+** +-----------------------------+ | -+** 4: | (unused padding) | | -+** +-----------------------------+ | -+** 8: | iChange | | -+** +-------+-------+-------------+ | -+** 12: | bInit | bBig | szPage | | -+** +-------+-------+-------------+ | -+** 16: | mxFrame | | First copy of the -+** +-----------------------------+ | WalIndexHdr object -+** 20: | nPage | | -+** +-----------------------------+ | -+** 24: | aFrameCksum | | -+** | | | -+** +-----------------------------+ | -+** 32: | aSalt | | -+** | | | -+** +-----------------------------+ | -+** 40: | aCksum | | -+** | | / -+** +-----------------------------+ -+** 48: | iVersion | \ -+** +-----------------------------+ | -+** 52: | (unused padding) | | -+** +-----------------------------+ | -+** 56: | iChange | | -+** +-------+-------+-------------+ | -+** 60: | bInit | bBig | szPage | | -+** +-------+-------+-------------+ | Second copy of the -+** 64: | mxFrame | | WalIndexHdr -+** +-----------------------------+ | -+** 68: | nPage | | -+** +-----------------------------+ | -+** 72: | aFrameCksum | | -+** | | | -+** +-----------------------------+ | -+** 80: | aSalt | | -+** | | | -+** +-----------------------------+ | -+** 88: | aCksum | | -+** | | / -+** +-----------------------------+ -+** 96: | nBackfill | -+** +-----------------------------+ -+** 100: | 5 read marks | -+** | | -+** | | -+** | | -+** | | -+** +-------+-------+------+------+ -+** 120: | Write | Ckpt | Rcvr | Rd0 | \ -+** +-------+-------+------+------+ ) 8 lock bytes -+** | Read1 | Read2 | Rd3 | Rd4 | / -+** +-------+-------+------+------+ -+** 128: | nBackfillAttempted | -+** +-----------------------------+ -+** 132: | (unused padding) | -+** +-----------------------------+ -+*/ - - /* A block of WALINDEX_LOCK_RESERVED bytes beginning at - ** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems -@@ -59838,6 +66380,11 @@ struct WalCkptInfo { - /* - ** An open write-ahead log file is represented by an instance of the - ** following object. -+** -+** writeLock: -+** This is usually set to 1 whenever the WRITER lock is held. However, -+** if it is set to 2, then the WRITER lock is held but must be released -+** by walHandleException() if a SEH exception is thrown. - */ - struct Wal { - sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ -@@ -59864,11 +66411,20 @@ struct Wal { - u32 iReCksum; /* On commit, recalculate checksums from here */ - const char *zWalName; /* Name of WAL file */ - u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ -+#ifdef SQLITE_USE_SEH -+ u32 lockMask; /* Mask of locks held */ -+ void *pFree; /* Pointer to sqlite3_free() if exception thrown */ -+ u32 *pWiValue; /* Value to write into apWiData[iWiPg] */ -+ int iWiPg; /* Write pWiValue into apWiData[iWiPg] */ -+ int iSysErrno; /* System error code following exception */ -+#endif - #ifdef SQLITE_DEBUG -+ int nSehTry; /* Number of nested SEH_TRY{} blocks */ - u8 lockError; /* True if a locking error has occurred */ - #endif - #ifdef SQLITE_ENABLE_SNAPSHOT - WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ -+ int bGetSnapshot; /* Transaction opened for sqlite3_get_snapshot() */ - #endif - #ifdef SQLITE_ENABLE_SETLK_TIMEOUT - sqlite3 *db; -@@ -59919,9 +66475,13 @@ struct WalIterator { - u32 *aPgno; /* Array of page numbers. */ - int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ - int iZero; /* Frame number associated with aPgno[0] */ -- } aSegment[1]; /* One for every 32KB page in the wal-index */ -+ } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */ - }; - -+/* Size (in bytes) of a WalIterator object suitable for N or fewer segments */ -+#define SZ_WALITERATOR(N) \ -+ (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment)) -+ - /* - ** Define the parameters of the hash tables in the wal-index file. There - ** is a hash-table following every HASHTABLE_NPAGE page numbers in the -@@ -59946,6 +66506,113 @@ struct WalIterator { - sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \ - ) - -+/* -+** Structured Exception Handling (SEH) is a Windows-specific technique -+** for catching exceptions raised while accessing memory-mapped files. -+** -+** The -DSQLITE_USE_SEH compile-time option means to use SEH to catch and -+** deal with system-level errors that arise during WAL -shm file processing. -+** Without this compile-time option, any system-level faults that appear -+** while accessing the memory-mapped -shm file will cause a process-wide -+** signal to be deliver, which will more than likely cause the entire -+** process to exit. -+*/ -+#ifdef SQLITE_USE_SEH -+#include -+ -+/* Beginning of a block of code in which an exception might occur */ -+# define SEH_TRY __try { \ -+ assert( walAssertLockmask(pWal) && pWal->nSehTry==0 ); \ -+ VVA_ONLY(pWal->nSehTry++); -+ -+/* The end of a block of code in which an exception might occur */ -+# define SEH_EXCEPT(X) \ -+ VVA_ONLY(pWal->nSehTry--); \ -+ assert( pWal->nSehTry==0 ); \ -+ } __except( sehExceptionFilter(pWal, GetExceptionCode(), GetExceptionInformation() ) ){ X } -+ -+/* Simulate a memory-mapping fault in the -shm file for testing purposes */ -+# define SEH_INJECT_FAULT sehInjectFault(pWal) -+ -+/* -+** The second argument is the return value of GetExceptionCode() for the -+** current exception. Return EXCEPTION_EXECUTE_HANDLER if the exception code -+** indicates that the exception may have been caused by accessing the *-shm -+** file mapping. Or EXCEPTION_CONTINUE_SEARCH otherwise. -+*/ -+static int sehExceptionFilter(Wal *pWal, int eCode, EXCEPTION_POINTERS *p){ -+ VVA_ONLY(pWal->nSehTry--); -+ if( eCode==EXCEPTION_IN_PAGE_ERROR ){ -+ if( p && p->ExceptionRecord && p->ExceptionRecord->NumberParameters>=3 ){ -+ /* From MSDN: For this type of exception, the first element of the -+ ** ExceptionInformation[] array is a read-write flag - 0 if the exception -+ ** was thrown while reading, 1 if while writing. The second element is -+ ** the virtual address being accessed. The "third array element specifies -+ ** the underlying NTSTATUS code that resulted in the exception". */ -+ pWal->iSysErrno = (int)p->ExceptionRecord->ExceptionInformation[2]; -+ } -+ return EXCEPTION_EXECUTE_HANDLER; -+ } -+ return EXCEPTION_CONTINUE_SEARCH; -+} -+ -+/* -+** If one is configured, invoke the xTestCallback callback with 650 as -+** the argument. If it returns true, throw the same exception that is -+** thrown by the system if the *-shm file mapping is accessed after it -+** has been invalidated. -+*/ -+static void sehInjectFault(Wal *pWal){ -+ int res; -+ assert( pWal->nSehTry>0 ); -+ -+ res = sqlite3FaultSim(650); -+ if( res!=0 ){ -+ ULONG_PTR aArg[3]; -+ aArg[0] = 0; -+ aArg[1] = 0; -+ aArg[2] = (ULONG_PTR)res; -+ RaiseException(EXCEPTION_IN_PAGE_ERROR, 0, 3, (const ULONG_PTR*)aArg); -+ } -+} -+ -+/* -+** There are two ways to use this macro. To set a pointer to be freed -+** if an exception is thrown: -+** -+** SEH_FREE_ON_ERROR(0, pPtr); -+** -+** and to cancel the same: -+** -+** SEH_FREE_ON_ERROR(pPtr, 0); -+** -+** In the first case, there must not already be a pointer registered to -+** be freed. In the second case, pPtr must be the registered pointer. -+*/ -+#define SEH_FREE_ON_ERROR(X,Y) \ -+ assert( (X==0 || Y==0) && pWal->pFree==X ); pWal->pFree = Y -+ -+/* -+** There are two ways to use this macro. To arrange for pWal->apWiData[iPg] -+** to be set to pValue if an exception is thrown: -+** -+** SEH_SET_ON_ERROR(iPg, pValue); -+** -+** and to cancel the same: -+** -+** SEH_SET_ON_ERROR(0, 0); -+*/ -+#define SEH_SET_ON_ERROR(X,Y) pWal->iWiPg = X; pWal->pWiValue = Y -+ -+#else -+# define SEH_TRY VVA_ONLY(pWal->nSehTry++); -+# define SEH_EXCEPT(X) VVA_ONLY(pWal->nSehTry--); assert( pWal->nSehTry==0 ); -+# define SEH_INJECT_FAULT assert( pWal->nSehTry>0 ); -+# define SEH_FREE_ON_ERROR(X,Y) -+# define SEH_SET_ON_ERROR(X,Y) -+#endif /* ifdef SQLITE_USE_SEH */ -+ -+ - /* - ** Obtain a pointer to the iPage'th page of the wal-index. The wal-index - ** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are -@@ -59956,9 +66623,13 @@ struct WalIterator { - ** so. It is safe to enlarge the wal-index if pWal->writeLock is true - ** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE. - ** --** If this call is successful, *ppPage is set to point to the wal-index --** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, --** then an SQLite error code is returned and *ppPage is set to 0. -+** Three possible result scenarios: -+** -+** (1) rc==SQLITE_OK and *ppPage==Requested-Wal-Index-Page -+** (2) rc>=SQLITE_ERROR and *ppPage==NULL -+** (3) rc==SQLITE_OK and *ppPage==NULL // only if iPage==0 -+** -+** Scenario (3) can only occur when pWal->writeLock is false and iPage==0 - */ - static SQLITE_NOINLINE int walIndexPageRealloc( - Wal *pWal, /* The WAL context */ -@@ -59969,7 +66640,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc( - - /* Enlarge the pWal->apWiData[] array if required */ - if( pWal->nWiData<=iPage ){ -- sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); -+ sqlite3_int64 nByte = sizeof(u32*)*(1+(i64)iPage); - volatile u32 **apNew; - apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte); - if( !apNew ){ -@@ -59991,7 +66662,9 @@ static SQLITE_NOINLINE int walIndexPageRealloc( - rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, - pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] - ); -- assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); -+ assert( pWal->apWiData[iPage]!=0 -+ || rc!=SQLITE_OK -+ || (pWal->writeLock==0 && iPage==0) ); - testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); - if( rc==SQLITE_OK ){ - if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; -@@ -60012,6 +66685,7 @@ static int walIndexPage( - int iPage, /* The page we seek */ - volatile u32 **ppPage /* Write the page pointer here */ - ){ -+ SEH_INJECT_FAULT; - if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){ - return walIndexPageRealloc(pWal, iPage, ppPage); - } -@@ -60023,6 +66697,7 @@ static int walIndexPage( - */ - static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); -+ SEH_INJECT_FAULT; - return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]); - } - -@@ -60031,6 +66706,7 @@ static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ - */ - static volatile WalIndexHdr *walIndexHdr(Wal *pWal){ - assert( pWal->nWiData>0 && pWal->apWiData[0] ); -+ SEH_INJECT_FAULT; - return (volatile WalIndexHdr*)pWal->apWiData[0]; - } - -@@ -60073,22 +66749,41 @@ static void walChecksumBytes( - s1 = s2 = 0; - } - -- assert( nByte>=8 ); -- assert( (nByte&0x00000007)==0 ); -- assert( nByte<=65536 ); -+ /* nByte is a multiple of 8 between 8 and 65536 */ -+ assert( nByte>=8 && (nByte&7)==0 && nByte<=65536 ); - -- if( nativeCksum ){ -+ if( !nativeCksum ){ -+ do { -+ s1 += BYTESWAP32(aData[0]) + s2; -+ s2 += BYTESWAP32(aData[1]) + s1; -+ aData += 2; -+ }while( aDatalockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) -+#ifdef SQLITE_USE_SEH -+ if( rc==SQLITE_OK ) pWal->lockMask |= (1 << lockIdx); -+#endif - return rc; - } - static void walUnlockShared(Wal *pWal, int lockIdx){ - if( pWal->exclusiveMode ) return; - (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, - SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); -+#ifdef SQLITE_USE_SEH -+ pWal->lockMask &= ~(1 << lockIdx); -+#endif - WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); - } - static int walLockExclusive(Wal *pWal, int lockIdx, int n){ -@@ -60283,12 +66984,20 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){ - WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, - walLockName(lockIdx), n, rc ? "failed" : "ok")); - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) -+#ifdef SQLITE_USE_SEH -+ if( rc==SQLITE_OK ){ -+ pWal->lockMask |= (((1<exclusiveMode ) return; - (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, - SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); -+#ifdef SQLITE_USE_SEH -+ pWal->lockMask &= ~(((1<iZero+N) in the log. - ** --** Finally, set pLoc->aPgno so that pLoc->aPgno[1] is the page number of the --** first frame indexed by the hash table, frame (pLoc->iZero+1). -+** Finally, set pLoc->aPgno so that pLoc->aPgno[0] is the page number of the -+** first frame indexed by the hash table, frame (pLoc->iZero). - */ - static int walHashGet( - Wal *pWal, /* WAL handle */ -@@ -60343,7 +67052,7 @@ static int walHashGet( - rc = walIndexPage(pWal, iHash, &pLoc->aPgno); - assert( rc==SQLITE_OK || iHash>0 ); - -- if( rc==SQLITE_OK ){ -+ if( pLoc->aPgno ){ - pLoc->aHash = (volatile ht_slot *)&pLoc->aPgno[HASHTABLE_NPAGE]; - if( iHash==0 ){ - pLoc->aPgno = &pLoc->aPgno[WALINDEX_HDR_SIZE/sizeof(u32)]; -@@ -60351,7 +67060,8 @@ static int walHashGet( - }else{ - pLoc->iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE; - } -- pLoc->aPgno = &pLoc->aPgno[-1]; -+ }else if( NEVER(rc==SQLITE_OK) ){ -+ rc = SQLITE_ERROR; - } - return rc; - } -@@ -60379,6 +67089,7 @@ static int walFramePage(u32 iFrame){ - */ - static u32 walFramePgno(Wal *pWal, u32 iFrame){ - int iHash = walFramePage(iFrame); -+ SEH_INJECT_FAULT; - if( iHash==0 ){ - return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1]; - } -@@ -60402,7 +67113,6 @@ static void walCleanupHash(Wal *pWal){ - int iLimit = 0; /* Zero values greater than this */ - int nByte; /* Number of bytes to zero in aPgno[] */ - int i; /* Used to iterate through aHash[] */ -- int rc; /* Return code form walHashGet() */ - - assert( pWal->writeLock ); - testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); -@@ -60417,8 +67127,8 @@ static void walCleanupHash(Wal *pWal){ - */ - assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); - assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); -- rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); -- if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */ -+ i = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); -+ if( NEVER(i) ) return; /* Defense-in-depth, in case (1) above is wrong */ - - /* Zero all hash-table entries that correspond to frame numbers greater - ** than pWal->hdr.mxFrame. -@@ -60434,8 +67144,9 @@ static void walCleanupHash(Wal *pWal){ - /* Zero the entries in the aPgno array that correspond to frames with - ** frame numbers greater than pWal->hdr.mxFrame. - */ -- nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit+1]); -- memset((void *)&sLoc.aPgno[iLimit+1], 0, nByte); -+ nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit]); -+ assert( nByte>=0 ); -+ memset((void *)&sLoc.aPgno[iLimit], 0, nByte); - - #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT - /* Verify that the every entry in the mapping region is still reachable -@@ -60444,11 +67155,11 @@ static void walCleanupHash(Wal *pWal){ - if( iLimit ){ - int j; /* Loop counter */ - int iKey; /* Hash key */ -- for(j=1; j<=iLimit; j++){ -+ for(j=0; j=0 ); -+ memset((void*)sLoc.aPgno, 0, nByte); - } - - /* If the entry in aPgno[] is already set, then the previous writer -@@ -60491,9 +67202,9 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ - ** Remove the remnants of that writers uncommitted transaction from - ** the hash-table before writing any new entries. - */ -- if( sLoc.aPgno[idx] ){ -+ if( sLoc.aPgno[idx-1] ){ - walCleanupHash(pWal); -- assert( !sLoc.aPgno[idx] ); -+ assert( !sLoc.aPgno[idx-1] ); - } - - /* Write the aPgno[] array entry and the hash-table slot. */ -@@ -60501,7 +67212,7 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ - for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ - if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; - } -- sLoc.aPgno[idx] = iPage; -+ sLoc.aPgno[idx-1] = iPage; - AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); - - #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT -@@ -60522,19 +67233,18 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ - */ - if( (idx&0x3ff)==0 ){ - int i; /* Loop counter */ -- for(i=1; i<=idx; i++){ -+ for(i=0; iapWiData[iPg] = aPrivate; - - for(iFrame=iFirst; iFrame<=iLast; iFrame++){ -@@ -60683,6 +67396,7 @@ static int walIndexRecover(Wal *pWal){ - } - } - pWal->apWiData[iPg] = aShare; -+ SEH_SET_ON_ERROR(0,0); - nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); - nHdr32 = nHdr / sizeof(u32); - #ifndef SQLITE_SAFER_WALINDEX_RECOVERY -@@ -60713,9 +67427,11 @@ static int walIndexRecover(Wal *pWal){ - } - } - #endif -+ SEH_INJECT_FAULT; - if( iFrame<=iLast ) break; - } - -+ SEH_FREE_ON_ERROR(aFrame, 0); - sqlite3_free(aFrame); - } - -@@ -60743,6 +67459,7 @@ finished: - }else{ - pInfo->aReadMark[i] = READMARK_NOT_USED; - } -+ SEH_INJECT_FAULT; - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc!=SQLITE_BUSY ){ - goto recovery_error; -@@ -60814,14 +67531,43 @@ SQLITE_PRIVATE int sqlite3WalOpen( - assert( zWalName && zWalName[0] ); - assert( pDbFd ); - -+ /* Verify the values of various constants. Any changes to the values -+ ** of these constants would result in an incompatible on-disk format -+ ** for the -shm file. Any change that causes one of these asserts to -+ ** fail is a backward compatibility problem, even if the change otherwise -+ ** works. -+ ** -+ ** This table also serves as a helpful cross-reference when trying to -+ ** interpret hex dumps of the -shm file. -+ */ -+ assert( 48 == sizeof(WalIndexHdr) ); -+ assert( 40 == sizeof(WalCkptInfo) ); -+ assert( 120 == WALINDEX_LOCK_OFFSET ); -+ assert( 136 == WALINDEX_HDR_SIZE ); -+ assert( 4096 == HASHTABLE_NPAGE ); -+ assert( 4062 == HASHTABLE_NPAGE_ONE ); -+ assert( 8192 == HASHTABLE_NSLOT ); -+ assert( 383 == HASHTABLE_HASH_1 ); -+ assert( 32768 == WALINDEX_PGSZ ); -+ assert( 8 == SQLITE_SHM_NLOCK ); -+ assert( 5 == WAL_NREADER ); -+ assert( 24 == WAL_FRAME_HDRSIZE ); -+ assert( 32 == WAL_HDRSIZE ); -+ assert( 120 == WALINDEX_LOCK_OFFSET + WAL_WRITE_LOCK ); -+ assert( 121 == WALINDEX_LOCK_OFFSET + WAL_CKPT_LOCK ); -+ assert( 122 == WALINDEX_LOCK_OFFSET + WAL_RECOVER_LOCK ); -+ assert( 123 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(0) ); -+ assert( 124 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(1) ); -+ assert( 125 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(2) ); -+ assert( 126 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(3) ); -+ assert( 127 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(4) ); -+ - /* In the amalgamation, the os_unix.c and os_win.c source files come before - ** this source file. Verify that the #defines of the locking byte offsets - ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value. - ** For that matter, if the lock offset ever changes from its initial design - ** value of 120, we need to know that so there is an assert() to check it. - */ -- assert( 120==WALINDEX_LOCK_OFFSET ); -- assert( 136==WALINDEX_HDR_SIZE ); - #ifdef WIN_SHM_BASE - assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET ); - #endif -@@ -60871,7 +67617,7 @@ SQLITE_PRIVATE int sqlite3WalOpen( - } - - /* --** Change the size to which the WAL file is trucated on each reset. -+** Change the size to which the WAL file is truncated on each reset. - */ - SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){ - if( pWal ) pWal->mxWalSize = iLimit; -@@ -61094,26 +67840,18 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ - - /* Allocate space for the WalIterator object. */ - nSegment = walFramePage(iLast) + 1; -- nByte = sizeof(WalIterator) -- + (nSegment-1)*sizeof(struct WalSegment) -+ nByte = SZ_WALITERATOR(nSegment) - + iLast*sizeof(ht_slot); -- p = (WalIterator *)sqlite3_malloc64(nByte); -+ p = (WalIterator *)sqlite3_malloc64(nByte -+ + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) -+ ); - if( !p ){ - return SQLITE_NOMEM_BKPT; - } - memset(p, 0, nByte); - p->nSegment = nSegment; -- -- /* Allocate temporary space used by the merge-sort routine. This block -- ** of memory will be freed before this function returns. -- */ -- aTmp = (ht_slot *)sqlite3_malloc64( -- sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) -- ); -- if( !aTmp ){ -- rc = SQLITE_NOMEM_BKPT; -- } -- -+ aTmp = (ht_slot*)&(((u8*)p)[nByte]); -+ SEH_FREE_ON_ERROR(0, p); - for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && iaSegment[i].aPgno = (u32 *)sLoc.aPgno; - } - } -- sqlite3_free(aTmp); -- - if( rc!=SQLITE_OK ){ -+ SEH_FREE_ON_ERROR(p, 0); - walIteratorFree(p); - p = 0; - } -@@ -61153,6 +67889,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ - } - - #ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ -+ -+/* -+** Attempt to enable blocking locks that block for nMs ms. Return 1 if -+** blocking locks are successfully enabled, or 0 otherwise. -+*/ -+static int walEnableBlockingMs(Wal *pWal, int nMs){ -+ int rc = sqlite3OsFileControl( -+ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs -+ ); -+ return (rc==SQLITE_OK); -+} -+ - /* - ** Attempt to enable blocking locks. Blocking locks are enabled only if (a) - ** they are supported by the VFS, and (b) the database handle is configured -@@ -61162,13 +67911,9 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ - static int walEnableBlocking(Wal *pWal){ - int res = 0; - if( pWal->db ){ -- int tmout = pWal->db->busyTimeout; -+ int tmout = pWal->db->setlkTimeout; - if( tmout ){ -- int rc; -- rc = sqlite3OsFileControl( -- pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout -- ); -- res = (rc==SQLITE_OK); -+ res = walEnableBlockingMs(pWal, tmout); - } - } - return res; -@@ -61217,20 +67962,10 @@ SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){ - pWal->db = db; - } - --/* --** Take an exclusive WRITE lock. Blocking if so configured. --*/ --static int walLockWriter(Wal *pWal){ -- int rc; -- walEnableBlocking(pWal); -- rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); -- walDisableBlocking(pWal); -- return rc; --} - #else - # define walEnableBlocking(x) 0 - # define walDisableBlocking(x) --# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) -+# define walEnableBlockingMs(pWal, ms) 0 - # define sqlite3WalDb(pWal, db) - #endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ - -@@ -61370,13 +68105,13 @@ static int walCheckpoint( - mxSafeFrame = pWal->hdr.mxFrame; - mxPage = pWal->hdr.nPage; - for(i=1; iaReadMark+i); -+ u32 y = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; - if( mxSafeFrame>y ){ - assert( y<=pWal->hdr.mxFrame ); - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); - if( rc==SQLITE_OK ){ - u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); -- AtomicStore(pInfo->aReadMark+i, iMark); -+ AtomicStore(pInfo->aReadMark+i, iMark); SEH_INJECT_FAULT; - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc==SQLITE_BUSY ){ - mxSafeFrame = y; -@@ -61397,8 +68132,7 @@ static int walCheckpoint( - && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK - ){ - u32 nBackfill = pInfo->nBackfill; -- -- pInfo->nBackfillAttempted = mxSafeFrame; -+ pInfo->nBackfillAttempted = mxSafeFrame; SEH_INJECT_FAULT; - - /* Sync the WAL to disk */ - rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); -@@ -61429,6 +68163,7 @@ static int walCheckpoint( - while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ - i64 iOffset; - assert( walFramePgno(pWal, iFrame)==iDbpage ); -+ SEH_INJECT_FAULT; - if( AtomicLoad(&db->u1.isInterrupted) ){ - rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; - break; -@@ -61458,7 +68193,7 @@ static int walCheckpoint( - } - } - if( rc==SQLITE_OK ){ -- AtomicStore(&pInfo->nBackfill, mxSafeFrame); -+ AtomicStore(&pInfo->nBackfill, mxSafeFrame); SEH_INJECT_FAULT; - } - } - -@@ -61480,6 +68215,7 @@ static int walCheckpoint( - */ - if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ - assert( pWal->writeLock ); -+ SEH_INJECT_FAULT; - if( pInfo->nBackfillhdr.mxFrame ){ - rc = SQLITE_BUSY; - }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ -@@ -61511,6 +68247,7 @@ static int walCheckpoint( - } - - walcheckpoint_out: -+ SEH_FREE_ON_ERROR(pIter, 0); - walIteratorFree(pIter); - return rc; - } -@@ -61533,6 +68270,95 @@ static void walLimitSize(Wal *pWal, i64 nMax){ - } - } - -+#ifdef SQLITE_USE_SEH -+/* -+** This is the "standard" exception handler used in a few places to handle -+** an exception thrown by reading from the *-shm mapping after it has become -+** invalid in SQLITE_USE_SEH builds. It is used as follows: -+** -+** SEH_TRY { ... } -+** SEH_EXCEPT( rc = walHandleException(pWal); ) -+** -+** This function does three things: -+** -+** 1) Determines the locks that should be held, based on the contents of -+** the Wal.readLock, Wal.writeLock and Wal.ckptLock variables. All other -+** held locks are assumed to be transient locks that would have been -+** released had the exception not been thrown and are dropped. -+** -+** 2) Frees the pointer at Wal.pFree, if any, using sqlite3_free(). -+** -+** 3) Set pWal->apWiData[pWal->iWiPg] to pWal->pWiValue if not NULL -+** -+** 4) Returns SQLITE_IOERR. -+*/ -+static int walHandleException(Wal *pWal){ -+ if( pWal->exclusiveMode==0 ){ -+ static const int S = 1; -+ static const int E = (1<writeLock==2 ) pWal->writeLock = 0; -+ mUnlock = pWal->lockMask & ~( -+ (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) -+ | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) -+ | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) -+ ); -+ for(ii=0; iipFree); -+ pWal->pFree = 0; -+ if( pWal->pWiValue ){ -+ pWal->apWiData[pWal->iWiPg] = pWal->pWiValue; -+ pWal->pWiValue = 0; -+ } -+ return SQLITE_IOERR_IN_PAGE; -+} -+ -+/* -+** Assert that the Wal.lockMask mask, which indicates the locks held -+** by the connection, is consistent with the Wal.readLock, Wal.writeLock -+** and Wal.ckptLock variables. To be used as: -+** -+** assert( walAssertLockmask(pWal) ); -+*/ -+static int walAssertLockmask(Wal *pWal){ -+ if( pWal->exclusiveMode==0 ){ -+ static const int S = 1; -+ static const int E = (1<readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) -+ | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) -+ | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) -+#ifdef SQLITE_ENABLE_SNAPSHOT -+ | (pWal->pSnapshot ? (pWal->lockMask & (1 << WAL_CKPT_LOCK)) : 0) -+#endif -+ ); -+ assert( mExpect==pWal->lockMask ); -+ } -+ return 1; -+} -+ -+/* -+** Return and zero the "system error" field set when an -+** EXCEPTION_IN_PAGE_ERROR exception is caught. -+*/ -+SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal *pWal){ -+ int iRet = 0; -+ if( pWal ){ -+ iRet = pWal->iSysErrno; -+ pWal->iSysErrno = 0; -+ } -+ return iRet; -+} -+ -+#else -+# define walAssertLockmask(x) 1 -+#endif /* ifdef SQLITE_USE_SEH */ -+ - /* - ** Close a connection to a log file. - */ -@@ -61547,6 +68373,8 @@ SQLITE_PRIVATE int sqlite3WalClose( - if( pWal ){ - int isDelete = 0; /* True to unlink wal and wal-index files */ - -+ assert( walAssertLockmask(pWal) ); -+ - /* If an EXCLUSIVE lock can be obtained on the database file (using the - ** ordinary, rollback-mode locking methods, this guarantees that the - ** connection associated with this log file is the only connection to -@@ -61571,7 +68399,7 @@ SQLITE_PRIVATE int sqlite3WalClose( - ); - if( bPersist!=1 ){ - /* Try to delete the WAL file if the checkpoint completed and -- ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal -+ ** fsynced (rc==SQLITE_OK) and if we are not in persistent-wal - ** mode (!bPersist) */ - isDelete = 1; - }else if( pWal->mxWalSize>=0 ){ -@@ -61638,7 +68466,7 @@ static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ - ** give false-positive warnings about these accesses because the tools do not - ** account for the double-read and the memory barrier. The use of mutexes - ** here would be problematic as the memory being accessed is potentially -- ** shared among multiple processes and not all mutex implementions work -+ ** shared among multiple processes and not all mutex implementations work - ** reliably in that environment. - */ - aHdr = walIndexHdr(pWal); -@@ -61740,15 +68568,23 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ - } - }else{ - int bWriteLock = pWal->writeLock; -- if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ -- pWal->writeLock = 1; -+ if( bWriteLock -+ || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) -+ ){ -+ /* If the write-lock was just obtained, set writeLock to 2 instead of -+ ** the usual 1. This causes walIndexPage() to behave as if the -+ ** write-lock were held (so that it allocates new pages as required), -+ ** and walHandleException() to unlock the write-lock if a SEH exception -+ ** is thrown. */ -+ if( !bWriteLock ) pWal->writeLock = 2; - if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ - badHdr = walIndexTryHdr(pWal, pChanged); - if( badHdr ){ - /* If the wal-index header is still malformed even while holding - ** a WRITE lock, it can only mean that the header is corrupted and - ** needs to be reconstructed. So run recovery to do exactly that. -- */ -+ ** Disable blocking locks first. */ -+ walDisableBlocking(pWal); - rc = walIndexRecover(pWal); - *pChanged = 1; - } -@@ -61904,7 +68740,9 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ - } - - /* Allocate a buffer to read frames into */ -- szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE; -+ assert( (pWal->szPage & (pWal->szPage-1))==0 ); -+ assert( pWal->szPage>=512 && pWal->szPage<=65536 ); -+ szFrame = pWal->szPage + WAL_FRAME_HDRSIZE; - aFrame = (u8 *)sqlite3_malloc64(szFrame); - if( aFrame==0 ){ - rc = SQLITE_NOMEM_BKPT; -@@ -61918,7 +68756,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ - ** the caller. */ - aSaveCksum[0] = pWal->hdr.aFrameCksum[0]; - aSaveCksum[1] = pWal->hdr.aFrameCksum[1]; -- for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); -+ for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->szPage); - iOffset+szFrame<=szWal; - iOffset+=szFrame - ){ -@@ -61956,6 +68794,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ - return rc; - } - -+/* -+** The final argument passed to walTryBeginRead() is of type (int*). The -+** caller should invoke walTryBeginRead as follows: -+** -+** int cnt = 0; -+** do { -+** rc = walTryBeginRead(..., &cnt); -+** }while( rc==WAL_RETRY ); -+** -+** The final value of "cnt" is of no use to the caller. It is used by -+** the implementation of walTryBeginRead() as follows: -+** -+** + Each time walTryBeginRead() is called, it is incremented. Once -+** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead() -+** has many times been invoked and failed with WAL_RETRY - walTryBeginRead() -+** returns SQLITE_PROTOCOL. -+** -+** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed -+** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS -+** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case -+** the next invocation of walTryBeginRead() may omit an expected call to -+** sqlite3OsSleep(). There has already been a delay when the previous call -+** waited on a lock. -+*/ -+#define WAL_RETRY_PROTOCOL_LIMIT 100 -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+# define WAL_RETRY_BLOCKED_MASK 0x10000000 -+#else -+# define WAL_RETRY_BLOCKED_MASK 0 -+#endif -+ - /* - ** Attempt to start a read transaction. This might fail due to a race or - ** other transient condition. When that happens, it returns WAL_RETRY to -@@ -62006,13 +68875,12 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ - ** so it takes care to hold an exclusive lock on the corresponding - ** WAL_READ_LOCK() while changing values. - */ --static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ -+static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ - volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ -- u32 mxReadMark; /* Largest aReadMark[] value */ -- int mxI; /* Index of largest aReadMark[] value */ -- int i; /* Loop counter */ - int rc = SQLITE_OK; /* Return code */ -- u32 mxFrame; /* Wal frame to lock to */ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ int nBlockTmout = 0; -+#endif - - assert( pWal->readLock<0 ); /* Not currently locked */ - -@@ -62036,14 +68904,34 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ - ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. - ** The total delay time before giving up is less than 10 seconds. - */ -- if( cnt>5 ){ -+ (*pCnt)++; -+ if( *pCnt>5 ){ - int nDelay = 1; /* Pause time in microseconds */ -- if( cnt>100 ){ -+ int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK); -+ if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){ - VVA_ONLY( pWal->lockError = 1; ) - return SQLITE_PROTOCOL; - } -- if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; -+ if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor -+ ** to block for locks for approximately nDelay us. This affects three -+ ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if -+ ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the -+ ** first attempted read fails, and (c) the shared lock taken on the -+ ** read-mark. -+ ** -+ ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error, -+ ** then sleep for the minimum of 1us. The previous call already provided -+ ** an extra delay while it was blocking on the lock. -+ */ -+ nBlockTmout = (nDelay+998) / 1000; -+ if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){ -+ if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1; -+ } -+#endif - sqlite3OsSleep(pWal->pVfs, nDelay); -+ *pCnt &= ~WAL_RETRY_BLOCKED_MASK; - } - - if( !useWal ){ -@@ -62051,6 +68939,12 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ - if( pWal->bShmUnreliable==0 ){ - rc = walIndexReadHdr(pWal, pChanged); - } -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ if( rc==SQLITE_BUSY_TIMEOUT ){ -+ rc = SQLITE_BUSY; -+ *pCnt |= WAL_RETRY_BLOCKED_MASK; -+ } -+#endif - if( rc==SQLITE_BUSY ){ - /* If there is not a recovery running in another thread or process - ** then convert BUSY errors to WAL_RETRY. If recovery is known to -@@ -62060,6 +68954,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ - ** WAL_RETRY this routine will be called again and will probably be - ** right on the second iteration. - */ -+ (void)walEnableBlocking(pWal); - if( pWal->apWiData[0]==0 ){ - /* This branch is taken when the xShmMap() method returns SQLITE_BUSY. - ** We assume this is a transient condition, so return WAL_RETRY. The -@@ -62076,6 +68971,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ - rc = SQLITE_BUSY_RECOVERY; - } - } -+ walDisableBlocking(pWal); - if( rc!=SQLITE_OK ){ - return rc; - } -@@ -62087,136 +68983,201 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ - assert( pWal->nWiData>0 ); - assert( pWal->apWiData[0]!=0 ); - pInfo = walCkptInfo(pWal); -- if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame -+ SEH_INJECT_FAULT; -+ { -+ u32 mxReadMark; /* Largest aReadMark[] value */ -+ int mxI; /* Index of largest aReadMark[] value */ -+ int i; /* Loop counter */ -+ u32 mxFrame; /* Wal frame to lock to */ -+ if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame - #ifdef SQLITE_ENABLE_SNAPSHOT -- && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) -+ && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) - #endif -- ){ -- /* The WAL has been completely backfilled (or it is empty). -- ** and can be safely ignored. -- */ -- rc = walLockShared(pWal, WAL_READ_LOCK(0)); -- walShmBarrier(pWal); -- if( rc==SQLITE_OK ){ -- if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ -- /* It is not safe to allow the reader to continue here if frames -- ** may have been appended to the log before READ_LOCK(0) was obtained. -- ** When holding READ_LOCK(0), the reader ignores the entire log file, -- ** which implies that the database file contains a trustworthy -- ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from -- ** happening, this is usually correct. -- ** -- ** However, if frames have been appended to the log (or if the log -- ** is wrapped and written for that matter) before the READ_LOCK(0) -- ** is obtained, that is not necessarily true. A checkpointer may -- ** have started to backfill the appended frames but crashed before -- ** it finished. Leaving a corrupt image in the database file. -- */ -- walUnlockShared(pWal, WAL_READ_LOCK(0)); -- return WAL_RETRY; -+ ){ -+ /* The WAL has been completely backfilled (or it is empty). -+ ** and can be safely ignored. -+ */ -+ rc = walLockShared(pWal, WAL_READ_LOCK(0)); -+ walShmBarrier(pWal); -+ if( rc==SQLITE_OK ){ -+ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){ -+ /* It is not safe to allow the reader to continue here if frames -+ ** may have been appended to the log before READ_LOCK(0) was obtained. -+ ** When holding READ_LOCK(0), the reader ignores the entire log file, -+ ** which implies that the database file contains a trustworthy -+ ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from -+ ** happening, this is usually correct. -+ ** -+ ** However, if frames have been appended to the log (or if the log -+ ** is wrapped and written for that matter) before the READ_LOCK(0) -+ ** is obtained, that is not necessarily true. A checkpointer may -+ ** have started to backfill the appended frames but crashed before -+ ** it finished. Leaving a corrupt image in the database file. -+ */ -+ walUnlockShared(pWal, WAL_READ_LOCK(0)); -+ return WAL_RETRY; -+ } -+ pWal->readLock = 0; -+ return SQLITE_OK; -+ }else if( rc!=SQLITE_BUSY ){ -+ return rc; - } -- pWal->readLock = 0; -- return SQLITE_OK; -- }else if( rc!=SQLITE_BUSY ){ -- return rc; - } -- } - -- /* If we get this far, it means that the reader will want to use -- ** the WAL to get at content from recent commits. The job now is -- ** to select one of the aReadMark[] entries that is closest to -- ** but not exceeding pWal->hdr.mxFrame and lock that entry. -- */ -- mxReadMark = 0; -- mxI = 0; -- mxFrame = pWal->hdr.mxFrame; -+ /* If we get this far, it means that the reader will want to use -+ ** the WAL to get at content from recent commits. The job now is -+ ** to select one of the aReadMark[] entries that is closest to -+ ** but not exceeding pWal->hdr.mxFrame and lock that entry. -+ */ -+ mxReadMark = 0; -+ mxI = 0; -+ mxFrame = pWal->hdr.mxFrame; - #ifdef SQLITE_ENABLE_SNAPSHOT -- if( pWal->pSnapshot && pWal->pSnapshot->mxFramepSnapshot->mxFrame; -- } --#endif -- for(i=1; iaReadMark+i); -- if( mxReadMark<=thisMark && thisMark<=mxFrame ){ -- assert( thisMark!=READMARK_NOT_USED ); -- mxReadMark = thisMark; -- mxI = i; -+ if( pWal->pSnapshot && pWal->pSnapshot->mxFramepSnapshot->mxFrame; - } -- } -- if( (pWal->readOnly & WAL_SHM_RDONLY)==0 -- && (mxReadMarkaReadMark+i,mxFrame); -- mxReadMark = mxFrame; -+ u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; -+ if( mxReadMark<=thisMark && thisMark<=mxFrame ){ -+ assert( thisMark!=READMARK_NOT_USED ); -+ mxReadMark = thisMark; - mxI = i; -- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); -- break; -- }else if( rc!=SQLITE_BUSY ){ -- return rc; - } - } -- } -- if( mxI==0 ){ -- assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); -- return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; -- } -+ if( (pWal->readOnly & WAL_SHM_RDONLY)==0 -+ && (mxReadMarkaReadMark+i,mxFrame); -+ mxReadMark = mxFrame; -+ mxI = i; -+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); -+ break; -+ }else if( rc!=SQLITE_BUSY ){ -+ return rc; -+ } -+ } -+ } -+ if( mxI==0 ){ -+ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); -+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; -+ } - -- rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); -- if( rc ){ -- return rc==SQLITE_BUSY ? WAL_RETRY : rc; -- } -- /* Now that the read-lock has been obtained, check that neither the -- ** value in the aReadMark[] array or the contents of the wal-index -- ** header have changed. -- ** -- ** It is necessary to check that the wal-index header did not change -- ** between the time it was read and when the shared-lock was obtained -- ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility -- ** that the log file may have been wrapped by a writer, or that frames -- ** that occur later in the log than pWal->hdr.mxFrame may have been -- ** copied into the database by a checkpointer. If either of these things -- ** happened, then reading the database with the current value of -- ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry -- ** instead. -- ** -- ** Before checking that the live wal-index header has not changed -- ** since it was read, set Wal.minFrame to the first frame in the wal -- ** file that has not yet been checkpointed. This client will not need -- ** to read any frames earlier than minFrame from the wal file - they -- ** can be safely read directly from the database file. -- ** -- ** Because a ShmBarrier() call is made between taking the copy of -- ** nBackfill and checking that the wal-header in shared-memory still -- ** matches the one cached in pWal->hdr, it is guaranteed that the -- ** checkpointer that set nBackfill was not working with a wal-index -- ** header newer than that cached in pWal->hdr. If it were, that could -- ** cause a problem. The checkpointer could omit to checkpoint -- ** a version of page X that lies before pWal->minFrame (call that version -- ** A) on the basis that there is a newer version (version B) of the same -- ** page later in the wal file. But if version B happens to like past -- ** frame pWal->hdr.mxFrame - then the client would incorrectly assume -- ** that it can read version A from the database file. However, since -- ** we can guarantee that the checkpointer that set nBackfill could not -- ** see any pages past pWal->hdr.mxFrame, this problem does not come up. -- */ -- pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; -- walShmBarrier(pWal); -- if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark -- || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) -- ){ -- walUnlockShared(pWal, WAL_READ_LOCK(mxI)); -- return WAL_RETRY; -- }else{ -- assert( mxReadMark<=pWal->hdr.mxFrame ); -- pWal->readLock = (i16)mxI; -+ (void)walEnableBlockingMs(pWal, nBlockTmout); -+ rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); -+ walDisableBlocking(pWal); -+ if( rc ){ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ if( rc==SQLITE_BUSY_TIMEOUT ){ -+ *pCnt |= WAL_RETRY_BLOCKED_MASK; -+ } -+#else -+ assert( rc!=SQLITE_BUSY_TIMEOUT ); -+#endif -+ assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT); -+ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; -+ } -+ /* Now that the read-lock has been obtained, check that neither the -+ ** value in the aReadMark[] array or the contents of the wal-index -+ ** header have changed. -+ ** -+ ** It is necessary to check that the wal-index header did not change -+ ** between the time it was read and when the shared-lock was obtained -+ ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility -+ ** that the log file may have been wrapped by a writer, or that frames -+ ** that occur later in the log than pWal->hdr.mxFrame may have been -+ ** copied into the database by a checkpointer. If either of these things -+ ** happened, then reading the database with the current value of -+ ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry -+ ** instead. -+ ** -+ ** Before checking that the live wal-index header has not changed -+ ** since it was read, set Wal.minFrame to the first frame in the wal -+ ** file that has not yet been checkpointed. This client will not need -+ ** to read any frames earlier than minFrame from the wal file - they -+ ** can be safely read directly from the database file. -+ ** -+ ** Because a ShmBarrier() call is made between taking the copy of -+ ** nBackfill and checking that the wal-header in shared-memory still -+ ** matches the one cached in pWal->hdr, it is guaranteed that the -+ ** checkpointer that set nBackfill was not working with a wal-index -+ ** header newer than that cached in pWal->hdr. If it were, that could -+ ** cause a problem. The checkpointer could omit to checkpoint -+ ** a version of page X that lies before pWal->minFrame (call that version -+ ** A) on the basis that there is a newer version (version B) of the same -+ ** page later in the wal file. But if version B happens to like past -+ ** frame pWal->hdr.mxFrame - then the client would incorrectly assume -+ ** that it can read version A from the database file. However, since -+ ** we can guarantee that the checkpointer that set nBackfill could not -+ ** see any pages past pWal->hdr.mxFrame, this problem does not come up. -+ */ -+ pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT; -+ walShmBarrier(pWal); -+ if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark -+ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) -+ ){ -+ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); -+ return WAL_RETRY; -+ }else{ -+ assert( mxReadMark<=pWal->hdr.mxFrame ); -+ pWal->readLock = (i16)mxI; -+ } - } - return rc; - } - - #ifdef SQLITE_ENABLE_SNAPSHOT -+/* -+** This function does the work of sqlite3WalSnapshotRecover(). -+*/ -+static int walSnapshotRecover( -+ Wal *pWal, /* WAL handle */ -+ void *pBuf1, /* Temp buffer pWal->szPage bytes in size */ -+ void *pBuf2 /* Temp buffer pWal->szPage bytes in size */ -+){ -+ int szPage = (int)pWal->szPage; -+ int rc; -+ i64 szDb; /* Size of db file in bytes */ -+ -+ rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); -+ if( rc==SQLITE_OK ){ -+ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); -+ u32 i = pInfo->nBackfillAttempted; -+ for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ -+ WalHashLoc sLoc; /* Hash table location */ -+ u32 pgno; /* Page number in db file */ -+ i64 iDbOff; /* Offset of db file entry */ -+ i64 iWalOff; /* Offset of wal file entry */ -+ -+ rc = walHashGet(pWal, walFramePage(i), &sLoc); -+ if( rc!=SQLITE_OK ) break; -+ assert( i - sLoc.iZero - 1 >=0 ); -+ pgno = sLoc.aPgno[i-sLoc.iZero-1]; -+ iDbOff = (i64)(pgno-1) * szPage; -+ -+ if( iDbOff+szPage<=szDb ){ -+ iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; -+ rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); -+ -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); -+ } -+ -+ if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ -+ break; -+ } -+ } -+ -+ pInfo->nBackfillAttempted = i-1; -+ } -+ } -+ -+ return rc; -+} -+ - /* - ** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted - ** variable so that older snapshots can be accessed. To do this, loop -@@ -62242,49 +69203,21 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){ - assert( pWal->readLock>=0 ); - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); - if( rc==SQLITE_OK ){ -- volatile WalCkptInfo *pInfo = walCkptInfo(pWal); -- int szPage = (int)pWal->szPage; -- i64 szDb; /* Size of db file in bytes */ -- -- rc = sqlite3OsFileSize(pWal->pDbFd, &szDb); -- if( rc==SQLITE_OK ){ -- void *pBuf1 = sqlite3_malloc(szPage); -- void *pBuf2 = sqlite3_malloc(szPage); -- if( pBuf1==0 || pBuf2==0 ){ -- rc = SQLITE_NOMEM; -- }else{ -- u32 i = pInfo->nBackfillAttempted; -- for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ -- WalHashLoc sLoc; /* Hash table location */ -- u32 pgno; /* Page number in db file */ -- i64 iDbOff; /* Offset of db file entry */ -- i64 iWalOff; /* Offset of wal file entry */ -- -- rc = walHashGet(pWal, walFramePage(i), &sLoc); -- if( rc!=SQLITE_OK ) break; -- pgno = sLoc.aPgno[i-sLoc.iZero]; -- iDbOff = (i64)(pgno-1) * szPage; -- -- if( iDbOff+szPage<=szDb ){ -- iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE; -- rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff); -- -- if( rc==SQLITE_OK ){ -- rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff); -- } -- -- if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){ -- break; -- } -- } -- -- pInfo->nBackfillAttempted = i-1; -- } -+ void *pBuf1 = sqlite3_malloc(pWal->szPage); -+ void *pBuf2 = sqlite3_malloc(pWal->szPage); -+ if( pBuf1==0 || pBuf2==0 ){ -+ rc = SQLITE_NOMEM; -+ }else{ -+ pWal->ckptLock = 1; -+ SEH_TRY { -+ rc = walSnapshotRecover(pWal, pBuf1, pBuf2); - } -- -- sqlite3_free(pBuf1); -- sqlite3_free(pBuf2); -+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) -+ pWal->ckptLock = 0; - } -+ -+ sqlite3_free(pBuf1); -+ sqlite3_free(pBuf2); - walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); - } - -@@ -62293,28 +69226,20 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){ - #endif /* SQLITE_ENABLE_SNAPSHOT */ - - /* --** Begin a read transaction on the database. --** --** This routine used to be called sqlite3OpenSnapshot() and with good reason: --** it takes a snapshot of the state of the WAL and wal-index for the current --** instant in time. The current thread will continue to use this snapshot. --** Other threads might append new content to the WAL and wal-index but --** that extra content is ignored by the current thread. --** --** If the database contents have changes since the previous read --** transaction, then *pChanged is set to 1 before returning. The --** Pager layer will use this to know that its cache is stale and --** needs to be flushed. -+** This function does the work of sqlite3WalBeginReadTransaction() (see -+** below). That function simply calls this one inside an SEH_TRY{...} block. - */ --SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ -+static int walBeginReadTransaction(Wal *pWal, int *pChanged){ - int rc; /* Return code */ - int cnt = 0; /* Number of TryBeginRead attempts */ - #ifdef SQLITE_ENABLE_SNAPSHOT -+ int ckptLock = 0; - int bChanged = 0; - WalIndexHdr *pSnapshot = pWal->pSnapshot; - #endif - - assert( pWal->ckptLock==0 ); -+ assert( pWal->nSehTry>0 ); - - #ifdef SQLITE_ENABLE_SNAPSHOT - if( pSnapshot ){ -@@ -62337,12 +69262,12 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ - if( rc!=SQLITE_OK ){ - return rc; - } -- pWal->ckptLock = 1; -+ ckptLock = 1; - } - #endif - - do{ -- rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); -+ rc = walTryBeginRead(pWal, pChanged, 0, &cnt); - }while( rc==WAL_RETRY ); - testcase( (rc&0xff)==SQLITE_BUSY ); - testcase( (rc&0xff)==SQLITE_IOERR ); -@@ -62401,22 +69326,47 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ - } - - /* Release the shared CKPT lock obtained above. */ -- if( pWal->ckptLock ){ -+ if( ckptLock ){ - assert( pSnapshot ); - walUnlockShared(pWal, WAL_CKPT_LOCK); -- pWal->ckptLock = 0; - } - #endif - return rc; - } - -+/* -+** Begin a read transaction on the database. -+** -+** This routine used to be called sqlite3OpenSnapshot() and with good reason: -+** it takes a snapshot of the state of the WAL and wal-index for the current -+** instant in time. The current thread will continue to use this snapshot. -+** Other threads might append new content to the WAL and wal-index but -+** that extra content is ignored by the current thread. -+** -+** If the database contents have changes since the previous read -+** transaction, then *pChanged is set to 1 before returning. The -+** Pager layer will use this to know that its cache is stale and -+** needs to be flushed. -+*/ -+SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ -+ int rc; -+ SEH_TRY { -+ rc = walBeginReadTransaction(pWal, pChanged); -+ } -+ SEH_EXCEPT( rc = walHandleException(pWal); ) -+ return rc; -+} -+ - /* - ** Finish with a read transaction. All this does is release the - ** read-lock. - */ - SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ -- sqlite3WalEndWriteTransaction(pWal); -+#ifndef SQLITE_ENABLE_SETLK_TIMEOUT -+ assert( pWal->writeLock==0 || pWal->readLock<0 ); -+#endif - if( pWal->readLock>=0 ){ -+ sqlite3WalEndWriteTransaction(pWal); - walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); - pWal->readLock = -1; - } -@@ -62430,7 +69380,7 @@ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ - ** Return SQLITE_OK if successful, or an error code if an error occurs. If an - ** error does occur, the final value of *piRead is undefined. - */ --SQLITE_PRIVATE int sqlite3WalFindFrame( -+static int walFindFrame( - Wal *pWal, /* WAL handle */ - Pgno pgno, /* Database page number to read data for */ - u32 *piRead /* OUT: Frame number (or zero) */ -@@ -62493,13 +69443,15 @@ SQLITE_PRIVATE int sqlite3WalFindFrame( - } - nCollide = HASHTABLE_NSLOT; - iKey = walHash(pgno); -+ SEH_INJECT_FAULT; - while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ - u32 iFrame = iH + sLoc.iZero; -- if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ -+ if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ - assert( iFrame>iRead || CORRUPT_DB ); - iRead = iFrame; - } - if( (nCollide--)==0 ){ -+ *piRead = 0; - return SQLITE_CORRUPT_BKPT; - } - iKey = walNextHash(iKey); -@@ -62529,6 +69481,30 @@ SQLITE_PRIVATE int sqlite3WalFindFrame( - return SQLITE_OK; - } - -+/* -+** Search the wal file for page pgno. If found, set *piRead to the frame that -+** contains the page. Otherwise, if pgno is not in the wal file, set *piRead -+** to zero. -+** -+** Return SQLITE_OK if successful, or an error code if an error occurs. If an -+** error does occur, the final value of *piRead is undefined. -+** -+** The difference between this function and walFindFrame() is that this -+** function wraps walFindFrame() in an SEH_TRY{...} block. -+*/ -+SQLITE_PRIVATE int sqlite3WalFindFrame( -+ Wal *pWal, /* WAL handle */ -+ Pgno pgno, /* Database page number to read data for */ -+ u32 *piRead /* OUT: Frame number (or zero) */ -+){ -+ int rc; -+ SEH_TRY { -+ rc = walFindFrame(pWal, pgno, piRead); -+ } -+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) -+ return rc; -+} -+ - /* - ** Read the contents of frame iRead from the wal file into buffer pOut - ** (which is nOut bytes in size). Return SQLITE_OK if successful, or an -@@ -62583,7 +69559,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ - ** read-transaction was even opened, making this call a no-op. - ** Return early. */ - if( pWal->writeLock ){ -- assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); -+ assert( !memcmp(&pWal->hdr,(void*)pWal->apWiData[0],sizeof(WalIndexHdr)) ); - return SQLITE_OK; - } - #endif -@@ -62610,12 +69586,17 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ - ** time the read transaction on this connection was started, then - ** the write is disallowed. - */ -- if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ -+ SEH_TRY { -+ if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ -+ rc = SQLITE_BUSY_SNAPSHOT; -+ } -+ } -+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) -+ -+ if( rc!=SQLITE_OK ){ - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); - pWal->writeLock = 0; -- rc = SQLITE_BUSY_SNAPSHOT; - } -- - return rc; - } - -@@ -62651,30 +69632,34 @@ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *p - Pgno iMax = pWal->hdr.mxFrame; - Pgno iFrame; - -- /* Restore the clients cache of the wal-index header to the state it -- ** was in before the client began writing to the database. -- */ -- memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); -- -- for(iFrame=pWal->hdr.mxFrame+1; -- ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; -- iFrame++ -- ){ -- /* This call cannot fail. Unless the page for which the page number -- ** is passed as the second argument is (a) in the cache and -- ** (b) has an outstanding reference, then xUndo is either a no-op -- ** (if (a) is false) or simply expels the page from the cache (if (b) -- ** is false). -- ** -- ** If the upper layer is doing a rollback, it is guaranteed that there -- ** are no outstanding references to any page other than page 1. And -- ** page 1 is never written to the log until the transaction is -- ** committed. As a result, the call to xUndo may not fail. -+ SEH_TRY { -+ /* Restore the clients cache of the wal-index header to the state it -+ ** was in before the client began writing to the database. - */ -- assert( walFramePgno(pWal, iFrame)!=1 ); -- rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); -+ memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); -+ -+ for(iFrame=pWal->hdr.mxFrame+1; -+ ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; -+ iFrame++ -+ ){ -+ /* This call cannot fail. Unless the page for which the page number -+ ** is passed as the second argument is (a) in the cache and -+ ** (b) has an outstanding reference, then xUndo is either a no-op -+ ** (if (a) is false) or simply expels the page from the cache (if (b) -+ ** is false). -+ ** -+ ** If the upper layer is doing a rollback, it is guaranteed that there -+ ** are no outstanding references to any page other than page 1. And -+ ** page 1 is never written to the log until the transaction is -+ ** committed. As a result, the call to xUndo may not fail. -+ */ -+ assert( walFramePgno(pWal, iFrame)!=1 ); -+ rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); -+ } -+ if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); - } -- if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal); -+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) -+ pWal->iReCksum = 0; - } - return rc; - } -@@ -62718,7 +69703,13 @@ SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ - pWal->hdr.mxFrame = aWalData[0]; - pWal->hdr.aFrameCksum[0] = aWalData[1]; - pWal->hdr.aFrameCksum[1] = aWalData[2]; -- walCleanupHash(pWal); -+ SEH_TRY { -+ walCleanupHash(pWal); -+ } -+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) -+ if( pWal->iReCksum>pWal->hdr.mxFrame ){ -+ pWal->iReCksum = 0; -+ } - } - - return rc; -@@ -62768,7 +69759,7 @@ static int walRestartLog(Wal *pWal){ - cnt = 0; - do{ - int notUsed; -- rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); -+ rc = walTryBeginRead(pWal, ¬Used, 1, &cnt); - }while( rc==WAL_RETRY ); - assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ - testcase( (rc&0xff)==SQLITE_IOERR ); -@@ -62899,7 +69890,7 @@ static int walRewriteChecksums(Wal *pWal, u32 iLast){ - ** Write a set of frames to the log. The caller must hold the write-lock - ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). - */ --SQLITE_PRIVATE int sqlite3WalFrames( -+static int walFrames( - Wal *pWal, /* Wal handle to write to */ - int szPage, /* Database page-size in bytes */ - PgHdr *pList, /* List of dirty pages to write */ -@@ -62987,7 +69978,9 @@ SQLITE_PRIVATE int sqlite3WalFrames( - if( rc ) return rc; - } - } -- assert( (int)pWal->szPage==szPage ); -+ if( (int)pWal->szPage!=szPage ){ -+ return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */ -+ } - - /* Setup information needed to write frames into the WAL */ - w.pWal = pWal; -@@ -63008,7 +70001,7 @@ SQLITE_PRIVATE int sqlite3WalFrames( - ** checksums must be recomputed when the transaction is committed. */ - if( iFirst && (p->pDirty || isCommit==0) ){ - u32 iWrite = 0; -- VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite); -+ VVA_ONLY(rc =) walFindFrame(pWal, p->pgno, &iWrite); - assert( rc==SQLITE_OK || iWrite==0 ); - if( iWrite>=iFirst ){ - i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; -@@ -63127,6 +70120,29 @@ SQLITE_PRIVATE int sqlite3WalFrames( - return rc; - } - -+/* -+** Write a set of frames to the log. The caller must hold the write-lock -+** on the log file (obtained using sqlite3WalBeginWriteTransaction()). -+** -+** The difference between this function and walFrames() is that this -+** function wraps walFrames() in an SEH_TRY{...} block. -+*/ -+SQLITE_PRIVATE int sqlite3WalFrames( -+ Wal *pWal, /* Wal handle to write to */ -+ int szPage, /* Database page-size in bytes */ -+ PgHdr *pList, /* List of dirty pages to write */ -+ Pgno nTruncate, /* Database size after this commit */ -+ int isCommit, /* True if this is a commit */ -+ int sync_flags /* Flags to pass to OsSync() (or 0) */ -+){ -+ int rc; -+ SEH_TRY { -+ rc = walFrames(pWal, szPage, pList, nTruncate, isCommit, sync_flags); -+ } -+ SEH_EXCEPT( rc = walHandleException(pWal); ) -+ return rc; -+} -+ - /* - ** This routine is called to implement sqlite3_wal_checkpoint() and - ** related interfaces. -@@ -63164,10 +70180,9 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( - if( pWal->readOnly ) return SQLITE_READONLY; - WALTRACE(("WAL%p: checkpoint begins\n", pWal)); - -- /* Enable blocking locks, if possible. If blocking locks are successfully -- ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ -+ /* Enable blocking locks, if possible. */ - sqlite3WalDb(pWal, db); -- (void)walEnableBlocking(pWal); -+ if( xBusy2 ) (void)walEnableBlocking(pWal); - - /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive - ** "checkpoint" lock on the database file. -@@ -63206,30 +70221,38 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( - - - /* Read the wal-index header. */ -- if( rc==SQLITE_OK ){ -- walDisableBlocking(pWal); -- rc = walIndexReadHdr(pWal, &isChanged); -- (void)walEnableBlocking(pWal); -- if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ -- sqlite3OsUnfetch(pWal->pDbFd, 0, 0); -+ SEH_TRY { -+ if( rc==SQLITE_OK ){ -+ /* For a passive checkpoint, do not re-enable blocking locks after -+ ** reading the wal-index header. A passive checkpoint should not block -+ ** or invoke the busy handler. The only lock such a checkpoint may -+ ** attempt to obtain is a lock on a read-slot, and it should give up -+ ** immediately and do a partial checkpoint if it cannot obtain it. */ -+ walDisableBlocking(pWal); -+ rc = walIndexReadHdr(pWal, &isChanged); -+ if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal); -+ if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ -+ sqlite3OsUnfetch(pWal->pDbFd, 0, 0); -+ } - } -- } -- -- /* Copy data from the log to the database file. */ -- if( rc==SQLITE_OK ){ - -- if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ -- rc = SQLITE_CORRUPT_BKPT; -- }else{ -- rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); -- } -+ /* Copy data from the log to the database file. */ -+ if( rc==SQLITE_OK ){ -+ if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ -+ rc = SQLITE_CORRUPT_BKPT; -+ }else{ -+ rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf); -+ } - -- /* If no error occurred, set the output variables. */ -- if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ -- if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; -- if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); -+ /* If no error occurred, set the output variables. */ -+ if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ -+ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; -+ SEH_INJECT_FAULT; -+ if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); -+ } - } - } -+ SEH_EXCEPT( rc = walHandleException(pWal); ) - - if( isChanged ){ - /* If a new wal-index header was loaded before the checkpoint was -@@ -63306,7 +70329,9 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){ - ** locks are taken in this case). Nor should the pager attempt to - ** upgrade to exclusive-mode following such an error. - */ -+#ifndef SQLITE_USE_SEH - assert( pWal->readLock>=0 || pWal->lockError ); -+#endif - assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) ); - - if( op==0 ){ -@@ -63374,7 +70399,20 @@ SQLITE_PRIVATE void sqlite3WalSnapshotOpen( - Wal *pWal, - sqlite3_snapshot *pSnapshot - ){ -- pWal->pSnapshot = (WalIndexHdr*)pSnapshot; -+ if( pSnapshot && ((WalIndexHdr*)pSnapshot)->iVersion==0 ){ -+ /* iVersion==0 means that this is a call to sqlite3_snapshot_get(). In -+ ** this case set the bGetSnapshot flag so that if the call to -+ ** sqlite3_snapshot_get() is about to read transaction on this wal -+ ** file, it does not take read-lock 0 if the wal file has been completely -+ ** checkpointed. Taking read-lock 0 would work, but then it would be -+ ** possible for a subsequent writer to destroy the snapshot even while -+ ** this connection is holding its read-transaction open. This is contrary -+ ** to user expectations, so we avoid it by not taking read-lock 0. */ -+ pWal->bGetSnapshot = 1; -+ }else{ -+ pWal->pSnapshot = (WalIndexHdr*)pSnapshot; -+ pWal->bGetSnapshot = 0; -+ } - } - - /* -@@ -63407,16 +70445,19 @@ SQLITE_API int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){ - */ - SQLITE_PRIVATE int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){ - int rc; -- rc = walLockShared(pWal, WAL_CKPT_LOCK); -- if( rc==SQLITE_OK ){ -- WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; -- if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) -- || pNew->mxFramenBackfillAttempted -- ){ -- rc = SQLITE_ERROR_SNAPSHOT; -- walUnlockShared(pWal, WAL_CKPT_LOCK); -+ SEH_TRY { -+ rc = walLockShared(pWal, WAL_CKPT_LOCK); -+ if( rc==SQLITE_OK ){ -+ WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; -+ if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) -+ || pNew->mxFramenBackfillAttempted -+ ){ -+ rc = SQLITE_ERROR_SNAPSHOT; -+ walUnlockShared(pWal, WAL_CKPT_LOCK); -+ } - } - } -+ SEH_EXCEPT( rc = walHandleException(pWal); ) - return rc; - } - -@@ -63539,7 +70580,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ - ** 22 1 Min embedded payload fraction (must be 32) - ** 23 1 Min leaf payload fraction (must be 32) - ** 24 4 File change counter --** 28 4 Reserved for future use -+** 28 4 The size of the database in pages - ** 32 4 First freelist page - ** 36 4 Number of freelist pages in the file - ** 40 60 15 4-byte meta values passed to higher layers -@@ -63647,7 +70688,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ - ** byte are used. The integer consists of all bytes that have bit 8 set and - ** the first byte with bit 8 clear. The most significant byte of the integer - ** appears first. A variable-length integer may not be more than 9 bytes long. --** As a special case, all 8 bytes of the 9th byte are used as data. This -+** As a special case, all 8 bits of the 9th byte are used as data. This - ** allows a 64-bit integer to be encoded in 9 bytes. - ** - ** 0x00 becomes 0x00000000 -@@ -63655,7 +70696,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ - ** 0x81 0x00 becomes 0x00000080 - ** 0x82 0x00 becomes 0x00000100 - ** 0x80 0x7f becomes 0x0000007f --** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678 -+** 0x81 0x91 0xd1 0xac 0x78 becomes 0x12345678 - ** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081 - ** - ** Variable length integers are used for rowids and to hold the number of -@@ -63738,7 +70779,7 @@ typedef struct CellInfo CellInfo; - ** page that has been loaded into memory. The information in this object - ** is derived from the raw on-disk page content. - ** --** As each database page is loaded into memory, the pager allocats an -+** As each database page is loaded into memory, the pager allocates an - ** instance of this object and zeros the first 8 bytes. (This is the - ** "extra" information associated with each page of the pager.) - ** -@@ -63747,7 +70788,6 @@ typedef struct CellInfo CellInfo; - */ - struct MemPage { - u8 isInit; /* True if previously initialized. MUST BE FIRST! */ -- u8 bBusy; /* Prevent endless loops on corrupt database files */ - u8 intKey; /* True if table b-trees. False for index b-trees */ - u8 intKeyLeaf; /* True if the leaf of an intKey table */ - Pgno pgno; /* Page number for this page */ -@@ -63769,7 +70809,9 @@ struct MemPage { - u8 *apOvfl[4]; /* Pointers to the body of overflow cells */ - BtShared *pBt; /* Pointer to BtShared that this page is part of */ - u8 *aData; /* Pointer to disk image of the page data */ -- u8 *aDataEnd; /* One byte past the end of usable data */ -+ u8 *aDataEnd; /* One byte past the end of the entire page - not just -+ ** the usable space, the entire page. Used to prevent -+ ** corruption-induced buffer overflow. */ - u8 *aCellIdx; /* The cell index area */ - u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */ - DbPage *pDbPage; /* Pager page handle */ -@@ -63825,9 +70867,12 @@ struct Btree { - u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */ - int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ - int nBackup; /* Number of backup operations reading this btree */ -- u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ -+ u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */ - Btree *pNext; /* List of other sharable Btrees from the same db */ - Btree *pPrev; /* Back pointer of the same list */ -+#ifdef SQLITE_DEBUG -+ u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */ -+#endif - #ifndef SQLITE_OMIT_SHARED_CACHE - BtLock lock; /* Object used to lock page 1 */ - #endif -@@ -63839,11 +70884,25 @@ struct Btree { - ** If the shared-data extension is enabled, there may be multiple users - ** of the Btree structure. At most one of these may open a write transaction, - ** but any number may have active read transactions. -+** -+** These values must match SQLITE_TXN_NONE, SQLITE_TXN_READ, and -+** SQLITE_TXN_WRITE - */ - #define TRANS_NONE 0 - #define TRANS_READ 1 - #define TRANS_WRITE 2 - -+#if TRANS_NONE!=SQLITE_TXN_NONE -+# error wrong numeric code for no-transaction -+#endif -+#if TRANS_READ!=SQLITE_TXN_READ -+# error wrong numeric code for read-transaction -+#endif -+#if TRANS_WRITE!=SQLITE_TXN_WRITE -+# error wrong numeric code for write-transaction -+#endif -+ -+ - /* - ** An instance of this object represents a single database file. - ** -@@ -63913,6 +70972,7 @@ struct BtShared { - Btree *pWriter; /* Btree with currently open write transaction */ - #endif - u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ -+ int nPreformatSize; /* Size of last cell written by TransferRow() */ - }; - - /* -@@ -63952,6 +71012,12 @@ struct CellInfo { - */ - #define BTCURSOR_MAX_DEPTH 20 - -+/* -+** Maximum amount of storage local to a database page, regardless of -+** page size. -+*/ -+#define BT_MAX_LOCAL 65501 /* 65536 - 35 */ -+ - /* - ** A cursor is a pointer to a particular entry within a particular - ** b-tree within a database file. -@@ -64012,7 +71078,7 @@ struct BtCursor { - #define BTCF_WriteFlag 0x01 /* True if a write cursor */ - #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ - #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ --#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ -+#define BTCF_AtLast 0x08 /* Cursor is pointing to the last entry */ - #define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ - #define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ - #define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ -@@ -64056,7 +71122,7 @@ struct BtCursor { - /* - ** The database page the PENDING_BYTE occupies. This page is never used. - */ --# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) -+#define PENDING_BYTE_PAGE(pBt) ((Pgno)((PENDING_BYTE/((pBt)->pageSize))+1)) - - /* - ** These macros define the location of the pointer-map entry for a -@@ -64130,15 +71196,15 @@ struct BtCursor { - ** So, this macro is defined instead. - */ - #ifndef SQLITE_OMIT_AUTOVACUUM --#define ISAUTOVACUUM (pBt->autoVacuum) -+#define ISAUTOVACUUM(pBt) (pBt->autoVacuum) - #else --#define ISAUTOVACUUM 0 -+#define ISAUTOVACUUM(pBt) 0 - #endif - - - /* --** This structure is passed around through all the sanity checking routines --** in order to keep track of some global state information. -+** This structure is passed around through all the PRAGMA integrity_check -+** checking routines in order to keep track of some global state information. - ** - ** The aRef[] array is allocated so that there is 1 bit for each page in - ** the database. As the integrity-check proceeds, for each page used in -@@ -64151,16 +71217,19 @@ struct IntegrityCk { - BtShared *pBt; /* The tree being checked out */ - Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ - u8 *aPgRef; /* 1 bit per page in the db (see above) */ -- Pgno nPage; /* Number of pages in the database */ -+ Pgno nCkPage; /* Pages in the database. 0 for partial check */ - int mxErr; /* Stop accumulating errors when this reaches zero */ - int nErr; /* Number of messages written to zErrMsg so far */ -- int bOomFault; /* A memory allocation error has occurred */ -+ int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */ -+ u32 nStep; /* Number of steps into the integrity_check process */ - const char *zPfx; /* Error message prefix */ -- Pgno v1; /* Value for first %u substitution in zPfx */ -- int v2; /* Value for second %d substitution in zPfx */ -+ Pgno v0; /* Value for first %u substitution in zPfx (root page) */ -+ Pgno v1; /* Value for second %u substitution in zPfx (current pg) */ -+ int v2; /* Value for third %d substitution in zPfx */ - StrAccum errMsg; /* Accumulate the error message text here */ - u32 *heap; /* Min-heap used for analyzing cell coverage */ - sqlite3 *db; /* Database connection running the check */ -+ i64 nRow; /* Number of rows visited in current tree */ - }; - - /* -@@ -64173,7 +71242,7 @@ struct IntegrityCk { - - /* - ** get2byteAligned(), unlike get2byte(), requires that its argument point to a --** two-byte aligned address. get2bytea() is only used for accessing the -+** two-byte aligned address. get2byteAligned() is only used for accessing the - ** cell addresses in a btree header. - */ - #if SQLITE_BYTEORDER==4321 -@@ -64350,14 +71419,14 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){ - ** - ** There is a corresponding leave-all procedures. - ** --** Enter the mutexes in accending order by BtShared pointer address -+** Enter the mutexes in ascending order by BtShared pointer address - ** to avoid the possibility of deadlock when two threads with - ** two or more btrees in common both try to lock all their btrees - ** at the same instant. - */ - static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){ - int i; -- int skipOk = 1; -+ u8 skipOk = 1; - Btree *p; - assert( sqlite3_mutex_held(db->mutex) ); - for(i=0; inDb; i++){ -@@ -64424,6 +71493,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ - SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){ - Btree *p; - assert( db!=0 ); -+ if( db->pVfs==0 && db->nDb==0 ) return 1; - if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema); - assert( iDb>=0 && iDbnDb ); - if( !sqlite3_mutex_held(db->mutex) ) return 0; -@@ -64595,6 +71665,17 @@ SQLITE_API int sqlite3_enable_shared_cache(int enable){ - #define hasReadConflicts(a, b) 0 - #endif - -+#ifdef SQLITE_DEBUG -+/* -+** Return and reset the seek counter for a Btree object. -+*/ -+SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ -+ u64 n = pBt->nSeek; -+ pBt->nSeek = 0; -+ return n; -+} -+#endif -+ - /* - ** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single - ** (MemPage*) as an argument. The (MemPage*) must not be NULL. -@@ -64608,8 +71689,8 @@ SQLITE_API int sqlite3_enable_shared_cache(int enable){ - int corruptPageError(int lineno, MemPage *p){ - char *zMsg; - sqlite3BeginBenignMalloc(); -- zMsg = sqlite3_mprintf("database corruption page %d of %s", -- (int)p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) -+ zMsg = sqlite3_mprintf("database corruption page %u of %s", -+ p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) - ); - sqlite3EndBenignMalloc(); - if( zMsg ){ -@@ -64623,8 +71704,47 @@ int corruptPageError(int lineno, MemPage *p){ - # define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) - #endif - -+/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled -+** or if the lock tracking is disabled. This is always the value for -+** release builds. -+*/ -+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/ -+ - #ifndef SQLITE_OMIT_SHARED_CACHE - -+#if 0 -+/* ^---- Change to 1 and recompile to enable shared-lock tracing -+** for debugging purposes. -+** -+** Print all shared-cache locks on a BtShared. Debugging use only. -+*/ -+static void sharedLockTrace( -+ BtShared *pBt, -+ const char *zMsg, -+ int iRoot, -+ int eLockType -+){ -+ BtLock *pLock; -+ if( iRoot>0 ){ -+ printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W"); -+ }else{ -+ printf("%s-%p:", zMsg, pBt); -+ } -+ for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ -+ printf(" %p/%u%s", pLock->pBtree, pLock->iTable, -+ pLock->eLock==READ_LOCK ? "R" : "W"); -+ while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){ -+ pLock = pLock->pNext; -+ printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W"); -+ } -+ } -+ printf("\n"); -+ fflush(stdout); -+} -+#undef SHARED_LOCK_TRACE -+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE) -+#endif /* Shared-lock tracing */ -+ - #ifdef SQLITE_DEBUG - /* - **** This function is only used as part of an assert() statement. *** -@@ -64686,7 +71806,7 @@ static int hasSharedCacheTableLock( - int bSeen = 0; - for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ - Index *pIdx = (Index *)sqliteHashData(p); -- if( pIdx->tnum==(int)iRoot ){ -+ if( pIdx->tnum==iRoot ){ - if( bSeen ){ - /* Two or more indexes share the same root page. There must - ** be imposter tables. So just return true. The assert is not -@@ -64701,6 +71821,8 @@ static int hasSharedCacheTableLock( - iTab = iRoot; - } - -+ SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType); -+ - /* Search for the required lock. Either a write-lock on root-page iTab, a - ** write-lock on the schema table, or (if the client is reading) a - ** read-lock on iTab will suffice. Return 1 if any of these are found. */ -@@ -64834,6 +71956,8 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ - BtLock *pLock = 0; - BtLock *pIter; - -+ SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock); -+ - assert( sqlite3BtreeHoldsMutex(p) ); - assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); - assert( p->db!=0 ); -@@ -64901,6 +72025,8 @@ static void clearAllSharedCacheTableLocks(Btree *p){ - assert( p->sharable || 0==*ppIter ); - assert( p->inTrans>0 ); - -+ SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0); -+ - while( *ppIter ){ - BtLock *pLock = *ppIter; - assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); -@@ -64939,6 +72065,9 @@ static void clearAllSharedCacheTableLocks(Btree *p){ - */ - static void downgradeAllSharedCacheTableLocks(Btree *p){ - BtShared *pBt = p->pBt; -+ -+ SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0); -+ - if( pBt->pWriter==p ){ - BtLock *pLock; - pBt->pWriter = 0; -@@ -65019,7 +72148,7 @@ static void invalidateIncrblobCursors( - int isClearTable /* True if all rows are being deleted */ - ){ - BtCursor *p; -- if( pBtree->hasIncrblobCur==0 ) return; -+ assert( pBtree->hasIncrblobCur ); - assert( sqlite3BtreeHoldsMutex(pBtree) ); - pBtree->hasIncrblobCur = 0; - for(p=pBtree->pBt->pCursor; p; p=p->pNext){ -@@ -65153,7 +72282,7 @@ static int saveCursorKey(BtCursor *pCur){ - ** below. */ - void *pKey; - pCur->nKey = sqlite3BtreePayloadSize(pCur); -- pKey = sqlite3Malloc( pCur->nKey + 9 + 8 ); -+ pKey = sqlite3Malloc( ((i64)pCur->nKey) + 9 + 8 ); - if( pKey ){ - rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); - if( rc==SQLITE_OK ){ -@@ -65279,7 +72408,7 @@ SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *pCur){ - /* - ** In this version of BtreeMoveto, pKey is a packed index record - ** such as is generated by the OP_MakeRecord opcode. Unpack the --** record and then call BtreeMovetoUnpacked() to do the work. -+** record and then call sqlite3BtreeIndexMoveto() to do the work. - */ - static int btreeMoveto( - BtCursor *pCur, /* Cursor open on the btree to be searched */ -@@ -65299,15 +72428,13 @@ static int btreeMoveto( - sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); - if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ - rc = SQLITE_CORRUPT_BKPT; -- goto moveto_done; -+ }else{ -+ rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes); - } -+ sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); - }else{ - pIdxKey = 0; -- } -- rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); --moveto_done: -- if( pIdxKey ){ -- sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); -+ rc = sqlite3BtreeTableMoveto(pCur, nKey, bias, pRes); - } - return rc; - } -@@ -65420,15 +72547,32 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow) - */ - SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){ - /* Used only by system that substitute their own storage engine */ -+#ifdef SQLITE_DEBUG -+ if( ALWAYS(eHintType==BTREE_HINT_RANGE) ){ -+ va_list ap; -+ Expr *pExpr; -+ Walker w; -+ memset(&w, 0, sizeof(w)); -+ w.xExprCallback = sqlite3CursorRangeHintExprCheck; -+ va_start(ap, eHintType); -+ pExpr = va_arg(ap, Expr*); -+ w.u.aMem = va_arg(ap, Mem*); -+ va_end(ap); -+ assert( pExpr!=0 ); -+ assert( w.u.aMem!=0 ); -+ sqlite3WalkExpr(&w, pExpr); -+ } -+#endif /* SQLITE_DEBUG */ - } --#endif -+#endif /* SQLITE_ENABLE_CURSOR_HINTS */ -+ - - /* - ** Provide flag hints to the cursor. - */ - SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){ - assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 ); -- pCur->hints = x; -+ pCur->hints = (u8)x; - } - - -@@ -65506,7 +72650,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ - pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); - - if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ -- TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); -+ TRACE(("PTRMAP_UPDATE: %u->(%u,%u)\n", key, eType, parent)); - *pRC= rc = sqlite3PagerWrite(pDbPage); - if( rc==SQLITE_OK ){ - pPtrmap[offset] = eType; -@@ -65615,6 +72759,25 @@ static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow( - pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4; - } - -+/* -+** Given a record with nPayload bytes of payload stored within btree -+** page pPage, return the number of bytes of payload stored locally. -+*/ -+static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){ -+ int maxLocal; /* Maximum amount of payload held locally */ -+ maxLocal = pPage->maxLocal; -+ assert( nPayload>=0 ); -+ if( nPayload<=maxLocal ){ -+ return (int)nPayload; -+ }else{ -+ int minLocal; /* Minimum amount of payload held locally */ -+ int surplus; /* Overflow payload available for local storage */ -+ minLocal = pPage->minLocal; -+ surplus = (int)(minLocal +(nPayload - minLocal)%(pPage->pBt->usableSize-4)); -+ return (surplus <= maxLocal) ? surplus : minLocal; -+ } -+} -+ - /* - ** The following routines are implementations of the MemPage.xParseCell() - ** method. -@@ -65681,19 +72844,37 @@ static void btreeParseCellPtr( - ** - ** pIter += getVarint(pIter, (u64*)&pInfo->nKey); - ** -- ** The code is inlined to avoid a function call. -+ ** The code is inlined and the loop is unrolled for performance. -+ ** This routine is a high-runner. - */ - iKey = *pIter; - if( iKey>=0x80 ){ -- u8 *pEnd = &pIter[7]; -- iKey &= 0x7f; -- while(1){ -- iKey = (iKey<<7) | (*++pIter & 0x7f); -- if( (*pIter)<0x80 ) break; -- if( pIter>=pEnd ){ -- iKey = (iKey<<8) | *++pIter; -- break; -+ u8 x; -+ iKey = (iKey<<7) ^ (x = *++pIter); -+ if( x>=0x80 ){ -+ iKey = (iKey<<7) ^ (x = *++pIter); -+ if( x>=0x80 ){ -+ iKey = (iKey<<7) ^ 0x10204000 ^ (x = *++pIter); -+ if( x>=0x80 ){ -+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); -+ if( x>=0x80 ){ -+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); -+ if( x>=0x80 ){ -+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); -+ if( x>=0x80 ){ -+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); -+ if( x>=0x80 ){ -+ iKey = (iKey<<8) ^ 0x8000 ^ (*++pIter); -+ } -+ } -+ } -+ } -+ } -+ }else{ -+ iKey ^= 0x204000; - } -+ }else{ -+ iKey ^= 0x4000; - } - } - pIter++; -@@ -65702,12 +72883,14 @@ static void btreeParseCellPtr( - pInfo->nPayload = nPayload; - pInfo->pPayload = pIter; - testcase( nPayload==pPage->maxLocal ); -- testcase( nPayload==pPage->maxLocal+1 ); -+ testcase( nPayload==(u32)pPage->maxLocal+1 ); -+ assert( nPayload>=0 ); -+ assert( pPage->maxLocal <= BT_MAX_LOCAL ); - if( nPayload<=pPage->maxLocal ){ - /* This is the (easy) common case where the entire payload fits - ** on the local page. No overflow is required. - */ -- pInfo->nSize = nPayload + (u16)(pIter - pCell); -+ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); - if( pInfo->nSize<4 ) pInfo->nSize = 4; - pInfo->nLocal = (u16)nPayload; - }else{ -@@ -65739,12 +72922,14 @@ static void btreeParseCellPtrIndex( - pInfo->nPayload = nPayload; - pInfo->pPayload = pIter; - testcase( nPayload==pPage->maxLocal ); -- testcase( nPayload==pPage->maxLocal+1 ); -+ testcase( nPayload==(u32)pPage->maxLocal+1 ); -+ assert( nPayload>=0 ); -+ assert( pPage->maxLocal <= BT_MAX_LOCAL ); - if( nPayload<=pPage->maxLocal ){ - /* This is the (easy) common case where the entire payload fits - ** on the local page. No overflow is required. - */ -- pInfo->nSize = nPayload + (u16)(pIter - pCell); -+ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); - if( pInfo->nSize<4 ) pInfo->nSize = 4; - pInfo->nLocal = (u16)nPayload; - }else{ -@@ -65769,10 +72954,12 @@ static void btreeParseCell( - ** the space used by the cell pointer. - ** - ** cellSizePtrNoPayload() => table internal nodes --** cellSizePtr() => all index nodes & table leaf nodes -+** cellSizePtrTableLeaf() => table leaf nodes -+** cellSizePtr() => index internal nodes -+** cellSizeIdxLeaf() => index leaf nodes - */ - static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ -- u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */ -+ u8 *pIter = pCell + 4; /* For looping over bytes of pCell */ - u8 *pEnd; /* End mark for a varint */ - u32 nSize; /* Size value to return */ - -@@ -65785,6 +72972,7 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ - pPage->xParseCell(pPage, pCell, &debuginfo); - #endif - -+ assert( pPage->childPtrSize==4 ); - nSize = *pIter; - if( nSize>=0x80 ){ - pEnd = &pIter[8]; -@@ -65794,15 +72982,50 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ - }while( *(pIter)>=0x80 && pIterintKey ){ -- /* pIter now points at the 64-bit integer key value, a variable length -- ** integer. The following block moves pIter to point at the first byte -- ** past the end of the key value. */ -- pEnd = &pIter[9]; -- while( (*pIter++)&0x80 && pItermaxLocal ); -+ testcase( nSize==(u32)pPage->maxLocal+1 ); -+ if( nSize<=pPage->maxLocal ){ -+ nSize += (u32)(pIter - pCell); -+ assert( nSize>4 ); -+ }else{ -+ int minLocal = pPage->minLocal; -+ nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); -+ testcase( nSize==pPage->maxLocal ); -+ testcase( nSize==(u32)pPage->maxLocal+1 ); -+ if( nSize>pPage->maxLocal ){ -+ nSize = minLocal; -+ } -+ nSize += 4 + (u16)(pIter - pCell); -+ } -+ assert( nSize==debuginfo.nSize || CORRUPT_DB ); -+ return (u16)nSize; -+} -+static u16 cellSizePtrIdxLeaf(MemPage *pPage, u8 *pCell){ -+ u8 *pIter = pCell; /* For looping over bytes of pCell */ -+ u8 *pEnd; /* End mark for a varint */ -+ u32 nSize; /* Size value to return */ -+ -+#ifdef SQLITE_DEBUG -+ /* The value returned by this function should always be the same as -+ ** the (CellInfo.nSize) value found by doing a full parse of the -+ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of -+ ** this function verifies that this invariant is not violated. */ -+ CellInfo debuginfo; -+ pPage->xParseCell(pPage, pCell, &debuginfo); -+#endif -+ -+ assert( pPage->childPtrSize==0 ); -+ nSize = *pIter; -+ if( nSize>=0x80 ){ -+ pEnd = &pIter[8]; -+ nSize &= 0x7f; -+ do{ -+ nSize = (nSize<<7) | (*++pIter & 0x7f); -+ }while( *(pIter)>=0x80 && pItermaxLocal ); -- testcase( nSize==pPage->maxLocal+1 ); -+ testcase( nSize==(u32)pPage->maxLocal+1 ); - if( nSize<=pPage->maxLocal ){ - nSize += (u32)(pIter - pCell); - if( nSize<4 ) nSize = 4; -@@ -65810,7 +73033,7 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ - int minLocal = pPage->minLocal; - nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); - testcase( nSize==pPage->maxLocal ); -- testcase( nSize==pPage->maxLocal+1 ); -+ testcase( nSize==(u32)pPage->maxLocal+1 ); - if( nSize>pPage->maxLocal ){ - nSize = minLocal; - } -@@ -65840,6 +73063,58 @@ static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){ - assert( debuginfo.nSize==(u16)(pIter - pCell) || CORRUPT_DB ); - return (u16)(pIter - pCell); - } -+static u16 cellSizePtrTableLeaf(MemPage *pPage, u8 *pCell){ -+ u8 *pIter = pCell; /* For looping over bytes of pCell */ -+ u8 *pEnd; /* End mark for a varint */ -+ u32 nSize; /* Size value to return */ -+ -+#ifdef SQLITE_DEBUG -+ /* The value returned by this function should always be the same as -+ ** the (CellInfo.nSize) value found by doing a full parse of the -+ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of -+ ** this function verifies that this invariant is not violated. */ -+ CellInfo debuginfo; -+ pPage->xParseCell(pPage, pCell, &debuginfo); -+#endif -+ -+ nSize = *pIter; -+ if( nSize>=0x80 ){ -+ pEnd = &pIter[8]; -+ nSize &= 0x7f; -+ do{ -+ nSize = (nSize<<7) | (*++pIter & 0x7f); -+ }while( *(pIter)>=0x80 && pItermaxLocal ); -+ testcase( nSize==(u32)pPage->maxLocal+1 ); -+ if( nSize<=pPage->maxLocal ){ -+ nSize += (u32)(pIter - pCell); -+ if( nSize<4 ) nSize = 4; -+ }else{ -+ int minLocal = pPage->minLocal; -+ nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); -+ testcase( nSize==pPage->maxLocal ); -+ testcase( nSize==(u32)pPage->maxLocal+1 ); -+ if( nSize>pPage->maxLocal ){ -+ nSize = minLocal; -+ } -+ nSize += 4 + (u16)(pIter - pCell); -+ } -+ assert( nSize==debuginfo.nSize || CORRUPT_DB ); -+ return (u16)nSize; -+} - - - #ifdef SQLITE_DEBUG -@@ -65853,7 +73128,7 @@ static u16 cellSize(MemPage *pPage, int iCell){ - #ifndef SQLITE_OMIT_AUTOVACUUM - /* - ** The cell pCell is currently part of page pSrc but will ultimately be part --** of pPage. (pSrc and pPager are often the same.) If pCell contains a -+** of pPage. (pSrc and pPage are often the same.) If pCell contains a - ** pointer to an overflow page, insert an entry into the pointer-map for - ** the overflow page that will be valid after pCell has been moved to pPage. - */ -@@ -65864,7 +73139,7 @@ static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ - pPage->xParseCell(pPage, pCell, &info); - if( info.nLocalaDataEnd, pCell, pCell+info.nLocal) ){ -+ if( SQLITE_OVERFLOW(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){ - testcase( pSrc!=pPage ); - *pRC = SQLITE_CORRUPT_BKPT; - return; -@@ -65902,14 +73177,14 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ - unsigned char *src; /* Source of content */ - int iCellFirst; /* First allowable cell index */ - int iCellLast; /* Last possible cell index */ -+ int iCellStart; /* First cell offset in input */ - - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( pPage->pBt!=0 ); - assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); - assert( pPage->nOverflow==0 ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); -- temp = 0; -- src = data = pPage->aData; -+ data = pPage->aData; - hdr = pPage->hdrOffset; - cellOffset = pPage->cellOffset; - nCell = pPage->nCell; -@@ -65943,7 +73218,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ - if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); - memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); - sz += sz2; -- }else if( NEVER(iFree+sz>usableSize) ){ -+ }else if( iFree+sz>usableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - -@@ -65962,41 +73237,39 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ - - cbrk = usableSize; - iCellLast = usableSize - 4; -- for(i=0; iiCellLast ){ -- return SQLITE_CORRUPT_PAGE(pPage); -- } -- assert( pc>=iCellFirst && pc<=iCellLast ); -- size = pPage->xCellSize(pPage, &src[pc]); -- cbrk -= size; -- if( cbrkusableSize ){ -- return SQLITE_CORRUPT_PAGE(pPage); -- } -- assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); -- testcase( cbrk+size==usableSize ); -- testcase( pc+size==usableSize ); -- put2byte(pAddr, cbrk); -- if( temp==0 ){ -- int x; -- if( cbrk==pc ) continue; -- temp = sqlite3PagerTempSpace(pPage->pBt->pPager); -- x = get2byte(&data[hdr+5]); -- memcpy(&temp[x], &data[x], (cbrk+size) - x); -- src = temp; -+ iCellStart = get2byte(&data[hdr+5]); -+ if( nCell>0 ){ -+ temp = sqlite3PagerTempSpace(pPage->pBt->pPager); -+ memcpy(temp, data, usableSize); -+ src = temp; -+ for(i=0; iiCellLast ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } -+ assert( pc>=0 && pc<=iCellLast ); -+ size = pPage->xCellSize(pPage, &src[pc]); -+ cbrk -= size; -+ if( cbrkusableSize ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } -+ assert( cbrk+size<=usableSize && cbrk>=iCellStart ); -+ testcase( cbrk+size==usableSize ); -+ testcase( pc+size==usableSize ); -+ put2byte(pAddr, cbrk); -+ memcpy(&data[cbrk], &src[pc], size); - } -- memcpy(&data[cbrk], &src[pc], size); - } - data[hdr+7] = 0; - -- defragment_out: -+defragment_out: - assert( pPage->nFree>=0 ); - if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ - return SQLITE_CORRUPT_PAGE(pPage); -@@ -66028,7 +73301,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - const int hdr = pPg->hdrOffset; /* Offset to page header */ - u8 * const aData = pPg->aData; /* Page data */ - int iAddr = hdr + 1; /* Address of ptr to pc */ -- int pc = get2byte(&aData[iAddr]); /* Address of a free slot */ -+ u8 *pTmp = &aData[iAddr]; /* Temporary ptr into aData[] */ -+ int pc = get2byte(pTmp); /* Address of a free slot */ - int x; /* Excess size of the slot */ - int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */ - int size; /* Size of the free slot */ -@@ -66038,7 +73312,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each - ** freeblock form a big-endian integer which is the size of the freeblock - ** in bytes, including the 4-byte header. */ -- size = get2byte(&aData[pc+2]); -+ pTmp = &aData[pc+2]; -+ size = get2byte(pTmp); - if( (x = size - nByte)>=0 ){ - testcase( x==4 ); - testcase( x==3 ); -@@ -66051,6 +73326,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - ** fragmented bytes within the page. */ - memcpy(&aData[iAddr], &aData[pc], 2); - aData[hdr+7] += (u8)x; -+ return &aData[pc]; - }else if( x+pc > maxPC ){ - /* This slot extends off the end of the usable part of the page */ - *pRc = SQLITE_CORRUPT_PAGE(pPg); -@@ -66063,10 +73339,11 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - return &aData[pc + x]; - } - iAddr = pc; -- pc = get2byte(&aData[pc]); -- if( pc<=iAddr+size ){ -+ pTmp = &aData[pc]; -+ pc = get2byte(pTmp); -+ if( pc<=iAddr ){ - if( pc ){ -- /* The next slot in the chain is not past the end of the current slot */ -+ /* The next slot in the chain comes before the current slot */ - *pRc = SQLITE_CORRUPT_PAGE(pPg); - } - return 0; -@@ -66092,11 +73369,12 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ - ** allocation is being made in order to insert a new cell, so we will - ** also end up needing a new cell pointer. - */ --static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ -+static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ - const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ - u8 * const data = pPage->aData; /* Local cache of pPage->aData */ - int top; /* First byte of cell content area */ - int rc = SQLITE_OK; /* Integer return code */ -+ u8 *pTmp; /* Temp ptr into data[] */ - int gap; /* First byte of gap between cell pointers and cell content */ - - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); -@@ -66115,14 +73393,16 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ - ** then the cell content offset of an empty page wants to be 65536. - ** However, that integer is too large to be stored in a 2-byte unsigned - ** integer, so a value of 0 is used in its place. */ -- top = get2byte(&data[hdr+5]); -- assert( top<=(int)pPage->pBt->usableSize ); /* by btreeComputeFreeSpace() */ -+ pTmp = &data[hdr+5]; -+ top = get2byte(pTmp); - if( gap>top ){ - if( top==0 && pPage->pBt->usableSize==65536 ){ - top = 65536; - }else{ - return SQLITE_CORRUPT_PAGE(pPage); - } -+ }else if( top>(int)pPage->pBt->usableSize ){ -+ return SQLITE_CORRUPT_PAGE(pPage); - } - - /* If there is enough space between gap and top for one more cell pointer, -@@ -66138,7 +73418,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ - int g2; - assert( pSpace+nByte<=data+pPage->pBt->usableSize ); - *pIdx = g2 = (int)(pSpace-data); -- if( NEVER(g2<=gap) ){ -+ if( g2<=gap ){ - return SQLITE_CORRUPT_PAGE(pPage); - }else{ - return SQLITE_OK; -@@ -66184,19 +73464,20 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ - ** - ** Even though the freeblock list was checked by btreeComputeFreeSpace(), - ** that routine will not detect overlap between cells or freeblocks. Nor --** does it detect cells or freeblocks that encrouch into the reserved bytes -+** does it detect cells or freeblocks that encroach into the reserved bytes - ** at the end of the page. So do additional corruption checks inside this - ** routine and return SQLITE_CORRUPT if any problems are found. - */ --static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ -- u16 iPtr; /* Address of ptr to next freeblock */ -- u16 iFreeBlk; /* Address of the next freeblock */ -+static int freeSpace(MemPage *pPage, int iStart, int iSize){ -+ int iPtr; /* Address of ptr to next freeblock */ -+ int iFreeBlk; /* Address of the next freeblock */ - u8 hdr; /* Page header size. 0 or 100 */ -- u8 nFrag = 0; /* Reduction in fragmentation */ -- u16 iOrigSize = iSize; /* Original value of iSize */ -- u16 x; /* Offset to cell content area */ -- u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ -+ int nFrag = 0; /* Reduction in fragmentation */ -+ int iOrigSize = iSize; /* Original value of iSize */ -+ int x; /* Offset to cell content area */ -+ int iEnd = iStart + iSize; /* First byte past the iStart buffer */ - unsigned char *data = pPage->aData; /* Page content */ -+ u8 *pTmp; /* Temporary ptr into data[] */ - - assert( pPage->pBt!=0 ); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); -@@ -66204,7 +73485,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ - assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( iSize>=4 ); /* Minimum cell size is 4 */ -- assert( iStart<=pPage->pBt->usableSize-4 ); -+ assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 ); - - /* The list of freeblocks must be in ascending order. Find the - ** spot on the list where iStart should be inserted. -@@ -66215,16 +73496,16 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ - iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */ - }else{ - while( (iFreeBlk = get2byte(&data[iPtr]))pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ -+ if( iFreeBlk>(int)pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ - return SQLITE_CORRUPT_PAGE(pPage); - } -- assert( iFreeBlk>iPtr || iFreeBlk==0 ); -+ assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB ); - - /* At this point: - ** iFreeBlk: First freeblock after iStart, or zero if none -@@ -66236,7 +73517,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ - nFrag = iFreeBlk - iEnd; - if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage); - iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); -- if( iEnd > pPage->pBt->usableSize ){ -+ if( iEnd > (int)pPage->pBt->usableSize ){ - return SQLITE_CORRUPT_PAGE(pPage); - } - iSize = iEnd - iStart; -@@ -66257,9 +73538,15 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ - } - } - if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); -- data[hdr+7] -= nFrag; -+ data[hdr+7] -= (u8)nFrag; -+ } -+ pTmp = &data[hdr+5]; -+ x = get2byte(pTmp); -+ if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ -+ /* Overwrite deleted information with zeros when the secure_delete -+ ** option is enabled */ -+ memset(&data[iStart], 0, iSize); - } -- x = get2byte(&data[hdr+5]); - if( iStart<=x ){ - /* The new freeblock is at the beginning of the cell content area, - ** so just extend the cell content area rather than create another -@@ -66271,14 +73558,10 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ - }else{ - /* Insert the new freeblock into the freelist */ - put2byte(&data[iPtr], iStart); -+ put2byte(&data[iStart], iFreeBlk); -+ assert( iSize>=0 && iSize<=0xffff ); -+ put2byte(&data[iStart+2], (u16)iSize); - } -- if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ -- /* Overwrite deleted information with zeros when the secure_delete -- ** option is enabled */ -- memset(&data[iStart], 0, iSize); -- } -- put2byte(&data[iStart], iFreeBlk); -- put2byte(&data[iStart+2], iSize); - pPage->nFree += iOrigSize; - return SQLITE_OK; - } -@@ -66290,57 +73573,67 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ - ** Only the following combinations are supported. Anything different - ** indicates a corrupt database files: - ** --** PTF_ZERODATA --** PTF_ZERODATA | PTF_LEAF --** PTF_LEAFDATA | PTF_INTKEY --** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF -+** PTF_ZERODATA (0x02, 2) -+** PTF_LEAFDATA | PTF_INTKEY (0x05, 5) -+** PTF_ZERODATA | PTF_LEAF (0x0a, 10) -+** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF (0x0d, 13) - */ - static int decodeFlags(MemPage *pPage, int flagByte){ - BtShared *pBt; /* A copy of pPage->pBt */ - - assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); -- pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 ); -- flagByte &= ~PTF_LEAF; -- pPage->childPtrSize = 4-4*pPage->leaf; -- pPage->xCellSize = cellSizePtr; - pBt = pPage->pBt; -- if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ -- /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an -- ** interior table b-tree page. */ -- assert( (PTF_LEAFDATA|PTF_INTKEY)==5 ); -- /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a -- ** leaf table b-tree page. */ -- assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); -- pPage->intKey = 1; -- if( pPage->leaf ){ -+ pPage->max1bytePayload = pBt->max1bytePayload; -+ if( flagByte>=(PTF_ZERODATA | PTF_LEAF) ){ -+ pPage->childPtrSize = 0; -+ pPage->leaf = 1; -+ if( flagByte==(PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF) ){ - pPage->intKeyLeaf = 1; -+ pPage->xCellSize = cellSizePtrTableLeaf; - pPage->xParseCell = btreeParseCellPtr; -+ pPage->intKey = 1; -+ pPage->maxLocal = pBt->maxLeaf; -+ pPage->minLocal = pBt->minLeaf; -+ }else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){ -+ pPage->intKey = 0; -+ pPage->intKeyLeaf = 0; -+ pPage->xCellSize = cellSizePtrIdxLeaf; -+ pPage->xParseCell = btreeParseCellPtrIndex; -+ pPage->maxLocal = pBt->maxLocal; -+ pPage->minLocal = pBt->minLocal; - }else{ -+ pPage->intKey = 0; -+ pPage->intKeyLeaf = 0; -+ pPage->xCellSize = cellSizePtrIdxLeaf; -+ pPage->xParseCell = btreeParseCellPtrIndex; -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } -+ }else{ -+ pPage->childPtrSize = 4; -+ pPage->leaf = 0; -+ if( flagByte==(PTF_ZERODATA) ){ -+ pPage->intKey = 0; -+ pPage->intKeyLeaf = 0; -+ pPage->xCellSize = cellSizePtr; -+ pPage->xParseCell = btreeParseCellPtrIndex; -+ pPage->maxLocal = pBt->maxLocal; -+ pPage->minLocal = pBt->minLocal; -+ }else if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ - pPage->intKeyLeaf = 0; - pPage->xCellSize = cellSizePtrNoPayload; - pPage->xParseCell = btreeParseCellPtrNoPayload; -+ pPage->intKey = 1; -+ pPage->maxLocal = pBt->maxLeaf; -+ pPage->minLocal = pBt->minLeaf; -+ }else{ -+ pPage->intKey = 0; -+ pPage->intKeyLeaf = 0; -+ pPage->xCellSize = cellSizePtr; -+ pPage->xParseCell = btreeParseCellPtrIndex; -+ return SQLITE_CORRUPT_PAGE(pPage); - } -- pPage->maxLocal = pBt->maxLeaf; -- pPage->minLocal = pBt->minLeaf; -- }else if( flagByte==PTF_ZERODATA ){ -- /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an -- ** interior index b-tree page. */ -- assert( (PTF_ZERODATA)==2 ); -- /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a -- ** leaf index b-tree page. */ -- assert( (PTF_ZERODATA|PTF_LEAF)==10 ); -- pPage->intKey = 0; -- pPage->intKeyLeaf = 0; -- pPage->xParseCell = btreeParseCellPtrIndex; -- pPage->maxLocal = pBt->maxLocal; -- pPage->minLocal = pBt->minLocal; -- }else{ -- /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is -- ** an error. */ -- return SQLITE_CORRUPT_PAGE(pPage); - } -- pPage->max1bytePayload = pBt->max1bytePayload; - return SQLITE_OK; - } - -@@ -66493,9 +73786,9 @@ static int btreeInitPage(MemPage *pPage){ - assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); - pPage->maskPage = (u16)(pBt->pageSize - 1); - pPage->nOverflow = 0; -- pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; -+ pPage->cellOffset = (u16)(pPage->hdrOffset + 8 + pPage->childPtrSize); - pPage->aCellIdx = data + pPage->childPtrSize + 8; -- pPage->aDataEnd = pPage->aData + pBt->usableSize; -+ pPage->aDataEnd = pPage->aData + pBt->pageSize; - pPage->aDataOfst = pPage->aData + pPage->childPtrSize; - /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the - ** number of cells on the page. */ -@@ -66527,10 +73820,10 @@ static int btreeInitPage(MemPage *pPage){ - static void zeroPage(MemPage *pPage, int flags){ - unsigned char *data = pPage->aData; - BtShared *pBt = pPage->pBt; -- u8 hdr = pPage->hdrOffset; -- u16 first; -+ int hdr = pPage->hdrOffset; -+ int first; - -- assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); -+ assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB ); - assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); - assert( sqlite3PagerGetData(pPage->pDbPage) == data ); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); -@@ -66545,8 +73838,8 @@ static void zeroPage(MemPage *pPage, int flags){ - put2byte(&data[hdr+5], pBt->usableSize); - pPage->nFree = (u16)(pBt->usableSize - first); - decodeFlags(pPage, flags); -- pPage->cellOffset = first; -- pPage->aDataEnd = &data[pBt->usableSize]; -+ pPage->cellOffset = (u16)first; -+ pPage->aDataEnd = &data[pBt->pageSize]; - pPage->aCellIdx = &data[first]; - pPage->aDataOfst = &data[pPage->childPtrSize]; - pPage->nOverflow = 0; -@@ -66631,68 +73924,41 @@ SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree *p){ - - /* - ** Get a page from the pager and initialize it. --** --** If pCur!=0 then the page is being fetched as part of a moveToChild() --** call. Do additional sanity checking on the page in this case. --** And if the fetch fails, this routine must decrement pCur->iPage. --** --** The page is fetched as read-write unless pCur is not NULL and is --** a read-only cursor. --** --** If an error occurs, then *ppPage is undefined. It --** may remain unchanged, or it may be set to an invalid value. - */ - static int getAndInitPage( - BtShared *pBt, /* The database file */ - Pgno pgno, /* Number of the page to get */ - MemPage **ppPage, /* Write the page pointer here */ -- BtCursor *pCur, /* Cursor to receive the page, or NULL */ - int bReadOnly /* True for a read-only page */ - ){ - int rc; - DbPage *pDbPage; -+ MemPage *pPage; - assert( sqlite3_mutex_held(pBt->mutex) ); -- assert( pCur==0 || ppPage==&pCur->pPage ); -- assert( pCur==0 || bReadOnly==pCur->curPagerFlags ); -- assert( pCur==0 || pCur->iPage>0 ); - - if( pgno>btreePagecount(pBt) ){ -- rc = SQLITE_CORRUPT_BKPT; -- goto getAndInitPage_error1; -+ *ppPage = 0; -+ return SQLITE_CORRUPT_BKPT; - } - rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); - if( rc ){ -- goto getAndInitPage_error1; -+ *ppPage = 0; -+ return rc; - } -- *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); -- if( (*ppPage)->isInit==0 ){ -+ pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage); -+ if( pPage->isInit==0 ){ - btreePageFromDbPage(pDbPage, pgno, pBt); -- rc = btreeInitPage(*ppPage); -+ rc = btreeInitPage(pPage); - if( rc!=SQLITE_OK ){ -- goto getAndInitPage_error2; -+ releasePage(pPage); -+ *ppPage = 0; -+ return rc; - } - } -- assert( (*ppPage)->pgno==pgno ); -- assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) ); -- -- /* If obtaining a child page for a cursor, we must verify that the page is -- ** compatible with the root page. */ -- if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ -- rc = SQLITE_CORRUPT_PGNO(pgno); -- goto getAndInitPage_error2; -- } -+ assert( pPage->pgno==pgno || CORRUPT_DB ); -+ assert( pPage->aData==sqlite3PagerGetData(pDbPage) ); -+ *ppPage = pPage; - return SQLITE_OK; -- --getAndInitPage_error2: -- releasePage(*ppPage); --getAndInitPage_error1: -- if( pCur ){ -- pCur->iPage--; -- pCur->pPage = pCur->apPage[pCur->iPage]; -- } -- testcase( pgno==0 ); -- assert( pgno!=0 || rc==SQLITE_CORRUPT ); -- return rc; - } - - /* -@@ -66775,7 +74041,7 @@ static void pageReinit(DbPage *pData){ - ** call to btreeInitPage() will likely return SQLITE_CORRUPT. - ** But no harm is done by this. And it is very important that - ** btreeInitPage() be called on every btree page so we make -- ** the call for every page that comes in for re-initing. */ -+ ** the call for every page that comes in for re-initializing. */ - btreeInitPage(pPage); - } - } -@@ -66954,6 +74220,9 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( - assert( sizeof(u16)==2 ); - assert( sizeof(Pgno)==4 ); - -+ /* Suppress false-positive compiler warning from PVS-Studio */ -+ memset(&zDbHeader[16], 0, 8); -+ - pBt = sqlite3MallocZero( sizeof(*pBt) ); - if( pBt==0 ){ - rc = SQLITE_NOMEM_BKPT; -@@ -67088,7 +74357,7 @@ btree_open_out: - ** do not change the pager-cache size. - */ - if( sqlite3BtreeSchema(p, 0, 0)==0 ){ -- sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); -+ sqlite3BtreeSetCacheSize(p, SQLITE_DEFAULT_CACHE_SIZE); - } - - pFile = sqlite3PagerFile(pBt->pPager); -@@ -67149,30 +74418,38 @@ static int removeFromSharingList(BtShared *pBt){ - ** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child - ** pointer. - */ --static void allocateTempSpace(BtShared *pBt){ -- if( !pBt->pTmpSpace ){ -- pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize ); -- -- /* One of the uses of pBt->pTmpSpace is to format cells before -- ** inserting them into a leaf page (function fillInCell()). If -- ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes -- ** by the various routines that manipulate binary cells. Which -- ** can mean that fillInCell() only initializes the first 2 or 3 -- ** bytes of pTmpSpace, but that the first 4 bytes are copied from -- ** it into a database page. This is not actually a problem, but it -- ** does cause a valgrind error when the 1 or 2 bytes of unitialized -- ** data is passed to system call write(). So to avoid this error, -- ** zero the first 4 bytes of temp space here. -- ** -- ** Also: Provide four bytes of initialized space before the -- ** beginning of pTmpSpace as an area available to prepend the -- ** left-child pointer to the beginning of a cell. -- */ -- if( pBt->pTmpSpace ){ -- memset(pBt->pTmpSpace, 0, 8); -- pBt->pTmpSpace += 4; -- } -+static SQLITE_NOINLINE int allocateTempSpace(BtShared *pBt){ -+ assert( pBt!=0 ); -+ assert( pBt->pTmpSpace==0 ); -+ /* This routine is called only by btreeCursor() when allocating the -+ ** first write cursor for the BtShared object */ -+ assert( pBt->pCursor!=0 && (pBt->pCursor->curFlags & BTCF_WriteFlag)!=0 ); -+ pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize ); -+ if( pBt->pTmpSpace==0 ){ -+ BtCursor *pCur = pBt->pCursor; -+ pBt->pCursor = pCur->pNext; /* Unlink the cursor */ -+ memset(pCur, 0, sizeof(*pCur)); -+ return SQLITE_NOMEM_BKPT; - } -+ -+ /* One of the uses of pBt->pTmpSpace is to format cells before -+ ** inserting them into a leaf page (function fillInCell()). If -+ ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes -+ ** by the various routines that manipulate binary cells. Which -+ ** can mean that fillInCell() only initializes the first 2 or 3 -+ ** bytes of pTmpSpace, but that the first 4 bytes are copied from -+ ** it into a database page. This is not actually a problem, but it -+ ** does cause a valgrind error when the 1 or 2 bytes of uninitialized -+ ** data is passed to system call write(). So to avoid this error, -+ ** zero the first 4 bytes of temp space here. -+ ** -+ ** Also: Provide four bytes of initialized space before the -+ ** beginning of pTmpSpace as an area available to prepend the -+ ** left-child pointer to the beginning of a cell. -+ */ -+ memset(pBt->pTmpSpace, 0, 8); -+ pBt->pTmpSpace += 4; -+ return SQLITE_OK; - } - - /* -@@ -67191,19 +74468,23 @@ static void freeTempSpace(BtShared *pBt){ - */ - SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ - BtShared *pBt = p->pBt; -- BtCursor *pCur; - - /* Close all cursors opened via this handle. */ - assert( sqlite3_mutex_held(p->db->mutex) ); - sqlite3BtreeEnter(p); -- pCur = pBt->pCursor; -- while( pCur ){ -- BtCursor *pTmp = pCur; -- pCur = pCur->pNext; -- if( pTmp->pBtree==p ){ -- sqlite3BtreeCloseCursor(pTmp); -+ -+ /* Verify that no other cursors have this Btree open */ -+#ifdef SQLITE_DEBUG -+ { -+ BtCursor *pCur = pBt->pCursor; -+ while( pCur ){ -+ BtCursor *pTmp = pCur; -+ pCur = pCur->pNext; -+ assert( pTmp->pBtree!=p ); -+ - } - } -+#endif - - /* Rollback any active transaction and free the handle structure. - ** The call to sqlite3BtreeRollback() drops any table-locks held by -@@ -67343,7 +74624,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, - BtShared *pBt = p->pBt; - assert( nReserve>=0 && nReserve<=255 ); - sqlite3BtreeEnter(p); -- pBt->nReserveWanted = nReserve; -+ pBt->nReserveWanted = (u8)nReserve; - x = pBt->pageSize - pBt->usableSize; - if( nReservebtsFlags & BTS_PAGESIZE_FIXED ){ -@@ -67355,6 +74636,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, - ((pageSize-1)&pageSize)==0 ){ - assert( (pageSize & 7)==0 ); - assert( !pBt->pCursor ); -+ if( nReserve>32 && pageSize==512 ) pageSize = 1024; - pBt->pageSize = (u32)pageSize; - freeTempSpace(pBt); - } -@@ -67392,7 +74674,7 @@ SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){ - - /* - ** Return the number of bytes of space at the end of every page that --** are intentually left unused. This is the "reserved" space that is -+** are intentionally left unused. This is the "reserved" space that is - ** sometimes used by extensions. - ** - ** The value returned is the larger of the current reserve size and -@@ -67448,7 +74730,7 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ - assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); - if( newFlag>=0 ){ - p->pBt->btsFlags &= ~BTS_FAST_SECURE; -- p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag; -+ p->pBt->btsFlags |= (u16)(BTS_SECURE_DELETE*newFlag); - } - b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; - sqlite3BtreeLeave(p); -@@ -67546,7 +74828,6 @@ static int lockBtree(BtShared *pBt){ - MemPage *pPage1; /* Page 1 of the database file */ - u32 nPage; /* Number of pages in the database */ - u32 nPageFile = 0; /* Number of pages in the database file */ -- u32 nPageHeader; /* Number of pages in the database according to hdr */ - - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( pBt->pPage1==0 ); -@@ -67558,7 +74839,7 @@ static int lockBtree(BtShared *pBt){ - /* Do some checking to help insure the file we opened really is - ** a valid database file. - */ -- nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); -+ nPage = get4byte(28+(u8*)pPage1->aData); - sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile); - if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ - nPage = nPageFile; -@@ -67593,7 +74874,7 @@ static int lockBtree(BtShared *pBt){ - goto page1_init_failed; - } - -- /* If the write version is set to 2, this database should be accessed -+ /* If the read version is set to 2, this database should be accessed - ** in WAL mode. If the log is not already open, open it now. Then - ** return SQLITE_OK and return without populating BtShared.pPage1. - ** The caller detects this and calls this function again. This is -@@ -67640,7 +74921,6 @@ static int lockBtree(BtShared *pBt){ - ){ - goto page1_init_failed; - } -- pBt->btsFlags |= BTS_PAGESIZE_FIXED; - assert( (pageSize & 7)==0 ); - /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte - ** integer at offset 20 is the number of bytes of space at the end of -@@ -67660,14 +74940,19 @@ static int lockBtree(BtShared *pBt){ - releasePageOne(pPage1); - pBt->usableSize = usableSize; - pBt->pageSize = pageSize; -+ pBt->btsFlags |= BTS_PAGESIZE_FIXED; - freeTempSpace(pBt); - rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, - pageSize-usableSize); - return rc; - } -- if( sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){ -- rc = SQLITE_CORRUPT_BKPT; -- goto page1_init_failed; -+ if( nPage>nPageFile ){ -+ if( sqlite3WritableSchema(pBt->db)==0 ){ -+ rc = SQLITE_CORRUPT_BKPT; -+ goto page1_init_failed; -+ }else{ -+ nPage = nPageFile; -+ } - } - /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to - ** be less than 480. In other words, if the page size is 512, then the -@@ -67675,6 +74960,7 @@ static int lockBtree(BtShared *pBt){ - if( usableSize<480 ){ - goto page1_init_failed; - } -+ pBt->btsFlags |= BTS_PAGESIZE_FIXED; - pBt->pageSize = pageSize; - pBt->usableSize = usableSize; - #ifndef SQLITE_OMIT_AUTOVACUUM -@@ -67853,7 +75139,11 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){ - ** when A already has a read lock, we encourage A to give up and let B - ** proceed. - */ --SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ -+static SQLITE_NOINLINE int btreeBeginTrans( -+ Btree *p, /* The btree in which to start the transaction */ -+ int wrflag, /* True to start a write transaction */ -+ int *pSchemaVersion /* Put schema version number here, if not NULL */ -+){ - BtShared *pBt = p->pBt; - Pager *pPager = pBt->pPager; - int rc = SQLITE_OK; -@@ -67960,6 +75250,13 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers - (void)sqlite3PagerWalWriteLock(pPager, 0); - unlockBtreeIfUnused(pBt); - } -+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) -+ if( rc==SQLITE_BUSY_TIMEOUT ){ -+ /* If a blocking lock timed out, break out of the loop here so that -+ ** the busy-handler is not invoked. */ -+ break; -+ } -+#endif - }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && - btreeInvokeBusyHandler(pBt) ); - sqlite3PagerWalDb(pPager, 0); -@@ -68025,6 +75322,28 @@ trans_begun: - sqlite3BtreeLeave(p); - return rc; - } -+SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ -+ BtShared *pBt; -+ if( p->sharable -+ || p->inTrans==TRANS_NONE -+ || (p->inTrans==TRANS_READ && wrflag!=0) -+ ){ -+ return btreeBeginTrans(p,wrflag,pSchemaVersion); -+ } -+ pBt = p->pBt; -+ if( pSchemaVersion ){ -+ *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]); -+ } -+ if( wrflag ){ -+ /* This call makes sure that the pager has the correct number of -+ ** open savepoints. If the second parameter is greater than 0 and -+ ** the sub-journal is not already open, then it will be opened here. -+ */ -+ return sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); -+ }else{ -+ return SQLITE_OK; -+ } -+} - - #ifndef SQLITE_OMIT_AUTOVACUUM - -@@ -68111,6 +75430,9 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ - } - } - }else{ -+ if( pCell+4 > pPage->aData+pPage->pBt->usableSize ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } - if( get4byte(pCell)==iFrom ){ - put4byte(pCell, iTo); - break; -@@ -68159,7 +75481,7 @@ static int relocatePage( - if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT; - - /* Move page iDbPage from its current location to page number iFreePage */ -- TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", -+ TRACE(("AUTOVACUUM: Moving %u to free page %u (ptr page %u type %u)\n", - iDbPage, iFreePage, iPtrPage, eType)); - rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit); - if( rc!=SQLITE_OK ){ -@@ -68297,12 +75619,17 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ - } - do { - MemPage *pFreePg; -+ Pgno dbSize = btreePagecount(pBt); - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); - if( rc!=SQLITE_OK ){ - releasePage(pLastPg); - return rc; - } - releasePage(pFreePg); -+ if( iFreePg>dbSize ){ -+ releasePage(pLastPg); -+ return SQLITE_CORRUPT_BKPT; -+ } - }while( bCommit && iFreePg>nFin ); - assert( iFreePgpPager; -- VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager); ) -+ Pager *pPager; -+ BtShared *pBt; -+ sqlite3 *db; -+ VVA_ONLY( int nRef ); -+ -+ assert( p!=0 ); -+ pBt = p->pBt; -+ pPager = pBt->pPager; -+ VVA_ONLY( nRef = sqlite3PagerRefcount(pPager); ) - - assert( sqlite3_mutex_held(pBt->mutex) ); - invalidateAllOverflowCache(pBt); -@@ -68408,6 +75737,7 @@ static int autoVacuumCommit(BtShared *pBt){ - if( !pBt->incrVacuum ){ - Pgno nFin; /* Number of pages in database after autovacuuming */ - Pgno nFree; /* Number of pages on the freelist initially */ -+ Pgno nVac; /* Number of pages to vacuum */ - Pgno iFree; /* The next page to be freed */ - Pgno nOrig; /* Database size before freeing */ - -@@ -68421,18 +75751,42 @@ static int autoVacuumCommit(BtShared *pBt){ - } - - nFree = get4byte(&pBt->pPage1->aData[36]); -- nFin = finalDbSize(pBt, nOrig, nFree); -+ db = p->db; -+ if( db->xAutovacPages ){ -+ int iDb; -+ for(iDb=0; ALWAYS(iDbnDb); iDb++){ -+ if( db->aDb[iDb].pBt==p ) break; -+ } -+ nVac = db->xAutovacPages( -+ db->pAutovacPagesArg, -+ db->aDb[iDb].zDbSName, -+ nOrig, -+ nFree, -+ pBt->pageSize -+ ); -+ if( nVac>nFree ){ -+ nVac = nFree; -+ } -+ if( nVac==0 ){ -+ return SQLITE_OK; -+ } -+ }else{ -+ nVac = nFree; -+ } -+ nFin = finalDbSize(pBt, nOrig, nVac); - if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; - if( nFinnFin && rc==SQLITE_OK; iFree--){ -- rc = incrVacuumStep(pBt, nFin, iFree, 1); -+ rc = incrVacuumStep(pBt, nFin, iFree, nVac==nFree); - } - if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); -- put4byte(&pBt->pPage1->aData[32], 0); -- put4byte(&pBt->pPage1->aData[36], 0); -+ if( nVac==nFree ){ -+ put4byte(&pBt->pPage1->aData[32], 0); -+ put4byte(&pBt->pPage1->aData[36], 0); -+ } - put4byte(&pBt->pPage1->aData[28], nFin); - pBt->bDoTruncate = 1; - pBt->nPage = nFin; -@@ -68483,7 +75837,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ - sqlite3BtreeEnter(p); - #ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ -- rc = autoVacuumCommit(pBt); -+ rc = autoVacuumCommit(p); - if( rc!=SQLITE_OK ){ - sqlite3BtreeLeave(p); - return rc; -@@ -68584,7 +75938,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ - sqlite3BtreeLeave(p); - return rc; - } -- p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ -+ p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */ - pBt->inTransaction = TRANS_READ; - btreeClearHasContent(pBt); - } -@@ -68670,7 +76024,7 @@ static void btreeSetNPage(BtShared *pBt, MemPage *pPage1){ - int nPage = get4byte(&pPage1->aData[28]); - testcase( nPage==0 ); - if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage); -- testcase( pBt->nPage!=nPage ); -+ testcase( pBt->nPage!=(u32)nPage ); - pBt->nPage = nPage; - } - -@@ -68882,10 +76236,6 @@ static int btreeCursor( - assert( pBt->pPage1 && pBt->pPage1->aData ); - assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 ); - -- if( wrFlag ){ -- allocateTempSpace(pBt); -- if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT; -- } - if( iTable<=1 ){ - if( iTable<1 ){ - return SQLITE_CORRUPT_BKPT; -@@ -68902,19 +76252,25 @@ static int btreeCursor( - pCur->pKeyInfo = pKeyInfo; - pCur->pBtree = p; - pCur->pBt = pBt; -- pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; -- pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; -+ pCur->curFlags = 0; - /* If there are two or more cursors on the same btree, then all such - ** cursors *must* have the BTCF_Multiple flag set. */ - for(pX=pBt->pCursor; pX; pX=pX->pNext){ - if( pX->pgnoRoot==iTable ){ - pX->curFlags |= BTCF_Multiple; -- pCur->curFlags |= BTCF_Multiple; -+ pCur->curFlags = BTCF_Multiple; - } - } -+ pCur->eState = CURSOR_INVALID; - pCur->pNext = pBt->pCursor; - pBt->pCursor = pCur; -- pCur->eState = CURSOR_INVALID; -+ if( wrFlag ){ -+ pCur->curFlags |= BTCF_WriteFlag; -+ pCur->curPagerFlags = 0; -+ if( pBt->pTmpSpace==0 ) return allocateTempSpace(pBt); -+ }else{ -+ pCur->curPagerFlags = PAGER_GET_READONLY; -+ } - return SQLITE_OK; - } - static int btreeCursorWithLock( -@@ -68956,6 +76312,25 @@ SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ - return ROUND8(sizeof(BtCursor)); - } - -+#ifdef SQLITE_DEBUG -+/* -+** Return true if and only if the Btree object will be automatically -+** closed with the BtCursor closes. This is used within assert() statements -+** only. -+*/ -+SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor( -+ Btree *pBtree, /* the btree object */ -+ BtCursor *pCur /* Corresponding cursor */ -+){ -+ BtShared *pBt = pBtree->pBt; -+ if( (pBt->openFlags & BTREE_SINGLE)==0 ) return 0; -+ if( pBt->pCursor!=pCur ) return 0; -+ if( pCur->pNext!=0 ) return 0; -+ if( pCur->pBtree!=pBtree ) return 0; -+ return 1; -+} -+#endif -+ - /* - ** Initialize memory that will be converted into a BtCursor object. - ** -@@ -68994,7 +76369,14 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){ - unlockBtreeIfUnused(pBt); - sqlite3_free(pCur->aOverflow); - sqlite3_free(pCur->pKey); -- sqlite3BtreeLeave(pBtree); -+ if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){ -+ /* Since the BtShared is not sharable, there is no need to -+ ** worry about the missing sqlite3BtreeLeave() call here. */ -+ assert( pBtree->sharable==0 ); -+ sqlite3BtreeClose(pBtree); -+ }else{ -+ sqlite3BtreeLeave(pBtree); -+ } - pCur->pBtree = 0; - } - return SQLITE_OK; -@@ -69076,7 +76458,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor *pCur){ - pCur->curFlags &= ~BTCF_Pinned; - } - --#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - /* - ** Return the offset into the database file for the start of the - ** payload to which the cursor is pointing. -@@ -69088,7 +76469,6 @@ SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor *pCur){ - return (i64)pCur->pBt->pageSize*((i64)pCur->pPage->pgno - 1) + - (i64)(pCur->info.pPayload - pCur->pPage->aData); - } --#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ - - /* - ** Return the number of bytes of payload for the entry that pCur is -@@ -69114,7 +76494,7 @@ SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor *pCur){ - ** routine always returns 2147483647 (which is the largest record - ** that SQLite can handle) or more. But returning a smaller value might - ** prevent large memory allocations when trying to interpret a --** corrupt datrabase. -+** corrupt database. - ** - ** The current implementation merely returns the size of the underlying - ** database file. -@@ -69281,7 +76661,9 @@ static int accessPayload( - assert( pPage ); - assert( eOp==0 || eOp==1 ); - assert( pCur->eState==CURSOR_VALID ); -- assert( pCur->ixnCell ); -+ if( pCur->ix>=pPage->nCell ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } - assert( cursorHoldsMutex(pCur) ); - - getCellInfo(pCur); -@@ -69331,9 +76713,12 @@ static int accessPayload( - if( pCur->aOverflow==0 - || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) - ){ -- Pgno *aNew = (Pgno*)sqlite3Realloc( -- pCur->aOverflow, nOvfl*2*sizeof(Pgno) -- ); -+ Pgno *aNew; -+ if( sqlite3FaultSim(413) ){ -+ aNew = 0; -+ }else{ -+ aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno)); -+ } - if( aNew==0 ){ - return SQLITE_NOMEM_BKPT; - }else{ -@@ -69343,6 +76728,12 @@ static int accessPayload( - memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); - pCur->curFlags |= BTCF_ValidOvfl; - }else{ -+ /* Sanity check the validity of the overflow page cache */ -+ assert( pCur->aOverflow[0]==nextPage -+ || pCur->aOverflow[0]==0 -+ || CORRUPT_DB ); -+ assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 ); -+ - /* If the overflow page-list cache has been allocated and the - ** entry for the first required overflow page is valid, skip - ** directly to it. -@@ -69412,7 +76803,6 @@ static int accessPayload( - assert( aWrite>=pBufStart ); /* due to (6) */ - memcpy(aSave, aWrite, 4); - rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); -- if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT; - nextPage = get4byte(aWrite); - memcpy(aWrite, aSave, 4); - }else -@@ -69468,7 +76858,6 @@ SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void - assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->pPage ); -- assert( pCur->ixpPage->nCell ); - return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); - } - -@@ -69530,7 +76919,7 @@ static const void *fetchPayload( - assert( pCur->eState==CURSOR_VALID ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorOwnsBtShared(pCur) ); -- assert( pCur->ixpPage->nCell ); -+ assert( pCur->ixpPage->nCell || CORRUPT_DB ); - assert( pCur->info.nSize>0 ); - assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB ); - assert( pCur->info.pPayloadpPage->aDataEnd ||CORRUPT_DB); -@@ -69575,8 +76964,7 @@ SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){ - ** vice-versa). - */ - static int moveToChild(BtCursor *pCur, u32 newPgno){ -- BtShared *pBt = pCur->pBt; -- -+ int rc; - assert( cursorOwnsBtShared(pCur) ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPageapPage[pCur->iPage] = pCur->pPage; - pCur->ix = 0; - pCur->iPage++; -- return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags); -+ rc = getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur->curPagerFlags); -+ assert( pCur->pPage!=0 || rc!=SQLITE_OK ); -+ if( rc==SQLITE_OK -+ && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) -+ ){ -+ releasePage(pCur->pPage); -+ rc = SQLITE_CORRUPT_PGNO(newPgno); -+ } -+ if( rc ){ -+ pCur->pPage = pCur->apPage[--pCur->iPage]; -+ } -+ return rc; - } - - #ifdef SQLITE_DEBUG -@@ -69681,7 +77080,7 @@ static int moveToRoot(BtCursor *pCur){ - while( --pCur->iPage ){ - releasePageNotNull(pCur->apPage[pCur->iPage]); - } -- pCur->pPage = pCur->apPage[0]; -+ pRoot = pCur->pPage = pCur->apPage[0]; - goto skip_init; - } - }else if( pCur->pgnoRoot==0 ){ -@@ -69696,8 +77095,8 @@ static int moveToRoot(BtCursor *pCur){ - } - sqlite3BtreeClearCursor(pCur); - } -- rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage, -- 0, pCur->curPagerFlags); -+ rc = getAndInitPage(pCur->pBt, pCur->pgnoRoot, &pCur->pPage, -+ pCur->curPagerFlags); - if( rc!=SQLITE_OK ){ - pCur->eState = CURSOR_INVALID; - return rc; -@@ -69706,7 +77105,7 @@ static int moveToRoot(BtCursor *pCur){ - pCur->curIntKey = pCur->pPage->intKey; - } - pRoot = pCur->pPage; -- assert( pRoot->pgno==pCur->pgnoRoot ); -+ assert( pRoot->pgno==pCur->pgnoRoot || CORRUPT_DB ); - - /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor - ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is -@@ -69728,7 +77127,6 @@ skip_init: - pCur->info.nSize = 0; - pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); - -- pRoot = pCur->pPage; - if( pRoot->nCell>0 ){ - pCur->eState = CURSOR_VALID; - }else if( !pRoot->leaf ){ -@@ -69810,40 +77208,36 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ - *pRes = 0; - rc = moveToLeftmost(pCur); - }else if( rc==SQLITE_EMPTY ){ -- assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); -+ assert( pCur->pgnoRoot==0 || (pCur->pPage!=0 && pCur->pPage->nCell==0) ); - *pRes = 1; - rc = SQLITE_OK; - } - return rc; - } - -+#ifdef SQLITE_DEBUG -+/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that -+** this flags are true for a consistent database. -+** -+** This routine is is called from within assert() statements only. -+** It is an internal verification routine and does not appear in production -+** builds. -+*/ -+static int cursorIsAtLastEntry(BtCursor *pCur){ -+ int ii; -+ for(ii=0; iiiPage; ii++){ -+ if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0; -+ } -+ return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0; -+} -+#endif -+ - /* Move the cursor to the last entry in the table. Return SQLITE_OK - ** on success. Set *pRes to 0 if the cursor actually points to something - ** or set *pRes to 1 if the table is empty. - */ --SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ -- int rc; -- -- assert( cursorOwnsBtShared(pCur) ); -- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); -- -- /* If the cursor already points to the last entry, this is a no-op. */ -- if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ --#ifdef SQLITE_DEBUG -- /* This block serves to assert() that the cursor really does point -- ** to the last entry in the b-tree. */ -- int ii; -- for(ii=0; iiiPage; ii++){ -- assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); -- } -- assert( pCur->ix==pCur->pPage->nCell-1 ); -- assert( pCur->pPage->leaf ); --#endif -- *pRes = 0; -- return SQLITE_OK; -- } -- -- rc = moveToRoot(pCur); -+static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){ -+ int rc = moveToRoot(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_VALID ); - *pRes = 0; -@@ -69860,13 +77254,250 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ - } - return rc; - } -+SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ -+ assert( cursorOwnsBtShared(pCur) ); -+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); -+ -+ /* If the cursor already points to the last entry, this is a no-op. */ -+ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ -+ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); -+ *pRes = 0; -+ return SQLITE_OK; -+ } -+ return btreeLast(pCur, pRes); -+} -+ -+/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY) -+** table near the key intKey. Return a success code. -+** -+** If an exact match is not found, then the cursor is always -+** left pointing at a leaf page which would hold the entry if it -+** were present. The cursor might point to an entry that comes -+** before or after the key. -+** -+** An integer is written into *pRes which is the result of -+** comparing the key with the entry to which the cursor is -+** pointing. The meaning of the integer written into -+** *pRes is as follows: -+** -+** *pRes<0 The cursor is left pointing at an entry that -+** is smaller than intKey or if the table is empty -+** and the cursor is therefore left point to nothing. -+** -+** *pRes==0 The cursor is left pointing at an entry that -+** exactly matches intKey. -+** -+** *pRes>0 The cursor is left pointing at an entry that -+** is larger than intKey. -+*/ -+SQLITE_PRIVATE int sqlite3BtreeTableMoveto( -+ BtCursor *pCur, /* The cursor to be moved */ -+ i64 intKey, /* The table key */ -+ int biasRight, /* If true, bias the search to the high end */ -+ int *pRes /* Write search results here */ -+){ -+ int rc; -+ -+ assert( cursorOwnsBtShared(pCur) ); -+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); -+ assert( pRes ); -+ assert( pCur->pKeyInfo==0 ); -+ assert( pCur->eState!=CURSOR_VALID || pCur->curIntKey!=0 ); -+ -+ /* If the cursor is already positioned at the point we are trying -+ ** to move to, then just return without doing any work */ -+ if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ -+ if( pCur->info.nKey==intKey ){ -+ *pRes = 0; -+ return SQLITE_OK; -+ } -+ if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ -+ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); -+ *pRes = -1; -+ return SQLITE_OK; -+ } -+ /* If the requested key is one more than the previous key, then -+ ** try to get there using sqlite3BtreeNext() rather than a full -+ ** binary search. This is an optimization only. The correct answer -+ ** is still obtained without this case, only a little more slowly. */ -+ if( pCur->info.nKey+1==intKey ){ -+ *pRes = 0; -+ rc = sqlite3BtreeNext(pCur, 0); -+ if( rc==SQLITE_OK ){ -+ getCellInfo(pCur); -+ if( pCur->info.nKey==intKey ){ -+ return SQLITE_OK; -+ } -+ }else if( rc!=SQLITE_DONE ){ -+ return rc; -+ } -+ } -+ } -+ } -+ -+#ifdef SQLITE_DEBUG -+ pCur->pBtree->nSeek++; /* Performance measurement during testing */ -+#endif -+ -+ rc = moveToRoot(pCur); -+ if( rc ){ -+ if( rc==SQLITE_EMPTY ){ -+ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 ); -+ *pRes = -1; -+ return SQLITE_OK; -+ } -+ return rc; -+ } -+ assert( pCur->pPage ); -+ assert( pCur->pPage->isInit ); -+ assert( pCur->eState==CURSOR_VALID ); -+ assert( pCur->pPage->nCell > 0 ); -+ assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); -+ assert( pCur->curIntKey ); -+ -+ for(;;){ -+ int lwr, upr, idx, c; -+ Pgno chldPg; -+ MemPage *pPage = pCur->pPage; -+ u8 *pCell; /* Pointer to current cell in pPage */ -+ -+ /* pPage->nCell must be greater than zero. If this is the root-page -+ ** the cursor would have been INVALID above and this for(;;) loop -+ ** not run. If this is not the root-page, then the moveToChild() routine -+ ** would have already detected db corruption. Similarly, pPage must -+ ** be the right kind (index or table) of b-tree page. Otherwise -+ ** a moveToChild() or moveToRoot() call would have detected corruption. */ -+ assert( pPage->nCell>0 ); -+ assert( pPage->intKey ); -+ lwr = 0; -+ upr = pPage->nCell-1; -+ assert( biasRight==0 || biasRight==1 ); -+ idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ -+ for(;;){ -+ i64 nCellKey; -+ pCell = findCellPastPtr(pPage, idx); -+ if( pPage->intKeyLeaf ){ -+ while( 0x80 <= *(pCell++) ){ -+ if( pCell>=pPage->aDataEnd ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } -+ } -+ } -+ getVarint(pCell, (u64*)&nCellKey); -+ if( nCellKeyupr ){ c = -1; break; } -+ }else if( nCellKey>intKey ){ -+ upr = idx-1; -+ if( lwr>upr ){ c = +1; break; } -+ }else{ -+ assert( nCellKey==intKey ); -+ pCur->ix = (u16)idx; -+ if( !pPage->leaf ){ -+ lwr = idx; -+ goto moveto_table_next_layer; -+ }else{ -+ pCur->curFlags |= BTCF_ValidNKey; -+ pCur->info.nKey = nCellKey; -+ pCur->info.nSize = 0; -+ *pRes = 0; -+ return SQLITE_OK; -+ } -+ } -+ assert( lwr+upr>=0 ); -+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ -+ } -+ assert( lwr==upr+1 || !pPage->leaf ); -+ assert( pPage->isInit ); -+ if( pPage->leaf ){ -+ assert( pCur->ixpPage->nCell ); -+ pCur->ix = (u16)idx; -+ *pRes = c; -+ rc = SQLITE_OK; -+ goto moveto_table_finish; -+ } -+moveto_table_next_layer: -+ if( lwr>=pPage->nCell ){ -+ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); -+ }else{ -+ chldPg = get4byte(findCell(pPage, lwr)); -+ } -+ pCur->ix = (u16)lwr; -+ rc = moveToChild(pCur, chldPg); -+ if( rc ) break; -+ } -+moveto_table_finish: -+ pCur->info.nSize = 0; -+ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); -+ return rc; -+} - --/* Move the cursor so that it points to an entry near the key --** specified by pIdxKey or intKey. Return a success code. -+/* -+** Compare the "idx"-th cell on the page the cursor pCur is currently -+** pointing to to pIdxKey using xRecordCompare. Return negative or -+** zero if the cell is less than or equal pIdxKey. Return positive -+** if unknown. -+** -+** Return value negative: Cell at pCur[idx] less than pIdxKey -+** -+** Return value is zero: Cell at pCur[idx] equals pIdxKey - ** --** For INTKEY tables, the intKey parameter is used. pIdxKey --** must be NULL. For index tables, pIdxKey is used and intKey --** is ignored. -+** Return value positive: Nothing is known about the relationship -+** of the cell at pCur[idx] and pIdxKey. -+** -+** This routine is part of an optimization. It is always safe to return -+** a positive value as that will cause the optimization to be skipped. -+*/ -+static int indexCellCompare( -+ BtCursor *pCur, -+ int idx, -+ UnpackedRecord *pIdxKey, -+ RecordCompare xRecordCompare -+){ -+ MemPage *pPage = pCur->pPage; -+ int c; -+ int nCell; /* Size of the pCell cell in bytes */ -+ u8 *pCell = findCellPastPtr(pPage, idx); -+ -+ nCell = pCell[0]; -+ if( nCell<=pPage->max1bytePayload ){ -+ /* This branch runs if the record-size field of the cell is a -+ ** single byte varint and the record fits entirely on the main -+ ** b-tree page. */ -+ testcase( pCell+nCell+1==pPage->aDataEnd ); -+ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); -+ }else if( !(pCell[1] & 0x80) -+ && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal -+ ){ -+ /* The record-size field is a 2 byte varint and the record -+ ** fits entirely on the main b-tree page. */ -+ testcase( pCell+nCell+2==pPage->aDataEnd ); -+ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); -+ }else{ -+ /* If the record extends into overflow pages, do not attempt -+ ** the optimization. */ -+ c = 99; -+ } -+ return c; -+} -+ -+/* -+** Return true (non-zero) if pCur is current pointing to the last -+** page of a table. -+*/ -+static int cursorOnLastPage(BtCursor *pCur){ -+ int i; -+ assert( pCur->eState==CURSOR_VALID ); -+ for(i=0; iiPage; i++){ -+ MemPage *pPage = pCur->apPage[i]; -+ if( pCur->aiIdx[i]nCell ) return 0; -+ } -+ return 1; -+} -+ -+/* Move the cursor so that it points to an entry in an index table -+** near the key pIdxKey. Return a success code. - ** - ** If an exact match is not found, then the cursor is always - ** left pointing at a leaf page which would hold the entry if it -@@ -69879,23 +77510,21 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ - ** *pRes is as follows: - ** - ** *pRes<0 The cursor is left pointing at an entry that --** is smaller than intKey/pIdxKey or if the table is empty -+** is smaller than pIdxKey or if the table is empty - ** and the cursor is therefore left point to nothing. - ** - ** *pRes==0 The cursor is left pointing at an entry that --** exactly matches intKey/pIdxKey. -+** exactly matches pIdxKey. - ** - ** *pRes>0 The cursor is left pointing at an entry that --** is larger than intKey/pIdxKey. -+** is larger than pIdxKey. - ** --** For index tables, the pIdxKey->eqSeen field is set to 1 if there -+** The pIdxKey->eqSeen field is set to 1 if there - ** exists an entry in the table that exactly matches pIdxKey. - */ --SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( -+SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( - BtCursor *pCur, /* The cursor to be moved */ - UnpackedRecord *pIdxKey, /* Unpacked index key */ -- i64 intKey, /* The table key */ -- int biasRight, /* If true, bias the search to the high end */ - int *pRes /* Write search results here */ - ){ - int rc; -@@ -69904,53 +77533,54 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( - assert( cursorOwnsBtShared(pCur) ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( pRes ); -- assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); -- assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) ); -+ assert( pCur->pKeyInfo!=0 ); - -- /* If the cursor is already positioned at the point we are trying -- ** to move to, then just return without doing any work */ -- if( pIdxKey==0 -- && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 -+#ifdef SQLITE_DEBUG -+ pCur->pBtree->nSeek++; /* Performance measurement during testing */ -+#endif -+ -+ xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); -+ pIdxKey->errCode = 0; -+ assert( pIdxKey->default_rc==1 -+ || pIdxKey->default_rc==0 -+ || pIdxKey->default_rc==-1 -+ ); -+ -+ -+ /* Check to see if we can skip a lot of work. Two cases: -+ ** -+ ** (1) If the cursor is already pointing to the very last cell -+ ** in the table and the pIdxKey search key is greater than or -+ ** equal to that last cell, then no movement is required. -+ ** -+ ** (2) If the cursor is on the last page of the table and the first -+ ** cell on that last page is less than or equal to the pIdxKey -+ ** search key, then we can start the search on the current page -+ ** without needing to go back to root. -+ */ -+ if( pCur->eState==CURSOR_VALID -+ && pCur->pPage->leaf -+ && cursorOnLastPage(pCur) - ){ -- if( pCur->info.nKey==intKey ){ -- *pRes = 0; -- return SQLITE_OK; -+ int c; -+ if( pCur->ix==pCur->pPage->nCell-1 -+ && (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0 -+ && pIdxKey->errCode==SQLITE_OK -+ ){ -+ *pRes = c; -+ return SQLITE_OK; /* Cursor already pointing at the correct spot */ - } -- if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ -- *pRes = -1; -- return SQLITE_OK; -- } -- /* If the requested key is one more than the previous key, then -- ** try to get there using sqlite3BtreeNext() rather than a full -- ** binary search. This is an optimization only. The correct answer -- ** is still obtained without this case, only a little more slowely */ -- if( pCur->info.nKey+1==intKey ){ -- *pRes = 0; -- rc = sqlite3BtreeNext(pCur, 0); -- if( rc==SQLITE_OK ){ -- getCellInfo(pCur); -- if( pCur->info.nKey==intKey ){ -- return SQLITE_OK; -- } -- }else if( rc==SQLITE_DONE ){ -- rc = SQLITE_OK; -- }else{ -- return rc; -- } -+ if( pCur->iPage>0 -+ && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0 -+ && pIdxKey->errCode==SQLITE_OK -+ ){ -+ pCur->curFlags &= ~(BTCF_ValidOvfl|BTCF_AtLast); -+ if( !pCur->pPage->isInit ){ -+ return SQLITE_CORRUPT_BKPT; - } -+ goto bypass_moveto_root; /* Start search on the current page */ - } -- } -- -- if( pIdxKey ){ -- xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); -- pIdxKey->errCode = 0; -- assert( pIdxKey->default_rc==1 -- || pIdxKey->default_rc==0 -- || pIdxKey->default_rc==-1 -- ); -- }else{ -- xRecordCompare = 0; /* All keys are integers */ -+ pIdxKey->errCode = SQLITE_OK; - } - - rc = moveToRoot(pCur); -@@ -69962,12 +77592,14 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( - } - return rc; - } -+ -+bypass_moveto_root: - assert( pCur->pPage ); - assert( pCur->pPage->isInit ); - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->pPage->nCell > 0 ); -- assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); -- assert( pCur->curIntKey || pIdxKey ); -+ assert( pCur->curIntKey==0 ); -+ assert( pIdxKey!=0 ); - for(;;){ - int lwr, upr, idx, c; - Pgno chldPg; -@@ -69981,154 +77613,140 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( - ** be the right kind (index or table) of b-tree page. Otherwise - ** a moveToChild() or moveToRoot() call would have detected corruption. */ - assert( pPage->nCell>0 ); -- assert( pPage->intKey==(pIdxKey==0) ); -+ assert( pPage->intKey==0 ); - lwr = 0; - upr = pPage->nCell-1; -- assert( biasRight==0 || biasRight==1 ); -- idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ -- pCur->ix = (u16)idx; -- if( xRecordCompare==0 ){ -- for(;;){ -- i64 nCellKey; -- pCell = findCellPastPtr(pPage, idx); -- if( pPage->intKeyLeaf ){ -- while( 0x80 <= *(pCell++) ){ -- if( pCell>=pPage->aDataEnd ){ -- return SQLITE_CORRUPT_PAGE(pPage); -- } -- } -- } -- getVarint(pCell, (u64*)&nCellKey); -- if( nCellKeyupr ){ c = -1; break; } -- }else if( nCellKey>intKey ){ -- upr = idx-1; -- if( lwr>upr ){ c = +1; break; } -- }else{ -- assert( nCellKey==intKey ); -- pCur->ix = (u16)idx; -- if( !pPage->leaf ){ -- lwr = idx; -- goto moveto_next_layer; -- }else{ -- pCur->curFlags |= BTCF_ValidNKey; -- pCur->info.nKey = nCellKey; -- pCur->info.nSize = 0; -- *pRes = 0; -- return SQLITE_OK; -- } -+ idx = upr>>1; /* idx = (lwr+upr)/2; */ -+ for(;;){ -+ int nCell; /* Size of the pCell cell in bytes */ -+ pCell = findCellPastPtr(pPage, idx); -+ -+ /* The maximum supported page-size is 65536 bytes. This means that -+ ** the maximum number of record bytes stored on an index B-Tree -+ ** page is less than 16384 bytes and may be stored as a 2-byte -+ ** varint. This information is used to attempt to avoid parsing -+ ** the entire cell by checking for the cases where the record is -+ ** stored entirely within the b-tree page by inspecting the first -+ ** 2 bytes of the cell. -+ */ -+ nCell = pCell[0]; -+ if( nCell<=pPage->max1bytePayload ){ -+ /* This branch runs if the record-size field of the cell is a -+ ** single byte varint and the record fits entirely on the main -+ ** b-tree page. */ -+ testcase( pCell+nCell+1==pPage->aDataEnd ); -+ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); -+ }else if( !(pCell[1] & 0x80) -+ && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal -+ ){ -+ /* The record-size field is a 2 byte varint and the record -+ ** fits entirely on the main b-tree page. */ -+ testcase( pCell+nCell+2==pPage->aDataEnd ); -+ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); -+ }else{ -+ /* The record flows over onto one or more overflow pages. In -+ ** this case the whole cell needs to be parsed, a buffer allocated -+ ** and accessPayload() used to retrieve the record into the -+ ** buffer before VdbeRecordCompare() can be called. -+ ** -+ ** If the record is corrupt, the xRecordCompare routine may read -+ ** up to two varints past the end of the buffer. An extra 18 -+ ** bytes of padding is allocated at the end of the buffer in -+ ** case this happens. */ -+ void *pCellKey; -+ u8 * const pCellBody = pCell - pPage->childPtrSize; -+ const int nOverrun = 18; /* Size of the overrun padding */ -+ pPage->xParseCell(pPage, pCellBody, &pCur->info); -+ nCell = (int)pCur->info.nKey; -+ testcase( nCell<0 ); /* True if key size is 2^32 or more */ -+ testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ -+ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ -+ testcase( nCell==2 ); /* Minimum legal index key size */ -+ if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ -+ rc = SQLITE_CORRUPT_PAGE(pPage); -+ goto moveto_index_finish; -+ } -+ pCellKey = sqlite3Malloc( (u64)nCell+(u64)nOverrun ); -+ if( pCellKey==0 ){ -+ rc = SQLITE_NOMEM_BKPT; -+ goto moveto_index_finish; - } -- assert( lwr+upr>=0 ); -- idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ -- } -- }else{ -- for(;;){ -- int nCell; /* Size of the pCell cell in bytes */ -- pCell = findCellPastPtr(pPage, idx); -- -- /* The maximum supported page-size is 65536 bytes. This means that -- ** the maximum number of record bytes stored on an index B-Tree -- ** page is less than 16384 bytes and may be stored as a 2-byte -- ** varint. This information is used to attempt to avoid parsing -- ** the entire cell by checking for the cases where the record is -- ** stored entirely within the b-tree page by inspecting the first -- ** 2 bytes of the cell. -- */ -- nCell = pCell[0]; -- if( nCell<=pPage->max1bytePayload ){ -- /* This branch runs if the record-size field of the cell is a -- ** single byte varint and the record fits entirely on the main -- ** b-tree page. */ -- testcase( pCell+nCell+1==pPage->aDataEnd ); -- c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); -- }else if( !(pCell[1] & 0x80) -- && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal -- ){ -- /* The record-size field is a 2 byte varint and the record -- ** fits entirely on the main b-tree page. */ -- testcase( pCell+nCell+2==pPage->aDataEnd ); -- c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); -- }else{ -- /* The record flows over onto one or more overflow pages. In -- ** this case the whole cell needs to be parsed, a buffer allocated -- ** and accessPayload() used to retrieve the record into the -- ** buffer before VdbeRecordCompare() can be called. -- ** -- ** If the record is corrupt, the xRecordCompare routine may read -- ** up to two varints past the end of the buffer. An extra 18 -- ** bytes of padding is allocated at the end of the buffer in -- ** case this happens. */ -- void *pCellKey; -- u8 * const pCellBody = pCell - pPage->childPtrSize; -- const int nOverrun = 18; /* Size of the overrun padding */ -- pPage->xParseCell(pPage, pCellBody, &pCur->info); -- nCell = (int)pCur->info.nKey; -- testcase( nCell<0 ); /* True if key size is 2^32 or more */ -- testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ -- testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ -- testcase( nCell==2 ); /* Minimum legal index key size */ -- if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ -- rc = SQLITE_CORRUPT_PAGE(pPage); -- goto moveto_finish; -- } -- pCellKey = sqlite3Malloc( nCell+nOverrun ); -- if( pCellKey==0 ){ -- rc = SQLITE_NOMEM_BKPT; -- goto moveto_finish; -- } -- pCur->ix = (u16)idx; -- rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); -- memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ -- pCur->curFlags &= ~BTCF_ValidOvfl; -- if( rc ){ -- sqlite3_free(pCellKey); -- goto moveto_finish; -- } -- c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); -+ pCur->ix = (u16)idx; -+ rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); -+ memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */ -+ pCur->curFlags &= ~BTCF_ValidOvfl; -+ if( rc ){ - sqlite3_free(pCellKey); -+ goto moveto_index_finish; - } -- assert( -- (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) -- && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) -- ); -- if( c<0 ){ -- lwr = idx+1; -- }else if( c>0 ){ -- upr = idx-1; -- }else{ -- assert( c==0 ); -- *pRes = 0; -- rc = SQLITE_OK; -- pCur->ix = (u16)idx; -- if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; -- goto moveto_finish; -- } -- if( lwr>upr ) break; -- assert( lwr+upr>=0 ); -- idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ -+ c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); -+ sqlite3_free(pCellKey); - } -+ assert( -+ (pIdxKey->errCode!=SQLITE_CORRUPT || c==0) -+ && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed) -+ ); -+ if( c<0 ){ -+ lwr = idx+1; -+ }else if( c>0 ){ -+ upr = idx-1; -+ }else{ -+ assert( c==0 ); -+ *pRes = 0; -+ rc = SQLITE_OK; -+ pCur->ix = (u16)idx; -+ if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT; -+ goto moveto_index_finish; -+ } -+ if( lwr>upr ) break; -+ assert( lwr+upr>=0 ); -+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ - } - assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); - assert( pPage->isInit ); - if( pPage->leaf ){ -- assert( pCur->ixpPage->nCell ); -+ assert( pCur->ixpPage->nCell || CORRUPT_DB ); - pCur->ix = (u16)idx; - *pRes = c; - rc = SQLITE_OK; -- goto moveto_finish; -+ goto moveto_index_finish; - } --moveto_next_layer: - if( lwr>=pPage->nCell ){ - chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); - }else{ - chldPg = get4byte(findCell(pPage, lwr)); - } -- pCur->ix = (u16)lwr; -- rc = moveToChild(pCur, chldPg); -- if( rc ) break; -- } --moveto_finish: -+ -+ /* This block is similar to an in-lined version of: -+ ** -+ ** pCur->ix = (u16)lwr; -+ ** rc = moveToChild(pCur, chldPg); -+ ** if( rc ) break; -+ */ -+ pCur->info.nSize = 0; -+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); -+ if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ -+ return SQLITE_CORRUPT_BKPT; -+ } -+ pCur->aiIdx[pCur->iPage] = (u16)lwr; -+ pCur->apPage[pCur->iPage] = pCur->pPage; -+ pCur->ix = 0; -+ pCur->iPage++; -+ rc = getAndInitPage(pCur->pBt, chldPg, &pCur->pPage, pCur->curPagerFlags); -+ if( rc==SQLITE_OK -+ && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) -+ ){ -+ releasePage(pCur->pPage); -+ rc = SQLITE_CORRUPT_PGNO(chldPg); -+ } -+ if( rc ){ -+ pCur->pPage = pCur->apPage[--pCur->iPage]; -+ break; -+ } -+ /* -+ ***** End of in-lined moveToChild() call */ -+ } -+moveto_index_finish: - pCur->info.nSize = 0; - assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); - return rc; -@@ -70162,10 +77780,10 @@ SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){ - assert( cursorOwnsBtShared(pCur) ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - -- /* Currently this interface is only called by the OP_IfSmaller -- ** opcode, and it that case the cursor will always be valid and -- ** will always point to a leaf node. */ -- if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; -+ /* Currently this interface is only called by the OP_IfSizeBetween -+ ** opcode and the OP_Count opcode with P3=1. In either case, -+ ** the cursor will always be valid unless the btree is empty. */ -+ if( pCur->eState!=CURSOR_VALID ) return 0; - if( NEVER(pCur->pPage->leaf==0) ) return -1; - - n = pCur->pPage->nCell; -@@ -70218,27 +77836,11 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ - - pPage = pCur->pPage; - idx = ++pCur->ix; -+ if( sqlite3FaultSim(412) ) pPage->isInit = 0; - if( !pPage->isInit ){ -- /* The only known way for this to happen is for there to be a -- ** recursive SQL function that does a DELETE operation as part of a -- ** SELECT which deletes content out from under an active cursor -- ** in a corrupt database file where the table being DELETE-ed from -- ** has pages in common with the table being queried. See TH3 -- ** module cov1/btree78.test testcase 220 (2018-06-08) for an -- ** example. */ - return SQLITE_CORRUPT_BKPT; - } - -- /* If the database file is corrupt, it is possible for the value of idx -- ** to be invalid here. This can only occur if a second cursor modifies -- ** the page while cursor pCur is holding a reference to it. Which can -- ** only happen if the database is corrupt in such a way as to link the -- ** page into more than one b-tree structure. -- ** -- ** Update 2019-12-23: appears to long longer be possible after the -- ** addition of anotherValidCursor() condition on balance_deeper(). */ -- harmless( idx>pPage->nCell ); -- - if( idx>=pPage->nCell ){ - if( !pPage->leaf ){ - rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); -@@ -70327,7 +77929,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ - } - - pPage = pCur->pPage; -- assert( pPage->isInit ); -+ if( sqlite3FaultSim(412) ) pPage->isInit = 0; -+ if( !pPage->isInit ){ -+ return SQLITE_CORRUPT_BKPT; -+ } - if( !pPage->leaf ){ - int idx = pCur->ix; - rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); -@@ -70411,8 +78016,8 @@ static int allocateBtreePage( - assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) ); - pPage1 = pBt->pPage1; - mxPage = btreePagecount(pBt); -- /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 -- ** stores stores the total number of pages on the freelist. */ -+ /* EVIDENCE-OF: R-21003-45125 The 4-byte big-endian integer at offset 36 -+ ** stores the total number of pages on the freelist. */ - n = get4byte(&pPage1->aData[36]); - testcase( n==mxPage-1 ); - if( n>=mxPage ){ -@@ -70498,7 +78103,7 @@ static int allocateBtreePage( - memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); - *ppPage = pTrunk; - pTrunk = 0; -- TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); -+ TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1)); - }else if( k>(u32)(pBt->usableSize/4 - 2) ){ - /* Value of k is out of range. Database corruption */ - rc = SQLITE_CORRUPT_PGNO(iTrunk); -@@ -70564,7 +78169,7 @@ static int allocateBtreePage( - } - } - pTrunk = 0; -- TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); -+ TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1)); - #endif - }else if( k>0 ){ - /* Extract a leaf from the trunk */ -@@ -70599,7 +78204,7 @@ static int allocateBtreePage( - - iPage = get4byte(&aData[8+closest*4]); - testcase( iPage==mxPage ); -- if( iPage>mxPage ){ -+ if( iPage>mxPage || iPage<2 ){ - rc = SQLITE_CORRUPT_PGNO(iTrunk); - goto end_allocate_page; - } -@@ -70609,8 +78214,8 @@ static int allocateBtreePage( - ){ - int noContent; - *pPgno = iPage; -- TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" -- ": %d more free pages\n", -+ TRACE(("ALLOCATE: %u was leaf %u of %u on trunk %u" -+ ": %u more free pages\n", - *pPgno, closest+1, k, pTrunk->pgno, n-1)); - rc = sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ) goto end_allocate_page; -@@ -70666,7 +78271,7 @@ static int allocateBtreePage( - ** becomes a new pointer-map page, the second is used by the caller. - */ - MemPage *pPg = 0; -- TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); -+ TRACE(("ALLOCATE: %u from end of file (pointer-map page)\n", pBt->nPage)); - assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent); - if( rc==SQLITE_OK ){ -@@ -70689,7 +78294,7 @@ static int allocateBtreePage( - releasePage(*ppPage); - *ppPage = 0; - } -- TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); -+ TRACE(("ALLOCATE: %u from end of file\n", *pPgno)); - } - - assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) ); -@@ -70757,7 +78362,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ - /* If the database supports auto-vacuum, write an entry in the pointer-map - ** to indicate that the page is free. - */ -- if( ISAUTOVACUUM ){ -+ if( ISAUTOVACUUM(pBt) ){ - ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); - if( rc ) goto freepage_out; - } -@@ -70817,7 +78422,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ - } - rc = btreeSetHasContent(pBt, iPage); - } -- TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); -+ TRACE(("FREE-PAGE: %u leaf on trunk page %u\n",pPage->pgno,pTrunk->pgno)); - goto freepage_out; - } - } -@@ -70838,7 +78443,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ - put4byte(pPage->aData, iTrunk); - put4byte(&pPage->aData[4], 0); - put4byte(&pPage1->aData[32], iPage); -- TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); -+ TRACE(("FREE-PAGE: %u new trunk page replacing %u\n", pPage->pgno, iTrunk)); - - freepage_out: - if( pPage ){ -@@ -70855,10 +78460,9 @@ static void freePage(MemPage *pPage, int *pRC){ - } - - /* --** Free any overflow pages associated with the given Cell. Store --** size information about the cell in pInfo. -+** Free the overflow pages associated with the given Cell. - */ --static int clearCell( -+static SQLITE_NOINLINE int clearCellOverflow( - MemPage *pPage, /* The page that contains the Cell */ - unsigned char *pCell, /* First byte of the Cell */ - CellInfo *pInfo /* Size information about the cell */ -@@ -70870,10 +78474,7 @@ static int clearCell( - u32 ovflPageSize; - - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); -- pPage->xParseCell(pPage, pCell, pInfo); -- if( pInfo->nLocal==pInfo->nPayload ){ -- return SQLITE_OK; /* No overflow pages. Return without doing anything */ -- } -+ assert( pInfo->nLocal!=pInfo->nPayload ); - testcase( pCell + pInfo->nSize == pPage->aDataEnd ); - testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd ); - if( pCell + pInfo->nSize > pPage->aDataEnd ){ -@@ -70929,6 +78530,21 @@ static int clearCell( - return SQLITE_OK; - } - -+/* Call xParseCell to compute the size of a cell. If the cell contains -+** overflow, then invoke cellClearOverflow to clear out that overflow. -+** Store the result code (SQLITE_OK or some error code) in rc. -+** -+** Implemented as macro to force inlining for performance. -+*/ -+#define BTREE_CLEAR_CELL(rc, pPage, pCell, sInfo) \ -+ pPage->xParseCell(pPage, pCell, &sInfo); \ -+ if( sInfo.nLocal!=sInfo.nPayload ){ \ -+ rc = clearCellOverflow(pPage, pCell, &sInfo); \ -+ }else{ \ -+ rc = SQLITE_OK; \ -+ } -+ -+ - /* - ** Create the byte sequence used to represent a cell on page pPage - ** and write that byte sequence into pCell[]. Overflow pages are -@@ -70989,7 +78605,10 @@ static int fillInCell( - n = nHeader + nPayload; - testcase( n==3 ); - testcase( n==4 ); -- if( n<4 ) n = 4; -+ if( n<4 ){ -+ n = 4; -+ pPayload[nPayload] = 0; -+ } - *pnSize = n; - assert( nSrc<=nPayload ); - testcase( nSrc=0 && idxnCell ); -+ assert( idx>=0 ); -+ assert( idxnCell ); - assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( pPage->nFree>=0 ); - data = pPage->aData; - ptr = &pPage->aCellIdx[2*idx]; -+ assert( pPage->pBt->usableSize > (u32)(ptr-data) ); - pc = get2byte(ptr); - hdr = pPage->hdrOffset; -- testcase( pc==get2byte(&data[hdr+5]) ); -+ testcase( pc==(u32)get2byte(&data[hdr+5]) ); - testcase( pc+sz==pPage->pBt->usableSize ); - if( pc+sz > pPage->pBt->usableSize ){ - *pRC = SQLITE_CORRUPT_BKPT; -@@ -71185,23 +78806,27 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ - ** Allocating a new entry in pPage->aCell[] implies that - ** pPage->nOverflow is incremented. - ** --** *pRC must be SQLITE_OK when this routine is called. -+** The insertCellFast() routine below works exactly the same as -+** insertCell() except that it lacks the pTemp and iChild parameters -+** which are assumed zero. Other than that, the two routines are the -+** same. -+** -+** Fixes or enhancements to this routine should be reflected in -+** insertCellFast()! - */ --static void insertCell( -+static int insertCell( - MemPage *pPage, /* Page into which we are copying */ - int i, /* New cell becomes the i-th cell of the page */ - u8 *pCell, /* Content of the new cell */ - int sz, /* Bytes of content in pCell */ - u8 *pTemp, /* Temp storage space for pCell, if needed */ -- Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ -- int *pRC /* Read and write return code from here */ -+ Pgno iChild /* If non-zero, replace first 4 bytes with this value */ - ){ - int idx = 0; /* Where to write new cell content in data[] */ - int j; /* Loop counter */ - u8 *data; /* The content of the whole page */ - u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ - -- assert( *pRC==SQLITE_OK ); - assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); - assert( MX_CELL(pPage->pBt)<=10921 ); - assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); -@@ -71210,14 +78835,103 @@ static void insertCell( - assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); - assert( pPage->nFree>=0 ); -+ assert( iChild>0 ); - if( pPage->nOverflow || sz+2>pPage->nFree ){ - if( pTemp ){ - memcpy(pTemp, pCell, sz); - pCell = pTemp; - } -- if( iChild ){ -- put4byte(pCell, iChild); -+ put4byte(pCell, iChild); -+ j = pPage->nOverflow++; -+ /* Comparison against ArraySize-1 since we hold back one extra slot -+ ** as a contingency. In other words, never need more than 3 overflow -+ ** slots but 4 are allocated, just to be safe. */ -+ assert( j < ArraySize(pPage->apOvfl)-1 ); -+ pPage->apOvfl[j] = pCell; -+ pPage->aiOvfl[j] = (u16)i; -+ -+ /* When multiple overflows occur, they are always sequential and in -+ ** sorted order. This invariants arise because multiple overflows can -+ ** only occur when inserting divider cells into the parent page during -+ ** balancing, and the dividers are adjacent and sorted. -+ */ -+ assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */ -+ assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */ -+ }else{ -+ int rc = sqlite3PagerWrite(pPage->pDbPage); -+ if( NEVER(rc!=SQLITE_OK) ){ -+ return rc; -+ } -+ assert( sqlite3PagerIswriteable(pPage->pDbPage) ); -+ data = pPage->aData; -+ assert( &data[pPage->cellOffset]==pPage->aCellIdx ); -+ rc = allocateSpace(pPage, sz, &idx); -+ if( rc ){ return rc; } -+ /* The allocateSpace() routine guarantees the following properties -+ ** if it returns successfully */ -+ assert( idx >= 0 ); -+ assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); -+ assert( idx+sz <= (int)pPage->pBt->usableSize ); -+ pPage->nFree -= (u16)(2 + sz); -+ /* In a corrupt database where an entry in the cell index section of -+ ** a btree page has a value of 3 or less, the pCell value might point -+ ** as many as 4 bytes in front of the start of the aData buffer for -+ ** the source page. Make sure this does not cause problems by not -+ ** reading the first 4 bytes */ -+ memcpy(&data[idx+4], pCell+4, sz-4); -+ put4byte(&data[idx], iChild); -+ pIns = pPage->aCellIdx + i*2; -+ memmove(pIns+2, pIns, 2*(pPage->nCell - i)); -+ put2byte(pIns, idx); -+ pPage->nCell++; -+ /* increment the cell count */ -+ if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++; -+ assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); -+#ifndef SQLITE_OMIT_AUTOVACUUM -+ if( pPage->pBt->autoVacuum ){ -+ int rc2 = SQLITE_OK; -+ /* The cell may contain a pointer to an overflow page. If so, write -+ ** the entry for the overflow page into the pointer map. -+ */ -+ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2); -+ if( rc2 ) return rc2; - } -+#endif -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** This variant of insertCell() assumes that the pTemp and iChild -+** parameters are both zero. Use this variant in sqlite3BtreeInsert() -+** for performance improvement, and also so that this variant is only -+** called from that one place, and is thus inlined, and thus runs must -+** faster. -+** -+** Fixes or enhancements to this routine should be reflected into -+** the insertCell() routine. -+*/ -+static int insertCellFast( -+ MemPage *pPage, /* Page into which we are copying */ -+ int i, /* New cell becomes the i-th cell of the page */ -+ u8 *pCell, /* Content of the new cell */ -+ int sz /* Bytes of content in pCell */ -+){ -+ int idx = 0; /* Where to write new cell content in data[] */ -+ int j; /* Loop counter */ -+ u8 *data; /* The content of the whole page */ -+ u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ -+ -+ assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); -+ assert( MX_CELL(pPage->pBt)<=10921 ); -+ assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); -+ assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) ); -+ assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) ); -+ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); -+ assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); -+ assert( pPage->nFree>=0 ); -+ assert( pPage->nOverflow==0 ); -+ if( sz+2>pPage->nFree ){ - j = pPage->nOverflow++; - /* Comparison against ArraySize-1 since we hold back one extra slot - ** as a contingency. In other words, never need more than 3 overflow -@@ -71236,31 +78950,20 @@ static void insertCell( - }else{ - int rc = sqlite3PagerWrite(pPage->pDbPage); - if( rc!=SQLITE_OK ){ -- *pRC = rc; -- return; -+ return rc; - } - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - data = pPage->aData; - assert( &data[pPage->cellOffset]==pPage->aCellIdx ); - rc = allocateSpace(pPage, sz, &idx); -- if( rc ){ *pRC = rc; return; } -+ if( rc ){ return rc; } - /* The allocateSpace() routine guarantees the following properties - ** if it returns successfully */ - assert( idx >= 0 ); - assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); - assert( idx+sz <= (int)pPage->pBt->usableSize ); - pPage->nFree -= (u16)(2 + sz); -- if( iChild ){ -- /* In a corrupt database where an entry in the cell index section of -- ** a btree page has a value of 3 or less, the pCell value might point -- ** as many as 4 bytes in front of the start of the aData buffer for -- ** the source page. Make sure this does not cause problems by not -- ** reading the first 4 bytes */ -- memcpy(&data[idx+4], pCell+4, sz-4); -- put4byte(&data[idx], iChild); -- }else{ -- memcpy(&data[idx], pCell, sz); -- } -+ memcpy(&data[idx], pCell, sz); - pIns = pPage->aCellIdx + i*2; - memmove(pIns+2, pIns, 2*(pPage->nCell - i)); - put2byte(pIns, idx); -@@ -71270,13 +78973,16 @@ static void insertCell( - assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); - #ifndef SQLITE_OMIT_AUTOVACUUM - if( pPage->pBt->autoVacuum ){ -+ int rc2 = SQLITE_OK; - /* The cell may contain a pointer to an overflow page. If so, write - ** the entry for the overflow page into the pointer map. - */ -- ptrmapPutOvflPtr(pPage, pPage, pCell, pRC); -+ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2); -+ if( rc2 ) return rc2; - } - #endif - } -+ return SQLITE_OK; - } - - /* -@@ -71377,14 +79083,16 @@ struct CellArray { - ** computed. - */ - static void populateCellCache(CellArray *p, int idx, int N){ -+ MemPage *pRef = p->pRef; -+ u16 *szCell = p->szCell; - assert( idx>=0 && idx+N<=p->nCell ); - while( N>0 ){ - assert( p->apCell[idx]!=0 ); -- if( p->szCell[idx]==0 ){ -- p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]); -+ if( szCell[idx]==0 ){ -+ szCell[idx] = pRef->xCellSize(pRef, p->apCell[idx]); - }else{ - assert( CORRUPT_DB || -- p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) ); -+ szCell[idx]==pRef->xCellSize(pRef, p->apCell[idx]) ); - } - idx++; - N--; -@@ -71438,12 +79146,14 @@ static int rebuildPage( - int k; /* Current slot in pCArray->apEnd[] */ - u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ - -+ assert( nCell>0 ); - assert( i(u32)usableSize) ){ j = 0; } -+ if( j>(u32)usableSize ){ j = 0; } - memcpy(&pTmp[j], &aData[j], usableSize - j); - -- for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kixNx[NB*2-1]>i ); -+ for(k=0; pCArray->ixNx[k]<=i; k++){} - pSrcEnd = pCArray->apEnd[k]; - - pData = pEnd; -@@ -71451,7 +79161,7 @@ static int rebuildPage( - u8 *pCell = pCArray->apCell[i]; - u16 sz = pCArray->szCell[i]; - assert( sz>0 ); -- if( SQLITE_WITHIN(pCell,aData,pEnd) ){ -+ if( SQLITE_WITHIN(pCell,aData+j,pEnd) ){ - if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT; - pCell = &pTmp[pCell - aData]; - }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd -@@ -71464,9 +79174,8 @@ static int rebuildPage( - put2byte(pCellptr, (pData - aData)); - pCellptr += 2; - if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT; -- memcpy(pData, pCell, sz); -+ memmove(pData, pCell, sz); - assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); -- testcase( sz!=pPg->xCellSize(pPg,pCell) ) - i++; - if( i>=iEnd ) break; - if( pCArray->ixNx[k]<=i ){ -@@ -71476,7 +79185,8 @@ static int rebuildPage( - } - - /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ -- pPg->nCell = nCell; -+ assert( nCell < 10922 ); -+ pPg->nCell = (u16)nCell; - pPg->nOverflow = 0; - - put2byte(&aData[hdr+1], 0); -@@ -71507,7 +79217,7 @@ static int rebuildPage( - ** Finally, argument pBegin points to the byte immediately following the - ** end of the space required by this page for the cell-pointer area (for - ** all cells - not just those inserted by the current call). If the content --** area must be extended to before this point in order to accomodate all -+** area must be extended to before this point in order to accommodate all - ** cells in apCell[], then the cells do not fit and non-zero is returned. - */ - static int pageInsertArray( -@@ -71527,7 +79237,8 @@ static int pageInsertArray( - u8 *pEnd; /* Maximum extent of cell data */ - assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ - if( iEnd<=iFirst ) return 0; -- for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kixNx[NB*2-1]>i ); -+ for(k=0; pCArray->ixNx[k]<=i ; k++){} - pEnd = pCArray->apEnd[k]; - while( 1 /*Exit by break*/ ){ - int sz, rc; -@@ -71585,37 +79296,50 @@ static int pageFreeArray( - u8 * const pEnd = &aData[pPg->pBt->usableSize]; - u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize]; - int nRet = 0; -- int i; -+ int i, j; - int iEnd = iFirst + nCell; -- u8 *pFree = 0; -- int szFree = 0; -+ int nFree = 0; -+ int aOfst[10]; -+ int aAfter[10]; - - for(i=iFirst; iapCell[i]; - if( SQLITE_WITHIN(pCell, pStart, pEnd) ){ - int sz; -+ int iAfter; -+ int iOfst; - /* No need to use cachedCellSize() here. The sizes of all cells that - ** are to be freed have already been computing while deciding which - ** cells need freeing */ - sz = pCArray->szCell[i]; assert( sz>0 ); -- if( pFree!=(pCell + sz) ){ -- if( pFree ){ -- assert( pFree>aData && (pFree - aData)<65536 ); -- freeSpace(pPg, (u16)(pFree - aData), szFree); -+ iOfst = (u16)(pCell - aData); -+ iAfter = iOfst+sz; -+ for(j=0; jpEnd ) return 0; -- }else{ -- pFree = pCell; -- szFree += sz; -+ } -+ if( j>=nFree ){ -+ if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){ -+ for(j=0; jpEnd ) return 0; -+ nFree++; - } - nRet++; - } - } -- if( pFree ){ -- assert( pFree>aData && (pFree - aData)<65536 ); -- freeSpace(pPg, (u16)(pFree - aData), szFree); -+ for(j=0; jpPg->aDataEnd) ) goto editpage_fail; - - /* Add cells to the start of the page */ - if( iNewnCell = nNew; -+ assert( nNew < 10922 ); -+ pPg->nCell = (u16)nNew; - pPg->nOverflow = 0; - - put2byte(&aData[hdr+3], pPg->nCell); -@@ -71731,6 +79460,7 @@ static int editPage( - return SQLITE_OK; - editpage_fail: - /* Unable to edit this page. Rebuild it from scratch instead. */ -+ if( nNew<1 ) return SQLITE_CORRUPT_BKPT; - populateCellCache(pCArray, iNew, nNew); - return rebuildPage(pCArray, iNew, nNew, pPg); - } -@@ -71797,6 +79527,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ - b.szCell = &szCell; - b.apEnd[0] = pPage->aDataEnd; - b.ixNx[0] = 2; -+ b.ixNx[NB*2-1] = 0x7fffffff; - rc = rebuildPage(&b, 0, 1, pNew); - if( NEVER(rc) ){ - releasePage(pNew); -@@ -71808,12 +79539,12 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ - ** with entries for the new page, and any pointer from the - ** cell on the page to an overflow page. If either of these - ** operations fails, the return code is set, but the contents -- ** of the parent page are still manipulated by thh code below. -+ ** of the parent page are still manipulated by the code below. - ** That is Ok, at this point the parent page is guaranteed to - ** be marked as dirty. Returning an error code will cause a - ** rollback, undoing any changes made to the parent page. - */ -- if( ISAUTOVACUUM ){ -+ if( ISAUTOVACUUM(pBt) ){ - ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); - if( szCell>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pNew, pCell, &rc); -@@ -71841,8 +79572,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ - - /* Insert the new divider cell into pParent. */ - if( rc==SQLITE_OK ){ -- insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), -- 0, pPage->pgno, &rc); -+ rc = insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), -+ 0, pPage->pgno); - } - - /* Set the right-child pointer of pParent to point to the new page. */ -@@ -71951,7 +79682,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ - /* If this is an auto-vacuum database, update the pointer-map entries - ** for any b-tree or overflow pages that pTo now contains the pointers to. - */ -- if( ISAUTOVACUUM ){ -+ if( ISAUTOVACUUM(pBt) ){ - *pRC = setChildPtrmaps(pTo); - } - } -@@ -72017,7 +79748,7 @@ static int balance_nonroot( - int pageFlags; /* Value of pPage->aData[0] */ - int iSpace1 = 0; /* First unused byte of aSpace1[] */ - int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ -- int szScratch; /* Size of scratch memory requested */ -+ u64 szScratch; /* Size of scratch memory requested */ - MemPage *apOld[NB]; /* pPage and up to two siblings */ - MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ - u8 *pRight; /* Location in parent of right-sibling pointer */ -@@ -72029,13 +79760,12 @@ static int balance_nonroot( - Pgno pgno; /* Temp var to store a page number in */ - u8 abDone[NB+2]; /* True after i'th new page is populated */ - Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */ -- Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */ -- u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */ -- CellArray b; /* Parsed information on cells being balanced */ -+ CellArray b; /* Parsed information on cells being balanced */ - - memset(abDone, 0, sizeof(abDone)); -- b.nCell = 0; -- b.apCell = 0; -+ assert( sizeof(b) - sizeof(b.ixNx) == offsetof(CellArray,ixNx) ); -+ memset(&b, 0, sizeof(b)-sizeof(b.ixNx[0])); -+ b.ixNx[NB*2-1] = 0x7fffffff; - pBt = pParent->pBt; - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( sqlite3PagerIswriteable(pParent->pDbPage) ); -@@ -72086,7 +79816,9 @@ static int balance_nonroot( - } - pgno = get4byte(pRight); - while( 1 ){ -- rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0); -+ if( rc==SQLITE_OK ){ -+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0); -+ } - if( rc ){ - memset(apOld, 0, (i+1)*sizeof(MemPage*)); - goto balance_cleanup; -@@ -72098,6 +79830,7 @@ static int balance_nonroot( - goto balance_cleanup; - } - } -+ nMaxCells += apOld[i]->nCell + ArraySize(pParent->apOvfl); - if( (i--)==0 ) break; - - if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ -@@ -72125,12 +79858,10 @@ static int balance_nonroot( - if( pBt->btsFlags & BTS_FAST_SECURE ){ - int iOff; - -+ /* If the following if() condition is not true, the db is corrupted. -+ ** The call to dropCell() below will detect this. */ - iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); -- if( (iOff+szNew[i])>(int)pBt->usableSize ){ -- rc = SQLITE_CORRUPT_BKPT; -- memset(apOld, 0, (i+1)*sizeof(MemPage*)); -- goto balance_cleanup; -- }else{ -+ if( (iOff+szNew[i])<=(int)pBt->usableSize ){ - memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]); - apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; - } -@@ -72141,7 +79872,6 @@ static int balance_nonroot( - - /* Make nMaxCells a multiple of 4 in order to preserve 8-byte - ** alignment */ -- nMaxCells = nOld*(MX_CELL(pBt) + ArraySize(pParent->apOvfl)); - nMaxCells = (nMaxCells + 3)&~3; - - /* -@@ -72194,7 +79924,7 @@ static int balance_nonroot( - ** table-interior, index-leaf, or index-interior). - */ - if( pOld->aData[0]!=apOld[0]->aData[0] ){ -- rc = SQLITE_CORRUPT_BKPT; -+ rc = SQLITE_CORRUPT_PAGE(pOld); - goto balance_cleanup; - } - -@@ -72218,7 +79948,7 @@ static int balance_nonroot( - memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); - if( pOld->nOverflow>0 ){ - if( NEVER(limitaiOvfl[0]) ){ -- rc = SQLITE_CORRUPT_BKPT; -+ rc = SQLITE_CORRUPT_PAGE(pOld); - goto balance_cleanup; - } - limit = pOld->aiOvfl[0]; -@@ -72258,7 +79988,7 @@ static int balance_nonroot( - b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection; - if( !pOld->leaf ){ - assert( leafCorrection==0 ); -- assert( pOld->hdrOffset==0 ); -+ assert( pOld->hdrOffset==0 || CORRUPT_DB ); - /* The right pointer of the child page pOld becomes the left - ** pointer of the divider cell */ - memcpy(b.apCell[b.nCell], &pOld->aData[8], 4); -@@ -72378,15 +80108,17 @@ static int balance_nonroot( - d = r + 1 - leafData; - (void)cachedCellSize(&b, d); - do{ -+ int szR, szD; - assert( d szLeft-(b.szCell[r]+(i==k-1?0:2)))){ -+ && (bBulk || szRight+szD+2 > szLeft-(szR+(i==k-1?0:2)))){ - break; - } -- szRight += b.szCell[d] + 2; -- szLeft -= b.szCell[r] + 2; -+ szRight += szD + 2; -+ szLeft -= szR + 2; - cntNew[i-1] = r; - r--; - d--; -@@ -72399,7 +80131,7 @@ static int balance_nonroot( - } - } - -- /* Sanity check: For a non-corrupt database file one of the follwing -+ /* Sanity check: For a non-corrupt database file one of the following - ** must be true: - ** (1) We found one or more cells (cntNew[0])>0), or - ** (2) pPage is a virtual root page. A virtual root page is when -@@ -72407,7 +80139,7 @@ static int balance_nonroot( - ** that page. - */ - assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB); -- TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n", -+ TRACE(("BALANCE: old: %u(nc=%u) %u(nc=%u) %u(nc=%u)\n", - apOld[0]->pgno, apOld[0]->nCell, - nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0, - nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0 -@@ -72424,6 +80156,11 @@ static int balance_nonroot( - apOld[i] = 0; - rc = sqlite3PagerWrite(pNew->pDbPage); - nNew++; -+ if( sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) -+ && rc==SQLITE_OK -+ ){ -+ rc = SQLITE_CORRUPT_BKPT; -+ } - if( rc ) goto balance_cleanup; - }else{ - assert( i>0 ); -@@ -72435,7 +80172,7 @@ static int balance_nonroot( - cntOld[i] = b.nCell; - - /* Set the pointer-map entry for the new sibling page. */ -- if( ISAUTOVACUUM ){ -+ if( ISAUTOVACUUM(pBt) ){ - ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); - if( rc!=SQLITE_OK ){ - goto balance_cleanup; -@@ -72450,47 +80187,44 @@ static int balance_nonroot( - ** of the table is closer to a linear scan through the file. That in turn - ** helps the operating system to deliver pages from the disk more rapidly. - ** -- ** An O(n^2) insertion sort algorithm is used, but since n is never more -- ** than (NB+2) (a small constant), that should not be a problem. -+ ** An O(N*N) sort algorithm is used, but since N is never more than NB+2 -+ ** (5), that is not a performance concern. - ** - ** When NB==3, this one optimization makes the database about 25% faster - ** for large insertions and deletions. - */ - for(i=0; ipgno; -- aPgFlags[i] = apNew[i]->pDbPage->flags; -- for(j=0; jpgno; -+ assert( apNew[i]->pDbPage->flags & PGHDR_WRITEABLE ); -+ assert( apNew[i]->pDbPage->flags & PGHDR_DIRTY ); - } -- for(i=0; ii ){ -- sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0); -- } -- sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]); -- apNew[i]->pgno = pgno; -+ for(i=0; ipgno < apNew[iB]->pgno ) iB = j; - } -- } - -- TRACE(("BALANCE: new: %d(%d nc=%d) %d(%d nc=%d) %d(%d nc=%d) " -- "%d(%d nc=%d) %d(%d nc=%d)\n", -+ /* If apNew[i] has a page number that is bigger than any of the -+ ** subsequence apNew[i] entries, then swap apNew[i] with the subsequent -+ ** entry that has the smallest page number (which we know to be -+ ** entry apNew[iB]). -+ */ -+ if( iB!=i ){ -+ Pgno pgnoA = apNew[i]->pgno; -+ Pgno pgnoB = apNew[iB]->pgno; -+ Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1; -+ u16 fgA = apNew[i]->pDbPage->flags; -+ u16 fgB = apNew[iB]->pDbPage->flags; -+ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB); -+ sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA); -+ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB); -+ apNew[i]->pgno = pgnoB; -+ apNew[iB]->pgno = pgnoA; -+ } -+ } -+ -+ TRACE(("BALANCE: new: %u(%u nc=%u) %u(%u nc=%u) %u(%u nc=%u) " -+ "%u(%u nc=%u) %u(%u nc=%u)\n", - apNew[0]->pgno, szNew[0], cntNew[0], - nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0, - nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0, -@@ -72531,7 +80265,7 @@ static int balance_nonroot( - ** updated. This happens below, after the sibling pages have been - ** populated, not here. - */ -- if( ISAUTOVACUUM ){ -+ if( ISAUTOVACUUM(pBt) ){ - MemPage *pOld; - MemPage *pNew = pOld = apNew[0]; - int cntOldNext = pNew->nCell + pNew->nOverflow; -@@ -72578,6 +80312,7 @@ static int balance_nonroot( - u8 *pCell; - u8 *pTemp; - int sz; -+ u8 *pSrcEnd; - MemPage *pNew = apNew[i]; - j = cntNew[i]; - -@@ -72621,7 +80356,14 @@ static int balance_nonroot( - iOvflSpace += sz; - assert( sz<=pBt->maxLocal+23 ); - assert( iOvflSpace <= (int)pBt->pageSize ); -- insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc); -+ assert( b.ixNx[NB*2-1]>j ); -+ for(k=0; b.ixNx[k]<=j; k++){} -+ pSrcEnd = b.apEnd[k]; -+ if( SQLITE_OVERFLOW(pSrcEnd, pCell, pCell+sz) ){ -+ rc = SQLITE_CORRUPT_BKPT; -+ goto balance_cleanup; -+ } -+ rc = insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno); - if( rc!=SQLITE_OK ) goto balance_cleanup; - assert( sqlite3PagerIswriteable(pParent->pDbPage) ); - } -@@ -72651,6 +80393,8 @@ static int balance_nonroot( - for(i=1-nNew; i=0 && iPg=1 || i>=0 ); -+ assert( iPg=0 /* On the upwards pass, or... */ - || cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */ -@@ -72717,7 +80461,7 @@ static int balance_nonroot( - ); - copyNodeContent(apNew[0], pParent, &rc); - freePage(apNew[0], &rc); -- }else if( ISAUTOVACUUM && !leafCorrection ){ -+ }else if( ISAUTOVACUUM(pBt) && !leafCorrection ){ - /* Fix the pointer map entries associated with the right-child of each - ** sibling page. All other pointer map entries have already been taken - ** care of. */ -@@ -72728,7 +80472,7 @@ static int balance_nonroot( - } - - assert( pParent->isInit ); -- TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", -+ TRACE(("BALANCE: finished: old=%u new=%u cells=%u\n", - nOld, nNew, b.nCell)); - - /* Free any old pages that were not reused as new pages. -@@ -72738,7 +80482,7 @@ static int balance_nonroot( - } - - #if 0 -- if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){ -+ if( ISAUTOVACUUM(pBt) && rc==SQLITE_OK && apNew[0]->isInit ){ - /* The ptrmapCheckPages() contains assert() statements that verify that - ** all pointer map pages are set correctly. This is helpful while - ** debugging. This is usually disabled because a corrupt database may -@@ -72800,7 +80544,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ - if( rc==SQLITE_OK ){ - rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); - copyNodeContent(pRoot, pChild, &rc); -- if( ISAUTOVACUUM ){ -+ if( ISAUTOVACUUM(pBt) ){ - ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); - } - } -@@ -72813,7 +80557,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ - assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); - assert( pChild->nCell==pRoot->nCell || CORRUPT_DB ); - -- TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); -+ TRACE(("BALANCE: copy root %u into %u\n", pRoot->pgno, pChild->pgno)); - - /* Copy the overflow cells from pRoot to pChild */ - memcpy(pChild->aiOvfl, pRoot->aiOvfl, -@@ -72834,7 +80578,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ - ** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid - ** on the same B-tree as pCur. - ** --** This can if a database is corrupt with two or more SQL tables -+** This can occur if a database is corrupt with two or more SQL tables - ** pointing to the same b-tree. If an insert occurs on one SQL table - ** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL - ** table linked to the same b-tree. If the secondary insert causes a -@@ -72848,7 +80592,7 @@ static int anotherValidCursor(BtCursor *pCur){ - && pOther->eState==CURSOR_VALID - && pOther->pPage==pCur->pPage - ){ -- return SQLITE_CORRUPT_BKPT; -+ return SQLITE_CORRUPT_PAGE(pCur->pPage); - } - } - return SQLITE_OK; -@@ -72866,7 +80610,6 @@ static int anotherValidCursor(BtCursor *pCur){ - */ - static int balance(BtCursor *pCur){ - int rc = SQLITE_OK; -- const int nMin = pCur->pBt->usableSize * 2 / 3; - u8 aBalanceQuickSpace[13]; - u8 *pFree = 0; - -@@ -72878,7 +80621,11 @@ static int balance(BtCursor *pCur){ - MemPage *pPage = pCur->pPage; - - if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break; -- if( pPage->nOverflow==0 && pPage->nFree<=nMin ){ -+ if( pPage->nOverflow==0 && pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ -+ /* No rebalance required as long as: -+ ** (1) There are no overflow cells -+ ** (2) The amount of free space on the page is less than 2/3rds of -+ ** the total usable space on the page. */ - break; - }else if( (iPage = pCur->iPage)==0 ){ - if( pPage->nOverflow && (rc = anotherValidCursor(pCur))==SQLITE_OK ){ -@@ -72901,6 +80648,11 @@ static int balance(BtCursor *pCur){ - }else{ - break; - } -+ }else if( sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){ -+ /* The page being written is not a root page, and there is currently -+ ** more than one reference to it. This only happens if the page is one -+ ** of its own ancestor pages. Corruption. */ -+ rc = SQLITE_CORRUPT_PAGE(pPage); - }else{ - MemPage * const pParent = pCur->apPage[iPage-1]; - int const iIdx = pCur->aiIdx[iPage-1]; -@@ -72999,7 +80751,7 @@ static int btreeOverwriteContent( - ){ - int nData = pX->nData - iOffset; - if( nData<=0 ){ -- /* Overwritting with zeros */ -+ /* Overwriting with zeros */ - int i; - for(i=0; ipData to write */ - int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ - int rc; /* Return code */ -@@ -73042,16 +80798,12 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ - Pgno ovflPgno; /* Next overflow page to write */ - u32 ovflPageSize; /* Size to write on overflow page */ - -- if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd -- || pCur->info.pPayload < pPage->aData + pPage->cellOffset -- ){ -- return SQLITE_CORRUPT_BKPT; -- } -+ assert( pCur->info.nLocalinfo.pPayload, pX, - 0, pCur->info.nLocal); - if( rc ) return rc; -- if( pCur->info.nLocal==nTotal ) return SQLITE_OK; - - /* Now overwrite the overflow pages */ - iOffset = pCur->info.nLocal; -@@ -73063,8 +80815,8 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ - do{ - rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); - if( rc ) return rc; -- if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){ -- rc = SQLITE_CORRUPT_BKPT; -+ if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ -+ rc = SQLITE_CORRUPT_PAGE(pPage); - }else{ - if( iOffset+ovflPageSize<(u32)nTotal ){ - ovflPgno = get4byte(pPage->aData); -@@ -73081,6 +80833,29 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ - return SQLITE_OK; - } - -+/* -+** Overwrite the cell that cursor pCur is pointing to with fresh content -+** contained in pX. -+*/ -+static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ -+ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */ -+ MemPage *pPage = pCur->pPage; /* Page being written */ -+ -+ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd -+ || pCur->info.pPayload < pPage->aData + pPage->cellOffset -+ ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } -+ if( pCur->info.nLocal==nTotal ){ -+ /* The entire cell is local */ -+ return btreeOverwriteContent(pPage, pCur->info.pPayload, pX, -+ 0, pCur->info.nLocal); -+ }else{ -+ /* The cell contains overflow content */ -+ return btreeOverwriteOverflowCell(pCur, pX); -+ } -+} -+ - - /* - ** Insert a new record into the BTree. The content of the new record -@@ -73098,7 +80873,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ - ** pX.pData,nData,nZero fields must be zero. - ** - ** If the seekResult parameter is non-zero, then a successful call to --** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already -+** sqlite3BtreeIndexMoveto() to seek cursor pCur to (pKey,nKey) has already - ** been performed. In other words, if seekResult!=0 then the cursor - ** is currently pointing to a cell that will be adjacent to the cell - ** to be inserted. If seekResult<0 then pCur points to a cell that is -@@ -73116,7 +80891,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - BtCursor *pCur, /* Insert data into the table of this cursor */ - const BtreePayload *pX, /* Content of the row to be inserted */ - int flags, /* True if this is likely an append */ -- int seekResult /* Result of prior MovetoUnpacked() call */ -+ int seekResult /* Result of prior IndexMoveto() call */ - ){ - int rc; - int loc = seekResult; /* -1: before desired location +1: after */ -@@ -73124,29 +80899,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - int idx; - MemPage *pPage; - Btree *p = pCur->pBtree; -- BtShared *pBt = p->pBt; - unsigned char *oldCell; - unsigned char *newCell = 0; - -- assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags ); -- -- if( pCur->eState==CURSOR_FAULT ){ -- assert( pCur->skipNext!=SQLITE_OK ); -- return pCur->skipNext; -- } -- -- assert( cursorOwnsBtShared(pCur) ); -- assert( (pCur->curFlags & BTCF_WriteFlag)!=0 -- && pBt->inTransaction==TRANS_WRITE -- && (pBt->btsFlags & BTS_READ_ONLY)==0 ); -- assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); -- -- /* Assert that the caller has been consistent. If this cursor was opened -- ** expecting an index b-tree, then the caller should be inserting blob -- ** keys with no associated data. If the cursor was opened expecting an -- ** intkey table, the caller should be inserting integer keys with a -- ** blob of associated data. */ -- assert( (pX->pKey==0)==(pCur->pKeyInfo==0) ); -+ assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags ); -+ assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 ); - - /* Save the positions of any other cursors open on this table. - ** -@@ -73160,15 +80917,48 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - ** not to clear the cursor here. - */ - if( pCur->curFlags & BTCF_Multiple ){ -- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); -+ rc = saveAllCursors(p->pBt, pCur->pgnoRoot, pCur); - if( rc ) return rc; -+ if( loc && pCur->iPage<0 ){ -+ /* This can only happen if the schema is corrupt such that there is more -+ ** than one table or index with the same root page as used by the cursor. -+ ** Which can only happen if the SQLITE_NoSchemaError flag was set when -+ ** the schema was loaded. This cannot be asserted though, as a user might -+ ** set the flag, load the schema, and then unset the flag. */ -+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); -+ } - } - -+ /* Ensure that the cursor is not in the CURSOR_FAULT state and that it -+ ** points to a valid cell. -+ */ -+ if( pCur->eState>=CURSOR_REQUIRESEEK ){ -+ testcase( pCur->eState==CURSOR_REQUIRESEEK ); -+ testcase( pCur->eState==CURSOR_FAULT ); -+ rc = moveToRoot(pCur); -+ if( rc && rc!=SQLITE_EMPTY ) return rc; -+ } -+ -+ assert( cursorOwnsBtShared(pCur) ); -+ assert( (pCur->curFlags & BTCF_WriteFlag)!=0 -+ && p->pBt->inTransaction==TRANS_WRITE -+ && (p->pBt->btsFlags & BTS_READ_ONLY)==0 ); -+ assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); -+ -+ /* Assert that the caller has been consistent. If this cursor was opened -+ ** expecting an index b-tree, then the caller should be inserting blob -+ ** keys with no associated data. If the cursor was opened expecting an -+ ** intkey table, the caller should be inserting integer keys with a -+ ** blob of associated data. */ -+ assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) ); -+ - if( pCur->pKeyInfo==0 ){ - assert( pX->pKey==0 ); - /* If this is an insert into a table b-tree, invalidate any incrblob - ** cursors open on the row being replaced */ -- invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); -+ if( p->hasIncrblobCur ){ -+ invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0); -+ } - - /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing - ** to a row with the same key as the new entry being inserted. -@@ -73201,7 +80991,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - ** to an adjacent cell. Move the cursor so that it is pointing either - ** to the cell to be overwritten or an adjacent cell. - */ -- rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc); -+ rc = sqlite3BtreeTableMoveto(pCur, pX->nKey, -+ (flags & BTREE_APPEND)!=0, &loc); - if( rc ) return rc; - } - }else{ -@@ -73224,13 +81015,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - r.aMem = pX->aMem; - r.nField = pX->nMem; - r.default_rc = 0; -- r.errCode = 0; -- r.r1 = 0; -- r.r2 = 0; - r.eqSeen = 0; -- rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc); -+ rc = sqlite3BtreeIndexMoveto(pCur, &r, &loc); - }else{ -- rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc); -+ rc = btreeMoveto(pCur, pX->pKey, pX->nKey, -+ (flags & BTREE_APPEND)!=0, &loc); - } - if( rc ) return rc; - } -@@ -73244,43 +81033,65 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - if( pCur->info.nKey==pX->nKey ){ - BtreePayload x2; - x2.pData = pX->pKey; -- x2.nData = pX->nKey; -+ x2.nData = (int)pX->nKey; assert( pX->nKey<=0x7fffffff ); - x2.nZero = 0; - return btreeOverwriteCell(pCur, &x2); - } - } -- - } - assert( pCur->eState==CURSOR_VALID -- || (pCur->eState==CURSOR_INVALID && loc) -- || CORRUPT_DB ); -+ || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB ); - - pPage = pCur->pPage; -- assert( pPage->intKey || pX->nKey>=0 ); -+ assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); - assert( pPage->leaf || !pPage->intKey ); - if( pPage->nFree<0 ){ -- if( pCur->eState>CURSOR_INVALID ){ -- rc = SQLITE_CORRUPT_BKPT; -+ if( NEVER(pCur->eState>CURSOR_INVALID) ){ -+ /* ^^^^^--- due to the moveToRoot() call above */ -+ rc = SQLITE_CORRUPT_PAGE(pPage); - }else{ - rc = btreeComputeFreeSpace(pPage); - } - if( rc ) return rc; - } - -- TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", -+ TRACE(("INSERT: table=%u nkey=%lld ndata=%u page=%u %s\n", - pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, - loc==0 ? "overwrite" : "new entry")); -- assert( pPage->isInit ); -- newCell = pBt->pTmpSpace; -+ assert( pPage->isInit || CORRUPT_DB ); -+ newCell = p->pBt->pTmpSpace; - assert( newCell!=0 ); -- rc = fillInCell(pPage, newCell, pX, &szNew); -- if( rc ) goto end_insert; -+ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); -+ if( flags & BTREE_PREFORMAT ){ -+ rc = SQLITE_OK; -+ szNew = p->pBt->nPreformatSize; -+ if( szNew<4 ){ -+ szNew = 4; -+ newCell[3] = 0; -+ } -+ if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){ -+ CellInfo info; -+ pPage->xParseCell(pPage, newCell, &info); -+ if( info.nPayload!=info.nLocal ){ -+ Pgno ovfl = get4byte(&newCell[szNew-4]); -+ ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc); -+ if( NEVER(rc) ) goto end_insert; -+ } -+ } -+ }else{ -+ rc = fillInCell(pPage, newCell, pX, &szNew); -+ if( rc ) goto end_insert; -+ } - assert( szNew==pPage->xCellSize(pPage, newCell) ); -- assert( szNew <= MX_CELL_SIZE(pBt) ); -+ assert( szNew <= MX_CELL_SIZE(p->pBt) ); - idx = pCur->ix; -+ pCur->info.nSize = 0; - if( loc==0 ){ - CellInfo info; -- assert( idxnCell ); -+ assert( idx>=0 ); -+ if( idx>=pPage->nCell ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } - rc = sqlite3PagerWrite(pPage->pDbPage); - if( rc ){ - goto end_insert; -@@ -73289,11 +81100,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - if( !pPage->leaf ){ - memcpy(newCell, oldCell, 4); - } -- rc = clearCell(pPage, oldCell, &info); -+ BTREE_CLEAR_CELL(rc, pPage, oldCell, info); - testcase( pCur->curFlags & BTCF_ValidOvfl ); - invalidateOverflowCache(pCur); - if( info.nSize==szNew && info.nLocal==info.nPayload -- && (!ISAUTOVACUUM || szNewminLocal) -+ && (!ISAUTOVACUUM(p->pBt) || szNewminLocal) - ){ - /* Overwrite the old cell with the new if they are the same size. - ** We could also try to do this if the old cell is smaller, then add -@@ -73306,10 +81117,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ - assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ - if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ -- return SQLITE_CORRUPT_BKPT; -+ return SQLITE_CORRUPT_PAGE(pPage); - } - if( oldCell+szNew > pPage->aDataEnd ){ -- return SQLITE_CORRUPT_BKPT; -+ return SQLITE_CORRUPT_PAGE(pPage); - } - memcpy(oldCell, newCell, szNew); - return SQLITE_OK; -@@ -73319,11 +81130,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - }else if( loc<0 && pPage->nCell>0 ){ - assert( pPage->leaf ); - idx = ++pCur->ix; -- pCur->curFlags &= ~BTCF_ValidNKey; -+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); - }else{ - assert( pPage->leaf ); - } -- insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); -+ rc = insertCellFast(pPage, idx, newCell, szNew); - assert( pPage->nOverflow==0 || rc==SQLITE_OK ); - assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); - -@@ -73347,10 +81158,9 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( - ** larger than the largest existing key, it is possible to insert the - ** row without seeking the cursor. This can be a big performance boost. - */ -- pCur->info.nSize = 0; - if( pPage->nOverflow ){ - assert( rc==SQLITE_OK ); -- pCur->curFlags &= ~(BTCF_ValidNKey); -+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); - rc = balance(pCur); - - /* Must make sure nOverflow is reset to zero even if the balance() -@@ -73380,6 +81190,118 @@ end_insert: - return rc; - } - -+/* -+** This function is used as part of copying the current row from cursor -+** pSrc into cursor pDest. If the cursors are open on intkey tables, then -+** parameter iKey is used as the rowid value when the record is copied -+** into pDest. Otherwise, the record is copied verbatim. -+** -+** This function does not actually write the new value to cursor pDest. -+** Instead, it creates and populates any required overflow pages and -+** writes the data for the new cell into the BtShared.pTmpSpace buffer -+** for the destination database. The size of the cell, in bytes, is left -+** in BtShared.nPreformatSize. The caller completes the insertion by -+** calling sqlite3BtreeInsert() with the BTREE_PREFORMAT flag specified. -+** -+** SQLITE_OK is returned if successful, or an SQLite error code otherwise. -+*/ -+SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){ -+ BtShared *pBt = pDest->pBt; -+ u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */ -+ const u8 *aIn; /* Pointer to next input buffer */ -+ u32 nIn; /* Size of input buffer aIn[] */ -+ u32 nRem; /* Bytes of data still to copy */ -+ -+ getCellInfo(pSrc); -+ if( pSrc->info.nPayload<0x80 ){ -+ *(aOut++) = (u8)pSrc->info.nPayload; -+ }else{ -+ aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload); -+ } -+ if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); -+ nIn = pSrc->info.nLocal; -+ aIn = pSrc->info.pPayload; -+ if( aIn+nIn>pSrc->pPage->aDataEnd ){ -+ return SQLITE_CORRUPT_PAGE(pSrc->pPage); -+ } -+ nRem = pSrc->info.nPayload; -+ if( nIn==nRem && nInpPage->maxLocal ){ -+ memcpy(aOut, aIn, nIn); -+ pBt->nPreformatSize = nIn + (int)(aOut - pBt->pTmpSpace); -+ return SQLITE_OK; -+ }else{ -+ int rc = SQLITE_OK; -+ Pager *pSrcPager = pSrc->pBt->pPager; -+ u8 *pPgnoOut = 0; -+ Pgno ovflIn = 0; -+ DbPage *pPageIn = 0; -+ MemPage *pPageOut = 0; -+ u32 nOut; /* Size of output buffer aOut[] */ -+ -+ nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload); -+ pBt->nPreformatSize = (int)nOut + (int)(aOut - pBt->pTmpSpace); -+ if( nOutinfo.nPayload ){ -+ pPgnoOut = &aOut[nOut]; -+ pBt->nPreformatSize += 4; -+ } -+ -+ if( nRem>nIn ){ -+ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ -+ return SQLITE_CORRUPT_PAGE(pSrc->pPage); -+ } -+ ovflIn = get4byte(&pSrc->info.pPayload[nIn]); -+ } -+ -+ do { -+ nRem -= nOut; -+ do{ -+ assert( nOut>0 ); -+ if( nIn>0 ){ -+ int nCopy = MIN(nOut, nIn); -+ memcpy(aOut, aIn, nCopy); -+ nOut -= nCopy; -+ nIn -= nCopy; -+ aOut += nCopy; -+ aIn += nCopy; -+ } -+ if( nOut>0 ){ -+ sqlite3PagerUnref(pPageIn); -+ pPageIn = 0; -+ rc = sqlite3PagerGet(pSrcPager, ovflIn, &pPageIn, PAGER_GET_READONLY); -+ if( rc==SQLITE_OK ){ -+ aIn = (const u8*)sqlite3PagerGetData(pPageIn); -+ ovflIn = get4byte(aIn); -+ aIn += 4; -+ nIn = pSrc->pBt->usableSize - 4; -+ } -+ } -+ }while( rc==SQLITE_OK && nOut>0 ); -+ -+ if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){ -+ Pgno pgnoNew; -+ MemPage *pNew = 0; -+ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); -+ put4byte(pPgnoOut, pgnoNew); -+ if( ISAUTOVACUUM(pBt) && pPageOut ){ -+ ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc); -+ } -+ releasePage(pPageOut); -+ pPageOut = pNew; -+ if( pPageOut ){ -+ pPgnoOut = pPageOut->aData; -+ put4byte(pPgnoOut, 0); -+ aOut = &pPgnoOut[4]; -+ nOut = MIN(pBt->usableSize - 4, nRem); -+ } -+ } -+ }while( nRem>0 && rc==SQLITE_OK ); -+ -+ releasePage(pPageOut); -+ sqlite3PagerUnref(pPageIn); -+ return rc; -+ } -+} -+ - /* - ** Delete the entry that the cursor is pointing to. - ** -@@ -73400,14 +81322,13 @@ end_insert: - SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; -- int rc; /* Return code */ -- MemPage *pPage; /* Page to delete cell from */ -- unsigned char *pCell; /* Pointer to cell to delete */ -- int iCellIdx; /* Index of cell to delete */ -- int iCellDepth; /* Depth of node containing pCell */ -- CellInfo info; /* Size of the cell being deleted */ -- int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ -- u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */ -+ int rc; /* Return code */ -+ MemPage *pPage; /* Page to delete cell from */ -+ unsigned char *pCell; /* Pointer to cell to delete */ -+ int iCellIdx; /* Index of cell to delete */ -+ int iCellDepth; /* Depth of node containing pCell */ -+ CellInfo info; /* Size of the cell being deleted */ -+ u8 bPreserve; /* Keep cursor valid. 2 for CURSOR_SKIPNEXT */ - - assert( cursorOwnsBtShared(pCur) ); - assert( pBt->inTransaction==TRANS_WRITE ); -@@ -73416,30 +81337,52 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); - assert( !hasReadConflicts(p, pCur->pgnoRoot) ); - assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); -- if( pCur->eState==CURSOR_REQUIRESEEK ){ -- rc = btreeRestoreCursorPosition(pCur); -- if( rc ) return rc; -+ if( pCur->eState!=CURSOR_VALID ){ -+ if( pCur->eState>=CURSOR_REQUIRESEEK ){ -+ rc = btreeRestoreCursorPosition(pCur); -+ assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); -+ if( rc || pCur->eState!=CURSOR_VALID ) return rc; -+ }else{ -+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); -+ } - } - assert( pCur->eState==CURSOR_VALID ); - - iCellDepth = pCur->iPage; - iCellIdx = pCur->ix; - pPage = pCur->pPage; -+ if( pPage->nCell<=iCellIdx ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } - pCell = findCell(pPage, iCellIdx); -- if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT; -+ if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } -+ if( pCell<&pPage->aCellIdx[pPage->nCell] ){ -+ return SQLITE_CORRUPT_PAGE(pPage); -+ } - -- /* If the bPreserve flag is set to true, then the cursor position must -+ /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must - ** be preserved following this delete operation. If the current delete - ** will cause a b-tree rebalance, then this is done by saving the cursor - ** key and leaving the cursor in CURSOR_REQUIRESEEK state before - ** returning. - ** -- ** Or, if the current delete will not cause a rebalance, then the cursor -+ ** If the current delete will not cause a rebalance, then the cursor - ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately -- ** before or after the deleted entry. In this case set bSkipnext to true. */ -+ ** before or after the deleted entry. -+ ** -+ ** The bPreserve value records which path is required: -+ ** -+ ** bPreserve==0 Not necessary to save the cursor position -+ ** bPreserve==1 Use CURSOR_REQUIRESEEK to save the cursor position -+ ** bPreserve==2 Cursor won't move. Set CURSOR_SKIPNEXT. -+ */ -+ bPreserve = (flags & BTREE_SAVEPOSITION)!=0; - if( bPreserve ){ - if( !pPage->leaf -- || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) -+ || (pPage->nFree+pPage->xCellSize(pPage,pCell)+2) > -+ (int)(pBt->usableSize*2/3) - || pPage->nCell==1 /* See dbfuzz001.test for a test case */ - ){ - /* A b-tree rebalance will be required after deleting this entry. -@@ -73447,7 +81390,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - rc = saveCursorKey(pCur); - if( rc ) return rc; - }else{ -- bSkipnext = 1; -+ bPreserve = 2; - } - } - -@@ -73473,7 +81416,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - - /* If this is a delete operation to remove a row from a table b-tree, - ** invalidate any incrblob cursors open on the row being deleted. */ -- if( pCur->pKeyInfo==0 ){ -+ if( pCur->pKeyInfo==0 && p->hasIncrblobCur ){ - invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0); - } - -@@ -73482,7 +81425,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - ** itself from within the page. */ - rc = sqlite3PagerWrite(pPage->pDbPage); - if( rc ) return rc; -- rc = clearCell(pPage, pCell, &info); -+ BTREE_CLEAR_CELL(rc, pPage, pCell, info); - dropCell(pPage, iCellIdx, info.nSize, &rc); - if( rc ) return rc; - -@@ -73507,14 +81450,14 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - n = pCur->pPage->pgno; - } - pCell = findCell(pLeaf, pLeaf->nCell-1); -- if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; -+ if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf); - nCell = pLeaf->xCellSize(pLeaf, pCell); - assert( MX_CELL_SIZE(pBt) >= nCell ); - pTmp = pBt->pTmpSpace; - assert( pTmp!=0 ); - rc = sqlite3PagerWrite(pLeaf->pDbPage); - if( rc==SQLITE_OK ){ -- insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); -+ rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n); - } - dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); - if( rc ) return rc; -@@ -73535,7 +81478,15 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - ** been corrected, so be it. Otherwise, after balancing the leaf node, - ** walk the cursor up the tree to the internal node and balance it as - ** well. */ -- rc = balance(pCur); -+ assert( pCur->pPage->nOverflow==0 ); -+ assert( pCur->pPage->nFree>=0 ); -+ if( pCur->pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){ -+ /* Optimization: If the free space is less than 2/3rds of the page, -+ ** then balance() will always be a no-op. No need to invoke it. */ -+ rc = SQLITE_OK; -+ }else{ -+ rc = balance(pCur); -+ } - if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){ - releasePageNotNull(pCur->pPage); - pCur->iPage--; -@@ -73547,8 +81498,8 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ - } - - if( rc==SQLITE_OK ){ -- if( bSkipnext ){ -- assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) ); -+ if( bPreserve>1 ){ -+ assert( (pCur->iPage==iCellDepth || CORRUPT_DB) ); - assert( pPage==pCur->pPage || CORRUPT_DB ); - assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell ); - pCur->eState = CURSOR_SKIPNEXT; -@@ -73586,7 +81537,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ - MemPage *pRoot; - Pgno pgnoRoot; - int rc; -- int ptfFlags; /* Page-type flage for the root page of new table */ -+ int ptfFlags; /* Page-type flags for the root page of new table */ - - assert( sqlite3BtreeHoldsMutex(p) ); - assert( pBt->inTransaction==TRANS_WRITE ); -@@ -73615,7 +81566,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ - */ - sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); - if( pgnoRoot>btreePagecount(pBt) ){ -- return SQLITE_CORRUPT_BKPT; -+ return SQLITE_CORRUPT_PGNO(pgnoRoot); - } - pgnoRoot++; - -@@ -73663,7 +81614,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ - } - rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); - if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ -- rc = SQLITE_CORRUPT_BKPT; -+ rc = SQLITE_CORRUPT_PGNO(pgnoRoot); - } - if( rc!=SQLITE_OK ){ - releasePage(pRoot); -@@ -73742,7 +81693,7 @@ static int clearDatabasePage( - BtShared *pBt, /* The BTree that contains the table */ - Pgno pgno, /* Page number to clear */ - int freePageFlag, /* Deallocate page if true */ -- int *pnChange /* Add number of Cells freed to this counter */ -+ i64 *pnChange /* Add number of Cells freed to this counter */ - ){ - MemPage *pPage; - int rc; -@@ -73753,15 +81704,16 @@ static int clearDatabasePage( - - assert( sqlite3_mutex_held(pBt->mutex) ); - if( pgno>btreePagecount(pBt) ){ -- return SQLITE_CORRUPT_BKPT; -+ return SQLITE_CORRUPT_PGNO(pgno); - } -- rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); -+ rc = getAndInitPage(pBt, pgno, &pPage, 0); - if( rc ) return rc; -- if( pPage->bBusy ){ -- rc = SQLITE_CORRUPT_BKPT; -+ if( (pBt->openFlags & BTREE_SINGLE)==0 -+ && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) -+ ){ -+ rc = SQLITE_CORRUPT_PAGE(pPage); - goto cleardatabasepage_out; - } -- pPage->bBusy = 1; - hdr = pPage->hdrOffset; - for(i=0; inCell; i++){ - pCell = findCell(pPage, i); -@@ -73769,14 +81721,15 @@ static int clearDatabasePage( - rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); - if( rc ) goto cleardatabasepage_out; - } -- rc = clearCell(pPage, pCell, &info); -+ BTREE_CLEAR_CELL(rc, pPage, pCell, info); - if( rc ) goto cleardatabasepage_out; - } - if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); - if( rc ) goto cleardatabasepage_out; -- }else if( pnChange ){ -- assert( pPage->intKey || CORRUPT_DB ); -+ if( pPage->intKey ) pnChange = 0; -+ } -+ if( pnChange ){ - testcase( !pPage->intKey ); - *pnChange += pPage->nCell; - } -@@ -73787,7 +81740,6 @@ static int clearDatabasePage( - } - - cleardatabasepage_out: -- pPage->bBusy = 0; - releasePage(pPage); - return rc; - } -@@ -73801,11 +81753,10 @@ cleardatabasepage_out: - ** read cursors on the table. Open write cursors are moved to the - ** root of the table. - ** --** If pnChange is not NULL, then table iTable must be an intkey table. The --** integer value pointed to by pnChange is incremented by the number of --** entries in the table. -+** If pnChange is not NULL, then the integer value pointed to by pnChange -+** is incremented by the number of entries in the table. - */ --SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ -+SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, i64 *pnChange){ - int rc; - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); -@@ -73817,7 +81768,9 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ - /* Invalidate all incrblob cursors open on table iTable (assuming iTable - ** is the root of a table b-tree - if it is not, the following call is - ** a no-op). */ -- invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); -+ if( p->hasIncrblobCur ){ -+ invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1); -+ } - rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); - } - sqlite3BtreeLeave(p); -@@ -73862,13 +81815,13 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ - assert( p->inTrans==TRANS_WRITE ); - assert( iTable>=2 ); - if( iTable>btreePagecount(pBt) ){ -- return SQLITE_CORRUPT_BKPT; -+ return SQLITE_CORRUPT_PGNO(iTable); - } - -- rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); -- if( rc ) return rc; - rc = sqlite3BtreeClearTable(p, iTable, 0); -- if( rc ){ -+ if( rc ) return rc; -+ rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); -+ if( NEVER(rc) ){ - releasePage(pPage); - return rc; - } -@@ -73977,7 +81930,7 @@ SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ - assert( idx>=0 && idx<=15 ); - - if( idx==BTREE_DATA_VERSION ){ -- *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; -+ *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion; - }else{ - *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); - } -@@ -74103,6 +82056,41 @@ SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){ - } - - #ifndef SQLITE_OMIT_INTEGRITY_CHECK -+/* -+** Record an OOM error during integrity_check -+*/ -+static void checkOom(IntegrityCk *pCheck){ -+ pCheck->rc = SQLITE_NOMEM; -+ pCheck->mxErr = 0; /* Causes integrity_check processing to stop */ -+ if( pCheck->nErr==0 ) pCheck->nErr++; -+} -+ -+/* -+** Invoke the progress handler, if appropriate. Also check for an -+** interrupt. -+*/ -+static void checkProgress(IntegrityCk *pCheck){ -+ sqlite3 *db = pCheck->db; -+ if( AtomicLoad(&db->u1.isInterrupted) ){ -+ pCheck->rc = SQLITE_INTERRUPT; -+ pCheck->nErr++; -+ pCheck->mxErr = 0; -+ } -+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK -+ if( db->xProgress ){ -+ assert( db->nProgressOps>0 ); -+ pCheck->nStep++; -+ if( (pCheck->nStep % db->nProgressOps)==0 -+ && db->xProgress(db->pProgressArg) -+ ){ -+ pCheck->rc = SQLITE_INTERRUPT; -+ pCheck->nErr++; -+ pCheck->mxErr = 0; -+ } -+ } -+#endif -+} -+ - /* - ** Append a message to the error message string. - */ -@@ -74112,6 +82100,7 @@ static void checkAppendMsg( - ... - ){ - va_list ap; -+ checkProgress(pCheck); - if( !pCheck->mxErr ) return; - pCheck->mxErr--; - pCheck->nErr++; -@@ -74120,12 +82109,13 @@ static void checkAppendMsg( - sqlite3_str_append(&pCheck->errMsg, "\n", 1); - } - if( pCheck->zPfx ){ -- sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2); -+ sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, -+ pCheck->v0, pCheck->v1, pCheck->v2); - } - sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap); - va_end(ap); - if( pCheck->errMsg.accError==SQLITE_NOMEM ){ -- pCheck->bOomFault = 1; -+ checkOom(pCheck); - } - } - #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ -@@ -74137,7 +82127,8 @@ static void checkAppendMsg( - ** corresponds to page iPg is already set. - */ - static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ -- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); -+ assert( pCheck->aPgRef!=0 ); -+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 ); - return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07))); - } - -@@ -74145,7 +82136,8 @@ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - ** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg. - */ - static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ -- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); -+ assert( pCheck->aPgRef!=0 ); -+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 ); - pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07)); - } - -@@ -74159,15 +82151,14 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - ** Also check that the page number is in bounds. - */ - static int checkRef(IntegrityCk *pCheck, Pgno iPage){ -- if( iPage>pCheck->nPage || iPage==0 ){ -- checkAppendMsg(pCheck, "invalid page number %d", iPage); -+ if( iPage>pCheck->nCkPage || iPage==0 ){ -+ checkAppendMsg(pCheck, "invalid page number %u", iPage); - return 1; - } - if( getPageReferenced(pCheck, iPage) ){ -- checkAppendMsg(pCheck, "2nd reference to page %d", iPage); -+ checkAppendMsg(pCheck, "2nd reference to page %u", iPage); - return 1; - } -- if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1; - setPageReferenced(pCheck, iPage); - return 0; - } -@@ -74190,14 +82181,14 @@ static void checkPtrmap( - - rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); - if( rc!=SQLITE_OK ){ -- if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1; -- checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild); -+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck); -+ checkAppendMsg(pCheck, "Failed to read ptrmap key=%u", iChild); - return; - } - - if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ - checkAppendMsg(pCheck, -- "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", -+ "Bad ptr map entry key=%u expected=(%u,%u) got=(%u,%u)", - iChild, eType, iParent, ePtrmapType, iPtrmapParent); - } - } -@@ -74222,7 +82213,7 @@ static void checkList( - if( checkRef(pCheck, iPage) ) break; - N--; - if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){ -- checkAppendMsg(pCheck, "failed to get page %d", iPage); -+ checkAppendMsg(pCheck, "failed to get page %u", iPage); - break; - } - pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage); -@@ -74235,7 +82226,7 @@ static void checkList( - #endif - if( n>pCheck->pBt->usableSize/4-2 ){ - checkAppendMsg(pCheck, -- "freelist leaf count too big on page %d", iPage); -+ "freelist leaf count too big on page %u", iPage); - N--; - }else{ - for(i=0; i<(int)n; i++){ -@@ -74267,7 +82258,7 @@ static void checkList( - } - if( N && nErrAtStart==pCheck->nErr ){ - checkAppendMsg(pCheck, -- "%s is %d but should be %d", -+ "%s is %u but should be %u", - isFreeList ? "size" : "overflow list length", - expected-N, expected); - } -@@ -74297,7 +82288,9 @@ static void checkList( - ** lower 16 bits are the index of the last byte of that range. - */ - static void btreeHeapInsert(u32 *aHeap, u32 x){ -- u32 j, i = ++aHeap[0]; -+ u32 j, i; -+ assert( aHeap!=0 ); -+ i = ++aHeap[0]; - aHeap[i] = x; - while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){ - x = aHeap[j]; -@@ -74374,15 +82367,18 @@ static int checkTreePage( - - /* Check that the page exists - */ -+ checkProgress(pCheck); -+ if( pCheck->mxErr==0 ) goto end_of_check; - pBt = pCheck->pBt; - usableSize = pBt->usableSize; - if( iPage==0 ) return 0; - if( checkRef(pCheck, iPage) ) return 0; -- pCheck->zPfx = "Page %u: "; -+ pCheck->zPfx = "Tree %u page %u: "; - pCheck->v1 = iPage; - if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ - checkAppendMsg(pCheck, - "unable to get the page. error code=%d", rc); -+ if( rc==SQLITE_IOERR_NOMEM ) pCheck->rc = SQLITE_NOMEM; - goto end_of_check; - } - -@@ -74405,7 +82401,7 @@ static int checkTreePage( - hdr = pPage->hdrOffset; - - /* Set up for cell analysis */ -- pCheck->zPfx = "On tree page %u cell %d: "; -+ pCheck->zPfx = "Tree %u page %u cell %u: "; - contentOffset = get2byteNotZero(&data[hdr+5]); - assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ - -@@ -74413,6 +82409,9 @@ static int checkTreePage( - ** number of cells on the page. */ - nCell = get2byte(&data[hdr+3]); - assert( pPage->nCell==nCell ); -+ if( pPage->leaf || pPage->intKey==0 ){ -+ pCheck->nRow += nCell; -+ } - - /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page - ** immediately follows the b-tree page header. */ -@@ -74425,7 +82424,7 @@ static int checkTreePage( - pgno = get4byte(&data[hdr+8]); - #ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ -- pCheck->zPfx = "On page %u at right child: "; -+ pCheck->zPfx = "Tree %u page %u right child: "; - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); - } - #endif -@@ -74449,7 +82448,7 @@ static int checkTreePage( - pc = get2byteAligned(pCellIdx); - pCellIdx -= 2; - if( pcusableSize-4 ){ -- checkAppendMsg(pCheck, "Offset %d out of range %d..%d", -+ checkAppendMsg(pCheck, "Offset %u out of range %u..%u", - pc, contentOffset, usableSize-4); - doCoverageCheck = 0; - continue; -@@ -74524,6 +82523,7 @@ static int checkTreePage( - btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); - } - } -+ assert( heap!=0 ); - /* Add the freeblocks to the min-heap - ** - ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header -@@ -74581,7 +82581,7 @@ static int checkTreePage( - */ - if( heap[0]==0 && nFrag!=data[hdr+7] ){ - checkAppendMsg(pCheck, -- "Fragmentation of %d bytes reported as %d on page %u", -+ "Fragmentation of %u bytes reported as %u on page %u", - nFrag, data[hdr+7], iPage); - } - } -@@ -74619,13 +82619,15 @@ end_of_check: - ** the unverified btrees. Except, if aRoot[1] is 1, then the freelist - ** checks are still performed. - */ --SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( -+SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( - sqlite3 *db, /* Database connection that is running the check */ - Btree *p, /* The btree to be checked */ - Pgno *aRoot, /* An array of root pages numbers for individual trees */ -+ Mem *aCnt, /* Memory cells to write counts for each tree to */ - int nRoot, /* Number of entries in aRoot[] */ - int mxErr, /* Stop reporting errors after this many */ -- int *pnErr /* Write number of errors seen to this variable */ -+ int *pnErr, /* OUT: Write number of errors seen to this variable */ -+ char **pzOut /* OUT: Write the error message string here */ - ){ - Pgno i; - IntegrityCk sCheck; -@@ -74635,7 +82637,9 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( - int bPartial = 0; /* True if not checking all btrees */ - int bCkFreelist = 1; /* True to scan the freelist */ - VVA_ONLY( int nRef ); -+ - assert( nRoot>0 ); -+ assert( aCnt!=0 ); - - /* aRoot[0]==0 means this is a partial check */ - if( aRoot[0]==0 ){ -@@ -74648,42 +82652,36 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( - assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); - VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) ); - assert( nRef>=0 ); -+ memset(&sCheck, 0, sizeof(sCheck)); - sCheck.db = db; - sCheck.pBt = pBt; - sCheck.pPager = pBt->pPager; -- sCheck.nPage = btreePagecount(sCheck.pBt); -+ sCheck.nCkPage = btreePagecount(sCheck.pBt); - sCheck.mxErr = mxErr; -- sCheck.nErr = 0; -- sCheck.bOomFault = 0; -- sCheck.zPfx = 0; -- sCheck.v1 = 0; -- sCheck.v2 = 0; -- sCheck.aPgRef = 0; -- sCheck.heap = 0; - sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); - sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL; -- if( sCheck.nPage==0 ){ -+ if( sCheck.nCkPage==0 ){ - goto integrity_ck_cleanup; - } - -- sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1); -+ sCheck.aPgRef = sqlite3MallocZero((sCheck.nCkPage / 8)+ 1); - if( !sCheck.aPgRef ){ -- sCheck.bOomFault = 1; -+ checkOom(&sCheck); - goto integrity_ck_cleanup; - } - sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize ); - if( sCheck.heap==0 ){ -- sCheck.bOomFault = 1; -+ checkOom(&sCheck); - goto integrity_ck_cleanup; - } - - i = PENDING_BYTE_PAGE(pBt); -- if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); -+ if( i<=sCheck.nCkPage ) setPageReferenced(&sCheck, i); - - /* Check the integrity of the freelist - */ - if( bCkFreelist ){ -- sCheck.zPfx = "Main freelist: "; -+ sCheck.zPfx = "Freelist: "; - checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), - get4byte(&pBt->pPage1->aData[36])); - sCheck.zPfx = 0; -@@ -74700,7 +82698,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( - mxInHdr = get4byte(&pBt->pPage1->aData[52]); - if( mx!=mxInHdr ){ - checkAppendMsg(&sCheck, -- "max rootpage (%d) disagrees with header (%d)", -+ "max rootpage (%u) disagrees with header (%u)", - mx, mxInHdr - ); - } -@@ -74714,24 +82712,28 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( - testcase( pBt->db->flags & SQLITE_CellSizeCk ); - pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; - for(i=0; (int)iautoVacuum && aRoot[i]>1 && !bPartial ){ -- checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); -- } -+ if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ -+ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); -+ } - #endif -- checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); -+ sCheck.v0 = aRoot[i]; -+ checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); -+ } -+ sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow); - } - pBt->db->flags = savedDbFlags; - - /* Make sure every page in the file is referenced - */ - if( !bPartial ){ -- for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ -+ for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){ - #ifdef SQLITE_OMIT_AUTOVACUUM - if( getPageReferenced(&sCheck, i)==0 ){ -- checkAppendMsg(&sCheck, "Page %d is never used", i); -+ checkAppendMsg(&sCheck, "Page %u: never used", i); - } - #else - /* If the database supports auto-vacuum, make sure no tables contain -@@ -74739,11 +82741,11 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( - */ - if( getPageReferenced(&sCheck, i)==0 && - (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ -- checkAppendMsg(&sCheck, "Page %d is never used", i); -+ checkAppendMsg(&sCheck, "Page %u: never used", i); - } - if( getPageReferenced(&sCheck, i)!=0 && - (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ -- checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); -+ checkAppendMsg(&sCheck, "Page %u: pointer map referenced", i); - } - #endif - } -@@ -74754,16 +82756,17 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( - integrity_ck_cleanup: - sqlite3PageFree(sCheck.heap); - sqlite3_free(sCheck.aPgRef); -- if( sCheck.bOomFault ){ -+ *pnErr = sCheck.nErr; -+ if( sCheck.nErr==0 ){ - sqlite3_str_reset(&sCheck.errMsg); -- sCheck.nErr++; -+ *pzOut = 0; -+ }else{ -+ *pzOut = sqlite3StrAccumFinish(&sCheck.errMsg); - } -- *pnErr = sCheck.nErr; -- if( sCheck.nErr==0 ) sqlite3_str_reset(&sCheck.errMsg); - /* Make sure this analysis did not leave any unref() pages. */ - assert( nRef==sqlite3PagerRefcount(pBt->pPager) ); - sqlite3BtreeLeave(p); -- return sqlite3StrAccumFinish(&sCheck.errMsg); -+ return sCheck.rc; - } - #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ - -@@ -74793,11 +82796,12 @@ SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *p){ - } - - /* --** Return non-zero if a transaction is active. -+** Return one of SQLITE_TXN_NONE, SQLITE_TXN_READ, or SQLITE_TXN_WRITE -+** to describe the current transaction state of Btree p. - */ --SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree *p){ -+SQLITE_PRIVATE int sqlite3BtreeTxnState(Btree *p){ - assert( p==0 || sqlite3_mutex_held(p->db->mutex) ); -- return (p && (p->inTrans==TRANS_WRITE)); -+ return p ? p->inTrans : 0; - } - - #ifndef SQLITE_OMIT_WAL -@@ -74826,14 +82830,8 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int * - #endif - - /* --** Return non-zero if a read (or write) transaction is active. -+** Return true if there is currently a backup running on Btree p. - */ --SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree *p){ -- assert( p ); -- assert( sqlite3_mutex_held(p->db->mutex) ); -- return p->inTrans!=TRANS_NONE; --} -- - SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){ - assert( p ); - assert( sqlite3_mutex_held(p->db->mutex) ); -@@ -74862,6 +82860,7 @@ SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){ - */ - SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ - BtShared *pBt = p->pBt; -+ assert( nBytes==0 || nBytes==sizeof(Schema) ); - sqlite3BtreeEnter(p); - if( !pBt->pSchema && nBytes ){ - pBt->pSchema = sqlite3DbMallocZero(0, nBytes); -@@ -75033,6 +83032,17 @@ SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *p){ - */ - SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } - -+/* -+** If no transaction is active and the database is not a temp-db, clear -+** the in-memory pager cache. -+*/ -+SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree *p){ -+ BtShared *pBt = p->pBt; -+ if( pBt->inTransaction==TRANS_NONE ){ -+ sqlite3PagerClearCache(pBt->pPager); -+ } -+} -+ - #if !defined(SQLITE_OMIT_SHARED_CACHE) - /* - ** Return true if the Btree passed as the only argument is sharable. -@@ -75141,14 +83151,13 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ - if( i==1 ){ - Parse sParse; - int rc = 0; -- memset(&sParse, 0, sizeof(sParse)); -- sParse.db = pDb; -+ sqlite3ParseObjectInit(&sParse,pDb); - if( sqlite3OpenTempDatabase(&sParse) ){ - sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); - rc = SQLITE_ERROR; - } - sqlite3DbFree(pErrorDb, sParse.zErrMsg); -- sqlite3ParserReset(&sParse); -+ sqlite3ParseObjectReset(&sParse); - if( rc ){ - return 0; - } -@@ -75179,7 +83188,7 @@ static int setDestPgsz(sqlite3_backup *p){ - ** message in database handle db. - */ - static int checkReadTransaction(sqlite3 *db, Btree *p){ -- if( sqlite3BtreeIsInReadTrans(p) ){ -+ if( sqlite3BtreeTxnState(p)!=SQLITE_TXN_NONE ){ - sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use"); - return SQLITE_ERROR; - } -@@ -75299,13 +83308,7 @@ static int backupOnePage( - assert( !isFatalError(p->rc) ); - assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); - assert( zSrcData ); -- -- /* Catch the case where the destination is an in-memory database and the -- ** page sizes of the source and destination differ. -- */ -- if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ -- rc = SQLITE_READONLY; -- } -+ assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); - - /* This loop runs once for each destination page spanned by the source - ** page. For each iteration, variable iOff is set to the byte offset -@@ -75410,7 +83413,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ - ** one now. If a transaction is opened here, then it will be closed - ** before this function exits. - */ -- if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ -+ if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(p->pSrc) ){ - rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0); - bCloseTrans = 1; - } -@@ -75438,7 +83441,10 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ - pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); - pgszDest = sqlite3BtreeGetPageSize(p->pDest); - destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); -- if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ -+ if( SQLITE_OK==rc -+ && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) -+ && pgszSrc!=pgszDest -+ ){ - rc = SQLITE_READONLY; - } - -@@ -75782,7 +83788,7 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ - sqlite3BtreeEnter(pTo); - sqlite3BtreeEnter(pFrom); - -- assert( sqlite3BtreeIsInTrans(pTo) ); -+ assert( sqlite3BtreeTxnState(pTo)==SQLITE_TXN_WRITE ); - pFd = sqlite3PagerFile(sqlite3BtreePager(pTo)); - if( pFd->pMethods ){ - i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom); -@@ -75818,7 +83824,7 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ - sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); - } - -- assert( sqlite3BtreeIsInTrans(pTo)==0 ); -+ assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE ); - copy_finished: - sqlite3BtreeLeave(pFrom); - sqlite3BtreeLeave(pTo); -@@ -75905,7 +83911,9 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){ - - /* The szMalloc field holds the correct memory allocation size */ - assert( p->szMalloc==0 -- || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc) ); -+ || (p->flags==MEM_Undefined -+ && p->szMalloc<=sqlite3DbMallocSize(p->db,p->zMalloc)) -+ || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc)); - - /* If p holds a string or blob, the Mem.z must point to exactly - ** one of the following: -@@ -75942,9 +83950,9 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ - i64 x; - assert( (p->flags&MEM_Int)*2==sizeof(x) ); - memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); -- sqlite3Int64ToText(x, zBuf); -+ p->n = sqlite3Int64ToText(x, zBuf); - #else -- sqlite3Int64ToText(p->u.i, zBuf); -+ p->n = sqlite3Int64ToText(p->u.i, zBuf); - #endif - }else{ - sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); -@@ -75952,6 +83960,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ - (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); - assert( acc.zText==zBuf && acc.mxAlloc<=0 ); - zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ -+ p->n = acc.nChar; - } - } - -@@ -75968,7 +83977,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ - ** corresponding string value, then it is important that the string be - ** derived from the numeric value, not the other way around, to ensure - ** that the index and table are consistent. See ticket --** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for -+** https://sqlite.org/src/info/343634942dd54ab (2018-01-31) for - ** an example. - ** - ** This routine looks at pMem to verify that if it has both a numeric -@@ -75979,10 +83988,12 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ - ** This routine is for use inside of assert() statements only. - */ - SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){ -+ Mem tmp; - char zBuf[100]; - char *z; - int i, j, incr; - if( (p->flags & MEM_Str)==0 ) return 1; -+ if( p->db && p->db->mallocFailed ) return 1; - if( p->flags & MEM_Term ){ - /* Insure that the string is properly zero-terminated. Pay particular - ** attention to the case where p->n is odd */ -@@ -75995,7 +84006,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){ - assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 ); - } - if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1; -- vdbeMemRenderNum(sizeof(zBuf), zBuf, p); -+ memcpy(&tmp, p, sizeof(tmp)); -+ vdbeMemRenderNum(sizeof(zBuf), zBuf, &tmp); - z = p->z; - i = j = 0; - incr = 1; -@@ -76028,10 +84040,15 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ - #ifndef SQLITE_OMIT_UTF16 - int rc; - #endif -+ assert( pMem!=0 ); - assert( !sqlite3VdbeMemIsRowSet(pMem) ); - assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE - || desiredEnc==SQLITE_UTF16BE ); -- if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){ -+ if( !(pMem->flags&MEM_Str) ){ -+ pMem->enc = desiredEnc; -+ return SQLITE_OK; -+ } -+ if( pMem->enc==desiredEnc ){ - return SQLITE_OK; - } - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); -@@ -76069,7 +84086,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre - testcase( bPreserve && pMem->z==0 ); - - assert( pMem->szMalloc==0 -- || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) ); -+ || (pMem->flags==MEM_Undefined -+ && pMem->szMalloc<=sqlite3DbMallocSize(pMem->db,pMem->zMalloc)) -+ || pMem->szMalloc==sqlite3DbMallocSize(pMem->db,pMem->zMalloc)); - if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){ - if( pMem->db ){ - pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n); -@@ -76131,6 +84150,40 @@ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ - return SQLITE_OK; - } - -+/* -+** If pMem is already a string, detect if it is a zero-terminated -+** string, or make it into one if possible, and mark it as such. -+** -+** This is an optimization. Correct operation continues even if -+** this routine is a no-op. -+*/ -+SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ -+ if( (pMem->flags & (MEM_Str|MEM_Term|MEM_Ephem|MEM_Static))!=MEM_Str ){ -+ /* pMem must be a string, and it cannot be an ephemeral or static string */ -+ return; -+ } -+ if( pMem->enc!=SQLITE_UTF8 ) return; -+ assert( pMem->z!=0 ); -+ if( pMem->flags & MEM_Dyn ){ -+ if( pMem->xDel==sqlite3_free -+ && sqlite3_msize(pMem->z) >= (u64)(pMem->n+1) -+ ){ -+ pMem->z[pMem->n] = 0; -+ pMem->flags |= MEM_Term; -+ return; -+ } -+ if( pMem->xDel==sqlite3RCStrUnref ){ -+ /* Blindly assume that all RCStr objects are zero-terminated */ -+ pMem->flags |= MEM_Term; -+ return; -+ } -+ }else if( pMem->szMalloc >= pMem->n+1 ){ -+ pMem->z[pMem->n] = 0; -+ pMem->flags |= MEM_Term; -+ return; -+ } -+} -+ - /* - ** It is already known that pMem contains an unterminated string. - ** Add the zero terminator. -@@ -76158,6 +84211,7 @@ static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){ - ** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails. - */ - SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( !sqlite3VdbeMemIsRowSet(pMem) ); - if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){ -@@ -76182,6 +84236,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ - #ifndef SQLITE_OMIT_INCRBLOB - SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ - int nByte; -+ assert( pMem!=0 ); - assert( pMem->flags & MEM_Zero ); - assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); - testcase( sqlite3_value_nochange(pMem) ); -@@ -76197,6 +84252,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ - if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){ - return SQLITE_NOMEM_BKPT; - } -+ assert( pMem->z!=0 ); -+ assert( sqlite3DbMallocSize(pMem->db,pMem->z) >= nByte ); - - memset(&pMem->z[pMem->n], 0, pMem->u.nZero); - pMem->n += pMem->u.nZero; -@@ -76209,6 +84266,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){ - ** Make sure the given Mem is \u0000 terminated. - */ - SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){ -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) ); - testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 ); -@@ -76236,6 +84294,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){ - SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ - const int nByte = 32; - -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( !(pMem->flags&MEM_Zero) ); - assert( !(pMem->flags&(MEM_Str|MEM_Blob)) ); -@@ -76251,7 +84310,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ - - vdbeMemRenderNum(nByte, pMem->z, pMem); - assert( pMem->z!=0 ); -- pMem->n = sqlite3Strlen30NN(pMem->z); -+ assert( pMem->n==(int)sqlite3Strlen30NN(pMem->z) ); - pMem->enc = SQLITE_UTF8; - pMem->flags |= MEM_Str|MEM_Term; - if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); -@@ -76271,9 +84330,11 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ - sqlite3_context ctx; - Mem t; - assert( pFunc!=0 ); -+ assert( pMem!=0 ); -+ assert( pMem->db!=0 ); - assert( pFunc->xFinalize!=0 ); - assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); -- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); -+ assert( sqlite3_mutex_held(pMem->db->mutex) ); - memset(&ctx, 0, sizeof(ctx)); - memset(&t, 0, sizeof(t)); - t.flags = MEM_Null; -@@ -76281,6 +84342,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ - ctx.pOut = &t; - ctx.pMem = pMem; - ctx.pFunc = pFunc; -+ ctx.enc = ENC(t.db); - pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ - assert( (pMem->flags & MEM_Dyn)==0 ); - if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc); -@@ -76302,12 +84364,14 @@ SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc - 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) ); -+ assert( pAccum->db!=0 ); -+ assert( sqlite3_mutex_held(pAccum->db->mutex) ); - memset(&ctx, 0, sizeof(ctx)); - sqlite3VdbeMemSetNull(pOut); - ctx.pOut = pOut; - ctx.pMem = pAccum; - ctx.pFunc = pFunc; -+ ctx.enc = ENC(pAccum->db); - pFunc->xValue(&ctx); - return ctx.isError; - } -@@ -76373,34 +84437,12 @@ SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){ - } - } - --/* --** Convert a 64-bit IEEE double into a 64-bit signed integer. --** If the double is out of range of a 64-bit signed integer then --** return the closest available 64-bit signed integer. -+/* Like sqlite3VdbeMemRelease() but faster for cases where we -+** know in advance that the Mem is not MEM_Dyn or MEM_Agg. - */ --static SQLITE_NOINLINE i64 doubleToInt64(double r){ --#ifdef SQLITE_OMIT_FLOATING_POINT -- /* When floating-point is omitted, double and int64 are the same thing */ -- return r; --#else -- /* -- ** Many compilers we encounter do not define constants for the -- ** minimum and maximum 64-bit integers, or they define them -- ** inconsistently. And many do not understand the "LL" notation. -- ** So we define our own static constants here using nothing -- ** larger than a 32-bit integer constant. -- */ -- static const i64 maxInt = LARGEST_INT64; -- static const i64 minInt = SMALLEST_INT64; -- -- if( r<=(double)minInt ){ -- return minInt; -- }else if( r>=(double)maxInt ){ -- return maxInt; -- }else{ -- return (i64)r; -- } --#endif -+SQLITE_PRIVATE void sqlite3VdbeMemReleaseMalloc(Mem *p){ -+ assert( !VdbeMemDynamic(p) ); -+ if( p->szMalloc ) vdbeMemClear(p); - } - - /* -@@ -76414,13 +84456,14 @@ static SQLITE_NOINLINE i64 doubleToInt64(double r){ - ** - ** If pMem represents a string value, its encoding might be changed. - */ --static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){ -+static SQLITE_NOINLINE i64 memIntValue(const Mem *pMem){ - i64 value = 0; - sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); - return value; - } --SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){ -+SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem *pMem){ - int flags; -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - flags = pMem->flags; -@@ -76428,7 +84471,7 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){ - testcase( flags & MEM_IntReal ); - return pMem->u.i; - }else if( flags & MEM_Real ){ -- return doubleToInt64(pMem->u.r); -+ return sqlite3RealToI64(pMem->u.r); - }else if( (flags & (MEM_Str|MEM_Blob))!=0 && pMem->z!=0 ){ - return memIntValue(pMem); - }else{ -@@ -76449,6 +84492,7 @@ static SQLITE_NOINLINE double memRealValue(Mem *pMem){ - return val; - } - SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){ -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - if( pMem->flags & MEM_Real ){ -@@ -76476,31 +84520,35 @@ SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){ - } - - /* --** The MEM structure is already a MEM_Real. Try to also make it a --** MEM_Int if we can. -+** The MEM structure is already a MEM_Real or MEM_IntReal. Try to -+** make it a MEM_Int if we can. - */ - SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ -- i64 ix; -- assert( pMem->flags & MEM_Real ); -+ assert( pMem!=0 ); -+ assert( pMem->flags & (MEM_Real|MEM_IntReal) ); - assert( !sqlite3VdbeMemIsRowSet(pMem) ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - -- ix = doubleToInt64(pMem->u.r); -- -- /* Only mark the value as an integer if -- ** -- ** (1) the round-trip conversion real->int->real is a no-op, and -- ** (2) The integer is neither the largest nor the smallest -- ** possible integer (ticket #3922) -- ** -- ** The second and third terms in the following conditional enforces -- ** the second condition under the assumption that addition overflow causes -- ** values to wrap around. -- */ -- if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; -+ if( pMem->flags & MEM_IntReal ){ - MemSetTypeFlag(pMem, MEM_Int); -+ }else{ -+ i64 ix = sqlite3RealToI64(pMem->u.r); -+ -+ /* Only mark the value as an integer if -+ ** -+ ** (1) the round-trip conversion real->int->real is a no-op, and -+ ** (2) The integer is neither the largest nor the smallest -+ ** possible integer (ticket #3922) -+ ** -+ ** The second and third terms in the following conditional enforces -+ ** the second condition under the assumption that addition overflow causes -+ ** values to wrap around. -+ */ -+ if( pMem->u.r==ix && ix>SMALLEST_INT64 && ixu.i = ix; -+ MemSetTypeFlag(pMem, MEM_Int); -+ } - } - } - -@@ -76508,6 +84556,7 @@ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ - ** Convert pMem to type integer. Invalidate any prior representations. - */ - SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){ -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( !sqlite3VdbeMemIsRowSet(pMem) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); -@@ -76522,6 +84571,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){ - ** Invalidate any prior representations. - */ - SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){ -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( EIGHT_BYTE_ALIGNMENT(pMem) ); - -@@ -76546,6 +84596,16 @@ SQLITE_PRIVATE int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){ - && i >= -2251799813685248LL && i < 2251799813685248LL); - } - -+/* Convert a floating point value to its closest integer. Do so in -+** a way that avoids 'outside the range of representable values' warnings -+** from UBSAN. -+*/ -+SQLITE_PRIVATE i64 sqlite3RealToI64(double r){ -+ if( r<-9223372036854774784.0 ) return SMALLEST_INT64; -+ if( r>+9223372036854774784.0 ) return LARGEST_INT64; -+ return (i64)r; -+} -+ - /* - ** Convert pMem so that it has type MEM_Real or MEM_Int. - ** Invalidate any prior representations. -@@ -76555,6 +84615,7 @@ SQLITE_PRIVATE int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){ - ** as much of the string as we can and ignore the rest. - */ - SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ -+ assert( pMem!=0 ); - testcase( pMem->flags & MEM_Int ); - testcase( pMem->flags & MEM_Real ); - testcase( pMem->flags & MEM_IntReal ); -@@ -76566,7 +84627,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); - if( ((rc==0 || rc==1) && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1) -- || sqlite3RealSameAsInt(pMem->u.r, (ix = (i64)pMem->u.r)) -+ || sqlite3RealSameAsInt(pMem->u.r, (ix = sqlite3RealToI64(pMem->u.r))) - ){ - pMem->u.i = ix; - MemSetTypeFlag(pMem, MEM_Int); -@@ -76612,13 +84673,17 @@ SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){ - break; - } - default: { -+ int rc; - assert( aff==SQLITE_AFF_TEXT ); - assert( MEM_Str==(MEM_Blob>>3) ); - pMem->flags |= (pMem->flags&MEM_Blob)>>3; - sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding); - assert( pMem->flags & MEM_Str || pMem->db->mallocFailed ); - pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero); -- return sqlite3VdbeChangeEncoding(pMem, encoding); -+ if( encoding!=SQLITE_UTF8 ) pMem->n &= ~1; -+ rc = sqlite3VdbeChangeEncoding(pMem, encoding); -+ if( rc ) return rc; -+ sqlite3VdbeMemZeroTerminateIfAble(pMem); - } - } - return SQLITE_OK; -@@ -76664,6 +84729,7 @@ SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){ - ** Delete any previous value and set the value to be a BLOB of length - ** n containing all zeros. - */ -+#ifndef SQLITE_OMIT_INCRBLOB - SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ - sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Blob|MEM_Zero; -@@ -76673,6 +84739,21 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ - pMem->enc = SQLITE_UTF8; - pMem->z = 0; - } -+#else -+SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ -+ int nByte = n>0?n:1; -+ if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){ -+ return SQLITE_NOMEM_BKPT; -+ } -+ assert( pMem->z!=0 ); -+ assert( sqlite3DbMallocSize(pMem->db, pMem->z)>=nByte ); -+ memset(pMem->z, 0, nByte); -+ pMem->n = n>0?n:0; -+ pMem->flags = MEM_Blob; -+ pMem->enc = SQLITE_UTF8; -+ return SQLITE_OK; -+} -+#endif - - /* - ** The pMem is known to contain content that needs to be destroyed prior -@@ -76698,6 +84779,13 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ - } - } - -+/* -+** Set the iIdx'th entry of array aMem[] to contain integer value val. -+*/ -+SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){ -+ sqlite3VdbeMemSetInt64(&aMem[iIdx], val); -+} -+ - /* A no-op destructor */ - SQLITE_PRIVATE void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } - -@@ -76712,6 +84800,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetPointer( - void (*xDestructor)(void*) - ){ - assert( pMem->flags==MEM_Null ); -+ vdbeMemClear(pMem); - pMem->u.zPType = zPType ? zPType : ""; - pMem->z = pPtr; - pMem->flags = MEM_Null|MEM_Dyn|MEM_Subtype|MEM_Term; -@@ -76793,27 +84882,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){ - SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ - int i; - Mem *pX; -- for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ -- if( pX->pScopyFrom==pMem ){ -- u16 mFlags; -- if( pVdbe->db->flags & SQLITE_VdbeTrace ){ -- sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", -- (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); -- } -- /* If pX is marked as a shallow copy of pMem, then try to verify that -- ** no significant changes have been made to pX since the OP_SCopy. -- ** A significant change would indicated a missed call to this -- ** function for pX. Minor changes, such as adding or removing a -- ** dual type, are allowed, as long as the underlying value is the -- ** same. */ -- mFlags = pMem->flags & pX->flags & pX->mScopyFlags; -- assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); -- -- /* pMem is the register that is changing. But also mark pX as -- ** undefined so that we can quickly detect the shallow-copy error */ -- pX->flags = MEM_Undefined; -- pX->pScopyFrom = 0; -- } -+ if( pMem->bScopy ){ -+ for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ -+ if( pX->pScopyFrom==pMem ){ -+ u16 mFlags; -+ if( pVdbe->db->flags & SQLITE_VdbeTrace ){ -+ sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", -+ (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); -+ } -+ /* If pX is marked as a shallow copy of pMem, then try to verify that -+ ** no significant changes have been made to pX since the OP_SCopy. -+ ** A significant change would indicated a missed call to this -+ ** function for pX. Minor changes, such as adding or removing a -+ ** dual type, are allowed, as long as the underlying value is the -+ ** same. */ -+ mFlags = pMem->flags & pX->flags & pX->mScopyFlags; -+ assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); -+ -+ /* pMem is the register that is changing. But also mark pX as -+ ** undefined so that we can quickly detect the shallow-copy error */ -+ pX->flags = MEM_Undefined; -+ pX->pScopyFrom = 0; -+ } -+ } -+ pMem->bScopy = 0; - } - pMem->pScopyFrom = 0; - } -@@ -76894,20 +84986,29 @@ SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){ - ** stored without allocating memory, then it is. If a memory allocation - ** is required to store the string, then value of pMem is unchanged. In - ** either case, SQLITE_TOOBIG is returned. -+** -+** The "enc" parameter is the text encoding for the string, or zero -+** to store a blob. -+** -+** If n is negative, then the string consists of all bytes up to but -+** excluding the first zero character. The n parameter must be -+** non-negative for blobs. - */ - SQLITE_PRIVATE int sqlite3VdbeMemSetStr( - Mem *pMem, /* Memory cell to set to string value */ - const char *z, /* String pointer */ -- int n, /* Bytes in string, or negative */ -+ i64 n, /* Bytes in string, or negative */ - u8 enc, /* Encoding of z. 0 for BLOBs */ - void (*xDel)(void*) /* Destructor function */ - ){ -- int nByte = n; /* New value for pMem->n */ -+ i64 nByte = n; /* New value for pMem->n */ - int iLimit; /* Maximum allowed string or blob size */ -- u16 flags = 0; /* New value for pMem->flags */ -+ u16 flags; /* New value for pMem->flags */ - -+ assert( pMem!=0 ); - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - assert( !sqlite3VdbeMemIsRowSet(pMem) ); -+ assert( enc!=0 || n>=0 ); - - /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ - if( !z ){ -@@ -76920,15 +85021,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( - }else{ - iLimit = SQLITE_MAX_LENGTH; - } -- flags = (enc==0?MEM_Blob:MEM_Str); - if( nByte<0 ){ - assert( enc!=0 ); - if( enc==SQLITE_UTF8 ){ -- nByte = 0x7fffffff & (int)strlen(z); -+ nByte = strlen(z); - }else{ - for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} - } -- flags |= MEM_Term; -+ flags= MEM_Str|MEM_Term; -+ }else if( enc==0 ){ -+ flags = MEM_Blob; -+ enc = SQLITE_UTF8; -+ }else{ -+ flags = MEM_Str; -+ } -+ if( nByte>iLimit ){ -+ if( xDel && xDel!=SQLITE_TRANSIENT ){ -+ if( xDel==SQLITE_DYNAMIC ){ -+ sqlite3DbFree(pMem->db, (void*)z); -+ }else{ -+ xDel((void*)z); -+ } -+ } -+ sqlite3VdbeMemSetNull(pMem); -+ return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); - } - - /* The following block sets the new values of Mem.z and Mem.xDel. It -@@ -76936,13 +85052,10 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( - ** management (one of MEM_Dyn or MEM_Static). - */ - if( xDel==SQLITE_TRANSIENT ){ -- u32 nAlloc = nByte; -+ i64 nAlloc = nByte; - if( flags&MEM_Term ){ - nAlloc += (enc==SQLITE_UTF8?1:2); - } -- if( nByte>iLimit ){ -- return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); -- } - testcase( nAlloc==0 ); - testcase( nAlloc==31 ); - testcase( nAlloc==32 ); -@@ -76962,18 +85075,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( - } - } - -- pMem->n = nByte; -+ pMem->n = (int)(nByte & 0x7fffffff); - pMem->flags = flags; -- if( enc ){ -- pMem->enc = enc; --#ifdef SQLITE_ENABLE_SESSION -- }else if( pMem->db==0 ){ -- pMem->enc = SQLITE_UTF8; --#endif -- }else{ -- assert( pMem->db!=0 ); -- pMem->enc = ENC(pMem->db); -- } -+ pMem->enc = enc; - - #ifndef SQLITE_OMIT_UTF16 - if( enc>SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){ -@@ -76981,9 +85085,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( - } - #endif - -- if( nByte>iLimit ){ -- return SQLITE_TOOBIG; -- } - - return SQLITE_OK; - } -@@ -77116,6 +85217,24 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){ - return valueToText(pVal, enc); - } - -+/* Return true if sqlit3_value object pVal is a string or blob value -+** that uses the destructor specified in the second argument. -+** -+** TODO: Maybe someday promote this interface into a published API so -+** that third-party extensions can get access to it? -+*/ -+SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){ -+ if( ALWAYS(pVal!=0) -+ && ALWAYS((pVal->flags & (MEM_Str|MEM_Blob))!=0) -+ && (pVal->flags & MEM_Dyn)!=0 -+ && pVal->xDel==xFree -+ ){ -+ return 1; -+ }else{ -+ return 0; -+ } -+} -+ - /* - ** Create a new sqlite3_value object. - */ -@@ -77157,7 +85276,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ - - if( pRec==0 ){ - Index *pIdx = p->pIdx; /* Index being probed */ -- int nByte; /* Bytes of space to allocate */ -+ i64 nByte; /* Bytes of space to allocate */ - int i; /* Counter variable */ - int nCol = pIdx->nColumn; /* Number of index columns including rowid */ - -@@ -77183,6 +85302,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ - } - - pRec->nField = p->iVal+1; -+ sqlite3VdbeMemSetNull(&pRec->aMem[p->iVal]); - return &pRec->aMem[p->iVal]; - } - #else -@@ -77214,7 +85334,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ - #ifdef SQLITE_ENABLE_STAT4 - static int valueFromFunction( - sqlite3 *db, /* The database connection */ -- Expr *p, /* The expression to evaluate */ -+ const Expr *p, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 aff, /* Affinity to use */ - sqlite3_value **ppVal, /* Write the new value here */ -@@ -77222,7 +85342,7 @@ static int valueFromFunction( - ){ - sqlite3_context ctx; /* Context object for function invocation */ - sqlite3_value **apVal = 0; /* Function arguments */ -- int nVal = 0; /* Size of apVal[] array */ -+ int nVal = 0; /* Number of function arguments */ - FuncDef *pFunc = 0; /* Function definition */ - sqlite3_value *pVal = 0; /* New value */ - int rc = SQLITE_OK; /* Return code */ -@@ -77231,12 +85351,17 @@ static int valueFromFunction( - - assert( pCtx!=0 ); - assert( (p->flags & EP_TokenOnly)==0 ); -+ assert( ExprUseXList(p) ); - pList = p->x.pList; - if( pList ) nVal = pList->nExpr; -+ assert( !ExprHasProperty(p, EP_IntValue) ); - pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0); -+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION -+ if( pFunc==0 ) return SQLITE_OK; -+#endif - assert( pFunc ); - if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 -- || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) -+ || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0 - ){ - return SQLITE_OK; - } -@@ -77248,7 +85373,8 @@ static int valueFromFunction( - goto value_from_function_out; - } - for(i=0; ia[i].pExpr, enc, aff, &apVal[i]); -+ rc = sqlite3Stat4ValueFromExpr(pCtx->pParse, pList->a[i].pExpr, aff, -+ &apVal[i]); - if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; - } - } -@@ -77259,10 +85385,10 @@ static int valueFromFunction( - goto value_from_function_out; - } - -- assert( pCtx->pParse->rc==SQLITE_OK ); - memset(&ctx, 0, sizeof(ctx)); - ctx.pOut = pVal; - ctx.pFunc = pFunc; -+ ctx.enc = ENC(db); - pFunc->xSFunc(&ctx, nVal, apVal); - if( ctx.isError ){ - rc = ctx.isError; -@@ -77271,16 +85397,16 @@ static int valueFromFunction( - sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); - assert( rc==SQLITE_OK ); - rc = sqlite3VdbeChangeEncoding(pVal, enc); -- if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){ -+ if( NEVER(rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal)) ){ - rc = SQLITE_TOOBIG; - pCtx->pParse->nErr++; - } - } -- pCtx->pParse->rc = rc; - - value_from_function_out: - if( rc!=SQLITE_OK ){ - pVal = 0; -+ pCtx->pParse->rc = rc; - } - if( apVal ){ - for(i=0; iop)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; --#if defined(SQLITE_ENABLE_STAT4) - if( op==TK_REGISTER ) op = pExpr->op2; --#else -- if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; --#endif - - /* Compressed expressions only appear when parsing the DEFAULT clause - ** on a table column definition, and hence only when pCtx==0. This -@@ -77336,25 +85458,40 @@ static int valueFromExpr( - assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 ); - - if( op==TK_CAST ){ -- u8 aff = sqlite3AffinityType(pExpr->u.zToken,0); -+ u8 aff; -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ aff = sqlite3AffinityType(pExpr->u.zToken,0); - rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); - testcase( rc!=SQLITE_OK ); - if( *ppVal ){ -- sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8); -- sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8); -+#ifdef SQLITE_ENABLE_STAT4 -+ rc = ExpandBlob(*ppVal); -+#else -+ /* zero-blobs only come from functions, not literal values. And -+ ** functions are only processed under STAT4 */ -+ assert( (ppVal[0][0].flags & MEM_Zero)==0 ); -+#endif -+ sqlite3VdbeMemCast(*ppVal, aff, enc); -+ sqlite3ValueApplyAffinity(*ppVal, affinity, enc); - } - return rc; - } - - /* Handle negative integers in a single step. This is needed in the -- ** case when the value is -9223372036854775808. -- */ -- if( op==TK_UMINUS -- && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ -- pExpr = pExpr->pLeft; -- op = pExpr->op; -- negInt = -1; -- zNeg = "-"; -+ ** case when the value is -9223372036854775808. Except - do not do this -+ ** for hexadecimal literals. */ -+ if( op==TK_UMINUS ){ -+ Expr *pLeft = pExpr->pLeft; -+ if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){ -+ if( ExprHasProperty(pLeft, EP_IntValue) -+ || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X' -+ ){ -+ pExpr = pLeft; -+ op = pExpr->op; -+ negInt = -1; -+ zNeg = "-"; -+ } -+ } - } - - if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ -@@ -77363,12 +85500,26 @@ static int valueFromExpr( - if( ExprHasProperty(pExpr, EP_IntValue) ){ - sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); - }else{ -- zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); -- if( zVal==0 ) goto no_mem; -- sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); -- } -- if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){ -- sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); -+ i64 iVal; -+ if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){ -+ sqlite3VdbeMemSetInt64(pVal, iVal*negInt); -+ }else{ -+ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); -+ if( zVal==0 ) goto no_mem; -+ sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); -+ } -+ } -+ if( affinity==SQLITE_AFF_BLOB ){ -+ if( op==TK_FLOAT ){ -+ assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) ); -+ sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8); -+ pVal->flags = MEM_Real; -+ }else if( op==TK_INTEGER ){ -+ /* This case is required by -9223372036854775808 and other strings -+ ** that look like integers but cannot be handled by the -+ ** sqlite3DecOrHexToI64() call above. */ -+ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); -+ } - }else{ - sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); - } -@@ -77409,6 +85560,7 @@ static int valueFromExpr( - #ifndef SQLITE_OMIT_BLOB_LITERAL - else if( op==TK_BLOB ){ - int nVal; -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); - assert( pExpr->u.zToken[1]=='\'' ); - pVal = valueNew(db, pCtx); -@@ -77426,10 +85578,12 @@ static int valueFromExpr( - } - #endif - else if( op==TK_TRUEFALSE ){ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pVal = valueNew(db, pCtx); - if( pVal ){ - pVal->flags = MEM_Int; - pVal->u.i = pExpr->u.zToken[4]==0; -+ sqlite3ValueApplyAffinity(pVal, affinity, enc); - } - } - -@@ -77438,7 +85592,7 @@ static int valueFromExpr( - - no_mem: - #ifdef SQLITE_ENABLE_STAT4 -- if( pCtx==0 || pCtx->pParse->nErr==0 ) -+ if( pCtx==0 || NEVER(pCtx->pParse->nErr==0) ) - #endif - sqlite3OomFault(db); - sqlite3DbFree(db, zVal); -@@ -77463,7 +85617,7 @@ no_mem: - */ - SQLITE_PRIVATE int sqlite3ValueFromExpr( - sqlite3 *db, /* The database connection */ -- Expr *pExpr, /* The expression to evaluate */ -+ const Expr *pExpr, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 affinity, /* Affinity to use */ - sqlite3_value **ppVal /* Write the new value here */ -@@ -77635,17 +85789,17 @@ SQLITE_PRIVATE int sqlite3Stat4Column( - sqlite3_value **ppVal /* OUT: Extracted value */ - ){ - u32 t = 0; /* a column type code */ -- int nHdr; /* Size of the header in the record */ -- int iHdr; /* Next unread header byte */ -- int iField; /* Next unread data byte */ -- int szField = 0; /* Size of the current data field */ -+ u32 nHdr; /* Size of the header in the record */ -+ u32 iHdr; /* Next unread header byte */ -+ i64 iField; /* Next unread data byte */ -+ u32 szField = 0; /* Size of the current data field */ - int i; /* Column index */ - u8 *a = (u8*)pRec; /* Typecast byte array */ - Mem *pMem = *ppVal; /* Write result into this Mem object */ - - assert( iCol>0 ); - iHdr = getVarint32(a, nHdr); -- if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; -+ if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; - iField = nHdr; - for(i=0; i<=iCol; i++){ - iHdr += getVarint32(&a[iHdr], t); -@@ -77723,6 +85877,9 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){ - if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){ - return p->n; - } -+ if( (p->flags & MEM_Str)!=0 && enc!=SQLITE_UTF8 && pVal->enc!=SQLITE_UTF8 ){ -+ return p->n; -+ } - if( (p->flags & MEM_Blob)!=0 ){ - if( p->flags & MEM_Zero ){ - return p->n + p->u.nZero; -@@ -77768,12 +85925,12 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ - memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp)); - p->db = db; - if( db->pVdbe ){ -- db->pVdbe->pPrev = p; -+ db->pVdbe->ppVPrev = &p->pVNext; - } -- p->pNext = db->pVdbe; -- p->pPrev = 0; -+ p->pVNext = db->pVdbe; -+ p->ppVPrev = &db->pVdbe; - db->pVdbe = p; -- p->magic = VDBE_MAGIC_INIT; -+ assert( p->eVdbeState==VDBE_INIT_STATE ); - p->pParse = pParse; - pParse->pVdbe = p; - assert( pParse->aLabel==0 ); -@@ -77853,21 +86010,28 @@ SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString( - #endif - - /* --** Swap all content between two VDBE structures. -+** Swap byte-code between two VDBE structures. -+** -+** This happens after pB was previously run and returned -+** SQLITE_SCHEMA. The statement was then reprepared in pA. -+** This routine transfers the new bytecode in pA over to pB -+** so that pB can be run again. The old pB byte code is -+** moved back to pA so that it will be cleaned up when pA is -+** finalized. - */ - SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ -- Vdbe tmp, *pTmp; -+ Vdbe tmp, *pTmp, **ppTmp; - char *zTmp; - assert( pA->db==pB->db ); - tmp = *pA; - *pA = *pB; - *pB = tmp; -- pTmp = pA->pNext; -- pA->pNext = pB->pNext; -- pB->pNext = pTmp; -- pTmp = pA->pPrev; -- pA->pPrev = pB->pPrev; -- pB->pPrev = pTmp; -+ pTmp = pA->pVNext; -+ pA->pVNext = pB->pVNext; -+ pB->pVNext = pTmp; -+ ppTmp = pA->ppVPrev; -+ pA->ppVPrev = pB->ppVPrev; -+ pB->ppVPrev = ppTmp; - zTmp = pA->zSql; - pA->zSql = pB->zSql; - pB->zSql = zTmp; -@@ -77918,7 +86082,7 @@ static int growOpArray(Vdbe *v, int nOp){ - return SQLITE_NOMEM; - } - -- assert( nOp<=(1024/sizeof(Op)) ); -+ assert( nOp<=(int)(1024/sizeof(Op)) ); - assert( nNew>=(v->nOpAlloc+nOp) ); - pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); - if( pNew ){ -@@ -77942,11 +86106,43 @@ static int growOpArray(Vdbe *v, int nOp){ - ** sqlite3CantopenError(lineno) - */ - static void test_addop_breakpoint(int pc, Op *pOp){ -- static int n = 0; -+ static u64 n = 0; -+ (void)pc; -+ (void)pOp; - n++; -+ if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */ - } - #endif - -+/* -+** Slow paths for sqlite3VdbeAddOp3() and sqlite3VdbeAddOp4Int() for the -+** unusual case when we need to increase the size of the Vdbe.aOp[] array -+** before adding the new opcode. -+*/ -+static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ -+ assert( p->nOpAlloc<=p->nOp ); -+ if( growOpArray(p, 1) ) return 1; -+ assert( p->nOpAlloc>p->nOp ); -+ return sqlite3VdbeAddOp3(p, op, p1, p2, p3); -+} -+static SQLITE_NOINLINE int addOp4IntSlow( -+ Vdbe *p, /* Add the opcode to this VM */ -+ int op, /* The new opcode */ -+ int p1, /* The P1 operand */ -+ int p2, /* The P2 operand */ -+ int p3, /* The P3 operand */ -+ int p4 /* The P4 operand as an integer */ -+){ -+ int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); -+ if( p->db->mallocFailed==0 ){ -+ VdbeOp *pOp = &p->aOp[addr]; -+ pOp->p4type = P4_INT32; -+ pOp->p4.i = p4; -+ } -+ return addr; -+} -+ -+ - /* - ** Add a new instruction to the list of instructions current in the - ** VDBE. Return the address of the new instruction. -@@ -77957,30 +86153,31 @@ static void test_addop_breakpoint(int pc, Op *pOp){ - ** - ** op The opcode for this instruction - ** --** p1, p2, p3 Operands --** --** Use the sqlite3VdbeResolveLabel() function to fix an address and --** the sqlite3VdbeChangeP4() function to change the value of the P4 --** operand. -+** p1, p2, p3, p4 Operands - */ --static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ -- assert( p->nOpAlloc<=p->nOp ); -- if( growOpArray(p, 1) ) return 1; -- assert( p->nOpAlloc>p->nOp ); -- return sqlite3VdbeAddOp3(p, op, p1, p2, p3); -+SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ -+ return sqlite3VdbeAddOp3(p, op, 0, 0, 0); -+} -+SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ -+ return sqlite3VdbeAddOp3(p, op, p1, 0, 0); -+} -+SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ -+ return sqlite3VdbeAddOp3(p, op, p1, p2, 0); - } - SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ - int i; - VdbeOp *pOp; - - i = p->nOp; -- assert( p->magic==VDBE_MAGIC_INIT ); -+ assert( p->eVdbeState==VDBE_INIT_STATE ); - assert( op>=0 && op<0xff ); - if( p->nOpAlloc<=i ){ - return growOp3(p, op, p1, p2, p3); - } -+ assert( p->aOp!=0 ); - p->nOp++; - pOp = &p->aOp[i]; -+ assert( pOp!=0 ); - pOp->opcode = (u8)op; - pOp->p5 = 0; - pOp->p1 = p1; -@@ -77988,32 +86185,78 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ - pOp->p3 = p3; - pOp->p4.p = 0; - pOp->p4type = P4_NOTUSED; -+ -+ /* Replicate this logic in sqlite3VdbeAddOp4Int() -+ ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ - #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - pOp->zComment = 0; - #endif -+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) -+ pOp->nExec = 0; -+ pOp->nCycle = 0; -+#endif - #ifdef SQLITE_DEBUG - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - sqlite3VdbePrintOp(0, i, &p->aOp[i]); - test_addop_breakpoint(i, &p->aOp[i]); - } - #endif --#ifdef VDBE_PROFILE -- pOp->cycles = 0; -- pOp->cnt = 0; --#endif - #ifdef SQLITE_VDBE_COVERAGE - pOp->iSrcLine = 0; - #endif -+ /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -+ ** Replicate in sqlite3VdbeAddOp4Int() */ -+ - return i; - } --SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ -- return sqlite3VdbeAddOp3(p, op, 0, 0, 0); --} --SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){ -- return sqlite3VdbeAddOp3(p, op, p1, 0, 0); --} --SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){ -- return sqlite3VdbeAddOp3(p, op, p1, p2, 0); -+SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( -+ Vdbe *p, /* Add the opcode to this VM */ -+ int op, /* The new opcode */ -+ int p1, /* The P1 operand */ -+ int p2, /* The P2 operand */ -+ int p3, /* The P3 operand */ -+ int p4 /* The P4 operand as an integer */ -+){ -+ int i; -+ VdbeOp *pOp; -+ -+ i = p->nOp; -+ if( p->nOpAlloc<=i ){ -+ return addOp4IntSlow(p, op, p1, p2, p3, p4); -+ } -+ p->nOp++; -+ pOp = &p->aOp[i]; -+ assert( pOp!=0 ); -+ pOp->opcode = (u8)op; -+ pOp->p5 = 0; -+ pOp->p1 = p1; -+ pOp->p2 = p2; -+ pOp->p3 = p3; -+ pOp->p4.i = p4; -+ pOp->p4type = P4_INT32; -+ -+ /* Replicate this logic in sqlite3VdbeAddOp3() -+ ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ -+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS -+ pOp->zComment = 0; -+#endif -+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) -+ pOp->nExec = 0; -+ pOp->nCycle = 0; -+#endif -+#ifdef SQLITE_DEBUG -+ if( p->db->flags & SQLITE_VdbeAddopTrace ){ -+ sqlite3VdbePrintOp(0, i, &p->aOp[i]); -+ test_addop_breakpoint(i, &p->aOp[i]); -+ } -+#endif -+#ifdef SQLITE_VDBE_COVERAGE -+ pOp->iSrcLine = 0; -+#endif -+ /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -+ ** Replicate in sqlite3VdbeAddOp3() */ -+ -+ return i; - } - - /* Generate code for an unconditional jump to instruction iDest -@@ -78097,12 +86340,10 @@ SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall( - int eCallCtx /* Calling context */ - ){ - Vdbe *v = pParse->pVdbe; -- int nByte; - int addr; - sqlite3_context *pCtx; - assert( v ); -- nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*); -- pCtx = sqlite3DbMallocRawNN(pParse->db, nByte); -+ pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg)); - if( pCtx==0 ){ - assert( pParse->db->mallocFailed ); - freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); -@@ -78117,6 +86358,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall( - addr = sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function, - p1, p2, p3, (char*)pCtx, P4_FUNCCTX); - sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef); -+ sqlite3MayAbort(pParse); - return addr; - } - -@@ -78167,11 +86409,12 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){ - ** If the bPush flag is true, then make this opcode the parent for - ** subsequent Explains until sqlite3VdbeExplainPop() is called. - */ --SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ --#ifndef SQLITE_DEBUG -+SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ -+ int addr = 0; -+#if !defined(SQLITE_DEBUG) - /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. - ** But omit them (for performance) during production builds */ -- if( pParse->explain==2 ) -+ if( pParse->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) - #endif - { - char *zMsg; -@@ -78183,13 +86426,15 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt - va_end(ap); - v = pParse->pVdbe; - iThis = v->nOp; -- sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, -+ addr = sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, - zMsg, P4_DYNAMIC); -- sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetOp(v,-1)->p4.z); -+ sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetLastOp(v)->p4.z); - if( bPush){ - pParse->addrExplain = iThis; - } -+ sqlite3VdbeScanStatus(v, iThis, -1, -1, 0, 0); - } -+ return addr; - } - - /* -@@ -78209,30 +86454,12 @@ SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse *pParse){ - ** The zWhere string must have been obtained from sqlite3_malloc(). - ** This routine will take ownership of the allocated memory. - */ --SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ -+SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, u16 p5){ - int j; - sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); -+ sqlite3VdbeChangeP5(p, p5); - for(j=0; jdb->nDb; j++) sqlite3VdbeUsesBtree(p, j); --} -- --/* --** Add an opcode that includes the p4 value as an integer. --*/ --SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( -- Vdbe *p, /* Add the opcode to this VM */ -- int op, /* The new opcode */ -- int p1, /* The P1 operand */ -- int p2, /* The P2 operand */ -- int p3, /* The P3 operand */ -- int p4 /* The P4 operand as an integer */ --){ -- int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); -- if( p->db->mallocFailed==0 ){ -- VdbeOp *pOp = &p->aOp[addr]; -- pOp->p4type = P4_INT32; -- pOp->p4.i = p4; -- } -- return addr; -+ sqlite3MayAbort(p->pParse); - } - - /* Insert the end of a co-routine -@@ -78295,6 +86522,9 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ - int i; - for(i=p->nLabelAlloc; iaLabel[i] = -1; - #endif -+ if( nNewSize>=100 && (nNewSize/100)>(p->nLabelAlloc/100) ){ -+ sqlite3ProgressCheck(p); -+ } - p->nLabelAlloc = nNewSize; - p->aLabel[j] = v->nOp; - } -@@ -78302,7 +86532,7 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ - SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ - Parse *p = v->pParse; - int j = ADDR(x); -- assert( v->magic==VDBE_MAGIC_INIT ); -+ assert( v->eVdbeState==VDBE_INIT_STATE ); - assert( j<-p->nLabel ); - assert( j>=0 ); - #ifdef SQLITE_DEBUG -@@ -78322,14 +86552,20 @@ SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ - ** Mark the VDBE as one that can only be run one time. - */ - SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe *p){ -- p->runOnlyOnce = 1; -+ sqlite3VdbeAddOp2(p, OP_Expire, 1, 1); - } - - /* --** Mark the VDBE as one that can only be run multiple times. -+** Mark the VDBE as one that can be run multiple times. - */ - SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe *p){ -- p->runOnlyOnce = 0; -+ int i; -+ for(i=1; ALWAYS(inOp); i++){ -+ if( ALWAYS(p->aOp[i].opcode==OP_Expire) ){ -+ p->aOp[1].opcode = OP_Noop; -+ break; -+ } -+ } - } - - #ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */ -@@ -78383,7 +86619,7 @@ static Op *opIterNext(VdbeOpIter *p){ - } - - if( pRet->p4type==P4_SUBPROGRAM ){ -- int nByte = (p->nSub+1)*sizeof(SubProgram*); -+ i64 nByte = (1+(u64)p->nSub)*sizeof(SubProgram*); - int j; - for(j=0; jnSub; j++){ - if( p->apSub[j]==pRet->p4.pProgram ) break; -@@ -78433,6 +86669,8 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ - int hasInitCoroutine = 0; - Op *pOp; - VdbeOpIter sIter; -+ -+ if( v==0 ) return 0; - memset(&sIter, 0, sizeof(sIter)); - sIter.v = v; - -@@ -78441,7 +86679,8 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ - if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename - || opcode==OP_VDestroy - || opcode==OP_VCreate -- || (opcode==OP_ParseSchema && pOp->p4.z==0) -+ || opcode==OP_ParseSchema -+ || opcode==OP_Function || opcode==OP_PureFunc - || ((opcode==OP_Halt || opcode==OP_HaltIfNull) - && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) - ){ -@@ -78510,13 +86749,13 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){ - ** (1) For each jump instruction with a negative P2 value (a label) - ** resolve the P2 value to an actual address. - ** --** (2) Compute the maximum number of arguments used by any SQL function --** and store that value in *pMaxFuncArgs. -+** (2) Compute the maximum number of arguments used by the xUpdate/xFilter -+** methods of any virtual table and store that value in *pMaxVtabArgs. - ** - ** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately - ** indicate what the prepared statement actually does. - ** --** (4) Initialize the p4.xAdvance pointer on opcodes that use it. -+** (4) (discontinued) - ** - ** (5) Reclaim the memory allocated for storing labels. - ** -@@ -78524,16 +86763,18 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){ - ** script numbers the opcodes correctly. Changes to this routine must be - ** coordinated with changes to mkopcodeh.tcl. - */ --static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ -- int nMaxArgs = *pMaxFuncArgs; -+static void resolveP2Values(Vdbe *p, int *pMaxVtabArgs){ -+ int nMaxVtabArgs = *pMaxVtabArgs; - Op *pOp; - Parse *pParse = p->pParse; - int *aLabel = pParse->aLabel; -+ -+ assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */ - p->readOnly = 1; - p->bIsReader = 0; - pOp = &p->aOp[p->nOp-1]; -- while(1){ -- -+ assert( p->aOp[0].opcode==OP_Init ); -+ while( 1 /* Loop terminates when it reaches the OP_Init opcode */ ){ - /* Only JUMP opcodes and the short list of special opcodes in the switch - ** below need to be considered. The mkopcodeh.tcl generator script groups - ** all these opcodes together near the front of the opcode list. Skip -@@ -78562,36 +86803,25 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ - p->bIsReader = 1; - break; - } -- case OP_Next: -- case OP_SorterNext: { -- pOp->p4.xAdvance = sqlite3BtreeNext; -- pOp->p4type = P4_ADVANCE; -- /* The code generator never codes any of these opcodes as a jump -- ** to a label. They are always coded as a jump backwards to a -- ** known address */ -+ case OP_Init: { - assert( pOp->p2>=0 ); -- break; -- } -- case OP_Prev: { -- pOp->p4.xAdvance = sqlite3BtreePrevious; -- pOp->p4type = P4_ADVANCE; -- /* The code generator never codes any of these opcodes as a jump -- ** to a label. They are always coded as a jump backwards to a -- ** known address */ -- assert( pOp->p2>=0 ); -- break; -+ goto resolve_p2_values_loop_exit; - } - #ifndef SQLITE_OMIT_VIRTUALTABLE - case OP_VUpdate: { -- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; -+ if( pOp->p2>nMaxVtabArgs ) nMaxVtabArgs = pOp->p2; - break; - } - case OP_VFilter: { - int n; -+ /* The instruction immediately prior to VFilter will be an -+ ** OP_Integer that sets the "argc" value for the VFilter. See -+ ** the code where OP_VFilter is generated at tag-20250207a. */ - assert( (pOp - p->aOp) >= 3 ); - assert( pOp[-1].opcode==OP_Integer ); -+ assert( pOp[-1].p2==pOp->p3+1 ); - n = pOp[-1].p1; -- if( n>nMaxArgs ) nMaxArgs = n; -+ if( n>nMaxVtabArgs ) nMaxVtabArgs = n; - /* Fall through into the default case */ - /* no break */ deliberate_fall_through - } -@@ -78603,8 +86833,18 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ - ** have non-negative values for P2. */ - assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); - assert( ADDR(pOp->p2)<-pParse->nLabel ); -+ assert( aLabel!=0 ); /* True because of tag-20230419-1 */ - pOp->p2 = aLabel[ADDR(pOp->p2)]; - } -+ -+ /* OPFLG_JUMP opcodes never have P2==0, though OPFLG_JUMP0 opcodes -+ ** might */ -+ assert( pOp->p2>0 -+ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP0)!=0 ); -+ -+ /* Jumps never go off the end of the bytecode array */ -+ assert( pOp->p2nOp -+ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)==0 ); - break; - } - } -@@ -78613,21 +86853,112 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ - ** have non-negative values for P2. */ - assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0); - } -- if( pOp==p->aOp ) break; -+ assert( pOp>p->aOp ); - pOp--; - } -- sqlite3DbFree(p->db, pParse->aLabel); -- pParse->aLabel = 0; -+resolve_p2_values_loop_exit: -+ if( aLabel ){ -+ sqlite3DbNNFreeNN(p->db, pParse->aLabel); -+ pParse->aLabel = 0; -+ } - pParse->nLabel = 0; -- *pMaxFuncArgs = nMaxArgs; -+ *pMaxVtabArgs = nMaxVtabArgs; - assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) ); - } - -+#ifdef SQLITE_DEBUG -+/* -+** Check to see if a subroutine contains a jump to a location outside of -+** the subroutine. If a jump outside the subroutine is detected, add code -+** that will cause the program to halt with an error message. -+** -+** The subroutine consists of opcodes between iFirst and iLast. Jumps to -+** locations within the subroutine are acceptable. iRetReg is a register -+** that contains the return address. Jumps to outside the range of iFirst -+** through iLast are also acceptable as long as the jump destination is -+** an OP_Return to iReturnAddr. -+** -+** A jump to an unresolved label means that the jump destination will be -+** beyond the current address. That is normally a jump to an early -+** termination and is consider acceptable. -+** -+** This routine only runs during debug builds. The purpose is (of course) -+** to detect invalid escapes out of a subroutine. The OP_Halt opcode -+** is generated rather than an assert() or other error, so that ".eqp full" -+** will still work to show the original bytecode, to aid in debugging. -+*/ -+SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn( -+ Vdbe *v, /* The byte-code program under construction */ -+ int iFirst, /* First opcode of the subroutine */ -+ int iLast, /* Last opcode of the subroutine */ -+ int iRetReg /* Subroutine return address register */ -+){ -+ VdbeOp *pOp; -+ Parse *pParse; -+ int i; -+ sqlite3_str *pErr = 0; -+ assert( v!=0 ); -+ pParse = v->pParse; -+ assert( pParse!=0 ); -+ if( pParse->nErr ) return; -+ assert( iLast>=iFirst ); -+ assert( iLastnOp ); -+ pOp = &v->aOp[iFirst]; -+ for(i=iFirst; i<=iLast; i++, pOp++){ -+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ){ -+ int iDest = pOp->p2; /* Jump destination */ -+ if( iDest==0 ) continue; -+ if( pOp->opcode==OP_Gosub ) continue; -+ if( pOp->p3==20230325 && pOp->opcode==OP_NotNull ){ -+ /* This is a deliberately taken illegal branch. tag-20230325-2 */ -+ continue; -+ } -+ if( iDest<0 ){ -+ int j = ADDR(iDest); -+ assert( j>=0 ); -+ if( j>=-pParse->nLabel || pParse->aLabel[j]<0 ){ -+ continue; -+ } -+ iDest = pParse->aLabel[j]; -+ } -+ if( iDestiLast ){ -+ int j = iDest; -+ for(; jnOp; j++){ -+ VdbeOp *pX = &v->aOp[j]; -+ if( pX->opcode==OP_Return ){ -+ if( pX->p1==iRetReg ) break; -+ continue; -+ } -+ if( pX->opcode==OP_Noop ) continue; -+ if( pX->opcode==OP_Explain ) continue; -+ if( pErr==0 ){ -+ pErr = sqlite3_str_new(0); -+ }else{ -+ sqlite3_str_appendchar(pErr, 1, '\n'); -+ } -+ sqlite3_str_appendf(pErr, -+ "Opcode at %d jumps to %d which is outside the " -+ "subroutine at %d..%d", -+ i, iDest, iFirst, iLast); -+ break; -+ } -+ } -+ } -+ } -+ if( pErr ){ -+ char *zErr = sqlite3_str_finish(pErr); -+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_INTERNAL, OE_Abort, 0, zErr, 0); -+ sqlite3_free(zErr); -+ sqlite3MayAbort(pParse); -+ } -+} -+#endif /* SQLITE_DEBUG */ -+ - /* - ** Return the address of the next instruction to be inserted. - */ - SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){ -- assert( p->magic==VDBE_MAGIC_INIT ); -+ assert( p->eVdbeState==VDBE_INIT_STATE ); - return p->nOp; - } - -@@ -78712,7 +87043,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList( - int i; - VdbeOp *pOut, *pFirst; - assert( nOp>0 ); -- assert( p->magic==VDBE_MAGIC_INIT ); -+ assert( p->eVdbeState==VDBE_INIT_STATE ); - if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ - return 0; - } -@@ -78759,20 +87090,83 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus( - LogEst nEst, /* Estimated number of output rows */ - const char *zName /* Name of table or index being scanned */ - ){ -- sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); -- ScanStatus *aNew; -- aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); -- if( aNew ){ -- ScanStatus *pNew = &aNew[p->nScan++]; -- pNew->addrExplain = addrExplain; -- pNew->addrLoop = addrLoop; -- pNew->addrVisit = addrVisit; -- pNew->nEst = nEst; -- pNew->zName = sqlite3DbStrDup(p->db, zName); -- p->aScan = aNew; -+ if( IS_STMT_SCANSTATUS(p->db) ){ -+ i64 nByte = (1+(i64)p->nScan) * sizeof(ScanStatus); -+ ScanStatus *aNew; -+ aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); -+ if( aNew ){ -+ ScanStatus *pNew = &aNew[p->nScan++]; -+ memset(pNew, 0, sizeof(ScanStatus)); -+ pNew->addrExplain = addrExplain; -+ pNew->addrLoop = addrLoop; -+ pNew->addrVisit = addrVisit; -+ pNew->nEst = nEst; -+ pNew->zName = sqlite3DbStrDup(p->db, zName); -+ p->aScan = aNew; -+ } - } - } --#endif -+ -+/* -+** Add the range of instructions from addrStart to addrEnd (inclusive) to -+** the set of those corresponding to the sqlite3_stmt_scanstatus() counters -+** associated with the OP_Explain instruction at addrExplain. The -+** sum of the sqlite3Hwtime() values for each of these instructions -+** will be returned for SQLITE_SCANSTAT_NCYCLE requests. -+*/ -+SQLITE_PRIVATE void sqlite3VdbeScanStatusRange( -+ Vdbe *p, -+ int addrExplain, -+ int addrStart, -+ int addrEnd -+){ -+ if( IS_STMT_SCANSTATUS(p->db) ){ -+ ScanStatus *pScan = 0; -+ int ii; -+ for(ii=p->nScan-1; ii>=0; ii--){ -+ pScan = &p->aScan[ii]; -+ if( pScan->addrExplain==addrExplain ) break; -+ pScan = 0; -+ } -+ if( pScan ){ -+ if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1; -+ for(ii=0; iiaAddrRange); ii+=2){ -+ if( pScan->aAddrRange[ii]==0 ){ -+ pScan->aAddrRange[ii] = addrStart; -+ pScan->aAddrRange[ii+1] = addrEnd; -+ break; -+ } -+ } -+ } -+ } -+} -+ -+/* -+** Set the addresses for the SQLITE_SCANSTAT_NLOOP and SQLITE_SCANSTAT_NROW -+** counters for the query element associated with the OP_Explain at -+** addrExplain. -+*/ -+SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters( -+ Vdbe *p, -+ int addrExplain, -+ int addrLoop, -+ int addrVisit -+){ -+ if( IS_STMT_SCANSTATUS(p->db) ){ -+ ScanStatus *pScan = 0; -+ int ii; -+ for(ii=p->nScan-1; ii>=0; ii--){ -+ pScan = &p->aScan[ii]; -+ if( pScan->addrExplain==addrExplain ) break; -+ pScan = 0; -+ } -+ if( pScan ){ -+ if( addrLoop>0 ) pScan->addrLoop = addrLoop; -+ if( addrVisit>0 ) pScan->addrVisit = addrVisit; -+ } -+ } -+} -+#endif /* defined(SQLITE_ENABLE_STMT_SCANSTATUS) */ - - - /* -@@ -78780,15 +87174,19 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus( - ** for a specific instruction. - */ - SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){ -+ assert( addr>=0 ); - sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode; - } - SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){ -+ assert( addr>=0 ); - sqlite3VdbeGetOp(p,addr)->p1 = val; - } - SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){ -+ assert( addr>=0 || p->db->mallocFailed ); - sqlite3VdbeGetOp(p,addr)->p2 = val; - } - SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){ -+ assert( addr>=0 ); - sqlite3VdbeGetOp(p,addr)->p3 = val; - } - SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ -@@ -78796,6 +87194,21 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){ - if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5; - } - -+/* -+** If the previous opcode is an OP_Column that delivers results -+** into register iDest, then add the OPFLAG_TYPEOFARG flag to that -+** opcode. -+*/ -+SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){ -+ VdbeOp *pOp = sqlite3VdbeGetLastOp(p); -+#ifdef SQLITE_DEBUG -+ while( pOp->opcode==OP_ReleaseReg ) pOp--; -+#endif -+ if( pOp->p3==iDest && pOp->opcode==OP_Column ){ -+ pOp->p5 |= OPFLAG_TYPEOFARG; -+ } -+} -+ - /* - ** Change the P2 operand of instruction addr so that it points to - ** the address of the next instruction to be coded. -@@ -78824,7 +87237,7 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ - || p->aOp[addr].opcode==OP_FkIfZero ); - assert( p->aOp[addr].p4type==0 ); - #ifdef SQLITE_VDBE_COVERAGE -- sqlite3VdbeGetOp(p,-1)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ -+ sqlite3VdbeGetLastOp(p)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ - #endif - p->nOp--; - }else{ -@@ -78835,11 +87248,12 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ - - /* - ** If the input FuncDef structure is ephemeral, then free it. If --** the FuncDef is not ephermal, then do nothing. -+** the FuncDef is not ephemeral, then do nothing. - */ - static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ -+ assert( db!=0 ); - if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ -- sqlite3DbFreeNN(db, pDef); -+ sqlite3DbNNFreeNN(db, pDef); - } - } - -@@ -78848,11 +87262,12 @@ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ - */ - static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){ - if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); -- sqlite3DbFreeNN(db, p); -+ sqlite3DbNNFreeNN(db, p); - } - static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){ -+ assert( db!=0 ); - freeEphemeralFunction(db, p->pFunc); -- sqlite3DbFreeNN(db, p); -+ sqlite3DbNNFreeNN(db, p); - } - static void freeP4(sqlite3 *db, int p4type, void *p4){ - assert( db ); -@@ -78864,9 +87279,8 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ - case P4_REAL: - case P4_INT64: - case P4_DYNAMIC: -- case P4_DYNBLOB: - case P4_INTARRAY: { -- sqlite3DbFree(db, p4); -+ if( p4 ) sqlite3DbNNFreeNN(db, p4); - break; - } - case P4_KEYINFO: { -@@ -78895,6 +87309,16 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ - if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); - break; - } -+ case P4_TABLEREF: { -+ if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4); -+ break; -+ } -+ case P4_SUBRTNSIG: { -+ SubrtnSig *pSig = (SubrtnSig*)p4; -+ sqlite3DbFree(db, pSig->zAff); -+ sqlite3DbFree(db, pSig); -+ break; -+ } - } - } - -@@ -78904,15 +87328,19 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ - ** nOp entries. - */ - static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ -+ assert( nOp>=0 ); -+ assert( db!=0 ); - if( aOp ){ -- Op *pOp; -- for(pOp=&aOp[nOp-1]; pOp>=aOp; pOp--){ -+ Op *pOp = &aOp[nOp-1]; -+ while(1){ /* Exit via break */ - if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p); - #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - sqlite3DbFree(db, pOp->zComment); - #endif -+ if( pOp==aOp ) break; -+ pOp--; - } -- sqlite3DbFreeNN(db, aOp); -+ sqlite3DbNNFreeNN(db, aOp); - } - } - -@@ -78972,7 +87400,7 @@ SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters( - u32 mask, /* Mask of registers to NOT release */ - int bUndefine /* If true, mark registers as undefined */ - ){ -- if( N==0 ) return; -+ if( N==0 || OptimizationDisabled(pParse->db, SQLITE_ReleaseReg) ) return; - assert( pParse->pVdbe ); - assert( iFirst>=1 ); - assert( iFirst+N-1<=pParse->nMem ); -@@ -78994,7 +87422,6 @@ SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters( - } - #endif /* SQLITE_DEBUG */ - -- - /* - ** Change the value of the P4 operand for a specific instruction. - ** This routine is useful when a large program is loaded from a -@@ -79019,7 +87446,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full( - int n - ){ - if( pOp->p4type ){ -- freeP4(p->db, pOp->p4type, pOp->p4.p); -+ assert( pOp->p4type > P4_FREE_IF_LE ); - pOp->p4type = 0; - pOp->p4.p = 0; - } -@@ -79036,7 +87463,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int - sqlite3 *db; - assert( p!=0 ); - db = p->db; -- assert( p->magic==VDBE_MAGIC_INIT ); -+ assert( p->eVdbeState==VDBE_INIT_STATE ); - assert( p->aOp!=0 || db->mallocFailed ); - if( db->mallocFailed ){ - if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); -@@ -79081,7 +87508,7 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){ - if( p->db->mallocFailed ){ - freeP4(p->db, n, pP4); - }else{ -- assert( pP4!=0 ); -+ assert( pP4!=0 || n==P4_DYNAMIC ); - assert( p->nOp>0 ); - pOp = &p->aOp[p->nOp-1]; - assert( pOp->p4type==P4_NOTUSED ); -@@ -79112,8 +87539,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ - */ - static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){ - assert( p->nOp>0 || p->aOp==0 ); -- assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed -- || p->pParse->nErr>0 ); -+ assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->pParse->nErr>0 ); - if( p->nOp ){ - assert( p->aOp ); - sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); -@@ -79144,13 +87570,13 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ - ** Set the value if the iSrcLine field for the previously coded instruction. - */ - SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){ -- sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine; -+ sqlite3VdbeGetLastOp(v)->iSrcLine = iLine; - } - #endif /* SQLITE_VDBE_COVERAGE */ - - /* --** Return the opcode for a given address. If the address is -1, then --** return the most recently inserted opcode. -+** Return the opcode for a given address. The address must be non-negative. -+** See sqlite3VdbeGetLastOp() to get the most recently added opcode. - ** - ** If a memory allocation error has occurred prior to the calling of this - ** routine, then a pointer to a dummy VdbeOp will be returned. That opcode -@@ -79165,10 +87591,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ - /* C89 specifies that the constant "dummy" will be initialized to all - ** zeros, which is correct. MSVC generates a warning, nevertheless. */ - static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ -- assert( p->magic==VDBE_MAGIC_INIT ); -- if( addr<0 ){ -- addr = p->nOp - 1; -- } -+ assert( p->eVdbeState==VDBE_INIT_STATE ); - assert( (addr>=0 && addrnOp) || p->db->mallocFailed ); - if( p->db->mallocFailed ){ - return (VdbeOp*)&dummy; -@@ -79177,6 +87600,12 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ - } - } - -+/* Return the most recently added opcode -+*/ -+SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){ -+ return sqlite3VdbeGetOp(p, p->nOp - 1); -+} -+ - #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) - /* - ** Return an integer value for one of the parameters to the opcode pOp -@@ -79221,13 +87650,9 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( - if( zOpName[nOpName+1] ){ - int seenCom = 0; - char c; -- zSynopsis = zOpName += nOpName + 1; -+ zSynopsis = zOpName + nOpName + 1; - if( strncmp(zSynopsis,"IF ",3)==0 ){ -- if( pOp->p5 & SQLITE_STOREP2 ){ -- sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3); -- }else{ -- sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); -- } -+ sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3); - zSynopsis = zAlt; - } - for(ii=0; (c = zSynopsis[ii])!=0; ii++){ -@@ -79236,8 +87661,11 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( - if( c=='4' ){ - sqlite3_str_appendall(&x, zP4); - }else if( c=='X' ){ -- sqlite3_str_appendall(&x, pOp->zComment); -- seenCom = 1; -+ if( pOp->zComment && pOp->zComment[0] ){ -+ sqlite3_str_appendall(&x, pOp->zComment); -+ seenCom = 1; -+ break; -+ } - }else{ - int v1 = translateP(c, pOp); - int v2; -@@ -79259,7 +87687,7 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayComment( - sqlite3_str_appendf(&x, "%d", v1); - }else if( pCtx->argc>1 ){ - sqlite3_str_appendf(&x, "%d..%d", v1, v1+pCtx->argc-1); -- }else{ -+ }else if( x.accError==0 ){ - assert( x.nChar>2 ); - x.nChar -= 2; - ii++; -@@ -79298,6 +87726,7 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){ - const char *zOp = 0; - switch( pExpr->op ){ - case TK_STRING: -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3_str_appendf(p, "%Q", pExpr->u.zToken); - break; - case TK_INTEGER: -@@ -79400,7 +87829,7 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ - case P4_COLLSEQ: { - static const char *const encnames[] = {"?", "8", "16LE", "16BE"}; - CollSeq *pColl = pOp->p4.pColl; -- assert( pColl->enc>=0 && pColl->enc<4 ); -+ assert( pColl->enc<4 ); - sqlite3_str_appendf(&x, "%.18s-%s", pColl->zName, - encnames[pColl->enc]); - break; -@@ -79465,14 +87894,15 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ - zP4 = "program"; - break; - } -- case P4_DYNBLOB: -- case P4_ADVANCE: { -- break; -- } - case P4_TABLE: { - zP4 = pOp->p4.pTab->zName; - break; - } -+ case P4_SUBRTNSIG: { -+ SubrtnSig *pSig = pOp->p4.pSubrtnSig; -+ sqlite3_str_appendf(&x, "subrtnsig:%d,%s", pSig->selId, pSig->zAff); -+ break; -+ } - default: { - zP4 = pOp->p4.z; - } -@@ -79600,26 +88030,48 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ - - /* - ** Initialize an array of N Mem element. -+** -+** This is a high-runner, so only those fields that really do need to -+** be initialized are set. The Mem structure is organized so that -+** the fields that get initialized are nearby and hopefully on the same -+** cache line. -+** -+** Mem.flags = flags -+** Mem.db = db -+** Mem.szMalloc = 0 -+** -+** All other fields of Mem can safely remain uninitialized for now. They -+** will be initialized before use. - */ - static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){ -- while( (N--)>0 ){ -- p->db = db; -- p->flags = flags; -- p->szMalloc = 0; -+ assert( db!=0 ); -+ if( N>0 ){ -+ do{ -+ p->flags = flags; -+ p->db = db; -+ p->szMalloc = 0; - #ifdef SQLITE_DEBUG -- p->pScopyFrom = 0; -+ p->pScopyFrom = 0; -+ p->bScopy = 0; - #endif -- p++; -+ p++; -+ }while( (--N)>0 ); - } - } - - /* --** Release an array of N Mem elements -+** Release auxiliary memory held in an array of N Mem elements. -+** -+** After this routine returns, all Mem elements in the array will still -+** be valid. Those Mem elements that were not holding auxiliary resources -+** will be unchanged. Mem elements which had something freed will be -+** set to MEM_Undefined. - */ - static void releaseMemArray(Mem *p, int N){ - if( p && N ){ - Mem *pEnd = &p[N]; - sqlite3 *db = p->db; -+ assert( db!=0 ); - if( db->pnBytesFreed ){ - do{ - if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); -@@ -79644,15 +88096,20 @@ static void releaseMemArray(Mem *p, int N){ - */ - testcase( p->flags & MEM_Agg ); - testcase( p->flags & MEM_Dyn ); -- testcase( p->xDel==sqlite3VdbeFrameMemDel ); - if( p->flags&(MEM_Agg|MEM_Dyn) ){ -+ testcase( (p->flags & MEM_Dyn)!=0 && p->xDel==sqlite3VdbeFrameMemDel ); - sqlite3VdbeMemRelease(p); -+ p->flags = MEM_Undefined; - }else if( p->szMalloc ){ -- sqlite3DbFreeNN(db, p->zMalloc); -+ sqlite3DbNNFreeNN(db, p->zMalloc); - p->szMalloc = 0; -+ p->flags = MEM_Undefined; - } -- -- p->flags = MEM_Undefined; -+#ifdef SQLITE_DEBUG -+ else{ -+ p->flags = MEM_Undefined; -+ } -+#endif - }while( (++p)nChildMem]; - assert( sqlite3VdbeFrameIsValid(p) ); - for(i=0; inChildCsr; i++){ -- sqlite3VdbeFreeCursor(p->v, apCsr[i]); -+ if( apCsr[i] ) sqlite3VdbeFreeCursorNN(p->v, apCsr[i]); - } - releaseMemArray(aMem, p->nChildMem); - sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0); -@@ -79850,7 +88307,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( - Op *pOp; /* Current opcode */ - - assert( p->explain ); -- assert( p->magic==VDBE_MAGIC_RUN ); -+ assert( p->eVdbeState==VDBE_RUN_STATE ); - assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); - - /* Even though this opcode does not use dynamic strings for -@@ -79858,7 +88315,6 @@ SQLITE_PRIVATE int sqlite3VdbeList( - ** sqlite3_column_text16(), causing a translation to UTF-16 encoding. - */ - releaseMemArray(pMem, 8); -- p->pResultSet = 0; - - if( p->rc==SQLITE_NOMEM ){ - /* This happens if a malloc() inside a call to sqlite3_column_text() or -@@ -79894,7 +88350,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( - sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); - sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); - sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free); -- p->nResColumn = 4; -+ assert( p->nResColumn==4 ); - }else{ - sqlite3VdbeMemSetInt64(pMem+0, i); - sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode), -@@ -79913,9 +88369,9 @@ SQLITE_PRIVATE int sqlite3VdbeList( - sqlite3VdbeMemSetNull(pMem+7); - #endif - sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free); -- p->nResColumn = 8; -+ assert( p->nResColumn==8 ); - } -- p->pResultSet = pMem; -+ p->pResultRow = pMem; - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM; - rc = SQLITE_ERROR; -@@ -80005,11 +88461,11 @@ struct ReusableSpace { - static void *allocSpace( - struct ReusableSpace *p, /* Bulk memory available for allocation */ - void *pBuf, /* Pointer to a prior allocation */ -- sqlite3_int64 nByte /* Bytes of memory needed */ -+ sqlite3_int64 nByte /* Bytes of memory needed. */ - ){ - assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); - if( pBuf==0 ){ -- nByte = ROUND8(nByte); -+ nByte = ROUND8P(nByte); - if( nByte <= p->nFree ){ - p->nFree -= nByte; - pBuf = &p->pSpace[p->nFree]; -@@ -80026,18 +88482,19 @@ static void *allocSpace( - ** running it. - */ - SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){ --#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) -+#if defined(SQLITE_DEBUG) - int i; - #endif - assert( p!=0 ); -- assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET ); -+ assert( p->eVdbeState==VDBE_INIT_STATE -+ || p->eVdbeState==VDBE_READY_STATE -+ || p->eVdbeState==VDBE_HALT_STATE ); - - /* There should be at least one opcode. - */ - assert( p->nOp>0 ); - -- /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */ -- p->magic = VDBE_MAGIC_RUN; -+ p->eVdbeState = VDBE_READY_STATE; - - #ifdef SQLITE_DEBUG - for(i=0; inMem; i++){ -@@ -80054,8 +88511,8 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){ - p->nFkConstraint = 0; - #ifdef VDBE_PROFILE - for(i=0; inOp; i++){ -- p->aOp[i].cnt = 0; -- p->aOp[i].cycles = 0; -+ p->aOp[i].nExec = 0; -+ p->aOp[i].nCycle = 0; - } - #endif - } -@@ -80086,15 +88543,18 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( - int nVar; /* Number of parameters */ - int nMem; /* Number of VM memory registers */ - int nCursor; /* Number of cursors required */ -- int nArg; /* Number of arguments in subprograms */ -+ int nArg; /* Max number args to xFilter or xUpdate */ - int n; /* Loop counter */ - struct ReusableSpace x; /* Reusable bulk memory */ - - assert( p!=0 ); - assert( p->nOp>0 ); - assert( pParse!=0 ); -- assert( p->magic==VDBE_MAGIC_INIT ); -+ assert( p->eVdbeState==VDBE_INIT_STATE ); - assert( pParse==p->pParse ); -+ assert( pParse->db==p->db ); -+ p->pVList = pParse->pVList; -+ pParse->pVList = 0; - db = p->db; - assert( db->mallocFailed==0 ); - nVar = pParse->nVar; -@@ -80114,7 +88574,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( - ** opcode array. This extra memory will be reallocated for other elements - ** of the prepared statement. - */ -- n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ -+ n = ROUND8P(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */ - x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */ - assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) ); - x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */ -@@ -80124,26 +88584,9 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( - resolveP2Values(p, &nArg); - p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); - if( pParse->explain ){ -- static const char * const azColName[] = { -- "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", -- "id", "parent", "notused", "detail" -- }; -- int iFirst, mx, i; - if( nMem<10 ) nMem = 10; - p->explain = pParse->explain; -- if( pParse->explain==2 ){ -- sqlite3VdbeSetNumCols(p, 4); -- iFirst = 8; -- mx = 12; -- }else{ -- sqlite3VdbeSetNumCols(p, 8); -- iFirst = 0; -- mx = 8; -- } -- for(i=iFirst; inResColumn = 12 - 4*p->explain; - } - p->expired = 0; - -@@ -80162,9 +88605,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( - p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); - p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); - p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); --#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); --#endif - if( x.nNeeded ){ - x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); - x.nFree = x.nNeeded; -@@ -80173,14 +88613,12 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( - p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); - p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); - p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); --#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); --#endif - } - } -+#ifdef SQLITE_DEBUG -+ p->napArg = nArg; -+#endif - -- p->pVList = pParse->pVList; -- pParse->pVList = 0; - if( db->mallocFailed ){ - p->nVar = 0; - p->nCursor = 0; -@@ -80192,9 +88630,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( - p->nMem = nMem; - initMemArray(p->aMem, nMem, db, MEM_Undefined); - memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*)); --#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- memset(p->anExec, 0, p->nOp*sizeof(i64)); --#endif - } - sqlite3VdbeRewind(p); - } -@@ -80204,24 +88639,33 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( - ** happens to hold. - */ - SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ -- if( pCx==0 ){ -+ if( pCx ) sqlite3VdbeFreeCursorNN(p,pCx); -+} -+static SQLITE_NOINLINE void freeCursorWithCache(Vdbe *p, VdbeCursor *pCx){ -+ VdbeTxtBlbCache *pCache = pCx->pCache; -+ assert( pCx->colCache ); -+ pCx->colCache = 0; -+ pCx->pCache = 0; -+ if( pCache->pCValue ){ -+ sqlite3RCStrUnref(pCache->pCValue); -+ pCache->pCValue = 0; -+ } -+ sqlite3DbFree(p->db, pCache); -+ sqlite3VdbeFreeCursorNN(p, pCx); -+} -+SQLITE_PRIVATE void sqlite3VdbeFreeCursorNN(Vdbe *p, VdbeCursor *pCx){ -+ if( pCx->colCache ){ -+ freeCursorWithCache(p, pCx); - return; - } -- assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); - switch( pCx->eCurType ){ - case CURTYPE_SORTER: { - sqlite3VdbeSorterClose(p->db, pCx); - break; - } - case CURTYPE_BTREE: { -- if( pCx->isEphemeral ){ -- if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx); -- /* The pCx->pCursor will be close automatically, if it exists, by -- ** the call above. */ -- }else{ -- assert( pCx->uc.pCursor!=0 ); -- sqlite3BtreeCloseCursor(pCx->uc.pCursor); -- } -+ assert( pCx->uc.pCursor!=0 ); -+ sqlite3BtreeCloseCursor(pCx->uc.pCursor); - break; - } - #ifndef SQLITE_OMIT_VIRTUALTABLE -@@ -80241,14 +88685,12 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ - ** Close all cursors in the current frame. - */ - static void closeCursorsInFrame(Vdbe *p){ -- if( p->apCsr ){ -- int i; -- for(i=0; inCursor; i++){ -- VdbeCursor *pC = p->apCsr[i]; -- if( pC ){ -- sqlite3VdbeFreeCursor(p, pC); -- p->apCsr[i] = 0; -- } -+ int i; -+ for(i=0; inCursor; i++){ -+ VdbeCursor *pC = p->apCsr[i]; -+ if( pC ){ -+ sqlite3VdbeFreeCursorNN(p, pC); -+ p->apCsr[i] = 0; - } - } - } -@@ -80261,9 +88703,6 @@ static void closeCursorsInFrame(Vdbe *p){ - SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ - Vdbe *v = pFrame->v; - closeCursorsInFrame(v); --#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- v->anExec = pFrame->anExec; --#endif - v->aOp = pFrame->aOp; - v->nOp = pFrame->nOp; - v->aMem = pFrame->aMem; -@@ -80297,9 +88736,7 @@ static void closeAllCursors(Vdbe *p){ - } - assert( p->nFrame==0 ); - closeCursorsInFrame(p); -- if( p->aMem ){ -- releaseMemArray(p->aMem, p->nMem); -- } -+ releaseMemArray(p->aMem, p->nMem); - while( p->pDelFrame ){ - VdbeFrame *pDel = p->pDelFrame; - p->pDelFrame = pDel->pParent; -@@ -80321,12 +88758,12 @@ SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ - int n; - sqlite3 *db = p->db; - -- if( p->nResColumn ){ -- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); -+ if( p->nResAlloc ){ -+ releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N); - sqlite3DbFree(db, p->aColName); - } - n = nResColumn*COLNAME_N; -- p->nResColumn = (u16)nResColumn; -+ p->nResColumn = p->nResAlloc = (u16)nResColumn; - p->aColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n ); - if( p->aColName==0 ) return; - initMemArray(p->aColName, n, db, MEM_Null); -@@ -80351,14 +88788,14 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName( - ){ - int rc; - Mem *pColName; -- assert( idxnResColumn ); -+ assert( idxnResAlloc ); - assert( vardb->mallocFailed ){ - assert( !zName || xDel!=SQLITE_DYNAMIC ); - return SQLITE_NOMEM_BKPT; - } - assert( p->aColName!=0 ); -- pColName = &(p->aColName[idx+var*p->nResColumn]); -+ pColName = &(p->aColName[idx+var*p->nResAlloc]); - rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); - assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); - return rc; -@@ -80401,7 +88838,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ - */ - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; -- if( sqlite3BtreeIsInTrans(pBt) ){ -+ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ - /* Whether or not a database might need a super-journal depends upon - ** its journal mode (among other things). This matrix determines which - ** journal modes use a super-journal and which do not */ -@@ -80536,7 +88973,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ - */ - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; -- if( sqlite3BtreeIsInTrans(pBt) ){ -+ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ - char const *zFile = sqlite3BtreeGetJournalname(pBt); - if( zFile==0 ){ - continue; /* Ignore TEMP and :memory: databases */ -@@ -80646,7 +89083,7 @@ static void checkActiveVdbeCnt(sqlite3 *db){ - if( p->readOnly==0 ) nWrite++; - if( p->bIsReader ) nRead++; - } -- p = p->pNext; -+ p = p->pVNext; - } - assert( cnt==db->nVdbeActive ); - assert( nWrite==db->nVdbeWrite ); -@@ -80739,7 +89176,8 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ - p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; - p->errorAction = OE_Abort; - sqlite3VdbeError(p, "FOREIGN KEY constraint failed"); -- return SQLITE_ERROR; -+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR; -+ return SQLITE_CONSTRAINT_FOREIGNKEY; - } - return SQLITE_OK; - } -@@ -80750,9 +89188,9 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ - ** has made changes and is in autocommit mode, then commit those - ** changes. If a rollback is needed, then do the rollback. - ** --** This routine is the only way to move the state of a VM from --** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. It is harmless to --** call this on a VM that is in the SQLITE_MAGIC_HALT state. -+** This routine is the only way to move the sqlite3eOpenState of a VM from -+** SQLITE_STATE_RUN to SQLITE_STATE_HALT. It is harmless to -+** call this on a VM that is in the SQLITE_STATE_HALT state. - ** - ** Return an error code. If the commit could not complete because of - ** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it -@@ -80778,9 +89216,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - ** one, or the complete transaction if there is no statement transaction. - */ - -- if( p->magic!=VDBE_MAGIC_RUN ){ -- return SQLITE_OK; -- } -+ assert( p->eVdbeState==VDBE_RUN_STATE ); - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM_BKPT; - } -@@ -80789,7 +89225,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - - /* No commit or rollback needed if the program never started or if the - ** SQL statement does not read or write a database file. */ -- if( p->pc>=0 && p->bIsReader ){ -+ if( p->bIsReader ){ - int mrc; /* Primary error code from p->rc */ - int eStatementOp = 0; - int isSpecialError; /* Set to true if a 'special' error */ -@@ -80798,9 +89234,15 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - sqlite3VdbeEnter(p); - - /* Check for one of the special errors */ -- mrc = p->rc & 0xff; -- isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR -- || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; -+ if( p->rc ){ -+ mrc = p->rc & 0xff; -+ isSpecialError = mrc==SQLITE_NOMEM -+ || mrc==SQLITE_IOERR -+ || mrc==SQLITE_INTERRUPT -+ || mrc==SQLITE_FULL; -+ }else{ -+ mrc = isSpecialError = 0; -+ } - if( isSpecialError ){ - /* If the query was read-only and the error code is SQLITE_INTERRUPT, - ** no rollback is necessary. Otherwise, at least a savepoint -@@ -80831,7 +89273,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - - /* Check for immediate foreign key violations. */ - if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ -- sqlite3VdbeCheckFk(p, 0); -+ (void)sqlite3VdbeCheckFk(p, 0); - } - - /* If the auto-commit flag is set and this is the only active writer -@@ -80852,6 +89294,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - return SQLITE_ERROR; - } - rc = SQLITE_CONSTRAINT_FOREIGNKEY; -+ }else if( db->flags & SQLITE_CorruptRdOnly ){ -+ rc = SQLITE_CORRUPT; -+ db->flags &= ~SQLITE_CorruptRdOnly; - }else{ - /* The auto-commit flag is true, the vdbe program was successful - ** or hit an 'OR FAIL' constraint and there are no deferred foreign -@@ -80863,6 +89308,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - sqlite3VdbeLeave(p); - return SQLITE_BUSY; - }else if( rc!=SQLITE_OK ){ -+ sqlite3SystemError(db, rc); - p->rc = rc; - sqlite3RollbackAll(db, SQLITE_OK); - p->nChange = 0; -@@ -80872,6 +89318,8 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - db->flags &= ~(u64)SQLITE_DeferFKs; - sqlite3CommitInternalChanges(db); - } -+ }else if( p->rc==SQLITE_SCHEMA && db->nVdbeActive>1 ){ -+ p->nChange = 0; - }else{ - sqlite3RollbackAll(db, SQLITE_OK); - p->nChange = 0; -@@ -80928,15 +89376,13 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ - } - - /* We have successfully halted and closed the VM. Record this fact. */ -- if( p->pc>=0 ){ -- db->nVdbeActive--; -- if( !p->readOnly ) db->nVdbeWrite--; -- if( p->bIsReader ) db->nVdbeRead--; -- assert( db->nVdbeActive>=db->nVdbeRead ); -- assert( db->nVdbeRead>=db->nVdbeWrite ); -- assert( db->nVdbeWrite>=0 ); -- } -- p->magic = VDBE_MAGIC_HALT; -+ db->nVdbeActive--; -+ if( !p->readOnly ) db->nVdbeWrite--; -+ if( p->bIsReader ) db->nVdbeRead--; -+ assert( db->nVdbeActive>=db->nVdbeRead ); -+ assert( db->nVdbeRead>=db->nVdbeWrite ); -+ assert( db->nVdbeWrite>=0 ); -+ p->eVdbeState = VDBE_HALT_STATE; - checkActiveVdbeCnt(db); - if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM_BKPT; -@@ -80985,6 +89431,7 @@ SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){ - sqlite3ValueSetNull(db->pErr); - } - db->errCode = rc; -+ db->errByteOffset = -1; - return rc; - } - -@@ -81017,8 +89464,8 @@ static void vdbeInvokeSqllog(Vdbe *v){ - ** again. - ** - ** To look at it another way, this routine resets the state of the --** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to --** VDBE_MAGIC_INIT. -+** virtual machine from VDBE_RUN_STATE or VDBE_HALT_STATE back to -+** VDBE_READY_STATE. - */ - SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) -@@ -81032,7 +89479,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - ** error, then it might not have been halted properly. So halt - ** it now. - */ -- sqlite3VdbeHalt(p); -+ if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p); - - /* If the VDBE has been run even partially, then transfer the error code - ** and error message from the VDBE into the main database structure. But -@@ -81046,13 +89493,6 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - }else{ - db->errCode = p->rc; - } -- if( p->runOnlyOnce ) p->expired = 1; -- }else if( p->rc && p->expired ){ -- /* The expired flag was set on the VDBE before the first call -- ** to sqlite3_step(). For consistency (since sqlite3_step() was -- ** called), set the database error in this case as well. -- */ -- sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); - } - - /* Reset register contents and reclaim error message memory. -@@ -81069,7 +89509,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - } -- p->pResultSet = 0; -+ p->pResultRow = 0; - #ifdef SQLITE_DEBUG - p->nWrite = 0; - #endif -@@ -81097,10 +89537,12 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - } - for(i=0; inOp; i++){ - char zHdr[100]; -+ i64 cnt = p->aOp[i].nExec; -+ i64 cycles = p->aOp[i].nCycle; - sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", -- p->aOp[i].cnt, -- p->aOp[i].cycles, -- p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 -+ cnt, -+ cycles, -+ cnt>0 ? cycles/cnt : 0 - ); - fprintf(out, "%s", zHdr); - sqlite3VdbePrintOp(out, i, &p->aOp[i]); -@@ -81109,7 +89551,6 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - } - } - #endif -- p->magic = VDBE_MAGIC_RESET; - return p->rc & db->errMask; - } - -@@ -81119,7 +89560,10 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ - */ - SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){ - int rc = SQLITE_OK; -- if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ -+ assert( VDBE_RUN_STATE>VDBE_READY_STATE ); -+ assert( VDBE_HALT_STATE>VDBE_READY_STATE ); -+ assert( VDBE_INIT_STATEeVdbeState>=VDBE_READY_STATE ){ - rc = sqlite3VdbeReset(p); - assert( (rc & p->db->errMask)==rc ); - } -@@ -81171,29 +89615,32 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, - ** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with - ** the database connection and frees the object itself. - */ --SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ -+static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ - SubProgram *pSub, *pNext; -+ assert( db!=0 ); - assert( p->db==0 || p->db==db ); -- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); -+ if( p->aColName ){ -+ releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N); -+ sqlite3DbNNFreeNN(db, p->aColName); -+ } - for(pSub=p->pProgram; pSub; pSub=pNext){ - pNext = pSub->pNext; - vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); - sqlite3DbFree(db, pSub); - } -- if( p->magic!=VDBE_MAGIC_INIT ){ -+ if( p->eVdbeState!=VDBE_INIT_STATE ){ - releaseMemArray(p->aVar, p->nVar); -- sqlite3DbFree(db, p->pVList); -- sqlite3DbFree(db, p->pFree); -+ if( p->pVList ) sqlite3DbNNFreeNN(db, p->pVList); -+ if( p->pFree ) sqlite3DbNNFreeNN(db, p->pFree); - } - vdbeFreeOpArray(db, p->aOp, p->nOp); -- sqlite3DbFree(db, p->aColName); -- sqlite3DbFree(db, p->zSql); -+ if( p->zSql ) sqlite3DbNNFreeNN(db, p->zSql); - #ifdef SQLITE_ENABLE_NORMALIZE - sqlite3DbFree(db, p->zNormSql); - { -- DblquoteStr *pThis, *pNext; -- for(pThis=p->pDblStr; pThis; pThis=pNext){ -- pNext = pThis->pNextStr; -+ DblquoteStr *pThis, *pNxt; -+ for(pThis=p->pDblStr; pThis; pThis=pNxt){ -+ pNxt = pThis->pNextStr; - sqlite3DbFree(db, pThis); - } - } -@@ -81217,20 +89664,17 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){ - - assert( p!=0 ); - db = p->db; -+ assert( db!=0 ); - assert( sqlite3_mutex_held(db->mutex) ); - sqlite3VdbeClearObject(db, p); -- if( p->pPrev ){ -- p->pPrev->pNext = p->pNext; -- }else{ -- assert( db->pVdbe==p ); -- db->pVdbe = p->pNext; -- } -- if( p->pNext ){ -- p->pNext->pPrev = p->pPrev; -+ if( db->pnBytesFreed==0 ){ -+ assert( p->ppVPrev!=0 ); -+ *p->ppVPrev = p->pVNext; -+ if( p->pVNext ){ -+ p->pVNext->ppVPrev = p->ppVPrev; -+ } - } -- p->magic = VDBE_MAGIC_DEAD; -- p->db = 0; -- sqlite3DbFreeNN(db, p); -+ sqlite3DbNNFreeNN(db, p); - } - - /* -@@ -81246,7 +89690,7 @@ SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor *p){ - assert( p->deferredMoveto ); - assert( p->isTable ); - assert( p->eCurType==CURTYPE_BTREE ); -- rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res); -+ rc = sqlite3BtreeTableMoveto(p->uc.pCursor, p->movetoTarget, 0, &res); - if( rc ) return rc; - if( res!=0 ) return SQLITE_CORRUPT_BKPT; - #ifdef SQLITE_TEST -@@ -81264,7 +89708,7 @@ SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor *p){ - ** is supposed to be pointing. If the row was deleted out from under the - ** cursor, set the cursor to point to a NULL row. - */ --static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ -+SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeHandleMovedCursor(VdbeCursor *p){ - int isDifferentRow, rc; - assert( p->eCurType==CURTYPE_BTREE ); - assert( p->uc.pCursor!=0 ); -@@ -81280,40 +89724,9 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ - ** if need be. Return any I/O error from the restore operation. - */ - SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){ -- assert( p->eCurType==CURTYPE_BTREE ); -- if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ -- return handleMovedCursor(p); -- } -- return SQLITE_OK; --} -- --/* --** Make sure the cursor p is ready to read or write the row to which it --** was last positioned. Return an error code if an OOM fault or I/O error --** prevents us from positioning the cursor to its correct position. --** --** If a MoveTo operation is pending on the given cursor, then do that --** MoveTo now. If no move is pending, check to see if the row has been --** deleted out from under the cursor and if it has, mark the row as --** a NULL row. --** --** If the cursor is already pointing to the correct row and that row has --** not been deleted out from under the cursor, then this routine is a no-op. --*/ --SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ -- VdbeCursor *p = *pp; -- assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); -- if( p->deferredMoveto ){ -- u32 iMap; -- if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ -- *pp = p->pAltCursor; -- *piCol = iMap - 1; -- return SQLITE_OK; -- } -- return sqlite3VdbeFinishMoveto(p); -- } -+ assert( p->eCurType==CURTYPE_BTREE || IsNullCursor(p) ); - if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ -- return handleMovedCursor(p); -+ return sqlite3VdbeHandleMovedCursor(p); - } - return SQLITE_OK; - } -@@ -81324,7 +89737,7 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ - ** sqlite3VdbeSerialType() - ** sqlite3VdbeSerialTypeLen() - ** sqlite3VdbeSerialLen() --** sqlite3VdbeSerialPut() -+** sqlite3VdbeSerialPut() <--- in-lined into OP_MakeRecord as of 2022-04-02 - ** sqlite3VdbeSerialGet() - ** - ** encapsulate the code that serializes values for storage in SQLite -@@ -81436,7 +89849,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){ - /* - ** The sizes for serial types less than 128 - */ --static const u8 sqlite3SmallTypeSizes[] = { -+SQLITE_PRIVATE const u8 sqlite3SmallTypeSizes[128] = { - /* 0 1 2 3 4 5 6 7 8 9 */ - /* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, - /* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, -@@ -81505,7 +89918,7 @@ SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){ - ** so we trust him. - */ - #ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT --static u64 floatSwap(u64 in){ -+SQLITE_PRIVATE u64 sqlite3FloatSwap(u64 in){ - union { - u64 r; - u32 i[2]; -@@ -81518,59 +89931,8 @@ static u64 floatSwap(u64 in){ - u.i[1] = t; - return u.r; - } --# define swapMixedEndianFloat(X) X = floatSwap(X) --#else --# define swapMixedEndianFloat(X) --#endif -+#endif /* SQLITE_MIXED_ENDIAN_64BIT_FLOAT */ - --/* --** Write the serialized data blob for the value stored in pMem into --** buf. It is assumed that the caller has allocated sufficient space. --** Return the number of bytes written. --** --** nBuf is the amount of space left in buf[]. The caller is responsible --** for allocating enough space to buf[] to hold the entire field, exclusive --** of the pMem->u.nZero bytes for a MEM_Zero value. --** --** Return the number of bytes actually written into buf[]. The number --** of bytes in the zero-filled tail is included in the return value only --** if those bytes were zeroed in buf[]. --*/ --SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ -- u32 len; -- -- /* Integer and Real */ -- if( serial_type<=7 && serial_type>0 ){ -- u64 v; -- u32 i; -- if( serial_type==7 ){ -- assert( sizeof(v)==sizeof(pMem->u.r) ); -- memcpy(&v, &pMem->u.r, sizeof(v)); -- swapMixedEndianFloat(v); -- }else{ -- v = pMem->u.i; -- } -- len = i = sqlite3SmallTypeSizes[serial_type]; -- assert( i>0 ); -- do{ -- buf[--i] = (u8)(v&0xFF); -- v >>= 8; -- }while( i ); -- return len; -- } -- -- /* String or blob */ -- if( serial_type>=12 ){ -- assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0) -- == (int)sqlite3VdbeSerialTypeLen(serial_type) ); -- len = pMem->n; -- if( len>0 ) memcpy(buf, pMem->z, len); -- return len; -- } -- -- /* NULL or constants 0 or 1 */ -- return 0; --} - - /* Input "x" is a sequence of unsigned characters that represent a - ** big-endian integer. Return the equivalent native integer -@@ -81583,14 +89945,14 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ - - /* - ** Deserialize the data blob pointed to by buf as serial type serial_type --** and store the result in pMem. Return the number of bytes read. -+** and store the result in pMem. - ** - ** This function is implemented as two separate routines for performance. - ** The few cases that require local variables are broken out into a separate - ** routine so that in most cases the overhead of moving the stack pointer - ** is avoided. - */ --static u32 serialGet( -+static void serialGet( - const unsigned char *buf, /* Buffer to deserialize from */ - u32 serial_type, /* Serial type to deserialize */ - Mem *pMem /* Memory cell to write value into */ -@@ -81624,9 +89986,25 @@ static u32 serialGet( - memcpy(&pMem->u.r, &x, sizeof(x)); - pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; - } -- return 8; - } --SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( -+static int serialGet7( -+ const unsigned char *buf, /* Buffer to deserialize from */ -+ Mem *pMem /* Memory cell to write value into */ -+){ -+ u64 x = FOUR_BYTE_UINT(buf); -+ u32 y = FOUR_BYTE_UINT(buf+4); -+ x = (x<<32) + y; -+ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); -+ swapMixedEndianFloat(x); -+ memcpy(&pMem->u.r, &x, sizeof(x)); -+ if( IsNaN(x) ){ -+ pMem->flags = MEM_Null; -+ return 1; -+ } -+ pMem->flags = MEM_Real; -+ return 0; -+} -+SQLITE_PRIVATE void sqlite3VdbeSerialGet( - const unsigned char *buf, /* Buffer to deserialize from */ - u32 serial_type, /* Serial type to deserialize */ - Mem *pMem /* Memory cell to write value into */ -@@ -81637,13 +90015,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - pMem->flags = MEM_Null|MEM_Zero; - pMem->n = 0; - pMem->u.nZero = 0; -- break; -+ return; - } - case 11: /* Reserved for future use */ - case 0: { /* Null */ - /* EVIDENCE-OF: R-24078-09375 Value is a NULL. */ - pMem->flags = MEM_Null; -- break; -+ return; - } - case 1: { - /* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement -@@ -81651,7 +90029,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - pMem->u.i = ONE_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); -- return 1; -+ return; - } - case 2: { /* 2-byte signed integer */ - /* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit -@@ -81659,7 +90037,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - pMem->u.i = TWO_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); -- return 2; -+ return; - } - case 3: { /* 3-byte signed integer */ - /* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit -@@ -81667,7 +90045,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - pMem->u.i = THREE_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); -- return 3; -+ return; - } - case 4: { /* 4-byte signed integer */ - /* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit -@@ -81679,7 +90057,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - #endif - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); -- return 4; -+ return; - } - case 5: { /* 6-byte signed integer */ - /* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit -@@ -81687,13 +90065,14 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf); - pMem->flags = MEM_Int; - testcase( pMem->u.i<0 ); -- return 6; -+ return; - } - case 6: /* 8-byte signed integer */ - case 7: { /* IEEE floating point */ - /* These use local variables, so do them in a separate routine - ** to avoid having to move the frame pointer in the common case */ -- return serialGet(buf,serial_type,pMem); -+ serialGet(buf,serial_type,pMem); -+ return; - } - case 8: /* Integer 0 */ - case 9: { /* Integer 1 */ -@@ -81701,7 +90080,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - /* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */ - pMem->u.i = serial_type-8; - pMem->flags = MEM_Int; -- return 0; -+ return; - } - default: { - /* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in -@@ -81712,10 +90091,10 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( - pMem->z = (char *)buf; - pMem->n = (serial_type-12)/2; - pMem->flags = aFlag[serial_type&1]; -- return pMem->n; -+ return; - } - } -- return 0; -+ return; - } - /* - ** This routine is used to allocate sufficient space for an UnpackedRecord -@@ -81736,10 +90115,11 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord( - ){ - UnpackedRecord *p; /* Unpacked record to return */ - int nByte; /* Number of bytes required for *p */ -- nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); -+ assert( sizeof(UnpackedRecord) + sizeof(Mem)*65536 < 0x7fffffff ); -+ nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1); - p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte); - if( !p ) return 0; -- p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; -+ p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))]; - assert( pKeyInfo->aSortFlags!=0 ); - p->pKeyInfo = pKeyInfo; - p->nField = pKeyInfo->nKeyField + 1; -@@ -81778,7 +90158,8 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( - /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */ - pMem->szMalloc = 0; - pMem->z = 0; -- d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); -+ sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); -+ d += sqlite3VdbeSerialTypeLen(serial_type); - pMem++; - if( (++u)>=p->nField ) break; - } -@@ -81857,12 +90238,22 @@ static int vdbeRecordCompareDebug( - if( d1+(u64)serial_type1+2>(u64)nKey1 - && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1 - ){ -+ if( serial_type1>=1 -+ && serial_type1<=7 -+ && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)<=(u64)nKey1+8 -+ && CORRUPT_DB -+ ){ -+ return 1; /* corrupt record not detected by -+ ** sqlite3VdbeRecordCompareWithSkip(). Return true -+ ** to avoid firing the assert() */ -+ } - break; - } - - /* Extract the values to be compared. - */ -- d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); -+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); -+ d1 += sqlite3VdbeSerialTypeLen(serial_type1); - - /* Do the comparison - */ -@@ -81973,8 +90364,8 @@ static int vdbeCompareMemString( - }else{ - rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2); - } -- sqlite3VdbeMemRelease(&c1); -- sqlite3VdbeMemRelease(&c2); -+ sqlite3VdbeMemReleaseMalloc(&c1); -+ sqlite3VdbeMemReleaseMalloc(&c2); - return rc; - } - } -@@ -82024,29 +90415,37 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem - return n1 - n2; - } - -+/* The following two functions are used only within testcase() to prove -+** test coverage. These functions do no exist for production builds. -+** We must use separate SQLITE_NOINLINE functions here, since otherwise -+** optimizer code movement causes gcov to become very confused. -+*/ -+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) -+static int SQLITE_NOINLINE doubleLt(double a, double b){ return a8 ){ -- LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; -- if( xr ) return +1; -- return 0; -+SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ -+ if( sqlite3IsNaN(r) ){ -+ /* SQLite considers NaN to be a NULL. And all integer values are greater -+ ** than NULL */ -+ return 1; - }else{ - i64 y; -- double s; - if( r<-9223372036854775808.0 ) return +1; - if( r>=9223372036854775808.0 ) return -1; - y = (i64)r; - if( iy ) return +1; -- s = (double)i; -- if( sr ) return +1; -- return 0; -+ testcase( doubleLt(((double)i),r) ); -+ testcase( doubleLt(r,((double)i)) ); -+ testcase( doubleEq(r,((double)i)) ); -+ return (((double)i)r); - } - } - -@@ -82232,14 +90631,22 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( - ** two elements in the keys are equal. Fix the various stack variables so - ** that this routine begins comparing at the second field. */ - if( bSkip ){ -- u32 s1; -- idx1 = 1 + getVarint32(&aKey1[1], s1); -+ u32 s1 = aKey1[1]; -+ if( s1<0x80 ){ -+ idx1 = 2; -+ }else{ -+ idx1 = 1 + sqlite3GetVarint32(&aKey1[1], &s1); -+ } - szHdr1 = aKey1[0]; - d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1); - i = 1; - pRhs++; - }else{ -- idx1 = getVarint32(aKey1, szHdr1); -+ if( (szHdr1 = aKey1[0])<0x80 ){ -+ idx1 = 1; -+ }else{ -+ idx1 = sqlite3GetVarint32(aKey1, &szHdr1); -+ } - d1 = szHdr1; - i = 0; - } -@@ -82254,7 +90661,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( - assert( pPKey2->pKeyInfo->aSortFlags!=0 ); - assert( pPKey2->pKeyInfo->nKeyField>0 ); - assert( idx1<=szHdr1 || CORRUPT_DB ); -- do{ -+ while( 1 /*exit-by-break*/ ){ - u32 serial_type; - - /* RHS is an integer */ -@@ -82264,11 +90671,11 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( - serial_type = aKey1[idx1]; - testcase( serial_type==12 ); - if( serial_type>=10 ){ -- rc = +1; -+ rc = serial_type==10 ? -1 : +1; - }else if( serial_type==0 ){ - rc = -1; - }else if( serial_type==7 ){ -- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); -+ serialGet7(&aKey1[d1], &mem1); - rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); - }else{ - i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); -@@ -82288,19 +90695,23 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( - /* Serial types 12 or greater are strings and blobs (greater than - ** numbers). Types 10 and 11 are currently "reserved for future - ** use", so it doesn't really matter what the results of comparing -- ** them to numberic values are. */ -- rc = +1; -+ ** them to numeric values are. */ -+ rc = serial_type==10 ? -1 : +1; - }else if( serial_type==0 ){ - rc = -1; - }else{ -- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); - if( serial_type==7 ){ -- if( mem1.u.ru.r ){ -+ if( serialGet7(&aKey1[d1], &mem1) ){ -+ rc = -1; /* mem1 is a NaN */ -+ }else if( mem1.u.ru.r ){ - rc = -1; - }else if( mem1.u.r>pRhs->u.r ){ - rc = +1; -+ }else{ -+ assert( rc==0 ); - } - }else{ -+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); - rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); - } - } -@@ -82370,7 +90781,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( - /* RHS is null */ - else{ - serial_type = aKey1[idx1]; -- rc = (serial_type!=0); -+ if( serial_type==0 -+ || serial_type==10 -+ || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0) -+ ){ -+ assert( rc==0 ); -+ }else{ -+ rc = 1; -+ } - } - - if( rc!=0 ){ -@@ -82392,8 +90810,13 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( - if( i==pPKey2->nField ) break; - pRhs++; - d1 += sqlite3VdbeSerialTypeLen(serial_type); -+ if( d1>(unsigned)nKey1 ) break; - idx1 += sqlite3VarintLen(serial_type); -- }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 ); -+ if( idx1>=(unsigned)szHdr1 ){ -+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; -+ return 0; /* Corrupt index */ -+ } -+ } - - /* No memory allocation is ever used on mem1. Prove this using - ** the following assert(). If the assert() fails, it indicates a -@@ -82495,7 +90918,8 @@ static int vdbeRecordCompareInt( - return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2); - } - -- v = pPKey2->aMem[0].u.i; -+ assert( pPKey2->u.i == pPKey2->aMem[0].u.i ); -+ v = pPKey2->u.i; - if( v>lhs ){ - res = pPKey2->r1; - }else if( vaMem[0].flags & MEM_Str ); -+ assert( pPKey2->aMem[0].n == pPKey2->n ); -+ assert( pPKey2->aMem[0].z == pPKey2->u.z ); - vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); -- serial_type = (u8)(aKey1[1]); -- if( serial_type >= 0x80 ){ -- sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); -- } -+ serial_type = (signed char)(aKey1[1]); -+ -+vrcs_restart: - if( serial_type<12 ){ -+ if( serial_type<0 ){ -+ sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); -+ if( serial_type>=12 ) goto vrcs_restart; -+ assert( CORRUPT_DB ); -+ } - res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ - }else if( !(serial_type & 0x01) ){ - res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ -@@ -82549,15 +90979,15 @@ static int vdbeRecordCompareString( - pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; - return 0; /* Corruption */ - } -- nCmp = MIN( pPKey2->aMem[0].n, nStr ); -- res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp); -+ nCmp = MIN( pPKey2->n, nStr ); -+ res = memcmp(&aKey1[szHdr], pPKey2->u.z, nCmp); - - if( res>0 ){ - res = pPKey2->r2; - }else if( res<0 ){ - res = pPKey2->r1; - }else{ -- res = nStr - pPKey2->aMem[0].n; -+ res = nStr - pPKey2->n; - if( res==0 ){ - if( pPKey2->nField>1 ){ - res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); -@@ -82612,6 +91042,7 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){ - p->r2 = 1; - } - if( (flags & MEM_Int) ){ -+ p->u.i = p->aMem[0].u.i; - return vdbeRecordCompareInt; - } - testcase( flags & MEM_Real ); -@@ -82621,6 +91052,8 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){ - && p->pKeyInfo->aColl[0]==0 - ){ - assert( flags & MEM_Str ); -+ p->u.z = p->aMem[0].z; -+ p->n = p->aMem[0].n; - return vdbeRecordCompareString; - } - } -@@ -82663,7 +91096,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ - /* The index entry must begin with a header size */ - getVarint32NR((u8*)m.z, szHdr); - testcase( szHdr==3 ); -- testcase( szHdr==m.n ); -+ testcase( szHdr==(u32)m.n ); - testcase( szHdr>0x7fffffff ); - assert( m.n>=0 ); - if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){ -@@ -82693,14 +91126,14 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ - /* Fetch the integer off the end of the index record */ - sqlite3VdbeSerialGet((u8*)&m.z[m.n-lenRowid], typeRowid, &v); - *rowid = v.u.i; -- sqlite3VdbeMemRelease(&m); -+ sqlite3VdbeMemReleaseMalloc(&m); - return SQLITE_OK; - - /* Jump here if database corruption is detected after m has been - ** allocated. Free the m object and return SQLITE_CORRUPT. */ - idx_rowid_corruption: - testcase( m.szMalloc!=0 ); -- sqlite3VdbeMemRelease(&m); -+ sqlite3VdbeMemReleaseMalloc(&m); - return SQLITE_CORRUPT_BKPT; - } - -@@ -82742,7 +91175,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( - return rc; - } - *res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, pUnpacked, 0); -- sqlite3VdbeMemRelease(&m); -+ sqlite3VdbeMemReleaseMalloc(&m); - return SQLITE_OK; - } - -@@ -82750,7 +91183,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( - ** This routine sets the value to be returned by subsequent calls to - ** sqlite3_changes() on the database handle 'db'. - */ --SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){ -+SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, i64 nChange){ - assert( sqlite3_mutex_held(db->mutex) ); - db->nChange = nChange; - db->nTotalChange += nChange; -@@ -82784,7 +91217,7 @@ SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe *v){ - */ - SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){ - Vdbe *p; -- for(p = db->pVdbe; p; p=p->pNext){ -+ for(p = db->pVdbe; p; p=p->pVNext){ - p->expired = iCode+1; - } - } -@@ -82815,7 +91248,8 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff - assert( iVar>0 ); - if( v ){ - Mem *pMem = &v->aVar[iVar-1]; -- assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); -+ assert( (v->db->flags & SQLITE_EnableQPSG)==0 -+ || (v->db->mDbFlags & DBFLAG_InternalFunc)!=0 ); - if( 0==(pMem->flags & MEM_Null) ){ - sqlite3_value *pRet = sqlite3ValueNew(v->db); - if( pRet ){ -@@ -82835,7 +91269,8 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff - */ - SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ - assert( iVar>0 ); -- assert( (v->db->flags & SQLITE_EnableQPSG)==0 ); -+ assert( (v->db->flags & SQLITE_EnableQPSG)==0 -+ || (v->db->mDbFlags & DBFLAG_InternalFunc)!=0 ); - if( iVar>=32 ){ - v->expmask |= 0x80000000; - }else{ -@@ -82877,6 +91312,20 @@ SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context *pCtx){ - return 1; - } - -+#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) -+/* -+** This Walker callback is used to help verify that calls to -+** sqlite3BtreeCursorHint() with opcode BTREE_HINT_RANGE have -+** byte-code register values correctly initialized. -+*/ -+SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr){ -+ if( pExpr->op==TK_REGISTER ){ -+ assert( (pWalker->u.aMem[pExpr->iTable].flags & MEM_Undefined)==0 ); -+ } -+ return WRC_Continue; -+} -+#endif /* SQLITE_ENABLE_CURSOR_HINTS && SQLITE_DEBUG */ -+ - #ifndef SQLITE_OMIT_VIRTUALTABLE - /* - ** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored -@@ -82905,13 +91354,14 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){ - ** the vdbeUnpackRecord() function found in vdbeapi.c. - */ - static void vdbeFreeUnpacked(sqlite3 *db, int nField, UnpackedRecord *p){ -+ assert( db!=0 ); - if( p ){ - int i; - for(i=0; iaMem[i]; -- if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem); -+ if( pMem->zMalloc ) sqlite3VdbeMemReleaseMalloc(pMem); - } -- sqlite3DbFreeNN(db, p); -+ sqlite3DbNNFreeNN(db, p); - } - } - #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ -@@ -82930,13 +91380,24 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( - const char *zDb, /* Database name */ - Table *pTab, /* Modified table */ - i64 iKey1, /* Initial key value */ -- int iReg /* Register for new.* record */ -+ int iReg, /* Register for new.* record */ -+ int iBlobWrite - ){ - sqlite3 *db = v->db; - i64 iKey2; - PreUpdate preupdate; - const char *zTbl = pTab->zName; - static const u8 fakeSortOrder = 0; -+#ifdef SQLITE_DEBUG -+ int nRealCol; -+ if( pTab->tabFlags & TF_WithoutRowid ){ -+ nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn; -+ }else if( pTab->tabFlags & TF_HasVirtual ){ -+ nRealCol = pTab->nNVCol; -+ }else{ -+ nRealCol = pTab->nCol; -+ } -+#endif - - assert( db->pPreUpdate==0 ); - memset(&preupdate, 0, sizeof(PreUpdate)); -@@ -82951,34 +91412,46 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( - } - } - -- assert( pCsr->nField==pTab->nCol -- || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) -+ assert( pCsr!=0 ); -+ assert( pCsr->eCurType==CURTYPE_BTREE ); -+ assert( pCsr->nField==nRealCol -+ || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1) - ); - - preupdate.v = v; - preupdate.pCsr = pCsr; - preupdate.op = op; - preupdate.iNewReg = iReg; -- preupdate.keyinfo.db = db; -- preupdate.keyinfo.enc = ENC(db); -- preupdate.keyinfo.nKeyField = pTab->nCol; -- preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; -+ preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace; -+ preupdate.pKeyinfo->db = db; -+ preupdate.pKeyinfo->enc = ENC(db); -+ preupdate.pKeyinfo->nKeyField = pTab->nCol; -+ preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder; - preupdate.iKey1 = iKey1; - preupdate.iKey2 = iKey2; - preupdate.pTab = pTab; -+ preupdate.iBlobWrite = iBlobWrite; - - db->pPreUpdate = &preupdate; - db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); - db->pPreUpdate = 0; - sqlite3DbFree(db, preupdate.aRecord); -- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); -- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); -+ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked); -+ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked); -+ sqlite3VdbeMemRelease(&preupdate.oldipk); - if( preupdate.aNew ){ - int i; - for(i=0; inField; i++){ - sqlite3VdbeMemRelease(&preupdate.aNew[i]); - } -- sqlite3DbFreeNN(db, preupdate.aNew); -+ sqlite3DbNNFreeNN(db, preupdate.aNew); -+ } -+ if( preupdate.apDflt ){ -+ int i; -+ for(i=0; inCol; i++){ -+ sqlite3ValueFree(preupdate.apDflt[i]); -+ } -+ sqlite3DbFree(db, preupdate.apDflt); - } - } - #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ -@@ -83002,6 +91475,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( - */ - /* #include "sqliteInt.h" */ - /* #include "vdbeInt.h" */ -+/* #include "opcodes.h" */ - - #ifndef SQLITE_OMIT_DEPRECATED - /* -@@ -83049,7 +91523,6 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ - sqlite3_int64 iNow; - sqlite3_int64 iElapse; - assert( p->startTime>0 ); -- assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); - assert( db->init.busy==0 ); - assert( p->zSql!=0 ); - sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); -@@ -83095,7 +91568,9 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ - if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; - sqlite3_mutex_enter(db->mutex); - checkProfileCallback(db, v); -- rc = sqlite3VdbeFinalize(v); -+ assert( v->eVdbeState>=VDBE_READY_STATE ); -+ rc = sqlite3VdbeReset(v); -+ sqlite3VdbeDelete(v); - rc = sqlite3ApiExit(db, rc); - sqlite3LeaveMutexAndCloseZombie(db); - } -@@ -83136,7 +91611,15 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ - int rc = SQLITE_OK; - Vdbe *p = (Vdbe*)pStmt; - #if SQLITE_THREADSAFE -- sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; -+ sqlite3_mutex *mutex; -+#endif -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pStmt==0 ){ -+ return SQLITE_MISUSE_BKPT; -+ } -+#endif -+#if SQLITE_THREADSAFE -+ mutex = p->db->mutex; - #endif - sqlite3_mutex_enter(mutex); - for(i=0; inVar; i++){ -@@ -83255,7 +91738,7 @@ SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ - SQLITE_NULL, /* 0x1f (not possible) */ - SQLITE_FLOAT, /* 0x20 INTREAL */ - SQLITE_NULL, /* 0x21 (not possible) */ -- SQLITE_TEXT, /* 0x22 INTREAL + TEXT */ -+ SQLITE_FLOAT, /* 0x22 INTREAL + TEXT */ - SQLITE_NULL, /* 0x23 (not possible) */ - SQLITE_FLOAT, /* 0x24 (not possible) */ - SQLITE_NULL, /* 0x25 (not possible) */ -@@ -83303,6 +91786,9 @@ SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ - #endif - return aType[pVal->flags&MEM_AffMask]; - } -+SQLITE_API int sqlite3_value_encoding(sqlite3_value *pVal){ -+ return pVal->enc; -+} - - /* Return true if a parameter to xUpdate represents an unchanged column */ - SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){ -@@ -83332,6 +91818,9 @@ SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){ - sqlite3ValueFree(pNew); - pNew = 0; - } -+ }else if( pNew->flags & MEM_Null ){ -+ /* Do not duplicate pointer values */ -+ pNew->flags &= ~(MEM_Term|MEM_Subtype); - } - return pNew; - } -@@ -83349,11 +91838,11 @@ SQLITE_API void sqlite3_value_free(sqlite3_value *pOld){ - ** the function result. - ** - ** The setStrOrError() function calls sqlite3VdbeMemSetStr() to store the --** result as a string or blob but if the string or blob is too large, it --** then sets the error code to SQLITE_TOOBIG -+** result as a string or blob. Appropriate errors are set if the string/blob -+** is too big or if an OOM occurs. - ** - ** The invokeValueDestructor(P,X) routine invokes destructor function X() --** on value P is not going to be used and need to be destroyed. -+** on value P if P is not going to be used and need to be destroyed. - */ - static void setResultStrOrError( - sqlite3_context *pCtx, /* Function context */ -@@ -83362,14 +91851,28 @@ static void setResultStrOrError( - u8 enc, /* Encoding of z. 0 for BLOBs */ - void (*xDel)(void*) /* Destructor function */ - ){ -- if( sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){ -+ Mem *pOut = pCtx->pOut; -+ int rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); -+ if( rc ){ -+ if( rc==SQLITE_TOOBIG ){ -+ sqlite3_result_error_toobig(pCtx); -+ }else{ -+ /* The only errors possible from sqlite3VdbeMemSetStr are -+ ** SQLITE_TOOBIG and SQLITE_NOMEM */ -+ assert( rc==SQLITE_NOMEM ); -+ sqlite3_result_error_nomem(pCtx); -+ } -+ return; -+ } -+ sqlite3VdbeChangeEncoding(pOut, pCtx->enc); -+ if( sqlite3VdbeMemTooBig(pOut) ){ - sqlite3_result_error_toobig(pCtx); - } - } - static int invokeValueDestructor( - const void *p, /* Value to destroy */ - void (*xDel)(void*), /* The destructor */ -- sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */ -+ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */ - ){ - assert( xDel!=SQLITE_DYNAMIC ); - if( xDel==0 ){ -@@ -83379,7 +91882,14 @@ static int invokeValueDestructor( - }else{ - xDel((void*)p); - } -- if( pCtx ) sqlite3_result_error_toobig(pCtx); -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx!=0 ){ -+ sqlite3_result_error_toobig(pCtx); -+ } -+#else -+ assert( pCtx!=0 ); -+ sqlite3_result_error_toobig(pCtx); -+#endif - return SQLITE_TOOBIG; - } - SQLITE_API void sqlite3_result_blob( -@@ -83388,6 +91898,12 @@ SQLITE_API void sqlite3_result_blob( - int n, - void (*xDel)(void *) - ){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 || n<0 ){ -+ invokeValueDestructor(z, xDel, pCtx); -+ return; -+ } -+#endif - assert( n>=0 ); - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, 0, xDel); -@@ -83398,8 +91914,14 @@ SQLITE_API void sqlite3_result_blob64( - sqlite3_uint64 n, - void (*xDel)(void *) - ){ -- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - assert( xDel!=SQLITE_DYNAMIC ); -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ){ -+ invokeValueDestructor(z, xDel, 0); -+ return; -+ } -+#endif -+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - if( n>0x7fffffff ){ - (void)invokeValueDestructor(z, xDel, pCtx); - }else{ -@@ -83407,30 +91929,48 @@ SQLITE_API void sqlite3_result_blob64( - } - } - SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); - } - SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - pCtx->isError = SQLITE_ERROR; - sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); - } - #ifndef SQLITE_OMIT_UTF16 - SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - pCtx->isError = SQLITE_ERROR; - sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); - } - #endif - SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); - } - SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); - } - SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlite3VdbeMemSetNull(pCtx->pOut); - } -@@ -83440,14 +91980,37 @@ SQLITE_API void sqlite3_result_pointer( - const char *zPType, - void (*xDestructor)(void*) - ){ -- Mem *pOut = pCtx->pOut; -+ Mem *pOut; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ){ -+ invokeValueDestructor(pPtr, xDestructor, 0); -+ return; -+ } -+#endif -+ pOut = pCtx->pOut; - assert( sqlite3_mutex_held(pOut->db->mutex) ); - sqlite3VdbeMemRelease(pOut); - pOut->flags = MEM_Null; - sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor); - } - SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ -- Mem *pOut = pCtx->pOut; -+ Mem *pOut; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif -+#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0 -+ if( pCtx->pFunc!=0 -+ && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0 -+ ){ -+ char zErr[200]; -+ sqlite3_snprintf(sizeof(zErr), zErr, -+ "misuse of sqlite3_result_subtype() by %s()", -+ pCtx->pFunc->zName); -+ sqlite3_result_error(pCtx, zErr, -1); -+ return; -+ } -+#endif /* SQLITE_STRICT_SUBTYPE */ -+ pOut = pCtx->pOut; - assert( sqlite3_mutex_held(pOut->db->mutex) ); - pOut->eSubtype = eSubtype & 0xff; - pOut->flags |= MEM_Subtype; -@@ -83458,6 +92021,12 @@ SQLITE_API void sqlite3_result_text( - int n, - void (*xDel)(void *) - ){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ){ -+ invokeValueDestructor(z, xDel, 0); -+ return; -+ } -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); - } -@@ -83468,13 +92037,23 @@ SQLITE_API void sqlite3_result_text64( - void (*xDel)(void *), - unsigned char enc - ){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ){ -+ invokeValueDestructor(z, xDel, 0); -+ return; -+ } -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - assert( xDel!=SQLITE_DYNAMIC ); -- if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; -+ if( enc!=SQLITE_UTF8 ){ -+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; -+ n &= ~(u64)1; -+ } - if( n>0x7fffffff ){ - (void)invokeValueDestructor(z, xDel, pCtx); - }else{ - setResultStrOrError(pCtx, z, (int)n, enc, xDel); -+ sqlite3VdbeMemZeroTerminateIfAble(pCtx->pOut); - } - } - #ifndef SQLITE_OMIT_UTF16 -@@ -83485,7 +92064,7 @@ SQLITE_API void sqlite3_result_text16( - void (*xDel)(void *) - ){ - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); -- setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); -+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16NATIVE, xDel); - } - SQLITE_API void sqlite3_result_text16be( - sqlite3_context *pCtx, -@@ -83494,7 +92073,7 @@ SQLITE_API void sqlite3_result_text16be( - void (*xDel)(void *) - ){ - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); -- setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); -+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16BE, xDel); - } - SQLITE_API void sqlite3_result_text16le( - sqlite3_context *pCtx, -@@ -83503,39 +92082,68 @@ SQLITE_API void sqlite3_result_text16le( - void (*xDel)(void *) - ){ - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); -- setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); -+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16LE, xDel); - } - #endif /* SQLITE_OMIT_UTF16 */ - SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ -+ Mem *pOut; -+ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+ if( pValue==0 ){ -+ sqlite3_result_null(pCtx); -+ return; -+ } -+#endif -+ pOut = pCtx->pOut; - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); -- sqlite3VdbeMemCopy(pCtx->pOut, pValue); -+ sqlite3VdbeMemCopy(pOut, pValue); -+ sqlite3VdbeChangeEncoding(pOut, pCtx->enc); -+ if( sqlite3VdbeMemTooBig(pOut) ){ -+ sqlite3_result_error_toobig(pCtx); -+ } - } - SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ -- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); -- sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n); -+ sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0); - } - SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){ -- Mem *pOut = pCtx->pOut; -+ Mem *pOut; -+ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return SQLITE_MISUSE_BKPT; -+#endif -+ pOut = pCtx->pOut; - assert( sqlite3_mutex_held(pOut->db->mutex) ); - if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ -+ sqlite3_result_error_toobig(pCtx); - return SQLITE_TOOBIG; - } -+#ifndef SQLITE_OMIT_INCRBLOB - sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); - return SQLITE_OK; -+#else -+ return sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); -+#endif - } - SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - pCtx->isError = errCode ? errCode : -1; - #ifdef SQLITE_DEBUG - if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; - #endif - if( pCtx->pOut->flags & MEM_Null ){ -- sqlite3VdbeMemSetStr(pCtx->pOut, sqlite3ErrStr(errCode), -1, -- SQLITE_UTF8, SQLITE_STATIC); -+ setResultStrOrError(pCtx, sqlite3ErrStr(errCode), -1, SQLITE_UTF8, -+ SQLITE_STATIC); - } - } - - /* Force an SQLITE_TOOBIG error. */ - SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - pCtx->isError = SQLITE_TOOBIG; - sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, -@@ -83544,6 +92152,9 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ - - /* An SQLITE_NOMEM error. */ - SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - sqlite3VdbeMemSetNull(pCtx->pOut); - pCtx->isError = SQLITE_NOMEM_BKPT; -@@ -83604,80 +92215,83 @@ static int sqlite3Step(Vdbe *p){ - int rc; - - assert(p); -- if( p->magic!=VDBE_MAGIC_RUN ){ -- /* We used to require that sqlite3_reset() be called before retrying -- ** sqlite3_step() after any error or after SQLITE_DONE. But beginning -- ** with version 3.7.0, we changed this so that sqlite3_reset() would -- ** be called automatically instead of throwing the SQLITE_MISUSE error. -- ** This "automatic-reset" change is not technically an incompatibility, -- ** since any application that receives an SQLITE_MISUSE is broken by -- ** definition. -- ** -- ** Nevertheless, some published applications that were originally written -- ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE -- ** returns, and those were broken by the automatic-reset change. As a -- ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the -- ** legacy behavior of returning SQLITE_MISUSE for cases where the -- ** previous sqlite3_step() returned something other than a SQLITE_LOCKED -- ** or SQLITE_BUSY error. -- */ --#ifdef SQLITE_OMIT_AUTORESET -- if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ -- sqlite3_reset((sqlite3_stmt*)p); -- }else{ -- return SQLITE_MISUSE_BKPT; -- } --#else -- sqlite3_reset((sqlite3_stmt*)p); --#endif -- } -- -- /* Check that malloc() has not failed. If it has, return early. */ - db = p->db; -- if( db->mallocFailed ){ -- p->rc = SQLITE_NOMEM; -- return SQLITE_NOMEM_BKPT; -- } -+ if( p->eVdbeState!=VDBE_RUN_STATE ){ -+ restart_step: -+ if( p->eVdbeState==VDBE_READY_STATE ){ -+ if( p->expired ){ -+ p->rc = SQLITE_SCHEMA; -+ rc = SQLITE_ERROR; -+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ -+ /* If this statement was prepared using saved SQL and an -+ ** error has occurred, then return the error code in p->rc to the -+ ** caller. Set the error code in the database handle to the same -+ ** value. -+ */ -+ rc = sqlite3VdbeTransferError(p); -+ } -+ goto end_of_step; -+ } - -- if( p->pc<0 && p->expired ){ -- p->rc = SQLITE_SCHEMA; -- rc = SQLITE_ERROR; -- if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ -- /* If this statement was prepared using saved SQL and an -- ** error has occurred, then return the error code in p->rc to the -- ** caller. Set the error code in the database handle to the same value. -+ /* If there are no other statements currently running, then -+ ** reset the interrupt flag. This prevents a call to sqlite3_interrupt -+ ** from interrupting a statement that has not yet started. - */ -- rc = sqlite3VdbeTransferError(p); -- } -- goto end_of_step; -- } -- if( p->pc<0 ){ -- /* If there are no other statements currently running, then -- ** reset the interrupt flag. This prevents a call to sqlite3_interrupt -- ** from interrupting a statement that has not yet started. -- */ -- if( db->nVdbeActive==0 ){ -- AtomicStore(&db->u1.isInterrupted, 0); -- } -+ if( db->nVdbeActive==0 ){ -+ AtomicStore(&db->u1.isInterrupted, 0); -+ } - -- assert( db->nVdbeWrite>0 || db->autoCommit==0 -- || (db->nDeferredCons==0 && db->nDeferredImmCons==0) -- ); -+ assert( db->nVdbeWrite>0 || db->autoCommit==0 -+ || ((db->nDeferredCons + db->nDeferredImmCons)==0) -+ ); - - #ifndef SQLITE_OMIT_TRACE -- if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 -- && !db->init.busy && p->zSql ){ -- sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); -- }else{ -- assert( p->startTime==0 ); -- } -+ if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 -+ && !db->init.busy && p->zSql ){ -+ sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); -+ }else{ -+ assert( p->startTime==0 ); -+ } - #endif - -- db->nVdbeActive++; -- if( p->readOnly==0 ) db->nVdbeWrite++; -- if( p->bIsReader ) db->nVdbeRead++; -- p->pc = 0; -+ db->nVdbeActive++; -+ if( p->readOnly==0 ) db->nVdbeWrite++; -+ if( p->bIsReader ) db->nVdbeRead++; -+ p->pc = 0; -+ p->eVdbeState = VDBE_RUN_STATE; -+ }else -+ -+ if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){ -+ /* We used to require that sqlite3_reset() be called before retrying -+ ** sqlite3_step() after any error or after SQLITE_DONE. But beginning -+ ** with version 3.7.0, we changed this so that sqlite3_reset() would -+ ** be called automatically instead of throwing the SQLITE_MISUSE error. -+ ** This "automatic-reset" change is not technically an incompatibility, -+ ** since any application that receives an SQLITE_MISUSE is broken by -+ ** definition. -+ ** -+ ** Nevertheless, some published applications that were originally written -+ ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE -+ ** returns, and those were broken by the automatic-reset change. As a -+ ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the -+ ** legacy behavior of returning SQLITE_MISUSE for cases where the -+ ** previous sqlite3_step() returned something other than a SQLITE_LOCKED -+ ** or SQLITE_BUSY error. -+ */ -+#ifdef SQLITE_OMIT_AUTORESET -+ if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ -+ sqlite3_reset((sqlite3_stmt*)p); -+ }else{ -+ return SQLITE_MISUSE_BKPT; -+ } -+#else -+ sqlite3_reset((sqlite3_stmt*)p); -+#endif -+ assert( p->eVdbeState==VDBE_READY_STATE ); -+ goto restart_step; -+ } - } -+ - #ifdef SQLITE_DEBUG - p->rcApp = SQLITE_OK; - #endif -@@ -83692,12 +92306,17 @@ static int sqlite3Step(Vdbe *p){ - db->nVdbeExec--; - } - -- if( rc!=SQLITE_ROW ){ -+ if( rc==SQLITE_ROW ){ -+ assert( p->rc==SQLITE_OK ); -+ assert( db->mallocFailed==0 ); -+ db->errCode = SQLITE_ROW; -+ return SQLITE_ROW; -+ }else{ - #ifndef SQLITE_OMIT_TRACE - /* If the statement completed successfully, invoke the profile callback */ - checkProfileCallback(db, p); - #endif -- -+ p->pResultRow = 0; - if( rc==SQLITE_DONE && db->autoCommit ){ - assert( p->rc==SQLITE_OK ); - p->rc = doWalCallbacks(db); -@@ -83744,7 +92363,6 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ - } - db = v->db; - sqlite3_mutex_enter(db->mutex); -- v->doingRerun = 0; - while( (rc = sqlite3Step(v))==SQLITE_SCHEMA - && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){ - int savedPc = v->pc; -@@ -83770,7 +92388,13 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ - break; - } - sqlite3_reset(pStmt); -- if( savedPc>=0 ) v->doingRerun = 1; -+ if( savedPc>=0 ){ -+ /* Setting minWriteFileFormat to 254 is a signal to the OP_Init and -+ ** OP_Trace opcodes to *not* perform SQLITE_TRACE_STMT because it has -+ ** already been done once on a prior invocation that failed due to -+ ** SQLITE_SCHEMA. tag-20220401a */ -+ v->minWriteFileFormat = 254; -+ } - assert( v->expired==0 ); - } - sqlite3_mutex_leave(db->mutex); -@@ -83783,6 +92407,9 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ - ** pointer to it. - */ - SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( p==0 ) return 0; -+#endif - assert( p && p->pFunc ); - return p->pFunc->pUserData; - } -@@ -83798,7 +92425,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ - ** application defined function. - */ - SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( p==0 ) return 0; -+#else - assert( p && p->pOut ); -+#endif - return p->pOut->db; - } - -@@ -83817,10 +92448,96 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ - ** value, as a signal to the xUpdate routine that the column is unchanged. - */ - SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( p==0 ) return 0; -+#else - assert( p ); -+#endif - return sqlite3_value_nochange(p->pOut); - } - -+/* -+** The destructor function for a ValueList object. This needs to be -+** a separate function, unknowable to the application, to ensure that -+** calls to sqlite3_vtab_in_first()/sqlite3_vtab_in_next() that are not -+** preceded by activation of IN processing via sqlite3_vtab_int() do not -+** try to access a fake ValueList object inserted by a hostile extension. -+*/ -+SQLITE_PRIVATE void sqlite3VdbeValueListFree(void *pToDelete){ -+ sqlite3_free(pToDelete); -+} -+ -+/* -+** Implementation of sqlite3_vtab_in_first() (if bNext==0) and -+** sqlite3_vtab_in_next() (if bNext!=0). -+*/ -+static int valueFromValueList( -+ sqlite3_value *pVal, /* Pointer to the ValueList object */ -+ sqlite3_value **ppOut, /* Store the next value from the list here */ -+ int bNext /* 1 for _next(). 0 for _first() */ -+){ -+ int rc; -+ ValueList *pRhs; -+ -+ *ppOut = 0; -+ if( pVal==0 ) return SQLITE_MISUSE_BKPT; -+ if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){ -+ return SQLITE_ERROR; -+ }else{ -+ assert( (pVal->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) == -+ (MEM_Null|MEM_Term|MEM_Subtype) ); -+ assert( pVal->eSubtype=='p' ); -+ assert( pVal->u.zPType!=0 && strcmp(pVal->u.zPType,"ValueList")==0 ); -+ pRhs = (ValueList*)pVal->z; -+ } -+ if( bNext ){ -+ rc = sqlite3BtreeNext(pRhs->pCsr, 0); -+ }else{ -+ int dummy = 0; -+ rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy); -+ assert( rc==SQLITE_OK || sqlite3BtreeEof(pRhs->pCsr) ); -+ if( sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE; -+ } -+ if( rc==SQLITE_OK ){ -+ u32 sz; /* Size of current row in bytes */ -+ Mem sMem; /* Raw content of current row */ -+ memset(&sMem, 0, sizeof(sMem)); -+ sz = sqlite3BtreePayloadSize(pRhs->pCsr); -+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem); -+ if( rc==SQLITE_OK ){ -+ u8 *zBuf = (u8*)sMem.z; -+ u32 iSerial; -+ sqlite3_value *pOut = pRhs->pOut; -+ int iOff = 1 + getVarint32(&zBuf[1], iSerial); -+ sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut); -+ pOut->enc = ENC(pOut->db); -+ if( (pOut->flags & MEM_Ephem)!=0 && sqlite3VdbeMemMakeWriteable(pOut) ){ -+ rc = SQLITE_NOMEM; -+ }else{ -+ *ppOut = pOut; -+ } -+ } -+ sqlite3VdbeMemRelease(&sMem); -+ } -+ return rc; -+} -+ -+/* -+** Set the iterator value pVal to point to the first value in the set. -+** Set (*ppOut) to point to this value before returning. -+*/ -+SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){ -+ return valueFromValueList(pVal, ppOut, 0); -+} -+ -+/* -+** Set the iterator value pVal to point to the next value in the set. -+** Set (*ppOut) to point to this value before returning. -+*/ -+SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut){ -+ return valueFromValueList(pVal, ppOut, 1); -+} -+ - /* - ** Return the current time for a statement. If the current time - ** is requested more than once within the same run of a single prepared -@@ -83894,6 +92611,9 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ - SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ - AuxData *pAuxData; - -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return 0; -+#endif - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - #if SQLITE_ENABLE_STAT4 - if( pCtx->pVdbe==0 ) return 0; -@@ -83926,8 +92646,12 @@ SQLITE_API void sqlite3_set_auxdata( - void (*xDelete)(void*) - ){ - AuxData *pAuxData; -- Vdbe *pVdbe = pCtx->pVdbe; -+ Vdbe *pVdbe; - -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pCtx==0 ) return; -+#endif -+ pVdbe= pCtx->pVdbe; - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); - #ifdef SQLITE_ENABLE_STAT4 - if( pVdbe==0 ) goto failed; -@@ -83983,7 +92707,8 @@ SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ - */ - SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; -- return pVm ? pVm->nResColumn : 0; -+ if( pVm==0 ) return 0; -+ return pVm->nResColumn; - } - - /* -@@ -83992,7 +92717,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ - */ - SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){ - Vdbe *pVm = (Vdbe *)pStmt; -- if( pVm==0 || pVm->pResultSet==0 ) return 0; -+ if( pVm==0 || pVm->pResultRow==0 ) return 0; - return pVm->nResColumn; - } - -@@ -84015,19 +92740,20 @@ static const Mem *columnNullValue(void){ - #endif - = { - /* .u = */ {0}, -+ /* .z = */ (char*)0, -+ /* .n = */ (int)0, - /* .flags = */ (u16)MEM_Null, - /* .enc = */ (u8)0, - /* .eSubtype = */ (u8)0, -- /* .n = */ (int)0, -- /* .z = */ (char*)0, -- /* .zMalloc = */ (char*)0, -+ /* .db = */ (sqlite3*)0, - /* .szMalloc = */ (int)0, - /* .uTemp = */ (u32)0, -- /* .db = */ (sqlite3*)0, -+ /* .zMalloc = */ (char*)0, - /* .xDel = */ (void(*)(void*))0, - #ifdef SQLITE_DEBUG - /* .pScopyFrom = */ (Mem*)0, - /* .mScopyFlags= */ 0, -+ /* .bScopy = */ 0, - #endif - }; - return &nullMem; -@@ -84047,8 +92773,8 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ - if( pVm==0 ) return (Mem*)columnNullValue(); - assert( pVm->db ); - sqlite3_mutex_enter(pVm->db->mutex); -- if( pVm->pResultSet!=0 && inResColumn && i>=0 ){ -- pOut = &pVm->pResultSet[i]; -+ if( pVm->pResultRow!=0 && inResColumn && i>=0 ){ -+ pOut = &pVm->pResultRow[i]; - }else{ - sqlite3Error(pVm->db, SQLITE_RANGE); - pOut = (Mem*)columnNullValue(); -@@ -84069,10 +92795,10 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ - ** sqlite3_column_int64() - ** sqlite3_column_text() - ** sqlite3_column_text16() --** sqlite3_column_real() -+** sqlite3_column_double() - ** sqlite3_column_bytes() - ** sqlite3_column_bytes16() --** sqiite3_column_blob() -+** sqlite3_column_blob() - */ - static void columnMallocFailure(sqlite3_stmt *pStmt) - { -@@ -84156,6 +92882,32 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ - return iType; - } - -+/* -+** Column names appropriate for EXPLAIN or EXPLAIN QUERY PLAN. -+*/ -+static const char * const azExplainColNames8[] = { -+ "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", /* EXPLAIN */ -+ "id", "parent", "notused", "detail" /* EQP */ -+}; -+static const u16 azExplainColNames16data[] = { -+ /* 0 */ 'a', 'd', 'd', 'r', 0, -+ /* 5 */ 'o', 'p', 'c', 'o', 'd', 'e', 0, -+ /* 12 */ 'p', '1', 0, -+ /* 15 */ 'p', '2', 0, -+ /* 18 */ 'p', '3', 0, -+ /* 21 */ 'p', '4', 0, -+ /* 24 */ 'p', '5', 0, -+ /* 27 */ 'c', 'o', 'm', 'm', 'e', 'n', 't', 0, -+ /* 35 */ 'i', 'd', 0, -+ /* 38 */ 'p', 'a', 'r', 'e', 'n', 't', 0, -+ /* 45 */ 'n', 'o', 't', 'u', 's', 'e', 'd', 0, -+ /* 53 */ 'd', 'e', 't', 'a', 'i', 'l', 0 -+}; -+static const u8 iExplainColNames16[] = { -+ 0, 5, 12, 15, 18, 21, 24, 27, -+ 35, 38, 45, 53 -+}; -+ - /* - ** Convert the N-th element of pStmt->pColName[] into a string using - ** xFunc() then return that string. If N is out of range, return 0. -@@ -84188,15 +92940,29 @@ static const void *columnName( - return 0; - } - #endif -+ if( N<0 ) return 0; - ret = 0; - p = (Vdbe *)pStmt; - db = p->db; - assert( db!=0 ); -- n = sqlite3_column_count(pStmt); -- if( N=0 ){ -+ sqlite3_mutex_enter(db->mutex); -+ -+ if( p->explain ){ -+ if( useType>0 ) goto columnName_end; -+ n = p->explain==1 ? 8 : 4; -+ if( N>=n ) goto columnName_end; -+ if( useUtf16 ){ -+ int i = iExplainColNames16[N + 8*p->explain - 8]; -+ ret = (void*)&azExplainColNames16data[i]; -+ }else{ -+ ret = (void*)azExplainColNames8[N + 8*p->explain - 8]; -+ } -+ goto columnName_end; -+ } -+ n = p->nResColumn; -+ if( NmallocFailed; - N += useType*n; -- sqlite3_mutex_enter(db->mutex); -- assert( db->mallocFailed==0 ); - #ifndef SQLITE_OMIT_UTF16 - if( useUtf16 ){ - ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); -@@ -84208,12 +92974,14 @@ static const void *columnName( - /* A malloc may have failed inside of the _text() call. If this - ** is the case, clear the mallocFailed flag and return NULL. - */ -- if( db->mallocFailed ){ -+ assert( db->mallocFailed==0 || db->mallocFailed==1 ); -+ if( db->mallocFailed > prior_mallocFailed ){ - sqlite3OomClear(db); - ret = 0; - } -- sqlite3_mutex_leave(db->mutex); - } -+columnName_end: -+ sqlite3_mutex_leave(db->mutex); - return ret; - } - -@@ -84306,33 +93074,43 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ - /* - ** Unbind the value bound to variable i in virtual machine p. This is the - ** the same as binding a NULL value to the column. If the "i" parameter is --** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. -+** out of range, then SQLITE_RANGE is returned. Otherwise SQLITE_OK. - ** - ** A successful evaluation of this routine acquires the mutex on p. - ** the mutex is released if any kind of error occurs. - ** - ** The error code stored in database p->db is overwritten with the return - ** value in any case. -+** -+** (tag-20240917-01) If vdbeUnbind(p,(u32)(i-1)) returns SQLITE_OK, -+** that means all of the the following will be true: -+** -+** p!=0 -+** p->pVar!=0 -+** i>0 -+** i<=p->nVar -+** -+** An assert() is normally added after vdbeUnbind() to help static analyzers -+** realize this. - */ --static int vdbeUnbind(Vdbe *p, int i){ -+static int vdbeUnbind(Vdbe *p, unsigned int i){ - Mem *pVar; - if( vdbeSafetyNotNull(p) ){ - return SQLITE_MISUSE_BKPT; - } - sqlite3_mutex_enter(p->db->mutex); -- if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ -- sqlite3Error(p->db, SQLITE_MISUSE); -+ if( p->eVdbeState!=VDBE_READY_STATE ){ -+ sqlite3Error(p->db, SQLITE_MISUSE_BKPT); - sqlite3_mutex_leave(p->db->mutex); - sqlite3_log(SQLITE_MISUSE, - "bind on a busy prepared statement: [%s]", p->zSql); - return SQLITE_MISUSE_BKPT; - } -- if( i<1 || i>p->nVar ){ -+ if( i>=(unsigned int)p->nVar ){ - sqlite3Error(p->db, SQLITE_RANGE); - sqlite3_mutex_leave(p->db->mutex); - return SQLITE_RANGE; - } -- i--; - pVar = &p->aVar[i]; - sqlite3VdbeMemRelease(pVar); - pVar->flags = MEM_Null; -@@ -84361,7 +93139,7 @@ static int bindText( - sqlite3_stmt *pStmt, /* The statement to bind against */ - int i, /* Index of the parameter to bind */ - const void *zData, /* Pointer to the data to be bound */ -- int nData, /* Number of bytes of data to be bound */ -+ i64 nData, /* Number of bytes of data to be bound */ - void (*xDel)(void*), /* Destructor for the data */ - u8 encoding /* Encoding for the data */ - ){ -@@ -84369,8 +93147,9 @@ static int bindText( - Mem *pVar; - int rc; - -- rc = vdbeUnbind(p, i); -+ rc = vdbeUnbind(p, (u32)(i-1)); - if( rc==SQLITE_OK ){ -+ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ - if( zData!=0 ){ - pVar = &p->aVar[i-1]; - rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); -@@ -84413,17 +93192,14 @@ SQLITE_API int sqlite3_bind_blob64( - void (*xDel)(void*) - ){ - assert( xDel!=SQLITE_DYNAMIC ); -- if( nData>0x7fffffff ){ -- return invokeValueDestructor(zData, xDel, 0); -- }else{ -- return bindText(pStmt, i, zData, (int)nData, xDel, 0); -- } -+ return bindText(pStmt, i, zData, nData, xDel, 0); - } - SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; -- rc = vdbeUnbind(p, i); -+ rc = vdbeUnbind(p, (u32)(i-1)); - if( rc==SQLITE_OK ){ -+ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ - sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); - sqlite3_mutex_leave(p->db->mutex); - } -@@ -84435,8 +93211,9 @@ SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ - SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ - int rc; - Vdbe *p = (Vdbe *)pStmt; -- rc = vdbeUnbind(p, i); -+ rc = vdbeUnbind(p, (u32)(i-1)); - if( rc==SQLITE_OK ){ -+ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ - sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); - sqlite3_mutex_leave(p->db->mutex); - } -@@ -84445,8 +93222,9 @@ SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValu - SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ - int rc; - Vdbe *p = (Vdbe*)pStmt; -- rc = vdbeUnbind(p, i); -+ rc = vdbeUnbind(p, (u32)(i-1)); - if( rc==SQLITE_OK ){ -+ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ - sqlite3_mutex_leave(p->db->mutex); - } - return rc; -@@ -84460,8 +93238,9 @@ SQLITE_API int sqlite3_bind_pointer( - ){ - int rc; - Vdbe *p = (Vdbe*)pStmt; -- rc = vdbeUnbind(p, i); -+ rc = vdbeUnbind(p, (u32)(i-1)); - if( rc==SQLITE_OK ){ -+ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ - sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); - sqlite3_mutex_leave(p->db->mutex); - }else if( xDestructor ){ -@@ -84487,22 +93266,21 @@ SQLITE_API int sqlite3_bind_text64( - unsigned char enc - ){ - assert( xDel!=SQLITE_DYNAMIC ); -- if( nData>0x7fffffff ){ -- return invokeValueDestructor(zData, xDel, 0); -- }else{ -+ if( enc!=SQLITE_UTF8 ){ - if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; -- return bindText(pStmt, i, zData, (int)nData, xDel, enc); -+ nData &= ~(u64)1; - } -+ return bindText(pStmt, i, zData, nData, xDel, enc); - } - #ifndef SQLITE_OMIT_UTF16 - SQLITE_API int sqlite3_bind_text16( - sqlite3_stmt *pStmt, - int i, - const void *zData, -- int nData, -+ int n, - void (*xDel)(void*) - ){ -- return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); -+ return bindText(pStmt, i, zData, n & ~(u64)1, xDel, SQLITE_UTF16NATIVE); - } - #endif /* SQLITE_OMIT_UTF16 */ - SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ -@@ -84513,7 +93291,10 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu - break; - } - case SQLITE_FLOAT: { -- rc = sqlite3_bind_double(pStmt, i, pValue->u.r); -+ assert( pValue->flags & (MEM_Real|MEM_IntReal) ); -+ rc = sqlite3_bind_double(pStmt, i, -+ (pValue->flags & MEM_Real) ? pValue->u.r : (double)pValue->u.i -+ ); - break; - } - case SQLITE_BLOB: { -@@ -84539,9 +93320,14 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu - SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ - int rc; - Vdbe *p = (Vdbe *)pStmt; -- rc = vdbeUnbind(p, i); -+ rc = vdbeUnbind(p, (u32)(i-1)); - if( rc==SQLITE_OK ){ -+ assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ -+#ifndef SQLITE_OMIT_INCRBLOB - sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); -+#else -+ rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); -+#endif - sqlite3_mutex_leave(p->db->mutex); - } - return rc; -@@ -84549,6 +93335,9 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ - SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){ - int rc; - Vdbe *p = (Vdbe *)pStmt; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( p==0 ) return SQLITE_MISUSE_BKPT; -+#endif - sqlite3_mutex_enter(p->db->mutex); - if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){ - rc = SQLITE_TOOBIG; -@@ -84669,12 +93458,48 @@ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){ - return pStmt ? ((Vdbe*)pStmt)->explain : 0; - } - -+/* -+** Set the explain mode for a statement. -+*/ -+SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){ -+ Vdbe *v = (Vdbe*)pStmt; -+ int rc; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( pStmt==0 ) return SQLITE_MISUSE_BKPT; -+#endif -+ sqlite3_mutex_enter(v->db->mutex); -+ if( ((int)v->explain)==eMode ){ -+ rc = SQLITE_OK; -+ }else if( eMode<0 || eMode>2 ){ -+ rc = SQLITE_ERROR; -+ }else if( (v->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){ -+ rc = SQLITE_ERROR; -+ }else if( v->eVdbeState!=VDBE_READY_STATE ){ -+ rc = SQLITE_BUSY; -+ }else if( v->nMem>=10 && (eMode!=2 || v->haveEqpOps) ){ -+ /* No reprepare necessary */ -+ v->explain = eMode; -+ rc = SQLITE_OK; -+ }else{ -+ v->explain = eMode; -+ rc = sqlite3Reprepare(v); -+ v->haveEqpOps = eMode==2; -+ } -+ if( v->explain ){ -+ v->nResColumn = 12 - 4*v->explain; -+ }else{ -+ v->nResColumn = v->nResAlloc; -+ } -+ sqlite3_mutex_leave(v->db->mutex); -+ return rc; -+} -+ - /* - ** Return true if the prepared statement is in need of being reset. - */ - SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ - Vdbe *v = (Vdbe*)pStmt; -- return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0; -+ return v!=0 && v->eVdbeState==VDBE_RUN_STATE; - } - - /* -@@ -84695,7 +93520,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ - if( pStmt==0 ){ - pNext = (sqlite3_stmt*)pDb->pVdbe; - }else{ -- pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pNext; -+ pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pVNext; - } - sqlite3_mutex_leave(pDb->mutex); - return pNext; -@@ -84720,9 +93545,11 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ - sqlite3_mutex_enter(db->mutex); - v = 0; - db->pnBytesFreed = (int*)&v; -- sqlite3VdbeClearObject(db, pVdbe); -- sqlite3DbFree(db, pVdbe); -+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); -+ db->lookaside.pEnd = db->lookaside.pStart; -+ sqlite3VdbeDelete(pVdbe); - db->pnBytesFreed = 0; -+ db->lookaside.pEnd = db->lookaside.pTrueEnd; - sqlite3_mutex_leave(db->mutex); - }else{ - v = pVdbe->aCounter[op]; -@@ -84806,10 +93633,17 @@ static UnpackedRecord *vdbeUnpackRecord( - ** a field of the row currently being updated or deleted. - */ - SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ -- PreUpdate *p = db->pPreUpdate; -+ PreUpdate *p; - Mem *pMem; - int rc = SQLITE_OK; -+ int iStore = 0; - -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( db==0 || ppValue==0 ){ -+ return SQLITE_MISUSE_BKPT; -+ } -+#endif -+ p = db->pPreUpdate; - /* Test that this call is being made from within an SQLITE_DELETE or - ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ - if( !p || p->op==SQLITE_INSERT ){ -@@ -84817,43 +93651,75 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa - goto preupdate_old_out; - } - if( p->pPk ){ -- iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); -+ iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); -+ }else{ -+ iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); - } -- if( iIdx>=p->pCsr->nField || iIdx<0 ){ -+ if( iStore>=p->pCsr->nField || iStore<0 ){ - rc = SQLITE_RANGE; - goto preupdate_old_out; - } - -- /* If the old.* record has not yet been loaded into memory, do so now. */ -- if( p->pUnpacked==0 ){ -- u32 nRec; -- u8 *aRec; -- -- nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); -- aRec = sqlite3DbMallocRaw(db, nRec); -- if( !aRec ) goto preupdate_old_out; -- rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); -- if( rc==SQLITE_OK ){ -- p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); -- if( !p->pUnpacked ) rc = SQLITE_NOMEM; -- } -- if( rc!=SQLITE_OK ){ -- sqlite3DbFree(db, aRec); -- goto preupdate_old_out; -- } -- p->aRecord = aRec; -- } -- -- pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; - if( iIdx==p->pTab->iPKey ){ -+ *ppValue = pMem = &p->oldipk; - sqlite3VdbeMemSetInt64(pMem, p->iKey1); -- }else if( iIdx>=p->pUnpacked->nField ){ -- *ppValue = (sqlite3_value *)columnNullValue(); -- }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ -- if( pMem->flags & (MEM_Int|MEM_IntReal) ){ -- testcase( pMem->flags & MEM_Int ); -- testcase( pMem->flags & MEM_IntReal ); -- sqlite3VdbeMemRealify(pMem); -+ }else{ -+ -+ /* If the old.* record has not yet been loaded into memory, do so now. */ -+ if( p->pUnpacked==0 ){ -+ u32 nRec; -+ u8 *aRec; -+ -+ assert( p->pCsr->eCurType==CURTYPE_BTREE ); -+ nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); -+ aRec = sqlite3DbMallocRaw(db, nRec); -+ if( !aRec ) goto preupdate_old_out; -+ rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); -+ if( rc==SQLITE_OK ){ -+ p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec); -+ if( !p->pUnpacked ) rc = SQLITE_NOMEM; -+ } -+ if( rc!=SQLITE_OK ){ -+ sqlite3DbFree(db, aRec); -+ goto preupdate_old_out; -+ } -+ p->aRecord = aRec; -+ } -+ -+ pMem = *ppValue = &p->pUnpacked->aMem[iStore]; -+ if( iStore>=p->pUnpacked->nField ){ -+ /* This occurs when the table has been extended using ALTER TABLE -+ ** ADD COLUMN. The value to return is the default value of the column. */ -+ Column *pCol = &p->pTab->aCol[iIdx]; -+ if( pCol->iDflt>0 ){ -+ if( p->apDflt==0 ){ -+ int nByte; -+ assert( sizeof(sqlite3_value*)*UMXV(p->pTab->nCol) < 0x7fffffff ); -+ nByte = sizeof(sqlite3_value*)*p->pTab->nCol; -+ p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte); -+ if( p->apDflt==0 ) goto preupdate_old_out; -+ } -+ if( p->apDflt[iIdx]==0 ){ -+ sqlite3_value *pVal = 0; -+ Expr *pDflt; -+ assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) ); -+ pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; -+ rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal); -+ if( rc==SQLITE_OK && pVal==0 ){ -+ rc = SQLITE_CORRUPT_BKPT; -+ } -+ p->apDflt[iIdx] = pVal; -+ } -+ *ppValue = p->apDflt[iIdx]; -+ }else{ -+ *ppValue = (sqlite3_value *)columnNullValue(); -+ } -+ }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ -+ if( pMem->flags & (MEM_Int|MEM_IntReal) ){ -+ testcase( pMem->flags & MEM_Int ); -+ testcase( pMem->flags & MEM_IntReal ); -+ sqlite3VdbeMemRealify(pMem); -+ } - } - } - -@@ -84869,8 +93735,13 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa - ** the number of columns in the row being updated, deleted or inserted. - */ - SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){ -- PreUpdate *p = db->pPreUpdate; -- return (p ? p->keyinfo.nKeyField : 0); -+ PreUpdate *p; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ p = db!=0 ? db->pPreUpdate : 0; -+#else -+ p = db->pPreUpdate; -+#endif -+ return (p ? p->pKeyinfo->nKeyField : 0); - } - #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ - -@@ -84887,29 +93758,60 @@ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){ - ** or SET DEFAULT action is considered a trigger. - */ - SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){ -- PreUpdate *p = db->pPreUpdate; -+ PreUpdate *p; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ p = db!=0 ? db->pPreUpdate : 0; -+#else -+ p = db->pPreUpdate; -+#endif - return (p ? p->v->nFrame : 0); - } - #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ - -+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -+/* -+** This function is designed to be called from within a pre-update callback -+** only. -+*/ -+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){ -+ PreUpdate *p; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ p = db!=0 ? db->pPreUpdate : 0; -+#else -+ p = db->pPreUpdate; -+#endif -+ return (p ? p->iBlobWrite : -1); -+} -+#endif -+ - #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - /* - ** This function is called from within a pre-update callback to retrieve - ** a field of the row currently being updated or inserted. - */ - SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ -- PreUpdate *p = db->pPreUpdate; -+ PreUpdate *p; - int rc = SQLITE_OK; - Mem *pMem; -+ int iStore = 0; - -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( db==0 || ppValue==0 ){ -+ return SQLITE_MISUSE_BKPT; -+ } -+#endif -+ p = db->pPreUpdate; - if( !p || p->op==SQLITE_DELETE ){ - rc = SQLITE_MISUSE_BKPT; - goto preupdate_new_out; - } - if( p->pPk && p->op!=SQLITE_UPDATE ){ -- iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); -+ iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); -+ }else{ -+ iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); - } -- if( iIdx>=p->pCsr->nField || iIdx<0 ){ -+ -+ if( iStore>=p->pCsr->nField || iStore<0 ){ - rc = SQLITE_RANGE; - goto preupdate_new_out; - } -@@ -84922,40 +93824,41 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa - Mem *pData = &p->v->aMem[p->iNewReg]; - rc = ExpandBlob(pData); - if( rc!=SQLITE_OK ) goto preupdate_new_out; -- pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); -+ pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z); - if( !pUnpack ){ - rc = SQLITE_NOMEM; - goto preupdate_new_out; - } - p->pNewUnpacked = pUnpack; - } -- pMem = &pUnpack->aMem[iIdx]; -+ pMem = &pUnpack->aMem[iStore]; - if( iIdx==p->pTab->iPKey ){ - sqlite3VdbeMemSetInt64(pMem, p->iKey2); -- }else if( iIdx>=pUnpack->nField ){ -+ }else if( iStore>=pUnpack->nField ){ - pMem = (sqlite3_value *)columnNullValue(); - } - }else{ -- /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required -+ /* For an UPDATE, memory cell (p->iNewReg+1+iStore) contains the required - ** value. Make a copy of the cell contents and return a pointer to it. - ** It is not safe to return a pointer to the memory cell itself as the - ** caller may modify the value text encoding. - */ - assert( p->op==SQLITE_UPDATE ); - if( !p->aNew ){ -- p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField); -+ assert( sizeof(Mem)*UMXV(p->pCsr->nField) < 0x7fffffff ); -+ p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem)*p->pCsr->nField); - if( !p->aNew ){ - rc = SQLITE_NOMEM; - goto preupdate_new_out; - } - } -- assert( iIdx>=0 && iIdxpCsr->nField ); -- pMem = &p->aNew[iIdx]; -+ assert( iStore>=0 && iStorepCsr->nField ); -+ pMem = &p->aNew[iStore]; - if( pMem->flags==0 ){ - if( iIdx==p->pTab->iPKey ){ - sqlite3VdbeMemSetInt64(pMem, p->iKey2); - }else{ -- rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); -+ rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iStore]); - if( rc!=SQLITE_OK ) goto preupdate_new_out; - } - } -@@ -84972,23 +93875,79 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa - /* - ** Return status data for a single loop within query pStmt. - */ --SQLITE_API int sqlite3_stmt_scanstatus( -+SQLITE_API int sqlite3_stmt_scanstatus_v2( - sqlite3_stmt *pStmt, /* Prepared statement being queried */ -- int idx, /* Index of loop to report on */ -+ int iScan, /* Index of loop to report on */ - int iScanStatusOp, /* Which metric to return */ -+ int flags, - void *pOut /* OUT: Write the answer here */ - ){ - Vdbe *p = (Vdbe*)pStmt; -- ScanStatus *pScan; -- if( idx<0 || idx>=p->nScan ) return 1; -+ VdbeOp *aOp; -+ int nOp; -+ ScanStatus *pScan = 0; -+ int idx; -+ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( p==0 || pOut==0 -+ || iScanStatusOpSQLITE_SCANSTAT_NCYCLE ){ -+ return 1; -+ } -+#endif -+ aOp = p->aOp; -+ nOp = p->nOp; -+ if( p->pFrame ){ -+ VdbeFrame *pFrame; -+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); -+ aOp = pFrame->aOp; -+ nOp = pFrame->nOp; -+ } -+ -+ if( iScan<0 ){ -+ int ii; -+ if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){ -+ i64 res = 0; -+ for(ii=0; iinScan; idx++){ -+ pScan = &p->aScan[idx]; -+ if( pScan->zName ){ -+ iScan--; -+ if( iScan<0 ) break; -+ } -+ } -+ } -+ if( idx>=p->nScan ) return 1; -+ assert( pScan==0 || pScan==&p->aScan[idx] ); - pScan = &p->aScan[idx]; -+ - switch( iScanStatusOp ){ - case SQLITE_SCANSTAT_NLOOP: { -- *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop]; -+ if( pScan->addrLoop>0 ){ -+ *(sqlite3_int64*)pOut = aOp[pScan->addrLoop].nExec; -+ }else{ -+ *(sqlite3_int64*)pOut = -1; -+ } - break; - } - case SQLITE_SCANSTAT_NVISIT: { -- *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit]; -+ if( pScan->addrVisit>0 ){ -+ *(sqlite3_int64*)pOut = aOp[pScan->addrVisit].nExec; -+ }else{ -+ *(sqlite3_int64*)pOut = -1; -+ } - break; - } - case SQLITE_SCANSTAT_EST: { -@@ -85007,7 +93966,7 @@ SQLITE_API int sqlite3_stmt_scanstatus( - } - case SQLITE_SCANSTAT_EXPLAIN: { - if( pScan->addrExplain ){ -- *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z; -+ *(const char**)pOut = aOp[ pScan->addrExplain ].p4.z; - }else{ - *(const char**)pOut = 0; - } -@@ -85015,12 +93974,51 @@ SQLITE_API int sqlite3_stmt_scanstatus( - } - case SQLITE_SCANSTAT_SELECTID: { - if( pScan->addrExplain ){ -- *(int*)pOut = p->aOp[ pScan->addrExplain ].p1; -+ *(int*)pOut = aOp[ pScan->addrExplain ].p1; -+ }else{ -+ *(int*)pOut = -1; -+ } -+ break; -+ } -+ case SQLITE_SCANSTAT_PARENTID: { -+ if( pScan->addrExplain ){ -+ *(int*)pOut = aOp[ pScan->addrExplain ].p2; - }else{ - *(int*)pOut = -1; - } - break; - } -+ case SQLITE_SCANSTAT_NCYCLE: { -+ i64 res = 0; -+ if( pScan->aAddrRange[0]==0 ){ -+ res = -1; -+ }else{ -+ int ii; -+ for(ii=0; iiaAddrRange); ii+=2){ -+ int iIns = pScan->aAddrRange[ii]; -+ int iEnd = pScan->aAddrRange[ii+1]; -+ if( iIns==0 ) break; -+ if( iIns>0 ){ -+ while( iIns<=iEnd ){ -+ res += aOp[iIns].nCycle; -+ iIns++; -+ } -+ }else{ -+ int iOp; -+ for(iOp=0; iOpp1!=iEnd ) continue; -+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){ -+ continue; -+ } -+ res += aOp[iOp].nCycle; -+ } -+ } -+ } -+ } -+ *(i64*)pOut = res; -+ break; -+ } - default: { - return 1; - } -@@ -85028,12 +94026,29 @@ SQLITE_API int sqlite3_stmt_scanstatus( - return 0; - } - -+/* -+** Return status data for a single loop within query pStmt. -+*/ -+SQLITE_API int sqlite3_stmt_scanstatus( -+ sqlite3_stmt *pStmt, /* Prepared statement being queried */ -+ int iScan, /* Index of loop to report on */ -+ int iScanStatusOp, /* Which metric to return */ -+ void *pOut /* OUT: Write the answer here */ -+){ -+ return sqlite3_stmt_scanstatus_v2(pStmt, iScan, iScanStatusOp, 0, pOut); -+} -+ - /* - ** Zero all counters associated with the sqlite3_stmt_scanstatus() data. - */ - SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; -- memset(p->anExec, 0, p->nOp * sizeof(i64)); -+ int ii; -+ for(ii=0; p!=0 && iinOp; ii++){ -+ Op *pOp = &p->aOp[ii]; -+ pOp->nExec = 0; -+ pOp->nCycle = 0; -+ } - } - #endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ - -@@ -85125,11 +94140,9 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( - #ifndef SQLITE_OMIT_UTF16 - Mem utf8; /* Used to convert UTF16 into UTF8 for display */ - #endif -- char zBase[100]; /* Initial working space */ - - db = p->db; -- sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase), -- db->aLimit[SQLITE_LIMIT_LENGTH]); -+ sqlite3StrAccumInit(&out, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); - if( db->nVdbeExec>1 ){ - while( *zRawSql ){ - const char *zStart = zRawSql; -@@ -85166,7 +94179,7 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( - assert( idx>0 ); - } - zRawSql += nToken; -- nextIndex = idx + 1; -+ nextIndex = MAX(idx + 1, nextIndex); - assert( idx>0 && idx<=p->nVar ); - pVar = &p->aVar[idx-1]; - if( pVar->flags & MEM_Null ){ -@@ -85259,6 +94272,104 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( - /* #include "sqliteInt.h" */ - /* #include "vdbeInt.h" */ - -+/* -+** High-resolution hardware timer used for debugging and testing only. -+*/ -+#if defined(VDBE_PROFILE) \ -+ || defined(SQLITE_PERFORMANCE_TRACE) \ -+ || defined(SQLITE_ENABLE_STMT_SCANSTATUS) -+/************** Include hwtime.h in the middle of vdbe.c *********************/ -+/************** Begin file hwtime.h ******************************************/ -+/* -+** 2008 May 27 -+** -+** 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 contains inline asm code for retrieving "high-performance" -+** counters for x86 and x86_64 class CPUs. -+*/ -+#ifndef SQLITE_HWTIME_H -+#define SQLITE_HWTIME_H -+ -+/* -+** The following routine only works on Pentium-class (or newer) processors. -+** It uses the RDTSC opcode to read the cycle count value out of the -+** processor and returns that value. This can be used for high-res -+** profiling. -+*/ -+#if !defined(__STRICT_ANSI__) && \ -+ (defined(__GNUC__) || defined(_MSC_VER)) && \ -+ (defined(i386) || defined(__i386__) || defined(_M_IX86)) -+ -+ #if defined(__GNUC__) -+ -+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ -+ unsigned int lo, hi; -+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); -+ return (sqlite_uint64)hi << 32 | lo; -+ } -+ -+ #elif defined(_MSC_VER) -+ -+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ -+ __asm { -+ rdtsc -+ ret ; return value at EDX:EAX -+ } -+ } -+ -+ #endif -+ -+#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) -+ -+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ -+ unsigned int lo, hi; -+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); -+ return (sqlite_uint64)hi << 32 | lo; -+ } -+ -+#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) -+ -+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ -+ unsigned long long retval; -+ unsigned long junk; -+ __asm__ __volatile__ ("\n\ -+ 1: mftbu %1\n\ -+ mftb %L0\n\ -+ mftbu %0\n\ -+ cmpw %0,%1\n\ -+ bne 1b" -+ : "=r" (retval), "=r" (junk)); -+ return retval; -+ } -+ -+#else -+ -+ /* -+ ** asm() is needed for hardware timing support. Without asm(), -+ ** disable the sqlite3Hwtime() routine. -+ ** -+ ** sqlite3Hwtime() is only used for some obscure debugging -+ ** and analysis configurations, not in any deliverable, so this -+ ** should not be a great loss. -+ */ -+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } -+ -+#endif -+ -+#endif /* !defined(SQLITE_HWTIME_H) */ -+ -+/************** End of hwtime.h **********************************************/ -+/************** Continuing where we left off in vdbe.c ***********************/ -+#endif -+ - /* - ** Invoke this macro on memory cells just prior to changing the - ** value of the cell. This macro verifies that shallow copies are -@@ -85370,8 +94481,12 @@ SQLITE_API int sqlite3_found_count = 0; - ** sqlite3CantopenError(lineno) - */ - static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ -- static int n = 0; -+ static u64 n = 0; -+ (void)pc; -+ (void)pOp; -+ (void)v; - n++; -+ if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */ - } - #endif - -@@ -85479,7 +94594,6 @@ static VdbeCursor *allocateCursor( - Vdbe *p, /* The virtual machine */ - int iCur, /* Index of the new VdbeCursor */ - int nField, /* Number of fields in the table or index */ -- int iDb, /* Database the cursor belongs to, or -1 */ - u8 eCurType /* Type of the new cursor */ - ){ - /* Find the memory cell that will be used to store the blob of memory -@@ -85502,34 +94616,46 @@ static VdbeCursor *allocateCursor( - */ - Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; - -- int nByte; -+ i64 nByte; - VdbeCursor *pCx = 0; -- nByte = -- ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + -- (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); -+ nByte = SZ_VDBECURSOR(nField); -+ assert( ROUND8(nByte)==nByte ); -+ if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize(); - - assert( iCur>=0 && iCurnCursor ); - if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ -- /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag -- ** is clear. Otherwise, if this is an ephemeral cursor created by -- ** OP_OpenDup, the cursor will not be closed and will still be part -- ** of a BtShared.pCursor list. */ -- if( p->apCsr[iCur]->pBtx==0 ) p->apCsr[iCur]->isEphemeral = 0; -- sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); -+ sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); - p->apCsr[iCur] = 0; - } -- if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ -- p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; -- memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); -- pCx->eCurType = eCurType; -- pCx->iDb = iDb; -- pCx->nField = nField; -- pCx->aOffset = &pCx->aType[nField]; -- if( eCurType==CURTYPE_BTREE ){ -- pCx->uc.pCursor = (BtCursor*) -- &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; -- sqlite3BtreeCursorZero(pCx->uc.pCursor); -+ -+ /* There used to be a call to sqlite3VdbeMemClearAndResize() to make sure -+ ** the pMem used to hold space for the cursor has enough storage available -+ ** in pMem->zMalloc. But for the special case of the aMem[] entries used -+ ** to hold cursors, it is faster to in-line the logic. */ -+ assert( pMem->flags==MEM_Undefined ); -+ assert( (pMem->flags & MEM_Dyn)==0 ); -+ assert( pMem->szMalloc==0 || pMem->z==pMem->zMalloc ); -+ if( pMem->szMallocszMalloc>0 ){ -+ sqlite3DbFreeNN(pMem->db, pMem->zMalloc); -+ } -+ pMem->z = pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, nByte); -+ if( pMem->zMalloc==0 ){ -+ pMem->szMalloc = 0; -+ return 0; - } -+ pMem->szMalloc = (int)nByte; -+ } -+ -+ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc; -+ memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); -+ pCx->eCurType = eCurType; -+ pCx->nField = nField; -+ pCx->aOffset = &pCx->aType[nField]; -+ if( eCurType==CURTYPE_BTREE ){ -+ assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) ); -+ pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)]; -+ sqlite3BtreeCursorZero(pCx->uc.pCursor); - } - return pCx; - } -@@ -85541,7 +94667,8 @@ static VdbeCursor *allocateCursor( - ** return false. - */ - static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){ -- i64 iValue = (double)rValue; -+ i64 iValue; -+ iValue = sqlite3RealToI64(rValue); - if( sqlite3RealSameAsInt(rValue,iValue) ){ - *piValue = iValue; - return 1; -@@ -85597,6 +94724,10 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){ - ** always preferred, even if the affinity is REAL, because - ** an integer representation is more space efficient on disk. - ** -+** SQLITE_AFF_FLEXNUM: -+** If the value is text, then try to convert it into a number of -+** some kind (integer or real) but do not make any other changes. -+** - ** SQLITE_AFF_TEXT: - ** Convert pRec to a text representation. - ** -@@ -85611,11 +94742,11 @@ static void applyAffinity( - ){ - if( affinity>=SQLITE_AFF_NUMERIC ){ - assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL -- || affinity==SQLITE_AFF_NUMERIC ); -+ || affinity==SQLITE_AFF_NUMERIC || affinity==SQLITE_AFF_FLEXNUM ); - if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/ -- if( (pRec->flags & MEM_Real)==0 ){ -+ if( (pRec->flags & (MEM_Real|MEM_IntReal))==0 ){ - if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1); -- }else{ -+ }else if( affinity<=SQLITE_AFF_REAL ){ - sqlite3VdbeIntegerAffinity(pRec); - } - } -@@ -85676,7 +94807,10 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ - sqlite3_int64 ix; - assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ); - assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); -- ExpandBlob(pMem); -+ if( ExpandBlob(pMem) ){ -+ pMem->u.i = 0; -+ return MEM_Int; -+ } - rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); - if( rc<=0 ){ - if( rc==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){ -@@ -85700,17 +94834,18 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ - ** But it does set pMem->u.r and pMem->u.i appropriately. - */ - static u16 numericType(Mem *pMem){ -- if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal) ){ -+ assert( (pMem->flags & MEM_Null)==0 -+ || pMem->db==0 || pMem->db->mallocFailed ); -+ if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null) ){ - testcase( pMem->flags & MEM_Int ); - testcase( pMem->flags & MEM_Real ); - testcase( pMem->flags & MEM_IntReal ); -- return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal); -- } -- if( pMem->flags & (MEM_Str|MEM_Blob) ){ -- testcase( pMem->flags & MEM_Str ); -- testcase( pMem->flags & MEM_Blob ); -- return computeNumericType(pMem); -+ return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null); - } -+ assert( pMem->flags & (MEM_Str|MEM_Blob) ); -+ testcase( pMem->flags & MEM_Str ); -+ testcase( pMem->flags & MEM_Blob ); -+ return computeNumericType(pMem); - return 0; - } - -@@ -85771,6 +94906,9 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){ - sqlite3_str_appendchar(pStr, 1, (c>=0x20&&c<=0x7f) ? c : '.'); - } - sqlite3_str_appendf(pStr, "]%s", encnames[pMem->enc]); -+ if( f & MEM_Term ){ -+ sqlite3_str_appendf(pStr, "(0-term)"); -+ } - } - } - #endif -@@ -85809,11 +94947,17 @@ static void registerTrace(int iReg, Mem *p){ - printf("R[%d] = ", iReg); - memTracePrint(p); - if( p->pScopyFrom ){ -+ assert( p->pScopyFrom->bScopy ); - printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg])); - } - printf("\n"); - sqlite3VdbeCheckMemInvariants(p); - } -+/**/ void sqlite3PrintMem(Mem *pMem){ -+ memTracePrint(pMem); -+ printf("\n"); -+ fflush(stdout); -+} - #endif - - #ifdef SQLITE_DEBUG -@@ -85834,106 +94978,6 @@ SQLITE_PRIVATE void sqlite3VdbeRegisterDump(Vdbe *v){ - # define REGISTER_TRACE(R,M) - #endif - -- --#ifdef VDBE_PROFILE -- --/* --** hwtime.h contains inline assembler code for implementing --** high-performance timing routines. --*/ --/************** Include hwtime.h in the middle of vdbe.c *********************/ --/************** Begin file hwtime.h ******************************************/ --/* --** 2008 May 27 --** --** 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 contains inline asm code for retrieving "high-performance" --** counters for x86 and x86_64 class CPUs. --*/ --#ifndef SQLITE_HWTIME_H --#define SQLITE_HWTIME_H -- --/* --** The following routine only works on pentium-class (or newer) processors. --** It uses the RDTSC opcode to read the cycle count value out of the --** processor and returns that value. This can be used for high-res --** profiling. --*/ --#if !defined(__STRICT_ANSI__) && \ -- (defined(__GNUC__) || defined(_MSC_VER)) && \ -- (defined(i386) || defined(__i386__) || defined(_M_IX86)) -- -- #if defined(__GNUC__) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned int lo, hi; -- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); -- return (sqlite_uint64)hi << 32 | lo; -- } -- -- #elif defined(_MSC_VER) -- -- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ -- __asm { -- rdtsc -- ret ; return value at EDX:EAX -- } -- } -- -- #endif -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long val; -- __asm__ __volatile__ ("rdtsc" : "=A" (val)); -- return val; -- } -- --#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) -- -- __inline__ sqlite_uint64 sqlite3Hwtime(void){ -- unsigned long long retval; -- unsigned long junk; -- __asm__ __volatile__ ("\n\ -- 1: mftbu %1\n\ -- mftb %L0\n\ -- mftbu %0\n\ -- cmpw %0,%1\n\ -- bne 1b" -- : "=r" (retval), "=r" (junk)); -- return retval; -- } -- --#else -- -- /* -- ** asm() is needed for hardware timing support. Without asm(), -- ** disable the sqlite3Hwtime() routine. -- ** -- ** sqlite3Hwtime() is only used for some obscure debugging -- ** and analysis configurations, not in any deliverable, so this -- ** should not be a great loss. -- */ --SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } -- --#endif -- --#endif /* !defined(SQLITE_HWTIME_H) */ -- --/************** End of hwtime.h **********************************************/ --/************** Continuing where we left off in vdbe.c ***********************/ -- --#endif -- - #ifndef NDEBUG - /* - ** This function is only called from within an assert() expression. It -@@ -85977,6 +95021,131 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ - } - } - -+/* -+** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning -+** with pOp->p3. Return the hash. -+*/ -+static u64 filterHash(const Mem *aMem, const Op *pOp){ -+ int i, mx; -+ u64 h = 0; -+ -+ assert( pOp->p4type==P4_INT32 ); -+ for(i=pOp->p3, mx=i+pOp->p4.i; iflags & (MEM_Int|MEM_IntReal) ){ -+ h += p->u.i; -+ }else if( p->flags & MEM_Real ){ -+ h += sqlite3VdbeIntValue(p); -+ }else if( p->flags & (MEM_Str|MEM_Blob) ){ -+ /* All strings have the same hash and all blobs have the same hash, -+ ** though, at least, those hashes are different from each other and -+ ** from NULL. */ -+ h += 4093 + (p->flags & (MEM_Str|MEM_Blob)); -+ } -+ } -+ return h; -+} -+ -+ -+/* -+** For OP_Column, factor out the case where content is loaded from -+** overflow pages, so that the code to implement this case is separate -+** the common case where all content fits on the page. Factoring out -+** the code reduces register pressure and helps the common case -+** to run faster. -+*/ -+static SQLITE_NOINLINE int vdbeColumnFromOverflow( -+ VdbeCursor *pC, /* The BTree cursor from which we are reading */ -+ int iCol, /* The column to read */ -+ int t, /* The serial-type code for the column value */ -+ i64 iOffset, /* Offset to the start of the content value */ -+ u32 cacheStatus, /* Current Vdbe.cacheCtr value */ -+ u32 colCacheCtr, /* Current value of the column cache counter */ -+ Mem *pDest /* Store the value into this register. */ -+){ -+ int rc; -+ sqlite3 *db = pDest->db; -+ int encoding = pDest->enc; -+ int len = sqlite3VdbeSerialTypeLen(t); -+ assert( pC->eCurType==CURTYPE_BTREE ); -+ if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) return SQLITE_TOOBIG; -+ if( len > 4000 && pC->pKeyInfo==0 ){ -+ /* Cache large column values that are on overflow pages using -+ ** an RCStr (reference counted string) so that if they are reloaded, -+ ** that do not have to be copied a second time. The overhead of -+ ** creating and managing the cache is such that this is only -+ ** profitable for larger TEXT and BLOB values. -+ ** -+ ** Only do this on table-btrees so that writes to index-btrees do not -+ ** need to clear the cache. This buys performance in the common case -+ ** in exchange for generality. -+ */ -+ VdbeTxtBlbCache *pCache; -+ char *pBuf; -+ if( pC->colCache==0 ){ -+ pC->pCache = sqlite3DbMallocZero(db, sizeof(VdbeTxtBlbCache) ); -+ if( pC->pCache==0 ) return SQLITE_NOMEM; -+ pC->colCache = 1; -+ } -+ pCache = pC->pCache; -+ if( pCache->pCValue==0 -+ || pCache->iCol!=iCol -+ || pCache->cacheStatus!=cacheStatus -+ || pCache->colCacheCtr!=colCacheCtr -+ || pCache->iOffset!=sqlite3BtreeOffset(pC->uc.pCursor) -+ ){ -+ if( pCache->pCValue ) sqlite3RCStrUnref(pCache->pCValue); -+ pBuf = pCache->pCValue = sqlite3RCStrNew( len+3 ); -+ if( pBuf==0 ) return SQLITE_NOMEM; -+ rc = sqlite3BtreePayload(pC->uc.pCursor, iOffset, len, pBuf); -+ if( rc ) return rc; -+ pBuf[len] = 0; -+ pBuf[len+1] = 0; -+ pBuf[len+2] = 0; -+ pCache->iCol = iCol; -+ pCache->cacheStatus = cacheStatus; -+ pCache->colCacheCtr = colCacheCtr; -+ pCache->iOffset = sqlite3BtreeOffset(pC->uc.pCursor); -+ }else{ -+ pBuf = pCache->pCValue; -+ } -+ assert( t>=12 ); -+ sqlite3RCStrRef(pBuf); -+ if( t&1 ){ -+ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding, -+ sqlite3RCStrUnref); -+ pDest->flags |= MEM_Term; -+ }else{ -+ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0, -+ sqlite3RCStrUnref); -+ } -+ }else{ -+ rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest); -+ if( rc ) return rc; -+ sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); -+ if( (t&1)!=0 && encoding==SQLITE_UTF8 ){ -+ pDest->z[len] = 0; -+ pDest->flags |= MEM_Term; -+ } -+ } -+ pDest->flags &= ~MEM_Ephem; -+ return rc; -+} -+ -+ -+/* -+** Return the symbolic name for the data type of a pMem -+*/ -+static const char *vdbeMemTypeName(Mem *pMem){ -+ static const char *azTypes[] = { -+ /* SQLITE_INTEGER */ "INT", -+ /* SQLITE_FLOAT */ "REAL", -+ /* SQLITE_TEXT */ "TEXT", -+ /* SQLITE_BLOB */ "BLOB", -+ /* SQLITE_NULL */ "NULL" -+ }; -+ return azTypes[sqlite3_value_type(pMem)-1]; -+} - - /* - ** Execute as much of a VDBE program as we can. -@@ -85987,11 +95156,10 @@ SQLITE_PRIVATE int sqlite3VdbeExec( - ){ - Op *aOp = p->aOp; /* Copy of p->aOp */ - Op *pOp = aOp; /* Current operation */ --#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) -- Op *pOrigOp; /* Value of pOp at the top of the loop */ --#endif - #ifdef SQLITE_DEBUG -+ Op *pOrigOp; /* Value of pOp at the top of the loop */ - int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */ -+ u8 iCompareIsInit = 0; /* iCompare is initialized */ - #endif - int rc = SQLITE_OK; /* Value to return */ - sqlite3 *db = p->db; /* The database */ -@@ -86007,13 +95175,17 @@ SQLITE_PRIVATE int sqlite3VdbeExec( - Mem *pIn2 = 0; /* 2nd input operand */ - Mem *pIn3 = 0; /* 3rd input operand */ - Mem *pOut = 0; /* Output operand */ --#ifdef VDBE_PROFILE -- u64 start; /* CPU clock count at start of opcode */ -+ u32 colCacheCtr = 0; /* Column cache counter */ -+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) -+ u64 *pnCycle = 0; -+ int bStmtScanStatus = IS_STMT_SCANSTATUS(db)!=0; - #endif - /*** INSERT STACK UNION HERE ***/ - -- assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ -- sqlite3VdbeEnter(p); -+ assert( p->eVdbeState==VDBE_RUN_STATE ); /* sqlite3_step() verifies this */ -+ if( DbMaskNonZero(p->lockMask) ){ -+ sqlite3VdbeEnter(p); -+ } - #ifndef SQLITE_OMIT_PROGRESS_CALLBACK - if( db->xProgress ){ - u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; -@@ -86034,7 +95206,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec( - assert( p->bIsReader || p->readOnly!=0 ); - p->iCurrentTime = 0; - assert( p->explain==0 ); -- p->pResultSet = 0; - db->busyHandler.nBusy = 0; - if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; - sqlite3VdbeIOTraceSql(p); -@@ -86071,12 +95242,18 @@ SQLITE_PRIVATE int sqlite3VdbeExec( - assert( rc==SQLITE_OK ); - - assert( pOp>=aOp && pOp<&aOp[p->nOp]); --#ifdef VDBE_PROFILE -- start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); --#endif - nVmStep++; --#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; -+ -+#if defined(VDBE_PROFILE) -+ pOp->nExec++; -+ pnCycle = &pOp->nCycle; -+ if( sqlite3NProfileCnt==0 ) *pnCycle -= sqlite3Hwtime(); -+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) -+ if( bStmtScanStatus ){ -+ pOp->nExec++; -+ pnCycle = &pOp->nCycle; -+ *pnCycle -= sqlite3Hwtime(); -+ } - #endif - - /* Only allow tracing if SQLITE_DEBUG is defined. -@@ -86138,7 +95315,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( - } - } - #endif --#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) -+#ifdef SQLITE_DEBUG - pOrigOp = pOp; - #endif - -@@ -86194,8 +95371,8 @@ SQLITE_PRIVATE int sqlite3VdbeExec( - case OP_Goto: { /* jump */ - - #ifdef SQLITE_DEBUG -- /* In debuggging mode, when the p5 flags is set on an OP_Goto, that -- ** means we should really jump back to the preceeding OP_ReleaseReg -+ /* In debugging mode, when the p5 flags is set on an OP_Goto, that -+ ** means we should really jump back to the preceding OP_ReleaseReg - ** instruction. */ - if( pOp->p5 ){ - assert( pOp->p2 < (int)(pOp - aOp) ); -@@ -86255,24 +95432,39 @@ case OP_Gosub: { /* jump */ - pIn1->flags = MEM_Int; - pIn1->u.i = (int)(pOp-aOp); - REGISTER_TRACE(pOp->p1, pIn1); -- -- /* Most jump operations do a goto to this spot in order to update -- ** the pOp pointer. */ --jump_to_p2: -- pOp = &aOp[pOp->p2 - 1]; -- break; -+ goto jump_to_p2_and_check_for_interrupt; - } - --/* Opcode: Return P1 * * * * -+/* Opcode: Return P1 P2 P3 * * -+** -+** Jump to the address stored in register P1. If P1 is a return address -+** register, then this accomplishes a return from a subroutine. -+** -+** If P3 is 1, then the jump is only taken if register P1 holds an integer -+** values, otherwise execution falls through to the next opcode, and the -+** OP_Return becomes a no-op. If P3 is 0, then register P1 must hold an -+** integer or else an assert() is raised. P3 should be set to 1 when -+** this opcode is used in combination with OP_BeginSubrtn, and set to 0 -+** otherwise. -+** -+** The value in register P1 is unchanged by this opcode. - ** --** Jump to the next instruction after the address in register P1. After --** the jump, register P1 becomes undefined. -+** P2 is not used by the byte-code engine. However, if P2 is positive -+** and also less than the current address, then the "EXPLAIN" output -+** formatter in the CLI will indent all opcodes from the P2 opcode up -+** to be not including the current Return. P2 should be the first opcode -+** in the subroutine from which this opcode is returning. Thus the P2 -+** value is a byte-code indentation hint. See tag-20220407a in -+** wherecode.c and shell.c. - */ - case OP_Return: { /* in1 */ - pIn1 = &aMem[pOp->p1]; -- assert( pIn1->flags==MEM_Int ); -- pOp = &aOp[pIn1->u.i]; -- pIn1->flags = MEM_Undefined; -+ if( pIn1->flags & MEM_Int ){ -+ if( pOp->p3 ){ VdbeBranchTaken(1, 2); } -+ pOp = &aOp[pIn1->u.i]; -+ }else if( ALWAYS(pOp->p3) ){ -+ VdbeBranchTaken(0, 2); -+ } - break; - } - -@@ -86287,7 +95479,7 @@ case OP_Return: { /* in1 */ - ** - ** See also: EndCoroutine - */ --case OP_InitCoroutine: { /* jump */ -+case OP_InitCoroutine: { /* jump0 */ - assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); - assert( pOp->p2>=0 && pOp->p2nOp ); - assert( pOp->p3>=0 && pOp->p3nOp ); -@@ -86295,7 +95487,14 @@ case OP_InitCoroutine: { /* jump */ - assert( !VdbeMemDynamic(pOut) ); - pOut->u.i = pOp->p3 - 1; - pOut->flags = MEM_Int; -- if( pOp->p2 ) goto jump_to_p2; -+ if( pOp->p2==0 ) break; -+ -+ /* Most jump operations do a goto to this spot in order to update -+ ** the pOp pointer. */ -+jump_to_p2: -+ assert( pOp->p2>0 ); /* There are never any jumps to instruction 0 */ -+ assert( pOp->p2nOp ); /* Jumps must be in range */ -+ pOp = &aOp[pOp->p2 - 1]; - break; - } - -@@ -86303,7 +95502,9 @@ case OP_InitCoroutine: { /* jump */ - ** - ** The instruction at the address in register P1 is a Yield. - ** Jump to the P2 parameter of that Yield. --** After the jump, register P1 becomes undefined. -+** After the jump, the value register P1 is left with a value -+** such that subsequent OP_Yields go back to the this same -+** OP_EndCoroutine instruction. - ** - ** See also: InitCoroutine - */ -@@ -86315,8 +95516,8 @@ case OP_EndCoroutine: { /* in1 */ - pCaller = &aOp[pIn1->u.i]; - assert( pCaller->opcode==OP_Yield ); - assert( pCaller->p2>=0 && pCaller->p2nOp ); -+ pIn1->u.i = (int)(pOp - p->aOp) - 1; - pOp = &aOp[pCaller->p2 - 1]; -- pIn1->flags = MEM_Undefined; - break; - } - -@@ -86333,7 +95534,7 @@ case OP_EndCoroutine: { /* in1 */ - ** - ** See also: InitCoroutine - */ --case OP_Yield: { /* in1, jump */ -+case OP_Yield: { /* in1, jump0 */ - int pcDest; - pIn1 = &aMem[pOp->p1]; - assert( VdbeMemDynamic(pIn1)==0 ); -@@ -86363,7 +95564,7 @@ case OP_HaltIfNull: { /* in3 */ - /* no break */ deliberate_fall_through - } - --/* Opcode: Halt P1 P2 * P4 P5 -+/* Opcode: Halt P1 P2 P3 P4 P5 - ** - ** Exit immediately. All open cursors, etc are closed - ** automatically. -@@ -86376,18 +95577,22 @@ case OP_HaltIfNull: { /* in3 */ - ** then back out all changes that have occurred during this execution of the - ** VDBE, but do not rollback the transaction. - ** --** If P4 is not null then it is an error message string. -+** If P3 is not zero and P4 is NULL, then P3 is a register that holds the -+** text of an error message. - ** --** P5 is a value between 0 and 4, inclusive, that modifies the P4 string. -+** If P3 is zero and P4 is not null then the error message string is held -+** in P4. - ** --** 0: (no change) --** 1: NOT NULL contraint failed: P4 -+** P5 is a value between 1 and 4, inclusive, then the P4 error message -+** string is modified as follows: -+** -+** 1: NOT NULL constraint failed: P4 - ** 2: UNIQUE constraint failed: P4 - ** 3: CHECK constraint failed: P4 - ** 4: FOREIGN KEY constraint failed: P4 - ** --** If P5 is not zero and P4 is NULL, then everything after the ":" is --** omitted. -+** If P3 is zero and P5 is not zero and P4 is NULL, then everything after -+** the ":" is omitted. - ** - ** There is an implied "Halt 0 0 0" instruction inserted at the very end of - ** every program. So a jump past the last instruction of the program -@@ -86397,11 +95602,19 @@ case OP_Halt: { - VdbeFrame *pFrame; - int pcx; - -- pcx = (int)(pOp - aOp); - #ifdef SQLITE_DEBUG - if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } - #endif -- if( pOp->p1==SQLITE_OK && p->pFrame ){ -+ assert( pOp->p4type==P4_NOTUSED -+ || pOp->p4type==P4_STATIC -+ || pOp->p4type==P4_DYNAMIC ); -+ -+ /* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates -+ ** something is wrong with the code generator. Raise an assertion in order -+ ** to bring this to the attention of fuzzers and other testing tools. */ -+ assert( pOp->p1!=SQLITE_INTERNAL ); -+ -+ if( p->pFrame && pOp->p1==SQLITE_OK ){ - /* Halt the sub-program. Return control to the parent frame. */ - pFrame = p->pFrame; - p->pFrame = pFrame->pParent; -@@ -86423,10 +95636,14 @@ case OP_Halt: { - } - p->rc = pOp->p1; - p->errorAction = (u8)pOp->p2; -- p->pc = pcx; - assert( pOp->p5<=4 ); - if( p->rc ){ -- if( pOp->p5 ){ -+ if( pOp->p3>0 && pOp->p4type==P4_NOTUSED ){ -+ const char *zErr; -+ assert( pOp->p3<=(p->nMem + 1 - p->nCursor) ); -+ zErr = sqlite3ValueText(&aMem[pOp->p3], SQLITE_UTF8); -+ sqlite3VdbeError(p, "%s", zErr); -+ }else if( pOp->p5 ){ - static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", - "FOREIGN KEY" }; - testcase( pOp->p5==1 ); -@@ -86440,7 +95657,8 @@ case OP_Halt: { - }else{ - sqlite3VdbeError(p, "%s", pOp->p4.z); - } -- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg); -+ pcx = (int)(pOp - aOp); -+ sqlite3_log(pOp->p1, "abort at %d: %s; [%s]", pcx, p->zErrMsg, p->zSql); - } - rc = sqlite3VdbeHalt(p); - assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); -@@ -86565,6 +95783,28 @@ case OP_String: { /* out2 */ - break; - } - -+/* Opcode: BeginSubrtn * P2 * * * -+** Synopsis: r[P2]=NULL -+** -+** Mark the beginning of a subroutine that can be entered in-line -+** or that can be called using OP_Gosub. The subroutine should -+** be terminated by an OP_Return instruction that has a P1 operand that -+** is the same as the P2 operand to this opcode and that has P3 set to 1. -+** If the subroutine is entered in-line, then the OP_Return will simply -+** fall through. But if the subroutine is entered using OP_Gosub, then -+** the OP_Return will jump back to the first instruction after the OP_Gosub. -+** -+** This routine works by loading a NULL into the P2 register. When the -+** return address register contains a NULL, the OP_Return instruction is -+** a no-op that simply falls through to the next instruction (assuming that -+** the OP_Return opcode has a P3 value of 1). Thus if the subroutine is -+** entered in-line, then the OP_Return will cause in-line execution to -+** continue. But if the subroutine is entered via OP_Gosub, then the -+** OP_Return will cause a return to the address following the OP_Gosub. -+** -+** This opcode is identical to OP_Null. It has a different name -+** only to make the byte code easier to read and verify. -+*/ - /* Opcode: Null P1 P2 P3 * * - ** Synopsis: r[P2..P3]=NULL - ** -@@ -86577,6 +95817,7 @@ case OP_String: { /* out2 */ - ** NULL values will not compare equal even if SQLITE_NULLEQ is set on - ** OP_Ne or OP_Eq. - */ -+case OP_BeginSubrtn: - case OP_Null: { /* out2 */ - int cnt; - u16 nullFlag; -@@ -86618,30 +95859,32 @@ case OP_SoftNull: { - ** Synopsis: r[P2]=P4 (len=P1) - ** - ** P4 points to a blob of data P1 bytes long. Store this --** blob in register P2. -+** blob in register P2. If P4 is a NULL pointer, then construct -+** a zero-filled blob that is P1 bytes long in P2. - */ - case OP_Blob: { /* out2 */ - assert( pOp->p1 <= SQLITE_MAX_LENGTH ); - pOut = out2Prerelease(p, pOp); -- sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); -+ if( pOp->p4.z==0 ){ -+ sqlite3VdbeMemSetZeroBlob(pOut, pOp->p1); -+ if( sqlite3VdbeMemExpandBlob(pOut) ) goto no_mem; -+ }else{ -+ sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); -+ } - pOut->enc = encoding; - UPDATE_MAX_BLOBSIZE(pOut); - break; - } - --/* Opcode: Variable P1 P2 * P4 * --** Synopsis: r[P2]=parameter(P1,P4) -+/* Opcode: Variable P1 P2 * * * -+** Synopsis: r[P2]=parameter(P1) - ** - ** Transfer the values of bound parameter P1 into register P2 --** --** If the parameter is named, then its name appears in P4. --** The P4 value is used by sqlite3_bind_parameter_name(). - */ - case OP_Variable: { /* out2 */ - Mem *pVar; /* Value being transferred */ - - assert( pOp->p1>0 && pOp->p1<=p->nVar ); -- assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) ); - pVar = &p->aVar[pOp->p1 - 1]; - if( sqlite3VdbeMemTooBig(pVar) ){ - goto too_big; -@@ -86688,6 +95931,7 @@ case OP_Move: { - { int i; - for(i=1; inMem; i++){ - if( aMem[i].pScopyFrom==pIn1 ){ -+ assert( aMem[i].bScopy ); - aMem[i].pScopyFrom = pOut; - } - } -@@ -86701,11 +95945,16 @@ case OP_Move: { - break; - } - --/* Opcode: Copy P1 P2 P3 * * -+/* Opcode: Copy P1 P2 P3 * P5 - ** Synopsis: r[P2@P3+1]=r[P1@P3+1] - ** - ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. - ** -+** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the -+** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot -+** be merged. The 0x0001 bit is used by the query planner and does not -+** come into play during query execution. -+** - ** This instruction makes a deep copy of the value. A duplicate - ** is made of any string or blob constant. See also OP_SCopy. - */ -@@ -86720,6 +95969,9 @@ case OP_Copy: { - memAboutToChange(p, pOut); - sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); - Deephemeralize(pOut); -+ if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){ -+ pOut->flags &= ~MEM_Subtype; -+ } - #ifdef SQLITE_DEBUG - pOut->pScopyFrom = 0; - #endif -@@ -86752,6 +96004,7 @@ case OP_SCopy: { /* out2 */ - #ifdef SQLITE_DEBUG - pOut->pScopyFrom = pIn1; - pOut->mScopyFlags = pIn1->flags; -+ pIn1->bScopy = 1; - #endif - break; - } -@@ -86772,6 +96025,24 @@ case OP_IntCopy: { /* out2 */ - break; - } - -+/* Opcode: FkCheck * * * * * -+** -+** Halt with an SQLITE_CONSTRAINT error if there are any unresolved -+** foreign key constraint violations. If there are no foreign key -+** constraint violations, this is a no-op. -+** -+** FK constraint violations are also checked when the prepared statement -+** exits. This opcode is used to raise foreign key constraint errors prior -+** to returning results such as a row change count or the result of a -+** RETURNING clause. -+*/ -+case OP_FkCheck: { -+ if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){ -+ goto abort_due_to_error; -+ } -+ break; -+} -+ - /* Opcode: ResultRow P1 P2 * * * - ** Synopsis: output=r[P1@P2] - ** -@@ -86782,73 +96053,32 @@ case OP_IntCopy: { /* out2 */ - ** the result row. - */ - case OP_ResultRow: { -- Mem *pMem; -- int i; - assert( p->nResColumn==pOp->p2 ); -- assert( pOp->p1>0 ); -+ assert( pOp->p1>0 || CORRUPT_DB ); - assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); - -- /* If this statement has violated immediate foreign key constraints, do -- ** not return the number of rows modified. And do not RELEASE the statement -- ** transaction. It needs to be rolled back. */ -- if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){ -- assert( db->flags&SQLITE_CountRows ); -- assert( p->usesStmtJournal ); -- goto abort_due_to_error; -- } -- -- /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then -- ** DML statements invoke this opcode to return the number of rows -- ** modified to the user. This is the only way that a VM that -- ** opens a statement transaction may invoke this opcode. -- ** -- ** In case this is such a statement, close any statement transaction -- ** opened by this VM before returning control to the user. This is to -- ** ensure that statement-transactions are always nested, not overlapping. -- ** If the open statement-transaction is not closed here, then the user -- ** may step another VM that opens its own statement transaction. This -- ** may lead to overlapping statement transactions. -- ** -- ** The statement transaction is never a top-level transaction. Hence -- ** the RELEASE call below can never fail. -- */ -- assert( p->iStatement==0 || db->flags&SQLITE_CountRows ); -- rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE); -- assert( rc==SQLITE_OK ); -- -- /* Invalidate all ephemeral cursor row caches */ - p->cacheCtr = (p->cacheCtr + 2)|1; -- -- /* Make sure the results of the current row are \000 terminated -- ** and have an assigned type. The results are de-ephemeralized as -- ** a side effect. -- */ -- pMem = p->pResultSet = &aMem[pOp->p1]; -- for(i=0; ip2; i++){ -- assert( memIsValid(&pMem[i]) ); -- Deephemeralize(&pMem[i]); -- assert( (pMem[i].flags & MEM_Ephem)==0 -- || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); -- sqlite3VdbeMemNulTerminate(&pMem[i]); -- REGISTER_TRACE(pOp->p1+i, &pMem[i]); -+ p->pResultRow = &aMem[pOp->p1]; - #ifdef SQLITE_DEBUG -- /* The registers in the result will not be used again when the -- ** prepared statement restarts. This is because sqlite3_column() -- ** APIs might have caused type conversions of made other changes to -- ** the register values. Therefore, we can go ahead and break any -- ** OP_SCopy dependencies. */ -- pMem[i].pScopyFrom = 0; --#endif -+ { -+ Mem *pMem = p->pResultRow; -+ int i; -+ for(i=0; ip2; i++){ -+ assert( memIsValid(&pMem[i]) ); -+ REGISTER_TRACE(pOp->p1+i, &pMem[i]); -+ /* The registers in the result will not be used again when the -+ ** prepared statement restarts. This is because sqlite3_column() -+ ** APIs might have caused type conversions of made other changes to -+ ** the register values. Therefore, we can go ahead and break any -+ ** OP_SCopy dependencies. */ -+ pMem[i].pScopyFrom = 0; -+ } - } -+#endif - if( db->mallocFailed ) goto no_mem; -- - if( db->mTrace & SQLITE_TRACE_ROW ){ - db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); - } -- -- -- /* Return SQLITE_ROW -- */ - p->pc = (int)(pOp - aOp) + 1; - rc = SQLITE_ROW; - goto vdbe_return; -@@ -86903,7 +96133,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ - if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } -- if( sqlite3VdbeMemGrow(pOut, (int)nByte+3, pOut==pIn2) ){ -+ if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){ - goto no_mem; - } - MemSetTypeFlag(pOut, MEM_Str); -@@ -86915,9 +96145,9 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ - memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); - assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); - pIn1->flags = flags1; -+ if( encoding>SQLITE_UTF8 ) nByte &= ~1; - pOut->z[nByte]=0; - pOut->z[nByte+1] = 0; -- pOut->z[nByte+2] = 0; - pOut->flags |= MEM_Term; - pOut->n = (int)nByte; - pOut->enc = encoding; -@@ -86968,7 +96198,6 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ - case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ - case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ - case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ -- u16 flags; /* Combined MEM_* flags from both inputs */ - u16 type1; /* Numeric type of left operand */ - u16 type2; /* Numeric type of right operand */ - i64 iA; /* Integer value of left operand */ -@@ -86977,12 +96206,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ - double rB; /* Real value of right operand */ - - pIn1 = &aMem[pOp->p1]; -- type1 = numericType(pIn1); -+ type1 = pIn1->flags; - pIn2 = &aMem[pOp->p2]; -- type2 = numericType(pIn2); -+ type2 = pIn2->flags; - pOut = &aMem[pOp->p3]; -- flags = pIn1->flags | pIn2->flags; - if( (type1 & type2 & MEM_Int)!=0 ){ -+int_math: - iA = pIn1->u.i; - iB = pIn2->u.i; - switch( pOp->opcode ){ -@@ -87004,9 +96233,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ - } - pOut->u.i = iB; - MemSetTypeFlag(pOut, MEM_Int); -- }else if( (flags & MEM_Null)!=0 ){ -+ }else if( ((type1 | type2) & MEM_Null)!=0 ){ - goto arithmetic_result_is_null; - }else{ -+ type1 = numericType(pIn1); -+ type2 = numericType(pIn2); -+ if( (type1 & type2 & MEM_Int)!=0 ) goto int_math; - fp_math: - rA = sqlite3VdbeRealValue(pIn1); - rB = sqlite3VdbeRealValue(pIn2); -@@ -87164,7 +96396,7 @@ case OP_AddImm: { /* in1 */ - pIn1 = &aMem[pOp->p1]; - memAboutToChange(p, pIn1); - sqlite3VdbeMemIntegerify(pIn1); -- pIn1->u.i += pOp->p2; -+ *(u64*)&pIn1->u.i += (u64)pOp->p2; - break; - } - -@@ -87175,7 +96407,7 @@ case OP_AddImm: { /* in1 */ - ** without data loss, then jump immediately to P2, or if P2==0 - ** raise an SQLITE_MISMATCH exception. - */ --case OP_MustBeInt: { /* jump, in1 */ -+case OP_MustBeInt: { /* jump0, in1 */ - pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_Int)==0 ){ - applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); -@@ -87216,7 +96448,7 @@ case OP_RealAffinity: { /* in1 */ - } - #endif - --#ifndef SQLITE_OMIT_CAST -+#if !defined(SQLITE_OMIT_CAST) || !defined(SQLITE_OMIT_ANALYZE) - /* Opcode: Cast P1 P2 * * * - ** Synopsis: affinity(r[P1]) - ** -@@ -87255,8 +96487,7 @@ case OP_Cast: { /* in1 */ - ** Synopsis: IF r[P3]==r[P1] - ** - ** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then --** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then --** store the result of comparison in register P2. -+** jump to address P2. - ** - ** The SQLITE_AFF_MASK portion of P5 must be an affinity character - - ** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made -@@ -87282,9 +96513,8 @@ case OP_Cast: { /* in1 */ - ** If neither operand is NULL the result is the same as it would be if - ** the SQLITE_NULLEQ flag were omitted from P5. - ** --** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the --** content of r[P2] is only changed if the new value is NULL or 0 (false). --** In other words, a prior r[P2] value will not be overwritten by 1 (true). -+** This opcode saves the result of comparison for use by the new -+** OP_Jump opcode. - */ - /* Opcode: Ne P1 P2 P3 P4 P5 - ** Synopsis: IF r[P3]!=r[P1] -@@ -87292,17 +96522,12 @@ case OP_Cast: { /* in1 */ - ** This works just like the Eq opcode except that the jump is taken if - ** the operands in registers P1 and P3 are not equal. See the Eq opcode for - ** additional information. --** --** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the --** content of r[P2] is only changed if the new value is NULL or 1 (true). --** In other words, a prior r[P2] value will not be overwritten by 0 (false). - */ - /* Opcode: Lt P1 P2 P3 P4 P5 - ** Synopsis: IF r[P3]p3]; - flags1 = pIn1->flags; - flags3 = pIn3->flags; -+ if( (flags1 & flags3 & MEM_Int)!=0 ){ -+ /* Common case of comparison of two integers */ -+ if( pIn3->u.i > pIn1->u.i ){ -+ if( sqlite3aGTb[pOp->opcode] ){ -+ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); -+ goto jump_to_p2; -+ } -+ iCompare = +1; -+ VVA_ONLY( iCompareIsInit = 1; ) -+ }else if( pIn3->u.i < pIn1->u.i ){ -+ if( sqlite3aLTb[pOp->opcode] ){ -+ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); -+ goto jump_to_p2; -+ } -+ iCompare = -1; -+ VVA_ONLY( iCompareIsInit = 1; ) -+ }else{ -+ if( sqlite3aEQb[pOp->opcode] ){ -+ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3); -+ goto jump_to_p2; -+ } -+ iCompare = 0; -+ VVA_ONLY( iCompareIsInit = 1; ) -+ } -+ VdbeBranchTaken(0, (pOp->p5 & SQLITE_NULLEQ)?2:3); -+ break; -+ } - if( (flags1 | flags3)&MEM_Null ){ - /* One or both operands are NULL */ - if( pOp->p5 & SQLITE_NULLEQ ){ -@@ -87384,44 +96639,33 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ - ** then the result is always NULL. - ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. - */ -- if( pOp->p5 & SQLITE_STOREP2 ){ -- pOut = &aMem[pOp->p2]; -- iCompare = 1; /* Operands are not equal */ -- memAboutToChange(p, pOut); -- MemSetTypeFlag(pOut, MEM_Null); -- REGISTER_TRACE(pOp->p2, pOut); -- }else{ -- VdbeBranchTaken(2,3); -- if( pOp->p5 & SQLITE_JUMPIFNULL ){ -- goto jump_to_p2; -- } -+ VdbeBranchTaken(2,3); -+ if( pOp->p5 & SQLITE_JUMPIFNULL ){ -+ goto jump_to_p2; - } -+ iCompare = 1; /* Operands are not equal */ -+ VVA_ONLY( iCompareIsInit = 1; ) - break; - } - }else{ -- /* Neither operand is NULL. Do a comparison. */ -+ /* Neither operand is NULL and we couldn't do the special high-speed -+ ** integer comparison case. So do a general-case comparison. */ - affinity = pOp->p5 & SQLITE_AFF_MASK; - if( affinity>=SQLITE_AFF_NUMERIC ){ - if( (flags1 | flags3)&MEM_Str ){ - if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ - applyNumericAffinity(pIn1,0); -- testcase( flags3==pIn3->flags ); -+ assert( flags3==pIn3->flags || CORRUPT_DB ); - flags3 = pIn3->flags; - } - if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ - applyNumericAffinity(pIn3,0); - } - } -- /* Handle the common case of integer comparison here, as an -- ** optimization, to avoid a call to sqlite3MemCompare() */ -- if( (pIn1->flags & pIn3->flags & MEM_Int)!=0 ){ -- if( pIn3->u.i > pIn1->u.i ){ res = +1; goto compare_op; } -- if( pIn3->u.i < pIn1->u.i ){ res = -1; goto compare_op; } -- res = 0; -- goto compare_op; -- } -- }else if( affinity==SQLITE_AFF_TEXT ){ -- if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ -+ }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){ -+ if( (flags1 & MEM_Str)!=0 ){ -+ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); -+ }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ - testcase( pIn1->flags & MEM_Int ); - testcase( pIn1->flags & MEM_Real ); - testcase( pIn1->flags & MEM_IntReal ); -@@ -87430,7 +96674,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ - flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); - if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str; - } -- if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ -+ if( (flags3 & MEM_Str)!=0 ){ -+ pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); -+ }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ - testcase( pIn3->flags & MEM_Int ); - testcase( pIn3->flags & MEM_Real ); - testcase( pIn3->flags & MEM_IntReal ); -@@ -87442,7 +96688,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ - assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); - res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); - } --compare_op: -+ - /* At this point, res is negative, zero, or positive if reg[P1] is - ** less than, equal to, or greater than reg[P3], respectively. Compute - ** the answer to this operator in res2, depending on what the comparison -@@ -87451,16 +96697,15 @@ compare_op: - ** order: NE, EQ, GT, LE, LT, GE */ - assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 ); - assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 ); -- if( res<0 ){ /* ne, eq, gt, le, lt, ge */ -- static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 }; -- res2 = aLTb[pOp->opcode - OP_Ne]; -+ if( res<0 ){ -+ res2 = sqlite3aLTb[pOp->opcode]; - }else if( res==0 ){ -- static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 }; -- res2 = aEQb[pOp->opcode - OP_Ne]; -+ res2 = sqlite3aEQb[pOp->opcode]; - }else{ -- static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 }; -- res2 = aGTb[pOp->opcode - OP_Ne]; -+ res2 = sqlite3aGTb[pOp->opcode]; - } -+ iCompare = res; -+ VVA_ONLY( iCompareIsInit = 1; ) - - /* Undo any changes made by applyAffinity() to the input registers. */ - assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); -@@ -87468,67 +96713,40 @@ compare_op: - assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); - pIn1->flags = flags1; - -- if( pOp->p5 & SQLITE_STOREP2 ){ -- pOut = &aMem[pOp->p2]; -- iCompare = res; -- if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){ -- /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1 -- ** and prevents OP_Ne from overwriting NULL with 0. This flag -- ** is only used in contexts where either: -- ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0) -- ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1) -- ** Therefore it is not necessary to check the content of r[P2] for -- ** NULL. */ -- assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq ); -- assert( res2==0 || res2==1 ); -- testcase( res2==0 && pOp->opcode==OP_Eq ); -- testcase( res2==1 && pOp->opcode==OP_Eq ); -- testcase( res2==0 && pOp->opcode==OP_Ne ); -- testcase( res2==1 && pOp->opcode==OP_Ne ); -- if( (pOp->opcode==OP_Eq)==res2 ) break; -- } -- memAboutToChange(p, pOut); -- MemSetTypeFlag(pOut, MEM_Int); -- pOut->u.i = res2; -- REGISTER_TRACE(pOp->p2, pOut); -- }else{ -- VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); -- if( res2 ){ -- goto jump_to_p2; -- } -+ VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); -+ if( res2 ){ -+ goto jump_to_p2; - } - break; - } - --/* Opcode: ElseNotEq * P2 * * * -+/* Opcode: ElseEq * P2 * * * - ** - ** This opcode must follow an OP_Lt or OP_Gt comparison operator. There - ** can be zero or more OP_ReleaseReg opcodes intervening, but no other - ** opcodes are allowed to occur between this instruction and the previous --** OP_Lt or OP_Gt. Furthermore, the prior OP_Lt or OP_Gt must have the --** SQLITE_STOREP2 bit set in the P5 field. -+** OP_Lt or OP_Gt. - ** --** If result of an OP_Eq comparison on the same two operands as the --** prior OP_Lt or OP_Gt would have been NULL or false (0), then then --** jump to P2. If the result of an OP_Eq comparison on the two previous --** operands would have been true (1), then fall through. -+** If the result of an OP_Eq comparison on the same two operands as -+** the prior OP_Lt or OP_Gt would have been true, then jump to P2. If -+** the result of an OP_Eq comparison on the two previous operands -+** would have been false or NULL, then fall through. - */ --case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ -+case OP_ElseEq: { /* same as TK_ESCAPE, jump */ - - #ifdef SQLITE_DEBUG - /* Verify the preconditions of this opcode - that it follows an OP_Lt or -- ** OP_Gt with the SQLITE_STOREP2 flag set, with zero or more intervening -- ** OP_ReleaseReg opcodes */ -+ ** OP_Gt with zero or more intervening OP_ReleaseReg opcodes */ - int iAddr; - for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){ - if( aOp[iAddr].opcode==OP_ReleaseReg ) continue; - assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt ); -- assert( aOp[iAddr].p5 & SQLITE_STOREP2 ); - break; - } - #endif /* SQLITE_DEBUG */ -- VdbeBranchTaken(iCompare!=0, 2); -- if( iCompare!=0 ) goto jump_to_p2; -+ assert( iCompareIsInit ); -+ VdbeBranchTaken(iCompare==0, 2); -+ if( iCompare==0 ) goto jump_to_p2; - break; - } - -@@ -87538,9 +96756,8 @@ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */ - ** Set the permutation used by the OP_Compare operator in the next - ** instruction. The permutation is stored in the P4 operand. - ** --** The permutation is only valid until the next OP_Compare that has --** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should --** occur immediately prior to the OP_Compare. -+** The permutation is only valid for the next opcode which must be -+** an OP_Compare that has the OPFLAG_PERMUTE bit set in P5. - ** - ** The first integer in the P4 integer array is the length of the array - ** and does not become part of the permutation. -@@ -87572,6 +96789,8 @@ case OP_Permutation: { - ** The comparison is a sort comparison, so NULLs compare equal, - ** NULLs are less than numbers, numbers are less than strings, - ** and strings are less than blobs. -+** -+** This opcode must be immediately followed by an OP_Jump opcode. - */ - case OP_Compare: { - int n; -@@ -87620,6 +96839,7 @@ case OP_Compare: { - pColl = pKeyInfo->aColl[i]; - bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC); - iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); -+ VVA_ONLY( iCompareIsInit = 1; ) - if( iCompare ){ - if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) - && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null)) -@@ -87630,16 +96850,21 @@ case OP_Compare: { - break; - } - } -+ assert( pOp[1].opcode==OP_Jump ); - break; - } - - /* Opcode: Jump P1 P2 P3 * * - ** - ** Jump to the instruction at address P1, P2, or P3 depending on whether --** in the most recent OP_Compare instruction the P1 vector was less than -+** in the most recent OP_Compare instruction the P1 vector was less than, - ** equal to, or greater than the P2 vector, respectively. -+** -+** This opcode must immediately follow an OP_Compare opcode. - */ - case OP_Jump: { /* jump */ -+ assert( pOp>aOp && pOp[-1].opcode==OP_Compare ); -+ assert( iCompareIsInit ); - if( iCompare<0 ){ - VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1]; - }else if( iCompare==0 ){ -@@ -87759,7 +96984,7 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ - break; - } - --/* Opcode: Once P1 P2 * * * -+/* Opcode: Once P1 P2 P3 * * - ** - ** Fall through to the next instruction the first time this opcode is - ** encountered on each invocation of the byte-code program. Jump to P2 -@@ -87775,6 +97000,12 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ - ** whether or not the jump should be taken. The bitmask is necessary - ** because the self-altering code trick does not work for recursive - ** triggers. -+** -+** The P3 operand is not used directly by this opcode. However P3 is -+** used by the code generator as follows: If this opcode is the start -+** of a subroutine and that subroutine uses a Bloom filter, then P3 will -+** be the register that holds that Bloom filter. See tag-202407032019 -+** in the source code for implementation details. - */ - case OP_Once: { /* jump */ - u32 iAddr; /* Address of this instruction */ -@@ -87839,6 +97070,117 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ - break; - } - -+/* Opcode: IsType P1 P2 P3 P4 P5 -+** Synopsis: if typeof(P1.P3) in P5 goto P2 -+** -+** Jump to P2 if the type of a column in a btree is one of the types specified -+** by the P5 bitmask. -+** -+** P1 is normally a cursor on a btree for which the row decode cache is -+** valid through at least column P3. In other words, there should have been -+** a prior OP_Column for column P3 or greater. If the cursor is not valid, -+** then this opcode might give spurious results. -+** The the btree row has fewer than P3 columns, then use P4 as the -+** datatype. -+** -+** If P1 is -1, then P3 is a register number and the datatype is taken -+** from the value in that register. -+** -+** P5 is a bitmask of data types. SQLITE_INTEGER is the least significant -+** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04. -+** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10. -+** -+** WARNING: This opcode does not reliably distinguish between NULL and REAL -+** when P1>=0. If the database contains a NaN value, this opcode will think -+** that the datatype is REAL when it should be NULL. When P1<0 and the value -+** is already stored in register P3, then this opcode does reliably -+** distinguish between NULL and REAL. The problem only arises then P1>=0. -+** -+** Take the jump to address P2 if and only if the datatype of the -+** value determined by P1 and P3 corresponds to one of the bits in the -+** P5 bitmask. -+** -+*/ -+case OP_IsType: { /* jump */ -+ VdbeCursor *pC; -+ u16 typeMask; -+ u32 serialType; -+ -+ assert( pOp->p1>=(-1) && pOp->p1nCursor ); -+ assert( pOp->p1>=0 || (pOp->p3>=0 && pOp->p3<=(p->nMem+1 - p->nCursor)) ); -+ if( pOp->p1>=0 ){ -+ pC = p->apCsr[pOp->p1]; -+ assert( pC!=0 ); -+ assert( pOp->p3>=0 ); -+ if( pOp->p3nHdrParsed ){ -+ serialType = pC->aType[pOp->p3]; -+ if( serialType>=12 ){ -+ if( serialType&1 ){ -+ typeMask = 0x04; /* SQLITE_TEXT */ -+ }else{ -+ typeMask = 0x08; /* SQLITE_BLOB */ -+ } -+ }else{ -+ static const unsigned char aMask[] = { -+ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2, -+ 0x01, 0x01, 0x10, 0x10 -+ }; -+ testcase( serialType==0 ); -+ testcase( serialType==1 ); -+ testcase( serialType==2 ); -+ testcase( serialType==3 ); -+ testcase( serialType==4 ); -+ testcase( serialType==5 ); -+ testcase( serialType==6 ); -+ testcase( serialType==7 ); -+ testcase( serialType==8 ); -+ testcase( serialType==9 ); -+ testcase( serialType==10 ); -+ testcase( serialType==11 ); -+ typeMask = aMask[serialType]; -+ } -+ }else{ -+ typeMask = 1 << (pOp->p4.i - 1); -+ testcase( typeMask==0x01 ); -+ testcase( typeMask==0x02 ); -+ testcase( typeMask==0x04 ); -+ testcase( typeMask==0x08 ); -+ testcase( typeMask==0x10 ); -+ } -+ }else{ -+ assert( memIsValid(&aMem[pOp->p3]) ); -+ typeMask = 1 << (sqlite3_value_type((sqlite3_value*)&aMem[pOp->p3])-1); -+ testcase( typeMask==0x01 ); -+ testcase( typeMask==0x02 ); -+ testcase( typeMask==0x04 ); -+ testcase( typeMask==0x08 ); -+ testcase( typeMask==0x10 ); -+ } -+ VdbeBranchTaken( (typeMask & pOp->p5)!=0, 2); -+ if( typeMask & pOp->p5 ){ -+ goto jump_to_p2; -+ } -+ break; -+} -+ -+/* Opcode: ZeroOrNull P1 P2 P3 * * -+** Synopsis: r[P2] = 0 OR NULL -+** -+** If both registers P1 and P3 are NOT NULL, then store a zero in -+** register P2. If either registers P1 or P3 are NULL then put -+** a NULL in register P2. -+*/ -+case OP_ZeroOrNull: { /* in1, in2, out2, in3 */ -+ if( (aMem[pOp->p1].flags & MEM_Null)!=0 -+ || (aMem[pOp->p3].flags & MEM_Null)!=0 -+ ){ -+ sqlite3VdbeMemSetNull(aMem + pOp->p2); -+ }else{ -+ sqlite3VdbeMemSetInt64(aMem + pOp->p2, 0); -+ } -+ break; -+} -+ - /* Opcode: NotNull P1 P2 * * * - ** Synopsis: if r[P1]!=NULL goto P2 - ** -@@ -87860,11 +97202,14 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ - ** If it is, then set register P3 to NULL and jump immediately to P2. - ** If P1 is not on a NULL row, then fall through without making any - ** changes. -+** -+** If P1 is not an open cursor, then this opcode is a no-op. - */ - case OP_IfNullRow: { /* jump */ -+ VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); -- assert( p->apCsr[pOp->p1]!=0 ); -- if( p->apCsr[pOp->p1]->nullRow ){ -+ pC = p->apCsr[pOp->p1]; -+ if( pC && pC->nullRow ){ - sqlite3VdbeMemSetNull(aMem + pOp->p3); - goto jump_to_p2; - } -@@ -87892,22 +97237,30 @@ case OP_Offset: { /* out3 */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - pOut = &p->aMem[pOp->p3]; -- if( NEVER(pC==0) || pC->eCurType!=CURTYPE_BTREE ){ -+ if( pC==0 || pC->eCurType!=CURTYPE_BTREE ){ - sqlite3VdbeMemSetNull(pOut); - }else{ -- sqlite3VdbeMemSetInt64(pOut, sqlite3BtreeOffset(pC->uc.pCursor)); -+ if( pC->deferredMoveto ){ -+ rc = sqlite3VdbeFinishMoveto(pC); -+ if( rc ) goto abort_due_to_error; -+ } -+ if( sqlite3BtreeEof(pC->uc.pCursor) ){ -+ sqlite3VdbeMemSetNull(pOut); -+ }else{ -+ sqlite3VdbeMemSetInt64(pOut, sqlite3BtreeOffset(pC->uc.pCursor)); -+ } - } - break; - } - #endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */ - - /* Opcode: Column P1 P2 P3 P4 P5 --** Synopsis: r[P3]=PX -+** Synopsis: r[P3]=PX cursor P1 column P2 - ** - ** Interpret the data that cursor P1 points to as a structure built using - ** the MakeRecord instruction. (See the MakeRecord opcode for additional - ** information about the format of the data.) Extract the P2-th column --** from this record. If there are less that (P2+1) -+** from this record. If there are less than (P2+1) - ** values in the record, extract a NULL. - ** - ** The value extracted is stored in register P3. -@@ -87916,15 +97269,17 @@ case OP_Offset: { /* out3 */ - ** if the P4 argument is a P4_MEM use the value of the P4 argument as - ** the result. - ** --** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then --** the result is guaranteed to only be used as the argument of a length() --** or typeof() function, respectively. The loading of large blobs can be --** skipped for length() and all content loading can be skipped for typeof(). -+** If the OPFLAG_LENGTHARG bit is set in P5 then the result is guaranteed -+** to only be used by the length() function or the equivalent. The content -+** of large blobs is not loaded, thus saving CPU cycles. If the -+** OPFLAG_TYPEOFARG bit is set then the result will only be used by the -+** typeof() function or the IS NULL or IS NOT NULL operators or the -+** equivalent. In this case, all content loading can be omitted. - */ --case OP_Column: { -+case OP_Column: { /* ncycle */ - u32 p2; /* column number to retrieve */ - VdbeCursor *pC; /* The VDBE cursor */ -- BtCursor *pCrsr; /* The BTree cursor */ -+ BtCursor *pCrsr; /* The B-Tree cursor corresponding to pC */ - u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int len; /* The length of the serialized data for the column */ - int i; /* Loop counter */ -@@ -87938,43 +97293,53 @@ case OP_Column: { - Mem *pReg; /* PseudoTable input register */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); -+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pC = p->apCsr[pOp->p1]; -- assert( pC!=0 ); - p2 = (u32)pOp->p2; - -- /* If the cursor cache is stale (meaning it is not currently point at -- ** the correct row) then bring it up-to-date by doing the necessary -- ** B-Tree seek. */ -- rc = sqlite3VdbeCursorMoveto(&pC, &p2); -- if( rc ) goto abort_due_to_error; -- -- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); -- pDest = &aMem[pOp->p3]; -- memAboutToChange(p, pDest); -+op_column_restart: - assert( pC!=0 ); -- assert( p2<(u32)pC->nField ); -+ assert( p2<(u32)pC->nField -+ || (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult==0) ); - aOffset = pC->aOffset; -+ assert( aOffset==pC->aType+pC->nField ); - assert( pC->eCurType!=CURTYPE_VTAB ); - assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); - assert( pC->eCurType!=CURTYPE_SORTER ); - - if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ - if( pC->nullRow ){ -- if( pC->eCurType==CURTYPE_PSEUDO ){ -+ if( pC->eCurType==CURTYPE_PSEUDO && pC->seekResult>0 ){ - /* For the special case of as pseudo-cursor, the seekResult field - ** identifies the register that holds the record */ -- assert( pC->seekResult>0 ); - pReg = &aMem[pC->seekResult]; - assert( pReg->flags & MEM_Blob ); - assert( memIsValid(pReg) ); - pC->payloadSize = pC->szRow = pReg->n; - pC->aRow = (u8*)pReg->z; - }else{ -+ pDest = &aMem[pOp->p3]; -+ memAboutToChange(p, pDest); - sqlite3VdbeMemSetNull(pDest); - goto op_column_out; - } - }else{ - pCrsr = pC->uc.pCursor; -+ if( pC->deferredMoveto ){ -+ u32 iMap; -+ assert( !pC->isEphemeral ); -+ if( pC->ub.aAltMap && (iMap = pC->ub.aAltMap[1+p2])>0 ){ -+ pC = pC->pAltCursor; -+ p2 = iMap - 1; -+ goto op_column_restart; -+ } -+ rc = sqlite3VdbeFinishMoveto(pC); -+ if( rc ) goto abort_due_to_error; -+ }else if( sqlite3BtreeCursorHasMoved(pCrsr) ){ -+ rc = sqlite3VdbeHandleMovedCursor(pC); -+ if( rc ) goto abort_due_to_error; -+ goto op_column_restart; -+ } - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pCrsr ); - assert( sqlite3BtreeCursorIsValid(pCrsr) ); -@@ -87982,15 +97347,15 @@ case OP_Column: { - pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &pC->szRow); - assert( pC->szRow<=pC->payloadSize ); - assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */ -- if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ -- goto too_big; -- } - } - pC->cacheStatus = p->cacheCtr; -- pC->iHdrOffset = getVarint32(pC->aRow, aOffset[0]); -+ if( (aOffset[0] = pC->aRow[0])<0x80 ){ -+ pC->iHdrOffset = 1; -+ }else{ -+ pC->iHdrOffset = sqlite3GetVarint32(pC->aRow, aOffset); -+ } - pC->nHdrParsed = 0; - -- - if( pC->szRowaRow does not have to hold the entire row, but it does at least - ** need to cover the header of the record. If pC->aRow does not contain -@@ -88030,6 +97395,10 @@ case OP_Column: { - testcase( aOffset[0]==0 ); - goto op_column_read_header; - } -+ }else if( sqlite3BtreeCursorHasMoved(pC->uc.pCursor) ){ -+ rc = sqlite3VdbeHandleMovedCursor(pC); -+ if( rc ) goto abort_due_to_error; -+ goto op_column_restart; - } - - /* Make sure at least the first p2+1 entries of the header have been -@@ -88098,6 +97467,8 @@ case OP_Column: { - ** columns. So the result will be either the default value or a NULL. - */ - if( pC->nHdrParsed<=p2 ){ -+ pDest = &aMem[pOp->p3]; -+ memAboutToChange(p, pDest); - if( pOp->p4type==P4_MEM ){ - sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); - }else{ -@@ -88115,6 +97486,8 @@ case OP_Column: { - */ - assert( p2nHdrParsed ); - assert( rc==SQLITE_OK ); -+ pDest = &aMem[pOp->p3]; -+ memAboutToChange(p, pDest); - assert( sqlite3VdbeCheckMemInvariants(pDest) ); - if( VdbeMemDynamic(pDest) ){ - sqlite3VdbeMemSetNull(pDest); -@@ -88135,6 +97508,7 @@ case OP_Column: { - pDest->n = len = (t-12)/2; - pDest->enc = encoding; - if( pDest->szMalloc < len+2 ){ -+ if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big; - pDest->flags = MEM_Null; - if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; - }else{ -@@ -88146,11 +97520,16 @@ case OP_Column: { - pDest->flags = aFlag[t&1]; - } - }else{ -+ u8 p5; - pDest->enc = encoding; -+ assert( pDest->db==db ); - /* This branch happens only when content is on overflow pages */ -- if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 -- && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) -- || (len = sqlite3VdbeSerialTypeLen(t))==0 -+ if( ((p5 = (pOp->p5 & OPFLAG_BYTELENARG))!=0 -+ && (p5==OPFLAG_TYPEOFARG -+ || (t>=12 && ((t&1)==0 || p5==OPFLAG_BYTELENARG)) -+ ) -+ ) -+ || sqlite3VdbeSerialTypeLen(t)==0 - ){ - /* Content is irrelevant for - ** 1. the typeof() function, -@@ -88167,10 +97546,13 @@ case OP_Column: { - */ - sqlite3VdbeSerialGet((u8*)sqlite3CtypeMap, t, pDest); - }else{ -- rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest); -- if( rc!=SQLITE_OK ) goto abort_due_to_error; -- sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); -- pDest->flags &= ~MEM_Ephem; -+ rc = vdbeColumnFromOverflow(pC, p2, t, aOffset[p2], -+ p->cacheCtr, colCacheCtr, pDest); -+ if( rc ){ -+ if( rc==SQLITE_NOMEM ) goto no_mem; -+ if( rc==SQLITE_TOOBIG ) goto too_big; -+ goto abort_due_to_error; -+ } - } - } - -@@ -88189,6 +97571,110 @@ op_column_corrupt: - } - } - -+/* Opcode: TypeCheck P1 P2 P3 P4 * -+** Synopsis: typecheck(r[P1@P2]) -+** -+** Apply affinities to the range of P2 registers beginning with P1. -+** Take the affinities from the Table object in P4. If any value -+** cannot be coerced into the correct type, then raise an error. -+** -+** This opcode is similar to OP_Affinity except that this opcode -+** forces the register type to the Table column type. This is used -+** to implement "strict affinity". -+** -+** GENERATED ALWAYS AS ... STATIC columns are only checked if P3 -+** is zero. When P3 is non-zero, no type checking occurs for -+** static generated columns. Virtual columns are computed at query time -+** and so they are never checked. -+** -+** Preconditions: -+** -+**
          -+**
        • P2 should be the number of non-virtual columns in the -+** table of P4. -+**
        • Table P4 should be a STRICT table. -+**
        -+** -+** If any precondition is false, an assertion fault occurs. -+*/ -+case OP_TypeCheck: { -+ Table *pTab; -+ Column *aCol; -+ int i; -+ -+ assert( pOp->p4type==P4_TABLE ); -+ pTab = pOp->p4.pTab; -+ assert( pTab->tabFlags & TF_Strict ); -+ assert( pTab->nNVCol==pOp->p2 ); -+ aCol = pTab->aCol; -+ pIn1 = &aMem[pOp->p1]; -+ for(i=0; inCol; i++){ -+ if( aCol[i].colFlags & COLFLAG_GENERATED ){ -+ if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue; -+ if( pOp->p3 ){ pIn1++; continue; } -+ } -+ assert( pIn1 < &aMem[pOp->p1+pOp->p2] ); -+ applyAffinity(pIn1, aCol[i].affinity, encoding); -+ if( (pIn1->flags & MEM_Null)==0 ){ -+ switch( aCol[i].eCType ){ -+ case COLTYPE_BLOB: { -+ if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error; -+ break; -+ } -+ case COLTYPE_INTEGER: -+ case COLTYPE_INT: { -+ if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error; -+ break; -+ } -+ case COLTYPE_TEXT: { -+ if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error; -+ break; -+ } -+ case COLTYPE_REAL: { -+ testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_Real ); -+ assert( (pIn1->flags & MEM_IntReal)==0 ); -+ if( pIn1->flags & MEM_Int ){ -+ /* When applying REAL affinity, if the result is still an MEM_Int -+ ** that will fit in 6 bytes, then change the type to MEM_IntReal -+ ** so that we keep the high-resolution integer value but know that -+ ** the type really wants to be REAL. */ -+ testcase( pIn1->u.i==140737488355328LL ); -+ testcase( pIn1->u.i==140737488355327LL ); -+ testcase( pIn1->u.i==-140737488355328LL ); -+ testcase( pIn1->u.i==-140737488355329LL ); -+ if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){ -+ pIn1->flags |= MEM_IntReal; -+ pIn1->flags &= ~MEM_Int; -+ }else{ -+ pIn1->u.r = (double)pIn1->u.i; -+ pIn1->flags |= MEM_Real; -+ pIn1->flags &= ~MEM_Int; -+ } -+ }else if( (pIn1->flags & (MEM_Real|MEM_IntReal))==0 ){ -+ goto vdbe_type_error; -+ } -+ break; -+ } -+ default: { -+ /* COLTYPE_ANY. Accept anything. */ -+ break; -+ } -+ } -+ } -+ REGISTER_TRACE((int)(pIn1-aMem), pIn1); -+ pIn1++; -+ } -+ assert( pIn1 == &aMem[pOp->p1+pOp->p2] ); -+ break; -+ -+vdbe_type_error: -+ sqlite3VdbeError(p, "cannot store %s value in %s column %s.%s", -+ vdbeMemTypeName(pIn1), sqlite3StdType[aCol[i].eCType-1], -+ pTab->zName, aCol[i].zCnName); -+ rc = SQLITE_CONSTRAINT_DATATYPE; -+ goto abort_due_to_error; -+} -+ - /* Opcode: Affinity P1 P2 * P4 * - ** Synopsis: affinity(r[P1@P2]) - ** -@@ -88225,7 +97711,7 @@ case OP_Affinity: { - }else{ - pIn1->u.r = (double)pIn1->u.i; - pIn1->flags |= MEM_Real; -- pIn1->flags &= ~MEM_Int; -+ pIn1->flags &= ~(MEM_Int|MEM_Str); - } - } - REGISTER_TRACE((int)(pIn1-aMem), pIn1); -@@ -88275,7 +97761,6 @@ case OP_MakeRecord: { - Mem *pLast; /* Last field of the record */ - int nField; /* Number of fields in the record */ - char *zAffinity; /* The affinity string for the record */ -- int file_format; /* File format to use for encoding */ - u32 len; /* Length of a field */ - u8 *zHdr; /* Where to write next byte of the header */ - u8 *zPayload; /* Where to write next byte of the payload */ -@@ -88304,7 +97789,6 @@ case OP_MakeRecord: { - pData0 = &aMem[nField]; - nField = pOp->p2; - pLast = &pData0[nField-1]; -- file_format = p->minWriteFileFormat; - - /* Identify the output register */ - assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); -@@ -88403,10 +97887,10 @@ case OP_MakeRecord: { - testcase( uu==127 ); testcase( uu==128 ); - testcase( uu==32767 ); testcase( uu==32768 ); - testcase( uu==8388607 ); testcase( uu==8388608 ); -- testcase( uu==2147483647 ); testcase( uu==2147483648 ); -+ testcase( uu==2147483647 ); testcase( uu==2147483648LL ); - testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL ); - if( uu<=127 ){ -- if( (i&1)==i && file_format>=4 ){ -+ if( (i&1)==i && p->minWriteFileFormat>=4 ){ - pRec->uTemp = 8+(u32)uu; - }else{ - nData++; -@@ -88511,18 +97995,70 @@ case OP_MakeRecord: { - zPayload = zHdr + nHdr; - - /* Write the record */ -- zHdr += putVarint32(zHdr, nHdr); -+ if( nHdr<0x80 ){ -+ *(zHdr++) = nHdr; -+ }else{ -+ zHdr += sqlite3PutVarint(zHdr,nHdr); -+ } - assert( pData0<=pLast ); - pRec = pData0; -- do{ -+ while( 1 /*exit-by-break*/ ){ - serial_type = pRec->uTemp; - /* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more -- ** additional varints, one per column. */ -- zHdr += putVarint32(zHdr, serial_type); /* serial type */ -- /* EVIDENCE-OF: R-64536-51728 The values for each column in the record -+ ** additional varints, one per column. -+ ** EVIDENCE-OF: R-64536-51728 The values for each column in the record - ** immediately follow the header. */ -- zPayload += sqlite3VdbeSerialPut(zPayload, pRec, serial_type); /* content */ -- }while( (++pRec)<=pLast ); -+ if( serial_type<=7 ){ -+ *(zHdr++) = serial_type; -+ if( serial_type==0 ){ -+ /* NULL value. No change in zPayload */ -+ }else{ -+ u64 v; -+ if( serial_type==7 ){ -+ assert( sizeof(v)==sizeof(pRec->u.r) ); -+ memcpy(&v, &pRec->u.r, sizeof(v)); -+ swapMixedEndianFloat(v); -+ }else{ -+ v = pRec->u.i; -+ } -+ len = sqlite3SmallTypeSizes[serial_type]; -+ assert( len>=1 && len<=8 && len!=5 && len!=7 ); -+ switch( len ){ -+ default: zPayload[7] = (u8)(v&0xff); v >>= 8; -+ zPayload[6] = (u8)(v&0xff); v >>= 8; -+ /* no break */ deliberate_fall_through -+ case 6: zPayload[5] = (u8)(v&0xff); v >>= 8; -+ zPayload[4] = (u8)(v&0xff); v >>= 8; -+ /* no break */ deliberate_fall_through -+ case 4: zPayload[3] = (u8)(v&0xff); v >>= 8; -+ /* no break */ deliberate_fall_through -+ case 3: zPayload[2] = (u8)(v&0xff); v >>= 8; -+ /* no break */ deliberate_fall_through -+ case 2: zPayload[1] = (u8)(v&0xff); v >>= 8; -+ /* no break */ deliberate_fall_through -+ case 1: zPayload[0] = (u8)(v&0xff); -+ } -+ zPayload += len; -+ } -+ }else if( serial_type<0x80 ){ -+ *(zHdr++) = serial_type; -+ if( serial_type>=14 && pRec->n>0 ){ -+ assert( pRec->z!=0 ); -+ memcpy(zPayload, pRec->z, pRec->n); -+ zPayload += pRec->n; -+ } -+ }else{ -+ zHdr += sqlite3PutVarint(zHdr, serial_type); -+ if( pRec->n ){ -+ assert( pRec->z!=0 ); -+ assert( pRec->z!=(const char*)sqlite3CtypeMap ); -+ memcpy(zPayload, pRec->z, pRec->n); -+ zPayload += pRec->n; -+ } -+ } -+ if( pRec==pLast ) break; -+ pRec++; -+ } - assert( nHdr==(int)(zHdr - (u8*)pOut->z) ); - assert( nByte==(int)(zPayload - (u8*)pOut->z) ); - -@@ -88531,7 +98067,7 @@ case OP_MakeRecord: { - break; - } - --/* Opcode: Count P1 P2 p3 * * -+/* Opcode: Count P1 P2 P3 * * - ** Synopsis: r[P2]=count() - ** - ** Store the number of entries (an integer value) in the table or index -@@ -88741,7 +98277,10 @@ case OP_Savepoint: { - } - } - if( rc ) goto abort_due_to_error; -- -+ if( p->eVdbeState==VDBE_HALT_STATE ){ -+ rc = SQLITE_DONE; -+ goto vdbe_return; -+ } - break; - } - -@@ -88814,7 +98353,8 @@ case OP_AutoCommit: { - ** active. - ** If P2 is non-zero, then a write-transaction is started, or if a - ** read-transaction is already active, it is upgraded to a write-transaction. --** If P2 is zero, then a read-transaction is started. -+** If P2 is zero, then a read-transaction is started. If P2 is 2 or more -+** then an exclusive transaction is started. - ** - ** P1 is the index of the database file on which the transaction is - ** started. Index 0 is the main database file and index 1 is the -@@ -88844,17 +98384,28 @@ case OP_AutoCommit: { - */ - case OP_Transaction: { - Btree *pBt; -+ Db *pDb; - int iMeta = 0; - - assert( p->bIsReader ); - assert( p->readOnly==0 || pOp->p2==0 ); -+ assert( pOp->p2>=0 && pOp->p2<=2 ); - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( DbMaskTest(p->btreeMask, pOp->p1) ); -- if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){ -- rc = SQLITE_READONLY; -+ assert( rc==SQLITE_OK ); -+ if( pOp->p2 && (db->flags & (SQLITE_QueryOnly|SQLITE_CorruptRdOnly))!=0 ){ -+ if( db->flags & SQLITE_QueryOnly ){ -+ /* Writes prohibited by the "PRAGMA query_only=TRUE" statement */ -+ rc = SQLITE_READONLY; -+ }else{ -+ /* Writes prohibited due to a prior SQLITE_CORRUPT in the current -+ ** transaction */ -+ rc = SQLITE_CORRUPT; -+ } - goto abort_due_to_error; - } -- pBt = db->aDb[pOp->p1].pBt; -+ pDb = &db->aDb[pOp->p1]; -+ pBt = pDb->pBt; - - if( pBt ){ - rc = sqlite3BtreeBeginTrans(pBt, pOp->p2, &iMeta); -@@ -88873,7 +98424,7 @@ case OP_Transaction: { - && pOp->p2 - && (db->autoCommit==0 || db->nVdbeRead>1) - ){ -- assert( sqlite3BtreeIsInTrans(pBt) ); -+ assert( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ); - if( p->iStatement==0 ){ - assert( db->nStatement>=0 && db->nSavepoint>=0 ); - db->nStatement++; -@@ -88893,9 +98444,9 @@ case OP_Transaction: { - } - } - assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); -- if( pOp->p5 -- && (iMeta!=pOp->p3 -- || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i) -+ if( rc==SQLITE_OK -+ && pOp->p5 -+ && (iMeta!=pOp->p3 || pDb->pSchema->iGeneration!=pOp->p4.i) - ){ - /* - ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema -@@ -88922,6 +98473,11 @@ case OP_Transaction: { - } - p->expired = 1; - rc = SQLITE_SCHEMA; -+ -+ /* Set changeCntOn to 0 to prevent the value returned by sqlite3_changes() -+ ** from being modified in sqlite3VdbeHalt(). If this statement is -+ ** reprepared, changeCntOn will be set again. */ -+ p->changeCntOn = 0; - } - if( rc ) goto abort_due_to_error; - break; -@@ -88988,8 +98544,9 @@ case OP_SetCookie: { - rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); - if( pOp->p2==BTREE_SCHEMA_VERSION ){ - /* When the schema cookie changes, record the new cookie internally */ -- pDb->pSchema->schema_cookie = pOp->p3 - pOp->p5; -+ *(u32*)&pDb->pSchema->schema_cookie = *(u32*)&pOp->p3 - pOp->p5; - db->mDbFlags |= DBFLAG_SchemaChange; -+ sqlite3FkClearTriggerCache(db, pOp->p1); - }else if( pOp->p2==BTREE_FILE_FORMAT ){ - /* Record changes in the file format */ - pDb->pSchema->file_format = pOp->p3; -@@ -89088,7 +98645,7 @@ case OP_SetCookie: { - ** - ** See also: OP_OpenRead, OP_ReopenIdx - */ --case OP_ReopenIdx: { -+case OP_ReopenIdx: { /* ncycle */ - int nField; - KeyInfo *pKeyInfo; - u32 p2; -@@ -89103,11 +98660,13 @@ case OP_ReopenIdx: { - pCur = p->apCsr[pOp->p1]; - if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ - assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ -+ assert( pCur->eCurType==CURTYPE_BTREE ); -+ sqlite3BtreeClearCursor(pCur->uc.pCursor); - goto open_cursor_set_hints; - } - /* If the cursor is not currently open or is open on a different - ** index, then fall through into OP_OpenRead to force a reopen */ --case OP_OpenRead: -+case OP_OpenRead: /* ncycle */ - case OP_OpenWrite: - - assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); -@@ -89136,23 +98695,23 @@ case OP_OpenWrite: - if( pDb->pSchema->file_format < p->minWriteFileFormat ){ - p->minWriteFileFormat = pDb->pSchema->file_format; - } -+ if( pOp->p5 & OPFLAG_P2ISREG ){ -+ assert( p2>0 ); -+ assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); -+ pIn2 = &aMem[p2]; -+ assert( memIsValid(pIn2) ); -+ assert( (pIn2->flags & MEM_Int)!=0 ); -+ sqlite3VdbeMemIntegerify(pIn2); -+ p2 = (int)pIn2->u.i; -+ /* The p2 value always comes from a prior OP_CreateBtree opcode and -+ ** that opcode will always set the p2 value to 2 or more or else fail. -+ ** If there were a failure, the prepared statement would have halted -+ ** before reaching this instruction. */ -+ assert( p2>=2 ); -+ } - }else{ - wrFlag = 0; -- } -- if( pOp->p5 & OPFLAG_P2ISREG ){ -- assert( p2>0 ); -- assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); -- assert( pOp->opcode==OP_OpenWrite ); -- pIn2 = &aMem[p2]; -- assert( memIsValid(pIn2) ); -- assert( (pIn2->flags & MEM_Int)!=0 ); -- sqlite3VdbeMemIntegerify(pIn2); -- p2 = (int)pIn2->u.i; -- /* The p2 value always comes from a prior OP_CreateBtree opcode and -- ** that opcode will always set the p2 value to 2 or more or else fail. -- ** If there were a failure, the prepared statement would have halted -- ** before reaching this instruction. */ -- assert( p2>=2 ); -+ assert( (pOp->p5 & OPFLAG_P2ISREG)==0 ); - } - if( pOp->p4type==P4_KEYINFO ){ - pKeyInfo = pOp->p4.pKeyInfo; -@@ -89165,8 +98724,9 @@ case OP_OpenWrite: - assert( pOp->p1>=0 ); - assert( nField>=0 ); - testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ -- pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); -+ pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_BTREE); - if( pCur==0 ) goto no_mem; -+ pCur->iDb = iDb; - pCur->nullRow = 1; - pCur->isOrdered = 1; - pCur->pgnoRoot = p2; -@@ -89200,15 +98760,15 @@ open_cursor_set_hints: - ** - ** Duplicate ephemeral cursors are used for self-joins of materialized views. - */ --case OP_OpenDup: { -+case OP_OpenDup: { /* ncycle */ - VdbeCursor *pOrig; /* The original cursor to be duplicated */ - VdbeCursor *pCx; /* The new cursor */ - - pOrig = p->apCsr[pOp->p2]; - assert( pOrig ); -- assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */ -+ assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */ - -- pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); -+ pCx = allocateCursor(p, pOp->p1, pOrig->nField, CURTYPE_BTREE); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - pCx->isEphemeral = 1; -@@ -89216,7 +98776,10 @@ case OP_OpenDup: { - pCx->isTable = pOrig->isTable; - pCx->pgnoRoot = pOrig->pgnoRoot; - pCx->isOrdered = pOrig->isOrdered; -- rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, -+ pCx->ub.pBtx = pOrig->ub.pBtx; -+ pCx->noReuse = 1; -+ pOrig->noReuse = 1; -+ rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, - pCx->pKeyInfo, pCx->uc.pCursor); - /* The sqlite3BtreeCursor() routine can only fail for the first cursor - ** opened for a database. Since there is already an open cursor when this -@@ -89226,7 +98789,7 @@ case OP_OpenDup: { - } - - --/* Opcode: OpenEphemeral P1 P2 * P4 P5 -+/* Opcode: OpenEphemeral P1 P2 P3 P4 P5 - ** Synopsis: nColumn=P2 - ** - ** Open a new cursor P1 to a transient table. -@@ -89246,6 +98809,10 @@ case OP_OpenDup: { - ** in btree.h. These flags control aspects of the operation of - ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are - ** added automatically. -+** -+** If P3 is positive, then reg[P3] is modified slightly so that it -+** can be used as zero-length data for OP_Insert. This is an optimization -+** that avoids an extra OP_Blob opcode to initialize that register. - */ - /* Opcode: OpenAutoindex P1 P2 * P4 * - ** Synopsis: nColumn=P2 -@@ -89255,8 +98822,8 @@ case OP_OpenDup: { - ** by this opcode will be used for automatically created transient - ** indices in joins. - */ --case OP_OpenAutoindex: --case OP_OpenEphemeral: { -+case OP_OpenAutoindex: /* ncycle */ -+case OP_OpenEphemeral: { /* ncycle */ - VdbeCursor *pCx; - KeyInfo *pKeyInfo; - -@@ -89268,50 +98835,68 @@ case OP_OpenEphemeral: { - SQLITE_OPEN_TRANSIENT_DB; - assert( pOp->p1>=0 ); - assert( pOp->p2>=0 ); -+ if( pOp->p3>0 ){ -+ /* Make register reg[P3] into a value that can be used as the data -+ ** form sqlite3BtreeInsert() where the length of the data is zero. */ -+ assert( pOp->p2==0 ); /* Only used when number of columns is zero */ -+ assert( pOp->opcode==OP_OpenEphemeral ); -+ assert( aMem[pOp->p3].flags & MEM_Null ); -+ aMem[pOp->p3].n = 0; -+ aMem[pOp->p3].z = ""; -+ } - pCx = p->apCsr[pOp->p1]; -- if( pCx && pCx->pBtx ){ -- /* If the ephermeral table is already open, erase all existing content -- ** so that the table is empty again, rather than creating a new table. */ -+ if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){ -+ /* If the ephemeral table is already open and has no duplicates from -+ ** OP_OpenDup, then erase all existing content so that the table is -+ ** empty again, rather than creating a new table. */ - assert( pCx->isEphemeral ); - pCx->seqCount = 0; - pCx->cacheStatus = CACHE_STALE; -- rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); -+ rc = sqlite3BtreeClearTable(pCx->ub.pBtx, pCx->pgnoRoot, 0); - }else{ -- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); -+ pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_BTREE); - if( pCx==0 ) goto no_mem; - pCx->isEphemeral = 1; -- rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, -+ rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->ub.pBtx, - BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, - vfsFlags); - if( rc==SQLITE_OK ){ -- rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); -- } -- if( rc==SQLITE_OK ){ -- /* If a transient index is required, create it by calling -- ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before -- ** opening it. If a transient table is required, just use the -- ** automatically created table with root-page 1 (an BLOB_INTKEY table). -- */ -- if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ -- assert( pOp->p4type==P4_KEYINFO ); -- rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, -- BTREE_BLOBKEY | pOp->p5); -- if( rc==SQLITE_OK ){ -- assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); -- assert( pKeyInfo->db==db ); -- assert( pKeyInfo->enc==ENC(db) ); -- rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, -- pKeyInfo, pCx->uc.pCursor); -+ rc = sqlite3BtreeBeginTrans(pCx->ub.pBtx, 1, 0); -+ if( rc==SQLITE_OK ){ -+ /* If a transient index is required, create it by calling -+ ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before -+ ** opening it. If a transient table is required, just use the -+ ** automatically created table with root-page 1 (an BLOB_INTKEY table). -+ */ -+ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ -+ assert( pOp->p4type==P4_KEYINFO ); -+ rc = sqlite3BtreeCreateTable(pCx->ub.pBtx, &pCx->pgnoRoot, -+ BTREE_BLOBKEY | pOp->p5); -+ if( rc==SQLITE_OK ){ -+ assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); -+ assert( pKeyInfo->db==db ); -+ assert( pKeyInfo->enc==ENC(db) ); -+ rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR, -+ pKeyInfo, pCx->uc.pCursor); -+ } -+ pCx->isTable = 0; -+ }else{ -+ pCx->pgnoRoot = SCHEMA_ROOT; -+ rc = sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR, -+ 0, pCx->uc.pCursor); -+ pCx->isTable = 1; - } -- pCx->isTable = 0; -+ } -+ pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); -+ assert( p->apCsr[pOp->p1]==pCx ); -+ if( rc ){ -+ assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); -+ sqlite3BtreeClose(pCx->ub.pBtx); -+ p->apCsr[pOp->p1] = 0; /* Not required; helps with static analysis */ - }else{ -- pCx->pgnoRoot = SCHEMA_ROOT; -- rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, -- 0, pCx->uc.pCursor); -- pCx->isTable = 1; -+ assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); - } - } -- pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); - } - if( rc ) goto abort_due_to_error; - pCx->nullRow = 1; -@@ -89333,7 +98918,7 @@ case OP_SorterOpen: { - - assert( pOp->p1>=0 ); - assert( pOp->p2>=0 ); -- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER); -+ pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_SORTER); - if( pCx==0 ) goto no_mem; - pCx->pKeyInfo = pOp->p4.pKeyInfo; - assert( pCx->pKeyInfo->db==db ); -@@ -89375,14 +98960,15 @@ case OP_SequenceTest: { - ** is the only cursor opcode that works with a pseudo-table. - ** - ** P3 is the number of fields in the records that will be stored by --** the pseudo-table. -+** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor -+** will return NULL for every column. - */ - case OP_OpenPseudo: { - VdbeCursor *pCx; - - assert( pOp->p1>=0 ); - assert( pOp->p3>=0 ); -- pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO); -+ pCx = allocateCursor(p, pOp->p1, pOp->p3, CURTYPE_PSEUDO); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - pCx->seekResult = pOp->p2; -@@ -89401,7 +98987,7 @@ case OP_OpenPseudo: { - ** Close a cursor previously opened as P1. If P1 is not - ** currently open, this instruction is a no-op. - */ --case OP_Close: { -+case OP_Close: { /* ncycle */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]); - p->apCsr[pOp->p1] = 0; -@@ -89518,10 +99104,10 @@ case OP_ColumnsUsed: { - ** - ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt - */ --case OP_SeekLT: /* jump, in3, group */ --case OP_SeekLE: /* jump, in3, group */ --case OP_SeekGE: /* jump, in3, group */ --case OP_SeekGT: { /* jump, in3, group */ -+case OP_SeekLT: /* jump0, in3, group, ncycle */ -+case OP_SeekLE: /* jump0, in3, group, ncycle */ -+case OP_SeekGE: /* jump0, in3, group, ncycle */ -+case OP_SeekGT: { /* jump0, in3, group, ncycle */ - int res; /* Comparison result */ - int oc; /* Opcode */ - VdbeCursor *pC; /* The cursor to seek */ -@@ -89570,6 +99156,7 @@ case OP_SeekGT: { /* jump, in3, group */ - /* If the P3 value could not be converted into an integer without - ** loss of information, then special processing is required... */ - if( (newType & (MEM_Int|MEM_IntReal))==0 ){ -+ int c; - if( (newType & MEM_Real)==0 ){ - if( (newType & MEM_Null) || oc>=OP_SeekGE ){ - VdbeBranchTaken(1,2); -@@ -89579,7 +99166,8 @@ case OP_SeekGT: { /* jump, in3, group */ - if( rc!=SQLITE_OK ) goto abort_due_to_error; - goto seek_not_found; - } -- }else -+ } -+ c = sqlite3IntFloatCompare(iKey, pIn3->u.r); - - /* If the approximation iKey is larger than the actual real search - ** term, substitute >= for > and < for <=. e.g. if the search term -@@ -89588,7 +99176,7 @@ case OP_SeekGT: { /* jump, in3, group */ - ** (x > 4.9) -> (x >= 5) - ** (x <= 4.9) -> (x < 5) - */ -- if( pIn3->u.r<(double)iKey ){ -+ if( c>0 ){ - assert( OP_SeekGE==(OP_SeekGT-1) ); - assert( OP_SeekLT==(OP_SeekLE-1) ); - assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) ); -@@ -89597,14 +99185,14 @@ case OP_SeekGT: { /* jump, in3, group */ - - /* If the approximation iKey is smaller than the actual real search - ** term, substitute <= for < and > for >=. */ -- else if( pIn3->u.r>(double)iKey ){ -+ else if( c<0 ){ - assert( OP_SeekLE==(OP_SeekLT+1) ); - assert( OP_SeekGT==(OP_SeekGE+1) ); - assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); - if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; - } - } -- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res); -+ rc = sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)iKey, 0, &res); - pC->movetoTarget = iKey; /* Used by OP_Delete */ - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; -@@ -89648,10 +99236,16 @@ case OP_SeekGT: { /* jump, in3, group */ - - r.aMem = &aMem[pOp->p3]; - #ifdef SQLITE_DEBUG -- { int i; for(i=0; i0 ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]); -+ } -+ } - #endif - r.eqSeen = 0; -- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res); -+ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } -@@ -89710,34 +99304,235 @@ seek_not_found: - break; - } - --/* Opcode: SeekHit P1 P2 * * * --** Synopsis: seekHit=P2 -+ -+/* Opcode: SeekScan P1 P2 * * P5 -+** Synopsis: Scan-ahead up to P1 rows -+** -+** This opcode is a prefix opcode to OP_SeekGE. In other words, this -+** opcode must be immediately followed by OP_SeekGE. This constraint is -+** checked by assert() statements. -+** -+** This opcode uses the P1 through P4 operands of the subsequent -+** OP_SeekGE. In the text that follows, the operands of the subsequent -+** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only -+** the P1, P2 and P5 operands of this opcode are also used, and are called -+** This.P1, This.P2 and This.P5. -+** -+** This opcode helps to optimize IN operators on a multi-column index -+** where the IN operator is on the later terms of the index by avoiding -+** unnecessary seeks on the btree, substituting steps to the next row -+** of the b-tree instead. A correct answer is obtained if this opcode -+** is omitted or is a no-op. -+** -+** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which -+** is the desired entry that we want the cursor SeekGE.P1 to be pointing -+** to. Call this SeekGE.P3/P4 row the "target". -+** -+** If the SeekGE.P1 cursor is not currently pointing to a valid row, -+** then this opcode is a no-op and control passes through into the OP_SeekGE. -+** -+** If the SeekGE.P1 cursor is pointing to a valid row, then that row -+** might be the target row, or it might be near and slightly before the -+** target row, or it might be after the target row. If the cursor is -+** currently before the target row, then this opcode attempts to position -+** the cursor on or after the target row by invoking sqlite3BtreeStep() -+** on the cursor between 1 and This.P1 times. -+** -+** The This.P5 parameter is a flag that indicates what to do if the -+** cursor ends up pointing at a valid row that is past the target -+** row. If This.P5 is false (0) then a jump is made to SeekGE.P2. If -+** This.P5 is true (non-zero) then a jump is made to This.P2. The P5==0 -+** case occurs when there are no inequality constraints to the right of -+** the IN constraint. The jump to SeekGE.P2 ends the loop. The P5!=0 case -+** occurs when there are inequality constraints to the right of the IN -+** operator. In that case, the This.P2 will point either directly to or -+** to setup code prior to the OP_IdxGT or OP_IdxGE opcode that checks for -+** loop terminate. -+** -+** Possible outcomes from this opcode:
          -+** -+**
        1. If the cursor is initially not pointed to any valid row, then -+** fall through into the subsequent OP_SeekGE opcode. -+** -+**
        2. If the cursor is left pointing to a row that is before the target -+** row, even after making as many as This.P1 calls to -+** sqlite3BtreeNext(), then also fall through into OP_SeekGE. -+** -+**
        3. If the cursor is left pointing at the target row, either because it -+** was at the target row to begin with or because one or more -+** sqlite3BtreeNext() calls moved the cursor to the target row, -+** then jump to This.P2.., -+** -+**
        4. If the cursor started out before the target row and a call to -+** to sqlite3BtreeNext() moved the cursor off the end of the index -+** (indicating that the target row definitely does not exist in the -+** btree) then jump to SeekGE.P2, ending the loop. -+** -+**
        5. If the cursor ends up on a valid row that is past the target row -+** (indicating that the target row does not exist in the btree) then -+** jump to SeekOP.P2 if This.P5==0 or to This.P2 if This.P5>0. -+**
        -+*/ -+case OP_SeekScan: { /* ncycle */ -+ VdbeCursor *pC; -+ int res; -+ int nStep; -+ UnpackedRecord r; -+ -+ assert( pOp[1].opcode==OP_SeekGE ); -+ -+ /* If pOp->p5 is clear, then pOp->p2 points to the first instruction past the -+ ** OP_IdxGT that follows the OP_SeekGE. Otherwise, it points to the first -+ ** opcode past the OP_SeekGE itself. */ -+ assert( pOp->p2>=(int)(pOp-aOp)+2 ); -+#ifdef SQLITE_DEBUG -+ if( pOp->p5==0 ){ -+ /* There are no inequality constraints following the IN constraint. */ -+ assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); -+ assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); -+ assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); -+ assert( aOp[pOp->p2-1].opcode==OP_IdxGT -+ || aOp[pOp->p2-1].opcode==OP_IdxGE ); -+ testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); -+ }else{ -+ /* There are inequality constraints. */ -+ assert( pOp->p2==(int)(pOp-aOp)+2 ); -+ assert( aOp[pOp->p2-1].opcode==OP_SeekGE ); -+ } -+#endif -+ -+ assert( pOp->p1>0 ); -+ pC = p->apCsr[pOp[1].p1]; -+ assert( pC!=0 ); -+ assert( pC->eCurType==CURTYPE_BTREE ); -+ assert( !pC->isTable ); -+ if( !sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) ){ -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ printf("... cursor not valid - fall through\n"); -+ } -+#endif -+ break; -+ } -+ nStep = pOp->p1; -+ assert( nStep>=1 ); -+ r.pKeyInfo = pC->pKeyInfo; -+ r.nField = (u16)pOp[1].p4.i; -+ r.default_rc = 0; -+ r.aMem = &aMem[pOp[1].p3]; -+#ifdef SQLITE_DEBUG -+ { -+ int i; -+ for(i=0; i0 && pOp->p5==0 ){ -+ seekscan_search_fail: -+ /* Jump to SeekGE.P2, ending the loop */ -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ printf("... %d steps and then skip\n", pOp->p1 - nStep); -+ } -+#endif -+ VdbeBranchTaken(1,3); -+ pOp++; -+ goto jump_to_p2; -+ } -+ if( res>=0 ){ -+ /* Jump to This.P2, bypassing the OP_SeekGE opcode */ -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ printf("... %d steps and then success\n", pOp->p1 - nStep); -+ } -+#endif -+ VdbeBranchTaken(2,3); -+ goto jump_to_p2; -+ break; -+ } -+ if( nStep<=0 ){ -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ printf("... fall through after %d steps\n", pOp->p1); -+ } -+#endif -+ VdbeBranchTaken(0,3); -+ break; -+ } -+ nStep--; -+ pC->cacheStatus = CACHE_STALE; -+ rc = sqlite3BtreeNext(pC->uc.pCursor, 0); -+ if( rc ){ -+ if( rc==SQLITE_DONE ){ -+ rc = SQLITE_OK; -+ goto seekscan_search_fail; -+ }else{ -+ goto abort_due_to_error; -+ } -+ } -+ } -+ -+ break; -+} -+ -+ -+/* Opcode: SeekHit P1 P2 P3 * * -+** Synopsis: set P2<=seekHit<=P3 -+** -+** Increase or decrease the seekHit value for cursor P1, if necessary, -+** so that it is no less than P2 and no greater than P3. - ** --** Set the seekHit flag on cursor P1 to the value in P2. --** The seekHit flag is used by the IfNoHope opcode. -+** The seekHit integer represents the maximum of terms in an index for which -+** there is known to be at least one match. If the seekHit value is smaller -+** than the total number of equality terms in an index lookup, then the -+** OP_IfNoHope opcode might run to see if the IN loop can be abandoned -+** early, thus saving work. This is part of the IN-early-out optimization. - ** --** P1 must be a valid b-tree cursor. P2 must be a boolean value, --** either 0 or 1. -+** P1 must be a valid b-tree cursor. - */ --case OP_SeekHit: { -+case OP_SeekHit: { /* ncycle */ - VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); -- assert( pOp->p2==0 || pOp->p2==1 ); -- pC->seekHit = pOp->p2 & 1; -+ assert( pOp->p3>=pOp->p2 ); -+ if( pC->seekHitp2 ){ -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p2); -+ } -+#endif -+ pC->seekHit = pOp->p2; -+ }else if( pC->seekHit>pOp->p3 ){ -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p3); -+ } -+#endif -+ pC->seekHit = pOp->p3; -+ } - break; - } - - /* Opcode: IfNotOpen P1 P2 * * * - ** Synopsis: if( !csr[P1] ) goto P2 - ** --** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through. -+** If cursor P1 is not open or if P1 is set to a NULL row using the -+** OP_NullRow opcode, then jump to instruction P2. Otherwise, fall through. - */ - case OP_IfNotOpen: { /* jump */ -+ VdbeCursor *pCur; -+ - assert( pOp->p1>=0 && pOp->p1nCursor ); -- VdbeBranchTaken(p->apCsr[pOp->p1]==0, 2); -- if( !p->apCsr[pOp->p1] ){ -+ pCur = p->apCsr[pOp->p1]; -+ VdbeBranchTaken(pCur==0 || pCur->nullRow, 2); -+ if( pCur==0 || pCur->nullRow ){ - goto jump_to_p2_and_check_for_interrupt; - } - break; -@@ -89783,16 +99578,20 @@ case OP_IfNotOpen: { /* jump */ - ** Synopsis: key=r[P3@P4] - ** - ** Register P3 is the first of P4 registers that form an unpacked --** record. -+** record. Cursor P1 is an index btree. P2 is a jump destination. -+** In other words, the operands to this opcode are the same as the -+** operands to OP_NotFound and OP_IdxGT. - ** --** Cursor P1 is on an index btree. If the seekHit flag is set on P1, then --** this opcode is a no-op. But if the seekHit flag of P1 is clear, then --** check to see if there is any entry in P1 that matches the --** prefix identified by P3 and P4. If no entry matches the prefix, --** jump to P2. Otherwise fall through. -+** This opcode is an optimization attempt only. If this opcode always -+** falls through, the correct answer is still obtained, but extra work -+** is performed. - ** --** This opcode behaves like OP_NotFound if the seekHit --** flag is clear and it behaves like OP_Noop if the seekHit flag is set. -+** A value of N in the seekHit flag of cursor P1 means that there exists -+** a key P3:N that will match some record in the index. We want to know -+** if it is possible for a record P3:P4 to match some record in the -+** index. If it is not possible, we can skip some work. So if seekHit -+** is less than P4, attempt to find out if a match is possible by running -+** OP_NotFound. - ** - ** This opcode is used in IN clause processing for a multi-column key. - ** If an IN clause is attached to an element of the key other than the -@@ -89829,24 +99628,26 @@ case OP_IfNotOpen: { /* jump */ - ** - ** See also: NotFound, Found, NotExists - */ --case OP_IfNoHope: { /* jump, in3 */ -+case OP_IfNoHope: { /* jump, in3, ncycle */ - VdbeCursor *pC; - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); -- if( pC->seekHit ) break; -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ printf("seekHit is %d\n", pC->seekHit); -+ } -+#endif -+ if( pC->seekHit>=pOp->p4.i ) break; - /* Fall through into OP_NotFound */ - /* no break */ deliberate_fall_through - } --case OP_NoConflict: /* jump, in3 */ --case OP_NotFound: /* jump, in3 */ --case OP_Found: { /* jump, in3 */ -+case OP_NoConflict: /* jump, in3, ncycle */ -+case OP_NotFound: /* jump, in3, ncycle */ -+case OP_Found: { /* jump, in3, ncycle */ - int alreadyExists; -- int takeJump; - int ii; - VdbeCursor *pC; -- int res; -- UnpackedRecord *pFree; - UnpackedRecord *pIdxKey; - UnpackedRecord r; - -@@ -89861,52 +99662,42 @@ case OP_Found: { /* jump, in3 */ - #ifdef SQLITE_DEBUG - pC->seekOp = pOp->opcode; - #endif -- pIn3 = &aMem[pOp->p3]; -+ r.aMem = &aMem[pOp->p3]; - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0 ); - assert( pC->isTable==0 ); -- if( pOp->p4.i>0 ){ -+ r.nField = (u16)pOp->p4.i; -+ if( r.nField>0 ){ -+ /* Key values in an array of registers */ - r.pKeyInfo = pC->pKeyInfo; -- r.nField = (u16)pOp->p4.i; -- r.aMem = pIn3; -+ r.default_rc = 0; - #ifdef SQLITE_DEBUG -+ (void)sqlite3FaultSim(50); /* For use by --counter in TH3 */ - for(ii=0; iip3+ii, &r.aMem[ii]); - } - #endif -- pIdxKey = &r; -- pFree = 0; -+ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &pC->seekResult); - }else{ -- assert( pIn3->flags & MEM_Blob ); -- rc = ExpandBlob(pIn3); -+ /* Composite key generated by OP_MakeRecord */ -+ assert( r.aMem->flags & MEM_Blob ); -+ assert( pOp->opcode!=OP_NoConflict ); -+ rc = ExpandBlob(r.aMem); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - if( rc ) goto no_mem; -- pFree = pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); -+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo); - if( pIdxKey==0 ) goto no_mem; -- sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); -- } -- pIdxKey->default_rc = 0; -- takeJump = 0; -- if( pOp->opcode==OP_NoConflict ){ -- /* For the OP_NoConflict opcode, take the jump if any of the -- ** input fields are NULL, since any key with a NULL will not -- ** conflict */ -- for(ii=0; iinField; ii++){ -- if( pIdxKey->aMem[ii].flags & MEM_Null ){ -- takeJump = 1; -- break; -- } -- } -+ sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey); -+ pIdxKey->default_rc = 0; -+ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult); -+ sqlite3DbFreeNN(db, pIdxKey); - } -- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); -- if( pFree ) sqlite3DbFreeNN(db, pFree); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } -- pC->seekResult = res; -- alreadyExists = (res==0); -+ alreadyExists = (pC->seekResult==0); - pC->nullRow = 1-alreadyExists; - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; -@@ -89914,8 +99705,25 @@ case OP_Found: { /* jump, in3 */ - VdbeBranchTaken(alreadyExists!=0,2); - if( alreadyExists ) goto jump_to_p2; - }else{ -- VdbeBranchTaken(takeJump||alreadyExists==0,2); -- if( takeJump || !alreadyExists ) goto jump_to_p2; -+ if( !alreadyExists ){ -+ VdbeBranchTaken(1,2); -+ goto jump_to_p2; -+ } -+ if( pOp->opcode==OP_NoConflict ){ -+ /* For the OP_NoConflict opcode, take the jump if any of the -+ ** input fields are NULL, since any key with a NULL will not -+ ** conflict */ -+ for(ii=0; iiopcode==OP_IfNoHope ){ -+ pC->seekHit = pOp->p4.i; -+ } - } - break; - } -@@ -89967,7 +99775,7 @@ case OP_Found: { /* jump, in3 */ - ** - ** See also: Found, NotFound, NoConflict, SeekRowid - */ --case OP_SeekRowid: { /* jump, in3 */ -+case OP_SeekRowid: { /* jump0, in3, ncycle */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; -@@ -89992,7 +99800,7 @@ case OP_SeekRowid: { /* jump, in3 */ - } - /* Fall through into OP_NotExists */ - /* no break */ deliberate_fall_through --case OP_NotExists: /* jump, in3 */ -+case OP_NotExists: /* jump, in3, ncycle */ - pIn3 = &aMem[pOp->p3]; - assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); - assert( pOp->p1>=0 && pOp->p1nCursor ); -@@ -90008,7 +99816,7 @@ notExistsWithKey: - pCrsr = pC->uc.pCursor; - assert( pCrsr!=0 ); - res = 0; -- rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); -+ rc = sqlite3BtreeTableMoveto(pCrsr, iKey, 0, &res); - assert( rc==SQLITE_OK || res==0 ); - pC->movetoTarget = iKey; /* Used by OP_Delete */ - pC->nullRow = 0; -@@ -90066,8 +99874,10 @@ case OP_NewRowid: { /* out2 */ - VdbeCursor *pC; /* Cursor of table to get the new rowid */ - int res; /* Result of an sqlite3BtreeLast() */ - int cnt; /* Counter to limit the number of searches */ -+#ifndef SQLITE_OMIT_AUTOINCREMENT - Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ - VdbeFrame *pFrame; /* Root frame of VDBE */ -+#endif - - v = 0; - res = 0; -@@ -90163,7 +99973,7 @@ case OP_NewRowid: { /* out2 */ - do{ - sqlite3_randomness(sizeof(v), &v); - v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */ -- }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v, -+ }while( ((rc = sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)v, - 0, &res))==SQLITE_OK) - && (res==0) - && (++cnt<100)); -@@ -90253,14 +100063,14 @@ case OP_Insert: { - assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) ); - }else{ - pTab = 0; -- zDb = 0; /* Not needed. Silence a compiler warning. */ -+ zDb = 0; - } - - #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - /* Invoke the pre-update hook, if any */ - if( pTab ){ - if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){ -- sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey,pOp->p2); -+ sqlite3VdbePreUpdateHook(p,pC,SQLITE_INSERT,zDb,pTab,x.nKey,pOp->p2,-1); - } - if( db->xUpdateCallback==0 || pTab->aCol==0 ){ - /* Prevent post-update hook from running in cases when it should not */ -@@ -90270,9 +100080,12 @@ case OP_Insert: { - if( pOp->p5 & OPFLAG_ISNOOP ) break; - #endif - -- if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; -- if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; -- assert( pData->flags & (MEM_Blob|MEM_Str) ); -+ assert( (pOp->p5 & OPFLAG_LASTROWID)==0 || (pOp->p5 & OPFLAG_NCHANGE)!=0 ); -+ if( pOp->p5 & OPFLAG_NCHANGE ){ -+ p->nChange++; -+ if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; -+ } -+ assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); - x.pData = pData->z; - x.nData = pData->n; - seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); -@@ -90282,11 +100095,14 @@ case OP_Insert: { - x.nZero = 0; - } - x.pKey = 0; -+ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); - rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, -- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult -+ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), -+ seekResult - ); - pC->deferredMoveto = 0; - pC->cacheStatus = CACHE_STALE; -+ colCacheCtr++; - - /* Invoke the update-hook if required. */ - if( rc ) goto abort_due_to_error; -@@ -90300,6 +100116,33 @@ case OP_Insert: { - break; - } - -+/* Opcode: RowCell P1 P2 P3 * * -+** -+** P1 and P2 are both open cursors. Both must be opened on the same type -+** of table - intkey or index. This opcode is used as part of copying -+** the current row from P2 into P1. If the cursors are opened on intkey -+** tables, register P3 contains the rowid to use with the new record in -+** P1. If they are opened on index tables, P3 is not used. -+** -+** This opcode must be followed by either an Insert or InsertIdx opcode -+** with the OPFLAG_PREFORMAT flag set to complete the insert operation. -+*/ -+case OP_RowCell: { -+ VdbeCursor *pDest; /* Cursor to write to */ -+ VdbeCursor *pSrc; /* Cursor to read from */ -+ i64 iKey; /* Rowid value to insert with */ -+ assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert ); -+ assert( pOp[1].opcode==OP_Insert || pOp->p3==0 ); -+ assert( pOp[1].opcode==OP_IdxInsert || pOp->p3>0 ); -+ assert( pOp[1].p5 & OPFLAG_PREFORMAT ); -+ pDest = p->apCsr[pOp->p1]; -+ pSrc = p->apCsr[pOp->p2]; -+ iKey = pOp->p3 ? aMem[pOp->p3].u.i : 0; -+ rc = sqlite3BtreeTransferRow(pDest->uc.pCursor, pSrc->uc.pCursor, iKey); -+ if( rc!=SQLITE_OK ) goto abort_due_to_error; -+ break; -+}; -+ - /* Opcode: Delete P1 P2 P3 P4 P5 - ** - ** Delete the record at which the P1 cursor is currently pointing. -@@ -90313,13 +100156,18 @@ case OP_Insert: { - ** left in an undefined state. - ** - ** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this --** delete one of several associated with deleting a table row and all its --** associated index entries. Exactly one of those deletes is the "primary" --** delete. The others are all on OPFLAG_FORDELETE cursors or else are --** marked with the AUXDELETE flag. -+** delete is one of several associated with deleting a table row and -+** all its associated index entries. Exactly one of those deletes is -+** the "primary" delete. The others are all on OPFLAG_FORDELETE -+** cursors or else are marked with the AUXDELETE flag. - ** --** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row --** change count is incremented (otherwise not). -+** If the OPFLAG_NCHANGE (0x01) flag of P2 (NB: P2 not P5) is set, then -+** the row change count is incremented (otherwise not). -+** -+** If the OPFLAG_ISNOOP (0x40) flag of P2 (not P5!) is set, then the -+** pre-update-hook for deletes is run, but the btree is otherwise unchanged. -+** This happens when the OP_Delete is to be shortly followed by an OP_Insert -+** with the same key, causing the btree entry to be overwritten. - ** - ** P1 must not be pseudo-table. It has to be a real table with - ** multiple rows. -@@ -90378,13 +100226,14 @@ case OP_Delete: { - pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor); - } - }else{ -- zDb = 0; /* Not needed. Silence a compiler warning. */ -- pTab = 0; /* Not needed. Silence a compiler warning. */ -+ zDb = 0; -+ pTab = 0; - } - - #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - /* Invoke the pre-update-hook if required. */ -- if( db->xPreUpdateCallback && pOp->p4.pTab ){ -+ assert( db->xPreUpdateCallback==0 || pTab==pOp->p4.pTab ); -+ if( db->xPreUpdateCallback && pTab ){ - assert( !(opflags & OPFLAG_ISUPDATE) - || HasRowid(pTab)==0 - || (aMem[pOp->p3].flags & MEM_Int) -@@ -90392,7 +100241,7 @@ case OP_Delete: { - sqlite3VdbePreUpdateHook(p, pC, - (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, - zDb, pTab, pC->movetoTarget, -- pOp->p3 -+ pOp->p3, -1 - ); - } - if( opflags & OPFLAG_ISNOOP ) break; -@@ -90419,13 +100268,14 @@ case OP_Delete: { - - rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); - pC->cacheStatus = CACHE_STALE; -+ colCacheCtr++; - pC->seekResult = 0; - if( rc ) goto abort_due_to_error; - - /* Invoke the update-hook if required. */ - if( opflags & OPFLAG_NCHANGE ){ - p->nChange++; -- if( db->xUpdateCallback && HasRowid(pTab) ){ -+ if( db->xUpdateCallback && ALWAYS(pTab!=0) && HasRowid(pTab) ){ - db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName, - pC->movetoTarget); - assert( pC->iDb>=0 ); -@@ -90486,13 +100336,13 @@ case OP_SorterCompare: { - ** Write into register P2 the current sorter data for sorter cursor P1. - ** Then clear the column header cache on cursor P3. - ** --** This opcode is normally use to move a record out of the sorter and into -+** This opcode is normally used to move a record out of the sorter and into - ** a register that is the source for a pseudo-table cursor created using - ** OpenPseudo. That pseudo-table cursor is the one that is identified by - ** parameter P3. Clearing the P3 column cache as part of this opcode saves - ** us from having to issue a separate NullRow instruction to clear that cache. - */ --case OP_SorterData: { -+case OP_SorterData: { /* ncycle */ - VdbeCursor *pC; - - pOut = &aMem[pOp->p2]; -@@ -90553,7 +100403,7 @@ case OP_RowData: { - /* The OP_RowData opcodes always follow OP_NotExists or - ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions - ** that might invalidate the cursor. -- ** If this where not the case, on of the following assert()s -+ ** If this were not the case, one of the following assert()s - ** would fail. Should this ever change (because of changes in the code - ** generator) then the fix would be to insert a call to - ** sqlite3VdbeCursorMoveto(). -@@ -90575,7 +100425,7 @@ case OP_RowData: { - } - - /* Opcode: Rowid P1 P2 * * * --** Synopsis: r[P2]=rowid -+** Synopsis: r[P2]=PX rowid of P1 - ** - ** Store in register P2 an integer which is the key of the table entry that - ** P1 is currently point to. -@@ -90584,7 +100434,7 @@ case OP_RowData: { - ** be a separate OP_VRowid opcode for use with virtual tables, but this - ** one opcode now works for both table types. - */ --case OP_Rowid: { /* out2 */ -+case OP_Rowid: { /* out2, ncycle */ - VdbeCursor *pC; - i64 v; - sqlite3_vtab *pVtab; -@@ -90630,13 +100480,25 @@ case OP_Rowid: { /* out2 */ - ** Move the cursor P1 to a null row. Any OP_Column operations - ** that occur while the cursor is on the null row will always - ** write a NULL. -+** -+** If cursor P1 is not previously opened, open it now to a special -+** pseudo-cursor that always returns NULL for every column. - */ - case OP_NullRow: { - VdbeCursor *pC; - - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; -- assert( pC!=0 ); -+ if( pC==0 ){ -+ /* If the cursor is not already open, create a special kind of -+ ** pseudo-cursor that always gives null rows. */ -+ pC = allocateCursor(p, pOp->p1, 1, CURTYPE_PSEUDO); -+ if( pC==0 ) goto no_mem; -+ pC->seekResult = 0; -+ pC->isTable = 1; -+ pC->noReuse = 1; -+ pC->uc.pCursor = sqlite3BtreeFakeValidCursor(); -+ } - pC->nullRow = 1; - pC->cacheStatus = CACHE_STALE; - if( pC->eCurType==CURTYPE_BTREE ){ -@@ -90671,8 +100533,8 @@ case OP_NullRow: { - ** from the end toward the beginning. In other words, the cursor is - ** configured to use Prev, not Next. - */ --case OP_SeekEnd: --case OP_Last: { /* jump */ -+case OP_SeekEnd: /* ncycle */ -+case OP_Last: { /* jump0, ncycle */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; -@@ -90706,28 +100568,38 @@ case OP_Last: { /* jump */ - break; - } - --/* Opcode: IfSmaller P1 P2 P3 * * -+/* Opcode: IfSizeBetween P1 P2 P3 P4 * - ** --** Estimate the number of rows in the table P1. Jump to P2 if that --** estimate is less than approximately 2**(0.1*P3). -+** Let N be the approximate number of rows in the table or index -+** with cursor P1 and let X be 10*log2(N) if N is positive or -1 -+** if N is zero. -+** -+** Jump to P2 if X is in between P3 and P4, inclusive. - */ --case OP_IfSmaller: { /* jump */ -+case OP_IfSizeBetween: { /* jump */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - i64 sz; - - assert( pOp->p1>=0 && pOp->p1nCursor ); -+ assert( pOp->p4type==P4_INT32 ); -+ assert( pOp->p3>=-1 && pOp->p3<=640*2 ); -+ assert( pOp->p4.i>=-1 && pOp->p4.i<=640*2 ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - pCrsr = pC->uc.pCursor; - assert( pCrsr ); - rc = sqlite3BtreeFirst(pCrsr, &res); - if( rc ) goto abort_due_to_error; -- if( res==0 ){ -+ if( res!=0 ){ -+ sz = -1; /* -Infinity encoding */ -+ }else{ - sz = sqlite3BtreeRowCountEst(pCrsr); -- if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)p3 ) res = 1; -+ assert( sz>0 ); -+ sz = sqlite3LogEst((u64)sz); - } -+ res = sz>=pOp->p3 && sz<=pOp->p4.i; - VdbeBranchTaken(res!=0,2); - if( res ) goto jump_to_p2; - break; -@@ -90755,8 +100627,8 @@ case OP_IfSmaller: { /* jump */ - ** regression tests can determine whether or not the optimizer is - ** correctly optimizing out sorts. - */ --case OP_SorterSort: /* jump */ --case OP_Sort: { /* jump */ -+case OP_SorterSort: /* jump ncycle */ -+case OP_Sort: { /* jump ncycle */ - #ifdef SQLITE_TEST - sqlite3_sort_count++; - sqlite3_search_count--; -@@ -90773,17 +100645,22 @@ case OP_Sort: { /* jump */ - ** If the table or index is not empty, fall through to the following - ** instruction. - ** -+** If P2 is zero, that is an assertion that the P1 table is never -+** empty and hence the jump will never be taken. -+** - ** This opcode leaves the cursor configured to move in forward order, - ** from the beginning toward the end. In other words, the cursor is - ** configured to use Next, not Prev. - */ --case OP_Rewind: { /* jump */ -+case OP_Rewind: { /* jump0, ncycle */ - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - - assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p5==0 ); -+ assert( pOp->p2>=0 && pOp->p2nOp ); -+ - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); -@@ -90803,13 +100680,14 @@ case OP_Rewind: { /* jump */ - } - if( rc ) goto abort_due_to_error; - pC->nullRow = (u8)res; -- assert( pOp->p2>0 && pOp->p2nOp ); -- VdbeBranchTaken(res!=0,2); -- if( res ) goto jump_to_p2; -+ if( pOp->p2>0 ){ -+ VdbeBranchTaken(res!=0,2); -+ if( res ) goto jump_to_p2; -+ } - break; - } - --/* Opcode: Next P1 P2 P3 P4 P5 -+/* Opcode: Next P1 P2 P3 * P5 - ** - ** Advance cursor P1 so that it points to the next key/data pair in its - ** table or index. If there are no more key/value pairs then fall through -@@ -90828,15 +100706,12 @@ case OP_Rewind: { /* jump */ - ** omitted if that index had been unique. P3 is usually 0. P3 is - ** always either 0 or 1. - ** --** P4 is always of type P4_ADVANCE. The function pointer points to --** sqlite3BtreeNext(). --** - ** If P5 is positive and the jump is taken, then event counter - ** number P5-1 in the prepared statement is incremented. - ** - ** See also: Prev - */ --/* Opcode: Prev P1 P2 P3 P4 P5 -+/* Opcode: Prev P1 P2 P3 * P5 - ** - ** Back up cursor P1 so that it points to the previous key/data pair in its - ** table or index. If there is no previous key/value pairs then fall through -@@ -90856,9 +100731,6 @@ case OP_Rewind: { /* jump */ - ** omitted if that index had been unique. P3 is usually 0. P3 is - ** always either 0 or 1. - ** --** P4 is always of type P4_ADVANCE. The function pointer points to --** sqlite3BtreePrevious(). --** - ** If P5 is positive and the jump is taken, then event counter - ** number P5-1 in the prepared statement is incremented. - */ -@@ -90876,30 +100748,37 @@ case OP_SorterNext: { /* jump */ - assert( isSorter(pC) ); - rc = sqlite3VdbeSorterNext(db, pC); - goto next_tail; --case OP_Prev: /* jump */ --case OP_Next: /* jump */ -+ -+case OP_Prev: /* jump, ncycle */ - assert( pOp->p1>=0 && pOp->p1nCursor ); -- assert( pOp->p5aCounter) ); -+ assert( pOp->p5==0 -+ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP -+ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); - assert( pC->deferredMoveto==0 ); - assert( pC->eCurType==CURTYPE_BTREE ); -- assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); -- assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); -+ assert( pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE -+ || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope -+ || pC->seekOp==OP_NullRow); -+ rc = sqlite3BtreePrevious(pC->uc.pCursor, pOp->p3); -+ goto next_tail; - -- /* The Next opcode is only used after SeekGT, SeekGE, Rewind, and Found. -- ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */ -- assert( pOp->opcode!=OP_Next -- || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE -+case OP_Next: /* jump, ncycle */ -+ assert( pOp->p1>=0 && pOp->p1nCursor ); -+ assert( pOp->p5==0 -+ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP -+ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX); -+ pC = p->apCsr[pOp->p1]; -+ assert( pC!=0 ); -+ assert( pC->deferredMoveto==0 ); -+ assert( pC->eCurType==CURTYPE_BTREE ); -+ assert( pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE - || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found - || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid - || pC->seekOp==OP_IfNoHope); -- assert( pOp->opcode!=OP_Prev -- || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE -- || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope -- || pC->seekOp==OP_NullRow); -+ rc = sqlite3BtreeNext(pC->uc.pCursor, pOp->p3); - -- rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3); - next_tail: - pC->cacheStatus = CACHE_STALE; - VdbeBranchTaken(rc==SQLITE_OK,2); -@@ -90955,7 +100834,7 @@ case OP_IdxInsert: { /* in2 */ - assert( pC!=0 ); - assert( !isSorter(pC) ); - pIn2 = &aMem[pOp->p2]; -- assert( pIn2->flags & MEM_Blob ); -+ assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) ); - if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->isTable==0 ); -@@ -90966,7 +100845,7 @@ case OP_IdxInsert: { /* in2 */ - x.aMem = aMem + pOp->p3; - x.nMem = (u16)pOp->p4.i; - rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, -- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), -+ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) - ); - assert( pC->deferredMoveto==0 ); -@@ -91012,7 +100891,8 @@ case OP_SorterInsert: { /* in2 */ - ** an UPDATE or DELETE statement and the index entry to be updated - ** or deleted is not found. For some uses of IdxDelete - ** (example: the EXCEPT operator) it does not matter that no matching --** entry is found. For those cases, P5 is zero. -+** entry is found. For those cases, P5 is zero. Also, do not raise -+** this (self-correcting and non-critical) error if in writable_schema mode. - */ - case OP_IdxDelete: { - VdbeCursor *pC; -@@ -91033,13 +100913,13 @@ case OP_IdxDelete: { - r.nField = (u16)pOp->p3; - r.default_rc = 0; - r.aMem = &aMem[pOp->p2]; -- rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); -+ rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res); - if( rc ) goto abort_due_to_error; - if( res==0 ){ - rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); - if( rc ) goto abort_due_to_error; -- }else if( pOp->p5 ){ -- rc = SQLITE_CORRUPT_INDEX; -+ }else if( pOp->p5 && !sqlite3WritableSchema(db) ){ -+ rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); - goto abort_due_to_error; - } - assert( pC->deferredMoveto==0 ); -@@ -91076,8 +100956,8 @@ case OP_IdxDelete: { - ** - ** See also: Rowid, MakeRecord. - */ --case OP_DeferredSeek: --case OP_IdxRowid: { /* out2 */ -+case OP_DeferredSeek: /* ncycle */ -+case OP_IdxRowid: { /* out2, ncycle */ - VdbeCursor *pC; /* The P1 index cursor */ - VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */ - i64 rowid; /* Rowid that P1 current points to */ -@@ -91085,9 +100965,9 @@ case OP_IdxRowid: { /* out2 */ - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; - assert( pC!=0 ); -- assert( pC->eCurType==CURTYPE_BTREE ); -+ assert( pC->eCurType==CURTYPE_BTREE || IsNullCursor(pC) ); - assert( pC->uc.pCursor!=0 ); -- assert( pC->isTable==0 ); -+ assert( pC->isTable==0 || IsNullCursor(pC) ); - assert( pC->deferredMoveto==0 ); - assert( !pC->nullRow || pOp->opcode==OP_IdxRowid ); - -@@ -91095,10 +100975,10 @@ case OP_IdxRowid: { /* out2 */ - ** of sqlite3VdbeCursorRestore() and sqlite3VdbeIdxRowid(). */ - rc = sqlite3VdbeCursorRestore(pC); - -- /* sqlite3VbeCursorRestore() can only fail if the record has been deleted -- ** out from under the cursor. That will never happens for an IdxRowid -- ** or Seek opcode */ -- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; -+ /* sqlite3VdbeCursorRestore() may fail if the cursor has been disturbed -+ ** since it was last positioned and an error (e.g. OOM or an IO error) -+ ** occurs while trying to reposition it. */ -+ if( rc!=SQLITE_OK ) goto abort_due_to_error; - - if( !pC->nullRow ){ - rowid = 0; /* Not needed. Only used to silence a warning. */ -@@ -91116,8 +100996,11 @@ case OP_IdxRowid: { /* out2 */ - pTabCur->nullRow = 0; - pTabCur->movetoTarget = rowid; - pTabCur->deferredMoveto = 1; -+ pTabCur->cacheStatus = CACHE_STALE; - assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); -- pTabCur->aAltMap = pOp->p4.ai; -+ assert( !pTabCur->isEphemeral ); -+ pTabCur->ub.aAltMap = pOp->p4.ai; -+ assert( !pC->isEphemeral ); - pTabCur->pAltCursor = pC; - }else{ - pOut = out2Prerelease(p, pOp); -@@ -91136,8 +101019,8 @@ case OP_IdxRowid: { /* out2 */ - ** seek operation now, without further delay. If the cursor seek has - ** already occurred, this instruction is a no-op. - */ --case OP_FinishSeek: { -- VdbeCursor *pC; /* The P1 index cursor */ -+case OP_FinishSeek: { /* ncycle */ -+ VdbeCursor *pC; /* The P1 index cursor */ - - assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; -@@ -91148,7 +101031,7 @@ case OP_FinishSeek: { - break; - } - --/* Opcode: IdxGE P1 P2 P3 P4 P5 -+/* Opcode: IdxGE P1 P2 P3 P4 * - ** Synopsis: key=r[P3@P4] - ** - ** The P4 register values beginning with P3 form an unpacked index -@@ -91159,7 +101042,7 @@ case OP_FinishSeek: { - ** If the P1 index entry is greater than or equal to the key value - ** then jump to P2. Otherwise fall through to the next instruction. - */ --/* Opcode: IdxGT P1 P2 P3 P4 P5 -+/* Opcode: IdxGT P1 P2 P3 P4 * - ** Synopsis: key=r[P3@P4] - ** - ** The P4 register values beginning with P3 form an unpacked index -@@ -91170,7 +101053,7 @@ case OP_FinishSeek: { - ** If the P1 index entry is greater than the key value - ** then jump to P2. Otherwise fall through to the next instruction. - */ --/* Opcode: IdxLT P1 P2 P3 P4 P5 -+/* Opcode: IdxLT P1 P2 P3 P4 * - ** Synopsis: key=r[P3@P4] - ** - ** The P4 register values beginning with P3 form an unpacked index -@@ -91181,7 +101064,7 @@ case OP_FinishSeek: { - ** If the P1 index entry is less than the key value then jump to P2. - ** Otherwise fall through to the next instruction. - */ --/* Opcode: IdxLE P1 P2 P3 P4 P5 -+/* Opcode: IdxLE P1 P2 P3 P4 * - ** Synopsis: key=r[P3@P4] - ** - ** The P4 register values beginning with P3 form an unpacked index -@@ -91192,10 +101075,10 @@ case OP_FinishSeek: { - ** If the P1 index entry is less than or equal to the key value then jump - ** to P2. Otherwise fall through to the next instruction. - */ --case OP_IdxLE: /* jump */ --case OP_IdxGT: /* jump */ --case OP_IdxLT: /* jump */ --case OP_IdxGE: { /* jump */ -+case OP_IdxLE: /* jump, ncycle */ -+case OP_IdxGT: /* jump, ncycle */ -+case OP_IdxLT: /* jump, ncycle */ -+case OP_IdxGE: { /* jump, ncycle */ - VdbeCursor *pC; - int res; - UnpackedRecord r; -@@ -91207,7 +101090,6 @@ case OP_IdxGE: { /* jump */ - assert( pC->eCurType==CURTYPE_BTREE ); - assert( pC->uc.pCursor!=0); - assert( pC->deferredMoveto==0 ); -- assert( pOp->p5==0 || pOp->p5==1 ); - assert( pOp->p4type==P4_INT32 ); - r.pKeyInfo = pC->pKeyInfo; - r.nField = (u16)pOp->p4.i; -@@ -91228,8 +101110,31 @@ case OP_IdxGE: { /* jump */ - } - } - #endif -- res = 0; /* Not needed. Only used to silence a warning. */ -- rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res); -+ -+ /* Inlined version of sqlite3VdbeIdxKeyCompare() */ -+ { -+ i64 nCellKey = 0; -+ BtCursor *pCur; -+ Mem m; -+ -+ assert( pC->eCurType==CURTYPE_BTREE ); -+ pCur = pC->uc.pCursor; -+ assert( sqlite3BtreeCursorIsValid(pCur) ); -+ nCellKey = sqlite3BtreePayloadSize(pCur); -+ /* nCellKey will always be between 0 and 0xffffffff because of the way -+ ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ -+ if( nCellKey<=0 || nCellKey>0x7fffffff ){ -+ rc = SQLITE_CORRUPT_BKPT; -+ goto abort_due_to_error; -+ } -+ sqlite3VdbeMemInit(&m, db, 0); -+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); -+ if( rc ) goto abort_due_to_error; -+ res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, &r, 0); -+ sqlite3VdbeMemReleaseMalloc(&m); -+ } -+ /* End of inlined sqlite3VdbeIdxKeyCompare() */ -+ - assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) ); - if( (pOp->opcode&1)==(OP_IdxLT&1) ){ - assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT ); -@@ -91239,7 +101144,7 @@ case OP_IdxGE: { /* jump */ - res++; - } - VdbeBranchTaken(res>0,2); -- if( rc ) goto abort_due_to_error; -+ assert( rc==SQLITE_OK ); - if( res>0 ) goto jump_to_p2; - break; - } -@@ -91250,7 +101155,7 @@ case OP_IdxGE: { /* jump */ - ** file is given by P1. - ** - ** The table being destroyed is in the main database file if P3==0. If --** P3==1 then the table to be clear is in the auxiliary database file -+** P3==1 then the table to be destroyed is in the auxiliary database file - ** that is used to store tables create using CREATE TEMPORARY TABLE. - ** - ** If AUTOVACUUM is enabled then it is possible that another root page -@@ -91310,28 +101215,25 @@ case OP_Destroy: { /* out2 */ - ** in the database file is given by P1. But, unlike Destroy, do not - ** remove the table or index from the database file. - ** --** The table being clear is in the main database file if P2==0. If --** P2==1 then the table to be clear is in the auxiliary database file -+** The table being cleared is in the main database file if P2==0. If -+** P2==1 then the table to be cleared is in the auxiliary database file - ** that is used to store tables create using CREATE TEMPORARY TABLE. - ** --** If the P3 value is non-zero, then the table referred to must be an --** intkey table (an SQL table, not an index). In this case the row change --** count is incremented by the number of rows in the table being cleared. --** If P3 is greater than zero, then the value stored in register P3 is --** also incremented by the number of rows in the table being cleared. -+** If the P3 value is non-zero, then the row change count is incremented -+** by the number of rows in the table being cleared. If P3 is greater -+** than zero, then the value stored in register P3 is also incremented -+** by the number of rows in the table being cleared. - ** - ** See also: Destroy - */ - case OP_Clear: { -- int nChange; -+ i64 nChange; - - sqlite3VdbeIncrWriteCounter(p, 0); - nChange = 0; - assert( p->readOnly==0 ); - assert( DbMaskTest(p->btreeMask, pOp->p2) ); -- rc = sqlite3BtreeClearTable( -- db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) -- ); -+ rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, (u32)pOp->p1, &nChange); - if( pOp->p3 ){ - p->nChange += nChange; - if( pOp->p3>0 ){ -@@ -91397,16 +101299,57 @@ case OP_CreateBtree: { /* out2 */ - break; - } - --/* Opcode: SqlExec * * * P4 * -+/* Opcode: SqlExec P1 P2 * P4 * - ** - ** Run the SQL statement or statements specified in the P4 string. -+** -+** The P1 parameter is a bitmask of options: -+** -+** 0x0001 Disable Auth and Trace callbacks while the statements -+** in P4 are running. -+** -+** 0x0002 Set db->nAnalysisLimit to P2 while the statements in -+** P4 are running. -+** - */ - case OP_SqlExec: { -+ char *zErr; -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ sqlite3_xauth xAuth; -+#endif -+ u8 mTrace; -+ int savedAnalysisLimit; -+ - sqlite3VdbeIncrWriteCounter(p, 0); - db->nSqlExec++; -- rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0); -+ zErr = 0; -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ xAuth = db->xAuth; -+#endif -+ mTrace = db->mTrace; -+ savedAnalysisLimit = db->nAnalysisLimit; -+ if( pOp->p1 & 0x0001 ){ -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ db->xAuth = 0; -+#endif -+ db->mTrace = 0; -+ } -+ if( pOp->p1 & 0x0002 ){ -+ db->nAnalysisLimit = pOp->p2; -+ } -+ rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr); - db->nSqlExec--; -- if( rc ) goto abort_due_to_error; -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ db->xAuth = xAuth; -+#endif -+ db->mTrace = mTrace; -+ db->nAnalysisLimit = savedAnalysisLimit; -+ if( zErr || rc ){ -+ sqlite3VdbeError(p, "%s", zErr); -+ sqlite3_free(zErr); -+ if( rc==SQLITE_NOMEM ) goto no_mem; -+ goto abort_due_to_error; -+ } - break; - } - -@@ -91437,19 +101380,21 @@ case OP_ParseSchema: { - - iDb = pOp->p1; - assert( iDb>=0 && iDbnDb ); -- assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); -+ assert( DbHasProperty(db, iDb, DB_SchemaLoaded) -+ || db->mallocFailed -+ || (CORRUPT_DB && (db->flags & SQLITE_NoSchemaError)!=0) ); - - #ifndef SQLITE_OMIT_ALTERTABLE - if( pOp->p4.z==0 ){ - sqlite3SchemaClear(db->aDb[iDb].pSchema); - db->mDbFlags &= ~DBFLAG_SchemaKnownOk; -- rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable); -+ rc = sqlite3InitOne(db, iDb, &p->zErrMsg, pOp->p5); - db->mDbFlags |= DBFLAG_SchemaChange; - p->expired = 0; - }else - #endif - { -- zSchema = DFLT_SCHEMA_TABLE; -+ zSchema = LEGACY_SCHEMA_TABLE; - initData.db = db; - initData.iDb = iDb; - initData.pzErrMsg = &p->zErrMsg; -@@ -91550,11 +101495,11 @@ case OP_DropTrigger: { - /* Opcode: IntegrityCk P1 P2 P3 P4 P5 - ** - ** Do an analysis of the currently open database. Store in --** register P1 the text of an error message describing any problems. --** If no problems are found, store a NULL in register P1. -+** register (P1+1) the text of an error message describing any problems. -+** If no problems are found, store a NULL in register (P1+1). - ** --** The register P3 contains one less than the maximum number of allowed errors. --** At most reg(P3) errors will be reported. -+** The register (P1) contains one less than the maximum number of allowed -+** errors. At most reg(P1) errors will be reported. - ** In other words, the analysis stops as soon as reg(P1) errors are - ** seen. Reg(P1) is updated with the number of errors remaining. - ** -@@ -91574,24 +101519,27 @@ case OP_IntegrityCk: { - Mem *pnErr; /* Register keeping track of errors remaining */ - - assert( p->bIsReader ); -+ assert( pOp->p4type==P4_INTARRAY ); - nRoot = pOp->p2; - aRoot = pOp->p4.ai; - assert( nRoot>0 ); -+ assert( aRoot!=0 ); - assert( aRoot[0]==(Pgno)nRoot ); -- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); -- pnErr = &aMem[pOp->p3]; -+ assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) ); -+ pnErr = &aMem[pOp->p1]; - assert( (pnErr->flags & MEM_Int)!=0 ); - assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); -- pIn1 = &aMem[pOp->p1]; -+ pIn1 = &aMem[pOp->p1+1]; - assert( pOp->p5nDb ); - assert( DbMaskTest(p->btreeMask, pOp->p5) ); -- z = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, -- (int)pnErr->u.i+1, &nErr); -+ rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], -+ &aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z); - sqlite3VdbeMemSetNull(pIn1); - if( nErr==0 ){ - assert( z==0 ); -- }else if( z==0 ){ -- goto no_mem; -+ }else if( rc ){ -+ sqlite3_free(z); -+ goto abort_due_to_error; - }else{ - pnErr->u.i -= nErr-1; - sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free); -@@ -91712,7 +101660,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */ - ** P1 contains the address of the memory cell that contains the first memory - ** cell in an array of values used as arguments to the sub-program. P2 - ** contains the address to jump to if the sub-program throws an IGNORE --** exception using the RAISE() function. Register P3 contains the address -+** exception using the RAISE() function. P2 might be zero, if there is -+** no possibility that an IGNORE exception will be raised. -+** Register P3 contains the address - ** of a memory cell in this (the parent) VM that is used to allocate the - ** memory required by the sub-vdbe at runtime. - ** -@@ -91720,9 +101670,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */ - ** - ** If P5 is non-zero, then recursive program invocation is enabled. - */ --case OP_Program: { /* jump */ -+case OP_Program: { /* jump0 */ - int nMem; /* Number of memory registers for sub-program */ -- int nByte; /* Bytes of runtime space required for sub-program */ -+ i64 nByte; /* Bytes of runtime space required for sub-program */ - Mem *pRt; /* Register to allocate runtime space */ - Mem *pMem; /* Used to iterate through memory cells */ - Mem *pEnd; /* Last memory cell in new array */ -@@ -91773,7 +101723,7 @@ case OP_Program: { /* jump */ - nByte = ROUND8(sizeof(VdbeFrame)) - + nMem * sizeof(Mem) - + pProgram->nCsr * sizeof(VdbeCursor*) -- + (pProgram->nOp + 7)/8; -+ + (7 + (i64)pProgram->nOp)/8; - pFrame = sqlite3DbMallocZero(db, nByte); - if( !pFrame ){ - goto no_mem; -@@ -91781,7 +101731,7 @@ case OP_Program: { /* jump */ - sqlite3VdbeMemRelease(pRt); - pRt->flags = MEM_Blob|MEM_Dyn; - pRt->z = (char*)pFrame; -- pRt->n = nByte; -+ pRt->n = (int)nByte; - pRt->xDel = sqlite3VdbeFrameMemDel; - - pFrame->v = p; -@@ -91795,9 +101745,6 @@ case OP_Program: { /* jump */ - pFrame->aOp = p->aOp; - pFrame->nOp = p->nOp; - pFrame->token = pProgram->token; --#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- pFrame->anExec = p->anExec; --#endif - #ifdef SQLITE_DEBUG - pFrame->iFrameMagic = SQLITE_FRAME_MAGIC; - #endif -@@ -91834,9 +101781,6 @@ case OP_Program: { /* jump */ - memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8); - p->aOp = aOp = pProgram->aOp; - p->nOp = pProgram->nOp; --#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -- p->anExec = 0; --#endif - #ifdef SQLITE_DEBUG - /* Verify that second and subsequent executions of the same trigger do not - ** try to reuse register values from the first use. */ -@@ -91886,12 +101830,14 @@ case OP_Param: { /* out2 */ - ** statement counter is incremented (immediate foreign key constraints). - */ - case OP_FkCounter: { -- if( db->flags & SQLITE_DeferFKs ){ -- db->nDeferredImmCons += pOp->p2; -- }else if( pOp->p1 ){ -+ if( pOp->p1 ){ - db->nDeferredCons += pOp->p2; - }else{ -- p->nFkConstraint += pOp->p2; -+ if( db->flags & SQLITE_DeferFKs ){ -+ db->nDeferredImmCons += pOp->p2; -+ }else{ -+ p->nFkConstraint += pOp->p2; -+ } - } - break; - } -@@ -91976,7 +101922,7 @@ case OP_IfPos: { /* jump, in1 */ - ** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) - ** - ** This opcode performs a commonly used computation associated with --** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3] -+** LIMIT and OFFSET processing. r[P1] holds the limit counter. r[P3] - ** holds the offset counter. The opcode computes the combined value - ** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2] - ** value computed is the total number of rows that will need to be -@@ -92091,23 +102037,35 @@ case OP_AggInverse: - case OP_AggStep: { - int n; - sqlite3_context *pCtx; -+ u64 nAlloc; - - assert( pOp->p4type==P4_FUNCDEF ); - n = pOp->p5; - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); - assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); -- pCtx = sqlite3DbMallocRawNN(db, n*sizeof(sqlite3_value*) + -- (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlite3_value*))); -+ -+ /* Allocate space for (a) the context object and (n-1) extra pointers -+ ** to append to the sqlite3_context.argv[1] array, and (b) a memory -+ ** cell in which to store the accumulation. Be careful that the memory -+ ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. -+ ** -+ ** Note: We could avoid this by using a regular memory cell from aMem[] for -+ ** the accumulator, instead of allocating one here. */ -+ nAlloc = ROUND8P( SZ_CONTEXT(n) ); -+ pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); - if( pCtx==0 ) goto no_mem; -- pCtx->pMem = 0; -- pCtx->pOut = (Mem*)&(pCtx->argv[n]); -+ pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); -+ assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); -+ - sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null); -+ pCtx->pMem = 0; - pCtx->pFunc = pOp->p4.pFunc; - pCtx->iOp = (int)(pOp - aOp); - pCtx->pVdbe = p; - pCtx->skipFlag = 0; - pCtx->isError = 0; -+ pCtx->enc = encoding; - pCtx->argc = n; - pOp->p4type = P4_FUNCCTX; - pOp->p4.pCtx = pCtx; -@@ -92142,7 +102100,7 @@ case OP_AggStep1: { - /* If this function is inside of a trigger, the register array in aMem[] - ** might change from one evaluation to the next. The next block of code - ** checks to see if the register array has changed, and if so it -- ** reinitializes the relavant parts of the sqlite3_context object */ -+ ** reinitializes the relevant parts of the sqlite3_context object */ - if( pCtx->pMem != pMem ){ - pCtx->pMem = pMem; - for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; -@@ -92237,9 +102195,7 @@ case OP_AggFinal: { - } - sqlite3VdbeChangeEncoding(pMem, encoding); - UPDATE_MAX_BLOBSIZE(pMem); -- if( sqlite3VdbeMemTooBig(pMem) ){ -- goto too_big; -- } -+ REGISTER_TRACE((int)(pMem-aMem), pMem); - break; - } - -@@ -92319,6 +102275,7 @@ case OP_JournalMode: { /* out2 */ - pPager = sqlite3BtreePager(pBt); - eOld = sqlite3PagerGetJournalMode(pPager); - if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; -+ assert( sqlite3BtreeHoldsMutex(pBt) ); - if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; - - #ifndef SQLITE_OMIT_WAL -@@ -92365,7 +102322,7 @@ case OP_JournalMode: { /* out2 */ - /* Open a transaction on the database file. Regardless of the journal - ** mode, this transaction always uses a rollback journal. - */ -- assert( sqlite3BtreeIsInTrans(pBt)==0 ); -+ assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); - } -@@ -92594,7 +102551,7 @@ case OP_VDestroy: { - ** P1 is a cursor number. This opcode opens a cursor to the virtual - ** table and stores that cursor in P1. - */ --case OP_VOpen: { -+case OP_VOpen: { /* ncycle */ - VdbeCursor *pCur; - sqlite3_vtab_cursor *pVCur; - sqlite3_vtab *pVtab; -@@ -92617,7 +102574,7 @@ case OP_VOpen: { - pVCur->pVtab = pVtab; - - /* Initialize vdbe cursor object */ -- pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB); -+ pCur = allocateCursor(p, pOp->p1, 0, CURTYPE_VTAB); - if( pCur ){ - pCur->uc.pVCur = pVCur; - pVtab->nRef++; -@@ -92630,6 +102587,80 @@ case OP_VOpen: { - } - #endif /* SQLITE_OMIT_VIRTUALTABLE */ - -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+/* Opcode: VCheck P1 P2 P3 P4 * -+** -+** P4 is a pointer to a Table object that is a virtual table in schema P1 -+** that supports the xIntegrity() method. This opcode runs the xIntegrity() -+** method for that virtual table, using P3 as the integer argument. If -+** an error is reported back, the table name is prepended to the error -+** message and that message is stored in P2. If no errors are seen, -+** register P2 is set to NULL. -+*/ -+case OP_VCheck: { /* out2 */ -+ Table *pTab; -+ sqlite3_vtab *pVtab; -+ const sqlite3_module *pModule; -+ char *zErr = 0; -+ -+ pOut = &aMem[pOp->p2]; -+ sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */ -+ assert( pOp->p4type==P4_TABLEREF ); -+ pTab = pOp->p4.pTab; -+ assert( pTab!=0 ); -+ assert( pTab->nTabRef>0 ); -+ assert( IsVirtual(pTab) ); -+ if( pTab->u.vtab.p==0 ) break; -+ pVtab = pTab->u.vtab.p->pVtab; -+ assert( pVtab!=0 ); -+ pModule = pVtab->pModule; -+ assert( pModule!=0 ); -+ assert( pModule->iVersion>=4 ); -+ assert( pModule->xIntegrity!=0 ); -+ sqlite3VtabLock(pTab->u.vtab.p); -+ assert( pOp->p1>=0 && pOp->p1nDb ); -+ rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName, -+ pOp->p3, &zErr); -+ sqlite3VtabUnlock(pTab->u.vtab.p); -+ if( rc ){ -+ sqlite3_free(zErr); -+ goto abort_due_to_error; -+ } -+ if( zErr ){ -+ sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free); -+ } -+ break; -+} -+#endif /* SQLITE_OMIT_VIRTUALTABLE */ -+ -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+/* Opcode: VInitIn P1 P2 P3 * * -+** Synopsis: r[P2]=ValueList(P1,P3) -+** -+** Set register P2 to be a pointer to a ValueList object for cursor P1 -+** with cache register P3 and output register P3+1. This ValueList object -+** can be used as the first argument to sqlite3_vtab_in_first() and -+** sqlite3_vtab_in_next() to extract all of the values stored in the P1 -+** cursor. Register P3 is used to hold the values returned by -+** sqlite3_vtab_in_first() and sqlite3_vtab_in_next(). -+*/ -+case OP_VInitIn: { /* out2, ncycle */ -+ VdbeCursor *pC; /* The cursor containing the RHS values */ -+ ValueList *pRhs; /* New ValueList object to put in reg[P2] */ -+ -+ pC = p->apCsr[pOp->p1]; -+ pRhs = sqlite3_malloc64( sizeof(*pRhs) ); -+ if( pRhs==0 ) goto no_mem; -+ pRhs->pCsr = pC->uc.pCursor; -+ pRhs->pOut = &aMem[pOp->p3]; -+ pOut = out2Prerelease(p, pOp); -+ pOut->flags = MEM_Null; -+ sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3VdbeValueListFree); -+ break; -+} -+#endif /* SQLITE_OMIT_VIRTUALTABLE */ -+ -+ - #ifndef SQLITE_OMIT_VIRTUALTABLE - /* Opcode: VFilter P1 P2 P3 P4 * - ** Synopsis: iplan=r[P3] zplan='P4' -@@ -92650,7 +102681,7 @@ case OP_VOpen: { - ** - ** A jump is made to P2 if the result set after filtering would be empty. - */ --case OP_VFilter: { /* jump */ -+case OP_VFilter: { /* jump, ncycle */ - int nArg; - int iQuery; - const sqlite3_module *pModule; -@@ -92668,6 +102699,7 @@ case OP_VFilter: { /* jump */ - pCur = p->apCsr[pOp->p1]; - assert( memIsValid(pQuery) ); - REGISTER_TRACE(pOp->p3, pQuery); -+ assert( pCur!=0 ); - assert( pCur->eCurType==CURTYPE_VTAB ); - pVCur = pCur->uc.pVCur; - pVtab = pVCur->pVtab; -@@ -92679,8 +102711,8 @@ case OP_VFilter: { /* jump */ - iQuery = (int)pQuery->u.i; - - /* Invoke the xFilter method */ -- res = 0; - apArg = p->apArg; -+ assert( nArg<=p->napArg ); - for(i = 0; iapCsr[pOp->p1]; -- assert( pCur->eCurType==CURTYPE_VTAB ); -+ assert( pCur!=0 ); - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pDest = &aMem[pOp->p3]; - memAboutToChange(p, pDest); -@@ -92725,11 +102758,16 @@ case OP_VColumn: { - sqlite3VdbeMemSetNull(pDest); - break; - } -+ assert( pCur->eCurType==CURTYPE_VTAB ); - pVtab = pCur->uc.pVCur->pVtab; - pModule = pVtab->pModule; - assert( pModule->xColumn ); - memset(&sContext, 0, sizeof(sContext)); - sContext.pOut = pDest; -+ sContext.enc = encoding; -+ nullFunc.pUserData = 0; -+ nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE; -+ sContext.pFunc = &nullFunc; - assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); - if( pOp->p5 & OPFLAG_NOCHNG ){ - sqlite3VdbeMemSetNull(pDest); -@@ -92748,9 +102786,6 @@ case OP_VColumn: { - REGISTER_TRACE(pOp->p3, pDest); - UPDATE_MAX_BLOBSIZE(pDest); - -- if( sqlite3VdbeMemTooBig(pDest) ){ -- goto too_big; -- } - if( rc ) goto abort_due_to_error; - break; - } -@@ -92763,14 +102798,14 @@ case OP_VColumn: { - ** jump to instruction P2. Or, if the virtual table has reached - ** the end of its result set, then fall through to the next instruction. - */ --case OP_VNext: { /* jump */ -+case OP_VNext: { /* jump, ncycle */ - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - int res; - VdbeCursor *pCur; - -- res = 0; - pCur = p->apCsr[pOp->p1]; -+ assert( pCur!=0 ); - assert( pCur->eCurType==CURTYPE_VTAB ); - if( pCur->nullRow ){ - break; -@@ -92866,7 +102901,7 @@ case OP_VUpdate: { - const sqlite3_module *pModule; - int nArg; - int i; -- sqlite_int64 rowid; -+ sqlite_int64 rowid = 0; - Mem **apArg; - Mem *pX; - -@@ -92888,6 +102923,7 @@ case OP_VUpdate: { - u8 vtabOnConflict = db->vtabOnConflict; - apArg = p->apArg; - pX = &aMem[pOp->p3]; -+ assert( nArg<=p->napArg ); - for(i=0; ip3]; - if( pCtx->pOut != pOut ){ - pCtx->pVdbe = p; - pCtx->pOut = pOut; -+ pCtx->enc = encoding; - for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i]; - } - assert( pCtx->pVdbe==p ); -@@ -93043,17 +103080,134 @@ case OP_Function: { /* group */ - if( rc ) goto abort_due_to_error; - } - -- /* Copy the result of the function into register P3 */ -- if( pOut->flags & (MEM_Str|MEM_Blob) ){ -- sqlite3VdbeChangeEncoding(pOut, encoding); -- if( sqlite3VdbeMemTooBig(pOut) ) goto too_big; -- } -+ assert( (pOut->flags&MEM_Str)==0 -+ || pOut->enc==encoding -+ || db->mallocFailed ); -+ assert( !sqlite3VdbeMemTooBig(pOut) ); - - REGISTER_TRACE(pOp->p3, pOut); - UPDATE_MAX_BLOBSIZE(pOut); - break; - } - -+/* Opcode: ClrSubtype P1 * * * * -+** Synopsis: r[P1].subtype = 0 -+** -+** Clear the subtype from register P1. -+*/ -+case OP_ClrSubtype: { /* in1 */ -+ pIn1 = &aMem[pOp->p1]; -+ pIn1->flags &= ~MEM_Subtype; -+ break; -+} -+ -+/* Opcode: GetSubtype P1 P2 * * * -+** Synopsis: r[P2] = r[P1].subtype -+** -+** Extract the subtype value from register P1 and write that subtype -+** into register P2. If P1 has no subtype, then P1 gets a NULL. -+*/ -+case OP_GetSubtype: { /* in1 out2 */ -+ pIn1 = &aMem[pOp->p1]; -+ pOut = &aMem[pOp->p2]; -+ if( pIn1->flags & MEM_Subtype ){ -+ sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype); -+ }else{ -+ sqlite3VdbeMemSetNull(pOut); -+ } -+ break; -+} -+ -+/* Opcode: SetSubtype P1 P2 * * * -+** Synopsis: r[P2].subtype = r[P1] -+** -+** Set the subtype value of register P2 to the integer from register P1. -+** If P1 is NULL, clear the subtype from p2. -+*/ -+case OP_SetSubtype: { /* in1 out2 */ -+ pIn1 = &aMem[pOp->p1]; -+ pOut = &aMem[pOp->p2]; -+ if( pIn1->flags & MEM_Null ){ -+ pOut->flags &= ~MEM_Subtype; -+ }else{ -+ assert( pIn1->flags & MEM_Int ); -+ pOut->flags |= MEM_Subtype; -+ pOut->eSubtype = (u8)(pIn1->u.i & 0xff); -+ } -+ break; -+} -+ -+/* Opcode: FilterAdd P1 * P3 P4 * -+** Synopsis: filter(P1) += key(P3@P4) -+** -+** Compute a hash on the P4 registers starting with r[P3] and -+** add that hash to the bloom filter contained in r[P1]. -+*/ -+case OP_FilterAdd: { -+ u64 h; -+ -+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); -+ pIn1 = &aMem[pOp->p1]; -+ assert( pIn1->flags & MEM_Blob ); -+ assert( pIn1->n>0 ); -+ h = filterHash(aMem, pOp); -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ int ii; -+ for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ -+ registerTrace(ii, &aMem[ii]); -+ } -+ printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); -+ } -+#endif -+ h %= (pIn1->n*8); -+ pIn1->z[h/8] |= 1<<(h&7); -+ break; -+} -+ -+/* Opcode: Filter P1 P2 P3 P4 * -+** Synopsis: if key(P3@P4) not in filter(P1) goto P2 -+** -+** Compute a hash on the key contained in the P4 registers starting -+** with r[P3]. Check to see if that hash is found in the -+** bloom filter hosted by register P1. If it is not present then -+** maybe jump to P2. Otherwise fall through. -+** -+** False negatives are harmless. It is always safe to fall through, -+** even if the value is in the bloom filter. A false negative causes -+** more CPU cycles to be used, but it should still yield the correct -+** answer. However, an incorrect answer may well arise from a -+** false positive - if the jump is taken when it should fall through. -+*/ -+case OP_Filter: { /* jump */ -+ u64 h; -+ -+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); -+ pIn1 = &aMem[pOp->p1]; -+ assert( (pIn1->flags & MEM_Blob)!=0 ); -+ assert( pIn1->n >= 1 ); -+ h = filterHash(aMem, pOp); -+#ifdef SQLITE_DEBUG -+ if( db->flags&SQLITE_VdbeTrace ){ -+ int ii; -+ for(ii=pOp->p3; iip3+pOp->p4.i; ii++){ -+ registerTrace(ii, &aMem[ii]); -+ } -+ printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n)); -+ } -+#endif -+ h %= (pIn1->n*8); -+ if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){ -+ VdbeBranchTaken(1, 2); -+ p->aCounter[SQLITE_STMTSTATUS_FILTER_HIT]++; -+ goto jump_to_p2; -+ }else{ -+ p->aCounter[SQLITE_STMTSTATUS_FILTER_MISS]++; -+ VdbeBranchTaken(0, 2); -+ } -+ break; -+} -+ - /* Opcode: Trace P1 P2 * P4 * - ** - ** Write P4 on the statement trace output if statement tracing is -@@ -93080,7 +103234,7 @@ case OP_Function: { /* group */ - ** error is encountered. - */ - case OP_Trace: --case OP_Init: { /* jump */ -+case OP_Init: { /* jump0 */ - int i; - #ifndef SQLITE_OMIT_TRACE - char *zTrace; -@@ -93102,7 +103256,7 @@ case OP_Init: { /* jump */ - - #ifndef SQLITE_OMIT_TRACE - if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 -- && !p->doingRerun -+ && p->minWriteFileFormat!=254 /* tag-20220401a */ - && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 - ){ - #ifndef SQLITE_OMIT_DEPRECATED -@@ -93241,14 +103395,29 @@ case OP_ReleaseReg: { - - /* Opcode: Noop * * * * * - ** --** Do nothing. This instruction is often useful as a jump --** destination. -+** Do nothing. Continue downward to the next opcode. - */ --/* --** The magic Explain opcode are only inserted when explain==2 (which --** is to say when the EXPLAIN QUERY PLAN syntax is used.) --** This opcode records information from the optimizer. It is the --** the same as a no-op. This opcodesnever appears in a real VM program. -+/* Opcode: Explain P1 P2 P3 P4 * -+** -+** This is the same as OP_Noop during normal query execution. The -+** purpose of this opcode is to hold information about the query -+** plan for the purpose of EXPLAIN QUERY PLAN output. -+** -+** The P4 value is human-readable text that describes the query plan -+** element. Something like "SCAN t1" or "SEARCH t2 USING INDEX t2x1". -+** -+** The P1 value is the ID of the current element and P2 is the parent -+** element for the case of nested query plan elements. If P2 is zero -+** then this element is a top-level element. -+** -+** For loop elements, P3 is the estimated code of each invocation of this -+** element. -+** -+** As with all opcodes, the meanings of the parameters for OP_Explain -+** are subject to change from one release to the next. Applications -+** should not attempt to interpret or use any of the information -+** contained in the OP_Explain opcode. The information provided by this -+** opcode is intended for testing and debugging use only. - */ - default: { /* This is really OP_Noop, OP_Explain */ - assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); -@@ -93264,11 +103433,13 @@ default: { /* This is really OP_Noop, OP_Explain */ - *****************************************************************************/ - } - --#ifdef VDBE_PROFILE -- { -- u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); -- if( endTime>start ) pOrigOp->cycles += endTime - start; -- pOrigOp->cnt++; -+#if defined(VDBE_PROFILE) -+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); -+ pnCycle = 0; -+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) -+ if( pnCycle ){ -+ *pnCycle += sqlite3Hwtime(); -+ pnCycle = 0; - } - #endif - -@@ -93292,7 +103463,7 @@ default: { /* This is really OP_Noop, OP_Explain */ - } - if( opProperty==0xff ){ - /* Never happens. This code exists to avoid a harmless linkage -- ** warning aboud sqlite3VdbeRegisterDump() being defined but not -+ ** warning about sqlite3VdbeRegisterDump() being defined but not - ** used. */ - sqlite3VdbeRegisterDump(p); - } -@@ -93305,18 +103476,37 @@ default: { /* This is really OP_Noop, OP_Explain */ - ** an error of some kind. - */ - abort_due_to_error: -- if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT; -+ if( db->mallocFailed ){ -+ rc = SQLITE_NOMEM_BKPT; -+ }else if( rc==SQLITE_IOERR_CORRUPTFS ){ -+ rc = SQLITE_CORRUPT_BKPT; -+ } - assert( rc ); -+#ifdef SQLITE_DEBUG -+ if( db->flags & SQLITE_VdbeTrace ){ -+ const char *zTrace = p->zSql; -+ if( zTrace==0 ){ -+ if( aOp[0].opcode==OP_Trace ){ -+ zTrace = aOp[0].p4.z; -+ } -+ if( zTrace==0 ) zTrace = "???"; -+ } -+ printf("ABORT-due-to-error (rc=%d): %s\n", rc, zTrace); -+ } -+#endif - if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ - sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); - } - p->rc = rc; - sqlite3SystemError(db, rc); - testcase( sqlite3GlobalConfig.xLog!=0 ); -- sqlite3_log(rc, "statement aborts at %d: [%s] %s", -- (int)(pOp - aOp), p->zSql, p->zErrMsg); -- sqlite3VdbeHalt(p); -+ sqlite3_log(rc, "statement aborts at %d: %s; [%s]", -+ (int)(pOp - aOp), p->zErrMsg, p->zSql); -+ if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p); - if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db); -+ if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){ -+ db->flags |= SQLITE_CorruptRdOnly; -+ } - rc = SQLITE_ERROR; - if( resetSchemaOnFault>0 ){ - sqlite3ResetOneSchema(db, resetSchemaOnFault-1); -@@ -93326,6 +103516,18 @@ abort_due_to_error: - ** release the mutexes on btrees that were acquired at the - ** top. */ - vdbe_return: -+#if defined(VDBE_PROFILE) -+ if( pnCycle ){ -+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); -+ pnCycle = 0; -+ } -+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) -+ if( pnCycle ){ -+ *pnCycle += sqlite3Hwtime(); -+ pnCycle = 0; -+ } -+#endif -+ - #ifndef SQLITE_OMIT_PROGRESS_CALLBACK - while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ - nProgressLimit += db->nProgressOps; -@@ -93337,7 +103539,9 @@ vdbe_return: - } - #endif - p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; -- sqlite3VdbeLeave(p); -+ if( DbMaskNonZero(p->lockMask) ){ -+ sqlite3VdbeLeave(p); -+ } - assert( rc!=SQLITE_OK || nExtraDelete==0 - || sqlite3_strlike("DELETE%",p->zSql,0)!=0 - ); -@@ -93432,8 +103636,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ - /* Set the value of register r[1] in the SQL statement to integer iRow. - ** This is done directly as a performance optimization - */ -- v->aMem[1].flags = MEM_Int; -- v->aMem[1].u.i = iRow; -+ sqlite3VdbeMemSetInt64(&v->aMem[1], iRow); - - /* If the statement has been run before (and is paused at the OP_ResultRow) - ** then back it up to the point where it does the OP_NotExists. This could -@@ -93448,7 +103651,10 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ - } - if( rc==SQLITE_ROW ){ - VdbeCursor *pC = v->apCsr[0]; -- u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; -+ u32 type; -+ assert( pC!=0 ); -+ assert( pC->eCurType==CURTYPE_BTREE ); -+ type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0; - testcase( pC->nHdrParsed==p->iCol ); - testcase( pC->nHdrParsed==p->iCol+1 ); - if( type<12 ){ -@@ -93504,6 +103710,7 @@ SQLITE_API int sqlite3_blob_open( - char *zErr = 0; - Table *pTab; - Incrblob *pBlob = 0; -+ int iDb; - Parse sParse; - - #ifdef SQLITE_ENABLE_API_ARMOR -@@ -93513,7 +103720,7 @@ SQLITE_API int sqlite3_blob_open( - #endif - *ppBlob = 0; - #ifdef SQLITE_ENABLE_API_ARMOR -- if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ -+ if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){ - return SQLITE_MISUSE_BKPT; - } - #endif -@@ -93522,10 +103729,9 @@ SQLITE_API int sqlite3_blob_open( - sqlite3_mutex_enter(db->mutex); - - pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); -- do { -- memset(&sParse, 0, sizeof(Parse)); -+ while(1){ -+ sqlite3ParseObjectInit(&sParse,db); - if( !pBlob ) goto blob_open_out; -- sParse.db = db; - sqlite3DbFree(db, zErr); - zErr = 0; - -@@ -93539,13 +103745,21 @@ SQLITE_API int sqlite3_blob_open( - pTab = 0; - sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); - } -+ if( pTab && (pTab->tabFlags&TF_HasGenerated)!=0 ){ -+ pTab = 0; -+ sqlite3ErrorMsg(&sParse, "cannot open table with generated columns: %s", -+ zTable); -+ } - #ifndef SQLITE_OMIT_VIEW -- if( pTab && pTab->pSelect ){ -+ if( pTab && IsView(pTab) ){ - pTab = 0; - sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); - } - #endif -- if( !pTab ){ -+ if( pTab==0 -+ || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 && -+ sqlite3OpenTempDatabase(&sParse)) -+ ){ - if( sParse.zErrMsg ){ - sqlite3DbFree(db, zErr); - zErr = sParse.zErrMsg; -@@ -93556,15 +103770,11 @@ SQLITE_API int sqlite3_blob_open( - goto blob_open_out; - } - pBlob->pTab = pTab; -- pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; -+ pBlob->zDb = db->aDb[iDb].zDbSName; - - /* Now search pTab for the exact column. */ -- for(iCol=0; iColnCol; iCol++) { -- if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ -- break; -- } -- } -- if( iCol==pTab->nCol ){ -+ iCol = sqlite3ColumnIndex(pTab, zColumn); -+ if( iCol<0 ){ - sqlite3DbFree(db, zErr); - zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); - rc = SQLITE_ERROR; -@@ -93585,7 +103795,8 @@ SQLITE_API int sqlite3_blob_open( - ** key columns must be indexed. The check below will pick up this - ** case. */ - FKey *pFKey; -- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ -+ assert( IsOrdinaryTable(pTab) ); -+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ - int j; - for(j=0; jnCol; j++){ - if( pFKey->aCol[j].iFrom==iCol ){ -@@ -93643,7 +103854,6 @@ SQLITE_API int sqlite3_blob_open( - {OP_Halt, 0, 0, 0}, /* 5 */ - }; - Vdbe *v = (Vdbe *)pBlob->pStmt; -- int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - VdbeOp *aOp; - - sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, -@@ -93701,7 +103911,9 @@ SQLITE_API int sqlite3_blob_open( - goto blob_open_out; - } - rc = blobSeekToRow(pBlob, iRow, &zErr); -- } while( (++nAttempt)=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break; -+ sqlite3ParseObjectReset(&sParse); -+ } - - blob_open_out: - if( rc==SQLITE_OK && db->mallocFailed==0 ){ -@@ -93710,9 +103922,9 @@ blob_open_out: - if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); - sqlite3DbFree(db, pBlob); - } -- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); -+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); - sqlite3DbFree(db, zErr); -- sqlite3ParserReset(&sParse); -+ sqlite3ParseObjectReset(&sParse); - rc = sqlite3ApiExit(db, rc); - sqlite3_mutex_leave(db->mutex); - return rc; -@@ -93792,8 +104004,10 @@ static int blobReadWrite( - */ - sqlite3_int64 iKey; - iKey = sqlite3BtreeIntegerKey(p->pCsr); -+ assert( v->apCsr[0]!=0 ); -+ assert( v->apCsr[0]->eCurType==CURTYPE_BTREE ); - sqlite3VdbePreUpdateHook( -- v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1 -+ v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol - ); - } - #endif -@@ -93864,9 +104078,10 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ - rc = SQLITE_ABORT; - }else{ - char *zErr; -+ ((Vdbe*)p->pStmt)->rc = SQLITE_OK; - rc = blobSeekToRow(p, iRow, &zErr); - if( rc!=SQLITE_OK ){ -- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); -+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); - sqlite3DbFree(db, zErr); - } - assert( rc!=SQLITE_SCHEMA ); -@@ -93969,7 +104184,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ - ** The threshold for the amount of main memory to use before flushing - ** records to a PMA is roughly the same as the limit configured for the - ** page-cache of the main database. Specifically, the threshold is set to --** the value returned by "PRAGMA main.page_size" multipled by -+** the value returned by "PRAGMA main.page_size" multiplied by - ** that returned by "PRAGMA main.cache_size", in bytes. - ** - ** If the sorter is running in single-threaded mode, then all PMAs generated -@@ -93992,7 +104207,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ - ** - ** If there are fewer than SORTER_MAX_MERGE_COUNT PMAs in total and the - ** sorter is running in single-threaded mode, then these PMAs are merged --** incrementally as keys are retreived from the sorter by the VDBE. The -+** incrementally as keys are retrieved from the sorter by the VDBE. The - ** MergeEngine object, described in further detail below, performs this - ** merge. - ** -@@ -94070,7 +104285,7 @@ struct SorterFile { - struct SorterList { - SorterRecord *pList; /* Linked list of records */ - u8 *aMemory; /* If non-NULL, bulk memory to hold pList */ -- int szPMA; /* Size of pList as PMA in bytes */ -+ i64 szPMA; /* Size of pList as PMA in bytes */ - }; - - /* -@@ -94155,7 +104370,7 @@ struct MergeEngine { - ** - ** Essentially, this structure contains all those fields of the VdbeSorter - ** structure for which each thread requires a separate instance. For example, --** each thread requries its own UnpackedRecord object to unpack records in -+** each thread requeries its own UnpackedRecord object to unpack records in - ** as part of comparison operations. - ** - ** Before a background thread is launched, variable bDone is set to 0. Then, -@@ -94179,10 +104394,10 @@ typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int); - struct SortSubtask { - SQLiteThread *pThread; /* Background thread, if any */ - int bDone; /* Set if thread is finished but not joined */ -+ int nPMA; /* Number of PMAs currently in file */ - VdbeSorter *pSorter; /* Sorter that owns this sub-task */ - UnpackedRecord *pUnpacked; /* Space to unpack a record */ - SorterList list; /* List for thread to write to a PMA */ -- int nPMA; /* Number of PMAs currently in file */ - SorterCompare xCompare; /* Compare function to use */ - SorterFile file; /* Temp file for level-0 PMAs */ - SorterFile file2; /* Space for other PMAs */ -@@ -94216,9 +104431,12 @@ struct VdbeSorter { - u8 iPrev; /* Previous thread used to flush PMA */ - u8 nTask; /* Size of aTask[] array */ - u8 typeMask; -- SortSubtask aTask[1]; /* One or more subtasks */ -+ SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */ - }; - -+/* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */ -+#define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask)) -+ - #define SORTER_TYPE_INTEGER 0x01 - #define SORTER_TYPE_TEXT 0x02 - -@@ -94227,7 +104445,7 @@ struct VdbeSorter { - ** PMA, in sorted order. The next key to be read is cached in nKey/aKey. - ** aKey might point into aMap or into aBuffer. If neither of those locations - ** contain a contiguous representation of the key, then aAlloc is allocated --** and the key is copied into aAlloc and aKey is made to poitn to aAlloc. -+** and the key is copied into aAlloc and aKey is made to point to aAlloc. - ** - ** pFd==0 at EOF. - */ -@@ -94440,13 +104658,14 @@ static int vdbePmaReadBlob( - while( nRem>0 ){ - int rc; /* vdbePmaReadBlob() return code */ - int nCopy; /* Number of bytes to copy */ -- u8 *aNext; /* Pointer to buffer to copy data from */ -+ u8 *aNext = 0; /* Pointer to buffer to copy data from */ - - nCopy = nRem; - if( nRem>p->nBuffer ) nCopy = p->nBuffer; - rc = vdbePmaReadBlob(p, nCopy, &aNext); - if( rc!=SQLITE_OK ) return rc; - assert( aNext!=p->aAlloc ); -+ assert( aNext!=0 ); - memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy); - nRem -= nCopy; - } -@@ -94819,7 +105038,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( - VdbeSorter *pSorter; /* The new sorter */ - KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */ - int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */ -- int sz; /* Size of pSorter in bytes */ -+ i64 sz; /* Size of pSorter in bytes */ - int rc = SQLITE_OK; - #if SQLITE_MAX_WORKER_THREADS==0 - # define nWorker 0 -@@ -94844,23 +105063,29 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( - } - #endif - -- assert( pCsr->pKeyInfo && pCsr->pBtx==0 ); -+ assert( pCsr->pKeyInfo ); -+ assert( !pCsr->isEphemeral ); - assert( pCsr->eCurType==CURTYPE_SORTER ); -- szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); -- sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); -+ assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) -+ < 0x7fffffff ); -+ szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField); -+ sz = SZ_VDBESORTER(nWorker+1); - - pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); - pCsr->uc.pSorter = pSorter; - if( pSorter==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ -+ Btree *pBt = db->aDb[0].pBt; - pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); - memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); - pKeyInfo->db = 0; - if( nField && nWorker==0 ){ - pKeyInfo->nKeyField = nField; - } -- pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt); -+ sqlite3BtreeEnter(pBt); -+ pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt); -+ sqlite3BtreeLeave(pBt); - pSorter->nTask = nWorker + 1; - pSorter->iPrev = (u8)(nWorker - 1); - pSorter->bUseThreads = (pSorter->nTask>1); -@@ -94954,8 +105179,9 @@ static void vdbeSorterWorkDebug(SortSubtask *pTask, const char *zEvent){ - fprintf(stderr, "%lld:%d %s\n", t, iTask, zEvent); - } - static void vdbeSorterRewindDebug(const char *zEvent){ -- i64 t; -- sqlite3OsCurrentTimeInt64(sqlite3_vfs_find(0), &t); -+ i64 t = 0; -+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0); -+ if( ALWAYS(pVfs) ) sqlite3OsCurrentTimeInt64(pVfs, &t); - fprintf(stderr, "%lld:X %s\n", t, zEvent); - } - static void vdbeSorterPopulateDebug( -@@ -95055,7 +105281,7 @@ static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){ - */ - static MergeEngine *vdbeMergeEngineNew(int nReader){ - int N = 2; /* Smallest power of two >= nReader */ -- int nByte; /* Total bytes of space to allocate */ -+ i64 nByte; /* Total bytes of space to allocate */ - MergeEngine *pNew; /* Pointer to allocated object to return */ - - assert( nReader<=SORTER_MAX_MERGE_COUNT ); -@@ -95169,7 +105395,7 @@ static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){ - sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); - sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); - sqlite3OsFetch(pFd, 0, (int)nByte, &p); -- sqlite3OsUnfetch(pFd, 0, p); -+ if( p ) sqlite3OsUnfetch(pFd, 0, p); - } - } - #else -@@ -95307,6 +105533,10 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ - p->u.pNext = 0; - for(i=0; aSlot[i]; i++){ - p = vdbeSorterMerge(pTask, p, aSlot[i]); -+ /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use -+ ** | up all 64 aSlots[] with only a 64-bit address space. -+ ** v */ -+ assert( inTask-1) sub-tasks are all still busy, - ** fall back to using the final sub-task. The first (pSorter->nTask-1) -- ** sub-tasks are prefered as they use background threads - the final -+ ** sub-tasks are preferred as they use background threads - the final - ** sub-task uses the main thread. */ - for(i=0; iiPrev + i + 1) % nWorker; -@@ -95651,8 +105881,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite( - int rc = SQLITE_OK; /* Return Code */ - SorterRecord *pNew; /* New list element */ - int bFlush; /* True to flush contents of memory to PMA */ -- int nReq; /* Bytes of memory required */ -- int nPMA; /* Bytes of PMA space required */ -+ i64 nReq; /* Bytes of memory required */ -+ i64 nPMA; /* Bytes of PMA space required */ - int t; /* serial type of first record field */ - - assert( pCsr->eCurType==CURTYPE_SORTER ); -@@ -95887,6 +106117,7 @@ static int vdbeIncrMergerNew( - vdbeMergeEngineFree(pMerger); - rc = SQLITE_NOMEM_BKPT; - } -+ assert( *ppOut!=0 || rc!=SQLITE_OK ); - return rc; - } - -@@ -96076,7 +106307,7 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ - - rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); - -- /* Set up the required files for pIncr. A multi-theaded IncrMerge object -+ /* Set up the required files for pIncr. A multi-threaded IncrMerge object - ** requires two temp files to itself, whereas a single-threaded object - ** only requires a region of pTask->file2. */ - if( rc==SQLITE_OK ){ -@@ -96716,6 +106947,8 @@ static int bytecodevtabConnect( - "p5 INT," - "comment TEXT," - "subprog TEXT," -+ "nexec INT," -+ "ncycle INT," - "stmt HIDDEN" - ");", - -@@ -96730,6 +106963,9 @@ static int bytecodevtabConnect( - ");" - }; - -+ (void)argc; -+ (void)argv; -+ (void)pzErr; - rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]); - if( rc==SQLITE_OK ){ - pNew = sqlite3_malloc( sizeof(*pNew) ); -@@ -96875,7 +107111,7 @@ static int bytecodevtabColumn( - } - } - } -- i += 10; -+ i += 20; - } - } - switch( i ){ -@@ -96925,16 +107161,31 @@ static int bytecodevtabColumn( - } - break; - } -- case 10: /* tables_used.type */ -+ -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ case 9: /* nexec */ -+ sqlite3_result_int64(ctx, pOp->nExec); -+ break; -+ case 10: /* ncycle */ -+ sqlite3_result_int64(ctx, pOp->nCycle); -+ break; -+#else -+ case 9: /* nexec */ -+ case 10: /* ncycle */ -+ sqlite3_result_int(ctx, 0); -+ break; -+#endif -+ -+ case 20: /* tables_used.type */ - sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); - break; -- case 11: /* tables_used.schema */ -+ case 21: /* tables_used.schema */ - sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); - break; -- case 12: /* tables_used.name */ -+ case 22: /* tables_used.name */ - sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); - break; -- case 13: /* tables_used.wr */ -+ case 23: /* tables_used.wr */ - sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); - break; - } -@@ -96965,6 +107216,7 @@ static int bytecodevtabFilter( - bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; - bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; - int rc = SQLITE_OK; -+ (void)idxStr; - - bytecodevtabCursorClear(pCur); - pCur->iRowid = 0; -@@ -97007,7 +107259,7 @@ static int bytecodevtabBestIndex( - int rc = SQLITE_CONSTRAINT; - struct sqlite3_index_constraint *p; - bytecodevtab *pVTab = (bytecodevtab*)tab; -- int iBaseCol = pVTab->bTablesUsed ? 4 : 8; -+ int iBaseCol = pVTab->bTablesUsed ? 4 : 10; - pIdxInfo->estimatedCost = (double)100; - pIdxInfo->estimatedRows = 100; - pIdxInfo->idxNum = 0; -@@ -97054,7 +107306,8 @@ static sqlite3_module bytecodevtabModule = { - /* xSavepoint */ 0, - /* xRelease */ 0, - /* xRollbackTo */ 0, -- /* xShadowName */ 0 -+ /* xShadowName */ 0, -+ /* xIntegrity */ 0 - }; - - -@@ -97144,7 +107397,6 @@ struct MemJournal { - int nChunkSize; /* In-memory chunk-size */ - - int nSpill; /* Bytes of data before flushing */ -- int nSize; /* Bytes of data currently in memory */ - FileChunk *pFirst; /* Head of in-memory chunk-list */ - FilePoint endpoint; /* Pointer to the end of the file */ - FilePoint readpoint; /* Pointer to the end of the last xRead() */ -@@ -97205,14 +107457,13 @@ static int memjrnlRead( - /* - ** Free the list of FileChunk structures headed at MemJournal.pFirst. - */ --static void memjrnlFreeChunks(MemJournal *p){ -+static void memjrnlFreeChunks(FileChunk *pFirst){ - FileChunk *pIter; - FileChunk *pNext; -- for(pIter=p->pFirst; pIter; pIter=pNext){ -+ for(pIter=pFirst; pIter; pIter=pNext){ - pNext = pIter->pNext; - sqlite3_free(pIter); - } -- p->pFirst = 0; - } - - /* -@@ -97239,7 +107490,7 @@ static int memjrnlCreateFile(MemJournal *p){ - } - if( rc==SQLITE_OK ){ - /* No error has occurred. Free the in-memory buffers. */ -- memjrnlFreeChunks(©); -+ memjrnlFreeChunks(copy.pFirst); - } - } - if( rc!=SQLITE_OK ){ -@@ -97254,6 +107505,9 @@ static int memjrnlCreateFile(MemJournal *p){ - } - - -+/* Forward reference */ -+static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size); -+ - /* - ** Write data to the file. - */ -@@ -97284,22 +107538,20 @@ static int memjrnlWrite( - ** the in-memory journal is being used by a connection using the - ** atomic-write optimization. In this case the first 28 bytes of the - ** journal file may be written as part of committing the transaction. */ -- assert( iOfst==p->endpoint.iOffset || iOfst==0 ); --#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ -- || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) -+ assert( iOfst<=p->endpoint.iOffset ); -+ if( iOfst>0 && iOfst!=p->endpoint.iOffset ){ -+ memjrnlTruncate(pJfd, iOfst); -+ } - if( iOfst==0 && p->pFirst ){ - assert( p->nChunkSize>iAmt ); - memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); -- }else --#else -- assert( iOfst>0 || p->pFirst==0 ); --#endif -- { -+ }else{ - while( nWrite>0 ){ - FileChunk *pChunk = p->endpoint.pChunk; - int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize); - int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset); - -+ assert( pChunk!=0 || iChunkOffset==0 ); - if( iChunkOffset==0 ){ - /* New chunk is required to extend the file. */ - FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize)); -@@ -97314,15 +107566,15 @@ static int memjrnlWrite( - assert( !p->pFirst ); - p->pFirst = pNew; - } -- p->endpoint.pChunk = pNew; -+ pChunk = p->endpoint.pChunk = pNew; - } - -- memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace); -+ assert( pChunk!=0 ); -+ memcpy((u8*)pChunk->zChunk + iChunkOffset, zWrite, iSpace); - zWrite += iSpace; - nWrite -= iSpace; - p->endpoint.iOffset += iSpace; - } -- p->nSize = iAmt + iOfst; - } - } - -@@ -97330,19 +107582,29 @@ static int memjrnlWrite( - } - - /* --** Truncate the file. --** --** If the journal file is already on disk, truncate it there. Or, if it --** is still in main memory but is being truncated to zero bytes in size, --** ignore -+** Truncate the in-memory file. - */ - static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ - MemJournal *p = (MemJournal *)pJfd; -- if( ALWAYS(size==0) ){ -- memjrnlFreeChunks(p); -- p->nSize = 0; -- p->endpoint.pChunk = 0; -- p->endpoint.iOffset = 0; -+ assert( p->endpoint.pChunk==0 || p->endpoint.pChunk->pNext==0 ); -+ if( sizeendpoint.iOffset ){ -+ FileChunk *pIter = 0; -+ if( size==0 ){ -+ memjrnlFreeChunks(p->pFirst); -+ p->pFirst = 0; -+ }else{ -+ i64 iOff = p->nChunkSize; -+ for(pIter=p->pFirst; ALWAYS(pIter) && iOffpNext){ -+ iOff += p->nChunkSize; -+ } -+ if( ALWAYS(pIter) ){ -+ memjrnlFreeChunks(pIter->pNext); -+ pIter->pNext = 0; -+ } -+ } -+ -+ p->endpoint.pChunk = pIter; -+ p->endpoint.iOffset = size; - p->readpoint.pChunk = 0; - p->readpoint.iOffset = 0; - } -@@ -97354,7 +107616,7 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ - */ - static int memjrnlClose(sqlite3_file *pJfd){ - MemJournal *p = (MemJournal *)pJfd; -- memjrnlFreeChunks(p); -+ memjrnlFreeChunks(p->pFirst); - return SQLITE_OK; - } - -@@ -97424,6 +107686,8 @@ SQLITE_PRIVATE int sqlite3JournalOpen( - ){ - MemJournal *p = (MemJournal*)pJfd; - -+ assert( zName || nSpill<0 || (flags & SQLITE_OPEN_EXCLUSIVE) ); -+ - /* Zero the file-handle object. If nSpill was passed zero, initialize - ** it using the sqlite3OsOpen() function of the underlying VFS. In this - ** case none of the code in this module is executed as a result of calls -@@ -97528,7 +107792,7 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){ - ** Walk all expressions linked into the list of Window objects passed - ** as the second argument. - */ --static int walkWindowList(Walker *pWalker, Window *pList){ -+static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){ - Window *pWin; - for(pWin=pList; pWin; pWin=pWin->pNextWin){ - int rc; -@@ -97538,15 +107802,11 @@ static int walkWindowList(Walker *pWalker, Window *pList){ - if( rc ) return WRC_Abort; - rc = sqlite3WalkExpr(pWalker, pWin->pFilter); - if( rc ) return WRC_Abort; -- -- /* The next two are purely for calls to sqlite3RenameExprUnmap() -- ** within sqlite3WindowOffsetExpr(). Because of constraints imposed -- ** by sqlite3WindowOffsetExpr(), they can never fail. The results do -- ** not matter anyhow. */ - rc = sqlite3WalkExpr(pWalker, pWin->pStart); -- if( NEVER(rc) ) return WRC_Abort; -+ if( rc ) return WRC_Abort; - rc = sqlite3WalkExpr(pWalker, pWin->pEnd); -- if( NEVER(rc) ) return WRC_Abort; -+ if( rc ) return WRC_Abort; -+ if( bOneOnly ) break; - } - return WRC_Continue; - } -@@ -97571,7 +107831,7 @@ static int walkWindowList(Walker *pWalker, Window *pList){ - ** The return value from this routine is WRC_Abort to abandon the tree walk - ** and WRC_Continue to continue. - */ --static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ -+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3WalkExprNN(Walker *pWalker, Expr *pExpr){ - int rc; - testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); - testcase( ExprHasProperty(pExpr, EP_Reduced) ); -@@ -97580,12 +107840,14 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ - if( rc ) return rc & WRC_Abort; - if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ - assert( pExpr->x.pList==0 || pExpr->pRight==0 ); -- if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; -+ if( pExpr->pLeft && sqlite3WalkExprNN(pWalker, pExpr->pLeft) ){ -+ return WRC_Abort; -+ } - if( pExpr->pRight ){ - assert( !ExprHasProperty(pExpr, EP_WinFunc) ); - pExpr = pExpr->pRight; - continue; -- }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ }else if( ExprUseXSelect(pExpr) ){ - assert( !ExprHasProperty(pExpr, EP_WinFunc) ); - if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; - }else{ -@@ -97594,7 +107856,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ - } - #ifndef SQLITE_OMIT_WINDOWFUNC - if( ExprHasProperty(pExpr, EP_WinFunc) ){ -- if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort; -+ if( walkWindowList(pWalker, pExpr->y.pWin, 1) ) return WRC_Abort; - } - #endif - } -@@ -97604,7 +107866,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ - return WRC_Continue; - } - SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ -- return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue; -+ return pExpr ? sqlite3WalkExprNN(pWalker,pExpr) : WRC_Continue; - } - - /* -@@ -97622,6 +107884,16 @@ SQLITE_PRIVATE int sqlite3WalkExprList(Walker *pWalker, ExprList *p){ - return WRC_Continue; - } - -+/* -+** This is a no-op callback for Walker->xSelectCallback2. If this -+** callback is set, then the Select->pWinDefn list is traversed. -+*/ -+SQLITE_PRIVATE void sqlite3WalkWinDefnDummyCallback(Walker *pWalker, Select *p){ -+ UNUSED_PARAMETER(pWalker); -+ UNUSED_PARAMETER(p); -+ /* No-op */ -+} -+ - /* - ** Walk all expressions associated with SELECT statement p. Do - ** not invoke the SELECT callback on p, but do (of course) invoke -@@ -97635,13 +107907,18 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ - if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort; - if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort; --#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE) -- { -- Parse *pParse = pWalker->pParse; -- if( pParse && IN_RENAME_OBJECT ){ -+#if !defined(SQLITE_OMIT_WINDOWFUNC) -+ if( p->pWinDefn ){ -+ Parse *pParse; -+ if( pWalker->xSelectCallback2==sqlite3WalkWinDefnDummyCallback -+ || ((pParse = pWalker->pParse)!=0 && IN_RENAME_OBJECT) -+#ifndef SQLITE_OMIT_CTE -+ || pWalker->xSelectCallback2==sqlite3SelectPopWith -+#endif -+ ){ - /* The following may return WRC_Abort if there are unresolvable - ** symbols (e.g. a table that does not exist) in a window definition. */ -- int rc = walkWindowList(pWalker, p->pWinDefn); -+ int rc = walkWindowList(pWalker, p->pWinDefn, 0); - return rc; - } - } -@@ -97659,12 +107936,14 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ - SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ - SrcList *pSrc; - int i; -- struct SrcList_item *pItem; -+ SrcItem *pItem; - - pSrc = p->pSrc; -- if( pSrc ){ -+ if( ALWAYS(pSrc) ){ - for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ -- if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){ -+ if( pItem->fg.isSubquery -+ && sqlite3WalkSelect(pWalker, pItem->u4.pSubq->pSelect) -+ ){ - return WRC_Abort; - } - if( pItem->fg.isTabFunc -@@ -97715,7 +107994,7 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){ - } - - /* Increase the walkerDepth when entering a subquery, and --** descrease when leaving the subquery. -+** decrease when leaving the subquery. - */ - SQLITE_PRIVATE int sqlite3WalkerDepthIncrease(Walker *pWalker, Select *pSelect){ - UNUSED_PARAMETER(pSelect); -@@ -97825,7 +108104,6 @@ static void resolveAlias( - ExprList *pEList, /* A result set */ - int iCol, /* A column in the result set. 0..pEList->nExpr-1 */ - Expr *pExpr, /* Transform this into an alias to the result set */ -- const char *zType, /* "GROUP" or "ORDER" or "" */ - int nSubquery /* Number of subqueries that the label is moving */ - ){ - Expr *pOrig; /* The iCol-th column of the result set */ -@@ -97835,74 +108113,63 @@ static void resolveAlias( - assert( iCol>=0 && iColnExpr ); - pOrig = pEList->a[iCol].pExpr; - assert( pOrig!=0 ); -+ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); -+ if( pExpr->pAggInfo ) return; - db = pParse->db; - pDup = sqlite3ExprDup(db, pOrig, 0); -- if( pDup!=0 ){ -- if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery); -+ if( db->mallocFailed ){ -+ sqlite3ExprDelete(db, pDup); -+ pDup = 0; -+ }else{ -+ Expr temp; -+ incrAggFunctionDepth(pDup, nSubquery); - if( pExpr->op==TK_COLLATE ){ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); - } -- -- /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This -- ** prevents ExprDelete() from deleting the Expr structure itself, -- ** allowing it to be repopulated by the memcpy() on the following line. -- ** The pExpr->u.zToken might point into memory that will be freed by the -- ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to -- ** make a copy of the token before doing the sqlite3DbFree(). -- */ -- ExprSetProperty(pExpr, EP_Static); -- sqlite3ExprDelete(db, pExpr); -- memcpy(pExpr, pDup, sizeof(*pExpr)); -- if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){ -- assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 ); -- pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken); -- pExpr->flags |= EP_MemToken; -- } -+ memcpy(&temp, pDup, sizeof(Expr)); -+ memcpy(pDup, pExpr, sizeof(Expr)); -+ memcpy(pExpr, &temp, sizeof(Expr)); - if( ExprHasProperty(pExpr, EP_WinFunc) ){ -- if( pExpr->y.pWin!=0 ){ -+ if( ALWAYS(pExpr->y.pWin!=0) ){ - pExpr->y.pWin->pOwner = pExpr; -- }else{ -- assert( db->mallocFailed ); - } - } -- sqlite3DbFree(db, pDup); -+ sqlite3ExprDeferredDelete(pParse, pDup); - } -- ExprSetProperty(pExpr, EP_Alias); - } - -- - /* --** Return TRUE if the name zCol occurs anywhere in the USING clause. -+** Subqueries store the original database, table and column names for their -+** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN", -+** and mark the expression-list item by setting ExprList.a[].fg.eEName -+** to ENAME_TAB. - ** --** Return FALSE if the USING clause is NULL or if it does not contain --** zCol. --*/ --static int nameInUsingClause(IdList *pUsing, const char *zCol){ -- if( pUsing ){ -- int k; -- for(k=0; knId; k++){ -- if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1; -- } -- } -- return 0; --} -- --/* --** Subqueries stores the original database, table and column names for their --** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". --** Check to see if the zSpan given to this routine matches the zDb, zTab, --** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will --** match anything. -+** Check to see if the zSpan/eEName of the expression-list item passed to this -+** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are -+** NULL then those fields will match anything. Return true if there is a match, -+** or false otherwise. -+** -+** SF_NestedFrom subqueries also store an entry for the implicit rowid (or -+** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID, -+** and setting zSpan to "DATABASE.TABLE.". This type of pItem -+** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid) -+** is set to 1 if there is this kind of match. - */ - SQLITE_PRIVATE int sqlite3MatchEName( - const struct ExprList_item *pItem, - const char *zCol, - const char *zTab, -- const char *zDb -+ const char *zDb, -+ int *pbRowid - ){ - int n; - const char *zSpan; -- if( pItem->eEName!=ENAME_TAB ) return 0; -+ int eEName = pItem->fg.eEName; -+ if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){ -+ return 0; -+ } -+ assert( pbRowid==0 || *pbRowid==0 ); - zSpan = pItem->zEName; - for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} - if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ -@@ -97914,9 +108181,11 @@ SQLITE_PRIVATE int sqlite3MatchEName( - return 0; - } - zSpan += n+1; -- if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ -- return 0; -+ if( zCol ){ -+ if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0; -+ if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0; - } -+ if( eEName==ENAME_ROWID ) *pbRowid = 1; - return 1; - } - -@@ -97946,8 +108215,10 @@ SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){ - Table *pExTab; - - n = pExpr->iColumn; -+ assert( ExprUseYTab(pExpr) ); - pExTab = pExpr->y.pTab; - assert( pExTab!=0 ); -+ assert( n < pExTab->nCol ); - if( (pExTab->tabFlags & TF_HasGenerated)!=0 - && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0 - ){ -@@ -97962,6 +108233,55 @@ SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){ - } - } - -+/* -+** Create a new expression term for the column specified by pMatch and -+** iColumn. Append this new expression term to the FULL JOIN Match set -+** in *ppList. Create a new *ppList if this is the first term in the -+** set. -+*/ -+static void extendFJMatch( -+ Parse *pParse, /* Parsing context */ -+ ExprList **ppList, /* ExprList to extend */ -+ SrcItem *pMatch, /* Source table containing the column */ -+ i16 iColumn /* The column number */ -+){ -+ Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); -+ if( pNew ){ -+ pNew->iTable = pMatch->iCursor; -+ pNew->iColumn = iColumn; -+ pNew->y.pTab = pMatch->pSTab; -+ assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ); -+ ExprSetProperty(pNew, EP_CanBeNull); -+ *ppList = sqlite3ExprListAppend(pParse, *ppList, pNew); -+ } -+} -+ -+/* -+** Return TRUE (non-zero) if zTab is a valid name for the schema table pTab. -+*/ -+static SQLITE_NOINLINE int isValidSchemaTableName( -+ const char *zTab, /* Name as it appears in the SQL */ -+ Table *pTab, /* The schema table we are trying to match */ -+ const char *zDb /* non-NULL if a database qualifier is present */ -+){ -+ const char *zLegacy; -+ assert( pTab!=0 ); -+ assert( pTab->tnum==1 ); -+ if( sqlite3StrNICmp(zTab, "sqlite_", 7)!=0 ) return 0; -+ zLegacy = pTab->zName; -+ if( strcmp(zLegacy+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){ -+ if( sqlite3StrICmp(zTab+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){ -+ return 1; -+ } -+ if( zDb==0 ) return 0; -+ if( sqlite3StrICmp(zTab+7, &LEGACY_SCHEMA_TABLE[7])==0 ) return 1; -+ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1; -+ }else{ -+ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1; -+ } -+ return 0; -+} -+ - /* - ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up - ** that name in the set of source tables in pSrcList and make the pExpr -@@ -97993,25 +108313,27 @@ static int lookupName( - Parse *pParse, /* The parsing context */ - const char *zDb, /* Name of the database containing table, or NULL */ - const char *zTab, /* Name of table containing column, or NULL */ -- const char *zCol, /* Name of the column. */ -+ const Expr *pRight, /* Name of the column. */ - NameContext *pNC, /* The name context used to resolve the name */ - Expr *pExpr /* Make this EXPR node point to the selected column */ - ){ - int i, j; /* Loop counters */ - int cnt = 0; /* Number of matching column names */ -- int cntTab = 0; /* Number of matching table names */ -+ int cntTab = 0; /* Number of potential "rowid" matches */ - int nSubquery = 0; /* How many levels of subquery */ - sqlite3 *db = pParse->db; /* The database connection */ -- struct SrcList_item *pItem; /* Use for looping over pSrcList items */ -- struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ -+ SrcItem *pItem; /* Use for looping over pSrcList items */ -+ SrcItem *pMatch = 0; /* The matching pSrcList item */ - NameContext *pTopNC = pNC; /* First namecontext in the list */ - Schema *pSchema = 0; /* Schema of the expression */ - int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ -- Table *pTab = 0; /* Table hold the row */ -- Column *pCol; /* A column of pTab */ -+ Table *pTab = 0; /* Table holding the row */ -+ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ -+ const char *zCol = pRight->u.zToken; - - assert( pNC ); /* the name context cannot be NULL. */ - assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ -+ assert( zDb==0 || zTab!=0 ); - assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); - - /* Initialize the node to no-match */ -@@ -98056,65 +108378,166 @@ static int lookupName( - - if( pSrcList ){ - for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ -- u8 hCol; -- pTab = pItem->pTab; -+ pTab = pItem->pSTab; - assert( pTab!=0 && pTab->zName!=0 ); -- assert( pTab->nCol>0 ); -- if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ -+ assert( pTab->nCol>0 || pParse->nErr ); -+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem)); -+ if( pItem->fg.isNestedFrom ){ -+ /* In this case, pItem is a subquery that has been formed from a -+ ** parenthesized subset of the FROM clause terms. Example: -+ ** .... FROM t1 LEFT JOIN (t2 RIGHT JOIN t3 USING(x)) USING(y) ... -+ ** \_________________________/ -+ ** This pItem -------------^ -+ */ - int hit = 0; -- pEList = pItem->pSelect->pEList; -+ Select *pSel; -+ assert( pItem->fg.isSubquery ); -+ assert( pItem->u4.pSubq!=0 ); -+ pSel = pItem->u4.pSubq->pSelect; -+ assert( pSel!=0 ); -+ pEList = pSel->pEList; -+ assert( pEList!=0 ); -+ assert( pEList->nExpr==pTab->nCol ); - for(j=0; jnExpr; j++){ -- if( sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ -+ int bRowid = 0; /* True if possible rowid match */ -+ if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){ -+ continue; -+ } -+ if( bRowid==0 ){ -+ if( cnt>0 ){ -+ if( pItem->fg.isUsing==0 -+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 -+ ){ -+ /* Two or more tables have the same column name which is -+ ** not joined by USING. This is an error. Signal as much -+ ** by clearing pFJMatch and letting cnt go above 1. */ -+ sqlite3ExprListDelete(db, pFJMatch); -+ pFJMatch = 0; -+ }else -+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){ -+ /* An INNER or LEFT JOIN. Use the left-most table */ -+ continue; -+ }else -+ if( (pItem->fg.jointype & JT_LEFT)==0 ){ -+ /* A RIGHT JOIN. Use the right-most table */ -+ cnt = 0; -+ sqlite3ExprListDelete(db, pFJMatch); -+ pFJMatch = 0; -+ }else{ -+ /* For a FULL JOIN, we must construct a coalesce() func */ -+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); -+ } -+ } - cnt++; -- cntTab = 2; -- pMatch = pItem; -- pExpr->iColumn = j; - hit = 1; -+ }else if( cnt>0 ){ -+ /* This is a potential rowid match, but there has already been -+ ** a real match found. So this can be ignored. */ -+ continue; - } -+ cntTab++; -+ pMatch = pItem; -+ pExpr->iColumn = j; -+ pEList->a[j].fg.bUsed = 1; -+ -+ /* rowid cannot be part of a USING clause - assert() this. */ -+ assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 ); -+ if( pEList->a[j].fg.bUsingTerm ) break; - } - if( hit || zTab==0 ) continue; - } -- if( zDb && pTab->pSchema!=pSchema ){ -- continue; -- } -+ assert( zDb==0 || zTab!=0 ); - if( zTab ){ -- const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; -- assert( zTabName!=0 ); -- if( sqlite3StrICmp(zTabName, zTab)!=0 ){ -- continue; -+ if( zDb ){ -+ if( pTab->pSchema!=pSchema ) continue; -+ if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue; -+ } -+ if( pItem->zAlias!=0 ){ -+ if( sqlite3StrICmp(zTab, pItem->zAlias)!=0 ){ -+ continue; -+ } -+ }else if( sqlite3StrICmp(zTab, pTab->zName)!=0 ){ -+ if( pTab->tnum!=1 ) continue; -+ if( !isValidSchemaTableName(zTab, pTab, zDb) ) continue; - } -+ assert( ExprUseYTab(pExpr) ); - if( IN_RENAME_OBJECT && pItem->zAlias ){ - sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab); - } - } -- if( 0==(cntTab++) ){ -+ j = sqlite3ColumnIndex(pTab, zCol); -+ if( j>=0 ){ -+ if( cnt>0 ){ -+ if( pItem->fg.isUsing==0 -+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 -+ ){ -+ /* Two or more tables have the same column name which is -+ ** not joined by USING. This is an error. Signal as much -+ ** by clearing pFJMatch and letting cnt go above 1. */ -+ sqlite3ExprListDelete(db, pFJMatch); -+ pFJMatch = 0; -+ }else -+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){ -+ /* An INNER or LEFT JOIN. Use the left-most table */ -+ continue; -+ }else -+ if( (pItem->fg.jointype & JT_LEFT)==0 ){ -+ /* A RIGHT JOIN. Use the right-most table */ -+ cnt = 0; -+ sqlite3ExprListDelete(db, pFJMatch); -+ pFJMatch = 0; -+ }else{ -+ /* For a FULL JOIN, we must construct a coalesce() func */ -+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); -+ } -+ } -+ cnt++; - pMatch = pItem; -+ /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ -+ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; -+ if( pItem->fg.isNestedFrom ){ -+ sqlite3SrcItemColumnUsed(pItem, j); -+ } - } -- hCol = sqlite3StrIHash(zCol); -- for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ -- if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ -- /* If there has been exactly one prior match and this match -- ** is for the right-hand table of a NATURAL JOIN or is in a -- ** USING clause, then skip this match. -- */ -- if( cnt==1 ){ -- if( pItem->fg.jointype & JT_NATURAL ) continue; -- if( nameInUsingClause(pItem->pUsing, zCol) ) continue; -- } -- cnt++; -+ if( 0==cnt && VisibleRowid(pTab) ){ -+ /* pTab is a potential ROWID match. Keep track of it and match -+ ** the ROWID later if that seems appropriate. (Search for "cntTab" -+ ** to find related code.) Only allow a ROWID match if there is -+ ** a single ROWID match candidate. -+ */ -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match -+ ** if there is a single VIEW candidate or if there is a single -+ ** non-VIEW candidate plus multiple VIEW candidates. In other -+ ** words non-VIEW candidate terms take precedence over VIEWs. -+ */ -+ if( cntTab==0 -+ || (cntTab==1 -+ && pMatch!=0 -+ && ALWAYS(pMatch->pSTab!=0) -+ && (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0 -+ && (pTab->tabFlags & TF_Ephemeral)==0) -+ ){ -+ cntTab = 1; - pMatch = pItem; -- /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ -- pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; -- break; -+ }else{ -+ cntTab++; - } -+#else -+ /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is -+ ** simpler since we require exactly one candidate, which will -+ ** always be a non-VIEW -+ */ -+ cntTab++; -+ pMatch = pItem; -+#endif - } - } - if( pMatch ){ - pExpr->iTable = pMatch->iCursor; -- pExpr->y.pTab = pMatch->pTab; -- /* RIGHT JOIN not (yet) supported */ -- assert( (pMatch->fg.jointype & JT_RIGHT)==0 ); -- if( (pMatch->fg.jointype & JT_LEFT)!=0 ){ -+ assert( ExprUseYTab(pExpr) ); -+ pExpr->y.pTab = pMatch->pSTab; -+ if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){ - ExprSetProperty(pExpr, EP_CanBeNull); - } - pSchema = pExpr->y.pTab->pSchema; -@@ -98124,28 +108547,38 @@ static int lookupName( - #if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) - /* If we have not already resolved the name, then maybe - ** it is a new.* or old.* trigger argument reference. Or -- ** maybe it is an excluded.* from an upsert. -+ ** maybe it is an excluded.* from an upsert. Or maybe it is -+ ** a reference in the RETURNING clause to a table being modified. - */ -- if( zDb==0 && zTab!=0 && cntTab==0 ){ -+ if( cnt==0 && zDb==0 ){ - pTab = 0; - #ifndef SQLITE_OMIT_TRIGGER - if( pParse->pTriggerTab!=0 ){ - int op = pParse->eTriggerOp; - assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); -- if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ -+ if( pParse->bReturning ){ -+ if( (pNC->ncFlags & NC_UBaseReg)!=0 -+ && ALWAYS(zTab==0 -+ || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0 -+ || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0)) -+ ){ -+ pExpr->iTable = op!=TK_DELETE; -+ pTab = pParse->pTriggerTab; -+ } -+ }else if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){ - pExpr->iTable = 1; - pTab = pParse->pTriggerTab; -- }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ -+ }else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){ - pExpr->iTable = 0; - pTab = pParse->pTriggerTab; - } - } - #endif /* SQLITE_OMIT_TRIGGER */ - #ifndef SQLITE_OMIT_UPSERT -- if( (pNC->ncFlags & NC_UUpsert)!=0 ){ -+ if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){ - Upsert *pUpsert = pNC->uNC.pUpsert; - if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){ -- pTab = pUpsert->pUpsertSrc->a[0].pTab; -+ pTab = pUpsert->pUpsertSrc->a[0].pSTab; - pExpr->iTable = EXCLUDED_TABLE_NUMBER; - } - } -@@ -98153,26 +108586,25 @@ static int lookupName( - - if( pTab ){ - int iCol; -- u8 hCol = sqlite3StrIHash(zCol); - pSchema = pTab->pSchema; - cntTab++; -- for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ -- if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ -- if( iCol==pTab->iPKey ){ -- iCol = -1; -- } -- break; -+ iCol = sqlite3ColumnIndex(pTab, zCol); -+ if( iCol>=0 ){ -+ if( pTab->iPKey==iCol ) iCol = -1; -+ }else{ -+ if( sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){ -+ iCol = -1; -+ }else{ -+ iCol = pTab->nCol; - } - } -- if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){ -- /* IMP: R-51414-32910 */ -- iCol = -1; -- } - if( iColnCol ){ - cnt++; -+ pMatch = 0; - #ifndef SQLITE_OMIT_UPSERT - if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){ - testcase( iCol==(-1) ); -+ assert( ExprUseYTab(pExpr) ); - if( IN_RENAME_OBJECT ){ - pExpr->iColumn = iCol; - pExpr->y.pTab = pTab; -@@ -98181,27 +108613,35 @@ static int lookupName( - pExpr->iTable = pNC->uNC.pUpsert->regData + - sqlite3TableColumnToStorage(pTab, iCol); - eNewExprOp = TK_REGISTER; -- ExprSetProperty(pExpr, EP_Alias); - } - }else - #endif /* SQLITE_OMIT_UPSERT */ - { --#ifndef SQLITE_OMIT_TRIGGER -- if( iCol<0 ){ -- pExpr->affExpr = SQLITE_AFF_INTEGER; -- }else if( pExpr->iTable==0 ){ -- testcase( iCol==31 ); -- testcase( iCol==32 ); -- pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<y.pTab = pTab; -- pExpr->iColumn = (i16)iCol; -- eNewExprOp = TK_TRIGGER; -+ if( pParse->bReturning ){ -+ eNewExprOp = TK_REGISTER; -+ pExpr->op2 = TK_COLUMN; -+ pExpr->iColumn = iCol; -+ pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable + -+ sqlite3TableColumnToStorage(pTab, iCol) + 1; -+ }else{ -+ pExpr->iColumn = (i16)iCol; -+ eNewExprOp = TK_TRIGGER; -+#ifndef SQLITE_OMIT_TRIGGER -+ if( iCol<0 ){ -+ pExpr->affExpr = SQLITE_AFF_INTEGER; -+ }else if( pExpr->iTable==0 ){ -+ testcase( iCol==31 ); -+ testcase( iCol==32 ); -+ pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<=1 - && pMatch - && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 - && sqlite3IsRowid(zCol) -- && VisibleRowid(pMatch->pTab) -+ && ALWAYS(VisibleRowid(pMatch->pSTab) || pMatch->fg.isNestedFrom) - ){ -- cnt = 1; -- pExpr->iColumn = -1; -+ cnt = cntTab; -+#if SQLITE_ALLOW_ROWID_IN_VIEW+0==2 -+ if( pMatch->pSTab!=0 && IsView(pMatch->pSTab) ){ -+ eNewExprOp = TK_NULL; -+ } -+#endif -+ if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; - pExpr->affExpr = SQLITE_AFF_INTEGER; - } - -@@ -98241,21 +108686,21 @@ static int lookupName( - ** is supported for backwards compatibility only. Hence, we issue a warning - ** on sqlite3_log() whenever the capability is used. - */ -- if( (pNC->ncFlags & NC_UEList)!=0 -- && cnt==0 -+ if( cnt==0 -+ && (pNC->ncFlags & NC_UEList)!=0 - && zTab==0 - ){ - pEList = pNC->uNC.pEList; - assert( pEList!=0 ); - for(j=0; jnExpr; j++){ - char *zAs = pEList->a[j].zEName; -- if( pEList->a[j].eEName==ENAME_NAME -+ if( pEList->a[j].fg.eEName==ENAME_NAME - && sqlite3_stricmp(zAs, zCol)==0 - ){ - Expr *pOrig; - assert( pExpr->pLeft==0 && pExpr->pRight==0 ); -- assert( pExpr->x.pList==0 ); -- assert( pExpr->x.pSelect==0 ); -+ assert( ExprUseXList(pExpr)==0 || pExpr->x.pList==0 ); -+ assert( ExprUseXSelect(pExpr)==0 || pExpr->x.pSelect==0 ); - pOrig = pEList->a[j].pExpr; - if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ - sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); -@@ -98271,7 +108716,7 @@ static int lookupName( - sqlite3ErrorMsg(pParse, "row value misused"); - return WRC_Abort; - } -- resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); -+ resolveAlias(pParse, pEList, j, pExpr, nSubquery); - cnt = 1; - pMatch = 0; - assert( zTab==0 && zDb==0 ); -@@ -98327,7 +108772,7 @@ static int lookupName( - sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); - #endif - pExpr->op = TK_STRING; -- pExpr->y.pTab = 0; -+ memset(&pExpr->y, 0, sizeof(pExpr->y)); - return WRC_Prune; - } - if( sqlite3ExprIdToTrueFalse(pExpr) ){ -@@ -98336,21 +108781,63 @@ static int lookupName( - } - - /* -- ** cnt==0 means there was not match. cnt>1 means there were two or -- ** more matches. Either way, we have an error. -+ ** cnt==0 means there was not match. -+ ** cnt>1 means there were two or more matches. -+ ** -+ ** cnt==0 is always an error. cnt>1 is often an error, but might -+ ** be multiple matches for a NATURAL LEFT JOIN or a LEFT JOIN USING. - */ -+ assert( pFJMatch==0 || cnt>0 ); -+ assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); - if( cnt!=1 ){ - const char *zErr; -+ if( pFJMatch ){ -+ if( pFJMatch->nExpr==cnt-1 ){ -+ if( ExprHasProperty(pExpr,EP_Leaf) ){ -+ ExprClearProperty(pExpr,EP_Leaf); -+ }else{ -+ sqlite3ExprDelete(db, pExpr->pLeft); -+ pExpr->pLeft = 0; -+ sqlite3ExprDelete(db, pExpr->pRight); -+ pExpr->pRight = 0; -+ } -+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); -+ pExpr->op = TK_FUNCTION; -+ pExpr->u.zToken = "coalesce"; -+ pExpr->x.pList = pFJMatch; -+ cnt = 1; -+ goto lookupname_end; -+ }else{ -+ sqlite3ExprListDelete(db, pFJMatch); -+ pFJMatch = 0; -+ } -+ } - zErr = cnt==0 ? "no such column" : "ambiguous column name"; - if( zDb ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); - }else if( zTab ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); -+ }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){ -+ sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a" -+ " string literal in single-quotes?", -+ zErr, zCol); - }else{ - sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); - } -+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); - pParse->checkSchema = 1; -- pTopNC->nErr++; -+ pTopNC->nNcErr++; -+ eNewExprOp = TK_NULL; -+ } -+ assert( pFJMatch==0 ); -+ -+ /* Remove all substructure from pExpr */ -+ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ -+ sqlite3ExprDelete(db, pExpr->pLeft); -+ pExpr->pLeft = 0; -+ sqlite3ExprDelete(db, pExpr->pRight); -+ pExpr->pRight = 0; -+ ExprSetProperty(pExpr, EP_Leaf); - } - - /* If a column from a table in pSrcList is referenced, then record -@@ -98367,24 +108854,25 @@ static int lookupName( - ** If a generated column is referenced, set bits for every column - ** of the table. - */ -- if( pExpr->iColumn>=0 && pMatch!=0 ){ -- pMatch->colUsed |= sqlite3ExprColUsed(pExpr); -+ if( pMatch ){ -+ if( pExpr->iColumn>=0 ){ -+ pMatch->colUsed |= sqlite3ExprColUsed(pExpr); -+ }else{ -+ pMatch->fg.rowidUsed = 1; -+ } - } - -- /* Clean up and return -- */ -- sqlite3ExprDelete(db, pExpr->pLeft); -- pExpr->pLeft = 0; -- sqlite3ExprDelete(db, pExpr->pRight); -- pExpr->pRight = 0; - pExpr->op = eNewExprOp; -- ExprSetProperty(pExpr, EP_Leaf); - lookupname_end: - if( cnt==1 ){ - assert( pNC!=0 ); -- if( !ExprHasProperty(pExpr, EP_Alias) ){ -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ if( pParse->db->xAuth -+ && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER) -+ ){ - sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); - } -+#endif - /* Increment the nRef value on all name contexts from TopNC up to - ** the point where the name matched. */ - for(;;){ -@@ -98406,8 +108894,10 @@ lookupname_end: - SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ - Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); - if( p ){ -- struct SrcList_item *pItem = &pSrc->a[iSrc]; -- Table *pTab = p->y.pTab = pItem->pTab; -+ SrcItem *pItem = &pSrc->a[iSrc]; -+ Table *pTab; -+ assert( ExprUseYTab(p) ); -+ pTab = p->y.pTab = pItem->pSTab; - p->iTable = pItem->iCursor; - if( p->y.pTab->iPKey==iCol ){ - p->iColumn = -1; -@@ -98449,7 +108939,8 @@ static void notValidImpl( - Parse *pParse, /* Leave error message here */ - NameContext *pNC, /* The name context */ - const char *zMsg, /* Type of error */ -- Expr *pExpr /* Invalidate this expression on error */ -+ Expr *pExpr, /* Invalidate this expression on error */ -+ Expr *pError /* Associate error with this expression */ - ){ - const char *zIn = "partial index WHERE clauses"; - if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions"; -@@ -98461,10 +108952,11 @@ static void notValidImpl( - #endif - sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn); - if( pExpr ) pExpr->op = TK_NULL; -+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); - } --#define sqlite3ResolveNotValid(P,N,M,X,E) \ -+#define sqlite3ResolveNotValid(P,N,M,X,E,R) \ - assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \ -- if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E); -+ if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R); - - /* - ** Expression p should encode a floating point value between 1.0 and 0.0. -@@ -98474,6 +108966,7 @@ static void notValidImpl( - static int exprProbability(Expr *p){ - double r = -1.0; - if( p->op!=TK_FLOAT ) return -1; -+ assert( !ExprHasProperty(p, EP_IntValue) ); - sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8); - assert( r>=0.0 ); - if( r>1.0 ) return -1; -@@ -98518,17 +109011,87 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - */ - case TK_ROW: { - SrcList *pSrcList = pNC->pSrcList; -- struct SrcList_item *pItem; -+ SrcItem *pItem; - assert( pSrcList && pSrcList->nSrc>=1 ); - pItem = pSrcList->a; - pExpr->op = TK_COLUMN; -- pExpr->y.pTab = pItem->pTab; -+ assert( ExprUseYTab(pExpr) ); -+ pExpr->y.pTab = pItem->pSTab; - pExpr->iTable = pItem->iCursor; - pExpr->iColumn--; - pExpr->affExpr = SQLITE_AFF_INTEGER; - break; - } - -+ /* An optimization: Attempt to convert -+ ** -+ ** "expr IS NOT NULL" --> "TRUE" -+ ** "expr IS NULL" --> "FALSE" -+ ** -+ ** if we can prove that "expr" is never NULL. Call this the -+ ** "NOT NULL strength reduction optimization". -+ ** -+ ** If this optimization occurs, also restore the NameContext ref-counts -+ ** to the state they where in before the "column" LHS expression was -+ ** resolved. This prevents "column" from being counted as having been -+ ** referenced, which might prevent a SELECT from being erroneously -+ ** marked as correlated. -+ ** -+ ** 2024-03-28: Beware of aggregates. A bare column of aggregated table -+ ** can still evaluate to NULL even though it is marked as NOT NULL. -+ ** Example: -+ ** -+ ** CREATE TABLE t1(a INT NOT NULL); -+ ** SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1; -+ ** -+ ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized -+ ** here because at the time this case is hit, we do not yet know whether -+ ** or not t1 is being aggregated. We have to assume the worst and omit -+ ** the optimization. The only time it is safe to apply this optimization -+ ** is within the WHERE clause. -+ */ -+ case TK_NOTNULL: -+ case TK_ISNULL: { -+ int anRef[8]; -+ NameContext *p; -+ int i; -+ for(i=0, p=pNC; p && ipNext, i++){ -+ anRef[i] = p->nRef; -+ } -+ sqlite3WalkExpr(pWalker, pExpr->pLeft); -+ if( IN_RENAME_OBJECT ) return WRC_Prune; -+ if( sqlite3ExprCanBeNull(pExpr->pLeft) ){ -+ /* The expression can be NULL. So the optimization does not apply */ -+ return WRC_Prune; -+ } -+ -+ for(i=0, p=pNC; p; p=p->pNext, i++){ -+ if( (p->ncFlags & NC_Where)==0 ){ -+ return WRC_Prune; /* Not in a WHERE clause. Unsafe to optimize. */ -+ } -+ } -+ testcase( ExprHasProperty(pExpr, EP_OuterON) ); -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x80000 ){ -+ sqlite3DebugPrintf( -+ "NOT NULL strength reduction converts the following to %d:\n", -+ pExpr->op==TK_NOTNULL -+ ); -+ sqlite3ShowExpr(pExpr); -+ } -+#endif /* TREETRACE_ENABLED */ -+ pExpr->u.iValue = (pExpr->op==TK_NOTNULL); -+ pExpr->flags |= EP_IntValue; -+ pExpr->op = TK_INTEGER; -+ for(i=0, p=pNC; p && ipNext, i++){ -+ p->nRef = anRef[i]; -+ } -+ sqlite3ExprDelete(pParse->db, pExpr->pLeft); -+ pExpr->pLeft = 0; -+ return WRC_Prune; -+ } -+ - /* A column name: ID - ** Or table name and column name: ID.ID - ** Or a database, table and column: ID.ID.ID -@@ -98539,7 +109102,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - */ - case TK_ID: - case TK_DOT: { -- const char *zColumn; - const char *zTable; - const char *zDb; - Expr *pRight; -@@ -98547,41 +109109,43 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - if( pExpr->op==TK_ID ){ - zDb = 0; - zTable = 0; -- zColumn = pExpr->u.zToken; -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ pRight = pExpr; - }else{ - Expr *pLeft = pExpr->pLeft; - testcase( pNC->ncFlags & NC_IdxExpr ); - testcase( pNC->ncFlags & NC_GenCol ); - sqlite3ResolveNotValid(pParse, pNC, "the \".\" operator", -- NC_IdxExpr|NC_GenCol, 0); -+ NC_IdxExpr|NC_GenCol, 0, pExpr); - pRight = pExpr->pRight; - if( pRight->op==TK_ID ){ - zDb = 0; - }else{ - assert( pRight->op==TK_DOT ); -+ assert( !ExprHasProperty(pRight, EP_IntValue) ); - zDb = pLeft->u.zToken; - pLeft = pRight->pLeft; - pRight = pRight->pRight; - } -+ assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); - zTable = pLeft->u.zToken; -- zColumn = pRight->u.zToken; -+ assert( ExprUseYTab(pExpr) ); - if( IN_RENAME_OBJECT ){ - sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); - sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); - } - } -- return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); -+ return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr); - } - - /* Resolve function names - */ - case TK_FUNCTION: { -- ExprList *pList = pExpr->x.pList; /* The argument list */ -- int n = pList ? pList->nExpr : 0; /* Number of arguments */ -+ ExprList *pList; /* The argument list */ -+ int n; /* Number of arguments */ - int no_such_func = 0; /* True if no such function exists */ - int wrong_num_args = 0; /* True if wrong number of arguments */ - int is_agg = 0; /* True if is an aggregate function */ -- int nId; /* Number of characters in function name */ - const char *zId; /* The function name. */ - FuncDef *pDef; /* Information about the function */ - u8 enc = ENC(pParse->db); /* The database encoding */ -@@ -98589,9 +109153,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - #ifndef SQLITE_OMIT_WINDOWFUNC - Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); - #endif -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); -+ assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); -+ assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER ); -+ pList = pExpr->x.pList; -+ n = pList ? pList->nExpr : 0; - zId = pExpr->u.zToken; -- nId = sqlite3Strlen30(zId); - pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); - if( pDef==0 ){ - pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0); -@@ -98608,9 +109174,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - pExpr->iTable = exprProbability(pList->a[1].pExpr); - if( pExpr->iTable<0 ){ - sqlite3ErrorMsg(pParse, -- "second argument to likelihood() must be a " -- "constant between 0.0 and 1.0"); -- pNC->nErr++; -+ "second argument to %#T() must be a " -+ "constant between 0.0 and 1.0", pExpr); -+ pNC->nNcErr++; - } - }else{ - /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is -@@ -98630,15 +109196,33 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - int auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0); - if( auth!=SQLITE_OK ){ - if( auth==SQLITE_DENY ){ -- sqlite3ErrorMsg(pParse, "not authorized to use function: %s", -- pDef->zName); -- pNC->nErr++; -+ sqlite3ErrorMsg(pParse, "not authorized to use function: %#T", -+ pExpr); -+ pNC->nNcErr++; - } - pExpr->op = TK_NULL; - return WRC_Prune; - } - } - #endif -+ -+ /* If the function may call sqlite3_value_subtype(), then set the -+ ** EP_SubtArg flag on all of its argument expressions. This prevents -+ ** where.c from replacing the expression with a value read from an -+ ** index on the same expression, which will not have the correct -+ ** subtype. Also set the flag if the function expression itself is -+ ** an EP_SubtArg expression. In this case subtypes are required as -+ ** the function may return a value with a subtype back to its -+ ** caller using sqlite3_result_value(). */ -+ if( (pDef->funcFlags & SQLITE_SUBTYPE) -+ || ExprHasProperty(pExpr, EP_SubtArg) -+ ){ -+ int ii; -+ for(ii=0; iia[ii].pExpr, EP_SubtArg); -+ } -+ } -+ - if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){ - /* For the purposes of the EP_ConstFunc flag, date and time - ** functions and other functions that change slowly are considered -@@ -98652,13 +109236,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - ** sqlite_version() that might change over time cannot be used - ** in an index or generated column. Curiously, they can be used - ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all -- ** all this. */ -+ ** allow this. */ - sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions", -- NC_IdxExpr|NC_PartIdx|NC_GenCol, 0); -+ NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr); - }else{ - assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */ - pExpr->op2 = pNC->ncFlags & NC_SelfRef; -- if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL); - } - if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0 - && pParse->nested==0 -@@ -98667,13 +109250,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - /* Internal-use-only functions are disallowed unless the - ** SQL is being compiled using sqlite3NestedParse() or - ** the SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test-control has be -- ** used to activate internal functionsn for testing purposes */ -+ ** used to activate internal functions for testing purposes */ - no_such_func = 1; - pDef = 0; - }else - if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 - && !IN_RENAME_OBJECT - ){ -+ if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL); - sqlite3ExprFunctionUsable(pParse, pExpr, pDef); - } - } -@@ -98686,9 +109270,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - ); - if( pDef && pDef->xValue==0 && pWin ){ - sqlite3ErrorMsg(pParse, -- "%.*s() may not be used as a window function", nId, zId -+ "%#T() may not be used as a window function", pExpr - ); -- pNC->nErr++; -+ pNC->nNcErr++; - }else if( - (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) - || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin) -@@ -98700,14 +109284,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - }else{ - zType = "aggregate"; - } -- sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId); -- pNC->nErr++; -+ sqlite3ErrorMsg(pParse, "misuse of %s function %#T()",zType,pExpr); -+ pNC->nNcErr++; - is_agg = 0; - } - #else - if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){ -- sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId); -- pNC->nErr++; -+ sqlite3ErrorMsg(pParse,"misuse of aggregate function %#T()",pExpr); -+ pNC->nNcErr++; - is_agg = 0; - } - #endif -@@ -98716,22 +109300,26 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - && pParse->explain==0 - #endif - ){ -- sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); -- pNC->nErr++; -+ sqlite3ErrorMsg(pParse, "no such function: %#T", pExpr); -+ pNC->nNcErr++; - }else if( wrong_num_args ){ -- sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", -- nId, zId); -- pNC->nErr++; -+ sqlite3ErrorMsg(pParse,"wrong number of arguments to function %#T()", -+ pExpr); -+ pNC->nNcErr++; - } - #ifndef SQLITE_OMIT_WINDOWFUNC - else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){ - sqlite3ErrorMsg(pParse, -- "FILTER may not be used with non-aggregate %.*s()", -- nId, zId -+ "FILTER may not be used with non-aggregate %#T()", -+ pExpr - ); -- pNC->nErr++; -+ pNC->nNcErr++; - } - #endif -+ else if( is_agg==0 && pExpr->pLeft ){ -+ sqlite3ExprOrderByAggregateError(pParse, pExpr); -+ pNC->nNcErr++; -+ } - if( is_agg ){ - /* Window functions may not be arguments of aggregate functions. - ** Or arguments of other window functions. But aggregate functions -@@ -98743,19 +109331,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - #endif - } - } --#ifndef SQLITE_OMIT_WINDOWFUNC -- else if( ExprHasProperty(pExpr, EP_WinFunc) ){ -+ else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){ - is_agg = 1; - } --#endif - sqlite3WalkExprList(pWalker, pList); - if( is_agg ){ -+ if( pExpr->pLeft ){ -+ assert( pExpr->pLeft->op==TK_ORDER ); -+ assert( ExprUseXList(pExpr->pLeft) ); -+ sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); -+ } - #ifndef SQLITE_OMIT_WINDOWFUNC -- if( pWin ){ -+ if( pWin && pParse->nErr==0 ){ - Select *pSel = pNC->pWinSelect; -- assert( pWin==pExpr->y.pWin ); -+ assert( ExprUseYWin(pExpr) && pWin==pExpr->y.pWin ); - if( IN_RENAME_OBJECT==0 ){ - sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef); -+ if( pParse->db->mallocFailed ) break; - } - sqlite3WalkExprList(pWalker, pWin->pPartition); - sqlite3WalkExprList(pWalker, pWin->pOrderBy); -@@ -98765,7 +109357,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - }else - #endif /* SQLITE_OMIT_WINDOWFUNC */ - { -- NameContext *pNC2 = pNC; -+ NameContext *pNC2; /* For looping up thru outer contexts */ - pExpr->op = TK_AGG_FUNCTION; - pExpr->op2 = 0; - #ifndef SQLITE_OMIT_WINDOWFUNC -@@ -98773,16 +109365,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); - } - #endif -- while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ -- pExpr->op2++; -+ pNC2 = pNC; -+ while( pNC2 -+ && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 -+ ){ -+ pExpr->op2 += (1 + pNC2->nNestedSelect); - pNC2 = pNC2->pNext; - } - assert( pDef!=0 || IN_RENAME_OBJECT ); - if( pNC2 && pDef ){ -+ pExpr->op2 += pNC2->nNestedSelect; - assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); -+ assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg ); - testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); -- pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); -- -+ testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 ); -+ pNC2->ncFlags |= NC_HasAgg -+ | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER) -+ & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER)); - } - } - pNC->ncFlags |= savedAllowFlags; -@@ -98798,20 +109397,24 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - #endif - case TK_IN: { - testcase( pExpr->op==TK_IN ); -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - int nRef = pNC->nRef; - testcase( pNC->ncFlags & NC_IsCheck ); - testcase( pNC->ncFlags & NC_PartIdx ); - testcase( pNC->ncFlags & NC_IdxExpr ); - testcase( pNC->ncFlags & NC_GenCol ); -- sqlite3ResolveNotValid(pParse, pNC, "subqueries", -- NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); -- sqlite3WalkSelect(pWalker, pExpr->x.pSelect); -+ assert( pExpr->x.pSelect ); -+ if( pNC->ncFlags & NC_SelfRef ){ -+ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); -+ }else{ -+ sqlite3WalkSelect(pWalker, pExpr->x.pSelect); -+ } - assert( pNC->nRef>=nRef ); - if( nRef!=pNC->nRef ){ - ExprSetProperty(pExpr, EP_VarSelect); -- pNC->ncFlags |= NC_VarSelect; -+ pExpr->x.pSelect->selFlags |= SF_Correlated; - } -+ pNC->ncFlags |= NC_Subquery; - } - break; - } -@@ -98821,7 +109424,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - testcase( pNC->ncFlags & NC_IdxExpr ); - testcase( pNC->ncFlags & NC_GenCol ); - sqlite3ResolveNotValid(pParse, pNC, "parameters", -- NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr); -+ NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr, pExpr); - break; - } - case TK_IS: -@@ -98830,7 +109433,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - assert( !ExprHasProperty(pExpr, EP_Reduced) ); - /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE", - ** and "x IS NOT FALSE". */ -- if( pRight && pRight->op==TK_ID ){ -+ if( ALWAYS(pRight) && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){ - int rc = resolveExprStep(pWalker, pRight); - if( rc==WRC_Abort ) return WRC_Abort; - if( pRight->op==TK_TRUEFALSE ){ -@@ -98853,6 +109456,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - assert( pExpr->pLeft!=0 ); - nLeft = sqlite3ExprVectorSize(pExpr->pLeft); - if( pExpr->op==TK_BETWEEN ){ -+ assert( ExprUseXList(pExpr) ); - nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[0].pExpr); - if( nRight==nLeft ){ - nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[1].pExpr); -@@ -98872,11 +109476,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ - testcase( pExpr->op==TK_ISNOT ); - testcase( pExpr->op==TK_BETWEEN ); - sqlite3ErrorMsg(pParse, "row value misused"); -+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); - } - break; - } - } -- return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; -+ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); -+ return pParse->nErr ? WRC_Abort : WRC_Continue; - } - - /* -@@ -98901,9 +109507,11 @@ static int resolveAsName( - UNUSED_PARAMETER(pParse); - - if( pE->op==TK_ID ){ -- char *zCol = pE->u.zToken; -+ const char *zCol; -+ assert( !ExprHasProperty(pE, EP_IntValue) ); -+ zCol = pE->u.zToken; - for(i=0; inExpr; i++){ -- if( pEList->a[i].eEName==ENAME_NAME -+ if( pEList->a[i].fg.eEName==ENAME_NAME - && sqlite3_stricmp(pEList->a[i].zEName, zCol)==0 - ){ - return i+1; -@@ -98943,7 +109551,7 @@ static int resolveOrderByTermToExprList( - int rc; /* Return code from subprocedures */ - u8 savedSuppErr; /* Saved value of db->suppressErr */ - -- assert( sqlite3ExprIsInteger(pE, &i)==0 ); -+ assert( sqlite3ExprIsInteger(pE, &i, 0)==0 ); - pEList = pSelect->pEList; - - /* Resolve all names in the ORDER BY term expression -@@ -98952,11 +109560,11 @@ static int resolveOrderByTermToExprList( - nc.pParse = pParse; - nc.pSrcList = pSelect->pSrc; - nc.uNC.pEList = pEList; -- nc.ncFlags = NC_AllowAgg|NC_UEList; -- nc.nErr = 0; -+ nc.ncFlags = NC_AllowAgg|NC_UEList|NC_NoSelect; -+ nc.nNcErr = 0; - db = pParse->db; - savedSuppErr = db->suppressErr; -- if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1; -+ db->suppressErr = 1; - rc = sqlite3ResolveExprNames(&nc, pE); - db->suppressErr = savedSuppErr; - if( rc ) return 0; -@@ -98982,11 +109590,13 @@ static void resolveOutOfRangeError( - Parse *pParse, /* The error context into which to write the error */ - const char *zType, /* "ORDER" or "GROUP" */ - int i, /* The index (1-based) of the term out of range */ -- int mx /* Largest permissible value of i */ -+ int mx, /* Largest permissible value of i */ -+ Expr *pError /* Associate the error with the expression */ - ){ - sqlite3ErrorMsg(pParse, - "%r %s BY term out of range - should be " - "between 1 and %d", i, zType, mx); -+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pError); - } - - /* -@@ -99022,7 +109632,7 @@ static int resolveCompoundOrderBy( - return 1; - } - for(i=0; inExpr; i++){ -- pOrderBy->a[i].done = 0; -+ pOrderBy->a[i].fg.done = 0; - } - pSelect->pNext = 0; - while( pSelect->pPrior ){ -@@ -99037,11 +109647,12 @@ static int resolveCompoundOrderBy( - for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - int iCol = -1; - Expr *pE, *pDup; -- if( pItem->done ) continue; -+ if( pItem->fg.done ) continue; - pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); -- if( sqlite3ExprIsInteger(pE, &iCol) ){ -+ if( NEVER(pE==0) ) continue; -+ if( sqlite3ExprIsInteger(pE, &iCol, 0) ){ - if( iCol<=0 || iCol>pEList->nExpr ){ -- resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); -+ resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr, pE); - return 1; - } - }else{ -@@ -99054,29 +109665,24 @@ static int resolveCompoundOrderBy( - ** Once the comparisons are finished, the duplicate expression - ** is deleted. - ** -- ** Or, if this is running as part of an ALTER TABLE operation, -- ** resolve the symbols in the actual expression, not a duplicate. -- ** And, if one of the comparisons is successful, leave the expression -- ** as is instead of transforming it to an integer as in the usual -- ** case. This allows the code in alter.c to modify column -- ** refererences within the ORDER BY expression as required. */ -- if( IN_RENAME_OBJECT ){ -- pDup = pE; -- }else{ -- pDup = sqlite3ExprDup(db, pE, 0); -- } -+ ** If this is running as part of an ALTER TABLE operation and -+ ** the symbols resolve successfully, also resolve the symbols in the -+ ** actual expression. This allows the code in alter.c to modify -+ ** column references within the ORDER BY expression as required. */ -+ pDup = sqlite3ExprDup(db, pE, 0); - if( !db->mallocFailed ){ - assert(pDup); - iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); -+ if( IN_RENAME_OBJECT && iCol>0 ){ -+ resolveOrderByTermToExprList(pParse, pSelect, pE); -+ } - } -- if( !IN_RENAME_OBJECT ){ -- sqlite3ExprDelete(db, pDup); -- } -+ sqlite3ExprDelete(db, pDup); - } - } - if( iCol>0 ){ - /* Convert the ORDER BY term into an integer column number iCol, -- ** taking care to preserve the COLLATE clause if it exists */ -+ ** taking care to preserve the COLLATE clause if it exists. */ - if( !IN_RENAME_OBJECT ){ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); - if( pNew==0 ) return 1; -@@ -99094,7 +109700,7 @@ static int resolveCompoundOrderBy( - sqlite3ExprDelete(db, pE); - pItem->u.x.iOrderByCol = (u16)iCol; - } -- pItem->done = 1; -+ pItem->fg.done = 1; - }else{ - moreToDo = 1; - } -@@ -99102,7 +109708,7 @@ static int resolveCompoundOrderBy( - pSelect = pSelect->pNext; - } - for(i=0; inExpr; i++){ -- if( pOrderBy->a[i].done==0 ){ -+ if( pOrderBy->a[i].fg.done==0 ){ - sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any " - "column in the result set", i+1); - return 1; -@@ -99142,11 +109748,10 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( - for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - if( pItem->u.x.iOrderByCol ){ - if( pItem->u.x.iOrderByCol>pEList->nExpr ){ -- resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); -+ resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr, 0); - return 1; - } -- resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, -- zType,0); -+ resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0); - } - } - return 0; -@@ -99212,12 +109817,13 @@ static int resolveOrderGroupBy( - Parse *pParse; /* Parsing context */ - int nResult; /* Number of terms in the result set */ - -- if( pOrderBy==0 ) return 0; -+ assert( pOrderBy!=0 ); - nResult = pSelect->pEList->nExpr; - pParse = pNC->pParse; - for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - Expr *pE = pItem->pExpr; - Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); -+ if( NEVER(pE2==0) ) continue; - if( zType[0]!='G' ){ - iCol = resolveAsName(pParse, pSelect->pEList, pE2); - if( iCol>0 ){ -@@ -99229,12 +109835,12 @@ static int resolveOrderGroupBy( - continue; - } - } -- if( sqlite3ExprIsInteger(pE2, &iCol) ){ -+ if( sqlite3ExprIsInteger(pE2, &iCol, 0) ){ - /* The ORDER BY term is an integer constant. Again, set the column - ** number so that sqlite3ResolveOrderGroupBy() will convert the - ** order-by term to a copy of the result-set expression */ - if( iCol<1 || iCol>0xffff ){ -- resolveOutOfRangeError(pParse, zType, i+1, nResult); -+ resolveOutOfRangeError(pParse, zType, i+1, nResult, pE2); - return 1; - } - pItem->u.x.iOrderByCol = (u16)iCol; -@@ -99248,7 +109854,7 @@ static int resolveOrderGroupBy( - } - for(j=0; jpEList->nExpr; j++){ - if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ -- /* Since this expresion is being changed into a reference -+ /* Since this expression is being changed into a reference - ** to an identical expression in the result set, remove all Window - ** objects belonging to the expression from the Select.pWin list. */ - windowRemoveExprFromSelect(pSelect, pE); -@@ -99292,7 +109898,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - */ - if( (p->selFlags & SF_Expanded)==0 ){ - sqlite3SelectPrep(pParse, p, pOuterNC); -- return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune; -+ return pParse->nErr ? WRC_Abort : WRC_Prune; - } - - isCompound = p->pPrior!=0; -@@ -99320,39 +109926,51 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - ** moves the pOrderBy down to the sub-query. It will be moved back - ** after the names have been resolved. */ - if( p->selFlags & SF_Converted ){ -- Select *pSub = p->pSrc->a[0].pSelect; -+ Select *pSub; -+ assert( p->pSrc->a[0].fg.isSubquery ); -+ assert( p->pSrc->a[0].u4.pSubq!=0 ); -+ pSub = p->pSrc->a[0].u4.pSubq->pSelect; -+ assert( pSub!=0 ); - assert( p->pSrc->nSrc==1 && p->pOrderBy ); - assert( pSub->pPrior && pSub->pOrderBy==0 ); - pSub->pOrderBy = p->pOrderBy; - p->pOrderBy = 0; - } - -- /* Recursively resolve names in all subqueries -+ /* Recursively resolve names in all subqueries in the FROM clause - */ -+ if( pOuterNC ) pOuterNC->nNestedSelect++; - for(i=0; ipSrc->nSrc; i++){ -- struct SrcList_item *pItem = &p->pSrc->a[i]; -- if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ -- NameContext *pNC; /* Used to iterate name contexts */ -- int nRef = 0; /* Refcount for pOuterNC and outer contexts */ -+ SrcItem *pItem = &p->pSrc->a[i]; -+ assert( pItem->zName!=0 -+ || pItem->fg.isSubquery ); /* Test of tag-20240424-1*/ -+ if( pItem->fg.isSubquery -+ && (pItem->u4.pSubq->pSelect->selFlags & SF_Resolved)==0 -+ ){ -+ int nRef = pOuterNC ? pOuterNC->nRef : 0; - const char *zSavedContext = pParse->zAuthContext; - -- /* Count the total number of references to pOuterNC and all of its -- ** parent contexts. After resolving references to expressions in -- ** pItem->pSelect, check if this value has changed. If so, then -- ** SELECT statement pItem->pSelect must be correlated. Set the -- ** pItem->fg.isCorrelated flag if this is the case. */ -- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef; -- - if( pItem->zName ) pParse->zAuthContext = pItem->zName; -- sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); -+ sqlite3ResolveSelectNames(pParse, pItem->u4.pSubq->pSelect, pOuterNC); - pParse->zAuthContext = zSavedContext; -- if( pParse->nErr || db->mallocFailed ) return WRC_Abort; -+ if( pParse->nErr ) return WRC_Abort; -+ assert( db->mallocFailed==0 ); - -- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef; -- assert( pItem->fg.isCorrelated==0 && nRef<=0 ); -- pItem->fg.isCorrelated = (nRef!=0); -+ /* If the number of references to the outer context changed when -+ ** expressions in the sub-select were resolved, the sub-select -+ ** is correlated. It is not required to check the refcount on any -+ ** but the innermost outer context object, as lookupName() increments -+ ** the refcount on all contexts between the current one and the -+ ** context containing the column when it resolves a name. */ -+ if( pOuterNC ){ -+ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef ); -+ pItem->fg.isCorrelated = (pOuterNC->nRef>nRef); -+ } - } - } -+ if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){ -+ pOuterNC->nNestedSelect--; -+ } - - /* Set up the local name-context to pass to sqlite3ResolveExprNames() to - ** resolve the result-set expression list. -@@ -99372,18 +109990,12 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - pGroupBy = p->pGroupBy; - if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){ - assert( NC_MinMaxAgg==SF_MinMaxAgg ); -- p->selFlags |= SF_Aggregate | (sNC.ncFlags&NC_MinMaxAgg); -+ assert( NC_OrderAgg==SF_OrderByReqd ); -+ p->selFlags |= SF_Aggregate | (sNC.ncFlags&(NC_MinMaxAgg|NC_OrderAgg)); - }else{ - sNC.ncFlags &= ~NC_AllowAgg; - } - -- /* If a HAVING clause is present, then there must be a GROUP BY clause. -- */ -- if( p->pHaving && !pGroupBy ){ -- sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); -- return WRC_Abort; -- } -- - /* Add the output column list to the name-context before parsing the - ** other expressions in the SELECT statement. This is so that - ** expressions in the WHERE clause (etc.) can refer to expressions by -@@ -99392,15 +110004,23 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - ** Minor point: If this is the case, then the expression will be - ** re-evaluated for each reference to it. - */ -- assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 ); -+ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 ); - sNC.uNC.pEList = p->pEList; - sNC.ncFlags |= NC_UEList; -- if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; -+ if( p->pHaving ){ -+ if( (p->selFlags & SF_Aggregate)==0 ){ -+ sqlite3ErrorMsg(pParse, "HAVING clause on a non-aggregate query"); -+ return WRC_Abort; -+ } -+ if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; -+ } -+ sNC.ncFlags |= NC_Where; - if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; -+ sNC.ncFlags &= ~NC_Where; - - /* Resolve names in table-valued-function arguments */ - for(i=0; ipSrc->nSrc; i++){ -- struct SrcList_item *pItem = &p->pSrc->a[i]; -+ SrcItem *pItem = &p->pSrc->a[i]; - if( pItem->fg.isTabFunc - && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg) - ){ -@@ -99408,6 +110028,19 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - } - } - -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ if( IN_RENAME_OBJECT ){ -+ Window *pWin; -+ for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ -+ if( sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) -+ || sqlite3ResolveExprListNames(&sNC, pWin->pPartition) -+ ){ -+ return WRC_Abort; -+ } -+ } -+ } -+#endif -+ - /* The ORDER BY and GROUP BY clauses may not refer to terms in - ** outer queries - */ -@@ -99420,7 +110053,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - ** These integers will be replaced by copies of the corresponding result - ** set expressions by the call to resolveOrderGroupBy() below. */ - if( p->selFlags & SF_Converted ){ -- Select *pSub = p->pSrc->a[0].pSelect; -+ Select *pSub; -+ assert( p->pSrc->a[0].fg.isSubquery ); -+ pSub = p->pSrc->a[0].u4.pSubq->pSelect; -+ assert( pSub!=0 ); - p->pOrderBy = pSub->pOrderBy; - pSub->pOrderBy = 0; - } -@@ -99435,7 +110071,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - ** is not detected until much later, and so we need to go ahead and - ** resolve those symbols on the incorrect ORDER BY for consistency. - */ -- if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ -+ if( p->pOrderBy!=0 -+ && isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ - && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") - ){ - return WRC_Abort; -@@ -99463,19 +110100,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ - } - } - --#ifndef SQLITE_OMIT_WINDOWFUNC -- if( IN_RENAME_OBJECT ){ -- Window *pWin; -- for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ -- if( sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) -- || sqlite3ResolveExprListNames(&sNC, pWin->pPartition) -- ){ -- return WRC_Abort; -- } -- } -- } --#endif -- - /* If this is part of a compound SELECT, check that it has the right - ** number of expressions in the select list. */ - if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){ -@@ -99555,11 +110179,11 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( - Walker w; - - if( pExpr==0 ) return SQLITE_OK; -- savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); -- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); -+ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); -+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); - w.pParse = pNC->pParse; - w.xExprCallback = resolveExprStep; -- w.xSelectCallback = resolveSelectStep; -+ w.xSelectCallback = (pNC->ncFlags & NC_NoSelect) ? 0 : resolveSelectStep; - w.xSelectCallback2 = 0; - w.u.pNC = pNC; - #if SQLITE_MAX_EXPR_DEPTH>0 -@@ -99568,7 +110192,8 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( - return SQLITE_ERROR; - } - #endif -- sqlite3WalkExpr(&w, pExpr); -+ assert( pExpr!=0 ); -+ sqlite3WalkExprNN(&w, pExpr); - #if SQLITE_MAX_EXPR_DEPTH>0 - w.pParse->nHeight -= pExpr->nHeight; - #endif -@@ -99578,13 +110203,16 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames( - testcase( pNC->ncFlags & NC_HasWin ); - ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); - pNC->ncFlags |= savedHasAgg; -- return pNC->nErr>0 || w.pParse->nErr>0; -+ return pNC->nNcErr>0 || w.pParse->nErr>0; - } - - /* - ** Resolve all names for all expression in an expression list. This is - ** just like sqlite3ResolveExprNames() except that it works for an expression - ** list rather than a single expression. -+** -+** The return value is SQLITE_OK (0) for success or SQLITE_ERROR (1) for a -+** failure. - */ - SQLITE_PRIVATE int sqlite3ResolveExprListNames( - NameContext *pNC, /* Namespace to resolve expressions in. */ -@@ -99593,24 +110221,24 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames( - int i; - int savedHasAgg = 0; - Walker w; -- if( pList==0 ) return WRC_Continue; -+ if( pList==0 ) return SQLITE_OK; - w.pParse = pNC->pParse; - w.xExprCallback = resolveExprStep; - w.xSelectCallback = resolveSelectStep; - w.xSelectCallback2 = 0; - w.u.pNC = pNC; -- savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); -- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); -+ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); -+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); - for(i=0; inExpr; i++){ - Expr *pExpr = pList->a[i].pExpr; - if( pExpr==0 ) continue; - #if SQLITE_MAX_EXPR_DEPTH>0 - w.pParse->nHeight += pExpr->nHeight; - if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){ -- return WRC_Abort; -+ return SQLITE_ERROR; - } - #endif -- sqlite3WalkExpr(&w, pExpr); -+ sqlite3WalkExprNN(&w, pExpr); - #if SQLITE_MAX_EXPR_DEPTH>0 - w.pParse->nHeight -= pExpr->nHeight; - #endif -@@ -99618,20 +110246,21 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames( - assert( EP_Win==NC_HasWin ); - testcase( pNC->ncFlags & NC_HasAgg ); - testcase( pNC->ncFlags & NC_HasWin ); -- if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){ -+ if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg) ){ - ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); -- savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); -- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); -+ savedHasAgg |= pNC->ncFlags & -+ (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); -+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg); - } -- if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort; -+ if( w.pParse->nErr>0 ) return SQLITE_ERROR; - } - pNC->ncFlags |= savedHasAgg; -- return WRC_Continue; -+ return SQLITE_OK; - } - - /* - ** Resolve all names in all expressions of a SELECT and in all --** decendents of the SELECT, including compounds off of p->pPrior, -+** descendants of the SELECT, including compounds off of p->pPrior, - ** subqueries in expressions, and subqueries used as FROM clause - ** terms. - ** -@@ -99682,20 +110311,22 @@ SQLITE_PRIVATE int sqlite3ResolveSelfReference( - Expr *pExpr, /* Expression to resolve. May be NULL. */ - ExprList *pList /* Expression list to resolve. May be NULL. */ - ){ -- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ -+ SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */ - NameContext sNC; /* Name context for pParse->pNewTable */ - int rc; -+ u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */ - - assert( type==0 || pTab!=0 ); - assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr - || type==NC_GenCol || pTab==0 ); - memset(&sNC, 0, sizeof(sNC)); -- memset(&sSrc, 0, sizeof(sSrc)); -+ pSrc = (SrcList*)srcSpace; -+ memset(pSrc, 0, SZ_SRCLIST_1); - if( pTab ){ -- sSrc.nSrc = 1; -- sSrc.a[0].zName = pTab->zName; -- sSrc.a[0].pTab = pTab; -- sSrc.a[0].iCursor = -1; -+ pSrc->nSrc = 1; -+ pSrc->a[0].zName = pTab->zName; -+ pSrc->a[0].pSTab = pTab; -+ pSrc->a[0].iCursor = -1; - if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ - /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP - ** schema elements */ -@@ -99703,7 +110334,7 @@ SQLITE_PRIVATE int sqlite3ResolveSelfReference( - } - } - sNC.pParse = pParse; -- sNC.pSrcList = &sSrc; -+ sNC.pSrcList = pSrc; - sNC.ncFlags = type | NC_IsDDL; - if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; - if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); -@@ -99735,9 +110366,9 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree); - /* - ** Return the affinity character for a single column of a table. - */ --SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){ -- assert( iColnCol ); -- return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER; -+SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table *pTab, int iCol){ -+ if( iCol<0 || NEVER(iCol>=pTab->nCol) ) return SQLITE_AFF_INTEGER; -+ return pTab->aCol[iCol].affinity; - } - - /* -@@ -99758,41 +110389,126 @@ SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){ - */ - SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ - int op; -- while( ExprHasProperty(pExpr, EP_Skip) ){ -- assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); -- pExpr = pExpr->pLeft; -- assert( pExpr!=0 ); -- } - op = pExpr->op; -- if( op==TK_SELECT ){ -- assert( pExpr->flags&EP_xIsSelect ); -- assert( pExpr->x.pSelect!=0 ); -- assert( pExpr->x.pSelect->pEList!=0 ); -- assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 ); -- return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); -- } -- if( op==TK_REGISTER ) op = pExpr->op2; -+ while( 1 /* exit-by-break */ ){ -+ if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){ -+ assert( ExprUseYTab(pExpr) ); -+ assert( pExpr->y.pTab!=0 ); -+ return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); -+ } -+ if( op==TK_SELECT ){ -+ assert( ExprUseXSelect(pExpr) ); -+ assert( pExpr->x.pSelect!=0 ); -+ assert( pExpr->x.pSelect->pEList!=0 ); -+ assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 ); -+ return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); -+ } - #ifndef SQLITE_OMIT_CAST -- if( op==TK_CAST ){ -- assert( !ExprHasProperty(pExpr, EP_IntValue) ); -- return sqlite3AffinityType(pExpr->u.zToken, 0); -- } -+ if( op==TK_CAST ){ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ return sqlite3AffinityType(pExpr->u.zToken, 0); -+ } - #endif -- if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->y.pTab ){ -- return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); -- } -- if( op==TK_SELECT_COLUMN ){ -- assert( pExpr->pLeft->flags&EP_xIsSelect ); -- return sqlite3ExprAffinity( -- pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr -- ); -- } -- if( op==TK_VECTOR ){ -- return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); -+ if( op==TK_SELECT_COLUMN ){ -+ assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) ); -+ assert( pExpr->iColumn < pExpr->iTable ); -+ assert( pExpr->iColumn >= 0 ); -+ assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr ); -+ return sqlite3ExprAffinity( -+ pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr -+ ); -+ } -+ if( op==TK_VECTOR -+ || (op==TK_FUNCTION && pExpr->affExpr==SQLITE_AFF_DEFER) -+ ){ -+ assert( ExprUseXList(pExpr) ); -+ return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr); -+ } -+ if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){ -+ assert( pExpr->op==TK_COLLATE -+ || pExpr->op==TK_IF_NULL_ROW -+ || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) ); -+ pExpr = pExpr->pLeft; -+ op = pExpr->op; -+ continue; -+ } -+ if( op!=TK_REGISTER ) break; -+ op = pExpr->op2; -+ if( NEVER( op==TK_REGISTER ) ) break; - } - return pExpr->affExpr; - } - -+/* -+** Make a guess at all the possible datatypes of the result that could -+** be returned by an expression. Return a bitmask indicating the answer: -+** -+** 0x01 Numeric -+** 0x02 Text -+** 0x04 Blob -+** -+** If the expression must return NULL, then 0x00 is returned. -+*/ -+SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr){ -+ while( pExpr ){ -+ switch( pExpr->op ){ -+ case TK_COLLATE: -+ case TK_IF_NULL_ROW: -+ case TK_UPLUS: { -+ pExpr = pExpr->pLeft; -+ break; -+ } -+ case TK_NULL: { -+ pExpr = 0; -+ break; -+ } -+ case TK_STRING: { -+ return 0x02; -+ } -+ case TK_BLOB: { -+ return 0x04; -+ } -+ case TK_CONCAT: { -+ return 0x06; -+ } -+ case TK_VARIABLE: -+ case TK_AGG_FUNCTION: -+ case TK_FUNCTION: { -+ return 0x07; -+ } -+ case TK_COLUMN: -+ case TK_AGG_COLUMN: -+ case TK_SELECT: -+ case TK_CAST: -+ case TK_SELECT_COLUMN: -+ case TK_VECTOR: { -+ int aff = sqlite3ExprAffinity(pExpr); -+ if( aff>=SQLITE_AFF_NUMERIC ) return 0x05; -+ if( aff==SQLITE_AFF_TEXT ) return 0x06; -+ return 0x07; -+ } -+ case TK_CASE: { -+ int res = 0; -+ int ii; -+ ExprList *pList = pExpr->x.pList; -+ assert( ExprUseXList(pExpr) && pList!=0 ); -+ assert( pList->nExpr > 0); -+ for(ii=1; iinExpr; ii+=2){ -+ res |= sqlite3ExprDataType(pList->a[ii].pExpr); -+ } -+ if( pList->nExpr % 2 ){ -+ res |= sqlite3ExprDataType(pList->a[pList->nExpr-1].pExpr); -+ } -+ return res; -+ } -+ default: { -+ return 0x01; -+ } -+ } /* End of switch(op) */ -+ } /* End of while(pExpr) */ -+ return 0x00; -+} -+ - /* - ** Set the collating sequence for expression pExpr to be the collating - ** sequence named by pToken. Return a pointer to a new Expr node that -@@ -99802,7 +110518,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ - ** and the pExpr parameter is returned unchanged. - */ - SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken( -- Parse *pParse, /* Parsing context */ -+ const Parse *pParse, /* Parsing context */ - Expr *pExpr, /* Add the "COLLATE" clause to this expression */ - const Token *pCollName, /* Name of collating sequence */ - int dequote /* True to dequote pCollName */ -@@ -99817,7 +110533,11 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken( - } - return pExpr; - } --SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){ -+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString( -+ const Parse *pParse, /* Parsing context */ -+ Expr *pExpr, /* Add the "COLLATE" clause to this expression */ -+ const char *zC /* The collating sequence name */ -+){ - Token s; - assert( zC!=0 ); - sqlite3TokenInit(&s, (char*)zC); -@@ -99829,7 +110549,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con - */ - SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ - while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){ -- assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); -+ assert( pExpr->op==TK_COLLATE ); - pExpr = pExpr->pLeft; - } - return pExpr; -@@ -99843,13 +110563,14 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){ - SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){ - while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){ - if( ExprHasProperty(pExpr, EP_Unlikely) ){ -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); -+ assert( ExprUseXList(pExpr) ); - assert( pExpr->x.pList->nExpr>0 ); - assert( pExpr->op==TK_FUNCTION ); - pExpr = pExpr->x.pList->a[0].pExpr; -- }else{ -- assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW ); -+ }else if( pExpr->op==TK_COLLATE ){ - pExpr = pExpr->pLeft; -+ }else{ -+ break; - } - } - return pExpr; -@@ -99876,14 +110597,14 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ - while( p ){ - int op = p->op; - if( op==TK_REGISTER ) op = p->op2; -- if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER) -- && p->y.pTab!=0 -+ if( (op==TK_AGG_COLUMN && p->y.pTab!=0) -+ || op==TK_COLUMN || op==TK_TRIGGER - ){ -- /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally -- ** a TK_COLUMN but was previously evaluated and cached in a register */ -- int j = p->iColumn; -- if( j>=0 ){ -- const char *zColl = p->y.pTab->aCol[j].zColl; -+ int j; -+ assert( ExprUseYTab(p) ); -+ assert( p->y.pTab!=0 ); -+ if( (j = p->iColumn)>=0 ){ -+ const char *zColl = sqlite3ColumnColl(&p->y.pTab->aCol[j]); - pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); - } - break; -@@ -99892,11 +110613,15 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ - p = p->pLeft; - continue; - } -- if( op==TK_VECTOR ){ -+ if( op==TK_VECTOR -+ || (op==TK_FUNCTION && p->affExpr==SQLITE_AFF_DEFER) -+ ){ -+ assert( ExprUseXList(p) ); - p = p->x.pList->a[0].pExpr; - continue; - } - if( op==TK_COLLATE ){ -+ assert( !ExprHasProperty(p, EP_IntValue) ); - pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); - break; - } -@@ -99906,13 +110631,10 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ - }else{ - Expr *pNext = p->pRight; - /* The Expr.x union is never used at the same time as Expr.pRight */ -- assert( p->x.pList==0 || p->pRight==0 ); -- if( p->x.pList!=0 -- && !db->mallocFailed -- && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) -- ){ -+ assert( !ExprUseXList(p) || p->x.pList==0 || p->pRight==0 ); -+ if( ExprUseXList(p) && p->x.pList!=0 && !db->mallocFailed ){ - int i; -- for(i=0; ALWAYS(ix.pList->nExpr); i++){ -+ for(i=0; ix.pList->nExpr; i++){ - if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ - pNext = p->x.pList->a[i].pExpr; - break; -@@ -99934,7 +110656,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ - /* - ** Return the collation sequence for the expression pExpr. If - ** there is no defined collating sequence, return a pointer to the --** defautl collation sequence. -+** default collation sequence. - ** - ** See also: sqlite3ExprCollSeq() - ** -@@ -99993,7 +110715,7 @@ static char comparisonAffinity(const Expr *pExpr){ - aff = sqlite3ExprAffinity(pExpr->pLeft); - if( pExpr->pRight ){ - aff = sqlite3CompareAffinity(pExpr->pRight, aff); -- }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ }else if( ExprUseXSelect(pExpr) ){ - aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); - }else if( aff==0 ){ - aff = SQLITE_AFF_BLOB; -@@ -100064,7 +110786,7 @@ SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq( - return pColl; - } - --/* Expresssion p is a comparison operator. Return a collation sequence -+/* Expression p is a comparison operator. Return a collation sequence - ** appropriate for the comparison operator. - ** - ** This is normally just a wrapper around sqlite3BinaryCompareCollSeq(). -@@ -100106,7 +110828,7 @@ static int codeCompare( - p5 = binaryCompareP5(pLeft, pRight, jumpIfNull); - addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1, - (void*)p4, P4_COLLSEQ); -- sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5); -+ sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5); - return addr; - } - -@@ -100119,7 +110841,7 @@ static int codeCompare( - ** But a TK_SELECT might be either a vector or a scalar. It is only - ** considered a vector if it has two or more result columns. - */ --SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr){ -+SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr){ - return sqlite3ExprVectorSize(pExpr)>1; - } - -@@ -100129,12 +110851,14 @@ SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr){ - ** is a sub-select, return the number of columns in the sub-select. For - ** any other type of expression, return 1. - */ --SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){ -+SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr){ - u8 op = pExpr->op; - if( op==TK_REGISTER ) op = pExpr->op2; - if( op==TK_VECTOR ){ -+ assert( ExprUseXList(pExpr) ); - return pExpr->x.pList->nExpr; - }else if( op==TK_SELECT ){ -+ assert( ExprUseXSelect(pExpr) ); - return pExpr->x.pSelect->pEList->nExpr; - }else{ - return 1; -@@ -100157,12 +110881,14 @@ SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){ - ** been positioned. - */ - SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ -- assert( iop==TK_ERROR ); - if( sqlite3ExprIsVector(pVector) ){ - assert( pVector->op2==0 || pVector->op==TK_REGISTER ); - if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){ -+ assert( ExprUseXSelect(pVector) ); - return pVector->x.pSelect->pEList->a[i].pExpr; - }else{ -+ assert( ExprUseXList(pVector) ); - return pVector->x.pList->a[i].pExpr; - } - } -@@ -100193,11 +110919,12 @@ SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){ - SQLITE_PRIVATE Expr *sqlite3ExprForVectorField( - Parse *pParse, /* Parsing context */ - Expr *pVector, /* The vector. List of expressions or a sub-SELECT */ -- int iField /* Which column of the vector to return */ -+ int iField, /* Which column of the vector to return */ -+ int nField /* Total number of columns in the vector */ - ){ - Expr *pRet; - if( pVector->op==TK_SELECT ){ -- assert( pVector->flags & EP_xIsSelect ); -+ assert( ExprUseXSelect(pVector) ); - /* The TK_SELECT_COLUMN Expr node: - ** - ** pLeft: pVector containing TK_SELECT. Not deleted. -@@ -100216,14 +110943,24 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField( - */ - pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0); - if( pRet ){ -+ ExprSetProperty(pRet, EP_FullSize); -+ pRet->iTable = nField; - pRet->iColumn = iField; - pRet->pLeft = pVector; - } -- assert( pRet==0 || pRet->iTable==0 ); - }else{ -- if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; -+ if( pVector->op==TK_VECTOR ){ -+ Expr **ppVector; -+ assert( ExprUseXList(pVector) ); -+ ppVector = &pVector->x.pList->a[iField].pExpr; -+ pVector = *ppVector; -+ if( IN_RENAME_OBJECT ){ -+ /* This must be a vector UPDATE inside a trigger */ -+ *ppVector = 0; -+ return pVector; -+ } -+ } - pRet = sqlite3ExprDup(pParse->db, pVector, 0); -- sqlite3RenameTokenRemap(pParse, pRet, pVector); - } - return pRet; - } -@@ -100273,17 +111010,22 @@ static int exprVectorRegister( - int *pRegFree /* OUT: Temp register to free */ - ){ - u8 op = pVector->op; -- assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT ); -+ assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT || op==TK_ERROR ); - if( op==TK_REGISTER ){ - *ppExpr = sqlite3VectorFieldSubexpr(pVector, iField); - return pVector->iTable+iField; - } - if( op==TK_SELECT ){ -+ assert( ExprUseXSelect(pVector) ); - *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr; - return regSelect+iField; - } -- *ppExpr = pVector->x.pList->a[iField].pExpr; -- return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); -+ if( op==TK_VECTOR ){ -+ assert( ExprUseXList(pVector) ); -+ *ppExpr = pVector->x.pList->a[iField].pExpr; -+ return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree); -+ } -+ return 0; - } - - /* -@@ -100312,6 +111054,7 @@ static void codeVectorCompare( - int regLeft = 0; - int regRight = 0; - u8 opx = op; -+ int addrCmp = 0; - int addrDone = sqlite3VdbeMakeLabel(pParse); - int isCommuted = ExprHasProperty(pExpr,EP_Commuted); - -@@ -100331,21 +111074,24 @@ static void codeVectorCompare( - assert( p5==0 || pExpr->op!=op ); - assert( p5==SQLITE_NULLEQ || pExpr->op==op ); - -- p5 |= SQLITE_STOREP2; -- if( opx==TK_LE ) opx = TK_LT; -- if( opx==TK_GE ) opx = TK_GT; -+ if( op==TK_LE ) opx = TK_LT; -+ if( op==TK_GE ) opx = TK_GT; -+ if( op==TK_NE ) opx = TK_EQ; - - regLeft = exprCodeSubselect(pParse, pLeft); - regRight = exprCodeSubselect(pParse, pRight); - -+ sqlite3VdbeAddOp2(v, OP_Integer, 1, dest); - for(i=0; 1 /*Loop exits by "break"*/; i++){ - int regFree1 = 0, regFree2 = 0; -- Expr *pL, *pR; -+ Expr *pL = 0, *pR = 0; - int r1, r2; - assert( i>=0 && i0 -@@ -100403,14 +111155,14 @@ SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse *pParse, int nHeight){ - ** to by pnHeight, the second parameter, then set *pnHeight to that - ** value. - */ --static void heightOfExpr(Expr *p, int *pnHeight){ -+static void heightOfExpr(const Expr *p, int *pnHeight){ - if( p ){ - if( p->nHeight>*pnHeight ){ - *pnHeight = p->nHeight; - } - } - } --static void heightOfExprList(ExprList *p, int *pnHeight){ -+static void heightOfExprList(const ExprList *p, int *pnHeight){ - if( p ){ - int i; - for(i=0; inExpr; i++){ -@@ -100418,8 +111170,8 @@ static void heightOfExprList(ExprList *p, int *pnHeight){ - } - } - } --static void heightOfSelect(Select *pSelect, int *pnHeight){ -- Select *p; -+static void heightOfSelect(const Select *pSelect, int *pnHeight){ -+ const Select *p; - for(p=pSelect; p; p=p->pPrior){ - heightOfExpr(p->pWhere, pnHeight); - heightOfExpr(p->pHaving, pnHeight); -@@ -100441,10 +111193,11 @@ static void heightOfSelect(Select *pSelect, int *pnHeight){ - ** if appropriate. - */ - static void exprSetHeight(Expr *p){ -- int nHeight = 0; -- heightOfExpr(p->pLeft, &nHeight); -- heightOfExpr(p->pRight, &nHeight); -- if( ExprHasProperty(p, EP_xIsSelect) ){ -+ int nHeight = p->pLeft ? p->pLeft->nHeight : 0; -+ if( NEVER(p->pRight) && p->pRight->nHeight>nHeight ){ -+ nHeight = p->pRight->nHeight; -+ } -+ if( ExprUseXSelect(p) ){ - heightOfSelect(p->x.pSelect, &nHeight); - }else if( p->x.pList ){ - heightOfExprList(p->x.pList, &nHeight); -@@ -100471,7 +111224,7 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ - ** Return the maximum height of any expression tree referenced - ** by the select statement passed as an argument. - */ --SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){ -+SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *p){ - int nHeight = 0; - heightOfSelect(p, &nHeight); - return nHeight; -@@ -100482,13 +111235,23 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){ - ** Expr.flags. - */ - SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ -- if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){ -+ if( pParse->nErr ) return; -+ if( p && ExprUseXList(p) && p->x.pList ){ - p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList); - } - } - #define exprSetHeight(y) - #endif /* SQLITE_MAX_EXPR_DEPTH>0 */ - -+/* -+** Set the error offset for an Expr node, if possible. -+*/ -+SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){ -+ if( pExpr==0 ) return; -+ if( NEVER(ExprUseWJoin(pExpr)) ) return; -+ pExpr->w.iOfst = iOfst; -+} -+ - /* - ** This routine is the core allocator for Expr nodes. - ** -@@ -100503,11 +111266,12 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ - ** appear to be quoted. If the quotes were of the form "..." (double-quotes) - ** then the EP_DblQuoted flag is set on the expression node. - ** --** Special case: If op==TK_INTEGER and pToken points to a string that --** can be translated into a 32-bit integer, then the token is not --** stored in u.zToken. Instead, the integer values is written --** into u.iValue and the EP_IntValue flag is set. No extra storage -+** Special case (tag-20240227-a): If op==TK_INTEGER and pToken points to -+** a string that can be translated into a 32-bit integer, then the token is -+** not stored in u.zToken. Instead, the integer values is written -+** into u.iValue and the EP_IntValue flag is set. No extra storage - ** is allocated to hold the integer text and the dequote flag is ignored. -+** See also tag-20240227-b. - */ - SQLITE_PRIVATE Expr *sqlite3ExprAlloc( - sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */ -@@ -100523,7 +111287,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( - if( pToken ){ - if( op!=TK_INTEGER || pToken->z==0 - || sqlite3GetInt32(pToken->z, &iValue)==0 ){ -- nExtra = pToken->n+1; -+ nExtra = pToken->n+1; /* tag-20240227-a */ - assert( iValue>=0 ); - } - } -@@ -100585,15 +111349,26 @@ SQLITE_PRIVATE void sqlite3ExprAttachSubtrees( - sqlite3ExprDelete(db, pLeft); - sqlite3ExprDelete(db, pRight); - }else{ -+ assert( ExprUseXList(pRoot) ); -+ assert( pRoot->x.pSelect==0 ); - if( pRight ){ - pRoot->pRight = pRight; - pRoot->flags |= EP_Propagate & pRight->flags; -+#if SQLITE_MAX_EXPR_DEPTH>0 -+ pRoot->nHeight = pRight->nHeight+1; -+ }else{ -+ pRoot->nHeight = 1; -+#endif - } - if( pLeft ){ - pRoot->pLeft = pLeft; - pRoot->flags |= EP_Propagate & pLeft->flags; -+#if SQLITE_MAX_EXPR_DEPTH>0 -+ if( pLeft->nHeight>=pRoot->nHeight ){ -+ pRoot->nHeight = pLeft->nHeight+1; -+ } -+#endif - } -- exprSetHeight(pRoot); - } - } - -@@ -100640,14 +111415,71 @@ SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, Select *pS - } - } - -+/* -+** Expression list pEList is a list of vector values. This function -+** converts the contents of pEList to a VALUES(...) Select statement -+** returning 1 row for each element of the list. For example, the -+** expression list: -+** -+** ( (1,2), (3,4) (5,6) ) -+** -+** is translated to the equivalent of: -+** -+** VALUES(1,2), (3,4), (5,6) -+** -+** Each of the vector values in pEList must contain exactly nElem terms. -+** If a list element that is not a vector or does not contain nElem terms, -+** an error message is left in pParse. -+** -+** This is used as part of processing IN(...) expressions with a list -+** of vectors on the RHS. e.g. "... IN ((1,2), (3,4), (5,6))". -+*/ -+SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse *pParse, int nElem, ExprList *pEList){ -+ int ii; -+ Select *pRet = 0; -+ assert( nElem>1 ); -+ for(ii=0; iinExpr; ii++){ -+ Select *pSel; -+ Expr *pExpr = pEList->a[ii].pExpr; -+ int nExprElem; -+ if( pExpr->op==TK_VECTOR ){ -+ assert( ExprUseXList(pExpr) ); -+ nExprElem = pExpr->x.pList->nExpr; -+ }else{ -+ nExprElem = 1; -+ } -+ if( nExprElem!=nElem ){ -+ sqlite3ErrorMsg(pParse, "IN(...) element has %d term%s - expected %d", -+ nExprElem, nExprElem>1?"s":"", nElem -+ ); -+ break; -+ } -+ assert( ExprUseXList(pExpr) ); -+ pSel = sqlite3SelectNew(pParse, pExpr->x.pList, 0, 0, 0, 0, 0, SF_Values,0); -+ pExpr->x.pList = 0; -+ if( pSel ){ -+ if( pRet ){ -+ pSel->op = TK_ALL; -+ pSel->pPrior = pRet; -+ } -+ pRet = pSel; -+ } -+ } -+ -+ if( pRet && pRet->pPrior ){ -+ pRet->selFlags |= SF_MultiValue; -+ } -+ sqlite3ExprListDelete(pParse->db, pEList); -+ return pRet; -+} - - /* - ** Join two expressions using an AND operator. If either expression is - ** NULL, then just return the other expression. - ** --** If one side or the other of the AND is known to be false, then instead --** of returning an AND expression, just return a constant expression with --** a value of false. -+** If one side or the other of the AND is known to be false, and neither side -+** is part of an ON clause, then instead of returning an AND expression, -+** just return a constant expression with a value of false. - */ - SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ - sqlite3 *db = pParse->db; -@@ -100655,14 +111487,17 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ - return pRight; - }else if( pRight==0 ){ - return pLeft; -- }else if( (ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight)) -- && !IN_RENAME_OBJECT -- ){ -- sqlite3ExprDelete(db, pLeft); -- sqlite3ExprDelete(db, pRight); -- return sqlite3Expr(db, TK_INTEGER, "0"); - }else{ -- return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); -+ u32 f = pLeft->flags | pRight->flags; -+ if( (f&(EP_OuterON|EP_InnerON|EP_IsFalse|EP_HasFunc))==EP_IsFalse -+ && !IN_RENAME_OBJECT -+ ){ -+ sqlite3ExprDeferredDelete(pParse, pLeft); -+ sqlite3ExprDeferredDelete(pParse, pRight); -+ return sqlite3Expr(db, TK_INTEGER, "0"); -+ }else{ -+ return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); -+ } - } - } - -@@ -100673,7 +111508,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ - SQLITE_PRIVATE Expr *sqlite3ExprFunction( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* Argument list */ -- Token *pToken, /* Name of the function */ -+ const Token *pToken, /* Name of the function */ - int eDistinct /* SF_Distinct or SF_ALL or 0 */ - ){ - Expr *pNew; -@@ -100684,17 +111519,83 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction( - sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ - return 0; - } -- if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ -+ assert( !ExprHasProperty(pNew, EP_InnerON|EP_OuterON) ); -+ pNew->w.iOfst = (int)(pToken->z - pParse->zTail); -+ if( pList -+ && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] -+ && !pParse->nested -+ ){ - sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken); - } - pNew->x.pList = pList; - ExprSetProperty(pNew, EP_HasFunc); -- assert( !ExprHasProperty(pNew, EP_xIsSelect) ); -+ assert( ExprUseXList(pNew) ); - sqlite3ExprSetHeightAndFlags(pParse, pNew); - if( eDistinct==SF_Distinct ) ExprSetProperty(pNew, EP_Distinct); - return pNew; - } - -+/* -+** Report an error when attempting to use an ORDER BY clause within -+** the arguments of a non-aggregate function. -+*/ -+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse *pParse, Expr *p){ -+ sqlite3ErrorMsg(pParse, -+ "ORDER BY may not be used with non-aggregate %#T()", p -+ ); -+} -+ -+/* -+** Attach an ORDER BY clause to a function call. -+** -+** functionname( arguments ORDER BY sortlist ) -+** \_____________________/ \______/ -+** pExpr pOrderBy -+** -+** The ORDER BY clause is inserted into a new Expr node of type TK_ORDER -+** and added to the Expr.pLeft field of the parent TK_FUNCTION node. -+*/ -+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy( -+ Parse *pParse, /* Parsing context */ -+ Expr *pExpr, /* The function call to which ORDER BY is to be added */ -+ ExprList *pOrderBy /* The ORDER BY clause to add */ -+){ -+ Expr *pOB; -+ sqlite3 *db = pParse->db; -+ if( NEVER(pOrderBy==0) ){ -+ assert( db->mallocFailed ); -+ return; -+ } -+ if( pExpr==0 ){ -+ assert( db->mallocFailed ); -+ sqlite3ExprListDelete(db, pOrderBy); -+ return; -+ } -+ assert( pExpr->op==TK_FUNCTION ); -+ assert( pExpr->pLeft==0 ); -+ assert( ExprUseXList(pExpr) ); -+ if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){ -+ /* Ignore ORDER BY on zero-argument aggregates */ -+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy); -+ return; -+ } -+ if( IsWindowFunc(pExpr) ){ -+ sqlite3ExprOrderByAggregateError(pParse, pExpr); -+ sqlite3ExprListDelete(db, pOrderBy); -+ return; -+ } -+ -+ pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0); -+ if( pOB==0 ){ -+ sqlite3ExprListDelete(db, pOrderBy); -+ return; -+ } -+ pOB->x.pList = pOrderBy; -+ assert( ExprUseXList(pOB) ); -+ pExpr->pLeft = pOB; -+ ExprSetProperty(pOB, EP_FullSize); -+} -+ - /* - ** Check to see if a function is usable according to current access - ** rules: -@@ -100708,8 +111609,8 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction( - */ - SQLITE_PRIVATE void sqlite3ExprFunctionUsable( - Parse *pParse, /* Parsing and code generating context */ -- Expr *pExpr, /* The function invocation */ -- FuncDef *pDef /* The function being invoked */ -+ const Expr *pExpr, /* The function invocation */ -+ const FuncDef *pDef /* The function being invoked */ - ){ - assert( !IN_RENAME_OBJECT ); - assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); -@@ -100724,7 +111625,7 @@ SQLITE_PRIVATE void sqlite3ExprFunctionUsable( - ** SQLITE_DBCONFIG_TRUSTED_SCHEMA is off (meaning - ** that the schema is possibly tainted). - */ -- sqlite3ErrorMsg(pParse, "unsafe use of %s()", pDef->zName); -+ sqlite3ErrorMsg(pParse, "unsafe use of %#T()", pExpr); - } - } - } -@@ -100780,6 +111681,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n - if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ - sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", - db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); -+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); - return; - } - x = (ynVar)i; -@@ -100807,6 +111709,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n - pExpr->iColumn = x; - if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ - sqlite3ErrorMsg(pParse, "too many SQL variables"); -+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); - } - } - -@@ -100815,27 +111718,27 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n - */ - static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ - assert( p!=0 ); -- /* Sanity check: Assert that the IntValue is non-negative if it exists */ -- assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); -- -- assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed ); -- assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced) -- || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) ); -+ assert( db!=0 ); -+exprDeleteRestart: -+ assert( !ExprUseUValue(p) || p->u.iValue>=0 ); -+ assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); -+ assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); -+ assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) ); - #ifdef SQLITE_DEBUG - if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){ - assert( p->pLeft==0 ); - assert( p->pRight==0 ); -- assert( p->x.pSelect==0 ); -+ assert( !ExprUseXSelect(p) || p->x.pSelect==0 ); -+ assert( !ExprUseXList(p) || p->x.pList==0 ); - } - #endif - if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ - /* The Expr.x union is never used at the same time as Expr.pRight */ -- assert( p->x.pList==0 || p->pRight==0 ); -- if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); -+ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); - if( p->pRight ){ - assert( !ExprHasProperty(p, EP_WinFunc) ); - sqlite3ExprDeleteNN(db, p->pRight); -- }else if( ExprHasProperty(p, EP_xIsSelect) ){ -+ }else if( ExprUseXSelect(p) ){ - assert( !ExprHasProperty(p, EP_WinFunc) ); - sqlite3SelectDelete(db, p->x.pSelect); - }else{ -@@ -100846,15 +111749,57 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ - } - #endif - } -+ if( p->pLeft && p->op!=TK_SELECT_COLUMN ){ -+ Expr *pLeft = p->pLeft; -+ if( !ExprHasProperty(p, EP_Static) -+ && !ExprHasProperty(pLeft, EP_Static) -+ ){ -+ /* Avoid unnecessary recursion on unary operators */ -+ sqlite3DbNNFreeNN(db, p); -+ p = pLeft; -+ goto exprDeleteRestart; -+ }else{ -+ sqlite3ExprDeleteNN(db, pLeft); -+ } -+ } - } -- if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); - if( !ExprHasProperty(p, EP_Static) ){ -- sqlite3DbFreeNN(db, p); -+ sqlite3DbNNFreeNN(db, p); - } - } - SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ - if( p ) sqlite3ExprDeleteNN(db, p); - } -+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){ -+ if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p); -+} -+ -+/* -+** Clear both elements of an OnOrUsing object -+*/ -+SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ -+ if( p==0 ){ -+ /* Nothing to clear */ -+ }else if( p->pOn ){ -+ sqlite3ExprDeleteNN(db, p->pOn); -+ }else if( p->pUsing ){ -+ sqlite3IdListDelete(db, p->pUsing); -+ } -+} -+ -+/* -+** Arrange to cause pExpr to be deleted when the pParse is deleted. -+** This is similar to sqlite3ExprDelete() except that the delete is -+** deferred until the pParse is deleted. -+** -+** The pExpr might be deleted immediately on an OOM error. -+** -+** Return 0 if the delete was successfully deferred. Return non-zero -+** if the delete happened immediately because of an OOM. -+*/ -+SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ -+ return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); -+} - - /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the - ** expression. -@@ -100873,7 +111818,7 @@ SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){ - ** passed as the first argument. This is always one of EXPR_FULLSIZE, - ** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. - */ --static int exprStructSize(Expr *p){ -+static int exprStructSize(const Expr *p){ - if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE; - if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE; - return EXPR_FULLSIZE; -@@ -100913,21 +111858,16 @@ static int exprStructSize(Expr *p){ - ** of dupedExprStructSize() contain multiple assert() statements that attempt - ** to enforce this constraint. - */ --static int dupedExprStructSize(Expr *p, int flags){ -+static int dupedExprStructSize(const Expr *p, int flags){ - int nSize; - assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ - assert( EXPR_FULLSIZE<=0xfff ); - assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 ); -- if( 0==flags || p->op==TK_SELECT_COLUMN --#ifndef SQLITE_OMIT_WINDOWFUNC -- || ExprHasProperty(p, EP_WinFunc) --#endif -- ){ -+ if( 0==flags || ExprHasProperty(p, EP_FullSize) ){ - nSize = EXPR_FULLSIZE; - }else{ - assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); -- assert( !ExprHasProperty(p, EP_FromJoin) ); -- assert( !ExprHasProperty(p, EP_MemToken) ); -+ assert( !ExprHasProperty(p, EP_OuterON) ); - assert( !ExprHasVVAProperty(p, EP_NoReduce) ); - if( p->pLeft || p->x.pList ){ - nSize = EXPR_REDUCEDSIZE | EP_Reduced; -@@ -100944,7 +111884,7 @@ static int dupedExprStructSize(Expr *p, int flags){ - ** of the Expr structure and a copy of the Expr.u.zToken string (if that - ** string is defined.) - */ --static int dupedExprNodeSize(Expr *p, int flags){ -+static int dupedExprNodeSize(const Expr *p, int flags){ - int nByte = dupedExprStructSize(p, flags) & 0xfff; - if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ - nByte += sqlite3Strlen30NN(p->u.zToken)+1; -@@ -100954,55 +111894,93 @@ static int dupedExprNodeSize(Expr *p, int flags){ - - /* - ** Return the number of bytes required to create a duplicate of the --** expression passed as the first argument. The second argument is a --** mask containing EXPRDUP_XXX flags. -+** expression passed as the first argument. - ** - ** The value returned includes space to create a copy of the Expr struct - ** itself and the buffer referred to by Expr.u.zToken, if any. - ** --** If the EXPRDUP_REDUCE flag is set, then the return value includes --** space to duplicate all Expr nodes in the tree formed by Expr.pLeft --** and Expr.pRight variables (but not for any structures pointed to or --** descended from the Expr.x.pList or Expr.x.pSelect variables). -+** The return value includes space to duplicate all Expr nodes in the -+** tree formed by Expr.pLeft and Expr.pRight, but not any other -+** substructure such as Expr.x.pList, Expr.x.pSelect, and Expr.y.pWin. - */ --static int dupedExprSize(Expr *p, int flags){ -- int nByte = 0; -- if( p ){ -- nByte = dupedExprNodeSize(p, flags); -- if( flags&EXPRDUP_REDUCE ){ -- nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags); -- } -- } -+static int dupedExprSize(const Expr *p){ -+ int nByte; -+ assert( p!=0 ); -+ nByte = dupedExprNodeSize(p, EXPRDUP_REDUCE); -+ if( p->pLeft ) nByte += dupedExprSize(p->pLeft); -+ if( p->pRight ) nByte += dupedExprSize(p->pRight); -+ assert( nByte==ROUND8(nByte) ); - return nByte; - } - - /* --** This function is similar to sqlite3ExprDup(), except that if pzBuffer --** is not NULL then *pzBuffer is assumed to point to a buffer large enough --** to store the copy of expression p, the copies of p->u.zToken --** (if applicable), and the copies of the p->pLeft and p->pRight expressions, --** if any. Before returning, *pzBuffer is set to the first byte past the --** portion of the buffer copied into by this function. -+** An EdupBuf is a memory allocation used to stored multiple Expr objects -+** together with their Expr.zToken content. This is used to help implement -+** compression while doing sqlite3ExprDup(). The top-level Expr does the -+** allocation for itself and many of its decendents, then passes an instance -+** of the structure down into exprDup() so that they decendents can have -+** access to that memory. -+*/ -+typedef struct EdupBuf EdupBuf; -+struct EdupBuf { -+ u8 *zAlloc; /* Memory space available for storage */ -+#ifdef SQLITE_DEBUG -+ u8 *zEnd; /* First byte past the end of memory */ -+#endif -+}; -+ -+/* -+** This function is similar to sqlite3ExprDup(), except that if pEdupBuf -+** is not NULL then it points to memory that can be used to store a copy -+** of the input Expr p together with its p->u.zToken (if any). pEdupBuf -+** is updated with the new buffer tail prior to returning. - */ --static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ -+static Expr *exprDup( -+ sqlite3 *db, /* Database connection (for memory allocation) */ -+ const Expr *p, /* Expr tree to be duplicated */ -+ int dupFlags, /* EXPRDUP_REDUCE for compression. 0 if not */ -+ EdupBuf *pEdupBuf /* Preallocated storage space, or NULL */ -+){ - Expr *pNew; /* Value to return */ -- u8 *zAlloc; /* Memory space from which to build Expr object */ -+ EdupBuf sEdupBuf; /* Memory space from which to build Expr object */ - u32 staticFlag; /* EP_Static if space not obtained from malloc */ -+ int nToken = -1; /* Space needed for p->u.zToken. -1 means unknown */ - - assert( db!=0 ); - assert( p ); - assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE ); -- assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE ); -+ assert( pEdupBuf==0 || dupFlags==EXPRDUP_REDUCE ); - - /* Figure out where to write the new Expr structure. */ -- if( pzBuffer ){ -- zAlloc = *pzBuffer; -+ if( pEdupBuf ){ -+ sEdupBuf.zAlloc = pEdupBuf->zAlloc; -+#ifdef SQLITE_DEBUG -+ sEdupBuf.zEnd = pEdupBuf->zEnd; -+#endif - staticFlag = EP_Static; -+ assert( sEdupBuf.zAlloc!=0 ); -+ assert( dupFlags==EXPRDUP_REDUCE ); - }else{ -- zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags)); -+ int nAlloc; -+ if( dupFlags ){ -+ nAlloc = dupedExprSize(p); -+ }else if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ -+ nToken = sqlite3Strlen30NN(p->u.zToken)+1; -+ nAlloc = ROUND8(EXPR_FULLSIZE + nToken); -+ }else{ -+ nToken = 0; -+ nAlloc = ROUND8(EXPR_FULLSIZE); -+ } -+ assert( nAlloc==ROUND8(nAlloc) ); -+ sEdupBuf.zAlloc = sqlite3DbMallocRawNN(db, nAlloc); -+#ifdef SQLITE_DEBUG -+ sEdupBuf.zEnd = sEdupBuf.zAlloc ? sEdupBuf.zAlloc+nAlloc : 0; -+#endif -+ - staticFlag = 0; - } -- pNew = (Expr *)zAlloc; -+ pNew = (Expr *)sEdupBuf.zAlloc; -+ assert( EIGHT_BYTE_ALIGNMENT(pNew) ); - - if( pNew ){ - /* Set nNewSize to the size allocated for the structure pointed to -@@ -101011,26 +111989,31 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ - ** by the copy of the p->u.zToken string (if any). - */ - const unsigned nStructSize = dupedExprStructSize(p, dupFlags); -- const int nNewSize = nStructSize & 0xfff; -- int nToken; -- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ -- nToken = sqlite3Strlen30(p->u.zToken) + 1; -- }else{ -- nToken = 0; -+ int nNewSize = nStructSize & 0xfff; -+ if( nToken<0 ){ -+ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ -+ nToken = sqlite3Strlen30(p->u.zToken) + 1; -+ }else{ -+ nToken = 0; -+ } - } - if( dupFlags ){ -+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= nNewSize+nToken ); - assert( ExprHasProperty(p, EP_Reduced)==0 ); -- memcpy(zAlloc, p, nNewSize); -+ memcpy(sEdupBuf.zAlloc, p, nNewSize); - }else{ - u32 nSize = (u32)exprStructSize(p); -- memcpy(zAlloc, p, nSize); -+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= -+ (int)EXPR_FULLSIZE+nToken ); -+ memcpy(sEdupBuf.zAlloc, p, nSize); - if( nSizeflags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken); -+ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static); - pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly); - pNew->flags |= staticFlag; - ExprClearVVAProperties(pNew); -@@ -101039,44 +112022,50 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ - } - - /* Copy the p->u.zToken string, if any. */ -- if( nToken ){ -- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize]; -+ assert( nToken>=0 ); -+ if( nToken>0 ){ -+ char *zToken = pNew->u.zToken = (char*)&sEdupBuf.zAlloc[nNewSize]; - memcpy(zToken, p->u.zToken, nToken); -+ nNewSize += nToken; - } -+ sEdupBuf.zAlloc += ROUND8(nNewSize); -+ -+ if( ((p->flags|pNew->flags)&(EP_TokenOnly|EP_Leaf))==0 ){ - -- if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){ - /* Fill in the pNew->x.pSelect or pNew->x.pList member. */ -- if( ExprHasProperty(p, EP_xIsSelect) ){ -+ if( ExprUseXSelect(p) ){ - pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags); - }else{ -- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags); -+ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, -+ p->op!=TK_ORDER ? dupFlags : 0); - } -- } - -- /* Fill in pNew->pLeft and pNew->pRight. */ -- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){ -- zAlloc += dupedExprNodeSize(p, dupFlags); -- if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){ -- pNew->pLeft = p->pLeft ? -- exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0; -- pNew->pRight = p->pRight ? -- exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0; -- } - #ifndef SQLITE_OMIT_WINDOWFUNC - if( ExprHasProperty(p, EP_WinFunc) ){ - pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin); - assert( ExprHasProperty(pNew, EP_WinFunc) ); - } - #endif /* SQLITE_OMIT_WINDOWFUNC */ -- if( pzBuffer ){ -- *pzBuffer = zAlloc; -- } -- }else{ -- if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ -- if( pNew->op==TK_SELECT_COLUMN ){ -+ -+ /* Fill in pNew->pLeft and pNew->pRight. */ -+ if( dupFlags ){ -+ if( p->op==TK_SELECT_COLUMN ){ -+ pNew->pLeft = p->pLeft; -+ assert( p->pRight==0 -+ || p->pRight==p->pLeft -+ || ExprHasProperty(p->pLeft, EP_Subquery) ); -+ }else{ -+ pNew->pLeft = p->pLeft ? -+ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &sEdupBuf) : 0; -+ } -+ pNew->pRight = p->pRight ? -+ exprDup(db, p->pRight, EXPRDUP_REDUCE, &sEdupBuf) : 0; -+ }else{ -+ if( p->op==TK_SELECT_COLUMN ){ - pNew->pLeft = p->pLeft; -- assert( p->iColumn==0 || p->pRight==0 ); -- assert( p->pRight==0 || p->pRight==p->pLeft ); -+ assert( p->pRight==0 -+ || p->pRight==p->pLeft -+ || ExprHasProperty(p->pLeft, EP_Subquery) ); - }else{ - pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); - } -@@ -101084,6 +112073,8 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ - } - } - } -+ if( pEdupBuf ) memcpy(pEdupBuf, &sEdupBuf, sizeof(sEdupBuf)); -+ assert( sEdupBuf.zAlloc <= sEdupBuf.zEnd ); - return pNew; - } - -@@ -101093,10 +112084,10 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){ - ** and the db->mallocFailed flag set. - */ - #ifndef SQLITE_OMIT_CTE --static With *withDup(sqlite3 *db, With *p){ -+SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ - With *pRet = 0; - if( p ){ -- sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); -+ sqlite3_int64 nByte = SZ_WITH(p->nCte); - pRet = sqlite3DbMallocZero(db, nByte); - if( pRet ){ - int i; -@@ -101105,13 +112096,14 @@ static With *withDup(sqlite3 *db, With *p){ - pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0); - pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0); - pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName); -+ pRet->a[i].eM10d = p->a[i].eM10d; - } - } - } - return pRet; - } - #else --# define withDup(x,y) 0 -+# define sqlite3WithDup(x,y) 0 - #endif - - #ifndef SQLITE_OMIT_WINDOWFUNC -@@ -101164,20 +112156,23 @@ static void gatherSelectWindows(Select *p){ - ** truncated version of the usual Expr structure that will be stored as - ** part of the in-memory representation of the database schema. - */ --SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){ -+SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, const Expr *p, int flags){ - assert( flags==0 || flags==EXPRDUP_REDUCE ); - return p ? exprDup(db, p, flags, 0) : 0; - } --SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ -+SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int flags){ - ExprList *pNew; -- struct ExprList_item *pItem, *pOldItem; -+ struct ExprList_item *pItem; -+ const struct ExprList_item *pOldItem; - int i; -- Expr *pPriorSelectCol = 0; -+ Expr *pPriorSelectColOld = 0; -+ Expr *pPriorSelectColNew = 0; - assert( db!=0 ); - if( p==0 ) return 0; - pNew = sqlite3DbMallocRawNN(db, sqlite3DbMallocSize(db, p)); - if( pNew==0 ) return 0; - pNew->nExpr = p->nExpr; -+ pNew->nAlloc = p->nAlloc; - pItem = pNew->a; - pOldItem = p->a; - for(i=0; inExpr; i++, pItem++, pOldItem++){ -@@ -101188,24 +112183,21 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags) - && pOldExpr->op==TK_SELECT_COLUMN - && (pNewExpr = pItem->pExpr)!=0 - ){ -- assert( pNewExpr->iColumn==0 || i>0 ); -- if( pNewExpr->iColumn==0 ){ -- assert( pOldExpr->pLeft==pOldExpr->pRight ); -- pPriorSelectCol = pNewExpr->pLeft = pNewExpr->pRight; -+ if( pNewExpr->pRight ){ -+ pPriorSelectColOld = pOldExpr->pRight; -+ pPriorSelectColNew = pNewExpr->pRight; -+ pNewExpr->pLeft = pNewExpr->pRight; - }else{ -- assert( i>0 ); -- assert( pItem[-1].pExpr!=0 ); -- assert( pNewExpr->iColumn==pItem[-1].pExpr->iColumn+1 ); -- assert( pPriorSelectCol==pItem[-1].pExpr->pLeft ); -- pNewExpr->pLeft = pPriorSelectCol; -+ if( pOldExpr->pLeft!=pPriorSelectColOld ){ -+ pPriorSelectColOld = pOldExpr->pLeft; -+ pPriorSelectColNew = sqlite3ExprDup(db, pPriorSelectColOld, flags); -+ pNewExpr->pRight = pPriorSelectColNew; -+ } -+ pNewExpr->pLeft = pPriorSelectColNew; - } - } - pItem->zEName = sqlite3DbStrDup(db, pOldItem->zEName); -- pItem->sortFlags = pOldItem->sortFlags; -- pItem->eEName = pOldItem->eEName; -- pItem->done = 0; -- pItem->bNulls = pOldItem->bNulls; -- pItem->bSorterRef = pOldItem->bSorterRef; -+ pItem->fg = pOldItem->fg; - pItem->u = pOldItem->u; - } - return pNew; -@@ -101219,76 +112211,88 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags) - */ - #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ - || !defined(SQLITE_OMIT_SUBQUERY) --SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ -+SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ - SrcList *pNew; - int i; -- int nByte; - assert( db!=0 ); - if( p==0 ) return 0; -- nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); -- pNew = sqlite3DbMallocRawNN(db, nByte ); -+ pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) ); - if( pNew==0 ) return 0; - pNew->nSrc = pNew->nAlloc = p->nSrc; - for(i=0; inSrc; i++){ -- struct SrcList_item *pNewItem = &pNew->a[i]; -- struct SrcList_item *pOldItem = &p->a[i]; -+ SrcItem *pNewItem = &pNew->a[i]; -+ const SrcItem *pOldItem = &p->a[i]; - Table *pTab; -- pNewItem->pSchema = pOldItem->pSchema; -- pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); -+ pNewItem->fg = pOldItem->fg; -+ if( pOldItem->fg.isSubquery ){ -+ Subquery *pNewSubq = sqlite3DbMallocRaw(db, sizeof(Subquery)); -+ if( pNewSubq==0 ){ -+ assert( db->mallocFailed ); -+ pNewItem->fg.isSubquery = 0; -+ }else{ -+ memcpy(pNewSubq, pOldItem->u4.pSubq, sizeof(*pNewSubq)); -+ pNewSubq->pSelect = sqlite3SelectDup(db, pNewSubq->pSelect, flags); -+ if( pNewSubq->pSelect==0 ){ -+ sqlite3DbFree(db, pNewSubq); -+ pNewSubq = 0; -+ pNewItem->fg.isSubquery = 0; -+ } -+ } -+ pNewItem->u4.pSubq = pNewSubq; -+ }else if( pOldItem->fg.fixedSchema ){ -+ pNewItem->u4.pSchema = pOldItem->u4.pSchema; -+ }else{ -+ pNewItem->u4.zDatabase = sqlite3DbStrDup(db, pOldItem->u4.zDatabase); -+ } - pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); - pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); -- pNewItem->fg = pOldItem->fg; - pNewItem->iCursor = pOldItem->iCursor; -- pNewItem->addrFillSub = pOldItem->addrFillSub; -- pNewItem->regReturn = pOldItem->regReturn; - if( pNewItem->fg.isIndexedBy ){ - pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); -- } -- pNewItem->pIBIndex = pOldItem->pIBIndex; -- if( pNewItem->fg.isTabFunc ){ -+ }else if( pNewItem->fg.isTabFunc ){ - pNewItem->u1.pFuncArg = - sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); -+ }else{ -+ pNewItem->u1.nRow = pOldItem->u1.nRow; - } -- pTab = pNewItem->pTab = pOldItem->pTab; -+ pNewItem->u2 = pOldItem->u2; -+ if( pNewItem->fg.isCte ){ -+ pNewItem->u2.pCteUse->nUse++; -+ } -+ pTab = pNewItem->pSTab = pOldItem->pSTab; - if( pTab ){ - pTab->nTabRef++; - } -- pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); -- pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); -- pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); -+ if( pOldItem->fg.isUsing ){ -+ assert( pNewItem->fg.isUsing ); -+ pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing); -+ }else{ -+ pNewItem->u3.pOn = sqlite3ExprDup(db, pOldItem->u3.pOn, flags); -+ } - pNewItem->colUsed = pOldItem->colUsed; - } - return pNew; - } --SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ -+SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ - IdList *pNew; - int i; - assert( db!=0 ); - if( p==0 ) return 0; -- pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) ); -+ pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId)); - if( pNew==0 ) return 0; - pNew->nId = p->nId; -- pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) ); -- if( pNew->a==0 ){ -- sqlite3DbFreeNN(db, pNew); -- return 0; -- } -- /* Note that because the size of the allocation for p->a[] is not -- ** necessarily a power of two, sqlite3IdListAppend() may not be called -- ** on the duplicate created by this function. */ - for(i=0; inId; i++){ - struct IdList_item *pNewItem = &pNew->a[i]; -- struct IdList_item *pOldItem = &p->a[i]; -+ const struct IdList_item *pOldItem = &p->a[i]; - pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); -- pNewItem->idx = pOldItem->idx; - } - return pNew; - } --SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ -+SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int flags){ - Select *pRet = 0; - Select *pNext = 0; - Select **pp = &pRet; -- Select *p; -+ const Select *p; - - assert( db!=0 ); - for(p=pDup; p; p=p->pPrior){ -@@ -101306,26 +112310,33 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ - pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); - pNew->iLimit = 0; - pNew->iOffset = 0; -- pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; -+ pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral; - pNew->addrOpenEphm[0] = -1; - pNew->addrOpenEphm[1] = -1; - pNew->nSelectRow = p->nSelectRow; -- pNew->pWith = withDup(db, p->pWith); -+ pNew->pWith = sqlite3WithDup(db, p->pWith); - #ifndef SQLITE_OMIT_WINDOWFUNC - pNew->pWin = 0; - pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); - if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew); - #endif - pNew->selId = p->selId; -+ if( db->mallocFailed ){ -+ /* Any prior OOM might have left the Select object incomplete. -+ ** Delete the whole thing rather than allow an incomplete Select -+ ** to be used by the code generator. */ -+ pNew->pNext = 0; -+ sqlite3SelectDelete(db, pNew); -+ break; -+ } - *pp = pNew; - pp = &pNew->pPrior; - pNext = pNew; - } -- - return pRet; - } - #else --SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ -+SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags){ - assert( p==0 ); - return 0; - } -@@ -101337,51 +112348,69 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ - ** initially NULL, then create a new expression list. - ** - ** The pList argument must be either NULL or a pointer to an ExprList --** obtained from a prior call to sqlite3ExprListAppend(). This routine --** may not be used with an ExprList obtained from sqlite3ExprListDup(). --** Reason: This routine assumes that the number of slots in pList->a[] --** is a power of two. That is true for sqlite3ExprListAppend() returns --** but is not necessarily true from the return value of sqlite3ExprListDup(). -+** obtained from a prior call to sqlite3ExprListAppend(). - ** - ** If a memory allocation error occurs, the entire list is freed and - ** NULL is returned. If non-NULL is returned, then it is guaranteed - ** that the new entry was successfully appended. - */ -+static const struct ExprList_item zeroItem = {0}; -+SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendNew( -+ sqlite3 *db, /* Database handle. Used for memory allocation */ -+ Expr *pExpr /* Expression to be appended. Might be NULL */ -+){ -+ struct ExprList_item *pItem; -+ ExprList *pList; -+ -+ pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4)); -+ if( pList==0 ){ -+ sqlite3ExprDelete(db, pExpr); -+ return 0; -+ } -+ pList->nAlloc = 4; -+ pList->nExpr = 1; -+ pItem = &pList->a[0]; -+ *pItem = zeroItem; -+ pItem->pExpr = pExpr; -+ return pList; -+} -+SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendGrow( -+ sqlite3 *db, /* Database handle. Used for memory allocation */ -+ ExprList *pList, /* List to which to append. Might be NULL */ -+ Expr *pExpr /* Expression to be appended. Might be NULL */ -+){ -+ struct ExprList_item *pItem; -+ ExprList *pNew; -+ pList->nAlloc *= 2; -+ pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc)); -+ if( pNew==0 ){ -+ sqlite3ExprListDelete(db, pList); -+ sqlite3ExprDelete(db, pExpr); -+ return 0; -+ }else{ -+ pList = pNew; -+ } -+ pItem = &pList->a[pList->nExpr++]; -+ *pItem = zeroItem; -+ pItem->pExpr = pExpr; -+ return pList; -+} - SQLITE_PRIVATE ExprList *sqlite3ExprListAppend( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* List to which to append. Might be NULL */ - Expr *pExpr /* Expression to be appended. Might be NULL */ - ){ - struct ExprList_item *pItem; -- sqlite3 *db = pParse->db; -- assert( db!=0 ); - if( pList==0 ){ -- pList = sqlite3DbMallocRawNN(db, sizeof(ExprList) ); -- if( pList==0 ){ -- goto no_mem; -- } -- pList->nExpr = 0; -- }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ -- ExprList *pNew; -- pNew = sqlite3DbRealloc(db, pList, -- sizeof(*pList)+(2*(sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0])); -- if( pNew==0 ){ -- goto no_mem; -- } -- pList = pNew; -+ return sqlite3ExprListAppendNew(pParse->db,pExpr); -+ } -+ if( pList->nAllocnExpr+1 ){ -+ return sqlite3ExprListAppendGrow(pParse->db,pList,pExpr); - } - pItem = &pList->a[pList->nExpr++]; -- assert( offsetof(struct ExprList_item,zEName)==sizeof(pItem->pExpr) ); -- assert( offsetof(struct ExprList_item,pExpr)==0 ); -- memset(&pItem->zEName,0,sizeof(*pItem)-offsetof(struct ExprList_item,zEName)); -+ *pItem = zeroItem; - pItem->pExpr = pExpr; - return pList; -- --no_mem: -- /* Avoid leaking memory if malloc has failed. */ -- sqlite3ExprDelete(db, pExpr); -- sqlite3ExprListDelete(db, pList); -- return 0; - } - - /* -@@ -101422,11 +112451,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector( - } - - for(i=0; inId; i++){ -- Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i); -+ Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i, pColumns->nId); - assert( pSubExpr!=0 || db->mallocFailed ); -- assert( pSubExpr==0 || pSubExpr->iTable==0 ); - if( pSubExpr==0 ) continue; -- pSubExpr->iTable = pColumns->nId; - pList = sqlite3ExprListAppend(pParse, pList, pSubExpr); - if( pList ){ - assert( pList->nExpr==iFirst+i+1 ); -@@ -101475,16 +112502,16 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int - ); - - pItem = &p->a[p->nExpr-1]; -- assert( pItem->bNulls==0 ); -+ assert( pItem->fg.bNulls==0 ); - if( iSortOrder==SQLITE_SO_UNDEFINED ){ - iSortOrder = SQLITE_SO_ASC; - } -- pItem->sortFlags = (u8)iSortOrder; -+ pItem->fg.sortFlags = (u8)iSortOrder; - - if( eNulls!=SQLITE_SO_UNDEFINED ){ -- pItem->bNulls = 1; -+ pItem->fg.bNulls = 1; - if( iSortOrder!=eNulls ){ -- pItem->sortFlags |= KEYINFO_ORDER_BIGNULL; -+ pItem->fg.sortFlags |= KEYINFO_ORDER_BIGNULL; - } - } - } -@@ -101500,7 +112527,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int - SQLITE_PRIVATE void sqlite3ExprListSetName( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* List to which to add the span. */ -- Token *pName, /* Name to be added */ -+ const Token *pName, /* Name to be added */ - int dequote /* True to cause the name to be dequoted */ - ){ - assert( pList!=0 || pParse->db->mallocFailed!=0 ); -@@ -101510,7 +112537,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName( - assert( pList->nExpr>0 ); - pItem = &pList->a[pList->nExpr-1]; - assert( pItem->zEName==0 ); -- assert( pItem->eEName==ENAME_NAME ); -+ assert( pItem->fg.eEName==ENAME_NAME ); - pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); - if( dequote ){ - /* If dequote==0, then pName->z does not point to part of a DDL -@@ -101518,7 +112545,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName( - ** to the token-map. */ - sqlite3Dequote(pItem->zEName); - if( IN_RENAME_OBJECT ){ -- sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName); -+ sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName); - } - } - } -@@ -101545,7 +112572,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetSpan( - assert( pList->nExpr>0 ); - if( pItem->zEName==0 ){ - pItem->zEName = sqlite3DbSpanDup(db, zStart, zEnd); -- pItem->eEName = ENAME_SPAN; -+ pItem->fg.eEName = ENAME_SPAN; - } - } - } -@@ -101574,16 +112601,20 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){ - int i = pList->nExpr; - struct ExprList_item *pItem = pList->a; - assert( pList->nExpr>0 ); -+ assert( db!=0 ); - do{ - sqlite3ExprDelete(db, pItem->pExpr); -- sqlite3DbFree(db, pItem->zEName); -+ if( pItem->zEName ) sqlite3DbNNFreeNN(db, pItem->zEName); - pItem++; - }while( --i>0 ); -- sqlite3DbFreeNN(db, pList); -+ sqlite3DbNNFreeNN(db, pList); - } - SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ - if( pList ) exprListDeleteNN(db, pList); - } -+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){ -+ if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList); -+} - - /* - ** Return the bitwise-OR of all Expr.flags fields in the given -@@ -101637,7 +112668,7 @@ SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char *zIn){ - SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ - u32 v; - assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); -- if( !ExprHasProperty(pExpr, EP_Quoted) -+ if( !ExprHasProperty(pExpr, EP_Quoted|EP_IntValue) - && (v = sqlite3IsTrueOrFalse(pExpr->u.zToken))!=0 - ){ - pExpr->op = TK_TRUEFALSE; -@@ -101652,8 +112683,9 @@ SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ - ** and 0 if it is FALSE. - */ - SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){ -- pExpr = sqlite3ExprSkipCollate((Expr*)pExpr); -+ pExpr = sqlite3ExprSkipCollateAndLikely((Expr*)pExpr); - assert( pExpr->op==TK_TRUEFALSE ); -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - assert( sqlite3StrICmp(pExpr->u.zToken,"true")==0 - || sqlite3StrICmp(pExpr->u.zToken,"false")==0 ); - return pExpr->u.zToken[4]==0; -@@ -101686,6 +112718,54 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ - return pExpr; - } - -+/* -+** pExpr is a TK_FUNCTION node. Try to determine whether or not the -+** function is a constant function. A function is constant if all of -+** the following are true: -+** -+** (1) It is a scalar function (not an aggregate or window function) -+** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG -+** property. -+** (3) All of its arguments are constants -+** -+** This routine sets pWalker->eCode to 0 if pExpr is not a constant. -+** It makes no changes to pWalker->eCode if pExpr is constant. In -+** every case, it returns WRC_Abort. -+** -+** Called as a service subroutine from exprNodeIsConstant(). -+*/ -+static SQLITE_NOINLINE int exprNodeIsConstantFunction( -+ Walker *pWalker, -+ Expr *pExpr -+){ -+ int n; /* Number of arguments */ -+ ExprList *pList; /* List of arguments */ -+ FuncDef *pDef; /* The function */ -+ sqlite3 *db; /* The database */ -+ -+ assert( pExpr->op==TK_FUNCTION ); -+ if( ExprHasProperty(pExpr, EP_TokenOnly) -+ || (pList = pExpr->x.pList)==0 -+ ){; -+ n = 0; -+ }else{ -+ n = pList->nExpr; -+ sqlite3WalkExprList(pWalker, pList); -+ if( pWalker->eCode==0 ) return WRC_Abort; -+ } -+ db = pWalker->pParse->db; -+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); -+ if( pDef==0 -+ || pDef->xFinalize!=0 -+ || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 -+ || ExprHasProperty(pExpr, EP_WinFunc) -+ ){ -+ pWalker->eCode = 0; -+ return WRC_Abort; -+ } -+ return WRC_Prune; -+} -+ - - /* - ** These routines are Walker callbacks used to check expressions to -@@ -101714,11 +112794,12 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ - ** malformed schema error. - */ - static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ -+ assert( pWalker->eCode>0 ); - - /* If pWalker->eCode is 2 then any term of the expression that comes from -- ** the ON or USING clauses of a left join disqualifies the expression -+ ** the ON or USING clauses of an outer join disqualifies the expression - ** from being considered constant. */ -- if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){ -+ if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){ - pWalker->eCode = 0; - return WRC_Abort; - } -@@ -101733,6 +112814,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ - ){ - if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL); - return WRC_Continue; -+ }else if( pWalker->pParse ){ -+ return exprNodeIsConstantFunction(pWalker, pExpr); - }else{ - pWalker->eCode = 0; - return WRC_Abort; -@@ -101761,9 +112844,11 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ - case TK_IF_NULL_ROW: - case TK_REGISTER: - case TK_DOT: -+ case TK_RAISE: - testcase( pExpr->op==TK_REGISTER ); - testcase( pExpr->op==TK_IF_NULL_ROW ); - testcase( pExpr->op==TK_DOT ); -+ testcase( pExpr->op==TK_RAISE ); - pWalker->eCode = 0; - return WRC_Abort; - case TK_VARIABLE: -@@ -101785,15 +112870,15 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ - return WRC_Continue; - } - } --static int exprIsConst(Expr *p, int initFlag, int iCur){ -+static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ - Walker w; - w.eCode = initFlag; -+ w.pParse = pParse; - w.xExprCallback = exprNodeIsConstant; - w.xSelectCallback = sqlite3SelectWalkFail; - #ifdef SQLITE_DEBUG - w.xSelectCallback2 = sqlite3SelectWalkAssert2; - #endif -- w.u.iCur = iCur; - sqlite3WalkExpr(&w, p); - return w.eCode; - } -@@ -101805,9 +112890,15 @@ static int exprIsConst(Expr *p, int initFlag, int iCur){ - ** For the purposes of this function, a double-quoted string (ex: "abc") - ** is considered a variable but a single-quoted string (ex: 'abc') is - ** a constant. -+** -+** The pParse parameter may be NULL. But if it is NULL, there is no way -+** to determine if function calls are constant or not, and hence all -+** function calls will be considered to be non-constant. If pParse is -+** not NULL, then a function call might be constant, depending on the -+** function and on its parameters. - */ --SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ -- return exprIsConst(p, 1, 0); -+SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse *pParse, Expr *p){ -+ return exprIsConst(pParse, p, 1); - } - - /* -@@ -101823,8 +112914,24 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ - ** can be added to the pParse->pConstExpr list and evaluated once when - ** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce(). - */ --SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ -- return exprIsConst(p, 2, 0); -+static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ -+ return exprIsConst(pParse, p, 2); -+} -+ -+/* -+** This routine examines sub-SELECT statements as an expression is being -+** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered -+** constant as long as they are uncorrelated - meaning that they do not -+** contain any terms from outer contexts. -+*/ -+static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ -+ assert( pSelect!=0 ); -+ assert( pWalker->eCode==3 || pWalker->eCode==0 ); -+ if( (pSelect->selFlags & SF_Correlated)!=0 ){ -+ pWalker->eCode = 0; -+ return WRC_Abort; -+ } -+ return WRC_Prune; - } - - /* -@@ -101832,9 +112939,103 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ - ** for any single row of the table with cursor iCur. In other words, the - ** expression must not refer to any non-deterministic function nor any - ** table other than iCur. -+** -+** Consider uncorrelated subqueries to be constants if the bAllowSubq -+** parameter is true. -+*/ -+static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){ -+ Walker w; -+ w.eCode = 3; -+ w.pParse = 0; -+ w.xExprCallback = exprNodeIsConstant; -+ if( bAllowSubq ){ -+ w.xSelectCallback = exprSelectWalkTableConstant; -+ }else{ -+ w.xSelectCallback = sqlite3SelectWalkFail; -+#ifdef SQLITE_DEBUG -+ w.xSelectCallback2 = sqlite3SelectWalkAssert2; -+#endif -+ } -+ w.u.iCur = iCur; -+ sqlite3WalkExpr(&w, p); -+ return w.eCode; -+} -+ -+/* -+** Check pExpr to see if it is an constraint on the single data source -+** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr -+** constrains pSrc but does not depend on any other tables or data -+** sources anywhere else in the query. Return true (non-zero) if pExpr -+** is a constraint on pSrc only. -+** -+** This is an optimization. False negatives will perhaps cause slower -+** queries, but false positives will yield incorrect answers. So when in -+** doubt, return 0. -+** -+** To be an single-source constraint, the following must be true: -+** -+** (1) pExpr cannot refer to any table other than pSrc->iCursor. -+** -+** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is -+** true and the subquery is non-correlated -+** -+** (2b) pExpr cannot use non-deterministic functions. -+** -+** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. -+** (Is there some way to relax this constraint?) -+** -+** (4) If pSrc is the right operand of a LEFT JOIN, then... -+** (4a) pExpr must come from an ON clause.. -+** (4b) and specifically the ON clause associated with the LEFT JOIN. -+** -+** (5) If pSrc is the right operand of a LEFT JOIN or the left -+** operand of a RIGHT JOIN, then pExpr must be from the WHERE -+** clause, not an ON clause. -+** -+** (6) Either: -+** -+** (6a) pExpr does not originate in an ON or USING clause, or -+** -+** (6b) The ON or USING clause from which pExpr is derived is -+** not to the left of a RIGHT JOIN (or FULL JOIN). -+** -+** Without this restriction, accepting pExpr as a single-table -+** constraint might move the the ON/USING filter expression -+** from the left side of a RIGHT JOIN over to the right side, -+** which leads to incorrect answers. See also restriction (9) -+** on push-down. - */ --SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ -- return exprIsConst(p, 3, iCur); -+SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( -+ Expr *pExpr, /* The constraint */ -+ const SrcList *pSrcList, /* Complete FROM clause */ -+ int iSrc, /* Which element of pSrcList to use */ -+ int bAllowSubq /* Allow non-correlated subqueries */ -+){ -+ const SrcItem *pSrc = &pSrcList->a[iSrc]; -+ if( pSrc->fg.jointype & JT_LTORJ ){ -+ return 0; /* rule (3) */ -+ } -+ if( pSrc->fg.jointype & JT_LEFT ){ -+ if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ -+ if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ -+ }else{ -+ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ -+ } -+ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) /* (6a) */ -+ && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (6b) */ -+ ){ -+ int jj; -+ for(jj=0; jjw.iJoin==pSrcList->a[jj].iCursor ){ -+ if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){ -+ return 0; /* restriction (6) */ -+ } -+ break; -+ } -+ } -+ } -+ /* Rules (1), (2a), and (2b) handled by the following: */ -+ return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq); - } - - -@@ -101858,7 +113059,7 @@ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){ - } - - /* Check if pExpr is a sub-select. If so, consider it variable. */ -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - pWalker->eCode = 0; - return WRC_Abort; - } -@@ -101919,7 +113120,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprLi - */ - SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){ - assert( isInit==0 || isInit==1 ); -- return exprIsConst(p, 4+isInit, 0); -+ return exprIsConst(0, p, 4+isInit); - } - - #ifdef SQLITE_ENABLE_CURSOR_HINTS -@@ -101945,8 +113146,12 @@ SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr *p){ - ** to fit in a 32-bit integer, return 1 and put the value of the integer - ** in *pValue. If the expression is not an integer or if it is too big - ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. -+** -+** If the pParse pointer is provided, then allow the expression p to be -+** a parameter (TK_VARIABLE) that is bound to an integer. -+** But if pParse is NULL, then p must be a pure integer literal. - */ --SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ -+SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue, Parse *pParse){ - int rc = 0; - if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */ - -@@ -101961,18 +113166,38 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ - } - switch( p->op ){ - case TK_UPLUS: { -- rc = sqlite3ExprIsInteger(p->pLeft, pValue); -+ rc = sqlite3ExprIsInteger(p->pLeft, pValue, 0); - break; - } - case TK_UMINUS: { -- int v; -- if( sqlite3ExprIsInteger(p->pLeft, &v) ){ -- assert( v!=(-2147483647-1) ); -+ int v = 0; -+ if( sqlite3ExprIsInteger(p->pLeft, &v, 0) ){ -+ assert( ((unsigned int)v)!=0x80000000 ); - *pValue = -v; - rc = 1; - } - break; - } -+ case TK_VARIABLE: { -+ sqlite3_value *pVal; -+ if( pParse==0 ) break; -+ if( NEVER(pParse->pVdbe==0) ) break; -+ if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) break; -+ sqlite3VdbeSetVarmask(pParse->pVdbe, p->iColumn); -+ pVal = sqlite3VdbeGetBoundValue(pParse->pReprepare, p->iColumn, -+ SQLITE_AFF_BLOB); -+ if( pVal ){ -+ if( sqlite3_value_type(pVal)==SQLITE_INTEGER ){ -+ sqlite3_int64 vv = sqlite3_value_int64(pVal); -+ if( vv == (vv & 0x7fffffff) ){ /* non-negative numbers only */ -+ *pValue = (int)vv; -+ rc = 1; -+ } -+ } -+ sqlite3ValueFree(pVal); -+ } -+ break; -+ } - default: break; - } - return rc; -@@ -101994,8 +113219,10 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ - */ - SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ - u8 op; -+ assert( p!=0 ); - while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ - p = p->pLeft; -+ assert( p!=0 ); - } - op = p->op; - if( op==TK_REGISTER ) op = p->op2; -@@ -102006,10 +113233,15 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ - case TK_BLOB: - return 0; - case TK_COLUMN: -- return ExprHasProperty(p, EP_CanBeNull) || -- p->y.pTab==0 || /* Reference to column of index on expression */ -- (p->iColumn>=0 -- && ALWAYS(p->y.pTab->aCol!=0) /* Defense against OOM problems */ -+ assert( ExprUseYTab(p) ); -+ return ExprHasProperty(p, EP_CanBeNull) -+ || NEVER(p->y.pTab==0) /* Reference to column of index on expr */ -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ || (p->iColumn==XN_ROWID && IsView(p->y.pTab)) -+#endif -+ || (p->iColumn>=0 -+ && p->y.pTab->aCol!=0 /* Possible due to prior error */ -+ && ALWAYS(p->iColumny.pTab->nCol) - && p->y.pTab->aCol[p->iColumn].notNull==0); - default: - return 1; -@@ -102069,21 +113301,36 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ - return 0; - } - -+/* -+** Return a pointer to a buffer containing a usable rowid alias for table -+** pTab. An alias is usable if there is not an explicit user-defined column -+** of the same name. -+*/ -+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab){ -+ const char *azOpt[] = {"_ROWID_", "ROWID", "OID"}; -+ int ii; -+ assert( VisibleRowid(pTab) ); -+ for(ii=0; iix.pSelect; - if( p->pPrior ) return 0; /* Not a compound SELECT */ -@@ -102098,10 +113345,10 @@ static Select *isCandidateForInOpt(Expr *pX){ - pSrc = p->pSrc; - assert( pSrc!=0 ); - if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */ -- if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */ -- pTab = pSrc->a[0].pTab; -+ if( pSrc->a[0].fg.isSubquery) return 0;/* FROM is not a subquery or view */ -+ pTab = pSrc->a[0].pSTab; - assert( pTab!=0 ); -- assert( pTab->pSelect==0 ); /* FROM clause is not a view */ -+ assert( !IsView(pTab) ); /* FROM clause is not a view */ - if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ - pEList = p->pEList; - assert( pEList!=0 ); -@@ -102139,13 +113386,13 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){ - ** The argument is an IN operator with a list (not a subquery) on the - ** right-hand side. Return TRUE if that list is constant. - */ --static int sqlite3InRhsIsConstant(Expr *pIn){ -+static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){ - Expr *pLHS; - int res; - assert( !ExprHasProperty(pIn, EP_xIsSelect) ); - pLHS = pIn->pLeft; - pIn->pLeft = 0; -- res = sqlite3ExprIsConstant(pIn); -+ res = sqlite3ExprIsConstant(pParse, pIn); - pIn->pLeft = pLHS; - return res; - } -@@ -102161,7 +113408,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ - ** all members of the RHS set, skipping duplicates. - ** - ** A cursor is opened on the b-tree object that is the RHS of the IN operator --** and pX->iTable is set to the index of that cursor. -+** and the *piTab parameter is set to the index of that cursor. - ** - ** The returned value of this function indicates the b-tree type, as follows: - ** -@@ -102169,7 +113416,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ - ** IN_INDEX_INDEX_ASC - The cursor was opened on an ascending index. - ** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index. - ** IN_INDEX_EPH - The cursor was opened on a specially created and --** populated epheremal table. -+** populated ephemeral table. - ** IN_INDEX_NOOP - No cursor was allocated. The IN operator must be - ** implemented as a sequence of comparisons. - ** -@@ -102181,7 +113428,10 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ - ** If the RHS of the IN operator is a list or a more complex subquery, then - ** an ephemeral table might need to be generated from the RHS and then - ** pX->iTable made to point to the ephemeral table instead of an --** existing table. -+** existing table. In this case, the creation and initialization of the -+** ephemeral table might be put inside of a subroutine, the EP_Subrtn flag -+** will be set on pX and the pX->y.sub fields will be set to show where -+** the subroutine is coded. - ** - ** The inFlags parameter must contain, at a minimum, one of the bits - ** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP but not both. If inFlags contains -@@ -102191,12 +113441,12 @@ static int sqlite3InRhsIsConstant(Expr *pIn){ - ** - ** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate - ** through the set members) then the b-tree must not contain duplicates. --** An epheremal table will be created unless the selected columns are guaranteed -+** An ephemeral table will be created unless the selected columns are guaranteed - ** to be unique - either because it is an INTEGER PRIMARY KEY or due to - ** a UNIQUE constraint or index. - ** - ** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used --** for fast set membership tests) then an epheremal table must -+** for fast set membership tests) then an ephemeral table must - ** be used unless is a single INTEGER PRIMARY KEY column or an - ** index can be found with the specified as its left-most. - ** -@@ -102242,19 +113492,20 @@ SQLITE_PRIVATE int sqlite3FindInIndex( - ){ - Select *p; /* SELECT to the right of IN operator */ - int eType = 0; /* Type of RHS table. IN_INDEX_* */ -- int iTab = pParse->nTab++; /* Cursor of the RHS table */ -+ int iTab; /* Cursor of the RHS table */ - int mustBeUnique; /* True if RHS must be unique */ - Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ - - assert( pX->op==TK_IN ); - mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; -+ iTab = pParse->nTab++; - - /* If the RHS of this IN(...) operator is a SELECT, and if it matters - ** whether or not the SELECT result contains NULL values, check whether - ** or not NULL is actually possible (it may not be, for example, due - ** to NOT NULL constraints in the schema). If no NULL values are possible, - ** set prRhsHasNull to 0 before continuing. */ -- if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){ -+ if( prRhsHasNull && ExprUseXSelect(pX) ){ - int i; - ExprList *pEList = pX->x.pSelect->pEList; - for(i=0; inExpr; i++){ -@@ -102278,11 +113529,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex( - assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ - assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ - assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ -- pTab = p->pSrc->a[0].pTab; -+ pTab = p->pSrc->a[0].pSTab; - - /* Code an OP_Transaction and OP_TableLock for . */ - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); -- assert( iDb>=0 && iDb=0 && iDbtnum, 0, pTab->zName); - -@@ -102355,7 +113606,6 @@ SQLITE_PRIVATE int sqlite3FindInIndex( - CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); - int j; - -- assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr ); - for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; - assert( pIdx->azColl[j] ); -@@ -102371,6 +113621,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( - if( aiMap ) aiMap[i] = j; - } - -+ assert( nExpr>0 && nExprx.pList->nExpr<=2) -+ && ExprUseXList(pX) -+ && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2) - ){ -+ pParse->nTab--; /* Back out the allocation of the unused cursor */ -+ iTab = -1; /* Cursor is not allocated */ - eType = IN_INDEX_NOOP; - } - -@@ -102455,14 +113708,14 @@ SQLITE_PRIVATE int sqlite3FindInIndex( - ** It is the responsibility of the caller to ensure that the returned - ** string is eventually freed using sqlite3DbFree(). - */ --static char *exprINAffinity(Parse *pParse, Expr *pExpr){ -+static char *exprINAffinity(Parse *pParse, const Expr *pExpr){ - Expr *pLeft = pExpr->pLeft; - int nVal = sqlite3ExprVectorSize(pLeft); -- Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0; -+ Select *pSelect = ExprUseXSelect(pExpr) ? pExpr->x.pSelect : 0; - char *zRet; - - assert( pExpr->op==TK_IN ); -- zRet = sqlite3DbMallocRaw(pParse->db, nVal+1); -+ zRet = sqlite3DbMallocRaw(pParse->db, 1+(i64)nVal); - if( zRet ){ - int i; - for(i=0; iflags & EP_xIsSelect ){ -+ if( ExprUseXSelect(pExpr) ){ - sqlite3SubselectError(pParse, pExpr->x.pSelect->pEList->nExpr, 1); - }else - #endif -@@ -102517,6 +113770,50 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ - } - } - -+#ifndef SQLITE_OMIT_SUBQUERY -+/* -+** Scan all previously generated bytecode looking for an OP_BeginSubrtn -+** that is compatible with pExpr. If found, add the y.sub values -+** to pExpr and return true. If not found, return false. -+*/ -+static int findCompatibleInRhsSubrtn( -+ Parse *pParse, /* Parsing context */ -+ Expr *pExpr, /* IN operator with RHS that we want to reuse */ -+ SubrtnSig *pNewSig /* Signature for the IN operator */ -+){ -+ VdbeOp *pOp, *pEnd; -+ SubrtnSig *pSig; -+ Vdbe *v; -+ -+ if( pNewSig==0 ) return 0; -+ if( (pParse->mSubrtnSig & (1<<(pNewSig->selId&7)))==0 ) return 0; -+ assert( pExpr->op==TK_IN ); -+ assert( !ExprUseYSub(pExpr) ); -+ assert( ExprUseXSelect(pExpr) ); -+ assert( pExpr->x.pSelect!=0 ); -+ assert( (pExpr->x.pSelect->selFlags & SF_All)==0 ); -+ v = pParse->pVdbe; -+ assert( v!=0 ); -+ pOp = sqlite3VdbeGetOp(v, 1); -+ pEnd = sqlite3VdbeGetLastOp(v); -+ for(; pOpp4type!=P4_SUBRTNSIG ) continue; -+ assert( pOp->opcode==OP_BeginSubrtn ); -+ pSig = pOp->p4.pSubrtnSig; -+ assert( pSig!=0 ); -+ if( !pSig->bComplete ) continue; -+ if( pNewSig->selId!=pSig->selId ) continue; -+ if( strcmp(pNewSig->zAff,pSig->zAff)!=0 ) continue; -+ pExpr->y.sub.iAddr = pSig->iAddr; -+ pExpr->y.sub.regReturn = pSig->regReturn; -+ pExpr->iTable = pSig->iTable; -+ ExprSetProperty(pExpr, EP_Subrtn); -+ return 1; -+ } -+ return 0; -+} -+#endif /* SQLITE_OMIT_SUBQUERY */ -+ - #ifndef SQLITE_OMIT_SUBQUERY - /* - ** Generate code that will construct an ephemeral table containing all terms -@@ -102527,7 +113824,7 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ - ** x IN (SELECT a FROM b) -- IN operator with subquery on the right - ** - ** The pExpr parameter is the IN operator. The cursor number for the --** constructed ephermeral table is returned. The first time the ephemeral -+** constructed ephemeral table is returned. The first time the ephemeral - ** table is computed, the cursor number is also stored in pExpr->iTable, - ** however the cursor number returned might not be the same, as it might - ** have been duplicated using OP_OpenDup. -@@ -102551,6 +113848,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( - KeyInfo *pKeyInfo = 0; /* Key information */ - int nVal; /* Size of vector pLeft */ - Vdbe *v; /* The prepared statement under construction */ -+ SubrtnSig *pSig = 0; /* Signature for this subroutine */ - - v = pParse->pVdbe; - assert( v!=0 ); -@@ -102566,31 +113864,60 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( - ** and reuse it many names. - */ - if( !ExprHasProperty(pExpr, EP_VarSelect) && pParse->iSelfTab==0 ){ -- /* Reuse of the RHS is allowed */ -- /* If this routine has already been coded, but the previous code -- ** might not have been invoked yet, so invoke it now as a subroutine. -+ /* Reuse of the RHS is allowed -+ ** -+ ** Compute a signature for the RHS of the IN operator to facility -+ ** finding and reusing prior instances of the same IN operator. - */ -- if( ExprHasProperty(pExpr, EP_Subrtn) ){ -+ assert( !ExprUseXSelect(pExpr) || pExpr->x.pSelect!=0 ); -+ if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_All)==0 ){ -+ pSig = sqlite3DbMallocRawNN(pParse->db, sizeof(pSig[0])); -+ if( pSig ){ -+ pSig->selId = pExpr->x.pSelect->selId; -+ pSig->zAff = exprINAffinity(pParse, pExpr); -+ } -+ } -+ -+ /* Check to see if there is a prior materialization of the RHS of -+ ** this IN operator. If there is, then make use of that prior -+ ** materialization rather than recomputing it. -+ */ -+ if( ExprHasProperty(pExpr, EP_Subrtn) -+ || findCompatibleInRhsSubrtn(pParse, pExpr, pSig) -+ ){ - addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", - pExpr->x.pSelect->selId)); - } -+ assert( ExprUseYSub(pExpr) ); - sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, - pExpr->y.sub.iAddr); -+ assert( iTab!=pExpr->iTable ); - sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); - sqlite3VdbeJumpHere(v, addrOnce); -+ if( pSig ){ -+ sqlite3DbFree(pParse->db, pSig->zAff); -+ sqlite3DbFree(pParse->db, pSig); -+ } - return; - } - - /* Begin coding the subroutine */ -+ assert( !ExprUseYWin(pExpr) ); - ExprSetProperty(pExpr, EP_Subrtn); - assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); - pExpr->y.sub.regReturn = ++pParse->nMem; - pExpr->y.sub.iAddr = -- sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; -- VdbeComment((v, "return address")); -- -+ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; -+ if( pSig ){ -+ pSig->bComplete = 0; -+ pSig->iAddr = pExpr->y.sub.iAddr; -+ pSig->regReturn = pExpr->y.sub.regReturn; -+ pSig->iTable = iTab; -+ pParse->mSubrtnSig = 1 << (pSig->selId&7); -+ sqlite3VdbeChangeP4(v, -1, (const char*)pSig, P4_SUBRTNSIG); -+ } - addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - } - -@@ -102604,7 +113931,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( - pExpr->iTable = iTab; - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal); - #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId)); - }else{ - VdbeComment((v, "RHS of IN operator")); -@@ -102612,7 +113939,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( - #endif - pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1); - -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - /* Case 1: expr IN (SELECT ...) - ** - ** Generate code to write the results of the select into the temporary -@@ -102627,19 +113954,39 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( - /* If the LHS and RHS of the IN operator do not match, that - ** error will have been caught long before we reach this point. */ - if( ALWAYS(pEList->nExpr==nVal) ){ -+ Select *pCopy; - SelectDest dest; - int i; -+ int rc; -+ int addrBloom = 0; - sqlite3SelectDestInit(&dest, SRT_Set, iTab); - dest.zAffSdst = exprINAffinity(pParse, pExpr); - pSelect->iLimit = 0; -+ if( addrOnce && OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ -+ int regBloom = ++pParse->nMem; -+ addrBloom = sqlite3VdbeAddOp2(v, OP_Blob, 10000, regBloom); -+ VdbeComment((v, "Bloom filter")); -+ dest.iSDParm2 = regBloom; -+ } - testcase( pSelect->selFlags & SF_Distinct ); - testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ -- if( sqlite3Select(pParse, pSelect, &dest) ){ -- sqlite3DbFree(pParse->db, dest.zAffSdst); -+ pCopy = sqlite3SelectDup(pParse->db, pSelect, 0); -+ rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest); -+ sqlite3SelectDelete(pParse->db, pCopy); -+ sqlite3DbFree(pParse->db, dest.zAffSdst); -+ if( addrBloom ){ -+ /* Remember that location of the Bloom filter in the P3 operand -+ ** of the OP_Once that began this subroutine. tag-202407032019 */ -+ sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2; -+ if( dest.iSDParm2==0 ){ -+ /* If the Bloom filter won't actually be used, keep it small */ -+ sqlite3VdbeGetOp(v, addrBloom)->p1 = 10; -+ } -+ } -+ if( rc ){ - sqlite3KeyInfoUnref(pKeyInfo); - return; - } -- sqlite3DbFree(pParse->db, dest.zAffSdst); - assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ - assert( pEList!=0 ); - assert( pEList->nExpr>0 ); -@@ -102686,7 +114033,8 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( - ** this code only executes once. Because for a non-constant - ** expression we need to rerun this code each time. - */ -- if( addrOnce && !sqlite3ExprIsConstant(pE2) ){ -+ if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){ -+ sqlite3VdbeChangeToNoop(v, addrOnce-1); - sqlite3VdbeChangeToNoop(v, addrOnce); - ExprClearProperty(pExpr, EP_Subrtn); - addrOnce = 0; -@@ -102700,14 +114048,20 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( - sqlite3ReleaseTempReg(pParse, r1); - sqlite3ReleaseTempReg(pParse, r2); - } -+ if( pSig ) pSig->bComplete = 1; - if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); - } - if( addrOnce ){ -+ sqlite3VdbeAddOp1(v, OP_NullRow, iTab); - sqlite3VdbeJumpHere(v, addrOnce); - /* Subroutine return */ -- sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); -- sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); -+ assert( ExprUseYSub(pExpr) ); -+ assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn -+ || pParse->nErr ); -+ sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, -+ pExpr->y.sub.iAddr, 1); -+ VdbeCoverage(v); - sqlite3ClearTempRegCache(pParse); - } - } -@@ -102735,15 +114089,37 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ - SelectDest dest; /* How to deal with SELECT result */ - int nReg; /* Registers to allocate */ - Expr *pLimit; /* New limit expression */ -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ int addrExplain; /* Address of OP_Explain instruction */ -+#endif - - Vdbe *v = pParse->pVdbe; - assert( v!=0 ); -+ if( pParse->nErr ) return 0; - testcase( pExpr->op==TK_EXISTS ); - testcase( pExpr->op==TK_SELECT ); - assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); -- assert( ExprHasProperty(pExpr, EP_xIsSelect) ); -+ assert( ExprUseXSelect(pExpr) ); - pSel = pExpr->x.pSelect; - -+ /* If this routine has already been coded, then invoke it as a -+ ** subroutine. */ -+ if( ExprHasProperty(pExpr, EP_Subrtn) ){ -+ ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); -+ assert( ExprUseYSub(pExpr) ); -+ sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, -+ pExpr->y.sub.iAddr); -+ return pExpr->iTable; -+ } -+ -+ /* Begin coding the subroutine */ -+ assert( !ExprUseYWin(pExpr) ); -+ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); -+ ExprSetProperty(pExpr, EP_Subrtn); -+ pExpr->y.sub.regReturn = ++pParse->nMem; -+ pExpr->y.sub.iAddr = -+ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; -+ - /* The evaluation of the EXISTS/SELECT must be repeated every time it - ** is encountered if any of the following is true: - ** -@@ -102755,22 +114131,6 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ - ** save the results, and reuse the same result on subsequent invocations. - */ - if( !ExprHasProperty(pExpr, EP_VarSelect) ){ -- /* If this routine has already been coded, then invoke it as a -- ** subroutine. */ -- if( ExprHasProperty(pExpr, EP_Subrtn) ){ -- ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); -- sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, -- pExpr->y.sub.iAddr); -- return pExpr->iTable; -- } -- -- /* Begin coding the subroutine */ -- ExprSetProperty(pExpr, EP_Subrtn); -- pExpr->y.sub.regReturn = ++pParse->nMem; -- pExpr->y.sub.iAddr = -- sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; -- VdbeComment((v, "return address")); -- - addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - } - -@@ -102784,8 +114144,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ - ** In both cases, the query is augmented with "LIMIT 1". Any - ** preexisting limit is discarded in place of the new LIMIT 1. - */ -- ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY %d", -+ ExplainQueryPlan2(addrExplain, (pParse, 1, "%sSCALAR SUBQUERY %d", - addrOnce?"":"CORRELATED ", pSel->selId)); -+ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, -1); - nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; - sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); - pParse->nMem += nReg; -@@ -102810,7 +114171,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ - pLimit = sqlite3PExpr(pParse, TK_NE, - sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit); - } -- sqlite3ExprDelete(db, pSel->pLimit->pLeft); -+ sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft); - pSel->pLimit->pLeft = pLimit; - }else{ - /* If there is no pre-existing limit add a limit of 1 */ -@@ -102819,19 +114180,25 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ - } - pSel->iLimit = 0; - if( sqlite3Select(pParse, pSel, &dest) ){ -+ pExpr->op2 = pExpr->op; -+ pExpr->op = TK_ERROR; - return 0; - } - pExpr->iTable = rReg = dest.iSDParm; - ExprSetVVAProperty(pExpr, EP_NoReduce); - if( addrOnce ){ - sqlite3VdbeJumpHere(v, addrOnce); -- -- /* Subroutine return */ -- sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); -- sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); -- sqlite3ClearTempRegCache(pParse); - } -+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); - -+ /* Subroutine return */ -+ assert( ExprUseYSub(pExpr) ); -+ assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn -+ || pParse->nErr ); -+ sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn, -+ pExpr->y.sub.iAddr, 1); -+ VdbeCoverage(v); -+ sqlite3ClearTempRegCache(pParse); - return rReg; - } - #endif /* SQLITE_OMIT_SUBQUERY */ -@@ -102845,7 +114212,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ - */ - SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){ - int nVector = sqlite3ExprVectorSize(pIn->pLeft); -- if( (pIn->flags & EP_xIsSelect) ){ -+ if( ExprUseXSelect(pIn) && !pParse->db->mallocFailed ){ - if( nVector!=pIn->x.pSelect->pEList->nExpr ){ - sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector); - return 1; -@@ -102914,9 +114281,7 @@ static void sqlite3ExprCodeIN( - if( sqlite3ExprCheckIN(pParse, pExpr) ) return; - zAff = exprINAffinity(pParse, pExpr); - nVector = sqlite3ExprVectorSize(pExpr->pLeft); -- aiMap = (int*)sqlite3DbMallocZero( -- pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 -- ); -+ aiMap = (int*)sqlite3DbMallocZero(pParse->db, nVector*sizeof(int)); - if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error; - - /* Attempt to compute the RHS. After this step, if anything other than -@@ -102979,13 +114344,15 @@ static void sqlite3ExprCodeIN( - ** This is step (1) in the in-operator.md optimized algorithm. - */ - if( eType==IN_INDEX_NOOP ){ -- ExprList *pList = pExpr->x.pList; -- CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); -+ ExprList *pList; -+ CollSeq *pColl; - int labelOk = sqlite3VdbeMakeLabel(pParse); - int r2, regToFree; - int regCkNull = 0; - int ii; -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); -+ assert( ExprUseXList(pExpr) ); -+ pList = pExpr->x.pList; -+ pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); - if( destIfNull!=destIfFalse ){ - regCkNull = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull); -@@ -103033,9 +114400,9 @@ static void sqlite3ExprCodeIN( - }else{ - destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse); - } -- if( pParse->nErr ) goto sqlite3ExprCodeIN_finished; - for(i=0; ipLeft, i); -+ if( pParse->nErr ) goto sqlite3ExprCodeIN_oom_error; - if( sqlite3ExprCanBeNull(p) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2); - VdbeCoverage(v); -@@ -103057,6 +114424,15 @@ static void sqlite3ExprCodeIN( - sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector); - if( destIfFalse==destIfNull ){ - /* Combine Step 3 and Step 5 into a single opcode */ -+ if( ExprHasProperty(pExpr, EP_Subrtn) ){ -+ const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); -+ assert( pOp->opcode==OP_Once || pParse->nErr ); -+ if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */ -+ assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ); -+ sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, -+ rLhs, nVector); VdbeCoverage(v); -+ } -+ } - sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, - rLhs, nVector); VdbeCoverage(v); - goto sqlite3ExprCodeIN_finished; -@@ -103173,11 +114549,12 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ - c = sqlite3DecOrHexToI64(z, &value); - if( (c==3 && !negFlag) || (c==2) || (negFlag && value==SMALLEST_INT64)){ - #ifdef SQLITE_OMIT_FLOATING_POINT -- sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); -+ sqlite3ErrorMsg(pParse, "oversized integer: %s%#T", negFlag?"-":"",pExpr); - #else - #ifndef SQLITE_OMIT_HEX_INTEGER - if( sqlite3_strnicmp(z,"0x",2)==0 ){ -- sqlite3ErrorMsg(pParse, "hex literal too big: %s%s", negFlag?"-":"",z); -+ sqlite3ErrorMsg(pParse, "hex literal too big: %s%#T", -+ negFlag?"-":"",pExpr); - }else - #endif - { -@@ -103221,12 +114598,14 @@ SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn( - ** and store the result in register regOut - */ - SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn( -- Parse *pParse, -- Column *pCol, -- int regOut -+ Parse *pParse, /* Parsing context */ -+ Table *pTab, /* Table containing the generated column */ -+ Column *pCol, /* The generated column */ -+ int regOut /* Put the result in this register */ - ){ - int iAddr; - Vdbe *v = pParse->pVdbe; -+ int nErr = pParse->nErr; - assert( v!=0 ); - assert( pParse->iSelfTab!=0 ); - if( pParse->iSelfTab>0 ){ -@@ -103234,11 +114613,12 @@ SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn( - }else{ - iAddr = 0; - } -- sqlite3ExprCodeCopy(pParse, pCol->pDflt, regOut); -+ sqlite3ExprCodeCopy(pParse, sqlite3ColumnExpr(pTab,pCol), regOut); - if( pCol->affinity>=SQLITE_AFF_TEXT ){ - sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1); - } - if( iAddr ) sqlite3VdbeJumpHere(v, iAddr); -+ if( pParse->nErr>nErr ) pParse->db->errByteOffset = -1; - } - #endif /* SQLITE_OMIT_GENERATED_COLUMNS */ - -@@ -103254,12 +114634,11 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable( - ){ - Column *pCol; - assert( v!=0 ); -- if( pTab==0 ){ -- sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut); -- return; -- } -+ assert( pTab!=0 ); -+ assert( iCol!=XN_EXPR ); - if( iCol<0 || iCol==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); -+ VdbeComment((v, "%s.rowid", pTab->zName)); - }else{ - int op; - int x; -@@ -103270,12 +114649,13 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable( - }else if( (pCol = &pTab->aCol[iCol])->colFlags & COLFLAG_VIRTUAL ){ - Parse *pParse = sqlite3VdbeParser(v); - if( pCol->colFlags & COLFLAG_BUSY ){ -- sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pCol->zName); -+ sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", -+ pCol->zCnName); - }else{ - int savedSelfTab = pParse->iSelfTab; - pCol->colFlags |= COLFLAG_BUSY; - pParse->iSelfTab = iTabCur+1; -- sqlite3ExprCodeGeneratedColumn(pParse, pCol, regOut); -+ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, regOut); - pParse->iSelfTab = savedSelfTab; - pCol->colFlags &= ~COLFLAG_BUSY; - } -@@ -103311,10 +114691,13 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn( - u8 p5 /* P5 value for OP_Column + FLAGS */ - ){ - assert( pParse->pVdbe!=0 ); -+ assert( (p5 & (OPFLAG_NOCHNG|OPFLAG_TYPEOFARG|OPFLAG_LENGTHARG))==p5 ); -+ assert( IsVirtual(pTab) || (p5 & OPFLAG_NOCHNG)==0 ); - sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pTab, iTable, iColumn, iReg); - if( p5 ){ -- VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe,-1); -+ VdbeOp *pOp = sqlite3VdbeGetLastOp(pParse->pVdbe); - if( pOp->opcode==OP_Column ) pOp->p5 = p5; -+ if( pOp->opcode==OP_VColumn ) pOp->p5 = (p5 & OPFLAG_NOCHNG); - } - return iReg; - } -@@ -103332,17 +114715,22 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int n - ** register iReg. The caller must ensure that iReg already contains - ** the correct value for the expression. - */ --static void exprToRegister(Expr *pExpr, int iReg){ -+SQLITE_PRIVATE void sqlite3ExprToRegister(Expr *pExpr, int iReg){ - Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); -- p->op2 = p->op; -- p->op = TK_REGISTER; -- p->iTable = iReg; -- ExprClearProperty(p, EP_Skip); -+ if( NEVER(p==0) ) return; -+ if( p->op==TK_REGISTER ){ -+ assert( p->iTable==iReg ); -+ }else{ -+ p->op2 = p->op; -+ p->op = TK_REGISTER; -+ p->iTable = iReg; -+ ExprClearProperty(p, EP_Skip); -+ } - } - - /* - ** Evaluate an expression (either a vector or a scalar expression) and store --** the result in continguous temporary registers. Return the index of -+** the result in contiguous temporary registers. Return the index of - ** the first register used to store the result. - ** - ** If the returned result register is a temporary scalar, then also write -@@ -103367,6 +114755,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ - int i; - iResult = pParse->nMem+1; - pParse->nMem += nResult; -+ assert( ExprUseXList(p) ); - for(i=0; ix.pList->a[i].pExpr, i+iResult); - } -@@ -103380,8 +114769,8 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ - ** so that a subsequent copy will not be merged into this one. - */ - static void setDoNotMergeFlagOnCopy(Vdbe *v){ -- if( sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){ -- sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */ -+ if( sqlite3VdbeGetLastOp(v)->opcode==OP_Copy ){ -+ sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergeable */ - } - } - -@@ -103427,7 +114816,17 @@ static int exprCodeInlineFunction( - caseExpr.x.pList = pFarg; - return sqlite3ExprCodeTarget(pParse, &caseExpr, target); - } -- -+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC -+ case INLINEFUNC_sqlite_offset: { -+ Expr *pArg = pFarg->a[0].pExpr; -+ if( pArg->op==TK_COLUMN && pArg->iTable>=0 ){ -+ sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); -+ }else{ -+ sqlite3VdbeAddOp2(v, OP_Null, 0, target); -+ } -+ break; -+ } -+#endif - default: { - /* The UNLIKELY() function is a no-op. The result is the value - ** of the first argument. -@@ -103441,6 +114840,7 @@ static int exprCodeInlineFunction( - ** Test-only SQL functions that are only usable if enabled - ** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS - */ -+#if !defined(SQLITE_UNTESTABLE) - case INLINEFUNC_expr_compare: { - /* Compare two expressions using sqlite3ExprCompare() */ - assert( nFarg==2 ); -@@ -103460,13 +114860,13 @@ static int exprCodeInlineFunction( - } - - case INLINEFUNC_implies_nonnull_row: { -- /* REsult of sqlite3ExprImpliesNonNullRow() */ -+ /* Result of sqlite3ExprImpliesNonNullRow() */ - Expr *pA1; - assert( nFarg==2 ); - pA1 = pFarg->a[1].pExpr; - if( pA1->op==TK_COLUMN ){ - sqlite3VdbeAddOp2(v, OP_Integer, -- sqlite3ExprImpliesNonNullRow(pFarg->a[0].pExpr,pA1->iTable), -+ sqlite3ExprImpliesNonNullRow(pFarg->a[0].pExpr,pA1->iTable,1), - target); - }else{ - sqlite3VdbeAddOp2(v, OP_Null, 0, target); -@@ -103474,25 +114874,184 @@ static int exprCodeInlineFunction( - break; - } - --#ifdef SQLITE_DEBUG - case INLINEFUNC_affinity: { - /* The AFFINITY() function evaluates to a string that describes - ** the type affinity of the argument. This is used for testing of - ** the SQLite type logic. - */ -- const char *azAff[] = { "blob", "text", "numeric", "integer", "real" }; -+ const char *azAff[] = { "blob", "text", "numeric", "integer", -+ "real", "flexnum" }; - char aff; - assert( nFarg==1 ); - aff = sqlite3ExprAffinity(pFarg->a[0].pExpr); -+ assert( aff<=SQLITE_AFF_NONE -+ || (aff>=SQLITE_AFF_BLOB && aff<=SQLITE_AFF_FLEXNUM) ); - sqlite3VdbeLoadString(v, target, - (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]); - break; - } --#endif -+#endif /* !defined(SQLITE_UNTESTABLE) */ - } - return target; - } - -+/* -+** Expression Node callback for sqlite3ExprCanReturnSubtype(). -+** -+** Only a function call is able to return a subtype. So if the node -+** is not a function call, return WRC_Prune immediately. -+** -+** A function call is able to return a subtype if it has the -+** SQLITE_RESULT_SUBTYPE property. -+** -+** Assume that every function is able to pass-through a subtype from -+** one of its argument (using sqlite3_result_value()). Most functions -+** are not this way, but we don't have a mechanism to distinguish those -+** that are from those that are not, so assume they all work this way. -+** That means that if one of its arguments is another function and that -+** other function is able to return a subtype, then this function is -+** able to return a subtype. -+*/ -+static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ -+ int n; -+ FuncDef *pDef; -+ sqlite3 *db; -+ if( pExpr->op!=TK_FUNCTION ){ -+ return WRC_Prune; -+ } -+ assert( ExprUseXList(pExpr) ); -+ db = pWalker->pParse->db; -+ n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; -+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); -+ if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ -+ pWalker->eCode = 1; -+ return WRC_Prune; -+ } -+ return WRC_Continue; -+} -+ -+/* -+** Return TRUE if expression pExpr is able to return a subtype. -+** -+** A TRUE return does not guarantee that a subtype will be returned. -+** It only indicates that a subtype return is possible. False positives -+** are acceptable as they only disable an optimization. False negatives, -+** on the other hand, can lead to incorrect answers. -+*/ -+static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ -+ Walker w; -+ memset(&w, 0, sizeof(w)); -+ w.pParse = pParse; -+ w.xExprCallback = exprNodeCanReturnSubtype; -+ sqlite3WalkExpr(&w, pExpr); -+ return w.eCode; -+} -+ -+ -+/* -+** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr. -+** If it is, then resolve the expression by reading from the index and -+** return the register into which the value has been read. If pExpr is -+** not an indexed expression, then return negative. -+*/ -+static SQLITE_NOINLINE int sqlite3IndexedExprLookup( -+ Parse *pParse, /* The parsing context */ -+ Expr *pExpr, /* The expression to potentially bypass */ -+ int target /* Where to store the result of the expression */ -+){ -+ IndexedExpr *p; -+ Vdbe *v; -+ for(p=pParse->pIdxEpr; p; p=p->pIENext){ -+ u8 exprAff; -+ int iDataCur = p->iDataCur; -+ if( iDataCur<0 ) continue; -+ if( pParse->iSelfTab ){ -+ if( p->iDataCur!=pParse->iSelfTab-1 ) continue; -+ iDataCur = -1; -+ } -+ if( sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue; -+ assert( p->aff>=SQLITE_AFF_BLOB && p->aff<=SQLITE_AFF_NUMERIC ); -+ exprAff = sqlite3ExprAffinity(pExpr); -+ if( (exprAff<=SQLITE_AFF_BLOB && p->aff!=SQLITE_AFF_BLOB) -+ || (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT) -+ || (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC) -+ ){ -+ /* Affinity mismatch on a generated column */ -+ continue; -+ } -+ -+ -+ /* Functions that might set a subtype should not be replaced by the -+ ** value taken from an expression index if they are themselves an -+ ** argument to another scalar function or aggregate. -+ ** https://sqlite.org/forum/forumpost/68d284c86b082c3e */ -+ if( ExprHasProperty(pExpr, EP_SubtArg) -+ && sqlite3ExprCanReturnSubtype(pParse, pExpr) -+ ){ -+ continue; -+ } -+ -+ v = pParse->pVdbe; -+ assert( v!=0 ); -+ if( p->bMaybeNullRow ){ -+ /* If the index is on a NULL row due to an outer join, then we -+ ** cannot extract the value from the index. The value must be -+ ** computed using the original expression. */ -+ int addr = sqlite3VdbeCurrentAddr(v); -+ sqlite3VdbeAddOp3(v, OP_IfNullRow, p->iIdxCur, addr+3, target); -+ VdbeCoverage(v); -+ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target); -+ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol)); -+ sqlite3VdbeGoto(v, 0); -+ p = pParse->pIdxEpr; -+ pParse->pIdxEpr = 0; -+ sqlite3ExprCode(pParse, pExpr, target); -+ pParse->pIdxEpr = p; -+ sqlite3VdbeJumpHere(v, addr+2); -+ }else{ -+ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target); -+ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol)); -+ } -+ return target; -+ } -+ return -1; /* Not found */ -+} -+ -+ -+/* -+** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This -+** function checks the Parse.pIdxPartExpr list to see if this column -+** can be replaced with a constant value. If so, it generates code to -+** put the constant value in a register (ideally, but not necessarily, -+** register iTarget) and returns the register number. -+** -+** Or, if the TK_COLUMN cannot be replaced by a constant, zero is -+** returned. -+*/ -+static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){ -+ IndexedExpr *p; -+ for(p=pParse->pIdxPartExpr; p; p=p->pIENext){ -+ if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){ -+ Vdbe *v = pParse->pVdbe; -+ int addr = 0; -+ int ret; -+ -+ if( p->bMaybeNullRow ){ -+ addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur); -+ } -+ ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget); -+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0, -+ (const char*)&p->aff, 1); -+ if( addr ){ -+ sqlite3VdbeJumpHere(v, addr); -+ sqlite3VdbeChangeP3(v, addr, ret); -+ } -+ return ret; -+ } -+ } -+ return 0; -+} -+ - - /* - ** Generate code into the current Vdbe to evaluate the given -@@ -103521,33 +115080,58 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) - expr_code_doover: - if( pExpr==0 ){ - op = TK_NULL; -+ }else if( pParse->pIdxEpr!=0 -+ && !ExprHasProperty(pExpr, EP_Leaf) -+ && (r1 = sqlite3IndexedExprLookup(pParse, pExpr, target))>=0 -+ ){ -+ return r1; - }else{ - assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); - op = pExpr->op; - } -+ assert( op!=TK_ORDER ); - switch( op ){ - case TK_AGG_COLUMN: { - AggInfo *pAggInfo = pExpr->pAggInfo; - struct AggInfo_col *pCol; - assert( pAggInfo!=0 ); -- assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); -+ assert( pExpr->iAgg>=0 ); -+ if( pExpr->iAgg>=pAggInfo->nColumn ){ -+ /* Happens when the left table of a RIGHT JOIN is null and -+ ** is using an expression index */ -+ sqlite3VdbeAddOp2(v, OP_Null, 0, target); -+#ifdef SQLITE_VDBE_COVERAGE -+ /* Verify that the OP_Null above is exercised by tests -+ ** tag-20230325-2 */ -+ sqlite3VdbeAddOp3(v, OP_NotNull, target, 1, 20230325); -+ VdbeCoverageNeverTaken(v); -+#endif -+ break; -+ } - pCol = &pAggInfo->aCol[pExpr->iAgg]; - if( !pAggInfo->directMode ){ -- assert( pCol->iMem>0 ); -- return pCol->iMem; -+ return AggInfoColumnReg(pAggInfo, pExpr->iAgg); - }else if( pAggInfo->useSortingIdx ){ - Table *pTab = pCol->pTab; - sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, - pCol->iSorterColumn, target); -- if( pCol->iColumn<0 ){ -+ if( pTab==0 ){ -+ /* No comment added */ -+ }else if( pCol->iColumn<0 ){ - VdbeComment((v,"%s.rowid",pTab->zName)); - }else{ -- VdbeComment((v,"%s.%s",pTab->zName,pTab->aCol[pCol->iColumn].zName)); -+ VdbeComment((v,"%s.%s", -+ pTab->zName, pTab->aCol[pCol->iColumn].zCnName)); - if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, target); - } - } - return target; -+ }else if( pExpr->y.pTab==0 ){ -+ /* This case happens when the argument to an aggregate function -+ ** is rewritten by aggregateConvertIndexedExprRefToColumn() */ -+ sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, pExpr->iColumn, target); -+ return target; - } - /* Otherwise, fall thru into the TK_COLUMN case */ - /* no break */ deliberate_fall_through -@@ -103558,19 +115142,17 @@ expr_code_doover: - if( ExprHasProperty(pExpr, EP_FixedCol) ){ - /* This COLUMN expression is really a constant due to WHERE clause - ** constraints, and that constant is coded by the pExpr->pLeft -- ** expresssion. However, make sure the constant has the correct -+ ** expression. However, make sure the constant has the correct - ** datatype by applying the Affinity of the table column to the - ** constant. - */ - int aff; - iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target); -- if( pExpr->y.pTab ){ -- aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); -- }else{ -- aff = pExpr->affExpr; -- } -+ assert( ExprUseYTab(pExpr) ); -+ assert( pExpr->y.pTab!=0 ); -+ aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn); - if( aff>SQLITE_AFF_BLOB ){ -- static const char zAff[] = "B\000C\000D\000E"; -+ static const char zAff[] = "B\000C\000D\000E\000F"; - assert( SQLITE_AFF_BLOB=='A' ); - assert( SQLITE_AFF_TEXT=='B' ); - sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0, -@@ -103587,9 +115169,11 @@ expr_code_doover: - ** immediately prior to the first column. - */ - Column *pCol; -- Table *pTab = pExpr->y.pTab; -+ Table *pTab; - int iSrc; - int iCol = pExpr->iColumn; -+ assert( ExprUseYTab(pExpr) ); -+ pTab = pExpr->y.pTab; - assert( pTab!=0 ); - assert( iCol>=XN_ROWID ); - assert( iColnCol ); -@@ -103603,12 +115187,12 @@ expr_code_doover: - if( pCol->colFlags & COLFLAG_GENERATED ){ - if( pCol->colFlags & COLFLAG_BUSY ){ - sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", -- pCol->zName); -+ pCol->zCnName); - return 0; - } - pCol->colFlags |= COLFLAG_BUSY; - if( pCol->colFlags & COLFLAG_NOTAVAIL ){ -- sqlite3ExprCodeGeneratedColumn(pParse, pCol, iSrc); -+ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, iSrc); - } - pCol->colFlags &= ~(COLFLAG_BUSY|COLFLAG_NOTAVAIL); - return iSrc; -@@ -103627,12 +115211,16 @@ expr_code_doover: - iTab = pParse->iSelfTab - 1; - } - } -+ else if( pParse->pIdxPartExpr -+ && 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target)) -+ ){ -+ return r1; -+ } -+ assert( ExprUseYTab(pExpr) ); -+ assert( pExpr->y.pTab!=0 ); - iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab, - pExpr->iColumn, iTab, target, - pExpr->op2); -- if( pExpr->y.pTab==0 && pExpr->affExpr==SQLITE_AFF_REAL ){ -- sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); -- } - return iReg; - } - case TK_INTEGER: { -@@ -103655,12 +115243,18 @@ expr_code_doover: - sqlite3VdbeLoadString(v, target, pExpr->u.zToken); - return target; - } -+ case TK_NULLS: { -+ /* Set a range of registers to NULL. pExpr->y.nReg registers starting -+ ** with target */ -+ sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1); -+ return target; -+ } - default: { - /* Make NULL the default case so that if a bug causes an illegal - ** Expr node to be passed into this function, it will be handled - ** sanely and not crash. But keep the assert() to bring the problem - ** to the attention of the developers. */ -- assert( op==TK_NULL ); -+ assert( op==TK_NULL || op==TK_ERROR || pParse->db->mallocFailed ); - sqlite3VdbeAddOp2(v, OP_Null, 0, target); - return target; - } -@@ -103685,12 +115279,6 @@ expr_code_doover: - assert( pExpr->u.zToken!=0 ); - assert( pExpr->u.zToken[0]!=0 ); - sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); -- if( pExpr->u.zToken[1]!=0 ){ -- const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn); -- assert( pExpr->u.zToken[0]=='?' || (z && !strcmp(pExpr->u.zToken, z)) ); -- pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */ -- sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC); -- } - return target; - } - case TK_REGISTER: { -@@ -103699,11 +115287,9 @@ expr_code_doover: - #ifndef SQLITE_OMIT_CAST - case TK_CAST: { - /* Expressions of the form: CAST(pLeft AS token) */ -- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); -- if( inReg!=target ){ -- sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); -- inReg = target; -- } -+ sqlite3ExprCode(pParse, pExpr->pLeft, target); -+ assert( inReg==target ); -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3VdbeAddOp2(v, OP_Cast, target, - sqlite3AffinityType(pExpr->u.zToken, 0)); - return inReg; -@@ -103726,8 +115312,9 @@ expr_code_doover: - }else{ - r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); - r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); -- codeCompare(pParse, pLeft, pExpr->pRight, op, -- r1, r2, inReg, SQLITE_STOREP2 | p5, -+ sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg); -+ codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2, -+ sqlite3VdbeCurrentAddr(v)+2, p5, - ExprHasProperty(pExpr,EP_Commuted)); - assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); - assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); -@@ -103735,6 +115322,11 @@ expr_code_doover: - assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); - assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); - assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); -+ if( p5==SQLITE_NULLEQ ){ -+ sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg); -+ }else{ -+ sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2); -+ } - testcase( regFree1==0 ); - testcase( regFree2==0 ); - } -@@ -103837,9 +115429,9 @@ expr_code_doover: - || NEVER(pExpr->iAgg>=pInfo->nFunc) - ){ - assert( !ExprHasProperty(pExpr, EP_IntValue) ); -- sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken); -+ sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr); - }else{ -- return pInfo->aFunc[pExpr->iAgg].iMem; -+ return AggInfoFuncReg(pInfo, pExpr->iAgg); - } - break; - } -@@ -103860,13 +115452,15 @@ expr_code_doover: - } - #endif - -- if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){ -+ if( ConstFactorOk(pParse) -+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr) -+ ){ - /* SQL functions can be expensive. So try to avoid running them - ** multiple times if we know they always give the same result */ - return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); - } -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - assert( !ExprHasProperty(pExpr, EP_TokenOnly) ); -+ assert( ExprUseXList(pExpr) ); - pFarg = pExpr->x.pList; - nFarg = pFarg ? pFarg->nExpr : 0; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); -@@ -103878,10 +115472,10 @@ expr_code_doover: - } - #endif - if( pDef==0 || pDef->xFinalize!=0 ){ -- sqlite3ErrorMsg(pParse, "unknown function: %s()", zId); -+ sqlite3ErrorMsg(pParse, "unknown function: %#T()", pExpr); - break; - } -- if( pDef->funcFlags & SQLITE_FUNC_INLINE ){ -+ if( (pDef->funcFlags & SQLITE_FUNC_INLINE)!=0 && ALWAYS(pFarg!=0) ){ - assert( (pDef->funcFlags & SQLITE_FUNC_UNSAFE)==0 ); - assert( (pDef->funcFlags & SQLITE_FUNC_DIRECT)==0 ); - return exprCodeInlineFunction(pParse, pFarg, -@@ -103891,7 +115485,7 @@ expr_code_doover: - } - - for(i=0; ia[i].pExpr) ){ -+ if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){ - testcase( i==31 ); - constMask |= MASKBIT32(i); - } -@@ -103907,10 +115501,10 @@ expr_code_doover: - r1 = sqlite3GetTempRange(pParse, nFarg); - } - -- /* For length() and typeof() functions with a column argument, -+ /* For length() and typeof() and octet_length() functions, - ** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG -- ** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data -- ** loading. -+ ** or OPFLAG_TYPEOFARG or OPFLAG_BYTELENARG respectively, to avoid -+ ** unnecessary data loading. - */ - if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){ - u8 exprOp; -@@ -103920,14 +115514,16 @@ expr_code_doover: - if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){ - assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG ); - assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG ); -- testcase( pDef->funcFlags & OPFLAG_LENGTHARG ); -- pFarg->a[0].pExpr->op2 = -- pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG); -+ assert( SQLITE_FUNC_BYTELEN==OPFLAG_BYTELENARG ); -+ assert( (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG)==OPFLAG_BYTELENARG ); -+ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_LENGTHARG ); -+ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_TYPEOFARG ); -+ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_BYTELENARG); -+ pFarg->a[0].pExpr->op2 = pDef->funcFlags & OPFLAG_BYTELENARG; - } - } - -- sqlite3ExprCodeExprList(pParse, pFarg, r1, 0, -- SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR); -+ sqlite3ExprCodeExprList(pParse, pFarg, r1, 0, SQLITE_ECEL_FACTOR); - }else{ - r1 = 0; - } -@@ -103954,20 +115550,8 @@ expr_code_doover: - if( !pColl ) pColl = db->pDfltColl; - sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); - } --#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC -- if( pDef->funcFlags & SQLITE_FUNC_OFFSET ){ -- Expr *pArg = pFarg->a[0].pExpr; -- if( pArg->op==TK_COLUMN ){ -- sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target); -- }else{ -- sqlite3VdbeAddOp2(v, OP_Null, 0, target); -- } -- }else --#endif -- { -- sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, -- pDef, pExpr->op2); -- } -+ sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg, -+ pDef, pExpr->op2); - if( nFarg ){ - if( constMask==0 ){ - sqlite3ReleaseTempRange(pParse, r1, nFarg); -@@ -103985,7 +115569,10 @@ expr_code_doover: - testcase( op==TK_SELECT ); - if( pParse->db->mallocFailed ){ - return 0; -- }else if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ -+ }else if( op==TK_SELECT -+ && ALWAYS( ExprUseXSelect(pExpr) ) -+ && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 -+ ){ - sqlite3SubselectError(pParse, nCol, 1); - }else{ - return sqlite3CodeSubselect(pParse, pExpr); -@@ -103994,17 +115581,18 @@ expr_code_doover: - } - case TK_SELECT_COLUMN: { - int n; -- if( pExpr->pLeft->iTable==0 ){ -- pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft); -+ Expr *pLeft = pExpr->pLeft; -+ if( pLeft->iTable==0 || pParse->withinRJSubrtn > pLeft->op2 ){ -+ pLeft->iTable = sqlite3CodeSubselect(pParse, pLeft); -+ pLeft->op2 = pParse->withinRJSubrtn; - } -- assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); -- if( pExpr->iTable!=0 -- && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft)) -- ){ -+ assert( pLeft->op==TK_SELECT || pLeft->op==TK_ERROR ); -+ n = sqlite3ExprVectorSize(pLeft); -+ if( pExpr->iTable!=n ){ - sqlite3ErrorMsg(pParse, "%d columns assigned %d values", - pExpr->iTable, n); - } -- return pExpr->pLeft->iTable + pExpr->iColumn; -+ return pLeft->iTable + pExpr->iColumn; - } - case TK_IN: { - int destIfFalse = sqlite3VdbeMakeLabel(pParse); -@@ -104035,8 +115623,24 @@ expr_code_doover: - exprCodeBetween(pParse, pExpr, target, 0, 0); - return target; - } -+ case TK_COLLATE: { -+ if( !ExprHasProperty(pExpr, EP_Collate) ){ -+ /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called -+ ** "SOFT-COLLATE" that is added to constraints that are pushed down -+ ** from outer queries into sub-queries by the WHERE-clause push-down -+ ** optimization. Clear subtypes as subtypes may not cross a subquery -+ ** boundary. -+ */ -+ assert( pExpr->pLeft ); -+ sqlite3ExprCode(pParse, pExpr->pLeft, target); -+ sqlite3VdbeAddOp1(v, OP_ClrSubtype, target); -+ return target; -+ }else{ -+ pExpr = pExpr->pLeft; -+ goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */ -+ } -+ } - case TK_SPAN: -- case TK_COLLATE: - case TK_UPLUS: { - pExpr = pExpr->pLeft; - goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */ -@@ -104068,9 +115672,14 @@ expr_code_doover: - ** p1==1 -> old.a p1==4 -> new.a - ** p1==2 -> old.b p1==5 -> new.b - */ -- Table *pTab = pExpr->y.pTab; -- int iCol = pExpr->iColumn; -- int p1 = pExpr->iTable * (pTab->nCol+1) + 1 -+ Table *pTab; -+ int iCol; -+ int p1; -+ -+ assert( ExprUseYTab(pExpr) ); -+ pTab = pExpr->y.pTab; -+ iCol = pExpr->iColumn; -+ p1 = pExpr->iTable * (pTab->nCol+1) + 1 - + sqlite3TableColumnToStorage(pTab, iCol); - - assert( pExpr->iTable==0 || pExpr->iTable==1 ); -@@ -104081,7 +115690,7 @@ expr_code_doover: - sqlite3VdbeAddOp2(v, OP_Param, p1, target); - VdbeComment((v, "r[%d]=%s.%s", target, - (pExpr->iTable ? "new" : "old"), -- (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zName) -+ (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zCnName) - )); - - #ifndef SQLITE_OMIT_FLOATING_POINT -@@ -104111,16 +115720,34 @@ expr_code_doover: - case TK_IF_NULL_ROW: { - int addrINR; - u8 okConstFactor = pParse->okConstFactor; -- addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable); -- /* Temporarily disable factoring of constant expressions, since -- ** even though expressions may appear to be constant, they are not -- ** really constant because they originate from the right-hand side -- ** of a LEFT JOIN. */ -- pParse->okConstFactor = 0; -- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); -+ AggInfo *pAggInfo = pExpr->pAggInfo; -+ if( pAggInfo ){ -+ assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); -+ if( !pAggInfo->directMode ){ -+ inReg = AggInfoColumnReg(pAggInfo, pExpr->iAgg); -+ break; -+ } -+ if( pExpr->pAggInfo->useSortingIdx ){ -+ sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, -+ pAggInfo->aCol[pExpr->iAgg].iSorterColumn, -+ target); -+ inReg = target; -+ break; -+ } -+ } -+ addrINR = sqlite3VdbeAddOp3(v, OP_IfNullRow, pExpr->iTable, 0, target); -+ /* The OP_IfNullRow opcode above can overwrite the result register with -+ ** NULL. So we have to ensure that the result register is not a value -+ ** that is suppose to be a constant. Two defenses are needed: -+ ** (1) Temporarily disable factoring of constant expressions -+ ** (2) Make sure the computed value really is stored in register -+ ** "target" and not someplace else. -+ */ -+ pParse->okConstFactor = 0; /* note (1) above */ -+ sqlite3ExprCode(pParse, pExpr->pLeft, target); -+ assert( target==inReg ); - pParse->okConstFactor = okConstFactor; - sqlite3VdbeJumpHere(v, addrINR); -- sqlite3VdbeChangeP3(v, addrINR, inReg); - break; - } - -@@ -104158,7 +115785,7 @@ expr_code_doover: - Expr *pDel = 0; - sqlite3 *db = pParse->db; - -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList ); -+ assert( ExprUseXList(pExpr) && pExpr->x.pList!=0 ); - assert(pExpr->x.pList->nExpr > 0); - pEList = pExpr->x.pList; - aListelem = pEList->a; -@@ -104171,7 +115798,7 @@ expr_code_doover: - break; - } - testcase( pX->op==TK_COLUMN ); -- exprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); -+ sqlite3ExprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); - testcase( regFree1==0 ); - memset(&opCompare, 0, sizeof(opCompare)); - opCompare.op = TK_EQ; -@@ -104225,15 +115852,14 @@ expr_code_doover: - } - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - if( pExpr->affExpr==OE_Ignore ){ -- sqlite3VdbeAddOp4( -- v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); -+ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, OE_Ignore); - VdbeCoverage(v); - }else{ -- sqlite3HaltConstraint(pParse, -+ r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); -+ sqlite3VdbeAddOp3(v, OP_Halt, - pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR, -- pExpr->affExpr, pExpr->u.zToken, 0, 0); -+ pExpr->affExpr, r1); - } -- - break; - } - #endif -@@ -104252,9 +115878,9 @@ expr_code_doover: - ** once. If no functions are involved, then factor the code out and put it at - ** the end of the prepared statement in the initialization section. - ** --** If regDest>=0 then the result is always stored in that register and the -+** If regDest>0 then the result is always stored in that register and the - ** result is not reusable. If regDest<0 then this routine is free to --** store the value whereever it wants. The register where the expression -+** store the value wherever it wants. The register where the expression - ** is stored is returned. When regDest<0, two identical expressions might - ** code to the same register, if they do not contain function calls and hence - ** are factored out into the initialization section at the end of the -@@ -104267,12 +115893,15 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( - ){ - ExprList *p; - assert( ConstFactorOk(pParse) ); -+ assert( regDest!=0 ); - p = pParse->pConstExpr; - if( regDest<0 && p ){ - struct ExprList_item *pItem; - int i; - for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){ -- if( pItem->reusable && sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 ){ -+ if( pItem->fg.reusable -+ && sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 -+ ){ - return pItem->u.iConstExprReg; - } - } -@@ -104295,7 +115924,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( - p = sqlite3ExprListAppend(pParse, p, pExpr); - if( p ){ - struct ExprList_item *pItem = &p->a[p->nExpr-1]; -- pItem->reusable = regDest<0; -+ pItem->fg.reusable = regDest<0; - if( regDest<0 ) regDest = ++pParse->nMem; - pItem->u.iConstExprReg = regDest; - } -@@ -104304,6 +115933,25 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( - return regDest; - } - -+/* -+** Make arrangements to invoke OP_Null on a range of registers -+** during initialization. -+*/ -+SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange( -+ Parse *pParse, /* Parsing context */ -+ int iReg, /* First register to set to NULL */ -+ int nReg /* Number of sequential registers to NULL out */ -+){ -+ u8 okConstFactor = pParse->okConstFactor; -+ Expr t; -+ memset(&t, 0, sizeof(t)); -+ t.op = TK_NULLS; -+ t.y.nReg = nReg; -+ pParse->okConstFactor = 1; -+ sqlite3ExprCodeRunJustOnce(pParse, &t, iReg); -+ pParse->okConstFactor = okConstFactor; -+} -+ - /* - ** Generate code to evaluate an expression and store the results - ** into a register. Return the register number where the results -@@ -104321,8 +115969,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ - int r2; - pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); - if( ConstFactorOk(pParse) -+ && ALWAYS(pExpr!=0) - && pExpr->op!=TK_REGISTER -- && sqlite3ExprIsConstantNotJoin(pExpr) -+ && sqlite3ExprIsConstantNotJoin(pParse, pExpr) - ){ - *pReg = 0; - r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); -@@ -104354,7 +116003,11 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ - inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); - if( inReg!=target ){ - u8 op; -- if( ExprHasProperty(pExpr,EP_Subquery) ){ -+ Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr); -+ testcase( pX!=pExpr ); -+ if( ALWAYS(pX) -+ && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER) -+ ){ - op = OP_Copy; - }else{ - op = OP_SCopy; -@@ -104382,7 +116035,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){ - ** might choose to code the expression at initialization time. - */ - SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ -- if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){ -+ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ - sqlite3ExprCodeRunJustOnce(pParse, pExpr, target); - }else{ - sqlite3ExprCodeCopy(pParse, pExpr, target); -@@ -104428,7 +116081,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( - for(pItem=pList->a, i=0; ipExpr; - #ifdef SQLITE_ENABLE_SORTER_REFERENCES -- if( pItem->bSorterRef ){ -+ if( pItem->fg.bSorterRef ){ - i--; - n--; - }else -@@ -104441,7 +116094,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( - sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); - } - }else if( (flags & SQLITE_ECEL_FACTOR)!=0 -- && sqlite3ExprIsConstantNotJoin(pExpr) -+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr) - ){ - sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i); - }else{ -@@ -104449,7 +116102,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( - if( inReg!=target+i ){ - VdbeOp *pOp; - if( copyOp==OP_Copy -- && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy -+ && (pOp=sqlite3VdbeGetLastOp(v))->opcode==OP_Copy - && pOp->p1+pOp->p3+1==inReg - && pOp->p2+pOp->p3+1==target+i - && pOp->p5==0 /* The do-not-merge flag must be clear */ -@@ -104502,7 +116155,7 @@ static void exprCodeBetween( - memset(&compRight, 0, sizeof(Expr)); - memset(&exprAnd, 0, sizeof(Expr)); - -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); -+ assert( ExprUseXList(pExpr) ); - pDel = sqlite3ExprDup(db, pExpr->pLeft, 0); - if( db->mallocFailed==0 ){ - exprAnd.op = TK_AND; -@@ -104514,7 +116167,7 @@ static void exprCodeBetween( - compRight.op = TK_LE; - compRight.pLeft = pDel; - compRight.pRight = pExpr->x.pList->a[1].pExpr; -- exprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); -+ sqlite3ExprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); - if( xJump ){ - xJump(pParse, &exprAnd, dest, jumpIfNull); - }else{ -@@ -104522,8 +116175,8 @@ static void exprCodeBetween( - ** so that the sqlite3ExprCodeTarget() routine will not attempt to move - ** it into the Parse.pConstExpr list. We should use a new bit for this, - ** for clarity, but we are out of bits in the Expr.flags field so we -- ** have to reuse the EP_FromJoin bit. Bummer. */ -- pDel->flags |= EP_FromJoin; -+ ** have to reuse the EP_OuterON bit. Bummer. */ -+ pDel->flags |= EP_OuterON; - sqlite3ExprCodeTarget(pParse, &exprAnd, dest); - } - sqlite3ReleaseTempReg(pParse, regFree1); -@@ -104648,10 +116301,11 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int - assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); - assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); -+ assert( regFree1==0 || regFree1==r1 ); -+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1); - sqlite3VdbeAddOp2(v, op, r1, dest); - VdbeCoverageIf(v, op==TK_ISNULL); - VdbeCoverageIf(v, op==TK_NOTNULL); -- testcase( regFree1==0 ); - break; - } - case TK_BETWEEN: { -@@ -104822,10 +116476,11 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int - case TK_ISNULL: - case TK_NOTNULL: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); -+ assert( regFree1==0 || regFree1==r1 ); -+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1); - sqlite3VdbeAddOp2(v, op, r1, dest); - testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL); - testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL); -- testcase( regFree1==0 ); - break; - } - case TK_BETWEEN: { -@@ -104891,12 +116546,23 @@ SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int dest,i - ** same as that currently bound to variable pVar, non-zero is returned. - ** Otherwise, if the values are not the same or if pExpr is not a simple - ** SQL value, zero is returned. -+** -+** If the SQLITE_EnableQPSG flag is set on the database connection, then -+** this routine always returns false. - */ --static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ -- int res = 0; -+static SQLITE_NOINLINE int exprCompareVariable( -+ const Parse *pParse, -+ const Expr *pVar, -+ const Expr *pExpr -+){ -+ int res = 2; - int iVar; - sqlite3_value *pL, *pR = 0; - -+ if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){ -+ return 0; -+ } -+ if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2; - sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR); - if( pR ){ - iVar = pVar->iColumn; -@@ -104906,12 +116572,11 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ - if( sqlite3_value_type(pL)==SQLITE_TEXT ){ - sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */ - } -- res = 0==sqlite3MemCompare(pL, pR, 0); -+ res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0; - } - sqlite3ValueFree(pR); - sqlite3ValueFree(pL); - } -- - return res; - } - -@@ -104937,20 +116602,23 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){ - ** just might result in some slightly slower code. But returning - ** an incorrect 0 or 1 could lead to a malfunction. - ** --** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in --** pParse->pReprepare can be matched against literals in pB. The --** pParse->pVdbe->expmask bitmask is updated for each variable referenced. --** If pParse is NULL (the normal case) then any TK_VARIABLE term in --** Argument pParse should normally be NULL. If it is not NULL and pA or --** pB causes a return value of 2. -+** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE -+** terms in pA with bindings in pParse->pReprepare can be matched against -+** literals in pB. The pParse->pVdbe->expmask bitmask is updated for -+** each variable referenced. - */ --SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ -+SQLITE_PRIVATE int sqlite3ExprCompare( -+ const Parse *pParse, -+ const Expr *pA, -+ const Expr *pB, -+ int iTab -+){ - u32 combinedFlags; - if( pA==0 || pB==0 ){ - return pB==pA ? 0 : 2; - } -- if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){ -- return 0; -+ if( pParse && pA->op==TK_VARIABLE ){ -+ return exprCompareVariable(pParse, pA, pB); - } - combinedFlags = pA->flags | pB->flags; - if( combinedFlags & EP_IntValue ){ -@@ -104966,9 +116634,17 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa - if( pB->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA,pB->pLeft,iTab)<2 ){ - return 1; - } -- return 2; -+ if( pA->op==TK_AGG_COLUMN && pB->op==TK_COLUMN -+ && pB->iTable<0 && pA->iTable==iTab -+ ){ -+ /* fall through */ -+ }else{ -+ return 2; -+ } - } -- if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ -+ assert( !ExprHasProperty(pA, EP_IntValue) ); -+ assert( !ExprHasProperty(pB, EP_IntValue) ); -+ if( pA->u.zToken ){ - if( pA->op==TK_FUNCTION || pA->op==TK_AGG_FUNCTION ){ - if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; - #ifndef SQLITE_OMIT_WINDOWFUNC -@@ -104986,7 +116662,12 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa - return 0; - }else if( pA->op==TK_COLLATE ){ - if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; -- }else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ -+ }else -+ if( pB->u.zToken!=0 -+ && pA->op!=TK_COLUMN -+ && pA->op!=TK_AGG_COLUMN -+ && strcmp(pA->u.zToken,pB->u.zToken)!=0 -+ ){ - return 2; - } - } -@@ -105028,7 +116709,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa - ** Two NULL pointers are considered to be the same. But a NULL pointer - ** always differs from a non-NULL pointer. - */ --SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ -+SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){ - int i; - if( pA==0 && pB==0 ) return 0; - if( pA==0 || pB==0 ) return 1; -@@ -105037,7 +116718,7 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ - int res; - Expr *pExprA = pA->a[i].pExpr; - Expr *pExprB = pB->a[i].pExpr; -- if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1; -+ if( pA->a[i].fg.sortFlags!=pB->a[i].fg.sortFlags ) return 1; - if( (res = sqlite3ExprCompare(0, pExprA, pExprB, iTab)) ) return res; - } - return 0; -@@ -105047,10 +116728,10 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){ - ** Like sqlite3ExprCompare() except COLLATE operators at the top-level - ** are ignored. - */ --SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ -+SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ - return sqlite3ExprCompare(0, -- sqlite3ExprSkipCollateAndLikely(pA), -- sqlite3ExprSkipCollateAndLikely(pB), -+ sqlite3ExprSkipCollate(pA), -+ sqlite3ExprSkipCollate(pB), - iTab); - } - -@@ -105061,9 +116742,9 @@ SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){ - ** non-NULL if pNN is not NULL - */ - static int exprImpliesNotNull( -- Parse *pParse, /* Parsing context */ -- Expr *p, /* The expression to be checked */ -- Expr *pNN, /* The expression that is NOT NULL */ -+ const Parse *pParse,/* Parsing context */ -+ const Expr *p, /* The expression to be checked */ -+ const Expr *pNN, /* The expression that is NOT NULL */ - int iTab, /* Table being evaluated */ - int seenNot /* Return true only if p can be any non-NULL value */ - ){ -@@ -105075,12 +116756,13 @@ static int exprImpliesNotNull( - switch( p->op ){ - case TK_IN: { - if( seenNot && ExprHasProperty(p, EP_xIsSelect) ) return 0; -- assert( ExprHasProperty(p,EP_xIsSelect) -- || (p->x.pList!=0 && p->x.pList->nExpr>0) ); -+ assert( ExprUseXSelect(p) || (p->x.pList!=0 && p->x.pList->nExpr>0) ); - return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1); - } - case TK_BETWEEN: { -- ExprList *pList = p->x.pList; -+ ExprList *pList; -+ assert( ExprUseXList(p) ); -+ pList = p->x.pList; - assert( pList!=0 ); - assert( pList->nExpr==2 ); - if( seenNot ) return 0; -@@ -105131,18 +116813,70 @@ static int exprImpliesNotNull( - return 0; - } - -+/* -+** Return true if the boolean value of the expression is always either -+** FALSE or NULL. -+*/ -+static int sqlite3ExprIsNotTrue(Expr *pExpr){ -+ int v; -+ if( pExpr->op==TK_NULL ) return 1; -+ if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1; -+ v = 1; -+ if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1; -+ return 0; -+} -+ -+/* -+** Return true if the expression is one of the following: -+** -+** CASE WHEN x THEN y END -+** CASE WHEN x THEN y ELSE NULL END -+** CASE WHEN x THEN y ELSE false END -+** iif(x,y) -+** iif(x,y,NULL) -+** iif(x,y,false) -+*/ -+static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){ -+ ExprList *pList; -+ if( pExpr->op==TK_FUNCTION ){ -+ const char *z = pExpr->u.zToken; -+ FuncDef *pDef; -+ if( (z[0]!='i' && z[0]!='I') ) return 0; -+ if( pExpr->x.pList==0 ) return 0; -+ pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0); -+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION -+ if( pDef==0 ) return 0; -+#else -+ if( NEVER(pDef==0) ) return 0; -+#endif -+ if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0; -+ if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0; -+ }else if( pExpr->op==TK_CASE ){ -+ if( pExpr->pLeft!=0 ) return 0; -+ }else{ -+ return 0; -+ } -+ pList = pExpr->x.pList; -+ assert( pList!=0 ); -+ if( pList->nExpr==2 ) return 1; -+ if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1; -+ return 0; -+} -+ - /* - ** Return true if we can prove the pE2 will always be true if pE1 is - ** true. Return false if we cannot complete the proof or if pE2 might - ** be false. Examples: - ** --** pE1: x==5 pE2: x==5 Result: true --** pE1: x>0 pE2: x==5 Result: false --** pE1: x=21 pE2: x=21 OR y=43 Result: true --** pE1: x!=123 pE2: x IS NOT NULL Result: true --** pE1: x!=?1 pE2: x IS NOT NULL Result: true --** pE1: x IS NULL pE2: x IS NOT NULL Result: false --** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false -+** pE1: x==5 pE2: x==5 Result: true -+** pE1: x>0 pE2: x==5 Result: false -+** pE1: x=21 pE2: x=21 OR y=43 Result: true -+** pE1: x!=123 pE2: x IS NOT NULL Result: true -+** pE1: x!=?1 pE2: x IS NOT NULL Result: true -+** pE1: x IS NULL pE2: x IS NOT NULL Result: false -+** pE1: x IS ?2 pE2: x IS NOT NULL Result: false -+** pE1: iif(x,y) pE2: x Result: true -+** PE1: iif(x,y,0) pE2: x Result: true - ** - ** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has - ** Expr.iTable<0 then assume a table number given by iTab. -@@ -105156,7 +116890,12 @@ static int exprImpliesNotNull( - ** improvement. Returning false might cause a performance reduction, but - ** it will always give the correct answer and is hence always safe. - */ --SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){ -+SQLITE_PRIVATE int sqlite3ExprImpliesExpr( -+ const Parse *pParse, -+ const Expr *pE1, -+ const Expr *pE2, -+ int iTab -+){ - if( sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){ - return 1; - } -@@ -105171,14 +116910,35 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, i - ){ - return 1; - } -+ if( sqlite3ExprIsIIF(pParse->db, pE1) ){ -+ return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab); -+ } - return 0; - } - -+/* This is a helper function to impliesNotNullRow(). In this routine, -+** set pWalker->eCode to one only if *both* of the input expressions -+** separately have the implies-not-null-row property. -+*/ -+static void bothImplyNotNullRow(Walker *pWalker, Expr *pE1, Expr *pE2){ -+ if( pWalker->eCode==0 ){ -+ sqlite3WalkExpr(pWalker, pE1); -+ if( pWalker->eCode ){ -+ pWalker->eCode = 0; -+ sqlite3WalkExpr(pWalker, pE2); -+ } -+ } -+} -+ - /* - ** This is the Expr node callback for sqlite3ExprImpliesNonNullRow(). - ** If the expression node requires that the table at pWalker->iCur - ** have one or more non-NULL column, then set pWalker->eCode to 1 and abort. - ** -+** pWalker->mWFlags is non-zero if this inquiry is being undertaking on -+** behalf of a RIGHT JOIN (or FULL JOIN). That makes a difference when -+** evaluating terms in the ON clause of an inner join. -+** - ** This routine controls an optimization. False positives (setting - ** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives - ** (never setting pWalker->eCode) is a harmless missed optimization. -@@ -105186,29 +116946,34 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, i - static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ - testcase( pExpr->op==TK_AGG_COLUMN ); - testcase( pExpr->op==TK_AGG_FUNCTION ); -- if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune; -+ if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune; -+ if( ExprHasProperty(pExpr, EP_InnerON) && pWalker->mWFlags ){ -+ /* If iCur is used in an inner-join ON clause to the left of a -+ ** RIGHT JOIN, that does *not* mean that the table must be non-null. -+ ** But it is difficult to check for that condition precisely. -+ ** To keep things simple, any use of iCur from any inner-join is -+ ** ignored while attempting to simplify a RIGHT JOIN. */ -+ return WRC_Prune; -+ } - switch( pExpr->op ){ - case TK_ISNOT: - case TK_ISNULL: - case TK_NOTNULL: - case TK_IS: -- case TK_OR: - case TK_VECTOR: -- case TK_CASE: -- case TK_IN: - case TK_FUNCTION: - case TK_TRUTH: -+ case TK_CASE: - testcase( pExpr->op==TK_ISNOT ); - testcase( pExpr->op==TK_ISNULL ); - testcase( pExpr->op==TK_NOTNULL ); - testcase( pExpr->op==TK_IS ); -- testcase( pExpr->op==TK_OR ); - testcase( pExpr->op==TK_VECTOR ); -- testcase( pExpr->op==TK_CASE ); -- testcase( pExpr->op==TK_IN ); - testcase( pExpr->op==TK_FUNCTION ); - testcase( pExpr->op==TK_TRUTH ); -+ testcase( pExpr->op==TK_CASE ); - return WRC_Prune; -+ - case TK_COLUMN: - if( pWalker->u.iCur==pExpr->iTable ){ - pWalker->eCode = 1; -@@ -105216,21 +116981,38 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ - } - return WRC_Prune; - -+ case TK_OR: - case TK_AND: -- if( pWalker->eCode==0 ){ -+ /* Both sides of an AND or OR must separately imply non-null-row. -+ ** Consider these cases: -+ ** 1. NOT (x AND y) -+ ** 2. x OR y -+ ** If only one of x or y is non-null-row, then the overall expression -+ ** can be true if the other arm is false (case 1) or true (case 2). -+ */ -+ testcase( pExpr->op==TK_OR ); -+ testcase( pExpr->op==TK_AND ); -+ bothImplyNotNullRow(pWalker, pExpr->pLeft, pExpr->pRight); -+ return WRC_Prune; -+ -+ case TK_IN: -+ /* Beware of "x NOT IN ()" and "x NOT IN (SELECT 1 WHERE false)", -+ ** both of which can be true. But apart from these cases, if -+ ** the left-hand side of the IN is NULL then the IN itself will be -+ ** NULL. */ -+ if( ExprUseXList(pExpr) && ALWAYS(pExpr->x.pList->nExpr>0) ){ - sqlite3WalkExpr(pWalker, pExpr->pLeft); -- if( pWalker->eCode ){ -- pWalker->eCode = 0; -- sqlite3WalkExpr(pWalker, pExpr->pRight); -- } - } - return WRC_Prune; - - case TK_BETWEEN: -- if( sqlite3WalkExpr(pWalker, pExpr->pLeft)==WRC_Abort ){ -- assert( pWalker->eCode ); -- return WRC_Abort; -- } -+ /* In "x NOT BETWEEN y AND z" either x must be non-null-row or else -+ ** both y and z must be non-null row */ -+ assert( ExprUseXList(pExpr) ); -+ assert( pExpr->x.pList->nExpr==2 ); -+ sqlite3WalkExpr(pWalker, pExpr->pLeft); -+ bothImplyNotNullRow(pWalker, pExpr->x.pList->a[0].pExpr, -+ pExpr->x.pList->a[1].pExpr); - return WRC_Prune; - - /* Virtual tables are allowed to use constraints like x=NULL. So -@@ -105252,10 +117034,14 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ - testcase( pExpr->op==TK_GE ); - /* The y.pTab=0 assignment in wherecode.c always happens after the - ** impliesNotNullRow() test */ -- if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) -- && IsVirtual(pLeft->y.pTab)) -- || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) -- && IsVirtual(pRight->y.pTab)) -+ assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) ); -+ assert( pRight->op!=TK_COLUMN || ExprUseYTab(pRight) ); -+ if( (pLeft->op==TK_COLUMN -+ && ALWAYS(pLeft->y.pTab!=0) -+ && IsVirtual(pLeft->y.pTab)) -+ || (pRight->op==TK_COLUMN -+ && ALWAYS(pRight->y.pTab!=0) -+ && IsVirtual(pRight->y.pTab)) - ){ - return WRC_Prune; - } -@@ -105279,8 +117065,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ - ** False positives are not allowed, however. A false positive may result - ** in an incorrect answer. - ** --** Terms of p that are marked with EP_FromJoin (and hence that come from --** the ON or USING clauses of LEFT JOINS) are excluded from the analysis. -+** Terms of p that are marked with EP_OuterON (and hence that come from -+** the ON or USING clauses of OUTER JOINS) are excluded from the analysis. - ** - ** This routine is used to check if a LEFT JOIN can be converted into - ** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE -@@ -105288,7 +117074,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ - ** be non-NULL, then the LEFT JOIN can be safely converted into an - ** ordinary join. - */ --SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ -+SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab, int isRJ){ - Walker w; - p = sqlite3ExprSkipCollateAndLikely(p); - if( p==0 ) return 0; -@@ -105296,7 +117082,7 @@ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ - p = p->pLeft; - }else{ - while( p->op==TK_AND ){ -- if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab) ) return 1; -+ if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab, isRJ) ) return 1; - p = p->pRight; - } - } -@@ -105304,6 +117090,7 @@ SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ - w.xSelectCallback = 0; - w.xSelectCallback2 = 0; - w.eCode = 0; -+ w.mWFlags = isRJ!=0; - w.u.iCur = iTab; - sqlite3WalkExpr(&w, p); - return w.eCode; -@@ -105364,88 +117151,132 @@ SQLITE_PRIVATE int sqlite3ExprCoveredByIndex( - } - - --/* --** An instance of the following structure is used by the tree walker --** to count references to table columns in the arguments of an --** aggregate function, in order to implement the --** sqlite3FunctionThisSrc() routine. --*/ --struct SrcCount { -- SrcList *pSrc; /* One particular FROM clause in a nested query */ -- int iSrcInner; /* Smallest cursor number in this context */ -- int nThis; /* Number of references to columns in pSrcList */ -- int nOther; /* Number of references to columns in other FROM clauses */ -+/* Structure used to pass information throughout the Walker in order to -+** implement sqlite3ReferencesSrcList(). -+*/ -+struct RefSrcList { -+ sqlite3 *db; /* Database connection used for sqlite3DbRealloc() */ -+ SrcList *pRef; /* Looking for references to these tables */ -+ i64 nExclude; /* Number of tables to exclude from the search */ -+ int *aiExclude; /* Cursor IDs for tables to exclude from the search */ - }; - - /* --** xSelect callback for sqlite3FunctionUsesThisSrc(). If this is the first --** SELECT with a FROM clause encountered during this iteration, set --** SrcCount.iSrcInner to the cursor number of the leftmost object in --** the FROM cause. -+** Walker SELECT callbacks for sqlite3ReferencesSrcList(). -+** -+** When entering a new subquery on the pExpr argument, add all FROM clause -+** entries for that subquery to the exclude list. -+** -+** When leaving the subquery, remove those entries from the exclude list. - */ --static int selectSrcCount(Walker *pWalker, Select *pSel){ -- struct SrcCount *p = pWalker->u.pSrcCount; -- if( p->iSrcInner==0x7FFFFFFF && ALWAYS(pSel->pSrc) && pSel->pSrc->nSrc ){ -- pWalker->u.pSrcCount->iSrcInner = pSel->pSrc->a[0].iCursor; -+static int selectRefEnter(Walker *pWalker, Select *pSelect){ -+ struct RefSrcList *p = pWalker->u.pRefSrcList; -+ SrcList *pSrc = pSelect->pSrc; -+ i64 i, j; -+ int *piNew; -+ if( pSrc->nSrc==0 ) return WRC_Continue; -+ j = p->nExclude; -+ p->nExclude += pSrc->nSrc; -+ piNew = sqlite3DbRealloc(p->db, p->aiExclude, p->nExclude*sizeof(int)); -+ if( piNew==0 ){ -+ p->nExclude = 0; -+ return WRC_Abort; -+ }else{ -+ p->aiExclude = piNew; -+ } -+ for(i=0; inSrc; i++, j++){ -+ p->aiExclude[j] = pSrc->a[i].iCursor; - } - return WRC_Continue; - } -+static void selectRefLeave(Walker *pWalker, Select *pSelect){ -+ struct RefSrcList *p = pWalker->u.pRefSrcList; -+ SrcList *pSrc = pSelect->pSrc; -+ if( p->nExclude ){ -+ assert( p->nExclude>=pSrc->nSrc ); -+ p->nExclude -= pSrc->nSrc; -+ } -+} - --/* --** Count the number of references to columns. -+/* This is the Walker EXPR callback for sqlite3ReferencesSrcList(). -+** -+** Set the 0x01 bit of pWalker->eCode if there is a reference to any -+** of the tables shown in RefSrcList.pRef. -+** -+** Set the 0x02 bit of pWalker->eCode if there is a reference to a -+** table is in neither RefSrcList.pRef nor RefSrcList.aiExclude. - */ --static int exprSrcCount(Walker *pWalker, Expr *pExpr){ -- /* There was once a NEVER() on the second term on the grounds that -- ** sqlite3FunctionUsesThisSrc() was always called before -- ** sqlite3ExprAnalyzeAggregates() and so the TK_COLUMNs have not yet -- ** been converted into TK_AGG_COLUMN. But this is no longer true due -- ** to window functions - sqlite3WindowRewrite() may now indirectly call -- ** FunctionUsesThisSrc() when creating a new sub-select. */ -- if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){ -+static int exprRefToSrcList(Walker *pWalker, Expr *pExpr){ -+ if( pExpr->op==TK_COLUMN -+ || pExpr->op==TK_AGG_COLUMN -+ ){ - int i; -- struct SrcCount *p = pWalker->u.pSrcCount; -- SrcList *pSrc = p->pSrc; -+ struct RefSrcList *p = pWalker->u.pRefSrcList; -+ SrcList *pSrc = p->pRef; - int nSrc = pSrc ? pSrc->nSrc : 0; - for(i=0; iiTable==pSrc->a[i].iCursor ) break; -+ if( pExpr->iTable==pSrc->a[i].iCursor ){ -+ pWalker->eCode |= 1; -+ return WRC_Continue; -+ } - } -- if( inThis++; -- }else if( pExpr->iTableiSrcInner ){ -- /* In a well-formed parse tree (no name resolution errors), -- ** TK_COLUMN nodes with smaller Expr.iTable values are in an -- ** outer context. Those are the only ones to count as "other" */ -- p->nOther++; -+ for(i=0; inExclude && p->aiExclude[i]!=pExpr->iTable; i++){} -+ if( i>=p->nExclude ){ -+ pWalker->eCode |= 2; - } - } - return WRC_Continue; - } - - /* --** Determine if any of the arguments to the pExpr Function reference --** pSrcList. Return true if they do. Also return true if the function --** has no arguments or has only constant arguments. Return false if pExpr --** references columns but not columns of tables found in pSrcList. -+** Check to see if pExpr references any tables in pSrcList. -+** Possible return values: -+** -+** 1 pExpr does references a table in pSrcList. -+** -+** 0 pExpr references some table that is not defined in either -+** pSrcList or in subqueries of pExpr itself. -+** -+** -1 pExpr only references no tables at all, or it only -+** references tables defined in subqueries of pExpr itself. -+** -+** As currently used, pExpr is always an aggregate function call. That -+** fact is exploited for efficiency. - */ --SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ -+SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){ - Walker w; -- struct SrcCount cnt; -- assert( pExpr->op==TK_AGG_FUNCTION ); -+ struct RefSrcList x; -+ assert( pParse->db!=0 ); - memset(&w, 0, sizeof(w)); -- w.xExprCallback = exprSrcCount; -- w.xSelectCallback = selectSrcCount; -- w.u.pSrcCount = &cnt; -- cnt.pSrc = pSrcList; -- cnt.iSrcInner = (pSrcList&&pSrcList->nSrc)?pSrcList->a[0].iCursor:0x7FFFFFFF; -- cnt.nThis = 0; -- cnt.nOther = 0; -+ memset(&x, 0, sizeof(x)); -+ w.xExprCallback = exprRefToSrcList; -+ w.xSelectCallback = selectRefEnter; -+ w.xSelectCallback2 = selectRefLeave; -+ w.u.pRefSrcList = &x; -+ x.db = pParse->db; -+ x.pRef = pSrcList; -+ assert( pExpr->op==TK_AGG_FUNCTION ); -+ assert( ExprUseXList(pExpr) ); - sqlite3WalkExprList(&w, pExpr->x.pList); -+ if( pExpr->pLeft ){ -+ assert( pExpr->pLeft->op==TK_ORDER ); -+ assert( ExprUseXList(pExpr->pLeft) ); -+ assert( pExpr->pLeft->x.pList!=0 ); -+ sqlite3WalkExprList(&w, pExpr->pLeft->x.pList); -+ } - #ifndef SQLITE_OMIT_WINDOWFUNC - if( ExprHasProperty(pExpr, EP_WinFunc) ){ - sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter); - } - #endif -- return cnt.nThis>0 || cnt.nOther==0; -+ if( x.aiExclude ) sqlite3DbNNFreeNN(pParse->db, x.aiExclude); -+ if( w.eCode & 0x01 ){ -+ return 1; -+ }else if( w.eCode ){ -+ return 0; -+ }else{ -+ return -1; -+ } - } - - /* -@@ -105456,10 +117287,8 @@ SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ - ** it does, make a copy. This is done because the pExpr argument is - ** subject to change. - ** --** The copy is stored on pParse->pConstExpr with a register number of 0. --** This will cause the expression to be deleted automatically when the --** Parse object is destroyed, but the zero register number means that it --** will not generate any code in the preamble. -+** The copy is scheduled for deletion using the sqlite3ExprDeferredDelete() -+** which builds on the sqlite3ParserAddCleanup() mechanism. - */ - static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ - if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced)) -@@ -105469,25 +117298,24 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ - int iAgg = pExpr->iAgg; - Parse *pParse = pWalker->pParse; - sqlite3 *db = pParse->db; -- assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION ); -- if( pExpr->op==TK_AGG_COLUMN ){ -- assert( iAgg>=0 && iAggnColumn ); -- if( pAggInfo->aCol[iAgg].pCExpr==pExpr ){ -+ assert( iAgg>=0 ); -+ if( pExpr->op!=TK_AGG_FUNCTION ){ -+ if( iAggnColumn -+ && pAggInfo->aCol[iAgg].pCExpr==pExpr -+ ){ - pExpr = sqlite3ExprDup(db, pExpr, 0); -- if( pExpr ){ -+ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ - pAggInfo->aCol[iAgg].pCExpr = pExpr; -- pParse->pConstExpr = -- sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); - } - } - }else{ -- assert( iAgg>=0 && iAggnFunc ); -- if( pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ -+ assert( pExpr->op==TK_AGG_FUNCTION ); -+ if( ALWAYS(iAggnFunc) -+ && pAggInfo->aFunc[iAgg].pFExpr==pExpr -+ ){ - pExpr = sqlite3ExprDup(db, pExpr, 0); -- if( pExpr ){ -+ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ - pAggInfo->aFunc[iAgg].pFExpr = pExpr; -- pParse->pConstExpr = -- sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); - } - } - } -@@ -105538,6 +117366,81 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){ - return i; - } - -+/* -+** Search the AggInfo object for an aCol[] entry that has iTable and iColumn. -+** Return the index in aCol[] of the entry that describes that column. -+** -+** If no prior entry is found, create a new one and return -1. The -+** new column will have an index of pAggInfo->nColumn-1. -+*/ -+static void findOrCreateAggInfoColumn( -+ Parse *pParse, /* Parsing context */ -+ AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */ -+ Expr *pExpr /* Expr describing the column to find or insert */ -+){ -+ struct AggInfo_col *pCol; -+ int k; -+ int mxTerm = pParse->db->aLimit[SQLITE_LIMIT_COLUMN]; -+ -+ assert( mxTerm <= SMXV(i16) ); -+ assert( pAggInfo->iFirstReg==0 ); -+ pCol = pAggInfo->aCol; -+ for(k=0; knColumn; k++, pCol++){ -+ if( pCol->pCExpr==pExpr ) return; -+ if( pCol->iTable==pExpr->iTable -+ && pCol->iColumn==pExpr->iColumn -+ && pExpr->op!=TK_IF_NULL_ROW -+ ){ -+ goto fix_up_expr; -+ } -+ } -+ k = addAggInfoColumn(pParse->db, pAggInfo); -+ if( k<0 ){ -+ /* OOM on resize */ -+ assert( pParse->db->mallocFailed ); -+ return; -+ } -+ if( k>mxTerm ){ -+ sqlite3ErrorMsg(pParse, "more than %d aggregate terms", mxTerm); -+ k = mxTerm; -+ } -+ pCol = &pAggInfo->aCol[k]; -+ assert( ExprUseYTab(pExpr) ); -+ pCol->pTab = pExpr->y.pTab; -+ pCol->iTable = pExpr->iTable; -+ pCol->iColumn = pExpr->iColumn; -+ pCol->iSorterColumn = -1; -+ pCol->pCExpr = pExpr; -+ if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){ -+ int j, n; -+ ExprList *pGB = pAggInfo->pGroupBy; -+ struct ExprList_item *pTerm = pGB->a; -+ n = pGB->nExpr; -+ for(j=0; jpExpr; -+ if( pE->op==TK_COLUMN -+ && pE->iTable==pExpr->iTable -+ && pE->iColumn==pExpr->iColumn -+ ){ -+ pCol->iSorterColumn = j; -+ break; -+ } -+ } -+ } -+ if( pCol->iSorterColumn<0 ){ -+ pCol->iSorterColumn = pAggInfo->nSortingColumn++; -+ } -+fix_up_expr: -+ ExprSetVVAProperty(pExpr, EP_NoReduce); -+ assert( pExpr->pAggInfo==0 || pExpr->pAggInfo==pAggInfo ); -+ pExpr->pAggInfo = pAggInfo; -+ if( pExpr->op==TK_COLUMN ){ -+ pExpr->op = TK_AGG_COLUMN; -+ } -+ assert( k <= SMXV(pExpr->iAgg) ); -+ pExpr->iAgg = (i16)k; -+} -+ - /* - ** This is the xExprCallback for a tree walker. It is used to - ** implement sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates -@@ -105551,104 +117454,130 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ - AggInfo *pAggInfo = pNC->uNC.pAggInfo; - - assert( pNC->ncFlags & NC_UAggInfo ); -+ assert( pAggInfo->iFirstReg==0 ); - switch( pExpr->op ){ -+ default: { -+ IndexedExpr *pIEpr; -+ Expr tmp; -+ assert( pParse->iSelfTab==0 ); -+ if( (pNC->ncFlags & NC_InAggFunc)==0 ) break; -+ if( pParse->pIdxEpr==0 ) break; -+ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){ -+ int iDataCur = pIEpr->iDataCur; -+ if( iDataCur<0 ) continue; -+ if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break; -+ } -+ if( pIEpr==0 ) break; -+ if( NEVER(!ExprUseYTab(pExpr)) ) break; -+ for(i=0; inSrc; i++){ -+ if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break; -+ } -+ if( i>=pSrcList->nSrc ) break; -+ if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */ -+ if( pParse->nErr ){ return WRC_Abort; } -+ -+ /* If we reach this point, it means that expression pExpr can be -+ ** translated into a reference to an index column as described by -+ ** pIEpr. -+ */ -+ memset(&tmp, 0, sizeof(tmp)); -+ tmp.op = TK_AGG_COLUMN; -+ tmp.iTable = pIEpr->iIdxCur; -+ tmp.iColumn = pIEpr->iIdxCol; -+ findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp); -+ if( pParse->nErr ){ return WRC_Abort; } -+ assert( pAggInfo->aCol!=0 ); -+ assert( tmp.iAggnColumn ); -+ pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr; -+ pExpr->pAggInfo = pAggInfo; -+ pExpr->iAgg = tmp.iAgg; -+ return WRC_Prune; -+ } -+ case TK_IF_NULL_ROW: - case TK_AGG_COLUMN: - case TK_COLUMN: { - testcase( pExpr->op==TK_AGG_COLUMN ); - testcase( pExpr->op==TK_COLUMN ); -+ testcase( pExpr->op==TK_IF_NULL_ROW ); - /* Check to see if the column is in one of the tables in the FROM - ** clause of the aggregate query */ - if( ALWAYS(pSrcList!=0) ){ -- struct SrcList_item *pItem = pSrcList->a; -+ SrcItem *pItem = pSrcList->a; - for(i=0; inSrc; i++, pItem++){ -- struct AggInfo_col *pCol; - assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); - if( pExpr->iTable==pItem->iCursor ){ -- /* If we reach this point, it means that pExpr refers to a table -- ** that is in the FROM clause of the aggregate query. -- ** -- ** Make an entry for the column in pAggInfo->aCol[] if there -- ** is not an entry there already. -- */ -- int k; -- pCol = pAggInfo->aCol; -- for(k=0; knColumn; k++, pCol++){ -- if( pCol->iTable==pExpr->iTable && -- pCol->iColumn==pExpr->iColumn ){ -- break; -- } -- } -- if( (k>=pAggInfo->nColumn) -- && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0 -- ){ -- pCol = &pAggInfo->aCol[k]; -- pCol->pTab = pExpr->y.pTab; -- pCol->iTable = pExpr->iTable; -- pCol->iColumn = pExpr->iColumn; -- pCol->iMem = ++pParse->nMem; -- pCol->iSorterColumn = -1; -- pCol->pCExpr = pExpr; -- if( pAggInfo->pGroupBy ){ -- int j, n; -- ExprList *pGB = pAggInfo->pGroupBy; -- struct ExprList_item *pTerm = pGB->a; -- n = pGB->nExpr; -- for(j=0; jpExpr; -- if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable && -- pE->iColumn==pExpr->iColumn ){ -- pCol->iSorterColumn = j; -- break; -- } -- } -- } -- if( pCol->iSorterColumn<0 ){ -- pCol->iSorterColumn = pAggInfo->nSortingColumn++; -- } -- } -- /* There is now an entry for pExpr in pAggInfo->aCol[] (either -- ** because it was there before or because we just created it). -- ** Convert the pExpr to be a TK_AGG_COLUMN referring to that -- ** pAggInfo->aCol[] entry. -- */ -- ExprSetVVAProperty(pExpr, EP_NoReduce); -- pExpr->pAggInfo = pAggInfo; -- pExpr->op = TK_AGG_COLUMN; -- pExpr->iAgg = (i16)k; -+ findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr); - break; - } /* endif pExpr->iTable==pItem->iCursor */ - } /* end loop over pSrcList */ - } -- return WRC_Prune; -+ return WRC_Continue; - } - case TK_AGG_FUNCTION: { - if( (pNC->ncFlags & NC_InAggFunc)==0 - && pWalker->walkerDepth==pExpr->op2 -+ && pExpr->pAggInfo==0 - ){ - /* Check to see if pExpr is a duplicate of another aggregate - ** function that is already in the pAggInfo structure - */ - struct AggInfo_func *pItem = pAggInfo->aFunc; -+ int mxTerm = pParse->db->aLimit[SQLITE_LIMIT_COLUMN]; -+ assert( mxTerm <= SMXV(i16) ); - for(i=0; inFunc; i++, pItem++){ -+ if( NEVER(pItem->pFExpr==pExpr) ) break; - if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ - break; - } - } -- if( i>=pAggInfo->nFunc ){ -+ if( i>mxTerm ){ -+ sqlite3ErrorMsg(pParse, "more than %d aggregate terms", mxTerm); -+ i = mxTerm; -+ assert( inFunc ); -+ }else if( i>=pAggInfo->nFunc ){ - /* pExpr is original. Make a new entry in pAggInfo->aFunc[] - */ - u8 enc = ENC(pParse->db); - i = addAggInfoFunc(pParse->db, pAggInfo); - if( i>=0 ){ -+ int nArg; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - pItem = &pAggInfo->aFunc[i]; - pItem->pFExpr = pExpr; -- pItem->iMem = ++pParse->nMem; -- assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ assert( ExprUseUToken(pExpr) ); -+ nArg = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; - pItem->pFunc = sqlite3FindFunction(pParse->db, -- pExpr->u.zToken, -- pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); -- if( pExpr->flags & EP_Distinct ){ -+ pExpr->u.zToken, nArg, enc, 0); -+ assert( pItem->bOBUnique==0 ); -+ if( pExpr->pLeft -+ && (pItem->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)==0 -+ ){ -+ /* The NEEDCOLL test above causes any ORDER BY clause on -+ ** aggregate min() or max() to be ignored. */ -+ ExprList *pOBList; -+ assert( nArg>0 ); -+ assert( pExpr->pLeft->op==TK_ORDER ); -+ assert( ExprUseXList(pExpr->pLeft) ); -+ pItem->iOBTab = pParse->nTab++; -+ pOBList = pExpr->pLeft->x.pList; -+ assert( pOBList->nExpr>0 ); -+ assert( pItem->bOBUnique==0 ); -+ if( pOBList->nExpr==1 -+ && nArg==1 -+ && sqlite3ExprCompare(0,pOBList->a[0].pExpr, -+ pExpr->x.pList->a[0].pExpr,0)==0 -+ ){ -+ pItem->bOBPayload = 0; -+ pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct); -+ }else{ -+ pItem->bOBPayload = 1; -+ } -+ pItem->bUseSubtype = -+ (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0; -+ }else{ -+ pItem->iOBTab = -1; -+ } -+ if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){ - pItem->iDistinct = pParse->nTab++; - }else{ - pItem->iDistinct = -1; -@@ -105659,6 +117588,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ - */ - assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); - ExprSetVVAProperty(pExpr, EP_NoReduce); -+ assert( i <= SMXV(pExpr->iAgg) ); - pExpr->iAgg = (i16)i; - pExpr->pAggInfo = pAggInfo; - return WRC_Prune; -@@ -105772,6 +117702,37 @@ SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){ - pParse->nRangeReg = 0; - } - -+/* -+** Make sure sufficient registers have been allocated so that -+** iReg is a valid register number. -+*/ -+SQLITE_PRIVATE void sqlite3TouchRegister(Parse *pParse, int iReg){ -+ if( pParse->nMemnMem = iReg; -+} -+ -+#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) -+/* -+** Return the latest reusable register in the set of all registers. -+** The value returned is no less than iMin. If any register iMin or -+** greater is in permanent use, then return one more than that last -+** permanent register. -+*/ -+SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse *pParse, int iMin){ -+ const ExprList *pList = pParse->pConstExpr; -+ if( pList ){ -+ int i; -+ for(i=0; inExpr; i++){ -+ if( pList->a[i].u.iConstExprReg>=iMin ){ -+ iMin = pList->a[i].u.iConstExprReg + 1; -+ } -+ } -+ } -+ pParse->nTempReg = 0; -+ pParse->nRangeReg = 0; -+ return iMin; -+} -+#endif /* SQLITE_ENABLE_STAT4 || SQLITE_DEBUG */ -+ - /* - ** Validate that no temporary register falls within the range of - ** iFirst..iLast, inclusive. This routine is only call from within assert() -@@ -105791,6 +117752,14 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ - return 0; - } - } -+ if( pParse->pConstExpr ){ -+ ExprList *pList = pParse->pConstExpr; -+ for(i=0; inExpr; i++){ -+ int iReg = pList->a[i].u.iConstExprReg; -+ if( iReg==0 ) continue; -+ if( iReg>=iFirst && iReg<=iLast ) return 0; -+ } -+ } - return 1; - } - #endif /* SQLITE_DEBUG */ -@@ -105830,6 +117799,7 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ - static int isAlterableTable(Parse *pParse, Table *pTab){ - if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) - #ifndef SQLITE_OMIT_VIRTUALTABLE -+ || (pTab->tabFlags & TF_Eponymous)!=0 - || ( (pTab->tabFlags & TF_Shadow)!=0 - && sqlite3ReadOnlyShadowTables(pParse->db) - ) -@@ -105848,25 +117818,56 @@ static int isAlterableTable(Parse *pParse, Table *pTab){ - ** statement to ensure that the operation has not rendered any schema - ** objects unusable. - */ --static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ -+static void renameTestSchema( -+ Parse *pParse, /* Parse context */ -+ const char *zDb, /* Name of db to verify schema of */ -+ int bTemp, /* True if this is the temp db */ -+ const char *zWhen, /* "when" part of error message */ -+ int bNoDQS /* Do not allow DQS in the schema */ -+){ -+ pParse->colNamesSet = 1; - sqlite3NestedParse(pParse, - "SELECT 1 " -- "FROM \"%w\"." DFLT_SCHEMA_TABLE " " -+ "FROM \"%w\"." LEGACY_SCHEMA_TABLE " " - "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" - " AND sql NOT LIKE 'create virtual%%'" -- " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ", -+ " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ", - zDb, -- zDb, bTemp -+ zDb, bTemp, zWhen, bNoDQS - ); - - if( bTemp==0 ){ - sqlite3NestedParse(pParse, - "SELECT 1 " -- "FROM temp." DFLT_SCHEMA_TABLE " " -+ "FROM temp." LEGACY_SCHEMA_TABLE " " - "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" - " AND sql NOT LIKE 'create virtual%%'" -- " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ", -- zDb -+ " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ", -+ zDb, zWhen, bNoDQS -+ ); -+ } -+} -+ -+/* -+** Generate VM code to replace any double-quoted strings (but not double-quoted -+** identifiers) within the "sql" column of the sqlite_schema table in -+** database zDb with their single-quoted equivalents. If argument bTemp is -+** not true, similarly update all SQL statements in the sqlite_schema table -+** of the temp db. -+*/ -+static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){ -+ sqlite3NestedParse(pParse, -+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE -+ " SET sql = sqlite_rename_quotefix(%Q, sql)" -+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" -+ " AND sql NOT LIKE 'create virtual%%'" , zDb, zDb -+ ); -+ if( bTemp==0 ){ -+ sqlite3NestedParse(pParse, -+ "UPDATE temp." LEGACY_SCHEMA_TABLE -+ " SET sql = sqlite_rename_quotefix('temp', sql)" -+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" -+ " AND sql NOT LIKE 'create virtual%%'" - ); - } - } -@@ -105875,12 +117876,12 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ - ** Generate code to reload the schema for database iDb. And, if iDb!=1, for - ** the temp database as well. - */ --static void renameReloadSchema(Parse *pParse, int iDb){ -+static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){ - Vdbe *v = pParse->pVdbe; - if( v ){ - sqlite3ChangeCookie(pParse, iDb); -- sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0); -- if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); -+ sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5); -+ if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5); - } - } - -@@ -105902,9 +117903,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( - const char *zTabName; /* Original name of the table */ - Vdbe *v; - VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ -- u32 savedDbFlags; /* Saved value of db->mDbFlags */ - -- savedDbFlags = db->mDbFlags; - if( NEVER(db->mallocFailed) ) goto exit_rename_table; - assert( pSrc->nSrc==1 ); - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); -@@ -105913,7 +117912,6 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( - if( !pTab ) goto exit_rename_table; - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - zDb = db->aDb[iDb].zDbSName; -- db->mDbFlags |= DBFLAG_PreferBuiltin; - - /* Get a NULL terminated version of the new table name. */ - zName = sqlite3NameFromToken(db, pName); -@@ -105942,7 +117940,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( - } - - #ifndef SQLITE_OMIT_VIEW -- if( pTab->pSelect ){ -+ if( IsView(pTab) ){ - sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); - goto exit_rename_table; - } -@@ -105984,7 +117982,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( - /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in - ** the schema to use the new table name. */ - sqlite3NestedParse(pParse, -- "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " -+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " - "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) " - "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)" - "AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" -@@ -105994,7 +117992,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( - /* Update the tbl_name and name columns of the sqlite_schema table - ** as required. */ - sqlite3NestedParse(pParse, -- "UPDATE %Q." DFLT_SCHEMA_TABLE " SET " -+ "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET " - "tbl_name = %Q, " - "name = CASE " - "WHEN type='table' THEN %Q " -@@ -106029,7 +118027,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( - "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " - "tbl_name = " - "CASE WHEN tbl_name=%Q COLLATE nocase AND " -- " sqlite_rename_test(%Q, sql, type, name, 1) " -+ " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) " - "THEN %Q ELSE tbl_name END " - "WHERE type IN ('view', 'trigger')" - , zDb, zTabName, zName, zTabName, zDb, zName); -@@ -106048,13 +118046,12 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( - } - #endif - -- renameReloadSchema(pParse, iDb); -- renameTestSchema(pParse, zDb, iDb==1); -+ renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); -+ renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); - - exit_rename_table: - sqlite3SrcListDelete(db, pSrc); - sqlite3DbFree(db, zName); -- db->mDbFlags = savedDbFlags; - } - - /* -@@ -106095,7 +118092,9 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ - int r1; /* Temporary registers */ - - db = pParse->db; -- if( pParse->nErr || db->mallocFailed ) return; -+ assert( db->pParse==pParse ); -+ if( pParse->nErr ) return; -+ assert( db->mallocFailed==0 ); - pNew = pParse->pNewTable; - assert( pNew ); - -@@ -106104,7 +118103,7 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ - zDb = db->aDb[iDb].zDbSName; - zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ - pCol = &pNew->aCol[pNew->nCol-1]; -- pDflt = pCol->pDflt; -+ pDflt = sqlite3ColumnExpr(pNew, pCol); - pTab = sqlite3FindTable(db, zTab, zDb); - assert( pTab ); - -@@ -106138,7 +118137,8 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ - if( pDflt && pDflt->pLeft->op==TK_NULL ){ - pDflt = 0; - } -- if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ -+ assert( IsOrdinaryTable(pNew) ); -+ if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){ - sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, - "Cannot add a REFERENCES column with non-NULL default value"); - } -@@ -106175,28 +118175,30 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ - zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); - if( zCol ){ - char *zEnd = &zCol[pColDef->n-1]; -- u32 savedDbFlags = db->mDbFlags; - while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ - *zEnd-- = '\0'; - } -- db->mDbFlags |= DBFLAG_PreferBuiltin; -+ /* substr() operations on characters, but addColOffset is in bytes. So we -+ ** have to use printf() to translate between these units: */ -+ assert( IsOrdinaryTable(pTab) ); -+ assert( IsOrdinaryTable(pNew) ); - sqlite3NestedParse(pParse, -- "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " -- "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " -+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " -+ "sql = printf('%%.%ds, ',sql) || %Q" -+ " || substr(sql,1+length(printf('%%.%ds',sql))) " - "WHERE type = 'table' AND name = %Q", -- zDb, pNew->addColOffset, zCol, pNew->addColOffset+1, -+ zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset, - zTab - ); - sqlite3DbFree(db, zCol); -- db->mDbFlags = savedDbFlags; - } - -- /* Make sure the schema version is at least 3. But do not upgrade -- ** from less than 3 to 4, as that will corrupt any preexisting DESC -- ** index. -- */ - v = sqlite3GetVdbe(pParse); - if( v ){ -+ /* Make sure the schema version is at least 3. But do not upgrade -+ ** from less than 3 to 4, as that will corrupt any preexisting DESC -+ ** index. -+ */ - r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); - sqlite3VdbeUsesBtree(v, iDb); -@@ -106205,10 +118207,30 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); - sqlite3ReleaseTempReg(pParse, r1); -- } - -- /* Reload the table definition */ -- renameReloadSchema(pParse, iDb); -+ /* Reload the table definition */ -+ renameReloadSchema(pParse, iDb, INITFLAG_AlterAdd); -+ -+ /* Verify that constraints are still satisfied */ -+ if( pNew->pCheck!=0 -+ || (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0) -+ || (pTab->tabFlags & TF_Strict)!=0 -+ ){ -+ sqlite3NestedParse(pParse, -+ "SELECT CASE WHEN quick_check GLOB 'CHECK*'" -+ " THEN raise(ABORT,'CHECK constraint failed')" -+ " WHEN quick_check GLOB 'non-* value in*'" -+ " THEN raise(ABORT,'type mismatch on DEFAULT')" -+ " ELSE raise(ABORT,'NOT NULL constraint failed')" -+ " END" -+ " FROM pragma_quick_check(%Q,%Q)" -+ " WHERE quick_check GLOB 'CHECK*'" -+ " OR quick_check GLOB 'NULL*'" -+ " OR quick_check GLOB 'non-* value in*'", -+ zTab, zDb -+ ); -+ } -+ } - } - - /* -@@ -106249,7 +118271,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ - #endif - - /* Make sure this is not an attempt to ALTER a view. */ -- if( pTab->pSelect ){ -+ if( IsView(pTab) ){ - sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); - goto exit_begin_add_column; - } -@@ -106258,7 +118280,8 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ - } - - sqlite3MayAbort(pParse); -- assert( pTab->addColOffset>0 ); -+ assert( IsOrdinaryTable(pTab) ); -+ assert( pTab->u.tab.addColOffset>0 ); - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - - /* Put a copy of the Table struct in Parse.pNewTable for the -@@ -106276,23 +118299,23 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ - assert( pNew->nCol>0 ); - nAlloc = (((pNew->nCol-1)/8)*8)+8; - assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); -- pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc); -+ pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*(u32)nAlloc); - pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); - if( !pNew->aCol || !pNew->zName ){ - assert( db->mallocFailed ); - goto exit_begin_add_column; - } -- memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); -+ memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol); - for(i=0; inCol; i++){ - Column *pCol = &pNew->aCol[i]; -- pCol->zName = sqlite3DbStrDup(db, pCol->zName); -- pCol->hName = sqlite3StrIHash(pCol->zName); -- pCol->zColl = 0; -- pCol->pDflt = 0; -+ pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName); -+ pCol->hName = sqlite3StrIHash(pCol->zCnName); - } -+ assert( IsOrdinaryTable(pNew) ); -+ pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0); - pNew->pSchema = db->aDb[iDb].pSchema; -- pNew->addColOffset = pTab->addColOffset; -- pNew->nTabRef = 1; -+ pNew->u.tab.addColOffset = pTab->u.tab.addColOffset; -+ assert( pNew->nTabRef==1 ); - - exit_begin_add_column: - sqlite3SrcListDelete(db, pSrc); -@@ -106308,10 +118331,10 @@ exit_begin_add_column: - ** Or, if pTab is not a view or virtual table, zero is returned. - */ - #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) --static int isRealTable(Parse *pParse, Table *pTab){ -+static int isRealTable(Parse *pParse, Table *pTab, int bDrop){ - const char *zType = 0; - #ifndef SQLITE_OMIT_VIEW -- if( pTab->pSelect ){ -+ if( IsView(pTab) ){ - zType = "view"; - } - #endif -@@ -106321,15 +118344,16 @@ static int isRealTable(Parse *pParse, Table *pTab){ - } - #endif - if( zType ){ -- sqlite3ErrorMsg( -- pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName -+ sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"", -+ (bDrop ? "drop column from" : "rename columns of"), -+ zType, pTab->zName - ); - return 1; - } - return 0; - } - #else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ --# define isRealTable(x,y) (0) -+# define isRealTable(x,y,z) (0) - #endif - - /* -@@ -106358,7 +118382,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( - - /* Cannot alter a system table */ - if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; -- if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column; -+ if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column; - - /* Which schema holds the table to be altered */ - iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); -@@ -106376,14 +118400,16 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( - ** altered. Set iCol to be the index of the column being renamed */ - zOld = sqlite3NameFromToken(db, pOld); - if( !zOld ) goto exit_rename_column; -- for(iCol=0; iColnCol; iCol++){ -- if( 0==sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break; -- } -- if( iCol==pTab->nCol ){ -- sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld); -+ iCol = sqlite3ColumnIndex(pTab, zOld); -+ if( iCol<0 ){ -+ sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld); - goto exit_rename_column; - } - -+ /* Ensure the schema contains no double-quoted strings */ -+ renameTestSchema(pParse, zDb, iSchema==1, "", 0); -+ renameFixQuotes(pParse, zDb, iSchema==1); -+ - /* Do the rename operation using a recursive UPDATE statement that - ** uses the sqlite_rename_column() SQL function to compute the new - ** CREATE statement text for the sqlite_schema table. -@@ -106394,26 +118420,25 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( - assert( pNew->n>0 ); - bQuote = sqlite3Isquote(pNew->z[0]); - sqlite3NestedParse(pParse, -- "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " -+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " - "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) " - "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' " -- " AND (type != 'index' OR tbl_name = %Q)" -- " AND sql NOT LIKE 'create virtual%%'", -+ " AND (type != 'index' OR tbl_name = %Q)", - zDb, - zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1, - pTab->zName - ); - - sqlite3NestedParse(pParse, -- "UPDATE temp." DFLT_SCHEMA_TABLE " SET " -+ "UPDATE temp." LEGACY_SCHEMA_TABLE " SET " - "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) " - "WHERE type IN ('trigger', 'view')", - zDb, pTab->zName, iCol, zNew, bQuote - ); - - /* Drop and reload the database schema. */ -- renameReloadSchema(pParse, iSchema); -- renameTestSchema(pParse, zDb, iSchema==1); -+ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); -+ renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1); - - exit_rename_column: - sqlite3SrcListDelete(db, pSrc); -@@ -106440,7 +118465,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( - ** the parse tree. - */ - struct RenameToken { -- void *p; /* Parse tree element created by token t */ -+ const void *p; /* Parse tree element created by token t */ - Token t; /* The token that created parse tree element p */ - RenameToken *pNext; /* Next is a list of all RenameToken objects */ - }; -@@ -106482,16 +118507,19 @@ struct RenameCtx { - ** Technically, as x no longer points into a valid object or to the byte - ** following a valid object, it may not be used in comparison operations. - */ --static void renameTokenCheckAll(Parse *pParse, void *pPtr){ -- if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){ -- RenameToken *p; -- u8 i = 0; -+static void renameTokenCheckAll(Parse *pParse, const void *pPtr){ -+ assert( pParse==pParse->db->pParse ); -+ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); -+ if( pParse->nErr==0 ){ -+ const RenameToken *p; -+ u32 i = 1; - for(p=pParse->pRename; p; p=p->pNext){ - if( p->p ){ - assert( p->p!=pPtr ); -- i += *(u8*)(p->p); -+ i += *(u8*)(p->p) | 1; - } - } -+ assert( i>0 ); - } - } - #else -@@ -106510,7 +118538,11 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){ - ** with tail recursion in tokenExpr() routine, for a small performance - ** improvement. - */ --SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ -+SQLITE_PRIVATE const void *sqlite3RenameTokenMap( -+ Parse *pParse, -+ const void *pPtr, -+ const Token *pToken -+){ - RenameToken *pNew; - assert( pPtr || pParse->db->mallocFailed ); - renameTokenCheckAll(pParse, pPtr); -@@ -106532,7 +118564,7 @@ SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pTo - ** with parse tree element pFrom. This function remaps the associated token - ** to parse tree element pTo. - */ --SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ -+SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){ - RenameToken *p; - renameTokenCheckAll(pParse, pTo); - for(p=pParse->pRename; p; p=p->pNext){ -@@ -106548,7 +118580,10 @@ SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFro - */ - static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ - Parse *pParse = pWalker->pParse; -- sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr); -+ sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr); -+ if( ExprUseYTab(pExpr) ){ -+ sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab); -+ } - return WRC_Continue; - } - -@@ -106559,15 +118594,31 @@ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ - static void renameWalkWith(Walker *pWalker, Select *pSelect){ - With *pWith = pSelect->pWith; - if( pWith ){ -+ Parse *pParse = pWalker->pParse; - int i; -+ With *pCopy = 0; -+ assert( pWith->nCte>0 ); -+ if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){ -+ /* Push a copy of the With object onto the with-stack. We use a copy -+ ** here as the original will be expanded and resolved (flags SF_Expanded -+ ** and SF_Resolved) below. And the parser code that uses the with-stack -+ ** fails if the Select objects on it have already been expanded and -+ ** resolved. */ -+ pCopy = sqlite3WithDup(pParse->db, pWith); -+ pCopy = sqlite3WithPush(pParse, pCopy, 1); -+ } - for(i=0; inCte; i++){ - Select *p = pWith->a[i].pSelect; - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); -- sNC.pParse = pWalker->pParse; -- sqlite3SelectPrep(sNC.pParse, p, &sNC); -+ sNC.pParse = pParse; -+ if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC); -+ if( sNC.pParse->db->mallocFailed ) return; - sqlite3WalkSelect(pWalker, p); -- sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); -+ sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); -+ } -+ if( pCopy && pParse->pWith==pCopy ){ -+ pParse->pWith = pCopy->pOuter; - } - } - } -@@ -106577,13 +118628,12 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){ - */ - static void unmapColumnIdlistNames( - Parse *pParse, -- IdList *pIdList -+ const IdList *pIdList - ){ -- if( pIdList ){ -- int ii; -- for(ii=0; iinId; ii++){ -- sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName); -- } -+ int ii; -+ assert( pIdList!=0 ); -+ for(ii=0; iinId; ii++){ -+ sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); - } - } - -@@ -106594,11 +118644,15 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ - Parse *pParse = pWalker->pParse; - int i; - if( pParse->nErr ) return WRC_Abort; -- if( NEVER(p->selFlags & SF_View) ) return WRC_Prune; -+ testcase( p->selFlags & SF_View ); -+ testcase( p->selFlags & SF_CopyCte ); -+ if( p->selFlags & (SF_View|SF_CopyCte) ){ -+ return WRC_Prune; -+ } - if( ALWAYS(p->pEList) ){ - ExprList *pList = p->pEList; - for(i=0; inExpr; i++){ -- if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){ -+ if( pList->a[i].zEName && pList->a[i].fg.eEName==ENAME_NAME ){ - sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); - } - } -@@ -106607,8 +118661,11 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){ - SrcList *pSrc = p->pSrc; - for(i=0; inSrc; i++){ - sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); -- if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort; -- unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing); -+ if( pSrc->a[i].fg.isUsing==0 ){ -+ sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn); -+ }else{ -+ unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing); -+ } - } - } - -@@ -106644,7 +118701,7 @@ SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){ - sWalker.xExprCallback = renameUnmapExprCb; - sqlite3WalkExprList(&sWalker, pEList); - for(i=0; inExpr; i++){ -- if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) ){ -+ if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) ){ - sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName); - } - } -@@ -106665,23 +118722,35 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){ - - /* - ** Search the Parse object passed as the first argument for a RenameToken --** object associated with parse tree element pPtr. If found, remove it --** from the Parse object and add it to the list maintained by the --** RenameCtx object passed as the second argument. -+** object associated with parse tree element pPtr. If found, return a pointer -+** to it. Otherwise, return NULL. -+** -+** If the second argument passed to this function is not NULL and a matching -+** RenameToken object is found, remove it from the Parse object and add it to -+** the list maintained by the RenameCtx object. - */ --static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ -+static RenameToken *renameTokenFind( -+ Parse *pParse, -+ struct RenameCtx *pCtx, -+ const void *pPtr -+){ - RenameToken **pp; -- assert( pPtr!=0 ); -+ if( NEVER(pPtr==0) ){ -+ return 0; -+ } - for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ - if( (*pp)->p==pPtr ){ - RenameToken *pToken = *pp; -- *pp = pToken->pNext; -- pToken->pNext = pCtx->pList; -- pCtx->pList = pToken; -- pCtx->nList++; -- break; -+ if( pCtx ){ -+ *pp = pToken->pNext; -+ pToken->pNext = pCtx->pList; -+ pCtx->pList = pToken; -+ pCtx->nList++; -+ } -+ return pToken; - } - } -+ return 0; - } - - /* -@@ -106690,7 +118759,11 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ - ** descend into sub-select statements. - */ - static int renameColumnSelectCb(Walker *pWalker, Select *p){ -- if( p->selFlags & SF_View ) return WRC_Prune; -+ if( p->selFlags & (SF_View|SF_CopyCte) ){ -+ testcase( p->selFlags & SF_View ); -+ testcase( p->selFlags & SF_CopyCte ); -+ return WRC_Prune; -+ } - renameWalkWith(pWalker, p); - return WRC_Continue; - } -@@ -106713,6 +118786,7 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ - renameTokenFind(pWalker->pParse, p, (void*)pExpr); - }else if( pExpr->op==TK_COLUMN - && pExpr->iColumn==p->iCol -+ && ALWAYS(ExprUseYTab(pExpr)) - && p->pTab==pExpr->y.pTab - ){ - renameTokenFind(pWalker->pParse, p, (void*)pExpr); -@@ -106744,7 +118818,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ - } - - /* --** An error occured while parsing or otherwise processing a database -+** An error occurred while parsing or otherwise processing a database - ** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an - ** ALTER TABLE RENAME COLUMN program. The error message emitted by the - ** sub-routine is currently stored in pParse->zErrMsg. This function -@@ -106752,7 +118826,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ - */ - static void renameColumnParseError( - sqlite3_context *pCtx, -- int bPost, -+ const char *zWhen, - sqlite3_value *pType, - sqlite3_value *pObject, - Parse *pParse -@@ -106761,12 +118835,12 @@ static void renameColumnParseError( - const char *zN = (const char*)sqlite3_value_text(pObject); - char *zErr; - -- zErr = sqlite3_mprintf("error in %s %s%s: %s", -- zT, zN, (bPost ? " after rename" : ""), -+ zErr = sqlite3MPrintf(pParse->db, "error in %s %s%s%s: %s", -+ zT, zN, (zWhen[0] ? " " : ""), zWhen, - pParse->zErrMsg - ); - sqlite3_result_error(pCtx, zErr, -1); -- sqlite3_free(zErr); -+ sqlite3DbFree(pParse->db, zErr); - } - - /* -@@ -106778,18 +118852,18 @@ static void renameColumnParseError( - static void renameColumnElistNames( - Parse *pParse, - RenameCtx *pCtx, -- ExprList *pEList, -+ const ExprList *pEList, - const char *zOld - ){ - if( pEList ){ - int i; - for(i=0; inExpr; i++){ -- char *zName = pEList->a[i].zEName; -- if( ALWAYS(pEList->a[i].eEName==ENAME_NAME) -+ const char *zName = pEList->a[i].zEName; -+ if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) - && ALWAYS(zName!=0) - && 0==sqlite3_stricmp(zName, zOld) - ){ -- renameTokenFind(pParse, pCtx, (void*)zName); -+ renameTokenFind(pParse, pCtx, (const void*)zName); - } - } - } -@@ -106803,15 +118877,15 @@ static void renameColumnElistNames( - static void renameColumnIdlistNames( - Parse *pParse, - RenameCtx *pCtx, -- IdList *pIdList, -+ const IdList *pIdList, - const char *zOld - ){ - if( pIdList ){ - int i; - for(i=0; inId; i++){ -- char *zName = pIdList->a[i].zName; -+ const char *zName = pIdList->a[i].zName; - if( 0==sqlite3_stricmp(zName, zOld) ){ -- renameTokenFind(pParse, pCtx, (void*)zName); -+ renameTokenFind(pParse, pCtx, (const void*)zName); - } - } - } -@@ -106830,24 +118904,33 @@ static int renameParseSql( - int bTemp /* True if SQL is from temp schema */ - ){ - int rc; -- char *zErr = 0; -+ u64 flags; - -- db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); -- -- /* Parse the SQL statement passed as the first argument. If no error -- ** occurs and the parse does not result in a new table, index or -- ** trigger object, the database must be corrupt. */ -- memset(p, 0, sizeof(Parse)); -+ sqlite3ParseObjectInit(p, db); -+ if( zSql==0 ){ -+ return SQLITE_NOMEM; -+ } -+ if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ -+ return SQLITE_CORRUPT_BKPT; -+ } -+ if( bTemp ){ -+ db->init.iDb = 1; -+ }else{ -+ int iDb = sqlite3FindDbName(db, zDb); -+ assert( iDb>=0 && iDb<=0xff ); -+ db->init.iDb = (u8)iDb; -+ } - p->eParseMode = PARSE_MODE_RENAME; - p->db = db; - p->nQueryLoop = 1; -- rc = sqlite3RunParser(p, zSql, &zErr); -- assert( p->zErrMsg==0 ); -- assert( rc!=SQLITE_OK || zErr==0 ); -- p->zErrMsg = zErr; -+ flags = db->flags; -+ testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 ); -+ db->flags |= SQLITE_Comments; -+ rc = sqlite3RunParser(p, zSql); -+ db->flags = flags; - if( db->mallocFailed ) rc = SQLITE_NOMEM; - if( rc==SQLITE_OK -- && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0 -+ && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) - ){ - rc = SQLITE_CORRUPT_BKPT; - } -@@ -106884,54 +118967,82 @@ static int renameEditSql( - const char *zNew, /* New token text */ - int bQuote /* True to always quote token */ - ){ -- int nNew = sqlite3Strlen30(zNew); -- int nSql = sqlite3Strlen30(zSql); -+ i64 nNew = sqlite3Strlen30(zNew); -+ i64 nSql = sqlite3Strlen30(zSql); - sqlite3 *db = sqlite3_context_db_handle(pCtx); - int rc = SQLITE_OK; -- char *zQuot; -+ char *zQuot = 0; - char *zOut; -- int nQuot; -- -- /* Set zQuot to point to a buffer containing a quoted copy of the -- ** identifier zNew. If the corresponding identifier in the original -- ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to -- ** point to zQuot so that all substitutions are made using the -- ** quoted version of the new column name. */ -- zQuot = sqlite3MPrintf(db, "\"%w\"", zNew); -- if( zQuot==0 ){ -- return SQLITE_NOMEM; -+ i64 nQuot = 0; -+ char *zBuf1 = 0; -+ char *zBuf2 = 0; -+ -+ if( zNew ){ -+ /* Set zQuot to point to a buffer containing a quoted copy of the -+ ** identifier zNew. If the corresponding identifier in the original -+ ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to -+ ** point to zQuot so that all substitutions are made using the -+ ** quoted version of the new column name. */ -+ zQuot = sqlite3MPrintf(db, "\"%w\" ", zNew); -+ if( zQuot==0 ){ -+ return SQLITE_NOMEM; -+ }else{ -+ nQuot = sqlite3Strlen30(zQuot)-1; -+ } -+ -+ assert( nQuot>=nNew && nSql>=0 && nNew>=0 ); -+ zOut = sqlite3DbMallocZero(db, (u64)nSql + pRename->nList*(u64)nQuot + 1); - }else{ -- nQuot = sqlite3Strlen30(zQuot); -- } -- if( bQuote ){ -- zNew = zQuot; -- nNew = nQuot; -+ assert( nSql>0 ); -+ zOut = (char*)sqlite3DbMallocZero(db, (2*(u64)nSql + 1) * 3); -+ if( zOut ){ -+ zBuf1 = &zOut[nSql*2+1]; -+ zBuf2 = &zOut[nSql*4+2]; -+ } - } - - /* At this point pRename->pList contains a list of RenameToken objects - ** corresponding to all tokens in the input SQL that must be replaced -- ** with the new column name. All that remains is to construct and -- ** return the edited SQL string. */ -- assert( nQuot>=nNew ); -- zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); -+ ** with the new column name, or with single-quoted versions of themselves. -+ ** All that remains is to construct and return the edited SQL string. */ - if( zOut ){ -- int nOut = nSql; -- memcpy(zOut, zSql, nSql); -+ i64 nOut = nSql; -+ assert( nSql>0 ); -+ memcpy(zOut, zSql, (size_t)nSql); - while( pRename->pList ){ - int iOff; /* Offset of token to replace in zOut */ -+ i64 nReplace; -+ const char *zReplace; - RenameToken *pBest = renameColumnTokenNext(pRename); - -- u32 nReplace; -- const char *zReplace; -- if( sqlite3IsIdChar(*pBest->t.z) ){ -- nReplace = nNew; -- zReplace = zNew; -+ if( zNew ){ -+ if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){ -+ nReplace = nNew; -+ zReplace = zNew; -+ }else{ -+ nReplace = nQuot; -+ zReplace = zQuot; -+ if( pBest->t.z[pBest->t.n]=='"' ) nReplace++; -+ } - }else{ -- nReplace = nQuot; -- zReplace = zQuot; -+ /* Dequote the double-quoted token. Then requote it again, this time -+ ** using single quotes. If the character immediately following the -+ ** original token within the input SQL was a single quote ('), then -+ ** add another space after the new, single-quoted version of the -+ ** token. This is so that (SELECT "string"'alias') maps to -+ ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ -+ memcpy(zBuf1, pBest->t.z, pBest->t.n); -+ zBuf1[pBest->t.n] = 0; -+ sqlite3Dequote(zBuf1); -+ assert( nSql < 0x15555554 /* otherwise malloc would have failed */ ); -+ sqlite3_snprintf((int)(nSql*2), zBuf2, "%Q%s", zBuf1, -+ pBest->t.z[pBest->t.n]=='\'' ? " " : "" -+ ); -+ zReplace = zBuf2; -+ nReplace = sqlite3Strlen30(zReplace); - } - -- iOff = pBest->t.z - zSql; -+ iOff = (int)(pBest->t.z - zSql); - if( pBest->t.n!=nReplace ){ - memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], - nOut - (iOff + pBest->t.n) -@@ -106953,6 +119064,20 @@ static int renameEditSql( - return rc; - } - -+/* -+** Set all pEList->a[].fg.eEName fields in the expression-list to val. -+*/ -+static void renameSetENames(ExprList *pEList, int val){ -+ assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN ); -+ if( pEList ){ -+ int i; -+ for(i=0; inExpr; i++){ -+ assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); -+ pEList->a[i].fg.eEName = val&0x3; -+ } -+ } -+} -+ - /* - ** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming - ** it was read from the schema of database zDb. Return SQLITE_OK if -@@ -106976,7 +119101,7 @@ static int renameResolveTrigger(Parse *pParse){ - /* ALWAYS() because if the table of the trigger does not exist, the - ** error would have been hit before this point */ - if( ALWAYS(pParse->pTriggerTab) ){ -- rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab); -+ rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab)!=0; - } - - /* Resolve symbols in WHEN clause */ -@@ -106992,18 +119117,46 @@ static int renameResolveTrigger(Parse *pParse){ - if( rc==SQLITE_OK && pStep->zTarget ){ - SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); - if( pSrc ){ -- int i; -- for(i=0; inSrc && rc==SQLITE_OK; i++){ -- struct SrcList_item *p = &pSrc->a[i]; -- p->pTab = sqlite3LocateTableItem(pParse, 0, p); -- p->iCursor = pParse->nTab++; -- if( p->pTab==0 ){ -- rc = SQLITE_ERROR; -- }else{ -- p->pTab->nTabRef++; -- rc = sqlite3ViewGetColumnNames(pParse, p->pTab); -+ Select *pSel = sqlite3SelectNew( -+ pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0 -+ ); -+ if( pSel==0 ){ -+ pStep->pExprList = 0; -+ pSrc = 0; -+ rc = SQLITE_NOMEM; -+ }else{ -+ /* pStep->pExprList contains an expression-list used for an UPDATE -+ ** statement. So the a[].zEName values are the RHS of the -+ ** "= " clauses of the UPDATE statement. So, before -+ ** running SelectPrep(), change all the eEName values in -+ ** pStep->pExprList to ENAME_SPAN (from their current value of -+ ** ENAME_NAME). This is to prevent any ids in ON() clauses that are -+ ** part of pSrc from being incorrectly resolved against the -+ ** a[].zEName values as if they were column aliases. */ -+ renameSetENames(pStep->pExprList, ENAME_SPAN); -+ sqlite3SelectPrep(pParse, pSel, 0); -+ renameSetENames(pStep->pExprList, ENAME_NAME); -+ rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; -+ assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); -+ assert( pSrc==pSel->pSrc ); -+ if( pStep->pExprList ) pSel->pEList = 0; -+ pSel->pSrc = 0; -+ sqlite3SelectDelete(db, pSel); -+ } -+ if( pStep->pFrom ){ -+ int i; -+ for(i=0; ipFrom->nSrc && rc==SQLITE_OK; i++){ -+ SrcItem *p = &pStep->pFrom->a[i]; -+ if( p->fg.isSubquery ){ -+ assert( p->u4.pSubq!=0 ); -+ sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0); -+ } - } - } -+ -+ if( db->mallocFailed ){ -+ rc = SQLITE_NOMEM; -+ } - sNC.pSrcList = pSrc; - if( rc==SQLITE_OK && pStep->pWhere ){ - rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); -@@ -107012,9 +119165,8 @@ static int renameResolveTrigger(Parse *pParse){ - rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); - } - assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); -- if( pStep->pUpsert ){ -+ if( pStep->pUpsert && rc==SQLITE_OK ){ - Upsert *pUpsert = pStep->pUpsert; -- assert( rc==SQLITE_OK ); - pUpsert->pUpsertSrc = pSrc; - sNC.uNC.pUpsert = pUpsert; - sNC.ncFlags = NC_UUpsert; -@@ -107063,6 +119215,16 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ - sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere); - sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere); - } -+ if( pStep->pFrom ){ -+ int i; -+ SrcList *pFrom = pStep->pFrom; -+ for(i=0; inSrc; i++){ -+ if( pFrom->a[i].fg.isSubquery ){ -+ assert( pFrom->a[i].u4.pSubq!=0 ); -+ sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect); -+ } -+ } -+ } - } - } - -@@ -107084,13 +119246,13 @@ static void renameParseCleanup(Parse *pParse){ - sqlite3DeleteTrigger(db, pParse->pNewTrigger); - sqlite3DbFree(db, pParse->zErrMsg); - renameTokenFree(db, pParse->pRename); -- sqlite3ParserReset(pParse); -+ sqlite3ParseObjectReset(pParse); - } - - /* - ** SQL function: - ** --** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld) -+** sqlite_rename_column(SQL,TYPE,OBJ,DB,TABLE,COL,NEWNAME,QUOTE,TEMP) - ** - ** 0. zSql: SQL statement to rewrite - ** 1. type: Type of object ("table", "view" etc.) -@@ -107108,7 +119270,8 @@ static void renameParseCleanup(Parse *pParse){ - ** - ** This function is used internally by the ALTER TABLE RENAME COLUMN command. - ** It is only accessible to SQL created using sqlite3NestedParse(). It is --** not reachable from ordinary SQL passed into sqlite3_prepare(). -+** not reachable from ordinary SQL passed into sqlite3_prepare() unless the -+** SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test setting is enabled. - */ - static void renameColumnFunc( - sqlite3_context *context, -@@ -107146,7 +119309,7 @@ static void renameColumnFunc( - sqlite3BtreeLeaveAll(db); - return; - } -- zOld = pTab->aCol[iCol].zName; -+ zOld = pTab->aCol[iCol].zCnName; - memset(&sCtx, 0, sizeof(sCtx)); - sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); - -@@ -107165,9 +119328,9 @@ static void renameColumnFunc( - sCtx.pTab = pTab; - if( rc!=SQLITE_OK ) goto renameColumnFunc_done; - if( sParse.pNewTable ){ -- Select *pSelect = sParse.pNewTable->pSelect; -- if( pSelect ){ -- pSelect->selFlags &= ~SF_View; -+ if( IsView(sParse.pNewTable) ){ -+ Select *pSelect = sParse.pNewTable->u.view.pSelect; -+ pSelect->selFlags &= ~(u32)SF_View; - sParse.rc = SQLITE_OK; - sqlite3SelectPrep(&sParse, pSelect, 0); - rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); -@@ -107175,16 +119338,17 @@ static void renameColumnFunc( - sqlite3WalkSelect(&sWalker, pSelect); - } - if( rc!=SQLITE_OK ) goto renameColumnFunc_done; -- }else{ -+ }else if( IsOrdinaryTable(sParse.pNewTable) ){ - /* A regular table */ - int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); - FKey *pFKey; -- assert( sParse.pNewTable->pSelect==0 ); - sCtx.pTab = sParse.pNewTable; - if( bFKOnly==0 ){ -- renameTokenFind( -- &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName -- ); -+ if( iColnCol ){ -+ renameTokenFind( -+ &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zCnName -+ ); -+ } - if( sCtx.iCol<0 ){ - renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey); - } -@@ -107195,14 +119359,17 @@ static void renameColumnFunc( - for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ - sqlite3WalkExprList(&sWalker, pIdx->aColExpr); - } -- } - #ifndef SQLITE_OMIT_GENERATED_COLUMNS -- for(i=0; inCol; i++){ -- sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); -- } -+ for(i=0; inCol; i++){ -+ Expr *pExpr = sqlite3ColumnExpr(sParse.pNewTable, -+ &sParse.pNewTable->aCol[i]); -+ sqlite3WalkExpr(&sWalker, pExpr); -+ } - #endif -+ } - -- for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ -+ assert( IsOrdinaryTable(sParse.pNewTable) ); -+ for(pFKey=sParse.pNewTable->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ - for(i=0; inCol; i++){ - if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ - renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); -@@ -107253,8 +119420,10 @@ static void renameColumnFunc( - - renameColumnFunc_done: - if( rc!=SQLITE_OK ){ -- if( sParse.zErrMsg ){ -- renameColumnParseError(context, 0, argv[1], argv[2], &sParse); -+ if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){ -+ sqlite3_result_value(context, argv[0]); -+ }else if( sParse.zErrMsg ){ -+ renameColumnParseError(context, "", argv[1], argv[2], &sParse); - }else{ - sqlite3_result_error_code(context, rc); - } -@@ -107273,7 +119442,10 @@ renameColumnFunc_done: - */ - static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ - RenameCtx *p = pWalker->u.pRename; -- if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){ -+ if( pExpr->op==TK_COLUMN -+ && ALWAYS(ExprUseYTab(pExpr)) -+ && p->pTab==pExpr->y.pTab -+ ){ - renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab); - } - return WRC_Continue; -@@ -107286,14 +119458,18 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ - int i; - RenameCtx *p = pWalker->u.pRename; - SrcList *pSrc = pSelect->pSrc; -- if( pSelect->selFlags & SF_View ) return WRC_Prune; -- if( pSrc==0 ){ -+ if( pSelect->selFlags & (SF_View|SF_CopyCte) ){ -+ testcase( pSelect->selFlags & SF_View ); -+ testcase( pSelect->selFlags & SF_CopyCte ); -+ return WRC_Prune; -+ } -+ if( NEVER(pSrc==0) ){ - assert( pWalker->pParse->db->mallocFailed ); - return WRC_Abort; - } - for(i=0; inSrc; i++){ -- struct SrcList_item *pItem = &pSrc->a[i]; -- if( pItem->pTab==p->pTab ){ -+ SrcItem *pItem = &pSrc->a[i]; -+ if( pItem->pSTab==p->pTab ){ - renameTokenFind(pWalker->pParse, p, pItem->zName); - } - } -@@ -107364,28 +119540,31 @@ static void renameTableFunc( - if( sParse.pNewTable ){ - Table *pTab = sParse.pNewTable; - -- if( pTab->pSelect ){ -+ if( IsView(pTab) ){ - if( isLegacy==0 ){ -- Select *pSelect = pTab->pSelect; -+ Select *pSelect = pTab->u.view.pSelect; - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = &sParse; - - assert( pSelect->selFlags & SF_View ); -- pSelect->selFlags &= ~SF_View; -- sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); -+ pSelect->selFlags &= ~(u32)SF_View; -+ sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); - if( sParse.nErr ){ - rc = sParse.rc; - }else{ -- sqlite3WalkSelect(&sWalker, pTab->pSelect); -+ sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); - } - } - }else{ - /* Modify any FK definitions to point to the new table. */ - #ifndef SQLITE_OMIT_FOREIGN_KEY -- if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){ -+ if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys)) -+ && !IsVirtual(pTab) -+ ){ - FKey *pFKey; -- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ -+ assert( IsOrdinaryTable(pTab) ); -+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ - if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ - renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo); - } -@@ -107431,6 +119610,15 @@ static void renameTableFunc( - if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ - renameTokenFind(&sParse, &sCtx, pStep->zTarget); - } -+ if( pStep->pFrom ){ -+ int i; -+ for(i=0; ipFrom->nSrc; i++){ -+ SrcItem *pItem = &pStep->pFrom->a[i]; -+ if( 0==sqlite3_stricmp(pItem->zName, zOld) ){ -+ renameTokenFind(&sParse, &sCtx, pItem->zName); -+ } -+ } -+ } - } - } - } -@@ -107442,8 +119630,10 @@ static void renameTableFunc( - rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote); - } - if( rc!=SQLITE_OK ){ -- if( sParse.zErrMsg ){ -- renameColumnParseError(context, 0, argv[1], argv[2], &sParse); -+ if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){ -+ sqlite3_result_value(context, argv[3]); -+ }else if( sParse.zErrMsg ){ -+ renameColumnParseError(context, "", argv[1], argv[2], &sParse); - }else{ - sqlite3_result_error_code(context, rc); - } -@@ -107460,7 +119650,131 @@ static void renameTableFunc( - return; - } - --/* -+static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){ -+ if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){ -+ renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr); -+ } -+ return WRC_Continue; -+} -+ -+/* SQL function: sqlite_rename_quotefix(DB,SQL) -+** -+** Rewrite the DDL statement "SQL" so that any string literals that use -+** double-quotes use single quotes instead. -+** -+** Two arguments must be passed: -+** -+** 0: Database name ("main", "temp" etc.). -+** 1: SQL statement to edit. -+** -+** The returned value is the modified SQL statement. For example, given -+** the database schema: -+** -+** CREATE TABLE t1(a, b, c); -+** -+** SELECT sqlite_rename_quotefix('main', -+** 'CREATE VIEW v1 AS SELECT "a", "string" FROM t1' -+** ); -+** -+** returns the string: -+** -+** CREATE VIEW v1 AS SELECT "a", 'string' FROM t1 -+** -+** If there is a error in the input SQL, then raise an error, except -+** if PRAGMA writable_schema=ON, then just return the input string -+** unmodified following an error. -+*/ -+static void renameQuotefixFunc( -+ sqlite3_context *context, -+ int NotUsed, -+ sqlite3_value **argv -+){ -+ sqlite3 *db = sqlite3_context_db_handle(context); -+ char const *zDb = (const char*)sqlite3_value_text(argv[0]); -+ char const *zInput = (const char*)sqlite3_value_text(argv[1]); -+ -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ sqlite3_xauth xAuth = db->xAuth; -+ db->xAuth = 0; -+#endif -+ -+ sqlite3BtreeEnterAll(db); -+ -+ UNUSED_PARAMETER(NotUsed); -+ if( zDb && zInput ){ -+ int rc; -+ Parse sParse; -+ rc = renameParseSql(&sParse, zDb, db, zInput, 0); -+ -+ if( rc==SQLITE_OK ){ -+ RenameCtx sCtx; -+ Walker sWalker; -+ -+ /* Walker to find tokens that need to be replaced. */ -+ memset(&sCtx, 0, sizeof(RenameCtx)); -+ memset(&sWalker, 0, sizeof(Walker)); -+ sWalker.pParse = &sParse; -+ sWalker.xExprCallback = renameQuotefixExprCb; -+ sWalker.xSelectCallback = renameColumnSelectCb; -+ sWalker.u.pRename = &sCtx; -+ -+ if( sParse.pNewTable ){ -+ if( IsView(sParse.pNewTable) ){ -+ Select *pSelect = sParse.pNewTable->u.view.pSelect; -+ pSelect->selFlags &= ~(u32)SF_View; -+ sParse.rc = SQLITE_OK; -+ sqlite3SelectPrep(&sParse, pSelect, 0); -+ rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); -+ if( rc==SQLITE_OK ){ -+ sqlite3WalkSelect(&sWalker, pSelect); -+ } -+ }else{ -+ int i; -+ sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); -+#ifndef SQLITE_OMIT_GENERATED_COLUMNS -+ for(i=0; inCol; i++){ -+ sqlite3WalkExpr(&sWalker, -+ sqlite3ColumnExpr(sParse.pNewTable, -+ &sParse.pNewTable->aCol[i])); -+ } -+#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ -+ } -+ }else if( sParse.pNewIndex ){ -+ sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); -+ sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); -+ }else{ -+#ifndef SQLITE_OMIT_TRIGGER -+ rc = renameResolveTrigger(&sParse); -+ if( rc==SQLITE_OK ){ -+ renameWalkTrigger(&sWalker, sParse.pNewTrigger); -+ } -+#endif /* SQLITE_OMIT_TRIGGER */ -+ } -+ -+ if( rc==SQLITE_OK ){ -+ rc = renameEditSql(context, &sCtx, zInput, 0, 0); -+ } -+ renameTokenFree(db, sCtx.pList); -+ } -+ if( rc!=SQLITE_OK ){ -+ if( sqlite3WritableSchema(db) && rc==SQLITE_ERROR ){ -+ sqlite3_result_value(context, argv[1]); -+ }else{ -+ sqlite3_result_error_code(context, rc); -+ } -+ } -+ renameParseCleanup(&sParse); -+ } -+ -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ db->xAuth = xAuth; -+#endif -+ -+ sqlite3BtreeLeaveAll(db); -+} -+ -+/* Function: sqlite_rename_test(DB,SQL,TYPE,NAME,ISTEMP,WHEN,DQS) -+** - ** An SQL user function that checks that there are no parse or symbol - ** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement. - ** After an ALTER TABLE .. RENAME operation is performed and the schema -@@ -107472,12 +119786,16 @@ static void renameTableFunc( - ** 2: Object type ("view", "table", "trigger" or "index"). - ** 3: Object name. - ** 4: True if object is from temp schema. -+** 5: "when" part of error message. -+** 6: True to disable the DQS quirk when parsing SQL. - ** --** Unless it finds an error, this function normally returns NULL. However, it --** returns integer value 1 if: -+** The return value is computed as follows: - ** --** * the SQL argument creates a trigger, and --** * the table that the trigger is attached to is in database zDb. -+** A. If an error is seen and not in PRAGMA writable_schema=ON mode, -+** then raise the error. -+** B. Else if a trigger is created and the the table that the trigger is -+** attached to is in database zDb, then return 1. -+** C. Otherwise return NULL. - */ - static void renameTableTest( - sqlite3_context *context, -@@ -107489,6 +119807,8 @@ static void renameTableTest( - char const *zInput = (const char*)sqlite3_value_text(argv[1]); - int bTemp = sqlite3_value_int(argv[4]); - int isLegacy = (db->flags & SQLITE_LegacyAlter); -+ char const *zWhen = (const char*)sqlite3_value_text(argv[5]); -+ int bNoDQS = sqlite3_value_int(argv[6]); - - #ifndef SQLITE_OMIT_AUTHORIZATION - sqlite3_xauth xAuth = db->xAuth; -@@ -107496,16 +119816,20 @@ static void renameTableTest( - #endif - - UNUSED_PARAMETER(NotUsed); -+ - if( zDb && zInput ){ - int rc; - Parse sParse; -+ u64 flags = db->flags; -+ if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); - rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); -+ db->flags = flags; - if( rc==SQLITE_OK ){ -- if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ -+ if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = &sParse; -- sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); -+ sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC); - if( sParse.nErr ) rc = sParse.rc; - } - -@@ -107516,13 +119840,17 @@ static void renameTableTest( - if( rc==SQLITE_OK ){ - int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema); - int i2 = sqlite3FindDbName(db, zDb); -- if( i1==i2 ) sqlite3_result_int(context, 1); -+ if( i1==i2 ){ -+ /* Handle output case B */ -+ sqlite3_result_int(context, 1); -+ } - } - } - } - -- if( rc!=SQLITE_OK ){ -- renameColumnParseError(context, 1, argv[2], argv[3], &sParse); -+ if( rc!=SQLITE_OK && zWhen && !sqlite3WritableSchema(db) ){ -+ /* Output case A */ -+ renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse); - } - renameParseCleanup(&sParse); - } -@@ -107532,14 +119860,237 @@ static void renameTableTest( - #endif - } - -+/* -+** The implementation of internal UDF sqlite_drop_column(). -+** -+** Arguments: -+** -+** argv[0]: An integer - the index of the schema containing the table -+** argv[1]: CREATE TABLE statement to modify. -+** argv[2]: An integer - the index of the column to remove. -+** -+** The value returned is a string containing the CREATE TABLE statement -+** with column argv[2] removed. -+*/ -+static void dropColumnFunc( -+ sqlite3_context *context, -+ int NotUsed, -+ sqlite3_value **argv -+){ -+ sqlite3 *db = sqlite3_context_db_handle(context); -+ int iSchema = sqlite3_value_int(argv[0]); -+ const char *zSql = (const char*)sqlite3_value_text(argv[1]); -+ int iCol = sqlite3_value_int(argv[2]); -+ const char *zDb = db->aDb[iSchema].zDbSName; -+ int rc; -+ Parse sParse; -+ RenameToken *pCol; -+ Table *pTab; -+ const char *zEnd; -+ char *zNew = 0; -+ -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ sqlite3_xauth xAuth = db->xAuth; -+ db->xAuth = 0; -+#endif -+ -+ UNUSED_PARAMETER(NotUsed); -+ rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); -+ if( rc!=SQLITE_OK ) goto drop_column_done; -+ pTab = sParse.pNewTable; -+ if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ -+ /* This can happen if the sqlite_schema table is corrupt */ -+ rc = SQLITE_CORRUPT_BKPT; -+ goto drop_column_done; -+ } -+ -+ pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); -+ if( iColnCol-1 ){ -+ RenameToken *pEnd; -+ pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName); -+ zEnd = (const char*)pEnd->t.z; -+ }else{ -+ assert( IsOrdinaryTable(pTab) ); -+ zEnd = (const char*)&zSql[pTab->u.tab.addColOffset]; -+ while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--; -+ } -+ -+ zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd); -+ sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT); -+ sqlite3_free(zNew); -+ -+drop_column_done: -+ renameParseCleanup(&sParse); -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ db->xAuth = xAuth; -+#endif -+ if( rc!=SQLITE_OK ){ -+ sqlite3_result_error_code(context, rc); -+ } -+} -+ -+/* -+** This function is called by the parser upon parsing an -+** -+** ALTER TABLE pSrc DROP COLUMN pName -+** -+** statement. Argument pSrc contains the possibly qualified name of the -+** table being edited, and token pName the name of the column to drop. -+*/ -+SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){ -+ sqlite3 *db = pParse->db; /* Database handle */ -+ Table *pTab; /* Table to modify */ -+ int iDb; /* Index of db containing pTab in aDb[] */ -+ const char *zDb; /* Database containing pTab ("main" etc.) */ -+ char *zCol = 0; /* Name of column to drop */ -+ int iCol; /* Index of column zCol in pTab->aCol[] */ -+ -+ /* Look up the table being altered. */ -+ assert( pParse->pNewTable==0 ); -+ assert( sqlite3BtreeHoldsAllMutexes(db) ); -+ if( NEVER(db->mallocFailed) ) goto exit_drop_column; -+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); -+ if( !pTab ) goto exit_drop_column; -+ -+ /* Make sure this is not an attempt to ALTER a view, virtual table or -+ ** system table. */ -+ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column; -+ if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column; -+ -+ /* Find the index of the column being dropped. */ -+ zCol = sqlite3NameFromToken(db, pName); -+ if( zCol==0 ){ -+ assert( db->mallocFailed ); -+ goto exit_drop_column; -+ } -+ iCol = sqlite3ColumnIndex(pTab, zCol); -+ if( iCol<0 ){ -+ sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pName); -+ goto exit_drop_column; -+ } -+ -+ /* Do not allow the user to drop a PRIMARY KEY column or a column -+ ** constrained by a UNIQUE constraint. */ -+ if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){ -+ sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"", -+ (pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE", -+ zCol -+ ); -+ goto exit_drop_column; -+ } -+ -+ /* Do not allow the number of columns to go to zero */ -+ if( pTab->nCol<=1 ){ -+ sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol); -+ goto exit_drop_column; -+ } -+ -+ /* Edit the sqlite_schema table */ -+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); -+ assert( iDb>=0 ); -+ zDb = db->aDb[iDb].zDbSName; -+#ifndef SQLITE_OMIT_AUTHORIZATION -+ /* Invoke the authorization callback. */ -+ if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ -+ goto exit_drop_column; -+ } -+#endif -+ renameTestSchema(pParse, zDb, iDb==1, "", 0); -+ renameFixQuotes(pParse, zDb, iDb==1); -+ sqlite3NestedParse(pParse, -+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " -+ "sql = sqlite_drop_column(%d, sql, %d) " -+ "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" -+ , zDb, iDb, iCol, pTab->zName -+ ); -+ -+ /* Drop and reload the database schema. */ -+ renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); -+ renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1); -+ -+ /* Edit rows of table on disk */ -+ if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ -+ int i; -+ int addr; -+ int reg; -+ int regRec; -+ Index *pPk = 0; -+ int nField = 0; /* Number of non-virtual columns after drop */ -+ int iCur; -+ Vdbe *v = sqlite3GetVdbe(pParse); -+ iCur = pParse->nTab++; -+ sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); -+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); -+ reg = ++pParse->nMem; -+ if( HasRowid(pTab) ){ -+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg); -+ pParse->nMem += pTab->nCol; -+ }else{ -+ pPk = sqlite3PrimaryKeyIndex(pTab); -+ pParse->nMem += pPk->nColumn; -+ for(i=0; inKeyCol; i++){ -+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, reg+i+1); -+ } -+ nField = pPk->nKeyCol; -+ } -+ regRec = ++pParse->nMem; -+ for(i=0; inCol; i++){ -+ if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ -+ int regOut; -+ if( pPk ){ -+ int iPos = sqlite3TableColumnToIndex(pPk, i); -+ int iColPos = sqlite3TableColumnToIndex(pPk, iCol); -+ if( iPosnKeyCol ) continue; -+ regOut = reg+1+iPos-(iPos>iColPos); -+ }else{ -+ regOut = reg+1+nField; -+ } -+ if( i==pTab->iPKey ){ -+ sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); -+ }else{ -+ char aff = pTab->aCol[i].affinity; -+ if( aff==SQLITE_AFF_REAL ){ -+ pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC; -+ } -+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); -+ pTab->aCol[i].affinity = aff; -+ } -+ nField++; -+ } -+ } -+ if( nField==0 ){ -+ /* dbsqlfuzz 5f09e7bcc78b4954d06bf9f2400d7715f48d1fef */ -+ pParse->nMem++; -+ sqlite3VdbeAddOp2(v, OP_Null, 0, reg+1); -+ nField = 1; -+ } -+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec); -+ if( pPk ){ -+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol); -+ }else{ -+ sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg); -+ } -+ sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); -+ -+ sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v); -+ sqlite3VdbeJumpHere(v, addr); -+ } -+ -+exit_drop_column: -+ sqlite3DbFree(db, zCol); -+ sqlite3SrcListDelete(db, pSrc); -+} -+ - /* - ** Register built-in functions used to help implement ALTER TABLE - */ - SQLITE_PRIVATE void sqlite3AlterFunctions(void){ - static FuncDef aAlterTableFuncs[] = { -- INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), -- INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), -- INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest), -+ INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), -+ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), -+ INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), -+ INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), -+ INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), - }; - sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); - } -@@ -107764,7 +120315,8 @@ static void openStatTable( - sqlite3NestedParse(pParse, - "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols - ); -- aRoot[i] = (u32)pParse->regRoot; -+ assert( pParse->isCreate || pParse->nErr ); -+ aRoot[i] = (u32)pParse->u1.cr.regRoot; - aCreateTbl[i] = OPFLAG_P2ISREG; - } - }else{ -@@ -107813,9 +120365,9 @@ static void openStatTable( - typedef struct StatAccum StatAccum; - typedef struct StatSample StatSample; - struct StatSample { -- tRowcnt *anEq; /* sqlite_stat4.nEq */ - tRowcnt *anDLt; /* sqlite_stat4.nDLt */ - #ifdef SQLITE_ENABLE_STAT4 -+ tRowcnt *anEq; /* sqlite_stat4.nEq */ - tRowcnt *anLt; /* sqlite_stat4.nLt */ - union { - i64 iRowid; /* Rowid in main table of the key */ -@@ -107955,7 +120507,7 @@ static void statInit( - int nCol; /* Number of columns in index being sampled */ - int nKeyCol; /* Number of key columns */ - int nColUp; /* nCol rounded up for alignment */ -- int n; /* Bytes of space to allocate */ -+ i64 n; /* Bytes of space to allocate */ - sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */ - #ifdef SQLITE_ENABLE_STAT4 - /* Maximum number of samples. 0 if STAT4 data is not collected */ -@@ -107973,16 +120525,15 @@ static void statInit( - - /* Allocate the space required for the StatAccum object */ - n = sizeof(*p) -- + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ -- + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ -+ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ - #ifdef SQLITE_ENABLE_STAT4 -+ n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */ - if( mxSample ){ - n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ - + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ - + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); - } - #endif -- db = sqlite3_context_db_handle(context); - p = sqlite3DbMallocZero(db, n); - if( p==0 ){ - sqlite3_result_error_nomem(context); -@@ -107992,14 +120543,14 @@ static void statInit( - p->db = db; - p->nEst = sqlite3_value_int64(argv[2]); - p->nRow = 0; -- p->nLimit = sqlite3_value_int64(argv[3]); -+ p->nLimit = sqlite3_value_int(argv[3]); - p->nCol = nCol; - p->nKeyCol = nKeyCol; - p->nSkipAhead = 0; - p->current.anDLt = (tRowcnt*)&p[1]; -- p->current.anEq = &p->current.anDLt[nColUp]; - - #ifdef SQLITE_ENABLE_STAT4 -+ p->current.anEq = &p->current.anDLt[nColUp]; - p->mxSample = p->nLimit==0 ? mxSample : 0; - if( mxSample ){ - u8 *pSpace; /* Allocated space not yet assigned */ -@@ -108266,7 +120817,9 @@ static void statPush( - - if( p->nRow==0 ){ - /* This is the first call to this function. Do initialization. */ -+#ifdef SQLITE_ENABLE_STAT4 - for(i=0; inCol; i++) p->current.anEq[i] = 1; -+#endif - }else{ - /* Second and subsequent calls get processed here */ - #ifdef SQLITE_ENABLE_STAT4 -@@ -108275,15 +120828,17 @@ static void statPush( - - /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply - ** to the current row of the index. */ -+#ifdef SQLITE_ENABLE_STAT4 - for(i=0; icurrent.anEq[i]++; - } -+#endif - for(i=iChng; inCol; i++){ - p->current.anDLt[i]++; - #ifdef SQLITE_ENABLE_STAT4 - if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; --#endif - p->current.anEq[i] = 1; -+#endif - } - } - -@@ -108397,32 +120952,31 @@ static void statGet( - ** * "WHERE a=? AND b=?" matches 2 rows. - ** - ** If D is the count of distinct values and K is the total number of -- ** rows, then each estimate is computed as: -+ ** rows, then each estimate is usually computed as: - ** - ** I = (K+D-1)/D -+ ** -+ ** In other words, I is K/D rounded up to the next whole integer. -+ ** However, if I is between 1.0 and 1.1 (in other words if I is -+ ** close to 1.0 but just a little larger) then do not round up but -+ ** instead keep the I value at 1.0. - */ -- char *z; -- int i; -- -- char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 ); -- if( zRet==0 ){ -- sqlite3_result_error_nomem(context); -- return; -- } -+ sqlite3_str sStat; /* Text of the constructed "stat" line */ -+ int i; /* Loop counter */ - -- sqlite3_snprintf(24, zRet, "%llu", -+ sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100); -+ sqlite3_str_appendf(&sStat, "%llu", - p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow); -- z = zRet + sqlite3Strlen30(zRet); - for(i=0; inKeyCol; i++){ - u64 nDistinct = p->current.anDLt[i] + 1; - u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; -- sqlite3_snprintf(24, z, " %llu", iVal); -- z += sqlite3Strlen30(z); -- assert( p->current.anEq[i] ); -+ if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; -+ sqlite3_str_appendf(&sStat, " %llu", iVal); -+#ifdef SQLITE_ENABLE_STAT4 -+ assert( p->current.anEq[i] || p->nRow==0 ); -+#endif - } -- assert( z[0]=='\0' && z>zRet ); -- -- sqlite3_result_text(context, zRet, -1, sqlite3_free); -+ sqlite3ResultStrAccum(context, &sStat); - } - #ifdef SQLITE_ENABLE_STAT4 - else if( eCall==STAT_GET_ROWID ){ -@@ -108441,6 +120995,8 @@ static void statGet( - } - }else{ - tRowcnt *aCnt = 0; -+ sqlite3_str sStat; -+ int i; - - assert( p->iGetnSample ); - switch( eCall ){ -@@ -108452,23 +121008,12 @@ static void statGet( - break; - } - } -- -- { -- char *zRet = sqlite3MallocZero(p->nCol * 25); -- if( zRet==0 ){ -- sqlite3_result_error_nomem(context); -- }else{ -- int i; -- char *z = zRet; -- for(i=0; inCol; i++){ -- sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]); -- z += sqlite3Strlen30(z); -- } -- assert( z[0]=='\0' && z>zRet ); -- z[-1] = '\0'; -- sqlite3_result_text(context, zRet, -1, sqlite3_free); -- } -+ sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100); -+ for(i=0; inCol; i++){ -+ sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]); - } -+ if( sStat.nChar ) sStat.nChar--; -+ sqlite3ResultStrAccum(context, &sStat); - } - #endif /* SQLITE_ENABLE_STAT4 */ - #ifndef SQLITE_DEBUG -@@ -108515,9 +121060,10 @@ static void analyzeVdbeCommentIndexWithColumnName( - if( NEVER(i==XN_ROWID) ){ - VdbeComment((v,"%s.rowid",pIdx->zName)); - }else if( i==XN_EXPR ){ -+ assert( pIdx->bHasExpr ); - VdbeComment((v,"%s.expr(%d)",pIdx->zName, k)); - }else{ -- VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zName)); -+ VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName)); - } - } - #else -@@ -108555,16 +121101,20 @@ static void analyzeOneTable( - int regIdxname = iMem++; /* Register containing index name */ - int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ - int regPrev = iMem; /* MUST BE LAST (see below) */ -+#ifdef SQLITE_ENABLE_STAT4 -+ int doOnce = 1; /* Flag for a one-time computation */ -+#endif - #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - Table *pStat1 = 0; - #endif - -- pParse->nMem = MAX(pParse->nMem, iMem); -+ sqlite3TouchRegister(pParse, iMem); -+ assert( sqlite3NoTempsInRange(pParse, regNewRowid, iMem) ); - v = sqlite3GetVdbe(pParse); - if( v==0 || NEVER(pTab==0) ){ - return; - } -- if( pTab->tnum==0 ){ -+ if( !IsOrdinaryTable(pTab) ){ - /* Do not gather statistics on views or virtual tables */ - return; - } -@@ -108591,7 +121141,7 @@ static void analyzeOneTable( - memcpy(pStat1->zName, "sqlite_stat1", 13); - pStat1->nCol = 3; - pStat1->iPKey = -1; -- sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNBLOB); -+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNAMIC); - } - #endif - -@@ -108608,7 +121158,7 @@ static void analyzeOneTable( - - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int nCol; /* Number of columns in pIdx. "N" */ -- int addrRewind; /* Address of "OP_Rewind iIdxCur" */ -+ int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */ - int addrNextRow; /* Address of "next_row:" */ - const char *zIdxName; /* Name of the index */ - int nColTest; /* Number of columns to test for changes */ -@@ -108632,9 +121182,14 @@ static void analyzeOneTable( - /* - ** Pseudo-code for loop that calls stat_push(): - ** -- ** Rewind csr -- ** if eof(csr) goto end_of_scan; - ** regChng = 0 -+ ** Rewind csr -+ ** if eof(csr){ -+ ** stat_init() with count = 0; -+ ** goto end_of_scan; -+ ** } -+ ** count() -+ ** stat_init() - ** goto chng_addr_0; - ** - ** next_row: -@@ -108665,7 +121220,7 @@ static void analyzeOneTable( - ** the regPrev array and a trailing rowid (the rowid slot is required - ** when building a record to insert into the sample column of - ** the sqlite_stat4 table. */ -- pParse->nMem = MAX(pParse->nMem, regPrev+nColTest); -+ sqlite3TouchRegister(pParse, regPrev+nColTest); - - /* Open a read-only cursor on the index being analyzed. */ - assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); -@@ -108673,41 +121228,36 @@ static void analyzeOneTable( - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - VdbeComment((v, "%s", pIdx->zName)); - -- /* Invoke the stat_init() function. The arguments are: -+ /* Implementation of the following: - ** -+ ** regChng = 0 -+ ** Rewind csr -+ ** if eof(csr){ -+ ** stat_init() with count = 0; -+ ** goto end_of_scan; -+ ** } -+ ** count() -+ ** stat_init() -+ ** goto chng_addr_0; -+ */ -+ assert( regTemp2==regStat+4 ); -+ sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); -+ -+ /* Arguments to stat_init(): - ** (1) the number of columns in the index including the rowid - ** (or for a WITHOUT ROWID table, the number of PK columns), - ** (2) the number of columns in the key without the rowid/pk -- ** (3) estimated number of rows in the index, -- */ -+ ** (3) estimated number of rows in the index. */ - sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); - assert( regRowid==regStat+2 ); - sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); --#ifdef SQLITE_ENABLE_STAT4 -- if( OptimizationEnabled(db, SQLITE_Stat4) ){ -- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp); -- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); -- VdbeCoverage(v); -- }else --#endif -- { -- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); -- VdbeCoverage(v); -- sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1); -- } -- assert( regTemp2==regStat+4 ); -- sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); -+ sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, -+ OptimizationDisabled(db, SQLITE_Stat4)); - sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, - &statInitFuncdef, 0); -+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); -+ VdbeCoverage(v); - -- /* Implementation of the following: -- ** -- ** Rewind csr -- ** if eof(csr) goto end_of_scan; -- ** regChng = 0 -- ** goto next_push_0; -- ** -- */ - sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); - addrNextRow = sqlite3VdbeCurrentAddr(v); - -@@ -108814,6 +121364,12 @@ static void analyzeOneTable( - } - - /* Add the entry to the stat1 table. */ -+ if( pIdx->pPartIdxWhere ){ -+ /* Partial indexes might get a zero-entry in sqlite_stat1. But -+ ** an empty table is omitted from sqlite_stat1. */ -+ sqlite3VdbeJumpHere(v, addrGotoEnd); -+ addrGotoEnd = 0; -+ } - callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); - assert( "BBB"[0]==SQLITE_AFF_TEXT ); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); -@@ -108837,7 +121393,42 @@ static void analyzeOneTable( - int addrIsNull; - u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; - -- pParse->nMem = MAX(pParse->nMem, regCol+nCol); -+ /* No STAT4 data is generated if the number of rows is zero */ -+ if( addrGotoEnd==0 ){ -+ sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER); -+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); -+ VdbeCoverage(v); -+ } -+ -+ if( doOnce ){ -+ int mxCol = nCol; -+ Index *pX; -+ -+ /* Compute the maximum number of columns in any index */ -+ for(pX=pTab->pIndex; pX; pX=pX->pNext){ -+ int nColX; /* Number of columns in pX */ -+ if( !HasRowid(pTab) && IsPrimaryKeyIndex(pX) ){ -+ nColX = pX->nKeyCol; -+ }else{ -+ nColX = pX->nColumn; -+ } -+ if( nColX>mxCol ) mxCol = nColX; -+ } -+ -+ /* Allocate space to compute results for the largest index */ -+ sqlite3TouchRegister(pParse, regCol+mxCol); -+ doOnce = 0; -+#ifdef SQLITE_DEBUG -+ /* Verify that the call to sqlite3ClearTempRegCache() below -+ ** really is needed. -+ ** https://sqlite.org/forum/forumpost/83cb4a95a0 (2023-03-25) -+ */ -+ testcase( !sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); -+#endif -+ sqlite3ClearTempRegCache(pParse); /* tag-20230325-1 */ -+ assert( sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); -+ } -+ assert( sqlite3NoTempsInRange(pParse, regEq, regCol+nCol) ); - - addrNext = sqlite3VdbeCurrentAddr(v); - callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid); -@@ -108861,7 +121452,7 @@ static void analyzeOneTable( - #endif /* SQLITE_ENABLE_STAT4 */ - - /* End of analysis */ -- sqlite3VdbeJumpHere(v, addrRewind); -+ if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd); - } - - -@@ -108918,6 +121509,11 @@ static void analyzeDatabase(Parse *pParse, int iDb){ - for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ - Table *pTab = (Table*)sqliteHashData(k); - analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab); -+#ifdef SQLITE_ENABLE_STAT4 -+ iMem = sqlite3FirstAvailableRegister(pParse, iMem); -+#else -+ assert( iMem==sqlite3FirstAvailableRegister(pParse,iMem) ); -+#endif - } - loadAnalysis(pParse, iDb); - } -@@ -109158,6 +121754,8 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ - ** and its contents. - */ - SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ -+ assert( db!=0 ); -+ assert( pIdx!=0 ); - #ifdef SQLITE_ENABLE_STAT4 - if( pIdx->aSample ){ - int j; -@@ -109167,7 +121765,7 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ - } - sqlite3DbFree(db, pIdx->aSample); - } -- if( db && db->pnBytesFreed==0 ){ -+ if( db->pnBytesFreed==0 ){ - pIdx->nSample = 0; - pIdx->aSample = 0; - } -@@ -109290,12 +121888,13 @@ static int loadStatTbl( - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - int nIdxCol = 1; /* Number of columns in stat4 records */ - -- char *zIndex; /* Index name */ -- Index *pIdx; /* Pointer to the index object */ -- int nSample; /* Number of samples */ -- int nByte; /* Bytes of space required */ -- int i; /* Bytes of space required */ -- tRowcnt *pSpace; -+ char *zIndex; /* Index name */ -+ Index *pIdx; /* Pointer to the index object */ -+ int nSample; /* Number of samples */ -+ i64 nByte; /* Bytes of space required */ -+ i64 i; /* Bytes of space required */ -+ tRowcnt *pSpace; /* Available allocated memory space */ -+ u8 *pPtr; /* Available memory as a u8 for easier manipulation */ - - zIndex = (char *)sqlite3_column_text(pStmt, 0); - if( zIndex==0 ) continue; -@@ -109303,6 +121902,10 @@ static int loadStatTbl( - pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); - assert( pIdx==0 || pIdx->nSample==0 ); - if( pIdx==0 ) continue; -+ if( pIdx->aSample!=0 ){ -+ /* The same index appears in sqlite_stat4 under multiple names */ -+ continue; -+ } - assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 ); - if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ - nIdxCol = pIdx->nKeyCol; -@@ -109310,7 +121913,8 @@ static int loadStatTbl( - nIdxCol = pIdx->nColumn; - } - pIdx->nSampleCol = nIdxCol; -- nByte = sizeof(IndexSample) * nSample; -+ pIdx->mxSample = nSample; -+ nByte = ROUND8(sizeof(IndexSample) * nSample); - nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; - nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ - -@@ -109319,8 +121923,12 @@ static int loadStatTbl( - sqlite3_finalize(pStmt); - return SQLITE_NOMEM_BKPT; - } -- pSpace = (tRowcnt*)&pIdx->aSample[nSample]; -+ pPtr = (u8*)pIdx->aSample; -+ pPtr += ROUND8(nSample*sizeof(pIdx->aSample[0])); -+ pSpace = (tRowcnt*)pPtr; -+ assert( EIGHT_BYTE_ALIGNMENT( pSpace ) ); - pIdx->aAvgEq = pSpace; pSpace += nIdxCol; -+ pIdx->pTable->tabFlags |= TF_HasStat4; - for(i=0; iaSample[i].anEq = pSpace; pSpace += nIdxCol; - pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol; -@@ -109348,6 +121956,11 @@ static int loadStatTbl( - if( zIndex==0 ) continue; - pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); - if( pIdx==0 ) continue; -+ if( pIdx->nSample>=pIdx->mxSample ){ -+ /* Too many slots used because the same index appears in -+ ** sqlite_stat4 using multiple names */ -+ continue; -+ } - /* This next condition is true if data has already been loaded from - ** the sqlite_stat4 table. */ - nCol = pIdx->nSampleCol; -@@ -109360,14 +121973,15 @@ static int loadStatTbl( - decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0); - decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0); - -- /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer. -+ /* Take a copy of the sample. Add 8 extra 0x00 bytes the end of the buffer. - ** This is in case the sample record is corrupted. In that case, the - ** sqlite3VdbeRecordCompare() may read up to two varints past the - ** end of the allocated buffer before it realizes it is dealing with -- ** a corrupt record. Adding the two 0x00 bytes prevents this from causing -+ ** a corrupt record. Or it might try to read a large integer from the -+ ** buffer. In any case, eight 0x00 bytes prevents this from causing - ** a buffer overread. */ - pSample->n = sqlite3_column_bytes(pStmt, 4); -- pSample->p = sqlite3DbMallocZero(db, pSample->n + 2); -+ pSample->p = sqlite3DbMallocZero(db, pSample->n + 8); - if( pSample->p==0 ){ - sqlite3_finalize(pStmt); - return SQLITE_NOMEM_BKPT; -@@ -109388,11 +122002,15 @@ static int loadStatTbl( - */ - static int loadStat4(sqlite3 *db, const char *zDb){ - int rc = SQLITE_OK; /* Result codes from subroutines */ -+ const Table *pStat4; - - assert( db->lookaside.bDisable ); -- if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){ -+ if( OptimizationEnabled(db, SQLITE_Stat4) -+ && (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 -+ && IsOrdinaryTable(pStat4) -+ ){ - rc = loadStatTbl(db, -- "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", -+ "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase", - "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", - zDb - ); -@@ -109427,6 +122045,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ - char *zSql; - int rc = SQLITE_OK; - Schema *pSchema = db->aDb[iDb].pSchema; -+ const Table *pStat1; - - assert( iDb>=0 && iDbnDb ); - assert( db->aDb[iDb].pBt!=0 ); -@@ -109449,7 +122068,9 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ - /* Load new statistics out of the sqlite_stat1 table */ - sInfo.db = db; - sInfo.zDatabase = db->aDb[iDb].zDbSName; -- if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){ -+ if( (pStat1 = sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)) -+ && IsOrdinaryTable(pStat1) -+ ){ - zSql = sqlite3MPrintf(db, - "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); - if( zSql==0 ){ -@@ -109579,7 +122200,7 @@ static void attachFunc( - char *zErr = 0; - unsigned int flags; - Db *aNew; /* New array of Db pointers */ -- Db *pNew; /* Db object for the newly attached database */ -+ Db *pNew = 0; /* Db object for the newly attached database */ - char *zErrDyn = 0; - sqlite3_vfs *pVfs; - -@@ -109589,7 +122210,7 @@ static void attachFunc( - if( zFile==0 ) zFile = ""; - if( zName==0 ) zName = ""; - --#ifdef SQLITE_ENABLE_DESERIALIZE -+#ifndef SQLITE_OMIT_DESERIALIZE - # define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb) - #else - # define REOPEN_AS_MEMDB(db) (0) -@@ -109599,13 +122220,26 @@ static void attachFunc( - /* This is not a real ATTACH. Instead, this routine is being called - ** from sqlite3_deserialize() to close database db->init.iDb and - ** reopen it as a MemDB */ -+ Btree *pNewBt = 0; - pVfs = sqlite3_vfs_find("memdb"); - if( pVfs==0 ) return; -- pNew = &db->aDb[db->init.iDb]; -- if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt); -- pNew->pBt = 0; -- pNew->pSchema = 0; -- rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); -+ rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB); -+ if( rc==SQLITE_OK ){ -+ Schema *pNewSchema = sqlite3SchemaGet(db, pNewBt); -+ if( pNewSchema ){ -+ /* Both the Btree and the new Schema were allocated successfully. -+ ** Close the old db and update the aDb[] slot with the new memdb -+ ** values. */ -+ pNew = &db->aDb[db->init.iDb]; -+ if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt); -+ pNew->pBt = pNewBt; -+ pNew->pSchema = pNewSchema; -+ }else{ -+ sqlite3BtreeClose(pNewBt); -+ rc = SQLITE_NOMEM; -+ } -+ } -+ if( rc ) goto attach_error; - }else{ - /* This is a real ATTACH - ** -@@ -109637,7 +122271,7 @@ static void attachFunc( - if( aNew==0 ) return; - memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); - }else{ -- aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); -+ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(1+(i64)db->nDb)); - if( aNew==0 ) return; - } - db->aDb = aNew; -@@ -109656,6 +122290,12 @@ static void attachFunc( - sqlite3_free(zErr); - return; - } -+ if( (db->flags & SQLITE_AttachWrite)==0 ){ -+ flags &= ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE); -+ flags |= SQLITE_OPEN_READONLY; -+ }else if( (db->flags & SQLITE_AttachCreate)==0 ){ -+ flags &= ~SQLITE_OPEN_CREATE; -+ } - assert( pVfs ); - flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); -@@ -109702,23 +122342,21 @@ static void attachFunc( - sqlite3BtreeEnterAll(db); - db->init.iDb = 0; - db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ if( db->setlkFlags & SQLITE_SETLK_BLOCK_ON_CONNECT ){ -+ int val = 1; -+ sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pNew->pBt)); -+ sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, &val); -+ } -+#endif - if( !REOPEN_AS_MEMDB(db) ){ - rc = sqlite3Init(db, &zErrDyn); - } - sqlite3BtreeLeaveAll(db); - assert( zErrDyn==0 || rc!=SQLITE_OK ); - } --#ifdef SQLITE_USER_AUTHENTICATION -- if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){ -- u8 newAuth = 0; -- rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); -- if( newAuthauth.authLevel ){ -- rc = SQLITE_AUTH_USER; -- } -- } --#endif - if( rc ){ -- if( !REOPEN_AS_MEMDB(db) ){ -+ if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){ - int iDb = db->nDb - 1; - assert( iDb>=2 ); - if( db->aDb[iDb].pBt ){ -@@ -109787,7 +122425,9 @@ static void detachFunc( - sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName); - goto detach_error; - } -- if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){ -+ if( sqlite3BtreeTxnState(pDb->pBt)!=SQLITE_TXN_NONE -+ || sqlite3BtreeIsInBackup(pDb->pBt) -+ ){ - sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName); - goto detach_error; - } -@@ -109833,22 +122473,25 @@ static void codeAttach( - sqlite3* db = pParse->db; - int regArgs; - -+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto attach_end; -+ - if( pParse->nErr ) goto attach_end; - memset(&sName, 0, sizeof(NameContext)); - sName.pParse = pParse; - - if( -- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) || -- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || -- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) -+ SQLITE_OK!=resolveAttachExpr(&sName, pFilename) || -+ SQLITE_OK!=resolveAttachExpr(&sName, pDbname) || -+ SQLITE_OK!=resolveAttachExpr(&sName, pKey) - ){ - goto attach_end; - } - - #ifndef SQLITE_OMIT_AUTHORIZATION -- if( pAuthArg ){ -+ if( ALWAYS(pAuthArg) ){ - char *zAuthArg; - if( pAuthArg->op==TK_STRING ){ -+ assert( !ExprHasProperty(pAuthArg, EP_IntValue) ); - zAuthArg = pAuthArg->u.zToken; - }else{ - zAuthArg = 0; -@@ -109925,6 +122568,70 @@ SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *p - } - #endif /* SQLITE_OMIT_ATTACH */ - -+/* -+** Expression callback used by sqlite3FixAAAA() routines. -+*/ -+static int fixExprCb(Walker *p, Expr *pExpr){ -+ DbFixer *pFix = p->u.pFix; -+ if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL); -+ if( pExpr->op==TK_VARIABLE ){ -+ if( pFix->pParse->db->init.busy ){ -+ pExpr->op = TK_NULL; -+ }else{ -+ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType); -+ return WRC_Abort; -+ } -+ } -+ return WRC_Continue; -+} -+ -+/* -+** Select callback used by sqlite3FixAAAA() routines. -+*/ -+static int fixSelectCb(Walker *p, Select *pSelect){ -+ DbFixer *pFix = p->u.pFix; -+ int i; -+ SrcItem *pItem; -+ sqlite3 *db = pFix->pParse->db; -+ int iDb = sqlite3FindDbName(db, pFix->zDb); -+ SrcList *pList = pSelect->pSrc; -+ -+ if( NEVER(pList==0) ) return WRC_Continue; -+ for(i=0, pItem=pList->a; inSrc; i++, pItem++){ -+ if( pFix->bTemp==0 && pItem->fg.isSubquery==0 ){ -+ if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ -+ if( iDb!=sqlite3FindDbName(db, pItem->u4.zDatabase) ){ -+ sqlite3ErrorMsg(pFix->pParse, -+ "%s %T cannot reference objects in database %s", -+ pFix->zType, pFix->pName, pItem->u4.zDatabase); -+ return WRC_Abort; -+ } -+ sqlite3DbFree(db, pItem->u4.zDatabase); -+ pItem->fg.notCte = 1; -+ pItem->fg.hadSchema = 1; -+ } -+ pItem->u4.pSchema = pFix->pSchema; -+ pItem->fg.fromDDL = 1; -+ pItem->fg.fixedSchema = 1; -+ } -+#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) -+ if( pList->a[i].fg.isUsing==0 -+ && sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn) -+ ){ -+ return WRC_Abort; -+ } -+#endif -+ } -+ if( pSelect->pWith ){ -+ for(i=0; ipWith->nCte; i++){ -+ if( sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){ -+ return WRC_Abort; -+ } -+ } -+ } -+ return WRC_Continue; -+} -+ - /* - ** Initialize a DbFixer structure. This routine must be called prior - ** to passing the structure to one of the sqliteFixAAAA() routines below. -@@ -109936,9 +122643,7 @@ SQLITE_PRIVATE void sqlite3FixInit( - const char *zType, /* "view", "trigger", or "index" */ - const Token *pName /* Name of the view, trigger, or index */ - ){ -- sqlite3 *db; -- -- db = pParse->db; -+ sqlite3 *db = pParse->db; - assert( db->nDb>iDb ); - pFix->pParse = pParse; - pFix->zDb = db->aDb[iDb].zDbSName; -@@ -109946,6 +122651,13 @@ SQLITE_PRIVATE void sqlite3FixInit( - pFix->zType = zType; - pFix->pName = pName; - pFix->bTemp = (iDb==1); -+ pFix->w.pParse = pParse; -+ pFix->w.xExprCallback = fixExprCb; -+ pFix->w.xSelectCallback = fixSelectCb; -+ pFix->w.xSelectCallback2 = sqlite3WalkWinDefnDummyCallback; -+ pFix->w.walkerDepth = 0; -+ pFix->w.eCode = 0; -+ pFix->w.u.pFix = pFix; - } - - /* -@@ -109966,115 +122678,27 @@ SQLITE_PRIVATE int sqlite3FixSrcList( - DbFixer *pFix, /* Context of the fixation */ - SrcList *pList /* The Source list to check and modify */ - ){ -- int i; -- struct SrcList_item *pItem; -- sqlite3 *db = pFix->pParse->db; -- int iDb = sqlite3FindDbName(db, pFix->zDb); -- -- if( NEVER(pList==0) ) return 0; -- -- for(i=0, pItem=pList->a; inSrc; i++, pItem++){ -- if( pFix->bTemp==0 ){ -- if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){ -- sqlite3ErrorMsg(pFix->pParse, -- "%s %T cannot reference objects in database %s", -- pFix->zType, pFix->pName, pItem->zDatabase); -- return 1; -- } -- sqlite3DbFree(db, pItem->zDatabase); -- pItem->zDatabase = 0; -- pItem->pSchema = pFix->pSchema; -- pItem->fg.fromDDL = 1; -- } --#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) -- if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1; -- if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1; --#endif -- if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){ -- return 1; -- } -+ int res = 0; -+ if( pList ){ -+ Select s; -+ memset(&s, 0, sizeof(s)); -+ s.pSrc = pList; -+ res = sqlite3WalkSelect(&pFix->w, &s); - } -- return 0; -+ return res; - } - #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) - SQLITE_PRIVATE int sqlite3FixSelect( - DbFixer *pFix, /* Context of the fixation */ - Select *pSelect /* The SELECT statement to be fixed to one database */ - ){ -- while( pSelect ){ -- if( sqlite3FixExprList(pFix, pSelect->pEList) ){ -- return 1; -- } -- if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){ -- return 1; -- } -- if( sqlite3FixExpr(pFix, pSelect->pWhere) ){ -- return 1; -- } -- if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){ -- return 1; -- } -- if( sqlite3FixExpr(pFix, pSelect->pHaving) ){ -- return 1; -- } -- if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){ -- return 1; -- } -- if( sqlite3FixExpr(pFix, pSelect->pLimit) ){ -- return 1; -- } -- if( pSelect->pWith ){ -- int i; -- for(i=0; ipWith->nCte; i++){ -- if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){ -- return 1; -- } -- } -- } -- pSelect = pSelect->pPrior; -- } -- return 0; -+ return sqlite3WalkSelect(&pFix->w, pSelect); - } - SQLITE_PRIVATE int sqlite3FixExpr( - DbFixer *pFix, /* Context of the fixation */ - Expr *pExpr /* The expression to be fixed to one database */ - ){ -- while( pExpr ){ -- if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL); -- if( pExpr->op==TK_VARIABLE ){ -- if( pFix->pParse->db->init.busy ){ -- pExpr->op = TK_NULL; -- }else{ -- sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType); -- return 1; -- } -- } -- if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break; -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -- if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1; -- }else{ -- if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1; -- } -- if( sqlite3FixExpr(pFix, pExpr->pRight) ){ -- return 1; -- } -- pExpr = pExpr->pLeft; -- } -- return 0; --} --SQLITE_PRIVATE int sqlite3FixExprList( -- DbFixer *pFix, /* Context of the fixation */ -- ExprList *pList /* The expression to be fixed to one database */ --){ -- int i; -- struct ExprList_item *pItem; -- if( pList==0 ) return 0; -- for(i=0, pItem=pList->a; inExpr; i++, pItem++){ -- if( sqlite3FixExpr(pFix, pItem->pExpr) ){ -- return 1; -- } -- } -- return 0; -+ return sqlite3WalkExpr(&pFix->w, pExpr); - } - #endif - -@@ -110084,32 +122708,30 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep( - TriggerStep *pStep /* The trigger step be fixed to one database */ - ){ - while( pStep ){ -- if( sqlite3FixSelect(pFix, pStep->pSelect) ){ -- return 1; -- } -- if( sqlite3FixExpr(pFix, pStep->pWhere) ){ -- return 1; -- } -- if( sqlite3FixExprList(pFix, pStep->pExprList) ){ -- return 1; -- } -- if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){ -+ if( sqlite3WalkSelect(&pFix->w, pStep->pSelect) -+ || sqlite3WalkExpr(&pFix->w, pStep->pWhere) -+ || sqlite3WalkExprList(&pFix->w, pStep->pExprList) -+ || sqlite3FixSrcList(pFix, pStep->pFrom) -+ ){ - return 1; - } - #ifndef SQLITE_OMIT_UPSERT -- if( pStep->pUpsert ){ -- Upsert *pUp = pStep->pUpsert; -- if( sqlite3FixExprList(pFix, pUp->pUpsertTarget) -- || sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere) -- || sqlite3FixExprList(pFix, pUp->pUpsertSet) -- || sqlite3FixExpr(pFix, pUp->pUpsertWhere) -- ){ -- return 1; -+ { -+ Upsert *pUp; -+ for(pUp=pStep->pUpsert; pUp; pUp=pUp->pNextUpsert){ -+ if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget) -+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere) -+ || sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet) -+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere) -+ ){ -+ return 1; -+ } - } - } - #endif - pStep = pStep->pNext; - } -+ - return 0; - } - #endif -@@ -110230,11 +122852,7 @@ SQLITE_PRIVATE int sqlite3AuthReadCol( - int rc; /* Auth callback return code */ - - if( db->init.busy ) return SQLITE_OK; -- rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext --#ifdef SQLITE_USER_AUTHENTICATION -- ,db->auth.zAuthUser --#endif -- ); -+ rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext); - if( rc==SQLITE_DENY ){ - char *z = sqlite3_mprintf("%s.%s", zTab, zCol); - if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z); -@@ -110261,7 +122879,6 @@ SQLITE_PRIVATE void sqlite3AuthRead( - Schema *pSchema, /* The schema of the expression */ - SrcList *pTabList /* All table that pExpr might refer to */ - ){ -- sqlite3 *db = pParse->db; - Table *pTab = 0; /* The table being read */ - const char *zCol; /* Name of the column of the table */ - int iSrc; /* Index in pTabList->a[] of table being read */ -@@ -110269,8 +122886,8 @@ SQLITE_PRIVATE void sqlite3AuthRead( - int iCol; /* Index of column in table */ - - assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); -- assert( !IN_RENAME_OBJECT || db->xAuth==0 ); -- if( db->xAuth==0 ) return; -+ assert( !IN_RENAME_OBJECT ); -+ assert( pParse->db->xAuth!=0 ); - iDb = sqlite3SchemaToIndex(pParse->db, pSchema); - if( iDb<0 ){ - /* An attempt to read a column out of a subquery or other -@@ -110282,26 +122899,26 @@ SQLITE_PRIVATE void sqlite3AuthRead( - pTab = pParse->pTriggerTab; - }else{ - assert( pTabList ); -- for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ -+ for(iSrc=0; iSrcnSrc; iSrc++){ - if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ -- pTab = pTabList->a[iSrc].pTab; -+ pTab = pTabList->a[iSrc].pSTab; - break; - } - } - } - iCol = pExpr->iColumn; -- if( NEVER(pTab==0) ) return; -+ if( pTab==0 ) return; - - if( iCol>=0 ){ - assert( iColnCol ); -- zCol = pTab->aCol[iCol].zName; -+ zCol = pTab->aCol[iCol].zCnName; - }else if( pTab->iPKey>=0 ){ - assert( pTab->iPKeynCol ); -- zCol = pTab->aCol[pTab->iPKey].zName; -+ zCol = pTab->aCol[pTab->iPKey].zCnName; - }else{ - zCol = "ROWID"; - } -- assert( iDb>=0 && iDbnDb ); -+ assert( iDb>=0 && iDbdb->nDb ); - if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ - pExpr->op = TK_NULL; - } -@@ -110323,15 +122940,11 @@ SQLITE_PRIVATE int sqlite3AuthCheck( - sqlite3 *db = pParse->db; - int rc; - -- /* Don't do any authorization checks if the database is initialising -+ /* Don't do any authorization checks if the database is initializing - ** or if the parser is being invoked from within sqlite3_declare_vtab. - */ - assert( !IN_RENAME_OBJECT || db->xAuth==0 ); -- if( db->init.busy || IN_SPECIAL_PARSE ){ -- return SQLITE_OK; -- } -- -- if( db->xAuth==0 ){ -+ if( db->xAuth==0 || db->init.busy || IN_SPECIAL_PARSE ){ - return SQLITE_OK; - } - -@@ -110346,11 +122959,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck( - testcase( zArg3==0 ); - testcase( pParse->zAuthContext==0 ); - -- rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext --#ifdef SQLITE_USER_AUTHENTICATION -- ,db->auth.zAuthUser --#endif -- ); -+ rc = db->xAuth(db->pAuthArg,code,zArg1,zArg2,zArg3,pParse->zAuthContext); - if( rc==SQLITE_DENY ){ - sqlite3ErrorMsg(pParse, "not authorized"); - pParse->rc = SQLITE_AUTH; -@@ -110440,21 +123049,20 @@ struct TableLock { - ** code to make the lock occur is generated by a later call to - ** codeTableLocks() which occurs during sqlite3FinishCoding(). - */ --SQLITE_PRIVATE void sqlite3TableLock( -+static SQLITE_NOINLINE void lockTable( - Parse *pParse, /* Parsing context */ - int iDb, /* Index of the database containing the table to lock */ - Pgno iTab, /* Root page number of the table to be locked */ - u8 isWriteLock, /* True for a write lock */ - const char *zName /* Name of the table to be locked */ - ){ -- Parse *pToplevel = sqlite3ParseToplevel(pParse); -+ Parse *pToplevel; - int i; - int nBytes; - TableLock *p; - assert( iDb>=0 ); - -- if( iDb==1 ) return; -- if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; -+ pToplevel = sqlite3ParseToplevel(pParse); - for(i=0; inTableLock; i++){ - p = &pToplevel->aTableLock[i]; - if( p->iDb==iDb && p->iTab==iTab ){ -@@ -110463,6 +123071,7 @@ SQLITE_PRIVATE void sqlite3TableLock( - } - } - -+ assert( pToplevel->nTableLock < 0x7fff0000 ); - nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); - pToplevel->aTableLock = - sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); -@@ -110477,6 +123086,17 @@ SQLITE_PRIVATE void sqlite3TableLock( - sqlite3OomFault(pToplevel->db); - } - } -+SQLITE_PRIVATE void sqlite3TableLock( -+ Parse *pParse, /* Parsing context */ -+ int iDb, /* Index of the database containing the table to lock */ -+ Pgno iTab, /* Root page number of the table to be locked */ -+ u8 isWriteLock, /* True for a write lock */ -+ const char *zName /* Name of the table to be locked */ -+){ -+ if( iDb==1 ) return; -+ if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return; -+ lockTable(pParse, iDb, iTab, isWriteLock, zName); -+} - - /* - ** Code an OP_TableLock instruction for each table locked by the -@@ -110484,10 +123104,8 @@ SQLITE_PRIVATE void sqlite3TableLock( - */ - static void codeTableLocks(Parse *pParse){ - int i; -- Vdbe *pVdbe; -- -- pVdbe = sqlite3GetVdbe(pParse); -- assert( pVdbe!=0 ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */ -+ Vdbe *pVdbe = pParse->pVdbe; -+ assert( pVdbe!=0 ); - - for(i=0; inTableLock; i++){ - TableLock *p = &pParse->aTableLock[i]; -@@ -110526,34 +123144,56 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask m){ - SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ - sqlite3 *db; - Vdbe *v; -+ int iDb, i; - - assert( pParse->pToplevel==0 ); - db = pParse->db; -+ assert( db->pParse==pParse ); - if( pParse->nested ) return; -- if( db->mallocFailed || pParse->nErr ){ -- if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR; -+ if( pParse->nErr ){ -+ if( db->mallocFailed ) pParse->rc = SQLITE_NOMEM; - return; - } -+ assert( db->mallocFailed==0 ); - - /* Begin by generating some termination code at the end of the - ** vdbe program - */ -- v = sqlite3GetVdbe(pParse); -+ v = pParse->pVdbe; -+ if( v==0 ){ -+ if( db->init.busy ){ -+ pParse->rc = SQLITE_DONE; -+ return; -+ } -+ v = sqlite3GetVdbe(pParse); -+ if( v==0 ) pParse->rc = SQLITE_ERROR; -+ } - assert( !pParse->isMultiWrite - || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); - if( v ){ -- sqlite3VdbeAddOp0(v, OP_Halt); -- --#if SQLITE_USER_AUTHENTICATION -- if( pParse->nTableLock>0 && db->init.busy==0 ){ -- sqlite3UserAuthInit(db); -- if( db->auth.authLevelrc = SQLITE_AUTH_USER; -- return; -+ if( pParse->bReturning ){ -+ Returning *pReturning; -+ int addrRewind; -+ int reg; -+ -+ assert( !pParse->isCreate ); -+ pReturning = pParse->u1.d.pReturning; -+ if( pReturning->nRetCol ){ -+ sqlite3VdbeAddOp0(v, OP_FkCheck); -+ addrRewind = -+ sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur); -+ VdbeCoverage(v); -+ reg = pReturning->iRetReg; -+ for(i=0; inRetCol; i++){ -+ sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i); -+ } -+ sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i); -+ sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1); -+ VdbeCoverage(v); -+ sqlite3VdbeJumpHere(v, addrRewind); - } - } --#endif -+ sqlite3VdbeAddOp0(v, OP_Halt); - - /* The cookie mask contains one bit for each database file open. - ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are -@@ -110561,73 +123201,75 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ - ** transaction on each used database and to verify the schema cookie - ** on each used database. - */ -- if( db->mallocFailed==0 -- && (DbMaskNonZero(pParse->cookieMask) || pParse->pConstExpr) -- ){ -- int iDb, i; -- assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); -- sqlite3VdbeJumpHere(v, 0); -- for(iDb=0; iDbnDb; iDb++){ -- Schema *pSchema; -- if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue; -- sqlite3VdbeUsesBtree(v, iDb); -- pSchema = db->aDb[iDb].pSchema; -- sqlite3VdbeAddOp4Int(v, -- OP_Transaction, /* Opcode */ -- iDb, /* P1 */ -- DbMaskTest(pParse->writeMask,iDb), /* P2 */ -- pSchema->schema_cookie, /* P3 */ -- pSchema->iGeneration /* P4 */ -- ); -- if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1); -- VdbeComment((v, -- "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); -- } -+ assert( pParse->nErr>0 || sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); -+ sqlite3VdbeJumpHere(v, 0); -+ assert( db->nDb>0 ); -+ iDb = 0; -+ do{ -+ Schema *pSchema; -+ if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue; -+ sqlite3VdbeUsesBtree(v, iDb); -+ pSchema = db->aDb[iDb].pSchema; -+ sqlite3VdbeAddOp4Int(v, -+ OP_Transaction, /* Opcode */ -+ iDb, /* P1 */ -+ DbMaskTest(pParse->writeMask,iDb), /* P2 */ -+ pSchema->schema_cookie, /* P3 */ -+ pSchema->iGeneration /* P4 */ -+ ); -+ if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1); -+ VdbeComment((v, -+ "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); -+ }while( ++iDbnDb ); - #ifndef SQLITE_OMIT_VIRTUALTABLE -- for(i=0; inVtabLock; i++){ -- char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); -- sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); -- } -- pParse->nVtabLock = 0; -+ for(i=0; inVtabLock; i++){ -+ char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); -+ sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); -+ } -+ pParse->nVtabLock = 0; - #endif - -- /* Once all the cookies have been verified and transactions opened, -- ** obtain the required table-locks. This is a no-op unless the -- ** shared-cache feature is enabled. -- */ -- codeTableLocks(pParse); -+#ifndef SQLITE_OMIT_SHARED_CACHE -+ /* Once all the cookies have been verified and transactions opened, -+ ** obtain the required table-locks. This is a no-op unless the -+ ** shared-cache feature is enabled. -+ */ -+ if( pParse->nTableLock ) codeTableLocks(pParse); -+#endif - -- /* Initialize any AUTOINCREMENT data structures required. -- */ -- sqlite3AutoincrementBegin(pParse); -+ /* Initialize any AUTOINCREMENT data structures required. -+ */ -+ if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse); - -- /* Code constant expressions that where factored out of inner loops. -- ** -- ** The pConstExpr list might also contain expressions that we simply -- ** want to keep around until the Parse object is deleted. Such -- ** expressions have iConstExprReg==0. Do not generate code for -- ** those expressions, of course. -- */ -- if( pParse->pConstExpr ){ -- ExprList *pEL = pParse->pConstExpr; -- pParse->okConstFactor = 0; -- for(i=0; inExpr; i++){ -- int iReg = pEL->a[i].u.iConstExprReg; -- if( iReg>0 ){ -- sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg); -- } -- } -+ /* Code constant expressions that were factored out of inner loops. -+ */ -+ if( pParse->pConstExpr ){ -+ ExprList *pEL = pParse->pConstExpr; -+ pParse->okConstFactor = 0; -+ for(i=0; inExpr; i++){ -+ assert( pEL->a[i].u.iConstExprReg>0 ); -+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg); - } -+ } - -- /* Finally, jump back to the beginning of the executable code. */ -- sqlite3VdbeGoto(v, 1); -+ if( pParse->bReturning ){ -+ Returning *pRet; -+ assert( !pParse->isCreate ); -+ pRet = pParse->u1.d.pReturning; -+ if( pRet->nRetCol ){ -+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol); -+ } - } -- } - -+ /* Finally, jump back to the beginning of the executable code. */ -+ sqlite3VdbeGoto(v, 1); -+ } - - /* Get the VDBE program ready for execution - */ -- if( v && pParse->nErr==0 && !db->mallocFailed ){ -+ assert( v!=0 || pParse->nErr ); -+ assert( db->mallocFailed==0 || pParse->nErr ); -+ if( pParse->nErr==0 ){ - /* A minimum of one cursor is required if autoincrement is used - * See ticket [a696379c1f08866] */ - assert( pParse->pAinc==0 || pParse->nTab>0 ); -@@ -110641,23 +123283,25 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ - /* - ** Run the parser and code generator recursively in order to generate - ** code for the SQL statement given onto the end of the pParse context --** currently under construction. When the parser is run recursively --** this way, the final OP_Halt is not appended and other initialization --** and finalization steps are omitted because those are handling by the --** outermost parser. -+** currently under construction. Notes: - ** --** Not everything is nestable. This facility is designed to permit --** INSERT, UPDATE, and DELETE operations against the schema table. Use --** care if you decide to try to use this routine for some other purposes. -+** * The final OP_Halt is not appended and other initialization -+** and finalization steps are omitted because those are handling by the -+** outermost parser. -+** -+** * Built-in SQL functions always take precedence over application-defined -+** SQL functions. In other words, it is not possible to override a -+** built-in function. - */ - SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ - va_list ap; - char *zSql; -- char *zErrMsg = 0; - sqlite3 *db = pParse->db; -+ u32 savedDbFlags = db->mDbFlags; - char saveBuf[PARSE_TAIL_SZ]; - - if( pParse->nErr ) return; -+ if( pParse->eParseMode ) return; - assert( pParse->nested<10 ); /* Nesting should only be of limited depth */ - va_start(ap, zFormat); - zSql = sqlite3VMPrintf(db, zFormat, ap); -@@ -110673,23 +123317,14 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){ - pParse->nested++; - memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ); - memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); -- sqlite3RunParser(pParse, zSql, &zErrMsg); -- sqlite3DbFree(db, zErrMsg); -+ db->mDbFlags |= DBFLAG_PreferBuiltin; -+ sqlite3RunParser(pParse, zSql); -+ db->mDbFlags = savedDbFlags; - sqlite3DbFree(db, zSql); - memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); - pParse->nested--; - } - --#if SQLITE_USER_AUTHENTICATION --/* --** Return TRUE if zTable is the name of the system table that stores the --** list of users and their access credentials. --*/ --SQLITE_PRIVATE int sqlite3UserAuthTable(const char *zTable){ -- return sqlite3_stricmp(zTable, "sqlite_user")==0; --} --#endif -- - /* - ** Locate the in-memory structure that describes a particular database - ** table given the name of that table and (optionally) the name of the -@@ -110708,13 +123343,6 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha - - /* All mutexes are required for schema access. Make sure we hold them. */ - assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); --#if SQLITE_USER_AUTHENTICATION -- /* Only the admin user is allowed to know that the sqlite_user table -- ** exists */ -- if( db->auth.authLevelnDb; i++){ - if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break; -@@ -110731,17 +123359,17 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha - p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName); - if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ - if( i==1 ){ -- if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 -- || sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 -- || sqlite3StrICmp(zName+7, &DFLT_SCHEMA_TABLE[7])==0 -+ if( sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 -+ || sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 -+ || sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 - ){ - p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, -- DFLT_TEMP_SCHEMA_TABLE); -+ LEGACY_TEMP_SCHEMA_TABLE); - } - }else{ -- if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ -+ if( sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ - p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, -- DFLT_SCHEMA_TABLE); -+ LEGACY_SCHEMA_TABLE); - } - } - } -@@ -110759,11 +123387,11 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha - if( p ) break; - } - if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ -- if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){ -- p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, DFLT_SCHEMA_TABLE); -- }else if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 ){ -+ if( sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){ -+ p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, LEGACY_SCHEMA_TABLE); -+ }else if( sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){ - p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, -- DFLT_TEMP_SCHEMA_TABLE); -+ LEGACY_TEMP_SCHEMA_TABLE); - } - } - } -@@ -110803,19 +123431,20 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( - /* If zName is the not the name of a table in the schema created using - ** CREATE, then check to see if it is the name of an virtual table that - ** can be an eponymous virtual table. */ -- if( pParse->disableVtab==0 ){ -+ if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){ - Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); - if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ - pMod = sqlite3PragmaVtabRegister(db, zName); - } - if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ -+ testcase( pMod->pEpoTab==0 ); - return pMod->pEpoTab; - } - } - #endif - if( flags & LOCATE_NOERR ) return 0; - pParse->checkSchema = 1; -- }else if( IsVirtual(p) && pParse->disableVtab ){ -+ }else if( IsVirtual(p) && (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)!=0 ){ - p = 0; - } - -@@ -110826,6 +123455,8 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( - }else{ - sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); - } -+ }else{ -+ assert( HasRowid(p) || p->iPKey<0 ); - } - - return p; -@@ -110843,19 +123474,35 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( - SQLITE_PRIVATE Table *sqlite3LocateTableItem( - Parse *pParse, - u32 flags, -- struct SrcList_item *p -+ SrcItem *p - ){ - const char *zDb; -- assert( p->pSchema==0 || p->zDatabase==0 ); -- if( p->pSchema ){ -- int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); -+ if( p->fg.fixedSchema ){ -+ int iDb = sqlite3SchemaToIndex(pParse->db, p->u4.pSchema); - zDb = pParse->db->aDb[iDb].zDbSName; - }else{ -- zDb = p->zDatabase; -+ assert( !p->fg.isSubquery ); -+ zDb = p->u4.zDatabase; - } - return sqlite3LocateTable(pParse, flags, p->zName, zDb); - } - -+/* -+** Return the preferred table name for system tables. Translate legacy -+** names into the new preferred names, as appropriate. -+*/ -+SQLITE_PRIVATE const char *sqlite3PreferredTableName(const char *zName){ -+ if( sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){ -+ if( sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 ){ -+ return PREFERRED_SCHEMA_TABLE; -+ } -+ if( sqlite3StrICmp(zName+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){ -+ return PREFERRED_TEMP_SCHEMA_TABLE; -+ } -+ } -+ return zName; -+} -+ - /* - ** Locate the in-memory structure that describes - ** a particular index given the name of that index -@@ -111020,6 +123667,84 @@ SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){ - db->mDbFlags &= ~DBFLAG_SchemaChange; - } - -+/* -+** Set the expression associated with a column. This is usually -+** the DEFAULT value, but might also be the expression that computes -+** the value for a generated column. -+*/ -+SQLITE_PRIVATE void sqlite3ColumnSetExpr( -+ Parse *pParse, /* Parsing context */ -+ Table *pTab, /* The table containing the column */ -+ Column *pCol, /* The column to receive the new DEFAULT expression */ -+ Expr *pExpr /* The new default expression */ -+){ -+ ExprList *pList; -+ assert( IsOrdinaryTable(pTab) ); -+ pList = pTab->u.tab.pDfltList; -+ if( pCol->iDflt==0 -+ || NEVER(pList==0) -+ || NEVER(pList->nExpriDflt) -+ ){ -+ pCol->iDflt = pList==0 ? 1 : pList->nExpr+1; -+ pTab->u.tab.pDfltList = sqlite3ExprListAppend(pParse, pList, pExpr); -+ }else{ -+ sqlite3ExprDelete(pParse->db, pList->a[pCol->iDflt-1].pExpr); -+ pList->a[pCol->iDflt-1].pExpr = pExpr; -+ } -+} -+ -+/* -+** Return the expression associated with a column. The expression might be -+** the DEFAULT clause or the AS clause of a generated column. -+** Return NULL if the column has no associated expression. -+*/ -+SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){ -+ if( pCol->iDflt==0 ) return 0; -+ if( !IsOrdinaryTable(pTab) ) return 0; -+ if( NEVER(pTab->u.tab.pDfltList==0) ) return 0; -+ if( NEVER(pTab->u.tab.pDfltList->nExpriDflt) ) return 0; -+ return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; -+} -+ -+/* -+** Set the collating sequence name for a column. -+*/ -+SQLITE_PRIVATE void sqlite3ColumnSetColl( -+ sqlite3 *db, -+ Column *pCol, -+ const char *zColl -+){ -+ i64 nColl; -+ i64 n; -+ char *zNew; -+ assert( zColl!=0 ); -+ n = sqlite3Strlen30(pCol->zCnName) + 1; -+ if( pCol->colFlags & COLFLAG_HASTYPE ){ -+ n += sqlite3Strlen30(pCol->zCnName+n) + 1; -+ } -+ nColl = sqlite3Strlen30(zColl) + 1; -+ zNew = sqlite3DbRealloc(db, pCol->zCnName, nColl+n); -+ if( zNew ){ -+ pCol->zCnName = zNew; -+ memcpy(pCol->zCnName + n, zColl, nColl); -+ pCol->colFlags |= COLFLAG_HASCOLL; -+ } -+} -+ -+/* -+** Return the collating sequence name for a column -+*/ -+SQLITE_PRIVATE const char *sqlite3ColumnColl(Column *pCol){ -+ const char *z; -+ if( (pCol->colFlags & COLFLAG_HASCOLL)==0 ) return 0; -+ z = pCol->zCnName; -+ while( *z ){ z++; } -+ if( pCol->colFlags & COLFLAG_HASTYPE ){ -+ do{ z++; }while( *z ); -+ } -+ return z+1; -+} -+ - /* - ** Delete memory allocated for the column names of a table or view (the - ** Table.aCol[] array). -@@ -111028,14 +123753,23 @@ SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){ - int i; - Column *pCol; - assert( pTable!=0 ); -+ assert( db!=0 ); - if( (pCol = pTable->aCol)!=0 ){ - for(i=0; inCol; i++, pCol++){ -- assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) ); -- sqlite3DbFree(db, pCol->zName); -- sqlite3ExprDelete(db, pCol->pDflt); -- sqlite3DbFree(db, pCol->zColl); -+ assert( pCol->zCnName==0 || pCol->hName==sqlite3StrIHash(pCol->zCnName) ); -+ sqlite3DbFree(db, pCol->zCnName); -+ } -+ sqlite3DbNNFreeNN(db, pTable->aCol); -+ if( IsOrdinaryTable(pTable) ){ -+ sqlite3ExprListDelete(db, pTable->u.tab.pDfltList); -+ } -+ if( db->pnBytesFreed==0 ){ -+ pTable->aCol = 0; -+ pTable->nCol = 0; -+ if( IsOrdinaryTable(pTable) ){ -+ pTable->u.tab.pDfltList = 0; -+ } - } -- sqlite3DbFree(db, pTable->aCol); - } - } - -@@ -111066,7 +123800,8 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ - ** a Table object that was going to be marked ephemeral. So do not check - ** that no lookaside memory is used in this case either. */ - int nLookaside = 0; -- if( db && !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){ -+ assert( db!=0 ); -+ if( !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){ - nLookaside = sqlite3LookasideUsed(db, 0); - } - #endif -@@ -111076,7 +123811,7 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ - pNext = pIndex->pNext; - assert( pIndex->pSchema==pTable->pSchema - || (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) ); -- if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){ -+ if( db->pnBytesFreed==0 && !IsVirtual(pTable) ){ - char *zName = pIndex->zName; - TESTONLY ( Index *pOld = ) sqlite3HashInsert( - &pIndex->pSchema->idxHash, zName, 0 -@@ -111087,19 +123822,25 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ - sqlite3FreeIndex(db, pIndex); - } - -- /* Delete any foreign keys attached to this table. */ -- sqlite3FkDelete(db, pTable); -+ if( IsOrdinaryTable(pTable) ){ -+ sqlite3FkDelete(db, pTable); -+ } -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+ else if( IsVirtual(pTable) ){ -+ sqlite3VtabClear(db, pTable); -+ } -+#endif -+ else{ -+ assert( IsView(pTable) ); -+ sqlite3SelectDelete(db, pTable->u.view.pSelect); -+ } - - /* Delete the Table structure itself. - */ - sqlite3DeleteColumnNames(db, pTable); - sqlite3DbFree(db, pTable->zName); - sqlite3DbFree(db, pTable->zColAff); -- sqlite3SelectDelete(db, pTable->pSelect); - sqlite3ExprListDelete(db, pTable->pCheck); --#ifndef SQLITE_OMIT_VIRTUALTABLE -- sqlite3VtabClear(db, pTable); --#endif - sqlite3DbFree(db, pTable); - - /* Verify that no lookaside memory was used by schema tables */ -@@ -111107,10 +123848,14 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ - } - SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ - /* Do not delete the table until the reference count reaches zero. */ -+ assert( db!=0 ); - if( !pTable ) return; -- if( ((!db || db->pnBytesFreed==0) && (--pTable->nTabRef)>0) ) return; -+ if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return; - deleteTable(db, pTable); - } -+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){ -+ sqlite3DeleteTable(db, (Table*)pTable); -+} - - - /* -@@ -111145,10 +123890,10 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char - ** are not \000 terminated and are not persistent. The returned string - ** is \000 terminated and is persistent. - */ --SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ -+SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, const Token *pName){ - char *zName; - if( pName ){ -- zName = sqlite3DbStrNDup(db, (char*)pName->z, pName->n); -+ zName = sqlite3DbStrNDup(db, (const char*)pName->z, pName->n); - sqlite3Dequote(zName); - }else{ - zName = 0; -@@ -111162,7 +123907,7 @@ SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ - */ - SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *p, int iDb){ - Vdbe *v = sqlite3GetVdbe(p); -- sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, DFLT_SCHEMA_TABLE); -+ sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, LEGACY_SCHEMA_TABLE); - sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, SCHEMA_ROOT, iDb, 5); - if( p->nTab==0 ){ - p->nTab = 1; -@@ -111242,7 +123987,7 @@ SQLITE_PRIVATE int sqlite3TwoPartName( - return -1; - } - }else{ -- assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT -+ assert( db->init.iDb==0 || db->init.busy || IN_SPECIAL_PARSE - || (db->mDbFlags & DBFLAG_Vacuum)!=0); - iDb = db->init.iDb; - *pUnqual = pName1; -@@ -111325,10 +124070,16 @@ SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table *pTab){ - ** find the (first) offset of that column in index pIdx. Or return -1 - ** if column iCol is not used in index pIdx. - */ --SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){ -+SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){ - int i; -+ i16 iCol16; -+ assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN ); -+ assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 ); -+ iCol16 = iCol; - for(i=0; inColumn; i++){ -- if( iCol==pIdx->aiColumn[i] ) return i; -+ if( iCol16==pIdx->aiColumn[i] ){ -+ return i; -+ } - } - return -1; - } -@@ -111411,6 +124162,23 @@ SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table *pTab, i16 iCol){ - } - #endif - -+/* -+** Insert a single OP_JournalMode query opcode in order to force the -+** prepared statement to return false for sqlite3_stmt_readonly(). This -+** is used by CREATE TABLE IF NOT EXISTS and similar if the table already -+** exists, so that the prepared statement for CREATE TABLE IF NOT EXISTS -+** will return false for sqlite3_stmt_readonly() even if that statement -+** is a read-only no-op. -+*/ -+static void sqlite3ForceNotReadOnly(Parse *pParse){ -+ int iReg = ++pParse->nMem; -+ Vdbe *v = sqlite3GetVdbe(pParse); -+ if( v ){ -+ sqlite3VdbeAddOp3(v, OP_JournalMode, 0, iReg, PAGER_JOURNALMODE_QUERY); -+ sqlite3VdbeUsesBtree(v, 0); -+ } -+} -+ - /* - ** Begin constructing a new table representation in memory. This is - ** the first of several action routines that get called in response -@@ -111506,10 +124274,12 @@ SQLITE_PRIVATE void sqlite3StartTable( - pTable = sqlite3FindTable(db, zName, zDb); - if( pTable ){ - if( !noErr ){ -- sqlite3ErrorMsg(pParse, "table %T already exists", pName); -+ sqlite3ErrorMsg(pParse, "%s %T already exists", -+ (IsView(pTable)? "view" : "table"), pName); - }else{ - assert( !db->init.busy || CORRUPT_DB ); - sqlite3CodeVerifySchema(pParse, iDb); -+ sqlite3ForceNotReadOnly(pParse); - } - goto begin_table_error; - } -@@ -111538,17 +124308,6 @@ SQLITE_PRIVATE void sqlite3StartTable( - assert( pParse->pNewTable==0 ); - pParse->pNewTable = pTable; - -- /* If this is the magic sqlite_sequence table used by autoincrement, -- ** then record a pointer to this table in the main database structure -- ** so that INSERT can find the table easily. -- */ --#ifndef SQLITE_OMIT_AUTOINCREMENT -- if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ -- assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); -- pTable->pSchema->pSeqTab = pTable; -- } --#endif -- - /* Begin generating the code that will insert the table record into - ** the schema table. Note in particular that we must go ahead - ** and allocate the record number for the table entry now. Before any -@@ -111574,8 +124333,9 @@ SQLITE_PRIVATE void sqlite3StartTable( - /* If the file format and encoding in the database have not been set, - ** set them now. - */ -- reg1 = pParse->regRowid = ++pParse->nMem; -- reg2 = pParse->regRoot = ++pParse->nMem; -+ assert( pParse->isCreate ); -+ reg1 = pParse->u1.cr.regRowid = ++pParse->nMem; -+ reg2 = pParse->u1.cr.regRoot = ++pParse->nMem; - reg3 = ++pParse->nMem; - sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT); - sqlite3VdbeUsesBtree(v, iDb); -@@ -111590,8 +124350,8 @@ SQLITE_PRIVATE void sqlite3StartTable( - ** The record created does not contain anything yet. It will be replaced - ** by the real entry in code generated at sqlite3EndTable(). - ** -- ** The rowid for the new entry is left in register pParse->regRowid. -- ** The root page number of the new table is left in reg pParse->regRoot. -+ ** The rowid for the new entry is left in register pParse->u1.cr.regRowid. -+ ** The root page of the new table is left in reg pParse->u1.cr.regRoot. - ** The rowid and root page number values are needed by the code that - ** sqlite3EndTable will generate. - */ -@@ -111601,7 +124361,8 @@ SQLITE_PRIVATE void sqlite3StartTable( - }else - #endif - { -- pParse->addrCrTab = -+ assert( !pParse->bReturning ); -+ pParse->u1.cr.addrCrTab = - sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY); - } - sqlite3OpenSchemaTable(pParse, iDb); -@@ -111617,6 +124378,7 @@ SQLITE_PRIVATE void sqlite3StartTable( - - /* If an error occurs, we jump here */ - begin_table_error: -+ pParse->checkSchema = 1; - sqlite3DbFree(db, zName); - return; - } -@@ -111626,14 +124388,85 @@ begin_table_error: - */ - #if SQLITE_ENABLE_HIDDEN_COLUMNS - SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ -- if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){ -+ if( sqlite3_strnicmp(pCol->zCnName, "__hidden__", 10)==0 ){ - pCol->colFlags |= COLFLAG_HIDDEN; -+ if( pTab ) pTab->tabFlags |= TF_HasHidden; - }else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){ - pTab->tabFlags |= TF_OOOHidden; - } - } - #endif - -+/* -+** Clean up the data structures associated with the RETURNING clause. -+*/ -+static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){ -+ Returning *pRet = (Returning*)pArg; -+ Hash *pHash; -+ pHash = &(db->aDb[1].pSchema->trigHash); -+ sqlite3HashInsert(pHash, pRet->zName, 0); -+ sqlite3ExprListDelete(db, pRet->pReturnEL); -+ sqlite3DbFree(db, pRet); -+} -+ -+/* -+** Add the RETURNING clause to the parse currently underway. -+** -+** This routine creates a special TEMP trigger that will fire for each row -+** of the DML statement. That TEMP trigger contains a single SELECT -+** statement with a result set that is the argument of the RETURNING clause. -+** The trigger has the Trigger.bReturning flag and an opcode of -+** TK_RETURNING instead of TK_SELECT, so that the trigger code generator -+** knows to handle it specially. The TEMP trigger is automatically -+** removed at the end of the parse. -+** -+** When this routine is called, we do not yet know if the RETURNING clause -+** is attached to a DELETE, INSERT, or UPDATE, so construct it as a -+** RETURNING trigger instead. It will then be converted into the appropriate -+** type on the first call to sqlite3TriggersExist(). -+*/ -+SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){ -+ Returning *pRet; -+ Hash *pHash; -+ sqlite3 *db = pParse->db; -+ if( pParse->pNewTrigger ){ -+ sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger"); -+ }else{ -+ assert( pParse->bReturning==0 || pParse->ifNotExists ); -+ } -+ pParse->bReturning = 1; -+ pRet = sqlite3DbMallocZero(db, sizeof(*pRet)); -+ if( pRet==0 ){ -+ sqlite3ExprListDelete(db, pList); -+ return; -+ } -+ assert( !pParse->isCreate ); -+ pParse->u1.d.pReturning = pRet; -+ pRet->pParse = pParse; -+ pRet->pReturnEL = pList; -+ sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet); -+ testcase( pParse->earlyCleanup ); -+ if( db->mallocFailed ) return; -+ sqlite3_snprintf(sizeof(pRet->zName), pRet->zName, -+ "sqlite_returning_%p", pParse); -+ pRet->retTrig.zName = pRet->zName; -+ pRet->retTrig.op = TK_RETURNING; -+ pRet->retTrig.tr_tm = TRIGGER_AFTER; -+ pRet->retTrig.bReturning = 1; -+ pRet->retTrig.pSchema = db->aDb[1].pSchema; -+ pRet->retTrig.pTabSchema = db->aDb[1].pSchema; -+ pRet->retTrig.step_list = &pRet->retTStep; -+ pRet->retTStep.op = TK_RETURNING; -+ pRet->retTStep.pTrig = &pRet->retTrig; -+ pRet->retTStep.pExprList = pList; -+ pHash = &(db->aDb[1].pSchema->trigHash); -+ assert( sqlite3HashFind(pHash, pRet->zName)==0 -+ || pParse->nErr || pParse->ifNotExists ); -+ if( sqlite3HashInsert(pHash, pRet->zName, &pRet->retTrig) -+ ==&pRet->retTrig ){ -+ sqlite3OomFault(db); -+ } -+} - - /* - ** Add a new column to the table currently being constructed. -@@ -111643,67 +124476,112 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ - ** first to get things going. Then this routine is called for each - ** column. - */ --SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ -+SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ - Table *p; - int i; - char *z; - char *zType; - Column *pCol; - sqlite3 *db = pParse->db; -+ Column *aNew; -+ u8 eType = COLTYPE_CUSTOM; -+ u8 szEst = 1; -+ char affinity = SQLITE_AFF_BLOB; -+ - if( (p = pParse->pNewTable)==0 ) return; - if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){ - sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName); - return; - } -- z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2); -+ if( !IN_RENAME_OBJECT ) sqlite3DequoteToken(&sName); -+ -+ /* Because keywords GENERATE ALWAYS can be converted into identifiers -+ ** by the parser, we can sometimes end up with a typename that ends -+ ** with "generated always". Check for this case and omit the surplus -+ ** text. */ -+ if( sType.n>=16 -+ && sqlite3_strnicmp(sType.z+(sType.n-6),"always",6)==0 -+ ){ -+ sType.n -= 6; -+ while( ALWAYS(sType.n>0) && sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; -+ if( sType.n>=9 -+ && sqlite3_strnicmp(sType.z+(sType.n-9),"generated",9)==0 -+ ){ -+ sType.n -= 9; -+ while( sType.n>0 && sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--; -+ } -+ } -+ -+ /* Check for standard typenames. For standard typenames we will -+ ** set the Column.eType field rather than storing the typename after -+ ** the column name, in order to save space. */ -+ if( sType.n>=3 ){ -+ sqlite3DequoteToken(&sType); -+ for(i=0; i0) ); - if( z==0 ) return; -- if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName); -- memcpy(z, pName->z, pName->n); -- z[pName->n] = 0; -+ if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, &sName); -+ memcpy(z, sName.z, sName.n); -+ z[sName.n] = 0; - sqlite3Dequote(z); -- for(i=0; inCol; i++){ -- if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){ -- sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); -- sqlite3DbFree(db, z); -- return; -- } -+ if( p->nCol && sqlite3ColumnIndex(p, z)>=0 ){ -+ sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); -+ sqlite3DbFree(db, z); -+ return; - } -- if( (p->nCol & 0x7)==0 ){ -- Column *aNew; -- aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0])); -- if( aNew==0 ){ -- sqlite3DbFree(db, z); -- return; -- } -- p->aCol = aNew; -+ aNew = sqlite3DbRealloc(db,p->aCol,((i64)p->nCol+1)*sizeof(p->aCol[0])); -+ if( aNew==0 ){ -+ sqlite3DbFree(db, z); -+ return; - } -+ p->aCol = aNew; - pCol = &p->aCol[p->nCol]; - memset(pCol, 0, sizeof(p->aCol[0])); -- pCol->zName = z; -+ pCol->zCnName = z; - pCol->hName = sqlite3StrIHash(z); - sqlite3ColumnPropertiesFromName(p, pCol); - -- if( pType->n==0 ){ -+ if( sType.n==0 ){ - /* If there is no type specified, columns have the default affinity - ** 'BLOB' with a default size of 4 bytes. */ -- pCol->affinity = SQLITE_AFF_BLOB; -- pCol->szEst = 1; -+ pCol->affinity = affinity; -+ pCol->eCType = eType; -+ pCol->szEst = szEst; - #ifdef SQLITE_ENABLE_SORTER_REFERENCES -- if( 4>=sqlite3GlobalConfig.szSorterRef ){ -- pCol->colFlags |= COLFLAG_SORTERREF; -+ if( affinity==SQLITE_AFF_BLOB ){ -+ if( 4>=sqlite3GlobalConfig.szSorterRef ){ -+ pCol->colFlags |= COLFLAG_SORTERREF; -+ } - } - #endif - }else{ - zType = z + sqlite3Strlen30(z) + 1; -- memcpy(zType, pType->z, pType->n); -- zType[pType->n] = 0; -+ memcpy(zType, sType.z, sType.n); -+ zType[sType.n] = 0; - sqlite3Dequote(zType); - pCol->affinity = sqlite3AffinityType(zType, pCol); - pCol->colFlags |= COLFLAG_HASTYPE; - } -+ if( p->nCol<=0xff ){ -+ u8 h = pCol->hName % sizeof(p->aHx); -+ p->aHx[h] = p->nCol; -+ } - p->nCol++; - p->nNVCol++; -- pParse->constraintName.n = 0; -+ assert( pParse->isCreate ); -+ pParse->u1.cr.constraintName.n = 0; - } - - /* -@@ -111766,7 +124644,8 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){ - - assert( zIn!=0 ); - while( zIn[0] ){ -- h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff]; -+ u8 x = *(u8*)zIn; -+ h = (h<<8) + sqlite3UpperToLower[x]; - zIn++; - if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */ - aff = SQLITE_AFF_TEXT; -@@ -111840,7 +124719,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue( - Parse *pParse, /* Parsing context */ - Expr *pExpr, /* The parsed expression of the default value */ - const char *zStart, /* Start of the default value text */ -- const char *zEnd /* First character past end of defaut value text */ -+ const char *zEnd /* First character past end of default value text */ - ){ - Table *p; - Column *pCol; -@@ -111851,7 +124730,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue( - pCol = &(p->aCol[p->nCol-1]); - if( !sqlite3ExprIsConstantOrFunction(pExpr, isInit) ){ - sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", -- pCol->zName); -+ pCol->zCnName); - #ifndef SQLITE_OMIT_GENERATED_COLUMNS - }else if( pCol->colFlags & COLFLAG_GENERATED ){ - testcase( pCol->colFlags & COLFLAG_VIRTUAL ); -@@ -111862,15 +124741,15 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue( - /* A copy of pExpr is used instead of the original, as pExpr contains - ** tokens that point to volatile memory. - */ -- Expr x; -- sqlite3ExprDelete(db, pCol->pDflt); -+ Expr x, *pDfltExpr; - memset(&x, 0, sizeof(x)); - x.op = TK_SPAN; - x.u.zToken = sqlite3DbSpanDup(db, zStart, zEnd); - x.pLeft = pExpr; - x.flags = EP_Skip; -- pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); -+ pDfltExpr = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); - sqlite3DbFree(db, x.u.zToken); -+ sqlite3ColumnSetExpr(pParse, p, pCol, pDfltExpr); - } - } - if( IN_RENAME_OBJECT ){ -@@ -111966,20 +124845,18 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( - assert( pCExpr!=0 ); - sqlite3StringToId(pCExpr); - if( pCExpr->op==TK_ID ){ -- const char *zCName = pCExpr->u.zToken; -- for(iCol=0; iColnCol; iCol++){ -- if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){ -- pCol = &pTab->aCol[iCol]; -- makeColumnPartOfPrimaryKey(pParse, pCol); -- break; -- } -+ assert( !ExprHasProperty(pCExpr, EP_IntValue) ); -+ iCol = sqlite3ColumnIndex(pTab, pCExpr->u.zToken); -+ if( iCol>=0 ){ -+ pCol = &pTab->aCol[iCol]; -+ makeColumnPartOfPrimaryKey(pParse, pCol); - } - } - } - } - if( nTerm==1 - && pCol -- && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0 -+ && pCol->eCType==COLTYPE_INTEGER - && sortOrder!=SQLITE_SO_DESC - ){ - if( IN_RENAME_OBJECT && pList ){ -@@ -111990,7 +124867,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( - pTab->keyConf = (u8)onError; - assert( autoInc==0 || autoInc==1 ); - pTab->tabFlags |= autoInc*TF_Autoincrement; -- if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags; -+ if( pList ) pParse->iPkSortOrder = pList->a[0].fg.sortFlags; - (void)sqlite3HasExplicitNulls(pParse, pList); - }else if( autoInc ){ - #ifndef SQLITE_OMIT_AUTOINCREMENT -@@ -112012,8 +124889,10 @@ primary_key_exit: - ** Add a new CHECK constraint to the table currently under construction. - */ - SQLITE_PRIVATE void sqlite3AddCheckConstraint( -- Parse *pParse, /* Parsing context */ -- Expr *pCheckExpr /* The check expression */ -+ Parse *pParse, /* Parsing context */ -+ Expr *pCheckExpr, /* The check expression */ -+ const char *zStart, /* Opening "(" */ -+ const char *zEnd /* Closing ")" */ - ){ - #ifndef SQLITE_OMIT_CHECK - Table *pTab = pParse->pNewTable; -@@ -112022,8 +124901,17 @@ SQLITE_PRIVATE void sqlite3AddCheckConstraint( - && !sqlite3BtreeIsReadonly(db->aDb[db->init.iDb].pBt) - ){ - pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr); -- if( pParse->constraintName.n ){ -- sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1); -+ assert( pParse->isCreate ); -+ if( pParse->u1.cr.constraintName.n ){ -+ sqlite3ExprListSetName(pParse, pTab->pCheck, -+ &pParse->u1.cr.constraintName, 1); -+ }else{ -+ Token t; -+ for(zStart++; sqlite3Isspace(zStart[0]); zStart++){} -+ while( sqlite3Isspace(zEnd[-1]) ){ zEnd--; } -+ t.z = zStart; -+ t.n = (int)(zEnd - t.z); -+ sqlite3ExprListSetName(pParse, pTab->pCheck, &t, 1); - } - }else - #endif -@@ -112042,7 +124930,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ - char *zColl; /* Dequoted name of collation sequence */ - sqlite3 *db; - -- if( (p = pParse->pNewTable)==0 ) return; -+ if( (p = pParse->pNewTable)==0 || IN_RENAME_OBJECT ) return; - i = p->nCol-1; - db = pParse->db; - zColl = sqlite3NameFromToken(db, pToken); -@@ -112050,8 +124938,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ - - if( sqlite3LocateCollSeq(pParse, zColl) ){ - Index *pIdx; -- sqlite3DbFree(db, p->aCol[i].zColl); -- p->aCol[i].zColl = zColl; -+ sqlite3ColumnSetColl(db, &p->aCol[i], zColl); - - /* If the column is declared as " PRIMARY KEY COLLATE ", - ** then an index may have been created on this column before the -@@ -112060,12 +124947,11 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ - for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ - assert( pIdx->nKeyCol==1 ); - if( pIdx->aiColumn[0]==i ){ -- pIdx->azColl[0] = p->aCol[i].zColl; -+ pIdx->azColl[0] = sqlite3ColumnColl(&p->aCol[i]); - } - } -- }else{ -- sqlite3DbFree(db, zColl); - } -+ sqlite3DbFree(db, zColl); - } - - /* Change the most recently parsed column to be a GENERATED ALWAYS AS -@@ -112085,7 +124971,7 @@ SQLITE_PRIVATE void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType - sqlite3ErrorMsg(pParse, "virtual tables cannot use computed columns"); - goto generated_done; - } -- if( pCol->pDflt ) goto generated_error; -+ if( pCol->iDflt>0 ) goto generated_error; - if( pType ){ - if( pType->n==7 && sqlite3StrNICmp("virtual",pType->z,7)==0 ){ - /* no-op */ -@@ -112103,13 +124989,21 @@ SQLITE_PRIVATE void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType - if( pCol->colFlags & COLFLAG_PRIMKEY ){ - makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */ - } -- pCol->pDflt = pExpr; -+ if( ALWAYS(pExpr) && pExpr->op==TK_ID ){ -+ /* The value of a generated column needs to be a real expression, not -+ ** just a reference to another column, in order for covering index -+ ** optimizations to work correctly. So if the value is not an expression, -+ ** turn it into one by adding a unary "+" operator. */ -+ pExpr = sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0); -+ } -+ if( pExpr && pExpr->op!=TK_RAISE ) pExpr->affExpr = pCol->affinity; -+ sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr); - pExpr = 0; - goto generated_done; - - generated_error: - sqlite3ErrorMsg(pParse, "error in generated column \"%s\"", -- pCol->zName); -+ pCol->zCnName); - generated_done: - sqlite3ExprDelete(pParse->db, pExpr); - #else -@@ -112171,7 +125065,7 @@ static int identLength(const char *z){ - ** to the specified offset in the buffer and updates *pIdx to refer - ** to the first byte after the last byte written before returning. - ** --** If the string zSignedIdent consists entirely of alpha-numeric -+** If the string zSignedIdent consists entirely of alphanumeric - ** characters, does not begin with a digit and is not an SQL keyword, - ** then it is copied to the output buffer exactly as it is. Otherwise, - ** it is quoted using double-quotes. -@@ -112205,13 +125099,14 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){ - ** from sqliteMalloc() and must be freed by the calling function. - */ - static char *createTableStmt(sqlite3 *db, Table *p){ -- int i, k, n; -+ int i, k, len; -+ i64 n; - char *zStmt; - char *zSep, *zSep2, *zEnd; - Column *pCol; - n = 0; - for(pCol = p->aCol, i=0; inCol; i++, pCol++){ -- n += identLength(pCol->zName) + 5; -+ n += identLength(pCol->zCnName) + 5; - } - n += identLength(p->zName); - if( n<50 ){ -@@ -112229,8 +125124,9 @@ static char *createTableStmt(sqlite3 *db, Table *p){ - sqlite3OomFault(db); - return 0; - } -- sqlite3_snprintf(n, zStmt, "CREATE TABLE "); -- k = sqlite3Strlen30(zStmt); -+ assert( n>14 && n<=0x7fffffff ); -+ memcpy(zStmt, "CREATE TABLE ", 13); -+ k = 13; - identPut(zStmt, &k, p->zName); - zStmt[k++] = '('; - for(pCol=p->aCol, i=0; inCol; i++, pCol++){ -@@ -112239,15 +125135,18 @@ static char *createTableStmt(sqlite3 *db, Table *p){ - /* SQLITE_AFF_TEXT */ " TEXT", - /* SQLITE_AFF_NUMERIC */ " NUM", - /* SQLITE_AFF_INTEGER */ " INT", -- /* SQLITE_AFF_REAL */ " REAL" -+ /* SQLITE_AFF_REAL */ " REAL", -+ /* SQLITE_AFF_FLEXNUM */ " NUM", - }; -- int len; - const char *zType; - -- sqlite3_snprintf(n-k, &zStmt[k], zSep); -- k += sqlite3Strlen30(&zStmt[k]); -+ len = sqlite3Strlen30(zSep); -+ assert( k+lenzName); -+ identPut(zStmt, &k, pCol->zCnName); -+ assert( kaffinity-SQLITE_AFF_BLOB >= 0 ); - assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) ); - testcase( pCol->affinity==SQLITE_AFF_BLOB ); -@@ -112255,16 +125154,21 @@ static char *createTableStmt(sqlite3 *db, Table *p){ - testcase( pCol->affinity==SQLITE_AFF_NUMERIC ); - testcase( pCol->affinity==SQLITE_AFF_INTEGER ); - testcase( pCol->affinity==SQLITE_AFF_REAL ); -+ testcase( pCol->affinity==SQLITE_AFF_FLEXNUM ); - - zType = azType[pCol->affinity - SQLITE_AFF_BLOB]; - len = sqlite3Strlen30(zType); - assert( pCol->affinity==SQLITE_AFF_BLOB -+ || pCol->affinity==SQLITE_AFF_FLEXNUM - || pCol->affinity==sqlite3AffinityType(zType, 0) ); -+ assert( k+lennColumn>=N ) return SQLITE_OK; -+ db = pParse->db; -+ assert( N>0 ); -+ assert( N <= SQLITE_MAX_COLUMN*2 /* tag-20250221-1 */ ); -+ testcase( N==2*pParse->db->aLimit[SQLITE_LIMIT_COLUMN] ); - assert( pIdx->isResized==0 ); -- nByte = (sizeof(char*) + sizeof(i16) + 1)*N; -+ nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*(u64)N; - zExtra = sqlite3DbMallocZero(db, nByte); - if( zExtra==0 ) return SQLITE_NOMEM_BKPT; - memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); - pIdx->azColl = (const char**)zExtra; - zExtra += sizeof(char*)*N; -+ memcpy(zExtra, pIdx->aiRowLogEst, sizeof(LogEst)*(pIdx->nKeyCol+1)); -+ pIdx->aiRowLogEst = (LogEst*)zExtra; -+ zExtra += sizeof(LogEst)*N; - memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn); - pIdx->aiColumn = (i16*)zExtra; - zExtra += sizeof(i16)*N; - memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn); - pIdx->aSortOrder = (u8*)zExtra; -- pIdx->nColumn = N; -+ pIdx->nColumn = (u16)N; /* See tag-20250221-1 above for proof of safety */ - pIdx->isResized = 1; - return SQLITE_OK; - } -@@ -112317,7 +125229,7 @@ static void estimateIndexWidth(Index *pIdx){ - for(i=0; inColumn; i++){ - i16 x = pIdx->aiColumn[i]; - assert( xpTable->nCol ); -- wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst; -+ wIndex += x<0 ? 1 : aCol[x].szEst; - } - pIdx->szIdxRow = sqlite3LogEst(wIndex*4); - } -@@ -112328,7 +125240,6 @@ static void estimateIndexWidth(Index *pIdx){ - */ - static int hasColumn(const i16 *aiCol, int nCol, int x){ - while( nCol-- > 0 ){ -- assert( aiCol[0]>=0 ); - if( x==*(aiCol++) ){ - return 1; - } -@@ -112373,7 +125284,8 @@ static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){ - /* Recompute the colNotIdxed field of the Index. - ** - ** colNotIdxed is a bitmask that has a 0 bit representing each indexed --** columns that are within the first 63 columns of the table. The -+** columns that are within the first 63 columns of the table and a 1 for -+** all other bits (all columns that are not in the index). The - ** high-order bit of colNotIdxed is always 1. All unindexed columns - ** of the table have a 1. - ** -@@ -112401,7 +125313,7 @@ static void recomputeColumnsNotIndexed(Index *pIdx){ - } - } - pIdx->colNotIdxed = ~m; -- assert( (pIdx->colNotIdxed>>63)==1 ); -+ assert( (pIdx->colNotIdxed>>63)==1 ); /* See note-20221022-a */ - } - - /* -@@ -112441,7 +125353,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ - */ - if( !db->init.imposterTable ){ - for(i=0; inCol; i++){ -- if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){ -+ if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 -+ && (pTab->aCol[i].notNull==OE_None) -+ ){ - pTab->aCol[i].notNull = OE_Abort; - } - } -@@ -112451,9 +125365,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ - /* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY - ** into BTREE_BLOBKEY. - */ -- if( pParse->addrCrTab ){ -+ assert( !pParse->bReturning ); -+ if( pParse->u1.cr.addrCrTab ){ - assert( v ); -- sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY); -+ sqlite3VdbeChangeP3(v, pParse->u1.cr.addrCrTab, BTREE_BLOBKEY); - } - - /* Locate the PRIMARY KEY index. Or, if this table was originally -@@ -112462,19 +125377,26 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ - if( pTab->iPKey>=0 ){ - ExprList *pList; - Token ipkToken; -- sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName); -+ sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zCnName); - pList = sqlite3ExprListAppend(pParse, 0, - sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0)); -- if( pList==0 ) return; -+ if( pList==0 ){ -+ pTab->tabFlags &= ~TF_WithoutRowid; -+ return; -+ } - if( IN_RENAME_OBJECT ){ - sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey); - } -- pList->a[0].sortFlags = pParse->iPkSortOrder; -+ pList->a[0].fg.sortFlags = pParse->iPkSortOrder; - assert( pParse->pNewTable==pTab ); - pTab->iPKey = -1; - sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0, - SQLITE_IDXTYPE_PRIMARYKEY); -- if( db->mallocFailed || pParse->nErr ) return; -+ if( pParse->nErr ){ -+ pTab->tabFlags &= ~TF_WithoutRowid; -+ return; -+ } -+ assert( db->mallocFailed==0 ); - pPk = sqlite3PrimaryKeyIndex(pTab); - assert( pPk->nKeyCol==1 ); - }else{ -@@ -112532,14 +125454,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ - pIdx->nColumn = pIdx->nKeyCol; - continue; - } -- if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return; -+ if( resizeIndexObject(pParse, pIdx, pIdx->nKeyCol+n) ) return; - for(i=0, j=pIdx->nKeyCol; inKeyCol, pPk, i) ){ - testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ); - pIdx->aiColumn[j] = pPk->aiColumn[i]; - pIdx->azColl[j] = pPk->azColl[i]; - if( pPk->aSortOrder[i] ){ -- /* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */ -+ /* See ticket https://sqlite.org/src/info/bba7b69f9849b5bf */ - pIdx->bAscKeyBug = 1; - } - j++; -@@ -112556,7 +125478,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ - if( !hasColumn(pPk->aiColumn, nPk, i) - && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++; - } -- if( resizeIndexObject(db, pPk, nPk+nExtra) ) return; -+ if( resizeIndexObject(pParse, pPk, nPk+nExtra) ) return; - for(i=0, j=nPk; inCol; i++){ - if( !hasColumn(pPk->aiColumn, j, i) - && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 -@@ -112586,7 +125508,7 @@ SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char * - nName = sqlite3Strlen30(pTab->zName); - if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0; - if( zName[nName]!='_' ) return 0; -- pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]); -+ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); - if( pMod==0 ) return 0; - if( pMod->pModule->iVersion<3 ) return 0; - if( pMod->pModule->xShadowName==0 ) return 0; -@@ -112594,6 +125516,41 @@ SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char * - } - #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ - -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+/* -+** Table pTab is a virtual table. If it the virtual table implementation -+** exists and has an xShadowName method, then loop over all other ordinary -+** tables within the same schema looking for shadow tables of pTab, and mark -+** any shadow tables seen using the TF_Shadow flag. -+*/ -+SQLITE_PRIVATE void sqlite3MarkAllShadowTablesOf(sqlite3 *db, Table *pTab){ -+ int nName; /* Length of pTab->zName */ -+ Module *pMod; /* Module for the virtual table */ -+ HashElem *k; /* For looping through the symbol table */ -+ -+ assert( IsVirtual(pTab) ); -+ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]); -+ if( pMod==0 ) return; -+ if( NEVER(pMod->pModule==0) ) return; -+ if( pMod->pModule->iVersion<3 ) return; -+ if( pMod->pModule->xShadowName==0 ) return; -+ assert( pTab->zName!=0 ); -+ nName = sqlite3Strlen30(pTab->zName); -+ for(k=sqliteHashFirst(&pTab->pSchema->tblHash); k; k=sqliteHashNext(k)){ -+ Table *pOther = sqliteHashData(k); -+ assert( pOther->zName!=0 ); -+ if( !IsOrdinaryTable(pOther) ) continue; -+ if( pOther->tabFlags & TF_Shadow ) continue; -+ if( sqlite3StrNICmp(pOther->zName, pTab->zName, nName)==0 -+ && pOther->zName[nName]=='_' -+ && pMod->pModule->xShadowName(pOther->zName+nName+1) -+ ){ -+ pOther->tabFlags |= TF_Shadow; -+ } -+ } -+} -+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ -+ - #ifndef SQLITE_OMIT_VIRTUALTABLE - /* - ** Return true if zName is a shadow table name in the current database -@@ -112625,6 +125582,7 @@ SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ - ** not pass them into code generator routines by mistake. - */ - static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){ -+ (void)pWalker; - ExprSetVVAProperty(pExpr, EP_Immutable); - return WRC_Continue; - } -@@ -112667,7 +125625,7 @@ SQLITE_PRIVATE void sqlite3EndTable( - Parse *pParse, /* Parse context */ - Token *pCons, /* The ',' token after the last column defn. */ - Token *pEnd, /* The ')' before options in the CREATE TABLE */ -- u8 tabOpts, /* Extra table options. Usually 0. */ -+ u32 tabOpts, /* Extra table options. Usually 0. */ - Select *pSelect /* Select from a "CREATE ... AS SELECT" */ - ){ - Table *p; /* The new table */ -@@ -112678,7 +125636,6 @@ SQLITE_PRIVATE void sqlite3EndTable( - if( pEnd==0 && pSelect==0 ){ - return; - } -- assert( !db->mallocFailed ); - p = pParse->pNewTable; - if( p==0 ) return; - -@@ -112696,7 +125653,7 @@ SQLITE_PRIVATE void sqlite3EndTable( - ** table itself. So mark it read-only. - */ - if( db->init.busy ){ -- if( pSelect ){ -+ if( pSelect || (!IsOrdinaryTable(p) && db->init.newTnum) ){ - sqlite3ErrorMsg(pParse, ""); - return; - } -@@ -112704,6 +125661,44 @@ SQLITE_PRIVATE void sqlite3EndTable( - if( p->tnum==1 ) p->tabFlags |= TF_Readonly; - } - -+ /* Special processing for tables that include the STRICT keyword: -+ ** -+ ** * Do not allow custom column datatypes. Every column must have -+ ** a datatype that is one of INT, INTEGER, REAL, TEXT, or BLOB. -+ ** -+ ** * If a PRIMARY KEY is defined, other than the INTEGER PRIMARY KEY, -+ ** then all columns of the PRIMARY KEY must have a NOT NULL -+ ** constraint. -+ */ -+ if( tabOpts & TF_Strict ){ -+ int ii; -+ p->tabFlags |= TF_Strict; -+ for(ii=0; iinCol; ii++){ -+ Column *pCol = &p->aCol[ii]; -+ if( pCol->eCType==COLTYPE_CUSTOM ){ -+ if( pCol->colFlags & COLFLAG_HASTYPE ){ -+ sqlite3ErrorMsg(pParse, -+ "unknown datatype for %s.%s: \"%s\"", -+ p->zName, pCol->zCnName, sqlite3ColumnType(pCol, "") -+ ); -+ }else{ -+ sqlite3ErrorMsg(pParse, "missing datatype for %s.%s", -+ p->zName, pCol->zCnName); -+ } -+ return; -+ }else if( pCol->eCType==COLTYPE_ANY ){ -+ pCol->affinity = SQLITE_AFF_BLOB; -+ } -+ if( (pCol->colFlags & COLFLAG_PRIMKEY)!=0 -+ && p->iPKey!=ii -+ && pCol->notNull == OE_None -+ ){ -+ pCol->notNull = OE_Abort; -+ p->tabFlags |= TF_HasNotNull; -+ } -+ } -+ } -+ - assert( (p->tabFlags & TF_HasPrimaryKey)==0 - || p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 ); - assert( (p->tabFlags & TF_HasPrimaryKey)!=0 -@@ -112748,7 +125743,7 @@ SQLITE_PRIVATE void sqlite3EndTable( - for(ii=0; iinCol; ii++){ - u32 colFlags = p->aCol[ii].colFlags; - if( (colFlags & COLFLAG_GENERATED)!=0 ){ -- Expr *pX = p->aCol[ii].pDflt; -+ Expr *pX = sqlite3ColumnExpr(p, &p->aCol[ii]); - testcase( colFlags & COLFLAG_VIRTUAL ); - testcase( colFlags & COLFLAG_STORED ); - if( sqlite3ResolveSelfReference(pParse, p, NC_GenCol, pX, 0) ){ -@@ -112758,8 +125753,8 @@ SQLITE_PRIVATE void sqlite3EndTable( - ** tree that have been allocated from lookaside memory, which is - ** illegal in a schema and will lead to errors or heap corruption - ** when the database connection closes. */ -- sqlite3ExprDelete(db, pX); -- p->aCol[ii].pDflt = sqlite3ExprAlloc(db, TK_NULL, 0, 0); -+ sqlite3ColumnSetExpr(pParse, p, &p->aCol[ii], -+ sqlite3ExprAlloc(db, TK_NULL, 0, 0)); - } - }else{ - nNG++; -@@ -112799,7 +125794,7 @@ SQLITE_PRIVATE void sqlite3EndTable( - /* - ** Initialize zType for the new view or table. - */ -- if( p->pSelect==0 ){ -+ if( IsOrdinaryTable(p) ){ - /* A regular table */ - zType = "table"; - zType2 = "TABLE"; -@@ -112813,7 +125808,7 @@ SQLITE_PRIVATE void sqlite3EndTable( - - /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT - ** statement to populate the new table. The root-page number for the -- ** new table is in register pParse->regRoot. -+ ** new table is in register pParse->u1.cr.regRoot. - ** - ** Once the SELECT has been coded by sqlite3Select(), it is in a - ** suitable state to query for the column names and types to be used -@@ -112832,15 +125827,21 @@ SQLITE_PRIVATE void sqlite3EndTable( - int regRowid; /* Rowid of the next row to insert */ - int addrInsLoop; /* Top of the loop for inserting rows */ - Table *pSelTab; /* A table that describes the SELECT results */ -+ int iCsr; /* Write cursor on the new table */ - -+ if( IN_SPECIAL_PARSE ){ -+ pParse->rc = SQLITE_ERROR; -+ pParse->nErr++; -+ return; -+ } -+ iCsr = pParse->nTab++; - regYield = ++pParse->nMem; - regRec = ++pParse->nMem; - regRowid = ++pParse->nMem; -- assert(pParse->nTab==1); - sqlite3MayAbort(pParse); -- sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); -+ assert( pParse->isCreate ); -+ sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->u1.cr.regRoot, iDb); - sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG); -- pParse->nTab = 2; - addrTop = sqlite3VdbeCurrentAddr(v) + 1; - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); - if( pParse->nErr ) return; -@@ -112861,11 +125862,11 @@ SQLITE_PRIVATE void sqlite3EndTable( - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec); - sqlite3TableAffinity(v, p, 0); -- sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid); -- sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid); -+ sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid); -+ sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid); - sqlite3VdbeGoto(v, addrInsLoop); - sqlite3VdbeJumpHere(v, addrInsLoop); -- sqlite3VdbeAddOp1(v, OP_Close, 1); -+ sqlite3VdbeAddOp1(v, OP_Close, iCsr); - } - - /* Compute the complete text of the CREATE statement */ -@@ -112884,17 +125885,18 @@ SQLITE_PRIVATE void sqlite3EndTable( - ** schema table. We just need to update that slot with all - ** the information we've collected. - */ -+ assert( pParse->isCreate ); - sqlite3NestedParse(pParse, -- "UPDATE %Q." DFLT_SCHEMA_TABLE -+ "UPDATE %Q." LEGACY_SCHEMA_TABLE - " SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q" - " WHERE rowid=#%d", - db->aDb[iDb].zDbSName, - zType, - p->zName, - p->zName, -- pParse->regRoot, -+ pParse->u1.cr.regRoot, - zStmt, -- pParse->regRowid -+ pParse->u1.cr.regRowid - ); - sqlite3DbFree(db, zStmt); - sqlite3ChangeCookie(pParse, iDb); -@@ -112903,7 +125905,7 @@ SQLITE_PRIVATE void sqlite3EndTable( - /* Check to see if we need to create an sqlite_sequence table for - ** keeping track of autoincrement keys. - */ -- if( (p->tabFlags & TF_Autoincrement)!=0 ){ -+ if( (p->tabFlags & TF_Autoincrement)!=0 && !IN_SPECIAL_PARSE ){ - Db *pDb = &db->aDb[iDb]; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( pDb->pSchema->pSeqTab==0 ){ -@@ -112917,7 +125919,15 @@ SQLITE_PRIVATE void sqlite3EndTable( - - /* Reparse everything to update our internal data structures */ - sqlite3VdbeAddParseSchemaOp(v, iDb, -- sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName)); -+ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0); -+ -+ /* Test for cycles in generated columns and illegal expressions -+ ** in CHECK constraints and in DEFAULT clauses. */ -+ if( p->tabFlags & TF_HasGenerated ){ -+ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, -+ sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", -+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); -+ } - } - - /* Add the table to the in-memory representation of the database. -@@ -112926,6 +125936,7 @@ SQLITE_PRIVATE void sqlite3EndTable( - Table *pOld; - Schema *pSchema = p->pSchema; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); -+ assert( HasRowid(p) || p->iPKey<0 ); - pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p); - if( pOld ){ - assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ -@@ -112935,19 +125946,27 @@ SQLITE_PRIVATE void sqlite3EndTable( - pParse->pNewTable = 0; - db->mDbFlags |= DBFLAG_SchemaChange; - --#ifndef SQLITE_OMIT_ALTERTABLE -- if( !p->pSelect ){ -- const char *zName = (const char *)pParse->sNameToken.z; -- int nName; -- assert( !pSelect && pCons && pEnd ); -- if( pCons->z==0 ){ -- pCons = pEnd; -- } -- nName = (int)((const char *)pCons->z - zName); -- p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName); -+ /* If this is the magic sqlite_sequence table used by autoincrement, -+ ** then record a pointer to this table in the main database structure -+ ** so that INSERT can find the table easily. */ -+ assert( !pParse->nested ); -+#ifndef SQLITE_OMIT_AUTOINCREMENT -+ if( strcmp(p->zName, "sqlite_sequence")==0 ){ -+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); -+ p->pSchema->pSeqTab = p; - } - #endif - } -+ -+#ifndef SQLITE_OMIT_ALTERTABLE -+ if( !pSelect && IsOrdinaryTable(p) ){ -+ assert( pCons && pEnd ); -+ if( pCons->z==0 ){ -+ pCons = pEnd; -+ } -+ p->u.tab.addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z); -+ } -+#endif - } - - #ifndef SQLITE_OMIT_VIEW -@@ -112980,6 +125999,19 @@ SQLITE_PRIVATE void sqlite3CreateView( - sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); - p = pParse->pNewTable; - if( p==0 || pParse->nErr ) goto create_view_fail; -+ -+ /* Legacy versions of SQLite allowed the use of the magic "rowid" column -+ ** on a view, even though views do not have rowids. The following flag -+ ** setting fixes this problem. But the fix can be disabled by compiling -+ ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that -+ ** depend upon the old buggy behavior. The ability can also be toggled -+ ** using sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW,...) */ -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */ -+#else -+ p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */ -+#endif -+ - sqlite3TwoPartName(pParse, pName1, pName2, &pName); - iDb = sqlite3SchemaToIndex(db, p->pSchema); - sqlite3FixInit(&sFix, pParse, iDb, "view", pName); -@@ -112992,12 +126024,13 @@ SQLITE_PRIVATE void sqlite3CreateView( - */ - pSelect->selFlags |= SF_View; - if( IN_RENAME_OBJECT ){ -- p->pSelect = pSelect; -+ p->u.view.pSelect = pSelect; - pSelect = 0; - }else{ -- p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); -+ p->u.view.pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); - } - p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE); -+ p->eTabType = TABTYP_VIEW; - if( db->mallocFailed ) goto create_view_fail; - - /* Locate the end of the CREATE VIEW statement. Make sEnd point to -@@ -113032,14 +126065,14 @@ create_view_fail: - #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) - /* - ** The Table structure pTable is really a VIEW. Fill in the names of --** the columns of the view in the pTable structure. Return the number --** of errors. If an error is seen leave an error message in pParse->zErrMsg. -+** the columns of the view in the pTable structure. Return non-zero if -+** there are errors. If an error is seen an error message is left -+** in pParse->zErrMsg. - */ --SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ -+static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){ - Table *pSelTab; /* A fake table from which we get the result set */ - Select *pSel; /* Copy of the SELECT that implements the view */ - int nErr = 0; /* Number of errors encountered */ -- int n; /* Temporarily holds the number of cursors assigned */ - sqlite3 *db = pParse->db; /* Database connection for malloc errors */ - #ifndef SQLITE_OMIT_VIRTUALTABLE - int rc; -@@ -113051,20 +126084,20 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - assert( pTable ); - - #ifndef SQLITE_OMIT_VIRTUALTABLE -- db->nSchemaLock++; -- rc = sqlite3VtabCallConnect(pParse, pTable); -- db->nSchemaLock--; -- if( rc ){ -- return 1; -+ if( IsVirtual(pTable) ){ -+ db->nSchemaLock++; -+ rc = sqlite3VtabCallConnect(pParse, pTable); -+ db->nSchemaLock--; -+ return rc; - } -- if( IsVirtual(pTable) ) return 0; - #endif - - #ifndef SQLITE_OMIT_VIEW - /* A positive nCol means the columns names for this view are -- ** already known. -+ ** already known. This routine is not called unless either the -+ ** table is virtual or nCol is zero. - */ -- if( pTable->nCol>0 ) return 0; -+ assert( pTable->nCol<=0 ); - - /* A negative nCol is a special marker meaning that we are currently - ** trying to compute the column names. If we enter this routine with -@@ -113094,12 +126127,13 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - ** to be permanent. So the computation is done on a copy of the SELECT - ** statement that defines the view. - */ -- assert( pTable->pSelect ); -- pSel = sqlite3SelectDup(db, pTable->pSelect, 0); -+ assert( IsView(pTable) ); -+ pSel = sqlite3SelectDup(db, pTable->u.view.pSelect, 0); - if( pSel ){ - u8 eParseMode = pParse->eParseMode; -+ int nTab = pParse->nTab; -+ int nSelect = pParse->nSelect; - pParse->eParseMode = PARSE_MODE_NORMAL; -- n = pParse->nTab; - sqlite3SrcListAssignCursors(pParse, pSel->pSrc); - pTable->nCol = -1; - DisableLookaside; -@@ -113111,7 +126145,8 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - #else - pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE); - #endif -- pParse->nTab = n; -+ pParse->nTab = nTab; -+ pParse->nSelect = nSelect; - if( pSelTab==0 ){ - pTable->nCol = 0; - nErr++; -@@ -113124,12 +126159,11 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - */ - sqlite3ColumnsFromExprList(pParse, pTable->pCheck, - &pTable->nCol, &pTable->aCol); -- if( db->mallocFailed==0 -- && pParse->nErr==0 -+ if( pParse->nErr==0 - && pTable->nCol==pSel->pEList->nExpr - ){ -- sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel, -- SQLITE_AFF_NONE); -+ assert( db->mallocFailed==0 ); -+ sqlite3SubqueryColumnTypes(pParse, pTable, pSel, SQLITE_AFF_NONE); - } - }else{ - /* CREATE VIEW name AS... without an argument list. Construct -@@ -113138,6 +126172,7 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - assert( pTable->aCol==0 ); - pTable->nCol = pSelTab->nCol; - pTable->aCol = pSelTab->aCol; -+ pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT); - pSelTab->nCol = 0; - pSelTab->aCol = 0; - assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); -@@ -113153,11 +126188,14 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ - pTable->pSchema->schemaFlags |= DB_UnresetViews; - if( db->mallocFailed ){ - sqlite3DeleteColumnNames(db, pTable); -- pTable->aCol = 0; -- pTable->nCol = 0; - } - #endif /* SQLITE_OMIT_VIEW */ -- return nErr; -+ return nErr + pParse->nErr; -+} -+SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ -+ assert( pTable!=0 ); -+ if( !IsVirtual(pTable) && pTable->nCol>0 ) return 0; -+ return viewGetColumnNames(pParse, pTable); - } - #endif /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ - -@@ -113171,10 +126209,8 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ - if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; - for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ - Table *pTab = sqliteHashData(i); -- if( pTab->pSelect ){ -+ if( IsView(pTab) ){ - sqlite3DeleteColumnNames(db, pTab); -- pTab->aCol = 0; -- pTab->nCol = 0; - } - } - DbClearProperty(db, idx, DB_UnresetViews); -@@ -113248,7 +126284,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ - ** token for additional information. - */ - sqlite3NestedParse(pParse, -- "UPDATE %Q." DFLT_SCHEMA_TABLE -+ "UPDATE %Q." LEGACY_SCHEMA_TABLE - " SET rootpage=%d WHERE #%d AND rootpage=#%d", - pParse->db->aDb[iDb].zDbSName, iTable, r1, r1); - #endif -@@ -113383,7 +126419,7 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in - ** database. - */ - sqlite3NestedParse(pParse, -- "DELETE FROM %Q." DFLT_SCHEMA_TABLE -+ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE - " WHERE tbl_name=%Q and type!='trigger'", - pDb->zDbSName, pTab->zName); - if( !isView && !IsVirtual(pTab) ){ -@@ -113411,6 +126447,7 @@ SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db){ - if( (db->flags & SQLITE_Defensive)!=0 - && db->pVtabCtx==0 - && db->nVdbeExec==0 -+ && !sqlite3VtabInSync(db) - ){ - return 1; - } -@@ -113430,6 +126467,9 @@ static int tableMayNotBeDropped(sqlite3 *db, Table *pTab){ - if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){ - return 1; - } -+ if( pTab->tabFlags & TF_Eponymous ){ -+ return 1; -+ } - return 0; - } - -@@ -113448,6 +126488,8 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, - } - assert( pParse->nErr==0 ); - assert( pName->nSrc==1 ); -+ assert( pName->a[0].fg.fixedSchema==0 ); -+ assert( pName->a[0].fg.isSubquery==0 ); - if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; - if( noErr ) db->suppressErr++; - assert( isView==0 || isView==LOCATE_VIEW ); -@@ -113455,7 +126497,10 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, - if( noErr ) db->suppressErr--; - - if( pTab==0 ){ -- if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); -+ if( noErr ){ -+ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); -+ sqlite3ForceNotReadOnly(pParse); -+ } - goto exit_drop_table; - } - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); -@@ -113511,11 +126556,11 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, - /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used - ** on a table. - */ -- if( isView && pTab->pSelect==0 ){ -+ if( isView && !IsView(pTab) ){ - sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); - goto exit_drop_table; - } -- if( !isView && pTab->pSelect ){ -+ if( !isView && IsView(pTab) ){ - sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); - goto exit_drop_table; - } -@@ -113566,7 +126611,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( - FKey *pFKey = 0; - FKey *pNextTo; - Table *p = pParse->pNewTable; -- int nByte; -+ i64 nByte; - int i; - int nCol; - char *z; -@@ -113579,7 +126624,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( - if( pToCol && pToCol->nExpr!=1 ){ - sqlite3ErrorMsg(pParse, "foreign key on %s" - " should reference only one column of table %T", -- p->aCol[iCol].zName, pTo); -+ p->aCol[iCol].zCnName, pTo); - goto fk_end; - } - nCol = 1; -@@ -113591,7 +126636,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( - }else{ - nCol = pFromCol->nExpr; - } -- nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1; -+ nByte = SZ_FKEY(nCol) + pTo->n + 1; - if( pToCol ){ - for(i=0; inExpr; i++){ - nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; -@@ -113602,7 +126647,8 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( - goto fk_end; - } - pFKey->pFrom = p; -- pFKey->pNextFrom = p->pFKey; -+ assert( IsOrdinaryTable(p) ); -+ pFKey->pNextFrom = p->u.tab.pFKey; - z = (char*)&pFKey->aCol[nCol]; - pFKey->zTo = z; - if( IN_RENAME_OBJECT ){ -@@ -113619,7 +126665,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( - for(i=0; inCol; j++){ -- if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zEName)==0 ){ -+ if( sqlite3StrICmp(p->aCol[j].zCnName, pFromCol->a[i].zEName)==0 ){ - pFKey->aCol[i].iFrom = j; - break; - } -@@ -113667,7 +126713,8 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( - - /* Link the foreign key to the table as the last step. - */ -- p->pFKey = pFKey; -+ assert( IsOrdinaryTable(p) ); -+ p->u.tab.pFKey = pFKey; - pFKey = 0; - - fk_end: -@@ -113688,7 +126735,9 @@ SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ - #ifndef SQLITE_OMIT_FOREIGN_KEY - Table *pTab; - FKey *pFKey; -- if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; -+ if( (pTab = pParse->pNewTable)==0 ) return; -+ if( NEVER(!IsOrdinaryTable(pTab)) ) return; -+ if( (pFKey = pTab->u.tab.pFKey)==0 ) return; - assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */ - pFKey->isDeferred = (u8)isDeferred; - #endif -@@ -113738,7 +126787,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ - tnum = pIndex->tnum; - } - pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); -- assert( pKey!=0 || db->mallocFailed || pParse->nErr ); -+ assert( pKey!=0 || pParse->nErr ); - - /* Open the sorter cursor if we are to use one. */ - iSorter = pParse->nTab++; -@@ -113789,7 +126838,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ - ** not work for UNIQUE constraint indexes on WITHOUT ROWID tables - ** with DESC primary keys, since those indexes have there keys in - ** a different order from the main table. -- ** See ticket: https://www.sqlite.org/src/info/bba7b69f9849b5bf -+ ** See ticket: https://sqlite.org/src/info/bba7b69f9849b5bf - */ - sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx); - } -@@ -113813,13 +126862,14 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ - */ - SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( - sqlite3 *db, /* Database connection */ -- i16 nCol, /* Total number of columns in the index */ -+ int nCol, /* Total number of columns in the index */ - int nExtra, /* Number of bytes of extra space to alloc */ - char **ppExtra /* Pointer to the "extra" space */ - ){ - Index *p; /* Allocated index object */ -- int nByte; /* Bytes of space for Index object + arrays */ -+ i64 nByte; /* Bytes of space for Index object + arrays */ - -+ assert( nCol <= 2*db->aLimit[SQLITE_LIMIT_COLUMN] ); - nByte = ROUND8(sizeof(Index)) + /* Index structure */ - ROUND8(sizeof(char*)*nCol) + /* Index.azColl */ - ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */ -@@ -113832,8 +126882,9 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( - p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1); - p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol; - p->aSortOrder = (u8*)pExtra; -- p->nColumn = nCol; -- p->nKeyCol = nCol - 1; -+ assert( nCol>0 ); -+ p->nColumn = (u16)nCol; -+ p->nKeyCol = (u16)(nCol - 1); - *ppExtra = ((char*)p) + nByte; - } - return p; -@@ -113848,8 +126899,8 @@ SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){ - if( pList ){ - int i; - for(i=0; inExpr; i++){ -- if( pList->a[i].bNulls ){ -- u8 sf = pList->a[i].sortFlags; -+ if( pList->a[i].fg.bNulls ){ -+ u8 sf = pList->a[i].fg.sortFlags; - sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s", - (sf==0 || sf==3) ? "FIRST" : "LAST" - ); -@@ -113902,9 +126953,11 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - char *zExtra = 0; /* Extra space after the Index object */ - Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ - -- if( db->mallocFailed || pParse->nErr>0 ){ -+ assert( db->pParse==pParse ); -+ if( pParse->nErr ){ - goto exit_create_index; - } -+ assert( db->mallocFailed==0 ); - if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){ - goto exit_create_index; - } -@@ -113932,7 +126985,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - #ifndef SQLITE_OMIT_TEMPDB - /* If the index name was unqualified, check if the table - ** is a temp table. If so, set the database to 1. Do not do this -- ** if initialising a database schema. -+ ** if initializing a database schema. - */ - if( !db->init.busy ){ - pTab = sqlite3SrcListLookup(pParse, pTblName); -@@ -113968,19 +127021,15 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - pDb = &db->aDb[iDb]; - - assert( pTab!=0 ); -- assert( pParse->nErr==0 ); - if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 - && db->init.busy==0 - && pTblName!=0 --#if SQLITE_USER_AUTHENTICATION -- && sqlite3UserAuthTable(pTab->zName)==0 --#endif - ){ - sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); - goto exit_create_index; - } - #ifndef SQLITE_OMIT_VIEW -- if( pTab->pSelect ){ -+ if( IsView(pTab) ){ - sqlite3ErrorMsg(pParse, "views may not be indexed"); - goto exit_create_index; - } -@@ -114014,7 +127063,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - } - if( !IN_RENAME_OBJECT ){ - if( !db->init.busy ){ -- if( sqlite3FindTable(db, zName, 0)!=0 ){ -+ if( sqlite3FindTable(db, zName, pDb->zDbSName)!=0 ){ - sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); - goto exit_create_index; - } -@@ -114025,6 +127074,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - }else{ - assert( !db->init.busy ); - sqlite3CodeVerifySchema(pParse, iDb); -+ sqlite3ForceNotReadOnly(pParse); - } - goto exit_create_index; - } -@@ -114070,7 +127120,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - Token prevCol; - Column *pCol = &pTab->aCol[pTab->nCol-1]; - pCol->colFlags |= COLFLAG_UNIQUE; -- sqlite3TokenInit(&prevCol, pCol->zName); -+ sqlite3TokenInit(&prevCol, pCol->zCnName); - pList = sqlite3ExprListAppend(pParse, 0, - sqlite3ExprAlloc(db, TK_ID, &prevCol, 0)); - if( pList==0 ) goto exit_create_index; -@@ -114088,6 +127138,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - Expr *pExpr = pList->a[i].pExpr; - assert( pExpr!=0 ); - if( pExpr->op==TK_COLLATE ){ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken)); - } - } -@@ -114165,6 +127216,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - j = XN_EXPR; - pIndex->aiColumn[i] = XN_EXPR; - pIndex->uniqNotNull = 0; -+ pIndex->bHasExpr = 1; - }else{ - j = pCExpr->iColumn; - assert( j<=0x7fff ); -@@ -114176,6 +127228,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - } - if( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ){ - pIndex->bHasVCol = 1; -+ pIndex->bHasExpr = 1; - } - } - pIndex->aiColumn[i] = (i16)j; -@@ -114183,6 +127236,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - zColl = 0; - if( pListItem->pExpr->op==TK_COLLATE ){ - int nColl; -+ assert( !ExprHasProperty(pListItem->pExpr, EP_IntValue) ); - zColl = pListItem->pExpr->u.zToken; - nColl = sqlite3Strlen30(zColl) + 1; - assert( nExtra>=nColl ); -@@ -114191,14 +127245,14 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - zExtra += nColl; - nExtra -= nColl; - }else if( j>=0 ){ -- zColl = pTab->aCol[j].zColl; -+ zColl = sqlite3ColumnColl(&pTab->aCol[j]); - } - if( !zColl ) zColl = sqlite3StrBINARY; - if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){ - goto exit_create_index; - } - pIndex->azColl[i] = zColl; -- requestedSortOrder = pListItem->sortFlags & sortOrderMask; -+ requestedSortOrder = pListItem->fg.sortFlags & sortOrderMask; - pIndex->aSortOrder[i] = (u8)requestedSortOrder; - } - -@@ -114389,13 +127443,13 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - /* Add an entry in sqlite_schema for this index - */ - sqlite3NestedParse(pParse, -- "INSERT INTO %Q." DFLT_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", -- db->aDb[iDb].zDbSName, -- pIndex->zName, -- pTab->zName, -- iMem, -- zStmt -- ); -+ "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);", -+ db->aDb[iDb].zDbSName, -+ pIndex->zName, -+ pTab->zName, -+ iMem, -+ zStmt -+ ); - sqlite3DbFree(db, zStmt); - - /* Fill the index with data and reparse the schema. Code an OP_Expire -@@ -114405,7 +127459,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - sqlite3RefillIndex(pParse, pIndex, iMem); - sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddParseSchemaOp(v, iDb, -- sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); -+ sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), 0); - sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); - } - -@@ -114426,8 +127480,12 @@ SQLITE_PRIVATE void sqlite3CreateIndex( - /* Clean up before exiting */ - exit_create_index: - if( pIndex ) sqlite3FreeIndex(db, pIndex); -- if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */ -- Index **ppFrom = &pTab->pIndex; -+ if( pTab ){ -+ /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list. -+ ** The list was already ordered when this routine was entered, so at this -+ ** point at most a single index (the newly added index) will be out of -+ ** order. So we have to reorder at most one index. */ -+ Index **ppFrom; - Index *pThis; - for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ - Index *pNext; -@@ -114440,6 +127498,16 @@ exit_create_index: - } - break; - } -+#ifdef SQLITE_DEBUG -+ /* Verify that all REPLACE indexes really are now at the end -+ ** of the index list. In other words, no other index type ever -+ ** comes after a REPLACE index on the list. */ -+ for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){ -+ assert( pThis->onError!=OE_Replace -+ || pThis->pNext==0 -+ || pThis->pNext->onError==OE_Replace ); -+ } -+#endif - } - sqlite3ExprDelete(db, pPIWhere); - sqlite3ExprListDelete(db, pList); -@@ -114491,7 +127559,7 @@ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ - if( x<99 ){ - pIdx->pTable->nRowLogEst = x = 99; - } -- if( pIdx->pPartIdxWhere!=0 ) x -= 10; assert( 10==sqlite3LogEst(2) ); -+ if( pIdx->pPartIdxWhere!=0 ){ x -= 10; assert( 10==sqlite3LogEst(2) ); } - a[0] = x; - - /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is -@@ -114515,20 +127583,23 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists - sqlite3 *db = pParse->db; - int iDb; - -- assert( pParse->nErr==0 ); /* Never called with prior errors */ - if( db->mallocFailed ){ - goto exit_drop_index; - } -+ assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ - assert( pName->nSrc==1 ); -+ assert( pName->a[0].fg.fixedSchema==0 ); -+ assert( pName->a[0].fg.isSubquery==0 ); - if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ - goto exit_drop_index; - } -- pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); -+ pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].u4.zDatabase); - if( pIndex==0 ){ - if( !ifExists ){ -- sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); -+ sqlite3ErrorMsg(pParse, "no such index: %S", pName->a); - }else{ -- sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); -+ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); -+ sqlite3ForceNotReadOnly(pParse); - } - pParse->checkSchema = 1; - goto exit_drop_index; -@@ -114548,7 +127619,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists - if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ - goto exit_drop_index; - } -- if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; -+ if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; - if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ - goto exit_drop_index; - } -@@ -114560,7 +127631,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists - if( v ){ - sqlite3BeginWriteOperation(pParse, 1, iDb); - sqlite3NestedParse(pParse, -- "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='index'", -+ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'", - db->aDb[iDb].zDbSName, pIndex->zName - ); - sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); -@@ -114624,20 +127695,18 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token * - sqlite3 *db = pParse->db; - int i; - if( pList==0 ){ -- pList = sqlite3DbMallocZero(db, sizeof(IdList) ); -+ pList = sqlite3DbMallocZero(db, SZ_IDLIST(1)); - if( pList==0 ) return 0; -+ }else{ -+ IdList *pNew; -+ pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1)); -+ if( pNew==0 ){ -+ sqlite3IdListDelete(db, pList); -+ return 0; -+ } -+ pList = pNew; - } -- pList->a = sqlite3ArrayAllocate( -- db, -- pList->a, -- sizeof(pList->a[0]), -- &pList->nId, -- &i -- ); -- if( i<0 ){ -- sqlite3IdListDelete(db, pList); -- return 0; -- } -+ i = pList->nId++; - pList->a[i].zName = sqlite3NameFromToken(db, pToken); - if( IN_RENAME_OBJECT && pList->a[i].zName ){ - sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken); -@@ -114650,12 +127719,12 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token * - */ - SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ - int i; -+ assert( db!=0 ); - if( pList==0 ) return; - for(i=0; inId; i++){ - sqlite3DbFree(db, pList->a[i].zName); - } -- sqlite3DbFree(db, pList->a); -- sqlite3DbFreeNN(db, pList); -+ sqlite3DbNNFreeNN(db, pList); - } - - /* -@@ -114664,7 +127733,7 @@ SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ - */ - SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){ - int i; -- if( pList==0 ) return -1; -+ assert( pList!=0 ); - for(i=0; inId; i++){ - if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i; - } -@@ -114729,8 +127798,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( - return 0; - } - if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; -- pNew = sqlite3DbRealloc(db, pSrc, -- sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); -+ pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc)); - if( pNew==0 ){ - assert( db->mallocFailed ); - return 0; -@@ -114798,14 +127866,14 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( - Token *pTable, /* Table to append */ - Token *pDatabase /* Database of the table */ - ){ -- struct SrcList_item *pItem; -+ SrcItem *pItem; - sqlite3 *db; - assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ - assert( pParse!=0 ); - assert( pParse->db!=0 ); - db = pParse->db; - if( pList==0 ){ -- pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); -+ pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1)); - if( pList==0 ) return 0; - pList->nAlloc = 1; - pList->nSrc = 1; -@@ -114824,12 +127892,14 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( - if( pDatabase && pDatabase->z==0 ){ - pDatabase = 0; - } -+ assert( pItem->fg.fixedSchema==0 ); -+ assert( pItem->fg.isSubquery==0 ); - if( pDatabase ){ - pItem->zName = sqlite3NameFromToken(db, pDatabase); -- pItem->zDatabase = sqlite3NameFromToken(db, pTable); -+ pItem->u4.zDatabase = sqlite3NameFromToken(db, pTable); - }else{ - pItem->zName = sqlite3NameFromToken(db, pTable); -- pItem->zDatabase = 0; -+ pItem->u4.zDatabase = 0; - } - return pList; - } -@@ -114839,40 +127909,130 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( - */ - SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ - int i; -- struct SrcList_item *pItem; -- assert(pList || pParse->db->mallocFailed ); -- if( pList ){ -+ SrcItem *pItem; -+ assert( pList || pParse->db->mallocFailed ); -+ if( ALWAYS(pList) ){ - for(i=0, pItem=pList->a; inSrc; i++, pItem++){ -- if( pItem->iCursor>=0 ) break; -+ if( pItem->iCursor>=0 ) continue; - pItem->iCursor = pParse->nTab++; -- if( pItem->pSelect ){ -- sqlite3SrcListAssignCursors(pParse, pItem->pSelect->pSrc); -+ if( pItem->fg.isSubquery ){ -+ assert( pItem->u4.pSubq!=0 ); -+ assert( pItem->u4.pSubq->pSelect!=0 ); -+ assert( pItem->u4.pSubq->pSelect->pSrc!=0 ); -+ sqlite3SrcListAssignCursors(pParse, pItem->u4.pSubq->pSelect->pSrc); - } - } - } - } - -+/* -+** Delete a Subquery object and its substructure. -+*/ -+SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3 *db, Subquery *pSubq){ -+ assert( pSubq!=0 && pSubq->pSelect!=0 ); -+ sqlite3SelectDelete(db, pSubq->pSelect); -+ sqlite3DbFree(db, pSubq); -+} -+ -+/* -+** Remove a Subquery from a SrcItem. Return the associated Select object. -+** The returned Select becomes the responsibility of the caller. -+*/ -+SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3 *db, SrcItem *pItem){ -+ Select *pSel; -+ assert( pItem!=0 ); -+ assert( pItem->fg.isSubquery ); -+ pSel = pItem->u4.pSubq->pSelect; -+ sqlite3DbFree(db, pItem->u4.pSubq); -+ pItem->u4.pSubq = 0; -+ pItem->fg.isSubquery = 0; -+ return pSel; -+} -+ - /* - ** Delete an entire SrcList including all its substructure. - */ - SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ - int i; -- struct SrcList_item *pItem; -+ SrcItem *pItem; -+ assert( db!=0 ); - if( pList==0 ) return; - for(pItem=pList->a, i=0; inSrc; i++, pItem++){ -- sqlite3DbFree(db, pItem->zDatabase); -- sqlite3DbFree(db, pItem->zName); -- sqlite3DbFree(db, pItem->zAlias); -+ -+ /* Check invariants on SrcItem */ -+ assert( !pItem->fg.isIndexedBy || !pItem->fg.isTabFunc ); -+ assert( !pItem->fg.isCte || !pItem->fg.isIndexedBy ); -+ assert( !pItem->fg.fixedSchema || !pItem->fg.isSubquery ); -+ assert( !pItem->fg.isSubquery || (pItem->u4.pSubq!=0 && -+ pItem->u4.pSubq->pSelect!=0) ); -+ -+ if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName); -+ if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias); -+ if( pItem->fg.isSubquery ){ -+ sqlite3SubqueryDelete(db, pItem->u4.pSubq); -+ }else if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ -+ sqlite3DbNNFreeNN(db, pItem->u4.zDatabase); -+ } - if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy); - if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); -- sqlite3DeleteTable(db, pItem->pTab); -- sqlite3SelectDelete(db, pItem->pSelect); -- sqlite3ExprDelete(db, pItem->pOn); -- sqlite3IdListDelete(db, pItem->pUsing); -+ sqlite3DeleteTable(db, pItem->pSTab); -+ if( pItem->fg.isUsing ){ -+ sqlite3IdListDelete(db, pItem->u3.pUsing); -+ }else if( pItem->u3.pOn ){ -+ sqlite3ExprDelete(db, pItem->u3.pOn); -+ } - } -- sqlite3DbFreeNN(db, pList); -+ sqlite3DbNNFreeNN(db, pList); - } - -+/* -+** Attach a Subquery object to pItem->uv.pSubq. Set the -+** pSelect value but leave all the other values initialized -+** to zero. -+** -+** A copy of the Select object is made if dupSelect is true, and the -+** SrcItem takes responsibility for deleting the copy. If dupSelect is -+** false, ownership of the Select passes to the SrcItem. Either way, -+** the SrcItem will take responsibility for deleting the Select. -+** -+** When dupSelect is zero, that means the Select might get deleted right -+** away if there is an OOM error. Beware. -+** -+** Return non-zero on success. Return zero on an OOM error. -+*/ -+SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery( -+ Parse *pParse, /* Parsing context */ -+ SrcItem *pItem, /* Item to which the subquery is to be attached */ -+ Select *pSelect, /* The subquery SELECT. Must be non-NULL */ -+ int dupSelect /* If true, attach a copy of pSelect, not pSelect itself.*/ -+){ -+ Subquery *p; -+ assert( pSelect!=0 ); -+ assert( pItem->fg.isSubquery==0 ); -+ if( pItem->fg.fixedSchema ){ -+ pItem->u4.pSchema = 0; -+ pItem->fg.fixedSchema = 0; -+ }else if( pItem->u4.zDatabase!=0 ){ -+ sqlite3DbFree(pParse->db, pItem->u4.zDatabase); -+ pItem->u4.zDatabase = 0; -+ } -+ if( dupSelect ){ -+ pSelect = sqlite3SelectDup(pParse->db, pSelect, 0); -+ if( pSelect==0 ) return 0; -+ } -+ p = pItem->u4.pSubq = sqlite3DbMallocRawNN(pParse->db, sizeof(Subquery)); -+ if( p==0 ){ -+ sqlite3SelectDelete(pParse->db, pSelect); -+ return 0; -+ } -+ pItem->fg.isSubquery = 1; -+ p->pSelect = pSelect; -+ assert( offsetof(Subquery, pSelect)==0 ); -+ memset(((char*)p)+sizeof(p->pSelect), 0, sizeof(*p)-sizeof(p->pSelect)); -+ return 1; -+} -+ -+ - /* - ** This routine is called by the parser to add a new term to the - ** end of a growing FROM clause. The "p" parameter is the part of -@@ -114896,14 +128056,13 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( - Token *pDatabase, /* Name of the database containing pTable */ - Token *pAlias, /* The right-hand side of the AS subexpression */ - Select *pSubquery, /* A subquery used in place of a table name */ -- Expr *pOn, /* The ON clause of a join */ -- IdList *pUsing /* The USING clause of a join */ -+ OnOrUsing *pOnUsing /* Either the ON clause or the USING clause */ - ){ -- struct SrcList_item *pItem; -+ SrcItem *pItem; - sqlite3 *db = pParse->db; -- if( !p && (pOn || pUsing) ){ -+ if( !p && pOnUsing!=0 && (pOnUsing->pOn || pOnUsing->pUsing) ){ - sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", -- (pOn ? "ON" : "USING") -+ (pOnUsing->pOn ? "ON" : "USING") - ); - goto append_from_error; - } -@@ -114923,15 +128082,29 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( - if( pAlias->n ){ - pItem->zAlias = sqlite3NameFromToken(db, pAlias); - } -- pItem->pSelect = pSubquery; -- pItem->pOn = pOn; -- pItem->pUsing = pUsing; -+ assert( pSubquery==0 || pDatabase==0 ); -+ if( pSubquery ){ -+ if( sqlite3SrcItemAttachSubquery(pParse, pItem, pSubquery, 0) ){ -+ if( pSubquery->selFlags & SF_NestedFrom ){ -+ pItem->fg.isNestedFrom = 1; -+ } -+ } -+ } -+ assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 ); -+ assert( pItem->fg.isUsing==0 ); -+ if( pOnUsing==0 ){ -+ pItem->u3.pOn = 0; -+ }else if( pOnUsing->pUsing ){ -+ pItem->fg.isUsing = 1; -+ pItem->u3.pUsing = pOnUsing->pUsing; -+ }else{ -+ pItem->u3.pOn = pOnUsing->pOn; -+ } - return p; - -- append_from_error: -+append_from_error: - assert( p==0 ); -- sqlite3ExprDelete(db, pOn); -- sqlite3IdListDelete(db, pUsing); -+ sqlite3ClearOnOrUsing(db, pOnUsing); - sqlite3SelectDelete(db, pSubquery); - return 0; - } -@@ -114943,7 +128116,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( - SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ - assert( pIndexedBy!=0 ); - if( p && pIndexedBy->n>0 ){ -- struct SrcList_item *pItem; -+ SrcItem *pItem; - assert( p->nSrc>0 ); - pItem = &p->a[p->nSrc-1]; - assert( pItem->fg.notIndexed==0 ); -@@ -114956,6 +128129,7 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI - }else{ - pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy); - pItem->fg.isIndexedBy = 1; -+ assert( pItem->fg.isCte==0 ); /* No collision on union u2 */ - } - } - } -@@ -114973,8 +128147,9 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, Src - sqlite3SrcListDelete(pParse->db, p2); - }else{ - p1 = pNew; -- memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item)); -+ memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem)); - sqlite3DbFree(pParse->db, p2); -+ p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype); - } - } - return p1; -@@ -114986,7 +128161,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, Src - */ - SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){ - if( p ){ -- struct SrcList_item *pItem = &p->a[p->nSrc-1]; -+ SrcItem *pItem = &p->a[p->nSrc-1]; - assert( pItem->fg.notIndexed==0 ); - assert( pItem->fg.isIndexedBy==0 ); - assert( pItem->fg.isTabFunc==0 ); -@@ -115011,14 +128186,34 @@ SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList * - ** The operator is "natural cross join". The A and B operands are stored - ** in p->a[0] and p->a[1], respectively. The parser initially stores the - ** operator with A. This routine shifts that operator over to B. -+** -+** Additional changes: -+** -+** * All tables to the left of the right-most RIGHT JOIN are tagged with -+** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the -+** code generator can easily tell that the table is part of -+** the left operand of at least one RIGHT JOIN. - */ --SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){ -- if( p ){ -- int i; -- for(i=p->nSrc-1; i>0; i--){ -- p->a[i].fg.jointype = p->a[i-1].fg.jointype; -- } -+SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse *pParse, SrcList *p){ -+ (void)pParse; -+ if( p && p->nSrc>1 ){ -+ int i = p->nSrc-1; -+ u8 allFlags = 0; -+ do{ -+ allFlags |= p->a[i].fg.jointype = p->a[i-1].fg.jointype; -+ }while( (--i)>0 ); - p->a[0].fg.jointype = 0; -+ -+ /* All terms to the left of a RIGHT JOIN should be tagged with the -+ ** JT_LTORJ flags */ -+ if( allFlags & JT_RIGHT ){ -+ for(i=p->nSrc-1; ALWAYS(i>0) && (p->a[i].fg.jointype&JT_RIGHT)==0; i--){} -+ i--; -+ assert( i>=0 ); -+ do{ -+ p->a[i].fg.jointype |= JT_LTORJ; -+ }while( (--i)>=0 ); -+ } - } - } - -@@ -115040,7 +128235,16 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){ - if( !v ) return; - if( type!=TK_DEFERRED ){ - for(i=0; inDb; i++){ -- sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); -+ int eTxnType; -+ Btree *pBt = db->aDb[i].pBt; -+ if( pBt && sqlite3BtreeIsReadonly(pBt) ){ -+ eTxnType = 0; /* Read txn */ -+ }else if( type==TK_EXCLUSIVE ){ -+ eTxnType = 2; /* Exclusive txn */ -+ }else{ -+ eTxnType = 1; /* Write txn */ -+ } -+ sqlite3VdbeAddOp2(v, OP_Transaction, i, eTxnType); - sqlite3VdbeUsesBtree(v, i); - } - } -@@ -115129,13 +128333,11 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){ - ** will occur at the end of the top-level VDBE and will be generated - ** later, by sqlite3FinishCoding(). - */ --SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ -- Parse *pToplevel = sqlite3ParseToplevel(pParse); -- -- assert( iDb>=0 && iDbdb->nDb ); -- assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 ); -- assert( iDbdb, iDb, 0) ); -+static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){ -+ assert( iDb>=0 && iDbdb->nDb ); -+ assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 ); -+ assert( iDbdb, iDb, 0) ); - if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){ - DbMaskSet(pToplevel->cookieMask, iDb); - if( !OMIT_TEMPDB && iDb==1 ){ -@@ -115143,6 +128345,10 @@ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ - } - } - } -+SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ -+ sqlite3CodeVerifySchemaAtToplevel(sqlite3ParseToplevel(pParse), iDb); -+} -+ - - /* - ** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each -@@ -115174,7 +128380,7 @@ SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb) - */ - SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); -- sqlite3CodeVerifySchema(pParse, iDb); -+ sqlite3CodeVerifySchemaAtToplevel(pToplevel, iDb); - DbMaskSet(pToplevel->writeMask, iDb); - pToplevel->isMultiWrite |= setStatement; - } -@@ -115225,7 +128431,9 @@ SQLITE_PRIVATE void sqlite3HaltConstraint( - i8 p4type, /* P4_STATIC or P4_TRANSIENT */ - u8 p5Errmsg /* P5_ErrMsg type */ - ){ -- Vdbe *v = sqlite3GetVdbe(pParse); -+ Vdbe *v; -+ assert( pParse->pVdbe!=0 ); -+ v = sqlite3GetVdbe(pParse); - assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested ); - if( onError==OE_Abort ){ - sqlite3MayAbort(pParse); -@@ -115255,7 +128463,7 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint( - for(j=0; jnKeyCol; j++){ - char *zCol; - assert( pIdx->aiColumn[j]>=0 ); -- zCol = pTab->aCol[pIdx->aiColumn[j]].zName; -+ zCol = pTab->aCol[pIdx->aiColumn[j]].zCnName; - if( j ) sqlite3_str_append(&errMsg, ", ", 2); - sqlite3_str_appendall(&errMsg, pTab->zName); - sqlite3_str_append(&errMsg, ".", 1); -@@ -115282,7 +128490,7 @@ SQLITE_PRIVATE void sqlite3RowidConstraint( - int rc; - if( pTab->iPKey>=0 ){ - zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName, -- pTab->aCol[pTab->iPKey].zName); -+ pTab->aCol[pTab->iPKey].zCnName); - rc = SQLITE_CONSTRAINT_PRIMARYKEY; - }else{ - zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName); -@@ -115405,7 +128613,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ - if( iDb<0 ) return; - z = sqlite3NameFromToken(db, pObjName); - if( z==0 ) return; -- zDb = db->aDb[iDb].zDbSName; -+ zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; - pTab = sqlite3FindTable(db, z, zDb); - if( pTab ){ - reindexTable(pParse, pTab, 0); -@@ -115415,6 +128623,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ - pIndex = sqlite3FindIndex(db, z, zDb); - sqlite3DbFree(db, z); - if( pIndex ){ -+ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); - return; -@@ -115470,24 +128679,76 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ - } - - #ifndef SQLITE_OMIT_CTE -+/* -+** Create a new CTE object -+*/ -+SQLITE_PRIVATE Cte *sqlite3CteNew( -+ Parse *pParse, /* Parsing context */ -+ Token *pName, /* Name of the common-table */ -+ ExprList *pArglist, /* Optional column name list for the table */ -+ Select *pQuery, /* Query used to initialize the table */ -+ u8 eM10d /* The MATERIALIZED flag */ -+){ -+ Cte *pNew; -+ sqlite3 *db = pParse->db; -+ -+ pNew = sqlite3DbMallocZero(db, sizeof(*pNew)); -+ assert( pNew!=0 || db->mallocFailed ); -+ -+ if( db->mallocFailed ){ -+ sqlite3ExprListDelete(db, pArglist); -+ sqlite3SelectDelete(db, pQuery); -+ }else{ -+ pNew->pSelect = pQuery; -+ pNew->pCols = pArglist; -+ pNew->zName = sqlite3NameFromToken(pParse->db, pName); -+ pNew->eM10d = eM10d; -+ } -+ return pNew; -+} -+ -+/* -+** Clear information from a Cte object, but do not deallocate storage -+** for the object itself. -+*/ -+static void cteClear(sqlite3 *db, Cte *pCte){ -+ assert( pCte!=0 ); -+ sqlite3ExprListDelete(db, pCte->pCols); -+ sqlite3SelectDelete(db, pCte->pSelect); -+ sqlite3DbFree(db, pCte->zName); -+} -+ -+/* -+** Free the contents of the CTE object passed as the second argument. -+*/ -+SQLITE_PRIVATE void sqlite3CteDelete(sqlite3 *db, Cte *pCte){ -+ assert( pCte!=0 ); -+ cteClear(db, pCte); -+ sqlite3DbFree(db, pCte); -+} -+ - /* - ** This routine is invoked once per CTE by the parser while parsing a --** WITH clause. -+** WITH clause. The CTE described by the third argument is added to -+** the WITH clause of the second argument. If the second argument is -+** NULL, then a new WITH argument is created. - */ - SQLITE_PRIVATE With *sqlite3WithAdd( - Parse *pParse, /* Parsing context */ - With *pWith, /* Existing WITH clause, or NULL */ -- Token *pName, /* Name of the common-table */ -- ExprList *pArglist, /* Optional column name list for the table */ -- Select *pQuery /* Query used to initialize the table */ -+ Cte *pCte /* CTE to add to the WITH clause */ - ){ - sqlite3 *db = pParse->db; - With *pNew; - char *zName; - -+ if( pCte==0 ){ -+ return pWith; -+ } -+ - /* Check that the CTE name is unique within this WITH clause. If - ** not, store an error in the Parse structure. */ -- zName = sqlite3NameFromToken(pParse->db, pName); -+ zName = pCte->zName; - if( zName && pWith ){ - int i; - for(i=0; inCte; i++){ -@@ -115498,24 +128759,18 @@ SQLITE_PRIVATE With *sqlite3WithAdd( - } - - if( pWith ){ -- sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); -- pNew = sqlite3DbRealloc(db, pWith, nByte); -+ pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1)); - }else{ -- pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); -+ pNew = sqlite3DbMallocZero(db, SZ_WITH(1)); - } - assert( (pNew!=0 && zName!=0) || db->mallocFailed ); - - if( db->mallocFailed ){ -- sqlite3ExprListDelete(db, pArglist); -- sqlite3SelectDelete(db, pQuery); -- sqlite3DbFree(db, zName); -+ sqlite3CteDelete(db, pCte); - pNew = pWith; - }else{ -- pNew->a[pNew->nCte].pSelect = pQuery; -- pNew->a[pNew->nCte].pCols = pArglist; -- pNew->a[pNew->nCte].zName = zName; -- pNew->a[pNew->nCte].zCteErr = 0; -- pNew->nCte++; -+ pNew->a[pNew->nCte++] = *pCte; -+ sqlite3DbFree(db, pCte); - } - - return pNew; -@@ -115528,14 +128783,14 @@ SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){ - if( pWith ){ - int i; - for(i=0; inCte; i++){ -- struct Cte *pCte = &pWith->a[i]; -- sqlite3ExprListDelete(db, pCte->pCols); -- sqlite3SelectDelete(db, pCte->pSelect); -- sqlite3DbFree(db, pCte->zName); -+ cteClear(db, &pWith->a[i]); - } - sqlite3DbFree(db, pWith); - } - } -+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){ -+ sqlite3WithDelete(db, (With*)pWith); -+} - #endif /* !defined(SQLITE_OMIT_CTE) */ - - /************** End of build.c ***********************************************/ -@@ -115727,6 +128982,7 @@ SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){ - ** strings is BINARY. - */ - db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0); -+ sqlite3ExpirePreparedStatements(db, 1); - } - - /* -@@ -115843,12 +129099,18 @@ static int matchQuality( - u8 enc /* Desired text encoding */ - ){ - int match; -- assert( p->nArg>=-1 ); -+ assert( p->nArg>=(-4) && p->nArg!=(-2) ); -+ assert( nArg>=(-2) ); - - /* Wrong number of arguments means "no match" */ - if( p->nArg!=nArg ){ -- if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH; -+ if( nArg==(-2) ) return p->xSFunc==0 ? 0 : FUNC_PERFECT_MATCH; - if( p->nArg>=0 ) return 0; -+ /* Special p->nArg values available to built-in functions only: -+ ** -3 1 or more arguments required -+ ** -4 2 or more arguments required -+ */ -+ if( p->nArg<(-2) && nArg<(-2-p->nArg) ) return 0; - } - - /* Give a better score to a function with a specific number of arguments -@@ -115879,6 +129141,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch( - ){ - FuncDef *p; - for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ -+ assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); - if( sqlite3StrICmp(p->zName, zFunc)==0 ){ - return p; - } -@@ -115899,7 +129162,7 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs( - const char *zName = aDef[i].zName; - int nName = sqlite3Strlen30(zName); - int h = SQLITE_FUNC_HASH(zName[0], nName); -- assert( zName[0]>='a' && zName[0]<='z' ); -+ assert( aDef[i].funcFlags & SQLITE_FUNC_BUILTIN ); - pOther = sqlite3FunctionSearch(h, zName); - if( pOther ){ - assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] ); -@@ -116031,19 +129294,21 @@ SQLITE_PRIVATE void sqlite3SchemaClear(void *p){ - Hash temp2; - HashElem *pElem; - Schema *pSchema = (Schema *)p; -+ sqlite3 xdb; - -+ memset(&xdb, 0, sizeof(xdb)); - temp1 = pSchema->tblHash; - temp2 = pSchema->trigHash; - sqlite3HashInit(&pSchema->trigHash); - sqlite3HashClear(&pSchema->idxHash); - for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ -- sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem)); -+ sqlite3DeleteTrigger(&xdb, (Trigger*)sqliteHashData(pElem)); - } - sqlite3HashClear(&temp2); - sqlite3HashInit(&pSchema->tblHash); - for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ - Table *pTab = sqliteHashData(pElem); -- sqlite3DeleteTable(0, pTab); -+ sqlite3DeleteTable(&xdb, pTab); - } - sqlite3HashClear(&temp1); - sqlite3HashClear(&pSchema->fkeyHash); -@@ -116105,26 +129370,37 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ - ** - ** The following fields are initialized appropriate in pSrc: - ** --** pSrc->a[0].pTab Pointer to the Table object --** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one -+** pSrc->a[0].spTab Pointer to the Table object -+** pSrc->a[0].u2.pIBIndex Pointer to the INDEXED BY index, if there is one - ** - */ - SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ -- struct SrcList_item *pItem = pSrc->a; -+ SrcItem *pItem = pSrc->a; - Table *pTab; - assert( pItem && pSrc->nSrc>=1 ); - pTab = sqlite3LocateTableItem(pParse, 0, pItem); -- sqlite3DeleteTable(pParse->db, pItem->pTab); -- pItem->pTab = pTab; -+ if( pItem->pSTab ) sqlite3DeleteTable(pParse->db, pItem->pSTab); -+ pItem->pSTab = pTab; -+ pItem->fg.notCte = 1; - if( pTab ){ - pTab->nTabRef++; -- } -- if( sqlite3IndexedByLookup(pParse, pItem) ){ -- pTab = 0; -+ if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){ -+ pTab = 0; -+ } - } - return pTab; - } - -+/* Generate byte-code that will report the number of rows modified -+** by a DELETE, INSERT, or UPDATE statement. -+*/ -+SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *zColName){ -+ sqlite3VdbeAddOp0(v, OP_FkCheck); -+ sqlite3VdbeAddOp2(v, OP_ResultRow, regCounter, 1); -+ sqlite3VdbeSetNumCols(v, 1); -+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, SQLITE_STATIC); -+} -+ - /* Return true if table pTab is read-only. - ** - ** A table is read-only if any of the following are true: -@@ -116132,18 +129408,43 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ - ** 1) It is a virtual table and no implementation of the xUpdate method - ** has been provided - ** --** 2) It is a system table (i.e. sqlite_schema), this call is not -+** 2) A trigger is currently being coded and the table is a virtual table -+** that is SQLITE_VTAB_DIRECTONLY or if PRAGMA trusted_schema=OFF and -+** the table is not SQLITE_VTAB_INNOCUOUS. -+** -+** 3) It is a system table (i.e. sqlite_schema), this call is not - ** part of a nested parse and writable_schema pragma has not - ** been specified - ** --** 3) The table is a shadow table, the database connection is in -+** 4) The table is a shadow table, the database connection is in - ** defensive mode, and the current sqlite3_prepare() - ** is for a top-level SQL statement. - */ -+static int vtabIsReadOnly(Parse *pParse, Table *pTab){ -+ assert( IsVirtual(pTab) ); -+ if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ -+ return 1; -+ } -+ -+ /* Within triggers: -+ ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY -+ ** virtual tables -+ ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS -+ ** virtual tables if PRAGMA trusted_schema=ON. -+ */ -+ if( pParse->pToplevel!=0 -+ && pTab->u.vtab.p->eVtabRisk > -+ ((pParse->db->flags & SQLITE_TrustedSchema)!=0) -+ ){ -+ sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", -+ pTab->zName); -+ } -+ return 0; -+} - static int tabIsReadOnly(Parse *pParse, Table *pTab){ - sqlite3 *db; - if( IsVirtual(pTab) ){ -- return sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0; -+ return vtabIsReadOnly(pParse, pTab); - } - if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0; - db = pParse->db; -@@ -116155,17 +129456,21 @@ static int tabIsReadOnly(Parse *pParse, Table *pTab){ - } - - /* --** Check to make sure the given table is writable. If it is not --** writable, generate an error message and return 1. If it is --** writable return 0; -+** Check to make sure the given table is writable. -+** -+** If pTab is not writable -> generate an error message and return 1. -+** If pTab is writable but other errors have occurred -> return 1. -+** If pTab is writable and no prior errors -> return 0; - */ --SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ -+SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, Trigger *pTrigger){ - if( tabIsReadOnly(pParse, pTab) ){ - sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); - return 1; - } - #ifndef SQLITE_OMIT_VIEW -- if( !viewOk && pTab->pSelect ){ -+ if( IsView(pTab) -+ && (pTrigger==0 || (pTrigger->bReturning && pTrigger->pNext==0)) -+ ){ - sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); - return 1; - } -@@ -116198,9 +129503,10 @@ SQLITE_PRIVATE void sqlite3MaterializeView( - if( pFrom ){ - assert( pFrom->nSrc==1 ); - pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); -- pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); -- assert( pFrom->a[0].pOn==0 ); -- assert( pFrom->a[0].pUsing==0 ); -+ assert( pFrom->a[0].fg.fixedSchema==0 && pFrom->a[0].fg.isSubquery==0 ); -+ pFrom->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); -+ assert( pFrom->a[0].fg.isUsing==0 ); -+ assert( pFrom->a[0].u3.pOn==0 ); - } - pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, - SF_IncludeHidden, pLimit); -@@ -116230,7 +129536,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( - sqlite3 *db = pParse->db; - Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */ - Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */ -- ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */ -+ ExprList *pEList = NULL; /* Expression list containing only pSelectRowid*/ - SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */ - Select *pSelect = NULL; /* Complete SELECT tree */ - Table *pTab; -@@ -116260,7 +129566,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( - ** ); - */ - -- pTab = pSrc->a[0].pTab; -+ pTab = pSrc->a[0].pSTab; - if( HasRowid(pTab) ){ - pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0); - pEList = sqlite3ExprListAppend( -@@ -116268,14 +129574,20 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( - ); - }else{ - Index *pPk = sqlite3PrimaryKeyIndex(pTab); -+ assert( pPk!=0 ); -+ assert( pPk->nKeyCol>=1 ); - if( pPk->nKeyCol==1 ){ -- const char *zName = pTab->aCol[pPk->aiColumn[0]].zName; -+ const char *zName; -+ assert( pPk->aiColumn[0]>=0 && pPk->aiColumn[0]nCol ); -+ zName = pTab->aCol[pPk->aiColumn[0]].zCnName; - pLhs = sqlite3Expr(db, TK_ID, zName); - pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName)); - }else{ - int i; - for(i=0; inKeyCol; i++){ -- Expr *p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName); -+ Expr *p; -+ assert( pPk->aiColumn[i]>=0 && pPk->aiColumn[i]nCol ); -+ p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName); - pEList = sqlite3ExprListAppend(pParse, pEList, p); - } - pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); -@@ -116287,17 +129599,24 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( - - /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree - ** and the SELECT subtree. */ -- pSrc->a[0].pTab = 0; -- pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0); -- pSrc->a[0].pTab = pTab; -- pSrc->a[0].pIBIndex = 0; -+ pSrc->a[0].pSTab = 0; -+ pSelectSrc = sqlite3SrcListDup(db, pSrc, 0); -+ pSrc->a[0].pSTab = pTab; -+ if( pSrc->a[0].fg.isIndexedBy ){ -+ assert( pSrc->a[0].fg.isCte==0 ); -+ pSrc->a[0].u2.pIBIndex = 0; -+ pSrc->a[0].fg.isIndexedBy = 0; -+ sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy); -+ }else if( pSrc->a[0].fg.isCte ){ -+ pSrc->a[0].u2.pCteUse->nUse++; -+ } - - /* generate the SELECT expression tree. */ - pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0, - pOrderBy,0,pLimit - ); - -- /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ -+ /* now generate the new WHERE rowid IN clause for the DELETE/UPDATE */ - pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0); - sqlite3PExprAddSelect(pParse, pInClause, pSelect); - return pInClause; -@@ -116357,12 +129676,13 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - - memset(&sContext, 0, sizeof(sContext)); - db = pParse->db; -- if( pParse->nErr || db->mallocFailed ){ -+ assert( db->pParse==pParse ); -+ if( pParse->nErr ){ - goto delete_from_cleanup; - } -+ assert( db->mallocFailed==0 ); - assert( pTabList->nSrc==1 ); - -- - /* Locate the table which we want to delete. This table has to be - ** put in an SrcList structure because some of the subroutines we - ** will be calling are designed to work with multiple tables and expect -@@ -116376,7 +129696,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - */ - #ifndef SQLITE_OMIT_TRIGGER - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); -- isView = pTab->pSelect!=0; -+ isView = IsView(pTab); - #else - # define pTrigger 0 - # define isView 0 -@@ -116387,6 +129707,14 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - # define isView 0 - #endif - -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x10000 ){ -+ sqlite3TreeViewLine(0, "In sqlite3Delete() at %s:%d", __FILE__, __LINE__); -+ sqlite3TreeViewDelete(pParse->pWith, pTabList, pWhere, -+ pOrderBy, pLimit, pTrigger); -+ } -+#endif -+ - #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT - if( !isView ){ - pWhere = sqlite3LimitWhere( -@@ -116403,7 +129731,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - goto delete_from_cleanup; - } - -- if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ -+ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ - goto delete_from_cleanup; - } - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); -@@ -116468,6 +129796,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - if( (db->flags & SQLITE_CountRows)!=0 - && !pParse->nested - && !pParse->pTriggerTab -+ && !pParse->bReturning - ){ - memCnt = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); -@@ -116501,18 +129830,22 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - } - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - assert( pIdx->pSchema==pTab->pSchema ); -- sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); -+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ -+ sqlite3VdbeAddOp3(v, OP_Clear, pIdx->tnum, iDb, memCnt ? memCnt : -1); -+ }else{ -+ sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); -+ } - } - }else - #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ - { -- u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE; -- if( sNC.ncFlags & NC_VarSelect ) bComplex = 1; -+ u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; -+ if( sNC.ncFlags & NC_Subquery ) bComplex = 1; - wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); - if( HasRowid(pTab) ){ - /* For a rowid table, initialize the RowSet to an empty set */ - pPk = 0; -- nPk = 1; -+ assert( nPk==1 ); - iRowSet = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); - }else{ -@@ -116536,12 +129869,16 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - ** ONEPASS_SINGLE: One-pass approach - at most one row deleted. - ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted. - */ -- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); -+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1); - if( pWInfo==0 ) goto delete_from_cleanup; - eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); - assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); -- assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF ); -+ assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF -+ || OptimizationDisabled(db, SQLITE_OnePass) ); - if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse); -+ if( sqlite3WhereUsesDeferredSeek(pWInfo) ){ -+ sqlite3VdbeAddOp1(v, OP_FinishSeek, iTabCur); -+ } - - /* Keep track of the number of rows to be deleted */ - if( memCnt ){ -@@ -116576,6 +129913,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0; - if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0; - if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen); -+ addrBypass = sqlite3VdbeMakeLabel(pParse); - }else{ - if( pPk ){ - /* Add the PK key for this row to the temporary table */ -@@ -116589,13 +129927,6 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - nKey = 1; /* OP_DeferredSeek always uses a single rowid */ - sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); - } -- } -- -- /* If this DELETE cannot use the ONEPASS strategy, this is the -- ** end of the WHERE loop */ -- if( eOnePass!=ONEPASS_OFF ){ -- addrBypass = sqlite3VdbeMakeLabel(pParse); -- }else{ - sqlite3WhereEnd(pWInfo); - } - -@@ -116625,7 +129956,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - if( eOnePass!=ONEPASS_OFF ){ - assert( nKey==nPk ); /* OP_Found will use an unpacked key */ - if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ -- assert( pPk!=0 || pTab->pSelect!=0 ); -+ assert( pPk!=0 || IsView(pTab) ); - sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); - VdbeCoverage(v); - } -@@ -116692,9 +130023,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( - ** invoke the callback function. - */ - if( memCnt ){ -- sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1); -- sqlite3VdbeSetNumCols(v, 1); -- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); -+ sqlite3CodeChangeCount(v, memCnt, "rows deleted"); - } - - delete_from_cleanup: -@@ -116705,7 +130034,7 @@ delete_from_cleanup: - sqlite3ExprListDelete(db, pOrderBy); - sqlite3ExprDelete(db, pLimit); - #endif -- sqlite3DbFree(db, aToOpen); -+ if( aToOpen ) sqlite3DbNNFreeNN(db, aToOpen); - return; - } - /* Make sure "isView" and other macros defined above are undefined. Otherwise -@@ -116859,7 +130188,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( - ** the update-hook is not invoked for rows removed by REPLACE, but the - ** pre-update-hook is. - */ -- if( pTab->pSelect==0 ){ -+ if( !IsView(pTab) ){ - u8 p5 = 0; - sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); - sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); -@@ -116882,9 +130211,11 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( - sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0); - - /* Invoke AFTER DELETE trigger programs. */ -- sqlite3CodeRowTrigger(pParse, pTrigger, -- TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel -- ); -+ if( pTrigger ){ -+ sqlite3CodeRowTrigger(pParse, pTrigger, -+ TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel -+ ); -+ } - - /* Jump here if the row had already been deleted before any BEFORE - ** trigger programs were invoked. Or if a trigger program throws a -@@ -117016,20 +130347,18 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey( - continue; - } - sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); -- /* If the column affinity is REAL but the number is an integer, then it -- ** might be stored in the table as an integer (using a compact -- ** representation) then converted to REAL by an OP_RealAffinity opcode. -- ** But we are getting ready to store this value back into an index, where -- ** it should be converted by to INTEGER again. So omit the OP_RealAffinity -- ** opcode if it is present */ -- sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); -+ if( pIdx->aiColumn[j]>=0 ){ -+ /* If the column affinity is REAL but the number is an integer, then it -+ ** might be stored in the table as an integer (using a compact -+ ** representation) then converted to REAL by an OP_RealAffinity opcode. -+ ** But we are getting ready to store this value back into an index, where -+ ** it should be converted by to INTEGER again. So omit the -+ ** OP_RealAffinity opcode if it is present */ -+ sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); -+ } - } - if( regOut ){ - sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); -- if( pIdx->pTable->pSelect ){ -- const char *zAff = sqlite3IndexAffinityStr(pParse->db, pIdx); -- sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); -- } - } - sqlite3ReleaseTempRange(pParse, regBase, nCol); - return regBase; -@@ -117147,6 +130476,18 @@ static void typeofFunc( - sqlite3_result_text(context, azType[i], -1, SQLITE_STATIC); - } - -+/* subtype(X) -+** -+** Return the subtype of X -+*/ -+static void subtypeFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ UNUSED_PARAMETER(argc); -+ sqlite3_result_int(context, sqlite3_value_subtype(argv[0])); -+} - - /* - ** Implementation of the length() function -@@ -117187,6 +130528,42 @@ static void lengthFunc( - } - } - -+/* -+** Implementation of the octet_length() function -+*/ -+static void bytelengthFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ assert( argc==1 ); -+ UNUSED_PARAMETER(argc); -+ switch( sqlite3_value_type(argv[0]) ){ -+ case SQLITE_BLOB: { -+ sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); -+ break; -+ } -+ case SQLITE_INTEGER: -+ case SQLITE_FLOAT: { -+ i64 m = sqlite3_context_db_handle(context)->enc<=SQLITE_UTF8 ? 1 : 2; -+ sqlite3_result_int64(context, sqlite3_value_bytes(argv[0])*m); -+ break; -+ } -+ case SQLITE_TEXT: { -+ if( sqlite3_value_encoding(argv[0])<=SQLITE_UTF8 ){ -+ sqlite3_result_int(context, sqlite3_value_bytes(argv[0])); -+ }else{ -+ sqlite3_result_int(context, sqlite3_value_bytes16(argv[0])); -+ } -+ break; -+ } -+ default: { -+ sqlite3_result_null(context); -+ break; -+ } -+ } -+} -+ - /* - ** Implementation of the abs() function. - ** -@@ -117308,7 +130685,7 @@ endInstrOOM: - } - - /* --** Implementation of the printf() function. -+** Implementation of the printf() (a.k.a. format()) SQL function. - */ - static void printfFunc( - sqlite3_context *context, -@@ -117356,16 +130733,10 @@ static void substrFunc( - int len; - int p0type; - i64 p1, p2; -- int negP2 = 0; - - assert( argc==3 || argc==2 ); -- if( sqlite3_value_type(argv[1])==SQLITE_NULL -- || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) -- ){ -- return; -- } - p0type = sqlite3_value_type(argv[0]); -- p1 = sqlite3_value_int(argv[1]); -+ p1 = sqlite3_value_int64(argv[1]); - if( p0type==SQLITE_BLOB ){ - len = sqlite3_value_bytes(argv[0]); - z = sqlite3_value_blob(argv[0]); -@@ -117381,28 +130752,31 @@ static void substrFunc( - } - } - } --#ifdef SQLITE_SUBSTR_COMPATIBILITY -- /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as -- ** as substr(X,1,N) - it returns the first N characters of X. This -- ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] -- ** from 2009-02-02 for compatibility of applications that exploited the -- ** old buggy behavior. */ -- if( p1==0 ) p1 = 1; /* */ --#endif - if( argc==3 ){ -- p2 = sqlite3_value_int(argv[2]); -- if( p2<0 ){ -- p2 = -p2; -- negP2 = 1; -- } -+ p2 = sqlite3_value_int64(argv[2]); -+ if( p2==0 && sqlite3_value_type(argv[2])==SQLITE_NULL ) return; - }else{ - p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH]; - } -+ if( p1==0 ){ -+#ifdef SQLITE_SUBSTR_COMPATIBILITY -+ /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as -+ ** as substr(X,1,N) - it returns the first N characters of X. This -+ ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] -+ ** from 2009-02-02 for compatibility of applications that exploited the -+ ** old buggy behavior. */ -+ p1 = 1; /* */ -+#endif -+ if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return; -+ } - if( p1<0 ){ - p1 += len; - if( p1<0 ){ -- p2 += p1; -- if( p2<0 ) p2 = 0; -+ if( p2<0 ){ -+ p2 = 0; -+ }else{ -+ p2 += p1; -+ } - p1 = 0; - } - }else if( p1>0 ){ -@@ -117410,12 +130784,13 @@ static void substrFunc( - }else if( p2>0 ){ - p2--; - } -- if( negP2 ){ -- p1 -= p2; -- if( p1<0 ){ -- p2 += p1; -- p1 = 0; -+ if( p2<0 ){ -+ if( p2<-p1 ){ -+ p2 = p1; -+ }else{ -+ p2 = -p2; - } -+ p1 -= p2; - } - assert( p1>=0 && p2>=0 ); - if( p0type!=SQLITE_BLOB ){ -@@ -117429,9 +130804,11 @@ static void substrFunc( - sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT, - SQLITE_UTF8); - }else{ -- if( p1+p2>len ){ -+ if( p1>=len ){ -+ p1 = p2 = 0; -+ }else if( p2>len-p1 ){ - p2 = len-p1; -- if( p2<0 ) p2 = 0; -+ assert( p2>0 ); - } - sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT); - } -@@ -117442,13 +130819,13 @@ static void substrFunc( - */ - #ifndef SQLITE_OMIT_FLOATING_POINT - static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ -- int n = 0; -+ i64 n = 0; - double r; - char *zBuf; - assert( argc==1 || argc==2 ); - if( argc==2 ){ - if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; -- n = sqlite3_value_int(argv[1]); -+ n = sqlite3_value_int64(argv[1]); - if( n>30 ) n = 30; - if( n<0 ) n = 0; - } -@@ -117463,7 +130840,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - }else if( n==0 ){ - r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5))); - }else{ -- zBuf = sqlite3_mprintf("%.*f",n,r); -+ zBuf = sqlite3_mprintf("%!.*f",(int)n,r); - if( zBuf==0 ){ - sqlite3_result_error_nomem(context); - return; -@@ -117621,9 +130998,9 @@ static void last_insert_rowid( - /* - ** Implementation of the changes() SQL function. - ** --** IMP: R-62073-11209 The changes() SQL function is a wrapper --** around the sqlite3_changes() C/C++ function and hence follows the same --** rules for counting changes. -+** IMP: R-32760-32347 The changes() SQL function is a wrapper -+** around the sqlite3_changes64() C/C++ function and hence follows the -+** same rules for counting changes. - */ - static void changes( - sqlite3_context *context, -@@ -117632,12 +131009,12 @@ static void changes( - ){ - sqlite3 *db = sqlite3_context_db_handle(context); - UNUSED_PARAMETER2(NotUsed, NotUsed2); -- sqlite3_result_int(context, sqlite3_changes(db)); -+ sqlite3_result_int64(context, sqlite3_changes64(db)); - } - - /* - ** Implementation of the total_changes() SQL function. The return value is --** the same as the sqlite3_total_changes() API function. -+** the same as the sqlite3_total_changes64() API function. - */ - static void total_changes( - sqlite3_context *context, -@@ -117646,9 +131023,9 @@ static void total_changes( - ){ - sqlite3 *db = sqlite3_context_db_handle(context); - UNUSED_PARAMETER2(NotUsed, NotUsed2); -- /* IMP: R-52756-41993 This function is a wrapper around the -- ** sqlite3_total_changes() C/C++ interface. */ -- sqlite3_result_int(context, sqlite3_total_changes(db)); -+ /* IMP: R-11217-42568 This function is a wrapper around the -+ ** sqlite3_total_changes64() C/C++ interface. */ -+ sqlite3_result_int64(context, sqlite3_total_changes64(db)); - } - - /* -@@ -117663,7 +131040,7 @@ struct compareInfo { - - /* - ** For LIKE and GLOB matching on EBCDIC machines, assume that every --** character is exactly one byte in size. Also, provde the Utf8Read() -+** character is exactly one byte in size. Also, provide the Utf8Read() - ** macro for fast reading of the next character in the common case where - ** the next character is ASCII. - */ -@@ -117744,7 +131121,8 @@ static int patternCompare( - /* Skip over multiple "*" characters in the pattern. If there - ** are also "?" characters, skip those as well, but consume a - ** single character of the input string for each "?" skipped */ -- while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){ -+ while( (c=Utf8Read(zPattern)) == matchAll -+ || (c == matchOne && matchOne!=0) ){ - if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){ - return SQLITE_NOWILDCARDMATCH; - } -@@ -117777,7 +131155,7 @@ static int patternCompare( - ** c but in the other case and search the input string for either - ** c or cx. - */ -- if( c<=0x80 ){ -+ if( c<0x80 ){ - char zStop[3]; - int bMatch; - if( noCase ){ -@@ -117860,7 +131238,13 @@ static int patternCompare( - ** non-zero if there is no match. - */ - SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){ -- return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '['); -+ if( zString==0 ){ -+ return zGlobPattern!=0; -+ }else if( zGlobPattern==0 ){ -+ return 1; -+ }else { -+ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '['); -+ } - } - - /* -@@ -117868,7 +131252,13 @@ SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){ - ** a miss - like strcmp(). - */ - SQLITE_API int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){ -- return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc); -+ if( zStr==0 ){ -+ return zPattern!=0; -+ }else if( zPattern==0 ){ -+ return 1; -+ }else{ -+ return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc); -+ } - } - - /* -@@ -117883,7 +131273,7 @@ SQLITE_API int sqlite3_like_count = 0; - - /* - ** Implementation of the like() SQL function. This function implements --** the build-in LIKE operator. The first argument to the function is the -+** the built-in LIKE operator. The first argument to the function is the - ** pattern and the second argument is the string. So, the SQL statements: - ** - ** A LIKE B -@@ -118076,39 +131466,42 @@ static const char hexdigits[] = { - }; - - /* --** Implementation of the QUOTE() function. This function takes a single --** argument. If the argument is numeric, the return value is the same as --** the argument. If the argument is NULL, the return value is the string --** "NULL". Otherwise, the argument is enclosed in single quotes with --** single-quote escapes. -+** Append to pStr text that is the SQL literal representation of the -+** value contained in pValue. - */ --static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ -- assert( argc==1 ); -- UNUSED_PARAMETER(argc); -- switch( sqlite3_value_type(argv[0]) ){ -+SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int bEscape){ -+ /* As currently implemented, the string must be initially empty. -+ ** we might relax this requirement in the future, but that will -+ ** require enhancements to the implementation. */ -+ assert( pStr!=0 && pStr->nChar==0 ); -+ -+ switch( sqlite3_value_type(pValue) ){ - case SQLITE_FLOAT: { - double r1, r2; -- char zBuf[50]; -- r1 = sqlite3_value_double(argv[0]); -- sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1); -- sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8); -- if( r1!=r2 ){ -- sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1); -+ const char *zVal; -+ r1 = sqlite3_value_double(pValue); -+ sqlite3_str_appendf(pStr, "%!0.15g", r1); -+ zVal = sqlite3_str_value(pStr); -+ if( zVal ){ -+ sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); -+ if( r1!=r2 ){ -+ sqlite3_str_reset(pStr); -+ sqlite3_str_appendf(pStr, "%!0.20e", r1); -+ } - } -- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); - break; - } - case SQLITE_INTEGER: { -- sqlite3_result_value(context, argv[0]); -+ sqlite3_str_appendf(pStr, "%lld", sqlite3_value_int64(pValue)); - break; - } - case SQLITE_BLOB: { -- char *zText = 0; -- char const *zBlob = sqlite3_value_blob(argv[0]); -- int nBlob = sqlite3_value_bytes(argv[0]); -- assert( zBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */ -- zText = (char *)contextMalloc(context, (2*(i64)nBlob)+4); -- if( zText ){ -+ char const *zBlob = sqlite3_value_blob(pValue); -+ i64 nBlob = sqlite3_value_bytes(pValue); -+ assert( zBlob==sqlite3_value_blob(pValue) ); /* No encoding change */ -+ sqlite3StrAccumEnlarge(pStr, nBlob*2 + 4); -+ if( pStr->accError==0 ){ -+ char *zText = pStr->zText; - int i; - for(i=0; i>4)&0x0F]; -@@ -118118,39 +131511,148 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ - zText[(nBlob*2)+3] = '\0'; - zText[0] = 'X'; - zText[1] = '\''; -- sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); -- sqlite3_free(zText); -+ pStr->nChar = nBlob*2 + 3; - } - break; - } - case SQLITE_TEXT: { -- int i,j; -- u64 n; -- const unsigned char *zArg = sqlite3_value_text(argv[0]); -- char *z; -- -- if( zArg==0 ) return; -- for(i=0, n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; } -- z = contextMalloc(context, ((i64)i)+((i64)n)+3); -- if( z ){ -- z[0] = '\''; -- for(i=0, j=1; zArg[i]; i++){ -- z[j++] = zArg[i]; -- if( zArg[i]=='\'' ){ -- z[j++] = '\''; -- } -- } -- z[j++] = '\''; -- z[j] = 0; -- sqlite3_result_text(context, z, j, sqlite3_free); -- } -+ const unsigned char *zArg = sqlite3_value_text(pValue); -+ sqlite3_str_appendf(pStr, bEscape ? "%#Q" : "%Q", zArg); - break; - } - default: { -- assert( sqlite3_value_type(argv[0])==SQLITE_NULL ); -- sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC); -+ assert( sqlite3_value_type(pValue)==SQLITE_NULL ); -+ sqlite3_str_append(pStr, "NULL", 4); -+ break; -+ } -+ } -+} -+ -+/* -+** Return true if z[] begins with N hexadecimal digits, and write -+** a decoding of those digits into *pVal. Or return false if any -+** one of the first N characters in z[] is not a hexadecimal digit. -+*/ -+static int isNHex(const char *z, int N, u32 *pVal){ -+ int i; -+ int v = 0; -+ for(i=0; i0 ){ -+ memmove(&zOut[j], &zIn[i], n); -+ j += n; -+ i += n; -+ } -+ if( zIn[i+1]=='\\' ){ -+ i += 2; -+ zOut[j++] = '\\'; -+ }else if( sqlite3Isxdigit(zIn[i+1]) ){ -+ if( !isNHex(&zIn[i+1], 4, &v) ) goto unistr_error; -+ i += 5; -+ j += sqlite3AppendOneUtf8Character(&zOut[j], v); -+ }else if( zIn[i+1]=='+' ){ -+ if( !isNHex(&zIn[i+2], 6, &v) ) goto unistr_error; -+ i += 8; -+ j += sqlite3AppendOneUtf8Character(&zOut[j], v); -+ }else if( zIn[i+1]=='u' ){ -+ if( !isNHex(&zIn[i+2], 4, &v) ) goto unistr_error; -+ i += 6; -+ j += sqlite3AppendOneUtf8Character(&zOut[j], v); -+ }else if( zIn[i+1]=='U' ){ -+ if( !isNHex(&zIn[i+2], 8, &v) ) goto unistr_error; -+ i += 10; -+ j += sqlite3AppendOneUtf8Character(&zOut[j], v); -+ }else{ -+ goto unistr_error; -+ } -+ } -+ zOut[j] = 0; -+ sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8); -+ return; -+ -+unistr_error: -+ sqlite3_free(zOut); -+ sqlite3_result_error(context, "invalid Unicode escape", -1); -+ return; -+} -+ -+ -+/* -+** Implementation of the QUOTE() function. -+** -+** The quote(X) function returns the text of an SQL literal which is the -+** value of its argument suitable for inclusion into an SQL statement. -+** Strings are surrounded by single-quotes with escapes on interior quotes -+** as needed. BLOBs are encoded as hexadecimal literals. Strings with -+** embedded NUL characters cannot be represented as string literals in SQL -+** and hence the returned string literal is truncated prior to the first NUL. -+** -+** If sqlite3_user_data() is non-zero, then the UNISTR_QUOTE() function is -+** implemented instead. The difference is that UNISTR_QUOTE() uses the -+** UNISTR() function to escape control characters. -+*/ -+static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ -+ sqlite3_str str; -+ sqlite3 *db = sqlite3_context_db_handle(context); -+ assert( argc==1 ); -+ UNUSED_PARAMETER(argc); -+ sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); -+ sqlite3QuoteValue(&str,argv[0],SQLITE_PTR_TO_INT(sqlite3_user_data(context))); -+ sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar, -+ SQLITE_DYNAMIC); -+ if( str.accError!=SQLITE_OK ){ -+ sqlite3_result_null(context); -+ sqlite3_result_error_code(context, str.accError); - } - } - -@@ -118207,6 +131709,7 @@ static void charFunc( - *zOut++ = 0x80 + (u8)(c & 0x3F); - } \ - } -+ *zOut = 0; - sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8); - } - -@@ -118235,10 +131738,101 @@ static void hexFunc( - *(z++) = hexdigits[c&0xf]; - } - *z = 0; -- sqlite3_result_text(context, zHex, n*2, sqlite3_free); -+ sqlite3_result_text64(context, zHex, (u64)(z-zHex), -+ sqlite3_free, SQLITE_UTF8); - } - } - -+/* -+** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr -+** contains character ch, or 0 if it does not. -+*/ -+static int strContainsChar(const u8 *zStr, int nStr, u32 ch){ -+ const u8 *zEnd = &zStr[nStr]; -+ const u8 *z = zStr; -+ while( z0 ){ -- azChar = contextMalloc(context, ((i64)nChar)*(sizeof(char*)+1)); -+ azChar = contextMalloc(context, -+ ((i64)nChar)*(sizeof(char*)+sizeof(unsigned))); - if( azChar==0 ){ - return; - } -- aLen = (unsigned char*)&azChar[nChar]; -+ aLen = (unsigned*)&azChar[nChar]; - for(z=zCharSet, nChar=0; *z; nChar++){ - azChar[nChar] = (unsigned char *)z; - SQLITE_SKIP_UTF8(z); -- aLen[nChar] = (u8)(z - azChar[nChar]); -+ aLen[nChar] = (unsigned)(z - azChar[nChar]); - } - } - } -@@ -118410,7 +132005,7 @@ static void trimFunc( - flags = SQLITE_PTR_TO_INT(sqlite3_user_data(context)); - if( flags & 1 ){ - while( nIn>0 ){ -- int len = 0; -+ unsigned int len = 0; - for(i=0; i0 ){ -- int len = 0; -+ unsigned int len = 0; - for(i=0; i0 && nSep>0 ){ -+ memcpy(&z[j], zSep, nSep); -+ j += nSep; -+ } -+ memcpy(&z[j], v, k); -+ j += k; -+ } -+ } -+ } -+ z[j] = 0; -+ assert( j<=n ); -+ sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8); -+} -+ -+/* -+** The CONCAT(...) function. Generate a string result that is the -+** concatentation of all non-null arguments. -+*/ -+static void concatFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ concatFuncCore(context, argc, argv, 0, ""); -+} -+ -+/* -+** The CONCAT_WS(separator, ...) function. -+** -+** Generate a string that is the concatenation of 2nd through the Nth -+** argument. Use the first argument (which must be non-NULL) as the -+** separator. -+*/ -+static void concatwsFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ int nSep = sqlite3_value_bytes(argv[0]); -+ const char *zSep = (const char*)sqlite3_value_text(argv[0]); -+ if( zSep==0 ) return; -+ concatFuncCore(context, argc-1, argv+1, nSep, zSep); -+} -+ - - #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION - /* - ** The "unknown" function is automatically substituted in place of - ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN --** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used. -+** when the SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION compile-time option is used. - ** When the "sqlite3" command-line shell is built using this functionality, - ** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries - ** involving application-defined functions to be examined in a generic -@@ -118455,6 +132125,9 @@ static void unknownFunc( - sqlite3_value **argv - ){ - /* no-op */ -+ (void)context; -+ (void)argc; -+ (void)argv; - } - #endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/ - -@@ -118556,13 +132229,68 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){ - */ - typedef struct SumCtx SumCtx; - struct SumCtx { -- double rSum; /* Floating point sum */ -- i64 iSum; /* Integer sum */ -+ double rSum; /* Running sum as as a double */ -+ double rErr; /* Error term for Kahan-Babushka-Neumaier summation */ -+ i64 iSum; /* Running sum as a signed integer */ - i64 cnt; /* Number of elements summed */ -- u8 overflow; /* True if integer overflow seen */ -- u8 approx; /* True if non-integer value was input to the sum */ -+ u8 approx; /* True if any non-integer value was input to the sum */ -+ u8 ovrfl; /* Integer overflow seen */ - }; - -+/* -+** Do one step of the Kahan-Babushka-Neumaier summation. -+** -+** https://en.wikipedia.org/wiki/Kahan_summation_algorithm -+** -+** Variables are marked "volatile" to defeat c89 x86 floating point -+** optimizations can mess up this algorithm. -+*/ -+static void kahanBabuskaNeumaierStep( -+ volatile SumCtx *pSum, -+ volatile double r -+){ -+ volatile double s = pSum->rSum; -+ volatile double t = s + r; -+ if( fabs(s) > fabs(r) ){ -+ pSum->rErr += (s - t) + r; -+ }else{ -+ pSum->rErr += (r - t) + s; -+ } -+ pSum->rSum = t; -+} -+ -+/* -+** Add a (possibly large) integer to the running sum. -+*/ -+static void kahanBabuskaNeumaierStepInt64(volatile SumCtx *pSum, i64 iVal){ -+ if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){ -+ i64 iBig, iSm; -+ iSm = iVal % 16384; -+ iBig = iVal - iSm; -+ kahanBabuskaNeumaierStep(pSum, iBig); -+ kahanBabuskaNeumaierStep(pSum, iSm); -+ }else{ -+ kahanBabuskaNeumaierStep(pSum, (double)iVal); -+ } -+} -+ -+/* -+** Initialize the Kahan-Babaska-Neumaier sum from a 64-bit integer -+*/ -+static void kahanBabuskaNeumaierInit( -+ volatile SumCtx *p, -+ i64 iVal -+){ -+ if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){ -+ i64 iSm = iVal % 16384; -+ p->rSum = (double)(iVal - iSm); -+ p->rErr = (double)iSm; -+ }else{ -+ p->rSum = (double)iVal; -+ p->rErr = 0.0; -+ } -+} -+ - /* - ** Routines used to compute the sum, average, and total. - ** -@@ -118570,7 +132298,7 @@ struct SumCtx { - ** that it returns NULL if it sums over no inputs. TOTAL returns - ** 0.0 in that case. In addition, TOTAL always returns a float where - ** SUM might return an integer if it never encounters a floating point --** value. TOTAL never fails, but SUM might through an exception if -+** value. TOTAL never fails, but SUM might throw an exception if - ** it overflows an integer. - */ - static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ -@@ -118582,15 +132310,29 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ - type = sqlite3_value_numeric_type(argv[0]); - if( p && type!=SQLITE_NULL ){ - p->cnt++; -- if( type==SQLITE_INTEGER ){ -- i64 v = sqlite3_value_int64(argv[0]); -- p->rSum += v; -- if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ -- p->approx = p->overflow = 1; -+ if( p->approx==0 ){ -+ if( type!=SQLITE_INTEGER ){ -+ kahanBabuskaNeumaierInit(p, p->iSum); -+ p->approx = 1; -+ kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0])); -+ }else{ -+ i64 x = p->iSum; -+ if( sqlite3AddInt64(&x, sqlite3_value_int64(argv[0]))==0 ){ -+ p->iSum = x; -+ }else{ -+ p->ovrfl = 1; -+ kahanBabuskaNeumaierInit(p, p->iSum); -+ p->approx = 1; -+ kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0])); -+ } - } - }else{ -- p->rSum += sqlite3_value_double(argv[0]); -- p->approx = 1; -+ if( type==SQLITE_INTEGER ){ -+ kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0])); -+ }else{ -+ p->ovrfl = 0; -+ kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0])); -+ } - } - } - } -@@ -118607,13 +132349,21 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){ - if( ALWAYS(p) && type!=SQLITE_NULL ){ - assert( p->cnt>0 ); - p->cnt--; -- assert( type==SQLITE_INTEGER || p->approx ); -- if( type==SQLITE_INTEGER && p->approx==0 ){ -- i64 v = sqlite3_value_int64(argv[0]); -- p->rSum -= v; -- p->iSum -= v; -+ if( !p->approx ){ -+ if( sqlite3SubInt64(&p->iSum, sqlite3_value_int64(argv[0])) ){ -+ p->ovrfl = 1; -+ p->approx = 1; -+ } -+ }else if( type==SQLITE_INTEGER ){ -+ i64 iVal = sqlite3_value_int64(argv[0]); -+ if( iVal!=SMALLEST_INT64 ){ -+ kahanBabuskaNeumaierStepInt64(p, -iVal); -+ }else{ -+ kahanBabuskaNeumaierStepInt64(p, LARGEST_INT64); -+ kahanBabuskaNeumaierStepInt64(p, 1); -+ } - }else{ -- p->rSum -= sqlite3_value_double(argv[0]); -+ kahanBabuskaNeumaierStep(p, -sqlite3_value_double(argv[0])); - } - } - } -@@ -118624,10 +132374,14 @@ static void sumFinalize(sqlite3_context *context){ - SumCtx *p; - p = sqlite3_aggregate_context(context, 0); - if( p && p->cnt>0 ){ -- if( p->overflow ){ -- sqlite3_result_error(context,"integer overflow",-1); -- }else if( p->approx ){ -- sqlite3_result_double(context, p->rSum); -+ if( p->approx ){ -+ if( p->ovrfl ){ -+ sqlite3_result_error(context,"integer overflow",-1); -+ }else if( !sqlite3IsOverflow(p->rErr) ){ -+ sqlite3_result_double(context, p->rSum+p->rErr); -+ }else{ -+ sqlite3_result_double(context, p->rSum); -+ } - }else{ - sqlite3_result_int64(context, p->iSum); - } -@@ -118637,14 +132391,29 @@ static void avgFinalize(sqlite3_context *context){ - SumCtx *p; - p = sqlite3_aggregate_context(context, 0); - if( p && p->cnt>0 ){ -- sqlite3_result_double(context, p->rSum/(double)p->cnt); -+ double r; -+ if( p->approx ){ -+ r = p->rSum; -+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; -+ }else{ -+ r = (double)(p->iSum); -+ } -+ sqlite3_result_double(context, r/(double)p->cnt); - } - } - static void totalFinalize(sqlite3_context *context){ - SumCtx *p; -+ double r = 0.0; - p = sqlite3_aggregate_context(context, 0); -- /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ -- sqlite3_result_double(context, p ? p->rSum : (double)0); -+ if( p ){ -+ if( p->approx ){ -+ r = p->rSum; -+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; -+ }else{ -+ r = (double)(p->iSum); -+ } -+ } -+ sqlite3_result_double(context, r); - } - - /* -@@ -118763,97 +132532,174 @@ static void minMaxFinalize(sqlite3_context *context){ - - /* - ** group_concat(EXPR, ?SEPARATOR?) -+** string_agg(EXPR, SEPARATOR) -+** -+** Content is accumulated in GroupConcatCtx.str with the SEPARATOR -+** coming before the EXPR value, except for the first entry which -+** omits the SEPARATOR. -+** -+** It is tragic that the SEPARATOR goes before the EXPR string. The -+** groupConcatInverse() implementation would have been easier if the -+** SEPARATOR were appended after EXPR. And the order is undocumented, -+** so we could change it, in theory. But the old behavior has been -+** around for so long that we dare not, for fear of breaking something. - */ -+typedef struct { -+ StrAccum str; /* The accumulated concatenation */ -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ int nAccum; /* Number of strings presently concatenated */ -+ int nFirstSepLength; /* Used to detect separator length change */ -+ /* If pnSepLengths!=0, refs an array of inter-string separator lengths, -+ ** stored as actually incorporated into presently accumulated result. -+ ** (Hence, its slots in use number nAccum-1 between method calls.) -+ ** If pnSepLengths==0, nFirstSepLength is the length used throughout. -+ */ -+ int *pnSepLengths; -+#endif -+} GroupConcatCtx; -+ - static void groupConcatStep( - sqlite3_context *context, - int argc, - sqlite3_value **argv - ){ - const char *zVal; -- StrAccum *pAccum; -+ GroupConcatCtx *pGCC; - const char *zSep; - int nVal, nSep; - assert( argc==1 || argc==2 ); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; -- pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum)); -- -- if( pAccum ){ -+ pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); -+ if( pGCC ){ - sqlite3 *db = sqlite3_context_db_handle(context); -- int firstTerm = pAccum->mxAlloc==0; -- pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; -- if( !firstTerm ){ -- if( argc==2 ){ -- zSep = (char*)sqlite3_value_text(argv[1]); -- nSep = sqlite3_value_bytes(argv[1]); -- }else{ -- zSep = ","; -- nSep = 1; -+ int firstTerm = pGCC->str.mxAlloc==0; -+ pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; -+ if( argc==1 ){ -+ if( !firstTerm ){ -+ sqlite3_str_appendchar(&pGCC->str, 1, ','); - } -- if( zSep ) sqlite3_str_append(pAccum, zSep, nSep); -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ else{ -+ pGCC->nFirstSepLength = 1; -+ } -+#endif -+ }else if( !firstTerm ){ -+ zSep = (char*)sqlite3_value_text(argv[1]); -+ nSep = sqlite3_value_bytes(argv[1]); -+ if( zSep ){ -+ sqlite3_str_append(&pGCC->str, zSep, nSep); -+ } -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ else{ -+ nSep = 0; -+ } -+ if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){ -+ int *pnsl = pGCC->pnSepLengths; -+ if( pnsl == 0 ){ -+ /* First separator length variation seen, start tracking them. */ -+ pnsl = (int*)sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int)); -+ if( pnsl!=0 ){ -+ int i = 0, nA = pGCC->nAccum-1; -+ while( inFirstSepLength; -+ } -+ }else{ -+ pnsl = (int*)sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int)); -+ } -+ if( pnsl!=0 ){ -+ if( ALWAYS(pGCC->nAccum>0) ){ -+ pnsl[pGCC->nAccum-1] = nSep; -+ } -+ pGCC->pnSepLengths = pnsl; -+ }else{ -+ sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM); -+ } -+ } -+#endif - } -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ else{ -+ pGCC->nFirstSepLength = sqlite3_value_bytes(argv[1]); -+ } -+ pGCC->nAccum += 1; -+#endif - zVal = (char*)sqlite3_value_text(argv[0]); - nVal = sqlite3_value_bytes(argv[0]); -- if( zVal ) sqlite3_str_append(pAccum, zVal, nVal); -+ if( zVal ) sqlite3_str_append(&pGCC->str, zVal, nVal); - } - } -+ - #ifndef SQLITE_OMIT_WINDOWFUNC - static void groupConcatInverse( - sqlite3_context *context, - int argc, - sqlite3_value **argv - ){ -- int n; -- StrAccum *pAccum; -+ GroupConcatCtx *pGCC; - assert( argc==1 || argc==2 ); -+ (void)argc; /* Suppress unused parameter warning */ - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; -- pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum)); -- /* pAccum is always non-NULL since groupConcatStep() will have always -- ** run frist to initialize it */ -- if( ALWAYS(pAccum) ){ -- n = sqlite3_value_bytes(argv[0]); -- if( argc==2 ){ -- n += sqlite3_value_bytes(argv[1]); -+ pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); -+ /* pGCC is always non-NULL since groupConcatStep() will have always -+ ** run first to initialize it */ -+ if( ALWAYS(pGCC) ){ -+ int nVS; /* Number of characters to remove */ -+ /* Must call sqlite3_value_text() to convert the argument into text prior -+ ** to invoking sqlite3_value_bytes(), in case the text encoding is UTF16 */ -+ (void)sqlite3_value_text(argv[0]); -+ nVS = sqlite3_value_bytes(argv[0]); -+ pGCC->nAccum -= 1; -+ if( pGCC->pnSepLengths!=0 ){ -+ assert(pGCC->nAccum >= 0); -+ if( pGCC->nAccum>0 ){ -+ nVS += *pGCC->pnSepLengths; -+ memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1, -+ (pGCC->nAccum-1)*sizeof(int)); -+ } - }else{ -- n++; -+ /* If removing single accumulated string, harmlessly over-do. */ -+ nVS += pGCC->nFirstSepLength; - } -- if( n>=(int)pAccum->nChar ){ -- pAccum->nChar = 0; -+ if( nVS>=(int)pGCC->str.nChar ){ -+ pGCC->str.nChar = 0; - }else{ -- pAccum->nChar -= n; -- memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar); -+ pGCC->str.nChar -= nVS; -+ memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar); -+ } -+ if( pGCC->str.nChar==0 ){ -+ pGCC->str.mxAlloc = 0; -+ sqlite3_free(pGCC->pnSepLengths); -+ pGCC->pnSepLengths = 0; - } -- if( pAccum->nChar==0 ) pAccum->mxAlloc = 0; - } - } - #else - # define groupConcatInverse 0 - #endif /* SQLITE_OMIT_WINDOWFUNC */ - static void groupConcatFinalize(sqlite3_context *context){ -- StrAccum *pAccum; -- pAccum = sqlite3_aggregate_context(context, 0); -- if( pAccum ){ -- if( pAccum->accError==SQLITE_TOOBIG ){ -- sqlite3_result_error_toobig(context); -- }else if( pAccum->accError==SQLITE_NOMEM ){ -- sqlite3_result_error_nomem(context); -- }else{ -- sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1, -- sqlite3_free); -- } -+ GroupConcatCtx *pGCC -+ = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); -+ if( pGCC ){ -+ sqlite3ResultStrAccum(context, &pGCC->str); -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ sqlite3_free(pGCC->pnSepLengths); -+#endif - } - } - #ifndef SQLITE_OMIT_WINDOWFUNC - static void groupConcatValue(sqlite3_context *context){ -- sqlite3_str *pAccum; -- pAccum = (sqlite3_str*)sqlite3_aggregate_context(context, 0); -- if( pAccum ){ -+ GroupConcatCtx *pGCC -+ = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0); -+ if( pGCC ){ -+ StrAccum *pAccum = &pGCC->str; - if( pAccum->accError==SQLITE_TOOBIG ){ - sqlite3_result_error_toobig(context); - }else if( pAccum->accError==SQLITE_NOMEM ){ - sqlite3_result_error_nomem(context); -+ }else if( pGCC->nAccum>0 && pAccum->nChar==0 ){ -+ sqlite3_result_text(context, "", 1, SQLITE_STATIC); - }else{ - const char *zText = sqlite3_str_value(pAccum); -- sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT); -+ sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT); - } - } - } -@@ -118880,8 +132726,10 @@ SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){ - ** sensitive. - */ - SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ -+ FuncDef *pDef; - struct compareInfo *pInfo; - int flags; -+ int nArg; - if( caseSensitive ){ - pInfo = (struct compareInfo*)&likeInfoAlt; - flags = SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE; -@@ -118889,10 +132737,13 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) - pInfo = (struct compareInfo*)&likeInfoNorm; - flags = SQLITE_FUNC_LIKE; - } -- 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); -- sqlite3FindFunction(db, "like", 2, SQLITE_UTF8, 0)->funcFlags |= flags; -- sqlite3FindFunction(db, "like", 3, SQLITE_UTF8, 0)->funcFlags |= flags; -+ for(nArg=2; nArg<=3; nArg++){ -+ sqlite3CreateFunc(db, "like", nArg, SQLITE_UTF8, pInfo, likeFunc, -+ 0, 0, 0, 0, 0); -+ pDef = sqlite3FindFunction(db, "like", nArg, SQLITE_UTF8, 0); -+ pDef->funcFlags |= flags; -+ pDef->funcFlags &= ~SQLITE_FUNC_UNSAFE; -+ } - } - - /* -@@ -118915,11 +132766,14 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) - SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ - FuncDef *pDef; - int nExpr; -- if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){ -+ assert( pExpr!=0 ); -+ assert( pExpr->op==TK_FUNCTION ); -+ assert( ExprUseXList(pExpr) ); -+ if( !pExpr->x.pList ){ - return 0; - } -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - nExpr = pExpr->x.pList->nExpr; -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0); - #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION - if( pDef==0 ) return 0; -@@ -118943,6 +132797,7 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas - Expr *pEscape = pExpr->x.pList->a[2].pExpr; - char *zEscape; - if( pEscape->op!=TK_STRING ) return 0; -+ assert( !ExprHasProperty(pEscape, EP_IntValue) ); - zEscape = pEscape->u.zToken; - if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; - if( zEscape[0]==aWc[0] ) return 0; -@@ -118954,6 +132809,326 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas - return 1; - } - -+/* Mathematical Constants */ -+#ifndef M_PI -+# define M_PI 3.141592653589793238462643383279502884 -+#endif -+#ifndef M_LN10 -+# define M_LN10 2.302585092994045684017991454684364208 -+#endif -+#ifndef M_LN2 -+# define M_LN2 0.693147180559945309417232121458176568 -+#endif -+ -+ -+/* Extra math functions that require linking with -lm -+*/ -+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS -+/* -+** Implementation SQL functions: -+** -+** ceil(X) -+** ceiling(X) -+** floor(X) -+** -+** The sqlite3_user_data() pointer is a pointer to the libm implementation -+** of the underlying C function. -+*/ -+static void ceilingFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ assert( argc==1 ); -+ switch( sqlite3_value_numeric_type(argv[0]) ){ -+ case SQLITE_INTEGER: { -+ sqlite3_result_int64(context, sqlite3_value_int64(argv[0])); -+ break; -+ } -+ case SQLITE_FLOAT: { -+ double (*x)(double) = (double(*)(double))sqlite3_user_data(context); -+ sqlite3_result_double(context, x(sqlite3_value_double(argv[0]))); -+ break; -+ } -+ default: { -+ break; -+ } -+ } -+} -+ -+/* -+** On some systems, ceil() and floor() are intrinsic function. You are -+** unable to take a pointer to these functions. Hence, we here wrap them -+** in our own actual functions. -+*/ -+static double xCeil(double x){ return ceil(x); } -+static double xFloor(double x){ return floor(x); } -+ -+/* -+** Some systems do not have log2() and log10() in their standard math -+** libraries. -+*/ -+#if defined(HAVE_LOG10) && HAVE_LOG10==0 -+# define log10(X) (0.4342944819032517867*log(X)) -+#endif -+#if defined(HAVE_LOG2) && HAVE_LOG2==0 -+# define log2(X) (1.442695040888963456*log(X)) -+#endif -+ -+ -+/* -+** Implementation of SQL functions: -+** -+** ln(X) - natural logarithm -+** log(X) - log X base 10 -+** log10(X) - log X base 10 -+** log(B,X) - log X base B -+*/ -+static void logFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ double x, b, ans; -+ assert( argc==1 || argc==2 ); -+ switch( sqlite3_value_numeric_type(argv[0]) ){ -+ case SQLITE_INTEGER: -+ case SQLITE_FLOAT: -+ x = sqlite3_value_double(argv[0]); -+ if( x<=0.0 ) return; -+ break; -+ default: -+ return; -+ } -+ if( argc==2 ){ -+ switch( sqlite3_value_numeric_type(argv[0]) ){ -+ case SQLITE_INTEGER: -+ case SQLITE_FLOAT: -+ b = log(x); -+ if( b<=0.0 ) return; -+ x = sqlite3_value_double(argv[1]); -+ if( x<=0.0 ) return; -+ break; -+ default: -+ return; -+ } -+ ans = log(x)/b; -+ }else{ -+ switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){ -+ case 1: -+ ans = log10(x); -+ break; -+ case 2: -+ ans = log2(x); -+ break; -+ default: -+ ans = log(x); -+ break; -+ } -+ } -+ sqlite3_result_double(context, ans); -+} -+ -+/* -+** Functions to converts degrees to radians and radians to degrees. -+*/ -+static double degToRad(double x){ return x*(M_PI/180.0); } -+static double radToDeg(double x){ return x*(180.0/M_PI); } -+ -+/* -+** Implementation of 1-argument SQL math functions: -+** -+** exp(X) - Compute e to the X-th power -+*/ -+static void math1Func( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ int type0; -+ double v0, ans; -+ double (*x)(double); -+ assert( argc==1 ); -+ type0 = sqlite3_value_numeric_type(argv[0]); -+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; -+ v0 = sqlite3_value_double(argv[0]); -+ x = (double(*)(double))sqlite3_user_data(context); -+ ans = x(v0); -+ sqlite3_result_double(context, ans); -+} -+ -+/* -+** Implementation of 2-argument SQL math functions: -+** -+** power(X,Y) - Compute X to the Y-th power -+*/ -+static void math2Func( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ int type0, type1; -+ double v0, v1, ans; -+ double (*x)(double,double); -+ assert( argc==2 ); -+ type0 = sqlite3_value_numeric_type(argv[0]); -+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; -+ type1 = sqlite3_value_numeric_type(argv[1]); -+ if( type1!=SQLITE_INTEGER && type1!=SQLITE_FLOAT ) return; -+ v0 = sqlite3_value_double(argv[0]); -+ v1 = sqlite3_value_double(argv[1]); -+ x = (double(*)(double,double))sqlite3_user_data(context); -+ ans = x(v0, v1); -+ sqlite3_result_double(context, ans); -+} -+ -+/* -+** Implementation of 0-argument pi() function. -+*/ -+static void piFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ assert( argc==0 ); -+ (void)argv; -+ sqlite3_result_double(context, M_PI); -+} -+ -+#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ -+ -+/* -+** Implementation of sign(X) function. -+*/ -+static void signFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ int type0; -+ double x; -+ UNUSED_PARAMETER(argc); -+ assert( argc==1 ); -+ type0 = sqlite3_value_numeric_type(argv[0]); -+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; -+ x = sqlite3_value_double(argv[0]); -+ sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); -+} -+ -+#ifdef SQLITE_DEBUG -+/* -+** Implementation of fpdecode(x,y,z) function. -+** -+** x is a real number that is to be decoded. y is the precision. -+** z is the maximum real precision. Return a string that shows the -+** results of the sqlite3FpDecode() function. -+** -+** Used for testing and debugging only, specifically testing and debugging -+** of the sqlite3FpDecode() function. This SQL function does not appear -+** in production builds. This function is not an API and is subject to -+** modification or removal in future versions of SQLite. -+*/ -+static void fpdecodeFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ FpDecode s; -+ double x; -+ int y, z; -+ char zBuf[100]; -+ UNUSED_PARAMETER(argc); -+ assert( argc==3 ); -+ x = sqlite3_value_double(argv[0]); -+ y = sqlite3_value_int(argv[1]); -+ z = sqlite3_value_int(argv[2]); -+ if( z<=0 ) z = 1; -+ sqlite3FpDecode(&s, x, y, z); -+ if( s.isSpecial==2 ){ -+ sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); -+ }else{ -+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP); -+ } -+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); -+} -+#endif /* SQLITE_DEBUG */ -+ -+#ifdef SQLITE_DEBUG -+/* -+** Implementation of parseuri(uri,flags) function. -+** -+** Required Arguments: -+** "uri" The URI to parse. -+** "flags" Bitmask of flags, as if to sqlite3_open_v2(). -+** -+** Additional arguments beyond the first two make calls to -+** sqlite3_uri_key() for integers and sqlite3_uri_parameter for -+** anything else. -+** -+** The result is a string showing the results of calling sqlite3ParseUri(). -+** -+** Used for testing and debugging only, specifically testing and debugging -+** of the sqlite3ParseUri() function. This SQL function does not appear -+** in production builds. This function is not an API and is subject to -+** modification or removal in future versions of SQLite. -+*/ -+static void parseuriFunc( -+ sqlite3_context *ctx, -+ int argc, -+ sqlite3_value **argv -+){ -+ sqlite3_str *pResult; -+ const char *zVfs; -+ const char *zUri; -+ unsigned int flgs; -+ int rc; -+ sqlite3_vfs *pVfs = 0; -+ char *zFile = 0; -+ char *zErr = 0; -+ -+ if( argc<2 ) return; -+ pVfs = sqlite3_vfs_find(0); -+ assert( pVfs ); -+ zVfs = pVfs->zName; -+ zUri = (const char*)sqlite3_value_text(argv[0]); -+ if( zUri==0 ) return; -+ flgs = (unsigned int)sqlite3_value_int(argv[1]); -+ rc = sqlite3ParseUri(zVfs, zUri, &flgs, &pVfs, &zFile, &zErr); -+ pResult = sqlite3_str_new(0); -+ if( pResult ){ -+ int i; -+ sqlite3_str_appendf(pResult, "rc=%d", rc); -+ sqlite3_str_appendf(pResult, ", flags=0x%x", flgs); -+ sqlite3_str_appendf(pResult, ", vfs=%Q", pVfs ? pVfs->zName: 0); -+ sqlite3_str_appendf(pResult, ", err=%Q", zErr); -+ sqlite3_str_appendf(pResult, ", file=%Q", zFile); -+ if( zFile ){ -+ const char *z = zFile; -+ z += sqlite3Strlen30(z)+1; -+ while( z[0] ){ -+ sqlite3_str_appendf(pResult, ", %Q", z); -+ z += sqlite3Strlen30(z)+1; -+ } -+ for(i=2; iu.pHash){ - int n = sqlite3Strlen30(p->zName); - int h = p->zName[0] + n; -+ assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); - printf(" %s(%d)", p->zName, h); - } - printf("\n"); -@@ -119316,7 +133542,9 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex( - */ - if( pParent->iPKey>=0 ){ - if( !zKey ) return 0; -- if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; -+ if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zCnName, zKey) ){ -+ return 0; -+ } - } - }else if( paiCol ){ - assert( nCol>1 ); -@@ -119358,11 +133586,11 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex( - /* If the index uses a collation sequence that is different from - ** the default collation sequence for the column, this index is - ** unusable. Bail out early in this case. */ -- zDfltColl = pParent->aCol[iCol].zColl; -+ zDfltColl = sqlite3ColumnColl(&pParent->aCol[iCol]); - if( !zDfltColl ) zDfltColl = sqlite3StrBINARY; - if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; - -- zIdxCol = pParent->aCol[iCol].zName; -+ zIdxCol = pParent->aCol[iCol].zCnName; - for(j=0; jaCol[j].zCol, zIdxCol)==0 ){ - if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; -@@ -119489,7 +133717,6 @@ static void fkLookupParent( - }else{ - int nCol = pFKey->nCol; - int regTemp = sqlite3GetTempRange(pParse, nCol); -- int regRec = sqlite3GetTempReg(pParse); - - sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); -@@ -119529,11 +133756,10 @@ static void fkLookupParent( - sqlite3VdbeGoto(v, iOk); - } - -- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec, -+ sqlite3VdbeAddOp4(v, OP_Affinity, regTemp, nCol, 0, - sqlite3IndexAffinityStr(pParse->db,pIdx), nCol); -- sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v); -- -- sqlite3ReleaseTempReg(pParse, regRec); -+ sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regTemp, nCol); -+ VdbeCoverage(v); - sqlite3ReleaseTempRange(pParse, regTemp, nCol); - } - } -@@ -119586,7 +133812,7 @@ static Expr *exprTableRegister( - pCol = &pTab->aCol[iCol]; - pExpr->iTable = regBase + sqlite3TableColumnToStorage(pTab,iCol) + 1; - pExpr->affExpr = pCol->affinity; -- zColl = pCol->zColl; -+ zColl = sqlite3ColumnColl(pCol); - if( zColl==0 ) zColl = db->pDfltColl->zName; - pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl); - }else{ -@@ -119609,6 +133835,7 @@ static Expr *exprTableColumn( - ){ - Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0); - if( pExpr ){ -+ assert( ExprUseYTab(pExpr) ); - pExpr->y.pTab = pTab; - pExpr->iTable = iCursor; - pExpr->iColumn = iCol; -@@ -119634,14 +133861,10 @@ static Expr *exprTableColumn( - ** Operation | FK type | Action taken - ** -------------------------------------------------------------------------- - ** DELETE immediate Increment the "immediate constraint counter". --** Or, if the ON (UPDATE|DELETE) action is RESTRICT, --** throw a "FOREIGN KEY constraint failed" exception. - ** - ** INSERT immediate Decrement the "immediate constraint counter". - ** - ** DELETE deferred Increment the "deferred constraint counter". --** Or, if the ON (UPDATE|DELETE) action is RESTRICT, --** throw a "FOREIGN KEY constraint failed" exception. - ** - ** INSERT deferred Decrement the "deferred constraint counter". - ** -@@ -119695,7 +133918,7 @@ static void fkScanChildren( - pLeft = exprTableRegister(pParse, pTab, regData, iCol); - iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; - assert( iCol>=0 ); -- zCol = pFKey->pFrom->aCol[iCol].zName; -+ zCol = pFKey->pFrom->aCol[iCol].zCnName; - pRight = sqlite3Expr(db, TK_ID, zCol); - pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); - pWhere = sqlite3ExprAnd(pParse, pWhere, pEq); -@@ -119730,7 +133953,7 @@ static void fkScanChildren( - i16 iCol = pIdx->aiColumn[i]; - assert( iCol>=0 ); - pLeft = exprTableRegister(pParse, pTab, regData, iCol); -- pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zName); -+ pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zCnName); - pEq = sqlite3PExpr(pParse, TK_IS, pLeft, pRight); - pAll = sqlite3ExprAnd(pParse, pAll, pEq); - } -@@ -119749,7 +133972,7 @@ static void fkScanChildren( - ** clause. For each row found, increment either the deferred or immediate - ** foreign key constraint counter. */ - if( pParse->nErr==0 ){ -- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); -+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0); - sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); - if( pWInfo ){ - sqlite3WhereEnd(pWInfo); -@@ -119800,6 +134023,25 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ - } - } - -+/* -+** Clear the apTrigger[] cache of CASCADE triggers for all foreign keys -+** in a particular database. This needs to happen when the schema -+** changes. -+*/ -+SQLITE_PRIVATE void sqlite3FkClearTriggerCache(sqlite3 *db, int iDb){ -+ HashElem *k; -+ Hash *pHash = &db->aDb[iDb].pSchema->tblHash; -+ for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k)){ -+ Table *pTab = sqliteHashData(k); -+ FKey *pFKey; -+ if( !IsOrdinaryTable(pTab) ) continue; -+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ -+ fkTriggerDelete(db, pFKey->apTrigger[0]); pFKey->apTrigger[0] = 0; -+ fkTriggerDelete(db, pFKey->apTrigger[1]); pFKey->apTrigger[1] = 0; -+ } -+ } -+} -+ - /* - ** This function is called to generate code that runs when table pTab is - ** being dropped from the database. The SrcList passed as the second argument -@@ -119819,12 +134061,12 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ - */ - SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ - sqlite3 *db = pParse->db; -- if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) ){ -+ if( (db->flags&SQLITE_ForeignKeys) && IsOrdinaryTable(pTab) ){ - int iSkip = 0; - Vdbe *v = sqlite3GetVdbe(pParse); - - assert( v ); /* VDBE has already been allocated */ -- assert( pTab->pSelect==0 ); /* Not a view */ -+ assert( IsOrdinaryTable(pTab) ); - if( sqlite3FkReferences(pTab)==0 ){ - /* Search for a deferred foreign key constraint for which this table - ** is the child table. If one cannot be found, return without -@@ -119832,7 +134074,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa - ** the entire DELETE if there are no outstanding deferred constraints - ** when this statement is run. */ - FKey *p; -- for(p=pTab->pFKey; p; p=p->pNextFrom){ -+ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ - if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break; - } - if( !p ) return; -@@ -119921,7 +134163,7 @@ static int fkParentIsModified( - if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){ - Column *pCol = &pTab->aCol[iKey]; - if( zKey ){ -- if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1; -+ if( 0==sqlite3StrICmp(pCol->zCnName, zKey) ) return 1; - }else if( pCol->colFlags & COLFLAG_PRIMKEY ){ - return 1; - } -@@ -119943,6 +134185,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){ - if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull) - || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull) - ){ -+ assert( (pTop->db->flags & SQLITE_FkNoAction)==0 ); - return 1; - } - } -@@ -119988,13 +134231,14 @@ SQLITE_PRIVATE void sqlite3FkCheck( - - /* If foreign-keys are disabled, this function is a no-op. */ - if( (db->flags&SQLITE_ForeignKeys)==0 ) return; -+ if( !IsOrdinaryTable(pTab) ) return; - - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - zDb = db->aDb[iDb].zDbSName; - - /* Loop through all the foreign key constraints for which pTab is the - ** child table (the table that the foreign key definition is part of). */ -- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ -+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ - Table *pTo; /* Parent table of foreign key pFKey */ - Index *pIdx = 0; /* Index on key columns in pTo */ - int *aiFree = 0; -@@ -120061,7 +134305,7 @@ SQLITE_PRIVATE void sqlite3FkCheck( - ** values read from the parent table are NULL. */ - if( db->xAuth ){ - int rcauth; -- char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; -+ char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zCnName; - rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); - bIgnore = (rcauth==SQLITE_IGNORE); - } -@@ -120125,10 +134369,10 @@ SQLITE_PRIVATE void sqlite3FkCheck( - ** child table as a SrcList for sqlite3WhereBegin() */ - pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); - if( pSrc ){ -- struct SrcList_item *pItem = pSrc->a; -- pItem->pTab = pFKey->pFrom; -+ SrcItem *pItem = pSrc->a; -+ pItem->pSTab = pFKey->pFrom; - pItem->zName = pFKey->pFrom->zName; -- pItem->pTab->nTabRef++; -+ pItem->pSTab->nTabRef++; - pItem->iCursor = pParse->nTab++; - - if( regNew!=0 ){ -@@ -120136,6 +134380,8 @@ SQLITE_PRIVATE void sqlite3FkCheck( - } - if( regOld!=0 ){ - int eAction = pFKey->aAction[aChange!=0]; -+ if( (db->flags & SQLITE_FkNoAction) ) eAction = OE_None; -+ - fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); - /* If this is a deferred FK constraint, or a CASCADE or SET NULL - ** action applies, then any foreign key violations caused by -@@ -120176,10 +134422,10 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask( - Table *pTab /* Table being modified */ - ){ - u32 mask = 0; -- if( pParse->db->flags&SQLITE_ForeignKeys ){ -+ if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ - FKey *p; - int i; -- for(p=pTab->pFKey; p; p=p->pNextFrom){ -+ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ - for(i=0; inCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); - } - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ -@@ -120213,7 +134459,9 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask( - ** - ** For an UPDATE, this function returns 2 if: - ** --** * There are any FKs for which pTab is the child and the parent table, or -+** * There are any FKs for which pTab is the child and the parent table -+** and any FK processing at all is required (even of a different FK), or -+** - ** * the UPDATE modifies one or more parent keys for which the action is - ** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL). - ** -@@ -120225,36 +134473,41 @@ SQLITE_PRIVATE int sqlite3FkRequired( - int *aChange, /* Non-NULL for UPDATE operations */ - int chngRowid /* True for UPDATE that affects rowid */ - ){ -- int eRet = 0; -- if( pParse->db->flags&SQLITE_ForeignKeys ){ -+ int eRet = 1; /* Value to return if bHaveFK is true */ -+ int bHaveFK = 0; /* If FK processing is required */ -+ if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){ - if( !aChange ){ - /* A DELETE operation. Foreign key processing is required if the - ** table in question is either the child or parent table for any - ** foreign key constraint. */ -- eRet = (sqlite3FkReferences(pTab) || pTab->pFKey); -+ bHaveFK = (sqlite3FkReferences(pTab) || pTab->u.tab.pFKey); - }else{ - /* This is an UPDATE. Foreign key processing is only required if the - ** operation modifies one or more child or parent key columns. */ - FKey *p; - - /* Check if any child key columns are being modified. */ -- for(p=pTab->pFKey; p; p=p->pNextFrom){ -- if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) return 2; -+ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ - if( fkChildIsModified(pTab, p, aChange, chngRowid) ){ -- eRet = 1; -+ if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2; -+ bHaveFK = 1; - } - } - - /* Check if any parent key columns are being modified. */ - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ - if( fkParentIsModified(pTab, p, aChange, chngRowid) ){ -- if( p->aAction[1]!=OE_None ) return 2; -- eRet = 1; -+ if( (pParse->db->flags & SQLITE_FkNoAction)==0 -+ && p->aAction[1]!=OE_None -+ ){ -+ return 2; -+ } -+ bHaveFK = 1; - } - } - } - } -- return eRet; -+ return bHaveFK ? eRet : 0; - } - - /* -@@ -120266,9 +134519,9 @@ SQLITE_PRIVATE int sqlite3FkRequired( - ** - ** It returns a pointer to a Trigger structure containing a trigger - ** equivalent to the ON UPDATE or ON DELETE action specified by pFKey. --** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is --** returned (these actions require no special handling by the triggers --** sub-system, code for them is created by fkScanChildren()). -+** If the action is "NO ACTION" then a NULL pointer is returned (these actions -+** require no special handling by the triggers sub-system, code for them is -+** created by fkScanChildren()). - ** - ** For example, if pFKey is the foreign key and pTab is table "p" in - ** the following schema: -@@ -120298,6 +134551,7 @@ static Trigger *fkActionTrigger( - int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */ - - action = pFKey->aAction[iAction]; -+ if( (db->flags & SQLITE_FkNoAction) ) action = OE_None; - if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){ - return 0; - } -@@ -120331,8 +134585,8 @@ static Trigger *fkActionTrigger( - assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKeynCol) ); - assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); - sqlite3TokenInit(&tToCol, -- pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName); -- sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName); -+ pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zCnName); -+ sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zCnName); - - /* Create the expression "OLD.zToCol = zFromCol". It is important - ** that the "OLD.zToCol" term is on the LHS of the = operator, so -@@ -120377,7 +134631,7 @@ static Trigger *fkActionTrigger( - testcase( pCol->colFlags & COLFLAG_STORED ); - pDflt = 0; - }else{ -- pDflt = pCol->pDflt; -+ pDflt = sqlite3ColumnExpr(pFKey->pFrom, pCol); - } - if( pDflt ){ - pNew = sqlite3ExprDup(db, pDflt, 0); -@@ -120397,18 +134651,25 @@ static Trigger *fkActionTrigger( - nFrom = sqlite3Strlen30(zFrom); - - if( action==OE_Restrict ){ -- Token tFrom; -+ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); -+ SrcList *pSrc; - Expr *pRaise; - -- tFrom.z = zFrom; -- tFrom.n = nFrom; -- pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); -+ pRaise = sqlite3Expr(db, TK_STRING, "FOREIGN KEY constraint failed"), -+ pRaise = sqlite3PExpr(pParse, TK_RAISE, pRaise, 0); - if( pRaise ){ - pRaise->affExpr = OE_Abort; - } -+ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); -+ if( pSrc ){ -+ assert( pSrc->nSrc==1 ); -+ pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom); -+ assert( pSrc->a[0].fg.fixedSchema==0 && pSrc->a[0].fg.isSubquery==0 ); -+ pSrc->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); -+ } - pSelect = sqlite3SelectNew(pParse, - sqlite3ExprListAppend(pParse, 0, pRaise), -- sqlite3SrcListAppend(pParse, 0, &tFrom, 0), -+ pSrc, - pWhere, - 0, 0, 0, 0, 0 - ); -@@ -120514,18 +134775,18 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){ - FKey *pFKey; /* Iterator variable */ - FKey *pNext; /* Copy of pFKey->pNextFrom */ - -- assert( db==0 || IsVirtual(pTab) -- || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); -- for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ -+ assert( IsOrdinaryTable(pTab) ); -+ assert( db!=0 ); -+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pNext){ -+ assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); - - /* Remove the FK from the fkeyHash hash table. */ -- if( !db || db->pnBytesFreed==0 ){ -+ if( db->pnBytesFreed==0 ){ - if( pFKey->pPrevTo ){ - pFKey->pPrevTo->pNextTo = pFKey->pNextTo; - }else{ -- void *p = (void *)pFKey->pNextTo; -- const char *z = (p ? pFKey->pNextTo->zTo : pFKey->zTo); -- sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, p); -+ const char *z = (pFKey->pNextTo ? pFKey->pNextTo->zTo : pFKey->zTo); -+ sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, pFKey->pNextTo); - } - if( pFKey->pNextTo ){ - pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; -@@ -120585,17 +134846,20 @@ SQLITE_PRIVATE void sqlite3OpenTable( - ){ - Vdbe *v; - assert( !IsVirtual(pTab) ); -- v = sqlite3GetVdbe(pParse); -+ assert( pParse->pVdbe!=0 ); -+ v = pParse->pVdbe; - assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); -- sqlite3TableLock(pParse, iDb, pTab->tnum, -- (opcode==OP_OpenWrite)?1:0, pTab->zName); -+ if( !pParse->db->noSharedCache ){ -+ sqlite3TableLock(pParse, iDb, pTab->tnum, -+ (opcode==OP_OpenWrite)?1:0, pTab->zName); -+ } - if( HasRowid(pTab) ){ - sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nNVCol); - VdbeComment((v, "%s", pTab->zName)); - }else{ - Index *pPk = sqlite3PrimaryKeyIndex(pTab); - assert( pPk!=0 ); -- assert( pPk->tnum==pTab->tnum ); -+ assert( pPk->tnum==pTab->tnum || CORRUPT_DB ); - sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pPk); - VdbeComment((v, "%s", pTab->zName)); -@@ -120622,85 +134886,139 @@ SQLITE_PRIVATE void sqlite3OpenTable( - ** is managed along with the rest of the Index structure. It will be - ** released when sqlite3DeleteIndex() is called. - */ --SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ -+static SQLITE_NOINLINE const char *computeIndexAffStr(sqlite3 *db, Index *pIdx){ -+ /* The first time a column affinity string for a particular index is -+ ** required, it is allocated and populated here. It is then stored as -+ ** a member of the Index structure for subsequent use. -+ ** -+ ** The column affinity string will eventually be deleted by -+ ** sqliteDeleteIndex() when the Index structure itself is cleaned -+ ** up. -+ */ -+ int n; -+ Table *pTab = pIdx->pTable; -+ pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1); - if( !pIdx->zColAff ){ -- /* The first time a column affinity string for a particular index is -- ** required, it is allocated and populated here. It is then stored as -- ** a member of the Index structure for subsequent use. -- ** -- ** The column affinity string will eventually be deleted by -- ** sqliteDeleteIndex() when the Index structure itself is cleaned -- ** up. -- */ -- int n; -- Table *pTab = pIdx->pTable; -- pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1); -- if( !pIdx->zColAff ){ -- sqlite3OomFault(db); -- return 0; -+ sqlite3OomFault(db); -+ return 0; -+ } -+ for(n=0; nnColumn; n++){ -+ i16 x = pIdx->aiColumn[n]; -+ char aff; -+ if( x>=0 ){ -+ aff = pTab->aCol[x].affinity; -+ }else if( x==XN_ROWID ){ -+ aff = SQLITE_AFF_INTEGER; -+ }else{ -+ assert( x==XN_EXPR ); -+ assert( pIdx->bHasExpr ); -+ assert( pIdx->aColExpr!=0 ); -+ aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); - } -- for(n=0; nnColumn; n++){ -- i16 x = pIdx->aiColumn[n]; -- char aff; -- if( x>=0 ){ -- aff = pTab->aCol[x].affinity; -- }else if( x==XN_ROWID ){ -- aff = SQLITE_AFF_INTEGER; -- }else{ -- assert( x==XN_EXPR ); -- assert( pIdx->aColExpr!=0 ); -- aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); -+ if( affSQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC; -+ pIdx->zColAff[n] = aff; -+ } -+ pIdx->zColAff[n] = 0; -+ return pIdx->zColAff; -+} -+SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ -+ if( !pIdx->zColAff ) return computeIndexAffStr(db, pIdx); -+ return pIdx->zColAff; -+} -+ -+ -+/* -+** Compute an affinity string for a table. Space is obtained -+** from sqlite3DbMalloc(). The caller is responsible for freeing -+** the space when done. -+*/ -+SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3 *db, const Table *pTab){ -+ char *zColAff; -+ zColAff = (char *)sqlite3DbMallocRaw(db, pTab->nCol+1); -+ if( zColAff ){ -+ int i, j; -+ for(i=j=0; inCol; i++){ -+ if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ -+ zColAff[j++] = pTab->aCol[i].affinity; - } -- if( affSQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC; -- pIdx->zColAff[n] = aff; - } -- pIdx->zColAff[n] = 0; -+ do{ -+ zColAff[j--] = 0; -+ }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB ); - } -- -- return pIdx->zColAff; -+ return zColAff; - } - - /* -+** Make changes to the evolving bytecode to do affinity transformations -+** of values that are about to be gathered into a row for table pTab. -+** -+** For ordinary (legacy, non-strict) tables: -+** ----------------------------------------- -+** - ** Compute the affinity string for table pTab, if it has not already been - ** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities. - ** --** If the affinity exists (if it is no entirely SQLITE_AFF_BLOB values) and --** if iReg>0 then code an OP_Affinity opcode that will set the affinities --** for register iReg and following. Or if affinities exists and iReg==0, -+** If the affinity string is empty (because it was all SQLITE_AFF_BLOB entries -+** which were then optimized out) then this routine becomes a no-op. -+** -+** Otherwise if iReg>0 then code an OP_Affinity opcode that will set the -+** affinities for register iReg and following. Or if iReg==0, - ** then just set the P4 operand of the previous opcode (which should be - ** an OP_MakeRecord) to the affinity string. - ** - ** A column affinity string has one character per column: - ** --** Character Column affinity --** ------------------------------ --** 'A' BLOB --** 'B' TEXT --** 'C' NUMERIC --** 'D' INTEGER --** 'E' REAL -+** Character Column affinity -+** --------- --------------- -+** 'A' BLOB -+** 'B' TEXT -+** 'C' NUMERIC -+** 'D' INTEGER -+** 'E' REAL -+** -+** For STRICT tables: -+** ------------------ -+** -+** Generate an appropriate OP_TypeCheck opcode that will verify the -+** datatypes against the column definitions in pTab. If iReg==0, that -+** means an OP_MakeRecord opcode has already been generated and should be -+** the last opcode generated. The new OP_TypeCheck needs to be inserted -+** before the OP_MakeRecord. The new OP_TypeCheck should use the same -+** register set as the OP_MakeRecord. If iReg>0 then register iReg is -+** the first of a series of registers that will form the new record. -+** Apply the type checking to that array of registers. - */ - SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ -- int i, j; -- char *zColAff = pTab->zColAff; -+ int i; -+ char *zColAff; -+ if( pTab->tabFlags & TF_Strict ){ -+ if( iReg==0 ){ -+ /* Move the previous opcode (which should be OP_MakeRecord) forward -+ ** by one slot and insert a new OP_TypeCheck where the current -+ ** OP_MakeRecord is found */ -+ VdbeOp *pPrev; -+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE); -+ pPrev = sqlite3VdbeGetLastOp(v); -+ assert( pPrev!=0 ); -+ assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed ); -+ pPrev->opcode = OP_TypeCheck; -+ sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3); -+ }else{ -+ /* Insert an isolated OP_Typecheck */ -+ sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol); -+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE); -+ } -+ return; -+ } -+ zColAff = pTab->zColAff; - if( zColAff==0 ){ -- sqlite3 *db = sqlite3VdbeDb(v); -- zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1); -+ zColAff = sqlite3TableAffinityStr(0, pTab); - if( !zColAff ){ -- sqlite3OomFault(db); -+ sqlite3OomFault(sqlite3VdbeDb(v)); - return; - } -- -- for(i=j=0; inCol; i++){ -- assert( pTab->aCol[i].affinity!=0 ); -- if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ -- zColAff[j++] = pTab->aCol[i].affinity; -- } -- } -- do{ -- zColAff[j--] = 0; -- }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB ); - pTab->zColAff = zColAff; - } - assert( zColAff!=0 ); -@@ -120709,6 +135027,8 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ - if( iReg ){ - sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); - }else{ -+ assert( sqlite3VdbeGetLastOp(v)->opcode==OP_MakeRecord -+ || sqlite3VdbeDb(v)->mallocFailed ); - sqlite3VdbeChangeP4(v, -1, zColAff, i); - } - } -@@ -120792,24 +135112,30 @@ SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns( - ** that appropriate affinity has been applied to the regular columns - */ - sqlite3TableAffinity(pParse->pVdbe, pTab, iRegStore); -- if( (pTab->tabFlags & TF_HasStored)!=0 -- && (pOp = sqlite3VdbeGetOp(pParse->pVdbe,-1))->opcode==OP_Affinity -- ){ -- /* Change the OP_Affinity argument to '@' (NONE) for all stored -- ** columns. '@' is the no-op affinity and those columns have not -- ** yet been computed. */ -- int ii, jj; -- char *zP4 = pOp->p4.z; -- assert( zP4!=0 ); -- assert( pOp->p4type==P4_DYNAMIC ); -- for(ii=jj=0; zP4[jj]; ii++){ -- if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ -- continue; -- } -- if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ -- zP4[jj] = SQLITE_AFF_NONE; -+ if( (pTab->tabFlags & TF_HasStored)!=0 ){ -+ pOp = sqlite3VdbeGetLastOp(pParse->pVdbe); -+ if( pOp->opcode==OP_Affinity ){ -+ /* Change the OP_Affinity argument to '@' (NONE) for all stored -+ ** columns. '@' is the no-op affinity and those columns have not -+ ** yet been computed. */ -+ int ii, jj; -+ char *zP4 = pOp->p4.z; -+ assert( zP4!=0 ); -+ assert( pOp->p4type==P4_DYNAMIC ); -+ for(ii=jj=0; zP4[jj]; ii++){ -+ if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){ -+ continue; -+ } -+ if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){ -+ zP4[jj] = SQLITE_AFF_NONE; -+ } -+ jj++; - } -- jj++; -+ }else if( pOp->opcode==OP_TypeCheck ){ -+ /* If an OP_TypeCheck was generated because the table is STRICT, -+ ** then set the P3 operand to indicate that generated columns should -+ ** not be checked */ -+ pOp->p3 = 1; - } - } - -@@ -120845,7 +135171,7 @@ SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns( - int x; - pCol->colFlags |= COLFLAG_BUSY; - w.eCode = 0; -- sqlite3WalkExpr(&w, pCol->pDflt); -+ sqlite3WalkExpr(&w, sqlite3ColumnExpr(pTab, pCol)); - pCol->colFlags &= ~COLFLAG_BUSY; - if( w.eCode & COLFLAG_NOTAVAIL ){ - pRedo = pCol; -@@ -120854,13 +135180,13 @@ SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns( - eProgress = 1; - assert( pCol->colFlags & COLFLAG_GENERATED ); - x = sqlite3TableColumnToStorage(pTab, i) + iRegStore; -- sqlite3ExprCodeGeneratedColumn(pParse, pCol, x); -+ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, x); - pCol->colFlags &= ~COLFLAG_NOTAVAIL; - } - } - }while( pRedo && eProgress ); - if( pRedo ){ -- sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zName); -+ sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zCnName); - } - pParse->iSelfTab = 0; - } -@@ -120910,7 +135236,7 @@ static int autoIncBegin( - ** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */ - if( pSeqTab==0 - || !HasRowid(pSeqTab) -- || IsVirtual(pSeqTab) -+ || NEVER(IsVirtual(pSeqTab)) - || pSeqTab->nCol!=2 - ){ - pParse->nErr++; -@@ -120922,7 +135248,9 @@ static int autoIncBegin( - while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } - if( pInfo==0 ){ - pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo)); -- if( pInfo==0 ) return 0; -+ sqlite3ParserAddCleanup(pToplevel, sqlite3DbFree, pInfo); -+ testcase( pParse->earlyCleanup ); -+ if( pParse->db->mallocFailed ) return 0; - pInfo->pNext = pToplevel->pAinc; - pToplevel->pAinc = pInfo; - pInfo->pTab = pTab; -@@ -121063,6 +135391,210 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ - # define autoIncStep(A,B,C) - #endif /* SQLITE_OMIT_AUTOINCREMENT */ - -+/* -+** If argument pVal is a Select object returned by an sqlite3MultiValues() -+** that was able to use the co-routine optimization, finish coding the -+** co-routine. -+*/ -+SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ -+ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ -+ SrcItem *pItem = &pVal->pSrc->a[0]; -+ assert( (pItem->fg.isSubquery && pItem->u4.pSubq!=0) || pParse->nErr ); -+ if( pItem->fg.isSubquery ){ -+ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->u4.pSubq->regReturn); -+ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->u4.pSubq->addrFillSub - 1); -+ } -+ } -+} -+ -+/* -+** Return true if all expressions in the expression-list passed as the -+** only argument are constant. -+*/ -+static int exprListIsConstant(Parse *pParse, ExprList *pRow){ -+ int ii; -+ for(ii=0; iinExpr; ii++){ -+ if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0; -+ } -+ return 1; -+} -+ -+/* -+** Return true if all expressions in the expression-list passed as the -+** only argument are both constant and have no affinity. -+*/ -+static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){ -+ int ii; -+ if( exprListIsConstant(pParse,pRow)==0 ) return 0; -+ for(ii=0; iinExpr; ii++){ -+ Expr *pExpr = pRow->a[ii].pExpr; -+ assert( pExpr->op!=TK_RAISE ); -+ assert( pExpr->affExpr==0 ); -+ if( 0!=sqlite3ExprAffinity(pExpr) ) return 0; -+ } -+ return 1; -+ -+} -+ -+/* -+** This function is called by the parser for the second and subsequent -+** rows of a multi-row VALUES clause. Argument pLeft is the part of -+** the VALUES clause already parsed, argument pRow is the vector of values -+** for the new row. The Select object returned represents the complete -+** VALUES clause, including the new row. -+** -+** There are two ways in which this may be achieved - by incremental -+** coding of a co-routine (the "co-routine" method) or by returning a -+** Select object equivalent to the following (the "UNION ALL" method): -+** -+** "pLeft UNION ALL SELECT pRow" -+** -+** If the VALUES clause contains a lot of rows, this compound Select -+** object may consume a lot of memory. -+** -+** When the co-routine method is used, each row that will be returned -+** by the VALUES clause is coded into part of a co-routine as it is -+** passed to this function. The returned Select object is equivalent to: -+** -+** SELECT * FROM ( -+** Select object to read co-routine -+** ) -+** -+** The co-routine method is used in most cases. Exceptions are: -+** -+** a) If the current statement has a WITH clause. This is to avoid -+** statements like: -+** -+** WITH cte AS ( VALUES('x'), ('y') ... ) -+** SELECT * FROM cte AS a, cte AS b; -+** -+** This will not work, as the co-routine uses a hard-coded register -+** for its OP_Yield instructions, and so it is not possible for two -+** cursors to iterate through it concurrently. -+** -+** b) The schema is currently being parsed (i.e. the VALUES clause is part -+** of a schema item like a VIEW or TRIGGER). In this case there is no VM -+** being generated when parsing is taking place, and so generating -+** a co-routine is not possible. -+** -+** c) There are non-constant expressions in the VALUES clause (e.g. -+** the VALUES clause is part of a correlated sub-query). -+** -+** d) One or more of the values in the first row of the VALUES clause -+** has an affinity (i.e. is a CAST expression). This causes problems -+** because the complex rules SQLite uses (see function -+** sqlite3SubqueryColumnTypes() in select.c) to determine the effective -+** affinity of such a column for all rows require access to all values in -+** the column simultaneously. -+*/ -+SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){ -+ -+ if( pParse->bHasWith /* condition (a) above */ -+ || pParse->db->init.busy /* condition (b) above */ -+ || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */ -+ || (pLeft->pSrc->nSrc==0 && -+ exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */ -+ || IN_SPECIAL_PARSE -+ ){ -+ /* The co-routine method cannot be used. Fall back to UNION ALL. */ -+ Select *pSelect = 0; -+ int f = SF_Values | SF_MultiValue; -+ if( pLeft->pSrc->nSrc ){ -+ sqlite3MultiValuesEnd(pParse, pLeft); -+ f = SF_Values; -+ }else if( pLeft->pPrior ){ -+ /* In this case set the SF_MultiValue flag only if it was set on pLeft */ -+ f = (f & pLeft->selFlags); -+ } -+ pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); -+ pLeft->selFlags &= ~(u32)SF_MultiValue; -+ if( pSelect ){ -+ pSelect->op = TK_ALL; -+ pSelect->pPrior = pLeft; -+ pLeft = pSelect; -+ } -+ }else{ -+ SrcItem *p = 0; /* SrcItem that reads from co-routine */ -+ -+ if( pLeft->pSrc->nSrc==0 ){ -+ /* Co-routine has not yet been started and the special Select object -+ ** that accesses the co-routine has not yet been created. This block -+ ** does both those things. */ -+ Vdbe *v = sqlite3GetVdbe(pParse); -+ Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0); -+ -+ /* Ensure the database schema has been read. This is to ensure we have -+ ** the correct text encoding. */ -+ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){ -+ sqlite3ReadSchema(pParse); -+ } -+ -+ if( pRet ){ -+ SelectDest dest; -+ Subquery *pSubq; -+ pRet->pSrc->nSrc = 1; -+ pRet->pPrior = pLeft->pPrior; -+ pRet->op = pLeft->op; -+ if( pRet->pPrior ) pRet->selFlags |= SF_Values; -+ pLeft->pPrior = 0; -+ pLeft->op = TK_SELECT; -+ assert( pLeft->pNext==0 ); -+ assert( pRet->pNext==0 ); -+ p = &pRet->pSrc->a[0]; -+ p->fg.viaCoroutine = 1; -+ p->iCursor = -1; -+ assert( !p->fg.isIndexedBy && !p->fg.isTabFunc ); -+ p->u1.nRow = 2; -+ if( sqlite3SrcItemAttachSubquery(pParse, p, pLeft, 0) ){ -+ pSubq = p->u4.pSubq; -+ pSubq->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; -+ pSubq->regReturn = ++pParse->nMem; -+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, -+ pSubq->regReturn, 0, pSubq->addrFillSub); -+ sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); -+ -+ /* Allocate registers for the output of the co-routine. Do so so -+ ** that there are two unused registers immediately before those -+ ** used by the co-routine. This allows the code in sqlite3Insert() -+ ** to use these registers directly, instead of copying the output -+ ** of the co-routine to a separate array for processing. */ -+ dest.iSdst = pParse->nMem + 3; -+ dest.nSdst = pLeft->pEList->nExpr; -+ pParse->nMem += 2 + dest.nSdst; -+ -+ pLeft->selFlags |= SF_MultiValue; -+ sqlite3Select(pParse, pLeft, &dest); -+ pSubq->regResult = dest.iSdst; -+ assert( pParse->nErr || dest.iSdst>0 ); -+ } -+ pLeft = pRet; -+ } -+ }else{ -+ p = &pLeft->pSrc->a[0]; -+ assert( !p->fg.isTabFunc && !p->fg.isIndexedBy ); -+ p->u1.nRow++; -+ } -+ -+ if( pParse->nErr==0 ){ -+ Subquery *pSubq; -+ assert( p!=0 ); -+ assert( p->fg.isSubquery ); -+ pSubq = p->u4.pSubq; -+ assert( pSubq!=0 ); -+ assert( pSubq->pSelect!=0 ); -+ assert( pSubq->pSelect->pEList!=0 ); -+ if( pSubq->pSelect->pEList->nExpr!=pRow->nExpr ){ -+ sqlite3SelectWrongNumTermsError(pParse, pSubq->pSelect); -+ }else{ -+ sqlite3ExprCodeExprList(pParse, pRow, pSubq->regResult, 0, 0); -+ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, pSubq->regReturn); -+ } -+ } -+ sqlite3ExprListDelete(pParse->db, pRow); -+ } -+ -+ return pLeft; -+} - - /* Forward declaration */ - static int xferOptimization( -@@ -121209,6 +135741,7 @@ SQLITE_PRIVATE void sqlite3Insert( - int regRowid; /* registers holding insert rowid */ - int regData; /* register holding first column to insert */ - int *aRegIdx = 0; /* One register allocated to each index */ -+ int *aTabColMap = 0; /* Mapping from pTab columns to pCol entries */ - - #ifndef SQLITE_OMIT_TRIGGER - int isView; /* True if attempting to insert into a view */ -@@ -121217,9 +135750,11 @@ SQLITE_PRIVATE void sqlite3Insert( - #endif - - db = pParse->db; -- if( pParse->nErr || db->mallocFailed ){ -+ assert( db->pParse==pParse ); -+ if( pParse->nErr ){ - goto insert_cleanup; - } -+ assert( db->mallocFailed==0 ); - dest.iSDParm = 0; /* Suppress a harmless compiler warning */ - - /* If the Select object is really just a simple VALUES() list with a -@@ -121253,7 +135788,7 @@ SQLITE_PRIVATE void sqlite3Insert( - */ - #ifndef SQLITE_OMIT_TRIGGER - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); -- isView = pTab->pSelect!=0; -+ isView = IsView(pTab); - #else - # define pTrigger 0 - # define tmask 0 -@@ -121265,6 +135800,14 @@ SQLITE_PRIVATE void sqlite3Insert( - #endif - assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); - -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x10000 ){ -+ sqlite3TreeViewLine(0, "In sqlite3Insert() at %s:%d", __FILE__, __LINE__); -+ sqlite3TreeViewInsert(pParse->pWith, pTabList, pColumn, pSelect, pList, -+ onError, pUpsert, pTrigger); -+ } -+#endif -+ - /* If pTab is really a view, make sure it has been initialized. - ** ViewGetColumnNames() is a no-op if pTab is not a view. - */ -@@ -121274,7 +135817,7 @@ SQLITE_PRIVATE void sqlite3Insert( - - /* Cannot insert into a read-only table. - */ -- if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ -+ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ - goto insert_cleanup; - } - -@@ -121295,7 +135838,11 @@ SQLITE_PRIVATE void sqlite3Insert( - ** - ** This is the 2nd template. - */ -- if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ -+ if( pColumn==0 -+ && pSelect!=0 -+ && pTrigger==0 -+ && xferOptimization(pParse, pTab, pSelect, onError, iDb) -+ ){ - assert( !pTrigger ); - assert( pList==0 ); - goto insert_end; -@@ -121339,35 +135886,31 @@ SQLITE_PRIVATE void sqlite3Insert( - */ - bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0; - if( pColumn ){ -+ aTabColMap = sqlite3DbMallocZero(db, pTab->nCol*sizeof(int)); -+ if( aTabColMap==0 ) goto insert_cleanup; - for(i=0; inId; i++){ -- pColumn->a[i].idx = -1; -- } -- for(i=0; inId; i++){ -- for(j=0; jnCol; j++){ -- if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ -- pColumn->a[i].idx = j; -- if( i!=j ) bIdListInOrder = 0; -- if( j==pTab->iPKey ){ -- ipkColumn = i; assert( !withoutRowid ); -- } -+ j = sqlite3ColumnIndex(pTab, pColumn->a[i].zName); -+ if( j>=0 ){ -+ if( aTabColMap[j]==0 ) aTabColMap[j] = i+1; -+ if( i!=j ) bIdListInOrder = 0; -+ if( j==pTab->iPKey ){ -+ ipkColumn = i; assert( !withoutRowid ); -+ } - #ifndef SQLITE_OMIT_GENERATED_COLUMNS -- if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ -- sqlite3ErrorMsg(pParse, -- "cannot INSERT into generated column \"%s\"", -- pTab->aCol[j].zName); -- goto insert_cleanup; -- } --#endif -- break; -+ if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ -+ sqlite3ErrorMsg(pParse, -+ "cannot INSERT into generated column \"%s\"", -+ pTab->aCol[j].zCnName); -+ goto insert_cleanup; - } -- } -- if( j>=pTab->nCol ){ -+#endif -+ }else{ - if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){ - ipkColumn = i; - bIdListInOrder = 0; - }else{ - sqlite3ErrorMsg(pParse, "table %S has no column named %s", -- pTabList, 0, pColumn->a[i].zName); -+ pTabList->a, pColumn->a[i].zName); - pParse->checkSchema = 1; - goto insert_cleanup; - } -@@ -121383,23 +135926,45 @@ SQLITE_PRIVATE void sqlite3Insert( - if( pSelect ){ - /* Data is coming from a SELECT or from a multi-row VALUES clause. - ** Generate a co-routine to run the SELECT. */ -- int regYield; /* Register holding co-routine entry-point */ -- int addrTop; /* Top of the co-routine */ - int rc; /* Result code */ - -- regYield = ++pParse->nMem; -- addrTop = sqlite3VdbeCurrentAddr(v) + 1; -- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); -- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); -- dest.iSdst = bIdListInOrder ? regData : 0; -- dest.nSdst = pTab->nCol; -- rc = sqlite3Select(pParse, pSelect, &dest); -- regFromSelect = dest.iSdst; -- if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; -- sqlite3VdbeEndCoroutine(v, regYield); -- sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ -- assert( pSelect->pEList ); -- nColumn = pSelect->pEList->nExpr; -+ if( pSelect->pSrc->nSrc==1 -+ && pSelect->pSrc->a[0].fg.viaCoroutine -+ && pSelect->pPrior==0 -+ ){ -+ SrcItem *pItem = &pSelect->pSrc->a[0]; -+ Subquery *pSubq; -+ assert( pItem->fg.isSubquery ); -+ pSubq = pItem->u4.pSubq; -+ dest.iSDParm = pSubq->regReturn; -+ regFromSelect = pSubq->regResult; -+ assert( pSubq->pSelect!=0 ); -+ assert( pSubq->pSelect->pEList!=0 ); -+ nColumn = pSubq->pSelect->pEList->nExpr; -+ ExplainQueryPlan((pParse, 0, "SCAN %S", pItem)); -+ if( bIdListInOrder && nColumn==pTab->nCol ){ -+ regData = regFromSelect; -+ regRowid = regData - 1; -+ regIns = regRowid - (IsVirtual(pTab) ? 1 : 0); -+ } -+ }else{ -+ int addrTop; /* Top of the co-routine */ -+ int regYield = ++pParse->nMem; -+ addrTop = sqlite3VdbeCurrentAddr(v) + 1; -+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); -+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); -+ dest.iSdst = bIdListInOrder ? regData : 0; -+ dest.nSdst = pTab->nCol; -+ rc = sqlite3Select(pParse, pSelect, &dest); -+ regFromSelect = dest.iSdst; -+ assert( db->pParse==pParse ); -+ if( rc || pParse->nErr ) goto insert_cleanup; -+ assert( db->mallocFailed==0 ); -+ sqlite3VdbeEndCoroutine(v, regYield); -+ sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ -+ assert( pSelect->pEList ); -+ nColumn = pSelect->pEList->nExpr; -+ } - - /* Set useTempTable to TRUE if the result of the SELECT statement - ** should be written into a temporary table (template 4). Set to -@@ -121480,19 +136045,24 @@ SQLITE_PRIVATE void sqlite3Insert( - } - } - #endif -- } - -- /* Make sure the number of columns in the source data matches the number -- ** of columns to be inserted into the table. -- */ -- for(i=0; inCol; i++){ -- if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++; -- } -- if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){ -- sqlite3ErrorMsg(pParse, -- "table %S has %d columns but %d values were supplied", -- pTabList, 0, pTab->nCol-nHidden, nColumn); -- goto insert_cleanup; -+ /* Make sure the number of columns in the source data matches the number -+ ** of columns to be inserted into the table. -+ */ -+ assert( TF_HasHidden==COLFLAG_HIDDEN ); -+ assert( TF_HasGenerated==COLFLAG_GENERATED ); -+ assert( COLFLAG_NOINSERT==(COLFLAG_GENERATED|COLFLAG_HIDDEN) ); -+ if( (pTab->tabFlags & (TF_HasGenerated|TF_HasHidden))!=0 ){ -+ for(i=0; inCol; i++){ -+ if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++; -+ } -+ } -+ if( nColumn!=(pTab->nCol-nHidden) ){ -+ sqlite3ErrorMsg(pParse, -+ "table %S has %d columns but %d values were supplied", -+ pTabList->a, pTab->nCol-nHidden, nColumn); -+ goto insert_cleanup; -+ } - } - if( pColumn!=0 && nColumn!=pColumn->nId ){ - sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); -@@ -121504,6 +136074,7 @@ SQLITE_PRIVATE void sqlite3Insert( - if( (db->flags & SQLITE_CountRows)!=0 - && !pParse->nested - && !pParse->pTriggerTab -+ && !pParse->bReturning - ){ - regRowCount = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); -@@ -121527,12 +136098,13 @@ SQLITE_PRIVATE void sqlite3Insert( - } - #ifndef SQLITE_OMIT_UPSERT - if( pUpsert ){ -+ Upsert *pNx; - if( IsVirtual(pTab) ){ - sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", - pTab->zName); - goto insert_cleanup; - } -- if( pTab->pSelect ){ -+ if( IsView(pTab) ){ - sqlite3ErrorMsg(pParse, "cannot UPSERT a view"); - goto insert_cleanup; - } -@@ -121540,13 +136112,19 @@ SQLITE_PRIVATE void sqlite3Insert( - goto insert_cleanup; - } - pTabList->a[0].iCursor = iDataCur; -- pUpsert->pUpsertSrc = pTabList; -- pUpsert->regData = regData; -- pUpsert->iDataCur = iDataCur; -- pUpsert->iIdxCur = iIdxCur; -- if( pUpsert->pUpsertTarget ){ -- sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert); -- } -+ pNx = pUpsert; -+ do{ -+ pNx->pUpsertSrc = pTabList; -+ pNx->regData = regData; -+ pNx->iDataCur = iDataCur; -+ pNx->iIdxCur = iIdxCur; -+ if( pNx->pUpsertTarget ){ -+ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){ -+ goto insert_cleanup; -+ } -+ } -+ pNx = pNx->pNextUpsert; -+ }while( pNx!=0 ); - } - #endif - -@@ -121624,23 +136202,30 @@ SQLITE_PRIVATE void sqlite3Insert( - continue; - }else if( pColumn==0 ){ - /* Hidden columns that are not explicitly named in the INSERT -- ** get there default value */ -- sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); -+ ** get their default value */ -+ sqlite3ExprCodeFactorable(pParse, -+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), -+ iRegStore); - continue; - } - } - if( pColumn ){ -- for(j=0; jnId && pColumn->a[j].idx!=i; j++){} -- if( j>=pColumn->nId ){ -+ j = aTabColMap[i]; -+ assert( j>=0 && j<=pColumn->nId ); -+ if( j==0 ){ - /* A column not named in the insert column list gets its - ** default value */ -- sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); -+ sqlite3ExprCodeFactorable(pParse, -+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), -+ iRegStore); - continue; - } -- k = j; -+ k = j - 1; - }else if( nColumn==0 ){ - /* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */ -- sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); -+ sqlite3ExprCodeFactorable(pParse, -+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), -+ iRegStore); - continue; - }else{ - k = i - nHidden; -@@ -121653,7 +136238,12 @@ SQLITE_PRIVATE void sqlite3Insert( - sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+k, iRegStore); - } - }else{ -- sqlite3ExprCode(pParse, pList->a[k].pExpr, iRegStore); -+ Expr *pX = pList->a[k].pExpr; -+ int y = sqlite3ExprCodeTarget(pParse, pX, iRegStore); -+ if( y!=iRegStore ){ -+ sqlite3VdbeAddOp2(v, -+ ExprHasProperty(pX, EP_Subquery) ? OP_Copy : OP_SCopy, y, iRegStore); -+ } - } - } - -@@ -121687,13 +136277,8 @@ SQLITE_PRIVATE void sqlite3Insert( - sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v); - } - -- /* Cannot have triggers on a virtual table. If it were possible, -- ** this block would have to account for hidden column. -- */ -- assert( !IsVirtual(pTab) ); -- - /* Copy the new data already generated. */ -- assert( pTab->nNVCol>0 ); -+ assert( pTab->nNVCol>0 || pParse->nErr>0 ); - sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1); - - #ifndef SQLITE_OMIT_GENERATED_COLUMNS -@@ -121790,12 +136375,14 @@ SQLITE_PRIVATE void sqlite3Insert( - }else - #endif - { -- int isReplace; /* Set to true if constraints may cause a replace */ -+ int isReplace = 0;/* Set to true if constraints may cause a replace */ - int bUseSeek; /* True to use OPFLAG_SEEKRESULT */ - sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, - regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert - ); -- sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); -+ if( db->flags & SQLITE_ForeignKeys ){ -+ sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); -+ } - - /* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE - ** constraints or (b) there are no triggers and this table is not a -@@ -121810,6 +136397,13 @@ SQLITE_PRIVATE void sqlite3Insert( - regIns, aRegIdx, 0, appendFlag, bUseSeek - ); - } -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ }else if( pParse->bReturning ){ -+ /* If there is a RETURNING clause, populate the rowid register with -+ ** constant value -1, in case one or more of the returned expressions -+ ** refer to the "rowid" of the view. */ -+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid); -+#endif - } - - /* Update the count of rows that are inserted -@@ -121846,7 +136440,9 @@ SQLITE_PRIVATE void sqlite3Insert( - sqlite3VdbeJumpHere(v, addrInsTop); - } - -+#ifndef SQLITE_OMIT_XFER_OPT - insert_end: -+#endif /* SQLITE_OMIT_XFER_OPT */ - /* Update the sqlite_sequence table by storing the content of the - ** maximum rowid counter values recorded while inserting into - ** autoincrement tables. -@@ -121861,9 +136457,7 @@ insert_end: - ** invoke the callback function. - */ - if( regRowCount ){ -- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); -- sqlite3VdbeSetNumCols(v, 1); -- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); -+ sqlite3CodeChangeCount(v, regRowCount, "rows inserted"); - } - - insert_cleanup: -@@ -121871,8 +136465,11 @@ insert_cleanup: - sqlite3ExprListDelete(db, pList); - sqlite3UpsertDelete(db, pUpsert); - sqlite3SelectDelete(db, pSelect); -- sqlite3IdListDelete(db, pColumn); -- sqlite3DbFree(db, aRegIdx); -+ if( pColumn ){ -+ sqlite3IdListDelete(db, pColumn); -+ sqlite3DbFree(db, aTabColMap); -+ } -+ if( aRegIdx ) sqlite3DbNNFreeNN(db, aRegIdx); - } - - /* Make sure "isView" and other macros defined above are undefined. Otherwise -@@ -121898,7 +136495,7 @@ insert_cleanup: - /* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn(). - * Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this - ** expression node references any of the --** columns that are being modifed by an UPDATE statement. -+** columns that are being modified by an UPDATE statement. - */ - static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ - if( pExpr->op==TK_COLUMN ){ -@@ -121951,6 +136548,70 @@ SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn( - return w.eCode!=0; - } - -+/* -+** The sqlite3GenerateConstraintChecks() routine usually wants to visit -+** the indexes of a table in the order provided in the Table->pIndex list. -+** However, sometimes (rarely - when there is an upsert) it wants to visit -+** the indexes in a different order. The following data structures accomplish -+** this. -+** -+** The IndexIterator object is used to walk through all of the indexes -+** of a table in either Index.pNext order, or in some other order established -+** by an array of IndexListTerm objects. -+*/ -+typedef struct IndexListTerm IndexListTerm; -+typedef struct IndexIterator IndexIterator; -+struct IndexIterator { -+ int eType; /* 0 for Index.pNext list. 1 for an array of IndexListTerm */ -+ int i; /* Index of the current item from the list */ -+ union { -+ struct { /* Use this object for eType==0: A Index.pNext list */ -+ Index *pIdx; /* The current Index */ -+ } lx; -+ struct { /* Use this object for eType==1; Array of IndexListTerm */ -+ int nIdx; /* Size of the array */ -+ IndexListTerm *aIdx; /* Array of IndexListTerms */ -+ } ax; -+ } u; -+}; -+ -+/* When IndexIterator.eType==1, then each index is an array of instances -+** of the following object -+*/ -+struct IndexListTerm { -+ Index *p; /* The index */ -+ int ix; /* Which entry in the original Table.pIndex list is this index*/ -+}; -+ -+/* Return the first index on the list */ -+static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){ -+ assert( pIter->i==0 ); -+ if( pIter->eType ){ -+ *pIx = pIter->u.ax.aIdx[0].ix; -+ return pIter->u.ax.aIdx[0].p; -+ }else{ -+ *pIx = 0; -+ return pIter->u.lx.pIdx; -+ } -+} -+ -+/* Return the next index from the list. Return NULL when out of indexes */ -+static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){ -+ if( pIter->eType ){ -+ int i = ++pIter->i; -+ if( i>=pIter->u.ax.nIdx ){ -+ *pIx = i; -+ return 0; -+ } -+ *pIx = pIter->u.ax.aIdx[i].ix; -+ return pIter->u.ax.aIdx[i].p; -+ }else{ -+ ++(*pIx); -+ pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext; -+ return pIter->u.lx.pIdx; -+ } -+} -+ - /* - ** Generate code to do constraint checks prior to an INSERT or an UPDATE - ** on table pTab. -@@ -122057,9 +136718,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - int *aiChng, /* column i is unchanged if aiChng[i]<0 */ - Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */ - ){ -- Vdbe *v; /* VDBE under constrution */ -+ Vdbe *v; /* VDBE under construction */ - Index *pIdx; /* Pointer to one of the indices */ -- Index *pPk = 0; /* The PRIMARY KEY index */ -+ Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */ - sqlite3 *db; /* Database connection */ - int i; /* loop counter */ - int ix; /* Index loop counter */ -@@ -122067,11 +136728,11 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - int onError; /* Conflict resolution strategy */ - int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ - int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ -- Index *pUpIdx = 0; /* Index to which to apply the upsert */ -- u8 isUpdate; /* True if this is an UPDATE operation */ -+ Upsert *pUpsertClause = 0; /* The specific ON CONFLICT clause for pIdx */ -+ u8 isUpdate; /* True if this is an UPDATE operation */ - u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */ -- int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */ -- int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */ -+ int upsertIpkReturn = 0; /* Address of Goto at end of IPK uniqueness check */ -+ int upsertIpkDelay = 0; /* Address of Goto to bypass initial IPK check */ - int ipkTop = 0; /* Top of the IPK uniqueness check */ - int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */ - /* Variables associated with retesting uniqueness constraints after -@@ -122081,12 +136742,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */ - Trigger *pTrigger; /* List of DELETE triggers on the table pTab */ - int nReplaceTrig = 0; /* Number of replace triggers coded */ -+ IndexIterator sIdxIter; /* Index iterator */ - - isUpdate = regOldData!=0; - db = pParse->db; -- v = sqlite3GetVdbe(pParse); -+ v = pParse->pVdbe; - assert( v!=0 ); -- assert( pTab->pSelect==0 ); /* This table is not a VIEW */ -+ assert( !IsView(pTab) ); /* This table is not a VIEW */ - nCol = pTab->nCol; - - /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for -@@ -122137,7 +136799,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - } - if( onError==OE_Replace ){ - if( b2ndPass /* REPLACE becomes ABORT on the 2nd pass */ -- || pCol->pDflt==0 /* REPLACE is ABORT if no DEFAULT value */ -+ || pCol->iDflt==0 /* REPLACE is ABORT if no DEFAULT value */ - ){ - testcase( pCol->colFlags & COLFLAG_VIRTUAL ); - testcase( pCol->colFlags & COLFLAG_STORED ); -@@ -122159,7 +136821,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - VdbeCoverage(v); - assert( (pCol->colFlags & COLFLAG_GENERATED)==0 ); - nSeenReplace++; -- sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg); -+ sqlite3ExprCodeCopy(pParse, -+ sqlite3ColumnExpr(pTab, pCol), iReg); - sqlite3VdbeJumpHere(v, addr1); - break; - } -@@ -122169,7 +136832,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - case OE_Rollback: - case OE_Fail: { - char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, -- pCol->zName); -+ pCol->zCnName); -+ testcase( zMsg==0 && db->mallocFailed==0 ); - sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, - onError, iReg); - sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); -@@ -122238,7 +136902,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - sqlite3VdbeGoto(v, ignoreDest); - }else{ - char *zName = pCheck->a[i].zEName; -- if( zName==0 ) zName = pTab->zName; -+ assert( zName!=0 || pParse->db->mallocFailed ); - if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-26383-51744 */ - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK, - onError, zName, P4_TRANSIENT, -@@ -122263,7 +136927,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - ** could happen in any order, but they are grouped up front for - ** convenience. - ** -- ** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43 -+ ** 2018-08-14: Ticket https://sqlite.org/src/info/908f001483982c43 - ** The order of constraints used to have OE_Update as (2) and OE_Abort - ** and so forth as (1). But apparently PostgreSQL checks the OE_Update - ** constraint before any others, so it had to be moved. -@@ -122278,19 +136942,63 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - ** list of indexes attached to a table puts all OE_Replace indexes last - ** in the list. See sqlite3CreateIndex() for where that happens. - */ -- -+ sIdxIter.eType = 0; -+ sIdxIter.i = 0; -+ sIdxIter.u.ax.aIdx = 0; /* Silence harmless compiler warning */ -+ sIdxIter.u.lx.pIdx = pTab->pIndex; - if( pUpsert ){ - if( pUpsert->pUpsertTarget==0 ){ -- /* An ON CONFLICT DO NOTHING clause, without a constraint-target. -- ** Make all unique constraint resolution be OE_Ignore */ -- assert( pUpsert->pUpsertSet==0 ); -- overrideError = OE_Ignore; -- pUpsert = 0; -- }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){ -- /* If the constraint-target uniqueness check must be run first. -- ** Jump to that uniqueness check now */ -- upsertJump = sqlite3VdbeAddOp0(v, OP_Goto); -- VdbeComment((v, "UPSERT constraint goes first")); -+ /* There is just on ON CONFLICT clause and it has no constraint-target */ -+ assert( pUpsert->pNextUpsert==0 ); -+ if( pUpsert->isDoUpdate==0 ){ -+ /* A single ON CONFLICT DO NOTHING clause, without a constraint-target. -+ ** Make all unique constraint resolution be OE_Ignore */ -+ overrideError = OE_Ignore; -+ pUpsert = 0; -+ }else{ -+ /* A single ON CONFLICT DO UPDATE. Make all resolutions OE_Update */ -+ overrideError = OE_Update; -+ } -+ }else if( pTab->pIndex!=0 ){ -+ /* Otherwise, we'll need to run the IndexListTerm array version of the -+ ** iterator to ensure that all of the ON CONFLICT conditions are -+ ** checked first and in order. */ -+ int nIdx, jj; -+ u64 nByte; -+ Upsert *pTerm; -+ u8 *bUsed; -+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ -+ assert( aRegIdx[nIdx]>0 ); -+ } -+ sIdxIter.eType = 1; -+ sIdxIter.u.ax.nIdx = nIdx; -+ nByte = (sizeof(IndexListTerm)+1)*nIdx + nIdx; -+ sIdxIter.u.ax.aIdx = sqlite3DbMallocZero(db, nByte); -+ if( sIdxIter.u.ax.aIdx==0 ) return; /* OOM */ -+ bUsed = (u8*)&sIdxIter.u.ax.aIdx[nIdx]; -+ pUpsert->pToFree = sIdxIter.u.ax.aIdx; -+ for(i=0, pTerm=pUpsert; pTerm; pTerm=pTerm->pNextUpsert){ -+ if( pTerm->pUpsertTarget==0 ) break; -+ if( pTerm->pUpsertIdx==0 ) continue; /* Skip ON CONFLICT for the IPK */ -+ jj = 0; -+ pIdx = pTab->pIndex; -+ while( ALWAYS(pIdx!=0) && pIdx!=pTerm->pUpsertIdx ){ -+ pIdx = pIdx->pNext; -+ jj++; -+ } -+ if( bUsed[jj] ) continue; /* Duplicate ON CONFLICT clause ignored */ -+ bUsed[jj] = 1; -+ sIdxIter.u.ax.aIdx[i].p = pIdx; -+ sIdxIter.u.ax.aIdx[i].ix = jj; -+ i++; -+ } -+ for(jj=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, jj++){ -+ if( bUsed[jj] ) continue; -+ sIdxIter.u.ax.aIdx[i].p = pIdx; -+ sIdxIter.u.ax.aIdx[i].ix = jj; -+ i++; -+ } -+ assert( i==nIdx ); - } - } - -@@ -122353,11 +137061,20 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - } - - /* figure out whether or not upsert applies in this case */ -- if( pUpsert && pUpsert->pUpsertIdx==0 ){ -- if( pUpsert->pUpsertSet==0 ){ -- onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ -- }else{ -- onError = OE_Update; /* DO UPDATE */ -+ if( pUpsert ){ -+ pUpsertClause = sqlite3UpsertOfIndex(pUpsert,0); -+ if( pUpsertClause!=0 ){ -+ if( pUpsertClause->isDoUpdate==0 ){ -+ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ -+ }else{ -+ onError = OE_Update; /* DO UPDATE */ -+ } -+ } -+ if( pUpsertClause!=pUpsert ){ -+ /* The first ON CONFLICT clause has a conflict target other than -+ ** the IPK. We have to jump ahead to that first ON CONFLICT clause -+ ** and then come back here and deal with the IPK afterwards */ -+ upsertIpkDelay = sqlite3VdbeAddOp0(v, OP_Goto); - } - } - -@@ -122367,8 +137084,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - ** the UNIQUE constraints have run. - */ - if( onError==OE_Replace /* IPK rule is REPLACE */ -- && onError!=overrideError /* Rules for other contraints are different */ -+ && onError!=overrideError /* Rules for other constraints are different */ - && pTab->pIndex /* There exist other constraints */ -+ && !upsertIpkDelay /* IPK check already deferred by UPSERT */ - ){ - ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1; - VdbeComment((v, "defer IPK REPLACE until last")); -@@ -122464,7 +137182,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - } - } - sqlite3VdbeResolveLabel(v, addrRowidOk); -- if( ipkTop ){ -+ if( pUpsert && pUpsertClause!=pUpsert ){ -+ upsertIpkReturn = sqlite3VdbeAddOp0(v, OP_Goto); -+ }else if( ipkTop ){ - ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, ipkTop-1); - } -@@ -122477,23 +137197,25 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - ** This loop also handles the case of the PRIMARY KEY index for a - ** WITHOUT ROWID table. - */ -- for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ -- int regIdx; /* Range of registers hold conent for pIdx */ -+ for(pIdx = indexIteratorFirst(&sIdxIter, &ix); -+ pIdx; -+ pIdx = indexIteratorNext(&sIdxIter, &ix) -+ ){ -+ int regIdx; /* Range of registers holding content for pIdx */ - int regR; /* Range of registers holding conflicting PK */ - int iThisCur; /* Cursor for this UNIQUE index */ - int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ - int addrConflictCk; /* First opcode in the conflict check logic */ - - if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ -- if( pUpIdx==pIdx ){ -- addrUniqueOk = upsertJump+1; -- upsertBypass = sqlite3VdbeGoto(v, 0); -- VdbeComment((v, "Skip upsert subroutine")); -- sqlite3VdbeJumpHere(v, upsertJump); -- }else{ -- addrUniqueOk = sqlite3VdbeMakeLabel(pParse); -+ if( pUpsert ){ -+ pUpsertClause = sqlite3UpsertOfIndex(pUpsert, pIdx); -+ if( upsertIpkDelay && pUpsertClause==pUpsert ){ -+ sqlite3VdbeJumpHere(v, upsertIpkDelay); -+ } - } -- if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ -+ addrUniqueOk = sqlite3VdbeMakeLabel(pParse); -+ if( bAffinityDone==0 ){ - sqlite3TableAffinity(v, pTab, regNewData+1); - bAffinityDone = 1; - } -@@ -122530,7 +137252,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - testcase( sqlite3TableColumnToStorage(pTab, iField)!=iField ); - x = sqlite3TableColumnToStorage(pTab, iField) + regNewData + 1; - sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i); -- VdbeComment((v, "%s", pTab->aCol[iField].zName)); -+ VdbeComment((v, "%s", pTab->aCol[iField].zCnName)); - } - } - sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); -@@ -122564,8 +137286,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - } - - /* Figure out if the upsert clause applies to this index */ -- if( pUpIdx==pIdx ){ -- if( pUpsert->pUpsertSet==0 ){ -+ if( pUpsertClause ){ -+ if( pUpsertClause->isDoUpdate==0 ){ - onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ - }else{ - onError = OE_Update; /* DO UPDATE */ -@@ -122582,6 +137304,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - ** This is not possible for ENABLE_PREUPDATE_HOOK builds, as the row - ** must be explicitly deleted in order to ensure any pre-update hook - ** is invoked. */ -+ assert( IsOrdinaryTable(pTab) ); - #ifndef SQLITE_ENABLE_PREUPDATE_HOOK - if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ - && pPk==pIdx /* Condition 2 */ -@@ -122589,7 +137312,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ - 0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) - && ( 0==(db->flags&SQLITE_ForeignKeys) || /* Condition 5 */ -- (0==pTab->pFKey && 0==sqlite3FkReferences(pTab))) -+ (0==pTab->u.tab.pFKey && 0==sqlite3FkReferences(pTab))) - ){ - sqlite3VdbeResolveLabel(v, addrUniqueOk); - continue; -@@ -122603,7 +137326,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - regIdx, pIdx->nKeyCol); VdbeCoverage(v); - - /* Generate code to handle collisions */ -- regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); -+ regR = pIdx==pPk ? regIdx : sqlite3GetTempRange(pParse, nPkField); - if( isUpdate || onError==OE_Replace ){ - if( HasRowid(pTab) ){ - sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); -@@ -122624,13 +137347,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - x = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); - sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); - VdbeComment((v, "%s.%s", pTab->zName, -- pTab->aCol[pPk->aiColumn[i]].zName)); -+ pTab->aCol[pPk->aiColumn[i]].zCnName)); - } - } - if( isUpdate ){ - /* If currently processing the PRIMARY KEY of a WITHOUT ROWID - ** table, only conflict if the new PRIMARY KEY values are actually -- ** different from the old. -+ ** different from the old. See TH3 withoutrowid04.test. - ** - ** For a UNIQUE index, only conflict if the PRIMARY KEY values - ** of the matched index row are different from the original PRIMARY -@@ -122688,7 +137411,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - - assert( onError==OE_Replace ); - nConflictCk = sqlite3VdbeCurrentAddr(v) - addrConflictCk; -- assert( nConflictCk>0 ); -+ assert( nConflictCk>0 || db->mallocFailed ); -+ testcase( nConflictCk<=0 ); - testcase( nConflictCk>1 ); - if( regTrigCnt ){ - sqlite3MultiWrite(pParse); -@@ -122755,19 +137479,23 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - break; - } - } -- if( pUpIdx==pIdx ){ -- sqlite3VdbeGoto(v, upsertJump+1); -- sqlite3VdbeJumpHere(v, upsertBypass); -- }else{ -- sqlite3VdbeResolveLabel(v, addrUniqueOk); -- } -+ sqlite3VdbeResolveLabel(v, addrUniqueOk); - if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField); -+ if( pUpsertClause -+ && upsertIpkReturn -+ && sqlite3UpsertNextIsIPK(pUpsertClause) -+ ){ -+ sqlite3VdbeGoto(v, upsertIpkDelay+1); -+ sqlite3VdbeJumpHere(v, upsertIpkReturn); -+ upsertIpkReturn = 0; -+ } - } - - /* If the IPK constraint is a REPLACE, run it last */ - if( ipkTop ){ - sqlite3VdbeGoto(v, ipkTop); - VdbeComment((v, "Do IPK REPLACE")); -+ assert( ipkBottom>0 ); - sqlite3VdbeJumpHere(v, ipkBottom); - } - -@@ -122820,13 +137548,39 @@ SQLITE_PRIVATE void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){ - if( pTab->pSchema->file_format<2 ) return; - - for(i=pTab->nCol-1; i>0; i--){ -- if( pTab->aCol[i].pDflt!=0 ) break; -+ if( pTab->aCol[i].iDflt!=0 ) break; - if( pTab->aCol[i].colFlags & COLFLAG_PRIMKEY ) break; - } - sqlite3VdbeChangeP5(v, i+1); - } - #endif - -+/* -+** Table pTab is a WITHOUT ROWID table that is being written to. The cursor -+** number is iCur, and register regData contains the new record for the -+** PK index. This function adds code to invoke the pre-update hook, -+** if one is registered. -+*/ -+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -+static void codeWithoutRowidPreupdate( -+ Parse *pParse, /* Parse context */ -+ Table *pTab, /* Table being updated */ -+ int iCur, /* Cursor number for table */ -+ int regData /* Data containing new record */ -+){ -+ Vdbe *v = pParse->pVdbe; -+ int r = sqlite3GetTempReg(pParse); -+ assert( !HasRowid(pTab) ); -+ assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB ); -+ sqlite3VdbeAddOp2(v, OP_Integer, 0, r); -+ sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE); -+ sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); -+ sqlite3ReleaseTempReg(pParse, r); -+} -+#else -+# define codeWithoutRowidPreupdate(a,b,c,d) -+#endif -+ - /* - ** This routine generates code to finish the INSERT or UPDATE operation - ** that was started by a prior call to sqlite3GenerateConstraintChecks. -@@ -122857,9 +137611,9 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( - || update_flags==(OPFLAG_ISUPDATE|OPFLAG_SAVEPOSITION) - ); - -- v = sqlite3GetVdbe(pParse); -+ v = pParse->pVdbe; - assert( v!=0 ); -- assert( pTab->pSelect==0 ); /* This table is not a VIEW */ -+ assert( !IsView(pTab) ); /* This table is not a VIEW */ - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - /* All REPLACE indexes are at the end of the list */ - assert( pIdx->onError!=OE_Replace -@@ -122872,20 +137626,11 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( - } - pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0); - if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ -- assert( pParse->nested==0 ); - pik_flags |= OPFLAG_NCHANGE; - pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); --#ifdef SQLITE_ENABLE_PREUPDATE_HOOK - if( update_flags==0 ){ -- int r = sqlite3GetTempReg(pParse); -- sqlite3VdbeAddOp2(v, OP_Integer, 0, r); -- sqlite3VdbeAddOp4(v, OP_Insert, -- iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE -- ); -- sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); -- sqlite3ReleaseTempReg(pParse, r); -+ codeWithoutRowidPreupdate(pParse, pTab, iIdxCur+i, aRegIdx[i]); - } --#endif - } - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i], - aRegIdx[i]+1, -@@ -122951,29 +137696,32 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices( - - assert( op==OP_OpenRead || op==OP_OpenWrite ); - assert( op==OP_OpenWrite || p5==0 ); -+ assert( piDataCur!=0 ); -+ assert( piIdxCur!=0 ); - if( IsVirtual(pTab) ){ - /* This routine is a no-op for virtual tables. Leave the output -- ** variables *piDataCur and *piIdxCur uninitialized so that valgrind -- ** can detect if they are used by mistake in the caller. */ -+ ** variables *piDataCur and *piIdxCur set to illegal cursor numbers -+ ** for improved error detection. */ -+ *piDataCur = *piIdxCur = -999; - return 0; - } - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); -- v = sqlite3GetVdbe(pParse); -+ v = pParse->pVdbe; - assert( v!=0 ); - if( iBase<0 ) iBase = pParse->nTab; - iDataCur = iBase++; -- if( piDataCur ) *piDataCur = iDataCur; -+ *piDataCur = iDataCur; - if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){ - sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op); -- }else{ -+ }else if( pParse->db->noSharedCache==0 ){ - sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); - } -- if( piIdxCur ) *piIdxCur = iBase; -+ *piIdxCur = iBase; - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - int iIdxCur = iBase++; - assert( pIdx->pSchema==pTab->pSchema ); - if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){ -- if( piDataCur ) *piDataCur = iIdxCur; -+ *piDataCur = iIdxCur; - p5 = 0; - } - if( aToOpen==0 || aToOpen[i+1] ){ -@@ -123083,7 +137831,7 @@ static int xferOptimization( - ExprList *pEList; /* The result set of the SELECT */ - Table *pSrc; /* The table in the FROM clause of SELECT */ - Index *pSrcIdx, *pDestIdx; /* Source and destination indices */ -- struct SrcList_item *pItem; /* An element of pSelect->pSrc */ -+ SrcItem *pItem; /* An element of pSelect->pSrc */ - int i; /* Loop counter */ - int iDbSrc; /* The database of pSrc */ - int iSrc, iDest; /* Cursors from source and destination */ -@@ -123095,18 +137843,13 @@ static int xferOptimization( - int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ - int regData, regRowid; /* Registers holding data and rowid */ - -- if( pSelect==0 ){ -- return 0; /* Must be of the form INSERT INTO ... SELECT ... */ -- } -+ assert( pSelect!=0 ); - if( pParse->pWith || pSelect->pWith ){ - /* Do not attempt to process this query if there are an WITH clauses - ** attached to it. Proceeding may generate a false "no such table: xxx" - ** error if pSelect reads from a CTE named "xxx". */ - return 0; - } -- if( sqlite3TriggerList(pParse, pDest) ){ -- return 0; /* tab1 must not have triggers */ -- } - #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pDest) ){ - return 0; /* tab1 must not be a virtual table */ -@@ -123120,7 +137863,7 @@ static int xferOptimization( - if( pSelect->pSrc->nSrc!=1 ){ - return 0; /* FROM clause must have exactly one term */ - } -- if( pSelect->pSrc->a[0].pSelect ){ -+ if( pSelect->pSrc->a[0].fg.isSubquery ){ - return 0; /* FROM clause cannot contain a subquery */ - } - if( pSelect->pWhere ){ -@@ -123169,13 +137912,8 @@ static int xferOptimization( - if( HasRowid(pDest)!=HasRowid(pSrc) ){ - return 0; /* source and destination must both be WITHOUT ROWID or not */ - } --#ifndef SQLITE_OMIT_VIRTUALTABLE -- if( IsVirtual(pSrc) ){ -- return 0; /* tab2 must not be a virtual table */ -- } --#endif -- if( pSrc->pSelect ){ -- return 0; /* tab2 may not be a view */ -+ if( !IsOrdinaryTable(pSrc) ){ -+ return 0; /* tab2 may not be a view or virtual table */ - } - if( pDest->nCol!=pSrc->nCol ){ - return 0; /* Number of columns must be the same in tab1 and tab2 */ -@@ -123183,6 +137921,9 @@ static int xferOptimization( - if( pDest->iPKey!=pSrc->iPKey ){ - return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ - } -+ if( (pDest->tabFlags & TF_Strict)!=0 && (pSrc->tabFlags & TF_Strict)==0 ){ -+ return 0; /* Cannot feed from a non-strict into a strict table */ -+ } - for(i=0; inCol; i++){ - Column *pDestCol = &pDest->aCol[i]; - Column *pSrcCol = &pSrc->aCol[i]; -@@ -123219,7 +137960,9 @@ static int xferOptimization( - ** This requirement could be relaxed for VIRTUAL columns, I suppose. - */ - if( (pDestCol->colFlags & COLFLAG_GENERATED)!=0 ){ -- if( sqlite3ExprCompare(0, pSrcCol->pDflt, pDestCol->pDflt, -1)!=0 ){ -+ if( sqlite3ExprCompare(0, -+ sqlite3ColumnExpr(pSrc, pSrcCol), -+ sqlite3ColumnExpr(pDest, pDestCol), -1)!=0 ){ - testcase( pDestCol->colFlags & COLFLAG_VIRTUAL ); - testcase( pDestCol->colFlags & COLFLAG_STORED ); - return 0; /* Different generator expressions */ -@@ -123229,7 +137972,8 @@ static int xferOptimization( - if( pDestCol->affinity!=pSrcCol->affinity ){ - return 0; /* Affinity must be the same on all columns */ - } -- if( sqlite3_stricmp(pDestCol->zColl, pSrcCol->zColl)!=0 ){ -+ if( sqlite3_stricmp(sqlite3ColumnColl(pDestCol), -+ sqlite3ColumnColl(pSrcCol))!=0 ){ - return 0; /* Collating sequence must be the same on all columns */ - } - if( pDestCol->notNull && !pSrcCol->notNull ){ -@@ -123237,11 +137981,15 @@ static int xferOptimization( - } - /* Default values for second and subsequent columns need to match. */ - if( (pDestCol->colFlags & COLFLAG_GENERATED)==0 && i>0 ){ -- assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN ); -- assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN ); -- if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0) -- || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken, -- pSrcCol->pDflt->u.zToken)!=0) -+ Expr *pDestExpr = sqlite3ColumnExpr(pDest, pDestCol); -+ Expr *pSrcExpr = sqlite3ColumnExpr(pSrc, pSrcCol); -+ assert( pDestExpr==0 || pDestExpr->op==TK_SPAN ); -+ assert( pDestExpr==0 || !ExprHasProperty(pDestExpr, EP_IntValue) ); -+ assert( pSrcExpr==0 || pSrcExpr->op==TK_SPAN ); -+ assert( pSrcExpr==0 || !ExprHasProperty(pSrcExpr, EP_IntValue) ); -+ if( (pDestExpr==0)!=(pSrcExpr==0) -+ || (pDestExpr!=0 && strcmp(pDestExpr->u.zToken, -+ pSrcExpr->u.zToken)!=0) - ){ - return 0; /* Default values must be the same for all columns */ - } -@@ -123266,19 +138014,23 @@ static int xferOptimization( - } - } - #ifndef SQLITE_OMIT_CHECK -- if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){ -+ if( pDest->pCheck -+ && (db->mDbFlags & DBFLAG_Vacuum)==0 -+ && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) -+ ){ - return 0; /* Tables have different CHECK constraints. Ticket #2252 */ - } - #endif - #ifndef SQLITE_OMIT_FOREIGN_KEY -- /* Disallow the transfer optimization if the destination table constains -+ /* Disallow the transfer optimization if the destination table contains - ** any foreign key constraints. This is more restrictive than necessary. - ** But the main beneficiary of the transfer optimization is the VACUUM - ** command, and the VACUUM command disables foreign key constraints. So - ** the extra complication to make this rule less restrictive is probably - ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] - */ -- if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ -+ assert( IsOrdinaryTable(pDest) ); -+ if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->u.tab.pFKey!=0 ){ - return 0; - } - #endif -@@ -123300,6 +138052,7 @@ static int xferOptimization( - iDest = pParse->nTab++; - regAutoinc = autoIncBegin(pParse, iDbDest, pDest); - regData = sqlite3GetTempReg(pParse); -+ sqlite3VdbeAddOp2(v, OP_Null, 0, regData); - regRowid = sqlite3GetTempReg(pParse); - sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); - assert( HasRowid(pDest) || destHasUniqueIdx ); -@@ -123335,11 +138088,13 @@ static int xferOptimization( - emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); - if( pDest->iPKey>=0 ){ - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); -- sqlite3VdbeVerifyAbortable(v, onError); -- addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); -- VdbeCoverage(v); -- sqlite3RowidConstraint(pParse, onError, pDest); -- sqlite3VdbeJumpHere(v, addr2); -+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ -+ sqlite3VdbeVerifyAbortable(v, onError); -+ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); -+ VdbeCoverage(v); -+ sqlite3RowidConstraint(pParse, onError, pDest); -+ sqlite3VdbeJumpHere(v, addr2); -+ } - autoIncStep(pParse, regAutoinc, regRowid); - }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){ - addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); -@@ -123347,16 +138102,28 @@ static int xferOptimization( - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); - assert( (pDest->tabFlags & TF_Autoincrement)==0 ); - } -+ - if( db->mDbFlags & DBFLAG_Vacuum ){ - sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); -- insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT; -+ insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT; - }else{ -- insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND; -+ insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT; -+ } -+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK -+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ -+ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); -+ insFlags &= ~OPFLAG_PREFORMAT; -+ }else -+#endif -+ { -+ sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid); -+ } -+ sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); -+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){ -+ sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE); - } -- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); -- sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid, -- (char*)pDest, P4_TABLE); - sqlite3VdbeChangeP5(v, insFlags); -+ - sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); - sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); -@@ -123398,13 +138165,22 @@ static int xferOptimization( - if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break; - } - if( i==pSrcIdx->nColumn ){ -- idxInsFlags = OPFLAG_USESEEKRESULT; -+ idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT; - sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); -+ sqlite3VdbeAddOp2(v, OP_RowCell, iDest, iSrc); - } - }else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ - idxInsFlags |= OPFLAG_NCHANGE; - } -- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); -+ if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){ -+ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); -+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 -+ && !HasRowid(pDest) -+ && IsPrimaryKeyIndex(pDestIdx) -+ ){ -+ codeWithoutRowidPreupdate(pParse, pDest, iDest, regData); -+ } -+ } - sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData); - sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND); - sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); -@@ -123926,10 +138702,43 @@ struct sqlite3_api_routines { - const char *(*filename_journal)(const char*); - const char *(*filename_wal)(const char*); - /* Version 3.32.0 and later */ -- char *(*create_filename)(const char*,const char*,const char*, -+ const char *(*create_filename)(const char*,const char*,const char*, - int,const char**); -- void (*free_filename)(char*); -+ void (*free_filename)(const char*); - sqlite3_file *(*database_file_object)(const char*); -+ /* Version 3.34.0 and later */ -+ int (*txn_state)(sqlite3*,const char*); -+ /* Version 3.36.1 and later */ -+ sqlite3_int64 (*changes64)(sqlite3*); -+ sqlite3_int64 (*total_changes64)(sqlite3*); -+ /* Version 3.37.0 and later */ -+ int (*autovacuum_pages)(sqlite3*, -+ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), -+ void*, void(*)(void*)); -+ /* Version 3.38.0 and later */ -+ int (*error_offset)(sqlite3*); -+ int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**); -+ int (*vtab_distinct)(sqlite3_index_info*); -+ int (*vtab_in)(sqlite3_index_info*,int,int); -+ int (*vtab_in_first)(sqlite3_value*,sqlite3_value**); -+ int (*vtab_in_next)(sqlite3_value*,sqlite3_value**); -+ /* Version 3.39.0 and later */ -+ int (*deserialize)(sqlite3*,const char*,unsigned char*, -+ sqlite3_int64,sqlite3_int64,unsigned); -+ unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*, -+ unsigned int); -+ const char *(*db_name)(sqlite3*,int); -+ /* Version 3.40.0 and later */ -+ int (*value_encoding)(sqlite3_value*); -+ /* Version 3.41.0 and later */ -+ int (*is_interrupted)(sqlite3*); -+ /* Version 3.43.0 and later */ -+ int (*stmt_explain)(sqlite3_stmt*,int); -+ /* Version 3.44.0 and later */ -+ void *(*get_clientdata)(sqlite3*,const char*); -+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); -+ /* Version 3.50.0 and later */ -+ int (*setlk_timeout)(sqlite3*,int,int); - }; - - /* -@@ -124234,6 +139043,37 @@ typedef int (*sqlite3_loadext_entry)( - #define sqlite3_create_filename sqlite3_api->create_filename - #define sqlite3_free_filename sqlite3_api->free_filename - #define sqlite3_database_file_object sqlite3_api->database_file_object -+/* Version 3.34.0 and later */ -+#define sqlite3_txn_state sqlite3_api->txn_state -+/* Version 3.36.1 and later */ -+#define sqlite3_changes64 sqlite3_api->changes64 -+#define sqlite3_total_changes64 sqlite3_api->total_changes64 -+/* Version 3.37.0 and later */ -+#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages -+/* Version 3.38.0 and later */ -+#define sqlite3_error_offset sqlite3_api->error_offset -+#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value -+#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct -+#define sqlite3_vtab_in sqlite3_api->vtab_in -+#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first -+#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next -+/* Version 3.39.0 and later */ -+#ifndef SQLITE_OMIT_DESERIALIZE -+#define sqlite3_deserialize sqlite3_api->deserialize -+#define sqlite3_serialize sqlite3_api->serialize -+#endif -+#define sqlite3_db_name sqlite3_api->db_name -+/* Version 3.40.0 and later */ -+#define sqlite3_value_encoding sqlite3_api->value_encoding -+/* Version 3.41.0 and later */ -+#define sqlite3_is_interrupted sqlite3_api->is_interrupted -+/* Version 3.43.0 and later */ -+#define sqlite3_stmt_explain sqlite3_api->stmt_explain -+/* Version 3.44.0 and later */ -+#define sqlite3_get_clientdata sqlite3_api->get_clientdata -+#define sqlite3_set_clientdata sqlite3_api->set_clientdata -+/* Version 3.50.0 and later */ -+#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout - #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ - - #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) -@@ -124716,6 +139556,48 @@ static const sqlite3_api_routines sqlite3Apis = { - sqlite3_create_filename, - sqlite3_free_filename, - sqlite3_database_file_object, -+ /* Version 3.34.0 and later */ -+ sqlite3_txn_state, -+ /* Version 3.36.1 and later */ -+ sqlite3_changes64, -+ sqlite3_total_changes64, -+ /* Version 3.37.0 and later */ -+ sqlite3_autovacuum_pages, -+ /* Version 3.38.0 and later */ -+ sqlite3_error_offset, -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+ sqlite3_vtab_rhs_value, -+ sqlite3_vtab_distinct, -+ sqlite3_vtab_in, -+ sqlite3_vtab_in_first, -+ sqlite3_vtab_in_next, -+#else -+ 0, -+ 0, -+ 0, -+ 0, -+ 0, -+#endif -+ /* Version 3.39.0 and later */ -+#ifndef SQLITE_OMIT_DESERIALIZE -+ sqlite3_deserialize, -+ sqlite3_serialize, -+#else -+ 0, -+ 0, -+#endif -+ sqlite3_db_name, -+ /* Version 3.40.0 and later */ -+ sqlite3_value_encoding, -+ /* Version 3.41.0 and later */ -+ sqlite3_is_interrupted, -+ /* Version 3.43.0 and later */ -+ sqlite3_stmt_explain, -+ /* Version 3.44.0 and later */ -+ sqlite3_get_clientdata, -+ sqlite3_set_clientdata, -+ /* Version 3.50.0 and later */ -+ sqlite3_setlk_timeout - }; - - /* True if x is the directory separator character -@@ -124751,7 +139633,7 @@ static int sqlite3LoadExtension( - const char *zEntry; - char *zAltEntry = 0; - void **aHandle; -- u64 nMsg = 300 + sqlite3Strlen30(zFile); -+ u64 nMsg = strlen(zFile); - int ii; - int rc; - -@@ -124785,26 +139667,32 @@ static int sqlite3LoadExtension( - - zEntry = zProc ? zProc : "sqlite3_extension_init"; - -+ /* tag-20210611-1. Some dlopen() implementations will segfault if given -+ ** an oversize filename. Most filesystems have a pathname limit of 4K, -+ ** so limit the extension filename length to about twice that. -+ ** https://sqlite.org/forum/forumpost/08a0d6d9bf -+ ** -+ ** Later (2023-03-25): Save an extra 6 bytes for the filename suffix. -+ ** See https://sqlite.org/forum/forumpost/24083b579d. -+ */ -+ if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; -+ -+ /* Do not allow sqlite3_load_extension() to link to a copy of the -+ ** running application, by passing in an empty filename. */ -+ if( nMsg==0 ) goto extension_not_found; -+ - handle = sqlite3OsDlOpen(pVfs, zFile); - #if SQLITE_OS_UNIX || SQLITE_OS_WIN - for(ii=0; iiaExtension[db->nExtension++] = handle; - return SQLITE_OK; -+ -+extension_not_found: -+ if( pzErrMsg ){ -+ nMsg += 300; -+ *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); -+ if( zErrmsg ){ -+ assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ -+ sqlite3_snprintf((int)nMsg, zErrmsg, -+ "unable to open shared library [%.*s]", SQLITE_MAX_PATHLEN, zFile); -+ sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); -+ } -+ } -+ return SQLITE_ERROR; - } - SQLITE_API int sqlite3_load_extension( - sqlite3 *db, /* Load the extension into this database connection */ -@@ -124911,6 +139813,9 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){ - ** default so as not to open security holes in older applications. - */ - SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; -+#endif - sqlite3_mutex_enter(db->mutex); - if( onoff ){ - db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc; -@@ -124960,6 +139865,9 @@ SQLITE_API int sqlite3_auto_extension( - void (*xInit)(void) - ){ - int rc = SQLITE_OK; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( xInit==0 ) return SQLITE_MISUSE_BKPT; -+#endif - #ifndef SQLITE_OMIT_AUTOINIT - rc = sqlite3_initialize(); - if( rc ){ -@@ -125012,6 +139920,9 @@ SQLITE_API int sqlite3_cancel_auto_extension( - int i; - int n = 0; - wsdAutoextInit; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( xInit==0 ) return 0; -+#endif - sqlite3_mutex_enter(mutex); - for(i=(int)wsdAutoext.nExt-1; i>=0; i--){ - if( wsdAutoext.aExt[i]==xInit ){ -@@ -125168,13 +140079,14 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ - #define PragTyp_SOFT_HEAP_LIMIT 35 - #define PragTyp_SYNCHRONOUS 36 - #define PragTyp_TABLE_INFO 37 --#define PragTyp_TEMP_STORE 38 --#define PragTyp_TEMP_STORE_DIRECTORY 39 --#define PragTyp_THREADS 40 --#define PragTyp_WAL_AUTOCHECKPOINT 41 --#define PragTyp_WAL_CHECKPOINT 42 --#define PragTyp_LOCK_STATUS 43 --#define PragTyp_STATS 44 -+#define PragTyp_TABLE_LIST 38 -+#define PragTyp_TEMP_STORE 39 -+#define PragTyp_TEMP_STORE_DIRECTORY 40 -+#define PragTyp_THREADS 41 -+#define PragTyp_WAL_AUTOCHECKPOINT 42 -+#define PragTyp_WAL_CHECKPOINT 43 -+#define PragTyp_LOCK_STATUS 44 -+#define PragTyp_STATS 45 - - /* Property flags associated with various pragma. */ - #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ -@@ -125207,45 +140119,51 @@ static const char *const pragCName[] = { - /* 13 */ "pk", - /* 14 */ "hidden", - /* table_info reuses 8 */ -- /* 15 */ "seqno", /* Used by: index_xinfo */ -- /* 16 */ "cid", -- /* 17 */ "name", -- /* 18 */ "desc", -- /* 19 */ "coll", -- /* 20 */ "key", -- /* 21 */ "name", /* Used by: function_list */ -- /* 22 */ "builtin", -+ /* 15 */ "name", /* Used by: function_list */ -+ /* 16 */ "builtin", -+ /* 17 */ "type", -+ /* 18 */ "enc", -+ /* 19 */ "narg", -+ /* 20 */ "flags", -+ /* 21 */ "schema", /* Used by: table_list */ -+ /* 22 */ "name", - /* 23 */ "type", -- /* 24 */ "enc", -- /* 25 */ "narg", -- /* 26 */ "flags", -- /* 27 */ "tbl", /* Used by: stats */ -- /* 28 */ "idx", -- /* 29 */ "wdth", -- /* 30 */ "hght", -- /* 31 */ "flgs", -- /* 32 */ "seq", /* Used by: index_list */ -- /* 33 */ "name", -- /* 34 */ "unique", -- /* 35 */ "origin", -- /* 36 */ "partial", -- /* 37 */ "table", /* Used by: foreign_key_check */ -- /* 38 */ "rowid", -- /* 39 */ "parent", -- /* 40 */ "fkid", -- /* index_info reuses 15 */ -- /* 41 */ "seq", /* Used by: database_list */ -- /* 42 */ "name", -- /* 43 */ "file", -- /* 44 */ "busy", /* Used by: wal_checkpoint */ -- /* 45 */ "log", -- /* 46 */ "checkpointed", -- /* collation_list reuses 32 */ -- /* 47 */ "database", /* Used by: lock_status */ -- /* 48 */ "status", -- /* 49 */ "cache_size", /* Used by: default_cache_size */ -+ /* 24 */ "ncol", -+ /* 25 */ "wr", -+ /* 26 */ "strict", -+ /* 27 */ "seqno", /* Used by: index_xinfo */ -+ /* 28 */ "cid", -+ /* 29 */ "name", -+ /* 30 */ "desc", -+ /* 31 */ "coll", -+ /* 32 */ "key", -+ /* 33 */ "seq", /* Used by: index_list */ -+ /* 34 */ "name", -+ /* 35 */ "unique", -+ /* 36 */ "origin", -+ /* 37 */ "partial", -+ /* 38 */ "tbl", /* Used by: stats */ -+ /* 39 */ "idx", -+ /* 40 */ "wdth", -+ /* 41 */ "hght", -+ /* 42 */ "flgs", -+ /* 43 */ "table", /* Used by: foreign_key_check */ -+ /* 44 */ "rowid", -+ /* 45 */ "parent", -+ /* 46 */ "fkid", -+ /* 47 */ "busy", /* Used by: wal_checkpoint */ -+ /* 48 */ "log", -+ /* 49 */ "checkpointed", -+ /* 50 */ "seq", /* Used by: database_list */ -+ /* 51 */ "name", -+ /* 52 */ "file", -+ /* index_info reuses 27 */ -+ /* 53 */ "database", /* Used by: lock_status */ -+ /* 54 */ "status", -+ /* collation_list reuses 33 */ -+ /* 55 */ "cache_size", /* Used by: default_cache_size */ - /* module_list pragma_list reuses 9 */ -- /* 50 */ "timeout", /* Used by: busy_timeout */ -+ /* 56 */ "timeout", /* Used by: busy_timeout */ - }; - - /* Definitions of all built-in pragmas */ -@@ -125296,7 +140214,7 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "busy_timeout", - /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, - /* ePragFlg: */ PragFlg_Result0, -- /* ColNames: */ 50, 1, -+ /* ColNames: */ 56, 1, - /* iArg: */ 0 }, - #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) - {/* zName: */ "cache_size", -@@ -125335,7 +140253,7 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "collation_list", - /* ePragTyp: */ PragTyp_COLLATION_LIST, - /* ePragFlg: */ PragFlg_Result0, -- /* ColNames: */ 32, 2, -+ /* ColNames: */ 33, 2, - /* iArg: */ 0 }, - #endif - #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) -@@ -125369,15 +140287,15 @@ static const PragmaName aPragmaName[] = { - #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) - {/* zName: */ "database_list", - /* ePragTyp: */ PragTyp_DATABASE_LIST, -- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, -- /* ColNames: */ 41, 3, -+ /* ePragFlg: */ PragFlg_Result0, -+ /* ColNames: */ 50, 3, - /* iArg: */ 0 }, - #endif - #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) - {/* zName: */ "default_cache_size", - /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, -- /* ColNames: */ 49, 1, -+ /* ColNames: */ 55, 1, - /* iArg: */ 0 }, - #endif - #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) -@@ -125407,7 +140325,7 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "foreign_key_check", - /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, -- /* ColNames: */ 37, 4, -+ /* ColNames: */ 43, 4, - /* iArg: */ 0 }, - #endif - #if !defined(SQLITE_OMIT_FOREIGN_KEY) -@@ -125450,7 +140368,7 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "function_list", - /* ePragTyp: */ PragTyp_FUNCTION_LIST, - /* ePragFlg: */ PragFlg_Result0, -- /* ColNames: */ 21, 6, -+ /* ColNames: */ 15, 6, - /* iArg: */ 0 }, - #endif - #endif -@@ -125479,23 +140397,23 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "index_info", - /* ePragTyp: */ PragTyp_INDEX_INFO, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, -- /* ColNames: */ 15, 3, -+ /* ColNames: */ 27, 3, - /* iArg: */ 0 }, - {/* zName: */ "index_list", - /* ePragTyp: */ PragTyp_INDEX_LIST, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, -- /* ColNames: */ 32, 5, -+ /* ColNames: */ 33, 5, - /* iArg: */ 0 }, - {/* zName: */ "index_xinfo", - /* ePragTyp: */ PragTyp_INDEX_INFO, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, -- /* ColNames: */ 15, 6, -+ /* ColNames: */ 27, 6, - /* iArg: */ 1 }, - #endif - #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) - {/* zName: */ "integrity_check", - /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, -- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, -+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 0, 0, - /* iArg: */ 0 }, - #endif -@@ -125529,7 +140447,7 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "lock_status", - /* ePragTyp: */ PragTyp_LOCK_STATUS, - /* ePragFlg: */ PragFlg_Result0, -- /* ColNames: */ 47, 2, -+ /* ColNames: */ 53, 2, - /* iArg: */ 0 }, - #endif - #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) -@@ -125603,7 +140521,7 @@ static const PragmaName aPragmaName[] = { - #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) - {/* zName: */ "quick_check", - /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, -- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1, -+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 0, 0, - /* iArg: */ 0 }, - #endif -@@ -125668,7 +140586,7 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "stats", - /* ePragTyp: */ PragTyp_STATS, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, -- /* ColNames: */ 27, 5, -+ /* ColNames: */ 38, 5, - /* iArg: */ 0 }, - #endif - #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) -@@ -125684,6 +140602,11 @@ static const PragmaName aPragmaName[] = { - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, - /* ColNames: */ 8, 6, - /* iArg: */ 0 }, -+ {/* zName: */ "table_list", -+ /* ePragTyp: */ PragTyp_TABLE_LIST, -+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, -+ /* ColNames: */ 21, 6, -+ /* iArg: */ 0 }, - {/* zName: */ "table_xinfo", - /* ePragTyp: */ PragTyp_TABLE_INFO, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, -@@ -125759,7 +140682,7 @@ static const PragmaName aPragmaName[] = { - {/* zName: */ "wal_checkpoint", - /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, - /* ePragFlg: */ PragFlg_NeedSchema, -- /* ColNames: */ 44, 3, -+ /* ColNames: */ 47, 3, - /* iArg: */ 0 }, - #endif - #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) -@@ -125770,11 +140693,39 @@ static const PragmaName aPragmaName[] = { - /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, - #endif - }; --/* Number of pragmas: 67 on by default, 77 total. */ -+/* Number of pragmas: 68 on by default, 78 total. */ - - /************** End of pragma.h **********************************************/ - /************** Continuing where we left off in pragma.c *********************/ - -+/* -+** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands -+** will be run with an analysis_limit set to the lessor of the value of -+** the following macro or to the actual analysis_limit if it is non-zero, -+** in order to prevent PRAGMA optimize from running for too long. -+** -+** The value of 2000 is chosen empirically so that the worst-case run-time -+** for PRAGMA optimize does not exceed 100 milliseconds against a variety -+** of test databases on a RaspberryPI-4 compiled using -Os and without -+** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of -+** this paragraph, "worst-case" means that ANALYZE ends up being -+** run on every table in the database. The worst case typically only -+** happens if PRAGMA optimize is run on a database file for which ANALYZE -+** has not been previously run and the 0x10000 flag is included so that -+** all tables are analyzed. The usual case for PRAGMA optimize is that -+** no ANALYZE commands will be run at all, or if any ANALYZE happens it -+** will be against a single table, so that expected timing for PRAGMA -+** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000 -+** flag or less than 100 microseconds without the 0x10000 flag. -+** -+** An analysis limit of 2000 is almost always sufficient for the query -+** planner to fully characterize an index. The additional accuracy from -+** a larger analysis is not usually helpful. -+*/ -+#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT -+# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000 -+#endif -+ - /* - ** Interpret the given string as a safety level. Return 0 for OFF, - ** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or -@@ -125876,7 +140827,9 @@ static int getTempStore(const char *z){ - static int invalidateTempStorage(Parse *pParse){ - sqlite3 *db = pParse->db; - if( db->aDb[1].pBt!=0 ){ -- if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){ -+ if( !db->autoCommit -+ || sqlite3BtreeTxnState(db->aDb[1].pBt)!=SQLITE_TXN_NONE -+ ){ - sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " - "from within a transaction"); - return SQLITE_ERROR; -@@ -126050,15 +141003,16 @@ static void pragmaFunclistLine( - int isBuiltin, /* True if this is a built-in function */ - int showInternFuncs /* True if showing internal functions */ - ){ -+ u32 mask = -+ SQLITE_DETERMINISTIC | -+ SQLITE_DIRECTONLY | -+ SQLITE_SUBTYPE | -+ SQLITE_INNOCUOUS | -+ SQLITE_FUNC_INTERNAL -+ ; -+ if( showInternFuncs ) mask = 0xffffffff; - for(; p; p=p->pNext){ - const char *zType; -- static const u32 mask = -- SQLITE_DETERMINISTIC | -- SQLITE_DIRECTONLY | -- SQLITE_SUBTYPE | -- SQLITE_INNOCUOUS | -- SQLITE_FUNC_INTERNAL -- ; - static const char *azEnc[] = { 0, "utf8", "utf16le", "utf16be" }; - - assert( SQLITE_FUNC_ENCMASK==0x3 ); -@@ -126210,7 +141164,11 @@ SQLITE_PRIVATE void sqlite3Pragma( - - /* Locate the pragma in the lookup table */ - pPragma = pragmaLocate(zLeft); -- if( pPragma==0 ) goto pragma_out; -+ if( pPragma==0 ){ -+ /* IMP: R-43042-22504 No error messages are generated if an -+ ** unknown pragma is issued. */ -+ goto pragma_out; -+ } - - /* Make sure the database schema is loaded if the pragma requires that */ - if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){ -@@ -126546,7 +141504,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - */ - #ifndef SQLITE_OMIT_AUTOVACUUM - case PragTyp_INCREMENTAL_VACUUM: { -- int iLimit, addr; -+ int iLimit = 0, addr; - if( zRight==0 || !sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){ - iLimit = 0x7fffffff; - } -@@ -126592,7 +141550,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** - ** The first form reports the current local setting for the - ** page cache spill size. The second form turns cache spill on -- ** or off. When turnning cache spill on, the size is set to the -+ ** or off. When turning cache spill on, the size is set to the - ** current cache_size. The third form sets a spill size that - ** may be different form the cache size. - ** If N is positive then that is the -@@ -126703,6 +141661,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** - */ - case PragTyp_TEMP_STORE_DIRECTORY: { -+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - if( !zRight ){ - returnSingleText(v, sqlite3_temp_directory); - }else{ -@@ -126712,6 +141671,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); - if( rc!=SQLITE_OK || res==0 ){ - sqlite3ErrorMsg(pParse, "not a writable directory"); -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - goto pragma_out; - } - } -@@ -126729,6 +141689,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - } - #endif /* SQLITE_OMIT_WSD */ - } -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - break; - } - -@@ -126747,6 +141708,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** - */ - case PragTyp_DATA_STORE_DIRECTORY: { -+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - if( !zRight ){ - returnSingleText(v, sqlite3_data_directory); - }else{ -@@ -126756,6 +141718,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); - if( rc!=SQLITE_OK || res==0 ){ - sqlite3ErrorMsg(pParse, "not a writable directory"); -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - goto pragma_out; - } - } -@@ -126767,6 +141730,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - } - #endif /* SQLITE_OMIT_WSD */ - } -+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR)); - break; - } - #endif -@@ -126848,18 +141812,27 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** in auto-commit mode. */ - mask &= ~(SQLITE_ForeignKeys); - } --#if SQLITE_USER_AUTHENTICATION -- if( db->auth.authLevel==UAUTH_User ){ -- /* Do not allow non-admin users to modify the schema arbitrarily */ -- mask &= ~(SQLITE_WriteSchema); -- } --#endif - - if( sqlite3GetBoolean(zRight, 0) ){ -- db->flags |= mask; -+ if( (mask & SQLITE_WriteSchema)==0 -+ || (db->flags & SQLITE_Defensive)==0 -+ ){ -+ db->flags |= mask; -+ } - }else{ - db->flags &= ~mask; -- if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0; -+ if( mask==SQLITE_DeferFKs ){ -+ db->nDeferredImmCons = 0; -+ db->nDeferredCons = 0; -+ } -+ if( (mask & SQLITE_WriteSchema)!=0 -+ && sqlite3_stricmp(zRight, "reset")==0 -+ ){ -+ /* IMP: R-60817-01178 If the argument is "RESET" then schema -+ ** writing is disabled (as with "PRAGMA writable_schema=OFF") and, -+ ** in addition, the schema is reloaded. */ -+ sqlite3ResetAllSchemasOfConnection(db); -+ } - } - - /* Many of the flag-pragmas modify the code generated by the SQL -@@ -126900,6 +141873,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - sqlite3ViewGetColumnNames(pParse, pTab); - for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ - int isHidden = 0; -+ const Expr *pColExpr; - if( pCol->colFlags & COLFLAG_NOINSERT ){ - if( pPragma->iArg==0 ){ - nHidden++; -@@ -126920,13 +141894,16 @@ SQLITE_PRIVATE void sqlite3Pragma( - }else{ - for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} - } -- assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN || isHidden>=2 ); -+ pColExpr = sqlite3ColumnExpr(pTab,pCol); -+ assert( pColExpr==0 || pColExpr->op==TK_SPAN || isHidden>=2 ); -+ assert( pColExpr==0 || !ExprHasProperty(pColExpr, EP_IntValue) -+ || isHidden>=2 ); - sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi", - i-nHidden, -- pCol->zName, -+ pCol->zCnName, - sqlite3ColumnType(pCol,""), - pCol->notNull ? 1 : 0, -- pCol->pDflt && isHidden<2 ? pCol->pDflt->u.zToken : 0, -+ (isHidden>=2 || pColExpr==0) ? 0 : pColExpr->u.zToken, - k, - isHidden); - } -@@ -126934,6 +141911,86 @@ SQLITE_PRIVATE void sqlite3Pragma( - } - break; - -+ /* -+ ** PRAGMA table_list -+ ** -+ ** Return a single row for each table, virtual table, or view in the -+ ** entire schema. -+ ** -+ ** schema: Name of attached database hold this table -+ ** name: Name of the table itself -+ ** type: "table", "view", "virtual", "shadow" -+ ** ncol: Number of columns -+ ** wr: True for a WITHOUT ROWID table -+ ** strict: True for a STRICT table -+ */ -+ case PragTyp_TABLE_LIST: { -+ int ii; -+ pParse->nMem = 6; -+ sqlite3CodeVerifyNamedSchema(pParse, zDb); -+ for(ii=0; iinDb; ii++){ -+ HashElem *k; -+ Hash *pHash; -+ int initNCol; -+ if( zDb && sqlite3_stricmp(zDb, db->aDb[ii].zDbSName)!=0 ) continue; -+ -+ /* Ensure that the Table.nCol field is initialized for all views -+ ** and virtual tables. Each time we initialize a Table.nCol value -+ ** for a table, that can potentially disrupt the hash table, so restart -+ ** the initialization scan. -+ */ -+ pHash = &db->aDb[ii].pSchema->tblHash; -+ initNCol = sqliteHashCount(pHash); -+ while( initNCol-- ){ -+ for(k=sqliteHashFirst(pHash); 1; k=sqliteHashNext(k) ){ -+ Table *pTab; -+ if( k==0 ){ initNCol = 0; break; } -+ pTab = sqliteHashData(k); -+ if( pTab->nCol==0 ){ -+ char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName); -+ if( zSql ){ -+ sqlite3_stmt *pDummy = 0; -+ (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG, -+ &pDummy, 0); -+ (void)sqlite3_finalize(pDummy); -+ sqlite3DbFree(db, zSql); -+ } -+ if( db->mallocFailed ){ -+ sqlite3ErrorMsg(db->pParse, "out of memory"); -+ db->pParse->rc = SQLITE_NOMEM_BKPT; -+ } -+ pHash = &db->aDb[ii].pSchema->tblHash; -+ break; -+ } -+ } -+ } -+ -+ for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k) ){ -+ Table *pTab = sqliteHashData(k); -+ const char *zType; -+ if( zRight && sqlite3_stricmp(zRight, pTab->zName)!=0 ) continue; -+ if( IsView(pTab) ){ -+ zType = "view"; -+ }else if( IsVirtual(pTab) ){ -+ zType = "virtual"; -+ }else if( pTab->tabFlags & TF_Shadow ){ -+ zType = "shadow"; -+ }else{ -+ zType = "table"; -+ } -+ sqlite3VdbeMultiLoad(v, 1, "sssiii", -+ db->aDb[ii].zDbSName, -+ sqlite3PreferredTableName(pTab->zName), -+ zType, -+ pTab->nCol, -+ (pTab->tabFlags & TF_WithoutRowid)!=0, -+ (pTab->tabFlags & TF_Strict)!=0 -+ ); -+ } -+ } -+ } -+ break; -+ - #ifdef SQLITE_DEBUG - case PragTyp_STATS: { - Index *pIdx; -@@ -126943,7 +142000,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){ - Table *pTab = sqliteHashData(i); - sqlite3VdbeMultiLoad(v, 1, "ssiii", -- pTab->zName, -+ sqlite3PreferredTableName(pTab->zName), - 0, - pTab->szTabRow, - pTab->nRowLogEst, -@@ -126993,7 +142050,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - for(i=0; iaiColumn[i]; - sqlite3VdbeMultiLoad(v, 1, "iisX", i, cnum, -- cnum<0 ? 0 : pTab->aCol[cnum].zName); -+ cnum<0 ? 0 : pTab->aCol[cnum].zCnName); - if( pPragma->iArg ){ - sqlite3VdbeMultiLoad(v, 4, "isiX", - pIdx->aSortOrder[i], -@@ -127062,11 +142119,13 @@ SQLITE_PRIVATE void sqlite3Pragma( - pParse->nMem = 6; - for(i=0; iu.pHash ){ -+ assert( p->funcFlags & SQLITE_FUNC_BUILTIN ); - pragmaFunclistLine(v, p, 1, showInternFunc); - } - } - for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){ - p = (FuncDef*)sqliteHashData(j); -+ assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); - pragmaFunclistLine(v, p, 0, showInternFunc); - } - } -@@ -127100,8 +142159,8 @@ SQLITE_PRIVATE void sqlite3Pragma( - FKey *pFK; - Table *pTab; - pTab = sqlite3FindTable(db, zRight, zDb); -- if( pTab ){ -- pFK = pTab->pFKey; -+ if( pTab && IsOrdinaryTable(pTab) ){ -+ pFK = pTab->u.tab.pFKey; - if( pFK ){ - int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); - int i = 0; -@@ -127114,7 +142173,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - i, - j, - pFK->zTo, -- pTab->aCol[pFK->aCol[j].iFrom].zName, -+ pTab->aCol[pFK->aCol[j].iFrom].zCnName, - pFK->aCol[j].zCol, - actionName(pFK->aAction[1]), /* ON UPDATE */ - actionName(pFK->aAction[0]), /* ON DELETE */ -@@ -127141,7 +142200,6 @@ SQLITE_PRIVATE void sqlite3Pragma( - HashElem *k; /* Loop counter: Next table in schema */ - int x; /* result variable */ - int regResult; /* 3 registers to hold a result row */ -- int regKey; /* Register to hold key for checking the FK */ - int regRow; /* Registers to hold a row from pTab */ - int addrTop; /* Top of a loop checking foreign keys */ - int addrOk; /* Jump here if the key is OK */ -@@ -127149,7 +142207,6 @@ SQLITE_PRIVATE void sqlite3Pragma( - - regResult = pParse->nMem+1; - pParse->nMem += 4; -- regKey = ++pParse->nMem; - regRow = ++pParse->nMem; - k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); - while( k ){ -@@ -127160,15 +142217,16 @@ SQLITE_PRIVATE void sqlite3Pragma( - pTab = (Table*)sqliteHashData(k); - k = sqliteHashNext(k); - } -- if( pTab==0 || pTab->pFKey==0 ) continue; -+ if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue; - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - zDb = db->aDb[iDb].zDbSName; - sqlite3CodeVerifySchema(pParse, iDb); - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); -- if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; -+ sqlite3TouchRegister(pParse, pTab->nCol+regRow); - sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); - sqlite3VdbeLoadString(v, regResult, pTab->zName); -- for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ -+ assert( IsOrdinaryTable(pTab) ); -+ for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ - pParent = sqlite3FindTable(db, pFK->zTo, zDb); - if( pParent==0 ) continue; - pIdx = 0; -@@ -127190,13 +142248,14 @@ SQLITE_PRIVATE void sqlite3Pragma( - if( pFK ) break; - if( pParse->nTabnTab = i; - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v); -- for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ -+ assert( IsOrdinaryTable(pTab) ); -+ for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ - pParent = sqlite3FindTable(db, pFK->zTo, zDb); - pIdx = 0; - aiCols = 0; - if( pParent ){ - x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); -- assert( x==0 ); -+ assert( x==0 || db->mallocFailed ); - } - addrOk = sqlite3VdbeMakeLabel(pParse); - -@@ -127204,6 +142263,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** regRow..regRow+n. If any of the child key values are NULL, this - ** row cannot cause an FK violation. Jump directly to addrOk in - ** this case. */ -+ sqlite3TouchRegister(pParse, regRow + pFK->nCol); - for(j=0; jnCol; j++){ - int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; - sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); -@@ -127213,15 +142273,15 @@ SQLITE_PRIVATE void sqlite3Pragma( - /* Generate code to query the parent index for a matching parent - ** key. If a match is found, jump to addrOk. */ - if( pIdx ){ -- sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey, -+ sqlite3VdbeAddOp4(v, OP_Affinity, regRow, pFK->nCol, 0, - sqlite3IndexAffinityStr(db,pIdx), pFK->nCol); -- sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); -+ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regRow, pFK->nCol); - VdbeCoverage(v); - }else if( pParent ){ - int jmp = sqlite3VdbeCurrentAddr(v)+2; - sqlite3VdbeAddOp3(v, OP_SeekRowid, i, jmp, regRow); VdbeCoverage(v); - sqlite3VdbeGoto(v, addrOk); -- assert( pFK->nCol==1 ); -+ assert( pFK->nCol==1 || db->mallocFailed ); - } - - /* Generate code to report an FK violation to the caller. */ -@@ -127270,9 +142330,9 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** The "quick_check" is reduced version of - ** integrity_check designed to detect most database corruption - ** without the overhead of cross-checking indexes. Quick_check -- ** is linear time wherease integrity_check is O(NlogN). -+ ** is linear time whereas integrity_check is O(NlogN). - ** -- ** The maximum nubmer of errors is 100 by default. A different default -+ ** The maximum number of errors is 100 by default. A different default - ** can be specified using a numeric parameter N. - ** - ** Or, the parameter N can be the name of a table. In that case, only -@@ -127309,7 +142369,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - /* Set the maximum error count */ - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; - if( zRight ){ -- if( sqlite3GetInt32(zRight, &mxErr) ){ -+ if( sqlite3GetInt32(pValue->z, &mxErr) ){ - if( mxErr<=0 ){ - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; - } -@@ -127326,12 +142386,12 @@ SQLITE_PRIVATE void sqlite3Pragma( - Hash *pTbls; /* Set of all tables in the schema */ - int *aRoot; /* Array of root page numbers of all btrees */ - int cnt = 0; /* Number of entries in aRoot[] */ -- int mxIdx = 0; /* Maximum number of indexes for any table */ - - if( OMIT_TEMPDB && i==1 ) continue; - if( iDb>=0 && i!=iDb ) continue; - - sqlite3CodeVerifySchema(pParse, i); -+ pParse->okConstFactor = 0; /* tag-20230327-1 */ - - /* Do an integrity check of the B-Tree - ** -@@ -127347,7 +142407,6 @@ SQLITE_PRIVATE void sqlite3Pragma( - if( pObjTab && pObjTab!=pTab ) continue; - if( HasRowid(pTab) ) cnt++; - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } -- if( nIdx>mxIdx ) mxIdx = nIdx; - } - if( cnt==0 ) continue; - if( pObjTab ) cnt++; -@@ -127367,12 +142426,13 @@ SQLITE_PRIVATE void sqlite3Pragma( - aRoot[0] = cnt; - - /* Make sure sufficient number of registers have been allocated */ -- pParse->nMem = MAX( pParse->nMem, 8+mxIdx ); -+ sqlite3TouchRegister(pParse, 8+cnt); -+ sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); - sqlite3ClearTempRegCache(pParse); - - /* Do the b-tree integrity checks */ -- sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY); -- sqlite3VdbeChangeP5(v, (u8)i); -+ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); -+ sqlite3VdbeChangeP5(v, (u16)i); - addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, - sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName), -@@ -127381,19 +142441,59 @@ SQLITE_PRIVATE void sqlite3Pragma( - integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, addr); - -+ /* Check that the indexes all have the right number of rows */ -+ cnt = pObjTab ? 1 : 0; -+ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); -+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ -+ int iTab = 0; -+ Table *pTab = sqliteHashData(x); -+ Index *pIdx; -+ if( pObjTab && pObjTab!=pTab ) continue; -+ if( HasRowid(pTab) ){ -+ iTab = cnt++; -+ }else{ -+ iTab = cnt; -+ for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){ -+ if( IsPrimaryKeyIndex(pIdx) ) break; -+ iTab++; -+ } -+ } -+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ -+ if( pIdx->pPartIdxWhere==0 ){ -+ addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab); -+ VdbeCoverageNeverNull(v); -+ sqlite3VdbeLoadString(v, 4, pIdx->zName); -+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); -+ integrityCheckResultRow(v); -+ sqlite3VdbeJumpHere(v, addr); -+ } -+ cnt++; -+ } -+ } -+ - /* Make sure all the indices are constructed correctly. - */ - for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ - Table *pTab = sqliteHashData(x); - Index *pIdx, *pPk; -- Index *pPrior = 0; -+ Index *pPrior = 0; /* Previous index */ - int loopTop; - int iDataCur, iIdxCur; - int r1 = -1; -+ int bStrict; /* True for a STRICT table */ -+ int r2; /* Previous key for WITHOUT ROWID tables */ -+ int mxCol; /* Maximum non-virtual column number */ - -- if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ - if( pObjTab && pObjTab!=pTab ) continue; -- pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); -+ if( !IsOrdinaryTable(pTab) ) continue; -+ if( isQuick || HasRowid(pTab) ){ -+ pPk = 0; -+ r2 = 0; -+ }else{ -+ pPk = sqlite3PrimaryKeyIndex(pTab); -+ r2 = sqlite3GetTempRange(pParse, pPk->nKeyCol); -+ sqlite3VdbeAddOp3(v, OP_Null, 1, r2, r2+pPk->nKeyCol-1); -+ } - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, - 1, 0, &iDataCur, &iIdxCur); - /* reg[7] counts the number of entries in the table. -@@ -127407,27 +142507,181 @@ SQLITE_PRIVATE void sqlite3Pragma( - assert( sqlite3NoTempsInRange(pParse,1,7+j) ); - sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v); - loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1); -+ -+ /* Fetch the right-most column from the table. This will cause -+ ** the entire record header to be parsed and sanity checked. It -+ ** will also prepopulate the cursor column cache that is used -+ ** by the OP_IsType code, so it is a required step. -+ */ -+ assert( !IsVirtual(pTab) ); -+ if( HasRowid(pTab) ){ -+ mxCol = -1; -+ for(j=0; jnCol; j++){ -+ if( (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)==0 ) mxCol++; -+ } -+ if( mxCol==pTab->iPKey ) mxCol--; -+ }else{ -+ /* COLFLAG_VIRTUAL columns are not included in the WITHOUT ROWID -+ ** PK index column-count, so there is no need to account for them -+ ** in this case. */ -+ mxCol = sqlite3PrimaryKeyIndex(pTab)->nColumn-1; -+ } -+ if( mxCol>=0 ){ -+ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, mxCol, 3); -+ sqlite3VdbeTypeofColumn(v, 3); -+ } -+ - if( !isQuick ){ -- /* Sanity check on record header decoding */ -- sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3); -- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); -+ if( pPk ){ -+ /* Verify WITHOUT ROWID keys are in ascending order */ -+ int a1; -+ char *zErr; -+ a1 = sqlite3VdbeAddOp4Int(v, OP_IdxGT, iDataCur, 0,r2,pPk->nKeyCol); -+ VdbeCoverage(v); -+ sqlite3VdbeAddOp1(v, OP_IsNull, r2); VdbeCoverage(v); -+ zErr = sqlite3MPrintf(db, -+ "row not in PRIMARY KEY order for %s", -+ pTab->zName); -+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); -+ integrityCheckResultRow(v); -+ sqlite3VdbeJumpHere(v, a1); -+ sqlite3VdbeJumpHere(v, a1+1); -+ for(j=0; jnKeyCol; j++){ -+ sqlite3ExprCodeLoadIndexColumn(pParse, pPk, iDataCur, j, r2+j); -+ } -+ } - } -- /* Verify that all NOT NULL columns really are NOT NULL */ -+ /* Verify datatypes for all columns: -+ ** -+ ** (1) NOT NULL columns may not contain a NULL -+ ** (2) Datatype must be exact for non-ANY columns in STRICT tables -+ ** (3) Datatype for TEXT columns in non-STRICT tables must be -+ ** NULL, TEXT, or BLOB. -+ ** (4) Datatype for numeric columns in non-STRICT tables must not -+ ** be a TEXT value that can be losslessly converted to numeric. -+ */ -+ bStrict = (pTab->tabFlags & TF_Strict)!=0; - for(j=0; jnCol; j++){ - char *zErr; -- int jmp2; -+ Column *pCol = pTab->aCol + j; /* The column to be checked */ -+ int labelError; /* Jump here to report an error */ -+ int labelOk; /* Jump here if all looks ok */ -+ int p1, p3, p4; /* Operands to the OP_IsType opcode */ -+ int doTypeCheck; /* Check datatypes (besides NOT NULL) */ -+ - if( j==pTab->iPKey ) continue; -- if( pTab->aCol[j].notNull==0 ) continue; -- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); -- if( sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){ -- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); -- } -- jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v); -- zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, -- pTab->aCol[j].zName); -- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); -+ if( bStrict ){ -+ doTypeCheck = pCol->eCType>COLTYPE_ANY; -+ }else{ -+ doTypeCheck = pCol->affinity>SQLITE_AFF_BLOB; -+ } -+ if( pCol->notNull==0 && !doTypeCheck ) continue; -+ -+ /* Compute the operands that will be needed for OP_IsType */ -+ p4 = SQLITE_NULL; -+ if( pCol->colFlags & COLFLAG_VIRTUAL ){ -+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); -+ p1 = -1; -+ p3 = 3; -+ }else{ -+ if( pCol->iDflt ){ -+ sqlite3_value *pDfltValue = 0; -+ sqlite3ValueFromExpr(db, sqlite3ColumnExpr(pTab,pCol), ENC(db), -+ pCol->affinity, &pDfltValue); -+ if( pDfltValue ){ -+ p4 = sqlite3_value_type(pDfltValue); -+ sqlite3ValueFree(pDfltValue); -+ } -+ } -+ p1 = iDataCur; -+ if( !HasRowid(pTab) ){ -+ testcase( j!=sqlite3TableColumnToStorage(pTab, j) ); -+ p3 = sqlite3TableColumnToIndex(sqlite3PrimaryKeyIndex(pTab), j); -+ }else{ -+ p3 = sqlite3TableColumnToStorage(pTab,j); -+ testcase( p3!=j); -+ } -+ } -+ -+ labelError = sqlite3VdbeMakeLabel(pParse); -+ labelOk = sqlite3VdbeMakeLabel(pParse); -+ if( pCol->notNull ){ -+ /* (1) NOT NULL columns may not contain a NULL */ -+ int jmp3; -+ int jmp2 = sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); -+ VdbeCoverage(v); -+ if( p1<0 ){ -+ sqlite3VdbeChangeP5(v, 0x0f); /* INT, REAL, TEXT, or BLOB */ -+ jmp3 = jmp2; -+ }else{ -+ sqlite3VdbeChangeP5(v, 0x0d); /* INT, TEXT, or BLOB */ -+ /* OP_IsType does not detect NaN values in the database file -+ ** which should be treated as a NULL. So if the header type -+ ** is REAL, we have to load the actual data using OP_Column -+ ** to reliably determine if the value is a NULL. */ -+ sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3); -+ sqlite3ColumnDefault(v, pTab, j, 3); -+ jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk); -+ VdbeCoverage(v); -+ } -+ zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, -+ pCol->zCnName); -+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); -+ if( doTypeCheck ){ -+ sqlite3VdbeGoto(v, labelError); -+ sqlite3VdbeJumpHere(v, jmp2); -+ sqlite3VdbeJumpHere(v, jmp3); -+ }else{ -+ /* VDBE byte code will fall thru */ -+ } -+ } -+ if( bStrict && doTypeCheck ){ -+ /* (2) Datatype must be exact for non-ANY columns in STRICT tables*/ -+ static unsigned char aStdTypeMask[] = { -+ 0x1f, /* ANY */ -+ 0x18, /* BLOB */ -+ 0x11, /* INT */ -+ 0x11, /* INTEGER */ -+ 0x13, /* REAL */ -+ 0x14 /* TEXT */ -+ }; -+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); -+ assert( pCol->eCType>=1 && pCol->eCType<=sizeof(aStdTypeMask) ); -+ sqlite3VdbeChangeP5(v, aStdTypeMask[pCol->eCType-1]); -+ VdbeCoverage(v); -+ zErr = sqlite3MPrintf(db, "non-%s value in %s.%s", -+ sqlite3StdType[pCol->eCType-1], -+ pTab->zName, pTab->aCol[j].zCnName); -+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); -+ }else if( !bStrict && pCol->affinity==SQLITE_AFF_TEXT ){ -+ /* (3) Datatype for TEXT columns in non-STRICT tables must be -+ ** NULL, TEXT, or BLOB. */ -+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); -+ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */ -+ VdbeCoverage(v); -+ zErr = sqlite3MPrintf(db, "NUMERIC value in %s.%s", -+ pTab->zName, pTab->aCol[j].zCnName); -+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); -+ }else if( !bStrict && pCol->affinity>=SQLITE_AFF_NUMERIC ){ -+ /* (4) Datatype for numeric columns in non-STRICT tables must not -+ ** be a TEXT value that can be converted to numeric. */ -+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); -+ sqlite3VdbeChangeP5(v, 0x1b); /* NULL, INT, FLOAT, or BLOB */ -+ VdbeCoverage(v); -+ if( p1>=0 ){ -+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3); -+ } -+ sqlite3VdbeAddOp4(v, OP_Affinity, 3, 1, 0, "C", P4_STATIC); -+ sqlite3VdbeAddOp4Int(v, OP_IsType, -1, labelOk, 3, p4); -+ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */ -+ VdbeCoverage(v); -+ zErr = sqlite3MPrintf(db, "TEXT value in %s.%s", -+ pTab->zName, pTab->aCol[j].zCnName); -+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); -+ } -+ sqlite3VdbeResolveLabel(v, labelError); - integrityCheckResultRow(v); -- sqlite3VdbeJumpHere(v, jmp2); -+ sqlite3VdbeResolveLabel(v, labelOk); - } - /* Verify CHECK constraints */ - if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ -@@ -127456,7 +142710,8 @@ SQLITE_PRIVATE void sqlite3Pragma( - if( !isQuick ){ /* Omit the remaining tests for quick_check */ - /* Validate index entries for the current row */ - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ -- int jmp2, jmp3, jmp4, jmp5; -+ int jmp2, jmp3, jmp4, jmp5, label6; -+ int kk; - int ckUniq = sqlite3VdbeMakeLabel(pParse); - if( pPk==pIdx ) continue; - r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, -@@ -127474,13 +142729,49 @@ SQLITE_PRIVATE void sqlite3Pragma( - sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); - jmp4 = integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, jmp2); -+ -+ /* The OP_IdxRowid opcode is an optimized version of OP_Column -+ ** that extracts the rowid off the end of the index record. -+ ** But it only works correctly if index record does not have -+ ** any extra bytes at the end. Verify that this is the case. */ -+ if( HasRowid(pTab) ){ -+ int jmp7; -+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur+j, 3); -+ jmp7 = sqlite3VdbeAddOp3(v, OP_Eq, 3, 0, r1+pIdx->nColumn-1); -+ VdbeCoverageNeverNull(v); -+ sqlite3VdbeLoadString(v, 3, -+ "rowid not at end-of-record for row "); -+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); -+ sqlite3VdbeLoadString(v, 4, " of index "); -+ sqlite3VdbeGoto(v, jmp5-1); -+ sqlite3VdbeJumpHere(v, jmp7); -+ } -+ -+ /* Any indexed columns with non-BINARY collations must still hold -+ ** the exact same text value as the table. */ -+ label6 = 0; -+ for(kk=0; kknKeyCol; kk++){ -+ if( pIdx->azColl[kk]==sqlite3StrBINARY ) continue; -+ if( label6==0 ) label6 = sqlite3VdbeMakeLabel(pParse); -+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur+j, kk, 3); -+ sqlite3VdbeAddOp3(v, OP_Ne, 3, label6, r1+kk); VdbeCoverage(v); -+ } -+ if( label6 ){ -+ int jmp6 = sqlite3VdbeAddOp0(v, OP_Goto); -+ sqlite3VdbeResolveLabel(v, label6); -+ sqlite3VdbeLoadString(v, 3, "row "); -+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); -+ sqlite3VdbeLoadString(v, 4, " values differ from index "); -+ sqlite3VdbeGoto(v, jmp5-1); -+ sqlite3VdbeJumpHere(v, jmp6); -+ } -+ - /* For UNIQUE indexes, verify that only one entry exists with the - ** current key. The entry is unique if (1) any column is NULL - ** or (2) the next entry has a different key */ - if( IsUniqueIndex(pIdx) ){ - int uniqOk = sqlite3VdbeMakeLabel(pParse); - int jmp6; -- int kk; - for(kk=0; kknKeyCol; kk++){ - int iCol = pIdx->aiColumn[kk]; - assert( iCol!=XN_ROWID && iColnCol ); -@@ -127503,20 +142794,43 @@ SQLITE_PRIVATE void sqlite3Pragma( - } - sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); - sqlite3VdbeJumpHere(v, loopTop-1); -- if( !isQuick ){ -- sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); -- for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ -- if( pPk==pIdx ) continue; -- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); -- addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v); -- sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); -- sqlite3VdbeLoadString(v, 4, pIdx->zName); -- sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); -- integrityCheckResultRow(v); -- sqlite3VdbeJumpHere(v, addr); -- } -+ if( pPk ){ -+ assert( !isQuick ); -+ sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); - } - } -+ -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+ /* Second pass to invoke the xIntegrity method on all virtual -+ ** tables. -+ */ -+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ -+ Table *pTab = sqliteHashData(x); -+ sqlite3_vtab *pVTab; -+ int a1; -+ if( pObjTab && pObjTab!=pTab ) continue; -+ if( IsOrdinaryTable(pTab) ) continue; -+ if( !IsVirtual(pTab) ) continue; -+ if( pTab->nCol<=0 ){ -+ const char *zMod = pTab->u.vtab.azArg[0]; -+ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue; -+ } -+ sqlite3ViewGetColumnNames(pParse, pTab); -+ if( pTab->u.vtab.p==0 ) continue; -+ pVTab = pTab->u.vtab.p->pVtab; -+ if( NEVER(pVTab==0) ) continue; -+ if( NEVER(pVTab->pModule==0) ) continue; -+ if( pVTab->pModule->iVersion<4 ) continue; -+ if( pVTab->pModule->xIntegrity==0 ) continue; -+ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); -+ pTab->nTabRef++; -+ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); -+ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); -+ integrityCheckResultRow(v); -+ sqlite3VdbeJumpHere(v, a1); -+ continue; -+ } -+#endif - } - { - static const int iLn = VDBE_OFFSET_LINENO(2); -@@ -127665,6 +142979,11 @@ SQLITE_PRIVATE void sqlite3Pragma( - aOp[1].p2 = iCookie; - aOp[1].p3 = sqlite3Atoi(zRight); - aOp[1].p5 = 1; -+ if( iCookie==BTREE_SCHEMA_VERSION && (db->flags & SQLITE_Defensive)!=0 ){ -+ /* Do not allow the use of PRAGMA schema_version=VALUE in defensive -+ ** mode. Change the OP_SetCookie opcode into a no-op. */ -+ aOp[1].opcode = OP_Noop; -+ } - }else{ - /* Read the specified cookie value */ - static const VdbeOpList readCookie[] = { -@@ -127712,7 +143031,7 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** Checkpoint the database. - */ - case PragTyp_WAL_CHECKPOINT: { -- int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); -+ int iBt = (pId2->z?iDb:SQLITE_MAX_DB); - int eMode = SQLITE_CHECKPOINT_PASSIVE; - if( zRight ){ - if( sqlite3StrICmp(zRight, "full")==0 ){ -@@ -127775,44 +143094,63 @@ SQLITE_PRIVATE void sqlite3Pragma( - ** - ** The optional argument is a bitmask of optimizations to perform: - ** -- ** 0x0001 Debugging mode. Do not actually perform any optimizations -- ** but instead return one line of text for each optimization -- ** that would have been done. Off by default. -+ ** 0x00001 Debugging mode. Do not actually perform any optimizations -+ ** but instead return one line of text for each optimization -+ ** that would have been done. Off by default. - ** -- ** 0x0002 Run ANALYZE on tables that might benefit. On by default. -- ** See below for additional information. -+ ** 0x00002 Run ANALYZE on tables that might benefit. On by default. -+ ** See below for additional information. - ** -- ** 0x0004 (Not yet implemented) Record usage and performance -- ** information from the current session in the -- ** database file so that it will be available to "optimize" -- ** pragmas run by future database connections. -+ ** 0x00010 Run all ANALYZE operations using an analysis_limit that -+ ** is the lessor of the current analysis_limit and the -+ ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option. -+ ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is -+ ** currently (2024-02-19) set to 2000, which is such that -+ ** the worst case run-time for PRAGMA optimize on a 100MB -+ ** database will usually be less than 100 milliseconds on -+ ** a RaspberryPI-4 class machine. On by default. - ** -- ** 0x0008 (Not yet implemented) Create indexes that might have -- ** been helpful to recent queries -+ ** 0x10000 Look at tables to see if they need to be reanalyzed -+ ** due to growth or shrinkage even if they have not been -+ ** queried during the current connection. Off by default. - ** -- ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all -- ** of the optimizations listed above except Debug Mode, including new -- ** optimizations that have not yet been invented. If new optimizations are -- ** ever added that should be off by default, those off-by-default -- ** optimizations will have bitmasks of 0x10000 or larger. -+ ** The default MASK is and always shall be 0x0fffe. In the current -+ ** implementation, the default mask only covers the 0x00002 optimization, -+ ** though additional optimizations that are covered by 0x0fffe might be -+ ** added in the future. Optimizations that are off by default and must -+ ** be explicitly requested have masks of 0x10000 or greater. - ** - ** DETERMINATION OF WHEN TO RUN ANALYZE - ** - ** In the current implementation, a table is analyzed if only if all of - ** the following are true: - ** -- ** (1) MASK bit 0x02 is set. -+ ** (1) MASK bit 0x00002 is set. -+ ** -+ ** (2) The table is an ordinary table, not a virtual table or view. - ** -- ** (2) The query planner used sqlite_stat1-style statistics for one or -- ** more indexes of the table at some point during the lifetime of -- ** the current connection. -+ ** (3) The table name does not begin with "sqlite_". - ** -- ** (3) One or more indexes of the table are currently unanalyzed OR -- ** the number of rows in the table has increased by 25 times or more -- ** since the last time ANALYZE was run. -+ ** (4) One or more of the following is true: -+ ** (4a) The 0x10000 MASK bit is set. -+ ** (4b) One or more indexes on the table lacks an entry -+ ** in the sqlite_stat1 table. -+ ** (4c) The query planner used sqlite_stat1-style statistics for one -+ ** or more indexes of the table at some point during the lifetime -+ ** of the current connection. -+ ** -+ ** (5) One or more of the following is true: -+ ** (5a) One or more indexes on the table lacks an entry -+ ** in the sqlite_stat1 table. (Same as 4a) -+ ** (5b) The number of rows in the table has increased or decreased by -+ ** 10-fold. In other words, the current size of the table is -+ ** 10 times larger than the size in sqlite_stat1 or else the -+ ** current size is less than 1/10th the size in sqlite_stat1. - ** - ** The rules for when tables are analyzed are likely to change in -- ** future releases. -+ ** future releases. Future versions of SQLite might accept a string -+ ** literal argument to this pragma that contains a mnemonic description -+ ** of the options rather than a bitmap. - */ - case PragTyp_OPTIMIZE: { - int iDbLast; /* Loop termination point for the schema loop */ -@@ -127821,9 +143159,13 @@ SQLITE_PRIVATE void sqlite3Pragma( - Schema *pSchema; /* The current schema */ - Table *pTab; /* A table in the schema */ - Index *pIdx; /* An index of the table */ -- LogEst szThreshold; /* Size threshold above which reanalysis is needd */ -+ LogEst szThreshold; /* Size threshold above which reanalysis needed */ - char *zSubSql; /* SQL statement for the OP_SqlExec opcode */ - u32 opMask; /* Mask of operations to perform */ -+ int nLimit; /* Analysis limit to use */ -+ int nCheck = 0; /* Number of tables to be optimized */ -+ int nBtree = 0; /* Number of btrees to scan */ -+ int nIndex; /* Number of indexes on the current table */ - - if( zRight ){ - opMask = (u32)sqlite3Atoi(zRight); -@@ -127831,6 +143173,14 @@ SQLITE_PRIVATE void sqlite3Pragma( - }else{ - opMask = 0xfffe; - } -+ if( (opMask & 0x10)==0 ){ -+ nLimit = 0; -+ }else if( db->nAnalysisLimit>0 -+ && db->nAnalysisLimitnTab++; - for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){ - if( iDb==1 ) continue; -@@ -127839,23 +143189,61 @@ SQLITE_PRIVATE void sqlite3Pragma( - for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ - pTab = (Table*)sqliteHashData(k); - -- /* If table pTab has not been used in a way that would benefit from -- ** having analysis statistics during the current session, then skip it. -- ** This also has the effect of skipping virtual tables and views */ -- if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue; -+ /* This only works for ordinary tables */ -+ if( !IsOrdinaryTable(pTab) ) continue; - -- /* Reanalyze if the table is 25 times larger than the last analysis */ -- szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 ); -+ /* Do not scan system tables */ -+ if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue; -+ -+ /* Find the size of the table as last recorded in sqlite_stat1. -+ ** If any index is unanalyzed, then the threshold is -1 to -+ ** indicate a new, unanalyzed index -+ */ -+ szThreshold = pTab->nRowLogEst; -+ nIndex = 0; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ -+ nIndex++; - if( !pIdx->hasStat1 ){ -- szThreshold = 0; /* Always analyze if any index lacks statistics */ -- break; -+ szThreshold = -1; /* Always analyze if any index lacks statistics */ - } - } -- if( szThreshold ){ -- sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); -- sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur, -- sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold); -+ -+ /* If table pTab has not been used in a way that would benefit from -+ ** having analysis statistics during the current session, then skip it, -+ ** unless the 0x10000 MASK bit is set. */ -+ if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){ -+ /* Check for size change if stat1 has been used for a query */ -+ }else if( opMask & 0x10000 ){ -+ /* Check for size change if 0x10000 is set */ -+ }else if( pTab->pIndex!=0 && szThreshold<0 ){ -+ /* Do analysis if unanalyzed indexes exists */ -+ }else{ -+ /* Otherwise, we can skip this table */ -+ continue; -+ } -+ -+ nCheck++; -+ if( nCheck==2 ){ -+ /* If ANALYZE might be invoked two or more times, hold a write -+ ** transaction for efficiency */ -+ sqlite3BeginWriteOperation(pParse, 0, iDb); -+ } -+ nBtree += nIndex+1; -+ -+ /* Reanalyze if the table is 10 times larger or smaller than -+ ** the last analysis. Unconditional reanalysis if there are -+ ** unanalyzed indexes. */ -+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); -+ if( szThreshold>=0 ){ -+ const LogEst iRange = 33; /* 10x size change */ -+ sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur, -+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1), -+ szThreshold>=iRange ? szThreshold-iRange : -1, -+ szThreshold+iRange); -+ VdbeCoverage(v); -+ }else{ -+ sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur, -+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1)); - VdbeCoverage(v); - } - zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"", -@@ -127865,11 +143253,27 @@ SQLITE_PRIVATE void sqlite3Pragma( - sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1); - }else{ -- sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC); -+ sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0, -+ zSubSql, P4_DYNAMIC); - } - } - } - sqlite3VdbeAddOp0(v, OP_Expire); -+ -+ /* In a schema with a large number of tables and indexes, scale back -+ ** the analysis_limit to avoid excess run-time in the worst case. -+ */ -+ if( !db->mallocFailed && nLimit>0 && nBtree>100 ){ -+ int iAddr, iEnd; -+ VdbeOp *aOp; -+ nLimit = 100*nLimit/nBtree; -+ if( nLimit<100 ) nLimit = 100; -+ aOp = sqlite3VdbeGetOp(v, 0); -+ iEnd = sqlite3VdbeCurrentAddr(v); -+ for(iAddr=0; iAddr=0 - ){ - db->nAnalysisLimit = (int)(N&0x7fffffff); - } -- returnSingleInt(v, db->nAnalysisLimit); -+ returnSingleInt(v, db->nAnalysisLimit); /* IMP: R-57594-65522 */ - break; - } - -@@ -128133,9 +143537,9 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - seen[0] = 0; - seen[1] = 0; - for(i=0; inConstraint; i++, pConstraint++){ -- if( pConstraint->usable==0 ) continue; -- if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - if( pConstraint->iColumn < pTab->iHidden ) continue; -+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; -+ if( pConstraint->usable==0 ) return SQLITE_CONSTRAINT; - j = pConstraint->iColumn - pTab->iHidden; - assert( j < 2 ); - seen[j] = i+1; -@@ -128148,12 +143552,13 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - j = seen[0]-1; - pIdxInfo->aConstraintUsage[j].argvIndex = 1; - pIdxInfo->aConstraintUsage[j].omit = 1; -- if( seen[1]==0 ) return SQLITE_OK; - pIdxInfo->estimatedCost = (double)20; - pIdxInfo->estimatedRows = 20; -- j = seen[1]-1; -- pIdxInfo->aConstraintUsage[j].argvIndex = 2; -- pIdxInfo->aConstraintUsage[j].omit = 1; -+ if( seen[1] ){ -+ j = seen[1]-1; -+ pIdxInfo->aConstraintUsage[j].argvIndex = 2; -+ pIdxInfo->aConstraintUsage[j].omit = 1; -+ } - return SQLITE_OK; - } - -@@ -128173,6 +143578,7 @@ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ - int i; - sqlite3_finalize(pCsr->pPragma); - pCsr->pPragma = 0; -+ pCsr->iRowid = 0; - for(i=0; iazArg); i++){ - sqlite3_free(pCsr->azArg[i]); - pCsr->azArg[i] = 0; -@@ -128313,7 +143719,8 @@ static const sqlite3_module pragmaVtabModule = { - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ -- 0 /* xShadowName */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - - /* -@@ -128360,7 +143767,7 @@ SQLITE_PRIVATE Module *sqlite3PragmaVtabRegister(sqlite3 *db, const char *zName) - */ - static void corruptSchema( - InitData *pData, /* Initialization context */ -- const char *zObj, /* Object being parsed at the point of error */ -+ char **azObj, /* Type and name of object being parsed */ - const char *zExtra /* Error information */ - ){ - sqlite3 *db = pData->db; -@@ -128368,14 +143775,23 @@ static void corruptSchema( - pData->rc = SQLITE_NOMEM_BKPT; - }else if( pData->pzErrMsg[0]!=0 ){ - /* A error message has already been generated. Do not overwrite it */ -- }else if( pData->mInitFlags & INITFLAG_AlterTable ){ -- *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra); -+ }else if( pData->mInitFlags & (INITFLAG_AlterMask) ){ -+ static const char *azAlterType[] = { -+ "rename", -+ "drop column", -+ "add column" -+ }; -+ *pData->pzErrMsg = sqlite3MPrintf(db, -+ "error in %s %s after %s: %s", azObj[0], azObj[1], -+ azAlterType[(pData->mInitFlags&INITFLAG_AlterMask)-1], -+ zExtra -+ ); - pData->rc = SQLITE_ERROR; - }else if( db->flags & SQLITE_WriteSchema ){ - pData->rc = SQLITE_CORRUPT_BKPT; - }else{ - char *z; -- if( zObj==0 ) zObj = "?"; -+ const char *zObj = azObj[1] ? azObj[1] : "?"; - z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj); - if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); - *pData->pzErrMsg = z; -@@ -128431,21 +143847,28 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char - UNUSED_PARAMETER2(NotUsed, argc); - assert( sqlite3_mutex_held(db->mutex) ); - db->mDbFlags |= DBFLAG_EncodingFixed; -+ if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ - pData->nInitRow++; - if( db->mallocFailed ){ -- corruptSchema(pData, argv[1], 0); -+ corruptSchema(pData, argv, 0); - return 1; - } - - assert( iDb>=0 && iDbnDb ); -- if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ - if( argv[3]==0 ){ -- corruptSchema(pData, argv[1], 0); -- }else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){ -+ corruptSchema(pData, argv, 0); -+ }else if( argv[4] -+ && 'c'==sqlite3UpperToLower[(unsigned char)argv[4][0]] -+ && 'r'==sqlite3UpperToLower[(unsigned char)argv[4][1]] ){ - /* Call the parser to process a CREATE TABLE, INDEX or VIEW. - ** But because db->init.busy is set to 1, no VDBE code is generated - ** or executed. All the parser does is build the internal data - ** structures that describe the table, index, or view. -+ ** -+ ** No other valid SQL statement, other than the variable CREATE statements, -+ ** can begin with the letters "C" and "R". Thus, it is not possible run -+ ** any other kind of statement while parsing the schema, even a corrupt -+ ** schema. - */ - int rc; - u8 saved_iDb = db->init.iDb; -@@ -128458,11 +143881,11 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char - || (db->init.newTnum>pData->mxPage && pData->mxPage>0) - ){ - if( sqlite3Config.bExtraSchemaChecks ){ -- corruptSchema(pData, argv[1], "invalid rootpage"); -+ corruptSchema(pData, argv, "invalid rootpage"); - } - } - db->init.orphanTrigger = 0; -- db->init.azInit = argv; -+ db->init.azInit = (const char**)argv; - pStmt = 0; - TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); - rc = db->errCode; -@@ -128477,13 +143900,14 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char - if( rc==SQLITE_NOMEM ){ - sqlite3OomFault(db); - }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){ -- corruptSchema(pData, argv[1], sqlite3_errmsg(db)); -+ corruptSchema(pData, argv, sqlite3_errmsg(db)); - } - } - } -+ db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */ - sqlite3_finalize(pStmt); - }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){ -- corruptSchema(pData, argv[1], 0); -+ corruptSchema(pData, argv, 0); - }else{ - /* If the SQL column is blank it means this is an index that - ** was created to be the PRIMARY KEY or to fulfill a UNIQUE -@@ -128494,7 +143918,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char - Index *pIndex; - pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); - if( pIndex==0 ){ -- corruptSchema(pData, argv[1], "orphan index"); -+ corruptSchema(pData, argv, "orphan index"); - }else - if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0 - || pIndex->tnum<2 -@@ -128502,7 +143926,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char - || sqlite3IndexHasDuplicateRootPage(pIndex) - ){ - if( sqlite3Config.bExtraSchemaChecks ){ -- corruptSchema(pData, argv[1], "invalid rootpage"); -+ corruptSchema(pData, argv, "invalid rootpage"); - } - } - } -@@ -128579,7 +144003,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl - ** on the b-tree database, open one now. If a transaction is opened, it - ** will be closed before this function returns. */ - sqlite3BtreeEnter(pDb->pBt); -- if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ -+ if( sqlite3BtreeTxnState(pDb->pBt)==SQLITE_TXN_NONE ){ - rc = sqlite3BtreeBeginTrans(pDb->pBt, 0, 0); - if( rc!=SQLITE_OK ){ - sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc)); -@@ -128705,18 +144129,22 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl - } - #endif - } -+ assert( pDb == &(db->aDb[iDb]) ); - if( db->mallocFailed ){ - rc = SQLITE_NOMEM_BKPT; - sqlite3ResetAllSchemasOfConnection(db); -- } -- if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ -- /* Black magic: If the SQLITE_NoSchemaError flag is set, then consider -- ** the schema loaded, even if errors occurred. In this situation the -- ** current sqlite3_prepare() operation will fail, but the following one -- ** will attempt to compile the supplied statement against whatever subset -- ** of the schema was loaded before the error occurred. The primary -- ** purpose of this is to allow access to the sqlite_schema table -- ** even when its contents have been corrupted. -+ pDb = &db->aDb[iDb]; -+ }else -+ if( rc==SQLITE_OK || ((db->flags&SQLITE_NoSchemaError) && rc!=SQLITE_NOMEM)){ -+ /* Hack: If the SQLITE_NoSchemaError flag is set, then consider -+ ** the schema loaded, even if errors (other than OOM) occurred. In -+ ** this situation the current sqlite3_prepare() operation will fail, -+ ** but the following one will attempt to compile the supplied statement -+ ** against whatever subset of the schema was loaded before the error -+ ** occurred. -+ ** -+ ** The primary purpose of this is to allow access to the sqlite_schema -+ ** table even when its contents have been corrupted. - */ - DbSetProperty(db, iDb, DB_SchemaLoaded); - rc = SQLITE_OK; -@@ -128822,10 +144250,11 @@ static void schemaIsValid(Parse *pParse){ - /* If there is not already a read-only (or read-write) transaction opened - ** on the b-tree database, open one now. If a transaction is opened, it - ** will be closed immediately after reading the meta-value. */ -- if( !sqlite3BtreeIsInReadTrans(pBt) ){ -+ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_NONE ){ - rc = sqlite3BtreeBeginTrans(pBt, 0, 0); - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ - sqlite3OomFault(db); -+ pParse->rc = SQLITE_NOMEM; - } - if( rc!=SQLITE_OK ) return; - openedTransaction = 1; -@@ -128837,8 +144266,8 @@ static void schemaIsValid(Parse *pParse){ - sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ -+ if( DbHasProperty(db, iDb, DB_SchemaLoaded) ) pParse->rc = SQLITE_SCHEMA; - sqlite3ResetOneSchema(db, iDb); -- pParse->rc = SQLITE_SCHEMA; - } - - /* Close the transaction, if one was opened. */ -@@ -128883,35 +144312,115 @@ SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ - } - - /* --** Deallocate a single AggInfo object -+** Free all memory allocations in the pParse object - */ --static void agginfoFree(sqlite3 *db, AggInfo *p){ -- sqlite3DbFree(db, p->aCol); -- sqlite3DbFree(db, p->aFunc); -- sqlite3DbFree(db, p); -+SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){ -+ sqlite3 *db = pParse->db; -+ assert( db!=0 ); -+ assert( db->pParse==pParse ); -+ assert( pParse->nested==0 ); -+#ifndef SQLITE_OMIT_SHARED_CACHE -+ if( pParse->aTableLock ) sqlite3DbNNFreeNN(db, pParse->aTableLock); -+#endif -+ while( pParse->pCleanup ){ -+ ParseCleanup *pCleanup = pParse->pCleanup; -+ pParse->pCleanup = pCleanup->pNext; -+ pCleanup->xCleanup(db, pCleanup->pPtr); -+ sqlite3DbNNFreeNN(db, pCleanup); -+ } -+ if( pParse->aLabel ) sqlite3DbNNFreeNN(db, pParse->aLabel); -+ if( pParse->pConstExpr ){ -+ sqlite3ExprListDelete(db, pParse->pConstExpr); -+ } -+ assert( db->lookaside.bDisable >= pParse->disableLookaside ); -+ db->lookaside.bDisable -= pParse->disableLookaside; -+ db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; -+ assert( pParse->db->pParse==pParse ); -+ db->pParse = pParse->pOuterParse; - } - - /* --** Free all memory allocations in the pParse object --*/ --SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){ -- sqlite3 *db = pParse->db; -- AggInfo *pThis = pParse->pAggList; -- while( pThis ){ -- AggInfo *pNext = pThis->pNext; -- agginfoFree(db, pThis); -- pThis = pNext; -- } -- sqlite3DbFree(db, pParse->aLabel); -- sqlite3ExprListDelete(db, pParse->pConstExpr); -- if( db ){ -- assert( db->lookaside.bDisable >= pParse->disableLookaside ); -- db->lookaside.bDisable -= pParse->disableLookaside; -- db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; -+** Add a new cleanup operation to a Parser. The cleanup should happen when -+** the parser object is destroyed. But, beware: the cleanup might happen -+** immediately. -+** -+** Use this mechanism for uncommon cleanups. There is a higher setup -+** cost for this mechanism (an extra malloc), so it should not be used -+** for common cleanups that happen on most calls. But for less -+** common cleanups, we save a single NULL-pointer comparison in -+** sqlite3ParseObjectReset(), which reduces the total CPU cycle count. -+** -+** If a memory allocation error occurs, then the cleanup happens immediately. -+** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the -+** pParse->earlyCleanup flag is set in that case. Calling code show verify -+** that test cases exist for which this happens, to guard against possible -+** use-after-free errors following an OOM. The preferred way to do this is -+** to immediately follow the call to this routine with: -+** -+** testcase( pParse->earlyCleanup ); -+** -+** This routine returns a copy of its pPtr input (the third parameter) -+** except if an early cleanup occurs, in which case it returns NULL. So -+** another way to check for early cleanup is to check the return value. -+** Or, stop using the pPtr parameter with this call and use only its -+** return value thereafter. Something like this: -+** -+** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj); -+*/ -+SQLITE_PRIVATE void *sqlite3ParserAddCleanup( -+ Parse *pParse, /* Destroy when this Parser finishes */ -+ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ -+ void *pPtr /* Pointer to object to be cleaned up */ -+){ -+ ParseCleanup *pCleanup; -+ if( sqlite3FaultSim(300) ){ -+ pCleanup = 0; -+ sqlite3OomFault(pParse->db); -+ }else{ -+ pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); -+ } -+ if( pCleanup ){ -+ pCleanup->pNext = pParse->pCleanup; -+ pParse->pCleanup = pCleanup; -+ pCleanup->pPtr = pPtr; -+ pCleanup->xCleanup = xCleanup; -+ }else{ -+ xCleanup(pParse->db, pPtr); -+ pPtr = 0; -+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) -+ pParse->earlyCleanup = 1; -+#endif - } -- pParse->disableLookaside = 0; -+ return pPtr; - } - -+/* -+** Turn bulk memory into a valid Parse object and link that Parse object -+** into database connection db. -+** -+** Call sqlite3ParseObjectReset() to undo this operation. -+** -+** Caution: Do not confuse this routine with sqlite3ParseObjectInit() which -+** is generated by Lemon. -+*/ -+SQLITE_PRIVATE void sqlite3ParseObjectInit(Parse *pParse, sqlite3 *db){ -+ memset(PARSE_HDR(pParse), 0, PARSE_HDR_SZ); -+ memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); -+ assert( db->pParse!=pParse ); -+ pParse->pOuterParse = db->pParse; -+ db->pParse = pParse; -+ pParse->db = db; -+ if( db->mallocFailed ) sqlite3ErrorMsg(pParse, "out of memory"); -+} -+ -+/* -+** Maximum number of times that we will try again to prepare a statement -+** that returns SQLITE_ERROR_RETRY. -+*/ -+#ifndef SQLITE_MAX_PREPARE_RETRY -+# define SQLITE_MAX_PREPARE_RETRY 25 -+#endif -+ - /* - ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. - */ -@@ -128924,16 +144433,28 @@ static int sqlite3Prepare( - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const char **pzTail /* OUT: End of parsed string */ - ){ -- char *zErrMsg = 0; /* Error message */ - int rc = SQLITE_OK; /* Result code */ - int i; /* Loop counter */ - Parse sParse; /* Parsing context */ - -- memset(&sParse, 0, PARSE_HDR_SZ); -+ /* sqlite3ParseObjectInit(&sParse, db); // inlined for performance */ -+ memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ); - memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); -- sParse.pReprepare = pReprepare; -+ sParse.pOuterParse = db->pParse; -+ db->pParse = &sParse; -+ sParse.db = db; -+ if( pReprepare ){ -+ sParse.pReprepare = pReprepare; -+ sParse.explain = sqlite3_stmt_isexplain((sqlite3_stmt*)pReprepare); -+ }else{ -+ assert( sParse.pReprepare==0 ); -+ } - assert( ppStmt && *ppStmt==0 ); -- /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ -+ if( db->mallocFailed ){ -+ sqlite3ErrorMsg(&sParse, "out of memory"); -+ db->errCode = rc = SQLITE_NOMEM; -+ goto end_prepare; -+ } - assert( sqlite3_mutex_held(db->mutex) ); - - /* For a long-term use prepared statement avoid the use of -@@ -128943,7 +144464,7 @@ static int sqlite3Prepare( - sParse.disableLookaside++; - DisableLookaside; - } -- sParse.disableVtab = (prepFlags & SQLITE_PREPARE_NO_VTAB)!=0; -+ sParse.prepFlags = prepFlags & 0xff; - - /* Check to verify that it is possible to get a read lock on all - ** database schemas. The inability to get a read lock indicates that -@@ -128984,9 +144505,10 @@ static int sqlite3Prepare( - } - } - -- sqlite3VtabUnlockList(db); -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+ if( db->pDisconnect ) sqlite3VtabUnlockList(db); -+#endif - -- sParse.db = db; - if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ - char *zSqlCopy; - int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; -@@ -128999,23 +144521,17 @@ static int sqlite3Prepare( - } - zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes); - if( zSqlCopy ){ -- sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg); -+ sqlite3RunParser(&sParse, zSqlCopy); - sParse.zTail = &zSql[sParse.zTail-zSqlCopy]; - sqlite3DbFree(db, zSqlCopy); - }else{ - sParse.zTail = &zSql[nBytes]; - } - }else{ -- sqlite3RunParser(&sParse, zSql, &zErrMsg); -+ sqlite3RunParser(&sParse, zSql); - } - assert( 0==sParse.nQueryLoop ); - -- if( sParse.rc==SQLITE_DONE ){ -- sParse.rc = SQLITE_OK; -- } -- if( sParse.checkSchema ){ -- schemaIsValid(&sParse); -- } - if( pzTail ){ - *pzTail = sParse.zTail; - } -@@ -129025,21 +144541,30 @@ static int sqlite3Prepare( - } - if( db->mallocFailed ){ - sParse.rc = SQLITE_NOMEM_BKPT; -+ sParse.checkSchema = 0; - } -- rc = sParse.rc; -- if( rc!=SQLITE_OK ){ -- if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe); -- assert(!(*ppStmt)); -+ if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){ -+ if( sParse.checkSchema && db->init.busy==0 ){ -+ schemaIsValid(&sParse); -+ } -+ if( sParse.pVdbe ){ -+ sqlite3VdbeFinalize(sParse.pVdbe); -+ } -+ assert( 0==(*ppStmt) ); -+ rc = sParse.rc; -+ if( sParse.zErrMsg ){ -+ sqlite3ErrorWithMsg(db, rc, "%s", sParse.zErrMsg); -+ sqlite3DbFree(db, sParse.zErrMsg); -+ }else{ -+ sqlite3Error(db, rc); -+ } - }else{ -+ assert( sParse.zErrMsg==0 ); - *ppStmt = (sqlite3_stmt*)sParse.pVdbe; -+ rc = SQLITE_OK; -+ sqlite3ErrorClear(db); - } - -- if( zErrMsg ){ -- sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg); -- sqlite3DbFree(db, zErrMsg); -- }else{ -- sqlite3Error(db, rc); -- } - - /* Delete any TriggerPrg structures allocated while parsing this statement. */ - while( sParse.pTriggerPrg ){ -@@ -129050,7 +144575,7 @@ static int sqlite3Prepare( - - end_prepare: - -- sqlite3ParserReset(&sParse); -+ sqlite3ParseObjectReset(&sParse); - return rc; - } - static int sqlite3LockAndPrepare( -@@ -129080,12 +144605,15 @@ static int sqlite3LockAndPrepare( - ** reset is considered a permanent error. */ - rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); - assert( rc==SQLITE_OK || *ppStmt==0 ); -- }while( rc==SQLITE_ERROR_RETRY -+ if( rc==SQLITE_OK || db->mallocFailed ) break; -+ }while( (rc==SQLITE_ERROR_RETRY && (cnt++)errMask)==rc ); -+ db->busyHandler.nBusy = 0; - sqlite3_mutex_leave(db->mutex); -+ assert( rc==SQLITE_OK || (*ppStmt)==0 ); - return rc; - } - -@@ -129218,12 +144746,24 @@ static int sqlite3Prepare16( - if( !sqlite3SafetyCheckOk(db)||zSql==0 ){ - return SQLITE_MISUSE_BKPT; - } -+ -+ /* Make sure nBytes is non-negative and correct. It should be the -+ ** number of bytes until the end of the input buffer or until the first -+ ** U+0000 character. If the input nBytes is odd, convert it into -+ ** an even number. If the input nBytes is negative, then the input -+ ** must be terminated by at least one U+0000 character */ - if( nBytes>=0 ){ - int sz; - const char *z = (const char*)zSql; - for(sz=0; szmutex); - zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); - if( zSql8 ){ -@@ -129237,7 +144777,7 @@ static int sqlite3Prepare16( - ** the same number of characters into the UTF-16 string. - */ - int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8)); -- *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed); -+ *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, nBytes, chars_parsed); - } - sqlite3DbFree(db, zSql8); - rc = sqlite3ApiExit(db, rc); -@@ -129320,7 +144860,7 @@ SQLITE_API int sqlite3_prepare16_v3( - */ - typedef struct DistinctCtx DistinctCtx; - struct DistinctCtx { -- u8 isTnct; /* True if the DISTINCT keyword is present */ -+ u8 isTnct; /* 0: Not distinct. 1: DISTICT 2: DISTINCT and ORDER BY */ - u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */ - int tabTnct; /* Ephemeral table used for DISTINCT processing */ - int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */ -@@ -129364,6 +144904,10 @@ struct SortCtx { - } aDefer[4]; - #endif - struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */ -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ int addrPush; /* First instruction to push data into sorter */ -+ int addrPushEnd; /* Last instruction that pushes data into sorter */ -+#endif - }; - #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ - -@@ -129375,6 +144919,7 @@ struct SortCtx { - ** If bFree==0, Leave the first Select object unfreed - */ - static void clearSelect(sqlite3 *db, Select *p, int bFree){ -+ assert( db!=0 ); - while( p ){ - Select *pPrior = p->pPrior; - sqlite3ExprListDelete(db, p->pEList); -@@ -129384,13 +144929,17 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){ - sqlite3ExprDelete(db, p->pHaving); - sqlite3ExprListDelete(db, p->pOrderBy); - sqlite3ExprDelete(db, p->pLimit); -+ if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); - #ifndef SQLITE_OMIT_WINDOWFUNC - if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){ - sqlite3WindowListDelete(db, p->pWinDefn); - } -+ while( p->pWin ){ -+ assert( p->pWin->ppThis==&p->pWin ); -+ sqlite3WindowUnlinkFromSelect(p->pWin); -+ } - #endif -- if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); -- if( bFree ) sqlite3DbFreeNN(db, p); -+ if( bFree ) sqlite3DbNNFreeNN(db, p); - p = pPrior; - bFree = 1; - } -@@ -129444,7 +144993,7 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( - pNew->addrOpenEphm[0] = -1; - pNew->addrOpenEphm[1] = -1; - pNew->nSelectRow = 0; -- if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc)); -+ if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); - pNew->pSrc = pSrc; - pNew->pWhere = pWhere; - pNew->pGroupBy = pGroupBy; -@@ -129474,6 +145023,9 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( - SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ - if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1); - } -+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){ -+ if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1); -+} - - /* - ** Return a pointer to the right-most SELECT statement in a compound. -@@ -129499,6 +145051,52 @@ static Select *findRightmost(Select *p){ - ** - ** If an illegal or unsupported join type is seen, then still return - ** a join type, but put an error in the pParse structure. -+** -+** These are the valid join types: -+** -+** -+** pA pB pC Return Value -+** ------- ----- ----- ------------ -+** CROSS - - JT_CROSS -+** INNER - - JT_INNER -+** LEFT - - JT_LEFT|JT_OUTER -+** LEFT OUTER - JT_LEFT|JT_OUTER -+** RIGHT - - JT_RIGHT|JT_OUTER -+** RIGHT OUTER - JT_RIGHT|JT_OUTER -+** FULL - - JT_LEFT|JT_RIGHT|JT_OUTER -+** FULL OUTER - JT_LEFT|JT_RIGHT|JT_OUTER -+** NATURAL INNER - JT_NATURAL|JT_INNER -+** NATURAL LEFT - JT_NATURAL|JT_LEFT|JT_OUTER -+** NATURAL LEFT OUTER JT_NATURAL|JT_LEFT|JT_OUTER -+** NATURAL RIGHT - JT_NATURAL|JT_RIGHT|JT_OUTER -+** NATURAL RIGHT OUTER JT_NATURAL|JT_RIGHT|JT_OUTER -+** NATURAL FULL - JT_NATURAL|JT_LEFT|JT_RIGHT -+** NATURAL FULL OUTER JT_NATRUAL|JT_LEFT|JT_RIGHT -+** -+** To preserve historical compatibly, SQLite also accepts a variety -+** of other non-standard and in many cases nonsensical join types. -+** This routine makes as much sense at it can from the nonsense join -+** type and returns a result. Examples of accepted nonsense join types -+** include but are not limited to: -+** -+** INNER CROSS JOIN -> same as JOIN -+** NATURAL CROSS JOIN -> same as NATURAL JOIN -+** OUTER LEFT JOIN -> same as LEFT JOIN -+** LEFT NATURAL JOIN -> same as NATURAL LEFT JOIN -+** LEFT RIGHT JOIN -> same as FULL JOIN -+** RIGHT OUTER FULL JOIN -> same as FULL JOIN -+** CROSS CROSS CROSS JOIN -> same as JOIN -+** -+** The only restrictions on the join type name are: -+** -+** * "INNER" cannot appear together with "OUTER", "LEFT", "RIGHT", -+** or "FULL". -+** -+** * "CROSS" cannot appear together with "OUTER", "LEFT", "RIGHT, -+** or "FULL". -+** -+** * If "OUTER" is present then there must also be one of -+** "LEFT", "RIGHT", or "FULL" - */ - SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ - int jointype = 0; -@@ -129511,13 +145109,13 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p - u8 nChar; /* Length of the keyword in characters */ - u8 code; /* Join type mask */ - } aKeyword[] = { -- /* natural */ { 0, 7, JT_NATURAL }, -- /* left */ { 6, 4, JT_LEFT|JT_OUTER }, -- /* outer */ { 10, 5, JT_OUTER }, -- /* right */ { 14, 5, JT_RIGHT|JT_OUTER }, -- /* full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, -- /* inner */ { 23, 5, JT_INNER }, -- /* cross */ { 28, 5, JT_INNER|JT_CROSS }, -+ /* (0) natural */ { 0, 7, JT_NATURAL }, -+ /* (1) left */ { 6, 4, JT_LEFT|JT_OUTER }, -+ /* (2) outer */ { 10, 5, JT_OUTER }, -+ /* (3) right */ { 14, 5, JT_RIGHT|JT_OUTER }, -+ /* (4) full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER }, -+ /* (5) inner */ { 23, 5, JT_INNER }, -+ /* (6) cross */ { 28, 5, JT_INNER|JT_CROSS }, - }; - int i, j; - apAll[0] = pA; -@@ -129540,18 +145138,15 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p - } - if( - (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || -- (jointype & JT_ERROR)!=0 -+ (jointype & JT_ERROR)!=0 || -+ (jointype & (JT_OUTER|JT_LEFT|JT_RIGHT))==JT_OUTER - ){ -- const char *zSp = " "; -- assert( pB!=0 ); -- if( pC==0 ){ zSp++; } -- sqlite3ErrorMsg(pParse, "unknown or unsupported join type: " -- "%T %T%s%T", pA, pB, zSp, pC); -- jointype = JT_INNER; -- }else if( (jointype & JT_OUTER)!=0 -- && (jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ){ -- sqlite3ErrorMsg(pParse, -- "RIGHT and FULL OUTER JOINs are not currently supported"); -+ const char *zSp1 = " "; -+ const char *zSp2 = " "; -+ if( pB==0 ){ zSp1++; } -+ if( pC==0 ){ zSp2++; } -+ sqlite3ErrorMsg(pParse, "unknown join type: " -+ "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC); - jointype = JT_INNER; - } - return jointype; -@@ -129561,19 +145156,61 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p - ** Return the index of a column in a table. Return -1 if the column - ** is not contained in the table. - */ --static int columnIndex(Table *pTab, const char *zCol){ -+SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol){ - int i; -- u8 h = sqlite3StrIHash(zCol); -- Column *pCol; -- for(pCol=pTab->aCol, i=0; inCol; pCol++, i++){ -- if( pCol->hName==h && sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; -+ u8 h; -+ const Column *aCol; -+ int nCol; -+ -+ h = sqlite3StrIHash(zCol); -+ aCol = pTab->aCol; -+ nCol = pTab->nCol; -+ -+ /* See if the aHx gives us a lucky match */ -+ i = pTab->aHx[h % sizeof(pTab->aHx)]; -+ assert( i=nCol ) break; - } - return -1; - } - - /* --** Search the first N tables in pSrc, from left to right, looking for a --** table that has a column named zCol. -+** Mark a subquery result column as having been used. -+*/ -+SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){ -+ assert( pItem!=0 ); -+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); -+ if( pItem->fg.isNestedFrom ){ -+ ExprList *pResults; -+ assert( pItem->fg.isSubquery ); -+ assert( pItem->u4.pSubq!=0 ); -+ assert( pItem->u4.pSubq->pSelect!=0 ); -+ pResults = pItem->u4.pSubq->pSelect->pEList; -+ assert( pResults!=0 ); -+ assert( iCol>=0 && iColnExpr ); -+ pResults->a[iCol].fg.bUsed = 1; -+ } -+} -+ -+/* -+** Search the tables iStart..iEnd (inclusive) in pSrc, looking for a -+** table that has a column named zCol. The search is left-to-right. -+** The first match found is returned. - ** - ** When found, set *piTab and *piCol to the table index and column index - ** of the matching column and return TRUE. -@@ -129582,22 +145219,27 @@ static int columnIndex(Table *pTab, const char *zCol){ - */ - static int tableAndColumnIndex( - SrcList *pSrc, /* Array of tables to search */ -- int N, /* Number of tables in pSrc->a[] to search */ -+ int iStart, /* First member of pSrc->a[] to check */ -+ int iEnd, /* Last member of pSrc->a[] to check */ - const char *zCol, /* Name of the column we are looking for */ - int *piTab, /* Write index of pSrc->a[] here */ - int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */ -- int bIgnoreHidden /* True to ignore hidden columns */ -+ int bIgnoreHidden /* Ignore hidden columns */ - ){ - int i; /* For looping over tables in pSrc */ - int iCol; /* Index of column matching zCol */ - -+ assert( iEndnSrc ); -+ assert( iStart>=0 ); - assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ -- for(i=0; ia[i].pTab, zCol); -+ -+ for(i=iStart; i<=iEnd; i++){ -+ iCol = sqlite3ColumnIndex(pSrc->a[i].pSTab, zCol); - if( iCol>=0 -- && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0) -+ && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pSTab->aCol[iCol])==0) - ){ - if( piTab ){ -+ sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol); - *piTab = i; - *piCol = iCol; - } -@@ -129608,63 +145250,19 @@ static int tableAndColumnIndex( - } - - /* --** This function is used to add terms implied by JOIN syntax to the --** WHERE clause expression of a SELECT statement. The new term, which --** is ANDed with the existing WHERE clause, is of the form: --** --** (tab1.col1 = tab2.col2) --** --** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the --** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is --** column iColRight of tab2. --*/ --static void addWhereTerm( -- Parse *pParse, /* Parsing context */ -- SrcList *pSrc, /* List of tables in FROM clause */ -- int iLeft, /* Index of first table to join in pSrc */ -- int iColLeft, /* Index of column in first table */ -- int iRight, /* Index of second table in pSrc */ -- int iColRight, /* Index of column in second table */ -- int isOuterJoin, /* True if this is an OUTER join */ -- Expr **ppWhere /* IN/OUT: The WHERE clause to add to */ --){ -- sqlite3 *db = pParse->db; -- Expr *pE1; -- Expr *pE2; -- Expr *pEq; -- -- assert( iLeftnSrc>iRight ); -- assert( pSrc->a[iLeft].pTab ); -- assert( pSrc->a[iRight].pTab ); -- -- pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); -- pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); -- -- pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2); -- if( pEq && isOuterJoin ){ -- ExprSetProperty(pEq, EP_FromJoin); -- assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); -- ExprSetVVAProperty(pEq, EP_NoReduce); -- pEq->iRightJoinTable = (i16)pE2->iTable; -- } -- *ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq); --} -- --/* --** Set the EP_FromJoin property on all terms of the given expression. --** And set the Expr.iRightJoinTable to iTable for every term in the -+** Set the EP_OuterON property on all terms of the given expression. -+** And set the Expr.w.iJoin to iTable for every term in the - ** expression. - ** --** The EP_FromJoin property is used on terms of an expression to tell --** the LEFT OUTER JOIN processing logic that this term is part of the -+** The EP_OuterON property is used on terms of an expression to tell -+** the OUTER JOIN processing logic that this term is part of the - ** join restriction specified in the ON or USING clause and not a part - ** of the more general WHERE clause. These terms are moved over to the - ** WHERE clause during join processing but we need to remember that they - ** originated in the ON or USING clause. - ** --** The Expr.iRightJoinTable tells the WHERE clause processing that the --** expression depends on table iRightJoinTable even if that table is not -+** The Expr.w.iJoin tells the WHERE clause processing that the -+** expression depends on table w.iJoin even if that table is not - ** explicitly mentioned in the expression. That information is needed - ** for cases like this: - ** -@@ -129677,114 +145275,132 @@ static void addWhereTerm( - ** after the t1 loop and rows with t1.x!=5 will never appear in - ** the output, which is incorrect. - */ --SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable){ -+SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){ -+ assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON ); - while( p ){ -- ExprSetProperty(p, EP_FromJoin); -+ ExprSetProperty(p, joinFlag); - assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); - ExprSetVVAProperty(p, EP_NoReduce); -- p->iRightJoinTable = (i16)iTable; -- if( p->op==TK_FUNCTION && p->x.pList ){ -- int i; -- for(i=0; ix.pList->nExpr; i++){ -- sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable); -+ p->w.iJoin = iTable; -+ if( p->op==TK_FUNCTION ){ -+ assert( ExprUseXList(p) ); -+ if( p->x.pList ){ -+ int i; -+ for(i=0; ix.pList->nExpr; i++){ -+ sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag); -+ } - } - } -- sqlite3SetJoinExpr(p->pLeft, iTable); -+ sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag); - p = p->pRight; - } - } - --/* Undo the work of sqlite3SetJoinExpr(). In the expression p, convert every --** term that is marked with EP_FromJoin and iRightJoinTable==iTable into --** an ordinary term that omits the EP_FromJoin mark. -+/* Undo the work of sqlite3SetJoinExpr(). This is used when a LEFT JOIN -+** is simplified into an ordinary JOIN, and when an ON expression is -+** "pushed down" into the WHERE clause of a subquery. -+** -+** Convert every term that is marked with EP_OuterON and w.iJoin==iTable into -+** an ordinary term that omits the EP_OuterON mark. Or if iTable<0, then -+** just clear every EP_OuterON and EP_InnerON mark from the expression tree. - ** --** This happens when a LEFT JOIN is simplified into an ordinary JOIN. -+** If nullable is true, that means that Expr p might evaluate to NULL even -+** if it is a reference to a NOT NULL column. This can happen, for example, -+** if the table that p references is on the left side of a RIGHT JOIN. -+** If nullable is true, then take care to not remove the EP_CanBeNull bit. -+** See forum thread https://sqlite.org/forum/forumpost/b40696f50145d21c - */ --static void unsetJoinExpr(Expr *p, int iTable){ -+static void unsetJoinExpr(Expr *p, int iTable, int nullable){ - while( p ){ -- if( ExprHasProperty(p, EP_FromJoin) -- && (iTable<0 || p->iRightJoinTable==iTable) ){ -- ExprClearProperty(p, EP_FromJoin); -+ if( iTable<0 || (ExprHasProperty(p, EP_OuterON) && p->w.iJoin==iTable) ){ -+ ExprClearProperty(p, EP_OuterON|EP_InnerON); -+ if( iTable>=0 ) ExprSetProperty(p, EP_InnerON); - } -- if( p->op==TK_FUNCTION && p->x.pList ){ -- int i; -- for(i=0; ix.pList->nExpr; i++){ -- unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); -+ if( p->op==TK_COLUMN && p->iTable==iTable && !nullable ){ -+ ExprClearProperty(p, EP_CanBeNull); -+ } -+ if( p->op==TK_FUNCTION ){ -+ assert( ExprUseXList(p) ); -+ assert( p->pLeft==0 ); -+ if( p->x.pList ){ -+ int i; -+ for(i=0; ix.pList->nExpr; i++){ -+ unsetJoinExpr(p->x.pList->a[i].pExpr, iTable, nullable); -+ } - } - } -- unsetJoinExpr(p->pLeft, iTable); -+ unsetJoinExpr(p->pLeft, iTable, nullable); - p = p->pRight; - } - } - - /* - ** This routine processes the join information for a SELECT statement. --** ON and USING clauses are converted into extra terms of the WHERE clause. --** NATURAL joins also create extra WHERE clause terms. -+** -+** * A NATURAL join is converted into a USING join. After that, we -+** do not need to be concerned with NATURAL joins and we only have -+** think about USING joins. -+** -+** * ON and USING clauses result in extra terms being added to the -+** WHERE clause to enforce the specified constraints. The extra -+** WHERE clause terms will be tagged with EP_OuterON or -+** EP_InnerON so that we know that they originated in ON/USING. - ** - ** The terms of a FROM clause are contained in the Select.pSrc structure. - ** The left most table is the first entry in Select.pSrc. The right-most - ** table is the last entry. The join operator is held in the entry to --** the left. Thus entry 0 contains the join operator for the join between -+** the right. Thus entry 1 contains the join operator for the join between - ** entries 0 and 1. Any ON or USING clauses associated with the join are --** also attached to the left entry. -+** also attached to the right entry. - ** - ** This routine returns the number of errors encountered. - */ --static int sqliteProcessJoin(Parse *pParse, Select *p){ -+static int sqlite3ProcessJoin(Parse *pParse, Select *p){ - SrcList *pSrc; /* All tables in the FROM clause */ - int i, j; /* Loop counters */ -- struct SrcList_item *pLeft; /* Left table being joined */ -- struct SrcList_item *pRight; /* Right table being joined */ -+ SrcItem *pLeft; /* Left table being joined */ -+ SrcItem *pRight; /* Right table being joined */ - - pSrc = p->pSrc; - pLeft = &pSrc->a[0]; - pRight = &pLeft[1]; - for(i=0; inSrc-1; i++, pRight++, pLeft++){ -- Table *pRightTab = pRight->pTab; -- int isOuter; -+ Table *pRightTab = pRight->pSTab; -+ u32 joinType; - -- if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; -- isOuter = (pRight->fg.jointype & JT_OUTER)!=0; -+ if( NEVER(pLeft->pSTab==0 || pRightTab==0) ) continue; -+ joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; - -- /* When the NATURAL keyword is present, add WHERE clause terms for -- ** every column that the two tables have in common. -+ /* If this is a NATURAL join, synthesize an appropriate USING clause -+ ** to specify which columns should be joined. - */ - if( pRight->fg.jointype & JT_NATURAL ){ -- if( pRight->pOn || pRight->pUsing ){ -+ IdList *pUsing = 0; -+ if( pRight->fg.isUsing || pRight->u3.pOn ){ - sqlite3ErrorMsg(pParse, "a NATURAL join may not have " - "an ON or USING clause", 0); - return 1; - } - for(j=0; jnCol; j++){ - char *zName; /* Name of column in the right table */ -- int iLeft; /* Matching left table */ -- int iLeftCol; /* Matching column in the left table */ - - if( IsHiddenColumn(&pRightTab->aCol[j]) ) continue; -- zName = pRightTab->aCol[j].zName; -- if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 1) ){ -- addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j, -- isOuter, &p->pWhere); -+ zName = pRightTab->aCol[j].zCnName; -+ if( tableAndColumnIndex(pSrc, 0, i, zName, 0, 0, 1) ){ -+ pUsing = sqlite3IdListAppend(pParse, pUsing, 0); -+ if( pUsing ){ -+ assert( pUsing->nId>0 ); -+ assert( pUsing->a[pUsing->nId-1].zName==0 ); -+ pUsing->a[pUsing->nId-1].zName = sqlite3DbStrDup(pParse->db, zName); -+ } - } - } -- } -- -- /* Disallow both ON and USING clauses in the same join -- */ -- if( pRight->pOn && pRight->pUsing ){ -- sqlite3ErrorMsg(pParse, "cannot have both ON and USING " -- "clauses in the same join"); -- return 1; -- } -- -- /* Add the ON clause to the end of the WHERE clause, connected by -- ** an AND operator. -- */ -- if( pRight->pOn ){ -- if( isOuter ) sqlite3SetJoinExpr(pRight->pOn, pRight->iCursor); -- p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->pOn); -- pRight->pOn = 0; -+ if( pUsing ){ -+ pRight->fg.isUsing = 1; -+ pRight->fg.isSynthUsing = 1; -+ pRight->u3.pUsing = pUsing; -+ } -+ if( pParse->nErr ) return 1; - } - - /* Create extra terms on the WHERE clause for each column named -@@ -129794,27 +145410,96 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ - ** Report an error if any column mentioned in the USING clause is - ** not contained in both tables to be joined. - */ -- if( pRight->pUsing ){ -- IdList *pList = pRight->pUsing; -+ if( pRight->fg.isUsing ){ -+ IdList *pList = pRight->u3.pUsing; -+ sqlite3 *db = pParse->db; -+ assert( pList!=0 ); - for(j=0; jnId; j++){ - char *zName; /* Name of the term in the USING clause */ - int iLeft; /* Table on the left with matching column name */ - int iLeftCol; /* Column number of matching column on the left */ - int iRightCol; /* Column number of matching column on the right */ -+ Expr *pE1; /* Reference to the column on the LEFT of the join */ -+ Expr *pE2; /* Reference to the column on the RIGHT of the join */ -+ Expr *pEq; /* Equality constraint. pE1 == pE2 */ - - zName = pList->a[j].zName; -- iRightCol = columnIndex(pRightTab, zName); -+ iRightCol = sqlite3ColumnIndex(pRightTab, zName); - if( iRightCol<0 -- || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0) -+ || tableAndColumnIndex(pSrc, 0, i, zName, &iLeft, &iLeftCol, -+ pRight->fg.isSynthUsing)==0 - ){ - sqlite3ErrorMsg(pParse, "cannot join using column %s - column " - "not present in both tables", zName); - return 1; - } -- addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol, -- isOuter, &p->pWhere); -+ pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); -+ sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); -+ if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 && pParse->nErr==0 ){ -+ /* This branch runs if the query contains one or more RIGHT or FULL -+ ** JOINs. If only a single table on the left side of this join -+ ** contains the zName column, then this branch is a no-op. -+ ** But if there are two or more tables on the left side -+ ** of the join, construct a coalesce() function that gathers all -+ ** such tables. Raise an error if more than one of those references -+ ** to zName is not also within a prior USING clause. -+ ** -+ ** We really ought to raise an error if there are two or more -+ ** non-USING references to zName on the left of an INNER or LEFT -+ ** JOIN. But older versions of SQLite do not do that, so we avoid -+ ** adding a new error so as to not break legacy applications. -+ */ -+ ExprList *pFuncArgs = 0; /* Arguments to the coalesce() */ -+ static const Token tkCoalesce = { "coalesce", 8 }; -+ assert( pE1!=0 ); -+ ExprSetProperty(pE1, EP_CanBeNull); -+ while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol, -+ pRight->fg.isSynthUsing)!=0 ){ -+ if( pSrc->a[iLeft].fg.isUsing==0 -+ || sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0 -+ ){ -+ sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()", -+ zName); -+ break; -+ } -+ pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1); -+ pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol); -+ sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol); -+ } -+ if( pFuncArgs ){ -+ pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1); -+ pE1 = sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0); -+ if( pE1 ){ -+ pE1->affExpr = SQLITE_AFF_DEFER; -+ } -+ } -+ }else if( (pSrc->a[i+1].fg.jointype & JT_LEFT)!=0 && pParse->nErr==0 ){ -+ assert( pE1!=0 ); -+ ExprSetProperty(pE1, EP_CanBeNull); -+ } -+ pE2 = sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol); -+ sqlite3SrcItemColumnUsed(pRight, iRightCol); -+ pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2); -+ assert( pE2!=0 || pEq==0 ); -+ if( pEq ){ -+ ExprSetProperty(pEq, joinType); -+ assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) ); -+ ExprSetVVAProperty(pEq, EP_NoReduce); -+ pEq->w.iJoin = pE2->iTable; -+ } -+ p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pEq); - } - } -+ -+ /* Add the ON clause to the end of the WHERE clause, connected by -+ ** an AND operator. -+ */ -+ else if( pRight->u3.pOn ){ -+ sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType); -+ p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn); -+ pRight->u3.pOn = 0; -+ pRight->fg.isOn = 1; -+ } - } - return 0; - } -@@ -129908,14 +145593,18 @@ static void pushOntoSorter( - ** (2) All output columns are included in the sort record. In that - ** case regData==regOrigData. - ** (3) Some output columns are omitted from the sort record due to -- ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the -+ ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the - ** SQLITE_ECEL_OMITREF optimization, or due to the -- ** SortCtx.pDeferredRowLoad optimiation. In any of these cases -+ ** SortCtx.pDeferredRowLoad optimization. In any of these cases - ** regOrigData is 0 to prevent this routine from trying to copy - ** values that might not yet exist. - */ - assert( nData==1 || regData==regOrigData || regOrigData==0 ); - -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ pSort->addrPush = sqlite3VdbeCurrentAddr(v); -+#endif -+ - if( nPrefixReg ){ - assert( nPrefixReg==nExpr+bSeq ); - regBase = regData - nPrefixReg; -@@ -129962,7 +145651,7 @@ static void pushOntoSorter( - testcase( pKI->nAllField > pKI->nKeyField+2 ); - pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat, - pKI->nAllField-pKI->nKeyField-1); -- pOp = 0; /* Ensure pOp not used after sqltie3VdbeAddOp3() */ -+ pOp = 0; /* Ensure pOp not used after sqlite3VdbeAddOp3() */ - addrJmp = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); - pSort->labelBkOut = sqlite3VdbeMakeLabel(pParse); -@@ -130016,6 +145705,9 @@ static void pushOntoSorter( - sqlite3VdbeChangeP2(v, iSkip, - pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v)); - } -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ pSort->addrPushEnd = sqlite3VdbeCurrentAddr(v)-1; -+#endif - } - - /* -@@ -130033,31 +145725,157 @@ static void codeOffset( - } - - /* --** Add code that will check to make sure the N registers starting at iMem --** form a distinct entry. iTab is a sorting index that holds previously --** seen combinations of the N values. A new entry is made in iTab --** if the current N values are new. -+** Add code that will check to make sure the array of registers starting at -+** iMem form a distinct entry. This is used by both "SELECT DISTINCT ..." and -+** distinct aggregates ("SELECT count(DISTINCT ) ..."). Three strategies -+** are available. Which is used depends on the value of parameter eTnctType, -+** as follows: - ** --** A jump to addrRepeat is made and the N+1 values are popped from the --** stack if the top N elements are not distinct. --*/ --static void codeDistinct( -+** WHERE_DISTINCT_UNORDERED/WHERE_DISTINCT_NOOP: -+** Build an ephemeral table that contains all entries seen before and -+** skip entries which have been seen before. -+** -+** Parameter iTab is the cursor number of an ephemeral table that must -+** be opened before the VM code generated by this routine is executed. -+** The ephemeral cursor table is queried for a record identical to the -+** record formed by the current array of registers. If one is found, -+** jump to VM address addrRepeat. Otherwise, insert a new record into -+** the ephemeral cursor and proceed. -+** -+** The returned value in this case is a copy of parameter iTab. -+** -+** WHERE_DISTINCT_ORDERED: -+** In this case rows are being delivered sorted order. The ephemeral -+** table is not required. Instead, the current set of values -+** is compared against previous row. If they match, the new row -+** is not distinct and control jumps to VM address addrRepeat. Otherwise, -+** the VM program proceeds with processing the new row. -+** -+** The returned value in this case is the register number of the first -+** in an array of registers used to store the previous result row so that -+** it can be compared to the next. The caller must ensure that this -+** register is initialized to NULL. (The fixDistinctOpenEph() routine -+** will take care of this initialization.) -+** -+** WHERE_DISTINCT_UNIQUE: -+** In this case it has already been determined that the rows are distinct. -+** No special action is required. The return value is zero. -+** -+** Parameter pEList is the list of expressions used to generated the -+** contents of each row. It is used by this routine to determine (a) -+** how many elements there are in the array of registers and (b) the -+** collation sequences that should be used for the comparisons if -+** eTnctType is WHERE_DISTINCT_ORDERED. -+*/ -+static int codeDistinct( - Parse *pParse, /* Parsing and code generating context */ -+ int eTnctType, /* WHERE_DISTINCT_* value */ - int iTab, /* A sorting index used to test for distinctness */ - int addrRepeat, /* Jump to here if not distinct */ -- int N, /* Number of elements */ -- int iMem /* First element */ -+ ExprList *pEList, /* Expression for each element */ -+ int regElem /* First element */ - ){ -- Vdbe *v; -- int r1; -+ int iRet = 0; -+ int nResultCol = pEList->nExpr; -+ Vdbe *v = pParse->pVdbe; - -- v = pParse->pVdbe; -- r1 = sqlite3GetTempReg(pParse); -- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v); -- sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); -- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, iMem, N); -- sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); -- sqlite3ReleaseTempReg(pParse, r1); -+ switch( eTnctType ){ -+ case WHERE_DISTINCT_ORDERED: { -+ int i; -+ int iJump; /* Jump destination */ -+ int regPrev; /* Previous row content */ -+ -+ /* Allocate space for the previous row */ -+ iRet = regPrev = pParse->nMem+1; -+ pParse->nMem += nResultCol; -+ -+ iJump = sqlite3VdbeCurrentAddr(v) + nResultCol; -+ for(i=0; ia[i].pExpr); -+ if( idb->mallocFailed ); -+ sqlite3VdbeAddOp3(v, OP_Copy, regElem, regPrev, nResultCol-1); -+ break; -+ } -+ -+ case WHERE_DISTINCT_UNIQUE: { -+ /* nothing to do */ -+ break; -+ } -+ -+ default: { -+ int r1 = sqlite3GetTempReg(pParse); -+ sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, regElem, nResultCol); -+ VdbeCoverage(v); -+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regElem, nResultCol, r1); -+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, regElem, nResultCol); -+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); -+ sqlite3ReleaseTempReg(pParse, r1); -+ iRet = iTab; -+ break; -+ } -+ } -+ -+ return iRet; -+} -+ -+/* -+** This routine runs after codeDistinct(). It makes necessary -+** adjustments to the OP_OpenEphemeral opcode that the codeDistinct() -+** routine made use of. This processing must be done separately since -+** sometimes codeDistinct is called before the OP_OpenEphemeral is actually -+** laid down. -+** -+** WHERE_DISTINCT_NOOP: -+** WHERE_DISTINCT_UNORDERED: -+** -+** No adjustments necessary. This function is a no-op. -+** -+** WHERE_DISTINCT_UNIQUE: -+** -+** The ephemeral table is not needed. So change the -+** OP_OpenEphemeral opcode into an OP_Noop. -+** -+** WHERE_DISTINCT_ORDERED: -+** -+** The ephemeral table is not needed. But we do need register -+** iVal to be initialized to NULL. So change the OP_OpenEphemeral -+** into an OP_Null on the iVal register. -+*/ -+static void fixDistinctOpenEph( -+ Parse *pParse, /* Parsing and code generating context */ -+ int eTnctType, /* WHERE_DISTINCT_* value */ -+ int iVal, /* Value returned by codeDistinct() */ -+ int iOpenEphAddr /* Address of OP_OpenEphemeral instruction for iTab */ -+){ -+ if( pParse->nErr==0 -+ && (eTnctType==WHERE_DISTINCT_UNIQUE || eTnctType==WHERE_DISTINCT_ORDERED) -+ ){ -+ Vdbe *v = pParse->pVdbe; -+ sqlite3VdbeChangeToNoop(v, iOpenEphAddr); -+ if( sqlite3VdbeGetOp(v, iOpenEphAddr+1)->opcode==OP_Explain ){ -+ sqlite3VdbeChangeToNoop(v, iOpenEphAddr+1); -+ } -+ if( eTnctType==WHERE_DISTINCT_ORDERED ){ -+ /* Change the OP_OpenEphemeral to an OP_Null that sets the MEM_Cleared -+ ** bit on the first register of the previous value. This will cause the -+ ** OP_Ne added in codeDistinct() to always fail on the first iteration of -+ ** the loop even if the first row is all NULLs. */ -+ VdbeOp *pOp = sqlite3VdbeGetOp(v, iOpenEphAddr); -+ pOp->opcode = OP_Null; -+ pOp->p1 = 1; -+ pOp->p2 = iVal; -+ } -+ } - } - - #ifdef SQLITE_ENABLE_SORTER_REFERENCES -@@ -130077,7 +145895,7 @@ static void codeDistinct( - ** retrieved directly from table t1. If the values are very large, this - ** can be more efficient than storing them directly in the sorter records. - ** --** The ExprList_item.bSorterRef flag is set for each expression in pEList -+** The ExprList_item.fg.bSorterRef flag is set for each expression in pEList - ** for which the sorter-reference optimization should be enabled. - ** Additionally, the pSort->aDefer[] array is populated with entries - ** for all cursors required to evaluate all selected expressions. Finally. -@@ -130098,9 +145916,13 @@ static void selectExprDefer( - struct ExprList_item *pItem = &pEList->a[i]; - if( pItem->u.x.iOrderByCol==0 ){ - Expr *pExpr = pItem->pExpr; -- Table *pTab = pExpr->y.pTab; -- if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab) -- && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF) -+ Table *pTab; -+ if( pExpr->op==TK_COLUMN -+ && pExpr->iColumn>=0 -+ && ALWAYS( ExprUseYTab(pExpr) ) -+ && (pTab = pExpr->y.pTab)!=0 -+ && IsOrdinaryTable(pTab) -+ && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)!=0 - ){ - int j; - for(j=0; jiTable = pExpr->iTable; -+ assert( ExprUseYTab(pNew) ); - pNew->y.pTab = pExpr->y.pTab; - pNew->iColumn = pPk ? pPk->aiColumn[k] : -1; - pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew); -@@ -130132,7 +145955,7 @@ static void selectExprDefer( - nDefer++; - } - } -- pItem->bSorterRef = 1; -+ pItem->fg.bSorterRef = 1; - } - } - } -@@ -130263,7 +146086,7 @@ static void selectInnerLoop( - for(i=0; inExpr; i++){ - if( pEList->a[i].u.x.iOrderByCol>0 - #ifdef SQLITE_ENABLE_SORTER_REFERENCES -- || pEList->a[i].bSorterRef -+ || pEList->a[i].fg.bSorterRef - #endif - ){ - nResultCol--; -@@ -130305,59 +146128,11 @@ static void selectInnerLoop( - ** part of the result. - */ - if( hasDistinct ){ -- switch( pDistinct->eTnctType ){ -- case WHERE_DISTINCT_ORDERED: { -- VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ -- int iJump; /* Jump destination */ -- int regPrev; /* Previous row content */ -- -- /* Allocate space for the previous row */ -- regPrev = pParse->nMem+1; -- pParse->nMem += nResultCol; -- -- /* Change the OP_OpenEphemeral coded earlier to an OP_Null -- ** sets the MEM_Cleared bit on the first register of the -- ** previous value. This will cause the OP_Ne below to always -- ** fail on the first iteration of the loop even if the first -- ** row is all NULLs. -- */ -- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); -- pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct); -- pOp->opcode = OP_Null; -- pOp->p1 = 1; -- pOp->p2 = regPrev; -- pOp = 0; /* Ensure pOp is not used after sqlite3VdbeAddOp() */ -- -- iJump = sqlite3VdbeCurrentAddr(v) + nResultCol; -- for(i=0; ipEList->a[i].pExpr); -- if( idb->mallocFailed ); -- sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1); -- break; -- } -- -- case WHERE_DISTINCT_UNIQUE: { -- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct); -- break; -- } -- -- default: { -- assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED ); -- codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, -- regResult); -- break; -- } -- } -+ int eType = pDistinct->eTnctType; -+ int iTab = pDistinct->tabTnct; -+ assert( nResultCol==p->pEList->nExpr ); -+ iTab = codeDistinct(pParse, eType, iTab, iContinue, p->pEList, regResult); -+ fixDistinctOpenEph(pParse, eType, iTab, pDistinct->addrTnct); - if( pSort==0 ){ - codeOffset(v, p->iOffset, iContinue); - } -@@ -130399,6 +146174,16 @@ static void selectInnerLoop( - testcase( eDest==SRT_Fifo ); - testcase( eDest==SRT_DistFifo ); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg); -+#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG) -+ /* A destination of SRT_Table and a non-zero iSDParm2 parameter means -+ ** that this is an "UPDATE ... FROM" on a virtual table or view. In this -+ ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC. -+ ** This does not affect operation in any way - it just allows MakeRecord -+ ** to process OPFLAG_NOCHANGE values without an assert() failing. */ -+ if( eDest==SRT_Table && pDest->iSDParm2 ){ -+ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); -+ } -+#endif - #ifndef SQLITE_OMIT_CTE - if( eDest==SRT_DistFifo ){ - /* If the destination is DistFifo, then cursor (iParm+1) is open -@@ -130464,12 +146249,18 @@ static void selectInnerLoop( - ** case the order does matter */ - pushOntoSorter( - pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); -+ pDest->iSDParm2 = 0; /* Signal that any Bloom filter is unpopulated */ - }else{ - int r1 = sqlite3GetTempReg(pParse); - assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol ); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol, - r1, pDest->zAffSdst, nResultCol); - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); -+ if( pDest->iSDParm2 ){ -+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pDest->iSDParm2, 0, -+ regResult, nResultCol); -+ ExplainQueryPlan((pParse, 0, "CREATE BLOOM FILTER")); -+ } - sqlite3ReleaseTempReg(pParse, r1); - } - break; -@@ -130593,8 +146384,8 @@ static void selectInnerLoop( - ** X extra columns. - */ - SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ -- int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*); -- KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra); -+ int nExtra = (N+X)*(sizeof(CollSeq*)+1); -+ KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra); - if( p ){ - p->aSortFlags = (u8*)&p->aColl[N+X]; - p->nKeyField = (u16)N; -@@ -130602,9 +146393,9 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ - p->enc = ENC(db); - p->db = db; - p->nRef = 1; -- memset(&p[1], 0, nExtra); -+ memset(p->aColl, 0, nExtra); - }else{ -- sqlite3OomFault(db); -+ return (KeyInfo*)sqlite3OomFault(db); - } - return p; - } -@@ -130614,9 +146405,10 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ - */ - SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){ - if( p ){ -+ assert( p->db!=0 ); - assert( p->nRef>0 ); - p->nRef--; -- if( p->nRef==0 ) sqlite3DbFreeNN(p->db, p); -+ if( p->nRef==0 ) sqlite3DbNNFreeNN(p->db, p); - } - } - -@@ -130673,7 +146465,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList( - assert( sqlite3KeyInfoIsWriteable(pInfo) ); - for(i=iStart, pItem=pList->a+iStart; iaColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr); -- pInfo->aSortFlags[i-iStart] = pItem->sortFlags; -+ pInfo->aSortFlags[i-iStart] = pItem->fg.sortFlags; - } - } - return pInfo; -@@ -130682,7 +146474,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList( - /* - ** Name of the connection operator, used for error messages. - */ --static const char *selectOpName(int id){ -+SQLITE_PRIVATE const char *sqlite3SelectOpName(int id){ - char *z; - switch( id ){ - case TK_ALL: z = "UNION ALL"; break; -@@ -130755,6 +146547,23 @@ static void generateSortTail( - int bSeq; /* True if sorter record includes seq. no. */ - int nRefKey = 0; - struct ExprList_item *aOutEx = p->pEList->a; -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ int addrExplain; /* Address of OP_Explain instruction */ -+#endif -+ -+ nKey = pOrderBy->nExpr - pSort->nOBSat; -+ if( pSort->nOBSat==0 || nKey==1 ){ -+ ExplainQueryPlan2(addrExplain, (pParse, 0, -+ "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat?"LAST TERM OF ":"" -+ )); -+ }else{ -+ ExplainQueryPlan2(addrExplain, (pParse, 0, -+ "USE TEMP B-TREE FOR LAST %d TERMS OF ORDER BY", nKey -+ )); -+ } -+ sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd); -+ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush); -+ - - assert( addrBreak<0 ); - if( pSort->labelBkOut ){ -@@ -130775,6 +146584,9 @@ static void generateSortTail( - - iTab = pSort->iECursor; - if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){ -+ if( eDest==SRT_Mem && p->iOffset ){ -+ sqlite3VdbeAddOp2(v, OP_Null, 0, pDest->iSdst); -+ } - regRowid = 0; - regRow = pDest->iSdst; - }else{ -@@ -130786,7 +146598,6 @@ static void generateSortTail( - regRow = sqlite3GetTempRange(pParse, nColumn); - } - } -- nKey = pOrderBy->nExpr - pSort->nOBSat; - if( pSort->sortFlags & SORTFLAG_UseSorter ){ - int regSortOut = ++pParse->nMem; - iSortTab = pParse->nTab++; -@@ -130798,7 +146609,7 @@ static void generateSortTail( - if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce); - addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); - VdbeCoverage(v); -- codeOffset(v, p->iOffset, addrContinue); -+ assert( p->iLimit==0 && p->iOffset==0 ); - sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab); - bSeq = 0; - }else{ -@@ -130806,10 +146617,13 @@ static void generateSortTail( - codeOffset(v, p->iOffset, addrContinue); - iSortTab = iTab; - bSeq = 1; -+ if( p->iOffset>0 ){ -+ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); -+ } - } - for(i=0, iCol=nKey+bSeq-1; i=0; i--){ - #ifdef SQLITE_ENABLE_SORTER_REFERENCES -- if( aOutEx[i].bSorterRef ){ -+ if( aOutEx[i].fg.bSorterRef ){ - sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i); - }else - #endif -@@ -130861,6 +146675,7 @@ static void generateSortTail( - VdbeComment((v, "%s", aOutEx[i].zEName)); - } - } -+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); - switch( eDest ){ - case SRT_Table: - case SRT_EphemTab: { -@@ -130922,6 +146737,7 @@ static void generateSortTail( - }else{ - sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v); - } -+ sqlite3VdbeScanStatusRange(v, addrExplain, sqlite3VdbeCurrentAddr(v)-1, -1); - if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn); - sqlite3VdbeResolveLabel(v, addrBreak); - } -@@ -130930,9 +146746,6 @@ static void generateSortTail( - ** Return a pointer to a string containing the 'declaration type' of the - ** expression pExpr. The string may be treated as static by the caller. - ** --** Also try to estimate the size of the returned value and return that --** result in *pEstWidth. --** - ** The declaration type is the exact datatype definition extracted from the - ** original CREATE TABLE statement if the expression is a column. The - ** declaration type for a ROWID field is INTEGER. Exactly when an expression -@@ -130989,8 +146802,12 @@ static const char *columnTypeImpl( - SrcList *pTabList = pNC->pSrcList; - for(j=0;jnSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++); - if( jnSrc ){ -- pTab = pTabList->a[j].pTab; -- pS = pTabList->a[j].pSelect; -+ pTab = pTabList->a[j].pSTab; -+ if( pTabList->a[j].fg.isSubquery ){ -+ pS = pTabList->a[j].u4.pSubq->pSelect; -+ }else{ -+ pS = 0; -+ } - }else{ - pNC = pNC->pNext; - } -@@ -131017,13 +146834,15 @@ static const char *columnTypeImpl( - break; - } - -- assert( pTab && pExpr->y.pTab==pTab ); -+ assert( pTab && ExprUseYTab(pExpr) && pExpr->y.pTab==pTab ); - if( pS ){ - /* The "table" is actually a sub-select or a view in the FROM clause - ** of the SELECT statement. Return the declaration type and origin - ** data for the result-set column of the sub-select. - */ -- if( iCol>=0 && iColpEList->nExpr ){ -+ if( iColpEList->nExpr -+ && (!ViewCanHaveRowid || iCol>=0) -+ ){ - /* If iCol is less than zero, then the expression requests the - ** rowid of the sub-select or view. This expression is legal (see - ** test case misc2.2.2) - it always evaluates to NULL. -@@ -131045,7 +146864,7 @@ static const char *columnTypeImpl( - zType = "INTEGER"; - zOrigCol = "rowid"; - }else{ -- zOrigCol = pTab->aCol[iCol].zName; -+ zOrigCol = pTab->aCol[iCol].zCnName; - zType = sqlite3ColumnType(&pTab->aCol[iCol],0); - } - zOrigTab = pTab->zName; -@@ -131071,9 +146890,11 @@ static const char *columnTypeImpl( - ** statement. - */ - NameContext sNC; -- Select *pS = pExpr->x.pSelect; -- Expr *p = pS->pEList->a[0].pExpr; -- assert( ExprHasProperty(pExpr, EP_xIsSelect) ); -+ Select *pS; -+ Expr *p; -+ assert( ExprUseXSelect(pExpr) ); -+ pS = pExpr->x.pSelect; -+ p = pS->pEList->a[0].pExpr; - sNC.pSrcList = pS->pSrc; - sNC.pNext = pNC; - sNC.pParse = pNC->pParse; -@@ -131165,7 +146986,7 @@ static void generateColumnTypes( - ** then the result column name with the table name - ** prefix, ex: TABLE.COLUMN. Otherwise use zSpan. - */ --static void generateColumnNames( -+SQLITE_PRIVATE void sqlite3GenerateColumnNames( - Parse *pParse, /* Parser context */ - Select *pSelect /* Generate column names for this SELECT statement */ - ){ -@@ -131178,17 +146999,10 @@ static void generateColumnNames( - int fullName; /* TABLE.COLUMN if no AS clause and is a direct table ref */ - int srcName; /* COLUMN or TABLE.COLUMN if no AS clause and is direct */ - --#ifndef SQLITE_OMIT_EXPLAIN -- /* If this is an EXPLAIN, skip this step */ -- if( pParse->explain ){ -- return; -- } --#endif -- - if( pParse->colNamesSet ) return; - /* Column names are determined by the left-most term of a compound select */ - while( pSelect->pPrior ) pSelect = pSelect->pPrior; -- SELECTTRACE(1,pParse,pSelect,("generating column names\n")); -+ TREETRACE(0x80,pParse,pSelect,("generating column names\n")); - pTabList = pSelect->pSrc; - pEList = pSelect->pEList; - assert( v!=0 ); -@@ -131202,8 +147016,9 @@ static void generateColumnNames( - - assert( p!=0 ); - assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */ -- assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */ -- if( pEList->a[i].zEName && pEList->a[i].eEName==ENAME_NAME ){ -+ assert( p->op!=TK_COLUMN -+ || (ExprUseYTab(p) && p->y.pTab!=0) ); /* Covering idx not yet coded */ -+ if( pEList->a[i].zEName && pEList->a[i].fg.eEName==ENAME_NAME ){ - /* An AS clause always takes first priority */ - char *zName = pEList->a[i].zEName; - sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); -@@ -131217,7 +147032,7 @@ static void generateColumnNames( - if( iCol<0 ){ - zCol = "rowid"; - }else{ -- zCol = pTab->aCol[iCol].zName; -+ zCol = pTab->aCol[iCol].zCnName; - } - if( fullName ){ - char *zName = 0; -@@ -131255,7 +147070,7 @@ static void generateColumnNames( - ** and will break if those assumptions changes. Hence, use extreme caution - ** when modifying this routine to avoid breaking legacy. - ** --** See Also: generateColumnNames() -+** See Also: sqlite3GenerateColumnNames() - */ - SQLITE_PRIVATE int sqlite3ColumnsFromExprList( - Parse *pParse, /* Parsing context */ -@@ -131271,13 +147086,14 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( - char *zName; /* Column name */ - int nName; /* Size of name in zName[] */ - Hash ht; /* Hash table of column names */ -+ Table *pTab; - - sqlite3HashInit(&ht); - if( pEList ){ - nCol = pEList->nExpr; - aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); - testcase( aCol==0 ); -- if( nCol>32767 ) nCol = 32767; -+ if( NEVER(nCol>32767) ) nCol = 32767; - }else{ - nCol = 0; - aCol = 0; -@@ -131286,30 +147102,34 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( - *pnCol = nCol; - *paCol = aCol; - -- for(i=0, pCol=aCol; imallocFailed; i++, pCol++){ -+ for(i=0, pCol=aCol; inErr; i++, pCol++){ -+ struct ExprList_item *pX = &pEList->a[i]; -+ struct ExprList_item *pCollide; - /* Get an appropriate name for the column - */ -- if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ -+ if( (zName = pX->zEName)!=0 && pX->fg.eEName==ENAME_NAME ){ - /* If the column contains an "AS " phrase, use as the name */ - }else{ -- Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); -- while( pColExpr->op==TK_DOT ){ -+ Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pX->pExpr); -+ while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ - pColExpr = pColExpr->pRight; - assert( pColExpr!=0 ); - } -- if( pColExpr->op==TK_COLUMN ){ -+ if( pColExpr->op==TK_COLUMN -+ && ALWAYS( ExprUseYTab(pColExpr) ) -+ && ALWAYS( pColExpr->y.pTab!=0 ) -+ ){ - /* For columns use the column name name */ - int iCol = pColExpr->iColumn; -- Table *pTab = pColExpr->y.pTab; -- assert( pTab!=0 ); -+ pTab = pColExpr->y.pTab; - if( iCol<0 ) iCol = pTab->iPKey; -- zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; -+ zName = iCol>=0 ? pTab->aCol[iCol].zCnName : "rowid"; - }else if( pColExpr->op==TK_ID ){ - assert( !ExprHasProperty(pColExpr, EP_IntValue) ); - zName = pColExpr->u.zToken; - }else{ - /* Use the original text of the column expression as its name */ -- zName = pEList->a[i].zEName; -+ assert( zName==pX->zEName ); /* pointer comparison intended */ - } - } - if( zName && !sqlite3IsTrueOrFalse(zName) ){ -@@ -131322,87 +147142,139 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList( - ** append an integer to the name so that it becomes unique. - */ - cnt = 0; -- while( zName && sqlite3HashFind(&ht, zName)!=0 ){ -+ while( zName && (pCollide = sqlite3HashFind(&ht, zName))!=0 ){ -+ if( pCollide->fg.bUsingTerm ){ -+ pCol->colFlags |= COLFLAG_NOEXPAND; -+ } - nName = sqlite3Strlen30(zName); - if( nName>0 ){ - for(j=nName-1; j>0 && sqlite3Isdigit(zName[j]); j--){} - if( zName[j]==':' ) nName = j; - } - zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt); -- if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt); -+ sqlite3ProgressCheck(pParse); -+ if( cnt>3 ){ -+ sqlite3_randomness(sizeof(cnt), &cnt); -+ } - } -- pCol->zName = zName; -+ pCol->zCnName = zName; - pCol->hName = sqlite3StrIHash(zName); -+ if( pX->fg.bNoExpand ){ -+ pCol->colFlags |= COLFLAG_NOEXPAND; -+ } - sqlite3ColumnPropertiesFromName(0, pCol); -- if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){ -+ if( zName && sqlite3HashInsert(&ht, zName, pX)==pX ){ - sqlite3OomFault(db); - } - } - sqlite3HashClear(&ht); -- if( db->mallocFailed ){ -+ if( pParse->nErr ){ - for(j=0; jrc; - } - return SQLITE_OK; - } - - /* --** Add type and collation information to a column list based on --** a SELECT statement. --** --** The column list presumably came from selectColumnNamesFromExprList(). --** The column list has only names, not types or collations. This --** routine goes through and adds the types and collations. -+** pTab is a transient Table object that represents a subquery of some -+** kind (maybe a parenthesized subquery in the FROM clause of a larger -+** query, or a VIEW, or a CTE). This routine computes type information -+** for that Table object based on the Select object that implements the -+** subquery. For the purposes of this routine, "type information" means: - ** --** This routine requires that all identifiers in the SELECT --** statement be resolved. -+** * The datatype name, as it might appear in a CREATE TABLE statement -+** * Which collating sequence to use for the column -+** * The affinity of the column - */ --SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation( -- Parse *pParse, /* Parsing contexts */ -- Table *pTab, /* Add column type information to this table */ -- Select *pSelect, /* SELECT used to determine types and collations */ -- char aff /* Default affinity for columns */ -+SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( -+ Parse *pParse, /* Parsing contexts */ -+ Table *pTab, /* Add column type information to this table */ -+ Select *pSelect, /* SELECT used to determine types and collations */ -+ char aff /* Default affinity. */ - ){ - sqlite3 *db = pParse->db; -- NameContext sNC; - Column *pCol; - CollSeq *pColl; -- int i; -+ int i,j; - Expr *p; - struct ExprList_item *a; -+ NameContext sNC; - - assert( pSelect!=0 ); - assert( (pSelect->selFlags & SF_Resolved)!=0 ); -- assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed ); -- if( db->mallocFailed ) return; -+ assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); -+ assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); -+ if( db->mallocFailed || IN_RENAME_OBJECT ) return; -+ while( pSelect->pPrior ) pSelect = pSelect->pPrior; -+ a = pSelect->pEList->a; - memset(&sNC, 0, sizeof(sNC)); - sNC.pSrcList = pSelect->pSrc; -- a = pSelect->pEList->a; - for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ - const char *zType; -- int n, m; -+ i64 n; -+ int m = 0; -+ Select *pS2 = pSelect; -+ pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); - p = a[i].pExpr; -- zType = columnType(&sNC, p, 0, 0, 0); - /* pCol->szEst = ... // Column size est for SELECT tables never used */ - pCol->affinity = sqlite3ExprAffinity(p); -+ while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){ -+ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); -+ pS2 = pS2->pNext; -+ pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr); -+ } -+ if( pCol->affinity<=SQLITE_AFF_NONE ){ -+ pCol->affinity = aff; -+ } -+ if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){ -+ for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){ -+ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); -+ } -+ if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ -+ pCol->affinity = SQLITE_AFF_BLOB; -+ }else -+ if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){ -+ pCol->affinity = SQLITE_AFF_BLOB; -+ } -+ if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){ -+ pCol->affinity = SQLITE_AFF_FLEXNUM; -+ } -+ } -+ zType = columnType(&sNC, p, 0, 0, 0); -+ if( zType==0 || pCol->affinity!=sqlite3AffinityType(zType, 0) ){ -+ if( pCol->affinity==SQLITE_AFF_NUMERIC -+ || pCol->affinity==SQLITE_AFF_FLEXNUM -+ ){ -+ zType = "NUM"; -+ }else{ -+ zType = 0; -+ for(j=1; jaffinity ){ -+ zType = sqlite3StdType[j]; -+ break; -+ } -+ } -+ } -+ } - if( zType ){ -- m = sqlite3Strlen30(zType); -- n = sqlite3Strlen30(pCol->zName); -- pCol->zName = sqlite3DbReallocOrFree(db, pCol->zName, n+m+2); -- if( pCol->zName ){ -- memcpy(&pCol->zName[n+1], zType, m+1); -+ const i64 k = sqlite3Strlen30(zType); -+ n = sqlite3Strlen30(pCol->zCnName); -+ pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2); -+ pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); -+ if( pCol->zCnName ){ -+ memcpy(&pCol->zCnName[n+1], zType, k+1); - pCol->colFlags |= COLFLAG_HASTYPE; - } - } -- if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff; - pColl = sqlite3ExprCollSeq(pParse, p); -- if( pColl && pCol->zColl==0 ){ -- pCol->zColl = sqlite3DbStrDup(db, pColl->zName); -+ if( pColl ){ -+ assert( pTab->pIndex==0 ); -+ sqlite3ColumnSetColl(db, pCol, pColl->zName); - } - } - pTab->szTabRow = 1; /* Any non-zero value works */ -@@ -131432,7 +147304,7 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, c - pTab->zName = 0; - pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); - sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); -- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff); -+ sqlite3SubqueryColumnTypes(pParse, pTab, pSelect, aff); - pTab->iPKey = -1; - if( db->mallocFailed ){ - sqlite3DeleteTable(db, pTab); -@@ -131502,7 +147374,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ - p->iLimit = iLimit = ++pParse->nMem; - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); -- if( sqlite3ExprIsInteger(pLimit->pLeft, &n) ){ -+ if( sqlite3ExprIsInteger(pLimit->pLeft, &n, pParse) ){ - sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); - VdbeComment((v, "LIMIT counter")); - if( n==0 ){ -@@ -131566,7 +147438,7 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ - */ - static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ - ExprList *pOrderBy = p->pOrderBy; -- int nOrderBy = p->pOrderBy->nExpr; -+ int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0; - sqlite3 *db = pParse->db; - KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); - if( pRet ){ -@@ -131586,7 +147458,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ - } - assert( sqlite3KeyInfoIsWriteable(pRet) ); - pRet->aColl[i] = pColl; -- pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags; -+ pRet->aSortFlags[i] = pOrderBy->a[i].fg.sortFlags; - } - } - -@@ -131638,7 +147510,8 @@ static void generateWithRecursiveQuery( - SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */ - int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */ - Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ -- Select *pSetup = p->pPrior; /* The setup query */ -+ Select *pSetup; /* The setup query */ -+ Select *pFirstRec; /* Left-most recursive term */ - int addrTop; /* Top of the loop */ - int addrCont, addrBreak; /* CONTINUE and BREAK addresses */ - int iCurrent = 0; /* The Current table */ -@@ -131646,7 +147519,7 @@ static void generateWithRecursiveQuery( - int iQueue; /* The Queue table */ - int iDistinct = 0; /* To ensure unique results if UNION */ - int eDest = SRT_Fifo; /* How to write to Queue */ -- SelectDest destQueue; /* SelectDest targetting the Queue table */ -+ SelectDest destQueue; /* SelectDest targeting the Queue table */ - int i; /* Loop counter */ - int rc; /* Result code */ - ExprList *pOrderBy; /* The ORDER BY clause */ -@@ -131714,7 +147587,24 @@ static void generateWithRecursiveQuery( - /* Detach the ORDER BY clause from the compound SELECT */ - p->pOrderBy = 0; - -+ /* Figure out how many elements of the compound SELECT are part of the -+ ** recursive query. Make sure no recursive elements use aggregate -+ ** functions. Mark the recursive elements as UNION ALL even if they -+ ** are really UNION because the distinctness will be enforced by the -+ ** iDistinct table. pFirstRec is left pointing to the left-most -+ ** recursive term of the CTE. -+ */ -+ for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){ -+ if( pFirstRec->selFlags & SF_Aggregate ){ -+ sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); -+ goto end_of_recursive_query; -+ } -+ pFirstRec->op = TK_ALL; -+ if( (pFirstRec->pPrior->selFlags & SF_Recursive)==0 ) break; -+ } -+ - /* Store the results of the setup-query in Queue. */ -+ pSetup = pFirstRec->pPrior; - pSetup->pNext = 0; - ExplainQueryPlan((pParse, 1, "SETUP")); - rc = sqlite3Select(pParse, pSetup, &destQueue); -@@ -131747,15 +147637,11 @@ static void generateWithRecursiveQuery( - /* Execute the recursive SELECT taking the single row in Current as - ** the value for the recursive-table. Store the results in the Queue. - */ -- if( p->selFlags & SF_Aggregate ){ -- sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); -- }else{ -- p->pPrior = 0; -- ExplainQueryPlan((pParse, 1, "RECURSIVE STEP")); -- sqlite3Select(pParse, p, &destQueue); -- assert( p->pPrior==0 ); -- p->pPrior = pSetup; -- } -+ pFirstRec->pPrior = 0; -+ ExplainQueryPlan((pParse, 1, "RECURSIVE STEP")); -+ sqlite3Select(pParse, p, &destQueue); -+ assert( pFirstRec->pPrior==0 ); -+ pFirstRec->pPrior = pSetup; - - /* Keep running the loop until the Queue is empty */ - sqlite3VdbeGoto(v, addrTop); -@@ -131790,7 +147676,7 @@ static int multiSelectOrderBy( - ** The "LIMIT of exactly 1" case of condition (1) comes about when a VALUES - ** clause occurs within scalar expression (ex: "SELECT (VALUES(1),(2),(3))"). - ** The sqlite3CodeSubselect will have added the LIMIT 1 clause in tht case. --** Since the limit is exactly 1, we only need to evalutes the left-most VALUES. -+** Since the limit is exactly 1, we only need to evaluate the left-most VALUES. - */ - static int multiSelectValues( - Parse *pParse, /* Parsing context */ -@@ -131824,6 +147710,16 @@ static int multiSelectValues( - return rc; - } - -+/* -+** Return true if the SELECT statement which is known to be the recursive -+** part of a recursive CTE still has its anchor terms attached. If the -+** anchor terms have already been removed, then return false. -+*/ -+static int hasAnchor(Select *p){ -+ while( p && (p->selFlags & SF_Recursive)!=0 ){ p = p->pPrior; } -+ return p!=0; -+} -+ - /* - ** This routine is called to process a compound query form from - ** two or more separate queries using UNION, UNION ALL, EXCEPT, or -@@ -131876,12 +147772,8 @@ static int multiSelect( - db = pParse->db; - pPrior = p->pPrior; - dest = *pDest; -- if( pPrior->pOrderBy || pPrior->pLimit ){ -- sqlite3ErrorMsg(pParse,"%s clause should come after %s not before", -- pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op)); -- rc = 1; -- goto multi_select_end; -- } -+ assert( pPrior->pOrderBy==0 ); -+ assert( pPrior->pLimit==0 ); - - v = sqlite3GetVdbe(pParse); - assert( v!=0 ); /* The VDBE already created by calling function */ -@@ -131909,7 +147801,7 @@ static int multiSelect( - assert( p->pEList->nExpr==pPrior->pEList->nExpr ); - - #ifndef SQLITE_OMIT_CTE -- if( p->selFlags & SF_Recursive ){ -+ if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){ - generateWithRecursiveQuery(pParse, p, &dest); - }else - #endif -@@ -131932,13 +147824,14 @@ static int multiSelect( - switch( p->op ){ - case TK_ALL: { - int addr = 0; -- int nLimit; -+ int nLimit = 0; /* Initialize to suppress harmless compiler warning */ - assert( !pPrior->pLimit ); - pPrior->iLimit = p->iLimit; - pPrior->iOffset = p->iOffset; - pPrior->pLimit = p->pLimit; -+ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); - rc = sqlite3Select(pParse, pPrior, &dest); -- p->pLimit = 0; -+ pPrior->pLimit = 0; - if( rc ){ - goto multi_select_end; - } -@@ -131954,13 +147847,14 @@ static int multiSelect( - } - } - ExplainQueryPlan((pParse, 1, "UNION ALL")); -+ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); - rc = sqlite3Select(pParse, p, &dest); - testcase( rc!=SQLITE_OK ); - pDelete = p->pPrior; - p->pPrior = pPrior; - p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); -- if( pPrior->pLimit -- && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit) -+ if( p->pLimit -+ && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) - && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) - ){ - p->nSelectRow = sqlite3LogEst((u64)nLimit); -@@ -132001,10 +147895,12 @@ static int multiSelect( - assert( p->pEList ); - } - -+ - /* Code the SELECT statements to our left - */ - assert( !pPrior->pOrderBy ); - sqlite3SelectDestInit(&uniondest, priorOp, unionTab); -+ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); - rc = sqlite3Select(pParse, pPrior, &uniondest); - if( rc ){ - goto multi_select_end; -@@ -132023,7 +147919,8 @@ static int multiSelect( - p->pLimit = 0; - uniondest.eDest = op; - ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", -- selectOpName(p->op))); -+ sqlite3SelectOpName(p->op))); -+ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); - rc = sqlite3Select(pParse, p, &uniondest); - testcase( rc!=SQLITE_OK ); - assert( p->pOrderBy==0 ); -@@ -132084,6 +147981,7 @@ static int multiSelect( - /* Code the SELECTs to our left into temporary table "tab1". - */ - sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); -+ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n")); - rc = sqlite3Select(pParse, pPrior, &intersectdest); - if( rc ){ - goto multi_select_end; -@@ -132099,7 +147997,8 @@ static int multiSelect( - p->pLimit = 0; - intersectdest.iSDParm = tab2; - ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", -- selectOpName(p->op))); -+ sqlite3SelectOpName(p->op))); -+ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n")); - rc = sqlite3Select(pParse, p, &intersectdest); - testcase( rc!=SQLITE_OK ); - pDelete = p->pPrior; -@@ -132160,6 +148059,7 @@ static int multiSelect( - int nCol; /* Number of columns in result set */ - - assert( p->pNext==0 ); -+ assert( p->pEList!=0 ); - nCol = p->pEList->nExpr; - pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); - if( !pKeyInfo ){ -@@ -132194,7 +148094,10 @@ static int multiSelect( - multi_select_end: - pDest->iSdst = dest.iSdst; - pDest->nSdst = dest.nSdst; -- sqlite3SelectDelete(db, pDelete); -+ pDest->iSDParm2 = dest.iSDParm2; -+ if( pDelete ){ -+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete); -+ } - return rc; - } - #endif /* SQLITE_OMIT_COMPOUND_SELECT */ -@@ -132208,13 +148111,14 @@ SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){ - sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); - }else{ - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" -- " do not have the same number of result columns", selectOpName(p->op)); -+ " do not have the same number of result columns", -+ sqlite3SelectOpName(p->op)); - } - } - - /* - ** Code an output subroutine for a coroutine implementation of a --** SELECT statment. -+** SELECT statement. - ** - ** The data to be output is contained in pIn->iSdst. There are - ** pIn->nSdst columns to be output. pDest is where the output should -@@ -132295,6 +148199,11 @@ static int generateOutputSubroutine( - r1, pDest->zAffSdst, pIn->nSdst); - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pDest->iSDParm, r1, - pIn->iSdst, pIn->nSdst); -+ if( pDest->iSDParm2>0 ){ -+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pDest->iSDParm2, 0, -+ pIn->iSdst, pIn->nSdst); -+ ExplainQueryPlan((pParse, 0, "CREATE BLOOM FILTER")); -+ } - sqlite3ReleaseTempReg(pParse, r1); - break; - } -@@ -132305,10 +148214,8 @@ static int generateOutputSubroutine( - ** if it is the RHS of a row-value IN operator. - */ - case SRT_Mem: { -- if( pParse->nErr==0 ){ -- testcase( pIn->nSdst>1 ); -- sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst); -- } -+ testcase( pIn->nSdst>1 ); -+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst); - /* The LIMIT clause will jump out of the loop for us */ - break; - } -@@ -132438,7 +148345,7 @@ static int generateOutputSubroutine( - ** - ** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not - ** actually called using Gosub and they do not Return. EofA and EofB loop --** until all data is exhausted then jump to the "end" labe. AltB, AeqB, -+** until all data is exhausted then jump to the "end" label. AltB, AeqB, - ** and AgtB jump to either L2 or to one of EofA or EofB. - */ - #ifndef SQLITE_OMIT_COMPOUND_SELECT -@@ -132449,6 +148356,8 @@ static int multiSelectOrderBy( - ){ - int i, j; /* Loop counters */ - Select *pPrior; /* Another SELECT immediately to our left */ -+ Select *pSplit; /* Left-most SELECT in the right-hand group */ -+ int nSelect; /* Number of SELECT statements in the compound */ - Vdbe *v; /* Generate code to this VDBE */ - SelectDest destA; /* Destination for coroutine A */ - SelectDest destB; /* Destination for coroutine B */ -@@ -132473,7 +148382,7 @@ static int multiSelectOrderBy( - int savedOffset; /* Saved value of p->iOffset */ - int labelCmpr; /* Label for the start of the merge algorithm */ - int labelEnd; /* Label for the end of the overall SELECT stmt */ -- int addr1; /* Jump instructions that get retargetted */ -+ int addr1; /* Jump instructions that get retargeted */ - int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ - KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ - KeyInfo *pKeyMerge; /* Comparison information for merging rows */ -@@ -132494,8 +148403,7 @@ static int multiSelectOrderBy( - /* Patch up the ORDER BY clause - */ - op = p->op; -- pPrior = p->pPrior; -- assert( pPrior->pOrderBy==0 ); -+ assert( p->pPrior->pOrderBy==0 ); - pOrderBy = p->pOrderBy; - assert( pOrderBy ); - nOrderBy = pOrderBy->nExpr; -@@ -132508,6 +148416,7 @@ static int multiSelectOrderBy( - for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ - struct ExprList_item *pItem; - for(j=0, pItem=pOrderBy->a; ju.x.iOrderByCol>0 ); - if( pItem->u.x.iOrderByCol==i ) break; - } -@@ -132534,6 +148443,7 @@ static int multiSelectOrderBy( - struct ExprList_item *pItem; - aPermute[0] = nOrderBy; - for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ -+ assert( pItem!=0 ); - assert( pItem->u.x.iOrderByCol>0 ); - assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); - aPermute[i] = pItem->u.x.iOrderByCol - 1; -@@ -132543,11 +148453,6 @@ static int multiSelectOrderBy( - pKeyMerge = 0; - } - -- /* Reattach the ORDER BY clause to the query. -- */ -- p->pOrderBy = pOrderBy; -- pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0); -- - /* Allocate a range of temporary registers and the KeyInfo needed - ** for the logic that removes duplicate result rows when the - ** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL). -@@ -132572,12 +148477,30 @@ static int multiSelectOrderBy( - - /* Separate the left and the right query from one another - */ -- p->pPrior = 0; -+ nSelect = 1; -+ if( (op==TK_ALL || op==TK_UNION) -+ && OptimizationEnabled(db, SQLITE_BalancedMerge) -+ ){ -+ for(pSplit=p; pSplit->pPrior!=0 && pSplit->op==op; pSplit=pSplit->pPrior){ -+ nSelect++; -+ assert( pSplit->pPrior->pNext==pSplit ); -+ } -+ } -+ if( nSelect<=3 ){ -+ pSplit = p; -+ }else{ -+ pSplit = p; -+ for(i=2; ipPrior; } -+ } -+ pPrior = pSplit->pPrior; -+ assert( pPrior!=0 ); -+ pSplit->pPrior = 0; - pPrior->pNext = 0; -+ assert( p->pOrderBy == pOrderBy ); -+ assert( pOrderBy!=0 || db->mallocFailed ); -+ pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0); - sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); -- if( pPrior->pPrior==0 ){ -- sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); -- } -+ sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); - - /* Compute the limit registers */ - computeLimitRegisters(pParse, p, labelEnd); -@@ -132600,7 +148523,7 @@ static int multiSelectOrderBy( - sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA); - sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB); - -- ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op))); -+ ExplainQueryPlan((pParse, 1, "MERGE (%s)", sqlite3SelectOpName(p->op))); - - /* Generate a coroutine to evaluate the SELECT statement to the - ** left of the compound operator - the "A" select. -@@ -132726,13 +148649,15 @@ static int multiSelectOrderBy( - */ - sqlite3VdbeResolveLabel(v, labelEnd); - -- /* Reassembly the compound query so that it will be freed correctly -- ** by the calling function */ -- if( p->pPrior ){ -- sqlite3SelectDelete(db, p->pPrior); -+ /* Make arrangements to free the 2nd and subsequent arms of the compound -+ ** after the parse has finished */ -+ if( pSplit->pPrior ){ -+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior); - } -- p->pPrior = pPrior; -- pPrior->pNext = p; -+ pSplit->pPrior = pPrior; -+ pPrior->pNext = pSplit; -+ sqlite3ExprListDelete(db, pPrior->pOrderBy); -+ pPrior->pOrderBy = 0; - - /*** TBD: Insert subroutine calls to close cursors on incomplete - **** subqueries ****/ -@@ -132748,13 +148673,42 @@ static int multiSelectOrderBy( - ** - ** All references to columns in table iTable are to be replaced by corresponding - ** expressions in pEList. -+** -+** ## About "isOuterJoin": -+** -+** The isOuterJoin column indicates that the replacement will occur into a -+** position in the parent that NULL-able due to an OUTER JOIN. Either the -+** target slot in the parent is the right operand of a LEFT JOIN, or one of -+** the left operands of a RIGHT JOIN. In either case, we need to potentially -+** bypass the substituted expression with OP_IfNullRow. -+** -+** Suppose the original expression is an integer constant. Even though the table -+** has the nullRow flag set, because the expression is an integer constant, -+** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode -+** that checks to see if the nullRow flag is set on the table. If the nullRow -+** flag is set, then the value in the register is set to NULL and the original -+** expression is bypassed. If the nullRow flag is not set, then the original -+** expression runs to populate the register. -+** -+** Example where this is needed: -+** -+** CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT); -+** CREATE TABLE t2(x INT UNIQUE); -+** -+** SELECT a,b,m,x FROM t1 LEFT JOIN (SELECT 59 AS m,x FROM t2) ON b=x; -+** -+** When the subquery on the right side of the LEFT JOIN is flattened, we -+** have to add OP_IfNullRow in front of the OP_Integer that implements the -+** "m" value of the subquery so that a NULL will be loaded instead of 59 -+** when processing a non-matched row of the left. - */ - typedef struct SubstContext { - Parse *pParse; /* The parsing context */ - int iTable; /* Replace references to this table */ - int iNewTable; /* New table number */ -- int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ -+ int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ - ExprList *pEList; /* Replacement expressions */ -+ ExprList *pCList; /* Collation sequences for replacement expr */ - } SubstContext; - - /* Forward Declarations */ -@@ -132779,58 +148733,81 @@ static Expr *substExpr( - Expr *pExpr /* Expr in which substitution occurs */ - ){ - if( pExpr==0 ) return 0; -- if( ExprHasProperty(pExpr, EP_FromJoin) -- && pExpr->iRightJoinTable==pSubst->iTable -+ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) -+ && pExpr->w.iJoin==pSubst->iTable - ){ -- pExpr->iRightJoinTable = pSubst->iNewTable; -+ testcase( ExprHasProperty(pExpr, EP_InnerON) ); -+ pExpr->w.iJoin = pSubst->iNewTable; - } - if( pExpr->op==TK_COLUMN - && pExpr->iTable==pSubst->iTable - && !ExprHasProperty(pExpr, EP_FixedCol) - ){ -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW - if( pExpr->iColumn<0 ){ - pExpr->op = TK_NULL; -- }else{ -+ }else -+#endif -+ { - Expr *pNew; -- Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; -+ int iColumn; -+ Expr *pCopy; - Expr ifNullRow; -- assert( pSubst->pEList!=0 && pExpr->iColumnpEList->nExpr ); -+ iColumn = pExpr->iColumn; -+ assert( iColumn>=0 ); -+ assert( pSubst->pEList!=0 && iColumnpEList->nExpr ); - assert( pExpr->pRight==0 ); -+ pCopy = pSubst->pEList->a[iColumn].pExpr; - if( sqlite3ExprIsVector(pCopy) ){ - sqlite3VectorErrorMsg(pSubst->pParse, pCopy); - }else{ - sqlite3 *db = pSubst->pParse->db; -- if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ -+ if( pSubst->isOuterJoin -+ && (pCopy->op!=TK_COLUMN || pCopy->iTable!=pSubst->iNewTable) -+ ){ - memset(&ifNullRow, 0, sizeof(ifNullRow)); - ifNullRow.op = TK_IF_NULL_ROW; - ifNullRow.pLeft = pCopy; - ifNullRow.iTable = pSubst->iNewTable; -- ifNullRow.flags = EP_Skip; -+ ifNullRow.iColumn = -99; -+ ifNullRow.flags = EP_IfNullRow; - pCopy = &ifNullRow; - } - testcase( ExprHasProperty(pCopy, EP_Subquery) ); - pNew = sqlite3ExprDup(db, pCopy, 0); -- if( pNew && pSubst->isLeftJoin ){ -+ if( db->mallocFailed ){ -+ sqlite3ExprDelete(db, pNew); -+ return pExpr; -+ } -+ if( pSubst->isOuterJoin ){ - ExprSetProperty(pNew, EP_CanBeNull); - } -- if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){ -- pNew->iRightJoinTable = pExpr->iRightJoinTable; -- ExprSetProperty(pNew, EP_FromJoin); -+ if( pNew->op==TK_TRUEFALSE ){ -+ pNew->u.iValue = sqlite3ExprTruthValue(pNew); -+ pNew->op = TK_INTEGER; -+ ExprSetProperty(pNew, EP_IntValue); - } -- sqlite3ExprDelete(db, pExpr); -- pExpr = pNew; - - /* Ensure that the expression now has an implicit collation sequence, - ** just as it did when it was a column of a view or sub-query. */ -- if( pExpr ){ -- if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){ -- CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr); -- pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr, -+ { -+ CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew); -+ CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, -+ pSubst->pCList->a[iColumn].pExpr -+ ); -+ if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){ -+ pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew, - (pColl ? pColl->zName : "BINARY") - ); - } -- ExprClearProperty(pExpr, EP_Collate); - } -+ ExprClearProperty(pNew, EP_Collate); -+ if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ -+ sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, -+ pExpr->flags & (EP_OuterON|EP_InnerON)); -+ } -+ sqlite3ExprDelete(db, pExpr); -+ pExpr = pNew; - } - } - }else{ -@@ -132839,7 +148816,7 @@ static Expr *substExpr( - } - pExpr->pLeft = substExpr(pSubst, pExpr->pLeft); - pExpr->pRight = substExpr(pSubst, pExpr->pRight); -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - substSelect(pSubst, pExpr->x.pSelect, 1); - }else{ - substExprList(pSubst, pExpr->x.pList); -@@ -132871,7 +148848,7 @@ static void substSelect( - int doPrior /* Do substitutes on p->pPrior too */ - ){ - SrcList *pSrc; -- struct SrcList_item *pItem; -+ SrcItem *pItem; - int i; - if( !p ) return; - do{ -@@ -132883,7 +148860,9 @@ static void substSelect( - pSrc = p->pSrc; - assert( pSrc!=0 ); - for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ -- substSelect(pSubst, pItem->pSelect, 1); -+ if( pItem->fg.isSubquery ){ -+ substSelect(pSubst, pItem->u4.pSubq->pSelect, 1); -+ } - if( pItem->fg.isTabFunc ){ - substExprList(pSubst, pItem->u1.pFuncArg); - } -@@ -132901,7 +148880,7 @@ static void substSelect( - ** pSrcItem->colUsed mask. - */ - static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){ -- struct SrcList_item *pItem; -+ SrcItem *pItem; - if( pExpr->op!=TK_COLUMN ) return WRC_Continue; - pItem = pWalker->u.pSrcItem; - if( pItem->iCursor!=pExpr->iTable ) return WRC_Continue; -@@ -132911,10 +148890,10 @@ static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){ - } - static void recomputeColumnsUsed( - Select *pSelect, /* The complete SELECT statement */ -- struct SrcList_item *pSrcItem /* Which FROM clause item to recompute */ -+ SrcItem *pSrcItem /* Which FROM clause item to recompute */ - ){ - Walker w; -- if( NEVER(pSrcItem->pTab==0) ) return; -+ if( NEVER(pSrcItem->pSTab==0) ) return; - memset(&w, 0, sizeof(w)); - w.xExprCallback = recomputeColumnsUsedExpr; - w.xSelectCallback = sqlite3SelectWalkNoop; -@@ -132924,6 +148903,145 @@ static void recomputeColumnsUsed( - } - #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ - -+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) -+/* -+** Assign new cursor numbers to each of the items in pSrc. For each -+** new cursor number assigned, set an entry in the aCsrMap[] array -+** to map the old cursor number to the new: -+** -+** aCsrMap[iOld+1] = iNew; -+** -+** The array is guaranteed by the caller to be large enough for all -+** existing cursor numbers in pSrc. aCsrMap[0] is the array size. -+** -+** If pSrc contains any sub-selects, call this routine recursively -+** on the FROM clause of each such sub-select, with iExcept set to -1. -+*/ -+static void srclistRenumberCursors( -+ Parse *pParse, /* Parse context */ -+ int *aCsrMap, /* Array to store cursor mappings in */ -+ SrcList *pSrc, /* FROM clause to renumber */ -+ int iExcept /* FROM clause item to skip */ -+){ -+ int i; -+ SrcItem *pItem; -+ for(i=0, pItem=pSrc->a; inSrc; i++, pItem++){ -+ if( i!=iExcept ){ -+ Select *p; -+ assert( pItem->iCursor < aCsrMap[0] ); -+ if( !pItem->fg.isRecursive || aCsrMap[pItem->iCursor+1]==0 ){ -+ aCsrMap[pItem->iCursor+1] = pParse->nTab++; -+ } -+ pItem->iCursor = aCsrMap[pItem->iCursor+1]; -+ if( pItem->fg.isSubquery ){ -+ for(p=pItem->u4.pSubq->pSelect; p; p=p->pPrior){ -+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1); -+ } -+ } -+ } -+ } -+} -+ -+/* -+** *piCursor is a cursor number. Change it if it needs to be mapped. -+*/ -+static void renumberCursorDoMapping(Walker *pWalker, int *piCursor){ -+ int *aCsrMap = pWalker->u.aiCol; -+ int iCsr = *piCursor; -+ if( iCsr < aCsrMap[0] && aCsrMap[iCsr+1]>0 ){ -+ *piCursor = aCsrMap[iCsr+1]; -+ } -+} -+ -+/* -+** Expression walker callback used by renumberCursors() to update -+** Expr objects to match newly assigned cursor numbers. -+*/ -+static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ -+ int op = pExpr->op; -+ if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){ -+ renumberCursorDoMapping(pWalker, &pExpr->iTable); -+ } -+ if( ExprHasProperty(pExpr, EP_OuterON) ){ -+ renumberCursorDoMapping(pWalker, &pExpr->w.iJoin); -+ } -+ return WRC_Continue; -+} -+ -+/* -+** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc) -+** of the SELECT statement passed as the second argument, and to each -+** cursor in the FROM clause of any FROM clause sub-selects, recursively. -+** Except, do not assign a new cursor number to the iExcept'th element in -+** the FROM clause of (*p). Update all expressions and other references -+** to refer to the new cursor numbers. -+** -+** Argument aCsrMap is an array that may be used for temporary working -+** space. Two guarantees are made by the caller: -+** -+** * the array is larger than the largest cursor number used within the -+** select statement passed as an argument, and -+** -+** * the array entries for all cursor numbers that do *not* appear in -+** FROM clauses of the select statement as described above are -+** initialized to zero. -+*/ -+static void renumberCursors( -+ Parse *pParse, /* Parse context */ -+ Select *p, /* Select to renumber cursors within */ -+ int iExcept, /* FROM clause item to skip */ -+ int *aCsrMap /* Working space */ -+){ -+ Walker w; -+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept); -+ memset(&w, 0, sizeof(w)); -+ w.u.aiCol = aCsrMap; -+ w.xExprCallback = renumberCursorsCb; -+ w.xSelectCallback = sqlite3SelectWalkNoop; -+ sqlite3WalkSelect(&w, p); -+} -+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ -+ -+/* -+** If pSel is not part of a compound SELECT, return a pointer to its -+** expression list. Otherwise, return a pointer to the expression list -+** of the leftmost SELECT in the compound. -+*/ -+static ExprList *findLeftmostExprlist(Select *pSel){ -+ while( pSel->pPrior ){ -+ pSel = pSel->pPrior; -+ } -+ return pSel->pEList; -+} -+ -+/* -+** Return true if any of the result-set columns in the compound query -+** have incompatible affinities on one or more arms of the compound. -+*/ -+static int compoundHasDifferentAffinities(Select *p){ -+ int ii; -+ ExprList *pList; -+ assert( p!=0 ); -+ assert( p->pEList!=0 ); -+ assert( p->pPrior!=0 ); -+ pList = p->pEList; -+ for(ii=0; iinExpr; ii++){ -+ char aff; -+ Select *pSub1; -+ assert( pList->a[ii].pExpr!=0 ); -+ aff = sqlite3ExprAffinity(pList->a[ii].pExpr); -+ for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){ -+ assert( pSub1->pEList!=0 ); -+ assert( pSub1->pEList->nExpr>ii ); -+ assert( pSub1->pEList->a[ii].pExpr!=0 ); -+ if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){ -+ return 1; -+ } -+ } -+ } -+ return 0; -+} -+ - #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) - /* - ** This routine attempts to flatten subqueries as a performance optimization. -@@ -132965,11 +149083,13 @@ static void recomputeColumnsUsed( - ** from 2015-02-09.) - ** - ** (3) If the subquery is the right operand of a LEFT JOIN then --** (3a) the subquery may not be a join and --** (3b) the FROM clause of the subquery may not contain a virtual --** table and --** (3c) the outer query may not be an aggregate. -+** (3a) the subquery may not be a join -+** (**) Was (3b): "the FROM clause of the subquery may not contain -+** a virtual table" -+** (**) Was: "The outer query may not have a GROUP BY." This case -+** is now managed correctly - ** (3d) the outer query may not be DISTINCT. -+** See also (26) for restrictions on RIGHT JOIN. - ** - ** (4) The subquery can not be DISTINCT. - ** -@@ -132990,7 +149110,7 @@ static void recomputeColumnsUsed( - ** (9) If the subquery uses LIMIT then the outer query may not be aggregate. - ** - ** (**) Restriction (10) was removed from the code on 2005-02-05 but we --** accidently carried the comment forward until 2014-09-15. Original -+** accidentally carried the comment forward until 2014-09-15. Original - ** constraint: "If the subquery is aggregate then the outer query - ** may not use LIMIT." - ** -@@ -133018,9 +149138,14 @@ static void recomputeColumnsUsed( - ** (17c) every term within the subquery compound must have a FROM clause - ** (17d) the outer query may not be - ** (17d1) aggregate, or --** (17d2) DISTINCT, or --** (17d3) a join. --** (17e) the subquery may not contain window functions -+** (17d2) DISTINCT -+** (17e) the subquery may not contain window functions, and -+** (17f) the subquery must not be the RHS of a LEFT JOIN. -+** (17g) either the subquery is the first element of the outer -+** query or there are no RIGHT or FULL JOINs in any arm -+** of the subquery. (This is a duplicate of condition (27b).) -+** (17h) The corresponding result set expressions in all arms of the -+** compound must have the same affinity. - ** - ** The parent and sub-query may contain WHERE clauses. Subject to - ** rules (11), (13) and (14), they may also contain ORDER BY, -@@ -133036,8 +149161,8 @@ static void recomputeColumnsUsed( - ** syntax error and return a detailed message. - ** - ** (18) If the sub-query is a compound select, then all terms of the --** ORDER BY clause of the parent must be simple references to --** columns of the sub-query. -+** ORDER BY clause of the parent must be copies of a term returned -+** by the parent query. - ** - ** (19) If the subquery uses LIMIT then the outer query may not - ** have a WHERE clause. -@@ -133053,9 +149178,8 @@ static void recomputeColumnsUsed( - ** - ** (22) The subquery may not be a recursive CTE. - ** --** (**) Subsumed into restriction (17d3). Was: If the outer query is --** a recursive CTE, then the sub-query may not be a compound query. --** This restriction is because transforming the -+** (23) If the outer query is a recursive CTE, then the sub-query may not be -+** a compound query. This restriction is because transforming the - ** parent to a compound query confuses the code that handles - ** recursive queries in multiSelect(). - ** -@@ -133069,6 +149193,18 @@ static void recomputeColumnsUsed( - ** function in the select list or ORDER BY clause, flattening - ** is not attempted. - ** -+** (26) The subquery may not be the right operand of a RIGHT JOIN. -+** See also (3) for restrictions on LEFT JOIN. -+** -+** (27) The subquery may not contain a FULL or RIGHT JOIN unless it -+** is the first element of the parent query. Two subcases: -+** (27a) the subquery is not a compound query. -+** (27b) the subquery is a compound query and the RIGHT JOIN occurs -+** in any arm of the compound query. (See also (17g).) -+** -+** (28) The subquery is not a MATERIALIZED CTE. (This is handled -+** in the caller before ever reaching this routine.) -+** - ** - ** In this routine, the "p" parameter is a pointer to the outer query. - ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query -@@ -133094,12 +149230,13 @@ static int flattenSubquery( - SrcList *pSubSrc; /* The FROM clause of the subquery */ - int iParent; /* VDBE cursor number of the pSub result set temp table */ - int iNewParent = -1;/* Replacement table for iParent */ -- int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ -+ int isOuterJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ - int i; /* Loop counter */ - Expr *pWhere; /* The WHERE clause */ -- struct SrcList_item *pSubitem; /* The subquery */ -+ SrcItem *pSubitem; /* The subquery */ - sqlite3 *db = pParse->db; - Walker w; /* Walker to persist agginfo data */ -+ int *aCsrMap = 0; - - /* Check to see if flattening is permitted. Return 0 if not. - */ -@@ -133110,7 +149247,8 @@ static int flattenSubquery( - assert( pSrc && iFrom>=0 && iFromnSrc ); - pSubitem = &pSrc->a[iFrom]; - iParent = pSubitem->iCursor; -- pSub = pSubitem->pSelect; -+ assert( pSubitem->fg.isSubquery ); -+ pSub = pSubitem->u4.pSubq->pSelect; - assert( pSub!=0 ); - - #ifndef SQLITE_OMIT_WINDOWFUNC -@@ -133159,32 +149297,26 @@ static int flattenSubquery( - ** - ** which is not at all the same thing. - ** -- ** If the subquery is the right operand of a LEFT JOIN, then the outer -- ** query cannot be an aggregate. (3c) This is an artifact of the way -- ** aggregates are processed - there is no mechanism to determine if -- ** the LEFT JOIN table should be all-NULL. -- ** - ** See also tickets #306, #350, and #3300. - */ -- if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){ -- isLeftJoin = 1; -- if( pSubSrc->nSrc>1 /* (3a) */ -- || isAgg /* (3b) */ -- || IsVirtual(pSubSrc->a[0].pTab) /* (3c) */ -- || (p->selFlags & SF_Distinct)!=0 /* (3d) */ -+ if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){ -+ if( pSubSrc->nSrc>1 /* (3a) */ -+ /**** || IsVirtual(pSubSrc->a[0].pSTab) (3b)-omitted */ -+ || (p->selFlags & SF_Distinct)!=0 /* (3d) */ -+ || (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */ - ){ - return 0; - } -+ isOuterJoin = 1; - } --#ifdef SQLITE_EXTRA_IFNULLROW -- else if( iFrom>0 && !isAgg ){ -- /* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for -- ** every reference to any result column from subquery in a join, even -- ** though they are not necessary. This will stress-test the OP_IfNullRow -- ** opcode. */ -- isLeftJoin = -1; -+ -+ assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */ -+ if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ -+ return 0; /* Restriction (27a) */ - } --#endif -+ -+ /* Condition (28) is blocked by the caller */ -+ assert( !pSubitem->fg.isCte || pSubitem->u2.pCteUse->eM10d!=M10d_Yes ); - - /* Restriction (17): If the sub-query is a compound SELECT, then it must - ** use only the UNION ALL operator. And none of the simple select queries -@@ -133192,16 +149324,18 @@ static int flattenSubquery( - ** queries. - */ - if( pSub->pPrior ){ -+ int ii; - if( pSub->pOrderBy ){ - return 0; /* Restriction (20) */ - } -- if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){ -- return 0; /* (17d1), (17d2), or (17d3) */ -+ if( isAgg || (p->selFlags & SF_Distinct)!=0 || isOuterJoin>0 ){ -+ return 0; /* (17d1), (17d2), or (17f) */ - } - for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){ - testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ); - testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate ); - assert( pSub->pSrc!=0 ); -+ assert( (pSub->selFlags & SF_Recursive)==0 ); - assert( pSub->pEList->nExpr==pSub1->pEList->nExpr ); - if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 /* (17b) */ - || (pSub1->pPrior && pSub1->op!=TK_ALL) /* (17a) */ -@@ -133212,28 +149346,38 @@ static int flattenSubquery( - ){ - return 0; - } -+ if( iFrom>0 && (pSub1->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ -+ /* Without this restriction, the JT_LTORJ flag would end up being -+ ** omitted on left-hand tables of the right join that is being -+ ** flattened. */ -+ return 0; /* Restrictions (17g), (27b) */ -+ } - testcase( pSub1->pSrc->nSrc>1 ); - } - - /* Restriction (18). */ - if( p->pOrderBy ){ -- int ii; - for(ii=0; iipOrderBy->nExpr; ii++){ - if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; - } - } -- } - -- /* Ex-restriction (23): -- ** The only way that the recursive part of a CTE can contain a compound -- ** subquery is for the subquery to be one term of a join. But if the -- ** subquery is a join, then the flattening has already been stopped by -- ** restriction (17d3) -- */ -- assert( (p->selFlags & SF_Recursive)==0 || pSub->pPrior==0 ); -+ /* Restriction (23) */ -+ if( (p->selFlags & SF_Recursive) ) return 0; -+ -+ /* Restriction (17h) */ -+ if( compoundHasDifferentAffinities(pSub) ) return 0; -+ -+ if( pSrc->nSrc>1 ){ -+ if( pParse->nSelect>500 ) return 0; -+ if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0; -+ aCsrMap = sqlite3DbMallocZero(db, ((i64)pParse->nTab+1)*sizeof(int)); -+ if( aCsrMap ) aCsrMap[0] = pParse->nTab; -+ } -+ } - - /***** If we reach this point, flattening is permitted. *****/ -- SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n", -+ TREETRACE(0x4,pParse,p,("flatten %u.%p from term %d\n", - pSub->selId, pSub, iFrom)); - - /* Authorize the subquery */ -@@ -133242,6 +149386,21 @@ static int flattenSubquery( - testcase( i==SQLITE_DENY ); - pParse->zAuthContext = zSavedAuthContext; - -+ /* Delete the transient structures associated with the subquery */ -+ -+ if( ALWAYS(pSubitem->fg.isSubquery) ){ -+ pSub1 = sqlite3SubqueryDetach(db, pSubitem); -+ }else{ -+ pSub1 = 0; -+ } -+ assert( pSubitem->fg.isSubquery==0 ); -+ assert( pSubitem->fg.fixedSchema==0 ); -+ sqlite3DbFree(db, pSubitem->zName); -+ sqlite3DbFree(db, pSubitem->zAlias); -+ pSubitem->zName = 0; -+ pSubitem->zAlias = 0; -+ assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 ); -+ - /* If the sub-query is a compound SELECT statement, then (by restrictions - ** 17 and 18 above) it must be a UNION ALL and the parent query must - ** be of the form: -@@ -133280,43 +149439,40 @@ static int flattenSubquery( - ExprList *pOrderBy = p->pOrderBy; - Expr *pLimit = p->pLimit; - Select *pPrior = p->pPrior; -+ Table *pItemTab = pSubitem->pSTab; -+ pSubitem->pSTab = 0; - p->pOrderBy = 0; -- p->pSrc = 0; - p->pPrior = 0; - p->pLimit = 0; - pNew = sqlite3SelectDup(db, p, 0); - p->pLimit = pLimit; - p->pOrderBy = pOrderBy; -- p->pSrc = pSrc; - p->op = TK_ALL; -+ pSubitem->pSTab = pItemTab; - if( pNew==0 ){ - p->pPrior = pPrior; - }else{ -+ pNew->selId = ++pParse->nSelect; -+ if( aCsrMap && ALWAYS(db->mallocFailed==0) ){ -+ renumberCursors(pParse, pNew, iFrom, aCsrMap); -+ } - pNew->pPrior = pPrior; - if( pPrior ) pPrior->pNext = pNew; - pNew->pNext = p; - p->pPrior = pNew; -- SELECTTRACE(2,pParse,p,("compound-subquery flattener" -+ TREETRACE(0x4,pParse,p,("compound-subquery flattener" - " creates %u as peer\n",pNew->selId)); - } -- if( db->mallocFailed ) return 1; -+ assert( pSubitem->fg.isSubquery==0 ); -+ } -+ sqlite3DbFree(db, aCsrMap); -+ if( db->mallocFailed ){ -+ assert( pSubitem->fg.fixedSchema==0 ); -+ assert( pSubitem->fg.isSubquery==0 ); -+ assert( pSubitem->u4.zDatabase==0 ); -+ sqlite3SrcItemAttachSubquery(pParse, pSubitem, pSub1, 0); -+ return 1; - } -- -- /* Begin flattening the iFrom-th entry of the FROM clause -- ** in the outer query. -- */ -- pSub = pSub1 = pSubitem->pSelect; -- -- /* Delete the transient table structure associated with the -- ** subquery -- */ -- sqlite3DbFree(db, pSubitem->zDatabase); -- sqlite3DbFree(db, pSubitem->zName); -- sqlite3DbFree(db, pSubitem->zAlias); -- pSubitem->zDatabase = 0; -- pSubitem->zName = 0; -- pSubitem->zAlias = 0; -- pSubitem->pSelect = 0; - - /* Defer deleting the Table object associated with the - ** subquery until code generation is -@@ -133325,16 +149481,16 @@ static int flattenSubquery( - ** - ** pSubitem->pTab is always non-NULL by test restrictions and tests above. - */ -- if( ALWAYS(pSubitem->pTab!=0) ){ -- Table *pTabToDel = pSubitem->pTab; -+ if( ALWAYS(pSubitem->pSTab!=0) ){ -+ Table *pTabToDel = pSubitem->pSTab; - if( pTabToDel->nTabRef==1 ){ - Parse *pToplevel = sqlite3ParseToplevel(pParse); -- pTabToDel->pNextZombie = pToplevel->pZombieTab; -- pToplevel->pZombieTab = pTabToDel; -+ sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel); -+ testcase( pToplevel->earlyCleanup ); - }else{ - pTabToDel->nTabRef--; - } -- pSubitem->pTab = 0; -+ pSubitem->pSTab = 0; - } - - /* The following loop runs once for each term in a compound-subquery -@@ -133350,22 +149506,18 @@ static int flattenSubquery( - ** those references with expressions that resolve to the subquery FROM - ** elements we are now copying in. - */ -+ pSub = pSub1; - for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){ - int nSubSrc; - u8 jointype = 0; -+ u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ; - assert( pSub!=0 ); - pSubSrc = pSub->pSrc; /* FROM clause of subquery */ - nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */ - pSrc = pParent->pSrc; /* FROM clause of the outer query */ - -- if( pSrc ){ -- assert( pParent==p ); /* First time through the loop */ -- jointype = pSubitem->fg.jointype; -- }else{ -- assert( pParent!=p ); /* 2nd and subsequent times through the loop */ -- pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); -- if( pSrc==0 ) break; -- pParent->pSrc = pSrc; -+ if( pParent==p ){ -+ jointype = pSubitem->fg.jointype; /* First time through the loop */ - } - - /* The subquery uses a single slot of the FROM clause of the outer -@@ -133392,14 +149544,20 @@ static int flattenSubquery( - /* Transfer the FROM clause terms from the subquery into the - ** outer query. - */ -+ iNewParent = pSubSrc->a[0].iCursor; - for(i=0; ia[i+iFrom].pUsing); -- assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); -- pSrc->a[i+iFrom] = pSubSrc->a[i]; -- iNewParent = pSubSrc->a[i].iCursor; -+ SrcItem *pItem = &pSrc->a[i+iFrom]; -+ assert( pItem->fg.isTabFunc==0 ); -+ assert( pItem->fg.isSubquery -+ || pItem->fg.fixedSchema -+ || pItem->u4.zDatabase==0 ); -+ if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); -+ *pItem = pSubSrc->a[i]; -+ pItem->fg.jointype |= ltorj; - memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); - } -- pSrc->a[iFrom].fg.jointype = jointype; -+ pSrc->a[iFrom].fg.jointype &= JT_LTORJ; -+ pSrc->a[iFrom].fg.jointype |= jointype | ltorj; - - /* Now begin substituting subquery result set expressions for - ** references to the iParent in the outer query. -@@ -133418,7 +149576,7 @@ static int flattenSubquery( - ** ORDER BY column expression is identical to the iOrderByCol'th - ** expression returned by SELECT statement pSub. Since these values - ** do not necessarily correspond to columns in SELECT statement pParent, -- ** zero them before transfering the ORDER BY clause. -+ ** zero them before transferring the ORDER BY clause. - ** - ** Not doing this may cause an error if a subsequent call to this - ** function attempts to flatten a compound sub-query into pParent -@@ -133434,8 +149592,9 @@ static int flattenSubquery( - } - pWhere = pSub->pWhere; - pSub->pWhere = 0; -- if( isLeftJoin>0 ){ -- sqlite3SetJoinExpr(pWhere, iNewParent); -+ if( isOuterJoin>0 ){ -+ assert( pSubSrc->nSrc==1 ); -+ sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); - } - if( pWhere ){ - if( pParent->pWhere ){ -@@ -133449,8 +149608,9 @@ static int flattenSubquery( - x.pParse = pParse; - x.iTable = iParent; - x.iNewTable = iNewParent; -- x.isLeftJoin = isLeftJoin; -+ x.isOuterJoin = isOuterJoin; - x.pEList = pSub->pEList; -+ x.pCList = findLeftmostExprlist(pSub); - substSelect(&x, pParent, 0); - } - -@@ -133470,23 +149630,22 @@ static int flattenSubquery( - pSub->pLimit = 0; - } - -- /* Recompute the SrcList_item.colUsed masks for the flattened -+ /* Recompute the SrcItem.colUsed masks for the flattened - ** tables. */ - for(i=0; ia[i+iFrom]); - } - } - -- /* Finially, delete what is left of the subquery and return -- ** success. -+ /* Finally, delete what is left of the subquery and return success. - */ - sqlite3AggInfoPersistWalkerInit(&w, pParse); - sqlite3WalkSelect(&w,pSub1); - sqlite3SelectDelete(db, pSub1); - --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x100 ){ -- SELECTTRACE(0x100,pParse,p,("After flattening:\n")); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x4 ){ -+ TREETRACE(0x4,pParse,p,("After flattening:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif -@@ -133502,14 +149661,18 @@ static int flattenSubquery( - typedef struct WhereConst WhereConst; - struct WhereConst { - Parse *pParse; /* Parsing context */ -+ u8 *pOomFault; /* Pointer to pParse->db->mallocFailed */ - int nConst; /* Number for COLUMN=CONSTANT terms */ - int nChng; /* Number of times a constant is propagated */ -+ int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ -+ u32 mExcludeOn; /* Which ON expressions to exclude from considertion. -+ ** Either EP_OuterON or EP_InnerON|EP_OuterON */ - Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ - }; - - /* - ** Add a new entry to the pConst object. Except, do not add duplicate --** pColumn entires. Also, do not add if doing so would not be appropriate. -+** pColumn entries. Also, do not add if doing so would not be appropriate. - ** - ** The caller guarantees the pColumn is a column and pValue is a constant. - ** This routine has to do some additional checks before completing the -@@ -133523,7 +149686,7 @@ static void constInsert( - ){ - int i; - assert( pColumn->op==TK_COLUMN ); -- assert( sqlite3ExprIsConstant(pValue) ); -+ assert( sqlite3ExprIsConstant(pConst->pParse, pValue) ); - - if( ExprHasProperty(pColumn, EP_FixedCol) ) return; - if( sqlite3ExprAffinity(pValue)!=0 ) return; -@@ -133542,6 +149705,10 @@ static void constInsert( - return; /* Already present. Return without doing anything. */ - } - } -+ assert( SQLITE_AFF_NONEbHasAffBlob = 1; -+ } - - pConst->nConst++; - pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, -@@ -133562,8 +149729,12 @@ static void constInsert( - */ - static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ - Expr *pRight, *pLeft; -- if( pExpr==0 ) return; -- if( ExprHasProperty(pExpr, EP_FromJoin) ) return; -+ if( NEVER(pExpr==0) ) return; -+ if( ExprHasProperty(pExpr, pConst->mExcludeOn) ){ -+ testcase( ExprHasProperty(pExpr, EP_OuterON) ); -+ testcase( ExprHasProperty(pExpr, EP_InnerON) ); -+ return; -+ } - if( pExpr->op==TK_AND ){ - findConstInWhere(pConst, pExpr->pRight); - findConstInWhere(pConst, pExpr->pLeft); -@@ -133574,46 +149745,94 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ - pLeft = pExpr->pLeft; - assert( pRight!=0 ); - assert( pLeft!=0 ); -- if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){ -+ if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){ - constInsert(pConst,pRight,pLeft,pExpr); - } -- if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){ -+ if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){ - constInsert(pConst,pLeft,pRight,pExpr); - } - } - - /* --** This is a Walker expression callback. pExpr is a candidate expression --** to be replaced by a value. If pExpr is equivalent to one of the --** columns named in pWalker->u.pConst, then overwrite it with its --** corresponding value. -+** This is a helper function for Walker callback propagateConstantExprRewrite(). -+** -+** Argument pExpr is a candidate expression to be replaced by a value. If -+** pExpr is equivalent to one of the columns named in pWalker->u.pConst, -+** then overwrite it with the corresponding value. Except, do not do so -+** if argument bIgnoreAffBlob is non-zero and the affinity of pExpr -+** is SQLITE_AFF_BLOB. - */ --static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ -+static int propagateConstantExprRewriteOne( -+ WhereConst *pConst, -+ Expr *pExpr, -+ int bIgnoreAffBlob -+){ - int i; -- WhereConst *pConst; -+ if( pConst->pOomFault[0] ) return WRC_Prune; - if( pExpr->op!=TK_COLUMN ) return WRC_Continue; -- if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ -+ if( ExprHasProperty(pExpr, EP_FixedCol|pConst->mExcludeOn) ){ - testcase( ExprHasProperty(pExpr, EP_FixedCol) ); -- testcase( ExprHasProperty(pExpr, EP_FromJoin) ); -+ testcase( ExprHasProperty(pExpr, EP_OuterON) ); -+ testcase( ExprHasProperty(pExpr, EP_InnerON) ); - return WRC_Continue; - } -- pConst = pWalker->u.pConst; - for(i=0; inConst; i++){ - Expr *pColumn = pConst->apExpr[i*2]; - if( pColumn==pExpr ) continue; - if( pColumn->iTable!=pExpr->iTable ) continue; - if( pColumn->iColumn!=pExpr->iColumn ) continue; -+ assert( SQLITE_AFF_NONEnChng++; - ExprClearProperty(pExpr, EP_Leaf); - ExprSetProperty(pExpr, EP_FixedCol); - assert( pExpr->pLeft==0 ); - pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); -+ if( pConst->pParse->db->mallocFailed ) return WRC_Prune; - break; - } - return WRC_Prune; - } - -+/* -+** This is a Walker expression callback. pExpr is a node from the WHERE -+** clause of a SELECT statement. This function examines pExpr to see if -+** any substitutions based on the contents of pWalker->u.pConst should -+** be made to pExpr or its immediate children. -+** -+** A substitution is made if: -+** -+** + pExpr is a column with an affinity other than BLOB that matches -+** one of the columns in pWalker->u.pConst, or -+** -+** + pExpr is a binary comparison operator (=, <=, >=, <, >) that -+** uses an affinity other than TEXT and one of its immediate -+** children is a column that matches one of the columns in -+** pWalker->u.pConst. -+*/ -+static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ -+ WhereConst *pConst = pWalker->u.pConst; -+ assert( TK_GT==TK_EQ+1 ); -+ assert( TK_LE==TK_EQ+2 ); -+ assert( TK_LT==TK_EQ+3 ); -+ assert( TK_GE==TK_EQ+4 ); -+ if( pConst->bHasAffBlob ){ -+ if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) -+ || pExpr->op==TK_IS -+ ){ -+ propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); -+ if( pConst->pOomFault[0] ) return WRC_Prune; -+ if( sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ -+ propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); -+ } -+ } -+ } -+ return propagateConstantExprRewriteOne(pConst, pExpr, pConst->bHasAffBlob); -+} -+ - /* - ** The WHERE-clause constant propagation optimization. - ** -@@ -133641,7 +149860,7 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ - ** SELECT * FROM t1 WHERE a=123 AND b=123; - ** - ** The two SELECT statements above should return different answers. b=a --** is alway true because the comparison uses numeric affinity, but b=123 -+** is always true because the comparison uses numeric affinity, but b=123 - ** is false because it uses text affinity and '0123' is not the same as '123'. - ** To work around this, the expression tree is not actually changed from - ** "b=a" to "b=123" but rather the "a" in "b=a" is tagged with EP_FixedCol -@@ -133649,6 +149868,21 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ - ** routines know to generate the constant "123" instead of looking up the - ** column value. Also, to avoid collation problems, this optimization is - ** only attempted if the "a=123" term uses the default BINARY collation. -+** -+** 2021-05-25 forum post 6a06202608: Another troublesome case is... -+** -+** CREATE TABLE t1(x); -+** INSERT INTO t1 VALUES(10.0); -+** SELECT 1 FROM t1 WHERE x=10 AND x LIKE 10; -+** -+** The query should return no rows, because the t1.x value is '10.0' not '10' -+** and '10.0' is not LIKE '10'. But if we are not careful, the first WHERE -+** term "x=10" will cause the second WHERE term to become "10 LIKE 10", -+** resulting in a false positive. To avoid this, constant propagation for -+** columns with BLOB affinity is only allowed if the constant is used with -+** operators ==, <=, <, >=, >, or IS in a way that will cause the correct -+** type conversions to occur. See logic associated with the bHasAffBlob flag -+** for details. - */ - static int propagateConstants( - Parse *pParse, /* The parsing context */ -@@ -133658,10 +149892,23 @@ static int propagateConstants( - Walker w; - int nChng = 0; - x.pParse = pParse; -+ x.pOomFault = &pParse->db->mallocFailed; - do{ - x.nConst = 0; - x.nChng = 0; - x.apExpr = 0; -+ x.bHasAffBlob = 0; -+ if( ALWAYS(p->pSrc!=0) -+ && p->pSrc->nSrc>0 -+ && (p->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 -+ ){ -+ /* Do not propagate constants on any ON clause if there is a -+ ** RIGHT JOIN anywhere in the query */ -+ x.mExcludeOn = EP_InnerON | EP_OuterON; -+ }else{ -+ /* Do not propagate constants through the ON clause of a LEFT JOIN */ -+ x.mExcludeOn = EP_OuterON; -+ } - findConstInWhere(&x, p->pWhere); - if( x.nConst ){ - memset(&w, 0, sizeof(w)); -@@ -133679,6 +149926,35 @@ static int propagateConstants( - return nChng; - } - -+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) -+# if !defined(SQLITE_OMIT_WINDOWFUNC) -+/* -+** This function is called to determine whether or not it is safe to -+** push WHERE clause expression pExpr down to FROM clause sub-query -+** pSubq, which contains at least one window function. Return 1 -+** if it is safe and the expression should be pushed down, or 0 -+** otherwise. -+** -+** It is only safe to push the expression down if it consists only -+** of constants and copies of expressions that appear in the PARTITION -+** BY clause of all window function used by the sub-query. It is safe -+** to filter out entire partitions, but not rows within partitions, as -+** this may change the results of the window functions. -+** -+** At the time this function is called it is guaranteed that -+** -+** * the sub-query uses only one distinct window frame, and -+** * that the window frame has a PARTITION BY clause. -+*/ -+static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ -+ assert( pSubq->pWin->pPartition ); -+ assert( (pSubq->selFlags & SF_MultiPart)==0 ); -+ assert( pSubq->pPrior==0 ); -+ return sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition); -+} -+# endif /* SQLITE_OMIT_WINDOWFUNC */ -+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ -+ - #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) - /* - ** Make copies of relevant WHERE clause terms of the outer query into -@@ -133694,6 +149970,19 @@ static int propagateConstants( - ** The hope is that the terms added to the inner query will make it more - ** efficient. - ** -+** NAME AMBIGUITY -+** -+** This optimization is called the "WHERE-clause push-down optimization" -+** or sometimes the "predicate push-down optimization". -+** -+** Do not confuse this optimization with another unrelated optimization -+** with a similar name: The "MySQL push-down optimization" causes WHERE -+** clause terms that can be evaluated using only the index and without -+** reference to the table are run first, so that if they are false, -+** unnecessary table seeks are avoided. -+** -+** RULES -+** - ** Do not attempt this optimization if: - ** - ** (1) (** This restriction was removed on 2017-09-29. We used to -@@ -133726,9 +150015,51 @@ static int propagateConstants( - ** But if the (b2=2) term were to be pushed down into the bb subquery, - ** then the (1,1,NULL) row would be suppressed. - ** --** (6) The inner query features one or more window-functions (since --** changes to the WHERE clause of the inner query could change the --** window over which window functions are calculated). -+** (6) Window functions make things tricky as changes to the WHERE clause -+** of the inner query could change the window over which window -+** functions are calculated. Therefore, do not attempt the optimization -+** if: -+** -+** (6a) The inner query uses multiple incompatible window partitions. -+** -+** (6b) The inner query is a compound and uses window-functions. -+** -+** (6c) The WHERE clause does not consist entirely of constants and -+** copies of expressions found in the PARTITION BY clause of -+** all window-functions used by the sub-query. It is safe to -+** filter out entire partitions, as this does not change the -+** window over which any window-function is calculated. -+** -+** (7) The inner query is a Common Table Expression (CTE) that should -+** be materialized. (This restriction is implemented in the calling -+** routine.) -+** -+** (8) If the subquery is a compound that uses UNION, INTERSECT, -+** or EXCEPT, then all of the result set columns for all arms of -+** the compound must use the BINARY collating sequence. -+** -+** (9) All three of the following are true: -+** -+** (9a) The WHERE clause expression originates in the ON or USING clause -+** of a join (either an INNER or an OUTER join), and -+** -+** (9b) The subquery is to the right of the ON/USING clause -+** -+** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING -+** clause and the subquery. -+** -+** Without this restriction, the WHERE-clause push-down optimization -+** might move the ON/USING filter expression from the left side of a -+** RIGHT JOIN over to the right side, which leads to incorrect answers. -+** See also restriction (6) in sqlite3ExprIsSingleTableConstraint(). -+** -+** (10) The inner query is not the right-hand table of a RIGHT JOIN. -+** -+** (11) The subquery is not a VALUES clause -+** -+** (12) The WHERE clause is not "rowid ISNULL" or the equivalent. This -+** case only comes up if SQLite is compiled using -+** SQLITE_ALLOW_ROWID_IN_VIEW. - ** - ** Return 0 if no changes are made and non-zero if one or more WHERE clause - ** terms are duplicated into the subquery. -@@ -133737,20 +150068,56 @@ static int pushDownWhereTerms( - Parse *pParse, /* Parse context (for malloc() and error reporting) */ - Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ - Expr *pWhere, /* The WHERE clause of the outer query */ -- int iCursor, /* Cursor number of the subquery */ -- int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */ -+ SrcList *pSrcList, /* The complete from clause of the outer query */ -+ int iSrc /* Which FROM clause term to try to push into */ - ){ - Expr *pNew; -+ SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */ - int nChng = 0; -- Select *pSel; -+ pSrc = &pSrcList->a[iSrc]; - if( pWhere==0 ) return 0; -- if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */ -+ if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ){ -+ return 0; /* restrictions (2) and (11) */ -+ } -+ if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ){ -+ return 0; /* restrictions (10) */ -+ } - -+ if( pSubq->pPrior ){ -+ Select *pSel; -+ int notUnionAll = 0; -+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){ -+ u8 op = pSel->op; -+ assert( op==TK_ALL || op==TK_SELECT -+ || op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT ); -+ if( op!=TK_ALL && op!=TK_SELECT ){ -+ notUnionAll = 1; -+ } - #ifndef SQLITE_OMIT_WINDOWFUNC -- for(pSel=pSubq; pSel; pSel=pSel->pPrior){ -- if( pSel->pWin ) return 0; /* restriction (6) */ -- } -+ if( pSel->pWin ) return 0; /* restriction (6b) */ - #endif -+ } -+ if( notUnionAll ){ -+ /* If any of the compound arms are connected using UNION, INTERSECT, -+ ** or EXCEPT, then we must ensure that none of the columns use a -+ ** non-BINARY collating sequence. */ -+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){ -+ int ii; -+ const ExprList *pList = pSel->pEList; -+ assert( pList!=0 ); -+ for(ii=0; iinExpr; ii++){ -+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[ii].pExpr); -+ if( !sqlite3IsBinary(pColl) ){ -+ return 0; /* Restriction (8) */ -+ } -+ } -+ } -+ } -+ }else{ -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0; -+#endif -+ } - - #ifdef SQLITE_DEBUG - /* Only the first term of a compound can have a WITH clause. But make -@@ -133769,31 +150136,75 @@ static int pushDownWhereTerms( - return 0; /* restriction (3) */ - } - while( pWhere->op==TK_AND ){ -- nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, -- iCursor, isLeftJoin); -+ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc); - pWhere = pWhere->pLeft; - } -+ -+#if 0 /* These checks now done by sqlite3ExprIsSingleTableConstraint() */ -+ if( ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) /* (9a) */ -+ && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (9c) */ -+ ){ -+ int jj; -+ for(jj=0; jjw.iJoin==pSrcList->a[jj].iCursor ){ -+ /* If we reach this point, both (9a) and (9b) are satisfied. -+ ** The following loop checks (9c): -+ */ -+ for(jj++; jja[jj].fg.jointype & JT_RIGHT)!=0 ){ -+ return 0; /* restriction (9) */ -+ } -+ } -+ } -+ } -+ } - if( isLeftJoin -- && (ExprHasProperty(pWhere,EP_FromJoin)==0 -- || pWhere->iRightJoinTable!=iCursor) -+ && (ExprHasProperty(pWhere,EP_OuterON)==0 -+ || pWhere->w.iJoin!=iCursor) - ){ - return 0; /* restriction (4) */ - } -- if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){ -+ if( ExprHasProperty(pWhere,EP_OuterON) -+ && pWhere->w.iJoin!=iCursor -+ ){ - return 0; /* restriction (5) */ - } -- if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ -+#endif -+ -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ if( ViewCanHaveRowid && (pWhere->op==TK_ISNULL || pWhere->op==TK_NOTNULL) ){ -+ Expr *pLeft = pWhere->pLeft; -+ if( ALWAYS(pLeft) -+ && pLeft->op==TK_COLUMN -+ && pLeft->iColumn < 0 -+ ){ -+ return 0; /* Restriction (12) */ -+ } -+ } -+#endif -+ -+ if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){ - nChng++; -+ pSubq->selFlags |= SF_PushDown; - while( pSubq ){ - SubstContext x; - pNew = sqlite3ExprDup(pParse->db, pWhere, 0); -- unsetJoinExpr(pNew, -1); -+ unsetJoinExpr(pNew, -1, 1); - x.pParse = pParse; -- x.iTable = iCursor; -- x.iNewTable = iCursor; -- x.isLeftJoin = 0; -+ x.iTable = pSrc->iCursor; -+ x.iNewTable = pSrc->iCursor; -+ x.isOuterJoin = 0; - x.pEList = pSubq->pEList; -+ x.pCList = findLeftmostExprlist(pSubq); - pNew = substExpr(&x, pNew); -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){ -+ /* Restriction 6c has prevented push-down in this case */ -+ sqlite3ExprDelete(pParse->db, pNew); -+ nChng--; -+ break; -+ } -+#endif - if( pSubq->selFlags & SF_Aggregate ){ - pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew); - }else{ -@@ -133806,6 +150217,78 @@ static int pushDownWhereTerms( - } - #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ - -+/* -+** Check to see if a subquery contains result-set columns that are -+** never used. If it does, change the value of those result-set columns -+** to NULL so that they do not cause unnecessary work to compute. -+** -+** Return the number of column that were changed to NULL. -+*/ -+static int disableUnusedSubqueryResultColumns(SrcItem *pItem){ -+ int nCol; -+ Select *pSub; /* The subquery to be simplified */ -+ Select *pX; /* For looping over compound elements of pSub */ -+ Table *pTab; /* The table that describes the subquery */ -+ int j; /* Column number */ -+ int nChng = 0; /* Number of columns converted to NULL */ -+ Bitmask colUsed; /* Columns that may not be NULLed out */ -+ -+ assert( pItem!=0 ); -+ if( pItem->fg.isCorrelated || pItem->fg.isCte ){ -+ return 0; -+ } -+ assert( pItem->pSTab!=0 ); -+ pTab = pItem->pSTab; -+ assert( pItem->fg.isSubquery ); -+ pSub = pItem->u4.pSubq->pSelect; -+ assert( pSub->pEList->nExpr==pTab->nCol ); -+ for(pX=pSub; pX; pX=pX->pPrior){ -+ if( (pX->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){ -+ testcase( pX->selFlags & SF_Distinct ); -+ testcase( pX->selFlags & SF_Aggregate ); -+ return 0; -+ } -+ if( pX->pPrior && pX->op!=TK_ALL ){ -+ /* This optimization does not work for compound subqueries that -+ ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */ -+ return 0; -+ } -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ if( pX->pWin ){ -+ /* This optimization does not work for subqueries that use window -+ ** functions. */ -+ return 0; -+ } -+#endif -+ } -+ colUsed = pItem->colUsed; -+ if( pSub->pOrderBy ){ -+ ExprList *pList = pSub->pOrderBy; -+ for(j=0; jnExpr; j++){ -+ u16 iCol = pList->a[j].u.x.iOrderByCol; -+ if( iCol>0 ){ -+ iCol--; -+ colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); -+ } -+ } -+ } -+ nCol = pTab->nCol; -+ for(j=0; jpPrior) { -+ Expr *pY = pX->pEList->a[j].pExpr; -+ if( pY->op==TK_NULL ) continue; -+ pY->op = TK_NULL; -+ ExprClearProperty(pY, EP_Skip|EP_Unlikely); -+ pX->selFlags |= SF_PushDown; -+ nChng++; -+ } -+ } -+ return nChng; -+} -+ -+ - /* - ** The pFunc is the only aggregate function in the query. Check to see - ** if the query is a candidate for the min/max optimization. -@@ -133824,7 +150307,7 @@ static int pushDownWhereTerms( - */ - static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ - int eRet = WHERE_ORDERBY_NORMAL; /* Return value */ -- ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */ -+ ExprList *pEList; /* Arguments to agg function */ - const char *zFunc; /* Name of aggregate function pFunc */ - ExprList *pOrderBy; - u8 sortFlags = 0; -@@ -133832,9 +150315,16 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ - assert( *ppMinMax==0 ); - assert( pFunc->op==TK_AGG_FUNCTION ); - assert( !IsWindowFunc(pFunc) ); -- if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){ -+ assert( ExprUseXList(pFunc) ); -+ pEList = pFunc->x.pList; -+ if( pEList==0 -+ || pEList->nExpr!=1 -+ || ExprHasProperty(pFunc, EP_WinFunc) -+ || OptimizationDisabled(db, SQLITE_MinMaxOpt) -+ ){ - return eRet; - } -+ assert( !ExprHasProperty(pFunc, EP_IntValue) ); - zFunc = pFunc->u.zToken; - if( sqlite3StrICmp(zFunc, "min")==0 ){ - eRet = WHERE_ORDERBY_MIN; -@@ -133849,7 +150339,7 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ - } - *ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0); - assert( pOrderBy!=0 || db->mallocFailed ); -- if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags; -+ if( pOrderBy ) pOrderBy->a[0].fg.sortFlags = sortFlags; - return eRet; - } - -@@ -133862,7 +150352,13 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ - ** - ** where table is a database table, not a sub-select or view. If the query - ** does match this pattern, then a pointer to the Table object representing --** is returned. Otherwise, 0 is returned. -+** is returned. Otherwise, NULL is returned. -+** -+** This routine checks to see if it is safe to use the count optimization. -+** A correct answer is still obtained (though perhaps more slowly) if -+** this routine returns NULL when it could have returned a table pointer. -+** But returning the pointer when NULL should have been returned can -+** result in incorrect answers and/or crashes. So, when in doubt, return NULL. - */ - static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ - Table *pTab; -@@ -133870,19 +150366,27 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ - - assert( !p->pGroupBy ); - -- if( p->pWhere || p->pEList->nExpr!=1 -- || p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect -+ if( p->pWhere -+ || p->pEList->nExpr!=1 -+ || p->pSrc->nSrc!=1 -+ || p->pSrc->a[0].fg.isSubquery -+ || pAggInfo->nFunc!=1 -+ || p->pHaving - ){ - return 0; - } -- pTab = p->pSrc->a[0].pTab; -+ pTab = p->pSrc->a[0].pSTab; -+ assert( pTab!=0 ); -+ assert( !IsView(pTab) ); -+ if( !IsOrdinaryTable(pTab) ) return 0; - pExpr = p->pEList->a[0].pExpr; -- assert( pTab && !pTab->pSelect && pExpr ); -- -- if( IsVirtual(pTab) ) return 0; -+ assert( pExpr!=0 ); - if( pExpr->op!=TK_AGG_FUNCTION ) return 0; -- if( NEVER(pAggInfo->nFunc==0) ) return 0; -+ if( pExpr->pAggInfo!=pAggInfo ) return 0; - if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0; -+ assert( pAggInfo->aFunc[0].pFExpr==pExpr ); -+ testcase( ExprHasProperty(pExpr, EP_Distinct) ); -+ testcase( ExprHasProperty(pExpr, EP_WinFunc) ); - if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0; - - return pTab; -@@ -133895,24 +150399,27 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ - ** SQLITE_ERROR and leave an error in pParse. Otherwise, populate - ** pFrom->pIndex and return SQLITE_OK. - */ --SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){ -- if( pFrom->pTab && pFrom->fg.isIndexedBy ){ -- Table *pTab = pFrom->pTab; -- char *zIndexedBy = pFrom->u1.zIndexedBy; -- Index *pIdx; -- for(pIdx=pTab->pIndex; -- pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy); -- pIdx=pIdx->pNext -- ); -- if( !pIdx ){ -- sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0); -- pParse->checkSchema = 1; -- return SQLITE_ERROR; -- } -- pFrom->pIBIndex = pIdx; -+SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ -+ Table *pTab = pFrom->pSTab; -+ char *zIndexedBy = pFrom->u1.zIndexedBy; -+ Index *pIdx; -+ assert( pTab!=0 ); -+ assert( pFrom->fg.isIndexedBy!=0 ); -+ -+ for(pIdx=pTab->pIndex; -+ pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy); -+ pIdx=pIdx->pNext -+ ); -+ if( !pIdx ){ -+ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0); -+ pParse->checkSchema = 1; -+ return SQLITE_ERROR; - } -+ assert( pFrom->fg.isCte==0 ); -+ pFrom->u2.pIBIndex = pIdx; - return SQLITE_OK; - } -+ - /* - ** Detect compound SELECT statements that use an ORDER BY clause with - ** an alternative collating sequence. -@@ -133928,7 +150435,7 @@ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pF - ** above that generates the code for a compound SELECT with an ORDER BY clause - ** uses a merge algorithm that requires the same collating sequence on the - ** result columns as on the ORDER BY clause. See ticket --** http://www.sqlite.org/src/info/6709574d2a -+** http://sqlite.org/src/info/6709574d2a - ** - ** This transformation is only needed for EXCEPT, INTERSECT, and UNION. - ** The UNION ALL operator works fine with multiSelectOrderBy() even when -@@ -133969,8 +150476,12 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ - pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); - if( pNew==0 ) return WRC_Abort; - memset(&dummy, 0, sizeof(dummy)); -- pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0); -- if( pNewSrc==0 ) return WRC_Abort; -+ pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); -+ assert( pNewSrc!=0 || pParse->nErr ); -+ if( pParse->nErr ){ -+ sqlite3SrcListDelete(db, pNewSrc); -+ return WRC_Abort; -+ } - *pNew = *p; - p->pSrc = pNewSrc; - p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0)); -@@ -133985,7 +150496,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ - #ifndef SQLITE_OMIT_WINDOWFUNC - p->pWinDefn = 0; - #endif -- p->selFlags &= ~SF_Compound; -+ p->selFlags &= ~(u32)SF_Compound; - assert( (p->selFlags & SF_Converted)==0 ); - p->selFlags |= SF_Converted; - assert( pNew->pPrior!=0 ); -@@ -133999,7 +150510,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ - ** arguments. If it does, leave an error message in pParse and return - ** non-zero, since pFrom is not allowed to be a table-valued function. - */ --static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){ -+static int cannotBeFunction(Parse *pParse, SrcItem *pFrom){ - if( pFrom->fg.isTabFunc ){ - sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName); - return 1; -@@ -134020,21 +150531,22 @@ static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){ - */ - static struct Cte *searchWith( - With *pWith, /* Current innermost WITH clause */ -- struct SrcList_item *pItem, /* FROM clause element to resolve */ -+ SrcItem *pItem, /* FROM clause element to resolve */ - With **ppContext /* OUT: WITH clause return value belongs to */ - ){ -- const char *zName; -- if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){ -- With *p; -- for(p=pWith; p; p=p->pOuter){ -- int i; -- for(i=0; inCte; i++){ -- if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){ -- *ppContext = p; -- return &p->a[i]; -- } -+ const char *zName = pItem->zName; -+ With *p; -+ assert( pItem->fg.fixedSchema || pItem->u4.zDatabase==0 ); -+ assert( zName!=0 ); -+ for(p=pWith; p; p=p->pOuter){ -+ int i; -+ for(i=0; inCte; i++){ -+ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){ -+ *ppContext = p; -+ return &p->a[i]; - } - } -+ if( p->bView ) break; - } - return 0; - } -@@ -134044,58 +150556,92 @@ static struct Cte *searchWith( - ** - ** This routine pushes the WITH clause passed as the second argument - ** onto the top of the stack. If argument bFree is true, then this --** WITH clause will never be popped from the stack. In this case it --** should be freed along with the Parse object. In other cases, when -+** WITH clause will never be popped from the stack but should instead -+** be freed along with the Parse object. In other cases, when - ** bFree==0, the With object will be freed along with the SELECT - ** statement with which it is associated. -+** -+** This routine returns a copy of pWith. Or, if bFree is true and -+** the pWith object is destroyed immediately due to an OOM condition, -+** then this routine return NULL. -+** -+** If bFree is true, do not continue to use the pWith pointer after -+** calling this routine, Instead, use only the return value. - */ --SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ -- assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) ); -+SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ - if( pWith ){ -- assert( pParse->pWith!=pWith ); -- pWith->pOuter = pParse->pWith; -- pParse->pWith = pWith; -- if( bFree ) pParse->pWithToFree = pWith; -+ if( bFree ){ -+ pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric, -+ pWith); -+ if( pWith==0 ) return 0; -+ } -+ if( pParse->nErr==0 ){ -+ assert( pParse->pWith!=pWith ); -+ pWith->pOuter = pParse->pWith; -+ pParse->pWith = pWith; -+ } - } -+ return pWith; - } - - /* - ** This function checks if argument pFrom refers to a CTE declared by --** a WITH clause on the stack currently maintained by the parser. And, --** if currently processing a CTE expression, if it is a recursive --** reference to the current CTE. -+** a WITH clause on the stack currently maintained by the parser (on the -+** pParse->pWith linked list). And if currently processing a CTE -+** CTE expression, through routine checks to see if the reference is -+** a recursive reference to the CTE. - ** --** If pFrom falls into either of the two categories above, pFrom->pTab --** and other fields are populated accordingly. The caller should check --** (pFrom->pTab!=0) to determine whether or not a successful match --** was found. -+** If pFrom matches a CTE according to either of these two above, pFrom->pTab -+** and other fields are populated accordingly. - ** --** Whether or not a match is found, SQLITE_OK is returned if no error --** occurs. If an error does occur, an error message is stored in the --** parser and some error code other than SQLITE_OK returned. -+** Return 0 if no match is found. -+** Return 1 if a match is found. -+** Return 2 if an error condition is detected. - */ --static int withExpand( -- Walker *pWalker, -- struct SrcList_item *pFrom -+static int resolveFromTermToCte( -+ Parse *pParse, /* The parsing context */ -+ Walker *pWalker, /* Current tree walker */ -+ SrcItem *pFrom /* The FROM clause term to check */ - ){ -- Parse *pParse = pWalker->pParse; -- sqlite3 *db = pParse->db; -- struct Cte *pCte; /* Matched CTE (or NULL if no match) */ -- With *pWith; /* WITH clause that pCte belongs to */ -+ Cte *pCte; /* Matched CTE (or NULL if no match) */ -+ With *pWith; /* The matching WITH */ - -- assert( pFrom->pTab==0 ); -+ assert( pFrom->pSTab==0 ); -+ if( pParse->pWith==0 ){ -+ /* There are no WITH clauses in the stack. No match is possible */ -+ return 0; -+ } - if( pParse->nErr ){ -- return SQLITE_ERROR; -+ /* Prior errors might have left pParse->pWith in a goofy state, so -+ ** go no further. */ -+ return 0; -+ } -+ assert( pFrom->fg.hadSchema==0 || pFrom->fg.notCte!=0 ); -+ if( pFrom->fg.fixedSchema==0 && pFrom->u4.zDatabase!=0 ){ -+ /* The FROM term contains a schema qualifier (ex: main.t1) and so -+ ** it cannot possibly be a CTE reference. */ -+ return 0; -+ } -+ if( pFrom->fg.notCte ){ -+ /* The FROM term is specifically excluded from matching a CTE. -+ ** (1) It is part of a trigger that used to have zDatabase but had -+ ** zDatabase removed by sqlite3FixTriggerStep(). -+ ** (2) This is the first term in the FROM clause of an UPDATE. -+ */ -+ return 0; - } -- - pCte = searchWith(pParse->pWith, pFrom, &pWith); - if( pCte ){ -+ sqlite3 *db = pParse->db; - Table *pTab; - ExprList *pEList; - Select *pSel; - Select *pLeft; /* Left-most SELECT statement */ -+ Select *pRecTerm; /* Left-most recursive term */ - int bMayRecursive; /* True if compound joined by UNION [ALL] */ - With *pSavedWith; /* Initial value of pParse->pWith */ -+ int iRecTab = -1; /* Cursor for recursive table */ -+ CteUse *pCteUse; - - /* If pCte->zCteErr is non-NULL at this point, then this is an illegal - ** recursive reference to CTE pCte. Leave an error in pParse and return -@@ -134103,63 +150649,100 @@ static int withExpand( - ** In this case, proceed. */ - if( pCte->zCteErr ){ - sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName); -- return SQLITE_ERROR; -+ return 2; - } -- if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR; -+ if( cannotBeFunction(pParse, pFrom) ) return 2; - -- assert( pFrom->pTab==0 ); -- pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); -- if( pTab==0 ) return WRC_Abort; -+ assert( pFrom->pSTab==0 ); -+ pTab = sqlite3DbMallocZero(db, sizeof(Table)); -+ if( pTab==0 ) return 2; -+ pCteUse = pCte->pUse; -+ if( pCteUse==0 ){ -+ pCte->pUse = pCteUse = sqlite3DbMallocZero(db, sizeof(pCteUse[0])); -+ if( pCteUse==0 -+ || sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCteUse)==0 -+ ){ -+ sqlite3DbFree(db, pTab); -+ return 2; -+ } -+ pCteUse->eM10d = pCte->eM10d; -+ } -+ pFrom->pSTab = pTab; - pTab->nTabRef = 1; - pTab->zName = sqlite3DbStrDup(db, pCte->zName); - pTab->iPKey = -1; - pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); - pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; -- pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); -- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; -- assert( pFrom->pSelect ); -+ sqlite3SrcItemAttachSubquery(pParse, pFrom, pCte->pSelect, 1); -+ if( db->mallocFailed ) return 2; -+ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); -+ pSel = pFrom->u4.pSubq->pSelect; -+ assert( pSel!=0 ); -+ pSel->selFlags |= SF_CopyCte; -+ if( pFrom->fg.isIndexedBy ){ -+ sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy); -+ return 2; -+ } -+ assert( !pFrom->fg.isIndexedBy ); -+ pFrom->fg.isCte = 1; -+ pFrom->u2.pCteUse = pCteUse; -+ pCteUse->nUse++; - - /* Check if this is a recursive CTE. */ -- pSel = pFrom->pSelect; -+ pRecTerm = pSel; - bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION ); -- if( bMayRecursive ){ -+ while( bMayRecursive && pRecTerm->op==pSel->op ){ - int i; -- SrcList *pSrc = pFrom->pSelect->pSrc; -+ SrcList *pSrc = pRecTerm->pSrc; -+ assert( pRecTerm->pPrior!=0 ); - for(i=0; inSrc; i++){ -- struct SrcList_item *pItem = &pSrc->a[i]; -- if( pItem->zDatabase==0 -- && pItem->zName!=0 -+ SrcItem *pItem = &pSrc->a[i]; -+ if( pItem->zName!=0 -+ && !pItem->fg.hadSchema -+ && ALWAYS( !pItem->fg.isSubquery ) -+ && (pItem->fg.fixedSchema || pItem->u4.zDatabase==0) - && 0==sqlite3StrICmp(pItem->zName, pCte->zName) -- ){ -- pItem->pTab = pTab; -- pItem->fg.isRecursive = 1; -+ ){ -+ pItem->pSTab = pTab; - pTab->nTabRef++; -- pSel->selFlags |= SF_Recursive; -+ pItem->fg.isRecursive = 1; -+ if( pRecTerm->selFlags & SF_Recursive ){ -+ sqlite3ErrorMsg(pParse, -+ "multiple references to recursive table: %s", pCte->zName -+ ); -+ return 2; -+ } -+ pRecTerm->selFlags |= SF_Recursive; -+ if( iRecTab<0 ) iRecTab = pParse->nTab++; -+ pItem->iCursor = iRecTab; - } - } -+ if( (pRecTerm->selFlags & SF_Recursive)==0 ) break; -+ pRecTerm = pRecTerm->pPrior; - } - -- /* Only one recursive reference is permitted. */ -- if( pTab->nTabRef>2 ){ -- sqlite3ErrorMsg( -- pParse, "multiple references to recursive table: %s", pCte->zName -- ); -- return SQLITE_ERROR; -- } -- assert( pTab->nTabRef==1 || -- ((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 )); -- - pCte->zCteErr = "circular reference: %s"; - pSavedWith = pParse->pWith; - pParse->pWith = pWith; -- if( bMayRecursive ){ -- Select *pPrior = pSel->pPrior; -- assert( pPrior->pWith==0 ); -- pPrior->pWith = pSel->pWith; -- sqlite3WalkSelect(pWalker, pPrior); -- pPrior->pWith = 0; -+ if( pSel->selFlags & SF_Recursive ){ -+ int rc; -+ assert( pRecTerm!=0 ); -+ assert( (pRecTerm->selFlags & SF_Recursive)==0 ); -+ assert( pRecTerm->pNext!=0 ); -+ assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 ); -+ assert( pRecTerm->pWith==0 ); -+ pRecTerm->pWith = pSel->pWith; -+ rc = sqlite3WalkSelect(pWalker, pRecTerm); -+ pRecTerm->pWith = 0; -+ if( rc ){ -+ pParse->pWith = pSavedWith; -+ return 2; -+ } - }else{ -- sqlite3WalkSelect(pWalker, pSel); -+ if( sqlite3WalkSelect(pWalker, pSel) ){ -+ pParse->pWith = pSavedWith; -+ return 2; -+ } - } - pParse->pWith = pWith; - -@@ -134171,7 +150754,7 @@ static int withExpand( - pCte->zName, pEList->nExpr, pCte->pCols->nExpr - ); - pParse->pWith = pSavedWith; -- return SQLITE_ERROR; -+ return 2; - } - pEList = pCte->pCols; - } -@@ -134187,9 +150770,9 @@ static int withExpand( - } - pCte->zCteErr = 0; - pParse->pWith = pSavedWith; -+ return 1; /* Success */ - } -- -- return SQLITE_OK; -+ return 0; /* No match */ - } - #endif - -@@ -134202,7 +150785,7 @@ static int withExpand( - ** sqlite3SelectExpand() when walking a SELECT tree to resolve table - ** names and other FROM clause elements. - */ --static void selectPopWith(Walker *pWalker, Select *p){ -+SQLITE_PRIVATE void sqlite3SelectPopWith(Walker *pWalker, Select *p){ - Parse *pParse = pWalker->pParse; - if( OK_IF_ALWAYS_TRUE(pParse->pWith) && p->pPrior==0 ){ - With *pWith = findRightmost(p)->pWith; -@@ -134212,39 +150795,72 @@ static void selectPopWith(Walker *pWalker, Select *p){ - } - } - } --#else --#define selectPopWith 0 - #endif - - /* --** The SrcList_item structure passed as the second argument represents a -+** The SrcItem structure passed as the second argument represents a - ** sub-query in the FROM clause of a SELECT statement. This function --** allocates and populates the SrcList_item.pTab object. If successful, -+** allocates and populates the SrcItem.pTab object. If successful, - ** SQLITE_OK is returned. Otherwise, if an OOM error is encountered, - ** SQLITE_NOMEM. - */ --SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){ -- Select *pSel = pFrom->pSelect; -+SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ -+ Select *pSel; - Table *pTab; - -+ assert( pFrom->fg.isSubquery ); -+ assert( pFrom->u4.pSubq!=0 ); -+ pSel = pFrom->u4.pSubq->pSelect; - assert( pSel ); -- pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); -+ pFrom->pSTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); - if( pTab==0 ) return SQLITE_NOMEM; - pTab->nTabRef = 1; - if( pFrom->zAlias ){ - pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias); - }else{ -- pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId); -+ pTab->zName = sqlite3MPrintf(pParse->db, "%!S", pFrom); - } - while( pSel->pPrior ){ pSel = pSel->pPrior; } - sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); - pTab->iPKey = -1; -+ pTab->eTabType = TABTYP_VIEW; - pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); -- pTab->tabFlags |= TF_Ephemeral; -- -+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW -+ /* The usual case - do not allow ROWID on a subquery */ -+ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; -+#else -+ /* Legacy compatibility mode */ -+ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid; -+#endif - return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; - } - -+ -+/* -+** Check the N SrcItem objects to the right of pBase. (N might be zero!) -+** If any of those SrcItem objects have a USING clause containing zName -+** then return true. -+** -+** If N is zero, or none of the N SrcItem objects to the right of pBase -+** contains a USING clause, or if none of the USING clauses contain zName, -+** then return false. -+*/ -+static int inAnyUsingClause( -+ const char *zName, /* Name we are looking for */ -+ SrcItem *pBase, /* The base SrcItem. Looking at pBase[1] and following */ -+ int N /* How many SrcItems to check */ -+){ -+ while( N>0 ){ -+ N--; -+ pBase++; -+ if( pBase->fg.isUsing==0 ) continue; -+ if( NEVER(pBase->u3.pUsing==0) ) continue; -+ if( sqlite3IdListIndex(pBase->u3.pUsing, zName)>=0 ) return 1; -+ } -+ return 0; -+} -+ -+ - /* - ** This routine is a Walker callback for "expanding" a SELECT statement. - ** "Expanding" means to do the following: -@@ -134271,10 +150887,10 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFr - */ - static int selectExpander(Walker *pWalker, Select *p){ - Parse *pParse = pWalker->pParse; -- int i, j, k; -+ int i, j, k, rc; - SrcList *pTabList; - ExprList *pEList; -- struct SrcList_item *pFrom; -+ SrcItem *pFrom; - sqlite3 *db = pParse->db; - Expr *pE, *pRight, *pExpr; - u16 selFlags = p->selFlags; -@@ -134294,6 +150910,15 @@ static int selectExpander(Walker *pWalker, Select *p){ - } - pTabList = p->pSrc; - pEList = p->pEList; -+ if( pParse->pWith && (p->selFlags & SF_View) ){ -+ if( p->pWith==0 ){ -+ p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) ); -+ if( p->pWith==0 ){ -+ return WRC_Abort; -+ } -+ } -+ p->pWith->bView = 1; -+ } - sqlite3WithPush(pParse, p->pWith, 0); - - /* Make sure cursor numbers have been assigned to all entries in -@@ -134307,31 +150932,35 @@ static int selectExpander(Walker *pWalker, Select *p){ - */ - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab; -- assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 ); -- if( pFrom->pTab ) continue; -+ assert( pFrom->fg.isRecursive==0 || pFrom->pSTab!=0 ); -+ if( pFrom->pSTab ) continue; - assert( pFrom->fg.isRecursive==0 ); --#ifndef SQLITE_OMIT_CTE -- if( withExpand(pWalker, pFrom) ) return WRC_Abort; -- if( pFrom->pTab ) {} else --#endif - if( pFrom->zName==0 ){ - #ifndef SQLITE_OMIT_SUBQUERY -- Select *pSel = pFrom->pSelect; -+ Select *pSel; -+ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq!=0 ); -+ pSel = pFrom->u4.pSubq->pSelect; - /* A sub-query in the FROM clause of a SELECT */ - assert( pSel!=0 ); -- assert( pFrom->pTab==0 ); -+ assert( pFrom->pSTab==0 ); - if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; - if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort; -+#endif -+#ifndef SQLITE_OMIT_CTE -+ }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){ -+ if( rc>1 ) return WRC_Abort; -+ pTab = pFrom->pSTab; -+ assert( pTab!=0 ); - #endif - }else{ - /* An ordinary table or view name in the FROM clause */ -- assert( pFrom->pTab==0 ); -- pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); -+ assert( pFrom->pSTab==0 ); -+ pFrom->pSTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); - if( pTab==0 ) return WRC_Abort; - if( pTab->nTabRef>=0xffff ){ - sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535", - pTab->zName); -- pFrom->pTab = 0; -+ pFrom->pSTab = 0; - return WRC_Abort; - } - pTab->nTabRef++; -@@ -134339,30 +150968,37 @@ static int selectExpander(Walker *pWalker, Select *p){ - return WRC_Abort; - } - #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) -- if( IsVirtual(pTab) || pTab->pSelect ){ -+ if( !IsOrdinaryTable(pTab) ){ - i16 nCol; - u8 eCodeOrig = pWalker->eCode; - if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; -- assert( pFrom->pSelect==0 ); -- if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){ -- sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", -- pTab->zName); -+ assert( pFrom->fg.isSubquery==0 ); -+ if( IsView(pTab) ){ -+ if( (db->flags & SQLITE_EnableView)==0 -+ && pTab->pSchema!=db->aDb[1].pSchema -+ ){ -+ sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", -+ pTab->zName); -+ } -+ sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1); - } - #ifndef SQLITE_OMIT_VIRTUALTABLE -- if( IsVirtual(pTab) -+ else if( ALWAYS(IsVirtual(pTab)) - && pFrom->fg.fromDDL -- && ALWAYS(pTab->pVTable!=0) -- && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) -+ && ALWAYS(pTab->u.vtab.p!=0) -+ && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) - ){ - sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", - pTab->zName); - } -+ assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 ); - #endif -- pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0); - nCol = pTab->nCol; - pTab->nCol = -1; - pWalker->eCode = 1; /* Turn on Select.selId renumbering */ -- sqlite3WalkSelect(pWalker, pFrom->pSelect); -+ if( pFrom->fg.isSubquery ){ -+ sqlite3WalkSelect(pWalker, pFrom->u4.pSubq->pSelect); -+ } - pWalker->eCode = eCodeOrig; - pTab->nCol = nCol; - } -@@ -134370,14 +151006,15 @@ static int selectExpander(Walker *pWalker, Select *p){ - } - - /* Locate the index named by the INDEXED BY clause, if any. */ -- if( sqlite3IndexedByLookup(pParse, pFrom) ){ -+ if( pFrom->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pFrom) ){ - return WRC_Abort; - } - } - - /* Process NATURAL keywords, and ON and USING clauses of joins. - */ -- if( pParse->nErr || db->mallocFailed || sqliteProcessJoin(pParse, p) ){ -+ assert( db->mallocFailed==0 || pParse->nErr!=0 ); -+ if( pParse->nErr || sqlite3ProcessJoin(pParse, p) ){ - return WRC_Abort; - } - -@@ -134425,7 +151062,7 @@ static int selectExpander(Walker *pWalker, Select *p){ - pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); - if( pNew ){ - pNew->a[pNew->nExpr-1].zEName = a[k].zEName; -- pNew->a[pNew->nExpr-1].eEName = a[k].eEName; -+ pNew->a[pNew->nExpr-1].fg.eEName = a[k].fg.eEName; - a[k].zEName = 0; - } - a[k].pExpr = 0; -@@ -134434,102 +151071,177 @@ static int selectExpander(Walker *pWalker, Select *p){ - ** expanded. */ - int tableSeen = 0; /* Set to 1 when TABLE matches */ - char *zTName = 0; /* text of name of TABLE */ -+ int iErrOfst; - if( pE->op==TK_DOT ){ -+ assert( (selFlags & SF_NestedFrom)==0 ); - assert( pE->pLeft!=0 ); - assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); - zTName = pE->pLeft->u.zToken; -+ assert( ExprUseWOfst(pE->pLeft) ); -+ iErrOfst = pE->pRight->w.iOfst; -+ }else{ -+ assert( ExprUseWOfst(pE) ); -+ iErrOfst = pE->w.iOfst; - } - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ -- Table *pTab = pFrom->pTab; -- Select *pSub = pFrom->pSelect; -- char *zTabName = pFrom->zAlias; -- const char *zSchemaName = 0; -- int iDb; -- if( zTabName==0 ){ -+ int nAdd; /* Number of cols including rowid */ -+ Table *pTab = pFrom->pSTab; /* Table for this data source */ -+ ExprList *pNestedFrom; /* Result-set of a nested FROM clause */ -+ char *zTabName; /* AS name for this data source */ -+ const char *zSchemaName = 0; /* Schema name for this data source */ -+ int iDb; /* Schema index for this data src */ -+ IdList *pUsing; /* USING clause for pFrom[1] */ -+ -+ if( (zTabName = pFrom->zAlias)==0 ){ - zTabName = pTab->zName; - } - if( db->mallocFailed ) break; -- if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ -- pSub = 0; -+ assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom) ); -+ if( pFrom->fg.isNestedFrom ){ -+ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); -+ assert( pFrom->u4.pSubq->pSelect!=0 ); -+ pNestedFrom = pFrom->u4.pSubq->pSelect->pEList; -+ assert( pNestedFrom!=0 ); -+ assert( pNestedFrom->nExpr==pTab->nCol ); -+ assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid ); -+ }else{ - if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ - continue; - } -+ pNestedFrom = 0; - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*"; - } -- for(j=0; jnCol; j++){ -- char *zName = pTab->aCol[j].zName; -- char *zColname; /* The computed column name */ -- char *zToFree; /* Malloced string that needs to be freed */ -- Token sColname; /* Computed column name as a token */ -- -- assert( zName ); -- if( zTName && pSub -- && sqlite3MatchEName(&pSub->pEList->a[j], 0, zTName, 0)==0 -- ){ -- continue; -+ if( i+1nSrc -+ && pFrom[1].fg.isUsing -+ && (selFlags & SF_NestedFrom)!=0 -+ ){ -+ int ii; -+ pUsing = pFrom[1].u3.pUsing; -+ for(ii=0; iinId; ii++){ -+ const char *zUName = pUsing->a[ii].zName; -+ pRight = sqlite3Expr(db, TK_ID, zUName); -+ sqlite3ExprSetErrorOffset(pRight, iErrOfst); -+ pNew = sqlite3ExprListAppend(pParse, pNew, pRight); -+ if( pNew ){ -+ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; -+ assert( pX->zEName==0 ); -+ pX->zEName = sqlite3MPrintf(db,"..%s", zUName); -+ pX->fg.eEName = ENAME_TAB; -+ pX->fg.bUsingTerm = 1; -+ } - } -+ }else{ -+ pUsing = 0; -+ } - -- /* If a column is marked as 'hidden', omit it from the expanded -- ** result-set list unless the SELECT has the SF_IncludeHidden -- ** bit set. -- */ -- if( (p->selFlags & SF_IncludeHidden)==0 -- && IsHiddenColumn(&pTab->aCol[j]) -- ){ -- continue; -- } -- tableSeen = 1; -+ nAdd = pTab->nCol; -+ if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++; -+ for(j=0; jnCol ){ -+ zName = sqlite3RowidAlias(pTab); -+ if( zName==0 ) continue; -+ }else{ -+ zName = pTab->aCol[j].zCnName; - -- if( i>0 && zTName==0 ){ -- if( (pFrom->fg.jointype & JT_NATURAL)!=0 -- && tableAndColumnIndex(pTabList, i, zName, 0, 0, 1) -+ /* If pTab is actually an SF_NestedFrom sub-select, do not -+ ** expand any ENAME_ROWID columns. */ -+ if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){ -+ continue; -+ } -+ -+ if( zTName -+ && pNestedFrom -+ && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0 -+ ){ -+ continue; -+ } -+ -+ /* If a column is marked as 'hidden', omit it from the expanded -+ ** result-set list unless the SELECT has the SF_IncludeHidden -+ ** bit set. -+ */ -+ if( (p->selFlags & SF_IncludeHidden)==0 -+ && IsHiddenColumn(&pTab->aCol[j]) -+ ){ -+ continue; -+ } -+ if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 -+ && zTName==0 -+ && (selFlags & (SF_NestedFrom))==0 - ){ -- /* In a NATURAL join, omit the join columns from the -- ** table to the right of the join */ - continue; - } -- if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ -+ } -+ assert( zName ); -+ tableSeen = 1; -+ -+ if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){ -+ if( pFrom->fg.isUsing -+ && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0 -+ ){ - /* In a join with a USING clause, omit columns in the - ** using clause from the table on the right. */ - continue; - } - } - pRight = sqlite3Expr(db, TK_ID, zName); -- zColname = zName; -- zToFree = 0; -- if( longNames || pTabList->nSrc>1 ){ -+ if( (pTabList->nSrc>1 -+ && ( (pFrom->fg.jointype & JT_LTORJ)==0 -+ || (selFlags & SF_NestedFrom)!=0 -+ || !inAnyUsingClause(zName,pFrom,pTabList->nSrc-i-1) -+ ) -+ ) -+ || IN_RENAME_OBJECT -+ ){ - Expr *pLeft; - pLeft = sqlite3Expr(db, TK_ID, zTabName); - pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); -+ if( IN_RENAME_OBJECT && pE->pLeft ){ -+ sqlite3RenameTokenRemap(pParse, pLeft, pE->pLeft); -+ } - if( zSchemaName ){ - pLeft = sqlite3Expr(db, TK_ID, zSchemaName); - pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr); - } -- if( longNames ){ -- zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); -- zToFree = zColname; -- } - }else{ - pExpr = pRight; - } -+ sqlite3ExprSetErrorOffset(pExpr, iErrOfst); - pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); -- sqlite3TokenInit(&sColname, zColname); -- sqlite3ExprListSetName(pParse, pNew, &sColname, 0); -- if( pNew && (p->selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ -- struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; -- sqlite3DbFree(db, pX->zEName); -- if( pSub ){ -- pX->zEName = sqlite3DbStrDup(db, pSub->pEList->a[j].zEName); -+ if( pNew==0 ){ -+ break; /* OOM */ -+ } -+ pX = &pNew->a[pNew->nExpr-1]; -+ assert( pX->zEName==0 ); -+ if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ -+ if( pNestedFrom && (!ViewCanHaveRowid || jnExpr) ){ -+ assert( jnExpr ); -+ pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName); - testcase( pX->zEName==0 ); - }else{ - pX->zEName = sqlite3MPrintf(db, "%s.%s.%s", -- zSchemaName, zTabName, zColname); -+ zSchemaName, zTabName, zName); - testcase( pX->zEName==0 ); - } -- pX->eEName = ENAME_TAB; -+ pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB); -+ if( (pFrom->fg.isUsing -+ && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0) -+ || (pUsing && sqlite3IdListIndex(pUsing, zName)>=0) -+ || (jnCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)) -+ ){ -+ pX->fg.bNoExpand = 1; -+ } -+ }else if( longNames ){ -+ pX->zEName = sqlite3MPrintf(db, "%s.%s", zTabName, zName); -+ pX->fg.eEName = ENAME_NAME; -+ }else{ -+ pX->zEName = sqlite3DbStrDup(db, zName); -+ pX->fg.eEName = ENAME_NAME; - } -- sqlite3DbFree(db, zToFree); - } - } - if( !tableSeen ){ -@@ -134553,6 +151265,12 @@ static int selectExpander(Walker *pWalker, Select *p){ - p->selFlags |= SF_ComplexResult; - } - } -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x8 ){ -+ TREETRACE(0x8,pParse,p,("After result-set wildcard expansion:\n")); -+ sqlite3TreeViewSelect(0, p, 0); -+ } -+#endif - return WRC_Continue; - } - -@@ -134589,7 +151307,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ - sqlite3WalkSelect(&w, pSelect); - } - w.xSelectCallback = selectExpander; -- w.xSelectCallback2 = selectPopWith; -+ w.xSelectCallback2 = sqlite3SelectPopWith; - w.eCode = 0; - sqlite3WalkSelect(&w, pSelect); - } -@@ -134600,37 +151318,33 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ - ** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo() - ** interface. - ** --** For each FROM-clause subquery, add Column.zType and Column.zColl --** information to the Table structure that represents the result set --** of that subquery. -+** For each FROM-clause subquery, add Column.zType, Column.zColl, and -+** Column.affinity information to the Table structure that represents -+** the result set of that subquery. - ** - ** The Table structure that represents the result set was constructed --** by selectExpander() but the type and collation information was omitted --** at that point because identifiers had not yet been resolved. This --** routine is called after identifier resolution. -+** by selectExpander() but the type and collation and affinity information -+** was omitted at that point because identifiers had not yet been resolved. -+** This routine is called after identifier resolution. - */ - static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ - Parse *pParse; - int i; - SrcList *pTabList; -- struct SrcList_item *pFrom; -+ SrcItem *pFrom; - -- assert( p->selFlags & SF_Resolved ); - if( p->selFlags & SF_HasTypeInfo ) return; - p->selFlags |= SF_HasTypeInfo; - pParse = pWalker->pParse; -+ assert( (p->selFlags & SF_Resolved) ); - pTabList = p->pSrc; - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ -- Table *pTab = pFrom->pTab; -+ Table *pTab = pFrom->pSTab; - assert( pTab!=0 ); -- if( (pTab->tabFlags & TF_Ephemeral)!=0 ){ -+ if( (pTab->tabFlags & TF_Ephemeral)!=0 && pFrom->fg.isSubquery ){ - /* A sub-query in the FROM clause of a SELECT */ -- Select *pSel = pFrom->pSelect; -- if( pSel ){ -- while( pSel->pPrior ) pSel = pSel->pPrior; -- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel, -- SQLITE_AFF_NONE); -- } -+ Select *pSel = pFrom->u4.pSubq->pSelect; -+ sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE); - } - } - } -@@ -134674,15 +151388,196 @@ SQLITE_PRIVATE void sqlite3SelectPrep( - NameContext *pOuterNC /* Name context for container */ - ){ - assert( p!=0 || pParse->db->mallocFailed ); -+ assert( pParse->db->pParse==pParse ); - if( pParse->db->mallocFailed ) return; - if( p->selFlags & SF_HasTypeInfo ) return; - sqlite3SelectExpand(pParse, p); -- if( pParse->nErr || pParse->db->mallocFailed ) return; -+ if( pParse->nErr ) return; - sqlite3ResolveSelectNames(pParse, p, pOuterNC); -- if( pParse->nErr || pParse->db->mallocFailed ) return; -+ if( pParse->nErr ) return; - sqlite3SelectAddTypeInfo(pParse, p); - } - -+#if TREETRACE_ENABLED -+/* -+** Display all information about an AggInfo object -+*/ -+static void printAggInfo(AggInfo *pAggInfo){ -+ int ii; -+ sqlite3DebugPrintf("AggInfo %d/%p:\n", -+ pAggInfo->selId, pAggInfo); -+ for(ii=0; iinColumn; ii++){ -+ struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; -+ sqlite3DebugPrintf( -+ "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d" -+ " iSorterColumn=%d %s\n", -+ ii, pCol->pTab ? pCol->pTab->zName : "NULL", -+ pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii, -+ pCol->iSorterColumn, -+ ii>=pAggInfo->nAccumulator ? "" : " Accumulator"); -+ sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); -+ } -+ for(ii=0; iinFunc; ii++){ -+ sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", -+ ii, pAggInfo->iFirstReg+pAggInfo->nColumn+ii); -+ sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0); -+ } -+} -+#endif /* TREETRACE_ENABLED */ -+ -+/* -+** Analyze the arguments to aggregate functions. Create new pAggInfo->aCol[] -+** entries for columns that are arguments to aggregate functions but which -+** are not otherwise used. -+** -+** The aCol[] entries in AggInfo prior to nAccumulator are columns that -+** are referenced outside of aggregate functions. These might be columns -+** that are part of the GROUP by clause, for example. Other database engines -+** would throw an error if there is a column reference that is not in the -+** GROUP BY clause and that is not part of an aggregate function argument. -+** But SQLite allows this. -+** -+** The aCol[] entries beginning with the aCol[nAccumulator] and following -+** are column references that are used exclusively as arguments to -+** aggregate functions. This routine is responsible for computing -+** (or recomputing) those aCol[] entries. -+*/ -+static void analyzeAggFuncArgs( -+ AggInfo *pAggInfo, -+ NameContext *pNC -+){ -+ int i; -+ assert( pAggInfo!=0 ); -+ assert( pAggInfo->iFirstReg==0 ); -+ pNC->ncFlags |= NC_InAggFunc; -+ for(i=0; inFunc; i++){ -+ Expr *pExpr = pAggInfo->aFunc[i].pFExpr; -+ assert( pExpr->op==TK_FUNCTION || pExpr->op==TK_AGG_FUNCTION ); -+ assert( ExprUseXList(pExpr) ); -+ sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList); -+ if( pExpr->pLeft ){ -+ assert( pExpr->pLeft->op==TK_ORDER ); -+ assert( ExprUseXList(pExpr->pLeft) ); -+ sqlite3ExprAnalyzeAggList(pNC, pExpr->pLeft->x.pList); -+ } -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ assert( !IsWindowFunc(pExpr) ); -+ if( ExprHasProperty(pExpr, EP_WinFunc) ){ -+ sqlite3ExprAnalyzeAggregates(pNC, pExpr->y.pWin->pFilter); -+ } -+#endif -+ } -+ pNC->ncFlags &= ~NC_InAggFunc; -+} -+ -+/* -+** An index on expressions is being used in the inner loop of an -+** aggregate query with a GROUP BY clause. This routine attempts -+** to adjust the AggInfo object to take advantage of index and to -+** perhaps use the index as a covering index. -+** -+*/ -+static void optimizeAggregateUseOfIndexedExpr( -+ Parse *pParse, /* Parsing context */ -+ Select *pSelect, /* The SELECT statement being processed */ -+ AggInfo *pAggInfo, /* The aggregate info */ -+ NameContext *pNC /* Name context used to resolve agg-func args */ -+){ -+ assert( pAggInfo->iFirstReg==0 ); -+ assert( pSelect!=0 ); -+ assert( pSelect->pGroupBy!=0 ); -+ pAggInfo->nColumn = pAggInfo->nAccumulator; -+ if( ALWAYS(pAggInfo->nSortingColumn>0) ){ -+ int mx = pSelect->pGroupBy->nExpr - 1; -+ int j, k; -+ for(j=0; jnColumn; j++){ -+ k = pAggInfo->aCol[j].iSorterColumn; -+ if( k>mx ) mx = k; -+ } -+ pAggInfo->nSortingColumn = mx+1; -+ } -+ analyzeAggFuncArgs(pAggInfo, pNC); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x20 ){ -+ IndexedExpr *pIEpr; -+ TREETRACE(0x20, pParse, pSelect, -+ ("AggInfo (possibly) adjusted for Indexed Exprs\n")); -+ sqlite3TreeViewSelect(0, pSelect, 0); -+ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){ -+ printf("data-cursor=%d index={%d,%d}\n", -+ pIEpr->iDataCur, pIEpr->iIdxCur, pIEpr->iIdxCol); -+ sqlite3TreeViewExpr(0, pIEpr->pExpr, 0); -+ } -+ printAggInfo(pAggInfo); -+ } -+#else -+ UNUSED_PARAMETER(pSelect); -+ UNUSED_PARAMETER(pParse); -+#endif -+} -+ -+/* -+** Walker callback for aggregateConvertIndexedExprRefToColumn(). -+*/ -+static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){ -+ AggInfo *pAggInfo; -+ struct AggInfo_col *pCol; -+ UNUSED_PARAMETER(pWalker); -+ if( pExpr->pAggInfo==0 ) return WRC_Continue; -+ if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue; -+ if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue; -+ if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue; -+ pAggInfo = pExpr->pAggInfo; -+ if( NEVER(pExpr->iAgg>=pAggInfo->nColumn) ) return WRC_Continue; -+ assert( pExpr->iAgg>=0 ); -+ pCol = &pAggInfo->aCol[pExpr->iAgg]; -+ pExpr->op = TK_AGG_COLUMN; -+ pExpr->iTable = pCol->iTable; -+ pExpr->iColumn = pCol->iColumn; -+ ExprClearProperty(pExpr, EP_Skip|EP_Collate|EP_Unlikely); -+ return WRC_Prune; -+} -+ -+/* -+** Convert every pAggInfo->aFunc[].pExpr such that any node within -+** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN -+** opcode. -+*/ -+static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){ -+ int i; -+ Walker w; -+ memset(&w, 0, sizeof(w)); -+ w.xExprCallback = aggregateIdxEprRefToColCallback; -+ for(i=0; inFunc; i++){ -+ sqlite3WalkExpr(&w, pAggInfo->aFunc[i].pFExpr); -+ } -+} -+ -+ -+/* -+** Allocate a block of registers so that there is one register for each -+** pAggInfo->aCol[] and pAggInfo->aFunc[] entry in pAggInfo. The first -+** register in this block is stored in pAggInfo->iFirstReg. -+** -+** This routine may only be called once for each AggInfo object. Prior -+** to calling this routine: -+** -+** * The aCol[] and aFunc[] arrays may be modified -+** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used -+** -+** After calling this routine: -+** -+** * The aCol[] and aFunc[] arrays are fixed -+** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used -+** -+*/ -+static void assignAggregateRegisters(Parse *pParse, AggInfo *pAggInfo){ -+ assert( pAggInfo!=0 ); -+ assert( pAggInfo->iFirstReg==0 ); -+ pAggInfo->iFirstReg = pParse->nMem + 1; -+ pParse->nMem += pAggInfo->nColumn + pAggInfo->nFunc; -+} -+ - /* - ** Reset the aggregate accumulator. - ** -@@ -134696,35 +151591,58 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ - int i; - struct AggInfo_func *pFunc; - int nReg = pAggInfo->nFunc + pAggInfo->nColumn; -+ assert( pAggInfo->iFirstReg>0 ); -+ assert( pParse->db->pParse==pParse ); -+ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); - if( nReg==0 ) return; -- if( pParse->nErr || pParse->db->mallocFailed ) return; --#ifdef SQLITE_DEBUG -- /* Verify that all AggInfo registers are within the range specified by -- ** AggInfo.mnReg..AggInfo.mxReg */ -- assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); -- for(i=0; inColumn; i++){ -- assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg -- && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); -- } -- for(i=0; inFunc; i++){ -- assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg -- && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); -- } --#endif -- sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); -+ if( pParse->nErr ) return; -+ sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->iFirstReg, -+ pAggInfo->iFirstReg+nReg-1); - for(pFunc=pAggInfo->aFunc, i=0; inFunc; i++, pFunc++){ - if( pFunc->iDistinct>=0 ){ - Expr *pE = pFunc->pFExpr; -- assert( !ExprHasProperty(pE, EP_xIsSelect) ); -+ assert( ExprUseXList(pE) ); - if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ - sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " - "argument"); - pFunc->iDistinct = -1; - }else{ - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0); -- sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, -- (char*)pKeyInfo, P4_KEYINFO); -+ pFunc->iDistAddr = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, -+ pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO); -+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(DISTINCT)", -+ pFunc->pFunc->zName)); -+ } -+ } -+ if( pFunc->iOBTab>=0 ){ -+ ExprList *pOBList; -+ KeyInfo *pKeyInfo; -+ int nExtra = 0; -+ assert( pFunc->pFExpr->pLeft!=0 ); -+ assert( pFunc->pFExpr->pLeft->op==TK_ORDER ); -+ assert( ExprUseXList(pFunc->pFExpr->pLeft) ); -+ assert( pFunc->pFunc!=0 ); -+ pOBList = pFunc->pFExpr->pLeft->x.pList; -+ if( !pFunc->bOBUnique ){ -+ nExtra++; /* One extra column for the OP_Sequence */ -+ } -+ if( pFunc->bOBPayload ){ -+ /* extra columns for the function arguments */ -+ assert( ExprUseXList(pFunc->pFExpr) ); -+ nExtra += pFunc->pFExpr->x.pList->nExpr; -+ } -+ if( pFunc->bUseSubtype ){ -+ nExtra += pFunc->pFExpr->x.pList->nExpr; -+ } -+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra); -+ if( !pFunc->bOBUnique && pParse->nErr==0 ){ -+ pKeyInfo->nKeyField++; - } -+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral, -+ pFunc->iOBTab, pOBList->nExpr+nExtra, 0, -+ (char*)pKeyInfo, P4_KEYINFO); -+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)", -+ pFunc->pFunc->zName)); - } - } - } -@@ -134738,24 +151656,82 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ - int i; - struct AggInfo_func *pF; - for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ -- ExprList *pList = pF->pFExpr->x.pList; -- assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); -- sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); -+ ExprList *pList; -+ assert( ExprUseXList(pF->pFExpr) ); -+ if( pParse->nErr ) return; -+ pList = pF->pFExpr->x.pList; -+ if( pF->iOBTab>=0 ){ -+ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs -+ ** were stored in emphermal table pF->iOBTab. Here, we extract those -+ ** inputs (in ORDER BY order) and make all calls to OP_AggStep -+ ** before doing the OP_AggFinal call. */ -+ int iTop; /* Start of loop for extracting columns */ -+ int nArg; /* Number of columns to extract */ -+ int nKey; /* Key columns to be skipped */ -+ int regAgg; /* Extract into this array */ -+ int j; /* Loop counter */ -+ -+ assert( pF->pFunc!=0 ); -+ nArg = pList->nExpr; -+ regAgg = sqlite3GetTempRange(pParse, nArg); -+ -+ if( pF->bOBPayload==0 ){ -+ nKey = 0; -+ }else{ -+ assert( pF->pFExpr->pLeft!=0 ); -+ assert( ExprUseXList(pF->pFExpr->pLeft) ); -+ assert( pF->pFExpr->pLeft->x.pList!=0 ); -+ nKey = pF->pFExpr->pLeft->x.pList->nExpr; -+ if( ALWAYS(!pF->bOBUnique) ) nKey++; -+ } -+ iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v); -+ for(j=nArg-1; j>=0; j--){ -+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j); -+ } -+ if( pF->bUseSubtype ){ -+ int regSubtype = sqlite3GetTempReg(pParse); -+ int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0); -+ for(j=nArg-1; j>=0; j--){ -+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype); -+ sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j); -+ } -+ sqlite3ReleaseTempReg(pParse, regSubtype); -+ } -+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); -+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); -+ sqlite3VdbeChangeP5(v, (u16)nArg); -+ sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); -+ sqlite3VdbeJumpHere(v, iTop); -+ sqlite3ReleaseTempRange(pParse, regAgg, nArg); -+ } -+ sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), -+ pList ? pList->nExpr : 0); - sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); - } - } - -- - /* --** Update the accumulator memory cells for an aggregate based on --** the current cursor position. -+** Generate code that will update the accumulator memory cells for an -+** aggregate based on the current cursor position. - ** - ** If regAcc is non-zero and there are no min() or max() aggregates - ** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator - ** registers if register regAcc contains 0. The caller will take care - ** of setting and clearing regAcc. -+** -+** For an ORDER BY aggregate, the actual accumulator memory cell update -+** is deferred until after all input rows have been received, so that they -+** can be run in the requested order. In that case, instead of invoking -+** OP_AggStep to update the accumulator, just add the arguments that would -+** have been passed into OP_AggStep into the sorting ephemeral table -+** (along with the appropriate sort key). - */ --static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ -+static void updateAccumulator( -+ Parse *pParse, -+ int regAcc, -+ AggInfo *pAggInfo, -+ int eDistinctType -+){ - Vdbe *v = pParse->pVdbe; - int i; - int regHit = 0; -@@ -134763,14 +151739,20 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ - struct AggInfo_func *pF; - struct AggInfo_col *pC; - -+ assert( pAggInfo->iFirstReg>0 ); -+ if( pParse->nErr ) return; - pAggInfo->directMode = 1; - for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ - int nArg; - int addrNext = 0; - int regAgg; -- ExprList *pList = pF->pFExpr->x.pList; -- assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); -+ int regAggSz = 0; -+ int regDistinct = 0; -+ ExprList *pList; -+ assert( ExprUseXList(pF->pFExpr) ); - assert( !IsWindowFunc(pF->pFExpr) ); -+ assert( pF->pFunc!=0 ); -+ pList = pF->pFExpr->x.pList; - if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ - Expr *pFilter = pF->pFExpr->y.pWin->pFilter; - if( pAggInfo->nAccumulator -@@ -134793,43 +151775,100 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ - addrNext = sqlite3VdbeMakeLabel(pParse); - sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL); - } -- if( pList ){ -+ if( pF->iOBTab>=0 ){ -+ /* Instead of invoking AggStep, we must push the arguments that would -+ ** have been passed to AggStep onto the sorting table. */ -+ int jj; /* Registered used so far in building the record */ -+ ExprList *pOBList; /* The ORDER BY clause */ -+ assert( pList!=0 ); -+ nArg = pList->nExpr; -+ assert( nArg>0 ); -+ assert( pF->pFExpr->pLeft!=0 ); -+ assert( pF->pFExpr->pLeft->op==TK_ORDER ); -+ assert( ExprUseXList(pF->pFExpr->pLeft) ); -+ pOBList = pF->pFExpr->pLeft->x.pList; -+ assert( pOBList!=0 ); -+ assert( pOBList->nExpr>0 ); -+ regAggSz = pOBList->nExpr; -+ if( !pF->bOBUnique ){ -+ regAggSz++; /* One register for OP_Sequence */ -+ } -+ if( pF->bOBPayload ){ -+ regAggSz += nArg; -+ } -+ if( pF->bUseSubtype ){ -+ regAggSz += nArg; -+ } -+ regAggSz++; /* One extra register to hold result of MakeRecord */ -+ regAgg = sqlite3GetTempRange(pParse, regAggSz); -+ regDistinct = regAgg; -+ sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP); -+ jj = pOBList->nExpr; -+ if( !pF->bOBUnique ){ -+ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj); -+ jj++; -+ } -+ if( pF->bOBPayload ){ -+ regDistinct = regAgg+jj; -+ sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP); -+ jj += nArg; -+ } -+ if( pF->bUseSubtype ){ -+ int kk; -+ int regBase = pF->bOBPayload ? regDistinct : regAgg; -+ for(kk=0; kknExpr; - regAgg = sqlite3GetTempRange(pParse, nArg); -+ regDistinct = regAgg; - sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP); - }else{ - nArg = 0; - regAgg = 0; - } -- if( pF->iDistinct>=0 ){ -+ if( pF->iDistinct>=0 && pList ){ - if( addrNext==0 ){ - addrNext = sqlite3VdbeMakeLabel(pParse); - } -- testcase( nArg==0 ); /* Error condition */ -- testcase( nArg>1 ); /* Also an error */ -- codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); -+ pF->iDistinct = codeDistinct(pParse, eDistinctType, -+ pF->iDistinct, addrNext, pList, regDistinct); - } -- if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ -- CollSeq *pColl = 0; -- struct ExprList_item *pItem; -- int j; -- assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */ -- for(j=0, pItem=pList->a; !pColl && jpExpr); -- } -- if( !pColl ){ -- pColl = pParse->db->pDfltColl; -+ if( pF->iOBTab>=0 ){ -+ /* Insert a new record into the ORDER BY table */ -+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1, -+ regAgg+regAggSz-1); -+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1, -+ regAgg, regAggSz-1); -+ sqlite3ReleaseTempRange(pParse, regAgg, regAggSz); -+ }else{ -+ /* Invoke the AggStep function */ -+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ -+ CollSeq *pColl = 0; -+ struct ExprList_item *pItem; -+ int j; -+ assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */ -+ for(j=0, pItem=pList->a; !pColl && jpExpr); -+ } -+ if( !pColl ){ -+ pColl = pParse->db->pDfltColl; -+ } -+ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; -+ sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, -+ (char *)pColl, P4_COLLSEQ); - } -- if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; -- sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ); -+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); -+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); -+ sqlite3VdbeChangeP5(v, (u16)nArg); -+ sqlite3ReleaseTempRange(pParse, regAgg, nArg); - } -- sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem); -- sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); -- sqlite3VdbeChangeP5(v, (u8)nArg); -- sqlite3ReleaseTempRange(pParse, regAgg, nArg); - if( addrNext ){ - sqlite3VdbeResolveLabel(v, addrNext); - } -+ if( pParse->nErr ) return; - } - if( regHit==0 && pAggInfo->nAccumulator ){ - regHit = regAcc; -@@ -134838,7 +151877,8 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ - addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v); - } - for(i=0, pC=pAggInfo->aCol; inAccumulator; i++, pC++){ -- sqlite3ExprCode(pParse, pC->pCExpr, pC->iMem); -+ sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i)); -+ if( pParse->nErr ) return; - } - - pAggInfo->directMode = 0; -@@ -134859,7 +151899,7 @@ static void explainSimpleCount( - ){ - if( pParse->explain==2 ){ - int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx))); -- sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s", -+ sqlite3VdbeExplain(pParse, 0, "SCAN %s%s%s", - pTab->zName, - bCover ? " USING COVERING INDEX " : "", - bCover ? pIdx->zName : "" -@@ -134884,7 +151924,17 @@ static void explainSimpleCount( - static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ - if( pExpr->op!=TK_AND ){ - Select *pS = pWalker->u.pSelect; -- if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){ -+ /* This routine is called before the HAVING clause of the current -+ ** SELECT is analyzed for aggregates. So if pExpr->pAggInfo is set -+ ** here, it indicates that the expression is a correlated reference to a -+ ** column from an outer aggregate query, or an aggregate function that -+ ** belongs to an outer query. Do not move the expression to the WHERE -+ ** clause in this obscure case, as doing so may corrupt the outer Select -+ ** statements AggInfo structure. */ -+ if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) -+ && ExprAlwaysFalse(pExpr)==0 -+ && pExpr->pAggInfo==0 -+ ){ - sqlite3 *db = pWalker->pParse->db; - Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1"); - if( pNew ){ -@@ -134922,42 +151972,50 @@ static void havingToWhere(Parse *pParse, Select *p){ - sWalker.xExprCallback = havingToWhereExprCb; - sWalker.u.pSelect = p; - sqlite3WalkExpr(&sWalker, p->pHaving); --#if SELECTTRACE_ENABLED -- if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){ -- SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); -+#if TREETRACE_ENABLED -+ if( sWalker.eCode && (sqlite3TreeTrace & 0x100)!=0 ){ -+ TREETRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif - } - - /* --** Check to see if the pThis entry of pTabList is a self-join of a prior view. --** If it is, then return the SrcList_item for the prior view. If it is not, --** then return 0. -+** Check to see if the pThis entry of pTabList is a self-join of another view. -+** Search FROM-clause entries in the range of iFirst..iEnd, including iFirst -+** but stopping before iEnd. -+** -+** If pThis is a self-join, then return the SrcItem for the first other -+** instance of that view found. If pThis is not a self-join then return 0. - */ --static struct SrcList_item *isSelfJoinView( -+static SrcItem *isSelfJoinView( - SrcList *pTabList, /* Search for self-joins in this FROM clause */ -- struct SrcList_item *pThis /* Search for prior reference to this subquery */ -+ SrcItem *pThis, /* Search for prior reference to this subquery */ -+ int iFirst, int iEnd /* Range of FROM-clause entries to search. */ - ){ -- struct SrcList_item *pItem; -- for(pItem = pTabList->a; pItemfg.isSubquery ); -+ pSel = pThis->u4.pSubq->pSelect; -+ assert( pSel!=0 ); -+ if( pSel->selFlags & SF_PushDown ) return 0; -+ while( iFirstpSelect==0 ) continue; -+ pItem = &pTabList->a[iFirst++]; -+ if( !pItem->fg.isSubquery ) continue; - if( pItem->fg.viaCoroutine ) continue; - if( pItem->zName==0 ) continue; -- assert( pItem->pTab!=0 ); -- assert( pThis->pTab!=0 ); -- if( pItem->pTab->pSchema!=pThis->pTab->pSchema ) continue; -+ assert( pItem->pSTab!=0 ); -+ assert( pThis->pSTab!=0 ); -+ if( pItem->pSTab->pSchema!=pThis->pSTab->pSchema ) continue; - if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; -- pS1 = pItem->pSelect; -- if( pItem->pTab->pSchema==0 && pThis->pSelect->selId!=pS1->selId ){ -+ pS1 = pItem->u4.pSubq->pSelect; -+ if( pItem->pSTab->pSchema==0 && pSel->selId!=pS1->selId ){ - /* The query flattener left two different CTE tables with identical - ** names in the same FROM clause. */ - continue; - } -- if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1) -- || sqlite3ExprCompare(0, pThis->pSelect->pHaving, pS1->pHaving, -1) -- ){ -+ if( pS1->selFlags & SF_PushDown ){ - /* The view was modified by some other optimization such as - ** pushDownWhereTerms() */ - continue; -@@ -134967,7 +152025,16 @@ static struct SrcList_item *isSelfJoinView( - return 0; - } - --#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION -+/* -+** Deallocate a single AggInfo object -+*/ -+static void agginfoFree(sqlite3 *db, void *pArg){ -+ AggInfo *p = (AggInfo*)pArg; -+ sqlite3DbFree(db, p->aCol); -+ sqlite3DbFree(db, p->aFunc); -+ sqlite3DbFreeNN(db, p); -+} -+ - /* - ** Attempt to transform a query of the form - ** -@@ -134984,6 +152051,7 @@ static struct SrcList_item *isSelfJoinView( - ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries - ** * The outer query is a simple count(*) with no WHERE clause or other - ** extraneous syntax. -+** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10) - ** - ** Return TRUE if the optimization is undertaken. - */ -@@ -134992,23 +152060,36 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ - Expr *pExpr; - Expr *pCount; - sqlite3 *db; -+ SrcItem *pFrom; - if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */ - if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ - if( p->pWhere ) return 0; -+ if( p->pHaving ) return 0; - if( p->pGroupBy ) return 0; -+ if( p->pOrderBy ) return 0; - pExpr = p->pEList->a[0].pExpr; - if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */ -+ assert( ExprUseUToken(pExpr) ); - if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */ -+ assert( ExprUseXList(pExpr) ); - if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */ - if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */ -- pSub = p->pSrc->a[0].pSelect; -- if( pSub==0 ) return 0; /* The FROM is a subquery */ -- if( pSub->pPrior==0 ) return 0; /* Must be a compound ry */ -+ if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */ -+ pFrom = p->pSrc->a; -+ if( pFrom->fg.isSubquery==0 ) return 0; /* FROM is a subquery */ -+ pSub = pFrom->u4.pSubq->pSelect; -+ if( pSub->pPrior==0 ) return 0; /* Must be a compound */ -+ if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ - do{ - if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ - if( pSub->pWhere ) return 0; /* No WHERE clause */ - if( pSub->pLimit ) return 0; /* No LIMIT clause */ -- if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ -+ if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){ -+ testcase( pSub->selFlags & SF_Aggregate ); -+ testcase( pSub->selFlags & SF_Distinct ); -+ return 0; /* Not an aggregate nor DISTINCT */ -+ } -+ assert( pSub->pHaving==0 ); /* Due to the previous */ - pSub = pSub->pPrior; /* Repeat over compound */ - }while( pSub ); - -@@ -135017,19 +152098,18 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ - db = pParse->db; - pCount = pExpr; - pExpr = 0; -- pSub = p->pSrc->a[0].pSelect; -- p->pSrc->a[0].pSelect = 0; -+ pSub = sqlite3SubqueryDetach(db, pFrom); - sqlite3SrcListDelete(db, p->pSrc); -- p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); -+ p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); - while( pSub ){ - Expr *pTerm; - pPrior = pSub->pPrior; - pSub->pPrior = 0; - pSub->pNext = 0; - pSub->selFlags |= SF_Aggregate; -- pSub->selFlags &= ~SF_Compound; -+ pSub->selFlags &= ~(u32)SF_Compound; - pSub->nSelectRow = 0; -- sqlite3ExprListDelete(db, pSub->pEList); -+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList); - pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount; - pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm); - pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0); -@@ -135042,20 +152122,104 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ - pSub = pPrior; - } - p->pEList->a[0].pExpr = pExpr; -- p->selFlags &= ~SF_Aggregate; -+ p->selFlags &= ~(u32)SF_Aggregate; - --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x400 ){ -- SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x200 ){ -+ TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif - return 1; - } --#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ - - /* --** Generate code for the SELECT statement given in the p argument. -+** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same -+** as pSrcItem but has the same alias as p0, then return true. -+** Otherwise return false. -+*/ -+static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ -+ int i; -+ for(i=0; inSrc; i++){ -+ SrcItem *p1 = &pSrc->a[i]; -+ if( p1==p0 ) continue; -+ if( p0->pSTab==p1->pSTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ -+ return 1; -+ } -+ if( p1->fg.isSubquery -+ && (p1->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 -+ && sameSrcAlias(p0, p1->u4.pSubq->pSelect->pSrc) -+ ){ -+ return 1; -+ } -+ } -+ return 0; -+} -+ -+/* -+** Return TRUE (non-zero) if the i-th entry in the pTabList SrcList can -+** be implemented as a co-routine. The i-th entry is guaranteed to be -+** a subquery. -+** -+** The subquery is implemented as a co-routine if all of the following are -+** true: -+** -+** (1) The subquery will likely be implemented in the outer loop of -+** the query. This will be the case if any one of the following -+** conditions hold: -+** (a) The subquery is the only term in the FROM clause -+** (b) The subquery is the left-most term and a CROSS JOIN or similar -+** requires it to be the outer loop -+** (c) All of the following are true: -+** (i) The subquery is the left-most subquery in the FROM clause -+** (ii) There is nothing that would prevent the subquery from -+** being used as the outer loop if the sqlite3WhereBegin() -+** routine nominates it to that position. -+** (iii) The query is not a UPDATE ... FROM -+** (2) The subquery is not a CTE that should be materialized because -+** (a) the AS MATERIALIZED keyword is used, or -+** (b) the CTE is used multiple times and does not have the -+** NOT MATERIALIZED keyword -+** (3) The subquery is not part of a left operand for a RIGHT JOIN -+** (4) The SQLITE_Coroutine optimization disable flag is not set -+** (5) The subquery is not self-joined -+*/ -+static int fromClauseTermCanBeCoroutine( -+ Parse *pParse, /* Parsing context */ -+ SrcList *pTabList, /* FROM clause */ -+ int i, /* Which term of the FROM clause holds the subquery */ -+ int selFlags /* Flags on the SELECT statement */ -+){ -+ SrcItem *pItem = &pTabList->a[i]; -+ if( pItem->fg.isCte ){ -+ const CteUse *pCteUse = pItem->u2.pCteUse; -+ if( pCteUse->eM10d==M10d_Yes ) return 0; /* (2a) */ -+ if( pCteUse->nUse>=2 && pCteUse->eM10d!=M10d_No ) return 0; /* (2b) */ -+ } -+ if( pTabList->a[0].fg.jointype & JT_LTORJ ) return 0; /* (3) */ -+ if( OptimizationDisabled(pParse->db, SQLITE_Coroutines) ) return 0; /* (4) */ -+ if( isSelfJoinView(pTabList, pItem, i+1, pTabList->nSrc)!=0 ){ -+ return 0; /* (5) */ -+ } -+ if( i==0 ){ -+ if( pTabList->nSrc==1 ) return 1; /* (1a) */ -+ if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */ -+ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ -+ return 1; -+ } -+ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ -+ while( 1 /*exit-by-break*/ ){ -+ if( pItem->fg.jointype & (JT_OUTER|JT_CROSS) ) return 0; /* (1c-ii) */ -+ if( i==0 ) break; -+ i--; -+ pItem--; -+ if( pItem->fg.isSubquery ) return 0; /* (1c-i) */ -+ } -+ return 1; -+} -+ -+/* -+** Generate byte-code for the SELECT statement given in the p argument. - ** - ** The results are returned according to the SelectDest structure. - ** See comments in sqliteInt.h for further information. -@@ -135066,6 +152230,40 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ - ** - ** This routine does NOT free the Select structure passed in. The - ** calling function needs to do that. -+** -+** This is a long function. The following is an outline of the processing -+** steps, with tags referencing various milestones: -+** -+** * Resolve names and similar preparation tag-select-0100 -+** * Scan of the FROM clause tag-select-0200 -+** + OUTER JOIN strength reduction tag-select-0220 -+** + Sub-query ORDER BY removal tag-select-0230 -+** + Query flattening tag-select-0240 -+** * Separate subroutine for compound-SELECT tag-select-0300 -+** * WHERE-clause constant propagation tag-select-0330 -+** * Count()-of-VIEW optimization tag-select-0350 -+** * Scan of the FROM clause again tag-select-0400 -+** + Authorize unreferenced tables tag-select-0410 -+** + Predicate push-down optimization tag-select-0420 -+** + Omit unused subquery columns optimization tag-select-0440 -+** + Generate code to implement subqueries tag-select-0480 -+** - Co-routines tag-select-0482 -+** - Reuse previously computed CTE tag-select-0484 -+** - REuse previously computed VIEW tag-select-0486 -+** - Materialize a VIEW or CTE tag-select-0488 -+** * DISTINCT ORDER BY -> GROUP BY optimization tag-select-0500 -+** * Set up for ORDER BY tag-select-0600 -+** * Create output table tag-select-0630 -+** * Prepare registers for LIMIT tag-select-0650 -+** * Setup for DISTINCT tag-select-0680 -+** * Generate code for non-aggregate and non-GROUP BY tag-select-0700 -+** * Generate code for aggregate and/or GROUP BY tag-select-0800 -+** + GROUP BY queries tag-select-0810 -+** + non-GROUP BY queries tag-select-0820 -+** - Special case of count() w/o GROUP BY tag-select-0821 -+** - General case of non-GROUP BY aggregates tag-select-0822 -+** * Sort results, as needed tag-select-0900 -+** * Internal self-checks tag-select-1000 - */ - SQLITE_PRIVATE int sqlite3Select( - Parse *pParse, /* The parser context */ -@@ -135091,77 +152289,100 @@ SQLITE_PRIVATE int sqlite3Select( - u8 minMaxFlag; /* Flag for min/max queries */ - - db = pParse->db; -+ assert( pParse==db->pParse ); - v = sqlite3GetVdbe(pParse); -- if( p==0 || db->mallocFailed || pParse->nErr ){ -+ if( p==0 || pParse->nErr ){ - return 1; - } -+ assert( db->mallocFailed==0 ); - if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; --#if SELECTTRACE_ENABLED -- SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); -- if( sqlite3_unsupported_selecttrace & 0x100 ){ -- sqlite3TreeViewSelect(0, p, 0); -+#if TREETRACE_ENABLED -+ TREETRACE(0x1,pParse,p, ("begin processing:\n", pParse->addrExplain)); -+ if( sqlite3TreeTrace & 0x10000 ){ -+ if( (sqlite3TreeTrace & 0x10001)==0x10000 ){ -+ sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d", -+ __FILE__, __LINE__); -+ } -+ sqlite3ShowSelect(p); - } - #endif - -+ /* tag-select-0100 */ - assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); - assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); - assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); - assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue ); -- if( IgnorableOrderby(pDest) ){ -- assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || -- pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || -- pDest->eDest==SRT_Queue || pDest->eDest==SRT_DistFifo || -- pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_Fifo); -- /* If ORDER BY makes no difference in the output then neither does -- ** DISTINCT so it can be removed too. */ -- sqlite3ExprListDelete(db, p->pOrderBy); -- p->pOrderBy = 0; -- p->selFlags &= ~SF_Distinct; -+ if( IgnorableDistinct(pDest) ){ -+ assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || -+ pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || -+ pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); -+ /* All of these destinations are also able to ignore the ORDER BY clause */ -+ if( p->pOrderBy ){ -+#if TREETRACE_ENABLED -+ TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n")); -+ if( sqlite3TreeTrace & 0x800 ){ -+ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); -+ } -+#endif -+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, -+ p->pOrderBy); -+ testcase( pParse->earlyCleanup ); -+ p->pOrderBy = 0; -+ } -+ p->selFlags &= ~(u32)SF_Distinct; - p->selFlags |= SF_NoopOrderBy; - } - sqlite3SelectPrep(pParse, p, 0); -- if( pParse->nErr || db->mallocFailed ){ -+ if( pParse->nErr ){ - goto select_end; - } -+ assert( db->mallocFailed==0 ); - assert( p->pEList!=0 ); --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x104 ){ -- SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x10 ){ -+ TREETRACE(0x10,pParse,p, ("after name resolution:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif - -- /* If the SF_UpdateFrom flag is set, then this function is being called -+ /* If the SF_UFSrcCheck flag is set, then this function is being called - ** as part of populating the temp table for an UPDATE...FROM statement. - ** In this case, it is an error if the target object (pSrc->a[0]) name -- ** or alias is duplicated within FROM clause (pSrc->a[1..n]). */ -- if( p->selFlags & SF_UpdateFrom ){ -- struct SrcList_item *p0 = &p->pSrc->a[0]; -- for(i=1; ipSrc->nSrc; i++){ -- struct SrcList_item *p1 = &p->pSrc->a[i]; -- if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ -- sqlite3ErrorMsg(pParse, -- "target object/alias may not appear in FROM clause: %s", -- p0->zAlias ? p0->zAlias : p0->pTab->zName -- ); -- goto select_end; -- } -+ ** or alias is duplicated within FROM clause (pSrc->a[1..n]). -+ ** -+ ** Postgres disallows this case too. The reason is that some other -+ ** systems handle this case differently, and not all the same way, -+ ** which is just confusing. To avoid this, we follow PG's lead and -+ ** disallow it altogether. */ -+ if( p->selFlags & SF_UFSrcCheck ){ -+ SrcItem *p0 = &p->pSrc->a[0]; -+ if( sameSrcAlias(p0, p->pSrc) ){ -+ sqlite3ErrorMsg(pParse, -+ "target object/alias may not appear in FROM clause: %s", -+ p0->zAlias ? p0->zAlias : p0->pSTab->zName -+ ); -+ goto select_end; - } -+ -+ /* Clear the SF_UFSrcCheck flag. The check has already been performed, -+ ** and leaving this flag set can cause errors if a compound sub-query -+ ** in p->pSrc is flattened into this query and this function called -+ ** again as part of compound SELECT processing. */ -+ p->selFlags &= ~(u32)SF_UFSrcCheck; - } - - if( pDest->eDest==SRT_Output ){ -- generateColumnNames(pParse, p); -+ sqlite3GenerateColumnNames(pParse, p); - } - - #ifndef SQLITE_OMIT_WINDOWFUNC -- rc = sqlite3WindowRewrite(pParse, p); -- if( rc ){ -- assert( db->mallocFailed || pParse->nErr>0 ); -+ if( sqlite3WindowRewrite(pParse, p) ){ -+ assert( pParse->nErr ); - goto select_end; - } --#if SELECTTRACE_ENABLED -- if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){ -- SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); -+#if TREETRACE_ENABLED -+ if( p->pWin && (sqlite3TreeTrace & 0x40)!=0 ){ -+ TREETRACE(0x40,pParse,p, ("after window rewrite:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif -@@ -135173,32 +152394,72 @@ SQLITE_PRIVATE int sqlite3Select( - - /* Try to do various optimizations (flattening subqueries, and strength - ** reduction of join operators) in the FROM clause up into the main query -+ ** tag-select-0200 - */ - #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) - for(i=0; !p->pPrior && inSrc; i++){ -- struct SrcList_item *pItem = &pTabList->a[i]; -- Select *pSub = pItem->pSelect; -- Table *pTab = pItem->pTab; -+ SrcItem *pItem = &pTabList->a[i]; -+ Select *pSub = pItem->fg.isSubquery ? pItem->u4.pSubq->pSelect : 0; -+ Table *pTab = pItem->pSTab; - - /* The expander should have already created transient Table objects - ** even for FROM clause elements such as subqueries that do not correspond - ** to a real table */ - assert( pTab!=0 ); - -- /* Convert LEFT JOIN into JOIN if there are terms of the right table -- ** of the LEFT JOIN used in the WHERE clause. -+ /* Try to simplify joins: -+ ** -+ ** LEFT JOIN -> JOIN -+ ** RIGHT JOIN -> JOIN -+ ** FULL JOIN -> RIGHT JOIN -+ ** -+ ** If terms of the i-th table are used in the WHERE clause in such a -+ ** way that the i-th table cannot be the NULL row of a join, then -+ ** perform the appropriate simplification. This is called -+ ** "OUTER JOIN strength reduction" in the SQLite documentation. -+ ** tag-select-0220 - */ -- if( (pItem->fg.jointype & JT_LEFT)!=0 -- && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor) -+ if( (pItem->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 -+ && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor, -+ pItem->fg.jointype & JT_LTORJ) - && OptimizationEnabled(db, SQLITE_SimplifyJoin) - ){ -- SELECTTRACE(0x100,pParse,p, -- ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); -- pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); -- unsetJoinExpr(p->pWhere, pItem->iCursor); -+ if( pItem->fg.jointype & JT_LEFT ){ -+ if( pItem->fg.jointype & JT_RIGHT ){ -+ TREETRACE(0x1000,pParse,p, -+ ("FULL-JOIN simplifies to RIGHT-JOIN on term %d\n",i)); -+ pItem->fg.jointype &= ~JT_LEFT; -+ }else{ -+ TREETRACE(0x1000,pParse,p, -+ ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); -+ pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); -+ unsetJoinExpr(p->pWhere, pItem->iCursor, 0); -+ } -+ } -+ if( pItem->fg.jointype & JT_LTORJ ){ -+ for(j=i+1; jnSrc; j++){ -+ SrcItem *pI2 = &pTabList->a[j]; -+ if( pI2->fg.jointype & JT_RIGHT ){ -+ if( pI2->fg.jointype & JT_LEFT ){ -+ TREETRACE(0x1000,pParse,p, -+ ("FULL-JOIN simplifies to LEFT-JOIN on term %d\n",j)); -+ pI2->fg.jointype &= ~JT_RIGHT; -+ }else{ -+ TREETRACE(0x1000,pParse,p, -+ ("RIGHT-JOIN simplifies to JOIN on term %d\n",j)); -+ pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER); -+ unsetJoinExpr(p->pWhere, pI2->iCursor, 1); -+ } -+ } -+ } -+ for(j=pTabList->nSrc-1; j>=0; j--){ -+ pTabList->a[j].fg.jointype &= ~JT_LTORJ; -+ if( pTabList->a[j].fg.jointype & JT_RIGHT ) break; -+ } -+ } - } - -- /* No futher action if this term of the FROM clause is no a subquery */ -+ /* No further action if this term of the FROM clause is not a subquery */ - if( pSub==0 ) continue; - - /* Catch mismatch in the declared columns of a view and the number of -@@ -135209,6 +152470,14 @@ SQLITE_PRIVATE int sqlite3Select( - goto select_end; - } - -+ /* Do not attempt the usual optimizations (flattening and ORDER BY -+ ** elimination) on a MATERIALIZED common table expression because -+ ** a MATERIALIZED common table expression is an optimization fence. -+ */ -+ if( pItem->fg.isCte && pItem->u2.pCteUse->eM10d==M10d_Yes ){ -+ continue; -+ } -+ - /* Do not try to flatten an aggregate subquery. - ** - ** Flattening an aggregate subquery is only possible if the outer query -@@ -135219,6 +152488,46 @@ SQLITE_PRIVATE int sqlite3Select( - if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; - assert( pSub->pGroupBy==0 ); - -+ /* tag-select-0230: -+ ** If a FROM-clause subquery has an ORDER BY clause that is not -+ ** really doing anything, then delete it now so that it does not -+ ** interfere with query flattening. See the discussion at -+ ** https://sqlite.org/forum/forumpost/2d76f2bcf65d256a -+ ** -+ ** Beware of these cases where the ORDER BY clause may not be safely -+ ** omitted: -+ ** -+ ** (1) There is also a LIMIT clause -+ ** (2) The subquery was added to help with window-function -+ ** processing -+ ** (3) The subquery is in the FROM clause of an UPDATE -+ ** (4) The outer query uses an aggregate function other than -+ ** the built-in count(), min(), or max(). -+ ** (5) The ORDER BY isn't going to accomplish anything because -+ ** one of: -+ ** (a) The outer query has a different ORDER BY clause -+ ** (b) The subquery is part of a join -+ ** See forum post 062d576715d277c8 -+ ** (6) The subquery is not a recursive CTE. ORDER BY has a different -+ ** meaning for recursive CTEs and this optimization does not -+ ** apply. -+ ** -+ ** Also retain the ORDER BY if the OmitOrderBy optimization is disabled. -+ */ -+ if( pSub->pOrderBy!=0 -+ && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */ -+ && pSub->pLimit==0 /* Condition (1) */ -+ && (pSub->selFlags & (SF_OrderByReqd|SF_Recursive))==0 /* (2) and (6) */ -+ && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ -+ && OptimizationEnabled(db, SQLITE_OmitOrderBy) -+ ){ -+ TREETRACE(0x800,pParse,p, -+ ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); -+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, -+ pSub->pOrderBy); -+ pSub->pOrderBy = 0; -+ } -+ - /* If the outer query contains a "complex" result set (that is, - ** if the result set of the outer query uses functions or subqueries) - ** and if the subquery contains an ORDER BY clause and if -@@ -135241,11 +152550,12 @@ SQLITE_PRIVATE int sqlite3Select( - && i==0 - && (p->selFlags & SF_ComplexResult)!=0 - && (pTabList->nSrc==1 -- || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) -+ || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) - ){ - continue; - } - -+ /* tag-select-0240 */ - if( flattenSubquery(pParse, p, i, isAgg) ){ - if( pParse->nErr ) goto select_end; - /* This subquery can be absorbed into its parent. */ -@@ -135261,13 +152571,13 @@ SQLITE_PRIVATE int sqlite3Select( - - #ifndef SQLITE_OMIT_COMPOUND_SELECT - /* Handle compound SELECT statements using the separate multiSelect() -- ** procedure. -+ ** procedure. tag-select-0300 - */ - if( p->pPrior ){ - rc = multiSelect(pParse, p, pDest); --#if SELECTTRACE_ENABLED -- SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); -- if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ -+#if TREETRACE_ENABLED -+ TREETRACE(0x400,pParse,p,("end compound-select processing\n")); -+ if( (sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){ - sqlite3TreeViewSelect(0, p, 0); - } - #endif -@@ -135277,47 +152587,53 @@ SQLITE_PRIVATE int sqlite3Select( - #endif - - /* Do the WHERE-clause constant propagation optimization if this is -- ** a join. No need to speed time on this operation for non-join queries -+ ** a join. No need to spend time on this operation for non-join queries - ** as the equivalent optimization will be handled by query planner in -- ** sqlite3WhereBegin(). -+ ** sqlite3WhereBegin(). tag-select-0330 - */ -- if( pTabList->nSrc>1 -+ if( p->pWhere!=0 -+ && p->pWhere->op==TK_AND - && OptimizationEnabled(db, SQLITE_PropagateConst) - && propagateConstants(pParse, p) - ){ --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x100 ){ -- SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x2000 ){ -+ TREETRACE(0x2000,pParse,p,("After constant propagation:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif - }else{ -- SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n")); -+ TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n")); - } - --#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION -+ /* tag-select-0350 */ - if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView) - && countOfViewOptimization(pParse, p) - ){ - if( db->mallocFailed ) goto select_end; -- pEList = p->pEList; - pTabList = p->pSrc; - } --#endif - -- /* For each term in the FROM clause, do two things: -- ** (1) Authorized unreferenced tables -- ** (2) Generate code for all sub-queries -+ /* Loop over all terms in the FROM clause and do two things for each term: -+ ** -+ ** (1) Authorize unreferenced tables -+ ** (2) Generate code for all sub-queries -+ ** -+ ** tag-select-0400 - */ - for(i=0; inSrc; i++){ -- struct SrcList_item *pItem = &pTabList->a[i]; -+ SrcItem *pItem = &pTabList->a[i]; -+ SrcItem *pPrior; - SelectDest dest; -+ Subquery *pSubq; - Select *pSub; - #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) - const char *zSavedAuthContext; - #endif - -- /* Issue SQLITE_READ authorizations with a fake column name for any -+ /* Authorized unreferenced tables. tag-select-0410 -+ ** -+ ** Issue SQLITE_READ authorizations with a fake column name for any - ** tables that are referenced but from which no values are extracted. - ** Examples of where these kinds of null SQLITE_READ authorizations - ** would occur: -@@ -135334,28 +152650,28 @@ SQLITE_PRIVATE int sqlite3Select( - ** string for the fake column name seems safer. - */ - if( pItem->colUsed==0 && pItem->zName!=0 ){ -- sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase); -+ const char *zDb; -+ if( pItem->fg.fixedSchema ){ -+ int iDb = sqlite3SchemaToIndex(pParse->db, pItem->u4.pSchema); -+ zDb = db->aDb[iDb].zDbSName; -+ }else if( pItem->fg.isSubquery ){ -+ zDb = 0; -+ }else{ -+ zDb = pItem->u4.zDatabase; -+ } -+ sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", zDb); - } - - #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) - /* Generate code for all sub-queries in the FROM clause - */ -- pSub = pItem->pSelect; -- if( pSub==0 ) continue; -+ if( pItem->fg.isSubquery==0 ) continue; -+ pSubq = pItem->u4.pSubq; -+ assert( pSubq!=0 ); -+ pSub = pSubq->pSelect; - -- /* The code for a subquery should only be generated once, though it is -- ** technically harmless for it to be generated multiple times. The -- ** following assert() will detect if something changes to cause -- ** the same subquery to be coded multiple times, as a signal to the -- ** developers to try to optimize the situation. -- ** -- ** Update 2019-07-24: -- ** See ticket https://sqlite.org/src/tktview/c52b09c7f38903b1311cec40. -- ** The dbsqlfuzz fuzzer found a case where the same subquery gets -- ** coded twice. So this assert() now becomes a testcase(). It should -- ** be very rare, though. -- */ -- testcase( pItem->addrFillSub!=0 ); -+ /* The code for a subquery should only be generated once. */ -+ if( pSubq->addrFillSub!=0 ) continue; - - /* Increment Parse.nHeight by the height of the largest expression - ** tree referred to by this, the parent select. The child select -@@ -135368,96 +152684,133 @@ SQLITE_PRIVATE int sqlite3Select( - - /* Make copies of constant WHERE-clause terms in the outer query down - ** inside the subquery. This can help the subquery to run more efficiently. -+ ** This is the "predicate push-down optimization". tag-select-0420 - */ - if( OptimizationEnabled(db, SQLITE_PushDown) -- && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, -- (pItem->fg.jointype & JT_OUTER)!=0) -+ && (pItem->fg.isCte==0 -+ || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) -+ && pushDownWhereTerms(pParse, pSub, p->pWhere, pTabList, i) - ){ --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x100 ){ -- SELECTTRACE(0x100,pParse,p, -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x4000 ){ -+ TREETRACE(0x4000,pParse,p, - ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); - sqlite3TreeViewSelect(0, p, 0); - } - #endif -+ assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); - }else{ -- SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); -+ TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n")); -+ } -+ -+ /* Convert unused result columns of the subquery into simple NULL -+ ** expressions, to avoid unneeded searching and computation. -+ ** tag-select-0440 -+ */ -+ if( OptimizationEnabled(db, SQLITE_NullUnusedCols) -+ && disableUnusedSubqueryResultColumns(pItem) -+ ){ -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x4000 ){ -+ TREETRACE(0x4000,pParse,p, -+ ("Change unused result columns to NULL for subquery %d:\n", -+ pSub->selId)); -+ sqlite3TreeViewSelect(0, p, 0); -+ } -+#endif - } - - zSavedAuthContext = pParse->zAuthContext; - pParse->zAuthContext = pItem->zName; - -- /* Generate code to implement the subquery -- ** -- ** The subquery is implemented as a co-routine if the subquery is -- ** guaranteed to be the outer loop (so that it does not need to be -- ** computed more than once) -- ** -- ** TODO: Are there other reasons beside (1) to use a co-routine -- ** implementation? -+ /* Generate byte-code to implement the subquery tag-select-0480 - */ -- if( i==0 -- && (pTabList->nSrc==1 -- || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */ -- ){ -+ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){ - /* Implement a co-routine that will return a single row of the result -- ** set on each invocation. -+ ** set on each invocation. tag-select-0482 - */ - int addrTop = sqlite3VdbeCurrentAddr(v)+1; - -- pItem->regReturn = ++pParse->nMem; -- sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); -- VdbeComment((v, "%s", pItem->pTab->zName)); -- pItem->addrFillSub = addrTop; -- sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); -- ExplainQueryPlan((pParse, 1, "CO-ROUTINE %u", pSub->selId)); -+ pSubq->regReturn = ++pParse->nMem; -+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, addrTop); -+ VdbeComment((v, "%!S", pItem)); -+ pSubq->addrFillSub = addrTop; -+ sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); -+ ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); - sqlite3Select(pParse, pSub, &dest); -- pItem->pTab->nRowLogEst = pSub->nSelectRow; -+ pItem->pSTab->nRowLogEst = pSub->nSelectRow; - pItem->fg.viaCoroutine = 1; -- pItem->regResult = dest.iSdst; -- sqlite3VdbeEndCoroutine(v, pItem->regReturn); -+ pSubq->regResult = dest.iSdst; -+ sqlite3VdbeEndCoroutine(v, pSubq->regReturn); -+ VdbeComment((v, "end %!S", pItem)); - sqlite3VdbeJumpHere(v, addrTop-1); - sqlite3ClearTempRegCache(pParse); -+ }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){ -+ /* This is a CTE for which materialization code has already been -+ ** generated. Invoke the subroutine to compute the materialization, -+ ** then make the pItem->iCursor be a copy of the ephemeral table that -+ ** holds the result of the materialization. tag-select-0484 */ -+ CteUse *pCteUse = pItem->u2.pCteUse; -+ sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); -+ if( pItem->iCursor!=pCteUse->iCur ){ -+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur); -+ VdbeComment((v, "%!S", pItem)); -+ } -+ pSub->nSelectRow = pCteUse->nRowEst; -+ }else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){ -+ /* This view has already been materialized by a prior entry in -+ ** this same FROM clause. Reuse it. tag-select-0486 */ -+ Subquery *pPriorSubq; -+ assert( pPrior->fg.isSubquery ); -+ pPriorSubq = pPrior->u4.pSubq; -+ assert( pPriorSubq!=0 ); -+ if( pPriorSubq->addrFillSub ){ -+ sqlite3VdbeAddOp2(v, OP_Gosub, pPriorSubq->regReturn, -+ pPriorSubq->addrFillSub); -+ } -+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); -+ pSub->nSelectRow = pPriorSubq->pSelect->nSelectRow; - }else{ -- /* Generate a subroutine that will fill an ephemeral table with -- ** the content of this subquery. pItem->addrFillSub will point -- ** to the address of the generated subroutine. pItem->regReturn -- ** is a register allocated to hold the subroutine return address -- */ -+ /* Materialize the view. If the view is not correlated, generate a -+ ** subroutine to do the materialization so that subsequent uses of -+ ** the same view can reuse the materialization. tag-select-0488 */ - int topAddr; - int onceAddr = 0; -- int retAddr; -- struct SrcList_item *pPrior; -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ int addrExplain; -+#endif - -- testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ -- pItem->regReturn = ++pParse->nMem; -- topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); -- pItem->addrFillSub = topAddr+1; -+ pSubq->regReturn = ++pParse->nMem; -+ topAddr = sqlite3VdbeAddOp0(v, OP_Goto); -+ pSubq->addrFillSub = topAddr+1; -+ pItem->fg.isMaterialized = 1; - if( pItem->fg.isCorrelated==0 ){ - /* If the subquery is not correlated and if we are not inside of - ** a trigger, then we only need to compute the value of the subquery - ** once. */ - onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); -- VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); -+ VdbeComment((v, "materialize %!S", pItem)); - }else{ -- VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); -+ VdbeNoopComment((v, "materialize %!S", pItem)); - } -- pPrior = isSelfJoinView(pTabList, pItem); -- if( pPrior ){ -- sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); -- assert( pPrior->pSelect!=0 ); -- pSub->nSelectRow = pPrior->pSelect->nSelectRow; -- }else{ -- sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); -- ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId)); -- sqlite3Select(pParse, pSub, &dest); -- } -- pItem->pTab->nRowLogEst = pSub->nSelectRow; -+ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); -+ -+ ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); -+ sqlite3Select(pParse, pSub, &dest); -+ pItem->pSTab->nRowLogEst = pSub->nSelectRow; - if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); -- retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); -- VdbeComment((v, "end %s", pItem->pTab->zName)); -- sqlite3VdbeChangeP1(v, topAddr, retAddr); -+ sqlite3VdbeAddOp2(v, OP_Return, pSubq->regReturn, topAddr+1); -+ VdbeComment((v, "end %!S", pItem)); -+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); -+ sqlite3VdbeJumpHere(v, topAddr); - sqlite3ClearTempRegCache(pParse); -+ if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ -+ CteUse *pCteUse = pItem->u2.pCteUse; -+ pCteUse->addrM9e = pSubq->addrFillSub; -+ pCteUse->regRtn = pSubq->regReturn; -+ pCteUse->iCur = pItem->iCursor; -+ pCteUse->nRowEst = pSub->nSelectRow; -+ } - } - if( db->mallocFailed ) goto select_end; - pParse->nHeight -= sqlite3SelectExprHeight(p); -@@ -135473,14 +152826,16 @@ SQLITE_PRIVATE int sqlite3Select( - pHaving = p->pHaving; - sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; - --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x400 ){ -- SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x8000 ){ -+ TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif - -- /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and -+ /* tag-select-0500 -+ ** -+ ** If the query is DISTINCT with an ORDER BY but is not an aggregate, and - ** if the select-list is the same as the ORDER BY list, then this query - ** can be rewritten as a GROUP BY. In other words, this: - ** -@@ -135497,21 +152852,28 @@ SQLITE_PRIVATE int sqlite3Select( - */ - if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct - && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 -+ && OptimizationEnabled(db, SQLITE_GroupByOrder) - #ifndef SQLITE_OMIT_WINDOWFUNC - && p->pWin==0 - #endif - ){ -- p->selFlags &= ~SF_Distinct; -+ p->selFlags &= ~(u32)SF_Distinct; - pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); -+ if( pGroupBy ){ -+ for(i=0; inExpr; i++){ -+ pGroupBy->a[i].u.x.iOrderByCol = i+1; -+ } -+ } - p->selFlags |= SF_Aggregate; - /* Notice that even thought SF_Distinct has been cleared from p->selFlags, - ** the sDistinct.isTnct is still set. Hence, isTnct represents the - ** original setting of the SF_Distinct flag, not the current setting */ - assert( sDistinct.isTnct ); -+ sDistinct.isTnct = 2; - --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x400 ){ -- SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x20000 ){ -+ TREETRACE(0x20000,pParse,p,("Transform DISTINCT into GROUP BY:\n")); - sqlite3TreeViewSelect(0, p, 0); - } - #endif -@@ -135523,7 +152885,7 @@ SQLITE_PRIVATE int sqlite3Select( - ** If that is the case, then the OP_OpenEphemeral instruction will be - ** changed to an OP_Noop once we figure out that the sorting index is - ** not needed. The sSort.addrSortIndex variable is used to facilitate -- ** that change. -+ ** that change. tag-select-0600 - */ - if( sSort.pOrderBy ){ - KeyInfo *pKeyInfo; -@@ -135540,24 +152902,37 @@ SQLITE_PRIVATE int sqlite3Select( - } - - /* If the output is destined for a temporary table, open that table. -+ ** tag-select-0630 - */ - if( pDest->eDest==SRT_EphemTab ){ - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); -+ if( p->selFlags & SF_NestedFrom ){ -+ /* Delete or NULL-out result columns that will never be used */ -+ int ii; -+ for(ii=pEList->nExpr-1; ii>0 && pEList->a[ii].fg.bUsed==0; ii--){ -+ sqlite3ExprDelete(db, pEList->a[ii].pExpr); -+ sqlite3DbFree(db, pEList->a[ii].zEName); -+ pEList->nExpr--; -+ } -+ for(ii=0; iinExpr; ii++){ -+ if( pEList->a[ii].fg.bUsed==0 ) pEList->a[ii].pExpr->op = TK_NULL; -+ } -+ } - } - -- /* Set the limiter. -+ /* Set the limiter. tag-select-0650 - */ - iEnd = sqlite3VdbeMakeLabel(pParse); - if( (p->selFlags & SF_FixedLimit)==0 ){ - p->nSelectRow = 320; /* 4 billion rows */ - } -- computeLimitRegisters(pParse, p, iEnd); -+ if( p->pLimit ) computeLimitRegisters(pParse, p, iEnd); - if( p->iLimit==0 && sSort.addrSortIndex>=0 ){ - sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen); - sSort.sortFlags |= SORTFLAG_UseSorter; - } - -- /* Open an ephemeral index to use for the distinct set. -+ /* Open an ephemeral index to use for the distinct set. tag-select-0680 - */ - if( p->selFlags & SF_Distinct ){ - sDistinct.tabTnct = pParse->nTab++; -@@ -135572,7 +152947,7 @@ SQLITE_PRIVATE int sqlite3Select( - } - - if( !isAgg && pGroupBy==0 ){ -- /* No aggregate functions and no GROUP BY clause */ -+ /* No aggregate functions and no GROUP BY clause. tag-select-0700 */ - u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0) - | (p->selFlags & SF_FixedLimit); - #ifndef SQLITE_OMIT_WINDOWFUNC -@@ -135585,12 +152960,18 @@ SQLITE_PRIVATE int sqlite3Select( - - - /* Begin the database scan. */ -- SELECTTRACE(1,pParse,p,("WhereBegin\n")); -+ TREETRACE(0x2,pParse,p,("WhereBegin\n")); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, -- p->pEList, wctrlFlags, p->nSelectRow); -+ p->pEList, p, wctrlFlags, p->nSelectRow); - if( pWInfo==0 ) goto select_end; - if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ - p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo); -+ if( pDest->eDest<=SRT_DistQueue && pDest->eDest>=SRT_DistFifo ){ -+ /* TUNING: For a UNION CTE, because UNION is implies DISTINCT, -+ ** reduce the estimated output row count by 8 (LogEst 30). -+ ** Search for tag-20250414a to see other cases */ -+ p->nSelectRow -= 30; -+ } - } - if( sDistinct.isTnct && sqlite3WhereIsDistinct(pWInfo) ){ - sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo); -@@ -135602,6 +152983,7 @@ SQLITE_PRIVATE int sqlite3Select( - sSort.pOrderBy = 0; - } - } -+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); - - /* If sorting index that was created by a prior OP_OpenEphemeral - ** instruction ended up not being needed, then change the OP_OpenEphemeral -@@ -135640,11 +153022,12 @@ SQLITE_PRIVATE int sqlite3Select( - - /* End the database scan loop. - */ -+ TREETRACE(0x2,pParse,p,("WhereEnd\n")); - sqlite3WhereEnd(pWInfo); - } - }else{ -- /* This case when there exist aggregate functions or a GROUP BY clause -- ** or both */ -+ /* This case is for when there exist aggregate functions or a GROUP BY -+ ** clause or both. tag-select-0800 */ - NameContext sNC; /* Name context for processing aggregate information */ - int iAMem; /* First Mem address for storing current GROUP BY */ - int iBMem; /* First Mem address for previous GROUP BY */ -@@ -135690,8 +153073,9 @@ SQLITE_PRIVATE int sqlite3Select( - ** ORDER BY to maximize the chances of rows being delivered in an - ** order that makes the ORDER BY redundant. */ - for(ii=0; iinExpr; ii++){ -- u8 sortFlags = sSort.pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_DESC; -- pGroupBy->a[ii].sortFlags = sortFlags; -+ u8 sortFlags; -+ sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; -+ pGroupBy->a[ii].fg.sortFlags = sortFlags; - } - if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ - orderByGrp = 1; -@@ -135710,18 +153094,22 @@ SQLITE_PRIVATE int sqlite3Select( - ** SELECT statement. - */ - pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); -- if( pAggInfo==0 ){ -+ if( pAggInfo ){ -+ sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo); -+ testcase( pParse->earlyCleanup ); -+ } -+ if( db->mallocFailed ){ - goto select_end; - } -- pAggInfo->pNext = pParse->pAggList; -- pParse->pAggList = pAggInfo; - pAggInfo->selId = p->selId; -+#ifdef SQLITE_DEBUG -+ pAggInfo->pSelect = p; -+#endif - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - sNC.pSrcList = pTabList; - sNC.uNC.pAggInfo = pAggInfo; - VVA_ONLY( sNC.ncFlags = NC_UAggInfo; ) -- pAggInfo->mnReg = pParse->nMem+1; - pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; - pAggInfo->pGroupBy = pGroupBy; - sqlite3ExprAnalyzeAggList(&sNC, pEList); -@@ -135742,46 +153130,27 @@ SQLITE_PRIVATE int sqlite3Select( - }else{ - minMaxFlag = WHERE_ORDERBY_NORMAL; - } -- for(i=0; inFunc; i++){ -- Expr *pExpr = pAggInfo->aFunc[i].pFExpr; -- assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); -- sNC.ncFlags |= NC_InAggFunc; -- sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList); --#ifndef SQLITE_OMIT_WINDOWFUNC -- assert( !IsWindowFunc(pExpr) ); -- if( ExprHasProperty(pExpr, EP_WinFunc) ){ -- sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter); -- } --#endif -- sNC.ncFlags &= ~NC_InAggFunc; -- } -- pAggInfo->mxReg = pParse->nMem; -+ analyzeAggFuncArgs(pAggInfo, &sNC); - if( db->mallocFailed ) goto select_end; --#if SELECTTRACE_ENABLED -- if( sqlite3_unsupported_selecttrace & 0x400 ){ -- int ii; -- SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x20 ){ -+ TREETRACE(0x20,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); - sqlite3TreeViewSelect(0, p, 0); -- for(ii=0; iinColumn; ii++){ -- sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", -- ii, pAggInfo->aCol[ii].iMem); -- sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); -- } -- for(ii=0; iinFunc; ii++){ -- sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", -- ii, pAggInfo->aFunc[ii].iMem); -- sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0); -+ if( minMaxFlag ){ -+ sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag); -+ sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY"); - } -+ printAggInfo(pAggInfo); - } - #endif - - - /* Processing for aggregates with GROUP BY is very different and -- ** much more complex than aggregates without a GROUP BY. -+ ** much more complex than aggregates without a GROUP BY. tag-select-0810 - */ - if( pGroupBy ){ - KeyInfo *pKeyInfo; /* Keying information for the group by clause */ -- int addr1; /* A-vs-B comparision jump */ -+ int addr1; /* A-vs-B comparison jump */ - int addrOutputRow; /* Start of subroutine that outputs a result row */ - int regOutputRow; /* Return address register for output subroutine */ - int addrSetAbort; /* Set the abort flag and return */ -@@ -135789,6 +153158,22 @@ SQLITE_PRIVATE int sqlite3Select( - int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */ - int addrReset; /* Subroutine for resetting the accumulator */ - int regReset; /* Return address register for reset subroutine */ -+ ExprList *pDistinct = 0; -+ u16 distFlag = 0; -+ int eDist = WHERE_DISTINCT_NOOP; -+ -+ if( pAggInfo->nFunc==1 -+ && pAggInfo->aFunc[0].iDistinct>=0 -+ && ALWAYS(pAggInfo->aFunc[0].pFExpr!=0) -+ && ALWAYS(ExprUseXList(pAggInfo->aFunc[0].pFExpr)) -+ && pAggInfo->aFunc[0].pFExpr->x.pList!=0 -+ ){ -+ Expr *pExpr = pAggInfo->aFunc[0].pFExpr->x.pList->a[0].pExpr; -+ pExpr = sqlite3ExprDup(db, pExpr, 0); -+ pDistinct = sqlite3ExprListDup(db, pGroupBy, 0); -+ pDistinct = sqlite3ExprListAppend(pParse, pDistinct, pExpr); -+ distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; -+ } - - /* If there is a GROUP BY clause we might need a sorting index to - ** implement it. Allocate that sorting index now. If it turns out -@@ -135817,6 +153202,7 @@ SQLITE_PRIVATE int sqlite3Select( - sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag); - VdbeComment((v, "clear abort flag")); - sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1); -+ sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr); - - /* Begin a loop that will extract all source rows in GROUP BY order. - ** This might involve two separate loops with an OP_Sort in between, or -@@ -135824,11 +153210,21 @@ SQLITE_PRIVATE int sqlite3Select( - ** in the right order to begin with. - */ - sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); -- SELECTTRACE(1,pParse,p,("WhereBegin\n")); -- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, -- WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 -+ TREETRACE(0x2,pParse,p,("WhereBegin\n")); -+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct, -+ p, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY) -+ | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0 - ); -- if( pWInfo==0 ) goto select_end; -+ if( pWInfo==0 ){ -+ sqlite3ExprListDelete(db, pDistinct); -+ goto select_end; -+ } -+ if( pParse->pIdxEpr ){ -+ optimizeAggregateUseOfIndexedExpr(pParse, p, pAggInfo, &sNC); -+ } -+ assignAggregateRegisters(pParse, pAggInfo); -+ eDist = sqlite3WhereIsDistinct(pWInfo); -+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); - if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){ - /* The optimizer is able to deliver rows in group by order so - ** we do not have to sort. The OP_OpenEphemeral table will be -@@ -135846,9 +153242,13 @@ SQLITE_PRIVATE int sqlite3Select( - int nCol; - int nGroupBy; - -- explainTempTable(pParse, -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ int addrExp; /* Address of OP_Explain instruction */ -+#endif -+ ExplainQueryPlan2(addrExp, (pParse, 0, "USE TEMP B-TREE FOR %s", - (sDistinct.isTnct && (p->selFlags&SF_Distinct)==0) ? -- "DISTINCT" : "GROUP BY"); -+ "DISTINCT" : "GROUP BY" -+ )); - - groupBySort = 1; - nGroupBy = pGroupBy->nExpr; -@@ -135863,27 +153263,50 @@ SQLITE_PRIVATE int sqlite3Select( - regBase = sqlite3GetTempRange(pParse, nCol); - sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0); - j = nGroupBy; -+ pAggInfo->directMode = 1; - for(i=0; inColumn; i++){ - struct AggInfo_col *pCol = &pAggInfo->aCol[i]; - if( pCol->iSorterColumn>=j ){ -- int r1 = j + regBase; -- sqlite3ExprCodeGetColumnOfTable(v, -- pCol->pTab, pCol->iTable, pCol->iColumn, r1); -+ sqlite3ExprCode(pParse, pCol->pCExpr, j + regBase); - j++; - } - } -+ pAggInfo->directMode = 0; - regRecord = sqlite3GetTempReg(pParse); -+ sqlite3VdbeScanStatusCounters(v, addrExp, 0, sqlite3VdbeCurrentAddr(v)); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord); - sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); -+ sqlite3VdbeScanStatusRange(v, addrExp, sqlite3VdbeCurrentAddr(v)-2, -1); - sqlite3ReleaseTempReg(pParse, regRecord); - sqlite3ReleaseTempRange(pParse, regBase, nCol); -+ TREETRACE(0x2,pParse,p,("WhereEnd\n")); - sqlite3WhereEnd(pWInfo); - pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; - sortOut = sqlite3GetTempReg(pParse); -+ sqlite3VdbeScanStatusCounters(v, addrExp, sqlite3VdbeCurrentAddr(v), 0); - sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol); - sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd); - VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); - pAggInfo->useSortingIdx = 1; -+ sqlite3VdbeScanStatusRange(v, addrExp, -1, sortPTab); -+ sqlite3VdbeScanStatusRange(v, addrExp, -1, pAggInfo->sortingIdx); -+ } -+ -+ /* If there are entries in pAgggInfo->aFunc[] that contain subexpressions -+ ** that are indexed (and that were previously identified and tagged -+ ** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions -+ ** must now be converted into a TK_AGG_COLUMN node so that the value -+ ** is correctly pulled from the index rather than being recomputed. */ -+ if( pParse->pIdxEpr ){ -+ aggregateConvertIndexedExprRefToColumn(pAggInfo); -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x20 ){ -+ TREETRACE(0x20, pParse, p, -+ ("AggInfo function expressions converted to reference index\n")); -+ sqlite3TreeViewSelect(0, p, 0); -+ printAggInfo(pAggInfo); -+ } -+#endif - } - - /* If the index or temporary table used by the GROUP BY sort -@@ -135911,12 +153334,29 @@ SQLITE_PRIVATE int sqlite3Select( - sortOut, sortPTab); - } - for(j=0; jnExpr; j++){ -+ int iOrderByCol = pGroupBy->a[j].u.x.iOrderByCol; -+ - if( groupBySort ){ - sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); - }else{ - pAggInfo->directMode = 1; - sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); - } -+ -+ if( iOrderByCol ){ -+ Expr *pX = p->pEList->a[iOrderByCol-1].pExpr; -+ Expr *pBase = sqlite3ExprSkipCollateAndLikely(pX); -+ while( ALWAYS(pBase!=0) && pBase->op==TK_IF_NULL_ROW ){ -+ pX = pBase->pLeft; -+ pBase = sqlite3ExprSkipCollateAndLikely(pX); -+ } -+ if( ALWAYS(pBase!=0) -+ && pBase->op!=TK_AGG_COLUMN -+ && pBase->op!=TK_REGISTER -+ ){ -+ sqlite3ExprToRegister(pX, iAMem+j); -+ } -+ } - } - sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, - (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); -@@ -135932,9 +153372,9 @@ SQLITE_PRIVATE int sqlite3Select( - ** and resets the aggregate accumulator registers in preparation - ** for the next GROUP BY batch. - */ -- sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); - sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow); - VdbeComment((v, "output one row")); -+ sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); - sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v); - VdbeComment((v, "check abort flag")); - sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); -@@ -135944,19 +153384,21 @@ SQLITE_PRIVATE int sqlite3Select( - ** the current row - */ - sqlite3VdbeJumpHere(v, addr1); -- updateAccumulator(pParse, iUseFlag, pAggInfo); -+ updateAccumulator(pParse, iUseFlag, pAggInfo, eDist); - sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); - VdbeComment((v, "indicate data in accumulator")); - - /* End of the loop - */ - if( groupBySort ){ -- sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop); -+ sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop); - VdbeCoverage(v); - }else{ -+ TREETRACE(0x2,pParse,p,("WhereEnd\n")); - sqlite3WhereEnd(pWInfo); - sqlite3VdbeChangeToNoop(v, addrSortingIdx); - } -+ sqlite3ExprListDelete(db, pDistinct); - - /* Output the final row of result - */ -@@ -136000,11 +153442,18 @@ SQLITE_PRIVATE int sqlite3Select( - VdbeComment((v, "indicate accumulator empty")); - sqlite3VdbeAddOp1(v, OP_Return, regReset); - -+ if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){ -+ struct AggInfo_func *pF = &pAggInfo->aFunc[0]; -+ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); -+ } - } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ - else { -+ /* Aggregate functions without GROUP BY. tag-select-0820 */ - Table *pTab; - if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){ -- /* If isSimpleCount() returns a pointer to a Table structure, then -+ /* tag-select-0821 -+ ** -+ ** If isSimpleCount() returns a pointer to a Table structure, then - ** the SQL statement is of the form: - ** - ** SELECT count(*) FROM -@@ -136058,12 +153507,17 @@ SQLITE_PRIVATE int sqlite3Select( - if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); - } -- sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); -+ assignAggregateRegisters(pParse, pAggInfo); -+ sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0)); - sqlite3VdbeAddOp1(v, OP_Close, iCsr); - explainSimpleCount(pParse, pTab, pBest); - }else{ -+ /* The general case of an aggregate query without GROUP BY -+ ** tag-select-0822 */ - int regAcc = 0; /* "populate accumulators" flag */ -- int addrSkip; -+ ExprList *pDistinct = 0; -+ u16 distFlag = 0; -+ int eDist; - - /* If there are accumulator registers but no min() or max() functions - ** without FILTER clauses, allocate register regAcc. Register regAcc -@@ -136087,7 +153541,12 @@ SQLITE_PRIVATE int sqlite3Select( - regAcc = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc); - } -+ }else if( pAggInfo->nFunc==1 && pAggInfo->aFunc[0].iDistinct>=0 ){ -+ assert( ExprUseXList(pAggInfo->aFunc[0].pFExpr) ); -+ pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList; -+ distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0; - } -+ assignAggregateRegisters(pParse, pAggInfo); - - /* This case runs if the aggregate has no GROUP BY clause. The - ** processing is much simpler since there is only a single row -@@ -136104,18 +153563,27 @@ SQLITE_PRIVATE int sqlite3Select( - assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 ); - assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 ); - -- SELECTTRACE(1,pParse,p,("WhereBegin\n")); -+ TREETRACE(0x2,pParse,p,("WhereBegin\n")); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, -- 0, minMaxFlag, 0); -+ pDistinct, p, minMaxFlag|distFlag, 0); - if( pWInfo==0 ){ - goto select_end; - } -- updateAccumulator(pParse, regAcc, pAggInfo); -+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n")); -+ eDist = sqlite3WhereIsDistinct(pWInfo); -+ updateAccumulator(pParse, regAcc, pAggInfo, eDist); -+ if( eDist!=WHERE_DISTINCT_NOOP ){ -+ struct AggInfo_func *pF = pAggInfo->aFunc; -+ if( pF ){ -+ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); -+ } -+ } -+ - if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc); -- addrSkip = sqlite3WhereOrderByLimitOptLabel(pWInfo); -- if( addrSkip!=sqlite3WhereContinueLabel(pWInfo) ){ -- sqlite3VdbeGoto(v, addrSkip); -+ if( minMaxFlag ){ -+ sqlite3WhereMinMaxOptEarlyOut(v, pWInfo); - } -+ TREETRACE(0x2,pParse,p,("WhereEnd\n")); - sqlite3WhereEnd(pWInfo); - finalizeAggFunctions(pParse, pAggInfo); - } -@@ -136134,11 +153602,9 @@ SQLITE_PRIVATE int sqlite3Select( - } - - /* If there is an ORDER BY clause, then we need to sort the results -- ** and send them to the callback one by one. -+ ** and send them to the callback one by one. tag-select-0900 - */ - if( sSort.pOrderBy ){ -- explainTempTable(pParse, -- sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY"); - assert( p->pEList==pEList ); - generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest); - } -@@ -136155,29 +153621,36 @@ SQLITE_PRIVATE int sqlite3Select( - ** successful coding of the SELECT. - */ - select_end: -+ assert( db->mallocFailed==0 || db->mallocFailed==1 ); -+ assert( db->mallocFailed==0 || pParse->nErr!=0 ); - sqlite3ExprListDelete(db, pMinMaxOrderBy); - #ifdef SQLITE_DEBUG -+ /* Internal self-checks. tag-select-1000 */ - if( pAggInfo && !db->mallocFailed ){ -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x20 ){ -+ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); -+ printAggInfo(pAggInfo); -+ } -+#endif - for(i=0; inColumn; i++){ - Expr *pExpr = pAggInfo->aCol[i].pCExpr; -- assert( pExpr!=0 || db->mallocFailed ); - if( pExpr==0 ) continue; - assert( pExpr->pAggInfo==pAggInfo ); - assert( pExpr->iAgg==i ); - } - for(i=0; inFunc; i++){ - Expr *pExpr = pAggInfo->aFunc[i].pFExpr; -- assert( pExpr!=0 || db->mallocFailed ); -- if( pExpr==0 ) continue; -+ assert( pExpr!=0 ); - assert( pExpr->pAggInfo==pAggInfo ); - assert( pExpr->iAgg==i ); - } - } - #endif - --#if SELECTTRACE_ENABLED -- SELECTTRACE(0x1,pParse,p,("end processing\n")); -- if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ -+#if TREETRACE_ENABLED -+ TREETRACE(0x1,pParse,p,("end processing\n")); -+ if( (sqlite3TreeTrace & 0x40000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ - sqlite3TreeViewSelect(0, p, 0); - } - #endif -@@ -136438,28 +153911,49 @@ SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerS - ** pTab as well as the triggers lised in pTab->pTrigger. - */ - SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ -- Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; -- Trigger *pList = 0; /* List of triggers to return */ -- -- if( pParse->disableTriggers ){ -- return 0; -+ Schema *pTmpSchema; /* Schema of the pTab table */ -+ Trigger *pList; /* List of triggers to return */ -+ HashElem *p; /* Loop variable for TEMP triggers */ -+ -+ assert( pParse->disableTriggers==0 ); -+ pTmpSchema = pParse->db->aDb[1].pSchema; -+ p = sqliteHashFirst(&pTmpSchema->trigHash); -+ pList = pTab->pTrigger; -+ while( p ){ -+ Trigger *pTrig = (Trigger *)sqliteHashData(p); -+ if( pTrig->pTabSchema==pTab->pSchema -+ && pTrig->table -+ && 0==sqlite3StrICmp(pTrig->table, pTab->zName) -+ && (pTrig->pTabSchema!=pTmpSchema || pTrig->bReturning) -+ ){ -+ pTrig->pNext = pList; -+ pList = pTrig; -+ }else if( pTrig->op==TK_RETURNING ){ -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+ assert( pParse->db->pVtabCtx==0 ); -+#endif -+ assert( pParse->bReturning ); -+ assert( !pParse->isCreate ); -+ assert( &(pParse->u1.d.pReturning->retTrig) == pTrig ); -+ pTrig->table = pTab->zName; -+ pTrig->pTabSchema = pTab->pSchema; -+ pTrig->pNext = pList; -+ pList = pTrig; -+ } -+ p = sqliteHashNext(p); - } -- -- if( pTmpSchema!=pTab->pSchema ){ -- HashElem *p; -- assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); -- for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ -- Trigger *pTrig = (Trigger *)sqliteHashData(p); -- if( pTrig->pTabSchema==pTab->pSchema -- && 0==sqlite3StrICmp(pTrig->table, pTab->zName) -- ){ -- pTrig->pNext = (pList ? pList : pTab->pTrigger); -- pList = pTrig; -- } -+#if 0 -+ if( pList ){ -+ Trigger *pX; -+ printf("Triggers for %s:", pTab->zName); -+ for(pX=pList; pX; pX=pX->pNext){ -+ printf(" %s", pX->zName); - } -+ printf("\n"); -+ fflush(stdout); - } -- -- return (pList ? pList : pTab->pTrigger); -+#endif -+ return pList; - } - - /* -@@ -136522,8 +154016,10 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( - ** name on pTableName if we are reparsing out of the schema table - */ - if( db->init.busy && iDb!=1 ){ -- sqlite3DbFree(db, pTableName->a[0].zDatabase); -- pTableName->a[0].zDatabase = 0; -+ assert( pTableName->a[0].fg.fixedSchema==0 ); -+ assert( pTableName->a[0].fg.isSubquery==0 ); -+ sqlite3DbFree(db, pTableName->a[0].u4.zDatabase); -+ pTableName->a[0].u4.zDatabase = 0; - } - - /* If the trigger name was unqualified, and the table is a temp table, -@@ -136547,22 +154043,15 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( - pTab = sqlite3SrcListLookup(pParse, pTableName); - if( !pTab ){ - /* The table does not exist. */ -- if( db->init.iDb==1 ){ -- /* Ticket #3810. -- ** Normally, whenever a table is dropped, all associated triggers are -- ** dropped too. But if a TEMP trigger is created on a non-TEMP table -- ** and the table is dropped by a different database connection, the -- ** trigger is not visible to the database connection that does the -- ** drop so the trigger cannot be dropped. This results in an -- ** "orphaned trigger" - a trigger whose associated table is missing. -- */ -- db->init.orphanTrigger = 1; -- } -- goto trigger_cleanup; -+ goto trigger_orphan_error; - } - if( IsVirtual(pTab) ){ - sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); -- goto trigger_cleanup; -+ goto trigger_orphan_error; -+ } -+ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){ -+ sqlite3ErrorMsg(pParse, "cannot create triggers on shadow tables"); -+ goto trigger_orphan_error; - } - - /* Check that the trigger name is not reserved and that no trigger of the -@@ -136583,6 +154072,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( - }else{ - assert( !db->init.busy ); - sqlite3CodeVerifySchema(pParse, iDb); -+ VVA_ONLY( pParse->ifNotExists = 1; ) - } - goto trigger_cleanup; - } -@@ -136597,15 +154087,15 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( - /* INSTEAD of triggers are only for views and views only support INSTEAD - ** of triggers. - */ -- if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ -+ if( IsView(pTab) && tr_tm!=TK_INSTEAD ){ - sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", -- (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); -- goto trigger_cleanup; -+ (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName->a); -+ goto trigger_orphan_error; - } -- if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ -+ if( !IsView(pTab) && tr_tm==TK_INSTEAD ){ - sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" -- " trigger on table: %S", pTableName, 0); -- goto trigger_cleanup; -+ " trigger on table: %S", pTableName->a); -+ goto trigger_orphan_error; - } - - #ifndef SQLITE_OMIT_AUTHORIZATION -@@ -136665,6 +154155,23 @@ trigger_cleanup: - }else{ - assert( pParse->pNewTrigger==pTrigger ); - } -+ return; -+ -+trigger_orphan_error: -+ if( db->init.iDb==1 ){ -+ /* Ticket #3810. -+ ** Normally, whenever a table is dropped, all associated triggers are -+ ** dropped too. But if a TEMP trigger is created on a non-TEMP table -+ ** and the table is dropped by a different database connection, the -+ ** trigger is not visible to the database connection that does the -+ ** drop so the trigger cannot be dropped. This results in an -+ ** "orphaned trigger" - a trigger whose associated table is missing. -+ ** -+ ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df -+ */ -+ db->init.orphanTrigger = 1; -+ } -+ goto trigger_cleanup; - } - - /* -@@ -136715,6 +154222,23 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( - Vdbe *v; - char *z; - -+ /* If this is a new CREATE TABLE statement, and if shadow tables -+ ** are read-only, and the trigger makes a change to a shadow table, -+ ** then raise an error - do not allow the trigger to be created. */ -+ if( sqlite3ReadOnlyShadowTables(db) ){ -+ TriggerStep *pStep; -+ for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){ -+ if( pStep->zTarget!=0 -+ && sqlite3ShadowTableName(db, pStep->zTarget) -+ ){ -+ sqlite3ErrorMsg(pParse, -+ "trigger \"%s\" may not write to shadow table \"%s\"", -+ pTrig->zName, pStep->zTarget); -+ goto triggerfinish_cleanup; -+ } -+ } -+ } -+ - /* Make an entry in the sqlite_schema table */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto triggerfinish_cleanup; -@@ -136722,14 +154246,14 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( - z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); - testcase( z==0 ); - sqlite3NestedParse(pParse, -- "INSERT INTO %Q." DFLT_SCHEMA_TABLE -+ "INSERT INTO %Q." LEGACY_SCHEMA_TABLE - " VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", - db->aDb[iDb].zDbSName, zName, - pTrig->table, z); - sqlite3DbFree(db, z); - sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddParseSchemaOp(v, iDb, -- sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName)); -+ sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName), 0); - } - - if( db->init.busy ){ -@@ -136807,6 +154331,7 @@ static TriggerStep *triggerStepAllocate( - sqlite3 *db = pParse->db; - TriggerStep *pTriggerStep; - -+ if( pParse->nErr ) return 0; - pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); - if( pTriggerStep ){ - char *z = (char*)&pTriggerStep[1]; -@@ -136877,7 +154402,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep( - SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep( - Parse *pParse, /* Parser */ - Token *pTableName, /* Name of the table to be updated */ -- SrcList *pFrom, -+ SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */ - ExprList *pEList, /* The SET clause: list of column and new values */ - Expr *pWhere, /* The WHERE clause */ - u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ -@@ -136942,7 +154467,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep( - ** Recursively delete a Trigger structure - */ - SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){ -- if( pTrigger==0 ) return; -+ if( pTrigger==0 || pTrigger->bReturning ) return; - sqlite3DeleteTriggerStep(db, pTrigger->step_list); - sqlite3DbFree(db, pTrigger->zName); - sqlite3DbFree(db, pTrigger->table); -@@ -136972,7 +154497,8 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) - } - - assert( pName->nSrc==1 ); -- zDb = pName->a[0].zDatabase; -+ assert( pName->a[0].fg.fixedSchema==0 && pName->a[0].fg.isSubquery==0 ); -+ zDb = pName->a[0].u4.zDatabase; - zName = pName->a[0].zName; - assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); - for(i=OMIT_TEMPDB; inDb; i++){ -@@ -136984,7 +154510,7 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) - } - if( !pTrigger ){ - if( !noErr ){ -- sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); -+ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a); - }else{ - sqlite3CodeVerifyNamedSchema(pParse, zDb); - } -@@ -137036,7 +154562,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ - */ - if( (v = sqlite3GetVdbe(pParse))!=0 ){ - sqlite3NestedParse(pParse, -- "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", -+ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", - db->aDb[iDb].zDbSName, pTrigger->zName - ); - sqlite3ChangeCookie(pParse, iDb); -@@ -137090,13 +154616,22 @@ static int checkColumnOverlap(IdList *pIdList, ExprList *pEList){ - return 0; - } - -+/* -+** Return true if any TEMP triggers exist -+*/ -+static int tempTriggersExist(sqlite3 *db){ -+ if( NEVER(db->aDb[1].pSchema==0) ) return 0; -+ if( sqliteHashFirst(&db->aDb[1].pSchema->trigHash)==0 ) return 0; -+ return 1; -+} -+ - /* - ** Return a list of all triggers on table pTab if there exists at least - ** one trigger that must be fired when an operation of type 'op' is - ** performed on the table, and, if that operation is an UPDATE, if at - ** least one of the columns in pChanges is being modified. - */ --SQLITE_PRIVATE Trigger *sqlite3TriggersExist( -+static SQLITE_NOINLINE Trigger *triggersReallyExist( - Parse *pParse, /* Parse context */ - Table *pTab, /* The table the contains the triggers */ - int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ -@@ -137107,20 +154642,74 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist( - Trigger *pList = 0; - Trigger *p; - -- if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){ -- pList = sqlite3TriggerList(pParse, pTab); -- } -- assert( pList==0 || IsVirtual(pTab)==0 ); -- for(p=pList; p; p=p->pNext){ -- if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ -- mask |= p->tr_tm; -+ pList = sqlite3TriggerList(pParse, pTab); -+ assert( pList==0 || IsVirtual(pTab)==0 -+ || (pList->bReturning && pList->pNext==0) ); -+ if( pList!=0 ){ -+ p = pList; -+ if( (pParse->db->flags & SQLITE_EnableTrigger)==0 -+ && pTab->pTrigger!=0 -+ ){ -+ /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that -+ ** only TEMP triggers are allowed. Truncate the pList so that it -+ ** includes only TEMP triggers */ -+ if( pList==pTab->pTrigger ){ -+ pList = 0; -+ goto exit_triggers_exist; -+ } -+ while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext; -+ p->pNext = 0; -+ p = pList; - } -+ do{ -+ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ -+ mask |= p->tr_tm; -+ }else if( p->op==TK_RETURNING ){ -+ /* The first time a RETURNING trigger is seen, the "op" value tells -+ ** us what time of trigger it should be. */ -+ assert( sqlite3IsToplevel(pParse) ); -+ p->op = op; -+ if( IsVirtual(pTab) ){ -+ if( op!=TK_INSERT ){ -+ sqlite3ErrorMsg(pParse, -+ "%s RETURNING is not available on virtual tables", -+ op==TK_DELETE ? "DELETE" : "UPDATE"); -+ } -+ p->tr_tm = TRIGGER_BEFORE; -+ }else{ -+ p->tr_tm = TRIGGER_AFTER; -+ } -+ mask |= p->tr_tm; -+ }else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE -+ && sqlite3IsToplevel(pParse) ){ -+ /* Also fire a RETURNING trigger for an UPSERT */ -+ mask |= p->tr_tm; -+ } -+ p = p->pNext; -+ }while( p ); - } -+exit_triggers_exist: - if( pMask ){ - *pMask = mask; - } - return (mask ? pList : 0); - } -+SQLITE_PRIVATE Trigger *sqlite3TriggersExist( -+ Parse *pParse, /* Parse context */ -+ Table *pTab, /* The table the contains the triggers */ -+ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ -+ ExprList *pChanges, /* Columns that change in an UPDATE statement */ -+ int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ -+){ -+ assert( pTab!=0 ); -+ if( (pTab->pTrigger==0 && !tempTriggersExist(pParse->db)) -+ || pParse->disableTriggers -+ ){ -+ if( pMask ) *pMask = 0; -+ return 0; -+ } -+ return triggersReallyExist(pParse,pTab,op,pChanges,pMask); -+} - - /* - ** Convert the pStep->zTarget string into a SrcList and return a pointer -@@ -137146,10 +154735,20 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc( - Schema *pSchema = pStep->pTrig->pSchema; - pSrc->a[0].zName = zName; - if( pSchema!=db->aDb[1].pSchema ){ -- pSrc->a[0].pSchema = pSchema; -+ assert( pSrc->a[0].fg.fixedSchema || pSrc->a[0].u4.zDatabase==0 ); -+ pSrc->a[0].u4.pSchema = pSchema; -+ pSrc->a[0].fg.fixedSchema = 1; - } - if( pStep->pFrom ){ - SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0); -+ if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){ -+ Select *pSubquery; -+ Token as; -+ pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0); -+ as.n = 0; -+ as.z = 0; -+ pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); -+ } - pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup); - } - }else{ -@@ -137158,6 +154757,224 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc( - return pSrc; - } - -+/* -+** Return true if the pExpr term from the RETURNING clause argument -+** list is of the form "*". Raise an error if the terms if of the -+** form "table.*". -+*/ -+static int isAsteriskTerm( -+ Parse *pParse, /* Parsing context */ -+ Expr *pTerm /* A term in the RETURNING clause */ -+){ -+ assert( pTerm!=0 ); -+ if( pTerm->op==TK_ASTERISK ) return 1; -+ if( pTerm->op!=TK_DOT ) return 0; -+ assert( pTerm->pRight!=0 ); -+ assert( pTerm->pLeft!=0 ); -+ if( pTerm->pRight->op!=TK_ASTERISK ) return 0; -+ sqlite3ErrorMsg(pParse, "RETURNING may not use \"TABLE.*\" wildcards"); -+ return 1; -+} -+ -+/* The input list pList is the list of result set terms from a RETURNING -+** clause. The table that we are returning from is pTab. -+** -+** This routine makes a copy of the pList, and at the same time expands -+** any "*" wildcards to be the complete set of columns from pTab. -+*/ -+static ExprList *sqlite3ExpandReturning( -+ Parse *pParse, /* Parsing context */ -+ ExprList *pList, /* The arguments to RETURNING */ -+ Table *pTab /* The table being updated */ -+){ -+ ExprList *pNew = 0; -+ sqlite3 *db = pParse->db; -+ int i; -+ -+ for(i=0; inExpr; i++){ -+ Expr *pOldExpr = pList->a[i].pExpr; -+ if( NEVER(pOldExpr==0) ) continue; -+ if( isAsteriskTerm(pParse, pOldExpr) ){ -+ int jj; -+ for(jj=0; jjnCol; jj++){ -+ Expr *pNewExpr; -+ if( IsHiddenColumn(pTab->aCol+jj) ) continue; -+ pNewExpr = sqlite3Expr(db, TK_ID, pTab->aCol[jj].zCnName); -+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr); -+ if( !db->mallocFailed ){ -+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; -+ pItem->zEName = sqlite3DbStrDup(db, pTab->aCol[jj].zCnName); -+ pItem->fg.eEName = ENAME_NAME; -+ } -+ } -+ }else{ -+ Expr *pNewExpr = sqlite3ExprDup(db, pOldExpr, 0); -+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr); -+ if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){ -+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1]; -+ pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName); -+ pItem->fg.eEName = pList->a[i].fg.eEName; -+ } -+ } -+ } -+ return pNew; -+} -+ -+/* If the Expr node is a subquery or an EXISTS operator or an IN operator that -+** uses a subquery, and if the subquery is SF_Correlated, then mark the -+** expression as EP_VarSelect. -+*/ -+static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ -+ UNUSED_PARAMETER(NotUsed); -+ if( ExprUseXSelect(pExpr) -+ && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 -+ ){ -+ testcase( ExprHasProperty(pExpr, EP_VarSelect) ); -+ ExprSetProperty(pExpr, EP_VarSelect); -+ } -+ return WRC_Continue; -+} -+ -+ -+/* -+** If the SELECT references the table pWalker->u.pTab, then do two things: -+** -+** (1) Mark the SELECT as as SF_Correlated. -+** (2) Set pWalker->eCode to non-zero so that the caller will know -+** that (1) has happened. -+*/ -+static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ -+ int i; -+ SrcList *pSrc; -+ assert( pSelect!=0 ); -+ pSrc = pSelect->pSrc; -+ assert( pSrc!=0 ); -+ for(i=0; inSrc; i++){ -+ if( pSrc->a[i].pSTab==pWalker->u.pTab ){ -+ testcase( pSelect->selFlags & SF_Correlated ); -+ pSelect->selFlags |= SF_Correlated; -+ pWalker->eCode = 1; -+ break; -+ } -+ } -+ return WRC_Continue; -+} -+ -+/* -+** Scan the expression list that is the argument to RETURNING looking -+** for subqueries that depend on the table which is being modified in the -+** statement that is hosting the RETURNING clause (pTab). Mark all such -+** subqueries as SF_Correlated. If the subqueries are part of an -+** expression, mark the expression as EP_VarSelect. -+** -+** https://sqlite.org/forum/forumpost/2c83569ce8945d39 -+*/ -+static void sqlite3ProcessReturningSubqueries( -+ ExprList *pEList, -+ Table *pTab -+){ -+ Walker w; -+ memset(&w, 0, sizeof(w)); -+ w.xExprCallback = sqlite3ExprWalkNoop; -+ w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; -+ w.u.pTab = pTab; -+ sqlite3WalkExprList(&w, pEList); -+ if( w.eCode ){ -+ w.xExprCallback = sqlite3ReturningSubqueryVarSelect; -+ w.xSelectCallback = sqlite3SelectWalkNoop; -+ sqlite3WalkExprList(&w, pEList); -+ } -+} -+ -+/* -+** Generate code for the RETURNING trigger. Unlike other triggers -+** that invoke a subprogram in the bytecode, the code for RETURNING -+** is generated in-line. -+*/ -+static void codeReturningTrigger( -+ Parse *pParse, /* Parse context */ -+ Trigger *pTrigger, /* The trigger step that defines the RETURNING */ -+ Table *pTab, /* The table to code triggers from */ -+ int regIn /* The first in an array of registers */ -+){ -+ Vdbe *v = pParse->pVdbe; -+ sqlite3 *db = pParse->db; -+ ExprList *pNew; -+ Returning *pReturning; -+ Select sSelect; -+ SrcList *pFrom; -+ u8 fromSpace[SZ_SRCLIST_1]; -+ -+ assert( v!=0 ); -+ if( !pParse->bReturning ){ -+ /* This RETURNING trigger must be for a different statement as -+ ** this statement lacks a RETURNING clause. */ -+ return; -+ } -+ assert( db->pParse==pParse ); -+ assert( !pParse->isCreate ); -+ pReturning = pParse->u1.d.pReturning; -+ if( pTrigger != &(pReturning->retTrig) ){ -+ /* This RETURNING trigger is for a different statement */ -+ return; -+ } -+ memset(&sSelect, 0, sizeof(sSelect)); -+ pFrom = (SrcList*)fromSpace; -+ memset(pFrom, 0, SZ_SRCLIST_1); -+ sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); -+ sSelect.pSrc = pFrom; -+ pFrom->nSrc = 1; -+ pFrom->a[0].pSTab = pTab; -+ pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */ -+ pFrom->a[0].iCursor = -1; -+ sqlite3SelectPrep(pParse, &sSelect, 0); -+ if( pParse->nErr==0 ){ -+ assert( db->mallocFailed==0 ); -+ sqlite3GenerateColumnNames(pParse, &sSelect); -+ } -+ sqlite3ExprListDelete(db, sSelect.pEList); -+ pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab); -+ if( pParse->nErr==0 ){ -+ NameContext sNC; -+ memset(&sNC, 0, sizeof(sNC)); -+ if( pReturning->nRetCol==0 ){ -+ pReturning->nRetCol = pNew->nExpr; -+ pReturning->iRetCur = pParse->nTab++; -+ } -+ sNC.pParse = pParse; -+ sNC.uNC.iBaseReg = regIn; -+ sNC.ncFlags = NC_UBaseReg; -+ pParse->eTriggerOp = pTrigger->op; -+ pParse->pTriggerTab = pTab; -+ if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK -+ && ALWAYS(!db->mallocFailed) -+ ){ -+ int i; -+ int nCol = pNew->nExpr; -+ int reg = pParse->nMem+1; -+ sqlite3ProcessReturningSubqueries(pNew, pTab); -+ pParse->nMem += nCol+2; -+ pReturning->iRetReg = reg; -+ for(i=0; ia[i].pExpr; -+ assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */ -+ sqlite3ExprCodeFactorable(pParse, pCol, reg+i); -+ if( sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){ -+ sqlite3VdbeAddOp1(v, OP_RealAffinity, reg+i); -+ } -+ } -+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i); -+ sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1); -+ sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1); -+ } -+ } -+ sqlite3ExprListDelete(db, pNew); -+ pParse->eTriggerOp = 0; -+ pParse->pTriggerTab = 0; -+} -+ -+ -+ - /* - ** Generate VDBE code for the statements inside the body of a single - ** trigger. -@@ -137207,6 +155024,7 @@ static int codeTriggerProgram( - sqlite3ExprDup(db, pStep->pWhere, 0), - pParse->eOrconf, 0, 0, 0 - ); -+ sqlite3VdbeAddOp0(v, OP_ResetCount); - break; - } - case TK_INSERT: { -@@ -137217,6 +155035,7 @@ static int codeTriggerProgram( - pParse->eOrconf, - sqlite3UpsertDup(db, pStep->pUpsert) - ); -+ sqlite3VdbeAddOp0(v, OP_ResetCount); - break; - } - case TK_DELETE: { -@@ -137224,6 +155043,7 @@ static int codeTriggerProgram( - sqlite3TriggerStepSrc(pParse, pStep), - sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0 - ); -+ sqlite3VdbeAddOp0(v, OP_ResetCount); - break; - } - default: assert( pStep->op==TK_SELECT ); { -@@ -137235,9 +155055,6 @@ static int codeTriggerProgram( - break; - } - } -- if( pStep->op!=TK_SELECT ){ -- sqlite3VdbeAddOp0(v, OP_ResetCount); -- } - } - - return 0; -@@ -137295,8 +155112,8 @@ static TriggerPrg *codeRowTrigger( - Vdbe *v; /* Temporary VM */ - NameContext sNC; /* Name context for sub-vdbe */ - SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ -- Parse *pSubParse; /* Parse context for sub-vdbe */ - int iEndTrigger = 0; /* Label to jump to if WHEN is false */ -+ Parse sSubParse; /* Parse context for sub-vdbe */ - - assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); - assert( pTop->pVdbe ); -@@ -137318,19 +155135,19 @@ static TriggerPrg *codeRowTrigger( - - /* Allocate and populate a new Parse context to use for coding the - ** trigger sub-program. */ -- pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); -- if( !pSubParse ) return 0; -+ sqlite3ParseObjectInit(&sSubParse, db); - memset(&sNC, 0, sizeof(sNC)); -- sNC.pParse = pSubParse; -- pSubParse->db = db; -- pSubParse->pTriggerTab = pTab; -- pSubParse->pToplevel = pTop; -- pSubParse->zAuthContext = pTrigger->zName; -- pSubParse->eTriggerOp = pTrigger->op; -- pSubParse->nQueryLoop = pParse->nQueryLoop; -- pSubParse->disableVtab = pParse->disableVtab; -- -- v = sqlite3GetVdbe(pSubParse); -+ sNC.pParse = &sSubParse; -+ sSubParse.pTriggerTab = pTab; -+ sSubParse.pToplevel = pTop; -+ sSubParse.zAuthContext = pTrigger->zName; -+ sSubParse.eTriggerOp = pTrigger->op; -+ sSubParse.nQueryLoop = pParse->nQueryLoop; -+ sSubParse.prepFlags = pParse->prepFlags; -+ sSubParse.oldmask = 0; -+ sSubParse.newmask = 0; -+ -+ v = sqlite3GetVdbe(&sSubParse); - if( v ){ - VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", - pTrigger->zName, onErrorText(orconf), -@@ -137353,17 +155170,17 @@ static TriggerPrg *codeRowTrigger( - ** OP_Halt inserted at the end of the program. */ - if( pTrigger->pWhen ){ - pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0); -- if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) -- && db->mallocFailed==0 -+ if( db->mallocFailed==0 -+ && SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) - ){ -- iEndTrigger = sqlite3VdbeMakeLabel(pSubParse); -- sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); -+ iEndTrigger = sqlite3VdbeMakeLabel(&sSubParse); -+ sqlite3ExprIfFalse(&sSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); - } - sqlite3ExprDelete(db, pWhen); - } - - /* Code the trigger program into the sub-vdbe. */ -- codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); -+ codeTriggerProgram(&sSubParse, pTrigger->step_list, orconf); - - /* Insert an OP_Halt at the end of the sub-program. */ - if( iEndTrigger ){ -@@ -137371,24 +155188,24 @@ static TriggerPrg *codeRowTrigger( - } - sqlite3VdbeAddOp0(v, OP_Halt); - VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); -+ transferParseError(pParse, &sSubParse); - -- transferParseError(pParse, pSubParse); -- if( db->mallocFailed==0 && pParse->nErr==0 ){ -+ if( pParse->nErr==0 ){ -+ assert( db->mallocFailed==0 ); - pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); - } -- pProgram->nMem = pSubParse->nMem; -- pProgram->nCsr = pSubParse->nTab; -+ pProgram->nMem = sSubParse.nMem; -+ pProgram->nCsr = sSubParse.nTab; - pProgram->token = (void *)pTrigger; -- pPrg->aColmask[0] = pSubParse->oldmask; -- pPrg->aColmask[1] = pSubParse->newmask; -+ pPrg->aColmask[0] = sSubParse.oldmask; -+ pPrg->aColmask[1] = sSubParse.newmask; - sqlite3VdbeDelete(v); -+ }else{ -+ transferParseError(pParse, &sSubParse); - } - -- assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); -- assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); -- sqlite3ParserReset(pSubParse); -- sqlite3StackFree(db, pSubParse); -- -+ assert( !sSubParse.pTriggerPrg && !sSubParse.nMaxArg ); -+ sqlite3ParseObjectReset(&sSubParse); - return pPrg; - } - -@@ -137421,6 +155238,7 @@ static TriggerPrg *getRowTrigger( - /* If an existing TriggerPrg could not be located, create a new one. */ - if( !pPrg ){ - pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); -+ pParse->db->errByteOffset = -1; - } - - return pPrg; -@@ -137443,7 +155261,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( - Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ - TriggerPrg *pPrg; - pPrg = getRowTrigger(pParse, p, pTab, orconf); -- assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); -+ assert( pPrg || pParse->nErr ); - - /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program - ** is a pointer to the sub-vdbe containing the trigger program. */ -@@ -137460,7 +155278,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( - ** invocation is disallowed if (a) the sub-program is really a trigger, - ** not a foreign key action, and (b) the flag to enable recursive triggers - ** is clear. */ -- sqlite3VdbeChangeP5(v, (u8)bRecursive); -+ sqlite3VdbeChangeP5(v, (u16)bRecursive); - } - } - -@@ -137486,7 +155304,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( - ** ... ... - ** reg+N OLD.* value of right-most column of pTab - ** reg+N+1 NEW.rowid --** reg+N+2 OLD.* value of left-most column of pTab -+** reg+N+2 NEW.* value of left-most column of pTab - ** ... ... - ** reg+N+N+1 NEW.* value of right-most column of pTab - ** -@@ -137531,12 +155349,20 @@ SQLITE_PRIVATE void sqlite3CodeRowTrigger( - assert( p->pSchema==p->pTabSchema - || p->pSchema==pParse->db->aDb[1].pSchema ); - -- /* Determine whether we should code this trigger */ -- if( p->op==op -+ /* Determine whether we should code this trigger. One of two choices: -+ ** 1. The trigger is an exact match to the current DML statement -+ ** 2. This is a RETURNING trigger for INSERT but we are currently -+ ** doing the UPDATE part of an UPSERT. -+ */ -+ if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE)) - && p->tr_tm==tr_tm - && checkColumnOverlap(p->pColumns, pChanges) - ){ -- sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); -+ if( !p->bReturning ){ -+ sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); -+ }else if( sqlite3IsToplevel(pParse) ){ -+ codeReturningTrigger(pParse, p, pTab, reg); -+ } - } - } - } -@@ -137580,14 +155406,22 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask( - Trigger *p; - - assert( isNew==1 || isNew==0 ); -+ if( IsView(pTab) ){ -+ return 0xffffffff; -+ } - for(p=pTrigger; p; p=p->pNext){ -- if( p->op==op && (tr_tm&p->tr_tm) -+ if( p->op==op -+ && (tr_tm&p->tr_tm) - && checkColumnOverlap(p->pColumns,pChanges) - ){ -- TriggerPrg *pPrg; -- pPrg = getRowTrigger(pParse, p, pTab, orconf); -- if( pPrg ){ -- mask |= pPrg->aColmask[isNew]; -+ if( p->bReturning ){ -+ mask = 0xffffffff; -+ }else{ -+ TriggerPrg *pPrg; -+ pPrg = getRowTrigger(pParse, p, pTab, orconf); -+ if( pPrg ){ -+ mask |= pPrg->aColmask[isNew]; -+ } - } - } - } -@@ -137660,21 +155494,25 @@ static void updateVirtualTable( - ** it has been converted into REAL. - */ - SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ -+ Column *pCol; - assert( pTab!=0 ); -- if( !pTab->pSelect ){ -+ assert( pTab->nCol>i ); -+ pCol = &pTab->aCol[i]; -+ if( pCol->iDflt ){ - sqlite3_value *pValue = 0; - u8 enc = ENC(sqlite3VdbeDb(v)); -- Column *pCol = &pTab->aCol[i]; -- VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); -+ assert( !IsView(pTab) ); -+ VdbeComment((v, "%s.%s", pTab->zName, pCol->zCnName)); - assert( inCol ); -- sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc, -+ sqlite3ValueFromExpr(sqlite3VdbeDb(v), -+ sqlite3ColumnExpr(pTab,pCol), enc, - pCol->affinity, &pValue); - if( pValue ){ - sqlite3VdbeAppendP4(v, pValue, P4_MEM); - } - } - #ifndef SQLITE_OMIT_FLOATING_POINT -- if( pTab->aCol[i].affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ -+ if( pCol->affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); - } - #endif -@@ -137799,7 +155637,7 @@ static void updateFromSelect( - Expr *pLimit2 = 0; - ExprList *pOrderBy2 = 0; - sqlite3 *db = pParse->db; -- Table *pTab = pTabList->a[0].pTab; -+ Table *pTab = pTabList->a[0].pSTab; - SrcList *pSrc; - Expr *pWhere2; - int eDest; -@@ -137821,9 +155659,10 @@ static void updateFromSelect( - - assert( pTabList->nSrc>1 ); - if( pSrc ){ -+ assert( pSrc->a[0].fg.notCte ); - pSrc->a[0].iCursor = -1; -- pSrc->a[0].pTab->nTabRef--; -- pSrc->a[0].pTab = 0; -+ pSrc->a[0].pSTab->nTabRef--; -+ pSrc->a[0].pSTab = 0; - } - if( pPk ){ - for(i=0; inKeyCol; i++){ -@@ -137835,8 +155674,8 @@ static void updateFromSelect( - #endif - pList = sqlite3ExprListAppend(pParse, pList, pNew); - } -- eDest = SRT_Upfrom; -- }else if( pTab->pSelect ){ -+ eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom; -+ }else if( IsView(pTab) ){ - for(i=0; inCol; i++){ - pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); - } -@@ -137850,7 +155689,8 @@ static void updateFromSelect( - } - #endif - } -- if( ALWAYS(pChanges) ){ -+ assert( pChanges!=0 || pParse->db->mallocFailed ); -+ if( pChanges ){ - for(i=0; inExpr; i++){ - pList = sqlite3ExprListAppend(pParse, pList, - sqlite3ExprDup(db, pChanges->a[i].pExpr, 0) -@@ -137858,8 +155698,10 @@ static void updateFromSelect( - } - } - pSelect = sqlite3SelectNew(pParse, pList, -- pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UpdateFrom|SF_IncludeHidden, pLimit2 -+ pSrc, pWhere2, pGrp, 0, pOrderBy2, -+ SF_UFSrcCheck|SF_IncludeHidden|SF_UpdateFrom, pLimit2 - ); -+ if( pSelect ) pSelect->selFlags |= SF_OrderByReqd; - sqlite3SelectDestInit(&dest, eDest, iEph); - dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1); - sqlite3Select(pParse, pSelect, &dest); -@@ -137944,9 +155786,11 @@ SQLITE_PRIVATE void sqlite3Update( - - memset(&sContext, 0, sizeof(sContext)); - db = pParse->db; -- if( pParse->nErr || db->mallocFailed ){ -+ assert( db->pParse==pParse ); -+ if( pParse->nErr ){ - goto update_cleanup; - } -+ assert( db->mallocFailed==0 ); - - /* Locate the table which we want to update. - */ -@@ -137959,7 +155803,7 @@ SQLITE_PRIVATE void sqlite3Update( - */ - #ifndef SQLITE_OMIT_TRIGGER - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask); -- isView = pTab->pSelect!=0; -+ isView = IsView(pTab); - assert( pTrigger || tmask==0 ); - #else - # define pTrigger 0 -@@ -137971,6 +155815,14 @@ SQLITE_PRIVATE void sqlite3Update( - # define isView 0 - #endif - -+#if TREETRACE_ENABLED -+ if( sqlite3TreeTrace & 0x10000 ){ -+ sqlite3TreeViewLine(0, "In sqlite3Update() at %s:%d", __FILE__, __LINE__); -+ sqlite3TreeViewUpdate(pParse->pWith, pTabList, pChanges, pWhere, -+ onError, pOrderBy, pLimit, pUpsert, pTrigger); -+ } -+#endif -+ - /* If there was a FROM clause, set nChangeFrom to the number of expressions - ** in the change-list. Otherwise, set it to 0. There cannot be a FROM - ** clause if this function is being called to generate code for part of -@@ -137991,7 +155843,7 @@ SQLITE_PRIVATE void sqlite3Update( - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto update_cleanup; - } -- if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ -+ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ - goto update_cleanup; - } - -@@ -138053,30 +155905,27 @@ SQLITE_PRIVATE void sqlite3Update( - if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ - goto update_cleanup; - } -- for(j=0; jnCol; j++){ -- if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zEName)==0 ){ -- if( j==pTab->iPKey ){ -- chngRowid = 1; -- pRowidExpr = pChanges->a[i].pExpr; -- iRowidExpr = i; -- }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ -- chngPk = 1; -- } -+ j = sqlite3ColumnIndex(pTab, pChanges->a[i].zEName); -+ if( j>=0 ){ -+ if( j==pTab->iPKey ){ -+ chngRowid = 1; -+ pRowidExpr = pChanges->a[i].pExpr; -+ iRowidExpr = i; -+ }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ -+ chngPk = 1; -+ } - #ifndef SQLITE_OMIT_GENERATED_COLUMNS -- else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){ -- testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ); -- testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); -- sqlite3ErrorMsg(pParse, -- "cannot UPDATE generated column \"%s\"", -- pTab->aCol[j].zName); -- goto update_cleanup; -- } --#endif -- aXRef[j] = i; -- break; -+ else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){ -+ testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ); -+ testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); -+ sqlite3ErrorMsg(pParse, -+ "cannot UPDATE generated column \"%s\"", -+ pTab->aCol[j].zCnName); -+ goto update_cleanup; - } -- } -- if( j>=pTab->nCol ){ -+#endif -+ aXRef[j] = i; -+ }else{ - if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zEName) ){ - j = -1; - chngRowid = 1; -@@ -138092,7 +155941,7 @@ SQLITE_PRIVATE void sqlite3Update( - { - int rc; - rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, -- j<0 ? "ROWID" : pTab->aCol[j].zName, -+ j<0 ? "ROWID" : pTab->aCol[j].zCnName, - db->aDb[iDb].zDbSName); - if( rc==SQLITE_DENY ){ - goto update_cleanup; -@@ -138124,8 +155973,10 @@ SQLITE_PRIVATE void sqlite3Update( - for(i=0; inCol; i++){ - if( aXRef[i]>=0 ) continue; - if( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ) continue; -- if( sqlite3ExprReferencesUpdatedColumn(pTab->aCol[i].pDflt, -- aXRef, chngRowid) ){ -+ if( sqlite3ExprReferencesUpdatedColumn( -+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]), -+ aXRef, chngRowid) -+ ){ - aXRef[i] = 99999; - bProgress = 1; - } -@@ -138244,6 +156095,7 @@ SQLITE_PRIVATE void sqlite3Update( - if( (db->flags&SQLITE_CountRows)!=0 - && !pParse->pTriggerTab - && !pParse->nested -+ && !pParse->bReturning - && pUpsert==0 - ){ - regRowCount = ++pParse->nMem; -@@ -138252,6 +156104,8 @@ SQLITE_PRIVATE void sqlite3Update( - - if( nChangeFrom==0 && HasRowid(pTab) ){ - sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); -+ iEph = pParse->nTab++; -+ addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet); - }else{ - assert( pPk!=0 || HasRowid(pTab) ); - nPk = pPk ? pPk->nKeyCol : 0; -@@ -138302,15 +156156,25 @@ SQLITE_PRIVATE void sqlite3Update( - /* Begin the database scan. - ** - ** Do not consider a single-pass strategy for a multi-row update if -- ** there are any triggers or foreign keys to process, or rows may -- ** be deleted as a result of REPLACE conflict handling. Any of these -- ** things might disturb a cursor being used to scan through the table -- ** or index, causing a single-pass approach to malfunction. */ -- flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE; -- if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ -+ ** there is anything that might disrupt the cursor being used to do -+ ** the UPDATE: -+ ** (1) This is a nested UPDATE -+ ** (2) There are triggers -+ ** (3) There are FOREIGN KEY constraints -+ ** (4) There are REPLACE conflict handlers -+ ** (5) There are subqueries in the WHERE clause -+ */ -+ flags = WHERE_ONEPASS_DESIRED; -+ if( !pParse->nested -+ && !pTrigger -+ && !hasFK -+ && !chngKey -+ && !bReplace -+ && (pWhere==0 || !ExprHasProperty(pWhere, EP_Subquery)) -+ ){ - flags |= WHERE_ONEPASS_MULTIROW; - } -- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur); -+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); - if( pWInfo==0 ) goto update_cleanup; - - /* A one-pass strategy that might update more than one row may not -@@ -138343,9 +156207,10 @@ SQLITE_PRIVATE void sqlite3Update( - ** leave it in register regOldRowid. */ - sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); - if( eOnePass==ONEPASS_OFF ){ -- /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ - aRegIdx[nAllIdx] = ++pParse->nMem; -- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); -+ sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid); -+ }else{ -+ if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen); - } - }else{ - /* Read the PK of the current row into an array of registers. In -@@ -138377,6 +156242,8 @@ SQLITE_PRIVATE void sqlite3Update( - - if( !isView ){ - int addrOnce = 0; -+ int iNotUsed1 = 0; -+ int iNotUsed2 = 0; - - /* Open every index that needs updating. */ - if( eOnePass!=ONEPASS_OFF ){ -@@ -138388,7 +156255,7 @@ SQLITE_PRIVATE void sqlite3Update( - addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - } - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, -- aToOpen, 0, 0); -+ aToOpen, &iNotUsed1, &iNotUsed2); - if( addrOnce ){ - sqlite3VdbeJumpHereOrPopInst(v, addrOnce); - } -@@ -138396,7 +156263,12 @@ SQLITE_PRIVATE void sqlite3Update( - - /* Top of the update loop */ - if( eOnePass!=ONEPASS_OFF ){ -- if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){ -+ if( aiCurOnePass[0]!=iDataCur -+ && aiCurOnePass[1]!=iDataCur -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ && !isView -+#endif -+ ){ - assert( pPk ); - sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey); - VdbeCoverage(v); -@@ -138433,8 +156305,9 @@ SQLITE_PRIVATE void sqlite3Update( - VdbeCoverage(v); - } - }else{ -- labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak, -- regOldRowid); -+ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); -+ labelContinue = sqlite3VdbeMakeLabel(pParse); -+ addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); - VdbeCoverage(v); -@@ -138477,6 +156350,9 @@ SQLITE_PRIVATE void sqlite3Update( - } - } - if( chngRowid==0 && pPk==0 ){ -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ if( isView ) sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); -+#endif - sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); - } - } -@@ -138600,7 +156476,7 @@ SQLITE_PRIVATE void sqlite3Update( - }else{ - sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid); - } -- VdbeCoverageNeverTaken(v); -+ VdbeCoverage(v); - } - - /* Do FK constraint checks. */ -@@ -138673,8 +156549,10 @@ SQLITE_PRIVATE void sqlite3Update( - sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); - } - -- sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, -- TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); -+ if( pTrigger ){ -+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, -+ TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); -+ } - - /* Repeat the above with the next record to be updated, until - ** all record selected by the WHERE clause have been updated. -@@ -138684,11 +156562,9 @@ SQLITE_PRIVATE void sqlite3Update( - }else if( eOnePass==ONEPASS_MULTI ){ - sqlite3VdbeResolveLabel(v, labelContinue); - sqlite3WhereEnd(pWInfo); -- }else if( pPk || nChangeFrom ){ -+ }else{ - sqlite3VdbeResolveLabel(v, labelContinue); - sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); -- }else{ -- sqlite3VdbeGoto(v, labelContinue); - } - sqlite3VdbeResolveLabel(v, labelBreak); - -@@ -138705,9 +156581,7 @@ SQLITE_PRIVATE void sqlite3Update( - ** that information. - */ - if( regRowCount ){ -- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); -- sqlite3VdbeSetNumCols(v, 1); -- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); -+ sqlite3CodeChangeCount(v, regRowCount, "rows updated"); - } - - update_cleanup: -@@ -138773,7 +156647,7 @@ static void updateVirtualTable( - int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */ - int regArg; /* First register in VUpdate arg array */ - int regRec; /* Register in which to assemble record */ -- int regRowid; /* Register for ephem table rowid */ -+ int regRowid; /* Register for ephemeral table rowid */ - int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */ - int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */ - int eOnePass; /* True to use onepass strategy */ -@@ -138788,12 +156662,26 @@ static void updateVirtualTable( - regArg = pParse->nMem + 1; - pParse->nMem += nArg; - if( pSrc->nSrc>1 ){ -+ Index *pPk = 0; - Expr *pRow; - ExprList *pList; -- if( pRowid ){ -- pRow = sqlite3ExprDup(db, pRowid, 0); -+ if( HasRowid(pTab) ){ -+ if( pRowid ){ -+ pRow = sqlite3ExprDup(db, pRowid, 0); -+ }else{ -+ pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0); -+ } - }else{ -- pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0); -+ i16 iPk; /* PRIMARY KEY column */ -+ pPk = sqlite3PrimaryKeyIndex(pTab); -+ assert( pPk!=0 ); -+ assert( pPk->nKeyCol==1 ); -+ iPk = pPk->aiColumn[0]; -+ if( aXRef[iPk]>=0 ){ -+ pRow = sqlite3ExprDup(db, pChanges->a[aXRef[iPk]].pExpr, 0); -+ }else{ -+ pRow = exprRowColumn(pParse, iPk); -+ } - } - pList = sqlite3ExprListAppend(pParse, 0, pRow); - -@@ -138803,11 +156691,13 @@ static void updateVirtualTable( - sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0) - ); - }else{ -- pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); -+ Expr *pRowExpr = exprRowColumn(pParse, i); -+ if( pRowExpr ) pRowExpr->op2 = OPFLAG_NOCHNG; -+ pList = sqlite3ExprListAppend(pParse, pList, pRowExpr); - } - } - -- updateFromSelect(pParse, ephemTab, 0, pList, pSrc, pWhere, 0, 0); -+ updateFromSelect(pParse, ephemTab, pPk, pList, pSrc, pWhere, 0, 0); - sqlite3ExprListDelete(db, pList); - eOnePass = ONEPASS_OFF; - }else{ -@@ -138815,7 +156705,9 @@ static void updateVirtualTable( - regRowid = ++pParse->nMem; - - /* Start scanning the virtual table */ -- pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0); -+ pWInfo = sqlite3WhereBegin( -+ pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0 -+ ); - if( pWInfo==0 ) return; - - /* Populate the argument registers. */ -@@ -138878,7 +156770,7 @@ static void updateVirtualTable( - sqlite3WhereEnd(pWInfo); - } - -- /* Begin scannning through the ephemeral table. */ -+ /* Begin scanning through the ephemeral table. */ - addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v); - - /* Extract arguments from the current row of the ephemeral table and -@@ -138926,15 +156818,22 @@ static void updateVirtualTable( - /* - ** Free a list of Upsert objects - */ --SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){ -- if( p ){ -+static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){ -+ do{ -+ Upsert *pNext = p->pNextUpsert; - sqlite3ExprListDelete(db, p->pUpsertTarget); - sqlite3ExprDelete(db, p->pUpsertTargetWhere); - sqlite3ExprListDelete(db, p->pUpsertSet); - sqlite3ExprDelete(db, p->pUpsertWhere); -+ sqlite3DbFree(db, p->pToFree); - sqlite3DbFree(db, p); -- } -+ p = pNext; -+ }while( p ); - } -+SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){ -+ if( p ) upsertDelete(db, p); -+} -+ - - /* - ** Duplicate an Upsert object. -@@ -138945,7 +156844,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){ - sqlite3ExprListDup(db, p->pUpsertTarget, 0), - sqlite3ExprDup(db, p->pUpsertTargetWhere, 0), - sqlite3ExprListDup(db, p->pUpsertSet, 0), -- sqlite3ExprDup(db, p->pUpsertWhere, 0) -+ sqlite3ExprDup(db, p->pUpsertWhere, 0), -+ sqlite3UpsertDup(db, p->pNextUpsert) - ); - } - -@@ -138957,22 +156857,25 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew( - ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */ - Expr *pTargetWhere, /* Optional WHERE clause on the target */ - ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */ -- Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */ -+ Expr *pWhere, /* WHERE clause for the ON CONFLICT UPDATE */ -+ Upsert *pNext /* Next ON CONFLICT clause in the list */ - ){ - Upsert *pNew; -- pNew = sqlite3DbMallocRaw(db, sizeof(Upsert)); -+ pNew = sqlite3DbMallocZero(db, sizeof(Upsert)); - if( pNew==0 ){ - sqlite3ExprListDelete(db, pTarget); - sqlite3ExprDelete(db, pTargetWhere); - sqlite3ExprListDelete(db, pSet); - sqlite3ExprDelete(db, pWhere); -+ sqlite3UpsertDelete(db, pNext); - return 0; - }else{ - pNew->pUpsertTarget = pTarget; - pNew->pUpsertTargetWhere = pTargetWhere; - pNew->pUpsertSet = pSet; - pNew->pUpsertWhere = pWhere; -- pNew->pUpsertIdx = 0; -+ pNew->isDoUpdate = pSet!=0; -+ pNew->pNextUpsert = pNext; - } - return pNew; - } -@@ -138987,7 +156890,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew( - SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( - Parse *pParse, /* The parsing context */ - SrcList *pTabList, /* Table into which we are inserting */ -- Upsert *pUpsert /* The ON CONFLICT clauses */ -+ Upsert *pUpsert, /* The ON CONFLICT clauses */ -+ Upsert *pAll /* Complete list of all ON CONFLICT clauses */ - ){ - Table *pTab; /* That table into which we are inserting */ - int rc; /* Result code */ -@@ -138997,9 +156901,10 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( - Expr *pTerm; /* One term of the conflict-target clause */ - NameContext sNC; /* Context for resolving symbolic names */ - Expr sCol[2]; /* Index column converted into an Expr */ -+ int nClause = 0; /* Counter of ON CONFLICT clauses */ - - assert( pTabList->nSrc==1 ); -- assert( pTabList->a[0].pTab!=0 ); -+ assert( pTabList->a[0].pSTab!=0 ); - assert( pUpsert!=0 ); - assert( pUpsert->pUpsertTarget!=0 ); - -@@ -139010,87 +156915,144 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - sNC.pSrcList = pTabList; -- rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); -- if( rc ) return rc; -- rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); -- if( rc ) return rc; -+ for(; pUpsert && pUpsert->pUpsertTarget; -+ pUpsert=pUpsert->pNextUpsert, nClause++){ -+ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); -+ if( rc ) return rc; -+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); -+ if( rc ) return rc; - -- /* Check to see if the conflict target matches the rowid. */ -- pTab = pTabList->a[0].pTab; -- pTarget = pUpsert->pUpsertTarget; -- iCursor = pTabList->a[0].iCursor; -- if( HasRowid(pTab) -- && pTarget->nExpr==1 -- && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN -- && pTerm->iColumn==XN_ROWID -- ){ -- /* The conflict-target is the rowid of the primary table */ -- assert( pUpsert->pUpsertIdx==0 ); -- return SQLITE_OK; -- } -+ /* Check to see if the conflict target matches the rowid. */ -+ pTab = pTabList->a[0].pSTab; -+ pTarget = pUpsert->pUpsertTarget; -+ iCursor = pTabList->a[0].iCursor; -+ if( HasRowid(pTab) -+ && pTarget->nExpr==1 -+ && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN -+ && pTerm->iColumn==XN_ROWID -+ ){ -+ /* The conflict-target is the rowid of the primary table */ -+ assert( pUpsert->pUpsertIdx==0 ); -+ continue; -+ } - -- /* Initialize sCol[0..1] to be an expression parse tree for a -- ** single column of an index. The sCol[0] node will be the TK_COLLATE -- ** operator and sCol[1] will be the TK_COLUMN operator. Code below -- ** will populate the specific collation and column number values -- ** prior to comparing against the conflict-target expression. -- */ -- memset(sCol, 0, sizeof(sCol)); -- sCol[0].op = TK_COLLATE; -- sCol[0].pLeft = &sCol[1]; -- sCol[1].op = TK_COLUMN; -- sCol[1].iTable = pTabList->a[0].iCursor; -+ /* Initialize sCol[0..1] to be an expression parse tree for a -+ ** single column of an index. The sCol[0] node will be the TK_COLLATE -+ ** operator and sCol[1] will be the TK_COLUMN operator. Code below -+ ** will populate the specific collation and column number values -+ ** prior to comparing against the conflict-target expression. -+ */ -+ memset(sCol, 0, sizeof(sCol)); -+ sCol[0].op = TK_COLLATE; -+ sCol[0].pLeft = &sCol[1]; -+ sCol[1].op = TK_COLUMN; -+ sCol[1].iTable = pTabList->a[0].iCursor; - -- /* Check for matches against other indexes */ -- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ -- int ii, jj, nn; -- if( !IsUniqueIndex(pIdx) ) continue; -- if( pTarget->nExpr!=pIdx->nKeyCol ) continue; -- if( pIdx->pPartIdxWhere ){ -- if( pUpsert->pUpsertTargetWhere==0 ) continue; -- if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, -- pIdx->pPartIdxWhere, iCursor)!=0 ){ -- continue; -+ /* Check for matches against other indexes */ -+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ -+ int ii, jj, nn; -+ if( !IsUniqueIndex(pIdx) ) continue; -+ if( pTarget->nExpr!=pIdx->nKeyCol ) continue; -+ if( pIdx->pPartIdxWhere ){ -+ if( pUpsert->pUpsertTargetWhere==0 ) continue; -+ if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, -+ pIdx->pPartIdxWhere, iCursor)!=0 ){ -+ continue; -+ } - } -- } -- nn = pIdx->nKeyCol; -- for(ii=0; iiazColl[ii]; -- if( pIdx->aiColumn[ii]==XN_EXPR ){ -- assert( pIdx->aColExpr!=0 ); -- assert( pIdx->aColExpr->nExpr>ii ); -- pExpr = pIdx->aColExpr->a[ii].pExpr; -- if( pExpr->op!=TK_COLLATE ){ -- sCol[0].pLeft = pExpr; -+ nn = pIdx->nKeyCol; -+ for(ii=0; iiazColl[ii]; -+ if( pIdx->aiColumn[ii]==XN_EXPR ){ -+ assert( pIdx->aColExpr!=0 ); -+ assert( pIdx->aColExpr->nExpr>ii ); -+ assert( pIdx->bHasExpr ); -+ pExpr = pIdx->aColExpr->a[ii].pExpr; -+ if( pExpr->op!=TK_COLLATE ){ -+ sCol[0].pLeft = pExpr; -+ pExpr = &sCol[0]; -+ } -+ }else{ -+ sCol[0].pLeft = &sCol[1]; -+ sCol[1].iColumn = pIdx->aiColumn[ii]; - pExpr = &sCol[0]; - } -- }else{ -- sCol[0].pLeft = &sCol[1]; -- sCol[1].iColumn = pIdx->aiColumn[ii]; -- pExpr = &sCol[0]; -- } -- for(jj=0; jja[jj].pExpr, pExpr,iCursor)<2 ){ -- break; /* Column ii of the index matches column jj of target */ -+ for(jj=0; jja[jj].pExpr,pExpr,iCursor)<2 ){ -+ break; /* Column ii of the index matches column jj of target */ -+ } -+ } -+ if( jj>=nn ){ -+ /* The target contains no match for column jj of the index */ -+ break; - } - } -- if( jj>=nn ){ -- /* The target contains no match for column jj of the index */ -- break; -+ if( iipUpsertIdx = pIdx; -+ if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){ -+ /* Really this should be an error. The isDup ON CONFLICT clause will -+ ** never fire. But this problem was not discovered until three years -+ ** after multi-CONFLICT upsert was added, and so we silently ignore -+ ** the problem to prevent breaking applications that might actually -+ ** have redundant ON CONFLICT clauses. */ -+ pUpsert->isDup = 1; -+ } -+ break; - } -- if( iipUpsertIdx==0 ){ -+ char zWhich[16]; -+ if( nClause==0 && pUpsert->pNextUpsert==0 ){ -+ zWhich[0] = 0; -+ }else{ -+ sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1); -+ } -+ sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any " -+ "PRIMARY KEY or UNIQUE constraint", zWhich); -+ return SQLITE_ERROR; - } -- pUpsert->pUpsertIdx = pIdx; -- return SQLITE_OK; - } -- sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any " -- "PRIMARY KEY or UNIQUE constraint"); -- return SQLITE_ERROR; -+ return SQLITE_OK; -+} -+ -+/* -+** Return true if pUpsert is the last ON CONFLICT clause with a -+** conflict target, or if pUpsert is followed by another ON CONFLICT -+** clause that targets the INTEGER PRIMARY KEY. -+*/ -+SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){ -+ Upsert *pNext; -+ if( NEVER(pUpsert==0) ) return 0; -+ pNext = pUpsert->pNextUpsert; -+ while( 1 /*exit-by-return*/ ){ -+ if( pNext==0 ) return 1; -+ if( pNext->pUpsertTarget==0 ) return 1; -+ if( pNext->pUpsertIdx==0 ) return 1; -+ if( !pNext->isDup ) return 0; -+ pNext = pNext->pNextUpsert; -+ } -+ return 0; -+} -+ -+/* -+** Given the list of ON CONFLICT clauses described by pUpsert, and -+** a particular index pIdx, return a pointer to the particular ON CONFLICT -+** clause that applies to the index. Or, if the index is not subject to -+** any ON CONFLICT clause, return NULL. -+*/ -+SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){ -+ while( -+ pUpsert -+ && pUpsert->pUpsertTarget!=0 -+ && pUpsert->pUpsertIdx!=pIdx -+ ){ -+ pUpsert = pUpsert->pNextUpsert; -+ } -+ return pUpsert; - } - - /* -@@ -139114,11 +157076,13 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate( - SrcList *pSrc; /* FROM clause for the UPDATE */ - int iDataCur; - int i; -+ Upsert *pTop = pUpsert; - - assert( v!=0 ); - assert( pUpsert!=0 ); -- VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); - iDataCur = pUpsert->iDataCur; -+ pUpsert = sqlite3UpsertOfIndex(pTop, pIdx); -+ VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); - if( pIdx && iCur!=iDataCur ){ - if( HasRowid(pTab) ){ - int regRowid = sqlite3GetTempReg(pParse); -@@ -139137,7 +157101,7 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate( - k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); - sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i); - VdbeComment((v, "%s.%s", pIdx->zName, -- pTab->aCol[pPk->aiColumn[i]].zName)); -+ pTab->aCol[pPk->aiColumn[i]].zCnName)); - } - sqlite3VdbeVerifyAbortable(v, OE_Abort); - i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk); -@@ -139148,19 +157112,17 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate( - sqlite3VdbeJumpHere(v, i); - } - } -- /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So -- ** we have to make a copy before passing it down into sqlite3Update() */ -- pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0); -+ /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does. -+ ** So we have to make a copy before passing it down into sqlite3Update() */ -+ pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0); - /* excluded.* columns of type REAL need to be converted to a hard real */ - for(i=0; inCol; i++){ - if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ -- sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i); -+ sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i); - } - } -- sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet, -- pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert); -- pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */ -- pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */ -+ sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0), -+ sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert); - VdbeNoopComment((v, "End DO UPDATE of UPSERT")); - } - -@@ -139286,7 +157248,7 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){ - #else - /* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments - ** to VACUUM are silently ignored. This is a back-out of a bug fix that -- ** occurred on 2016-08-19 (https://www.sqlite.org/src/info/083f9e6270). -+ ** occurred on 2016-08-19 (https://sqlite.org/src/info/083f9e6270). - ** The buggy behavior is required for binary compatibility with some - ** legacy applications. */ - iDb = sqlite3FindDb(pParse->db, pNm); -@@ -139321,8 +157283,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - Btree *pTemp; /* The temporary database we vacuum into */ - u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ - u64 saved_flags; /* Saved value of db->flags */ -- int saved_nChange; /* Saved value of db->nChange */ -- int saved_nTotalChange; /* Saved value of db->nTotalChange */ -+ i64 saved_nChange; /* Saved value of db->nChange */ -+ i64 saved_nTotalChange; /* Saved value of db->nTotalChange */ - u32 saved_openFlags; /* Saved value of db->openFlags */ - u8 saved_mTrace; /* Saved trace settings */ - Db *pDb = 0; /* Database to detach at end of vacuum */ -@@ -139331,6 +157293,10 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - int nDb; /* Number of attached databases */ - const char *zDbMain; /* Schema name of database to vacuum */ - const char *zOut; /* Name of output file */ -+ u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */ -+ u64 iRandom; /* Random value used for zDbVacuum[] */ -+ char zDbVacuum[42]; /* Name of the ATTACH-ed database used for vacuum */ -+ - - if( !db->autoCommit ){ - sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); -@@ -139361,7 +157327,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - saved_nChange = db->nChange; - saved_nTotalChange = db->nTotalChange; - saved_mTrace = db->mTrace; -- db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; -+ db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments; - db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; - db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder - | SQLITE_Defensive | SQLITE_CountRows); -@@ -139371,27 +157337,29 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - pMain = db->aDb[iDb].pBt; - isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); - -- /* Attach the temporary database as 'vacuum_db'. The synchronous pragma -+ /* Attach the temporary database as 'vacuum_XXXXXX'. The synchronous pragma - ** can be set to 'off' for this file, as it is not recovered if a crash - ** occurs anyway. The integrity of the database is maintained by a - ** (possibly synchronous) transaction opened on the main database before - ** sqlite3BtreeCopyFile() is called. - ** -- ** An optimisation would be to use a non-journaled pager. -- ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but -+ ** An optimization would be to use a non-journaled pager. -+ ** (Later:) I tried setting "PRAGMA vacuum_XXXXXX.journal_mode=OFF" but - ** that actually made the VACUUM run slower. Very little journalling - ** actually occurs when doing a vacuum since the vacuum_db is initially - ** empty. Only the journal header is written. Apparently it takes more - ** time to parse and run the PRAGMA to turn journalling off than it does - ** to write the journal header file. - */ -+ sqlite3_randomness(sizeof(iRandom),&iRandom); -+ sqlite3_snprintf(sizeof(zDbVacuum), zDbVacuum, "vacuum_%016llx", iRandom); - nDb = db->nDb; -- rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut); -+ rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS %s", zOut, zDbVacuum); - db->openFlags = saved_openFlags; - if( rc!=SQLITE_OK ) goto end_of_vacuum; - assert( (db->nDb-1)==nDb ); - pDb = &db->aDb[nDb]; -- assert( strcmp(pDb->zDbSName,"vacuum_db")==0 ); -+ assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); - pTemp = pDb->pBt; - if( pOut ){ - sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); -@@ -139402,12 +157370,17 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - goto end_of_vacuum; - } - db->mDbFlags |= DBFLAG_VacuumInto; -+ -+ /* For a VACUUM INTO, the pager-flags are set to the same values as -+ ** they are for the database being vacuumed, except that PAGER_CACHESPILL -+ ** is always set. */ -+ pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); - } - nRes = sqlite3BtreeGetRequestedReserve(pMain); - - sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); - sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); -- sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL); -+ sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); - - /* Begin a transaction and take an exclusive lock on the main database - ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below, -@@ -139420,7 +157393,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - - /* Do not attempt to change the page size for a WAL database */ - if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) -- ==PAGER_JOURNALMODE_WAL ){ -+ ==PAGER_JOURNALMODE_WAL -+ && pOut==0 -+ ){ - db->nextPagesize = 0; - } - -@@ -139461,11 +157436,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - ** the contents to the temporary database. - */ - rc = execSqlF(db, pzErrMsg, -- "SELECT'INSERT INTO vacuum_db.'||quote(name)" -+ "SELECT'INSERT INTO %s.'||quote(name)" - "||' SELECT*FROM\"%w\".'||quote(name)" -- "FROM vacuum_db.sqlite_schema " -+ "FROM %s.sqlite_schema " - "WHERE type='table'AND coalesce(rootpage,1)>0", -- zDbMain -+ zDbVacuum, zDbMain, zDbVacuum - ); - assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); - db->mDbFlags &= ~DBFLAG_Vacuum; -@@ -139477,11 +157452,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - ** from the schema table. - */ - rc = execSqlF(db, pzErrMsg, -- "INSERT INTO vacuum_db.sqlite_schema" -+ "INSERT INTO %s.sqlite_schema" - " SELECT*FROM \"%w\".sqlite_schema" - " WHERE type IN('view','trigger')" - " OR(type='table'AND rootpage=0)", -- zDbMain -+ zDbVacuum, zDbMain - ); - if( rc ) goto end_of_vacuum; - -@@ -139509,8 +157484,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( - BTREE_APPLICATION_ID, 0, /* Preserve the application id */ - }; - -- assert( 1==sqlite3BtreeIsInTrans(pTemp) ); -- assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) ); -+ assert( SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pTemp) ); -+ assert( pOut!=0 || SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pMain) ); - - /* Copy Btree meta values */ - for(i=0; ipVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); -+ for(pVtab=pTab->u.vtab.p; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); - return pVtab; - } - -@@ -139782,36 +157758,40 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){ - - assert( db ); - assert( pVTab->nRef>0 ); -- assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE ); -+ assert( db->eOpenState==SQLITE_STATE_OPEN -+ || db->eOpenState==SQLITE_STATE_ZOMBIE ); - - pVTab->nRef--; - if( pVTab->nRef==0 ){ - sqlite3_vtab *p = pVTab->pVtab; -- sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod); - if( p ){ - p->pModule->xDisconnect(p); - } -+ sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod); - sqlite3DbFree(db, pVTab); - } - } - - /* - ** Table p is a virtual table. This function moves all elements in the --** p->pVTable list to the sqlite3.pDisconnect lists of their associated -+** p->u.vtab.p list to the sqlite3.pDisconnect lists of their associated - ** database connections to be disconnected at the next opportunity. - ** Except, if argument db is not NULL, then the entry associated with --** connection db is left in the p->pVTable list. -+** connection db is left in the p->u.vtab.p list. - */ - static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ - VTable *pRet = 0; -- VTable *pVTable = p->pVTable; -- p->pVTable = 0; -+ VTable *pVTable; -+ -+ assert( IsVirtual(p) ); -+ pVTable = p->u.vtab.p; -+ p->u.vtab.p = 0; - - /* Assert that the mutex (if any) associated with the BtShared database - ** that contains table p is held by the caller. See header comments - ** above function sqlite3VtabUnlockList() for an explanation of why - ** this makes it safe to access the sqlite3.pDisconnect list of any -- ** database connection that may have an entry in the p->pVTable list. -+ ** database connection that may have an entry in the p->u.vtab.p list. - */ - assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); - -@@ -139821,7 +157801,7 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ - assert( db2 ); - if( db2==db ){ - pRet = pVTable; -- p->pVTable = pRet; -+ p->u.vtab.p = pRet; - pRet->pNext = 0; - }else{ - pVTable->pNext = db2->pDisconnect; -@@ -139849,7 +157829,7 @@ SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){ - assert( sqlite3BtreeHoldsAllMutexes(db) ); - assert( sqlite3_mutex_held(db->mutex) ); - -- for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){ -+ for(ppVTab=&p->u.vtab.p; *ppVTab; ppVTab=&(*ppVTab)->pNext){ - if( (*ppVTab)->db==db ){ - VTable *pVTab = *ppVTab; - *ppVTab = pVTab->pNext; -@@ -139888,7 +157868,6 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){ - - if( p ){ - db->pDisconnect = 0; -- sqlite3ExpirePreparedStatements(db, 0); - do { - VTable *pNext = p->pNext; - sqlite3VtabUnlock(p); -@@ -139912,37 +157891,42 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){ - ** database connection. - */ - SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){ -- if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); -- if( p->azModuleArg ){ -+ assert( IsVirtual(p) ); -+ assert( db!=0 ); -+ if( db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); -+ if( p->u.vtab.azArg ){ - int i; -- for(i=0; inModuleArg; i++){ -- if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]); -+ for(i=0; iu.vtab.nArg; i++){ -+ if( i!=1 ) sqlite3DbFree(db, p->u.vtab.azArg[i]); - } -- sqlite3DbFree(db, p->azModuleArg); -+ sqlite3DbFree(db, p->u.vtab.azArg); - } - } - - /* --** Add a new module argument to pTable->azModuleArg[]. -+** Add a new module argument to pTable->u.vtab.azArg[]. - ** The string is not copied - the pointer is stored. The - ** string will be freed automatically when the table is - ** deleted. - */ - static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){ -- sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg); -+ sqlite3_int64 nBytes; - char **azModuleArg; - sqlite3 *db = pParse->db; -- if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ -+ -+ assert( IsVirtual(pTable) ); -+ nBytes = sizeof(char *)*(2+pTable->u.vtab.nArg); -+ if( pTable->u.vtab.nArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ - sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName); - } -- azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); -+ azModuleArg = sqlite3DbRealloc(db, pTable->u.vtab.azArg, nBytes); - if( azModuleArg==0 ){ - sqlite3DbFree(db, zArg); - }else{ -- int i = pTable->nModuleArg++; -+ int i = pTable->u.vtab.nArg++; - azModuleArg[i] = zArg; - azModuleArg[i+1] = 0; -- pTable->azModuleArg = azModuleArg; -+ pTable->u.vtab.azArg = azModuleArg; - } - } - -@@ -139965,10 +157949,11 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( - pTable = pParse->pNewTable; - if( pTable==0 ) return; - assert( 0==pTable->pIndex ); -+ pTable->eTabType = TABTYP_VTAB; - - db = pParse->db; - -- assert( pTable->nModuleArg==0 ); -+ assert( pTable->u.vtab.nArg==0 ); - addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName)); - addModuleArgument(pParse, pTable, 0); - addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName)); -@@ -139985,11 +157970,11 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse( - ** sqlite_schema table, has already been made by sqlite3StartTable(). - ** The second call, to obtain permission to create the table, is made now. - */ -- if( pTable->azModuleArg ){ -+ if( pTable->u.vtab.azArg ){ - int iDb = sqlite3SchemaToIndex(db, pTable->pSchema); - assert( iDb>=0 ); /* The database the table is being created in */ - sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, -- pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName); -+ pTable->u.vtab.azArg[0], pParse->db->aDb[iDb].zDbSName); - } - #endif - } -@@ -140017,9 +158002,10 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ - sqlite3 *db = pParse->db; /* The database connection */ - - if( pTab==0 ) return; -+ assert( IsVirtual(pTab) ); - addArgumentToVtab(pParse); - pParse->sArg.z = 0; -- if( pTab->nModuleArg<1 ) return; -+ if( pTab->u.vtab.nArg<1 ) return; - - /* If the CREATE VIRTUAL TABLE statement is being entered for the - ** first time (in other words if the virtual table is actually being -@@ -140046,44 +158032,41 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ - ** schema table. We just need to update that slot with all - ** the information we've collected. - ** -- ** The VM register number pParse->regRowid holds the rowid of an -- ** entry in the sqlite_schema table tht was created for this vtab -+ ** The VM register number pParse->u1.cr.regRowid holds the rowid of an -+ ** entry in the sqlite_schema table that was created for this vtab - ** by sqlite3StartTable(). - */ - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); -+ assert( pParse->isCreate ); - sqlite3NestedParse(pParse, -- "UPDATE %Q." DFLT_SCHEMA_TABLE " " -+ "UPDATE %Q." LEGACY_SCHEMA_TABLE " " - "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " - "WHERE rowid=#%d", - db->aDb[iDb].zDbSName, - pTab->zName, - pTab->zName, - zStmt, -- pParse->regRowid -+ pParse->u1.cr.regRowid - ); - v = sqlite3GetVdbe(pParse); - sqlite3ChangeCookie(pParse, iDb); - - sqlite3VdbeAddOp0(v, OP_Expire); - zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt); -- sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); -+ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0); - sqlite3DbFree(db, zStmt); - - iReg = ++pParse->nMem; - sqlite3VdbeLoadString(v, iReg, pTab->zName); - sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg); -- } -- -- /* If we are rereading the sqlite_schema table create the in-memory -- ** record of the table. The xConnect() method is not called until -- ** the first time the virtual table is used in an SQL statement. This -- ** allows a schema that contains virtual tables to be loaded before -- ** the required virtual table implementations are registered. */ -- else { -+ }else{ -+ /* If we are rereading the sqlite_schema table create the in-memory -+ ** record of the table. */ - Table *pOld; - Schema *pSchema = pTab->pSchema; - const char *zName = pTab->zName; -- assert( sqlite3SchemaMutexHeld(db, 0, pSchema) ); -+ assert( zName!=0 ); -+ sqlite3MarkAllShadowTablesOf(db, pTab); - pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab); - if( pOld ){ - sqlite3OomFault(db); -@@ -140134,13 +158117,16 @@ static int vtabCallConstructor( - VtabCtx sCtx; - VTable *pVTable; - int rc; -- const char *const*azArg = (const char *const*)pTab->azModuleArg; -- int nArg = pTab->nModuleArg; -+ const char *const*azArg; -+ int nArg = pTab->u.vtab.nArg; - char *zErr = 0; - char *zModuleName; - int iDb; - VtabCtx *pCtx; - -+ assert( IsVirtual(pTab) ); -+ azArg = (const char *const*)pTab->u.vtab.azArg; -+ - /* Check that the virtual-table is not already being initialized */ - for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){ - if( pCtx->pTab==pTab ){ -@@ -140167,7 +158153,7 @@ static int vtabCallConstructor( - pVTable->eVtabRisk = SQLITE_VTABRISK_Normal; - - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); -- pTab->azModuleArg[1] = db->aDb[iDb].zDbSName; -+ pTab->u.vtab.azArg[1] = db->aDb[iDb].zDbSName; - - /* Invoke the virtual table constructor */ - assert( &db->pVtabCtx ); -@@ -140177,7 +158163,11 @@ static int vtabCallConstructor( - sCtx.pPrior = db->pVtabCtx; - sCtx.bDeclared = 0; - db->pVtabCtx = &sCtx; -+ pTab->nTabRef++; - rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); -+ assert( pTab!=0 ); -+ assert( pTab->nTabRef>1 || rc!=SQLITE_OK ); -+ sqlite3DeleteTable(db, pTab); - db->pVtabCtx = sCtx.pPrior; - if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); - assert( sCtx.pTab==pTab ); -@@ -140199,19 +158189,19 @@ static int vtabCallConstructor( - pVTable->nRef = 1; - if( sCtx.bDeclared==0 ){ - const char *zFormat = "vtable constructor did not declare schema: %s"; -- *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); -+ *pzErr = sqlite3MPrintf(db, zFormat, zModuleName); - sqlite3VtabUnlock(pVTable); - rc = SQLITE_ERROR; - }else{ - int iCol; - u16 oooHidden = 0; - /* If everything went according to plan, link the new VTable structure -- ** into the linked list headed by pTab->pVTable. Then loop through the -+ ** into the linked list headed by pTab->u.vtab.p. Then loop through the - ** columns of the table to see if any of them contain the token "hidden". - ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from - ** the type string. */ -- pVTable->pNext = pTab->pVTable; -- pTab->pVTable = pVTable; -+ pVTable->pNext = pTab->u.vtab.p; -+ pTab->u.vtab.p = pVTable; - - for(iCol=0; iColnCol; iCol++){ - char *zType = sqlite3ColumnType(&pTab->aCol[iCol], ""); -@@ -140237,6 +158227,7 @@ static int vtabCallConstructor( - zType[i-1] = '\0'; - } - pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN; -+ pTab->tabFlags |= TF_HasHidden; - oooHidden = TF_OOOHidden; - }else{ - pTab->tabFlags |= oooHidden; -@@ -140263,16 +158254,17 @@ SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ - int rc; - - assert( pTab ); -- if( !IsVirtual(pTab) || sqlite3GetVTable(db, pTab) ){ -+ assert( IsVirtual(pTab) ); -+ if( sqlite3GetVTable(db, pTab) ){ - return SQLITE_OK; - } - - /* Locate the required virtual table module */ -- zMod = pTab->azModuleArg[0]; -+ zMod = pTab->u.vtab.azArg[0]; - pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); - - if( !pMod ){ -- const char *zModule = pTab->azModuleArg[0]; -+ const char *zModule = pTab->u.vtab.azArg[0]; - sqlite3ErrorMsg(pParse, "no such module: %s", zModule); - rc = SQLITE_ERROR; - }else{ -@@ -140335,10 +158327,10 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, - const char *zMod; - - pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); -- assert( pTab && IsVirtual(pTab) && !pTab->pVTable ); -+ assert( pTab && IsVirtual(pTab) && !pTab->u.vtab.p ); - - /* Locate the required virtual table module */ -- zMod = pTab->azModuleArg[0]; -+ zMod = pTab->u.vtab.azArg[0]; - pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); - - /* If the module has been registered and includes a Create method, -@@ -140373,39 +158365,67 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ - VtabCtx *pCtx; - int rc = SQLITE_OK; - Table *pTab; -- char *zErr = 0; - Parse sParse; -+ int initBusy; -+ int i; -+ const unsigned char *z; -+ static const u8 aKeyword[] = { TK_CREATE, TK_TABLE, 0 }; - - #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ - return SQLITE_MISUSE_BKPT; - } - #endif -+ -+ /* Verify that the first two keywords in the CREATE TABLE statement -+ ** really are "CREATE" and "TABLE". If this is not the case, then -+ ** sqlite3_declare_vtab() is being misused. -+ */ -+ z = (const unsigned char*)zCreateTable; -+ for(i=0; aKeyword[i]; i++){ -+ int tokenType = 0; -+ do{ -+ z += sqlite3GetToken(z, &tokenType); -+ }while( tokenType==TK_SPACE || tokenType==TK_COMMENT ); -+ if( tokenType!=aKeyword[i] ){ -+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); -+ return SQLITE_ERROR; -+ } -+ } -+ - sqlite3_mutex_enter(db->mutex); - pCtx = db->pVtabCtx; - if( !pCtx || pCtx->bDeclared ){ -- sqlite3Error(db, SQLITE_MISUSE); -+ sqlite3Error(db, SQLITE_MISUSE_BKPT); - sqlite3_mutex_leave(db->mutex); - return SQLITE_MISUSE_BKPT; - } -+ - pTab = pCtx->pTab; - assert( IsVirtual(pTab) ); - -- memset(&sParse, 0, sizeof(sParse)); -+ sqlite3ParseObjectInit(&sParse, db); - sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; -- sParse.db = db; -+ sParse.disableTriggers = 1; -+ /* We should never be able to reach this point while loading the -+ ** schema. Nevertheless, defend against that (turn off db->init.busy) -+ ** in case a bug arises. */ -+ assert( db->init.busy==0 ); -+ initBusy = db->init.busy; -+ db->init.busy = 0; - sParse.nQueryLoop = 1; -- if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) -- && sParse.pNewTable -- && !db->mallocFailed -- && !sParse.pNewTable->pSelect -- && !IsVirtual(sParse.pNewTable) -- ){ -+ if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) ){ -+ assert( sParse.pNewTable!=0 ); -+ assert( !db->mallocFailed ); -+ assert( IsOrdinaryTable(sParse.pNewTable) ); -+ assert( sParse.zErrMsg==0 ); - if( !pTab->aCol ){ - Table *pNew = sParse.pNewTable; - Index *pIdx; - pTab->aCol = pNew->aCol; -- pTab->nCol = pNew->nCol; -+ assert( IsOrdinaryTable(pNew) ); -+ sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); -+ pTab->nNVCol = pTab->nCol = pNew->nCol; - pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); - pNew->nCol = 0; - pNew->aCol = 0; -@@ -140429,8 +158449,9 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ - } - pCtx->bDeclared = 1; - }else{ -- sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); -- sqlite3DbFree(db, zErr); -+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, -+ (sParse.zErrMsg ? "%s" : 0), sParse.zErrMsg); -+ sqlite3DbFree(db, sParse.zErrMsg); - rc = SQLITE_ERROR; - } - sParse.eParseMode = PARSE_MODE_NORMAL; -@@ -140439,7 +158460,8 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ - sqlite3VdbeFinalize(sParse.pVdbe); - } - sqlite3DeleteTable(db, sParse.pNewTable); -- sqlite3ParserReset(&sParse); -+ sqlite3ParseObjectReset(&sParse); -+ db->init.busy = initBusy; - - assert( (rc&0xff)==rc ); - rc = sqlite3ApiExit(db, rc); -@@ -140459,10 +158481,13 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab - Table *pTab; - - pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName); -- if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){ -+ if( ALWAYS(pTab!=0) -+ && ALWAYS(IsVirtual(pTab)) -+ && ALWAYS(pTab->u.vtab.p!=0) -+ ){ - VTable *p; - int (*xDestroy)(sqlite3_vtab *); -- for(p=pTab->pVTable; p; p=p->pNext){ -+ for(p=pTab->u.vtab.p; p; p=p->pNext){ - assert( p->pVtab ); - if( p->pVtab->nRef>0 ){ - return SQLITE_LOCKED; -@@ -140476,9 +158501,9 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab - rc = xDestroy(p->pVtab); - /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ - if( rc==SQLITE_OK ){ -- assert( pTab->pVTable==p && p->pNext==0 ); -+ assert( pTab->u.vtab.p==p && p->pNext==0 ); - p->pVtab = 0; -- pTab->pVTable = 0; -+ pTab->u.vtab.p = 0; - sqlite3VtabUnlock(p); - } - sqlite3DeleteTable(db, pTab); -@@ -140653,7 +158678,10 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ - break; - } - if( xMethod && pVTab->iSavepoint>iSavepoint ){ -+ u64 savedFlags = (db->flags & SQLITE_Defensive); -+ db->flags &= ~(u64)SQLITE_Defensive; - rc = xMethod(pVTab->pVtab, iSavepoint); -+ db->flags |= savedFlags; - } - sqlite3VtabUnlock(pVTab); - } -@@ -140692,8 +158720,9 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction( - /* Check to see the left operand is a column in a virtual table */ - if( NEVER(pExpr==0) ) return pDef; - if( pExpr->op!=TK_COLUMN ) return pDef; -+ assert( ExprUseYTab(pExpr) ); - pTab = pExpr->y.pTab; -- if( pTab==0 ) return pDef; -+ if( NEVER(pTab==0) ) return pDef; - if( !IsVirtual(pTab) ) return pDef; - pVtab = sqlite3GetVTable(db, pTab)->pVtab; - assert( pVtab!=0 ); -@@ -140766,12 +158795,13 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ - /* - ** Check to see if virtual table module pMod can be have an eponymous - ** virtual table instance. If it can, create one if one does not already --** exist. Return non-zero if the eponymous virtual table instance exists --** when this routine returns, and return zero if it does not exist. -+** exist. Return non-zero if either the eponymous virtual table instance -+** exists when this routine returns or if an attempt to create it failed -+** and an error message was left in pParse. - ** - ** An eponymous virtual table instance is one that is named after its - ** module, and more importantly, does not require a CREATE VIRTUAL TABLE --** statement in order to come into existance. Eponymous virtual table -+** statement in order to come into existence. Eponymous virtual table - ** instances always exist. They cannot be DROP-ed. - ** - ** Any virtual table module for which xConnect and xCreate are the same -@@ -140794,9 +158824,11 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ - } - pMod->pEpoTab = pTab; - pTab->nTabRef = 1; -+ pTab->eTabType = TABTYP_VTAB; - pTab->pSchema = db->aDb[0].pSchema; -- assert( pTab->nModuleArg==0 ); -+ assert( pTab->u.vtab.nArg==0 ); - pTab->iPKey = -1; -+ pTab->tabFlags |= TF_Eponymous; - addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); - addModuleArgument(pParse, pTab, 0); - addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); -@@ -140805,7 +158837,6 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ - sqlite3ErrorMsg(pParse, "%s", zErr); - sqlite3DbFree(db, zErr); - sqlite3VtabEponymousTableClear(db, pMod); -- return 0; - } - return 1; - } -@@ -140879,6 +158910,10 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ - p->pVTable->eVtabRisk = SQLITE_VTABRISK_High; - break; - } -+ case SQLITE_VTAB_USES_ALL_SCHEMAS: { -+ p->pVTable->bAllSchemas = 1; -+ break; -+ } - default: { - rc = SQLITE_MISUSE_BKPT; - break; -@@ -140937,19 +158972,6 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ - #ifndef SQLITE_WHEREINT_H - #define SQLITE_WHEREINT_H - --/* --** Trace output macros --*/ --#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) --/***/ extern int sqlite3WhereTrace; --#endif --#if defined(SQLITE_DEBUG) \ -- && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) --# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X --# define WHERETRACE_ENABLED 1 --#else --# define WHERETRACE(K,X) --#endif - - /* Forward references - */ -@@ -140965,6 +158987,28 @@ typedef struct WhereLoopBuilder WhereLoopBuilder; - typedef struct WhereScan WhereScan; - typedef struct WhereOrCost WhereOrCost; - typedef struct WhereOrSet WhereOrSet; -+typedef struct WhereMemBlock WhereMemBlock; -+typedef struct WhereRightJoin WhereRightJoin; -+ -+/* -+** This object is a header on a block of allocated memory that will be -+** automatically freed when its WInfo object is destructed. -+*/ -+struct WhereMemBlock { -+ WhereMemBlock *pNext; /* Next block in the chain */ -+ u64 sz; /* Bytes of space */ -+}; -+ -+/* -+** Extra information attached to a WhereLevel that is a RIGHT JOIN. -+*/ -+struct WhereRightJoin { -+ int iMatch; /* Cursor used to determine prior matched rows */ -+ int regBloom; /* Bloom filter for iRJMatch */ -+ int regReturn; /* Return register for the interior subroutine */ -+ int addrSubrtn; /* Starting address for the interior subroutine */ -+ int endSubrtn; /* The last opcode in the interior subroutine */ -+}; - - /* - ** This object contains information needed to implement a single nested -@@ -140997,6 +159041,8 @@ struct WhereLevel { - u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */ - int addrLikeRep; /* LIKE range processing address */ - #endif -+ int regFilter; /* Bloom filter */ -+ WhereRightJoin *pRJ; /* Extra information for RIGHT JOIN */ - u8 iFrom; /* Which entry in the FROM clause */ - u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ - int p1, p2; /* Operands of the opcode used to end the loop */ -@@ -141007,11 +159053,11 @@ struct WhereLevel { - int iCur; /* The VDBE cursor used by this IN operator */ - int addrInTop; /* Top of the IN loop */ - int iBase; /* Base register of multi-key index record */ -- int nPrefix; /* Number of prior entires in the key */ -+ int nPrefix; /* Number of prior entries in the key */ - u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ - } *aInLoop; /* Information about each nested IN operator */ - } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */ -- Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ -+ Index *pCoveringIdx; /* Possible covering index for WHERE_MULTI_OR */ - } u; - struct WhereLoop *pWLoop; /* The selected WhereLoop object */ - Bitmask notReady; /* FROM entries not usable at this level */ -@@ -141052,13 +159098,17 @@ struct WhereLoop { - u16 nTop; /* Size of TOP vector */ - u16 nDistinctCol; /* Index columns used to sort for DISTINCT */ - Index *pIndex; /* Index used, or NULL */ -+ ExprList *pOrderBy; /* ORDER BY clause if this is really a subquery */ - } btree; - struct { /* Information for virtual tables */ - int idxNum; /* Index number */ -- u8 needFree; /* True if sqlite3_free(idxStr) is needed */ -+ u32 needFree : 1; /* True if sqlite3_free(idxStr) is needed */ -+ u32 bOmitOffset : 1; /* True to let virtual table handle offset */ -+ u32 bIdxNumHex : 1; /* Show idxNum as hex in EXPLAIN QUERY PLAN */ - i8 isOrdered; /* True if satisfies ORDER BY */ - u16 omitMask; /* Terms that may be omitted */ - char *idxStr; /* Index identifier string */ -+ u32 mHandleIn; /* Terms to handle as IN(...) instead of == */ - } vtab; - } u; - u32 wsFlags; /* WHERE_* flags describing the plan */ -@@ -141067,6 +159117,10 @@ struct WhereLoop { - /**** whereLoopXfer() copies fields above ***********************/ - # define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot) - u16 nLSlot; /* Number of slots allocated for aLTerm[] */ -+#ifdef WHERETRACE_ENABLED -+ LogEst rStarDelta; /* Cost delta due to star-schema heuristic. Not -+ ** initialized unless pWInfo->bStarUsed */ -+#endif - WhereTerm **aLTerm; /* WhereTerms used */ - WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */ - WhereTerm *aLTermSpace[3]; /* Initial aLTerm[] space */ -@@ -141115,7 +159169,7 @@ struct WherePath { - Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */ - LogEst nRow; /* Estimated number of rows generated by this path */ - LogEst rCost; /* Total cost of this path */ -- LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */ -+ LogEst rUnsort; /* Total cost of this path ignoring sorting costs */ - i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */ - WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */ - }; -@@ -141181,9 +159235,11 @@ struct WhereTerm { - u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */ - int iParent; /* Disable pWC->a[iParent] when this term disabled */ - int leftCursor; /* Cursor number of X in "X " */ -- int iField; /* Field in (?,?,?) IN (SELECT...) vector */ - union { -- int leftColumn; /* Column number of X in "X " */ -+ struct { -+ int leftColumn; /* Column number of X in "X " */ -+ int iField; /* Field in (?,?,?) IN (SELECT...) vector */ -+ } x; /* Opcode other than OP_OR or OP_AND */ - WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ - WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ - } u; -@@ -141200,12 +159256,8 @@ struct WhereTerm { - #define TERM_COPIED 0x0008 /* Has a child */ - #define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */ - #define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */ --#define TERM_OR_OK 0x0040 /* Used during OR-clause processing */ --#ifdef SQLITE_ENABLE_STAT4 --# define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */ --#else --# define TERM_VNULL 0x0000 /* Disabled if not using stat4 */ --#endif -+#define TERM_OK 0x0040 /* Used during OR-clause processing */ -+#define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */ - #define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */ - #define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */ - #define TERM_LIKE 0x0400 /* The original LIKE operator */ -@@ -141217,6 +159269,7 @@ struct WhereTerm { - #else - # define TERM_HIGHTRUTH 0 /* Only used with STAT4 */ - #endif -+#define TERM_SLICE 0x8000 /* One slice of a row-value/vector comparison */ - - /* - ** An instance of the WhereScan object is used as an iterator for locating -@@ -141227,11 +159280,11 @@ struct WhereScan { - WhereClause *pWC; /* WhereClause currently being scanned */ - const char *zCollName; /* Required collating sequence, if not NULL */ - Expr *pIdxExpr; /* Search for this index expression */ -- char idxaff; /* Must match this affinity, if zCollName!=NULL */ -- unsigned char nEquiv; /* Number of entries in aEquiv[] */ -- unsigned char iEquiv; /* Next unused slot in aEquiv[] */ -- u32 opMask; /* Acceptable operators */ - int k; /* Resume scanning at this->pWC->a[this->k] */ -+ u32 opMask; /* Acceptable operators */ -+ char idxaff; /* Must match this affinity, if zCollName!=NULL */ -+ unsigned char iEquiv; /* Current slot in aiCur[] and aiColumn[] */ -+ unsigned char nEquiv; /* Number of entries in aiCur[] and aiColumn[] */ - int aiCur[11]; /* Cursors in the equivalence class */ - i16 aiColumn[11]; /* Corresponding column number in the eq-class */ - }; -@@ -141255,7 +159308,8 @@ struct WhereClause { - u8 hasOr; /* True if any a[].eOperator is WO_OR */ - int nTerm; /* Number of terms */ - int nSlot; /* Number of entries in a[] */ -- WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ -+ int nBase; /* Number of terms through the last non-Virtual */ -+ WhereTerm *a; /* Each a[] describes a term of the WHERE clause */ - #if defined(SQLITE_SMALL_STACK) - WhereTerm aStatic[1]; /* Initial static space for a[] */ - #else -@@ -141285,7 +159339,7 @@ struct WhereAndInfo { - ** between VDBE cursor numbers and bits of the bitmasks in WhereTerm. - ** - ** The VDBE cursor numbers are small integers contained in --** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE -+** SrcItem.iCursor and Expr.iTable fields. For any given WHERE - ** clause, the cursor numbers might not begin with 0 and they might - ** contain gaps in the numbering sequence. But we want to make maximum - ** use of the bits in our bitmasks. This structure provides a mapping -@@ -141312,11 +159366,6 @@ struct WhereMaskSet { - int ix[BMS]; /* Cursor assigned to each bit */ - }; - --/* --** Initialize a WhereMaskSet object --*/ --#define initMaskSet(P) (P)->n=0 -- - /* - ** This object is a convenience wrapper holding all information needed - ** to construct WhereLoop objects for a particular query. -@@ -141324,7 +159373,6 @@ struct WhereMaskSet { - struct WhereLoopBuilder { - WhereInfo *pWInfo; /* Information about this WHERE */ - WhereClause *pWC; /* WHERE clause terms */ -- ExprList *pOrderBy; /* ORDER BY clause */ - WhereLoop *pNew; /* Template WhereLoop */ - WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ - #ifdef SQLITE_ENABLE_STAT4 -@@ -141362,20 +159410,6 @@ struct WhereLoopBuilder { - # define SQLITE_QUERY_PLANNER_LIMIT_INCR 1000 - #endif - --/* --** Each instance of this object records a change to a single node --** in an expression tree to cause that node to point to a column --** of an index rather than an expression or a virtual column. All --** such transformations need to be undone at the end of WHERE clause --** processing. --*/ --typedef struct WhereExprMod WhereExprMod; --struct WhereExprMod { -- WhereExprMod *pNext; /* Next translation on a list of them all */ -- Expr *pExpr; /* The Expr node that was transformed */ -- Expr orig; /* Original value of the Expr node */ --}; -- - /* - ** The WHERE clause processing routine has two halves. The - ** first part does the start of the WHERE loop and the second -@@ -141391,7 +159425,10 @@ struct WhereInfo { - SrcList *pTabList; /* List of tables in the join */ - ExprList *pOrderBy; /* The ORDER BY clause or NULL */ - ExprList *pResultSet; /* Result set of the query */ -+#if WHERETRACE_ENABLED - Expr *pWhere; /* The complete WHERE clause */ -+#endif -+ Select *pSelect; /* The entire SELECT statement containing WHERE */ - int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ - int iContinue; /* Jump here to continue with next record */ - int iBreak; /* Jump here to break out of the loop */ -@@ -141405,17 +159442,28 @@ struct WhereInfo { - unsigned bDeferredSeek :1; /* Uses OP_DeferredSeek */ - unsigned untestedTerms :1; /* Not all WHERE terms resolved by outer loop */ - unsigned bOrderedInnerLoop:1;/* True if only the inner-most loop is ordered */ -- unsigned sorted :1; /* True if really sorted (not just grouped) */ -+ unsigned sorted :1; /* True if really sorted (not just grouped) */ -+ unsigned bStarDone :1; /* True if check for star-query is complete */ -+ unsigned bStarUsed :1; /* True if star-query heuristic is used */ - LogEst nRowOut; /* Estimated number of output rows */ -+#ifdef WHERETRACE_ENABLED -+ LogEst rTotalCost; /* Total cost of the solution */ -+#endif - int iTop; /* The very beginning of the WHERE loop */ -+ int iEndWhere; /* End of the WHERE clause itself */ - WhereLoop *pLoops; /* List of all WhereLoop objects */ -- WhereExprMod *pExprMods; /* Expression modifications */ -+ WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ - Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ - WhereClause sWC; /* Decomposition of the WHERE clause */ - WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ -- WhereLevel a[1]; /* Information about each nest loop in WHERE */ -+ WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */ - }; - -+/* -+** The size (in bytes) of a WhereInfo object that holds N WhereLevels. -+*/ -+#define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel)) -+ - /* - ** Private interfaces - callable only by other where.c routines. - ** -@@ -141425,7 +159473,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); - #ifdef WHERETRACE_ENABLED - SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC); - SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm); --SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC); -+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC); - #endif - SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm( - WhereClause *pWC, /* The WHERE clause to be searched */ -@@ -141435,6 +159483,8 @@ SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm( - u32 op, /* Mask of WO_xx values describing operator */ - Index *pIdx /* Must be compatible with this index, if not NULL */ - ); -+SQLITE_PRIVATE void *sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte); -+SQLITE_PRIVATE void *sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte); - - /* wherecode.c: */ - #ifndef SQLITE_OMIT_EXPLAIN -@@ -141444,8 +159494,22 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( - WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ - u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ - ); -+SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( -+ const Parse *pParse, /* Parse context */ -+ const WhereInfo *pWInfo, /* WHERE clause */ -+ const WhereLevel *pLevel /* Bloom filter on this level */ -+); -+SQLITE_PRIVATE void sqlite3WhereAddExplainText( -+ Parse *pParse, /* Parse context */ -+ int addr, -+ SrcList *pTabList, /* Table list this loop refers to */ -+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ -+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ -+); - #else - # define sqlite3WhereExplainOneScan(u,v,w,x) 0 -+# define sqlite3WhereExplainBloomFilter(u,v,w) 0 -+# define sqlite3WhereAddExplainText(u,v,w,x,y) - #endif /* SQLITE_OMIT_EXPLAIN */ - #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - SQLITE_PRIVATE void sqlite3WhereAddScanStatus( -@@ -141465,16 +159529,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - WhereLevel *pLevel, /* The current level pointer */ - Bitmask notReady /* Which tables are currently available */ - ); -+SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( -+ WhereInfo *pWInfo, -+ int iLevel, -+ WhereLevel *pLevel -+); - - /* whereexpr.c: */ - SQLITE_PRIVATE void sqlite3WhereClauseInit(WhereClause*,WhereInfo*); - SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause*); - SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause*,Expr*,u8); -+SQLITE_PRIVATE void sqlite3WhereAddLimit(WhereClause*, Select*); - SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*); - SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*); - SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); - SQLITE_PRIVATE void sqlite3WhereExprAnalyze(SrcList*, WhereClause*); --SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*); -+SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); - - - -@@ -141506,8 +159576,9 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC - #define WO_AND 0x0400 /* Two or more AND-connected terms */ - #define WO_EQUIV 0x0800 /* Of the form A==B, both columns */ - #define WO_NOOP 0x1000 /* This term does not restrict search space */ -+#define WO_ROWVAL 0x2000 /* A row-value term */ - --#define WO_ALL 0x1fff /* Mask of all possible WO_* values */ -+#define WO_ALL 0x3fff /* Mask of all possible WO_* values */ - #define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */ - - /* -@@ -141536,6 +159607,14 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC - #define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */ - #define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */ - #define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */ -+#define WHERE_IN_SEEKSCAN 0x00100000 /* Seek-scan optimization for IN */ -+#define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */ -+#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ -+#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ -+#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ -+#define WHERE_COROUTINE 0x02000000 /* Implemented by co-routine. -+ ** NB: False-negatives are possible */ -+#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */ - - #endif /* !defined(SQLITE_WHEREINT_H) */ - -@@ -141551,7 +159630,7 @@ static const char *explainIndexColumnName(Index *pIdx, int i){ - i = pIdx->aiColumn[i]; - if( i==XN_EXPR ) return ""; - if( i==XN_ROWID ) return "rowid"; -- return pIdx->pTable->aCol[i].zName; -+ return pIdx->pTable->aCol[i].zCnName; - } - - /* -@@ -141632,54 +159711,46 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){ - } - - /* --** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN --** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was --** defined at compile-time. If it is not a no-op, a single OP_Explain opcode --** is added to the output to describe the table scan strategy in pLevel. --** --** If an OP_Explain opcode is added to the VM, its address is returned. --** Otherwise, if no OP_Explain is coded, zero is returned. -+** This function sets the P4 value of an existing OP_Explain opcode to -+** text describing the loop in pLevel. If the OP_Explain opcode already has -+** a P4 value, it is freed before it is overwritten. - */ --SQLITE_PRIVATE int sqlite3WhereExplainOneScan( -+SQLITE_PRIVATE void sqlite3WhereAddExplainText( - Parse *pParse, /* Parse context */ -+ int addr, /* Address of OP_Explain opcode */ - SrcList *pTabList, /* Table list this loop refers to */ - WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ - u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ - ){ -- int ret = 0; --#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS) -- if( sqlite3ParseToplevel(pParse)->explain==2 ) -+#if !defined(SQLITE_DEBUG) -+ if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) - #endif - { -- struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; -- Vdbe *v = pParse->pVdbe; /* VM being constructed */ -+ VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr); -+ -+ SrcItem *pItem = &pTabList->a[pLevel->iFrom]; - sqlite3 *db = pParse->db; /* Database handle */ - int isSearch; /* True for a SEARCH. False for SCAN. */ - WhereLoop *pLoop; /* The controlling WhereLoop object */ - u32 flags; /* Flags that describe this loop */ -+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) - char *zMsg; /* Text to add to EQP output */ -+#endif - StrAccum str; /* EQP output string */ - char zBuf[100]; /* Initial space for EQP output string */ - -+ if( db->mallocFailed ) return; -+ - pLoop = pLevel->pWLoop; - flags = pLoop->wsFlags; -- if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0; - - isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 - || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) - || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); - - sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); -- sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN"); -- if( pItem->pSelect ){ -- sqlite3_str_appendf(&str, " SUBQUERY %u", pItem->pSelect->selId); -- }else{ -- sqlite3_str_appendf(&str, " TABLE %s", pItem->zName); -- } -- -- if( pItem->zAlias ){ -- sqlite3_str_appendf(&str, " AS %s", pItem->zAlias); -- } -+ str.printfFlags = SQLITE_PRINTF_INTERNAL; -+ sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem); - if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){ - const char *zFmt = 0; - Index *pIdx; -@@ -141687,7 +159758,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( - assert( pLoop->u.btree.pIndex!=0 ); - pIdx = pLoop->u.btree.pIndex; - assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) ); -- if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){ -+ if( !HasRowid(pItem->pSTab) && IsPrimaryKeyIndex(pIdx) ){ - if( isSearch ){ - zFmt = "PRIMARY KEY"; - } -@@ -141695,7 +159766,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( - zFmt = "AUTOMATIC PARTIAL COVERING INDEX"; - }else if( flags & WHERE_AUTO_INDEX ){ - zFmt = "AUTOMATIC COVERING INDEX"; -- }else if( flags & WHERE_IDX_ONLY ){ -+ }else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ - zFmt = "COVERING INDEX %s"; - }else{ - zFmt = "INDEX %s"; -@@ -141706,26 +159777,39 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( - explainIndexRange(&str, pLoop); - } - }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){ -- const char *zRangeOp; -+ char cRangeOp; -+#if 0 /* Better output, but breaks many tests */ -+ const Table *pTab = pItem->pTab; -+ const char *zRowid = pTab->iPKey>=0 ? pTab->aCol[pTab->iPKey].zCnName: -+ "rowid"; -+#else -+ const char *zRowid = "rowid"; -+#endif -+ sqlite3_str_appendf(&str, " USING INTEGER PRIMARY KEY (%s", zRowid); - if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){ -- zRangeOp = "="; -+ cRangeOp = '='; - }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ -- zRangeOp = ">? AND rowid<"; -+ sqlite3_str_appendf(&str, ">? AND %s", zRowid); -+ cRangeOp = '<'; - }else if( flags&WHERE_BTM_LIMIT ){ -- zRangeOp = ">"; -+ cRangeOp = '>'; - }else{ - assert( flags&WHERE_TOP_LIMIT); -- zRangeOp = "<"; -+ cRangeOp = '<'; - } -- sqlite3_str_appendf(&str, -- " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp); -+ sqlite3_str_appendf(&str, "%c?)", cRangeOp); - } - #ifndef SQLITE_OMIT_VIRTUALTABLE - else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ -- sqlite3_str_appendf(&str, " VIRTUAL TABLE INDEX %d:%s", -+ sqlite3_str_appendall(&str, " VIRTUAL TABLE INDEX "); -+ sqlite3_str_appendf(&str, -+ pLoop->u.vtab.bIdxNumHex ? "0x%x:%s" : "%d:%s", - pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr); - } - #endif -+ if( pItem->fg.jointype & JT_LEFT ){ -+ sqlite3_str_appendf(&str, " LEFT-JOIN"); -+ } - #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS - if( pLoop->nOut>=10 ){ - sqlite3_str_appendf(&str, " (~%llu rows)", -@@ -141734,13 +159818,105 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( - sqlite3_str_append(&str, " (~1 row)", 9); - } - #endif -+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) - zMsg = sqlite3StrAccumFinish(&str); - sqlite3ExplainBreakpoint("",zMsg); -- ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), -- pParse->addrExplain, 0, zMsg,P4_DYNAMIC); -+#endif -+ -+ assert( pOp->opcode==OP_Explain ); -+ assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 ); -+ sqlite3DbFree(db, pOp->p4.z); -+ pOp->p4type = P4_DYNAMIC; -+ pOp->p4.z = sqlite3StrAccumFinish(&str); -+ } -+} -+ -+ -+/* -+** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN -+** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG -+** was defined at compile-time. If it is not a no-op, a single OP_Explain -+** opcode is added to the output to describe the table scan strategy in pLevel. -+** -+** If an OP_Explain opcode is added to the VM, its address is returned. -+** Otherwise, if no OP_Explain is coded, zero is returned. -+*/ -+SQLITE_PRIVATE int sqlite3WhereExplainOneScan( -+ Parse *pParse, /* Parse context */ -+ SrcList *pTabList, /* Table list this loop refers to */ -+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ -+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ -+){ -+ int ret = 0; -+#if !defined(SQLITE_DEBUG) -+ if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) -+#endif -+ { -+ if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0 -+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 -+ ){ -+ Vdbe *v = pParse->pVdbe; -+ int addr = sqlite3VdbeCurrentAddr(v); -+ ret = sqlite3VdbeAddOp3( -+ v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun -+ ); -+ sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags); -+ } - } - return ret; - } -+ -+/* -+** Add a single OP_Explain opcode that describes a Bloom filter. -+** -+** Or if not processing EXPLAIN QUERY PLAN and not in a SQLITE_DEBUG and/or -+** SQLITE_ENABLE_STMT_SCANSTATUS build, then OP_Explain opcodes are not -+** required and this routine is a no-op. -+** -+** If an OP_Explain opcode is added to the VM, its address is returned. -+** Otherwise, if no OP_Explain is coded, zero is returned. -+*/ -+SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( -+ const Parse *pParse, /* Parse context */ -+ const WhereInfo *pWInfo, /* WHERE clause */ -+ const WhereLevel *pLevel /* Bloom filter on this level */ -+){ -+ int ret = 0; -+ SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom]; -+ Vdbe *v = pParse->pVdbe; /* VM being constructed */ -+ sqlite3 *db = pParse->db; /* Database handle */ -+ char *zMsg; /* Text to add to EQP output */ -+ int i; /* Loop counter */ -+ WhereLoop *pLoop; /* The where loop */ -+ StrAccum str; /* EQP output string */ -+ char zBuf[100]; /* Initial space for EQP output string */ -+ -+ sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); -+ str.printfFlags = SQLITE_PRINTF_INTERNAL; -+ sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem); -+ pLoop = pLevel->pWLoop; -+ if( pLoop->wsFlags & WHERE_IPK ){ -+ const Table *pTab = pItem->pSTab; -+ if( pTab->iPKey>=0 ){ -+ sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName); -+ }else{ -+ sqlite3_str_appendf(&str, "rowid=?"); -+ } -+ }else{ -+ for(i=pLoop->nSkip; iu.btree.nEq; i++){ -+ const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i); -+ if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5); -+ sqlite3_str_appendf(&str, "%s=?", z); -+ } -+ } -+ sqlite3_str_append(&str, ")", 1); -+ zMsg = sqlite3StrAccumFinish(&str); -+ ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), -+ pParse->addrExplain, 0, zMsg,P4_DYNAMIC); -+ -+ sqlite3VdbeScanStatus(v, sqlite3VdbeCurrentAddr(v)-1, 0, 0, 0, 0); -+ return ret; -+} - #endif /* SQLITE_OMIT_EXPLAIN */ - - #ifdef SQLITE_ENABLE_STMT_SCANSTATUS -@@ -141759,16 +159935,40 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus( - WhereLevel *pLvl, /* Level to add scanstatus() entry for */ - int addrExplain /* Address of OP_Explain (or 0) */ - ){ -- const char *zObj = 0; -- WhereLoop *pLoop = pLvl->pWLoop; -- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ -- zObj = pLoop->u.btree.pIndex->zName; -- }else{ -- zObj = pSrclist->a[pLvl->iFrom].zName; -+ if( IS_STMT_SCANSTATUS( sqlite3VdbeDb(v) ) ){ -+ const char *zObj = 0; -+ WhereLoop *pLoop = pLvl->pWLoop; -+ int wsFlags = pLoop->wsFlags; -+ int viaCoroutine = 0; -+ -+ if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ -+ zObj = pLoop->u.btree.pIndex->zName; -+ }else{ -+ zObj = pSrclist->a[pLvl->iFrom].zName; -+ viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine; -+ } -+ sqlite3VdbeScanStatus( -+ v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj -+ ); -+ -+ if( viaCoroutine==0 ){ -+ if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){ -+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur); -+ } -+ if( wsFlags & WHERE_INDEXED ){ -+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); -+ } -+ }else{ -+ int addr; -+ VdbeOp *pOp; -+ assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); -+ addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; -+ pOp = sqlite3VdbeGetOp(v, addr-1); -+ assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); -+ assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); -+ sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); -+ } - } -- sqlite3VdbeScanStatus( -- v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj -- ); - } - #endif - -@@ -141819,7 +160019,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ - int nLoop = 0; - assert( pTerm!=0 ); - while( (pTerm->wtFlags & TERM_CODED)==0 -- && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) -+ && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_OuterON)) - && (pLevel->notReady & pTerm->prereqAll)==0 - ){ - if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){ -@@ -141827,6 +160027,12 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ - }else{ - pTerm->wtFlags |= TERM_CODED; - } -+#ifdef WHERETRACE_ENABLED -+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){ -+ sqlite3DebugPrintf("DISABLE-"); -+ sqlite3WhereTermPrint(pTerm, (int)(pTerm - (pTerm->pWC->a))); -+ } -+#endif - if( pTerm->iParent<0 ) break; - pTerm = &pTerm->pWC->a[pTerm->iParent]; - assert( pTerm!=0 ); -@@ -141900,11 +160106,44 @@ static void updateRangeAffinityStr( - } - } - -+/* -+** The pOrderBy->a[].u.x.iOrderByCol values might be incorrect because -+** columns might have been rearranged in the result set. This routine -+** fixes them up. -+** -+** pEList is the new result set. The pEList->a[].u.x.iOrderByCol values -+** contain the *old* locations of each expression. This is a temporary -+** use of u.x.iOrderByCol, not its intended use. The caller must reset -+** u.x.iOrderByCol back to zero for all entries in pEList before the -+** caller returns. -+** -+** This routine changes pOrderBy->a[].u.x.iOrderByCol values from -+** pEList->a[N].u.x.iOrderByCol into N+1. (The "+1" is because of the 1-based -+** indexing used by iOrderByCol.) Or if no match, iOrderByCol is set to zero. -+*/ -+static void adjustOrderByCol(ExprList *pOrderBy, ExprList *pEList){ -+ int i, j; -+ if( pOrderBy==0 ) return; -+ for(i=0; inExpr; i++){ -+ int t = pOrderBy->a[i].u.x.iOrderByCol; -+ if( t==0 ) continue; -+ for(j=0; jnExpr; j++){ -+ if( pEList->a[j].u.x.iOrderByCol==t ){ -+ pOrderBy->a[i].u.x.iOrderByCol = j+1; -+ break; -+ } -+ } -+ if( j>=pEList->nExpr ){ -+ pOrderBy->a[i].u.x.iOrderByCol = 0; -+ } -+ } -+} -+ - - /* - ** pX is an expression of the form: (vector) IN (SELECT ...) - ** In other words, it is a vector IN operator with a SELECT clause on the --** LHS. But not all terms in the vector are indexable and the terms might -+** RHS. But not all terms in the vector are indexable and the terms might - ** not be in the correct order for indexing. - ** - ** This routine makes a copy of the input pX expression and then adjusts -@@ -141937,66 +160176,214 @@ static Expr *removeUnindexableInClauseTerms( - Expr *pX /* The IN expression to be reduced */ - ){ - sqlite3 *db = pParse->db; -+ Select *pSelect; /* Pointer to the SELECT on the RHS */ - Expr *pNew; - pNew = sqlite3ExprDup(db, pX, 0); - if( db->mallocFailed==0 ){ -- ExprList *pOrigRhs = pNew->x.pSelect->pEList; /* Original unmodified RHS */ -- ExprList *pOrigLhs = pNew->pLeft->x.pList; /* Original unmodified LHS */ -- ExprList *pRhs = 0; /* New RHS after modifications */ -- ExprList *pLhs = 0; /* New LHS after mods */ -- int i; /* Loop counter */ -- Select *pSelect; /* Pointer to the SELECT on the RHS */ -+ for(pSelect=pNew->x.pSelect; pSelect; pSelect=pSelect->pPrior){ -+ ExprList *pOrigRhs; /* Original unmodified RHS */ -+ ExprList *pOrigLhs = 0; /* Original unmodified LHS */ -+ ExprList *pRhs = 0; /* New RHS after modifications */ -+ ExprList *pLhs = 0; /* New LHS after mods */ -+ int i; /* Loop counter */ -+ -+ assert( ExprUseXSelect(pNew) ); -+ pOrigRhs = pSelect->pEList; -+ assert( pNew->pLeft!=0 ); -+ assert( ExprUseXList(pNew->pLeft) ); -+ if( pSelect==pNew->x.pSelect ){ -+ pOrigLhs = pNew->pLeft->x.pList; -+ } -+ for(i=iEq; inLTerm; i++){ -+ if( pLoop->aLTerm[i]->pExpr==pX ){ -+ int iField; -+ assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 ); -+ iField = pLoop->aLTerm[i]->u.x.iField - 1; -+ if( NEVER(pOrigRhs->a[iField].pExpr==0) ){ -+ continue; /* Duplicate PK column */ -+ } -+ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); -+ pOrigRhs->a[iField].pExpr = 0; -+ if( pRhs ) pRhs->a[pRhs->nExpr-1].u.x.iOrderByCol = iField+1; -+ if( pOrigLhs ){ -+ assert( pOrigLhs->a[iField].pExpr!=0 ); -+ pLhs = sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr); -+ pOrigLhs->a[iField].pExpr = 0; -+ } -+ } -+ } -+ sqlite3ExprListDelete(db, pOrigRhs); -+ if( pOrigLhs ){ -+ sqlite3ExprListDelete(db, pOrigLhs); -+ pNew->pLeft->x.pList = pLhs; -+ } -+ pSelect->pEList = pRhs; -+ pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */ -+ if( pLhs && pLhs->nExpr==1 ){ -+ /* Take care here not to generate a TK_VECTOR containing only a -+ ** single value. Since the parser never creates such a vector, some -+ ** of the subroutines do not handle this case. */ -+ Expr *p = pLhs->a[0].pExpr; -+ pLhs->a[0].pExpr = 0; -+ sqlite3ExprDelete(db, pNew->pLeft); -+ pNew->pLeft = p; -+ } - -- for(i=iEq; inLTerm; i++){ -- if( pLoop->aLTerm[i]->pExpr==pX ){ -- int iField = pLoop->aLTerm[i]->iField - 1; -- if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ -- pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); -- pOrigRhs->a[iField].pExpr = 0; -- assert( pOrigLhs->a[iField].pExpr!=0 ); -- pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr); -- pOrigLhs->a[iField].pExpr = 0; -- } -- } -- sqlite3ExprListDelete(db, pOrigRhs); -- sqlite3ExprListDelete(db, pOrigLhs); -- pNew->pLeft->x.pList = pLhs; -- pNew->x.pSelect->pEList = pRhs; -- if( pLhs && pLhs->nExpr==1 ){ -- /* Take care here not to generate a TK_VECTOR containing only a -- ** single value. Since the parser never creates such a vector, some -- ** of the subroutines do not handle this case. */ -- Expr *p = pLhs->a[0].pExpr; -- pLhs->a[0].pExpr = 0; -- sqlite3ExprDelete(db, pNew->pLeft); -- pNew->pLeft = p; -- } -- pSelect = pNew->x.pSelect; -- if( pSelect->pOrderBy ){ -- /* If the SELECT statement has an ORDER BY clause, zero the -- ** iOrderByCol variables. These are set to non-zero when an -- ** ORDER BY term exactly matches one of the terms of the -- ** result-set. Since the result-set of the SELECT statement may -- ** have been modified or reordered, these variables are no longer -- ** set correctly. Since setting them is just an optimization, -- ** it's easiest just to zero them here. */ -- ExprList *pOrderBy = pSelect->pOrderBy; -- for(i=0; inExpr; i++){ -- pOrderBy->a[i].u.x.iOrderByCol = 0; -+ /* If either the ORDER BY clause or the GROUP BY clause contains -+ ** references to result-set columns, those references might now be -+ ** obsolete. So fix them up. -+ */ -+ assert( pRhs!=0 || db->mallocFailed ); -+ if( pRhs ){ -+ adjustOrderByCol(pSelect->pOrderBy, pRhs); -+ adjustOrderByCol(pSelect->pGroupBy, pRhs); -+ for(i=0; inExpr; i++) pRhs->a[i].u.x.iOrderByCol = 0; - } -- } - - #if 0 -- printf("For indexing, change the IN expr:\n"); -- sqlite3TreeViewExpr(0, pX, 0); -- printf("Into:\n"); -- sqlite3TreeViewExpr(0, pNew, 0); -+ printf("For indexing, change the IN expr:\n"); -+ sqlite3TreeViewExpr(0, pX, 0); -+ printf("Into:\n"); -+ sqlite3TreeViewExpr(0, pNew, 0); - #endif -+ } - } - return pNew; - } - - -+#ifndef SQLITE_OMIT_SUBQUERY -+/* -+** Generate code for a single X IN (....) term of the WHERE clause. -+** -+** This is a special-case of codeEqualityTerm() that works for IN operators -+** only. It is broken out into a subroutine because this case is -+** uncommon and by splitting it off into a subroutine, the common case -+** runs faster. -+** -+** The current value for the constraint is left in register iTarget. -+** This routine sets up a loop that will iterate over all values of X. -+*/ -+static SQLITE_NOINLINE void codeINTerm( -+ Parse *pParse, /* The parsing context */ -+ WhereTerm *pTerm, /* The term of the WHERE clause to be coded */ -+ WhereLevel *pLevel, /* The level of the FROM clause we are working on */ -+ int iEq, /* Index of the equality term within this level */ -+ int bRev, /* True for reverse-order IN operations */ -+ int iTarget /* Attempt to leave results in this register */ -+){ -+ Expr *pX = pTerm->pExpr; -+ int eType = IN_INDEX_NOOP; -+ int iTab; -+ struct InLoop *pIn; -+ WhereLoop *pLoop = pLevel->pWLoop; -+ Vdbe *v = pParse->pVdbe; -+ int i; -+ int nEq = 0; -+ int *aiMap = 0; -+ -+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 -+ && pLoop->u.btree.pIndex!=0 -+ && pLoop->u.btree.pIndex->aSortOrder[iEq] -+ ){ -+ testcase( iEq==0 ); -+ testcase( bRev ); -+ bRev = !bRev; -+ } -+ assert( pX->op==TK_IN ); -+ -+ for(i=0; iaLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){ -+ disableTerm(pLevel, pTerm); -+ return; -+ } -+ } -+ for(i=iEq; inLTerm; i++){ -+ assert( pLoop->aLTerm[i]!=0 ); -+ if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; -+ } -+ -+ iTab = 0; -+ if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){ -+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); -+ }else{ -+ sqlite3 *db = pParse->db; -+ Expr *pXMod = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); -+ if( !db->mallocFailed ){ -+ aiMap = (int*)sqlite3DbMallocZero(db, sizeof(int)*nEq); -+ eType = sqlite3FindInIndex(pParse, pXMod, IN_INDEX_LOOP, 0, aiMap, &iTab); -+ } -+ sqlite3ExprDelete(db, pXMod); -+ } -+ -+ if( eType==IN_INDEX_INDEX_DESC ){ -+ testcase( bRev ); -+ bRev = !bRev; -+ } -+ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); -+ VdbeCoverageIf(v, bRev); -+ VdbeCoverageIf(v, !bRev); -+ -+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); -+ pLoop->wsFlags |= WHERE_IN_ABLE; -+ if( pLevel->u.in.nIn==0 ){ -+ pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); -+ } -+ if( iEq>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 ){ -+ pLoop->wsFlags |= WHERE_IN_EARLYOUT; -+ } -+ -+ i = pLevel->u.in.nIn; -+ pLevel->u.in.nIn += nEq; -+ pLevel->u.in.aInLoop = -+ sqlite3WhereRealloc(pTerm->pWC->pWInfo, -+ pLevel->u.in.aInLoop, -+ sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); -+ pIn = pLevel->u.in.aInLoop; -+ if( pIn ){ -+ int iMap = 0; /* Index in aiMap[] */ -+ pIn += i; -+ for(i=iEq; inLTerm; i++){ -+ if( pLoop->aLTerm[i]->pExpr==pX ){ -+ int iOut = iTarget + i - iEq; -+ if( eType==IN_INDEX_ROWID ){ -+ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); -+ }else{ -+ int iCol = aiMap ? aiMap[iMap++] : 0; -+ pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut); -+ } -+ sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v); -+ if( i==iEq ){ -+ pIn->iCur = iTab; -+ pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; -+ if( iEq>0 ){ -+ pIn->iBase = iTarget - i; -+ pIn->nPrefix = i; -+ }else{ -+ pIn->nPrefix = 0; -+ } -+ }else{ -+ pIn->eEndLoopOp = OP_Noop; -+ } -+ pIn++; -+ } -+ } -+ testcase( iEq>0 -+ && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 -+ && (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ); -+ if( iEq>0 -+ && (pLoop->wsFlags & (WHERE_IN_SEEKSCAN|WHERE_VIRTUALTABLE))==0 -+ ){ -+ sqlite3VdbeAddOp3(v, OP_SeekHit, pLevel->iIdxCur, 0, iEq); -+ } -+ }else{ -+ pLevel->u.in.nIn = 0; -+ } -+ sqlite3DbFree(pParse->db, aiMap); -+} -+#endif -+ -+ - /* - ** Generate code for a single equality term of the WHERE clause. An equality - ** term can be either X=expr or X IN (...). pTerm is the term to be -@@ -142021,7 +160408,6 @@ static int codeEqualityTerm( - int iTarget /* Attempt to leave results in this register */ - ){ - Expr *pX = pTerm->pExpr; -- Vdbe *v = pParse->pVdbe; - int iReg; /* Register holding results */ - - assert( pLevel->pWLoop->aLTerm[iEq]==pTerm ); -@@ -142030,111 +160416,30 @@ static int codeEqualityTerm( - iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); - }else if( pX->op==TK_ISNULL ){ - iReg = iTarget; -- sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); -+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_Null, 0, iReg); - #ifndef SQLITE_OMIT_SUBQUERY - }else{ -- int eType = IN_INDEX_NOOP; -- int iTab; -- struct InLoop *pIn; -- WhereLoop *pLoop = pLevel->pWLoop; -- int i; -- int nEq = 0; -- int *aiMap = 0; -- -- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 -- && pLoop->u.btree.pIndex!=0 -- && pLoop->u.btree.pIndex->aSortOrder[iEq] -- ){ -- testcase( iEq==0 ); -- testcase( bRev ); -- bRev = !bRev; -- } - assert( pX->op==TK_IN ); - iReg = iTarget; -- -- for(i=0; iaLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){ -- disableTerm(pLevel, pTerm); -- return iTarget; -- } -- } -- for(i=iEq;inLTerm; i++){ -- assert( pLoop->aLTerm[i]!=0 ); -- if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; -- } -- -- iTab = 0; -- if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ -- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); -- }else{ -- sqlite3 *db = pParse->db; -- pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); -- -- if( !db->mallocFailed ){ -- aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); -- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); -- pTerm->pExpr->iTable = iTab; -- } -- sqlite3ExprDelete(db, pX); -- pX = pTerm->pExpr; -- } -- -- if( eType==IN_INDEX_INDEX_DESC ){ -- testcase( bRev ); -- bRev = !bRev; -- } -- sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); -- VdbeCoverageIf(v, bRev); -- VdbeCoverageIf(v, !bRev); -- assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); -- -- pLoop->wsFlags |= WHERE_IN_ABLE; -- if( pLevel->u.in.nIn==0 ){ -- pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); -- } -- -- i = pLevel->u.in.nIn; -- pLevel->u.in.nIn += nEq; -- pLevel->u.in.aInLoop = -- sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop, -- sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); -- pIn = pLevel->u.in.aInLoop; -- if( pIn ){ -- int iMap = 0; /* Index in aiMap[] */ -- pIn += i; -- for(i=iEq;inLTerm; i++){ -- if( pLoop->aLTerm[i]->pExpr==pX ){ -- int iOut = iReg + i - iEq; -- if( eType==IN_INDEX_ROWID ){ -- pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); -- }else{ -- int iCol = aiMap ? aiMap[iMap++] : 0; -- pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut); -- } -- sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v); -- if( i==iEq ){ -- pIn->iCur = iTab; -- pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; -- if( iEq>0 ){ -- pIn->iBase = iReg - i; -- pIn->nPrefix = i; -- pLoop->wsFlags |= WHERE_IN_EARLYOUT; -- }else{ -- pIn->nPrefix = 0; -- } -- }else{ -- pIn->eEndLoopOp = OP_Noop; -- } -- pIn++; -- } -- } -- }else{ -- pLevel->u.in.nIn = 0; -- } -- sqlite3DbFree(pParse->db, aiMap); -+ codeINTerm(pParse, pTerm, pLevel, iEq, bRev, iTarget); - #endif - } -- disableTerm(pLevel, pTerm); -+ -+ /* As an optimization, try to disable the WHERE clause term that is -+ ** driving the index as it will always be true. The correct answer is -+ ** obtained regardless, but we might get the answer with fewer CPU cycles -+ ** by omitting the term. -+ ** -+ ** But do not disable the term unless we are certain that the term is -+ ** not a transitive constraint. For an example of where that does not -+ ** work, see https://sqlite.org/forum/forumpost/eb8613976a (2021-05-04) -+ */ -+ if( (pLevel->pWLoop->wsFlags & WHERE_TRANSCONS)==0 -+ || (pTerm->eOperator & WO_EQUIV)==0 -+ ){ -+ disableTerm(pLevel, pTerm); -+ } -+ - return iReg; - } - -@@ -142212,7 +160517,7 @@ static int codeAllEqualityTerms( - /* Figure out how many memory cells we will need then allocate them. - */ - regBase = pParse->nMem + 1; -- nReg = pLoop->u.btree.nEq + nExtraReg; -+ nReg = nEq + nExtraReg; - pParse->nMem += nReg; - - zAff = sqlite3DbStrDup(pParse->db,sqlite3IndexAffinityStr(pParse->db,pIdx)); -@@ -142220,11 +160525,13 @@ static int codeAllEqualityTerms( - - if( nSkip ){ - int iIdxCur = pLevel->iIdxCur; -+ sqlite3VdbeAddOp3(v, OP_Null, 0, regBase, regBase+nSkip-1); - sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur); - VdbeCoverageIf(v, bRev==0); - VdbeCoverageIf(v, bRev!=0); - VdbeComment((v, "begin skip-scan on %s", pIdx->zName)); - j = sqlite3VdbeAddOp0(v, OP_Goto); -+ assert( pLevel->addrSkip==0 ); - pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT), - iIdxCur, 0, regBase, nSkip); - VdbeCoverageIf(v, bRev==0); -@@ -142254,7 +160561,7 @@ static int codeAllEqualityTerms( - sqlite3ReleaseTempReg(pParse, regBase); - regBase = r1; - }else{ -- sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); -+ sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j); - } - } - if( pTerm->eOperator & WO_IN ){ -@@ -142271,7 +160578,8 @@ static int codeAllEqualityTerms( - sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); - VdbeCoverage(v); - } -- if( zAff ){ -+ if( pParse->nErr==0 ){ -+ assert( pParse->db->mallocFailed==0 ); - if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ - zAff[j] = SQLITE_AFF_BLOB; - } -@@ -142311,7 +160619,7 @@ static void whereLikeOptimizationStringFixup( - if( pTerm->wtFlags & TERM_LIKEOPT ){ - VdbeOp *pOp; - assert( pLevel->iLikeRepCntr>0 ); -- pOp = sqlite3VdbeGetOp(v, -1); -+ pOp = sqlite3VdbeGetLastOp(v); - assert( pOp!=0 ); - assert( pOp->opcode==OP_String8 - || pTerm->pWC->pWInfo->pParse->db->mallocFailed ); -@@ -142398,18 +160706,19 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){ - ** 2) transform the expression node to a TK_REGISTER node that reads - ** from the newly populated register. - ** --** Also, if the node is a TK_COLUMN that does access the table idenified -+** Also, if the node is a TK_COLUMN that does access the table identified - ** by pCCurHint.iTabCur, and an index is being used (which we will - ** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into - ** an access of the index rather than the original table. - */ - static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ - int rc = WRC_Continue; -+ int reg; - struct CCurHint *pHint = pWalker->u.pCCurHint; - if( pExpr->op==TK_COLUMN ){ - if( pExpr->iTable!=pHint->iTabCur ){ -- int reg = ++pWalker->pParse->nMem; /* Register for column value */ -- sqlite3ExprCode(pWalker->pParse, pExpr, reg); -+ reg = ++pWalker->pParse->nMem; /* Register for column value */ -+ reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); - pExpr->op = TK_REGISTER; - pExpr->iTable = reg; - }else if( pHint->pIdx!=0 ){ -@@ -142417,15 +160726,15 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ - pExpr->iColumn = sqlite3TableColumnToIndex(pHint->pIdx, pExpr->iColumn); - assert( pExpr->iColumn>=0 ); - } -- }else if( pExpr->op==TK_AGG_FUNCTION ){ -- /* An aggregate function in the WHERE clause of a query means this must -- ** be a correlated sub-query, and expression pExpr is an aggregate from -- ** the parent context. Do not walk the function arguments in this case. -- ** -- ** todo: It should be possible to replace this node with a TK_REGISTER -- ** expression, as the result of the expression must be stored in a -- ** register at this point. The same holds for TK_AGG_COLUMN nodes. */ -+ }else if( pExpr->pAggInfo ){ - rc = WRC_Prune; -+ reg = ++pWalker->pParse->nMem; /* Register for column value */ -+ reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); -+ pExpr->op = TK_REGISTER; -+ pExpr->iTable = reg; -+ }else if( pExpr->op==TK_TRUEFALSE ){ -+ /* Do not walk disabled expressions. tag-20230504-1 */ -+ return WRC_Prune; - } - return rc; - } -@@ -142434,7 +160743,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ - ** Insert an OP_CursorHint instruction if it is appropriate to do so. - */ - static void codeCursorHint( -- struct SrcList_item *pTabItem, /* FROM clause item */ -+ SrcItem *pTabItem, /* FROM clause item */ - WhereInfo *pWInfo, /* The where clause */ - WhereLevel *pLevel, /* Which loop to provide hints for */ - WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */ -@@ -142461,7 +160770,7 @@ static void codeCursorHint( - sWalker.pParse = pParse; - sWalker.u.pCCurHint = &sHint; - pWC = &pWInfo->sWC; -- for(i=0; inTerm; i++){ -+ for(i=0; inBase; i++){ - pTerm = &pWC->a[i]; - if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( pTerm->prereqAll & pLevel->notReady ) continue; -@@ -142490,8 +160799,8 @@ static void codeCursorHint( - */ - if( pTabItem->fg.jointype & JT_LEFT ){ - Expr *pExpr = pTerm->pExpr; -- if( !ExprHasProperty(pExpr, EP_FromJoin) -- || pExpr->iRightJoinTable!=pTabItem->iCursor -+ if( !ExprHasProperty(pExpr, EP_OuterON) -+ || pExpr->w.iJoin!=pTabItem->iCursor - ){ - sWalker.eCode = 0; - sWalker.xExprCallback = codeCursorHintIsOrFunction; -@@ -142499,7 +160808,7 @@ static void codeCursorHint( - if( sWalker.eCode ) continue; - } - }else{ -- if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue; -+ if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) continue; - } - - /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize -@@ -142527,7 +160836,7 @@ static void codeCursorHint( - } - if( pExpr!=0 ){ - sWalker.xExprCallback = codeCursorHintFixExpr; -- sqlite3WalkExpr(&sWalker, pExpr); -+ if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr); - sqlite3VdbeAddOp4(v, OP_CursorHint, - (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0, - (const char*)pExpr, P4_EXPR); -@@ -142547,13 +160856,21 @@ static void codeCursorHint( - ** - ** OP_DeferredSeek $iCur $iRowid - ** -+** Which causes a seek on $iCur to the row with rowid $iRowid. -+** - ** However, if the scan currently being coded is a branch of an OR-loop and --** the statement currently being coded is a SELECT, then P3 of OP_DeferredSeek --** is set to iIdxCur and P4 is set to point to an array of integers --** containing one entry for each column of the table cursor iCur is open --** on. For each table column, if the column is the i'th column of the --** index, then the corresponding array entry is set to (i+1). If the column --** does not appear in the index at all, the array entry is set to 0. -+** the statement currently being coded is a SELECT, then additional information -+** is added that might allow OP_Column to omit the seek and instead do its -+** lookup on the index, thus avoiding an expensive seek operation. To -+** enable this optimization, the P3 of OP_DeferredSeek is set to iIdxCur -+** and P4 is set to an array of integers containing one entry for each column -+** in the table. For each table column, if the column is the i'th -+** column of the index, then the corresponding array entry is set to (i+1). -+** If the column does not appear in the index at all, the array entry is set -+** to 0. The OP_Column opcode can check this array to see if the column it -+** wants is in the index and if it is, it will substitute the index cursor -+** and column number and continue with those new values, rather than seeking -+** the table cursor. - */ - static void codeDeferredSeek( - WhereInfo *pWInfo, /* Where clause context */ -@@ -142569,7 +160886,7 @@ static void codeDeferredSeek( - - pWInfo->bDeferredSeek = 1; - sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur); -- if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) -+ if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) - && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) - ){ - int i; -@@ -142603,7 +160920,7 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ - assert( nReg>0 ); - if( p && sqlite3ExprIsVector(p) ){ - #ifndef SQLITE_OMIT_SUBQUERY -- if( (p->flags & EP_xIsSelect) ){ -+ if( ExprUseXSelect(p) ){ - Vdbe *v = pParse->pVdbe; - int iSelect; - assert( p->op==TK_SELECT ); -@@ -142613,154 +160930,20 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ - #endif - { - int i; -- ExprList *pList = p->x.pList; -+ const ExprList *pList; -+ assert( ExprUseXList(p) ); -+ pList = p->x.pList; - assert( nReg<=pList->nExpr ); - for(i=0; ia[i].pExpr, iReg+i); - } - } - }else{ -- assert( nReg==1 ); -+ assert( nReg==1 || pParse->nErr ); - sqlite3ExprCode(pParse, p, iReg); - } - } - --/* An instance of the IdxExprTrans object carries information about a --** mapping from an expression on table columns into a column in an index --** down through the Walker. --*/ --typedef struct IdxExprTrans { -- Expr *pIdxExpr; /* The index expression */ -- int iTabCur; /* The cursor of the corresponding table */ -- int iIdxCur; /* The cursor for the index */ -- int iIdxCol; /* The column for the index */ -- int iTabCol; /* The column for the table */ -- WhereInfo *pWInfo; /* Complete WHERE clause information */ -- sqlite3 *db; /* Database connection (for malloc()) */ --} IdxExprTrans; -- --/* --** Preserve pExpr on the WhereETrans list of the WhereInfo. --*/ --static void preserveExpr(IdxExprTrans *pTrans, Expr *pExpr){ -- WhereExprMod *pNew; -- pNew = sqlite3DbMallocRaw(pTrans->db, sizeof(*pNew)); -- if( pNew==0 ) return; -- pNew->pNext = pTrans->pWInfo->pExprMods; -- pTrans->pWInfo->pExprMods = pNew; -- pNew->pExpr = pExpr; -- memcpy(&pNew->orig, pExpr, sizeof(*pExpr)); --} -- --/* The walker node callback used to transform matching expressions into --** a reference to an index column for an index on an expression. --** --** If pExpr matches, then transform it into a reference to the index column --** that contains the value of pExpr. --*/ --static int whereIndexExprTransNode(Walker *p, Expr *pExpr){ -- IdxExprTrans *pX = p->u.pIdxTrans; -- if( sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){ -- preserveExpr(pX, pExpr); -- pExpr->affExpr = sqlite3ExprAffinity(pExpr); -- pExpr->op = TK_COLUMN; -- pExpr->iTable = pX->iIdxCur; -- pExpr->iColumn = pX->iIdxCol; -- pExpr->y.pTab = 0; -- testcase( ExprHasProperty(pExpr, EP_Skip) ); -- testcase( ExprHasProperty(pExpr, EP_Unlikely) ); -- ExprClearProperty(pExpr, EP_Skip|EP_Unlikely); -- return WRC_Prune; -- }else{ -- return WRC_Continue; -- } --} -- --#ifndef SQLITE_OMIT_GENERATED_COLUMNS --/* A walker node callback that translates a column reference to a table --** into a corresponding column reference of an index. --*/ --static int whereIndexExprTransColumn(Walker *p, Expr *pExpr){ -- if( pExpr->op==TK_COLUMN ){ -- IdxExprTrans *pX = p->u.pIdxTrans; -- if( pExpr->iTable==pX->iTabCur && pExpr->iColumn==pX->iTabCol ){ -- assert( pExpr->y.pTab!=0 ); -- preserveExpr(pX, pExpr); -- pExpr->affExpr = sqlite3TableColumnAffinity(pExpr->y.pTab,pExpr->iColumn); -- pExpr->iTable = pX->iIdxCur; -- pExpr->iColumn = pX->iIdxCol; -- pExpr->y.pTab = 0; -- } -- } -- return WRC_Continue; --} --#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ -- --/* --** For an indexes on expression X, locate every instance of expression X --** in pExpr and change that subexpression into a reference to the appropriate --** column of the index. --** --** 2019-10-24: Updated to also translate references to a VIRTUAL column in --** the table into references to the corresponding (stored) column of the --** index. --*/ --static void whereIndexExprTrans( -- Index *pIdx, /* The Index */ -- int iTabCur, /* Cursor of the table that is being indexed */ -- int iIdxCur, /* Cursor of the index itself */ -- WhereInfo *pWInfo /* Transform expressions in this WHERE clause */ --){ -- int iIdxCol; /* Column number of the index */ -- ExprList *aColExpr; /* Expressions that are indexed */ -- Table *pTab; -- Walker w; -- IdxExprTrans x; -- aColExpr = pIdx->aColExpr; -- if( aColExpr==0 && !pIdx->bHasVCol ){ -- /* The index does not reference any expressions or virtual columns -- ** so no translations are needed. */ -- return; -- } -- pTab = pIdx->pTable; -- memset(&w, 0, sizeof(w)); -- w.u.pIdxTrans = &x; -- x.iTabCur = iTabCur; -- x.iIdxCur = iIdxCur; -- x.pWInfo = pWInfo; -- x.db = pWInfo->pParse->db; -- for(iIdxCol=0; iIdxColnColumn; iIdxCol++){ -- i16 iRef = pIdx->aiColumn[iIdxCol]; -- if( iRef==XN_EXPR ){ -- assert( aColExpr->a[iIdxCol].pExpr!=0 ); -- x.pIdxExpr = aColExpr->a[iIdxCol].pExpr; -- if( sqlite3ExprIsConstant(x.pIdxExpr) ) continue; -- w.xExprCallback = whereIndexExprTransNode; --#ifndef SQLITE_OMIT_GENERATED_COLUMNS -- }else if( iRef>=0 -- && (pTab->aCol[iRef].colFlags & COLFLAG_VIRTUAL)!=0 -- && (pTab->aCol[iRef].zColl==0 -- || sqlite3StrICmp(pTab->aCol[iRef].zColl, sqlite3StrBINARY)==0) -- ){ -- /* Check to see if there are direct references to generated columns -- ** that are contained in the index. Pulling the generated column -- ** out of the index is an optimization only - the main table is always -- ** available if the index cannot be used. To avoid unnecessary -- ** complication, omit this optimization if the collating sequence for -- ** the column is non-standard */ -- x.iTabCol = iRef; -- w.xExprCallback = whereIndexExprTransColumn; --#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ -- }else{ -- continue; -- } -- x.iIdxCol = iIdxCol; -- sqlite3WalkExpr(&w, pWInfo->pWhere); -- sqlite3WalkExprList(&w, pWInfo->pOrderBy); -- sqlite3WalkExprList(&w, pWInfo->pResultSet); -- } --} -- - /* - ** The pTruth expression is always true because it is the WHERE clause - ** a partial index that is driving a query loop. Look through all of the -@@ -142789,6 +160972,91 @@ static void whereApplyPartialIndexConstraints( - } - } - -+/* -+** This routine is called right after An OP_Filter has been generated and -+** before the corresponding index search has been performed. This routine -+** checks to see if there are additional Bloom filters in inner loops that -+** can be checked prior to doing the index lookup. If there are available -+** inner-loop Bloom filters, then evaluate those filters now, before the -+** index lookup. The idea is that a Bloom filter check is way faster than -+** an index lookup, and the Bloom filter might return false, meaning that -+** the index lookup can be skipped. -+** -+** We know that an inner loop uses a Bloom filter because it has the -+** WhereLevel.regFilter set. If an inner-loop Bloom filter is checked, -+** then clear the WhereLevel.regFilter value to prevent the Bloom filter -+** from being checked a second time when the inner loop is evaluated. -+*/ -+static SQLITE_NOINLINE void filterPullDown( -+ Parse *pParse, /* Parsing context */ -+ WhereInfo *pWInfo, /* Complete information about the WHERE clause */ -+ int iLevel, /* Which level of pWInfo->a[] should be coded */ -+ int addrNxt, /* Jump here to bypass inner loops */ -+ Bitmask notReady /* Loops that are not ready */ -+){ -+ while( ++iLevel < pWInfo->nLevel ){ -+ WhereLevel *pLevel = &pWInfo->a[iLevel]; -+ WhereLoop *pLoop = pLevel->pWLoop; -+ if( pLevel->regFilter==0 ) continue; -+ if( pLevel->pWLoop->nSkip ) continue; -+ /* ,--- Because sqlite3ConstructBloomFilter() has will not have set -+ ** vvvvv--' pLevel->regFilter if this were true. */ -+ if( NEVER(pLoop->prereq & notReady) ) continue; -+ assert( pLevel->addrBrk==0 ); -+ pLevel->addrBrk = addrNxt; -+ if( pLoop->wsFlags & WHERE_IPK ){ -+ WhereTerm *pTerm = pLoop->aLTerm[0]; -+ int regRowid; -+ assert( pTerm!=0 ); -+ assert( pTerm->pExpr!=0 ); -+ testcase( pTerm->wtFlags & TERM_VIRTUAL ); -+ regRowid = sqlite3GetTempReg(pParse); -+ regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, regRowid); -+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_MustBeInt, regRowid, addrNxt); -+ VdbeCoverage(pParse->pVdbe); -+ sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, -+ addrNxt, regRowid, 1); -+ VdbeCoverage(pParse->pVdbe); -+ }else{ -+ u16 nEq = pLoop->u.btree.nEq; -+ int r1; -+ char *zStartAff; -+ -+ assert( pLoop->wsFlags & WHERE_INDEXED ); -+ assert( (pLoop->wsFlags & WHERE_COLUMN_IN)==0 ); -+ r1 = codeAllEqualityTerms(pParse,pLevel,0,0,&zStartAff); -+ codeApplyAffinity(pParse, r1, nEq, zStartAff); -+ sqlite3DbFree(pParse->db, zStartAff); -+ sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter, -+ addrNxt, r1, nEq); -+ VdbeCoverage(pParse->pVdbe); -+ } -+ pLevel->regFilter = 0; -+ pLevel->addrBrk = 0; -+ } -+} -+ -+/* -+** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...) -+** operator. Return true if level pLoop is guaranteed to visit only one -+** row for each key generated for the index. -+*/ -+static int whereLoopIsOneRow(WhereLoop *pLoop){ -+ if( pLoop->u.btree.pIndex->onError -+ && pLoop->nSkip==0 -+ && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol -+ ){ -+ int ii; -+ for(ii=0; iiu.btree.nEq; ii++){ -+ if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){ -+ return 0; -+ } -+ } -+ return 1; -+ } -+ return 0; -+} -+ - /* - ** Generate code for the start of the iLevel-th loop in the WHERE clause - ** implementation described by pWInfo. -@@ -142809,7 +161077,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - WhereClause *pWC; /* Decomposition of the entire WHERE clause */ - WhereTerm *pTerm; /* A WHERE clause term */ - sqlite3 *db; /* Database connection */ -- struct SrcList_item *pTabItem; /* FROM clause term being coded */ -+ SrcItem *pTabItem; /* FROM clause term being coded */ - int addrBrk; /* Jump here to break out of the loop */ - int addrHalt; /* addrBrk for the outermost loop */ - int addrCont; /* Jump here to continue with next cycle */ -@@ -142825,14 +161093,17 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - iCur = pTabItem->iCursor; - pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); - bRev = (pWInfo->revMask>>iLevel)&1; -- VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); --#if WHERETRACE_ENABLED /* 0x20800 */ -- if( sqlite3WhereTrace & 0x800 ){ -+ VdbeModuleComment((v, "Begin WHERE-loop%d: %s", -+ iLevel, pTabItem->pSTab->zName)); -+#if WHERETRACE_ENABLED /* 0x4001 */ -+ if( sqlite3WhereTrace & 0x1 ){ - sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n", - iLevel, pWInfo->nLevel, (u64)notReady, pLevel->iFrom); -- sqlite3WhereLoopPrint(pLoop, pWC); -+ if( sqlite3WhereTrace & 0x1000 ){ -+ sqlite3WhereLoopPrint(pLoop, pWC); -+ } - } -- if( sqlite3WhereTrace & 0x20000 ){ -+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){ - if( iLevel==0 ){ - sqlite3DebugPrintf("WHERE clause being coded:\n"); - sqlite3TreeViewExpr(0, pWInfo->pWhere, 0); -@@ -142859,27 +161130,34 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** initialize a memory cell that records if this table matches any - ** row of the left table of the join. - */ -- assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) -+ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN)) - || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0 - ); - if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ - pLevel->iLeftJoin = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); -- VdbeComment((v, "init LEFT JOIN no-match flag")); -+ VdbeComment((v, "init LEFT JOIN match flag")); - } - - /* Compute a safe address to jump to if we discover that the table for - ** this loop is empty and can never contribute content. */ -- for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){} -+ for(j=iLevel; j>0; j--){ -+ if( pWInfo->a[j].iLeftJoin ) break; -+ if( pWInfo->a[j].pRJ ) break; -+ } - addrHalt = pWInfo->a[j].addrBrk; - - /* Special case of a FROM clause subquery implemented as a co-routine */ - if( pTabItem->fg.viaCoroutine ){ -- int regYield = pTabItem->regReturn; -- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); -+ int regYield; -+ Subquery *pSubq; -+ assert( pTabItem->fg.isSubquery && pTabItem->u4.pSubq!=0 ); -+ pSubq = pTabItem->u4.pSubq; -+ regYield = pSubq->regReturn; -+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); - pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); - VdbeCoverage(v); -- VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); -+ VdbeComment((v, "next row of %s", pTabItem->pSTab->zName)); - pLevel->op = OP_Goto; - }else - -@@ -142891,7 +161169,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - int iReg; /* P3 Value for OP_VFilter */ - int addrNotFound; - int nConstraint = pLoop->nLTerm; -- int iIn; /* Counter for IN constraints */ - - iReg = sqlite3GetTempRange(pParse, nConstraint+2); - addrNotFound = pLevel->addrBrk; -@@ -142900,57 +161177,94 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - pTerm = pLoop->aLTerm[j]; - if( NEVER(pTerm==0) ) continue; - if( pTerm->eOperator & WO_IN ){ -- codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); -- addrNotFound = pLevel->addrNxt; -+ if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){ -+ int iTab = pParse->nTab++; -+ int iCache = ++pParse->nMem; -+ sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab); -+ sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache); -+ }else{ -+ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); -+ addrNotFound = pLevel->addrNxt; -+ } - }else{ - Expr *pRight = pTerm->pExpr->pRight; - codeExprOrVector(pParse, pRight, iTarget, 1); -+ if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET -+ && pLoop->u.vtab.bOmitOffset -+ ){ -+ assert( pTerm->eOperator==WO_AUX ); -+ assert( pWInfo->pSelect!=0 ); -+ assert( pWInfo->pSelect->iOffset>0 ); -+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pSelect->iOffset); -+ VdbeComment((v,"Zero OFFSET counter")); -+ } - } - } - sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); - sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1); -+ /* The instruction immediately prior to OP_VFilter must be an OP_Integer -+ ** that sets the "argc" value for xVFilter. This is necessary for -+ ** resolveP2() to work correctly. See tag-20250207a. */ - sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, - pLoop->u.vtab.idxStr, - pLoop->u.vtab.needFree ? P4_DYNAMIC : P4_STATIC); - VdbeCoverage(v); - pLoop->u.vtab.needFree = 0; -+ /* An OOM inside of AddOp4(OP_VFilter) instruction above might have freed -+ ** the u.vtab.idxStr. NULL it out to prevent a use-after-free */ -+ if( db->mallocFailed ) pLoop->u.vtab.idxStr = 0; - pLevel->p1 = iCur; - pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext; - pLevel->p2 = sqlite3VdbeCurrentAddr(v); -- iIn = pLevel->u.in.nIn; -- for(j=nConstraint-1; j>=0; j--){ -+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); -+ -+ for(j=0; jaLTerm[j]; -- if( (pTerm->eOperator & WO_IN)!=0 ) iIn--; - if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){ - disableTerm(pLevel, pTerm); -- }else if( (pTerm->eOperator & WO_IN)!=0 -- && sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 -+ continue; -+ } -+ if( (pTerm->eOperator & WO_IN)!=0 -+ && (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0 -+ && !db->mallocFailed - ){ - Expr *pCompare; /* The comparison operator */ - Expr *pRight; /* RHS of the comparison */ - VdbeOp *pOp; /* Opcode to access the value of the IN constraint */ -+ int iIn; /* IN loop corresponding to the j-th constraint */ - - /* Reload the constraint value into reg[iReg+j+2]. The same value - ** was loaded into the same register prior to the OP_VFilter, but - ** the xFilter implementation might have changed the datatype or -- ** encoding of the value in the register, so it *must* be reloaded. */ -- assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed ); -- if( !db->mallocFailed ){ -- assert( iIn>=0 && iInu.in.nIn ); -+ ** encoding of the value in the register, so it *must* be reloaded. -+ */ -+ for(iIn=0; ALWAYS(iInu.in.nIn); iIn++){ - pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[iIn].addrInTop); -- assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid ); -- assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 ); -- assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 ); -- testcase( pOp->opcode==OP_Rowid ); -- sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); -+ if( (pOp->opcode==OP_Column && pOp->p3==iReg+j+2) -+ || (pOp->opcode==OP_Rowid && pOp->p2==iReg+j+2) -+ ){ -+ testcase( pOp->opcode==OP_Rowid ); -+ sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); -+ break; -+ } - } - - /* Generate code that will continue to the next row if -- ** the IN constraint is not satisfied */ -+ ** the IN constraint is not satisfied -+ */ - pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0); -- assert( pCompare!=0 || db->mallocFailed ); -- if( pCompare ){ -- pCompare->pLeft = pTerm->pExpr->pLeft; -+ if( !db->mallocFailed ){ -+ int iFld = pTerm->u.x.iField; -+ Expr *pLeft = pTerm->pExpr->pLeft; -+ assert( pLeft!=0 ); -+ if( iFld>0 ){ -+ assert( pLeft->op==TK_VECTOR ); -+ assert( ExprUseXList(pLeft) ); -+ assert( iFld<=pLeft->x.pList->nExpr ); -+ pCompare->pLeft = pLeft->x.pList->a[iFld-1].pExpr; -+ }else{ -+ pCompare->pLeft = pLeft; -+ } - pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0); - if( pRight ){ - pRight->iTable = iReg+j+2; -@@ -142959,11 +161273,11 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ); - } - pCompare->pLeft = 0; -- sqlite3ExprDelete(db, pCompare); - } -+ sqlite3ExprDelete(db, pCompare); - } - } -- assert( iIn==0 || db->mallocFailed ); -+ - /* These registers need to be preserved in case there is an IN operator - ** loop. So we could deallocate the registers here (and potentially - ** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems -@@ -142991,12 +161305,17 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); - if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg); - addrNxt = pLevel->addrNxt; -+ if( pLevel->regFilter ){ -+ sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); -+ VdbeCoverage(v); -+ sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, -+ iRowidReg, 1); -+ VdbeCoverage(v); -+ filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); -+ } - sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); - VdbeCoverage(v); - pLevel->op = OP_Noop; -- if( (pTerm->prereqAll & pLevel->notReady)==0 ){ -- pTerm->wtFlags |= TERM_CODED; -- } - }else if( (pLoop->wsFlags & WHERE_IPK)!=0 - && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 - ){ -@@ -143034,7 +161353,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - }; - assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */ - assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */ -- assert( TK_GE==TK_GT+3 ); /* ... is correcct. */ -+ assert( TK_GE==TK_GT+3 ); /* ... is correct. */ - - assert( (pStart->wtFlags & TERM_VNULL)==0 ); - testcase( pStart->wtFlags & TERM_VIRTUAL ); -@@ -143172,6 +161491,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */ - int omitTable; /* True if we use the index only */ - int regBignull = 0; /* big-null flag register */ -+ int addrSeekScan = 0; /* Opcode of the OP_SeekScan, if any */ - - pIdx = pLoop->u.btree.pIndex; - iIdxCur = pLevel->iIdxCur; -@@ -143243,14 +161563,18 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** a forward order scan on a descending index, interchange the - ** start and end terms (pRangeStart and pRangeEnd). - */ -- if( (nEqnKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) -- || (bRev && pIdx->nKeyCol==nEq) -- ){ -+ if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) ){ - SWAP(WhereTerm *, pRangeEnd, pRangeStart); - SWAP(u8, bSeekPastNull, bStopAtNull); - SWAP(u8, nBtm, nTop); - } - -+ if( iLevel>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){ -+ /* In case OP_SeekScan is used, ensure that the index cursor does not -+ ** point to a valid row for the first iteration of this loop. */ -+ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); -+ } -+ - /* Generate code to evaluate all constraint terms using == or IN - ** and store the values of those terms in an array of registers - ** starting at regBase. -@@ -143310,16 +161634,38 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** above has already left the cursor sitting on the correct row, - ** so no further seeking is needed */ - }else{ -- if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){ -- sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur); -- } - if( regBignull ){ - sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull); - VdbeComment((v, "NULL-scan pass ctr")); - } -+ if( pLevel->regFilter ){ -+ sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt, -+ regBase, nEq); -+ VdbeCoverage(v); -+ filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady); -+ } - - op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; - assert( op!=0 ); -+ if( (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 && op==OP_SeekGE ){ -+ assert( regBignull==0 ); -+ /* TUNING: The OP_SeekScan opcode seeks to reduce the number -+ ** of expensive seek operations by replacing a single seek with -+ ** 1 or more step operations. The question is, how many steps -+ ** should we try before giving up and going with a seek. The cost -+ ** of a seek is proportional to the logarithm of the of the number -+ ** of entries in the tree, so basing the number of steps to try -+ ** on the estimated number of rows in the btree seems like a good -+ ** guess. */ -+ addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan, -+ (pIdx->aiRowLogEst[0]+9)/10); -+ if( pRangeStart || pRangeEnd ){ -+ sqlite3VdbeChangeP5(v, 1); -+ sqlite3VdbeChangeP2(v, addrSeekScan, sqlite3VdbeCurrentAddr(v)+1); -+ addrSeekScan = 0; -+ } -+ VdbeCoverage(v); -+ } - sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); - VdbeCoverage(v); - VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind ); -@@ -143351,8 +161697,10 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** range (if any). - */ - nConstraint = nEq; -+ assert( pLevel->p2==0 ); - if( pRangeEnd ){ - Expr *pRight = pRangeEnd->pExpr->pRight; -+ assert( addrSeekScan==0 ); - codeExprOrVector(pParse, pRight, regBase+nEq, nTop); - whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); - if( (pRangeEnd->wtFlags & TERM_VNULL)==0 -@@ -143382,8 +161730,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - } - nConstraint++; - } -- sqlite3DbFree(db, zStartAff); -- sqlite3DbFree(db, zEndAff); -+ if( zStartAff ) sqlite3DbNNFreeNN(db, zStartAff); -+ if( zEndAff ) sqlite3DbNNFreeNN(db, zEndAff); - - /* Top of the loop body */ - pLevel->p2 = sqlite3VdbeCurrentAddr(v); -@@ -143402,6 +161750,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE ); - testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT ); - testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); -+ if( addrSeekScan ) sqlite3VdbeJumpHere(v, addrSeekScan); - } - if( regBignull ){ - /* During a NULL-scan, check to see if we have reached the end of -@@ -143421,27 +161770,17 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); - } - -- if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){ -- sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1); -+ if( (pLoop->wsFlags & WHERE_IN_EARLYOUT)!=0 ){ -+ sqlite3VdbeAddOp3(v, OP_SeekHit, iIdxCur, nEq, nEq); - } - - /* Seek the table cursor, if required */ - omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 -- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; -+ && (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0; - if( omitTable ){ - /* pIdx is a covering index. No need to access the main table. */ - }else if( HasRowid(pIdx->pTable) ){ -- if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE) -- || ( (pWInfo->wctrlFlags & WHERE_SEEK_UNIQ_TABLE)!=0 -- && (pWInfo->eOnePass==ONEPASS_SINGLE || pLoop->nLTerm==0) ) -- ){ -- iRowidReg = ++pParse->nMem; -- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); -- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg); -- VdbeCoverage(v); -- }else{ -- codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur); -- } -+ codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur); - }else if( iCur!=iIdxCur ){ - Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); - iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol); -@@ -143454,35 +161793,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - } - - if( pLevel->iLeftJoin==0 ){ -- /* If pIdx is an index on one or more expressions, then look through -- ** all the expressions in pWInfo and try to transform matching expressions -- ** into reference to index columns. Also attempt to translate references -- ** to virtual columns in the table into references to (stored) columns -- ** of the index. -- ** -- ** Do not do this for the RHS of a LEFT JOIN. This is because the -- ** expression may be evaluated after OP_NullRow has been executed on -- ** the cursor. In this case it is important to do the full evaluation, -- ** as the result of the expression may not be NULL, even if all table -- ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a -- ** -- ** Also, do not do this when processing one index an a multi-index -- ** OR clause, since the transformation will become invalid once we -- ** move forward to the next index. -- ** https://sqlite.org/src/info/4e8e4857d32d401f -- */ -- if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ -- whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); -- } -- - /* If a partial index is driving the loop, try to eliminate WHERE clause - ** terms from the query that must be true due to the WHERE clause of -- ** the partial index. -+ ** the partial index. This optimization does not work on an outer join, -+ ** as shown by: - ** -- ** 2019-11-02 ticket 623eff57e76d45f6: This optimization does not work -- ** for a LEFT JOIN. -+ ** 2019-11-02 ticket 623eff57e76d45f6 (LEFT JOIN) -+ ** 2025-05-29 forum post 7dee41d32506c4ae (RIGHT JOIN) - */ -- if( pIdx->pPartIdxWhere ){ -+ if( pIdx->pPartIdxWhere && pLevel->pRJ==0 ){ - whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC); - } - }else{ -@@ -143490,11 +161809,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - /* The following assert() is not a requirement, merely an observation: - ** The OR-optimization doesn't work for the right hand table of - ** a LEFT JOIN: */ -- assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ); -+ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ); - } - - /* Record the instruction used to terminate the loop. */ -- if( pLoop->wsFlags & WHERE_ONEROW ){ -+ if( (pLoop->wsFlags & WHERE_ONEROW) -+ || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop)) -+ ){ - pLevel->op = OP_Noop; - }else if( bRev ){ - pLevel->op = OP_Prev; -@@ -143568,9 +161889,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - int iRetInit; /* Address of regReturn init */ - int untestedTerms = 0; /* Some terms not completely tested */ - int ii; /* Loop counter */ -- u16 wctrlFlags; /* Flags for sub-WHERE clause */ - Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ -- Table *pTab = pTabItem->pTab; -+ Table *pTab = pTabItem->pSTab; - - pTerm = pLoop->aLTerm[0]; - assert( pTerm!=0 ); -@@ -143586,10 +161906,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - */ - if( pWInfo->nLevel>1 ){ - int nNotReady; /* The number of notReady tables */ -- struct SrcList_item *origSrc; /* Original list of tables */ -+ SrcItem *origSrc; /* Original list of tables */ - nNotReady = pWInfo->nLevel - iLevel - 1; -- pOrTab = sqlite3StackAllocRaw(db, -- sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); -+ pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1)); - if( pOrTab==0 ) return notReady; - pOrTab->nAlloc = (u8)(nNotReady + 1); - pOrTab->nSrc = pOrTab->nAlloc; -@@ -143629,7 +161948,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); - - /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y -- ** Then for every term xN, evaluate as the subexpression: xN AND z -+ ** Then for every term xN, evaluate as the subexpression: xN AND y - ** That way, terms in y that are factored into the disjunction will - ** be picked up by the recursive calls to sqlite3WhereBegin() below. - ** -@@ -143640,7 +161959,21 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** - ** This optimization also only applies if the (x1 OR x2 OR ...) term - ** is not contained in the ON clause of a LEFT JOIN. -- ** See ticket http://www.sqlite.org/src/info/f2369304e4 -+ ** See ticket http://sqlite.org/src/info/f2369304e4 -+ ** -+ ** 2022-02-04: Do not push down slices of a row-value comparison. -+ ** In other words, "w" or "y" may not be a slice of a vector. Otherwise, -+ ** the initialization of the right-hand operand of the vector comparison -+ ** might not occur, or might occur only in an OR branch that is not -+ ** taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1. -+ ** -+ ** 2022-03-03: Do not push down expressions that involve subqueries. -+ ** The subquery might get coded as a subroutine. Any table-references -+ ** in the subquery might be resolved to index-references for the index on -+ ** the OR branch in which the subroutine is coded. But if the subroutine -+ ** is invoked from a different OR branch that uses a different index, such -+ ** index-references will not work. tag-20220303a -+ ** https://sqlite.org/forum/forumpost/36937b197273d403 - */ - if( pWC->nTerm>1 ){ - int iTerm; -@@ -143649,9 +161982,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - if( &pWC->a[iTerm] == pTerm ) continue; - testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL ); - testcase( pWC->a[iTerm].wtFlags & TERM_CODED ); -- if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue; -+ testcase( pWC->a[iTerm].wtFlags & TERM_SLICE ); -+ if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED|TERM_SLICE))!=0 ){ -+ continue; -+ } - if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; -- testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); -+ if( ExprHasProperty(pExpr, EP_Subquery) ) continue; /* tag-20220303a */ - pExpr = sqlite3ExprDup(db, pExpr, 0); - pAndExpr = sqlite3ExprAnd(pParse, pAndExpr, pExpr); - } -@@ -143659,7 +161995,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - /* The extra 0x10000 bit on the opcode is masked off and does not - ** become part of the new Expr.op. However, it does make the - ** op==TK_AND comparison inside of sqlite3PExpr() false, and this -- ** prevents sqlite3PExpr() from implementing AND short-circuit -+ ** prevents sqlite3PExpr() from applying the AND short-circuit - ** optimization, which we do not want here. */ - pAndExpr = sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr); - } -@@ -143669,27 +162005,32 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** eliminating duplicates from other WHERE clauses, the action for each - ** sub-WHERE clause is to to invoke the main loop body as a subroutine. - */ -- wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE); - ExplainQueryPlan((pParse, 1, "MULTI-INDEX OR")); - for(ii=0; iinTerm; ii++){ - WhereTerm *pOrTerm = &pOrWc->a[ii]; - if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ - WhereInfo *pSubWInfo; /* Info for single OR-term scan */ - Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ -+ Expr *pDelete; /* Local copy of OR clause term */ - int jmp1 = 0; /* Address of jump operation */ - testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0 -- && !ExprHasProperty(pOrExpr, EP_FromJoin) -+ && !ExprHasProperty(pOrExpr, EP_OuterON) - ); /* See TH3 vtab25.400 and ticket 614b25314c766238 */ -+ pDelete = pOrExpr = sqlite3ExprDup(db, pOrExpr, 0); -+ if( db->mallocFailed ){ -+ sqlite3ExprDelete(db, pDelete); -+ continue; -+ } - if( pAndExpr ){ - pAndExpr->pLeft = pOrExpr; - pOrExpr = pAndExpr; - } - /* Loop through table entries that match term pOrTerm. */ - ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); -- WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); -- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, -- wctrlFlags, iCovCur); -- assert( pSubWInfo || pParse->nErr || db->mallocFailed ); -+ WHERETRACE(0xffffffff, ("Subplan for OR-clause:\n")); -+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0, -+ WHERE_OR_SUBCLAUSE, iCovCur); -+ assert( pSubWInfo || pParse->nErr ); - if( pSubWInfo ){ - WhereLoop *pSubLoop; - int addrExplain = sqlite3WhereExplainOneScan( -@@ -143786,15 +162127,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - }else{ - pCov = 0; - } -+ if( sqlite3WhereUsesDeferredSeek(pSubWInfo) ){ -+ pWInfo->bDeferredSeek = 1; -+ } - - /* Finish the loop through table entries that match term pOrTerm. */ - sqlite3WhereEnd(pSubWInfo); - ExplainQueryPlanPop(pParse); - } -+ sqlite3ExprDelete(db, pDelete); - } - } - ExplainQueryPlanPop(pParse); -- pLevel->u.pCovidx = pCov; -+ assert( pLevel->pWLoop==pLoop ); -+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)!=0 ); -+ assert( (pLoop->wsFlags & WHERE_IN_ABLE)==0 ); -+ pLevel->u.pCoveringIdx = pCov; - if( pCov ) pLevel->iIdxCur = iCovCur; - if( pAndExpr ){ - pAndExpr->pLeft = 0; -@@ -143804,7 +162152,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - sqlite3VdbeGoto(v, pLevel->addrBrk); - sqlite3VdbeResolveLabel(v, iLoopBody); - -- if( pWInfo->nLevel>1 ){ sqlite3StackFree(db, pOrTab); } -+ /* Set the P2 operand of the OP_Return opcode that will end the current -+ ** loop to point to this spot, which is the top of the next containing -+ ** loop. The byte-code formatter will use that P2 value as a hint to -+ ** indent everything in between the this point and the final OP_Return. -+ ** See tag-20220407a in vdbe.c and shell.c */ -+ assert( pLevel->op==OP_Return ); -+ pLevel->p2 = sqlite3VdbeCurrentAddr(v); -+ -+ if( pWInfo->nLevel>1 ){ sqlite3DbFreeNN(db, pOrTab); } - if( !untestedTerms ) disableTerm(pLevel, pTerm); - }else - #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ -@@ -143848,6 +162204,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** iLoop==3: Code all remaining expressions. - ** - ** An effort is made to skip unnecessary iterations of the loop. -+ ** -+ ** This optimization of causing simple query restrictions to occur before -+ ** more complex one is call the "push-down" optimization in MySQL. Here -+ ** in SQLite, the name is "MySQL push-down", since there is also another -+ ** totally unrelated optimization called "WHERE-clause push-down". -+ ** Sometimes the qualifier is omitted, resulting in an ambiguity, so beware. - */ - iLoop = (pIdx ? 1 : 2); - do{ -@@ -143866,10 +162228,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - } - pE = pTerm->pExpr; - assert( pE!=0 ); -- if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){ -- continue; -+ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ -+ if( !ExprHasProperty(pE,EP_OuterON|EP_InnerON) ){ -+ /* Defer processing WHERE clause constraints until after outer -+ ** join processing. tag-20220513a */ -+ continue; -+ }else if( (pTabItem->fg.jointype & JT_LEFT)==JT_LEFT -+ && !ExprHasProperty(pE,EP_OuterON) ){ -+ continue; -+ }else{ -+ Bitmask m = sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin); -+ if( m & pLevel->notReady ){ -+ /* An ON clause that is not ripe */ -+ continue; -+ } -+ } - } -- - if( iLoop==1 && !sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){ - iNext = 2; - continue; -@@ -143896,12 +162270,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - } - #endif - } --#ifdef WHERETRACE_ENABLED /* 0xffff */ -+#ifdef WHERETRACE_ENABLED /* 0xffffffff */ - if( sqlite3WhereTrace ){ - VdbeNoopComment((v, "WhereTerm[%d] (%p) priority=%d", - pWC->nTerm-j, pTerm, iLoop)); - } -- if( sqlite3WhereTrace & 0x800 ){ -+ if( sqlite3WhereTrace & 0x4000 ){ - sqlite3DebugPrintf("Coding auxiliary constraint:\n"); - sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); - } -@@ -143921,29 +162295,30 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** then we cannot use the "t1.a=t2.b" constraint, but we can code - ** the implied "t1.a=123" constraint. - */ -- for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ -+ for(pTerm=pWC->a, j=pWC->nBase; j>0; j--, pTerm++){ - Expr *pE, sEAlt; - WhereTerm *pAlt; - if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; - if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; - if( pTerm->leftCursor!=iCur ) continue; -- if( pTabItem->fg.jointype & JT_LEFT ) continue; -+ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue; - pE = pTerm->pExpr; --#ifdef WHERETRACE_ENABLED /* 0x800 */ -- if( sqlite3WhereTrace & 0x800 ){ -+#ifdef WHERETRACE_ENABLED /* 0x4001 */ -+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){ - sqlite3DebugPrintf("Coding transitive constraint:\n"); - sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); - } - #endif -- assert( !ExprHasProperty(pE, EP_FromJoin) ); -+ assert( !ExprHasProperty(pE, EP_OuterON) ); - assert( (pTerm->prereqRight & pLevel->notReady)!=0 ); -- pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.leftColumn, notReady, -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.x.leftColumn, notReady, - WO_EQ|WO_IN|WO_IS, 0); - if( pAlt==0 ) continue; - if( pAlt->wtFlags & (TERM_CODED) ) continue; - if( (pAlt->eOperator & WO_IN) -- && (pAlt->pExpr->flags & EP_xIsSelect) -+ && ExprUseXSelect(pAlt->pExpr) - && (pAlt->pExpr->x.pSelect->pEList->nExpr>1) - ){ - continue; -@@ -143955,6 +162330,48 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - sEAlt = *pAlt->pExpr; - sEAlt.pLeft = pE->pLeft; - sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL); -+ pAlt->wtFlags |= TERM_CODED; -+ } -+ -+ /* For a RIGHT OUTER JOIN, record the fact that the current row has -+ ** been matched at least once. -+ */ -+ if( pLevel->pRJ ){ -+ Table *pTab; -+ int nPk; -+ int r; -+ int jmp1 = 0; -+ WhereRightJoin *pRJ = pLevel->pRJ; -+ -+ /* pTab is the right-hand table of the RIGHT JOIN. Generate code that -+ ** will record that the current row of that table has been matched at -+ ** least once. This is accomplished by storing the PK for the row in -+ ** both the iMatch index and the regBloom Bloom filter. -+ */ -+ pTab = pWInfo->pTabList->a[pLevel->iFrom].pSTab; -+ if( HasRowid(pTab) ){ -+ r = sqlite3GetTempRange(pParse, 2); -+ sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1); -+ nPk = 1; -+ }else{ -+ int iPk; -+ Index *pPk = sqlite3PrimaryKeyIndex(pTab); -+ nPk = pPk->nKeyCol; -+ r = sqlite3GetTempRange(pParse, nPk+1); -+ for(iPk=0; iPkaiColumn[iPk]; -+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+1+iPk); -+ } -+ } -+ jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, 0, r+1, nPk); -+ VdbeCoverage(v); -+ VdbeComment((v, "match against %s", pTab->zName)); -+ sqlite3VdbeAddOp3(v, OP_MakeRecord, r+1, nPk, r); -+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pRJ->iMatch, r, r+1, nPk); -+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pRJ->regBloom, 0, r+1, nPk); -+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); -+ sqlite3VdbeJumpHere(v, jmp1); -+ sqlite3ReleaseTempRange(pParse, r, nPk+1); - } - - /* For a LEFT OUTER JOIN, generate code that will record the fact that -@@ -143964,7 +162381,31 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - pLevel->addrFirst = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); - VdbeComment((v, "record LEFT JOIN hit")); -- for(pTerm=pWC->a, j=0; jnTerm; j++, pTerm++){ -+ if( pLevel->pRJ==0 ){ -+ goto code_outer_join_constraints; /* WHERE clause constraints */ -+ } -+ } -+ -+ if( pLevel->pRJ ){ -+ /* Create a subroutine used to process all interior loops and code -+ ** of the RIGHT JOIN. During normal operation, the subroutine will -+ ** be in-line with the rest of the code. But at the end, a separate -+ ** loop will run that invokes this subroutine for unmatched rows -+ ** of pTab, with all tables to left begin set to NULL. -+ */ -+ WhereRightJoin *pRJ = pLevel->pRJ; -+ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn); -+ pRJ->addrSubrtn = sqlite3VdbeCurrentAddr(v); -+ assert( pParse->withinRJSubrtn < 255 ); -+ pParse->withinRJSubrtn++; -+ -+ /* WHERE clause constraints must be deferred until after outer join -+ ** row elimination has completed, since WHERE clause constraints apply -+ ** to the results of the OUTER JOIN. The following loop generates the -+ ** appropriate WHERE clause constraint checks. tag-20220513a. -+ */ -+ code_outer_join_constraints: -+ for(pTerm=pWC->a, j=0; jnBase; j++, pTerm++){ - testcase( pTerm->wtFlags & TERM_VIRTUAL ); - testcase( pTerm->wtFlags & TERM_CODED ); - if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; -@@ -143972,19 +162413,20 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - assert( pWInfo->untestedTerms ); - continue; - } -+ if( pTabItem->fg.jointype & JT_LTORJ ) continue; - assert( pTerm->pExpr ); - sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); - pTerm->wtFlags |= TERM_CODED; - } - } - --#if WHERETRACE_ENABLED /* 0x20800 */ -- if( sqlite3WhereTrace & 0x20000 ){ -+#if WHERETRACE_ENABLED /* 0x4001 */ -+ if( sqlite3WhereTrace & 0x4000 ){ - sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n", - iLevel); - sqlite3WhereClausePrint(pWC); - } -- if( sqlite3WhereTrace & 0x800 ){ -+ if( sqlite3WhereTrace & 0x1 ){ - sqlite3DebugPrintf("End Coding level %d: notReady=%llx\n", - iLevel, (u64)pLevel->notReady); - } -@@ -143992,6 +162434,111 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - return pLevel->notReady; - } - -+/* -+** Generate the code for the loop that finds all non-matched terms -+** for a RIGHT JOIN. -+*/ -+SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( -+ WhereInfo *pWInfo, -+ int iLevel, -+ WhereLevel *pLevel -+){ -+ Parse *pParse = pWInfo->pParse; -+ Vdbe *v = pParse->pVdbe; -+ WhereRightJoin *pRJ = pLevel->pRJ; -+ Expr *pSubWhere = 0; -+ WhereClause *pWC = &pWInfo->sWC; -+ WhereInfo *pSubWInfo; -+ WhereLoop *pLoop = pLevel->pWLoop; -+ SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; -+ SrcList *pFrom; -+ u8 fromSpace[SZ_SRCLIST_1]; -+ Bitmask mAll = 0; -+ int k; -+ -+ ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); -+ sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, -+ pRJ->regReturn); -+ for(k=0; ka[k].pWLoop->iTab == pWInfo->a[k].iFrom ); -+ pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; -+ mAll |= pWInfo->a[k].pWLoop->maskSelf; -+ if( pRight->fg.viaCoroutine ){ -+ Subquery *pSubq; -+ assert( pRight->fg.isSubquery && pRight->u4.pSubq!=0 ); -+ pSubq = pRight->u4.pSubq; -+ assert( pSubq->pSelect!=0 && pSubq->pSelect->pEList!=0 ); -+ sqlite3VdbeAddOp3( -+ v, OP_Null, 0, pSubq->regResult, -+ pSubq->regResult + pSubq->pSelect->pEList->nExpr-1 -+ ); -+ } -+ sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); -+ iIdxCur = pWInfo->a[k].iIdxCur; -+ if( iIdxCur ){ -+ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); -+ } -+ } -+ if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ -+ mAll |= pLoop->maskSelf; -+ for(k=0; knTerm; k++){ -+ WhereTerm *pTerm = &pWC->a[k]; -+ if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0 -+ && pTerm->eOperator!=WO_ROWVAL -+ ){ -+ break; -+ } -+ if( pTerm->prereqAll & ~mAll ) continue; -+ if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; -+ pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, -+ sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); -+ } -+ } -+ pFrom = (SrcList*)fromSpace; -+ pFrom->nSrc = 1; -+ pFrom->nAlloc = 1; -+ memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem)); -+ pFrom->a[0].fg.jointype = 0; -+ assert( pParse->withinRJSubrtn < 100 ); -+ pParse->withinRJSubrtn++; -+ pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0, -+ WHERE_RIGHT_JOIN, 0); -+ if( pSubWInfo ){ -+ int iCur = pLevel->iTabCur; -+ int r = ++pParse->nMem; -+ int nPk; -+ int jmp; -+ int addrCont = sqlite3WhereContinueLabel(pSubWInfo); -+ Table *pTab = pTabItem->pSTab; -+ if( HasRowid(pTab) ){ -+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r); -+ nPk = 1; -+ }else{ -+ int iPk; -+ Index *pPk = sqlite3PrimaryKeyIndex(pTab); -+ nPk = pPk->nKeyCol; -+ pParse->nMem += nPk - 1; -+ for(iPk=0; iPkaiColumn[iPk]; -+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk); -+ } -+ } -+ jmp = sqlite3VdbeAddOp4Int(v, OP_Filter, pRJ->regBloom, 0, r, nPk); -+ VdbeCoverage(v); -+ sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, addrCont, r, nPk); -+ VdbeCoverage(v); -+ sqlite3VdbeJumpHere(v, jmp); -+ sqlite3VdbeAddOp2(v, OP_Gosub, pRJ->regReturn, pRJ->addrSubrtn); -+ sqlite3WhereEnd(pSubWInfo); -+ } -+ sqlite3ExprDelete(pParse->db, pSubWhere); -+ ExplainQueryPlanPop(pParse); -+ assert( pParse->withinRJSubrtn>0 ); -+ pParse->withinRJSubrtn--; -+} -+ - /************** End of wherecode.c *******************************************/ - /************** Begin file whereexpr.c ***************************************/ - /* -@@ -144009,7 +162556,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( - ** the WHERE clause of SQL statements. - ** - ** This file was originally part of where.c but was split out to improve --** readability and editabiliity. This file contains utility routines for -+** readability and editability. This file contains utility routines for - ** analyzing Expr objects in the WHERE clause. - */ - /* #include "sqliteInt.h" */ -@@ -144060,7 +162607,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ - if( pWC->nTerm>=pWC->nSlot ){ - WhereTerm *pOld = pWC->a; - sqlite3 *db = pWC->pWInfo->pParse->db; -- pWC->a = sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 ); -+ pWC->a = sqlite3WhereMalloc(pWC->pWInfo, sizeof(pWC->a[0])*pWC->nSlot*2 ); - if( pWC->a==0 ){ - if( wtFlags & TERM_DYNAMIC ){ - sqlite3ExprDelete(db, p); -@@ -144069,12 +162616,10 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ - return 0; - } - memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm); -- if( pOld!=pWC->aStatic ){ -- sqlite3DbFree(db, pOld); -- } -- pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]); -+ pWC->nSlot = pWC->nSlot*2; - } - pTerm = &pWC->a[idx = pWC->nTerm++]; -+ if( (wtFlags & TERM_VIRTUAL)==0 ) pWC->nBase = pWC->nTerm; - if( p && ExprHasProperty(p, EP_Unlikely) ){ - pTerm->truthProb = sqlite3LogEst(p->iTable) - 270; - }else{ -@@ -144099,7 +162644,12 @@ static int allowedOp(int op){ - assert( TK_LT>TK_EQ && TK_LTTK_EQ && TK_LE=TK_EQ && op<=TK_GE) || op==TK_ISNULL || op==TK_IS; -+ assert( TK_INTK_GE ) return 0; -+ if( op>=TK_EQ ) return 1; -+ return op==TK_IN || op==TK_ISNULL || op==TK_IS; - } - - /* -@@ -144132,15 +162682,16 @@ static u16 exprCommute(Parse *pParse, Expr *pExpr){ - static u16 operatorMask(int op){ - u16 c; - assert( allowedOp(op) ); -- if( op==TK_IN ){ -+ if( op>=TK_EQ ){ -+ assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); -+ c = (u16)(WO_EQ<<(op-TK_EQ)); -+ }else if( op==TK_IN ){ - c = WO_IN; - }else if( op==TK_ISNULL ){ - c = WO_ISNULL; -- }else if( op==TK_IS ){ -- c = WO_IS; - }else{ -- assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); -- c = (u16)(WO_EQ<<(op-TK_EQ)); -+ assert( op==TK_IS ); -+ c = WO_IS; - } - assert( op!=TK_ISNULL || c==WO_ISNULL ); - assert( op!=TK_IN || c==WO_IN ); -@@ -144191,6 +162742,7 @@ static int isLikeOrGlob( - #ifdef SQLITE_EBCDIC - if( *pnoCase ) return 0; - #endif -+ assert( ExprUseXList(pExpr) ); - pList = pExpr->x.pList; - pLeft = pList->a[1].pExpr; - -@@ -144206,15 +162758,32 @@ static int isLikeOrGlob( - sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); - assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); - }else if( op==TK_STRING ){ -- z = (u8*)pRight->u.zToken; -+ assert( !ExprHasProperty(pRight, EP_IntValue) ); -+ z = (u8*)pRight->u.zToken; - } - if( z ){ -- -- /* Count the number of prefix characters prior to the first wildcard */ -+ /* Count the number of prefix bytes prior to the first wildcard, -+ ** U+fffd character, or malformed utf-8. If the underlying database -+ ** has a UTF16LE encoding, then only consider ASCII characters. Note that -+ ** the encoding of z[] is UTF8 - we are dealing with only UTF8 here in this -+ ** code, but the database engine itself might be processing content using a -+ ** different encoding. */ - cnt = 0; - while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ - cnt++; -- if( c==wc[3] && z[cnt]!=0 ) cnt++; -+ if( c==wc[3] && z[cnt]>0 && z[cnt]<0x80 ){ -+ cnt++; -+ }else if( c>=0x80 ){ -+ const u8 *z2 = z+cnt-1; -+ if( c==0xff || sqlite3Utf8Read(&z2)==0xfffd /* bad utf-8 */ -+ || ENC(db)==SQLITE_UTF16LE -+ ){ -+ cnt--; -+ break; -+ }else{ -+ cnt = (int)(z2-z); -+ } -+ } - } - - /* The optimization is possible only if (1) the pattern does not begin -@@ -144225,17 +162794,19 @@ static int isLikeOrGlob( - ** range search. The third is because the caller assumes that the pattern - ** consists of at least one character after all escapes have been - ** removed. */ -- if( cnt!=0 && 255!=(u8)z[cnt-1] && (cnt>1 || z[0]!=wc[3]) ){ -+ if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && ALWAYS(255!=(u8)z[cnt-1]) ){ - Expr *pPrefix; - - /* A "complete" match if the pattern ends with "*" or "%" */ -- *pisComplete = c==wc[0] && z[cnt+1]==0; -+ *pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE; - - /* Get the pattern prefix. Remove all escapes from the prefix. */ - pPrefix = sqlite3Expr(db, TK_STRING, (char*)z); - if( pPrefix ){ - int iFrom, iTo; -- char *zNew = pPrefix->u.zToken; -+ char *zNew; -+ assert( !ExprHasProperty(pPrefix, EP_IntValue) ); -+ zNew = pPrefix->u.zToken; - zNew[cnt] = 0; - for(iFrom=iTo=0; iFromop!=TK_COLUMN - || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT -- || IsVirtual(pLeft->y.pTab) /* Value might be numeric */ -+ || (ALWAYS( ExprUseYTab(pLeft) ) -+ && ALWAYS(pLeft->y.pTab) -+ && IsVirtual(pLeft->y.pTab)) /* Might be numeric */ - ){ - int isNum; - double rDummy; -@@ -144287,6 +162860,7 @@ static int isLikeOrGlob( - if( op==TK_VARIABLE ){ - Vdbe *v = pParse->pVdbe; - sqlite3VdbeSetVarmask(v, pRight->iColumn); -+ assert( !ExprHasProperty(pRight, EP_IntValue) ); - if( *pisComplete && pRight->u.zToken[1] ){ - /* If the rhs of the LIKE expression is a variable, and the current - ** value of the variable means there is no need to invoke the LIKE -@@ -144360,6 +162934,7 @@ static int isAuxiliaryVtabOperator( - Expr *pCol; /* Column reference */ - int i; - -+ assert( ExprUseXList(pExpr) ); - pList = pExpr->x.pList; - if( pList==0 || pList->nExpr!=2 ){ - return 0; -@@ -144373,9 +162948,10 @@ static int isAuxiliaryVtabOperator( - ** MATCH(expression,vtab_column) - */ - pCol = pList->a[1].pExpr; -- testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); -+ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); - if( ExprIsVtab(pCol) ){ - for(i=0; iu.zToken, aOp[i].zOp)==0 ){ - *peOp2 = aOp[i].eOp2; - *ppRight = pList->a[0].pExpr; -@@ -144396,7 +162972,8 @@ static int isAuxiliaryVtabOperator( - ** with function names in an arbitrary case. - */ - pCol = pList->a[0].pExpr; -- testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); -+ assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) ); -+ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); - if( ExprIsVtab(pCol) ){ - sqlite3_vtab *pVtab; - sqlite3_module *pMod; -@@ -144405,6 +162982,7 @@ static int isAuxiliaryVtabOperator( - pVtab = sqlite3GetVTable(db, pCol->y.pTab)->pVtab; - assert( pVtab!=0 ); - assert( pVtab->pModule!=0 ); -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pMod = (sqlite3_module *)pVtab->pModule; - if( pMod->xFindFunction!=0 ){ - i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed); -@@ -144416,15 +162994,23 @@ static int isAuxiliaryVtabOperator( - } - } - } -+ }else if( pExpr->op>=TK_EQ ){ -+ /* Comparison operators are a common case. Save a few comparisons for -+ ** that common case by terminating early. */ -+ assert( TK_NE < TK_EQ ); -+ assert( TK_ISNOT < TK_EQ ); -+ assert( TK_NOTNULL < TK_EQ ); -+ return 0; - }else if( pExpr->op==TK_NE || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL ){ - int res = 0; - Expr *pLeft = pExpr->pLeft; - Expr *pRight = pExpr->pRight; -- testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); -+ assert( pLeft->op!=TK_COLUMN || (ExprUseYTab(pLeft) && pLeft->y.pTab!=0) ); - if( ExprIsVtab(pLeft) ){ - res++; - } -- testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); -+ assert( pRight==0 || pRight->op!=TK_COLUMN -+ || (ExprUseYTab(pRight) && pRight->y.pTab!=0) ); - if( pRight && ExprIsVtab(pRight) ){ - res++; - SWAP(Expr*, pLeft, pRight); -@@ -144445,9 +163031,9 @@ static int isAuxiliaryVtabOperator( - ** a join, then transfer the appropriate markings over to derived. - */ - static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ -- if( pDerived ){ -- pDerived->flags |= pBase->flags & EP_FromJoin; -- pDerived->iRightJoinTable = pBase->iRightJoinTable; -+ if( pDerived && ExprHasProperty(pBase, EP_OuterON|EP_InnerON) ){ -+ pDerived->flags |= pBase->flags & (EP_OuterON|EP_InnerON); -+ pDerived->w.iJoin = pBase->w.iJoin; - } - } - -@@ -144507,6 +163093,7 @@ static void whereCombineDisjuncts( - int op; /* Operator for the combined expression */ - int idxNew; /* Index in pWC of the next virtual term */ - -+ if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; - if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; - if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; - if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp -@@ -144675,6 +163262,7 @@ static void exprAnalyzeOrTerm( - pOrTerm->u.pAndInfo = pAndInfo; - pOrTerm->wtFlags |= TERM_ANDINFO; - pOrTerm->eOperator = WO_AND; -+ pOrTerm->leftCursor = -1; - pAndWC = &pAndInfo->wc; - memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic)); - sqlite3WhereClauseInit(pAndWC, pWC->pWInfo); -@@ -144717,11 +163305,10 @@ static void exprAnalyzeOrTerm( - ** empty. - */ - pOrInfo->indexable = indexable; -+ pTerm->eOperator = WO_OR; -+ pTerm->leftCursor = -1; - if( indexable ){ -- pTerm->eOperator = WO_OR; - pWC->hasOr = 1; -- }else{ -- pTerm->eOperator = WO_OR; - } - - /* For a two-way OR, attempt to implementation case 2. -@@ -144776,7 +163363,7 @@ static void exprAnalyzeOrTerm( - pOrTerm = pOrWc->a; - for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ - assert( pOrTerm->eOperator & WO_EQ ); -- pOrTerm->wtFlags &= ~TERM_OR_OK; -+ pOrTerm->wtFlags &= ~TERM_OK; - if( pOrTerm->leftCursor==iCursor ){ - /* This is the 2-bit case and we are on the second iteration and - ** current term is from the first iteration. So skip this term. */ -@@ -144787,14 +163374,15 @@ static void exprAnalyzeOrTerm( - pOrTerm->leftCursor))==0 ){ - /* This term must be of the form t1.a==t2.b where t2 is in the - ** chngToIN set but t1 is not. This term will be either preceded -- ** or follwed by an inverted copy (t2.b==t1.a). Skip this term -+ ** or followed by an inverted copy (t2.b==t1.a). Skip this term - ** and use its inversion. */ - testcase( pOrTerm->wtFlags & TERM_COPIED ); - testcase( pOrTerm->wtFlags & TERM_VIRTUAL ); - assert( pOrTerm->wtFlags & (TERM_COPIED|TERM_VIRTUAL) ); - continue; - } -- iColumn = pOrTerm->u.leftColumn; -+ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ iColumn = pOrTerm->u.x.leftColumn; - iCursor = pOrTerm->leftCursor; - pLeft = pOrTerm->pExpr->pLeft; - break; -@@ -144814,9 +163402,10 @@ static void exprAnalyzeOrTerm( - okToChngToIN = 1; - for(; i>=0 && okToChngToIN; i--, pOrTerm++){ - assert( pOrTerm->eOperator & WO_EQ ); -+ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); - if( pOrTerm->leftCursor!=iCursor ){ -- pOrTerm->wtFlags &= ~TERM_OR_OK; -- }else if( pOrTerm->u.leftColumn!=iColumn || (iColumn==XN_EXPR -+ pOrTerm->wtFlags &= ~TERM_OK; -+ }else if( pOrTerm->u.x.leftColumn!=iColumn || (iColumn==XN_EXPR - && sqlite3ExprCompare(pParse, pOrTerm->pExpr->pLeft, pLeft, -1) - )){ - okToChngToIN = 0; -@@ -144831,7 +163420,7 @@ static void exprAnalyzeOrTerm( - if( affRight!=0 && affRight!=affLeft ){ - okToChngToIN = 0; - }else{ -- pOrTerm->wtFlags |= TERM_OR_OK; -+ pOrTerm->wtFlags |= TERM_OK; - } - } - } -@@ -144848,10 +163437,11 @@ static void exprAnalyzeOrTerm( - Expr *pNew; /* The complete IN operator */ - - for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ -- if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; -+ if( (pOrTerm->wtFlags & TERM_OK)==0 ) continue; - assert( pOrTerm->eOperator & WO_EQ ); -+ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 ); - assert( pOrTerm->leftCursor==iCursor ); -- assert( pOrTerm->u.leftColumn==iColumn ); -+ assert( pOrTerm->u.x.leftColumn==iColumn ); - pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); - pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup); - pLeft = pOrTerm->pExpr->pLeft; -@@ -144862,12 +163452,12 @@ static void exprAnalyzeOrTerm( - if( pNew ){ - int idxNew; - transferJoinMarkings(pNew, pExpr); -- assert( !ExprHasProperty(pNew, EP_xIsSelect) ); -+ assert( ExprUseXList(pNew) ); - pNew->x.pList = pList; - idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); - testcase( idxNew==0 ); - exprAnalyze(pSrc, pWC, idxNew); -- /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */ -+ /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where reused */ - markTermAsChild(pWC, idxNew, idxTerm); - }else{ - sqlite3ExprListDelete(db, pList); -@@ -144884,30 +163474,42 @@ static void exprAnalyzeOrTerm( - ** 1. The SQLITE_Transitive optimization must be enabled - ** 2. Must be either an == or an IS operator - ** 3. Not originating in the ON clause of an OUTER JOIN --** 4. The affinities of A and B must be compatible --** 5a. Both operands use the same collating sequence OR --** 5b. The overall collating sequence is BINARY -+** 4. The operator is not IS or else the query does not contain RIGHT JOIN -+** 5. The affinities of A and B must be compatible -+** 6a. Both operands use the same collating sequence OR -+** 6b. The overall collating sequence is BINARY - ** If this routine returns TRUE, that means that the RHS can be substituted - ** for the LHS anyplace else in the WHERE clause where the LHS column occurs. - ** This is an optimization. No harm comes from returning 0. But if 1 is - ** returned when it should not be, then incorrect answers might result. - */ --static int termIsEquivalence(Parse *pParse, Expr *pExpr){ -+static int termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){ - char aff1, aff2; - CollSeq *pColl; -- if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; -- if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; -- if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0; -+ if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; /* (1) */ -+ if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; /* (2) */ -+ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* (3) */ -+ assert( pSrc!=0 ); -+ if( pExpr->op==TK_IS -+ && pSrc->nSrc -+ && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 -+ ){ -+ return 0; /* (4) */ -+ } - aff1 = sqlite3ExprAffinity(pExpr->pLeft); - aff2 = sqlite3ExprAffinity(pExpr->pRight); - if( aff1!=aff2 - && (!sqlite3IsNumericAffinity(aff1) || !sqlite3IsNumericAffinity(aff2)) - ){ -- return 0; -+ return 0; /* (5) */ - } - pColl = sqlite3ExprCompareCollSeq(pParse, pExpr); -- if( sqlite3IsBinary(pColl) ) return 1; -- return sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight); -+ if( !sqlite3IsBinary(pColl) -+ && !sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight) -+ ){ -+ return 0; /* (6) */ -+ } -+ return 1; - } - - /* -@@ -144927,8 +163529,12 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ - if( ALWAYS(pSrc!=0) ){ - int i; - for(i=0; inSrc; i++){ -- mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); -- mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn); -+ if( pSrc->a[i].fg.isSubquery ){ -+ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].u4.pSubq->pSelect); -+ } -+ if( pSrc->a[i].fg.isUsing==0 ){ -+ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn); -+ } - if( pSrc->a[i].fg.isTabFunc ){ - mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); - } -@@ -144954,35 +163560,40 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ - */ - static SQLITE_NOINLINE int exprMightBeIndexed2( - SrcList *pFrom, /* The FROM clause */ -- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ - int *aiCurCol, /* Write the referenced table cursor and column here */ -- Expr *pExpr /* An operand of a comparison operator */ -+ Expr *pExpr, /* An operand of a comparison operator */ -+ int j /* Start looking with the j-th pFrom entry */ - ){ - Index *pIdx; - int i; - int iCur; -- for(i=0; mPrereq>1; i++, mPrereq>>=1){} -- iCur = pFrom->a[i].iCursor; -- for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ -- if( pIdx->aColExpr==0 ) continue; -- for(i=0; inKeyCol; i++){ -- if( pIdx->aiColumn[i]!=XN_EXPR ) continue; -- if( sqlite3ExprCompareSkip(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){ -- aiCurCol[0] = iCur; -- aiCurCol[1] = XN_EXPR; -- return 1; -+ do{ -+ iCur = pFrom->a[j].iCursor; -+ for(pIdx=pFrom->a[j].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){ -+ if( pIdx->aColExpr==0 ) continue; -+ for(i=0; inKeyCol; i++){ -+ if( pIdx->aiColumn[i]!=XN_EXPR ) continue; -+ assert( pIdx->bHasExpr ); -+ if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0 -+ && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr) -+ ){ -+ aiCurCol[0] = iCur; -+ aiCurCol[1] = XN_EXPR; -+ return 1; -+ } - } - } -- } -+ }while( ++j < pFrom->nSrc ); - return 0; - } - static int exprMightBeIndexed( - SrcList *pFrom, /* The FROM clause */ -- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */ - int *aiCurCol, /* Write the referenced table cursor & column here */ - Expr *pExpr, /* An operand of a comparison operator */ - int op /* The specific comparison operator */ - ){ -+ int i; -+ - /* If this expression is a vector to the left or right of a - ** inequality constraint (>, <, >= or <=), perform the processing - ** on the first element of the vector. */ -@@ -144990,6 +163601,7 @@ static int exprMightBeIndexed( - assert( TK_ISop==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){ -+ assert( ExprUseXList(pExpr) ); - pExpr = pExpr->x.pList->a[0].pExpr; - } - -@@ -144998,11 +163610,19 @@ static int exprMightBeIndexed( - aiCurCol[1] = pExpr->iColumn; - return 1; - } -- if( mPrereq==0 ) return 0; /* No table references */ -- if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */ -- return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr); -+ -+ for(i=0; inSrc; i++){ -+ Index *pIdx; -+ for(pIdx=pFrom->a[i].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){ -+ if( pIdx->aColExpr ){ -+ return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i); -+ } -+ } -+ } -+ return 0; - } - -+ - /* - ** The input to this routine is an WhereTerm structure with only the - ** "pExpr" field filled in. The job of this routine is to analyze the -@@ -145030,8 +163650,8 @@ static void exprAnalyze( - WhereTerm *pTerm; /* The term to be analyzed */ - WhereMaskSet *pMaskSet; /* Set of table index masks */ - Expr *pExpr; /* The expression to be analyzed */ -- Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ -- Bitmask prereqAll; /* Prerequesites of pExpr */ -+ Bitmask prereqLeft; /* Prerequisites of the pExpr->pLeft */ -+ Bitmask prereqAll; /* Prerequisites of pExpr */ - Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ - Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ - int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ -@@ -145045,36 +163665,67 @@ static void exprAnalyze( - if( db->mallocFailed ){ - return; - } -+ assert( pWC->nTerm > idxTerm ); - pTerm = &pWC->a[idxTerm]; - pMaskSet = &pWInfo->sMaskSet; - pExpr = pTerm->pExpr; -+ assert( pExpr!=0 ); /* Because malloc() has not failed */ - assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); -+ pMaskSet->bVarSelect = 0; - prereqLeft = sqlite3WhereExprUsage(pMaskSet, pExpr->pLeft); - op = pExpr->op; - if( op==TK_IN ){ - assert( pExpr->pRight==0 ); - if( sqlite3ExprCheckIN(pParse, pExpr) ) return; -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect); - }else{ - pTerm->prereqRight = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList); - } -- }else if( op==TK_ISNULL ){ -- pTerm->prereqRight = 0; -+ prereqAll = prereqLeft | pTerm->prereqRight; - }else{ - pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight); -+ if( pExpr->pLeft==0 -+ || ExprHasProperty(pExpr, EP_xIsSelect|EP_IfNullRow) -+ || pExpr->x.pList!=0 -+ ){ -+ prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr); -+ }else{ -+ prereqAll = prereqLeft | pTerm->prereqRight; -+ } - } -- pMaskSet->bVarSelect = 0; -- prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr); - if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT; -- if( ExprHasProperty(pExpr, EP_FromJoin) ){ -- Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); -- prereqAll |= x; -- extraRight = x-1; /* ON clause terms may not be used with an index -- ** on left table of a LEFT JOIN. Ticket #3015 */ -- if( (prereqAll>>1)>=x ){ -- sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); -- return; -+ -+#ifdef SQLITE_DEBUG -+ if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){ -+ printf("\n*** Incorrect prereqAll computed for:\n"); -+ sqlite3TreeViewExpr(0,pExpr,0); -+ assert( 0 ); -+ } -+#endif -+ -+ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){ -+ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin); -+ if( ExprHasProperty(pExpr, EP_OuterON) ){ -+ prereqAll |= x; -+ extraRight = x-1; /* ON clause terms may not be used with an index -+ ** on left table of a LEFT JOIN. Ticket #3015 */ -+ if( (prereqAll>>1)>=x ){ -+ sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); -+ return; -+ } -+ }else if( (prereqAll>>1)>=x ){ -+ /* The ON clause of an INNER JOIN references a table to its right. -+ ** Most other SQL database engines raise an error. But SQLite versions -+ ** 3.0 through 3.38 just put the ON clause constraint into the WHERE -+ ** clause and carried on. Beginning with 3.39, raise an error only -+ ** if there is a RIGHT or FULL JOIN in the query. This makes SQLite -+ ** more like other systems, and also preserves legacy. */ -+ if( ALWAYS(pSrc->nSrc>0) && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){ -+ sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); -+ return; -+ } -+ ExprClearProperty(pExpr, EP_InnerON); - } - } - pTerm->prereqAll = prereqAll; -@@ -145087,25 +163738,28 @@ static void exprAnalyze( - Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); - u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; - -- if( pTerm->iField>0 ){ -+ if( pTerm->u.x.iField>0 ){ - assert( op==TK_IN ); - assert( pLeft->op==TK_VECTOR ); -- pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr; -+ assert( ExprUseXList(pLeft) ); -+ pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr; - } - -- if( exprMightBeIndexed(pSrc, prereqLeft, aiCurCol, pLeft, op) ){ -+ if( exprMightBeIndexed(pSrc, aiCurCol, pLeft, op) ){ - pTerm->leftCursor = aiCurCol[0]; -- pTerm->u.leftColumn = aiCurCol[1]; -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ pTerm->u.x.leftColumn = aiCurCol[1]; - pTerm->eOperator = operatorMask(op) & opMask; - } - if( op==TK_IS ) pTerm->wtFlags |= TERM_IS; - if( pRight -- && exprMightBeIndexed(pSrc, pTerm->prereqRight, aiCurCol, pRight, op) -+ && exprMightBeIndexed(pSrc, aiCurCol, pRight, op) -+ && !ExprHasProperty(pRight, EP_FixedCol) - ){ - WhereTerm *pNew; - Expr *pDup; - u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ -- assert( pTerm->iField==0 ); -+ assert( pTerm->u.x.iField==0 ); - if( pTerm->leftCursor>=0 ){ - int idxNew; - pDup = sqlite3ExprDup(db, pExpr, 0); -@@ -145120,8 +163774,8 @@ static void exprAnalyze( - if( op==TK_IS ) pNew->wtFlags |= TERM_IS; - pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags |= TERM_COPIED; -- -- if( termIsEquivalence(pParse, pDup) ){ -+ assert( pWInfo->pTabList!=0 ); -+ if( termIsEquivalence(pParse, pDup, pWInfo->pTabList) ){ - pTerm->eOperator |= WO_EQUIV; - eExtraOp = WO_EQUIV; - } -@@ -145131,11 +163785,23 @@ static void exprAnalyze( - } - pNew->wtFlags |= exprCommute(pParse, pDup); - pNew->leftCursor = aiCurCol[0]; -- pNew->u.leftColumn = aiCurCol[1]; -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ pNew->u.x.leftColumn = aiCurCol[1]; - testcase( (prereqLeft | extraRight) != prereqLeft ); - pNew->prereqRight = prereqLeft | extraRight; - pNew->prereqAll = prereqAll; - pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; -+ }else -+ if( op==TK_ISNULL -+ && !ExprHasProperty(pExpr,EP_OuterON) -+ && 0==sqlite3ExprCanBeNull(pLeft) -+ ){ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */ -+ pExpr->u.zToken = "false"; -+ ExprSetProperty(pExpr, EP_IsFalse); -+ pTerm->prereqAll = 0; -+ pTerm->eOperator = 0; - } - } - -@@ -145156,9 +163822,11 @@ static void exprAnalyze( - ** BETWEEN term is skipped. - */ - else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ -- ExprList *pList = pExpr->x.pList; -+ ExprList *pList; - int i; - static const u8 ops[] = {TK_GE, TK_LE}; -+ assert( ExprUseXList(pExpr) ); -+ pList = pExpr->x.pList; - assert( pList!=0 ); - assert( pList->nExpr==2 ); - for(i=0; i<2; i++){ -@@ -145187,6 +163855,42 @@ static void exprAnalyze( - pTerm = &pWC->a[idxTerm]; - } - #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ -+ /* The form "x IS NOT NULL" can sometimes be evaluated more efficiently -+ ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a -+ ** virtual term of that form. -+ ** -+ ** The virtual term must be tagged with TERM_VNULL. -+ */ -+ else if( pExpr->op==TK_NOTNULL ){ -+ if( pExpr->pLeft->op==TK_COLUMN -+ && pExpr->pLeft->iColumn>=0 -+ && !ExprHasProperty(pExpr, EP_OuterON) -+ ){ -+ Expr *pNewExpr; -+ Expr *pLeft = pExpr->pLeft; -+ int idxNew; -+ WhereTerm *pNewTerm; -+ -+ pNewExpr = sqlite3PExpr(pParse, TK_GT, -+ sqlite3ExprDup(db, pLeft, 0), -+ sqlite3ExprAlloc(db, TK_NULL, 0, 0)); -+ -+ idxNew = whereClauseInsert(pWC, pNewExpr, -+ TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); -+ if( idxNew ){ -+ pNewTerm = &pWC->a[idxNew]; -+ pNewTerm->prereqRight = 0; -+ pNewTerm->leftCursor = pLeft->iTable; -+ pNewTerm->u.x.leftColumn = pLeft->iColumn; -+ pNewTerm->eOperator = WO_GT; -+ markTermAsChild(pWC, idxNew, idxTerm); -+ pTerm = &pWC->a[idxTerm]; -+ pTerm->wtFlags |= TERM_COPIED; -+ pNewTerm->prereqAll = pTerm->prereqAll; -+ } -+ } -+ } -+ - - #ifndef SQLITE_OMIT_LIKE_OPTIMIZATION - /* Add constraints to reduce the search space on a LIKE or GLOB -@@ -145202,7 +163906,8 @@ static void exprAnalyze( - ** bound is made all lowercase so that the bounds also work when comparing - ** BLOBs. - */ -- if( pWC->op==TK_AND -+ else if( pExpr->op==TK_FUNCTION -+ && pWC->op==TK_AND - && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) - ){ - Expr *pLeft; /* LHS of LIKE/GLOB operator */ -@@ -145214,8 +163919,12 @@ static void exprAnalyze( - const char *zCollSeqName; /* Name of collating sequence */ - const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC; - -+ assert( ExprUseXList(pExpr) ); - pLeft = pExpr->x.pList->a[1].pExpr; - pStr2 = sqlite3ExprDup(db, pStr1, 0); -+ assert( pStr1==0 || !ExprHasProperty(pStr1, EP_IntValue) ); -+ assert( pStr2==0 || !ExprHasProperty(pStr2, EP_IntValue) ); -+ - - /* Convert the lower bound to upper-case and the upper bound to - ** lower-case (upper-case is less than lower-case in ASCII) so that -@@ -145232,9 +163941,8 @@ static void exprAnalyze( - } - - if( !db->mallocFailed ){ -- u8 c, *pC; /* Last character before the first wildcard */ -+ u8 *pC; /* Last character before the first wildcard */ - pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; -- c = *pC; - if( noCase ){ - /* The point is to increment the last character before the first - ** wildcard. But if we increment '@', that will push it into the -@@ -145242,10 +163950,17 @@ static void exprAnalyze( - ** inequality. To avoid this, make sure to also run the full - ** LIKE on all candidate expressions by clearing the isComplete flag - */ -- if( c=='A'-1 ) isComplete = 0; -- c = sqlite3UpperToLower[c]; -+ if( *pC=='A'-1 ) isComplete = 0; -+ *pC = sqlite3UpperToLower[*pC]; -+ } -+ -+ /* Increment the value of the last utf8 character in the prefix. */ -+ while( *pC==0xBF && pC>(u8*)pStr2->u.zToken ){ -+ *pC = 0x80; -+ pC--; - } -- *pC = c + 1; -+ assert( *pC!=0xFF ); /* isLikeOrGlob() guarantees this */ -+ (*pC)++; - } - zCollSeqName = noCase ? "NOCASE" : sqlite3StrBINARY; - pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); -@@ -145255,7 +163970,6 @@ static void exprAnalyze( - transferJoinMarkings(pNewExpr1, pExpr); - idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags); - testcase( idxNew1==0 ); -- exprAnalyze(pSrc, pWC, idxNew1); - pNewExpr2 = sqlite3ExprDup(db, pLeft, 0); - pNewExpr2 = sqlite3PExpr(pParse, TK_LT, - sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName), -@@ -145263,6 +163977,7 @@ static void exprAnalyze( - transferJoinMarkings(pNewExpr2, pExpr); - idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags); - testcase( idxNew2==0 ); -+ exprAnalyze(pSrc, pWC, idxNew1); - exprAnalyze(pSrc, pWC, idxNew2); - pTerm = &pWC->a[idxTerm]; - if( isComplete ){ -@@ -145272,147 +163987,114 @@ static void exprAnalyze( - } - #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ - --#ifndef SQLITE_OMIT_VIRTUALTABLE -- /* Add a WO_AUX auxiliary term to the constraint set if the -- ** current expression is of the form "column OP expr" where OP -- ** is an operator that gets passed into virtual tables but which is -- ** not normally optimized for ordinary tables. In other words, OP -- ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL. -- ** This information is used by the xBestIndex methods of -- ** virtual tables. The native query optimizer does not attempt -- ** to do anything with MATCH functions. -- */ -- if( pWC->op==TK_AND ){ -- Expr *pRight = 0, *pLeft = 0; -- int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight); -- while( res-- > 0 ){ -- int idxNew; -- WhereTerm *pNewTerm; -- Bitmask prereqColumn, prereqExpr; -- -- prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight); -- prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft); -- if( (prereqExpr & prereqColumn)==0 ){ -- Expr *pNewExpr; -- pNewExpr = sqlite3PExpr(pParse, TK_MATCH, -- 0, sqlite3ExprDup(db, pRight, 0)); -- if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){ -- ExprSetProperty(pNewExpr, EP_FromJoin); -- pNewExpr->iRightJoinTable = pExpr->iRightJoinTable; -- } -- idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); -- testcase( idxNew==0 ); -- pNewTerm = &pWC->a[idxNew]; -- pNewTerm->prereqRight = prereqExpr; -- pNewTerm->leftCursor = pLeft->iTable; -- pNewTerm->u.leftColumn = pLeft->iColumn; -- pNewTerm->eOperator = WO_AUX; -- pNewTerm->eMatchOp = eOp2; -- markTermAsChild(pWC, idxNew, idxTerm); -- pTerm = &pWC->a[idxTerm]; -- pTerm->wtFlags |= TERM_COPIED; -- pNewTerm->prereqAll = pTerm->prereqAll; -- } -- SWAP(Expr*, pLeft, pRight); -- } -- } --#endif /* SQLITE_OMIT_VIRTUALTABLE */ -- - /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create - ** new terms for each component comparison - "a = ?" and "b = ?". The - ** new terms completely replace the original vector comparison, which is - ** no longer used. - ** - ** This is only required if at least one side of the comparison operation -- ** is not a sub-select. */ -- if( pWC->op==TK_AND -- && (pExpr->op==TK_EQ || pExpr->op==TK_IS) -- && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1 -- && sqlite3ExprVectorSize(pExpr->pRight)==nLeft -- && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 -- || (pExpr->pRight->flags & EP_xIsSelect)==0) -+ ** is not a sub-select. -+ ** -+ ** tag-20220128a -+ */ -+ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS) -+ && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1 -+ && sqlite3ExprVectorSize(pExpr->pRight)==nLeft -+ && ( (pExpr->pLeft->flags & EP_xIsSelect)==0 -+ || (pExpr->pRight->flags & EP_xIsSelect)==0) -+ && pWC->op==TK_AND - ){ - int i; - for(i=0; ipLeft, i); -- Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i); -+ Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, nLeft); -+ Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i, nLeft); - - pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight); - transferJoinMarkings(pNew, pExpr); -- idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); -+ idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_SLICE); - exprAnalyze(pSrc, pWC, idxNew); - } - pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */ -- pTerm->eOperator = 0; -+ pTerm->eOperator = WO_ROWVAL; - } - - /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create - ** a virtual term for each vector component. The expression object - ** used by each such virtual term is pExpr (the full vector IN(...) -- ** expression). The WhereTerm.iField variable identifies the index within -+ ** expression). The WhereTerm.u.x.iField variable identifies the index within - ** the vector on the LHS that the virtual term represents. - ** - ** This only works if the RHS is a simple SELECT (not a compound) that does - ** not use window functions. - */ -- if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0 -+ else if( pExpr->op==TK_IN -+ && pTerm->u.x.iField==0 - && pExpr->pLeft->op==TK_VECTOR -- && pExpr->x.pSelect->pPrior==0 -+ && ALWAYS( ExprUseXSelect(pExpr) ) -+ && (pExpr->x.pSelect->pPrior==0 || (pExpr->x.pSelect->selFlags & SF_Values)) - #ifndef SQLITE_OMIT_WINDOWFUNC - && pExpr->x.pSelect->pWin==0 - #endif -+ && pWC->op==TK_AND - ){ - int i; - for(i=0; ipLeft); i++){ - int idxNew; -- idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL); -- pWC->a[idxNew].iField = i+1; -+ idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL|TERM_SLICE); -+ pWC->a[idxNew].u.x.iField = i+1; - exprAnalyze(pSrc, pWC, idxNew); - markTermAsChild(pWC, idxNew, idxTerm); - } - } - --#ifdef SQLITE_ENABLE_STAT4 -- /* When sqlite_stat4 histogram data is available an operator of the -- ** form "x IS NOT NULL" can sometimes be evaluated more efficiently -- ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a -- ** virtual term of that form. -- ** -- ** Note that the virtual term must be tagged with TERM_VNULL. -+#ifndef SQLITE_OMIT_VIRTUALTABLE -+ /* Add a WO_AUX auxiliary term to the constraint set if the -+ ** current expression is of the form "column OP expr" where OP -+ ** is an operator that gets passed into virtual tables but which is -+ ** not normally optimized for ordinary tables. In other words, OP -+ ** is one of MATCH, LIKE, GLOB, REGEXP, !=, IS, IS NOT, or NOT NULL. -+ ** This information is used by the xBestIndex methods of -+ ** virtual tables. The native query optimizer does not attempt -+ ** to do anything with MATCH functions. - */ -- if( pExpr->op==TK_NOTNULL -- && pExpr->pLeft->op==TK_COLUMN -- && pExpr->pLeft->iColumn>=0 -- && !ExprHasProperty(pExpr, EP_FromJoin) -- && OptimizationEnabled(db, SQLITE_Stat4) -- ){ -- Expr *pNewExpr; -- Expr *pLeft = pExpr->pLeft; -- int idxNew; -- WhereTerm *pNewTerm; -- -- pNewExpr = sqlite3PExpr(pParse, TK_GT, -- sqlite3ExprDup(db, pLeft, 0), -- sqlite3ExprAlloc(db, TK_NULL, 0, 0)); -- -- idxNew = whereClauseInsert(pWC, pNewExpr, -- TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); -- if( idxNew ){ -- pNewTerm = &pWC->a[idxNew]; -- pNewTerm->prereqRight = 0; -- pNewTerm->leftCursor = pLeft->iTable; -- pNewTerm->u.leftColumn = pLeft->iColumn; -- pNewTerm->eOperator = WO_GT; -- markTermAsChild(pWC, idxNew, idxTerm); -- pTerm = &pWC->a[idxTerm]; -- pTerm->wtFlags |= TERM_COPIED; -- pNewTerm->prereqAll = pTerm->prereqAll; -+ else if( pWC->op==TK_AND ){ -+ Expr *pRight = 0, *pLeft = 0; -+ int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight); -+ while( res-- > 0 ){ -+ int idxNew; -+ WhereTerm *pNewTerm; -+ Bitmask prereqColumn, prereqExpr; -+ -+ prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight); -+ prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft); -+ if( (prereqExpr & prereqColumn)==0 ){ -+ Expr *pNewExpr; -+ pNewExpr = sqlite3PExpr(pParse, TK_MATCH, -+ 0, sqlite3ExprDup(db, pRight, 0)); -+ if( ExprHasProperty(pExpr, EP_OuterON) && pNewExpr ){ -+ ExprSetProperty(pNewExpr, EP_OuterON); -+ pNewExpr->w.iJoin = pExpr->w.iJoin; -+ } -+ idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); -+ testcase( idxNew==0 ); -+ pNewTerm = &pWC->a[idxNew]; -+ pNewTerm->prereqRight = prereqExpr; -+ pNewTerm->leftCursor = pLeft->iTable; -+ pNewTerm->u.x.leftColumn = pLeft->iColumn; -+ pNewTerm->eOperator = WO_AUX; -+ pNewTerm->eMatchOp = eOp2; -+ markTermAsChild(pWC, idxNew, idxTerm); -+ pTerm = &pWC->a[idxTerm]; -+ pTerm->wtFlags |= TERM_COPIED; -+ pNewTerm->prereqAll = pTerm->prereqAll; -+ } -+ SWAP(Expr*, pLeft, pRight); - } - } --#endif /* SQLITE_ENABLE_STAT4 */ -+#endif /* SQLITE_OMIT_VIRTUALTABLE */ - - /* Prevent ON clause terms of a LEFT JOIN from being used to drive - ** an index for tables to the left of the join. -@@ -145447,6 +164129,7 @@ static void exprAnalyze( - SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ - Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); - pWC->op = op; -+ assert( pE2!=0 || pExpr==0 ); - if( pE2==0 ) return; - if( pE2->op!=op ){ - whereClauseInsert(pWC, pExpr, 0); -@@ -145456,6 +164139,123 @@ SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ - } - } - -+/* -+** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or -+** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the -+** where-clause passed as the first argument. The value for the term -+** is found in register iReg. -+** -+** In the common case where the value is a simple integer -+** (example: "LIMIT 5 OFFSET 10") then the expression codes as a -+** TK_INTEGER so that it will be available to sqlite3_vtab_rhs_value(). -+** If not, then it codes as a TK_REGISTER expression. -+*/ -+static void whereAddLimitExpr( -+ WhereClause *pWC, /* Add the constraint to this WHERE clause */ -+ int iReg, /* Register that will hold value of the limit/offset */ -+ Expr *pExpr, /* Expression that defines the limit/offset */ -+ int iCsr, /* Cursor to which the constraint applies */ -+ int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */ -+){ -+ Parse *pParse = pWC->pWInfo->pParse; -+ sqlite3 *db = pParse->db; -+ Expr *pNew; -+ int iVal = 0; -+ -+ if( sqlite3ExprIsInteger(pExpr, &iVal, pParse) && iVal>=0 ){ -+ Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); -+ if( pVal==0 ) return; -+ ExprSetProperty(pVal, EP_IntValue); -+ pVal->u.iValue = iVal; -+ pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); -+ }else{ -+ Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0); -+ if( pVal==0 ) return; -+ pVal->iTable = iReg; -+ pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); -+ } -+ if( pNew ){ -+ WhereTerm *pTerm; -+ int idx; -+ idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL); -+ pTerm = &pWC->a[idx]; -+ pTerm->leftCursor = iCsr; -+ pTerm->eOperator = WO_AUX; -+ pTerm->eMatchOp = eMatchOp; -+ } -+} -+ -+/* -+** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the -+** SELECT statement passed as the second argument. These terms are only -+** added if: -+** -+** 1. The SELECT statement has a LIMIT clause, and -+** 2. The SELECT statement is not an aggregate or DISTINCT query, and -+** 3. The SELECT statement has exactly one object in its from clause, and -+** that object is a virtual table, and -+** 4. There are no terms in the WHERE clause that will not be passed -+** to the virtual table xBestIndex method. -+** 5. The ORDER BY clause, if any, will be made available to the xBestIndex -+** method. -+** -+** LIMIT and OFFSET terms are ignored by most of the planner code. They -+** exist only so that they may be passed to the xBestIndex method of the -+** single virtual table in the FROM clause of the SELECT. -+*/ -+SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ -+ assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */ -+ if( p->pGroupBy==0 -+ && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ -+ && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pSTab)) /* 3 */ -+ ){ -+ ExprList *pOrderBy = p->pOrderBy; -+ int iCsr = p->pSrc->a[0].iCursor; -+ int ii; -+ -+ /* Check condition (4). Return early if it is not met. */ -+ for(ii=0; iinTerm; ii++){ -+ if( pWC->a[ii].wtFlags & TERM_CODED ){ -+ /* This term is a vector operation that has been decomposed into -+ ** other, subsequent terms. It can be ignored. See tag-20220128a */ -+ assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); -+ assert( pWC->a[ii].eOperator==WO_ROWVAL ); -+ continue; -+ } -+ if( pWC->a[ii].nChild ){ -+ /* If this term has child terms, then they are also part of the -+ ** pWC->a[] array. So this term can be ignored, as a LIMIT clause -+ ** will only be added if each of the child terms passes the -+ ** (leftCursor==iCsr) test below. */ -+ continue; -+ } -+ if( pWC->a[ii].leftCursor!=iCsr ) return; -+ if( pWC->a[ii].prereqRight!=0 ) return; -+ } -+ -+ /* Check condition (5). Return early if it is not met. */ -+ if( pOrderBy ){ -+ for(ii=0; iinExpr; ii++){ -+ Expr *pExpr = pOrderBy->a[ii].pExpr; -+ if( pExpr->op!=TK_COLUMN ) return; -+ if( pExpr->iTable!=iCsr ) return; -+ if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return; -+ } -+ } -+ -+ /* All conditions are met. Add the terms to the where-clause object. */ -+ assert( p->pLimit->op==TK_LIMIT ); -+ if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){ -+ whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, -+ iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); -+ } -+ if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){ -+ whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, -+ iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); -+ } -+ } -+} -+ - /* - ** Initialize a preallocated WhereClause structure. - */ -@@ -145467,6 +164267,7 @@ SQLITE_PRIVATE void sqlite3WhereClauseInit( - pWC->hasOr = 0; - pWC->pOuter = 0; - pWC->nTerm = 0; -+ pWC->nBase = 0; - pWC->nSlot = ArraySize(pWC->aStatic); - pWC->a = pWC->aStatic; - } -@@ -145477,22 +164278,36 @@ SQLITE_PRIVATE void sqlite3WhereClauseInit( - ** sqlite3WhereClauseInit(). - */ - SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){ -- int i; -- WhereTerm *a; - sqlite3 *db = pWC->pWInfo->pParse->db; -- for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){ -- if( a->wtFlags & TERM_DYNAMIC ){ -- sqlite3ExprDelete(db, a->pExpr); -+ assert( pWC->nTerm>=pWC->nBase ); -+ if( pWC->nTerm>0 ){ -+ WhereTerm *a = pWC->a; -+ WhereTerm *aLast = &pWC->a[pWC->nTerm-1]; -+#ifdef SQLITE_DEBUG -+ int i; -+ /* Verify that every term past pWC->nBase is virtual */ -+ for(i=pWC->nBase; inTerm; i++){ -+ assert( (pWC->a[i].wtFlags & TERM_VIRTUAL)!=0 ); - } -- if( a->wtFlags & TERM_ORINFO ){ -- whereOrInfoDelete(db, a->u.pOrInfo); -- }else if( a->wtFlags & TERM_ANDINFO ){ -- whereAndInfoDelete(db, a->u.pAndInfo); -+#endif -+ while(1){ -+ assert( a->eMatchOp==0 || a->eOperator==WO_AUX ); -+ if( a->wtFlags & TERM_DYNAMIC ){ -+ sqlite3ExprDelete(db, a->pExpr); -+ } -+ if( a->wtFlags & (TERM_ORINFO|TERM_ANDINFO) ){ -+ if( a->wtFlags & TERM_ORINFO ){ -+ assert( (a->wtFlags & TERM_ANDINFO)==0 ); -+ whereOrInfoDelete(db, a->u.pOrInfo); -+ }else{ -+ assert( (a->wtFlags & TERM_ANDINFO)!=0 ); -+ whereAndInfoDelete(db, a->u.pAndInfo); -+ } -+ } -+ if( a==aLast ) break; -+ a++; - } - } -- if( pWC->a!=pWC->aStatic ){ -- sqlite3DbFree(db, pWC->a); -- } - } - - -@@ -145500,28 +164315,52 @@ SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){ - ** These routines walk (recursively) an expression tree and generate - ** a bitmask indicating which tables are used in that expression - ** tree. -+** -+** sqlite3WhereExprUsage(MaskSet, Expr) -> -+** -+** Return a Bitmask of all tables referenced by Expr. Expr can be -+** be NULL, in which case 0 is returned. -+** -+** sqlite3WhereExprUsageNN(MaskSet, Expr) -> -+** -+** Same as sqlite3WhereExprUsage() except that Expr must not be -+** NULL. The "NN" suffix on the name stands for "Not Null". -+** -+** sqlite3WhereExprListUsage(MaskSet, ExprList) -> -+** -+** Return a Bitmask of all tables referenced by every expression -+** in the expression list ExprList. ExprList can be NULL, in which -+** case 0 is returned. -+** -+** sqlite3WhereExprUsageFull(MaskSet, ExprList) -> -+** -+** Internal use only. Called only by sqlite3WhereExprUsageNN() for -+** complex expressions that require pushing register values onto -+** the stack. Many calls to sqlite3WhereExprUsageNN() do not need -+** the more complex analysis done by this routine. Hence, the -+** computations done by this routine are broken out into a separate -+** "no-inline" function to avoid the stack push overhead in the -+** common case where it is not needed. - */ --SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ -+static SQLITE_NOINLINE Bitmask sqlite3WhereExprUsageFull( -+ WhereMaskSet *pMaskSet, -+ Expr *p -+){ - Bitmask mask; -- if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ -- return sqlite3WhereGetMask(pMaskSet, p->iTable); -- }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ -- assert( p->op!=TK_IF_NULL_ROW ); -- return 0; -- } - mask = (p->op==TK_IF_NULL_ROW) ? sqlite3WhereGetMask(pMaskSet, p->iTable) : 0; - if( p->pLeft ) mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pLeft); - if( p->pRight ){ - mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pRight); - assert( p->x.pList==0 ); -- }else if( ExprHasProperty(p, EP_xIsSelect) ){ -+ }else if( ExprUseXSelect(p) ){ - if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1; - mask |= exprSelectUsage(pMaskSet, p->x.pSelect); - }else if( p->x.pList ){ - mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList); - } - #ifndef SQLITE_OMIT_WINDOWFUNC -- if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && p->y.pWin ){ -+ if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && ExprUseYWin(p) ){ -+ assert( p->y.pWin!=0 ); - mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition); - mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy); - mask |= sqlite3WhereExprUsage(pMaskSet, p->y.pWin->pFilter); -@@ -145529,6 +164368,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ - #endif - return mask; - } -+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){ -+ if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ -+ return sqlite3WhereGetMask(pMaskSet, p->iTable); -+ }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ -+ assert( p->op!=TK_IF_NULL_ROW ); -+ return 0; -+ } -+ return sqlite3WhereExprUsageFull(pMaskSet, p); -+} - SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ - return p ? sqlite3WhereExprUsageNN(pMaskSet,p) : 0; - } -@@ -145571,7 +164419,7 @@ SQLITE_PRIVATE void sqlite3WhereExprAnalyze( - */ - SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( - Parse *pParse, /* Parsing context */ -- struct SrcList_item *pItem, /* The FROM clause term to process */ -+ SrcItem *pItem, /* The FROM clause term to process */ - WhereClause *pWC /* Xfer function arguments to here */ - ){ - Table *pTab; -@@ -145580,12 +164428,13 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( - Expr *pColRef; - Expr *pTerm; - if( pItem->fg.isTabFunc==0 ) return; -- pTab = pItem->pTab; -+ pTab = pItem->pSTab; - assert( pTab!=0 ); - pArgs = pItem->u1.pFuncArg; - if( pArgs==0 ) return; - for(j=k=0; jnExpr; j++){ - Expr *pRhs; -+ u32 joinType; - while( knCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;} - if( k>=pTab->nCol ){ - sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d", -@@ -145596,13 +164445,21 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( - if( pColRef==0 ) return; - pColRef->iTable = pItem->iCursor; - pColRef->iColumn = k++; -+ assert( ExprUseYTab(pColRef) ); - pColRef->y.pTab = pTab; -+ pItem->colUsed |= sqlite3ExprColUsed(pColRef); - pRhs = sqlite3PExpr(pParse, TK_UPLUS, - sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); - pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); -- if( pItem->fg.jointype & JT_LEFT ){ -- sqlite3SetJoinExpr(pTerm, pItem->iCursor); -+ if( pItem->fg.jointype & (JT_LEFT|JT_RIGHT) ){ -+ testcase( pItem->fg.jointype & JT_LEFT ); /* testtag-20230227a */ -+ testcase( pItem->fg.jointype & JT_RIGHT ); /* testtag-20230227b */ -+ joinType = EP_OuterON; -+ }else{ -+ testcase( pItem->fg.jointype & JT_LTORJ ); /* testtag-20230227c */ -+ joinType = EP_InnerON; - } -+ sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType); - whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); - } - } -@@ -145641,19 +164498,24 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( - */ - typedef struct HiddenIndexInfo HiddenIndexInfo; - struct HiddenIndexInfo { -- WhereClause *pWC; /* The Where clause being analyzed */ -- Parse *pParse; /* The parsing context */ -+ WhereClause *pWC; /* The Where clause being analyzed */ -+ Parse *pParse; /* The parsing context */ -+ int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ -+ u32 mIn; /* Mask of terms that are IN (...) */ -+ u32 mHandleIn; /* Terms that vtab will handle as IN (...) */ -+ sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST -+ ** Extra space is allocated to hold up -+ ** to nTerm such values */ - }; - -+/* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as -+** many as N constraints */ -+#define SZ_HIDDENINDEXINFO(N) \ -+ (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*)) -+ - /* Forward declaration of methods */ - static int whereLoopResize(sqlite3*, WhereLoop*, int); - --/* Test variable that can be set to enable WHERE tracing */ --#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) --/***/ int sqlite3WhereTrace = 0; --#endif -- -- - /* - ** Return the estimated number of output rows from a WHERE clause - */ -@@ -145678,7 +164540,7 @@ SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){ - ** block sorting is required. - */ - SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){ -- return pWInfo->nOBSat; -+ return pWInfo->nOBSat<0 ? 0 : pWInfo->nOBSat; - } - - /* -@@ -145713,7 +164575,33 @@ SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){ - } - pInner = &pWInfo->a[pWInfo->nLevel-1]; - assert( pInner->addrNxt!=0 ); -- return pInner->addrNxt; -+ return pInner->pRJ ? pWInfo->iContinue : pInner->addrNxt; -+} -+ -+/* -+** While generating code for the min/max optimization, after handling -+** the aggregate-step call to min() or max(), check to see if any -+** additional looping is required. If the output order is such that -+** we are certain that the correct answer has already been found, then -+** code an OP_Goto to by pass subsequent processing. -+** -+** Any extra OP_Goto that is coded here is an optimization. The -+** correct answer should be obtained regardless. This OP_Goto just -+** makes the answer appear faster. -+*/ -+SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe *v, WhereInfo *pWInfo){ -+ WhereLevel *pInner; -+ int i; -+ if( !pWInfo->bOrderedInnerLoop ) return; -+ if( pWInfo->nOBSat==0 ) return; -+ for(i=pWInfo->nLevel-1; i>=0; i--){ -+ pInner = &pWInfo->a[i]; -+ if( (pInner->pWLoop->wsFlags & WHERE_COLUMN_IN)!=0 ){ -+ sqlite3VdbeGoto(v, pInner->addrNxt); -+ return; -+ } -+ } -+ sqlite3VdbeGoto(v, pWInfo->iBreak); - } - - /* -@@ -145825,7 +164713,12 @@ whereOrInsert_done: - SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ - int i; - assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); -- for(i=0; in; i++){ -+ assert( pMaskSet->n>0 || pMaskSet->ix[0]<0 ); -+ assert( iCursor>=-1 ); -+ if( pMaskSet->ix[0]==iCursor ){ -+ return 1; -+ } -+ for(i=1; in; i++){ - if( pMaskSet->ix[i]==iCursor ){ - return MASKBIT(i); - } -@@ -145833,6 +164726,30 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ - return 0; - } - -+/* Allocate memory that is automatically freed when pWInfo is freed. -+*/ -+SQLITE_PRIVATE void *sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte){ -+ WhereMemBlock *pBlock; -+ pBlock = sqlite3DbMallocRawNN(pWInfo->pParse->db, nByte+sizeof(*pBlock)); -+ if( pBlock ){ -+ pBlock->pNext = pWInfo->pMemToFree; -+ pBlock->sz = nByte; -+ pWInfo->pMemToFree = pBlock; -+ pBlock++; -+ } -+ return (void*)pBlock; -+} -+SQLITE_PRIVATE void *sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte){ -+ void *pNew = sqlite3WhereMalloc(pWInfo, nByte); -+ if( pNew && pOld ){ -+ WhereMemBlock *pOldBlk = (WhereMemBlock*)pOld; -+ pOldBlk--; -+ assert( pOldBlk->szsz); -+ } -+ return pNew; -+} -+ - /* - ** Create a new mask for cursor iCursor. - ** -@@ -145846,6 +164763,54 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){ - pMaskSet->ix[pMaskSet->n++] = iCursor; - } - -+/* -+** If the right-hand branch of the expression is a TK_COLUMN, then return -+** a pointer to the right-hand branch. Otherwise, return NULL. -+*/ -+static Expr *whereRightSubexprIsColumn(Expr *p){ -+ p = sqlite3ExprSkipCollateAndLikely(p->pRight); -+ if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){ -+ return p; -+ } -+ return 0; -+} -+ -+/* -+** Term pTerm is guaranteed to be a WO_IN term. It may be a component term -+** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)". -+** This function checks to see if the term is compatible with an index -+** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so, -+** it returns a pointer to the name of the collation sequence (e.g. "BINARY" -+** or "NOCASE") used by the comparison in pTerm. If it is not compatible -+** with affinity idxaff, NULL is returned. -+*/ -+static SQLITE_NOINLINE const char *indexInAffinityOk( -+ Parse *pParse, -+ WhereTerm *pTerm, -+ u8 idxaff -+){ -+ Expr *pX = pTerm->pExpr; -+ Expr inexpr; -+ -+ assert( pTerm->eOperator & WO_IN ); -+ -+ if( sqlite3ExprIsVector(pX->pLeft) ){ -+ int iField = pTerm->u.x.iField - 1; -+ inexpr.flags = 0; -+ inexpr.op = TK_EQ; -+ inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr; -+ assert( ExprUseXSelect(pX) ); -+ inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr; -+ pX = &inexpr; -+ } -+ -+ if( sqlite3IndexAffinityOk(pX, idxaff) ){ -+ CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX); -+ return pRet ? pRet->zName : sqlite3StrBINARY; -+ } -+ return 0; -+} -+ - /* - ** Advance to the next WhereTerm that matches according to the criteria - ** established when the pScan object was initialized by whereScanInit(). -@@ -145865,19 +164830,20 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ - iColumn = pScan->aiColumn[pScan->iEquiv-1]; - iCur = pScan->aiCur[pScan->iEquiv-1]; - assert( pWC!=0 ); -+ assert( iCur>=0 ); - do{ - for(pTerm=pWC->a+k; knTerm; k++, pTerm++){ -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 || pTerm->leftCursor<0 ); - if( pTerm->leftCursor==iCur -- && pTerm->u.leftColumn==iColumn -+ && pTerm->u.x.leftColumn==iColumn - && (iColumn!=XN_EXPR - || sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, - pScan->pIdxExpr,iCur)==0) -- && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) -+ && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_OuterON)) - ){ - if( (pTerm->eOperator & WO_EQUIV)!=0 - && pScan->nEquivaiCur) -- && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op -- ==TK_COLUMN -+ && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0 - ){ - int j; - for(j=0; jnEquiv; j++){ -@@ -145895,21 +164861,30 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ - if( (pTerm->eOperator & pScan->opMask)!=0 ){ - /* Verify the affinity and collating sequence match */ - if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ -- CollSeq *pColl; -+ const char *zCollName; - Parse *pParse = pWC->pWInfo->pParse; - pX = pTerm->pExpr; -- if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ -- continue; -+ -+ if( (pTerm->eOperator & WO_IN) ){ -+ zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff); -+ if( !zCollName ) continue; -+ }else{ -+ CollSeq *pColl; -+ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ -+ continue; -+ } -+ assert(pX->pLeft); -+ pColl = sqlite3ExprCompareCollSeq(pParse, pX); -+ zCollName = pColl ? pColl->zName : sqlite3StrBINARY; - } -- assert(pX->pLeft); -- pColl = sqlite3ExprCompareCollSeq(pParse, pX); -- if( pColl==0 ) pColl = pParse->db->pDfltColl; -- if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){ -+ -+ if( sqlite3StrICmp(zCollName, pScan->zCollName) ){ - continue; - } - } - if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0 -- && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN -+ && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0)) -+ && pX->op==TK_COLUMN - && pX->iTable==pScan->aiCur[0] - && pX->iColumn==pScan->aiColumn[0] - ){ -@@ -145918,6 +164893,18 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ - } - pScan->pWC = pWC; - pScan->k = k+1; -+#ifdef WHERETRACE_ENABLED -+ if( sqlite3WhereTrace & 0x20000 ){ -+ int ii; -+ sqlite3DebugPrintf("SCAN-TERM %p: nEquiv=%d", -+ pTerm, pScan->nEquiv); -+ for(ii=0; iinEquiv; ii++){ -+ sqlite3DebugPrintf(" {%d:%d}", -+ pScan->aiCur[ii], pScan->aiColumn[ii]); -+ } -+ sqlite3DebugPrintf("\n"); -+ } -+#endif - return pTerm; - } - } -@@ -145984,16 +164971,16 @@ static WhereTerm *whereScanInit( - if( pIdx ){ - int j = iColumn; - iColumn = pIdx->aiColumn[j]; -- if( iColumn==XN_EXPR ){ -- pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; -- pScan->zCollName = pIdx->azColl[j]; -- pScan->aiColumn[0] = XN_EXPR; -- return whereScanInitIndexExpr(pScan); -- }else if( iColumn==pIdx->pTable->iPKey ){ -+ if( iColumn==pIdx->pTable->iPKey ){ - iColumn = XN_ROWID; - }else if( iColumn>=0 ){ - pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; - pScan->zCollName = pIdx->azColl[j]; -+ }else if( iColumn==XN_EXPR ){ -+ pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; -+ pScan->zCollName = pIdx->azColl[j]; -+ pScan->aiColumn[0] = XN_EXPR; -+ return whereScanInitIndexExpr(pScan); - } - }else if( iColumn==XN_EXPR ){ - return 0; -@@ -146073,7 +165060,8 @@ static int findIndexCol( - - for(i=0; inExpr; i++){ - Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); -- if( p->op==TK_COLUMN -+ if( ALWAYS(p!=0) -+ && (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) - && p->iColumn==pIdx->aiColumn[iCol] - && p->iTable==iBase - ){ -@@ -146129,7 +165117,7 @@ static int isDistinctRedundant( - ** clause is redundant. */ - if( pTabList->nSrc!=1 ) return 0; - iBase = pTabList->a[0].iCursor; -- pTab = pTabList->a[0].pTab; -+ pTab = pTabList->a[0].pSTab; - - /* If any of the expressions is an IPK column on table iBase, then return - ** true. Note: The (p->iTable==iBase) part of this test may be false if the -@@ -146137,7 +165125,9 @@ static int isDistinctRedundant( - */ - for(i=0; inExpr; i++){ - Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); -- if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; -+ if( NEVER(p==0) ) continue; -+ if( p->op!=TK_COLUMN && p->op!=TK_AGG_COLUMN ) continue; -+ if( p->iTable==iBase && p->iColumn<0 ) return 1; - } - - /* Loop through all indices on the table, checking each to see if it makes -@@ -146155,6 +165145,7 @@ static int isDistinctRedundant( - */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( !IsUniqueIndex(pIdx) ) continue; -+ if( pIdx->pPartIdxWhere ) continue; - for(i=0; inKeyCol; i++){ - if( 0==sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx) ){ - if( findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0 ) break; -@@ -146201,22 +165192,39 @@ static void translateColumnToCopy( - VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart); - int iEnd = sqlite3VdbeCurrentAddr(v); - if( pParse->db->mallocFailed ) return; -+#ifdef SQLITE_DEBUG -+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ -+ printf("CHECKING for column-to-copy on cursor %d for %d..%d\n", -+ iTabCur, iStart, iEnd); -+ } -+#endif - for(; iStartp1!=iTabCur ) continue; - if( pOp->opcode==OP_Column ){ -+#ifdef SQLITE_DEBUG -+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ -+ printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart); -+ } -+#endif - pOp->opcode = OP_Copy; - pOp->p1 = pOp->p2 + iRegister; - pOp->p2 = pOp->p3; - pOp->p3 = 0; -+ pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ - }else if( pOp->opcode==OP_Rowid ){ -- if( iAutoidxCur ){ -- pOp->opcode = OP_Sequence; -- pOp->p1 = iAutoidxCur; -- }else{ -+#ifdef SQLITE_DEBUG -+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ -+ printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart); -+ } -+#endif -+ pOp->opcode = OP_Sequence; -+ pOp->p1 = iAutoidxCur; -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ if( iAutoidxCur==0 ){ - pOp->opcode = OP_Null; -- pOp->p1 = 0; - pOp->p3 = 0; - } -+#endif - } - } - } -@@ -146228,16 +165236,22 @@ static void translateColumnToCopy( - ** are no-ops. - */ - #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) --static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ -+static void whereTraceIndexInfoInputs( -+ sqlite3_index_info *p, /* The IndexInfo object */ -+ Table *pTab /* The TABLE that is the virtual table */ -+){ - int i; -- if( !sqlite3WhereTrace ) return; -+ if( (sqlite3WhereTrace & 0x10)==0 ) return; -+ sqlite3DebugPrintf("sqlite3_index_info inputs for %s:\n", pTab->zName); - for(i=0; inConstraint; i++){ -- sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n", -+ sqlite3DebugPrintf( -+ " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", - i, - p->aConstraint[i].iColumn, - p->aConstraint[i].iTermOffset, - p->aConstraint[i].op, -- p->aConstraint[i].usable); -+ p->aConstraint[i].usable, -+ sqlite3_vtab_collation(p,i)); - } - for(i=0; inOrderBy; i++){ - sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", -@@ -146246,9 +165260,13 @@ static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ - p->aOrderBy[i].desc); - } - } --static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ -+static void whereTraceIndexInfoOutputs( -+ sqlite3_index_info *p, /* The IndexInfo object */ -+ Table *pTab /* The TABLE that is the virtual table */ -+){ - int i; -- if( !sqlite3WhereTrace ) return; -+ if( (sqlite3WhereTrace & 0x10)==0 ) return; -+ sqlite3DebugPrintf("sqlite3_index_info outputs for %s:\n", pTab->zName); - for(i=0; inConstraint; i++){ - sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", - i, -@@ -146262,10 +165280,86 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ - sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); - } - #else --#define whereTraceIndexInfoInputs(A) --#define whereTraceIndexInfoOutputs(A) -+#define whereTraceIndexInfoInputs(A,B) -+#define whereTraceIndexInfoOutputs(A,B) - #endif - -+/* -+** We know that pSrc is an operand of an outer join. Return true if -+** pTerm is a constraint that is compatible with that join. -+** -+** pTerm must be EP_OuterON if pSrc is the right operand of an -+** outer join. pTerm can be either EP_OuterON or EP_InnerON if pSrc -+** is the left operand of a RIGHT join. -+** -+** See https://sqlite.org/forum/forumpost/206d99a16dd9212f -+** for an example of a WHERE clause constraints that may not be used on -+** the right table of a RIGHT JOIN because the constraint implies a -+** not-NULL condition on the left table of the RIGHT JOIN. -+*/ -+static int constraintCompatibleWithOuterJoin( -+ const WhereTerm *pTerm, /* WHERE clause term to check */ -+ const SrcItem *pSrc /* Table we are trying to access */ -+){ -+ assert( (pSrc->fg.jointype&(JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ); /* By caller */ -+ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT ); -+ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ ); -+ testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) -+ testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) ); -+ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) -+ || pTerm->pExpr->w.iJoin != pSrc->iCursor -+ ){ -+ return 0; -+ } -+ if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0 -+ && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON)) -+ ){ -+ return 0; -+ } -+ return 1; -+} -+ -+#ifndef SQLITE_OMIT_AUTOMATIC_INDEX -+/* -+** Return true if column iCol of table pTab seem like it might be a -+** good column to use as part of a query-time index. -+** -+** Current algorithm (subject to improvement!): -+** -+** 1. If iCol is already the left-most column of some other index, -+** then return false. -+** -+** 2. If iCol is part of an existing index that has an aiRowLogEst of -+** more than 20, then return false. -+** -+** 3. If no disqualifying conditions above are found, return true. -+** -+** 2025-01-03: I experimented with a new rule that returns false if the -+** the datatype of the column is "BOOLEAN". This did not improve -+** performance on any queries at hand, but it did burn CPU cycles, so the -+** idea was not committed. -+*/ -+static SQLITE_NOINLINE int columnIsGoodIndexCandidate( -+ const Table *pTab, -+ int iCol -+){ -+ const Index *pIdx; -+ for(pIdx = pTab->pIndex; pIdx!=0; pIdx=pIdx->pNext){ -+ int j; -+ for(j=0; jnKeyCol; j++){ -+ if( pIdx->aiColumn[j]==iCol ){ -+ if( j==0 ) return 0; -+ if( pIdx->hasStat1 && pIdx->aiRowLogEst[j+1]>20 ) return 0; -+ break; -+ } -+ } -+ } -+ return 1; -+} -+#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ -+ -+ -+ - #ifndef SQLITE_OMIT_AUTOMATIC_INDEX - /* - ** Return TRUE if the WHERE clause term pTerm is of a form where it -@@ -146273,43 +165367,94 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ - ** index existed. - */ - static int termCanDriveIndex( -- WhereTerm *pTerm, /* WHERE clause term to check */ -- struct SrcList_item *pSrc, /* Table we are trying to access */ -- Bitmask notReady /* Tables in outer loops of the join */ -+ const WhereTerm *pTerm, /* WHERE clause term to check */ -+ const SrcItem *pSrc, /* Table we are trying to access */ -+ const Bitmask notReady /* Tables in outer loops of the join */ - ){ - char aff; -+ int leftCol; -+ - if( pTerm->leftCursor!=pSrc->iCursor ) return 0; - if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; -- if( (pSrc->fg.jointype & JT_LEFT) -- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) -- && (pTerm->eOperator & WO_IS) -+ assert( (pSrc->fg.jointype & JT_RIGHT)==0 ); -+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 -+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc) - ){ -- /* Cannot use an IS term from the WHERE clause as an index driver for -- ** the RHS of a LEFT JOIN. Such a term can only be used if it is from -- ** the ON clause. */ -- return 0; -+ return 0; /* See https://sqlite.org/forum/forumpost/51e6959f61 */ - } - if( (pTerm->prereqRight & notReady)!=0 ) return 0; -- if( pTerm->u.leftColumn<0 ) return 0; -- aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ leftCol = pTerm->u.x.leftColumn; -+ if( leftCol<0 ) return 0; -+ aff = pSrc->pSTab->aCol[leftCol].affinity; - if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; - testcase( pTerm->pExpr->op==TK_IS ); -- return 1; -+ return columnIsGoodIndexCandidate(pSrc->pSTab, leftCol); - } - #endif - - - #ifndef SQLITE_OMIT_AUTOMATIC_INDEX -+ -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+/* -+** Argument pIdx represents an automatic index that the current statement -+** will create and populate. Add an OP_Explain with text of the form: -+** -+** CREATE AUTOMATIC INDEX ON
        () [WHERE ] -+** -+** This is only required if sqlite3_stmt_scanstatus() is enabled, to -+** associate an SQLITE_SCANSTAT_NCYCLE and SQLITE_SCANSTAT_NLOOP -+** values with. In order to avoid breaking legacy code and test cases, -+** the OP_Explain is not added if this is an EXPLAIN QUERY PLAN command. -+*/ -+static void explainAutomaticIndex( -+ Parse *pParse, -+ Index *pIdx, /* Automatic index to explain */ -+ int bPartial, /* True if pIdx is a partial index */ -+ int *pAddrExplain /* OUT: Address of OP_Explain */ -+){ -+ if( IS_STMT_SCANSTATUS(pParse->db) && pParse->explain!=2 ){ -+ Table *pTab = pIdx->pTable; -+ const char *zSep = ""; -+ char *zText = 0; -+ int ii = 0; -+ sqlite3_str *pStr = sqlite3_str_new(pParse->db); -+ sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName); -+ assert( pIdx->nColumn>1 ); -+ assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID || !HasRowid(pTab) ); -+ for(ii=0; ii<(pIdx->nColumn-1); ii++){ -+ const char *zName = 0; -+ int iCol = pIdx->aiColumn[ii]; -+ -+ zName = pTab->aCol[iCol].zCnName; -+ sqlite3_str_appendf(pStr, "%s%s", zSep, zName); -+ zSep = ", "; -+ } -+ zText = sqlite3_str_finish(pStr); -+ if( zText==0 ){ -+ sqlite3OomFault(pParse->db); -+ }else{ -+ *pAddrExplain = sqlite3VdbeExplain( -+ pParse, 0, "%s)%s", zText, (bPartial ? " WHERE " : "") -+ ); -+ sqlite3_free(zText); -+ } -+ } -+} -+#else -+# define explainAutomaticIndex(a,b,c,d) -+#endif -+ - /* - ** Generate code to construct the Index object for an automatic index - ** and to set up the WhereLevel object pLevel so that the code generator - ** makes use of the automatic index. - */ --static void constructAutomaticIndex( -+static SQLITE_NOINLINE void constructAutomaticIndex( - Parse *pParse, /* The parsing context */ - WhereClause *pWC, /* The WHERE clause */ -- struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ -- Bitmask notReady, /* Mask of cursors that are not available */ -+ const Bitmask notReady, /* Mask of cursors that are not available */ - WhereLevel *pLevel /* Write new index here */ - ){ - int nKeyCol; /* Number of columns in the constructed index */ -@@ -146329,12 +165474,17 @@ static void constructAutomaticIndex( - char *zNotUsed; /* Extra space on the end of pIdx */ - Bitmask idxCols; /* Bitmap of columns used for indexing */ - Bitmask extraCols; /* Bitmap of additional columns */ -- u8 sentWarning = 0; /* True if a warnning has been issued */ -+ u8 sentWarning = 0; /* True if a warning has been issued */ -+ u8 useBloomFilter = 0; /* True to also add a Bloom filter */ - Expr *pPartial = 0; /* Partial Index Expression */ - int iContinue = 0; /* Jump here to skip excluded rows */ -- struct SrcList_item *pTabItem; /* FROM clause term being indexed */ -+ SrcList *pTabList; /* The complete FROM clause */ -+ SrcItem *pSrc; /* The FROM clause term to get the next index */ - int addrCounter = 0; /* Address where integer counter is initialized */ - int regBase; /* Array of registers where record is assembled */ -+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS -+ int addrExp = 0; /* Address of OP_Explain */ -+#endif - - /* Generate code to skip over the creation and initialization of the - ** transient index on 2nd and subsequent iterations of the loop. */ -@@ -146345,31 +165495,35 @@ static void constructAutomaticIndex( - /* Count the number of columns that will be added to the index - ** and used to match WHERE clause constraints */ - nKeyCol = 0; -- pTable = pSrc->pTab; -+ pTabList = pWC->pWInfo->pTabList; -+ pSrc = &pTabList->a[pLevel->iFrom]; -+ pTable = pSrc->pSTab; - pWCEnd = &pWC->a[pWC->nTerm]; - pLoop = pLevel->pWLoop; - idxCols = 0; - for(pTerm=pWC->a; pTermpExpr; -- assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ -- || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ -- || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ -- if( pLoop->prereq==0 -- && (pTerm->wtFlags & TERM_VIRTUAL)==0 -- && !ExprHasProperty(pExpr, EP_FromJoin) -- && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){ -+ /* Make the automatic index a partial index if there are terms in the -+ ** WHERE clause (or the ON clause of a LEFT join) that constrain which -+ ** rows of the target table (pSrc) that can be used. */ -+ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 -+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0) -+ ){ - pPartial = sqlite3ExprAnd(pParse, pPartial, - sqlite3ExprDup(pParse->db, pExpr, 0)); - } - if( termCanDriveIndex(pTerm, pSrc, notReady) ){ -- int iCol = pTerm->u.leftColumn; -- Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); -+ int iCol; -+ Bitmask cMask; -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ iCol = pTerm->u.x.leftColumn; -+ cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); - testcase( iCol==BMS ); - testcase( iCol==BMS-1 ); - if( !sentWarning ){ - sqlite3_log(SQLITE_WARNING_AUTOINDEX, - "automatic index on %s(%s)", pTable->zName, -- pTable->aCol[iCol].zName); -+ pTable->aCol[iCol].zCnName); - sentWarning = 1; - } - if( (idxCols & cMask)==0 ){ -@@ -146381,7 +165535,7 @@ static void constructAutomaticIndex( - } - } - } -- assert( nKeyCol>0 ); -+ assert( nKeyCol>0 || pParse->db->mallocFailed ); - pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol; - pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED - | WHERE_AUTO_INDEX; -@@ -146394,7 +165548,24 @@ static void constructAutomaticIndex( - ** original table changes and the index and table cannot both be used - ** if they go out of sync. - */ -- extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); -+ if( IsView(pTable) ){ -+ extraCols = ALLBITS & ~idxCols; -+ }else{ -+ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); -+ } -+ if( !HasRowid(pTable) ){ -+ /* For WITHOUT ROWID tables, ensure that all PRIMARY KEY columns are -+ ** either in the idxCols mask or in the extraCols mask */ -+ for(i=0; inCol; i++){ -+ if( (pTable->aCol[i].colFlags & COLFLAG_PRIMKEY)==0 ) continue; -+ if( i>=BMS-1 ){ -+ extraCols |= MASKBIT(BMS-1); -+ break; -+ } -+ if( idxCols & MASKBIT(i) ) continue; -+ extraCols |= MASKBIT(i); -+ } -+ } - mxBitCol = MIN(BMS-1,pTable->nCol); - testcase( pTable->nCol==BMS-1 ); - testcase( pTable->nCol==BMS-2 ); -@@ -146406,7 +165577,10 @@ static void constructAutomaticIndex( - } - - /* Construct the Index object to describe this index */ -- pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed); -+ assert( nKeyCol <= pTable->nCol + MAX(0, pTable->nCol - BMS + 1) ); -+ /* ^-- This guarantees that the number of index columns will fit in the u16 */ -+ pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable), -+ 0, &zNotUsed); - if( pIdx==0 ) goto end_auto_index_create; - pLoop->u.btree.pIndex = pIdx; - pIdx->zName = "auto-index"; -@@ -146415,18 +165589,31 @@ static void constructAutomaticIndex( - idxCols = 0; - for(pTerm=pWC->a; pTermu.leftColumn; -- Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); -+ int iCol; -+ Bitmask cMask; -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ iCol = pTerm->u.x.leftColumn; -+ cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); - testcase( iCol==BMS-1 ); - testcase( iCol==BMS ); - if( (idxCols & cMask)==0 ){ - Expr *pX = pTerm->pExpr; - idxCols |= cMask; -- pIdx->aiColumn[n] = pTerm->u.leftColumn; -+ pIdx->aiColumn[n] = pTerm->u.x.leftColumn; - pColl = sqlite3ExprCompareCollSeq(pParse, pX); - assert( pColl!=0 || pParse->nErr>0 ); /* TH3 collate01.800 */ - pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY; - n++; -+ if( ALWAYS(pX->pLeft!=0) -+ && sqlite3ExprAffinity(pX->pLeft)!=SQLITE_AFF_TEXT -+ ){ -+ /* TUNING: only use a Bloom filter on an automatic index -+ ** if one or more key columns has the ability to hold numeric -+ ** values, since strings all have the same hash in the Bloom -+ ** filter implementation and hence a Bloom filter on a text column -+ ** is not usually helpful. */ -+ useBloomFilter = 1; -+ } - } - } - } -@@ -146449,25 +165636,38 @@ static void constructAutomaticIndex( - } - } - assert( n==nKeyCol ); -- pIdx->aiColumn[n] = XN_ROWID; -- pIdx->azColl[n] = sqlite3StrBINARY; -+ if( HasRowid(pTable) ){ -+ pIdx->aiColumn[n] = XN_ROWID; -+ pIdx->azColl[n] = sqlite3StrBINARY; -+ } - - /* Create the automatic index */ -+ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp); - assert( pLevel->iIdxCur>=0 ); - pLevel->iIdxCur = pParse->nTab++; - sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - VdbeComment((v, "for %s", pTable->zName)); -+ if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) && useBloomFilter ){ -+ sqlite3WhereExplainBloomFilter(pParse, pWC->pWInfo, pLevel); -+ pLevel->regFilter = ++pParse->nMem; -+ sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); -+ } - - /* Fill the automatic index with content */ -- pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; -- if( pTabItem->fg.viaCoroutine ){ -- int regYield = pTabItem->regReturn; -+ assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] ); -+ if( pSrc->fg.viaCoroutine ){ -+ int regYield; -+ Subquery *pSubq; -+ assert( pSrc->fg.isSubquery ); -+ pSubq = pSrc->u4.pSubq; -+ assert( pSubq!=0 ); -+ regYield = pSubq->regReturn; - addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); -- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); -+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); - addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); - VdbeCoverage(v); -- VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); -+ VdbeComment((v, "next row of %s", pSrc->pSTab->zName)); - }else{ - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); - } -@@ -146480,17 +165680,23 @@ static void constructAutomaticIndex( - regBase = sqlite3GenerateIndexKey( - pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 - ); -+ if( pLevel->regFilter ){ -+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, -+ regBase, pLoop->u.btree.nEq); -+ } -+ sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v)); - sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); - sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); -- if( pTabItem->fg.viaCoroutine ){ -+ if( pSrc->fg.viaCoroutine ){ -+ assert( pSrc->fg.isSubquery && pSrc->u4.pSubq!=0 ); - sqlite3VdbeChangeP2(v, addrCounter, regBase+n); - testcase( pParse->db->mallocFailed ); - assert( pLevel->iIdxCur>0 ); - translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, -- pTabItem->regResult, pLevel->iIdxCur); -+ pSrc->u4.pSubq->regResult, pLevel->iIdxCur); - sqlite3VdbeGoto(v, addrTop); -- pTabItem->fg.viaCoroutine = 0; -+ pSrc->fg.viaCoroutine = 0; - }else{ - sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); - sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); -@@ -146500,28 +165706,183 @@ static void constructAutomaticIndex( - - /* Jump here when skipping the initialization */ - sqlite3VdbeJumpHere(v, addrInit); -+ sqlite3VdbeScanStatusRange(v, addrExp, addrExp, -1); - - end_auto_index_create: - sqlite3ExprDelete(pParse->db, pPartial); - } - #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ - -+/* -+** Generate bytecode that will initialize a Bloom filter that is appropriate -+** for pLevel. -+** -+** If there are inner loops within pLevel that have the WHERE_BLOOMFILTER -+** flag set, initialize a Bloomfilter for them as well. Except don't do -+** this recursive initialization if the SQLITE_BloomPulldown optimization has -+** been turned off. -+** -+** When the Bloom filter is initialized, the WHERE_BLOOMFILTER flag is cleared -+** from the loop, but the regFilter value is set to a register that implements -+** the Bloom filter. When regFilter is positive, the -+** sqlite3WhereCodeOneLoopStart() will generate code to test the Bloom filter -+** and skip the subsequence B-Tree seek if the Bloom filter indicates that -+** no matching rows exist. -+** -+** This routine may only be called if it has previously been determined that -+** the loop would benefit from a Bloom filter, and the WHERE_BLOOMFILTER bit -+** is set. -+*/ -+static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( -+ WhereInfo *pWInfo, /* The WHERE clause */ -+ int iLevel, /* Index in pWInfo->a[] that is pLevel */ -+ WhereLevel *pLevel, /* Make a Bloom filter for this FROM term */ -+ Bitmask notReady /* Loops that are not ready */ -+){ -+ int addrOnce; /* Address of opening OP_Once */ -+ int addrTop; /* Address of OP_Rewind */ -+ int addrCont; /* Jump here to skip a row */ -+ const WhereTerm *pTerm; /* For looping over WHERE clause terms */ -+ const WhereTerm *pWCEnd; /* Last WHERE clause term */ -+ Parse *pParse = pWInfo->pParse; /* Parsing context */ -+ Vdbe *v = pParse->pVdbe; /* VDBE under construction */ -+ WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */ -+ int iCur; /* Cursor for table getting the filter */ -+ IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */ -+ IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */ -+ -+ saved_pIdxEpr = pParse->pIdxEpr; -+ saved_pIdxPartExpr = pParse->pIdxPartExpr; -+ pParse->pIdxEpr = 0; -+ pParse->pIdxPartExpr = 0; -+ -+ assert( pLoop!=0 ); -+ assert( v!=0 ); -+ assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); -+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ); -+ -+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); -+ do{ -+ const SrcList *pTabList; -+ const SrcItem *pItem; -+ const Table *pTab; -+ u64 sz; -+ int iSrc; -+ sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); -+ addrCont = sqlite3VdbeMakeLabel(pParse); -+ iCur = pLevel->iTabCur; -+ pLevel->regFilter = ++pParse->nMem; -+ -+ /* The Bloom filter is a Blob held in a register. Initialize it -+ ** to zero-filled blob of at least 80K bits, but maybe more if the -+ ** estimated size of the table is larger. We could actually -+ ** measure the size of the table at run-time using OP_Count with -+ ** P3==1 and use that value to initialize the blob. But that makes -+ ** testing complicated. By basing the blob size on the value in the -+ ** sqlite_stat1 table, testing is much easier. -+ */ -+ pTabList = pWInfo->pTabList; -+ iSrc = pLevel->iFrom; -+ pItem = &pTabList->a[iSrc]; -+ assert( pItem!=0 ); -+ pTab = pItem->pSTab; -+ assert( pTab!=0 ); -+ sz = sqlite3LogEstToInt(pTab->nRowLogEst); -+ if( sz<10000 ){ -+ sz = 10000; -+ }else if( sz>10000000 ){ -+ sz = 10000000; -+ } -+ sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter); -+ -+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); -+ pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; -+ for(pTerm=pWInfo->sWC.a; pTermpExpr; -+ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 -+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0) -+ ){ -+ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); -+ } -+ } -+ if( pLoop->wsFlags & WHERE_IPK ){ -+ int r1 = sqlite3GetTempReg(pParse); -+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); -+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1); -+ sqlite3ReleaseTempReg(pParse, r1); -+ }else{ -+ Index *pIdx = pLoop->u.btree.pIndex; -+ int n = pLoop->u.btree.nEq; -+ int r1 = sqlite3GetTempRange(pParse, n); -+ int jj; -+ for(jj=0; jjpTable==pItem->pSTab ); -+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj); -+ } -+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n); -+ sqlite3ReleaseTempRange(pParse, r1, n); -+ } -+ sqlite3VdbeResolveLabel(v, addrCont); -+ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); -+ VdbeCoverage(v); -+ sqlite3VdbeJumpHere(v, addrTop); -+ pLoop->wsFlags &= ~WHERE_BLOOMFILTER; -+ if( OptimizationDisabled(pParse->db, SQLITE_BloomPulldown) ) break; -+ while( ++iLevel < pWInfo->nLevel ){ -+ const SrcItem *pTabItem; -+ pLevel = &pWInfo->a[iLevel]; -+ pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; -+ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue; -+ pLoop = pLevel->pWLoop; -+ if( NEVER(pLoop==0) ) continue; -+ if( pLoop->prereq & notReady ) continue; -+ if( (pLoop->wsFlags & (WHERE_BLOOMFILTER|WHERE_COLUMN_IN)) -+ ==WHERE_BLOOMFILTER -+ ){ -+ /* This is a candidate for bloom-filter pull-down (early evaluation). -+ ** The test that WHERE_COLUMN_IN is omitted is important, as we are -+ ** not able to do early evaluation of bloom filters that make use of -+ ** the IN operator */ -+ break; -+ } -+ } -+ }while( iLevel < pWInfo->nLevel ); -+ sqlite3VdbeJumpHere(v, addrOnce); -+ pParse->pIdxEpr = saved_pIdxEpr; -+ pParse->pIdxPartExpr = saved_pIdxPartExpr; -+} -+ -+ - #ifndef SQLITE_OMIT_VIRTUALTABLE -+/* -+** Return term iTerm of the WhereClause passed as the first argument. Terms -+** are numbered from 0 upwards, starting with the terms in pWC->a[], then -+** those in pWC->pOuter->a[] (if any), and so on. -+*/ -+static WhereTerm *termFromWhereClause(WhereClause *pWC, int iTerm){ -+ WhereClause *p; -+ for(p=pWC; p; p=p->pOuter){ -+ if( iTermnTerm ) return &p->a[iTerm]; -+ iTerm -= p->nTerm; -+ } -+ return 0; -+} -+ - /* - ** Allocate and populate an sqlite3_index_info structure. It is the - ** responsibility of the caller to eventually release the structure --** by passing the pointer returned by this function to sqlite3_free(). -+** by passing the pointer returned by this function to freeIndexInfo(). - */ - static sqlite3_index_info *allocateIndexInfo( -- Parse *pParse, /* The parsing context */ -+ WhereInfo *pWInfo, /* The WHERE clause */ - WhereClause *pWC, /* The WHERE clause being analyzed */ - Bitmask mUnusable, /* Ignore terms with these prereqs */ -- struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */ -- ExprList *pOrderBy, /* The ORDER BY clause */ -+ SrcItem *pSrc, /* The FROM clause term that is the vtab */ - u16 *pmNoOmit /* Mask of terms not to omit */ - ){ - int i, j; - int nTerm; -+ Parse *pParse = pWInfo->pParse; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_orderby *pIdxOrderBy; - struct sqlite3_index_constraint_usage *pUsage; -@@ -146530,21 +165891,44 @@ static sqlite3_index_info *allocateIndexInfo( - int nOrderBy; - sqlite3_index_info *pIdxInfo; - u16 mNoOmit = 0; -+ const Table *pTab; -+ int eDistinct = 0; -+ ExprList *pOrderBy = pWInfo->pOrderBy; -+ WhereClause *p; - -- /* Count the number of possible WHERE clause constraints referring -- ** to this virtual table */ -- for(i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++){ -- if( pTerm->leftCursor != pSrc->iCursor ) continue; -- if( pTerm->prereqRight & mUnusable ) continue; -- assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); -- testcase( pTerm->eOperator & WO_IN ); -- testcase( pTerm->eOperator & WO_ISNULL ); -- testcase( pTerm->eOperator & WO_IS ); -- testcase( pTerm->eOperator & WO_ALL ); -- if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; -- if( pTerm->wtFlags & TERM_VNULL ) continue; -- assert( pTerm->u.leftColumn>=(-1) ); -- nTerm++; -+ assert( pSrc!=0 ); -+ pTab = pSrc->pSTab; -+ assert( pTab!=0 ); -+ assert( IsVirtual(pTab) ); -+ -+ /* Find all WHERE clause constraints referring to this virtual table. -+ ** Mark each term with the TERM_OK flag. Set nTerm to the number of -+ ** terms found. -+ */ -+ for(p=pWC, nTerm=0; p; p=p->pOuter){ -+ for(i=0, pTerm=p->a; inTerm; i++, pTerm++){ -+ pTerm->wtFlags &= ~TERM_OK; -+ if( pTerm->leftCursor != pSrc->iCursor ) continue; -+ if( pTerm->prereqRight & mUnusable ) continue; -+ assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); -+ testcase( pTerm->eOperator & WO_IN ); -+ testcase( pTerm->eOperator & WO_ISNULL ); -+ testcase( pTerm->eOperator & WO_IS ); -+ testcase( pTerm->eOperator & WO_ALL ); -+ if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; -+ if( pTerm->wtFlags & TERM_VNULL ) continue; -+ -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); -+ assert( pTerm->u.x.leftColumn>=XN_ROWID ); -+ assert( pTerm->u.x.leftColumnnCol ); -+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 -+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc) -+ ){ -+ continue; -+ } -+ nTerm++; -+ pTerm->wtFlags |= TERM_OK; -+ } - } - - /* If the ORDER BY clause contains only columns in the current -@@ -146556,11 +165940,49 @@ static sqlite3_index_info *allocateIndexInfo( - int n = pOrderBy->nExpr; - for(i=0; ia[i].pExpr; -- if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break; -- if( pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL ) break; -+ Expr *pE2; -+ -+ /* Skip over constant terms in the ORDER BY clause */ -+ if( sqlite3ExprIsConstant(0, pExpr) ){ -+ continue; -+ } -+ -+ /* Virtual tables are unable to deal with NULLS FIRST */ -+ if( pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) break; -+ -+ /* First case - a direct column references without a COLLATE operator */ -+ if( pExpr->op==TK_COLUMN && pExpr->iTable==pSrc->iCursor ){ -+ assert( pExpr->iColumn>=XN_ROWID && pExpr->iColumnnCol ); -+ continue; -+ } -+ -+ /* 2nd case - a column reference with a COLLATE operator. Only match -+ ** of the COLLATE operator matches the collation of the column. */ -+ if( pExpr->op==TK_COLLATE -+ && (pE2 = pExpr->pLeft)->op==TK_COLUMN -+ && pE2->iTable==pSrc->iCursor -+ ){ -+ const char *zColl; /* The collating sequence name */ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ assert( pExpr->u.zToken!=0 ); -+ assert( pE2->iColumn>=XN_ROWID && pE2->iColumnnCol ); -+ pExpr->iColumn = pE2->iColumn; -+ if( pE2->iColumn<0 ) continue; /* Collseq does not matter for rowid */ -+ zColl = sqlite3ColumnColl(&pTab->aCol[pE2->iColumn]); -+ if( zColl==0 ) zColl = sqlite3StrBINARY; -+ if( sqlite3_stricmp(pExpr->u.zToken, zColl)==0 ) continue; -+ } -+ -+ /* No matches cause a break out of the loop */ -+ break; - } -- if( i==n){ -+ if( i==n ){ - nOrderBy = n; -+ if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ -+ eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); -+ }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ -+ eDistinct = 1; -+ } - } - } - -@@ -146568,89 +165990,131 @@ static sqlite3_index_info *allocateIndexInfo( - */ - pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) - + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm -- + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) ); -+ + sizeof(*pIdxOrderBy)*nOrderBy -+ + SZ_HIDDENINDEXINFO(nTerm) ); - if( pIdxInfo==0 ){ - sqlite3ErrorMsg(pParse, "out of memory"); - return 0; - } - pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; -- pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1]; -+ pIdxCons = (struct sqlite3_index_constraint*)&pHidden->aRhs[nTerm]; - pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm]; - pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; -- pIdxInfo->nOrderBy = nOrderBy; - pIdxInfo->aConstraint = pIdxCons; - pIdxInfo->aOrderBy = pIdxOrderBy; - pIdxInfo->aConstraintUsage = pUsage; -+ pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; -+ if( HasRowid(pTab)==0 ){ -+ /* Ensure that all bits associated with PK columns are set. This is to -+ ** ensure they are available for cases like RIGHT joins or OR loops. */ -+ Index *pPk = sqlite3PrimaryKeyIndex((Table*)pTab); -+ assert( pPk!=0 ); -+ for(i=0; inKeyCol; i++){ -+ int iCol = pPk->aiColumn[i]; -+ assert( iCol>=0 ); -+ if( iCol>=BMS-1 ) iCol = BMS-1; -+ pIdxInfo->colUsed |= MASKBIT(iCol); -+ } -+ } - pHidden->pWC = pWC; - pHidden->pParse = pParse; -- for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ -- u16 op; -- if( pTerm->leftCursor != pSrc->iCursor ) continue; -- if( pTerm->prereqRight & mUnusable ) continue; -- assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); -- testcase( pTerm->eOperator & WO_IN ); -- testcase( pTerm->eOperator & WO_IS ); -- testcase( pTerm->eOperator & WO_ISNULL ); -- testcase( pTerm->eOperator & WO_ALL ); -- if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; -- if( pTerm->wtFlags & TERM_VNULL ) continue; -- -- /* tag-20191211-002: WHERE-clause constraints are not useful to the -- ** right-hand table of a LEFT JOIN. See tag-20191211-001 for the -- ** equivalent restriction for ordinary tables. */ -- if( (pSrc->fg.jointype & JT_LEFT)!=0 -- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) -- ){ -- continue; -- } -- assert( pTerm->u.leftColumn>=(-1) ); -- pIdxCons[j].iColumn = pTerm->u.leftColumn; -- pIdxCons[j].iTermOffset = i; -- op = pTerm->eOperator & WO_ALL; -- if( op==WO_IN ) op = WO_EQ; -- if( op==WO_AUX ){ -- pIdxCons[j].op = pTerm->eMatchOp; -- }else if( op & (WO_ISNULL|WO_IS) ){ -- if( op==WO_ISNULL ){ -- pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; -- }else{ -- pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; -- } -- }else{ -- pIdxCons[j].op = (u8)op; -- /* The direct assignment in the previous line is possible only because -- ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The -- ** following asserts verify this fact. */ -- assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); -- assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); -- assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); -- assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); -- assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); -- assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) ); -- -- if( op & (WO_LT|WO_LE|WO_GT|WO_GE) -- && sqlite3ExprIsVector(pTerm->pExpr->pRight) -- ){ -- testcase( j!=i ); -- if( j<16 ) mNoOmit |= (1 << j); -- if( op==WO_LT ) pIdxCons[j].op = WO_LE; -- if( op==WO_GT ) pIdxCons[j].op = WO_GE; -+ pHidden->eDistinct = eDistinct; -+ pHidden->mIn = 0; -+ for(p=pWC, i=j=0; p; p=p->pOuter){ -+ int nLast = i+p->nTerm;; -+ for(pTerm=p->a; iwtFlags & TERM_OK)==0 ) continue; -+ pIdxCons[j].iColumn = pTerm->u.x.leftColumn; -+ pIdxCons[j].iTermOffset = i; -+ op = pTerm->eOperator & WO_ALL; -+ if( op==WO_IN ){ -+ if( (pTerm->wtFlags & TERM_SLICE)==0 ){ -+ pHidden->mIn |= SMASKBIT32(j); -+ } -+ op = WO_EQ; -+ } -+ if( op==WO_AUX ){ -+ pIdxCons[j].op = pTerm->eMatchOp; -+ }else if( op & (WO_ISNULL|WO_IS) ){ -+ if( op==WO_ISNULL ){ -+ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; -+ }else{ -+ pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; -+ } -+ }else{ -+ pIdxCons[j].op = (u8)op; -+ /* The direct assignment in the previous line is possible only because -+ ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The -+ ** following asserts verify this fact. */ -+ assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); -+ assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); -+ assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); -+ assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); -+ assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); -+ assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) ); -+ -+ if( op & (WO_LT|WO_LE|WO_GT|WO_GE) -+ && sqlite3ExprIsVector(pTerm->pExpr->pRight) -+ ){ -+ testcase( j!=i ); -+ if( j<16 ) mNoOmit |= (1 << j); -+ if( op==WO_LT ) pIdxCons[j].op = WO_LE; -+ if( op==WO_GT ) pIdxCons[j].op = WO_GE; -+ } - } -- } - -- j++; -+ j++; -+ } - } -+ assert( j==nTerm ); - pIdxInfo->nConstraint = j; -- for(i=0; ia[i].pExpr; -- pIdxOrderBy[i].iColumn = pExpr->iColumn; -- pIdxOrderBy[i].desc = pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC; -+ if( sqlite3ExprIsConstant(0, pExpr) ) continue; -+ assert( pExpr->op==TK_COLUMN -+ || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN -+ && pExpr->iColumn==pExpr->pLeft->iColumn) ); -+ pIdxOrderBy[j].iColumn = pExpr->iColumn; -+ pIdxOrderBy[j].desc = pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC; -+ j++; - } -+ pIdxInfo->nOrderBy = j; - - *pmNoOmit = mNoOmit; - return pIdxInfo; - } - -+/* -+** Free and zero the sqlite3_index_info.idxStr value if needed. -+*/ -+static void freeIdxStr(sqlite3_index_info *pIdxInfo){ -+ if( pIdxInfo->needToFreeIdxStr ){ -+ sqlite3_free(pIdxInfo->idxStr); -+ pIdxInfo->idxStr = 0; -+ pIdxInfo->needToFreeIdxStr = 0; -+ } -+} -+ -+/* -+** Free an sqlite3_index_info structure allocated by allocateIndexInfo() -+** and possibly modified by xBestIndex methods. -+*/ -+static void freeIndexInfo(sqlite3 *db, sqlite3_index_info *pIdxInfo){ -+ HiddenIndexInfo *pHidden; -+ int i; -+ assert( pIdxInfo!=0 ); -+ pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; -+ assert( pHidden->pParse!=0 ); -+ assert( pHidden->pParse->db==db ); -+ for(i=0; inConstraint; i++){ -+ sqlite3ValueFree(pHidden->aRhs[i]); /* IMP: R-14553-25174 */ -+ pHidden->aRhs[i] = 0; -+ } -+ freeIdxStr(pIdxInfo); -+ sqlite3DbFree(db, pIdxInfo); -+} -+ - /* - ** The table object reference passed as the second argument to this function - ** must represent a virtual table. This function invokes the xBestIndex() -@@ -146668,12 +166132,16 @@ static sqlite3_index_info *allocateIndexInfo( - ** that this is required. - */ - static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ -- sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; - int rc; -+ sqlite3_vtab *pVtab; - -- whereTraceIndexInfoInputs(p); -+ assert( IsVirtual(pTab) ); -+ pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; -+ whereTraceIndexInfoInputs(p, pTab); -+ pParse->db->nSchemaLock++; - rc = pVtab->pModule->xBestIndex(pVtab, p); -- whereTraceIndexInfoOutputs(p); -+ pParse->db->nSchemaLock--; -+ whereTraceIndexInfoOutputs(p, pTab); - - if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ - if( rc==SQLITE_NOMEM ){ -@@ -146684,6 +166152,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ - sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); - } - } -+ if( pTab->u.vtab.p->bAllSchemas ){ -+ sqlite3VtabUsesAllSchemas(pParse); -+ } - sqlite3_free(pVtab->zErrMsg); - pVtab->zErrMsg = 0; - return rc; -@@ -146726,7 +166197,8 @@ static int whereKeyStats( - #endif - assert( pRec!=0 ); - assert( pIdx->nSample>0 ); -- assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); -+ assert( pRec->nField>0 ); -+ - - /* Do a binary search to find the first sample greater than or equal - ** to pRec. If pRec contains a single field, the set of samples to search -@@ -146772,7 +166244,12 @@ static int whereKeyStats( - ** it is extended to two fields. The duplicates that this creates do not - ** cause any problems. - */ -- nField = pRec->nField; -+ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ -+ nField = pIdx->nKeyCol; -+ }else{ -+ nField = pIdx->nColumn; -+ } -+ nField = MIN(pRec->nField, nField); - iCol = 0; - iSample = pIdx->nSample * nField; - do{ -@@ -146838,12 +166315,12 @@ static int whereKeyStats( - if( iCol>0 ){ - pRec->nField = iCol; - assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0 -- || pParse->db->mallocFailed ); -+ || pParse->db->mallocFailed || CORRUPT_DB ); - } - if( i>0 ){ - pRec->nField = nField; - assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 -- || pParse->db->mallocFailed ); -+ || pParse->db->mallocFailed || CORRUPT_DB ); - } - } - } -@@ -146860,7 +166337,7 @@ static int whereKeyStats( - ** is larger than all samples in the array. */ - tRowcnt iUpper, iGap; - if( i>=pIdx->nSample ){ -- iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); -+ iUpper = pIdx->nRowEst0; - }else{ - iUpper = aSample[i].anLt[iCol]; - } -@@ -146935,7 +166412,7 @@ SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCo - ** Value pLoop->nOut is currently set to the estimated number of rows - ** visited for scanning (a=? AND b=?). This function reduces that estimate - ** by some factor to account for the (c BETWEEN ? AND ?) expression based --** on the stat4 data for the index. this scan will be peformed multiple -+** on the stat4 data for the index. this scan will be performed multiple - ** times (once for each (a,b) combination that matches a=?) is dealt with - ** by the caller. - ** -@@ -147016,7 +166493,7 @@ static int whereRangeSkipScanEst( - int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff)); - pLoop->nOut -= nAdjust; - *pbDone = 1; -- WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", -+ WHERETRACE(0x20, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", - nLower, nUpper, nAdjust*-1, pLoop->nOut)); - } - -@@ -147187,14 +166664,15 @@ static int whereRangeScanEst( - ** sample, then assume they are 4x more selective. This brings - ** the estimated selectivity more in line with what it would be - ** if estimated without the use of STAT4 tables. */ -- if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) ); -+ if( iLwrIdx==iUprIdx ){ nNew -= 20; } -+ assert( 20==sqlite3LogEst(4) ); - }else{ - nNew = 10; assert( 10==sqlite3LogEst(2) ); - } - if( nNewwtFlags & TERM_VNULL)==0 ); -+ assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 || pParse->nErr>0 ); - nNew = whereRangeAdjust(pLower, nOut); - nNew = whereRangeAdjust(pUpper, nNew); - -@@ -147227,7 +166705,7 @@ static int whereRangeScanEst( - if( nNewnOut>nOut ){ -- WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n", -+ WHERETRACE(0x20,("Range scan lowers nOut from %d to %d\n", - pLoop->nOut, nOut)); - } - #endif -@@ -147292,7 +166770,7 @@ static int whereEqualScanEst( - pBuilder->nRecValid = nEq; - - whereKeyStats(pParse, p, pRec, 0, a); -- WHERETRACE(0x10,("equality scan regions %s(%d): %d\n", -+ WHERETRACE(0x20,("equality scan regions %s(%d): %d\n", - p->zName, nEq-1, (int)a[1])); - *pnRow = a[1]; - -@@ -147340,9 +166818,9 @@ static int whereInScanEst( - } - - if( rc==SQLITE_OK ){ -- if( nRowEst > nRow0 ) nRowEst = nRow0; -+ if( nRowEst > (tRowcnt)nRow0 ) nRowEst = nRow0; - *pnRow = nRowEst; -- WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst)); -+ WHERETRACE(0x20,("IN row estimate: est=%d\n", nRowEst)); - } - assert( pBuilder->nRecValid==nRecValid ); - return rc; -@@ -147350,7 +166828,7 @@ static int whereInScanEst( - #endif /* SQLITE_ENABLE_STAT4 */ - - --#ifdef WHERETRACE_ENABLED -+#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG) - /* - ** Print the content of a WhereTerm object - */ -@@ -147363,13 +166841,14 @@ SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ - memcpy(zType, "....", 5); - if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; - if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; -- if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L'; -+ if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) zType[2] = 'L'; - if( pTerm->wtFlags & TERM_CODED ) zType[3] = 'C'; - if( pTerm->eOperator & WO_SINGLE ){ -+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); - sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", -- pTerm->leftCursor, pTerm->u.leftColumn); -+ pTerm->leftCursor, pTerm->u.x.leftColumn); - }else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){ -- sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld", -+ sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%llx", - pTerm->u.pOrInfo->indexable); - }else{ - sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor); -@@ -147383,8 +166862,8 @@ SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ - sqlite3DebugPrintf(" prob=%-3d prereq=%llx,%llx", - pTerm->truthProb, (u64)pTerm->prereqAll, (u64)pTerm->prereqRight); - } -- if( pTerm->iField ){ -- sqlite3DebugPrintf(" iField=%d", pTerm->iField); -+ if( (pTerm->eOperator & (WO_OR|WO_AND))==0 && pTerm->u.x.iField ){ -+ sqlite3DebugPrintf(" iField=%d", pTerm->u.x.iField); - } - if( pTerm->iParent>=0 ){ - sqlite3DebugPrintf(" iParent=%d", pTerm->iParent); -@@ -147393,6 +166872,9 @@ SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){ - sqlite3TreeViewExpr(0, pTerm->pExpr, 0); - } - } -+SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){ -+ sqlite3WhereTermPrint(pTerm, 0); -+} - #endif - - #ifdef WHERETRACE_ENABLED -@@ -147410,17 +166892,36 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){ - #ifdef WHERETRACE_ENABLED - /* - ** Print a WhereLoop object for debugging purposes -+** -+** Format example: -+** -+** .--- Position in WHERE clause rSetup, rRun, nOut ---. -+** | | -+** | .--- selfMask nTerm ------. | -+** | | | | -+** | | .-- prereq Idx wsFlags----. | | -+** | | | Name | | | -+** | | | __|__ nEq ---. ___|__ | __|__ -+** | / \ / \ / \ | / \ / \ / \ -+** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 - */ --SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ -- WhereInfo *pWInfo = pWC->pWInfo; -- int nb = 1+(pWInfo->pTabList->nSrc+3)/4; -- struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab; -- Table *pTab = pItem->pTab; -- Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; -- sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, -- p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); -- sqlite3DebugPrintf(" %12s", -- pItem->zAlias ? pItem->zAlias : pTab->zName); -+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ -+ WhereInfo *pWInfo; -+ if( pWC ){ -+ pWInfo = pWC->pWInfo; -+ int nb = 1+(pWInfo->pTabList->nSrc+3)/4; -+ SrcItem *pItem = pWInfo->pTabList->a + p->iTab; -+ Table *pTab = pItem->pSTab; -+ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; -+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, -+ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); -+ sqlite3DebugPrintf(" %12s", -+ pItem->zAlias ? pItem->zAlias : pTab->zName); -+ }else{ -+ pWInfo = 0; -+ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d", -+ p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab); -+ } - if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ - const char *zName; - if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){ -@@ -147445,18 +166946,32 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ - sqlite3_free(z); - } - if( p->wsFlags & WHERE_SKIPSCAN ){ -- sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); -+ sqlite3DebugPrintf(" f %06x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); - }else{ -- sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm); -+ sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm); - } -- sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); -- if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){ -+ if( pWInfo && pWInfo->bStarUsed && p->rStarDelta!=0 ){ -+ sqlite3DebugPrintf(" cost %d,%d,%d delta=%d\n", -+ p->rSetup, p->rRun, p->nOut, p->rStarDelta); -+ }else{ -+ sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); -+ } -+ if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){ - int i; - for(i=0; inLTerm; i++){ - sqlite3WhereTermPrint(p->aLTerm[i], i); - } - } - } -+SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){ -+ if( p ) sqlite3WhereLoopPrint(p, 0); -+} -+SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){ -+ while( p ){ -+ sqlite3ShowWhereLoop(p); -+ p = p->pNextLoop; -+ } -+} - #endif - - /* -@@ -147488,12 +167003,18 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){ - } - - /* --** Deallocate internal memory used by a WhereLoop object -+** Deallocate internal memory used by a WhereLoop object. Leave the -+** object in an initialized state, as if it had been newly allocated. - */ - static void whereLoopClear(sqlite3 *db, WhereLoop *p){ -- if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFreeNN(db, p->aLTerm); -+ if( p->aLTerm!=p->aLTermSpace ){ -+ sqlite3DbFreeNN(db, p->aLTerm); -+ p->aLTerm = p->aLTermSpace; -+ p->nLSlot = ArraySize(p->aLTermSpace); -+ } - whereLoopClearUnion(db, p); -- whereLoopInit(p); -+ p->nLTerm = 0; -+ p->wsFlags = 0; - } - - /* -@@ -147517,8 +167038,10 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){ - */ - static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ - whereLoopClearUnion(db, pTo); -- if( whereLoopResize(db, pTo, pFrom->nLTerm) ){ -- memset(&pTo->u, 0, sizeof(pTo->u)); -+ if( pFrom->nLTerm > pTo->nLSlot -+ && whereLoopResize(db, pTo, pFrom->nLTerm) -+ ){ -+ memset(pTo, 0, WHERE_LOOP_XFER_SZ); - return SQLITE_NOMEM_BKPT; - } - memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); -@@ -147535,80 +167058,91 @@ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ - ** Delete a WhereLoop object - */ - static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ -+ assert( db!=0 ); - whereLoopClear(db, p); -- sqlite3DbFreeNN(db, p); -+ sqlite3DbNNFreeNN(db, p); - } - - /* - ** Free a WhereInfo structure - */ - static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ -- int i; - assert( pWInfo!=0 ); -- for(i=0; inLevel; i++){ -- WhereLevel *pLevel = &pWInfo->a[i]; -- if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ -- sqlite3DbFree(db, pLevel->u.in.aInLoop); -- } -- } -+ assert( db!=0 ); - sqlite3WhereClauseClear(&pWInfo->sWC); - while( pWInfo->pLoops ){ - WhereLoop *p = pWInfo->pLoops; - pWInfo->pLoops = p->pNextLoop; - whereLoopDelete(db, p); - } -- assert( pWInfo->pExprMods==0 ); -- sqlite3DbFreeNN(db, pWInfo); -+ while( pWInfo->pMemToFree ){ -+ WhereMemBlock *pNext = pWInfo->pMemToFree->pNext; -+ sqlite3DbNNFreeNN(db, pWInfo->pMemToFree); -+ pWInfo->pMemToFree = pNext; -+ } -+ sqlite3DbNNFreeNN(db, pWInfo); - } - - /* --** Return TRUE if all of the following are true: -+** Return TRUE if X is a proper subset of Y but is of equal or less cost. -+** In other words, return true if all constraints of X are also part of Y -+** and Y has additional constraints that might speed the search that X lacks -+** but the cost of running X is not more than the cost of running Y. -+** -+** In other words, return true if the cost relationship between X and Y -+** is inverted and needs to be adjusted. -+** -+** Case 1: - ** --** (1) X has the same or lower cost that Y --** (2) X uses fewer WHERE clause terms than Y --** (3) Every WHERE clause term used by X is also used by Y --** (4) X skips at least as many columns as Y --** (5) If X is a covering index, than Y is too -+** (1a) X and Y use the same index. -+** (1b) X has fewer == terms than Y -+** (1c) Neither X nor Y use skip-scan -+** (1d) X does not have a a greater cost than Y - ** --** Conditions (2) and (3) mean that X is a "proper subset" of Y. --** If X is a proper subset of Y then Y is a better choice and ought --** to have a lower cost. This routine returns TRUE when that cost --** relationship is inverted and needs to be adjusted. Constraint (4) --** was added because if X uses skip-scan less than Y it still might --** deserve a lower cost even if it is a proper subset of Y. Constraint (5) --** was added because a covering index probably deserves to have a lower cost --** than a non-covering index even if it is a proper subset. -+** Case 2: -+** -+** (2a) X has the same or lower cost, or returns the same or fewer rows, -+** than Y. -+** (2b) X uses fewer WHERE clause terms than Y -+** (2c) Every WHERE clause term used by X is also used by Y -+** (2d) X skips at least as many columns as Y -+** (2e) If X is a covering index, than Y is too - */ - static int whereLoopCheaperProperSubset( - const WhereLoop *pX, /* First WhereLoop to compare */ - const WhereLoop *pY /* Compare against this WhereLoop */ - ){ - int i, j; -- if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ -- return 0; /* X is not a subset of Y */ -+ if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */ -+ assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 ); -+ assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 ); -+ if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */ -+ && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */ -+ && pX->nSkip==0 && pY->nSkip==0 /* (1c) */ -+ ){ -+ return 1; /* Case 1 is true */ - } -- if( pY->nSkip > pX->nSkip ) return 0; -- if( pX->rRun >= pY->rRun ){ -- if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */ -- if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */ -+ if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ -+ return 0; /* (2b) */ - } -+ if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */ - for(i=pX->nLTerm-1; i>=0; i--){ - if( pX->aLTerm[i]==0 ) continue; - for(j=pY->nLTerm-1; j>=0; j--){ - if( pY->aLTerm[j]==pX->aLTerm[i] ) break; - } -- if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */ -+ if( j<0 ) return 0; /* (2c) */ - } - if( (pX->wsFlags&WHERE_IDX_ONLY)!=0 - && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){ -- return 0; /* Constraint (5) */ -+ return 0; /* (2e) */ - } -- return 1; /* All conditions meet */ -+ return 1; /* Case 2 is true */ - } - - /* --** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so --** that: -+** Try to adjust the cost and number of output rows of WhereLoop pTemplate -+** upwards or downwards so that: - ** - ** (1) pTemplate costs less than any other WhereLoops that are a proper - ** subset of pTemplate -@@ -147629,16 +167163,20 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ - /* Adjust pTemplate cost downward so that it is cheaper than its - ** subset p. */ - WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", -- pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1)); -- pTemplate->rRun = p->rRun; -- pTemplate->nOut = p->nOut - 1; -+ pTemplate->rRun, pTemplate->nOut, -+ MIN(p->rRun, pTemplate->rRun), -+ MIN(p->nOut - 1, pTemplate->nOut))); -+ pTemplate->rRun = MIN(p->rRun, pTemplate->rRun); -+ pTemplate->nOut = MIN(p->nOut - 1, pTemplate->nOut); - }else if( whereLoopCheaperProperSubset(pTemplate, p) ){ - /* Adjust pTemplate cost upward so that it is costlier than p since - ** pTemplate is a proper subset of p */ - WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", -- pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1)); -- pTemplate->rRun = p->rRun; -- pTemplate->nOut = p->nOut + 1; -+ pTemplate->rRun, pTemplate->nOut, -+ MAX(p->rRun, pTemplate->rRun), -+ MAX(p->nOut + 1, pTemplate->nOut))); -+ pTemplate->rRun = MAX(p->rRun, pTemplate->rRun); -+ pTemplate->nOut = MAX(p->nOut + 1, pTemplate->nOut); - } - } - } -@@ -147680,7 +167218,7 @@ static WhereLoop **whereLoopFindLesser( - ** rSetup. Call this SETUP-INVARIANT */ - assert( p->rSetup>=pTemplate->rSetup ); - -- /* Any loop using an appliation-defined index (or PRIMARY KEY or -+ /* Any loop using an application-defined index (or PRIMARY KEY or - ** UNIQUE constraint) with one or more == constraints is better - ** than an automatic index. Unless it is a skip-scan. */ - if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 -@@ -147707,7 +167245,7 @@ static WhereLoop **whereLoopFindLesser( - - /* If pTemplate is always better than p, then cause p to be overwritten - ** with pTemplate. pTemplate is better than p if: -- ** (1) pTemplate has no more dependences than p, and -+ ** (1) pTemplate has no more dependencies than p, and - ** (2) pTemplate has an equal or lower cost than p. - */ - if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */ -@@ -147825,7 +167363,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ - }else{ - /* We will be overwriting WhereLoop p[]. But before we do, first - ** go through the rest of the list and delete any other entries besides -- ** p[] that are also supplated by pTemplate */ -+ ** p[] that are also supplanted by pTemplate */ - WhereLoop **ppTail = &p->pNextLoop; - WhereLoop *pToDel; - while( *ppTail ){ -@@ -147893,11 +167431,11 @@ static void whereLoopOutputAdjust( - LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */ - - assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 ); -- for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){ -+ for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){ - assert( pTerm!=0 ); -- if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break; -- if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; - if( (pTerm->prereqAll & notAllowed)!=0 ) continue; -+ if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue; -+ if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) continue; - for(j=pLoop->nLTerm-1; j>=0; j--){ - pX = pLoop->aLTerm[j]; - if( pX==0 ) continue; -@@ -147905,6 +167443,24 @@ static void whereLoopOutputAdjust( - if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break; - } - if( j<0 ){ -+ sqlite3ProgressCheck(pWC->pWInfo->pParse); -+ if( pLoop->maskSelf==pTerm->prereqAll ){ -+ /* If there are extra terms in the WHERE clause not used by an index -+ ** that depend only on the table being scanned, and that will tend to -+ ** cause many rows to be omitted, then mark that table as -+ ** "self-culling". -+ ** -+ ** 2022-03-24: Self-culling only applies if either the extra terms -+ ** are straight comparison operators that are non-true with NULL -+ ** operand, or if the loop is not an OUTER JOIN. -+ */ -+ if( (pTerm->eOperator & 0x3f)!=0 -+ || (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype -+ & (JT_LEFT|JT_LTORJ))==0 -+ ){ -+ pLoop->wsFlags |= WHERE_SELFCULL; -+ } -+ } - if( pTerm->truthProb<=0 ){ - /* If a truth probability is specified using the likelihood() hints, - ** then use the probability provided by the application. */ -@@ -147919,7 +167475,7 @@ static void whereLoopOutputAdjust( - Expr *pRight = pTerm->pExpr->pRight; - int k = 0; - testcase( pTerm->pExpr->op==TK_IS ); -- if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){ -+ if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){ - k = 10; - }else{ - k = 20; -@@ -147932,7 +167488,9 @@ static void whereLoopOutputAdjust( - } - } - } -- if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce; -+ if( pLoop->nOut > nRow-iReduce ){ -+ pLoop->nOut = nRow - iReduce; -+ } - } - - /* -@@ -147969,9 +167527,12 @@ static int whereRangeVectorLen( - char aff; /* Comparison affinity */ - char idxaff = 0; /* Indexed columns affinity */ - CollSeq *pColl; /* Comparison collation sequence */ -- Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; -- Expr *pRhs = pTerm->pExpr->pRight; -- if( pRhs->flags & EP_xIsSelect ){ -+ Expr *pLhs, *pRhs; -+ -+ assert( ExprUseXList(pTerm->pExpr->pLeft) ); -+ pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr; -+ pRhs = pTerm->pExpr->pRight; -+ if( ExprUseXSelect(pRhs) ){ - pRhs = pRhs->x.pSelect->pEList->a[i].pExpr; - }else{ - pRhs = pRhs->x.pList->a[i].pExpr; -@@ -148002,7 +167563,7 @@ static int whereRangeVectorLen( - } - - /* --** Adjust the cost C by the costMult facter T. This only occurs if -+** Adjust the cost C by the costMult factor T. This only occurs if - ** compiled with -DSQLITE_ENABLE_COSTMULT - */ - #ifdef SQLITE_ENABLE_COSTMULT -@@ -148025,11 +167586,11 @@ static int whereRangeVectorLen( - */ - static int whereLoopAddBtreeIndex( - WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ -- struct SrcList_item *pSrc, /* FROM clause term being analyzed */ -+ SrcItem *pSrc, /* FROM clause term being analyzed */ - Index *pProbe, /* An index on pSrc */ - LogEst nInMul /* log(Number of iterations due to IN) */ - ){ -- WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */ -+ WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyze context */ - Parse *pParse = pWInfo->pParse; /* Parsing context */ - sqlite3 *db = pParse->db; /* Database connection malloc context */ - WhereLoop *pNew; /* Template WhereLoop under construction */ -@@ -148050,10 +167611,13 @@ static int whereLoopAddBtreeIndex( - WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ - - pNew = pBuilder->pNew; -- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; -- WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n", -+ assert( db->mallocFailed==0 || pParse->nErr>0 ); -+ if( pParse->nErr ){ -+ return pParse->rc; -+ } -+ WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n", - pProbe->pTable->zName,pProbe->zName, -- pNew->u.btree.nEq, pNew->nSkip)); -+ pNew->u.btree.nEq, pNew->nSkip, pNew->rRun)); - - assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); - assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); -@@ -148063,9 +167627,13 @@ static int whereLoopAddBtreeIndex( - assert( pNew->u.btree.nBtm==0 ); - opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; - } -- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); -+ if( pProbe->bUnordered ){ -+ opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); -+ } - - assert( pNew->u.btree.nEqnColumn ); -+ assert( pNew->u.btree.nEqnKeyCol -+ || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY ); - - saved_nEq = pNew->u.btree.nEq; - saved_nBtm = pNew->u.btree.nBtm; -@@ -148099,15 +167667,11 @@ static int whereLoopAddBtreeIndex( - ** to mix with a lower range bound from some other source */ - if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; - -- /* tag-20191211-001: Do not allow constraints from the WHERE clause to -- ** be used by the right table of a LEFT JOIN. Only constraints in the -- ** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */ -- if( (pSrc->fg.jointype & JT_LEFT)!=0 -- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) -+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 -+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc) - ){ - continue; - } -- - if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ - pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE; - }else{ -@@ -148118,7 +167682,11 @@ static int whereLoopAddBtreeIndex( - pNew->u.btree.nBtm = saved_nBtm; - pNew->u.btree.nTop = saved_nTop; - pNew->nLTerm = saved_nLTerm; -- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ -+ if( pNew->nLTerm>=pNew->nLSlot -+ && whereLoopResize(db, pNew, pNew->nLTerm+1) -+ ){ -+ break; /* OOM while trying to enlarge the pNew->aLTerm array */ -+ } - pNew->aLTerm[pNew->nLTerm++] = pTerm; - pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf; - -@@ -148130,9 +167698,10 @@ static int whereLoopAddBtreeIndex( - - if( eOp & WO_IN ){ - Expr *pExpr = pTerm->pExpr; -- if( ExprHasProperty(pExpr, EP_xIsSelect) ){ -+ if( ExprUseXSelect(pExpr) ){ - /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ - int i; -+ int bRedundant = 0; - nIn = 46; assert( 46==sqlite3LogEst(25) ); - - /* The expression may actually be of the form (x, y) IN (SELECT...). -@@ -148141,14 +167710,27 @@ static int whereLoopAddBtreeIndex( - ** for each such term. The following loop checks that pTerm is the - ** first such term in use, and sets nIn back to 0 if it is not. */ - for(i=0; inLTerm-1; i++){ -- if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0; -+ if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ){ -+ nIn = 0; -+ if( pNew->aLTerm[i]->u.x.iField == pTerm->u.x.iField ){ -+ /* Detect when two or more columns of an index match the same -+ ** column of a vector IN operater, and avoid adding the column -+ ** to the WhereLoop more than once. See tag-20250707-01 -+ ** in test/rowvalue.test */ -+ bRedundant = 1; -+ } -+ } -+ } -+ if( bRedundant ){ -+ pNew->nLTerm--; -+ continue; - } - }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ - /* "x IN (value, value, ...)" */ - nIn = sqlite3LogEst(pExpr->x.pList->nExpr); - } -- if( pProbe->hasStat1 ){ -- LogEst M, logK, safetyMargin; -+ if( pProbe->hasStat1 && rLogSize>=10 ){ -+ LogEst M, logK, x; - /* Let: - ** N = the total number of rows in the table - ** K = the number of entries on the RHS of the IN operator -@@ -148166,20 +167748,30 @@ static int whereLoopAddBtreeIndex( - ** a safety margin of 2 (LogEst: 10) that favors using the IN operator - ** with the index, as using an index has better worst-case behavior. - ** If we do not have real sqlite_stat1 data, always prefer to use -- ** the index. -+ ** the index. Do not bother with this optimization on very small -+ ** tables (less than 2 rows) as it is pointless in that case. - */ - M = pProbe->aiRowLogEst[saved_nEq]; - logK = estLog(nIn); -- safetyMargin = 10; /* TUNING: extra weight for indexed IN */ -- if( M + logK + safetyMargin < nIn + rLogSize ){ -+ /* TUNING v----- 10 to bias toward indexed IN */ -+ x = M + logK + 10 - (nIn + rLogSize); -+ if( x>=0 ){ - WHERETRACE(0x40, -- ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n", -- saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); -- continue; -+ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d) " -+ "prefers indexed lookup\n", -+ saved_nEq, M, logK, nIn, rLogSize, x)); -+ }else if( nInMul<2 && OptimizationEnabled(db, SQLITE_SeekScan) ){ -+ WHERETRACE(0x40, -+ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" -+ " nInMul=%d) prefers skip-scan\n", -+ saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); -+ pNew->wsFlags |= WHERE_IN_SEEKSCAN; - }else{ - WHERETRACE(0x40, -- ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n", -- saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); -+ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d" -+ " nInMul=%d) prefers normal scan\n", -+ saved_nEq, M, logK, nIn, rLogSize, x, nInMul)); -+ continue; - } - } - pNew->wsFlags |= WHERE_COLUMN_IN; -@@ -148191,47 +167783,49 @@ static int whereLoopAddBtreeIndex( - || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) - ){ - if( iCol==XN_ROWID || pProbe->uniqNotNull -- || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ) -+ || (pProbe->nKeyCol==1 && pProbe->onError && (eOp & WO_EQ)) - ){ - pNew->wsFlags |= WHERE_ONEROW; - }else{ - pNew->wsFlags |= WHERE_UNQ_WANTED; - } - } -+ if( scan.iEquiv>1 ) pNew->wsFlags |= WHERE_TRANSCONS; - }else if( eOp & WO_ISNULL ){ - pNew->wsFlags |= WHERE_COLUMN_NULL; -- }else if( eOp & (WO_GT|WO_GE) ){ -- testcase( eOp & WO_GT ); -- testcase( eOp & WO_GE ); -- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; -- pNew->u.btree.nBtm = whereRangeVectorLen( -- pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm -- ); -- pBtm = pTerm; -- pTop = 0; -- if( pTerm->wtFlags & TERM_LIKEOPT ){ -- /* Range contraints that come from the LIKE optimization are -- ** always used in pairs. */ -- pTop = &pTerm[1]; -- assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); -- assert( pTop->wtFlags & TERM_LIKEOPT ); -- assert( pTop->eOperator==WO_LT ); -- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ -- pNew->aLTerm[pNew->nLTerm++] = pTop; -- pNew->wsFlags |= WHERE_TOP_LIMIT; -- pNew->u.btree.nTop = 1; -- } -- }else{ -- assert( eOp & (WO_LT|WO_LE) ); -- testcase( eOp & WO_LT ); -- testcase( eOp & WO_LE ); -- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; -- pNew->u.btree.nTop = whereRangeVectorLen( -+ }else{ -+ int nVecLen = whereRangeVectorLen( - pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm - ); -- pTop = pTerm; -- pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? -- pNew->aLTerm[pNew->nLTerm-2] : 0; -+ if( eOp & (WO_GT|WO_GE) ){ -+ testcase( eOp & WO_GT ); -+ testcase( eOp & WO_GE ); -+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; -+ pNew->u.btree.nBtm = nVecLen; -+ pBtm = pTerm; -+ pTop = 0; -+ if( pTerm->wtFlags & TERM_LIKEOPT ){ -+ /* Range constraints that come from the LIKE optimization are -+ ** always used in pairs. */ -+ pTop = &pTerm[1]; -+ assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); -+ assert( pTop->wtFlags & TERM_LIKEOPT ); -+ assert( pTop->eOperator==WO_LT ); -+ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ -+ pNew->aLTerm[pNew->nLTerm++] = pTop; -+ pNew->wsFlags |= WHERE_TOP_LIMIT; -+ pNew->u.btree.nTop = 1; -+ } -+ }else{ -+ assert( eOp & (WO_LT|WO_LE) ); -+ testcase( eOp & WO_LT ); -+ testcase( eOp & WO_LE ); -+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; -+ pNew->u.btree.nTop = nVecLen; -+ pTop = pTerm; -+ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? -+ pNew->aLTerm[pNew->nLTerm-2] : 0; -+ } - } - - /* At this point pNew->nOut is set to the number of rows expected to -@@ -148259,8 +167853,8 @@ static int whereLoopAddBtreeIndex( - tRowcnt nOut = 0; - if( nInMul==0 - && pProbe->nSample -- && pNew->u.btree.nEq<=pProbe->nSampleCol -- && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)) -+ && ALWAYS(pNew->u.btree.nEq<=pProbe->nSampleCol) -+ && ((eOp & WO_IN)==0 || ExprUseXList(pTerm->pExpr)) - && OptimizationEnabled(db, SQLITE_Stat4) - ){ - Expr *pExpr = pTerm->pExpr; -@@ -148283,7 +167877,7 @@ static int whereLoopAddBtreeIndex( - && pNew->nOut+10 > pProbe->aiRowLogEst[0] - ){ - #if WHERETRACE_ENABLED /* 0x01 */ -- if( sqlite3WhereTrace & 0x01 ){ -+ if( sqlite3WhereTrace & 0x20 ){ - sqlite3DebugPrintf( - "STAT4 determines term has low selectivity:\n"); - sqlite3WhereTermPrint(pTerm, 999); -@@ -148315,14 +167909,33 @@ static int whereLoopAddBtreeIndex( - } - } - -- /* Set rCostIdx to the cost of visiting selected rows in index. Add -- ** it to pNew->rRun, which is currently set to the cost of the index -- ** seek only. Then, if this is a non-covering index, add the cost of -- ** visiting the rows in the main table. */ -- assert( pSrc->pTab->szTabRow>0 ); -- rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; -- pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx); -- if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){ -+ /* Set rCostIdx to the estimated cost of visiting selected rows in the -+ ** index. The estimate is the sum of two values: -+ ** 1. The cost of doing one search-by-key to find the first matching -+ ** entry -+ ** 2. Stepping forward in the index pNew->nOut times to find all -+ ** additional matching entries. -+ */ -+ assert( pSrc->pSTab->szTabRow>0 ); -+ if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ -+ /* The pProbe->szIdxRow is low for an IPK table since the interior -+ ** pages are small. Thus szIdxRow gives a good estimate of seek cost. -+ ** But the leaf pages are full-size, so pProbe->szIdxRow would badly -+ ** under-estimate the scanning cost. */ -+ rCostIdx = pNew->nOut + 16; -+ }else{ -+ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pSTab->szTabRow; -+ } -+ rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx); -+ -+ /* Estimate the cost of running the loop. If all data is coming -+ ** from the index, then this is just the cost of doing the index -+ ** lookup and scan. But if some data is coming out of the main table, -+ ** we also have to add in the cost of doing pNew->nOut searches to -+ ** locate the row in the main table that corresponds to the index entry. -+ */ -+ pNew->rRun = rCostIdx; -+ if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){ - pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); - } - ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult); -@@ -148341,7 +167954,12 @@ static int whereLoopAddBtreeIndex( - - if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 - && pNew->u.btree.nEqnColumn -+ && (pNew->u.btree.nEqnKeyCol || -+ pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY) - ){ -+ if( pNew->u.btree.nEq>3 ){ -+ sqlite3ProgressCheck(pParse); -+ } - whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); - } - pNew->nOut = saved_nOut; -@@ -148421,7 +168039,10 @@ static int indexMightHelpWithOrderBy( - if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; - for(ii=0; iinExpr; ii++){ - Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); -- if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ -+ if( NEVER(pExpr==0) ) continue; -+ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) -+ && pExpr->iTable==iCursor -+ ){ - if( pExpr->iColumn<0 ) return 1; - for(jj=0; jjnKeyCol; jj++){ - if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; -@@ -148443,24 +168064,48 @@ static int indexMightHelpWithOrderBy( - */ - static int whereUsablePartialIndex( - int iTab, /* The table for which we want an index */ -- int isLeft, /* True if iTab is the right table of a LEFT JOIN */ -+ u8 jointype, /* The JT_* flags on the join */ - WhereClause *pWC, /* The WHERE clause of the query */ - Expr *pWhere /* The WHERE clause from the partial index */ - ){ - int i; - WhereTerm *pTerm; -- Parse *pParse = pWC->pWInfo->pParse; -+ Parse *pParse; -+ -+ if( jointype & JT_LTORJ ) return 0; -+ pParse = pWC->pWInfo->pParse; - while( pWhere->op==TK_AND ){ -- if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0; -+ if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0; - pWhere = pWhere->pRight; - } -- if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; - for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ - Expr *pExpr; - pExpr = pTerm->pExpr; -- if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) -- && (isLeft==0 || ExprHasProperty(pExpr, EP_FromJoin)) -+ if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) -+ && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON)) - && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) -+ && !sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, -1) -+ && (pTerm->wtFlags & TERM_VNULL)==0 -+ ){ -+ return 1; -+ } -+ } -+ return 0; -+} -+ -+/* -+** pIdx is an index containing expressions. Check it see if any of the -+** expressions in the index match the pExpr expression. -+*/ -+static int exprIsCoveredByIndex( -+ const Expr *pExpr, -+ const Index *pIdx, -+ int iTabCur -+){ -+ int i; -+ for(i=0; inColumn; i++){ -+ if( pIdx->aiColumn[i]==XN_EXPR -+ && sqlite3ExprCompare(0, pExpr, pIdx->aColExpr->a[i].pExpr, iTabCur)==0 - ){ - return 1; - } -@@ -148468,6 +168113,223 @@ static int whereUsablePartialIndex( - return 0; - } - -+/* -+** Structure passed to the whereIsCoveringIndex Walker callback. -+*/ -+typedef struct CoveringIndexCheck CoveringIndexCheck; -+struct CoveringIndexCheck { -+ Index *pIdx; /* The index */ -+ int iTabCur; /* Cursor number for the corresponding table */ -+ u8 bExpr; /* Uses an indexed expression */ -+ u8 bUnidx; /* Uses an unindexed column not within an indexed expr */ -+}; -+ -+/* -+** Information passed in is pWalk->u.pCovIdxCk. Call it pCk. -+** -+** If the Expr node references the table with cursor pCk->iTabCur, then -+** make sure that column is covered by the index pCk->pIdx. We know that -+** all columns less than 63 (really BMS-1) are covered, so we don't need -+** to check them. But we do need to check any column at 63 or greater. -+** -+** If the index does not cover the column, then set pWalk->eCode to -+** non-zero and return WRC_Abort to stop the search. -+** -+** If this node does not disprove that the index can be a covering index, -+** then just return WRC_Continue, to continue the search. -+** -+** If pCk->pIdx contains indexed expressions and one of those expressions -+** matches pExpr, then prune the search. -+*/ -+static int whereIsCoveringIndexWalkCallback(Walker *pWalk, Expr *pExpr){ -+ int i; /* Loop counter */ -+ const Index *pIdx; /* The index of interest */ -+ const i16 *aiColumn; /* Columns contained in the index */ -+ u16 nColumn; /* Number of columns in the index */ -+ CoveringIndexCheck *pCk; /* Info about this search */ -+ -+ pCk = pWalk->u.pCovIdxCk; -+ pIdx = pCk->pIdx; -+ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) ){ -+ /* if( pExpr->iColumn<(BMS-1) && pIdx->bHasExpr==0 ) return WRC_Continue;*/ -+ if( pExpr->iTable!=pCk->iTabCur ) return WRC_Continue; -+ pIdx = pWalk->u.pCovIdxCk->pIdx; -+ aiColumn = pIdx->aiColumn; -+ nColumn = pIdx->nColumn; -+ for(i=0; iiColumn ) return WRC_Continue; -+ } -+ pCk->bUnidx = 1; -+ return WRC_Abort; -+ }else if( pIdx->bHasExpr -+ && exprIsCoveredByIndex(pExpr, pIdx, pWalk->u.pCovIdxCk->iTabCur) ){ -+ pCk->bExpr = 1; -+ return WRC_Prune; -+ } -+ return WRC_Continue; -+} -+ -+ -+/* -+** pIdx is an index that covers all of the low-number columns used by -+** pWInfo->pSelect (columns from 0 through 62) or an index that has -+** expressions terms. Hence, we cannot determine whether or not it is -+** a covering index by using the colUsed bitmasks. We have to do a search -+** to see if the index is covering. This routine does that search. -+** -+** The return value is one of these: -+** -+** 0 The index is definitely not a covering index -+** -+** WHERE_IDX_ONLY The index is definitely a covering index -+** -+** WHERE_EXPRIDX The index is likely a covering index, but it is -+** difficult to determine precisely because of the -+** expressions that are indexed. Score it as a -+** covering index, but still keep the main table open -+** just in case we need it. -+** -+** This routine is an optimization. It is always safe to return zero. -+** But returning one of the other two values when zero should have been -+** returned can lead to incorrect bytecode and assertion faults. -+*/ -+static SQLITE_NOINLINE u32 whereIsCoveringIndex( -+ WhereInfo *pWInfo, /* The WHERE clause context */ -+ Index *pIdx, /* Index that is being tested */ -+ int iTabCur /* Cursor for the table being indexed */ -+){ -+ int i, rc; -+ struct CoveringIndexCheck ck; -+ Walker w; -+ if( pWInfo->pSelect==0 ){ -+ /* We don't have access to the full query, so we cannot check to see -+ ** if pIdx is covering. Assume it is not. */ -+ return 0; -+ } -+ if( pIdx->bHasExpr==0 ){ -+ for(i=0; inColumn; i++){ -+ if( pIdx->aiColumn[i]>=BMS-1 ) break; -+ } -+ if( i>=pIdx->nColumn ){ -+ /* pIdx does not index any columns greater than 62, but we know from -+ ** colMask that columns greater than 62 are used, so this is not a -+ ** covering index */ -+ return 0; -+ } -+ } -+ ck.pIdx = pIdx; -+ ck.iTabCur = iTabCur; -+ ck.bExpr = 0; -+ ck.bUnidx = 0; -+ memset(&w, 0, sizeof(w)); -+ w.xExprCallback = whereIsCoveringIndexWalkCallback; -+ w.xSelectCallback = sqlite3SelectWalkNoop; -+ w.u.pCovIdxCk = &ck; -+ sqlite3WalkSelect(&w, pWInfo->pSelect); -+ if( ck.bUnidx ){ -+ rc = 0; -+ }else if( ck.bExpr ){ -+ rc = WHERE_EXPRIDX; -+ }else{ -+ rc = WHERE_IDX_ONLY; -+ } -+ return rc; -+} -+ -+/* -+** This is an sqlite3ParserAddCleanup() callback that is invoked to -+** free the Parse->pIdxEpr list when the Parse object is destroyed. -+*/ -+static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){ -+ IndexedExpr **pp = (IndexedExpr**)pObject; -+ while( *pp!=0 ){ -+ IndexedExpr *p = *pp; -+ *pp = p->pIENext; -+ sqlite3ExprDelete(db, p->pExpr); -+ sqlite3DbFreeNN(db, p); -+ } -+} -+ -+/* -+** This function is called for a partial index - one with a WHERE clause - in -+** two scenarios. In both cases, it determines whether or not the WHERE -+** clause on the index implies that a column of the table may be safely -+** replaced by a constant expression. For example, in the following -+** SELECT: -+** -+** CREATE INDEX i1 ON t1(b, c) WHERE a=; -+** SELECT a, b, c FROM t1 WHERE a= AND b=?; -+** -+** The "a" in the select-list may be replaced by , iff: -+** -+** (a) is a constant expression, and -+** (b) The (a=) comparison uses the BINARY collation sequence, and -+** (c) Column "a" has an affinity other than NONE or BLOB. -+** -+** If argument pItem is NULL, then pMask must not be NULL. In this case this -+** function is being called as part of determining whether or not pIdx -+** is a covering index. This function clears any bits in (*pMask) -+** corresponding to columns that may be replaced by constants as described -+** above. -+** -+** Otherwise, if pItem is not NULL, then this function is being called -+** as part of coding a loop that uses index pIdx. In this case, add entries -+** to the Parse.pIdxPartExpr list for each column that can be replaced -+** by a constant. -+*/ -+static void wherePartIdxExpr( -+ Parse *pParse, /* Parse context */ -+ Index *pIdx, /* Partial index being processed */ -+ Expr *pPart, /* WHERE clause being processed */ -+ Bitmask *pMask, /* Mask to clear bits in */ -+ int iIdxCur, /* Cursor number for index */ -+ SrcItem *pItem /* The FROM clause entry for the table */ -+){ -+ assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 ); -+ assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) ); -+ -+ if( pPart->op==TK_AND ){ -+ wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem); -+ pPart = pPart->pLeft; -+ } -+ -+ if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){ -+ Expr *pLeft = pPart->pLeft; -+ Expr *pRight = pPart->pRight; -+ u8 aff; -+ -+ if( pLeft->op!=TK_COLUMN ) return; -+ if( !sqlite3ExprIsConstant(0, pRight) ) return; -+ if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; -+ if( pLeft->iColumn<0 ) return; -+ aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; -+ if( aff>=SQLITE_AFF_TEXT ){ -+ if( pItem ){ -+ sqlite3 *db = pParse->db; -+ IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p)); -+ if( p ){ -+ int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0; -+ p->pExpr = sqlite3ExprDup(db, pRight, 0); -+ p->iDataCur = pItem->iCursor; -+ p->iIdxCur = iIdxCur; -+ p->iIdxCol = pLeft->iColumn; -+ p->bMaybeNullRow = bNullRow; -+ p->pIENext = pParse->pIdxPartExpr; -+ p->aff = aff; -+ pParse->pIdxPartExpr = p; -+ if( p->pIENext==0 ){ -+ void *pArg = (void*)&pParse->pIdxPartExpr; -+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); -+ } -+ } -+ }else if( pLeft->iColumn<(BMS-1) ){ -+ *pMask &= ~((Bitmask)1 << pLeft->iColumn); -+ } -+ } -+ } -+} -+ -+ - /* - ** Add all WhereLoop objects for a single table of the join where the table - ** is identified by pBuilder->pNew->iTab. That table is guaranteed to be -@@ -148506,7 +168368,7 @@ static int whereUsablePartialIndex( - */ - static int whereLoopAddBtree( - WhereLoopBuilder *pBuilder, /* WHERE clause information */ -- Bitmask mPrereq /* Extra prerequesites for using this table */ -+ Bitmask mPrereq /* Extra prerequisites for using this table */ - ){ - WhereInfo *pWInfo; /* WHERE analysis context */ - Index *pProbe; /* An index we are evaluating */ -@@ -148514,13 +168376,12 @@ static int whereLoopAddBtree( - LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */ - i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */ - SrcList *pTabList; /* The FROM clause */ -- struct SrcList_item *pSrc; /* The FROM clause btree term to add */ -+ SrcItem *pSrc; /* The FROM clause btree term to add */ - WhereLoop *pNew; /* Template WhereLoop object */ - int rc = SQLITE_OK; /* Return code */ - int iSortIdx = 1; /* Index number */ - int b; /* A boolean value */ - LogEst rSize; /* number of rows in the table */ -- LogEst rLogSize; /* Logarithm of the number of rows in the table */ - WhereClause *pWC; /* The parsed WHERE clause */ - Table *pTab; /* Table being queried */ - -@@ -148528,13 +168389,14 @@ static int whereLoopAddBtree( - pWInfo = pBuilder->pWInfo; - pTabList = pWInfo->pTabList; - pSrc = pTabList->a + pNew->iTab; -- pTab = pSrc->pTab; -+ pTab = pSrc->pSTab; - pWC = pBuilder->pWC; -- assert( !IsVirtual(pSrc->pTab) ); -+ assert( !IsVirtual(pSrc->pSTab) ); - -- if( pSrc->pIBIndex ){ -+ if( pSrc->fg.isIndexedBy ){ -+ assert( pSrc->fg.isCte==0 ); - /* An INDEXED BY clause specifies a particular index to use */ -- pProbe = pSrc->pIBIndex; -+ pProbe = pSrc->u2.pIBIndex; - }else if( !HasRowid(pTab) ){ - pProbe = pTab->pIndex; - }else{ -@@ -148550,11 +168412,11 @@ static int whereLoopAddBtree( - sPk.aiRowLogEst = aiRowEstPk; - sPk.onError = OE_Replace; - sPk.pTable = pTab; -- sPk.szIdxRow = pTab->szTabRow; -+ sPk.szIdxRow = 3; /* TUNING: Interior rows of IPK table are very small */ - sPk.idxType = SQLITE_IDXTYPE_IPK; - aiRowEstPk[0] = pTab->nRowLogEst; - aiRowEstPk[1] = 0; -- pFirst = pSrc->pTab->pIndex; -+ pFirst = pSrc->pSTab->pIndex; - if( pSrc->fg.notIndexed==0 ){ - /* The real indices of the table are only considered if the - ** NOT INDEXED qualifier is omitted from the FROM clause */ -@@ -148563,22 +168425,23 @@ static int whereLoopAddBtree( - pProbe = &sPk; - } - rSize = pTab->nRowLogEst; -- rLogSize = estLog(rSize); - - #ifndef SQLITE_OMIT_AUTOMATIC_INDEX - /* Automatic indexes */ - if( !pBuilder->pOrSet /* Not part of an OR optimization */ -- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 -+ && (pWInfo->wctrlFlags & (WHERE_RIGHT_JOIN|WHERE_OR_SUBCLAUSE))==0 - && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 -- && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ -+ && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */ - && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ -- && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ - && !pSrc->fg.isCorrelated /* Not a correlated subquery */ - && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ -+ && (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */ - ){ - /* Generate auto-index WhereLoops */ -+ LogEst rLogSize; /* Logarithm of the number of rows in the table */ - WhereTerm *pTerm; - WhereTerm *pWCEnd = pWC->a + pWC->nTerm; -+ rLogSize = estLog(rSize); - for(pTerm=pWC->a; rc==SQLITE_OK && pTermprereqRight & pNew->maskSelf ) continue; - if( termCanDriveIndex(pTerm, pSrc, 0) ){ -@@ -148596,10 +168459,11 @@ static int whereLoopAddBtree( - ** those objects, since there is no opportunity to add schema - ** indexes on subqueries and views. */ - pNew->rSetup = rLogSize + rSize; -- if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){ -+ if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){ - pNew->rSetup += 28; - }else{ -- pNew->rSetup -= 10; -+ pNew->rSetup -= 25; /* Greatly reduced setup cost for auto indexes -+ ** on ephemeral materializations of views */ - } - ApplyCostMultiplier(pNew->rSetup, pTab->costMult); - if( pNew->rSetup<0 ) pNew->rSetup = 0; -@@ -148620,11 +168484,10 @@ static int whereLoopAddBtree( - /* Loop over all indices. If there was an INDEXED BY clause, then only - ** consider index pProbe. */ - for(; rc==SQLITE_OK && pProbe; -- pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++ -+ pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++ - ){ -- int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0; - if( pProbe->pPartIdxWhere!=0 -- && !whereUsablePartialIndex(pSrc->iCursor, isLeft, pWC, -+ && !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC, - pProbe->pPartIdxWhere) - ){ - testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */ -@@ -148635,6 +168498,7 @@ static int whereLoopAddBtree( - pNew->u.btree.nEq = 0; - pNew->u.btree.nBtm = 0; - pNew->u.btree.nTop = 0; -+ pNew->u.btree.nDistinctCol = 0; - pNew->nSkip = 0; - pNew->nLTerm = 0; - pNew->iSortIdx = 0; -@@ -148642,6 +168506,7 @@ static int whereLoopAddBtree( - pNew->prereq = mPrereq; - pNew->nOut = rSize; - pNew->u.btree.pIndex = pProbe; -+ pNew->u.btree.pOrderBy = 0; - b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); - - /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ -@@ -148652,21 +168517,74 @@ static int whereLoopAddBtree( - - /* Full table scan */ - pNew->iSortIdx = b ? iSortIdx : 0; -- /* TUNING: Cost of full table scan is (N*3.0). */ -+ /* TUNING: Cost of full table scan is 3.0*N. The 3.0 factor is an -+ ** extra cost designed to discourage the use of full table scans, -+ ** since index lookups have better worst-case performance if our -+ ** stat guesses are wrong. Reduce the 3.0 penalty slightly -+ ** (to 2.75) if we have valid STAT4 information for the table. -+ ** At 2.75, a full table scan is preferred over using an index on -+ ** a column with just two distinct values where each value has about -+ ** an equal number of appearances. Without STAT4 data, we still want -+ ** to use an index in that case, since the constraint might be for -+ ** the scarcer of the two values, and in that case an index lookup is -+ ** better. -+ */ -+#ifdef SQLITE_ENABLE_STAT4 -+ pNew->rRun = rSize + 16 - 2*((pTab->tabFlags & TF_HasStat4)!=0); -+#else - pNew->rRun = rSize + 16; -+#endif - ApplyCostMultiplier(pNew->rRun, pTab->costMult); - whereLoopOutputAdjust(pWC, pNew, rSize); -+ if( pSrc->fg.isSubquery ){ -+ if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE; -+ pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; -+ } - rc = whereLoopInsert(pBuilder, pNew); - pNew->nOut = rSize; - if( rc ) break; - }else{ - Bitmask m; - if( pProbe->isCovering ){ -- pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; - m = 0; -+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; - }else{ - m = pSrc->colUsed & pProbe->colNotIdxed; -- pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; -+ if( pProbe->pPartIdxWhere ){ -+ wherePartIdxExpr( -+ pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0 -+ ); -+ } -+ pNew->wsFlags = WHERE_INDEXED; -+ if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){ -+ u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor); -+ if( isCov==0 ){ -+ WHERETRACE(0x200, -+ ("-> %s is not a covering index" -+ " according to whereIsCoveringIndex()\n", pProbe->zName)); -+ assert( m!=0 ); -+ }else{ -+ m = 0; -+ pNew->wsFlags |= isCov; -+ if( isCov & WHERE_IDX_ONLY ){ -+ WHERETRACE(0x200, -+ ("-> %s is a covering expression index" -+ " according to whereIsCoveringIndex()\n", pProbe->zName)); -+ }else{ -+ assert( isCov==WHERE_EXPRIDX ); -+ WHERETRACE(0x200, -+ ("-> %s might be a covering expression index" -+ " according to whereIsCoveringIndex()\n", pProbe->zName)); -+ } -+ } -+ }else if( m==0 -+ && (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700)) -+ ){ -+ WHERETRACE(0x200, -+ ("-> %s is a covering index according to bitmasks\n", -+ pProbe->zName, m==0 ? "is" : "is not")); -+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; -+ } - } - - /* Full scan via index */ -@@ -148717,7 +168635,14 @@ static int whereLoopAddBtree( - } - ApplyCostMultiplier(pNew->rRun, pTab->costMult); - whereLoopOutputAdjust(pWC, pNew, rSize); -- rc = whereLoopInsert(pBuilder, pNew); -+ if( (pSrc->fg.jointype & JT_RIGHT)!=0 && pProbe->aColExpr ){ -+ /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN -+ ** because the cursor used to access the index might not be -+ ** positioned to the correct row during the right-join no-match -+ ** loop. */ -+ }else{ -+ rc = whereLoopInsert(pBuilder, pNew); -+ } - pNew->nOut = rSize; - if( rc ) break; - } -@@ -148730,7 +168655,7 @@ static int whereLoopAddBtree( - ** unique index is used (making the index functionally non-unique) - ** then the sqlite_stat1 data becomes important for scoring the - ** plan */ -- pTab->tabFlags |= TF_StatsUsed; -+ pTab->tabFlags |= TF_MaybeReanalyze; - } - #ifdef SQLITE_ENABLE_STAT4 - sqlite3Stat4ProbeFree(pBuilder->pRec); -@@ -148743,6 +168668,30 @@ static int whereLoopAddBtree( - - #ifndef SQLITE_OMIT_VIRTUALTABLE - -+/* -+** Return true if pTerm is a virtual table LIMIT or OFFSET term. -+*/ -+static int isLimitTerm(WhereTerm *pTerm){ -+ assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); -+ return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT -+ && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; -+} -+ -+/* -+** Return true if the first nCons constraints in the pUsage array are -+** marked as in-use (have argvIndex>0). False otherwise. -+*/ -+static int allConstraintsUsed( -+ struct sqlite3_index_constraint_usage *aUsage, -+ int nCons -+){ -+ int ii; -+ for(ii=0; iipNew->iTab. This -@@ -148770,9 +168719,11 @@ static int whereLoopAddVirtualOne( - u16 mExclude, /* Exclude terms using these operators */ - sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ - u16 mNoOmit, /* Do not omit these constraints */ -- int *pbIn /* OUT: True if plan uses an IN(...) op */ -+ int *pbIn, /* OUT: True if plan uses an IN(...) op */ -+ int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */ - ){ - WhereClause *pWC = pBuilder->pWC; -+ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; - int i; -@@ -148780,7 +168731,7 @@ static int whereLoopAddVirtualOne( - int rc = SQLITE_OK; - WhereLoop *pNew = pBuilder->pNew; - Parse *pParse = pBuilder->pWInfo->pParse; -- struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; -+ SrcItem *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; - int nConstraint = pIdxInfo->nConstraint; - - assert( (mUsable & mPrereq)==mPrereq ); -@@ -148791,10 +168742,11 @@ static int whereLoopAddVirtualOne( - ** arguments mUsable and mExclude. */ - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; ia[pIdxCons->iTermOffset]; -+ WhereTerm *pTerm = termFromWhereClause(pWC, pIdxCons->iTermOffset); - pIdxCons->usable = 0; - if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight - && (pTerm->eOperator & mExclude)==0 -+ && (pbRetryLimit || !isLimitTerm(pTerm)) - ){ - pIdxCons->usable = 1; - } -@@ -148809,17 +168761,18 @@ static int whereLoopAddVirtualOne( - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; - pIdxInfo->estimatedRows = 25; - pIdxInfo->idxFlags = 0; -- pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; -+ pHidden->mHandleIn = 0; - - /* Invoke the virtual table xBestIndex() method */ -- rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); -+ rc = vtabBestIndex(pParse, pSrc->pSTab, pIdxInfo); - if( rc ){ - if( rc==SQLITE_CONSTRAINT ){ - /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means - ** that the particular combination of parameters provided is unusable. - ** Make no entries in the loop table. - */ -- WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n")); -+ WHERETRACE(0xffffffff, (" ^^^^--- non-viable plan rejected!\n")); -+ freeIdxStr(pIdxInfo); - return SQLITE_OK; - } - return rc; -@@ -148827,8 +168780,8 @@ static int whereLoopAddVirtualOne( - - mxTerm = -1; - assert( pNew->nLSlot>=nConstraint ); -- for(i=0; iaLTerm[i] = 0; -- pNew->u.vtab.omitMask = 0; -+ memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint ); -+ memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab)); - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; iiTermOffset; - if( iTerm>=nConstraint - || j<0 -- || j>=pWC->nTerm -+ || (pTerm = termFromWhereClause(pWC, j))==0 - || pNew->aLTerm[iTerm]!=0 - || pIdxCons->usable==0 - ){ -- sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); -- testcase( pIdxInfo->needToFreeIdxStr ); -+ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); -+ freeIdxStr(pIdxInfo); - return SQLITE_ERROR; - } - testcase( iTerm==nConstraint-1 ); - testcase( j==0 ); - testcase( j==pWC->nTerm-1 ); -- pTerm = &pWC->a[j]; - pNew->prereq |= pTerm->prereqRight; - assert( iTermnLSlot ); - pNew->aLTerm[iTerm] = pTerm; -@@ -148862,8 +168814,13 @@ static int whereLoopAddVirtualOne( - }else{ - testcase( i!=iTerm ); - } -+ if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){ -+ pNew->u.vtab.bOmitOffset = 1; -+ } - } -- if( (pTerm->eOperator & WO_IN)!=0 ){ -+ if( SMASKBIT32(i) & pHidden->mHandleIn ){ -+ pNew->u.vtab.mHandleIn |= MASKBIT32(iTerm); -+ }else if( (pTerm->eOperator & WO_IN)!=0 ){ - /* A virtual table that is constrained by an IN clause may not - ** consume the ORDER BY clause because (1) the order of IN terms - ** is not necessarily related to the order of output terms and -@@ -148873,6 +168830,25 @@ static int whereLoopAddVirtualOne( - pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; - *pbIn = 1; assert( (mExclude & WO_IN)==0 ); - } -+ -+ /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET -+ ** terms. And if there are any, they should follow all other terms. */ -+ assert( pbRetryLimit || !isLimitTerm(pTerm) ); -+ assert( !isLimitTerm(pTerm) || i>=nConstraint-2 ); -+ assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) ); -+ -+ if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){ -+ /* If there is an IN(...) term handled as an == (separate call to -+ ** xFilter for each value on the RHS of the IN) and a LIMIT or -+ ** OFFSET term handled as well, the plan is unusable. Similarly, -+ ** if there is a LIMIT/OFFSET and there are other unused terms, -+ ** the plan cannot be used. In these cases set variable *pbRetryLimit -+ ** to true to tell the caller to retry with LIMIT and OFFSET -+ ** disabled. */ -+ freeIdxStr(pIdxInfo); -+ *pbRetryLimit = 1; -+ return SQLITE_OK; -+ } - } - } - -@@ -148881,8 +168857,8 @@ static int whereLoopAddVirtualOne( - if( pNew->aLTerm[i]==0 ){ - /* The non-zero argvIdx values must be contiguous. Raise an - ** error if they are not */ -- sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); -- testcase( pIdxInfo->needToFreeIdxStr ); -+ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); -+ freeIdxStr(pIdxInfo); - return SQLITE_ERROR; - } - } -@@ -148893,6 +168869,7 @@ static int whereLoopAddVirtualOne( - pNew->u.vtab.idxStr = pIdxInfo->idxStr; - pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? - pIdxInfo->nOrderBy : 0); -+ pNew->u.vtab.bIdxNumHex = (pIdxInfo->idxFlags&SQLITE_INDEX_SCAN_HEX)!=0; - pNew->rSetup = 0; - pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); - pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); -@@ -148909,7 +168886,7 @@ static int whereLoopAddVirtualOne( - sqlite3_free(pNew->u.vtab.idxStr); - pNew->u.vtab.needFree = 0; - } -- WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", -+ WHERETRACE(0xffffffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", - *pbIn, (sqlite3_uint64)mPrereq, - (sqlite3_uint64)(pNew->prereq & ~mPrereq))); - -@@ -148917,11 +168894,19 @@ static int whereLoopAddVirtualOne( - } - - /* --** If this function is invoked from within an xBestIndex() callback, it --** returns a pointer to a buffer containing the name of the collation --** sequence associated with element iCons of the sqlite3_index_info.aConstraint --** array. Or, if iCons is out of range or there is no active xBestIndex --** call, return NULL. -+** Return the collating sequence for a constraint passed into xBestIndex. -+** -+** pIdxInfo must be an sqlite3_index_info structure passed into xBestIndex. -+** This routine depends on there being a HiddenIndexInfo structure immediately -+** following the sqlite3_index_info structure. -+** -+** Return a pointer to the collation name: -+** -+** 1. If there is an explicit COLLATE operator on the constraint, return it. -+** -+** 2. Else, if the column has an alternative collation, return that. -+** -+** 3. Otherwise, return "BINARY". - */ - SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){ - HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; -@@ -148929,7 +168914,7 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int - if( iCons>=0 && iConsnConstraint ){ - CollSeq *pC = 0; - int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset; -- Expr *pX = pHidden->pWC->a[iTerm].pExpr; -+ Expr *pX = termFromWhereClause(pHidden->pWC, iTerm)->pExpr; - if( pX->pLeft ){ - pC = sqlite3ExprCompareCollSeq(pHidden->pParse, pX); - } -@@ -148938,6 +168923,94 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int - return zRet; - } - -+/* -+** Return true if constraint iCons is really an IN(...) constraint, or -+** false otherwise. If iCons is an IN(...) constraint, set (if bHandle!=0) -+** or clear (if bHandle==0) the flag to handle it using an iterator. -+*/ -+SQLITE_API int sqlite3_vtab_in(sqlite3_index_info *pIdxInfo, int iCons, int bHandle){ -+ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; -+ u32 m = SMASKBIT32(iCons); -+ if( m & pHidden->mIn ){ -+ if( bHandle==0 ){ -+ pHidden->mHandleIn &= ~m; -+ }else if( bHandle>0 ){ -+ pHidden->mHandleIn |= m; -+ } -+ return 1; -+ } -+ return 0; -+} -+ -+/* -+** This interface is callable from within the xBestIndex callback only. -+** -+** If possible, set (*ppVal) to point to an object containing the value -+** on the right-hand-side of constraint iCons. -+*/ -+SQLITE_API int sqlite3_vtab_rhs_value( -+ sqlite3_index_info *pIdxInfo, /* Copy of first argument to xBestIndex */ -+ int iCons, /* Constraint for which RHS is wanted */ -+ sqlite3_value **ppVal /* Write value extracted here */ -+){ -+ HiddenIndexInfo *pH = (HiddenIndexInfo*)&pIdxInfo[1]; -+ sqlite3_value *pVal = 0; -+ int rc = SQLITE_OK; -+ if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ -+ rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */ -+ }else{ -+ if( pH->aRhs[iCons]==0 ){ -+ WhereTerm *pTerm = termFromWhereClause( -+ pH->pWC, pIdxInfo->aConstraint[iCons].iTermOffset -+ ); -+ rc = sqlite3ValueFromExpr( -+ pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db), -+ SQLITE_AFF_BLOB, &pH->aRhs[iCons] -+ ); -+ testcase( rc!=SQLITE_OK ); -+ } -+ pVal = pH->aRhs[iCons]; -+ } -+ *ppVal = pVal; -+ -+ if( rc==SQLITE_OK && pVal==0 ){ /* IMP: R-19933-32160 */ -+ rc = SQLITE_NOTFOUND; /* IMP: R-36424-56542 */ -+ } -+ -+ return rc; -+} -+ -+/* -+** Return true if ORDER BY clause may be handled as DISTINCT. -+*/ -+SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){ -+ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; -+ assert( pHidden->eDistinct>=0 && pHidden->eDistinct<=3 ); -+ return pHidden->eDistinct; -+} -+ -+/* -+** Cause the prepared statement that is associated with a call to -+** xBestIndex to potentially use all schemas. If the statement being -+** prepared is read-only, then just start read transactions on all -+** schemas. But if this is a write operation, start writes on all -+** schemas. -+** -+** This is used by the (built-in) sqlite_dbpage virtual table. -+*/ -+SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse *pParse){ -+ int nDb = pParse->db->nDb; -+ int i; -+ for(i=0; iwriteMask) ){ -+ for(i=0; ipNew->iTab. That table is guaranteed to be a virtual table. -@@ -148972,13 +169045,14 @@ static int whereLoopAddVirtual( - WhereInfo *pWInfo; /* WHERE analysis context */ - Parse *pParse; /* The parsing context */ - WhereClause *pWC; /* The WHERE clause */ -- struct SrcList_item *pSrc; /* The FROM clause term to search */ -+ SrcItem *pSrc; /* The FROM clause term to search */ - sqlite3_index_info *p; /* Object to pass to xBestIndex() */ - int nConstraint; /* Number of constraints in p */ - int bIn; /* True if plan uses IN(...) operator */ - WhereLoop *pNew; - Bitmask mBest; /* Tables used by best possible plan */ - u16 mNoOmit; -+ int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */ - - assert( (mPrereq & mUnusable)==0 ); - pWInfo = pBuilder->pWInfo; -@@ -148986,9 +169060,8 @@ static int whereLoopAddVirtual( - pWC = pBuilder->pWC; - pNew = pBuilder->pNew; - pSrc = &pWInfo->pTabList->a[pNew->iTab]; -- assert( IsVirtual(pSrc->pTab) ); -- p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, -- &mNoOmit); -+ assert( IsVirtual(pSrc->pSTab) ); -+ p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); - if( p==0 ) return SQLITE_NOMEM_BKPT; - pNew->rSetup = 0; - pNew->wsFlags = WHERE_VIRTUALTABLE; -@@ -148996,14 +169069,22 @@ static int whereLoopAddVirtual( - pNew->u.vtab.needFree = 0; - nConstraint = p->nConstraint; - if( whereLoopResize(pParse->db, pNew, nConstraint) ){ -- sqlite3DbFree(pParse->db, p); -+ freeIndexInfo(pParse->db, p); - return SQLITE_NOMEM_BKPT; - } - - /* First call xBestIndex() with all constraints usable. */ -- WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); -- WHERETRACE(0x40, (" VirtualOne: all usable\n")); -- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); -+ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pSTab->zName)); -+ WHERETRACE(0x800, (" VirtualOne: all usable\n")); -+ rc = whereLoopAddVirtualOne( -+ pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry -+ ); -+ if( bRetry ){ -+ assert( rc==SQLITE_OK ); -+ rc = whereLoopAddVirtualOne( -+ pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0 -+ ); -+ } - - /* If the call to xBestIndex() with all terms enabled produced a plan - ** that does not require any source tables (IOW: a plan with mBest==0) -@@ -149019,9 +169100,9 @@ static int whereLoopAddVirtual( - /* If the plan produced by the earlier call uses an IN(...) term, call - ** xBestIndex again, this time with IN(...) terms disabled. */ - if( bIn ){ -- WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); -+ WHERETRACE(0x800, (" VirtualOne: all usable w/o IN\n")); - rc = whereLoopAddVirtualOne( -- pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); -+ pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0); - assert( bIn==0 ); - mBestNoIn = pNew->prereq & ~mPrereq; - if( mBestNoIn==0 ){ -@@ -149037,18 +169118,17 @@ static int whereLoopAddVirtual( - Bitmask mNext = ALLBITS; - assert( mNext>0 ); - for(i=0; ia[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq -- ); -+ int iTerm = p->aConstraint[i].iTermOffset; -+ Bitmask mThis = termFromWhereClause(pWC, iTerm)->prereqRight & ~mPrereq; - if( mThis>mPrev && mThisprereq==mPrereq ){ - seenZero = 1; - if( bIn==0 ) seenZeroNoIN = 1; -@@ -149059,9 +169139,9 @@ static int whereLoopAddVirtual( - ** that requires no source tables at all (i.e. one guaranteed to be - ** usable), make a call here with all source tables disabled */ - if( rc==SQLITE_OK && seenZero==0 ){ -- WHERETRACE(0x40, (" VirtualOne: all disabled\n")); -+ WHERETRACE(0x800, (" VirtualOne: all disabled\n")); - rc = whereLoopAddVirtualOne( -- pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); -+ pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0); - if( bIn==0 ) seenZeroNoIN = 1; - } - -@@ -149069,15 +169149,14 @@ static int whereLoopAddVirtual( - ** that requires no source tables at all and does not use an IN(...) - ** operator, make a final call to obtain one here. */ - if( rc==SQLITE_OK && seenZeroNoIN==0 ){ -- WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); -+ WHERETRACE(0x800, (" VirtualOne: all disabled and w/o IN\n")); - rc = whereLoopAddVirtualOne( -- pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); -+ pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); - } - } - -- if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); -- sqlite3DbFreeNN(pParse->db, p); -- WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); -+ freeIndexInfo(pParse->db, p); -+ WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pSTab->zName, rc)); - return rc; - } - #endif /* SQLITE_OMIT_VIRTUALTABLE */ -@@ -149100,7 +169179,7 @@ static int whereLoopAddOr( - WhereClause tempWC; - WhereLoopBuilder sSubBuild; - WhereOrSet sSum, sCur; -- struct SrcList_item *pItem; -+ SrcItem *pItem; - - pWC = pBuilder->pWC; - pWCEnd = pWC->a + pWC->nTerm; -@@ -149109,6 +169188,9 @@ static int whereLoopAddOr( - pItem = pWInfo->pTabList->a + pNew->iTab; - iCur = pItem->iCursor; - -+ /* The multi-index OR optimization does not work for RIGHT and FULL JOIN */ -+ if( pItem->fg.jointype & JT_RIGHT ) return SQLITE_OK; -+ - for(pTerm=pWC->a; pTermeOperator & WO_OR)!=0 - && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0 -@@ -149120,10 +169202,9 @@ static int whereLoopAddOr( - int i, j; - - sSubBuild = *pBuilder; -- sSubBuild.pOrderBy = 0; - sSubBuild.pOrSet = &sCur; - -- WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); -+ WHERETRACE(0x400, ("Begin processing OR-clause %p\n", pTerm)); - for(pOrTerm=pOrWC->a; pOrTermeOperator & WO_AND)!=0 ){ - sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc; -@@ -149132,6 +169213,7 @@ static int whereLoopAddOr( - tempWC.pOuter = pWC; - tempWC.op = TK_AND; - tempWC.nTerm = 1; -+ tempWC.nBase = 1; - tempWC.a = pOrTerm; - sSubBuild.pWC = &tempWC; - }else{ -@@ -149139,14 +169221,14 @@ static int whereLoopAddOr( - } - sCur.n = 0; - #ifdef WHERETRACE_ENABLED -- WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n", -+ WHERETRACE(0x400, ("OR-term %d of %p has %d subterms:\n", - (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); -- if( sqlite3WhereTrace & 0x400 ){ -+ if( sqlite3WhereTrace & 0x20000 ){ - sqlite3WhereClausePrint(sSubBuild.pWC); - } - #endif - #ifndef SQLITE_OMIT_VIRTUALTABLE -- if( IsVirtual(pItem->pTab) ){ -+ if( IsVirtual(pItem->pSTab) ){ - rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable); - }else - #endif -@@ -149156,7 +169238,7 @@ static int whereLoopAddOr( - if( rc==SQLITE_OK ){ - rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); - } -- assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0 ); -+ testcase( rc==SQLITE_NOMEM && sCur.n>0 ); - testcase( rc==SQLITE_DONE ); - if( sCur.n==0 ){ - sSum.n = 0; -@@ -149201,7 +169283,7 @@ static int whereLoopAddOr( - pNew->prereq = sSum.a[i].prereq; - rc = whereLoopInsert(pBuilder, pNew); - } -- WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm)); -+ WHERETRACE(0x400, ("End processing OR-clause %p\n", pTerm)); - } - } - return rc; -@@ -149216,33 +169298,54 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ - Bitmask mPrior = 0; - int iTab; - SrcList *pTabList = pWInfo->pTabList; -- struct SrcList_item *pItem; -- struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel]; -+ SrcItem *pItem; -+ SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; - sqlite3 *db = pWInfo->pParse->db; - int rc = SQLITE_OK; -+ int bFirstPastRJ = 0; -+ int hasRightJoin = 0; - WhereLoop *pNew; - -+ - /* Loop over the tables in the join, from left to right */ - pNew = pBuilder->pNew; -- whereLoopInit(pNew); -+ -+ /* Verify that pNew has already been initialized */ -+ assert( pNew->nLTerm==0 ); -+ assert( pNew->wsFlags==0 ); -+ assert( pNew->nLSlot>=ArraySize(pNew->aLTermSpace) ); -+ assert( pNew->aLTerm!=0 ); -+ - pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; - for(iTab=0, pItem=pTabList->a; pItemiTab = iTab; - pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; - pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); -- if( (pItem->fg.jointype & (JT_LEFT|JT_CROSS))!=0 ){ -- /* This condition is true when pItem is the FROM clause term on the -- ** right-hand-side of a LEFT or CROSS JOIN. */ -- mPrereq = mPrior; -- }else{ -+ if( bFirstPastRJ -+ || (pItem->fg.jointype & (JT_OUTER|JT_CROSS|JT_LTORJ))!=0 -+ ){ -+ /* Add prerequisites to prevent reordering of FROM clause terms -+ ** across CROSS joins and outer joins. The bFirstPastRJ boolean -+ ** prevents the right operand of a RIGHT JOIN from being swapped with -+ ** other elements even further to the right. -+ ** -+ ** The JT_LTORJ case and the hasRightJoin flag work together to -+ ** prevent FROM-clause terms from moving from the right side of -+ ** a LEFT JOIN over to the left side of that join if the LEFT JOIN -+ ** is itself on the left side of a RIGHT JOIN. -+ */ -+ if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; -+ mPrereq |= mPrior; -+ bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; -+ }else if( !hasRightJoin ){ - mPrereq = 0; - } - #ifndef SQLITE_OMIT_VIRTUALTABLE -- if( IsVirtual(pItem->pTab) ){ -- struct SrcList_item *p; -+ if( IsVirtual(pItem->pSTab) ){ -+ SrcItem *p; - for(p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS)) ){ -+ if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){ - mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); - } - } -@@ -149271,6 +169374,97 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ - return rc; - } - -+/* Implementation of the order-by-subquery optimization: -+** -+** WhereLoop pLoop, which the iLoop-th term of the nested loop, is really -+** a subquery or CTE that has an ORDER BY clause. See if any of the terms -+** in the subquery ORDER BY clause will satisfy pOrderBy from the outer -+** query. Mark off all satisfied terms (by setting bits in *pOBSat) and -+** return TRUE if they do. If not, return false. -+** -+** Example: -+** -+** CREATE TABLE t1(a,b,c, PRIMARY KEY(a,b)); -+** CREATE TABLE t2(x,y); -+** WITH t3(p,q) AS MATERIALIZED (SELECT x+y, x-y FROM t2 ORDER BY x+y) -+** SELECT * FROM t3 JOIN t1 ON a=q ORDER BY p, b; -+** -+** The CTE named "t3" comes out in the natural order of "p", so the first -+** first them of "ORDER BY p,b" is satisfied by a sequential scan of "t3" -+** and sorting only needs to occur on the second term "b". -+** -+** Limitations: -+** -+** (1) The optimization is not applied if the outer ORDER BY contains -+** a COLLATE clause. The optimization might be applied if the -+** outer ORDER BY uses NULLS FIRST, NULLS LAST, ASC, and/or DESC as -+** long as the subquery ORDER BY does the same. But if the -+** outer ORDER BY uses COLLATE, even a redundant COLLATE, the -+** optimization is bypassed. -+** -+** (2) The subquery ORDER BY terms must exactly match subquery result -+** columns, including any COLLATE annotations. This routine relies -+** on iOrderByCol to do matching between order by terms and result -+** columns, and iOrderByCol will not be set if the result column -+** and ORDER BY collations differ. -+** -+** (3) The subquery and outer ORDER BY can be in opposite directions as -+** long as the subquery is materialized. If the subquery is -+** implemented as a co-routine, the sort orders must be in the same -+** direction because there is no way to run a co-routine backwards. -+*/ -+static SQLITE_NOINLINE int wherePathMatchSubqueryOB( -+ WhereInfo *pWInfo, /* The WHERE clause */ -+ WhereLoop *pLoop, /* The nested loop term that is a subquery */ -+ int iLoop, /* Which level of the nested loop. 0==outermost */ -+ int iCur, /* Cursor used by the this loop */ -+ ExprList *pOrderBy, /* The ORDER BY clause on the whole query */ -+ Bitmask *pRevMask, /* When loops need to go in reverse order */ -+ Bitmask *pOBSat /* Which terms of pOrderBy are satisfied so far */ -+){ -+ int iOB; /* Index into pOrderBy->a[] */ -+ int jSub; /* Index into pSubOB->a[] */ -+ u8 rev = 0; /* True if iOB and jSub sort in opposite directions */ -+ u8 revIdx = 0; /* Sort direction for jSub */ -+ Expr *pOBExpr; /* Current term of outer ORDER BY */ -+ ExprList *pSubOB; /* Complete ORDER BY on the subquery */ -+ -+ pSubOB = pLoop->u.btree.pOrderBy; -+ assert( pSubOB!=0 ); -+ for(iOB=0; (MASKBIT(iOB) & *pOBSat)!=0; iOB++){} -+ for(jSub=0; jSubnExpr && iOBnExpr; jSub++, iOB++){ -+ if( pSubOB->a[jSub].u.x.iOrderByCol==0 ) break; -+ pOBExpr = pOrderBy->a[iOB].pExpr; -+ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) break; -+ if( pOBExpr->iTable!=iCur ) break; -+ if( pOBExpr->iColumn!=pSubOB->a[jSub].u.x.iOrderByCol-1 ) break; -+ if( (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){ -+ u8 sfOB = pOrderBy->a[iOB].fg.sortFlags; /* sortFlags for iOB */ -+ u8 sfSub = pSubOB->a[jSub].fg.sortFlags; /* sortFlags for jSub */ -+ if( (sfSub & KEYINFO_ORDER_BIGNULL) != (sfOB & KEYINFO_ORDER_BIGNULL) ){ -+ break; -+ } -+ revIdx = sfSub & KEYINFO_ORDER_DESC; -+ if( jSub>0 ){ -+ if( (rev^revIdx)!=(sfOB & KEYINFO_ORDER_DESC) ){ -+ break; -+ } -+ }else{ -+ rev = revIdx ^ (sfOB & KEYINFO_ORDER_DESC); -+ if( rev ){ -+ if( (pLoop->wsFlags & WHERE_COROUTINE)!=0 ){ -+ /* Cannot run a co-routine in reverse order */ -+ break; -+ } -+ *pRevMask |= MASKBIT(iLoop); -+ } -+ } -+ } -+ *pOBSat |= MASKBIT(iOB); -+ } -+ return jSub>0; -+} -+ - /* - ** Examine a WherePath (with the addition of the extra WhereLoop of the 6th - ** parameters) to see if it outputs rows in the requested ORDER BY -@@ -149367,12 +169561,12 @@ static i8 wherePathSatisfiesOrderBy( - pLoop = pLast; - } - if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ -- if( pLoop->u.vtab.isOrdered && (wctrlFlags & WHERE_DISTINCTBY)==0 ){ -+ if( pLoop->u.vtab.isOrdered -+ && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) -+ ){ - obSat = obDone; - } - break; -- }else if( wctrlFlags & WHERE_DISTINCTBY ){ -- pLoop->u.btree.nDistinctCol = 0; - } - iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; - -@@ -149384,7 +169578,8 @@ static i8 wherePathSatisfiesOrderBy( - for(i=0; ia[i].pExpr); -- if( pOBExpr->op!=TK_COLUMN ) continue; -+ if( NEVER(pOBExpr==0) ) continue; -+ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; - if( pOBExpr->iTable!=iCur ) continue; - pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, - ~ready, eqOpMask, 0); -@@ -149413,9 +169608,18 @@ static i8 wherePathSatisfiesOrderBy( - - if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){ - if( pLoop->wsFlags & WHERE_IPK ){ -+ if( pLoop->u.btree.pOrderBy -+ && OptimizationEnabled(db, SQLITE_OrderBySubq) -+ && wherePathMatchSubqueryOB(pWInfo,pLoop,iLoop,iCur, -+ pOrderBy,pRevMask, &obSat) -+ ){ -+ nColumn = 0; -+ isOrderDistinct = 0; -+ }else{ -+ nColumn = 1; -+ } - pIndex = 0; - nKeyCol = 0; -- nColumn = 1; - }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){ - return 0; - }else{ -@@ -149424,6 +169628,10 @@ static i8 wherePathSatisfiesOrderBy( - assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) ); - assert( pIndex->aiColumn[nColumn-1]==XN_ROWID - || !HasRowid(pIndex->pTable)); -+ /* All relevant terms of the index must also be non-NULL in order -+ ** for isOrderDistinct to be true. So the isOrderDistinct value -+ ** computed here might be a false positive. Corrections will be -+ ** made at tag-20210426-1 below */ - isOrderDistinct = IsUniqueIndex(pIndex) - && (pLoop->wsFlags & WHERE_SKIPSCAN)==0; - } -@@ -149491,18 +169699,22 @@ static i8 wherePathSatisfiesOrderBy( - } - - /* An unconstrained column that might be NULL means that this -- ** WhereLoop is not well-ordered -+ ** WhereLoop is not well-ordered. tag-20210426-1 - */ -- if( isOrderDistinct -- && iColumn>=0 -- && j>=pLoop->u.btree.nEq -- && pIndex->pTable->aCol[iColumn].notNull==0 -- ){ -- isOrderDistinct = 0; -+ if( isOrderDistinct ){ -+ if( iColumn>=0 -+ && j>=pLoop->u.btree.nEq -+ && pIndex->pTable->aCol[iColumn].notNull==0 -+ ){ -+ isOrderDistinct = 0; -+ } -+ if( iColumn==XN_EXPR ){ -+ isOrderDistinct = 0; -+ } - } - - /* Find the ORDER BY term that corresponds to the j-th column -- ** of the index and mark that ORDER BY term off -+ ** of the index and mark that ORDER BY term having been satisfied. - */ - isMatch = 0; - for(i=0; bOnce && ia[i].pExpr); - testcase( wctrlFlags & WHERE_GROUPBY ); - testcase( wctrlFlags & WHERE_DISTINCTBY ); -+ if( NEVER(pOBExpr==0) ) continue; - if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; - if( iColumn>=XN_ROWID ){ -- if( pOBExpr->op!=TK_COLUMN ) continue; -+ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue; - if( pOBExpr->iTable!=iCur ) continue; - if( pOBExpr->iColumn!=iColumn ) continue; - }else{ -- Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr; -- if( sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){ -+ Expr *pIxExpr = pIndex->aColExpr->a[j].pExpr; -+ if( sqlite3ExprCompareSkip(pOBExpr, pIxExpr, iCur) ){ - continue; - } - } -@@ -149535,16 +169748,18 @@ static i8 wherePathSatisfiesOrderBy( - /* Make sure the sort order is compatible in an ORDER BY clause. - ** Sort order is irrelevant for a GROUP BY clause. */ - if( revSet ){ -- if( (rev ^ revIdx)!=(pOrderBy->a[i].sortFlags&KEYINFO_ORDER_DESC) ){ -+ if( (rev ^ revIdx) -+ != (pOrderBy->a[i].fg.sortFlags&KEYINFO_ORDER_DESC) -+ ){ - isMatch = 0; - } - }else{ -- rev = revIdx ^ (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC); -+ rev = revIdx ^ (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC); - if( rev ) *pRevMask |= MASKBIT(iLoop); - revSet = 1; - } - } -- if( isMatch && (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL) ){ -+ if( isMatch && (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL) ){ - if( j==pLoop->u.btree.nEq ){ - pLoop->wsFlags |= WHERE_BIGNULL_SORT; - }else{ -@@ -149581,7 +169796,7 @@ static i8 wherePathSatisfiesOrderBy( - if( MASKBIT(i) & obSat ) continue; - p = pOrderBy->a[i].pExpr; - mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); -- if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue; -+ if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue; - if( (mTerm&~orderDistinctMask)==0 ){ - obSat |= MASKBIT(i); - } -@@ -149591,7 +169806,7 @@ static i8 wherePathSatisfiesOrderBy( - if( obSat==obDone ) return (i8)nOrderBy; - if( !isOrderDistinct ){ - for(i=nOrderBy-1; i>0; i--){ -- Bitmask m = MASKBIT(i) - 1; -+ Bitmask m = ALWAYS(iwctrlFlags & WHERE_GROUPBY ); -+ assert( pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY) ); - assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); - return pWInfo->sorted; - } -@@ -149647,38 +169862,275 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ - ** order. - */ - static LogEst whereSortingCost( -- WhereInfo *pWInfo, -- LogEst nRow, -- int nOrderBy, -- int nSorted -+ WhereInfo *pWInfo, /* Query planning context */ -+ LogEst nRow, /* Estimated number of rows to sort */ -+ int nOrderBy, /* Number of ORDER BY clause terms */ -+ int nSorted /* Number of initial ORDER BY terms naturally in order */ - ){ -- /* TUNING: Estimated cost of a full external sort, where N is -+ /* Estimated cost of a full external sort, where N is - ** the number of rows to sort is: - ** -- ** cost = (3.0 * N * log(N)). -+ ** cost = (K * N * log(N)). - ** - ** Or, if the order-by clause has X terms but only the last Y - ** terms are out of order, then block-sorting will reduce the - ** sorting cost to: - ** -- ** cost = (3.0 * N * log(N)) * (Y/X) -+ ** cost = (K * N * log(N)) * (Y/X) -+ ** -+ ** The constant K is at least 2.0 but will be larger if there are a -+ ** large number of columns to be sorted, as the sorting time is -+ ** proportional to the amount of content to be sorted. The algorithm -+ ** does not currently distinguish between fat columns (BLOBs and TEXTs) -+ ** and skinny columns (INTs). It just uses the number of columns as -+ ** an approximation for the row width. - ** -- ** The (Y/X) term is implemented using stack variable rScale -- ** below. */ -- LogEst rScale, rSortCost; -- assert( nOrderBy>0 && 66==sqlite3LogEst(100) ); -- rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; -- rSortCost = nRow + rScale + 16; -+ ** And extra factor of 2.0 or 3.0 is added to the sorting cost if the sort -+ ** is built using OP_IdxInsert and OP_Sort rather than with OP_SorterInsert. -+ */ -+ LogEst rSortCost, nCol; -+ assert( pWInfo->pSelect!=0 ); -+ assert( pWInfo->pSelect->pEList!=0 ); -+ /* TUNING: sorting cost proportional to the number of output columns: */ -+ nCol = sqlite3LogEst((pWInfo->pSelect->pEList->nExpr+59)/30); -+ rSortCost = nRow + nCol; -+ if( nSorted>0 ){ -+ /* Scale the result by (Y/X) */ -+ rSortCost += sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; -+ } - - /* Multiple by log(M) where M is the number of output rows. -- ** Use the LIMIT for M if it is smaller */ -- if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimitiLimit; -+ ** Use the LIMIT for M if it is smaller. Or if this sort is for -+ ** a DISTINCT operator, M will be the number of distinct output -+ ** rows, so fudge it downwards a bit. -+ */ -+ if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 ){ -+ rSortCost += 10; /* TUNING: Extra 2.0x if using LIMIT */ -+ if( nSorted!=0 ){ -+ rSortCost += 6; /* TUNING: Extra 1.5x if also using partial sort */ -+ } -+ if( pWInfo->iLimitiLimit; -+ } -+ }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ -+ /* TUNING: In the sort for a DISTINCT operator, assume that the DISTINCT -+ ** reduces the number of output rows by a factor of 2 */ -+ if( nRow>10 ){ nRow -= 10; assert( 10==sqlite3LogEst(2) ); } - } - rSortCost += estLog(nRow); - return rSortCost; - } - -+/* -+** Compute the maximum number of paths in the solver algorithm, for -+** queries that have three or more terms in the FROM clause. Queries with -+** two or fewer FROM clause terms are handled by the caller. -+** -+** Query planning is NP-hard. We must limit the number of paths at -+** each step of the solver search algorithm to avoid exponential behavior. -+** -+** The value returned is a tuning parameter. Currently the value is: -+** -+** 18 for star queries -+** 12 otherwise -+** -+** For the purposes of this heuristic, a star-query is defined as a query -+** with a large central table that is joined using an INNER JOIN, -+** not CROSS or OUTER JOINs, against four or more smaller tables. -+** The central table is called the "fact" table. The smaller tables -+** that get joined are "dimension tables". Also, any table that is -+** self-joined cannot be a dimension table; we assume that dimension -+** tables may only be joined against fact tables. -+** -+** SIDE EFFECT: (and really the whole point of this subroutine) -+** -+** If pWInfo describes a star-query, then the cost for SCANs of dimension -+** WhereLoops is increased to be slightly larger than the cost of a SCAN -+** in the fact table. Only SCAN costs are increased. SEARCH costs are -+** unchanged. This heuristic helps keep fact tables in outer loops. Without -+** this heuristic, paths with fact tables in outer loops tend to get pruned -+** by the mxChoice limit on the number of paths, resulting in poor query -+** plans. See the starschema1.test test module for examples of queries -+** that need this heuristic to find good query plans. -+** -+** This heuristic can be completely disabled, so that no query is -+** considered a star-query, using SQLITE_TESTCTRL_OPTIMIZATION to -+** disable the SQLITE_StarQuery optimization. In the CLI, the command -+** to do that is: ".testctrl opt -starquery". -+** -+** HISTORICAL NOTES: -+** -+** This optimization was first added on 2024-05-09 by check-in 38db9b5c83d. -+** The original optimization reduced the cost and output size estimate for -+** fact tables to help them move to outer loops. But months later (as people -+** started upgrading) performance regression reports started caming in, -+** including: -+** -+** forum post b18ef983e68d06d1 (2024-12-21) -+** forum post 0025389d0860af82 (2025-01-14) -+** forum post d87570a145599033 (2025-01-17) -+** -+** To address these, the criteria for a star-query was tightened to exclude -+** cases where the fact and dimensions are separated by an outer join, and -+** the affect of star-schema detection was changed to increase the rRun cost -+** on just full table scans of dimension tables, rather than reducing costs -+** in the all access methods of the fact table. -+*/ -+static int computeMxChoice(WhereInfo *pWInfo){ -+ int nLoop = pWInfo->nLevel; /* Number of terms in the join */ -+ WhereLoop *pWLoop; /* For looping over WhereLoops */ -+ -+#ifdef SQLITE_DEBUG -+ /* The star-query detection code below makes use of the following -+ ** properties of the WhereLoop list, so verify them before -+ ** continuing: -+ ** (1) .maskSelf is the bitmask corresponding to .iTab -+ ** (2) The WhereLoop list is in ascending .iTab order -+ */ -+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ -+ assert( pWLoop->maskSelf==MASKBIT(pWLoop->iTab) ); -+ assert( pWLoop->pNextLoop==0 || pWLoop->iTab<=pWLoop->pNextLoop->iTab ); -+ } -+#endif /* SQLITE_DEBUG */ -+ -+ if( nLoop>=5 -+ && !pWInfo->bStarDone -+ && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery) -+ ){ -+ SrcItem *aFromTabs; /* All terms of the FROM clause */ -+ int iFromIdx; /* Term of FROM clause is the candidate fact-table */ -+ Bitmask m; /* Bitmask for candidate fact-table */ -+ Bitmask mSelfJoin = 0; /* Tables that cannot be dimension tables */ -+ WhereLoop *pStart; /* Where to start searching for dimension-tables */ -+ -+ pWInfo->bStarDone = 1; /* Only do this computation once */ -+ -+ /* Look for fact tables with four or more dimensions where the -+ ** dimension tables are not separately from the fact tables by an outer -+ ** or cross join. Adjust cost weights if found. -+ */ -+ assert( !pWInfo->bStarUsed ); -+ aFromTabs = pWInfo->pTabList->a; -+ pStart = pWInfo->pLoops; -+ for(iFromIdx=0, m=1; iFromIdxfg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ -+ /* If the candidate fact-table is the right table of an outer join -+ ** restrict the search for dimension-tables to be tables to the right -+ ** of the fact-table. */ -+ if( iFromIdx+4 > nLoop ) break; /* Impossible to reach nDep>=4 */ -+ while( pStart && pStart->iTab<=iFromIdx ){ -+ pStart = pStart->pNextLoop; -+ } -+ } -+ for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ -+ if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ -+ /* Fact-tables and dimension-tables cannot be separated by an -+ ** outer join (at least for the definition of fact- and dimension- -+ ** used by this heuristic). */ -+ break; -+ } -+ if( (pWLoop->prereq & m)!=0 /* pWInfo depends on iFromIdx */ -+ && (pWLoop->maskSelf & mSeen)==0 /* pWInfo not already a dependency */ -+ && (pWLoop->maskSelf & mSelfJoin)==0 /* Not a self-join */ -+ ){ -+ if( aFromTabs[pWLoop->iTab].pSTab==pFactTab->pSTab ){ -+ mSelfJoin |= m; -+ }else{ -+ nDep++; -+ mSeen |= pWLoop->maskSelf; -+ } -+ } -+ } -+ if( nDep<=3 ) continue; -+ -+ /* If we reach this point, it means that pFactTab is a fact table -+ ** with four or more dimensions connected by inner joins. Proceed -+ ** to make cost adjustments. */ -+ -+#ifdef WHERETRACE_ENABLED -+ /* Make sure rStarDelta values are initialized */ -+ if( !pWInfo->bStarUsed ){ -+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ -+ pWLoop->rStarDelta = 0; -+ } -+ } -+#endif -+ pWInfo->bStarUsed = 1; -+ -+ /* Compute the maximum cost of any WhereLoop for the -+ ** fact table plus one epsilon */ -+ mxRun = LOGEST_MIN; -+ for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ -+ if( pWLoop->iTabiTab>iFromIdx ) break; -+ if( pWLoop->rRun>mxRun ) mxRun = pWLoop->rRun; -+ } -+ if( ALWAYS(mxRunpNextLoop){ -+ if( (pWLoop->maskSelf & mSeen)==0 ) continue; -+ if( pWLoop->nLTerm ) continue; -+ if( pWLoop->rRuniTab; -+ sqlite3DebugPrintf( -+ "Increase SCAN cost of dimension %s(%d) of fact %s(%d) to %d\n", -+ pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab, -+ pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName, -+ iFromIdx, mxRun -+ ); -+ } -+ pWLoop->rStarDelta = mxRun - pWLoop->rRun; -+#endif /* WHERETRACE_ENABLED */ -+ pWLoop->rRun = mxRun; -+ } -+ } -+ } -+#ifdef WHERETRACE_ENABLED /* 0x80000 */ -+ if( (sqlite3WhereTrace & 0x80000)!=0 && pWInfo->bStarUsed ){ -+ sqlite3DebugPrintf("WhereLoops changed by star-query heuristic:\n"); -+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ -+ if( pWLoop->rStarDelta ){ -+ sqlite3WhereLoopPrint(pWLoop, &pWInfo->sWC); -+ } -+ } -+ } -+#endif -+ } -+ return pWInfo->bStarUsed ? 18 : 12; -+} -+ -+/* -+** Two WhereLoop objects, pCandidate and pBaseline, are known to have the -+** same cost. Look deep into each to see if pCandidate is even slightly -+** better than pBaseline. Return false if it is, if pCandidate is is preferred. -+** Return true if pBaseline is preferred or if we cannot tell the difference. -+** -+** Result Meaning -+** -------- ---------------------------------------------------------- -+** true We cannot tell the difference in pCandidate and pBaseline -+** false pCandidate seems like a better choice than pBaseline -+*/ -+static SQLITE_NOINLINE int whereLoopIsNoBetter( -+ const WhereLoop *pCandidate, -+ const WhereLoop *pBaseline -+){ -+ if( (pCandidate->wsFlags & WHERE_INDEXED)==0 ) return 1; -+ if( (pBaseline->wsFlags & WHERE_INDEXED)==0 ) return 1; -+ if( pCandidate->u.btree.pIndex->szIdxRow < -+ pBaseline->u.btree.pIndex->szIdxRow ) return 0; -+ return 1; -+} -+ - /* - ** Given the list of WhereLoop objects at pWInfo->pLoops, this routine - ** attempts to find the lowest cost path that visits each WhereLoop -@@ -149695,13 +170147,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - int mxChoice; /* Maximum number of simultaneous paths tracked */ - int nLoop; /* Number of terms in the join */ - Parse *pParse; /* Parsing context */ -- sqlite3 *db; /* The database connection */ - int iLoop; /* Loop counter over the terms of the join */ - int ii, jj; /* Loop counters */ - int mxI = 0; /* Index of next entry to replace */ - int nOrderBy; /* Number of ORDER BY clause terms */ - LogEst mxCost = 0; /* Maximum cost of a set of paths */ -- LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */ -+ LogEst mxUnsort = 0; /* Maximum unsorted cost of a set of path */ - int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */ - WherePath *aFrom; /* All nFrom paths at the previous level */ - WherePath *aTo; /* The nTo best paths at the current level */ -@@ -149714,14 +170165,28 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - int nSpace; /* Bytes of space allocated at pSpace */ - - pParse = pWInfo->pParse; -- db = pParse->db; - nLoop = pWInfo->nLevel; -- /* TUNING: For simple queries, only the best path is tracked. -- ** For 2-way joins, the 5 best paths are followed. -- ** For joins of 3 or more tables, track the 10 best paths */ -- mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10); -+ WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d, nQueryLoop=%d)\n", -+ nRowEst, pParse->nQueryLoop)); -+ /* TUNING: mxChoice is the maximum number of possible paths to preserve -+ ** at each step. Based on the number of loops in the FROM clause: -+ ** -+ ** nLoop mxChoice -+ ** ----- -------- -+ ** 1 1 // the most common case -+ ** 2 5 -+ ** 3+ 12 or 18 // see computeMxChoice() -+ */ -+ if( nLoop<=1 ){ -+ mxChoice = 1; -+ }else if( nLoop==2 ){ -+ mxChoice = 5; -+ }else if( pParse->nErr ){ -+ mxChoice = 1; -+ }else{ -+ mxChoice = computeMxChoice(pWInfo); -+ } - assert( nLoop<=pWInfo->pTabList->nSrc ); -- WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst)); - - /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this - ** case the purpose of this call is to estimate the number of rows returned -@@ -149737,7 +170202,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - /* Allocate and initialize space for aTo, aFrom and aSortCost[] */ - nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2; - nSpace += sizeof(LogEst) * nOrderBy; -- pSpace = sqlite3DbMallocRawNN(db, nSpace); -+ pSpace = sqlite3StackAllocRawNN(pParse->db, nSpace); - if( pSpace==0 ) return SQLITE_NOMEM_BKPT; - aTo = (WherePath*)pSpace; - aFrom = aTo+mxChoice; -@@ -149786,10 +170251,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ - LogEst nOut; /* Rows visited by (pFrom+pWLoop) */ - LogEst rCost; /* Cost of path (pFrom+pWLoop) */ -- LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ -- i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */ -+ LogEst rUnsort; /* Unsorted cost of (pFrom+pWLoop) */ -+ i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */ - Bitmask maskNew; /* Mask of src visited by (..) */ -- Bitmask revMask = 0; /* Mask of rev-order loops for (..) */ -+ Bitmask revMask; /* Mask of rev-order loops for (..) */ - - if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue; - if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue; -@@ -149804,11 +170269,16 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - - /* At this point, pWLoop is a candidate to be the next loop. - ** Compute its cost */ -- rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow); -- rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); -+ rUnsort = pWLoop->rRun + pFrom->nRow; -+ if( pWLoop->rSetup ){ -+ rUnsort = sqlite3LogEstAdd(pWLoop->rSetup, rUnsort); -+ } -+ rUnsort = sqlite3LogEstAdd(rUnsort, pFrom->rUnsort); - nOut = pFrom->nRow + pWLoop->nOut; - maskNew = pFrom->maskLoop | pWLoop->maskSelf; -+ isOrdered = pFrom->isOrdered; - if( isOrdered<0 ){ -+ revMask = 0; - isOrdered = wherePathSatisfiesOrderBy(pWInfo, - pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags, - iLoop, pWLoop, &revMask); -@@ -149821,19 +170291,19 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - pWInfo, nRowEst, nOrderBy, isOrdered - ); - } -- /* TUNING: Add a small extra penalty (5) to sorting as an -- ** extra encouragment to the query planner to select a plan -+ /* TUNING: Add a small extra penalty (3) to sorting as an -+ ** extra encouragement to the query planner to select a plan - ** where the rows emerge in the correct order without any sorting - ** required. */ -- rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 5; -+ rCost = sqlite3LogEstAdd(rUnsort, aSortCost[isOrdered]) + 3; - - WHERETRACE(0x002, - ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n", - aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy, -- rUnsorted, rCost)); -+ rUnsort, rCost)); - }else{ -- rCost = rUnsorted; -- rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */ -+ rCost = rUnsort; -+ rUnsort -= 2; /* TUNING: Slight bias in favor of no-sort plans */ - } - - /* Check to see if pWLoop should be added to the set of -@@ -149847,6 +170317,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range - ** of legal values for isOrdered, -1..64. - */ -+ testcase( nTo==0 ); - for(jj=0, pTo=aTo; jjmaskLoop==maskNew - && ((pTo->isOrdered^isOrdered)&0x80)==0 -@@ -149858,7 +170329,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - if( jj>=nTo ){ - /* None of the existing best-so-far paths match the candidate. */ - if( nTo>=mxChoice -- && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) -+ && (rCost>mxCost || (rCost==mxCost && rUnsort>=mxUnsort)) - ){ - /* The current candidate is no better than any of the mxChoice - ** paths currently in the best-so-far buffer. So discard -@@ -149866,7 +170337,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - #ifdef WHERETRACE_ENABLED /* 0x4 */ - if( sqlite3WhereTrace&0x4 ){ - sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n", -- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, -+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, - isOrdered>=0 ? isOrdered+'0' : '?'); - } - #endif -@@ -149885,7 +170356,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - #ifdef WHERETRACE_ENABLED /* 0x4 */ - if( sqlite3WhereTrace&0x4 ){ - sqlite3DebugPrintf("New %s cost=%-3d,%3d,%3d order=%c\n", -- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, -+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, - isOrdered>=0 ? isOrdered+'0' : '?'); - } - #endif -@@ -149896,24 +170367,23 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - ** pTo or if the candidate should be skipped. - ** - ** The conditional is an expanded vector comparison equivalent to: -- ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted) -+ ** (pTo->rCost,pTo->nRow,pTo->rUnsort) <= (rCost,nOut,rUnsort) - */ -- if( pTo->rCostrCost==rCost -- && (pTo->nRownRow==nOut && pTo->rUnsorted<=rUnsorted) -- ) -- ) -+ if( (pTo->rCostrCost==rCost && pTo->nRowrCost==rCost && pTo->nRow==nOut && pTo->rUnsortrCost==rCost && pTo->nRow==nOut && pTo->rUnsort==rUnsort -+ && whereLoopIsNoBetter(pWLoop, pTo->aLoop[iLoop]) ) - ){ - #ifdef WHERETRACE_ENABLED /* 0x4 */ - if( sqlite3WhereTrace&0x4 ){ - sqlite3DebugPrintf( - "Skip %s cost=%-3d,%3d,%3d order=%c", -- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, -+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, - isOrdered>=0 ? isOrdered+'0' : '?'); - sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, -- pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); -+ pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); - } - #endif - /* Discard the candidate path from further consideration */ -@@ -149927,11 +170397,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - if( sqlite3WhereTrace&0x4 ){ - sqlite3DebugPrintf( - "Update %s cost=%-3d,%3d,%3d order=%c", -- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, -+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, - isOrdered>=0 ? isOrdered+'0' : '?'); - sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, -- pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); -+ pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); - } - #endif - } -@@ -149940,20 +170410,20 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - pTo->revLoop = revMask; - pTo->nRow = nOut; - pTo->rCost = rCost; -- pTo->rUnsorted = rUnsorted; -+ pTo->rUnsort = rUnsort; - pTo->isOrdered = isOrdered; - memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop); - pTo->aLoop[iLoop] = pWLoop; - if( nTo>=mxChoice ){ - mxI = 0; - mxCost = aTo[0].rCost; -- mxUnsorted = aTo[0].nRow; -+ mxUnsort = aTo[0].nRow; - for(jj=1, pTo=&aTo[1]; jjrCost>mxCost -- || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted) -+ || (pTo->rCost==mxCost && pTo->rUnsort>mxUnsort) - ){ - mxCost = pTo->rCost; -- mxUnsorted = pTo->rUnsorted; -+ mxUnsort = pTo->rUnsort; - mxI = jj; - } - } -@@ -149963,17 +170433,32 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - - #ifdef WHERETRACE_ENABLED /* >=2 */ - if( sqlite3WhereTrace & 0x02 ){ -+ LogEst rMin, rFloor = 0; -+ int nDone = 0; -+ int nProgress; - sqlite3DebugPrintf("---- after round %d ----\n", iLoop); -- for(ii=0, pTo=aTo; iirCost, pTo->nRow, -- pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); -- if( pTo->isOrdered>0 ){ -- sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); -- }else{ -- sqlite3DebugPrintf("\n"); -+ do{ -+ nProgress = 0; -+ rMin = 0x7fff; -+ for(ii=0, pTo=aTo; iirCost>rFloor && pTo->rCostrCost; -+ } -+ for(ii=0, pTo=aTo; iirCost==rMin ){ -+ sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c", -+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, -+ pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); -+ if( pTo->isOrdered>0 ){ -+ sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); -+ }else{ -+ sqlite3DebugPrintf("\n"); -+ } -+ nDone++; -+ nProgress++; -+ } - } -- } -+ rFloor = rMin; -+ }while( nDone0 ); - } - #endif - -@@ -149986,7 +170471,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - - if( nFrom==0 ){ - sqlite3ErrorMsg(pParse, "no query solution"); -- sqlite3DbFreeNN(db, pSpace); -+ sqlite3StackFreeNN(pParse->db, pSpace); - return SQLITE_ERROR; - } - -@@ -150017,12 +170502,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - } - pWInfo->bOrderedInnerLoop = 0; - if( pWInfo->pOrderBy ){ -+ pWInfo->nOBSat = pFrom->isOrdered; - if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ - if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ - pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; - } -+ /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */ -+ assert( pWInfo->pSelect->pOrderBy==0 -+ || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr ); - }else{ -- pWInfo->nOBSat = pFrom->isOrdered; - pWInfo->revMask = pFrom->revLoop; - if( pWInfo->nOBSat<=0 ){ - pWInfo->nOBSat = 0; -@@ -150064,14 +170552,93 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - } - } - -- - pWInfo->nRowOut = pFrom->nRow; -+#ifdef WHERETRACE_ENABLED -+ pWInfo->rTotalCost = pFrom->rCost; -+#endif - - /* Free temporary memory and return success */ -- sqlite3DbFreeNN(db, pSpace); -+ sqlite3StackFreeNN(pParse->db, pSpace); - return SQLITE_OK; - } - -+/* -+** This routine implements a heuristic designed to improve query planning. -+** This routine is called in between the first and second call to -+** wherePathSolver(). Hence the name "Interstage" "Heuristic". -+** -+** The first call to wherePathSolver() (hereafter just "solver()") computes -+** the best path without regard to the order of the outputs. The second call -+** to the solver() builds upon the first call to try to find an alternative -+** path that satisfies the ORDER BY clause. -+** -+** This routine looks at the results of the first solver() run, and for -+** every FROM clause term in the resulting query plan that uses an equality -+** constraint against an index, disable other WhereLoops for that same -+** FROM clause term that would try to do a full-table scan. This prevents -+** an index search from being converted into a full-table scan in order to -+** satisfy an ORDER BY clause, since even though we might get slightly better -+** performance using the full-scan without sorting if the output size -+** estimates are very precise, we might also get severe performance -+** degradation using the full-scan if the output size estimate is too large. -+** It is better to err on the side of caution. -+** -+** Except, if the first solver() call generated a full-table scan in an outer -+** loop then stop this analysis at the first full-scan, since the second -+** solver() run might try to swap that full-scan for another in order to -+** get the output into the correct order. In other words, we allow a -+** rewrite like this: -+** -+** First Solver() Second Solver() -+** |-- SCAN t1 |-- SCAN t2 -+** |-- SEARCH t2 `-- SEARCH t1 -+** `-- SORT USING B-TREE -+** -+** The purpose of this routine is to disallow rewrites such as: -+** -+** First Solver() Second Solver() -+** |-- SEARCH t1 |-- SCAN t2 <--- bad! -+** |-- SEARCH t2 `-- SEARCH t1 -+** `-- SORT USING B-TREE -+** -+** See test cases in test/whereN.test for the real-world query that -+** originally provoked this heuristic. -+*/ -+static SQLITE_NOINLINE void whereInterstageHeuristic(WhereInfo *pWInfo){ -+ int i; -+#ifdef WHERETRACE_ENABLED -+ int once = 0; -+#endif -+ for(i=0; inLevel; i++){ -+ WhereLoop *p = pWInfo->a[i].pWLoop; -+ if( p==0 ) break; -+ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue; -+ if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){ -+ u8 iTab = p->iTab; -+ WhereLoop *pLoop; -+ for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){ -+ if( pLoop->iTab!=iTab ) continue; -+ if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){ -+ /* Auto-index and index-constrained loops allowed to remain */ -+ continue; -+ } -+#ifdef WHERETRACE_ENABLED -+ if( sqlite3WhereTrace & 0x80 ){ -+ if( once==0 ){ -+ sqlite3DebugPrintf("Loops disabled by interstage heuristic:\n"); -+ once = 1; -+ } -+ sqlite3WhereLoopPrint(pLoop, &pWInfo->sWC); -+ } -+#endif /* WHERETRACE_ENABLED */ -+ pLoop->prereq = ALLBITS; /* Prevent 2nd solver() from using this one */ -+ } -+ }else{ -+ break; -+ } -+ } -+} -+ - /* - ** Most queries use only a single table (they are not joins) and have - ** simple == constraints against indexed fields. This routine attempts -@@ -150085,7 +170652,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ - */ - static int whereShortCut(WhereLoopBuilder *pBuilder){ - WhereInfo *pWInfo; -- struct SrcList_item *pItem; -+ SrcItem *pItem; - WhereClause *pWC; - WhereTerm *pTerm; - WhereLoop *pLoop; -@@ -150093,20 +170660,26 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ - int j; - Table *pTab; - Index *pIdx; -+ WhereScan scan; - - pWInfo = pBuilder->pWInfo; - if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; - assert( pWInfo->pTabList->nSrc>=1 ); - pItem = pWInfo->pTabList->a; -- pTab = pItem->pTab; -+ pTab = pItem->pSTab; - if( IsVirtual(pTab) ) return 0; -- if( pItem->fg.isIndexedBy ) return 0; -+ if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){ -+ testcase( pItem->fg.isIndexedBy ); -+ testcase( pItem->fg.notIndexed ); -+ return 0; -+ } - iCur = pItem->iCursor; - pWC = &pWInfo->sWC; - pLoop = pBuilder->pNew; - pLoop->wsFlags = 0; - pLoop->nSkip = 0; -- pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); -+ pTerm = whereScanInit(&scan, pWC, iCur, -1, WO_EQ|WO_IS, 0); -+ while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); - if( pTerm ){ - testcase( pTerm->eOperator & WO_IS ); - pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; -@@ -150125,7 +170698,8 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ - ) continue; - opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ; - for(j=0; jnKeyCol; j++){ -- pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx); -+ pTerm = whereScanInit(&scan, pWC, iCur, j, opMask, pIdx); -+ while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan); - if( pTerm==0 ) break; - testcase( pTerm->eOperator & WO_IS ); - pLoop->aLTerm[j] = pTerm; -@@ -150154,8 +170728,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ - if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; - } -+ if( scan.iEquiv>1 ) pLoop->wsFlags |= WHERE_TRANSCONS; - #ifdef SQLITE_DEBUG - pLoop->cId = '0'; -+#endif -+#ifdef WHERETRACE_ENABLED -+ if( sqlite3WhereTrace & 0x02 ){ -+ sqlite3DebugPrintf("whereShortCut() used to compute solution\n"); -+ } - #endif - return 1; - } -@@ -150210,6 +170790,255 @@ static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){ - # define WHERETRACE_ALL_LOOPS(W,C) - #endif - -+/* Attempt to omit tables from a join that do not affect the result. -+** For a table to not affect the result, the following must be true: -+** -+** 1) The query must not be an aggregate. -+** 2) The table must be the RHS of a LEFT JOIN. -+** 3) Either the query must be DISTINCT, or else the ON or USING clause -+** must contain a constraint that limits the scan of the table to -+** at most a single row. -+** 4) The table must not be referenced by any part of the query apart -+** from its own USING or ON clause. -+** 5) The table must not have an inner-join ON or USING clause if there is -+** a RIGHT JOIN anywhere in the query. Otherwise the ON/USING clause -+** might move from the right side to the left side of the RIGHT JOIN. -+** Note: Due to (2), this condition can only arise if the table is -+** the right-most table of a subquery that was flattened into the -+** main query and that subquery was the right-hand operand of an -+** inner join that held an ON or USING clause. -+** 6) The ORDER BY clause has 63 or fewer terms -+** 7) The omit-noop-join optimization is enabled. -+** -+** Items (1), (6), and (7) are checked by the caller. -+** -+** For example, given: -+** -+** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); -+** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); -+** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); -+** -+** then table t2 can be omitted from the following: -+** -+** SELECT v1, v3 FROM t1 -+** LEFT JOIN t2 ON (t1.ipk=t2.ipk) -+** LEFT JOIN t3 ON (t1.ipk=t3.ipk) -+** -+** or from: -+** -+** SELECT DISTINCT v1, v3 FROM t1 -+** LEFT JOIN t2 -+** LEFT JOIN t3 ON (t1.ipk=t3.ipk) -+*/ -+static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( -+ WhereInfo *pWInfo, -+ Bitmask notReady -+){ -+ int i; -+ Bitmask tabUsed; -+ int hasRightJoin; -+ -+ /* Preconditions checked by the caller */ -+ assert( pWInfo->nLevel>=2 ); -+ assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) ); -+ -+ /* These two preconditions checked by the caller combine to guarantee -+ ** condition (1) of the header comment */ -+ assert( pWInfo->pResultSet!=0 ); -+ assert( 0==(pWInfo->wctrlFlags & WHERE_AGG_DISTINCT) ); -+ -+ tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet); -+ if( pWInfo->pOrderBy ){ -+ tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); -+ } -+ hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; -+ for(i=pWInfo->nLevel-1; i>=1; i--){ -+ WhereTerm *pTerm, *pEnd; -+ SrcItem *pItem; -+ WhereLoop *pLoop; -+ Bitmask m1; -+ pLoop = pWInfo->a[i].pWLoop; -+ pItem = &pWInfo->pTabList->a[pLoop->iTab]; -+ if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; -+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0 -+ && (pLoop->wsFlags & WHERE_ONEROW)==0 -+ ){ -+ continue; -+ } -+ if( (tabUsed & pLoop->maskSelf)!=0 ) continue; -+ pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm; -+ for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ -+ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON) -+ || pTerm->pExpr->w.iJoin!=pItem->iCursor -+ ){ -+ break; -+ } -+ } -+ if( hasRightJoin -+ && ExprHasProperty(pTerm->pExpr, EP_InnerON) -+ && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor) -+ ){ -+ break; /* restriction (5) */ -+ } -+ } -+ if( pTerm omit unused FROM-clause term %c\n",pLoop->cId)); -+ m1 = MASKBIT(i)-1; -+ testcase( ((pWInfo->revMask>>1) & ~m1)!=0 ); -+ pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); -+ notReady &= ~pLoop->maskSelf; -+ for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ -+ pTerm->wtFlags |= TERM_CODED; -+ } -+ } -+ if( i!=pWInfo->nLevel-1 ){ -+ int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); -+ memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); -+ } -+ pWInfo->nLevel--; -+ assert( pWInfo->nLevel>0 ); -+ } -+ return notReady; -+} -+ -+/* -+** Check to see if there are any SEARCH loops that might benefit from -+** using a Bloom filter. Consider a Bloom filter if: -+** -+** (1) The SEARCH happens more than N times where N is the number -+** of rows in the table that is being considered for the Bloom -+** filter. -+** (2) Some searches are expected to find zero rows. (This is determined -+** by the WHERE_SELFCULL flag on the term.) -+** (3) Bloom-filter processing is not disabled. (Checked by the -+** caller.) -+** (4) The size of the table being searched is known by ANALYZE. -+** -+** This block of code merely checks to see if a Bloom filter would be -+** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the -+** WhereLoop. The implementation of the Bloom filter comes further -+** down where the code for each WhereLoop is generated. -+*/ -+static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( -+ const WhereInfo *pWInfo -+){ -+ int i; -+ LogEst nSearch = 0; -+ -+ assert( pWInfo->nLevel>=2 ); -+ assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) ); -+ for(i=0; inLevel; i++){ -+ WhereLoop *pLoop = pWInfo->a[i].pWLoop; -+ const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); -+ SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; -+ Table *pTab = pItem->pSTab; -+ if( (pTab->tabFlags & TF_HasStat1)==0 ) break; -+ pTab->tabFlags |= TF_MaybeReanalyze; -+ if( i>=1 -+ && (pLoop->wsFlags & reqFlags)==reqFlags -+ /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ -+ && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0) -+ ){ -+ if( nSearch > pTab->nRowLogEst ){ -+ testcase( pItem->fg.jointype & JT_LEFT ); -+ pLoop->wsFlags |= WHERE_BLOOMFILTER; -+ pLoop->wsFlags &= ~WHERE_IDX_ONLY; -+ WHERETRACE(0xffffffff, ( -+ "-> use Bloom-filter on loop %c because there are ~%.1e " -+ "lookups into %s which has only ~%.1e rows\n", -+ pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName, -+ (double)sqlite3LogEstToInt(pTab->nRowLogEst))); -+ } -+ } -+ nSearch += pLoop->nOut; -+ } -+} -+ -+/* -+** The index pIdx is used by a query and contains one or more expressions. -+** In other words pIdx is an index on an expression. iIdxCur is the cursor -+** number for the index and iDataCur is the cursor number for the corresponding -+** table. -+** -+** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for -+** each of the expressions in the index so that the expression code generator -+** will know to replace occurrences of the indexed expression with -+** references to the corresponding column of the index. -+*/ -+static SQLITE_NOINLINE void whereAddIndexedExpr( -+ Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxEpr */ -+ Index *pIdx, /* The index-on-expression that contains the expressions */ -+ int iIdxCur, /* Cursor number for pIdx */ -+ SrcItem *pTabItem /* The FROM clause entry for the table */ -+){ -+ int i; -+ IndexedExpr *p; -+ Table *pTab; -+ assert( pIdx->bHasExpr ); -+ pTab = pIdx->pTable; -+ for(i=0; inColumn; i++){ -+ Expr *pExpr; -+ int j = pIdx->aiColumn[i]; -+ if( j==XN_EXPR ){ -+ pExpr = pIdx->aColExpr->a[i].pExpr; -+ }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ -+ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); -+ }else{ -+ continue; -+ } -+ if( sqlite3ExprIsConstant(0,pExpr) ) continue; -+ p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); -+ if( p==0 ) break; -+ p->pIENext = pParse->pIdxEpr; -+#ifdef WHERETRACE_ENABLED -+ if( sqlite3WhereTrace & 0x200 ){ -+ sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i); -+ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(pExpr); -+ } -+#endif -+ p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); -+ p->iDataCur = pTabItem->iCursor; -+ p->iIdxCur = iIdxCur; -+ p->iIdxCol = i; -+ p->bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; -+ if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){ -+ p->aff = pIdx->zColAff[i]; -+ } -+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS -+ p->zIdxName = pIdx->zName; -+#endif -+ pParse->pIdxEpr = p; -+ if( p->pIENext==0 ){ -+ void *pArg = (void*)&pParse->pIdxEpr; -+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); -+ } -+ } -+} -+ -+/* -+** Set the reverse-scan order mask to one for all tables in the query -+** with the exception of MATERIALIZED common table expressions that have -+** their own internal ORDER BY clauses. -+** -+** This implements the PRAGMA reverse_unordered_selects=ON setting. -+** (Also SQLITE_DBCONFIG_REVERSE_SCANORDER). -+*/ -+static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){ -+ int ii; -+ for(ii=0; iipTabList->nSrc; ii++){ -+ SrcItem *pItem = &pWInfo->pTabList->a[ii]; -+ if( !pItem->fg.isCte -+ || pItem->u2.pCteUse->eM10d!=M10d_Yes -+ || NEVER(pItem->fg.isSubquery==0) -+ || pItem->u4.pSubq->pSelect->pOrderBy==0 -+ ){ -+ pWInfo->revMask |= MASKBIT(ii); -+ } -+ } -+} -+ - /* - ** Generate the beginning of the loop used for WHERE clause processing. - ** The return value is a pointer to an opaque structure that contains -@@ -150268,7 +171097,7 @@ static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){ - ** - ** OUTER JOINS - ** --** An outer join of tables t1 and t2 is conceptally coded as follows: -+** An outer join of tables t1 and t2 is conceptually coded as follows: - ** - ** foreach row1 in t1 do - ** flag = 0 -@@ -150304,6 +171133,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - Expr *pWhere, /* The WHERE clause */ - ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ - ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ -+ Select *pSelect, /* The entire SELECT statement */ - u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ - int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number - ** If WHERE_USE_LIMIT, then the limit amount */ -@@ -150337,13 +171167,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - - /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ - testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); -- if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0; -- sWLB.pOrderBy = pOrderBy; -- -- /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via -- ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ -- if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ -+ if( pOrderBy && pOrderBy->nExpr>=BMS ){ -+ pOrderBy = 0; - wctrlFlags &= ~WHERE_WANT_DISTINCT; -+ wctrlFlags |= WHERE_KEEP_ALL_JOINS; /* Disable omit-noop-join opt */ - } - - /* The number of tables in the FROM clause is limited by the number of -@@ -150369,7 +171196,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - ** field (type Bitmask) it must be aligned on an 8-byte boundary on - ** some architectures. Hence the ROUND8() below. - */ -- nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); -+ nByteWInfo = SZ_WHEREINFO(nTabList); - pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); - if( db->mallocFailed ){ - sqlite3DbFree(db, pWInfo); -@@ -150379,7 +171206,9 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - pWInfo->pParse = pParse; - pWInfo->pTabList = pTabList; - pWInfo->pOrderBy = pOrderBy; -+#if WHERETRACE_ENABLED - pWInfo->pWhere = pWhere; -+#endif - pWInfo->pResultSet = pResultSet; - pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; - pWInfo->nLevel = nTabList; -@@ -150387,11 +171216,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - pWInfo->wctrlFlags = wctrlFlags; - pWInfo->iLimit = iAuxArg; - pWInfo->savedNQueryLoop = pParse->nQueryLoop; -+ pWInfo->pSelect = pSelect; - memset(&pWInfo->nOBSat, 0, - offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); - memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); - assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */ - pMaskSet = &pWInfo->sMaskSet; -+ pMaskSet->n = 0; -+ pMaskSet->ix[0] = -99; /* Initialize ix[0] to a value that can never be -+ ** a valid cursor number, to avoid an initial -+ ** test for pMaskSet->n==0 in sqlite3WhereGetMask() */ - sWLB.pWInfo = pWInfo; - sWLB.pWC = &pWInfo->sWC; - sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo); -@@ -150404,7 +171238,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - /* Split the WHERE clause into separate subexpressions where each - ** subexpression is separated by an AND operator. - */ -- initMaskSet(pMaskSet); - sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); - sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); - -@@ -150412,16 +171245,22 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - */ - if( nTabList==0 ){ - if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr; -- if( wctrlFlags & WHERE_WANT_DISTINCT ){ -+ if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0 -+ && OptimizationEnabled(db, SQLITE_DistinctOpt) -+ ){ - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; - } -- ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); -+ if( ALWAYS(pWInfo->pSelect) -+ && (pWInfo->pSelect->selFlags & SF_MultiValue)==0 -+ ){ -+ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); -+ } - }else{ - /* Assign a bit from the bitmask to every term in the FROM clause. - ** - ** The N-th term of the FROM clause is assigned a bitmask of 1<sWC); -- if( db->mallocFailed ) goto whereBeginError; -+ if( pSelect && pSelect->pLimit ){ -+ sqlite3WhereAddLimit(&pWInfo->sWC, pSelect); -+ } -+ if( pParse->nErr ) goto whereBeginError; - -- /* Special case: WHERE terms that do not refer to any tables in the join -- ** (constant expressions). Evaluate each such term, and jump over all the -- ** generated code if the result is not true. -+ /* The False-WHERE-Term-Bypass optimization: - ** -- ** Do not do this if the expression contains non-deterministic functions -- ** that are not within a sub-select. This is not strictly required, but -- ** preserves SQLite's legacy behaviour in the following two cases: -+ ** If there are WHERE terms that are false, then no rows will be output, -+ ** so skip over all of the code generated here. - ** -- ** FROM ... WHERE random()>0; -- eval random() once per row -- ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall -+ ** Conditions: -+ ** -+ ** (1) The WHERE term must not refer to any tables in the join. -+ ** (2) The term must not come from an ON clause on the -+ ** right-hand side of a LEFT or FULL JOIN. -+ ** (3) The term must not come from an ON clause, or there must be -+ ** no RIGHT or FULL OUTER joins in pTabList. -+ ** (4) If the expression contains non-deterministic functions -+ ** that are not within a sub-select. This is not required -+ ** for correctness but rather to preserves SQLite's legacy -+ ** behaviour in the following two cases: -+ ** -+ ** WHERE random()>0; -- eval random() once per row -+ ** WHERE (SELECT random())>0; -- eval random() just once overall -+ ** -+ ** Note that the Where term need not be a constant in order for this -+ ** optimization to apply, though it does need to be constant relative to -+ ** the current subquery (condition 1). The term might include variables -+ ** from outer queries so that the value of the term changes from one -+ ** invocation of the current subquery to the next. - */ -- for(ii=0; iinTerm; ii++){ -- WhereTerm *pT = &sWLB.pWC->a[ii]; -+ for(ii=0; iinBase; ii++){ -+ WhereTerm *pT = &sWLB.pWC->a[ii]; /* A term of the WHERE clause */ -+ Expr *pX; /* The expression of pT */ - if( pT->wtFlags & TERM_VIRTUAL ) continue; -- if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ -- sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); -+ pX = pT->pExpr; -+ assert( pX!=0 ); -+ assert( pT->prereqAll!=0 || !ExprHasProperty(pX, EP_OuterON) ); -+ if( pT->prereqAll==0 /* Conditions (1) and (2) */ -+ && (nTabList==0 || exprIsDeterministic(pX)) /* Condition (4) */ -+ && !(ExprHasProperty(pX, EP_InnerON) /* Condition (3) */ -+ && (pTabList->a[0].fg.jointype & JT_LTORJ)!=0 ) -+ ){ -+ sqlite3ExprIfFalse(pParse, pX, pWInfo->iBreak, SQLITE_JUMPIFNULL); - pT->wtFlags |= TERM_CODED; - } - } - - if( wctrlFlags & WHERE_WANT_DISTINCT ){ -- if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ -+ if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){ -+ /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via -+ ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ -+ wctrlFlags &= ~WHERE_WANT_DISTINCT; -+ pWInfo->wctrlFlags &= ~WHERE_WANT_DISTINCT; -+ }else if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ - /* The DISTINCT marking is pointless. Ignore it. */ - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; - }else if( pOrderBy==0 ){ -@@ -150485,13 +171355,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - - /* Construct the WhereLoop objects */ - #if defined(WHERETRACE_ENABLED) -- if( sqlite3WhereTrace & 0xffff ){ -+ if( sqlite3WhereTrace & 0xffffffff ){ - sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags); - if( wctrlFlags & WHERE_USE_LIMIT ){ - sqlite3DebugPrintf(", limit: %d", iAuxArg); - } - sqlite3DebugPrintf(")\n"); -- if( sqlite3WhereTrace & 0x100 ){ -+ if( sqlite3WhereTrace & 0x8000 ){ - Select sSelect; - memset(&sSelect, 0, sizeof(sSelect)); - sSelect.selFlags = SF_WhereBegin; -@@ -150501,10 +171371,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - sSelect.pEList = pResultSet; - sqlite3TreeViewSelect(0, &sSelect, 0); - } -- } -- if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ -- sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n"); -- sqlite3WhereClausePrint(sWLB.pWC); -+ if( sqlite3WhereTrace & 0x4000 ){ /* Display all WHERE clause terms */ -+ sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n"); -+ sqlite3WhereClausePrint(sWLB.pWC); -+ } - } - #endif - -@@ -150520,7 +171390,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - ** loops will be built using the revised truthProb values. */ - if( sWLB.bldFlags2 & SQLITE_BLDF2_2NDPASS ){ - WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC); -- WHERETRACE(0xffff, -+ WHERETRACE(0xffffffff, - ("**** Redo all loop computations due to" - " TERM_HIGHTRUTH changes ****\n")); - while( pWInfo->pLoops ){ -@@ -150537,19 +171407,34 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - wherePathSolver(pWInfo, 0); - if( db->mallocFailed ) goto whereBeginError; - if( pWInfo->pOrderBy ){ -- wherePathSolver(pWInfo, pWInfo->nRowOut+1); -+ whereInterstageHeuristic(pWInfo); -+ wherePathSolver(pWInfo, pWInfo->nRowOut<0 ? 1 : pWInfo->nRowOut+1); - if( db->mallocFailed ) goto whereBeginError; - } -+ -+ /* TUNING: Assume that a DISTINCT clause on a subquery reduces -+ ** the output size by a factor of 8 (LogEst -30). Search for -+ ** tag-20250414a to see other cases. -+ */ -+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){ -+ WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n", -+ pWInfo->nRowOut, pWInfo->nRowOut-30)); -+ pWInfo->nRowOut -= 30; -+ } -+ - } -+ assert( pWInfo->pTabList!=0 ); - if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ -- pWInfo->revMask = ALLBITS; -+ whereReverseScanOrder(pWInfo); - } -- if( pParse->nErr || NEVER(db->mallocFailed) ){ -+ if( pParse->nErr ){ - goto whereBeginError; - } -+ assert( db->mallocFailed==0 ); - #ifdef WHERETRACE_ENABLED - if( sqlite3WhereTrace ){ -- sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); -+ sqlite3DebugPrintf("---- Solution cost=%d, nRow=%d", -+ pWInfo->rTotalCost, pWInfo->nRowOut); - if( pWInfo->nOBSat>0 ){ - sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); - } -@@ -150574,89 +171459,42 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - } - #endif - -- /* Attempt to omit tables from the join that do not affect the result. -- ** For a table to not affect the result, the following must be true: -- ** -- ** 1) The query must not be an aggregate. -- ** 2) The table must be the RHS of a LEFT JOIN. -- ** 3) Either the query must be DISTINCT, or else the ON or USING clause -- ** must contain a constraint that limits the scan of the table to -- ** at most a single row. -- ** 4) The table must not be referenced by any part of the query apart -- ** from its own USING or ON clause. -- ** -- ** For example, given: -- ** -- ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); -- ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); -- ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); -- ** -- ** then table t2 can be omitted from the following: -+ /* Attempt to omit tables from a join that do not affect the result. -+ ** See the comment on whereOmitNoopJoin() for further information. - ** -- ** SELECT v1, v3 FROM t1 -- ** LEFT JOIN t2 ON (t1.ipk=t2.ipk) -- ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) -- ** -- ** or from: -- ** -- ** SELECT DISTINCT v1, v3 FROM t1 -- ** LEFT JOIN t2 -- ** LEFT JOIN t3 ON (t1.ipk=t3.ipk) -+ ** This query optimization is factored out into a separate "no-inline" -+ ** procedure to keep the sqlite3WhereBegin() procedure from becoming -+ ** too large. If sqlite3WhereBegin() becomes too large, that prevents -+ ** some C-compiler optimizers from in-lining the -+ ** sqlite3WhereCodeOneLoopStart() procedure, and it is important to -+ ** in-line sqlite3WhereCodeOneLoopStart() for performance reasons. - */ - notReady = ~(Bitmask)0; -+ if( pWInfo->nLevel>=2 /* Must be a join, or this opt8n is pointless */ -+ && pResultSet!=0 /* Condition (1) */ -+ && 0==(wctrlFlags & (WHERE_AGG_DISTINCT|WHERE_KEEP_ALL_JOINS)) /* (1),(6) */ -+ && OptimizationEnabled(db, SQLITE_OmitNoopJoin) /* (7) */ -+ ){ -+ notReady = whereOmitNoopJoin(pWInfo, notReady); -+ nTabList = pWInfo->nLevel; -+ assert( nTabList>0 ); -+ } -+ -+ /* Check to see if there are any SEARCH loops that might benefit from -+ ** using a Bloom filter. -+ */ - if( pWInfo->nLevel>=2 -- && pResultSet!=0 /* guarantees condition (1) above */ -- && OptimizationEnabled(db, SQLITE_OmitNoopJoin) -+ && OptimizationEnabled(db, SQLITE_BloomFilter) - ){ -- int i; -- Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet); -- if( sWLB.pOrderBy ){ -- tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); -- } -- for(i=pWInfo->nLevel-1; i>=1; i--){ -- WhereTerm *pTerm, *pEnd; -- struct SrcList_item *pItem; -- pLoop = pWInfo->a[i].pWLoop; -- pItem = &pWInfo->pTabList->a[pLoop->iTab]; -- if( (pItem->fg.jointype & JT_LEFT)==0 ) continue; -- if( (wctrlFlags & WHERE_WANT_DISTINCT)==0 -- && (pLoop->wsFlags & WHERE_ONEROW)==0 -- ){ -- continue; -- } -- if( (tabUsed & pLoop->maskSelf)!=0 ) continue; -- pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; -- for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ -- if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin) -- || pTerm->pExpr->iRightJoinTable!=pItem->iCursor -- ){ -- break; -- } -- } -- } -- if( pTerm drop loop %c not used\n", pLoop->cId)); -- notReady &= ~pLoop->maskSelf; -- for(pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0 ){ -- pTerm->wtFlags |= TERM_CODED; -- } -- } -- if( i!=pWInfo->nLevel-1 ){ -- int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); -- memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); -- } -- pWInfo->nLevel--; -- nTabList--; -- } -+ whereCheckIfBloomFilterIsUseful(pWInfo); - } -+ - #if defined(WHERETRACE_ENABLED) -- if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ -+ if( sqlite3WhereTrace & 0x4000 ){ /* Display all terms of the WHERE clause */ - sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n"); - sqlite3WhereClausePrint(sWLB.pWC); - } -- WHERETRACE(0xffff,("*** Optimizer Finished ***\n")); -+ WHERETRACE(0xffffffff,("*** Optimizer Finished ***\n")); - #endif - pWInfo->pParse->nQueryLoop += pWInfo->nRowOut; - -@@ -150683,14 +171521,15 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ - int wsFlags = pWInfo->a[0].pWLoop->wsFlags; - int bOnerow = (wsFlags & WHERE_ONEROW)!=0; -- assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab) ); -+ assert( !(wsFlags&WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pSTab) ); - if( bOnerow || ( - 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) -- && !IsVirtual(pTabList->a[0].pTab) -+ && !IsVirtual(pTabList->a[0].pSTab) - && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) -+ && OptimizationEnabled(db, SQLITE_OnePass) - )){ - pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; -- if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){ -+ if( HasRowid(pTabList->a[0].pSTab) && (wsFlags & WHERE_IDX_ONLY) ){ - if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){ - bFordelete = OPFLAG_FORDELETE; - } -@@ -150705,13 +171544,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - for(ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom]; -- pTab = pTabItem->pTab; -+ pTab = pTabItem->pSTab; - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - pLoop = pLevel->pWLoop; -- if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ -+ if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){ - /* Do nothing */ - }else - #ifndef SQLITE_OMIT_VIRTUALTABLE -@@ -150723,8 +171562,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - /* noop */ - }else - #endif -- if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 -- && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ -+ if( ((pLoop->wsFlags & WHERE_IDX_ONLY)==0 -+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0) -+ || (pTabItem->fg.jointype & (JT_LTORJ|JT_RIGHT))!=0 -+ ){ - int op = OP_OpenRead; - if( pWInfo->eOnePass!=ONEPASS_OFF ){ - op = OP_OpenWrite; -@@ -150737,6 +171578,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - if( pWInfo->eOnePass==ONEPASS_OFF - && pTab->nColtabFlags & (TF_HasGenerated|TF_WithoutRowid))==0 -+ && (pLoop->wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))==0 - ){ - /* If we know that only a prefix of the record will be used, - ** it is advantageous to reduce the "column count" field in -@@ -150748,7 +171590,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - assert( n<=pTab->nCol ); - } - #ifdef SQLITE_ENABLE_CURSOR_HINTS -- if( pLoop->u.btree.pIndex!=0 ){ -+ if( pLoop->u.btree.pIndex!=0 && (pTab->tabFlags & TF_WithoutRowid)==0 ){ - sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); - }else - #endif -@@ -150776,7 +171618,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - iIndexCur = pLevel->iTabCur; - op = 0; - }else if( pWInfo->eOnePass!=ONEPASS_OFF ){ -- Index *pJ = pTabItem->pTab->pIndex; -+ Index *pJ = pTabItem->pSTab->pIndex; - iIndexCur = iAuxArg; - assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); - while( ALWAYS(pJ) && pJ!=pIx ){ -@@ -150790,8 +171632,17 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - op = OP_ReopenIdx; - }else{ - iIndexCur = pParse->nTab++; -+ if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){ -+ whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem); -+ } -+ if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){ -+ wherePartIdxExpr( -+ pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem -+ ); -+ } - } - pLevel->iIdxCur = iIndexCur; -+ assert( pIx!=0 ); - assert( pIx->pSchema==pTab->pSchema ); - assert( iIndexCur>=0 ); - if( op ){ -@@ -150800,6 +171651,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 - && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 - && (pLoop->wsFlags & WHERE_BIGNULL_SORT)==0 -+ && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 - && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 - && pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED - ){ -@@ -150824,6 +171676,37 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - } - } - if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb); -+ if( (pTabItem->fg.jointype & JT_RIGHT)!=0 -+ && (pLevel->pRJ = sqlite3WhereMalloc(pWInfo, sizeof(WhereRightJoin)))!=0 -+ ){ -+ WhereRightJoin *pRJ = pLevel->pRJ; -+ pRJ->iMatch = pParse->nTab++; -+ pRJ->regBloom = ++pParse->nMem; -+ sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom); -+ pRJ->regReturn = ++pParse->nMem; -+ sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn); -+ assert( pTab==pTabItem->pSTab ); -+ if( HasRowid(pTab) ){ -+ KeyInfo *pInfo; -+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1); -+ pInfo = sqlite3KeyInfoAlloc(pParse->db, 1, 0); -+ if( pInfo ){ -+ pInfo->aColl[0] = 0; -+ pInfo->aSortFlags[0] = 0; -+ sqlite3VdbeAppendP4(v, pInfo, P4_KEYINFO); -+ } -+ }else{ -+ Index *pPk = sqlite3PrimaryKeyIndex(pTab); -+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, pPk->nKeyCol); -+ sqlite3VdbeSetP4KeyInfo(pParse, pPk); -+ } -+ pLoop->wsFlags &= ~WHERE_IDX_ONLY; -+ /* The nature of RIGHT JOIN processing is such that it messes up -+ ** the output order. So omit any ORDER BY/GROUP BY elimination -+ ** optimizations. We need to do an actual sort for RIGHT JOIN. */ -+ pWInfo->nOBSat = 0; -+ pWInfo->eDistinct = WHERE_DISTINCT_UNORDERED; -+ } - } - pWInfo->iTop = sqlite3VdbeCurrentAddr(v); - if( db->mallocFailed ) goto whereBeginError; -@@ -150835,15 +171718,36 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - for(ii=0; iinErr ) goto whereBeginError; - pLevel = &pWInfo->a[ii]; - wsFlags = pLevel->pWLoop->wsFlags; -+ pSrc = &pTabList->a[pLevel->iFrom]; -+ if( pSrc->fg.isMaterialized ){ -+ Subquery *pSubq; -+ int iOnce = 0; -+ assert( pSrc->fg.isSubquery ); -+ pSubq = pSrc->u4.pSubq; -+ if( pSrc->fg.isCorrelated==0 ){ -+ iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); -+ }else{ -+ iOnce = 0; -+ } -+ sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub); -+ VdbeComment((v, "materialize %!S", pSrc)); -+ if( iOnce ) sqlite3VdbeJumpHere(v, iOnce); -+ } -+ assert( pTabList == pWInfo->pTabList ); -+ if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ -+ if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ - #ifndef SQLITE_OMIT_AUTOMATIC_INDEX -- if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){ -- constructAutomaticIndex(pParse, &pWInfo->sWC, -- &pTabList->a[pLevel->iFrom], notReady, pLevel); -+ constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel); -+#endif -+ }else{ -+ sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); -+ } - if( db->mallocFailed ) goto whereBeginError; - } --#endif - addrExplain = sqlite3WhereExplainOneScan( - pParse, pTabList, pLevel, wctrlFlags - ); -@@ -150857,6 +171761,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( - - /* Done. */ - VdbeModuleComment((v, "Begin WHERE-core")); -+ pWInfo->iEndWhere = sqlite3VdbeCurrentAddr(v); - return pWInfo; - - /* Jump here if malloc fails */ -@@ -150865,6 +171770,11 @@ whereBeginError: - pParse->nQueryLoop = pWInfo->savedNQueryLoop; - whereInfoFree(db, pWInfo); - } -+#ifdef WHERETRACE_ENABLED -+ /* Prevent harmless compiler warnings about debugging routines -+ ** being declared but never used */ -+ sqlite3ShowWhereLoopList(0); -+#endif /* WHERETRACE_ENABLED */ - return 0; - } - -@@ -150885,6 +171795,7 @@ whereBeginError: - ){ - if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; - sqlite3VdbePrintOp(0, pc, pOp); -+ sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */ - } - #endif - -@@ -150900,6 +171811,8 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - WhereLoop *pLoop; - SrcList *pTabList = pWInfo->pTabList; - sqlite3 *db = pParse->db; -+ int iEnd = sqlite3VdbeCurrentAddr(v); -+ int nRJ = 0; - - /* Generate loop termination code. - */ -@@ -150907,6 +171820,17 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - for(i=pWInfo->nLevel-1; i>=0; i--){ - int addr; - pLevel = &pWInfo->a[i]; -+ if( pLevel->pRJ ){ -+ /* Terminate the subroutine that forms the interior of the loop of -+ ** the RIGHT JOIN table */ -+ WhereRightJoin *pRJ = pLevel->pRJ; -+ sqlite3VdbeResolveLabel(v, pLevel->addrCont); -+ pLevel->addrCont = 0; -+ pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v); -+ sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1); -+ VdbeCoverage(v); -+ nRJ++; -+ } - pLoop = pLevel->pWLoop; - if( pLevel->op!=OP_Noop ){ - #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT -@@ -150934,7 +171858,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - } - #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ - /* The common case: Advance to the next row */ -- sqlite3VdbeResolveLabel(v, pLevel->addrCont); -+ if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont); - sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); - sqlite3VdbeChangeP5(v, pLevel->p5); - VdbeCoverage(v); -@@ -150949,18 +171873,22 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT - if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek); - #endif -- }else{ -+ }else if( pLevel->addrCont ){ - sqlite3VdbeResolveLabel(v, pLevel->addrCont); - } -- if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ -+ if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){ - struct InLoop *pIn; - int j; - sqlite3VdbeResolveLabel(v, pLevel->addrNxt); - for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ -+ assert( sqlite3VdbeGetOp(v, pIn->addrInTop+1)->opcode==OP_IsNull -+ || pParse->db->mallocFailed ); - sqlite3VdbeJumpHere(v, pIn->addrInTop+1); - if( pIn->eEndLoopOp!=OP_Noop ){ - if( pIn->nPrefix ){ -- assert( pLoop->wsFlags & WHERE_IN_EARLYOUT ); -+ int bEarlyOut = -+ (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 -+ && (pLoop->wsFlags & WHERE_IN_EARLYOUT)!=0; - if( pLevel->iLeftJoin ){ - /* For LEFT JOIN queries, cursor pIn->iCur may not have been - ** opened yet. This occurs for WHERE clauses such as -@@ -150971,16 +171899,19 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - ** jump over the OP_Next or OP_Prev instruction about to - ** be coded. */ - sqlite3VdbeAddOp2(v, OP_IfNotOpen, pIn->iCur, -- sqlite3VdbeCurrentAddr(v) + 2 + -- ((pLoop->wsFlags & WHERE_VIRTUALTABLE)==0) -- ); -+ sqlite3VdbeCurrentAddr(v) + 2 + bEarlyOut); - VdbeCoverage(v); - } -- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){ -+ if( bEarlyOut ){ - sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur, - sqlite3VdbeCurrentAddr(v)+2, - pIn->iBase, pIn->nPrefix); - VdbeCoverage(v); -+ /* Retarget the OP_IsNull against the left operand of IN so -+ ** it jumps past the OP_IfNoHope. This is because the -+ ** OP_IsNull also bypasses the OP_Affinity opcode that is -+ ** required by OP_IfNoHope. */ -+ sqlite3VdbeJumpHere(v, pIn->addrInTop+1); - } - } - sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); -@@ -150992,6 +171923,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - } - } - sqlite3VdbeResolveLabel(v, pLevel->addrBrk); -+ if( pLevel->pRJ ){ -+ sqlite3VdbeAddOp3(v, OP_Return, pLevel->pRJ->regReturn, 0, 1); -+ VdbeCoverage(v); -+ } - if( pLevel->addrSkip ){ - sqlite3VdbeGoto(v, pLevel->addrSkip); - VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName)); -@@ -151010,12 +171945,27 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); - assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); - if( (ws & WHERE_IDX_ONLY)==0 ){ -- assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor ); -+ SrcItem *pSrc = &pTabList->a[pLevel->iFrom]; -+ assert( pLevel->iTabCur==pSrc->iCursor ); -+ if( pSrc->fg.viaCoroutine ){ -+ int m, n; -+ assert( pSrc->fg.isSubquery ); -+ n = pSrc->u4.pSubq->regResult; -+ assert( pSrc->pSTab!=0 ); -+ m = pSrc->pSTab->nCol; -+ sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); -+ } - sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); - } - if( (ws & WHERE_INDEXED) -- || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx) -+ || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) - ){ -+ if( ws & WHERE_MULTI_OR ){ -+ Index *pIx = pLevel->u.pCoveringIdx; -+ int iDb = sqlite3SchemaToIndex(db, pIx->pSchema); -+ sqlite3VdbeAddOp3(v, OP_ReopenIdx, pLevel->iIdxCur, pIx->tnum, iDb); -+ sqlite3VdbeSetP4KeyInfo(pParse, pIx); -+ } - sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); - } - if( pLevel->op==OP_Return ){ -@@ -151026,58 +171976,41 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - sqlite3VdbeJumpHere(v, addr); - } - VdbeModuleComment((v, "End WHERE-loop%d: %s", i, -- pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); -+ pWInfo->pTabList->a[pLevel->iFrom].pSTab->zName)); - } - -- /* The "break" point is here, just past the end of the outer loop. -- ** Set it. -- */ -- sqlite3VdbeResolveLabel(v, pWInfo->iBreak); -- - assert( pWInfo->nLevel<=pTabList->nSrc ); - for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ - int k, last; -- VdbeOp *pOp; -+ VdbeOp *pOp, *pLastOp; - Index *pIdx = 0; -- struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; -- Table *pTab = pTabItem->pTab; -+ SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; -+ Table *pTab = pTabItem->pSTab; - assert( pTab!=0 ); - pLoop = pLevel->pWLoop; - -+ /* Do RIGHT JOIN processing. Generate code that will output the -+ ** unmatched rows of the right operand of the RIGHT JOIN with -+ ** all of the columns of the left operand set to NULL. -+ */ -+ if( pLevel->pRJ ){ -+ sqlite3WhereRightJoinLoop(pWInfo, i, pLevel); -+ continue; -+ } -+ - /* For a co-routine, change all OP_Column references to the table of - ** the co-routine into OP_Copy of result contained in a register. - ** OP_Rowid becomes OP_Null. - */ - if( pTabItem->fg.viaCoroutine ){ - testcase( pParse->db->mallocFailed ); -+ assert( pTabItem->fg.isSubquery ); -+ assert( pTabItem->u4.pSubq->regResult>=0 ); - translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, -- pTabItem->regResult, 0); -+ pTabItem->u4.pSubq->regResult, 0); - continue; - } - --#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE -- /* Close all of the cursors that were opened by sqlite3WhereBegin. -- ** Except, do not close cursors that will be reused by the OR optimization -- ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors -- ** created for the ONEPASS optimization. -- */ -- if( (pTab->tabFlags & TF_Ephemeral)==0 -- && pTab->pSelect==0 -- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 -- ){ -- int ws = pLoop->wsFlags; -- if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){ -- sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); -- } -- if( (ws & WHERE_INDEXED)!=0 -- && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 -- && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] -- ){ -- sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); -- } -- } --#endif -- - /* If this scan uses an index, make VDBE code substitutions to read data - ** from the index instead of from the table where possible. In some cases - ** this optimization prevents the table from ever being read, which can -@@ -151092,29 +172025,63 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){ - pIdx = pLoop->u.btree.pIndex; - }else if( pLoop->wsFlags & WHERE_MULTI_OR ){ -- pIdx = pLevel->u.pCovidx; -+ pIdx = pLevel->u.pCoveringIdx; - } - if( pIdx -- && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable)) - && !db->mallocFailed - ){ -- last = sqlite3VdbeCurrentAddr(v); -- k = pLevel->addrBody; -+ if( pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable) ){ -+ last = iEnd; -+ }else{ -+ last = pWInfo->iEndWhere; -+ } -+ if( pIdx->bHasExpr ){ -+ IndexedExpr *p = pParse->pIdxEpr; -+ while( p ){ -+ if( p->iIdxCur==pLevel->iIdxCur ){ -+#ifdef WHERETRACE_ENABLED -+ if( sqlite3WhereTrace & 0x200 ){ -+ sqlite3DebugPrintf("Disable pParse->pIdxEpr term {%d,%d}\n", -+ p->iIdxCur, p->iIdxCol); -+ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(p->pExpr); -+ } -+#endif -+ p->iDataCur = -1; -+ p->iIdxCur = -1; -+ } -+ p = p->pIENext; -+ } -+ } -+ k = pLevel->addrBody + 1; - #ifdef SQLITE_DEBUG - if( db->flags & SQLITE_VdbeAddopTrace ){ -- printf("TRANSLATE opcodes in range %d..%d\n", k, last-1); -+ printf("TRANSLATE cursor %d->%d in opcode range %d..%d\n", -+ pLevel->iTabCur, pLevel->iIdxCur, k, last-1); - } -+ /* Proof that the "+1" on the k value above is safe */ -+ pOp = sqlite3VdbeGetOp(v, k - 1); -+ assert( pOp->opcode!=OP_Column || pOp->p1!=pLevel->iTabCur ); -+ assert( pOp->opcode!=OP_Rowid || pOp->p1!=pLevel->iTabCur ); -+ assert( pOp->opcode!=OP_IfNullRow || pOp->p1!=pLevel->iTabCur ); - #endif - pOp = sqlite3VdbeGetOp(v, k); -- for(; kp1!=pLevel->iTabCur ) continue; -- if( pOp->opcode==OP_Column -+ pLastOp = pOp + (last - k); -+ assert( pOp<=pLastOp ); -+ do{ -+ if( pOp->p1!=pLevel->iTabCur ){ -+ /* no-op */ -+ }else if( pOp->opcode==OP_Column - #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - || pOp->opcode==OP_Offset - #endif - ){ - int x = pOp->p2; - assert( pIdx->pTable==pTab ); -+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC -+ if( pOp->opcode==OP_Offset ){ -+ /* Do not need to translate the column number */ -+ }else -+#endif - if( !HasRowid(pTab) ){ - Index *pPk = sqlite3PrimaryKeyIndex(pTab); - x = pPk->aiColumn[x]; -@@ -151128,9 +172095,30 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - pOp->p2 = x; - pOp->p1 = pLevel->iIdxCur; - OpcodeRewriteTrace(db, k, pOp); -+ }else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ -+ if( pLoop->wsFlags & WHERE_IDX_ONLY ){ -+ /* An error. pLoop is supposed to be a covering index loop, -+ ** and yet the VM code refers to a column of the table that -+ ** is not part of the index. */ -+ sqlite3ErrorMsg(pParse, "internal query planner error"); -+ pParse->rc = SQLITE_INTERNAL; -+ }else{ -+ /* The WHERE_EXPRIDX flag is set by the planner when it is likely -+ ** that pLoop is a covering index loop, but it is not possible -+ ** to be 100% sure. In this case, any OP_Explain opcode -+ ** corresponding to this loop describes the index as a "COVERING -+ ** INDEX". But, pOp proves that pLoop is not actually a covering -+ ** index loop. So clear the WHERE_EXPRIDX flag and rewrite the -+ ** text that accompanies the OP_Explain opcode, if any. */ -+ pLoop->wsFlags &= ~WHERE_EXPRIDX; -+ sqlite3WhereAddExplainText(pParse, -+ pLevel->addrBody-1, -+ pTabList, -+ pLevel, -+ pWInfo->wctrlFlags -+ ); -+ } - } -- assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 -- || pWInfo->eOnePass ); - }else if( pOp->opcode==OP_Rowid ){ - pOp->p1 = pLevel->iIdxCur; - pOp->opcode = OP_IdxRowid; -@@ -151139,25 +172127,26 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - pOp->p1 = pLevel->iIdxCur; - OpcodeRewriteTrace(db, k, pOp); - } -- } -+#ifdef SQLITE_DEBUG -+ k++; -+#endif -+ }while( (++pOp)flags & SQLITE_VdbeAddopTrace ) printf("TRANSLATE complete\n"); - #endif - } - } - -- /* Undo all Expr node modifications */ -- while( pWInfo->pExprMods ){ -- WhereExprMod *p = pWInfo->pExprMods; -- pWInfo->pExprMods = p->pNext; -- memcpy(p->pExpr, &p->orig, sizeof(p->orig)); -- sqlite3DbFree(db, p); -- } -+ /* The "break" point is here, just past the end of the outer loop. -+ ** Set it. -+ */ -+ sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - - /* Final cleanup - */ - pParse->nQueryLoop = pWInfo->savedNQueryLoop; - whereInfoFree(db, pWInfo); -+ pParse->withinRJSubrtn -= nRJ; - return; - } - -@@ -151282,7 +172271,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ - ** - ** These are the same built-in window functions supported by Postgres. - ** Although the behaviour of aggregate window functions (functions that --** can be used as either aggregates or window funtions) allows them to -+** can be used as either aggregates or window functions) allows them to - ** be implemented using an API, built-in window functions are much more - ** esoteric. Additionally, some window functions (e.g. nth_value()) - ** may only be implemented by caching the entire partition in memory. -@@ -151746,7 +172735,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ } - /* Window functions that use all window interfaces: xStep, xFinal, - ** xValue, and xInverse */ - #define WINDOWFUNCALL(name,nArg,extra) { \ -- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ -+ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ - name ## StepFunc, name ## FinalizeFunc, name ## ValueFunc, \ - name ## InvFunc, name ## Name, {0} \ - } -@@ -151754,7 +172743,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ } - /* Window functions that are implemented using bytecode and thus have - ** no-op routines for their methods */ - #define WINDOWFUNCNOOP(name,nArg,extra) { \ -- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ -+ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ - noopStepFunc, noopValueFunc, noopValueFunc, \ - noopStepFunc, name ## Name, {0} \ - } -@@ -151763,7 +172752,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ } - ** same routine for xFinalize and xValue and which never call - ** xInverse. */ - #define WINDOWFUNCX(name,nArg,extra) { \ -- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ -+ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \ - name ## StepFunc, name ## ValueFunc, name ## ValueFunc, \ - noopStepFunc, name ## Name, {0} \ - } -@@ -151812,7 +172801,7 @@ static Window *windowFind(Parse *pParse, Window *pList, const char *zName){ - ** is the Window object representing the associated OVER clause. This - ** function updates the contents of pWin as follows: - ** --** * If the OVER clause refered to a named window (as in "max(x) OVER win"), -+** * If the OVER clause referred to a named window (as in "max(x) OVER win"), - ** search list pList for a matching WINDOW definition, and update pWin - ** accordingly. If no such WINDOW clause can be found, leave an error - ** in pParse. -@@ -151889,7 +172878,7 @@ SQLITE_PRIVATE void sqlite3WindowUpdate( - } - } - } -- pWin->pFunc = pFunc; -+ pWin->pWFunc = pFunc; - } - - /* -@@ -151950,9 +172939,11 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ - } - /* no break */ deliberate_fall_through - -+ case TK_IF_NULL_ROW: - case TK_AGG_FUNCTION: - case TK_COLUMN: { - int iCol = -1; -+ if( pParse->db->mallocFailed ) return WRC_Abort; - if( p->pSub ){ - int i; - for(i=0; ipSub->nExpr; i++){ -@@ -152062,22 +173053,24 @@ static ExprList *exprListAppendList( - int i; - int nInit = pList ? pList->nExpr : 0; - for(i=0; inExpr; i++){ -- Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); -- assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); -- if( bIntToNull && pDup ){ -+ sqlite3 *db = pParse->db; -+ Expr *pDup = sqlite3ExprDup(db, pAppend->a[i].pExpr, 0); -+ if( db->mallocFailed ){ -+ sqlite3ExprDelete(db, pDup); -+ break; -+ } -+ if( bIntToNull ){ - int iDummy; - Expr *pSub; -- for(pSub=pDup; ExprHasProperty(pSub, EP_Skip); pSub=pSub->pLeft){ -- assert( pSub ); -- } -- if( sqlite3ExprIsInteger(pSub, &iDummy) ){ -+ pSub = sqlite3ExprSkipCollateAndLikely(pDup); -+ if( sqlite3ExprIsInteger(pSub, &iDummy, 0) ){ - pSub->op = TK_NULL; - pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); - pSub->u.zToken = 0; - } - } - pList = sqlite3ExprListAppend(pParse, pList, pDup); -- if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; -+ if( pList ) pList->a[nInit+i].fg.sortFlags = pAppend->a[i].fg.sortFlags; - } - } - return pList; -@@ -152100,6 +173093,15 @@ static int sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){ - return WRC_Continue; - } - -+static int disallowAggregatesInOrderByCb(Walker *pWalker, Expr *pExpr){ -+ if( pExpr->op==TK_AGG_FUNCTION && pExpr->pAggInfo==0 ){ -+ assert( !ExprHasProperty(pExpr, EP_IntValue) ); -+ sqlite3ErrorMsg(pWalker->pParse, -+ "misuse of aggregate: %s()", pExpr->u.zToken); -+ } -+ return WRC_Continue; -+} -+ - /* - ** If the SELECT statement passed as the second argument does not invoke - ** any SQL window functions, this function is a no-op. Otherwise, it -@@ -152109,7 +173111,11 @@ static int sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){ - */ - SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ - int rc = SQLITE_OK; -- if( p->pWin && p->pPrior==0 && (p->selFlags & SF_WinRewrite)==0 ){ -+ if( p->pWin -+ && p->pPrior==0 -+ && ALWAYS((p->selFlags & SF_WinRewrite)==0) -+ && ALWAYS(!IN_RENAME_OBJECT) -+ ){ - Vdbe *v = sqlite3GetVdbe(pParse); - sqlite3 *db = pParse->db; - Select *pSub = 0; /* The subquery */ -@@ -152133,12 +173139,17 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ - } - sqlite3AggInfoPersistWalkerInit(&w, pParse); - sqlite3WalkSelect(&w, p); -+ if( (p->selFlags & SF_Aggregate)==0 ){ -+ w.xExprCallback = disallowAggregatesInOrderByCb; -+ w.xSelectCallback = 0; -+ sqlite3WalkExprList(&w, p->pOrderBy); -+ } - - p->pSrc = 0; - p->pWhere = 0; - p->pGroupBy = 0; - p->pHaving = 0; -- p->selFlags &= ~SF_Aggregate; -+ p->selFlags &= ~(u32)SF_Aggregate; - p->selFlags |= SF_WinRewrite; - - /* Create the ORDER BY clause for the sub-select. This is the concatenation -@@ -152177,8 +173188,11 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ - ** window function - one for the accumulator, another for interim - ** results. */ - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ -- ExprList *pArgs = pWin->pOwner->x.pList; -- if( pWin->pFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ -+ ExprList *pArgs; -+ assert( ExprUseXList(pWin->pOwner) ); -+ assert( pWin->pWFunc!=0 ); -+ pArgs = pWin->pOwner->x.pList; -+ if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){ - selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); - pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); - pWin->bExprArgs = 1; -@@ -152210,15 +173224,20 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ - pSub = sqlite3SelectNew( - pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0 - ); -- SELECTTRACE(1,pParse,pSub, -+ TREETRACE(0x40,pParse,pSub, - ("New window-function subquery in FROM clause of (%u/%p)\n", - p->selId, p)); - p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); -- if( p->pSrc ){ -+ assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside -+ ** of sqlite3DbMallocRawNN() called from -+ ** sqlite3SrcListAppend() */ -+ if( p->pSrc==0 ){ -+ sqlite3SelectDelete(db, pSub); -+ }else if( sqlite3SrcItemAttachSubquery(pParse, &p->pSrc->a[0], pSub, 0) ){ - Table *pTab2; -- p->pSrc->a[0].pSelect = pSub; -+ p->pSrc->a[0].fg.isCorrelated = 1; - sqlite3SrcListAssignCursors(pParse, p->pSrc); -- pSub->selFlags |= SF_Expanded; -+ pSub->selFlags |= SF_Expanded|SF_OrderByReqd; - pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE); - pSub->selFlags |= (selFlags & SF_Aggregate); - if( pTab2==0 ){ -@@ -152229,7 +173248,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ - }else{ - memcpy(pTab, pTab2, sizeof(Table)); - pTab->tabFlags |= TF_Ephemeral; -- p->pSrc->a[0].pTab = pTab; -+ p->pSrc->a[0].pSTab = pTab; - pTab = pTab2; - memset(&w, 0, sizeof(w)); - w.xExprCallback = sqlite3WindowExtraAggFuncDepth; -@@ -152237,19 +173256,16 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ - w.xSelectCallback2 = sqlite3WalkerDepthDecrease; - sqlite3WalkSelect(&w, pSub); - } -- }else{ -- sqlite3SelectDelete(db, pSub); - } - if( db->mallocFailed ) rc = SQLITE_NOMEM; -- sqlite3DbFree(db, pTab); -- } - -- if( rc ){ -- if( pParse->nErr==0 ){ -- assert( pParse->db->mallocFailed ); -- sqlite3ErrorToParser(pParse->db, SQLITE_NOMEM); -- } -+ /* Defer deleting the temporary table pTab because if an error occurred, -+ ** there could still be references to that table embedded in the -+ ** result-set or ORDER BY clause of the SELECT statement p. */ -+ sqlite3ParserAddCleanup(pParse, sqlite3DbFree, pTab); - } -+ -+ assert( rc==SQLITE_OK || pParse->nErr!=0 ); - return rc; - } - -@@ -152301,7 +173317,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){ - ** variable values in the expression tree. - */ - static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ -- if( 0==sqlite3ExprIsConstant(pExpr) ){ -+ if( 0==sqlite3ExprIsConstant(0,pExpr) ){ - if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr); - sqlite3ExprDelete(pParse->db, pExpr); - pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0); -@@ -152405,7 +173421,7 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble( - } - - /* --** Window *pWin has just been created from a WINDOW clause. Tokne pBase -+** Window *pWin has just been created from a WINDOW clause. Token pBase - ** is the base window. Earlier windows from the same WINDOW clause are - ** stored in the linked list starting at pWin->pNextWin. This function - ** either updates *pWin according to the base specification, or else -@@ -152449,8 +173465,9 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ - if( p ){ - assert( p->op==TK_FUNCTION ); - assert( pWin ); -+ assert( ExprIsFullSize(p) ); - p->y.pWin = pWin; -- ExprSetProperty(p, EP_WinFunc); -+ ExprSetProperty(p, EP_WinFunc|EP_FullSize); - pWin->pOwner = p; - if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){ - sqlite3ErrorMsg(pParse, -@@ -152469,15 +173486,19 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ - ** SELECT, or (b) the windows already linked use a compatible window frame. - */ - SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){ -- if( pSel!=0 -- && (0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0)) -- ){ -- pWin->pNextWin = pSel->pWin; -- if( pSel->pWin ){ -- pSel->pWin->ppThis = &pWin->pNextWin; -+ if( pSel ){ -+ if( 0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){ -+ pWin->pNextWin = pSel->pWin; -+ if( pSel->pWin ){ -+ pSel->pWin->ppThis = &pWin->pNextWin; -+ } -+ pSel->pWin = pWin; -+ pWin->ppThis = &pSel->pWin; -+ }else{ -+ if( sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){ -+ pSel->selFlags |= SF_MultiPart; -+ } - } -- pSel->pWin = pWin; -- pWin->ppThis = &pSel->pWin; - } - } - -@@ -152486,7 +173507,12 @@ SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){ - ** different, or 2 if it cannot be determined if the objects are identical - ** or not. Identical window objects can be processed in a single scan. - */ --SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, int bFilter){ -+SQLITE_PRIVATE int sqlite3WindowCompare( -+ const Parse *pParse, -+ const Window *p1, -+ const Window *p2, -+ int bFilter -+){ - int res; - if( NEVER(p1==0) || NEVER(p2==0) ) return 1; - if( p1->eFrmType!=p2->eFrmType ) return 1; -@@ -152516,10 +173542,15 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, i - ** and initialize registers and cursors used by sqlite3WindowCodeStep(). - */ - SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ -- int nEphExpr = pSelect->pSrc->a[0].pSelect->pEList->nExpr; -- Window *pMWin = pSelect->pWin; - Window *pWin; -- Vdbe *v = sqlite3GetVdbe(pParse); -+ int nEphExpr; -+ Window *pMWin; -+ Vdbe *v; -+ -+ assert( pSelect->pSrc->a[0].fg.isSubquery ); -+ nEphExpr = pSelect->pSrc->a[0].u4.pSubq->pSelect->pEList->nExpr; -+ pMWin = pSelect->pWin; -+ v = sqlite3GetVdbe(pParse); - - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, nEphExpr); - sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr); -@@ -152549,7 +173580,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ - } - - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ -- FuncDef *p = pWin->pFunc; -+ FuncDef *p = pWin->pWFunc; - if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){ - /* The inline versions of min() and max() require a single ephemeral - ** table and 3 registers. The registers are used as follows: -@@ -152558,12 +173589,15 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ - ** regApp+1: integer value used to ensure keys are unique - ** regApp+2: output of MakeRecord - */ -- ExprList *pList = pWin->pOwner->x.pList; -- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); -+ ExprList *pList; -+ KeyInfo *pKeyInfo; -+ assert( ExprUseXList(pWin->pOwner) ); -+ pList = pWin->pOwner->x.pList; -+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0); - pWin->csrApp = pParse->nTab++; - pWin->regApp = pParse->nMem+1; - pParse->nMem += 3; -- if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){ -+ if( pKeyInfo && pWin->pWFunc->zName[1]=='i' ){ - assert( pKeyInfo->aSortFlags[0]==0 ); - pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC; - } -@@ -152630,6 +173664,7 @@ static void windowCheckValue(Parse *pParse, int reg, int eCond){ - VdbeCoverageIf(v, eCond==2); - } - sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg); -+ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC); - VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */ - VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */ - VdbeCoverageNeverNullIf(v, eCond==2); -@@ -152646,7 +173681,9 @@ static void windowCheckValue(Parse *pParse, int reg, int eCond){ - ** with the object passed as the only argument to this function. - */ - static int windowArgCount(Window *pWin){ -- ExprList *pList = pWin->pOwner->x.pList; -+ const ExprList *pList; -+ assert( ExprUseXList(pWin->pOwner) ); -+ pList = pWin->pOwner->x.pList; - return (pList ? pList->nExpr : 0); - } - -@@ -152696,7 +173733,7 @@ struct WindowCsrAndReg { - ** - ** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING) - ** --** The windows functions implmentation caches the input rows in a temp -+** The windows functions implementation caches the input rows in a temp - ** table, sorted by "a, b" (it actually populates the cache lazily, and - ** aggressively removes rows once they are no longer required, but that's - ** a mere detail). It keeps three cursors open on the temp table. One -@@ -152724,6 +173761,7 @@ struct WindowCodeArg { - int regGosub; /* Register used with OP_Gosub(addrGosub) */ - int regArg; /* First in array of accumulator registers */ - int eDelete; /* See above */ -+ int regRowid; - - WindowCsrAndReg start; - WindowCsrAndReg current; -@@ -152782,10 +173820,11 @@ static void windowAggStep( - Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ -- FuncDef *pFunc = pWin->pFunc; -+ FuncDef *pFunc = pWin->pWFunc; - int regArg; - int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); - int i; -+ int addrIf = 0; - - assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED ); - -@@ -152802,6 +173841,18 @@ static void windowAggStep( - } - regArg = reg; - -+ if( pWin->pFilter ){ -+ int regTmp; -+ assert( ExprUseXList(pWin->pOwner) ); -+ assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); -+ assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); -+ regTmp = sqlite3GetTempReg(pParse); -+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); -+ addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); -+ VdbeCoverage(v); -+ sqlite3ReleaseTempReg(pParse, regTmp); -+ } -+ - if( pMWin->regStartRowid==0 - && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) - && (pWin->eStart!=TK_UNBOUNDED) -@@ -152821,35 +173872,25 @@ static void windowAggStep( - } - sqlite3VdbeJumpHere(v, addrIsNull); - }else if( pWin->regApp ){ -+ assert( pWin->pFilter==0 ); - assert( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ); - assert( bInverse==0 || bInverse==1 ); - sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); - }else if( pFunc->xSFunc!=noopStepFunc ){ -- int addrIf = 0; -- if( pWin->pFilter ){ -- int regTmp; -- assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); -- assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); -- regTmp = sqlite3GetTempReg(pParse); -- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); -- addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); -- VdbeCoverage(v); -- sqlite3ReleaseTempReg(pParse, regTmp); -- } -- - if( pWin->bExprArgs ){ -- int iStart = sqlite3VdbeCurrentAddr(v); -- VdbeOp *pOp, *pEnd; -+ int iOp = sqlite3VdbeCurrentAddr(v); -+ int iEnd; - -+ assert( ExprUseXList(pWin->pOwner) ); - nArg = pWin->pOwner->x.pList->nExpr; - regArg = sqlite3GetTempRange(pParse, nArg); - sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0); - -- pEnd = sqlite3VdbeGetOp(v, -1); -- for(pOp=sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){ -- if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){ -+ for(iEnd=sqlite3VdbeCurrentAddr(v); iOpopcode==OP_Column && pOp->p1==pMWin->iEphCsr ){ - pOp->p1 = csr; - } - } -@@ -152857,18 +173898,20 @@ static void windowAggStep( - if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ - CollSeq *pColl; - assert( nArg>0 ); -+ assert( ExprUseXList(pWin->pOwner) ); - pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); - sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); - } - sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, - bInverse, regArg, pWin->regAccum); - sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); -- sqlite3VdbeChangeP5(v, (u8)nArg); -+ sqlite3VdbeChangeP5(v, (u16)nArg); - if( pWin->bExprArgs ){ - sqlite3ReleaseTempRange(pParse, regArg, nArg); - } -- if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); - } -+ -+ if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); - } - } - -@@ -152893,7 +173936,7 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ - - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - if( pMWin->regStartRowid==0 -- && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) -+ && (pWin->pWFunc->funcFlags & SQLITE_FUNC_MINMAX) - && (pWin->eStart!=TK_UNBOUNDED) - ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); -@@ -152907,12 +173950,12 @@ static void windowAggFinal(WindowCodeArg *p, int bFin){ - int nArg = windowArgCount(pWin); - if( bFin ){ - sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg); -- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); -+ sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); - sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); - sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); - }else{ - sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult); -- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); -+ sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF); - } - } - } -@@ -153041,7 +174084,8 @@ static void windowReturnOneRow(WindowCodeArg *p){ - Window *pWin; - - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ -- FuncDef *pFunc = pWin->pFunc; -+ FuncDef *pFunc = pWin->pWFunc; -+ assert( ExprUseXList(pWin->pOwner) ); - if( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ){ -@@ -153112,7 +174156,7 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ - int nArg = 0; - Window *pWin; - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ -- FuncDef *pFunc = pWin->pFunc; -+ FuncDef *pFunc = pWin->pWFunc; - assert( pWin->regAccum ); - sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); - nArg = MAX(nArg, windowArgCount(pWin)); -@@ -153142,7 +174186,7 @@ static int windowCacheFrame(Window *pMWin){ - Window *pWin; - if( pMWin->regStartRowid ) return 1; - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ -- FuncDef *pFunc = pWin->pFunc; -+ FuncDef *pFunc = pWin->pWFunc; - if( (pFunc->zName==nth_valueName) - || (pFunc->zName==first_valueName) - || (pFunc->zName==leadName) -@@ -153207,7 +174251,7 @@ static void windowIfNewPeer( - ** if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl; - ** - ** A special type of arithmetic is used such that if csr1.peerVal is not --** a numeric type (real or integer), then the result of the addition addition -+** a numeric type (real or integer), then the result of the addition - ** or subtraction is a a copy of csr1.peerVal. - */ - static void windowCodeRangeTest( -@@ -153226,10 +174270,16 @@ static void windowCodeRangeTest( - int regString = ++pParse->nMem; /* Reg. for constant value '' */ - int arith = OP_Add; /* OP_Add or OP_Subtract */ - int addrGe; /* Jump destination */ -+ int addrDone = sqlite3VdbeMakeLabel(pParse); /* Address past OP_Ge */ -+ CollSeq *pColl; -+ -+ /* Read the peer-value from each cursor into a register */ -+ windowReadPeerValues(p, csr1, reg1); -+ windowReadPeerValues(p, csr2, reg2); - - assert( op==OP_Ge || op==OP_Gt || op==OP_Le ); - assert( pOrderBy && pOrderBy->nExpr==1 ); -- if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){ -+ if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_DESC ){ - switch( op ){ - case OP_Ge: op = OP_Le; break; - case OP_Gt: op = OP_Lt; break; -@@ -153238,34 +174288,11 @@ static void windowCodeRangeTest( - arith = OP_Subtract; - } - -- /* Read the peer-value from each cursor into a register */ -- windowReadPeerValues(p, csr1, reg1); -- windowReadPeerValues(p, csr2, reg2); -- - VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl", - reg1, (arith==OP_Add ? "+" : "-"), regVal, - ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2 - )); - -- /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). -- ** This block adds (or subtracts for DESC) the numeric value in regVal -- ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), -- ** then leave reg1 as it is. In pseudo-code, this is implemented as: -- ** -- ** if( reg1>='' ) goto addrGe; -- ** reg1 = reg1 +/- regVal -- ** addrGe: -- ** -- ** Since all strings and blobs are greater-than-or-equal-to an empty string, -- ** the add/subtract is skipped for these, as required. If reg1 is a NULL, -- ** then the arithmetic is performed, but since adding or subtracting from -- ** NULL is always NULL anyway, this case is handled as required too. */ -- sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); -- addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); -- VdbeCoverage(v); -- sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); -- sqlite3VdbeJumpHere(v, addrGe); -- - /* If the BIGNULL flag is set for the ORDER BY, then it is required to - ** consider NULL values to be larger than all other values, instead of - ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this -@@ -153285,7 +174312,7 @@ static void windowCodeRangeTest( - ** Additionally, if either reg1 or reg2 are NULL but the jump to lbl is - ** not taken, control jumps over the comparison operator coded below this - ** block. */ -- if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_BIGNULL ){ -+ if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_BIGNULL ){ - /* This block runs if reg1 contains a NULL. */ - int addr = sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v); - switch( op ){ -@@ -153302,21 +174329,45 @@ static void windowCodeRangeTest( - break; - default: assert( op==OP_Lt ); /* no-op */ break; - } -- sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3); -+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone); - - /* This block runs if reg1 is not NULL, but reg2 is. */ - sqlite3VdbeJumpHere(v, addr); -- sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v); -- if( op==OP_Gt || op==OP_Ge ){ -- sqlite3VdbeChangeP2(v, -1, sqlite3VdbeCurrentAddr(v)+1); -- } -+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2, -+ (op==OP_Gt || op==OP_Ge) ? addrDone : lbl); -+ VdbeCoverage(v); -+ } -+ -+ /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1). -+ ** This block adds (or subtracts for DESC) the numeric value in regVal -+ ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob), -+ ** then leave reg1 as it is. In pseudo-code, this is implemented as: -+ ** -+ ** if( reg1>='' ) goto addrGe; -+ ** reg1 = reg1 +/- regVal -+ ** addrGe: -+ ** -+ ** Since all strings and blobs are greater-than-or-equal-to an empty string, -+ ** the add/subtract is skipped for these, as required. If reg1 is a NULL, -+ ** then the arithmetic is performed, but since adding or subtracting from -+ ** NULL is always NULL anyway, this case is handled as required too. */ -+ sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); -+ addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); -+ VdbeCoverage(v); -+ if( (op==OP_Ge && arith==OP_Add) || (op==OP_Le && arith==OP_Subtract) ){ -+ sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); - } -+ sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); -+ sqlite3VdbeJumpHere(v, addrGe); - - /* Compare registers reg2 and reg1, taking the jump if required. Note that - ** control skips over this test if the BIGNULL flag is set and either - ** reg1 or reg2 contain a NULL value. */ - sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); -+ pColl = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr); -+ sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ); - sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); -+ sqlite3VdbeResolveLabel(v, addrDone); - - assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); - testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); -@@ -153392,16 +174443,24 @@ static int windowCodeOp( - /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING) or - ** (RANGE BETWEEN b PRECEDING AND a PRECEDING) frame, ensure the - ** start cursor does not advance past the end cursor within the -- ** temporary table. It otherwise might, if (a>b). */ -+ ** temporary table. It otherwise might, if (a>b). Also ensure that, -+ ** if the input cursor is still finding new rows, that the end -+ ** cursor does not go past it to EOF. */ - if( pMWin->eStart==pMWin->eEnd && regCountdown -- && pMWin->eFrmType==TK_RANGE && op==WINDOW_AGGINVERSE -+ && pMWin->eFrmType==TK_RANGE - ){ - int regRowid1 = sqlite3GetTempReg(pParse); - int regRowid2 = sqlite3GetTempReg(pParse); -- sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); -- sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); -- sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); -- VdbeCoverage(v); -+ if( op==WINDOW_AGGINVERSE ){ -+ sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1); -+ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2); -+ sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1); -+ VdbeCoverage(v); -+ }else if( p->regRowid ){ -+ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid1); -+ sqlite3VdbeAddOp3(v, OP_Ge, p->regRowid, lblDone, regRowid1); -+ VdbeCoverageNeverNull(v); -+ } - sqlite3ReleaseTempReg(pParse, regRowid1); - sqlite3ReleaseTempReg(pParse, regRowid2); - assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ); -@@ -153484,7 +174543,7 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ - pNew->zName = sqlite3DbStrDup(db, p->zName); - pNew->zBase = sqlite3DbStrDup(db, p->zBase); - pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0); -- pNew->pFunc = p->pFunc; -+ pNew->pWFunc = p->pWFunc; - pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0); - pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0); - pNew->eFrmType = p->eFrmType; -@@ -153685,7 +174744,7 @@ static int windowExprGtZero(Parse *pParse, Expr *pExpr){ - ** - ** For the most part, the patterns above are adapted to support UNBOUNDED by - ** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and --** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING". -+** CURRENT ROW by assuming that it is equivalent to "0 PRECEDING/FOLLOWING". - ** This is optimized of course - branches that will never be taken and - ** conditions that are always true are omitted from the VM code. The only - ** exceptional case is: -@@ -153890,7 +174949,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - Vdbe *v = sqlite3GetVdbe(pParse); - int csrWrite; /* Cursor used to write to eph. table */ - int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ -- int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ -+ int nInput = p->pSrc->a[0].pSTab->nCol; /* Number of cols returned by sub */ - int iInput; /* To iterate through sub cols */ - int addrNe; /* Address of OP_Ne */ - int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */ -@@ -153898,7 +174957,6 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - int addrEmpty; /* Address of OP_Rewind in flush: */ - int regNew; /* Array of registers holding new input row */ - int regRecord; /* regNew array in record form */ -- int regRowid; /* Rowid for regRecord in eph table */ - int regNewPeer = 0; /* Peer values for new row (part of regNew) */ - int regPeer = 0; /* Peer values for current row */ - int regFlushPart = 0; /* Register for "Gosub flush_partition" */ -@@ -153965,12 +175023,12 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - } - - /* Allocate registers for the array of values from the sub-query, the -- ** samve values in record form, and the rowid used to insert said record -+ ** same values in record form, and the rowid used to insert said record - ** into the ephemeral table. */ - regNew = pParse->nMem+1; - pParse->nMem += nInput; - regRecord = ++pParse->nMem; -- regRowid = ++pParse->nMem; -+ s.regRowid = ++pParse->nMem; - - /* If the window frame contains an " PRECEDING" or " FOLLOWING" - ** clause, allocate registers to store the results of evaluating each -@@ -154026,9 +175084,9 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - } - - /* Insert the new row into the ephemeral table */ -- sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid); -- sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid); -- addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid); -+ sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, s.regRowid); -+ sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, s.regRowid); -+ addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, s.regRowid); - VdbeCoverageNeverNull(v); - - /* This block is run for the first row of each partition */ -@@ -154049,8 +175107,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound */ - VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */ - windowAggFinal(&s, 0); -- sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); -- VdbeCoverageNeverTaken(v); -+ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr); - windowReturnOneRow(&s); - sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); - sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); -@@ -154062,13 +175119,10 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - } - - if( pMWin->eStart!=TK_UNBOUNDED ){ -- sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1); -- VdbeCoverageNeverTaken(v); -+ sqlite3VdbeAddOp1(v, OP_Rewind, s.start.csr); - } -- sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); -- VdbeCoverageNeverTaken(v); -- sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1); -- VdbeCoverageNeverTaken(v); -+ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr); -+ sqlite3VdbeAddOp1(v, OP_Rewind, s.end.csr); - if( regPeer && pOrderBy ){ - sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1); - sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1); -@@ -154146,6 +175200,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - sqlite3VdbeJumpHere(v, addrGosubFlush); - } - -+ s.regRowid = 0; - addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); - VdbeCoverage(v); - if( pMWin->eEnd==TK_PRECEDING ){ -@@ -154208,8 +175263,11 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - - /************** End of window.c **********************************************/ - /************** Begin file parse.c *******************************************/ -+/* This file is automatically generated by Lemon from input grammar -+** source file "parse.y". -+*/ - /* --** 2000-05-29 -+** 2001-09-15 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: -@@ -154219,25 +175277,23 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( - ** May you share freely, never taking more than you give. - ** - ************************************************************************* --** Driver template for the LEMON parser generator. -+** This file contains SQLite's SQL parser. - ** --** The "lemon" program processes an LALR(1) input grammar file, then uses --** this template to construct a parser. The "lemon" program inserts text --** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the --** interstitial "-" characters) contained in this template is changed into --** the value of the %name directive from the grammar. Otherwise, the content --** of this template is copied straight through into the generate parser --** source file. --** --** The following is the concatenation of all %include directives from the --** input grammar file: -+** The canonical source code to this file ("parse.y") is a Lemon grammar -+** file that specifies the input grammar and actions to take while parsing. -+** That input file is processed by Lemon to generate a C-language -+** implementation of a parser for the given grammar. You might be reading -+** this comment as part of the translated C-code. Edits should be made -+** to the original parse.y sources. - */ --/* #include */ --/* #include */ --/************ Begin %include sections from the grammar ************************/ - - /* #include "sqliteInt.h" */ - -+/* -+** Verify that the pParse->isCreate field is set -+*/ -+#define ASSERT_IS_CREATE assert(pParse->isCreate) -+ - /* - ** Disable all error recovery processing in the parser push-down - ** automaton. -@@ -154287,6 +175343,13 @@ struct TrigEvent { int a; IdList * b; }; - - struct FrameBound { int eType; Expr *pExpr; }; - -+/* -+** Generate a syntax error -+*/ -+static void parserSyntaxError(Parse *pParse, Token *p){ -+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p); -+} -+ - /* - ** Disable lookaside memory allocation for objects that might be - ** shared across database connections. -@@ -154294,6 +175357,10 @@ struct FrameBound { int eType; Expr *pExpr; }; - static void disableLookaside(Parse *pParse){ - sqlite3 *db = pParse->db; - pParse->disableLookaside++; -+#ifdef SQLITE_DEBUG -+ pParse->isCreate = 1; -+#endif -+ memset(&pParse->u1.cr, 0, sizeof(pParse->u1.cr)); - DisableLookaside; - } - -@@ -154327,26 +175394,54 @@ static void updateDeleteLimitError( - static void parserDoubleLinkSelect(Parse *pParse, Select *p){ - assert( p!=0 ); - if( p->pPrior ){ -- Select *pNext = 0, *pLoop; -- int mxSelect, cnt = 0; -- for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ -+ Select *pNext = 0, *pLoop = p; -+ int mxSelect, cnt = 1; -+ while(1){ - pLoop->pNext = pNext; - pLoop->selFlags |= SF_Compound; -+ pNext = pLoop; -+ pLoop = pLoop->pPrior; -+ if( pLoop==0 ) break; -+ cnt++; -+ if( pLoop->pOrderBy || pLoop->pLimit ){ -+ sqlite3ErrorMsg(pParse,"%s clause should come after %s not before", -+ pLoop->pOrderBy!=0 ? "ORDER BY" : "LIMIT", -+ sqlite3SelectOpName(pNext->op)); -+ break; -+ } - } -- if( (p->selFlags & SF_MultiValue)==0 && -- (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 && -- cnt>mxSelect -+ if( (p->selFlags & (SF_MultiValue|SF_Values))==0 -+ && (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 -+ && cnt>mxSelect - ){ - sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); - } - } - } - -+ /* Attach a With object describing the WITH clause to a Select -+ ** object describing the query for which the WITH clause is a prefix. -+ */ -+ static Select *attachWithToSelect(Parse *pParse, Select *pSelect, With *pWith){ -+ if( pSelect ){ -+ pSelect->pWith = pWith; -+ parserDoubleLinkSelect(pParse, pSelect); -+ }else{ -+ sqlite3WithDelete(pParse->db, pWith); -+ } -+ return pSelect; -+ } - -- /* Construct a new Expr object from a single identifier. Use the -- ** new Expr to populate pOut. Set the span of pOut to be the identifier -- ** that created the expression. -+ /* Memory allocator for parser stack resizing. This is a thin wrapper around -+ ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate -+ ** testing. - */ -+ static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){ -+ return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); -+ } -+ -+ -+ /* Construct a new Expr object from a single token */ - static Expr *tokenExpr(Parse *pParse, int op, Token t){ - Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1); - if( p ){ -@@ -154355,17 +175450,18 @@ static void updateDeleteLimitError( - p->affExpr = 0; - p->flags = EP_Leaf; - ExprClearVVAProperties(p); -- p->iAgg = -1; -+ /* p->iAgg = -1; // Not required */ - p->pLeft = p->pRight = 0; -- p->x.pList = 0; - p->pAggInfo = 0; -- p->y.pTab = 0; -+ memset(&p->x, 0, sizeof(p->x)); -+ memset(&p->y, 0, sizeof(p->y)); - p->op2 = 0; - p->iTable = 0; - p->iColumn = 0; - p->u.zToken = (char*)&p[1]; - memcpy(p->u.zToken, t.z, t.n); - p->u.zToken[t.n] = 0; -+ p->w.iOfst = (int)(t.z - pParse->zTail); - if( sqlite3Isquote(p->u.zToken[0]) ){ - sqlite3DequoteExpr(p); - } -@@ -154418,11 +175514,197 @@ static void updateDeleteLimitError( - # error too many tokens in the grammar - #endif - /**************** End of %include directives **********************************/ --/* These constants specify the various numeric values for terminal symbols --** in a format understandable to "makeheaders". This section is blank unless --** "lemon" is run with the "-m" command-line option. --***************** Begin makeheaders token definitions *************************/ --/**************** End makeheaders token definitions ***************************/ -+/* These constants specify the various numeric values for terminal symbols. -+***************** Begin token definitions *************************************/ -+#ifndef TK_SEMI -+#define TK_SEMI 1 -+#define TK_EXPLAIN 2 -+#define TK_QUERY 3 -+#define TK_PLAN 4 -+#define TK_BEGIN 5 -+#define TK_TRANSACTION 6 -+#define TK_DEFERRED 7 -+#define TK_IMMEDIATE 8 -+#define TK_EXCLUSIVE 9 -+#define TK_COMMIT 10 -+#define TK_END 11 -+#define TK_ROLLBACK 12 -+#define TK_SAVEPOINT 13 -+#define TK_RELEASE 14 -+#define TK_TO 15 -+#define TK_TABLE 16 -+#define TK_CREATE 17 -+#define TK_IF 18 -+#define TK_NOT 19 -+#define TK_EXISTS 20 -+#define TK_TEMP 21 -+#define TK_LP 22 -+#define TK_RP 23 -+#define TK_AS 24 -+#define TK_COMMA 25 -+#define TK_WITHOUT 26 -+#define TK_ABORT 27 -+#define TK_ACTION 28 -+#define TK_AFTER 29 -+#define TK_ANALYZE 30 -+#define TK_ASC 31 -+#define TK_ATTACH 32 -+#define TK_BEFORE 33 -+#define TK_BY 34 -+#define TK_CASCADE 35 -+#define TK_CAST 36 -+#define TK_CONFLICT 37 -+#define TK_DATABASE 38 -+#define TK_DESC 39 -+#define TK_DETACH 40 -+#define TK_EACH 41 -+#define TK_FAIL 42 -+#define TK_OR 43 -+#define TK_AND 44 -+#define TK_IS 45 -+#define TK_ISNOT 46 -+#define TK_MATCH 47 -+#define TK_LIKE_KW 48 -+#define TK_BETWEEN 49 -+#define TK_IN 50 -+#define TK_ISNULL 51 -+#define TK_NOTNULL 52 -+#define TK_NE 53 -+#define TK_EQ 54 -+#define TK_GT 55 -+#define TK_LE 56 -+#define TK_LT 57 -+#define TK_GE 58 -+#define TK_ESCAPE 59 -+#define TK_ID 60 -+#define TK_COLUMNKW 61 -+#define TK_DO 62 -+#define TK_FOR 63 -+#define TK_IGNORE 64 -+#define TK_INITIALLY 65 -+#define TK_INSTEAD 66 -+#define TK_NO 67 -+#define TK_KEY 68 -+#define TK_OF 69 -+#define TK_OFFSET 70 -+#define TK_PRAGMA 71 -+#define TK_RAISE 72 -+#define TK_RECURSIVE 73 -+#define TK_REPLACE 74 -+#define TK_RESTRICT 75 -+#define TK_ROW 76 -+#define TK_ROWS 77 -+#define TK_TRIGGER 78 -+#define TK_VACUUM 79 -+#define TK_VIEW 80 -+#define TK_VIRTUAL 81 -+#define TK_WITH 82 -+#define TK_NULLS 83 -+#define TK_FIRST 84 -+#define TK_LAST 85 -+#define TK_CURRENT 86 -+#define TK_FOLLOWING 87 -+#define TK_PARTITION 88 -+#define TK_PRECEDING 89 -+#define TK_RANGE 90 -+#define TK_UNBOUNDED 91 -+#define TK_EXCLUDE 92 -+#define TK_GROUPS 93 -+#define TK_OTHERS 94 -+#define TK_TIES 95 -+#define TK_GENERATED 96 -+#define TK_ALWAYS 97 -+#define TK_MATERIALIZED 98 -+#define TK_REINDEX 99 -+#define TK_RENAME 100 -+#define TK_CTIME_KW 101 -+#define TK_ANY 102 -+#define TK_BITAND 103 -+#define TK_BITOR 104 -+#define TK_LSHIFT 105 -+#define TK_RSHIFT 106 -+#define TK_PLUS 107 -+#define TK_MINUS 108 -+#define TK_STAR 109 -+#define TK_SLASH 110 -+#define TK_REM 111 -+#define TK_CONCAT 112 -+#define TK_PTR 113 -+#define TK_COLLATE 114 -+#define TK_BITNOT 115 -+#define TK_ON 116 -+#define TK_INDEXED 117 -+#define TK_STRING 118 -+#define TK_JOIN_KW 119 -+#define TK_CONSTRAINT 120 -+#define TK_DEFAULT 121 -+#define TK_NULL 122 -+#define TK_PRIMARY 123 -+#define TK_UNIQUE 124 -+#define TK_CHECK 125 -+#define TK_REFERENCES 126 -+#define TK_AUTOINCR 127 -+#define TK_INSERT 128 -+#define TK_DELETE 129 -+#define TK_UPDATE 130 -+#define TK_SET 131 -+#define TK_DEFERRABLE 132 -+#define TK_FOREIGN 133 -+#define TK_DROP 134 -+#define TK_UNION 135 -+#define TK_ALL 136 -+#define TK_EXCEPT 137 -+#define TK_INTERSECT 138 -+#define TK_SELECT 139 -+#define TK_VALUES 140 -+#define TK_DISTINCT 141 -+#define TK_DOT 142 -+#define TK_FROM 143 -+#define TK_JOIN 144 -+#define TK_USING 145 -+#define TK_ORDER 146 -+#define TK_GROUP 147 -+#define TK_HAVING 148 -+#define TK_LIMIT 149 -+#define TK_WHERE 150 -+#define TK_RETURNING 151 -+#define TK_INTO 152 -+#define TK_NOTHING 153 -+#define TK_FLOAT 154 -+#define TK_BLOB 155 -+#define TK_INTEGER 156 -+#define TK_VARIABLE 157 -+#define TK_CASE 158 -+#define TK_WHEN 159 -+#define TK_THEN 160 -+#define TK_ELSE 161 -+#define TK_INDEX 162 -+#define TK_ALTER 163 -+#define TK_ADD 164 -+#define TK_WINDOW 165 -+#define TK_OVER 166 -+#define TK_FILTER 167 -+#define TK_COLUMN 168 -+#define TK_AGG_FUNCTION 169 -+#define TK_AGG_COLUMN 170 -+#define TK_TRUEFALSE 171 -+#define TK_FUNCTION 172 -+#define TK_UPLUS 173 -+#define TK_UMINUS 174 -+#define TK_TRUTH 175 -+#define TK_REGISTER 176 -+#define TK_VECTOR 177 -+#define TK_SELECT_COLUMN 178 -+#define TK_IF_NULL_ROW 179 -+#define TK_ASTERISK 180 -+#define TK_SPAN 181 -+#define TK_ERROR 182 -+#define TK_QNUMBER 183 -+#define TK_SPACE 184 -+#define TK_COMMENT 185 -+#define TK_ILLEGAL 186 -+#endif -+/**************** End token definitions ***************************************/ - - /* The next sections is a series of control #defines. - ** various aspects of the generated parser. -@@ -154461,6 +175743,9 @@ static void updateDeleteLimitError( - ** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser - ** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser - ** sqlite3ParserCTX_* As sqlite3ParserARG_ except for %extra_context -+** YYREALLOC Name of the realloc() function to use -+** YYFREE Name of the free() function to use -+** YYDYNSTACK True if stack space should be extended on heap - ** YYERRORSYMBOL is the code number of the error symbol. If not - ** defined, then do no error processing. - ** YYNSTATE the combined number of states. -@@ -154474,34 +175759,39 @@ static void updateDeleteLimitError( - ** YY_NO_ACTION The yy_action[] code for no-op - ** YY_MIN_REDUCE Minimum value for reduce actions - ** YY_MAX_REDUCE Maximum value for reduce actions -+** YY_MIN_DSTRCTR Minimum symbol value that has a destructor -+** YY_MAX_DSTRCTR Maximum symbol value that has a destructor - */ - #ifndef INTERFACE - # define INTERFACE 1 - #endif - /************* Begin control #defines *****************************************/ - #define YYCODETYPE unsigned short int --#define YYNOCODE 310 -+#define YYNOCODE 323 - #define YYACTIONTYPE unsigned short int --#define YYWILDCARD 100 -+#define YYWILDCARD 102 - #define sqlite3ParserTOKENTYPE Token - typedef union { - int yyinit; - sqlite3ParserTOKENTYPE yy0; -- SrcList* yy47; -- u8 yy58; -- struct FrameBound yy77; -- With* yy131; -- int yy192; -- Expr* yy202; -- struct {int value; int mask;} yy207; -- struct TrigEvent yy230; -- ExprList* yy242; -- Window* yy303; -- Upsert* yy318; -- const char* yy436; -- TriggerStep* yy447; -- Select* yy539; -- IdList* yy600; -+ u32 yy9; -+ struct TrigEvent yy28; -+ With* yy125; -+ IdList* yy204; -+ struct FrameBound yy205; -+ TriggerStep* yy319; -+ const char* yy342; -+ Cte* yy361; -+ ExprList* yy402; -+ Upsert* yy403; -+ OnOrUsing yy421; -+ u8 yy444; -+ struct {int value; int mask;} yy481; -+ Window* yy483; -+ int yy502; -+ SrcList* yy563; -+ Expr* yy590; -+ Select* yy637; - } YYMINORTYPE; - #ifndef YYSTACKDEPTH - #define YYSTACKDEPTH 100 -@@ -154511,24 +175801,29 @@ typedef union { - #define sqlite3ParserARG_PARAM - #define sqlite3ParserARG_FETCH - #define sqlite3ParserARG_STORE -+#define YYREALLOC parserStackRealloc -+#define YYFREE sqlite3_free -+#define YYDYNSTACK 1 - #define sqlite3ParserCTX_SDECL Parse *pParse; - #define sqlite3ParserCTX_PDECL ,Parse *pParse - #define sqlite3ParserCTX_PARAM ,pParse - #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; - #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; - #define YYFALLBACK 1 --#define YYNSTATE 553 --#define YYNRULE 385 --#define YYNRULE_WITH_ACTION 325 --#define YYNTOKEN 181 --#define YY_MAX_SHIFT 552 --#define YY_MIN_SHIFTREDUCE 803 --#define YY_MAX_SHIFTREDUCE 1187 --#define YY_ERROR_ACTION 1188 --#define YY_ACCEPT_ACTION 1189 --#define YY_NO_ACTION 1190 --#define YY_MIN_REDUCE 1191 --#define YY_MAX_REDUCE 1575 -+#define YYNSTATE 583 -+#define YYNRULE 409 -+#define YYNRULE_WITH_ACTION 344 -+#define YYNTOKEN 187 -+#define YY_MAX_SHIFT 582 -+#define YY_MIN_SHIFTREDUCE 845 -+#define YY_MAX_SHIFTREDUCE 1253 -+#define YY_ERROR_ACTION 1254 -+#define YY_ACCEPT_ACTION 1255 -+#define YY_NO_ACTION 1256 -+#define YY_MIN_REDUCE 1257 -+#define YY_MAX_REDUCE 1665 -+#define YY_MIN_DSTRCTR 206 -+#define YY_MAX_DSTRCTR 320 - /************* End control #defines *******************************************/ - #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) - -@@ -154544,6 +175839,22 @@ typedef union { - # define yytestcase(X) - #endif - -+/* Macro to determine if stack space has the ability to grow using -+** heap memory. -+*/ -+#if YYSTACKDEPTH<=0 || YYDYNSTACK -+# define YYGROWABLESTACK 1 -+#else -+# define YYGROWABLESTACK 0 -+#endif -+ -+/* Guarantee a minimum number of initial stack slots. -+*/ -+#if YYSTACKDEPTH<=0 -+# undef YYSTACKDEPTH -+# define YYSTACKDEPTH 2 /* Need a minimum stack size */ -+#endif -+ - - /* Next are the tables used to determine what action to take based on the - ** current state and lookahead token. These tables are used to implement -@@ -154595,586 +175906,643 @@ typedef union { - ** yy_default[] Default action for each state. - ** - *********** Begin parsing tables **********************************************/ --#define YY_ACTTAB_COUNT (1962) -+#define YY_ACTTAB_COUNT (2207) - static const YYACTIONTYPE yy_action[] = { -- /* 0 */ 546, 1222, 546, 451, 1260, 546, 1239, 546, 114, 111, -- /* 10 */ 211, 546, 1537, 546, 1260, 523, 114, 111, 211, 392, -- /* 20 */ 1232, 344, 42, 42, 42, 42, 1225, 42, 42, 71, -- /* 30 */ 71, 937, 1224, 71, 71, 71, 71, 1462, 1493, 938, -- /* 40 */ 820, 453, 6, 121, 122, 112, 1165, 1165, 1006, 1009, -- /* 50 */ 999, 999, 119, 119, 120, 120, 120, 120, 1543, 392, -- /* 60 */ 1358, 1517, 552, 2, 1193, 194, 528, 436, 143, 291, -- /* 70 */ 528, 136, 528, 371, 261, 504, 272, 385, 1273, 527, -- /* 80 */ 503, 493, 164, 121, 122, 112, 1165, 1165, 1006, 1009, -- /* 90 */ 999, 999, 119, 119, 120, 120, 120, 120, 1358, 442, -- /* 100 */ 1514, 118, 118, 118, 118, 117, 117, 116, 116, 116, -- /* 110 */ 115, 424, 266, 266, 266, 266, 1498, 358, 1500, 435, -- /* 120 */ 357, 1498, 517, 524, 1485, 543, 1114, 543, 1114, 392, -- /* 130 */ 405, 241, 208, 114, 111, 211, 98, 290, 537, 221, -- /* 140 */ 1029, 118, 118, 118, 118, 117, 117, 116, 116, 116, -- /* 150 */ 115, 424, 1142, 121, 122, 112, 1165, 1165, 1006, 1009, -- /* 160 */ 999, 999, 119, 119, 120, 120, 120, 120, 406, 428, -- /* 170 */ 117, 117, 116, 116, 116, 115, 424, 1418, 468, 123, -- /* 180 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, -- /* 190 */ 424, 116, 116, 116, 115, 424, 540, 540, 540, 392, -- /* 200 */ 505, 120, 120, 120, 120, 113, 1051, 1142, 1143, 1144, -- /* 210 */ 1051, 118, 118, 118, 118, 117, 117, 116, 116, 116, -- /* 220 */ 115, 424, 1461, 121, 122, 112, 1165, 1165, 1006, 1009, -- /* 230 */ 999, 999, 119, 119, 120, 120, 120, 120, 392, 444, -- /* 240 */ 316, 83, 463, 81, 359, 382, 1142, 80, 118, 118, -- /* 250 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 179, -- /* 260 */ 434, 424, 121, 122, 112, 1165, 1165, 1006, 1009, 999, -- /* 270 */ 999, 119, 119, 120, 120, 120, 120, 434, 433, 266, -- /* 280 */ 266, 118, 118, 118, 118, 117, 117, 116, 116, 116, -- /* 290 */ 115, 424, 543, 1109, 903, 506, 1142, 114, 111, 211, -- /* 300 */ 1431, 1142, 1143, 1144, 206, 491, 1109, 392, 449, 1109, -- /* 310 */ 545, 330, 120, 120, 120, 120, 298, 1431, 1433, 17, -- /* 320 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, -- /* 330 */ 424, 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, -- /* 340 */ 119, 119, 120, 120, 120, 120, 392, 1358, 434, 1142, -- /* 350 */ 482, 1142, 1143, 1144, 996, 996, 1007, 1010, 445, 118, -- /* 360 */ 118, 118, 118, 117, 117, 116, 116, 116, 115, 424, -- /* 370 */ 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, -- /* 380 */ 119, 120, 120, 120, 120, 1054, 1054, 465, 1431, 118, -- /* 390 */ 118, 118, 118, 117, 117, 116, 116, 116, 115, 424, -- /* 400 */ 1142, 451, 546, 1426, 1142, 1143, 1144, 233, 966, 1142, -- /* 410 */ 481, 478, 477, 171, 360, 392, 164, 407, 414, 842, -- /* 420 */ 476, 164, 185, 334, 71, 71, 1243, 1000, 118, 118, -- /* 430 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 121, -- /* 440 */ 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, 119, -- /* 450 */ 120, 120, 120, 120, 392, 1142, 1143, 1144, 835, 12, -- /* 460 */ 314, 509, 163, 356, 1142, 1143, 1144, 114, 111, 211, -- /* 470 */ 508, 290, 537, 546, 276, 180, 290, 537, 121, 122, -- /* 480 */ 112, 1165, 1165, 1006, 1009, 999, 999, 119, 119, 120, -- /* 490 */ 120, 120, 120, 345, 484, 71, 71, 118, 118, 118, -- /* 500 */ 118, 117, 117, 116, 116, 116, 115, 424, 1142, 209, -- /* 510 */ 411, 523, 1142, 1109, 1571, 378, 252, 269, 342, 487, -- /* 520 */ 337, 486, 238, 392, 513, 364, 1109, 1127, 333, 1109, -- /* 530 */ 191, 409, 286, 32, 457, 443, 118, 118, 118, 118, -- /* 540 */ 117, 117, 116, 116, 116, 115, 424, 121, 122, 112, -- /* 550 */ 1165, 1165, 1006, 1009, 999, 999, 119, 119, 120, 120, -- /* 560 */ 120, 120, 392, 1142, 1143, 1144, 987, 1142, 1143, 1144, -- /* 570 */ 1142, 233, 492, 1492, 481, 478, 477, 6, 163, 546, -- /* 580 */ 512, 546, 115, 424, 476, 5, 121, 122, 112, 1165, -- /* 590 */ 1165, 1006, 1009, 999, 999, 119, 119, 120, 120, 120, -- /* 600 */ 120, 13, 13, 13, 13, 118, 118, 118, 118, 117, -- /* 610 */ 117, 116, 116, 116, 115, 424, 403, 502, 408, 546, -- /* 620 */ 1486, 544, 1142, 892, 892, 1142, 1143, 1144, 1473, 1142, -- /* 630 */ 275, 392, 808, 809, 810, 971, 422, 422, 422, 16, -- /* 640 */ 16, 55, 55, 1242, 118, 118, 118, 118, 117, 117, -- /* 650 */ 116, 116, 116, 115, 424, 121, 122, 112, 1165, 1165, -- /* 660 */ 1006, 1009, 999, 999, 119, 119, 120, 120, 120, 120, -- /* 670 */ 392, 1189, 1, 1, 552, 2, 1193, 1142, 1143, 1144, -- /* 680 */ 194, 291, 898, 136, 1142, 1143, 1144, 897, 521, 1492, -- /* 690 */ 1273, 3, 380, 6, 121, 122, 112, 1165, 1165, 1006, -- /* 700 */ 1009, 999, 999, 119, 119, 120, 120, 120, 120, 858, -- /* 710 */ 546, 924, 546, 118, 118, 118, 118, 117, 117, 116, -- /* 720 */ 116, 116, 115, 424, 266, 266, 1092, 1569, 1142, 551, -- /* 730 */ 1569, 1193, 13, 13, 13, 13, 291, 543, 136, 392, -- /* 740 */ 485, 421, 420, 966, 344, 1273, 468, 410, 859, 279, -- /* 750 */ 140, 221, 118, 118, 118, 118, 117, 117, 116, 116, -- /* 760 */ 116, 115, 424, 121, 122, 112, 1165, 1165, 1006, 1009, -- /* 770 */ 999, 999, 119, 119, 120, 120, 120, 120, 546, 266, -- /* 780 */ 266, 428, 392, 1142, 1143, 1144, 1172, 830, 1172, 468, -- /* 790 */ 431, 145, 543, 1146, 401, 314, 439, 302, 838, 1490, -- /* 800 */ 71, 71, 412, 6, 1090, 473, 221, 100, 112, 1165, -- /* 810 */ 1165, 1006, 1009, 999, 999, 119, 119, 120, 120, 120, -- /* 820 */ 120, 118, 118, 118, 118, 117, 117, 116, 116, 116, -- /* 830 */ 115, 424, 237, 1425, 546, 451, 428, 287, 986, 546, -- /* 840 */ 236, 235, 234, 830, 97, 529, 429, 1265, 1265, 1146, -- /* 850 */ 494, 307, 430, 838, 977, 546, 71, 71, 976, 1241, -- /* 860 */ 546, 51, 51, 300, 118, 118, 118, 118, 117, 117, -- /* 870 */ 116, 116, 116, 115, 424, 194, 103, 70, 70, 266, -- /* 880 */ 266, 546, 71, 71, 266, 266, 30, 391, 344, 976, -- /* 890 */ 976, 978, 543, 528, 1109, 328, 392, 543, 495, 397, -- /* 900 */ 1470, 195, 530, 13, 13, 1358, 240, 1109, 277, 280, -- /* 910 */ 1109, 280, 304, 457, 306, 333, 392, 31, 188, 419, -- /* 920 */ 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, -- /* 930 */ 119, 120, 120, 120, 120, 142, 392, 365, 457, 986, -- /* 940 */ 121, 122, 112, 1165, 1165, 1006, 1009, 999, 999, 119, -- /* 950 */ 119, 120, 120, 120, 120, 977, 323, 1142, 326, 976, -- /* 960 */ 121, 110, 112, 1165, 1165, 1006, 1009, 999, 999, 119, -- /* 970 */ 119, 120, 120, 120, 120, 464, 377, 1185, 118, 118, -- /* 980 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 1142, -- /* 990 */ 976, 976, 978, 305, 9, 366, 244, 362, 118, 118, -- /* 1000 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 313, -- /* 1010 */ 546, 344, 1142, 1143, 1144, 299, 290, 537, 118, 118, -- /* 1020 */ 118, 118, 117, 117, 116, 116, 116, 115, 424, 1263, -- /* 1030 */ 1263, 1163, 13, 13, 278, 421, 420, 468, 392, 923, -- /* 1040 */ 260, 260, 289, 1169, 1142, 1143, 1144, 189, 1171, 266, -- /* 1050 */ 266, 468, 390, 543, 1186, 546, 1170, 263, 144, 489, -- /* 1060 */ 922, 546, 543, 122, 112, 1165, 1165, 1006, 1009, 999, -- /* 1070 */ 999, 119, 119, 120, 120, 120, 120, 71, 71, 1142, -- /* 1080 */ 1172, 1272, 1172, 13, 13, 898, 1070, 1163, 546, 468, -- /* 1090 */ 897, 107, 538, 1491, 4, 1268, 1109, 6, 525, 1049, -- /* 1100 */ 12, 1071, 1092, 1570, 312, 455, 1570, 520, 541, 1109, -- /* 1110 */ 56, 56, 1109, 1489, 423, 1358, 1072, 6, 345, 285, -- /* 1120 */ 118, 118, 118, 118, 117, 117, 116, 116, 116, 115, -- /* 1130 */ 424, 425, 1271, 321, 1142, 1143, 1144, 878, 266, 266, -- /* 1140 */ 1277, 107, 538, 535, 4, 1488, 293, 879, 1211, 6, -- /* 1150 */ 210, 543, 543, 164, 294, 496, 416, 204, 541, 267, -- /* 1160 */ 267, 1214, 398, 511, 499, 204, 266, 266, 396, 531, -- /* 1170 */ 8, 986, 543, 519, 546, 922, 458, 105, 105, 543, -- /* 1180 */ 1090, 425, 266, 266, 106, 417, 425, 548, 547, 266, -- /* 1190 */ 266, 976, 518, 535, 1373, 543, 15, 15, 266, 266, -- /* 1200 */ 456, 1120, 543, 266, 266, 1070, 1372, 515, 290, 537, -- /* 1210 */ 546, 543, 514, 97, 444, 316, 543, 546, 922, 125, -- /* 1220 */ 1071, 986, 976, 976, 978, 979, 27, 105, 105, 401, -- /* 1230 */ 343, 1511, 44, 44, 106, 1072, 425, 548, 547, 57, -- /* 1240 */ 57, 976, 343, 1511, 107, 538, 546, 4, 462, 401, -- /* 1250 */ 214, 1120, 459, 297, 377, 1091, 534, 1309, 546, 539, -- /* 1260 */ 398, 541, 290, 537, 104, 244, 102, 526, 58, 58, -- /* 1270 */ 546, 199, 976, 976, 978, 979, 27, 1516, 1131, 427, -- /* 1280 */ 59, 59, 270, 237, 425, 138, 95, 375, 375, 374, -- /* 1290 */ 255, 372, 60, 60, 817, 1180, 535, 546, 273, 546, -- /* 1300 */ 1163, 1308, 389, 388, 546, 438, 546, 215, 210, 296, -- /* 1310 */ 515, 849, 546, 265, 208, 516, 1476, 295, 274, 61, -- /* 1320 */ 61, 62, 62, 308, 986, 109, 45, 45, 46, 46, -- /* 1330 */ 105, 105, 1186, 922, 47, 47, 341, 106, 546, 425, -- /* 1340 */ 548, 547, 1542, 546, 976, 867, 340, 217, 546, 937, -- /* 1350 */ 397, 107, 538, 218, 4, 156, 1163, 938, 158, 546, -- /* 1360 */ 49, 49, 1162, 546, 268, 50, 50, 546, 541, 1450, -- /* 1370 */ 63, 63, 546, 1449, 216, 976, 976, 978, 979, 27, -- /* 1380 */ 446, 64, 64, 546, 460, 65, 65, 546, 318, 14, -- /* 1390 */ 14, 425, 1305, 546, 66, 66, 1087, 546, 141, 379, -- /* 1400 */ 38, 546, 963, 535, 322, 127, 127, 546, 393, 67, -- /* 1410 */ 67, 546, 325, 290, 537, 52, 52, 515, 546, 68, -- /* 1420 */ 68, 845, 514, 69, 69, 399, 165, 857, 856, 53, -- /* 1430 */ 53, 986, 311, 151, 151, 97, 432, 105, 105, 327, -- /* 1440 */ 152, 152, 526, 1048, 106, 1048, 425, 548, 547, 1131, -- /* 1450 */ 427, 976, 1032, 270, 968, 239, 329, 243, 375, 375, -- /* 1460 */ 374, 255, 372, 940, 941, 817, 1296, 546, 220, 546, -- /* 1470 */ 107, 538, 546, 4, 546, 1256, 199, 845, 215, 1036, -- /* 1480 */ 296, 1530, 976, 976, 978, 979, 27, 541, 295, 76, -- /* 1490 */ 76, 54, 54, 980, 72, 72, 128, 128, 864, 865, -- /* 1500 */ 107, 538, 546, 4, 1047, 546, 1047, 533, 469, 546, -- /* 1510 */ 425, 546, 450, 1240, 546, 243, 546, 541, 217, 546, -- /* 1520 */ 452, 197, 535, 243, 73, 73, 156, 129, 129, 158, -- /* 1530 */ 336, 130, 130, 126, 126, 1036, 150, 150, 149, 149, -- /* 1540 */ 425, 134, 134, 317, 474, 216, 97, 239, 331, 980, -- /* 1550 */ 986, 97, 535, 346, 347, 546, 105, 105, 902, 931, -- /* 1560 */ 546, 895, 243, 106, 109, 425, 548, 547, 546, 1505, -- /* 1570 */ 976, 828, 99, 538, 139, 4, 546, 133, 133, 393, -- /* 1580 */ 986, 1317, 131, 131, 290, 537, 105, 105, 1357, 541, -- /* 1590 */ 132, 132, 1292, 106, 1303, 425, 548, 547, 75, 75, -- /* 1600 */ 976, 976, 976, 978, 979, 27, 546, 432, 896, 1289, -- /* 1610 */ 532, 109, 425, 1363, 546, 1221, 1213, 1202, 258, 546, -- /* 1620 */ 349, 546, 1201, 11, 535, 1203, 1524, 351, 77, 77, -- /* 1630 */ 376, 976, 976, 978, 979, 27, 74, 74, 353, 213, -- /* 1640 */ 301, 43, 43, 48, 48, 437, 310, 201, 303, 1350, -- /* 1650 */ 315, 355, 986, 454, 479, 1239, 339, 192, 105, 105, -- /* 1660 */ 1422, 1421, 193, 536, 205, 106, 1527, 425, 548, 547, -- /* 1670 */ 1180, 167, 976, 270, 247, 1469, 1467, 1177, 375, 375, -- /* 1680 */ 374, 255, 372, 200, 369, 817, 400, 83, 79, 82, -- /* 1690 */ 1427, 448, 177, 95, 1342, 161, 169, 1339, 215, 440, -- /* 1700 */ 296, 172, 173, 976, 976, 978, 979, 27, 295, 174, -- /* 1710 */ 175, 441, 472, 223, 1347, 383, 35, 381, 36, 461, -- /* 1720 */ 88, 1353, 181, 447, 384, 1416, 227, 467, 259, 229, -- /* 1730 */ 186, 488, 470, 324, 1250, 230, 231, 320, 217, 1204, -- /* 1740 */ 1438, 1259, 386, 1258, 413, 90, 156, 849, 1541, 158, -- /* 1750 */ 206, 415, 1540, 507, 1300, 1257, 94, 348, 1229, 1301, -- /* 1760 */ 387, 1510, 1228, 338, 1227, 216, 350, 1539, 498, 283, -- /* 1770 */ 284, 1249, 501, 1299, 352, 245, 246, 418, 1298, 354, -- /* 1780 */ 1496, 1495, 124, 10, 526, 363, 101, 1324, 253, 96, -- /* 1790 */ 510, 1210, 34, 549, 1137, 254, 256, 257, 166, 393, -- /* 1800 */ 550, 1199, 1282, 361, 290, 537, 1281, 196, 367, 368, -- /* 1810 */ 1194, 153, 1454, 137, 281, 1323, 1455, 804, 154, 426, -- /* 1820 */ 198, 155, 1453, 1452, 292, 212, 202, 432, 1402, 203, -- /* 1830 */ 271, 135, 288, 78, 1046, 1044, 960, 168, 157, 881, -- /* 1840 */ 170, 219, 309, 222, 1060, 176, 964, 159, 402, 84, -- /* 1850 */ 178, 404, 85, 86, 87, 160, 1063, 224, 394, 395, -- /* 1860 */ 225, 1059, 146, 18, 226, 319, 243, 1174, 466, 228, -- /* 1870 */ 1052, 182, 183, 37, 819, 471, 340, 232, 332, 483, -- /* 1880 */ 184, 89, 162, 19, 20, 475, 91, 480, 847, 335, -- /* 1890 */ 147, 860, 282, 92, 490, 93, 1125, 148, 1012, 1095, -- /* 1900 */ 39, 497, 1096, 40, 500, 262, 207, 264, 930, 187, -- /* 1910 */ 925, 109, 1111, 1115, 1113, 7, 1099, 242, 33, 1119, -- /* 1920 */ 21, 522, 22, 23, 24, 1118, 25, 190, 97, 26, -- /* 1930 */ 1027, 1013, 1011, 1015, 1069, 1016, 1068, 249, 248, 28, -- /* 1940 */ 41, 891, 981, 829, 108, 29, 250, 542, 251, 370, -- /* 1950 */ 373, 1133, 1132, 1190, 1190, 1190, 1190, 1190, 1190, 1190, -- /* 1960 */ 1532, 1531, -+ /* 0 */ 130, 127, 234, 282, 282, 1328, 576, 1307, 460, 289, -+ /* 10 */ 289, 576, 1622, 381, 576, 1328, 573, 576, 562, 413, -+ /* 20 */ 1300, 1542, 573, 481, 562, 524, 460, 459, 558, 82, -+ /* 30 */ 82, 983, 294, 375, 51, 51, 498, 61, 61, 984, -+ /* 40 */ 82, 82, 1577, 137, 138, 91, 7, 1228, 1228, 1063, -+ /* 50 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 413, -+ /* 60 */ 288, 288, 182, 288, 288, 481, 536, 288, 288, 130, -+ /* 70 */ 127, 234, 432, 573, 525, 562, 573, 557, 562, 1290, -+ /* 80 */ 573, 421, 562, 137, 138, 91, 559, 1228, 1228, 1063, -+ /* 90 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 296, -+ /* 100 */ 460, 398, 1249, 134, 134, 134, 134, 133, 133, 132, -+ /* 110 */ 132, 132, 131, 128, 451, 451, 1050, 1050, 1064, 1067, -+ /* 120 */ 1255, 1, 1, 582, 2, 1259, 581, 1174, 1259, 1174, -+ /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 481, 1341, -+ /* 140 */ 456, 299, 1341, 134, 134, 134, 134, 133, 133, 132, -+ /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 498, 1228, -+ /* 160 */ 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, -+ /* 170 */ 136, 1204, 862, 1281, 288, 288, 283, 288, 288, 523, -+ /* 180 */ 523, 1250, 139, 578, 7, 578, 1345, 573, 1169, 562, -+ /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 573, 547, -+ /* 200 */ 562, 1169, 245, 1541, 1169, 245, 133, 133, 132, 132, -+ /* 210 */ 132, 131, 128, 451, 302, 134, 134, 134, 134, 133, -+ /* 220 */ 133, 132, 132, 132, 131, 128, 451, 1575, 1204, 1205, -+ /* 230 */ 1204, 7, 470, 550, 455, 413, 550, 455, 130, 127, -+ /* 240 */ 234, 134, 134, 134, 134, 133, 133, 132, 132, 132, -+ /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 483, 137, -+ /* 260 */ 138, 91, 1019, 1228, 1228, 1063, 1066, 1053, 1053, 135, -+ /* 270 */ 135, 136, 136, 136, 136, 1085, 576, 1204, 132, 132, -+ /* 280 */ 132, 131, 128, 451, 93, 214, 134, 134, 134, 134, -+ /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 401, 19, -+ /* 300 */ 19, 134, 134, 134, 134, 133, 133, 132, 132, 132, -+ /* 310 */ 131, 128, 451, 1498, 426, 267, 344, 467, 332, 134, -+ /* 320 */ 134, 134, 134, 133, 133, 132, 132, 132, 131, 128, -+ /* 330 */ 451, 1281, 576, 6, 1204, 1205, 1204, 257, 576, 413, -+ /* 340 */ 511, 508, 507, 1279, 94, 1019, 464, 1204, 551, 551, -+ /* 350 */ 506, 1224, 1571, 44, 38, 51, 51, 411, 576, 413, -+ /* 360 */ 45, 51, 51, 137, 138, 91, 530, 1228, 1228, 1063, -+ /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 398, -+ /* 380 */ 1148, 82, 82, 137, 138, 91, 39, 1228, 1228, 1063, -+ /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 344, -+ /* 400 */ 44, 288, 288, 375, 1204, 1205, 1204, 209, 1204, 1224, -+ /* 410 */ 320, 567, 471, 576, 573, 576, 562, 576, 316, 264, -+ /* 420 */ 231, 46, 160, 134, 134, 134, 134, 133, 133, 132, -+ /* 430 */ 132, 132, 131, 128, 451, 303, 82, 82, 82, 82, -+ /* 440 */ 82, 82, 442, 134, 134, 134, 134, 133, 133, 132, -+ /* 450 */ 132, 132, 131, 128, 451, 1582, 544, 320, 567, 1250, -+ /* 460 */ 874, 1582, 380, 382, 413, 1204, 1205, 1204, 360, 182, -+ /* 470 */ 288, 288, 1576, 557, 1339, 557, 7, 557, 1277, 472, -+ /* 480 */ 346, 526, 531, 573, 556, 562, 439, 1511, 137, 138, -+ /* 490 */ 91, 219, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, -+ /* 500 */ 136, 136, 136, 136, 465, 1511, 1513, 532, 413, 288, -+ /* 510 */ 288, 423, 512, 288, 288, 411, 288, 288, 874, 130, -+ /* 520 */ 127, 234, 573, 1107, 562, 1204, 573, 1107, 562, 573, -+ /* 530 */ 560, 562, 137, 138, 91, 1293, 1228, 1228, 1063, 1066, -+ /* 540 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 134, 134, -+ /* 550 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, -+ /* 560 */ 493, 503, 1292, 1204, 257, 288, 288, 511, 508, 507, -+ /* 570 */ 1204, 1628, 1169, 123, 568, 275, 4, 506, 573, 1511, -+ /* 580 */ 562, 331, 1204, 1205, 1204, 1169, 548, 548, 1169, 261, -+ /* 590 */ 571, 7, 134, 134, 134, 134, 133, 133, 132, 132, -+ /* 600 */ 132, 131, 128, 451, 108, 533, 130, 127, 234, 1204, -+ /* 610 */ 448, 447, 413, 1451, 452, 983, 886, 96, 1598, 1233, -+ /* 620 */ 1204, 1205, 1204, 984, 1235, 1450, 565, 1204, 1205, 1204, -+ /* 630 */ 229, 522, 1234, 534, 1333, 1333, 137, 138, 91, 1449, -+ /* 640 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, -+ /* 650 */ 136, 136, 373, 1595, 971, 1040, 413, 1236, 418, 1236, -+ /* 660 */ 879, 121, 121, 948, 373, 1595, 1204, 1205, 1204, 122, -+ /* 670 */ 1204, 452, 577, 452, 363, 417, 1028, 882, 373, 1595, -+ /* 680 */ 137, 138, 91, 462, 1228, 1228, 1063, 1066, 1053, 1053, -+ /* 690 */ 135, 135, 136, 136, 136, 136, 134, 134, 134, 134, -+ /* 700 */ 133, 133, 132, 132, 132, 131, 128, 451, 1028, 1028, -+ /* 710 */ 1030, 1031, 35, 570, 570, 570, 197, 423, 1040, 198, -+ /* 720 */ 1204, 123, 568, 1204, 4, 320, 567, 1204, 1205, 1204, -+ /* 730 */ 40, 388, 576, 384, 882, 1029, 423, 1188, 571, 1028, -+ /* 740 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, -+ /* 750 */ 128, 451, 529, 1568, 1204, 19, 19, 1204, 575, 492, -+ /* 760 */ 413, 157, 452, 489, 1187, 1331, 1331, 5, 1204, 949, -+ /* 770 */ 431, 1028, 1028, 1030, 565, 22, 22, 1204, 1205, 1204, -+ /* 780 */ 1204, 1205, 1204, 477, 137, 138, 91, 212, 1228, 1228, -+ /* 790 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, -+ /* 800 */ 1188, 48, 111, 1040, 413, 1204, 213, 970, 1041, 121, -+ /* 810 */ 121, 1204, 1205, 1204, 1204, 1205, 1204, 122, 221, 452, -+ /* 820 */ 577, 452, 44, 487, 1028, 1204, 1205, 1204, 137, 138, -+ /* 830 */ 91, 378, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, -+ /* 840 */ 136, 136, 136, 136, 134, 134, 134, 134, 133, 133, -+ /* 850 */ 132, 132, 132, 131, 128, 451, 1028, 1028, 1030, 1031, -+ /* 860 */ 35, 461, 1204, 1205, 1204, 1569, 1040, 377, 214, 1149, -+ /* 870 */ 1657, 535, 1657, 437, 902, 320, 567, 1568, 364, 320, -+ /* 880 */ 567, 412, 329, 1029, 519, 1188, 3, 1028, 134, 134, -+ /* 890 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, -+ /* 900 */ 1659, 399, 1169, 307, 893, 307, 515, 576, 413, 214, -+ /* 910 */ 498, 944, 1024, 540, 903, 1169, 943, 392, 1169, 1028, -+ /* 920 */ 1028, 1030, 406, 298, 1204, 50, 1149, 1658, 413, 1658, -+ /* 930 */ 145, 145, 137, 138, 91, 293, 1228, 1228, 1063, 1066, -+ /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 1188, 1147, -+ /* 950 */ 514, 1568, 137, 138, 91, 1505, 1228, 1228, 1063, 1066, -+ /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 323, -+ /* 970 */ 435, 539, 111, 1506, 274, 291, 372, 517, 367, 516, -+ /* 980 */ 262, 1204, 1205, 1204, 1574, 481, 363, 576, 7, 1569, -+ /* 990 */ 1568, 377, 134, 134, 134, 134, 133, 133, 132, 132, -+ /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 576, 232, 576, -+ /* 1010 */ 19, 19, 134, 134, 134, 134, 133, 133, 132, 132, -+ /* 1020 */ 132, 131, 128, 451, 1169, 433, 576, 1207, 19, 19, -+ /* 1030 */ 19, 19, 19, 19, 1627, 576, 911, 1169, 47, 120, -+ /* 1040 */ 1169, 117, 413, 306, 498, 438, 1125, 206, 336, 19, -+ /* 1050 */ 19, 1435, 49, 449, 449, 449, 1368, 315, 81, 81, -+ /* 1060 */ 576, 304, 413, 1570, 207, 377, 137, 138, 91, 115, -+ /* 1070 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, -+ /* 1080 */ 136, 136, 576, 82, 82, 1207, 137, 138, 91, 1340, -+ /* 1090 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, -+ /* 1100 */ 136, 136, 1569, 386, 377, 82, 82, 463, 1126, 1552, -+ /* 1110 */ 333, 463, 335, 131, 128, 451, 1569, 161, 377, 16, -+ /* 1120 */ 317, 387, 428, 1127, 448, 447, 134, 134, 134, 134, -+ /* 1130 */ 133, 133, 132, 132, 132, 131, 128, 451, 1128, 576, -+ /* 1140 */ 1105, 10, 445, 267, 576, 1554, 134, 134, 134, 134, -+ /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 532, 576, -+ /* 1160 */ 922, 576, 19, 19, 576, 1573, 576, 147, 147, 7, -+ /* 1170 */ 923, 1236, 498, 1236, 576, 487, 413, 552, 285, 1224, -+ /* 1180 */ 969, 215, 82, 82, 66, 66, 1435, 67, 67, 21, -+ /* 1190 */ 21, 1110, 1110, 495, 334, 297, 413, 53, 53, 297, -+ /* 1200 */ 137, 138, 91, 119, 1228, 1228, 1063, 1066, 1053, 1053, -+ /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 1336, 1311, 446, -+ /* 1220 */ 137, 138, 91, 227, 1228, 1228, 1063, 1066, 1053, 1053, -+ /* 1230 */ 135, 135, 136, 136, 136, 136, 574, 1224, 936, 936, -+ /* 1240 */ 137, 126, 91, 141, 1228, 1228, 1063, 1066, 1053, 1053, -+ /* 1250 */ 135, 135, 136, 136, 136, 136, 533, 429, 472, 346, -+ /* 1260 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, -+ /* 1270 */ 128, 451, 576, 457, 233, 343, 1435, 403, 498, 1550, -+ /* 1280 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, -+ /* 1290 */ 128, 451, 576, 324, 576, 82, 82, 487, 576, 969, -+ /* 1300 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, -+ /* 1310 */ 128, 451, 288, 288, 546, 68, 68, 54, 54, 553, -+ /* 1320 */ 413, 69, 69, 351, 6, 573, 944, 562, 410, 409, -+ /* 1330 */ 1435, 943, 450, 545, 260, 259, 258, 576, 158, 576, -+ /* 1340 */ 413, 222, 1180, 479, 969, 138, 91, 430, 1228, 1228, -+ /* 1350 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, -+ /* 1360 */ 70, 70, 71, 71, 576, 1126, 91, 576, 1228, 1228, -+ /* 1370 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, -+ /* 1380 */ 1127, 166, 850, 851, 852, 1282, 419, 72, 72, 108, -+ /* 1390 */ 73, 73, 1310, 358, 1180, 1128, 576, 305, 576, 123, -+ /* 1400 */ 568, 494, 4, 488, 134, 134, 134, 134, 133, 133, -+ /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 55, -+ /* 1420 */ 55, 56, 56, 576, 134, 134, 134, 134, 133, 133, -+ /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 1104, 233, 1104, -+ /* 1440 */ 452, 1602, 582, 2, 1259, 576, 57, 57, 576, 321, -+ /* 1450 */ 576, 155, 565, 1435, 485, 353, 576, 356, 1341, 59, -+ /* 1460 */ 59, 576, 44, 969, 569, 419, 576, 238, 60, 60, -+ /* 1470 */ 261, 74, 74, 75, 75, 287, 231, 576, 1366, 76, -+ /* 1480 */ 76, 1040, 420, 184, 20, 20, 576, 121, 121, 77, -+ /* 1490 */ 77, 97, 218, 288, 288, 122, 125, 452, 577, 452, -+ /* 1500 */ 143, 143, 1028, 576, 520, 576, 573, 576, 562, 144, -+ /* 1510 */ 144, 474, 227, 1244, 478, 123, 568, 576, 4, 320, -+ /* 1520 */ 567, 245, 411, 576, 443, 411, 78, 78, 62, 62, -+ /* 1530 */ 79, 79, 571, 319, 1028, 1028, 1030, 1031, 35, 418, -+ /* 1540 */ 63, 63, 576, 290, 411, 9, 80, 80, 1144, 576, -+ /* 1550 */ 400, 576, 486, 455, 576, 1223, 452, 576, 325, 342, -+ /* 1560 */ 576, 111, 576, 1188, 242, 64, 64, 473, 565, 576, -+ /* 1570 */ 23, 576, 170, 170, 171, 171, 576, 87, 87, 328, -+ /* 1580 */ 65, 65, 542, 83, 83, 146, 146, 541, 123, 568, -+ /* 1590 */ 341, 4, 84, 84, 168, 168, 576, 1040, 576, 148, -+ /* 1600 */ 148, 576, 1380, 121, 121, 571, 1021, 576, 266, 576, -+ /* 1610 */ 424, 122, 576, 452, 577, 452, 576, 553, 1028, 142, -+ /* 1620 */ 142, 169, 169, 576, 162, 162, 528, 889, 371, 452, -+ /* 1630 */ 152, 152, 151, 151, 1379, 149, 149, 109, 370, 150, -+ /* 1640 */ 150, 565, 576, 480, 576, 266, 86, 86, 576, 1092, -+ /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 482, 576, 266, 466, -+ /* 1660 */ 543, 123, 568, 1616, 4, 88, 88, 85, 85, 475, -+ /* 1670 */ 1040, 52, 52, 222, 901, 900, 121, 121, 571, 1188, -+ /* 1680 */ 58, 58, 244, 1032, 122, 889, 452, 577, 452, 908, -+ /* 1690 */ 909, 1028, 300, 347, 504, 111, 263, 361, 165, 111, -+ /* 1700 */ 111, 1088, 452, 263, 974, 1153, 266, 1092, 986, 987, -+ /* 1710 */ 942, 939, 125, 125, 565, 1103, 872, 1103, 159, 941, -+ /* 1720 */ 1309, 125, 1557, 1028, 1028, 1030, 1031, 35, 542, 337, -+ /* 1730 */ 1530, 205, 1529, 541, 499, 1589, 490, 348, 1376, 352, -+ /* 1740 */ 355, 1032, 357, 1040, 359, 1324, 1308, 366, 563, 121, -+ /* 1750 */ 121, 376, 1188, 1389, 1434, 1362, 280, 122, 1374, 452, -+ /* 1760 */ 577, 452, 167, 1439, 1028, 1289, 1280, 1268, 1267, 1269, -+ /* 1770 */ 1609, 1359, 312, 313, 314, 397, 12, 237, 224, 1421, -+ /* 1780 */ 295, 1416, 1409, 1426, 339, 484, 340, 509, 1371, 1612, -+ /* 1790 */ 1372, 1425, 1244, 404, 301, 228, 1028, 1028, 1030, 1031, -+ /* 1800 */ 35, 1601, 1192, 454, 345, 1307, 292, 369, 1502, 1501, -+ /* 1810 */ 270, 396, 396, 395, 277, 393, 1370, 1369, 859, 1549, -+ /* 1820 */ 186, 123, 568, 235, 4, 1188, 391, 210, 211, 223, -+ /* 1830 */ 1547, 239, 1241, 327, 422, 96, 220, 195, 571, 180, -+ /* 1840 */ 188, 326, 468, 469, 190, 191, 502, 192, 193, 566, -+ /* 1850 */ 247, 109, 1430, 491, 199, 251, 102, 281, 402, 476, -+ /* 1860 */ 405, 1496, 452, 497, 253, 1422, 13, 1428, 14, 1427, -+ /* 1870 */ 203, 1507, 241, 500, 565, 354, 407, 92, 95, 1270, -+ /* 1880 */ 175, 254, 518, 43, 1327, 255, 1326, 1325, 436, 1518, -+ /* 1890 */ 350, 1318, 104, 229, 893, 1626, 440, 441, 1625, 408, -+ /* 1900 */ 240, 1296, 268, 1040, 310, 269, 1297, 527, 444, 121, -+ /* 1910 */ 121, 368, 1295, 1594, 1624, 311, 1394, 122, 1317, 452, -+ /* 1920 */ 577, 452, 374, 1580, 1028, 1393, 140, 553, 11, 90, -+ /* 1930 */ 568, 385, 4, 116, 318, 414, 1579, 110, 1483, 537, -+ /* 1940 */ 320, 567, 1350, 555, 42, 579, 571, 1349, 1198, 383, -+ /* 1950 */ 276, 390, 216, 389, 278, 279, 1028, 1028, 1030, 1031, -+ /* 1960 */ 35, 172, 580, 1265, 458, 1260, 415, 416, 185, 156, -+ /* 1970 */ 452, 1534, 1535, 173, 1533, 1532, 89, 308, 225, 226, -+ /* 1980 */ 846, 174, 565, 453, 217, 1188, 322, 236, 1102, 154, -+ /* 1990 */ 1100, 330, 187, 176, 1223, 243, 189, 925, 338, 246, -+ /* 2000 */ 1116, 194, 177, 425, 178, 427, 98, 196, 99, 100, -+ /* 2010 */ 101, 1040, 179, 1119, 1115, 248, 249, 121, 121, 163, -+ /* 2020 */ 24, 250, 349, 1238, 496, 122, 1108, 452, 577, 452, -+ /* 2030 */ 1192, 454, 1028, 266, 292, 200, 252, 201, 861, 396, -+ /* 2040 */ 396, 395, 277, 393, 15, 501, 859, 370, 292, 256, -+ /* 2050 */ 202, 554, 505, 396, 396, 395, 277, 393, 103, 239, -+ /* 2060 */ 859, 327, 25, 26, 1028, 1028, 1030, 1031, 35, 326, -+ /* 2070 */ 362, 510, 891, 239, 365, 327, 513, 904, 105, 309, -+ /* 2080 */ 164, 181, 27, 326, 106, 521, 107, 1185, 1069, 1155, -+ /* 2090 */ 17, 1154, 230, 1188, 284, 286, 265, 204, 125, 1171, -+ /* 2100 */ 241, 28, 978, 972, 29, 41, 1175, 1179, 175, 1173, -+ /* 2110 */ 30, 43, 31, 8, 241, 1178, 32, 1160, 208, 549, -+ /* 2120 */ 33, 111, 175, 1083, 1070, 43, 1068, 1072, 240, 113, -+ /* 2130 */ 114, 34, 561, 118, 1124, 271, 1073, 36, 18, 572, -+ /* 2140 */ 1033, 873, 240, 124, 37, 935, 272, 273, 1617, 183, -+ /* 2150 */ 153, 394, 1194, 1193, 1256, 1256, 1256, 1256, 1256, 1256, -+ /* 2160 */ 1256, 1256, 1256, 414, 1256, 1256, 1256, 1256, 320, 567, -+ /* 2170 */ 1256, 1256, 1256, 1256, 1256, 1256, 1256, 414, 1256, 1256, -+ /* 2180 */ 1256, 1256, 320, 567, 1256, 1256, 1256, 1256, 1256, 1256, -+ /* 2190 */ 1256, 1256, 458, 1256, 1256, 1256, 1256, 1256, 1256, 1256, -+ /* 2200 */ 1256, 1256, 1256, 1256, 1256, 1256, 458, - }; - static const YYCODETYPE yy_lookahead[] = { -- /* 0 */ 189, 211, 189, 189, 218, 189, 220, 189, 267, 268, -- /* 10 */ 269, 189, 210, 189, 228, 189, 267, 268, 269, 19, -- /* 20 */ 218, 189, 211, 212, 211, 212, 211, 211, 212, 211, -- /* 30 */ 212, 31, 211, 211, 212, 211, 212, 288, 300, 39, -- /* 40 */ 21, 189, 304, 43, 44, 45, 46, 47, 48, 49, -- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 225, 19, -- /* 60 */ 189, 183, 184, 185, 186, 189, 248, 263, 236, 191, -- /* 70 */ 248, 193, 248, 197, 208, 257, 262, 201, 200, 257, -- /* 80 */ 200, 257, 81, 43, 44, 45, 46, 47, 48, 49, -- /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 189, 80, -- /* 100 */ 189, 101, 102, 103, 104, 105, 106, 107, 108, 109, -- /* 110 */ 110, 111, 234, 235, 234, 235, 305, 306, 305, 118, -- /* 120 */ 307, 305, 306, 297, 298, 247, 86, 247, 88, 19, -- /* 130 */ 259, 251, 252, 267, 268, 269, 26, 136, 137, 261, -- /* 140 */ 121, 101, 102, 103, 104, 105, 106, 107, 108, 109, -- /* 150 */ 110, 111, 59, 43, 44, 45, 46, 47, 48, 49, -- /* 160 */ 50, 51, 52, 53, 54, 55, 56, 57, 259, 291, -- /* 170 */ 105, 106, 107, 108, 109, 110, 111, 158, 189, 69, -- /* 180 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, -- /* 190 */ 111, 107, 108, 109, 110, 111, 205, 206, 207, 19, -- /* 200 */ 19, 54, 55, 56, 57, 58, 29, 114, 115, 116, -- /* 210 */ 33, 101, 102, 103, 104, 105, 106, 107, 108, 109, -- /* 220 */ 110, 111, 233, 43, 44, 45, 46, 47, 48, 49, -- /* 230 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 126, -- /* 240 */ 127, 148, 65, 24, 214, 200, 59, 67, 101, 102, -- /* 250 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 22, -- /* 260 */ 189, 111, 43, 44, 45, 46, 47, 48, 49, 50, -- /* 270 */ 51, 52, 53, 54, 55, 56, 57, 206, 207, 234, -- /* 280 */ 235, 101, 102, 103, 104, 105, 106, 107, 108, 109, -- /* 290 */ 110, 111, 247, 76, 107, 114, 59, 267, 268, 269, -- /* 300 */ 189, 114, 115, 116, 162, 163, 89, 19, 263, 92, -- /* 310 */ 189, 23, 54, 55, 56, 57, 189, 206, 207, 22, -- /* 320 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, -- /* 330 */ 111, 43, 44, 45, 46, 47, 48, 49, 50, 51, -- /* 340 */ 52, 53, 54, 55, 56, 57, 19, 189, 277, 59, -- /* 350 */ 23, 114, 115, 116, 46, 47, 48, 49, 61, 101, -- /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, -- /* 370 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -- /* 380 */ 53, 54, 55, 56, 57, 125, 126, 127, 277, 101, -- /* 390 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, -- /* 400 */ 59, 189, 189, 276, 114, 115, 116, 117, 73, 59, -- /* 410 */ 120, 121, 122, 72, 214, 19, 81, 259, 19, 23, -- /* 420 */ 130, 81, 72, 24, 211, 212, 221, 119, 101, 102, -- /* 430 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 43, -- /* 440 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -- /* 450 */ 54, 55, 56, 57, 19, 114, 115, 116, 23, 208, -- /* 460 */ 125, 248, 189, 189, 114, 115, 116, 267, 268, 269, -- /* 470 */ 189, 136, 137, 189, 262, 22, 136, 137, 43, 44, -- /* 480 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, -- /* 490 */ 55, 56, 57, 189, 95, 211, 212, 101, 102, 103, -- /* 500 */ 104, 105, 106, 107, 108, 109, 110, 111, 59, 189, -- /* 510 */ 111, 189, 59, 76, 294, 295, 117, 118, 119, 120, -- /* 520 */ 121, 122, 123, 19, 87, 189, 89, 23, 129, 92, -- /* 530 */ 279, 227, 248, 22, 189, 284, 101, 102, 103, 104, -- /* 540 */ 105, 106, 107, 108, 109, 110, 111, 43, 44, 45, -- /* 550 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, -- /* 560 */ 56, 57, 19, 114, 115, 116, 23, 114, 115, 116, -- /* 570 */ 59, 117, 299, 300, 120, 121, 122, 304, 189, 189, -- /* 580 */ 143, 189, 110, 111, 130, 22, 43, 44, 45, 46, -- /* 590 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -- /* 600 */ 57, 211, 212, 211, 212, 101, 102, 103, 104, 105, -- /* 610 */ 106, 107, 108, 109, 110, 111, 226, 189, 226, 189, -- /* 620 */ 298, 132, 59, 134, 135, 114, 115, 116, 189, 59, -- /* 630 */ 285, 19, 7, 8, 9, 23, 205, 206, 207, 211, -- /* 640 */ 212, 211, 212, 221, 101, 102, 103, 104, 105, 106, -- /* 650 */ 107, 108, 109, 110, 111, 43, 44, 45, 46, 47, -- /* 660 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -- /* 670 */ 19, 181, 182, 183, 184, 185, 186, 114, 115, 116, -- /* 680 */ 189, 191, 133, 193, 114, 115, 116, 138, 299, 300, -- /* 690 */ 200, 22, 201, 304, 43, 44, 45, 46, 47, 48, -- /* 700 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 35, -- /* 710 */ 189, 141, 189, 101, 102, 103, 104, 105, 106, 107, -- /* 720 */ 108, 109, 110, 111, 234, 235, 22, 23, 59, 184, -- /* 730 */ 26, 186, 211, 212, 211, 212, 191, 247, 193, 19, -- /* 740 */ 66, 105, 106, 73, 189, 200, 189, 226, 74, 226, -- /* 750 */ 22, 261, 101, 102, 103, 104, 105, 106, 107, 108, -- /* 760 */ 109, 110, 111, 43, 44, 45, 46, 47, 48, 49, -- /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 189, 234, -- /* 780 */ 235, 291, 19, 114, 115, 116, 150, 59, 152, 189, -- /* 790 */ 233, 236, 247, 59, 189, 125, 126, 127, 59, 300, -- /* 800 */ 211, 212, 128, 304, 100, 19, 261, 156, 45, 46, -- /* 810 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -- /* 820 */ 57, 101, 102, 103, 104, 105, 106, 107, 108, 109, -- /* 830 */ 110, 111, 46, 233, 189, 189, 291, 248, 99, 189, -- /* 840 */ 125, 126, 127, 115, 26, 200, 289, 230, 231, 115, -- /* 850 */ 200, 16, 189, 114, 115, 189, 211, 212, 119, 221, -- /* 860 */ 189, 211, 212, 258, 101, 102, 103, 104, 105, 106, -- /* 870 */ 107, 108, 109, 110, 111, 189, 156, 211, 212, 234, -- /* 880 */ 235, 189, 211, 212, 234, 235, 22, 201, 189, 150, -- /* 890 */ 151, 152, 247, 248, 76, 16, 19, 247, 248, 113, -- /* 900 */ 189, 24, 257, 211, 212, 189, 26, 89, 262, 223, -- /* 910 */ 92, 225, 77, 189, 79, 129, 19, 53, 226, 248, -- /* 920 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -- /* 930 */ 53, 54, 55, 56, 57, 236, 19, 271, 189, 99, -- /* 940 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -- /* 950 */ 53, 54, 55, 56, 57, 115, 77, 59, 79, 119, -- /* 960 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -- /* 970 */ 53, 54, 55, 56, 57, 259, 22, 23, 101, 102, -- /* 980 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 59, -- /* 990 */ 150, 151, 152, 158, 22, 244, 24, 246, 101, 102, -- /* 1000 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 285, -- /* 1010 */ 189, 189, 114, 115, 116, 200, 136, 137, 101, 102, -- /* 1020 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 230, -- /* 1030 */ 231, 59, 211, 212, 285, 105, 106, 189, 19, 141, -- /* 1040 */ 234, 235, 239, 113, 114, 115, 116, 226, 118, 234, -- /* 1050 */ 235, 189, 249, 247, 100, 189, 126, 23, 236, 107, -- /* 1060 */ 26, 189, 247, 44, 45, 46, 47, 48, 49, 50, -- /* 1070 */ 51, 52, 53, 54, 55, 56, 57, 211, 212, 59, -- /* 1080 */ 150, 233, 152, 211, 212, 133, 12, 115, 189, 189, -- /* 1090 */ 138, 19, 20, 300, 22, 233, 76, 304, 226, 11, -- /* 1100 */ 208, 27, 22, 23, 200, 19, 26, 87, 36, 89, -- /* 1110 */ 211, 212, 92, 300, 248, 189, 42, 304, 189, 250, -- /* 1120 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, -- /* 1130 */ 111, 59, 200, 233, 114, 115, 116, 63, 234, 235, -- /* 1140 */ 235, 19, 20, 71, 22, 300, 189, 73, 200, 304, -- /* 1150 */ 116, 247, 247, 81, 189, 200, 227, 26, 36, 234, -- /* 1160 */ 235, 203, 204, 143, 200, 26, 234, 235, 194, 200, -- /* 1170 */ 48, 99, 247, 66, 189, 141, 284, 105, 106, 247, -- /* 1180 */ 100, 59, 234, 235, 112, 259, 114, 115, 116, 234, -- /* 1190 */ 235, 119, 85, 71, 266, 247, 211, 212, 234, 235, -- /* 1200 */ 114, 94, 247, 234, 235, 12, 266, 85, 136, 137, -- /* 1210 */ 189, 247, 90, 26, 126, 127, 247, 189, 26, 22, -- /* 1220 */ 27, 99, 150, 151, 152, 153, 154, 105, 106, 189, -- /* 1230 */ 302, 303, 211, 212, 112, 42, 114, 115, 116, 211, -- /* 1240 */ 212, 119, 302, 303, 19, 20, 189, 22, 274, 189, -- /* 1250 */ 15, 144, 278, 189, 22, 23, 63, 189, 189, 203, -- /* 1260 */ 204, 36, 136, 137, 155, 24, 157, 143, 211, 212, -- /* 1270 */ 189, 140, 150, 151, 152, 153, 154, 0, 1, 2, -- /* 1280 */ 211, 212, 5, 46, 59, 161, 147, 10, 11, 12, -- /* 1290 */ 13, 14, 211, 212, 17, 60, 71, 189, 258, 189, -- /* 1300 */ 59, 189, 105, 106, 189, 189, 189, 30, 116, 32, -- /* 1310 */ 85, 124, 189, 251, 252, 90, 189, 40, 258, 211, -- /* 1320 */ 212, 211, 212, 189, 99, 26, 211, 212, 211, 212, -- /* 1330 */ 105, 106, 100, 141, 211, 212, 119, 112, 189, 114, -- /* 1340 */ 115, 116, 23, 189, 119, 26, 129, 70, 189, 31, -- /* 1350 */ 113, 19, 20, 24, 22, 78, 115, 39, 81, 189, -- /* 1360 */ 211, 212, 26, 189, 22, 211, 212, 189, 36, 189, -- /* 1370 */ 211, 212, 189, 189, 97, 150, 151, 152, 153, 154, -- /* 1380 */ 127, 211, 212, 189, 189, 211, 212, 189, 189, 211, -- /* 1390 */ 212, 59, 189, 189, 211, 212, 23, 189, 22, 26, -- /* 1400 */ 24, 189, 149, 71, 189, 211, 212, 189, 131, 211, -- /* 1410 */ 212, 189, 189, 136, 137, 211, 212, 85, 189, 211, -- /* 1420 */ 212, 59, 90, 211, 212, 292, 293, 118, 119, 211, -- /* 1430 */ 212, 99, 23, 211, 212, 26, 159, 105, 106, 189, -- /* 1440 */ 211, 212, 143, 150, 112, 152, 114, 115, 116, 1, -- /* 1450 */ 2, 119, 23, 5, 23, 26, 189, 26, 10, 11, -- /* 1460 */ 12, 13, 14, 83, 84, 17, 253, 189, 139, 189, -- /* 1470 */ 19, 20, 189, 22, 189, 189, 140, 115, 30, 59, -- /* 1480 */ 32, 139, 150, 151, 152, 153, 154, 36, 40, 211, -- /* 1490 */ 212, 211, 212, 59, 211, 212, 211, 212, 7, 8, -- /* 1500 */ 19, 20, 189, 22, 150, 189, 152, 231, 281, 189, -- /* 1510 */ 59, 189, 23, 189, 189, 26, 189, 36, 70, 189, -- /* 1520 */ 23, 237, 71, 26, 211, 212, 78, 211, 212, 81, -- /* 1530 */ 189, 211, 212, 211, 212, 115, 211, 212, 211, 212, -- /* 1540 */ 59, 211, 212, 23, 23, 97, 26, 26, 23, 115, -- /* 1550 */ 99, 26, 71, 189, 189, 189, 105, 106, 107, 23, -- /* 1560 */ 189, 23, 26, 112, 26, 114, 115, 116, 189, 309, -- /* 1570 */ 119, 23, 19, 20, 26, 22, 189, 211, 212, 131, -- /* 1580 */ 99, 189, 211, 212, 136, 137, 105, 106, 189, 36, -- /* 1590 */ 211, 212, 189, 112, 189, 114, 115, 116, 211, 212, -- /* 1600 */ 119, 150, 151, 152, 153, 154, 189, 159, 23, 250, -- /* 1610 */ 189, 26, 59, 189, 189, 189, 189, 189, 280, 189, -- /* 1620 */ 250, 189, 189, 238, 71, 189, 189, 250, 211, 212, -- /* 1630 */ 187, 150, 151, 152, 153, 154, 211, 212, 250, 290, -- /* 1640 */ 240, 211, 212, 211, 212, 254, 286, 209, 254, 241, -- /* 1650 */ 240, 254, 99, 286, 215, 220, 214, 244, 105, 106, -- /* 1660 */ 214, 214, 244, 273, 224, 112, 192, 114, 115, 116, -- /* 1670 */ 60, 290, 119, 5, 139, 196, 196, 38, 10, 11, -- /* 1680 */ 12, 13, 14, 238, 240, 17, 196, 148, 287, 287, -- /* 1690 */ 276, 113, 22, 147, 241, 43, 229, 241, 30, 18, -- /* 1700 */ 32, 232, 232, 150, 151, 152, 153, 154, 40, 232, -- /* 1710 */ 232, 196, 18, 195, 265, 265, 264, 241, 264, 196, -- /* 1720 */ 155, 229, 229, 241, 241, 241, 195, 62, 196, 195, -- /* 1730 */ 22, 113, 216, 196, 222, 195, 195, 282, 70, 196, -- /* 1740 */ 283, 213, 216, 213, 64, 22, 78, 124, 219, 81, -- /* 1750 */ 162, 111, 219, 142, 256, 213, 113, 255, 213, 256, -- /* 1760 */ 216, 303, 215, 213, 213, 97, 255, 213, 216, 275, -- /* 1770 */ 275, 222, 216, 256, 255, 196, 91, 82, 256, 255, -- /* 1780 */ 308, 308, 146, 22, 143, 196, 155, 260, 25, 145, -- /* 1790 */ 144, 199, 26, 198, 13, 190, 190, 6, 293, 131, -- /* 1800 */ 188, 188, 245, 244, 136, 137, 245, 243, 242, 241, -- /* 1810 */ 188, 202, 208, 217, 217, 260, 208, 4, 202, 3, -- /* 1820 */ 22, 202, 208, 208, 160, 15, 209, 159, 270, 209, -- /* 1830 */ 98, 16, 272, 208, 23, 23, 137, 148, 128, 20, -- /* 1840 */ 140, 24, 16, 142, 1, 140, 149, 128, 61, 53, -- /* 1850 */ 148, 37, 53, 53, 53, 128, 114, 34, 296, 296, -- /* 1860 */ 139, 1, 5, 22, 113, 158, 26, 75, 41, 139, -- /* 1870 */ 68, 68, 113, 24, 20, 19, 129, 123, 23, 96, -- /* 1880 */ 22, 22, 37, 22, 22, 67, 22, 67, 59, 24, -- /* 1890 */ 23, 28, 67, 147, 22, 26, 23, 23, 23, 23, -- /* 1900 */ 22, 24, 23, 22, 24, 23, 139, 23, 114, 22, -- /* 1910 */ 141, 26, 88, 75, 86, 44, 23, 34, 22, 75, -- /* 1920 */ 34, 24, 34, 34, 34, 93, 34, 26, 26, 34, -- /* 1930 */ 23, 23, 23, 23, 23, 11, 23, 22, 26, 22, -- /* 1940 */ 22, 133, 23, 23, 22, 22, 139, 26, 139, 23, -- /* 1950 */ 15, 1, 1, 310, 310, 310, 310, 310, 310, 310, -- /* 1960 */ 139, 139, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 1970 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 1980 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 1990 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2000 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2010 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2020 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2030 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2040 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2050 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2060 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2070 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2080 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2090 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2100 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2110 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2120 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2130 */ 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, -- /* 2140 */ 310, 310, 310, -+ /* 0 */ 277, 278, 279, 241, 242, 225, 195, 227, 195, 241, -+ /* 10 */ 242, 195, 217, 221, 195, 235, 254, 195, 256, 19, -+ /* 20 */ 225, 298, 254, 195, 256, 206, 213, 214, 206, 218, -+ /* 30 */ 219, 31, 206, 195, 218, 219, 195, 218, 219, 39, -+ /* 40 */ 218, 219, 313, 43, 44, 45, 317, 47, 48, 49, -+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 19, -+ /* 60 */ 241, 242, 195, 241, 242, 195, 255, 241, 242, 277, -+ /* 70 */ 278, 279, 234, 254, 255, 256, 254, 255, 256, 218, -+ /* 80 */ 254, 240, 256, 43, 44, 45, 264, 47, 48, 49, -+ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 271, -+ /* 100 */ 287, 22, 23, 103, 104, 105, 106, 107, 108, 109, -+ /* 110 */ 110, 111, 112, 113, 114, 114, 47, 48, 49, 50, -+ /* 120 */ 187, 188, 189, 190, 191, 192, 190, 87, 192, 89, -+ /* 130 */ 197, 19, 199, 197, 318, 199, 320, 25, 195, 206, -+ /* 140 */ 299, 271, 206, 103, 104, 105, 106, 107, 108, 109, -+ /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 195, 47, -+ /* 160 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -+ /* 170 */ 58, 60, 21, 195, 241, 242, 215, 241, 242, 312, -+ /* 180 */ 313, 102, 70, 205, 317, 207, 242, 254, 77, 256, -+ /* 190 */ 254, 122, 256, 55, 56, 57, 58, 59, 254, 88, -+ /* 200 */ 256, 90, 269, 240, 93, 269, 107, 108, 109, 110, -+ /* 210 */ 111, 112, 113, 114, 271, 103, 104, 105, 106, 107, -+ /* 220 */ 108, 109, 110, 111, 112, 113, 114, 313, 117, 118, -+ /* 230 */ 119, 317, 81, 195, 301, 19, 195, 301, 277, 278, -+ /* 240 */ 279, 103, 104, 105, 106, 107, 108, 109, 110, 111, -+ /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 195, 43, -+ /* 260 */ 44, 45, 74, 47, 48, 49, 50, 51, 52, 53, -+ /* 270 */ 54, 55, 56, 57, 58, 124, 195, 60, 109, 110, -+ /* 280 */ 111, 112, 113, 114, 68, 195, 103, 104, 105, 106, -+ /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 208, 218, -+ /* 300 */ 219, 103, 104, 105, 106, 107, 108, 109, 110, 111, -+ /* 310 */ 112, 113, 114, 162, 233, 24, 128, 129, 130, 103, -+ /* 320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, -+ /* 330 */ 114, 195, 195, 215, 117, 118, 119, 120, 195, 19, -+ /* 340 */ 123, 124, 125, 207, 24, 74, 246, 60, 310, 311, -+ /* 350 */ 133, 60, 311, 82, 22, 218, 219, 257, 195, 19, -+ /* 360 */ 73, 218, 219, 43, 44, 45, 206, 47, 48, 49, -+ /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 22, -+ /* 380 */ 23, 218, 219, 43, 44, 45, 54, 47, 48, 49, -+ /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 128, -+ /* 400 */ 82, 241, 242, 195, 117, 118, 119, 289, 60, 118, -+ /* 410 */ 139, 140, 294, 195, 254, 195, 256, 195, 255, 259, -+ /* 420 */ 260, 73, 22, 103, 104, 105, 106, 107, 108, 109, -+ /* 430 */ 110, 111, 112, 113, 114, 206, 218, 219, 218, 219, -+ /* 440 */ 218, 219, 234, 103, 104, 105, 106, 107, 108, 109, -+ /* 450 */ 110, 111, 112, 113, 114, 318, 319, 139, 140, 102, -+ /* 460 */ 60, 318, 319, 221, 19, 117, 118, 119, 23, 195, -+ /* 470 */ 241, 242, 313, 255, 206, 255, 317, 255, 206, 129, -+ /* 480 */ 130, 206, 264, 254, 264, 256, 264, 195, 43, 44, -+ /* 490 */ 45, 151, 47, 48, 49, 50, 51, 52, 53, 54, -+ /* 500 */ 55, 56, 57, 58, 246, 213, 214, 19, 19, 241, -+ /* 510 */ 242, 195, 23, 241, 242, 257, 241, 242, 118, 277, -+ /* 520 */ 278, 279, 254, 29, 256, 60, 254, 33, 256, 254, -+ /* 530 */ 206, 256, 43, 44, 45, 218, 47, 48, 49, 50, -+ /* 540 */ 51, 52, 53, 54, 55, 56, 57, 58, 103, 104, -+ /* 550 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, -+ /* 560 */ 66, 19, 218, 60, 120, 241, 242, 123, 124, 125, -+ /* 570 */ 60, 232, 77, 19, 20, 26, 22, 133, 254, 287, -+ /* 580 */ 256, 265, 117, 118, 119, 90, 312, 313, 93, 47, -+ /* 590 */ 36, 317, 103, 104, 105, 106, 107, 108, 109, 110, -+ /* 600 */ 111, 112, 113, 114, 116, 117, 277, 278, 279, 60, -+ /* 610 */ 107, 108, 19, 276, 60, 31, 23, 152, 195, 116, -+ /* 620 */ 117, 118, 119, 39, 121, 276, 72, 117, 118, 119, -+ /* 630 */ 166, 167, 129, 145, 237, 238, 43, 44, 45, 276, -+ /* 640 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -+ /* 650 */ 57, 58, 315, 316, 144, 101, 19, 154, 116, 156, -+ /* 660 */ 23, 107, 108, 109, 315, 316, 117, 118, 119, 115, -+ /* 670 */ 60, 117, 118, 119, 132, 200, 122, 60, 315, 316, -+ /* 680 */ 43, 44, 45, 272, 47, 48, 49, 50, 51, 52, -+ /* 690 */ 53, 54, 55, 56, 57, 58, 103, 104, 105, 106, -+ /* 700 */ 107, 108, 109, 110, 111, 112, 113, 114, 154, 155, -+ /* 710 */ 156, 157, 158, 212, 213, 214, 22, 195, 101, 22, -+ /* 720 */ 60, 19, 20, 60, 22, 139, 140, 117, 118, 119, -+ /* 730 */ 22, 251, 195, 253, 117, 118, 195, 183, 36, 122, -+ /* 740 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, -+ /* 750 */ 113, 114, 195, 195, 60, 218, 219, 60, 195, 284, -+ /* 760 */ 19, 25, 60, 288, 23, 237, 238, 22, 60, 109, -+ /* 770 */ 233, 154, 155, 156, 72, 218, 219, 117, 118, 119, -+ /* 780 */ 117, 118, 119, 116, 43, 44, 45, 265, 47, 48, -+ /* 790 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, -+ /* 800 */ 183, 243, 25, 101, 19, 60, 265, 144, 23, 107, -+ /* 810 */ 108, 117, 118, 119, 117, 118, 119, 115, 151, 117, -+ /* 820 */ 118, 119, 82, 195, 122, 117, 118, 119, 43, 44, -+ /* 830 */ 45, 195, 47, 48, 49, 50, 51, 52, 53, 54, -+ /* 840 */ 55, 56, 57, 58, 103, 104, 105, 106, 107, 108, -+ /* 850 */ 109, 110, 111, 112, 113, 114, 154, 155, 156, 157, -+ /* 860 */ 158, 121, 117, 118, 119, 307, 101, 309, 195, 22, -+ /* 870 */ 23, 195, 25, 19, 35, 139, 140, 195, 24, 139, -+ /* 880 */ 140, 208, 195, 118, 109, 183, 22, 122, 103, 104, -+ /* 890 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, -+ /* 900 */ 304, 305, 77, 230, 127, 232, 67, 195, 19, 195, -+ /* 910 */ 195, 136, 23, 88, 75, 90, 141, 203, 93, 154, -+ /* 920 */ 155, 156, 208, 295, 60, 243, 22, 23, 19, 25, -+ /* 930 */ 218, 219, 43, 44, 45, 100, 47, 48, 49, 50, -+ /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 183, 102, -+ /* 950 */ 96, 195, 43, 44, 45, 240, 47, 48, 49, 50, -+ /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 134, -+ /* 970 */ 131, 146, 25, 286, 120, 121, 122, 123, 124, 125, -+ /* 980 */ 126, 117, 118, 119, 313, 195, 132, 195, 317, 307, -+ /* 990 */ 195, 309, 103, 104, 105, 106, 107, 108, 109, 110, -+ /* 1000 */ 111, 112, 113, 114, 195, 195, 102, 195, 195, 195, -+ /* 1010 */ 218, 219, 103, 104, 105, 106, 107, 108, 109, 110, -+ /* 1020 */ 111, 112, 113, 114, 77, 233, 195, 60, 218, 219, -+ /* 1030 */ 218, 219, 218, 219, 23, 195, 25, 90, 243, 159, -+ /* 1040 */ 93, 161, 19, 233, 195, 233, 23, 233, 16, 218, -+ /* 1050 */ 219, 195, 243, 212, 213, 214, 262, 263, 218, 219, -+ /* 1060 */ 195, 271, 19, 307, 233, 309, 43, 44, 45, 160, -+ /* 1070 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -+ /* 1080 */ 57, 58, 195, 218, 219, 118, 43, 44, 45, 240, -+ /* 1090 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, -+ /* 1100 */ 57, 58, 307, 195, 309, 218, 219, 263, 12, 195, -+ /* 1110 */ 78, 267, 80, 112, 113, 114, 307, 22, 309, 24, -+ /* 1120 */ 255, 281, 266, 27, 107, 108, 103, 104, 105, 106, -+ /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 195, -+ /* 1140 */ 11, 22, 255, 24, 195, 195, 103, 104, 105, 106, -+ /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 19, 195, -+ /* 1160 */ 64, 195, 218, 219, 195, 313, 195, 218, 219, 317, -+ /* 1170 */ 74, 154, 195, 156, 195, 195, 19, 233, 23, 60, -+ /* 1180 */ 25, 24, 218, 219, 218, 219, 195, 218, 219, 218, -+ /* 1190 */ 219, 128, 129, 130, 162, 263, 19, 218, 219, 267, -+ /* 1200 */ 43, 44, 45, 160, 47, 48, 49, 50, 51, 52, -+ /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 240, 228, 255, -+ /* 1220 */ 43, 44, 45, 25, 47, 48, 49, 50, 51, 52, -+ /* 1230 */ 53, 54, 55, 56, 57, 58, 135, 118, 137, 138, -+ /* 1240 */ 43, 44, 45, 22, 47, 48, 49, 50, 51, 52, -+ /* 1250 */ 53, 54, 55, 56, 57, 58, 117, 266, 129, 130, -+ /* 1260 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, -+ /* 1270 */ 113, 114, 195, 195, 119, 295, 195, 206, 195, 195, -+ /* 1280 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, -+ /* 1290 */ 113, 114, 195, 195, 195, 218, 219, 195, 195, 144, -+ /* 1300 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, -+ /* 1310 */ 113, 114, 241, 242, 67, 218, 219, 218, 219, 146, -+ /* 1320 */ 19, 218, 219, 240, 215, 254, 136, 256, 107, 108, -+ /* 1330 */ 195, 141, 255, 86, 128, 129, 130, 195, 165, 195, -+ /* 1340 */ 19, 143, 95, 272, 25, 44, 45, 266, 47, 48, -+ /* 1350 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, -+ /* 1360 */ 218, 219, 218, 219, 195, 12, 45, 195, 47, 48, -+ /* 1370 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, -+ /* 1380 */ 27, 23, 7, 8, 9, 210, 211, 218, 219, 116, -+ /* 1390 */ 218, 219, 228, 16, 147, 42, 195, 295, 195, 19, -+ /* 1400 */ 20, 266, 22, 294, 103, 104, 105, 106, 107, 108, -+ /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 218, -+ /* 1420 */ 219, 218, 219, 195, 103, 104, 105, 106, 107, 108, -+ /* 1430 */ 109, 110, 111, 112, 113, 114, 195, 154, 119, 156, -+ /* 1440 */ 60, 189, 190, 191, 192, 195, 218, 219, 195, 197, -+ /* 1450 */ 195, 199, 72, 195, 19, 78, 195, 80, 206, 218, -+ /* 1460 */ 219, 195, 82, 144, 210, 211, 195, 15, 218, 219, -+ /* 1470 */ 47, 218, 219, 218, 219, 259, 260, 195, 261, 218, -+ /* 1480 */ 219, 101, 302, 303, 218, 219, 195, 107, 108, 218, -+ /* 1490 */ 219, 150, 151, 241, 242, 115, 25, 117, 118, 119, -+ /* 1500 */ 218, 219, 122, 195, 146, 195, 254, 195, 256, 218, -+ /* 1510 */ 219, 246, 25, 61, 246, 19, 20, 195, 22, 139, -+ /* 1520 */ 140, 269, 257, 195, 266, 257, 218, 219, 218, 219, -+ /* 1530 */ 218, 219, 36, 246, 154, 155, 156, 157, 158, 116, -+ /* 1540 */ 218, 219, 195, 22, 257, 49, 218, 219, 23, 195, -+ /* 1550 */ 25, 195, 117, 301, 195, 25, 60, 195, 195, 23, -+ /* 1560 */ 195, 25, 195, 183, 24, 218, 219, 130, 72, 195, -+ /* 1570 */ 22, 195, 218, 219, 218, 219, 195, 218, 219, 195, -+ /* 1580 */ 218, 219, 86, 218, 219, 218, 219, 91, 19, 20, -+ /* 1590 */ 153, 22, 218, 219, 218, 219, 195, 101, 195, 218, -+ /* 1600 */ 219, 195, 195, 107, 108, 36, 23, 195, 25, 195, -+ /* 1610 */ 62, 115, 195, 117, 118, 119, 195, 146, 122, 218, -+ /* 1620 */ 219, 218, 219, 195, 218, 219, 19, 60, 122, 60, -+ /* 1630 */ 218, 219, 218, 219, 195, 218, 219, 150, 132, 218, -+ /* 1640 */ 219, 72, 195, 23, 195, 25, 218, 219, 195, 60, -+ /* 1650 */ 154, 155, 156, 157, 158, 86, 23, 195, 25, 195, -+ /* 1660 */ 91, 19, 20, 142, 22, 218, 219, 218, 219, 130, -+ /* 1670 */ 101, 218, 219, 143, 121, 122, 107, 108, 36, 183, -+ /* 1680 */ 218, 219, 142, 60, 115, 118, 117, 118, 119, 7, -+ /* 1690 */ 8, 122, 153, 23, 23, 25, 25, 23, 23, 25, -+ /* 1700 */ 25, 23, 60, 25, 23, 98, 25, 118, 84, 85, -+ /* 1710 */ 23, 23, 25, 25, 72, 154, 23, 156, 25, 23, -+ /* 1720 */ 228, 25, 195, 154, 155, 156, 157, 158, 86, 195, -+ /* 1730 */ 195, 258, 195, 91, 291, 322, 195, 195, 195, 195, -+ /* 1740 */ 195, 118, 195, 101, 195, 195, 195, 195, 238, 107, -+ /* 1750 */ 108, 195, 183, 195, 195, 195, 290, 115, 195, 117, -+ /* 1760 */ 118, 119, 244, 195, 122, 195, 195, 195, 195, 195, -+ /* 1770 */ 195, 258, 258, 258, 258, 193, 245, 300, 216, 274, -+ /* 1780 */ 247, 270, 270, 274, 296, 296, 248, 222, 262, 198, -+ /* 1790 */ 262, 274, 61, 274, 248, 231, 154, 155, 156, 157, -+ /* 1800 */ 158, 0, 1, 2, 247, 227, 5, 221, 221, 221, -+ /* 1810 */ 142, 10, 11, 12, 13, 14, 262, 262, 17, 202, -+ /* 1820 */ 300, 19, 20, 300, 22, 183, 247, 251, 251, 245, -+ /* 1830 */ 202, 30, 38, 32, 202, 152, 151, 22, 36, 43, -+ /* 1840 */ 236, 40, 18, 202, 239, 239, 18, 239, 239, 283, -+ /* 1850 */ 201, 150, 236, 202, 236, 201, 159, 202, 248, 248, -+ /* 1860 */ 248, 248, 60, 63, 201, 275, 273, 275, 273, 275, -+ /* 1870 */ 22, 286, 71, 223, 72, 202, 223, 297, 297, 202, -+ /* 1880 */ 79, 201, 116, 82, 220, 201, 220, 220, 65, 293, -+ /* 1890 */ 292, 229, 22, 166, 127, 226, 24, 114, 226, 223, -+ /* 1900 */ 99, 222, 202, 101, 285, 92, 220, 308, 83, 107, -+ /* 1910 */ 108, 220, 220, 316, 220, 285, 268, 115, 229, 117, -+ /* 1920 */ 118, 119, 223, 321, 122, 268, 149, 146, 22, 19, -+ /* 1930 */ 20, 202, 22, 159, 282, 134, 321, 148, 280, 147, -+ /* 1940 */ 139, 140, 252, 141, 25, 204, 36, 252, 13, 251, -+ /* 1950 */ 196, 248, 250, 249, 196, 6, 154, 155, 156, 157, -+ /* 1960 */ 158, 209, 194, 194, 163, 194, 306, 306, 303, 224, -+ /* 1970 */ 60, 215, 215, 209, 215, 215, 215, 224, 216, 216, -+ /* 1980 */ 4, 209, 72, 3, 22, 183, 164, 15, 23, 16, -+ /* 1990 */ 23, 140, 152, 131, 25, 24, 143, 20, 16, 145, -+ /* 2000 */ 1, 143, 131, 62, 131, 37, 54, 152, 54, 54, -+ /* 2010 */ 54, 101, 131, 117, 1, 34, 142, 107, 108, 5, -+ /* 2020 */ 22, 116, 162, 76, 41, 115, 69, 117, 118, 119, -+ /* 2030 */ 1, 2, 122, 25, 5, 69, 142, 116, 20, 10, -+ /* 2040 */ 11, 12, 13, 14, 24, 19, 17, 132, 5, 126, -+ /* 2050 */ 22, 141, 68, 10, 11, 12, 13, 14, 22, 30, -+ /* 2060 */ 17, 32, 22, 22, 154, 155, 156, 157, 158, 40, -+ /* 2070 */ 23, 68, 60, 30, 24, 32, 97, 28, 22, 68, -+ /* 2080 */ 23, 37, 34, 40, 150, 22, 25, 23, 23, 23, -+ /* 2090 */ 22, 98, 142, 183, 23, 23, 34, 22, 25, 89, -+ /* 2100 */ 71, 34, 117, 144, 34, 22, 76, 76, 79, 87, -+ /* 2110 */ 34, 82, 34, 44, 71, 94, 34, 23, 25, 24, -+ /* 2120 */ 34, 25, 79, 23, 23, 82, 23, 23, 99, 143, -+ /* 2130 */ 143, 22, 25, 25, 23, 22, 11, 22, 22, 25, -+ /* 2140 */ 23, 23, 99, 22, 22, 136, 142, 142, 142, 25, -+ /* 2150 */ 23, 15, 1, 1, 323, 323, 323, 323, 323, 323, -+ /* 2160 */ 323, 323, 323, 134, 323, 323, 323, 323, 139, 140, -+ /* 2170 */ 323, 323, 323, 323, 323, 323, 323, 134, 323, 323, -+ /* 2180 */ 323, 323, 139, 140, 323, 323, 323, 323, 323, 323, -+ /* 2190 */ 323, 323, 163, 323, 323, 323, 323, 323, 323, 323, -+ /* 2200 */ 323, 323, 323, 323, 323, 323, 163, 323, 323, 323, -+ /* 2210 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2220 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2230 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2240 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2250 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2260 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2270 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2280 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2290 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2300 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2310 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2320 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2330 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -+ /* 2340 */ 323, 187, 187, 187, 187, 187, 187, 187, 187, 187, -+ /* 2350 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, -+ /* 2360 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, -+ /* 2370 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, -+ /* 2380 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, -+ /* 2390 */ 187, 187, 187, 187, - }; --#define YY_SHIFT_COUNT (552) -+#define YY_SHIFT_COUNT (582) - #define YY_SHIFT_MIN (0) --#define YY_SHIFT_MAX (1951) -+#define YY_SHIFT_MAX (2152) - static const unsigned short int yy_shift_ofst[] = { -- /* 0 */ 1448, 1277, 1668, 1072, 1072, 340, 1122, 1225, 1332, 1481, -- /* 10 */ 1481, 1481, 335, 0, 0, 180, 897, 1481, 1481, 1481, -- /* 20 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, -- /* 30 */ 930, 930, 1020, 1020, 290, 1, 340, 340, 340, 340, -- /* 40 */ 340, 340, 40, 110, 219, 288, 327, 396, 435, 504, -- /* 50 */ 543, 612, 651, 720, 877, 897, 897, 897, 897, 897, -- /* 60 */ 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, -- /* 70 */ 897, 897, 897, 917, 897, 1019, 763, 763, 1451, 1481, -- /* 80 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, -- /* 90 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, -- /* 100 */ 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, -- /* 110 */ 1481, 1481, 1553, 1481, 1481, 1481, 1481, 1481, 1481, 1481, -- /* 120 */ 1481, 1481, 1481, 1481, 1481, 1481, 147, 258, 258, 258, -- /* 130 */ 258, 258, 79, 65, 84, 449, 19, 786, 449, 636, -- /* 140 */ 636, 449, 880, 880, 880, 880, 113, 142, 142, 472, -- /* 150 */ 150, 1962, 1962, 399, 399, 399, 93, 237, 341, 237, -- /* 160 */ 237, 1074, 1074, 437, 350, 704, 1080, 449, 449, 449, -- /* 170 */ 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, -- /* 180 */ 449, 449, 449, 449, 449, 449, 449, 449, 818, 818, -- /* 190 */ 449, 1088, 217, 217, 734, 734, 1124, 1126, 1962, 1962, -- /* 200 */ 1962, 739, 840, 840, 453, 454, 511, 187, 563, 570, -- /* 210 */ 898, 669, 449, 449, 449, 449, 449, 449, 449, 449, -- /* 220 */ 449, 670, 449, 449, 449, 449, 449, 449, 449, 449, -- /* 230 */ 449, 449, 449, 449, 674, 674, 674, 449, 449, 449, -- /* 240 */ 449, 1034, 449, 449, 449, 972, 1107, 449, 449, 1193, -- /* 250 */ 449, 449, 449, 449, 449, 449, 449, 449, 260, 177, -- /* 260 */ 489, 1241, 1241, 1241, 1241, 1192, 489, 489, 952, 1197, -- /* 270 */ 625, 1235, 1131, 181, 181, 1086, 1139, 1131, 1086, 1187, -- /* 280 */ 1319, 1237, 1318, 1318, 1318, 181, 1299, 1299, 1109, 1336, -- /* 290 */ 549, 1376, 1610, 1535, 1535, 1639, 1639, 1535, 1539, 1578, -- /* 300 */ 1670, 1546, 1652, 1546, 1681, 1681, 1681, 1681, 1535, 1694, -- /* 310 */ 1546, 1546, 1578, 1670, 1652, 1546, 1652, 1546, 1535, 1694, -- /* 320 */ 1565, 1665, 1535, 1694, 1708, 1535, 1694, 1535, 1694, 1708, -- /* 330 */ 1618, 1618, 1618, 1680, 1723, 1723, 1708, 1618, 1623, 1618, -- /* 340 */ 1680, 1618, 1618, 1588, 1708, 1640, 1640, 1708, 1611, 1643, -- /* 350 */ 1611, 1643, 1611, 1643, 1611, 1643, 1535, 1685, 1685, 1695, -- /* 360 */ 1695, 1636, 1641, 1761, 1535, 1631, 1636, 1644, 1646, 1546, -- /* 370 */ 1763, 1766, 1781, 1781, 1791, 1791, 1791, 1962, 1962, 1962, -- /* 380 */ 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, -- /* 390 */ 1962, 1962, 308, 835, 954, 1232, 879, 715, 728, 1373, -- /* 400 */ 864, 1329, 1253, 1409, 297, 1431, 1489, 1497, 1520, 1521, -- /* 410 */ 1525, 1362, 1309, 1491, 1217, 1420, 1429, 1536, 1380, 1538, -- /* 420 */ 1293, 1354, 1548, 1585, 1434, 1342, 1813, 1816, 1798, 1664, -- /* 430 */ 1810, 1732, 1815, 1811, 1812, 1699, 1689, 1710, 1817, 1700, -- /* 440 */ 1819, 1701, 1826, 1843, 1705, 1697, 1719, 1787, 1814, 1702, -- /* 450 */ 1796, 1799, 1800, 1801, 1727, 1742, 1823, 1721, 1860, 1857, -- /* 460 */ 1841, 1751, 1707, 1802, 1840, 1803, 1792, 1827, 1730, 1759, -- /* 470 */ 1849, 1854, 1856, 1747, 1754, 1858, 1818, 1859, 1861, 1855, -- /* 480 */ 1862, 1820, 1829, 1865, 1783, 1863, 1864, 1825, 1845, 1867, -- /* 490 */ 1746, 1872, 1873, 1874, 1875, 1869, 1876, 1878, 1877, 1879, -- /* 500 */ 1881, 1880, 1767, 1882, 1884, 1794, 1883, 1887, 1769, 1885, -- /* 510 */ 1886, 1888, 1889, 1890, 1824, 1838, 1828, 1871, 1844, 1832, -- /* 520 */ 1892, 1893, 1896, 1897, 1901, 1902, 1895, 1907, 1885, 1908, -- /* 530 */ 1909, 1910, 1911, 1912, 1913, 1915, 1924, 1917, 1918, 1919, -- /* 540 */ 1920, 1922, 1923, 1921, 1808, 1807, 1809, 1821, 1822, 1926, -- /* 550 */ 1935, 1950, 1951, -+ /* 0 */ 2029, 1801, 2043, 1380, 1380, 318, 271, 1496, 1569, 1642, -+ /* 10 */ 702, 702, 702, 740, 318, 318, 318, 318, 318, 0, -+ /* 20 */ 0, 216, 1177, 702, 702, 702, 702, 702, 702, 702, -+ /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 503, 503, -+ /* 40 */ 111, 111, 217, 287, 348, 610, 610, 736, 736, 736, -+ /* 50 */ 736, 40, 112, 320, 340, 445, 489, 593, 637, 741, -+ /* 60 */ 785, 889, 909, 1023, 1043, 1157, 1177, 1177, 1177, 1177, -+ /* 70 */ 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, -+ /* 80 */ 1177, 1177, 1177, 1177, 1197, 1177, 1301, 1321, 1321, 554, -+ /* 90 */ 1802, 1910, 702, 702, 702, 702, 702, 702, 702, 702, -+ /* 100 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, -+ /* 110 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, -+ /* 120 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, -+ /* 130 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, -+ /* 140 */ 702, 702, 138, 198, 198, 198, 198, 198, 198, 198, -+ /* 150 */ 183, 99, 169, 549, 610, 151, 542, 610, 610, 1017, -+ /* 160 */ 1017, 610, 1001, 350, 464, 464, 464, 586, 1, 1, -+ /* 170 */ 2207, 2207, 854, 854, 854, 465, 694, 694, 694, 694, -+ /* 180 */ 1096, 1096, 825, 549, 847, 904, 610, 610, 610, 610, -+ /* 190 */ 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, -+ /* 200 */ 610, 610, 610, 610, 610, 488, 947, 947, 610, 1129, -+ /* 210 */ 495, 495, 1139, 1139, 967, 967, 1173, 2207, 2207, 2207, -+ /* 220 */ 2207, 2207, 2207, 2207, 617, 765, 765, 697, 444, 708, -+ /* 230 */ 660, 745, 510, 663, 864, 610, 610, 610, 610, 610, -+ /* 240 */ 610, 610, 610, 610, 610, 188, 610, 610, 610, 610, -+ /* 250 */ 610, 610, 610, 610, 610, 610, 610, 610, 839, 839, -+ /* 260 */ 839, 610, 610, 610, 1155, 610, 610, 610, 1119, 1247, -+ /* 270 */ 610, 1353, 610, 610, 610, 610, 610, 610, 610, 610, -+ /* 280 */ 1063, 494, 1101, 291, 291, 291, 291, 1319, 1101, 1101, -+ /* 290 */ 775, 1221, 1375, 1452, 667, 1341, 1198, 1341, 1435, 1487, -+ /* 300 */ 667, 667, 1487, 667, 1198, 1435, 777, 1011, 1423, 584, -+ /* 310 */ 584, 584, 1273, 1273, 1273, 1273, 1471, 1471, 880, 1530, -+ /* 320 */ 1190, 1095, 1731, 1731, 1668, 1668, 1794, 1794, 1668, 1683, -+ /* 330 */ 1685, 1815, 1796, 1824, 1824, 1824, 1824, 1668, 1828, 1701, -+ /* 340 */ 1685, 1685, 1701, 1815, 1796, 1701, 1796, 1701, 1668, 1828, -+ /* 350 */ 1697, 1800, 1668, 1828, 1848, 1668, 1828, 1668, 1828, 1848, -+ /* 360 */ 1766, 1766, 1766, 1823, 1870, 1870, 1848, 1766, 1767, 1766, -+ /* 370 */ 1823, 1766, 1766, 1727, 1872, 1783, 1783, 1848, 1668, 1813, -+ /* 380 */ 1813, 1825, 1825, 1777, 1781, 1906, 1668, 1774, 1777, 1789, -+ /* 390 */ 1792, 1701, 1919, 1935, 1935, 1949, 1949, 1949, 2207, 2207, -+ /* 400 */ 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, -+ /* 410 */ 2207, 2207, 2207, 69, 1032, 79, 357, 1377, 1206, 400, -+ /* 420 */ 1525, 835, 332, 1540, 1437, 1539, 1536, 1548, 1583, 1620, -+ /* 430 */ 1633, 1670, 1671, 1674, 1567, 1553, 1682, 1506, 1675, 1358, -+ /* 440 */ 1607, 1589, 1678, 1681, 1624, 1687, 1688, 1283, 1561, 1693, -+ /* 450 */ 1696, 1623, 1521, 1976, 1980, 1962, 1822, 1972, 1973, 1965, -+ /* 460 */ 1967, 1851, 1840, 1862, 1969, 1969, 1971, 1853, 1977, 1854, -+ /* 470 */ 1982, 1999, 1858, 1871, 1969, 1873, 1941, 1968, 1969, 1855, -+ /* 480 */ 1952, 1954, 1955, 1956, 1881, 1896, 1981, 1874, 2013, 2014, -+ /* 490 */ 1998, 1905, 1860, 1957, 2008, 1966, 1947, 1983, 1894, 1921, -+ /* 500 */ 2020, 2018, 2026, 1915, 1923, 2028, 1984, 2036, 2040, 2047, -+ /* 510 */ 2041, 2003, 2012, 2050, 1979, 2049, 2056, 2011, 2044, 2057, -+ /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1950, -+ /* 530 */ 2071, 2072, 1985, 2062, 2075, 1959, 2073, 2067, 2070, 2076, -+ /* 540 */ 2078, 2010, 2030, 2022, 2069, 2031, 2021, 2082, 2094, 2083, -+ /* 550 */ 2095, 2093, 2096, 2086, 1986, 1987, 2100, 2073, 2101, 2103, -+ /* 560 */ 2104, 2109, 2107, 2108, 2111, 2113, 2125, 2115, 2116, 2117, -+ /* 570 */ 2118, 2121, 2122, 2114, 2009, 2004, 2005, 2006, 2124, 2127, -+ /* 580 */ 2136, 2151, 2152, - }; --#define YY_REDUCE_COUNT (391) --#define YY_REDUCE_MIN (-262) --#define YY_REDUCE_MAX (1625) -+#define YY_REDUCE_COUNT (412) -+#define YY_REDUCE_MIN (-277) -+#define YY_REDUCE_MAX (1772) - static const short yy_reduce_ofst[] = { -- /* 0 */ 490, -122, 545, 645, 650, -120, -189, -187, -184, -182, -- /* 10 */ -178, -176, 45, 30, 200, -251, -134, 390, 392, 521, -- /* 20 */ 523, 213, 692, 821, 284, 589, 872, 666, 671, 866, -- /* 30 */ 71, 111, 273, 389, 686, 815, 904, 932, 948, 955, -- /* 40 */ 964, 969, -259, -259, -259, -259, -259, -259, -259, -259, -- /* 50 */ -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -- /* 60 */ -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -- /* 70 */ -259, -259, -259, -259, -259, -259, -259, -259, 428, 430, -- /* 80 */ 899, 985, 1021, 1028, 1057, 1069, 1081, 1108, 1110, 1115, -- /* 90 */ 1117, 1123, 1149, 1154, 1159, 1170, 1174, 1178, 1183, 1194, -- /* 100 */ 1198, 1204, 1208, 1212, 1218, 1222, 1229, 1278, 1280, 1283, -- /* 110 */ 1285, 1313, 1316, 1320, 1322, 1325, 1327, 1330, 1366, 1371, -- /* 120 */ 1379, 1387, 1417, 1425, 1430, 1432, -259, -259, -259, -259, -- /* 130 */ -259, -259, -259, -259, -259, 557, 974, -214, -174, -9, -- /* 140 */ 431, -124, 806, 925, 806, 925, 251, 928, 940, -259, -- /* 150 */ -259, -259, -259, -198, -198, -198, 127, -186, -168, 212, -- /* 160 */ 646, 617, 799, -262, 555, 220, 220, 491, 605, 1040, -- /* 170 */ 1060, 699, -11, 600, 848, 862, 345, -129, 724, -91, -- /* 180 */ 158, 749, 716, 900, 304, 822, 929, 926, 499, 793, -- /* 190 */ 322, 892, 813, 845, 958, 1056, 751, 905, 1133, 1062, -- /* 200 */ 803, -210, -185, -179, -148, -167, -89, 121, 274, 281, -- /* 210 */ 320, 336, 439, 663, 711, 957, 965, 1064, 1068, 1112, -- /* 220 */ 1116, -196, 1127, 1134, 1180, 1184, 1195, 1199, 1203, 1215, -- /* 230 */ 1223, 1250, 1267, 1286, 205, 422, 638, 1324, 1341, 1364, -- /* 240 */ 1365, 1213, 1392, 1399, 1403, 869, 1260, 1405, 1421, 1276, -- /* 250 */ 1424, 121, 1426, 1427, 1428, 1433, 1436, 1437, 1227, 1338, -- /* 260 */ 1284, 1359, 1370, 1377, 1388, 1213, 1284, 1284, 1385, 1438, -- /* 270 */ 1443, 1349, 1400, 1391, 1394, 1360, 1408, 1410, 1367, 1439, -- /* 280 */ 1440, 1435, 1442, 1446, 1447, 1397, 1413, 1418, 1390, 1444, -- /* 290 */ 1445, 1474, 1381, 1479, 1480, 1401, 1402, 1490, 1414, 1449, -- /* 300 */ 1452, 1453, 1467, 1456, 1469, 1470, 1477, 1478, 1515, 1518, -- /* 310 */ 1476, 1482, 1450, 1454, 1492, 1483, 1493, 1484, 1523, 1531, -- /* 320 */ 1457, 1455, 1532, 1534, 1516, 1537, 1540, 1543, 1541, 1526, -- /* 330 */ 1528, 1530, 1542, 1512, 1529, 1533, 1544, 1545, 1547, 1550, -- /* 340 */ 1549, 1551, 1554, 1458, 1552, 1494, 1495, 1556, 1498, 1502, -- /* 350 */ 1503, 1511, 1517, 1519, 1522, 1524, 1579, 1472, 1473, 1527, -- /* 360 */ 1555, 1557, 1559, 1558, 1589, 1560, 1561, 1564, 1566, 1568, -- /* 370 */ 1592, 1595, 1605, 1606, 1612, 1613, 1622, 1562, 1563, 1505, -- /* 380 */ 1609, 1604, 1608, 1614, 1615, 1616, 1596, 1597, 1617, 1620, -- /* 390 */ 1625, 1619, -+ /* 0 */ -67, 1252, -64, -178, -181, 160, 1071, 143, -184, 137, -+ /* 10 */ 218, 220, 222, -174, 229, 268, 272, 275, 324, -208, -+ /* 20 */ 242, -277, -39, 81, 537, 792, 810, 812, -189, 814, -+ /* 30 */ 831, 163, 865, 944, 887, 840, 964, 1077, -187, 292, -+ /* 40 */ -133, 274, 673, 558, 682, 795, 809, -238, -232, -238, -+ /* 50 */ -232, 329, 329, 329, 329, 329, 329, 329, 329, 329, -+ /* 60 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, -+ /* 70 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, -+ /* 80 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 557, -+ /* 90 */ 712, 949, 966, 969, 971, 979, 1097, 1099, 1103, 1142, -+ /* 100 */ 1144, 1169, 1172, 1201, 1203, 1228, 1241, 1250, 1253, 1255, -+ /* 110 */ 1261, 1266, 1271, 1282, 1291, 1308, 1310, 1312, 1322, 1328, -+ /* 120 */ 1347, 1354, 1356, 1359, 1362, 1365, 1367, 1374, 1376, 1381, -+ /* 130 */ 1401, 1403, 1406, 1412, 1414, 1417, 1421, 1428, 1447, 1449, -+ /* 140 */ 1453, 1462, 329, 329, 329, 329, 329, 329, 329, 329, -+ /* 150 */ 329, 329, 329, -22, -159, 475, -220, 756, 38, 501, -+ /* 160 */ 841, 714, 329, 118, 337, 349, 363, -56, 329, 329, -+ /* 170 */ 329, 329, -205, -205, -205, 687, -172, -130, -57, 790, -+ /* 180 */ 397, 528, -271, 136, 596, 596, 90, 316, 522, 541, -+ /* 190 */ -37, 715, 849, 977, 628, 856, 980, 991, 1081, 1102, -+ /* 200 */ 1135, 1083, -162, 208, 1258, 794, -86, 159, 41, 1109, -+ /* 210 */ 671, 852, 844, 932, 1175, 1254, 480, 1180, 100, 258, -+ /* 220 */ 1265, 1268, 1216, 1287, -139, 317, 344, 63, 339, 423, -+ /* 230 */ 563, 636, 676, 813, 908, 914, 950, 1078, 1084, 1098, -+ /* 240 */ 1363, 1384, 1407, 1439, 1464, 411, 1527, 1534, 1535, 1537, -+ /* 250 */ 1541, 1542, 1543, 1544, 1545, 1547, 1549, 1550, 990, 1164, -+ /* 260 */ 1492, 1551, 1552, 1556, 1217, 1558, 1559, 1560, 1473, 1413, -+ /* 270 */ 1563, 1510, 1568, 563, 1570, 1571, 1572, 1573, 1574, 1575, -+ /* 280 */ 1443, 1466, 1518, 1513, 1514, 1515, 1516, 1217, 1518, 1518, -+ /* 290 */ 1531, 1562, 1582, 1477, 1505, 1511, 1533, 1512, 1488, 1538, -+ /* 300 */ 1509, 1517, 1546, 1519, 1557, 1489, 1565, 1564, 1578, 1586, -+ /* 310 */ 1587, 1588, 1526, 1528, 1554, 1555, 1576, 1577, 1566, 1579, -+ /* 320 */ 1584, 1591, 1520, 1523, 1617, 1628, 1580, 1581, 1632, 1585, -+ /* 330 */ 1590, 1593, 1604, 1605, 1606, 1608, 1609, 1641, 1649, 1610, -+ /* 340 */ 1592, 1594, 1611, 1595, 1616, 1612, 1618, 1613, 1651, 1654, -+ /* 350 */ 1596, 1598, 1655, 1663, 1650, 1673, 1680, 1677, 1684, 1653, -+ /* 360 */ 1664, 1666, 1667, 1662, 1669, 1672, 1676, 1686, 1679, 1691, -+ /* 370 */ 1689, 1692, 1694, 1597, 1599, 1619, 1630, 1699, 1700, 1602, -+ /* 380 */ 1615, 1648, 1657, 1690, 1698, 1658, 1729, 1652, 1695, 1702, -+ /* 390 */ 1704, 1703, 1741, 1754, 1758, 1768, 1769, 1771, 1660, 1661, -+ /* 400 */ 1665, 1752, 1756, 1757, 1759, 1760, 1764, 1745, 1753, 1762, -+ /* 410 */ 1763, 1761, 1772, - }; - static const YYACTIONTYPE yy_default[] = { -- /* 0 */ 1575, 1575, 1575, 1411, 1188, 1297, 1188, 1188, 1188, 1411, -- /* 10 */ 1411, 1411, 1188, 1327, 1327, 1464, 1219, 1188, 1188, 1188, -- /* 20 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1410, 1188, 1188, -- /* 30 */ 1188, 1188, 1494, 1494, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 40 */ 1188, 1188, 1188, 1336, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 50 */ 1412, 1413, 1188, 1188, 1188, 1463, 1465, 1428, 1346, 1345, -- /* 60 */ 1344, 1343, 1446, 1314, 1341, 1334, 1338, 1406, 1407, 1405, -- /* 70 */ 1409, 1413, 1412, 1188, 1337, 1377, 1391, 1376, 1188, 1188, -- /* 80 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 90 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 100 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 110 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 120 */ 1188, 1188, 1188, 1188, 1188, 1188, 1385, 1390, 1396, 1389, -- /* 130 */ 1386, 1379, 1378, 1380, 1381, 1188, 1209, 1261, 1188, 1188, -- /* 140 */ 1188, 1188, 1482, 1481, 1188, 1188, 1219, 1371, 1370, 1382, -- /* 150 */ 1383, 1393, 1392, 1471, 1529, 1528, 1429, 1188, 1188, 1188, -- /* 160 */ 1188, 1188, 1188, 1494, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 170 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 180 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1494, 1494, -- /* 190 */ 1188, 1219, 1494, 1494, 1215, 1215, 1321, 1188, 1477, 1297, -- /* 200 */ 1288, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 210 */ 1188, 1188, 1188, 1188, 1188, 1468, 1466, 1188, 1188, 1188, -- /* 220 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 230 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 240 */ 1188, 1188, 1188, 1188, 1188, 1293, 1188, 1188, 1188, 1188, -- /* 250 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1523, 1188, 1441, -- /* 260 */ 1275, 1293, 1293, 1293, 1293, 1295, 1276, 1274, 1287, 1220, -- /* 270 */ 1195, 1567, 1294, 1316, 1316, 1564, 1340, 1294, 1564, 1236, -- /* 280 */ 1545, 1231, 1327, 1327, 1327, 1316, 1321, 1321, 1408, 1294, -- /* 290 */ 1287, 1188, 1567, 1302, 1302, 1566, 1566, 1302, 1429, 1349, -- /* 300 */ 1355, 1340, 1264, 1340, 1270, 1270, 1270, 1270, 1302, 1206, -- /* 310 */ 1340, 1340, 1349, 1355, 1264, 1340, 1264, 1340, 1302, 1206, -- /* 320 */ 1445, 1561, 1302, 1206, 1419, 1302, 1206, 1302, 1206, 1419, -- /* 330 */ 1262, 1262, 1262, 1251, 1188, 1188, 1419, 1262, 1236, 1262, -- /* 340 */ 1251, 1262, 1262, 1512, 1419, 1423, 1423, 1419, 1320, 1315, -- /* 350 */ 1320, 1315, 1320, 1315, 1320, 1315, 1302, 1504, 1504, 1330, -- /* 360 */ 1330, 1335, 1321, 1414, 1302, 1188, 1335, 1333, 1331, 1340, -- /* 370 */ 1212, 1254, 1526, 1526, 1522, 1522, 1522, 1572, 1572, 1477, -- /* 380 */ 1538, 1219, 1219, 1219, 1219, 1538, 1238, 1238, 1220, 1220, -- /* 390 */ 1219, 1538, 1188, 1188, 1188, 1188, 1188, 1188, 1533, 1188, -- /* 400 */ 1430, 1306, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 410 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 420 */ 1188, 1188, 1188, 1188, 1188, 1360, 1188, 1191, 1474, 1188, -- /* 430 */ 1188, 1472, 1188, 1188, 1188, 1188, 1188, 1188, 1307, 1188, -- /* 440 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 450 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1563, 1188, 1188, -- /* 460 */ 1188, 1188, 1188, 1188, 1444, 1443, 1188, 1188, 1304, 1188, -- /* 470 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 480 */ 1188, 1188, 1234, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 490 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 500 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1332, -- /* 510 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 520 */ 1188, 1188, 1188, 1188, 1509, 1322, 1188, 1188, 1554, 1188, -- /* 530 */ 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -- /* 540 */ 1188, 1188, 1188, 1549, 1278, 1362, 1188, 1361, 1365, 1188, -- /* 550 */ 1200, 1188, 1188, -+ /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254, -+ /* 10 */ 1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397, -+ /* 20 */ 1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254, -+ /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254, -+ /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254, -+ /* 60 */ 1492, 1493, 1254, 1254, 1254, 1254, 1543, 1545, 1508, 1420, -+ /* 70 */ 1419, 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, -+ /* 80 */ 1486, 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, -+ /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 140 */ 1254, 1254, 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, -+ /* 150 */ 1456, 1458, 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, -+ /* 160 */ 1254, 1254, 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, -+ /* 170 */ 1473, 1472, 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, -+ /* 180 */ 1254, 1254, 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 200 */ 1254, 1254, 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, -+ /* 210 */ 1578, 1578, 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, -+ /* 220 */ 1358, 1358, 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, -+ /* 240 */ 1546, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, -+ /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, -+ /* 280 */ 1254, 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, -+ /* 290 */ 1357, 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, -+ /* 300 */ 1423, 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, -+ /* 310 */ 1397, 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, -+ /* 320 */ 1357, 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, -+ /* 330 */ 1638, 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, -+ /* 340 */ 1638, 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, -+ /* 350 */ 1525, 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, -+ /* 360 */ 1330, 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, -+ /* 370 */ 1319, 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, -+ /* 380 */ 1588, 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, -+ /* 390 */ 1401, 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, -+ /* 400 */ 1558, 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, -+ /* 410 */ 1288, 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, -+ /* 420 */ 1254, 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 440 */ 1564, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 450 */ 1254, 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, -+ /* 460 */ 1254, 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, -+ /* 470 */ 1254, 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, -+ /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, -+ /* 490 */ 1254, 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, -+ /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 510 */ 1254, 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 530 */ 1254, 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, -+ /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 550 */ 1254, 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, -+ /* 560 */ 1254, 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, -+ /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254, -+ /* 580 */ 1266, 1254, 1254, - }; - /********** End of lemon-generated parsing tables *****************************/ - -@@ -155196,52 +176564,53 @@ static const YYACTIONTYPE yy_default[] = { - static const YYCODETYPE yyFallback[] = { - 0, /* $ => nothing */ - 0, /* SEMI => nothing */ -- 59, /* EXPLAIN => ID */ -- 59, /* QUERY => ID */ -- 59, /* PLAN => ID */ -- 59, /* BEGIN => ID */ -+ 60, /* EXPLAIN => ID */ -+ 60, /* QUERY => ID */ -+ 60, /* PLAN => ID */ -+ 60, /* BEGIN => ID */ - 0, /* TRANSACTION => nothing */ -- 59, /* DEFERRED => ID */ -- 59, /* IMMEDIATE => ID */ -- 59, /* EXCLUSIVE => ID */ -+ 60, /* DEFERRED => ID */ -+ 60, /* IMMEDIATE => ID */ -+ 60, /* EXCLUSIVE => ID */ - 0, /* COMMIT => nothing */ -- 59, /* END => ID */ -- 59, /* ROLLBACK => ID */ -- 59, /* SAVEPOINT => ID */ -- 59, /* RELEASE => ID */ -+ 60, /* END => ID */ -+ 60, /* ROLLBACK => ID */ -+ 60, /* SAVEPOINT => ID */ -+ 60, /* RELEASE => ID */ - 0, /* TO => nothing */ - 0, /* TABLE => nothing */ - 0, /* CREATE => nothing */ -- 59, /* IF => ID */ -+ 60, /* IF => ID */ - 0, /* NOT => nothing */ - 0, /* EXISTS => nothing */ -- 59, /* TEMP => ID */ -+ 60, /* TEMP => ID */ - 0, /* LP => nothing */ - 0, /* RP => nothing */ - 0, /* AS => nothing */ -- 59, /* WITHOUT => ID */ - 0, /* COMMA => nothing */ -- 59, /* ABORT => ID */ -- 59, /* ACTION => ID */ -- 59, /* AFTER => ID */ -- 59, /* ANALYZE => ID */ -- 59, /* ASC => ID */ -- 59, /* ATTACH => ID */ -- 59, /* BEFORE => ID */ -- 59, /* BY => ID */ -- 59, /* CASCADE => ID */ -- 59, /* CAST => ID */ -- 59, /* CONFLICT => ID */ -- 59, /* DATABASE => ID */ -- 59, /* DESC => ID */ -- 59, /* DETACH => ID */ -- 59, /* EACH => ID */ -- 59, /* FAIL => ID */ -+ 60, /* WITHOUT => ID */ -+ 60, /* ABORT => ID */ -+ 60, /* ACTION => ID */ -+ 60, /* AFTER => ID */ -+ 60, /* ANALYZE => ID */ -+ 60, /* ASC => ID */ -+ 60, /* ATTACH => ID */ -+ 60, /* BEFORE => ID */ -+ 60, /* BY => ID */ -+ 60, /* CASCADE => ID */ -+ 60, /* CAST => ID */ -+ 60, /* CONFLICT => ID */ -+ 60, /* DATABASE => ID */ -+ 60, /* DESC => ID */ -+ 60, /* DETACH => ID */ -+ 60, /* EACH => ID */ -+ 60, /* FAIL => ID */ - 0, /* OR => nothing */ - 0, /* AND => nothing */ - 0, /* IS => nothing */ -- 59, /* MATCH => ID */ -- 59, /* LIKE_KW => ID */ -+ 0, /* ISNOT => nothing */ -+ 60, /* MATCH => ID */ -+ 60, /* LIKE_KW => ID */ - 0, /* BETWEEN => nothing */ - 0, /* IN => nothing */ - 0, /* ISNULL => nothing */ -@@ -155254,46 +176623,47 @@ static const YYCODETYPE yyFallback[] = { - 0, /* GE => nothing */ - 0, /* ESCAPE => nothing */ - 0, /* ID => nothing */ -- 59, /* COLUMNKW => ID */ -- 59, /* DO => ID */ -- 59, /* FOR => ID */ -- 59, /* IGNORE => ID */ -- 59, /* INITIALLY => ID */ -- 59, /* INSTEAD => ID */ -- 59, /* NO => ID */ -- 59, /* KEY => ID */ -- 59, /* OF => ID */ -- 59, /* OFFSET => ID */ -- 59, /* PRAGMA => ID */ -- 59, /* RAISE => ID */ -- 59, /* RECURSIVE => ID */ -- 59, /* REPLACE => ID */ -- 59, /* RESTRICT => ID */ -- 59, /* ROW => ID */ -- 59, /* ROWS => ID */ -- 59, /* TRIGGER => ID */ -- 59, /* VACUUM => ID */ -- 59, /* VIEW => ID */ -- 59, /* VIRTUAL => ID */ -- 59, /* WITH => ID */ -- 59, /* NULLS => ID */ -- 59, /* FIRST => ID */ -- 59, /* LAST => ID */ -- 59, /* CURRENT => ID */ -- 59, /* FOLLOWING => ID */ -- 59, /* PARTITION => ID */ -- 59, /* PRECEDING => ID */ -- 59, /* RANGE => ID */ -- 59, /* UNBOUNDED => ID */ -- 59, /* EXCLUDE => ID */ -- 59, /* GROUPS => ID */ -- 59, /* OTHERS => ID */ -- 59, /* TIES => ID */ -- 59, /* GENERATED => ID */ -- 59, /* ALWAYS => ID */ -- 59, /* REINDEX => ID */ -- 59, /* RENAME => ID */ -- 59, /* CTIME_KW => ID */ -+ 60, /* COLUMNKW => ID */ -+ 60, /* DO => ID */ -+ 60, /* FOR => ID */ -+ 60, /* IGNORE => ID */ -+ 60, /* INITIALLY => ID */ -+ 60, /* INSTEAD => ID */ -+ 60, /* NO => ID */ -+ 60, /* KEY => ID */ -+ 60, /* OF => ID */ -+ 60, /* OFFSET => ID */ -+ 60, /* PRAGMA => ID */ -+ 60, /* RAISE => ID */ -+ 60, /* RECURSIVE => ID */ -+ 60, /* REPLACE => ID */ -+ 60, /* RESTRICT => ID */ -+ 60, /* ROW => ID */ -+ 60, /* ROWS => ID */ -+ 60, /* TRIGGER => ID */ -+ 60, /* VACUUM => ID */ -+ 60, /* VIEW => ID */ -+ 60, /* VIRTUAL => ID */ -+ 60, /* WITH => ID */ -+ 60, /* NULLS => ID */ -+ 60, /* FIRST => ID */ -+ 60, /* LAST => ID */ -+ 60, /* CURRENT => ID */ -+ 60, /* FOLLOWING => ID */ -+ 60, /* PARTITION => ID */ -+ 60, /* PRECEDING => ID */ -+ 60, /* RANGE => ID */ -+ 60, /* UNBOUNDED => ID */ -+ 60, /* EXCLUDE => ID */ -+ 60, /* GROUPS => ID */ -+ 60, /* OTHERS => ID */ -+ 60, /* TIES => ID */ -+ 60, /* GENERATED => ID */ -+ 60, /* ALWAYS => ID */ -+ 60, /* MATERIALIZED => ID */ -+ 60, /* REINDEX => ID */ -+ 60, /* RENAME => ID */ -+ 60, /* CTIME_KW => ID */ - 0, /* ANY => nothing */ - 0, /* BITAND => nothing */ - 0, /* BITOR => nothing */ -@@ -155305,6 +176675,7 @@ static const YYCODETYPE yyFallback[] = { - 0, /* SLASH => nothing */ - 0, /* REM => nothing */ - 0, /* CONCAT => nothing */ -+ 0, /* PTR => nothing */ - 0, /* COLLATE => nothing */ - 0, /* BITNOT => nothing */ - 0, /* ON => nothing */ -@@ -155342,6 +176713,7 @@ static const YYCODETYPE yyFallback[] = { - 0, /* HAVING => nothing */ - 0, /* LIMIT => nothing */ - 0, /* WHERE => nothing */ -+ 0, /* RETURNING => nothing */ - 0, /* INTO => nothing */ - 0, /* NOTHING => nothing */ - 0, /* FLOAT => nothing */ -@@ -155362,10 +176734,9 @@ static const YYCODETYPE yyFallback[] = { - 0, /* AGG_FUNCTION => nothing */ - 0, /* AGG_COLUMN => nothing */ - 0, /* TRUEFALSE => nothing */ -- 0, /* ISNOT => nothing */ - 0, /* FUNCTION => nothing */ -- 0, /* UMINUS => nothing */ - 0, /* UPLUS => nothing */ -+ 0, /* UMINUS => nothing */ - 0, /* TRUTH => nothing */ - 0, /* REGISTER => nothing */ - 0, /* VECTOR => nothing */ -@@ -155373,7 +176744,10 @@ static const YYCODETYPE yyFallback[] = { - 0, /* IF_NULL_ROW => nothing */ - 0, /* ASTERISK => nothing */ - 0, /* SPAN => nothing */ -+ 0, /* ERROR => nothing */ -+ 0, /* QNUMBER => nothing */ - 0, /* SPACE => nothing */ -+ 0, /* COMMENT => nothing */ - 0, /* ILLEGAL => nothing */ - }; - #endif /* YYFALLBACK */ -@@ -155415,17 +176789,13 @@ struct yyParser { - #endif - sqlite3ParserARG_SDECL /* A place to hold %extra_argument */ - sqlite3ParserCTX_SDECL /* A place to hold %extra_context */ --#if YYSTACKDEPTH<=0 -- int yystksz; /* Current side of the stack */ -- yyStackEntry *yystack; /* The parser's stack */ -- yyStackEntry yystk0; /* First stack entry */ --#else -- yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ -- yyStackEntry *yystackEnd; /* Last entry in the stack */ --#endif -+ yyStackEntry *yystackEnd; /* Last entry in the stack */ -+ yyStackEntry *yystack; /* The parser stack */ -+ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ - }; - typedef struct yyParser yyParser; - -+/* #include */ - #ifndef NDEBUG - /* #include */ - static FILE *yyTraceFILE = 0; -@@ -155487,8 +176857,8 @@ static const char *const yyTokenName[] = { - /* 22 */ "LP", - /* 23 */ "RP", - /* 24 */ "AS", -- /* 25 */ "WITHOUT", -- /* 26 */ "COMMA", -+ /* 25 */ "COMMA", -+ /* 26 */ "WITHOUT", - /* 27 */ "ABORT", - /* 28 */ "ACTION", - /* 29 */ "AFTER", -@@ -155508,270 +176878,283 @@ static const char *const yyTokenName[] = { - /* 43 */ "OR", - /* 44 */ "AND", - /* 45 */ "IS", -- /* 46 */ "MATCH", -- /* 47 */ "LIKE_KW", -- /* 48 */ "BETWEEN", -- /* 49 */ "IN", -- /* 50 */ "ISNULL", -- /* 51 */ "NOTNULL", -- /* 52 */ "NE", -- /* 53 */ "EQ", -- /* 54 */ "GT", -- /* 55 */ "LE", -- /* 56 */ "LT", -- /* 57 */ "GE", -- /* 58 */ "ESCAPE", -- /* 59 */ "ID", -- /* 60 */ "COLUMNKW", -- /* 61 */ "DO", -- /* 62 */ "FOR", -- /* 63 */ "IGNORE", -- /* 64 */ "INITIALLY", -- /* 65 */ "INSTEAD", -- /* 66 */ "NO", -- /* 67 */ "KEY", -- /* 68 */ "OF", -- /* 69 */ "OFFSET", -- /* 70 */ "PRAGMA", -- /* 71 */ "RAISE", -- /* 72 */ "RECURSIVE", -- /* 73 */ "REPLACE", -- /* 74 */ "RESTRICT", -- /* 75 */ "ROW", -- /* 76 */ "ROWS", -- /* 77 */ "TRIGGER", -- /* 78 */ "VACUUM", -- /* 79 */ "VIEW", -- /* 80 */ "VIRTUAL", -- /* 81 */ "WITH", -- /* 82 */ "NULLS", -- /* 83 */ "FIRST", -- /* 84 */ "LAST", -- /* 85 */ "CURRENT", -- /* 86 */ "FOLLOWING", -- /* 87 */ "PARTITION", -- /* 88 */ "PRECEDING", -- /* 89 */ "RANGE", -- /* 90 */ "UNBOUNDED", -- /* 91 */ "EXCLUDE", -- /* 92 */ "GROUPS", -- /* 93 */ "OTHERS", -- /* 94 */ "TIES", -- /* 95 */ "GENERATED", -- /* 96 */ "ALWAYS", -- /* 97 */ "REINDEX", -- /* 98 */ "RENAME", -- /* 99 */ "CTIME_KW", -- /* 100 */ "ANY", -- /* 101 */ "BITAND", -- /* 102 */ "BITOR", -- /* 103 */ "LSHIFT", -- /* 104 */ "RSHIFT", -- /* 105 */ "PLUS", -- /* 106 */ "MINUS", -- /* 107 */ "STAR", -- /* 108 */ "SLASH", -- /* 109 */ "REM", -- /* 110 */ "CONCAT", -- /* 111 */ "COLLATE", -- /* 112 */ "BITNOT", -- /* 113 */ "ON", -- /* 114 */ "INDEXED", -- /* 115 */ "STRING", -- /* 116 */ "JOIN_KW", -- /* 117 */ "CONSTRAINT", -- /* 118 */ "DEFAULT", -- /* 119 */ "NULL", -- /* 120 */ "PRIMARY", -- /* 121 */ "UNIQUE", -- /* 122 */ "CHECK", -- /* 123 */ "REFERENCES", -- /* 124 */ "AUTOINCR", -- /* 125 */ "INSERT", -- /* 126 */ "DELETE", -- /* 127 */ "UPDATE", -- /* 128 */ "SET", -- /* 129 */ "DEFERRABLE", -- /* 130 */ "FOREIGN", -- /* 131 */ "DROP", -- /* 132 */ "UNION", -- /* 133 */ "ALL", -- /* 134 */ "EXCEPT", -- /* 135 */ "INTERSECT", -- /* 136 */ "SELECT", -- /* 137 */ "VALUES", -- /* 138 */ "DISTINCT", -- /* 139 */ "DOT", -- /* 140 */ "FROM", -- /* 141 */ "JOIN", -- /* 142 */ "USING", -- /* 143 */ "ORDER", -- /* 144 */ "GROUP", -- /* 145 */ "HAVING", -- /* 146 */ "LIMIT", -- /* 147 */ "WHERE", -- /* 148 */ "INTO", -- /* 149 */ "NOTHING", -- /* 150 */ "FLOAT", -- /* 151 */ "BLOB", -- /* 152 */ "INTEGER", -- /* 153 */ "VARIABLE", -- /* 154 */ "CASE", -- /* 155 */ "WHEN", -- /* 156 */ "THEN", -- /* 157 */ "ELSE", -- /* 158 */ "INDEX", -- /* 159 */ "ALTER", -- /* 160 */ "ADD", -- /* 161 */ "WINDOW", -- /* 162 */ "OVER", -- /* 163 */ "FILTER", -- /* 164 */ "COLUMN", -- /* 165 */ "AGG_FUNCTION", -- /* 166 */ "AGG_COLUMN", -- /* 167 */ "TRUEFALSE", -- /* 168 */ "ISNOT", -- /* 169 */ "FUNCTION", -- /* 170 */ "UMINUS", -- /* 171 */ "UPLUS", -- /* 172 */ "TRUTH", -- /* 173 */ "REGISTER", -- /* 174 */ "VECTOR", -- /* 175 */ "SELECT_COLUMN", -- /* 176 */ "IF_NULL_ROW", -- /* 177 */ "ASTERISK", -- /* 178 */ "SPAN", -- /* 179 */ "SPACE", -- /* 180 */ "ILLEGAL", -- /* 181 */ "input", -- /* 182 */ "cmdlist", -- /* 183 */ "ecmd", -- /* 184 */ "cmdx", -- /* 185 */ "explain", -- /* 186 */ "cmd", -- /* 187 */ "transtype", -- /* 188 */ "trans_opt", -- /* 189 */ "nm", -- /* 190 */ "savepoint_opt", -- /* 191 */ "create_table", -- /* 192 */ "create_table_args", -- /* 193 */ "createkw", -- /* 194 */ "temp", -- /* 195 */ "ifnotexists", -- /* 196 */ "dbnm", -- /* 197 */ "columnlist", -- /* 198 */ "conslist_opt", -- /* 199 */ "table_options", -- /* 200 */ "select", -- /* 201 */ "columnname", -- /* 202 */ "carglist", -- /* 203 */ "typetoken", -- /* 204 */ "typename", -- /* 205 */ "signed", -- /* 206 */ "plus_num", -- /* 207 */ "minus_num", -- /* 208 */ "scanpt", -- /* 209 */ "scantok", -- /* 210 */ "ccons", -- /* 211 */ "term", -- /* 212 */ "expr", -- /* 213 */ "onconf", -- /* 214 */ "sortorder", -- /* 215 */ "autoinc", -- /* 216 */ "eidlist_opt", -- /* 217 */ "refargs", -- /* 218 */ "defer_subclause", -- /* 219 */ "generated", -- /* 220 */ "refarg", -- /* 221 */ "refact", -- /* 222 */ "init_deferred_pred_opt", -- /* 223 */ "conslist", -- /* 224 */ "tconscomma", -- /* 225 */ "tcons", -- /* 226 */ "sortlist", -- /* 227 */ "eidlist", -- /* 228 */ "defer_subclause_opt", -- /* 229 */ "orconf", -- /* 230 */ "resolvetype", -- /* 231 */ "raisetype", -- /* 232 */ "ifexists", -- /* 233 */ "fullname", -- /* 234 */ "selectnowith", -- /* 235 */ "oneselect", -- /* 236 */ "wqlist", -- /* 237 */ "multiselect_op", -- /* 238 */ "distinct", -- /* 239 */ "selcollist", -- /* 240 */ "from", -- /* 241 */ "where_opt", -- /* 242 */ "groupby_opt", -- /* 243 */ "having_opt", -- /* 244 */ "orderby_opt", -- /* 245 */ "limit_opt", -- /* 246 */ "window_clause", -- /* 247 */ "values", -- /* 248 */ "nexprlist", -- /* 249 */ "sclp", -- /* 250 */ "as", -- /* 251 */ "seltablist", -- /* 252 */ "stl_prefix", -- /* 253 */ "joinop", -- /* 254 */ "indexed_opt", -- /* 255 */ "on_opt", -- /* 256 */ "using_opt", -- /* 257 */ "exprlist", -- /* 258 */ "xfullname", -- /* 259 */ "idlist", -- /* 260 */ "nulls", -- /* 261 */ "with", -- /* 262 */ "setlist", -- /* 263 */ "insert_cmd", -- /* 264 */ "idlist_opt", -- /* 265 */ "upsert", -- /* 266 */ "filter_over", -- /* 267 */ "likeop", -- /* 268 */ "between_op", -- /* 269 */ "in_op", -- /* 270 */ "paren_exprlist", -- /* 271 */ "case_operand", -- /* 272 */ "case_exprlist", -- /* 273 */ "case_else", -- /* 274 */ "uniqueflag", -- /* 275 */ "collate", -- /* 276 */ "vinto", -- /* 277 */ "nmnum", -- /* 278 */ "trigger_decl", -- /* 279 */ "trigger_cmd_list", -- /* 280 */ "trigger_time", -- /* 281 */ "trigger_event", -- /* 282 */ "foreach_clause", -- /* 283 */ "when_clause", -- /* 284 */ "trigger_cmd", -- /* 285 */ "trnm", -- /* 286 */ "tridxby", -- /* 287 */ "database_kw_opt", -- /* 288 */ "key_opt", -- /* 289 */ "add_column_fullname", -- /* 290 */ "kwcolumn_opt", -- /* 291 */ "create_vtab", -- /* 292 */ "vtabarglist", -- /* 293 */ "vtabarg", -- /* 294 */ "vtabargtoken", -- /* 295 */ "lp", -- /* 296 */ "anylist", -- /* 297 */ "windowdefn_list", -- /* 298 */ "windowdefn", -- /* 299 */ "window", -- /* 300 */ "frame_opt", -- /* 301 */ "part_opt", -- /* 302 */ "filter_clause", -- /* 303 */ "over_clause", -- /* 304 */ "range_or_rows", -- /* 305 */ "frame_bound", -- /* 306 */ "frame_bound_s", -- /* 307 */ "frame_bound_e", -- /* 308 */ "frame_exclude_opt", -- /* 309 */ "frame_exclude", -+ /* 46 */ "ISNOT", -+ /* 47 */ "MATCH", -+ /* 48 */ "LIKE_KW", -+ /* 49 */ "BETWEEN", -+ /* 50 */ "IN", -+ /* 51 */ "ISNULL", -+ /* 52 */ "NOTNULL", -+ /* 53 */ "NE", -+ /* 54 */ "EQ", -+ /* 55 */ "GT", -+ /* 56 */ "LE", -+ /* 57 */ "LT", -+ /* 58 */ "GE", -+ /* 59 */ "ESCAPE", -+ /* 60 */ "ID", -+ /* 61 */ "COLUMNKW", -+ /* 62 */ "DO", -+ /* 63 */ "FOR", -+ /* 64 */ "IGNORE", -+ /* 65 */ "INITIALLY", -+ /* 66 */ "INSTEAD", -+ /* 67 */ "NO", -+ /* 68 */ "KEY", -+ /* 69 */ "OF", -+ /* 70 */ "OFFSET", -+ /* 71 */ "PRAGMA", -+ /* 72 */ "RAISE", -+ /* 73 */ "RECURSIVE", -+ /* 74 */ "REPLACE", -+ /* 75 */ "RESTRICT", -+ /* 76 */ "ROW", -+ /* 77 */ "ROWS", -+ /* 78 */ "TRIGGER", -+ /* 79 */ "VACUUM", -+ /* 80 */ "VIEW", -+ /* 81 */ "VIRTUAL", -+ /* 82 */ "WITH", -+ /* 83 */ "NULLS", -+ /* 84 */ "FIRST", -+ /* 85 */ "LAST", -+ /* 86 */ "CURRENT", -+ /* 87 */ "FOLLOWING", -+ /* 88 */ "PARTITION", -+ /* 89 */ "PRECEDING", -+ /* 90 */ "RANGE", -+ /* 91 */ "UNBOUNDED", -+ /* 92 */ "EXCLUDE", -+ /* 93 */ "GROUPS", -+ /* 94 */ "OTHERS", -+ /* 95 */ "TIES", -+ /* 96 */ "GENERATED", -+ /* 97 */ "ALWAYS", -+ /* 98 */ "MATERIALIZED", -+ /* 99 */ "REINDEX", -+ /* 100 */ "RENAME", -+ /* 101 */ "CTIME_KW", -+ /* 102 */ "ANY", -+ /* 103 */ "BITAND", -+ /* 104 */ "BITOR", -+ /* 105 */ "LSHIFT", -+ /* 106 */ "RSHIFT", -+ /* 107 */ "PLUS", -+ /* 108 */ "MINUS", -+ /* 109 */ "STAR", -+ /* 110 */ "SLASH", -+ /* 111 */ "REM", -+ /* 112 */ "CONCAT", -+ /* 113 */ "PTR", -+ /* 114 */ "COLLATE", -+ /* 115 */ "BITNOT", -+ /* 116 */ "ON", -+ /* 117 */ "INDEXED", -+ /* 118 */ "STRING", -+ /* 119 */ "JOIN_KW", -+ /* 120 */ "CONSTRAINT", -+ /* 121 */ "DEFAULT", -+ /* 122 */ "NULL", -+ /* 123 */ "PRIMARY", -+ /* 124 */ "UNIQUE", -+ /* 125 */ "CHECK", -+ /* 126 */ "REFERENCES", -+ /* 127 */ "AUTOINCR", -+ /* 128 */ "INSERT", -+ /* 129 */ "DELETE", -+ /* 130 */ "UPDATE", -+ /* 131 */ "SET", -+ /* 132 */ "DEFERRABLE", -+ /* 133 */ "FOREIGN", -+ /* 134 */ "DROP", -+ /* 135 */ "UNION", -+ /* 136 */ "ALL", -+ /* 137 */ "EXCEPT", -+ /* 138 */ "INTERSECT", -+ /* 139 */ "SELECT", -+ /* 140 */ "VALUES", -+ /* 141 */ "DISTINCT", -+ /* 142 */ "DOT", -+ /* 143 */ "FROM", -+ /* 144 */ "JOIN", -+ /* 145 */ "USING", -+ /* 146 */ "ORDER", -+ /* 147 */ "GROUP", -+ /* 148 */ "HAVING", -+ /* 149 */ "LIMIT", -+ /* 150 */ "WHERE", -+ /* 151 */ "RETURNING", -+ /* 152 */ "INTO", -+ /* 153 */ "NOTHING", -+ /* 154 */ "FLOAT", -+ /* 155 */ "BLOB", -+ /* 156 */ "INTEGER", -+ /* 157 */ "VARIABLE", -+ /* 158 */ "CASE", -+ /* 159 */ "WHEN", -+ /* 160 */ "THEN", -+ /* 161 */ "ELSE", -+ /* 162 */ "INDEX", -+ /* 163 */ "ALTER", -+ /* 164 */ "ADD", -+ /* 165 */ "WINDOW", -+ /* 166 */ "OVER", -+ /* 167 */ "FILTER", -+ /* 168 */ "COLUMN", -+ /* 169 */ "AGG_FUNCTION", -+ /* 170 */ "AGG_COLUMN", -+ /* 171 */ "TRUEFALSE", -+ /* 172 */ "FUNCTION", -+ /* 173 */ "UPLUS", -+ /* 174 */ "UMINUS", -+ /* 175 */ "TRUTH", -+ /* 176 */ "REGISTER", -+ /* 177 */ "VECTOR", -+ /* 178 */ "SELECT_COLUMN", -+ /* 179 */ "IF_NULL_ROW", -+ /* 180 */ "ASTERISK", -+ /* 181 */ "SPAN", -+ /* 182 */ "ERROR", -+ /* 183 */ "QNUMBER", -+ /* 184 */ "SPACE", -+ /* 185 */ "COMMENT", -+ /* 186 */ "ILLEGAL", -+ /* 187 */ "input", -+ /* 188 */ "cmdlist", -+ /* 189 */ "ecmd", -+ /* 190 */ "cmdx", -+ /* 191 */ "explain", -+ /* 192 */ "cmd", -+ /* 193 */ "transtype", -+ /* 194 */ "trans_opt", -+ /* 195 */ "nm", -+ /* 196 */ "savepoint_opt", -+ /* 197 */ "create_table", -+ /* 198 */ "create_table_args", -+ /* 199 */ "createkw", -+ /* 200 */ "temp", -+ /* 201 */ "ifnotexists", -+ /* 202 */ "dbnm", -+ /* 203 */ "columnlist", -+ /* 204 */ "conslist_opt", -+ /* 205 */ "table_option_set", -+ /* 206 */ "select", -+ /* 207 */ "table_option", -+ /* 208 */ "columnname", -+ /* 209 */ "carglist", -+ /* 210 */ "typetoken", -+ /* 211 */ "typename", -+ /* 212 */ "signed", -+ /* 213 */ "plus_num", -+ /* 214 */ "minus_num", -+ /* 215 */ "scanpt", -+ /* 216 */ "scantok", -+ /* 217 */ "ccons", -+ /* 218 */ "term", -+ /* 219 */ "expr", -+ /* 220 */ "onconf", -+ /* 221 */ "sortorder", -+ /* 222 */ "autoinc", -+ /* 223 */ "eidlist_opt", -+ /* 224 */ "refargs", -+ /* 225 */ "defer_subclause", -+ /* 226 */ "generated", -+ /* 227 */ "refarg", -+ /* 228 */ "refact", -+ /* 229 */ "init_deferred_pred_opt", -+ /* 230 */ "conslist", -+ /* 231 */ "tconscomma", -+ /* 232 */ "tcons", -+ /* 233 */ "sortlist", -+ /* 234 */ "eidlist", -+ /* 235 */ "defer_subclause_opt", -+ /* 236 */ "orconf", -+ /* 237 */ "resolvetype", -+ /* 238 */ "raisetype", -+ /* 239 */ "ifexists", -+ /* 240 */ "fullname", -+ /* 241 */ "selectnowith", -+ /* 242 */ "oneselect", -+ /* 243 */ "wqlist", -+ /* 244 */ "multiselect_op", -+ /* 245 */ "distinct", -+ /* 246 */ "selcollist", -+ /* 247 */ "from", -+ /* 248 */ "where_opt", -+ /* 249 */ "groupby_opt", -+ /* 250 */ "having_opt", -+ /* 251 */ "orderby_opt", -+ /* 252 */ "limit_opt", -+ /* 253 */ "window_clause", -+ /* 254 */ "values", -+ /* 255 */ "nexprlist", -+ /* 256 */ "mvalues", -+ /* 257 */ "sclp", -+ /* 258 */ "as", -+ /* 259 */ "seltablist", -+ /* 260 */ "stl_prefix", -+ /* 261 */ "joinop", -+ /* 262 */ "on_using", -+ /* 263 */ "indexed_by", -+ /* 264 */ "exprlist", -+ /* 265 */ "xfullname", -+ /* 266 */ "idlist", -+ /* 267 */ "indexed_opt", -+ /* 268 */ "nulls", -+ /* 269 */ "with", -+ /* 270 */ "where_opt_ret", -+ /* 271 */ "setlist", -+ /* 272 */ "insert_cmd", -+ /* 273 */ "idlist_opt", -+ /* 274 */ "upsert", -+ /* 275 */ "returning", -+ /* 276 */ "filter_over", -+ /* 277 */ "likeop", -+ /* 278 */ "between_op", -+ /* 279 */ "in_op", -+ /* 280 */ "paren_exprlist", -+ /* 281 */ "case_operand", -+ /* 282 */ "case_exprlist", -+ /* 283 */ "case_else", -+ /* 284 */ "uniqueflag", -+ /* 285 */ "collate", -+ /* 286 */ "vinto", -+ /* 287 */ "nmnum", -+ /* 288 */ "trigger_decl", -+ /* 289 */ "trigger_cmd_list", -+ /* 290 */ "trigger_time", -+ /* 291 */ "trigger_event", -+ /* 292 */ "foreach_clause", -+ /* 293 */ "when_clause", -+ /* 294 */ "trigger_cmd", -+ /* 295 */ "trnm", -+ /* 296 */ "tridxby", -+ /* 297 */ "database_kw_opt", -+ /* 298 */ "key_opt", -+ /* 299 */ "add_column_fullname", -+ /* 300 */ "kwcolumn_opt", -+ /* 301 */ "create_vtab", -+ /* 302 */ "vtabarglist", -+ /* 303 */ "vtabarg", -+ /* 304 */ "vtabargtoken", -+ /* 305 */ "lp", -+ /* 306 */ "anylist", -+ /* 307 */ "wqitem", -+ /* 308 */ "wqas", -+ /* 309 */ "withnm", -+ /* 310 */ "windowdefn_list", -+ /* 311 */ "windowdefn", -+ /* 312 */ "window", -+ /* 313 */ "frame_opt", -+ /* 314 */ "part_opt", -+ /* 315 */ "filter_clause", -+ /* 316 */ "over_clause", -+ /* 317 */ "range_or_rows", -+ /* 318 */ "frame_bound", -+ /* 319 */ "frame_bound_s", -+ /* 320 */ "frame_bound_e", -+ /* 321 */ "frame_exclude_opt", -+ /* 322 */ "frame_exclude", - }; - #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ - -@@ -155798,407 +177181,439 @@ static const char *const yyRuleName[] = { - /* 16 */ "ifnotexists ::= IF NOT EXISTS", - /* 17 */ "temp ::= TEMP", - /* 18 */ "temp ::=", -- /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_options", -+ /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_option_set", - /* 20 */ "create_table_args ::= AS select", -- /* 21 */ "table_options ::=", -- /* 22 */ "table_options ::= WITHOUT nm", -- /* 23 */ "columnname ::= nm typetoken", -- /* 24 */ "typetoken ::=", -- /* 25 */ "typetoken ::= typename LP signed RP", -- /* 26 */ "typetoken ::= typename LP signed COMMA signed RP", -- /* 27 */ "typename ::= typename ID|STRING", -- /* 28 */ "scanpt ::=", -- /* 29 */ "scantok ::=", -- /* 30 */ "ccons ::= CONSTRAINT nm", -- /* 31 */ "ccons ::= DEFAULT scantok term", -- /* 32 */ "ccons ::= DEFAULT LP expr RP", -- /* 33 */ "ccons ::= DEFAULT PLUS scantok term", -- /* 34 */ "ccons ::= DEFAULT MINUS scantok term", -- /* 35 */ "ccons ::= DEFAULT scantok ID|INDEXED", -- /* 36 */ "ccons ::= NOT NULL onconf", -- /* 37 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", -- /* 38 */ "ccons ::= UNIQUE onconf", -- /* 39 */ "ccons ::= CHECK LP expr RP", -- /* 40 */ "ccons ::= REFERENCES nm eidlist_opt refargs", -- /* 41 */ "ccons ::= defer_subclause", -- /* 42 */ "ccons ::= COLLATE ID|STRING", -- /* 43 */ "generated ::= LP expr RP", -- /* 44 */ "generated ::= LP expr RP ID", -- /* 45 */ "autoinc ::=", -- /* 46 */ "autoinc ::= AUTOINCR", -- /* 47 */ "refargs ::=", -- /* 48 */ "refargs ::= refargs refarg", -- /* 49 */ "refarg ::= MATCH nm", -- /* 50 */ "refarg ::= ON INSERT refact", -- /* 51 */ "refarg ::= ON DELETE refact", -- /* 52 */ "refarg ::= ON UPDATE refact", -- /* 53 */ "refact ::= SET NULL", -- /* 54 */ "refact ::= SET DEFAULT", -- /* 55 */ "refact ::= CASCADE", -- /* 56 */ "refact ::= RESTRICT", -- /* 57 */ "refact ::= NO ACTION", -- /* 58 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", -- /* 59 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", -- /* 60 */ "init_deferred_pred_opt ::=", -- /* 61 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", -- /* 62 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", -- /* 63 */ "conslist_opt ::=", -- /* 64 */ "tconscomma ::= COMMA", -- /* 65 */ "tcons ::= CONSTRAINT nm", -- /* 66 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", -- /* 67 */ "tcons ::= UNIQUE LP sortlist RP onconf", -- /* 68 */ "tcons ::= CHECK LP expr RP onconf", -- /* 69 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", -- /* 70 */ "defer_subclause_opt ::=", -- /* 71 */ "onconf ::=", -- /* 72 */ "onconf ::= ON CONFLICT resolvetype", -- /* 73 */ "orconf ::=", -- /* 74 */ "orconf ::= OR resolvetype", -- /* 75 */ "resolvetype ::= IGNORE", -- /* 76 */ "resolvetype ::= REPLACE", -- /* 77 */ "cmd ::= DROP TABLE ifexists fullname", -- /* 78 */ "ifexists ::= IF EXISTS", -- /* 79 */ "ifexists ::=", -- /* 80 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", -- /* 81 */ "cmd ::= DROP VIEW ifexists fullname", -- /* 82 */ "cmd ::= select", -- /* 83 */ "select ::= WITH wqlist selectnowith", -- /* 84 */ "select ::= WITH RECURSIVE wqlist selectnowith", -- /* 85 */ "select ::= selectnowith", -- /* 86 */ "selectnowith ::= selectnowith multiselect_op oneselect", -- /* 87 */ "multiselect_op ::= UNION", -- /* 88 */ "multiselect_op ::= UNION ALL", -- /* 89 */ "multiselect_op ::= EXCEPT|INTERSECT", -- /* 90 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", -- /* 91 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", -- /* 92 */ "values ::= VALUES LP nexprlist RP", -- /* 93 */ "values ::= values COMMA LP nexprlist RP", -- /* 94 */ "distinct ::= DISTINCT", -- /* 95 */ "distinct ::= ALL", -- /* 96 */ "distinct ::=", -- /* 97 */ "sclp ::=", -- /* 98 */ "selcollist ::= sclp scanpt expr scanpt as", -- /* 99 */ "selcollist ::= sclp scanpt STAR", -- /* 100 */ "selcollist ::= sclp scanpt nm DOT STAR", -- /* 101 */ "as ::= AS nm", -- /* 102 */ "as ::=", -- /* 103 */ "from ::=", -- /* 104 */ "from ::= FROM seltablist", -- /* 105 */ "stl_prefix ::= seltablist joinop", -- /* 106 */ "stl_prefix ::=", -- /* 107 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", -- /* 108 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", -- /* 109 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", -- /* 110 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", -- /* 111 */ "dbnm ::=", -- /* 112 */ "dbnm ::= DOT nm", -- /* 113 */ "fullname ::= nm", -- /* 114 */ "fullname ::= nm DOT nm", -- /* 115 */ "xfullname ::= nm", -- /* 116 */ "xfullname ::= nm DOT nm", -- /* 117 */ "xfullname ::= nm DOT nm AS nm", -- /* 118 */ "xfullname ::= nm AS nm", -- /* 119 */ "joinop ::= COMMA|JOIN", -- /* 120 */ "joinop ::= JOIN_KW JOIN", -- /* 121 */ "joinop ::= JOIN_KW nm JOIN", -- /* 122 */ "joinop ::= JOIN_KW nm nm JOIN", -- /* 123 */ "on_opt ::= ON expr", -- /* 124 */ "on_opt ::=", -- /* 125 */ "indexed_opt ::=", -- /* 126 */ "indexed_opt ::= INDEXED BY nm", -- /* 127 */ "indexed_opt ::= NOT INDEXED", -- /* 128 */ "using_opt ::= USING LP idlist RP", -- /* 129 */ "using_opt ::=", -- /* 130 */ "orderby_opt ::=", -- /* 131 */ "orderby_opt ::= ORDER BY sortlist", -- /* 132 */ "sortlist ::= sortlist COMMA expr sortorder nulls", -- /* 133 */ "sortlist ::= expr sortorder nulls", -- /* 134 */ "sortorder ::= ASC", -- /* 135 */ "sortorder ::= DESC", -- /* 136 */ "sortorder ::=", -- /* 137 */ "nulls ::= NULLS FIRST", -- /* 138 */ "nulls ::= NULLS LAST", -- /* 139 */ "nulls ::=", -- /* 140 */ "groupby_opt ::=", -- /* 141 */ "groupby_opt ::= GROUP BY nexprlist", -- /* 142 */ "having_opt ::=", -- /* 143 */ "having_opt ::= HAVING expr", -- /* 144 */ "limit_opt ::=", -- /* 145 */ "limit_opt ::= LIMIT expr", -- /* 146 */ "limit_opt ::= LIMIT expr OFFSET expr", -- /* 147 */ "limit_opt ::= LIMIT expr COMMA expr", -- /* 148 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt", -- /* 149 */ "where_opt ::=", -- /* 150 */ "where_opt ::= WHERE expr", -- /* 151 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt", -- /* 152 */ "setlist ::= setlist COMMA nm EQ expr", -- /* 153 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", -- /* 154 */ "setlist ::= nm EQ expr", -- /* 155 */ "setlist ::= LP idlist RP EQ expr", -- /* 156 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", -- /* 157 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES", -- /* 158 */ "upsert ::=", -- /* 159 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt", -- /* 160 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING", -- /* 161 */ "upsert ::= ON CONFLICT DO NOTHING", -- /* 162 */ "insert_cmd ::= INSERT orconf", -- /* 163 */ "insert_cmd ::= REPLACE", -- /* 164 */ "idlist_opt ::=", -- /* 165 */ "idlist_opt ::= LP idlist RP", -- /* 166 */ "idlist ::= idlist COMMA nm", -- /* 167 */ "idlist ::= nm", -- /* 168 */ "expr ::= LP expr RP", -- /* 169 */ "expr ::= ID|INDEXED", -- /* 170 */ "expr ::= JOIN_KW", -- /* 171 */ "expr ::= nm DOT nm", -- /* 172 */ "expr ::= nm DOT nm DOT nm", -- /* 173 */ "term ::= NULL|FLOAT|BLOB", -- /* 174 */ "term ::= STRING", -- /* 175 */ "term ::= INTEGER", -- /* 176 */ "expr ::= VARIABLE", -- /* 177 */ "expr ::= expr COLLATE ID|STRING", -- /* 178 */ "expr ::= CAST LP expr AS typetoken RP", -- /* 179 */ "expr ::= ID|INDEXED LP distinct exprlist RP", -- /* 180 */ "expr ::= ID|INDEXED LP STAR RP", -- /* 181 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", -- /* 182 */ "expr ::= ID|INDEXED LP STAR RP filter_over", -- /* 183 */ "term ::= CTIME_KW", -- /* 184 */ "expr ::= LP nexprlist COMMA expr RP", -- /* 185 */ "expr ::= expr AND expr", -- /* 186 */ "expr ::= expr OR expr", -- /* 187 */ "expr ::= expr LT|GT|GE|LE expr", -- /* 188 */ "expr ::= expr EQ|NE expr", -- /* 189 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", -- /* 190 */ "expr ::= expr PLUS|MINUS expr", -- /* 191 */ "expr ::= expr STAR|SLASH|REM expr", -- /* 192 */ "expr ::= expr CONCAT expr", -- /* 193 */ "likeop ::= NOT LIKE_KW|MATCH", -- /* 194 */ "expr ::= expr likeop expr", -- /* 195 */ "expr ::= expr likeop expr ESCAPE expr", -- /* 196 */ "expr ::= expr ISNULL|NOTNULL", -- /* 197 */ "expr ::= expr NOT NULL", -- /* 198 */ "expr ::= expr IS expr", -- /* 199 */ "expr ::= expr IS NOT expr", -- /* 200 */ "expr ::= NOT expr", -- /* 201 */ "expr ::= BITNOT expr", -- /* 202 */ "expr ::= PLUS|MINUS expr", -- /* 203 */ "between_op ::= BETWEEN", -- /* 204 */ "between_op ::= NOT BETWEEN", -- /* 205 */ "expr ::= expr between_op expr AND expr", -- /* 206 */ "in_op ::= IN", -- /* 207 */ "in_op ::= NOT IN", -- /* 208 */ "expr ::= expr in_op LP exprlist RP", -- /* 209 */ "expr ::= LP select RP", -- /* 210 */ "expr ::= expr in_op LP select RP", -- /* 211 */ "expr ::= expr in_op nm dbnm paren_exprlist", -- /* 212 */ "expr ::= EXISTS LP select RP", -- /* 213 */ "expr ::= CASE case_operand case_exprlist case_else END", -- /* 214 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", -- /* 215 */ "case_exprlist ::= WHEN expr THEN expr", -- /* 216 */ "case_else ::= ELSE expr", -- /* 217 */ "case_else ::=", -- /* 218 */ "case_operand ::= expr", -- /* 219 */ "case_operand ::=", -- /* 220 */ "exprlist ::=", -- /* 221 */ "nexprlist ::= nexprlist COMMA expr", -- /* 222 */ "nexprlist ::= expr", -- /* 223 */ "paren_exprlist ::=", -- /* 224 */ "paren_exprlist ::= LP exprlist RP", -- /* 225 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", -- /* 226 */ "uniqueflag ::= UNIQUE", -- /* 227 */ "uniqueflag ::=", -- /* 228 */ "eidlist_opt ::=", -- /* 229 */ "eidlist_opt ::= LP eidlist RP", -- /* 230 */ "eidlist ::= eidlist COMMA nm collate sortorder", -- /* 231 */ "eidlist ::= nm collate sortorder", -- /* 232 */ "collate ::=", -- /* 233 */ "collate ::= COLLATE ID|STRING", -- /* 234 */ "cmd ::= DROP INDEX ifexists fullname", -- /* 235 */ "cmd ::= VACUUM vinto", -- /* 236 */ "cmd ::= VACUUM nm vinto", -- /* 237 */ "vinto ::= INTO expr", -- /* 238 */ "vinto ::=", -- /* 239 */ "cmd ::= PRAGMA nm dbnm", -- /* 240 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", -- /* 241 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", -- /* 242 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", -- /* 243 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", -- /* 244 */ "plus_num ::= PLUS INTEGER|FLOAT", -- /* 245 */ "minus_num ::= MINUS INTEGER|FLOAT", -- /* 246 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", -- /* 247 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", -- /* 248 */ "trigger_time ::= BEFORE|AFTER", -- /* 249 */ "trigger_time ::= INSTEAD OF", -- /* 250 */ "trigger_time ::=", -- /* 251 */ "trigger_event ::= DELETE|INSERT", -- /* 252 */ "trigger_event ::= UPDATE", -- /* 253 */ "trigger_event ::= UPDATE OF idlist", -- /* 254 */ "when_clause ::=", -- /* 255 */ "when_clause ::= WHEN expr", -- /* 256 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", -- /* 257 */ "trigger_cmd_list ::= trigger_cmd SEMI", -- /* 258 */ "trnm ::= nm DOT nm", -- /* 259 */ "tridxby ::= INDEXED BY nm", -- /* 260 */ "tridxby ::= NOT INDEXED", -- /* 261 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", -- /* 262 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", -- /* 263 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", -- /* 264 */ "trigger_cmd ::= scanpt select scanpt", -- /* 265 */ "expr ::= RAISE LP IGNORE RP", -- /* 266 */ "expr ::= RAISE LP raisetype COMMA nm RP", -- /* 267 */ "raisetype ::= ROLLBACK", -- /* 268 */ "raisetype ::= ABORT", -- /* 269 */ "raisetype ::= FAIL", -- /* 270 */ "cmd ::= DROP TRIGGER ifexists fullname", -- /* 271 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", -- /* 272 */ "cmd ::= DETACH database_kw_opt expr", -- /* 273 */ "key_opt ::=", -- /* 274 */ "key_opt ::= KEY expr", -- /* 275 */ "cmd ::= REINDEX", -- /* 276 */ "cmd ::= REINDEX nm dbnm", -- /* 277 */ "cmd ::= ANALYZE", -- /* 278 */ "cmd ::= ANALYZE nm dbnm", -- /* 279 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", -- /* 280 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", -- /* 281 */ "add_column_fullname ::= fullname", -- /* 282 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", -- /* 283 */ "cmd ::= create_vtab", -- /* 284 */ "cmd ::= create_vtab LP vtabarglist RP", -- /* 285 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", -- /* 286 */ "vtabarg ::=", -- /* 287 */ "vtabargtoken ::= ANY", -- /* 288 */ "vtabargtoken ::= lp anylist RP", -- /* 289 */ "lp ::= LP", -- /* 290 */ "with ::= WITH wqlist", -- /* 291 */ "with ::= WITH RECURSIVE wqlist", -- /* 292 */ "wqlist ::= nm eidlist_opt AS LP select RP", -- /* 293 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", -- /* 294 */ "windowdefn_list ::= windowdefn", -- /* 295 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", -- /* 296 */ "windowdefn ::= nm AS LP window RP", -- /* 297 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", -- /* 298 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", -- /* 299 */ "window ::= ORDER BY sortlist frame_opt", -- /* 300 */ "window ::= nm ORDER BY sortlist frame_opt", -- /* 301 */ "window ::= frame_opt", -- /* 302 */ "window ::= nm frame_opt", -- /* 303 */ "frame_opt ::=", -- /* 304 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", -- /* 305 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", -- /* 306 */ "range_or_rows ::= RANGE|ROWS|GROUPS", -- /* 307 */ "frame_bound_s ::= frame_bound", -- /* 308 */ "frame_bound_s ::= UNBOUNDED PRECEDING", -- /* 309 */ "frame_bound_e ::= frame_bound", -- /* 310 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", -- /* 311 */ "frame_bound ::= expr PRECEDING|FOLLOWING", -- /* 312 */ "frame_bound ::= CURRENT ROW", -- /* 313 */ "frame_exclude_opt ::=", -- /* 314 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", -- /* 315 */ "frame_exclude ::= NO OTHERS", -- /* 316 */ "frame_exclude ::= CURRENT ROW", -- /* 317 */ "frame_exclude ::= GROUP|TIES", -- /* 318 */ "window_clause ::= WINDOW windowdefn_list", -- /* 319 */ "filter_over ::= filter_clause over_clause", -- /* 320 */ "filter_over ::= over_clause", -- /* 321 */ "filter_over ::= filter_clause", -- /* 322 */ "over_clause ::= OVER LP window RP", -- /* 323 */ "over_clause ::= OVER nm", -- /* 324 */ "filter_clause ::= FILTER LP WHERE expr RP", -- /* 325 */ "input ::= cmdlist", -- /* 326 */ "cmdlist ::= cmdlist ecmd", -- /* 327 */ "cmdlist ::= ecmd", -- /* 328 */ "ecmd ::= SEMI", -- /* 329 */ "ecmd ::= cmdx SEMI", -- /* 330 */ "ecmd ::= explain cmdx SEMI", -- /* 331 */ "trans_opt ::=", -- /* 332 */ "trans_opt ::= TRANSACTION", -- /* 333 */ "trans_opt ::= TRANSACTION nm", -- /* 334 */ "savepoint_opt ::= SAVEPOINT", -- /* 335 */ "savepoint_opt ::=", -- /* 336 */ "cmd ::= create_table create_table_args", -- /* 337 */ "columnlist ::= columnlist COMMA columnname carglist", -- /* 338 */ "columnlist ::= columnname carglist", -- /* 339 */ "nm ::= ID|INDEXED", -- /* 340 */ "nm ::= STRING", -- /* 341 */ "nm ::= JOIN_KW", -- /* 342 */ "typetoken ::= typename", -- /* 343 */ "typename ::= ID|STRING", -- /* 344 */ "signed ::= plus_num", -- /* 345 */ "signed ::= minus_num", -- /* 346 */ "carglist ::= carglist ccons", -- /* 347 */ "carglist ::=", -- /* 348 */ "ccons ::= NULL onconf", -- /* 349 */ "ccons ::= GENERATED ALWAYS AS generated", -- /* 350 */ "ccons ::= AS generated", -- /* 351 */ "conslist_opt ::= COMMA conslist", -- /* 352 */ "conslist ::= conslist tconscomma tcons", -- /* 353 */ "conslist ::= tcons", -- /* 354 */ "tconscomma ::=", -- /* 355 */ "defer_subclause_opt ::= defer_subclause", -- /* 356 */ "resolvetype ::= raisetype", -- /* 357 */ "selectnowith ::= oneselect", -- /* 358 */ "oneselect ::= values", -- /* 359 */ "sclp ::= selcollist COMMA", -- /* 360 */ "as ::= ID|STRING", -- /* 361 */ "expr ::= term", -- /* 362 */ "likeop ::= LIKE_KW|MATCH", -- /* 363 */ "exprlist ::= nexprlist", -- /* 364 */ "nmnum ::= plus_num", -- /* 365 */ "nmnum ::= nm", -- /* 366 */ "nmnum ::= ON", -- /* 367 */ "nmnum ::= DELETE", -- /* 368 */ "nmnum ::= DEFAULT", -- /* 369 */ "plus_num ::= INTEGER|FLOAT", -- /* 370 */ "foreach_clause ::=", -- /* 371 */ "foreach_clause ::= FOR EACH ROW", -- /* 372 */ "trnm ::= nm", -- /* 373 */ "tridxby ::=", -- /* 374 */ "database_kw_opt ::= DATABASE", -- /* 375 */ "database_kw_opt ::=", -- /* 376 */ "kwcolumn_opt ::=", -- /* 377 */ "kwcolumn_opt ::= COLUMNKW", -- /* 378 */ "vtabarglist ::= vtabarg", -- /* 379 */ "vtabarglist ::= vtabarglist COMMA vtabarg", -- /* 380 */ "vtabarg ::= vtabarg vtabargtoken", -- /* 381 */ "anylist ::=", -- /* 382 */ "anylist ::= anylist LP anylist RP", -- /* 383 */ "anylist ::= anylist ANY", -- /* 384 */ "with ::=", -+ /* 21 */ "table_option_set ::=", -+ /* 22 */ "table_option_set ::= table_option_set COMMA table_option", -+ /* 23 */ "table_option ::= WITHOUT nm", -+ /* 24 */ "table_option ::= nm", -+ /* 25 */ "columnname ::= nm typetoken", -+ /* 26 */ "typetoken ::=", -+ /* 27 */ "typetoken ::= typename LP signed RP", -+ /* 28 */ "typetoken ::= typename LP signed COMMA signed RP", -+ /* 29 */ "typename ::= typename ID|STRING", -+ /* 30 */ "scanpt ::=", -+ /* 31 */ "scantok ::=", -+ /* 32 */ "ccons ::= CONSTRAINT nm", -+ /* 33 */ "ccons ::= DEFAULT scantok term", -+ /* 34 */ "ccons ::= DEFAULT LP expr RP", -+ /* 35 */ "ccons ::= DEFAULT PLUS scantok term", -+ /* 36 */ "ccons ::= DEFAULT MINUS scantok term", -+ /* 37 */ "ccons ::= DEFAULT scantok ID|INDEXED", -+ /* 38 */ "ccons ::= NOT NULL onconf", -+ /* 39 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", -+ /* 40 */ "ccons ::= UNIQUE onconf", -+ /* 41 */ "ccons ::= CHECK LP expr RP", -+ /* 42 */ "ccons ::= REFERENCES nm eidlist_opt refargs", -+ /* 43 */ "ccons ::= defer_subclause", -+ /* 44 */ "ccons ::= COLLATE ID|STRING", -+ /* 45 */ "generated ::= LP expr RP", -+ /* 46 */ "generated ::= LP expr RP ID", -+ /* 47 */ "autoinc ::=", -+ /* 48 */ "autoinc ::= AUTOINCR", -+ /* 49 */ "refargs ::=", -+ /* 50 */ "refargs ::= refargs refarg", -+ /* 51 */ "refarg ::= MATCH nm", -+ /* 52 */ "refarg ::= ON INSERT refact", -+ /* 53 */ "refarg ::= ON DELETE refact", -+ /* 54 */ "refarg ::= ON UPDATE refact", -+ /* 55 */ "refact ::= SET NULL", -+ /* 56 */ "refact ::= SET DEFAULT", -+ /* 57 */ "refact ::= CASCADE", -+ /* 58 */ "refact ::= RESTRICT", -+ /* 59 */ "refact ::= NO ACTION", -+ /* 60 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", -+ /* 61 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", -+ /* 62 */ "init_deferred_pred_opt ::=", -+ /* 63 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", -+ /* 64 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", -+ /* 65 */ "conslist_opt ::=", -+ /* 66 */ "tconscomma ::= COMMA", -+ /* 67 */ "tcons ::= CONSTRAINT nm", -+ /* 68 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf", -+ /* 69 */ "tcons ::= UNIQUE LP sortlist RP onconf", -+ /* 70 */ "tcons ::= CHECK LP expr RP onconf", -+ /* 71 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt", -+ /* 72 */ "defer_subclause_opt ::=", -+ /* 73 */ "onconf ::=", -+ /* 74 */ "onconf ::= ON CONFLICT resolvetype", -+ /* 75 */ "orconf ::=", -+ /* 76 */ "orconf ::= OR resolvetype", -+ /* 77 */ "resolvetype ::= IGNORE", -+ /* 78 */ "resolvetype ::= REPLACE", -+ /* 79 */ "cmd ::= DROP TABLE ifexists fullname", -+ /* 80 */ "ifexists ::= IF EXISTS", -+ /* 81 */ "ifexists ::=", -+ /* 82 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", -+ /* 83 */ "cmd ::= DROP VIEW ifexists fullname", -+ /* 84 */ "cmd ::= select", -+ /* 85 */ "select ::= WITH wqlist selectnowith", -+ /* 86 */ "select ::= WITH RECURSIVE wqlist selectnowith", -+ /* 87 */ "select ::= selectnowith", -+ /* 88 */ "selectnowith ::= selectnowith multiselect_op oneselect", -+ /* 89 */ "multiselect_op ::= UNION", -+ /* 90 */ "multiselect_op ::= UNION ALL", -+ /* 91 */ "multiselect_op ::= EXCEPT|INTERSECT", -+ /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", -+ /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", -+ /* 94 */ "values ::= VALUES LP nexprlist RP", -+ /* 95 */ "oneselect ::= mvalues", -+ /* 96 */ "mvalues ::= values COMMA LP nexprlist RP", -+ /* 97 */ "mvalues ::= mvalues COMMA LP nexprlist RP", -+ /* 98 */ "distinct ::= DISTINCT", -+ /* 99 */ "distinct ::= ALL", -+ /* 100 */ "distinct ::=", -+ /* 101 */ "sclp ::=", -+ /* 102 */ "selcollist ::= sclp scanpt expr scanpt as", -+ /* 103 */ "selcollist ::= sclp scanpt STAR", -+ /* 104 */ "selcollist ::= sclp scanpt nm DOT STAR", -+ /* 105 */ "as ::= AS nm", -+ /* 106 */ "as ::=", -+ /* 107 */ "from ::=", -+ /* 108 */ "from ::= FROM seltablist", -+ /* 109 */ "stl_prefix ::= seltablist joinop", -+ /* 110 */ "stl_prefix ::=", -+ /* 111 */ "seltablist ::= stl_prefix nm dbnm as on_using", -+ /* 112 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", -+ /* 113 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", -+ /* 114 */ "seltablist ::= stl_prefix LP select RP as on_using", -+ /* 115 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", -+ /* 116 */ "dbnm ::=", -+ /* 117 */ "dbnm ::= DOT nm", -+ /* 118 */ "fullname ::= nm", -+ /* 119 */ "fullname ::= nm DOT nm", -+ /* 120 */ "xfullname ::= nm", -+ /* 121 */ "xfullname ::= nm DOT nm", -+ /* 122 */ "xfullname ::= nm DOT nm AS nm", -+ /* 123 */ "xfullname ::= nm AS nm", -+ /* 124 */ "joinop ::= COMMA|JOIN", -+ /* 125 */ "joinop ::= JOIN_KW JOIN", -+ /* 126 */ "joinop ::= JOIN_KW nm JOIN", -+ /* 127 */ "joinop ::= JOIN_KW nm nm JOIN", -+ /* 128 */ "on_using ::= ON expr", -+ /* 129 */ "on_using ::= USING LP idlist RP", -+ /* 130 */ "on_using ::=", -+ /* 131 */ "indexed_opt ::=", -+ /* 132 */ "indexed_by ::= INDEXED BY nm", -+ /* 133 */ "indexed_by ::= NOT INDEXED", -+ /* 134 */ "orderby_opt ::=", -+ /* 135 */ "orderby_opt ::= ORDER BY sortlist", -+ /* 136 */ "sortlist ::= sortlist COMMA expr sortorder nulls", -+ /* 137 */ "sortlist ::= expr sortorder nulls", -+ /* 138 */ "sortorder ::= ASC", -+ /* 139 */ "sortorder ::= DESC", -+ /* 140 */ "sortorder ::=", -+ /* 141 */ "nulls ::= NULLS FIRST", -+ /* 142 */ "nulls ::= NULLS LAST", -+ /* 143 */ "nulls ::=", -+ /* 144 */ "groupby_opt ::=", -+ /* 145 */ "groupby_opt ::= GROUP BY nexprlist", -+ /* 146 */ "having_opt ::=", -+ /* 147 */ "having_opt ::= HAVING expr", -+ /* 148 */ "limit_opt ::=", -+ /* 149 */ "limit_opt ::= LIMIT expr", -+ /* 150 */ "limit_opt ::= LIMIT expr OFFSET expr", -+ /* 151 */ "limit_opt ::= LIMIT expr COMMA expr", -+ /* 152 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", -+ /* 153 */ "where_opt ::=", -+ /* 154 */ "where_opt ::= WHERE expr", -+ /* 155 */ "where_opt_ret ::=", -+ /* 156 */ "where_opt_ret ::= WHERE expr", -+ /* 157 */ "where_opt_ret ::= RETURNING selcollist", -+ /* 158 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", -+ /* 159 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", -+ /* 160 */ "setlist ::= setlist COMMA nm EQ expr", -+ /* 161 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", -+ /* 162 */ "setlist ::= nm EQ expr", -+ /* 163 */ "setlist ::= LP idlist RP EQ expr", -+ /* 164 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", -+ /* 165 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", -+ /* 166 */ "upsert ::=", -+ /* 167 */ "upsert ::= RETURNING selcollist", -+ /* 168 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", -+ /* 169 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", -+ /* 170 */ "upsert ::= ON CONFLICT DO NOTHING returning", -+ /* 171 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", -+ /* 172 */ "returning ::= RETURNING selcollist", -+ /* 173 */ "insert_cmd ::= INSERT orconf", -+ /* 174 */ "insert_cmd ::= REPLACE", -+ /* 175 */ "idlist_opt ::=", -+ /* 176 */ "idlist_opt ::= LP idlist RP", -+ /* 177 */ "idlist ::= idlist COMMA nm", -+ /* 178 */ "idlist ::= nm", -+ /* 179 */ "expr ::= LP expr RP", -+ /* 180 */ "expr ::= ID|INDEXED|JOIN_KW", -+ /* 181 */ "expr ::= nm DOT nm", -+ /* 182 */ "expr ::= nm DOT nm DOT nm", -+ /* 183 */ "term ::= NULL|FLOAT|BLOB", -+ /* 184 */ "term ::= STRING", -+ /* 185 */ "term ::= INTEGER", -+ /* 186 */ "expr ::= VARIABLE", -+ /* 187 */ "expr ::= expr COLLATE ID|STRING", -+ /* 188 */ "expr ::= CAST LP expr AS typetoken RP", -+ /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP", -+ /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP", -+ /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", -+ /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", -+ /* 193 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over", -+ /* 194 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", -+ /* 195 */ "term ::= CTIME_KW", -+ /* 196 */ "expr ::= LP nexprlist COMMA expr RP", -+ /* 197 */ "expr ::= expr AND expr", -+ /* 198 */ "expr ::= expr OR expr", -+ /* 199 */ "expr ::= expr LT|GT|GE|LE expr", -+ /* 200 */ "expr ::= expr EQ|NE expr", -+ /* 201 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", -+ /* 202 */ "expr ::= expr PLUS|MINUS expr", -+ /* 203 */ "expr ::= expr STAR|SLASH|REM expr", -+ /* 204 */ "expr ::= expr CONCAT expr", -+ /* 205 */ "likeop ::= NOT LIKE_KW|MATCH", -+ /* 206 */ "expr ::= expr likeop expr", -+ /* 207 */ "expr ::= expr likeop expr ESCAPE expr", -+ /* 208 */ "expr ::= expr ISNULL|NOTNULL", -+ /* 209 */ "expr ::= expr NOT NULL", -+ /* 210 */ "expr ::= expr IS expr", -+ /* 211 */ "expr ::= expr IS NOT expr", -+ /* 212 */ "expr ::= expr IS NOT DISTINCT FROM expr", -+ /* 213 */ "expr ::= expr IS DISTINCT FROM expr", -+ /* 214 */ "expr ::= NOT expr", -+ /* 215 */ "expr ::= BITNOT expr", -+ /* 216 */ "expr ::= PLUS|MINUS expr", -+ /* 217 */ "expr ::= expr PTR expr", -+ /* 218 */ "between_op ::= BETWEEN", -+ /* 219 */ "between_op ::= NOT BETWEEN", -+ /* 220 */ "expr ::= expr between_op expr AND expr", -+ /* 221 */ "in_op ::= IN", -+ /* 222 */ "in_op ::= NOT IN", -+ /* 223 */ "expr ::= expr in_op LP exprlist RP", -+ /* 224 */ "expr ::= LP select RP", -+ /* 225 */ "expr ::= expr in_op LP select RP", -+ /* 226 */ "expr ::= expr in_op nm dbnm paren_exprlist", -+ /* 227 */ "expr ::= EXISTS LP select RP", -+ /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END", -+ /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", -+ /* 230 */ "case_exprlist ::= WHEN expr THEN expr", -+ /* 231 */ "case_else ::= ELSE expr", -+ /* 232 */ "case_else ::=", -+ /* 233 */ "case_operand ::=", -+ /* 234 */ "exprlist ::=", -+ /* 235 */ "nexprlist ::= nexprlist COMMA expr", -+ /* 236 */ "nexprlist ::= expr", -+ /* 237 */ "paren_exprlist ::=", -+ /* 238 */ "paren_exprlist ::= LP exprlist RP", -+ /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", -+ /* 240 */ "uniqueflag ::= UNIQUE", -+ /* 241 */ "uniqueflag ::=", -+ /* 242 */ "eidlist_opt ::=", -+ /* 243 */ "eidlist_opt ::= LP eidlist RP", -+ /* 244 */ "eidlist ::= eidlist COMMA nm collate sortorder", -+ /* 245 */ "eidlist ::= nm collate sortorder", -+ /* 246 */ "collate ::=", -+ /* 247 */ "collate ::= COLLATE ID|STRING", -+ /* 248 */ "cmd ::= DROP INDEX ifexists fullname", -+ /* 249 */ "cmd ::= VACUUM vinto", -+ /* 250 */ "cmd ::= VACUUM nm vinto", -+ /* 251 */ "vinto ::= INTO expr", -+ /* 252 */ "vinto ::=", -+ /* 253 */ "cmd ::= PRAGMA nm dbnm", -+ /* 254 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", -+ /* 255 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", -+ /* 256 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", -+ /* 257 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", -+ /* 258 */ "plus_num ::= PLUS INTEGER|FLOAT", -+ /* 259 */ "minus_num ::= MINUS INTEGER|FLOAT", -+ /* 260 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", -+ /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", -+ /* 262 */ "trigger_time ::= BEFORE|AFTER", -+ /* 263 */ "trigger_time ::= INSTEAD OF", -+ /* 264 */ "trigger_time ::=", -+ /* 265 */ "trigger_event ::= DELETE|INSERT", -+ /* 266 */ "trigger_event ::= UPDATE", -+ /* 267 */ "trigger_event ::= UPDATE OF idlist", -+ /* 268 */ "when_clause ::=", -+ /* 269 */ "when_clause ::= WHEN expr", -+ /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", -+ /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI", -+ /* 272 */ "trnm ::= nm DOT nm", -+ /* 273 */ "tridxby ::= INDEXED BY nm", -+ /* 274 */ "tridxby ::= NOT INDEXED", -+ /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", -+ /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", -+ /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", -+ /* 278 */ "trigger_cmd ::= scanpt select scanpt", -+ /* 279 */ "expr ::= RAISE LP IGNORE RP", -+ /* 280 */ "expr ::= RAISE LP raisetype COMMA expr RP", -+ /* 281 */ "raisetype ::= ROLLBACK", -+ /* 282 */ "raisetype ::= ABORT", -+ /* 283 */ "raisetype ::= FAIL", -+ /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname", -+ /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", -+ /* 286 */ "cmd ::= DETACH database_kw_opt expr", -+ /* 287 */ "key_opt ::=", -+ /* 288 */ "key_opt ::= KEY expr", -+ /* 289 */ "cmd ::= REINDEX", -+ /* 290 */ "cmd ::= REINDEX nm dbnm", -+ /* 291 */ "cmd ::= ANALYZE", -+ /* 292 */ "cmd ::= ANALYZE nm dbnm", -+ /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", -+ /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", -+ /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", -+ /* 296 */ "add_column_fullname ::= fullname", -+ /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", -+ /* 298 */ "cmd ::= create_vtab", -+ /* 299 */ "cmd ::= create_vtab LP vtabarglist RP", -+ /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", -+ /* 301 */ "vtabarg ::=", -+ /* 302 */ "vtabargtoken ::= ANY", -+ /* 303 */ "vtabargtoken ::= lp anylist RP", -+ /* 304 */ "lp ::= LP", -+ /* 305 */ "with ::= WITH wqlist", -+ /* 306 */ "with ::= WITH RECURSIVE wqlist", -+ /* 307 */ "wqas ::= AS", -+ /* 308 */ "wqas ::= AS MATERIALIZED", -+ /* 309 */ "wqas ::= AS NOT MATERIALIZED", -+ /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP", -+ /* 311 */ "withnm ::= nm", -+ /* 312 */ "wqlist ::= wqitem", -+ /* 313 */ "wqlist ::= wqlist COMMA wqitem", -+ /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", -+ /* 315 */ "windowdefn ::= nm AS LP window RP", -+ /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", -+ /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", -+ /* 318 */ "window ::= ORDER BY sortlist frame_opt", -+ /* 319 */ "window ::= nm ORDER BY sortlist frame_opt", -+ /* 320 */ "window ::= nm frame_opt", -+ /* 321 */ "frame_opt ::=", -+ /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", -+ /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", -+ /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS", -+ /* 325 */ "frame_bound_s ::= frame_bound", -+ /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING", -+ /* 327 */ "frame_bound_e ::= frame_bound", -+ /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", -+ /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING", -+ /* 330 */ "frame_bound ::= CURRENT ROW", -+ /* 331 */ "frame_exclude_opt ::=", -+ /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", -+ /* 333 */ "frame_exclude ::= NO OTHERS", -+ /* 334 */ "frame_exclude ::= CURRENT ROW", -+ /* 335 */ "frame_exclude ::= GROUP|TIES", -+ /* 336 */ "window_clause ::= WINDOW windowdefn_list", -+ /* 337 */ "filter_over ::= filter_clause over_clause", -+ /* 338 */ "filter_over ::= over_clause", -+ /* 339 */ "filter_over ::= filter_clause", -+ /* 340 */ "over_clause ::= OVER LP window RP", -+ /* 341 */ "over_clause ::= OVER nm", -+ /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP", -+ /* 343 */ "term ::= QNUMBER", -+ /* 344 */ "input ::= cmdlist", -+ /* 345 */ "cmdlist ::= cmdlist ecmd", -+ /* 346 */ "cmdlist ::= ecmd", -+ /* 347 */ "ecmd ::= SEMI", -+ /* 348 */ "ecmd ::= cmdx SEMI", -+ /* 349 */ "ecmd ::= explain cmdx SEMI", -+ /* 350 */ "trans_opt ::=", -+ /* 351 */ "trans_opt ::= TRANSACTION", -+ /* 352 */ "trans_opt ::= TRANSACTION nm", -+ /* 353 */ "savepoint_opt ::= SAVEPOINT", -+ /* 354 */ "savepoint_opt ::=", -+ /* 355 */ "cmd ::= create_table create_table_args", -+ /* 356 */ "table_option_set ::= table_option", -+ /* 357 */ "columnlist ::= columnlist COMMA columnname carglist", -+ /* 358 */ "columnlist ::= columnname carglist", -+ /* 359 */ "nm ::= ID|INDEXED|JOIN_KW", -+ /* 360 */ "nm ::= STRING", -+ /* 361 */ "typetoken ::= typename", -+ /* 362 */ "typename ::= ID|STRING", -+ /* 363 */ "signed ::= plus_num", -+ /* 364 */ "signed ::= minus_num", -+ /* 365 */ "carglist ::= carglist ccons", -+ /* 366 */ "carglist ::=", -+ /* 367 */ "ccons ::= NULL onconf", -+ /* 368 */ "ccons ::= GENERATED ALWAYS AS generated", -+ /* 369 */ "ccons ::= AS generated", -+ /* 370 */ "conslist_opt ::= COMMA conslist", -+ /* 371 */ "conslist ::= conslist tconscomma tcons", -+ /* 372 */ "conslist ::= tcons", -+ /* 373 */ "tconscomma ::=", -+ /* 374 */ "defer_subclause_opt ::= defer_subclause", -+ /* 375 */ "resolvetype ::= raisetype", -+ /* 376 */ "selectnowith ::= oneselect", -+ /* 377 */ "oneselect ::= values", -+ /* 378 */ "sclp ::= selcollist COMMA", -+ /* 379 */ "as ::= ID|STRING", -+ /* 380 */ "indexed_opt ::= indexed_by", -+ /* 381 */ "returning ::=", -+ /* 382 */ "expr ::= term", -+ /* 383 */ "likeop ::= LIKE_KW|MATCH", -+ /* 384 */ "case_operand ::= expr", -+ /* 385 */ "exprlist ::= nexprlist", -+ /* 386 */ "nmnum ::= plus_num", -+ /* 387 */ "nmnum ::= nm", -+ /* 388 */ "nmnum ::= ON", -+ /* 389 */ "nmnum ::= DELETE", -+ /* 390 */ "nmnum ::= DEFAULT", -+ /* 391 */ "plus_num ::= INTEGER|FLOAT", -+ /* 392 */ "foreach_clause ::=", -+ /* 393 */ "foreach_clause ::= FOR EACH ROW", -+ /* 394 */ "trnm ::= nm", -+ /* 395 */ "tridxby ::=", -+ /* 396 */ "database_kw_opt ::= DATABASE", -+ /* 397 */ "database_kw_opt ::=", -+ /* 398 */ "kwcolumn_opt ::=", -+ /* 399 */ "kwcolumn_opt ::= COLUMNKW", -+ /* 400 */ "vtabarglist ::= vtabarg", -+ /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg", -+ /* 402 */ "vtabarg ::= vtabarg vtabargtoken", -+ /* 403 */ "anylist ::=", -+ /* 404 */ "anylist ::= anylist LP anylist RP", -+ /* 405 */ "anylist ::= anylist ANY", -+ /* 406 */ "with ::=", -+ /* 407 */ "windowdefn_list ::= windowdefn", -+ /* 408 */ "window ::= frame_opt", - }; - #endif /* NDEBUG */ - - --#if YYSTACKDEPTH<=0 -+#if YYGROWABLESTACK - /* - ** Try to increase the size of the parser stack. Return the number - ** of errors. Return 0 on success. - */ - static int yyGrowStack(yyParser *p){ -+ int oldSize = 1 + (int)(p->yystackEnd - p->yystack); - int newSize; - int idx; - yyStackEntry *pNew; - -- newSize = p->yystksz*2 + 100; -- idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; -- if( p->yystack==&p->yystk0 ){ -- pNew = malloc(newSize*sizeof(pNew[0])); -- if( pNew ) pNew[0] = p->yystk0; -+ newSize = oldSize*2 + 100; -+ idx = (int)(p->yytos - p->yystack); -+ if( p->yystack==p->yystk0 ){ -+ pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); -+ if( pNew==0 ) return 1; -+ memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); - }else{ -- pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); -+ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); -+ if( pNew==0 ) return 1; - } -- if( pNew ){ -- p->yystack = pNew; -- p->yytos = &p->yystack[idx]; -+ p->yystack = pNew; -+ p->yytos = &p->yystack[idx]; - #ifndef NDEBUG -- if( yyTraceFILE ){ -- fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", -- yyTracePrompt, p->yystksz, newSize); -- } --#endif -- p->yystksz = newSize; -+ if( yyTraceFILE ){ -+ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", -+ yyTracePrompt, oldSize, newSize); - } -- return pNew==0; -+#endif -+ p->yystackEnd = &p->yystack[newSize-1]; -+ return 0; - } -+#endif /* YYGROWABLESTACK */ -+ -+#if !YYGROWABLESTACK -+/* For builds that do no have a growable stack, yyGrowStack always -+** returns an error. -+*/ -+# define yyGrowStack(X) 1 - #endif - - /* Datatype of the argument to the memory allocated passed as the -@@ -156218,24 +177633,14 @@ SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL) - #ifdef YYTRACKMAXSTACKDEPTH - yypParser->yyhwm = 0; - #endif --#if YYSTACKDEPTH<=0 -- yypParser->yytos = NULL; -- yypParser->yystack = NULL; -- yypParser->yystksz = 0; -- if( yyGrowStack(yypParser) ){ -- yypParser->yystack = &yypParser->yystk0; -- yypParser->yystksz = 1; -- } --#endif -+ yypParser->yystack = yypParser->yystk0; -+ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; - #ifndef YYNOERRORRECOVERY - yypParser->yyerrcnt = -1; - #endif - yypParser->yytos = yypParser->yystack; - yypParser->yystack[0].stateno = 0; - yypParser->yystack[0].major = 0; --#if YYSTACKDEPTH>0 -- yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; --#endif - } - - #ifndef sqlite3Parser_ENGINEALWAYSONSTACK -@@ -156289,98 +177694,98 @@ static void yy_destructor( - ** inside the C code. - */ - /********* Begin destructor definitions ***************************************/ -- case 200: /* select */ -- case 234: /* selectnowith */ -- case 235: /* oneselect */ -- case 247: /* values */ -+ case 206: /* select */ -+ case 241: /* selectnowith */ -+ case 242: /* oneselect */ -+ case 254: /* values */ -+ case 256: /* mvalues */ - { --sqlite3SelectDelete(pParse->db, (yypminor->yy539)); --} -- break; -- case 211: /* term */ -- case 212: /* expr */ -- case 241: /* where_opt */ -- case 243: /* having_opt */ -- case 255: /* on_opt */ -- case 271: /* case_operand */ -- case 273: /* case_else */ -- case 276: /* vinto */ -- case 283: /* when_clause */ -- case 288: /* key_opt */ -- case 302: /* filter_clause */ -+sqlite3SelectDelete(pParse->db, (yypminor->yy637)); -+} -+ break; -+ case 218: /* term */ -+ case 219: /* expr */ -+ case 248: /* where_opt */ -+ case 250: /* having_opt */ -+ case 270: /* where_opt_ret */ -+ case 281: /* case_operand */ -+ case 283: /* case_else */ -+ case 286: /* vinto */ -+ case 293: /* when_clause */ -+ case 298: /* key_opt */ -+ case 315: /* filter_clause */ - { --sqlite3ExprDelete(pParse->db, (yypminor->yy202)); --} -- break; -- case 216: /* eidlist_opt */ -- case 226: /* sortlist */ -- case 227: /* eidlist */ -- case 239: /* selcollist */ -- case 242: /* groupby_opt */ -- case 244: /* orderby_opt */ -- case 248: /* nexprlist */ -- case 249: /* sclp */ -- case 257: /* exprlist */ -- case 262: /* setlist */ -- case 270: /* paren_exprlist */ -- case 272: /* case_exprlist */ -- case 301: /* part_opt */ -+sqlite3ExprDelete(pParse->db, (yypminor->yy590)); -+} -+ break; -+ case 223: /* eidlist_opt */ -+ case 233: /* sortlist */ -+ case 234: /* eidlist */ -+ case 246: /* selcollist */ -+ case 249: /* groupby_opt */ -+ case 251: /* orderby_opt */ -+ case 255: /* nexprlist */ -+ case 257: /* sclp */ -+ case 264: /* exprlist */ -+ case 271: /* setlist */ -+ case 280: /* paren_exprlist */ -+ case 282: /* case_exprlist */ -+ case 314: /* part_opt */ - { --sqlite3ExprListDelete(pParse->db, (yypminor->yy242)); -+sqlite3ExprListDelete(pParse->db, (yypminor->yy402)); - } - break; -- case 233: /* fullname */ -- case 240: /* from */ -- case 251: /* seltablist */ -- case 252: /* stl_prefix */ -- case 258: /* xfullname */ -+ case 240: /* fullname */ -+ case 247: /* from */ -+ case 259: /* seltablist */ -+ case 260: /* stl_prefix */ -+ case 265: /* xfullname */ - { --sqlite3SrcListDelete(pParse->db, (yypminor->yy47)); -+sqlite3SrcListDelete(pParse->db, (yypminor->yy563)); - } - break; -- case 236: /* wqlist */ -+ case 243: /* wqlist */ - { --sqlite3WithDelete(pParse->db, (yypminor->yy131)); -+sqlite3WithDelete(pParse->db, (yypminor->yy125)); - } - break; -- case 246: /* window_clause */ -- case 297: /* windowdefn_list */ -+ case 253: /* window_clause */ -+ case 310: /* windowdefn_list */ - { --sqlite3WindowListDelete(pParse->db, (yypminor->yy303)); -+sqlite3WindowListDelete(pParse->db, (yypminor->yy483)); - } - break; -- case 256: /* using_opt */ -- case 259: /* idlist */ -- case 264: /* idlist_opt */ -+ case 266: /* idlist */ -+ case 273: /* idlist_opt */ - { --sqlite3IdListDelete(pParse->db, (yypminor->yy600)); -+sqlite3IdListDelete(pParse->db, (yypminor->yy204)); - } - break; -- case 266: /* filter_over */ -- case 298: /* windowdefn */ -- case 299: /* window */ -- case 300: /* frame_opt */ -- case 303: /* over_clause */ -+ case 276: /* filter_over */ -+ case 311: /* windowdefn */ -+ case 312: /* window */ -+ case 313: /* frame_opt */ -+ case 316: /* over_clause */ - { --sqlite3WindowDelete(pParse->db, (yypminor->yy303)); -+sqlite3WindowDelete(pParse->db, (yypminor->yy483)); - } - break; -- case 279: /* trigger_cmd_list */ -- case 284: /* trigger_cmd */ -+ case 289: /* trigger_cmd_list */ -+ case 294: /* trigger_cmd */ - { --sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy447)); -+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy319)); - } - break; -- case 281: /* trigger_event */ -+ case 291: /* trigger_event */ - { --sqlite3IdListDelete(pParse->db, (yypminor->yy230).b); -+sqlite3IdListDelete(pParse->db, (yypminor->yy28).b); - } - break; -- case 305: /* frame_bound */ -- case 306: /* frame_bound_s */ -- case 307: /* frame_bound_e */ -+ case 318: /* frame_bound */ -+ case 319: /* frame_bound_s */ -+ case 320: /* frame_bound_e */ - { --sqlite3ExprDelete(pParse->db, (yypminor->yy77).pExpr); -+sqlite3ExprDelete(pParse->db, (yypminor->yy205).pExpr); - } - break; - /********* End destructor definitions *****************************************/ -@@ -156414,9 +177819,26 @@ static void yy_pop_parser_stack(yyParser *pParser){ - */ - SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){ - yyParser *pParser = (yyParser*)p; -- while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); --#if YYSTACKDEPTH<=0 -- if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); -+ -+ /* In-lined version of calling yy_pop_parser_stack() for each -+ ** element left in the stack */ -+ yyStackEntry *yytos = pParser->yytos; -+ while( yytos>pParser->yystack ){ -+#ifndef NDEBUG -+ if( yyTraceFILE ){ -+ fprintf(yyTraceFILE,"%sPopping %s\n", -+ yyTracePrompt, -+ yyTokenName[yytos->major]); -+ } -+#endif -+ if( yytos->major>=YY_MIN_DSTRCTR ){ -+ yy_destructor(pParser, yytos->major, &yytos->minor); -+ } -+ yytos--; -+ } -+ -+#if YYGROWABLESTACK -+ if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); - #endif - } - -@@ -156547,7 +177969,7 @@ static YYACTIONTYPE yy_find_shift_action( - #endif /* YYWILDCARD */ - return yy_default[stateno]; - }else{ -- assert( i>=0 && i=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); - return yy_action[i]; - } - }while(1); -@@ -156599,7 +178021,7 @@ static void yyStackOverflow(yyParser *yypParser){ - ** stack every overflows */ - /******** Begin %stack_overflow code ******************************************/ - -- sqlite3ErrorMsg(pParse, "parser stack overflow"); -+ sqlite3OomFault(pParse->db); - /******** End %stack_overflow code ********************************************/ - sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */ - sqlite3ParserCTX_STORE -@@ -156643,25 +178065,19 @@ static void yy_shift( - assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); - } - #endif --#if YYSTACKDEPTH>0 -- if( yypParser->yytos>yypParser->yystackEnd ){ -- yypParser->yytos--; -- yyStackOverflow(yypParser); -- return; -- } --#else -- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ -+ yytos = yypParser->yytos; -+ if( yytos>yypParser->yystackEnd ){ - if( yyGrowStack(yypParser) ){ - yypParser->yytos--; - yyStackOverflow(yypParser); - return; - } -+ yytos = yypParser->yytos; -+ assert( yytos <= yypParser->yystackEnd ); - } --#endif - if( yyNewState > YY_MAX_SHIFT ){ - yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; - } -- yytos = yypParser->yytos; - yytos->stateno = yyNewState; - yytos->major = yyMajor; - yytos->minor.yy0 = yyMinor; -@@ -156671,391 +178087,415 @@ static void yy_shift( - /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side - ** of that rule */ - static const YYCODETYPE yyRuleInfoLhs[] = { -- 185, /* (0) explain ::= EXPLAIN */ -- 185, /* (1) explain ::= EXPLAIN QUERY PLAN */ -- 184, /* (2) cmdx ::= cmd */ -- 186, /* (3) cmd ::= BEGIN transtype trans_opt */ -- 187, /* (4) transtype ::= */ -- 187, /* (5) transtype ::= DEFERRED */ -- 187, /* (6) transtype ::= IMMEDIATE */ -- 187, /* (7) transtype ::= EXCLUSIVE */ -- 186, /* (8) cmd ::= COMMIT|END trans_opt */ -- 186, /* (9) cmd ::= ROLLBACK trans_opt */ -- 186, /* (10) cmd ::= SAVEPOINT nm */ -- 186, /* (11) cmd ::= RELEASE savepoint_opt nm */ -- 186, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ -- 191, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ -- 193, /* (14) createkw ::= CREATE */ -- 195, /* (15) ifnotexists ::= */ -- 195, /* (16) ifnotexists ::= IF NOT EXISTS */ -- 194, /* (17) temp ::= TEMP */ -- 194, /* (18) temp ::= */ -- 192, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ -- 192, /* (20) create_table_args ::= AS select */ -- 199, /* (21) table_options ::= */ -- 199, /* (22) table_options ::= WITHOUT nm */ -- 201, /* (23) columnname ::= nm typetoken */ -- 203, /* (24) typetoken ::= */ -- 203, /* (25) typetoken ::= typename LP signed RP */ -- 203, /* (26) typetoken ::= typename LP signed COMMA signed RP */ -- 204, /* (27) typename ::= typename ID|STRING */ -- 208, /* (28) scanpt ::= */ -- 209, /* (29) scantok ::= */ -- 210, /* (30) ccons ::= CONSTRAINT nm */ -- 210, /* (31) ccons ::= DEFAULT scantok term */ -- 210, /* (32) ccons ::= DEFAULT LP expr RP */ -- 210, /* (33) ccons ::= DEFAULT PLUS scantok term */ -- 210, /* (34) ccons ::= DEFAULT MINUS scantok term */ -- 210, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ -- 210, /* (36) ccons ::= NOT NULL onconf */ -- 210, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ -- 210, /* (38) ccons ::= UNIQUE onconf */ -- 210, /* (39) ccons ::= CHECK LP expr RP */ -- 210, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ -- 210, /* (41) ccons ::= defer_subclause */ -- 210, /* (42) ccons ::= COLLATE ID|STRING */ -- 219, /* (43) generated ::= LP expr RP */ -- 219, /* (44) generated ::= LP expr RP ID */ -- 215, /* (45) autoinc ::= */ -- 215, /* (46) autoinc ::= AUTOINCR */ -- 217, /* (47) refargs ::= */ -- 217, /* (48) refargs ::= refargs refarg */ -- 220, /* (49) refarg ::= MATCH nm */ -- 220, /* (50) refarg ::= ON INSERT refact */ -- 220, /* (51) refarg ::= ON DELETE refact */ -- 220, /* (52) refarg ::= ON UPDATE refact */ -- 221, /* (53) refact ::= SET NULL */ -- 221, /* (54) refact ::= SET DEFAULT */ -- 221, /* (55) refact ::= CASCADE */ -- 221, /* (56) refact ::= RESTRICT */ -- 221, /* (57) refact ::= NO ACTION */ -- 218, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -- 218, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ -- 222, /* (60) init_deferred_pred_opt ::= */ -- 222, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ -- 222, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -- 198, /* (63) conslist_opt ::= */ -- 224, /* (64) tconscomma ::= COMMA */ -- 225, /* (65) tcons ::= CONSTRAINT nm */ -- 225, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -- 225, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ -- 225, /* (68) tcons ::= CHECK LP expr RP onconf */ -- 225, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ -- 228, /* (70) defer_subclause_opt ::= */ -- 213, /* (71) onconf ::= */ -- 213, /* (72) onconf ::= ON CONFLICT resolvetype */ -- 229, /* (73) orconf ::= */ -- 229, /* (74) orconf ::= OR resolvetype */ -- 230, /* (75) resolvetype ::= IGNORE */ -- 230, /* (76) resolvetype ::= REPLACE */ -- 186, /* (77) cmd ::= DROP TABLE ifexists fullname */ -- 232, /* (78) ifexists ::= IF EXISTS */ -- 232, /* (79) ifexists ::= */ -- 186, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ -- 186, /* (81) cmd ::= DROP VIEW ifexists fullname */ -- 186, /* (82) cmd ::= select */ -- 200, /* (83) select ::= WITH wqlist selectnowith */ -- 200, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ -- 200, /* (85) select ::= selectnowith */ -- 234, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ -- 237, /* (87) multiselect_op ::= UNION */ -- 237, /* (88) multiselect_op ::= UNION ALL */ -- 237, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ -- 235, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -- 235, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -- 247, /* (92) values ::= VALUES LP nexprlist RP */ -- 247, /* (93) values ::= values COMMA LP nexprlist RP */ -- 238, /* (94) distinct ::= DISTINCT */ -- 238, /* (95) distinct ::= ALL */ -- 238, /* (96) distinct ::= */ -- 249, /* (97) sclp ::= */ -- 239, /* (98) selcollist ::= sclp scanpt expr scanpt as */ -- 239, /* (99) selcollist ::= sclp scanpt STAR */ -- 239, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ -- 250, /* (101) as ::= AS nm */ -- 250, /* (102) as ::= */ -- 240, /* (103) from ::= */ -- 240, /* (104) from ::= FROM seltablist */ -- 252, /* (105) stl_prefix ::= seltablist joinop */ -- 252, /* (106) stl_prefix ::= */ -- 251, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ -- 251, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ -- 251, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ -- 251, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ -- 196, /* (111) dbnm ::= */ -- 196, /* (112) dbnm ::= DOT nm */ -- 233, /* (113) fullname ::= nm */ -- 233, /* (114) fullname ::= nm DOT nm */ -- 258, /* (115) xfullname ::= nm */ -- 258, /* (116) xfullname ::= nm DOT nm */ -- 258, /* (117) xfullname ::= nm DOT nm AS nm */ -- 258, /* (118) xfullname ::= nm AS nm */ -- 253, /* (119) joinop ::= COMMA|JOIN */ -- 253, /* (120) joinop ::= JOIN_KW JOIN */ -- 253, /* (121) joinop ::= JOIN_KW nm JOIN */ -- 253, /* (122) joinop ::= JOIN_KW nm nm JOIN */ -- 255, /* (123) on_opt ::= ON expr */ -- 255, /* (124) on_opt ::= */ -- 254, /* (125) indexed_opt ::= */ -- 254, /* (126) indexed_opt ::= INDEXED BY nm */ -- 254, /* (127) indexed_opt ::= NOT INDEXED */ -- 256, /* (128) using_opt ::= USING LP idlist RP */ -- 256, /* (129) using_opt ::= */ -- 244, /* (130) orderby_opt ::= */ -- 244, /* (131) orderby_opt ::= ORDER BY sortlist */ -- 226, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ -- 226, /* (133) sortlist ::= expr sortorder nulls */ -- 214, /* (134) sortorder ::= ASC */ -- 214, /* (135) sortorder ::= DESC */ -- 214, /* (136) sortorder ::= */ -- 260, /* (137) nulls ::= NULLS FIRST */ -- 260, /* (138) nulls ::= NULLS LAST */ -- 260, /* (139) nulls ::= */ -- 242, /* (140) groupby_opt ::= */ -- 242, /* (141) groupby_opt ::= GROUP BY nexprlist */ -- 243, /* (142) having_opt ::= */ -- 243, /* (143) having_opt ::= HAVING expr */ -- 245, /* (144) limit_opt ::= */ -- 245, /* (145) limit_opt ::= LIMIT expr */ -- 245, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ -- 245, /* (147) limit_opt ::= LIMIT expr COMMA expr */ -- 186, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ -- 241, /* (149) where_opt ::= */ -- 241, /* (150) where_opt ::= WHERE expr */ -- 186, /* (151) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt */ -- 262, /* (152) setlist ::= setlist COMMA nm EQ expr */ -- 262, /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */ -- 262, /* (154) setlist ::= nm EQ expr */ -- 262, /* (155) setlist ::= LP idlist RP EQ expr */ -- 186, /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ -- 186, /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ -- 265, /* (158) upsert ::= */ -- 265, /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ -- 265, /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ -- 265, /* (161) upsert ::= ON CONFLICT DO NOTHING */ -- 263, /* (162) insert_cmd ::= INSERT orconf */ -- 263, /* (163) insert_cmd ::= REPLACE */ -- 264, /* (164) idlist_opt ::= */ -- 264, /* (165) idlist_opt ::= LP idlist RP */ -- 259, /* (166) idlist ::= idlist COMMA nm */ -- 259, /* (167) idlist ::= nm */ -- 212, /* (168) expr ::= LP expr RP */ -- 212, /* (169) expr ::= ID|INDEXED */ -- 212, /* (170) expr ::= JOIN_KW */ -- 212, /* (171) expr ::= nm DOT nm */ -- 212, /* (172) expr ::= nm DOT nm DOT nm */ -- 211, /* (173) term ::= NULL|FLOAT|BLOB */ -- 211, /* (174) term ::= STRING */ -- 211, /* (175) term ::= INTEGER */ -- 212, /* (176) expr ::= VARIABLE */ -- 212, /* (177) expr ::= expr COLLATE ID|STRING */ -- 212, /* (178) expr ::= CAST LP expr AS typetoken RP */ -- 212, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */ -- 212, /* (180) expr ::= ID|INDEXED LP STAR RP */ -- 212, /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ -- 212, /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */ -- 211, /* (183) term ::= CTIME_KW */ -- 212, /* (184) expr ::= LP nexprlist COMMA expr RP */ -- 212, /* (185) expr ::= expr AND expr */ -- 212, /* (186) expr ::= expr OR expr */ -- 212, /* (187) expr ::= expr LT|GT|GE|LE expr */ -- 212, /* (188) expr ::= expr EQ|NE expr */ -- 212, /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ -- 212, /* (190) expr ::= expr PLUS|MINUS expr */ -- 212, /* (191) expr ::= expr STAR|SLASH|REM expr */ -- 212, /* (192) expr ::= expr CONCAT expr */ -- 267, /* (193) likeop ::= NOT LIKE_KW|MATCH */ -- 212, /* (194) expr ::= expr likeop expr */ -- 212, /* (195) expr ::= expr likeop expr ESCAPE expr */ -- 212, /* (196) expr ::= expr ISNULL|NOTNULL */ -- 212, /* (197) expr ::= expr NOT NULL */ -- 212, /* (198) expr ::= expr IS expr */ -- 212, /* (199) expr ::= expr IS NOT expr */ -- 212, /* (200) expr ::= NOT expr */ -- 212, /* (201) expr ::= BITNOT expr */ -- 212, /* (202) expr ::= PLUS|MINUS expr */ -- 268, /* (203) between_op ::= BETWEEN */ -- 268, /* (204) between_op ::= NOT BETWEEN */ -- 212, /* (205) expr ::= expr between_op expr AND expr */ -- 269, /* (206) in_op ::= IN */ -- 269, /* (207) in_op ::= NOT IN */ -- 212, /* (208) expr ::= expr in_op LP exprlist RP */ -- 212, /* (209) expr ::= LP select RP */ -- 212, /* (210) expr ::= expr in_op LP select RP */ -- 212, /* (211) expr ::= expr in_op nm dbnm paren_exprlist */ -- 212, /* (212) expr ::= EXISTS LP select RP */ -- 212, /* (213) expr ::= CASE case_operand case_exprlist case_else END */ -- 272, /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */ -- 272, /* (215) case_exprlist ::= WHEN expr THEN expr */ -- 273, /* (216) case_else ::= ELSE expr */ -- 273, /* (217) case_else ::= */ -- 271, /* (218) case_operand ::= expr */ -- 271, /* (219) case_operand ::= */ -- 257, /* (220) exprlist ::= */ -- 248, /* (221) nexprlist ::= nexprlist COMMA expr */ -- 248, /* (222) nexprlist ::= expr */ -- 270, /* (223) paren_exprlist ::= */ -- 270, /* (224) paren_exprlist ::= LP exprlist RP */ -- 186, /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ -- 274, /* (226) uniqueflag ::= UNIQUE */ -- 274, /* (227) uniqueflag ::= */ -- 216, /* (228) eidlist_opt ::= */ -- 216, /* (229) eidlist_opt ::= LP eidlist RP */ -- 227, /* (230) eidlist ::= eidlist COMMA nm collate sortorder */ -- 227, /* (231) eidlist ::= nm collate sortorder */ -- 275, /* (232) collate ::= */ -- 275, /* (233) collate ::= COLLATE ID|STRING */ -- 186, /* (234) cmd ::= DROP INDEX ifexists fullname */ -- 186, /* (235) cmd ::= VACUUM vinto */ -- 186, /* (236) cmd ::= VACUUM nm vinto */ -- 276, /* (237) vinto ::= INTO expr */ -- 276, /* (238) vinto ::= */ -- 186, /* (239) cmd ::= PRAGMA nm dbnm */ -- 186, /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */ -- 186, /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */ -- 186, /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */ -- 186, /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */ -- 206, /* (244) plus_num ::= PLUS INTEGER|FLOAT */ -- 207, /* (245) minus_num ::= MINUS INTEGER|FLOAT */ -- 186, /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ -- 278, /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ -- 280, /* (248) trigger_time ::= BEFORE|AFTER */ -- 280, /* (249) trigger_time ::= INSTEAD OF */ -- 280, /* (250) trigger_time ::= */ -- 281, /* (251) trigger_event ::= DELETE|INSERT */ -- 281, /* (252) trigger_event ::= UPDATE */ -- 281, /* (253) trigger_event ::= UPDATE OF idlist */ -- 283, /* (254) when_clause ::= */ -- 283, /* (255) when_clause ::= WHEN expr */ -- 279, /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ -- 279, /* (257) trigger_cmd_list ::= trigger_cmd SEMI */ -- 285, /* (258) trnm ::= nm DOT nm */ -- 286, /* (259) tridxby ::= INDEXED BY nm */ -- 286, /* (260) tridxby ::= NOT INDEXED */ -- 284, /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -- 284, /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ -- 284, /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -- 284, /* (264) trigger_cmd ::= scanpt select scanpt */ -- 212, /* (265) expr ::= RAISE LP IGNORE RP */ -- 212, /* (266) expr ::= RAISE LP raisetype COMMA nm RP */ -- 231, /* (267) raisetype ::= ROLLBACK */ -- 231, /* (268) raisetype ::= ABORT */ -- 231, /* (269) raisetype ::= FAIL */ -- 186, /* (270) cmd ::= DROP TRIGGER ifexists fullname */ -- 186, /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ -- 186, /* (272) cmd ::= DETACH database_kw_opt expr */ -- 288, /* (273) key_opt ::= */ -- 288, /* (274) key_opt ::= KEY expr */ -- 186, /* (275) cmd ::= REINDEX */ -- 186, /* (276) cmd ::= REINDEX nm dbnm */ -- 186, /* (277) cmd ::= ANALYZE */ -- 186, /* (278) cmd ::= ANALYZE nm dbnm */ -- 186, /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */ -- 186, /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ -- 289, /* (281) add_column_fullname ::= fullname */ -- 186, /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ -- 186, /* (283) cmd ::= create_vtab */ -- 186, /* (284) cmd ::= create_vtab LP vtabarglist RP */ -- 291, /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ -- 293, /* (286) vtabarg ::= */ -- 294, /* (287) vtabargtoken ::= ANY */ -- 294, /* (288) vtabargtoken ::= lp anylist RP */ -- 295, /* (289) lp ::= LP */ -- 261, /* (290) with ::= WITH wqlist */ -- 261, /* (291) with ::= WITH RECURSIVE wqlist */ -- 236, /* (292) wqlist ::= nm eidlist_opt AS LP select RP */ -- 236, /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ -- 297, /* (294) windowdefn_list ::= windowdefn */ -- 297, /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */ -- 298, /* (296) windowdefn ::= nm AS LP window RP */ -- 299, /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ -- 299, /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ -- 299, /* (299) window ::= ORDER BY sortlist frame_opt */ -- 299, /* (300) window ::= nm ORDER BY sortlist frame_opt */ -- 299, /* (301) window ::= frame_opt */ -- 299, /* (302) window ::= nm frame_opt */ -- 300, /* (303) frame_opt ::= */ -- 300, /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ -- 300, /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ -- 304, /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */ -- 306, /* (307) frame_bound_s ::= frame_bound */ -- 306, /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */ -- 307, /* (309) frame_bound_e ::= frame_bound */ -- 307, /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */ -- 305, /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */ -- 305, /* (312) frame_bound ::= CURRENT ROW */ -- 308, /* (313) frame_exclude_opt ::= */ -- 308, /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */ -- 309, /* (315) frame_exclude ::= NO OTHERS */ -- 309, /* (316) frame_exclude ::= CURRENT ROW */ -- 309, /* (317) frame_exclude ::= GROUP|TIES */ -- 246, /* (318) window_clause ::= WINDOW windowdefn_list */ -- 266, /* (319) filter_over ::= filter_clause over_clause */ -- 266, /* (320) filter_over ::= over_clause */ -- 266, /* (321) filter_over ::= filter_clause */ -- 303, /* (322) over_clause ::= OVER LP window RP */ -- 303, /* (323) over_clause ::= OVER nm */ -- 302, /* (324) filter_clause ::= FILTER LP WHERE expr RP */ -- 181, /* (325) input ::= cmdlist */ -- 182, /* (326) cmdlist ::= cmdlist ecmd */ -- 182, /* (327) cmdlist ::= ecmd */ -- 183, /* (328) ecmd ::= SEMI */ -- 183, /* (329) ecmd ::= cmdx SEMI */ -- 183, /* (330) ecmd ::= explain cmdx SEMI */ -- 188, /* (331) trans_opt ::= */ -- 188, /* (332) trans_opt ::= TRANSACTION */ -- 188, /* (333) trans_opt ::= TRANSACTION nm */ -- 190, /* (334) savepoint_opt ::= SAVEPOINT */ -- 190, /* (335) savepoint_opt ::= */ -- 186, /* (336) cmd ::= create_table create_table_args */ -- 197, /* (337) columnlist ::= columnlist COMMA columnname carglist */ -- 197, /* (338) columnlist ::= columnname carglist */ -- 189, /* (339) nm ::= ID|INDEXED */ -- 189, /* (340) nm ::= STRING */ -- 189, /* (341) nm ::= JOIN_KW */ -- 203, /* (342) typetoken ::= typename */ -- 204, /* (343) typename ::= ID|STRING */ -- 205, /* (344) signed ::= plus_num */ -- 205, /* (345) signed ::= minus_num */ -- 202, /* (346) carglist ::= carglist ccons */ -- 202, /* (347) carglist ::= */ -- 210, /* (348) ccons ::= NULL onconf */ -- 210, /* (349) ccons ::= GENERATED ALWAYS AS generated */ -- 210, /* (350) ccons ::= AS generated */ -- 198, /* (351) conslist_opt ::= COMMA conslist */ -- 223, /* (352) conslist ::= conslist tconscomma tcons */ -- 223, /* (353) conslist ::= tcons */ -- 224, /* (354) tconscomma ::= */ -- 228, /* (355) defer_subclause_opt ::= defer_subclause */ -- 230, /* (356) resolvetype ::= raisetype */ -- 234, /* (357) selectnowith ::= oneselect */ -- 235, /* (358) oneselect ::= values */ -- 249, /* (359) sclp ::= selcollist COMMA */ -- 250, /* (360) as ::= ID|STRING */ -- 212, /* (361) expr ::= term */ -- 267, /* (362) likeop ::= LIKE_KW|MATCH */ -- 257, /* (363) exprlist ::= nexprlist */ -- 277, /* (364) nmnum ::= plus_num */ -- 277, /* (365) nmnum ::= nm */ -- 277, /* (366) nmnum ::= ON */ -- 277, /* (367) nmnum ::= DELETE */ -- 277, /* (368) nmnum ::= DEFAULT */ -- 206, /* (369) plus_num ::= INTEGER|FLOAT */ -- 282, /* (370) foreach_clause ::= */ -- 282, /* (371) foreach_clause ::= FOR EACH ROW */ -- 285, /* (372) trnm ::= nm */ -- 286, /* (373) tridxby ::= */ -- 287, /* (374) database_kw_opt ::= DATABASE */ -- 287, /* (375) database_kw_opt ::= */ -- 290, /* (376) kwcolumn_opt ::= */ -- 290, /* (377) kwcolumn_opt ::= COLUMNKW */ -- 292, /* (378) vtabarglist ::= vtabarg */ -- 292, /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ -- 293, /* (380) vtabarg ::= vtabarg vtabargtoken */ -- 296, /* (381) anylist ::= */ -- 296, /* (382) anylist ::= anylist LP anylist RP */ -- 296, /* (383) anylist ::= anylist ANY */ -- 261, /* (384) with ::= */ -+ 191, /* (0) explain ::= EXPLAIN */ -+ 191, /* (1) explain ::= EXPLAIN QUERY PLAN */ -+ 190, /* (2) cmdx ::= cmd */ -+ 192, /* (3) cmd ::= BEGIN transtype trans_opt */ -+ 193, /* (4) transtype ::= */ -+ 193, /* (5) transtype ::= DEFERRED */ -+ 193, /* (6) transtype ::= IMMEDIATE */ -+ 193, /* (7) transtype ::= EXCLUSIVE */ -+ 192, /* (8) cmd ::= COMMIT|END trans_opt */ -+ 192, /* (9) cmd ::= ROLLBACK trans_opt */ -+ 192, /* (10) cmd ::= SAVEPOINT nm */ -+ 192, /* (11) cmd ::= RELEASE savepoint_opt nm */ -+ 192, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ -+ 197, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ -+ 199, /* (14) createkw ::= CREATE */ -+ 201, /* (15) ifnotexists ::= */ -+ 201, /* (16) ifnotexists ::= IF NOT EXISTS */ -+ 200, /* (17) temp ::= TEMP */ -+ 200, /* (18) temp ::= */ -+ 198, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ -+ 198, /* (20) create_table_args ::= AS select */ -+ 205, /* (21) table_option_set ::= */ -+ 205, /* (22) table_option_set ::= table_option_set COMMA table_option */ -+ 207, /* (23) table_option ::= WITHOUT nm */ -+ 207, /* (24) table_option ::= nm */ -+ 208, /* (25) columnname ::= nm typetoken */ -+ 210, /* (26) typetoken ::= */ -+ 210, /* (27) typetoken ::= typename LP signed RP */ -+ 210, /* (28) typetoken ::= typename LP signed COMMA signed RP */ -+ 211, /* (29) typename ::= typename ID|STRING */ -+ 215, /* (30) scanpt ::= */ -+ 216, /* (31) scantok ::= */ -+ 217, /* (32) ccons ::= CONSTRAINT nm */ -+ 217, /* (33) ccons ::= DEFAULT scantok term */ -+ 217, /* (34) ccons ::= DEFAULT LP expr RP */ -+ 217, /* (35) ccons ::= DEFAULT PLUS scantok term */ -+ 217, /* (36) ccons ::= DEFAULT MINUS scantok term */ -+ 217, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ -+ 217, /* (38) ccons ::= NOT NULL onconf */ -+ 217, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ -+ 217, /* (40) ccons ::= UNIQUE onconf */ -+ 217, /* (41) ccons ::= CHECK LP expr RP */ -+ 217, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ -+ 217, /* (43) ccons ::= defer_subclause */ -+ 217, /* (44) ccons ::= COLLATE ID|STRING */ -+ 226, /* (45) generated ::= LP expr RP */ -+ 226, /* (46) generated ::= LP expr RP ID */ -+ 222, /* (47) autoinc ::= */ -+ 222, /* (48) autoinc ::= AUTOINCR */ -+ 224, /* (49) refargs ::= */ -+ 224, /* (50) refargs ::= refargs refarg */ -+ 227, /* (51) refarg ::= MATCH nm */ -+ 227, /* (52) refarg ::= ON INSERT refact */ -+ 227, /* (53) refarg ::= ON DELETE refact */ -+ 227, /* (54) refarg ::= ON UPDATE refact */ -+ 228, /* (55) refact ::= SET NULL */ -+ 228, /* (56) refact ::= SET DEFAULT */ -+ 228, /* (57) refact ::= CASCADE */ -+ 228, /* (58) refact ::= RESTRICT */ -+ 228, /* (59) refact ::= NO ACTION */ -+ 225, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -+ 225, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ -+ 229, /* (62) init_deferred_pred_opt ::= */ -+ 229, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ -+ 229, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -+ 204, /* (65) conslist_opt ::= */ -+ 231, /* (66) tconscomma ::= COMMA */ -+ 232, /* (67) tcons ::= CONSTRAINT nm */ -+ 232, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -+ 232, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ -+ 232, /* (70) tcons ::= CHECK LP expr RP onconf */ -+ 232, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ -+ 235, /* (72) defer_subclause_opt ::= */ -+ 220, /* (73) onconf ::= */ -+ 220, /* (74) onconf ::= ON CONFLICT resolvetype */ -+ 236, /* (75) orconf ::= */ -+ 236, /* (76) orconf ::= OR resolvetype */ -+ 237, /* (77) resolvetype ::= IGNORE */ -+ 237, /* (78) resolvetype ::= REPLACE */ -+ 192, /* (79) cmd ::= DROP TABLE ifexists fullname */ -+ 239, /* (80) ifexists ::= IF EXISTS */ -+ 239, /* (81) ifexists ::= */ -+ 192, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ -+ 192, /* (83) cmd ::= DROP VIEW ifexists fullname */ -+ 192, /* (84) cmd ::= select */ -+ 206, /* (85) select ::= WITH wqlist selectnowith */ -+ 206, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ -+ 206, /* (87) select ::= selectnowith */ -+ 241, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ -+ 244, /* (89) multiselect_op ::= UNION */ -+ 244, /* (90) multiselect_op ::= UNION ALL */ -+ 244, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ -+ 242, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -+ 242, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -+ 254, /* (94) values ::= VALUES LP nexprlist RP */ -+ 242, /* (95) oneselect ::= mvalues */ -+ 256, /* (96) mvalues ::= values COMMA LP nexprlist RP */ -+ 256, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ -+ 245, /* (98) distinct ::= DISTINCT */ -+ 245, /* (99) distinct ::= ALL */ -+ 245, /* (100) distinct ::= */ -+ 257, /* (101) sclp ::= */ -+ 246, /* (102) selcollist ::= sclp scanpt expr scanpt as */ -+ 246, /* (103) selcollist ::= sclp scanpt STAR */ -+ 246, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ -+ 258, /* (105) as ::= AS nm */ -+ 258, /* (106) as ::= */ -+ 247, /* (107) from ::= */ -+ 247, /* (108) from ::= FROM seltablist */ -+ 260, /* (109) stl_prefix ::= seltablist joinop */ -+ 260, /* (110) stl_prefix ::= */ -+ 259, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ -+ 259, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ -+ 259, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ -+ 259, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ -+ 259, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ -+ 202, /* (116) dbnm ::= */ -+ 202, /* (117) dbnm ::= DOT nm */ -+ 240, /* (118) fullname ::= nm */ -+ 240, /* (119) fullname ::= nm DOT nm */ -+ 265, /* (120) xfullname ::= nm */ -+ 265, /* (121) xfullname ::= nm DOT nm */ -+ 265, /* (122) xfullname ::= nm DOT nm AS nm */ -+ 265, /* (123) xfullname ::= nm AS nm */ -+ 261, /* (124) joinop ::= COMMA|JOIN */ -+ 261, /* (125) joinop ::= JOIN_KW JOIN */ -+ 261, /* (126) joinop ::= JOIN_KW nm JOIN */ -+ 261, /* (127) joinop ::= JOIN_KW nm nm JOIN */ -+ 262, /* (128) on_using ::= ON expr */ -+ 262, /* (129) on_using ::= USING LP idlist RP */ -+ 262, /* (130) on_using ::= */ -+ 267, /* (131) indexed_opt ::= */ -+ 263, /* (132) indexed_by ::= INDEXED BY nm */ -+ 263, /* (133) indexed_by ::= NOT INDEXED */ -+ 251, /* (134) orderby_opt ::= */ -+ 251, /* (135) orderby_opt ::= ORDER BY sortlist */ -+ 233, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ -+ 233, /* (137) sortlist ::= expr sortorder nulls */ -+ 221, /* (138) sortorder ::= ASC */ -+ 221, /* (139) sortorder ::= DESC */ -+ 221, /* (140) sortorder ::= */ -+ 268, /* (141) nulls ::= NULLS FIRST */ -+ 268, /* (142) nulls ::= NULLS LAST */ -+ 268, /* (143) nulls ::= */ -+ 249, /* (144) groupby_opt ::= */ -+ 249, /* (145) groupby_opt ::= GROUP BY nexprlist */ -+ 250, /* (146) having_opt ::= */ -+ 250, /* (147) having_opt ::= HAVING expr */ -+ 252, /* (148) limit_opt ::= */ -+ 252, /* (149) limit_opt ::= LIMIT expr */ -+ 252, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ -+ 252, /* (151) limit_opt ::= LIMIT expr COMMA expr */ -+ 192, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ -+ 248, /* (153) where_opt ::= */ -+ 248, /* (154) where_opt ::= WHERE expr */ -+ 270, /* (155) where_opt_ret ::= */ -+ 270, /* (156) where_opt_ret ::= WHERE expr */ -+ 270, /* (157) where_opt_ret ::= RETURNING selcollist */ -+ 270, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ -+ 192, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ -+ 271, /* (160) setlist ::= setlist COMMA nm EQ expr */ -+ 271, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ -+ 271, /* (162) setlist ::= nm EQ expr */ -+ 271, /* (163) setlist ::= LP idlist RP EQ expr */ -+ 192, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ -+ 192, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ -+ 274, /* (166) upsert ::= */ -+ 274, /* (167) upsert ::= RETURNING selcollist */ -+ 274, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -+ 274, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -+ 274, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ -+ 274, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -+ 275, /* (172) returning ::= RETURNING selcollist */ -+ 272, /* (173) insert_cmd ::= INSERT orconf */ -+ 272, /* (174) insert_cmd ::= REPLACE */ -+ 273, /* (175) idlist_opt ::= */ -+ 273, /* (176) idlist_opt ::= LP idlist RP */ -+ 266, /* (177) idlist ::= idlist COMMA nm */ -+ 266, /* (178) idlist ::= nm */ -+ 219, /* (179) expr ::= LP expr RP */ -+ 219, /* (180) expr ::= ID|INDEXED|JOIN_KW */ -+ 219, /* (181) expr ::= nm DOT nm */ -+ 219, /* (182) expr ::= nm DOT nm DOT nm */ -+ 218, /* (183) term ::= NULL|FLOAT|BLOB */ -+ 218, /* (184) term ::= STRING */ -+ 218, /* (185) term ::= INTEGER */ -+ 219, /* (186) expr ::= VARIABLE */ -+ 219, /* (187) expr ::= expr COLLATE ID|STRING */ -+ 219, /* (188) expr ::= CAST LP expr AS typetoken RP */ -+ 219, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ -+ 219, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ -+ 219, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ -+ 219, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ -+ 219, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ -+ 219, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ -+ 218, /* (195) term ::= CTIME_KW */ -+ 219, /* (196) expr ::= LP nexprlist COMMA expr RP */ -+ 219, /* (197) expr ::= expr AND expr */ -+ 219, /* (198) expr ::= expr OR expr */ -+ 219, /* (199) expr ::= expr LT|GT|GE|LE expr */ -+ 219, /* (200) expr ::= expr EQ|NE expr */ -+ 219, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ -+ 219, /* (202) expr ::= expr PLUS|MINUS expr */ -+ 219, /* (203) expr ::= expr STAR|SLASH|REM expr */ -+ 219, /* (204) expr ::= expr CONCAT expr */ -+ 277, /* (205) likeop ::= NOT LIKE_KW|MATCH */ -+ 219, /* (206) expr ::= expr likeop expr */ -+ 219, /* (207) expr ::= expr likeop expr ESCAPE expr */ -+ 219, /* (208) expr ::= expr ISNULL|NOTNULL */ -+ 219, /* (209) expr ::= expr NOT NULL */ -+ 219, /* (210) expr ::= expr IS expr */ -+ 219, /* (211) expr ::= expr IS NOT expr */ -+ 219, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ -+ 219, /* (213) expr ::= expr IS DISTINCT FROM expr */ -+ 219, /* (214) expr ::= NOT expr */ -+ 219, /* (215) expr ::= BITNOT expr */ -+ 219, /* (216) expr ::= PLUS|MINUS expr */ -+ 219, /* (217) expr ::= expr PTR expr */ -+ 278, /* (218) between_op ::= BETWEEN */ -+ 278, /* (219) between_op ::= NOT BETWEEN */ -+ 219, /* (220) expr ::= expr between_op expr AND expr */ -+ 279, /* (221) in_op ::= IN */ -+ 279, /* (222) in_op ::= NOT IN */ -+ 219, /* (223) expr ::= expr in_op LP exprlist RP */ -+ 219, /* (224) expr ::= LP select RP */ -+ 219, /* (225) expr ::= expr in_op LP select RP */ -+ 219, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ -+ 219, /* (227) expr ::= EXISTS LP select RP */ -+ 219, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ -+ 282, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ -+ 282, /* (230) case_exprlist ::= WHEN expr THEN expr */ -+ 283, /* (231) case_else ::= ELSE expr */ -+ 283, /* (232) case_else ::= */ -+ 281, /* (233) case_operand ::= */ -+ 264, /* (234) exprlist ::= */ -+ 255, /* (235) nexprlist ::= nexprlist COMMA expr */ -+ 255, /* (236) nexprlist ::= expr */ -+ 280, /* (237) paren_exprlist ::= */ -+ 280, /* (238) paren_exprlist ::= LP exprlist RP */ -+ 192, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ -+ 284, /* (240) uniqueflag ::= UNIQUE */ -+ 284, /* (241) uniqueflag ::= */ -+ 223, /* (242) eidlist_opt ::= */ -+ 223, /* (243) eidlist_opt ::= LP eidlist RP */ -+ 234, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ -+ 234, /* (245) eidlist ::= nm collate sortorder */ -+ 285, /* (246) collate ::= */ -+ 285, /* (247) collate ::= COLLATE ID|STRING */ -+ 192, /* (248) cmd ::= DROP INDEX ifexists fullname */ -+ 192, /* (249) cmd ::= VACUUM vinto */ -+ 192, /* (250) cmd ::= VACUUM nm vinto */ -+ 286, /* (251) vinto ::= INTO expr */ -+ 286, /* (252) vinto ::= */ -+ 192, /* (253) cmd ::= PRAGMA nm dbnm */ -+ 192, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ -+ 192, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ -+ 192, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ -+ 192, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ -+ 213, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ -+ 214, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ -+ 192, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ -+ 288, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ -+ 290, /* (262) trigger_time ::= BEFORE|AFTER */ -+ 290, /* (263) trigger_time ::= INSTEAD OF */ -+ 290, /* (264) trigger_time ::= */ -+ 291, /* (265) trigger_event ::= DELETE|INSERT */ -+ 291, /* (266) trigger_event ::= UPDATE */ -+ 291, /* (267) trigger_event ::= UPDATE OF idlist */ -+ 293, /* (268) when_clause ::= */ -+ 293, /* (269) when_clause ::= WHEN expr */ -+ 289, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ -+ 289, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ -+ 295, /* (272) trnm ::= nm DOT nm */ -+ 296, /* (273) tridxby ::= INDEXED BY nm */ -+ 296, /* (274) tridxby ::= NOT INDEXED */ -+ 294, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -+ 294, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ -+ 294, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -+ 294, /* (278) trigger_cmd ::= scanpt select scanpt */ -+ 219, /* (279) expr ::= RAISE LP IGNORE RP */ -+ 219, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ -+ 238, /* (281) raisetype ::= ROLLBACK */ -+ 238, /* (282) raisetype ::= ABORT */ -+ 238, /* (283) raisetype ::= FAIL */ -+ 192, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ -+ 192, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ -+ 192, /* (286) cmd ::= DETACH database_kw_opt expr */ -+ 298, /* (287) key_opt ::= */ -+ 298, /* (288) key_opt ::= KEY expr */ -+ 192, /* (289) cmd ::= REINDEX */ -+ 192, /* (290) cmd ::= REINDEX nm dbnm */ -+ 192, /* (291) cmd ::= ANALYZE */ -+ 192, /* (292) cmd ::= ANALYZE nm dbnm */ -+ 192, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ -+ 192, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ -+ 192, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ -+ 299, /* (296) add_column_fullname ::= fullname */ -+ 192, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ -+ 192, /* (298) cmd ::= create_vtab */ -+ 192, /* (299) cmd ::= create_vtab LP vtabarglist RP */ -+ 301, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ -+ 303, /* (301) vtabarg ::= */ -+ 304, /* (302) vtabargtoken ::= ANY */ -+ 304, /* (303) vtabargtoken ::= lp anylist RP */ -+ 305, /* (304) lp ::= LP */ -+ 269, /* (305) with ::= WITH wqlist */ -+ 269, /* (306) with ::= WITH RECURSIVE wqlist */ -+ 308, /* (307) wqas ::= AS */ -+ 308, /* (308) wqas ::= AS MATERIALIZED */ -+ 308, /* (309) wqas ::= AS NOT MATERIALIZED */ -+ 307, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ -+ 309, /* (311) withnm ::= nm */ -+ 243, /* (312) wqlist ::= wqitem */ -+ 243, /* (313) wqlist ::= wqlist COMMA wqitem */ -+ 310, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ -+ 311, /* (315) windowdefn ::= nm AS LP window RP */ -+ 312, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ -+ 312, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ -+ 312, /* (318) window ::= ORDER BY sortlist frame_opt */ -+ 312, /* (319) window ::= nm ORDER BY sortlist frame_opt */ -+ 312, /* (320) window ::= nm frame_opt */ -+ 313, /* (321) frame_opt ::= */ -+ 313, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ -+ 313, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ -+ 317, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ -+ 319, /* (325) frame_bound_s ::= frame_bound */ -+ 319, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ -+ 320, /* (327) frame_bound_e ::= frame_bound */ -+ 320, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ -+ 318, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ -+ 318, /* (330) frame_bound ::= CURRENT ROW */ -+ 321, /* (331) frame_exclude_opt ::= */ -+ 321, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ -+ 322, /* (333) frame_exclude ::= NO OTHERS */ -+ 322, /* (334) frame_exclude ::= CURRENT ROW */ -+ 322, /* (335) frame_exclude ::= GROUP|TIES */ -+ 253, /* (336) window_clause ::= WINDOW windowdefn_list */ -+ 276, /* (337) filter_over ::= filter_clause over_clause */ -+ 276, /* (338) filter_over ::= over_clause */ -+ 276, /* (339) filter_over ::= filter_clause */ -+ 316, /* (340) over_clause ::= OVER LP window RP */ -+ 316, /* (341) over_clause ::= OVER nm */ -+ 315, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ -+ 218, /* (343) term ::= QNUMBER */ -+ 187, /* (344) input ::= cmdlist */ -+ 188, /* (345) cmdlist ::= cmdlist ecmd */ -+ 188, /* (346) cmdlist ::= ecmd */ -+ 189, /* (347) ecmd ::= SEMI */ -+ 189, /* (348) ecmd ::= cmdx SEMI */ -+ 189, /* (349) ecmd ::= explain cmdx SEMI */ -+ 194, /* (350) trans_opt ::= */ -+ 194, /* (351) trans_opt ::= TRANSACTION */ -+ 194, /* (352) trans_opt ::= TRANSACTION nm */ -+ 196, /* (353) savepoint_opt ::= SAVEPOINT */ -+ 196, /* (354) savepoint_opt ::= */ -+ 192, /* (355) cmd ::= create_table create_table_args */ -+ 205, /* (356) table_option_set ::= table_option */ -+ 203, /* (357) columnlist ::= columnlist COMMA columnname carglist */ -+ 203, /* (358) columnlist ::= columnname carglist */ -+ 195, /* (359) nm ::= ID|INDEXED|JOIN_KW */ -+ 195, /* (360) nm ::= STRING */ -+ 210, /* (361) typetoken ::= typename */ -+ 211, /* (362) typename ::= ID|STRING */ -+ 212, /* (363) signed ::= plus_num */ -+ 212, /* (364) signed ::= minus_num */ -+ 209, /* (365) carglist ::= carglist ccons */ -+ 209, /* (366) carglist ::= */ -+ 217, /* (367) ccons ::= NULL onconf */ -+ 217, /* (368) ccons ::= GENERATED ALWAYS AS generated */ -+ 217, /* (369) ccons ::= AS generated */ -+ 204, /* (370) conslist_opt ::= COMMA conslist */ -+ 230, /* (371) conslist ::= conslist tconscomma tcons */ -+ 230, /* (372) conslist ::= tcons */ -+ 231, /* (373) tconscomma ::= */ -+ 235, /* (374) defer_subclause_opt ::= defer_subclause */ -+ 237, /* (375) resolvetype ::= raisetype */ -+ 241, /* (376) selectnowith ::= oneselect */ -+ 242, /* (377) oneselect ::= values */ -+ 257, /* (378) sclp ::= selcollist COMMA */ -+ 258, /* (379) as ::= ID|STRING */ -+ 267, /* (380) indexed_opt ::= indexed_by */ -+ 275, /* (381) returning ::= */ -+ 219, /* (382) expr ::= term */ -+ 277, /* (383) likeop ::= LIKE_KW|MATCH */ -+ 281, /* (384) case_operand ::= expr */ -+ 264, /* (385) exprlist ::= nexprlist */ -+ 287, /* (386) nmnum ::= plus_num */ -+ 287, /* (387) nmnum ::= nm */ -+ 287, /* (388) nmnum ::= ON */ -+ 287, /* (389) nmnum ::= DELETE */ -+ 287, /* (390) nmnum ::= DEFAULT */ -+ 213, /* (391) plus_num ::= INTEGER|FLOAT */ -+ 292, /* (392) foreach_clause ::= */ -+ 292, /* (393) foreach_clause ::= FOR EACH ROW */ -+ 295, /* (394) trnm ::= nm */ -+ 296, /* (395) tridxby ::= */ -+ 297, /* (396) database_kw_opt ::= DATABASE */ -+ 297, /* (397) database_kw_opt ::= */ -+ 300, /* (398) kwcolumn_opt ::= */ -+ 300, /* (399) kwcolumn_opt ::= COLUMNKW */ -+ 302, /* (400) vtabarglist ::= vtabarg */ -+ 302, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ -+ 303, /* (402) vtabarg ::= vtabarg vtabargtoken */ -+ 306, /* (403) anylist ::= */ -+ 306, /* (404) anylist ::= anylist LP anylist RP */ -+ 306, /* (405) anylist ::= anylist ANY */ -+ 269, /* (406) with ::= */ -+ 310, /* (407) windowdefn_list ::= windowdefn */ -+ 312, /* (408) window ::= frame_opt */ - }; - - /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number -@@ -157080,372 +178520,396 @@ static const signed char yyRuleInfoNRhs[] = { - -3, /* (16) ifnotexists ::= IF NOT EXISTS */ - -1, /* (17) temp ::= TEMP */ - 0, /* (18) temp ::= */ -- -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ -+ -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ - -2, /* (20) create_table_args ::= AS select */ -- 0, /* (21) table_options ::= */ -- -2, /* (22) table_options ::= WITHOUT nm */ -- -2, /* (23) columnname ::= nm typetoken */ -- 0, /* (24) typetoken ::= */ -- -4, /* (25) typetoken ::= typename LP signed RP */ -- -6, /* (26) typetoken ::= typename LP signed COMMA signed RP */ -- -2, /* (27) typename ::= typename ID|STRING */ -- 0, /* (28) scanpt ::= */ -- 0, /* (29) scantok ::= */ -- -2, /* (30) ccons ::= CONSTRAINT nm */ -- -3, /* (31) ccons ::= DEFAULT scantok term */ -- -4, /* (32) ccons ::= DEFAULT LP expr RP */ -- -4, /* (33) ccons ::= DEFAULT PLUS scantok term */ -- -4, /* (34) ccons ::= DEFAULT MINUS scantok term */ -- -3, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */ -- -3, /* (36) ccons ::= NOT NULL onconf */ -- -5, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */ -- -2, /* (38) ccons ::= UNIQUE onconf */ -- -4, /* (39) ccons ::= CHECK LP expr RP */ -- -4, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */ -- -1, /* (41) ccons ::= defer_subclause */ -- -2, /* (42) ccons ::= COLLATE ID|STRING */ -- -3, /* (43) generated ::= LP expr RP */ -- -4, /* (44) generated ::= LP expr RP ID */ -- 0, /* (45) autoinc ::= */ -- -1, /* (46) autoinc ::= AUTOINCR */ -- 0, /* (47) refargs ::= */ -- -2, /* (48) refargs ::= refargs refarg */ -- -2, /* (49) refarg ::= MATCH nm */ -- -3, /* (50) refarg ::= ON INSERT refact */ -- -3, /* (51) refarg ::= ON DELETE refact */ -- -3, /* (52) refarg ::= ON UPDATE refact */ -- -2, /* (53) refact ::= SET NULL */ -- -2, /* (54) refact ::= SET DEFAULT */ -- -1, /* (55) refact ::= CASCADE */ -- -1, /* (56) refact ::= RESTRICT */ -- -2, /* (57) refact ::= NO ACTION */ -- -3, /* (58) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -- -2, /* (59) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ -- 0, /* (60) init_deferred_pred_opt ::= */ -- -2, /* (61) init_deferred_pred_opt ::= INITIALLY DEFERRED */ -- -2, /* (62) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -- 0, /* (63) conslist_opt ::= */ -- -1, /* (64) tconscomma ::= COMMA */ -- -2, /* (65) tcons ::= CONSTRAINT nm */ -- -7, /* (66) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -- -5, /* (67) tcons ::= UNIQUE LP sortlist RP onconf */ -- -5, /* (68) tcons ::= CHECK LP expr RP onconf */ -- -10, /* (69) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ -- 0, /* (70) defer_subclause_opt ::= */ -- 0, /* (71) onconf ::= */ -- -3, /* (72) onconf ::= ON CONFLICT resolvetype */ -- 0, /* (73) orconf ::= */ -- -2, /* (74) orconf ::= OR resolvetype */ -- -1, /* (75) resolvetype ::= IGNORE */ -- -1, /* (76) resolvetype ::= REPLACE */ -- -4, /* (77) cmd ::= DROP TABLE ifexists fullname */ -- -2, /* (78) ifexists ::= IF EXISTS */ -- 0, /* (79) ifexists ::= */ -- -9, /* (80) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ -- -4, /* (81) cmd ::= DROP VIEW ifexists fullname */ -- -1, /* (82) cmd ::= select */ -- -3, /* (83) select ::= WITH wqlist selectnowith */ -- -4, /* (84) select ::= WITH RECURSIVE wqlist selectnowith */ -- -1, /* (85) select ::= selectnowith */ -- -3, /* (86) selectnowith ::= selectnowith multiselect_op oneselect */ -- -1, /* (87) multiselect_op ::= UNION */ -- -2, /* (88) multiselect_op ::= UNION ALL */ -- -1, /* (89) multiselect_op ::= EXCEPT|INTERSECT */ -- -9, /* (90) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -- -10, /* (91) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -- -4, /* (92) values ::= VALUES LP nexprlist RP */ -- -5, /* (93) values ::= values COMMA LP nexprlist RP */ -- -1, /* (94) distinct ::= DISTINCT */ -- -1, /* (95) distinct ::= ALL */ -- 0, /* (96) distinct ::= */ -- 0, /* (97) sclp ::= */ -- -5, /* (98) selcollist ::= sclp scanpt expr scanpt as */ -- -3, /* (99) selcollist ::= sclp scanpt STAR */ -- -5, /* (100) selcollist ::= sclp scanpt nm DOT STAR */ -- -2, /* (101) as ::= AS nm */ -- 0, /* (102) as ::= */ -- 0, /* (103) from ::= */ -- -2, /* (104) from ::= FROM seltablist */ -- -2, /* (105) stl_prefix ::= seltablist joinop */ -- 0, /* (106) stl_prefix ::= */ -- -7, /* (107) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ -- -9, /* (108) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ -- -7, /* (109) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ -- -7, /* (110) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ -- 0, /* (111) dbnm ::= */ -- -2, /* (112) dbnm ::= DOT nm */ -- -1, /* (113) fullname ::= nm */ -- -3, /* (114) fullname ::= nm DOT nm */ -- -1, /* (115) xfullname ::= nm */ -- -3, /* (116) xfullname ::= nm DOT nm */ -- -5, /* (117) xfullname ::= nm DOT nm AS nm */ -- -3, /* (118) xfullname ::= nm AS nm */ -- -1, /* (119) joinop ::= COMMA|JOIN */ -- -2, /* (120) joinop ::= JOIN_KW JOIN */ -- -3, /* (121) joinop ::= JOIN_KW nm JOIN */ -- -4, /* (122) joinop ::= JOIN_KW nm nm JOIN */ -- -2, /* (123) on_opt ::= ON expr */ -- 0, /* (124) on_opt ::= */ -- 0, /* (125) indexed_opt ::= */ -- -3, /* (126) indexed_opt ::= INDEXED BY nm */ -- -2, /* (127) indexed_opt ::= NOT INDEXED */ -- -4, /* (128) using_opt ::= USING LP idlist RP */ -- 0, /* (129) using_opt ::= */ -- 0, /* (130) orderby_opt ::= */ -- -3, /* (131) orderby_opt ::= ORDER BY sortlist */ -- -5, /* (132) sortlist ::= sortlist COMMA expr sortorder nulls */ -- -3, /* (133) sortlist ::= expr sortorder nulls */ -- -1, /* (134) sortorder ::= ASC */ -- -1, /* (135) sortorder ::= DESC */ -- 0, /* (136) sortorder ::= */ -- -2, /* (137) nulls ::= NULLS FIRST */ -- -2, /* (138) nulls ::= NULLS LAST */ -- 0, /* (139) nulls ::= */ -- 0, /* (140) groupby_opt ::= */ -- -3, /* (141) groupby_opt ::= GROUP BY nexprlist */ -- 0, /* (142) having_opt ::= */ -- -2, /* (143) having_opt ::= HAVING expr */ -- 0, /* (144) limit_opt ::= */ -- -2, /* (145) limit_opt ::= LIMIT expr */ -- -4, /* (146) limit_opt ::= LIMIT expr OFFSET expr */ -- -4, /* (147) limit_opt ::= LIMIT expr COMMA expr */ -- -6, /* (148) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ -- 0, /* (149) where_opt ::= */ -- -2, /* (150) where_opt ::= WHERE expr */ -- -9, /* (151) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt */ -- -5, /* (152) setlist ::= setlist COMMA nm EQ expr */ -- -7, /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */ -- -3, /* (154) setlist ::= nm EQ expr */ -- -5, /* (155) setlist ::= LP idlist RP EQ expr */ -- -7, /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ -- -7, /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ -- 0, /* (158) upsert ::= */ -- -11, /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ -- -8, /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ -- -4, /* (161) upsert ::= ON CONFLICT DO NOTHING */ -- -2, /* (162) insert_cmd ::= INSERT orconf */ -- -1, /* (163) insert_cmd ::= REPLACE */ -- 0, /* (164) idlist_opt ::= */ -- -3, /* (165) idlist_opt ::= LP idlist RP */ -- -3, /* (166) idlist ::= idlist COMMA nm */ -- -1, /* (167) idlist ::= nm */ -- -3, /* (168) expr ::= LP expr RP */ -- -1, /* (169) expr ::= ID|INDEXED */ -- -1, /* (170) expr ::= JOIN_KW */ -- -3, /* (171) expr ::= nm DOT nm */ -- -5, /* (172) expr ::= nm DOT nm DOT nm */ -- -1, /* (173) term ::= NULL|FLOAT|BLOB */ -- -1, /* (174) term ::= STRING */ -- -1, /* (175) term ::= INTEGER */ -- -1, /* (176) expr ::= VARIABLE */ -- -3, /* (177) expr ::= expr COLLATE ID|STRING */ -- -6, /* (178) expr ::= CAST LP expr AS typetoken RP */ -- -5, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */ -- -4, /* (180) expr ::= ID|INDEXED LP STAR RP */ -- -6, /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ -- -5, /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */ -- -1, /* (183) term ::= CTIME_KW */ -- -5, /* (184) expr ::= LP nexprlist COMMA expr RP */ -- -3, /* (185) expr ::= expr AND expr */ -- -3, /* (186) expr ::= expr OR expr */ -- -3, /* (187) expr ::= expr LT|GT|GE|LE expr */ -- -3, /* (188) expr ::= expr EQ|NE expr */ -- -3, /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ -- -3, /* (190) expr ::= expr PLUS|MINUS expr */ -- -3, /* (191) expr ::= expr STAR|SLASH|REM expr */ -- -3, /* (192) expr ::= expr CONCAT expr */ -- -2, /* (193) likeop ::= NOT LIKE_KW|MATCH */ -- -3, /* (194) expr ::= expr likeop expr */ -- -5, /* (195) expr ::= expr likeop expr ESCAPE expr */ -- -2, /* (196) expr ::= expr ISNULL|NOTNULL */ -- -3, /* (197) expr ::= expr NOT NULL */ -- -3, /* (198) expr ::= expr IS expr */ -- -4, /* (199) expr ::= expr IS NOT expr */ -- -2, /* (200) expr ::= NOT expr */ -- -2, /* (201) expr ::= BITNOT expr */ -- -2, /* (202) expr ::= PLUS|MINUS expr */ -- -1, /* (203) between_op ::= BETWEEN */ -- -2, /* (204) between_op ::= NOT BETWEEN */ -- -5, /* (205) expr ::= expr between_op expr AND expr */ -- -1, /* (206) in_op ::= IN */ -- -2, /* (207) in_op ::= NOT IN */ -- -5, /* (208) expr ::= expr in_op LP exprlist RP */ -- -3, /* (209) expr ::= LP select RP */ -- -5, /* (210) expr ::= expr in_op LP select RP */ -- -5, /* (211) expr ::= expr in_op nm dbnm paren_exprlist */ -- -4, /* (212) expr ::= EXISTS LP select RP */ -- -5, /* (213) expr ::= CASE case_operand case_exprlist case_else END */ -- -5, /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */ -- -4, /* (215) case_exprlist ::= WHEN expr THEN expr */ -- -2, /* (216) case_else ::= ELSE expr */ -- 0, /* (217) case_else ::= */ -- -1, /* (218) case_operand ::= expr */ -- 0, /* (219) case_operand ::= */ -- 0, /* (220) exprlist ::= */ -- -3, /* (221) nexprlist ::= nexprlist COMMA expr */ -- -1, /* (222) nexprlist ::= expr */ -- 0, /* (223) paren_exprlist ::= */ -- -3, /* (224) paren_exprlist ::= LP exprlist RP */ -- -12, /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ -- -1, /* (226) uniqueflag ::= UNIQUE */ -- 0, /* (227) uniqueflag ::= */ -- 0, /* (228) eidlist_opt ::= */ -- -3, /* (229) eidlist_opt ::= LP eidlist RP */ -- -5, /* (230) eidlist ::= eidlist COMMA nm collate sortorder */ -- -3, /* (231) eidlist ::= nm collate sortorder */ -- 0, /* (232) collate ::= */ -- -2, /* (233) collate ::= COLLATE ID|STRING */ -- -4, /* (234) cmd ::= DROP INDEX ifexists fullname */ -- -2, /* (235) cmd ::= VACUUM vinto */ -- -3, /* (236) cmd ::= VACUUM nm vinto */ -- -2, /* (237) vinto ::= INTO expr */ -- 0, /* (238) vinto ::= */ -- -3, /* (239) cmd ::= PRAGMA nm dbnm */ -- -5, /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */ -- -6, /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */ -- -5, /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */ -- -6, /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */ -- -2, /* (244) plus_num ::= PLUS INTEGER|FLOAT */ -- -2, /* (245) minus_num ::= MINUS INTEGER|FLOAT */ -- -5, /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ -- -11, /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ -- -1, /* (248) trigger_time ::= BEFORE|AFTER */ -- -2, /* (249) trigger_time ::= INSTEAD OF */ -- 0, /* (250) trigger_time ::= */ -- -1, /* (251) trigger_event ::= DELETE|INSERT */ -- -1, /* (252) trigger_event ::= UPDATE */ -- -3, /* (253) trigger_event ::= UPDATE OF idlist */ -- 0, /* (254) when_clause ::= */ -- -2, /* (255) when_clause ::= WHEN expr */ -- -3, /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ -- -2, /* (257) trigger_cmd_list ::= trigger_cmd SEMI */ -- -3, /* (258) trnm ::= nm DOT nm */ -- -3, /* (259) tridxby ::= INDEXED BY nm */ -- -2, /* (260) tridxby ::= NOT INDEXED */ -- -9, /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -- -8, /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ -- -6, /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -- -3, /* (264) trigger_cmd ::= scanpt select scanpt */ -- -4, /* (265) expr ::= RAISE LP IGNORE RP */ -- -6, /* (266) expr ::= RAISE LP raisetype COMMA nm RP */ -- -1, /* (267) raisetype ::= ROLLBACK */ -- -1, /* (268) raisetype ::= ABORT */ -- -1, /* (269) raisetype ::= FAIL */ -- -4, /* (270) cmd ::= DROP TRIGGER ifexists fullname */ -- -6, /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ -- -3, /* (272) cmd ::= DETACH database_kw_opt expr */ -- 0, /* (273) key_opt ::= */ -- -2, /* (274) key_opt ::= KEY expr */ -- -1, /* (275) cmd ::= REINDEX */ -- -3, /* (276) cmd ::= REINDEX nm dbnm */ -- -1, /* (277) cmd ::= ANALYZE */ -- -3, /* (278) cmd ::= ANALYZE nm dbnm */ -- -6, /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */ -- -7, /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ -- -1, /* (281) add_column_fullname ::= fullname */ -- -8, /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ -- -1, /* (283) cmd ::= create_vtab */ -- -4, /* (284) cmd ::= create_vtab LP vtabarglist RP */ -- -8, /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ -- 0, /* (286) vtabarg ::= */ -- -1, /* (287) vtabargtoken ::= ANY */ -- -3, /* (288) vtabargtoken ::= lp anylist RP */ -- -1, /* (289) lp ::= LP */ -- -2, /* (290) with ::= WITH wqlist */ -- -3, /* (291) with ::= WITH RECURSIVE wqlist */ -- -6, /* (292) wqlist ::= nm eidlist_opt AS LP select RP */ -- -8, /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ -- -1, /* (294) windowdefn_list ::= windowdefn */ -- -3, /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */ -- -5, /* (296) windowdefn ::= nm AS LP window RP */ -- -5, /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ -- -6, /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ -- -4, /* (299) window ::= ORDER BY sortlist frame_opt */ -- -5, /* (300) window ::= nm ORDER BY sortlist frame_opt */ -- -1, /* (301) window ::= frame_opt */ -- -2, /* (302) window ::= nm frame_opt */ -- 0, /* (303) frame_opt ::= */ -- -3, /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ -- -6, /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ -- -1, /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */ -- -1, /* (307) frame_bound_s ::= frame_bound */ -- -2, /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */ -- -1, /* (309) frame_bound_e ::= frame_bound */ -- -2, /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */ -- -2, /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */ -- -2, /* (312) frame_bound ::= CURRENT ROW */ -- 0, /* (313) frame_exclude_opt ::= */ -- -2, /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */ -- -2, /* (315) frame_exclude ::= NO OTHERS */ -- -2, /* (316) frame_exclude ::= CURRENT ROW */ -- -1, /* (317) frame_exclude ::= GROUP|TIES */ -- -2, /* (318) window_clause ::= WINDOW windowdefn_list */ -- -2, /* (319) filter_over ::= filter_clause over_clause */ -- -1, /* (320) filter_over ::= over_clause */ -- -1, /* (321) filter_over ::= filter_clause */ -- -4, /* (322) over_clause ::= OVER LP window RP */ -- -2, /* (323) over_clause ::= OVER nm */ -- -5, /* (324) filter_clause ::= FILTER LP WHERE expr RP */ -- -1, /* (325) input ::= cmdlist */ -- -2, /* (326) cmdlist ::= cmdlist ecmd */ -- -1, /* (327) cmdlist ::= ecmd */ -- -1, /* (328) ecmd ::= SEMI */ -- -2, /* (329) ecmd ::= cmdx SEMI */ -- -3, /* (330) ecmd ::= explain cmdx SEMI */ -- 0, /* (331) trans_opt ::= */ -- -1, /* (332) trans_opt ::= TRANSACTION */ -- -2, /* (333) trans_opt ::= TRANSACTION nm */ -- -1, /* (334) savepoint_opt ::= SAVEPOINT */ -- 0, /* (335) savepoint_opt ::= */ -- -2, /* (336) cmd ::= create_table create_table_args */ -- -4, /* (337) columnlist ::= columnlist COMMA columnname carglist */ -- -2, /* (338) columnlist ::= columnname carglist */ -- -1, /* (339) nm ::= ID|INDEXED */ -- -1, /* (340) nm ::= STRING */ -- -1, /* (341) nm ::= JOIN_KW */ -- -1, /* (342) typetoken ::= typename */ -- -1, /* (343) typename ::= ID|STRING */ -- -1, /* (344) signed ::= plus_num */ -- -1, /* (345) signed ::= minus_num */ -- -2, /* (346) carglist ::= carglist ccons */ -- 0, /* (347) carglist ::= */ -- -2, /* (348) ccons ::= NULL onconf */ -- -4, /* (349) ccons ::= GENERATED ALWAYS AS generated */ -- -2, /* (350) ccons ::= AS generated */ -- -2, /* (351) conslist_opt ::= COMMA conslist */ -- -3, /* (352) conslist ::= conslist tconscomma tcons */ -- -1, /* (353) conslist ::= tcons */ -- 0, /* (354) tconscomma ::= */ -- -1, /* (355) defer_subclause_opt ::= defer_subclause */ -- -1, /* (356) resolvetype ::= raisetype */ -- -1, /* (357) selectnowith ::= oneselect */ -- -1, /* (358) oneselect ::= values */ -- -2, /* (359) sclp ::= selcollist COMMA */ -- -1, /* (360) as ::= ID|STRING */ -- -1, /* (361) expr ::= term */ -- -1, /* (362) likeop ::= LIKE_KW|MATCH */ -- -1, /* (363) exprlist ::= nexprlist */ -- -1, /* (364) nmnum ::= plus_num */ -- -1, /* (365) nmnum ::= nm */ -- -1, /* (366) nmnum ::= ON */ -- -1, /* (367) nmnum ::= DELETE */ -- -1, /* (368) nmnum ::= DEFAULT */ -- -1, /* (369) plus_num ::= INTEGER|FLOAT */ -- 0, /* (370) foreach_clause ::= */ -- -3, /* (371) foreach_clause ::= FOR EACH ROW */ -- -1, /* (372) trnm ::= nm */ -- 0, /* (373) tridxby ::= */ -- -1, /* (374) database_kw_opt ::= DATABASE */ -- 0, /* (375) database_kw_opt ::= */ -- 0, /* (376) kwcolumn_opt ::= */ -- -1, /* (377) kwcolumn_opt ::= COLUMNKW */ -- -1, /* (378) vtabarglist ::= vtabarg */ -- -3, /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ -- -2, /* (380) vtabarg ::= vtabarg vtabargtoken */ -- 0, /* (381) anylist ::= */ -- -4, /* (382) anylist ::= anylist LP anylist RP */ -- -2, /* (383) anylist ::= anylist ANY */ -- 0, /* (384) with ::= */ -+ 0, /* (21) table_option_set ::= */ -+ -3, /* (22) table_option_set ::= table_option_set COMMA table_option */ -+ -2, /* (23) table_option ::= WITHOUT nm */ -+ -1, /* (24) table_option ::= nm */ -+ -2, /* (25) columnname ::= nm typetoken */ -+ 0, /* (26) typetoken ::= */ -+ -4, /* (27) typetoken ::= typename LP signed RP */ -+ -6, /* (28) typetoken ::= typename LP signed COMMA signed RP */ -+ -2, /* (29) typename ::= typename ID|STRING */ -+ 0, /* (30) scanpt ::= */ -+ 0, /* (31) scantok ::= */ -+ -2, /* (32) ccons ::= CONSTRAINT nm */ -+ -3, /* (33) ccons ::= DEFAULT scantok term */ -+ -4, /* (34) ccons ::= DEFAULT LP expr RP */ -+ -4, /* (35) ccons ::= DEFAULT PLUS scantok term */ -+ -4, /* (36) ccons ::= DEFAULT MINUS scantok term */ -+ -3, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ -+ -3, /* (38) ccons ::= NOT NULL onconf */ -+ -5, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ -+ -2, /* (40) ccons ::= UNIQUE onconf */ -+ -4, /* (41) ccons ::= CHECK LP expr RP */ -+ -4, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ -+ -1, /* (43) ccons ::= defer_subclause */ -+ -2, /* (44) ccons ::= COLLATE ID|STRING */ -+ -3, /* (45) generated ::= LP expr RP */ -+ -4, /* (46) generated ::= LP expr RP ID */ -+ 0, /* (47) autoinc ::= */ -+ -1, /* (48) autoinc ::= AUTOINCR */ -+ 0, /* (49) refargs ::= */ -+ -2, /* (50) refargs ::= refargs refarg */ -+ -2, /* (51) refarg ::= MATCH nm */ -+ -3, /* (52) refarg ::= ON INSERT refact */ -+ -3, /* (53) refarg ::= ON DELETE refact */ -+ -3, /* (54) refarg ::= ON UPDATE refact */ -+ -2, /* (55) refact ::= SET NULL */ -+ -2, /* (56) refact ::= SET DEFAULT */ -+ -1, /* (57) refact ::= CASCADE */ -+ -1, /* (58) refact ::= RESTRICT */ -+ -2, /* (59) refact ::= NO ACTION */ -+ -3, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -+ -2, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ -+ 0, /* (62) init_deferred_pred_opt ::= */ -+ -2, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ -+ -2, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -+ 0, /* (65) conslist_opt ::= */ -+ -1, /* (66) tconscomma ::= COMMA */ -+ -2, /* (67) tcons ::= CONSTRAINT nm */ -+ -7, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -+ -5, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ -+ -5, /* (70) tcons ::= CHECK LP expr RP onconf */ -+ -10, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ -+ 0, /* (72) defer_subclause_opt ::= */ -+ 0, /* (73) onconf ::= */ -+ -3, /* (74) onconf ::= ON CONFLICT resolvetype */ -+ 0, /* (75) orconf ::= */ -+ -2, /* (76) orconf ::= OR resolvetype */ -+ -1, /* (77) resolvetype ::= IGNORE */ -+ -1, /* (78) resolvetype ::= REPLACE */ -+ -4, /* (79) cmd ::= DROP TABLE ifexists fullname */ -+ -2, /* (80) ifexists ::= IF EXISTS */ -+ 0, /* (81) ifexists ::= */ -+ -9, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ -+ -4, /* (83) cmd ::= DROP VIEW ifexists fullname */ -+ -1, /* (84) cmd ::= select */ -+ -3, /* (85) select ::= WITH wqlist selectnowith */ -+ -4, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ -+ -1, /* (87) select ::= selectnowith */ -+ -3, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ -+ -1, /* (89) multiselect_op ::= UNION */ -+ -2, /* (90) multiselect_op ::= UNION ALL */ -+ -1, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ -+ -9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -+ -10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -+ -4, /* (94) values ::= VALUES LP nexprlist RP */ -+ -1, /* (95) oneselect ::= mvalues */ -+ -5, /* (96) mvalues ::= values COMMA LP nexprlist RP */ -+ -5, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ -+ -1, /* (98) distinct ::= DISTINCT */ -+ -1, /* (99) distinct ::= ALL */ -+ 0, /* (100) distinct ::= */ -+ 0, /* (101) sclp ::= */ -+ -5, /* (102) selcollist ::= sclp scanpt expr scanpt as */ -+ -3, /* (103) selcollist ::= sclp scanpt STAR */ -+ -5, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ -+ -2, /* (105) as ::= AS nm */ -+ 0, /* (106) as ::= */ -+ 0, /* (107) from ::= */ -+ -2, /* (108) from ::= FROM seltablist */ -+ -2, /* (109) stl_prefix ::= seltablist joinop */ -+ 0, /* (110) stl_prefix ::= */ -+ -5, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ -+ -6, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ -+ -8, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ -+ -6, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ -+ -6, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ -+ 0, /* (116) dbnm ::= */ -+ -2, /* (117) dbnm ::= DOT nm */ -+ -1, /* (118) fullname ::= nm */ -+ -3, /* (119) fullname ::= nm DOT nm */ -+ -1, /* (120) xfullname ::= nm */ -+ -3, /* (121) xfullname ::= nm DOT nm */ -+ -5, /* (122) xfullname ::= nm DOT nm AS nm */ -+ -3, /* (123) xfullname ::= nm AS nm */ -+ -1, /* (124) joinop ::= COMMA|JOIN */ -+ -2, /* (125) joinop ::= JOIN_KW JOIN */ -+ -3, /* (126) joinop ::= JOIN_KW nm JOIN */ -+ -4, /* (127) joinop ::= JOIN_KW nm nm JOIN */ -+ -2, /* (128) on_using ::= ON expr */ -+ -4, /* (129) on_using ::= USING LP idlist RP */ -+ 0, /* (130) on_using ::= */ -+ 0, /* (131) indexed_opt ::= */ -+ -3, /* (132) indexed_by ::= INDEXED BY nm */ -+ -2, /* (133) indexed_by ::= NOT INDEXED */ -+ 0, /* (134) orderby_opt ::= */ -+ -3, /* (135) orderby_opt ::= ORDER BY sortlist */ -+ -5, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ -+ -3, /* (137) sortlist ::= expr sortorder nulls */ -+ -1, /* (138) sortorder ::= ASC */ -+ -1, /* (139) sortorder ::= DESC */ -+ 0, /* (140) sortorder ::= */ -+ -2, /* (141) nulls ::= NULLS FIRST */ -+ -2, /* (142) nulls ::= NULLS LAST */ -+ 0, /* (143) nulls ::= */ -+ 0, /* (144) groupby_opt ::= */ -+ -3, /* (145) groupby_opt ::= GROUP BY nexprlist */ -+ 0, /* (146) having_opt ::= */ -+ -2, /* (147) having_opt ::= HAVING expr */ -+ 0, /* (148) limit_opt ::= */ -+ -2, /* (149) limit_opt ::= LIMIT expr */ -+ -4, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ -+ -4, /* (151) limit_opt ::= LIMIT expr COMMA expr */ -+ -6, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ -+ 0, /* (153) where_opt ::= */ -+ -2, /* (154) where_opt ::= WHERE expr */ -+ 0, /* (155) where_opt_ret ::= */ -+ -2, /* (156) where_opt_ret ::= WHERE expr */ -+ -2, /* (157) where_opt_ret ::= RETURNING selcollist */ -+ -4, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ -+ -9, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ -+ -5, /* (160) setlist ::= setlist COMMA nm EQ expr */ -+ -7, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ -+ -3, /* (162) setlist ::= nm EQ expr */ -+ -5, /* (163) setlist ::= LP idlist RP EQ expr */ -+ -7, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ -+ -8, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ -+ 0, /* (166) upsert ::= */ -+ -2, /* (167) upsert ::= RETURNING selcollist */ -+ -12, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -+ -9, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -+ -5, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ -+ -8, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -+ -2, /* (172) returning ::= RETURNING selcollist */ -+ -2, /* (173) insert_cmd ::= INSERT orconf */ -+ -1, /* (174) insert_cmd ::= REPLACE */ -+ 0, /* (175) idlist_opt ::= */ -+ -3, /* (176) idlist_opt ::= LP idlist RP */ -+ -3, /* (177) idlist ::= idlist COMMA nm */ -+ -1, /* (178) idlist ::= nm */ -+ -3, /* (179) expr ::= LP expr RP */ -+ -1, /* (180) expr ::= ID|INDEXED|JOIN_KW */ -+ -3, /* (181) expr ::= nm DOT nm */ -+ -5, /* (182) expr ::= nm DOT nm DOT nm */ -+ -1, /* (183) term ::= NULL|FLOAT|BLOB */ -+ -1, /* (184) term ::= STRING */ -+ -1, /* (185) term ::= INTEGER */ -+ -1, /* (186) expr ::= VARIABLE */ -+ -3, /* (187) expr ::= expr COLLATE ID|STRING */ -+ -6, /* (188) expr ::= CAST LP expr AS typetoken RP */ -+ -5, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ -+ -8, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ -+ -4, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ -+ -6, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ -+ -9, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ -+ -5, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ -+ -1, /* (195) term ::= CTIME_KW */ -+ -5, /* (196) expr ::= LP nexprlist COMMA expr RP */ -+ -3, /* (197) expr ::= expr AND expr */ -+ -3, /* (198) expr ::= expr OR expr */ -+ -3, /* (199) expr ::= expr LT|GT|GE|LE expr */ -+ -3, /* (200) expr ::= expr EQ|NE expr */ -+ -3, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ -+ -3, /* (202) expr ::= expr PLUS|MINUS expr */ -+ -3, /* (203) expr ::= expr STAR|SLASH|REM expr */ -+ -3, /* (204) expr ::= expr CONCAT expr */ -+ -2, /* (205) likeop ::= NOT LIKE_KW|MATCH */ -+ -3, /* (206) expr ::= expr likeop expr */ -+ -5, /* (207) expr ::= expr likeop expr ESCAPE expr */ -+ -2, /* (208) expr ::= expr ISNULL|NOTNULL */ -+ -3, /* (209) expr ::= expr NOT NULL */ -+ -3, /* (210) expr ::= expr IS expr */ -+ -4, /* (211) expr ::= expr IS NOT expr */ -+ -6, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ -+ -5, /* (213) expr ::= expr IS DISTINCT FROM expr */ -+ -2, /* (214) expr ::= NOT expr */ -+ -2, /* (215) expr ::= BITNOT expr */ -+ -2, /* (216) expr ::= PLUS|MINUS expr */ -+ -3, /* (217) expr ::= expr PTR expr */ -+ -1, /* (218) between_op ::= BETWEEN */ -+ -2, /* (219) between_op ::= NOT BETWEEN */ -+ -5, /* (220) expr ::= expr between_op expr AND expr */ -+ -1, /* (221) in_op ::= IN */ -+ -2, /* (222) in_op ::= NOT IN */ -+ -5, /* (223) expr ::= expr in_op LP exprlist RP */ -+ -3, /* (224) expr ::= LP select RP */ -+ -5, /* (225) expr ::= expr in_op LP select RP */ -+ -5, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ -+ -4, /* (227) expr ::= EXISTS LP select RP */ -+ -5, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ -+ -5, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ -+ -4, /* (230) case_exprlist ::= WHEN expr THEN expr */ -+ -2, /* (231) case_else ::= ELSE expr */ -+ 0, /* (232) case_else ::= */ -+ 0, /* (233) case_operand ::= */ -+ 0, /* (234) exprlist ::= */ -+ -3, /* (235) nexprlist ::= nexprlist COMMA expr */ -+ -1, /* (236) nexprlist ::= expr */ -+ 0, /* (237) paren_exprlist ::= */ -+ -3, /* (238) paren_exprlist ::= LP exprlist RP */ -+ -12, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ -+ -1, /* (240) uniqueflag ::= UNIQUE */ -+ 0, /* (241) uniqueflag ::= */ -+ 0, /* (242) eidlist_opt ::= */ -+ -3, /* (243) eidlist_opt ::= LP eidlist RP */ -+ -5, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ -+ -3, /* (245) eidlist ::= nm collate sortorder */ -+ 0, /* (246) collate ::= */ -+ -2, /* (247) collate ::= COLLATE ID|STRING */ -+ -4, /* (248) cmd ::= DROP INDEX ifexists fullname */ -+ -2, /* (249) cmd ::= VACUUM vinto */ -+ -3, /* (250) cmd ::= VACUUM nm vinto */ -+ -2, /* (251) vinto ::= INTO expr */ -+ 0, /* (252) vinto ::= */ -+ -3, /* (253) cmd ::= PRAGMA nm dbnm */ -+ -5, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ -+ -6, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ -+ -5, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ -+ -6, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ -+ -2, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ -+ -2, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ -+ -5, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ -+ -11, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ -+ -1, /* (262) trigger_time ::= BEFORE|AFTER */ -+ -2, /* (263) trigger_time ::= INSTEAD OF */ -+ 0, /* (264) trigger_time ::= */ -+ -1, /* (265) trigger_event ::= DELETE|INSERT */ -+ -1, /* (266) trigger_event ::= UPDATE */ -+ -3, /* (267) trigger_event ::= UPDATE OF idlist */ -+ 0, /* (268) when_clause ::= */ -+ -2, /* (269) when_clause ::= WHEN expr */ -+ -3, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ -+ -2, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ -+ -3, /* (272) trnm ::= nm DOT nm */ -+ -3, /* (273) tridxby ::= INDEXED BY nm */ -+ -2, /* (274) tridxby ::= NOT INDEXED */ -+ -9, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -+ -8, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ -+ -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -+ -3, /* (278) trigger_cmd ::= scanpt select scanpt */ -+ -4, /* (279) expr ::= RAISE LP IGNORE RP */ -+ -6, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ -+ -1, /* (281) raisetype ::= ROLLBACK */ -+ -1, /* (282) raisetype ::= ABORT */ -+ -1, /* (283) raisetype ::= FAIL */ -+ -4, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ -+ -6, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ -+ -3, /* (286) cmd ::= DETACH database_kw_opt expr */ -+ 0, /* (287) key_opt ::= */ -+ -2, /* (288) key_opt ::= KEY expr */ -+ -1, /* (289) cmd ::= REINDEX */ -+ -3, /* (290) cmd ::= REINDEX nm dbnm */ -+ -1, /* (291) cmd ::= ANALYZE */ -+ -3, /* (292) cmd ::= ANALYZE nm dbnm */ -+ -6, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ -+ -7, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ -+ -6, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ -+ -1, /* (296) add_column_fullname ::= fullname */ -+ -8, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ -+ -1, /* (298) cmd ::= create_vtab */ -+ -4, /* (299) cmd ::= create_vtab LP vtabarglist RP */ -+ -8, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ -+ 0, /* (301) vtabarg ::= */ -+ -1, /* (302) vtabargtoken ::= ANY */ -+ -3, /* (303) vtabargtoken ::= lp anylist RP */ -+ -1, /* (304) lp ::= LP */ -+ -2, /* (305) with ::= WITH wqlist */ -+ -3, /* (306) with ::= WITH RECURSIVE wqlist */ -+ -1, /* (307) wqas ::= AS */ -+ -2, /* (308) wqas ::= AS MATERIALIZED */ -+ -3, /* (309) wqas ::= AS NOT MATERIALIZED */ -+ -6, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ -+ -1, /* (311) withnm ::= nm */ -+ -1, /* (312) wqlist ::= wqitem */ -+ -3, /* (313) wqlist ::= wqlist COMMA wqitem */ -+ -3, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ -+ -5, /* (315) windowdefn ::= nm AS LP window RP */ -+ -5, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ -+ -6, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ -+ -4, /* (318) window ::= ORDER BY sortlist frame_opt */ -+ -5, /* (319) window ::= nm ORDER BY sortlist frame_opt */ -+ -2, /* (320) window ::= nm frame_opt */ -+ 0, /* (321) frame_opt ::= */ -+ -3, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ -+ -6, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ -+ -1, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ -+ -1, /* (325) frame_bound_s ::= frame_bound */ -+ -2, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ -+ -1, /* (327) frame_bound_e ::= frame_bound */ -+ -2, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ -+ -2, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ -+ -2, /* (330) frame_bound ::= CURRENT ROW */ -+ 0, /* (331) frame_exclude_opt ::= */ -+ -2, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ -+ -2, /* (333) frame_exclude ::= NO OTHERS */ -+ -2, /* (334) frame_exclude ::= CURRENT ROW */ -+ -1, /* (335) frame_exclude ::= GROUP|TIES */ -+ -2, /* (336) window_clause ::= WINDOW windowdefn_list */ -+ -2, /* (337) filter_over ::= filter_clause over_clause */ -+ -1, /* (338) filter_over ::= over_clause */ -+ -1, /* (339) filter_over ::= filter_clause */ -+ -4, /* (340) over_clause ::= OVER LP window RP */ -+ -2, /* (341) over_clause ::= OVER nm */ -+ -5, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ -+ -1, /* (343) term ::= QNUMBER */ -+ -1, /* (344) input ::= cmdlist */ -+ -2, /* (345) cmdlist ::= cmdlist ecmd */ -+ -1, /* (346) cmdlist ::= ecmd */ -+ -1, /* (347) ecmd ::= SEMI */ -+ -2, /* (348) ecmd ::= cmdx SEMI */ -+ -3, /* (349) ecmd ::= explain cmdx SEMI */ -+ 0, /* (350) trans_opt ::= */ -+ -1, /* (351) trans_opt ::= TRANSACTION */ -+ -2, /* (352) trans_opt ::= TRANSACTION nm */ -+ -1, /* (353) savepoint_opt ::= SAVEPOINT */ -+ 0, /* (354) savepoint_opt ::= */ -+ -2, /* (355) cmd ::= create_table create_table_args */ -+ -1, /* (356) table_option_set ::= table_option */ -+ -4, /* (357) columnlist ::= columnlist COMMA columnname carglist */ -+ -2, /* (358) columnlist ::= columnname carglist */ -+ -1, /* (359) nm ::= ID|INDEXED|JOIN_KW */ -+ -1, /* (360) nm ::= STRING */ -+ -1, /* (361) typetoken ::= typename */ -+ -1, /* (362) typename ::= ID|STRING */ -+ -1, /* (363) signed ::= plus_num */ -+ -1, /* (364) signed ::= minus_num */ -+ -2, /* (365) carglist ::= carglist ccons */ -+ 0, /* (366) carglist ::= */ -+ -2, /* (367) ccons ::= NULL onconf */ -+ -4, /* (368) ccons ::= GENERATED ALWAYS AS generated */ -+ -2, /* (369) ccons ::= AS generated */ -+ -2, /* (370) conslist_opt ::= COMMA conslist */ -+ -3, /* (371) conslist ::= conslist tconscomma tcons */ -+ -1, /* (372) conslist ::= tcons */ -+ 0, /* (373) tconscomma ::= */ -+ -1, /* (374) defer_subclause_opt ::= defer_subclause */ -+ -1, /* (375) resolvetype ::= raisetype */ -+ -1, /* (376) selectnowith ::= oneselect */ -+ -1, /* (377) oneselect ::= values */ -+ -2, /* (378) sclp ::= selcollist COMMA */ -+ -1, /* (379) as ::= ID|STRING */ -+ -1, /* (380) indexed_opt ::= indexed_by */ -+ 0, /* (381) returning ::= */ -+ -1, /* (382) expr ::= term */ -+ -1, /* (383) likeop ::= LIKE_KW|MATCH */ -+ -1, /* (384) case_operand ::= expr */ -+ -1, /* (385) exprlist ::= nexprlist */ -+ -1, /* (386) nmnum ::= plus_num */ -+ -1, /* (387) nmnum ::= nm */ -+ -1, /* (388) nmnum ::= ON */ -+ -1, /* (389) nmnum ::= DELETE */ -+ -1, /* (390) nmnum ::= DEFAULT */ -+ -1, /* (391) plus_num ::= INTEGER|FLOAT */ -+ 0, /* (392) foreach_clause ::= */ -+ -3, /* (393) foreach_clause ::= FOR EACH ROW */ -+ -1, /* (394) trnm ::= nm */ -+ 0, /* (395) tridxby ::= */ -+ -1, /* (396) database_kw_opt ::= DATABASE */ -+ 0, /* (397) database_kw_opt ::= */ -+ 0, /* (398) kwcolumn_opt ::= */ -+ -1, /* (399) kwcolumn_opt ::= COLUMNKW */ -+ -1, /* (400) vtabarglist ::= vtabarg */ -+ -3, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ -+ -2, /* (402) vtabarg ::= vtabarg vtabargtoken */ -+ 0, /* (403) anylist ::= */ -+ -4, /* (404) anylist ::= anylist LP anylist RP */ -+ -2, /* (405) anylist ::= anylist ANY */ -+ 0, /* (406) with ::= */ -+ -1, /* (407) windowdefn_list ::= windowdefn */ -+ -1, /* (408) window ::= frame_opt */ - }; - - static void yy_accept(yyParser*); /* Forward Declaration */ -@@ -157475,54 +178939,6 @@ static YYACTIONTYPE yy_reduce( - (void)yyLookahead; - (void)yyLookaheadToken; - yymsp = yypParser->yytos; --#ifndef NDEBUG -- if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ -- yysize = yyRuleInfoNRhs[yyruleno]; -- if( yysize ){ -- fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", -- yyTracePrompt, -- yyruleno, yyRuleName[yyruleno], -- yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ -- yypParser->yyhwm++; -- assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); -- } --#endif --#if YYSTACKDEPTH>0 -- if( yypParser->yytos>=yypParser->yystackEnd ){ -- yyStackOverflow(yypParser); -- /* The call to yyStackOverflow() above pops the stack until it is -- ** empty, causing the main parser loop to exit. So the return value -- ** is never used and does not matter. */ -- return 0; -- } --#else -- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ -- if( yyGrowStack(yypParser) ){ -- yyStackOverflow(yypParser); -- /* The call to yyStackOverflow() above pops the stack until it is -- ** empty, causing the main parser loop to exit. So the return value -- ** is never used and does not matter. */ -- return 0; -- } -- yymsp = yypParser->yytos; -- } --#endif -- } - - switch( yyruleno ){ - /* Beginning here are the reduction cases. A typical example -@@ -157536,25 +178952,25 @@ static YYACTIONTYPE yy_reduce( - /********** Begin reduce actions **********************************************/ - YYMINORTYPE yylhsminor; - case 0: /* explain ::= EXPLAIN */ --{ pParse->explain = 1; } -+{ if( pParse->pReprepare==0 ) pParse->explain = 1; } - break; - case 1: /* explain ::= EXPLAIN QUERY PLAN */ --{ pParse->explain = 2; } -+{ if( pParse->pReprepare==0 ) pParse->explain = 2; } - break; - case 2: /* cmdx ::= cmd */ - { sqlite3FinishCoding(pParse); } - break; - case 3: /* cmd ::= BEGIN transtype trans_opt */ --{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy192);} -+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy502);} - break; - case 4: /* transtype ::= */ --{yymsp[1].minor.yy192 = TK_DEFERRED;} -+{yymsp[1].minor.yy502 = TK_DEFERRED;} - break; - case 5: /* transtype ::= DEFERRED */ - case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); - case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); -- case 306: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==306); --{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/} -+ case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324); -+{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/} - break; - case 8: /* cmd ::= COMMIT|END trans_opt */ - case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); -@@ -157577,104 +178993,122 @@ static YYACTIONTYPE yy_reduce( - break; - case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - { -- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy192,0,0,yymsp[-2].minor.yy192); -+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy502,0,0,yymsp[-2].minor.yy502); - } - break; - case 14: /* createkw ::= CREATE */ --{disableLookaside(pParse);} -+{ -+ disableLookaside(pParse); -+} - break; - case 15: /* ifnotexists ::= */ - case 18: /* temp ::= */ yytestcase(yyruleno==18); -- case 21: /* table_options ::= */ yytestcase(yyruleno==21); -- case 45: /* autoinc ::= */ yytestcase(yyruleno==45); -- case 60: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==60); -- case 70: /* defer_subclause_opt ::= */ yytestcase(yyruleno==70); -- case 79: /* ifexists ::= */ yytestcase(yyruleno==79); -- case 96: /* distinct ::= */ yytestcase(yyruleno==96); -- case 232: /* collate ::= */ yytestcase(yyruleno==232); --{yymsp[1].minor.yy192 = 0;} -+ case 47: /* autoinc ::= */ yytestcase(yyruleno==47); -+ case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); -+ case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); -+ case 81: /* ifexists ::= */ yytestcase(yyruleno==81); -+ case 100: /* distinct ::= */ yytestcase(yyruleno==100); -+ case 246: /* collate ::= */ yytestcase(yyruleno==246); -+{yymsp[1].minor.yy502 = 0;} - break; - case 16: /* ifnotexists ::= IF NOT EXISTS */ --{yymsp[-2].minor.yy192 = 1;} -+{yymsp[-2].minor.yy502 = 1;} - break; - case 17: /* temp ::= TEMP */ -- case 46: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==46); --{yymsp[0].minor.yy192 = 1;} -+{yymsp[0].minor.yy502 = pParse->db->init.busy==0;} - break; -- case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ -+ case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ - { -- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy192,0); -+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy9,0); - } - break; - case 20: /* create_table_args ::= AS select */ - { -- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy539); -- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy539); -+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy637); -+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); - } - break; -- case 22: /* table_options ::= WITHOUT nm */ -+ case 21: /* table_option_set ::= */ -+{yymsp[1].minor.yy9 = 0;} -+ break; -+ case 22: /* table_option_set ::= table_option_set COMMA table_option */ -+{yylhsminor.yy9 = yymsp[-2].minor.yy9|yymsp[0].minor.yy9;} -+ yymsp[-2].minor.yy9 = yylhsminor.yy9; -+ break; -+ case 23: /* table_option ::= WITHOUT nm */ - { - if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ -- yymsp[-1].minor.yy192 = TF_WithoutRowid | TF_NoVisibleRowid; -+ yymsp[-1].minor.yy9 = TF_WithoutRowid | TF_NoVisibleRowid; - }else{ -- yymsp[-1].minor.yy192 = 0; -+ yymsp[-1].minor.yy9 = 0; - sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); - } - } - break; -- case 23: /* columnname ::= nm typetoken */ --{sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} -+ case 24: /* table_option ::= nm */ -+{ -+ if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ -+ yylhsminor.yy9 = TF_Strict; -+ }else{ -+ yylhsminor.yy9 = 0; -+ sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); -+ } -+} -+ yymsp[0].minor.yy9 = yylhsminor.yy9; -+ break; -+ case 25: /* columnname ::= nm typetoken */ -+{sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} - break; -- case 24: /* typetoken ::= */ -- case 63: /* conslist_opt ::= */ yytestcase(yyruleno==63); -- case 102: /* as ::= */ yytestcase(yyruleno==102); -+ case 26: /* typetoken ::= */ -+ case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65); -+ case 106: /* as ::= */ yytestcase(yyruleno==106); - {yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;} - break; -- case 25: /* typetoken ::= typename LP signed RP */ -+ case 27: /* typetoken ::= typename LP signed RP */ - { - yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z); - } - break; -- case 26: /* typetoken ::= typename LP signed COMMA signed RP */ -+ case 28: /* typetoken ::= typename LP signed COMMA signed RP */ - { - yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z); - } - break; -- case 27: /* typename ::= typename ID|STRING */ -+ case 29: /* typename ::= typename ID|STRING */ - {yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);} - break; -- case 28: /* scanpt ::= */ -+ case 30: /* scanpt ::= */ - { - assert( yyLookahead!=YYNOCODE ); -- yymsp[1].minor.yy436 = yyLookaheadToken.z; -+ yymsp[1].minor.yy342 = yyLookaheadToken.z; - } - break; -- case 29: /* scantok ::= */ -+ case 31: /* scantok ::= */ - { - assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy0 = yyLookaheadToken; - } - break; -- case 30: /* ccons ::= CONSTRAINT nm */ -- case 65: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==65); --{pParse->constraintName = yymsp[0].minor.yy0;} -+ case 32: /* ccons ::= CONSTRAINT nm */ -+ case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67); -+{ASSERT_IS_CREATE; pParse->u1.cr.constraintName = yymsp[0].minor.yy0;} - break; -- case 31: /* ccons ::= DEFAULT scantok term */ --{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy202,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} -+ case 33: /* ccons ::= DEFAULT scantok term */ -+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} - break; -- case 32: /* ccons ::= DEFAULT LP expr RP */ --{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy202,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} -+ case 34: /* ccons ::= DEFAULT LP expr RP */ -+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} - break; -- case 33: /* ccons ::= DEFAULT PLUS scantok term */ --{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy202,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} -+ case 35: /* ccons ::= DEFAULT PLUS scantok term */ -+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} - break; -- case 34: /* ccons ::= DEFAULT MINUS scantok term */ -+ case 36: /* ccons ::= DEFAULT MINUS scantok term */ - { -- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy202, 0); -+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy590, 0); - sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); - } - break; -- case 35: /* ccons ::= DEFAULT scantok ID|INDEXED */ -+ case 37: /* ccons ::= DEFAULT scantok ID|INDEXED */ - { - Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0); - if( p ){ -@@ -157684,576 +179118,607 @@ static YYACTIONTYPE yy_reduce( - sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n); - } - break; -- case 36: /* ccons ::= NOT NULL onconf */ --{sqlite3AddNotNull(pParse, yymsp[0].minor.yy192);} -+ case 38: /* ccons ::= NOT NULL onconf */ -+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy502);} - break; -- case 37: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ --{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy192,yymsp[0].minor.yy192,yymsp[-2].minor.yy192);} -+ case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy502,yymsp[0].minor.yy502,yymsp[-2].minor.yy502);} - break; -- case 38: /* ccons ::= UNIQUE onconf */ --{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy192,0,0,0,0, -+ case 40: /* ccons ::= UNIQUE onconf */ -+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy502,0,0,0,0, - SQLITE_IDXTYPE_UNIQUE);} - break; -- case 39: /* ccons ::= CHECK LP expr RP */ --{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy202);} -+ case 41: /* ccons ::= CHECK LP expr RP */ -+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} - break; -- case 40: /* ccons ::= REFERENCES nm eidlist_opt refargs */ --{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy242,yymsp[0].minor.yy192);} -+ case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy402,yymsp[0].minor.yy502);} - break; -- case 41: /* ccons ::= defer_subclause */ --{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy192);} -+ case 43: /* ccons ::= defer_subclause */ -+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy502);} - break; -- case 42: /* ccons ::= COLLATE ID|STRING */ -+ case 44: /* ccons ::= COLLATE ID|STRING */ - {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} - break; -- case 43: /* generated ::= LP expr RP */ --{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy202,0);} -+ case 45: /* generated ::= LP expr RP */ -+{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy590,0);} - break; -- case 44: /* generated ::= LP expr RP ID */ --{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy202,&yymsp[0].minor.yy0);} -+ case 46: /* generated ::= LP expr RP ID */ -+{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy590,&yymsp[0].minor.yy0);} - break; -- case 47: /* refargs ::= */ --{ yymsp[1].minor.yy192 = OE_None*0x0101; /* EV: R-19803-45884 */} -+ case 48: /* autoinc ::= AUTOINCR */ -+{yymsp[0].minor.yy502 = 1;} - break; -- case 48: /* refargs ::= refargs refarg */ --{ yymsp[-1].minor.yy192 = (yymsp[-1].minor.yy192 & ~yymsp[0].minor.yy207.mask) | yymsp[0].minor.yy207.value; } -+ case 49: /* refargs ::= */ -+{ yymsp[1].minor.yy502 = OE_None*0x0101; /* EV: R-19803-45884 */} - break; -- case 49: /* refarg ::= MATCH nm */ --{ yymsp[-1].minor.yy207.value = 0; yymsp[-1].minor.yy207.mask = 0x000000; } -+ case 50: /* refargs ::= refargs refarg */ -+{ yymsp[-1].minor.yy502 = (yymsp[-1].minor.yy502 & ~yymsp[0].minor.yy481.mask) | yymsp[0].minor.yy481.value; } - break; -- case 50: /* refarg ::= ON INSERT refact */ --{ yymsp[-2].minor.yy207.value = 0; yymsp[-2].minor.yy207.mask = 0x000000; } -+ case 51: /* refarg ::= MATCH nm */ -+{ yymsp[-1].minor.yy481.value = 0; yymsp[-1].minor.yy481.mask = 0x000000; } - break; -- case 51: /* refarg ::= ON DELETE refact */ --{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy192; yymsp[-2].minor.yy207.mask = 0x0000ff; } -+ case 52: /* refarg ::= ON INSERT refact */ -+{ yymsp[-2].minor.yy481.value = 0; yymsp[-2].minor.yy481.mask = 0x000000; } - break; -- case 52: /* refarg ::= ON UPDATE refact */ --{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy192<<8; yymsp[-2].minor.yy207.mask = 0x00ff00; } -+ case 53: /* refarg ::= ON DELETE refact */ -+{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502; yymsp[-2].minor.yy481.mask = 0x0000ff; } - break; -- case 53: /* refact ::= SET NULL */ --{ yymsp[-1].minor.yy192 = OE_SetNull; /* EV: R-33326-45252 */} -+ case 54: /* refarg ::= ON UPDATE refact */ -+{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502<<8; yymsp[-2].minor.yy481.mask = 0x00ff00; } - break; -- case 54: /* refact ::= SET DEFAULT */ --{ yymsp[-1].minor.yy192 = OE_SetDflt; /* EV: R-33326-45252 */} -+ case 55: /* refact ::= SET NULL */ -+{ yymsp[-1].minor.yy502 = OE_SetNull; /* EV: R-33326-45252 */} - break; -- case 55: /* refact ::= CASCADE */ --{ yymsp[0].minor.yy192 = OE_Cascade; /* EV: R-33326-45252 */} -+ case 56: /* refact ::= SET DEFAULT */ -+{ yymsp[-1].minor.yy502 = OE_SetDflt; /* EV: R-33326-45252 */} - break; -- case 56: /* refact ::= RESTRICT */ --{ yymsp[0].minor.yy192 = OE_Restrict; /* EV: R-33326-45252 */} -+ case 57: /* refact ::= CASCADE */ -+{ yymsp[0].minor.yy502 = OE_Cascade; /* EV: R-33326-45252 */} - break; -- case 57: /* refact ::= NO ACTION */ --{ yymsp[-1].minor.yy192 = OE_None; /* EV: R-33326-45252 */} -+ case 58: /* refact ::= RESTRICT */ -+{ yymsp[0].minor.yy502 = OE_Restrict; /* EV: R-33326-45252 */} - break; -- case 58: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ --{yymsp[-2].minor.yy192 = 0;} -+ case 59: /* refact ::= NO ACTION */ -+{ yymsp[-1].minor.yy502 = OE_None; /* EV: R-33326-45252 */} - break; -- case 59: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ -- case 74: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==74); -- case 162: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==162); --{yymsp[-1].minor.yy192 = yymsp[0].minor.yy192;} -+ case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -+{yymsp[-2].minor.yy502 = 0;} - break; -- case 61: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ -- case 78: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==78); -- case 204: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==204); -- case 207: /* in_op ::= NOT IN */ yytestcase(yyruleno==207); -- case 233: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==233); --{yymsp[-1].minor.yy192 = 1;} -+ case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ -+ case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); -+ case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173); -+{yymsp[-1].minor.yy502 = yymsp[0].minor.yy502;} - break; -- case 62: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ --{yymsp[-1].minor.yy192 = 0;} -+ case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ -+ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); -+ case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219); -+ case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222); -+ case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247); -+{yymsp[-1].minor.yy502 = 1;} - break; -- case 64: /* tconscomma ::= COMMA */ --{pParse->constraintName.n = 0;} -+ case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -+{yymsp[-1].minor.yy502 = 0;} - break; -- case 66: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ --{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy242,yymsp[0].minor.yy192,yymsp[-2].minor.yy192,0);} -+ case 66: /* tconscomma ::= COMMA */ -+{ASSERT_IS_CREATE; pParse->u1.cr.constraintName.n = 0;} - break; -- case 67: /* tcons ::= UNIQUE LP sortlist RP onconf */ --{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy242,yymsp[0].minor.yy192,0,0,0,0, -+ case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy402,yymsp[0].minor.yy502,yymsp[-2].minor.yy502,0);} -+ break; -+ case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ -+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy402,yymsp[0].minor.yy502,0,0,0,0, - SQLITE_IDXTYPE_UNIQUE);} - break; -- case 68: /* tcons ::= CHECK LP expr RP onconf */ --{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy202);} -+ case 70: /* tcons ::= CHECK LP expr RP onconf */ -+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy590,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} - break; -- case 69: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ -+ case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - { -- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy242, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy242, yymsp[-1].minor.yy192); -- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy192); -+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy402, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[-1].minor.yy502); -+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy502); - } - break; -- case 71: /* onconf ::= */ -- case 73: /* orconf ::= */ yytestcase(yyruleno==73); --{yymsp[1].minor.yy192 = OE_Default;} -+ case 73: /* onconf ::= */ -+ case 75: /* orconf ::= */ yytestcase(yyruleno==75); -+{yymsp[1].minor.yy502 = OE_Default;} - break; -- case 72: /* onconf ::= ON CONFLICT resolvetype */ --{yymsp[-2].minor.yy192 = yymsp[0].minor.yy192;} -+ case 74: /* onconf ::= ON CONFLICT resolvetype */ -+{yymsp[-2].minor.yy502 = yymsp[0].minor.yy502;} - break; -- case 75: /* resolvetype ::= IGNORE */ --{yymsp[0].minor.yy192 = OE_Ignore;} -+ case 77: /* resolvetype ::= IGNORE */ -+{yymsp[0].minor.yy502 = OE_Ignore;} - break; -- case 76: /* resolvetype ::= REPLACE */ -- case 163: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==163); --{yymsp[0].minor.yy192 = OE_Replace;} -+ case 78: /* resolvetype ::= REPLACE */ -+ case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174); -+{yymsp[0].minor.yy502 = OE_Replace;} - break; -- case 77: /* cmd ::= DROP TABLE ifexists fullname */ -+ case 79: /* cmd ::= DROP TABLE ifexists fullname */ - { -- sqlite3DropTable(pParse, yymsp[0].minor.yy47, 0, yymsp[-1].minor.yy192); -+ sqlite3DropTable(pParse, yymsp[0].minor.yy563, 0, yymsp[-1].minor.yy502); - } - break; -- case 80: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ -+ case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - { -- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy242, yymsp[0].minor.yy539, yymsp[-7].minor.yy192, yymsp[-5].minor.yy192); -+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[0].minor.yy637, yymsp[-7].minor.yy502, yymsp[-5].minor.yy502); - } - break; -- case 81: /* cmd ::= DROP VIEW ifexists fullname */ -+ case 83: /* cmd ::= DROP VIEW ifexists fullname */ - { -- sqlite3DropTable(pParse, yymsp[0].minor.yy47, 1, yymsp[-1].minor.yy192); -+ sqlite3DropTable(pParse, yymsp[0].minor.yy563, 1, yymsp[-1].minor.yy502); - } - break; -- case 82: /* cmd ::= select */ -+ case 84: /* cmd ::= select */ - { - SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; -- sqlite3Select(pParse, yymsp[0].minor.yy539, &dest); -- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy539); --} -- break; -- case 83: /* select ::= WITH wqlist selectnowith */ --{ -- Select *p = yymsp[0].minor.yy539; -- if( p ){ -- p->pWith = yymsp[-1].minor.yy131; -- parserDoubleLinkSelect(pParse, p); -- }else{ -- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy131); -+ if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0 -+ || sqlite3ReadSchema(pParse)==SQLITE_OK -+ ){ -+ sqlite3Select(pParse, yymsp[0].minor.yy637, &dest); - } -- yymsp[-2].minor.yy539 = p; -+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); - } - break; -- case 84: /* select ::= WITH RECURSIVE wqlist selectnowith */ --{ -- Select *p = yymsp[0].minor.yy539; -- if( p ){ -- p->pWith = yymsp[-1].minor.yy131; -- parserDoubleLinkSelect(pParse, p); -- }else{ -- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy131); -- } -- yymsp[-3].minor.yy539 = p; --} -+ case 85: /* select ::= WITH wqlist selectnowith */ -+{yymsp[-2].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} - break; -- case 85: /* select ::= selectnowith */ -+ case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ -+{yymsp[-3].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} -+ break; -+ case 87: /* select ::= selectnowith */ - { -- Select *p = yymsp[0].minor.yy539; -+ Select *p = yymsp[0].minor.yy637; - if( p ){ - parserDoubleLinkSelect(pParse, p); - } -- yymsp[0].minor.yy539 = p; /*A-overwrites-X*/ - } - break; -- case 86: /* selectnowith ::= selectnowith multiselect_op oneselect */ -+ case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ - { -- Select *pRhs = yymsp[0].minor.yy539; -- Select *pLhs = yymsp[-2].minor.yy539; -+ Select *pRhs = yymsp[0].minor.yy637; -+ Select *pLhs = yymsp[-2].minor.yy637; - if( pRhs && pRhs->pPrior ){ - SrcList *pFrom; - Token x; - x.n = 0; - parserDoubleLinkSelect(pParse, pRhs); -- pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); -+ pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0); - pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); - } - if( pRhs ){ -- pRhs->op = (u8)yymsp[-1].minor.yy192; -+ pRhs->op = (u8)yymsp[-1].minor.yy502; - pRhs->pPrior = pLhs; -- if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; -- pRhs->selFlags &= ~SF_MultiValue; -- if( yymsp[-1].minor.yy192!=TK_ALL ) pParse->hasCompound = 1; -+ if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue; -+ pRhs->selFlags &= ~(u32)SF_MultiValue; -+ if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1; - }else{ - sqlite3SelectDelete(pParse->db, pLhs); - } -- yymsp[-2].minor.yy539 = pRhs; -+ yymsp[-2].minor.yy637 = pRhs; - } - break; -- case 87: /* multiselect_op ::= UNION */ -- case 89: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==89); --{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-OP*/} -+ case 89: /* multiselect_op ::= UNION */ -+ case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); -+{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-OP*/} - break; -- case 88: /* multiselect_op ::= UNION ALL */ --{yymsp[-1].minor.yy192 = TK_ALL;} -+ case 90: /* multiselect_op ::= UNION ALL */ -+{yymsp[-1].minor.yy502 = TK_ALL;} - break; -- case 90: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -+ case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - { -- yymsp[-8].minor.yy539 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy242,yymsp[-5].minor.yy47,yymsp[-4].minor.yy202,yymsp[-3].minor.yy242,yymsp[-2].minor.yy202,yymsp[-1].minor.yy242,yymsp[-7].minor.yy192,yymsp[0].minor.yy202); -+ yymsp[-8].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy402,yymsp[-5].minor.yy563,yymsp[-4].minor.yy590,yymsp[-3].minor.yy402,yymsp[-2].minor.yy590,yymsp[-1].minor.yy402,yymsp[-7].minor.yy502,yymsp[0].minor.yy590); - } - break; -- case 91: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -+ case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - { -- yymsp[-9].minor.yy539 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy242,yymsp[-6].minor.yy47,yymsp[-5].minor.yy202,yymsp[-4].minor.yy242,yymsp[-3].minor.yy202,yymsp[-1].minor.yy242,yymsp[-8].minor.yy192,yymsp[0].minor.yy202); -- if( yymsp[-9].minor.yy539 ){ -- yymsp[-9].minor.yy539->pWinDefn = yymsp[-2].minor.yy303; -+ yymsp[-9].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy402,yymsp[-6].minor.yy563,yymsp[-5].minor.yy590,yymsp[-4].minor.yy402,yymsp[-3].minor.yy590,yymsp[-1].minor.yy402,yymsp[-8].minor.yy502,yymsp[0].minor.yy590); -+ if( yymsp[-9].minor.yy637 ){ -+ yymsp[-9].minor.yy637->pWinDefn = yymsp[-2].minor.yy483; - }else{ -- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy303); -+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy483); - } - } - break; -- case 92: /* values ::= VALUES LP nexprlist RP */ -+ case 94: /* values ::= VALUES LP nexprlist RP */ - { -- yymsp[-3].minor.yy539 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy242,0,0,0,0,0,SF_Values,0); -+ yymsp[-3].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy402,0,0,0,0,0,SF_Values,0); - } - break; -- case 93: /* values ::= values COMMA LP nexprlist RP */ -+ case 95: /* oneselect ::= mvalues */ - { -- Select *pRight, *pLeft = yymsp[-4].minor.yy539; -- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy242,0,0,0,0,0,SF_Values|SF_MultiValue,0); -- if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; -- if( pRight ){ -- pRight->op = TK_ALL; -- pRight->pPrior = pLeft; -- yymsp[-4].minor.yy539 = pRight; -- }else{ -- yymsp[-4].minor.yy539 = pLeft; -- } -+ sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy637); -+} -+ break; -+ case 96: /* mvalues ::= values COMMA LP nexprlist RP */ -+ case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97); -+{ -+ yymsp[-4].minor.yy637 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy637, yymsp[-1].minor.yy402); - } - break; -- case 94: /* distinct ::= DISTINCT */ --{yymsp[0].minor.yy192 = SF_Distinct;} -+ case 98: /* distinct ::= DISTINCT */ -+{yymsp[0].minor.yy502 = SF_Distinct;} - break; -- case 95: /* distinct ::= ALL */ --{yymsp[0].minor.yy192 = SF_All;} -+ case 99: /* distinct ::= ALL */ -+{yymsp[0].minor.yy502 = SF_All;} - break; -- case 97: /* sclp ::= */ -- case 130: /* orderby_opt ::= */ yytestcase(yyruleno==130); -- case 140: /* groupby_opt ::= */ yytestcase(yyruleno==140); -- case 220: /* exprlist ::= */ yytestcase(yyruleno==220); -- case 223: /* paren_exprlist ::= */ yytestcase(yyruleno==223); -- case 228: /* eidlist_opt ::= */ yytestcase(yyruleno==228); --{yymsp[1].minor.yy242 = 0;} -+ case 101: /* sclp ::= */ -+ case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134); -+ case 144: /* groupby_opt ::= */ yytestcase(yyruleno==144); -+ case 234: /* exprlist ::= */ yytestcase(yyruleno==234); -+ case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237); -+ case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242); -+{yymsp[1].minor.yy402 = 0;} - break; -- case 98: /* selcollist ::= sclp scanpt expr scanpt as */ -+ case 102: /* selcollist ::= sclp scanpt expr scanpt as */ - { -- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[-2].minor.yy202); -- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[0].minor.yy0, 1); -- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy242,yymsp[-3].minor.yy436,yymsp[-1].minor.yy436); -+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); -+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[0].minor.yy0, 1); -+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy402,yymsp[-3].minor.yy342,yymsp[-1].minor.yy342); - } - break; -- case 99: /* selcollist ::= sclp scanpt STAR */ -+ case 103: /* selcollist ::= sclp scanpt STAR */ - { - Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); -- yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy242, p); -+ sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); -+ yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy402, p); - } - break; -- case 100: /* selcollist ::= sclp scanpt nm DOT STAR */ -+ case 104: /* selcollist ::= sclp scanpt nm DOT STAR */ - { -- Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); -- Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); -- Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); -- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, pDot); -+ Expr *pRight, *pLeft, *pDot; -+ pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); -+ sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); -+ pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); -+ pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); -+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, pDot); - } - break; -- case 101: /* as ::= AS nm */ -- case 112: /* dbnm ::= DOT nm */ yytestcase(yyruleno==112); -- case 244: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==244); -- case 245: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==245); -+ case 105: /* as ::= AS nm */ -+ case 117: /* dbnm ::= DOT nm */ yytestcase(yyruleno==117); -+ case 258: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==258); -+ case 259: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==259); - {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} - break; -- case 103: /* from ::= */ -- case 106: /* stl_prefix ::= */ yytestcase(yyruleno==106); --{yymsp[1].minor.yy47 = 0;} -+ case 107: /* from ::= */ -+ case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110); -+{yymsp[1].minor.yy563 = 0;} - break; -- case 104: /* from ::= FROM seltablist */ -+ case 108: /* from ::= FROM seltablist */ - { -- yymsp[-1].minor.yy47 = yymsp[0].minor.yy47; -- sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy47); -+ yymsp[-1].minor.yy563 = yymsp[0].minor.yy563; -+ sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy563); - } - break; -- case 105: /* stl_prefix ::= seltablist joinop */ -+ case 109: /* stl_prefix ::= seltablist joinop */ - { -- if( ALWAYS(yymsp[-1].minor.yy47 && yymsp[-1].minor.yy47->nSrc>0) ) yymsp[-1].minor.yy47->a[yymsp[-1].minor.yy47->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy192; -+ if( ALWAYS(yymsp[-1].minor.yy563 && yymsp[-1].minor.yy563->nSrc>0) ) yymsp[-1].minor.yy563->a[yymsp[-1].minor.yy563->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy502; - } - break; -- case 107: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ -+ case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */ - { -- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); -- sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy47, &yymsp[-2].minor.yy0); -+ yymsp[-4].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy563,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); - } - break; -- case 108: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ -+ case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ - { -- yymsp[-8].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy47,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); -- sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy47, yymsp[-4].minor.yy242); -+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy421); -+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-1].minor.yy0); - } - break; -- case 109: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ -+ case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ - { -- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy539,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); -+ yymsp[-7].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy563,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); -+ sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy563, yymsp[-3].minor.yy402); -+} -+ break; -+ case 114: /* seltablist ::= stl_prefix LP select RP as on_using */ -+{ -+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy637,&yymsp[0].minor.yy421); - } - break; -- case 110: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ -+ case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ - { -- if( yymsp[-6].minor.yy47==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy202==0 && yymsp[0].minor.yy600==0 ){ -- yymsp[-6].minor.yy47 = yymsp[-4].minor.yy47; -- }else if( yymsp[-4].minor.yy47->nSrc==1 ){ -- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); -- if( yymsp[-6].minor.yy47 ){ -- struct SrcList_item *pNew = &yymsp[-6].minor.yy47->a[yymsp[-6].minor.yy47->nSrc-1]; -- struct SrcList_item *pOld = yymsp[-4].minor.yy47->a; -+ if( yymsp[-5].minor.yy563==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy421.pOn==0 && yymsp[0].minor.yy421.pUsing==0 ){ -+ yymsp[-5].minor.yy563 = yymsp[-3].minor.yy563; -+ }else if( ALWAYS(yymsp[-3].minor.yy563!=0) && yymsp[-3].minor.yy563->nSrc==1 ){ -+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); -+ if( yymsp[-5].minor.yy563 ){ -+ SrcItem *pNew = &yymsp[-5].minor.yy563->a[yymsp[-5].minor.yy563->nSrc-1]; -+ SrcItem *pOld = yymsp[-3].minor.yy563->a; -+ assert( pOld->fg.fixedSchema==0 ); - pNew->zName = pOld->zName; -- pNew->zDatabase = pOld->zDatabase; -- pNew->pSelect = pOld->pSelect; -+ assert( pOld->fg.fixedSchema==0 ); -+ if( pOld->fg.isSubquery ){ -+ pNew->fg.isSubquery = 1; -+ pNew->u4.pSubq = pOld->u4.pSubq; -+ pOld->u4.pSubq = 0; -+ pOld->fg.isSubquery = 0; -+ assert( pNew->u4.pSubq!=0 && pNew->u4.pSubq->pSelect!=0 ); -+ if( (pNew->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 ){ -+ pNew->fg.isNestedFrom = 1; -+ } -+ }else{ -+ pNew->u4.zDatabase = pOld->u4.zDatabase; -+ pOld->u4.zDatabase = 0; -+ } - if( pOld->fg.isTabFunc ){ - pNew->u1.pFuncArg = pOld->u1.pFuncArg; - pOld->u1.pFuncArg = 0; - pOld->fg.isTabFunc = 0; - pNew->fg.isTabFunc = 1; - } -- pOld->zName = pOld->zDatabase = 0; -- pOld->pSelect = 0; -+ pOld->zName = 0; - } -- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy47); -+ sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy563); - }else{ - Select *pSubquery; -- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy47); -- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy47,0,0,0,0,SF_NestedFrom,0); -- yymsp[-6].minor.yy47 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy47,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy202,yymsp[0].minor.yy600); -+ sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy563); -+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy563,0,0,0,0,SF_NestedFrom,0); -+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy421); - } - } - break; -- case 111: /* dbnm ::= */ -- case 125: /* indexed_opt ::= */ yytestcase(yyruleno==125); -+ case 116: /* dbnm ::= */ -+ case 131: /* indexed_opt ::= */ yytestcase(yyruleno==131); - {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} - break; -- case 113: /* fullname ::= nm */ -+ case 118: /* fullname ::= nm */ - { -- yylhsminor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); -- if( IN_RENAME_OBJECT && yylhsminor.yy47 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy47->a[0].zName, &yymsp[0].minor.yy0); -+ yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); -+ if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); - } -- yymsp[0].minor.yy47 = yylhsminor.yy47; -+ yymsp[0].minor.yy563 = yylhsminor.yy563; - break; -- case 114: /* fullname ::= nm DOT nm */ -+ case 119: /* fullname ::= nm DOT nm */ - { -- yylhsminor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); -- if( IN_RENAME_OBJECT && yylhsminor.yy47 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy47->a[0].zName, &yymsp[0].minor.yy0); -+ yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); -+ if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); - } -- yymsp[-2].minor.yy47 = yylhsminor.yy47; -+ yymsp[-2].minor.yy563 = yylhsminor.yy563; - break; -- case 115: /* xfullname ::= nm */ --{yymsp[0].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} -+ case 120: /* xfullname ::= nm */ -+{yymsp[0].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} - break; -- case 116: /* xfullname ::= nm DOT nm */ --{yymsp[-2].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} -+ case 121: /* xfullname ::= nm DOT nm */ -+{yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} - break; -- case 117: /* xfullname ::= nm DOT nm AS nm */ -+ case 122: /* xfullname ::= nm DOT nm AS nm */ - { -- yymsp[-4].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ -- if( yymsp[-4].minor.yy47 ) yymsp[-4].minor.yy47->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); -+ yymsp[-4].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ -+ if( yymsp[-4].minor.yy563 ) yymsp[-4].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); - } - break; -- case 118: /* xfullname ::= nm AS nm */ -+ case 123: /* xfullname ::= nm AS nm */ - { -- yymsp[-2].minor.yy47 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ -- if( yymsp[-2].minor.yy47 ) yymsp[-2].minor.yy47->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); -+ yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ -+ if( yymsp[-2].minor.yy563 ) yymsp[-2].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); - } - break; -- case 119: /* joinop ::= COMMA|JOIN */ --{ yymsp[0].minor.yy192 = JT_INNER; } -+ case 124: /* joinop ::= COMMA|JOIN */ -+{ yymsp[0].minor.yy502 = JT_INNER; } -+ break; -+ case 125: /* joinop ::= JOIN_KW JOIN */ -+{yymsp[-1].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} - break; -- case 120: /* joinop ::= JOIN_KW JOIN */ --{yymsp[-1].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} -+ case 126: /* joinop ::= JOIN_KW nm JOIN */ -+{yymsp[-2].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} - break; -- case 121: /* joinop ::= JOIN_KW nm JOIN */ --{yymsp[-2].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} -+ case 127: /* joinop ::= JOIN_KW nm nm JOIN */ -+{yymsp[-3].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} - break; -- case 122: /* joinop ::= JOIN_KW nm nm JOIN */ --{yymsp[-3].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} -+ case 128: /* on_using ::= ON expr */ -+{yymsp[-1].minor.yy421.pOn = yymsp[0].minor.yy590; yymsp[-1].minor.yy421.pUsing = 0;} - break; -- case 123: /* on_opt ::= ON expr */ -- case 143: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==143); -- case 150: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==150); -- case 216: /* case_else ::= ELSE expr */ yytestcase(yyruleno==216); -- case 237: /* vinto ::= INTO expr */ yytestcase(yyruleno==237); --{yymsp[-1].minor.yy202 = yymsp[0].minor.yy202;} -+ case 129: /* on_using ::= USING LP idlist RP */ -+{yymsp[-3].minor.yy421.pOn = 0; yymsp[-3].minor.yy421.pUsing = yymsp[-1].minor.yy204;} - break; -- case 124: /* on_opt ::= */ -- case 142: /* having_opt ::= */ yytestcase(yyruleno==142); -- case 144: /* limit_opt ::= */ yytestcase(yyruleno==144); -- case 149: /* where_opt ::= */ yytestcase(yyruleno==149); -- case 217: /* case_else ::= */ yytestcase(yyruleno==217); -- case 219: /* case_operand ::= */ yytestcase(yyruleno==219); -- case 238: /* vinto ::= */ yytestcase(yyruleno==238); --{yymsp[1].minor.yy202 = 0;} -+ case 130: /* on_using ::= */ -+{yymsp[1].minor.yy421.pOn = 0; yymsp[1].minor.yy421.pUsing = 0;} - break; -- case 126: /* indexed_opt ::= INDEXED BY nm */ -+ case 132: /* indexed_by ::= INDEXED BY nm */ - {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} - break; -- case 127: /* indexed_opt ::= NOT INDEXED */ -+ case 133: /* indexed_by ::= NOT INDEXED */ - {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} - break; -- case 128: /* using_opt ::= USING LP idlist RP */ --{yymsp[-3].minor.yy600 = yymsp[-1].minor.yy600;} -- break; -- case 129: /* using_opt ::= */ -- case 164: /* idlist_opt ::= */ yytestcase(yyruleno==164); --{yymsp[1].minor.yy600 = 0;} -- break; -- case 131: /* orderby_opt ::= ORDER BY sortlist */ -- case 141: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==141); --{yymsp[-2].minor.yy242 = yymsp[0].minor.yy242;} -+ case 135: /* orderby_opt ::= ORDER BY sortlist */ -+ case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145); -+{yymsp[-2].minor.yy402 = yymsp[0].minor.yy402;} - break; -- case 132: /* sortlist ::= sortlist COMMA expr sortorder nulls */ -+ case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */ - { -- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202); -- sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy242,yymsp[-1].minor.yy192,yymsp[0].minor.yy192); -+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402,yymsp[-2].minor.yy590); -+ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); - } - break; -- case 133: /* sortlist ::= expr sortorder nulls */ -+ case 137: /* sortlist ::= expr sortorder nulls */ - { -- yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy202); /*A-overwrites-Y*/ -- sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy242,yymsp[-1].minor.yy192,yymsp[0].minor.yy192); -+ yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy590); /*A-overwrites-Y*/ -+ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); - } - break; -- case 134: /* sortorder ::= ASC */ --{yymsp[0].minor.yy192 = SQLITE_SO_ASC;} -+ case 138: /* sortorder ::= ASC */ -+{yymsp[0].minor.yy502 = SQLITE_SO_ASC;} - break; -- case 135: /* sortorder ::= DESC */ --{yymsp[0].minor.yy192 = SQLITE_SO_DESC;} -+ case 139: /* sortorder ::= DESC */ -+{yymsp[0].minor.yy502 = SQLITE_SO_DESC;} - break; -- case 136: /* sortorder ::= */ -- case 139: /* nulls ::= */ yytestcase(yyruleno==139); --{yymsp[1].minor.yy192 = SQLITE_SO_UNDEFINED;} -+ case 140: /* sortorder ::= */ -+ case 143: /* nulls ::= */ yytestcase(yyruleno==143); -+{yymsp[1].minor.yy502 = SQLITE_SO_UNDEFINED;} - break; -- case 137: /* nulls ::= NULLS FIRST */ --{yymsp[-1].minor.yy192 = SQLITE_SO_ASC;} -+ case 141: /* nulls ::= NULLS FIRST */ -+{yymsp[-1].minor.yy502 = SQLITE_SO_ASC;} - break; -- case 138: /* nulls ::= NULLS LAST */ --{yymsp[-1].minor.yy192 = SQLITE_SO_DESC;} -+ case 142: /* nulls ::= NULLS LAST */ -+{yymsp[-1].minor.yy502 = SQLITE_SO_DESC;} - break; -- case 145: /* limit_opt ::= LIMIT expr */ --{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy202,0);} -+ case 146: /* having_opt ::= */ -+ case 148: /* limit_opt ::= */ yytestcase(yyruleno==148); -+ case 153: /* where_opt ::= */ yytestcase(yyruleno==153); -+ case 155: /* where_opt_ret ::= */ yytestcase(yyruleno==155); -+ case 232: /* case_else ::= */ yytestcase(yyruleno==232); -+ case 233: /* case_operand ::= */ yytestcase(yyruleno==233); -+ case 252: /* vinto ::= */ yytestcase(yyruleno==252); -+{yymsp[1].minor.yy590 = 0;} - break; -- case 146: /* limit_opt ::= LIMIT expr OFFSET expr */ --{yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} -+ case 147: /* having_opt ::= HAVING expr */ -+ case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154); -+ case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156); -+ case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231); -+ case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251); -+{yymsp[-1].minor.yy590 = yymsp[0].minor.yy590;} - break; -- case 147: /* limit_opt ::= LIMIT expr COMMA expr */ --{yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy202,yymsp[-2].minor.yy202);} -+ case 149: /* limit_opt ::= LIMIT expr */ -+{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,0);} - break; -- case 148: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ -+ case 150: /* limit_opt ::= LIMIT expr OFFSET expr */ -+{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} -+ break; -+ case 151: /* limit_opt ::= LIMIT expr COMMA expr */ -+{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,yymsp[-2].minor.yy590);} -+ break; -+ case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - { -- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy47, &yymsp[-1].minor.yy0); -- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy47,yymsp[0].minor.yy202,0,0); -+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy563, &yymsp[-1].minor.yy0); -+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy563,yymsp[0].minor.yy590,0,0); - } - break; -- case 151: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt */ -+ case 157: /* where_opt_ret ::= RETURNING selcollist */ -+{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-1].minor.yy590 = 0;} -+ break; -+ case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ -+{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-3].minor.yy590 = yymsp[-2].minor.yy590;} -+ break; -+ case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - { -- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy47, &yymsp[-4].minor.yy0); -- sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy242,"set list"); -- yymsp[-5].minor.yy47 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy47, yymsp[-1].minor.yy47); -- sqlite3Update(pParse,yymsp[-5].minor.yy47,yymsp[-2].minor.yy242,yymsp[0].minor.yy202,yymsp[-6].minor.yy192,0,0,0); -+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-4].minor.yy0); -+ sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy402,"set list"); -+ if( yymsp[-1].minor.yy563 ){ -+ SrcList *pFromClause = yymsp[-1].minor.yy563; -+ if( pFromClause->nSrc>1 ){ -+ Select *pSubquery; -+ Token as; -+ pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0); -+ as.n = 0; -+ as.z = 0; -+ pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); -+ } -+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy563, pFromClause); -+ } -+ sqlite3Update(pParse,yymsp[-5].minor.yy563,yymsp[-2].minor.yy402,yymsp[0].minor.yy590,yymsp[-6].minor.yy502,0,0,0); - } - break; -- case 152: /* setlist ::= setlist COMMA nm EQ expr */ -+ case 160: /* setlist ::= setlist COMMA nm EQ expr */ - { -- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[0].minor.yy202); -- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, 1); -+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[0].minor.yy590); -+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, 1); - } - break; -- case 153: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ -+ case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ - { -- yymsp[-6].minor.yy242 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy242, yymsp[-3].minor.yy600, yymsp[0].minor.yy202); -+ yymsp[-6].minor.yy402 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy402, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); - } - break; -- case 154: /* setlist ::= nm EQ expr */ -+ case 162: /* setlist ::= nm EQ expr */ - { -- yylhsminor.yy242 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy202); -- sqlite3ExprListSetName(pParse, yylhsminor.yy242, &yymsp[-2].minor.yy0, 1); -+ yylhsminor.yy402 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy590); -+ sqlite3ExprListSetName(pParse, yylhsminor.yy402, &yymsp[-2].minor.yy0, 1); - } -- yymsp[-2].minor.yy242 = yylhsminor.yy242; -+ yymsp[-2].minor.yy402 = yylhsminor.yy402; - break; -- case 155: /* setlist ::= LP idlist RP EQ expr */ -+ case 163: /* setlist ::= LP idlist RP EQ expr */ - { -- yymsp[-4].minor.yy242 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy600, yymsp[0].minor.yy202); -+ yymsp[-4].minor.yy402 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); - } - break; -- case 156: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ -+ case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - { -- sqlite3Insert(pParse, yymsp[-3].minor.yy47, yymsp[-1].minor.yy539, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, yymsp[0].minor.yy318); -+ sqlite3Insert(pParse, yymsp[-3].minor.yy563, yymsp[-1].minor.yy637, yymsp[-2].minor.yy204, yymsp[-5].minor.yy502, yymsp[0].minor.yy403); - } - break; -- case 157: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ -+ case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - { -- sqlite3Insert(pParse, yymsp[-3].minor.yy47, 0, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, 0); -+ sqlite3Insert(pParse, yymsp[-4].minor.yy563, 0, yymsp[-3].minor.yy204, yymsp[-6].minor.yy502, 0); - } - break; -- case 158: /* upsert ::= */ --{ yymsp[1].minor.yy318 = 0; } -+ case 166: /* upsert ::= */ -+{ yymsp[1].minor.yy403 = 0; } -+ break; -+ case 167: /* upsert ::= RETURNING selcollist */ -+{ yymsp[-1].minor.yy403 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy402); } - break; -- case 159: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ --{ yymsp[-10].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy242,yymsp[-5].minor.yy202,yymsp[-1].minor.yy242,yymsp[0].minor.yy202);} -+ case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -+{ yymsp[-11].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy402,yymsp[-6].minor.yy590,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,yymsp[0].minor.yy403);} - break; -- case 160: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ --{ yymsp[-7].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202,0,0); } -+ case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -+{ yymsp[-8].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy402,yymsp[-3].minor.yy590,0,0,yymsp[0].minor.yy403); } - break; -- case 161: /* upsert ::= ON CONFLICT DO NOTHING */ --{ yymsp[-3].minor.yy318 = sqlite3UpsertNew(pParse->db,0,0,0,0); } -+ case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */ -+{ yymsp[-4].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } - break; -- case 165: /* idlist_opt ::= LP idlist RP */ --{yymsp[-2].minor.yy600 = yymsp[-1].minor.yy600;} -+ case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -+{ yymsp[-7].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,0);} - break; -- case 166: /* idlist ::= idlist COMMA nm */ --{yymsp[-2].minor.yy600 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy600,&yymsp[0].minor.yy0);} -+ case 172: /* returning ::= RETURNING selcollist */ -+{sqlite3AddReturning(pParse,yymsp[0].minor.yy402);} - break; -- case 167: /* idlist ::= nm */ --{yymsp[0].minor.yy600 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} -+ case 175: /* idlist_opt ::= */ -+{yymsp[1].minor.yy204 = 0;} - break; -- case 168: /* expr ::= LP expr RP */ --{yymsp[-2].minor.yy202 = yymsp[-1].minor.yy202;} -+ case 176: /* idlist_opt ::= LP idlist RP */ -+{yymsp[-2].minor.yy204 = yymsp[-1].minor.yy204;} - break; -- case 169: /* expr ::= ID|INDEXED */ -- case 170: /* expr ::= JOIN_KW */ yytestcase(yyruleno==170); --{yymsp[0].minor.yy202=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} -+ case 177: /* idlist ::= idlist COMMA nm */ -+{yymsp[-2].minor.yy204 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy204,&yymsp[0].minor.yy0);} - break; -- case 171: /* expr ::= nm DOT nm */ -+ case 178: /* idlist ::= nm */ -+{yymsp[0].minor.yy204 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} -+ break; -+ case 179: /* expr ::= LP expr RP */ -+{yymsp[-2].minor.yy590 = yymsp[-1].minor.yy590;} -+ break; -+ case 180: /* expr ::= ID|INDEXED|JOIN_KW */ -+{yymsp[0].minor.yy590=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} -+ break; -+ case 181: /* expr ::= nm DOT nm */ - { -- Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); -- Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); -- if( IN_RENAME_OBJECT ){ -- sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0); -- sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0); -- } -- yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); -+ Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); -+ Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); -+ yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); - } -- yymsp[-2].minor.yy202 = yylhsminor.yy202; -+ yymsp[-2].minor.yy590 = yylhsminor.yy590; - break; -- case 172: /* expr ::= nm DOT nm DOT nm */ -+ case 182: /* expr ::= nm DOT nm DOT nm */ - { -- Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1); -- Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); -- Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); -+ Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0); -+ Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); -+ Expr *temp3 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); - Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3); - if( IN_RENAME_OBJECT ){ -- sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0); -- sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0); -+ sqlite3RenameTokenRemap(pParse, 0, temp1); - } -- yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); -+ yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); - } -- yymsp[-4].minor.yy202 = yylhsminor.yy202; -+ yymsp[-4].minor.yy590 = yylhsminor.yy590; - break; -- case 173: /* term ::= NULL|FLOAT|BLOB */ -- case 174: /* term ::= STRING */ yytestcase(yyruleno==174); --{yymsp[0].minor.yy202=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} -+ case 183: /* term ::= NULL|FLOAT|BLOB */ -+ case 184: /* term ::= STRING */ yytestcase(yyruleno==184); -+{yymsp[0].minor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} - break; -- case 175: /* term ::= INTEGER */ -+ case 185: /* term ::= INTEGER */ - { -- yylhsminor.yy202 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); -+ yylhsminor.yy590 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); -+ if( yylhsminor.yy590 ) yylhsminor.yy590->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); - } -- yymsp[0].minor.yy202 = yylhsminor.yy202; -+ yymsp[0].minor.yy590 = yylhsminor.yy590; - break; -- case 176: /* expr ::= VARIABLE */ -+ case 186: /* expr ::= VARIABLE */ - { - if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ - u32 n = yymsp[0].minor.yy0.n; -- yymsp[0].minor.yy202 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); -- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy202, n); -+ yymsp[0].minor.yy590 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); -+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy590, n); - }else{ - /* When doing a nested parse, one can include terms in an expression - ** that look like this: #1 #2 ... These terms refer to registers -@@ -158261,359 +179726,427 @@ static YYACTIONTYPE yy_reduce( - Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/ - assert( t.n>=2 ); - if( pParse->nested==0 ){ -- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); -- yymsp[0].minor.yy202 = 0; -+ parserSyntaxError(pParse, &t); -+ yymsp[0].minor.yy590 = 0; - }else{ -- yymsp[0].minor.yy202 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); -- if( yymsp[0].minor.yy202 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy202->iTable); -+ yymsp[0].minor.yy590 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); -+ if( yymsp[0].minor.yy590 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy590->iTable); - } - } - } - break; -- case 177: /* expr ::= expr COLLATE ID|STRING */ -+ case 187: /* expr ::= expr COLLATE ID|STRING */ -+{ -+ yymsp[-2].minor.yy590 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy590, &yymsp[0].minor.yy0, 1); -+} -+ break; -+ case 188: /* expr ::= CAST LP expr AS typetoken RP */ -+{ -+ yymsp[-5].minor.yy590 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); -+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy590, yymsp[-3].minor.yy590, 0); -+} -+ break; -+ case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ - { -- yymsp[-2].minor.yy202 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy202, &yymsp[0].minor.yy0, 1); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy502); - } -+ yymsp[-4].minor.yy590 = yylhsminor.yy590; - break; -- case 178: /* expr ::= CAST LP expr AS typetoken RP */ -+ case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ - { -- yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); -- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy202, yymsp[-3].minor.yy202, 0); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy402, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy502); -+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-1].minor.yy402); - } -+ yymsp[-7].minor.yy590 = yylhsminor.yy590; - break; -- case 179: /* expr ::= ID|INDEXED LP distinct exprlist RP */ -+ case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ - { -- yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy192); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); - } -- yymsp[-4].minor.yy202 = yylhsminor.yy202; -+ yymsp[-3].minor.yy590 = yylhsminor.yy590; - break; -- case 180: /* expr ::= ID|INDEXED LP STAR RP */ -+ case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ - { -- yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy402, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy502); -+ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); - } -- yymsp[-3].minor.yy202 = yylhsminor.yy202; -+ yymsp[-5].minor.yy590 = yylhsminor.yy590; - break; -- case 181: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ -+ case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ - { -- yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy242, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy192); -- sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy402, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy502); -+ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); -+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-2].minor.yy402); - } -- yymsp[-5].minor.yy202 = yylhsminor.yy202; -+ yymsp[-8].minor.yy590 = yylhsminor.yy590; - break; -- case 182: /* expr ::= ID|INDEXED LP STAR RP filter_over */ -+ case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ - { -- yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); -- sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); -+ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); - } -- yymsp[-4].minor.yy202 = yylhsminor.yy202; -+ yymsp[-4].minor.yy590 = yylhsminor.yy590; - break; -- case 183: /* term ::= CTIME_KW */ -+ case 195: /* term ::= CTIME_KW */ - { -- yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); - } -- yymsp[0].minor.yy202 = yylhsminor.yy202; -+ yymsp[0].minor.yy590 = yylhsminor.yy590; - break; -- case 184: /* expr ::= LP nexprlist COMMA expr RP */ -+ case 196: /* expr ::= LP nexprlist COMMA expr RP */ - { -- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202); -- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); -- if( yymsp[-4].minor.yy202 ){ -- yymsp[-4].minor.yy202->x.pList = pList; -+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590); -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); -+ if( yymsp[-4].minor.yy590 ){ -+ yymsp[-4].minor.yy590->x.pList = pList; - if( ALWAYS(pList->nExpr) ){ -- yymsp[-4].minor.yy202->flags |= pList->a[0].pExpr->flags & EP_Propagate; -+ yymsp[-4].minor.yy590->flags |= pList->a[0].pExpr->flags & EP_Propagate; - } - }else{ - sqlite3ExprListDelete(pParse->db, pList); - } - } - break; -- case 185: /* expr ::= expr AND expr */ --{yymsp[-2].minor.yy202=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} -+ case 197: /* expr ::= expr AND expr */ -+{yymsp[-2].minor.yy590=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} - break; -- case 186: /* expr ::= expr OR expr */ -- case 187: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==187); -- case 188: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==188); -- case 189: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==189); -- case 190: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==190); -- case 191: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==191); -- case 192: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==192); --{yymsp[-2].minor.yy202=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);} -+ case 198: /* expr ::= expr OR expr */ -+ case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199); -+ case 200: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==200); -+ case 201: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==201); -+ case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202); -+ case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203); -+ case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204); -+{yymsp[-2].minor.yy590=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} - break; -- case 193: /* likeop ::= NOT LIKE_KW|MATCH */ -+ case 205: /* likeop ::= NOT LIKE_KW|MATCH */ - {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} - break; -- case 194: /* expr ::= expr likeop expr */ -+ case 206: /* expr ::= expr likeop expr */ - { - ExprList *pList; - int bNot = yymsp[-1].minor.yy0.n & 0x80000000; - yymsp[-1].minor.yy0.n &= 0x7fffffff; -- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy202); -- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy202); -- yymsp[-2].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); -- if( bNot ) yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy202, 0); -- if( yymsp[-2].minor.yy202 ) yymsp[-2].minor.yy202->flags |= EP_InfixFunc; -+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy590); -+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy590); -+ yymsp[-2].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); -+ if( bNot ) yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy590, 0); -+ if( yymsp[-2].minor.yy590 ) yymsp[-2].minor.yy590->flags |= EP_InfixFunc; - } - break; -- case 195: /* expr ::= expr likeop expr ESCAPE expr */ -+ case 207: /* expr ::= expr likeop expr ESCAPE expr */ - { - ExprList *pList; - int bNot = yymsp[-3].minor.yy0.n & 0x80000000; - yymsp[-3].minor.yy0.n &= 0x7fffffff; -- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); -- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy202); -- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202); -- yymsp[-4].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); -- if( bNot ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); -- if( yymsp[-4].minor.yy202 ) yymsp[-4].minor.yy202->flags |= EP_InfixFunc; -+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); -+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy590); -+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); -+ yymsp[-4].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); -+ if( bNot ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); -+ if( yymsp[-4].minor.yy590 ) yymsp[-4].minor.yy590->flags |= EP_InfixFunc; - } - break; -- case 196: /* expr ::= expr ISNULL|NOTNULL */ --{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy202,0);} -+ case 208: /* expr ::= expr ISNULL|NOTNULL */ -+{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy590,0);} - break; -- case 197: /* expr ::= expr NOT NULL */ --{yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy202,0);} -+ case 209: /* expr ::= expr NOT NULL */ -+{yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy590,0);} - break; -- case 198: /* expr ::= expr IS expr */ -+ case 210: /* expr ::= expr IS expr */ - { -- yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy202,yymsp[0].minor.yy202); -- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-2].minor.yy202, TK_ISNULL); -+ yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy590,yymsp[0].minor.yy590); -+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-2].minor.yy590, TK_ISNULL); - } - break; -- case 199: /* expr ::= expr IS NOT expr */ -+ case 211: /* expr ::= expr IS NOT expr */ - { -- yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy202,yymsp[0].minor.yy202); -- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-3].minor.yy202, TK_NOTNULL); -+ yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy590,yymsp[0].minor.yy590); -+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-3].minor.yy590, TK_NOTNULL); - } - break; -- case 200: /* expr ::= NOT expr */ -- case 201: /* expr ::= BITNOT expr */ yytestcase(yyruleno==201); --{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy202, 0);/*A-overwrites-B*/} -+ case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */ -+{ -+ yymsp[-5].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy590,yymsp[0].minor.yy590); -+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-5].minor.yy590, TK_ISNULL); -+} - break; -- case 202: /* expr ::= PLUS|MINUS expr */ -+ case 213: /* expr ::= expr IS DISTINCT FROM expr */ - { -- yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy202, 0); -- /*A-overwrites-B*/ -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy590,yymsp[0].minor.yy590); -+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-4].minor.yy590, TK_NOTNULL); - } - break; -- case 203: /* between_op ::= BETWEEN */ -- case 206: /* in_op ::= IN */ yytestcase(yyruleno==206); --{yymsp[0].minor.yy192 = 0;} -+ case 214: /* expr ::= NOT expr */ -+ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); -+{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy590, 0);/*A-overwrites-B*/} - break; -- case 205: /* expr ::= expr between_op expr AND expr */ -+ case 216: /* expr ::= PLUS|MINUS expr */ - { -- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); -- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202); -- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy202, 0); -- if( yymsp[-4].minor.yy202 ){ -- yymsp[-4].minor.yy202->x.pList = pList; -+ Expr *p = yymsp[0].minor.yy590; -+ u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); -+ assert( TK_UPLUS>TK_PLUS ); -+ assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); -+ if( p && p->op==TK_UPLUS ){ -+ p->op = op; -+ yymsp[-1].minor.yy590 = p; -+ }else{ -+ yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, op, p, 0); -+ /*A-overwrites-B*/ -+ } -+} -+ break; -+ case 217: /* expr ::= expr PTR expr */ -+{ -+ ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy590); -+ pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy590); -+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); -+} -+ yymsp[-2].minor.yy590 = yylhsminor.yy590; -+ break; -+ case 218: /* between_op ::= BETWEEN */ -+ case 221: /* in_op ::= IN */ yytestcase(yyruleno==221); -+{yymsp[0].minor.yy502 = 0;} -+ break; -+ case 220: /* expr ::= expr between_op expr AND expr */ -+{ -+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); -+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy590, 0); -+ if( yymsp[-4].minor.yy590 ){ -+ yymsp[-4].minor.yy590->x.pList = pList; - }else{ - sqlite3ExprListDelete(pParse->db, pList); - } -- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); -+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); - } - break; -- case 208: /* expr ::= expr in_op LP exprlist RP */ -+ case 223: /* expr ::= expr in_op LP exprlist RP */ - { -- if( yymsp[-1].minor.yy242==0 ){ -+ if( yymsp[-1].minor.yy402==0 ){ - /* Expressions of the form - ** - ** expr1 IN () - ** expr1 NOT IN () - ** -- ** simplify to constants 0 (false) and 1 (true), respectively, -- ** regardless of the value of expr1. -+ ** simplify to constants 0 (false) and 1 (true), respectively. -+ ** -+ ** Except, do not apply this optimization if expr1 contains a function -+ ** because that function might be an aggregate (we don't know yet whether -+ ** it is or not) and if it is an aggregate, that could change the meaning -+ ** of the whole query. - */ -- sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202); -- yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0"); -- }else if( yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){ -- Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr; -- yymsp[-1].minor.yy242->a[0].pExpr = 0; -- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); -- pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); -- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS); -- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); -+ Expr *pB = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy502 ? "true" : "false"); -+ if( pB ) sqlite3ExprIdToTrueFalse(pB); -+ if( !ExprHasProperty(yymsp[-4].minor.yy590, EP_HasFunc) ){ -+ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy590); -+ yymsp[-4].minor.yy590 = pB; -+ }else{ -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, yymsp[-3].minor.yy502 ? TK_OR : TK_AND, pB, yymsp[-4].minor.yy590); -+ } - }else{ -- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); -- if( yymsp[-4].minor.yy202 ){ -- yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy242; -- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202); -+ Expr *pRHS = yymsp[-1].minor.yy402->a[0].pExpr; -+ if( yymsp[-1].minor.yy402->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy590->op!=TK_VECTOR ){ -+ yymsp[-1].minor.yy402->a[0].pExpr = 0; -+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); -+ pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy590, pRHS); -+ }else if( yymsp[-1].minor.yy402->nExpr==1 && pRHS->op==TK_SELECT ){ -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); -+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pRHS->x.pSelect); -+ pRHS->x.pSelect = 0; -+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); - }else{ -- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242); -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); -+ if( yymsp[-4].minor.yy590==0 ){ -+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); -+ }else if( yymsp[-4].minor.yy590->pLeft->op==TK_VECTOR ){ -+ int nExpr = yymsp[-4].minor.yy590->pLeft->x.pList->nExpr; -+ Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy402); -+ if( pSelectRHS ){ -+ parserDoubleLinkSelect(pParse, pSelectRHS); -+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelectRHS); -+ } -+ }else{ -+ yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy402; -+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); -+ } - } -- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); -+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); - } - } - break; -- case 209: /* expr ::= LP select RP */ -+ case 224: /* expr ::= LP select RP */ - { -- yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); -- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy202, yymsp[-1].minor.yy539); -+ yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); -+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy590, yymsp[-1].minor.yy637); - } - break; -- case 210: /* expr ::= expr in_op LP select RP */ -+ case 225: /* expr ::= expr in_op LP select RP */ - { -- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); -- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, yymsp[-1].minor.yy539); -- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); -+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, yymsp[-1].minor.yy637); -+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); - } - break; -- case 211: /* expr ::= expr in_op nm dbnm paren_exprlist */ -+ case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */ - { - SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); - Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); -- if( yymsp[0].minor.yy242 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy242); -- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0); -- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, pSelect); -- if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0); -+ if( yymsp[0].minor.yy402 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy402); -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); -+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelect); -+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); - } - break; -- case 212: /* expr ::= EXISTS LP select RP */ -+ case 227: /* expr ::= EXISTS LP select RP */ - { - Expr *p; -- p = yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); -- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy539); -+ p = yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); -+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy637); - } - break; -- case 213: /* expr ::= CASE case_operand case_exprlist case_else END */ -+ case 228: /* expr ::= CASE case_operand case_exprlist case_else END */ - { -- yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy202, 0); -- if( yymsp[-4].minor.yy202 ){ -- yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy202 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[-1].minor.yy202) : yymsp[-2].minor.yy242; -- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202); -+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy590, 0); -+ if( yymsp[-4].minor.yy590 ){ -+ yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy590 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590) : yymsp[-2].minor.yy402; -+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); - }else{ -- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy242); -- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy202); -+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy402); -+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); - } - } - break; -- case 214: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ -+ case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ - { -- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[-2].minor.yy202); -- yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[0].minor.yy202); -+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); -+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[0].minor.yy590); - } - break; -- case 215: /* case_exprlist ::= WHEN expr THEN expr */ -+ case 230: /* case_exprlist ::= WHEN expr THEN expr */ - { -- yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202); -- yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy242, yymsp[0].minor.yy202); -+ yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); -+ yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy402, yymsp[0].minor.yy590); - } - break; -- case 218: /* case_operand ::= expr */ --{yymsp[0].minor.yy202 = yymsp[0].minor.yy202; /*A-overwrites-X*/} -- break; -- case 221: /* nexprlist ::= nexprlist COMMA expr */ --{yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[0].minor.yy202);} -+ case 235: /* nexprlist ::= nexprlist COMMA expr */ -+{yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[0].minor.yy590);} - break; -- case 222: /* nexprlist ::= expr */ --{yymsp[0].minor.yy242 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy202); /*A-overwrites-Y*/} -+ case 236: /* nexprlist ::= expr */ -+{yymsp[0].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy590); /*A-overwrites-Y*/} - break; -- case 224: /* paren_exprlist ::= LP exprlist RP */ -- case 229: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==229); --{yymsp[-2].minor.yy242 = yymsp[-1].minor.yy242;} -+ case 238: /* paren_exprlist ::= LP exprlist RP */ -+ case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243); -+{yymsp[-2].minor.yy402 = yymsp[-1].minor.yy402;} - break; -- case 225: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ -+ case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - { - sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, -- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy242, yymsp[-10].minor.yy192, -- &yymsp[-11].minor.yy0, yymsp[0].minor.yy202, SQLITE_SO_ASC, yymsp[-8].minor.yy192, SQLITE_IDXTYPE_APPDEF); -+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy402, yymsp[-10].minor.yy502, -+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy590, SQLITE_SO_ASC, yymsp[-8].minor.yy502, SQLITE_IDXTYPE_APPDEF); - if( IN_RENAME_OBJECT && pParse->pNewIndex ){ - sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); - } - } - break; -- case 226: /* uniqueflag ::= UNIQUE */ -- case 268: /* raisetype ::= ABORT */ yytestcase(yyruleno==268); --{yymsp[0].minor.yy192 = OE_Abort;} -+ case 240: /* uniqueflag ::= UNIQUE */ -+ case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282); -+{yymsp[0].minor.yy502 = OE_Abort;} - break; -- case 227: /* uniqueflag ::= */ --{yymsp[1].minor.yy192 = OE_None;} -+ case 241: /* uniqueflag ::= */ -+{yymsp[1].minor.yy502 = OE_None;} - break; -- case 230: /* eidlist ::= eidlist COMMA nm collate sortorder */ -+ case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */ - { -- yymsp[-4].minor.yy242 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); -+ yymsp[-4].minor.yy402 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); - } - break; -- case 231: /* eidlist ::= nm collate sortorder */ -+ case 245: /* eidlist ::= nm collate sortorder */ - { -- yymsp[-2].minor.yy242 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); /*A-overwrites-Y*/ -+ yymsp[-2].minor.yy402 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); /*A-overwrites-Y*/ - } - break; -- case 234: /* cmd ::= DROP INDEX ifexists fullname */ --{sqlite3DropIndex(pParse, yymsp[0].minor.yy47, yymsp[-1].minor.yy192);} -+ case 248: /* cmd ::= DROP INDEX ifexists fullname */ -+{sqlite3DropIndex(pParse, yymsp[0].minor.yy563, yymsp[-1].minor.yy502);} - break; -- case 235: /* cmd ::= VACUUM vinto */ --{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy202);} -+ case 249: /* cmd ::= VACUUM vinto */ -+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy590);} - break; -- case 236: /* cmd ::= VACUUM nm vinto */ --{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy202);} -+ case 250: /* cmd ::= VACUUM nm vinto */ -+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy590);} - break; -- case 239: /* cmd ::= PRAGMA nm dbnm */ -+ case 253: /* cmd ::= PRAGMA nm dbnm */ - {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} - break; -- case 240: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ -+ case 254: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ - {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} - break; -- case 241: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ -+ case 255: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ - {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} - break; -- case 242: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ -+ case 256: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ - {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} - break; -- case 243: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ -+ case 257: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ - {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} - break; -- case 246: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ -+ case 260: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - { - Token all; - all.z = yymsp[-3].minor.yy0.z; - all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; -- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy447, &all); -+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy319, &all); - } - break; -- case 247: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ -+ case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - { -- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy192, yymsp[-4].minor.yy230.a, yymsp[-4].minor.yy230.b, yymsp[-2].minor.yy47, yymsp[0].minor.yy202, yymsp[-10].minor.yy192, yymsp[-8].minor.yy192); -+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy502, yymsp[-4].minor.yy28.a, yymsp[-4].minor.yy28.b, yymsp[-2].minor.yy563, yymsp[0].minor.yy590, yymsp[-10].minor.yy502, yymsp[-8].minor.yy502); - yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ -+#ifdef SQLITE_DEBUG -+ assert( pParse->isCreate ); /* Set by createkw reduce action */ -+ pParse->isCreate = 0; /* But, should not be set for CREATE TRIGGER */ -+#endif - } - break; -- case 248: /* trigger_time ::= BEFORE|AFTER */ --{ yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/ } -+ case 262: /* trigger_time ::= BEFORE|AFTER */ -+{ yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/ } - break; -- case 249: /* trigger_time ::= INSTEAD OF */ --{ yymsp[-1].minor.yy192 = TK_INSTEAD;} -+ case 263: /* trigger_time ::= INSTEAD OF */ -+{ yymsp[-1].minor.yy502 = TK_INSTEAD;} - break; -- case 250: /* trigger_time ::= */ --{ yymsp[1].minor.yy192 = TK_BEFORE; } -+ case 264: /* trigger_time ::= */ -+{ yymsp[1].minor.yy502 = TK_BEFORE; } - break; -- case 251: /* trigger_event ::= DELETE|INSERT */ -- case 252: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==252); --{yymsp[0].minor.yy230.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy230.b = 0;} -+ case 265: /* trigger_event ::= DELETE|INSERT */ -+ case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266); -+{yymsp[0].minor.yy28.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy28.b = 0;} - break; -- case 253: /* trigger_event ::= UPDATE OF idlist */ --{yymsp[-2].minor.yy230.a = TK_UPDATE; yymsp[-2].minor.yy230.b = yymsp[0].minor.yy600;} -+ case 267: /* trigger_event ::= UPDATE OF idlist */ -+{yymsp[-2].minor.yy28.a = TK_UPDATE; yymsp[-2].minor.yy28.b = yymsp[0].minor.yy204;} - break; -- case 254: /* when_clause ::= */ -- case 273: /* key_opt ::= */ yytestcase(yyruleno==273); --{ yymsp[1].minor.yy202 = 0; } -+ case 268: /* when_clause ::= */ -+ case 287: /* key_opt ::= */ yytestcase(yyruleno==287); -+{ yymsp[1].minor.yy590 = 0; } - break; -- case 255: /* when_clause ::= WHEN expr */ -- case 274: /* key_opt ::= KEY expr */ yytestcase(yyruleno==274); --{ yymsp[-1].minor.yy202 = yymsp[0].minor.yy202; } -+ case 269: /* when_clause ::= WHEN expr */ -+ case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288); -+{ yymsp[-1].minor.yy590 = yymsp[0].minor.yy590; } - break; -- case 256: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ -+ case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - { -- assert( yymsp[-2].minor.yy447!=0 ); -- yymsp[-2].minor.yy447->pLast->pNext = yymsp[-1].minor.yy447; -- yymsp[-2].minor.yy447->pLast = yymsp[-1].minor.yy447; -+ assert( yymsp[-2].minor.yy319!=0 ); -+ yymsp[-2].minor.yy319->pLast->pNext = yymsp[-1].minor.yy319; -+ yymsp[-2].minor.yy319->pLast = yymsp[-1].minor.yy319; - } - break; -- case 257: /* trigger_cmd_list ::= trigger_cmd SEMI */ -+ case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */ - { -- assert( yymsp[-1].minor.yy447!=0 ); -- yymsp[-1].minor.yy447->pLast = yymsp[-1].minor.yy447; -+ assert( yymsp[-1].minor.yy319!=0 ); -+ yymsp[-1].minor.yy319->pLast = yymsp[-1].minor.yy319; - } - break; -- case 258: /* trnm ::= nm DOT nm */ -+ case 272: /* trnm ::= nm DOT nm */ - { - yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; - sqlite3ErrorMsg(pParse, -@@ -158621,344 +180154,377 @@ static YYACTIONTYPE yy_reduce( - "statements within triggers"); - } - break; -- case 259: /* tridxby ::= INDEXED BY nm */ -+ case 273: /* tridxby ::= INDEXED BY nm */ - { - sqlite3ErrorMsg(pParse, - "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " - "within triggers"); - } - break; -- case 260: /* tridxby ::= NOT INDEXED */ -+ case 274: /* tridxby ::= NOT INDEXED */ - { - sqlite3ErrorMsg(pParse, - "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " - "within triggers"); - } - break; -- case 261: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ --{yylhsminor.yy447 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy47, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202, yymsp[-7].minor.yy192, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy436);} -- yymsp[-8].minor.yy447 = yylhsminor.yy447; -+ case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -+{yylhsminor.yy319 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy563, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590, yymsp[-7].minor.yy502, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy342);} -+ yymsp[-8].minor.yy319 = yylhsminor.yy319; - break; -- case 262: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ -+ case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - { -- yylhsminor.yy447 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy600,yymsp[-2].minor.yy539,yymsp[-6].minor.yy192,yymsp[-1].minor.yy318,yymsp[-7].minor.yy436,yymsp[0].minor.yy436);/*yylhsminor.yy447-overwrites-yymsp[-6].minor.yy192*/ -+ yylhsminor.yy319 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy204,yymsp[-2].minor.yy637,yymsp[-6].minor.yy502,yymsp[-1].minor.yy403,yymsp[-7].minor.yy342,yymsp[0].minor.yy342);/*yylhsminor.yy319-overwrites-yymsp[-6].minor.yy502*/ - } -- yymsp[-7].minor.yy447 = yylhsminor.yy447; -+ yymsp[-7].minor.yy319 = yylhsminor.yy319; - break; -- case 263: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ --{yylhsminor.yy447 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy202, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy436);} -- yymsp[-5].minor.yy447 = yylhsminor.yy447; -+ case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -+{yylhsminor.yy319 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy590, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy342);} -+ yymsp[-5].minor.yy319 = yylhsminor.yy319; - break; -- case 264: /* trigger_cmd ::= scanpt select scanpt */ --{yylhsminor.yy447 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy539, yymsp[-2].minor.yy436, yymsp[0].minor.yy436); /*yylhsminor.yy447-overwrites-yymsp[-1].minor.yy539*/} -- yymsp[-2].minor.yy447 = yylhsminor.yy447; -+ case 278: /* trigger_cmd ::= scanpt select scanpt */ -+{yylhsminor.yy319 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy637, yymsp[-2].minor.yy342, yymsp[0].minor.yy342); /*yylhsminor.yy319-overwrites-yymsp[-1].minor.yy637*/} -+ yymsp[-2].minor.yy319 = yylhsminor.yy319; - break; -- case 265: /* expr ::= RAISE LP IGNORE RP */ -+ case 279: /* expr ::= RAISE LP IGNORE RP */ - { -- yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); -- if( yymsp[-3].minor.yy202 ){ -- yymsp[-3].minor.yy202->affExpr = OE_Ignore; -+ yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); -+ if( yymsp[-3].minor.yy590 ){ -+ yymsp[-3].minor.yy590->affExpr = OE_Ignore; - } - } - break; -- case 266: /* expr ::= RAISE LP raisetype COMMA nm RP */ -+ case 280: /* expr ::= RAISE LP raisetype COMMA expr RP */ - { -- yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); -- if( yymsp[-5].minor.yy202 ) { -- yymsp[-5].minor.yy202->affExpr = (char)yymsp[-3].minor.yy192; -+ yymsp[-5].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy590, 0); -+ if( yymsp[-5].minor.yy590 ) { -+ yymsp[-5].minor.yy590->affExpr = (char)yymsp[-3].minor.yy502; - } - } - break; -- case 267: /* raisetype ::= ROLLBACK */ --{yymsp[0].minor.yy192 = OE_Rollback;} -+ case 281: /* raisetype ::= ROLLBACK */ -+{yymsp[0].minor.yy502 = OE_Rollback;} - break; -- case 269: /* raisetype ::= FAIL */ --{yymsp[0].minor.yy192 = OE_Fail;} -+ case 283: /* raisetype ::= FAIL */ -+{yymsp[0].minor.yy502 = OE_Fail;} - break; -- case 270: /* cmd ::= DROP TRIGGER ifexists fullname */ -+ case 284: /* cmd ::= DROP TRIGGER ifexists fullname */ - { -- sqlite3DropTrigger(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy192); -+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy563,yymsp[-1].minor.yy502); - } - break; -- case 271: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ -+ case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - { -- sqlite3Attach(pParse, yymsp[-3].minor.yy202, yymsp[-1].minor.yy202, yymsp[0].minor.yy202); -+ sqlite3Attach(pParse, yymsp[-3].minor.yy590, yymsp[-1].minor.yy590, yymsp[0].minor.yy590); - } - break; -- case 272: /* cmd ::= DETACH database_kw_opt expr */ -+ case 286: /* cmd ::= DETACH database_kw_opt expr */ - { -- sqlite3Detach(pParse, yymsp[0].minor.yy202); -+ sqlite3Detach(pParse, yymsp[0].minor.yy590); - } - break; -- case 275: /* cmd ::= REINDEX */ -+ case 289: /* cmd ::= REINDEX */ - {sqlite3Reindex(pParse, 0, 0);} - break; -- case 276: /* cmd ::= REINDEX nm dbnm */ -+ case 290: /* cmd ::= REINDEX nm dbnm */ - {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} - break; -- case 277: /* cmd ::= ANALYZE */ -+ case 291: /* cmd ::= ANALYZE */ - {sqlite3Analyze(pParse, 0, 0);} - break; -- case 278: /* cmd ::= ANALYZE nm dbnm */ -+ case 292: /* cmd ::= ANALYZE nm dbnm */ - {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} - break; -- case 279: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ -+ case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ - { -- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy47,&yymsp[0].minor.yy0); -+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy563,&yymsp[0].minor.yy0); - } - break; -- case 280: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ -+ case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - { - yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; - sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); - } - break; -- case 281: /* add_column_fullname ::= fullname */ -+ case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ -+{ -+ sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy563, &yymsp[0].minor.yy0); -+} -+ break; -+ case 296: /* add_column_fullname ::= fullname */ - { - disableLookaside(pParse); -- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy47); -+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy563); - } - break; -- case 282: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ -+ case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - { -- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy47, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); -+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy563, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); - } - break; -- case 283: /* cmd ::= create_vtab */ -+ case 298: /* cmd ::= create_vtab */ - {sqlite3VtabFinishParse(pParse,0);} - break; -- case 284: /* cmd ::= create_vtab LP vtabarglist RP */ -+ case 299: /* cmd ::= create_vtab LP vtabarglist RP */ - {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} - break; -- case 285: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ -+ case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - { -- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy192); -+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy502); - } - break; -- case 286: /* vtabarg ::= */ -+ case 301: /* vtabarg ::= */ - {sqlite3VtabArgInit(pParse);} - break; -- case 287: /* vtabargtoken ::= ANY */ -- case 288: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==288); -- case 289: /* lp ::= LP */ yytestcase(yyruleno==289); -+ case 302: /* vtabargtoken ::= ANY */ -+ case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303); -+ case 304: /* lp ::= LP */ yytestcase(yyruleno==304); - {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} - break; -- case 290: /* with ::= WITH wqlist */ -- case 291: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==291); --{ sqlite3WithPush(pParse, yymsp[0].minor.yy131, 1); } -+ case 305: /* with ::= WITH wqlist */ -+ case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306); -+{ sqlite3WithPush(pParse, yymsp[0].minor.yy125, 1); } -+ break; -+ case 307: /* wqas ::= AS */ -+{yymsp[0].minor.yy444 = M10d_Any;} - break; -- case 292: /* wqlist ::= nm eidlist_opt AS LP select RP */ -+ case 308: /* wqas ::= AS MATERIALIZED */ -+{yymsp[-1].minor.yy444 = M10d_Yes;} -+ break; -+ case 309: /* wqas ::= AS NOT MATERIALIZED */ -+{yymsp[-2].minor.yy444 = M10d_No;} -+ break; -+ case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */ - { -- yymsp[-5].minor.yy131 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); /*A-overwrites-X*/ -+ yymsp[-5].minor.yy361 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy402, yymsp[-1].minor.yy637, yymsp[-3].minor.yy444); /*A-overwrites-X*/ - } - break; -- case 293: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ -+ case 311: /* withnm ::= nm */ -+{pParse->bHasWith = 1;} -+ break; -+ case 312: /* wqlist ::= wqitem */ - { -- yymsp[-7].minor.yy131 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy131, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); -+ yymsp[0].minor.yy125 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy361); /*A-overwrites-X*/ - } - break; -- case 294: /* windowdefn_list ::= windowdefn */ --{ yylhsminor.yy303 = yymsp[0].minor.yy303; } -- yymsp[0].minor.yy303 = yylhsminor.yy303; -- break; -- case 295: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ -+ case 313: /* wqlist ::= wqlist COMMA wqitem */ - { -- assert( yymsp[0].minor.yy303!=0 ); -- sqlite3WindowChain(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy303); -- yymsp[0].minor.yy303->pNextWin = yymsp[-2].minor.yy303; -- yylhsminor.yy303 = yymsp[0].minor.yy303; -+ yymsp[-2].minor.yy125 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy125, yymsp[0].minor.yy361); - } -- yymsp[-2].minor.yy303 = yylhsminor.yy303; - break; -- case 296: /* windowdefn ::= nm AS LP window RP */ -+ case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ - { -- if( ALWAYS(yymsp[-1].minor.yy303) ){ -- yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); -- } -- yylhsminor.yy303 = yymsp[-1].minor.yy303; -+ assert( yymsp[0].minor.yy483!=0 ); -+ sqlite3WindowChain(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy483); -+ yymsp[0].minor.yy483->pNextWin = yymsp[-2].minor.yy483; -+ yylhsminor.yy483 = yymsp[0].minor.yy483; - } -- yymsp[-4].minor.yy303 = yylhsminor.yy303; -+ yymsp[-2].minor.yy483 = yylhsminor.yy483; - break; -- case 297: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ -+ case 315: /* windowdefn ::= nm AS LP window RP */ - { -- yymsp[-4].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, 0); -+ if( ALWAYS(yymsp[-1].minor.yy483) ){ -+ yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); -+ } -+ yylhsminor.yy483 = yymsp[-1].minor.yy483; - } -+ yymsp[-4].minor.yy483 = yylhsminor.yy483; - break; -- case 298: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ -+ case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - { -- yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, &yymsp[-5].minor.yy0); -+ yymsp[-4].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, 0); - } -- yymsp[-5].minor.yy303 = yylhsminor.yy303; - break; -- case 299: /* window ::= ORDER BY sortlist frame_opt */ -+ case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - { -- yymsp[-3].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, 0); -+ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, &yymsp[-5].minor.yy0); - } -+ yymsp[-5].minor.yy483 = yylhsminor.yy483; - break; -- case 300: /* window ::= nm ORDER BY sortlist frame_opt */ -+ case 318: /* window ::= ORDER BY sortlist frame_opt */ - { -- yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0); -+ yymsp[-3].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, 0); - } -- yymsp[-4].minor.yy303 = yylhsminor.yy303; - break; -- case 301: /* window ::= frame_opt */ -- case 320: /* filter_over ::= over_clause */ yytestcase(yyruleno==320); -+ case 319: /* window ::= nm ORDER BY sortlist frame_opt */ - { -- yylhsminor.yy303 = yymsp[0].minor.yy303; -+ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0); - } -- yymsp[0].minor.yy303 = yylhsminor.yy303; -+ yymsp[-4].minor.yy483 = yylhsminor.yy483; - break; -- case 302: /* window ::= nm frame_opt */ -+ case 320: /* window ::= nm frame_opt */ - { -- yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, 0, &yymsp[-1].minor.yy0); -+ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, 0, &yymsp[-1].minor.yy0); - } -- yymsp[-1].minor.yy303 = yylhsminor.yy303; -+ yymsp[-1].minor.yy483 = yylhsminor.yy483; - break; -- case 303: /* frame_opt ::= */ -+ case 321: /* frame_opt ::= */ - { -- yymsp[1].minor.yy303 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); -+ yymsp[1].minor.yy483 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); - } - break; -- case 304: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ -+ case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - { -- yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy192, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy58); -+ yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy502, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy444); - } -- yymsp[-2].minor.yy303 = yylhsminor.yy303; -+ yymsp[-2].minor.yy483 = yylhsminor.yy483; - break; -- case 305: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ -+ case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - { -- yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy192, yymsp[-3].minor.yy77.eType, yymsp[-3].minor.yy77.pExpr, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, yymsp[0].minor.yy58); -+ yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy502, yymsp[-3].minor.yy205.eType, yymsp[-3].minor.yy205.pExpr, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, yymsp[0].minor.yy444); - } -- yymsp[-5].minor.yy303 = yylhsminor.yy303; -+ yymsp[-5].minor.yy483 = yylhsminor.yy483; - break; -- case 307: /* frame_bound_s ::= frame_bound */ -- case 309: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==309); --{yylhsminor.yy77 = yymsp[0].minor.yy77;} -- yymsp[0].minor.yy77 = yylhsminor.yy77; -+ case 325: /* frame_bound_s ::= frame_bound */ -+ case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327); -+{yylhsminor.yy205 = yymsp[0].minor.yy205;} -+ yymsp[0].minor.yy205 = yylhsminor.yy205; - break; -- case 308: /* frame_bound_s ::= UNBOUNDED PRECEDING */ -- case 310: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==310); -- case 312: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==312); --{yylhsminor.yy77.eType = yymsp[-1].major; yylhsminor.yy77.pExpr = 0;} -- yymsp[-1].minor.yy77 = yylhsminor.yy77; -+ case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */ -+ case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328); -+ case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330); -+{yylhsminor.yy205.eType = yymsp[-1].major; yylhsminor.yy205.pExpr = 0;} -+ yymsp[-1].minor.yy205 = yylhsminor.yy205; - break; -- case 311: /* frame_bound ::= expr PRECEDING|FOLLOWING */ --{yylhsminor.yy77.eType = yymsp[0].major; yylhsminor.yy77.pExpr = yymsp[-1].minor.yy202;} -- yymsp[-1].minor.yy77 = yylhsminor.yy77; -+ case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */ -+{yylhsminor.yy205.eType = yymsp[0].major; yylhsminor.yy205.pExpr = yymsp[-1].minor.yy590;} -+ yymsp[-1].minor.yy205 = yylhsminor.yy205; - break; -- case 313: /* frame_exclude_opt ::= */ --{yymsp[1].minor.yy58 = 0;} -+ case 331: /* frame_exclude_opt ::= */ -+{yymsp[1].minor.yy444 = 0;} - break; -- case 314: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ --{yymsp[-1].minor.yy58 = yymsp[0].minor.yy58;} -+ case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -+{yymsp[-1].minor.yy444 = yymsp[0].minor.yy444;} - break; -- case 315: /* frame_exclude ::= NO OTHERS */ -- case 316: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==316); --{yymsp[-1].minor.yy58 = yymsp[-1].major; /*A-overwrites-X*/} -+ case 333: /* frame_exclude ::= NO OTHERS */ -+ case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334); -+{yymsp[-1].minor.yy444 = yymsp[-1].major; /*A-overwrites-X*/} - break; -- case 317: /* frame_exclude ::= GROUP|TIES */ --{yymsp[0].minor.yy58 = yymsp[0].major; /*A-overwrites-X*/} -+ case 335: /* frame_exclude ::= GROUP|TIES */ -+{yymsp[0].minor.yy444 = yymsp[0].major; /*A-overwrites-X*/} - break; -- case 318: /* window_clause ::= WINDOW windowdefn_list */ --{ yymsp[-1].minor.yy303 = yymsp[0].minor.yy303; } -+ case 336: /* window_clause ::= WINDOW windowdefn_list */ -+{ yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; } - break; -- case 319: /* filter_over ::= filter_clause over_clause */ -+ case 337: /* filter_over ::= filter_clause over_clause */ - { -- yymsp[0].minor.yy303->pFilter = yymsp[-1].minor.yy202; -- yylhsminor.yy303 = yymsp[0].minor.yy303; -+ if( yymsp[0].minor.yy483 ){ -+ yymsp[0].minor.yy483->pFilter = yymsp[-1].minor.yy590; -+ }else{ -+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); -+ } -+ yylhsminor.yy483 = yymsp[0].minor.yy483; - } -- yymsp[-1].minor.yy303 = yylhsminor.yy303; -+ yymsp[-1].minor.yy483 = yylhsminor.yy483; - break; -- case 321: /* filter_over ::= filter_clause */ -+ case 338: /* filter_over ::= over_clause */ - { -- yylhsminor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); -- if( yylhsminor.yy303 ){ -- yylhsminor.yy303->eFrmType = TK_FILTER; -- yylhsminor.yy303->pFilter = yymsp[0].minor.yy202; -+ yylhsminor.yy483 = yymsp[0].minor.yy483; -+} -+ yymsp[0].minor.yy483 = yylhsminor.yy483; -+ break; -+ case 339: /* filter_over ::= filter_clause */ -+{ -+ yylhsminor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); -+ if( yylhsminor.yy483 ){ -+ yylhsminor.yy483->eFrmType = TK_FILTER; -+ yylhsminor.yy483->pFilter = yymsp[0].minor.yy590; - }else{ -- sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy202); -+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy590); - } - } -- yymsp[0].minor.yy303 = yylhsminor.yy303; -+ yymsp[0].minor.yy483 = yylhsminor.yy483; - break; -- case 322: /* over_clause ::= OVER LP window RP */ -+ case 340: /* over_clause ::= OVER LP window RP */ - { -- yymsp[-3].minor.yy303 = yymsp[-1].minor.yy303; -- assert( yymsp[-3].minor.yy303!=0 ); -+ yymsp[-3].minor.yy483 = yymsp[-1].minor.yy483; -+ assert( yymsp[-3].minor.yy483!=0 ); - } - break; -- case 323: /* over_clause ::= OVER nm */ -+ case 341: /* over_clause ::= OVER nm */ - { -- yymsp[-1].minor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); -- if( yymsp[-1].minor.yy303 ){ -- yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); -+ yymsp[-1].minor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); -+ if( yymsp[-1].minor.yy483 ){ -+ yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); - } - } - break; -- case 324: /* filter_clause ::= FILTER LP WHERE expr RP */ --{ yymsp[-4].minor.yy202 = yymsp[-1].minor.yy202; } -+ case 342: /* filter_clause ::= FILTER LP WHERE expr RP */ -+{ yymsp[-4].minor.yy590 = yymsp[-1].minor.yy590; } -+ break; -+ case 343: /* term ::= QNUMBER */ -+{ -+ yylhsminor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); -+ sqlite3DequoteNumber(pParse, yylhsminor.yy590); -+} -+ yymsp[0].minor.yy590 = yylhsminor.yy590; - break; - default: -- /* (325) input ::= cmdlist */ yytestcase(yyruleno==325); -- /* (326) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==326); -- /* (327) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=327); -- /* (328) ecmd ::= SEMI */ yytestcase(yyruleno==328); -- /* (329) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==329); -- /* (330) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=330); -- /* (331) trans_opt ::= */ yytestcase(yyruleno==331); -- /* (332) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==332); -- /* (333) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==333); -- /* (334) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==334); -- /* (335) savepoint_opt ::= */ yytestcase(yyruleno==335); -- /* (336) cmd ::= create_table create_table_args */ yytestcase(yyruleno==336); -- /* (337) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==337); -- /* (338) columnlist ::= columnname carglist */ yytestcase(yyruleno==338); -- /* (339) nm ::= ID|INDEXED */ yytestcase(yyruleno==339); -- /* (340) nm ::= STRING */ yytestcase(yyruleno==340); -- /* (341) nm ::= JOIN_KW */ yytestcase(yyruleno==341); -- /* (342) typetoken ::= typename */ yytestcase(yyruleno==342); -- /* (343) typename ::= ID|STRING */ yytestcase(yyruleno==343); -- /* (344) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=344); -- /* (345) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=345); -- /* (346) carglist ::= carglist ccons */ yytestcase(yyruleno==346); -- /* (347) carglist ::= */ yytestcase(yyruleno==347); -- /* (348) ccons ::= NULL onconf */ yytestcase(yyruleno==348); -- /* (349) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==349); -- /* (350) ccons ::= AS generated */ yytestcase(yyruleno==350); -- /* (351) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==351); -- /* (352) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==352); -- /* (353) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=353); -- /* (354) tconscomma ::= */ yytestcase(yyruleno==354); -- /* (355) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=355); -- /* (356) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=356); -- /* (357) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=357); -- /* (358) oneselect ::= values */ yytestcase(yyruleno==358); -- /* (359) sclp ::= selcollist COMMA */ yytestcase(yyruleno==359); -- /* (360) as ::= ID|STRING */ yytestcase(yyruleno==360); -- /* (361) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=361); -- /* (362) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==362); -- /* (363) exprlist ::= nexprlist */ yytestcase(yyruleno==363); -- /* (364) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); -- /* (365) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=365); -- /* (366) nmnum ::= ON */ yytestcase(yyruleno==366); -- /* (367) nmnum ::= DELETE */ yytestcase(yyruleno==367); -- /* (368) nmnum ::= DEFAULT */ yytestcase(yyruleno==368); -- /* (369) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==369); -- /* (370) foreach_clause ::= */ yytestcase(yyruleno==370); -- /* (371) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==371); -- /* (372) trnm ::= nm */ yytestcase(yyruleno==372); -- /* (373) tridxby ::= */ yytestcase(yyruleno==373); -- /* (374) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==374); -- /* (375) database_kw_opt ::= */ yytestcase(yyruleno==375); -- /* (376) kwcolumn_opt ::= */ yytestcase(yyruleno==376); -- /* (377) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==377); -- /* (378) vtabarglist ::= vtabarg */ yytestcase(yyruleno==378); -- /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==379); -- /* (380) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==380); -- /* (381) anylist ::= */ yytestcase(yyruleno==381); -- /* (382) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==382); -- /* (383) anylist ::= anylist ANY */ yytestcase(yyruleno==383); -- /* (384) with ::= */ yytestcase(yyruleno==384); -+ /* (344) input ::= cmdlist */ yytestcase(yyruleno==344); -+ /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345); -+ /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346); -+ /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347); -+ /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348); -+ /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349); -+ /* (350) trans_opt ::= */ yytestcase(yyruleno==350); -+ /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351); -+ /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352); -+ /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353); -+ /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354); -+ /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355); -+ /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356); -+ /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357); -+ /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358); -+ /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359); -+ /* (360) nm ::= STRING */ yytestcase(yyruleno==360); -+ /* (361) typetoken ::= typename */ yytestcase(yyruleno==361); -+ /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362); -+ /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); -+ /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); -+ /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365); -+ /* (366) carglist ::= */ yytestcase(yyruleno==366); -+ /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367); -+ /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368); -+ /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369); -+ /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370); -+ /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371); -+ /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372); -+ /* (373) tconscomma ::= */ yytestcase(yyruleno==373); -+ /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374); -+ /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375); -+ /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376); -+ /* (377) oneselect ::= values */ yytestcase(yyruleno==377); -+ /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378); -+ /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379); -+ /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380); -+ /* (381) returning ::= */ yytestcase(yyruleno==381); -+ /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382); -+ /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383); -+ /* (384) case_operand ::= expr */ yytestcase(yyruleno==384); -+ /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385); -+ /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386); -+ /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387); -+ /* (388) nmnum ::= ON */ yytestcase(yyruleno==388); -+ /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389); -+ /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390); -+ /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391); -+ /* (392) foreach_clause ::= */ yytestcase(yyruleno==392); -+ /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393); -+ /* (394) trnm ::= nm */ yytestcase(yyruleno==394); -+ /* (395) tridxby ::= */ yytestcase(yyruleno==395); -+ /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396); -+ /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397); -+ /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398); -+ /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399); -+ /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400); -+ /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401); -+ /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402); -+ /* (403) anylist ::= */ yytestcase(yyruleno==403); -+ /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404); -+ /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405); -+ /* (406) with ::= */ yytestcase(yyruleno==406); -+ /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407); -+ /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408); - break; - /********** End reduce actions ************************************************/ - }; -@@ -159021,7 +180587,7 @@ static void yy_syntax_error( - - UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */ - if( TOKEN.z[0] ){ -- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); -+ parserSyntaxError(pParse, &TOKEN); - }else{ - sqlite3ErrorMsg(pParse, "incomplete input"); - } -@@ -159110,12 +180676,49 @@ SQLITE_PRIVATE void sqlite3Parser( - } - #endif - -- do{ -+ while(1){ /* Exit by "break" */ -+ assert( yypParser->yytos>=yypParser->yystack ); - assert( yyact==yypParser->yytos->stateno ); - yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); - if( yyact >= YY_MIN_REDUCE ){ -- yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor, -- yyminor sqlite3ParserCTX_PARAM); -+ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ -+#ifndef NDEBUG -+ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); -+ if( yyTraceFILE ){ -+ int yysize = yyRuleInfoNRhs[yyruleno]; -+ if( yysize ){ -+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", -+ yyTracePrompt, -+ yyruleno, yyRuleName[yyruleno], -+ yyrulenoyytos[yysize].stateno); -+ }else{ -+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", -+ yyTracePrompt, yyruleno, yyRuleName[yyruleno], -+ yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ -+ yypParser->yyhwm++; -+ assert( yypParser->yyhwm == -+ (int)(yypParser->yytos - yypParser->yystack)); -+ } -+#endif -+ if( yypParser->yytos>=yypParser->yystackEnd ){ -+ if( yyGrowStack(yypParser) ){ -+ yyStackOverflow(yypParser); -+ break; -+ } -+ } -+ } -+ yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM); - }else if( yyact <= YY_MAX_SHIFTREDUCE ){ - yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); - #ifndef YYNOERRORRECOVERY -@@ -159171,14 +180774,13 @@ SQLITE_PRIVATE void sqlite3Parser( - yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); - yymajor = YYNOCODE; - }else{ -- while( yypParser->yytos >= yypParser->yystack -- && (yyact = yy_find_reduce_action( -- yypParser->yytos->stateno, -- YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE -- ){ -+ while( yypParser->yytos > yypParser->yystack ){ -+ yyact = yy_find_reduce_action(yypParser->yytos->stateno, -+ YYERRORSYMBOL); -+ if( yyact<=YY_MAX_SHIFTREDUCE ) break; - yy_pop_parser_stack(yypParser); - } -- if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ -+ if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ - yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); - yy_parse_failed(yypParser); - #ifndef YYNOERRORRECOVERY -@@ -159228,7 +180830,7 @@ SQLITE_PRIVATE void sqlite3Parser( - break; - #endif - } -- }while( yypParser->yytos>yypParser->yystack ); -+ } - #ifndef NDEBUG - if( yyTraceFILE ){ - yyStackEntry *i; -@@ -159289,8 +180891,8 @@ SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){ - ** all of them need to be used within the switch. - */ - #define CC_X 0 /* The letter 'x', or start of BLOB literal */ --#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */ --#define CC_ID 2 /* unicode characters usable in IDs */ -+#define CC_KYWD0 1 /* First letter of a keyword */ -+#define CC_KYWD 2 /* Alphabetics or '_'. Usable in a keyword */ - #define CC_DIGIT 3 /* Digits */ - #define CC_DOLLAR 4 /* '$' */ - #define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */ -@@ -159315,47 +180917,49 @@ SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){ - #define CC_AND 24 /* '&' */ - #define CC_TILDA 25 /* '~' */ - #define CC_DOT 26 /* '.' */ --#define CC_ILLEGAL 27 /* Illegal character */ --#define CC_NUL 28 /* 0x00 */ -+#define CC_ID 27 /* unicode characters usable in IDs */ -+#define CC_ILLEGAL 28 /* Illegal character */ -+#define CC_NUL 29 /* 0x00 */ -+#define CC_BOM 30 /* First byte of UTF8 BOM: 0xEF 0xBB 0xBF */ - - static const unsigned char aiClass[] = { - #ifdef SQLITE_ASCII - /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ --/* 0x */ 28, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27, --/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -+/* 0x */ 29, 28, 28, 28, 28, 28, 28, 28, 28, 7, 7, 28, 7, 7, 28, 28, -+/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - /* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16, - /* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6, - /* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, --/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1, -+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 9, 28, 28, 28, 2, - /* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, --/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27, --/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, --/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, --/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, --/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, --/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, --/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, --/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, --/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 -+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 28, 10, 28, 25, 28, -+/* 8x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -+/* 9x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -+/* Ax */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -+/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -+/* Cx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -+/* Dx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -+/* Ex */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 30, -+/* Fx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27 - #endif - #ifdef SQLITE_EBCDIC - /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ --/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27, --/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, --/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, --/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, --/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 12, 17, 20, 10, --/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27, --/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 6, --/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8, --/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, --/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, --/* Ax */ 27, 25, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, --/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27, --/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, --/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27, --/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27, --/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27, -+/* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28, -+/* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -+/* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -+/* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -+/* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10, -+/* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28, -+/* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6, -+/* 7x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 8, 5, 5, 5, 8, 14, 8, -+/* 8x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, -+/* 9x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, -+/* Ax */ 28, 25, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28, -+/* Bx */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 9, 28, 28, 28, 28, 28, -+/* Cx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, -+/* Dx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28, -+/* Ex */ 28, 28, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28, -+/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 28, 28, 28, 28, 28, 28, - #endif - }; - -@@ -159420,20 +181024,21 @@ const unsigned char ebcdicToAscii[] = { - ** is substantially reduced. This is important for embedded applications - ** on platforms with limited memory. - */ --/* Hash score: 227 */ --/* zKWText[] encodes 984 bytes of keyword text in 648 bytes */ -+/* Hash score: 231 */ -+/* zKWText[] encodes 1007 bytes of keyword text in 667 bytes */ - /* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */ - /* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYISNULLSAVEPOINTERSECT */ - /* IESNOTNULLIKEXCEPTRANSACTIONATURALTERAISEXCLUSIVEXISTS */ - /* CONSTRAINTOFFSETRIGGERANGENERATEDETACHAVINGLOBEGINNEREFERENCES */ - /* UNIQUERYWITHOUTERELEASEATTACHBETWEENOTHINGROUPSCASCADEFAULT */ - /* CASECOLLATECREATECURRENT_DATEIMMEDIATEJOINSERTMATCHPLANALYZE */ --/* PRAGMABORTUPDATEVALUESVIRTUALWAYSWHENWHERECURSIVEAFTERENAMEAND */ --/* EFERREDISTINCTAUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSS */ --/* CURRENT_TIMESTAMPARTITIONDROPRECEDINGFAILASTFILTEREPLACEFIRST */ --/* FOLLOWINGFROMFULLIMITIFORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */ --/* UNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBYINITIALLYPRIMARY */ --static const char zKWText[647] = { -+/* PRAGMATERIALIZEDEFERREDISTINCTUPDATEVALUESVIRTUALWAYSWHENWHERE */ -+/* CURSIVEABORTAFTERENAMEANDROPARTITIONAUTOINCREMENTCASTCOLUMN */ -+/* COMMITCONFLICTCROSSCURRENT_TIMESTAMPRECEDINGFAILASTFILTER */ -+/* EPLACEFIRSTFOLLOWINGFROMFULLIMITIFORDERESTRICTOTHERSOVER */ -+/* ETURNINGRIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBY */ -+/* INITIALLYPRIMARY */ -+static const char zKWText[666] = { - 'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H', - 'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G', - 'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A', -@@ -159454,86 +181059,87 @@ static const char zKWText[647] = { - 'C','R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E', - 'I','M','M','E','D','I','A','T','E','J','O','I','N','S','E','R','T','M', - 'A','T','C','H','P','L','A','N','A','L','Y','Z','E','P','R','A','G','M', -- 'A','B','O','R','T','U','P','D','A','T','E','V','A','L','U','E','S','V', -- 'I','R','T','U','A','L','W','A','Y','S','W','H','E','N','W','H','E','R', -- 'E','C','U','R','S','I','V','E','A','F','T','E','R','E','N','A','M','E', -- 'A','N','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','A', -- 'U','T','O','I','N','C','R','E','M','E','N','T','C','A','S','T','C','O', -- 'L','U','M','N','C','O','M','M','I','T','C','O','N','F','L','I','C','T', -- 'C','R','O','S','S','C','U','R','R','E','N','T','_','T','I','M','E','S', -- 'T','A','M','P','A','R','T','I','T','I','O','N','D','R','O','P','R','E', -- 'C','E','D','I','N','G','F','A','I','L','A','S','T','F','I','L','T','E', -- 'R','E','P','L','A','C','E','F','I','R','S','T','F','O','L','L','O','W', -- 'I','N','G','F','R','O','M','F','U','L','L','I','M','I','T','I','F','O', -- 'R','D','E','R','E','S','T','R','I','C','T','O','T','H','E','R','S','O', -- 'V','E','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O','W', -- 'S','U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S','I', -- 'N','G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W','B', -- 'Y','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y', -+ 'A','T','E','R','I','A','L','I','Z','E','D','E','F','E','R','R','E','D', -+ 'I','S','T','I','N','C','T','U','P','D','A','T','E','V','A','L','U','E', -+ 'S','V','I','R','T','U','A','L','W','A','Y','S','W','H','E','N','W','H', -+ 'E','R','E','C','U','R','S','I','V','E','A','B','O','R','T','A','F','T', -+ 'E','R','E','N','A','M','E','A','N','D','R','O','P','A','R','T','I','T', -+ 'I','O','N','A','U','T','O','I','N','C','R','E','M','E','N','T','C','A', -+ 'S','T','C','O','L','U','M','N','C','O','M','M','I','T','C','O','N','F', -+ 'L','I','C','T','C','R','O','S','S','C','U','R','R','E','N','T','_','T', -+ 'I','M','E','S','T','A','M','P','R','E','C','E','D','I','N','G','F','A', -+ 'I','L','A','S','T','F','I','L','T','E','R','E','P','L','A','C','E','F', -+ 'I','R','S','T','F','O','L','L','O','W','I','N','G','F','R','O','M','F', -+ 'U','L','L','I','M','I','T','I','F','O','R','D','E','R','E','S','T','R', -+ 'I','C','T','O','T','H','E','R','S','O','V','E','R','E','T','U','R','N', -+ 'I','N','G','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O', -+ 'W','S','U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S', -+ 'I','N','G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W', -+ 'B','Y','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y', - }; - /* aKWHash[i] is the hash value for the i-th keyword */ - static const unsigned char aKWHash[127] = { -- 84, 102, 132, 82, 114, 29, 0, 0, 91, 0, 85, 72, 0, -- 53, 35, 86, 15, 0, 42, 94, 54, 126, 133, 19, 0, 0, -- 138, 0, 40, 128, 0, 22, 104, 0, 9, 0, 0, 122, 80, -- 0, 78, 6, 0, 65, 99, 145, 0, 134, 112, 0, 0, 48, -- 0, 100, 24, 0, 17, 0, 27, 70, 23, 26, 5, 60, 140, -- 107, 121, 0, 73, 101, 71, 143, 61, 119, 74, 0, 49, 0, -- 11, 41, 0, 110, 0, 0, 0, 106, 10, 108, 113, 124, 14, -- 50, 123, 0, 89, 0, 18, 120, 142, 56, 129, 137, 88, 83, -- 37, 30, 125, 0, 0, 105, 51, 130, 127, 0, 34, 0, 0, -- 44, 0, 95, 38, 39, 0, 20, 45, 116, 90, -+ 84, 92, 134, 82, 105, 29, 0, 0, 94, 0, 85, 72, 0, -+ 53, 35, 86, 15, 0, 42, 97, 54, 89, 135, 19, 0, 0, -+ 140, 0, 40, 129, 0, 22, 107, 0, 9, 0, 0, 123, 80, -+ 0, 78, 6, 0, 65, 103, 147, 0, 136, 115, 0, 0, 48, -+ 0, 90, 24, 0, 17, 0, 27, 70, 23, 26, 5, 60, 142, -+ 110, 122, 0, 73, 91, 71, 145, 61, 120, 74, 0, 49, 0, -+ 11, 41, 0, 113, 0, 0, 0, 109, 10, 111, 116, 125, 14, -+ 50, 124, 0, 100, 0, 18, 121, 144, 56, 130, 139, 88, 83, -+ 37, 30, 126, 0, 0, 108, 51, 131, 128, 0, 34, 0, 0, -+ 132, 0, 98, 38, 39, 0, 20, 45, 117, 93, - }; - /* aKWNext[] forms the hash collision chain. If aKWHash[i]==0 - ** then the i-th keyword has no more hash collisions. Otherwise, - ** the next keyword with the same hash is aKWHash[i]-1. */ --static const unsigned char aKWNext[145] = { -- 0, 0, 0, 0, 4, 0, 43, 0, 0, 103, 111, 0, 0, -- 0, 2, 0, 0, 141, 0, 0, 0, 13, 0, 0, 0, 0, -- 139, 0, 0, 118, 52, 0, 0, 135, 12, 0, 0, 62, 0, -- 136, 0, 131, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0, -+static const unsigned char aKWNext[148] = {0, -+ 0, 0, 0, 0, 4, 0, 43, 0, 0, 106, 114, 0, 0, -+ 0, 2, 0, 0, 143, 0, 0, 0, 13, 0, 0, 0, 0, -+ 141, 0, 0, 119, 52, 0, 0, 137, 12, 0, 0, 62, 0, -+ 138, 0, 133, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0, - 0, 59, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 69, 0, 0, 0, 0, 0, 144, 3, 0, 58, 0, 1, -- 75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 64, 66, -- 63, 0, 0, 0, 0, 46, 0, 16, 0, 115, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 81, 97, 0, 8, 0, 109, -- 21, 7, 67, 0, 79, 93, 117, 0, 0, 68, 0, 0, 96, -- 0, 55, 0, 76, 0, 92, 32, 33, 57, 25, 0, 98, 0, -- 0, 87, -+ 0, 69, 0, 0, 0, 0, 0, 146, 3, 0, 58, 0, 1, -+ 75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 127, 0, 104, -+ 0, 64, 66, 63, 0, 0, 0, 0, 0, 46, 0, 16, 8, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 101, 0, -+ 112, 21, 7, 67, 0, 79, 96, 118, 0, 0, 68, 0, 0, -+ 99, 44, 0, 55, 0, 76, 0, 95, 32, 33, 57, 25, 0, -+ 102, 0, 0, 87, - }; - /* aKWLen[i] is the length (in bytes) of the i-th keyword */ --static const unsigned char aKWLen[145] = { -+static const unsigned char aKWLen[148] = {0, - 7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6, - 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7, - 6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4, - 4, 6, 11, 6, 2, 7, 5, 5, 9, 6, 10, 4, 6, - 2, 3, 7, 5, 9, 6, 6, 4, 5, 5, 10, 6, 5, - 7, 4, 5, 7, 6, 7, 7, 6, 5, 7, 3, 7, 4, -- 7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 5, 6, 6, -- 7, 6, 4, 5, 9, 5, 6, 3, 8, 8, 2, 13, 2, -- 2, 4, 6, 6, 8, 5, 17, 12, 7, 9, 4, 9, 4, -- 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6, 4, -- 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2, 2, 9, -- 3, 7, -+ 7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 12, 8, 8, -+ 2, 6, 6, 7, 6, 4, 5, 9, 5, 5, 6, 3, 4, -+ 9, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, 7, 9, -+ 4, 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6, -+ 4, 9, 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2, -+ 2, 9, 3, 7, - }; - /* aKWOffset[i] is the index into zKWText[] of the start of - ** the text for the i-th keyword. */ --static const unsigned short int aKWOffset[145] = { -+static const unsigned short int aKWOffset[148] = {0, - 0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33, - 36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81, - 86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126, - 129, 132, 137, 142, 146, 147, 152, 156, 160, 168, 174, 181, 184, - 184, 187, 189, 195, 198, 206, 211, 216, 219, 222, 226, 236, 239, - 244, 244, 248, 252, 259, 265, 271, 277, 277, 283, 284, 288, 295, -- 299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 360, 365, 371, -- 377, 382, 388, 392, 395, 404, 408, 414, 416, 423, 424, 431, 433, -- 435, 444, 448, 454, 460, 468, 473, 473, 473, 489, 498, 501, 510, -- 513, 517, 522, 529, 534, 543, 547, 550, 555, 557, 561, 569, 575, -- 578, 583, 591, 591, 595, 604, 609, 614, 620, 623, 626, 629, 631, -- 636, 640, -+ 299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 359, 370, 377, -+ 378, 385, 391, 397, 402, 408, 412, 415, 424, 429, 433, 439, 441, -+ 444, 453, 455, 457, 466, 470, 476, 482, 490, 495, 495, 495, 511, -+ 520, 523, 527, 532, 539, 544, 553, 557, 560, 565, 567, 571, 579, -+ 585, 588, 597, 602, 610, 610, 614, 623, 628, 633, 639, 642, 645, -+ 648, 650, 655, 659, - }; - /* aKWCode[i] is the parser symbol code for the i-th keyword */ --static const unsigned char aKWCode[145] = { -+static const unsigned char aKWCode[148] = {0, - TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE, - TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN, - TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD, -@@ -159551,18 +181157,19 @@ static const unsigned char aKWCode[145] = { - TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP, TK_CASCADE, - TK_ASC, TK_DEFAULT, TK_CASE, TK_COLLATE, TK_CREATE, - TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_MATCH, -- TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT, TK_UPDATE, -- TK_VALUES, TK_VIRTUAL, TK_ALWAYS, TK_WHEN, TK_WHERE, -- TK_RECURSIVE, TK_AFTER, TK_RENAME, TK_AND, TK_DEFERRED, -- TK_DISTINCT, TK_IS, TK_AUTOINCR, TK_TO, TK_IN, -- TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, -- TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT, TK_PARTITION, TK_DROP, -- TK_PRECEDING, TK_FAIL, TK_LAST, TK_FILTER, TK_REPLACE, -- TK_FIRST, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_LIMIT, -- TK_IF, TK_ORDER, TK_RESTRICT, TK_OTHERS, TK_OVER, -- TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, TK_ROW, TK_UNBOUNDED, -- TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_WINDOW, -- TK_DO, TK_BY, TK_INITIALLY, TK_ALL, TK_PRIMARY, -+ TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_MATERIALIZED, TK_DEFERRED, -+ TK_DISTINCT, TK_IS, TK_UPDATE, TK_VALUES, TK_VIRTUAL, -+ TK_ALWAYS, TK_WHEN, TK_WHERE, TK_RECURSIVE, TK_ABORT, -+ TK_AFTER, TK_RENAME, TK_AND, TK_DROP, TK_PARTITION, -+ TK_AUTOINCR, TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, -+ TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, -+ TK_CURRENT, TK_PRECEDING, TK_FAIL, TK_LAST, TK_FILTER, -+ TK_REPLACE, TK_FIRST, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, -+ TK_LIMIT, TK_IF, TK_ORDER, TK_RESTRICT, TK_OTHERS, -+ TK_OVER, TK_RETURNING, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, -+ TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM, -+ TK_VIEW, TK_WINDOW, TK_DO, TK_BY, TK_INITIALLY, -+ TK_ALL, TK_PRIMARY, - }; - /* Hash table decoded: - ** 0: INSERT -@@ -159586,7 +181193,7 @@ static const unsigned char aKWCode[145] = { - ** 18: TRANSACTION RIGHT - ** 19: WHEN - ** 20: SET HAVING --** 21: IF -+** 21: MATERIALIZED IF - ** 22: ROWS - ** 23: SELECT - ** 24: -@@ -159682,7 +181289,7 @@ static const unsigned char aKWCode[145] = { - ** 114: INTERSECT UNBOUNDED - ** 115: - ** 116: --** 117: ON -+** 117: RETURNING ON - ** 118: - ** 119: WHERE - ** 120: NO INNER -@@ -159699,183 +181306,185 @@ static const unsigned char aKWCode[145] = { - static int keywordCode(const char *z, int n, int *pType){ - int i, j; - const char *zKW; -- if( n>=2 ){ -- i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127; -- for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){ -- if( aKWLen[i]!=n ) continue; -- zKW = &zKWText[aKWOffset[i]]; -+ assert( n>=2 ); -+ i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; -+ for(i=(int)aKWHash[i]; i>0; i=aKWNext[i]){ -+ if( aKWLen[i]!=n ) continue; -+ zKW = &zKWText[aKWOffset[i]]; - #ifdef SQLITE_ASCII -- if( (z[0]&~0x20)!=zKW[0] ) continue; -- if( (z[1]&~0x20)!=zKW[1] ) continue; -- j = 2; -- while( j=2 ) keywordCode((char*)z, n, &id); - return id; - } --#define SQLITE_N_KEYWORD 145 -+#define SQLITE_N_KEYWORD 147 - SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){ - if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR; -+ i++; - *pzName = zKWText + aKWOffset[i]; - *pnName = aKWLen[i]; - return SQLITE_OK; -@@ -159940,7 +181549,7 @@ static int getToken(const unsigned char **pz){ - int t; /* Token type to return */ - do { - z += sqlite3GetToken(z, &t); -- }while( t==TK_SPACE ); -+ }while( t==TK_SPACE || t==TK_COMMENT ); - if( t==TK_ID - || t==TK_STRING - || t==TK_JOIN_KW -@@ -160029,8 +181638,11 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ - case CC_MINUS: { - if( z[1]=='-' ){ - for(i=2; (c=z[i])!=0 && c!='\n'; i++){} -- *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ -+ *tokenType = TK_COMMENT; - return i; -+ }else if( z[1]=='>' ){ -+ *tokenType = TK_PTR; -+ return 2 + (z[2]=='>'); - } - *tokenType = TK_MINUS; - return 1; -@@ -160062,7 +181674,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ - } - for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} - if( c ) i++; -- *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ -+ *tokenType = TK_COMMENT; - return i; - } - case CC_PERCENT: { -@@ -160171,31 +181783,62 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ - testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); - testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); - testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); -- testcase( z[0]=='9' ); -+ testcase( z[0]=='9' ); testcase( z[0]=='.' ); - *tokenType = TK_INTEGER; - #ifndef SQLITE_OMIT_HEX_INTEGER - if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ -- for(i=3; sqlite3Isxdigit(z[i]); i++){} -- return i; -- } -+ for(i=3; 1; i++){ -+ if( sqlite3Isxdigit(z[i])==0 ){ -+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ -+ *tokenType = TK_QNUMBER; -+ }else{ -+ break; -+ } -+ } -+ } -+ }else - #endif -- for(i=0; sqlite3Isdigit(z[i]); i++){} -+ { -+ for(i=0; 1; i++){ -+ if( sqlite3Isdigit(z[i])==0 ){ -+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ -+ *tokenType = TK_QNUMBER; -+ }else{ -+ break; -+ } -+ } -+ } - #ifndef SQLITE_OMIT_FLOATING_POINT -- if( z[i]=='.' ){ -- i++; -- while( sqlite3Isdigit(z[i]) ){ i++; } -- *tokenType = TK_FLOAT; -- } -- if( (z[i]=='e' || z[i]=='E') && -- ( sqlite3Isdigit(z[i+1]) -- || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) -- ) -- ){ -- i += 2; -- while( sqlite3Isdigit(z[i]) ){ i++; } -- *tokenType = TK_FLOAT; -- } -+ if( z[i]=='.' ){ -+ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; -+ for(i++; 1; i++){ -+ if( sqlite3Isdigit(z[i])==0 ){ -+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ -+ *tokenType = TK_QNUMBER; -+ }else{ -+ break; -+ } -+ } -+ } -+ } -+ if( (z[i]=='e' || z[i]=='E') && -+ ( sqlite3Isdigit(z[i+1]) -+ || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) -+ ) -+ ){ -+ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; -+ for(i+=2; 1; i++){ -+ if( sqlite3Isdigit(z[i])==0 ){ -+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){ -+ *tokenType = TK_QNUMBER; -+ }else{ -+ break; -+ } -+ } -+ } -+ } - #endif -+ } - while( IdChar(z[i]) ){ - *tokenType = TK_ILLEGAL; - i++; -@@ -160242,8 +181885,9 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ - if( n==0 ) *tokenType = TK_ILLEGAL; - return i; - } -- case CC_KYWD: { -- for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} -+ case CC_KYWD0: { -+ if( aiClass[z[1]]>CC_KYWD ){ i = 1; break; } -+ for(i=2; aiClass[z[i]]<=CC_KYWD; i++){} - if( IdChar(z[i]) ){ - /* This token started out using characters that can appear in keywords, - ** but z[i] is a character not allowed within keywords, so this must -@@ -160272,10 +181916,19 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ - ** SQL keywords start with the letter 'x'. Fall through */ - /* no break */ deliberate_fall_through - } -+ case CC_KYWD: - case CC_ID: { - i = 1; - break; - } -+ case CC_BOM: { -+ if( z[1]==0xbb && z[2]==0xbf ){ -+ *tokenType = TK_SPACE; -+ return 3; -+ } -+ i = 1; -+ break; -+ } - case CC_NUL: { - *tokenType = TK_ILLEGAL; - return 0; -@@ -160291,13 +181944,9 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ - } - - /* --** Run the parser on the given SQL string. The parser structure is --** passed in. An SQLITE_ status code is returned. If an error occurs --** then an and attempt is made to write an error message into --** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that --** error message. -+** Run the parser on the given SQL string. - */ --SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ -+SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ - int nErr = 0; /* Number of errors encountered */ - void *pEngine; /* The LEMON-generated LALR(1) parser */ - int n = 0; /* Length of the next token token */ -@@ -160305,6 +181954,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr - int lastTokenParsed = -1; /* type of the previous token */ - sqlite3 *db = pParse->db; /* The database connection */ - int mxSqlLen; /* Max length of an SQL string */ -+ Parse *pParentParse = 0; /* Outer parse context, if any */ - #ifdef sqlite3Parser_ENGINEALWAYSONSTACK - yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ - #endif -@@ -160317,7 +181967,6 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr - } - pParse->rc = SQLITE_OK; - pParse->zTail = zSql; -- assert( pzErrMsg!=0 ); - #ifdef SQLITE_DEBUG - if( db->flags & SQLITE_ParserTrace ){ - printf("parser: [[[%s]]]\n", zSql); -@@ -160340,26 +181989,31 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr - assert( pParse->pNewTrigger==0 ); - assert( pParse->nVar==0 ); - assert( pParse->pVList==0 ); -- pParse->pParentParse = db->pParse; -+ pParentParse = db->pParse; - db->pParse = pParse; - while( 1 ){ - n = sqlite3GetToken((u8*)zSql, &tokenType); - mxSqlLen -= n; - if( mxSqlLen<0 ){ - pParse->rc = SQLITE_TOOBIG; -+ pParse->nErr++; - break; - } - #ifndef SQLITE_OMIT_WINDOWFUNC - if( tokenType>=TK_WINDOW ){ - assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER - || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW -+ || tokenType==TK_QNUMBER || tokenType==TK_COMMENT - ); - #else - if( tokenType>=TK_SPACE ){ -- assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); -+ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL -+ || tokenType==TK_QNUMBER || tokenType==TK_COMMENT -+ ); - #endif /* SQLITE_OMIT_WINDOWFUNC */ - if( AtomicLoad(&db->u1.isInterrupted) ){ - pParse->rc = SQLITE_INTERRUPT; -+ pParse->nErr++; - break; - } - if( tokenType==TK_SPACE ){ -@@ -160388,8 +182042,18 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr - assert( n==6 ); - tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); - #endif /* SQLITE_OMIT_WINDOWFUNC */ -- }else{ -- sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql); -+ }else if( tokenType==TK_COMMENT -+ && (db->init.busy || (db->flags & SQLITE_Comments)!=0) -+ ){ -+ /* Ignore SQL comments if either (1) we are reparsing the schema or -+ ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */ -+ zSql += n; -+ continue; -+ }else if( tokenType!=TK_QNUMBER ){ -+ Token x; -+ x.z = zSql; -+ x.n = n; -+ sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x); - break; - } - } -@@ -160417,58 +182081,32 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr - if( db->mallocFailed ){ - pParse->rc = SQLITE_NOMEM_BKPT; - } -- if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ -- pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); -- } -- assert( pzErrMsg!=0 ); -- if( pParse->zErrMsg ){ -- *pzErrMsg = pParse->zErrMsg; -- sqlite3_log(pParse->rc, "%s in \"%s\"", -- *pzErrMsg, pParse->zTail); -- pParse->zErrMsg = 0; -+ if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ -+ if( pParse->zErrMsg==0 ){ -+ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); -+ } -+ if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){ -+ sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); -+ } - nErr++; - } - pParse->zTail = zSql; -- if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){ -- sqlite3VdbeDelete(pParse->pVdbe); -- pParse->pVdbe = 0; -- } --#ifndef SQLITE_OMIT_SHARED_CACHE -- if( pParse->nested==0 ){ -- sqlite3DbFree(db, pParse->aTableLock); -- pParse->aTableLock = 0; -- pParse->nTableLock = 0; -- } --#endif - #ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3_free(pParse->apVtabLock); - #endif - -- if( !IN_SPECIAL_PARSE ){ -+ if( pParse->pNewTable && !IN_SPECIAL_PARSE ){ - /* If the pParse->declareVtab flag is set, do not delete any table - ** structure built up in pParse->pNewTable. The calling code (see vtab.c) - ** will take responsibility for freeing the Table structure. - */ - sqlite3DeleteTable(db, pParse->pNewTable); - } -- if( !IN_RENAME_OBJECT ){ -+ if( pParse->pNewTrigger && !IN_RENAME_OBJECT ){ - sqlite3DeleteTrigger(db, pParse->pNewTrigger); - } -- -- if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree); -- sqlite3DbFree(db, pParse->pVList); -- while( pParse->pAinc ){ -- AutoincInfo *p = pParse->pAinc; -- pParse->pAinc = p->pNext; -- sqlite3DbFreeNN(db, p); -- } -- while( pParse->pZombieTab ){ -- Table *p = pParse->pZombieTab; -- pParse->pZombieTab = p->pNextZombie; -- sqlite3DeleteTable(db, p); -- } -- db->pParse = pParse->pParentParse; -- pParse->pParentParse = 0; -+ if( pParse->pVList ) sqlite3DbNNFreeNN(db, pParse->pVList); -+ db->pParse = pParentParse; - assert( nErr==0 || pParse->rc!=SQLITE_OK ); - return nErr; - } -@@ -160517,6 +182155,7 @@ SQLITE_PRIVATE char *sqlite3Normalize( - n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); - if( NEVER(n<=0) ) break; - switch( tokenType ){ -+ case TK_COMMENT: - case TK_SPACE: { - break; - } -@@ -161040,33 +182679,20 @@ static int sqlite3TestExtInit(sqlite3 *db){ - ** Forward declarations of external module initializer functions - ** for modules that need them. - */ --#ifdef SQLITE_ENABLE_FTS1 --SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*); --#endif --#ifdef SQLITE_ENABLE_FTS2 --SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*); --#endif - #ifdef SQLITE_ENABLE_FTS5 - SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); - #endif --#ifdef SQLITE_ENABLE_JSON1 --SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*); --#endif - #ifdef SQLITE_ENABLE_STMTVTAB - SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*); - #endif -- -+#ifdef SQLITE_EXTRA_AUTOEXT -+int SQLITE_EXTRA_AUTOEXT(sqlite3*); -+#endif - /* - ** An array of pointers to extension initializer functions for - ** built-in extensions. - */ - static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { --#ifdef SQLITE_ENABLE_FTS1 -- sqlite3Fts1Init, --#endif --#ifdef SQLITE_ENABLE_FTS2 -- sqlite3Fts2Init, --#endif - #ifdef SQLITE_ENABLE_FTS3 - sqlite3Fts3Init, - #endif -@@ -161086,8 +182712,8 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { - sqlite3DbstatRegister, - #endif - sqlite3TestExtInit, --#ifdef SQLITE_ENABLE_JSON1 -- sqlite3Json1Init, -+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) -+ sqlite3JsonTableFunctions, - #endif - #ifdef SQLITE_ENABLE_STMTVTAB - sqlite3StmtVtabInit, -@@ -161095,6 +182721,9 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { - #ifdef SQLITE_ENABLE_BYTECODE_VTAB - sqlite3VdbeBytecodeVtabInit, - #endif -+#ifdef SQLITE_EXTRA_AUTOEXT -+ SQLITE_EXTRA_AUTOEXT, -+#endif - }; - - #ifndef SQLITE_AMALGAMATION -@@ -161304,7 +182933,7 @@ SQLITE_API int sqlite3_initialize(void){ - sqlite3GlobalConfig.isPCacheInit = 1; - rc = sqlite3OsInit(); - } --#ifdef SQLITE_ENABLE_DESERIALIZE -+#ifndef SQLITE_OMIT_DESERIALIZE - if( rc==SQLITE_OK ){ - rc = sqlite3MemdbInit(); - } -@@ -161312,6 +182941,14 @@ SQLITE_API int sqlite3_initialize(void){ - if( rc==SQLITE_OK ){ - sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, - sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); -+#ifdef SQLITE_EXTRA_INIT_MUTEXED -+ { -+ int SQLITE_EXTRA_INIT_MUTEXED(const char*); -+ rc = SQLITE_EXTRA_INIT_MUTEXED(0); -+ } -+#endif -+ } -+ if( rc==SQLITE_OK ){ - sqlite3MemoryBarrier(); - sqlite3GlobalConfig.isInit = 1; - #ifdef SQLITE_EXTRA_INIT -@@ -161362,7 +182999,6 @@ SQLITE_API int sqlite3_initialize(void){ - rc = SQLITE_EXTRA_INIT(0); - } - #endif -- - return rc; - } - -@@ -161432,9 +183068,21 @@ SQLITE_API int sqlite3_config(int op, ...){ - va_list ap; - int rc = SQLITE_OK; - -- /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while -- ** the SQLite library is in use. */ -- if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT; -+ /* sqlite3_config() normally returns SQLITE_MISUSE if it is invoked while -+ ** the SQLite library is in use. Except, a few selected opcodes -+ ** are allowed. -+ */ -+ if( sqlite3GlobalConfig.isInit ){ -+ static const u64 mAnytimeConfigOption = 0 -+ | MASKBIT64( SQLITE_CONFIG_LOG ) -+ | MASKBIT64( SQLITE_CONFIG_PCACHE_HDRSZ ) -+ ; -+ if( op<0 || op>63 || (MASKBIT64(op) & mAnytimeConfigOption)==0 ){ -+ return SQLITE_MISUSE_BKPT; -+ } -+ testcase( op==SQLITE_CONFIG_LOG ); -+ testcase( op==SQLITE_CONFIG_PCACHE_HDRSZ ); -+ } - - va_start(ap, op); - switch( op ){ -@@ -161503,6 +183151,7 @@ SQLITE_API int sqlite3_config(int op, ...){ - break; - } - case SQLITE_CONFIG_MEMSTATUS: { -+ assert( !sqlite3GlobalConfig.isInit ); /* Cannot change at runtime */ - /* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes - ** single argument of type int, interpreted as a boolean, which enables - ** or disables the collection of memory allocation statistics. */ -@@ -161626,8 +183275,10 @@ SQLITE_API int sqlite3_config(int op, ...){ - ** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*)); - */ - typedef void(*LOGFUNC_t)(void*,int,const char*); -- sqlite3GlobalConfig.xLog = va_arg(ap, LOGFUNC_t); -- sqlite3GlobalConfig.pLogArg = va_arg(ap, void*); -+ LOGFUNC_t xLog = va_arg(ap, LOGFUNC_t); -+ void *pLogArg = va_arg(ap, void*); -+ AtomicStore(&sqlite3GlobalConfig.xLog, xLog); -+ AtomicStore(&sqlite3GlobalConfig.pLogArg, pLogArg); - break; - } - -@@ -161641,7 +183292,8 @@ SQLITE_API int sqlite3_config(int op, ...){ - ** argument of type int. If non-zero, then URI handling is globally - ** enabled. If the parameter is zero, then URI handling is globally - ** disabled. */ -- sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); -+ int bOpenUri = va_arg(ap, int); -+ AtomicStore(&sqlite3GlobalConfig.bOpenUri, bOpenUri); - break; - } - -@@ -161719,12 +183371,24 @@ SQLITE_API int sqlite3_config(int op, ...){ - } - #endif /* SQLITE_ENABLE_SORTER_REFERENCES */ - --#ifdef SQLITE_ENABLE_DESERIALIZE -+#ifndef SQLITE_OMIT_DESERIALIZE - case SQLITE_CONFIG_MEMDB_MAXSIZE: { - sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlite3_int64); - break; - } --#endif /* SQLITE_ENABLE_DESERIALIZE */ -+#endif /* SQLITE_OMIT_DESERIALIZE */ -+ -+ case SQLITE_CONFIG_ROWID_IN_VIEW: { -+ int *pVal = va_arg(ap,int*); -+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW -+ if( 0==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid; -+ if( 1==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = 0; -+ *pVal = (sqlite3GlobalConfig.mNoVisibleRowid==0); -+#else -+ *pVal = 0; -+#endif -+ break; -+ } - - default: { - rc = SQLITE_ERROR; -@@ -161741,17 +183405,22 @@ SQLITE_API int sqlite3_config(int op, ...){ - ** If lookaside is already active, return SQLITE_BUSY. - ** - ** The sz parameter is the number of bytes in each lookaside slot. --** The cnt parameter is the number of slots. If pStart is NULL the --** space for the lookaside memory is obtained from sqlite3_malloc(). --** If pStart is not NULL then it is sz*cnt bytes of memory to use for --** the lookaside memory. -+** The cnt parameter is the number of slots. If pBuf is NULL the -+** space for the lookaside memory is obtained from sqlite3_malloc() -+** or similar. If pBuf is not NULL then it is sz*cnt bytes of memory -+** to use for the lookaside memory. - */ --static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ -+static int setupLookaside( -+ sqlite3 *db, /* Database connection being configured */ -+ void *pBuf, /* Memory to use for lookaside. May be NULL */ -+ int sz, /* Desired size of each lookaside memory slot */ -+ int cnt /* Number of slots to allocate */ -+){ - #ifndef SQLITE_OMIT_LOOKASIDE -- void *pStart; -- sqlite3_int64 szAlloc = sz*(sqlite3_int64)cnt; -- int nBig; /* Number of full-size slots */ -- int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */ -+ void *pStart; /* Start of the lookaside buffer */ -+ sqlite3_int64 szAlloc; /* Total space set aside for lookaside memory */ -+ int nBig; /* Number of full-size slots */ -+ int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */ - - if( sqlite3LookasideUsed(db,0)>0 ){ - return SQLITE_BUSY; -@@ -161764,17 +183433,22 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ - sqlite3_free(db->lookaside.pStart); - } - /* The size of a lookaside slot after ROUNDDOWN8 needs to be larger -- ** than a pointer to be useful. -+ ** than a pointer and small enough to fit in a u16. - */ -- sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ -+ sz = ROUNDDOWN8(sz); - if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; -- if( cnt<0 ) cnt = 0; -- if( sz==0 || cnt==0 ){ -+ if( sz>65528 ) sz = 65528; -+ /* Count must be at least 1 to be useful, but not so large as to use -+ ** more than 0x7fff0000 total bytes for lookaside. */ -+ if( cnt<1 ) cnt = 0; -+ if( sz>0 && cnt>(0x7fff0000/sz) ) cnt = 0x7fff0000/sz; -+ szAlloc = (i64)sz*(i64)cnt; -+ if( szAlloc==0 ){ - sz = 0; - pStart = 0; - }else if( pBuf==0 ){ - sqlite3BeginBenignMalloc(); -- pStart = sqlite3Malloc( szAlloc ); /* IMP: R-61949-35727 */ -+ pStart = sqlite3Malloc( szAlloc ); - sqlite3EndBenignMalloc(); - if( pStart ) szAlloc = sqlite3MallocSize(pStart); - }else{ -@@ -161783,10 +183457,10 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ - #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - if( sz>=LOOKASIDE_SMALL*3 ){ - nBig = szAlloc/(3*LOOKASIDE_SMALL+sz); -- nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL; -+ nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL; - }else if( sz>=LOOKASIDE_SMALL*2 ){ - nBig = szAlloc/(LOOKASIDE_SMALL+sz); -- nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL; -+ nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL; - }else - #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ - if( sz>0 ){ -@@ -161826,18 +183500,19 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ - db->lookaside.bMalloced = pBuf==0 ?1:0; - db->lookaside.nSlot = nBig+nSm; - }else{ -- db->lookaside.pStart = db; -+ db->lookaside.pStart = 0; - #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - db->lookaside.pSmallInit = 0; - db->lookaside.pSmallFree = 0; -- db->lookaside.pMiddle = db; -+ db->lookaside.pMiddle = 0; - #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ -- db->lookaside.pEnd = db; -+ db->lookaside.pEnd = 0; - db->lookaside.bDisable = 1; - db->lookaside.sz = 0; - db->lookaside.bMalloced = 0; - db->lookaside.nSlot = 0; - } -+ db->lookaside.pTrueEnd = db->lookaside.pEnd; - assert( sqlite3LookasideUsed(db,0)==0 ); - #endif /* SQLITE_OMIT_LOOKASIDE */ - return SQLITE_OK; -@@ -161896,7 +183571,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){ - sqlite3BtreeEnterAll(db); - for(i=0; rc==SQLITE_OK && inDb; i++){ - Btree *pBt = db->aDb[i].pBt; -- if( pBt && sqlite3BtreeIsInTrans(pBt) ){ -+ if( pBt && sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){ - Pager *pPager = sqlite3BtreePager(pBt); - rc = sqlite3PagerFlush(pPager); - if( rc==SQLITE_BUSY ){ -@@ -161916,6 +183591,11 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){ - SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ - va_list ap; - int rc; -+ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; -+#endif -+ sqlite3_mutex_enter(db->mutex); - va_start(ap, op); - switch( op ){ - case SQLITE_DBCONFIG_MAINDBNAME: { -@@ -161935,7 +183615,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ - default: { - static const struct { - int op; /* The opcode */ -- u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ -+ u64 mask; /* Mask of the bit in sqlite3.flags to set/clear */ - } aFlagOp[] = { - { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, - { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, -@@ -161954,6 +183634,11 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ - { SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML }, - { SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, SQLITE_LegacyFileFmt }, - { SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema }, -+ { SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus }, -+ { SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder }, -+ { SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, SQLITE_AttachCreate }, -+ { SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, SQLITE_AttachWrite }, -+ { SQLITE_DBCONFIG_ENABLE_COMMENTS, SQLITE_Comments }, - }; - unsigned int i; - rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ -@@ -161981,6 +183666,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ - } - } - va_end(ap); -+ sqlite3_mutex_leave(db->mutex); - return rc; - } - -@@ -162085,7 +183771,7 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3 *db, sqlite3_int64 iRowid) - /* - ** Return the number of changes in the most recent call to sqlite3_exec(). - */ --SQLITE_API int sqlite3_changes(sqlite3 *db){ -+SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3 *db){ - #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ){ - (void)SQLITE_MISUSE_BKPT; -@@ -162094,11 +183780,14 @@ SQLITE_API int sqlite3_changes(sqlite3 *db){ - #endif - return db->nChange; - } -+SQLITE_API int sqlite3_changes(sqlite3 *db){ -+ return (int)sqlite3_changes64(db); -+} - - /* - ** Return the number of changes since the database handle was opened. - */ --SQLITE_API int sqlite3_total_changes(sqlite3 *db){ -+SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3 *db){ - #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ){ - (void)SQLITE_MISUSE_BKPT; -@@ -162107,6 +183796,9 @@ SQLITE_API int sqlite3_total_changes(sqlite3 *db){ - #endif - return db->nTotalChange; - } -+SQLITE_API int sqlite3_total_changes(sqlite3 *db){ -+ return (int)sqlite3_total_changes64(db); -+} - - /* - ** Close all open savepoints. This function only manipulates fields of the -@@ -162131,7 +183823,9 @@ SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *db){ - ** with SQLITE_ANY as the encoding. - */ - static void functionDestroy(sqlite3 *db, FuncDef *p){ -- FuncDestructor *pDestructor = p->u.pDestructor; -+ FuncDestructor *pDestructor; -+ assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 ); -+ pDestructor = p->u.pDestructor; - if( pDestructor ){ - pDestructor->nRef--; - if( pDestructor->nRef==0 ){ -@@ -162233,17 +183927,55 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){ - } - #endif - -+ while( db->pDbData ){ -+ DbClientData *p = db->pDbData; -+ db->pDbData = p->pNext; -+ assert( p->pData!=0 ); -+ if( p->xDestructor ) p->xDestructor(p->pData); -+ sqlite3_free(p); -+ } -+ - /* Convert the connection into a zombie and then close it. - */ -- db->magic = SQLITE_MAGIC_ZOMBIE; -+ db->eOpenState = SQLITE_STATE_ZOMBIE; - sqlite3LeaveMutexAndCloseZombie(db); - return SQLITE_OK; - } - -+/* -+** Return the transaction state for a single databse, or the maximum -+** transaction state over all attached databases if zSchema is null. -+*/ -+SQLITE_API int sqlite3_txn_state(sqlite3 *db, const char *zSchema){ -+ int iDb, nDb; -+ int iTxn = -1; -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) ){ -+ (void)SQLITE_MISUSE_BKPT; -+ return -1; -+ } -+#endif -+ sqlite3_mutex_enter(db->mutex); -+ if( zSchema ){ -+ nDb = iDb = sqlite3FindDbName(db, zSchema); -+ if( iDb<0 ) nDb--; -+ }else{ -+ iDb = 0; -+ nDb = db->nDb-1; -+ } -+ for(; iDb<=nDb; iDb++){ -+ Btree *pBt = db->aDb[iDb].pBt; -+ int x = pBt!=0 ? sqlite3BtreeTxnState(pBt) : SQLITE_TXN_NONE; -+ if( x>iTxn ) iTxn = x; -+ } -+ sqlite3_mutex_leave(db->mutex); -+ return iTxn; -+} -+ - /* - ** Two variations on the public interface for closing a database - ** connection. The sqlite3_close() version returns SQLITE_BUSY and --** leaves the connection option if there are unfinalized prepared -+** leaves the connection open if there are unfinalized prepared - ** statements or unfinished sqlite3_backups. The sqlite3_close_v2() - ** version forces the connection to become a zombie if there are - ** unclosed resources, and arranges for deallocation when the last -@@ -162269,7 +184001,7 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ - ** or if the connection has not yet been closed by sqlite3_close_v2(), - ** then just leave the mutex and return. - */ -- if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){ -+ if( db->eOpenState!=SQLITE_STATE_ZOMBIE || connectionIsBusy(db) ){ - sqlite3_mutex_leave(db->mutex); - return; - } -@@ -162350,12 +184082,8 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ - sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ - sqlite3ValueFree(db->pErr); - sqlite3CloseExtensions(db); --#if SQLITE_USER_AUTHENTICATION -- sqlite3_free(db->auth.zAuthUser); -- sqlite3_free(db->auth.zAuthPW); --#endif - -- db->magic = SQLITE_MAGIC_ERROR; -+ db->eOpenState = SQLITE_STATE_ERROR; - - /* The temp-database schema is allocated differently from the other schema - ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). -@@ -162364,8 +184092,11 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ - ** structure? - */ - sqlite3DbFree(db, db->aDb[1].pSchema); -+ if( db->xAutovacDestr ){ -+ db->xAutovacDestr(db->pAutovacPagesArg); -+ } - sqlite3_mutex_leave(db->mutex); -- db->magic = SQLITE_MAGIC_CLOSED; -+ db->eOpenState = SQLITE_STATE_CLOSED; - sqlite3_mutex_free(db->mutex); - assert( sqlite3LookasideUsed(db,0)==0 ); - if( db->lookaside.bMalloced ){ -@@ -162400,7 +184131,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ - for(i=0; inDb; i++){ - Btree *p = db->aDb[i].pBt; - if( p ){ -- if( sqlite3BtreeIsInTrans(p) ){ -+ if( sqlite3BtreeTxnState(p)==SQLITE_TXN_WRITE ){ - inTrans = 1; - } - sqlite3BtreeRollback(p, tripCode, !schemaChange); -@@ -162418,7 +184149,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ - /* Any deferred constraint violations have now been resolved. */ - db->nDeferredCons = 0; - db->nDeferredImmCons = 0; -- db->flags &= ~(u64)SQLITE_DeferFKs; -+ db->flags &= ~(u64)(SQLITE_DeferFKs|SQLITE_CorruptRdOnly); - - /* If one has been configured, invoke the rollback-hook callback */ - if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ -@@ -162524,6 +184255,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ - case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break; - case SQLITE_NOTICE_RECOVER_ROLLBACK: - zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break; -+ case SQLITE_NOTICE_RBU: zName = "SQLITE_NOTICE_RBU"; break; - case SQLITE_WARNING: zName = "SQLITE_WARNING"; break; - case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break; - case SQLITE_DONE: zName = "SQLITE_DONE"; break; -@@ -162616,9 +184348,9 @@ static int sqliteDefaultBusyCallback( - void *ptr, /* Database connection */ - int count /* Number of times table has been busy */ - ){ --#if SQLITE_OS_WIN || HAVE_USLEEP -+#if SQLITE_OS_WIN || !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP - /* This case is for systems that have support for sleeping for fractions of -- ** a second. Examples: All windows systems, unix systems with usleep() */ -+ ** a second. Examples: All windows systems, unix systems with nanosleep() */ - static const u8 delays[] = - { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; - static const u8 totals[] = -@@ -162693,6 +184425,9 @@ SQLITE_API int sqlite3_busy_handler( - db->busyHandler.pBusyArg = pArg; - db->busyHandler.nBusy = 0; - db->busyTimeout = 0; -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ db->setlkTimeout = 0; -+#endif - sqlite3_mutex_leave(db->mutex); - return SQLITE_OK; - } -@@ -162742,18 +184477,57 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ - sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, - (void*)db); - db->busyTimeout = ms; -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ db->setlkTimeout = ms; -+#endif - }else{ - sqlite3_busy_handler(db, 0, 0); - } - return SQLITE_OK; - } - -+/* -+** Set the setlk timeout value. -+*/ -+SQLITE_API int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){ -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ int iDb; -+ int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0); -+#endif -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; -+#endif -+ if( ms<-1 ) return SQLITE_RANGE; -+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -+ sqlite3_mutex_enter(db->mutex); -+ db->setlkTimeout = ms; -+ db->setlkFlags = flags; -+ sqlite3BtreeEnterAll(db); -+ for(iDb=0; iDbnDb; iDb++){ -+ Btree *pBt = db->aDb[iDb].pBt; -+ if( pBt ){ -+ sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); -+ sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); -+ } -+ } -+ sqlite3BtreeLeaveAll(db); -+ sqlite3_mutex_leave(db->mutex); -+#endif -+#if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT) -+ UNUSED_PARAMETER(db); -+ UNUSED_PARAMETER(flags); -+#endif -+ return SQLITE_OK; -+} -+ - /* - ** Cause any pending operation to stop at its earliest opportunity. - */ - SQLITE_API void sqlite3_interrupt(sqlite3 *db){ - #ifdef SQLITE_ENABLE_API_ARMOR -- if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){ -+ if( !sqlite3SafetyCheckOk(db) -+ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) -+ ){ - (void)SQLITE_MISUSE_BKPT; - return; - } -@@ -162761,6 +184535,21 @@ SQLITE_API void sqlite3_interrupt(sqlite3 *db){ - AtomicStore(&db->u1.isInterrupted, 1); - } - -+/* -+** Return true or false depending on whether or not an interrupt is -+** pending on connection db. -+*/ -+SQLITE_API int sqlite3_is_interrupted(sqlite3 *db){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) -+ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) -+ ){ -+ (void)SQLITE_MISUSE_BKPT; -+ return 0; -+ } -+#endif -+ return AtomicLoad(&db->u1.isInterrupted)!=0; -+} - - /* - ** This function is exactly the same as sqlite3_create_function(), except -@@ -162782,7 +184571,6 @@ SQLITE_PRIVATE int sqlite3CreateFunc( - FuncDestructor *pDestructor - ){ - FuncDef *p; -- int nName; - int extraFlags; - - assert( sqlite3_mutex_held(db->mutex) ); -@@ -162792,7 +184580,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc( - || ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */ - || ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */ - || (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) -- || (255<(nName = sqlite3Strlen30( zFunctionName))) -+ || (255nRef==0 ){ -- assert( rc!=SQLITE_OK ); -+ assert( rc!=SQLITE_OK || (xStep==0 && xFinal==0) ); - xDestroy(p); - sqlite3_free(pArg); - } -@@ -163062,7 +184866,7 @@ SQLITE_API int sqlite3_overload_function( - rc = sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0; - sqlite3_mutex_leave(db->mutex); - if( rc ) return SQLITE_OK; -- zCopy = sqlite3_mprintf(zName); -+ zCopy = sqlite3_mprintf("%s", zName); - if( zCopy==0 ) return SQLITE_NOMEM; - return sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8, - zCopy, sqlite3InvalidFunction, 0, 0, sqlite3_free); -@@ -163242,6 +185046,12 @@ SQLITE_API void *sqlite3_preupdate_hook( - void *pArg /* First callback argument */ - ){ - void *pRet; -+ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( db==0 ){ -+ return 0; -+ } -+#endif - sqlite3_mutex_enter(db->mutex); - pRet = db->pPreUpdateArg; - db->xPreUpdateCallback = xCallback; -@@ -163251,6 +185061,34 @@ SQLITE_API void *sqlite3_preupdate_hook( - } - #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ - -+/* -+** Register a function to be invoked prior to each autovacuum that -+** determines the number of pages to vacuum. -+*/ -+SQLITE_API int sqlite3_autovacuum_pages( -+ sqlite3 *db, /* Attach the hook to this database */ -+ unsigned int (*xCallback)(void*,const char*,u32,u32,u32), -+ void *pArg, /* Argument to the function */ -+ void (*xDestructor)(void*) /* Destructor for pArg */ -+){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) ){ -+ if( xDestructor ) xDestructor(pArg); -+ return SQLITE_MISUSE_BKPT; -+ } -+#endif -+ sqlite3_mutex_enter(db->mutex); -+ if( db->xAutovacDestr ){ -+ db->xAutovacDestr(db->pAutovacPagesArg); -+ } -+ db->xAutovacPages = xCallback; -+ db->pAutovacPagesArg = pArg; -+ db->xAutovacDestr = xDestructor; -+ sqlite3_mutex_leave(db->mutex); -+ return SQLITE_OK; -+} -+ -+ - #ifndef SQLITE_OMIT_WAL - /* - ** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint(). -@@ -163343,7 +185181,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( - return SQLITE_OK; - #else - int rc; /* Return code */ -- int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ -+ int iDb; /* Schema to checkpoint */ - - #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; -@@ -163360,12 +185198,14 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( - if( eModeSQLITE_CHECKPOINT_TRUNCATE ){ - /* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint - ** mode: */ -- return SQLITE_MISUSE; -+ return SQLITE_MISUSE_BKPT; - } - - sqlite3_mutex_enter(db->mutex); - if( zDb && zDb[0] ){ - iDb = sqlite3FindDbName(db, zDb); -+ }else{ -+ iDb = SQLITE_MAX_DB; /* This means process all schemas */ - } - if( iDb<0 ){ - rc = SQLITE_ERROR; -@@ -163414,7 +185254,7 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ - ** associated with the specific b-tree being checkpointed is taken by - ** this function while the checkpoint is running. - ** --** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are -+** If iDb is passed SQLITE_MAX_DB then all attached databases are - ** checkpointed. If an error is encountered it is returned immediately - - ** no attempt is made to checkpoint any remaining databases. - ** -@@ -163429,9 +185269,11 @@ SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog - assert( sqlite3_mutex_held(db->mutex) ); - assert( !pnLog || *pnLog==-1 ); - assert( !pnCkpt || *pnCkpt==-1 ); -+ testcase( iDb==SQLITE_MAX_ATTACHED ); /* See forum post a006d86f72 */ -+ testcase( iDb==SQLITE_MAX_DB ); - - for(i=0; inDb && rc==SQLITE_OK; i++){ -- if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ -+ if( i==iDb || iDb==SQLITE_MAX_DB ){ - rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); - pnLog = 0; - pnCkpt = 0; -@@ -163509,6 +185351,19 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ - return z; - } - -+/* -+** Return the byte offset of the most recent error -+*/ -+SQLITE_API int sqlite3_error_offset(sqlite3 *db){ -+ int iOffset = -1; -+ if( db && sqlite3SafetyCheckSickOrOk(db) && db->errCode ){ -+ sqlite3_mutex_enter(db->mutex); -+ iOffset = db->errByteOffset; -+ sqlite3_mutex_leave(db->mutex); -+ } -+ return iOffset; -+} -+ - #ifndef SQLITE_OMIT_UTF16 - /* - ** Return UTF-16 encoded English language explanation of the most recent -@@ -163701,8 +185556,8 @@ static const int aHardLimit[] = { - #if SQLITE_MAX_VDBE_OP<40 - # error SQLITE_MAX_VDBE_OP must be at least 40 - #endif --#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127 --# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127 -+#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767 -+# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767 - #endif - #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 - # error SQLITE_MAX_ATTACHED must be between 0 and 125 -@@ -163769,6 +185624,8 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ - if( newLimit>=0 ){ /* IMP: R-52476-28732 */ - if( newLimit>aHardLimit[limitId] ){ - newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ -+ }else if( newLimitaLimit[limitId] = newLimit; - } -@@ -163818,9 +185675,9 @@ SQLITE_PRIVATE int sqlite3ParseUri( - - assert( *pzErrMsg==0 ); - -- if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */ -- || sqlite3GlobalConfig.bOpenUri) /* IMP: R-51689-46548 */ -- && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */ -+ if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */ -+ || AtomicLoad(&sqlite3GlobalConfig.bOpenUri)) /* IMP: R-51689-46548 */ -+ && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */ - ){ - char *zOpt; - int eState; /* Parser state when parsing URI */ -@@ -164040,7 +185897,7 @@ SQLITE_PRIVATE int sqlite3ParseUri( - */ - static const char *uriParameter(const char *zFilename, const char *zParam){ - zFilename += sqlite3Strlen30(zFilename) + 1; -- while( zFilename[0] ){ -+ while( ALWAYS(zFilename!=0) && zFilename[0] ){ - int x = strcmp(zFilename, zParam); - zFilename += sqlite3Strlen30(zFilename) + 1; - if( x==0 ) return zFilename; -@@ -164100,8 +185957,8 @@ static int openDatabase( - ** dealt with in the previous code block. Besides these, the only - ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, - ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, -- ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask -- ** off all other flags. -+ ** SQLITE_OPEN_PRIVATECACHE, SQLITE_OPEN_EXRESCODE, and some reserved -+ ** bits. Silently mask off all other flags. - */ - flags &= ~( SQLITE_OPEN_DELETEONCLOSE | - SQLITE_OPEN_EXCLUSIVE | -@@ -164136,9 +185993,9 @@ static int openDatabase( - } - } - sqlite3_mutex_enter(db->mutex); -- db->errMask = 0xff; -+ db->errMask = (flags & SQLITE_OPEN_EXRESCODE)!=0 ? 0xffffffff : 0xff; - db->nDb = 2; -- db->magic = SQLITE_MAGIC_BUSY; -+ db->eOpenState = SQLITE_STATE_BUSY; - db->aDb = db->aDbStatic; - db->lookaside.bDisable = 1; - db->lookaside.sz = 0; -@@ -164150,11 +186007,22 @@ static int openDatabase( - db->nextAutovac = -1; - db->szMmap = sqlite3GlobalConfig.szMmap; - db->nextPagesize = 0; -+ db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */ -+#ifdef SQLITE_ENABLE_SORTER_MMAP -+ /* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map -+ ** the temporary files used to do external sorts (see code in vdbesort.c) -+ ** is disabled. It can still be used either by defining -+ ** SQLITE_ENABLE_SORTER_MMAP at compile time or by using the -+ ** SQLITE_TESTCTRL_SORTER_MMAP test-control at runtime. */ - db->nMaxSorterMmap = 0x7FFFFFFF; -+#endif - db->flags |= SQLITE_ShortColNames - | SQLITE_EnableTrigger - | SQLITE_EnableView - | SQLITE_CacheSpill -+ | SQLITE_AttachCreate -+ | SQLITE_AttachWrite -+ | SQLITE_Comments - #if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0 - | SQLITE_TrustedSchema - #endif -@@ -164170,7 +186038,7 @@ static int openDatabase( - ** 0 off off - ** - ** Legacy behavior is 3 (double-quoted string literals are allowed anywhere) --** and so that is the default. But developers are encouranged to use -+** and so that is the default. But developers are encouraged to use - ** -DSQLITE_DQS=0 (best) or -DSQLITE_DQS=1 (second choice) if possible. - */ - #if !defined(SQLITE_DQS) -@@ -164218,6 +186086,9 @@ static int openDatabase( - #endif - #if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE) - | SQLITE_LegacyAlter -+#endif -+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) -+ | SQLITE_StmtScanStatus - #endif - ; - sqlite3HashInit(&db->aCollSeq); -@@ -164241,6 +186112,19 @@ static int openDatabase( - goto opendb_out; - } - -+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) -+ /* Process magic filenames ":localStorage:" and ":sessionStorage:" */ -+ if( zFilename && zFilename[0]==':' ){ -+ if( strcmp(zFilename, ":localStorage:")==0 ){ -+ zFilename = "file:local?vfs=kvvfs"; -+ flags |= SQLITE_OPEN_URI; -+ }else if( strcmp(zFilename, ":sessionStorage:")==0 ){ -+ zFilename = "file:session?vfs=kvvfs"; -+ flags |= SQLITE_OPEN_URI; -+ } -+ } -+#endif /* SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) */ -+ - /* Parse the filename/URI argument - ** - ** Only allow sensible combinations of bits in the flags argument. -@@ -164263,6 +186147,7 @@ static int openDatabase( - if( ((1<<(flags&7)) & 0x46)==0 ){ - rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ - }else{ -+ if( zFilename==0 ) zFilename = ":memory:"; - rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); - } - if( rc!=SQLITE_OK ){ -@@ -164271,6 +186156,12 @@ static int openDatabase( - sqlite3_free(zErrMsg); - goto opendb_out; - } -+ assert( db->pVfs!=0 ); -+#if SQLITE_OS_KV || defined(SQLITE_OS_KV_OPTIONAL) -+ if( sqlite3_stricmp(db->pVfs->zName, "kvvfs")==0 ){ -+ db->temp_store = 2; -+ } -+#endif - - /* Open the backend database driver */ - rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, -@@ -164298,7 +186189,7 @@ static int openDatabase( - db->aDb[1].zDbSName = "temp"; - db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; - -- db->magic = SQLITE_MAGIC_OPEN; -+ db->eOpenState = SQLITE_STATE_OPEN; - if( db->mallocFailed ){ - goto opendb_out; - } -@@ -164360,12 +186251,12 @@ opendb_out: - sqlite3_mutex_leave(db->mutex); - } - rc = sqlite3_errcode(db); -- assert( db!=0 || rc==SQLITE_NOMEM ); -- if( rc==SQLITE_NOMEM ){ -+ assert( db!=0 || (rc&0xff)==SQLITE_NOMEM ); -+ if( (rc&0xff)==SQLITE_NOMEM ){ - sqlite3_close(db); - db = 0; - }else if( rc!=SQLITE_OK ){ -- db->magic = SQLITE_MAGIC_SICK; -+ db->eOpenState = SQLITE_STATE_SICK; - } - *ppDb = db; - #ifdef SQLITE_ENABLE_SQLLOG -@@ -164376,7 +186267,7 @@ opendb_out: - } - #endif - sqlite3_free_filename(zOpen); -- return rc & 0xff; -+ return rc; - } - - -@@ -164548,6 +186439,69 @@ SQLITE_API int sqlite3_collation_needed16( - } - #endif /* SQLITE_OMIT_UTF16 */ - -+/* -+** Find existing client data. -+*/ -+SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){ -+ DbClientData *p; -+ sqlite3_mutex_enter(db->mutex); -+ for(p=db->pDbData; p; p=p->pNext){ -+ if( strcmp(p->zName, zName)==0 ){ -+ void *pResult = p->pData; -+ sqlite3_mutex_leave(db->mutex); -+ return pResult; -+ } -+ } -+ sqlite3_mutex_leave(db->mutex); -+ return 0; -+} -+ -+/* -+** Add new client data to a database connection. -+*/ -+SQLITE_API int sqlite3_set_clientdata( -+ sqlite3 *db, /* Attach client data to this connection */ -+ const char *zName, /* Name of the client data */ -+ void *pData, /* The client data itself */ -+ void (*xDestructor)(void*) /* Destructor */ -+){ -+ DbClientData *p, **pp; -+ sqlite3_mutex_enter(db->mutex); -+ pp = &db->pDbData; -+ for(p=db->pDbData; p && strcmp(p->zName,zName); p=p->pNext){ -+ pp = &p->pNext; -+ } -+ if( p ){ -+ assert( p->pData!=0 ); -+ if( p->xDestructor ) p->xDestructor(p->pData); -+ if( pData==0 ){ -+ *pp = p->pNext; -+ sqlite3_free(p); -+ sqlite3_mutex_leave(db->mutex); -+ return SQLITE_OK; -+ } -+ }else if( pData==0 ){ -+ sqlite3_mutex_leave(db->mutex); -+ return SQLITE_OK; -+ }else{ -+ size_t n = strlen(zName); -+ p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) ); -+ if( p==0 ){ -+ if( xDestructor ) xDestructor(pData); -+ sqlite3_mutex_leave(db->mutex); -+ return SQLITE_NOMEM; -+ } -+ memcpy(p->zName, zName, n+1); -+ p->pNext = db->pDbData; -+ db->pDbData = p; -+ } -+ p->pData = pData; -+ p->xDestructor = xDestructor; -+ sqlite3_mutex_leave(db->mutex); -+ return SQLITE_OK; -+} -+ -+ - #ifndef SQLITE_OMIT_DEPRECATED - /* - ** This function is now an anachronism. It used to be used to recover from a -@@ -164676,22 +186630,19 @@ SQLITE_API int sqlite3_table_column_metadata( - - /* Locate the table in question */ - pTab = sqlite3FindTable(db, zTableName, zDbName); -- if( !pTab || pTab->pSelect ){ -+ if( !pTab || IsView(pTab) ){ - pTab = 0; - goto error_out; - } - - /* Find the column for which info is requested */ - if( zColumnName==0 ){ -- /* Query for existance of table only */ -+ /* Query for existence of table only */ - }else{ -- for(iCol=0; iColnCol; iCol++){ -+ iCol = sqlite3ColumnIndex(pTab, zColumnName); -+ if( iCol>=0 ){ - pCol = &pTab->aCol[iCol]; -- if( 0==sqlite3StrICmp(pCol->zName, zColumnName) ){ -- break; -- } -- } -- if( iCol==pTab->nCol ){ -+ }else{ - if( HasRowid(pTab) && sqlite3IsRowid(zColumnName) ){ - iCol = pTab->iPKey; - pCol = iCol>=0 ? &pTab->aCol[iCol] : 0; -@@ -164714,7 +186665,7 @@ SQLITE_API int sqlite3_table_column_metadata( - */ - if( pCol ){ - zDataType = sqlite3ColumnType(pCol,0); -- zCollSeq = pCol->zColl; -+ zCollSeq = sqlite3ColumnColl(pCol); - notnull = pCol->notNull!=0; - primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0; - autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0; -@@ -164764,7 +186715,7 @@ SQLITE_API int sqlite3_sleep(int ms){ - /* This function works in milliseconds, but the underlying OsSleep() - ** API uses microseconds. Hence the 1000's. - */ -- rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000); -+ rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000); - return rc; - } - -@@ -164820,8 +186771,13 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo - sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); - } - rc = SQLITE_OK; -+ }else if( op==SQLITE_FCNTL_RESET_CACHE ){ -+ sqlite3BtreeClearCache(pBtree); -+ rc = SQLITE_OK; - }else{ -+ int nSave = db->busyHandler.nBusy; - rc = sqlite3OsFileControl(fd, op, pArg); -+ db->busyHandler.nBusy = nSave; - } - sqlite3BtreeLeave(pBtree); - } -@@ -164892,6 +186848,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){ - } - #endif - -+ /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); -+ ** -+ ** If b is true, then activate the SQLITE_FkNoAction setting. If b is -+ ** false then clear that setting. If the SQLITE_FkNoAction setting is -+ ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if -+ ** they were NO ACTION, regardless of how they are defined. -+ ** -+ ** NB: One must usually run "PRAGMA writable_schema=RESET" after -+ ** using this test-control, before it will take full effect. failing -+ ** to reset the schema can result in some unexpected behavior. -+ */ -+ case SQLITE_TESTCTRL_FK_NO_ACTION: { -+ sqlite3 *db = va_arg(ap, sqlite3*); -+ int b = va_arg(ap, int); -+ if( b ){ -+ db->flags |= SQLITE_FkNoAction; -+ }else{ -+ db->flags &= ~SQLITE_FkNoAction; -+ } -+ break; -+ } -+ - /* - ** sqlite3_test_control(BITVEC_TEST, size, program) - ** -@@ -164919,12 +186897,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){ - ** sqlite3_test_control(). - */ - case SQLITE_TESTCTRL_FAULT_INSTALL: { -- /* MSVC is picky about pulling func ptrs from va lists. -- ** http://support.microsoft.com/kb/47961 -+ /* A bug in MSVC prevents it from understanding pointers to functions -+ ** types in the second argument to va_arg(). Work around the problem -+ ** using a typedef. -+ ** http://support.microsoft.com/kb/47961 <-- dead hyperlink -+ ** Search at http://web.archive.org/ to find the 2015-03-16 archive -+ ** of the link above to see the original text. - ** sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int)); - */ -- typedef int(*TESTCALLBACKFUNC_t)(int); -- sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t); -+ typedef int(*sqlite3FaultFuncType)(int); -+ sqlite3GlobalConfig.xTestCallback = va_arg(ap, sqlite3FaultFuncType); - rc = sqlite3FaultSim(0); - break; - } -@@ -164983,6 +186965,29 @@ SQLITE_API int sqlite3_test_control(int op, ...){ - volatile int x = 0; - assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 ); - rc = x; -+#if defined(SQLITE_DEBUG) -+ /* Invoke these debugging routines so that the compiler does not -+ ** issue "defined but not used" warnings. */ -+ if( x==9999 ){ -+ sqlite3ShowExpr(0); -+ sqlite3ShowExprList(0); -+ sqlite3ShowIdList(0); -+ sqlite3ShowSrcList(0); -+ sqlite3ShowWith(0); -+ sqlite3ShowUpsert(0); -+#ifndef SQLITE_OMIT_TRIGGER -+ sqlite3ShowTriggerStep(0); -+ sqlite3ShowTriggerStepList(0); -+ sqlite3ShowTrigger(0); -+ sqlite3ShowTriggerList(0); -+#endif -+#ifndef SQLITE_OMIT_WINDOWFUNC -+ sqlite3ShowWindow(0); -+ sqlite3ShowWinFunc(0); -+#endif -+ sqlite3ShowSelect(0); -+ } -+#endif - break; - } - -@@ -165047,17 +187052,43 @@ SQLITE_API int sqlite3_test_control(int op, ...){ - */ - case SQLITE_TESTCTRL_OPTIMIZATIONS: { - sqlite3 *db = va_arg(ap, sqlite3*); -- db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff); -+ db->dbOptFlags = va_arg(ap, u32); - break; - } - -- /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); -+ /* sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, sqlite3 *db, int *N) - ** -- ** If parameter onoff is non-zero, subsequent calls to localtime() -- ** and its variants fail. If onoff is zero, undo this setting. -+ ** Write the current optimization settings into *N. A zero bit means that -+ ** the optimization is on, and a 1 bit means that the optimization is off. -+ */ -+ case SQLITE_TESTCTRL_GETOPT: { -+ sqlite3 *db = va_arg(ap, sqlite3*); -+ int *pN = va_arg(ap, int*); -+ *pN = db->dbOptFlags; -+ break; -+ } -+ -+ /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt); -+ ** -+ ** If parameter onoff is 1, subsequent calls to localtime() fail. -+ ** If 2, then invoke xAlt() instead of localtime(). If 0, normal -+ ** processing. -+ ** -+ ** xAlt arguments are void pointers, but they really want to be: -+ ** -+ ** int xAlt(const time_t*, struct tm*); -+ ** -+ ** xAlt should write results in to struct tm object of its 2nd argument -+ ** and return zero on success, or return non-zero on failure. - */ - case SQLITE_TESTCTRL_LOCALTIME_FAULT: { - sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); -+ if( sqlite3GlobalConfig.bLocaltimeFault==2 ){ -+ typedef int(*sqlite3LocaltimeType)(const void*,void*); -+ sqlite3GlobalConfig.xAltLocaltime = va_arg(ap, sqlite3LocaltimeType); -+ }else{ -+ sqlite3GlobalConfig.xAltLocaltime = 0; -+ } - break; - } - -@@ -165078,7 +187109,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){ - ** formed and never corrupt. This flag is clear by default, indicating that - ** database files might have arbitrary corruption. Setting the flag during - ** testing causes certain assert() statements in the code to be activated -- ** that demonstrat invariants on well-formed database files. -+ ** that demonstrate invariants on well-formed database files. - */ - case SQLITE_TESTCTRL_NEVER_CORRUPT: { - sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int); -@@ -165162,12 +187193,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){ - */ - case SQLITE_TESTCTRL_IMPOSTER: { - sqlite3 *db = va_arg(ap, sqlite3*); -+ int iDb; - sqlite3_mutex_enter(db->mutex); -- db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); -- db->init.busy = db->init.imposterTable = va_arg(ap,int); -- db->init.newTnum = va_arg(ap,int); -- if( db->init.busy==0 && db->init.newTnum>0 ){ -- sqlite3ResetAllSchemasOfConnection(db); -+ iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); -+ if( iDb>=0 ){ -+ db->init.iDb = iDb; -+ db->init.busy = db->init.imposterTable = va_arg(ap,int); -+ db->init.newTnum = va_arg(ap,int); -+ if( db->init.busy==0 && db->init.newTnum>0 ){ -+ sqlite3ResetAllSchemasOfConnection(db); -+ } - } - sqlite3_mutex_leave(db->mutex); - break; -@@ -165204,6 +187239,117 @@ SQLITE_API int sqlite3_test_control(int op, ...){ - sqlite3ResultIntReal(pCtx); - break; - } -+ -+ /* sqlite3_test_control(SQLITE_TESTCTRL_SEEK_COUNT, -+ ** sqlite3 *db, // Database connection -+ ** u64 *pnSeek // Write seek count here -+ ** ); -+ ** -+ ** This test-control queries the seek-counter on the "main" database -+ ** file. The seek-counter is written into *pnSeek and is then reset. -+ ** The seek-count is only available if compiled with SQLITE_DEBUG. -+ */ -+ case SQLITE_TESTCTRL_SEEK_COUNT: { -+ sqlite3 *db = va_arg(ap, sqlite3*); -+ u64 *pn = va_arg(ap, sqlite3_uint64*); -+ *pn = sqlite3BtreeSeekCount(db->aDb->pBt); -+ (void)db; /* Silence harmless unused variable warning */ -+ break; -+ } -+ -+ /* sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, op, ptr) -+ ** -+ ** "ptr" is a pointer to a u32. -+ ** -+ ** op==0 Store the current sqlite3TreeTrace in *ptr -+ ** op==1 Set sqlite3TreeTrace to the value *ptr -+ ** op==2 Store the current sqlite3WhereTrace in *ptr -+ ** op==3 Set sqlite3WhereTrace to the value *ptr -+ */ -+ case SQLITE_TESTCTRL_TRACEFLAGS: { -+ int opTrace = va_arg(ap, int); -+ u32 *ptr = va_arg(ap, u32*); -+ switch( opTrace ){ -+ case 0: *ptr = sqlite3TreeTrace; break; -+ case 1: sqlite3TreeTrace = *ptr; break; -+ case 2: *ptr = sqlite3WhereTrace; break; -+ case 3: sqlite3WhereTrace = *ptr; break; -+ } -+ break; -+ } -+ -+ /* sqlite3_test_control(SQLITE_TESTCTRL_LOGEST, -+ ** double fIn, // Input value -+ ** int *pLogEst, // sqlite3LogEstFromDouble(fIn) -+ ** u64 *pInt, // sqlite3LogEstToInt(*pLogEst) -+ ** int *pLogEst2 // sqlite3LogEst(*pInt) -+ ** ); -+ ** -+ ** Test access for the LogEst conversion routines. -+ */ -+ case SQLITE_TESTCTRL_LOGEST: { -+ double rIn = va_arg(ap, double); -+ LogEst rLogEst = sqlite3LogEstFromDouble(rIn); -+ int *pI1 = va_arg(ap,int*); -+ u64 *pU64 = va_arg(ap,u64*); -+ int *pI2 = va_arg(ap,int*); -+ *pI1 = rLogEst; -+ *pU64 = sqlite3LogEstToInt(rLogEst); -+ *pI2 = sqlite3LogEst(*pU64); -+ break; -+ } -+ -+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) -+ /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) -+ ** -+ ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value -+ ** of the id-th tuning parameter to *piValue. If "id" is between -1 -+ ** and -SQLITE_NTUNE, then write the current value of the (-id)-th -+ ** tuning parameter into *piValue. -+ ** -+ ** Tuning parameters are for use during transient development builds, -+ ** to help find the best values for constants in the query planner. -+ ** Access tuning parameters using the Tuning(ID) macro. Set the -+ ** parameters in the CLI using ".testctrl tune ID VALUE". -+ ** -+ ** Transient use only. Tuning parameters should not be used in -+ ** checked-in code. -+ */ -+ case SQLITE_TESTCTRL_TUNE: { -+ int id = va_arg(ap, int); -+ int *piValue = va_arg(ap, int*); -+ if( id>0 && id<=SQLITE_NTUNE ){ -+ Tuning(id) = *piValue; -+ }else if( id<0 && id>=-SQLITE_NTUNE ){ -+ *piValue = Tuning(-id); -+ }else{ -+ rc = SQLITE_NOTFOUND; -+ } -+ break; -+ } -+#endif -+ -+ /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff); -+ ** -+ ** Activate or deactivate validation of JSONB that is generated from -+ ** text. Off by default, as the validation is slow. Validation is -+ ** only available if compiled using SQLITE_DEBUG. -+ ** -+ ** If onOff is initially 1, then turn it on. If onOff is initially -+ ** off, turn it off. If onOff is initially -1, then change onOff -+ ** to be the current setting. -+ */ -+ case SQLITE_TESTCTRL_JSON_SELFCHECK: { -+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) -+ int *pOnOff = va_arg(ap, int*); -+ if( *pOnOff<0 ){ -+ *pOnOff = sqlite3Config.bJsonSelfcheck; -+ }else{ -+ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff); -+ } -+#endif -+ break; -+ } - } - va_end(ap); - #endif /* SQLITE_UNTESTABLE */ -@@ -165244,7 +187390,7 @@ static char *appendText(char *p, const char *z){ - ** Memory layout must be compatible with that generated by the pager - ** and expected by sqlite3_uri_parameter() and databaseName(). - */ --SQLITE_API char *sqlite3_create_filename( -+SQLITE_API const char *sqlite3_create_filename( - const char *zDatabase, - const char *zJournal, - const char *zWal, -@@ -165280,10 +187426,10 @@ SQLITE_API char *sqlite3_create_filename( - ** error to call this routine with any parameter other than a pointer - ** previously obtained from sqlite3_create_filename() or a NULL pointer. - */ --SQLITE_API void sqlite3_free_filename(char *p){ -+SQLITE_API void sqlite3_free_filename(const char *p){ - if( p==0 ) return; -- p = (char*)databaseName(p); -- sqlite3_free(p - 4); -+ p = databaseName(p); -+ sqlite3_free((char*)p - 4); - } - - -@@ -165311,7 +187457,7 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N){ - if( zFilename==0 || N<0 ) return 0; - zFilename = databaseName(zFilename); - zFilename += sqlite3Strlen30(zFilename) + 1; -- while( zFilename[0] && (N--)>0 ){ -+ while( ALWAYS(zFilename) && zFilename[0] && (N--)>0 ){ - zFilename += sqlite3Strlen30(zFilename) + 1; - zFilename += sqlite3Strlen30(zFilename) + 1; - } -@@ -165354,12 +187500,14 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64( - ** corruption. - */ - SQLITE_API const char *sqlite3_filename_database(const char *zFilename){ -+ if( zFilename==0 ) return 0; - return databaseName(zFilename); - } - SQLITE_API const char *sqlite3_filename_journal(const char *zFilename){ -+ if( zFilename==0 ) return 0; - zFilename = databaseName(zFilename); - zFilename += sqlite3Strlen30(zFilename) + 1; -- while( zFilename[0] ){ -+ while( ALWAYS(zFilename) && zFilename[0] ){ - zFilename += sqlite3Strlen30(zFilename) + 1; - zFilename += sqlite3Strlen30(zFilename) + 1; - } -@@ -165370,7 +187518,7 @@ SQLITE_API const char *sqlite3_filename_wal(const char *zFilename){ - return 0; - #else - zFilename = sqlite3_filename_journal(zFilename); -- zFilename += sqlite3Strlen30(zFilename) + 1; -+ if( zFilename ) zFilename += sqlite3Strlen30(zFilename) + 1; - return zFilename; - #endif - } -@@ -165383,6 +187531,24 @@ SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ - return iDb<0 ? 0 : db->aDb[iDb].pBt; - } - -+/* -+** Return the name of the N-th database schema. Return NULL if N is out -+** of range. -+*/ -+SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N){ -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) ){ -+ (void)SQLITE_MISUSE_BKPT; -+ return 0; -+ } -+#endif -+ if( N<0 || N>=db->nDb ){ -+ return 0; -+ }else{ -+ return db->aDb[N].zDbSName; -+ } -+} -+ - /* - ** Return the filename of the database associated with a database - ** connection. -@@ -165439,8 +187605,12 @@ SQLITE_API int sqlite3_snapshot_get( - int iDb = sqlite3FindDbName(db, zDb); - if( iDb==0 || iDb>1 ){ - Btree *pBt = db->aDb[iDb].pBt; -- if( 0==sqlite3BtreeIsInTrans(pBt) ){ -+ if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){ -+ Pager *pPager = sqlite3BtreePager(pBt); -+ i64 dummy = 0; -+ sqlite3PagerSnapshotOpen(pPager, (sqlite3_snapshot*)&dummy); - rc = sqlite3BtreeBeginTrans(pBt, 0, 0); -+ sqlite3PagerSnapshotOpen(pPager, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); - } -@@ -165454,7 +187624,7 @@ SQLITE_API int sqlite3_snapshot_get( - } - - /* --** Open a read-transaction on the snapshot idendified by pSnapshot. -+** Open a read-transaction on the snapshot identified by pSnapshot. - */ - SQLITE_API int sqlite3_snapshot_open( - sqlite3 *db, -@@ -165475,10 +187645,10 @@ SQLITE_API int sqlite3_snapshot_open( - iDb = sqlite3FindDbName(db, zDb); - if( iDb==0 || iDb>1 ){ - Btree *pBt = db->aDb[iDb].pBt; -- if( sqlite3BtreeIsInTrans(pBt)==0 ){ -+ if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ){ - Pager *pPager = sqlite3BtreePager(pBt); - int bUnlock = 0; -- if( sqlite3BtreeIsInReadTrans(pBt) ){ -+ if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_NONE ){ - if( db->nVdbeActive==0 ){ - rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot); - if( rc==SQLITE_OK ){ -@@ -165514,8 +187684,8 @@ SQLITE_API int sqlite3_snapshot_open( - */ - SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){ - int rc = SQLITE_ERROR; -- int iDb; - #ifndef SQLITE_OMIT_WAL -+ int iDb; - - #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ){ -@@ -165527,7 +187697,7 @@ SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){ - iDb = sqlite3FindDbName(db, zDb); - if( iDb==0 || iDb>1 ){ - Btree *pBt = db->aDb[iDb].pBt; -- if( 0==sqlite3BtreeIsInReadTrans(pBt) ){ -+ if( SQLITE_TXN_NONE==sqlite3BtreeTxnState(pBt) ){ - rc = sqlite3BtreeBeginTrans(pBt, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt)); -@@ -165561,7 +187731,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ - int nOpt; - const char **azCompileOpt; - --#if SQLITE_ENABLE_API_ARMOR -+#ifdef SQLITE_ENABLE_API_ARMOR - if( zOptName==0 ){ - (void)SQLITE_MISUSE_BKPT; - return 0; -@@ -165756,6 +187926,9 @@ SQLITE_API int sqlite3_unlock_notify( - ){ - int rc = SQLITE_OK; - -+#ifdef SQLITE_ENABLE_API_ARMOR -+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; -+#endif - sqlite3_mutex_enter(db->mutex); - enterMutex(); - -@@ -166026,7 +188199,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ - ** Here, array { X } means zero or more occurrences of X, adjacent in - ** memory. A "position" is an index of a token in the token stream - ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur --** in the same logical place as the position element, and act as sentinals -+** in the same logical place as the position element, and act as sentinels - ** ending a position list array. POS_END is 0. POS_COLUMN is 1. - ** The positions numbers are not stored literally but rather as two more - ** than the difference from the prior position, or the just the position plus -@@ -166245,6 +188418,13 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ - #ifndef _FTSINT_H - #define _FTSINT_H - -+/* #include */ -+/* #include */ -+/* #include */ -+/* #include */ -+/* #include */ -+/* #include */ -+ - #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) - # define NDEBUG 1 - #endif -@@ -166646,7 +188826,7 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi - ** is used for assert() conditions that are true only if it can be - ** guranteed that the database is not corrupt. - */ --#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) -+#ifdef SQLITE_DEBUG - SQLITE_API extern int sqlite3_fts3_may_be_corrupt; - # define assert_fts3_nc(x) assert(sqlite3_fts3_may_be_corrupt || (x)) - #else -@@ -166663,17 +188843,18 @@ SQLITE_API extern int sqlite3_fts3_may_be_corrupt; - ** Macros indicating that conditional expressions are always true or - ** false. - */ --#ifdef SQLITE_COVERAGE_TEST --# define ALWAYS(x) (1) --# define NEVER(X) (0) --#elif defined(SQLITE_DEBUG) --# define ALWAYS(x) sqlite3Fts3Always((x)!=0) --# define NEVER(x) sqlite3Fts3Never((x)!=0) --SQLITE_PRIVATE int sqlite3Fts3Always(int b); --SQLITE_PRIVATE int sqlite3Fts3Never(int b); -+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) -+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 -+#endif -+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) -+# define ALWAYS(X) (1) -+# define NEVER(X) (0) -+#elif !defined(NDEBUG) -+# define ALWAYS(X) ((X)?1:(assert(0),0)) -+# define NEVER(X) ((X)?(assert(0),1):0) - #else --# define ALWAYS(x) (x) --# define NEVER(x) (x) -+# define ALWAYS(X) (X) -+# define NEVER(X) (X) - #endif - - /* -@@ -166713,6 +188894,19 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */ - - #define deliberate_fall_through - -+/* -+** Macros needed to provide flexible arrays in a portable way -+*/ -+#ifndef offsetof -+# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) -+#endif -+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -+# define FLEXARRAY -+#else -+# define FLEXARRAY 1 -+#endif -+ -+ - #endif /* SQLITE_AMALGAMATION */ - - #ifdef SQLITE_DEBUG -@@ -166776,6 +188970,7 @@ struct Fts3Table { - int nPgsz; /* Page size for host database */ - char *zSegmentsTbl; /* Name of %_segments table */ - sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ -+ int iSavepoint; - - /* - ** The following array of hash tables is used to buffer pending index -@@ -166816,7 +189011,7 @@ struct Fts3Table { - #endif - - #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) -- /* True to disable the incremental doclist optimization. This is controled -+ /* True to disable the incremental doclist optimization. This is controlled - ** by special insert command 'test-no-incr-doclist'. */ - int bNoIncrDoclist; - -@@ -166868,7 +189063,7 @@ struct Fts3Cursor { - - /* - ** The Fts3Cursor.eSearch member is always set to one of the following. --** Actualy, Fts3Cursor.eSearch can be greater than or equal to -+** Actually, Fts3Cursor.eSearch can be greater than or equal to - ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index - ** of the column to be searched. For example, in - ** -@@ -166941,9 +189136,13 @@ struct Fts3Phrase { - */ - int nToken; /* Number of tokens in the phrase */ - int iColumn; /* Index of column this phrase must match */ -- Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ -+ Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */ - }; - -+/* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */ -+#define SZ_FTS3PHRASE(N) \ -+ (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken)) -+ - /* - ** A tree of these objects forms the RHS of a MATCH operator. - ** -@@ -167069,7 +189268,7 @@ struct Fts3MultiSegReader { - int nAdvance; /* How many seg-readers to advance */ - Fts3SegFilter *pFilter; /* Pointer to filter object */ - char *aBuffer; /* Buffer to merge doclists in */ -- int nBuffer; /* Allocated size of aBuffer[] in bytes */ -+ i64 nBuffer; /* Allocated size of aBuffer[] in bytes */ - - int iColFilter; /* If >=0, filter for this column */ - int bRestart; -@@ -167132,6 +189331,7 @@ SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); - SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash*); - SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db); - #endif -+SQLITE_PRIVATE void *sqlite3Fts3MallocZero(i64 nByte); - - SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer(sqlite3_tokenizer *, int, const char *, int, - sqlite3_tokenizer_cursor ** -@@ -167149,9 +189349,10 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( - SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **); - SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); - SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); -+SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor*, Fts3Expr*); - - /* fts3_tokenize_vtab.c */ --SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *); -+SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *, void(*xDestroy)(void*)); - - /* fts3_unicode2.c (functions generated by parsing unicode text files) */ - #ifndef SQLITE_DISABLE_FTS3_UNICODE -@@ -167160,6 +189361,10 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int); - SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); - #endif - -+SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); -+ -+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk); -+ - #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ - #endif /* _FTSINT_H */ - -@@ -167171,12 +189376,6 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); - # define SQLITE_CORE 1 - #endif - --/* #include */ --/* #include */ --/* #include */ --/* #include */ --/* #include */ --/* #include */ - - /* #include "fts3.h" */ - #ifndef SQLITE_CORE -@@ -167184,25 +189383,26 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); - SQLITE_EXTENSION_INIT1 - #endif - -+typedef struct Fts3HashWrapper Fts3HashWrapper; -+struct Fts3HashWrapper { -+ Fts3Hash hash; /* Hash table */ -+ int nRef; /* Number of pointers to this object */ -+}; -+ - static int fts3EvalNext(Fts3Cursor *pCsr); - static int fts3EvalStart(Fts3Cursor *pCsr); - static int fts3TermSegReaderCursor( - Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); - --#ifndef SQLITE_AMALGAMATION --# if defined(SQLITE_DEBUG) --SQLITE_PRIVATE int sqlite3Fts3Always(int b) { assert( b ); return b; } --SQLITE_PRIVATE int sqlite3Fts3Never(int b) { assert( !b ); return b; } --# endif --#endif -- - /* - ** This variable is set to false when running tests for which the on disk - ** structures should not be corrupt. Otherwise, true. If it is false, extra - ** assert() conditions in the fts3 code are activated - conditions that are - ** only true if it is guaranteed that the fts3 database is not corrupt. - */ -+#ifdef SQLITE_DEBUG - SQLITE_API int sqlite3_fts3_may_be_corrupt = 1; -+#endif - - /* - ** Write a 64-bit variable-length integer to memory starting at p[0]. -@@ -167515,6 +189715,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){ - - zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid"); - sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); -+ sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS); - - /* Create a list of user columns for the virtual table */ - zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); -@@ -168053,7 +190254,7 @@ static int fts3InitVtab( - sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ - char **pzErr /* Write any error message here */ - ){ -- Fts3Hash *pHash = (Fts3Hash *)pAux; -+ Fts3Hash *pHash = &((Fts3HashWrapper*)pAux)->hash; - Fts3Table *p = 0; /* Pointer to allocated vtab */ - int rc = SQLITE_OK; /* Return code */ - int i; /* Iterator variable */ -@@ -168773,7 +190974,7 @@ static int fts3ScanInteriorNode( - char *zBuffer = 0; /* Buffer to load terms into */ - i64 nAlloc = 0; /* Size of allocated buffer */ - int isFirstTerm = 1; /* True when processing first term on page */ -- sqlite3_int64 iChild; /* Block id of child node to descend to */ -+ u64 iChild; /* Block id of child node to descend to */ - int nBuffer = 0; /* Total term size */ - - /* Skip over the 'height' varint that occurs at the start of every -@@ -168789,8 +190990,8 @@ static int fts3ScanInteriorNode( - ** table, then there are always 20 bytes of zeroed padding following the - ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). - */ -- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); -- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); -+ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); -+ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild); - if( zCsr>zEnd ){ - return FTS_CORRUPT_VTAB; - } -@@ -168843,20 +191044,20 @@ static int fts3ScanInteriorNode( - */ - cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); - if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ -- *piFirst = iChild; -+ *piFirst = (i64)iChild; - piFirst = 0; - } - - if( piLast && cmp<0 ){ -- *piLast = iChild; -+ *piLast = (i64)iChild; - piLast = 0; - } - - iChild++; - }; - -- if( piFirst ) *piFirst = iChild; -- if( piLast ) *piLast = iChild; -+ if( piFirst ) *piFirst = (i64)iChild; -+ if( piLast ) *piLast = (i64)iChild; - - finish_scan: - sqlite3_free(zBuffer); -@@ -169218,10 +191419,15 @@ static int fts3PoslistPhraseMerge( - if( *p1==POS_COLUMN ){ - p1++; - p1 += fts3GetVarint32(p1, &iCol1); -+ /* iCol1==0 indicates corruption. Column 0 does not have a POS_COLUMN -+ ** entry, so this is actually end-of-doclist. */ -+ if( iCol1==0 ) return 0; - } - if( *p2==POS_COLUMN ){ - p2++; - p2 += fts3GetVarint32(p2, &iCol2); -+ /* As above, iCol2==0 indicates corruption. */ -+ if( iCol2==0 ) return 0; - } - - while( 1 ){ -@@ -169508,7 +191714,7 @@ static int fts3DoclistOrMerge( - ** sizes of the two inputs, plus enough space for exactly one of the input - ** docids to grow. - ** -- ** A symetric argument may be made if the doclists are in descending -+ ** A symmetric argument may be made if the doclists are in descending - ** order. - */ - aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); -@@ -169763,7 +191969,7 @@ static int fts3TermSelectMerge( - ** - ** Similar padding is added in the fts3DoclistOrMerge() function. - */ -- pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1); -+ pTS->aaOutput[0] = sqlite3_malloc64((i64)nDoclist + FTS3_VARINT_MAX + 1); - pTS->anOutput[0] = nDoclist; - if( pTS->aaOutput[0] ){ - memcpy(pTS->aaOutput[0], aDoclist, nDoclist); -@@ -170462,14 +192668,20 @@ static int fts3SetHasStat(Fts3Table *p){ - */ - static int fts3BeginMethod(sqlite3_vtab *pVtab){ - Fts3Table *p = (Fts3Table*)pVtab; -+ int rc; - UNUSED_PARAMETER(pVtab); - assert( p->pSegments==0 ); - assert( p->nPendingData==0 ); - assert( p->inTransaction!=1 ); -- TESTONLY( p->inTransaction = 1 ); -- TESTONLY( p->mxSavepoint = -1; ); - p->nLeafAdd = 0; -- return fts3SetHasStat(p); -+ rc = fts3SetHasStat(p); -+#ifdef SQLITE_DEBUG -+ if( rc==SQLITE_OK ){ -+ p->inTransaction = 1; -+ p->mxSavepoint = -1; -+ } -+#endif -+ return rc; - } - - /* -@@ -170758,6 +192970,8 @@ static int fts3RenameMethod( - rc = sqlite3Fts3PendingTermsFlush(p); - } - -+ p->bIgnoreSavepoint = 1; -+ - if( p->zContentTbl==0 ){ - fts3DbExec(&rc, db, - "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", -@@ -170785,6 +192999,8 @@ static int fts3RenameMethod( - "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';", - p->zDb, p->zName, zName - ); -+ -+ p->bIgnoreSavepoint = 0; - return rc; - } - -@@ -170795,12 +193011,28 @@ static int fts3RenameMethod( - */ - static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - int rc = SQLITE_OK; -- UNUSED_PARAMETER(iSavepoint); -- assert( ((Fts3Table *)pVtab)->inTransaction ); -- assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint ); -- TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint ); -- if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){ -- rc = fts3SyncMethod(pVtab); -+ Fts3Table *pTab = (Fts3Table*)pVtab; -+ assert( pTab->inTransaction ); -+ assert( pTab->mxSavepoint<=iSavepoint ); -+ TESTONLY( pTab->mxSavepoint = iSavepoint ); -+ -+ if( pTab->bIgnoreSavepoint==0 ){ -+ if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){ -+ char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')", -+ pTab->zDb, pTab->zName, pTab->zName -+ ); -+ if( zSql ){ -+ pTab->bIgnoreSavepoint = 1; -+ rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0); -+ pTab->bIgnoreSavepoint = 0; -+ sqlite3_free(zSql); -+ }else{ -+ rc = SQLITE_NOMEM; -+ } -+ } -+ if( rc==SQLITE_OK ){ -+ pTab->iSavepoint = iSavepoint+1; -+ } - } - return rc; - } -@@ -170811,12 +193043,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - ** This is a no-op. - */ - static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ -- TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); -- UNUSED_PARAMETER(iSavepoint); -- UNUSED_PARAMETER(pVtab); -- assert( p->inTransaction ); -- assert( p->mxSavepoint >= iSavepoint ); -- TESTONLY( p->mxSavepoint = iSavepoint-1 ); -+ Fts3Table *pTab = (Fts3Table*)pVtab; -+ assert( pTab->inTransaction ); -+ assert( pTab->mxSavepoint >= iSavepoint ); -+ TESTONLY( pTab->mxSavepoint = iSavepoint-1 ); -+ pTab->iSavepoint = iSavepoint; - return SQLITE_OK; - } - -@@ -170826,11 +193057,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - ** Discard the contents of the pending terms table. - */ - static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ -- Fts3Table *p = (Fts3Table*)pVtab; -+ Fts3Table *pTab = (Fts3Table*)pVtab; - UNUSED_PARAMETER(iSavepoint); -- assert( p->inTransaction ); -- TESTONLY( p->mxSavepoint = iSavepoint ); -- sqlite3Fts3PendingTermsClear(p); -+ assert( pTab->inTransaction ); -+ TESTONLY( pTab->mxSavepoint = iSavepoint ); -+ if( (iSavepoint+1)<=pTab->iSavepoint ){ -+ sqlite3Fts3PendingTermsClear(pTab); -+ } - return SQLITE_OK; - } - -@@ -170849,8 +193082,42 @@ static int fts3ShadowName(const char *zName){ - return 0; - } - -+/* -+** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual -+** table. -+*/ -+static int fts3IntegrityMethod( -+ sqlite3_vtab *pVtab, /* The virtual table to be checked */ -+ const char *zSchema, /* Name of schema in which pVtab lives */ -+ const char *zTabname, /* Name of the pVTab table */ -+ int isQuick, /* True if this is a quick_check */ -+ char **pzErr /* Write error message here */ -+){ -+ Fts3Table *p = (Fts3Table*)pVtab; -+ int rc = SQLITE_OK; -+ int bOk = 0; -+ -+ UNUSED_PARAMETER(isQuick); -+ rc = sqlite3Fts3IntegrityCheck(p, &bOk); -+ assert( rc!=SQLITE_CORRUPT_VTAB ); -+ if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ -+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" -+ " FTS%d table %s.%s: %s", -+ p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); -+ if( *pzErr ) rc = SQLITE_OK; -+ }else if( rc==SQLITE_OK && bOk==0 ){ -+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s", -+ p->bFts4 ? 4 : 3, zSchema, zTabname); -+ if( *pzErr==0 ) rc = SQLITE_NOMEM; -+ } -+ sqlite3Fts3SegmentsClose(p); -+ return rc; -+} -+ -+ -+ - static const sqlite3_module fts3Module = { -- /* iVersion */ 3, -+ /* iVersion */ 4, - /* xCreate */ fts3CreateMethod, - /* xConnect */ fts3ConnectMethod, - /* xBestIndex */ fts3BestIndexMethod, -@@ -170874,6 +193141,7 @@ static const sqlite3_module fts3Module = { - /* xRelease */ fts3ReleaseMethod, - /* xRollbackTo */ fts3RollbackToMethod, - /* xShadowName */ fts3ShadowName, -+ /* xIntegrity */ fts3IntegrityMethod, - }; - - /* -@@ -170882,9 +193150,12 @@ static const sqlite3_module fts3Module = { - ** allocated for the tokenizer hash table. - */ - static void hashDestroy(void *p){ -- Fts3Hash *pHash = (Fts3Hash *)p; -- sqlite3Fts3HashClear(pHash); -- sqlite3_free(pHash); -+ Fts3HashWrapper *pHash = (Fts3HashWrapper *)p; -+ pHash->nRef--; -+ if( pHash->nRef<=0 ){ -+ sqlite3Fts3HashClear(&pHash->hash); -+ sqlite3_free(pHash); -+ } - } - - /* -@@ -170914,7 +193185,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const - */ - SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ - int rc = SQLITE_OK; -- Fts3Hash *pHash = 0; -+ Fts3HashWrapper *pHash = 0; - const sqlite3_tokenizer_module *pSimple = 0; - const sqlite3_tokenizer_module *pPorter = 0; - #ifndef SQLITE_DISABLE_FTS3_UNICODE -@@ -170942,23 +193213,24 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ - sqlite3Fts3PorterTokenizerModule(&pPorter); - - /* Allocate and initialize the hash-table used to store tokenizers. */ -- pHash = sqlite3_malloc(sizeof(Fts3Hash)); -+ pHash = sqlite3_malloc(sizeof(Fts3HashWrapper)); - if( !pHash ){ - rc = SQLITE_NOMEM; - }else{ -- sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); -+ sqlite3Fts3HashInit(&pHash->hash, FTS3_HASH_STRING, 1); -+ pHash->nRef = 0; - } - - /* Load the built-in tokenizers into the hash table */ - if( rc==SQLITE_OK ){ -- if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) -- || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) -+ if( sqlite3Fts3HashInsert(&pHash->hash, "simple", 7, (void *)pSimple) -+ || sqlite3Fts3HashInsert(&pHash->hash, "porter", 7, (void *)pPorter) - - #ifndef SQLITE_DISABLE_FTS3_UNICODE -- || sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode) -+ || sqlite3Fts3HashInsert(&pHash->hash, "unicode61", 10, (void *)pUnicode) - #endif - #ifdef SQLITE_ENABLE_ICU -- || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) -+ || (pIcu && sqlite3Fts3HashInsert(&pHash->hash, "icu", 4, (void *)pIcu)) - #endif - ){ - rc = SQLITE_NOMEM; -@@ -170967,7 +193239,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ - - #ifdef SQLITE_TEST - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts3ExprInitTestInterface(db, pHash); -+ rc = sqlite3Fts3ExprInitTestInterface(db, &pHash->hash); - } - #endif - -@@ -170976,23 +193248,26 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ - ** module with sqlite. - */ - if( SQLITE_OK==rc -- && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) -+ && SQLITE_OK==(rc=sqlite3Fts3InitHashTable(db,&pHash->hash,"fts3_tokenizer")) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 2)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1)) - ){ -+ pHash->nRef++; - rc = sqlite3_create_module_v2( - db, "fts3", &fts3Module, (void *)pHash, hashDestroy - ); - if( rc==SQLITE_OK ){ -+ pHash->nRef++; - rc = sqlite3_create_module_v2( -- db, "fts4", &fts3Module, (void *)pHash, 0 -+ db, "fts4", &fts3Module, (void *)pHash, hashDestroy - ); - } - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts3InitTok(db, (void *)pHash); -+ pHash->nRef++; -+ rc = sqlite3Fts3InitTok(db, (void *)pHash, hashDestroy); - } - return rc; - } -@@ -171001,7 +193276,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ - /* An error has occurred. Delete the hash table and return the error code. */ - assert( rc!=SQLITE_OK ); - if( pHash ){ -- sqlite3Fts3HashClear(pHash); -+ sqlite3Fts3HashClear(&pHash->hash); - sqlite3_free(pHash); - } - return rc; -@@ -171170,8 +193445,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ - char *aPoslist = 0; /* Position list for deferred tokens */ - int nPoslist = 0; /* Number of bytes in aPoslist */ - int iPrev = -1; /* Token number of previous deferred token */ -- -- assert( pPhrase->doclist.bFreeList==0 ); -+ char *aFree = (pPhrase->doclist.bFreeList ? pPhrase->doclist.pList : 0); - - for(iToken=0; iTokennToken; iToken++){ - Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; -@@ -171185,6 +193459,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ - - if( pList==0 ){ - sqlite3_free(aPoslist); -+ sqlite3_free(aFree); - pPhrase->doclist.pList = 0; - pPhrase->doclist.nList = 0; - return SQLITE_OK; -@@ -171205,6 +193480,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ - nPoslist = (int)(aOut - aPoslist); - if( nPoslist==0 ){ - sqlite3_free(aPoslist); -+ sqlite3_free(aFree); - pPhrase->doclist.pList = 0; - pPhrase->doclist.nList = 0; - return SQLITE_OK; -@@ -171237,13 +193513,14 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ - nDistance = iPrev - nMaxUndeferred; - } - -- aOut = (char *)sqlite3_malloc(nPoslist+8); -+ aOut = (char *)sqlite3Fts3MallocZero(((i64)nPoslist)+FTS3_BUFFER_PADDING); - if( !aOut ){ - sqlite3_free(aPoslist); - return SQLITE_NOMEM; - } - - pPhrase->doclist.pList = aOut; -+ assert( p1 && p2 ); - if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ - pPhrase->doclist.bFreeList = 1; - pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList); -@@ -171256,6 +193533,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ - } - } - -+ if( pPhrase->doclist.pList!=aFree ) sqlite3_free(aFree); - return SQLITE_OK; - } - #endif /* SQLITE_DISABLE_FTS4_DEFERRED */ -@@ -171348,7 +193626,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev( - - assert( nDoclist>0 ); - assert( *pbEof==0 ); -- assert( p || *piDocid==0 ); -+ assert_fts3_nc( p || *piDocid==0 ); - assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); - - if( p==0 ){ -@@ -171534,7 +193812,7 @@ static int incrPhraseTokenNext( - ** - ** * does not contain any deferred tokens. - ** --** Advance it to the next matching documnent in the database and populate -+** Advance it to the next matching document in the database and populate - ** the Fts3Doclist.pList and nList fields. - ** - ** If there is no "next" entry and no error occurs, then *pbEof is set to -@@ -171604,7 +193882,7 @@ static int fts3EvalIncrPhraseNext( - if( bEof==0 ){ - int nList = 0; - int nByte = a[p->nToken-1].nList; -- char *aDoclist = sqlite3_malloc(nByte+FTS3_BUFFER_PADDING); -+ char *aDoclist = sqlite3_malloc64((i64)nByte+FTS3_BUFFER_PADDING); - if( !aDoclist ) return SQLITE_NOMEM; - memcpy(aDoclist, a[p->nToken-1].pList, nByte+1); - memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING); -@@ -171998,16 +194276,15 @@ static int fts3EvalStart(Fts3Cursor *pCsr){ - #ifndef SQLITE_DISABLE_FTS4_DEFERRED - if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){ - Fts3TokenAndCost *aTC; -- Fts3Expr **apOr; - aTC = (Fts3TokenAndCost *)sqlite3_malloc64( - sizeof(Fts3TokenAndCost) * nToken - + sizeof(Fts3Expr *) * nOr * 2 - ); -- apOr = (Fts3Expr **)&aTC[nToken]; - - if( !aTC ){ - rc = SQLITE_NOMEM; - }else{ -+ Fts3Expr **apOr = (Fts3Expr **)&aTC[nToken]; - int ii; - Fts3TokenAndCost *pTC = aTC; - Fts3Expr **ppOr = apOr; -@@ -172088,9 +194365,9 @@ static int fts3EvalNearTrim( - ); - if( res ){ - nNew = (int)(pOut - pPhrase->doclist.pList) - 1; -- if( nNew>=0 ){ -+ assert_fts3_nc( nNew<=pPhrase->doclist.nList && nNew>0 ); -+ if( nNew>=0 && nNew<=pPhrase->doclist.nList ){ - assert( pPhrase->doclist.pList[nNew]=='\0' ); -- assert( nNew<=pPhrase->doclist.nList && nNew>0 ); - memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); - pPhrase->doclist.nList = nNew; - } -@@ -172147,9 +194424,8 @@ static void fts3EvalNextRow( - Fts3Expr *pExpr, /* Expr. to advance to next matching row */ - int *pRc /* IN/OUT: Error code */ - ){ -- if( *pRc==SQLITE_OK ){ -+ if( *pRc==SQLITE_OK && pExpr->bEof==0 ){ - int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */ -- assert( pExpr->bEof==0 ); - pExpr->bStart = 1; - - switch( pExpr->eType ){ -@@ -172213,8 +194489,8 @@ static void fts3EvalNextRow( - Fts3Expr *pRight = pExpr->pRight; - sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); - -- assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); -- assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); -+ assert_fts3_nc( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); -+ assert_fts3_nc( pRight->bStart || pLeft->iDocid==pRight->iDocid ); - - if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ - fts3EvalNextRow(pCsr, pLeft, pRc); -@@ -172322,7 +194598,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ - nTmp += p->pRight->pPhrase->doclist.nList; - } - nTmp += p->pPhrase->doclist.nList; -- aTmp = sqlite3_malloc64(nTmp*2); -+ aTmp = sqlite3_malloc64(nTmp*2 + FTS3_VARINT_MAX); - if( !aTmp ){ - *pRc = SQLITE_NOMEM; - res = 0; -@@ -172431,11 +194707,10 @@ static int fts3EvalTestExpr( - - default: { - #ifndef SQLITE_DISABLE_FTS4_DEFERRED -- if( pCsr->pDeferred -- && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred) -- ){ -+ if( pCsr->pDeferred && (pExpr->bDeferred || ( -+ pExpr->iDocid==pCsr->iPrevId && pExpr->pPhrase->doclist.pList -+ ))){ - Fts3Phrase *pPhrase = pExpr->pPhrase; -- assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 ); - if( pExpr->bDeferred ){ - fts3EvalInvalidatePoslist(pPhrase); - } -@@ -172544,7 +194819,7 @@ static int fts3EvalNext(Fts3Cursor *pCsr){ - } - - /* --** Restart interation for expression pExpr so that the next call to -+** Restart iteration for expression pExpr so that the next call to - ** fts3EvalNext() visits the first row. Do not allow incremental - ** loading or merging of phrase doclists for this iteration. - ** -@@ -172587,6 +194862,24 @@ static void fts3EvalRestart( - } - } - -+/* -+** Expression node pExpr is an MSR phrase. This function restarts pExpr -+** so that it is a regular phrase query, not an MSR. SQLITE_OK is returned -+** if successful, or an SQLite error code otherwise. -+*/ -+SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor *pCsr, Fts3Expr *pExpr){ -+ int rc = SQLITE_OK; -+ if( pExpr->bEof==0 ){ -+ i64 iDocid = pExpr->iDocid; -+ fts3EvalRestart(pCsr, pExpr, &rc); -+ while( rc==SQLITE_OK && pExpr->iDocid!=iDocid ){ -+ fts3EvalNextRow(pCsr, pExpr, &rc); -+ if( pExpr->bEof ) rc = FTS_CORRUPT_VTAB; -+ } -+ } -+ return rc; -+} -+ - /* - ** After allocating the Fts3Expr.aMI[] array for each phrase in the - ** expression rooted at pExpr, the cursor iterates through all rows matched -@@ -172626,6 +194919,22 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr, int nCol){ - } - } - -+/* -+** This is an sqlite3Fts3ExprIterate() callback. If the Fts3Expr.aMI[] array -+** has not yet been allocated, allocate and zero it. Otherwise, just zero -+** it. -+*/ -+static int fts3AllocateMSI(Fts3Expr *pExpr, int iPhrase, void *pCtx){ -+ Fts3Table *pTab = (Fts3Table*)pCtx; -+ UNUSED_PARAMETER(iPhrase); -+ if( pExpr->aMI==0 ){ -+ pExpr->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); -+ if( pExpr->aMI==0 ) return SQLITE_NOMEM; -+ } -+ memset(pExpr->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); -+ return SQLITE_OK; -+} -+ - /* - ** Expression pExpr must be of type FTSQUERY_PHRASE. - ** -@@ -172647,7 +194956,6 @@ static int fts3EvalGatherStats( - if( pExpr->aMI==0 ){ - Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; - Fts3Expr *pRoot; /* Root of NEAR expression */ -- Fts3Expr *p; /* Iterator used for several purposes */ - - sqlite3_int64 iPrevId = pCsr->iPrevId; - sqlite3_int64 iDocid; -@@ -172655,7 +194963,9 @@ static int fts3EvalGatherStats( - - /* Find the root of the NEAR expression */ - pRoot = pExpr; -- while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){ -+ while( pRoot->pParent -+ && (pRoot->pParent->eType==FTSQUERY_NEAR || pRoot->bDeferred) -+ ){ - pRoot = pRoot->pParent; - } - iDocid = pRoot->iDocid; -@@ -172663,14 +194973,8 @@ static int fts3EvalGatherStats( - assert( pRoot->bStart ); - - /* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */ -- for(p=pRoot; p; p=p->pLeft){ -- Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight); -- assert( pE->aMI==0 ); -- pE->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); -- if( !pE->aMI ) return SQLITE_NOMEM; -- memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); -- } -- -+ rc = sqlite3Fts3ExprIterate(pRoot, fts3AllocateMSI, (void*)pTab); -+ if( rc!=SQLITE_OK ) return rc; - fts3EvalRestart(pCsr, pRoot, &rc); - - while( pCsr->isEof==0 && rc==SQLITE_OK ){ -@@ -172826,6 +195130,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( - u8 bTreeEof = 0; - Fts3Expr *p; /* Used to iterate from pExpr to root */ - Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */ -+ Fts3Expr *pRun; /* Closest non-deferred ancestor of pNear */ - int bMatch; - - /* Check if this phrase descends from an OR expression node. If not, -@@ -172840,22 +195145,30 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( - if( p->bEof ) bTreeEof = 1; - } - if( bOr==0 ) return SQLITE_OK; -+ pRun = pNear; -+ while( pRun->bDeferred ){ -+ assert( pRun->pParent ); -+ pRun = pRun->pParent; -+ } - - /* This is the descendent of an OR node. In this case we cannot use - ** an incremental phrase. Load the entire doclist for the phrase - ** into memory in this case. */ - if( pPhrase->bIncr ){ -- int bEofSave = pNear->bEof; -- fts3EvalRestart(pCsr, pNear, &rc); -- while( rc==SQLITE_OK && !pNear->bEof ){ -- fts3EvalNextRow(pCsr, pNear, &rc); -- if( bEofSave==0 && pNear->iDocid==iDocid ) break; -+ int bEofSave = pRun->bEof; -+ fts3EvalRestart(pCsr, pRun, &rc); -+ while( rc==SQLITE_OK && !pRun->bEof ){ -+ fts3EvalNextRow(pCsr, pRun, &rc); -+ if( bEofSave==0 && pRun->iDocid==iDocid ) break; - } - assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); -+ if( rc==SQLITE_OK && pRun->bEof!=bEofSave ){ -+ rc = FTS_CORRUPT_VTAB; -+ } - } - if( bTreeEof ){ -- while( rc==SQLITE_OK && !pNear->bEof ){ -- fts3EvalNextRow(pCsr, pNear, &rc); -+ while( rc==SQLITE_OK && !pRun->bEof ){ -+ fts3EvalNextRow(pCsr, pRun, &rc); - } - } - if( rc!=SQLITE_OK ) return rc; -@@ -172954,7 +195267,7 @@ SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ - } - #endif - --#if !SQLITE_CORE -+#if !defined(SQLITE_CORE) - /* - ** Initialize API pointer table, if required. - */ -@@ -173274,6 +195587,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ - if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; - memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); - iCol = 0; -+ rc = SQLITE_OK; - - while( iaStat[iCol+1].nDoc++; - eState = 2; -@@ -173325,7 +195643,6 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ - } - - pCsr->iCol = 0; -- rc = SQLITE_OK; - }else{ - pCsr->isEof = 1; - } -@@ -173383,6 +195700,7 @@ static int fts3auxFilterMethod( - sqlite3Fts3SegReaderFinish(&pCsr->csr); - sqlite3_free((void *)pCsr->filter.zTerm); - sqlite3_free(pCsr->aStat); -+ sqlite3_free(pCsr->zStop); - memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); - - pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; -@@ -173517,7 +195835,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ -- 0 /* xShadowName */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - int rc; /* Return code */ - -@@ -173653,7 +195972,7 @@ static int fts3isspace(char c){ - ** zero the memory before returning a pointer to it. If unsuccessful, - ** return NULL. - */ --static void *fts3MallocZero(sqlite3_int64 nByte){ -+SQLITE_PRIVATE void *sqlite3Fts3MallocZero(sqlite3_int64 nByte){ - void *pRet = sqlite3_malloc64(nByte); - if( pRet ) memset(pRet, 0, nByte); - return pRet; -@@ -173692,6 +196011,23 @@ SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer( - */ - static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); - -+/* -+** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis -+** is defined, search for '(' and ')' as well. Return the index of the first -+** such character in the buffer. If there is no such character, return -1. -+*/ -+static int findBarredChar(const char *z, int n){ -+ int ii; -+ for(ii=0; iiiLangid, z, i, &pCursor); -+ *pnConsumed = n; -+ rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); - if( rc==SQLITE_OK ){ - const char *zToken; - int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; -@@ -173733,8 +196062,19 @@ static int getNextToken( - - rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); - if( rc==SQLITE_OK ){ -- nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; -- pRet = (Fts3Expr *)fts3MallocZero(nByte); -+ /* Check that this tokenization did not gobble up any " characters. Or, -+ ** if enable_parenthesis is true, that it did not gobble up any -+ ** open or close parenthesis characters either. If it did, call -+ ** getNextToken() again, but pass only that part of the input buffer -+ ** up to the first such character. */ -+ int iBarred = findBarredChar(z, iEnd); -+ if( iBarred>=0 ){ -+ pModule->xClose(pCursor); -+ return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed); -+ } -+ -+ nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken; -+ pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); - if( !pRet ){ - rc = SQLITE_NOMEM; - }else{ -@@ -173743,7 +196083,7 @@ static int getNextToken( - pRet->pPhrase->nToken = 1; - pRet->pPhrase->iColumn = iCol; - pRet->pPhrase->aToken[0].n = nToken; -- pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; -+ pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1]; - memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); - - if( iEnd=0 ){ -+ *pnConsumed = iBarred; -+ } - rc = SQLITE_OK; - } - -@@ -173814,9 +196158,9 @@ static int getNextString( - Fts3Expr *p = 0; - sqlite3_tokenizer_cursor *pCursor = 0; - char *zTemp = 0; -- int nTemp = 0; -+ i64 nTemp = 0; - -- const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); -+ const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1); - int nToken = 0; - - /* The final Fts3Expr data structure, including the Fts3Phrase, -@@ -173850,10 +196194,11 @@ static int getNextString( - Fts3PhraseToken *pToken; - - p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); -- if( !p ) goto no_mem; -- - zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); -- if( !zTemp ) goto no_mem; -+ if( !zTemp || !p ){ -+ rc = SQLITE_NOMEM; -+ goto getnextstring_out; -+ } - - assert( nToken==ii ); - pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; -@@ -173868,9 +196213,6 @@ static int getNextString( - nToken = ii+1; - } - } -- -- pModule->xClose(pCursor); -- pCursor = 0; - } - - if( rc==SQLITE_DONE ){ -@@ -173878,7 +196220,10 @@ static int getNextString( - char *zBuf = 0; - - p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); -- if( !p ) goto no_mem; -+ if( !p ){ -+ rc = SQLITE_NOMEM; -+ goto getnextstring_out; -+ } - memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); - p->eType = FTSQUERY_PHRASE; - p->pPhrase = (Fts3Phrase *)&p[1]; -@@ -173886,11 +196231,9 @@ static int getNextString( - p->pPhrase->nToken = nToken; - - zBuf = (char *)&p->pPhrase->aToken[nToken]; -+ assert( nTemp==0 || zTemp ); - if( zTemp ){ - memcpy(zBuf, zTemp, nTemp); -- sqlite3_free(zTemp); -- }else{ -- assert( nTemp==0 ); - } - - for(jj=0; jjpPhrase->nToken; jj++){ -@@ -173900,17 +196243,17 @@ static int getNextString( - rc = SQLITE_OK; - } - -- *ppExpr = p; -- return rc; --no_mem: -- -+ getnextstring_out: - if( pCursor ){ - pModule->xClose(pCursor); - } - sqlite3_free(zTemp); -- sqlite3_free(p); -- *ppExpr = 0; -- return SQLITE_NOMEM; -+ if( rc!=SQLITE_OK ){ -+ sqlite3_free(p); -+ p = 0; -+ } -+ *ppExpr = p; -+ return rc; - } - - /* -@@ -173989,7 +196332,7 @@ static int getNextNode( - if( fts3isspace(cNext) - || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 - ){ -- pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr)); -+ pRet = (Fts3Expr *)sqlite3Fts3MallocZero(sizeof(Fts3Expr)); - if( !pRet ){ - return SQLITE_NOMEM; - } -@@ -174024,6 +196367,11 @@ static int getNextNode( - if( *zInput=='(' ){ - int nConsumed = 0; - pParse->nNest++; -+#if !defined(SQLITE_MAX_EXPR_DEPTH) -+ if( pParse->nNest>1000 ) return SQLITE_ERROR; -+#elif SQLITE_MAX_EXPR_DEPTH>0 -+ if( pParse->nNest>SQLITE_MAX_EXPR_DEPTH ) return SQLITE_ERROR; -+#endif - rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed); - *pnConsumed = (int)(zInput - z) + 1 + nConsumed; - return rc; -@@ -174163,7 +196511,7 @@ static int fts3ExprParse( - && p->eType==FTSQUERY_PHRASE && pParse->isNot - ){ - /* Create an implicit NOT operator. */ -- Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); -+ Fts3Expr *pNot = sqlite3Fts3MallocZero(sizeof(Fts3Expr)); - if( !pNot ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_NOMEM; -@@ -174184,7 +196532,7 @@ static int fts3ExprParse( - - /* The isRequirePhrase variable is set to true if a phrase or - ** an expression contained in parenthesis is required. If a -- ** binary operator (AND, OR, NOT or NEAR) is encounted when -+ ** binary operator (AND, OR, NOT or NEAR) is encountered when - ** isRequirePhrase is set, this is a syntax error. - */ - if( !isPhrase && isRequirePhrase ){ -@@ -174197,7 +196545,7 @@ static int fts3ExprParse( - /* Insert an implicit AND operator. */ - Fts3Expr *pAnd; - assert( pRet && pPrev ); -- pAnd = fts3MallocZero(sizeof(Fts3Expr)); -+ pAnd = sqlite3Fts3MallocZero(sizeof(Fts3Expr)); - if( !pAnd ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_NOMEM; -@@ -174766,7 +197114,6 @@ static void fts3ExprTestCommon( - } - - if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ -- sqlite3Fts3ExprFree(pExpr); - sqlite3_result_error(context, "Error parsing expression", -1); - }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ - sqlite3_result_error_nomem(context); -@@ -175009,7 +197356,7 @@ static void fts3HashInsertElement( - } - - --/* Resize the hash table so that it cantains "new_size" buckets. -+/* Resize the hash table so that it contains "new_size" buckets. - ** "new_size" must be a power of 2. The hash table might fail - ** to resize if sqliteMalloc() fails. - ** -@@ -175464,7 +197811,7 @@ static int star_oh(const char *z){ - - /* - ** If the word ends with zFrom and xCond() is true for the stem --** of the word that preceeds the zFrom ending, then change the -+** of the word that precedes the zFrom ending, then change the - ** ending to zTo. - ** - ** The input word *pz and zFrom are both in reverse order. zTo -@@ -175829,7 +198176,7 @@ static int porterNext( - if( n>c->nAllocated ){ - char *pNew; - c->nAllocated = n+20; -- pNew = sqlite3_realloc(c->zToken, c->nAllocated); -+ pNew = sqlite3_realloc64(c->zToken, c->nAllocated); - if( !pNew ) return SQLITE_NOMEM; - c->zToken = pNew; - } -@@ -176099,11 +198446,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( - - #ifdef SQLITE_TEST - --#if defined(INCLUDE_SQLITE_TCL_H) --# include "sqlite_tcl.h" --#else --# include "tcl.h" --#endif -+#include "tclsqlite.h" - /* #include */ - - /* -@@ -176581,7 +198924,7 @@ static int simpleNext( - if( n>c->nTokenAllocated ){ - char *pNew; - c->nTokenAllocated = n+20; -- pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated); -+ pNew = sqlite3_realloc64(c->pToken, c->nTokenAllocated); - if( !pNew ) return SQLITE_NOMEM; - c->pToken = pNew; - } -@@ -176979,7 +199322,7 @@ static int fts3tokFilterMethod( - fts3tokResetCursor(pCsr); - if( idxNum==1 ){ - const char *zByte = (const char *)sqlite3_value_text(apVal[0]); -- int nByte = sqlite3_value_bytes(apVal[0]); -+ sqlite3_int64 nByte = sqlite3_value_bytes(apVal[0]); - pCsr->zInput = sqlite3_malloc64(nByte+1); - if( pCsr->zInput==0 ){ - rc = SQLITE_NOMEM; -@@ -177053,7 +199396,7 @@ static int fts3tokRowidMethod( - ** Register the fts3tok module with database connection db. Return SQLITE_OK - ** if successful or an error code if sqlite3_create_module() fails. - */ --SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){ -+SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){ - static const sqlite3_module fts3tok_module = { - 0, /* iVersion */ - fts3tokConnectMethod, /* xCreate */ -@@ -177078,11 +199421,14 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ -- 0 /* xShadowName */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - int rc; /* Return code */ - -- rc = sqlite3_create_module(db, "fts3tokenize", &fts3tok_module, (void*)pHash); -+ rc = sqlite3_create_module_v2( -+ db, "fts3tokenize", &fts3tok_module, (void*)pHash, xDestroy -+ ); - return rc; - } - -@@ -177741,7 +200087,7 @@ static int fts3PendingListAppendVarint( - - /* Allocate or grow the PendingList as required. */ - if( !p ){ -- p = sqlite3_malloc(sizeof(*p) + 100); -+ p = sqlite3_malloc64(sizeof(*p) + 100); - if( !p ){ - return SQLITE_NOMEM; - } -@@ -177750,14 +200096,14 @@ static int fts3PendingListAppendVarint( - p->nData = 0; - } - else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){ -- int nNew = p->nSpace * 2; -- p = sqlite3_realloc(p, sizeof(*p) + nNew); -+ i64 nNew = p->nSpace * 2; -+ p = sqlite3_realloc64(p, sizeof(*p) + nNew); - if( !p ){ - sqlite3_free(*pp); - *pp = 0; - return SQLITE_NOMEM; - } -- p->nSpace = nNew; -+ p->nSpace = (int)nNew; - p->aData = (char *)&p[1]; - } - -@@ -178314,7 +200660,7 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock( - int nByte = sqlite3_blob_bytes(p->pSegments); - *pnBlob = nByte; - if( paBlob ){ -- char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING); -+ char *aByte = sqlite3_malloc64((i64)nByte + FTS3_NODE_PADDING); - if( !aByte ){ - rc = SQLITE_NOMEM; - }else{ -@@ -178427,9 +200773,19 @@ static int fts3SegReaderNext( - char *aCopy; - PendingList *pList = (PendingList *)fts3HashData(pElem); - int nCopy = pList->nData+1; -- pReader->zTerm = (char *)fts3HashKey(pElem); -- pReader->nTerm = fts3HashKeysize(pElem); -- aCopy = (char*)sqlite3_malloc(nCopy); -+ -+ int nTerm = fts3HashKeysize(pElem); -+ if( (nTerm+1)>pReader->nTermAlloc ){ -+ sqlite3_free(pReader->zTerm); -+ pReader->zTerm = (char*)sqlite3_malloc64(((i64)nTerm+1)*2); -+ if( !pReader->zTerm ) return SQLITE_NOMEM; -+ pReader->nTermAlloc = (nTerm+1)*2; -+ } -+ memcpy(pReader->zTerm, fts3HashKey(pElem), nTerm); -+ pReader->zTerm[nTerm] = '\0'; -+ pReader->nTerm = nTerm; -+ -+ aCopy = (char*)sqlite3_malloc64(nCopy); - if( !aCopy ) return SQLITE_NOMEM; - memcpy(aCopy, pList->aData, nCopy); - pReader->nNode = pReader->nDoclist = nCopy; -@@ -178681,9 +201037,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrOvfl( - */ - SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){ - if( pReader ){ -- if( !fts3SegReaderIsPending(pReader) ){ -- sqlite3_free(pReader->zTerm); -- } -+ sqlite3_free(pReader->zTerm); - if( !fts3SegReaderIsRootOnly(pReader) ){ - sqlite3_free(pReader->aNode); - } -@@ -178718,7 +201072,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( - nExtra = nRoot + FTS3_NODE_PADDING; - } - -- pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); -+ pReader = (Fts3SegReader *)sqlite3_malloc64(sizeof(Fts3SegReader) + nExtra); - if( !pReader ){ - return SQLITE_NOMEM; - } -@@ -178810,7 +201164,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( - if( nElem==nAlloc ){ - Fts3HashElem **aElem2; - nAlloc += 16; -- aElem2 = (Fts3HashElem **)sqlite3_realloc( -+ aElem2 = (Fts3HashElem **)sqlite3_realloc64( - aElem, nAlloc*sizeof(Fts3HashElem *) - ); - if( !aElem2 ){ -@@ -178899,7 +201253,7 @@ static int fts3SegReaderCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ - if( rc==0 ){ - rc = pRhs->iIdx - pLhs->iIdx; - } -- assert( rc!=0 ); -+ assert_fts3_nc( rc!=0 ); - return rc; - } - -@@ -179095,8 +201449,8 @@ static int fts3PrefixCompress( - int nNext /* Size of buffer zNext in bytes */ - ){ - int n; -- UNUSED_PARAMETER(nNext); -- for(n=0; naData==(char *)&pTree[1] ); -- pTree->aData = (char *)sqlite3_malloc(nReq); -+ pTree->aData = (char *)sqlite3_malloc64(nReq); - if( !pTree->aData ){ - return SQLITE_NOMEM; - } -@@ -179162,7 +201516,7 @@ static int fts3NodeAddTerm( - - if( isCopyTerm ){ - if( pTree->nMalloczMalloc, nTerm*2); -+ char *zNew = sqlite3_realloc64(pTree->zMalloc, (i64)nTerm*2); - if( !zNew ){ - return SQLITE_NOMEM; - } -@@ -179188,7 +201542,7 @@ static int fts3NodeAddTerm( - ** now. Instead, the term is inserted into the parent of pTree. If pTree - ** has no parent, one is created here. - */ -- pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize); -+ pNew = (SegmentNode *)sqlite3_malloc64(sizeof(SegmentNode) + p->nNodeSize); - if( !pNew ){ - return SQLITE_NOMEM; - } -@@ -179326,7 +201680,7 @@ static int fts3SegWriterAdd( - ){ - int nPrefix; /* Size of term prefix in bytes */ - int nSuffix; /* Size of term suffix in bytes */ -- int nReq; /* Number of bytes required on leaf page */ -+ i64 nReq; /* Number of bytes required on leaf page */ - int nData; - SegmentWriter *pWriter = *ppWriter; - -@@ -179335,13 +201689,13 @@ static int fts3SegWriterAdd( - sqlite3_stmt *pStmt; - - /* Allocate the SegmentWriter structure */ -- pWriter = (SegmentWriter *)sqlite3_malloc(sizeof(SegmentWriter)); -+ pWriter = (SegmentWriter *)sqlite3_malloc64(sizeof(SegmentWriter)); - if( !pWriter ) return SQLITE_NOMEM; - memset(pWriter, 0, sizeof(SegmentWriter)); - *ppWriter = pWriter; - - /* Allocate a buffer in which to accumulate data */ -- pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize); -+ pWriter->aData = (char *)sqlite3_malloc64(p->nNodeSize); - if( !pWriter->aData ) return SQLITE_NOMEM; - pWriter->nSize = p->nNodeSize; - -@@ -179416,7 +201770,7 @@ static int fts3SegWriterAdd( - ** the buffer to make it large enough. - */ - if( nReq>pWriter->nSize ){ -- char *aNew = sqlite3_realloc(pWriter->aData, nReq); -+ char *aNew = sqlite3_realloc64(pWriter->aData, nReq); - if( !aNew ) return SQLITE_NOMEM; - pWriter->aData = aNew; - pWriter->nSize = nReq; -@@ -179441,7 +201795,7 @@ static int fts3SegWriterAdd( - */ - if( isCopyTerm ){ - if( nTerm>pWriter->nMalloc ){ -- char *zNew = sqlite3_realloc(pWriter->zMalloc, nTerm*2); -+ char *zNew = sqlite3_realloc64(pWriter->zMalloc, (i64)nTerm*2); - if( !zNew ){ - return SQLITE_NOMEM; - } -@@ -179749,18 +202103,20 @@ static void fts3ColumnFilter( - static int fts3MsrBufferData( - Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */ - char *pList, -- int nList -+ i64 nList - ){ -- if( nList>pMsr->nBuffer ){ -+ if( (nList+FTS3_NODE_PADDING)>pMsr->nBuffer ){ - char *pNew; -- pMsr->nBuffer = nList*2; -- pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer); -+ int nNew = nList*2 + FTS3_NODE_PADDING; -+ pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, nNew); - if( !pNew ) return SQLITE_NOMEM; - pMsr->aBuffer = pNew; -+ pMsr->nBuffer = nNew; - } - - assert( nList>0 ); - memcpy(pMsr->aBuffer, pList, nList); -+ memset(&pMsr->aBuffer[nList], 0, FTS3_NODE_PADDING); - return SQLITE_OK; - } - -@@ -179810,7 +202166,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( - fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp); - - if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){ -- rc = fts3MsrBufferData(pMsr, pList, nList+1); -+ rc = fts3MsrBufferData(pMsr, pList, (i64)nList+1); - if( rc!=SQLITE_OK ) return rc; - assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); - pList = pMsr->aBuffer; -@@ -179947,11 +202303,11 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){ - return SQLITE_OK; - } - --static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){ -+static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, i64 nReq){ - if( nReq>pCsr->nBuffer ){ - char *aNew; - pCsr->nBuffer = nReq*2; -- aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); -+ aNew = sqlite3_realloc64(pCsr->aBuffer, pCsr->nBuffer); - if( !aNew ){ - return SQLITE_NOMEM; - } -@@ -180042,7 +202398,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( - ){ - pCsr->nDoclist = apSegment[0]->nDoclist; - if( fts3SegReaderIsPending(apSegment[0]) ){ -- rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); -+ rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, -+ (i64)pCsr->nDoclist); - pCsr->aDoclist = pCsr->aBuffer; - }else{ - pCsr->aDoclist = apSegment[0]->aDoclist; -@@ -180095,7 +202452,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( - - nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); - -- rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist); -+ rc = fts3GrowSegReaderBuffer(pCsr, -+ (i64)nByte+nDoclist+FTS3_NODE_PADDING); - if( rc ) return rc; - - if( isFirst ){ -@@ -180121,7 +202479,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( - fts3SegReaderSort(apSegment, nMerge, j, xCmp); - } - if( nDoclist>0 ){ -- rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING); -+ rc = fts3GrowSegReaderBuffer(pCsr, (i64)nDoclist+FTS3_NODE_PADDING); - if( rc ) return rc; - memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING); - pCsr->aDoclist = pCsr->aBuffer; -@@ -180405,7 +202763,6 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ - rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING); - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - } -- sqlite3Fts3PendingTermsClear(p); - - /* Determine the auto-incr-merge setting if unknown. If enabled, - ** estimate the number of leaf blocks of content to be written -@@ -180427,6 +202784,10 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ - rc = sqlite3_reset(pStmt); - } - } -+ -+ if( rc==SQLITE_OK ){ -+ sqlite3Fts3PendingTermsClear(p); -+ } - return rc; - } - -@@ -180834,7 +203195,7 @@ struct NodeReader { - static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){ - if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){ - int nAlloc = nMin; -- char *a = (char *)sqlite3_realloc(pBlob->a, nAlloc); -+ char *a = (char *)sqlite3_realloc64(pBlob->a, nAlloc); - if( a ){ - pBlob->nAlloc = nAlloc; - pBlob->a = a; -@@ -180875,7 +203236,7 @@ static int nodeReaderNext(NodeReader *p){ - return FTS_CORRUPT_VTAB; - } - blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); -- if( rc==SQLITE_OK ){ -+ if( rc==SQLITE_OK && ALWAYS(p->term.a!=0) ){ - memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix); - p->term.n = nPrefix+nSuffix; - p->iOff += nSuffix; -@@ -180983,6 +203344,8 @@ static int fts3IncrmergePush( - pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix); - } - pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix); -+ assert( nPrefix+nSuffix<=nTerm ); -+ assert( nPrefix>=0 ); - memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix); - pBlk->n += nSuffix; - -@@ -181031,7 +203394,7 @@ static int fts3IncrmergePush( - ** - ** It is assumed that the buffer associated with pNode is already large - ** enough to accommodate the new entry. The buffer associated with pPrev --** is extended by this function if requrired. -+** is extended by this function if required. - ** - ** If an error (i.e. OOM condition) occurs, an SQLite error code is - ** returned. Otherwise, SQLITE_OK. -@@ -181056,6 +203419,8 @@ static int fts3AppendToNode( - - blobGrowBuffer(pPrev, nTerm, &rc); - if( rc!=SQLITE_OK ) return rc; -+ assert( pPrev!=0 ); -+ assert( pPrev->a!=0 ); - - nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm); - nSuffix = nTerm - nPrefix; -@@ -181105,15 +203470,20 @@ static int fts3IncrmergeAppend( - pLeaf = &pWriter->aNodeWriter[0]; - nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm); - nSuffix = nTerm - nPrefix; -+ if(nSuffix<=0 ) return FTS_CORRUPT_VTAB; - - nSpace = sqlite3Fts3VarintLen(nPrefix); - nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix; - nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist; - - /* If the current block is not empty, and if adding this term/doclist -- ** to the current block would make it larger than Fts3Table.nNodeSize -- ** bytes, write this block out to the database. */ -- if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){ -+ ** to the current block would make it larger than Fts3Table.nNodeSize bytes, -+ ** and if there is still room for another leaf page, write this block out to -+ ** the database. */ -+ if( pLeaf->block.n>0 -+ && (pLeaf->block.n + nSpace)>p->nNodeSize -+ && pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst) -+ ){ - rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n); - pWriter->nWork++; - -@@ -181269,7 +203639,11 @@ static int fts3TermCmp( - int nCmp = MIN(nLhs, nRhs); - int res; - -- res = (nCmp ? memcmp(zLhs, zRhs, nCmp) : 0); -+ if( nCmp && ALWAYS(zLhs) && ALWAYS(zRhs) ){ -+ res = memcmp(zLhs, zRhs, nCmp); -+ }else{ -+ res = 0; -+ } - if( res==0 ) res = nLhs - nRhs; - - return res; -@@ -181420,6 +203794,7 @@ static int fts3IncrmergeLoad( - - for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){ - NodeReader reader; -+ memset(&reader, 0, sizeof(reader)); - pNode = &pWriter->aNodeWriter[i]; - - if( pNode->block.a){ -@@ -181427,14 +203802,17 @@ static int fts3IncrmergeLoad( - while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader); - blobGrowBuffer(&pNode->key, reader.term.n, &rc); - if( rc==SQLITE_OK ){ -- memcpy(pNode->key.a, reader.term.a, reader.term.n); -+ assert_fts3_nc( reader.term.n>0 || reader.aNode==0 ); -+ if( reader.term.n>0 ){ -+ memcpy(pNode->key.a, reader.term.a, reader.term.n); -+ } - pNode->key.n = reader.term.n; - if( i>0 ){ - char *aBlock = 0; - int nBlock = 0; - pNode = &pWriter->aNodeWriter[i-1]; - pNode->iBlock = reader.iChild; -- rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0); -+ rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0); - blobGrowBuffer(&pNode->block, - MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc - ); -@@ -181621,7 +203999,7 @@ static int fts3RepackSegdirLevel( - if( nIdx>=nAlloc ){ - int *aNew; - nAlloc += 16; -- aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int)); -+ aNew = sqlite3_realloc64(aIdx, nAlloc*sizeof(int)); - if( !aNew ){ - rc = SQLITE_NOMEM; - break; -@@ -181910,7 +204288,7 @@ static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){ - if( aHint ){ - blobGrowBuffer(pHint, nHint, &rc); - if( rc==SQLITE_OK ){ -- memcpy(pHint->a, aHint, nHint); -+ if( ALWAYS(pHint->a!=0) ) memcpy(pHint->a, aHint, nHint); - pHint->n = nHint; - } - } -@@ -181995,7 +204373,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){ - - /* Allocate space for the cursor, filter and writer objects */ - const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter); -- pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc); -+ pWriter = (IncrmergeWriter *)sqlite3_malloc64(nAlloc); - if( !pWriter ) return SQLITE_NOMEM; - pFilter = (Fts3SegFilter *)&pWriter[1]; - pCsr = (Fts3MultiSegReader *)&pFilter[1]; -@@ -182287,7 +204665,7 @@ static u64 fts3ChecksumIndex( - int rc; - u64 cksum = 0; - -- assert( *pRc==SQLITE_OK ); -+ if( *pRc ) return 0; - - memset(&filter, 0, sizeof(filter)); - memset(&csr, 0, sizeof(csr)); -@@ -182354,7 +204732,7 @@ static u64 fts3ChecksumIndex( - ** If an error occurs (e.g. an OOM or IO error), return an SQLite error - ** code. The final value of *pbOk is undefined in this case. - */ --static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ -+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){ - int rc = SQLITE_OK; /* Return code */ - u64 cksum1 = 0; /* Checksum based on FTS index contents */ - u64 cksum2 = 0; /* Checksum based on %_content contents */ -@@ -182432,7 +204810,12 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ - sqlite3_finalize(pStmt); - } - -- *pbOk = (cksum1==cksum2); -+ if( rc==SQLITE_CORRUPT_VTAB ){ -+ rc = SQLITE_OK; -+ *pbOk = 0; -+ }else{ -+ *pbOk = (rc==SQLITE_OK && cksum1==cksum2); -+ } - return rc; - } - -@@ -182472,7 +204855,7 @@ static int fts3DoIntegrityCheck( - ){ - int rc; - int bOk = 0; -- rc = fts3IntegrityCheck(p, &bOk); -+ rc = sqlite3Fts3IntegrityCheck(p, &bOk); - if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB; - return rc; - } -@@ -182502,8 +204885,11 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ - rc = fts3DoIncrmerge(p, &zVal[6]); - }else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){ - rc = fts3DoAutoincrmerge(p, &zVal[10]); -+ }else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){ -+ rc = sqlite3Fts3PendingTermsFlush(p); -+ } - #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) -- }else{ -+ else{ - int v; - if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ - v = atoi(&zVal[9]); -@@ -182521,8 +204907,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ - if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v; - rc = SQLITE_OK; - } --#endif - } -+#endif - return rc; - } - -@@ -182631,7 +205017,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList( - return SQLITE_OK; - } - -- pRet = (char *)sqlite3_malloc(p->pList->nData); -+ pRet = (char *)sqlite3_malloc64(p->pList->nData); - if( !pRet ) return SQLITE_NOMEM; - - nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy); -@@ -182651,7 +205037,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken( - int iCol /* Column that token must appear in (or -1) */ - ){ - Fts3DeferredToken *pDeferred; -- pDeferred = sqlite3_malloc(sizeof(*pDeferred)); -+ pDeferred = sqlite3_malloc64(sizeof(*pDeferred)); - if( !pDeferred ){ - return SQLITE_NOMEM; - } -@@ -182671,7 +205057,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken( - /* - ** SQLite value pRowid contains the rowid of a row that may or may not be - ** present in the FTS3 table. If it is, delete it and adjust the contents --** of subsiduary data structures accordingly. -+** of subsidiary data structures accordingly. - */ - static int fts3DeleteByRowid( - Fts3Table *p, -@@ -182906,6 +205292,10 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ - /* #include */ - /* #include */ - -+#ifndef SQLITE_AMALGAMATION -+typedef sqlite3_int64 i64; -+#endif -+ - /* - ** Characters that may appear in the second argument to matchinfo(). - */ -@@ -182926,7 +205316,7 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ - - - /* --** Used as an fts3ExprIterate() context when loading phrase doclists to -+** Used as an sqlite3Fts3ExprIterate() context when loading phrase doclists to - ** Fts3Expr.aDoclist[]/nDoclist. - */ - typedef struct LoadDoclistCtx LoadDoclistCtx; -@@ -182956,9 +205346,9 @@ struct SnippetIter { - struct SnippetPhrase { - int nToken; /* Number of tokens in phrase */ - char *pList; /* Pointer to start of phrase position list */ -- int iHead; /* Next value in position list */ -+ i64 iHead; /* Next value in position list */ - char *pHead; /* Position list data following iHead */ -- int iTail; /* Next value in trailing position list */ -+ i64 iTail; /* Next value in trailing position list */ - char *pTail; /* Position list data following iTail */ - }; - -@@ -182970,7 +205360,7 @@ struct SnippetFragment { - }; - - /* --** This type is used as an fts3ExprIterate() context object while -+** This type is used as an sqlite3Fts3ExprIterate() context object while - ** accumulating the data returned by the matchinfo() function. - */ - typedef struct MatchInfo MatchInfo; -@@ -182993,9 +205383,13 @@ struct MatchinfoBuffer { - int nElem; - int bGlobal; /* Set if global data is loaded */ - char *zMatchinfo; -- u32 aMatchinfo[1]; -+ u32 aMI[FLEXARRAY]; - }; - -+/* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */ -+#define SZ_MATCHINFOBUFFER(N) \ -+ (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64)) -+ - - /* - ** The snippet() and offsets() functions both return text values. An instance -@@ -183020,14 +205414,13 @@ struct StrBuffer { - static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ - MatchinfoBuffer *pRet; - sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) -- + sizeof(MatchinfoBuffer); -+ + SZ_MATCHINFOBUFFER(1); - sqlite3_int64 nStr = strlen(zMatchinfo); - -- pRet = sqlite3_malloc64(nByte + nStr+1); -+ pRet = sqlite3Fts3MallocZero(nByte + nStr+1); - if( pRet ){ -- memset(pRet, 0, nByte); -- pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; -- pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] -+ pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet; -+ pRet->aMI[1+nElem] = pRet->aMI[0] - + sizeof(u32)*((int)nElem+1); - pRet->nElem = (int)nElem; - pRet->zMatchinfo = ((char*)pRet) + nByte; -@@ -183041,10 +205434,10 @@ static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ - static void fts3MIBufferFree(void *p){ - MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); - -- assert( (u32*)p==&pBuf->aMatchinfo[1] -- || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2] -+ assert( (u32*)p==&pBuf->aMI[1] -+ || (u32*)p==&pBuf->aMI[pBuf->nElem+2] - ); -- if( (u32*)p==&pBuf->aMatchinfo[1] ){ -+ if( (u32*)p==&pBuf->aMI[1] ){ - pBuf->aRef[1] = 0; - }else{ - pBuf->aRef[2] = 0; -@@ -183061,18 +205454,18 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){ - - if( p->aRef[1]==0 ){ - p->aRef[1] = 1; -- aOut = &p->aMatchinfo[1]; -+ aOut = &p->aMI[1]; - xRet = fts3MIBufferFree; - } - else if( p->aRef[2]==0 ){ - p->aRef[2] = 1; -- aOut = &p->aMatchinfo[p->nElem+2]; -+ aOut = &p->aMI[p->nElem+2]; - xRet = fts3MIBufferFree; - }else{ - aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); - if( aOut ){ - xRet = sqlite3_free; -- if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); -+ if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32)); - } - } - -@@ -183082,7 +205475,7 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){ - - static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ - p->bGlobal = 1; -- memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32)); -+ memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32)); - } - - /* -@@ -183123,14 +205516,14 @@ SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){ - ** After it returns, *piPos contains the value of the next element of the - ** list and *pp is advanced to the following varint. - */ --static void fts3GetDeltaPosition(char **pp, int *piPos){ -+static void fts3GetDeltaPosition(char **pp, i64 *piPos){ - int iVal; - *pp += fts3GetVarint32(*pp, &iVal); - *piPos += (iVal-2); - } - - /* --** Helper function for fts3ExprIterate() (see below). -+** Helper function for sqlite3Fts3ExprIterate() (see below). - */ - static int fts3ExprIterate2( - Fts3Expr *pExpr, /* Expression to iterate phrases of */ -@@ -183164,7 +205557,7 @@ static int fts3ExprIterate2( - ** Otherwise, SQLITE_OK is returned after a callback has been made for - ** all eligible phrase nodes. - */ --static int fts3ExprIterate( -+SQLITE_PRIVATE int sqlite3Fts3ExprIterate( - Fts3Expr *pExpr, /* Expression to iterate phrases of */ - int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */ - void *pCtx /* Second argument to pass to callback */ -@@ -183173,10 +205566,9 @@ static int fts3ExprIterate( - return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx); - } - -- - /* --** This is an fts3ExprIterate() callback used while loading the doclists --** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also -+** This is an sqlite3Fts3ExprIterate() callback used while loading the -+** doclists for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also - ** fts3ExprLoadDoclists(). - */ - static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ -@@ -183208,9 +205600,9 @@ static int fts3ExprLoadDoclists( - int *pnToken /* OUT: Number of tokens in query */ - ){ - int rc; /* Return Code */ -- LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ -+ LoadDoclistCtx sCtx = {0,0,0}; /* Context for sqlite3Fts3ExprIterate() */ - sCtx.pCsr = pCsr; -- rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx); -+ rc = sqlite3Fts3ExprIterate(pCsr->pExpr,fts3ExprLoadDoclistsCb,(void*)&sCtx); - if( pnPhrase ) *pnPhrase = sCtx.nPhrase; - if( pnToken ) *pnToken = sCtx.nToken; - return rc; -@@ -183223,7 +205615,7 @@ static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ - } - static int fts3ExprPhraseCount(Fts3Expr *pExpr){ - int nPhrase = 0; -- (void)fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase); -+ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase); - return nPhrase; - } - -@@ -183232,10 +205624,10 @@ static int fts3ExprPhraseCount(Fts3Expr *pExpr){ - ** arguments so that it points to the first element with a value greater - ** than or equal to parameter iNext. - */ --static void fts3SnippetAdvance(char **ppIter, int *piIter, int iNext){ -+static void fts3SnippetAdvance(char **ppIter, i64 *piIter, int iNext){ - char *pIter = *ppIter; - if( pIter ){ -- int iIter = *piIter; -+ i64 iIter = *piIter; - - while( iIternSnippet>=0 ); - pIter->iCurrent = iStart = iEnd - pIter->nSnippet + 1; - for(i=0; inPhrase; i++){ - SnippetPhrase *pPhrase = &pIter->aPhrase[i]; -@@ -183318,7 +205711,7 @@ static void fts3SnippetDetails( - SnippetPhrase *pPhrase = &pIter->aPhrase[i]; - if( pPhrase->pTail ){ - char *pCsr = pPhrase->pTail; -- int iCsr = pPhrase->iTail; -+ i64 iCsr = pPhrase->iTail; - - while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){ - int j; -@@ -183333,7 +205726,7 @@ static void fts3SnippetDetails( - } - mCover |= mPhrase; - -- for(j=0; jnToken; j++){ -+ for(j=0; jnToken && jnSnippet; j++){ - mHighlight |= (mPos>>j); - } - -@@ -183351,8 +205744,9 @@ static void fts3SnippetDetails( - } - - /* --** This function is an fts3ExprIterate() callback used by fts3BestSnippet(). --** Each invocation populates an element of the SnippetIter.aPhrase[] array. -+** This function is an sqlite3Fts3ExprIterate() callback used by -+** fts3BestSnippet(). Each invocation populates an element of the -+** SnippetIter.aPhrase[] array. - */ - static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ - SnippetIter *p = (SnippetIter *)ctx; -@@ -183364,7 +205758,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ - rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr); - assert( rc==SQLITE_OK || pCsr==0 ); - if( pCsr ){ -- int iFirst = 0; -+ i64 iFirst = 0; - pPhrase->pList = pCsr; - fts3GetDeltaPosition(&pCsr, &iFirst); - if( iFirst<0 ){ -@@ -183429,11 +205823,10 @@ static int fts3BestSnippet( - ** the required space using malloc(). - */ - nByte = sizeof(SnippetPhrase) * nList; -- sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc64(nByte); -+ sIter.aPhrase = (SnippetPhrase *)sqlite3Fts3MallocZero(nByte); - if( !sIter.aPhrase ){ - return SQLITE_NOMEM; - } -- memset(sIter.aPhrase, 0, nByte); - - /* Initialize the contents of the SnippetIter object. Then iterate through - ** the set of phrases in the expression to populate the aPhrase[] array. -@@ -183443,7 +205836,9 @@ static int fts3BestSnippet( - sIter.nSnippet = nSnippet; - sIter.nPhrase = nList; - sIter.iCurrent = -1; -- rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter); -+ rc = sqlite3Fts3ExprIterate( -+ pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter -+ ); - if( rc==SQLITE_OK ){ - - /* Set the *pmSeen output variable. */ -@@ -183495,7 +205890,7 @@ static int fts3StringAppend( - } - - /* If there is insufficient space allocated at StrBuffer.z, use realloc() -- ** to grow the buffer until so that it is big enough to accomadate the -+ ** to grow the buffer until so that it is big enough to accommodate the - ** appended data. - */ - if( pStr->n+nAppend+1>=pStr->nAlloc ){ -@@ -183804,10 +206199,10 @@ static int fts3ExprLHitGather( - } - - /* --** fts3ExprIterate() callback used to collect the "global" matchinfo stats --** for a single query. -+** sqlite3Fts3ExprIterate() callback used to collect the "global" matchinfo -+** stats for a single query. - ** --** fts3ExprIterate() callback to load the 'global' elements of a -+** sqlite3Fts3ExprIterate() callback to load the 'global' elements of a - ** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements - ** of the matchinfo array that are constant for all rows returned by the - ** current query. -@@ -183842,7 +206237,7 @@ static int fts3ExprGlobalHitsCb( - } - - /* --** fts3ExprIterate() callback used to collect the "local" part of the -+** sqlite3Fts3ExprIterate() callback used to collect the "local" part of the - ** FTS3_MATCHINFO_HITS array. The local stats are those elements of the - ** array that are different for each row returned by the query. - */ -@@ -183907,16 +206302,16 @@ static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ - break; - - case FTS3_MATCHINFO_LHITS: -- nVal = pInfo->nCol * pInfo->nPhrase; -+ nVal = (size_t)pInfo->nCol * pInfo->nPhrase; - break; - - case FTS3_MATCHINFO_LHITS_BM: -- nVal = pInfo->nPhrase * ((pInfo->nCol + 31) / 32); -+ nVal = (size_t)pInfo->nPhrase * ((pInfo->nCol + 31) / 32); - break; - - default: - assert( cArg==FTS3_MATCHINFO_HITS ); -- nVal = pInfo->nCol * pInfo->nPhrase * 3; -+ nVal = (size_t)pInfo->nCol * pInfo->nPhrase * 3; - break; - } - -@@ -183997,10 +206392,12 @@ static int fts3MatchinfoLcsCb( - ** position list for the next column. - */ - static int fts3LcsIteratorAdvance(LcsIterator *pIter){ -- char *pRead = pIter->pRead; -+ char *pRead; - sqlite3_int64 iRead; - int rc = 0; - -+ if( NEVER(pIter==0) ) return 1; -+ pRead = pIter->pRead; - pRead += sqlite3Fts3GetVarint(pRead, &iRead); - if( iRead==0 || iRead==1 ){ - pRead = 0; -@@ -184034,10 +206431,9 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ - /* Allocate and populate the array of LcsIterator objects. The array - ** contains one element for each matchable phrase in the query. - **/ -- aIter = sqlite3_malloc64(sizeof(LcsIterator) * pCsr->nPhrase); -+ aIter = sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase); - if( !aIter ) return SQLITE_NOMEM; -- memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); -- (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); -+ (void)sqlite3Fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); - - for(i=0; inPhrase; i++){ - LcsIterator *pIter = &aIter[i]; -@@ -184214,11 +206610,11 @@ static int fts3MatchinfoValues( - rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0); - if( rc!=SQLITE_OK ) break; - } -- rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo); -+ rc = sqlite3Fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo); - sqlite3Fts3EvalTestDeferred(pCsr, &rc); - if( rc!=SQLITE_OK ) break; - } -- (void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo); -+ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo); - break; - } - } -@@ -184428,8 +206824,8 @@ typedef struct TermOffsetCtx TermOffsetCtx; - - struct TermOffset { - char *pList; /* Position-list */ -- int iPos; /* Position just read from pList */ -- int iOff; /* Offset of this term from read positions */ -+ i64 iPos; /* Position just read from pList */ -+ i64 iOff; /* Offset of this term from read positions */ - }; - - struct TermOffsetCtx { -@@ -184441,14 +206837,14 @@ struct TermOffsetCtx { - }; - - /* --** This function is an fts3ExprIterate() callback used by sqlite3Fts3Offsets(). -+** This function is an sqlite3Fts3ExprIterate() callback used by sqlite3Fts3Offsets(). - */ - static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ - TermOffsetCtx *p = (TermOffsetCtx *)ctx; - int nTerm; /* Number of tokens in phrase */ - int iTerm; /* For looping through nTerm phrase terms */ - char *pList; /* Pointer to position list for phrase */ -- int iPos = 0; /* First position in position-list */ -+ i64 iPos = 0; /* First position in position-list */ - int rc; - - UNUSED_PARAMETER(iPhrase); -@@ -184469,6 +206865,22 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ - return rc; - } - -+/* -+** If expression pExpr is a phrase expression that uses an MSR query, -+** restart it as a regular, non-incremental query. Return SQLITE_OK -+** if successful, or an SQLite error code otherwise. -+*/ -+static int fts3ExprRestartIfCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ -+ TermOffsetCtx *p = (TermOffsetCtx*)ctx; -+ int rc = SQLITE_OK; -+ UNUSED_PARAMETER(iPhrase); -+ if( pExpr->pPhrase && pExpr->pPhrase->bIncr ){ -+ rc = sqlite3Fts3MsrCancel(p->pCsr, pExpr); -+ pExpr->pPhrase->bIncr = 0; -+ } -+ return rc; -+} -+ - /* - ** Implementation of offsets() function. - */ -@@ -184497,7 +206909,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( - if( rc!=SQLITE_OK ) goto offsets_out; - - /* Allocate the array of TermOffset iterators. */ -- sCtx.aTerm = (TermOffset *)sqlite3_malloc64(sizeof(TermOffset)*nToken); -+ sCtx.aTerm = (TermOffset *)sqlite3Fts3MallocZero(sizeof(TermOffset)*nToken); - if( 0==sCtx.aTerm ){ - rc = SQLITE_NOMEM; - goto offsets_out; -@@ -184505,6 +206917,12 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( - sCtx.iDocid = pCsr->iPrevId; - sCtx.pCsr = pCsr; - -+ /* If a query restart will be required, do it here, rather than later of -+ ** after pointers to poslist buffers that may be invalidated by a restart -+ ** have been saved. */ -+ rc = sqlite3Fts3ExprIterate(pCsr->pExpr, fts3ExprRestartIfCb, (void*)&sCtx); -+ if( rc!=SQLITE_OK ) goto offsets_out; -+ - /* Loop through the table columns, appending offset information to - ** string-buffer res for each column. - */ -@@ -184518,13 +206936,15 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( - const char *zDoc; - int nDoc; - -- /* Initialize the contents of sCtx.aTerm[] for column iCol. There is -- ** no way that this operation can fail, so the return code from -- ** fts3ExprIterate() can be discarded. -+ /* Initialize the contents of sCtx.aTerm[] for column iCol. This -+ ** operation may fail if the database contains corrupt records. - */ - sCtx.iCol = iCol; - sCtx.iTerm = 0; -- (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx); -+ rc = sqlite3Fts3ExprIterate( -+ pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx -+ ); -+ if( rc!=SQLITE_OK ) goto offsets_out; - - /* Retreive the text stored in column iCol. If an SQL NULL is stored - ** in column iCol, jump immediately to the next iteration of the loop. -@@ -184925,6 +207345,7 @@ static int unicodeOpen( - pCsr->aInput = (const unsigned char *)aInput; - if( aInput==0 ){ - pCsr->nInput = 0; -+ pCsr->aInput = (const unsigned char*)""; - }else if( nInput<0 ){ - pCsr->nInput = (int)strlen(aInput); - }else{ -@@ -185422,7 +207843,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ - #endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */ - - /************** End of fts3_unicode2.c ***************************************/ --/************** Begin file json1.c *******************************************/ -+/************** Begin file json.c ********************************************/ - /* - ** 2015-08-12 - ** -@@ -185435,102 +207856,295 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ - ** - ****************************************************************************** - ** --** This SQLite extension implements JSON functions. The interface is --** modeled after MySQL JSON functions: --** --** https://dev.mysql.com/doc/refman/5.7/en/json.html -+** SQLite JSON functions. -+** -+** This file began as an extension in ext/misc/json1.c in 2015. That -+** extension proved so useful that it has now been moved into the core. -+** -+** The original design stored all JSON as pure text, canonical RFC-8259. -+** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16). -+** All generated JSON text still conforms strictly to RFC-8259, but text -+** with JSON-5 extensions is accepted as input. -+** -+** Beginning with version 3.45.0 (circa 2024-01-01), these routines also -+** accept BLOB values that have JSON encoded using a binary representation -+** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk -+** format for SQLite-JSONB is completely different and incompatible with -+** PostgreSQL-JSONB. -+** -+** Decoding and interpreting JSONB is still O(N) where N is the size of -+** the input, the same as text JSON. However, the constant of proportionality -+** for JSONB is much smaller due to faster parsing. The size of each -+** element in JSONB is encoded in its header, so there is no need to search -+** for delimiters using persnickety syntax rules. JSONB seems to be about -+** 3x faster than text JSON as a result. JSONB is also tends to be slightly -+** smaller than text JSON, by 5% or 10%, but there are corner cases where -+** JSONB can be slightly larger. So you are not far mistaken to say that -+** a JSONB blob is the same size as the equivalent RFC-8259 text. -+** -+** -+** THE JSONB ENCODING: -+** -+** Every JSON element is encoded in JSONB as a header and a payload. -+** The header is between 1 and 9 bytes in size. The payload is zero -+** or more bytes. -+** -+** The lower 4 bits of the first byte of the header determines the -+** element type: -+** -+** 0: NULL -+** 1: TRUE -+** 2: FALSE -+** 3: INT -- RFC-8259 integer literal -+** 4: INT5 -- JSON5 integer literal -+** 5: FLOAT -- RFC-8259 floating point literal -+** 6: FLOAT5 -- JSON5 floating point literal -+** 7: TEXT -- Text literal acceptable to both SQL and JSON -+** 8: TEXTJ -- Text containing RFC-8259 escapes -+** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes -+** 10: TEXTRAW -- Text containing unescaped syntax characters -+** 11: ARRAY -+** 12: OBJECT -+** -+** The other three possible values (13-15) are reserved for future -+** enhancements. -+** -+** The upper 4 bits of the first byte determine the size of the header -+** and sometimes also the size of the payload. If X is the first byte -+** of the element and if X>>4 is between 0 and 11, then the payload -+** will be that many bytes in size and the header is exactly one byte -+** in size. Other four values for X>>4 (12-15) indicate that the header -+** is more than one byte in size and that the payload size is determined -+** by the remainder of the header, interpreted as a unsigned big-endian -+** integer. -+** -+** Value of X>>4 Size integer Total header size -+** ------------- -------------------- ----------------- -+** 12 1 byte (0-255) 2 -+** 13 2 byte (0-65535) 3 -+** 14 4 byte (0-4294967295) 5 -+** 15 8 byte (0-1.8e19) 9 -+** -+** The payload size need not be expressed in its minimal form. For example, -+** if the payload size is 10, the size can be expressed in any of 5 different -+** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by one 0x0a byte, -+** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by -+** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and -+** a single byte of 0x0a. The shorter forms are preferred, of course, but -+** sometimes when generating JSONB, the payload size is not known in advance -+** and it is convenient to reserve sufficient header space to cover the -+** largest possible payload size and then come back later and patch up -+** the size when it becomes known, resulting in a non-minimal encoding. -+** -+** The value (X>>4)==15 is not actually used in the current implementation -+** (as SQLite is currently unable to handle BLOBs larger than about 2GB) -+** but is included in the design to allow for future enhancements. -+** -+** The payload follows the header. NULL, TRUE, and FALSE have no payload and -+** their payload size must always be zero. The payload for INT, INT5, -+** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the -+** "..." or '...' delimiters are omitted from the various text encodings. -+** The payload for ARRAY and OBJECT is a list of additional elements that -+** are the content for the array or object. The payload for an OBJECT -+** must be an even number of elements. The first element of each pair is -+** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW. -+** -+** A valid JSONB blob consists of a single element, as described above. -+** Usually this will be an ARRAY or OBJECT element which has many more -+** elements as its content. But the overall blob is just a single element. -+** -+** Input validation for JSONB blobs simply checks that the element type -+** code is between 0 and 12 and that the total size of the element -+** (header plus payload) is the same as the size of the BLOB. If those -+** checks are true, the BLOB is assumed to be JSONB and processing continues. -+** Errors are only raised if some other miscoding is discovered during -+** processing. - ** --** For the time being, all JSON is stored as pure text. (We might add --** a JSONB type in the future which stores a binary encoding of JSON in --** a BLOB, but there is no support for JSONB in the current implementation. --** This implementation parses JSON text at 250 MB/s, so it is hard to see --** how JSONB might improve on that.) -+** Additional information can be found in the doc/jsonb.md file of the -+** canonical SQLite source tree. - */ --#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) --#if !defined(SQLITEINT_H) --/* #include "sqlite3ext.h" */ --#endif --SQLITE_EXTENSION_INIT1 --/* #include */ --/* #include */ --/* #include */ --/* #include */ -+#ifndef SQLITE_OMIT_JSON -+/* #include "sqliteInt.h" */ - --/* Mark a function parameter as unused, to suppress nuisance compiler --** warnings. */ --#ifndef UNUSED_PARAM --# define UNUSED_PARAM(X) (void)(X) --#endif -+/* JSONB element types -+*/ -+#define JSONB_NULL 0 /* "null" */ -+#define JSONB_TRUE 1 /* "true" */ -+#define JSONB_FALSE 2 /* "false" */ -+#define JSONB_INT 3 /* integer acceptable to JSON and SQL */ -+#define JSONB_INT5 4 /* integer in 0x000 notation */ -+#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */ -+#define JSONB_FLOAT5 6 /* float with JSON5 extensions */ -+#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */ -+#define JSONB_TEXTJ 8 /* Text with JSON escapes */ -+#define JSONB_TEXT5 9 /* Text with JSON-5 escape */ -+#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */ -+#define JSONB_ARRAY 11 /* An array */ -+#define JSONB_OBJECT 12 /* An object */ -+ -+/* Human-readable names for the JSONB values. The index for each -+** string must correspond to the JSONB_* integer above. -+*/ -+static const char * const jsonbType[] = { -+ "null", "true", "false", "integer", "integer", -+ "real", "real", "text", "text", "text", -+ "text", "array", "object", "", "", "", "" -+}; - --#ifndef LARGEST_INT64 --# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) --# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) -+/* -+** Growing our own isspace() routine this way is twice as fast as -+** the library isspace() function, resulting in a 7% overall performance -+** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). -+*/ -+static const char jsonIsSpace[] = { -+#ifdef SQLITE_ASCII -+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ -+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */ -+ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */ - #endif -- --#ifndef deliberate_fall_through --# define deliberate_fall_through -+#ifdef SQLITE_EBCDIC -+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 0 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ -+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */ -+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */ -+ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */ - #endif - -+}; -+#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) -+ - /* --** Versions of isspace(), isalnum() and isdigit() to which it is safe --** to pass signed char values. -+** The set of all space characters recognized by jsonIsspace(). -+** Useful as the second argument to strspn(). - */ --#ifdef sqlite3Isdigit -- /* Use the SQLite core versions if this routine is part of the -- ** SQLite amalgamation */ --# define safe_isdigit(x) sqlite3Isdigit(x) --# define safe_isalnum(x) sqlite3Isalnum(x) --# define safe_isxdigit(x) sqlite3Isxdigit(x) --#else -- /* Use the standard library for separate compilation */ --#include /* amalgamator: keep */ --# define safe_isdigit(x) isdigit((unsigned char)(x)) --# define safe_isalnum(x) isalnum((unsigned char)(x)) --# define safe_isxdigit(x) isxdigit((unsigned char)(x)) -+#ifdef SQLITE_ASCII -+static const char jsonSpaces[] = "\011\012\015\040"; - #endif -+#ifdef SQLITE_EBCDIC -+static const char jsonSpaces[] = "\005\045\015\100"; -+#endif -+ - - /* --** Growing our own isspace() routine this way is twice as fast as --** the library isspace() function, resulting in a 7% overall performance --** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). -+** Characters that are special to JSON. Control characters, -+** '"' and '\\' and '\''. Actually, '\'' is not special to -+** canonical JSON, but it is special in JSON-5, so we include -+** it in the set of special characters. - */ --static const char jsonIsSpace[] = { -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, --}; --#define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) -- --#ifndef SQLITE_AMALGAMATION -- /* Unsigned integer types. These are already defined in the sqliteInt.h, -- ** but the definitions need to be repeated for separate compilation. */ -- typedef sqlite3_uint64 u64; -- typedef unsigned int u32; -- typedef unsigned short int u16; -- typedef unsigned char u8; -+static const char jsonIsOk[256] = { -+#ifdef SQLITE_ASCII -+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ -+ 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 2 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 3 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 5 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ -+ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */ -+#endif -+#ifdef SQLITE_EBCDIC -+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ -+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 3 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 5 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, /* 7 */ -+ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */ -+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */ - #endif -+}; - - /* Objects */ -+typedef struct JsonCache JsonCache; - typedef struct JsonString JsonString; --typedef struct JsonNode JsonNode; - typedef struct JsonParse JsonParse; - -+/* -+** Magic number used for the JSON parse cache in sqlite3_get_auxdata() -+*/ -+#define JSON_CACHE_ID (-429938) /* Cache entry */ -+#define JSON_CACHE_SIZE 4 /* Max number of cache entries */ -+ -+/* -+** jsonUnescapeOneChar() returns this invalid code point if it encounters -+** a syntax error. -+*/ -+#define JSON_INVALID_CHAR 0x99999 -+ -+/* A cache mapping JSON text into JSONB blobs. -+** -+** Each cache entry is a JsonParse object with the following restrictions: -+** -+** * The bReadOnly flag must be set -+** -+** * The aBlob[] array must be owned by the JsonParse object. In other -+** words, nBlobAlloc must be non-zero. -+** -+** * eEdit and delta must be zero. -+** -+** * zJson must be an RCStr. In other words bJsonIsRCStr must be true. -+*/ -+struct JsonCache { -+ sqlite3 *db; /* Database connection */ -+ int nUsed; /* Number of active entries in the cache */ -+ JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */ -+}; -+ - /* An instance of this object represents a JSON string - ** under construction. Really, this is a generic string accumulator - ** that can be and is used to create strings other than JSON. -+** -+** If the generated string is longer than will fit into the zSpace[] buffer, -+** then it will be an RCStr string. This aids with caching of large -+** JSON strings. - */ - struct JsonString { - sqlite3_context *pCtx; /* Function context - put error messages here */ -@@ -185538,88 +208152,227 @@ struct JsonString { - u64 nAlloc; /* Bytes of storage available in zBuf[] */ - u64 nUsed; /* Bytes of zBuf[] currently used */ - u8 bStatic; /* True if zBuf is static space */ -- u8 bErr; /* True if an error has been encountered */ -+ u8 eErr; /* True if an error has been encountered */ - char zSpace[100]; /* Initial static space */ - }; - --/* JSON type values --*/ --#define JSON_NULL 0 --#define JSON_TRUE 1 --#define JSON_FALSE 2 --#define JSON_INT 3 --#define JSON_REAL 4 --#define JSON_STRING 5 --#define JSON_ARRAY 6 --#define JSON_OBJECT 7 -+/* Allowed values for JsonString.eErr */ -+#define JSTRING_OOM 0x01 /* Out of memory */ -+#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */ -+#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */ - --/* The "subtype" set for JSON values */ -+/* The "subtype" set for text JSON values passed through using -+** sqlite3_result_subtype() and sqlite3_value_subtype(). -+*/ - #define JSON_SUBTYPE 74 /* Ascii for "J" */ - - /* --** Names of the various JSON types: --*/ --static const char * const jsonType[] = { -- "null", "true", "false", "integer", "real", "text", "array", "object" --}; -- --/* Bit values for the JsonNode.jnFlag field -+** Bit values for the flags passed into various SQL function implementations -+** via the sqlite3_user_data() value. - */ --#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ --#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ --#define JNODE_REMOVE 0x04 /* Do not output */ --#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ --#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ --#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ --#define JNODE_LABEL 0x40 /* Is a label of an object */ -+#define JSON_JSON 0x01 /* Result is always JSON */ -+#define JSON_SQL 0x02 /* Result is always SQL */ -+#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ -+#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ -+#define JSON_BLOB 0x08 /* Use the BLOB output format */ - - --/* A single node of parsed JSON --*/ --struct JsonNode { -- u8 eType; /* One of the JSON_ type values */ -- u8 jnFlags; /* JNODE flags */ -- u32 n; /* Bytes of content, or number of sub-nodes */ -- union { -- const char *zJContent; /* Content for INT, REAL, and STRING */ -- u32 iAppend; /* More terms for ARRAY and OBJECT */ -- u32 iKey; /* Key for ARRAY objects in json_tree() */ -- u32 iReplace; /* Replacement content for JNODE_REPLACE */ -- JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */ -- } u; --}; -- --/* A completely parsed JSON string -+/* A parsed JSON value. Lifecycle: -+** -+** 1. JSON comes in and is parsed into a JSONB value in aBlob. The -+** original text is stored in zJson. This step is skipped if the -+** input is JSONB instead of text JSON. -+** -+** 2. The aBlob[] array is searched using the JSON path notation, if needed. -+** -+** 3. Zero or more changes are made to aBlob[] (via json_remove() or -+** json_replace() or json_patch() or similar). -+** -+** 4. New JSON text is generated from the aBlob[] for output. This step -+** is skipped if the function is one of the jsonb_* functions that -+** returns JSONB instead of text JSON. - */ - struct JsonParse { -- u32 nNode; /* Number of slots of aNode[] used */ -- u32 nAlloc; /* Number of slots of aNode[] allocated */ -- JsonNode *aNode; /* Array of nodes containing the parse */ -- const char *zJson; /* Original JSON string */ -- u32 *aUp; /* Index of parent of each node */ -- u8 oom; /* Set to true if out of memory */ -- u8 nErr; /* Number of errors seen */ -- u16 iDepth; /* Nesting depth */ -+ u8 *aBlob; /* JSONB representation of JSON value */ -+ u32 nBlob; /* Bytes of aBlob[] actually used */ -+ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ -+ char *zJson; /* Json text used for parsing */ -+ sqlite3 *db; /* The database connection to which this object belongs */ - int nJson; /* Length of the zJson string in bytes */ -- u32 iHold; /* Replace cache line with the lowest iHold value */ -+ u32 nJPRef; /* Number of references to this object */ -+ u32 iErr; /* Error location in zJson[] */ -+ u16 iDepth; /* Nesting depth */ -+ u8 nErr; /* Number of errors seen */ -+ u8 oom; /* Set to true if out of memory */ -+ u8 bJsonIsRCStr; /* True if zJson is an RCStr */ -+ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ -+ u8 bReadOnly; /* Do not modify. */ -+ /* Search and edit information. See jsonLookupStep() */ -+ u8 eEdit; /* Edit operation to apply */ -+ int delta; /* Size change due to the edit */ -+ u32 nIns; /* Number of bytes to insert */ -+ u32 iLabel; /* Location of label if search landed on an object value */ -+ u8 *aIns; /* Content to be inserted */ - }; - -+/* Allowed values for JsonParse.eEdit */ -+#define JEDIT_DEL 1 /* Delete if exists */ -+#define JEDIT_REPL 2 /* Overwrite if exists */ -+#define JEDIT_INS 3 /* Insert if not exists */ -+#define JEDIT_SET 4 /* Insert or overwrite */ -+ - /* - ** Maximum nesting depth of JSON for this implementation. - ** - ** This limit is needed to avoid a stack overflow in the recursive --** descent parser. A depth of 2000 is far deeper than any sane JSON --** should go. -+** descent parser. A depth of 1000 is far deeper than any sane JSON -+** should go. Historical note: This limit was 2000 prior to version 3.42.0 -+*/ -+#ifndef SQLITE_JSON_MAX_DEPTH -+# define JSON_MAX_DEPTH 1000 -+#else -+# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH -+#endif -+ -+/* -+** Allowed values for the flgs argument to jsonParseFuncArg(); - */ --#define JSON_MAX_DEPTH 2000 -+#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ -+#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */ -+ -+/************************************************************************** -+** Forward references -+**************************************************************************/ -+static void jsonReturnStringAsBlob(JsonString*); -+static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p); -+static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*); -+static void jsonReturnParse(sqlite3_context*,JsonParse*); -+static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); -+static void jsonParseFree(JsonParse*); -+static u32 jsonbPayloadSize(const JsonParse*, u32, u32*); -+static u32 jsonUnescapeOneChar(const char*, u32, u32*); -+ -+/************************************************************************** -+** Utility routines for dealing with JsonCache objects -+**************************************************************************/ -+ -+/* -+** Free a JsonCache object. -+*/ -+static void jsonCacheDelete(JsonCache *p){ -+ int i; -+ for(i=0; inUsed; i++){ -+ jsonParseFree(p->a[i]); -+ } -+ sqlite3DbFree(p->db, p); -+} -+static void jsonCacheDeleteGeneric(void *p){ -+ jsonCacheDelete((JsonCache*)p); -+} -+ -+/* -+** Insert a new entry into the cache. If the cache is full, expel -+** the least recently used entry. Return SQLITE_OK on success or a -+** result code otherwise. -+** -+** Cache entries are stored in age order, oldest first. -+*/ -+static int jsonCacheInsert( -+ sqlite3_context *ctx, /* The SQL statement context holding the cache */ -+ JsonParse *pParse /* The parse object to be added to the cache */ -+){ -+ JsonCache *p; -+ -+ assert( pParse->zJson!=0 ); -+ assert( pParse->bJsonIsRCStr ); -+ assert( pParse->delta==0 ); -+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); -+ if( p==0 ){ -+ sqlite3 *db = sqlite3_context_db_handle(ctx); -+ p = sqlite3DbMallocZero(db, sizeof(*p)); -+ if( p==0 ) return SQLITE_NOMEM; -+ p->db = db; -+ sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric); -+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); -+ if( p==0 ) return SQLITE_NOMEM; -+ } -+ if( p->nUsed >= JSON_CACHE_SIZE ){ -+ jsonParseFree(p->a[0]); -+ memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0])); -+ p->nUsed = JSON_CACHE_SIZE-1; -+ } -+ assert( pParse->nBlobAlloc>0 ); -+ pParse->eEdit = 0; -+ pParse->nJPRef++; -+ pParse->bReadOnly = 1; -+ p->a[p->nUsed] = pParse; -+ p->nUsed++; -+ return SQLITE_OK; -+} -+ -+/* -+** Search for a cached translation the json text supplied by pArg. Return -+** the JsonParse object if found. Return NULL if not found. -+** -+** When a match if found, the matching entry is moved to become the -+** most-recently used entry if it isn't so already. -+** -+** The JsonParse object returned still belongs to the Cache and might -+** be deleted at any moment. If the caller wants the JsonParse to -+** linger, it needs to increment the nPJRef reference counter. -+*/ -+static JsonParse *jsonCacheSearch( -+ sqlite3_context *ctx, /* The SQL statement context holding the cache */ -+ sqlite3_value *pArg /* Function argument containing SQL text */ -+){ -+ JsonCache *p; -+ int i; -+ const char *zJson; -+ int nJson; -+ -+ if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){ -+ return 0; -+ } -+ zJson = (const char*)sqlite3_value_text(pArg); -+ if( zJson==0 ) return 0; -+ nJson = sqlite3_value_bytes(pArg); -+ -+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); -+ if( p==0 ){ -+ return 0; -+ } -+ for(i=0; inUsed; i++){ -+ if( p->a[i]->zJson==zJson ) break; -+ } -+ if( i>=p->nUsed ){ -+ for(i=0; inUsed; i++){ -+ if( p->a[i]->nJson!=nJson ) continue; -+ if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break; -+ } -+ } -+ if( inUsed ){ -+ if( inUsed-1 ){ -+ /* Make the matching entry the most recently used entry */ -+ JsonParse *tmp = p->a[i]; -+ memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); -+ p->a[p->nUsed-1] = tmp; -+ i = p->nUsed - 1; -+ } -+ assert( p->a[i]->delta==0 ); -+ return p->a[i]; -+ }else{ -+ return 0; -+ } -+} - - /************************************************************************** - ** Utility routines for dealing with JsonString objects - **************************************************************************/ - --/* Set the JsonString object to an empty string -+/* Turn uninitialized bulk memory into a valid JsonString object -+** holding a zero-length string. - */ --static void jsonZero(JsonString *p){ -+static void jsonStringZero(JsonString *p){ - p->zBuf = p->zSpace; - p->nAlloc = sizeof(p->zSpace); - p->nUsed = 0; -@@ -185628,53 +208381,51 @@ static void jsonZero(JsonString *p){ - - /* Initialize the JsonString object - */ --static void jsonInit(JsonString *p, sqlite3_context *pCtx){ -+static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){ - p->pCtx = pCtx; -- p->bErr = 0; -- jsonZero(p); -+ p->eErr = 0; -+ jsonStringZero(p); - } - -- - /* Free all allocated memory and reset the JsonString object back to its - ** initial state. - */ --static void jsonReset(JsonString *p){ -- if( !p->bStatic ) sqlite3_free(p->zBuf); -- jsonZero(p); -+static void jsonStringReset(JsonString *p){ -+ if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf); -+ jsonStringZero(p); - } - -- - /* Report an out-of-memory (OOM) condition - */ --static void jsonOom(JsonString *p){ -- p->bErr = 1; -- sqlite3_result_error_nomem(p->pCtx); -- jsonReset(p); -+static void jsonStringOom(JsonString *p){ -+ p->eErr |= JSTRING_OOM; -+ if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx); -+ jsonStringReset(p); - } - - /* Enlarge pJson->zBuf so that it can hold at least N more bytes. - ** Return zero on success. Return non-zero on an OOM error - */ --static int jsonGrow(JsonString *p, u32 N){ -+static int jsonStringGrow(JsonString *p, u32 N){ - u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+10; - char *zNew; - if( p->bStatic ){ -- if( p->bErr ) return 1; -- zNew = sqlite3_malloc64(nTotal); -+ if( p->eErr ) return 1; -+ zNew = sqlite3RCStrNew(nTotal); - if( zNew==0 ){ -- jsonOom(p); -+ jsonStringOom(p); - return SQLITE_NOMEM; - } - memcpy(zNew, p->zBuf, (size_t)p->nUsed); - p->zBuf = zNew; - p->bStatic = 0; - }else{ -- zNew = sqlite3_realloc64(p->zBuf, nTotal); -- if( zNew==0 ){ -- jsonOom(p); -+ p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); -+ if( p->zBuf==0 ){ -+ p->eErr |= JSTRING_OOM; -+ jsonStringZero(p); - return SQLITE_NOMEM; - } -- p->zBuf = zNew; - } - p->nAlloc = nTotal; - return SQLITE_OK; -@@ -185682,18 +208433,40 @@ static int jsonGrow(JsonString *p, u32 N){ - - /* Append N bytes from zIn onto the end of the JsonString string. - */ --static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ -- if( N==0 ) return; -- if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return; -+static SQLITE_NOINLINE void jsonStringExpandAndAppend( -+ JsonString *p, -+ const char *zIn, -+ u32 N -+){ -+ assert( N>0 ); -+ if( jsonStringGrow(p,N) ) return; - memcpy(p->zBuf+p->nUsed, zIn, N); - p->nUsed += N; - } -+static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ -+ if( N==0 ) return; -+ if( N+p->nUsed >= p->nAlloc ){ -+ jsonStringExpandAndAppend(p,zIn,N); -+ }else{ -+ memcpy(p->zBuf+p->nUsed, zIn, N); -+ p->nUsed += N; -+ } -+} -+static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ -+ assert( N>0 ); -+ if( N+p->nUsed >= p->nAlloc ){ -+ jsonStringExpandAndAppend(p,zIn,N); -+ }else{ -+ memcpy(p->zBuf+p->nUsed, zIn, N); -+ p->nUsed += N; -+ } -+} - - /* Append formatted text (not to exceed N bytes) to the JsonString. - */ - static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ - va_list ap; -- if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return; -+ if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return; - va_start(ap, zFormat); - sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap); - va_end(ap); -@@ -185702,10 +208475,38 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ - - /* Append a single character - */ --static void jsonAppendChar(JsonString *p, char c){ -- if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return; -+static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){ -+ if( jsonStringGrow(p,1) ) return; - p->zBuf[p->nUsed++] = c; - } -+static void jsonAppendChar(JsonString *p, char c){ -+ if( p->nUsed>=p->nAlloc ){ -+ jsonAppendCharExpand(p,c); -+ }else{ -+ p->zBuf[p->nUsed++] = c; -+ } -+} -+ -+/* Remove a single character from the end of the string -+*/ -+static void jsonStringTrimOneChar(JsonString *p){ -+ if( p->eErr==0 ){ -+ assert( p->nUsed>0 ); -+ p->nUsed--; -+ } -+} -+ -+ -+/* Make sure there is a zero terminator on p->zBuf[] -+** -+** Return true on success. Return false if an OOM prevents this -+** from happening. -+*/ -+static int jsonStringTerminate(JsonString *p){ -+ jsonAppendChar(p, 0); -+ jsonStringTrimOneChar(p); -+ return p->eErr==0; -+} - - /* Append a comma separator to the output buffer, if the previous - ** character is not '[' or '{'. -@@ -185714,68 +208515,137 @@ static void jsonAppendSeparator(JsonString *p){ - char c; - if( p->nUsed==0 ) return; - c = p->zBuf[p->nUsed-1]; -- if( c!='[' && c!='{' ) jsonAppendChar(p, ','); -+ if( c=='[' || c=='{' ) return; -+ jsonAppendChar(p, ','); -+} -+ -+/* c is a control character. Append the canonical JSON representation -+** of that control character to p. -+** -+** This routine assumes that the output buffer has already been enlarged -+** sufficiently to hold the worst-case encoding plus a nul terminator. -+*/ -+static void jsonAppendControlChar(JsonString *p, u8 c){ -+ static const char aSpecial[] = { -+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -+ }; -+ assert( sizeof(aSpecial)==32 ); -+ assert( aSpecial['\b']=='b' ); -+ assert( aSpecial['\f']=='f' ); -+ assert( aSpecial['\n']=='n' ); -+ assert( aSpecial['\r']=='r' ); -+ assert( aSpecial['\t']=='t' ); -+ assert( c>=0 && cnUsed+7 <= p->nAlloc ); -+ if( aSpecial[c] ){ -+ p->zBuf[p->nUsed] = '\\'; -+ p->zBuf[p->nUsed+1] = aSpecial[c]; -+ p->nUsed += 2; -+ }else{ -+ p->zBuf[p->nUsed] = '\\'; -+ p->zBuf[p->nUsed+1] = 'u'; -+ p->zBuf[p->nUsed+2] = '0'; -+ p->zBuf[p->nUsed+3] = '0'; -+ p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4]; -+ p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf]; -+ p->nUsed += 6; -+ } - } - - /* Append the N-byte string in zIn to the end of the JsonString string --** under construction. Enclose the string in "..." and escape --** any double-quotes or backslash characters contained within the -+** under construction. Enclose the string in double-quotes ("...") and -+** escape any double-quotes or backslash characters contained within the - ** string. -+** -+** This routine is a high-runner. There is a measurable performance -+** increase associated with unwinding the jsonIsOk[] loop. - */ - static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ -- u32 i; -- if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return; -+ u32 k; -+ u8 c; -+ const u8 *z = (const u8*)zIn; -+ if( z==0 ) return; -+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return; - p->zBuf[p->nUsed++] = '"'; -- for(i=0; i=N ){ -+ while( k=N ){ -+ if( k>0 ){ -+ memcpy(&p->zBuf[p->nUsed], z, k); -+ p->nUsed += k; -+ } -+ break; -+ } -+ if( k>0 ){ -+ memcpy(&p->zBuf[p->nUsed], z, k); -+ p->nUsed += k; -+ z += k; -+ N -= k; -+ } -+ c = z[0]; - if( c=='"' || c=='\\' ){ -- json_simple_escape: -- if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return; -- p->zBuf[p->nUsed++] = '\\'; -- }else if( c<=0x1f ){ -- static const char aSpecial[] = { -- 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -- }; -- assert( sizeof(aSpecial)==32 ); -- assert( aSpecial['\b']=='b' ); -- assert( aSpecial['\f']=='f' ); -- assert( aSpecial['\n']=='n' ); -- assert( aSpecial['\r']=='r' ); -- assert( aSpecial['\t']=='t' ); -- if( aSpecial[c] ){ -- c = aSpecial[c]; -- goto json_simple_escape; -- } -- if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return; -+ if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return; - p->zBuf[p->nUsed++] = '\\'; -- p->zBuf[p->nUsed++] = 'u'; -- p->zBuf[p->nUsed++] = '0'; -- p->zBuf[p->nUsed++] = '0'; -- p->zBuf[p->nUsed++] = '0' + (c>>4); -- c = "0123456789abcdef"[c&0xf]; -+ p->zBuf[p->nUsed++] = c; -+ }else if( c=='\'' ){ -+ p->zBuf[p->nUsed++] = c; -+ }else{ -+ if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; -+ jsonAppendControlChar(p, c); - } -- p->zBuf[p->nUsed++] = c; -+ z++; -+ N--; - } - p->zBuf[p->nUsed++] = '"'; - assert( p->nUsednAlloc ); - } - - /* --** Append a function parameter value to the JSON string under --** construction. -+** Append an sqlite3_value (such as a function parameter) to the JSON -+** string under construction in p. - */ --static void jsonAppendValue( -+static void jsonAppendSqlValue( - JsonString *p, /* Append to this JSON string */ - sqlite3_value *pValue /* Value to append */ - ){ - switch( sqlite3_value_type(pValue) ){ - case SQLITE_NULL: { -- jsonAppendRaw(p, "null", 4); -+ jsonAppendRawNZ(p, "null", 4); -+ break; -+ } -+ case SQLITE_FLOAT: { -+ jsonPrintf(100, p, "%!0.15g", sqlite3_value_double(pValue)); - break; - } -- case SQLITE_INTEGER: -- case SQLITE_FLOAT: { -+ case SQLITE_INTEGER: { - const char *z = (const char*)sqlite3_value_text(pValue); - u32 n = (u32)sqlite3_value_bytes(pValue); - jsonAppendRaw(p, z, n); -@@ -185792,177 +208662,125 @@ static void jsonAppendValue( - break; - } - default: { -- if( p->bErr==0 ){ -+ JsonParse px; -+ memset(&px, 0, sizeof(px)); -+ if( jsonArgIsJsonb(pValue, &px) ){ -+ jsonTranslateBlobToText(&px, 0, p); -+ }else if( p->eErr==0 ){ - sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); -- p->bErr = 2; -- jsonReset(p); -+ p->eErr = JSTRING_ERR; -+ jsonStringReset(p); - } - break; - } - } - } - -- --/* Make the JSON in p the result of the SQL function. -+/* Make the text in p (which is probably a generated JSON text string) -+** the result of the SQL function. -+** -+** The JsonString is reset. -+** -+** If pParse and ctx are both non-NULL, then the SQL string in p is -+** loaded into the zJson field of the pParse object as a RCStr and the -+** pParse is added to the cache. - */ --static void jsonResult(JsonString *p){ -- if( p->bErr==0 ){ -- sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, -- p->bStatic ? SQLITE_TRANSIENT : sqlite3_free, -- SQLITE_UTF8); -- jsonZero(p); -+static void jsonReturnString( -+ JsonString *p, /* String to return */ -+ JsonParse *pParse, /* JSONB source or NULL */ -+ sqlite3_context *ctx /* Where to cache */ -+){ -+ assert( (pParse!=0)==(ctx!=0) ); -+ assert( ctx==0 || ctx==p->pCtx ); -+ if( p->eErr==0 ){ -+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); -+ if( flags & JSON_BLOB ){ -+ jsonReturnStringAsBlob(p); -+ }else if( p->bStatic ){ -+ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, -+ SQLITE_TRANSIENT, SQLITE_UTF8); -+ }else if( jsonStringTerminate(p) ){ -+ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){ -+ int rc; -+ pParse->zJson = sqlite3RCStrRef(p->zBuf); -+ pParse->nJson = p->nUsed; -+ pParse->bJsonIsRCStr = 1; -+ rc = jsonCacheInsert(ctx, pParse); -+ if( rc==SQLITE_NOMEM ){ -+ sqlite3_result_error_nomem(ctx); -+ jsonStringReset(p); -+ return; -+ } -+ } -+ sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed, -+ sqlite3RCStrUnref, -+ SQLITE_UTF8); -+ }else{ -+ sqlite3_result_error_nomem(p->pCtx); -+ } -+ }else if( p->eErr & JSTRING_OOM ){ -+ sqlite3_result_error_nomem(p->pCtx); -+ }else if( p->eErr & JSTRING_MALFORMED ){ -+ sqlite3_result_error(p->pCtx, "malformed JSON", -1); - } -- assert( p->bStatic ); -+ jsonStringReset(p); - } - - /************************************************************************** --** Utility routines for dealing with JsonNode and JsonParse objects -+** Utility routines for dealing with JsonParse objects - **************************************************************************/ - --/* --** Return the number of consecutive JsonNode slots need to represent --** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and --** OBJECT types, the number might be larger. --** --** Appended elements are not counted. The value returned is the number --** by which the JsonNode counter should increment in order to go to the --** next peer value. --*/ --static u32 jsonNodeSize(JsonNode *pNode){ -- return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1; --} -- - /* - ** Reclaim all memory allocated by a JsonParse object. But do not - ** delete the JsonParse object itself. - */ - static void jsonParseReset(JsonParse *pParse){ -- sqlite3_free(pParse->aNode); -- pParse->aNode = 0; -- pParse->nNode = 0; -- pParse->nAlloc = 0; -- sqlite3_free(pParse->aUp); -- pParse->aUp = 0; -+ assert( pParse->nJPRef<=1 ); -+ if( pParse->bJsonIsRCStr ){ -+ sqlite3RCStrUnref(pParse->zJson); -+ pParse->zJson = 0; -+ pParse->nJson = 0; -+ pParse->bJsonIsRCStr = 0; -+ } -+ if( pParse->nBlobAlloc ){ -+ sqlite3DbFree(pParse->db, pParse->aBlob); -+ pParse->aBlob = 0; -+ pParse->nBlob = 0; -+ pParse->nBlobAlloc = 0; -+ } - } - - /* --** Free a JsonParse object that was obtained from sqlite3_malloc(). -+** Decrement the reference count on the JsonParse object. When the -+** count reaches zero, free the object. - */ - static void jsonParseFree(JsonParse *pParse){ -- jsonParseReset(pParse); -- sqlite3_free(pParse); --} -- --/* --** Convert the JsonNode pNode into a pure JSON string and --** append to pOut. Subsubstructure is also included. Return --** the number of JsonNode objects that are encoded. --*/ --static void jsonRenderNode( -- JsonNode *pNode, /* The node to render */ -- JsonString *pOut, /* Write JSON here */ -- sqlite3_value **aReplace /* Replacement values */ --){ -- if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){ -- if( pNode->jnFlags & JNODE_REPLACE ){ -- jsonAppendValue(pOut, aReplace[pNode->u.iReplace]); -- return; -- } -- pNode = pNode->u.pPatch; -- } -- switch( pNode->eType ){ -- default: { -- assert( pNode->eType==JSON_NULL ); -- jsonAppendRaw(pOut, "null", 4); -- break; -- } -- case JSON_TRUE: { -- jsonAppendRaw(pOut, "true", 4); -- break; -- } -- case JSON_FALSE: { -- jsonAppendRaw(pOut, "false", 5); -- break; -- } -- case JSON_STRING: { -- if( pNode->jnFlags & JNODE_RAW ){ -- jsonAppendString(pOut, pNode->u.zJContent, pNode->n); -- break; -- } -- /* no break */ deliberate_fall_through -- } -- case JSON_REAL: -- case JSON_INT: { -- jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); -- break; -- } -- case JSON_ARRAY: { -- u32 j = 1; -- jsonAppendChar(pOut, '['); -- for(;;){ -- while( j<=pNode->n ){ -- if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){ -- jsonAppendSeparator(pOut); -- jsonRenderNode(&pNode[j], pOut, aReplace); -- } -- j += jsonNodeSize(&pNode[j]); -- } -- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; -- pNode = &pNode[pNode->u.iAppend]; -- j = 1; -- } -- jsonAppendChar(pOut, ']'); -- break; -- } -- case JSON_OBJECT: { -- u32 j = 1; -- jsonAppendChar(pOut, '{'); -- for(;;){ -- while( j<=pNode->n ){ -- if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){ -- jsonAppendSeparator(pOut); -- jsonRenderNode(&pNode[j], pOut, aReplace); -- jsonAppendChar(pOut, ':'); -- jsonRenderNode(&pNode[j+1], pOut, aReplace); -- } -- j += 1 + jsonNodeSize(&pNode[j+1]); -- } -- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; -- pNode = &pNode[pNode->u.iAppend]; -- j = 1; -- } -- jsonAppendChar(pOut, '}'); -- break; -+ if( pParse ){ -+ if( pParse->nJPRef>1 ){ -+ pParse->nJPRef--; -+ }else{ -+ jsonParseReset(pParse); -+ sqlite3DbFree(pParse->db, pParse); - } - } - } - --/* --** Return a JsonNode and all its descendents as a JSON string. --*/ --static void jsonReturnJson( -- JsonNode *pNode, /* Node to return */ -- sqlite3_context *pCtx, /* Return value for this function */ -- sqlite3_value **aReplace /* Array of replacement values */ --){ -- JsonString s; -- jsonInit(&s, pCtx); -- jsonRenderNode(pNode, &s, aReplace); -- jsonResult(&s); -- sqlite3_result_subtype(pCtx, JSON_SUBTYPE); --} -+/************************************************************************** -+** Utility routines for the JSON text parser -+**************************************************************************/ - - /* - ** Translate a single byte of Hex into an integer. --** This routine only works if h really is a valid hexadecimal --** character: 0..9a..fA..F -+** This routine only gives a correct answer if h really is a valid hexadecimal -+** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not -+** assert() if the digit is not hex. - */ - static u8 jsonHexToInt(int h){ -- assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); -+#ifdef SQLITE_ASCII -+ h += 9*(1&(h>>6)); -+#endif - #ifdef SQLITE_EBCDIC - h += 9*(1&~(h>>4)); --#else -- h += 9*(1&(h>>6)); - #endif - return (u8)(h & 0xf); - } -@@ -185972,10 +208790,6 @@ static u8 jsonHexToInt(int h){ - */ - static u32 jsonHexToInt4(const char *z){ - u32 v; -- assert( safe_isxdigit(z[0]) ); -- assert( safe_isxdigit(z[1]) ); -- assert( safe_isxdigit(z[2]) ); -- assert( safe_isxdigit(z[3]) ); - v = (jsonHexToInt(z[0])<<12) - + (jsonHexToInt(z[1])<<8) - + (jsonHexToInt(z[2])<<4) -@@ -185984,409 +208798,1116 @@ static u32 jsonHexToInt4(const char *z){ - } - - /* --** Make the JsonNode the return value of the function. -+** Return true if z[] begins with 2 (or more) hexadecimal digits - */ --static void jsonReturn( -- JsonNode *pNode, /* Node to return */ -- sqlite3_context *pCtx, /* Return value for this function */ -- sqlite3_value **aReplace /* Array of replacement values */ --){ -- switch( pNode->eType ){ -- default: { -- assert( pNode->eType==JSON_NULL ); -- sqlite3_result_null(pCtx); -- break; -- } -- case JSON_TRUE: { -- sqlite3_result_int(pCtx, 1); -- break; -- } -- case JSON_FALSE: { -- sqlite3_result_int(pCtx, 0); -- break; -- } -- case JSON_INT: { -- sqlite3_int64 i = 0; -- const char *z = pNode->u.zJContent; -- if( z[0]=='-' ){ z++; } -- while( z[0]>='0' && z[0]<='9' ){ -- unsigned v = *(z++) - '0'; -- if( i>=LARGEST_INT64/10 ){ -- if( i>LARGEST_INT64/10 ) goto int_as_real; -- if( z[0]>='0' && z[0]<='9' ) goto int_as_real; -- if( v==9 ) goto int_as_real; -- if( v==8 ){ -- if( pNode->u.zJContent[0]=='-' ){ -- sqlite3_result_int64(pCtx, SMALLEST_INT64); -- goto int_done; -- }else{ -- goto int_as_real; -+static int jsonIs2Hex(const char *z){ -+ return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]); -+} -+ -+/* -+** Return true if z[] begins with 4 (or more) hexadecimal digits -+*/ -+static int jsonIs4Hex(const char *z){ -+ return jsonIs2Hex(z) && jsonIs2Hex(&z[2]); -+} -+ -+/* -+** Return the number of bytes of JSON5 whitespace at the beginning of -+** the input string z[]. -+** -+** JSON5 whitespace consists of any of the following characters: -+** -+** Unicode UTF-8 Name -+** U+0009 09 horizontal tab -+** U+000a 0a line feed -+** U+000b 0b vertical tab -+** U+000c 0c form feed -+** U+000d 0d carriage return -+** U+0020 20 space -+** U+00a0 c2 a0 non-breaking space -+** U+1680 e1 9a 80 ogham space mark -+** U+2000 e2 80 80 en quad -+** U+2001 e2 80 81 em quad -+** U+2002 e2 80 82 en space -+** U+2003 e2 80 83 em space -+** U+2004 e2 80 84 three-per-em space -+** U+2005 e2 80 85 four-per-em space -+** U+2006 e2 80 86 six-per-em space -+** U+2007 e2 80 87 figure space -+** U+2008 e2 80 88 punctuation space -+** U+2009 e2 80 89 thin space -+** U+200a e2 80 8a hair space -+** U+2028 e2 80 a8 line separator -+** U+2029 e2 80 a9 paragraph separator -+** U+202f e2 80 af narrow no-break space (NNBSP) -+** U+205f e2 81 9f medium mathematical space (MMSP) -+** U+3000 e3 80 80 ideographical space -+** U+FEFF ef bb bf byte order mark -+** -+** In addition, comments between '/', '*' and '*', '/' and -+** from '/', '/' to end-of-line are also considered to be whitespace. -+*/ -+static int json5Whitespace(const char *zIn){ -+ int n = 0; -+ const u8 *z = (u8*)zIn; -+ while( 1 /*exit by "goto whitespace_done"*/ ){ -+ switch( z[n] ){ -+ case 0x09: -+ case 0x0a: -+ case 0x0b: -+ case 0x0c: -+ case 0x0d: -+ case 0x20: { -+ n++; -+ break; -+ } -+ case '/': { -+ if( z[n+1]=='*' && z[n+2]!=0 ){ -+ int j; -+ for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ -+ if( z[j]==0 ) goto whitespace_done; -+ } -+ n = j+1; -+ break; -+ }else if( z[n+1]=='/' ){ -+ int j; -+ char c; -+ for(j=n+2; (c = z[j])!=0; j++){ -+ if( c=='\n' || c=='\r' ) break; -+ if( 0xe2==(u8)c && 0x80==(u8)z[j+1] -+ && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]) -+ ){ -+ j += 2; -+ break; - } - } -+ n = j; -+ if( z[n] ) n++; -+ break; - } -- i = i*10 + v; -+ goto whitespace_done; - } -- if( pNode->u.zJContent[0]=='-' ){ i = -i; } -- sqlite3_result_int64(pCtx, i); -- int_done: -- break; -- int_as_real: i=0; /* no break */ deliberate_fall_through -- } -- case JSON_REAL: { -- double r; --#ifdef SQLITE_AMALGAMATION -- const char *z = pNode->u.zJContent; -- sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); --#else -- r = strtod(pNode->u.zJContent, 0); --#endif -- sqlite3_result_double(pCtx, r); -- break; -- } -- case JSON_STRING: { --#if 0 /* Never happens because JNODE_RAW is only set by json_set(), -- ** json_insert() and json_replace() and those routines do not -- ** call jsonReturn() */ -- if( pNode->jnFlags & JNODE_RAW ){ -- sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, -- SQLITE_TRANSIENT); -- }else --#endif -- assert( (pNode->jnFlags & JNODE_RAW)==0 ); -- if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ -- /* JSON formatted without any backslash-escapes */ -- sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, -- SQLITE_TRANSIENT); -- }else{ -- /* Translate JSON formatted string into raw text */ -- u32 i; -- u32 n = pNode->n; -- const char *z = pNode->u.zJContent; -- char *zOut; -- u32 j; -- zOut = sqlite3_malloc( n+1 ); -- if( zOut==0 ){ -- sqlite3_result_error_nomem(pCtx); -+ case 0xc2: { -+ if( z[n+1]==0xa0 ){ -+ n += 2; - break; - } -- for(i=1, j=0; i>6)); -- zOut[j++] = 0x80 | (v&0x3f); -- }else{ -- u32 vlo; -- if( (v&0xfc00)==0xd800 -- && i>18); -- zOut[j++] = 0x80 | ((v>>12)&0x3f); -- zOut[j++] = 0x80 | ((v>>6)&0x3f); -- zOut[j++] = 0x80 | (v&0x3f); -- }else{ -- zOut[j++] = 0xe0 | (v>>12); -- zOut[j++] = 0x80 | ((v>>6)&0x3f); -- zOut[j++] = 0x80 | (v&0x3f); -- } -- } -- }else{ -- if( c=='b' ){ -- c = '\b'; -- }else if( c=='f' ){ -- c = '\f'; -- }else if( c=='n' ){ -- c = '\n'; -- }else if( c=='r' ){ -- c = '\r'; -- }else if( c=='t' ){ -- c = '\t'; -- } -- zOut[j++] = c; -- } -+ goto whitespace_done; -+ } -+ case 0xe1: { -+ if( z[n+1]==0x9a && z[n+2]==0x80 ){ -+ n += 3; -+ break; -+ } -+ goto whitespace_done; -+ } -+ case 0xe2: { -+ if( z[n+1]==0x80 ){ -+ u8 c = z[n+2]; -+ if( c<0x80 ) goto whitespace_done; -+ if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){ -+ n += 3; -+ break; - } -+ }else if( z[n+1]==0x81 && z[n+2]==0x9f ){ -+ n += 3; -+ break; - } -- zOut[j] = 0; -- sqlite3_result_text(pCtx, zOut, j, sqlite3_free); -+ goto whitespace_done; -+ } -+ case 0xe3: { -+ if( z[n+1]==0x80 && z[n+2]==0x80 ){ -+ n += 3; -+ break; -+ } -+ goto whitespace_done; -+ } -+ case 0xef: { -+ if( z[n+1]==0xbb && z[n+2]==0xbf ){ -+ n += 3; -+ break; -+ } -+ goto whitespace_done; -+ } -+ default: { -+ goto whitespace_done; - } -- break; -- } -- case JSON_ARRAY: -- case JSON_OBJECT: { -- jsonReturnJson(pNode, pCtx, aReplace); -- break; - } - } -+ whitespace_done: -+ return n; - } - --/* Forward reference */ --static int jsonParseAddNode(JsonParse*,u32,u32,const char*); -+/* -+** Extra floating-point literals to allow in JSON. -+*/ -+static const struct NanInfName { -+ char c1; -+ char c2; -+ char n; -+ char eType; -+ char nRepl; -+ char *zMatch; -+ char *zRepl; -+} aNanInfName[] = { -+ { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" }, -+ { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" }, -+ { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" }, -+ { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" }, -+ { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" }, -+}; -+ - - /* --** A macro to hint to the compiler that a function should not be --** inlined. -+** Report the wrong number of arguments for json_insert(), json_replace() -+** or json_set(). - */ --#if defined(__GNUC__) --# define JSON_NOINLINE __attribute__((noinline)) --#elif defined(_MSC_VER) && _MSC_VER>=1310 --# define JSON_NOINLINE __declspec(noinline) --#else --# define JSON_NOINLINE --#endif -+static void jsonWrongNumArgs( -+ sqlite3_context *pCtx, -+ const char *zFuncName -+){ -+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", -+ zFuncName); -+ sqlite3_result_error(pCtx, zMsg, -1); -+ sqlite3_free(zMsg); -+} -+ -+/**************************************************************************** -+** Utility routines for dealing with the binary BLOB representation of JSON -+****************************************************************************/ -+ -+/* -+** Expand pParse->aBlob so that it holds at least N bytes. -+** -+** Return the number of errors. -+*/ -+static int jsonBlobExpand(JsonParse *pParse, u32 N){ -+ u8 *aNew; -+ u64 t; -+ assert( N>pParse->nBlobAlloc ); -+ if( pParse->nBlobAlloc==0 ){ -+ t = 100; -+ }else{ -+ t = pParse->nBlobAlloc*2; -+ } -+ if( tdb, pParse->aBlob, t); -+ if( aNew==0 ){ pParse->oom = 1; return 1; } -+ assert( t<0x7fffffff ); -+ pParse->aBlob = aNew; -+ pParse->nBlobAlloc = (u32)t; -+ return 0; -+} - -+/* -+** If pParse->aBlob is not previously editable (because it is taken -+** from sqlite3_value_blob(), as indicated by the fact that -+** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable -+** by making a copy into space obtained from malloc. -+** -+** Return true on success. Return false on OOM. -+*/ -+static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ -+ u8 *aOld; -+ u32 nSize; -+ assert( !pParse->bReadOnly ); -+ if( pParse->oom ) return 0; -+ if( pParse->nBlobAlloc>0 ) return 1; -+ aOld = pParse->aBlob; -+ nSize = pParse->nBlob + nExtra; -+ pParse->aBlob = 0; -+ if( jsonBlobExpand(pParse, nSize) ){ -+ return 0; -+ } -+ assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra ); -+ memcpy(pParse->aBlob, aOld, pParse->nBlob); -+ return 1; -+} - --static JSON_NOINLINE int jsonParseAddNodeExpand( -- JsonParse *pParse, /* Append the node to this object */ -- u32 eType, /* Node type */ -- u32 n, /* Content size or sub-node count */ -- const char *zContent /* Content */ -+/* Expand pParse->aBlob and append one bytes. -+*/ -+static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte( -+ JsonParse *pParse, -+ u8 c - ){ -- u32 nNew; -- JsonNode *pNew; -- assert( pParse->nNode>=pParse->nAlloc ); -- if( pParse->oom ) return -1; -- nNew = pParse->nAlloc*2 + 10; -- pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew); -- if( pNew==0 ){ -- pParse->oom = 1; -- return -1; -+ jsonBlobExpand(pParse, pParse->nBlob+1); -+ if( pParse->oom==0 ){ -+ assert( pParse->nBlob+1<=pParse->nBlobAlloc ); -+ pParse->aBlob[pParse->nBlob++] = c; - } -- pParse->nAlloc = nNew; -- pParse->aNode = pNew; -- assert( pParse->nNodenAlloc ); -- return jsonParseAddNode(pParse, eType, n, zContent); - } - --/* --** Create a new JsonNode instance based on the arguments and append that --** instance to the JsonParse. Return the index in pParse->aNode[] of the --** new node, or -1 if a memory allocation fails. -+/* Append a single character. -+*/ -+static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ -+ if( pParse->nBlob >= pParse->nBlobAlloc ){ -+ jsonBlobExpandAndAppendOneByte(pParse, c); -+ }else{ -+ pParse->aBlob[pParse->nBlob++] = c; -+ } -+} -+ -+/* Slow version of jsonBlobAppendNode() that first resizes the -+** pParse->aBlob structure. -+*/ -+static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*); -+static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode( -+ JsonParse *pParse, -+ u8 eType, -+ u32 szPayload, -+ const void *aPayload -+){ -+ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return; -+ jsonBlobAppendNode(pParse, eType, szPayload, aPayload); -+} -+ -+ -+/* Append a node type byte together with the payload size and -+** possibly also the payload. -+** -+** If aPayload is not NULL, then it is a pointer to the payload which -+** is also appended. If aPayload is NULL, the pParse->aBlob[] array -+** is resized (if necessary) so that it is big enough to hold the -+** payload, but the payload is not appended and pParse->nBlob is left -+** pointing to where the first byte of payload will eventually be. - */ --static int jsonParseAddNode( -- JsonParse *pParse, /* Append the node to this object */ -- u32 eType, /* Node type */ -- u32 n, /* Content size or sub-node count */ -- const char *zContent /* Content */ -+static void jsonBlobAppendNode( -+ JsonParse *pParse, /* The JsonParse object under construction */ -+ u8 eType, /* Node type. One of JSONB_* */ -+ u32 szPayload, /* Number of bytes of payload */ -+ const void *aPayload /* The payload. Might be NULL */ - ){ -- JsonNode *p; -- if( pParse->nNode>=pParse->nAlloc ){ -- return jsonParseAddNodeExpand(pParse, eType, n, zContent); -+ u8 *a; -+ if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){ -+ jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload); -+ return; - } -- p = &pParse->aNode[pParse->nNode]; -- p->eType = (u8)eType; -- p->jnFlags = 0; -- p->n = n; -- p->u.zJContent = zContent; -- return pParse->nNode++; -+ assert( pParse->aBlob!=0 ); -+ a = &pParse->aBlob[pParse->nBlob]; -+ if( szPayload<=11 ){ -+ a[0] = eType | (szPayload<<4); -+ pParse->nBlob += 1; -+ }else if( szPayload<=0xff ){ -+ a[0] = eType | 0xc0; -+ a[1] = szPayload & 0xff; -+ pParse->nBlob += 2; -+ }else if( szPayload<=0xffff ){ -+ a[0] = eType | 0xd0; -+ a[1] = (szPayload >> 8) & 0xff; -+ a[2] = szPayload & 0xff; -+ pParse->nBlob += 3; -+ }else{ -+ a[0] = eType | 0xe0; -+ a[1] = (szPayload >> 24) & 0xff; -+ a[2] = (szPayload >> 16) & 0xff; -+ a[3] = (szPayload >> 8) & 0xff; -+ a[4] = szPayload & 0xff; -+ pParse->nBlob += 5; -+ } -+ if( aPayload ){ -+ pParse->nBlob += szPayload; -+ memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload); -+ } -+} -+ -+/* Change the payload size for the node at index i to be szPayload. -+*/ -+static int jsonBlobChangePayloadSize( -+ JsonParse *pParse, -+ u32 i, -+ u32 szPayload -+){ -+ u8 *a; -+ u8 szType; -+ u8 nExtra; -+ u8 nNeeded; -+ int delta; -+ if( pParse->oom ) return 0; -+ a = &pParse->aBlob[i]; -+ szType = a[0]>>4; -+ if( szType<=11 ){ -+ nExtra = 0; -+ }else if( szType==12 ){ -+ nExtra = 1; -+ }else if( szType==13 ){ -+ nExtra = 2; -+ }else if( szType==14 ){ -+ nExtra = 4; -+ }else{ -+ nExtra = 8; -+ } -+ if( szPayload<=11 ){ -+ nNeeded = 0; -+ }else if( szPayload<=0xff ){ -+ nNeeded = 1; -+ }else if( szPayload<=0xffff ){ -+ nNeeded = 2; -+ }else{ -+ nNeeded = 4; -+ } -+ delta = nNeeded - nExtra; -+ if( delta ){ -+ u32 newSize = pParse->nBlob + delta; -+ if( delta>0 ){ -+ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){ -+ return 0; /* OOM error. Error state recorded in pParse->oom. */ -+ } -+ a = &pParse->aBlob[i]; -+ memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1)); -+ }else{ -+ memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta)); -+ } -+ pParse->nBlob = newSize; -+ } -+ if( nNeeded==0 ){ -+ a[0] = (a[0] & 0x0f) | (szPayload<<4); -+ }else if( nNeeded==1 ){ -+ a[0] = (a[0] & 0x0f) | 0xc0; -+ a[1] = szPayload & 0xff; -+ }else if( nNeeded==2 ){ -+ a[0] = (a[0] & 0x0f) | 0xd0; -+ a[1] = (szPayload >> 8) & 0xff; -+ a[2] = szPayload & 0xff; -+ }else{ -+ a[0] = (a[0] & 0x0f) | 0xe0; -+ a[1] = (szPayload >> 24) & 0xff; -+ a[2] = (szPayload >> 16) & 0xff; -+ a[3] = (szPayload >> 8) & 0xff; -+ a[4] = szPayload & 0xff; -+ } -+ return delta; - } - - /* --** Return true if z[] begins with 4 (or more) hexadecimal digits -+** If z[0] is 'u' and is followed by exactly 4 hexadecimal character, -+** then set *pOp to JSONB_TEXTJ and return true. If not, do not make -+** any changes to *pOp and return false. - */ --static int jsonIs4Hex(const char *z){ -- int i; -- for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0; -+static int jsonIs4HexB(const char *z, int *pOp){ -+ if( z[0]!='u' ) return 0; -+ if( !jsonIs4Hex(&z[1]) ) return 0; -+ *pOp = JSONB_TEXTJ; - return 1; - } - - /* --** Parse a single JSON value which begins at pParse->zJson[i]. Return the --** index of the first character past the end of the value parsed. -+** Check a single element of the JSONB in pParse for validity. -+** -+** The element to be checked starts at offset i and must end at on the -+** last byte before iEnd. -+** -+** Return 0 if everything is correct. Return the 1-based byte offset of the -+** error if a problem is detected. (In other words, if the error is at offset -+** 0, return 1). -+*/ -+static u32 jsonbValidityCheck( -+ const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */ -+ u32 i, /* Start of element as pParse->aBlob[i] */ -+ u32 iEnd, /* One more than the last byte of the element */ -+ u32 iDepth /* Current nesting depth */ -+){ -+ u32 n, sz, j, k; -+ const u8 *z; -+ u8 x; -+ if( iDepth>JSON_MAX_DEPTH ) return i+1; -+ sz = 0; -+ n = jsonbPayloadSize(pParse, i, &sz); -+ if( NEVER(n==0) ) return i+1; /* Checked by caller */ -+ if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */ -+ z = pParse->aBlob; -+ x = z[i] & 0x0f; -+ switch( x ){ -+ case JSONB_NULL: -+ case JSONB_TRUE: -+ case JSONB_FALSE: { -+ return n+sz==1 ? 0 : i+1; -+ } -+ case JSONB_INT: { -+ if( sz<1 ) return i+1; -+ j = i+n; -+ if( z[j]=='-' ){ -+ j++; -+ if( sz<2 ) return i+1; -+ } -+ k = i+n+sz; -+ while( jk ) return j+1; -+ if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1; -+ j++; -+ } -+ for(; j0 ) return j+1; -+ if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){ -+ return j+1; -+ } -+ seen = 1; -+ continue; -+ } -+ if( z[j]=='e' || z[j]=='E' ){ -+ if( seen==2 ) return j+1; -+ if( j==k-1 ) return j+1; -+ if( z[j+1]=='+' || z[j+1]=='-' ){ -+ j++; -+ if( j==k-1 ) return j+1; -+ } -+ seen = 2; -+ continue; -+ } -+ return j+1; -+ } -+ if( seen==0 ) return i+1; -+ return 0; -+ } -+ case JSONB_TEXT: { -+ j = i+n; -+ k = j+sz; -+ while( j=k ){ -+ return j+1; -+ }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){ -+ j++; -+ }else if( z[j+1]=='u' ){ -+ if( j+5>=k ) return j+1; -+ if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1; -+ j++; -+ }else if( x!=JSONB_TEXT5 ){ -+ return j+1; -+ }else{ -+ u32 c = 0; -+ u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c); -+ if( c==JSON_INVALID_CHAR ) return j+1; -+ j += szC - 1; -+ } -+ } -+ j++; -+ } -+ return 0; -+ } -+ case JSONB_TEXTRAW: { -+ return 0; -+ } -+ case JSONB_ARRAY: { -+ u32 sub; -+ j = i+n; -+ k = j+sz; -+ while( jk ) return j+1; -+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); -+ if( sub ) return sub; -+ j += n + sz; -+ } -+ assert( j==k ); -+ return 0; -+ } -+ case JSONB_OBJECT: { -+ u32 cnt = 0; -+ u32 sub; -+ j = i+n; -+ k = j+sz; -+ while( jk ) return j+1; -+ if( (cnt & 1)==0 ){ -+ x = z[j] & 0x0f; -+ if( xJSONB_TEXTRAW ) return j+1; -+ } -+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); -+ if( sub ) return sub; -+ cnt++; -+ j += n + sz; -+ } -+ assert( j==k ); -+ if( (cnt & 1)!=0 ) return j+1; -+ return 0; -+ } -+ default: { -+ return i+1; -+ } -+ } -+} -+ -+/* -+** Translate a single element of JSON text at pParse->zJson[i] into -+** its equivalent binary JSONB representation. Append the translation into -+** pParse->aBlob[] beginning at pParse->nBlob. The size of -+** pParse->aBlob[] is increased as necessary. - ** --** Return negative for a syntax error. Special cases: return -2 if the --** first non-whitespace character is '}' and return -3 if the first --** non-whitespace character is ']'. -+** Return the index of the first character past the end of the element parsed, -+** or one of the following special result codes: -+** -+** 0 End of input -+** -1 Syntax error or OOM -+** -2 '}' seen \ -+** -3 ']' seen \___ For these returns, pParse->iErr is set to -+** -4 ',' seen / the index in zJson[] of the seen character -+** -5 ':' seen / - */ --static int jsonParseValue(JsonParse *pParse, u32 i){ -+static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){ - char c; - u32 j; -- int iThis; -+ u32 iThis, iStart; - int x; -- JsonNode *pNode; -+ u8 t; - const char *z = pParse->zJson; -- while( safe_isspace(z[i]) ){ i++; } -- if( (c = z[i])=='{' ){ -+json_parse_restart: -+ switch( (u8)z[i] ){ -+ case '{': { - /* Parse object */ -- iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); -- if( iThis<0 ) return -1; -+ iThis = pParse->nBlob; -+ jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0); -+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){ -+ pParse->iErr = i; -+ return -1; -+ } -+ iStart = pParse->nBlob; - for(j=i+1;;j++){ -- while( safe_isspace(z[j]) ){ j++; } -- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; -- x = jsonParseValue(pParse, j); -- if( x<0 ){ -- pParse->iDepth--; -- if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1; -- return -1; -+ u32 iBlob = pParse->nBlob; -+ x = jsonTranslateTextToBlob(pParse, j); -+ if( x<=0 ){ -+ int op; -+ if( x==(-2) ){ -+ j = pParse->iErr; -+ if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1; -+ break; -+ } -+ j += json5Whitespace(&z[j]); -+ op = JSONB_TEXT; -+ if( sqlite3JsonId1(z[j]) -+ || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op)) -+ ){ -+ int k = j+1; -+ while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) -+ || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op)) -+ ){ -+ k++; -+ } -+ assert( iBlob==pParse->nBlob ); -+ jsonBlobAppendNode(pParse, op, k-j, &z[j]); -+ pParse->hasNonstd = 1; -+ x = k; -+ }else{ -+ if( x!=-1 ) pParse->iErr = j; -+ return -1; -+ } - } - if( pParse->oom ) return -1; -- pNode = &pParse->aNode[pParse->nNode-1]; -- if( pNode->eType!=JSON_STRING ) return -1; -- pNode->jnFlags |= JNODE_LABEL; -+ t = pParse->aBlob[iBlob] & 0x0f; -+ if( tJSONB_TEXTRAW ){ -+ pParse->iErr = j; -+ return -1; -+ } - j = x; -- while( safe_isspace(z[j]) ){ j++; } -- if( z[j]!=':' ) return -1; -- j++; -- x = jsonParseValue(pParse, j); -- pParse->iDepth--; -- if( x<0 ) return -1; -+ if( z[j]==':' ){ -+ j++; -+ }else{ -+ if( jsonIsspace(z[j]) ){ -+ /* strspn() is not helpful here */ -+ do{ j++; }while( jsonIsspace(z[j]) ); -+ if( z[j]==':' ){ -+ j++; -+ goto parse_object_value; -+ } -+ } -+ x = jsonTranslateTextToBlob(pParse, j); -+ if( x!=(-5) ){ -+ if( x!=(-1) ) pParse->iErr = j; -+ return -1; -+ } -+ j = pParse->iErr+1; -+ } -+ parse_object_value: -+ x = jsonTranslateTextToBlob(pParse, j); -+ if( x<=0 ){ -+ if( x!=(-1) ) pParse->iErr = j; -+ return -1; -+ } - j = x; -- while( safe_isspace(z[j]) ){ j++; } -- c = z[j]; -- if( c==',' ) continue; -- if( c!='}' ) return -1; -- break; -+ if( z[j]==',' ){ -+ continue; -+ }else if( z[j]=='}' ){ -+ break; -+ }else{ -+ if( jsonIsspace(z[j]) ){ -+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces); -+ if( z[j]==',' ){ -+ continue; -+ }else if( z[j]=='}' ){ -+ break; -+ } -+ } -+ x = jsonTranslateTextToBlob(pParse, j); -+ if( x==(-4) ){ -+ j = pParse->iErr; -+ continue; -+ } -+ if( x==(-2) ){ -+ j = pParse->iErr; -+ break; -+ } -+ } -+ pParse->iErr = j; -+ return -1; - } -- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; -+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); -+ pParse->iDepth--; - return j+1; -- }else if( c=='[' ){ -+ } -+ case '[': { - /* Parse array */ -- iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); -- if( iThis<0 ) return -1; -+ iThis = pParse->nBlob; -+ assert( i<=(u32)pParse->nJson ); -+ jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); -+ iStart = pParse->nBlob; -+ if( pParse->oom ) return -1; -+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){ -+ pParse->iErr = i; -+ return -1; -+ } - for(j=i+1;;j++){ -- while( safe_isspace(z[j]) ){ j++; } -- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; -- x = jsonParseValue(pParse, j); -- pParse->iDepth--; -- if( x<0 ){ -- if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1; -+ x = jsonTranslateTextToBlob(pParse, j); -+ if( x<=0 ){ -+ if( x==(-3) ){ -+ j = pParse->iErr; -+ if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1; -+ break; -+ } -+ if( x!=(-1) ) pParse->iErr = j; - return -1; - } - j = x; -- while( safe_isspace(z[j]) ){ j++; } -- c = z[j]; -- if( c==',' ) continue; -- if( c!=']' ) return -1; -- break; -+ if( z[j]==',' ){ -+ continue; -+ }else if( z[j]==']' ){ -+ break; -+ }else{ -+ if( jsonIsspace(z[j]) ){ -+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces); -+ if( z[j]==',' ){ -+ continue; -+ }else if( z[j]==']' ){ -+ break; -+ } -+ } -+ x = jsonTranslateTextToBlob(pParse, j); -+ if( x==(-4) ){ -+ j = pParse->iErr; -+ continue; -+ } -+ if( x==(-3) ){ -+ j = pParse->iErr; -+ break; -+ } -+ } -+ pParse->iErr = j; -+ return -1; - } -- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; -+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); -+ pParse->iDepth--; - return j+1; -- }else if( c=='"' ){ -+ } -+ case '\'': { -+ u8 opcode; -+ char cDelim; -+ pParse->hasNonstd = 1; -+ opcode = JSONB_TEXT; -+ goto parse_string; -+ case '"': - /* Parse string */ -- u8 jnFlags = 0; -+ opcode = JSONB_TEXT; -+ parse_string: -+ cDelim = z[i]; - j = i+1; -- for(;;){ -- c = z[j]; -- if( (c & ~0x1f)==0 ){ -- /* Control characters are not allowed in strings */ -- return -1; -+ while( 1 /*exit-by-break*/ ){ -+ if( jsonIsOk[(u8)z[j]] ){ -+ if( !jsonIsOk[(u8)z[j+1]] ){ -+ j += 1; -+ }else if( !jsonIsOk[(u8)z[j+2]] ){ -+ j += 2; -+ }else{ -+ j += 3; -+ continue; -+ } - } -- if( c=='\\' ){ -+ c = z[j]; -+ if( c==cDelim ){ -+ break; -+ }else if( c=='\\' ){ - c = z[++j]; - if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' - || c=='n' || c=='r' || c=='t' -- || (c=='u' && jsonIs4Hex(z+j+1)) ){ -- jnFlags = JNODE_ESCAPE; -+ || (c=='u' && jsonIs4Hex(&z[j+1])) ){ -+ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ; -+ }else if( c=='\'' || c=='v' || c=='\n' -+#ifdef SQLITE_BUG_COMPATIBLE_20250510 -+ || (c=='0') /* Legacy bug compatible */ -+#else -+ || (c=='0' && !sqlite3Isdigit(z[j+1])) /* Correct implementation */ -+#endif -+ || (0xe2==(u8)c && 0x80==(u8)z[j+1] -+ && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) -+ || (c=='x' && jsonIs2Hex(&z[j+1])) ){ -+ opcode = JSONB_TEXT5; -+ pParse->hasNonstd = 1; -+ }else if( c=='\r' ){ -+ if( z[j+1]=='\n' ) j++; -+ opcode = JSONB_TEXT5; -+ pParse->hasNonstd = 1; - }else{ -+ pParse->iErr = j; -+ return -1; -+ } -+ }else if( c<=0x1f ){ -+ if( c==0 ){ -+ pParse->iErr = j; - return -1; - } -+ /* Control characters are not allowed in canonical JSON string -+ ** literals, but are allowed in JSON5 string literals. */ -+ opcode = JSONB_TEXT5; -+ pParse->hasNonstd = 1; - }else if( c=='"' ){ -- break; -+ opcode = JSONB_TEXT5; - } - j++; - } -- jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); -- if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; -+ jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]); - return j+1; -- }else if( c=='n' -- && strncmp(z+i,"null",4)==0 -- && !safe_isalnum(z[i+4]) ){ -- jsonParseAddNode(pParse, JSON_NULL, 0, 0); -- return i+4; -- }else if( c=='t' -- && strncmp(z+i,"true",4)==0 -- && !safe_isalnum(z[i+4]) ){ -- jsonParseAddNode(pParse, JSON_TRUE, 0, 0); -- return i+4; -- }else if( c=='f' -- && strncmp(z+i,"false",5)==0 -- && !safe_isalnum(z[i+5]) ){ -- jsonParseAddNode(pParse, JSON_FALSE, 0, 0); -- return i+5; -- }else if( c=='-' || (c>='0' && c<='9') ){ -+ } -+ case 't': { -+ if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ -+ jsonBlobAppendOneByte(pParse, JSONB_TRUE); -+ return i+4; -+ } -+ pParse->iErr = i; -+ return -1; -+ } -+ case 'f': { -+ if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ -+ jsonBlobAppendOneByte(pParse, JSONB_FALSE); -+ return i+5; -+ } -+ pParse->iErr = i; -+ return -1; -+ } -+ case '+': { -+ u8 seenE; -+ pParse->hasNonstd = 1; -+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ -+ goto parse_number; -+ case '.': -+ if( sqlite3Isdigit(z[i+1]) ){ -+ pParse->hasNonstd = 1; -+ t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ -+ seenE = 0; -+ goto parse_number_2; -+ } -+ pParse->iErr = i; -+ return -1; -+ case '-': -+ case '0': -+ case '1': -+ case '2': -+ case '3': -+ case '4': -+ case '5': -+ case '6': -+ case '7': -+ case '8': -+ case '9': - /* Parse number */ -- u8 seenDP = 0; -- u8 seenE = 0; -+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ -+ parse_number: -+ seenE = 0; - assert( '-' < '0' ); -+ assert( '+' < '0' ); -+ assert( '.' < '0' ); -+ c = z[i]; -+ - if( c<='0' ){ -- j = c=='-' ? i+1 : i; -- if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1; -+ if( c=='0' ){ -+ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ -+ assert( t==0x00 ); -+ pParse->hasNonstd = 1; -+ t = 0x01; -+ for(j=i+3; sqlite3Isxdigit(z[j]); j++){} -+ goto parse_number_finish; -+ }else if( sqlite3Isdigit(z[i+1]) ){ -+ pParse->iErr = i+1; -+ return -1; -+ } -+ }else{ -+ if( !sqlite3Isdigit(z[i+1]) ){ -+ /* JSON5 allows for "+Infinity" and "-Infinity" using exactly -+ ** that case. SQLite also allows these in any case and it allows -+ ** "+inf" and "-inf". */ -+ if( (z[i+1]=='I' || z[i+1]=='i') -+ && sqlite3StrNICmp(&z[i+1], "inf",3)==0 -+ ){ -+ pParse->hasNonstd = 1; -+ if( z[i]=='-' ){ -+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); -+ }else{ -+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); -+ } -+ return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); -+ } -+ if( z[i+1]=='.' ){ -+ pParse->hasNonstd = 1; -+ t |= 0x01; -+ goto parse_number_2; -+ } -+ pParse->iErr = i; -+ return -1; -+ } -+ if( z[i+1]=='0' ){ -+ if( sqlite3Isdigit(z[i+2]) ){ -+ pParse->iErr = i+1; -+ return -1; -+ }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ -+ pParse->hasNonstd = 1; -+ t |= 0x01; -+ for(j=i+4; sqlite3Isxdigit(z[j]); j++){} -+ goto parse_number_finish; -+ } -+ } -+ } - } -- j = i+1; -- for(;; j++){ -+ -+ parse_number_2: -+ for(j=i+1;; j++){ - c = z[j]; -- if( c>='0' && c<='9' ) continue; -+ if( sqlite3Isdigit(c) ) continue; - if( c=='.' ){ -- if( z[j-1]=='-' ) return -1; -- if( seenDP ) return -1; -- seenDP = 1; -+ if( (t & 0x02)!=0 ){ -+ pParse->iErr = j; -+ return -1; -+ } -+ t |= 0x02; - continue; - } - if( c=='e' || c=='E' ){ -- if( z[j-1]<'0' ) return -1; -- if( seenE ) return -1; -- seenDP = seenE = 1; -+ if( z[j-1]<'0' ){ -+ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ -+ pParse->hasNonstd = 1; -+ t |= 0x01; -+ }else{ -+ pParse->iErr = j; -+ return -1; -+ } -+ } -+ if( seenE ){ -+ pParse->iErr = j; -+ return -1; -+ } -+ t |= 0x02; -+ seenE = 1; - c = z[j+1]; - if( c=='+' || c=='-' ){ - j++; - c = z[j+1]; - } -- if( c<'0' || c>'9' ) return -1; -+ if( c<'0' || c>'9' ){ -+ pParse->iErr = j; -+ return -1; -+ } - continue; - } - break; - } -- if( z[j-1]<'0' ) return -1; -- jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, -- j - i, &z[i]); -+ if( z[j-1]<'0' ){ -+ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ -+ pParse->hasNonstd = 1; -+ t |= 0x01; -+ }else{ -+ pParse->iErr = j; -+ return -1; -+ } -+ } -+ parse_number_finish: -+ assert( JSONB_INT+0x01==JSONB_INT5 ); -+ assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 ); -+ assert( JSONB_INT+0x02==JSONB_FLOAT ); -+ if( z[i]=='+' ) i++; -+ jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]); - return j; -- }else if( c=='}' ){ -+ } -+ case '}': { -+ pParse->iErr = i; - return -2; /* End of {...} */ -- }else if( c==']' ){ -+ } -+ case ']': { -+ pParse->iErr = i; - return -3; /* End of [...] */ -- }else if( c==0 ){ -+ } -+ case ',': { -+ pParse->iErr = i; -+ return -4; /* List separator */ -+ } -+ case ':': { -+ pParse->iErr = i; -+ return -5; /* Object label/value separator */ -+ } -+ case 0: { - return 0; /* End of file */ -- }else{ -+ } -+ case 0x09: -+ case 0x0a: -+ case 0x0d: -+ case 0x20: { -+ i += 1 + (u32)strspn(&z[i+1], jsonSpaces); -+ goto json_parse_restart; -+ } -+ case 0x0b: -+ case 0x0c: -+ case '/': -+ case 0xc2: -+ case 0xe1: -+ case 0xe2: -+ case 0xe3: -+ case 0xef: { -+ j = json5Whitespace(&z[i]); -+ if( j>0 ){ -+ i += j; -+ pParse->hasNonstd = 1; -+ goto json_parse_restart; -+ } -+ pParse->iErr = i; -+ return -1; -+ } -+ case 'n': { -+ if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ -+ jsonBlobAppendOneByte(pParse, JSONB_NULL); -+ return i+4; -+ } -+ /* fall-through into the default case that checks for NaN */ -+ /* no break */ deliberate_fall_through -+ } -+ default: { -+ u32 k; -+ int nn; -+ c = z[i]; -+ for(k=0; khasNonstd = 1; -+ return i + nn; -+ } -+ pParse->iErr = i; - return -1; /* Syntax error */ - } -+ } /* End switch(z[i]) */ - } - -+ - /* - ** Parse a complete JSON string. Return 0 on success or non-zero if there --** are any errors. If an error occurs, free all memory associated with --** pParse. -+** are any errors. If an error occurs, free all memory held by pParse, -+** but not pParse itself. - ** --** pParse is uninitialized when this routine is called. -+** pParse must be initialized to an empty parse object prior to calling -+** this routine. - */ --static int jsonParse( -+static int jsonConvertTextToBlob( - JsonParse *pParse, /* Initialize and fill this JsonParse object */ -- sqlite3_context *pCtx, /* Report errors here */ -- const char *zJson /* Input JSON text to be parsed */ -+ sqlite3_context *pCtx /* Report errors here */ - ){ - int i; -- memset(pParse, 0, sizeof(*pParse)); -- if( zJson==0 ) return 1; -- pParse->zJson = zJson; -- i = jsonParseValue(pParse, 0); -+ const char *zJson = pParse->zJson; -+ i = jsonTranslateTextToBlob(pParse, 0); - if( pParse->oom ) i = -1; - if( i>0 ){ -+#ifdef SQLITE_DEBUG - assert( pParse->iDepth==0 ); -- while( safe_isspace(zJson[i]) ) i++; -- if( zJson[i] ) i = -1; -+ if( sqlite3Config.bJsonSelfcheck ){ -+ assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 ); -+ } -+#endif -+ while( jsonIsspace(zJson[i]) ) i++; -+ if( zJson[i] ){ -+ i += json5Whitespace(&zJson[i]); -+ if( zJson[i] ){ -+ if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1); -+ jsonParseReset(pParse); -+ return 1; -+ } -+ pParse->hasNonstd = 1; -+ } - } - if( i<=0 ){ - if( pCtx!=0 ){ -@@ -186402,453 +209923,1773 @@ static int jsonParse( - return 0; - } - --/* Mark node i of pParse as being a child of iParent. Call recursively --** to fill in all the descendants of node i. -+/* -+** The input string pStr is a well-formed JSON text string. Convert -+** this into the JSONB format and make it the return value of the -+** SQL function. - */ --static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ -- JsonNode *pNode = &pParse->aNode[i]; -- u32 j; -- pParse->aUp[i] = iParent; -- switch( pNode->eType ){ -- case JSON_ARRAY: { -- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){ -- jsonParseFillInParentage(pParse, i+j, i); -+static void jsonReturnStringAsBlob(JsonString *pStr){ -+ JsonParse px; -+ memset(&px, 0, sizeof(px)); -+ jsonStringTerminate(pStr); -+ if( pStr->eErr ){ -+ sqlite3_result_error_nomem(pStr->pCtx); -+ return; -+ } -+ px.zJson = pStr->zBuf; -+ px.nJson = pStr->nUsed; -+ px.db = sqlite3_context_db_handle(pStr->pCtx); -+ (void)jsonTranslateTextToBlob(&px, 0); -+ if( px.oom ){ -+ sqlite3DbFree(px.db, px.aBlob); -+ sqlite3_result_error_nomem(pStr->pCtx); -+ }else{ -+ assert( px.nBlobAlloc>0 ); -+ assert( !px.bReadOnly ); -+ sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC); -+ } -+} -+ -+/* The byte at index i is a node type-code. This routine -+** determines the payload size for that node and writes that -+** payload size in to *pSz. It returns the offset from i to the -+** beginning of the payload. Return 0 on error. -+*/ -+static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ -+ u8 x; -+ u32 sz; -+ u32 n; -+ assert( i<=pParse->nBlob ); -+ x = pParse->aBlob[i]>>4; -+ if( x<=11 ){ -+ sz = x; -+ n = 1; -+ }else if( x==12 ){ -+ if( i+1>=pParse->nBlob ){ -+ *pSz = 0; -+ return 0; -+ } -+ sz = pParse->aBlob[i+1]; -+ n = 2; -+ }else if( x==13 ){ -+ if( i+2>=pParse->nBlob ){ -+ *pSz = 0; -+ return 0; -+ } -+ sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; -+ n = 3; -+ }else if( x==14 ){ -+ if( i+4>=pParse->nBlob ){ -+ *pSz = 0; -+ return 0; -+ } -+ sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + -+ (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; -+ n = 5; -+ }else{ -+ if( i+8>=pParse->nBlob -+ || pParse->aBlob[i+1]!=0 -+ || pParse->aBlob[i+2]!=0 -+ || pParse->aBlob[i+3]!=0 -+ || pParse->aBlob[i+4]!=0 -+ ){ -+ *pSz = 0; -+ return 0; -+ } -+ sz = ((u32)pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) + -+ (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; -+ n = 9; -+ } -+ if( (i64)i+sz+n > pParse->nBlob -+ && (i64)i+sz+n > pParse->nBlob-pParse->delta -+ ){ -+ *pSz = 0; -+ return 0; -+ } -+ *pSz = sz; -+ return n; -+} -+ -+ -+/* -+** Translate the binary JSONB representation of JSON beginning at -+** pParse->aBlob[i] into a JSON text string. Append the JSON -+** text onto the end of pOut. Return the index in pParse->aBlob[] -+** of the first byte past the end of the element that is translated. -+** -+** If an error is detected in the BLOB input, the pOut->eErr flag -+** might get set to JSTRING_MALFORMED. But not all BLOB input errors -+** are detected. So a malformed JSONB input might either result -+** in an error, or in incorrect JSON. -+** -+** The pOut->eErr JSTRING_OOM flag is set on a OOM. -+*/ -+static u32 jsonTranslateBlobToText( -+ const JsonParse *pParse, /* the complete parse of the JSON */ -+ u32 i, /* Start rendering at this index */ -+ JsonString *pOut /* Write JSON here */ -+){ -+ u32 sz, n, j, iEnd; -+ -+ n = jsonbPayloadSize(pParse, i, &sz); -+ if( n==0 ){ -+ pOut->eErr |= JSTRING_MALFORMED; -+ return pParse->nBlob+1; -+ } -+ switch( pParse->aBlob[i] & 0x0f ){ -+ case JSONB_NULL: { -+ jsonAppendRawNZ(pOut, "null", 4); -+ return i+1; -+ } -+ case JSONB_TRUE: { -+ jsonAppendRawNZ(pOut, "true", 4); -+ return i+1; -+ } -+ case JSONB_FALSE: { -+ jsonAppendRawNZ(pOut, "false", 5); -+ return i+1; -+ } -+ case JSONB_INT: -+ case JSONB_FLOAT: { -+ if( sz==0 ) goto malformed_jsonb; -+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); -+ break; -+ } -+ case JSONB_INT5: { /* Integer literal in hexadecimal notation */ -+ u32 k = 2; -+ sqlite3_uint64 u = 0; -+ const char *zIn = (const char*)&pParse->aBlob[i+n]; -+ int bOverflow = 0; -+ if( sz==0 ) goto malformed_jsonb; -+ if( zIn[0]=='-' ){ -+ jsonAppendChar(pOut, '-'); -+ k++; -+ }else if( zIn[0]=='+' ){ -+ k++; -+ } -+ for(; keErr |= JSTRING_MALFORMED; -+ break; -+ }else if( (u>>60)!=0 ){ -+ bOverflow = 1; -+ }else{ -+ u = u*16 + sqlite3HexToInt(zIn[k]); -+ } -+ } -+ jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u); -+ break; -+ } -+ case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ -+ u32 k = 0; -+ const char *zIn = (const char*)&pParse->aBlob[i+n]; -+ if( sz==0 ) goto malformed_jsonb; -+ if( zIn[0]=='-' ){ -+ jsonAppendChar(pOut, '-'); -+ k++; -+ } -+ if( zIn[k]=='.' ){ -+ jsonAppendChar(pOut, '0'); -+ } -+ for(; knUsed+sz+2<=pOut->nAlloc || jsonStringGrow(pOut, sz+2)==0 ){ -+ pOut->zBuf[pOut->nUsed] = '"'; -+ memcpy(pOut->zBuf+pOut->nUsed+1,(const char*)&pParse->aBlob[i+n],sz); -+ pOut->zBuf[pOut->nUsed+sz+1] = '"'; -+ pOut->nUsed += sz+2; -+ } -+ break; -+ } -+ case JSONB_TEXT5: { -+ const char *zIn; -+ u32 k; -+ u32 sz2 = sz; -+ zIn = (const char*)&pParse->aBlob[i+n]; -+ jsonAppendChar(pOut, '"'); -+ while( sz2>0 ){ -+ for(k=0; k0 ){ -+ jsonAppendRawNZ(pOut, zIn, k); -+ if( k>=sz2 ){ -+ break; -+ } -+ zIn += k; -+ sz2 -= k; -+ } -+ if( zIn[0]=='"' ){ -+ jsonAppendRawNZ(pOut, "\\\"", 2); -+ zIn++; -+ sz2--; -+ continue; -+ } -+ if( zIn[0]<=0x1f ){ -+ if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break; -+ jsonAppendControlChar(pOut, zIn[0]); -+ zIn++; -+ sz2--; -+ continue; -+ } -+ assert( zIn[0]=='\\' ); -+ assert( sz2>=1 ); -+ if( sz2<2 ){ -+ pOut->eErr |= JSTRING_MALFORMED; -+ break; -+ } -+ switch( (u8)zIn[1] ){ -+ case '\'': -+ jsonAppendChar(pOut, '\''); -+ break; -+ case 'v': -+ jsonAppendRawNZ(pOut, "\\u0009", 6); -+ break; -+ case 'x': -+ if( sz2<4 ){ -+ pOut->eErr |= JSTRING_MALFORMED; -+ sz2 = 2; -+ break; -+ } -+ jsonAppendRawNZ(pOut, "\\u00", 4); -+ jsonAppendRawNZ(pOut, &zIn[2], 2); -+ zIn += 2; -+ sz2 -= 2; -+ break; -+ case '0': -+ jsonAppendRawNZ(pOut, "\\u0000", 6); -+ break; -+ case '\r': -+ if( sz2>2 && zIn[2]=='\n' ){ -+ zIn++; -+ sz2--; -+ } -+ break; -+ case '\n': -+ break; -+ case 0xe2: -+ /* '\' followed by either U+2028 or U+2029 is ignored as -+ ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29. -+ ** U+2029 is the same except for the last byte */ -+ if( sz2<4 -+ || 0x80!=(u8)zIn[2] -+ || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) -+ ){ -+ pOut->eErr |= JSTRING_MALFORMED; -+ sz2 = 2; -+ break; -+ } -+ zIn += 2; -+ sz2 -= 2; -+ break; -+ default: -+ jsonAppendRawNZ(pOut, zIn, 2); -+ break; -+ } -+ assert( sz2>=2 ); -+ zIn += 2; -+ sz2 -= 2; -+ } -+ jsonAppendChar(pOut, '"'); -+ break; -+ } -+ case JSONB_TEXTRAW: { -+ jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz); -+ break; -+ } -+ case JSONB_ARRAY: { -+ jsonAppendChar(pOut, '['); -+ j = i+n; -+ iEnd = j+sz; -+ while( jeErr==0 ){ -+ j = jsonTranslateBlobToText(pParse, j, pOut); -+ jsonAppendChar(pOut, ','); -+ } -+ if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; -+ if( sz>0 ) jsonStringTrimOneChar(pOut); -+ jsonAppendChar(pOut, ']'); -+ break; -+ } -+ case JSONB_OBJECT: { -+ int x = 0; -+ jsonAppendChar(pOut, '{'); -+ j = i+n; -+ iEnd = j+sz; -+ while( jeErr==0 ){ -+ j = jsonTranslateBlobToText(pParse, j, pOut); -+ jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); -+ } -+ if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; -+ if( sz>0 ) jsonStringTrimOneChar(pOut); -+ jsonAppendChar(pOut, '}'); -+ break; -+ } -+ -+ default: { -+ malformed_jsonb: -+ pOut->eErr |= JSTRING_MALFORMED; -+ break; -+ } -+ } -+ return i+n+sz; -+} -+ -+/* Context for recursion of json_pretty() -+*/ -+typedef struct JsonPretty JsonPretty; -+struct JsonPretty { -+ JsonParse *pParse; /* The BLOB being rendered */ -+ JsonString *pOut; /* Generate pretty output into this string */ -+ const char *zIndent; /* Use this text for indentation */ -+ u32 szIndent; /* Bytes in zIndent[] */ -+ u32 nIndent; /* Current level of indentation */ -+}; -+ -+/* Append indentation to the pretty JSON under construction */ -+static void jsonPrettyIndent(JsonPretty *pPretty){ -+ u32 jj; -+ for(jj=0; jjnIndent; jj++){ -+ jsonAppendRaw(pPretty->pOut, pPretty->zIndent, pPretty->szIndent); -+ } -+} -+ -+/* -+** Translate the binary JSONB representation of JSON beginning at -+** pParse->aBlob[i] into a JSON text string. Append the JSON -+** text onto the end of pOut. Return the index in pParse->aBlob[] -+** of the first byte past the end of the element that is translated. -+** -+** This is a variant of jsonTranslateBlobToText() that "pretty-prints" -+** the output. Extra whitespace is inserted to make the JSON easier -+** for humans to read. -+** -+** If an error is detected in the BLOB input, the pOut->eErr flag -+** might get set to JSTRING_MALFORMED. But not all BLOB input errors -+** are detected. So a malformed JSONB input might either result -+** in an error, or in incorrect JSON. -+** -+** The pOut->eErr JSTRING_OOM flag is set on a OOM. -+*/ -+static u32 jsonTranslateBlobToPrettyText( -+ JsonPretty *pPretty, /* Pretty-printing context */ -+ u32 i /* Start rendering at this index */ -+){ -+ u32 sz, n, j, iEnd; -+ const JsonParse *pParse = pPretty->pParse; -+ JsonString *pOut = pPretty->pOut; -+ n = jsonbPayloadSize(pParse, i, &sz); -+ if( n==0 ){ -+ pOut->eErr |= JSTRING_MALFORMED; -+ return pParse->nBlob+1; -+ } -+ switch( pParse->aBlob[i] & 0x0f ){ -+ case JSONB_ARRAY: { -+ j = i+n; -+ iEnd = j+sz; -+ jsonAppendChar(pOut, '['); -+ if( jnIndent++; -+ while( pOut->eErr==0 ){ -+ jsonPrettyIndent(pPretty); -+ j = jsonTranslateBlobToPrettyText(pPretty, j); -+ if( j>=iEnd ) break; -+ jsonAppendRawNZ(pOut, ",\n", 2); -+ } -+ jsonAppendChar(pOut, '\n'); -+ pPretty->nIndent--; -+ jsonPrettyIndent(pPretty); - } -+ jsonAppendChar(pOut, ']'); -+ i = iEnd; - break; - } -- case JSON_OBJECT: { -- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){ -- pParse->aUp[i+j] = i; -- jsonParseFillInParentage(pParse, i+j+1, i); -+ case JSONB_OBJECT: { -+ j = i+n; -+ iEnd = j+sz; -+ jsonAppendChar(pOut, '{'); -+ if( jnIndent++; -+ while( pOut->eErr==0 ){ -+ jsonPrettyIndent(pPretty); -+ j = jsonTranslateBlobToText(pParse, j, pOut); -+ if( j>iEnd ){ -+ pOut->eErr |= JSTRING_MALFORMED; -+ break; -+ } -+ jsonAppendRawNZ(pOut, ": ", 2); -+ j = jsonTranslateBlobToPrettyText(pPretty, j); -+ if( j>=iEnd ) break; -+ jsonAppendRawNZ(pOut, ",\n", 2); -+ } -+ jsonAppendChar(pOut, '\n'); -+ pPretty->nIndent--; -+ jsonPrettyIndent(pPretty); - } -+ jsonAppendChar(pOut, '}'); -+ i = iEnd; - break; - } - default: { -+ i = jsonTranslateBlobToText(pParse, i, pOut); - break; - } - } -+ return i; - } - - /* --** Compute the parentage of all nodes in a completed parse. -+** Given that a JSONB_ARRAY object starts at offset i, return -+** the number of entries in that array. - */ --static int jsonParseFindParents(JsonParse *pParse){ -- u32 *aUp; -- assert( pParse->aUp==0 ); -- aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); -- if( aUp==0 ){ -- pParse->oom = 1; -- return SQLITE_NOMEM; -+static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ -+ u32 n, sz, i, iEnd; -+ u32 k = 0; -+ n = jsonbPayloadSize(pParse, iRoot, &sz); -+ iEnd = iRoot+n+sz; -+ for(i=iRoot+n; n>0 && idelta. - */ --#define JSON_CACHE_ID (-429938) /* First cache entry */ --#define JSON_CACHE_SZ 4 /* Max number of cache entries */ -+static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ -+ u32 sz = 0; -+ u32 nBlob; -+ assert( pParse->delta!=0 ); -+ assert( pParse->nBlobAlloc >= pParse->nBlob ); -+ nBlob = pParse->nBlob; -+ pParse->nBlob = pParse->nBlobAlloc; -+ (void)jsonbPayloadSize(pParse, iRoot, &sz); -+ pParse->nBlob = nBlob; -+ sz += pParse->delta; -+ pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz); -+} - - /* --** Obtain a complete parse of the JSON found in the first argument --** of the argv array. Use the sqlite3_get_auxdata() cache for this --** parse if it is available. If the cache is not available or if it --** is no longer valid, parse the JSON again and return the new parse, --** and also register the new parse so that it will be available for --** future sqlite3_get_auxdata() calls. -+** If the JSONB at aIns[0..nIns-1] can be expanded (by denormalizing the -+** size field) by d bytes, then write the expansion into aOut[] and -+** return true. In this way, an overwrite happens without changing the -+** size of the JSONB, which reduces memcpy() operations and also make it -+** faster and easier to update the B-Tree entry that contains the JSONB -+** in the database. -+** -+** If the expansion of aIns[] by d bytes cannot be (easily) accomplished -+** then return false. -+** -+** The d parameter is guaranteed to be between 1 and 8. -+** -+** This routine is an optimization. A correct answer is obtained if it -+** always leaves the output unchanged and returns false. - */ --static JsonParse *jsonParseCached( -- sqlite3_context *pCtx, -- sqlite3_value **argv, -- sqlite3_context *pErrCtx -+static int jsonBlobOverwrite( -+ u8 *aOut, /* Overwrite here */ -+ const u8 *aIns, /* New content */ -+ u32 nIns, /* Bytes of new content */ -+ u32 d /* Need to expand new content by this much */ - ){ -- const char *zJson = (const char*)sqlite3_value_text(argv[0]); -- int nJson = sqlite3_value_bytes(argv[0]); -- JsonParse *p; -- JsonParse *pMatch = 0; -- int iKey; -- int iMinKey = 0; -- u32 iMinHold = 0xffffffff; -- u32 iMaxHold = 0; -- if( zJson==0 ) return 0; -- for(iKey=0; iKey>4 ){ -+ default: { /* aIns[] header size 1 */ -+ if( ((1<nJson==nJson -- && memcmp(p->zJson,zJson,nJson)==0 -- ){ -- p->nErr = 0; -- pMatch = p; -- }else if( p->iHoldiHold; -- iMinKey = iKey; -+ case 12: { /* aIns[] header size is 2 */ -+ if( ((1<iHold>iMaxHold ){ -- iMaxHold = p->iHold; -+ case 15: { /* aIns[] header size is 9 */ -+ return 0; /* No solution */ - } - } -- if( pMatch ){ -- pMatch->nErr = 0; -- pMatch->iHold = iMaxHold+1; -- return pMatch; -+ assert( i>=2 && i<=9 && aType[i-2]!=0 ); -+ aOut[0] = (aIns[0] & 0x0f) | aType[i-2]; -+ memcpy(&aOut[i], &aIns[szHdr], nIns-szHdr); -+ szPayload = nIns - szHdr; -+ while( 1/*edit-by-break*/ ){ -+ i--; -+ aOut[i] = szPayload & 0xff; -+ if( i==1 ) break; -+ szPayload >>= 8; - } -- p = sqlite3_malloc64( sizeof(*p) + nJson + 1 ); -- if( p==0 ){ -- sqlite3_result_error_nomem(pCtx); -- return 0; -+ assert( (szPayload>>8)==0 ); -+ return 1; -+} -+ -+/* -+** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of -+** content beginning at iDel, and replacing them with nIns bytes of -+** content given by aIns. -+** -+** nDel may be zero, in which case no bytes are removed. But iDel is -+** still important as new bytes will be insert beginning at iDel. -+** -+** aIns may be zero, in which case space is created to hold nIns bytes -+** beginning at iDel, but that space is uninitialized. -+** -+** Set pParse->oom if an OOM occurs. -+*/ -+static void jsonBlobEdit( -+ JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */ -+ u32 iDel, /* First byte to be removed */ -+ u32 nDel, /* Number of bytes to remove */ -+ const u8 *aIns, /* Content to insert */ -+ u32 nIns /* Bytes of content to insert */ -+){ -+ i64 d = (i64)nIns - (i64)nDel; -+ if( d<0 && d>=(-8) && aIns!=0 -+ && jsonBlobOverwrite(&pParse->aBlob[iDel], aIns, nIns, (int)-d) -+ ){ -+ return; - } -- memset(p, 0, sizeof(*p)); -- p->zJson = (char*)&p[1]; -- memcpy((char*)p->zJson, zJson, nJson+1); -- if( jsonParse(p, pErrCtx, p->zJson) ){ -- sqlite3_free(p); -- return 0; -+ if( d!=0 ){ -+ if( pParse->nBlob + d > pParse->nBlobAlloc ){ -+ jsonBlobExpand(pParse, pParse->nBlob+d); -+ if( pParse->oom ) return; -+ } -+ memmove(&pParse->aBlob[iDel+nIns], -+ &pParse->aBlob[iDel+nDel], -+ pParse->nBlob - (iDel+nDel)); -+ pParse->nBlob += d; -+ pParse->delta += d; -+ } -+ if( nIns && aIns ){ -+ memcpy(&pParse->aBlob[iDel], aIns, nIns); -+ } -+} -+ -+/* -+** Return the number of escaped newlines to be ignored. -+** An escaped newline is a one of the following byte sequences: -+** -+** 0x5c 0x0a -+** 0x5c 0x0d -+** 0x5c 0x0d 0x0a -+** 0x5c 0xe2 0x80 0xa8 -+** 0x5c 0xe2 0x80 0xa9 -+*/ -+static u32 jsonBytesToBypass(const char *z, u32 n){ -+ u32 i = 0; -+ while( i+10 ); -+ assert( z[0]=='\\' ); -+ if( n<2 ){ -+ *piOut = JSON_INVALID_CHAR; -+ return n; -+ } -+ switch( (u8)z[1] ){ -+ case 'u': { -+ u32 v, vlo; -+ if( n<6 ){ -+ *piOut = JSON_INVALID_CHAR; -+ return n; -+ } -+ v = jsonHexToInt4(&z[2]); -+ if( (v & 0xfc00)==0xd800 -+ && n>=12 -+ && z[6]=='\\' -+ && z[7]=='u' -+ && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00 -+ ){ -+ *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000; -+ return 12; -+ }else{ -+ *piOut = v; -+ return 6; -+ } -+ } -+ case 'b': { *piOut = '\b'; return 2; } -+ case 'f': { *piOut = '\f'; return 2; } -+ case 'n': { *piOut = '\n'; return 2; } -+ case 'r': { *piOut = '\r'; return 2; } -+ case 't': { *piOut = '\t'; return 2; } -+ case 'v': { *piOut = '\v'; return 2; } -+ case '0': { -+ /* JSON5 requires that the \0 escape not be followed by a digit. -+ ** But SQLite did not enforce this restriction in versions 3.42.0 -+ ** through 3.49.2. That was a bug. But some applications might have -+ ** come to depend on that bug. Use the SQLITE_BUG_COMPATIBLE_20250510 -+ ** option to restore the old buggy behavior. */ -+#ifdef SQLITE_BUG_COMPATIBLE_20250510 -+ /* Legacy bug-compatible behavior */ -+ *piOut = 0; -+#else -+ /* Correct behavior */ -+ *piOut = (n>2 && sqlite3Isdigit(z[2])) ? JSON_INVALID_CHAR : 0; -+#endif -+ return 2; -+ } -+ case '\'': -+ case '"': -+ case '/': -+ case '\\':{ *piOut = z[1]; return 2; } -+ case 'x': { -+ if( n<4 ){ -+ *piOut = JSON_INVALID_CHAR; -+ return n; -+ } -+ *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]); -+ return 4; -+ } -+ case 0xe2: -+ case '\r': -+ case '\n': { -+ u32 nSkip = jsonBytesToBypass(z, n); -+ if( nSkip==0 ){ -+ *piOut = JSON_INVALID_CHAR; -+ return n; -+ }else if( nSkip==n ){ -+ *piOut = 0; -+ return n; -+ }else if( z[nSkip]=='\\' ){ -+ return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut); -+ }else{ -+ int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut); -+ return nSkip + sz; -+ } -+ } -+ default: { -+ *piOut = JSON_INVALID_CHAR; -+ return 2; -+ } -+ } -+} -+ -+ -+/* -+** Compare two object labels. Return 1 if they are equal and -+** 0 if they differ. -+** -+** In this version, we know that one or the other or both of the -+** two comparands contains an escape sequence. -+*/ -+static SQLITE_NOINLINE int jsonLabelCompareEscaped( -+ const char *zLeft, /* The left label */ -+ u32 nLeft, /* Size of the left label in bytes */ -+ int rawLeft, /* True if zLeft contains no escapes */ -+ const char *zRight, /* The right label */ -+ u32 nRight, /* Size of the right label in bytes */ -+ int rawRight /* True if zRight is escape-free */ -+){ -+ u32 cLeft, cRight; -+ assert( rawLeft==0 || rawRight==0 ); -+ while( 1 /*exit-by-return*/ ){ -+ if( nLeft==0 ){ -+ cLeft = 0; -+ }else if( rawLeft || zLeft[0]!='\\' ){ -+ cLeft = ((u8*)zLeft)[0]; -+ if( cLeft>=0xc0 ){ -+ int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft); -+ zLeft += sz; -+ nLeft -= sz; -+ }else{ -+ zLeft++; -+ nLeft--; -+ } -+ }else{ -+ u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft); -+ zLeft += n; -+ assert( n<=nLeft ); -+ nLeft -= n; -+ } -+ if( nRight==0 ){ -+ cRight = 0; -+ }else if( rawRight || zRight[0]!='\\' ){ -+ cRight = ((u8*)zRight)[0]; -+ if( cRight>=0xc0 ){ -+ int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight); -+ zRight += sz; -+ nRight -= sz; -+ }else{ -+ zRight++; -+ nRight--; -+ } -+ }else{ -+ u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight); -+ zRight += n; -+ assert( n<=nRight ); -+ nRight -= n; -+ } -+ if( cLeft!=cRight ) return 0; -+ if( cLeft==0 ) return 1; - } -- p->nJson = nJson; -- p->iHold = iMaxHold+1; -- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, -- (void(*)(void*))jsonParseFree); -- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); - } - - /* --** Compare the OBJECT label at pNode against zKey,nKey. Return true on --** a match. -+** Compare two object labels. Return 1 if they are equal and -+** 0 if they differ. Return -1 if an OOM occurs. - */ --static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ -- if( pNode->jnFlags & JNODE_RAW ){ -- if( pNode->n!=nKey ) return 0; -- return strncmp(pNode->u.zJContent, zKey, nKey)==0; -+static int jsonLabelCompare( -+ const char *zLeft, /* The left label */ -+ u32 nLeft, /* Size of the left label in bytes */ -+ int rawLeft, /* True if zLeft contains no escapes */ -+ const char *zRight, /* The right label */ -+ u32 nRight, /* Size of the right label in bytes */ -+ int rawRight /* True if zRight is escape-free */ -+){ -+ if( rawLeft && rawRight ){ -+ /* Simpliest case: Neither label contains escapes. A simple -+ ** memcmp() is sufficient. */ -+ if( nLeft!=nRight ) return 0; -+ return memcmp(zLeft, zRight, nLeft)==0; - }else{ -- if( pNode->n!=nKey+2 ) return 0; -- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; -+ return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft, -+ zRight, nRight, rawRight); - } - } - --/* forward declaration */ --static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); -+/* -+** Error returns from jsonLookupStep() -+*/ -+#define JSON_LOOKUP_ERROR 0xffffffff -+#define JSON_LOOKUP_NOTFOUND 0xfffffffe -+#define JSON_LOOKUP_PATHERROR 0xfffffffd -+#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) -+ -+/* Forward declaration */ -+static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); -+ -+ -+/* This helper routine for jsonLookupStep() populates pIns with -+** binary data that is to be inserted into pParse. -+** -+** In the common case, pIns just points to pParse->aIns and pParse->nIns. -+** But if the zPath of the original edit operation includes path elements -+** that go deeper, additional substructure must be created. -+** -+** For example: -+** -+** json_insert('{}', '$.a.b.c', 123); -+** -+** The search stops at '$.a' But additional substructure must be -+** created for the ".b.c" part of the patch so that the final result -+** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with -+** the binary equivalent of {"b":{"c":123}} so that it can be inserted. -+** -+** The caller is responsible for resetting pIns when it has finished -+** using the substructure. -+*/ -+static u32 jsonCreateEditSubstructure( -+ JsonParse *pParse, /* The original JSONB that is being edited */ -+ JsonParse *pIns, /* Populate this with the blob data to insert */ -+ const char *zTail /* Tail of the path that determins substructure */ -+){ -+ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; -+ int rc; -+ memset(pIns, 0, sizeof(*pIns)); -+ pIns->db = pParse->db; -+ if( zTail[0]==0 ){ -+ /* No substructure. Just insert what is given in pParse. */ -+ pIns->aBlob = pParse->aIns; -+ pIns->nBlob = pParse->nIns; -+ rc = 0; -+ }else{ -+ /* Construct the binary substructure */ -+ pIns->nBlob = 1; -+ pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.']; -+ pIns->eEdit = pParse->eEdit; -+ pIns->nIns = pParse->nIns; -+ pIns->aIns = pParse->aIns; -+ rc = jsonLookupStep(pIns, 0, zTail, 0); -+ pParse->oom |= pIns->oom; -+ } -+ return rc; /* Error code only */ -+} - - /* --** Search along zPath to find the node specified. Return a pointer --** to that node, or NULL if zPath is malformed or if there is no such --** node. -+** Search along zPath to find the Json element specified. Return an -+** index into pParse->aBlob[] for the start of that element's value. -+** -+** If the value found by this routine is the value half of label/value pair -+** within an object, then set pPath->iLabel to the start of the corresponding -+** label, before returning. -+** -+** Return one of the JSON_LOOKUP error codes if problems are seen. - ** --** If pApnd!=0, then try to append new nodes to complete zPath if it is --** possible to do so and if no existing node corresponds to zPath. If --** new nodes are appended *pApnd is set to 1. -+** This routine will also modify the blob. If pParse->eEdit is one of -+** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be -+** made to the selected value. If an edit is performed, then the return -+** value does not necessarily point to the select element. If an edit -+** is performed, the return value is only useful for detecting error -+** conditions. - */ --static JsonNode *jsonLookupStep( -+static u32 jsonLookupStep( - JsonParse *pParse, /* The JSON to search */ -- u32 iRoot, /* Begin the search at this node */ -+ u32 iRoot, /* Begin the search at this element of aBlob[] */ - const char *zPath, /* The path to search */ -- int *pApnd, /* Append nodes to complete path if not NULL */ -- const char **pzErr /* Make *pzErr point to any syntax error in zPath */ -+ u32 iLabel /* Label if iRoot is a value of in an object */ - ){ -- u32 i, j, nKey; -+ u32 i, j, k, nKey, sz, n, iEnd, rc; - const char *zKey; -- JsonNode *pRoot = &pParse->aNode[iRoot]; -- if( zPath[0]==0 ) return pRoot; -- if( pRoot->jnFlags & JNODE_REPLACE ) return 0; -+ u8 x; -+ -+ if( zPath[0]==0 ){ -+ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){ -+ n = jsonbPayloadSize(pParse, iRoot, &sz); -+ sz += n; -+ if( pParse->eEdit==JEDIT_DEL ){ -+ if( iLabel>0 ){ -+ sz += iRoot - iLabel; -+ iRoot = iLabel; -+ } -+ jsonBlobEdit(pParse, iRoot, sz, 0, 0); -+ }else if( pParse->eEdit==JEDIT_INS ){ -+ /* Already exists, so json_insert() is a no-op */ -+ }else{ -+ /* json_set() or json_replace() */ -+ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); -+ } -+ } -+ pParse->iLabel = iLabel; -+ return iRoot; -+ } - if( zPath[0]=='.' ){ -- if( pRoot->eType!=JSON_OBJECT ) return 0; -+ int rawKey = 1; -+ x = pParse->aBlob[iRoot]; - zPath++; - if( zPath[0]=='"' ){ - zKey = zPath + 1; -- for(i=1; zPath[i] && zPath[i]!='"'; i++){} -+ for(i=1; zPath[i] && zPath[i]!='"'; i++){ -+ if( zPath[i]=='\\' && zPath[i+1]!=0 ) i++; -+ } - nKey = i-1; - if( zPath[i] ){ - i++; - }else{ -- *pzErr = zPath; -- return 0; -+ return JSON_LOOKUP_PATHERROR; - } -+ testcase( nKey==0 ); -+ rawKey = memchr(zKey, '\\', nKey)==0; - }else{ - zKey = zPath; - for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} - nKey = i; -- } -- if( nKey==0 ){ -- *pzErr = zPath; -- return 0; -- } -- j = 1; -- for(;;){ -- while( j<=pRoot->n ){ -- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){ -- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr); -- } -- j++; -- j += jsonNodeSize(&pRoot[j]); -+ if( nKey==0 ){ -+ return JSON_LOOKUP_PATHERROR; -+ } -+ } -+ if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND; -+ n = jsonbPayloadSize(pParse, iRoot, &sz); -+ j = iRoot + n; /* j is the index of a label */ -+ iEnd = j+sz; -+ while( jaBlob[j] & 0x0f; -+ if( xJSONB_TEXTRAW ) return JSON_LOOKUP_ERROR; -+ n = jsonbPayloadSize(pParse, j, &sz); -+ if( n==0 ) return JSON_LOOKUP_ERROR; -+ k = j+n; /* k is the index of the label text */ -+ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR; -+ zLabel = (const char*)&pParse->aBlob[k]; -+ rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW; -+ if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){ -+ u32 v = k+sz; /* v is the index of the value */ -+ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; -+ n = jsonbPayloadSize(pParse, v, &sz); -+ if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR; -+ assert( j>0 ); -+ rc = jsonLookupStep(pParse, v, &zPath[i], j); -+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); -+ return rc; - } -- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; -- iRoot += pRoot->u.iAppend; -- pRoot = &pParse->aNode[iRoot]; -- j = 1; -- } -- if( pApnd ){ -- u32 iStart, iLabel; -- JsonNode *pNode; -- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); -- iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); -- zPath += i; -- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); -- if( pParse->oom ) return 0; -- if( pNode ){ -- pRoot = &pParse->aNode[iRoot]; -- pRoot->u.iAppend = iStart - iRoot; -- pRoot->jnFlags |= JNODE_APPEND; -- pParse->aNode[iLabel].jnFlags |= JNODE_RAW; -- } -- return pNode; -+ j = k+sz; -+ if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; -+ n = jsonbPayloadSize(pParse, j, &sz); -+ if( n==0 ) return JSON_LOOKUP_ERROR; -+ j += n+sz; -+ } -+ if( j>iEnd ) return JSON_LOOKUP_ERROR; -+ if( pParse->eEdit>=JEDIT_INS ){ -+ u32 nIns; /* Total bytes to insert (label+value) */ -+ JsonParse v; /* BLOB encoding of the value to be inserted */ -+ JsonParse ix; /* Header of the label to be inserted */ -+ testcase( pParse->eEdit==JEDIT_INS ); -+ testcase( pParse->eEdit==JEDIT_SET ); -+ memset(&ix, 0, sizeof(ix)); -+ ix.db = pParse->db; -+ jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); -+ pParse->oom |= ix.oom; -+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); -+ if( !JSON_LOOKUP_ISERROR(rc) -+ && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) -+ ){ -+ assert( !pParse->oom ); -+ nIns = ix.nBlob + nKey + v.nBlob; -+ jsonBlobEdit(pParse, j, 0, 0, nIns); -+ if( !pParse->oom ){ -+ assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */ -+ assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */ -+ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); -+ k = j + ix.nBlob; -+ memcpy(&pParse->aBlob[k], zKey, nKey); -+ k += nKey; -+ memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); -+ if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot); -+ } -+ } -+ jsonParseReset(&v); -+ jsonParseReset(&ix); -+ return rc; - } - }else if( zPath[0]=='[' ){ -- i = 0; -- j = 1; -- while( safe_isdigit(zPath[j]) ){ -- i = i*10 + zPath[j] - '0'; -- j++; -+ x = pParse->aBlob[iRoot] & 0x0f; -+ if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; -+ n = jsonbPayloadSize(pParse, iRoot, &sz); -+ k = 0; -+ i = 1; -+ while( sqlite3Isdigit(zPath[i]) ){ -+ k = k*10 + zPath[i] - '0'; -+ i++; - } -- if( j<2 || zPath[j]!=']' ){ -+ if( i<2 || zPath[i]!=']' ){ - if( zPath[1]=='#' ){ -- JsonNode *pBase = pRoot; -- int iBase = iRoot; -- if( pRoot->eType!=JSON_ARRAY ) return 0; -- for(;;){ -- while( j<=pBase->n ){ -- if( (pBase[j].jnFlags & JNODE_REMOVE)==0 ) i++; -- j += jsonNodeSize(&pBase[j]); -- } -- if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; -- iBase += pBase->u.iAppend; -- pBase = &pParse->aNode[iBase]; -- j = 1; -- } -- j = 2; -- if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){ -- unsigned int x = 0; -- j = 3; -+ k = jsonbArrayCount(pParse, iRoot); -+ i = 2; -+ if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ -+ unsigned int nn = 0; -+ i = 3; - do{ -- x = x*10 + zPath[j] - '0'; -- j++; -- }while( safe_isdigit(zPath[j]) ); -- if( x>i ) return 0; -- i -= x; -+ nn = nn*10 + zPath[i] - '0'; -+ i++; -+ }while( sqlite3Isdigit(zPath[i]) ); -+ if( nn>k ) return JSON_LOOKUP_NOTFOUND; -+ k -= nn; - } -- if( zPath[j]!=']' ){ -- *pzErr = zPath; -- return 0; -+ if( zPath[i]!=']' ){ -+ return JSON_LOOKUP_PATHERROR; - } - }else{ -- *pzErr = zPath; -- return 0; -+ return JSON_LOOKUP_PATHERROR; - } - } -- if( pRoot->eType!=JSON_ARRAY ) return 0; -- zPath += j + 1; -- j = 1; -- for(;;){ -- while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){ -- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--; -- j += jsonNodeSize(&pRoot[j]); -+ j = iRoot+n; -+ iEnd = j+sz; -+ while( jdelta ) jsonAfterEditSizeAdjust(pParse, iRoot); -+ return rc; - } -- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; -- iRoot += pRoot->u.iAppend; -- pRoot = &pParse->aNode[iRoot]; -- j = 1; -- } -- if( j<=pRoot->n ){ -- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr); -- } -- if( i==0 && pApnd ){ -- u32 iStart; -- JsonNode *pNode; -- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); -- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); -- if( pParse->oom ) return 0; -- if( pNode ){ -- pRoot = &pParse->aNode[iRoot]; -- pRoot->u.iAppend = iStart - iRoot; -- pRoot->jnFlags |= JNODE_APPEND; -+ k--; -+ n = jsonbPayloadSize(pParse, j, &sz); -+ if( n==0 ) return JSON_LOOKUP_ERROR; -+ j += n+sz; -+ } -+ if( j>iEnd ) return JSON_LOOKUP_ERROR; -+ if( k>0 ) return JSON_LOOKUP_NOTFOUND; -+ if( pParse->eEdit>=JEDIT_INS ){ -+ JsonParse v; -+ testcase( pParse->eEdit==JEDIT_INS ); -+ testcase( pParse->eEdit==JEDIT_SET ); -+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); -+ if( !JSON_LOOKUP_ISERROR(rc) -+ && jsonBlobMakeEditable(pParse, v.nBlob) -+ ){ -+ assert( !pParse->oom ); -+ jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob); - } -- return pNode; -+ jsonParseReset(&v); -+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); -+ return rc; - } - }else{ -- *pzErr = zPath; -+ return JSON_LOOKUP_PATHERROR; - } -- return 0; -+ return JSON_LOOKUP_NOTFOUND; - } - - /* --** Append content to pParse that will complete zPath. Return a pointer --** to the inserted node, or return NULL if the append fails. -+** Convert a JSON BLOB into text and make that text the return value -+** of an SQL function. - */ --static JsonNode *jsonLookupAppend( -- JsonParse *pParse, /* Append content to the JSON parse */ -- const char *zPath, /* Description of content to append */ -- int *pApnd, /* Set this flag to 1 */ -- const char **pzErr /* Make this point to any syntax error */ -+static void jsonReturnTextJsonFromBlob( -+ sqlite3_context *ctx, -+ const u8 *aBlob, -+ u32 nBlob - ){ -- *pApnd = 1; -- if( zPath[0]==0 ){ -- jsonParseAddNode(pParse, JSON_NULL, 0, 0); -- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1]; -- } -- if( zPath[0]=='.' ){ -- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); -- }else if( strncmp(zPath,"[0]",3)==0 ){ -- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); -- }else{ -- return 0; -- } -- if( pParse->oom ) return 0; -- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr); -+ JsonParse x; -+ JsonString s; -+ -+ if( NEVER(aBlob==0) ) return; -+ memset(&x, 0, sizeof(x)); -+ x.aBlob = (u8*)aBlob; -+ x.nBlob = nBlob; -+ jsonStringInit(&s, ctx); -+ jsonTranslateBlobToText(&x, 0, &s); -+ jsonReturnString(&s, 0, 0); - } - -+ - /* --** Return the text of a syntax error message on a JSON path. Space is --** obtained from sqlite3_malloc(). -+** Return the value of the BLOB node at index i. -+** -+** If the value is a primitive, return it as an SQL value. -+** If the value is an array or object, return it as either -+** JSON text or the BLOB encoding, depending on the JSON_B flag -+** on the userdata. - */ --static char *jsonPathSyntaxError(const char *zErr){ -- return sqlite3_mprintf("JSON path error near '%q'", zErr); -+static void jsonReturnFromBlob( -+ JsonParse *pParse, /* Complete JSON parse tree */ -+ u32 i, /* Index of the node */ -+ sqlite3_context *pCtx, /* Return value for this function */ -+ int textOnly /* return text JSON. Disregard user-data */ -+){ -+ u32 n, sz; -+ int rc; -+ sqlite3 *db = sqlite3_context_db_handle(pCtx); -+ -+ n = jsonbPayloadSize(pParse, i, &sz); -+ if( n==0 ){ -+ sqlite3_result_error(pCtx, "malformed JSON", -1); -+ return; -+ } -+ switch( pParse->aBlob[i] & 0x0f ){ -+ case JSONB_NULL: { -+ if( sz ) goto returnfromblob_malformed; -+ sqlite3_result_null(pCtx); -+ break; -+ } -+ case JSONB_TRUE: { -+ if( sz ) goto returnfromblob_malformed; -+ sqlite3_result_int(pCtx, 1); -+ break; -+ } -+ case JSONB_FALSE: { -+ if( sz ) goto returnfromblob_malformed; -+ sqlite3_result_int(pCtx, 0); -+ break; -+ } -+ case JSONB_INT5: -+ case JSONB_INT: { -+ sqlite3_int64 iRes = 0; -+ char *z; -+ int bNeg = 0; -+ char x; -+ if( sz==0 ) goto returnfromblob_malformed; -+ x = (char)pParse->aBlob[i+n]; -+ if( x=='-' ){ -+ if( sz<2 ) goto returnfromblob_malformed; -+ n++; -+ sz--; -+ bNeg = 1; -+ } -+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); -+ if( z==0 ) goto returnfromblob_oom; -+ rc = sqlite3DecOrHexToI64(z, &iRes); -+ sqlite3DbFree(db, z); -+ if( rc==0 ){ -+ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes); -+ }else if( rc==3 && bNeg ){ -+ sqlite3_result_int64(pCtx, SMALLEST_INT64); -+ }else if( rc==1 ){ -+ goto returnfromblob_malformed; -+ }else{ -+ if( bNeg ){ n--; sz++; } -+ goto to_double; -+ } -+ break; -+ } -+ case JSONB_FLOAT5: -+ case JSONB_FLOAT: { -+ double r; -+ char *z; -+ if( sz==0 ) goto returnfromblob_malformed; -+ to_double: -+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); -+ if( z==0 ) goto returnfromblob_oom; -+ rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); -+ sqlite3DbFree(db, z); -+ if( rc<=0 ) goto returnfromblob_malformed; -+ sqlite3_result_double(pCtx, r); -+ break; -+ } -+ case JSONB_TEXTRAW: -+ case JSONB_TEXT: { -+ sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz, -+ SQLITE_TRANSIENT); -+ break; -+ } -+ case JSONB_TEXT5: -+ case JSONB_TEXTJ: { -+ /* Translate JSON formatted string into raw text */ -+ u32 iIn, iOut; -+ const char *z; -+ char *zOut; -+ u32 nOut = sz; -+ z = (const char*)&pParse->aBlob[i+n]; -+ zOut = sqlite3DbMallocRaw(db, ((u64)nOut)+1); -+ if( zOut==0 ) goto returnfromblob_oom; -+ for(iIn=iOut=0; iIn=2 ); -+ zOut[iOut++] = (char)(0xc0 | (v>>6)); -+ zOut[iOut++] = 0x80 | (v&0x3f); -+ }else if( v<0x10000 ){ -+ assert( szEscape>=3 ); -+ zOut[iOut++] = 0xe0 | (v>>12); -+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f); -+ zOut[iOut++] = 0x80 | (v&0x3f); -+ }else if( v==JSON_INVALID_CHAR ){ -+ /* Silently ignore illegal unicode */ -+ }else{ -+ assert( szEscape>=4 ); -+ zOut[iOut++] = 0xf0 | (v>>18); -+ zOut[iOut++] = 0x80 | ((v>>12)&0x3f); -+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f); -+ zOut[iOut++] = 0x80 | (v&0x3f); -+ } -+ iIn += szEscape - 1; -+ }else{ -+ zOut[iOut++] = c; -+ } -+ } /* end for() */ -+ assert( iOut<=nOut ); -+ zOut[iOut] = 0; -+ sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC); -+ break; -+ } -+ case JSONB_ARRAY: -+ case JSONB_OBJECT: { -+ int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); -+ if( flags & JSON_BLOB ){ -+ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT); -+ }else{ -+ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n); -+ } -+ break; -+ } -+ default: { -+ goto returnfromblob_malformed; -+ } -+ } -+ return; -+ -+returnfromblob_oom: -+ sqlite3_result_error_nomem(pCtx); -+ return; -+ -+returnfromblob_malformed: -+ sqlite3_result_error(pCtx, "malformed JSON", -1); -+ return; - } - - /* --** Do a node lookup using zPath. Return a pointer to the node on success. --** Return NULL if not found or if there is an error. -+** pArg is a function argument that might be an SQL value or a JSON -+** value. Figure out what it is and encode it as a JSONB blob. -+** Return the results in pParse. - ** --** On an error, write an error message into pCtx and increment the --** pParse->nErr counter. -+** pParse is uninitialized upon entry. This routine will handle the -+** initialization of pParse. The result will be contained in -+** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically -+** allocated (if pParse->nBlobAlloc is greater than zero) in which case -+** the caller is responsible for freeing the space allocated to pParse->aBlob -+** when it has finished with it. Or pParse->aBlob might be a static string -+** or a value obtained from sqlite3_value_blob(pArg). - ** --** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if --** nodes are appended. -+** If the argument is a BLOB that is clearly not a JSONB, then this -+** function might set an error message in ctx and return non-zero. -+** It might also set an error message and return non-zero on an OOM error. - */ --static JsonNode *jsonLookup( -- JsonParse *pParse, /* The JSON to search */ -- const char *zPath, /* The path to search */ -- int *pApnd, /* Append nodes to complete path if not NULL */ -- sqlite3_context *pCtx /* Report errors here, if not NULL */ --){ -- const char *zErr = 0; -- JsonNode *pNode = 0; -- char *zMsg; -- -- if( zPath==0 ) return 0; -- if( zPath[0]!='$' ){ -- zErr = zPath; -- goto lookup_err; -+static int jsonFunctionArgToBlob( -+ sqlite3_context *ctx, -+ sqlite3_value *pArg, -+ JsonParse *pParse -+){ -+ int eType = sqlite3_value_type(pArg); -+ static u8 aNull[] = { 0x00 }; -+ memset(pParse, 0, sizeof(pParse[0])); -+ pParse->db = sqlite3_context_db_handle(ctx); -+ switch( eType ){ -+ default: { -+ pParse->aBlob = aNull; -+ pParse->nBlob = 1; -+ return 0; -+ } -+ case SQLITE_BLOB: { -+ if( !jsonArgIsJsonb(pArg, pParse) ){ -+ sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1); -+ return 1; -+ } -+ break; -+ } -+ case SQLITE_TEXT: { -+ const char *zJson = (const char*)sqlite3_value_text(pArg); -+ int nJson = sqlite3_value_bytes(pArg); -+ if( zJson==0 ) return 1; -+ if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){ -+ pParse->zJson = (char*)zJson; -+ pParse->nJson = nJson; -+ if( jsonConvertTextToBlob(pParse, ctx) ){ -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ sqlite3DbFree(pParse->db, pParse->aBlob); -+ memset(pParse, 0, sizeof(pParse[0])); -+ return 1; -+ } -+ }else{ -+ jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson); -+ } -+ break; -+ } -+ case SQLITE_FLOAT: { -+ double r = sqlite3_value_double(pArg); -+ if( NEVER(sqlite3IsNaN(r)) ){ -+ jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0); -+ }else{ -+ int n = sqlite3_value_bytes(pArg); -+ const char *z = (const char*)sqlite3_value_text(pArg); -+ if( z==0 ) return 1; -+ if( z[0]=='I' ){ -+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); -+ }else if( z[0]=='-' && z[1]=='I' ){ -+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); -+ }else{ -+ jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z); -+ } -+ } -+ break; -+ } -+ case SQLITE_INTEGER: { -+ int n = sqlite3_value_bytes(pArg); -+ const char *z = (const char*)sqlite3_value_text(pArg); -+ if( z==0 ) return 1; -+ jsonBlobAppendNode(pParse, JSONB_INT, n, z); -+ break; -+ } -+ } -+ if( pParse->oom ){ -+ sqlite3_result_error_nomem(ctx); -+ return 1; -+ }else{ -+ return 0; - } -- zPath++; -- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr); -- if( zErr==0 ) return pNode; -+} - --lookup_err: -- pParse->nErr++; -- assert( zErr!=0 && pCtx!=0 ); -- zMsg = jsonPathSyntaxError(zErr); -+/* -+** Generate a bad path error. -+** -+** If ctx is not NULL then push the error message into ctx and return NULL. -+** If ctx is NULL, then return the text of the error message. -+*/ -+static char *jsonBadPathError( -+ sqlite3_context *ctx, /* The function call containing the error */ -+ const char *zPath /* The path with the problem */ -+){ -+ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); -+ if( ctx==0 ) return zMsg; - if( zMsg ){ -- sqlite3_result_error(pCtx, zMsg, -1); -+ sqlite3_result_error(ctx, zMsg, -1); - sqlite3_free(zMsg); - }else{ -- sqlite3_result_error_nomem(pCtx); -+ sqlite3_result_error_nomem(ctx); - } - return 0; - } - -+/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent -+** arguments come in pairs where each pair contains a JSON path and -+** content to insert or set at that patch. Do the updates -+** and return the result. -+** -+** The specific operation is determined by eEdit, which can be one -+** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. -+*/ -+static void jsonInsertIntoBlob( -+ sqlite3_context *ctx, -+ int argc, -+ sqlite3_value **argv, -+ int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ -+){ -+ int i; -+ u32 rc = 0; -+ const char *zPath = 0; -+ int flgs; -+ JsonParse *p; -+ JsonParse ax; -+ -+ assert( (argc&1)==1 ); -+ flgs = argc==1 ? 0 : JSON_EDITABLE; -+ p = jsonParseFuncArg(ctx, argv[0], flgs); -+ if( p==0 ) return; -+ for(i=1; inBlob, ax.aBlob, ax.nBlob); -+ } -+ rc = 0; -+ }else{ -+ p->eEdit = eEdit; -+ p->nIns = ax.nBlob; -+ p->aIns = ax.aBlob; -+ p->delta = 0; -+ rc = jsonLookupStep(p, 0, zPath+1, 0); -+ } -+ jsonParseReset(&ax); -+ if( rc==JSON_LOOKUP_NOTFOUND ) continue; -+ if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; -+ } -+ jsonReturnParse(ctx, p); -+ jsonParseFree(p); -+ return; -+ -+jsonInsertIntoBlob_patherror: -+ jsonParseFree(p); -+ if( rc==JSON_LOOKUP_ERROR ){ -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ }else{ -+ jsonBadPathError(ctx, zPath); -+ } -+ return; -+} -+ -+/* -+** If pArg is a blob that seems like a JSONB blob, then initialize -+** p to point to that JSONB and return TRUE. If pArg does not seem like -+** a JSONB blob, then return FALSE. -+** -+** For small BLOBs (having no more than 7 bytes of payload) a full -+** validity check is done. So for small BLOBs this routine only returns -+** true if the value is guaranteed to be a valid JSONB. For larger BLOBs -+** (8 byte or more of payload) only the size of the outermost element is -+** checked to verify that the BLOB is superficially valid JSONB. -+** -+** A full JSONB validation is done on smaller BLOBs because those BLOBs might -+** also be text JSON that has been incorrectly cast into a BLOB. -+** (See tag-20240123-a and https://sqlite.org/forum/forumpost/012136abd5) -+** If the BLOB is 9 bytes are larger, then it is not possible for the -+** superficial size check done here to pass if the input is really text -+** JSON so we do not need to look deeper in that case. -+** -+** Why we only need to do full JSONB validation for smaller BLOBs: -+** -+** The first byte of valid JSON text must be one of: '{', '[', '"', ' ', '\n', -+** '\r', '\t', '-', or a digit '0' through '9'. Of these, only a subset -+** can also be the first byte of JSONB: '{', '[', and digits '3' -+** through '9'. In every one of those cases, the payload size is 7 bytes -+** or less. So if we do full JSONB validation for every BLOB where the -+** payload is less than 7 bytes, we will never get a false positive for -+** JSONB on an input that is really text JSON. -+*/ -+static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){ -+ u32 n, sz = 0; -+ u8 c; -+ if( sqlite3_value_type(pArg)!=SQLITE_BLOB ) return 0; -+ p->aBlob = (u8*)sqlite3_value_blob(pArg); -+ p->nBlob = (u32)sqlite3_value_bytes(pArg); -+ if( p->nBlob>0 -+ && ALWAYS(p->aBlob!=0) -+ && ((c = p->aBlob[0]) & 0x0f)<=JSONB_OBJECT -+ && (n = jsonbPayloadSize(p, 0, &sz))>0 -+ && sz+n==p->nBlob -+ && ((c & 0x0f)>JSONB_FALSE || sz==0) -+ && (sz>7 -+ || (c!=0x7b && c!=0x5b && !sqlite3Isdigit(c)) -+ || jsonbValidityCheck(p, 0, p->nBlob, 1)==0) -+ ){ -+ return 1; -+ } -+ p->aBlob = 0; -+ p->nBlob = 0; -+ return 0; -+} - - /* --** Report the wrong number of arguments for json_insert(), json_replace() --** or json_set(). -+** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, -+** from the SQL function argument pArg. Return a pointer to the new -+** JsonParse object. -+** -+** Ownership of the new JsonParse object is passed to the caller. The -+** caller should invoke jsonParseFree() on the return value when it -+** has finished using it. -+** -+** If any errors are detected, an appropriate error messages is set -+** using sqlite3_result_error() or the equivalent and this routine -+** returns NULL. This routine also returns NULL if the pArg argument -+** is an SQL NULL value, but no error message is set in that case. This -+** is so that SQL functions that are given NULL arguments will return -+** a NULL value. - */ --static void jsonWrongNumArgs( -- sqlite3_context *pCtx, -- const char *zFuncName -+static JsonParse *jsonParseFuncArg( -+ sqlite3_context *ctx, -+ sqlite3_value *pArg, -+ u32 flgs - ){ -- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", -- zFuncName); -- sqlite3_result_error(pCtx, zMsg, -1); -- sqlite3_free(zMsg); -+ int eType; /* Datatype of pArg */ -+ JsonParse *p = 0; /* Value to be returned */ -+ JsonParse *pFromCache = 0; /* Value taken from cache */ -+ sqlite3 *db; /* The database connection */ -+ -+ assert( ctx!=0 ); -+ eType = sqlite3_value_type(pArg); -+ if( eType==SQLITE_NULL ){ -+ return 0; -+ } -+ pFromCache = jsonCacheSearch(ctx, pArg); -+ if( pFromCache ){ -+ pFromCache->nJPRef++; -+ if( (flgs & JSON_EDITABLE)==0 ){ -+ return pFromCache; -+ } -+ } -+ db = sqlite3_context_db_handle(ctx); -+rebuild_from_cache: -+ p = sqlite3DbMallocZero(db, sizeof(*p)); -+ if( p==0 ) goto json_pfa_oom; -+ memset(p, 0, sizeof(*p)); -+ p->db = db; -+ p->nJPRef = 1; -+ if( pFromCache!=0 ){ -+ u32 nBlob = pFromCache->nBlob; -+ p->aBlob = sqlite3DbMallocRaw(db, nBlob); -+ if( p->aBlob==0 ) goto json_pfa_oom; -+ memcpy(p->aBlob, pFromCache->aBlob, nBlob); -+ p->nBlobAlloc = p->nBlob = nBlob; -+ p->hasNonstd = pFromCache->hasNonstd; -+ jsonParseFree(pFromCache); -+ return p; -+ } -+ if( eType==SQLITE_BLOB ){ -+ if( jsonArgIsJsonb(pArg,p) ){ -+ if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){ -+ goto json_pfa_oom; -+ } -+ return p; -+ } -+ /* If the blob is not valid JSONB, fall through into trying to cast -+ ** the blob into text which is then interpreted as JSON. (tag-20240123-a) -+ ** -+ ** This goes against all historical documentation about how the SQLite -+ ** JSON functions were suppose to work. From the beginning, blob was -+ ** reserved for expansion and a blob value should have raised an error. -+ ** But it did not, due to a bug. And many applications came to depend -+ ** upon this buggy behavior, especially when using the CLI and reading -+ ** JSON text using readfile(), which returns a blob. For this reason -+ ** we will continue to support the bug moving forward. -+ ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d -+ */ -+ } -+ p->zJson = (char*)sqlite3_value_text(pArg); -+ p->nJson = sqlite3_value_bytes(pArg); -+ if( db->mallocFailed ) goto json_pfa_oom; -+ if( p->nJson==0 ) goto json_pfa_malformed; -+ assert( p->zJson!=0 ); -+ if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ -+ if( flgs & JSON_KEEPERROR ){ -+ p->nErr = 1; -+ return p; -+ }else{ -+ jsonParseFree(p); -+ return 0; -+ } -+ }else{ -+ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref); -+ int rc; -+ if( !isRCStr ){ -+ char *zNew = sqlite3RCStrNew( p->nJson ); -+ if( zNew==0 ) goto json_pfa_oom; -+ memcpy(zNew, p->zJson, p->nJson); -+ p->zJson = zNew; -+ p->zJson[p->nJson] = 0; -+ }else{ -+ sqlite3RCStrRef(p->zJson); -+ } -+ p->bJsonIsRCStr = 1; -+ rc = jsonCacheInsert(ctx, p); -+ if( rc==SQLITE_NOMEM ) goto json_pfa_oom; -+ if( flgs & JSON_EDITABLE ){ -+ pFromCache = p; -+ p = 0; -+ goto rebuild_from_cache; -+ } -+ } -+ return p; -+ -+json_pfa_malformed: -+ if( flgs & JSON_KEEPERROR ){ -+ p->nErr = 1; -+ return p; -+ }else{ -+ jsonParseFree(p); -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ return 0; -+ } -+ -+json_pfa_oom: -+ jsonParseFree(pFromCache); -+ jsonParseFree(p); -+ sqlite3_result_error_nomem(ctx); -+ return 0; - } - - /* --** Mark all NULL entries in the Object passed in as JNODE_REMOVE. -+** Make the return value of a JSON function either the raw JSONB blob -+** or make it JSON text, depending on whether the JSON_BLOB flag is -+** set on the function. - */ --static void jsonRemoveAllNulls(JsonNode *pNode){ -- int i, n; -- assert( pNode->eType==JSON_OBJECT ); -- n = pNode->n; -- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){ -- switch( pNode[i].eType ){ -- case JSON_NULL: -- pNode[i].jnFlags |= JNODE_REMOVE; -- break; -- case JSON_OBJECT: -- jsonRemoveAllNulls(&pNode[i]); -- break; -+static void jsonReturnParse( -+ sqlite3_context *ctx, -+ JsonParse *p -+){ -+ int flgs; -+ if( p->oom ){ -+ sqlite3_result_error_nomem(ctx); -+ return; -+ } -+ flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); -+ if( flgs & JSON_BLOB ){ -+ if( p->nBlobAlloc>0 && !p->bReadOnly ){ -+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC); -+ p->nBlobAlloc = 0; -+ }else{ -+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT); - } -+ }else{ -+ JsonString s; -+ jsonStringInit(&s, ctx); -+ p->delta = 0; -+ jsonTranslateBlobToText(p, 0, &s); -+ jsonReturnString(&s, p, ctx); -+ sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } - } - -- - /**************************************************************************** - ** SQL functions used for testing and debugging - ****************************************************************************/ - --#ifdef SQLITE_DEBUG -+#if SQLITE_DEBUG - /* --** The json_parse(JSON) function returns a string which describes --** a parse of the JSON provided. Or it returns NULL if JSON is not --** well-formed. --*/ --static void jsonParseFunc( -- sqlite3_context *ctx, -- int argc, -- sqlite3_value **argv --){ -- JsonString s; /* Output string - not real JSON */ -- JsonParse x; /* The parse */ -- u32 i; -- -- assert( argc==1 ); -- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; -- jsonParseFindParents(&x); -- jsonInit(&s, ctx); -- for(i=0; iaBlob[iStart] & 0x0f; -+ u32 savedNBlob = pParse->nBlob; -+ sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, ""); -+ if( pParse->nBlobAlloc>pParse->nBlob ){ -+ pParse->nBlob = pParse->nBlobAlloc; -+ } -+ nn = n = jsonbPayloadSize(pParse, iStart, &sz); -+ if( nn==0 ) nn = 1; -+ if( sz>0 && xaBlob[iStart+i]); -+ } -+ if( n==0 ){ -+ sqlite3_str_appendf(pOut, " ERROR invalid node size\n"); -+ iStart = n==0 ? iStart+1 : iEnd; -+ continue; -+ } -+ pParse->nBlob = savedNBlob; -+ if( iStart+n+sz>iEnd ){ -+ iEnd = iStart+n+sz; -+ if( iEnd>pParse->nBlob ){ -+ if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){ -+ iEnd = pParse->nBlobAlloc; -+ }else{ -+ iEnd = pParse->nBlob; -+ } -+ } -+ } -+ sqlite3_str_appendall(pOut," <-- "); -+ switch( x ){ -+ case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break; -+ case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break; -+ case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break; -+ case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break; -+ case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break; -+ case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break; -+ case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break; -+ case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break; -+ case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break; -+ case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break; -+ case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break; -+ case JSONB_ARRAY: { -+ sqlite3_str_appendf(pOut,"array, %u bytes\n", sz); -+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); -+ showContent = 0; -+ break; -+ } -+ case JSONB_OBJECT: { -+ sqlite3_str_appendf(pOut, "object, %u bytes\n", sz); -+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); -+ showContent = 0; -+ break; -+ } -+ default: { -+ sqlite3_str_appendall(pOut, "ERROR: unknown node type\n"); -+ showContent = 0; -+ break; -+ } - } -- jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d", -- i, zType, x.aNode[i].n, x.aUp[i]); -- if( x.aNode[i].u.zJContent!=0 ){ -- jsonAppendRaw(&s, " ", 1); -- jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n); -+ if( showContent ){ -+ if( sz==0 && x<=JSONB_FALSE ){ -+ sqlite3_str_append(pOut, "\n", 1); -+ }else{ -+ u32 j; -+ sqlite3_str_appendall(pOut, ": \""); -+ for(j=iStart+n; jaBlob[j]; -+ if( c<0x20 || c>=0x7f ) c = '.'; -+ sqlite3_str_append(pOut, (char*)&c, 1); -+ } -+ sqlite3_str_append(pOut, "\"\n", 2); -+ } - } -- jsonAppendRaw(&s, "\n", 1); -+ iStart += n + sz; -+ } -+} -+static void jsonShowParse(JsonParse *pParse){ -+ sqlite3_str out; -+ char zBuf[1000]; -+ if( pParse==0 ){ -+ printf("NULL pointer\n"); -+ return; -+ }else{ -+ printf("nBlobAlloc = %u\n", pParse->nBlobAlloc); -+ printf("nBlob = %u\n", pParse->nBlob); -+ printf("delta = %d\n", pParse->delta); -+ if( pParse->nBlob==0 ) return; -+ printf("content (bytes 0..%u):\n", pParse->nBlob-1); - } -- jsonParseReset(&x); -- jsonResult(&s); -+ sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000); -+ jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out); -+ printf("%s", sqlite3_str_value(&out)); -+ sqlite3_str_reset(&out); - } -+#endif /* SQLITE_DEBUG */ - -+#ifdef SQLITE_DEBUG - /* --** The json_test1(JSON) function return true (1) if the input is JSON --** text generated by another json function. It returns (0) if the input --** is not known to be JSON. -+** SQL function: json_parse(JSON) -+** -+** Parse JSON using jsonParseFuncArg(). Return text that is a -+** human-readable dump of the binary JSONB for the input parameter. - */ --static void jsonTest1Func( -+static void jsonParseFunc( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv - ){ -- UNUSED_PARAM(argc); -- sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); -+ JsonParse *p; /* The parse */ -+ sqlite3_str out; -+ -+ assert( argc>=1 ); -+ sqlite3StrAccumInit(&out, 0, 0, 0, 1000000); -+ p = jsonParseFuncArg(ctx, argv[0], 0); -+ if( p==0 ) return; -+ if( argc==1 ){ -+ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); -+ sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8); -+ }else{ -+ jsonShowParse(p); -+ } -+ jsonParseFree(p); -+ sqlite3_str_reset(&out); - } - #endif /* SQLITE_DEBUG */ - -@@ -186857,7 +211698,7 @@ static void jsonTest1Func( - ****************************************************************************/ - - /* --** Implementation of the json_QUOTE(VALUE) function. Return a JSON value -+** Implementation of the json_quote(VALUE) function. Return a JSON value - ** corresponding to the SQL value input. Mostly this means putting - ** double-quotes around strings and returning the unquoted string "null" - ** when given a NULL input. -@@ -186868,11 +211709,11 @@ static void jsonQuoteFunc( - sqlite3_value **argv - ){ - JsonString jx; -- UNUSED_PARAM(argc); -+ UNUSED_PARAMETER(argc); - -- jsonInit(&jx, ctx); -- jsonAppendValue(&jx, argv[0]); -- jsonResult(&jx); -+ jsonStringInit(&jx, ctx); -+ jsonAppendSqlValue(&jx, argv[0]); -+ jsonReturnString(&jx, 0, 0); - sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } - -@@ -186889,18 +211730,17 @@ static void jsonArrayFunc( - int i; - JsonString jx; - -- jsonInit(&jx, ctx); -+ jsonStringInit(&jx, ctx); - jsonAppendChar(&jx, '['); - for(i=0; inNode ); - if( argc==2 ){ - const char *zPath = (const char*)sqlite3_value_text(argv[1]); -- pNode = jsonLookup(p, zPath, 0, ctx); -+ if( zPath==0 ){ -+ jsonParseFree(p); -+ return; -+ } -+ i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); -+ if( JSON_LOOKUP_ISERROR(i) ){ -+ if( i==JSON_LOOKUP_NOTFOUND ){ -+ /* no-op */ -+ }else if( i==JSON_LOOKUP_PATHERROR ){ -+ jsonBadPathError(ctx, zPath); -+ }else{ -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ } -+ eErr = 1; -+ i = 0; -+ } - }else{ -- pNode = p->aNode; -- } -- if( pNode==0 ){ -- return; -+ i = 0; - } -- if( pNode->eType==JSON_ARRAY ){ -- assert( (pNode->jnFlags & JNODE_APPEND)==0 ); -- for(i=1; i<=pNode->n; n++){ -- i += jsonNodeSize(&pNode[i]); -- } -+ if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ -+ cnt = jsonbArrayCount(p, i); - } -- sqlite3_result_int64(ctx, n); -+ if( !eErr ) sqlite3_result_int64(ctx, cnt); -+ jsonParseFree(p); -+} -+ -+/* True if the string is all alphanumerics and underscores */ -+static int jsonAllAlphanum(const char *z, int n){ -+ int i; -+ for(i=0; i"(JSON,PATH) -+** "->>"(JSON,PATH) - ** --** Return the element described by PATH. Return NULL if there is no --** PATH element. If there are multiple PATHs, then return a JSON array --** with the result from each path. Throw an error if the JSON or any PATH --** is malformed. -+** Return the element described by PATH. Return NULL if that PATH element -+** is not found. -+** -+** If JSON_JSON is set or if more that one PATH argument is supplied then -+** always return a JSON representation of the result. If JSON_SQL is set, -+** then always return an SQL representation of the result. If neither flag -+** is present and argc==2, then return JSON for objects and arrays and SQL -+** for all other values. -+** -+** When multiple PATH arguments are supplied, the result is a JSON array -+** containing the result of each PATH. -+** -+** Abbreviated JSON path expressions are allows if JSON_ABPATH, for -+** compatibility with PG. - */ - static void jsonExtractFunc( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv - ){ -- JsonParse *p; /* The parse */ -- JsonNode *pNode; -- const char *zPath; -- JsonString jx; -- int i; -+ JsonParse *p = 0; /* The parse */ -+ int flags; /* Flags associated with the function */ -+ int i; /* Loop counter */ -+ JsonString jx; /* String for array result */ - - if( argc<2 ) return; -- p = jsonParseCached(ctx, argv, ctx); -+ p = jsonParseFuncArg(ctx, argv[0], 0); - if( p==0 ) return; -- jsonInit(&jx, ctx); -- jsonAppendChar(&jx, '['); -+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); -+ jsonStringInit(&jx, ctx); -+ if( argc>2 ){ -+ jsonAppendChar(&jx, '['); -+ } - for(i=1; inErr ) break; -- if( argc>2 ){ -- jsonAppendSeparator(&jx); -- if( pNode ){ -- jsonRenderNode(pNode, &jx, 0); -+ /* With a single PATH argument */ -+ const char *zPath = (const char*)sqlite3_value_text(argv[i]); -+ int nPath; -+ u32 j; -+ if( zPath==0 ) goto json_extract_error; -+ nPath = sqlite3Strlen30(zPath); -+ if( zPath[0]=='$' ){ -+ j = jsonLookupStep(p, 0, zPath+1, 0); -+ }else if( (flags & JSON_ABPATH) ){ -+ /* The -> and ->> operators accept abbreviated PATH arguments. This -+ ** is mostly for compatibility with PostgreSQL, but also for -+ ** convenience. -+ ** -+ ** NUMBER ==> $[NUMBER] // PG compatible -+ ** LABEL ==> $.LABEL // PG compatible -+ ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience -+ ** -+ ** Updated 2024-05-27: If the NUMBER is negative, then PG counts from -+ ** the right of the array. Hence for negative NUMBER: -+ ** -+ ** NUMBER ==> $[#NUMBER] // PG compatible -+ */ -+ jsonStringInit(&jx, ctx); -+ if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ -+ jsonAppendRawNZ(&jx, "[", 1); -+ if( zPath[0]=='-' ) jsonAppendRawNZ(&jx,"#",1); -+ jsonAppendRaw(&jx, zPath, nPath); -+ jsonAppendRawNZ(&jx, "]", 2); -+ }else if( jsonAllAlphanum(zPath, nPath) ){ -+ jsonAppendRawNZ(&jx, ".", 1); -+ jsonAppendRaw(&jx, zPath, nPath); -+ }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){ -+ jsonAppendRaw(&jx, zPath, nPath); -+ }else{ -+ jsonAppendRawNZ(&jx, ".\"", 2); -+ jsonAppendRaw(&jx, zPath, nPath); -+ jsonAppendRawNZ(&jx, "\"", 1); -+ } -+ jsonStringTerminate(&jx); -+ j = jsonLookupStep(p, 0, jx.zBuf, 0); -+ jsonStringReset(&jx); -+ }else{ -+ jsonBadPathError(ctx, zPath); -+ goto json_extract_error; -+ } -+ if( jnBlob ){ -+ if( argc==2 ){ -+ if( flags & JSON_JSON ){ -+ jsonStringInit(&jx, ctx); -+ jsonTranslateBlobToText(p, j, &jx); -+ jsonReturnString(&jx, 0, 0); -+ jsonStringReset(&jx); -+ assert( (flags & JSON_BLOB)==0 ); -+ sqlite3_result_subtype(ctx, JSON_SUBTYPE); -+ }else{ -+ jsonReturnFromBlob(p, j, ctx, 0); -+ if( (flags & (JSON_SQL|JSON_BLOB))==0 -+ && (p->aBlob[j]&0x0f)>=JSONB_ARRAY -+ ){ -+ sqlite3_result_subtype(ctx, JSON_SUBTYPE); -+ } -+ } -+ }else{ -+ jsonAppendSeparator(&jx); -+ jsonTranslateBlobToText(p, j, &jx); -+ } -+ }else if( j==JSON_LOOKUP_NOTFOUND ){ -+ if( argc==2 ){ -+ goto json_extract_error; /* Return NULL if not found */ - }else{ -- jsonAppendRaw(&jx, "null", 4); -+ jsonAppendSeparator(&jx); -+ jsonAppendRawNZ(&jx, "null", 4); - } -- }else if( pNode ){ -- jsonReturn(pNode, ctx, 0); -+ }else if( j==JSON_LOOKUP_ERROR ){ -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ goto json_extract_error; -+ }else{ -+ jsonBadPathError(ctx, zPath); -+ goto json_extract_error; - } - } -- if( argc>2 && i==argc ){ -+ if( argc>2 ){ - jsonAppendChar(&jx, ']'); -- jsonResult(&jx); -- sqlite3_result_subtype(ctx, JSON_SUBTYPE); -+ jsonReturnString(&jx, 0, 0); -+ if( (flags & JSON_BLOB)==0 ){ -+ sqlite3_result_subtype(ctx, JSON_SUBTYPE); -+ } - } -- jsonReset(&jx); -+json_extract_error: -+ jsonStringReset(&jx); -+ jsonParseFree(p); -+ return; - } - --/* This is the RFC 7396 MergePatch algorithm. --*/ --static JsonNode *jsonMergePatch( -- JsonParse *pParse, /* The JSON parser that contains the TARGET */ -- u32 iTarget, /* Node of the TARGET in pParse */ -- JsonNode *pPatch /* The PATCH */ --){ -- u32 i, j; -- u32 iRoot; -- JsonNode *pTarget; -- if( pPatch->eType!=JSON_OBJECT ){ -- return pPatch; -- } -- assert( iTarget>=0 && iTargetnNode ); -- pTarget = &pParse->aNode[iTarget]; -- assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); -- if( pTarget->eType!=JSON_OBJECT ){ -- jsonRemoveAllNulls(pPatch); -- return pPatch; -- } -- iRoot = iTarget; -- for(i=1; in; i += jsonNodeSize(&pPatch[i+1])+1){ -- u32 nKey; -- const char *zKey; -- assert( pPatch[i].eType==JSON_STRING ); -- assert( pPatch[i].jnFlags & JNODE_LABEL ); -- nKey = pPatch[i].n; -- zKey = pPatch[i].u.zJContent; -- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); -- for(j=1; jn; j += jsonNodeSize(&pTarget[j+1])+1 ){ -- assert( pTarget[j].eType==JSON_STRING ); -- assert( pTarget[j].jnFlags & JNODE_LABEL ); -- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); -- if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){ -- if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; -- if( pPatch[i+1].eType==JSON_NULL ){ -- pTarget[j+1].jnFlags |= JNODE_REMOVE; -- }else{ -- JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); -- if( pNew==0 ) return 0; -- pTarget = &pParse->aNode[iTarget]; -- if( pNew!=&pTarget[j+1] ){ -- pTarget[j+1].u.pPatch = pNew; -- pTarget[j+1].jnFlags |= JNODE_PATCH; -- } -- } -- break; -+/* -+** Return codes for jsonMergePatch() -+*/ -+#define JSON_MERGE_OK 0 /* Success */ -+#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */ -+#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */ -+#define JSON_MERGE_OOM 3 /* Out-of-memory condition */ -+ -+/* -+** RFC-7396 MergePatch for two JSONB blobs. -+** -+** pTarget is the target. pPatch is the patch. The target is updated -+** in place. The patch is read-only. -+** -+** The original RFC-7396 algorithm is this: -+** -+** define MergePatch(Target, Patch): -+** if Patch is an Object: -+** if Target is not an Object: -+** Target = {} # Ignore the contents and set it to an empty Object -+** for each Name/Value pair in Patch: -+** if Value is null: -+** if Name exists in Target: -+** remove the Name/Value pair from Target -+** else: -+** Target[Name] = MergePatch(Target[Name], Value) -+** return Target -+** else: -+** return Patch -+** -+** Here is an equivalent algorithm restructured to show the actual -+** implementation: -+** -+** 01 define MergePatch(Target, Patch): -+** 02 if Patch is not an Object: -+** 03 return Patch -+** 04 else: // if Patch is an Object -+** 05 if Target is not an Object: -+** 06 Target = {} -+** 07 for each Name/Value pair in Patch: -+** 08 if Name exists in Target: -+** 09 if Value is null: -+** 10 remove the Name/Value pair from Target -+** 11 else -+** 12 Target[name] = MergePatch(Target[Name], Value) -+** 13 else if Value is not NULL: -+** 14 if Value is not an Object: -+** 15 Target[name] = Value -+** 16 else: -+** 17 Target[name] = MergePatch('{}',value) -+** 18 return Target -+** | -+** ^---- Line numbers referenced in comments in the implementation -+*/ -+static int jsonMergePatch( -+ JsonParse *pTarget, /* The JSON parser that contains the TARGET */ -+ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ -+ const JsonParse *pPatch, /* The PATCH */ -+ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */ -+){ -+ u8 x; /* Type of a single node */ -+ u32 n, sz=0; /* Return values from jsonbPayloadSize() */ -+ u32 iTCursor; /* Cursor position while scanning the target object */ -+ u32 iTStart; /* First label in the target object */ -+ u32 iTEndBE; /* Original first byte past end of target, before edit */ -+ u32 iTEnd; /* Current first byte past end of target */ -+ u8 eTLabel; /* Node type of the target label */ -+ u32 iTLabel = 0; /* Index of the label */ -+ u32 nTLabel = 0; /* Header size in bytes for the target label */ -+ u32 szTLabel = 0; /* Size of the target label payload */ -+ u32 iTValue = 0; /* Index of the target value */ -+ u32 nTValue = 0; /* Header size of the target value */ -+ u32 szTValue = 0; /* Payload size for the target value */ -+ -+ u32 iPCursor; /* Cursor position while scanning the patch */ -+ u32 iPEnd; /* First byte past the end of the patch */ -+ u8 ePLabel; /* Node type of the patch label */ -+ u32 iPLabel; /* Start of patch label */ -+ u32 nPLabel; /* Size of header on the patch label */ -+ u32 szPLabel; /* Payload size of the patch label */ -+ u32 iPValue; /* Start of patch value */ -+ u32 nPValue; /* Header size for the patch value */ -+ u32 szPValue; /* Payload size of the patch value */ -+ -+ assert( iTarget>=0 && iTargetnBlob ); -+ assert( iPatch>=0 && iPatchnBlob ); -+ x = pPatch->aBlob[iPatch] & 0x0f; -+ if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */ -+ u32 szPatch; /* Total size of the patch, header+payload */ -+ u32 szTarget; /* Total size of the target, header+payload */ -+ n = jsonbPayloadSize(pPatch, iPatch, &sz); -+ szPatch = n+sz; -+ sz = 0; -+ n = jsonbPayloadSize(pTarget, iTarget, &sz); -+ szTarget = n+sz; -+ jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch); -+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */ -+ } -+ x = pTarget->aBlob[iTarget] & 0x0f; -+ if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */ -+ n = jsonbPayloadSize(pTarget, iTarget, &sz); -+ jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0); -+ x = pTarget->aBlob[iTarget]; -+ pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT; -+ } -+ n = jsonbPayloadSize(pPatch, iPatch, &sz); -+ if( NEVER(n==0) ) return JSON_MERGE_BADPATCH; -+ iPCursor = iPatch+n; -+ iPEnd = iPCursor+sz; -+ n = jsonbPayloadSize(pTarget, iTarget, &sz); -+ if( NEVER(n==0) ) return JSON_MERGE_BADTARGET; -+ iTStart = iTarget+n; -+ iTEndBE = iTStart+sz; -+ -+ while( iPCursoraBlob[iPCursor] & 0x0f; -+ if( ePLabelJSONB_TEXTRAW ){ -+ return JSON_MERGE_BADPATCH; -+ } -+ nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel); -+ if( nPLabel==0 ) return JSON_MERGE_BADPATCH; -+ iPValue = iPCursor + nPLabel + szPLabel; -+ if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH; -+ nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue); -+ if( nPValue==0 ) return JSON_MERGE_BADPATCH; -+ iPCursor = iPValue + nPValue + szPValue; -+ if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH; -+ -+ iTCursor = iTStart; -+ iTEnd = iTEndBE + pTarget->delta; -+ while( iTCursoraBlob[iTCursor] & 0x0f; -+ if( eTLabelJSONB_TEXTRAW ){ -+ return JSON_MERGE_BADTARGET; -+ } -+ nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel); -+ if( nTLabel==0 ) return JSON_MERGE_BADTARGET; -+ iTValue = iTLabel + nTLabel + szTLabel; -+ if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET; -+ nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue); -+ if( nTValue==0 ) return JSON_MERGE_BADTARGET; -+ if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET; -+ isEqual = jsonLabelCompare( -+ (const char*)&pPatch->aBlob[iPLabel+nPLabel], -+ szPLabel, -+ (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW), -+ (const char*)&pTarget->aBlob[iTLabel+nTLabel], -+ szTLabel, -+ (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW)); -+ if( isEqual ) break; -+ iTCursor = iTValue + nTValue + szTValue; -+ } -+ x = pPatch->aBlob[iPValue] & 0x0f; -+ if( iTCursoroom) ) return JSON_MERGE_OOM; -+ }else{ -+ /* Algorithm line 12 */ -+ int rc, savedDelta = pTarget->delta; -+ pTarget->delta = 0; -+ rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue); -+ if( rc ) return rc; -+ pTarget->delta += savedDelta; -+ } -+ }else if( x>0 ){ /* Algorithm line 13 */ -+ /* No match and patch value is not NULL */ -+ u32 szNew = szPLabel+nPLabel; -+ if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */ -+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew); -+ if( pTarget->oom ) return JSON_MERGE_OOM; -+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); -+ memcpy(&pTarget->aBlob[iTEnd+szNew], -+ &pPatch->aBlob[iPValue], szPValue+nPValue); -+ }else{ -+ int rc, savedDelta; -+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1); -+ if( pTarget->oom ) return JSON_MERGE_OOM; -+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); -+ pTarget->aBlob[iTEnd+szNew] = 0x00; -+ savedDelta = pTarget->delta; -+ pTarget->delta = 0; -+ rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue); -+ if( rc ) return rc; -+ pTarget->delta += savedDelta; - } - } -- if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){ -- int iStart, iPatch; -- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); -- jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); -- iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0); -- if( pParse->oom ) return 0; -- jsonRemoveAllNulls(pPatch); -- pTarget = &pParse->aNode[iTarget]; -- pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; -- pParse->aNode[iRoot].u.iAppend = iStart - iRoot; -- iRoot = iStart; -- pParse->aNode[iPatch].jnFlags |= JNODE_PATCH; -- pParse->aNode[iPatch].u.pPatch = &pPatch[i+1]; -- } - } -- return pTarget; -+ if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget); -+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; - } - -+ - /* - ** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON - ** object that is the result of running the RFC 7396 MergePatch() algorithm -@@ -187063,25 +212137,27 @@ static void jsonPatchFunc( - int argc, - sqlite3_value **argv - ){ -- JsonParse x; /* The JSON that is being patched */ -- JsonParse y; /* The patch */ -- JsonNode *pResult; /* The result of the merge */ -+ JsonParse *pTarget; /* The TARGET */ -+ JsonParse *pPatch; /* The PATCH */ -+ int rc; /* Result code */ - -- UNUSED_PARAM(argc); -- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; -- if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){ -- jsonParseReset(&x); -- return; -- } -- pResult = jsonMergePatch(&x, 0, y.aNode); -- assert( pResult!=0 || x.oom ); -- if( pResult ){ -- jsonReturnJson(pResult, ctx, 0); -- }else{ -- sqlite3_result_error_nomem(ctx); -+ UNUSED_PARAMETER(argc); -+ assert( argc==2 ); -+ pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); -+ if( pTarget==0 ) return; -+ pPatch = jsonParseFuncArg(ctx, argv[1], 0); -+ if( pPatch ){ -+ rc = jsonMergePatch(pTarget, 0, pPatch, 0); -+ if( rc==JSON_MERGE_OK ){ -+ jsonReturnParse(ctx, pTarget); -+ }else if( rc==JSON_MERGE_OOM ){ -+ sqlite3_result_error_nomem(ctx); -+ }else{ -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ } -+ jsonParseFree(pPatch); - } -- jsonParseReset(&x); -- jsonParseReset(&y); -+ jsonParseFree(pTarget); - } - - -@@ -187105,23 +212181,23 @@ static void jsonObjectFunc( - "of arguments", -1); - return; - } -- jsonInit(&jx, ctx); -+ jsonStringInit(&jx, ctx); - jsonAppendChar(&jx, '{'); - for(i=0; i1 ? JSON_EDITABLE : 0); -+ if( p==0 ) return; -+ for(i=1; ijnFlags |= JNODE_REMOVE; -- } -- if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){ -- jsonReturnJson(x.aNode, ctx, 0); -+ if( zPath==0 ){ -+ goto json_remove_done; -+ } -+ if( zPath[0]!='$' ){ -+ goto json_remove_patherror; -+ } -+ if( zPath[1]==0 ){ -+ /* json_remove(j,'$') returns NULL */ -+ goto json_remove_done; -+ } -+ p->eEdit = JEDIT_DEL; -+ p->delta = 0; -+ rc = jsonLookupStep(p, 0, zPath+1, 0); -+ if( JSON_LOOKUP_ISERROR(rc) ){ -+ if( rc==JSON_LOOKUP_NOTFOUND ){ -+ continue; /* No-op */ -+ }else if( rc==JSON_LOOKUP_PATHERROR ){ -+ jsonBadPathError(ctx, zPath); -+ }else{ -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ } -+ goto json_remove_done; -+ } - } --remove_done: -- jsonParseReset(&x); -+ jsonReturnParse(ctx, p); -+ jsonParseFree(p); -+ return; -+ -+json_remove_patherror: -+ jsonBadPathError(ctx, zPath); -+ -+json_remove_done: -+ jsonParseFree(p); -+ return; - } - - /* -@@ -187170,36 +212270,15 @@ static void jsonReplaceFunc( - int argc, - sqlite3_value **argv - ){ -- JsonParse x; /* The parse */ -- JsonNode *pNode; -- const char *zPath; -- u32 i; -- - if( argc<1 ) return; - if( (argc&1)==0 ) { - jsonWrongNumArgs(ctx, "replace"); - return; - } -- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; -- assert( x.nNode ); -- for(i=1; i<(u32)argc; i+=2){ -- zPath = (const char*)sqlite3_value_text(argv[i]); -- pNode = jsonLookup(&x, zPath, 0, ctx); -- if( x.nErr ) goto replace_err; -- if( pNode ){ -- pNode->jnFlags |= (u8)JNODE_REPLACE; -- pNode->u.iReplace = i + 1; -- } -- } -- if( x.aNode[0].jnFlags & JNODE_REPLACE ){ -- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); -- }else{ -- jsonReturnJson(x.aNode, ctx, argv); -- } --replace_err: -- jsonParseReset(&x); -+ jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL); - } - -+ - /* - ** json_set(JSON, PATH, VALUE, ...) - ** -@@ -187217,49 +212296,24 @@ static void jsonSetFunc( - int argc, - sqlite3_value **argv - ){ -- JsonParse x; /* The parse */ -- JsonNode *pNode; -- const char *zPath; -- u32 i; -- int bApnd; -- int bIsSet = *(int*)sqlite3_user_data(ctx); -+ -+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); -+ int bIsSet = (flags&JSON_ISSET)!=0; - - if( argc<1 ) return; - if( (argc&1)==0 ) { - jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); - return; - } -- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return; -- assert( x.nNode ); -- for(i=1; i<(u32)argc; i+=2){ -- zPath = (const char*)sqlite3_value_text(argv[i]); -- bApnd = 0; -- pNode = jsonLookup(&x, zPath, &bApnd, ctx); -- if( x.oom ){ -- sqlite3_result_error_nomem(ctx); -- goto jsonSetDone; -- }else if( x.nErr ){ -- goto jsonSetDone; -- }else if( pNode && (bApnd || bIsSet) ){ -- pNode->jnFlags |= (u8)JNODE_REPLACE; -- pNode->u.iReplace = i + 1; -- } -- } -- if( x.aNode[0].jnFlags & JNODE_REPLACE ){ -- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]); -- }else{ -- jsonReturnJson(x.aNode, ctx, argv); -- } --jsonSetDone: -- jsonParseReset(&x); -+ jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); - } - - /* - ** json_type(JSON) - ** json_type(JSON, PATH) - ** --** Return the top-level "type" of a JSON string. Throw an error if --** either the JSON or PATH inputs are not well-formed. -+** Return the top-level "type" of a JSON string. json_type() raises an -+** error if either the JSON or PATH inputs are not well-formed. - */ - static void jsonTypeFunc( - sqlite3_context *ctx, -@@ -187267,27 +212321,127 @@ static void jsonTypeFunc( - sqlite3_value **argv - ){ - JsonParse *p; /* The parse */ -- const char *zPath; -- JsonNode *pNode; -+ const char *zPath = 0; -+ u32 i; - -- p = jsonParseCached(ctx, argv, ctx); -+ p = jsonParseFuncArg(ctx, argv[0], 0); - if( p==0 ) return; - if( argc==2 ){ - zPath = (const char*)sqlite3_value_text(argv[1]); -- pNode = jsonLookup(p, zPath, 0, ctx); -+ if( zPath==0 ) goto json_type_done; -+ if( zPath[0]!='$' ){ -+ jsonBadPathError(ctx, zPath); -+ goto json_type_done; -+ } -+ i = jsonLookupStep(p, 0, zPath+1, 0); -+ if( JSON_LOOKUP_ISERROR(i) ){ -+ if( i==JSON_LOOKUP_NOTFOUND ){ -+ /* no-op */ -+ }else if( i==JSON_LOOKUP_PATHERROR ){ -+ jsonBadPathError(ctx, zPath); -+ }else{ -+ sqlite3_result_error(ctx, "malformed JSON", -1); -+ } -+ goto json_type_done; -+ } - }else{ -- pNode = p->aNode; -+ i = 0; - } -- if( pNode ){ -- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC); -+ sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC); -+json_type_done: -+ jsonParseFree(p); -+} -+ -+/* -+** json_pretty(JSON) -+** json_pretty(JSON, INDENT) -+** -+** Return text that is a pretty-printed rendering of the input JSON. -+** If the argument is not valid JSON, return NULL. -+** -+** The INDENT argument is text that is used for indentation. If omitted, -+** it defaults to four spaces (the same as PostgreSQL). -+*/ -+static void jsonPrettyFunc( -+ sqlite3_context *ctx, -+ int argc, -+ sqlite3_value **argv -+){ -+ JsonString s; /* The output string */ -+ JsonPretty x; /* Pretty printing context */ -+ -+ memset(&x, 0, sizeof(x)); -+ x.pParse = jsonParseFuncArg(ctx, argv[0], 0); -+ if( x.pParse==0 ) return; -+ x.pOut = &s; -+ jsonStringInit(&s, ctx); -+ if( argc==1 || (x.zIndent = (const char*)sqlite3_value_text(argv[1]))==0 ){ -+ x.zIndent = " "; -+ x.szIndent = 4; -+ }else{ -+ x.szIndent = (u32)strlen(x.zIndent); - } -+ jsonTranslateBlobToPrettyText(&x, 0); -+ jsonReturnString(&s, 0, 0); -+ jsonParseFree(x.pParse); - } - - /* - ** json_valid(JSON) --** --** Return 1 if JSON is a well-formed JSON string according to RFC-7159. --** Return 0 otherwise. -+** json_valid(JSON, FLAGS) -+** -+** Check the JSON argument to see if it is well-formed. The FLAGS argument -+** encodes the various constraints on what is meant by "well-formed": -+** -+** 0x01 Canonical RFC-8259 JSON text -+** 0x02 JSON text with optional JSON-5 extensions -+** 0x04 Superficially appears to be JSONB -+** 0x08 Strictly well-formed JSONB -+** -+** If the FLAGS argument is omitted, it defaults to 1. Useful values for -+** FLAGS include: -+** -+** 1 Strict canonical JSON text -+** 2 JSON text perhaps with JSON-5 extensions -+** 4 Superficially appears to be JSONB -+** 5 Canonical JSON text or superficial JSONB -+** 6 JSON-5 text or superficial JSONB -+** 8 Strict JSONB -+** 9 Canonical JSON text or strict JSONB -+** 10 JSON-5 text or strict JSONB -+** -+** Other flag combinations are redundant. For example, every canonical -+** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3 -+** are the same. Similarly, any input that passes a strict JSONB validation -+** will also pass the superficial validation so 12 through 15 are the same -+** as 8 through 11 respectively. -+** -+** This routine runs in linear time to validate text and when doing strict -+** JSONB validation. Superficial JSONB validation is constant time, -+** assuming the BLOB is already in memory. The performance advantage -+** of superficial JSONB validation is why that option is provided. -+** Application developers can choose to do fast superficial validation or -+** slower strict validation, according to their specific needs. -+** -+** Only the lower four bits of the FLAGS argument are currently used. -+** Higher bits are reserved for future expansion. To facilitate -+** compatibility, the current implementation raises an error if any bit -+** in FLAGS is set other than the lower four bits. -+** -+** The original circa 2015 implementation of the JSON routines in -+** SQLite only supported canonical RFC-8259 JSON text and the json_valid() -+** function only accepted one argument. That is why the default value -+** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only -+** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS -+** argument was added when the JSON routines were extended to support -+** JSON5-like extensions and binary JSONB stored in BLOBs. -+** -+** Return Values: -+** -+** * Raise an error if FLAGS is outside the range of 1 to 15. -+** * Return NULL if the input is NULL -+** * Return 1 if the input is well-formed. -+** * Return 0 if the input is not well-formed. - */ - static void jsonValidFunc( - sqlite3_context *ctx, -@@ -187295,11 +212449,121 @@ static void jsonValidFunc( - sqlite3_value **argv - ){ - JsonParse *p; /* The parse */ -- UNUSED_PARAM(argc); -- p = jsonParseCached(ctx, argv, 0); -- sqlite3_result_int(ctx, p!=0); -+ u8 flags = 1; -+ u8 res = 0; -+ if( argc==2 ){ -+ i64 f = sqlite3_value_int64(argv[1]); -+ if( f<1 || f>15 ){ -+ sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be" -+ " between 1 and 15", -1); -+ return; -+ } -+ flags = f & 0x0f; -+ } -+ switch( sqlite3_value_type(argv[0]) ){ -+ case SQLITE_NULL: { -+#ifdef SQLITE_LEGACY_JSON_VALID -+ /* Incorrect legacy behavior was to return FALSE for a NULL input */ -+ sqlite3_result_int(ctx, 0); -+#endif -+ return; -+ } -+ case SQLITE_BLOB: { -+ JsonParse py; -+ memset(&py, 0, sizeof(py)); -+ if( jsonArgIsJsonb(argv[0], &py) ){ -+ if( flags & 0x04 ){ -+ /* Superficial checking only - accomplished by the -+ ** jsonArgIsJsonb() call above. */ -+ res = 1; -+ }else if( flags & 0x08 ){ -+ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If -+ ** no errors occur, call that a "strict check". */ -+ res = 0==jsonbValidityCheck(&py, 0, py.nBlob, 1); -+ } -+ break; -+ } -+ /* Fall through into interpreting the input as text. See note -+ ** above at tag-20240123-a. */ -+ /* no break */ deliberate_fall_through -+ } -+ default: { -+ JsonParse px; -+ if( (flags & 0x3)==0 ) break; -+ memset(&px, 0, sizeof(px)); -+ -+ p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR); -+ if( p ){ -+ if( p->oom ){ -+ sqlite3_result_error_nomem(ctx); -+ }else if( p->nErr ){ -+ /* no-op */ -+ }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){ -+ res = 1; -+ } -+ jsonParseFree(p); -+ }else{ -+ sqlite3_result_error_nomem(ctx); -+ } -+ break; -+ } -+ } -+ sqlite3_result_int(ctx, res); - } - -+/* -+** json_error_position(JSON) -+** -+** If the argument is NULL, return NULL -+** -+** If the argument is BLOB, do a full validity check and return non-zero -+** if the check fails. The return value is the approximate 1-based offset -+** to the byte of the element that contains the first error. -+** -+** Otherwise interpret the argument is TEXT (even if it is numeric) and -+** return the 1-based character position for where the parser first recognized -+** that the input was not valid JSON, or return 0 if the input text looks -+** ok. JSON-5 extensions are accepted. -+*/ -+static void jsonErrorFunc( -+ sqlite3_context *ctx, -+ int argc, -+ sqlite3_value **argv -+){ -+ i64 iErrPos = 0; /* Error position to be returned */ -+ JsonParse s; -+ -+ assert( argc==1 ); -+ UNUSED_PARAMETER(argc); -+ memset(&s, 0, sizeof(s)); -+ s.db = sqlite3_context_db_handle(ctx); -+ if( jsonArgIsJsonb(argv[0], &s) ){ -+ iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1); -+ }else{ -+ s.zJson = (char*)sqlite3_value_text(argv[0]); -+ if( s.zJson==0 ) return; /* NULL input or OOM */ -+ s.nJson = sqlite3_value_bytes(argv[0]); -+ if( jsonConvertTextToBlob(&s,0) ){ -+ if( s.oom ){ -+ iErrPos = -1; -+ }else{ -+ /* Convert byte-offset s.iErr into a character offset */ -+ u32 k; -+ assert( s.zJson!=0 ); /* Because s.oom is false */ -+ for(k=0; kzBuf==0 ){ -- jsonInit(pStr, ctx); -+ jsonStringInit(pStr, ctx); - jsonAppendChar(pStr, '['); - }else if( pStr->nUsed>1 ){ - jsonAppendChar(pStr, ','); -- pStr->pCtx = ctx; - } -- jsonAppendValue(pStr, argv[0]); -+ pStr->pCtx = ctx; -+ jsonAppendSqlValue(pStr, argv[0]); - } - } - static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ - JsonString *pStr; - pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); - if( pStr ){ -+ int flags; - pStr->pCtx = ctx; - jsonAppendChar(pStr, ']'); -- if( pStr->bErr ){ -- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); -- assert( pStr->bStatic ); -+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); -+ if( pStr->eErr ){ -+ jsonReturnString(pStr, 0, 0); -+ return; -+ }else if( flags & JSON_BLOB ){ -+ jsonReturnStringAsBlob(pStr); -+ if( isFinal ){ -+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); -+ }else{ -+ jsonStringTrimOneChar(pStr); -+ } -+ return; - }else if( isFinal ){ - sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, -- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); -+ pStr->bStatic ? SQLITE_TRANSIENT : -+ sqlite3RCStrUnref); - pStr->bStatic = 1; - }else{ - sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); -- pStr->nUsed--; -+ jsonStringTrimOneChar(pStr); - } - }else{ - sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); -@@ -187375,20 +212650,16 @@ static void jsonGroupInverse( - char *z; - char c; - JsonString *pStr; -- UNUSED_PARAM(argc); -- UNUSED_PARAM(argv); -+ UNUSED_PARAMETER(argc); -+ UNUSED_PARAMETER(argv); - pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); - #ifdef NEVER - /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will -- ** always have been called to initalize it */ -+ ** always have been called to initialize it */ - if( NEVER(!pStr) ) return; - #endif - z = pStr->zBuf; -- for(i=1; (c = z[i])!=',' || inStr || nNest; i++){ -- if( i>=pStr->nUsed ){ -- pStr->nUsed = 1; -- return; -- } -+ for(i=1; inUsed && ((c = z[i])!=',' || inStr || nNest); i++){ - if( c=='"' ){ - inStr = !inStr; - }else if( c=='\\' ){ -@@ -187398,8 +212669,13 @@ static void jsonGroupInverse( - if( c=='}' || c==']' ) nNest--; - } - } -- pStr->nUsed -= i; -- memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); -+ if( inUsed ){ -+ pStr->nUsed -= i; -+ memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1); -+ z[pStr->nUsed] = 0; -+ }else{ -+ pStr->nUsed = 1; -+ } - } - #else - # define jsonGroupInverse 0 -@@ -187419,38 +212695,52 @@ static void jsonObjectStep( - JsonString *pStr; - const char *z; - u32 n; -- UNUSED_PARAM(argc); -+ UNUSED_PARAMETER(argc); - pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); - if( pStr ){ -+ z = (const char*)sqlite3_value_text(argv[0]); -+ n = sqlite3Strlen30(z); - if( pStr->zBuf==0 ){ -- jsonInit(pStr, ctx); -+ jsonStringInit(pStr, ctx); - jsonAppendChar(pStr, '{'); -- }else if( pStr->nUsed>1 ){ -+ }else if( pStr->nUsed>1 && z!=0 ){ - jsonAppendChar(pStr, ','); -- pStr->pCtx = ctx; - } -- z = (const char*)sqlite3_value_text(argv[0]); -- n = (u32)sqlite3_value_bytes(argv[0]); -- jsonAppendString(pStr, z, n); -- jsonAppendChar(pStr, ':'); -- jsonAppendValue(pStr, argv[1]); -+ pStr->pCtx = ctx; -+ if( z!=0 ){ -+ jsonAppendString(pStr, z, n); -+ jsonAppendChar(pStr, ':'); -+ jsonAppendSqlValue(pStr, argv[1]); -+ } - } - } - static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ - JsonString *pStr; - pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); - if( pStr ){ -+ int flags; - jsonAppendChar(pStr, '}'); -- if( pStr->bErr ){ -- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); -- assert( pStr->bStatic ); -+ pStr->pCtx = ctx; -+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); -+ if( pStr->eErr ){ -+ jsonReturnString(pStr, 0, 0); -+ return; -+ }else if( flags & JSON_BLOB ){ -+ jsonReturnStringAsBlob(pStr); -+ if( isFinal ){ -+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); -+ }else{ -+ jsonStringTrimOneChar(pStr); -+ } -+ return; - }else if( isFinal ){ - sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, -- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free); -+ pStr->bStatic ? SQLITE_TRANSIENT : -+ sqlite3RCStrUnref); - pStr->bStatic = 1; - }else{ - sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); -- pStr->nUsed--; -+ jsonStringTrimOneChar(pStr); - } - }else{ - sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); -@@ -187470,19 +212760,37 @@ static void jsonObjectFinal(sqlite3_context *ctx){ - /**************************************************************************** - ** The json_each virtual table - ****************************************************************************/ -+typedef struct JsonParent JsonParent; -+struct JsonParent { -+ u32 iHead; /* Start of object or array */ -+ u32 iValue; /* Start of the value */ -+ u32 iEnd; /* First byte past the end */ -+ u32 nPath; /* Length of path */ -+ i64 iKey; /* Key for JSONB_ARRAY */ -+}; -+ - typedef struct JsonEachCursor JsonEachCursor; - struct JsonEachCursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - u32 iRowid; /* The rowid */ -- u32 iBegin; /* The first node of the scan */ -- u32 i; /* Index in sParse.aNode[] of current row */ -+ u32 i; /* Index in sParse.aBlob[] of current row */ - u32 iEnd; /* EOF when i equals or exceeds this value */ -- u8 eType; /* Type of top-level element */ -+ u32 nRoot; /* Size of the root path in bytes */ -+ u8 eType; /* Type of the container for element i */ - u8 bRecursive; /* True for json_tree(). False for json_each() */ -- char *zJson; /* Input JSON */ -- char *zRoot; /* Path by which to filter zJson */ -+ u32 nParent; /* Current nesting depth */ -+ u32 nParentAlloc; /* Space allocated for aParent[] */ -+ JsonParent *aParent; /* Parent elements of i */ -+ sqlite3 *db; /* Database connection */ -+ JsonString path; /* Current path */ - JsonParse sParse; /* Parse of the input JSON */ - }; -+typedef struct JsonEachConnection JsonEachConnection; -+struct JsonEachConnection { -+ sqlite3_vtab base; /* Base class - must be first */ -+ sqlite3 *db; /* Database connection */ -+}; -+ - - /* Constructor for the json_each virtual table */ - static int jsonEachConnect( -@@ -187492,7 +212800,7 @@ static int jsonEachConnect( - sqlite3_vtab **ppVtab, - char **pzErr - ){ -- sqlite3_vtab *pNew; -+ JsonEachConnection *pNew; - int rc; - - /* Column numbers */ -@@ -187510,36 +212818,40 @@ static int jsonEachConnect( - #define JEACH_JSON 8 - #define JEACH_ROOT 9 - -- UNUSED_PARAM(pzErr); -- UNUSED_PARAM(argv); -- UNUSED_PARAM(argc); -- UNUSED_PARAM(pAux); -+ UNUSED_PARAMETER(pzErr); -+ UNUSED_PARAMETER(argv); -+ UNUSED_PARAMETER(argc); -+ UNUSED_PARAMETER(pAux); - rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," - "json HIDDEN,root HIDDEN)"); - if( rc==SQLITE_OK ){ -- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); -+ pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew)); -+ *ppVtab = (sqlite3_vtab*)pNew; - if( pNew==0 ) return SQLITE_NOMEM; -- memset(pNew, 0, sizeof(*pNew)); - sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); -+ pNew->db = db; - } - return rc; - } - - /* destructor for json_each virtual table */ - static int jsonEachDisconnect(sqlite3_vtab *pVtab){ -- sqlite3_free(pVtab); -+ JsonEachConnection *p = (JsonEachConnection*)pVtab; -+ sqlite3DbFree(p->db, pVtab); - return SQLITE_OK; - } - - /* constructor for a JsonEachCursor object for json_each(). */ - static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ -+ JsonEachConnection *pVtab = (JsonEachConnection*)p; - JsonEachCursor *pCur; - -- UNUSED_PARAM(p); -- pCur = sqlite3_malloc( sizeof(*pCur) ); -+ UNUSED_PARAMETER(p); -+ pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur)); - if( pCur==0 ) return SQLITE_NOMEM; -- memset(pCur, 0, sizeof(*pCur)); -+ pCur->db = pVtab->db; -+ jsonStringZero(&pCur->path); - *ppCursor = &pCur->base; - return SQLITE_OK; - } -@@ -187557,22 +212869,24 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ - /* Reset a JsonEachCursor back to its original state. Free any memory - ** held. */ - static void jsonEachCursorReset(JsonEachCursor *p){ -- sqlite3_free(p->zJson); -- sqlite3_free(p->zRoot); - jsonParseReset(&p->sParse); -+ jsonStringReset(&p->path); -+ sqlite3DbFree(p->db, p->aParent); - p->iRowid = 0; - p->i = 0; -+ p->aParent = 0; -+ p->nParent = 0; -+ p->nParentAlloc = 0; - p->iEnd = 0; - p->eType = 0; -- p->zJson = 0; -- p->zRoot = 0; - } - - /* Destructor for a jsonEachCursor object */ - static int jsonEachClose(sqlite3_vtab_cursor *cur){ - JsonEachCursor *p = (JsonEachCursor*)cur; - jsonEachCursorReset(p); -- sqlite3_free(cur); -+ -+ sqlite3DbFree(p->db, cur); - return SQLITE_OK; - } - -@@ -187583,167 +212897,233 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){ - return p->i >= p->iEnd; - } - -+/* -+** If the cursor is currently pointing at the label of a object entry, -+** then return the index of the value. For all other cases, return the -+** current pointer position, which is the value. -+*/ -+static int jsonSkipLabel(JsonEachCursor *p){ -+ if( p->eType==JSONB_OBJECT ){ -+ u32 sz = 0; -+ u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz); -+ return p->i + n + sz; -+ }else{ -+ return p->i; -+ } -+} -+ -+/* -+** Append the path name for the current element. -+*/ -+static void jsonAppendPathName(JsonEachCursor *p){ -+ assert( p->nParent>0 ); -+ assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT ); -+ if( p->eType==JSONB_ARRAY ){ -+ jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey); -+ }else{ -+ u32 n, sz = 0, k, i; -+ const char *z; -+ int needQuote = 0; -+ n = jsonbPayloadSize(&p->sParse, p->i, &sz); -+ k = p->i + n; -+ z = (const char*)&p->sParse.aBlob[k]; -+ if( sz==0 || !sqlite3Isalpha(z[0]) ){ -+ needQuote = 1; -+ }else{ -+ for(i=0; ipath,".\"%.*s\"", sz, z); -+ }else{ -+ jsonPrintf(sz+2,&p->path,".%.*s", sz, z); -+ } -+ } -+} -+ - /* Advance the cursor to the next element for json_tree() */ - static int jsonEachNext(sqlite3_vtab_cursor *cur){ - JsonEachCursor *p = (JsonEachCursor*)cur; -+ int rc = SQLITE_OK; - if( p->bRecursive ){ -- if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++; -- p->i++; -- p->iRowid++; -- if( p->iiEnd ){ -- u32 iUp = p->sParse.aUp[p->i]; -- JsonNode *pUp = &p->sParse.aNode[iUp]; -- p->eType = pUp->eType; -- if( pUp->eType==JSON_ARRAY ){ -- if( iUp==p->i-1 ){ -- pUp->u.iKey = 0; -- }else{ -- pUp->u.iKey++; -- } -+ u8 x; -+ u8 levelChange = 0; -+ u32 n, sz = 0; -+ u32 i = jsonSkipLabel(p); -+ x = p->sParse.aBlob[i] & 0x0f; -+ n = jsonbPayloadSize(&p->sParse, i, &sz); -+ if( x==JSONB_OBJECT || x==JSONB_ARRAY ){ -+ JsonParent *pParent; -+ if( p->nParent>=p->nParentAlloc ){ -+ JsonParent *pNew; -+ u64 nNew; -+ nNew = p->nParentAlloc*2 + 3; -+ pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew); -+ if( pNew==0 ) return SQLITE_NOMEM; -+ p->nParentAlloc = (u32)nNew; -+ p->aParent = pNew; -+ } -+ levelChange = 1; -+ pParent = &p->aParent[p->nParent]; -+ pParent->iHead = p->i; -+ pParent->iValue = i; -+ pParent->iEnd = i + n + sz; -+ pParent->iKey = -1; -+ pParent->nPath = (u32)p->path.nUsed; -+ if( p->eType && p->nParent ){ -+ jsonAppendPathName(p); -+ if( p->path.eErr ) rc = SQLITE_NOMEM; -+ } -+ p->nParent++; -+ p->i = i + n; -+ }else{ -+ p->i = i + n + sz; -+ } -+ while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ -+ p->nParent--; -+ p->path.nUsed = p->aParent[p->nParent].nPath; -+ levelChange = 1; -+ } -+ if( levelChange ){ -+ if( p->nParent>0 ){ -+ JsonParent *pParent = &p->aParent[p->nParent-1]; -+ u32 iVal = pParent->iValue; -+ p->eType = p->sParse.aBlob[iVal] & 0x0f; -+ }else{ -+ p->eType = 0; - } - } - }else{ -- switch( p->eType ){ -- case JSON_ARRAY: { -- p->i += jsonNodeSize(&p->sParse.aNode[p->i]); -- p->iRowid++; -- break; -- } -- case JSON_OBJECT: { -- p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]); -- p->iRowid++; -- break; -- } -- default: { -- p->i = p->iEnd; -- break; -- } -- } -+ u32 n, sz = 0; -+ u32 i = jsonSkipLabel(p); -+ n = jsonbPayloadSize(&p->sParse, i, &sz); -+ p->i = i + n + sz; - } -- return SQLITE_OK; -+ if( p->eType==JSONB_ARRAY && p->nParent ){ -+ p->aParent[p->nParent-1].iKey++; -+ } -+ p->iRowid++; -+ return rc; - } - --/* Append the name of the path for element i to pStr -+/* Length of the path for rowid==0 in bRecursive mode. - */ --static void jsonEachComputePath( -- JsonEachCursor *p, /* The cursor */ -- JsonString *pStr, /* Write the path here */ -- u32 i /* Path to this element */ --){ -- JsonNode *pNode, *pUp; -- u32 iUp; -- if( i==0 ){ -- jsonAppendChar(pStr, '$'); -- return; -- } -- iUp = p->sParse.aUp[i]; -- jsonEachComputePath(p, pStr, iUp); -- pNode = &p->sParse.aNode[i]; -- pUp = &p->sParse.aNode[iUp]; -- if( pUp->eType==JSON_ARRAY ){ -- jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); -- }else{ -- assert( pUp->eType==JSON_OBJECT ); -- if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; -- assert( pNode->eType==JSON_STRING ); -- assert( pNode->jnFlags & JNODE_LABEL ); -- jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1); -+static int jsonEachPathLength(JsonEachCursor *p){ -+ u32 n = p->path.nUsed; -+ char *z = p->path.zBuf; -+ if( p->iRowid==0 && p->bRecursive && n>=2 ){ -+ while( n>1 ){ -+ n--; -+ if( z[n]=='[' || z[n]=='.' ){ -+ u32 x, sz = 0; -+ char cSaved = z[n]; -+ z[n] = 0; -+ assert( p->sParse.eEdit==0 ); -+ x = jsonLookupStep(&p->sParse, 0, z+1, 0); -+ z[n] = cSaved; -+ if( JSON_LOOKUP_ISERROR(x) ) continue; -+ if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break; -+ } -+ } - } -+ return n; - } - - /* Return the value of a column */ - static int jsonEachColumn( - sqlite3_vtab_cursor *cur, /* The cursor */ - sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ -- int i /* Which column to return */ -+ int iColumn /* Which column to return */ - ){ - JsonEachCursor *p = (JsonEachCursor*)cur; -- JsonNode *pThis = &p->sParse.aNode[p->i]; -- switch( i ){ -+ switch( iColumn ){ - case JEACH_KEY: { -- if( p->i==0 ) break; -- if( p->eType==JSON_OBJECT ){ -- jsonReturn(pThis, ctx, 0); -- }else if( p->eType==JSON_ARRAY ){ -- u32 iKey; -- if( p->bRecursive ){ -- if( p->iRowid==0 ) break; -- iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; -+ if( p->nParent==0 ){ -+ u32 n, j; -+ if( p->nRoot==1 ) break; -+ j = jsonEachPathLength(p); -+ n = p->nRoot - j; -+ if( n==0 ){ -+ break; -+ }else if( p->path.zBuf[j]=='[' ){ -+ i64 x; -+ sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8); -+ sqlite3_result_int64(ctx, x); -+ }else if( p->path.zBuf[j+1]=='"' ){ -+ sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT); - }else{ -- iKey = p->iRowid; -+ sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT); - } -- sqlite3_result_int64(ctx, (sqlite3_int64)iKey); -+ break; -+ } -+ if( p->eType==JSONB_OBJECT ){ -+ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); -+ }else{ -+ assert( p->eType==JSONB_ARRAY ); -+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); - } - break; - } - case JEACH_VALUE: { -- if( pThis->jnFlags & JNODE_LABEL ) pThis++; -- jsonReturn(pThis, ctx, 0); -+ u32 i = jsonSkipLabel(p); -+ jsonReturnFromBlob(&p->sParse, i, ctx, 1); -+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){ -+ sqlite3_result_subtype(ctx, JSON_SUBTYPE); -+ } - break; - } - case JEACH_TYPE: { -- if( pThis->jnFlags & JNODE_LABEL ) pThis++; -- sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC); -+ u32 i = jsonSkipLabel(p); -+ u8 eType = p->sParse.aBlob[i] & 0x0f; -+ sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); - break; - } - case JEACH_ATOM: { -- if( pThis->jnFlags & JNODE_LABEL ) pThis++; -- if( pThis->eType>=JSON_ARRAY ) break; -- jsonReturn(pThis, ctx, 0); -+ u32 i = jsonSkipLabel(p); -+ if( (p->sParse.aBlob[i] & 0x0f)sParse, i, ctx, 1); -+ } - break; - } - case JEACH_ID: { -- sqlite3_result_int64(ctx, -- (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0)); -+ sqlite3_result_int64(ctx, (sqlite3_int64)p->i); - break; - } - case JEACH_PARENT: { -- if( p->i>p->iBegin && p->bRecursive ){ -- sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]); -+ if( p->nParent>0 && p->bRecursive ){ -+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); - } - break; - } - case JEACH_FULLKEY: { -- JsonString x; -- jsonInit(&x, ctx); -- if( p->bRecursive ){ -- jsonEachComputePath(p, &x, p->i); -- }else{ -- if( p->zRoot ){ -- jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); -- }else{ -- jsonAppendChar(&x, '$'); -- } -- if( p->eType==JSON_ARRAY ){ -- jsonPrintf(30, &x, "[%d]", p->iRowid); -- }else if( p->eType==JSON_OBJECT ){ -- jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1); -- } -- } -- jsonResult(&x); -+ u64 nBase = p->path.nUsed; -+ if( p->nParent ) jsonAppendPathName(p); -+ sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, -+ SQLITE_TRANSIENT, SQLITE_UTF8); -+ p->path.nUsed = nBase; - break; - } - case JEACH_PATH: { -- if( p->bRecursive ){ -- JsonString x; -- jsonInit(&x, ctx); -- jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); -- jsonResult(&x); -- break; -- } -- /* For json_each() path and root are the same so fall through -- ** into the root case */ -- /* no break */ deliberate_fall_through -+ u32 n = jsonEachPathLength(p); -+ sqlite3_result_text64(ctx, p->path.zBuf, n, -+ SQLITE_TRANSIENT, SQLITE_UTF8); -+ break; - } - default: { -- const char *zRoot = p->zRoot; -- if( zRoot==0 ) zRoot = "$"; -- sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); -+ sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC); - break; - } - case JEACH_JSON: { -- assert( i==JEACH_JSON ); -- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); -+ if( p->sParse.zJson==0 ){ -+ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, -+ SQLITE_TRANSIENT); -+ }else{ -+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT); -+ } - break; - } - } -@@ -187775,7 +213155,7 @@ static int jsonEachBestIndex( - /* This implementation assumes that JSON and ROOT are the last two - ** columns in the table */ - assert( JEACH_ROOT == JEACH_JSON+1 ); -- UNUSED_PARAM(tab); -+ UNUSED_PARAMETER(tab); - aIdx[0] = aIdx[1] = -1; - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ -@@ -187784,6 +213164,7 @@ static int jsonEachBestIndex( - if( pConstraint->iColumn < JEACH_JSON ) continue; - iCol = pConstraint->iColumn - JEACH_JSON; - assert( iCol==0 || iCol==1 ); -+ testcase( iCol==0 ); - iMask = 1 << iCol; - if( pConstraint->usable==0 ){ - unusableMask |= iMask; -@@ -187792,6 +213173,13 @@ static int jsonEachBestIndex( - idxMask |= iMask; - } - } -+ if( pIdxInfo->nOrderBy>0 -+ && pIdxInfo->aOrderBy[0].iColumn<0 -+ && pIdxInfo->aOrderBy[0].desc==0 -+ ){ -+ pIdxInfo->orderByConsumed = 1; -+ } -+ - if( (unusableMask & ~idxMask)!=0 ){ - /* If there are any unusable constraints on JSON or ROOT, then reject - ** this entire plan */ -@@ -187826,76 +213214,96 @@ static int jsonEachFilter( - int argc, sqlite3_value **argv - ){ - JsonEachCursor *p = (JsonEachCursor*)cur; -- const char *z; - const char *zRoot = 0; -- sqlite3_int64 n; -+ u32 i, n, sz; - -- UNUSED_PARAM(idxStr); -- UNUSED_PARAM(argc); -+ UNUSED_PARAMETER(idxStr); -+ UNUSED_PARAMETER(argc); - jsonEachCursorReset(p); - if( idxNum==0 ) return SQLITE_OK; -- z = (const char*)sqlite3_value_text(argv[0]); -- if( z==0 ) return SQLITE_OK; -- n = sqlite3_value_bytes(argv[0]); -- p->zJson = sqlite3_malloc64( n+1 ); -- if( p->zJson==0 ) return SQLITE_NOMEM; -- memcpy(p->zJson, z, (size_t)n+1); -- if( jsonParse(&p->sParse, 0, p->zJson) ){ -- int rc = SQLITE_NOMEM; -- if( p->sParse.oom==0 ){ -- sqlite3_free(cur->pVtab->zErrMsg); -- cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); -- if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR; -- } -- jsonEachCursorReset(p); -- return rc; -- }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){ -- jsonEachCursorReset(p); -- return SQLITE_NOMEM; -+ memset(&p->sParse, 0, sizeof(p->sParse)); -+ p->sParse.nJPRef = 1; -+ p->sParse.db = p->db; -+ if( jsonArgIsJsonb(argv[0], &p->sParse) ){ -+ /* We have JSONB */ - }else{ -- JsonNode *pNode = 0; -- if( idxNum==3 ){ -- const char *zErr = 0; -- zRoot = (const char*)sqlite3_value_text(argv[1]); -- if( zRoot==0 ) return SQLITE_OK; -- n = sqlite3_value_bytes(argv[1]); -- p->zRoot = sqlite3_malloc64( n+1 ); -- if( p->zRoot==0 ) return SQLITE_NOMEM; -- memcpy(p->zRoot, zRoot, (size_t)n+1); -- if( zRoot[0]!='$' ){ -- zErr = zRoot; -- }else{ -- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr); -+ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); -+ p->sParse.nJson = sqlite3_value_bytes(argv[0]); -+ if( p->sParse.zJson==0 ){ -+ p->i = p->iEnd = 0; -+ return SQLITE_OK; -+ } -+ if( jsonConvertTextToBlob(&p->sParse, 0) ){ -+ if( p->sParse.oom ){ -+ return SQLITE_NOMEM; - } -- if( zErr ){ -+ goto json_each_malformed_input; -+ } -+ } -+ if( idxNum==3 ){ -+ zRoot = (const char*)sqlite3_value_text(argv[1]); -+ if( zRoot==0 ) return SQLITE_OK; -+ if( zRoot[0]!='$' ){ -+ sqlite3_free(cur->pVtab->zErrMsg); -+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); -+ jsonEachCursorReset(p); -+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; -+ } -+ p->nRoot = sqlite3Strlen30(zRoot); -+ if( zRoot[1]==0 ){ -+ i = p->i = 0; -+ p->eType = 0; -+ }else{ -+ i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0); -+ if( JSON_LOOKUP_ISERROR(i) ){ -+ if( i==JSON_LOOKUP_NOTFOUND ){ -+ p->i = 0; -+ p->eType = 0; -+ p->iEnd = 0; -+ return SQLITE_OK; -+ } - sqlite3_free(cur->pVtab->zErrMsg); -- cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr); -+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); - jsonEachCursorReset(p); - return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; -- }else if( pNode==0 ){ -- return SQLITE_OK; - } -- }else{ -- pNode = p->sParse.aNode; -- } -- p->iBegin = p->i = (int)(pNode - p->sParse.aNode); -- p->eType = pNode->eType; -- if( p->eType>=JSON_ARRAY ){ -- pNode->u.iKey = 0; -- p->iEnd = p->i + pNode->n + 1; -- if( p->bRecursive ){ -- p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType; -- if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){ -- p->i--; -- } -+ if( p->sParse.iLabel ){ -+ p->i = p->sParse.iLabel; -+ p->eType = JSONB_OBJECT; - }else{ -- p->i++; -+ p->i = i; -+ p->eType = JSONB_ARRAY; - } -- }else{ -- p->iEnd = p->i+1; - } -+ jsonAppendRaw(&p->path, zRoot, p->nRoot); -+ }else{ -+ i = p->i = 0; -+ p->eType = 0; -+ p->nRoot = 1; -+ jsonAppendRaw(&p->path, "$", 1); -+ } -+ p->nParent = 0; -+ n = jsonbPayloadSize(&p->sParse, i, &sz); -+ p->iEnd = i+n+sz; -+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ -+ p->i = i + n; -+ p->eType = p->sParse.aBlob[i] & 0x0f; -+ p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent)); -+ if( p->aParent==0 ) return SQLITE_NOMEM; -+ p->nParent = 1; -+ p->nParentAlloc = 1; -+ p->aParent[0].iKey = 0; -+ p->aParent[0].iEnd = p->iEnd; -+ p->aParent[0].iHead = p->i; -+ p->aParent[0].iValue = i; - } - return SQLITE_OK; -+ -+json_each_malformed_input: -+ sqlite3_free(cur->pVtab->zErrMsg); -+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); -+ jsonEachCursorReset(p); -+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; - } - - /* The methods of the json_each virtual table */ -@@ -187923,7 +213331,8 @@ static sqlite3_module jsonEachModule = { - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ -- 0 /* xShadowName */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - - /* The methods of the json_tree virtual table. */ -@@ -187951,111 +213360,98 @@ static sqlite3_module jsonTreeModule = { - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ -- 0 /* xShadowName */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - #endif /* SQLITE_OMIT_VIRTUALTABLE */ -- --/**************************************************************************** --** The following routines are the only publically visible identifiers in this --** file. Call the following routines in order to register the various SQL --** functions and the virtual table implemented by this file. --****************************************************************************/ -- --SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){ -- int rc = SQLITE_OK; -- unsigned int i; -- static const struct { -- const char *zName; -- int nArg; -- int flag; -- void (*xFunc)(sqlite3_context*,int,sqlite3_value**); -- } aFunc[] = { -- { "json", 1, 0, jsonRemoveFunc }, -- { "json_array", -1, 0, jsonArrayFunc }, -- { "json_array_length", 1, 0, jsonArrayLengthFunc }, -- { "json_array_length", 2, 0, jsonArrayLengthFunc }, -- { "json_extract", -1, 0, jsonExtractFunc }, -- { "json_insert", -1, 0, jsonSetFunc }, -- { "json_object", -1, 0, jsonObjectFunc }, -- { "json_patch", 2, 0, jsonPatchFunc }, -- { "json_quote", 1, 0, jsonQuoteFunc }, -- { "json_remove", -1, 0, jsonRemoveFunc }, -- { "json_replace", -1, 0, jsonReplaceFunc }, -- { "json_set", -1, 1, jsonSetFunc }, -- { "json_type", 1, 0, jsonTypeFunc }, -- { "json_type", 2, 0, jsonTypeFunc }, -- { "json_valid", 1, 0, jsonValidFunc }, -- -+#endif /* !defined(SQLITE_OMIT_JSON) */ -+ -+/* -+** Register JSON functions. -+*/ -+SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){ -+#ifndef SQLITE_OMIT_JSON -+ static FuncDef aJsonFunc[] = { -+ /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */ -+ /* | | */ -+ /* Uses cache ------, | | ,---- Returns JSONB */ -+ /* | | | | */ -+ /* Number of arguments ---, | | | | ,--- Flags */ -+ /* | | | | | | */ -+ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), -+ JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), -+ JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), -+ JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), -+ JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), -+ JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), -+ JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), -+ JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc), -+ JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc), -+ JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc), -+ JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc), -+ JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc), -+ JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc), -+ JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc), -+ JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc), -+ JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc), -+ JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc), -+ JFUNCTION(json_pretty, 1,1,0, 0,0,0, jsonPrettyFunc), -+ JFUNCTION(json_pretty, 2,1,0, 0,0,0, jsonPrettyFunc), -+ JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc), -+ JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc), -+ JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc), -+ JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc), -+ JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc), -+ JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc), -+ JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc), -+ JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc), -+ JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc), -+ JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc), -+ JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc), - #if SQLITE_DEBUG -- /* DEBUG and TESTING functions */ -- { "json_parse", 1, 0, jsonParseFunc }, -- { "json_test1", 1, 0, jsonTest1Func }, --#endif -+ JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc), -+#endif -+ WAGGREGATE(json_group_array, 1, 0, 0, -+ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, -+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| -+ SQLITE_DETERMINISTIC), -+ WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0, -+ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, -+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), -+ WAGGREGATE(json_group_object, 2, 0, 0, -+ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, -+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), -+ WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0, -+ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, -+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| -+ SQLITE_DETERMINISTIC) - }; -+ sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); -+#endif -+} -+ -+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) -+/* -+** Register the JSON table-valued functions -+*/ -+SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3 *db){ -+ int rc = SQLITE_OK; - static const struct { -- const char *zName; -- int nArg; -- void (*xStep)(sqlite3_context*,int,sqlite3_value**); -- void (*xFinal)(sqlite3_context*); -- void (*xValue)(sqlite3_context*); -- } aAgg[] = { -- { "json_group_array", 1, -- jsonArrayStep, jsonArrayFinal, jsonArrayValue }, -- { "json_group_object", 2, -- jsonObjectStep, jsonObjectFinal, jsonObjectValue }, -- }; --#ifndef SQLITE_OMIT_VIRTUALTABLE -- static const struct { -- const char *zName; -- sqlite3_module *pModule; -+ const char *zName; -+ sqlite3_module *pModule; - } aMod[] = { - { "json_each", &jsonEachModule }, - { "json_tree", &jsonTreeModule }, - }; --#endif -- static const int enc = -- SQLITE_UTF8 | -- SQLITE_DETERMINISTIC | -- SQLITE_INNOCUOUS; -- for(i=0; i */ -+ -+/* -+** If building separately, we will need some setup that is normally -+** found in sqliteInt.h -+*/ -+#if !defined(SQLITE_AMALGAMATION) - #include "sqlite3rtree.h" - typedef sqlite3_int64 i64; - typedef sqlite3_uint64 u64; -@@ -188136,6 +213538,32 @@ typedef unsigned int u32; - #if defined(NDEBUG) && defined(SQLITE_DEBUG) - # undef NDEBUG - #endif -+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) -+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 -+#endif -+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) -+# define ALWAYS(X) (1) -+# define NEVER(X) (0) -+#elif !defined(NDEBUG) -+# define ALWAYS(X) ((X)?1:(assert(0),0)) -+# define NEVER(X) ((X)?(assert(0),1):0) -+#else -+# define ALWAYS(X) (X) -+# define NEVER(X) (X) -+#endif -+#ifndef offsetof -+#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) -+#endif -+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -+# define FLEXARRAY -+#else -+# define FLEXARRAY 1 -+#endif -+#endif /* !defined(SQLITE_AMALGAMATION) */ -+ -+/* Macro to check for 4-byte alignment. Only used inside of assert() */ -+#ifdef SQLITE_DEBUG -+# define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) - #endif - - /* #include */ -@@ -188194,13 +213622,16 @@ struct Rtree { - u8 nBytesPerCell; /* Bytes consumed per cell */ - u8 inWrTrans; /* True if inside write transaction */ - u8 nAux; /* # of auxiliary columns in %_rowid */ -+#ifdef SQLITE_ENABLE_GEOPOLY - u8 nAuxNotNull; /* Number of initial not-null aux columns */ -+#endif - #ifdef SQLITE_DEBUG - u8 bCorrupt; /* Shadow table corruption detected */ - #endif - int iDepth; /* Current depth of the r-tree structure */ - char *zDb; /* Name of database containing r-tree table */ - char *zName; /* Name of r-tree table */ -+ char *zNodeName; /* Name of the %_node table */ - u32 nBusy; /* Current number of users of this structure */ - i64 nRowEst; /* Estimated number of rows in this table */ - u32 nCursor; /* Number of open cursors */ -@@ -188213,7 +213644,6 @@ struct Rtree { - ** headed by the node (leaf nodes have RtreeNode.iNode==0). - */ - RtreeNode *pDeleted; -- int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */ - - /* Blob I/O on xxx_node */ - sqlite3_blob *pNodeBlob; -@@ -188449,9 +213879,13 @@ struct RtreeMatchArg { - RtreeGeomCallback cb; /* Info about the callback functions */ - int nParam; /* Number of parameters to the SQL function */ - sqlite3_value **apSqlParam; /* Original SQL parameter values */ -- RtreeDValue aParam[1]; /* Values for parameters to the SQL function */ -+ RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */ - }; - -+/* Size of an RtreeMatchArg object with N parameters */ -+#define SZ_RTREEMATCHARG(N) \ -+ (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue)) -+ - #ifndef MAX - # define MAX(x,y) ((x) < (y) ? (y) : (x)) - #endif -@@ -188476,7 +213910,12 @@ struct RtreeMatchArg { - ** it is not, make it a no-op. - */ - #ifndef SQLITE_AMALGAMATION --# define testcase(X) -+# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) -+ unsigned int sqlite3RtreeTestcase = 0; -+# define testcase(X) if( X ){ sqlite3RtreeTestcase += __LINE__; } -+# else -+# define testcase(X) -+# endif - #endif - - /* -@@ -188505,17 +213944,23 @@ struct RtreeMatchArg { - ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined - ** at run-time. - */ --#ifndef SQLITE_BYTEORDER --#if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ -- defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ -- defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ -- defined(__arm__) --# define SQLITE_BYTEORDER 1234 --#elif defined(sparc) || defined(__ppc__) --# define SQLITE_BYTEORDER 4321 --#else --# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */ --#endif -+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */ -+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ -+# define SQLITE_BYTEORDER 4321 -+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ -+# define SQLITE_BYTEORDER 1234 -+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 -+# define SQLITE_BYTEORDER 4321 -+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ -+ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ -+ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ -+ defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) -+# define SQLITE_BYTEORDER 1234 -+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) -+# define SQLITE_BYTEORDER 4321 -+# else -+# define SQLITE_BYTEORDER 0 -+# endif - #endif - - -@@ -188536,7 +213981,7 @@ static int readInt16(u8 *p){ - return (p[0]<<8) + p[1]; - } - static void readCoord(u8 *p, RtreeCoord *pCoord){ -- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ -+ assert( FOUR_BYTE_ALIGNED(p) ); - #if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300 - pCoord->u = _byteswap_ulong(*(u32*)p); - #elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 -@@ -188590,7 +214035,7 @@ static void writeInt16(u8 *p, int i){ - } - static int writeCoord(u8 *p, RtreeCoord *pCoord){ - u32 i; -- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */ -+ assert( FOUR_BYTE_ALIGNED(p) ); - assert( sizeof(RtreeCoord)==4 ); - assert( sizeof(u32)==4 ); - #if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000 -@@ -188718,23 +214163,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ - ** Clear the Rtree.pNodeBlob object - */ - static void nodeBlobReset(Rtree *pRtree){ -- if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){ -- sqlite3_blob *pBlob = pRtree->pNodeBlob; -- pRtree->pNodeBlob = 0; -- sqlite3_blob_close(pBlob); -- } --} -- --/* --** Check to see if pNode is the same as pParent or any of the parents --** of pParent. --*/ --static int nodeInParentChain(const RtreeNode *pNode, const RtreeNode *pParent){ -- do{ -- if( pNode==pParent ) return 1; -- pParent = pParent->pParent; -- }while( pParent ); -- return 0; -+ sqlite3_blob *pBlob = pRtree->pNodeBlob; -+ pRtree->pNodeBlob = 0; -+ sqlite3_blob_close(pBlob); - } - - /* -@@ -188753,14 +214184,7 @@ static int nodeAcquire( - ** increase its reference count and return it. - */ - if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ -- if( pParent && !pNode->pParent ){ -- if( nodeInParentChain(pNode, pParent) ){ -- RTREE_IS_CORRUPT(pRtree); -- return SQLITE_CORRUPT_VTAB; -- } -- pParent->nRef++; -- pNode->pParent = pParent; -- }else if( pParent && pNode->pParent && pParent!=pNode->pParent ){ -+ if( pParent && ALWAYS(pParent!=pNode->pParent) ){ - RTREE_IS_CORRUPT(pRtree); - return SQLITE_CORRUPT_VTAB; - } -@@ -188780,14 +214204,11 @@ static int nodeAcquire( - } - } - if( pRtree->pNodeBlob==0 ){ -- char *zTab = sqlite3_mprintf("%s_node", pRtree->zName); -- if( zTab==0 ) return SQLITE_NOMEM; -- rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0, -+ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName, -+ "data", iNode, 0, - &pRtree->pNodeBlob); -- sqlite3_free(zTab); - } - if( rc ){ -- nodeBlobReset(pRtree); - *ppNode = 0; - /* If unable to open an sqlite3_blob on the desired row, that can only - ** be because the shadow tables hold erroneous data. */ -@@ -188818,7 +214239,7 @@ static int nodeAcquire( - ** are the leaves, and so on. If the depth as specified on the root node - ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. - */ -- if( pNode && iNode==1 ){ -+ if( rc==SQLITE_OK && pNode && iNode==1 ){ - pRtree->iDepth = readInt16(pNode->zData); - if( pRtree->iDepth>RTREE_MAX_DEPTH ){ - rc = SQLITE_CORRUPT_VTAB; -@@ -188847,6 +214268,7 @@ static int nodeAcquire( - } - *ppNode = pNode; - }else{ -+ nodeBlobReset(pRtree); - if( pNode ){ - pRtree->nNodeRef--; - sqlite3_free(pNode); -@@ -188991,6 +214413,7 @@ static void nodeGetCoord( - int iCoord, /* Which coordinate to extract */ - RtreeCoord *pCoord /* OUT: Space to write result to */ - ){ -+ assert( iCellzData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); - } - -@@ -189180,7 +214603,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){ - sqlite3_finalize(pCsr->pReadAux); - sqlite3_free(pCsr); - pRtree->nCursor--; -- nodeBlobReset(pRtree); -+ if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){ -+ nodeBlobReset(pRtree); -+ } - return SQLITE_OK; - } - -@@ -189337,24 +214762,33 @@ static void rtreeNonleafConstraint( - assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE - || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE - || p->op==RTREE_FALSE ); -- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ -+ assert( FOUR_BYTE_ALIGNED(pCellData) ); - switch( p->op ){ - case RTREE_TRUE: return; /* Always satisfied */ - case RTREE_FALSE: break; /* Never satisfied */ -+ case RTREE_EQ: -+ RTREE_DECODE_COORD(eInt, pCellData, val); -+ /* val now holds the lower bound of the coordinate pair */ -+ if( p->u.rValue>=val ){ -+ pCellData += 4; -+ RTREE_DECODE_COORD(eInt, pCellData, val); -+ /* val now holds the upper bound of the coordinate pair */ -+ if( p->u.rValue<=val ) return; -+ } -+ break; - case RTREE_LE: - case RTREE_LT: -- case RTREE_EQ: - RTREE_DECODE_COORD(eInt, pCellData, val); - /* val now holds the lower bound of the coordinate pair */ - if( p->u.rValue>=val ) return; -- if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */ -- /* Fall through for the RTREE_EQ case */ -+ break; - -- default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */ -+ default: - pCellData += 4; - RTREE_DECODE_COORD(eInt, pCellData, val); - /* val now holds the upper bound of the coordinate pair */ - if( p->u.rValue<=val ) return; -+ break; - } - *peWithin = NOT_WITHIN; - } -@@ -189381,7 +214815,7 @@ static void rtreeLeafConstraint( - || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE - || p->op==RTREE_FALSE ); - pCellData += 8 + p->iCoord*4; -- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */ -+ assert( FOUR_BYTE_ALIGNED(pCellData) ); - RTREE_DECODE_COORD(eInt, pCellData, xN); - switch( p->op ){ - case RTREE_TRUE: return; /* Always satisfied */ -@@ -189424,11 +214858,12 @@ static int nodeRowidIndex( - */ - static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ - RtreeNode *pParent = pNode->pParent; -- if( pParent ){ -+ if( ALWAYS(pParent) ){ - return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); -+ }else{ -+ *piIndex = -1; -+ return SQLITE_OK; - } -- *piIndex = -1; -- return SQLITE_OK; - } - - /* -@@ -189551,7 +214986,8 @@ static RtreeSearchPoint *rtreeSearchPointNew( - pNew = rtreeEnqueue(pCur, rScore, iLevel); - if( pNew==0 ) return 0; - ii = (int)(pNew - pCur->aPoint) + 1; -- if( iiaNode[ii]==0 ); - pCur->aNode[ii] = pCur->aNode[0]; - }else{ -@@ -189612,7 +215048,7 @@ static void rtreeSearchPointPop(RtreeCursor *p){ - if( p->bPoint ){ - p->anQueue[p->sPoint.iLevel]--; - p->bPoint = 0; -- }else if( p->nPoint ){ -+ }else if( ALWAYS(p->nPoint) ){ - p->anQueue[p->aPoint[0].iLevel]--; - n = --p->nPoint; - p->aPoint[0] = p->aPoint[n]; -@@ -189753,8 +215189,12 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ - RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr); - int rc = SQLITE_OK; - RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); -- if( rc==SQLITE_OK && p ){ -- *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); -+ if( rc==SQLITE_OK && ALWAYS(p) ){ -+ if( p->iCell>=NCELL(pNode) ){ -+ rc = SQLITE_ABORT; -+ }else{ -+ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); -+ } - } - return rc; - } -@@ -189771,7 +215211,8 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ - RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); - - if( rc ) return rc; -- if( p==0 ) return SQLITE_OK; -+ if( NEVER(p==0) ) return SQLITE_OK; -+ if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT; - if( i==0 ){ - sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); - }else if( i<=pRtree->nDim2 ){ -@@ -189869,6 +215310,8 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ - return SQLITE_OK; - } - -+SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); -+ - /* - ** Rtree virtual table module xFilter method. - */ -@@ -189898,7 +215341,8 @@ static int rtreeFilter( - i64 iNode = 0; - int eType = sqlite3_value_numeric_type(argv[0]); - if( eType==SQLITE_INTEGER -- || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid) -+ || (eType==SQLITE_FLOAT -+ && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0]))) - ){ - rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); - }else{ -@@ -189949,7 +215393,20 @@ static int rtreeFilter( - p->pInfo->nCoord = pRtree->nDim2; - p->pInfo->anQueue = pCsr->anQueue; - p->pInfo->mxLevel = pRtree->iDepth + 1; -- }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ -+ }else if( eType==SQLITE_INTEGER ){ -+ sqlite3_int64 iVal = sqlite3_value_int64(argv[ii]); -+#ifdef SQLITE_RTREE_INT_ONLY -+ p->u.rValue = iVal; -+#else -+ p->u.rValue = (double)iVal; -+ if( iVal>=((sqlite3_int64)1)<<48 -+ || iVal<=-(((sqlite3_int64)1)<<48) -+ ){ -+ if( p->op==RTREE_LT ) p->op = RTREE_LE; -+ if( p->op==RTREE_GT ) p->op = RTREE_GE; -+ } -+#endif -+ }else if( eType==SQLITE_FLOAT ){ - #ifdef SQLITE_RTREE_INT_ONLY - p->u.rValue = sqlite3_value_int64(argv[ii]); - #else -@@ -189970,8 +215427,11 @@ static int rtreeFilter( - } - if( rc==SQLITE_OK ){ - RtreeSearchPoint *pNew; -+ assert( pCsr->bPoint==0 ); /* Due to the resetCursor() call above */ - pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1)); -- if( pNew==0 ) return SQLITE_NOMEM; -+ if( NEVER(pNew==0) ){ /* Because pCsr->bPoint was FALSE */ -+ return SQLITE_NOMEM; -+ } - pNew->id = 1; - pNew->iCell = 0; - pNew->eWithin = PARTLY_WITHIN; -@@ -190048,7 +215508,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; - - if( bMatch==0 && p->usable -- && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ -+ && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - /* We have an equality constraint on the rowid. Use strategy 1. */ - int jj; -@@ -190077,11 +215537,12 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) - ){ - u8 op; -+ u8 doOmit = 1; - switch( p->op ){ -- case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; -- case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; -+ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; doOmit = 0; break; -+ case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; doOmit = 0; break; - case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; -- case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; -+ case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; doOmit = 0; break; - case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; - case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break; - default: op = 0; break; -@@ -190090,15 +215551,19 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - zIdxStr[iIdx++] = op; - zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); - pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); -- pIdxInfo->aConstraintUsage[ii].omit = 1; -+ pIdxInfo->aConstraintUsage[ii].omit = doOmit; - } - } - } - - pIdxInfo->idxNum = 2; - pIdxInfo->needToFreeIdxStr = 1; -- if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){ -- return SQLITE_NOMEM; -+ if( iIdx>0 ){ -+ pIdxInfo->idxStr = sqlite3_malloc( iIdx+1 ); -+ if( pIdxInfo->idxStr==0 ){ -+ return SQLITE_NOMEM; -+ } -+ memcpy(pIdxInfo->idxStr, zIdxStr, iIdx+1); - } - - nRow = pRtree->nRowEst >> (iIdx/2); -@@ -190109,7 +215574,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - } - - /* --** Return the N-dimensional volumn of the cell stored in *p. -+** Return the N-dimensional volume of the cell stored in *p. - */ - static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ - RtreeDValue area = (RtreeDValue)1; -@@ -190177,31 +215642,22 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ - */ - static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ - int ii; -- int isInt = (pRtree->eCoordType==RTREE_COORD_INT32); -- for(ii=0; iinDim2; ii+=2){ -- RtreeCoord *a1 = &p1->aCoord[ii]; -- RtreeCoord *a2 = &p2->aCoord[ii]; -- if( (!isInt && (a2[0].fa1[1].f)) -- || ( isInt && (a2[0].ia1[1].i)) -- ){ -- return 0; -+ if( pRtree->eCoordType==RTREE_COORD_INT32 ){ -+ for(ii=0; iinDim2; ii+=2){ -+ RtreeCoord *a1 = &p1->aCoord[ii]; -+ RtreeCoord *a2 = &p2->aCoord[ii]; -+ if( a2[0].ia1[1].i ) return 0; -+ } -+ }else{ -+ for(ii=0; iinDim2; ii+=2){ -+ RtreeCoord *a1 = &p1->aCoord[ii]; -+ RtreeCoord *a2 = &p2->aCoord[ii]; -+ if( a2[0].fa1[1].f ) return 0; - } - } - return 1; - } - --/* --** Return the amount cell p would grow by if it were unioned with pCell. --*/ --static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){ -- RtreeDValue area; -- RtreeCell cell; -- memcpy(&cell, p, sizeof(RtreeCell)); -- area = cellArea(pRtree, &cell); -- cellUnion(pRtree, &cell, pCell); -- return (cellArea(pRtree, &cell)-area); --} -- - static RtreeDValue cellOverlap( - Rtree *pRtree, - RtreeCell *p, -@@ -190248,38 +215704,52 @@ static int ChooseLeaf( - for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){ - int iCell; - sqlite3_int64 iBest = 0; -- -+ int bFound = 0; - RtreeDValue fMinGrowth = RTREE_ZERO; - RtreeDValue fMinArea = RTREE_ZERO; -- - int nCell = NCELL(pNode); -- RtreeCell cell; -- RtreeNode *pChild; -- -- RtreeCell *aCell = 0; -+ RtreeNode *pChild = 0; - -- /* Select the child node which will be enlarged the least if pCell -- ** is inserted into it. Resolve ties by choosing the entry with -- ** the smallest area. -+ /* First check to see if there is are any cells in pNode that completely -+ ** contains pCell. If two or more cells in pNode completely contain pCell -+ ** then pick the smallest. - */ - for(iCell=0; iCellpParent ){ - RtreeNode *pParent = p->pParent; - RtreeCell cell; - int iCell; - -- if( (++cnt)>1000 || nodeParentIndex(pRtree, p, &iCell) ){ -+ cnt++; -+ if( NEVER(cnt>100) ){ -+ RTREE_IS_CORRUPT(pRtree); -+ return SQLITE_CORRUPT_VTAB; -+ } -+ rc = nodeParentIndex(pRtree, p, &iCell); -+ if( NEVER(rc!=SQLITE_OK) ){ - RTREE_IS_CORRUPT(pRtree); - return SQLITE_CORRUPT_VTAB; - } -@@ -190345,77 +215822,6 @@ static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){ - static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int); - - --/* --** Arguments aIdx, aDistance and aSpare all point to arrays of size --** nIdx. The aIdx array contains the set of integers from 0 to --** (nIdx-1) in no particular order. This function sorts the values --** in aIdx according to the indexed values in aDistance. For --** example, assuming the inputs: --** --** aIdx = { 0, 1, 2, 3 } --** aDistance = { 5.0, 2.0, 7.0, 6.0 } --** --** this function sets the aIdx array to contain: --** --** aIdx = { 0, 1, 2, 3 } --** --** The aSpare array is used as temporary working space by the --** sorting algorithm. --*/ --static void SortByDistance( -- int *aIdx, -- int nIdx, -- RtreeDValue *aDistance, -- int *aSpare --){ -- if( nIdx>1 ){ -- int iLeft = 0; -- int iRight = 0; -- -- int nLeft = nIdx/2; -- int nRight = nIdx-nLeft; -- int *aLeft = aIdx; -- int *aRight = &aIdx[nLeft]; -- -- SortByDistance(aLeft, nLeft, aDistance, aSpare); -- SortByDistance(aRight, nRight, aDistance, aSpare); -- -- memcpy(aSpare, aLeft, sizeof(int)*nLeft); -- aLeft = aSpare; -- -- while( iLeft0 ){ - RtreeNode *pChild = nodeHashLookup(pRtree, iRowid); -+ RtreeNode *p; -+ for(p=pNode; p; p=p->pParent){ -+ if( p==pChild ) return SQLITE_CORRUPT_VTAB; -+ } - if( pChild ){ - nodeRelease(pRtree, pChild->pParent); - nodeReference(pNode); - pChild->pParent = pNode; - } - } -+ if( NEVER(pNode==0) ) return SQLITE_ERROR; - return xSetMapping(pRtree, iRowid, pNode->iNode); - } - -@@ -190690,11 +216101,12 @@ static int SplitNode( - RtreeNode *pParent = pLeft->pParent; - int iCell; - rc = nodeParentIndex(pRtree, pLeft, &iCell); -- if( rc==SQLITE_OK ){ -+ if( ALWAYS(rc==SQLITE_OK) ){ - nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); - rc = AdjustTree(pRtree, pParent, &leftbbox); -+ assert( rc==SQLITE_OK ); - } -- if( rc!=SQLITE_OK ){ -+ if( NEVER(rc!=SQLITE_OK) ){ - goto splitnode_out; - } - } -@@ -190769,7 +216181,7 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ - */ - iNode = sqlite3_column_int64(pRtree->pReadParent, 0); - for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); -- if( !pTest ){ -+ if( pTest==0 ){ - rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); - } - } -@@ -190800,6 +216212,7 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ - pParent = pNode->pParent; - pNode->pParent = 0; - rc = deleteCell(pRtree, pParent, iCell, iHeight+1); -+ testcase( rc!=SQLITE_OK ); - } - rc2 = nodeRelease(pRtree, pParent); - if( rc==SQLITE_OK ){ -@@ -190893,107 +216306,6 @@ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){ - return rc; - } - --static int Reinsert( -- Rtree *pRtree, -- RtreeNode *pNode, -- RtreeCell *pCell, -- int iHeight --){ -- int *aOrder; -- int *aSpare; -- RtreeCell *aCell; -- RtreeDValue *aDistance; -- int nCell; -- RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS]; -- int iDim; -- int ii; -- int rc = SQLITE_OK; -- int n; -- -- memset(aCenterCoord, 0, sizeof(RtreeDValue)*RTREE_MAX_DIMENSIONS); -- -- nCell = NCELL(pNode)+1; -- n = (nCell+1)&(~1); -- -- /* Allocate the buffers used by this operation. The allocation is -- ** relinquished before this function returns. -- */ -- aCell = (RtreeCell *)sqlite3_malloc64(n * ( -- sizeof(RtreeCell) + /* aCell array */ -- sizeof(int) + /* aOrder array */ -- sizeof(int) + /* aSpare array */ -- sizeof(RtreeDValue) /* aDistance array */ -- )); -- if( !aCell ){ -- return SQLITE_NOMEM; -- } -- aOrder = (int *)&aCell[n]; -- aSpare = (int *)&aOrder[n]; -- aDistance = (RtreeDValue *)&aSpare[n]; -- -- for(ii=0; iinDim; iDim++){ -- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]); -- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]); -- } -- } -- for(iDim=0; iDimnDim; iDim++){ -- aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2)); -- } -- -- for(ii=0; iinDim; iDim++){ -- RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) - -- DCOORD(aCell[ii].aCoord[iDim*2])); -- aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]); -- } -- } -- -- SortByDistance(aOrder, nCell, aDistance, aSpare); -- nodeZero(pRtree, pNode); -- -- for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){ -- RtreeCell *p = &aCell[aOrder[ii]]; -- nodeInsertCell(pRtree, pNode, p); -- if( p->iRowid==pCell->iRowid ){ -- if( iHeight==0 ){ -- rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); -- }else{ -- rc = parentWrite(pRtree, p->iRowid, pNode->iNode); -- } -- } -- } -- if( rc==SQLITE_OK ){ -- rc = fixBoundingBox(pRtree, pNode); -- } -- for(; rc==SQLITE_OK && iiiNode currently contains -- ** the height of the sub-tree headed by the cell. -- */ -- RtreeNode *pInsert; -- RtreeCell *p = &aCell[aOrder[ii]]; -- rc = ChooseLeaf(pRtree, p, iHeight, &pInsert); -- if( rc==SQLITE_OK ){ -- int rc2; -- rc = rtreeInsertCell(pRtree, pInsert, p, iHeight); -- rc2 = nodeRelease(pRtree, pInsert); -- if( rc==SQLITE_OK ){ -- rc = rc2; -- } -- } -- } -- -- sqlite3_free(aCell); -- return rc; --} -- - /* - ** Insert cell pCell into node pNode. Node pNode is the head of a - ** subtree iHeight high (leaf nodes have iHeight==0). -@@ -191014,15 +216326,10 @@ static int rtreeInsertCell( - } - } - if( nodeInsertCell(pRtree, pNode, pCell) ){ -- if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){ -- rc = SplitNode(pRtree, pNode, pCell, iHeight); -- }else{ -- pRtree->iReinsertHeight = iHeight; -- rc = Reinsert(pRtree, pNode, pCell, iHeight); -- } -+ rc = SplitNode(pRtree, pNode, pCell, iHeight); - }else{ - rc = AdjustTree(pRtree, pNode, pCell); -- if( rc==SQLITE_OK ){ -+ if( ALWAYS(rc==SQLITE_OK) ){ - if( iHeight==0 ){ - rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); - }else{ -@@ -191128,7 +216435,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ - int rc2; - RtreeNode *pChild = 0; - i64 iChild = nodeGetRowid(pRtree, pRoot, 0); -- rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); -+ rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); /* tag-20210916a */ - if( rc==SQLITE_OK ){ - rc = removeNode(pRtree, pChild, pRtree->iDepth-1); - } -@@ -191261,7 +216568,7 @@ static int rtreeUpdate( - rtreeReference(pRtree); - assert(nData>=1); - -- cell.iRowid = 0; /* Used only to suppress a compiler warning */ -+ memset(&cell, 0, sizeof(cell)); - - /* Constraint handling. A write operation on an r-tree table may return - ** SQLITE_CONSTRAINT for two reasons: -@@ -191362,7 +216669,6 @@ static int rtreeUpdate( - } - if( rc==SQLITE_OK ){ - int rc2; -- pRtree->iReinsertHeight = -1; - rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); - rc2 = nodeRelease(pRtree, pLeaf); - if( rc==SQLITE_OK ){ -@@ -191392,7 +216698,7 @@ constraint: - static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ - Rtree *pRtree = (Rtree *)pVtab; - assert( pRtree->inWrTrans==0 ); -- pRtree->inWrTrans++; -+ pRtree->inWrTrans = 1; - return SQLITE_OK; - } - -@@ -191406,6 +216712,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){ - nodeBlobReset(pRtree); - return SQLITE_OK; - } -+static int rtreeRollback(sqlite3_vtab *pVtab){ -+ return rtreeEndTransaction(pVtab); -+} - - /* - ** The xRename method for rtree module virtual tables. -@@ -191463,7 +216772,7 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ - char *zSql; - sqlite3_stmt *p; - int rc; -- i64 nRow = 0; -+ i64 nRow = RTREE_MIN_ROWEST; - - rc = sqlite3_table_column_metadata( - db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0 -@@ -191480,20 +216789,10 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ - if( rc==SQLITE_OK ){ - if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0); - rc = sqlite3_finalize(p); -- }else if( rc!=SQLITE_NOMEM ){ -- rc = SQLITE_OK; -- } -- -- if( rc==SQLITE_OK ){ -- if( nRow==0 ){ -- pRtree->nRowEst = RTREE_DEFAULT_ROWEST; -- }else{ -- pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); -- } - } - sqlite3_free(zSql); - } -- -+ pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); - return rc; - } - -@@ -191513,8 +216812,11 @@ static int rtreeShadowName(const char *zName){ - return 0; - } - -+/* Forward declaration */ -+static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**); -+ - static sqlite3_module rtreeModule = { -- 3, /* iVersion */ -+ 4, /* iVersion */ - rtreeCreate, /* xCreate - create a table */ - rtreeConnect, /* xConnect - connect to an existing table */ - rtreeBestIndex, /* xBestIndex - Determine search strategy */ -@@ -191531,13 +216833,14 @@ static sqlite3_module rtreeModule = { - rtreeBeginTransaction, /* xBegin - begin transaction */ - rtreeEndTransaction, /* xSync - sync transaction */ - rtreeEndTransaction, /* xCommit - commit transaction */ -- rtreeEndTransaction, /* xRollback - rollback transaction */ -+ rtreeRollback, /* xRollback - rollback transaction */ - 0, /* xFindFunction - function overloading */ - rtreeRename, /* xRename - rename the table */ - rtreeSavepoint, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ -- rtreeShadowName /* xShadowName */ -+ rtreeShadowName, /* xShadowName */ -+ rtreeIntegrity /* xIntegrity */ - }; - - static int rtreeSqlInit( -@@ -191630,7 +216933,7 @@ static int rtreeSqlInit( - } - sqlite3_free(zSql); - } -- if( pRtree->nAux ){ -+ if( pRtree->nAux && rc!=SQLITE_NOMEM ){ - pRtree->zReadAuxSql = sqlite3_mprintf( - "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1", - zDb, zPrefix); -@@ -191643,9 +216946,12 @@ static int rtreeSqlInit( - sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix); - for(ii=0; iinAux; ii++){ - if( ii ) sqlite3_str_append(p, ",", 1); -+#ifdef SQLITE_ENABLE_GEOPOLY - if( iinAuxNotNull ){ - sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii); -- }else{ -+ }else -+#endif -+ { - sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2); - } - } -@@ -191790,22 +217096,27 @@ static int rtreeInit( - } - - sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); -+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); -+ - - /* Allocate the sqlite3_vtab structure */ - nDb = (int)strlen(argv[1]); - nName = (int)strlen(argv[2]); -- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); -+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8); - if( !pRtree ){ - return SQLITE_NOMEM; - } -- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); -+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8); - pRtree->nBusy = 1; - pRtree->base.pModule = &rtreeModule; - pRtree->zDb = (char *)&pRtree[1]; - pRtree->zName = &pRtree->zDb[nDb+1]; -+ pRtree->zNodeName = &pRtree->zName[nName+1]; - pRtree->eCoordType = (u8)eCoordType; - memcpy(pRtree->zDb, argv[1], nDb); - memcpy(pRtree->zName, argv[2], nName); -+ memcpy(pRtree->zNodeName, argv[2], nName); -+ memcpy(&pRtree->zNodeName[nName], "_node", 6); - - - /* Create/Connect to the underlying relational database schema. If -@@ -191910,6 +217221,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ - tree.nDim2 = tree.nDim*2; - tree.nBytesPerCell = 8 + 8 * tree.nDim; - node.zData = (u8 *)sqlite3_value_blob(apArg[1]); -+ if( node.zData==0 ) return; - nData = sqlite3_value_bytes(apArg[1]); - if( nData<4 ) return; - if( nDatadb, pRtree->zDb, pRtree->zName, pzErr); -+ if( rc==SQLITE_OK && *pzErr ){ -+ *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z", -+ pRtree->zDb, pRtree->zName, *pzErr); -+ if( (*pzErr)==0 ) rc = SQLITE_NOMEM; -+ } -+ return rc; -+} -+ - /* - ** Usage: - ** -@@ -192455,11 +217783,7 @@ static void rtreecheck( - # define GEODEBUG(X) - #endif - --#ifndef JSON_NULL /* The following stuff repeats things found in json1 */ --/* --** Versions of isspace(), isalnum() and isdigit() to which it is safe --** to pass signed char values. --*/ -+/* Character class routines */ - #ifdef sqlite3Isdigit - /* Use the SQLite core versions if this routine is part of the - ** SQLite amalgamation */ -@@ -192474,6 +217798,7 @@ static void rtreecheck( - # define safe_isxdigit(x) isxdigit((unsigned char)(x)) - #endif - -+#ifndef JSON_NULL /* The following stuff repeats things found in json1 */ - /* - ** Growing our own isspace() routine this way is twice as fast as - ** the library isspace() function. -@@ -192496,7 +217821,7 @@ static const char geopolyIsSpace[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; --#define safe_isspace(x) (geopolyIsSpace[(unsigned char)x]) -+#define fast_isspace(x) (geopolyIsSpace[(unsigned char)x]) - #endif /* JSON NULL - back to original code */ - - /* Compiler and version */ -@@ -192585,7 +217910,7 @@ static void geopolySwab32(unsigned char *a){ - - /* Skip whitespace. Return the next non-whitespace character. */ - static char geopolySkipSpace(GeoParse *p){ -- while( safe_isspace(p->z[0]) ) p->z++; -+ while( fast_isspace(p->z[0]) ) p->z++; - return p->z[0]; - } - -@@ -192734,11 +218059,16 @@ static GeoPoly *geopolyFuncParam( - ){ - GeoPoly *p = 0; - int nByte; -+ testcase( pCtx==0 ); - if( sqlite3_value_type(pVal)==SQLITE_BLOB -- && (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord)) -+ && (nByte = sqlite3_value_bytes(pVal))>=(int)(4+6*sizeof(GeoCoord)) - ){ - const unsigned char *a = sqlite3_value_blob(pVal); - int nVertex; -+ if( a==0 ){ -+ if( pCtx ) sqlite3_result_error_nomem(pCtx); -+ return 0; -+ } - nVertex = (a[1]<<16) + (a[2]<<8) + a[3]; - if( (a[0]==0 || a[0]==1) - && (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte -@@ -192789,6 +218119,7 @@ static void geopolyBlobFunc( - sqlite3_value **argv - ){ - GeoPoly *p = geopolyFuncParam(context, argv[0], 0); -+ (void)argc; - if( p ){ - sqlite3_result_blob(context, p->hdr, - 4+8*p->nVertex, SQLITE_TRANSIENT); -@@ -192808,6 +218139,7 @@ static void geopolyJsonFunc( - sqlite3_value **argv - ){ - GeoPoly *p = geopolyFuncParam(context, argv[0], 0); -+ (void)argc; - if( p ){ - sqlite3 *db = sqlite3_context_db_handle(context); - sqlite3_str *x = sqlite3_str_new(db); -@@ -192889,6 +218221,7 @@ static void geopolyXformFunc( - double F = sqlite3_value_double(argv[6]); - GeoCoord x1, y1, x0, y0; - int ii; -+ (void)argc; - if( p ){ - for(ii=0; iinVertex; ii++){ - x0 = GeoX(p,ii); -@@ -192939,6 +218272,7 @@ static void geopolyAreaFunc( - sqlite3_value **argv - ){ - GeoPoly *p = geopolyFuncParam(context, argv[0], 0); -+ (void)argc; - if( p ){ - sqlite3_result_double(context, geopolyArea(p)); - sqlite3_free(p); -@@ -192964,6 +218298,7 @@ static void geopolyCcwFunc( - sqlite3_value **argv - ){ - GeoPoly *p = geopolyFuncParam(context, argv[0], 0); -+ (void)argc; - if( p ){ - if( geopolyArea(p)<0.0 ){ - int ii, jj; -@@ -193018,6 +218353,7 @@ static void geopolyRegularFunc( - int n = sqlite3_value_int(argv[3]); - int i; - GeoPoly *p; -+ (void)argc; - - if( n<3 || r<=0.0 ) return; - if( n>1000 ) n = 1000; -@@ -193112,7 +218448,7 @@ static GeoPoly *geopolyBBox( - aCoord[2].f = mnY; - aCoord[3].f = mxY; - } -- }else{ -+ }else if( aCoord ){ - memset(aCoord, 0, sizeof(RtreeCoord)*4); - } - return pOut; -@@ -193127,6 +218463,7 @@ static void geopolyBBoxFunc( - sqlite3_value **argv - ){ - GeoPoly *p = geopolyBBox(context, argv[0], 0, 0); -+ (void)argc; - if( p ){ - sqlite3_result_blob(context, p->hdr, - 4+8*p->nVertex, SQLITE_TRANSIENT); -@@ -193154,6 +218491,7 @@ static void geopolyBBoxStep( - ){ - RtreeCoord a[4]; - int rc = SQLITE_OK; -+ (void)argc; - (void)geopolyBBox(context, argv[0], a, &rc); - if( rc==SQLITE_OK ){ - GeoBBox *pBBox; -@@ -193190,7 +218528,7 @@ static void geopolyBBoxFinal( - ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). - ** Returns: - ** --** +2 x0,y0 is on the line segement -+** +2 x0,y0 is on the line segment - ** - ** +1 x0,y0 is beneath line segment - ** -@@ -193242,6 +218580,8 @@ static void geopolyContainsPointFunc( - int v = 0; - int cnt = 0; - int ii; -+ (void)argc; -+ - if( p1==0 ) return; - for(ii=0; iinVertex-1; ii++){ - v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii), -@@ -193281,6 +218621,7 @@ static void geopolyWithinFunc( - ){ - GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0); - GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0); -+ (void)argc; - if( p1 && p2 ){ - int x = geopolyOverlap(p1, p2); - if( x<0 ){ -@@ -193293,7 +218634,7 @@ static void geopolyWithinFunc( - sqlite3_free(p2); - } - --/* Objects used by the overlap algorihm. */ -+/* Objects used by the overlap algorithm. */ - typedef struct GeoEvent GeoEvent; - typedef struct GeoSegment GeoSegment; - typedef struct GeoOverlap GeoOverlap; -@@ -193504,7 +218845,7 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ - geopolyAddSegments(p, p1, 1); - geopolyAddSegments(p, p2, 2); - pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); -- rX = pThisEvent->x==0.0 ? -1.0 : 0.0; -+ rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0; - memset(aOverlap, 0, sizeof(aOverlap)); - while( pThisEvent ){ - if( pThisEvent->x!=rX ){ -@@ -193563,11 +218904,11 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ - }else{ - /* Remove a segment */ - if( pActive==pThisEvent->pSeg ){ -- pActive = pActive->pNext; -+ pActive = ALWAYS(pActive) ? pActive->pNext : 0; - }else{ - for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){ - if( pSeg->pNext==pThisEvent->pSeg ){ -- pSeg->pNext = pSeg->pNext->pNext; -+ pSeg->pNext = ALWAYS(pSeg->pNext) ? pSeg->pNext->pNext : 0; - break; - } - } -@@ -193611,6 +218952,7 @@ static void geopolyOverlapFunc( - ){ - GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0); - GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0); -+ (void)argc; - if( p1 && p2 ){ - int x = geopolyOverlap(p1, p2); - if( x<0 ){ -@@ -193631,8 +218973,12 @@ static void geopolyDebugFunc( - int argc, - sqlite3_value **argv - ){ -+ (void)context; -+ (void)argc; - #ifdef GEOPOLY_ENABLE_DEBUG - geo_debug = sqlite3_value_int(argv[0]); -+#else -+ (void)argv; - #endif - } - -@@ -193660,26 +219006,31 @@ static int geopolyInit( - sqlite3_str *pSql; - char *zSql; - int ii; -+ (void)pAux; - - sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); -+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); - - /* Allocate the sqlite3_vtab structure */ - nDb = strlen(argv[1]); - nName = strlen(argv[2]); -- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); -+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8); - if( !pRtree ){ - return SQLITE_NOMEM; - } -- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); -+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8); - pRtree->nBusy = 1; - pRtree->base.pModule = &rtreeModule; - pRtree->zDb = (char *)&pRtree[1]; - pRtree->zName = &pRtree->zDb[nDb+1]; -+ pRtree->zNodeName = &pRtree->zName[nName+1]; - pRtree->eCoordType = RTREE_COORD_REAL32; - pRtree->nDim = 2; - pRtree->nDim2 = 4; - memcpy(pRtree->zDb, argv[1], nDb); - memcpy(pRtree->zName, argv[2], nName); -+ memcpy(pRtree->zNodeName, argv[2], nName); -+ memcpy(&pRtree->zNodeName[nName], "_node", 6); - - - /* Create/Connect to the underlying relational database schema. If -@@ -193776,6 +219127,7 @@ static int geopolyFilter( - RtreeNode *pRoot = 0; - int rc = SQLITE_OK; - int iCell = 0; -+ (void)idxStr; - - rtreeReference(pRtree); - -@@ -193811,6 +219163,7 @@ static int geopolyFilter( - RtreeCoord bbox[4]; - RtreeConstraint *p; - assert( argc==1 ); -+ assert( argv[0]!=0 ); - geopolyBBox(0, argv[0], bbox, &rc); - if( rc ){ - goto geopoly_filter_end; -@@ -193901,6 +219254,7 @@ static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int iRowidTerm = -1; - int iFuncTerm = -1; - int idxNum = 0; -+ (void)tab; - - for(ii=0; iinConstraint; ii++){ - struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; -@@ -194038,6 +219392,7 @@ static int geopolyUpdate( - || !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */ - || oldRowid!=newRowid) /* Rowid change */ - ){ -+ assert( aData[2]!=0 ); - geopolyBBox(0, aData[2], cell.aCoord, &rc); - if( rc ){ - if( rc==SQLITE_ERROR ){ -@@ -194089,7 +219444,6 @@ static int geopolyUpdate( - } - if( rc==SQLITE_OK ){ - int rc2; -- pRtree->iReinsertHeight = -1; - rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); - rc2 = nodeRelease(pRtree, pLeaf); - if( rc==SQLITE_OK ){ -@@ -194120,7 +219474,7 @@ static int geopolyUpdate( - sqlite3_free(p); - nChange = 1; - } -- for(jj=1; jjnAux; jj++){ -+ for(jj=1; jjxGeom = 0; - pGeomCtx->xQueryFunc = xQueryFunc; - pGeomCtx->xDestructor = xDestructor; -@@ -194401,7 +219760,7 @@ SQLITE_API int sqlite3_rtree_query_callback( - ); - } - --#if !SQLITE_CORE -+#ifndef SQLITE_CORE - #ifdef _WIN32 - __declspec(dllexport) - #endif -@@ -194720,8 +220079,9 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){ - - if( U_SUCCESS(status) ){ - sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete); -- }else{ -- assert(!pExpr); -+ pExpr = sqlite3_get_auxdata(p, 0); -+ } -+ if( !pExpr ){ - icuFunctionError(p, "uregex_open", status); - return; - } -@@ -194891,7 +220251,7 @@ static void icuLoadCollation( - UCollator *pUCollator; /* ICU library collation object */ - int rc; /* Return code from sqlite3_create_collation_x() */ - -- assert(nArg==2); -+ assert(nArg==2 || nArg==3); - (void)nArg; /* Unused parameter */ - zLocale = (const char *)sqlite3_value_text(apArg[0]); - zName = (const char *)sqlite3_value_text(apArg[1]); -@@ -194906,7 +220266,39 @@ static void icuLoadCollation( - return; - } - assert(p); -- -+ if(nArg==3){ -+ const char *zOption = (const char*)sqlite3_value_text(apArg[2]); -+ static const struct { -+ const char *zName; -+ UColAttributeValue val; -+ } aStrength[] = { -+ { "PRIMARY", UCOL_PRIMARY }, -+ { "SECONDARY", UCOL_SECONDARY }, -+ { "TERTIARY", UCOL_TERTIARY }, -+ { "DEFAULT", UCOL_DEFAULT_STRENGTH }, -+ { "QUARTERNARY", UCOL_QUATERNARY }, -+ { "IDENTICAL", UCOL_IDENTICAL }, -+ }; -+ unsigned int i; -+ for(i=0; i=sizeof(aStrength)/sizeof(aStrength[0]) ){ -+ sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p)); -+ sqlite3_str_appendf(pStr, -+ "unknown collation strength \"%s\" - should be one of:", -+ zOption); -+ for(i=0; i naming scheme. -+** tables or views named using the data_ naming scheme. - ** - ** Instead of the plain data_ naming scheme, RBU database tables - ** may also be named data_, where is any sequence -@@ -195445,7 +220838,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule( - ** - ** If the target database table is a virtual table or a table that has no - ** PRIMARY KEY declaration, the data_% table must also contain a column --** named "rbu_rowid". This column is mapped to the tables implicit primary -+** named "rbu_rowid". This column is mapped to the table's implicit primary - ** key column - "rowid". Virtual tables for which the "rowid" column does - ** not function like a primary key value cannot be updated using RBU. For - ** example, if the target db contains either of the following: -@@ -195680,7 +221073,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open( - ** the next call to sqlite3rbu_vacuum() opens a handle that starts a - ** new RBU vacuum operation. - ** --** As with sqlite3rbu_open(), Zipvfs users should rever to the comment -+** As with sqlite3rbu_open(), Zipvfs users should refer to the comment - ** describing the sqlite3rbu_create_vfs() API function below for - ** a description of the complications associated with using RBU with - ** zipvfs databases. -@@ -195776,7 +221169,7 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu); - ** - ** If the RBU update has been completely applied, mark the RBU database - ** as fully applied. Otherwise, assuming no error has occurred, save the --** current state of the RBU update appliation to the RBU database. -+** current state of the RBU update application to the RBU database. - ** - ** If an error has already occurred as part of an sqlite3rbu_step() - ** or sqlite3rbu_open() call, or if one occurs within this function, an -@@ -195878,6 +221271,34 @@ SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo); - - SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu); - -+/* -+** As part of applying an RBU update or performing an RBU vacuum operation, -+** the system must at one point move the *-oal file to the equivalent *-wal -+** path. Normally, it does this by invoking POSIX function rename(2) directly. -+** Except on WINCE platforms, where it uses win32 API MoveFileW(). This -+** function may be used to register a callback that the RBU module will invoke -+** instead of one of these APIs. -+** -+** If a callback is registered with an RBU handle, it invokes it instead -+** of rename(2) when it needs to move a file within the file-system. The -+** first argument passed to the xRename() callback is a copy of the second -+** argument (pArg) passed to this function. The second is the full path -+** to the file to move and the third the full path to which it should be -+** moved. The callback function should return SQLITE_OK to indicate -+** success. If an error occurs, it should return an SQLite error code. -+** In this case the RBU operation will be abandoned and the error returned -+** to the RBU user. -+** -+** Passing a NULL pointer in place of the xRename argument to this function -+** restores the default behaviour. -+*/ -+SQLITE_API void sqlite3rbu_rename_handler( -+ sqlite3rbu *pRbu, -+ void *pArg, -+ int (*xRename)(void *pArg, const char *zOld, const char *zNew) -+); -+ -+ - /* - ** Create an RBU VFS named zName that accesses the underlying file-system - ** via existing VFS zParent. Or, if the zParent parameter is passed NULL, -@@ -195962,6 +221383,13 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName); - # define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;} - #endif - -+/* -+** Name of the URI option that causes RBU to take an exclusive lock as -+** part of the incremental checkpoint operation. -+*/ -+#define RBU_EXCLUSIVE_CHECKPOINT "rbu_exclusive_checkpoint" -+ -+ - /* - ** The rbu_state table is used to save the state of a partially applied - ** update so that it can be resumed later. The table consists of integer -@@ -196044,6 +221472,7 @@ typedef unsigned int u32; - typedef unsigned short u16; - typedef unsigned char u8; - typedef sqlite3_int64 i64; -+typedef sqlite3_uint64 u64; - #endif - - /* -@@ -196180,6 +221609,27 @@ struct RbuFrame { - u32 iWalFrame; - }; - -+#ifndef UNUSED_PARAMETER -+/* -+** The following macros are used to suppress compiler warnings and to -+** make it clear to human readers when a function parameter is deliberately -+** left unused within the body of a function. This usually happens when -+** a function is called via a function pointer. For example the -+** implementation of an SQL aggregate step callback may not use the -+** parameter indicating the number of arguments passed to the aggregate, -+** if it knows that this is enforced elsewhere. -+** -+** When a function parameter is not used at all within the body of a function, -+** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. -+** However, these macros may also be used to suppress warnings related to -+** parameters that may or may not be used depending on compilation options. -+** For example those parameters only used in assert() statements. In these -+** cases the parameters are named as per the usual conventions. -+*/ -+#define UNUSED_PARAMETER(x) (void)(x) -+#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) -+#endif -+ - /* - ** RBU handle. - ** -@@ -196231,13 +221681,15 @@ struct sqlite3rbu { - int rc; /* Value returned by last rbu_step() call */ - char *zErrmsg; /* Error message if rc!=SQLITE_OK */ - int nStep; /* Rows processed for current object */ -- int nProgress; /* Rows processed for all objects */ -+ sqlite3_int64 nProgress; /* Rows processed for all objects */ - RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ - const char *zVfsName; /* Name of automatically created rbu vfs */ - rbu_file *pTargetFd; /* File handle open on target db */ - int nPagePerSector; /* Pages per sector for pTargetFd */ - i64 iOalSz; - i64 nPhaseOneStep; -+ void *pRenameArg; -+ int (*xRename)(void*, const char*, const char*); - - /* The following state variables are used as part of the incremental - ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding -@@ -196346,7 +221798,7 @@ static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){ - v = (v<<6) + c; - } - z--; -- *pLen -= z - zStart; -+ *pLen -= (int)(z - zStart); - *pz = (char*)z; - return v; - } -@@ -196531,6 +221983,7 @@ static void rbuFossilDeltaFunc( - char *aOut; - - assert( argc==2 ); -+ UNUSED_PARAMETER(argc); - - nOrig = sqlite3_value_bytes(argv[0]); - aOrig = (const char*)sqlite3_value_blob(argv[0]); -@@ -196728,6 +222181,7 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){ - if( rc!=SQLITE_ROW ){ - rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg); - pIter->zTbl = 0; -+ pIter->zDataTbl = 0; - }else{ - pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); - pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1); -@@ -197046,7 +222500,9 @@ static void rbuTableType( - assert( p->rc==SQLITE_OK ); - p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg, - sqlite3_mprintf( -- "SELECT (sql LIKE 'create virtual%%'), rootpage" -+ "SELECT " -+ " (sql COLLATE nocase BETWEEN 'CREATE VIRTUAL' AND 'CREATE VIRTUAM')," -+ " rootpage" - " FROM sqlite_schema" - " WHERE name=%Q", zTab - )); -@@ -197406,7 +222862,7 @@ static char *rbuVacuumTableStart( - ** the caller has to use an OFFSET clause to extract only the required - ** rows from the sourct table, just as it does for an RBU update operation. - */ --char *rbuVacuumIndexStart( -+static char *rbuVacuumIndexStart( - sqlite3rbu *p, /* RBU handle */ - RbuObjIter *pIter /* RBU iterator object */ - ){ -@@ -197472,7 +222928,9 @@ char *rbuVacuumIndexStart( - zSep = ""; - for(iCol=0; iColnCol; iCol++){ - const char *zQuoted = (const char*)sqlite3_column_text(pSel, iCol); -- if( zQuoted[0]=='N' ){ -+ if( zQuoted==0 ){ -+ p->rc = SQLITE_NOMEM; -+ }else if( zQuoted[0]=='N' ){ - bFailed = 1; - break; - } -@@ -198105,13 +223563,13 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){ - else if( c==')' ){ - nParen--; - if( nParen==0 ){ -- int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; -+ int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); - pIter->aIdxCol[iIdxCol++].nSpan = nSpan; - i++; - break; - } - }else if( c==',' && nParen==1 ){ -- int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; -+ int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); - pIter->aIdxCol[iIdxCol++].nSpan = nSpan; - pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1]; - }else if( c=='"' || c=='\'' || c=='`' ){ -@@ -198577,7 +224035,7 @@ static RbuState *rbuLoadState(sqlite3rbu *p){ - break; - - case RBU_STATE_OALSZ: -- pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1); -+ pRet->iOalSz = sqlite3_column_int64(pStmt, 1); - break; - - case RBU_STATE_PHASEONESTEP: -@@ -198604,19 +224062,25 @@ static RbuState *rbuLoadState(sqlite3rbu *p){ - /* - ** Open the database handle and attach the RBU database as "rbu". If an - ** error occurs, leave an error code and message in the RBU handle. -+** -+** If argument dbMain is not NULL, then it is a database handle already -+** open on the target database. Use this handle instead of opening a new -+** one. - */ --static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){ -+static void rbuOpenDatabase(sqlite3rbu *p, sqlite3 *dbMain, int *pbRetry){ - assert( p->rc || (p->dbMain==0 && p->dbRbu==0) ); - assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 ); -+ assert( dbMain==0 || rbuIsVacuum(p)==0 ); - - /* Open the RBU database */ - p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1); -+ p->dbMain = dbMain; - - if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){ - sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p); - if( p->zState==0 ){ - const char *zFile = sqlite3_db_filename(p->dbRbu, "main"); -- p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile); -+ p->zState = rbuMPrintf(p, "file:///%s-vacuum?modeof=%s", zFile, zFile); - } - } - -@@ -198795,6 +224259,8 @@ static void rbuFileSuffix3(const char *zBase, char *z){ - for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} - if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4); - } -+#else -+ UNUSED_PARAMETER2(zBase,z); - #endif - } - -@@ -198812,7 +224278,7 @@ static i64 rbuShmChecksum(sqlite3rbu *p){ - u32 volatile *ptr; - p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr); - if( p->rc==SQLITE_OK ){ -- iRet = ((i64)ptr[10] << 32) + ptr[11]; -+ iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]); - } - } - return iRet; -@@ -198864,11 +224330,11 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){ - ** no-ops. These locks will not be released until the connection - ** is closed. - ** -- ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL -+ ** * Attempting to xSync() the database file causes an SQLITE_NOTICE - ** error. - ** - ** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the -- ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[] -+ ** checkpoint below fails with SQLITE_NOTICE, and leaves the aFrame[] - ** array populated with a set of (frame -> page) mappings. Because the - ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy - ** data from the wal file into the database file according to the -@@ -198878,7 +224344,7 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){ - int rc2; - p->eStage = RBU_STAGE_CAPTURE; - rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0); -- if( rc2!=SQLITE_INTERNAL ) p->rc = rc2; -+ if( rc2!=SQLITE_NOTICE ) p->rc = rc2; - } - - if( p->rc==SQLITE_OK && p->nFrame>0 ){ -@@ -198924,7 +224390,7 @@ static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){ - - if( pRbu->mLock!=mReq ){ - pRbu->rc = SQLITE_BUSY; -- return SQLITE_INTERNAL; -+ return SQLITE_NOTICE_RBU; - } - - pRbu->pgsz = iAmt; -@@ -198974,17 +224440,49 @@ static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){ - p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff); - } - -+/* -+** This value is copied from the definition of ZIPVFS_CTRL_FILE_POINTER -+** in zipvfs.h. -+*/ -+#define RBU_ZIPVFS_CTRL_FILE_POINTER 230439 - - /* --** Take an EXCLUSIVE lock on the database file. -+** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if -+** successful, or an SQLite error code otherwise. - */ --static void rbuLockDatabase(sqlite3rbu *p){ -- sqlite3_file *pReal = p->pTargetFd->pReal; -- assert( p->rc==SQLITE_OK ); -- p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED); -- if( p->rc==SQLITE_OK ){ -- p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE); -+static int rbuLockDatabase(sqlite3 *db){ -+ int rc = SQLITE_OK; -+ sqlite3_file *fd = 0; -+ -+ sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); -+ if( fd ){ -+ sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); -+ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); -+ if( rc==SQLITE_OK ){ -+ rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE); -+ } -+ sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); -+ }else{ -+ sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); -+ } -+ -+ if( rc==SQLITE_OK && fd->pMethods ){ -+ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); -+ if( rc==SQLITE_OK ){ -+ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); -+ } - } -+ return rc; -+} -+ -+/* -+** Return true if the database handle passed as the only argument -+** was opened with the rbu_exclusive_checkpoint=1 URI parameter -+** specified. Or false otherwise. -+*/ -+static int rbuExclusiveCheckpoint(sqlite3 *db){ -+ const char *zUri = sqlite3_db_filename(db, 0); -+ return sqlite3_uri_boolean(zUri, RBU_EXCLUSIVE_CHECKPOINT, 0); - } - - #if defined(_WIN32_WCE) -@@ -199042,49 +224540,38 @@ static void rbuMoveOalFile(sqlite3rbu *p){ - ** In order to ensure that there are no database readers, an EXCLUSIVE - ** lock is obtained here before the *-oal is moved to *-wal. - */ -- rbuLockDatabase(p); -- if( p->rc==SQLITE_OK ){ -- rbuFileSuffix3(zBase, zWal); -- rbuFileSuffix3(zBase, zOal); -+ sqlite3 *dbMain = 0; -+ rbuFileSuffix3(zBase, zWal); -+ rbuFileSuffix3(zBase, zOal); - -- /* Re-open the databases. */ -- rbuObjIterFinalize(&p->objiter); -- sqlite3_close(p->dbRbu); -- sqlite3_close(p->dbMain); -- p->dbMain = 0; -- p->dbRbu = 0; -+ /* Re-open the databases. */ -+ rbuObjIterFinalize(&p->objiter); -+ sqlite3_close(p->dbRbu); -+ sqlite3_close(p->dbMain); -+ p->dbMain = 0; -+ p->dbRbu = 0; - --#if defined(_WIN32_WCE) -- { -- LPWSTR zWideOal; -- LPWSTR zWideWal; -- -- zWideOal = rbuWinUtf8ToUnicode(zOal); -- if( zWideOal ){ -- zWideWal = rbuWinUtf8ToUnicode(zWal); -- if( zWideWal ){ -- if( MoveFileW(zWideOal, zWideWal) ){ -- p->rc = SQLITE_OK; -- }else{ -- p->rc = SQLITE_IOERR; -- } -- sqlite3_free(zWideWal); -- }else{ -- p->rc = SQLITE_IOERR_NOMEM; -- } -- sqlite3_free(zWideOal); -- }else{ -- p->rc = SQLITE_IOERR_NOMEM; -- } -- } --#else -- p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; --#endif -+ dbMain = rbuOpenDbhandle(p, p->zTarget, 1); -+ if( dbMain ){ -+ assert( p->rc==SQLITE_OK ); -+ p->rc = rbuLockDatabase(dbMain); -+ } - -- if( p->rc==SQLITE_OK ){ -- rbuOpenDatabase(p, 0); -- rbuSetupCheckpoint(p, 0); -- } -+ if( p->rc==SQLITE_OK ){ -+ p->rc = p->xRename(p->pRenameArg, zOal, zWal); -+ } -+ -+ if( p->rc!=SQLITE_OK -+ || rbuIsVacuum(p) -+ || rbuExclusiveCheckpoint(dbMain)==0 -+ ){ -+ sqlite3_close(dbMain); -+ dbMain = 0; -+ } -+ -+ if( p->rc==SQLITE_OK ){ -+ rbuOpenDatabase(p, dbMain, 0); -+ rbuSetupCheckpoint(p, 0); - } - } - -@@ -199358,7 +224845,7 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){ - "(%d, %Q), " - "(%d, %Q), " - "(%d, %d), " -- "(%d, %d), " -+ "(%d, %lld), " - "(%d, %lld), " - "(%d, %lld), " - "(%d, %lld), " -@@ -199658,7 +225145,8 @@ static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){ - static void rbuDeleteOalFile(sqlite3rbu *p){ - char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget); - if( zOal ){ -- sqlite3_vfs *pVfs = sqlite3_vfs_find(0); -+ sqlite3_vfs *pVfs = 0; -+ sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_VFS_POINTER, &pVfs); - assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 ); - pVfs->xDelete(pVfs, zOal, 0); - sqlite3_free(zOal); -@@ -199715,6 +225203,7 @@ static void rbuIndexCntFunc( - sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain); - - assert( nVal==1 ); -+ UNUSED_PARAMETER(nVal); - - rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg, - sqlite3_mprintf("SELECT count(*) FROM sqlite_schema " -@@ -199810,6 +225299,7 @@ static sqlite3rbu *openRbuHandle( - - /* Create the custom VFS. */ - memset(p, 0, sizeof(sqlite3rbu)); -+ sqlite3rbu_rename_handler(p, 0, 0); - rbuCreateVfs(p); - - /* Open the target, RBU and state databases */ -@@ -199835,9 +225325,9 @@ static sqlite3rbu *openRbuHandle( - ** If this is the case, it will have been checkpointed and deleted - ** when the handle was closed and a second attempt to open the - ** database may succeed. */ -- rbuOpenDatabase(p, &bRetry); -+ rbuOpenDatabase(p, 0, &bRetry); - if( bRetry ){ -- rbuOpenDatabase(p, 0); -+ rbuOpenDatabase(p, 0, 0); - } - } - -@@ -199932,6 +225422,14 @@ static sqlite3rbu *openRbuHandle( - }else if( p->eStage==RBU_STAGE_MOVE ){ - /* no-op */ - }else if( p->eStage==RBU_STAGE_CKPT ){ -+ if( !rbuIsVacuum(p) && rbuExclusiveCheckpoint(p->dbMain) ){ -+ /* If the rbu_exclusive_checkpoint=1 URI parameter was specified -+ ** and an incremental checkpoint is being resumed, attempt an -+ ** exclusive lock on the db file. If this fails, so be it. */ -+ p->eStage = RBU_STAGE_DONE; -+ rbuLockDatabase(p->dbMain); -+ p->eStage = RBU_STAGE_CKPT; -+ } - rbuSetupCheckpoint(p, pState); - }else if( p->eStage==RBU_STAGE_DONE ){ - p->rc = SQLITE_DONE; -@@ -199969,7 +225467,6 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open( - const char *zState - ){ - if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); } -- /* TODO: Check that zTarget and zRbu are non-NULL */ - return openRbuHandle(zTarget, zRbu, zState); - } - -@@ -199982,7 +225479,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( - ){ - if( zTarget==0 ){ return rbuMisuseError(); } - if( zState ){ -- int n = strlen(zState); -+ size_t n = strlen(zState); - if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ - return rbuMisuseError(); - } -@@ -200194,6 +225691,55 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){ - return rc; - } - -+/* -+** Default xRename callback for RBU. -+*/ -+static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){ -+ int rc = SQLITE_OK; -+ UNUSED_PARAMETER(pArg); -+#if defined(_WIN32_WCE) -+ { -+ LPWSTR zWideOld; -+ LPWSTR zWideNew; -+ -+ zWideOld = rbuWinUtf8ToUnicode(zOld); -+ if( zWideOld ){ -+ zWideNew = rbuWinUtf8ToUnicode(zNew); -+ if( zWideNew ){ -+ if( MoveFileW(zWideOld, zWideNew) ){ -+ rc = SQLITE_OK; -+ }else{ -+ rc = SQLITE_IOERR; -+ } -+ sqlite3_free(zWideNew); -+ }else{ -+ rc = SQLITE_IOERR_NOMEM; -+ } -+ sqlite3_free(zWideOld); -+ }else{ -+ rc = SQLITE_IOERR_NOMEM; -+ } -+ } -+#else -+ rc = rename(zOld, zNew) ? SQLITE_IOERR : SQLITE_OK; -+#endif -+ return rc; -+} -+ -+SQLITE_API void sqlite3rbu_rename_handler( -+ sqlite3rbu *pRbu, -+ void *pArg, -+ int (*xRename)(void *pArg, const char *zOld, const char *zNew) -+){ -+ if( xRename ){ -+ pRbu->xRename = xRename; -+ pRbu->pRenameArg = pArg; -+ }else{ -+ pRbu->xRename = xDefaultRename; -+ pRbu->pRenameArg = 0; -+ } -+} -+ - /************************************************************************** - ** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour - ** of a standard VFS in the following ways: -@@ -200250,7 +225796,7 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){ - ** database file are recorded. xShmLock() calls to unlock the same - ** locks are no-ops (so that once obtained, these locks are never - ** relinquished). Finally, calls to xSync() on the target database --** file fail with SQLITE_INTERNAL errors. -+** file fail with SQLITE_NOTICE errors. - */ - - static void rbuUnlockShm(rbu_file *p){ -@@ -200359,9 +225905,12 @@ static int rbuVfsClose(sqlite3_file *pFile){ - sqlite3_free(p->zDel); - - if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ -+ const sqlite3_io_methods *pMeth = p->pReal->pMethods; - rbuMainlistRemove(p); - rbuUnlockShm(p); -- p->pReal->pMethods->xShmUnmap(p->pReal, 0); -+ if( pMeth->iVersion>1 && pMeth->xShmUnmap ){ -+ pMeth->xShmUnmap(p->pReal, 0); -+ } - } - else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){ - rbuUpdateTempSize(p, 0); -@@ -200529,7 +226078,7 @@ static int rbuVfsSync(sqlite3_file *pFile, int flags){ - rbu_file *p = (rbu_file *)pFile; - if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){ - if( p->openFlags & SQLITE_OPEN_MAIN_DB ){ -- return SQLITE_INTERNAL; -+ return SQLITE_NOTICE_RBU; - } - return SQLITE_OK; - } -@@ -200546,7 +226095,7 @@ static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ - - /* If this is an RBU vacuum operation and this is the target database, - ** pretend that it has at least one page. Otherwise, SQLite will not -- ** check for the existance of a *-wal file. rbuVfsRead() contains -+ ** check for the existence of a *-wal file. rbuVfsRead() contains - ** similar logic. */ - if( rc==SQLITE_OK && *pSize==0 - && p->pRbu && rbuIsVacuum(p->pRbu) -@@ -200680,22 +226229,24 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ - #endif - - assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); -- if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){ -- /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from -- ** taking this lock also prevents any checkpoints from occurring. -- ** todo: really, it's not clear why this might occur, as -- ** wal_autocheckpoint ought to be turned off. */ -+ if( pRbu && ( -+ pRbu->eStage==RBU_STAGE_OAL -+ || pRbu->eStage==RBU_STAGE_MOVE -+ || pRbu->eStage==RBU_STAGE_DONE -+ )){ -+ /* Prevent SQLite from taking a shm-lock on the target file when it -+ ** is supplying heap memory to the upper layer in place of *-shm -+ ** segments. */ - if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY; - }else{ - int bCapture = 0; - if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){ - bCapture = 1; - } -- - if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){ - rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); - if( bCapture && rc==SQLITE_OK ){ -- pRbu->mLock |= (1 << ofst); -+ pRbu->mLock |= ((1<pRealVfs; - rbu_file *pFd = (rbu_file *)pFile; -@@ -200842,28 +226412,14 @@ static int rbuVfsOpen( - rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0); - if( pDb ){ - if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ -- /* This call is to open a *-wal file. Intead, open the *-oal. This -- ** code ensures that the string passed to xOpen() is terminated by a -- ** pair of '\0' bytes in case the VFS attempts to extract a URI -- ** parameter from it. */ -- const char *zBase = zName; -- size_t nCopy; -- char *zCopy; -+ /* This call is to open a *-wal file. Intead, open the *-oal. */ -+ size_t nOpen; - if( rbuIsVacuum(pDb->pRbu) ){ -- zBase = sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); -- zBase = sqlite3_filename_wal(zBase); -- } -- nCopy = strlen(zBase); -- zCopy = sqlite3_malloc64(nCopy+2); -- if( zCopy ){ -- memcpy(zCopy, zBase, nCopy); -- zCopy[nCopy-3] = 'o'; -- zCopy[nCopy] = '\0'; -- zCopy[nCopy+1] = '\0'; -- zOpen = (const char*)(pFd->zDel = zCopy); -- }else{ -- rc = SQLITE_NOMEM; -+ zOpen = sqlite3_db_filename(pDb->pRbu->dbRbu, "main"); -+ zOpen = sqlite3_filename_wal(zOpen); - } -+ nOpen = strlen(zOpen); -+ ((char*)zOpen)[nOpen-3] = 'o'; - pFd->pRbu = pDb->pRbu; - } - pDb->pWalFd = pFd; -@@ -200886,10 +226442,15 @@ static int rbuVfsOpen( - rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags); - } - if( pFd->pReal->pMethods ){ -+ const sqlite3_io_methods *pMeth = pFd->pReal->pMethods; - /* The xOpen() operation has succeeded. Set the sqlite3_file.pMethods - ** pointer and, if the file is a main database file, link it into the - ** mutex protected linked list of all such files. */ -- pFile->pMethods = &rbuvfs_io_methods; -+ if( pMeth->iVersion<2 || pMeth->xShmLock==0 ){ -+ pFile->pMethods = &rbuvfs_io_methods1; -+ }else{ -+ pFile->pMethods = &rbuvfs_io_methods; -+ } - if( flags & SQLITE_OPEN_MAIN_DB ){ - rbuMainlistAdd(pFd); - } -@@ -201040,6 +226601,9 @@ static int rbuVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ - ** No-op. - */ - static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){ -+ UNUSED_PARAMETER(pVfs); -+ UNUSED_PARAMETER(a); -+ UNUSED_PARAMETER(b); - return 0; - } - -@@ -201184,6 +226748,15 @@ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){ - #if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \ - && !defined(SQLITE_OMIT_VIRTUALTABLE) - -+/* -+** The pager and btree modules arrange objects in memory so that there are -+** always approximately 200 bytes of addressable memory following each page -+** buffer. This way small buffer overreads caused by corrupt database pages -+** do not cause undefined behaviour. This module pads each page buffer -+** by the following number of bytes for the same purpose. -+*/ -+#define DBSTAT_PAGE_PADDING_BYTES 256 -+ - /* - ** Page paths: - ** -@@ -201251,9 +226824,8 @@ struct StatCell { - /* Size information for a single btree page */ - struct StatPage { - u32 iPgno; /* Page number */ -- DbPage *pPg; /* Page content */ -+ u8 *aPg; /* Page buffer from sqlite3_malloc() */ - int iCell; /* Current cell */ -- - char *zPath; /* Path to this page */ - - /* Variables populated by statDecodePage(): */ -@@ -201314,6 +226886,7 @@ static int statConnect( - StatTable *pTab = 0; - int rc = SQLITE_OK; - int iDb; -+ (void)pAux; - - if( argc>=4 ){ - Token nm; -@@ -201367,6 +226940,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int iSchema = -1; - int iName = -1; - int iAgg = -1; -+ (void)tab; - - /* Look for a valid schema=? constraint. If found, change the idxNum to - ** 1 and request the value of that constraint be sent to xFilter. And -@@ -201428,6 +227002,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - pIdxInfo->orderByConsumed = 1; - pIdxInfo->idxNum |= 0x08; - } -+ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_HEX; - - return SQLITE_OK; - } -@@ -201465,18 +227040,25 @@ static void statClearCells(StatPage *p){ - } - - static void statClearPage(StatPage *p){ -+ u8 *aPg = p->aPg; - statClearCells(p); -- sqlite3PagerUnref(p->pPg); - sqlite3_free(p->zPath); - memset(p, 0, sizeof(StatPage)); -+ p->aPg = aPg; - } - - static void statResetCsr(StatCursor *pCsr){ - int i; -- sqlite3_reset(pCsr->pStmt); -+ /* In some circumstances, specifically if an OOM has occurred, the call -+ ** to sqlite3_reset() may cause the pager to be reset (emptied). It is -+ ** important that statClearPage() is called to free any page refs before -+ ** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */ - for(i=0; iaPage); i++){ - statClearPage(&pCsr->aPage[i]); -+ sqlite3_free(pCsr->aPage[i].aPg); -+ pCsr->aPage[i].aPg = 0; - } -+ sqlite3_reset(pCsr->pStmt); - pCsr->iPage = 0; - sqlite3_free(pCsr->zPath); - pCsr->zPath = 0; -@@ -201541,7 +227123,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ - int isLeaf; - int szPage; - -- u8 *aData = sqlite3PagerGetData(p->pPg); -+ u8 *aData = p->aPg; - u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; - - p->flags = aHdr[0]; -@@ -201612,7 +227194,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ - if( nPayload>(u32)nLocal ){ - int j; - int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); -- if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){ -+ if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){ - goto statPageIsCorrupt; - } - pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); -@@ -201671,6 +227253,38 @@ static void statSizeAndOffset(StatCursor *pCsr){ - } - } - -+/* -+** Load a copy of the page data for page iPg into the buffer belonging -+** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK -+** if successful, or an SQLite error code otherwise. -+*/ -+static int statGetPage( -+ Btree *pBt, /* Load page from this b-tree */ -+ u32 iPg, /* Page number to load */ -+ StatPage *pPg /* Load page into this object */ -+){ -+ int pgsz = sqlite3BtreeGetPageSize(pBt); -+ DbPage *pDbPage = 0; -+ int rc; -+ -+ if( pPg->aPg==0 ){ -+ pPg->aPg = (u8*)sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES); -+ if( pPg->aPg==0 ){ -+ return SQLITE_NOMEM_BKPT; -+ } -+ memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES); -+ } -+ -+ rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPg, &pDbPage, 0); -+ if( rc==SQLITE_OK ){ -+ const u8 *a = sqlite3PagerGetData(pDbPage); -+ memcpy(pPg->aPg, a, pgsz); -+ sqlite3PagerUnref(pDbPage); -+ } -+ -+ return rc; -+} -+ - /* - ** Move a DBSTAT cursor to the next entry. Normally, the next - ** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0), -@@ -201689,7 +227303,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){ - pCsr->zPath = 0; - - statNextRestart: -- if( pCsr->aPage[0].pPg==0 ){ -+ if( pCsr->iPage<0 ){ - /* Start measuring space on the next btree */ - statResetCounts(pCsr); - rc = sqlite3_step(pCsr->pStmt); -@@ -201701,7 +227315,7 @@ statNextRestart: - pCsr->isEof = 1; - return sqlite3_reset(pCsr->pStmt); - } -- rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0); -+ rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]); - pCsr->aPage[0].iPgno = iRoot; - pCsr->aPage[0].iCell = 0; - if( !pCsr->isAgg ){ -@@ -201752,9 +227366,8 @@ statNextRestart: - - if( !p->iRightChildPg || p->iCell>p->nCell ){ - statClearPage(p); -- if( pCsr->iPage>0 ){ -- pCsr->iPage--; -- }else if( pCsr->isAgg ){ -+ pCsr->iPage--; -+ if( pCsr->isAgg && pCsr->iPage<0 ){ - /* label-statNext-done: When computing aggregate space usage over - ** an entire btree, this is the exit point from this function */ - return SQLITE_OK; -@@ -201773,7 +227386,7 @@ statNextRestart: - }else{ - p[1].iPgno = p->aCell[p->iCell].iChildPg; - } -- rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0); -+ rc = statGetPage(pBt, p[1].iPgno, &p[1]); - pCsr->nPage++; - p[1].iCell = 0; - if( !pCsr->isAgg ){ -@@ -201854,6 +227467,8 @@ static int statFilter( - int iArg = 0; /* Count of argv[] parameters used so far */ - int rc = SQLITE_OK; /* Result of this operation */ - const char *zName = 0; /* Only provide analysis of this table */ -+ (void)argc; -+ (void)idxStr; - - statResetCsr(pCsr); - sqlite3_finalize(pCsr->pStmt); -@@ -201903,6 +227518,7 @@ static int statFilter( - } - - if( rc==SQLITE_OK ){ -+ pCsr->iPage = -1; - rc = statNext(pCursor); - } - return rc; -@@ -201936,16 +227552,16 @@ static int statColumn( - } - break; - case 4: /* ncell */ -- sqlite3_result_int(ctx, pCsr->nCell); -+ sqlite3_result_int64(ctx, pCsr->nCell); - break; - case 5: /* payload */ -- sqlite3_result_int(ctx, pCsr->nPayload); -+ sqlite3_result_int64(ctx, pCsr->nPayload); - break; - case 6: /* unused */ -- sqlite3_result_int(ctx, pCsr->nUnused); -+ sqlite3_result_int64(ctx, pCsr->nUnused); - break; - case 7: /* mx_payload */ -- sqlite3_result_int(ctx, pCsr->nMxPayload); -+ sqlite3_result_int64(ctx, pCsr->nMxPayload); - break; - case 8: /* pgoffset */ - if( !pCsr->isAgg ){ -@@ -201953,7 +227569,7 @@ static int statColumn( - } - break; - case 9: /* pgsize */ -- sqlite3_result_int(ctx, pCsr->szPage); -+ sqlite3_result_int64(ctx, pCsr->szPage); - break; - case 10: { /* schema */ - sqlite3 *db = sqlite3_context_db_handle(ctx); -@@ -202003,7 +227619,8 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ -- 0 /* xShadowName */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); - } -@@ -202043,7 +227660,13 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; } - ** - ** The data field of sqlite_dbpage table can be updated. The new - ** value must be a BLOB which is the correct page size, otherwise the --** update fails. Rows may not be deleted or inserted. -+** update fails. INSERT operations also work, and operate as if they -+** where REPLACE. The size of the database can be extended by INSERT-ing -+** new pages on the end. -+** -+** Rows may not be deleted. However, doing an INSERT to page number N -+** with NULL page data causes the N-th page and all subsequent pages to be -+** deleted and the database to be truncated. - */ - - /* #include "sqliteInt.h" ** Requires access to internal data structures ** */ -@@ -202066,6 +227689,8 @@ struct DbpageCursor { - struct DbpageTable { - sqlite3_vtab base; /* Base class. Must be first */ - sqlite3 *db; /* The database */ -+ int iDbTrunc; /* Database to truncate */ -+ Pgno pgnoTrunc; /* Size to truncate to */ - }; - - /* Columns */ -@@ -202074,7 +227699,6 @@ struct DbpageTable { - #define DBPAGE_COLUMN_SCHEMA 2 - - -- - /* - ** Connect to or create a dbpagevfs virtual table. - */ -@@ -202087,8 +227711,13 @@ static int dbpageConnect( - ){ - DbpageTable *pTab = 0; - int rc = SQLITE_OK; -+ (void)pAux; -+ (void)argc; -+ (void)argv; -+ (void)pzErr; - - sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); -+ sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS); - rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)"); - if( rc==SQLITE_OK ){ -@@ -202125,6 +227754,7 @@ static int dbpageDisconnect(sqlite3_vtab *pVtab){ - static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int i; - int iPlan = 0; -+ (void)tab; - - /* If there is a schema= constraint, it must be honored. Report a - ** ridiculously large estimated cost if the schema= constraint is -@@ -202239,6 +227869,8 @@ static int dbpageFilter( - sqlite3 *db = pTab->db; - Btree *pBt; - -+ (void)idxStr; -+ - /* Default setting is no rows of result */ - pCsr->pgno = 1; - pCsr->mxPgno = 0; -@@ -202253,7 +227885,7 @@ static int dbpageFilter( - pCsr->iDb = 0; - } - pBt = db->aDb[pCsr->iDb].pBt; -- if( pBt==0 ) return SQLITE_OK; -+ if( NEVER(pBt==0) ) return SQLITE_OK; - pCsr->pPager = sqlite3BtreePager(pBt); - pCsr->szPage = sqlite3BtreeGetPageSize(pBt); - pCsr->mxPgno = sqlite3BtreeLastPage(pBt); -@@ -202288,12 +227920,18 @@ static int dbpageColumn( - } - case 1: { /* data */ - DbPage *pDbPage = 0; -- rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); -- if( rc==SQLITE_OK ){ -- sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage, -- SQLITE_TRANSIENT); -+ if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){ -+ /* The pending byte page. Assume it is zeroed out. Attempting to -+ ** request this page from the page is an SQLITE_CORRUPT error. */ -+ sqlite3_result_zeroblob(ctx, pCsr->szPage); -+ }else{ -+ rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); -+ if( rc==SQLITE_OK ){ -+ sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage, -+ SQLITE_TRANSIENT); -+ } -+ sqlite3PagerUnref(pDbPage); - } -- sqlite3PagerUnref(pDbPage); - break; - } - default: { /* schema */ -@@ -202302,7 +227940,7 @@ static int dbpageColumn( - break; - } - } -- return SQLITE_OK; -+ return rc; - } - - static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ -@@ -202311,6 +227949,24 @@ static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - return SQLITE_OK; - } - -+/* -+** Open write transactions. Since we do not know in advance which database -+** files will be written by the sqlite_dbpage virtual table, start a write -+** transaction on them all. -+** -+** Return SQLITE_OK if successful, or an SQLite error code otherwise. -+*/ -+static int dbpageBeginTrans(DbpageTable *pTab){ -+ sqlite3 *db = pTab->db; -+ int rc = SQLITE_OK; -+ int i; -+ for(i=0; rc==SQLITE_OK && inDb; i++){ -+ Btree *pBt = db->aDb[i].pBt; -+ if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0); -+ } -+ return rc; -+} -+ - static int dbpageUpdate( - sqlite3_vtab *pVtab, - int argc, -@@ -202322,12 +227978,13 @@ static int dbpageUpdate( - DbPage *pDbPage = 0; - int rc = SQLITE_OK; - char *zErr = 0; -- const char *zSchema; - int iDb; - Btree *pBt; - Pager *pPager; - int szPage; -+ int isInsert; - -+ (void)pRowid; - if( pTab->db->flags & SQLITE_Defensive ){ - zErr = "read-only"; - goto update_fail; -@@ -202336,19 +227993,29 @@ static int dbpageUpdate( - zErr = "cannot delete"; - goto update_fail; - } -- pgno = sqlite3_value_int(argv[0]); -- if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ -- zErr = "cannot insert"; -- goto update_fail; -+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ -+ pgno = (Pgno)sqlite3_value_int(argv[2]); -+ isInsert = 1; -+ }else{ -+ pgno = sqlite3_value_int(argv[0]); -+ if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ -+ zErr = "cannot insert"; -+ goto update_fail; -+ } -+ isInsert = 0; - } -- zSchema = (const char*)sqlite3_value_text(argv[4]); -- iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1; -- if( iDb<0 ){ -- zErr = "no such schema"; -- goto update_fail; -+ if( sqlite3_value_type(argv[4])==SQLITE_NULL ){ -+ iDb = 0; -+ }else{ -+ const char *zSchema = (const char*)sqlite3_value_text(argv[4]); -+ iDb = sqlite3FindDbName(pTab->db, zSchema); -+ if( iDb<0 ){ -+ zErr = "no such schema"; -+ goto update_fail; -+ } - } - pBt = pTab->db->aDb[iDb].pBt; -- if( pgno<1 || pBt==0 || pgno>(int)sqlite3BtreeLastPage(pBt) ){ -+ if( pgno<1 || NEVER(pBt==0) ){ - zErr = "bad page number"; - goto update_fail; - } -@@ -202356,50 +228023,83 @@ static int dbpageUpdate( - if( sqlite3_value_type(argv[3])!=SQLITE_BLOB - || sqlite3_value_bytes(argv[3])!=szPage - ){ -- zErr = "bad page value"; -+ if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){ -+ /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and -+ ** all subsequent pages to be deleted. */ -+ pTab->iDbTrunc = iDb; -+ pTab->pgnoTrunc = pgno-1; -+ pgno = 1; -+ }else{ -+ zErr = "bad page value"; -+ goto update_fail; -+ } -+ } -+ -+ if( dbpageBeginTrans(pTab)!=SQLITE_OK ){ -+ zErr = "failed to open transaction"; - goto update_fail; - } -+ - pPager = sqlite3BtreePager(pBt); - rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); - if( rc==SQLITE_OK ){ -- rc = sqlite3PagerWrite(pDbPage); -- if( rc==SQLITE_OK ){ -- memcpy(sqlite3PagerGetData(pDbPage), -- sqlite3_value_blob(argv[3]), -- szPage); -+ const void *pData = sqlite3_value_blob(argv[3]); -+ if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){ -+ unsigned char *aPage = sqlite3PagerGetData(pDbPage); -+ memcpy(aPage, pData, szPage); -+ pTab->pgnoTrunc = 0; - } -+ }else{ -+ pTab->pgnoTrunc = 0; - } - sqlite3PagerUnref(pDbPage); - return rc; - - update_fail: -+ pTab->pgnoTrunc = 0; - sqlite3_free(pVtab->zErrMsg); - pVtab->zErrMsg = sqlite3_mprintf("%s", zErr); - return SQLITE_ERROR; - } - --/* Since we do not know in advance which database files will be --** written by the sqlite_dbpage virtual table, start a write transaction --** on them all. --*/ - static int dbpageBegin(sqlite3_vtab *pVtab){ - DbpageTable *pTab = (DbpageTable *)pVtab; -- sqlite3 *db = pTab->db; -- int i; -- for(i=0; inDb; i++){ -- Btree *pBt = db->aDb[i].pBt; -- if( pBt ) sqlite3BtreeBeginTrans(pBt, 1, 0); -+ pTab->pgnoTrunc = 0; -+ return SQLITE_OK; -+} -+ -+/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT -+*/ -+static int dbpageSync(sqlite3_vtab *pVtab){ -+ DbpageTable *pTab = (DbpageTable *)pVtab; -+ if( pTab->pgnoTrunc>0 ){ -+ Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; -+ Pager *pPager = sqlite3BtreePager(pBt); -+ sqlite3BtreeEnter(pBt); -+ if( pTab->pgnoTruncpgnoTrunc); -+ } -+ sqlite3BtreeLeave(pBt); - } -+ pTab->pgnoTrunc = 0; - return SQLITE_OK; - } - -+/* Cancel any pending truncate. -+*/ -+static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){ -+ DbpageTable *pTab = (DbpageTable *)pVtab; -+ pTab->pgnoTrunc = 0; -+ (void)notUsed1; -+ return SQLITE_OK; -+} - - /* - ** Invoke this routine to register the "dbpage" virtual table module - */ - SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ - static sqlite3_module dbpage_module = { -- 0, /* iVersion */ -+ 2, /* iVersion */ - dbpageConnect, /* xCreate */ - dbpageConnect, /* xConnect */ - dbpageBestIndex, /* xBestIndex */ -@@ -202414,15 +228114,16 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ - dbpageRowid, /* xRowid - read data */ - dbpageUpdate, /* xUpdate */ - dbpageBegin, /* xBegin */ -- 0, /* xSync */ -+ dbpageSync, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ -- 0, /* xRollbackTo */ -- 0 /* xShadowName */ -+ dbpageRollbackTo, /* xRollbackTo */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); - } -@@ -202459,6 +228160,8 @@ typedef struct SessionInput SessionInput; - # endif - #endif - -+#define SESSIONS_ROWID "_rowid_" -+ - static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; - - typedef struct SessionHook SessionHook; -@@ -202476,12 +228179,16 @@ struct SessionHook { - struct sqlite3_session { - sqlite3 *db; /* Database handle session is attached to */ - char *zDb; /* Name of database session is attached to */ -+ int bEnableSize; /* True if changeset_size() enabled */ - int bEnable; /* True if currently recording */ - int bIndirect; /* True if all changes are indirect */ - int bAutoAttach; /* True to auto-attach tables */ -+ int bImplicitPK; /* True to handle tables with implicit PK */ - int rc; /* Non-zero if an error has occurred */ - void *pFilterCtx; /* First argument to pass to xTableFilter */ - int (*xTableFilter)(void *pCtx, const char *zTab); -+ i64 nMalloc; /* Number of bytes of data allocated */ -+ i64 nMaxChangesetSize; - sqlite3_value *pZeroBlob; /* Value containing X'' */ - sqlite3_session *pNext; /* Next session object on same db. */ - SessionTable *pTable; /* List of attached tables */ -@@ -202502,6 +228209,10 @@ struct SessionBuffer { - ** input data. Input data may be supplied either as a single large buffer - ** (e.g. sqlite3changeset_start()) or using a stream function (e.g. - ** sqlite3changeset_start_strm()). -+** -+** bNoDiscard: -+** If true, then the only time data is discarded is as a result of explicit -+** sessionDiscardData() calls. Not within every sessionInputBuffer() call. - */ - struct SessionInput { - int bNoDiscard; /* If true, do not discard in InputBuffer() */ -@@ -202524,6 +228235,7 @@ struct sqlite3_changeset_iter { - SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */ - int bPatchset; /* True if this is a patchset */ - int bInvert; /* True to invert changeset */ -+ int bSkipEmpty; /* Skip noop UPDATE changes */ - int rc; /* Iterator error code */ - sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ - char *zTab; /* Current table */ -@@ -202546,17 +228258,34 @@ struct sqlite3_changeset_iter { - ** The data associated with each hash-table entry is a structure containing - ** a subset of the initial values that the modified row contained at the - ** start of the session. Or no initial values if the row was inserted. -+** -+** pDfltStmt: -+** This is only used by the sqlite3changegroup_xxx() APIs, not by -+** regular sqlite3_session objects. It is a SELECT statement that -+** selects the default value for each table column. For example, -+** if the table is -+** -+** CREATE TABLE xx(a DEFAULT 1, b, c DEFAULT 'abc') -+** -+** then this variable is the compiled version of: -+** -+** SELECT 1, NULL, 'abc' - */ - struct SessionTable { - SessionTable *pNext; - char *zName; /* Local name of table */ -- int nCol; /* Number of columns in table zName */ -+ int nCol; /* Number of non-hidden columns */ -+ int nTotalCol; /* Number of columns including hidden */ - int bStat1; /* True if this is sqlite_stat1 */ -+ int bRowid; /* True if this table uses rowid for PK */ - const char **azCol; /* Column names */ -+ const char **azDflt; /* Default value expressions */ -+ int *aiIdx; /* Index to pass to xNew/xOld */ - u8 *abPK; /* Array of primary key flags */ - int nEntry; /* Total number of entries in hash table */ - int nChange; /* Size of apChange[] array */ - SessionChange **apChange; /* Hash table buckets */ -+ sqlite3_stmt *pDfltStmt; - }; - - /* -@@ -202723,8 +228452,10 @@ struct SessionTable { - ** this structure stored in a SessionTable.aChange[] hash table. - */ - struct SessionChange { -- int op; /* One of UPDATE, DELETE, INSERT */ -- int bIndirect; /* True if this change is "indirect" */ -+ u8 op; /* One of UPDATE, DELETE, INSERT */ -+ u8 bIndirect; /* True if this change is "indirect" */ -+ u16 nRecordField; /* Number of fields in aRecord[] */ -+ int nMaxSize; /* Max size of eventual changeset record */ - int nRecord; /* Number of bytes in buffer aRecord[] */ - u8 *aRecord; /* Buffer containing old.* record */ - SessionChange *pNext; /* For hash-table collisions */ -@@ -202749,7 +228480,7 @@ static int sessionVarintLen(int iVal){ - ** Read a varint value from aBuf[] into *piVal. Return the number of - ** bytes read. - */ --static int sessionVarintGet(u8 *aBuf, int *piVal){ -+static int sessionVarintGet(const u8 *aBuf, int *piVal){ - return getVarint32(aBuf, *piVal); - } - -@@ -202849,7 +228580,7 @@ static int sessionSerializeValue( - - if( aBuf ){ - sessionVarintPut(&aBuf[1], n); -- if( n ) memcpy(&aBuf[nVarint + 1], z, n); -+ if( n>0 ) memcpy(&aBuf[nVarint + 1], z, n); - } - - nByte = 1 + nVarint + n; -@@ -202865,6 +228596,26 @@ static int sessionSerializeValue( - return SQLITE_OK; - } - -+/* -+** Allocate and return a pointer to a buffer nByte bytes in size. If -+** pSession is not NULL, increase the sqlite3_session.nMalloc variable -+** by the number of bytes allocated. -+*/ -+static void *sessionMalloc64(sqlite3_session *pSession, i64 nByte){ -+ void *pRet = sqlite3_malloc64(nByte); -+ if( pSession ) pSession->nMalloc += sqlite3_msize(pRet); -+ return pRet; -+} -+ -+/* -+** Free buffer pFree, which must have been allocated by an earlier -+** call to sessionMalloc64(). If pSession is not NULL, decrease the -+** sqlite3_session.nMalloc counter by the number of bytes freed. -+*/ -+static void sessionFree(sqlite3_session *pSession, void *pFree){ -+ if( pSession ) pSession->nMalloc -= sqlite3_msize(pFree); -+ sqlite3_free(pFree); -+} - - /* - ** This macro is used to calculate hash key values for data structures. In -@@ -202923,6 +228674,7 @@ static unsigned int sessionHashAppendType(unsigned int h, int eType){ - */ - static int sessionPreupdateHash( - sqlite3_session *pSession, /* Session object that owns pTab */ -+ i64 iRowid, - SessionTable *pTab, /* Session table handle */ - int bNew, /* True to hash the new.* PK */ - int *piHash, /* OUT: Hash value */ -@@ -202931,48 +228683,53 @@ static int sessionPreupdateHash( - unsigned int h = 0; /* Hash value to return */ - int i; /* Used to iterate through columns */ - -- assert( *pbNullPK==0 ); -- assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); -- for(i=0; inCol; i++){ -- if( pTab->abPK[i] ){ -- int rc; -- int eType; -- sqlite3_value *pVal; -- -- if( bNew ){ -- rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); -- }else{ -- rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); -- } -- if( rc!=SQLITE_OK ) return rc; -- -- eType = sqlite3_value_type(pVal); -- h = sessionHashAppendType(h, eType); -- if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ -- i64 iVal; -- if( eType==SQLITE_INTEGER ){ -- iVal = sqlite3_value_int64(pVal); -+ assert( pTab->nTotalCol==pSession->hook.xCount(pSession->hook.pCtx) ); -+ if( pTab->bRowid ){ -+ h = sessionHashAppendI64(h, iRowid); -+ }else{ -+ assert( *pbNullPK==0 ); -+ for(i=0; inCol; i++){ -+ if( pTab->abPK[i] ){ -+ int rc; -+ int eType; -+ sqlite3_value *pVal; -+ int iIdx = pTab->aiIdx[i]; -+ -+ if( bNew ){ -+ rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); - }else{ -- double rVal = sqlite3_value_double(pVal); -- assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); -- memcpy(&iVal, &rVal, 8); -+ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); - } -- h = sessionHashAppendI64(h, iVal); -- }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ -- const u8 *z; -- int n; -- if( eType==SQLITE_TEXT ){ -- z = (const u8 *)sqlite3_value_text(pVal); -+ if( rc!=SQLITE_OK ) return rc; -+ -+ eType = sqlite3_value_type(pVal); -+ h = sessionHashAppendType(h, eType); -+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ -+ i64 iVal; -+ if( eType==SQLITE_INTEGER ){ -+ iVal = sqlite3_value_int64(pVal); -+ }else{ -+ double rVal = sqlite3_value_double(pVal); -+ assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); -+ memcpy(&iVal, &rVal, 8); -+ } -+ h = sessionHashAppendI64(h, iVal); -+ }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ -+ const u8 *z; -+ int n; -+ if( eType==SQLITE_TEXT ){ -+ z = (const u8 *)sqlite3_value_text(pVal); -+ }else{ -+ z = (const u8 *)sqlite3_value_blob(pVal); -+ } -+ n = sqlite3_value_bytes(pVal); -+ if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; -+ h = sessionHashAppendBlob(h, n, z); - }else{ -- z = (const u8 *)sqlite3_value_blob(pVal); -+ assert( eType==SQLITE_NULL ); -+ assert( pTab->bStat1==0 || i!=1 ); -+ *pbNullPK = 1; - } -- n = sqlite3_value_bytes(pVal); -- if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; -- h = sessionHashAppendBlob(h, n, z); -- }else{ -- assert( eType==SQLITE_NULL ); -- assert( pTab->bStat1==0 || i!=1 ); -- *pbNullPK = 1; - } - } - } -@@ -202986,9 +228743,11 @@ static int sessionPreupdateHash( - ** Return the number of bytes of space occupied by the value (including - ** the type byte). - */ --static int sessionSerialLen(u8 *a){ -- int e = *a; -+static int sessionSerialLen(const u8 *a){ -+ int e; - int n; -+ assert( a!=0 ); -+ e = *a; - if( e==0 || e==0xFF ) return 1; - if( e==SQLITE_NULL ) return 1; - if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; -@@ -203255,6 +229014,7 @@ static int sessionMergeUpdate( - */ - static int sessionPreupdateEqual( - sqlite3_session *pSession, /* Session object that owns SessionTable */ -+ i64 iRowid, /* Rowid value if pTab->bRowid */ - SessionTable *pTab, /* Table associated with change */ - SessionChange *pChange, /* Change to compare to */ - int op /* Current pre-update operation */ -@@ -203262,6 +229022,11 @@ static int sessionPreupdateEqual( - int iCol; /* Used to iterate through columns */ - u8 *a = pChange->aRecord; /* Cursor used to scan change record */ - -+ if( pTab->bRowid ){ -+ if( a[0]!=SQLITE_INTEGER ) return 0; -+ return sessionGetI64(&a[1])==iRowid; -+ } -+ - assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); - for(iCol=0; iColnCol; iCol++){ - if( !pTab->abPK[iCol] ){ -@@ -203270,6 +229035,7 @@ static int sessionPreupdateEqual( - sqlite3_value *pVal; /* Value returned by preupdate_new/old */ - int rc; /* Error code from preupdate_new/old */ - int eType = *a++; /* Type of value from change record */ -+ int iIdx = pTab->aiIdx[iCol]; - - /* The following calls to preupdate_new() and preupdate_old() can not - ** fail. This is because they cache their return values, and by the -@@ -203278,12 +229044,13 @@ static int sessionPreupdateEqual( - ** this (that the method has already been called). */ - if( op==SQLITE_INSERT ){ - /* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */ -- rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal); -+ rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); - }else{ - /* assert( db->pPreUpdate->pUnpacked ); */ -- rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal); -+ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); - } - assert( rc==SQLITE_OK ); -+ (void)rc; /* Suppress warning about unused variable */ - if( sqlite3_value_type(pVal)!=eType ) return 0; - - /* A SessionChange object never has a NULL value in a PK column */ -@@ -203332,13 +229099,19 @@ static int sessionPreupdateEqual( - ** Growing the hash table in this case is a performance optimization only, - ** it is not required for correct operation. - */ --static int sessionGrowHash(int bPatchset, SessionTable *pTab){ -+static int sessionGrowHash( -+ sqlite3_session *pSession, /* For memory accounting. May be NULL */ -+ int bPatchset, -+ SessionTable *pTab -+){ - if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){ - int i; - SessionChange **apNew; - sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128); - -- apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew); -+ apNew = (SessionChange**)sessionMalloc64( -+ pSession, sizeof(SessionChange*) * nNew -+ ); - if( apNew==0 ){ - if( pTab->nChange==0 ){ - return SQLITE_ERROR; -@@ -203359,7 +229132,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ - } - } - -- sqlite3_free(pTab->apChange); -+ sessionFree(pSession, pTab->apChange); - pTab->nChange = nNew; - pTab->apChange = apNew; - } -@@ -203380,26 +229153,32 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ - ** - ** For example, if the table is declared as: - ** --** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z)); -+** CREATE TABLE tbl1(w, x DEFAULT 'abc', y, z, PRIMARY KEY(w, z)); - ** --** Then the four output variables are populated as follows: -+** Then the five output variables are populated as follows: - ** - ** *pnCol = 4 - ** *pzTab = "tbl1" - ** *pazCol = {"w", "x", "y", "z"} -+** *pazDflt = {NULL, 'abc', NULL, NULL} - ** *pabPK = {1, 0, 0, 1} - ** - ** All returned buffers are part of the same single allocation, which must - ** be freed using sqlite3_free() by the caller - */ - static int sessionTableInfo( -+ sqlite3_session *pSession, /* For memory accounting. May be NULL */ - sqlite3 *db, /* Database connection */ - const char *zDb, /* Name of attached database (e.g. "main") */ - const char *zThis, /* Table name */ - int *pnCol, /* OUT: number of columns */ -+ int *pnTotalCol, /* OUT: number of hidden columns */ - const char **pzTab, /* OUT: Copy of zThis */ - const char ***pazCol, /* OUT: Array of column names for table */ -- u8 **pabPK /* OUT: Array of booleans - true for PK col */ -+ const char ***pazDflt, /* OUT: Array of default value expressions */ -+ int **paiIdx, /* OUT: Array of xNew/xOld indexes */ -+ u8 **pabPK, /* OUT: Array of booleans - true for PK col */ -+ int *pbRowid /* OUT: True if only PK is a rowid */ - ){ - char *zPragma; - sqlite3_stmt *pStmt; -@@ -203410,19 +229189,30 @@ static int sessionTableInfo( - int i; - u8 *pAlloc = 0; - char **azCol = 0; -+ char **azDflt = 0; - u8 *abPK = 0; -+ int *aiIdx = 0; -+ int bRowid = 0; /* Set to true to use rowid as PK */ - - assert( pazCol && pabPK ); - -+ *pazCol = 0; -+ *pabPK = 0; -+ *pnCol = 0; -+ if( pnTotalCol ) *pnTotalCol = 0; -+ if( paiIdx ) *paiIdx = 0; -+ if( pzTab ) *pzTab = 0; -+ if( pazDflt ) *pazDflt = 0; -+ - nThis = sqlite3Strlen30(zThis); - if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){ - rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0); - if( rc==SQLITE_OK ){ - /* For sqlite_stat1, pretend that (tbl,idx) is the PRIMARY KEY. */ - zPragma = sqlite3_mprintf( -- "SELECT 0, 'tbl', '', 0, '', 1 UNION ALL " -- "SELECT 1, 'idx', '', 0, '', 2 UNION ALL " -- "SELECT 2, 'stat', '', 0, '', 0" -+ "SELECT 0, 'tbl', '', 0, '', 1, 0 UNION ALL " -+ "SELECT 1, 'idx', '', 0, '', 2, 0 UNION ALL " -+ "SELECT 2, 'stat', '', 0, '', 0, 0" - ); - }else if( rc==SQLITE_ERROR ){ - zPragma = sqlite3_mprintf(""); -@@ -203430,32 +229220,47 @@ static int sessionTableInfo( - return rc; - } - }else{ -- zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); -+ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_xinfo('%q')", zDb, zThis); -+ } -+ if( !zPragma ){ -+ return SQLITE_NOMEM; - } -- if( !zPragma ) return SQLITE_NOMEM; - - rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0); - sqlite3_free(zPragma); -- if( rc!=SQLITE_OK ) return rc; -+ if( rc!=SQLITE_OK ){ -+ return rc; -+ } - - nByte = nThis + 1; -+ bRowid = (pbRowid!=0); - while( SQLITE_ROW==sqlite3_step(pStmt) ){ -- nByte += sqlite3_column_bytes(pStmt, 1); -- nDbCol++; -+ nByte += sqlite3_column_bytes(pStmt, 1); /* name */ -+ nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */ -+ if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ -+ nDbCol++; -+ } -+ if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */ - } -+ if( nDbCol==0 ) bRowid = 0; -+ nDbCol += bRowid; -+ nByte += strlen(SESSIONS_ROWID); - rc = sqlite3_reset(pStmt); - - if( rc==SQLITE_OK ){ -- nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); -- pAlloc = sqlite3_malloc64(nByte); -+ nByte += nDbCol * (sizeof(const char *)*2 +sizeof(int)+sizeof(u8) + 1 + 1); -+ pAlloc = sessionMalloc64(pSession, nByte); - if( pAlloc==0 ){ - rc = SQLITE_NOMEM; -+ }else{ -+ memset(pAlloc, 0, nByte); - } - } - if( rc==SQLITE_OK ){ - azCol = (char **)pAlloc; -- pAlloc = (u8 *)&azCol[nDbCol]; -- abPK = (u8 *)pAlloc; -+ azDflt = (char**)&azCol[nDbCol]; -+ aiIdx = (int*)&azDflt[nDbCol]; -+ abPK = (u8 *)&aiIdx[nDbCol]; - pAlloc = &abPK[nDbCol]; - if( pzTab ){ - memcpy(pAlloc, zThis, nThis+1); -@@ -203464,43 +229269,63 @@ static int sessionTableInfo( - } - - i = 0; -- while( SQLITE_ROW==sqlite3_step(pStmt) ){ -- int nName = sqlite3_column_bytes(pStmt, 1); -- const unsigned char *zName = sqlite3_column_text(pStmt, 1); -- if( zName==0 ) break; -- memcpy(pAlloc, zName, nName+1); -- azCol[i] = (char *)pAlloc; -+ if( bRowid ){ -+ size_t nName = strlen(SESSIONS_ROWID); -+ memcpy(pAlloc, SESSIONS_ROWID, nName+1); -+ azCol[i] = (char*)pAlloc; - pAlloc += nName+1; -- abPK[i] = sqlite3_column_int(pStmt, 5); -+ abPK[i] = 1; -+ aiIdx[i] = -1; - i++; - } -+ while( SQLITE_ROW==sqlite3_step(pStmt) ){ -+ if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ -+ int nName = sqlite3_column_bytes(pStmt, 1); -+ int nDflt = sqlite3_column_bytes(pStmt, 4); -+ const unsigned char *zName = sqlite3_column_text(pStmt, 1); -+ const unsigned char *zDflt = sqlite3_column_text(pStmt, 4); -+ -+ if( zName==0 ) break; -+ memcpy(pAlloc, zName, nName+1); -+ azCol[i] = (char *)pAlloc; -+ pAlloc += nName+1; -+ if( zDflt ){ -+ memcpy(pAlloc, zDflt, nDflt+1); -+ azDflt[i] = (char *)pAlloc; -+ pAlloc += nDflt+1; -+ }else{ -+ azDflt[i] = 0; -+ } -+ abPK[i] = sqlite3_column_int(pStmt, 5); -+ aiIdx[i] = sqlite3_column_int(pStmt, 0); -+ i++; -+ } -+ if( pnTotalCol ) (*pnTotalCol)++; -+ } - rc = sqlite3_reset(pStmt); -- - } - - /* If successful, populate the output variables. Otherwise, zero them and - ** free any allocation made. An error code will be returned in this case. - */ - if( rc==SQLITE_OK ){ -- *pazCol = (const char **)azCol; -+ *pazCol = (const char**)azCol; -+ if( pazDflt ) *pazDflt = (const char**)azDflt; - *pabPK = abPK; - *pnCol = nDbCol; -+ if( paiIdx ) *paiIdx = aiIdx; - }else{ -- *pazCol = 0; -- *pabPK = 0; -- *pnCol = 0; -- if( pzTab ) *pzTab = 0; -- sqlite3_free(azCol); -+ sessionFree(pSession, azCol); - } -+ if( pbRowid ) *pbRowid = bRowid; - sqlite3_finalize(pStmt); - return rc; - } - - /* --** This function is only called from within a pre-update handler for a --** write to table pTab, part of session pSession. If this is the first --** write to this table, initalize the SessionTable.nCol, azCol[] and --** abPK[] arrays accordingly. -+** This function is called to initialize the SessionTable.nCol, azCol[] -+** abPK[] and azDflt[] members of SessionTable object pTab. If these -+** fields are already initialized, this function is a no-op. - ** - ** If an error occurs, an error code is stored in sqlite3_session.rc and - ** non-zero returned. Or, if no error occurs but the table has no primary -@@ -203508,14 +229333,25 @@ static int sessionTableInfo( - ** indicate that updates on this table should be ignored. SessionTable.abPK - ** is set to NULL in this case. - */ --static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ -+static int sessionInitTable( -+ sqlite3_session *pSession, /* Optional session handle */ -+ SessionTable *pTab, /* Table object to initialize */ -+ sqlite3 *db, /* Database handle to read schema from */ -+ const char *zDb /* Name of db - "main", "temp" etc. */ -+){ -+ int rc = SQLITE_OK; -+ - if( pTab->nCol==0 ){ - u8 *abPK; - assert( pTab->azCol==0 || pTab->abPK==0 ); -- pSession->rc = sessionTableInfo(pSession->db, pSession->zDb, -- pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK -+ sqlite3_free(pTab->azCol); -+ pTab->abPK = 0; -+ rc = sessionTableInfo(pSession, db, zDb, -+ pTab->zName, &pTab->nCol, &pTab->nTotalCol, 0, &pTab->azCol, -+ &pTab->azDflt, &pTab->aiIdx, &abPK, -+ ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0) - ); -- if( pSession->rc==SQLITE_OK ){ -+ if( rc==SQLITE_OK ){ - int i; - for(i=0; inCol; i++){ - if( abPK[i] ){ -@@ -203526,9 +229362,326 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ - if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){ - pTab->bStat1 = 1; - } -+ -+ if( pSession && pSession->bEnableSize ){ -+ pSession->nMaxChangesetSize += ( -+ 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1 -+ ); -+ } -+ } -+ } -+ -+ if( pSession ){ -+ pSession->rc = rc; -+ return (rc || pTab->abPK==0); -+ } -+ return rc; -+} -+ -+/* -+** Re-initialize table object pTab. -+*/ -+static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){ -+ int nCol = 0; -+ int nTotalCol = 0; -+ const char **azCol = 0; -+ const char **azDflt = 0; -+ int *aiIdx = 0; -+ u8 *abPK = 0; -+ int bRowid = 0; -+ -+ assert( pSession->rc==SQLITE_OK ); -+ -+ pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, -+ pTab->zName, &nCol, &nTotalCol, 0, &azCol, &azDflt, &aiIdx, &abPK, -+ (pSession->bImplicitPK ? &bRowid : 0) -+ ); -+ if( pSession->rc==SQLITE_OK ){ -+ if( pTab->nCol>nCol || pTab->bRowid!=bRowid ){ -+ pSession->rc = SQLITE_SCHEMA; -+ }else{ -+ int ii; -+ int nOldCol = pTab->nCol; -+ for(ii=0; iinCol ){ -+ if( pTab->abPK[ii]!=abPK[ii] ){ -+ pSession->rc = SQLITE_SCHEMA; -+ } -+ }else if( abPK[ii] ){ -+ pSession->rc = SQLITE_SCHEMA; -+ } -+ } -+ -+ if( pSession->rc==SQLITE_OK ){ -+ const char **a = pTab->azCol; -+ pTab->azCol = azCol; -+ pTab->nCol = nCol; -+ pTab->nTotalCol = nTotalCol; -+ pTab->azDflt = azDflt; -+ pTab->abPK = abPK; -+ pTab->aiIdx = aiIdx; -+ azCol = a; -+ } -+ if( pSession->bEnableSize ){ -+ pSession->nMaxChangesetSize += (nCol - nOldCol); -+ pSession->nMaxChangesetSize += sessionVarintLen(nCol); -+ pSession->nMaxChangesetSize -= sessionVarintLen(nOldCol); -+ } -+ } -+ } -+ -+ sqlite3_free((char*)azCol); -+ return pSession->rc; -+} -+ -+/* -+** Session-change object (*pp) contains an old.* record with fewer than -+** nCol fields. This function updates it with the default values for -+** the missing fields. -+*/ -+static void sessionUpdateOneChange( -+ sqlite3_session *pSession, /* For memory accounting */ -+ int *pRc, /* IN/OUT: Error code */ -+ SessionChange **pp, /* IN/OUT: Change object to update */ -+ int nCol, /* Number of columns now in table */ -+ sqlite3_stmt *pDflt /* SELECT */ -+){ -+ SessionChange *pOld = *pp; -+ -+ while( pOld->nRecordFieldnRecordField; -+ int eType = sqlite3_column_type(pDflt, iField); -+ switch( eType ){ -+ case SQLITE_NULL: -+ nIncr = 1; -+ break; -+ case SQLITE_INTEGER: -+ case SQLITE_FLOAT: -+ nIncr = 9; -+ break; -+ default: { -+ int n = sqlite3_column_bytes(pDflt, iField); -+ nIncr = 1 + sessionVarintLen(n) + n; -+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); -+ break; -+ } -+ } -+ -+ nByte = nIncr + (sizeof(SessionChange) + pOld->nRecord); -+ pNew = sessionMalloc64(pSession, nByte); -+ if( pNew==0 ){ -+ *pRc = SQLITE_NOMEM; -+ return; -+ }else{ -+ memcpy(pNew, pOld, sizeof(SessionChange)); -+ pNew->aRecord = (u8*)&pNew[1]; -+ memcpy(pNew->aRecord, pOld->aRecord, pOld->nRecord); -+ pNew->aRecord[pNew->nRecord++] = (u8)eType; -+ switch( eType ){ -+ case SQLITE_INTEGER: { -+ i64 iVal = sqlite3_column_int64(pDflt, iField); -+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal); -+ pNew->nRecord += 8; -+ break; -+ } -+ -+ case SQLITE_FLOAT: { -+ double rVal = sqlite3_column_double(pDflt, iField); -+ i64 iVal = 0; -+ memcpy(&iVal, &rVal, sizeof(rVal)); -+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal); -+ pNew->nRecord += 8; -+ break; -+ } -+ -+ case SQLITE_TEXT: { -+ int n = sqlite3_column_bytes(pDflt, iField); -+ const char *z = (const char*)sqlite3_column_text(pDflt, iField); -+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n); -+ memcpy(&pNew->aRecord[pNew->nRecord], z, n); -+ pNew->nRecord += n; -+ break; -+ } -+ -+ case SQLITE_BLOB: { -+ int n = sqlite3_column_bytes(pDflt, iField); -+ const u8 *z = (const u8*)sqlite3_column_blob(pDflt, iField); -+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n); -+ memcpy(&pNew->aRecord[pNew->nRecord], z, n); -+ pNew->nRecord += n; -+ break; -+ } -+ -+ default: -+ assert( eType==SQLITE_NULL ); -+ break; -+ } -+ -+ sessionFree(pSession, pOld); -+ *pp = pOld = pNew; -+ pNew->nRecordField++; -+ pNew->nMaxSize += nIncr; -+ if( pSession ){ -+ pSession->nMaxChangesetSize += nIncr; -+ } -+ } -+ } -+} -+ -+/* -+** Ensure that there is room in the buffer to append nByte bytes of data. -+** If not, use sqlite3_realloc() to grow the buffer so that there is. -+** -+** If successful, return zero. Otherwise, if an OOM condition is encountered, -+** set *pRc to SQLITE_NOMEM and return non-zero. -+*/ -+static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){ -+#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1) -+ i64 nReq = p->nBuf + nByte; -+ if( *pRc==SQLITE_OK && nReq>p->nAlloc ){ -+ u8 *aNew; -+ i64 nNew = p->nAlloc ? p->nAlloc : 128; -+ -+ do { -+ nNew = nNew*2; -+ }while( nNewSESSION_MAX_BUFFER_SZ ){ -+ nNew = SESSION_MAX_BUFFER_SZ; -+ if( nNewaBuf, nNew); -+ if( 0==aNew ){ -+ *pRc = SQLITE_NOMEM; -+ }else{ -+ p->aBuf = aNew; -+ p->nAlloc = nNew; -+ } -+ } -+ return (*pRc!=SQLITE_OK); -+} -+ -+ -+/* -+** This function is a no-op if *pRc is other than SQLITE_OK when it is -+** called. Otherwise, append a string to the buffer. All bytes in the string -+** up to (but not including) the nul-terminator are written to the buffer. -+** -+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before -+** returning. -+*/ -+static void sessionAppendStr( -+ SessionBuffer *p, -+ const char *zStr, -+ int *pRc -+){ -+ int nStr = sqlite3Strlen30(zStr); -+ if( 0==sessionBufferGrow(p, nStr+1, pRc) ){ -+ memcpy(&p->aBuf[p->nBuf], zStr, nStr); -+ p->nBuf += nStr; -+ p->aBuf[p->nBuf] = 0x00; -+ } -+} -+ -+/* -+** Format a string using printf() style formatting and then append it to the -+** buffer using sessionAppendString(). -+*/ -+static void sessionAppendPrintf( -+ SessionBuffer *p, /* Buffer to append to */ -+ int *pRc, -+ const char *zFmt, -+ ... -+){ -+ if( *pRc==SQLITE_OK ){ -+ char *zApp = 0; -+ va_list ap; -+ va_start(ap, zFmt); -+ zApp = sqlite3_vmprintf(zFmt, ap); -+ if( zApp==0 ){ -+ *pRc = SQLITE_NOMEM; -+ }else{ -+ sessionAppendStr(p, zApp, pRc); -+ } -+ va_end(ap); -+ sqlite3_free(zApp); -+ } -+} -+ -+/* -+** Prepare a statement against database handle db that SELECTs a single -+** row containing the default values for each column in table pTab. For -+** example, if pTab is declared as: -+** -+** CREATE TABLE pTab(a PRIMARY KEY, b DEFAULT 123, c DEFAULT 'abcd'); -+** -+** Then this function prepares and returns the SQL statement: -+** -+** SELECT NULL, 123, 'abcd'; -+*/ -+static int sessionPrepareDfltStmt( -+ sqlite3 *db, /* Database handle */ -+ SessionTable *pTab, /* Table to prepare statement for */ -+ sqlite3_stmt **ppStmt /* OUT: Statement handle */ -+){ -+ SessionBuffer sql = {0,0,0}; -+ int rc = SQLITE_OK; -+ const char *zSep = " "; -+ int ii = 0; -+ -+ *ppStmt = 0; -+ sessionAppendPrintf(&sql, &rc, "SELECT"); -+ for(ii=0; iinCol; ii++){ -+ const char *zDflt = pTab->azDflt[ii] ? pTab->azDflt[ii] : "NULL"; -+ sessionAppendPrintf(&sql, &rc, "%s%s", zSep, zDflt); -+ zSep = ", "; -+ } -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3_prepare_v2(db, (const char*)sql.aBuf, -1, ppStmt, 0); -+ } -+ sqlite3_free(sql.aBuf); -+ -+ return rc; -+} -+ -+/* -+** Table pTab has one or more existing change-records with old.* records -+** with fewer than pTab->nCol columns. This function updates all such -+** change-records with the default values for the missing columns. -+*/ -+static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){ -+ sqlite3_stmt *pStmt = 0; -+ int rc = pSession->rc; -+ -+ rc = sessionPrepareDfltStmt(pSession->db, pTab, &pStmt); -+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ -+ int ii = 0; -+ SessionChange **pp = 0; -+ for(ii=0; iinChange; ii++){ -+ for(pp=&pTab->apChange[ii]; *pp; pp=&((*pp)->pNext)){ -+ if( (*pp)->nRecordField!=pTab->nCol ){ -+ sessionUpdateOneChange(pSession, &rc, pp, pTab->nCol, pStmt); -+ } -+ } - } - } -- return (pSession->rc || pTab->abPK==0); -+ -+ pSession->rc = rc; -+ rc = sqlite3_finalize(pStmt); -+ if( pSession->rc==SQLITE_OK ) pSession->rc = rc; -+ return pSession->rc; - } - - /* -@@ -203571,6 +229724,109 @@ static int sessionStat1Depth(void *pCtx){ - return p->hook.xDepth(p->hook.pCtx); - } - -+static int sessionUpdateMaxSize( -+ int op, -+ sqlite3_session *pSession, /* Session object pTab is attached to */ -+ SessionTable *pTab, /* Table that change applies to */ -+ SessionChange *pC /* Update pC->nMaxSize */ -+){ -+ i64 nNew = 2; -+ if( pC->op==SQLITE_INSERT ){ -+ if( pTab->bRowid ) nNew += 9; -+ if( op!=SQLITE_DELETE ){ -+ int ii; -+ for(ii=0; iinCol; ii++){ -+ sqlite3_value *p = 0; -+ pSession->hook.xNew(pSession->hook.pCtx, pTab->aiIdx[ii], &p); -+ sessionSerializeValue(0, p, &nNew); -+ } -+ } -+ }else if( op==SQLITE_DELETE ){ -+ nNew += pC->nRecord; -+ if( sqlite3_preupdate_blobwrite(pSession->db)>=0 ){ -+ nNew += pC->nRecord; -+ } -+ }else{ -+ int ii; -+ u8 *pCsr = pC->aRecord; -+ if( pTab->bRowid ){ -+ nNew += 9 + 1; -+ pCsr += 9; -+ } -+ for(ii=pTab->bRowid; iinCol; ii++){ -+ int bChanged = 1; -+ int nOld = 0; -+ int eType; -+ int iIdx = pTab->aiIdx[ii]; -+ sqlite3_value *p = 0; -+ pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); -+ if( p==0 ){ -+ return SQLITE_NOMEM; -+ } -+ -+ eType = *pCsr++; -+ switch( eType ){ -+ case SQLITE_NULL: -+ bChanged = sqlite3_value_type(p)!=SQLITE_NULL; -+ break; -+ -+ case SQLITE_FLOAT: -+ case SQLITE_INTEGER: { -+ if( eType==sqlite3_value_type(p) ){ -+ sqlite3_int64 iVal = sessionGetI64(pCsr); -+ if( eType==SQLITE_INTEGER ){ -+ bChanged = (iVal!=sqlite3_value_int64(p)); -+ }else{ -+ double dVal; -+ memcpy(&dVal, &iVal, 8); -+ bChanged = (dVal!=sqlite3_value_double(p)); -+ } -+ } -+ nOld = 8; -+ pCsr += 8; -+ break; -+ } -+ -+ default: { -+ int nByte; -+ nOld = sessionVarintGet(pCsr, &nByte); -+ pCsr += nOld; -+ nOld += nByte; -+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); -+ if( eType==sqlite3_value_type(p) -+ && nByte==sqlite3_value_bytes(p) -+ && (nByte==0 || 0==memcmp(pCsr, sqlite3_value_blob(p), nByte)) -+ ){ -+ bChanged = 0; -+ } -+ pCsr += nByte; -+ break; -+ } -+ } -+ -+ if( bChanged && pTab->abPK[ii] ){ -+ nNew = pC->nRecord + 2; -+ break; -+ } -+ -+ if( bChanged ){ -+ nNew += 1 + nOld; -+ sessionSerializeValue(0, p, &nNew); -+ }else if( pTab->abPK[ii] ){ -+ nNew += 2 + nOld; -+ }else{ -+ nNew += 2; -+ } -+ } -+ } -+ -+ if( nNew>pC->nMaxSize ){ -+ int nIncr = nNew - pC->nMaxSize; -+ pC->nMaxSize = nNew; -+ pSession->nMaxChangesetSize += nIncr; -+ } -+ return SQLITE_OK; -+} - - /* - ** This function is only called from with a pre-update-hook reporting a -@@ -203582,28 +229838,35 @@ static int sessionStat1Depth(void *pCtx){ - */ - static void sessionPreupdateOneChange( - int op, /* One of SQLITE_UPDATE, INSERT, DELETE */ -+ i64 iRowid, - sqlite3_session *pSession, /* Session object pTab is attached to */ - SessionTable *pTab /* Table that change applies to */ - ){ - int iHash; - int bNull = 0; - int rc = SQLITE_OK; -+ int nExpect = 0; - SessionStat1Ctx stat1 = {{0,0,0,0,0},0}; - - if( pSession->rc ) return; - - /* Load table details if required */ -- if( sessionInitTable(pSession, pTab) ) return; -+ if( sessionInitTable(pSession, pTab, pSession->db, pSession->zDb) ) return; - - /* Check the number of columns in this xPreUpdate call matches the - ** number of columns in the table. */ -- if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){ -+ nExpect = pSession->hook.xCount(pSession->hook.pCtx); -+ if( pTab->nTotalColnTotalCol!=nExpect ){ - pSession->rc = SQLITE_SCHEMA; - return; - } - - /* Grow the hash table if required */ -- if( sessionGrowHash(0, pTab) ){ -+ if( sessionGrowHash(pSession, 0, pTab) ){ - pSession->rc = SQLITE_NOMEM; - return; - } -@@ -203630,21 +229893,22 @@ static void sessionPreupdateOneChange( - /* Calculate the hash-key for this change. If the primary key of the row - ** includes a NULL value, exit early. Such changes are ignored by the - ** session module. */ -- rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull); -+ rc = sessionPreupdateHash( -+ pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull -+ ); - if( rc!=SQLITE_OK ) goto error_out; - - if( bNull==0 ){ - /* Search the hash table for an existing record for this row. */ - SessionChange *pC; - for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ -- if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break; -+ if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break; - } - - if( pC==0 ){ - /* Create a new change object containing all the old values (if - ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK - ** values (if this is an INSERT). */ -- SessionChange *pChange; /* New change object */ - sqlite3_int64 nByte; /* Number of bytes to allocate */ - int i; /* Used to iterate through columns */ - -@@ -203653,30 +229917,37 @@ static void sessionPreupdateOneChange( - - /* Figure out how large an allocation is required */ - nByte = sizeof(SessionChange); -- for(i=0; inCol; i++){ -+ for(i=pTab->bRowid; inCol; i++){ -+ int iIdx = pTab->aiIdx[i]; - sqlite3_value *p = 0; - if( op!=SQLITE_INSERT ){ -- TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); -- assert( trc==SQLITE_OK ); -+ /* This may fail if the column has a non-NULL default and was added -+ ** using ALTER TABLE ADD COLUMN after this record was created. */ -+ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); - }else if( pTab->abPK[i] ){ -- TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); -+ TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx,iIdx,&p); - assert( trc==SQLITE_OK ); - } - -- /* This may fail if SQLite value p contains a utf-16 string that must -- ** be converted to utf-8 and an OOM error occurs while doing so. */ -- rc = sessionSerializeValue(0, p, &nByte); -+ if( rc==SQLITE_OK ){ -+ /* This may fail if SQLite value p contains a utf-16 string that must -+ ** be converted to utf-8 and an OOM error occurs while doing so. */ -+ rc = sessionSerializeValue(0, p, &nByte); -+ } - if( rc!=SQLITE_OK ) goto error_out; - } -+ if( pTab->bRowid ){ -+ nByte += 9; /* Size of rowid field - an integer */ -+ } - - /* Allocate the change object */ -- pChange = (SessionChange *)sqlite3_malloc64(nByte); -- if( !pChange ){ -+ pC = (SessionChange*)sessionMalloc64(pSession, nByte); -+ if( !pC ){ - rc = SQLITE_NOMEM; - goto error_out; - }else{ -- memset(pChange, 0, sizeof(SessionChange)); -- pChange->aRecord = (u8 *)&pChange[1]; -+ memset(pC, 0, sizeof(SessionChange)); -+ pC->aRecord = (u8 *)&pC[1]; - } - - /* Populate the change object. None of the preupdate_old(), -@@ -203684,24 +229955,31 @@ static void sessionPreupdateOneChange( - ** required values and encodings have already been cached in memory. - ** It is not possible for an OOM to occur in this block. */ - nByte = 0; -- for(i=0; inCol; i++){ -+ if( pTab->bRowid ){ -+ pC->aRecord[0] = SQLITE_INTEGER; -+ sessionPutI64(&pC->aRecord[1], iRowid); -+ nByte = 9; -+ } -+ for(i=pTab->bRowid; inCol; i++){ - sqlite3_value *p = 0; -+ int iIdx = pTab->aiIdx[i]; - if( op!=SQLITE_INSERT ){ -- pSession->hook.xOld(pSession->hook.pCtx, i, &p); -+ pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); - }else if( pTab->abPK[i] ){ -- pSession->hook.xNew(pSession->hook.pCtx, i, &p); -+ pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); - } -- sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); -+ sessionSerializeValue(&pC->aRecord[nByte], p, &nByte); - } - - /* Add the change to the hash-table */ - if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){ -- pChange->bIndirect = 1; -+ pC->bIndirect = 1; - } -- pChange->nRecord = nByte; -- pChange->op = op; -- pChange->pNext = pTab->apChange[iHash]; -- pTab->apChange[iHash] = pChange; -+ pC->nRecordField = pTab->nCol; -+ pC->nRecord = nByte; -+ pC->op = op; -+ pC->pNext = pTab->apChange[iHash]; -+ pTab->apChange[iHash] = pC; - - }else if( pC->bIndirect ){ - /* If the existing change is considered "indirect", but this current -@@ -203712,8 +229990,14 @@ static void sessionPreupdateOneChange( - pC->bIndirect = 0; - } - } -+ -+ assert( rc==SQLITE_OK ); -+ if( pSession->bEnableSize ){ -+ rc = sessionUpdateMaxSize(op, pSession, pTab, pC); -+ } - } - -+ - /* If an error has occurred, mark the session object as failed. */ - error_out: - if( pTab->bStat1 ){ -@@ -203746,7 +230030,11 @@ static int sessionFindTable( - ){ - rc = sqlite3session_attach(pSession, zName); - if( rc==SQLITE_OK ){ -- for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext); -+ pRet = pSession->pTable; -+ while( ALWAYS(pRet) && pRet->pNext ){ -+ pRet = pRet->pNext; -+ } -+ assert( pRet!=0 ); - assert( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) ); - } - } -@@ -203773,6 +230061,8 @@ static void xPreUpdate( - int nDb = sqlite3Strlen30(zDb); - - assert( sqlite3_mutex_held(db->mutex) ); -+ (void)iKey1; -+ (void)iKey2; - - for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){ - SessionTable *pTab; -@@ -203787,9 +230077,10 @@ static void xPreUpdate( - pSession->rc = sessionFindTable(pSession, zName, &pTab); - if( pTab ){ - assert( pSession->rc==SQLITE_OK ); -- sessionPreupdateOneChange(op, pSession, pTab); -+ assert( op==SQLITE_UPDATE || iKey1==iKey2 ); -+ sessionPreupdateOneChange(op, iKey1, pSession, pTab); - if( op==SQLITE_UPDATE ){ -- sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab); -+ sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab); - } - } - } -@@ -203828,6 +230119,7 @@ static void sessionPreupdateHooks( - typedef struct SessionDiffCtx SessionDiffCtx; - struct SessionDiffCtx { - sqlite3_stmt *pStmt; -+ int bRowid; - int nOldOff; - }; - -@@ -203836,19 +230128,20 @@ struct SessionDiffCtx { - */ - static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){ - SessionDiffCtx *p = (SessionDiffCtx*)pCtx; -- *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff); -+ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid); - return SQLITE_OK; - } - static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){ - SessionDiffCtx *p = (SessionDiffCtx*)pCtx; -- *ppVal = sqlite3_column_value(p->pStmt, iVal); -+ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid); - return SQLITE_OK; - } - static int sessionDiffCount(void *pCtx){ - SessionDiffCtx *p = (SessionDiffCtx*)pCtx; -- return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt); -+ return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid; - } - static int sessionDiffDepth(void *pCtx){ -+ (void)pCtx; - return 0; - } - -@@ -203922,17 +230215,18 @@ static char *sessionExprCompareOther( - } - - static char *sessionSelectFindNew( -- int nCol, - const char *zDb1, /* Pick rows in this db only */ - const char *zDb2, /* But not in this one */ -+ int bRowid, - const char *zTbl, /* Table name */ - const char *zExpr - ){ -+ const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*"); - char *zRet = sqlite3_mprintf( -- "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS (" -+ "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS (" - " SELECT 1 FROM \"%w\".\"%w\" WHERE %s" - ")", -- zDb1, zTbl, zDb2, zTbl, zExpr -+ zSel, zDb1, zTbl, zDb2, zTbl, zExpr - ); - return zRet; - } -@@ -203946,7 +230240,9 @@ static int sessionDiffFindNew( - char *zExpr - ){ - int rc = SQLITE_OK; -- char *zStmt = sessionSelectFindNew(pTab->nCol, zDb1, zDb2, pTab->zName,zExpr); -+ char *zStmt = sessionSelectFindNew( -+ zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr -+ ); - - if( zStmt==0 ){ - rc = SQLITE_NOMEM; -@@ -203957,8 +230253,10 @@ static int sessionDiffFindNew( - SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; - pDiffCtx->pStmt = pStmt; - pDiffCtx->nOldOff = 0; -+ pDiffCtx->bRowid = pTab->bRowid; - while( SQLITE_ROW==sqlite3_step(pStmt) ){ -- sessionPreupdateOneChange(op, pSession, pTab); -+ i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); -+ sessionPreupdateOneChange(op, iRowid, pSession, pTab); - } - rc = sqlite3_finalize(pStmt); - } -@@ -203968,6 +230266,27 @@ static int sessionDiffFindNew( - return rc; - } - -+/* -+** Return a comma-separated list of the fully-qualified (with both database -+** and table name) column names from table pTab. e.g. -+** -+** "main"."t1"."a", "main"."t1"."b", "main"."t1"."c" -+*/ -+static char *sessionAllCols( -+ const char *zDb, -+ SessionTable *pTab -+){ -+ int ii; -+ char *zRet = 0; -+ for(ii=0; iinCol; ii++){ -+ zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"", -+ zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii] -+ ); -+ if( !zRet ) break; -+ } -+ return zRet; -+} -+ - static int sessionDiffFindModified( - sqlite3_session *pSession, - SessionTable *pTab, -@@ -203982,11 +230301,13 @@ static int sessionDiffFindModified( - if( zExpr2==0 ){ - rc = SQLITE_NOMEM; - }else{ -+ char *z1 = sessionAllCols(pSession->zDb, pTab); -+ char *z2 = sessionAllCols(zFrom, pTab); - char *zStmt = sqlite3_mprintf( -- "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", -- pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 -+ "SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", -+ z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 - ); -- if( zStmt==0 ){ -+ if( zStmt==0 || z1==0 || z2==0 ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3_stmt *pStmt; -@@ -203997,12 +230318,15 @@ static int sessionDiffFindModified( - pDiffCtx->pStmt = pStmt; - pDiffCtx->nOldOff = pTab->nCol; - while( SQLITE_ROW==sqlite3_step(pStmt) ){ -- sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab); -+ i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); -+ sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab); - } - rc = sqlite3_finalize(pStmt); - } -- sqlite3_free(zStmt); - } -+ sqlite3_free(zStmt); -+ sqlite3_free(z1); -+ sqlite3_free(z2); - } - - return rc; -@@ -204029,9 +230353,11 @@ SQLITE_API int sqlite3session_diff( - SessionTable *pTo; /* Table zTbl */ - - /* Locate and if necessary initialize the target table object */ -+ pSession->bAutoAttach++; - rc = sessionFindTable(pSession, zTbl, &pTo); -+ pSession->bAutoAttach--; - if( pTo==0 ) goto diff_out; -- if( sessionInitTable(pSession, pTo) ){ -+ if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){ - rc = pSession->rc; - goto diff_out; - } -@@ -204040,13 +230366,43 @@ SQLITE_API int sqlite3session_diff( - if( rc==SQLITE_OK ){ - int bHasPk = 0; - int bMismatch = 0; -- int nCol; /* Columns in zFrom.zTbl */ -- u8 *abPK; -+ int nCol = 0; /* Columns in zFrom.zTbl */ -+ int bRowid = 0; -+ u8 *abPK = 0; - const char **azCol = 0; -- rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); -+ char *zDbExists = 0; -+ -+ /* Check that database zFrom is attached. */ -+ zDbExists = sqlite3_mprintf("SELECT * FROM %Q.sqlite_schema", zFrom); -+ if( zDbExists==0 ){ -+ rc = SQLITE_NOMEM; -+ }else{ -+ sqlite3_stmt *pDbExists = 0; -+ rc = sqlite3_prepare_v2(db, zDbExists, -1, &pDbExists, 0); -+ if( rc==SQLITE_ERROR ){ -+ rc = SQLITE_OK; -+ nCol = -1; -+ } -+ sqlite3_finalize(pDbExists); -+ sqlite3_free(zDbExists); -+ } -+ -+ if( rc==SQLITE_OK && nCol==0 ){ -+ rc = sessionTableInfo(0, db, zFrom, zTbl, -+ &nCol, 0, 0, &azCol, 0, 0, &abPK, -+ pSession->bImplicitPK ? &bRowid : 0 -+ ); -+ } - if( rc==SQLITE_OK ){ - if( pTo->nCol!=nCol ){ -- bMismatch = 1; -+ if( nCol<=0 ){ -+ rc = SQLITE_SCHEMA; -+ if( pzErrMsg ){ -+ *pzErrMsg = sqlite3_mprintf("no such table: %s.%s", zFrom, zTbl); -+ } -+ }else{ -+ bMismatch = 1; -+ } - }else{ - int i; - for(i=0; iapChange[i]; p; p=pNextChange){ - pNextChange = p->pNext; -- sqlite3_free(p); -+ sessionFree(pSession, p); - } - } -- sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */ -- sqlite3_free(pTab->apChange); -- sqlite3_free(pTab); -+ sqlite3_finalize(pTab->pDfltStmt); -+ sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */ -+ sessionFree(pSession, pTab->apChange); -+ sessionFree(pSession, pTab); - } - } - -@@ -204186,9 +230543,9 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){ - - /* Delete all attached table objects. And the contents of their - ** associated hash-tables. */ -- sessionDeleteTable(pSession->pTable); -+ sessionDeleteTable(pSession, pSession->pTable); - -- /* Free the session object itself. */ -+ /* Free the session object. */ - sqlite3_free(pSession); - } - -@@ -204235,7 +230592,8 @@ SQLITE_API int sqlite3session_attach( - - if( !pTab ){ - /* Allocate new SessionTable object. */ -- pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1); -+ int nByte = sizeof(SessionTable) + nName + 1; -+ pTab = (SessionTable*)sessionMalloc64(pSession, nByte); - if( !pTab ){ - rc = SQLITE_NOMEM; - }else{ -@@ -204258,32 +230616,6 @@ SQLITE_API int sqlite3session_attach( - return rc; - } - --/* --** Ensure that there is room in the buffer to append nByte bytes of data. --** If not, use sqlite3_realloc() to grow the buffer so that there is. --** --** If successful, return zero. Otherwise, if an OOM condition is encountered, --** set *pRc to SQLITE_NOMEM and return non-zero. --*/ --static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){ -- if( *pRc==SQLITE_OK && (size_t)(p->nAlloc-p->nBuf)nAlloc ? p->nAlloc : 128; -- do { -- nNew = nNew*2; -- }while( (size_t)(nNew-p->nBuf)aBuf, nNew); -- if( 0==aNew ){ -- *pRc = SQLITE_NOMEM; -- }else{ -- p->aBuf = aNew; -- p->nAlloc = nNew; -- } -- } -- return (*pRc!=SQLITE_OK); --} -- - /* - ** Append the value passed as the second argument to the buffer passed - ** as the first. -@@ -204352,26 +230684,6 @@ static void sessionAppendBlob( - } - } - --/* --** This function is a no-op if *pRc is other than SQLITE_OK when it is --** called. Otherwise, append a string to the buffer. All bytes in the string --** up to (but not including) the nul-terminator are written to the buffer. --** --** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before --** returning. --*/ --static void sessionAppendStr( -- SessionBuffer *p, -- const char *zStr, -- int *pRc --){ -- int nStr = sqlite3Strlen30(zStr); -- if( 0==sessionBufferGrow(p, nStr, pRc) ){ -- memcpy(&p->aBuf[p->nBuf], zStr, nStr); -- p->nBuf += nStr; -- } --} -- - /* - ** This function is a no-op if *pRc is other than SQLITE_OK when it is - ** called. Otherwise, append the string representation of integer iVal -@@ -204404,17 +230716,20 @@ static void sessionAppendIdent( - const char *zStr, /* String to quote, escape and append */ - int *pRc /* IN/OUT: Error code */ - ){ -- int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1; -+ int nStr = sqlite3Strlen30(zStr)*2 + 2 + 2; - if( 0==sessionBufferGrow(p, nStr, pRc) ){ - char *zOut = (char *)&p->aBuf[p->nBuf]; - const char *zIn = zStr; - *zOut++ = '"'; -- while( *zIn ){ -- if( *zIn=='"' ) *zOut++ = '"'; -- *zOut++ = *(zIn++); -+ if( zIn!=0 ){ -+ while( *zIn ){ -+ if( *zIn=='"' ) *zOut++ = '"'; -+ *zOut++ = *(zIn++); -+ } - } - *zOut++ = '"'; - p->nBuf = (int)((u8 *)zOut - p->aBuf); -+ p->aBuf[p->nBuf] = 0x00; - } - } - -@@ -204500,6 +230815,7 @@ static int sessionAppendUpdate( - int i; /* Used to iterate through columns */ - u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */ - -+ assert( abPK!=0 ); - sessionAppendByte(pBuf, SQLITE_UPDATE, &rc); - sessionAppendByte(pBuf, p->bIndirect, &rc); - for(i=0; i FROM zDb.zTab WHERE (pk1, pk2,...) IS (?1, ?2,...) -+** -+** where is: -+** -+** 1 AND (?A OR ?1 IS ) AND ... -+** -+** for each non-pk . - */ - static int sessionSelectStmt( - sqlite3 *db, /* Database handle */ -+ int bIgnoreNoop, - const char *zDb, /* Database name */ - const char *zTab, /* Table name */ -+ int bRowid, - int nCol, /* Number of columns in table */ - const char **azCol, /* Names of table columns */ - u8 *abPK, /* PRIMARY KEY array */ -@@ -204651,8 +230975,57 @@ static int sessionSelectStmt( - ){ - int rc = SQLITE_OK; - char *zSql = 0; -+ const char *zSep = ""; - int nSql = -1; -+ int i; -+ -+ SessionBuffer cols = {0, 0, 0}; -+ SessionBuffer nooptest = {0, 0, 0}; -+ SessionBuffer pkfield = {0, 0, 0}; -+ SessionBuffer pkvar = {0, 0, 0}; -+ -+ sessionAppendStr(&nooptest, ", 1", &rc); -+ -+ if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){ -+ sessionAppendStr(&nooptest, " AND (?6 OR ?3 IS stat)", &rc); -+ sessionAppendStr(&pkfield, "tbl, idx", &rc); -+ sessionAppendStr(&pkvar, -+ "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc -+ ); -+ sessionAppendStr(&cols, "tbl, ?2, stat", &rc); -+ }else{ -+ #if 0 -+ if( bRowid ){ -+ sessionAppendStr(&cols, SESSIONS_ROWID, &rc); -+ } -+ #endif -+ for(i=0; idb; /* Source database handle */ - SessionTable *pTab; /* Used to iterate through attached tables */ -- SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */ -+ SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */ - int rc; /* Return code */ - -- assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0 ) ); -+ assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); -+ assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); - - /* Zero the output variables in case an error occurs. If this session - ** object is already in the error state (sqlite3_session.rc != SQLITE_OK), - ** this call will be a no-op. */ - if( xOutput==0 ){ -+ assert( pnChangeset!=0 && ppChangeset!=0 ); - *pnChangeset = 0; - *ppChangeset = 0; - } -@@ -204823,18 +231202,16 @@ static int sessionGenerateChangeset( - for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ - if( pTab->nEntry ){ - const char *zName = pTab->zName; -- int nCol; /* Number of columns in table */ -- u8 *abPK; /* Primary key array */ -- const char **azCol = 0; /* Table columns */ - int i; /* Used to iterate through hash buckets */ - sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ - int nRewind = buf.nBuf; /* Initial size of write buffer */ - int nNoop; /* Size of buffer after writing tbl header */ -+ int nOldCol = pTab->nCol; - - /* Check the table schema is still Ok. */ -- rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK); -- if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){ -- rc = SQLITE_SCHEMA; -+ rc = sessionReinitTable(pSession, pTab); -+ if( rc==SQLITE_OK && pTab->nCol!=nOldCol ){ -+ rc = sessionUpdateChanges(pSession, pTab); - } - - /* Write a table header */ -@@ -204842,8 +231219,9 @@ static int sessionGenerateChangeset( - - /* Build and compile a statement to execute: */ - if( rc==SQLITE_OK ){ -- rc = sessionSelectStmt( -- db, pSession->zDb, zName, nCol, azCol, abPK, &pSel); -+ rc = sessionSelectStmt(db, 0, pSession->zDb, -+ zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel -+ ); - } - - nNoop = buf.nBuf; -@@ -204851,21 +231229,22 @@ static int sessionGenerateChangeset( - SessionChange *p; /* Used to iterate through changes */ - - for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){ -- rc = sessionSelectBind(pSel, nCol, abPK, p); -+ rc = sessionSelectBind(pSel, pTab->nCol, pTab->abPK, p); - if( rc!=SQLITE_OK ) continue; - if( sqlite3_step(pSel)==SQLITE_ROW ){ - if( p->op==SQLITE_INSERT ){ - int iCol; - sessionAppendByte(&buf, SQLITE_INSERT, &rc); - sessionAppendByte(&buf, p->bIndirect, &rc); -- for(iCol=0; iColnCol; iCol++){ - sessionAppendCol(&buf, pSel, iCol, &rc); - } - }else{ -- rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK); -+ assert( pTab->abPK!=0 ); -+ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK); - } - }else if( p->op!=SQLITE_INSERT ){ -- rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK); -+ rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_reset(pSel); -@@ -204890,7 +231269,6 @@ static int sessionGenerateChangeset( - if( buf.nBuf==nNoop ){ - buf.nBuf = nRewind; - } -- sqlite3_free((char*)azCol); /* cast works around VC++ bug */ - } - } - -@@ -204922,7 +231300,14 @@ SQLITE_API int sqlite3session_changeset( - int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ - void **ppChangeset /* OUT: Buffer containing changeset */ - ){ -- return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); -+ int rc; -+ -+ if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; -+ rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); -+ assert( rc || pnChangeset==0 -+ || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize -+ ); -+ return rc; - } - - /* -@@ -204933,6 +231318,7 @@ SQLITE_API int sqlite3session_changeset_strm( - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut - ){ -+ if( xOutput==0 ) return SQLITE_MISUSE; - return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0); - } - -@@ -204944,6 +231330,7 @@ SQLITE_API int sqlite3session_patchset_strm( - int (*xOutput)(void *pOut, const void *pData, int nData), - void *pOut - ){ -+ if( xOutput==0 ) return SQLITE_MISUSE; - return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0); - } - -@@ -204959,6 +231346,7 @@ SQLITE_API int sqlite3session_patchset( - int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */ - void **ppPatchset /* OUT: Buffer containing changeset */ - ){ -+ if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE; - return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset); - } - -@@ -205007,6 +231395,59 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession){ - return (ret==0); - } - -+/* -+** Return the amount of heap memory in use. -+*/ -+SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession){ -+ return pSession->nMalloc; -+} -+ -+/* -+** Configure the session object passed as the first argument. -+*/ -+SQLITE_API int sqlite3session_object_config(sqlite3_session *pSession, int op, void *pArg){ -+ int rc = SQLITE_OK; -+ switch( op ){ -+ case SQLITE_SESSION_OBJCONFIG_SIZE: { -+ int iArg = *(int*)pArg; -+ if( iArg>=0 ){ -+ if( pSession->pTable ){ -+ rc = SQLITE_MISUSE; -+ }else{ -+ pSession->bEnableSize = (iArg!=0); -+ } -+ } -+ *(int*)pArg = pSession->bEnableSize; -+ break; -+ } -+ -+ case SQLITE_SESSION_OBJCONFIG_ROWID: { -+ int iArg = *(int*)pArg; -+ if( iArg>=0 ){ -+ if( pSession->pTable ){ -+ rc = SQLITE_MISUSE; -+ }else{ -+ pSession->bImplicitPK = (iArg!=0); -+ } -+ } -+ *(int*)pArg = pSession->bImplicitPK; -+ break; -+ } -+ -+ default: -+ rc = SQLITE_MISUSE; -+ } -+ -+ return rc; -+} -+ -+/* -+** Return the maximum size of sqlite3session_changeset() output. -+*/ -+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession){ -+ return pSession->nMaxChangesetSize; -+} -+ - /* - ** Do the work for either sqlite3changeset_start() or start_strm(). - */ -@@ -205016,7 +231457,8 @@ static int sessionChangesetStart( - void *pIn, - int nChangeset, /* Size of buffer pChangeset in bytes */ - void *pChangeset, /* Pointer to buffer containing changeset */ -- int bInvert /* True to invert changeset */ -+ int bInvert, /* True to invert changeset */ -+ int bSkipEmpty /* True to skip empty UPDATE changes */ - ){ - sqlite3_changeset_iter *pRet; /* Iterator to return */ - int nByte; /* Number of bytes to allocate for iterator */ -@@ -205037,6 +231479,7 @@ static int sessionChangesetStart( - pRet->in.pIn = pIn; - pRet->in.bEof = (xInput ? 0 : 1); - pRet->bInvert = bInvert; -+ pRet->bSkipEmpty = bSkipEmpty; - - /* Populate the output variable and return success. */ - *pp = pRet; -@@ -205051,7 +231494,7 @@ SQLITE_API int sqlite3changeset_start( - int nChangeset, /* Size of buffer pChangeset in bytes */ - void *pChangeset /* Pointer to buffer containing changeset */ - ){ -- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0); -+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0, 0); - } - SQLITE_API int sqlite3changeset_start_v2( - sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ -@@ -205060,7 +231503,7 @@ SQLITE_API int sqlite3changeset_start_v2( - int flags - ){ - int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); -- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert); -+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert, 0); - } - - /* -@@ -205071,7 +231514,7 @@ SQLITE_API int sqlite3changeset_start_strm( - int (*xInput)(void *pIn, void *pData, int *pnData), - void *pIn - ){ -- return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0); -+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0, 0); - } - SQLITE_API int sqlite3changeset_start_v2_strm( - sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ -@@ -205080,7 +231523,7 @@ SQLITE_API int sqlite3changeset_start_v2_strm( - int flags - ){ - int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT); -- return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert); -+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert, 0); - } - - /* -@@ -205088,14 +231531,15 @@ SQLITE_API int sqlite3changeset_start_v2_strm( - ** object and the buffer is full, discard some data to free up space. - */ - static void sessionDiscardData(SessionInput *pIn){ -- if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){ -- int nMove = pIn->buf.nBuf - pIn->iNext; -+ if( pIn->xInput && pIn->iCurrent>=sessions_strm_chunk_size ){ -+ int nMove = pIn->buf.nBuf - pIn->iCurrent; - assert( nMove>=0 ); - if( nMove>0 ){ -- memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iNext], nMove); -+ memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iCurrent], nMove); - } -- pIn->buf.nBuf -= pIn->iNext; -- pIn->iNext = 0; -+ pIn->buf.nBuf -= pIn->iCurrent; -+ pIn->iNext -= pIn->iCurrent; -+ pIn->iCurrent = 0; - pIn->nData = pIn->buf.nBuf; - } - } -@@ -205206,11 +231650,14 @@ static int sessionReadRecord( - SessionInput *pIn, /* Input data */ - int nCol, /* Number of values in record */ - u8 *abPK, /* Array of primary key flags, or NULL */ -- sqlite3_value **apOut /* Write values to this array */ -+ sqlite3_value **apOut, /* Write values to this array */ -+ int *pbEmpty - ){ - int i; /* Used to iterate through columns */ - int rc = SQLITE_OK; - -+ assert( pbEmpty==0 || *pbEmpty==0 ); -+ if( pbEmpty ) *pbEmpty = 1; - for(i=0; iaData[pIn->iNext++]; - assert( apOut[i]==0 ); - if( eType ){ -+ if( pbEmpty ) *pbEmpty = 0; - apOut[i] = sqlite3ValueNew(0); - if( !apOut[i] ) rc = SQLITE_NOMEM; - } -@@ -205245,15 +231693,19 @@ static int sessionReadRecord( - } - } - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ -- sqlite3_int64 v = sessionGetI64(aVal); -- if( eType==SQLITE_INTEGER ){ -- sqlite3VdbeMemSetInt64(apOut[i], v); -+ if( (pIn->nData-pIn->iNext)<8 ){ -+ rc = SQLITE_CORRUPT_BKPT; - }else{ -- double d; -- memcpy(&d, &v, 8); -- sqlite3VdbeMemSetDouble(apOut[i], d); -+ sqlite3_int64 v = sessionGetI64(aVal); -+ if( eType==SQLITE_INTEGER ){ -+ sqlite3VdbeMemSetInt64(apOut[i], v); -+ }else{ -+ double d; -+ memcpy(&d, &v, 8); -+ sqlite3VdbeMemSetDouble(apOut[i], d); -+ } -+ pIn->iNext += 8; - } -- pIn->iNext += 8; - } - } - } -@@ -205400,6 +231852,149 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){ - return (p->rc = rc); - } - -+/* -+** Advance the changeset iterator to the next change. The differences between -+** this function and sessionChangesetNext() are that -+** -+** * If pbEmpty is not NULL and the change is a no-op UPDATE (an UPDATE -+** that modifies no columns), this function sets (*pbEmpty) to 1. -+** -+** * If the iterator is configured to skip no-op UPDATEs, -+** sessionChangesetNext() does that. This function does not. -+*/ -+static int sessionChangesetNextOne( -+ sqlite3_changeset_iter *p, /* Changeset iterator */ -+ u8 **paRec, /* If non-NULL, store record pointer here */ -+ int *pnRec, /* If non-NULL, store size of record here */ -+ int *pbNew, /* If non-NULL, true if new table */ -+ int *pbEmpty -+){ -+ int i; -+ u8 op; -+ -+ assert( (paRec==0 && pnRec==0) || (paRec && pnRec) ); -+ assert( pbEmpty==0 || *pbEmpty==0 ); -+ -+ /* If the iterator is in the error-state, return immediately. */ -+ if( p->rc!=SQLITE_OK ) return p->rc; -+ -+ /* Free the current contents of p->apValue[], if any. */ -+ if( p->apValue ){ -+ for(i=0; inCol*2; i++){ -+ sqlite3ValueFree(p->apValue[i]); -+ } -+ memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); -+ } -+ -+ /* Make sure the buffer contains at least 10 bytes of input data, or all -+ ** remaining data if there are less than 10 bytes available. This is -+ ** sufficient either for the 'T' or 'P' byte and the varint that follows -+ ** it, or for the two single byte values otherwise. */ -+ p->rc = sessionInputBuffer(&p->in, 2); -+ if( p->rc!=SQLITE_OK ) return p->rc; -+ -+ p->in.iCurrent = p->in.iNext; -+ sessionDiscardData(&p->in); -+ -+ /* If the iterator is already at the end of the changeset, return DONE. */ -+ if( p->in.iNext>=p->in.nData ){ -+ return SQLITE_DONE; -+ } -+ -+ op = p->in.aData[p->in.iNext++]; -+ while( op=='T' || op=='P' ){ -+ if( pbNew ) *pbNew = 1; -+ p->bPatchset = (op=='P'); -+ if( sessionChangesetReadTblhdr(p) ) return p->rc; -+ if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; -+ p->in.iCurrent = p->in.iNext; -+ if( p->in.iNext>=p->in.nData ) return SQLITE_DONE; -+ op = p->in.aData[p->in.iNext++]; -+ } -+ -+ if( p->zTab==0 || (p->bPatchset && p->bInvert) ){ -+ /* The first record in the changeset is not a table header. Must be a -+ ** corrupt changeset. */ -+ assert( p->in.iNext==1 || p->zTab ); -+ return (p->rc = SQLITE_CORRUPT_BKPT); -+ } -+ -+ p->op = op; -+ p->bIndirect = p->in.aData[p->in.iNext++]; -+ if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ -+ return (p->rc = SQLITE_CORRUPT_BKPT); -+ } -+ -+ if( paRec ){ -+ int nVal; /* Number of values to buffer */ -+ if( p->bPatchset==0 && op==SQLITE_UPDATE ){ -+ nVal = p->nCol * 2; -+ }else if( p->bPatchset && op==SQLITE_DELETE ){ -+ nVal = 0; -+ for(i=0; inCol; i++) if( p->abPK[i] ) nVal++; -+ }else{ -+ nVal = p->nCol; -+ } -+ p->rc = sessionChangesetBufferRecord(&p->in, nVal, pnRec); -+ if( p->rc!=SQLITE_OK ) return p->rc; -+ *paRec = &p->in.aData[p->in.iNext]; -+ p->in.iNext += *pnRec; -+ }else{ -+ sqlite3_value **apOld = (p->bInvert ? &p->apValue[p->nCol] : p->apValue); -+ sqlite3_value **apNew = (p->bInvert ? p->apValue : &p->apValue[p->nCol]); -+ -+ /* If this is an UPDATE or DELETE, read the old.* record. */ -+ if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){ -+ u8 *abPK = p->bPatchset ? p->abPK : 0; -+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld, 0); -+ if( p->rc!=SQLITE_OK ) return p->rc; -+ } -+ -+ /* If this is an INSERT or UPDATE, read the new.* record. */ -+ if( p->op!=SQLITE_DELETE ){ -+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew, pbEmpty); -+ if( p->rc!=SQLITE_OK ) return p->rc; -+ } -+ -+ if( (p->bPatchset || p->bInvert) && p->op==SQLITE_UPDATE ){ -+ /* If this is an UPDATE that is part of a patchset, then all PK and -+ ** modified fields are present in the new.* record. The old.* record -+ ** is currently completely empty. This block shifts the PK fields from -+ ** new.* to old.*, to accommodate the code that reads these arrays. */ -+ for(i=0; inCol; i++){ -+ assert( p->bPatchset==0 || p->apValue[i]==0 ); -+ if( p->abPK[i] ){ -+ assert( p->apValue[i]==0 ); -+ p->apValue[i] = p->apValue[i+p->nCol]; -+ if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT); -+ p->apValue[i+p->nCol] = 0; -+ } -+ } -+ }else if( p->bInvert ){ -+ if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE; -+ else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT; -+ } -+ -+ /* If this is an UPDATE that is part of a changeset, then check that -+ ** there are no fields in the old.* record that are not (a) PK fields, -+ ** or (b) also present in the new.* record. -+ ** -+ ** Such records are technically corrupt, but the rebaser was at one -+ ** point generating them. Under most circumstances this is benign, but -+ ** can cause spurious SQLITE_RANGE errors when applying the changeset. */ -+ if( p->bPatchset==0 && p->op==SQLITE_UPDATE){ -+ for(i=0; inCol; i++){ -+ if( p->abPK[i]==0 && p->apValue[i+p->nCol]==0 ){ -+ sqlite3ValueFree(p->apValue[i]); -+ p->apValue[i] = 0; -+ } -+ } -+ } -+ } -+ -+ return SQLITE_ROW; -+} -+ - /* - ** Advance the changeset iterator to the next change. - ** -@@ -205422,113 +232017,13 @@ static int sessionChangesetNext( - int *pnRec, /* If non-NULL, store size of record here */ - int *pbNew /* If non-NULL, true if new table */ - ){ -- int i; -- u8 op; -- -- assert( (paRec==0 && pnRec==0) || (paRec && pnRec) ); -- -- /* If the iterator is in the error-state, return immediately. */ -- if( p->rc!=SQLITE_OK ) return p->rc; -- -- /* Free the current contents of p->apValue[], if any. */ -- if( p->apValue ){ -- for(i=0; inCol*2; i++){ -- sqlite3ValueFree(p->apValue[i]); -- } -- memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); -- } -- -- /* Make sure the buffer contains at least 10 bytes of input data, or all -- ** remaining data if there are less than 10 bytes available. This is -- ** sufficient either for the 'T' or 'P' byte and the varint that follows -- ** it, or for the two single byte values otherwise. */ -- p->rc = sessionInputBuffer(&p->in, 2); -- if( p->rc!=SQLITE_OK ) return p->rc; -- -- /* If the iterator is already at the end of the changeset, return DONE. */ -- if( p->in.iNext>=p->in.nData ){ -- return SQLITE_DONE; -- } -- -- sessionDiscardData(&p->in); -- p->in.iCurrent = p->in.iNext; -- -- op = p->in.aData[p->in.iNext++]; -- while( op=='T' || op=='P' ){ -- if( pbNew ) *pbNew = 1; -- p->bPatchset = (op=='P'); -- if( sessionChangesetReadTblhdr(p) ) return p->rc; -- if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; -- p->in.iCurrent = p->in.iNext; -- if( p->in.iNext>=p->in.nData ) return SQLITE_DONE; -- op = p->in.aData[p->in.iNext++]; -- } -- -- if( p->zTab==0 || (p->bPatchset && p->bInvert) ){ -- /* The first record in the changeset is not a table header. Must be a -- ** corrupt changeset. */ -- assert( p->in.iNext==1 || p->zTab ); -- return (p->rc = SQLITE_CORRUPT_BKPT); -- } -- -- p->op = op; -- p->bIndirect = p->in.aData[p->in.iNext++]; -- if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ -- return (p->rc = SQLITE_CORRUPT_BKPT); -- } -- -- if( paRec ){ -- int nVal; /* Number of values to buffer */ -- if( p->bPatchset==0 && op==SQLITE_UPDATE ){ -- nVal = p->nCol * 2; -- }else if( p->bPatchset && op==SQLITE_DELETE ){ -- nVal = 0; -- for(i=0; inCol; i++) if( p->abPK[i] ) nVal++; -- }else{ -- nVal = p->nCol; -- } -- p->rc = sessionChangesetBufferRecord(&p->in, nVal, pnRec); -- if( p->rc!=SQLITE_OK ) return p->rc; -- *paRec = &p->in.aData[p->in.iNext]; -- p->in.iNext += *pnRec; -- }else{ -- sqlite3_value **apOld = (p->bInvert ? &p->apValue[p->nCol] : p->apValue); -- sqlite3_value **apNew = (p->bInvert ? p->apValue : &p->apValue[p->nCol]); -- -- /* If this is an UPDATE or DELETE, read the old.* record. */ -- if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){ -- u8 *abPK = p->bPatchset ? p->abPK : 0; -- p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld); -- if( p->rc!=SQLITE_OK ) return p->rc; -- } -- -- /* If this is an INSERT or UPDATE, read the new.* record. */ -- if( p->op!=SQLITE_DELETE ){ -- p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew); -- if( p->rc!=SQLITE_OK ) return p->rc; -- } -- -- if( (p->bPatchset || p->bInvert) && p->op==SQLITE_UPDATE ){ -- /* If this is an UPDATE that is part of a patchset, then all PK and -- ** modified fields are present in the new.* record. The old.* record -- ** is currently completely empty. This block shifts the PK fields from -- ** new.* to old.*, to accommodate the code that reads these arrays. */ -- for(i=0; inCol; i++){ -- assert( p->bPatchset==0 || p->apValue[i]==0 ); -- if( p->abPK[i] ){ -- assert( p->apValue[i]==0 ); -- p->apValue[i] = p->apValue[i+p->nCol]; -- if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT); -- p->apValue[i+p->nCol] = 0; -- } -- } -- }else if( p->bInvert ){ -- if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE; -- else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT; -- } -- } -- -- return SQLITE_ROW; -+ int bEmpty; -+ int rc; -+ do { -+ bEmpty = 0; -+ rc = sessionChangesetNextOne(p, paRec, pnRec, pbNew, &bEmpty); -+ }while( rc==SQLITE_ROW && p->bSkipEmpty && bEmpty); -+ return rc; - } - - /* -@@ -205803,9 +232298,9 @@ static int sessionChangesetInvert( - - /* Read the old.* and new.* records for the update change. */ - pInput->iNext += 2; -- rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]); -+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[0], 0); - if( rc==SQLITE_OK ){ -- rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]); -+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol], 0); - } - - /* Write the new old.* record. Consists of the PK columns from the -@@ -205849,11 +232344,11 @@ static int sessionChangesetInvert( - } - - assert( rc==SQLITE_OK ); -- if( pnInverted ){ -+ if( pnInverted && ALWAYS(ppInverted) ){ - *pnInverted = sOut.nBuf; - *ppInverted = sOut.aBuf; - sOut.aBuf = 0; -- }else if( sOut.nBuf>0 ){ -+ }else if( sOut.nBuf>0 && ALWAYS(xOutput!=0) ){ - rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); - } - -@@ -205906,16 +232401,25 @@ SQLITE_API int sqlite3changeset_invert_strm( - return rc; - } - -+ -+typedef struct SessionUpdate SessionUpdate; -+struct SessionUpdate { -+ sqlite3_stmt *pStmt; -+ u32 *aMask; -+ SessionUpdate *pNext; -+}; -+ - typedef struct SessionApplyCtx SessionApplyCtx; - struct SessionApplyCtx { - sqlite3 *db; - sqlite3_stmt *pDelete; /* DELETE statement */ -- sqlite3_stmt *pUpdate; /* UPDATE statement */ - sqlite3_stmt *pInsert; /* INSERT statement */ - sqlite3_stmt *pSelect; /* SELECT statement */ - int nCol; /* Size of azCol[] and abPK[] arrays */ - const char **azCol; /* Array of column names */ - u8 *abPK; /* Boolean array - true if column is in PK */ -+ u32 *aUpdateMask; /* Used by sessionUpdateFind */ -+ SessionUpdate *pUp; - int bStat1; /* True if table is sqlite_stat1 */ - int bDeferConstraints; /* True to defer constraints */ - int bInvertConstraints; /* Invert when iterating constraints buffer */ -@@ -205923,8 +232427,171 @@ struct SessionApplyCtx { - SessionBuffer rebase; /* Rebase information (if any) here */ - u8 bRebaseStarted; /* If table header is already in rebase */ - u8 bRebase; /* True to collect rebase information */ -+ u8 bIgnoreNoop; /* True to ignore no-op conflicts */ -+ int bRowid; - }; - -+/* Number of prepared UPDATE statements to cache. */ -+#define SESSION_UPDATE_CACHE_SZ 12 -+ -+/* -+** Find a prepared UPDATE statement suitable for the UPDATE step currently -+** being visited by the iterator. The UPDATE is of the form: -+** -+** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ? -+*/ -+static int sessionUpdateFind( -+ sqlite3_changeset_iter *pIter, -+ SessionApplyCtx *p, -+ int bPatchset, -+ sqlite3_stmt **ppStmt -+){ -+ int rc = SQLITE_OK; -+ SessionUpdate *pUp = 0; -+ int nCol = pIter->nCol; -+ int nU32 = (pIter->nCol+33)/32; -+ int ii; -+ -+ if( p->aUpdateMask==0 ){ -+ p->aUpdateMask = sqlite3_malloc(nU32*sizeof(u32)); -+ if( p->aUpdateMask==0 ){ -+ rc = SQLITE_NOMEM; -+ } -+ } -+ -+ if( rc==SQLITE_OK ){ -+ memset(p->aUpdateMask, 0, nU32*sizeof(u32)); -+ rc = SQLITE_CORRUPT; -+ for(ii=0; iinCol; ii++){ -+ if( sessionChangesetNew(pIter, ii) ){ -+ p->aUpdateMask[ii/32] |= (1<<(ii%32)); -+ rc = SQLITE_OK; -+ } -+ } -+ } -+ -+ if( rc==SQLITE_OK ){ -+ if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32)); -+ -+ if( p->pUp ){ -+ int nUp = 0; -+ SessionUpdate **pp = &p->pUp; -+ while( 1 ){ -+ nUp++; -+ if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){ -+ pUp = *pp; -+ *pp = pUp->pNext; -+ pUp->pNext = p->pUp; -+ p->pUp = pUp; -+ break; -+ } -+ -+ if( (*pp)->pNext ){ -+ pp = &(*pp)->pNext; -+ }else{ -+ if( nUp>=SESSION_UPDATE_CACHE_SZ ){ -+ sqlite3_finalize((*pp)->pStmt); -+ sqlite3_free(*pp); -+ *pp = 0; -+ } -+ break; -+ } -+ } -+ } -+ -+ if( pUp==0 ){ -+ int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32); -+ int bStat1 = (sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0); -+ pUp = (SessionUpdate*)sqlite3_malloc(nByte); -+ if( pUp==0 ){ -+ rc = SQLITE_NOMEM; -+ }else{ -+ const char *zSep = ""; -+ SessionBuffer buf; -+ -+ memset(&buf, 0, sizeof(buf)); -+ pUp->aMask = (u32*)&pUp[1]; -+ memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32)); -+ -+ sessionAppendStr(&buf, "UPDATE main.", &rc); -+ sessionAppendIdent(&buf, pIter->zTab, &rc); -+ sessionAppendStr(&buf, " SET ", &rc); -+ -+ /* Create the assignments part of the UPDATE */ -+ for(ii=0; iinCol; ii++){ -+ if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){ -+ sessionAppendStr(&buf, zSep, &rc); -+ sessionAppendIdent(&buf, p->azCol[ii], &rc); -+ sessionAppendStr(&buf, " = ?", &rc); -+ sessionAppendInteger(&buf, ii*2+1, &rc); -+ zSep = ", "; -+ } -+ } -+ -+ /* Create the WHERE clause part of the UPDATE */ -+ zSep = ""; -+ sessionAppendStr(&buf, " WHERE ", &rc); -+ for(ii=0; iinCol; ii++){ -+ if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){ -+ sessionAppendStr(&buf, zSep, &rc); -+ if( bStat1 && ii==1 ){ -+ assert( sqlite3_stricmp(p->azCol[ii], "idx")==0 ); -+ sessionAppendStr(&buf, -+ "idx IS CASE " -+ "WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL " -+ "ELSE ?4 END ", &rc -+ ); -+ }else{ -+ sessionAppendIdent(&buf, p->azCol[ii], &rc); -+ sessionAppendStr(&buf, " IS ?", &rc); -+ sessionAppendInteger(&buf, ii*2+2, &rc); -+ } -+ zSep = " AND "; -+ } -+ } -+ -+ if( rc==SQLITE_OK ){ -+ char *zSql = (char*)buf.aBuf; -+ rc = sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0); -+ } -+ -+ if( rc!=SQLITE_OK ){ -+ sqlite3_free(pUp); -+ pUp = 0; -+ }else{ -+ pUp->pNext = p->pUp; -+ p->pUp = pUp; -+ } -+ sqlite3_free(buf.aBuf); -+ } -+ } -+ } -+ -+ assert( (rc==SQLITE_OK)==(pUp!=0) ); -+ if( pUp ){ -+ *ppStmt = pUp->pStmt; -+ }else{ -+ *ppStmt = 0; -+ } -+ return rc; -+} -+ -+/* -+** Free all cached UPDATE statements. -+*/ -+static void sessionUpdateFree(SessionApplyCtx *p){ -+ SessionUpdate *pUp; -+ SessionUpdate *pNext; -+ for(pUp=p->pUp; pUp; pUp=pNext){ -+ pNext = pUp->pNext; -+ sqlite3_finalize(pUp->pStmt); -+ sqlite3_free(pUp); -+ } -+ p->pUp = 0; -+ sqlite3_free(p->aUpdateMask); -+ p->aUpdateMask = 0; -+} -+ - /* - ** Formulate a statement to DELETE a row from database db. Assuming a table - ** structure like this: -@@ -205994,103 +232661,6 @@ static int sessionDeleteRow( - return rc; - } - --/* --** Formulate and prepare a statement to UPDATE a row from database db. --** Assuming a table structure like this: --** --** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c)); --** --** The UPDATE statement looks like this: --** --** UPDATE x SET --** a = CASE WHEN ?2 THEN ?3 ELSE a END, --** b = CASE WHEN ?5 THEN ?6 ELSE b END, --** c = CASE WHEN ?8 THEN ?9 ELSE c END, --** d = CASE WHEN ?11 THEN ?12 ELSE d END --** WHERE a = ?1 AND c = ?7 AND (?13 OR --** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND --** ) --** --** For each column in the table, there are three variables to bind: --** --** ?(i*3+1) The old.* value of the column, if any. --** ?(i*3+2) A boolean flag indicating that the value is being modified. --** ?(i*3+3) The new.* value of the column, if any. --** --** Also, a boolean flag that, if set to true, causes the statement to update --** a row even if the non-PK values do not match. This is required if the --** conflict-handler is invoked with CHANGESET_DATA and returns --** CHANGESET_REPLACE. This is variable "?(nCol*3+1)". --** --** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left --** pointing to the prepared version of the SQL statement. --*/ --static int sessionUpdateRow( -- sqlite3 *db, /* Database handle */ -- const char *zTab, /* Table name */ -- SessionApplyCtx *p /* Session changeset-apply context */ --){ -- int rc = SQLITE_OK; -- int i; -- const char *zSep = ""; -- SessionBuffer buf = {0, 0, 0}; -- -- /* Append "UPDATE tbl SET " */ -- sessionAppendStr(&buf, "UPDATE main.", &rc); -- sessionAppendIdent(&buf, zTab, &rc); -- sessionAppendStr(&buf, " SET ", &rc); -- -- /* Append the assignments */ -- for(i=0; inCol; i++){ -- sessionAppendStr(&buf, zSep, &rc); -- sessionAppendIdent(&buf, p->azCol[i], &rc); -- sessionAppendStr(&buf, " = CASE WHEN ?", &rc); -- sessionAppendInteger(&buf, i*3+2, &rc); -- sessionAppendStr(&buf, " THEN ?", &rc); -- sessionAppendInteger(&buf, i*3+3, &rc); -- sessionAppendStr(&buf, " ELSE ", &rc); -- sessionAppendIdent(&buf, p->azCol[i], &rc); -- sessionAppendStr(&buf, " END", &rc); -- zSep = ", "; -- } -- -- /* Append the PK part of the WHERE clause */ -- sessionAppendStr(&buf, " WHERE ", &rc); -- for(i=0; inCol; i++){ -- if( p->abPK[i] ){ -- sessionAppendIdent(&buf, p->azCol[i], &rc); -- sessionAppendStr(&buf, " = ?", &rc); -- sessionAppendInteger(&buf, i*3+1, &rc); -- sessionAppendStr(&buf, " AND ", &rc); -- } -- } -- -- /* Append the non-PK part of the WHERE clause */ -- sessionAppendStr(&buf, " (?", &rc); -- sessionAppendInteger(&buf, p->nCol*3+1, &rc); -- sessionAppendStr(&buf, " OR 1", &rc); -- for(i=0; inCol; i++){ -- if( !p->abPK[i] ){ -- sessionAppendStr(&buf, " AND (?", &rc); -- sessionAppendInteger(&buf, i*3+2, &rc); -- sessionAppendStr(&buf, "=0 OR ", &rc); -- sessionAppendIdent(&buf, p->azCol[i], &rc); -- sessionAppendStr(&buf, " IS ?", &rc); -- sessionAppendInteger(&buf, i*3+1, &rc); -- sessionAppendStr(&buf, ")", &rc); -- } -- } -- sessionAppendStr(&buf, ")", &rc); -- -- if( rc==SQLITE_OK ){ -- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0); -- } -- sqlite3_free(buf.aBuf); -- -- return rc; --} -- -- - /* - ** Formulate and prepare an SQL statement to query table zTab by primary - ** key. Assuming the following table structure: -@@ -206109,8 +232679,10 @@ static int sessionSelectRow( - const char *zTab, /* Table name */ - SessionApplyCtx *p /* Session changeset-apply context */ - ){ -- return sessionSelectStmt( -- db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect); -+ /* TODO */ -+ return sessionSelectStmt(db, p->bIgnoreNoop, -+ "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect -+ ); - } - - /* -@@ -206171,17 +232743,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){ - "?3)" - ); - } -- if( rc==SQLITE_OK ){ -- rc = sessionPrepare(db, &p->pUpdate, -- "UPDATE main.sqlite_stat1 SET " -- "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, " -- "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, " -- "stat = CASE WHEN ?8 THEN ?9 ELSE stat END " -- "WHERE tbl=?1 AND idx IS " -- "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END " -- "AND (?10 OR ?8=0 OR stat IS ?7)" -- ); -- } - if( rc==SQLITE_OK ){ - rc = sessionPrepare(db, &p->pDelete, - "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS " -@@ -206247,7 +232808,7 @@ static int sessionBindRow( - - for(i=0; rc==SQLITE_OK && ipSelect; - int rc; /* Return code */ - int nCol; /* Number of columns in table */ - int op; /* Changset operation (SQLITE_UPDATE etc.) */ - const char *zDummy; /* Unused */ - -+ sqlite3_clear_bindings(pSelect); - sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0); - rc = sessionBindRow(pIter, - op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old, -- nCol, abPK, pSelect -+ nCol, p->abPK, pSelect - ); - -+ if( op!=SQLITE_DELETE && p->bIgnoreNoop ){ -+ int ii; -+ for(ii=0; rc==SQLITE_OK && iiabPK[ii]==0 ){ -+ sqlite3_value *pVal = 0; -+ sqlite3changeset_new(pIter, ii, &pVal); -+ sqlite3_bind_int(pSelect, ii+1+nCol, (pVal==0)); -+ if( pVal ) rc = sessionBindValue(pSelect, ii+1, pVal); -+ } -+ } -+ } -+ - if( rc==SQLITE_OK ){ - rc = sqlite3_step(pSelect); - if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect); -@@ -206409,16 +232982,22 @@ static int sessionConflictHandler( - - /* Bind the new.* PRIMARY KEY values to the SELECT statement. */ - if( pbReplace ){ -- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect); -+ rc = sessionSeekToRow(pIter, p); - }else{ - rc = SQLITE_OK; - } - - if( rc==SQLITE_ROW ){ - /* There exists another row with the new.* primary key. */ -- pIter->pConflict = p->pSelect; -- res = xConflict(pCtx, eType, pIter); -- pIter->pConflict = 0; -+ if( p->bIgnoreNoop -+ && sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1) -+ ){ -+ res = SQLITE_CHANGESET_OMIT; -+ }else{ -+ pIter->pConflict = p->pSelect; -+ res = xConflict(pCtx, eType, pIter); -+ pIter->pConflict = 0; -+ } - rc = sqlite3_reset(p->pSelect); - }else if( rc==SQLITE_OK ){ - if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){ -@@ -206498,7 +233077,7 @@ static int sessionApplyOneOp( - int nCol; - int rc = SQLITE_OK; - -- assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect ); -+ assert( p->pDelete && p->pInsert && p->pSelect ); - assert( p->azCol && p->abPK ); - assert( !pbReplace || *pbReplace==0 ); - -@@ -206526,7 +233105,7 @@ static int sessionApplyOneOp( - - sqlite3_step(p->pDelete); - rc = sqlite3_reset(p->pDelete); -- if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ -+ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 && p->bIgnoreNoop==0 ){ - rc = sessionConflictHandler( - SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry - ); -@@ -206538,29 +233117,28 @@ static int sessionApplyOneOp( - - }else if( op==SQLITE_UPDATE ){ - int i; -+ sqlite3_stmt *pUp = 0; -+ int bPatchset = (pbRetry==0 || pIter->bPatchset); -+ -+ rc = sessionUpdateFind(pIter, p, bPatchset, &pUp); - - /* Bind values to the UPDATE statement. */ - for(i=0; rc==SQLITE_OK && ipUpdate, i*3+2, !!pNew); -- if( pOld ){ -- rc = sessionBindValue(p->pUpdate, i*3+1, pOld); -+ if( p->abPK[i] || (bPatchset==0 && pOld) ){ -+ rc = sessionBindValue(pUp, i*2+2, pOld); - } - if( rc==SQLITE_OK && pNew ){ -- rc = sessionBindValue(p->pUpdate, i*3+3, pNew); -+ rc = sessionBindValue(pUp, i*2+1, pNew); - } - } -- if( rc==SQLITE_OK ){ -- sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset); -- } - if( rc!=SQLITE_OK ) return rc; - - /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict, - ** the result will be SQLITE_OK with 0 rows modified. */ -- sqlite3_step(p->pUpdate); -- rc = sqlite3_reset(p->pUpdate); -+ sqlite3_step(pUp); -+ rc = sqlite3_reset(pUp); - - if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ - /* A NOTFOUND or DATA error. Search the table to see if it contains -@@ -206584,7 +233162,7 @@ static int sessionApplyOneOp( - /* Check if there is a conflicting row. For sqlite_stat1, this needs - ** to be done using a SELECT, as there is no PRIMARY KEY in the - ** database schema to throw an exception if a duplicate is inserted. */ -- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect); -+ rc = sessionSeekToRow(pIter, p); - if( rc==SQLITE_ROW ){ - rc = SQLITE_CONSTRAINT; - sqlite3_reset(p->pSelect); -@@ -206692,7 +233270,7 @@ static int sessionRetryConstraints( - memset(&pApply->constraints, 0, sizeof(SessionBuffer)); - - rc = sessionChangesetStart( -- &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints -+ &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1 - ); - if( rc==SQLITE_OK ){ - size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); -@@ -206754,14 +233332,21 @@ static int sessionChangesetApply( - int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ - SessionApplyCtx sApply; /* changeset_apply() context object */ - int bPatchset; -+ u64 savedFlag = db->flags & SQLITE_FkNoAction; - - assert( xConflict!=0 ); - -+ sqlite3_mutex_enter(sqlite3_db_mutex(db)); -+ if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ -+ db->flags |= ((u64)SQLITE_FkNoAction); -+ db->aDb[0].pSchema->schema_cookie -= 32; -+ } -+ - pIter->in.bNoDiscard = 1; - memset(&sApply, 0, sizeof(sApply)); - sApply.bRebase = (ppRebase && pnRebase); - sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); -- sqlite3_mutex_enter(sqlite3_db_mutex(db)); -+ sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); - if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ - rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); - } -@@ -206783,14 +233368,13 @@ static int sessionChangesetApply( - ); - if( rc!=SQLITE_OK ) break; - -+ sessionUpdateFree(&sApply); - sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ - sqlite3_finalize(sApply.pDelete); -- sqlite3_finalize(sApply.pUpdate); - sqlite3_finalize(sApply.pInsert); - sqlite3_finalize(sApply.pSelect); - sApply.db = db; - sApply.pDelete = 0; -- sApply.pUpdate = 0; - sApply.pInsert = 0; - sApply.pSelect = 0; - sApply.nCol = 0; -@@ -206799,6 +233383,7 @@ static int sessionChangesetApply( - sApply.bStat1 = 0; - sApply.bDeferConstraints = 1; - sApply.bRebaseStarted = 0; -+ sApply.bRowid = 0; - memset(&sApply.constraints, 0, sizeof(SessionBuffer)); - - /* If an xFilter() callback was specified, invoke it now. If the -@@ -206818,8 +233403,9 @@ static int sessionChangesetApply( - int i; - - sqlite3changeset_pk(pIter, &abPK, 0); -- rc = sessionTableInfo( -- db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK -+ rc = sessionTableInfo(0, db, "main", zNew, -+ &sApply.nCol, 0, &zTab, &sApply.azCol, 0, 0, -+ &sApply.abPK, &sApply.bRowid - ); - if( rc!=SQLITE_OK ) break; - for(i=0; iflags & SQLITE_FkNoAction ); -+ db->flags &= ~((u64)SQLITE_FkNoAction); -+ db->aDb[0].pSchema->schema_cookie -= 32; -+ } - sqlite3_mutex_leave(sqlite3_db_mutex(db)); - return rc; - } -@@ -206950,13 +233546,15 @@ SQLITE_API int sqlite3changeset_apply_v2( - int flags - ){ - sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ -- int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); -- int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse); -+ int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); -+ int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1); -+ - if( rc==SQLITE_OK ){ - rc = sessionChangesetApply( - db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags - ); - } -+ - return rc; - } - -@@ -207009,7 +233607,7 @@ SQLITE_API int sqlite3changeset_apply_v2_strm( - ){ - sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); -- int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse); -+ int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1); - if( rc==SQLITE_OK ){ - rc = sessionChangesetApply( - db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags -@@ -207044,6 +233642,10 @@ struct sqlite3_changegroup { - int rc; /* Error code */ - int bPatch; /* True to accumulate patchsets */ - SessionTable *pList; /* List of tables in current patch */ -+ SessionBuffer rec; -+ -+ sqlite3 *db; /* Configured by changegroup_schema() */ -+ char *zDb; /* Configured by changegroup_schema() */ - }; - - /* -@@ -207064,6 +233666,7 @@ static int sessionChangeMerge( - ){ - SessionChange *pNew = 0; - int rc = SQLITE_OK; -+ assert( aRec!=0 ); - - if( !pExist ){ - pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec); -@@ -207230,84 +233833,242 @@ static int sessionChangeMerge( - } - - /* --** Add all changes in the changeset traversed by the iterator passed as --** the first argument to the changegroup hash tables. -+** Check if a changeset entry with nCol columns and the PK array passed -+** as the final argument to this function is compatible with SessionTable -+** pTab. If so, return 1. Otherwise, if they are incompatible in some way, -+** return 0. - */ --static int sessionChangesetToHash( -- sqlite3_changeset_iter *pIter, /* Iterator to read from */ -- sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ -- int bRebase /* True if hash table is for rebasing */ -+static int sessionChangesetCheckCompat( -+ SessionTable *pTab, -+ int nCol, -+ u8 *abPK - ){ -- u8 *aRec; -- int nRec; -- int rc = SQLITE_OK; -- SessionTable *pTab = 0; -- -- while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ -- const char *zNew; -- int nCol; -- int op; -- int iHash; -- int bIndirect; -- SessionChange *pChange; -- SessionChange *pExist = 0; -- SessionChange **pp; -- -- if( pGrp->pList==0 ){ -- pGrp->bPatch = pIter->bPatchset; -- }else if( pIter->bPatchset!=pGrp->bPatch ){ -- rc = SQLITE_ERROR; -- break; -+ if( pTab->azCol && nColnCol ){ -+ int ii; -+ for(ii=0; iinCol; ii++){ -+ u8 bPK = (ii < nCol) ? abPK[ii] : 0; -+ if( pTab->abPK[ii]!=bPK ) return 0; - } -+ return 1; -+ } -+ return (pTab->nCol==nCol && 0==memcmp(abPK, pTab->abPK, nCol)); -+} - -- sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); -- if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ -- /* Search the list for a matching table */ -- int nNew = (int)strlen(zNew); -- u8 *abPK; -+static int sessionChangesetExtendRecord( -+ sqlite3_changegroup *pGrp, -+ SessionTable *pTab, -+ int nCol, -+ int op, -+ const u8 *aRec, -+ int nRec, -+ SessionBuffer *pOut -+){ -+ int rc = SQLITE_OK; -+ int ii = 0; - -- sqlite3changeset_pk(pIter, &abPK, 0); -- for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ -- if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; -+ assert( pTab->azCol ); -+ assert( nColnCol ); -+ -+ pOut->nBuf = 0; -+ if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){ -+ /* Append the missing default column values to the record. */ -+ sessionAppendBlob(pOut, aRec, nRec, &rc); -+ if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){ -+ rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt); -+ if( rc==SQLITE_OK && SQLITE_ROW!=sqlite3_step(pTab->pDfltStmt) ){ -+ rc = sqlite3_errcode(pGrp->db); - } -- if( !pTab ){ -- SessionTable **ppTab; -+ } -+ for(ii=nCol; rc==SQLITE_OK && iinCol; ii++){ -+ int eType = sqlite3_column_type(pTab->pDfltStmt, ii); -+ sessionAppendByte(pOut, eType, &rc); -+ switch( eType ){ -+ case SQLITE_FLOAT: -+ case SQLITE_INTEGER: { -+ i64 iVal; -+ if( eType==SQLITE_INTEGER ){ -+ iVal = sqlite3_column_int64(pTab->pDfltStmt, ii); -+ }else{ -+ double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii); -+ memcpy(&iVal, &rVal, sizeof(i64)); -+ } -+ if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ -+ sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); -+ pOut->nBuf += 8; -+ } -+ break; -+ } - -- pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); -- if( !pTab ){ -- rc = SQLITE_NOMEM; -+ case SQLITE_BLOB: -+ case SQLITE_TEXT: { -+ int n = sqlite3_column_bytes(pTab->pDfltStmt, ii); -+ sessionAppendVarint(pOut, n, &rc); -+ if( eType==SQLITE_TEXT ){ -+ const u8 *z = (const u8*)sqlite3_column_text(pTab->pDfltStmt, ii); -+ sessionAppendBlob(pOut, z, n, &rc); -+ }else{ -+ const u8 *z = (const u8*)sqlite3_column_blob(pTab->pDfltStmt, ii); -+ sessionAppendBlob(pOut, z, n, &rc); -+ } - break; - } -- memset(pTab, 0, sizeof(SessionTable)); -- pTab->nCol = nCol; -- pTab->abPK = (u8*)&pTab[1]; -- memcpy(pTab->abPK, abPK, nCol); -- pTab->zName = (char*)&pTab->abPK[nCol]; -- memcpy(pTab->zName, zNew, nNew+1); -- -- /* The new object must be linked on to the end of the list, not -- ** simply added to the start of it. This is to ensure that the -- ** tables within the output of sqlite3changegroup_output() are in -- ** the right order. */ -- for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext); -- *ppTab = pTab; -- }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){ -- rc = SQLITE_SCHEMA; -- break; -+ -+ default: -+ assert( eType==SQLITE_NULL ); -+ break; -+ } -+ } -+ }else if( op==SQLITE_UPDATE ){ -+ /* Append missing "undefined" entries to the old.* record. And, if this -+ ** is an UPDATE, to the new.* record as well. */ -+ int iOff = 0; -+ if( pGrp->bPatch==0 ){ -+ for(ii=0; iinCol-nCol); ii++){ -+ sessionAppendByte(pOut, 0x00, &rc); - } - } - -- if( sessionGrowHash(pIter->bPatchset, pTab) ){ -- rc = SQLITE_NOMEM; -- break; -+ sessionAppendBlob(pOut, &aRec[iOff], nRec-iOff, &rc); -+ for(ii=0; ii<(pTab->nCol-nCol); ii++){ -+ sessionAppendByte(pOut, 0x00, &rc); -+ } -+ }else{ -+ assert( op==SQLITE_DELETE && pGrp->bPatch ); -+ sessionAppendBlob(pOut, aRec, nRec, &rc); -+ } -+ -+ return rc; -+} -+ -+/* -+** Locate or create a SessionTable object that may be used to add the -+** change currently pointed to by iterator pIter to changegroup pGrp. -+** If successful, set output variable (*ppTab) to point to the table -+** object and return SQLITE_OK. Otherwise, if some error occurs, return -+** an SQLite error code and leave (*ppTab) set to NULL. -+*/ -+static int sessionChangesetFindTable( -+ sqlite3_changegroup *pGrp, -+ const char *zTab, -+ sqlite3_changeset_iter *pIter, -+ SessionTable **ppTab -+){ -+ int rc = SQLITE_OK; -+ SessionTable *pTab = 0; -+ int nTab = (int)strlen(zTab); -+ u8 *abPK = 0; -+ int nCol = 0; -+ -+ *ppTab = 0; -+ sqlite3changeset_pk(pIter, &abPK, &nCol); -+ -+ /* Search the list for an existing table */ -+ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ -+ if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; -+ } -+ -+ /* If one was not found above, create a new table now */ -+ if( !pTab ){ -+ SessionTable **ppNew; -+ -+ pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1); -+ if( !pTab ){ -+ return SQLITE_NOMEM; -+ } -+ memset(pTab, 0, sizeof(SessionTable)); -+ pTab->nCol = nCol; -+ pTab->abPK = (u8*)&pTab[1]; -+ memcpy(pTab->abPK, abPK, nCol); -+ pTab->zName = (char*)&pTab->abPK[nCol]; -+ memcpy(pTab->zName, zTab, nTab+1); -+ -+ if( pGrp->db ){ -+ pTab->nCol = 0; -+ rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); -+ if( rc ){ -+ assert( pTab->azCol==0 ); -+ sqlite3_free(pTab); -+ return rc; -+ } - } -+ -+ /* The new object must be linked on to the end of the list, not -+ ** simply added to the start of it. This is to ensure that the -+ ** tables within the output of sqlite3changegroup_output() are in -+ ** the right order. */ -+ for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext); -+ *ppNew = pTab; -+ } -+ -+ /* Check that the table is compatible. */ -+ if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ -+ rc = SQLITE_SCHEMA; -+ } -+ -+ *ppTab = pTab; -+ return rc; -+} -+ -+/* -+** Add the change currently indicated by iterator pIter to the hash table -+** belonging to changegroup pGrp. -+*/ -+static int sessionOneChangeToHash( -+ sqlite3_changegroup *pGrp, -+ sqlite3_changeset_iter *pIter, -+ int bRebase -+){ -+ int rc = SQLITE_OK; -+ int nCol = 0; -+ int op = 0; -+ int iHash = 0; -+ int bIndirect = 0; -+ SessionChange *pChange = 0; -+ SessionChange *pExist = 0; -+ SessionChange **pp = 0; -+ SessionTable *pTab = 0; -+ u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; -+ int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; -+ -+ assert( nRec>0 ); -+ -+ /* Ensure that only changesets, or only patchsets, but not a mixture -+ ** of both, are being combined. It is an error to try to combine a -+ ** changeset and a patchset. */ -+ if( pGrp->pList==0 ){ -+ pGrp->bPatch = pIter->bPatchset; -+ }else if( pIter->bPatchset!=pGrp->bPatch ){ -+ rc = SQLITE_ERROR; -+ } -+ -+ if( rc==SQLITE_OK ){ -+ const char *zTab = 0; -+ sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); -+ rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); -+ } -+ -+ if( rc==SQLITE_OK && nColnCol ){ -+ SessionBuffer *pBuf = &pGrp->rec; -+ rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); -+ aRec = pBuf->aBuf; -+ nRec = pBuf->nBuf; -+ assert( pGrp->db ); -+ } -+ -+ if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ -+ rc = SQLITE_NOMEM; -+ } -+ -+ if( rc==SQLITE_OK ){ -+ /* Search for existing entry. If found, remove it from the hash table. -+ ** Code below may link it back in. */ - iHash = sessionChangeHash( - pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange - ); -- -- /* Search for existing entry. If found, remove it from the hash table. -- ** Code below may link it back in. -- */ - for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ - int bPkOnly1 = 0; - int bPkOnly2 = 0; -@@ -207322,16 +234083,40 @@ static int sessionChangesetToHash( - break; - } - } -+ } - -+ if( rc==SQLITE_OK ){ - rc = sessionChangeMerge(pTab, bRebase, - pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange - ); -- if( rc ) break; -- if( pChange ){ -- pChange->pNext = pTab->apChange[iHash]; -- pTab->apChange[iHash] = pChange; -- pTab->nEntry++; -- } -+ } -+ if( rc==SQLITE_OK && pChange ){ -+ pChange->pNext = pTab->apChange[iHash]; -+ pTab->apChange[iHash] = pChange; -+ pTab->nEntry++; -+ } -+ -+ if( rc==SQLITE_OK ) rc = pIter->rc; -+ return rc; -+} -+ -+/* -+** Add all changes in the changeset traversed by the iterator passed as -+** the first argument to the changegroup hash tables. -+*/ -+static int sessionChangesetToHash( -+ sqlite3_changeset_iter *pIter, /* Iterator to read from */ -+ sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ -+ int bRebase /* True if hash table is for rebasing */ -+){ -+ u8 *aRec; -+ int nRec; -+ int rc = SQLITE_OK; -+ -+ pIter->in.bNoDiscard = 1; -+ while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ -+ rc = sessionOneChangeToHash(pGrp, pIter, bRebase); -+ if( rc!=SQLITE_OK ) break; - } - - if( rc==SQLITE_OK ) rc = pIter->rc; -@@ -207393,9 +234178,9 @@ static int sessionChangegroupOutput( - if( rc==SQLITE_OK ){ - if( xOutput ){ - if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf); -- }else{ -+ }else if( ppOut ){ - *ppOut = buf.aBuf; -- *pnOut = buf.nBuf; -+ if( pnOut ) *pnOut = buf.nBuf; - buf.aBuf = 0; - } - } -@@ -207420,6 +234205,31 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){ - return rc; - } - -+/* -+** Provide a database schema to the changegroup object. -+*/ -+SQLITE_API int sqlite3changegroup_schema( -+ sqlite3_changegroup *pGrp, -+ sqlite3 *db, -+ const char *zDb -+){ -+ int rc = SQLITE_OK; -+ -+ if( pGrp->pList || pGrp->db ){ -+ /* Cannot add a schema after one or more calls to sqlite3changegroup_add(), -+ ** or after sqlite3changegroup_schema() has already been called. */ -+ rc = SQLITE_MISUSE; -+ }else{ -+ pGrp->zDb = sqlite3_mprintf("%s", zDb); -+ if( pGrp->zDb==0 ){ -+ rc = SQLITE_NOMEM; -+ }else{ -+ pGrp->db = db; -+ } -+ } -+ return rc; -+} -+ - /* - ** Add the changeset currently stored in buffer pData, size nData bytes, - ** to changeset-group p. -@@ -207436,6 +234246,28 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void - return rc; - } - -+/* -+** Add a single change to a changeset-group. -+*/ -+SQLITE_API int sqlite3changegroup_add_change( -+ sqlite3_changegroup *pGrp, -+ sqlite3_changeset_iter *pIter -+){ -+ int rc = SQLITE_OK; -+ -+ if( pIter->in.iCurrent==pIter->in.iNext -+ || pIter->rc!=SQLITE_OK -+ || pIter->bInvert -+ ){ -+ /* Iterator does not point to any valid entry or is an INVERT iterator. */ -+ rc = SQLITE_ERROR; -+ }else{ -+ pIter->in.bNoDiscard = 1; -+ rc = sessionOneChangeToHash(pGrp, pIter, 0); -+ } -+ return rc; -+} -+ - /* - ** Obtain a buffer containing a changeset representing the concatenation - ** of all changesets added to the group so far. -@@ -207483,7 +234315,9 @@ SQLITE_API int sqlite3changegroup_output_strm( - */ - SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ - if( pGrp ){ -- sessionDeleteTable(pGrp->pList); -+ sqlite3_free(pGrp->zDb); -+ sessionDeleteTable(0, pGrp->pList); -+ sqlite3_free(pGrp->rec.aBuf); - sqlite3_free(pGrp); - } - } -@@ -207629,10 +234463,10 @@ static void sessionAppendPartialUpdate( - int n1 = sessionSerialLen(a1); - int n2 = sessionSerialLen(a2); - if( pIter->abPK[i] || a2[0]==0 ){ -- if( !pIter->abPK[i] ) bData = 1; -+ if( !pIter->abPK[i] && a1[0] ) bData = 1; - memcpy(pOut, a1, n1); - pOut += n1; -- }else if( a2[0]!=0xFF ){ -+ }else if( a2[0]!=0xFF && a1[0] ){ - bData = 1; - memcpy(pOut, a2, n2); - pOut += n2; -@@ -207795,7 +234629,7 @@ static int sessionRebase( - if( sOut.nBuf>0 ){ - rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); - } -- }else{ -+ }else if( ppOut ){ - *ppOut = (void*)sOut.aBuf; - *pnOut = sOut.nBuf; - sOut.aBuf = 0; -@@ -207884,7 +234718,8 @@ SQLITE_API int sqlite3rebaser_rebase_strm( - */ - SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ - if( p ){ -- sessionDeleteTable(p->grp.pList); -+ sessionDeleteTable(0, p->grp.pList); -+ sqlite3_free(p->grp.rec.aBuf); - sqlite3_free(p); - } - } -@@ -207915,7 +234750,27 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){ - /************** End of sqlite3session.c **************************************/ - /************** Begin file fts5.c ********************************************/ - -- -+/* -+** This, the "fts5.c" source file, is a composite file that is itself -+** assembled from the following files: -+** -+** fts5.h -+** fts5Int.h -+** fts5parse.h <--- Generated from fts5parse.y by Lemon -+** fts5parse.c <--- Generated from fts5parse.y by Lemon -+** fts5_aux.c -+** fts5_buffer.c -+** fts5_config.c -+** fts5_expr.c -+** fts5_hash.c -+** fts5_index.c -+** fts5_main.c -+** fts5_storage.c -+** fts5_tokenize.c -+** fts5_unicode2.c -+** fts5_varint.c -+** fts5_vocab.c -+*/ - #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) - - #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) -@@ -207925,6 +234780,12 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){ - # undef NDEBUG - #endif - -+#ifdef HAVE_STDINT_H -+/* #include */ -+#endif -+#ifdef HAVE_INTTYPES_H -+/* #include */ -+#endif - /* - ** 2014 May 31 - ** -@@ -207982,8 +234843,8 @@ struct Fts5PhraseIter { - ** EXTENSION API FUNCTIONS - ** - ** xUserData(pFts): --** Return a copy of the context pointer the extension function was --** registered with. -+** Return a copy of the pUserData pointer passed to the xCreateFunction() -+** API when the extension function was registered. - ** - ** xColumnTotalSize(pFts, iCol, pnToken): - ** If parameter iCol is less than zero, set output variable *pnToken -@@ -208015,8 +234876,11 @@ struct Fts5PhraseIter { - ** created with the "columnsize=0" option. - ** - ** xColumnText: --** This function attempts to retrieve the text of column iCol of the --** current document. If successful, (*pz) is set to point to a buffer -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of columns in the table, SQLITE_RANGE is returned. -+** -+** Otherwise, this function attempts to retrieve the text of column iCol of -+** the current document. If successful, (*pz) is set to point to a buffer - ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes - ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, - ** if an error occurs, an SQLite error code is returned and the final values -@@ -208026,8 +234890,10 @@ struct Fts5PhraseIter { - ** Returns the number of phrases in the current query expression. - ** - ** xPhraseSize: --** Returns the number of tokens in phrase iPhrase of the query. Phrases --** are numbered starting from zero. -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of phrases in the current query, as returned by xPhraseCount, -+** 0 is returned. Otherwise, this function returns the number of tokens in -+** phrase iPhrase of the query. Phrases are numbered starting from zero. - ** - ** xInstCount: - ** Set *pnInst to the total number of occurrences of all phrases within -@@ -208043,12 +234909,13 @@ struct Fts5PhraseIter { - ** Query for the details of phrase match iIdx within the current row. - ** Phrase matches are numbered starting from zero, so the iIdx argument - ** should be greater than or equal to zero and smaller than the value --** output by xInstCount(). -+** output by xInstCount(). If iIdx is less than zero or greater than -+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. - ** --** Usually, output parameter *piPhrase is set to the phrase number, *piCol -+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol - ** to the column in which it occurs and *piOff the token offset of the --** first token of the phrase. Returns SQLITE_OK if successful, or an error --** code (i.e. SQLITE_NOMEM) if an error occurs. -+** first token of the phrase. SQLITE_OK is returned if successful, or an -+** error code (i.e. SQLITE_NOMEM) if an error occurs. - ** - ** This API can be quite slow if used with an FTS5 table created with the - ** "detail=none" or "detail=column" option. -@@ -208074,6 +234941,10 @@ struct Fts5PhraseIter { - ** Invoking Api.xUserData() returns a copy of the pointer passed as - ** the third argument to pUserData. - ** -+** If parameter iPhrase is less than zero, or greater than or equal to -+** the number of phrases in the query, as returned by xPhraseCount(), -+** this function returns SQLITE_RANGE. -+** - ** If the callback function returns any value other than SQLITE_OK, the - ** query is abandoned and the xQueryPhrase function returns immediately. - ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. -@@ -208155,6 +235026,10 @@ struct Fts5PhraseIter { - ** (i.e. if it is a contentless table), then this API always iterates - ** through an empty set (all calls to xPhraseFirst() set iCol to -1). - ** -+** In all cases, matches are visited in (column ASC, offset ASC) order. -+** i.e. all those in column 0, sorted by offset, followed by those in -+** column 1, etc. -+** - ** xPhraseNext() - ** See xPhraseFirst above. - ** -@@ -208188,9 +235063,80 @@ struct Fts5PhraseIter { - ** - ** xPhraseNextColumn() - ** See xPhraseFirstColumn above. -+** -+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) -+** This is used to access token iToken of phrase iPhrase of the current -+** query. Before returning, output parameter *ppToken is set to point -+** to a buffer containing the requested token, and *pnToken to the -+** size of this buffer in bytes. -+** -+** If iPhrase or iToken are less than zero, or if iPhrase is greater than -+** or equal to the number of phrases in the query as reported by -+** xPhraseCount(), or if iToken is equal to or greater than the number of -+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken -+ are both zeroed. -+** -+** The output text is not a copy of the query text that specified the -+** token. It is the output of the tokenizer module. For tokendata=1 -+** tables, this includes any embedded 0x00 and trailing data. -+** -+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) -+** This is used to access token iToken of phrase hit iIdx within the -+** current row. If iIdx is less than zero or greater than or equal to the -+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, -+** output variable (*ppToken) is set to point to a buffer containing the -+** matching document token, and (*pnToken) to the size of that buffer in -+** bytes. -+** -+** The output text is not a copy of the document text that was tokenized. -+** It is the output of the tokenizer module. For tokendata=1 tables, this -+** includes any embedded 0x00 and trailing data. -+** -+** This API may be slow in some cases if the token identified by parameters -+** iIdx and iToken matched a prefix token in the query. In most cases, the -+** first call to this API for each prefix token in the query is forced -+** to scan the portion of the full-text index that matches the prefix -+** token to collect the extra data required by this API. If the prefix -+** token matches a large number of token instances in the document set, -+** this may be a performance problem. -+** -+** If the user knows in advance that a query may use this API for a -+** prefix token, FTS5 may be configured to collect all required data as part -+** of the initial querying of the full-text index, avoiding the second scan -+** entirely. This also causes prefix queries that do not use this API to -+** run more slowly and use more memory. FTS5 may be configured in this way -+** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] -+** option, or on a per-query basis using the -+** [fts5_insttoken | fts5_insttoken()] user function. -+** -+** This API can be quite slow if used with an FTS5 table created with the -+** "detail=none" or "detail=column" option. -+** -+** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of columns in the table, SQLITE_RANGE is returned. -+** -+** Otherwise, this function attempts to retrieve the locale associated -+** with column iCol of the current row. Usually, there is no associated -+** locale, and output parameters (*pzLocale) and (*pnLocale) are set -+** to NULL and 0, respectively. However, if the fts5_locale() function -+** was used to associate a locale with the value when it was inserted -+** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated -+** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) -+** is set to the size in bytes of the buffer, not including the -+** nul-terminator. -+** -+** If successful, SQLITE_OK is returned. Or, if an error occurs, an -+** SQLite error code is returned. The final value of the output parameters -+** is undefined in this case. -+** -+** xTokenize_v2: -+** Tokenize text using the tokenizer belonging to the FTS5 table. This -+** API is the same as the xTokenize() API, except that it allows a tokenizer -+** locale to be specified. - */ - struct Fts5ExtensionApi { -- int iVersion; /* Currently always set to 3 */ -+ int iVersion; /* Currently always set to 4 */ - - void *(*xUserData)(Fts5Context*); - -@@ -208225,6 +235171,22 @@ struct Fts5ExtensionApi { - - int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); - void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); -+ -+ /* Below this point are iVersion>=3 only */ -+ int (*xQueryToken)(Fts5Context*, -+ int iPhrase, int iToken, -+ const char **ppToken, int *pnToken -+ ); -+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); -+ -+ /* Below this point are iVersion>=4 only */ -+ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); -+ int (*xTokenize_v2)(Fts5Context*, -+ const char *pText, int nText, /* Text to tokenize */ -+ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ -+ void *pCtx, /* Context passed to xToken() */ -+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ -+ ); - }; - - /* -@@ -208245,7 +235207,7 @@ struct Fts5ExtensionApi { - ** A tokenizer instance is required to actually tokenize text. - ** - ** The first argument passed to this function is a copy of the (void*) --** pointer provided by the application when the fts5_tokenizer object -+** pointer provided by the application when the fts5_tokenizer_v2 object - ** was registered with FTS5 (the third argument to xCreateTokenizer()). - ** The second and third arguments are an array of nul-terminated strings - ** containing the tokenizer arguments, if any, specified following the -@@ -208269,7 +235231,7 @@ struct Fts5ExtensionApi { - ** argument passed to this function is a pointer to an Fts5Tokenizer object - ** returned by an earlier call to xCreate(). - ** --** The second argument indicates the reason that FTS5 is requesting -+** The third argument indicates the reason that FTS5 is requesting - ** tokenization of the supplied text. This is always one of the following - ** four values: - ** -@@ -208293,6 +235255,13 @@ struct Fts5ExtensionApi { - ** on a columnsize=0 database. - ** - ** -+** The sixth and seventh arguments passed to xTokenize() - pLocale and -+** nLocale - are a pointer to a buffer containing the locale to use for -+** tokenization (e.g. "en_US") and its size in bytes, respectively. The -+** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in -+** which case nLocale is always 0) to indicate that the tokenizer should -+** use its default locale. -+** - ** For each token in the input string, the supplied callback xToken() must - ** be invoked. The first argument to it should be a copy of the pointer - ** passed as the second argument to xTokenize(). The third and fourth -@@ -208316,6 +235285,30 @@ struct Fts5ExtensionApi { - ** may abandon the tokenization and return any error code other than - ** SQLITE_OK or SQLITE_DONE. - ** -+** If the tokenizer is registered using an fts5_tokenizer_v2 object, -+** then the xTokenize() method has two additional arguments - pLocale -+** and nLocale. These specify the locale that the tokenizer should use -+** for the current request. If pLocale and nLocale are both 0, then the -+** tokenizer should use its default locale. Otherwise, pLocale points to -+** an nLocale byte buffer containing the name of the locale to use as utf-8 -+** text. pLocale is not nul-terminated. -+** -+** FTS5_TOKENIZER -+** -+** There is also an fts5_tokenizer object. This is an older, deprecated, -+** version of fts5_tokenizer_v2. It is similar except that: -+** -+**
          -+**
        • There is no "iVersion" field, and -+**
        • The xTokenize() method does not take a locale argument. -+**
        -+** -+** Legacy fts5_tokenizer tokenizers must be registered using the -+** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). -+** -+** Tokenizer implementations registered using either API may be retrieved -+** using both xFindTokenizer() and xFindTokenizer_v2(). -+** - ** SYNONYM SUPPORT - ** - ** Custom tokenizers may also support synonyms. Consider a case in which a -@@ -208419,11 +235412,38 @@ struct Fts5ExtensionApi { - ** as separate queries of the FTS index are required for each synonym. - ** - ** When using methods (2) or (3), it is important that the tokenizer only --** provide synonyms when tokenizing document text (method (2)) or query --** text (method (3)), not both. Doing so will not cause any errors, but is -+** provide synonyms when tokenizing document text (method (3)) or query -+** text (method (2)), not both. Doing so will not cause any errors, but is - ** inefficient. - */ - typedef struct Fts5Tokenizer Fts5Tokenizer; -+typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; -+struct fts5_tokenizer_v2 { -+ int iVersion; /* Currently always 2 */ -+ -+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); -+ void (*xDelete)(Fts5Tokenizer*); -+ int (*xTokenize)(Fts5Tokenizer*, -+ void *pCtx, -+ int flags, /* Mask of FTS5_TOKENIZE_* flags */ -+ const char *pText, int nText, -+ const char *pLocale, int nLocale, -+ int (*xToken)( -+ void *pCtx, /* Copy of 2nd argument to xTokenize() */ -+ int tflags, /* Mask of FTS5_TOKEN_* flags */ -+ const char *pToken, /* Pointer to buffer containing token */ -+ int nToken, /* Size of token in bytes */ -+ int iStart, /* Byte offset of token within input text */ -+ int iEnd /* Byte offset of end of token within input text */ -+ ) -+ ); -+}; -+ -+/* -+** New code should use the fts5_tokenizer_v2 type to define tokenizer -+** implementations. The following type is included for legacy applications -+** that still use it. -+*/ - typedef struct fts5_tokenizer fts5_tokenizer; - struct fts5_tokenizer { - int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); -@@ -208443,6 +235463,7 @@ struct fts5_tokenizer { - ); - }; - -+ - /* Flags that may be passed as the third argument to xTokenize() */ - #define FTS5_TOKENIZE_QUERY 0x0001 - #define FTS5_TOKENIZE_PREFIX 0x0002 -@@ -208462,13 +235483,13 @@ struct fts5_tokenizer { - */ - typedef struct fts5_api fts5_api; - struct fts5_api { -- int iVersion; /* Currently always set to 2 */ -+ int iVersion; /* Currently always set to 3 */ - - /* Create a new tokenizer */ - int (*xCreateTokenizer)( - fts5_api *pApi, - const char *zName, -- void *pContext, -+ void *pUserData, - fts5_tokenizer *pTokenizer, - void (*xDestroy)(void*) - ); -@@ -208477,7 +235498,7 @@ struct fts5_api { - int (*xFindTokenizer)( - fts5_api *pApi, - const char *zName, -- void **ppContext, -+ void **ppUserData, - fts5_tokenizer *pTokenizer - ); - -@@ -208485,10 +235506,29 @@ struct fts5_api { - int (*xCreateFunction)( - fts5_api *pApi, - const char *zName, -- void *pContext, -+ void *pUserData, - fts5_extension_function xFunction, - void (*xDestroy)(void*) - ); -+ -+ /* APIs below this point are only available if iVersion>=3 */ -+ -+ /* Create a new tokenizer */ -+ int (*xCreateTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void *pUserData, -+ fts5_tokenizer_v2 *pTokenizer, -+ void (*xDestroy)(void*) -+ ); -+ -+ /* Find an existing tokenizer */ -+ int (*xFindTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void **ppUserData, -+ fts5_tokenizer_v2 **ppTokenizer -+ ); - }; - - /* -@@ -208523,6 +235563,7 @@ SQLITE_EXTENSION_INIT1 - - /* #include */ - /* #include */ -+/* #include */ - - #ifndef SQLITE_AMALGAMATION - -@@ -208538,8 +235579,20 @@ typedef sqlite3_uint64 u64; - #endif - - #define testcase(x) --#define ALWAYS(x) 1 --#define NEVER(x) 0 -+ -+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) -+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1 -+#endif -+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS) -+# define ALWAYS(X) (1) -+# define NEVER(X) (0) -+#elif !defined(NDEBUG) -+# define ALWAYS(X) ((X)?1:(assert(0),0)) -+# define NEVER(X) ((X)?(assert(0),1):0) -+#else -+# define ALWAYS(X) (X) -+# define NEVER(X) (X) -+#endif - - #define MIN(x,y) (((x) < (y)) ? (x) : (y)) - #define MAX(x,y) (((x) > (y)) ? (x) : (y)) -@@ -208550,6 +235603,34 @@ typedef sqlite3_uint64 u64; - # define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) - # define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) - -+/* The uptr type is an unsigned integer large enough to hold a pointer -+*/ -+#if defined(HAVE_STDINT_H) -+ typedef uintptr_t uptr; -+#elif SQLITE_PTRSIZE==4 -+ typedef u32 uptr; -+#else -+ typedef u64 uptr; -+#endif -+ -+#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC -+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) -+#else -+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) -+#endif -+ -+/* -+** Macros needed to provide flexible arrays in a portable way -+*/ -+#ifndef offsetof -+# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) -+#endif -+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -+# define FLEXARRAY -+#else -+# define FLEXARRAY 1 -+#endif -+ - #endif - - /* Truncate very long tokens to this many bytes. Hard limit is -@@ -208599,7 +235680,7 @@ SQLITE_API extern int sqlite3_fts5_may_be_corrupt; - ** A version of memcmp() that does not cause asan errors if one of the pointer - ** parameters is NULL and the number of bytes to compare is zero. - */ --#define fts5Memcmp(s1, s2, n) ((n)==0 ? 0 : memcmp((s1), (s2), (n))) -+#define fts5Memcmp(s1, s2, n) ((n)<=0 ? 0 : memcmp((s1), (s2), (n))) - - /* Mark a function parameter as unused, to suppress nuisance compiler - ** warnings. */ -@@ -208622,10 +235703,11 @@ typedef struct Fts5Colset Fts5Colset; - */ - struct Fts5Colset { - int nCol; -- int aiCol[1]; -+ int aiCol[FLEXARRAY]; - }; - -- -+/* Size (int bytes) of a complete Fts5Colset object with N columns. */ -+#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2)) - - /************************************************************************** - ** Interface to code in fts5_config.c. fts5_config.c contains contains code -@@ -208633,6 +235715,18 @@ struct Fts5Colset { - */ - - typedef struct Fts5Config Fts5Config; -+typedef struct Fts5TokenizerConfig Fts5TokenizerConfig; -+ -+struct Fts5TokenizerConfig { -+ Fts5Tokenizer *pTok; -+ fts5_tokenizer_v2 *pApi2; -+ fts5_tokenizer *pApi1; -+ const char **azArg; -+ int nArg; -+ int ePattern; /* FTS_PATTERN_XXX constant */ -+ const char *pLocale; /* Current locale to use */ -+ int nLocale; /* Size of pLocale in bytes */ -+}; - - /* - ** An instance of the following structure encodes all information that can -@@ -208645,6 +235739,10 @@ typedef struct Fts5Config Fts5Config; - ** attempt to merge together. A value of 1 sets the object to use the - ** compile time default. Zero disables auto-merge altogether. - ** -+** bContentlessDelete: -+** True if the contentless_delete option was present in the CREATE -+** VIRTUAL TABLE statement. -+** - ** zContent: - ** - ** zContentRowid: -@@ -208668,9 +235766,12 @@ typedef struct Fts5Config Fts5Config; - ** - ** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex); - ** -+** bLocale: -+** Set to true if locale=1 was specified when the table was created. - */ - struct Fts5Config { - sqlite3 *db; /* Database handle */ -+ Fts5Global *pGlobal; /* Global fts5 object for handle db */ - char *zDb; /* Database holding FTS index (e.g. "main") */ - char *zName; /* Name of FTS index */ - int nCol; /* Number of columns */ -@@ -208679,16 +235780,21 @@ struct Fts5Config { - int nPrefix; /* Number of prefix indexes */ - int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ - int eContent; /* An FTS5_CONTENT value */ -+ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ -+ int bContentlessUnindexed; /* "contentless_unindexed=" option (dflt=0) */ - char *zContent; /* content table */ - char *zContentRowid; /* "content_rowid=" option value */ - int bColumnsize; /* "columnsize=" option value (dflt==1) */ -+ int bTokendata; /* "tokendata=" option value (dflt==0) */ -+ int bLocale; /* "locale=" option value (dflt==0) */ - int eDetail; /* FTS5_DETAIL_XXX value */ - char *zContentExprlist; -- Fts5Tokenizer *pTok; -- fts5_tokenizer *pTokApi; -+ Fts5TokenizerConfig t; - int bLock; /* True when table is preparing statement */ - -+ - /* Values loaded from the %_config table */ -+ int iVersion; /* fts5 file format 'version' */ - int iCookie; /* Incremented when %_config is modified */ - int pgsz; /* Approximate page size used in %_data */ - int nAutomerge; /* 'automerge' setting */ -@@ -208697,6 +235803,9 @@ struct Fts5Config { - int nHashSize; /* Bytes of memory for in-memory hash */ - char *zRank; /* Name of rank function */ - char *zRankArgs; /* Arguments to rank function */ -+ int bSecureDelete; /* 'secure-delete' */ -+ int nDeleteMerge; /* 'deletemerge' */ -+ int bPrefixInsttoken; /* 'prefix-insttoken' */ - - /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ - char **pzErrmsg; -@@ -208706,18 +235815,24 @@ struct Fts5Config { - #endif - }; - --/* Current expected value of %_config table 'version' field */ --#define FTS5_CURRENT_VERSION 4 -+/* Current expected value of %_config table 'version' field. And -+** the expected version if the 'secure-delete' option has ever been -+** set on the table. */ -+#define FTS5_CURRENT_VERSION 4 -+#define FTS5_CURRENT_VERSION_SECUREDELETE 5 - --#define FTS5_CONTENT_NORMAL 0 --#define FTS5_CONTENT_NONE 1 --#define FTS5_CONTENT_EXTERNAL 2 -- --#define FTS5_DETAIL_FULL 0 --#define FTS5_DETAIL_NONE 1 --#define FTS5_DETAIL_COLUMNS 2 -+#define FTS5_CONTENT_NORMAL 0 -+#define FTS5_CONTENT_NONE 1 -+#define FTS5_CONTENT_EXTERNAL 2 -+#define FTS5_CONTENT_UNINDEXED 3 - -+#define FTS5_DETAIL_FULL 0 -+#define FTS5_DETAIL_NONE 1 -+#define FTS5_DETAIL_COLUMNS 2 - -+#define FTS5_PATTERN_NONE 0 -+#define FTS5_PATTERN_LIKE 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */ -+#define FTS5_PATTERN_GLOB 66 /* matches SQLITE_INDEX_CONSTRAINT_GLOB */ - - static int sqlite3Fts5ConfigParse( - Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** -@@ -208744,6 +235859,8 @@ static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, i - - static int sqlite3Fts5ConfigParseRank(const char*, char**, char**); - -+static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...); -+ - /* - ** End of interface to code in fts5_config.c. - **************************************************************************/ -@@ -208774,7 +235891,7 @@ static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...); - static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...); - - #define fts5BufferZero(x) sqlite3Fts5BufferZero(x) --#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,c) -+#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,(i64)c) - #define fts5BufferFree(a) sqlite3Fts5BufferFree(a) - #define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d) - #define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d) -@@ -208788,7 +235905,7 @@ static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...); - static void sqlite3Fts5Put32(u8*, int); - static int sqlite3Fts5Get32(const u8*); - --#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32) -+#define FTS5_POS2COLUMN(iPos) (int)((iPos >> 32) & 0x7FFFFFFF) - #define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF) - - typedef struct Fts5PoslistReader Fts5PoslistReader; -@@ -208861,16 +235978,19 @@ struct Fts5IndexIter { - /* - ** Values used as part of the flags argument passed to IndexQuery(). - */ --#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ --#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ --#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ --#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ -+#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ -+#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ -+#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ -+#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ - - /* The following are used internally by the fts5_index.c module. They are - ** defined here only to make it easier to avoid clashes with the flags - ** above. */ --#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 --#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 -+#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 -+#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 -+#define FTS5INDEX_QUERY_SKIPHASH 0x0040 -+#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080 -+#define FTS5INDEX_QUERY_SCANONETERM 0x0100 - - /* - ** Create/destroy an Fts5Index object. -@@ -208935,7 +236055,21 @@ static void sqlite3Fts5IndexCloseReader(Fts5Index*); - */ - static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*); - static int sqlite3Fts5IterNextScan(Fts5IndexIter*); -+static void *sqlite3Fts5StructureRef(Fts5Index*); -+static void sqlite3Fts5StructureRelease(void*); -+static int sqlite3Fts5StructureTest(Fts5Index*, void*); - -+/* -+** Used by xInstToken(): -+*/ -+static int sqlite3Fts5IterToken( -+ Fts5IndexIter *pIndexIter, -+ const char *pToken, int nToken, -+ i64 iRowid, -+ int iCol, -+ int iOff, -+ const char **ppOut, int *pnOut -+); - - /* - ** Insert or remove data to or from the index. Each time a document is -@@ -208987,7 +236121,7 @@ static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int); - /* - ** Functions called by the storage module as part of integrity-check. - */ --static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum); -+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum, int bUseCksum); - - /* - ** Called during virtual module initialization to register UDF -@@ -209010,6 +236144,16 @@ static int sqlite3Fts5IndexReset(Fts5Index *p); - - static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); - -+static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); -+static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); -+ -+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); -+ -+/* Used to populate hash tables for xInstToken in detail=none/column mode. */ -+static int sqlite3Fts5IndexIterWriteTokendata( -+ Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff -+); -+ - /* - ** End of interface to code in fts5_index.c. - **************************************************************************/ -@@ -209022,7 +236166,7 @@ static int sqlite3Fts5GetVarintLen(u32 iVal); - static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*); - static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); - --#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b) -+#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&(b)) - #define fts5GetVarint sqlite3Fts5GetVarint - - #define fts5FastGetVarint32(a, iOff, nVal) { \ -@@ -209053,19 +236197,20 @@ struct Fts5Table { - Fts5Index *pIndex; /* Full-text index */ - }; - --static int sqlite3Fts5GetTokenizer( -- Fts5Global*, -- const char **azArg, -- int nArg, -- Fts5Tokenizer**, -- fts5_tokenizer**, -- char **pzErr --); -+static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig); - - static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); - - static int sqlite3Fts5FlushToDisk(Fts5Table*); - -+static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); -+static void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc); -+ -+static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal); -+static int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal, -+ const char **ppText, int *pnText, const char **ppLoc, int *pnLoc -+); -+ - /* - ** End of interface to code in fts5.c. - **************************************************************************/ -@@ -209095,6 +236240,11 @@ static int sqlite3Fts5HashWrite( - */ - static void sqlite3Fts5HashClear(Fts5Hash*); - -+/* -+** Return true if the hash is empty, false otherwise. -+*/ -+static int sqlite3Fts5HashIsEmpty(Fts5Hash*); -+ - static int sqlite3Fts5HashQuery( - Fts5Hash*, /* Hash table to query */ - int nPre, -@@ -209111,11 +236261,13 @@ static void sqlite3Fts5HashScanNext(Fts5Hash*); - static int sqlite3Fts5HashScanEof(Fts5Hash*); - static void sqlite3Fts5HashScanEntry(Fts5Hash *, - const char **pzTerm, /* OUT: term (nul-terminated) */ -+ int *pnTerm, /* OUT: Size of term in bytes */ - const u8 **ppDoclist, /* OUT: pointer to doclist */ - int *pnDoclist /* OUT: size of doclist in bytes */ - ); - - -+ - /* - ** End of interface to code in fts5_hash.c. - **************************************************************************/ -@@ -209138,11 +236290,11 @@ static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); - static int sqlite3Fts5DropAll(Fts5Config*); - static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); - --static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**); --static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*); -+static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); -+static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*); - static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); - --static int sqlite3Fts5StorageIntegrity(Fts5Storage *p); -+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); - - static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); - static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); -@@ -209164,6 +236316,9 @@ static int sqlite3Fts5StorageOptimize(Fts5Storage *p); - static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge); - static int sqlite3Fts5StorageReset(Fts5Storage *p); - -+static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage*); -+static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel); -+ - /* - ** End of interface to code in fts5_storage.c. - **************************************************************************/ -@@ -209187,11 +236342,19 @@ struct Fts5Token { - /* Parse a MATCH expression. */ - static int sqlite3Fts5ExprNew( - Fts5Config *pConfig, -+ int bPhraseToAnd, - int iCol, /* Column on LHS of MATCH operator */ - const char *zExpr, - Fts5Expr **ppNew, - char **pzErr - ); -+static int sqlite3Fts5ExprPattern( -+ Fts5Config *pConfig, -+ int bGlob, -+ int iCol, -+ const char *zText, -+ Fts5Expr **pp -+); - - /* - ** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc); -@@ -209228,6 +236391,10 @@ static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); - - static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); - -+static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); -+static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*); -+static void sqlite3Fts5ExprClearTokens(Fts5Expr*); -+ - /******************************************* - ** The fts5_expr.c API above this point is used by the other hand-written - ** C code in this module. The interfaces below this point are called by -@@ -209300,6 +236467,11 @@ static int sqlite3Fts5AuxInit(fts5_api*); - */ - - static int sqlite3Fts5TokenizerInit(fts5_api*); -+static int sqlite3Fts5TokenizerPattern( -+ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), -+ Fts5Tokenizer *pTok -+); -+static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig*); - /* - ** End of interface to code in fts5_tokenizer.c. - **************************************************************************/ -@@ -209346,6 +236518,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); - #define FTS5_PLUS 14 - #define FTS5_STAR 15 - -+/* This file is automatically generated by Lemon from input grammar -+** source file "fts5parse.y". -+*/ - /* - ** 2000-05-29 - ** -@@ -209361,7 +236536,7 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); - ** - ** The "lemon" program processes an LALR(1) input grammar file, then uses - ** this template to construct a parser. The "lemon" program inserts text --** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the -+** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the - ** interstitial "-" characters) contained in this template is changed into - ** the value of the %name directive from the grammar. Otherwise, the content - ** of this template is copied straight through into the generate parser -@@ -209370,8 +236545,6 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); - ** The following is the concatenation of all %include directives from the - ** input grammar file: - */ --/* #include */ --/* #include */ - /************ Begin %include sections from the grammar ************************/ - - /* #include "fts5Int.h" */ -@@ -209401,11 +236574,26 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); - #define fts5YYMALLOCARGTYPE u64 - - /**************** End of %include directives **********************************/ --/* These constants specify the various numeric values for terminal symbols --** in a format understandable to "makeheaders". This section is blank unless --** "lemon" is run with the "-m" command-line option. --***************** Begin makeheaders token definitions *************************/ --/**************** End makeheaders token definitions ***************************/ -+/* These constants specify the various numeric values for terminal symbols. -+***************** Begin token definitions *************************************/ -+#ifndef FTS5_OR -+#define FTS5_OR 1 -+#define FTS5_AND 2 -+#define FTS5_NOT 3 -+#define FTS5_TERM 4 -+#define FTS5_COLON 5 -+#define FTS5_MINUS 6 -+#define FTS5_LCP 7 -+#define FTS5_RCP 8 -+#define FTS5_STRING 9 -+#define FTS5_LP 10 -+#define FTS5_RP 11 -+#define FTS5_CARET 12 -+#define FTS5_COMMA 13 -+#define FTS5_PLUS 14 -+#define FTS5_STAR 15 -+#endif -+/**************** End token definitions ***************************************/ - - /* The next sections is a series of control #defines. - ** various aspects of the generated parser. -@@ -209444,6 +236632,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); - ** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser - ** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser - ** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context -+** fts5YYREALLOC Name of the realloc() function to use -+** fts5YYFREE Name of the free() function to use -+** fts5YYDYNSTACK True if stack space should be extended on heap - ** fts5YYERRORSYMBOL is the code number of the error symbol. If not - ** defined, then do no error processing. - ** fts5YYNSTATE the combined number of states. -@@ -209457,6 +236648,8 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); - ** fts5YY_NO_ACTION The fts5yy_action[] code for no-op - ** fts5YY_MIN_REDUCE Minimum value for reduce actions - ** fts5YY_MAX_REDUCE Maximum value for reduce actions -+** fts5YY_MIN_DSTRCTR Minimum symbol value that has a destructor -+** fts5YY_MAX_DSTRCTR Maximum symbol value that has a destructor - */ - #ifndef INTERFACE - # define INTERFACE 1 -@@ -209483,6 +236676,9 @@ typedef union { - #define sqlite3Fts5ParserARG_PARAM ,pParse - #define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse; - #define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse; -+#define fts5YYREALLOC realloc -+#define fts5YYFREE free -+#define fts5YYDYNSTACK 0 - #define sqlite3Fts5ParserCTX_SDECL - #define sqlite3Fts5ParserCTX_PDECL - #define sqlite3Fts5ParserCTX_PARAM -@@ -209500,6 +236696,8 @@ typedef union { - #define fts5YY_NO_ACTION 82 - #define fts5YY_MIN_REDUCE 83 - #define fts5YY_MAX_REDUCE 110 -+#define fts5YY_MIN_DSTRCTR 16 -+#define fts5YY_MAX_DSTRCTR 24 - /************* End control #defines *******************************************/ - #define fts5YY_NLOOKAHEAD ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]))) - -@@ -209515,6 +236713,22 @@ typedef union { - # define fts5yytestcase(X) - #endif - -+/* Macro to determine if stack space has the ability to grow using -+** heap memory. -+*/ -+#if fts5YYSTACKDEPTH<=0 || fts5YYDYNSTACK -+# define fts5YYGROWABLESTACK 1 -+#else -+# define fts5YYGROWABLESTACK 0 -+#endif -+ -+/* Guarantee a minimum number of initial stack slots. -+*/ -+#if fts5YYSTACKDEPTH<=0 -+# undef fts5YYSTACKDEPTH -+# define fts5YYSTACKDEPTH 2 /* Need a minimum stack size */ -+#endif -+ - - /* Next are the tables used to determine what action to take based on the - ** current state and lookahead token. These tables are used to implement -@@ -209675,17 +236889,13 @@ struct fts5yyParser { - #endif - sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */ - sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */ --#if fts5YYSTACKDEPTH<=0 -- int fts5yystksz; /* Current side of the stack */ -- fts5yyStackEntry *fts5yystack; /* The parser's stack */ -- fts5yyStackEntry fts5yystk0; /* First stack entry */ --#else -- fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */ -- fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ --#endif -+ fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ -+ fts5yyStackEntry *fts5yystack; /* The parser stack */ -+ fts5yyStackEntry fts5yystk0[fts5YYSTACKDEPTH]; /* Initial stack space */ - }; - typedef struct fts5yyParser fts5yyParser; - -+/* #include */ - #ifndef NDEBUG - /* #include */ - static FILE *fts5yyTraceFILE = 0; -@@ -209788,37 +236998,45 @@ static const char *const fts5yyRuleName[] = { - #endif /* NDEBUG */ - - --#if fts5YYSTACKDEPTH<=0 -+#if fts5YYGROWABLESTACK - /* - ** Try to increase the size of the parser stack. Return the number - ** of errors. Return 0 on success. - */ - static int fts5yyGrowStack(fts5yyParser *p){ -+ int oldSize = 1 + (int)(p->fts5yystackEnd - p->fts5yystack); - int newSize; - int idx; - fts5yyStackEntry *pNew; - -- newSize = p->fts5yystksz*2 + 100; -- idx = p->fts5yytos ? (int)(p->fts5yytos - p->fts5yystack) : 0; -- if( p->fts5yystack==&p->fts5yystk0 ){ -- pNew = malloc(newSize*sizeof(pNew[0])); -- if( pNew ) pNew[0] = p->fts5yystk0; -+ newSize = oldSize*2 + 100; -+ idx = (int)(p->fts5yytos - p->fts5yystack); -+ if( p->fts5yystack==p->fts5yystk0 ){ -+ pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0])); -+ if( pNew==0 ) return 1; -+ memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0])); - }else{ -- pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0])); -+ pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0])); -+ if( pNew==0 ) return 1; - } -- if( pNew ){ -- p->fts5yystack = pNew; -- p->fts5yytos = &p->fts5yystack[idx]; -+ p->fts5yystack = pNew; -+ p->fts5yytos = &p->fts5yystack[idx]; - #ifndef NDEBUG -- if( fts5yyTraceFILE ){ -- fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", -- fts5yyTracePrompt, p->fts5yystksz, newSize); -- } --#endif -- p->fts5yystksz = newSize; -+ if( fts5yyTraceFILE ){ -+ fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", -+ fts5yyTracePrompt, oldSize, newSize); - } -- return pNew==0; -+#endif -+ p->fts5yystackEnd = &p->fts5yystack[newSize-1]; -+ return 0; - } -+#endif /* fts5YYGROWABLESTACK */ -+ -+#if !fts5YYGROWABLESTACK -+/* For builds that do no have a growable stack, fts5yyGrowStack always -+** returns an error. -+*/ -+# define fts5yyGrowStack(X) 1 - #endif - - /* Datatype of the argument to the memory allocated passed as the -@@ -209838,24 +237056,14 @@ static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PD - #ifdef fts5YYTRACKMAXSTACKDEPTH - fts5yypParser->fts5yyhwm = 0; - #endif --#if fts5YYSTACKDEPTH<=0 -- fts5yypParser->fts5yytos = NULL; -- fts5yypParser->fts5yystack = NULL; -- fts5yypParser->fts5yystksz = 0; -- if( fts5yyGrowStack(fts5yypParser) ){ -- fts5yypParser->fts5yystack = &fts5yypParser->fts5yystk0; -- fts5yypParser->fts5yystksz = 1; -- } --#endif -+ fts5yypParser->fts5yystack = fts5yypParser->fts5yystk0; -+ fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; - #ifndef fts5YYNOERRORRECOVERY - fts5yypParser->fts5yyerrcnt = -1; - #endif - fts5yypParser->fts5yytos = fts5yypParser->fts5yystack; - fts5yypParser->fts5yystack[0].stateno = 0; - fts5yypParser->fts5yystack[0].major = 0; --#if fts5YYSTACKDEPTH>0 -- fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; --#endif - } - - #ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK -@@ -209969,9 +237177,26 @@ static void fts5yy_pop_parser_stack(fts5yyParser *pParser){ - */ - static void sqlite3Fts5ParserFinalize(void *p){ - fts5yyParser *pParser = (fts5yyParser*)p; -- while( pParser->fts5yytos>pParser->fts5yystack ) fts5yy_pop_parser_stack(pParser); --#if fts5YYSTACKDEPTH<=0 -- if( pParser->fts5yystack!=&pParser->fts5yystk0 ) free(pParser->fts5yystack); -+ -+ /* In-lined version of calling fts5yy_pop_parser_stack() for each -+ ** element left in the stack */ -+ fts5yyStackEntry *fts5yytos = pParser->fts5yytos; -+ while( fts5yytos>pParser->fts5yystack ){ -+#ifndef NDEBUG -+ if( fts5yyTraceFILE ){ -+ fprintf(fts5yyTraceFILE,"%sPopping %s\n", -+ fts5yyTracePrompt, -+ fts5yyTokenName[fts5yytos->major]); -+ } -+#endif -+ if( fts5yytos->major>=fts5YY_MIN_DSTRCTR ){ -+ fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor); -+ } -+ fts5yytos--; -+ } -+ -+#if fts5YYGROWABLESTACK -+ if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack); - #endif - } - -@@ -210102,7 +237327,7 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action( - #endif /* fts5YYWILDCARD */ - return fts5yy_default[stateno]; - }else{ -- assert( i>=0 && i=0 && i<(int)(sizeof(fts5yy_action)/sizeof(fts5yy_action[0])) ); - return fts5yy_action[i]; - } - }while(1); -@@ -210198,25 +237423,19 @@ static void fts5yy_shift( - assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) ); - } - #endif --#if fts5YYSTACKDEPTH>0 -- if( fts5yypParser->fts5yytos>fts5yypParser->fts5yystackEnd ){ -- fts5yypParser->fts5yytos--; -- fts5yyStackOverflow(fts5yypParser); -- return; -- } --#else -- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz] ){ -+ fts5yytos = fts5yypParser->fts5yytos; -+ if( fts5yytos>fts5yypParser->fts5yystackEnd ){ - if( fts5yyGrowStack(fts5yypParser) ){ - fts5yypParser->fts5yytos--; - fts5yyStackOverflow(fts5yypParser); - return; - } -+ fts5yytos = fts5yypParser->fts5yytos; -+ assert( fts5yytos <= fts5yypParser->fts5yystackEnd ); - } --#endif - if( fts5yyNewState > fts5YY_MAX_SHIFT ){ - fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE; - } -- fts5yytos = fts5yypParser->fts5yytos; - fts5yytos->stateno = fts5yyNewState; - fts5yytos->major = fts5yyMajor; - fts5yytos->minor.fts5yy0 = fts5yyMinor; -@@ -210316,54 +237535,6 @@ static fts5YYACTIONTYPE fts5yy_reduce( - (void)fts5yyLookahead; - (void)fts5yyLookaheadToken; - fts5yymsp = fts5yypParser->fts5yytos; --#ifndef NDEBUG -- if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){ -- fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; -- if( fts5yysize ){ -- fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", -- fts5yyTracePrompt, -- fts5yyruleno, fts5yyRuleName[fts5yyruleno], -- fts5yyrulenofts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ -- fts5yypParser->fts5yyhwm++; -- assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); -- } --#endif --#if fts5YYSTACKDEPTH>0 -- if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ -- fts5yyStackOverflow(fts5yypParser); -- /* The call to fts5yyStackOverflow() above pops the stack until it is -- ** empty, causing the main parser loop to exit. So the return value -- ** is never used and does not matter. */ -- return 0; -- } --#else -- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){ -- if( fts5yyGrowStack(fts5yypParser) ){ -- fts5yyStackOverflow(fts5yypParser); -- /* The call to fts5yyStackOverflow() above pops the stack until it is -- ** empty, causing the main parser loop to exit. So the return value -- ** is never used and does not matter. */ -- return 0; -- } -- fts5yymsp = fts5yypParser->fts5yytos; -- } --#endif -- } - - switch( fts5yyruleno ){ - /* Beginning here are the reduction cases. A typical example -@@ -210666,12 +237837,49 @@ static void sqlite3Fts5Parser( - } - #endif - -- do{ -+ while(1){ /* Exit by "break" */ -+ assert( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystack ); - assert( fts5yyact==fts5yypParser->fts5yytos->stateno ); - fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact); - if( fts5yyact >= fts5YY_MIN_REDUCE ){ -- fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE,fts5yymajor, -- fts5yyminor sqlite3Fts5ParserCTX_PARAM); -+ unsigned int fts5yyruleno = fts5yyact - fts5YY_MIN_REDUCE; /* Reduce by this rule */ -+#ifndef NDEBUG -+ assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ); -+ if( fts5yyTraceFILE ){ -+ int fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; -+ if( fts5yysize ){ -+ fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", -+ fts5yyTracePrompt, -+ fts5yyruleno, fts5yyRuleName[fts5yyruleno], -+ fts5yyrulenofts5yytos[fts5yysize].stateno); -+ }else{ -+ fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s.\n", -+ fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno], -+ fts5yyrulenofts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ -+ fts5yypParser->fts5yyhwm++; -+ assert( fts5yypParser->fts5yyhwm == -+ (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); -+ } -+#endif -+ if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ -+ if( fts5yyGrowStack(fts5yypParser) ){ -+ fts5yyStackOverflow(fts5yypParser); -+ break; -+ } -+ } -+ } -+ fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM); - }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){ - fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPE)fts5yymajor,fts5yyminor); - #ifndef fts5YYNOERRORRECOVERY -@@ -210727,14 +237935,13 @@ static void sqlite3Fts5Parser( - fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion); - fts5yymajor = fts5YYNOCODE; - }else{ -- while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack -- && (fts5yyact = fts5yy_find_reduce_action( -- fts5yypParser->fts5yytos->stateno, -- fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE -- ){ -+ while( fts5yypParser->fts5yytos > fts5yypParser->fts5yystack ){ -+ fts5yyact = fts5yy_find_reduce_action(fts5yypParser->fts5yytos->stateno, -+ fts5YYERRORSYMBOL); -+ if( fts5yyact<=fts5YY_MAX_SHIFTREDUCE ) break; - fts5yy_pop_parser_stack(fts5yypParser); - } -- if( fts5yypParser->fts5yytos < fts5yypParser->fts5yystack || fts5yymajor==0 ){ -+ if( fts5yypParser->fts5yytos <= fts5yypParser->fts5yystack || fts5yymajor==0 ){ - fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion); - fts5yy_parse_failed(fts5yypParser); - #ifndef fts5YYNOERRORRECOVERY -@@ -210784,7 +237991,7 @@ static void sqlite3Fts5Parser( - break; - #endif - } -- }while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ); -+ } - #ifndef NDEBUG - if( fts5yyTraceFILE ){ - fts5yyStackEntry *i; -@@ -210926,15 +238133,19 @@ static int fts5CInstIterInit( - */ - typedef struct HighlightContext HighlightContext; - struct HighlightContext { -- CInstIter iter; /* Coalesced Instance Iterator */ -- int iPos; /* Current token offset in zIn[] */ -+ /* Constant parameters to fts5HighlightCb() */ - int iRangeStart; /* First token to include */ - int iRangeEnd; /* If non-zero, last token to include */ - const char *zOpen; /* Opening highlight */ - const char *zClose; /* Closing highlight */ - const char *zIn; /* Input text */ - int nIn; /* Size of input text in bytes */ -- int iOff; /* Current offset within zIn[] */ -+ -+ /* Variables modified by fts5HighlightCb() */ -+ CInstIter iter; /* Coalesced Instance Iterator */ -+ int iPos; /* Current token offset in zIn[] */ -+ int iOff; /* Have copied up to this offset in zIn[] */ -+ int bOpen; /* True if highlight is open */ - char *zOut; /* Output value */ - }; - -@@ -210967,8 +238178,8 @@ static int fts5HighlightCb( - int tflags, /* Mask of FTS5_TOKEN_* flags */ - const char *pToken, /* Buffer containing token */ - int nToken, /* Size of token in bytes */ -- int iStartOff, /* Start offset of token */ -- int iEndOff /* End offset of token */ -+ int iStartOff, /* Start byte offset of token */ -+ int iEndOff /* End byte offset of token */ - ){ - HighlightContext *p = (HighlightContext*)pContext; - int rc = SQLITE_OK; -@@ -210979,40 +238190,66 @@ static int fts5HighlightCb( - if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK; - iPos = p->iPos++; - -- if( p->iRangeEnd>0 ){ -+ if( p->iRangeEnd>=0 ){ - if( iPosiRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK; - if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff; - } - -- if( iPos==p->iter.iStart ){ -+ /* If the parenthesis is open, and this token is not part of the current -+ ** phrase, and the starting byte offset of this token is past the point -+ ** that has currently been copied into the output buffer, close the -+ ** parenthesis. */ -+ if( p->bOpen -+ && (iPos<=p->iter.iStart || p->iter.iStart<0) -+ && iStartOff>p->iOff -+ ){ -+ fts5HighlightAppend(&rc, p, p->zClose, -1); -+ p->bOpen = 0; -+ } -+ -+ /* If this is the start of a new phrase, and the highlight is not open: -+ ** -+ ** * copy text from the input up to the start of the phrase, and -+ ** * open the highlight. -+ */ -+ if( iPos==p->iter.iStart && p->bOpen==0 ){ - fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff); - fts5HighlightAppend(&rc, p, p->zOpen, -1); - p->iOff = iStartOff; -+ p->bOpen = 1; - } - - if( iPos==p->iter.iEnd ){ -- if( p->iRangeEnd && p->iter.iStartiRangeStart ){ -+ if( p->bOpen==0 ){ -+ assert( p->iRangeEnd>=0 ); - fts5HighlightAppend(&rc, p, p->zOpen, -1); -+ p->bOpen = 1; - } - fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); -- fts5HighlightAppend(&rc, p, p->zClose, -1); - p->iOff = iEndOff; -+ - if( rc==SQLITE_OK ){ - rc = fts5CInstIterNext(&p->iter); - } - } - -- if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){ -- fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); -- p->iOff = iEndOff; -- if( iPos>=p->iter.iStart && iPositer.iEnd ){ -+ if( iPos==p->iRangeEnd ){ -+ if( p->bOpen ){ -+ if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){ -+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); -+ p->iOff = iEndOff; -+ } - fts5HighlightAppend(&rc, p, p->zClose, -1); -+ p->bOpen = 0; - } -+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); -+ p->iOff = iEndOff; - } - - return rc; - } - -+ - /* - ** Implementation of highlight() function. - */ -@@ -211037,15 +238274,28 @@ static void fts5HighlightFunction( - memset(&ctx, 0, sizeof(HighlightContext)); - ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); - ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); -+ ctx.iRangeEnd = -1; - rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); -- -- if( ctx.zIn ){ -+ if( rc==SQLITE_RANGE ){ -+ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); -+ rc = SQLITE_OK; -+ }else if( ctx.zIn ){ -+ const char *pLoc = 0; /* Locale of column iCol */ -+ int nLoc = 0; /* Size of pLoc in bytes */ - if( rc==SQLITE_OK ){ - rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); - } - - if( rc==SQLITE_OK ){ -- rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); -+ rc = pApi->xColumnLocale(pFts, iCol, &pLoc, &nLoc); -+ } -+ if( rc==SQLITE_OK ){ -+ rc = pApi->xTokenize_v2( -+ pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx, fts5HighlightCb -+ ); -+ } -+ if( ctx.bOpen ){ -+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); - } - fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); - -@@ -211222,6 +238472,7 @@ static void fts5SnippetFunction( - iCol = sqlite3_value_int(apVal[0]); - ctx.zOpen = fts5ValueToText(apVal[1]); - ctx.zClose = fts5ValueToText(apVal[2]); -+ ctx.iRangeEnd = -1; - zEllips = fts5ValueToText(apVal[3]); - nToken = sqlite3_value_int(apVal[4]); - -@@ -211238,6 +238489,8 @@ static void fts5SnippetFunction( - memset(&sFinder, 0, sizeof(Fts5SFinder)); - for(i=0; ixColumnText(pFts, i, &sFinder.zDoc, &nDoc); - if( rc!=SQLITE_OK ) break; -- rc = pApi->xTokenize(pFts, -- sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb -+ rc = pApi->xColumnLocale(pFts, i, &pLoc, &nLoc); -+ if( rc!=SQLITE_OK ) break; -+ rc = pApi->xTokenize_v2(pFts, -+ sFinder.zDoc, nDoc, pLoc, nLoc, (void*)&sFinder, fts5SentenceFinderCb - ); - if( rc!=SQLITE_OK ) break; - rc = pApi->xColumnSize(pFts, i, &nDocsize); -@@ -211304,6 +238559,9 @@ static void fts5SnippetFunction( - rc = pApi->xColumnSize(pFts, iBestCol, &nColSize); - } - if( ctx.zIn ){ -+ const char *pLoc = 0; /* Locale of column iBestCol */ -+ int nLoc = 0; /* Bytes in pLoc */ -+ - if( rc==SQLITE_OK ){ - rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); - } -@@ -211322,7 +238580,15 @@ static void fts5SnippetFunction( - } - - if( rc==SQLITE_OK ){ -- rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); -+ rc = pApi->xColumnLocale(pFts, iBestCol, &pLoc, &nLoc); -+ } -+ if( rc==SQLITE_OK ){ -+ rc = pApi->xTokenize_v2( -+ pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx,fts5HighlightCb -+ ); -+ } -+ if( ctx.bOpen ){ -+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); - } - if( ctx.iRangeEnd>=(nColSize-1) ){ - fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); -@@ -211382,7 +238648,7 @@ static int fts5Bm25GetData( - int rc = SQLITE_OK; /* Return code */ - Fts5Bm25Data *p; /* Object to return */ - -- p = pApi->xGetAuxdata(pFts, 0); -+ p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0); - if( p==0 ){ - int nPhrase; /* Number of phrases in query */ - sqlite3_int64 nRow = 0; /* Number of rows in table */ -@@ -211424,7 +238690,7 @@ static int fts5Bm25GetData( - ** under consideration. - ** - ** The problem with this is that if (N < 2*nHit), the IDF is -- ** negative. Which is undesirable. So the mimimum allowable IDF is -+ ** negative. Which is undesirable. So the minimum allowable IDF is - ** (1e-6) - roughly the same as a term that appears in just over - ** half of set of 5,000,000 documents. */ - double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); -@@ -211456,7 +238722,7 @@ static void fts5Bm25Function( - ){ - const double k1 = 1.2; /* Constant "k1" from BM25 formula */ - const double b = 0.75; /* Constant "b" from BM25 formula */ -- int rc = SQLITE_OK; /* Error code */ -+ int rc; /* Error code */ - double score = 0.0; /* SQL function return value */ - Fts5Bm25Data *pData; /* Values allocated/calculated once only */ - int i; /* Iterator variable */ -@@ -211488,23 +238754,68 @@ static void fts5Bm25Function( - D = (double)nTok; - } - -- /* Determine the BM25 score for the current row. */ -- for(i=0; rc==SQLITE_OK && inPhrase; i++){ -- score += pData->aIDF[i] * ( -- ( aFreq[i] * (k1 + 1.0) ) / -- ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) -- ); -- } -- -- /* If no error has occurred, return the calculated score. Otherwise, -- ** throw an SQL exception. */ -+ /* Determine and return the BM25 score for the current row. Or, if an -+ ** error has occurred, throw an exception. */ - if( rc==SQLITE_OK ){ -+ for(i=0; inPhrase; i++){ -+ score += pData->aIDF[i] * ( -+ ( aFreq[i] * (k1 + 1.0) ) / -+ ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) -+ ); -+ } - sqlite3_result_double(pCtx, -1.0 * score); - }else{ - sqlite3_result_error_code(pCtx, rc); - } - } - -+/* -+** Implementation of fts5_get_locale() function. -+*/ -+static void fts5GetLocaleFunction( -+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ -+ Fts5Context *pFts, /* First arg to pass to pApi functions */ -+ sqlite3_context *pCtx, /* Context for returning result/error */ -+ int nVal, /* Number of values in apVal[] array */ -+ sqlite3_value **apVal /* Array of trailing arguments */ -+){ -+ int iCol = 0; -+ int eType = 0; -+ int rc = SQLITE_OK; -+ const char *zLocale = 0; -+ int nLocale = 0; -+ -+ /* xColumnLocale() must be available */ -+ assert( pApi->iVersion>=4 ); -+ -+ if( nVal!=1 ){ -+ const char *z = "wrong number of arguments to function fts5_get_locale()"; -+ sqlite3_result_error(pCtx, z, -1); -+ return; -+ } -+ -+ eType = sqlite3_value_numeric_type(apVal[0]); -+ if( eType!=SQLITE_INTEGER ){ -+ const char *z = "non-integer argument passed to function fts5_get_locale()"; -+ sqlite3_result_error(pCtx, z, -1); -+ return; -+ } -+ -+ iCol = sqlite3_value_int(apVal[0]); -+ if( iCol<0 || iCol>=pApi->xColumnCount(pFts) ){ -+ sqlite3_result_error_code(pCtx, SQLITE_RANGE); -+ return; -+ } -+ -+ rc = pApi->xColumnLocale(pFts, iCol, &zLocale, &nLocale); -+ if( rc!=SQLITE_OK ){ -+ sqlite3_result_error_code(pCtx, rc); -+ return; -+ } -+ -+ sqlite3_result_text(pCtx, zLocale, nLocale, SQLITE_TRANSIENT); -+} -+ - static int sqlite3Fts5AuxInit(fts5_api *pApi){ - struct Builtin { - const char *zFunc; /* Function name (nul-terminated) */ -@@ -211512,9 +238823,10 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){ - fts5_extension_function xFunc;/* Callback function */ - void (*xDestroy)(void*); /* Destructor function */ - } aBuiltin [] = { -- { "snippet", 0, fts5SnippetFunction, 0 }, -- { "highlight", 0, fts5HighlightFunction, 0 }, -- { "bm25", 0, fts5Bm25Function, 0 }, -+ { "snippet", 0, fts5SnippetFunction, 0 }, -+ { "highlight", 0, fts5HighlightFunction, 0 }, -+ { "bm25", 0, fts5Bm25Function, 0 }, -+ { "fts5_get_locale", 0, fts5GetLocaleFunction, 0 }, - }; - int rc = SQLITE_OK; /* Return code */ - int i; /* To iterate through builtin functions */ -@@ -211599,9 +238911,9 @@ static void sqlite3Fts5BufferAppendBlob( - u32 nData, - const u8 *pData - ){ -- assert_nc( *pRc || nData>=0 ); - if( nData ){ - if( fts5BufferGrow(pRc, pBuf, nData) ) return; -+ assert( pBuf->p!=0 ); - memcpy(&pBuf->p[pBuf->n], pData, nData); - pBuf->n += nData; - } -@@ -211703,13 +239015,15 @@ static int sqlite3Fts5PoslistNext64( - i64 *piOff /* IN/OUT: Current offset */ - ){ - int i = *pi; -+ assert( a!=0 || i==0 ); - if( i>=n ){ - /* EOF */ - *piOff = -1; - return 1; - }else{ - i64 iOff = *piOff; -- int iVal; -+ u32 iVal; -+ assert( a!=0 ); - fts5FastGetVarint32(a, i, iVal); - if( iVal<=1 ){ - if( iVal==0 ){ -@@ -211718,15 +239032,19 @@ static int sqlite3Fts5PoslistNext64( - } - fts5FastGetVarint32(a, i, iVal); - iOff = ((i64)iVal) << 32; -+ assert( iOff>=0 ); - fts5FastGetVarint32(a, i, iVal); - if( iVal<2 ){ - /* This is a corrupt record. So stop parsing it here. */ - *piOff = -1; - return 1; - } -+ *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); -+ }else{ -+ *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF); - } -- *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); - *pi = i; -+ assert_nc( *piOff>=iOff ); - return 0; - } - } -@@ -211765,14 +239083,16 @@ static void sqlite3Fts5PoslistSafeAppend( - i64 *piPrev, - i64 iPos - ){ -- static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; -- if( (iPos & colmask) != (*piPrev & colmask) ){ -- pBuf->p[pBuf->n++] = 1; -- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); -- *piPrev = (iPos & colmask); -+ if( iPos>=*piPrev ){ -+ static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; -+ if( (iPos & colmask) != (*piPrev & colmask) ){ -+ pBuf->p[pBuf->n++] = 1; -+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); -+ *piPrev = (iPos & colmask); -+ } -+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); -+ *piPrev = iPos; - } -- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); -- *piPrev = iPos; - } - - static int sqlite3Fts5PoslistWriterAppend( -@@ -211833,7 +239153,7 @@ static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){ - ** * The 52 upper and lower case ASCII characters, and - ** * The 10 integer ASCII characters. - ** * The underscore character "_" (0x5F). --** * The unicode "subsitute" character (0x1A). -+** * The unicode "substitute" character (0x1A). - */ - static int sqlite3Fts5IsBareword(char t){ - u8 aBareword[128] = { -@@ -211959,6 +239279,8 @@ static void sqlite3Fts5TermsetFree(Fts5Termset *p){ - #define FTS5_DEFAULT_CRISISMERGE 16 - #define FTS5_DEFAULT_HASHSIZE (1024*1024) - -+#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */ -+ - /* Maximum allowed page size */ - #define FTS5_MAX_PAGE_SIZE (64*1024) - -@@ -212169,7 +239491,6 @@ static int fts5ConfigSetEnum( - ** eventually free any such error message using sqlite3_free(). - */ - static int fts5ConfigParseSpecial( -- Fts5Global *pGlobal, - Fts5Config *pConfig, /* Configuration object to update */ - const char *zCmd, /* Special command to parse */ - const char *zArg, /* Argument to parse */ -@@ -212177,6 +239498,7 @@ static int fts5ConfigParseSpecial( - ){ - int rc = SQLITE_OK; - int nCmd = (int)strlen(zCmd); -+ - if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){ - const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES; - const char *p; -@@ -212233,12 +239555,11 @@ static int fts5ConfigParseSpecial( - if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){ - const char *p = (const char*)zArg; - sqlite3_int64 nArg = strlen(zArg) + 1; -- char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg); -- char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2); -- char *pSpace = pDel; -+ char **azArg = sqlite3Fts5MallocZero(&rc, (sizeof(char*) + 2) * nArg); - -- if( azArg && pSpace ){ -- if( pConfig->pTok ){ -+ if( azArg ){ -+ char *pSpace = (char*)&azArg[nArg]; -+ if( pConfig->t.azArg ){ - *pzErr = sqlite3_mprintf("multiple tokenize=... directives"); - rc = SQLITE_ERROR; - }else{ -@@ -212261,16 +239582,14 @@ static int fts5ConfigParseSpecial( - *pzErr = sqlite3_mprintf("parse error in tokenize directive"); - rc = SQLITE_ERROR; - }else{ -- rc = sqlite3Fts5GetTokenizer(pGlobal, -- (const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi, -- pzErr -- ); -+ pConfig->t.azArg = (const char**)azArg; -+ pConfig->t.nArg = nArg; -+ azArg = 0; - } - } - } -- - sqlite3_free(azArg); -- sqlite3_free(pDel); -+ - return rc; - } - -@@ -212289,6 +239608,26 @@ static int fts5ConfigParseSpecial( - return rc; - } - -+ if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){ -+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ -+ *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); -+ rc = SQLITE_ERROR; -+ }else{ -+ pConfig->bContentlessDelete = (zArg[0]=='1'); -+ } -+ return rc; -+ } -+ -+ if( sqlite3_strnicmp("contentless_unindexed", zCmd, nCmd)==0 ){ -+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ -+ *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); -+ rc = SQLITE_ERROR; -+ }else{ -+ pConfig->bContentlessUnindexed = (zArg[0]=='1'); -+ } -+ return rc; -+ } -+ - if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ - if( pConfig->zContentRowid ){ - *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); -@@ -212309,6 +239648,16 @@ static int fts5ConfigParseSpecial( - return rc; - } - -+ if( sqlite3_strnicmp("locale", zCmd, nCmd)==0 ){ -+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ -+ *pzErr = sqlite3_mprintf("malformed locale=... directive"); -+ rc = SQLITE_ERROR; -+ }else{ -+ pConfig->bLocale = (zArg[0]=='1'); -+ } -+ return rc; -+ } -+ - if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){ - const Fts5Enum aDetail[] = { - { "none", FTS5_DETAIL_NONE }, -@@ -212323,22 +239672,20 @@ static int fts5ConfigParseSpecial( - return rc; - } - -+ if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){ -+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ -+ *pzErr = sqlite3_mprintf("malformed tokendata=... directive"); -+ rc = SQLITE_ERROR; -+ }else{ -+ pConfig->bTokendata = (zArg[0]=='1'); -+ } -+ return rc; -+ } -+ - *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); - return SQLITE_ERROR; - } - --/* --** Allocate an instance of the default tokenizer ("simple") at --** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error --** code if an error occurs. --*/ --static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){ -- assert( pConfig->pTok==0 && pConfig->pTokApi==0 ); -- return sqlite3Fts5GetTokenizer( -- pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0 -- ); --} -- - /* - ** Gobble up the first bareword or quoted word from the input buffer zIn. - ** Return a pointer to the character immediately following the last in -@@ -212398,7 +239745,8 @@ static int fts5ConfigParseColumn( - Fts5Config *p, - char *zCol, - char *zArg, -- char **pzErr -+ char **pzErr, -+ int *pbUnindexed - ){ - int rc = SQLITE_OK; - if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME) -@@ -212409,6 +239757,7 @@ static int fts5ConfigParseColumn( - }else if( zArg ){ - if( 0==sqlite3_stricmp(zArg, "unindexed") ){ - p->abUnindexed[p->nCol] = 1; -+ *pbUnindexed = 1; - }else{ - *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg); - rc = SQLITE_ERROR; -@@ -212429,11 +239778,26 @@ static int fts5ConfigMakeExprlist(Fts5Config *p){ - - sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid); - if( p->eContent!=FTS5_CONTENT_NONE ){ -+ assert( p->eContent==FTS5_CONTENT_EXTERNAL -+ || p->eContent==FTS5_CONTENT_NORMAL -+ || p->eContent==FTS5_CONTENT_UNINDEXED -+ ); - for(i=0; inCol; i++){ - if( p->eContent==FTS5_CONTENT_EXTERNAL ){ - sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]); -- }else{ -+ }else if( p->eContent==FTS5_CONTENT_NORMAL || p->abUnindexed[i] ){ - sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i); -+ }else{ -+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); -+ } -+ } -+ } -+ if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){ -+ for(i=0; inCol; i++){ -+ if( p->abUnindexed[i]==0 ){ -+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i); -+ }else{ -+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); - } - } - } -@@ -212467,16 +239831,18 @@ static int sqlite3Fts5ConfigParse( - Fts5Config *pRet; /* New object to return */ - int i; - sqlite3_int64 nByte; -+ int bUnindexed = 0; /* True if there are one or more UNINDEXED */ - - *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); - if( pRet==0 ) return SQLITE_NOMEM; - memset(pRet, 0, sizeof(Fts5Config)); -+ pRet->pGlobal = pGlobal; - pRet->db = db; - pRet->iCookie = -1; - - nByte = nArg * (sizeof(char*) + sizeof(u8)); - pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte); -- pRet->abUnindexed = (u8*)&pRet->azCol[nArg]; -+ pRet->abUnindexed = pRet->azCol ? (u8*)&pRet->azCol[nArg] : 0; - pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1); - pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1); - pRet->bColumnsize = 1; -@@ -212489,6 +239855,7 @@ static int sqlite3Fts5ConfigParse( - rc = SQLITE_ERROR; - } - -+ assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK ); - for(i=3; rc==SQLITE_OK && ipTok==0 ){ -- rc = fts5ConfigDefaultTokenizer(pGlobal, pRet); -+ /* We only allow contentless_delete=1 if the table is indeed contentless. */ -+ if( rc==SQLITE_OK -+ && pRet->bContentlessDelete -+ && pRet->eContent!=FTS5_CONTENT_NONE -+ ){ -+ *pzErr = sqlite3_mprintf( -+ "contentless_delete=1 requires a contentless table" -+ ); -+ rc = SQLITE_ERROR; -+ } -+ -+ /* We only allow contentless_delete=1 if columnsize=0 is not present. -+ ** -+ ** This restriction may be removed at some point. -+ */ -+ if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){ -+ *pzErr = sqlite3_mprintf( -+ "contentless_delete=1 is incompatible with columnsize=0" -+ ); -+ rc = SQLITE_ERROR; -+ } -+ -+ /* We only allow contentless_unindexed=1 if the table is actually a -+ ** contentless one. -+ */ -+ if( rc==SQLITE_OK -+ && pRet->bContentlessUnindexed -+ && pRet->eContent!=FTS5_CONTENT_NONE -+ ){ -+ *pzErr = sqlite3_mprintf( -+ "contentless_unindexed=1 requires a contentless table" -+ ); -+ rc = SQLITE_ERROR; - } - - /* If no zContent option was specified, fill in the default values. */ -@@ -212544,6 +239944,9 @@ static int sqlite3Fts5ConfigParse( - ); - if( pRet->eContent==FTS5_CONTENT_NORMAL ){ - zTail = "content"; -+ }else if( bUnindexed && pRet->bContentlessUnindexed ){ -+ pRet->eContent = FTS5_CONTENT_UNINDEXED; -+ zTail = "content"; - }else if( pRet->bColumnsize ){ - zTail = "docsize"; - } -@@ -212577,9 +239980,14 @@ static int sqlite3Fts5ConfigParse( - static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){ - if( pConfig ){ - int i; -- if( pConfig->pTok ){ -- pConfig->pTokApi->xDelete(pConfig->pTok); -+ if( pConfig->t.pTok ){ -+ if( pConfig->t.pApi1 ){ -+ pConfig->t.pApi1->xDelete(pConfig->t.pTok); -+ }else{ -+ pConfig->t.pApi2->xDelete(pConfig->t.pTok); -+ } - } -+ sqlite3_free((char*)pConfig->t.azArg); - sqlite3_free(pConfig->zDb); - sqlite3_free(pConfig->zName); - for(i=0; inCol; i++){ -@@ -212654,10 +240062,24 @@ static int sqlite3Fts5Tokenize( - void *pCtx, /* Context passed to xToken() */ - int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ - ){ -- if( pText==0 ) return SQLITE_OK; -- return pConfig->pTokApi->xTokenize( -- pConfig->pTok, pCtx, flags, pText, nText, xToken -- ); -+ int rc = SQLITE_OK; -+ if( pText ){ -+ if( pConfig->t.pTok==0 ){ -+ rc = sqlite3Fts5LoadTokenizer(pConfig); -+ } -+ if( rc==SQLITE_OK ){ -+ if( pConfig->t.pApi1 ){ -+ rc = pConfig->t.pApi1->xTokenize( -+ pConfig->t.pTok, pCtx, flags, pText, nText, xToken -+ ); -+ }else{ -+ rc = pConfig->t.pApi2->xTokenize(pConfig->t.pTok, pCtx, flags, -+ pText, nText, pConfig->t.pLocale, pConfig->t.nLocale, xToken -+ ); -+ } -+ } -+ } -+ return rc; - } - - /* -@@ -212823,6 +240245,18 @@ static int sqlite3Fts5ConfigSetValue( - } - } - -+ else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){ -+ int nVal = -1; -+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ -+ nVal = sqlite3_value_int(pVal); -+ }else{ -+ *pbBadkey = 1; -+ } -+ if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE; -+ if( nVal>100 ) nVal = 0; -+ pConfig->nDeleteMerge = nVal; -+ } -+ - else if( 0==sqlite3_stricmp(zKey, "rank") ){ - const char *zIn = (const char*)sqlite3_value_text(pVal); - char *zRank; -@@ -212837,6 +240271,31 @@ static int sqlite3Fts5ConfigSetValue( - rc = SQLITE_OK; - *pbBadkey = 1; - } -+ } -+ -+ else if( 0==sqlite3_stricmp(zKey, "secure-delete") ){ -+ int bVal = -1; -+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ -+ bVal = sqlite3_value_int(pVal); -+ } -+ if( bVal<0 ){ -+ *pbBadkey = 1; -+ }else{ -+ pConfig->bSecureDelete = (bVal ? 1 : 0); -+ } -+ } -+ -+ else if( 0==sqlite3_stricmp(zKey, "insttoken") ){ -+ int bVal = -1; -+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ -+ bVal = sqlite3_value_int(pVal); -+ } -+ if( bVal<0 ){ -+ *pbBadkey = 1; -+ }else{ -+ pConfig->bPrefixInsttoken = (bVal ? 1 : 0); -+ } -+ - }else{ - *pbBadkey = 1; - } -@@ -212859,6 +240318,7 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ - pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE; - pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE; - pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE; -+ pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE; - - zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName); - if( zSql ){ -@@ -212881,15 +240341,17 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ - rc = sqlite3_finalize(p); - } - -- if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){ -+ if( rc==SQLITE_OK -+ && iVersion!=FTS5_CURRENT_VERSION -+ && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE -+ ){ - rc = SQLITE_ERROR; -- if( pConfig->pzErrmsg ){ -- assert( 0==*pConfig->pzErrmsg ); -- *pConfig->pzErrmsg = sqlite3_mprintf( -- "invalid fts5 file format (found %d, expected %d) - run 'rebuild'", -- iVersion, FTS5_CURRENT_VERSION -- ); -- } -+ sqlite3Fts5ConfigErrmsg(pConfig, "invalid fts5 file format " -+ "(found %d, expected %d or %d) - run 'rebuild'", -+ iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE -+ ); -+ }else{ -+ pConfig->iVersion = iVersion; - } - - if( rc==SQLITE_OK ){ -@@ -212898,6 +240360,29 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ - return rc; - } - -+/* -+** Set (*pConfig->pzErrmsg) to point to an sqlite3_malloc()ed buffer -+** containing the error message created using printf() style formatting -+** string zFmt and its trailing arguments. -+*/ -+static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){ -+ va_list ap; /* ... printf arguments */ -+ char *zMsg = 0; -+ -+ va_start(ap, zFmt); -+ zMsg = sqlite3_vmprintf(zFmt, ap); -+ if( pConfig->pzErrmsg ){ -+ assert( *pConfig->pzErrmsg==0 ); -+ *pConfig->pzErrmsg = zMsg; -+ }else{ -+ sqlite3_free(zMsg); -+ } -+ -+ va_end(ap); -+} -+ -+ -+ - /* - ** 2014 May 31 - ** -@@ -212917,6 +240402,10 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ - /* #include "fts5Int.h" */ - /* #include "fts5parse.h" */ - -+#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH -+# define SQLITE_FTS5_MAX_EXPR_DEPTH 256 -+#endif -+ - /* - ** All token types in the generated fts5parse.h file are greater than 0. - */ -@@ -212950,18 +240439,28 @@ struct Fts5Expr { - - /* - ** eType: --** Expression node type. Always one of: -+** Expression node type. Usually one of: - ** - ** FTS5_AND (nChild, apChild valid) - ** FTS5_OR (nChild, apChild valid) - ** FTS5_NOT (nChild, apChild valid) - ** FTS5_STRING (pNear valid) - ** FTS5_TERM (pNear valid) -+** -+** An expression node with eType==0 may also exist. It always matches zero -+** rows. This is created when a phrase containing no tokens is parsed. -+** e.g. "". -+** -+** iHeight: -+** Distance from this node to furthest leaf. This is always 0 for nodes -+** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one -+** greater than the largest child value. - */ - struct Fts5ExprNode { - int eType; /* Node type */ - int bEof; /* True at EOF */ - int bNomatch; /* True if entry is not a match */ -+ int iHeight; /* Distance to tree leaf nodes */ - - /* Next method for this node. */ - int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64); -@@ -212972,9 +240471,13 @@ struct Fts5ExprNode { - /* Child nodes. For a NOT node, this array always contains 2 entries. For - ** AND or OR nodes, it contains 2 or more entries. */ - int nChild; /* Number of child nodes */ -- Fts5ExprNode *apChild[1]; /* Array of child nodes */ -+ Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */ - }; - -+/* Size (in bytes) of an Fts5ExprNode object that holds up to N children */ -+#define SZ_FTS5EXPRNODE(N) \ -+ (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*)) -+ - #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) - - /* -@@ -212990,7 +240493,9 @@ struct Fts5ExprNode { - struct Fts5ExprTerm { - u8 bPrefix; /* True for a prefix term */ - u8 bFirst; /* True if token must be first in column */ -- char *zTerm; /* nul-terminated term */ -+ char *pTerm; /* Term data */ -+ int nQueryTerm; /* Effective size of term in bytes */ -+ int nFullTerm; /* Size of term in bytes incl. tokendata */ - Fts5IndexIter *pIter; /* Iterator for this term */ - Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */ - }; -@@ -213003,9 +240508,13 @@ struct Fts5ExprPhrase { - Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ - Fts5Buffer poslist; /* Current position list */ - int nTerm; /* Number of entries in aTerm[] */ -- Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */ -+ Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */ - }; - -+/* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */ -+#define SZ_FTS5EXPRPHRASE(N) \ -+ (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm)) -+ - /* - ** One or more phrases that must appear within a certain token distance of - ** each other within each matching document. -@@ -213014,9 +240523,12 @@ struct Fts5ExprNearset { - int nNear; /* NEAR parameter */ - Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ - int nPhrase; /* Number of entries in aPhrase[] array */ -- Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */ -+ Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */ - }; - -+/* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */ -+#define SZ_FTS5EXPRNEARSET(N) \ -+ (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*)) - - /* - ** Parse context. -@@ -213028,12 +240540,39 @@ struct Fts5Parse { - int nPhrase; /* Size of apPhrase array */ - Fts5ExprPhrase **apPhrase; /* Array of all phrases */ - Fts5ExprNode *pExpr; /* Result of a successful parse */ -+ int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ - }; - -+/* -+** Check that the Fts5ExprNode.iHeight variables are set correctly in -+** the expression tree passed as the only argument. -+*/ -+#ifndef NDEBUG -+static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){ -+ if( rc==SQLITE_OK ){ -+ if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){ -+ assert( p->iHeight==0 ); -+ }else{ -+ int ii; -+ int iMaxChild = 0; -+ for(ii=0; iinChild; ii++){ -+ Fts5ExprNode *pChild = p->apChild[ii]; -+ iMaxChild = MAX(iMaxChild, pChild->iHeight); -+ assert_expr_depth_ok(SQLITE_OK, pChild); -+ } -+ assert( p->iHeight==iMaxChild+1 ); -+ } -+ } -+} -+#else -+# define assert_expr_depth_ok(rc, p) -+#endif -+ - static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ - va_list ap; - va_start(ap, zFmt); - if( pParse->rc==SQLITE_OK ){ -+ assert( pParse->zErr==0 ); - pParse->zErr = sqlite3_vmprintf(zFmt, ap); - pParse->rc = SQLITE_ERROR; - } -@@ -213116,6 +240655,7 @@ static void fts5ParseFree(void *p){ sqlite3_free(p); } - - static int sqlite3Fts5ExprNew( - Fts5Config *pConfig, /* FTS5 Configuration */ -+ int bPhraseToAnd, - int iCol, - const char *zExpr, /* Expression text */ - Fts5Expr **ppNew, -@@ -213131,6 +240671,7 @@ static int sqlite3Fts5ExprNew( - *ppNew = 0; - *pzErr = 0; - memset(&sParse, 0, sizeof(sParse)); -+ sParse.bPhraseToAnd = bPhraseToAnd; - pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc); - if( pEngine==0 ){ return SQLITE_NOMEM; } - sParse.pConfig = pConfig; -@@ -213141,10 +240682,13 @@ static int sqlite3Fts5ExprNew( - }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); - sqlite3Fts5ParserFree(pEngine, fts5ParseFree); - -+ assert( sParse.pExpr || sParse.rc!=SQLITE_OK ); -+ assert_expr_depth_ok(sParse.rc, sParse.pExpr); -+ - /* If the LHS of the MATCH expression was a user column, apply the - ** implicit column-filter. */ -- if( iColnCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ -- int n = sizeof(Fts5Colset); -+ if( sParse.rc==SQLITE_OK && iColnCol ){ -+ int n = SZ_FTS5COLSET(1); - Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); - if( pColset ){ - pColset->nCol = 1; -@@ -213160,19 +240704,12 @@ static int sqlite3Fts5ExprNew( - sParse.rc = SQLITE_NOMEM; - sqlite3Fts5ParseNodeFree(sParse.pExpr); - }else{ -- if( !sParse.pExpr ){ -- const int nByte = sizeof(Fts5ExprNode); -- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&sParse.rc, nByte); -- if( pNew->pRoot ){ -- pNew->pRoot->bEof = 1; -- } -- }else{ -- pNew->pRoot = sParse.pExpr; -- } -+ pNew->pRoot = sParse.pExpr; - pNew->pIndex = 0; - pNew->pConfig = pConfig; - pNew->apExprPhrase = sParse.apPhrase; - pNew->nPhrase = sParse.nPhrase; -+ pNew->bDesc = 0; - sParse.apPhrase = 0; - } - }else{ -@@ -213180,10 +240717,103 @@ static int sqlite3Fts5ExprNew( - } - - sqlite3_free(sParse.apPhrase); -- *pzErr = sParse.zErr; -+ if( 0==*pzErr ){ -+ *pzErr = sParse.zErr; -+ }else{ -+ sqlite3_free(sParse.zErr); -+ } - return sParse.rc; - } - -+/* -+** Assuming that buffer z is at least nByte bytes in size and contains a -+** valid utf-8 string, return the number of characters in the string. -+*/ -+static int fts5ExprCountChar(const char *z, int nByte){ -+ int nRet = 0; -+ int ii; -+ for(ii=0; ii=3 ){ -+ int jj; -+ zExpr[iOut++] = '"'; -+ for(jj=iFirst; jj0 ){ -+ int bAnd = 0; -+ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ -+ bAnd = 1; -+ if( pConfig->eDetail==FTS5_DETAIL_NONE ){ -+ iCol = pConfig->nCol; -+ } -+ } -+ zExpr[iOut] = '\0'; -+ rc = sqlite3Fts5ExprNew(pConfig, bAnd, iCol, zExpr, pp,pConfig->pzErrmsg); -+ }else{ -+ *pp = 0; -+ } -+ sqlite3_free(zExpr); -+ } -+ -+ return rc; -+} -+ - /* - ** Free the expression node object passed as the only argument. - */ -@@ -213213,7 +240843,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ - Fts5Parse sParse; - memset(&sParse, 0, sizeof(sParse)); - -- if( *pp1 ){ -+ if( *pp1 && p2 ){ - Fts5Expr *p1 = *pp1; - int nPhrase = p1->nPhrase + p2->nPhrase; - -@@ -213238,7 +240868,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ - } - sqlite3_free(p2->apExprPhrase); - sqlite3_free(p2); -- }else{ -+ }else if( p2 ){ - *pp1 = p2; - } - -@@ -213254,6 +240884,7 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){ - int bRetValid = 0; - Fts5ExprTerm *p; - -+ assert( pTerm ); - assert( pTerm->pSynonym ); - assert( bDesc==0 || bDesc==1 ); - for(p=pTerm; p; p=p->pSynonym){ -@@ -213735,7 +241366,7 @@ static int fts5ExprNearInitAll( - p->pIter = 0; - } - rc = sqlite3Fts5IndexQuery( -- pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm), -+ pExpr->pIndex, p->pTerm, p->nQueryTerm, - (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | - (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), - pNear->pColset, -@@ -213891,7 +241522,7 @@ static int fts5ExprNodeTest_STRING( - } - }else{ - Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; -- if( pIter->iRowid==iLast || pIter->bEof ) continue; -+ if( pIter->iRowid==iLast ) continue; - bMatch = 0; - if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){ - return rc; -@@ -214321,8 +241952,8 @@ static int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bD - } - - /* If the iterator is not at a real match, skip forward until it is. */ -- while( pRoot->bNomatch ){ -- assert( pRoot->bEof==0 && rc==SQLITE_OK ); -+ while( pRoot->bNomatch && rc==SQLITE_OK ){ -+ assert( pRoot->bEof==0 ); - rc = fts5ExprNodeNext(p, pRoot, 0, 0); - } - return rc; -@@ -214372,7 +242003,7 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ - Fts5ExprTerm *pSyn; - Fts5ExprTerm *pNext; - Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; -- sqlite3_free(pTerm->zTerm); -+ sqlite3_free(pTerm->pTerm); - sqlite3Fts5IterClose(pTerm->pIter); - for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){ - pNext = pSyn->pSynonym; -@@ -214413,12 +242044,9 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( - Fts5ExprNearset *pRet = 0; - - if( pParse->rc==SQLITE_OK ){ -- if( pPhrase==0 ){ -- return pNear; -- } - if( pNear==0 ){ - sqlite3_int64 nByte; -- nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); -+ nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1); - pRet = sqlite3_malloc64(nByte); - if( pRet==0 ){ - pParse->rc = SQLITE_NOMEM; -@@ -214429,7 +242057,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( - int nNew = pNear->nPhrase + SZALLOC; - sqlite3_int64 nByte; - -- nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); -+ nByte = SZ_FTS5EXPRNEARSET(nNew+1); - pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); - if( pRet==0 ){ - pParse->rc = SQLITE_NOMEM; -@@ -214446,6 +242074,9 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( - }else{ - if( pRet->nPhrase>0 ){ - Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1]; -+ assert( pParse!=0 ); -+ assert( pParse->apPhrase!=0 ); -+ assert( pParse->nPhrase>=2 ); - assert( pLast==pParse->apPhrase[pParse->nPhrase-2] ); - if( pPhrase->nTerm==0 ){ - fts5ExprPhraseFree(pPhrase); -@@ -214467,6 +242098,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( - typedef struct TokenCtx TokenCtx; - struct TokenCtx { - Fts5ExprPhrase *pPhrase; -+ Fts5Config *pConfig; - int rc; - }; - -@@ -214500,8 +242132,12 @@ static int fts5ParseTokenize( - rc = SQLITE_NOMEM; - }else{ - memset(pSyn, 0, (size_t)nByte); -- pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); -- memcpy(pSyn->zTerm, pToken, nToken); -+ pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); -+ pSyn->nFullTerm = pSyn->nQueryTerm = nToken; -+ if( pCtx->pConfig->bTokendata ){ -+ pSyn->nQueryTerm = (int)strlen(pSyn->pTerm); -+ } -+ memcpy(pSyn->pTerm, pToken, nToken); - pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; - pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; - } -@@ -214512,12 +242148,12 @@ static int fts5ParseTokenize( - int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); - - pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, -- sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew -+ SZ_FTS5EXPRPHRASE(nNew+1) - ); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ -- if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase)); -+ if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1)); - pCtx->pPhrase = pPhrase = pNew; - pNew->nTerm = nNew - SZALLOC; - } -@@ -214526,7 +242162,11 @@ static int fts5ParseTokenize( - if( rc==SQLITE_OK ){ - pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; - memset(pTerm, 0, sizeof(Fts5ExprTerm)); -- pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); -+ pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); -+ pTerm->nFullTerm = pTerm->nQueryTerm = nToken; -+ if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){ -+ pTerm->nQueryTerm = (int)strlen(pTerm->pTerm); -+ } - } - } - -@@ -214561,6 +242201,20 @@ static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){ - pParse->pExpr = p; - } - -+static int parseGrowPhraseArray(Fts5Parse *pParse){ -+ if( (pParse->nPhrase % 8)==0 ){ -+ sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); -+ Fts5ExprPhrase **apNew; -+ apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); -+ if( apNew==0 ){ -+ pParse->rc = SQLITE_NOMEM; -+ return SQLITE_NOMEM; -+ } -+ pParse->apPhrase = apNew; -+ } -+ return SQLITE_OK; -+} -+ - /* - ** This function is called by the parser to process a string token. The - ** string may or may not be quoted. In any case it is tokenized and a -@@ -214579,6 +242233,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( - - memset(&sCtx, 0, sizeof(TokenCtx)); - sCtx.pPhrase = pAppend; -+ sCtx.pConfig = pConfig; - - rc = fts5ParseStringFromToken(pToken, &z); - if( rc==SQLITE_OK ){ -@@ -214596,16 +242251,9 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( - }else{ - - if( pAppend==0 ){ -- if( (pParse->nPhrase % 8)==0 ){ -- sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); -- Fts5ExprPhrase **apNew; -- apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); -- if( apNew==0 ){ -- pParse->rc = SQLITE_NOMEM; -- fts5ExprPhraseFree(sCtx.pPhrase); -- return 0; -- } -- pParse->apPhrase = apNew; -+ if( parseGrowPhraseArray(pParse) ){ -+ fts5ExprPhraseFree(sCtx.pPhrase); -+ return 0; - } - pParse->nPhrase++; - } -@@ -214613,10 +242261,11 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( - if( sCtx.pPhrase==0 ){ - /* This happens when parsing a token or quoted phrase that contains - ** no token characters at all. (e.g ... MATCH '""'). */ -- sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); -+ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1)); - }else if( sCtx.pPhrase->nTerm ){ - sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; - } -+ assert( pParse->apPhrase!=0 ); - pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; - } - -@@ -214633,30 +242282,32 @@ static int sqlite3Fts5ExprClonePhrase( - Fts5Expr **ppNew - ){ - int rc = SQLITE_OK; /* Return code */ -- Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */ -+ Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */ - Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ -- TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */ -- -- pOrig = pExpr->apExprPhrase[iPhrase]; -- pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); -+ TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */ -+ if( !pExpr || iPhrase<0 || iPhrase>=pExpr->nPhrase ){ -+ rc = SQLITE_RANGE; -+ }else{ -+ pOrig = pExpr->apExprPhrase[iPhrase]; -+ pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); -+ } - if( rc==SQLITE_OK ){ - pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, - sizeof(Fts5ExprPhrase*)); - } - if( rc==SQLITE_OK ){ -- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, -- sizeof(Fts5ExprNode)); -+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1)); - } - if( rc==SQLITE_OK ){ - pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, -- sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); -+ SZ_FTS5EXPRNEARSET(2)); - } -- if( rc==SQLITE_OK ){ -+ if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ - Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; - if( pColsetOrig ){ - sqlite3_int64 nByte; - Fts5Colset *pColset; -- nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); -+ nByte = SZ_FTS5COLSET(pColsetOrig->nCol); - pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); - if( pColset ){ - memcpy(pColset, pColsetOrig, (size_t)nByte); -@@ -214665,29 +242316,30 @@ static int sqlite3Fts5ExprClonePhrase( - } - } - -- if( pOrig->nTerm ){ -- int i; /* Used to iterate through phrase terms */ -- for(i=0; rc==SQLITE_OK && inTerm; i++){ -- int tflags = 0; -- Fts5ExprTerm *p; -- for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ -- const char *zTerm = p->zTerm; -- rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm), -- 0, 0); -- tflags = FTS5_TOKEN_COLOCATED; -- } -- if( rc==SQLITE_OK ){ -- sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; -- sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; -+ if( rc==SQLITE_OK ){ -+ if( pOrig->nTerm ){ -+ int i; /* Used to iterate through phrase terms */ -+ sCtx.pConfig = pExpr->pConfig; -+ for(i=0; rc==SQLITE_OK && inTerm; i++){ -+ int tflags = 0; -+ Fts5ExprTerm *p; -+ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ -+ rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0); -+ tflags = FTS5_TOKEN_COLOCATED; -+ } -+ if( rc==SQLITE_OK ){ -+ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; -+ sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; -+ } - } -+ }else{ -+ /* This happens when parsing a token or quoted phrase that contains -+ ** no token characters at all. (e.g ... MATCH '""'). */ -+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1)); - } -- }else{ -- /* This happens when parsing a token or quoted phrase that contains -- ** no token characters at all. (e.g ... MATCH '""'). */ -- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); - } - -- if( rc==SQLITE_OK ){ -+ if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ - /* All the allocations succeeded. Put the expression object together. */ - pNew->pIndex = pExpr->pIndex; - pNew->pConfig = pExpr->pConfig; -@@ -214748,7 +242400,8 @@ static void sqlite3Fts5ParseSetDistance( - ); - return; - } -- nNear = nNear * 10 + (p->p[i] - '0'); -+ if( nNear<214748363 ) nNear = nNear * 10 + (p->p[i] - '0'); -+ /* ^^^^^^^^^^^^^^^--- Prevent integer overflow */ - } - }else{ - nNear = FTS5_DEFAULT_NEARDIST; -@@ -214777,7 +242430,7 @@ static Fts5Colset *fts5ParseColset( - assert( pParse->rc==SQLITE_OK ); - assert( iCol>=0 && iColpConfig->nCol ); - -- pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); -+ pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1)); - if( pNew==0 ){ - pParse->rc = SQLITE_NOMEM; - }else{ -@@ -214812,7 +242465,7 @@ static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p - int nCol = pParse->pConfig->nCol; - - pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, -- sizeof(Fts5Colset) + sizeof(int)*nCol -+ SZ_FTS5COLSET(nCol+1) - ); - if( pRet ){ - int i; -@@ -214873,7 +242526,7 @@ static Fts5Colset *sqlite3Fts5ParseColset( - static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ - Fts5Colset *pRet; - if( pOrig ){ -- sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); -+ sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol); - pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); - if( pRet ){ - memcpy(pRet, pOrig, (size_t)nByte); -@@ -214958,9 +242611,8 @@ static void sqlite3Fts5ParseSetColset( - ){ - Fts5Colset *pFree = pColset; - if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ -- pParse->rc = SQLITE_ERROR; -- pParse->zErr = sqlite3_mprintf( -- "fts5: column queries are not supported (detail=none)" -+ sqlite3Fts5ParseError(pParse, -+ "fts5: column queries are not supported (detail=none)" - ); - }else{ - fts5ParseSetColset(pParse, pExpr, pColset, &pFree); -@@ -215001,7 +242653,11 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){ - } - } - -+/* -+** Add pSub as a child of p. -+*/ - static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ -+ int ii = p->nChild; - if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){ - int nByte = sizeof(Fts5ExprNode*) * pSub->nChild; - memcpy(&p->apChild[p->nChild], pSub->apChild, nByte); -@@ -215010,6 +242666,73 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ - }else{ - p->apChild[p->nChild++] = pSub; - } -+ for( ; iinChild; ii++){ -+ p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1); -+ } -+} -+ -+/* -+** This function is used when parsing LIKE or GLOB patterns against -+** trigram indexes that specify either detail=column or detail=none. -+** It converts a phrase: -+** -+** abc + def + ghi -+** -+** into an AND tree: -+** -+** abc AND def AND ghi -+*/ -+static Fts5ExprNode *fts5ParsePhraseToAnd( -+ Fts5Parse *pParse, -+ Fts5ExprNearset *pNear -+){ -+ int nTerm = pNear->apPhrase[0]->nTerm; -+ int ii; -+ int nByte; -+ Fts5ExprNode *pRet; -+ -+ assert( pNear->nPhrase==1 ); -+ assert( pParse->bPhraseToAnd ); -+ -+ nByte = SZ_FTS5EXPRNODE(nTerm+1); -+ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); -+ if( pRet ){ -+ pRet->eType = FTS5_AND; -+ pRet->nChild = nTerm; -+ pRet->iHeight = 1; -+ fts5ExprAssignXNext(pRet); -+ pParse->nPhrase--; -+ for(ii=0; iirc, SZ_FTS5EXPRPHRASE(1) -+ ); -+ if( pPhrase ){ -+ if( parseGrowPhraseArray(pParse) ){ -+ fts5ExprPhraseFree(pPhrase); -+ }else{ -+ Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii]; -+ Fts5ExprTerm *pTo = &pPhrase->aTerm[0]; -+ pParse->apPhrase[pParse->nPhrase++] = pPhrase; -+ pPhrase->nTerm = 1; -+ pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm); -+ pTo->nQueryTerm = p->nQueryTerm; -+ pTo->nFullTerm = p->nFullTerm; -+ pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING, -+ 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) -+ ); -+ } -+ } -+ } -+ -+ if( pParse->rc ){ -+ sqlite3Fts5ParseNodeFree(pRet); -+ pRet = 0; -+ }else{ -+ sqlite3Fts5ParseNearsetFree(pNear); -+ } -+ } -+ -+ return pRet; - } - - /* -@@ -215036,51 +242759,67 @@ static Fts5ExprNode *sqlite3Fts5ParseNode( - if( eType!=FTS5_STRING && pLeft==0 ) return pRight; - if( eType!=FTS5_STRING && pRight==0 ) return pLeft; - -- if( eType==FTS5_NOT ){ -- nChild = 2; -- }else if( eType==FTS5_AND || eType==FTS5_OR ){ -- nChild = 2; -- if( pLeft->eType==eType ) nChild += pLeft->nChild-1; -- if( pRight->eType==eType ) nChild += pRight->nChild-1; -- } -+ if( eType==FTS5_STRING -+ && pParse->bPhraseToAnd -+ && pNear->apPhrase[0]->nTerm>1 -+ ){ -+ pRet = fts5ParsePhraseToAnd(pParse, pNear); -+ }else{ -+ if( eType==FTS5_NOT ){ -+ nChild = 2; -+ }else if( eType==FTS5_AND || eType==FTS5_OR ){ -+ nChild = 2; -+ if( pLeft->eType==eType ) nChild += pLeft->nChild-1; -+ if( pRight->eType==eType ) nChild += pRight->nChild-1; -+ } - -- nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); -- pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); -+ nByte = SZ_FTS5EXPRNODE(nChild); -+ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); - -- if( pRet ){ -- pRet->eType = eType; -- pRet->pNear = pNear; -- fts5ExprAssignXNext(pRet); -- if( eType==FTS5_STRING ){ -- int iPhrase; -- for(iPhrase=0; iPhrasenPhrase; iPhrase++){ -- pNear->apPhrase[iPhrase]->pNode = pRet; -- if( pNear->apPhrase[iPhrase]->nTerm==0 ){ -- pRet->xNext = 0; -- pRet->eType = FTS5_EOF; -- } -- } -- -- if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ -- Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; -- if( pNear->nPhrase!=1 -- || pPhrase->nTerm>1 -- || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) -- ){ -- assert( pParse->rc==SQLITE_OK ); -- pParse->rc = SQLITE_ERROR; -- assert( pParse->zErr==0 ); -- pParse->zErr = sqlite3_mprintf( -- "fts5: %s queries are not supported (detail!=full)", -- pNear->nPhrase==1 ? "phrase": "NEAR" -- ); -- sqlite3_free(pRet); -+ if( pRet ){ -+ pRet->eType = eType; -+ pRet->pNear = pNear; -+ fts5ExprAssignXNext(pRet); -+ if( eType==FTS5_STRING ){ -+ int iPhrase; -+ for(iPhrase=0; iPhrasenPhrase; iPhrase++){ -+ pNear->apPhrase[iPhrase]->pNode = pRet; -+ if( pNear->apPhrase[iPhrase]->nTerm==0 ){ -+ pRet->xNext = 0; -+ pRet->eType = FTS5_EOF; -+ } -+ } -+ -+ if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ -+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; -+ if( pNear->nPhrase!=1 -+ || pPhrase->nTerm>1 -+ || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst) -+ ){ -+ sqlite3Fts5ParseError(pParse, -+ "fts5: %s queries are not supported (detail!=full)", -+ pNear->nPhrase==1 ? "phrase": "NEAR" -+ ); -+ sqlite3Fts5ParseNodeFree(pRet); -+ pRet = 0; -+ pNear = 0; -+ assert( pLeft==0 && pRight==0 ); -+ } -+ } -+ }else{ -+ assert( pNear==0 ); -+ fts5ExprAddChildren(pRet, pLeft); -+ fts5ExprAddChildren(pRet, pRight); -+ pLeft = pRight = 0; -+ if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){ -+ sqlite3Fts5ParseError(pParse, -+ "fts5 expression tree is too large (maximum depth %d)", -+ SQLITE_FTS5_MAX_EXPR_DEPTH -+ ); -+ sqlite3Fts5ParseNodeFree(pRet); - pRet = 0; - } - } -- }else{ -- fts5ExprAddChildren(pRet, pLeft); -- fts5ExprAddChildren(pRet, pRight); - } - } - } -@@ -215115,6 +242854,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( - assert( pRight->eType==FTS5_STRING - || pRight->eType==FTS5_TERM - || pRight->eType==FTS5_EOF -+ || (pRight->eType==FTS5_AND && pParse->bPhraseToAnd) - ); - - if( pLeft->eType==FTS5_AND ){ -@@ -215128,6 +242868,8 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( - ); - - if( pRight->eType==FTS5_EOF ){ -+ assert( pParse->apPhrase!=0 ); -+ assert( pParse->nPhrase>0 ); - assert( pParse->apPhrase[pParse->nPhrase-1]==pRight->pNear->apPhrase[0] ); - sqlite3Fts5ParseNodeFree(pRight); - pRet = pLeft; -@@ -215158,6 +242900,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( - return pRet; - } - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ - sqlite3_int64 nByte = 0; - Fts5ExprTerm *p; -@@ -215165,16 +242908,17 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ - - /* Determine the maximum amount of space required. */ - for(p=pTerm; p; p=p->pSynonym){ -- nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2; -+ nByte += pTerm->nQueryTerm * 2 + 3 + 2; - } - zQuoted = sqlite3_malloc64(nByte); - - if( zQuoted ){ - int i = 0; - for(p=pTerm; p; p=p->pSynonym){ -- char *zIn = p->zTerm; -+ char *zIn = p->pTerm; -+ char *zEnd = &zIn[p->nQueryTerm]; - zQuoted[i++] = '"'; -- while( *zIn ){ -+ while( zInnTerm; iTerm++){ -- char *zTerm = pPhrase->aTerm[iTerm].zTerm; -- zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); -+ Fts5ExprTerm *p = &pPhrase->aTerm[iTerm]; -+ zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ", -+ p->nQueryTerm, p->pTerm -+ ); - if( pPhrase->aTerm[iTerm].bPrefix ){ - zRet = fts5PrintfAppend(zRet, "*"); - } -@@ -215263,6 +243009,8 @@ static char *fts5ExprPrintTcl( - if( zRet==0 ) return 0; - } - -+ }else if( pExpr->eType==0 ){ -+ zRet = sqlite3_mprintf("{}"); - }else{ - char const *zOp = 0; - int i; -@@ -215301,8 +243049,17 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){ - int iTerm; - - if( pNear->pColset ){ -- int iCol = pNear->pColset->aiCol[0]; -- zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[iCol]); -+ int ii; -+ Fts5Colset *pColset = pNear->pColset; -+ if( pColset->nCol>1 ) zRet = fts5PrintfAppend(zRet, "{"); -+ for(ii=0; iinCol; ii++){ -+ zRet = fts5PrintfAppend(zRet, "%s%s", -+ pConfig->azCol[pColset->aiCol[ii]], ii==pColset->nCol-1 ? "" : " " -+ ); -+ } -+ if( zRet ){ -+ zRet = fts5PrintfAppend(zRet, "%s : ", pColset->nCol>1 ? "}" : ""); -+ } - if( zRet==0 ) return 0; - } - -@@ -215425,7 +243182,7 @@ static void fts5ExprFunction( - - rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr); - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts5ExprNew(pConfig, pConfig->nCol, zExpr, &pExpr, &zErr); -+ rc = sqlite3Fts5ExprNew(pConfig, 0, pConfig->nCol, zExpr, &pExpr, &zErr); - } - if( rc==SQLITE_OK ){ - char *zText; -@@ -215515,12 +243272,14 @@ static void fts5ExprFold( - sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics)); - } - } -+#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */ - - /* - ** This is called during initialization to register the fts5_expr() scalar - ** UDF with the SQLite handle passed as the only argument. - */ - static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - struct Fts5ExprFunc { - const char *z; - void (*x)(sqlite3_context*,int,sqlite3_value**); -@@ -215538,6 +243297,10 @@ static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ - struct Fts5ExprFunc *p = &aFunc[i]; - rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0); - } -+#else -+ int rc = SQLITE_OK; -+ UNUSED_PARAM2(pGlobal,db); -+#endif - - /* Avoid warnings indicating that sqlite3Fts5ParserTrace() and - ** sqlite3Fts5ParserFallback() are unused */ -@@ -215588,6 +243351,15 @@ struct Fts5PoslistPopulator { - int bMiss; - }; - -+/* -+** Clear the position lists associated with all phrases in the expression -+** passed as the first argument. Argument bLive is true if the expression -+** might be pointing to a real entry, otherwise it has just been reset. -+** -+** At present this function is only used for detail=col and detail=none -+** fts5 tables. This implies that all phrases must be at most 1 token -+** in size, as phrase matches are not supported without detail=full. -+*/ - static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ - Fts5PoslistPopulator *pRet; - pRet = sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); -@@ -215597,7 +243369,7 @@ static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int b - for(i=0; inPhrase; i++){ - Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist; - Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; -- assert( pExpr->apExprPhrase[i]->nTerm==1 ); -+ assert( pExpr->apExprPhrase[i]->nTerm<=1 ); - if( bLive && - (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof) - ){ -@@ -215628,6 +243400,17 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ - return 0; - } - -+/* -+** pToken is a buffer nToken bytes in size that may or may not contain -+** an embedded 0x00 byte. If it does, return the number of bytes in -+** the buffer before the 0x00. If it does not, return nToken. -+*/ -+static int fts5QueryTerm(const char *pToken, int nToken){ -+ int ii; -+ for(ii=0; iipExpr; - int i; -+ int nQuery = nToken; -+ i64 iRowid = pExpr->pRoot->iRowid; - - UNUSED_PARAM2(iUnused1, iUnused2); - -- if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; -+ if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE; -+ if( pExpr->pConfig->bTokendata ){ -+ nQuery = fts5QueryTerm(pToken, nQuery); -+ } - if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; - for(i=0; inPhrase; i++){ -- Fts5ExprTerm *pTerm; -+ Fts5ExprTerm *pT; - if( p->aPopulator[i].bOk==0 ) continue; -- for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ -- int nTerm = (int)strlen(pTerm->zTerm); -- if( (nTerm==nToken || (nTermbPrefix)) -- && memcmp(pTerm->zTerm, pToken, nTerm)==0 -+ for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ -+ if( (pT->nQueryTerm==nQuery || (pT->nQueryTermbPrefix)) -+ && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 - ){ - int rc = sqlite3Fts5PoslistWriterAppend( - &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff - ); -+ if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){ -+ int iCol = p->iOff>>32; -+ int iTokOff = p->iOff & 0x7FFFFFFF; -+ rc = sqlite3Fts5IndexIterWriteTokendata( -+ pT->pIter, pToken, nToken, iRowid, iCol, iTokOff -+ ); -+ } - if( rc ) return rc; - break; - } -@@ -215708,6 +243502,7 @@ static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){ - pNode->iRowid = iRowid; - pNode->bEof = 0; - switch( pNode->eType ){ -+ case 0: - case FTS5_TERM: - case FTS5_STRING: - return (pNode->pNear->apPhrase[0]->poslist.n>0); -@@ -215790,6 +243585,82 @@ static int sqlite3Fts5ExprPhraseCollist( - return rc; - } - -+/* -+** Does the work of the fts5_api.xQueryToken() API method. -+*/ -+static int sqlite3Fts5ExprQueryToken( -+ Fts5Expr *pExpr, -+ int iPhrase, -+ int iToken, -+ const char **ppOut, -+ int *pnOut -+){ -+ Fts5ExprPhrase *pPhrase = 0; -+ -+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ -+ return SQLITE_RANGE; -+ } -+ pPhrase = pExpr->apExprPhrase[iPhrase]; -+ if( iToken<0 || iToken>=pPhrase->nTerm ){ -+ return SQLITE_RANGE; -+ } -+ -+ *ppOut = pPhrase->aTerm[iToken].pTerm; -+ *pnOut = pPhrase->aTerm[iToken].nFullTerm; -+ return SQLITE_OK; -+} -+ -+/* -+** Does the work of the fts5_api.xInstToken() API method. -+*/ -+static int sqlite3Fts5ExprInstToken( -+ Fts5Expr *pExpr, -+ i64 iRowid, -+ int iPhrase, -+ int iCol, -+ int iOff, -+ int iToken, -+ const char **ppOut, -+ int *pnOut -+){ -+ Fts5ExprPhrase *pPhrase = 0; -+ Fts5ExprTerm *pTerm = 0; -+ int rc = SQLITE_OK; -+ -+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ -+ return SQLITE_RANGE; -+ } -+ pPhrase = pExpr->apExprPhrase[iPhrase]; -+ if( iToken<0 || iToken>=pPhrase->nTerm ){ -+ return SQLITE_RANGE; -+ } -+ pTerm = &pPhrase->aTerm[iToken]; -+ if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){ -+ rc = sqlite3Fts5IterToken( -+ pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm, -+ iRowid, iCol, iOff+iToken, ppOut, pnOut -+ ); -+ }else{ -+ *ppOut = pTerm->pTerm; -+ *pnOut = pTerm->nFullTerm; -+ } -+ return rc; -+} -+ -+/* -+** Clear the token mappings for all Fts5IndexIter objects managed by -+** the expression passed as the only argument. -+*/ -+static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ -+ int ii; -+ for(ii=0; iinPhrase; ii++){ -+ Fts5ExprTerm *pT; -+ for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){ -+ sqlite3Fts5IndexIterClearTokendata(pT->pIter); -+ } -+ } -+} -+ - /* - ** 2014 August 11 - ** -@@ -215812,7 +243683,7 @@ typedef struct Fts5HashEntry Fts5HashEntry; - - /* - ** This file contains the implementation of an in-memory hash table used --** to accumuluate "term -> doclist" content before it is flused to a level-0 -+** to accumulate "term -> doclist" content before it is flushed to a level-0 - ** segment. - */ - -@@ -215828,10 +243699,15 @@ struct Fts5Hash { - - /* - ** Each entry in the hash table is represented by an object of the --** following type. Each object, its key (a nul-terminated string) and --** its current data are stored in a single memory allocation. The --** key immediately follows the object in memory. The position list --** data immediately follows the key data in memory. -+** following type. Each object, its key, and its current data are stored -+** in a single memory allocation. The key immediately follows the object -+** in memory. The position list data immediately follows the key data -+** in memory. -+** -+** The key is Fts5HashEntry.nKey bytes in size. It consists of a single -+** byte identifying the index (either the main term index or a prefix-index), -+** followed by the term data. For example: "0token". There is no -+** nul-terminator - in this case nKey=6. - ** - ** The data that follows the key is in a similar, but not identical format - ** to the doclist data stored in the database. It is: -@@ -215864,7 +243740,7 @@ struct Fts5HashEntry { - }; - - /* --** Eqivalent to: -+** Equivalent to: - ** - ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } - */ -@@ -215966,8 +243842,7 @@ static int fts5HashResize(Fts5Hash *pHash){ - unsigned int iHash; - Fts5HashEntry *p = apOld[i]; - apOld[i] = p->pHashNext; -- iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), -- (int)strlen(fts5EntryKey(p))); -+ iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey); - p->pHashNext = apNew[iHash]; - apNew[iHash] = p; - } -@@ -216051,7 +243926,7 @@ static int sqlite3Fts5HashWrite( - for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ - char *zKey = fts5EntryKey(p); - if( zKey[0]==bByte -- && p->nKey==nToken -+ && p->nKey==nToken+1 - && memcmp(&zKey[1], pToken, nToken)==0 - ){ - break; -@@ -216081,9 +243956,9 @@ static int sqlite3Fts5HashWrite( - zKey[0] = bByte; - memcpy(&zKey[1], pToken, nToken); - assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) ); -- p->nKey = nToken; -+ p->nKey = nToken+1; - zKey[nToken+1] = '\0'; -- p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry); -+ p->nData = nToken+1 + sizeof(Fts5HashEntry); - p->pHashNext = pHash->aSlot[iHash]; - pHash->aSlot[iHash] = p; - pHash->nEntry++; -@@ -216098,7 +243973,6 @@ static int sqlite3Fts5HashWrite( - p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1); - } - -- nIncr += p->nData; - }else{ - - /* Appending to an existing hash-entry. Check that there is enough -@@ -216131,8 +244005,9 @@ static int sqlite3Fts5HashWrite( - /* If this is a new rowid, append the 4-byte size field for the previous - ** entry, and the new rowid for this entry. */ - if( iRowid!=p->iRowid ){ -+ u64 iDiff = (u64)iRowid - (u64)p->iRowid; - fts5HashAddPoslistSize(pHash, p, 0); -- p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); -+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iDiff); - p->iRowid = iRowid; - bNew = 1; - p->iSzPoslist = p->nData; -@@ -216148,7 +244023,7 @@ static int sqlite3Fts5HashWrite( - p->bContent = 1; - }else{ - /* Append a new column value, if necessary */ -- assert( iCol>=p->iCol ); -+ assert_nc( iCol>=p->iCol ); - if( iCol!=p->iCol ){ - if( pHash->eDetail==FTS5_DETAIL_FULL ){ - pPtr[p->nData++] = 0x01; -@@ -216200,12 +244075,17 @@ static Fts5HashEntry *fts5HashEntryMerge( - *ppOut = p1; - p1 = 0; - }else{ -- int i = 0; - char *zKey1 = fts5EntryKey(p1); - char *zKey2 = fts5EntryKey(p2); -- while( zKey1[i]==zKey2[i] ) i++; -+ int nMin = MIN(p1->nKey, p2->nKey); -+ -+ int cmp = memcmp(zKey1, zKey2, nMin); -+ if( cmp==0 ){ -+ cmp = p1->nKey - p2->nKey; -+ } -+ assert( cmp!=0 ); - -- if( ((u8)zKey1[i])>((u8)zKey2[i]) ){ -+ if( cmp>0 ){ - /* p2 is smaller */ - *ppOut = p2; - ppOut = &p2->pScanNext; -@@ -216224,10 +244104,8 @@ static Fts5HashEntry *fts5HashEntryMerge( - } - - /* --** Extract all tokens from hash table iHash and link them into a list --** in sorted order. The hash table is cleared before returning. It is --** the responsibility of the caller to free the elements of the returned --** list. -+** Link all tokens from hash table iHash into a list in sorted order. The -+** tokens are not removed from the hash table. - */ - static int fts5HashEntrySort( - Fts5Hash *pHash, -@@ -216249,7 +244127,7 @@ static int fts5HashEntrySort( - Fts5HashEntry *pIter; - for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ - if( pTerm==0 -- || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) -+ || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) - ){ - Fts5HashEntry *pEntry = pIter; - pEntry->pScanNext = 0; -@@ -216267,7 +244145,6 @@ static int fts5HashEntrySort( - pList = fts5HashEntryMerge(pList, ap[i]); - } - -- pHash->nEntry = 0; - sqlite3_free(ap); - *ppSorted = pList; - return SQLITE_OK; -@@ -216289,12 +244166,11 @@ static int sqlite3Fts5HashQuery( - - for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ - zKey = fts5EntryKey(p); -- assert( p->nKey+1==(int)strlen(zKey) ); -- if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break; -+ if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break; - } - - if( p ){ -- int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1; -+ int nHashPre = sizeof(Fts5HashEntry) + nTerm; - int nList = p->nData - nHashPre; - u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10)); - if( pRet ){ -@@ -216321,6 +244197,28 @@ static int sqlite3Fts5HashScanInit( - return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan); - } - -+#ifdef SQLITE_DEBUG -+static int fts5HashCount(Fts5Hash *pHash){ -+ int nEntry = 0; -+ int ii; -+ for(ii=0; iinSlot; ii++){ -+ Fts5HashEntry *p = 0; -+ for(p=pHash->aSlot[ii]; p; p=p->pHashNext){ -+ nEntry++; -+ } -+ } -+ return nEntry; -+} -+#endif -+ -+/* -+** Return true if the hash table is empty, false otherwise. -+*/ -+static int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){ -+ assert( pHash->nEntry==fts5HashCount(pHash) ); -+ return pHash->nEntry==0; -+} -+ - static void sqlite3Fts5HashScanNext(Fts5Hash *p){ - assert( !sqlite3Fts5HashScanEof(p) ); - p->pScan = p->pScan->pScanNext; -@@ -216333,19 +244231,22 @@ static int sqlite3Fts5HashScanEof(Fts5Hash *p){ - static void sqlite3Fts5HashScanEntry( - Fts5Hash *pHash, - const char **pzTerm, /* OUT: term (nul-terminated) */ -+ int *pnTerm, /* OUT: Size of term in bytes */ - const u8 **ppDoclist, /* OUT: pointer to doclist */ - int *pnDoclist /* OUT: size of doclist in bytes */ - ){ - Fts5HashEntry *p; - if( (p = pHash->pScan) ){ - char *zKey = fts5EntryKey(p); -- int nTerm = (int)strlen(zKey); -+ int nTerm = p->nKey; - fts5HashAddPoslistSize(pHash, p, 0); - *pzTerm = zKey; -- *ppDoclist = (const u8*)&zKey[nTerm+1]; -- *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); -+ *pnTerm = nTerm; -+ *ppDoclist = (const u8*)&zKey[nTerm]; -+ *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm); - }else{ - *pzTerm = 0; -+ *pnTerm = 0; - *ppDoclist = 0; - *pnDoclist = 0; - } -@@ -216407,6 +244308,26 @@ static void sqlite3Fts5HashScanEntry( - # error "FTS5_MAX_PREFIX_INDEXES is too large" - #endif - -+#define FTS5_MAX_LEVEL 64 -+ -+/* -+** There are two versions of the format used for the structure record: -+** -+** 1. the legacy format, that may be read by all fts5 versions, and -+** -+** 2. the V2 format, which is used by contentless_delete=1 databases. -+** -+** Both begin with a 4-byte "configuration cookie" value. Then, a legacy -+** format structure record contains a varint - the number of levels in -+** the structure. Whereas a V2 structure record contains the constant -+** 4 bytes [0xff 0x00 0x00 0x01]. This is unambiguous as the value of a -+** varint has to be at least 16256 to begin with "0xFF". And the default -+** maximum number of levels is 64. -+** -+** See below for more on structure record formats. -+*/ -+#define FTS5_STRUCTURE_V2 "\xFF\x00\x00\x01" -+ - /* - ** Details: - ** -@@ -216414,7 +244335,7 @@ static void sqlite3Fts5HashScanEntry( - ** - ** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB); - ** --** , contains the following 5 types of records. See the comments surrounding -+** , contains the following 6 types of records. See the comments surrounding - ** the FTS5_*_ROWID macros below for a description of how %_data rowids are - ** assigned to each fo them. - ** -@@ -216423,12 +244344,12 @@ static void sqlite3Fts5HashScanEntry( - ** The set of segments that make up an index - the index structure - are - ** recorded in a single record within the %_data table. The record consists - ** of a single 32-bit configuration cookie value followed by a list of --** SQLite varints. If the FTS table features more than one index (because --** there are one or more prefix indexes), it is guaranteed that all share --** the same cookie value. -+** SQLite varints. - ** --** Immediately following the configuration cookie, the record begins with --** three varints: -+** If the structure record is a V2 record, the configuration cookie is -+** followed by the following 4 bytes: [0xFF 0x00 0x00 0x01]. -+** -+** Next, the record continues with three varints: - ** - ** + number of levels, - ** + total number of segments on all levels, -@@ -216443,6 +244364,12 @@ static void sqlite3Fts5HashScanEntry( - ** + first leaf page number (often 1, always greater than 0) - ** + final leaf page number - ** -+** Then, for V2 structures only: -+** -+** + lower origin counter value, -+** + upper origin counter value, -+** + the number of tombstone hash pages. -+** - ** 2. The Averages Record: - ** - ** A single record within the %_data table. The data is a list of varints. -@@ -216558,6 +244485,38 @@ static void sqlite3Fts5HashScanEntry( - ** * A list of delta-encoded varints - the first rowid on each subsequent - ** child page. - ** -+** 6. Tombstone Hash Page -+** -+** These records are only ever present in contentless_delete=1 tables. -+** There are zero or more of these associated with each segment. They -+** are used to store the tombstone rowids for rows contained in the -+** associated segments. -+** -+** The set of nHashPg tombstone hash pages associated with a single -+** segment together form a single hash table containing tombstone rowids. -+** To find the page of the hash on which a key might be stored: -+** -+** iPg = (rowid % nHashPg) -+** -+** Then, within page iPg, which has nSlot slots: -+** -+** iSlot = (rowid / nHashPg) % nSlot -+** -+** Each tombstone hash page begins with an 8 byte header: -+** -+** 1-byte: Key-size (the size in bytes of each slot). Either 4 or 8. -+** 1-byte: rowid-0-tombstone flag. This flag is only valid on the -+** first tombstone hash page for each segment (iPg=0). If set, -+** the hash table contains rowid 0. If clear, it does not. -+** Rowid 0 is handled specially. -+** 2-bytes: unused. -+** 4-bytes: Big-endian integer containing number of entries on page. -+** -+** Following this are nSlot 4 or 8 byte slots (depending on the key-size -+** in the first byte of the page header). The number of slots may be -+** determined based on the size of the page record and the key-size: -+** -+** nSlot = (nByte - 8) / key-size - */ - - /* -@@ -216591,6 +244550,7 @@ static void sqlite3Fts5HashScanEntry( - - #define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno) - #define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno) -+#define FTS5_TOMBSTONE_ROWID(segid,ipg) fts5_dri(segid+(1<<16), 0, 0, ipg) - - #ifdef SQLITE_DEBUG - static int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; } -@@ -216617,6 +244577,9 @@ typedef struct Fts5SegWriter Fts5SegWriter; - typedef struct Fts5Structure Fts5Structure; - typedef struct Fts5StructureLevel Fts5StructureLevel; - typedef struct Fts5StructureSegment Fts5StructureSegment; -+typedef struct Fts5TokenDataIter Fts5TokenDataIter; -+typedef struct Fts5TokenDataMap Fts5TokenDataMap; -+typedef struct Fts5TombstoneArray Fts5TombstoneArray; - - struct Fts5Data { - u8 *p; /* Pointer to buffer containing record */ -@@ -216626,6 +244589,12 @@ struct Fts5Data { - - /* - ** One object per %_data table. -+** -+** nContentlessDelete: -+** The number of contentless delete operations since the most recent -+** call to fts5IndexFlush() or fts5IndexDiscardData(). This is tracked -+** so that extra auto-merge work can be done by fts5IndexFlush() to -+** account for the delete operations. - */ - struct Fts5Index { - Fts5Config *pConfig; /* Virtual table configuration */ -@@ -216640,19 +244609,25 @@ struct Fts5Index { - int nPendingData; /* Current bytes of pending data */ - i64 iWriteRowid; /* Rowid for current doc being written */ - int bDelete; /* Current write is a delete */ -+ int nContentlessDelete; /* Number of contentless delete ops */ -+ int nPendingRow; /* Number of INSERT in hash table */ - - /* Error state. */ - int rc; /* Current error code */ -+ int flushRc; - - /* State used by the fts5DataXXX() functions. */ - sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ - sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ - sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ - sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ -- sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=? */ -+ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ - sqlite3_stmt *pIdxSelect; -+ sqlite3_stmt *pIdxNextSelect; - int nRead; /* Total number of blocks read */ - -+ sqlite3_stmt *pDeleteFromIdx; -+ - sqlite3_stmt *pDataVersion; - i64 iStructVersion; /* data_version when pStruct read */ - Fts5Structure *pStruct; /* Current db structure (or NULL) */ -@@ -216672,11 +244647,23 @@ struct Fts5DoclistIter { - ** The contents of the "structure" record for each index are represented - ** using an Fts5Structure record in memory. Which uses instances of the - ** other Fts5StructureXXX types as components. -+** -+** nOriginCntr: -+** This value is set to non-zero for structure records created for -+** contentlessdelete=1 tables only. In that case it represents the -+** origin value to apply to the next top-level segment created. - */ - struct Fts5StructureSegment { - int iSegid; /* Segment id */ - int pgnoFirst; /* First leaf page number in segment */ - int pgnoLast; /* Last leaf page number in segment */ -+ -+ /* contentlessdelete=1 tables only: */ -+ u64 iOrigin1; -+ u64 iOrigin2; -+ int nPgTombstone; /* Number of tombstone hash table pages */ -+ u64 nEntryTombstone; /* Number of tombstone entries that "count" */ -+ u64 nEntry; /* Number of rows in this segment */ - }; - struct Fts5StructureLevel { - int nMerge; /* Number of segments in incr-merge */ -@@ -216686,11 +244673,16 @@ struct Fts5StructureLevel { - struct Fts5Structure { - int nRef; /* Object reference count */ - u64 nWriteCounter; /* Total leaves written to level 0 */ -+ u64 nOriginCntr; /* Origin value for next top-level segment */ - int nSegment; /* Total segments in this structure */ - int nLevel; /* Number of levels in this index */ -- Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */ -+ Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */ - }; - -+/* Size (in bytes) of an Fts5Structure object holding up to N levels */ -+#define SZ_FTS5STRUCTURE(N) \ -+ (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel)) -+ - /* - ** An object of type Fts5SegWriter is used to write to segments. - */ -@@ -216745,9 +244737,6 @@ struct Fts5CResult { - ** iLeafOffset: - ** Byte offset within the current leaf that is the first byte of the - ** position list data (one byte passed the position-list size field). --** rowid field of the current entry. Usually this is the size field of the --** position list data. The exception is if the rowid for the current entry --** is the last thing on the leaf page. - ** - ** pLeaf: - ** Buffer containing current leaf page data. Set to NULL at EOF. -@@ -216777,6 +244766,13 @@ struct Fts5CResult { - ** - ** iTermIdx: - ** Index of current term on iTermLeafPgno. -+** -+** apTombstone/nTombstone: -+** These are used for contentless_delete=1 tables only. When the cursor -+** is first allocated, the apTombstone[] array is allocated so that it -+** is large enough for all tombstones hash pages associated with the -+** segment. The pages themselves are loaded lazily from the database as -+** they are required. - */ - struct Fts5SegIter { - Fts5StructureSegment *pSeg; /* Segment to iterate through */ -@@ -216784,7 +244780,8 @@ struct Fts5SegIter { - int iLeafPgno; /* Current leaf page number */ - Fts5Data *pLeaf; /* Current leaf data */ - Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ -- int iLeafOffset; /* Byte offset within current leaf */ -+ i64 iLeafOffset; /* Byte offset within current leaf */ -+ Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */ - - /* Next method */ - void (*xNext)(Fts5Index*, Fts5SegIter*, int*); -@@ -216811,6 +244808,19 @@ struct Fts5SegIter { - u8 bDel; /* True if the delete flag is set */ - }; - -+/* -+** Array of tombstone pages. Reference counted. -+*/ -+struct Fts5TombstoneArray { -+ int nRef; /* Number of pointers to this object */ -+ int nTombstone; -+ Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */ -+}; -+ -+/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */ -+#define SZ_FTS5TOMBSTONEARRAY(N) \ -+ (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*)) -+ - /* - ** Argument is a pointer to an Fts5Data structure that contains a - ** leaf page. -@@ -216855,9 +244865,16 @@ struct Fts5SegIter { - ** poslist: - ** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered. - ** There is no way to tell if this is populated or not. -+** -+** pColset: -+** If not NULL, points to an object containing a set of column indices. -+** Only matches that occur in one of these columns will be returned. -+** The Fts5Iter does not own the Fts5Colset object, and so it is not -+** freed when the iterator is closed - it is owned by the upper layer. - */ - struct Fts5Iter { - Fts5IndexIter base; /* Base class containing output vars */ -+ Fts5TokenDataIter *pTokenDataIter; - - Fts5Index *pIndex; /* Index that owns this iterator */ - Fts5Buffer poslist; /* Buffer containing current poslist */ -@@ -216872,9 +244889,11 @@ struct Fts5Iter { - - i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ - Fts5CResult *aFirst; /* Current merge state (see above) */ -- Fts5SegIter aSeg[1]; /* Array of segment iterators */ -+ Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */ - }; - -+/* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */ -+#define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter)) - - /* - ** An instance of the following type is used to iterate through the contents -@@ -216902,9 +244921,13 @@ struct Fts5DlidxLvl { - struct Fts5DlidxIter { - int nLvl; - int iSegid; -- Fts5DlidxLvl aLvl[1]; -+ Fts5DlidxLvl aLvl[FLEXARRAY]; - }; - -+/* Size (in bytes) of an Fts5DlidxIter object with up to N levels */ -+#define SZ_FTS5DLIDXITER(N) \ -+ (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl)) -+ - static void fts5PutU16(u8 *aOut, u16 iVal){ - aOut[0] = (iVal>>8); - aOut[1] = (iVal&0xFF); -@@ -216914,6 +244937,60 @@ static u16 fts5GetU16(const u8 *aIn){ - return ((u16)aIn[0] << 8) + aIn[1]; - } - -+/* -+** The only argument points to a buffer at least 8 bytes in size. This -+** function interprets the first 8 bytes of the buffer as a 64-bit big-endian -+** unsigned integer and returns the result. -+*/ -+static u64 fts5GetU64(u8 *a){ -+ return ((u64)a[0] << 56) -+ + ((u64)a[1] << 48) -+ + ((u64)a[2] << 40) -+ + ((u64)a[3] << 32) -+ + ((u64)a[4] << 24) -+ + ((u64)a[5] << 16) -+ + ((u64)a[6] << 8) -+ + ((u64)a[7] << 0); -+} -+ -+/* -+** The only argument points to a buffer at least 4 bytes in size. This -+** function interprets the first 4 bytes of the buffer as a 32-bit big-endian -+** unsigned integer and returns the result. -+*/ -+static u32 fts5GetU32(const u8 *a){ -+ return ((u32)a[0] << 24) -+ + ((u32)a[1] << 16) -+ + ((u32)a[2] << 8) -+ + ((u32)a[3] << 0); -+} -+ -+/* -+** Write iVal, formated as a 64-bit big-endian unsigned integer, to the -+** buffer indicated by the first argument. -+*/ -+static void fts5PutU64(u8 *a, u64 iVal){ -+ a[0] = ((iVal >> 56) & 0xFF); -+ a[1] = ((iVal >> 48) & 0xFF); -+ a[2] = ((iVal >> 40) & 0xFF); -+ a[3] = ((iVal >> 32) & 0xFF); -+ a[4] = ((iVal >> 24) & 0xFF); -+ a[5] = ((iVal >> 16) & 0xFF); -+ a[6] = ((iVal >> 8) & 0xFF); -+ a[7] = ((iVal >> 0) & 0xFF); -+} -+ -+/* -+** Write iVal, formated as a 32-bit big-endian unsigned integer, to the -+** buffer indicated by the first argument. -+*/ -+static void fts5PutU32(u8 *a, u32 iVal){ -+ a[0] = ((iVal >> 24) & 0xFF); -+ a[1] = ((iVal >> 16) & 0xFF); -+ a[2] = ((iVal >> 8) & 0xFF); -+ a[3] = ((iVal >> 0) & 0xFF); -+} -+ - /* - ** Allocate and return a buffer at least nByte bytes in size. - ** -@@ -216953,8 +245030,11 @@ static int fts5BufferCompareBlob( - ** res = *pLeft - *pRight - */ - static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ -- int nCmp = MIN(pLeft->n, pRight->n); -- int res = fts5Memcmp(pLeft->p, pRight->p, nCmp); -+ int nCmp, res; -+ nCmp = MIN(pLeft->n, pRight->n); -+ assert( nCmp<=0 || pLeft->p!=0 ); -+ assert( nCmp<=0 || pRight->p!=0 ); -+ res = fts5Memcmp(pLeft->p, pRight->p, nCmp); - return (res==0 ? (pLeft->n - pRight->n) : res); - } - -@@ -216967,11 +245047,13 @@ static int fts5LeafFirstTermOff(Fts5Data *pLeaf){ - /* - ** Close the read-only blob handle, if it is open. - */ --static void sqlite3Fts5IndexCloseReader(Fts5Index *p){ -+static void fts5IndexCloseReader(Fts5Index *p){ - if( p->pReader ){ -+ int rc; - sqlite3_blob *pReader = p->pReader; - p->pReader = 0; -- sqlite3_blob_close(pReader); -+ rc = sqlite3_blob_close(pReader); -+ if( p->rc==SQLITE_OK ) p->rc = rc; - } - } - -@@ -216996,7 +245078,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ - assert( p->pReader==0 ); - p->pReader = pBlob; - if( rc!=SQLITE_OK ){ -- sqlite3Fts5IndexCloseReader(p); -+ fts5IndexCloseReader(p); - } - if( rc==SQLITE_ABORT ) rc = SQLITE_OK; - } -@@ -217020,11 +245102,12 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ - if( rc==SQLITE_OK ){ - u8 *aOut = 0; /* Read blob data into this buffer */ - int nByte = sqlite3_blob_bytes(p->pReader); -- sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING; -+ int szData = (sizeof(Fts5Data) + 7) & ~7; -+ sqlite3_int64 nAlloc = szData + nByte + FTS5_DATA_PADDING; - pRet = (Fts5Data*)sqlite3_malloc64(nAlloc); - if( pRet ){ - pRet->nn = nByte; -- aOut = pRet->p = (u8*)&pRet[1]; -+ aOut = pRet->p = (u8*)pRet + szData; - }else{ - rc = SQLITE_NOMEM; - } -@@ -217047,9 +245130,11 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ - } - - assert( (pRet==0)==(p->rc!=SQLITE_OK) ); -+ assert( pRet==0 || EIGHT_BYTE_ALIGNMENT( pRet->p ) ); - return pRet; - } - -+ - /* - ** Release a reference to data record returned by an earlier call to - ** fts5DataRead(). -@@ -217077,9 +245162,13 @@ static int fts5IndexPrepareStmt( - ){ - if( p->rc==SQLITE_OK ){ - if( zSql ){ -- p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, -+ int rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, - SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB, - ppStmt, 0); -+ /* If this prepare() call fails with SQLITE_ERROR, then one of the -+ ** %_idx or %_data tables has been removed or modified. Call this -+ ** corruption. */ -+ p->rc = (rc==SQLITE_ERROR ? SQLITE_CORRUPT : rc); - }else{ - p->rc = SQLITE_NOMEM; - } -@@ -217137,10 +245226,17 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){ - /* - ** Remove all records associated with segment iSegid. - */ --static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){ -+static void fts5DataRemoveSegment(Fts5Index *p, Fts5StructureSegment *pSeg){ -+ int iSegid = pSeg->iSegid; - i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0); - i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)-1; - fts5DataDelete(p, iFirst, iLast); -+ -+ if( pSeg->nPgTombstone ){ -+ i64 iTomb1 = FTS5_TOMBSTONE_ROWID(iSegid, 0); -+ i64 iTomb2 = FTS5_TOMBSTONE_ROWID(iSegid, pSeg->nPgTombstone-1); -+ fts5DataDelete(p, iTomb1, iTomb2); -+ } - if( p->pIdxDeleter==0 ){ - Fts5Config *pConfig = p->pConfig; - fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintf( -@@ -217174,6 +245270,58 @@ static void fts5StructureRef(Fts5Structure *pStruct){ - pStruct->nRef++; - } - -+static void *sqlite3Fts5StructureRef(Fts5Index *p){ -+ fts5StructureRef(p->pStruct); -+ return (void*)p->pStruct; -+} -+static void sqlite3Fts5StructureRelease(void *p){ -+ if( p ){ -+ fts5StructureRelease((Fts5Structure*)p); -+ } -+} -+static int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){ -+ if( p->pStruct!=(Fts5Structure*)pStruct ){ -+ return SQLITE_ABORT; -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Ensure that structure object (*pp) is writable. -+** -+** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If -+** an error occurs, (*pRc) is set to an SQLite error code before returning. -+*/ -+static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ -+ Fts5Structure *p = *pp; -+ if( *pRc==SQLITE_OK && p->nRef>1 ){ -+ i64 nByte = SZ_FTS5STRUCTURE(p->nLevel); -+ Fts5Structure *pNew; -+ pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); -+ if( pNew ){ -+ int i; -+ memcpy(pNew, p, nByte); -+ for(i=0; inLevel; i++) pNew->aLevel[i].aSeg = 0; -+ for(i=0; inLevel; i++){ -+ Fts5StructureLevel *pLvl = &pNew->aLevel[i]; -+ nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg; -+ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(pRc, nByte); -+ if( pLvl->aSeg==0 ){ -+ for(i=0; inLevel; i++){ -+ sqlite3_free(pNew->aLevel[i].aSeg); -+ } -+ sqlite3_free(pNew); -+ return; -+ } -+ memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte); -+ } -+ p->nRef--; -+ pNew->nRef = 1; -+ } -+ *pp = pNew; -+ } -+} -+ - /* - ** Deserialize and return the structure record currently stored in serialized - ** form within buffer pData/nData. -@@ -217199,11 +245347,19 @@ static int fts5StructureDecode( - int nSegment = 0; - sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */ - Fts5Structure *pRet = 0; /* Structure object to return */ -+ int bStructureV2 = 0; /* True for FTS5_STRUCTURE_V2 */ -+ u64 nOriginCntr = 0; /* Largest origin value seen so far */ - - /* Grab the cookie value */ - if( piCookie ) *piCookie = sqlite3Fts5Get32(pData); - i = 4; - -+ /* Check if this is a V2 structure record. Set bStructureV2 if it is. */ -+ if( 0==memcmp(&pData[i], FTS5_STRUCTURE_V2, 4) ){ -+ i += 4; -+ bStructureV2 = 1; -+ } -+ - /* Read the total number of levels and segments from the start of the - ** structure record. */ - i += fts5GetVarint32(&pData[i], nLevel); -@@ -217213,10 +245369,7 @@ static int fts5StructureDecode( - ){ - return FTS5_CORRUPT; - } -- nByte = ( -- sizeof(Fts5Structure) + /* Main structure */ -- sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ -- ); -+ nByte = SZ_FTS5STRUCTURE(nLevel); - pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); - - if( pRet ){ -@@ -217250,9 +245403,18 @@ static int fts5StructureDecode( - rc = FTS5_CORRUPT; - break; - } -+ assert( pSeg!=0 ); - i += fts5GetVarint32(&pData[i], pSeg->iSegid); - i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); - i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); -+ if( bStructureV2 ){ -+ i += fts5GetVarint(&pData[i], &pSeg->iOrigin1); -+ i += fts5GetVarint(&pData[i], &pSeg->iOrigin2); -+ i += fts5GetVarint32(&pData[i], pSeg->nPgTombstone); -+ i += fts5GetVarint(&pData[i], &pSeg->nEntryTombstone); -+ i += fts5GetVarint(&pData[i], &pSeg->nEntry); -+ nOriginCntr = MAX(nOriginCntr, pSeg->iOrigin2); -+ } - if( pSeg->pgnoLastpgnoFirst ){ - rc = FTS5_CORRUPT; - break; -@@ -217263,6 +245425,9 @@ static int fts5StructureDecode( - } - } - if( nSegment!=0 && rc==SQLITE_OK ) rc = FTS5_CORRUPT; -+ if( bStructureV2 ){ -+ pRet->nOriginCntr = nOriginCntr+1; -+ } - - if( rc!=SQLITE_OK ){ - fts5StructureRelease(pRet); -@@ -217275,16 +245440,16 @@ static int fts5StructureDecode( - } - - /* --** -+** Add a level to the Fts5Structure.aLevel[] array of structure object -+** (*ppStruct). - */ - static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ -+ fts5StructureMakeWritable(pRc, ppStruct); -+ assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); - if( *pRc==SQLITE_OK ){ - Fts5Structure *pStruct = *ppStruct; - int nLevel = pStruct->nLevel; -- sqlite3_int64 nByte = ( -- sizeof(Fts5Structure) + /* Main structure */ -- sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ -- ); -+ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2); - - pStruct = sqlite3_realloc64(pStruct, nByte); - if( pStruct ){ -@@ -217472,6 +245637,7 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ - Fts5Buffer buf; /* Buffer to serialize record into */ - int iLvl; /* Used to iterate through levels */ - int iCookie; /* Cookie value to store */ -+ int nHdr = (pStruct->nOriginCntr>0 ? (4+4+9+9+9) : (4+9+9)); - - assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); - memset(&buf, 0, sizeof(Fts5Buffer)); -@@ -217480,9 +245646,12 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ - iCookie = p->pConfig->iCookie; - if( iCookie<0 ) iCookie = 0; - -- if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, 4+9+9+9) ){ -+ if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, nHdr) ){ - sqlite3Fts5Put32(buf.p, iCookie); - buf.n = 4; -+ if( pStruct->nOriginCntr>0 ){ -+ fts5BufferSafeAppendBlob(&buf, FTS5_STRUCTURE_V2, 4); -+ } - fts5BufferSafeAppendVarint(&buf, pStruct->nLevel); - fts5BufferSafeAppendVarint(&buf, pStruct->nSegment); - fts5BufferSafeAppendVarint(&buf, (i64)pStruct->nWriteCounter); -@@ -217496,9 +245665,17 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ - assert( pLvl->nMerge<=pLvl->nSeg ); - - for(iSeg=0; iSegnSeg; iSeg++){ -- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid); -- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst); -- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast); -+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iSegid); -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoFirst); -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoLast); -+ if( pStruct->nOriginCntr>0 ){ -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin1); -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin2); -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nPgTombstone); -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntryTombstone); -+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntry); -+ } - } - } - -@@ -217641,9 +245818,9 @@ static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){ - } - - if( iOffnn ){ -- i64 iVal; -+ u64 iVal; - pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1; -- iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal); -+ iOff += fts5GetVarint(&pData->p[iOff], &iVal); - pLvl->iRowid += iVal; - pLvl->iOff = iOff; - }else{ -@@ -217736,42 +245913,25 @@ static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){ - pLvl->bEof = 1; - }else{ - u8 *a = pLvl->pData->p; -- i64 iVal; -- int iLimit; -- int ii; -- int nZero = 0; -- -- /* Currently iOff points to the first byte of a varint. This block -- ** decrements iOff until it points to the first byte of the previous -- ** varint. Taking care not to read any memory locations that occur -- ** before the buffer in memory. */ -- iLimit = (iOff>9 ? iOff-9 : 0); -- for(iOff--; iOff>iLimit; iOff--){ -- if( (a[iOff-1] & 0x80)==0 ) break; -- } -- -- fts5GetVarint(&a[iOff], (u64*)&iVal); -- pLvl->iRowid -= iVal; -- pLvl->iLeafPgno--; -- -- /* Skip backwards past any 0x00 varints. */ -- for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){ -- nZero++; -- } -- if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){ -- /* The byte immediately before the last 0x00 byte has the 0x80 bit -- ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80 -- ** bytes before a[ii]. */ -- int bZero = 0; /* True if last 0x00 counts */ -- if( (ii-8)>=pLvl->iFirstOff ){ -- int j; -- for(j=1; j<=8 && (a[ii-j] & 0x80); j++); -- bZero = (j>8); -+ -+ pLvl->iOff = 0; -+ fts5DlidxLvlNext(pLvl); -+ while( 1 ){ -+ int nZero = 0; -+ int ii = pLvl->iOff; -+ u64 delta = 0; -+ -+ while( a[ii]==0 ){ -+ nZero++; -+ ii++; - } -- if( bZero==0 ) nZero--; -+ ii += sqlite3Fts5GetVarint(&a[ii], &delta); -+ -+ if( ii>=iOff ) break; -+ pLvl->iLeafPgno += nZero+1; -+ pLvl->iRowid += delta; -+ pLvl->iOff = ii; - } -- pLvl->iLeafPgno -= nZero; -- pLvl->iOff = iOff - nZero; - } - - return pLvl->bEof; -@@ -217828,7 +245988,7 @@ static Fts5DlidxIter *fts5DlidxIterInit( - int bDone = 0; - - for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ -- sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); -+ sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1); - Fts5DlidxIter *pNew; - - pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); -@@ -217964,10 +246124,10 @@ static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ - - static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ - u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ -- int iOff = pIter->iLeafOffset; -+ i64 iOff = pIter->iLeafOffset; - - ASSERT_SZLEAF_OK(pIter->pLeaf); -- if( iOff>=pIter->pLeaf->szLeaf ){ -+ while( iOff>=pIter->pLeaf->szLeaf ){ - fts5SegIterNextPage(p, pIter); - if( pIter->pLeaf==0 ){ - if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; -@@ -217997,7 +246157,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ - */ - static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ - u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ -- int iOff = pIter->iLeafOffset; /* Offset to read at */ -+ i64 iOff = pIter->iLeafOffset; /* Offset to read at */ - int nNew; /* Bytes of new data */ - - iOff += fts5GetVarint32(&a[iOff], nNew); -@@ -218038,6 +246198,25 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ - } - } - -+/* -+** Allocate a tombstone hash page array object (pIter->pTombArray) for -+** the iterator passed as the second argument. If an OOM error occurs, -+** leave an error in the Fts5Index object. -+*/ -+static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ -+ const i64 nTomb = (i64)pIter->pSeg->nPgTombstone; -+ if( nTomb>0 ){ -+ i64 nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1); -+ Fts5TombstoneArray *pNew; -+ pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); -+ if( pNew ){ -+ pNew->nTombstone = nTomb; -+ pNew->nRef = 1; -+ pIter->pTombArray = pNew; -+ } -+ } -+} -+ - /* - ** Initialize the iterator object pIter to iterate through the entries in - ** segment pSeg. The iterator is left pointing to the first entry when -@@ -218066,16 +246245,20 @@ static void fts5SegIterInit( - fts5SegIterSetNext(p, pIter); - pIter->pSeg = pSeg; - pIter->iLeafPgno = pSeg->pgnoFirst-1; -- fts5SegIterNextPage(p, pIter); -+ do { -+ fts5SegIterNextPage(p, pIter); -+ }while( p->rc==SQLITE_OK && pIter->pLeaf && pIter->pLeaf->nn==4 ); - } - -- if( p->rc==SQLITE_OK ){ -+ if( p->rc==SQLITE_OK && pIter->pLeaf ){ - pIter->iLeafOffset = 4; -+ assert( pIter->pLeaf!=0 ); - assert_nc( pIter->pLeaf->nn>4 ); - assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); - pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; - fts5SegIterLoadTerm(p, pIter, 0); - fts5SegIterLoadNPos(p, pIter); -+ fts5SegIterAllocTombstone(p, pIter); - } - } - -@@ -218107,7 +246290,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ - - ASSERT_SZLEAF_OK(pIter->pLeaf); - while( 1 ){ -- i64 iDelta = 0; -+ u64 iDelta = 0; - - if( eDetail==FTS5_DETAIL_NONE ){ - /* todo */ -@@ -218122,7 +246305,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ - i += nPos; - } - if( i>=n ) break; -- i += fts5GetVarint(&a[i], (u64*)&iDelta); -+ i += fts5GetVarint(&a[i], &iDelta); - pIter->iRowid += iDelta; - - /* If necessary, grow the pIter->aRowidOffset[] array. */ -@@ -218173,8 +246356,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ - int iRowidOff; - iRowidOff = fts5LeafFirstRowidOff(pNew); - if( iRowidOff ){ -- pIter->pLeaf = pNew; -- pIter->iLeafOffset = iRowidOff; -+ if( iRowidOff>=pNew->szLeaf ){ -+ p->rc = FTS5_CORRUPT; -+ }else{ -+ pIter->pLeaf = pNew; -+ pIter->iLeafOffset = iRowidOff; -+ } - } - } - -@@ -218221,7 +246408,7 @@ static void fts5SegIterNext_Reverse( - if( pIter->iRowidOffset>0 ){ - u8 *a = pIter->pLeaf->p; - int iOff; -- i64 iDelta; -+ u64 iDelta; - - pIter->iRowidOffset--; - pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset]; -@@ -218230,7 +246417,7 @@ static void fts5SegIterNext_Reverse( - if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){ - iOff += pIter->nPos; - } -- fts5GetVarint(&a[iOff], (u64*)&iDelta); -+ fts5GetVarint(&a[iOff], &iDelta); - pIter->iRowid -= iDelta; - }else{ - fts5SegIterReverseNewPage(p, pIter); -@@ -218258,7 +246445,7 @@ static void fts5SegIterNext_None( - iOff = pIter->iLeafOffset; - - /* Next entry is on the next page */ -- if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ -+ while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ - fts5SegIterNextPage(p, pIter); - if( p->rc || pIter->pLeaf==0 ) return; - pIter->iRowid = 0; -@@ -218267,7 +246454,7 @@ static void fts5SegIterNext_None( - - if( iOffiEndofDoclist ){ - /* Next entry is on the current page */ -- i64 iDelta; -+ u64 iDelta; - iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta); - pIter->iLeafOffset = iOff; - pIter->iRowid += iDelta; -@@ -218282,15 +246469,16 @@ static void fts5SegIterNext_None( - }else{ - const u8 *pList = 0; - const char *zTerm = 0; -+ int nTerm = 0; - int nList; - sqlite3Fts5HashScanNext(p->pHash); -- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); -+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); - if( pList==0 ) goto next_none_eof; - pIter->pLeaf->p = (u8*)pList; - pIter->pLeaf->nn = nList; - pIter->pLeaf->szLeaf = nList; - pIter->iEndofDoclist = nList; -- sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm); -+ sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm); - pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); - } - -@@ -218356,11 +246544,12 @@ static void fts5SegIterNext( - }else if( pIter->pSeg==0 ){ - const u8 *pList = 0; - const char *zTerm = 0; -+ int nTerm = 0; - int nList = 0; - assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); - if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ - sqlite3Fts5HashScanNext(p->pHash); -- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); -+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); - } - if( pList==0 ){ - fts5DataRelease(pIter->pLeaf); -@@ -218370,8 +246559,7 @@ static void fts5SegIterNext( - pIter->pLeaf->nn = nList; - pIter->pLeaf->szLeaf = nList; - pIter->iEndofDoclist = nList+1; -- sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), -- (u8*)zTerm); -+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm); - pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); - *pbNewTerm = 1; - } -@@ -218423,14 +246611,9 @@ static void fts5SegIterNext( - }else{ - /* The following could be done by calling fts5SegIterLoadNPos(). But - ** this block is particularly performance critical, so equivalent -- ** code is inlined. -- ** -- ** Later: Switched back to fts5SegIterLoadNPos() because it supports -- ** detail=none mode. Not ideal. -- */ -+ ** code is inlined. */ - int nSz; -- assert( p->rc==SQLITE_OK ); -- assert( pIter->iLeafOffset<=pIter->pLeaf->nn ); -+ assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn ); - fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); - pIter->bDel = (nSz & 0x0001); - pIter->nPos = nSz>>1; -@@ -218456,10 +246639,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ - Fts5Data *pLast = 0; - int pgnoLast = 0; - -- if( pDlidx ){ -+ if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){ - int iSegid = pIter->pSeg->iSegid; - pgnoLast = fts5DlidxIterPgno(pDlidx); -- pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); -+ pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); - }else{ - Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ - -@@ -218486,7 +246669,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ - ** forward to find the page containing the last rowid. */ - for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){ - i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); -- Fts5Data *pNew = fts5DataRead(p, iAbs); -+ Fts5Data *pNew = fts5LeafRead(p, iAbs); - if( pNew ){ - int iRowid, bTermless; - iRowid = fts5LeafFirstRowidOff(pNew); -@@ -218517,6 +246700,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ - pIter->pLeaf = pLast; - pIter->iLeafPgno = pgnoLast; - iOff = fts5LeafFirstRowidOff(pLast); -+ if( iOff>pLast->szLeaf ){ -+ p->rc = FTS5_CORRUPT; -+ return; -+ } - iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); - pIter->iLeafOffset = iOff; - -@@ -218525,7 +246712,6 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ - }else{ - pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast); - } -- - } - - fts5SegIterReverseInitPage(p, pIter); -@@ -218577,21 +246763,20 @@ static void fts5LeafSeek( - Fts5SegIter *pIter, /* Iterator to seek */ - const u8 *pTerm, int nTerm /* Term to search for */ - ){ -- int iOff; -+ u32 iOff; - const u8 *a = pIter->pLeaf->p; -- int szLeaf = pIter->pLeaf->szLeaf; -- int n = pIter->pLeaf->nn; -+ u32 n = (u32)pIter->pLeaf->nn; - - u32 nMatch = 0; - u32 nKeep = 0; - u32 nNew = 0; - u32 iTermOff; -- int iPgidx; /* Current offset in pgidx */ -+ u32 iPgidx; /* Current offset in pgidx */ - int bEndOfPage = 0; - - assert( p->rc==SQLITE_OK ); - -- iPgidx = szLeaf; -+ iPgidx = (u32)pIter->pLeaf->szLeaf; - iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff); - iOff = iTermOff; - if( iOff>n ){ -@@ -218657,15 +246842,15 @@ static void fts5LeafSeek( - if( pIter->pLeaf==0 ) return; - a = pIter->pLeaf->p; - if( fts5LeafIsTermless(pIter->pLeaf)==0 ){ -- iPgidx = pIter->pLeaf->szLeaf; -+ iPgidx = (u32)pIter->pLeaf->szLeaf; - iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff); -- if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){ -+ if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){ - p->rc = FTS5_CORRUPT; - return; - }else{ - nKeep = 0; - iTermOff = iOff; -- n = pIter->pLeaf->nn; -+ n = (u32)pIter->pLeaf->nn; - iOff += fts5GetVarint32(&a[iOff], nNew); - break; - } -@@ -218760,7 +246945,7 @@ static void fts5SegIterSeekInit( - fts5LeafSeek(p, bGe, pIter, pTerm, nTerm); - } - -- if( p->rc==SQLITE_OK && bGe==0 ){ -+ if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){ - pIter->flags |= FTS5_SEGITER_ONETERM; - if( pIter->pLeaf ){ - if( flags & FTS5INDEX_QUERY_DESC ){ -@@ -218776,6 +246961,9 @@ static void fts5SegIterSeekInit( - } - - fts5SegIterSetNext(p, pIter); -+ if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){ -+ fts5SegIterAllocTombstone(p, pIter); -+ } - - /* Either: - ** -@@ -218792,6 +246980,79 @@ static void fts5SegIterSeekInit( - ); - } - -+ -+/* -+** SQL used by fts5SegIterNextInit() to find the page to open. -+*/ -+static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){ -+ if( p->pIdxNextSelect==0 ){ -+ Fts5Config *pConfig = p->pConfig; -+ fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf( -+ "SELECT pgno FROM '%q'.'%q_idx' WHERE " -+ "segid=? AND term>? ORDER BY term ASC LIMIT 1", -+ pConfig->zDb, pConfig->zName -+ )); -+ -+ } -+ return p->pIdxNextSelect; -+} -+ -+/* -+** This is similar to fts5SegIterSeekInit(), except that it initializes -+** the segment iterator to point to the first term following the page -+** with pToken/nToken on it. -+*/ -+static void fts5SegIterNextInit( -+ Fts5Index *p, -+ const char *pTerm, int nTerm, -+ Fts5StructureSegment *pSeg, /* Description of segment */ -+ Fts5SegIter *pIter /* Object to populate */ -+){ -+ int iPg = -1; /* Page of segment to open */ -+ int bDlidx = 0; -+ sqlite3_stmt *pSel = 0; /* SELECT to find iPg */ -+ -+ pSel = fts5IdxNextStmt(p); -+ if( pSel ){ -+ assert( p->rc==SQLITE_OK ); -+ sqlite3_bind_int(pSel, 1, pSeg->iSegid); -+ sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC); -+ -+ if( sqlite3_step(pSel)==SQLITE_ROW ){ -+ i64 val = sqlite3_column_int64(pSel, 0); -+ iPg = (int)(val>>1); -+ bDlidx = (val & 0x0001); -+ } -+ p->rc = sqlite3_reset(pSel); -+ sqlite3_bind_null(pSel, 2); -+ if( p->rc ) return; -+ } -+ -+ memset(pIter, 0, sizeof(*pIter)); -+ pIter->pSeg = pSeg; -+ pIter->flags |= FTS5_SEGITER_ONETERM; -+ if( iPg>=0 ){ -+ pIter->iLeafPgno = iPg - 1; -+ fts5SegIterNextPage(p, pIter); -+ fts5SegIterSetNext(p, pIter); -+ } -+ if( pIter->pLeaf ){ -+ const u8 *a = pIter->pLeaf->p; -+ int iTermOff = 0; -+ -+ pIter->iPgidxOff = pIter->pLeaf->szLeaf; -+ pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff); -+ pIter->iLeafOffset = iTermOff; -+ fts5SegIterLoadTerm(p, pIter, 0); -+ fts5SegIterLoadNPos(p, pIter); -+ if( bDlidx ) fts5SegIterLoadDlidx(p, pIter); -+ -+ assert( p->rc!=SQLITE_OK || -+ fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0 -+ ); -+ } -+} -+ - /* - ** Initialize the object pIter to point to term pTerm/nTerm within the - ** in-memory hash table. If there is no such term in the hash-table, the -@@ -218818,14 +247079,21 @@ static void fts5SegIterHashInit( - const u8 *pList = 0; - - p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); -- sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList); -- n = (z ? (int)strlen((const char*)z) : 0); -+ sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList); - if( pList ){ - pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); - if( pLeaf ){ - pLeaf->p = (u8*)pList; - } - } -+ -+ /* The call to sqlite3Fts5HashScanInit() causes the hash table to -+ ** fill the size field of all existing position lists. This means they -+ ** can no longer be appended to. Since the only scenario in which they -+ ** can be appended to is if the previous operation on this table was -+ ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this -+ ** possibility altogether. */ -+ p->bDelete = 0; - }else{ - p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data), - (const char*)pTerm, nTerm, (void**)&pLeaf, &nList -@@ -218856,6 +247124,37 @@ static void fts5SegIterHashInit( - fts5SegIterSetNext(p, pIter); - } - -+/* -+** Array ap[] contains n elements. Release each of these elements using -+** fts5DataRelease(). Then free the array itself using sqlite3_free(). -+*/ -+static void fts5IndexFreeArray(Fts5Data **ap, int n){ -+ if( ap ){ -+ int ii; -+ for(ii=0; iinRef--; -+ if( p->nRef<=0 ){ -+ int ii; -+ for(ii=0; iinTombstone; ii++){ -+ fts5DataRelease(p->apTombstone[ii]); -+ } -+ sqlite3_free(p); -+ } -+ } -+} -+ - /* - ** Zero the iterator passed as the only argument. - */ -@@ -218863,6 +247162,7 @@ static void fts5SegIterClear(Fts5SegIter *pIter){ - fts5BufferFree(&pIter->term); - fts5DataRelease(pIter->pLeaf); - fts5DataRelease(pIter->pNextLeaf); -+ fts5TombstoneArrayDelete(pIter->pTombArray); - fts5DlidxIterFree(pIter->pDlidx); - sqlite3_free(pIter->aRowidOffset); - memset(pIter, 0, sizeof(Fts5SegIter)); -@@ -218996,7 +247296,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ - assert_nc( i2!=0 ); - pRes->bTermEq = 1; - if( p1->iRowid==p2->iRowid ){ -- p1->bDel = p2->bDel; - return i2; - } - res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1; -@@ -219015,7 +247314,8 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ - - /* - ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. --** It is an error if leaf iLeafPgno does not exist or contains no rowids. -+** It is an error if leaf iLeafPgno does not exist. Unless the db is -+** a 'secure-delete' db, if it contains no rowids then this is also an error. - */ - static void fts5SegIterGotoPage( - Fts5Index *p, /* FTS5 backend object */ -@@ -219030,21 +247330,23 @@ static void fts5SegIterGotoPage( - fts5DataRelease(pIter->pNextLeaf); - pIter->pNextLeaf = 0; - pIter->iLeafPgno = iLeafPgno-1; -- fts5SegIterNextPage(p, pIter); -- assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); - -- if( p->rc==SQLITE_OK ){ -+ while( p->rc==SQLITE_OK ){ - int iOff; -- u8 *a = pIter->pLeaf->p; -- int n = pIter->pLeaf->szLeaf; -- -+ fts5SegIterNextPage(p, pIter); -+ if( pIter->pLeaf==0 ) break; - iOff = fts5LeafFirstRowidOff(pIter->pLeaf); -- if( iOff<4 || iOff>=n ){ -- p->rc = FTS5_CORRUPT; -- }else{ -- iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); -- pIter->iLeafOffset = iOff; -- fts5SegIterLoadNPos(p, pIter); -+ if( iOff>0 ){ -+ u8 *a = pIter->pLeaf->p; -+ int n = pIter->pLeaf->szLeaf; -+ if( iOff<4 || iOff>=n ){ -+ p->rc = FTS5_CORRUPT; -+ }else{ -+ iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); -+ pIter->iLeafOffset = iOff; -+ fts5SegIterLoadNPos(p, pIter); -+ } -+ break; - } - } - } -@@ -219105,7 +247407,6 @@ static void fts5SegIterNextFrom( - }while( p->rc==SQLITE_OK ); - } - -- - /* - ** Free the iterator object passed as the second argument. - */ -@@ -219197,6 +247498,85 @@ static void fts5MultiIterSetEof(Fts5Iter *pIter){ - pIter->iSwitchRowid = pSeg->iRowid; - } - -+/* -+** The argument to this macro must be an Fts5Data structure containing a -+** tombstone hash page. This macro returns the key-size of the hash-page. -+*/ -+#define TOMBSTONE_KEYSIZE(pPg) (pPg->p[0]==4 ? 4 : 8) -+ -+#define TOMBSTONE_NSLOT(pPg) \ -+ ((pPg->nn > 16) ? ((pPg->nn-8) / TOMBSTONE_KEYSIZE(pPg)) : 1) -+ -+/* -+** Query a single tombstone hash table for rowid iRowid. Return true if -+** it is found or false otherwise. The tombstone hash table is one of -+** nHashTable tables. -+*/ -+static int fts5IndexTombstoneQuery( -+ Fts5Data *pHash, /* Hash table page to query */ -+ int nHashTable, /* Number of pages attached to segment */ -+ u64 iRowid /* Rowid to query hash for */ -+){ -+ const int szKey = TOMBSTONE_KEYSIZE(pHash); -+ const int nSlot = TOMBSTONE_NSLOT(pHash); -+ int iSlot = (iRowid / nHashTable) % nSlot; -+ int nCollide = nSlot; -+ -+ if( iRowid==0 ){ -+ return pHash->p[1]; -+ }else if( szKey==4 ){ -+ u32 *aSlot = (u32*)&pHash->p[8]; -+ while( aSlot[iSlot] ){ -+ if( fts5GetU32((u8*)&aSlot[iSlot])==iRowid ) return 1; -+ if( nCollide--==0 ) break; -+ iSlot = (iSlot+1)%nSlot; -+ } -+ }else{ -+ u64 *aSlot = (u64*)&pHash->p[8]; -+ while( aSlot[iSlot] ){ -+ if( fts5GetU64((u8*)&aSlot[iSlot])==iRowid ) return 1; -+ if( nCollide--==0 ) break; -+ iSlot = (iSlot+1)%nSlot; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+** Return true if the iterator passed as the only argument points -+** to an segment entry for which there is a tombstone. Return false -+** if there is no tombstone or if the iterator is already at EOF. -+*/ -+static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ -+ int iFirst = pIter->aFirst[1].iFirst; -+ Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; -+ Fts5TombstoneArray *pArray = pSeg->pTombArray; -+ -+ if( pSeg->pLeaf && pArray ){ -+ /* Figure out which page the rowid might be present on. */ -+ int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone; -+ assert( iPg>=0 ); -+ -+ /* If tombstone hash page iPg has not yet been loaded from the -+ ** database, load it now. */ -+ if( pArray->apTombstone[iPg]==0 ){ -+ pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex, -+ FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg) -+ ); -+ if( pArray->apTombstone[iPg]==0 ) return 0; -+ } -+ -+ return fts5IndexTombstoneQuery( -+ pArray->apTombstone[iPg], -+ pArray->nTombstone, -+ pSeg->iRowid -+ ); -+ } -+ -+ return 0; -+} -+ - /* - ** Move the iterator to the next entry. - ** -@@ -219234,7 +247614,9 @@ static void fts5MultiIterNext( - - fts5AssertMultiIterSetup(p, pIter); - assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf ); -- if( pIter->bSkipEmpty==0 || pSeg->nPos ){ -+ if( (pIter->bSkipEmpty==0 || pSeg->nPos) -+ && 0==fts5MultiIterIsDeleted(pIter) -+ ){ - pIter->xSetOutputs(pIter, pSeg); - return; - } -@@ -219266,7 +247648,9 @@ static void fts5MultiIterNext2( - } - fts5AssertMultiIterSetup(p, pIter); - -- }while( fts5MultiIterIsEmpty(p, pIter) ); -+ }while( (fts5MultiIterIsEmpty(p, pIter) || fts5MultiIterIsDeleted(pIter)) -+ && (p->rc==SQLITE_OK) -+ ); - } - } - -@@ -219279,12 +247663,11 @@ static Fts5Iter *fts5MultiIterAlloc( - int nSeg - ){ - Fts5Iter *pNew; -- int nSlot; /* Power of two >= nSeg */ -+ i64 nSlot; /* Power of two >= nSeg */ - - for(nSlot=2; nSlotaSeg[] */ -+ SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */ - sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ - ); - if( pNew ){ -@@ -219422,7 +247805,7 @@ static void fts5ChunkIterate( - int pgno = pSeg->iLeafPgno; - int pgnoSave = 0; - -- /* This function does notmwork with detail=none databases. */ -+ /* This function does not work with detail=none databases. */ - assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE ); - - if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){ -@@ -219435,6 +247818,9 @@ static void fts5ChunkIterate( - fts5DataRelease(pData); - if( nRem<=0 ){ - break; -+ }else if( pSeg->pSeg==0 ){ -+ p->rc = FTS5_CORRUPT; -+ return; - }else{ - pgno++; - pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno)); -@@ -219462,7 +247848,11 @@ static void fts5SegiterPoslist( - Fts5Colset *pColset, - Fts5Buffer *pBuf - ){ -+ assert( pBuf!=0 ); -+ assert( pSeg!=0 ); - if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){ -+ assert( pBuf->p!=0 ); -+ assert( pBuf->nSpace >= pBuf->n+pSeg->nPos+FTS5_DATA_ZERO_PADDING ); - memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING); - if( pColset==0 ){ - fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); -@@ -219486,66 +247876,72 @@ static void fts5SegiterPoslist( - } - - /* --** IN/OUT parameter (*pa) points to a position list n bytes in size. If --** the position list contains entries for column iCol, then (*pa) is set --** to point to the sub-position-list for that column and the number of --** bytes in it returned. Or, if the argument position list does not --** contain any entries for column iCol, return 0. -+** Parameter pPos points to a buffer containing a position list, size nPos. -+** This function filters it according to pColset (which must be non-NULL) -+** and sets pIter->base.pData/nData to point to the new position list. -+** If memory is required for the new position list, use buffer pIter->poslist. -+** Or, if the new position list is a contiguous subset of the input, set -+** pIter->base.pData/nData to point directly to it. -+** -+** This function is a no-op if *pRc is other than SQLITE_OK when it is -+** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM -+** before returning. - */ --static int fts5IndexExtractCol( -- const u8 **pa, /* IN/OUT: Pointer to poslist */ -- int n, /* IN: Size of poslist in bytes */ -- int iCol /* Column to extract from poslist */ --){ -- int iCurrent = 0; /* Anything before the first 0x01 is col 0 */ -- const u8 *p = *pa; -- const u8 *pEnd = &p[n]; /* One byte past end of position list */ -- -- while( iCol>iCurrent ){ -- /* Advance pointer p until it points to pEnd or an 0x01 byte that is -- ** not part of a varint. Note that it is not possible for a negative -- ** or extremely large varint to occur within an uncorrupted position -- ** list. So the last byte of each varint may be assumed to have a clear -- ** 0x80 bit. */ -- while( *p!=0x01 ){ -- while( *p++ & 0x80 ); -- if( p>=pEnd ) return 0; -- } -- *pa = p++; -- iCurrent = *p++; -- if( iCurrent & 0x80 ){ -- p--; -- p += fts5GetVarint32(p, iCurrent); -- } -- } -- if( iCol!=iCurrent ) return 0; -- -- /* Advance pointer p until it points to pEnd or an 0x01 byte that is -- ** not part of a varint */ -- while( pnCol; i++){ -- const u8 *pSub = pPos; -- int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]); -- if( nSub ){ -- fts5BufferAppendBlob(pRc, pBuf, nSub, pSub); -+ const u8 *p = pPos; -+ const u8 *aCopy = p; -+ const u8 *pEnd = &p[nPos]; /* One byte past end of position list */ -+ int i = 0; -+ int iCurrent = 0; -+ -+ if( pColset->nCol>1 && sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){ -+ return; -+ } -+ -+ while( 1 ){ -+ while( pColset->aiCol[i]nCol ){ -+ pIter->base.pData = pIter->poslist.p; -+ pIter->base.nData = pIter->poslist.n; -+ return; -+ } -+ } -+ -+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is -+ ** not part of a varint */ -+ while( paiCol[i]==iCurrent ){ -+ if( pColset->nCol==1 ){ -+ pIter->base.pData = aCopy; -+ pIter->base.nData = p-aCopy; -+ return; -+ } -+ fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy); -+ } -+ if( p>=pEnd ){ -+ pIter->base.pData = pIter->poslist.p; -+ pIter->base.nData = pIter->poslist.n; -+ return; -+ } -+ aCopy = p++; -+ iCurrent = *p++; -+ if( iCurrent & 0x80 ){ -+ p--; -+ p += fts5GetVarint32(p, iCurrent); - } - } - } -+ - } - - /* -@@ -219665,16 +248061,9 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ - /* All data is stored on the current page. Populate the output - ** variables to point into the body of the page object. */ - const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset]; -- if( pColset->nCol==1 ){ -- pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]); -- pIter->base.pData = a; -- }else{ -- int *pRc = &pIter->pIndex->rc; -- fts5BufferZero(&pIter->poslist); -- fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist); -- pIter->base.pData = pIter->poslist.p; -- pIter->base.nData = pIter->poslist.n; -- } -+ int *pRc = &pIter->pIndex->rc; -+ fts5BufferZero(&pIter->poslist); -+ fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter); - }else{ - /* The data is distributed over two or more pages. Copy it into the - ** Fts5Iter.poslist buffer and then set the output pointer to point -@@ -219687,6 +248076,7 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){ - } - - static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ -+ assert( pIter!=0 || (*pRc)!=SQLITE_OK ); - if( *pRc==SQLITE_OK ){ - Fts5Config *pConfig = pIter->pIndex->pConfig; - if( pConfig->eDetail==FTS5_DETAIL_NONE ){ -@@ -219717,6 +248107,32 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ - } - } - -+/* -+** All the component segment-iterators of pIter have been set up. This -+** functions finishes setup for iterator pIter itself. -+*/ -+static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){ -+ int iIter; -+ for(iIter=pIter->nSeg-1; iIter>0; iIter--){ -+ int iEq; -+ if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){ -+ Fts5SegIter *pSeg = &pIter->aSeg[iEq]; -+ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); -+ fts5MultiIterAdvanced(p, pIter, iEq, iIter); -+ } -+ } -+ fts5MultiIterSetEof(pIter); -+ fts5AssertMultiIterSetup(p, pIter); -+ -+ if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter)) -+ || fts5MultiIterIsDeleted(pIter) -+ ){ -+ fts5MultiIterNext(p, pIter, 0, 0); -+ }else if( pIter->base.bEof==0 ){ -+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; -+ pIter->xSetOutputs(pIter, pSeg); -+ } -+} - - /* - ** Allocate a new Fts5Iter object. -@@ -219752,13 +248168,16 @@ static void fts5MultiIterNew( - if( iLevel<0 ){ - assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); - nSeg = pStruct->nSegment; -- nSeg += (p->pHash ? 1 : 0); -+ nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH)); - }else{ - nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); - } - } - *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); -- if( pNew==0 ) return; -+ if( pNew==0 ){ -+ assert( p->rc!=SQLITE_OK ); -+ goto fts5MultiIterNew_post_check; -+ } - pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC)); - pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); - pNew->pColset = pColset; -@@ -219770,7 +248189,7 @@ static void fts5MultiIterNew( - if( p->rc==SQLITE_OK ){ - if( iLevel<0 ){ - Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; -- if( p->pHash ){ -+ if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){ - /* Add a segment iterator for the current contents of the hash table. */ - Fts5SegIter *pIter = &pNew->aSeg[iIter++]; - fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); -@@ -219795,33 +248214,20 @@ static void fts5MultiIterNew( - assert( iIter==nSeg ); - } - -- /* If the above was successful, each component iterators now points -+ /* If the above was successful, each component iterator now points - ** to the first entry in its segment. In this case initialize the - ** aFirst[] array. Or, if an error has occurred, free the iterator - ** object and set the output variable to NULL. */ - if( p->rc==SQLITE_OK ){ -- for(iIter=pNew->nSeg-1; iIter>0; iIter--){ -- int iEq; -- if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ -- Fts5SegIter *pSeg = &pNew->aSeg[iEq]; -- if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); -- fts5MultiIterAdvanced(p, pNew, iEq, iIter); -- } -- } -- fts5MultiIterSetEof(pNew); -- fts5AssertMultiIterSetup(p, pNew); -- -- if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){ -- fts5MultiIterNext(p, pNew, 0, 0); -- }else if( pNew->base.bEof==0 ){ -- Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst]; -- pNew->xSetOutputs(pNew, pSeg); -- } -- -+ fts5MultiIterFinishSetup(p, pNew); - }else{ - fts5MultiIterFree(pNew); - *ppOut = 0; - } -+ -+fts5MultiIterNew_post_check: -+ assert( (*ppOut)!=0 || p->rc!=SQLITE_OK ); -+ return; - } - - /* -@@ -219838,7 +248244,6 @@ static void fts5MultiIterNew2( - pNew = fts5MultiIterAlloc(p, 2); - if( pNew ){ - Fts5SegIter *pIter = &pNew->aSeg[1]; -- - pIter->flags = FTS5_SEGITER_ONETERM; - if( pData->szLeaf>0 ){ - pIter->pLeaf = pData; -@@ -219869,7 +248274,8 @@ static void fts5MultiIterNew2( - ** False otherwise. - */ - static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){ -- assert( p->rc -+ assert( pIter!=0 || p->rc!=SQLITE_OK ); -+ assert( p->rc!=SQLITE_OK - || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof - ); - return (p->rc || pIter->base.bEof); -@@ -219984,7 +248390,10 @@ static void fts5IndexDiscardData(Fts5Index *p){ - if( p->pHash ){ - sqlite3Fts5HashClear(p->pHash); - p->nPendingData = 0; -+ p->nPendingRow = 0; -+ p->flushRc = SQLITE_OK; - } -+ p->nContentlessDelete = 0; - } - - /* -@@ -220198,7 +248607,7 @@ static void fts5WriteDlidxAppend( - } - - if( pDlidx->bPrevValid ){ -- iVal = iRowid - pDlidx->iPrev; -+ iVal = (u64)iRowid - (u64)pDlidx->iPrev; - }else{ - i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno); - assert( pDlidx->buf.n==0 ); -@@ -220365,7 +248774,9 @@ static void fts5WriteAppendRowid( - fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid); - }else{ - assert_nc( p->rc || iRowid>pWriter->iPrevRowid ); -- fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); -+ fts5BufferAppendVarint(&p->rc, &pPage->buf, -+ (u64)iRowid - (u64)pWriter->iPrevRowid -+ ); - } - pWriter->iPrevRowid = iRowid; - pWriter->bFirstRowidInDoclist = 0; -@@ -220383,7 +248794,7 @@ static void fts5WriteAppendPoslistData( - const u8 *a = aData; - int n = nData; - -- assert( p->pConfig->pgsz>0 ); -+ assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK ); - while( p->rc==SQLITE_OK - && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz - ){ -@@ -220518,7 +248929,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ - fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); - fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); - fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); -- fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff,&pData->p[iOff]); -+ fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff]); - if( p->rc==SQLITE_OK ){ - /* Set the szLeaf field */ - fts5PutU16(&buf.p[2], (u16)buf.n); -@@ -220619,6 +249030,12 @@ static void fts5IndexMergeLevel( - - /* Read input from all segments in the input level */ - nInput = pLvl->nSeg; -+ -+ /* Set the range of origins that will go into the output segment. */ -+ if( pStruct->nOriginCntr>0 ){ -+ pSeg->iOrigin1 = pLvl->aSeg[0].iOrigin1; -+ pSeg->iOrigin2 = pLvl->aSeg[pLvl->nSeg-1].iOrigin2; -+ } - } - bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2); - -@@ -220673,12 +249090,16 @@ static void fts5IndexMergeLevel( - ** and last leaf page number at the same time. */ - fts5WriteFinish(p, &writer, &pSeg->pgnoLast); - -+ assert( pIter!=0 || p->rc!=SQLITE_OK ); - if( fts5MultiIterEof(p, pIter) ){ - int i; - - /* Remove the redundant segments from the %_data table */ -+ assert( pSeg->nEntry==0 ); - for(i=0; iaSeg[i].iSegid); -+ Fts5StructureSegment *pOld = &pLvl->aSeg[i]; -+ pSeg->nEntry += (pOld->nEntry - pOld->nEntryTombstone); -+ fts5DataRemoveSegment(p, pOld); - } - - /* Remove the redundant segments from the input level */ -@@ -220704,6 +249125,48 @@ static void fts5IndexMergeLevel( - if( pnRem ) *pnRem -= writer.nLeafWritten; - } - -+/* -+** If this is not a contentless_delete=1 table, or if the 'deletemerge' -+** configuration option is set to 0, then this function always returns -1. -+** Otherwise, it searches the structure object passed as the second argument -+** for a level suitable for merging due to having a large number of -+** tombstones in the tombstone hash. If one is found, its index is returned. -+** Otherwise, if there is no suitable level, -1. -+*/ -+static int fts5IndexFindDeleteMerge(Fts5Index *p, Fts5Structure *pStruct){ -+ Fts5Config *pConfig = p->pConfig; -+ int iRet = -1; -+ if( pConfig->bContentlessDelete && pConfig->nDeleteMerge>0 ){ -+ int ii; -+ int nBest = 0; -+ -+ for(ii=0; iinLevel; ii++){ -+ Fts5StructureLevel *pLvl = &pStruct->aLevel[ii]; -+ i64 nEntry = 0; -+ i64 nTomb = 0; -+ int iSeg; -+ for(iSeg=0; iSegnSeg; iSeg++){ -+ nEntry += pLvl->aSeg[iSeg].nEntry; -+ nTomb += pLvl->aSeg[iSeg].nEntryTombstone; -+ } -+ assert_nc( nEntry>0 || pLvl->nSeg==0 ); -+ if( nEntry>0 ){ -+ int nPercent = (nTomb * 100) / nEntry; -+ if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){ -+ iRet = ii; -+ nBest = nPercent; -+ } -+ } -+ -+ /* If pLvl is already the input level to an ongoing merge, look no -+ ** further for a merge candidate. The caller should be allowed to -+ ** continue merging from pLvl first. */ -+ if( pLvl->nMerge ) break; -+ } -+ } -+ return iRet; -+} -+ - /* - ** Do up to nPg pages of automerge work on the index. - ** -@@ -220723,14 +249186,15 @@ static int fts5IndexMerge( - int iBestLvl = 0; /* Level offering the most input segments */ - int nBest = 0; /* Number of input segments on best level */ - -- /* Set iBestLvl to the level to read input segments from. */ -+ /* Set iBestLvl to the level to read input segments from. Or to -1 if -+ ** there is no level suitable to merge segments from. */ - assert( pStruct->nLevel>0 ); - for(iLvl=0; iLvlnLevel; iLvl++){ - Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; - if( pLvl->nMerge ){ - if( pLvl->nMerge>nBest ){ - iBestLvl = iLvl; -- nBest = pLvl->nMerge; -+ nBest = nMin; - } - break; - } -@@ -220739,22 +249203,18 @@ static int fts5IndexMerge( - iBestLvl = iLvl; - } - } -- -- /* If nBest is still 0, then the index must be empty. */ --#ifdef SQLITE_DEBUG -- for(iLvl=0; nBest==0 && iLvlnLevel; iLvl++){ -- assert( pStruct->aLevel[iLvl].nSeg==0 ); -+ if( nBestaLevel[iBestLvl].nMerge==0 ){ -- break; -- } -+ if( iBestLvl<0 ) break; - bRet = 1; - fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem); - if( p->rc==SQLITE_OK && pStruct->aLevel[iBestLvl].nMerge==0 ){ - fts5StructurePromote(p, iBestLvl+1, pStruct); - } -+ -+ if( nMin==1 ) nMin = 2; - } - *ppStruct = pStruct; - return bRet; -@@ -220773,7 +249233,7 @@ static void fts5IndexAutomerge( - Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ - int nLeaf /* Number of output leaves just written */ - ){ -- if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 ){ -+ if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 && ALWAYS((*ppStruct)!=0) ){ - Fts5Structure *pStruct = *ppStruct; - u64 nWrite; /* Initial value of write-counter */ - int nWork; /* Number of work-quanta to perform */ -@@ -220795,16 +249255,16 @@ static void fts5IndexCrisismerge( - ){ - const int nCrisis = p->pConfig->nCrisisMerge; - Fts5Structure *pStruct = *ppStruct; -- int iLvl = 0; -- -- assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 ); -- while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ -- fts5IndexMergeLevel(p, &pStruct, iLvl, 0); -- assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); -- fts5StructurePromote(p, iLvl+1, pStruct); -- iLvl++; -+ if( pStruct && pStruct->nLevel>0 ){ -+ int iLvl = 0; -+ while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ -+ fts5IndexMergeLevel(p, &pStruct, iLvl, 0); -+ assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); -+ fts5StructurePromote(p, iLvl+1, pStruct); -+ iLvl++; -+ } -+ *ppStruct = pStruct; - } -- *ppStruct = pStruct; - } - - static int fts5IndexReturn(Fts5Index *p){ -@@ -220813,6 +249273,14 @@ static int fts5IndexReturn(Fts5Index *p){ - return rc; - } - -+/* -+** Close the read-only blob handle, if it is open. -+*/ -+static void sqlite3Fts5IndexCloseReader(Fts5Index *p){ -+ fts5IndexCloseReader(p); -+ fts5IndexReturn(p); -+} -+ - typedef struct Fts5FlushCtx Fts5FlushCtx; - struct Fts5FlushCtx { - Fts5Index *pIdx; -@@ -220838,6 +249306,491 @@ static int fts5PoslistPrefix(const u8 *aBuf, int nMax){ - return ret; - } - -+/* -+** Execute the SQL statement: -+** -+** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno); -+** -+** This is used when a secure-delete operation removes the last term -+** from a segment leaf page. In that case the %_idx entry is removed -+** too. This is done to ensure that if all instances of a token are -+** removed from an fts5 database in secure-delete mode, no trace of -+** the token itself remains in the database. -+*/ -+static void fts5SecureDeleteIdxEntry( -+ Fts5Index *p, /* FTS5 backend object */ -+ int iSegid, /* Id of segment to delete entry for */ -+ int iPgno /* Page number within segment */ -+){ -+ if( iPgno!=1 ){ -+ assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE ); -+ if( p->pDeleteFromIdx==0 ){ -+ fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintf( -+ "DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)", -+ p->pConfig->zDb, p->pConfig->zName -+ )); -+ } -+ if( p->rc==SQLITE_OK ){ -+ sqlite3_bind_int(p->pDeleteFromIdx, 1, iSegid); -+ sqlite3_bind_int(p->pDeleteFromIdx, 2, iPgno); -+ sqlite3_step(p->pDeleteFromIdx); -+ p->rc = sqlite3_reset(p->pDeleteFromIdx); -+ } -+ } -+} -+ -+/* -+** This is called when a secure-delete operation removes a position-list -+** that overflows onto segment page iPgno of segment pSeg. This function -+** rewrites node iPgno, and possibly one or more of its right-hand peers, -+** to remove this portion of the position list. -+** -+** Output variable (*pbLastInDoclist) is set to true if the position-list -+** removed is followed by a new term or the end-of-segment, or false if -+** it is followed by another rowid/position list. -+*/ -+static void fts5SecureDeleteOverflow( -+ Fts5Index *p, -+ Fts5StructureSegment *pSeg, -+ int iPgno, -+ int *pbLastInDoclist -+){ -+ const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); -+ int pgno; -+ Fts5Data *pLeaf = 0; -+ assert( iPgno!=1 ); -+ -+ *pbLastInDoclist = 1; -+ for(pgno=iPgno; p->rc==SQLITE_OK && pgno<=pSeg->pgnoLast; pgno++){ -+ i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); -+ int iNext = 0; -+ u8 *aPg = 0; -+ -+ pLeaf = fts5DataRead(p, iRowid); -+ if( pLeaf==0 ) break; -+ aPg = pLeaf->p; -+ -+ iNext = fts5GetU16(&aPg[0]); -+ if( iNext!=0 ){ -+ *pbLastInDoclist = 0; -+ } -+ if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ -+ fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext); -+ } -+ -+ if( iNext==0 ){ -+ /* The page contains no terms or rowids. Replace it with an empty -+ ** page and move on to the right-hand peer. */ -+ const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04}; -+ assert_nc( bDetailNone==0 || pLeaf->nn==4 ); -+ if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty)); -+ fts5DataRelease(pLeaf); -+ pLeaf = 0; -+ }else if( bDetailNone ){ -+ break; -+ }else if( iNext>=pLeaf->szLeaf || pLeaf->nnszLeaf || iNext<4 ){ -+ p->rc = FTS5_CORRUPT; -+ break; -+ }else{ -+ int nShift = iNext - 4; -+ int nPg; -+ -+ int nIdx = 0; -+ u8 *aIdx = 0; -+ -+ /* Unless the current page footer is 0 bytes in size (in which case -+ ** the new page footer will be as well), allocate and populate a -+ ** buffer containing the new page footer. Set stack variables aIdx -+ ** and nIdx accordingly. */ -+ if( pLeaf->nn>pLeaf->szLeaf ){ -+ int iFirst = 0; -+ int i1 = pLeaf->szLeaf; -+ int i2 = 0; -+ -+ i1 += fts5GetVarint32(&aPg[i1], iFirst); -+ if( iFirstrc = FTS5_CORRUPT; -+ break; -+ } -+ aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2); -+ if( aIdx==0 ) break; -+ i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift); -+ if( i1nn ){ -+ memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1); -+ i2 += (pLeaf->nn-i1); -+ } -+ nIdx = i2; -+ } -+ -+ /* Modify the contents of buffer aPg[]. Set nPg to the new size -+ ** in bytes. The new page is always smaller than the old. */ -+ nPg = pLeaf->szLeaf - nShift; -+ memmove(&aPg[4], &aPg[4+nShift], nPg-4); -+ fts5PutU16(&aPg[2], nPg); -+ if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4); -+ if( nIdx>0 ){ -+ memcpy(&aPg[nPg], aIdx, nIdx); -+ nPg += nIdx; -+ } -+ sqlite3_free(aIdx); -+ -+ /* Write the new page to disk and exit the loop */ -+ assert( nPg>4 || fts5GetU16(aPg)==0 ); -+ fts5DataWrite(p, iRowid, aPg, nPg); -+ break; -+ } -+ } -+ fts5DataRelease(pLeaf); -+} -+ -+/* -+** Completely remove the entry that pSeg currently points to from -+** the database. -+*/ -+static void fts5DoSecureDelete( -+ Fts5Index *p, -+ Fts5SegIter *pSeg -+){ -+ const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); -+ int iSegid = pSeg->pSeg->iSegid; -+ u8 *aPg = pSeg->pLeaf->p; -+ int nPg = pSeg->pLeaf->nn; -+ int iPgIdx = pSeg->pLeaf->szLeaf; -+ -+ u64 iDelta = 0; -+ int iNextOff = 0; -+ int iOff = 0; -+ int nIdx = 0; -+ u8 *aIdx = 0; -+ int bLastInDoclist = 0; -+ int iIdx = 0; -+ int iStart = 0; -+ int iDelKeyOff = 0; /* Offset of deleted key, if any */ -+ -+ nIdx = nPg-iPgIdx; -+ aIdx = sqlite3Fts5MallocZero(&p->rc, ((i64)nIdx)+16); -+ if( p->rc ) return; -+ memcpy(aIdx, &aPg[iPgIdx], nIdx); -+ -+ /* At this point segment iterator pSeg points to the entry -+ ** this function should remove from the b-tree segment. -+ ** -+ ** In detail=full or detail=column mode, pSeg->iLeafOffset is the -+ ** offset of the first byte in the position-list for the entry to -+ ** remove. Immediately before this comes two varints that will also -+ ** need to be removed: -+ ** -+ ** + the rowid or delta rowid value for the entry, and -+ ** + the size of the position list in bytes. -+ ** -+ ** Or, in detail=none mode, there is a single varint prior to -+ ** pSeg->iLeafOffset - the rowid or delta rowid value. -+ ** -+ ** This block sets the following variables: -+ ** -+ ** iStart: -+ ** The offset of the first byte of the rowid or delta-rowid -+ ** value for the doclist entry being removed. -+ ** -+ ** iDelta: -+ ** The value of the rowid or delta-rowid value for the doclist -+ ** entry being removed. -+ ** -+ ** iNextOff: -+ ** The offset of the next entry following the position list -+ ** for the one being removed. If the position list for this -+ ** entry overflows onto the next leaf page, this value will be -+ ** greater than pLeaf->szLeaf. -+ */ -+ { -+ int iSOP; /* Start-Of-Position-list */ -+ if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){ -+ iStart = pSeg->iTermLeafOffset; -+ }else{ -+ iStart = fts5GetU16(&aPg[0]); -+ } -+ -+ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); -+ assert_nc( iSOP<=pSeg->iLeafOffset ); -+ -+ if( bDetailNone ){ -+ while( iSOPiLeafOffset ){ -+ if( aPg[iSOP]==0x00 ) iSOP++; -+ if( aPg[iSOP]==0x00 ) iSOP++; -+ iStart = iSOP; -+ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); -+ } -+ -+ iNextOff = iSOP; -+ if( iNextOffiEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; -+ if( iNextOffiEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; -+ -+ }else{ -+ int nPos = 0; -+ iSOP += fts5GetVarint32(&aPg[iSOP], nPos); -+ while( iSOPiLeafOffset ){ -+ iStart = iSOP + (nPos/2); -+ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); -+ iSOP += fts5GetVarint32(&aPg[iSOP], nPos); -+ } -+ assert_nc( iSOP==pSeg->iLeafOffset ); -+ iNextOff = pSeg->iLeafOffset + pSeg->nPos; -+ } -+ } -+ -+ iOff = iStart; -+ -+ /* If the position-list for the entry being removed flows over past -+ ** the end of this page, delete the portion of the position-list on the -+ ** next page and beyond. -+ ** -+ ** Set variable bLastInDoclist to true if this entry happens -+ ** to be the last rowid in the doclist for its term. */ -+ if( iNextOff>=iPgIdx ){ -+ int pgno = pSeg->iLeafPgno+1; -+ fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); -+ iNextOff = iPgIdx; -+ } -+ -+ if( pSeg->bDel==0 ){ -+ if( iNextOff!=iPgIdx ){ -+ /* Loop through the page-footer. If iNextOff (offset of the -+ ** entry following the one we are removing) is equal to the -+ ** offset of a key on this page, then the entry is the last -+ ** in its doclist. */ -+ int iKeyOff = 0; -+ for(iIdx=0; iIdxbDel ){ -+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta); -+ aPg[iOff++] = 0x01; -+ }else if( bLastInDoclist==0 ){ -+ if( iNextOff!=iPgIdx ){ -+ u64 iNextDelta = 0; -+ iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta); -+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta); -+ } -+ }else if( -+ pSeg->iLeafPgno==pSeg->iTermLeafPgno -+ && iStart==pSeg->iTermLeafOffset -+ ){ -+ /* The entry being removed was the only position list in its -+ ** doclist. Therefore the term needs to be removed as well. */ -+ int iKey = 0; -+ int iKeyOff = 0; -+ -+ /* Set iKeyOff to the offset of the term that will be removed - the -+ ** last offset in the footer that is not greater than iStart. */ -+ for(iIdx=0; iIdx(u32)iStart ) break; -+ iKeyOff += iVal; -+ } -+ assert_nc( iKey>=1 ); -+ -+ /* Set iDelKeyOff to the value of the footer entry to remove from -+ ** the page. */ -+ iDelKeyOff = iOff = iKeyOff; -+ -+ if( iNextOff!=iPgIdx ){ -+ /* This is the only position-list associated with the term, and there -+ ** is another term following it on this page. So the subsequent term -+ ** needs to be moved to replace the term associated with the entry -+ ** being removed. */ -+ int nPrefix = 0; -+ int nSuffix = 0; -+ int nPrefix2 = 0; -+ int nSuffix2 = 0; -+ -+ iDelKeyOff = iNextOff; -+ iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); -+ iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); -+ -+ if( iKey!=1 ){ -+ iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); -+ } -+ iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); -+ -+ nPrefix = MIN(nPrefix, nPrefix2); -+ nSuffix = (nPrefix2 + nSuffix2) - nPrefix; -+ -+ if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ -+ p->rc = FTS5_CORRUPT; -+ }else{ -+ if( iKey!=1 ){ -+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); -+ } -+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); -+ if( nPrefix2>pSeg->term.n ){ -+ p->rc = FTS5_CORRUPT; -+ }else if( nPrefix2>nPrefix ){ -+ memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); -+ iOff += (nPrefix2-nPrefix); -+ } -+ memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2); -+ iOff += nSuffix2; -+ iNextOff += nSuffix2; -+ } -+ } -+ }else if( iStart==4 ){ -+ int iPgno; -+ -+ assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno ); -+ /* The entry being removed may be the only position list in -+ ** its doclist. */ -+ for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ -+ Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); -+ int bEmpty = (pPg && pPg->nn==4); -+ fts5DataRelease(pPg); -+ if( bEmpty==0 ) break; -+ } -+ -+ if( iPgno==pSeg->iTermLeafPgno ){ -+ i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); -+ Fts5Data *pTerm = fts5DataRead(p, iId); -+ if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ -+ u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; -+ int nTermIdx = pTerm->nn - pTerm->szLeaf; -+ int iTermIdx = 0; -+ int iTermOff = 0; -+ -+ while( 1 ){ -+ u32 iVal = 0; -+ int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); -+ iTermOff += iVal; -+ if( (iTermIdx+nByte)>=nTermIdx ) break; -+ iTermIdx += nByte; -+ } -+ nTermIdx = iTermIdx; -+ -+ memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); -+ fts5PutU16(&pTerm->p[2], iTermOff); -+ -+ fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); -+ if( nTermIdx==0 ){ -+ fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); -+ } -+ } -+ fts5DataRelease(pTerm); -+ } -+ } -+ -+ /* Assuming no error has occurred, this block does final edits to the -+ ** leaf page before writing it back to disk. Input variables are: -+ ** -+ ** nPg: Total initial size of leaf page. -+ ** iPgIdx: Initial offset of page footer. -+ ** -+ ** iOff: Offset to move data to -+ ** iNextOff: Offset to move data from -+ */ -+ if( p->rc==SQLITE_OK ){ -+ const int nMove = nPg - iNextOff; /* Number of bytes to move */ -+ int nShift = iNextOff - iOff; /* Distance to move them */ -+ -+ int iPrevKeyOut = 0; -+ int iKeyIn = 0; -+ -+ memmove(&aPg[iOff], &aPg[iNextOff], nMove); -+ iPgIdx -= nShift; -+ nPg = iPgIdx; -+ fts5PutU16(&aPg[2], iPgIdx); -+ -+ for(iIdx=0; iIdxiOff ? nShift : 0)); -+ nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOut - iPrevKeyOut); -+ iPrevKeyOut = iKeyOut; -+ } -+ } -+ -+ if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ -+ fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); -+ } -+ -+ assert_nc( nPg>4 || fts5GetU16(aPg)==0 ); -+ fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg, nPg); -+ } -+ sqlite3_free(aIdx); -+} -+ -+/* -+** This is called as part of flushing a delete to disk in 'secure-delete' -+** mode. It edits the segments within the database described by argument -+** pStruct to remove the entries for term zTerm, rowid iRowid. -+** -+** Return SQLITE_OK if successful, or an SQLite error code if an error -+** has occurred. Any error code is also stored in the Fts5Index handle. -+*/ -+static int fts5FlushSecureDelete( -+ Fts5Index *p, -+ Fts5Structure *pStruct, -+ const char *zTerm, -+ int nTerm, -+ i64 iRowid -+){ -+ const int f = FTS5INDEX_QUERY_SKIPHASH; -+ Fts5Iter *pIter = 0; /* Used to find term instance */ -+ -+ /* If the version number has not been set to SECUREDELETE, do so now. */ -+ if( p->pConfig->iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ){ -+ Fts5Config *pConfig = p->pConfig; -+ sqlite3_stmt *pStmt = 0; -+ fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf( -+ "REPLACE INTO %Q.'%q_config' VALUES ('version', %d)", -+ pConfig->zDb, pConfig->zName, FTS5_CURRENT_VERSION_SECUREDELETE -+ )); -+ if( p->rc==SQLITE_OK ){ -+ int rc; -+ sqlite3_step(pStmt); -+ rc = sqlite3_finalize(pStmt); -+ if( p->rc==SQLITE_OK ) p->rc = rc; -+ pConfig->iCookie++; -+ pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE; -+ } -+ } -+ -+ fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); -+ if( fts5MultiIterEof(p, pIter)==0 ){ -+ i64 iThis = fts5MultiIterRowid(pIter); -+ if( iThisrc==SQLITE_OK -+ && fts5MultiIterEof(p, pIter)==0 -+ && iRowid==fts5MultiIterRowid(pIter) -+ ){ -+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; -+ fts5DoSecureDelete(p, pSeg); -+ } -+ } -+ -+ fts5MultiIterFree(pIter); -+ return p->rc; -+} -+ -+ - /* - ** Flush the contents of in-memory hash table iHash to a new level-0 - ** segment on disk. Also update the corresponding structure record. -@@ -220854,143 +249807,199 @@ static void fts5FlushOneHash(Fts5Index *p){ - /* Obtain a reference to the index structure and allocate a new segment-id - ** for the new level-0 segment. */ - pStruct = fts5StructureRead(p); -- iSegid = fts5AllocateSegid(p, pStruct); - fts5StructureInvalidate(p); - -- if( iSegid ){ -- const int pgsz = p->pConfig->pgsz; -- int eDetail = p->pConfig->eDetail; -- Fts5StructureSegment *pSeg; /* New segment within pStruct */ -- Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ -- Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ -+ if( sqlite3Fts5HashIsEmpty(pHash)==0 ){ -+ iSegid = fts5AllocateSegid(p, pStruct); -+ if( iSegid ){ -+ const int pgsz = p->pConfig->pgsz; -+ int eDetail = p->pConfig->eDetail; -+ int bSecureDelete = p->pConfig->bSecureDelete; -+ Fts5StructureSegment *pSeg; /* New segment within pStruct */ -+ Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ -+ Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ -+ -+ Fts5SegWriter writer; -+ fts5WriteInit(p, &writer, iSegid); -+ -+ pBuf = &writer.writer.buf; -+ pPgidx = &writer.writer.pgidx; -+ -+ /* fts5WriteInit() should have initialized the buffers to (most likely) -+ ** the maximum space required. */ -+ assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) ); -+ assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) ); -+ -+ /* Begin scanning through hash table entries. This loop runs once for each -+ ** term/doclist currently stored within the hash table. */ -+ if( p->rc==SQLITE_OK ){ -+ p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); -+ } -+ while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ -+ const char *zTerm; /* Buffer containing term */ -+ int nTerm; /* Size of zTerm in bytes */ -+ const u8 *pDoclist; /* Pointer to doclist for this term */ -+ int nDoclist; /* Size of doclist in bytes */ - -- Fts5SegWriter writer; -- fts5WriteInit(p, &writer, iSegid); -+ /* Get the term and doclist for this entry. */ -+ sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist); -+ if( bSecureDelete==0 ){ -+ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); -+ if( p->rc!=SQLITE_OK ) break; -+ assert( writer.bFirstRowidInPage==0 ); -+ } - -- pBuf = &writer.writer.buf; -- pPgidx = &writer.writer.pgidx; -+ if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ -+ /* The entire doclist will fit on the current leaf. */ -+ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); -+ }else{ -+ int bTermWritten = !bSecureDelete; -+ i64 iRowid = 0; -+ i64 iPrev = 0; -+ int iOff = 0; -+ -+ /* The entire doclist will not fit on this leaf. The following -+ ** loop iterates through the poslists that make up the current -+ ** doclist. */ -+ while( p->rc==SQLITE_OK && iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ -+ iOff++; -+ continue; -+ } -+ } -+ } - -- /* fts5WriteInit() should have initialized the buffers to (most likely) -- ** the maximum space required. */ -- assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) ); -- assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) ); -+ if( p->rc==SQLITE_OK && bTermWritten==0 ){ -+ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); -+ bTermWritten = 1; -+ assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 ); -+ } - -- /* Begin scanning through hash table entries. This loop runs once for each -- ** term/doclist currently stored within the hash table. */ -- if( p->rc==SQLITE_OK ){ -- p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); -- } -- while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ -- const char *zTerm; /* Buffer containing term */ -- const u8 *pDoclist; /* Pointer to doclist for this term */ -- int nDoclist; /* Size of doclist in bytes */ -- -- /* Write the term for this entry to disk. */ -- sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); -- fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm); -- if( p->rc!=SQLITE_OK ) break; -- -- assert( writer.bFirstRowidInPage==0 ); -- if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ -- /* The entire doclist will fit on the current leaf. */ -- fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); -- }else{ -- i64 iRowid = 0; -- i64 iDelta = 0; -- int iOff = 0; -- -- /* The entire doclist will not fit on this leaf. The following -- ** loop iterates through the poslists that make up the current -- ** doclist. */ -- while( p->rc==SQLITE_OK && iOffp[0], (u16)pBuf->n); /* first rowid on page */ -- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); -- writer.bFirstRowidInPage = 0; -- fts5WriteDlidxAppend(p, &writer, iRowid); -+ if( writer.bFirstRowidInPage ){ -+ fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ -+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); -+ writer.bFirstRowidInPage = 0; -+ fts5WriteDlidxAppend(p, &writer, iRowid); -+ }else{ -+ u64 iRowidDelta = (u64)iRowid - (u64)iPrev; -+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowidDelta); -+ } - if( p->rc!=SQLITE_OK ) break; -- }else{ -- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); -- } -- assert( pBuf->n<=pBuf->nSpace ); -+ assert( pBuf->n<=pBuf->nSpace ); -+ iPrev = iRowid; - -- if( eDetail==FTS5_DETAIL_NONE ){ -- if( iOffp[pBuf->n++] = 0; -- iOff++; -+ if( eDetail==FTS5_DETAIL_NONE ){ - if( iOffp[pBuf->n++] = 0; - iOff++; -+ if( iOffp[pBuf->n++] = 0; -+ iOff++; -+ } -+ } -+ if( (pBuf->n + pPgidx->n)>=pgsz ){ -+ fts5WriteFlushLeaf(p, &writer); - } -- } -- if( (pBuf->n + pPgidx->n)>=pgsz ){ -- fts5WriteFlushLeaf(p, &writer); -- } -- }else{ -- int bDummy; -- int nPos; -- int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); -- nCopy += nPos; -- if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ -- /* The entire poslist will fit on the current leaf. So copy -- ** it in one go. */ -- fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); - }else{ -- /* The entire poslist will not fit on this leaf. So it needs -- ** to be broken into sections. The only qualification being -- ** that each varint must be stored contiguously. */ -- const u8 *pPoslist = &pDoclist[iOff]; -- int iPos = 0; -- while( p->rc==SQLITE_OK ){ -- int nSpace = pgsz - pBuf->n - pPgidx->n; -- int n = 0; -- if( (nCopy - iPos)<=nSpace ){ -- n = nCopy - iPos; -- }else{ -- n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); -- } -- assert( n>0 ); -- fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); -- iPos += n; -- if( (pBuf->n + pPgidx->n)>=pgsz ){ -- fts5WriteFlushLeaf(p, &writer); -+ int bDel = 0; -+ int nPos = 0; -+ int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel); -+ if( bDel && bSecureDelete ){ -+ fts5BufferAppendVarint(&p->rc, pBuf, nPos*2); -+ iOff += nCopy; -+ nCopy = nPos; -+ }else{ -+ nCopy += nPos; -+ } -+ if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ -+ /* The entire poslist will fit on the current leaf. So copy -+ ** it in one go. */ -+ fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); -+ }else{ -+ /* The entire poslist will not fit on this leaf. So it needs -+ ** to be broken into sections. The only qualification being -+ ** that each varint must be stored contiguously. */ -+ const u8 *pPoslist = &pDoclist[iOff]; -+ int iPos = 0; -+ while( p->rc==SQLITE_OK ){ -+ int nSpace = pgsz - pBuf->n - pPgidx->n; -+ int n = 0; -+ if( (nCopy - iPos)<=nSpace ){ -+ n = nCopy - iPos; -+ }else{ -+ n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); -+ } -+ assert( n>0 ); -+ fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); -+ iPos += n; -+ if( (pBuf->n + pPgidx->n)>=pgsz ){ -+ fts5WriteFlushLeaf(p, &writer); -+ } -+ if( iPos>=nCopy ) break; - } -- if( iPos>=nCopy ) break; - } -+ iOff += nCopy; - } -- iOff += nCopy; - } - } -- } - -- /* TODO2: Doclist terminator written here. */ -- /* pBuf->p[pBuf->n++] = '\0'; */ -- assert( pBuf->n<=pBuf->nSpace ); -- if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); -- } -- sqlite3Fts5HashClear(pHash); -- fts5WriteFinish(p, &writer, &pgnoLast); -+ /* TODO2: Doclist terminator written here. */ -+ /* pBuf->p[pBuf->n++] = '\0'; */ -+ assert( pBuf->n<=pBuf->nSpace ); -+ if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); -+ } -+ fts5WriteFinish(p, &writer, &pgnoLast); - -- /* Update the Fts5Structure. It is written back to the database by the -- ** fts5StructureRelease() call below. */ -- if( pStruct->nLevel==0 ){ -- fts5StructureAddLevel(&p->rc, &pStruct); -- } -- fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); -- if( p->rc==SQLITE_OK ){ -- pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; -- pSeg->iSegid = iSegid; -- pSeg->pgnoFirst = 1; -- pSeg->pgnoLast = pgnoLast; -- pStruct->nSegment++; -+ assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); -+ if( pgnoLast>0 ){ -+ /* Update the Fts5Structure. It is written back to the database by the -+ ** fts5StructureRelease() call below. */ -+ if( pStruct->nLevel==0 ){ -+ fts5StructureAddLevel(&p->rc, &pStruct); -+ } -+ fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); -+ if( p->rc==SQLITE_OK ){ -+ pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; -+ pSeg->iSegid = iSegid; -+ pSeg->pgnoFirst = 1; -+ pSeg->pgnoLast = pgnoLast; -+ if( pStruct->nOriginCntr>0 ){ -+ pSeg->iOrigin1 = pStruct->nOriginCntr; -+ pSeg->iOrigin2 = pStruct->nOriginCntr; -+ pSeg->nEntry = p->nPendingRow; -+ pStruct->nOriginCntr++; -+ } -+ pStruct->nSegment++; -+ } -+ fts5StructurePromote(p, 0, pStruct); -+ } - } -- fts5StructurePromote(p, 0, pStruct); - } - -- fts5IndexAutomerge(p, &pStruct, pgnoLast); -+ fts5IndexAutomerge(p, &pStruct, pgnoLast + p->nContentlessDelete); - fts5IndexCrisismerge(p, &pStruct); - fts5StructureWrite(p, pStruct); - fts5StructureRelease(pStruct); -@@ -221001,10 +250010,21 @@ static void fts5FlushOneHash(Fts5Index *p){ - */ - static void fts5IndexFlush(Fts5Index *p){ - /* Unless it is empty, flush the hash table to disk */ -- if( p->nPendingData ){ -+ if( p->flushRc ){ -+ p->rc = p->flushRc; -+ return; -+ } -+ if( p->nPendingData || p->nContentlessDelete ){ - assert( p->pHash ); -- p->nPendingData = 0; - fts5FlushOneHash(p); -+ if( p->rc==SQLITE_OK ){ -+ sqlite3Fts5HashClear(p->pHash); -+ p->nPendingData = 0; -+ p->nPendingRow = 0; -+ p->nContentlessDelete = 0; -+ }else if( p->nPendingData || p->nContentlessDelete ){ -+ p->flushRc = p->rc; -+ } - } - } - -@@ -221013,40 +250033,47 @@ static Fts5Structure *fts5IndexOptimizeStruct( - Fts5Structure *pStruct - ){ - Fts5Structure *pNew = 0; -- sqlite3_int64 nByte = sizeof(Fts5Structure); -+ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1); - int nSeg = pStruct->nSegment; - int i; - - /* Figure out if this structure requires optimization. A structure does - ** not require optimization if either: - ** -- ** + it consists of fewer than two segments, or -- ** + all segments are on the same level, or -- ** + all segments except one are currently inputs to a merge operation. -+ ** 1. it consists of fewer than two segments, or -+ ** 2. all segments are on the same level, or -+ ** 3. all segments except one are currently inputs to a merge operation. - ** -- ** In the first case, return NULL. In the second, increment the ref-count -- ** on *pStruct and return a copy of the pointer to it. -+ ** In the first case, if there are no tombstone hash pages, return NULL. In -+ ** the second, increment the ref-count on *pStruct and return a copy of the -+ ** pointer to it. - */ -- if( nSeg<2 ) return 0; -+ if( nSeg==0 ) return 0; - for(i=0; inLevel; i++){ - int nThis = pStruct->aLevel[i].nSeg; -- if( nThis==nSeg || (nThis==nSeg-1 && pStruct->aLevel[i].nMerge==nThis) ){ -+ int nMerge = pStruct->aLevel[i].nMerge; -+ if( nThis>0 && (nThis==nSeg || (nThis==nSeg-1 && nMerge==nThis)) ){ -+ if( nSeg==1 && nThis==1 && pStruct->aLevel[i].aSeg[0].nPgTombstone==0 ){ -+ return 0; -+ } - fts5StructureRef(pStruct); - return pStruct; - } - assert( pStruct->aLevel[i].nMerge<=nThis ); - } - -- nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel); -+ nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); -+ assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); - pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); - - if( pNew ){ - Fts5StructureLevel *pLvl; - nByte = nSeg * sizeof(Fts5StructureSegment); -- pNew->nLevel = pStruct->nLevel+1; -+ pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL); - pNew->nRef = 1; - pNew->nWriteCounter = pStruct->nWriteCounter; -- pLvl = &pNew->aLevel[pStruct->nLevel]; -+ pNew->nOriginCntr = pStruct->nOriginCntr; -+ pLvl = &pNew->aLevel[pNew->nLevel-1]; - pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte); - if( pLvl->aSeg ){ - int iLvl, iSeg; -@@ -221076,7 +250103,9 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){ - - assert( p->rc==SQLITE_OK ); - fts5IndexFlush(p); -+ assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 ); - pStruct = fts5StructureRead(p); -+ assert( p->rc!=SQLITE_OK || pStruct!=0 ); - fts5StructureInvalidate(p); - - if( pStruct ){ -@@ -221105,7 +250134,10 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){ - ** INSERT command. - */ - static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ -- Fts5Structure *pStruct = fts5StructureRead(p); -+ Fts5Structure *pStruct = 0; -+ -+ fts5IndexFlush(p); -+ pStruct = fts5StructureRead(p); - if( pStruct ){ - int nMin = p->pConfig->nUsermerge; - fts5StructureInvalidate(p); -@@ -221113,7 +250145,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ - Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct); - fts5StructureRelease(pStruct); - pStruct = pNew; -- nMin = 2; -+ nMin = 1; - nMerge = nMerge*-1; - } - if( pStruct && pStruct->nLevel ){ -@@ -221128,7 +250160,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ - - static void fts5AppendRowid( - Fts5Index *p, -- i64 iDelta, -+ u64 iDelta, - Fts5Iter *pUnused, - Fts5Buffer *pBuf - ){ -@@ -221138,7 +250170,7 @@ static void fts5AppendRowid( - - static void fts5AppendPoslist( - Fts5Index *p, -- i64 iDelta, -+ u64 iDelta, - Fts5Iter *pMulti, - Fts5Buffer *pBuf - ){ -@@ -221157,7 +250189,7 @@ static void fts5AppendPoslist( - static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ - u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; - -- assert( pIter->aPoslist ); -+ assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) ); - if( p>=pIter->aEof ){ - pIter->aPoslist = 0; - }else{ -@@ -221177,6 +250209,9 @@ static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ - } - - pIter->aPoslist = p; -+ if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){ -+ pIter->aPoslist = 0; -+ } - } - } - -@@ -221185,9 +250220,11 @@ static void fts5DoclistIterInit( - Fts5DoclistIter *pIter - ){ - memset(pIter, 0, sizeof(*pIter)); -- pIter->aPoslist = pBuf->p; -- pIter->aEof = &pBuf->p[pBuf->n]; -- fts5DoclistIterNext(pIter); -+ if( pBuf->n>0 ){ -+ pIter->aPoslist = pBuf->p; -+ pIter->aEof = &pBuf->p[pBuf->n]; -+ fts5DoclistIterNext(pIter); -+ } - } - - #if 0 -@@ -221208,10 +250245,10 @@ static void fts5MergeAppendDocid( - } - #endif - --#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ -- assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ -- fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \ -- (iLastRowid) = (iRowid); \ -+#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ -+ assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ -+ fts5BufferSafeAppendVarint((pBuf), (u64)(iRowid) - (u64)(iLastRowid)); \ -+ (iLastRowid) = (iRowid); \ - } - - /* -@@ -221241,16 +250278,20 @@ static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){ - static void fts5MergeRowidLists( - Fts5Index *p, /* FTS5 backend object */ - Fts5Buffer *p1, /* First list to merge */ -- Fts5Buffer *p2 /* Second list to merge */ -+ int nBuf, /* Number of entries in apBuf[] */ -+ Fts5Buffer *aBuf /* Array of other lists to merge into p1 */ - ){ - int i1 = 0; - int i2 = 0; - i64 iRowid1 = 0; - i64 iRowid2 = 0; - i64 iOut = 0; -- -+ Fts5Buffer *p2 = &aBuf[0]; - Fts5Buffer out; -+ -+ (void)nBuf; - memset(&out, 0, sizeof(out)); -+ assert( nBuf==1 ); - sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n); - if( p->rc ) return; - -@@ -221277,252 +250318,665 @@ static void fts5MergeRowidLists( - fts5BufferFree(&out); - } - -+typedef struct PrefixMerger PrefixMerger; -+struct PrefixMerger { -+ Fts5DoclistIter iter; /* Doclist iterator */ -+ i64 iPos; /* For iterating through a position list */ -+ int iOff; -+ u8 *aPos; -+ PrefixMerger *pNext; /* Next in docid/poslist order */ -+}; -+ -+static void fts5PrefixMergerInsertByRowid( -+ PrefixMerger **ppHead, -+ PrefixMerger *p -+){ -+ if( p->iter.aPoslist ){ -+ PrefixMerger **pp = ppHead; -+ while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){ -+ pp = &(*pp)->pNext; -+ } -+ p->pNext = *pp; -+ *pp = p; -+ } -+} -+ -+static void fts5PrefixMergerInsertByPosition( -+ PrefixMerger **ppHead, -+ PrefixMerger *p -+){ -+ if( p->iPos>=0 ){ -+ PrefixMerger **pp = ppHead; -+ while( *pp && p->iPos>(*pp)->iPos ){ -+ pp = &(*pp)->pNext; -+ } -+ p->pNext = *pp; -+ *pp = p; -+ } -+} -+ -+ - /* --** Buffers p1 and p2 contain doclists. This function merges the content --** of the two doclists together and sets buffer p1 to the result before --** returning. --** --** If an error occurs, an error code is left in p->rc. If an error has --** already occurred, this function is a no-op. -+** Array aBuf[] contains nBuf doclists. These are all merged in with the -+** doclist in buffer p1. - */ - static void fts5MergePrefixLists( - Fts5Index *p, /* FTS5 backend object */ - Fts5Buffer *p1, /* First list to merge */ -- Fts5Buffer *p2 /* Second list to merge */ --){ -- if( p2->n ){ -- i64 iLastRowid = 0; -- Fts5DoclistIter i1; -- Fts5DoclistIter i2; -- Fts5Buffer out = {0, 0, 0}; -- Fts5Buffer tmp = {0, 0, 0}; -- -- /* The maximum size of the output is equal to the sum of the two -- ** input sizes + 1 varint (9 bytes). The extra varint is because if the -- ** first rowid in one input is a large negative number, and the first in -- ** the other a non-negative number, the delta for the non-negative -- ** number will be larger on disk than the literal integer value -- ** was. -- ** -- ** Or, if the input position-lists are corrupt, then the output might -- ** include up to 2 extra 10-byte positions created by interpreting -1 -- ** (the value PoslistNext64() uses for EOF) as a position and appending -- ** it to the output. This can happen at most once for each input -- ** position-list, hence two 10 byte paddings. */ -- if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9+10+10) ) return; -- fts5DoclistIterInit(p1, &i1); -- fts5DoclistIterInit(p2, &i2); -+ int nBuf, /* Number of buffers in array aBuf[] */ -+ Fts5Buffer *aBuf /* Other lists to merge in */ -+){ -+#define fts5PrefixMergerNextPosition(p) \ -+ sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos) -+#define FTS5_MERGE_NLIST 16 -+ PrefixMerger aMerger[FTS5_MERGE_NLIST]; -+ PrefixMerger *pHead = 0; -+ int i; -+ int nOut = 0; -+ Fts5Buffer out = {0, 0, 0}; -+ Fts5Buffer tmp = {0, 0, 0}; -+ i64 iLastRowid = 0; -+ -+ /* Initialize a doclist-iterator for each input buffer. Arrange them in -+ ** a linked-list starting at pHead in ascending order of rowid. Avoid -+ ** linking any iterators already at EOF into the linked list at all. */ -+ assert( nBuf+1<=(int)(sizeof(aMerger)/sizeof(aMerger[0])) ); -+ memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1)); -+ pHead = &aMerger[nBuf]; -+ fts5DoclistIterInit(p1, &pHead->iter); -+ for(i=0; in + 9 + 10*nBuf; -+ -+ /* The maximum size of the output is equal to the sum of the -+ ** input sizes + 1 varint (9 bytes). The extra varint is because if the -+ ** first rowid in one input is a large negative number, and the first in -+ ** the other a non-negative number, the delta for the non-negative -+ ** number will be larger on disk than the literal integer value -+ ** was. -+ ** -+ ** Or, if the input position-lists are corrupt, then the output might -+ ** include up to (nBuf+1) extra 10-byte positions created by interpreting -1 -+ ** (the value PoslistNext64() uses for EOF) as a position and appending -+ ** it to the output. This can happen at most once for each input -+ ** position-list, hence (nBuf+1) 10 byte paddings. */ -+ if( sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return; -+ -+ while( pHead ){ -+ fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid); -+ -+ if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){ -+ /* Merge data from two or more poslists */ -+ i64 iPrev = 0; -+ int nTmp = FTS5_DATA_ZERO_PADDING; -+ int nMerge = 0; -+ PrefixMerger *pSave = pHead; -+ PrefixMerger *pThis = 0; -+ int nTail = 0; -+ -+ pHead = 0; -+ while( pSave && pSave->iter.iRowid==iLastRowid ){ -+ PrefixMerger *pNext = pSave->pNext; -+ pSave->iOff = 0; -+ pSave->iPos = 0; -+ pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize]; -+ fts5PrefixMergerNextPosition(pSave); -+ nTmp += pSave->iter.nPoslist + 10; -+ nMerge++; -+ fts5PrefixMergerInsertByPosition(&pHead, pSave); -+ pSave = pNext; -+ } -+ -+ if( pHead==0 || pHead->pNext==0 ){ -+ p->rc = FTS5_CORRUPT; -+ break; -+ } - -- while( 1 ){ -- if( i1.iRowidp) + (i2.aPoslist-p2->p)+9+10+10) ); -- } -- else if( i2.iRowid!=i1.iRowid ){ -- /* Copy entry from i2 */ -- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); -- fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize); -- fts5DoclistIterNext(&i2); -- if( i2.aPoslist==0 ) break; -- assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) ); -+ /* See the earlier comment in this function for an explanation of why -+ ** corrupt input position lists might cause the output to consume -+ ** at most nMerge*10 bytes of unexpected space. */ -+ if( sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){ -+ break; - } -- else{ -- /* Merge the two position lists. */ -- i64 iPos1 = 0; -- i64 iPos2 = 0; -- int iOff1 = 0; -- int iOff2 = 0; -- u8 *a1 = &i1.aPoslist[i1.nSize]; -- u8 *a2 = &i2.aPoslist[i2.nSize]; -- int nCopy; -- u8 *aCopy; -- -- i64 iPrev = 0; -- Fts5PoslistWriter writer; -- memset(&writer, 0, sizeof(writer)); -- -- /* See the earlier comment in this function for an explanation of why -- ** corrupt input position lists might cause the output to consume -- ** at most 20 bytes of unexpected space. */ -- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); -- fts5BufferZero(&tmp); -- sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist + 10 + 10); -- if( p->rc ) break; -+ fts5BufferZero(&tmp); - -- sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1); -- sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); -- assert_nc( iPos1>=0 && iPos2>=0 ); -+ pThis = pHead; -+ pHead = pThis->pNext; -+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); -+ fts5PrefixMergerNextPosition(pThis); -+ fts5PrefixMergerInsertByPosition(&pHead, pThis); - -- if( iPos1=0 && iPos2>=0 ){ -- while( 1 ){ -- if( iPos1pNext ){ -+ pThis = pHead; -+ if( pThis->iPos!=iPrev ){ -+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos); - } -+ fts5PrefixMergerNextPosition(pThis); -+ pHead = pThis->pNext; -+ fts5PrefixMergerInsertByPosition(&pHead, pThis); -+ } - -- if( iPos1>=0 ){ -- if( iPos1!=iPrev ){ -- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1); -- } -- aCopy = &a1[iOff1]; -- nCopy = i1.nPoslist - iOff1; -- }else{ -- assert_nc( iPos2>=0 && iPos2!=iPrev ); -- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2); -- aCopy = &a2[iOff2]; -- nCopy = i2.nPoslist - iOff2; -- } -- if( nCopy>0 ){ -- fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy); -- } -+ if( pHead->iPos!=iPrev ){ -+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos); -+ } -+ nTail = pHead->iter.nPoslist - pHead->iOff; - -- /* WRITEPOSLISTSIZE */ -- assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist ); -- assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 ); -- if( tmp.n>i1.nPoslist+i2.nPoslist ){ -- if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; -- break; -+ /* WRITEPOSLISTSIZE */ -+ assert_nc( tmp.n+nTail<=nTmp ); -+ assert( tmp.n+nTail<=nTmp+nMerge*10 ); -+ if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){ -+ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; -+ break; -+ } -+ fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2); -+ fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); -+ if( nTail>0 ){ -+ fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail); -+ } -+ -+ pHead = pSave; -+ for(i=0; iiter.aPoslist && pX->iter.iRowid==iLastRowid ){ -+ fts5DoclistIterNext(&pX->iter); -+ fts5PrefixMergerInsertByRowid(&pHead, pX); - } -- fts5BufferSafeAppendVarint(&out, tmp.n * 2); -- fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); -- fts5DoclistIterNext(&i1); -- fts5DoclistIterNext(&i2); -- assert_nc( out.n<=(p1->n+p2->n+9) ); -- if( i1.aPoslist==0 || i2.aPoslist==0 ) break; -- assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) ); - } -+ -+ }else{ -+ /* Copy poslist from pHead to output */ -+ PrefixMerger *pThis = pHead; -+ Fts5DoclistIter *pI = &pThis->iter; -+ fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize); -+ fts5DoclistIterNext(pI); -+ pHead = pThis->pNext; -+ fts5PrefixMergerInsertByRowid(&pHead, pThis); - } -+ } - -- if( i1.aPoslist ){ -- fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid); -- fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist); -+ fts5BufferFree(p1); -+ fts5BufferFree(&tmp); -+ memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING); -+ *p1 = out; -+} -+ -+ -+/* -+** Iterate through a range of entries in the FTS index, invoking the xVisit -+** callback for each of them. -+** -+** Parameter pToken points to an nToken buffer containing an FTS index term -+** (i.e. a document term with the preceding 1 byte index identifier - -+** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits -+** all entries for terms that have pToken/nToken as a prefix. If bPrefix -+** is false, then only entries with pToken/nToken as the entire key are -+** visited. -+** -+** If the current table is a tokendata=1 table, then if bPrefix is true then -+** each index term is treated separately. However, if bPrefix is false, then -+** all index terms corresponding to pToken/nToken are collapsed into a single -+** term before the callback is invoked. -+** -+** The callback invoked for each entry visited is specified by paramter xVisit. -+** Each time it is invoked, it is passed a pointer to the Fts5Index object, -+** a copy of the 7th paramter to this function (pCtx) and a pointer to the -+** iterator that indicates the current entry. If the current entry is the -+** first with a new term (i.e. different from that of the previous entry, -+** including the very first term), then the final two parameters are passed -+** a pointer to the term and its size in bytes, respectively. If the current -+** entry is not the first associated with its term, these two parameters -+** are passed 0. -+** -+** If parameter pColset is not NULL, then it is used to filter entries before -+** the callback is invoked. -+*/ -+static int fts5VisitEntries( -+ Fts5Index *p, /* Fts5 index object */ -+ Fts5Colset *pColset, /* Columns filter to apply, or NULL */ -+ u8 *pToken, /* Buffer containing token */ -+ int nToken, /* Size of buffer pToken in bytes */ -+ int bPrefix, /* True for a prefix scan */ -+ void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int), -+ void *pCtx /* Passed as second argument to xVisit() */ -+){ -+ const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0) -+ | FTS5INDEX_QUERY_SKIPEMPTY -+ | FTS5INDEX_QUERY_NOOUTPUT; -+ Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ -+ int bNewTerm = 1; -+ Fts5Structure *pStruct = fts5StructureRead(p); -+ -+ fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); -+ fts5IterSetOutputCb(&p->rc, p1); -+ for( /* no-op */ ; -+ fts5MultiIterEof(p, p1)==0; -+ fts5MultiIterNext2(p, p1, &bNewTerm) -+ ){ -+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; -+ int nNew = 0; -+ const u8 *pNew = 0; -+ -+ p1->xSetOutputs(p1, pSeg); -+ if( p->rc ) break; -+ -+ if( bNewTerm ){ -+ nNew = pSeg->term.n; -+ pNew = pSeg->term.p; -+ if( nNewrc; -+} -+ -+ -+/* -+** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an -+** array of these for each row it visits (so all iRowid fields are the same). -+** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an -+** array of these for the entire query (in which case iRowid fields may take -+** a variety of values). -+** -+** Each instance in the array indicates the iterator (and therefore term) -+** associated with position iPos of rowid iRowid. This is used by the -+** xInstToken() API. -+** -+** iRowid: -+** Rowid for the current entry. -+** -+** iPos: -+** Position of current entry within row. In the usual ((iCol<<32)+iOff) -+** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()). -+** -+** iIter: -+** If the Fts5TokenDataIter iterator that the entry is part of is -+** actually an iterator (i.e. with nIter>0, not just a container for -+** Fts5TokenDataMap structures), then this variable is an index into -+** the apIter[] array. The corresponding term is that which the iterator -+** at apIter[iIter] currently points to. -+** -+** Or, if the Fts5TokenDataIter iterator is just a container object -+** (nIter==0), then iIter is an index into the term.p[] buffer where -+** the term is stored. -+** -+** nByte: -+** In the case where iIter is an index into term.p[], this variable -+** is the size of the term in bytes. If iIter is an index into apIter[], -+** this variable is unused. -+*/ -+struct Fts5TokenDataMap { -+ i64 iRowid; /* Row this token is located in */ -+ i64 iPos; /* Position of token */ -+ int iIter; /* Iterator token was read from */ -+ int nByte; /* Length of token in bytes (or 0) */ -+}; -+ -+/* -+** An object used to supplement Fts5Iter for tokendata=1 iterators. -+** -+** This object serves two purposes. The first is as a container for an array -+** of Fts5TokenDataMap structures, which are used to find the token required -+** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and -+** aMap[] variables. -+*/ -+struct Fts5TokenDataIter { -+ int nMapAlloc; /* Allocated size of aMap[] in entries */ -+ int nMap; /* Number of valid entries in aMap[] */ -+ Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */ -+ -+ /* The following are used for prefix-queries only. */ -+ Fts5Buffer terms; -+ -+ /* The following are used for other full-token tokendata queries only. */ -+ int nIter; -+ int nIterAlloc; -+ Fts5PoslistReader *aPoslistReader; -+ int *aPoslistToIter; -+ Fts5Iter *apIter[FLEXARRAY]; -+}; -+ -+/* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */ -+#define SZ_FTS5TOKENDATAITER(N) \ -+ (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter)) -+ -+/* -+** The two input arrays - a1[] and a2[] - are in sorted order. This function -+** merges the two arrays together and writes the result to output array -+** aOut[]. aOut[] is guaranteed to be large enough to hold the result. -+** -+** Duplicate entries are copied into the output. So the size of the output -+** array is always (n1+n2) entries. -+*/ -+static void fts5TokendataMerge( -+ Fts5TokenDataMap *a1, int n1, /* Input array 1 */ -+ Fts5TokenDataMap *a2, int n2, /* Input array 2 */ -+ Fts5TokenDataMap *aOut /* Output array */ -+){ -+ int i1 = 0; -+ int i2 = 0; -+ -+ assert( n1>=0 && n2>=0 ); -+ while( i1=n2 || (i1rc==SQLITE_OK ){ -+ if( pT->nMap==pT->nMapAlloc ){ -+ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; -+ int nAlloc = nNew * sizeof(Fts5TokenDataMap); -+ Fts5TokenDataMap *aNew; -+ -+ aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc); -+ if( aNew==0 ){ -+ p->rc = SQLITE_NOMEM; -+ return; -+ } -+ -+ pT->aMap = aNew; -+ pT->nMapAlloc = nNew; -+ } -+ -+ pT->aMap[pT->nMap].iRowid = iRowid; -+ pT->aMap[pT->nMap].iPos = iPos; -+ pT->aMap[pT->nMap].iIter = iIter; -+ pT->aMap[pT->nMap].nByte = nByte; -+ pT->nMap++; -+ } -+} -+ -+/* -+** Sort the contents of the pT->aMap[] array. -+** -+** The sorting algorithm requires a malloc(). If this fails, an error code -+** is left in Fts5Index.rc before returning. -+*/ -+static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ -+ Fts5TokenDataMap *aTmp = 0; -+ int nByte = pT->nMap * sizeof(Fts5TokenDataMap); -+ -+ aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte); -+ if( aTmp ){ -+ Fts5TokenDataMap *a1 = pT->aMap; -+ Fts5TokenDataMap *a2 = aTmp; -+ i64 nHalf; -+ -+ for(nHalf=1; nHalfnMap; nHalf=nHalf*2){ -+ int i1; -+ for(i1=0; i1nMap; i1+=(nHalf*2)){ -+ int n1 = MIN(nHalf, pT->nMap-i1); -+ int n2 = MIN(nHalf, pT->nMap-i1-n1); -+ fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]); -+ } -+ SWAPVAL(Fts5TokenDataMap*, a1, a2); -+ } -+ -+ if( a1!=pT->aMap ){ -+ memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap)); -+ } -+ sqlite3_free(aTmp); -+ -+#ifdef SQLITE_DEBUG -+ { -+ int ii; -+ for(ii=1; iinMap; ii++){ -+ Fts5TokenDataMap *p1 = &pT->aMap[ii-1]; -+ Fts5TokenDataMap *p2 = &pT->aMap[ii]; -+ assert( p1->iRowidiRowid -+ || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos) -+ ); -+ } -+ } -+#endif -+ } -+} -+ -+/* -+** Delete an Fts5TokenDataIter structure and its contents. -+*/ -+static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ -+ if( pSet ){ -+ int ii; -+ for(ii=0; iinIter; ii++){ -+ fts5MultiIterFree(pSet->apIter[ii]); -+ } -+ fts5BufferFree(&pSet->terms); -+ sqlite3_free(pSet->aPoslistReader); -+ sqlite3_free(pSet->aMap); -+ sqlite3_free(pSet); -+ } -+} -+ -+ -+/* -+** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata() -+** to pass data to prefixIterSetupTokendataCb(). -+*/ -+typedef struct TokendataSetupCtx TokendataSetupCtx; -+struct TokendataSetupCtx { -+ Fts5TokenDataIter *pT; /* Object being populated with mappings */ -+ int iTermOff; /* Offset of current term in terms.p[] */ -+ int nTermByte; /* Size of current term in bytes */ -+}; -+ -+/* -+** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This -+** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each -+** position in the current position-list. It doesn't matter that some of -+** these may be out of order - they will be sorted later. -+*/ -+static void prefixIterSetupTokendataCb( -+ Fts5Index *p, -+ void *pCtx, -+ Fts5Iter *p1, -+ const u8 *pNew, -+ int nNew -+){ -+ TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx; -+ int iPosOff = 0; -+ i64 iPos = 0; -+ -+ if( pNew ){ -+ pSetup->nTermByte = nNew-1; -+ pSetup->iTermOff = pSetup->pT->terms.n; -+ fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1); -+ } -+ -+ while( 0==sqlite3Fts5PoslistNext64( -+ p1->base.pData, p1->base.nData, &iPosOff, &iPos -+ ) ){ -+ fts5TokendataIterAppendMap(p, -+ pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos -+ ); -+ } -+} -+ -+ -+/* -+** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries(). -+*/ -+typedef struct PrefixSetupCtx PrefixSetupCtx; -+struct PrefixSetupCtx { -+ void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); -+ void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); -+ i64 iLastRowid; -+ int nMerge; -+ Fts5Buffer *aBuf; -+ int nBuf; -+ Fts5Buffer doclist; -+ TokendataSetupCtx *pTokendata; -+}; -+ -+/* -+** fts5VisitEntries() callback used by fts5SetupPrefixIter() -+*/ -+static void prefixIterSetupCb( -+ Fts5Index *p, -+ void *pCtx, -+ Fts5Iter *p1, -+ const u8 *pNew, -+ int nNew -+){ -+ PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx; -+ const int nMerge = pSetup->nMerge; -+ -+ if( p1->base.nData>0 ){ -+ if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){ -+ int i; -+ for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){ -+ int i1 = i*nMerge; -+ int iStore; -+ assert( i1+nMerge<=pSetup->nBuf ); -+ for(iStore=i1; iStoreaBuf[iStore].n==0 ){ -+ fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]); -+ fts5BufferZero(&pSetup->doclist); -+ break; -+ } -+ } -+ if( iStore==i1+nMerge ){ -+ pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]); -+ for(iStore=i1; iStoreaBuf[iStore]); -+ } -+ } -+ } -+ pSetup->iLastRowid = 0; - } -- assert_nc( out.n<=(p1->n+p2->n+9) ); - -- fts5BufferSet(&p->rc, p1, out.n, out.p); -- fts5BufferFree(&tmp); -- fts5BufferFree(&out); -+ pSetup->xAppend( -+ p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist -+ ); -+ pSetup->iLastRowid = p1->base.iRowid; -+ } -+ -+ if( pSetup->pTokendata ){ -+ prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew); - } - } - - static void fts5SetupPrefixIter( - Fts5Index *p, /* Index to read from */ - int bDesc, /* True for "ORDER BY rowid DESC" */ -- const u8 *pToken, /* Buffer containing prefix to match */ -+ int iIdx, /* Index to scan for data */ -+ u8 *pToken, /* Buffer containing prefix to match */ - int nToken, /* Size of buffer pToken in bytes */ - Fts5Colset *pColset, /* Restrict matches to these columns */ -- Fts5Iter **ppIter /* OUT: New iterator */ -+ Fts5Iter **ppIter /* OUT: New iterator */ - ){ - Fts5Structure *pStruct; -- Fts5Buffer *aBuf; -- const int nBuf = 32; -+ PrefixSetupCtx s; -+ TokendataSetupCtx s2; -+ -+ memset(&s, 0, sizeof(s)); -+ memset(&s2, 0, sizeof(s2)); -+ -+ s.nMerge = 1; -+ s.iLastRowid = 0; -+ s.nBuf = 32; -+ if( iIdx==0 -+ && p->pConfig->eDetail==FTS5_DETAIL_FULL -+ && p->pConfig->bPrefixInsttoken -+ ){ -+ s.pTokendata = &s2; -+ s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1)); -+ } - -- void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*); -- void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*); - if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ -- xMerge = fts5MergeRowidLists; -- xAppend = fts5AppendRowid; -+ s.xMerge = fts5MergeRowidLists; -+ s.xAppend = fts5AppendRowid; - }else{ -- xMerge = fts5MergePrefixLists; -- xAppend = fts5AppendPoslist; -+ s.nMerge = FTS5_MERGE_NLIST-1; -+ s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */ -+ s.xMerge = fts5MergePrefixLists; -+ s.xAppend = fts5AppendPoslist; - } - -- aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); -+ s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf); - pStruct = fts5StructureRead(p); -+ assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) ); - -- if( aBuf && pStruct ){ -- const int flags = FTS5INDEX_QUERY_SCAN -- | FTS5INDEX_QUERY_SKIPEMPTY -- | FTS5INDEX_QUERY_NOOUTPUT; -+ if( p->rc==SQLITE_OK ){ -+ void *pCtx = (void*)&s; - int i; -- i64 iLastRowid = 0; -- Fts5Iter *p1 = 0; /* Iterator used to gather data from index */ - Fts5Data *pData; -- Fts5Buffer doclist; -- int bNewTerm = 1; -- -- memset(&doclist, 0, sizeof(doclist)); -- fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); -- fts5IterSetOutputCb(&p->rc, p1); -- for( /* no-op */ ; -- fts5MultiIterEof(p, p1)==0; -- fts5MultiIterNext2(p, p1, &bNewTerm) -- ){ -- Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; -- int nTerm = pSeg->term.n; -- const u8 *pTerm = pSeg->term.p; -- p1->xSetOutputs(p1, pSeg); - -- assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); -- if( bNewTerm ){ -- if( nTermbase.nData==0 ) continue; -- -- if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ -- for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ -- assert( ibase.iRowid-iLastRowid, p1, &doclist); -- iLastRowid = p1->base.iRowid; -+ /* If iIdx is non-zero, then it is the number of a prefix-index for -+ ** prefixes 1 character longer than the prefix being queried for. That -+ ** index contains all the doclists required, except for the one -+ ** corresponding to the prefix itself. That one is extracted from the -+ ** main term index here. */ -+ if( iIdx!=0 ){ -+ pToken[0] = FTS5_MAIN_PREFIX; -+ fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx); - } - -- for(i=0; irc==SQLITE_OK ){ -- xMerge(p, &doclist, &aBuf[i]); -+ s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]); -+ } -+ for(iFree=i; iFreerc!=SQLITE_OK ); - if( pData ){ - pData->p = (u8*)&pData[1]; -- pData->nn = pData->szLeaf = doclist.n; -- if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); -+ pData->nn = pData->szLeaf = s.doclist.n; -+ if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n); - fts5MultiIterNew2(p, pData, bDesc, ppIter); - } -- fts5BufferFree(&doclist); -+ -+ assert( (*ppIter)!=0 || p->rc!=SQLITE_OK ); -+ if( p->rc==SQLITE_OK && s.pTokendata ){ -+ fts5TokendataIterSortMap(p, s2.pT); -+ (*ppIter)->pTokenDataIter = s2.pT; -+ s2.pT = 0; -+ } - } - -+ fts5TokendataIterDelete(s2.pT); -+ fts5BufferFree(&s.doclist); - fts5StructureRelease(pStruct); -- sqlite3_free(aBuf); -+ sqlite3_free(s.aBuf); - } - - -@@ -221548,6 +251002,9 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ - - p->iWriteRowid = iRowid; - p->bDelete = bDelete; -+ if( bDelete==0 ){ -+ p->nPendingRow++; -+ } - return fts5IndexReturn(p); - } - -@@ -221557,7 +251014,7 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ - static int sqlite3Fts5IndexSync(Fts5Index *p){ - assert( p->rc==SQLITE_OK ); - fts5IndexFlush(p); -- sqlite3Fts5IndexCloseReader(p); -+ fts5IndexCloseReader(p); - return fts5IndexReturn(p); - } - -@@ -221568,11 +251025,10 @@ static int sqlite3Fts5IndexSync(Fts5Index *p){ - ** records must be invalidated. - */ - static int sqlite3Fts5IndexRollback(Fts5Index *p){ -- sqlite3Fts5IndexCloseReader(p); -+ fts5IndexCloseReader(p); - fts5IndexDiscardData(p); - fts5StructureInvalidate(p); -- /* assert( p->rc==SQLITE_OK ); */ -- return SQLITE_OK; -+ return fts5IndexReturn(p); - } - - /* -@@ -221581,12 +251037,17 @@ static int sqlite3Fts5IndexRollback(Fts5Index *p){ - ** and the initial version of the "averages" record (a zero-byte blob). - */ - static int sqlite3Fts5IndexReinit(Fts5Index *p){ -- Fts5Structure s; -+ Fts5Structure *pTmp; -+ u8 tmpSpace[SZ_FTS5STRUCTURE(1)]; - fts5StructureInvalidate(p); - fts5IndexDiscardData(p); -- memset(&s, 0, sizeof(Fts5Structure)); -+ pTmp = (Fts5Structure*)tmpSpace; -+ memset(pTmp, 0, SZ_FTS5STRUCTURE(1)); -+ if( p->pConfig->bContentlessDelete ){ -+ pTmp->nOriginCntr = 1; -+ } - fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); -- fts5StructureWrite(p, &s); -+ fts5StructureWrite(p, pTmp); - return fts5IndexReturn(p); - } - -@@ -221648,7 +251109,9 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){ - sqlite3_finalize(p->pIdxWriter); - sqlite3_finalize(p->pIdxDeleter); - sqlite3_finalize(p->pIdxSelect); -+ sqlite3_finalize(p->pIdxNextSelect); - sqlite3_finalize(p->pDataVersion); -+ sqlite3_finalize(p->pDeleteFromIdx); - sqlite3Fts5HashFree(p->pHash); - sqlite3_free(p->zDataTbl); - sqlite3_free(p); -@@ -221742,6 +251205,387 @@ static int sqlite3Fts5IndexWrite( - return rc; - } - -+/* -+** pToken points to a buffer of size nToken bytes containing a search -+** term, including the index number at the start, used on a tokendata=1 -+** table. This function returns true if the term in buffer pBuf matches -+** token pToken/nToken. -+*/ -+static int fts5IsTokendataPrefix( -+ Fts5Buffer *pBuf, -+ const u8 *pToken, -+ int nToken -+){ -+ return ( -+ pBuf->n>=nToken -+ && 0==memcmp(pBuf->p, pToken, nToken) -+ && (pBuf->n==nToken || pBuf->p[nToken]==0x00) -+ ); -+} -+ -+/* -+** Ensure the segment-iterator passed as the only argument points to EOF. -+*/ -+static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ -+ fts5DataRelease(pSeg->pLeaf); -+ pSeg->pLeaf = 0; -+} -+ -+static void fts5IterClose(Fts5IndexIter *pIndexIter){ -+ if( pIndexIter ){ -+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; -+ Fts5Index *pIndex = pIter->pIndex; -+ fts5TokendataIterDelete(pIter->pTokenDataIter); -+ fts5MultiIterFree(pIter); -+ fts5IndexCloseReader(pIndex); -+ } -+} -+ -+/* -+** This function appends iterator pAppend to Fts5TokenDataIter pIn and -+** returns the result. -+*/ -+static Fts5TokenDataIter *fts5AppendTokendataIter( -+ Fts5Index *p, /* Index object (for error code) */ -+ Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */ -+ Fts5Iter *pAppend /* Append this iterator */ -+){ -+ Fts5TokenDataIter *pRet = pIn; -+ -+ if( p->rc==SQLITE_OK ){ -+ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ -+ int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; -+ int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); -+ Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); -+ -+ if( pNew==0 ){ -+ p->rc = SQLITE_NOMEM; -+ }else{ -+ if( pIn==0 ) memset(pNew, 0, nByte); -+ pRet = pNew; -+ pNew->nIterAlloc = nAlloc; -+ } -+ } -+ } -+ if( p->rc ){ -+ fts5IterClose((Fts5IndexIter*)pAppend); -+ }else{ -+ pRet->apIter[pRet->nIter++] = pAppend; -+ } -+ assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); -+ -+ return pRet; -+} -+ -+/* -+** The iterator passed as the only argument must be a tokendata=1 iterator -+** (pIter->pTokenDataIter!=0). This function sets the iterator output -+** variables (pIter->base.*) according to the contents of the current -+** row. -+*/ -+static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ -+ int ii; -+ int nHit = 0; -+ i64 iRowid = SMALLEST_INT64; -+ int iMin = 0; -+ -+ Fts5TokenDataIter *pT = pIter->pTokenDataIter; -+ -+ pIter->base.nData = 0; -+ pIter->base.pData = 0; -+ -+ for(ii=0; iinIter; ii++){ -+ Fts5Iter *p = pT->apIter[ii]; -+ if( p->base.bEof==0 ){ -+ if( nHit==0 || p->base.iRowidbase.iRowid; -+ nHit = 1; -+ pIter->base.pData = p->base.pData; -+ pIter->base.nData = p->base.nData; -+ iMin = ii; -+ }else if( p->base.iRowid==iRowid ){ -+ nHit++; -+ } -+ } -+ } -+ -+ if( nHit==0 ){ -+ pIter->base.bEof = 1; -+ }else{ -+ int eDetail = pIter->pIndex->pConfig->eDetail; -+ pIter->base.bEof = 0; -+ pIter->base.iRowid = iRowid; -+ -+ if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ -+ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1); -+ }else -+ if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ -+ int nReader = 0; -+ int nByte = 0; -+ i64 iPrev = 0; -+ -+ /* Allocate array of iterators if they are not already allocated. */ -+ if( pT->aPoslistReader==0 ){ -+ pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero( -+ &pIter->pIndex->rc, -+ pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)) -+ ); -+ if( pT->aPoslistReader==0 ) return; -+ pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter]; -+ } -+ -+ /* Populate an iterator for each poslist that will be merged */ -+ for(ii=0; iinIter; ii++){ -+ Fts5Iter *p = pT->apIter[ii]; -+ if( iRowid==p->base.iRowid ){ -+ pT->aPoslistToIter[nReader] = ii; -+ sqlite3Fts5PoslistReaderInit( -+ p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++] -+ ); -+ nByte += p->base.nData; -+ } -+ } -+ -+ /* Ensure the output buffer is large enough */ -+ if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){ -+ return; -+ } -+ -+ /* Ensure the token-mapping is large enough */ -+ if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){ -+ int nNew = (pT->nMapAlloc + nByte) * 2; -+ Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc( -+ pT->aMap, nNew*sizeof(Fts5TokenDataMap) -+ ); -+ if( aNew==0 ){ -+ pIter->pIndex->rc = SQLITE_NOMEM; -+ return; -+ } -+ pT->aMap = aNew; -+ pT->nMapAlloc = nNew; -+ } -+ -+ pIter->poslist.n = 0; -+ -+ while( 1 ){ -+ i64 iMinPos = LARGEST_INT64; -+ -+ /* Find smallest position */ -+ iMin = 0; -+ for(ii=0; iiaPoslistReader[ii]; -+ if( pReader->bEof==0 ){ -+ if( pReader->iPosiPos; -+ iMin = ii; -+ } -+ } -+ } -+ -+ /* If all readers were at EOF, break out of the loop. */ -+ if( iMinPos==LARGEST_INT64 ) break; -+ -+ sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos); -+ sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]); -+ -+ if( eDetail==FTS5_DETAIL_FULL ){ -+ pT->aMap[pT->nMap].iPos = iMinPos; -+ pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin]; -+ pT->aMap[pT->nMap].iRowid = iRowid; -+ pT->nMap++; -+ } -+ } -+ -+ pIter->base.pData = pIter->poslist.p; -+ pIter->base.nData = pIter->poslist.n; -+ } -+ } -+} -+ -+/* -+** The iterator passed as the only argument must be a tokendata=1 iterator -+** (pIter->pTokenDataIter!=0). This function advances the iterator. If -+** argument bFrom is false, then the iterator is advanced to the next -+** entry. Or, if bFrom is true, it is advanced to the first entry with -+** a rowid of iFrom or greater. -+*/ -+static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ -+ int ii; -+ Fts5TokenDataIter *pT = pIter->pTokenDataIter; -+ Fts5Index *pIndex = pIter->pIndex; -+ -+ for(ii=0; iinIter; ii++){ -+ Fts5Iter *p = pT->apIter[ii]; -+ if( p->base.bEof==0 -+ && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowidbase.bEof==0 -+ && p->base.iRowidrc==SQLITE_OK -+ ){ -+ fts5MultiIterNext(pIndex, p, 0, 0); -+ } -+ } -+ } -+ -+ if( pIndex->rc==SQLITE_OK ){ -+ fts5IterSetOutputsTokendata(pIter); -+ } -+} -+ -+/* -+** If the segment-iterator passed as the first argument is at EOF, then -+** set pIter->term to a copy of buffer pTerm. -+*/ -+static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ -+ if( pIter && pIter->aSeg[0].pLeaf==0 ){ -+ fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p); -+ } -+} -+ -+/* -+** This function sets up an iterator to use for a non-prefix query on a -+** tokendata=1 table. -+*/ -+static Fts5Iter *fts5SetupTokendataIter( -+ Fts5Index *p, /* FTS index to query */ -+ const u8 *pToken, /* Buffer containing query term */ -+ int nToken, /* Size of buffer pToken in bytes */ -+ Fts5Colset *pColset /* Colset to filter on */ -+){ -+ Fts5Iter *pRet = 0; -+ Fts5TokenDataIter *pSet = 0; -+ Fts5Structure *pStruct = 0; -+ const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN; -+ -+ Fts5Buffer bSeek = {0, 0, 0}; -+ Fts5Buffer *pSmall = 0; -+ -+ fts5IndexFlush(p); -+ pStruct = fts5StructureRead(p); -+ -+ while( p->rc==SQLITE_OK ){ -+ Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0; -+ Fts5Iter *pNew = 0; -+ Fts5SegIter *pNewIter = 0; -+ Fts5SegIter *pPrevIter = 0; -+ -+ int iLvl, iSeg, ii; -+ -+ pNew = fts5MultiIterAlloc(p, pStruct->nSegment); -+ if( pSmall ){ -+ fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p); -+ fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0"); -+ }else{ -+ fts5BufferSet(&p->rc, &bSeek, nToken, pToken); -+ } -+ if( p->rc ){ -+ fts5IterClose((Fts5IndexIter*)pNew); -+ break; -+ } -+ -+ pNewIter = &pNew->aSeg[0]; -+ pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0); -+ for(iLvl=0; iLvlnLevel; iLvl++){ -+ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ -+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; -+ int bDone = 0; -+ -+ if( pPrevIter ){ -+ if( fts5BufferCompare(pSmall, &pPrevIter->term) ){ -+ memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter)); -+ memset(pPrevIter, 0, sizeof(Fts5SegIter)); -+ bDone = 1; -+ }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){ -+ fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter); -+ bDone = 1; -+ } -+ } -+ -+ if( bDone==0 ){ -+ fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); -+ } -+ -+ if( pPrevIter ){ -+ if( pPrevIter->pTombArray ){ -+ pNewIter->pTombArray = pPrevIter->pTombArray; -+ pNewIter->pTombArray->nRef++; -+ } -+ }else{ -+ fts5SegIterAllocTombstone(p, pNewIter); -+ } -+ -+ pNewIter++; -+ if( pPrevIter ) pPrevIter++; -+ if( p->rc ) break; -+ } -+ } -+ fts5TokendataSetTermIfEof(pPrev, pSmall); -+ -+ pNew->bSkipEmpty = 1; -+ pNew->pColset = pColset; -+ fts5IterSetOutputCb(&p->rc, pNew); -+ -+ /* Loop through all segments in the new iterator. Find the smallest -+ ** term that any segment-iterator points to. Iterator pNew will be -+ ** used for this term. Also, set any iterator that points to a term that -+ ** does not match pToken/nToken to point to EOF */ -+ pSmall = 0; -+ for(ii=0; iinSeg; ii++){ -+ Fts5SegIter *pII = &pNew->aSeg[ii]; -+ if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){ -+ fts5SegIterSetEOF(pII); -+ } -+ if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){ -+ pSmall = &pII->term; -+ } -+ } -+ -+ /* If pSmall is still NULL at this point, then the new iterator does -+ ** not point to any terms that match the query. So delete it and break -+ ** out of the loop - all required iterators have been collected. */ -+ if( pSmall==0 ){ -+ fts5IterClose((Fts5IndexIter*)pNew); -+ break; -+ } -+ -+ /* Append this iterator to the set and continue. */ -+ pSet = fts5AppendTokendataIter(p, pSet, pNew); -+ } -+ -+ if( p->rc==SQLITE_OK && pSet ){ -+ int ii; -+ for(ii=0; iinIter; ii++){ -+ Fts5Iter *pIter = pSet->apIter[ii]; -+ int iSeg; -+ for(iSeg=0; iSegnSeg; iSeg++){ -+ pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM; -+ } -+ fts5MultiIterFinishSetup(p, pIter); -+ } -+ } -+ -+ if( p->rc==SQLITE_OK ){ -+ pRet = fts5MultiIterAlloc(p, 0); -+ } -+ if( pRet ){ -+ pRet->nSeg = 0; -+ pRet->pTokenDataIter = pSet; -+ if( pSet ){ -+ fts5IterSetOutputsTokendata(pRet); -+ }else{ -+ pRet->base.bEof = 1; -+ } -+ }else{ -+ fts5TokendataIterDelete(pSet); -+ } -+ -+ fts5StructureRelease(pStruct); -+ fts5BufferFree(&bSeek); -+ return pRet; -+} -+ - /* - ** Open a new iterator to iterate though all rowid that match the - ** specified token or token prefix. -@@ -221762,7 +251606,19 @@ static int sqlite3Fts5IndexQuery( - - if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ - int iIdx = 0; /* Index to search */ -- if( nToken ) memcpy(&buf.p[1], pToken, nToken); -+ int iPrefixIdx = 0; /* +1 prefix index */ -+ int bTokendata = pConfig->bTokendata; -+ assert( buf.p!=0 ); -+ if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); -+ -+ /* The NOTOKENDATA flag is set when each token in a tokendata=1 table -+ ** should be treated individually, instead of merging all those with -+ ** a common prefix into a single entry. This is used, for example, by -+ ** queries performed as part of an integrity-check, or by the fts5vocab -+ ** module. */ -+ if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ -+ bTokendata = 0; -+ } - - /* Figure out which index to search and set iIdx accordingly. If this - ** is a prefix query for which there is no prefix index, set iIdx to -@@ -221783,11 +251639,16 @@ static int sqlite3Fts5IndexQuery( - if( flags & FTS5INDEX_QUERY_PREFIX ){ - int nChar = fts5IndexCharlen(pToken, nToken); - for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){ -- if( pConfig->aPrefix[iIdx-1]==nChar ) break; -+ int nIdxChar = pConfig->aPrefix[iIdx-1]; -+ if( nIdxChar==nChar ) break; -+ if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx; - } - } - -- if( iIdx<=pConfig->nPrefix ){ -+ if( bTokendata && iIdx==0 ){ -+ buf.p[0] = FTS5_MAIN_PREFIX; -+ pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); -+ }else if( iIdx<=pConfig->nPrefix ){ - /* Straight index lookup */ - Fts5Structure *pStruct = fts5StructureRead(p); - buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); -@@ -221798,22 +251659,25 @@ static int sqlite3Fts5IndexQuery( - fts5StructureRelease(pStruct); - } - }else{ -- /* Scan multiple terms in the main index */ -+ /* Scan multiple terms in the main index for a prefix query. */ - int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; -- buf.p[0] = FTS5_MAIN_PREFIX; -- fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet); -- assert( p->rc!=SQLITE_OK || pRet->pColset==0 ); -- fts5IterSetOutputCb(&p->rc, pRet); -- if( p->rc==SQLITE_OK ){ -- Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; -- if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); -+ fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); -+ if( pRet==0 ){ -+ assert( p->rc!=SQLITE_OK ); -+ }else{ -+ assert( pRet->pColset==0 ); -+ fts5IterSetOutputCb(&p->rc, pRet); -+ if( p->rc==SQLITE_OK ){ -+ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; -+ if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg); -+ } - } - } - - if( p->rc ){ -- sqlite3Fts5IterClose((Fts5IndexIter*)pRet); -+ fts5IterClose((Fts5IndexIter*)pRet); - pRet = 0; -- sqlite3Fts5IndexCloseReader(p); -+ fts5IndexCloseReader(p); - } - - *ppIter = (Fts5IndexIter*)pRet; -@@ -221831,7 +251695,12 @@ static int sqlite3Fts5IndexQuery( - static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ - Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - assert( pIter->pIndex->rc==SQLITE_OK ); -- fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); -+ if( pIter->nSeg==0 ){ -+ assert( pIter->pTokenDataIter ); -+ fts5TokendataIterNext(pIter, 0, 0); -+ }else{ -+ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); -+ } - return fts5IndexReturn(pIter->pIndex); - } - -@@ -221864,7 +251733,12 @@ static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){ - */ - static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ - Fts5Iter *pIter = (Fts5Iter*)pIndexIter; -- fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); -+ if( pIter->nSeg==0 ){ -+ assert( pIter->pTokenDataIter ); -+ fts5TokendataIterNext(pIter, 1, iMatch); -+ }else{ -+ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); -+ } - return fts5IndexReturn(pIter->pIndex); - } - -@@ -221874,8 +251748,180 @@ static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ - static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ - int n; - const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n); -+ assert_nc( z || n<=1 ); - *pn = n-1; -- return &z[1]; -+ return (z ? &z[1] : 0); -+} -+ -+/* -+** pIter is a prefix query. This function populates pIter->pTokenDataIter -+** with an Fts5TokenDataIter object containing mappings for all rows -+** matched by the query. -+*/ -+static int fts5SetupPrefixIterTokendata( -+ Fts5Iter *pIter, -+ const char *pToken, /* Token prefix to search for */ -+ int nToken /* Size of pToken in bytes */ -+){ -+ Fts5Index *p = pIter->pIndex; -+ Fts5Buffer token = {0, 0, 0}; -+ TokendataSetupCtx ctx; -+ -+ memset(&ctx, 0, sizeof(ctx)); -+ -+ fts5BufferGrow(&p->rc, &token, nToken+1); -+ assert( token.p!=0 || p->rc!=SQLITE_OK ); -+ ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, -+ SZ_FTS5TOKENDATAITER(1)); -+ -+ if( p->rc==SQLITE_OK ){ -+ -+ /* Fill in the token prefix to search for */ -+ token.p[0] = FTS5_MAIN_PREFIX; -+ memcpy(&token.p[1], pToken, nToken); -+ token.n = nToken+1; -+ -+ fts5VisitEntries( -+ p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx -+ ); -+ -+ fts5TokendataIterSortMap(p, ctx.pT); -+ } -+ -+ if( p->rc==SQLITE_OK ){ -+ pIter->pTokenDataIter = ctx.pT; -+ }else{ -+ fts5TokendataIterDelete(ctx.pT); -+ } -+ fts5BufferFree(&token); -+ -+ return fts5IndexReturn(p); -+} -+ -+/* -+** This is used by xInstToken() to access the token at offset iOff, column -+** iCol of row iRowid. The token is returned via output variables *ppOut -+** and *pnOut. The iterator passed as the first argument must be a tokendata=1 -+** iterator (pIter->pTokenDataIter!=0). -+** -+** pToken/nToken: -+*/ -+static int sqlite3Fts5IterToken( -+ Fts5IndexIter *pIndexIter, -+ const char *pToken, int nToken, -+ i64 iRowid, -+ int iCol, -+ int iOff, -+ const char **ppOut, int *pnOut -+){ -+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; -+ Fts5TokenDataIter *pT = pIter->pTokenDataIter; -+ i64 iPos = (((i64)iCol)<<32) + iOff; -+ Fts5TokenDataMap *aMap = 0; -+ int i1 = 0; -+ int i2 = 0; -+ int iTest = 0; -+ -+ assert( pT || (pToken && pIter->nSeg>0) ); -+ if( pT==0 ){ -+ int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken); -+ if( rc!=SQLITE_OK ) return rc; -+ pT = pIter->pTokenDataIter; -+ } -+ -+ i2 = pT->nMap; -+ aMap = pT->aMap; -+ -+ while( i2>i1 ){ -+ iTest = (i1 + i2) / 2; -+ -+ if( aMap[iTest].iRowidiRowid ){ -+ i2 = iTest; -+ }else{ -+ if( aMap[iTest].iPosiPos ){ -+ i2 = iTest; -+ }else{ -+ break; -+ } -+ } -+ } -+ -+ if( i2>i1 ){ -+ if( pIter->nSeg==0 ){ -+ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; -+ *ppOut = (const char*)pMap->aSeg[0].term.p+1; -+ *pnOut = pMap->aSeg[0].term.n-1; -+ }else{ -+ Fts5TokenDataMap *p = &aMap[iTest]; -+ *ppOut = (const char*)&pT->terms.p[p->iIter]; -+ *pnOut = aMap[iTest].nByte; -+ } -+ } -+ -+ return SQLITE_OK; -+} -+ -+/* -+** Clear any existing entries from the token-map associated with the -+** iterator passed as the only argument. -+*/ -+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ -+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; -+ if( pIter && pIter->pTokenDataIter -+ && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL) -+ ){ -+ pIter->pTokenDataIter->nMap = 0; -+ } -+} -+ -+/* -+** Set a token-mapping for the iterator passed as the first argument. This -+** is used in detail=column or detail=none mode when a token is requested -+** using the xInstToken() API. In this case the caller tokenizers the -+** current row and configures the token-mapping via multiple calls to this -+** function. -+*/ -+static int sqlite3Fts5IndexIterWriteTokendata( -+ Fts5IndexIter *pIndexIter, -+ const char *pToken, int nToken, -+ i64 iRowid, int iCol, int iOff -+){ -+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; -+ Fts5TokenDataIter *pT = pIter->pTokenDataIter; -+ Fts5Index *p = pIter->pIndex; -+ i64 iPos = (((i64)iCol)<<32) + iOff; -+ -+ assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); -+ assert( pIter->pTokenDataIter || pIter->nSeg>0 ); -+ if( pIter->nSeg>0 ){ -+ /* This is a prefix term iterator. */ -+ if( pT==0 ){ -+ pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, -+ SZ_FTS5TOKENDATAITER(1)); -+ pIter->pTokenDataIter = pT; -+ } -+ if( pT ){ -+ fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); -+ fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); -+ } -+ }else{ -+ int ii; -+ for(ii=0; iinIter; ii++){ -+ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; -+ if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; -+ } -+ if( iinIter ){ -+ fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos); -+ } -+ } -+ return fts5IndexReturn(p); - } - - /* -@@ -221883,10 +251929,9 @@ static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ - */ - static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ - if( pIndexIter ){ -- Fts5Iter *pIter = (Fts5Iter*)pIndexIter; -- Fts5Index *pIndex = pIter->pIndex; -- fts5MultiIterFree(pIter); -- sqlite3Fts5IndexCloseReader(pIndex); -+ Fts5Index *pIndex = ((Fts5Iter*)pIndexIter)->pIndex; -+ fts5IterClose(pIndexIter); -+ fts5IndexReturn(pIndex); - } - } - -@@ -221968,6 +252013,347 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){ - return fts5IndexReturn(p); - } - -+/* -+** Retrieve the origin value that will be used for the segment currently -+** being accumulated in the in-memory hash table when it is flushed to -+** disk. If successful, SQLITE_OK is returned and (*piOrigin) set to -+** the queried value. Or, if an error occurs, an error code is returned -+** and the final value of (*piOrigin) is undefined. -+*/ -+static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin){ -+ Fts5Structure *pStruct; -+ pStruct = fts5StructureRead(p); -+ if( pStruct ){ -+ *piOrigin = pStruct->nOriginCntr; -+ fts5StructureRelease(pStruct); -+ } -+ return fts5IndexReturn(p); -+} -+ -+/* -+** Buffer pPg contains a page of a tombstone hash table - one of nPg pages -+** associated with the same segment. This function adds rowid iRowid to -+** the hash table. The caller is required to guarantee that there is at -+** least one free slot on the page. -+** -+** If parameter bForce is false and the hash table is deemed to be full -+** (more than half of the slots are occupied), then non-zero is returned -+** and iRowid not inserted. Or, if bForce is true or if the hash table page -+** is not full, iRowid is inserted and zero returned. -+*/ -+static int fts5IndexTombstoneAddToPage( -+ Fts5Data *pPg, -+ int bForce, -+ int nPg, -+ u64 iRowid -+){ -+ const int szKey = TOMBSTONE_KEYSIZE(pPg); -+ const int nSlot = TOMBSTONE_NSLOT(pPg); -+ const int nElem = fts5GetU32(&pPg->p[4]); -+ int iSlot = (iRowid / nPg) % nSlot; -+ int nCollide = nSlot; -+ -+ if( szKey==4 && iRowid>0xFFFFFFFF ) return 2; -+ if( iRowid==0 ){ -+ pPg->p[1] = 0x01; -+ return 0; -+ } -+ -+ if( bForce==0 && nElem>=(nSlot/2) ){ -+ return 1; -+ } -+ -+ fts5PutU32(&pPg->p[4], nElem+1); -+ if( szKey==4 ){ -+ u32 *aSlot = (u32*)&pPg->p[8]; -+ while( aSlot[iSlot] ){ -+ iSlot = (iSlot + 1) % nSlot; -+ if( nCollide--==0 ) return 0; -+ } -+ fts5PutU32((u8*)&aSlot[iSlot], (u32)iRowid); -+ }else{ -+ u64 *aSlot = (u64*)&pPg->p[8]; -+ while( aSlot[iSlot] ){ -+ iSlot = (iSlot + 1) % nSlot; -+ if( nCollide--==0 ) return 0; -+ } -+ fts5PutU64((u8*)&aSlot[iSlot], iRowid); -+ } -+ -+ return 0; -+} -+ -+/* -+** This function attempts to build a new hash containing all the keys -+** currently in the tombstone hash table for segment pSeg. The new -+** hash will be stored in the nOut buffers passed in array apOut[]. -+** All pages of the new hash use key-size szKey (4 or 8). -+** -+** Return 0 if the hash is successfully rebuilt into the nOut pages. -+** Or non-zero if it is not (because one page became overfull). In this -+** case the caller should retry with a larger nOut parameter. -+** -+** Parameter pData1 is page iPg1 of the hash table being rebuilt. -+*/ -+static int fts5IndexTombstoneRehash( -+ Fts5Index *p, -+ Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ -+ Fts5Data *pData1, /* One page of current hash - or NULL */ -+ int iPg1, /* Which page of the current hash is pData1 */ -+ int szKey, /* 4 or 8, the keysize */ -+ int nOut, /* Number of output pages */ -+ Fts5Data **apOut /* Array of output hash pages */ -+){ -+ int ii; -+ int res = 0; -+ -+ /* Initialize the headers of all the output pages */ -+ for(ii=0; iip[0] = szKey; -+ fts5PutU32(&apOut[ii]->p[4], 0); -+ } -+ -+ /* Loop through the current pages of the hash table. */ -+ for(ii=0; res==0 && iinPgTombstone; ii++){ -+ Fts5Data *pData = 0; /* Page ii of the current hash table */ -+ Fts5Data *pFree = 0; /* Free this at the end of the loop */ -+ -+ if( iPg1==ii ){ -+ pData = pData1; -+ }else{ -+ pFree = pData = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid, ii)); -+ } -+ -+ if( pData ){ -+ int szKeyIn = TOMBSTONE_KEYSIZE(pData); -+ int nSlotIn = (pData->nn - 8) / szKeyIn; -+ int iIn; -+ for(iIn=0; iInp[8]; -+ if( aSlot[iIn] ) iVal = fts5GetU32((u8*)&aSlot[iIn]); -+ }else{ -+ u64 *aSlot = (u64*)&pData->p[8]; -+ if( aSlot[iIn] ) iVal = fts5GetU64((u8*)&aSlot[iIn]); -+ } -+ -+ /* If iVal is not 0 at this point, insert it into the new hash table */ -+ if( iVal ){ -+ Fts5Data *pPg = apOut[(iVal % nOut)]; -+ res = fts5IndexTombstoneAddToPage(pPg, 0, nOut, iVal); -+ if( res ) break; -+ } -+ } -+ -+ /* If this is page 0 of the old hash, copy the rowid-0-flag from the -+ ** old hash to the new. */ -+ if( ii==0 ){ -+ apOut[0]->p[1] = pData->p[1]; -+ } -+ } -+ fts5DataRelease(pFree); -+ } -+ -+ return res; -+} -+ -+/* -+** This is called to rebuild the hash table belonging to segment pSeg. -+** If parameter pData1 is not NULL, then one page of the existing hash table -+** has already been loaded - pData1, which is page iPg1. The key-size for -+** the new hash table is szKey (4 or 8). -+** -+** If successful, the new hash table is not written to disk. Instead, -+** output parameter (*pnOut) is set to the number of pages in the new -+** hash table, and (*papOut) to point to an array of buffers containing -+** the new page data. -+** -+** If an error occurs, an error code is left in the Fts5Index object and -+** both output parameters set to 0 before returning. -+*/ -+static void fts5IndexTombstoneRebuild( -+ Fts5Index *p, -+ Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */ -+ Fts5Data *pData1, /* One page of current hash - or NULL */ -+ int iPg1, /* Which page of the current hash is pData1 */ -+ int szKey, /* 4 or 8, the keysize */ -+ int *pnOut, /* OUT: Number of output pages */ -+ Fts5Data ***papOut /* OUT: Output hash pages */ -+){ -+ const int MINSLOT = 32; -+ int nSlotPerPage = MAX(MINSLOT, (p->pConfig->pgsz - 8) / szKey); -+ int nSlot = 0; /* Number of slots in each output page */ -+ int nOut = 0; -+ -+ /* Figure out how many output pages (nOut) and how many slots per -+ ** page (nSlot). There are three possibilities: -+ ** -+ ** 1. The hash table does not yet exist. In this case the new hash -+ ** table will consist of a single page with MINSLOT slots. -+ ** -+ ** 2. The hash table exists but is currently a single page. In this -+ ** case an attempt is made to grow the page to accommodate the new -+ ** entry. The page is allowed to grow up to nSlotPerPage (see above) -+ ** slots. -+ ** -+ ** 3. The hash table already consists of more than one page, or of -+ ** a single page already so large that it cannot be grown. In this -+ ** case the new hash consists of (nPg*2+1) pages of nSlotPerPage -+ ** slots each, where nPg is the current number of pages in the -+ ** hash table. -+ */ -+ if( pSeg->nPgTombstone==0 ){ -+ /* Case 1. */ -+ nOut = 1; -+ nSlot = MINSLOT; -+ }else if( pSeg->nPgTombstone==1 ){ -+ /* Case 2. */ -+ int nElem = (int)fts5GetU32(&pData1->p[4]); -+ assert( pData1 && iPg1==0 ); -+ nOut = 1; -+ nSlot = MAX(nElem*4, MINSLOT); -+ if( nSlot>nSlotPerPage ) nOut = 0; -+ } -+ if( nOut==0 ){ -+ /* Case 3. */ -+ nOut = (pSeg->nPgTombstone * 2 + 1); -+ nSlot = nSlotPerPage; -+ } -+ -+ /* Allocate the required array and output pages */ -+ while( 1 ){ -+ int res = 0; -+ int ii = 0; -+ int szPage = 0; -+ Fts5Data **apOut = 0; -+ -+ /* Allocate space for the new hash table */ -+ assert( nSlot>=MINSLOT ); -+ apOut = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data*) * nOut); -+ szPage = 8 + nSlot*szKey; -+ for(ii=0; iirc, -+ sizeof(Fts5Data)+szPage -+ ); -+ if( pNew ){ -+ pNew->nn = szPage; -+ pNew->p = (u8*)&pNew[1]; -+ apOut[ii] = pNew; -+ } -+ } -+ -+ /* Rebuild the hash table. */ -+ if( p->rc==SQLITE_OK ){ -+ res = fts5IndexTombstoneRehash(p, pSeg, pData1, iPg1, szKey, nOut, apOut); -+ } -+ if( res==0 ){ -+ if( p->rc ){ -+ fts5IndexFreeArray(apOut, nOut); -+ apOut = 0; -+ nOut = 0; -+ } -+ *pnOut = nOut; -+ *papOut = apOut; -+ break; -+ } -+ -+ /* If control flows to here, it was not possible to rebuild the hash -+ ** table. Free all buffers and then try again with more pages. */ -+ assert( p->rc==SQLITE_OK ); -+ fts5IndexFreeArray(apOut, nOut); -+ nSlot = nSlotPerPage; -+ nOut = nOut*2 + 1; -+ } -+} -+ -+ -+/* -+** Add a tombstone for rowid iRowid to segment pSeg. -+*/ -+static void fts5IndexTombstoneAdd( -+ Fts5Index *p, -+ Fts5StructureSegment *pSeg, -+ u64 iRowid -+){ -+ Fts5Data *pPg = 0; -+ int iPg = -1; -+ int szKey = 0; -+ int nHash = 0; -+ Fts5Data **apHash = 0; -+ -+ p->nContentlessDelete++; -+ -+ if( pSeg->nPgTombstone>0 ){ -+ iPg = iRowid % pSeg->nPgTombstone; -+ pPg = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg)); -+ if( pPg==0 ){ -+ assert( p->rc!=SQLITE_OK ); -+ return; -+ } -+ -+ if( 0==fts5IndexTombstoneAddToPage(pPg, 0, pSeg->nPgTombstone, iRowid) ){ -+ fts5DataWrite(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg), pPg->p, pPg->nn); -+ fts5DataRelease(pPg); -+ return; -+ } -+ } -+ -+ /* Have to rebuild the hash table. First figure out the key-size (4 or 8). */ -+ szKey = pPg ? TOMBSTONE_KEYSIZE(pPg) : 4; -+ if( iRowid>0xFFFFFFFF ) szKey = 8; -+ -+ /* Rebuild the hash table */ -+ fts5IndexTombstoneRebuild(p, pSeg, pPg, iPg, szKey, &nHash, &apHash); -+ assert( p->rc==SQLITE_OK || (nHash==0 && apHash==0) ); -+ -+ /* If all has succeeded, write the new rowid into one of the new hash -+ ** table pages, then write them all out to disk. */ -+ if( nHash ){ -+ int ii = 0; -+ fts5IndexTombstoneAddToPage(apHash[iRowid % nHash], 1, nHash, iRowid); -+ for(ii=0; iiiSegid, ii); -+ fts5DataWrite(p, iTombstoneRowid, apHash[ii]->p, apHash[ii]->nn); -+ } -+ pSeg->nPgTombstone = nHash; -+ fts5StructureWrite(p, p->pStruct); -+ } -+ -+ fts5DataRelease(pPg); -+ fts5IndexFreeArray(apHash, nHash); -+} -+ -+/* -+** Add iRowid to the tombstone list of the segment or segments that contain -+** rows from origin iOrigin. Return SQLITE_OK if successful, or an SQLite -+** error code otherwise. -+*/ -+static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid){ -+ Fts5Structure *pStruct; -+ pStruct = fts5StructureRead(p); -+ if( pStruct ){ -+ int bFound = 0; /* True after pSeg->nEntryTombstone incr. */ -+ int iLvl; -+ for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){ -+ int iSeg; -+ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ -+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; -+ if( pSeg->iOrigin1<=(u64)iOrigin && pSeg->iOrigin2>=(u64)iOrigin ){ -+ if( bFound==0 ){ -+ pSeg->nEntryTombstone++; -+ bFound = 1; -+ } -+ fts5IndexTombstoneAdd(p, pSeg, iRowid); -+ } -+ } -+ } -+ fts5StructureRelease(pStruct); -+ } -+ return fts5IndexReturn(p); -+} - - /************************************************************************* - ************************************************************************** -@@ -222051,9 +252437,11 @@ static int fts5QueryCksum( - int eDetail = p->pConfig->eDetail; - u64 cksum = *pCksum; - Fts5IndexIter *pIter = 0; -- int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter); -+ int rc = sqlite3Fts5IndexQuery( -+ p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter -+ ); - -- while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIter) ){ -+ while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){ - i64 rowid = pIter->iRowid; - - if( eDetail==FTS5_DETAIL_NONE ){ -@@ -222073,7 +252461,7 @@ static int fts5QueryCksum( - rc = sqlite3Fts5IterNext(pIter); - } - } -- sqlite3Fts5IterClose(pIter); -+ fts5IterClose(pIter); - - *pCksum = cksum; - return rc; -@@ -222218,7 +252606,7 @@ static void fts5IndexIntegrityCheckEmpty( - } - - static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ -- int iTermOff = 0; -+ i64 iTermOff = 0; - int ii; - - Fts5Buffer buf1 = {0,0,0}; -@@ -222227,7 +252615,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ - ii = pLeaf->szLeaf; - while( iinn && p->rc==SQLITE_OK ){ - int res; -- int iOff; -+ i64 iOff; - int nIncr; - - ii += fts5GetVarint32(&pLeaf->p[ii], nIncr); -@@ -222272,6 +252660,7 @@ static void fts5IndexIntegrityCheckSegment( - Fts5StructureSegment *pSeg /* Segment to check internal consistency */ - ){ - Fts5Config *pConfig = p->pConfig; -+ int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE); - sqlite3_stmt *pStmt = 0; - int rc2; - int iIdxPrevLeaf = pSeg->pgnoFirst-1; -@@ -222307,7 +252696,19 @@ static void fts5IndexIntegrityCheckSegment( - ** is also a rowid pointer within the leaf page header, it points to a - ** location before the term. */ - if( pLeaf->nn<=pLeaf->szLeaf ){ -- p->rc = FTS5_CORRUPT; -+ -+ if( nIdxTerm==0 -+ && pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE -+ && pLeaf->nn==pLeaf->szLeaf -+ && pLeaf->nn==4 -+ ){ -+ /* special case - the very first page in a segment keeps its %_idx -+ ** entry even if all the terms are removed from it by secure-delete -+ ** operations. */ -+ }else{ -+ p->rc = FTS5_CORRUPT; -+ } -+ - }else{ - int iOff; /* Offset of first term on leaf */ - int iRowidOff; /* Offset of first rowid on leaf */ -@@ -222371,9 +252772,12 @@ static void fts5IndexIntegrityCheckSegment( - ASSERT_SZLEAF_OK(pLeaf); - if( iRowidOff>=pLeaf->szLeaf ){ - p->rc = FTS5_CORRUPT; -- }else{ -+ }else if( bSecureDelete==0 || iRowidOff>0 ){ -+ i64 iDlRowid = fts5DlidxIterRowid(pDlidx); - fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); -- if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT; -+ if( iRowidrc = FTS5_CORRUPT; -+ } - } - fts5DataRelease(pLeaf); - } -@@ -222412,12 +252816,13 @@ static void fts5IndexIntegrityCheckSegment( - ** error, or some other SQLite error code if another error (e.g. OOM) - ** occurs. - */ --static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ -+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){ - int eDetail = p->pConfig->eDetail; - u64 cksum2 = 0; /* Checksum based on contents of indexes */ - Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ - Fts5Iter *pIter; /* Used to iterate through entire index */ - Fts5Structure *pStruct; /* Index structure */ -+ int iLvl, iSeg; - - #ifdef SQLITE_DEBUG - /* Used by extra internal tests only run if NDEBUG is not defined */ -@@ -222428,15 +252833,16 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ - - /* Load the FTS index structure */ - pStruct = fts5StructureRead(p); -+ if( pStruct==0 ){ -+ assert( p->rc!=SQLITE_OK ); -+ return fts5IndexReturn(p); -+ } - - /* Check that the internal nodes of each segment match the leaves */ -- if( pStruct ){ -- int iLvl, iSeg; -- for(iLvl=0; iLvlnLevel; iLvl++){ -- for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ -- Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; -- fts5IndexIntegrityCheckSegment(p, pSeg); -- } -+ for(iLvl=0; iLvlnLevel; iLvl++){ -+ for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ -+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; -+ fts5IndexIntegrityCheckSegment(p, pSeg); - } - } - -@@ -222465,6 +252871,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ - - /* If this is a new term, query for it. Update cksum3 with the results. */ - fts5TestTerm(p, &term, z, n, cksum2, &cksum3); -+ if( p->rc ) break; - - if( eDetail==FTS5_DETAIL_NONE ){ - if( 0==fts5MultiIterIsEmpty(p, pIter) ){ -@@ -222473,6 +252880,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ - }else{ - poslist.n = 0; - fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); -+ fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0"); - while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ - int iCol = FTS5_POS2COLUMN(iPos); - int iTokOff = FTS5_POS2OFFSET(iPos); -@@ -222483,7 +252891,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ - fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3); - - fts5MultiIterFree(pIter); -- if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT; -+ if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT; - - fts5StructureRelease(pStruct); - #ifdef SQLITE_DEBUG -@@ -222499,12 +252907,14 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ - ** function only. - */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - /* - ** Decode a segment-data rowid from the %_data table. This function is - ** the opposite of macro FTS5_SEGMENT_ROWID(). - */ - static void fts5DecodeRowid( - i64 iRowid, /* Rowid from %_data table */ -+ int *pbTombstone, /* OUT: Tombstone hash flag */ - int *piSegid, /* OUT: Segment id */ - int *pbDlidx, /* OUT: Dlidx flag */ - int *piHeight, /* OUT: Height */ -@@ -222520,11 +252930,16 @@ static void fts5DecodeRowid( - iRowid >>= FTS5_DATA_DLI_B; - - *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); -+ iRowid >>= FTS5_DATA_ID_B; -+ -+ *pbTombstone = (int)(iRowid & 0x0001); - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ -- int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */ -- fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno); -+ int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */ -+ fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); - - if( iSegid==0 ){ - if( iKey==FTS5_AVERAGES_ROWID ){ -@@ -222534,12 +252949,16 @@ static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ - } - } - else{ -- sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%ssegid=%d h=%d pgno=%d}", -- bDlidx ? "dlidx " : "", iSegid, iHeight, iPgno -+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%s%ssegid=%d h=%d pgno=%d}", -+ bDlidx ? "dlidx " : "", -+ bTomb ? "tombstone " : "", -+ iSegid, iHeight, iPgno - ); - } - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - static void fts5DebugStructure( - int *pRc, /* IN/OUT: error code */ - Fts5Buffer *pBuf, -@@ -222554,14 +252973,22 @@ static void fts5DebugStructure( - ); - for(iSeg=0; iSegnSeg; iSeg++){ - Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; -- sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d}", -+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d", - pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast - ); -+ if( pSeg->iOrigin1>0 ){ -+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " origin=%lld..%lld", -+ pSeg->iOrigin1, pSeg->iOrigin2 -+ ); -+ } -+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); - } - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); - } - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - /* - ** This is part of the fts5_decode() debugging aid. - ** -@@ -222586,7 +253013,9 @@ static void fts5DecodeStructure( - fts5DebugStructure(pRc, pBuf, p); - fts5StructureRelease(p); - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - /* - ** This is part of the fts5_decode() debugging aid. - ** -@@ -222609,7 +253038,9 @@ static void fts5DecodeAverages( - zSpace = " "; - } - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - /* - ** Buffer (a/n) is assumed to contain a list of serialized varints. Read - ** each varint and append its string representation to buffer pBuf. Return -@@ -222626,7 +253057,9 @@ static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ - } - return iOff; - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - /* - ** The start of buffer (a/n) contains the start of a doclist. The doclist - ** may or may not finish within the buffer. This function appends a text -@@ -222659,7 +253092,9 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ - - return iOff; - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - /* - ** This function is part of the fts5_decode() debugging function. It is - ** only ever used with detail=none tables. -@@ -222700,7 +253135,27 @@ static void fts5DecodeRowidList( - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp); - } - } -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ -+ -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) -+static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){ -+ int ii; -+ fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1); -+ if( *pRc==SQLITE_OK ){ -+ for(ii=0; iin; ii++){ -+ if( pTerm->p[ii]==0x00 ){ -+ pBuf->p[pBuf->n++] = '\\'; -+ pBuf->p[pBuf->n++] = '0'; -+ }else{ -+ pBuf->p[pBuf->n++] = pTerm->p[ii]; -+ } -+ } -+ pBuf->p[pBuf->n] = 0x00; -+ } -+} -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - /* - ** The implementation of user-defined scalar function fts5_decode(). - */ -@@ -222711,6 +253166,7 @@ static void fts5DecodeFunction( - ){ - i64 iRowid; /* Rowid for record being decoded */ - int iSegid,iHeight,iPgno,bDlidx;/* Rowid components */ -+ int bTomb; - const u8 *aBlob; int n; /* Record to decode */ - u8 *a = 0; - Fts5Buffer s; /* Build up text to return here */ -@@ -222728,12 +253184,12 @@ static void fts5DecodeFunction( - ** buffer overreads even if the record is corrupt. */ - n = sqlite3_value_bytes(apVal[1]); - aBlob = sqlite3_value_blob(apVal[1]); -- nSpace = n + FTS5_DATA_ZERO_PADDING; -+ nSpace = ((i64)n) + FTS5_DATA_ZERO_PADDING; - a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace); - if( a==0 ) goto decode_out; - if( n>0 ) memcpy(a, aBlob, n); - -- fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno); -+ fts5DecodeRowid(iRowid, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); - - fts5DebugRowid(&rc, &s, iRowid); - if( bDlidx ){ -@@ -222752,6 +253208,28 @@ static void fts5DecodeFunction( - " %d(%lld)", lvl.iLeafPgno, lvl.iRowid - ); - } -+ }else if( bTomb ){ -+ u32 nElem = fts5GetU32(&a[4]); -+ int szKey = (aBlob[0]==4 || aBlob[0]==8) ? aBlob[0] : 8; -+ int nSlot = (n - 8) / szKey; -+ int ii; -+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " nElem=%d", (int)nElem); -+ if( aBlob[1] ){ -+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " 0"); -+ } -+ for(ii=0; iiestimatedCost = (double)100; -+ pIdxInfo->estimatedRows = 100; -+ pIdxInfo->idxNum = 0; -+ for(i=0, p=pIdxInfo->aConstraint; inConstraint; i++, p++){ -+ if( p->usable==0 ) continue; -+ if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==11 ){ -+ rc = SQLITE_OK; -+ pIdxInfo->aConstraintUsage[i].omit = 1; -+ pIdxInfo->aConstraintUsage[i].argvIndex = 1; -+ break; -+ } -+ } -+ return rc; -+} -+ -+/* -+** This method is the destructor for bytecodevtab objects. -+*/ -+static int fts5structDisconnectMethod(sqlite3_vtab *pVtab){ -+ Fts5StructVtab *p = (Fts5StructVtab*)pVtab; -+ sqlite3_free(p); -+ return SQLITE_OK; -+} -+ -+/* -+** Constructor for a new bytecodevtab_cursor object. -+*/ -+static int fts5structOpenMethod(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){ -+ int rc = SQLITE_OK; -+ Fts5StructVcsr *pNew = 0; -+ -+ pNew = sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); -+ *ppCsr = (sqlite3_vtab_cursor*)pNew; -+ -+ return SQLITE_OK; -+} -+ -+/* -+** Destructor for a bytecodevtab_cursor. -+*/ -+static int fts5structCloseMethod(sqlite3_vtab_cursor *cur){ -+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; -+ fts5StructureRelease(pCsr->pStruct); -+ sqlite3_free(pCsr); -+ return SQLITE_OK; -+} -+ -+ -+/* -+** Advance a bytecodevtab_cursor to its next row of output. -+*/ -+static int fts5structNextMethod(sqlite3_vtab_cursor *cur){ -+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; -+ Fts5Structure *p = pCsr->pStruct; -+ -+ assert( pCsr->pStruct ); -+ pCsr->iSeg++; -+ pCsr->iRowid++; -+ while( pCsr->iLevelnLevel && pCsr->iSeg>=p->aLevel[pCsr->iLevel].nSeg ){ -+ pCsr->iLevel++; -+ pCsr->iSeg = 0; -+ } -+ if( pCsr->iLevel>=p->nLevel ){ -+ fts5StructureRelease(pCsr->pStruct); -+ pCsr->pStruct = 0; -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Return TRUE if the cursor has been moved off of the last -+** row of output. -+*/ -+static int fts5structEofMethod(sqlite3_vtab_cursor *cur){ -+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; -+ return pCsr->pStruct==0; -+} -+ -+static int fts5structRowidMethod( -+ sqlite3_vtab_cursor *cur, -+ sqlite_int64 *piRowid -+){ -+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; -+ *piRowid = pCsr->iRowid; -+ return SQLITE_OK; -+} -+ -+/* -+** Return values of columns for the row at which the bytecodevtab_cursor -+** is currently pointing. -+*/ -+static int fts5structColumnMethod( -+ sqlite3_vtab_cursor *cur, /* The cursor */ -+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ -+ int i /* Which column to return */ -+){ -+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur; -+ Fts5Structure *p = pCsr->pStruct; -+ Fts5StructureSegment *pSeg = &p->aLevel[pCsr->iLevel].aSeg[pCsr->iSeg]; -+ -+ switch( i ){ -+ case 0: /* level */ -+ sqlite3_result_int(ctx, pCsr->iLevel); -+ break; -+ case 1: /* segment */ -+ sqlite3_result_int(ctx, pCsr->iSeg); -+ break; -+ case 2: /* merge */ -+ sqlite3_result_int(ctx, pCsr->iSeg < p->aLevel[pCsr->iLevel].nMerge); -+ break; -+ case 3: /* segid */ -+ sqlite3_result_int(ctx, pSeg->iSegid); -+ break; -+ case 4: /* leaf1 */ -+ sqlite3_result_int(ctx, pSeg->pgnoFirst); -+ break; -+ case 5: /* leaf2 */ -+ sqlite3_result_int(ctx, pSeg->pgnoLast); -+ break; -+ case 6: /* origin1 */ -+ sqlite3_result_int64(ctx, pSeg->iOrigin1); -+ break; -+ case 7: /* origin2 */ -+ sqlite3_result_int64(ctx, pSeg->iOrigin2); -+ break; -+ case 8: /* npgtombstone */ -+ sqlite3_result_int(ctx, pSeg->nPgTombstone); -+ break; -+ case 9: /* nentrytombstone */ -+ sqlite3_result_int64(ctx, pSeg->nEntryTombstone); -+ break; -+ case 10: /* nentry */ -+ sqlite3_result_int64(ctx, pSeg->nEntry); -+ break; -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Initialize a cursor. -+** -+** idxNum==0 means show all subprograms -+** idxNum==1 means show only the main bytecode and omit subprograms. -+*/ -+static int fts5structFilterMethod( -+ sqlite3_vtab_cursor *pVtabCursor, -+ int idxNum, const char *idxStr, -+ int argc, sqlite3_value **argv -+){ -+ Fts5StructVcsr *pCsr = (Fts5StructVcsr *)pVtabCursor; -+ int rc = SQLITE_OK; -+ -+ const u8 *aBlob = 0; -+ int nBlob = 0; -+ -+ assert( argc==1 ); -+ fts5StructureRelease(pCsr->pStruct); -+ pCsr->pStruct = 0; -+ -+ nBlob = sqlite3_value_bytes(argv[0]); -+ aBlob = (const u8*)sqlite3_value_blob(argv[0]); -+ rc = fts5StructureDecode(aBlob, nBlob, 0, &pCsr->pStruct); -+ if( rc==SQLITE_OK ){ -+ pCsr->iLevel = 0; -+ pCsr->iRowid = 0; -+ pCsr->iSeg = -1; -+ rc = fts5structNextMethod(pVtabCursor); -+ } -+ -+ return rc; -+} -+ -+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ - - /* - ** This is called as part of registering the FTS5 module with database -@@ -222953,6 +253663,7 @@ static void fts5RowidFunction( - ** SQLite error code is returned instead. - */ - static int sqlite3Fts5IndexInit(sqlite3 *db){ -+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) - int rc = sqlite3_create_function( - db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0 - ); -@@ -222969,7 +253680,42 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){ - db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0 - ); - } -+ -+ if( rc==SQLITE_OK ){ -+ static const sqlite3_module fts5structure_module = { -+ 0, /* iVersion */ -+ 0, /* xCreate */ -+ fts5structConnectMethod, /* xConnect */ -+ fts5structBestIndexMethod, /* xBestIndex */ -+ fts5structDisconnectMethod, /* xDisconnect */ -+ 0, /* xDestroy */ -+ fts5structOpenMethod, /* xOpen */ -+ fts5structCloseMethod, /* xClose */ -+ fts5structFilterMethod, /* xFilter */ -+ fts5structNextMethod, /* xNext */ -+ fts5structEofMethod, /* xEof */ -+ fts5structColumnMethod, /* xColumn */ -+ fts5structRowidMethod, /* xRowid */ -+ 0, /* xUpdate */ -+ 0, /* xBegin */ -+ 0, /* xSync */ -+ 0, /* xCommit */ -+ 0, /* xRollback */ -+ 0, /* xFindFunction */ -+ 0, /* xRename */ -+ 0, /* xSavepoint */ -+ 0, /* xRelease */ -+ 0, /* xRollbackTo */ -+ 0, /* xShadowName */ -+ 0 /* xIntegrity */ -+ }; -+ rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0); -+ } - return rc; -+#else -+ return SQLITE_OK; -+ UNUSED_PARAM(db); -+#endif - } - - -@@ -223005,7 +253751,9 @@ static int sqlite3Fts5IndexReset(Fts5Index *p){ - ** assert() conditions in the fts5 code are activated - conditions that are - ** only true if it is guaranteed that the fts5 database is not corrupt. - */ -+#ifdef SQLITE_DEBUG - SQLITE_API int sqlite3_fts5_may_be_corrupt = 1; -+#endif - - - typedef struct Fts5Auxdata Fts5Auxdata; -@@ -223064,8 +253812,18 @@ struct Fts5Global { - Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ - Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ - Fts5Cursor *pCsr; /* First in list of all open cursors */ -+ u32 aLocaleHdr[4]; - }; - -+/* -+** Size of header on fts5_locale() values. And macro to access a buffer -+** containing a copy of the header from an Fts5Config pointer. -+*/ -+#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) -+#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr)) -+ -+#define FTS5_INSTTOKEN_SUBTYPE 73 -+ - /* - ** Each auxiliary function registered with the FTS5 module is represented - ** by an object of the following type. All such objects are stored as part -@@ -223084,11 +253842,28 @@ struct Fts5Auxiliary { - ** Each tokenizer module registered with the FTS5 module is represented - ** by an object of the following type. All such objects are stored as part - ** of the Fts5Global.pTok list. -+** -+** bV2Native: -+** True if the tokenizer was registered using xCreateTokenizer_v2(), false -+** for xCreateTokenizer(). If this variable is true, then x2 is populated -+** with the routines as supplied by the caller and x1 contains synthesized -+** wrapper routines. In this case the user-data pointer passed to -+** x1.xCreate should be a pointer to the Fts5TokenizerModule structure, -+** not a copy of pUserData. -+** -+** Of course, if bV2Native is false, then x1 contains the real routines and -+** x2 the synthesized ones. In this case a pointer to the Fts5TokenizerModule -+** object should be passed to x2.xCreate. -+** -+** The synthesized wrapper routines are necessary for xFindTokenizer(_v2) -+** calls. - */ - struct Fts5TokenizerModule { - char *zName; /* Name of tokenizer */ - void *pUserData; /* User pointer passed to xCreate() */ -- fts5_tokenizer x; /* Tokenizer functions */ -+ int bV2Native; /* True if v2 native tokenizer */ -+ fts5_tokenizer x1; /* Tokenizer functions */ -+ fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ - void (*xDestroy)(void*); /* Destructor function */ - Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ - }; -@@ -223098,6 +253873,8 @@ struct Fts5FullTable { - Fts5Storage *pStorage; /* Document store */ - Fts5Global *pGlobal; /* Global (connection wide) data */ - Fts5Cursor *pSortCsr; /* Sort data from this cursor */ -+ int iSavepoint; /* Successful xSavepoint()+1 */ -+ - #ifdef SQLITE_DEBUG - struct Fts5TransactionState ts; - #endif -@@ -223122,9 +253899,11 @@ struct Fts5Sorter { - i64 iRowid; /* Current rowid */ - const u8 *aPoslist; /* Position lists for current row */ - int nIdx; /* Number of entries in aIdx[] */ -- int aIdx[1]; /* Offsets into aPoslist for current row */ -+ int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */ - }; - -+/* Size (int bytes) of an Fts5Sorter object with N indexes */ -+#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64)) - - /* - ** Virtual-table cursor object. -@@ -223174,7 +253953,7 @@ struct Fts5Cursor { - Fts5Auxiliary *pAux; /* Currently executing extension function */ - Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ - -- /* Cache used by auxiliary functions xInst() and xInstCount() */ -+ /* Cache used by auxiliary API functions xInst() and xInstCount() */ - Fts5PoslistReader *aInstIter; /* One for each phrase */ - int nInstAlloc; /* Size of aInst[] array (entries / 3) */ - int nInstCount; /* Number of phrase instances */ -@@ -223241,7 +254020,7 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ - break; - - case FTS5_SYNC: -- assert( p->ts.eState==1 ); -+ assert( p->ts.eState==1 || p->ts.eState==2 ); - p->ts.eState = 2; - break; - -@@ -223256,21 +254035,21 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ - break; - - case FTS5_SAVEPOINT: -- assert( p->ts.eState==1 ); -+ assert( p->ts.eState>=1 ); - assert( iSavepoint>=0 ); - assert( iSavepoint>=p->ts.iSavepoint ); - p->ts.iSavepoint = iSavepoint; - break; - - case FTS5_RELEASE: -- assert( p->ts.eState==1 ); -+ assert( p->ts.eState>=1 ); - assert( iSavepoint>=0 ); - assert( iSavepoint<=p->ts.iSavepoint ); - p->ts.iSavepoint = iSavepoint-1; - break; - - case FTS5_ROLLBACKTO: -- assert( p->ts.eState==1 ); -+ assert( p->ts.eState>=1 ); - assert( iSavepoint>=-1 ); - /* The following assert() can fail if another vtab strikes an error - ** within an xSavepoint() call then SQLite calls xRollbackTo() - without -@@ -223285,10 +254064,16 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ - #endif - - /* --** Return true if pTab is a contentless table. -+** Return true if pTab is a contentless table. If parameter bIncludeUnindexed -+** is true, this includes contentless tables that store UNINDEXED columns -+** only. - */ --static int fts5IsContentless(Fts5FullTable *pTab){ -- return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE; -+static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){ -+ int eContent = pTab->p.pConfig->eContent; -+ return ( -+ eContent==FTS5_CONTENT_NONE -+ || (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED) -+ ); - } - - /* -@@ -223356,8 +254141,12 @@ static int fts5InitVtab( - assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 ); - } - if( rc==SQLITE_OK ){ -+ pConfig->pzErrmsg = pzErr; - pTab->p.pConfig = pConfig; - pTab->pGlobal = pGlobal; -+ if( bCreate || sqlite3Fts5TokenizerPreload(&pConfig->t) ){ -+ rc = sqlite3Fts5LoadTokenizer(pConfig); -+ } - } - - /* Open the index sub-system */ -@@ -223379,13 +254168,17 @@ static int fts5InitVtab( - - /* Load the initial configuration */ - if( rc==SQLITE_OK ){ -- assert( pConfig->pzErrmsg==0 ); -- pConfig->pzErrmsg = pzErr; -- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); -- sqlite3Fts5IndexRollback(pTab->p.pIndex); -- pConfig->pzErrmsg = 0; -+ rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie-1); -+ } -+ -+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ -+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1); -+ } -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); - } - -+ if( pConfig ) pConfig->pzErrmsg = 0; - if( rc!=SQLITE_OK ){ - fts5FreeVtab(pTab); - pTab = 0; -@@ -223447,6 +254240,23 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ - #endif - } - -+static int fts5UsePatternMatch( -+ Fts5Config *pConfig, -+ struct sqlite3_index_constraint *p -+){ -+ assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB ); -+ assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE ); -+ if( pConfig->t.ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){ -+ return 1; -+ } -+ if( pConfig->t.ePattern==FTS5_PATTERN_LIKE -+ && (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB) -+ ){ -+ return 1; -+ } -+ return 0; -+} -+ - /* - ** Implementation of the xBestIndex method for FTS5 tables. Within the - ** WHERE constraint, it searches for the following: -@@ -223476,7 +254286,9 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ - ** - ** Match against table column: "m" - ** Match against rank column: "r" --** Match against other column: "" -+** Match against other column: "M" -+** LIKE against other column: "L" -+** GLOB against other column: "G" - ** Equality constraint against the rowid: "=" - ** A < or <= against the rowid: "<" - ** A > or >= against the rowid: ">" -@@ -223484,10 +254296,10 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ - ** This function ensures that there is at most one "r" or "=". And that if - ** there exists an "=" then there is no "<" or ">". - ** --** Costs are assigned as follows: -+** If an unusable MATCH operator is present in the WHERE clause, then -+** SQLITE_CONSTRAINT is returned. - ** --** a) If an unusable MATCH operator is present in the WHERE clause, the --** cost is unconditionally set to 1e50 (a really big number). -+** Costs are assigned as follows: - ** - ** a) If a MATCH operator is present, the cost depends on the other - ** constraints also present. As follows: -@@ -223520,7 +254332,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - int bSeenEq = 0; - int bSeenGt = 0; - int bSeenLt = 0; -- int bSeenMatch = 0; -+ int nSeenMatch = 0; - int bSeenRank = 0; - - -@@ -223537,7 +254349,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - return SQLITE_ERROR; - } - -- idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1); -+ idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1); - if( idxStr==0 ) return SQLITE_NOMEM; - pInfo->idxStr = idxStr; - pInfo->needToFreeIdxStr = 1; -@@ -223551,35 +254363,38 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - /* A MATCH operator or equivalent */ - if( p->usable==0 || iCol<0 ){ - /* As there exists an unusable MATCH constraint this is an -- ** unusable plan. Set a prohibitively high cost. */ -- pInfo->estimatedCost = 1e50; -- assert( iIdxStr < pInfo->nConstraint*6 + 1 ); -+ ** unusable plan. Return SQLITE_CONSTRAINT. */ - idxStr[iIdxStr] = 0; -- return SQLITE_OK; -+ return SQLITE_CONSTRAINT; - }else{ - if( iCol==nCol+1 ){ - if( bSeenRank ) continue; - idxStr[iIdxStr++] = 'r'; - bSeenRank = 1; - }else{ -- bSeenMatch = 1; -- idxStr[iIdxStr++] = 'm'; -- if( iColaConstraintUsage[i].argvIndex = ++iCons; - pInfo->aConstraintUsage[i].omit = 1; - } -- } -- else if( p->usable && bSeenEq==0 -- && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 -- ){ -- idxStr[iIdxStr++] = '='; -- bSeenEq = 1; -- pInfo->aConstraintUsage[i].argvIndex = ++iCons; -+ }else if( p->usable ){ -+ if( iCol>=0 && iColop==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB ); -+ idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G'; -+ sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol); -+ idxStr += strlen(&idxStr[iIdxStr]); -+ pInfo->aConstraintUsage[i].argvIndex = ++iCons; -+ assert( idxStr[iIdxStr]=='\0' ); -+ nSeenMatch++; -+ }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){ -+ idxStr[iIdxStr++] = '='; -+ bSeenEq = 1; -+ pInfo->aConstraintUsage[i].argvIndex = ++iCons; -+ } - } - } - -@@ -223605,12 +254420,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - } - idxStr[iIdxStr] = '\0'; - -- /* Set idxFlags flags for the ORDER BY clause */ -+ /* Set idxFlags flags for the ORDER BY clause -+ ** -+ ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC". -+ */ - if( pInfo->nOrderBy==1 ){ - int iSort = pInfo->aOrderBy[0].iColumn; -- if( iSort==(pConfig->nCol+1) && bSeenMatch ){ -+ if( iSort==(pConfig->nCol+1) && nSeenMatch>0 ){ - idxFlags |= FTS5_BI_ORDER_RANK; -- }else if( iSort==-1 ){ -+ }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){ - idxFlags |= FTS5_BI_ORDER_ROWID; - } - if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){ -@@ -223623,14 +254441,17 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - - /* Calculate the estimated cost based on the flags set in idxFlags. */ - if( bSeenEq ){ -- pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0; -- if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo); -+ pInfo->estimatedCost = nSeenMatch ? 1000.0 : 10.0; -+ if( nSeenMatch==0 ) fts5SetUniqueFlag(pInfo); - }else if( bSeenLt && bSeenGt ){ -- pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0; -+ pInfo->estimatedCost = nSeenMatch ? 5000.0 : 250000.0; - }else if( bSeenLt || bSeenGt ){ -- pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0; -+ pInfo->estimatedCost = nSeenMatch ? 7500.0 : 750000.0; - }else{ -- pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0; -+ pInfo->estimatedCost = nSeenMatch ? 10000.0 : 1000000.0; -+ } -+ for(i=1; iestimatedCost *= 0.4; - } - - pInfo->idxNum = idxFlags; -@@ -223762,7 +254583,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){ - rc = sqlite3_step(pSorter->pStmt); - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; -- CsrFlagSet(pCsr, FTS5CSR_EOF); -+ CsrFlagSet(pCsr, FTS5CSR_EOF|FTS5CSR_REQUIRE_CONTENT); - }else if( rc==SQLITE_ROW ){ - const u8 *a; - const u8 *aBlob; -@@ -223862,6 +254683,16 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ - ); - assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) ); - -+ /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table, -+ ** clear any token mappings accumulated at the fts5_index.c level. In -+ ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH, -+ ** we need to retain the mappings for the entire query. */ -+ if( pCsr->ePlan==FTS5_PLAN_MATCH -+ && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata -+ ){ -+ sqlite3Fts5ExprClearTokens(pCsr->pExpr); -+ } -+ - if( pCsr->ePlan<3 ){ - int bSkip = 0; - if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; -@@ -223896,6 +254727,7 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ - } - }else{ - rc = SQLITE_OK; -+ CsrFlagSet(pCsr, FTS5CSR_REQUIRE_DOCSIZE); - } - break; - } -@@ -223925,7 +254757,7 @@ static int fts5PrepareStatement( - rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, - SQLITE_PREPARE_PERSISTENT, &pRet, 0); - if( rc!=SQLITE_OK ){ -- *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); -+ sqlite3Fts5ConfigErrmsg(pConfig, "%s", sqlite3_errmsg(pConfig->db)); - } - sqlite3_free(zSql); - } -@@ -223949,7 +254781,7 @@ static int fts5CursorFirstSorted( - const char *zRankArgs = pCsr->zRankArgs; - - nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); -- nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); -+ nByte = SZ_FTS5SORTER(nPhrase); - pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); - if( pSorter==0 ) return SQLITE_NOMEM; - memset(pSorter, 0, (size_t)nByte); -@@ -224149,6 +254981,145 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){ - return iDefault; - } - -+/* -+** Set the error message on the virtual table passed as the first argument. -+*/ -+static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ -+ va_list ap; /* ... printf arguments */ -+ va_start(ap, zFormat); -+ sqlite3_free(p->p.base.zErrMsg); -+ p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); -+ va_end(ap); -+} -+ -+/* -+** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale -+** specified by pLocale/nLocale. The buffer indicated by pLocale must remain -+** valid until after the final call to sqlite3Fts5Tokenize() that will use -+** the locale. -+*/ -+static void sqlite3Fts5SetLocale( -+ Fts5Config *pConfig, -+ const char *zLocale, -+ int nLocale -+){ -+ Fts5TokenizerConfig *pT = &pConfig->t; -+ pT->pLocale = zLocale; -+ pT->nLocale = nLocale; -+} -+ -+/* -+** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale(). -+*/ -+static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ -+ sqlite3Fts5SetLocale(pConfig, 0, 0); -+} -+ -+/* -+** Return true if the value passed as the only argument is an -+** fts5_locale() value. -+*/ -+static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){ -+ int ret = 0; -+ if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ -+ /* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case. -+ ** If the blob was created using zeroblob(), then sqlite3_value_blob() -+ ** may call malloc(). If this malloc() fails, then the values returned -+ ** by both value_blob() and value_bytes() will be 0. If value_bytes() were -+ ** called first, then the NULL pointer returned by value_blob() might -+ ** be dereferenced. */ -+ const u8 *pBlob = sqlite3_value_blob(pVal); -+ int nBlob = sqlite3_value_bytes(pVal); -+ if( nBlob>FTS5_LOCALE_HDR_SIZE -+ && 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig), FTS5_LOCALE_HDR_SIZE) -+ ){ -+ ret = 1; -+ } -+ } -+ return ret; -+} -+ -+/* -+** Value pVal is guaranteed to be an fts5_locale() value, according to -+** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale -+** from the value and returns them separately. -+** -+** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set -+** to point to buffers containing the text and locale, as utf-8, -+** respectively. In this case output parameters (*pnText) and (*pnLoc) are -+** set to the sizes in bytes of these two buffers. -+** -+** Or, if an error occurs, then an SQLite error code is returned. The final -+** value of the four output parameters is undefined in this case. -+*/ -+static int sqlite3Fts5DecodeLocaleValue( -+ sqlite3_value *pVal, -+ const char **ppText, -+ int *pnText, -+ const char **ppLoc, -+ int *pnLoc -+){ -+ const char *p = sqlite3_value_blob(pVal); -+ int n = sqlite3_value_bytes(pVal); -+ int nLoc = 0; -+ -+ assert( sqlite3_value_type(pVal)==SQLITE_BLOB ); -+ assert( n>FTS5_LOCALE_HDR_SIZE ); -+ -+ for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){ -+ if( nLoc==(n-1) ){ -+ return SQLITE_MISMATCH; -+ } -+ } -+ *ppLoc = &p[FTS5_LOCALE_HDR_SIZE]; -+ *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE; -+ -+ *ppText = &p[nLoc+1]; -+ *pnText = n - nLoc - 1; -+ return SQLITE_OK; -+} -+ -+/* -+** Argument pVal is the text of a full-text search expression. It may or -+** may not have been wrapped by fts5_locale(). This function extracts -+** the text of the expression, and sets output variable (*pzText) to -+** point to a nul-terminated buffer containing the expression. -+** -+** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called -+** to set the tokenizer to use the specified locale. -+** -+** If output variable (*pbFreeAndReset) is set to true, then the caller -+** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer -+** locale, and (b) call sqlite3_free() to free (*pzText). -+*/ -+static int fts5ExtractExprText( -+ Fts5Config *pConfig, /* Fts5 configuration */ -+ sqlite3_value *pVal, /* Value to extract expression text from */ -+ char **pzText, /* OUT: nul-terminated buffer of text */ -+ int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ -+){ -+ int rc = SQLITE_OK; -+ -+ if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ -+ const char *pText = 0; -+ int nText = 0; -+ const char *pLoc = 0; -+ int nLoc = 0; -+ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); -+ *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText); -+ if( rc==SQLITE_OK ){ -+ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); -+ } -+ *pbFreeAndReset = 1; -+ }else{ -+ *pzText = (char*)sqlite3_value_text(pVal); -+ *pbFreeAndReset = 0; -+ } -+ -+ return rc; -+} -+ -+ - /* - ** This is the xFilter interface for the virtual table. See - ** the virtual table xFilter method documentation for additional -@@ -224179,17 +255150,12 @@ static int fts5FilterMethod( - sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ - int iCol; /* Column on LHS of MATCH operator */ - char **pzErrmsg = pConfig->pzErrmsg; -+ int bPrefixInsttoken = pConfig->bPrefixInsttoken; - int i; - int iIdxStr = 0; - Fts5Expr *pExpr = 0; - -- if( pConfig->bLock ){ -- pTab->p.base.zErrMsg = sqlite3_mprintf( -- "recursively defined fts5 content table" -- ); -- return SQLITE_ERROR; -- } -- -+ assert( pConfig->bLock==0 ); - if( pCsr->ePlan ){ - fts5FreeCursorComponents(pCsr); - memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); -@@ -224212,36 +255178,65 @@ static int fts5FilterMethod( - case 'r': - pRank = apVal[i]; - break; -- case 'm': { -- const char *zText = (const char*)sqlite3_value_text(apVal[i]); -- if( zText==0 ) zText = ""; -+ case 'M': { -+ char *zText = 0; -+ int bFreeAndReset = 0; -+ int bInternal = 0; - -- if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){ -- iCol = 0; -- do{ -- iCol = iCol*10 + (idxStr[iIdxStr]-'0'); -- iIdxStr++; -- }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); -- }else{ -- iCol = pConfig->nCol; -+ rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); -+ if( rc!=SQLITE_OK ) goto filter_out; -+ if( zText==0 ) zText = ""; -+ if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){ -+ pConfig->bPrefixInsttoken = 1; - } - -+ iCol = 0; -+ do{ -+ iCol = iCol*10 + (idxStr[iIdxStr]-'0'); -+ iIdxStr++; -+ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); -+ - if( zText[0]=='*' ){ - /* The user has issued a query of the form "MATCH '*...'". This - ** indicates that the MATCH expression is not a full text query, - ** but a request for an internal parameter. */ - rc = fts5SpecialMatch(pTab, pCsr, &zText[1]); -- goto filter_out; -+ bInternal = 1; - }else{ - char **pzErr = &pTab->p.base.zErrMsg; -- rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr); -+ rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); - pExpr = 0; - } -- if( rc!=SQLITE_OK ) goto filter_out; - } - -+ if( bFreeAndReset ){ -+ sqlite3_free(zText); -+ sqlite3Fts5ClearLocale(pConfig); -+ } -+ -+ if( bInternal || rc!=SQLITE_OK ) goto filter_out; -+ -+ break; -+ } -+ case 'L': -+ case 'G': { -+ int bGlob = (idxStr[iIdxStr-1]=='G'); -+ const char *zText = (const char*)sqlite3_value_text(apVal[i]); -+ iCol = 0; -+ do{ -+ iCol = iCol*10 + (idxStr[iIdxStr]-'0'); -+ iIdxStr++; -+ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); -+ if( zText ){ -+ rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr); -+ } -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); -+ pExpr = 0; -+ } -+ if( rc!=SQLITE_OK ) goto filter_out; - break; - } - case '=': -@@ -224273,6 +255268,9 @@ static int fts5FilterMethod( - pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64); - } - -+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); -+ if( rc!=SQLITE_OK ) goto filter_out; -+ - if( pTab->pSortCsr ){ - /* If pSortCsr is non-NULL, then this call is being made as part of - ** processing for a "... MATCH ORDER BY rank" query (ePlan is -@@ -224295,6 +255293,7 @@ static int fts5FilterMethod( - pCsr->pExpr = pTab->pSortCsr->pExpr; - rc = fts5CursorFirst(pTab, pCsr, bDesc); - }else if( pCsr->pExpr ){ -+ assert( rc==SQLITE_OK ); - rc = fts5CursorParseRank(pConfig, pCsr, pRank); - if( rc==SQLITE_OK ){ - if( bOrderByRank ){ -@@ -224306,9 +255305,7 @@ static int fts5FilterMethod( - } - } - }else if( pConfig->zContent==0 ){ -- *pConfig->pzErrmsg = sqlite3_mprintf( -- "%s: table does not support scanning", pConfig->zName -- ); -+ fts5SetVtabError(pTab,"%s: table does not support scanning",pConfig->zName); - rc = SQLITE_ERROR; - }else{ - /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup -@@ -224318,7 +255315,8 @@ static int fts5FilterMethod( - pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg - ); - if( rc==SQLITE_OK ){ -- if( pCsr->ePlan==FTS5_PLAN_ROWID ){ -+ if( pRowidEq!=0 ){ -+ assert( pCsr->ePlan==FTS5_PLAN_ROWID ); - sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq); - }else{ - sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid); -@@ -224331,6 +255329,7 @@ static int fts5FilterMethod( - filter_out: - sqlite3Fts5ExprFree(pExpr); - pConfig->pzErrmsg = pzErrmsg; -+ pConfig->bPrefixInsttoken = bPrefixInsttoken; - return rc; - } - -@@ -224350,9 +255349,13 @@ static i64 fts5CursorRowid(Fts5Cursor *pCsr){ - assert( pCsr->ePlan==FTS5_PLAN_MATCH - || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH - || pCsr->ePlan==FTS5_PLAN_SOURCE -+ || pCsr->ePlan==FTS5_PLAN_SCAN -+ || pCsr->ePlan==FTS5_PLAN_ROWID - ); - if( pCsr->pSorter ){ - return pCsr->pSorter->iRowid; -+ }else if( pCsr->ePlan>=FTS5_PLAN_SCAN ){ -+ return sqlite3_column_int64(pCsr->pStmt, 0); - }else{ - return sqlite3Fts5ExprRowid(pCsr->pExpr); - } -@@ -224369,25 +255372,16 @@ static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - int ePlan = pCsr->ePlan; - - assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); -- switch( ePlan ){ -- case FTS5_PLAN_SPECIAL: -- *pRowid = 0; -- break; -- -- case FTS5_PLAN_SOURCE: -- case FTS5_PLAN_MATCH: -- case FTS5_PLAN_SORTED_MATCH: -- *pRowid = fts5CursorRowid(pCsr); -- break; -- -- default: -- *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); -- break; -+ if( ePlan==FTS5_PLAN_SPECIAL ){ -+ *pRowid = 0; -+ }else{ -+ *pRowid = fts5CursorRowid(pCsr); - } - - return SQLITE_OK; - } - -+ - /* - ** If the cursor requires seeking (bSeekRequired flag is set), seek it. - ** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise. -@@ -224424,8 +255418,13 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ - rc = sqlite3_reset(pCsr->pStmt); - if( rc==SQLITE_OK ){ - rc = FTS5_CORRUPT; -+ fts5SetVtabError((Fts5FullTable*)pTab, -+ "fts5: missing row %lld from content table %s", -+ fts5CursorRowid(pCsr), -+ pTab->pConfig->zContent -+ ); - }else if( pTab->pConfig->pzErrmsg ){ -- *pTab->pConfig->pzErrmsg = sqlite3_mprintf( -+ fts5SetVtabError((Fts5FullTable*)pTab, - "%s", sqlite3_errmsg(pTab->pConfig->db) - ); - } -@@ -224434,14 +255433,6 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ - return rc; - } - --static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ -- va_list ap; /* ... printf arguments */ -- va_start(ap, zFormat); -- assert( p->p.base.zErrMsg==0 ); -- p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); -- va_end(ap); --} -- - /* - ** This function is called to handle an FTS INSERT command. In other words, - ** an INSERT statement of the form: -@@ -224465,6 +255456,7 @@ static int fts5SpecialInsert( - Fts5Config *pConfig = pTab->p.pConfig; - int rc = SQLITE_OK; - int bError = 0; -+ int bLoadConfig = 0; - - if( 0==sqlite3_stricmp("delete-all", zCmd) ){ - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ -@@ -224476,8 +255468,9 @@ static int fts5SpecialInsert( - }else{ - rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); - } -+ bLoadConfig = 1; - }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ -- if( pConfig->eContent==FTS5_CONTENT_NONE ){ -+ if( fts5IsContentless(pTab, 1) ){ - fts5SetVtabError(pTab, - "'rebuild' may not be used with a contentless fts5 table" - ); -@@ -224485,19 +255478,26 @@ static int fts5SpecialInsert( - }else{ - rc = sqlite3Fts5StorageRebuild(pTab->pStorage); - } -+ bLoadConfig = 1; - }else if( 0==sqlite3_stricmp("optimize", zCmd) ){ - rc = sqlite3Fts5StorageOptimize(pTab->pStorage); - }else if( 0==sqlite3_stricmp("merge", zCmd) ){ - int nMerge = sqlite3_value_int(pVal); - rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge); - }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){ -- rc = sqlite3Fts5StorageIntegrity(pTab->pStorage); -+ int iArg = sqlite3_value_int(pVal); -+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, iArg); - #ifdef SQLITE_DEBUG - }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){ - pConfig->bPrefixIndex = sqlite3_value_int(pVal); - #endif -+ }else if( 0==sqlite3_stricmp("flush", zCmd) ){ -+ rc = sqlite3Fts5FlushToDisk(&pTab->p); - }else{ -- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); -+ rc = sqlite3Fts5FlushToDisk(&pTab->p); -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); -+ } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError); - } -@@ -224509,6 +255509,12 @@ static int fts5SpecialInsert( - } - } - } -+ -+ if( rc==SQLITE_OK && bLoadConfig ){ -+ pTab->p.pConfig->iCookie--; -+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); -+ } -+ - return rc; - } - -@@ -224520,7 +255526,7 @@ static int fts5SpecialDelete( - int eType1 = sqlite3_value_type(apVal[1]); - if( eType1==SQLITE_INTEGER ){ - sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); -- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]); -+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2], 0); - } - return rc; - } -@@ -224533,7 +255539,7 @@ static void fts5StorageInsert( - ){ - int rc = *pRc; - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid); -+ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid); - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); -@@ -224541,6 +255547,67 @@ static void fts5StorageInsert( - *pRc = rc; - } - -+/* -+** -+** This function is called when the user attempts an UPDATE on a contentless -+** table. Parameter bRowidModified is true if the UPDATE statement modifies -+** the rowid value. Parameter apVal[] contains the new values for each user -+** defined column of the fts5 table. pConfig is the configuration object of the -+** table being updated (guaranteed to be contentless). The contentless_delete=1 -+** and contentless_unindexed=1 options may or may not be set. -+** -+** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite -+** error code if it cannot. In this case an error message is also loaded into -+** pConfig. Output parameter (*pbContent) is set to true if the caller should -+** update the %_content table only - not the FTS index or any other shadow -+** table. This occurs when an UPDATE modifies only UNINDEXED columns of the -+** table. -+** -+** An UPDATE may proceed if: -+** -+** * The only columns modified are UNINDEXED columns, or -+** -+** * The contentless_delete=1 option was specified and all of the indexed -+** columns (not a subset) have been modified. -+*/ -+static int fts5ContentlessUpdate( -+ Fts5Config *pConfig, -+ sqlite3_value **apVal, -+ int bRowidModified, -+ int *pbContent -+){ -+ int ii; -+ int bSeenIndex = 0; /* Have seen modified indexed column */ -+ int bSeenIndexNC = 0; /* Have seen unmodified indexed column */ -+ int rc = SQLITE_OK; -+ -+ for(ii=0; iinCol; ii++){ -+ if( pConfig->abUnindexed[ii]==0 ){ -+ if( sqlite3_value_nochange(apVal[ii]) ){ -+ bSeenIndexNC++; -+ }else{ -+ bSeenIndex++; -+ } -+ } -+ } -+ -+ if( bSeenIndex==0 && bRowidModified==0 ){ -+ *pbContent = 1; -+ }else{ -+ if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){ -+ rc = SQLITE_ERROR; -+ sqlite3Fts5ConfigErrmsg(pConfig, -+ (pConfig->bContentlessDelete ? -+ "%s a subset of columns on fts5 contentless-delete table: %s" : -+ "%s contentless fts5 table: %s") -+ , "cannot UPDATE", pConfig->zName -+ ); -+ } -+ } -+ -+ return rc; -+} -+ - /* - ** This function is the implementation of the xUpdate callback used by - ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be -@@ -224567,7 +255634,7 @@ static int fts5UpdateMethod( - int rc = SQLITE_OK; /* Return code */ - - /* A transaction must be open when this is called. */ -- assert( pTab->ts.eState==1 ); -+ assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); - - assert( pVtab->zErrMsg==0 ); - assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); -@@ -224575,6 +255642,11 @@ static int fts5UpdateMethod( - || sqlite3_value_type(apVal[0])==SQLITE_NULL - ); - assert( pTab->p.pConfig->pzErrmsg==0 ); -+ if( pConfig->pgsz==0 ){ -+ rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie); -+ if( rc!=SQLITE_OK ) return rc; -+ } -+ - pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; - - /* Put any active cursors into REQUIRE_SEEK state. */ -@@ -224589,7 +255661,14 @@ static int fts5UpdateMethod( - if( pConfig->eContent!=FTS5_CONTENT_NORMAL - && 0==sqlite3_stricmp("delete", z) - ){ -- rc = fts5SpecialDelete(pTab, apVal); -+ if( pConfig->bContentlessDelete ){ -+ fts5SetVtabError(pTab, -+ "'delete' may not be used with a contentless_delete=1 table" -+ ); -+ rc = SQLITE_ERROR; -+ }else{ -+ rc = fts5SpecialDelete(pTab, apVal); -+ } - }else{ - rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); - } -@@ -224606,74 +255685,111 @@ static int fts5UpdateMethod( - ** Cases 3 and 4 may violate the rowid constraint. - */ - int eConflict = SQLITE_ABORT; -- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ -+ if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){ - eConflict = sqlite3_vtab_on_conflict(pConfig->db); - } - - assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); - assert( nArg!=1 || eType0==SQLITE_INTEGER ); - -- /* Filter out attempts to run UPDATE or DELETE on contentless tables. -- ** This is not suported. */ -- if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){ -- pTab->p.base.zErrMsg = sqlite3_mprintf( -- "cannot %s contentless fts5 table: %s", -- (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName -- ); -- rc = SQLITE_ERROR; -- } -- - /* DELETE */ -- else if( nArg==1 ){ -- i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ -- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); -+ if( nArg==1 ){ -+ /* It is only possible to DELETE from a contentless table if the -+ ** contentless_delete=1 flag is set. */ -+ if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){ -+ fts5SetVtabError(pTab, -+ "cannot DELETE from contentless fts5 table: %s", pConfig->zName -+ ); -+ rc = SQLITE_ERROR; -+ }else{ -+ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ -+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); -+ } - } - - /* INSERT or UPDATE */ - else{ - int eType1 = sqlite3_value_numeric_type(apVal[1]); - -- if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){ -- rc = SQLITE_MISMATCH; -+ /* It is an error to write an fts5_locale() value to a table without -+ ** the locale=1 option. */ -+ if( pConfig->bLocale==0 ){ -+ int ii; -+ for(ii=0; iinCol; ii++){ -+ sqlite3_value *pVal = apVal[ii+2]; -+ if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ -+ fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); -+ rc = SQLITE_MISMATCH; -+ goto update_out; -+ } -+ } - } - -- else if( eType0!=SQLITE_INTEGER ){ -- /* If this is a REPLACE, first remove the current entry (if any) */ -+ if( eType0!=SQLITE_INTEGER ){ -+ /* An INSERT statement. If the conflict-mode is REPLACE, first remove -+ ** the current entry (if any). */ - if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ - i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ -- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); -+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); - } - fts5StorageInsert(&rc, pTab, apVal, pRowid); - } - - /* UPDATE */ - else{ -+ Fts5Storage *pStorage = pTab->pStorage; - i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ - i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ -- if( eType1==SQLITE_INTEGER && iOld!=iNew ){ -+ int bContent = 0; /* Content only update */ -+ -+ /* If this is a contentless table (including contentless_unindexed=1 -+ ** tables), check if the UPDATE may proceed. */ -+ if( fts5IsContentless(pTab, 1) ){ -+ rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent); -+ if( rc!=SQLITE_OK ) goto update_out; -+ } -+ -+ if( eType1!=SQLITE_INTEGER ){ -+ rc = SQLITE_MISMATCH; -+ }else if( iOld!=iNew ){ -+ assert( bContent==0 ); - if( eConflict==SQLITE_REPLACE ){ -- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); -+ rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); -+ rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0); - } - fts5StorageInsert(&rc, pTab, apVal, pRowid); - }else{ -- rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); -+ rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid); -+ } - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); -+ rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0); - } - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); -+ rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid); - } - } -+ }else if( bContent ){ -+ /* This occurs when an UPDATE on a contentless table affects *only* -+ ** UNINDEXED columns. This is a no-op for contentless_unindexed=0 -+ ** tables, or a write to the %_content table only for =1 tables. */ -+ assert( fts5IsContentless(pTab, 1) ); -+ rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid); -+ } - }else{ -- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); -+ rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); - fts5StorageInsert(&rc, pTab, apVal, pRowid); - } -+ sqlite3Fts5StorageReleaseDeleteRow(pStorage); - } - } - } - -+ update_out: - pTab->p.pConfig->pzErrmsg = 0; - return rc; - } -@@ -224686,8 +255802,7 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ - Fts5FullTable *pTab = (Fts5FullTable*)pVtab; - fts5CheckTransactionState(pTab, FTS5_SYNC, 0); - pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; -- fts5TripCursors(pTab); -- rc = sqlite3Fts5StorageSync(pTab->pStorage); -+ rc = sqlite3Fts5FlushToDisk(&pTab->p); - pTab->p.pConfig->pzErrmsg = 0; - return rc; - } -@@ -224696,9 +255811,11 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ - ** Implementation of xBegin() method. - */ - static int fts5BeginMethod(sqlite3_vtab *pVtab){ -- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); -- fts5NewTransaction((Fts5FullTable*)pVtab); -- return SQLITE_OK; -+ int rc = fts5NewTransaction((Fts5FullTable*)pVtab); -+ if( rc==SQLITE_OK ){ -+ fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); -+ } -+ return rc; - } - - /* -@@ -224721,6 +255838,7 @@ static int fts5RollbackMethod(sqlite3_vtab *pVtab){ - Fts5FullTable *pTab = (Fts5FullTable*)pVtab; - fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); - rc = sqlite3Fts5StorageRollback(pTab->pStorage); -+ pTab->p.pConfig->pgsz = 0; - return rc; - } - -@@ -224752,17 +255870,40 @@ static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ - return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); - } - --static int fts5ApiTokenize( -+/* -+** Implementation of xTokenize_v2() API. -+*/ -+static int fts5ApiTokenize_v2( - Fts5Context *pCtx, - const char *pText, int nText, -+ const char *pLoc, int nLoc, - void *pUserData, - int (*xToken)(void*, int, const char*, int, int, int) - ){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); -- return sqlite3Fts5Tokenize( -- pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken -+ int rc = SQLITE_OK; -+ -+ sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc); -+ rc = sqlite3Fts5Tokenize(pTab->pConfig, -+ FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken - ); -+ sqlite3Fts5SetLocale(pTab->pConfig, 0, 0); -+ -+ return rc; -+} -+ -+/* -+** Implementation of xTokenize() API. This is just xTokenize_v2() with NULL/0 -+** passed as the locale. -+*/ -+static int fts5ApiTokenize( -+ Fts5Context *pCtx, -+ const char *pText, int nText, -+ void *pUserData, -+ int (*xToken)(void*, int, const char*, int, int, int) -+){ -+ return fts5ApiTokenize_v2(pCtx, pText, nText, 0, 0, pUserData, xToken); - } - - static int fts5ApiPhraseCount(Fts5Context *pCtx){ -@@ -224775,6 +255916,49 @@ static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ - return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); - } - -+/* -+** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This -+** function extracts the text value of column iCol of the current row. -+** Additionally, if there is an associated locale, it invokes -+** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller -+** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point -+** after this function returns. -+** -+** If successful, (*ppText) is set to point to a buffer containing the text -+** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that -+** buffer in bytes. It is not guaranteed to be nul-terminated. If an error -+** occurs, an SQLite error code is returned. The final values of the two -+** output parameters are undefined in this case. -+*/ -+static int fts5TextFromStmt( -+ Fts5Config *pConfig, -+ sqlite3_stmt *pStmt, -+ int iCol, -+ const char **ppText, -+ int *pnText -+){ -+ sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1); -+ const char *pLoc = 0; -+ int nLoc = 0; -+ int rc = SQLITE_OK; -+ -+ if( pConfig->bLocale -+ && pConfig->eContent==FTS5_CONTENT_EXTERNAL -+ && sqlite3Fts5IsLocaleValue(pConfig, pVal) -+ ){ -+ rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc); -+ }else{ -+ *ppText = (const char*)sqlite3_value_text(pVal); -+ *pnText = sqlite3_value_bytes(pVal); -+ if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){ -+ pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol); -+ nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol); -+ } -+ } -+ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); -+ return rc; -+} -+ - static int fts5ApiColumnText( - Fts5Context *pCtx, - int iCol, -@@ -224783,46 +255967,69 @@ static int fts5ApiColumnText( - ){ - int rc = SQLITE_OK; - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; -- if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) -- || pCsr->ePlan==FTS5_PLAN_SPECIAL -- ){ -+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); -+ -+ assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); -+ if( iCol<0 || iCol>=pTab->pConfig->nCol ){ -+ rc = SQLITE_RANGE; -+ }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){ - *pz = 0; - *pn = 0; - }else{ - rc = fts5SeekCursor(pCsr, 0); - if( rc==SQLITE_OK ){ -- *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); -- *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); -+ rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn); -+ sqlite3Fts5ClearLocale(pTab->pConfig); - } - } - return rc; - } - -+/* -+** This is called by various API functions - xInst, xPhraseFirst, -+** xPhraseFirstColumn etc. - to obtain the position list for phrase iPhrase -+** of the current row. This function works for both detail=full tables (in -+** which case the position-list was read from the fts index) or for other -+** detail= modes if the row content is available. -+*/ - static int fts5CsrPoslist( -- Fts5Cursor *pCsr, -- int iPhrase, -- const u8 **pa, -- int *pn -+ Fts5Cursor *pCsr, /* Fts5 cursor object */ -+ int iPhrase, /* Phrase to find position list for */ -+ const u8 **pa, /* OUT: Pointer to position list buffer */ -+ int *pn /* OUT: Size of (*pa) in bytes */ - ){ - Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; - int rc = SQLITE_OK; - int bLive = (pCsr->pSorter==0); - -- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ -- -+ if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ -+ rc = SQLITE_RANGE; -+ }else if( pConfig->eDetail!=FTS5_DETAIL_FULL -+ && fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) -+ ){ -+ *pa = 0; -+ *pn = 0; -+ return SQLITE_OK; -+ }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ - if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ - Fts5PoslistPopulator *aPopulator; - int i; -+ - aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); - if( aPopulator==0 ) rc = SQLITE_NOMEM; -+ if( rc==SQLITE_OK ){ -+ rc = fts5SeekCursor(pCsr, 0); -+ } - for(i=0; inCol && rc==SQLITE_OK; i++){ -- int n; const char *z; -- rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n); -+ const char *z = 0; -+ int n = 0; -+ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ExprPopulatePoslists( - pConfig, pCsr->pExpr, aPopulator, i, z, n - ); - } -+ sqlite3Fts5ClearLocale(pConfig); - } - sqlite3_free(aPopulator); - -@@ -224833,13 +256040,18 @@ static int fts5CsrPoslist( - CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST); - } - -- if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ -- Fts5Sorter *pSorter = pCsr->pSorter; -- int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); -- *pn = pSorter->aIdx[iPhrase] - i1; -- *pa = &pSorter->aPoslist[i1]; -+ if( rc==SQLITE_OK ){ -+ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ -+ Fts5Sorter *pSorter = pCsr->pSorter; -+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); -+ *pn = pSorter->aIdx[iPhrase] - i1; -+ *pa = &pSorter->aPoslist[i1]; -+ }else{ -+ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); -+ } - }else{ -- *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); -+ *pa = 0; -+ *pn = 0; - } - - return rc; -@@ -224892,13 +256104,15 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ - - nInst++; - if( nInst>=pCsr->nInstAlloc ){ -- pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; -+ int nNewSize = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; - aInst = (int*)sqlite3_realloc64( -- pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 -+ pCsr->aInst, nNewSize*sizeof(int)*3 - ); - if( aInst ){ - pCsr->aInst = aInst; -+ pCsr->nInstAlloc = nNewSize; - }else{ -+ nInst--; - rc = SQLITE_NOMEM; - break; - } -@@ -224908,7 +256122,8 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ - aInst[0] = iBest; - aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); - aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); -- if( aInst[1]<0 || aInst[1]>=nCol ){ -+ assert( aInst[1]>=0 ); -+ if( aInst[1]>=nCol ){ - rc = FTS5_CORRUPT; - break; - } -@@ -224946,12 +256161,6 @@ static int fts5ApiInst( - ){ - if( iIdx<0 || iIdx>=pCsr->nInstCount ){ - rc = SQLITE_RANGE; --#if 0 -- }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){ -- *piPhrase = pCsr->aInst[iIdx*3]; -- *piCol = pCsr->aInst[iIdx*3 + 2]; -- *piOff = -1; --#endif - }else{ - *piPhrase = pCsr->aInst[iIdx*3]; - *piCol = pCsr->aInst[iIdx*3 + 1]; -@@ -224992,7 +256201,7 @@ static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ - if( pConfig->bColumnsize ){ - i64 iRowid = fts5CursorRowid(pCsr); - rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); -- }else if( pConfig->zContent==0 ){ -+ }else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ - int i; - for(i=0; inCol; i++){ - if( pConfig->abUnindexed[i]==0 ){ -@@ -225001,17 +256210,19 @@ static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ - } - }else{ - int i; -+ rc = fts5SeekCursor(pCsr, 0); - for(i=0; rc==SQLITE_OK && inCol; i++){ - if( pConfig->abUnindexed[i]==0 ){ -- const char *z; int n; -- void *p = (void*)(&pCsr->aColumnSize[i]); -+ const char *z = 0; -+ int n = 0; - pCsr->aColumnSize[i] = 0; -- rc = fts5ApiColumnText(pCtx, i, &z, &n); -+ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts5Tokenize( -- pConfig, FTS5_TOKENIZE_AUX, z, n, p, fts5ColumnSizeCb -+ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, -+ z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb - ); - } -+ sqlite3Fts5ClearLocale(pConfig); - } - } - } -@@ -225091,11 +256302,10 @@ static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ - } - - static void fts5ApiPhraseNext( -- Fts5Context *pUnused, -+ Fts5Context *pCtx, - Fts5PhraseIter *pIter, - int *piCol, int *piOff - ){ -- UNUSED_PARAM(pUnused); - if( pIter->a>=pIter->b ){ - *piCol = -1; - *piOff = -1; -@@ -225103,8 +256313,12 @@ static void fts5ApiPhraseNext( - int iVal; - pIter->a += fts5GetVarint32(pIter->a, iVal); - if( iVal==1 ){ -+ /* Avoid returning a (*piCol) value that is too large for the table, -+ ** even if the position-list is corrupt. The caller might not be -+ ** expecting it. */ -+ int nCol = ((Fts5Table*)(((Fts5Cursor*)pCtx)->base.pVtab))->pConfig->nCol; - pIter->a += fts5GetVarint32(pIter->a, iVal); -- *piCol = iVal; -+ *piCol = (iVal>=nCol ? nCol-1 : iVal); - *piOff = 0; - pIter->a += fts5GetVarint32(pIter->a, iVal); - } -@@ -225122,7 +256336,8 @@ static int fts5ApiPhraseFirst( - int n; - int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); - if( rc==SQLITE_OK ){ -- pIter->b = &pIter->a[n]; -+ assert( pIter->a || n==0 ); -+ pIter->b = (pIter->a ? &pIter->a[n] : 0); - *piCol = 0; - *piOff = 0; - fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); -@@ -225181,7 +256396,8 @@ static int fts5ApiPhraseFirstColumn( - rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); - } - if( rc==SQLITE_OK ){ -- pIter->b = &pIter->a[n]; -+ assert( pIter->a || n==0 ); -+ pIter->b = (pIter->a ? &pIter->a[n] : 0); - *piCol = 0; - fts5ApiPhraseNextColumn(pCtx, pIter, piCol); - } -@@ -225189,7 +256405,8 @@ static int fts5ApiPhraseFirstColumn( - int n; - rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); - if( rc==SQLITE_OK ){ -- pIter->b = &pIter->a[n]; -+ assert( pIter->a || n==0 ); -+ pIter->b = (pIter->a ? &pIter->a[n] : 0); - if( n<=0 ){ - *piCol = -1; - }else if( pIter->a[0]==0x01 ){ -@@ -225203,13 +256420,96 @@ static int fts5ApiPhraseFirstColumn( - return rc; - } - -+/* -+** xQueryToken() API implemenetation. -+*/ -+static int fts5ApiQueryToken( -+ Fts5Context* pCtx, -+ int iPhrase, -+ int iToken, -+ const char **ppOut, -+ int *pnOut -+){ -+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; -+ return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut); -+} -+ -+/* -+** xInstToken() API implemenetation. -+*/ -+static int fts5ApiInstToken( -+ Fts5Context *pCtx, -+ int iIdx, -+ int iToken, -+ const char **ppOut, int *pnOut -+){ -+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; -+ int rc = SQLITE_OK; -+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 -+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) -+ ){ -+ if( iIdx<0 || iIdx>=pCsr->nInstCount ){ -+ rc = SQLITE_RANGE; -+ }else{ -+ int iPhrase = pCsr->aInst[iIdx*3]; -+ int iCol = pCsr->aInst[iIdx*3 + 1]; -+ int iOff = pCsr->aInst[iIdx*3 + 2]; -+ i64 iRowid = fts5CursorRowid(pCsr); -+ rc = sqlite3Fts5ExprInstToken( -+ pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut -+ ); -+ } -+ } -+ return rc; -+} -+ - - static int fts5ApiQueryPhrase(Fts5Context*, int, void*, - int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) - ); - -+/* -+** The xColumnLocale() API. -+*/ -+static int fts5ApiColumnLocale( -+ Fts5Context *pCtx, -+ int iCol, -+ const char **pzLocale, -+ int *pnLocale -+){ -+ int rc = SQLITE_OK; -+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; -+ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; -+ -+ *pzLocale = 0; -+ *pnLocale = 0; -+ -+ assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); -+ if( iCol<0 || iCol>=pConfig->nCol ){ -+ rc = SQLITE_RANGE; -+ }else if( -+ pConfig->abUnindexed[iCol]==0 -+ && 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) -+ && pConfig->bLocale -+ ){ -+ rc = fts5SeekCursor(pCsr, 0); -+ if( rc==SQLITE_OK ){ -+ const char *zDummy = 0; -+ int nDummy = 0; -+ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy); -+ if( rc==SQLITE_OK ){ -+ *pzLocale = pConfig->t.pLocale; -+ *pnLocale = pConfig->t.nLocale; -+ } -+ sqlite3Fts5ClearLocale(pConfig); -+ } -+ } -+ -+ return rc; -+} -+ - static const Fts5ExtensionApi sFts5Api = { -- 2, /* iVersion */ -+ 4, /* iVersion */ - fts5ApiUserData, - fts5ApiColumnCount, - fts5ApiRowCount, -@@ -225229,6 +256529,10 @@ static const Fts5ExtensionApi sFts5Api = { - fts5ApiPhraseNext, - fts5ApiPhraseFirstColumn, - fts5ApiPhraseNextColumn, -+ fts5ApiQueryToken, -+ fts5ApiInstToken, -+ fts5ApiColumnLocale, -+ fts5ApiTokenize_v2 - }; - - /* -@@ -225279,6 +256583,7 @@ static void fts5ApiInvoke( - sqlite3_value **argv - ){ - assert( pCsr->pAux==0 ); -+ assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); - pCsr->pAux = pAux; - pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); - pCsr->pAux = 0; -@@ -225292,6 +256597,21 @@ static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){ - return pCsr; - } - -+/* -+** Parameter zFmt is a printf() style formatting string. This function -+** formats it using the trailing arguments and returns the result as -+** an error message to the context passed as the first argument. -+*/ -+static void fts5ResultError(sqlite3_context *pCtx, const char *zFmt, ...){ -+ char *zErr = 0; -+ va_list ap; -+ va_start(ap, zFmt); -+ zErr = sqlite3_vmprintf(zFmt, ap); -+ sqlite3_result_error(pCtx, zErr, -1); -+ sqlite3_free(zErr); -+ va_end(ap); -+} -+ - static void fts5ApiCallback( - sqlite3_context *context, - int argc, -@@ -225307,12 +256627,13 @@ static void fts5ApiCallback( - iCsrId = sqlite3_value_int64(argv[0]); - - pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId); -- if( pCsr==0 || pCsr->ePlan==0 ){ -- char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId); -- sqlite3_result_error(context, zErr, -1); -- sqlite3_free(zErr); -+ if( pCsr==0 || (pCsr->ePlan==0 || pCsr->ePlan==FTS5_PLAN_SPECIAL) ){ -+ fts5ResultError(context, "no such cursor: %lld", iCsrId); - }else{ -+ sqlite3_vtab *pTab = pCsr->base.pVtab; - fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]); -+ sqlite3_free(pTab->zErrMsg); -+ pTab->zErrMsg = 0; - } - } - -@@ -225430,8 +256751,8 @@ static int fts5ColumnMethod( - ** auxiliary function. */ - sqlite3_result_int64(pCtx, pCsr->iCsrId); - }else if( iCol==pConfig->nCol+1 ){ -- - /* The value of the "rank" column. */ -+ - if( pCsr->ePlan==FTS5_PLAN_SOURCE ){ - fts5PoslistBlob(pCtx, pCsr); - }else if( -@@ -225442,14 +256763,32 @@ static int fts5ColumnMethod( - fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); - } - } -- }else if( !fts5IsContentless(pTab) ){ -- pConfig->pzErrmsg = &pTab->p.base.zErrMsg; -- rc = fts5SeekCursor(pCsr, 1); -- if( rc==SQLITE_OK ){ -- sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); -+ }else{ -+ if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){ -+ pConfig->pzErrmsg = &pTab->p.base.zErrMsg; -+ rc = fts5SeekCursor(pCsr, 1); -+ if( rc==SQLITE_OK ){ -+ sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); -+ if( pConfig->bLocale -+ && pConfig->eContent==FTS5_CONTENT_EXTERNAL -+ && sqlite3Fts5IsLocaleValue(pConfig, pVal) -+ ){ -+ const char *z = 0; -+ int n = 0; -+ rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n); -+ if( rc==SQLITE_OK ){ -+ sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT); -+ } -+ sqlite3Fts5ClearLocale(pConfig); -+ }else{ -+ sqlite3_result_value(pCtx, pVal); -+ } -+ } -+ -+ pConfig->pzErrmsg = 0; - } -- pConfig->pzErrmsg = 0; - } -+ - return rc; - } - -@@ -225487,8 +256826,10 @@ static int fts5RenameMethod( - sqlite3_vtab *pVtab, /* Virtual table handle */ - const char *zName /* New name of table */ - ){ -+ int rc; - Fts5FullTable *pTab = (Fts5FullTable*)pVtab; -- return sqlite3Fts5StorageRename(pTab->pStorage, zName); -+ rc = sqlite3Fts5StorageRename(pTab->pStorage, zName); -+ return rc; - } - - static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ -@@ -225502,9 +256843,15 @@ static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ - ** Flush the contents of the pending-terms table to disk. - */ - static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ -- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ -- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint); -- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); -+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; -+ int rc = SQLITE_OK; -+ -+ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); -+ rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); -+ if( rc==SQLITE_OK ){ -+ pTab->iSavepoint = iSavepoint+1; -+ } -+ return rc; - } - - /* -@@ -225513,9 +256860,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - ** This is a no-op. - */ - static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ -- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ -- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint); -- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); -+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; -+ int rc = SQLITE_OK; -+ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); -+ if( (iSavepoint+1)iSavepoint ){ -+ rc = sqlite3Fts5FlushToDisk(&pTab->p); -+ if( rc==SQLITE_OK ){ -+ pTab->iSavepoint = iSavepoint; -+ } -+ } -+ return rc; - } - - /* -@@ -225525,10 +256879,14 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - */ - static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5FullTable *pTab = (Fts5FullTable*)pVtab; -- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ -+ int rc = SQLITE_OK; - fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); - fts5TripCursors(pTab); -- return sqlite3Fts5StorageRollback(pTab->pStorage); -+ if( (iSavepoint+1)<=pTab->iSavepoint ){ -+ pTab->p.pConfig->pgsz = 0; -+ rc = sqlite3Fts5StorageRollback(pTab->pStorage); -+ } -+ return rc; - } - - /* -@@ -225570,47 +256928,210 @@ static int fts5CreateAux( - } - - /* --** Register a new tokenizer. This is the implementation of the --** fts5_api.xCreateTokenizer() method. -+** This function is used by xCreateTokenizer_v2() and xCreateTokenizer(). -+** It allocates and partially populates a new Fts5TokenizerModule object. -+** The new object is already linked into the Fts5Global context before -+** returning. -+** -+** If successful, SQLITE_OK is returned and a pointer to the new -+** Fts5TokenizerModule object returned via output parameter (*ppNew). All -+** that is required is for the caller to fill in the methods in -+** Fts5TokenizerModule.x1 and x2, and to set Fts5TokenizerModule.bV2Native -+** as appropriate. -+** -+** If an error occurs, an SQLite error code is returned and the final value -+** of (*ppNew) undefined. - */ --static int fts5CreateTokenizer( -- fts5_api *pApi, /* Global context (one per db handle) */ -+static int fts5NewTokenizerModule( -+ Fts5Global *pGlobal, /* Global context (one per db handle) */ - const char *zName, /* Name of new function */ - void *pUserData, /* User data for aux. function */ -- fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ -- void(*xDestroy)(void*) /* Destructor for pUserData */ -+ void(*xDestroy)(void*), /* Destructor for pUserData */ -+ Fts5TokenizerModule **ppNew - ){ -- Fts5Global *pGlobal = (Fts5Global*)pApi; -- Fts5TokenizerModule *pNew; -- sqlite3_int64 nName; /* Size of zName and its \0 terminator */ -- sqlite3_int64 nByte; /* Bytes of space to allocate */ - int rc = SQLITE_OK; -+ Fts5TokenizerModule *pNew; -+ sqlite3_int64 nName; /* Size of zName and its \0 terminator */ -+ sqlite3_int64 nByte; /* Bytes of space to allocate */ - - nName = strlen(zName) + 1; - nByte = sizeof(Fts5TokenizerModule) + nName; -- pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte); -+ *ppNew = pNew = (Fts5TokenizerModule*)sqlite3Fts5MallocZero(&rc, nByte); - if( pNew ){ -- memset(pNew, 0, (size_t)nByte); - pNew->zName = (char*)&pNew[1]; - memcpy(pNew->zName, zName, nName); - pNew->pUserData = pUserData; -- pNew->x = *pTokenizer; - pNew->xDestroy = xDestroy; - pNew->pNext = pGlobal->pTok; - pGlobal->pTok = pNew; - if( pNew->pNext==0 ){ - pGlobal->pDfltTok = pNew; - } -+ } -+ -+ return rc; -+} -+ -+/* -+** An instance of this type is used as the Fts5Tokenizer object for -+** wrapper tokenizers - those that provide access to a v1 tokenizer via -+** the fts5_tokenizer_v2 API, and those that provide access to a v2 tokenizer -+** via the fts5_tokenizer API. -+*/ -+typedef struct Fts5VtoVTokenizer Fts5VtoVTokenizer; -+struct Fts5VtoVTokenizer { -+ int bV2Native; /* True if v2 native tokenizer */ -+ fts5_tokenizer x1; /* Tokenizer functions */ -+ fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ -+ Fts5Tokenizer *pReal; -+}; -+ -+/* -+** Create a wrapper tokenizer. The context argument pCtx points to the -+** Fts5TokenizerModule object. -+*/ -+static int fts5VtoVCreate( -+ void *pCtx, -+ const char **azArg, -+ int nArg, -+ Fts5Tokenizer **ppOut -+){ -+ Fts5TokenizerModule *pMod = (Fts5TokenizerModule*)pCtx; -+ Fts5VtoVTokenizer *pNew = 0; -+ int rc = SQLITE_OK; -+ -+ pNew = (Fts5VtoVTokenizer*)sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); -+ if( rc==SQLITE_OK ){ -+ pNew->x1 = pMod->x1; -+ pNew->x2 = pMod->x2; -+ pNew->bV2Native = pMod->bV2Native; -+ if( pMod->bV2Native ){ -+ rc = pMod->x2.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); -+ }else{ -+ rc = pMod->x1.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); -+ } -+ if( rc!=SQLITE_OK ){ -+ sqlite3_free(pNew); -+ pNew = 0; -+ } -+ } -+ -+ *ppOut = (Fts5Tokenizer*)pNew; -+ return rc; -+} -+ -+/* -+** Delete an Fts5VtoVTokenizer wrapper tokenizer. -+*/ -+static void fts5VtoVDelete(Fts5Tokenizer *pTok){ -+ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; -+ if( p ){ -+ if( p->bV2Native ){ -+ p->x2.xDelete(p->pReal); -+ }else{ -+ p->x1.xDelete(p->pReal); -+ } -+ sqlite3_free(p); -+ } -+} -+ -+ -+/* -+** xTokenizer method for a wrapper tokenizer that offers the v1 interface -+** (no support for locales). -+*/ -+static int fts5V1toV2Tokenize( -+ Fts5Tokenizer *pTok, -+ void *pCtx, int flags, -+ const char *pText, int nText, -+ int (*xToken)(void*, int, const char*, int, int, int) -+){ -+ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; -+ assert( p->bV2Native ); -+ return p->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken); -+} -+ -+/* -+** xTokenizer method for a wrapper tokenizer that offers the v2 interface -+** (with locale support). -+*/ -+static int fts5V2toV1Tokenize( -+ Fts5Tokenizer *pTok, -+ void *pCtx, int flags, -+ const char *pText, int nText, -+ const char *pLocale, int nLocale, -+ int (*xToken)(void*, int, const char*, int, int, int) -+){ -+ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; -+ assert( p->bV2Native==0 ); -+ UNUSED_PARAM2(pLocale,nLocale); -+ return p->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken); -+} -+ -+/* -+** Register a new tokenizer. This is the implementation of the -+** fts5_api.xCreateTokenizer_v2() method. -+*/ -+static int fts5CreateTokenizer_v2( -+ fts5_api *pApi, /* Global context (one per db handle) */ -+ const char *zName, /* Name of new function */ -+ void *pUserData, /* User data for aux. function */ -+ fts5_tokenizer_v2 *pTokenizer, /* Tokenizer implementation */ -+ void(*xDestroy)(void*) /* Destructor for pUserData */ -+){ -+ Fts5Global *pGlobal = (Fts5Global*)pApi; -+ int rc = SQLITE_OK; -+ -+ if( pTokenizer->iVersion>2 ){ -+ rc = SQLITE_ERROR; - }else{ -- rc = SQLITE_NOMEM; -+ Fts5TokenizerModule *pNew = 0; -+ rc = fts5NewTokenizerModule(pGlobal, zName, pUserData, xDestroy, &pNew); -+ if( pNew ){ -+ pNew->x2 = *pTokenizer; -+ pNew->bV2Native = 1; -+ pNew->x1.xCreate = fts5VtoVCreate; -+ pNew->x1.xTokenize = fts5V1toV2Tokenize; -+ pNew->x1.xDelete = fts5VtoVDelete; -+ } - } - - return rc; - } - -+/* -+** The fts5_api.xCreateTokenizer() method. -+*/ -+static int fts5CreateTokenizer( -+ fts5_api *pApi, /* Global context (one per db handle) */ -+ const char *zName, /* Name of new function */ -+ void *pUserData, /* User data for aux. function */ -+ fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ -+ void(*xDestroy)(void*) /* Destructor for pUserData */ -+){ -+ Fts5TokenizerModule *pNew = 0; -+ int rc = SQLITE_OK; -+ -+ rc = fts5NewTokenizerModule( -+ (Fts5Global*)pApi, zName, pUserData, xDestroy, &pNew -+ ); -+ if( pNew ){ -+ pNew->x1 = *pTokenizer; -+ pNew->x2.xCreate = fts5VtoVCreate; -+ pNew->x2.xTokenize = fts5V2toV1Tokenize; -+ pNew->x2.xDelete = fts5VtoVDelete; -+ } -+ return rc; -+} -+ -+/* -+** Search the global context passed as the first argument for a tokenizer -+** module named zName. If found, return a pointer to the Fts5TokenizerModule -+** object. Otherwise, return NULL. -+*/ - static Fts5TokenizerModule *fts5LocateTokenizer( -- Fts5Global *pGlobal, -- const char *zName -+ Fts5Global *pGlobal, /* Global (one per db handle) object */ -+ const char *zName /* Name of tokenizer module to find */ - ){ - Fts5TokenizerModule *pMod = 0; - -@@ -225625,6 +257146,36 @@ static Fts5TokenizerModule *fts5LocateTokenizer( - return pMod; - } - -+/* -+** Find a tokenizer. This is the implementation of the -+** fts5_api.xFindTokenizer_v2() method. -+*/ -+static int fts5FindTokenizer_v2( -+ fts5_api *pApi, /* Global context (one per db handle) */ -+ const char *zName, /* Name of tokenizer */ -+ void **ppUserData, -+ fts5_tokenizer_v2 **ppTokenizer /* Populate this object */ -+){ -+ int rc = SQLITE_OK; -+ Fts5TokenizerModule *pMod; -+ -+ pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); -+ if( pMod ){ -+ if( pMod->bV2Native ){ -+ *ppUserData = pMod->pUserData; -+ }else{ -+ *ppUserData = (void*)pMod; -+ } -+ *ppTokenizer = &pMod->x2; -+ }else{ -+ *ppTokenizer = 0; -+ *ppUserData = 0; -+ rc = SQLITE_ERROR; -+ } -+ -+ return rc; -+} -+ - /* - ** Find a tokenizer. This is the implementation of the - ** fts5_api.xFindTokenizer() method. -@@ -225640,48 +257191,75 @@ static int fts5FindTokenizer( - - pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); - if( pMod ){ -- *pTokenizer = pMod->x; -- *ppUserData = pMod->pUserData; -+ if( pMod->bV2Native==0 ){ -+ *ppUserData = pMod->pUserData; -+ }else{ -+ *ppUserData = (void*)pMod; -+ } -+ *pTokenizer = pMod->x1; - }else{ -- memset(pTokenizer, 0, sizeof(fts5_tokenizer)); -+ memset(pTokenizer, 0, sizeof(*pTokenizer)); -+ *ppUserData = 0; - rc = SQLITE_ERROR; - } - - return rc; - } - --static int sqlite3Fts5GetTokenizer( -- Fts5Global *pGlobal, -- const char **azArg, -- int nArg, -- Fts5Tokenizer **ppTok, -- fts5_tokenizer **ppTokApi, -- char **pzErr --){ -- Fts5TokenizerModule *pMod; -+/* -+** Attempt to instantiate the tokenizer. -+*/ -+static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig){ -+ const char **azArg = pConfig->t.azArg; -+ const int nArg = pConfig->t.nArg; -+ Fts5TokenizerModule *pMod = 0; - int rc = SQLITE_OK; - -- pMod = fts5LocateTokenizer(pGlobal, nArg==0 ? 0 : azArg[0]); -+ pMod = fts5LocateTokenizer(pConfig->pGlobal, nArg==0 ? 0 : azArg[0]); - if( pMod==0 ){ - assert( nArg>0 ); - rc = SQLITE_ERROR; -- *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]); -+ sqlite3Fts5ConfigErrmsg(pConfig, "no such tokenizer: %s", azArg[0]); - }else{ -- rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok); -- *ppTokApi = &pMod->x; -- if( rc!=SQLITE_OK && pzErr ){ -- *pzErr = sqlite3_mprintf("error in tokenizer constructor"); -+ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**) = 0; -+ if( pMod->bV2Native ){ -+ xCreate = pMod->x2.xCreate; -+ pConfig->t.pApi2 = &pMod->x2; -+ }else{ -+ pConfig->t.pApi1 = &pMod->x1; -+ xCreate = pMod->x1.xCreate; -+ } -+ -+ rc = xCreate(pMod->pUserData, -+ (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->t.pTok -+ ); -+ -+ if( rc!=SQLITE_OK ){ -+ if( rc!=SQLITE_NOMEM ){ -+ sqlite3Fts5ConfigErrmsg(pConfig, "error in tokenizer constructor"); -+ } -+ }else if( pMod->bV2Native==0 ){ -+ pConfig->t.ePattern = sqlite3Fts5TokenizerPattern( -+ pMod->x1.xCreate, pConfig->t.pTok -+ ); - } - } - - if( rc!=SQLITE_OK ){ -- *ppTokApi = 0; -- *ppTok = 0; -+ pConfig->t.pApi1 = 0; -+ pConfig->t.pApi2 = 0; -+ pConfig->t.pTok = 0; - } - - return rc; - } - -+ -+/* -+** xDestroy callback passed to sqlite3_create_module(). This is invoked -+** when the db handle is being closed. Free memory associated with -+** tokenizers and aux functions registered with this db handle. -+*/ - static void fts5ModuleDestroy(void *pCtx){ - Fts5TokenizerModule *pTok, *pNextTok; - Fts5Auxiliary *pAux, *pNextAux; -@@ -225702,6 +257280,10 @@ static void fts5ModuleDestroy(void *pCtx){ - sqlite3_free(pGlobal); - } - -+/* -+** Implementation of the fts5() function used by clients to obtain the -+** API pointer. -+*/ - static void fts5Fts5Func( - sqlite3_context *pCtx, /* Function call context */ - int nArg, /* Number of args */ -@@ -225725,7 +257307,82 @@ static void fts5SourceIdFunc( - ){ - assert( nArg==0 ); - UNUSED_PARAM2(nArg, apUnused); -- sqlite3_result_text(pCtx, "fts5: 2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f", -1, SQLITE_TRANSIENT); -+ sqlite3_result_text(pCtx, "fts5: 2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3", -1, SQLITE_TRANSIENT); -+} -+ -+/* -+** Implementation of fts5_locale(LOCALE, TEXT) function. -+** -+** If parameter LOCALE is NULL, or a zero-length string, then a copy of -+** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as -+** text, and the value returned is a blob consisting of: -+** -+** * The 4 bytes 0x00, 0xE0, 0xB2, 0xEb (FTS5_LOCALE_HEADER). -+** * The LOCALE, as utf-8 text, followed by -+** * 0x00, followed by -+** * The TEXT, as utf-8 text. -+** -+** There is no final nul-terminator following the TEXT value. -+*/ -+static void fts5LocaleFunc( -+ sqlite3_context *pCtx, /* Function call context */ -+ int nArg, /* Number of args */ -+ sqlite3_value **apArg /* Function arguments */ -+){ -+ const char *zLocale = 0; -+ int nLocale = 0; -+ const char *zText = 0; -+ int nText = 0; -+ -+ assert( nArg==2 ); -+ UNUSED_PARAM(nArg); -+ -+ zLocale = (const char*)sqlite3_value_text(apArg[0]); -+ nLocale = sqlite3_value_bytes(apArg[0]); -+ -+ zText = (const char*)sqlite3_value_text(apArg[1]); -+ nText = sqlite3_value_bytes(apArg[1]); -+ -+ if( zLocale==0 || zLocale[0]=='\0' ){ -+ sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT); -+ }else{ -+ Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx); -+ u8 *pBlob = 0; -+ u8 *pCsr = 0; -+ int nBlob = 0; -+ -+ nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText; -+ pBlob = (u8*)sqlite3_malloc(nBlob); -+ if( pBlob==0 ){ -+ sqlite3_result_error_nomem(pCtx); -+ return; -+ } -+ -+ pCsr = pBlob; -+ memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE); -+ pCsr += FTS5_LOCALE_HDR_SIZE; -+ memcpy(pCsr, zLocale, nLocale); -+ pCsr += nLocale; -+ (*pCsr++) = 0x00; -+ if( zText ) memcpy(pCsr, zText, nText); -+ assert( &pCsr[nText]==&pBlob[nBlob] ); -+ -+ sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); -+ } -+} -+ -+/* -+** Implementation of fts5_insttoken() function. -+*/ -+static void fts5InsttokenFunc( -+ sqlite3_context *pCtx, /* Function call context */ -+ int nArg, /* Number of args */ -+ sqlite3_value **apArg /* Function arguments */ -+){ -+ assert( nArg==1 ); -+ (void)nArg; -+ sqlite3_result_value(pCtx, apArg[0]); -+ sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE); - } - - /* -@@ -225743,9 +257400,47 @@ static int fts5ShadowName(const char *zName){ - return 0; - } - -+/* -+** Run an integrity check on the FTS5 data structures. Return a string -+** if anything is found amiss. Return a NULL pointer if everything is -+** OK. -+*/ -+static int fts5IntegrityMethod( -+ sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */ -+ const char *zSchema, /* Name of schema in which this table lives */ -+ const char *zTabname, /* Name of the table itself */ -+ int isQuick, /* True if this is a quick-check */ -+ char **pzErr /* Write error message here */ -+){ -+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; -+ int rc; -+ -+ assert( pzErr!=0 && *pzErr==0 ); -+ UNUSED_PARAM(isQuick); -+ assert( pTab->p.pConfig->pzErrmsg==0 ); -+ pTab->p.pConfig->pzErrmsg = pzErr; -+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0); -+ if( *pzErr==0 && rc!=SQLITE_OK ){ -+ if( (rc&0xff)==SQLITE_CORRUPT ){ -+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s", -+ zSchema, zTabname); -+ rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM; -+ }else{ -+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" -+ " FTS5 table %s.%s: %s", -+ zSchema, zTabname, sqlite3_errstr(rc)); -+ } -+ } -+ -+ sqlite3Fts5IndexCloseReader(pTab->p.pIndex); -+ pTab->p.pConfig->pzErrmsg = 0; -+ -+ return rc; -+} -+ - static int fts5Init(sqlite3 *db){ - static const sqlite3_module fts5Mod = { -- /* iVersion */ 3, -+ /* iVersion */ 4, - /* xCreate */ fts5CreateMethod, - /* xConnect */ fts5ConnectMethod, - /* xBestIndex */ fts5BestIndexMethod, -@@ -225768,7 +257463,8 @@ static int fts5Init(sqlite3 *db){ - /* xSavepoint */ fts5SavepointMethod, - /* xRelease */ fts5ReleaseMethod, - /* xRollbackTo */ fts5RollbackToMethod, -- /* xShadowName */ fts5ShadowName -+ /* xShadowName */ fts5ShadowName, -+ /* xIntegrity */ fts5IntegrityMethod - }; - - int rc; -@@ -225781,10 +257477,22 @@ static int fts5Init(sqlite3 *db){ - void *p = (void*)pGlobal; - memset(pGlobal, 0, sizeof(Fts5Global)); - pGlobal->db = db; -- pGlobal->api.iVersion = 2; -+ pGlobal->api.iVersion = 3; - pGlobal->api.xCreateFunction = fts5CreateAux; - pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; - pGlobal->api.xFindTokenizer = fts5FindTokenizer; -+ pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; -+ pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; -+ -+ /* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector. -+ ** The constants below were generated randomly. */ -+ sqlite3_randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr); -+ pGlobal->aLocaleHdr[0] ^= 0xF924976D; -+ pGlobal->aLocaleHdr[1] ^= 0x16596E13; -+ pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA; -+ pGlobal->aLocaleHdr[3] ^= 0x9B03A67F; -+ assert( sizeof(pGlobal->aLocaleHdr)==16 ); -+ - rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); - if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); - if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); -@@ -225798,7 +257506,23 @@ static int fts5Init(sqlite3 *db){ - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function( -- db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0 -+ db, "fts5_source_id", 0, -+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS, -+ p, fts5SourceIdFunc, 0, 0 -+ ); -+ } -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3_create_function( -+ db, "fts5_locale", 2, -+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE, -+ p, fts5LocaleFunc, 0, 0 -+ ); -+ } -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3_create_function( -+ db, "fts5_insttoken", 1, -+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, -+ p, fts5InsttokenFunc, 0, 0 - ); - } - } -@@ -225808,8 +257532,8 @@ static int fts5Init(sqlite3 *db){ - ** its entry point to enable the matchinfo() demo. */ - #ifdef SQLITE_FTS5_ENABLE_TEST_MI - if( rc==SQLITE_OK ){ -- extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*); -- rc = sqlite3Fts5TestRegisterMatchinfo(db); -+ extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*); -+ rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api); - } - #endif - -@@ -225875,13 +257599,40 @@ SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3 *db){ - - /* #include "fts5Int.h" */ - -+/* -+** pSavedRow: -+** SQL statement FTS5_STMT_LOOKUP2 is a copy of FTS5_STMT_LOOKUP, it -+** does a by-rowid lookup to retrieve a single row from the %_content -+** table or equivalent external-content table/view. -+** -+** However, FTS5_STMT_LOOKUP2 is only used when retrieving the original -+** values for a row being UPDATEd. In that case, the SQL statement is -+** not reset and pSavedRow is set to point at it. This is so that the -+** insert operation that follows the delete may access the original -+** row values for any new values for which sqlite3_value_nochange() returns -+** true. i.e. if the user executes: -+** -+** CREATE VIRTUAL TABLE ft USING fts5(a, b, c, locale=1); -+** ... -+** UPDATE fts SET a=?, b=? WHERE rowid=?; -+** -+** then the value passed to the xUpdate() method of this table as the -+** new.c value is an sqlite3_value_nochange() value. So in this case it -+** must be read from the saved row stored in Fts5Storage.pSavedRow. -+** -+** This is necessary - using sqlite3_value_nochange() instead of just having -+** SQLite pass the original value back via xUpdate() - so as not to discard -+** any locale information associated with such values. -+** -+*/ - struct Fts5Storage { - Fts5Config *pConfig; - Fts5Index *pIndex; - int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ - i64 nTotalRow; /* Total number of rows in FTS table */ - i64 *aTotalSize; /* Total sizes of each column */ -- sqlite3_stmt *aStmt[11]; -+ sqlite3_stmt *pSavedRow; -+ sqlite3_stmt *aStmt[12]; - }; - - -@@ -225895,14 +257646,15 @@ struct Fts5Storage { - # error "FTS5_STMT_LOOKUP mismatch" - #endif - --#define FTS5_STMT_INSERT_CONTENT 3 --#define FTS5_STMT_REPLACE_CONTENT 4 --#define FTS5_STMT_DELETE_CONTENT 5 --#define FTS5_STMT_REPLACE_DOCSIZE 6 --#define FTS5_STMT_DELETE_DOCSIZE 7 --#define FTS5_STMT_LOOKUP_DOCSIZE 8 --#define FTS5_STMT_REPLACE_CONFIG 9 --#define FTS5_STMT_SCAN 10 -+#define FTS5_STMT_LOOKUP2 3 -+#define FTS5_STMT_INSERT_CONTENT 4 -+#define FTS5_STMT_REPLACE_CONTENT 5 -+#define FTS5_STMT_DELETE_CONTENT 6 -+#define FTS5_STMT_REPLACE_DOCSIZE 7 -+#define FTS5_STMT_DELETE_DOCSIZE 8 -+#define FTS5_STMT_LOOKUP_DOCSIZE 9 -+#define FTS5_STMT_REPLACE_CONFIG 10 -+#define FTS5_STMT_SCAN 11 - - /* - ** Prepare the two insert statements - Fts5Storage.pInsertContent and -@@ -225932,14 +257684,15 @@ static int fts5StorageGetStmt( - "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC", - "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC", - "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */ -+ "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP2 */ - - "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ - "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ - "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */ -- "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */ -+ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */ - "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */ - -- "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ -+ "SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ - - "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */ - "SELECT %s FROM %s AS T", /* SCAN */ -@@ -225947,6 +257700,8 @@ static int fts5StorageGetStmt( - Fts5Config *pC = p->pConfig; - char *zSql = 0; - -+ assert( ArraySize(azStmt)==ArraySize(p->aStmt) ); -+ - switch( eStmt ){ - case FTS5_STMT_SCAN: - zSql = sqlite3_mprintf(azStmt[eStmt], -@@ -225963,6 +257718,7 @@ static int fts5StorageGetStmt( - break; - - case FTS5_STMT_LOOKUP: -+ case FTS5_STMT_LOOKUP2: - zSql = sqlite3_mprintf(azStmt[eStmt], - pC->zContentExprlist, pC->zContent, pC->zContentRowid - ); -@@ -225970,23 +257726,51 @@ static int fts5StorageGetStmt( - - case FTS5_STMT_INSERT_CONTENT: - case FTS5_STMT_REPLACE_CONTENT: { -- int nCol = pC->nCol + 1; -- char *zBind; -+ char *zBind = 0; - int i; - -- zBind = sqlite3_malloc64(1 + nCol*2); -- if( zBind ){ -- for(i=0; ieContent==FTS5_CONTENT_NORMAL -+ || pC->eContent==FTS5_CONTENT_UNINDEXED -+ ); -+ -+ /* Add bindings for the "c*" columns - those that store the actual -+ ** table content. If eContent==NORMAL, then there is one binding -+ ** for each column. Or, if eContent==UNINDEXED, then there are only -+ ** bindings for the UNINDEXED columns. */ -+ for(i=0; rc==SQLITE_OK && i<(pC->nCol+1); i++){ -+ if( !i || pC->eContent==FTS5_CONTENT_NORMAL || pC->abUnindexed[i-1] ){ -+ zBind = sqlite3Fts5Mprintf(&rc, "%z%s?%d", zBind, zBind?",":"",i+1); -+ } -+ } -+ -+ /* Add bindings for any "l*" columns. Only non-UNINDEXED columns -+ ** require these. */ -+ if( pC->bLocale && pC->eContent==FTS5_CONTENT_NORMAL ){ -+ for(i=0; rc==SQLITE_OK && inCol; i++){ -+ if( pC->abUnindexed[i]==0 ){ -+ zBind = sqlite3Fts5Mprintf(&rc, "%z,?%d", zBind, pC->nCol+i+2); -+ } - } -- zBind[i*2-1] = '\0'; -- zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind); -- sqlite3_free(zBind); - } -+ -+ zSql = sqlite3Fts5Mprintf(&rc, azStmt[eStmt], pC->zDb, pC->zName,zBind); -+ sqlite3_free(zBind); - break; - } - -+ case FTS5_STMT_REPLACE_DOCSIZE: -+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, -+ (pC->bContentlessDelete ? ",?" : "") -+ ); -+ break; -+ -+ case FTS5_STMT_LOOKUP_DOCSIZE: -+ zSql = sqlite3_mprintf(azStmt[eStmt], -+ (pC->bContentlessDelete ? ",origin" : ""), -+ pC->zDb, pC->zName -+ ); -+ break; -+ - default: - zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName); - break; -@@ -225996,7 +257780,7 @@ static int fts5StorageGetStmt( - rc = SQLITE_NOMEM; - }else{ - int f = SQLITE_PREPARE_PERSISTENT; -- if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB; -+ if( eStmt>FTS5_STMT_LOOKUP2 ) f |= SQLITE_PREPARE_NO_VTAB; - p->pConfig->bLock++; - rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0); - p->pConfig->bLock--; -@@ -226004,6 +257788,11 @@ static int fts5StorageGetStmt( - if( rc!=SQLITE_OK && pzErrMsg ){ - *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); - } -+ if( rc==SQLITE_ERROR && eStmt>FTS5_STMT_LOOKUP2 && eStmtpIndex = pIndex; - - if( bCreate ){ -- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ -+ if( pConfig->eContent==FTS5_CONTENT_NORMAL -+ || pConfig->eContent==FTS5_CONTENT_UNINDEXED -+ ){ - int nDefn = 32 + pConfig->nCol*10; -- char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); -+ char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); - if( zDefn==0 ){ - rc = SQLITE_NOMEM; - }else{ -@@ -226167,8 +257958,20 @@ static int sqlite3Fts5StorageOpen( - sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); - iOff = (int)strlen(zDefn); - for(i=0; inCol; i++){ -- sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); -- iOff += (int)strlen(&zDefn[iOff]); -+ if( pConfig->eContent==FTS5_CONTENT_NORMAL -+ || pConfig->abUnindexed[i] -+ ){ -+ sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); -+ iOff += (int)strlen(&zDefn[iOff]); -+ } -+ } -+ if( pConfig->bLocale ){ -+ for(i=0; inCol; i++){ -+ if( pConfig->abUnindexed[i]==0 ){ -+ sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); -+ iOff += (int)strlen(&zDefn[iOff]); -+ } -+ } - } - rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); - } -@@ -226176,9 +257979,11 @@ static int sqlite3Fts5StorageOpen( - } - - if( rc==SQLITE_OK && pConfig->bColumnsize ){ -- rc = sqlite3Fts5CreateTable( -- pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr -- ); -+ const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB"; -+ if( pConfig->bContentlessDelete ){ -+ zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER"; -+ } -+ rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr); - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5CreateTable( -@@ -226243,60 +258048,193 @@ static int fts5StorageInsertCallback( - return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); - } - -+/* -+** This function is used as part of an UPDATE statement that modifies the -+** rowid of a row. In that case, this function is called first to set -+** Fts5Storage.pSavedRow to point to a statement that may be used to -+** access the original values of the row being deleted - iDel. -+** -+** SQLITE_OK is returned if successful, or an SQLite error code otherwise. -+** It is not considered an error if row iDel does not exist. In this case -+** pSavedRow is not set and SQLITE_OK returned. -+*/ -+static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){ -+ int rc = SQLITE_OK; -+ sqlite3_stmt *pSeek = 0; -+ -+ assert( p->pSavedRow==0 ); -+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+1, &pSeek, 0); -+ if( rc==SQLITE_OK ){ -+ sqlite3_bind_int64(pSeek, 1, iDel); -+ if( sqlite3_step(pSeek)!=SQLITE_ROW ){ -+ rc = sqlite3_reset(pSeek); -+ }else{ -+ p->pSavedRow = pSeek; -+ } -+ } -+ -+ return rc; -+} -+ - /* - ** If a row with rowid iDel is present in the %_content table, add the - ** delete-markers to the FTS index necessary to delete it. Do not actually - ** remove the %_content row at this time though. -+** -+** If parameter bSaveRow is true, then Fts5Storage.pSavedRow is left -+** pointing to a statement (FTS5_STMT_LOOKUP2) that may be used to access -+** the original values of the row being deleted. This is used by UPDATE -+** statements. - */ - static int fts5StorageDeleteFromIndex( - Fts5Storage *p, - i64 iDel, -- sqlite3_value **apVal -+ sqlite3_value **apVal, -+ int bSaveRow /* True to set pSavedRow */ - ){ - Fts5Config *pConfig = p->pConfig; - sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ -- int rc; /* Return code */ -+ int rc = SQLITE_OK; /* Return code */ - int rc2; /* sqlite3_reset() return code */ - int iCol; - Fts5InsertCtx ctx; - -+ assert( bSaveRow==0 || apVal==0 ); -+ assert( bSaveRow==0 || bSaveRow==1 ); -+ assert( FTS5_STMT_LOOKUP2==FTS5_STMT_LOOKUP+1 ); -+ - if( apVal==0 ){ -- rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0); -- if( rc!=SQLITE_OK ) return rc; -- sqlite3_bind_int64(pSeek, 1, iDel); -- if( sqlite3_step(pSeek)!=SQLITE_ROW ){ -- return sqlite3_reset(pSeek); -+ if( p->pSavedRow && bSaveRow ){ -+ pSeek = p->pSavedRow; -+ p->pSavedRow = 0; -+ }else{ -+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+bSaveRow, &pSeek, 0); -+ if( rc!=SQLITE_OK ) return rc; -+ sqlite3_bind_int64(pSeek, 1, iDel); -+ if( sqlite3_step(pSeek)!=SQLITE_ROW ){ -+ return sqlite3_reset(pSeek); -+ } - } - } - - ctx.pStorage = p; - ctx.iCol = -1; -- rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); - for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ - if( pConfig->abUnindexed[iCol-1]==0 ){ -- const char *zText; -- int nText; -+ sqlite3_value *pVal = 0; -+ sqlite3_value *pFree = 0; -+ const char *pText = 0; -+ int nText = 0; -+ const char *pLoc = 0; -+ int nLoc = 0; -+ -+ assert( pSeek==0 || apVal==0 ); -+ assert( pSeek!=0 || apVal!=0 ); - if( pSeek ){ -- zText = (const char*)sqlite3_column_text(pSeek, iCol); -- nText = sqlite3_column_bytes(pSeek, iCol); -+ pVal = sqlite3_column_value(pSeek, iCol); - }else{ -- zText = (const char*)sqlite3_value_text(apVal[iCol-1]); -- nText = sqlite3_value_bytes(apVal[iCol-1]); -+ pVal = apVal[iCol-1]; - } -- ctx.szCol = 0; -- rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, -- zText, nText, (void*)&ctx, fts5StorageInsertCallback -- ); -- p->aTotalSize[iCol-1] -= (i64)ctx.szCol; -+ -+ if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ -+ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); -+ }else{ -+ if( sqlite3_value_type(pVal)!=SQLITE_TEXT ){ -+ /* Make a copy of the value to work with. This is because the call -+ ** to sqlite3_value_text() below forces the type of the value to -+ ** SQLITE_TEXT, and we may need to use it again later. */ -+ pFree = pVal = sqlite3_value_dup(pVal); -+ if( pVal==0 ){ -+ rc = SQLITE_NOMEM; -+ } -+ } -+ if( rc==SQLITE_OK ){ -+ pText = (const char*)sqlite3_value_text(pVal); -+ nText = sqlite3_value_bytes(pVal); -+ if( pConfig->bLocale && pSeek ){ -+ pLoc = (const char*)sqlite3_column_text(pSeek, iCol+pConfig->nCol); -+ nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol); -+ } -+ } -+ } -+ -+ if( rc==SQLITE_OK ){ -+ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); -+ ctx.szCol = 0; -+ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, -+ pText, nText, (void*)&ctx, fts5StorageInsertCallback -+ ); -+ p->aTotalSize[iCol-1] -= (i64)ctx.szCol; -+ if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ -+ rc = FTS5_CORRUPT; -+ } -+ sqlite3Fts5ClearLocale(pConfig); -+ } -+ sqlite3_value_free(pFree); - } - } -- p->nTotalRow--; -+ if( rc==SQLITE_OK && p->nTotalRow<1 ){ -+ rc = FTS5_CORRUPT; -+ }else{ -+ p->nTotalRow--; -+ } - -- rc2 = sqlite3_reset(pSeek); -- if( rc==SQLITE_OK ) rc = rc2; -+ if( rc==SQLITE_OK && bSaveRow ){ -+ assert( p->pSavedRow==0 ); -+ p->pSavedRow = pSeek; -+ }else{ -+ rc2 = sqlite3_reset(pSeek); -+ if( rc==SQLITE_OK ) rc = rc2; -+ } - return rc; - } - -+/* -+** Reset any saved statement pSavedRow. Zero pSavedRow as well. This -+** should be called by the xUpdate() method of the fts5 table before -+** returning from any operation that may have set Fts5Storage.pSavedRow. -+*/ -+static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){ -+ assert( pStorage->pSavedRow==0 -+ || pStorage->pSavedRow==pStorage->aStmt[FTS5_STMT_LOOKUP2] -+ ); -+ sqlite3_reset(pStorage->pSavedRow); -+ pStorage->pSavedRow = 0; -+} -+ -+/* -+** This function is called to process a DELETE on a contentless_delete=1 -+** table. It adds the tombstone required to delete the entry with rowid -+** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs, -+** an SQLite error code. -+*/ -+static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){ -+ i64 iOrigin = 0; -+ sqlite3_stmt *pLookup = 0; -+ int rc = SQLITE_OK; -+ -+ assert( p->pConfig->bContentlessDelete ); -+ assert( p->pConfig->eContent==FTS5_CONTENT_NONE -+ || p->pConfig->eContent==FTS5_CONTENT_UNINDEXED -+ ); -+ -+ /* Look up the origin of the document in the %_docsize table. Store -+ ** this in stack variable iOrigin. */ -+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); -+ if( rc==SQLITE_OK ){ -+ sqlite3_bind_int64(pLookup, 1, iDel); -+ if( SQLITE_ROW==sqlite3_step(pLookup) ){ -+ iOrigin = sqlite3_column_int64(pLookup, 1); -+ } -+ rc = sqlite3_reset(pLookup); -+ } -+ -+ if( rc==SQLITE_OK && iOrigin!=0 ){ -+ rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel); -+ } -+ -+ return rc; -+} - - /* - ** Insert a record into the %_docsize table. Specifically, do: -@@ -226317,6 +258255,13 @@ static int fts5StorageInsertDocsize( - rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pReplace, 1, iRowid); -+ if( p->pConfig->bContentlessDelete ){ -+ i64 iOrigin = 0; -+ rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin); -+ sqlite3_bind_int64(pReplace, 3, iOrigin); -+ } -+ } -+ if( rc==SQLITE_OK ){ - sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); - sqlite3_step(pReplace); - rc = sqlite3_reset(pReplace); -@@ -226374,7 +258319,12 @@ static int fts5StorageSaveTotals(Fts5Storage *p){ - /* - ** Remove a row from the FTS table. - */ --static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){ -+static int sqlite3Fts5StorageDelete( -+ Fts5Storage *p, /* Storage object */ -+ i64 iDel, /* Rowid to delete from table */ -+ sqlite3_value **apVal, /* Optional - values to remove from index */ -+ int bSaveRow /* If true, set pSavedRow for deleted row */ -+){ - Fts5Config *pConfig = p->pConfig; - int rc; - sqlite3_stmt *pDel = 0; -@@ -226384,7 +258334,21 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap - - /* Delete the index records */ - if( rc==SQLITE_OK ){ -- rc = fts5StorageDeleteFromIndex(p, iDel, apVal); -+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); -+ } -+ -+ if( rc==SQLITE_OK ){ -+ if( p->pConfig->bContentlessDelete ){ -+ rc = fts5StorageContentlessDelete(p, iDel); -+ if( rc==SQLITE_OK -+ && bSaveRow -+ && p->pConfig->eContent==FTS5_CONTENT_UNINDEXED -+ ){ -+ rc = sqlite3Fts5StorageFindDeleteRow(p, iDel); -+ } -+ }else{ -+ rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); -+ } - } - - /* Delete the %_docsize record */ -@@ -226398,7 +258362,9 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap - } - - /* Delete the %_content record */ -- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ -+ if( pConfig->eContent==FTS5_CONTENT_NORMAL -+ || pConfig->eContent==FTS5_CONTENT_UNINDEXED -+ ){ - if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); - } -@@ -226430,8 +258396,13 @@ static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ - ); - if( rc==SQLITE_OK && pConfig->bColumnsize ){ - rc = fts5ExecPrintf(pConfig->db, 0, -- "DELETE FROM %Q.'%q_docsize';", -- pConfig->zDb, pConfig->zName -+ "DELETE FROM %Q.'%q_docsize';", pConfig->zDb, pConfig->zName -+ ); -+ } -+ -+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ -+ rc = fts5ExecPrintf(pConfig->db, 0, -+ "DELETE FROM %Q.'%q_content';", pConfig->zDb, pConfig->zName - ); - } - -@@ -226461,7 +258432,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ - } - - if( rc==SQLITE_OK ){ -- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); -+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg); - } - - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){ -@@ -226472,14 +258443,36 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ - for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ - ctx.szCol = 0; - if( pConfig->abUnindexed[ctx.iCol]==0 ){ -- const char *zText = (const char*)sqlite3_column_text(pScan, ctx.iCol+1); -- int nText = sqlite3_column_bytes(pScan, ctx.iCol+1); -- rc = sqlite3Fts5Tokenize(pConfig, -- FTS5_TOKENIZE_DOCUMENT, -- zText, nText, -- (void*)&ctx, -- fts5StorageInsertCallback -- ); -+ int nText = 0; /* Size of pText in bytes */ -+ const char *pText = 0; /* Pointer to buffer containing text value */ -+ int nLoc = 0; /* Size of pLoc in bytes */ -+ const char *pLoc = 0; /* Pointer to buffer containing text value */ -+ -+ sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); -+ if( pConfig->eContent==FTS5_CONTENT_EXTERNAL -+ && sqlite3Fts5IsLocaleValue(pConfig, pVal) -+ ){ -+ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); -+ }else{ -+ pText = (const char*)sqlite3_value_text(pVal); -+ nText = sqlite3_value_bytes(pVal); -+ if( pConfig->bLocale ){ -+ int iCol = ctx.iCol + 1 + pConfig->nCol; -+ pLoc = (const char*)sqlite3_column_text(pScan, iCol); -+ nLoc = sqlite3_column_bytes(pScan, iCol); -+ } -+ } -+ -+ if( rc==SQLITE_OK ){ -+ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); -+ rc = sqlite3Fts5Tokenize(pConfig, -+ FTS5_TOKENIZE_DOCUMENT, -+ pText, nText, -+ (void*)&ctx, -+ fts5StorageInsertCallback -+ ); -+ sqlite3Fts5ClearLocale(pConfig); -+ } - } - sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); - p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; -@@ -226545,6 +258538,7 @@ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ - */ - static int sqlite3Fts5StorageContentInsert( - Fts5Storage *p, -+ int bReplace, /* True to use REPLACE instead of INSERT */ - sqlite3_value **apVal, - i64 *piRowid - ){ -@@ -226552,7 +258546,9 @@ static int sqlite3Fts5StorageContentInsert( - int rc = SQLITE_OK; - - /* Insert the new row into the %_content table. */ -- if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ -+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL -+ && pConfig->eContent!=FTS5_CONTENT_UNINDEXED -+ ){ - if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ - *piRowid = sqlite3_value_int64(apVal[1]); - }else{ -@@ -226561,9 +258557,52 @@ static int sqlite3Fts5StorageContentInsert( - }else{ - sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ - int i; /* Counter variable */ -- rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); -- for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ -- rc = sqlite3_bind_value(pInsert, i, apVal[i]); -+ -+ assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT ); -+ assert( bReplace==0 || bReplace==1 ); -+ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT+bReplace, &pInsert, 0); -+ if( pInsert ) sqlite3_clear_bindings(pInsert); -+ -+ /* Bind the rowid value */ -+ sqlite3_bind_value(pInsert, 1, apVal[1]); -+ -+ /* Loop through values for user-defined columns. i=2 is the leftmost -+ ** user-defined column. As is column 1 of pSavedRow. */ -+ for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ -+ int bUnindexed = pConfig->abUnindexed[i-2]; -+ if( pConfig->eContent==FTS5_CONTENT_NORMAL || bUnindexed ){ -+ sqlite3_value *pVal = apVal[i]; -+ -+ if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ -+ /* This is an UPDATE statement, and user-defined column (i-2) was not -+ ** modified. Retrieve the value from Fts5Storage.pSavedRow. */ -+ pVal = sqlite3_column_value(p->pSavedRow, i-1); -+ if( pConfig->bLocale && bUnindexed==0 ){ -+ sqlite3_bind_value(pInsert, pConfig->nCol + i, -+ sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1) -+ ); -+ } -+ }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ -+ const char *pText = 0; -+ const char *pLoc = 0; -+ int nText = 0; -+ int nLoc = 0; -+ assert( pConfig->bLocale ); -+ -+ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); -+ if( rc==SQLITE_OK ){ -+ sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); -+ if( bUnindexed==0 ){ -+ int iLoc = pConfig->nCol + i; -+ sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT); -+ } -+ } -+ -+ continue; -+ } -+ -+ rc = sqlite3_bind_value(pInsert, i, pVal); -+ } - } - if( rc==SQLITE_OK ){ - sqlite3_step(pInsert); -@@ -226598,14 +258637,38 @@ static int sqlite3Fts5StorageIndexInsert( - for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ - ctx.szCol = 0; - if( pConfig->abUnindexed[ctx.iCol]==0 ){ -- const char *zText = (const char*)sqlite3_value_text(apVal[ctx.iCol+2]); -- int nText = sqlite3_value_bytes(apVal[ctx.iCol+2]); -- rc = sqlite3Fts5Tokenize(pConfig, -- FTS5_TOKENIZE_DOCUMENT, -- zText, nText, -- (void*)&ctx, -- fts5StorageInsertCallback -- ); -+ int nText = 0; /* Size of pText in bytes */ -+ const char *pText = 0; /* Pointer to buffer containing text value */ -+ int nLoc = 0; /* Size of pText in bytes */ -+ const char *pLoc = 0; /* Pointer to buffer containing text value */ -+ -+ sqlite3_value *pVal = apVal[ctx.iCol+2]; -+ if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ -+ pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); -+ if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ -+ int iCol = ctx.iCol + 1 + pConfig->nCol; -+ pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol); -+ nLoc = sqlite3_column_bytes(p->pSavedRow, iCol); -+ } -+ }else{ -+ pVal = apVal[ctx.iCol+2]; -+ } -+ -+ if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ -+ rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); -+ }else{ -+ pText = (const char*)sqlite3_value_text(pVal); -+ nText = sqlite3_value_bytes(pVal); -+ } -+ -+ if( rc==SQLITE_OK ){ -+ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); -+ rc = sqlite3Fts5Tokenize(pConfig, -+ FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, -+ fts5StorageInsertCallback -+ ); -+ sqlite3Fts5ClearLocale(pConfig); -+ } - } - sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); - p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; -@@ -226733,13 +258796,14 @@ static int fts5StorageIntegrityCallback( - ** some other SQLite error code if an error occurs while attempting to - ** determine this. - */ --static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ -+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){ - Fts5Config *pConfig = p->pConfig; -- int rc; /* Return code */ -+ int rc = SQLITE_OK; /* Return code */ - int *aColSize; /* Array of size pConfig->nCol */ - i64 *aTotalSize; /* Array of size pConfig->nCol */ - Fts5IntegrityCtx ctx; - sqlite3_stmt *pScan; -+ int bUseCksum; - - memset(&ctx, 0, sizeof(Fts5IntegrityCtx)); - ctx.pConfig = p->pConfig; -@@ -226748,83 +258812,120 @@ static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ - aColSize = (int*)&aTotalSize[pConfig->nCol]; - memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol); - -- /* Generate the expected index checksum based on the contents of the -- ** %_content table. This block stores the checksum in ctx.cksum. */ -- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); -- if( rc==SQLITE_OK ){ -- int rc2; -- while( SQLITE_ROW==sqlite3_step(pScan) ){ -- int i; -- ctx.iRowid = sqlite3_column_int64(pScan, 0); -- ctx.szCol = 0; -- if( pConfig->bColumnsize ){ -- rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); -- } -- if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ -- rc = sqlite3Fts5TermsetNew(&ctx.pTermset); -- } -- for(i=0; rc==SQLITE_OK && inCol; i++){ -- if( pConfig->abUnindexed[i] ) continue; -- ctx.iCol = i; -+ bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL -+ || (pConfig->eContent==FTS5_CONTENT_EXTERNAL && iArg) -+ ); -+ if( bUseCksum ){ -+ /* Generate the expected index checksum based on the contents of the -+ ** %_content table. This block stores the checksum in ctx.cksum. */ -+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); -+ if( rc==SQLITE_OK ){ -+ int rc2; -+ while( SQLITE_ROW==sqlite3_step(pScan) ){ -+ int i; -+ ctx.iRowid = sqlite3_column_int64(pScan, 0); - ctx.szCol = 0; -- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ -- rc = sqlite3Fts5TermsetNew(&ctx.pTermset); -- } -- if( rc==SQLITE_OK ){ -- const char *zText = (const char*)sqlite3_column_text(pScan, i+1); -- int nText = sqlite3_column_bytes(pScan, i+1); -- rc = sqlite3Fts5Tokenize(pConfig, -- FTS5_TOKENIZE_DOCUMENT, -- zText, nText, -- (void*)&ctx, -- fts5StorageIntegrityCallback -- ); -+ if( pConfig->bColumnsize ){ -+ rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); - } -- if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ -- rc = FTS5_CORRUPT; -+ if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ -+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); - } -- aTotalSize[i] += ctx.szCol; -- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ -- sqlite3Fts5TermsetFree(ctx.pTermset); -- ctx.pTermset = 0; -+ for(i=0; rc==SQLITE_OK && inCol; i++){ -+ if( pConfig->abUnindexed[i]==0 ){ -+ const char *pText = 0; -+ int nText = 0; -+ const char *pLoc = 0; -+ int nLoc = 0; -+ sqlite3_value *pVal = sqlite3_column_value(pScan, i+1); -+ -+ if( pConfig->eContent==FTS5_CONTENT_EXTERNAL -+ && sqlite3Fts5IsLocaleValue(pConfig, pVal) -+ ){ -+ rc = sqlite3Fts5DecodeLocaleValue( -+ pVal, &pText, &nText, &pLoc, &nLoc -+ ); -+ }else{ -+ if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ -+ int iCol = i + 1 + pConfig->nCol; -+ pLoc = (const char*)sqlite3_column_text(pScan, iCol); -+ nLoc = sqlite3_column_bytes(pScan, iCol); -+ } -+ pText = (const char*)sqlite3_value_text(pVal); -+ nText = sqlite3_value_bytes(pVal); -+ } -+ -+ ctx.iCol = i; -+ ctx.szCol = 0; -+ -+ if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ -+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); -+ } -+ -+ if( rc==SQLITE_OK ){ -+ sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); -+ rc = sqlite3Fts5Tokenize(pConfig, -+ FTS5_TOKENIZE_DOCUMENT, -+ pText, nText, -+ (void*)&ctx, -+ fts5StorageIntegrityCallback -+ ); -+ sqlite3Fts5ClearLocale(pConfig); -+ } -+ -+ /* If this is not a columnsize=0 database, check that the number -+ ** of tokens in the value matches the aColSize[] value read from -+ ** the %_docsize table. */ -+ if( rc==SQLITE_OK -+ && pConfig->bColumnsize -+ && ctx.szCol!=aColSize[i] -+ ){ -+ rc = FTS5_CORRUPT; -+ } -+ aTotalSize[i] += ctx.szCol; -+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ -+ sqlite3Fts5TermsetFree(ctx.pTermset); -+ ctx.pTermset = 0; -+ } -+ } - } -- } -- sqlite3Fts5TermsetFree(ctx.pTermset); -- ctx.pTermset = 0; -+ sqlite3Fts5TermsetFree(ctx.pTermset); -+ ctx.pTermset = 0; - -- if( rc!=SQLITE_OK ) break; -+ if( rc!=SQLITE_OK ) break; -+ } -+ rc2 = sqlite3_reset(pScan); -+ if( rc==SQLITE_OK ) rc = rc2; - } -- rc2 = sqlite3_reset(pScan); -- if( rc==SQLITE_OK ) rc = rc2; -- } - -- /* Test that the "totals" (sometimes called "averages") record looks Ok */ -- if( rc==SQLITE_OK ){ -- int i; -- rc = fts5StorageLoadTotals(p, 0); -- for(i=0; rc==SQLITE_OK && inCol; i++){ -- if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT; -+ /* Test that the "totals" (sometimes called "averages") record looks Ok */ -+ if( rc==SQLITE_OK ){ -+ int i; -+ rc = fts5StorageLoadTotals(p, 0); -+ for(i=0; rc==SQLITE_OK && inCol; i++){ -+ if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT; -+ } - } -- } - -- /* Check that the %_docsize and %_content tables contain the expected -- ** number of rows. */ -- if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ -- i64 nRow = 0; -- rc = fts5StorageCount(p, "content", &nRow); -- if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; -- } -- if( rc==SQLITE_OK && pConfig->bColumnsize ){ -- i64 nRow = 0; -- rc = fts5StorageCount(p, "docsize", &nRow); -- if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; -+ /* Check that the %_docsize and %_content tables contain the expected -+ ** number of rows. */ -+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ -+ i64 nRow = 0; -+ rc = fts5StorageCount(p, "content", &nRow); -+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; -+ } -+ if( rc==SQLITE_OK && pConfig->bColumnsize ){ -+ i64 nRow = 0; -+ rc = fts5StorageCount(p, "docsize", &nRow); -+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT; -+ } - } - - /* Pass the expected checksum down to the FTS index module. It will - ** verify, amongst other things, that it matches the checksum generated by - ** inspecting the index itself. */ - if( rc==SQLITE_OK ){ -- rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum); -+ rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum, bUseCksum); - } - - sqlite3_free(aTotalSize); -@@ -226904,8 +259005,9 @@ static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ - - assert( p->pConfig->bColumnsize ); - rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); -- if( rc==SQLITE_OK ){ -+ if( pLookup ){ - int bCorrupt = 1; -+ assert( rc==SQLITE_OK ); - sqlite3_bind_int64(pLookup, 1, iRowid); - if( SQLITE_ROW==sqlite3_step(pLookup) ){ - const u8 *aBlob = sqlite3_column_blob(pLookup, 0); -@@ -226918,6 +259020,8 @@ static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ - if( bCorrupt && rc==SQLITE_OK ){ - rc = FTS5_CORRUPT; - } -+ }else{ -+ assert( rc!=SQLITE_OK ); - } - - return rc; -@@ -226963,7 +259067,9 @@ static int sqlite3Fts5StorageSync(Fts5Storage *p){ - i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db); - if( p->bTotalsValid ){ - rc = fts5StorageSaveTotals(p); -- p->bTotalsValid = 0; -+ if( rc==SQLITE_OK ){ -+ p->bTotalsValid = 0; -+ } - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexSync(p->pIndex); -@@ -227206,7 +259312,7 @@ static const unsigned char sqlite3Utf8Trans1[] = { - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = sqlite3Utf8Trans1[c-0xc0]; \ -- while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ -+ while( zIn=0xc0 ){ \ -+ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \ -+ } \ -+} -+ - typedef struct Unicode61Tokenizer Unicode61Tokenizer; - struct Unicode61Tokenizer { - unsigned char aTokenChar[128]; /* ASCII range token characters */ -@@ -227388,7 +259500,6 @@ static int fts5UnicodeCreate( - zCat = azArg[i+1]; - } - } -- - if( rc==SQLITE_OK ){ - rc = unicodeSetCategories(p, zCat); - } -@@ -227418,7 +259529,6 @@ static int fts5UnicodeCreate( - rc = SQLITE_ERROR; - } - } -- - }else{ - rc = SQLITE_NOMEM; - } -@@ -227557,7 +259667,7 @@ static int fts5UnicodeTokenize( - - typedef struct PorterTokenizer PorterTokenizer; - struct PorterTokenizer { -- fts5_tokenizer tokenizer; /* Parent tokenizer module */ -+ fts5_tokenizer_v2 tokenizer_v2; /* Parent tokenizer module */ - Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */ - char aBuf[FTS5_PORTER_MAX_TOKEN + 64]; - }; -@@ -227569,7 +259679,7 @@ static void fts5PorterDelete(Fts5Tokenizer *pTok){ - if( pTok ){ - PorterTokenizer *p = (PorterTokenizer*)pTok; - if( p->pTokenizer ){ -- p->tokenizer.xDelete(p->pTokenizer); -+ p->tokenizer_v2.xDelete(p->pTokenizer); - } - sqlite3_free(p); - } -@@ -227588,6 +259698,7 @@ static int fts5PorterCreate( - PorterTokenizer *pRet; - void *pUserdata = 0; - const char *zBase = "unicode61"; -+ fts5_tokenizer_v2 *pV2 = 0; - - if( nArg>0 ){ - zBase = azArg[0]; -@@ -227596,14 +259707,15 @@ static int fts5PorterCreate( - pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer)); - if( pRet ){ - memset(pRet, 0, sizeof(PorterTokenizer)); -- rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer); -+ rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2); - }else{ - rc = SQLITE_NOMEM; - } - if( rc==SQLITE_OK ){ - int nArg2 = (nArg>0 ? nArg-1 : 0); -- const char **azArg2 = (nArg2 ? &azArg[1] : 0); -- rc = pRet->tokenizer.xCreate(pUserdata, azArg2, nArg2, &pRet->pTokenizer); -+ const char **az2 = (nArg2 ? &azArg[1] : 0); -+ memcpy(&pRet->tokenizer_v2, pV2, sizeof(fts5_tokenizer_v2)); -+ rc = pRet->tokenizer_v2.xCreate(pUserdata, az2, nArg2, &pRet->pTokenizer); - } - - if( rc!=SQLITE_OK ){ -@@ -228254,6 +260366,7 @@ static int fts5PorterTokenize( - void *pCtx, - int flags, - const char *pText, int nText, -+ const char *pLoc, int nLoc, - int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) - ){ - PorterTokenizer *p = (PorterTokenizer*)pTokenizer; -@@ -228261,11 +260374,194 @@ static int fts5PorterTokenize( - sCtx.xToken = xToken; - sCtx.pCtx = pCtx; - sCtx.aBuf = p->aBuf; -- return p->tokenizer.xTokenize( -- p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb -+ return p->tokenizer_v2.xTokenize( -+ p->pTokenizer, (void*)&sCtx, flags, pText, nText, pLoc, nLoc, fts5PorterCb - ); - } - -+/************************************************************************** -+** Start of trigram implementation. -+*/ -+typedef struct TrigramTokenizer TrigramTokenizer; -+struct TrigramTokenizer { -+ int bFold; /* True to fold to lower-case */ -+ int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */ -+}; -+ -+/* -+** Free a trigram tokenizer. -+*/ -+static void fts5TriDelete(Fts5Tokenizer *p){ -+ sqlite3_free(p); -+} -+ -+/* -+** Allocate a trigram tokenizer. -+*/ -+static int fts5TriCreate( -+ void *pUnused, -+ const char **azArg, -+ int nArg, -+ Fts5Tokenizer **ppOut -+){ -+ int rc = SQLITE_OK; -+ TrigramTokenizer *pNew = 0; -+ UNUSED_PARAM(pUnused); -+ if( nArg%2 ){ -+ rc = SQLITE_ERROR; -+ }else{ -+ int i; -+ pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); -+ if( pNew==0 ){ -+ rc = SQLITE_NOMEM; -+ }else{ -+ pNew->bFold = 1; -+ pNew->iFoldParam = 0; -+ -+ for(i=0; rc==SQLITE_OK && ibFold = (zArg[0]=='0'); -+ } -+ }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ -+ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ -+ rc = SQLITE_ERROR; -+ }else{ -+ pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; -+ } -+ }else{ -+ rc = SQLITE_ERROR; -+ } -+ } -+ -+ if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ -+ rc = SQLITE_ERROR; -+ } -+ -+ if( rc!=SQLITE_OK ){ -+ fts5TriDelete((Fts5Tokenizer*)pNew); -+ pNew = 0; -+ } -+ } -+ } -+ *ppOut = (Fts5Tokenizer*)pNew; -+ return rc; -+} -+ -+/* -+** Trigram tokenizer tokenize routine. -+*/ -+static int fts5TriTokenize( -+ Fts5Tokenizer *pTok, -+ void *pCtx, -+ int unusedFlags, -+ const char *pText, int nText, -+ int (*xToken)(void*, int, const char*, int, int, int) -+){ -+ TrigramTokenizer *p = (TrigramTokenizer*)pTok; -+ int rc = SQLITE_OK; -+ char aBuf[32]; -+ char *zOut = aBuf; -+ int ii; -+ const unsigned char *zIn = (const unsigned char*)pText; -+ const unsigned char *zEof = (zIn ? &zIn[nText] : 0); -+ u32 iCode = 0; -+ int aStart[3]; /* Input offset of each character in aBuf[] */ -+ -+ UNUSED_PARAM(unusedFlags); -+ -+ /* Populate aBuf[] with the characters for the first trigram. */ -+ for(ii=0; ii<3; ii++){ -+ do { -+ aStart[ii] = zIn - (const unsigned char*)pText; -+ if( zIn>=zEof ) return SQLITE_OK; -+ READ_UTF8(zIn, zEof, iCode); -+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); -+ }while( iCode==0 ); -+ WRITE_UTF8(zOut, iCode); -+ } -+ -+ /* At the start of each iteration of this loop: -+ ** -+ ** aBuf: Contains 3 characters. The 3 characters of the next trigram. -+ ** zOut: Points to the byte following the last character in aBuf. -+ ** aStart[3]: Contains the byte offset in the input text corresponding -+ ** to the start of each of the three characters in the buffer. -+ */ -+ assert( zIn<=zEof ); -+ while( 1 ){ -+ int iNext; /* Start of character following current tri */ -+ const char *z1; -+ -+ /* Read characters from the input up until the first non-diacritic */ -+ do { -+ iNext = zIn - (const unsigned char*)pText; -+ if( zIn>=zEof ){ -+ iCode = 0; -+ break; -+ } -+ READ_UTF8(zIn, zEof, iCode); -+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); -+ }while( iCode==0 ); -+ -+ /* Pass the current trigram back to fts5 */ -+ rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); -+ if( iCode==0 || rc!=SQLITE_OK ) break; -+ -+ /* Remove the first character from buffer aBuf[]. Append the character -+ ** with codepoint iCode. */ -+ z1 = aBuf; -+ FTS5_SKIP_UTF8(z1); -+ memmove(aBuf, z1, zOut - z1); -+ zOut -= (z1 - aBuf); -+ WRITE_UTF8(zOut, iCode); -+ -+ /* Update the aStart[] array */ -+ aStart[0] = aStart[1]; -+ aStart[1] = aStart[2]; -+ aStart[2] = iNext; -+ } -+ -+ return rc; -+} -+ -+/* -+** Argument xCreate is a pointer to a constructor function for a tokenizer. -+** pTok is a tokenizer previously created using the same method. This function -+** returns one of FTS5_PATTERN_NONE, FTS5_PATTERN_LIKE or FTS5_PATTERN_GLOB -+** indicating the style of pattern matching that the tokenizer can support. -+** In practice, this is: -+** -+** "trigram" tokenizer, case_sensitive=1 - FTS5_PATTERN_GLOB -+** "trigram" tokenizer, case_sensitive=0 (the default) - FTS5_PATTERN_LIKE -+** all other tokenizers - FTS5_PATTERN_NONE -+*/ -+static int sqlite3Fts5TokenizerPattern( -+ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), -+ Fts5Tokenizer *pTok -+){ -+ if( xCreate==fts5TriCreate ){ -+ TrigramTokenizer *p = (TrigramTokenizer*)pTok; -+ if( p->iFoldParam==0 ){ -+ return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; -+ } -+ } -+ return FTS5_PATTERN_NONE; -+} -+ -+/* -+** Return true if the tokenizer described by p->azArg[] is the trigram -+** tokenizer. This tokenizer needs to be loaded before xBestIndex is -+** called for the first time in order to correctly handle LIKE/GLOB. -+*/ -+static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig *p){ -+ return (p->nArg>=1 && 0==sqlite3_stricmp(p->azArg[0], "trigram")); -+} -+ -+ - /* - ** Register all built-in tokenizers with FTS5. - */ -@@ -228276,7 +260572,7 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ - } aBuiltin[] = { - { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, - { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, -- { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }}, -+ { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}}, - }; - - int rc = SQLITE_OK; /* Return code */ -@@ -228290,7 +260586,20 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ - 0 - ); - } -- -+ if( rc==SQLITE_OK ){ -+ fts5_tokenizer_v2 sPorter = { -+ 2, -+ fts5PorterCreate, -+ fts5PorterDelete, -+ fts5PorterTokenize -+ }; -+ rc = pApi->xCreateTokenizer_v2(pApi, -+ "porter", -+ (void*)pApi, -+ &sPorter, -+ 0 -+ ); -+ } - return rc; - } - -@@ -228660,6 +260969,9 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){ - default: return 1; } - break; - -+ -+ default: -+ return 1; - } - return 0; - } -@@ -229069,6 +261381,7 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){ - } - iTbl++; - } -+ aAscii[0] = 0; /* 0x00 is never a token character */ - } - - /* -@@ -229478,9 +261791,11 @@ struct Fts5VocabCursor { - - int bEof; /* True if this cursor is at EOF */ - Fts5IndexIter *pIter; /* Term/rowid iterator object */ -+ void *pStruct; /* From sqlite3Fts5StructureRef() */ - - int nLeTerm; /* Size of zLeTerm in bytes */ - char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ -+ int colUsed; /* Copy of sqlite3_index_info.colUsed */ - - /* These are used by 'col' tables only */ - int iCol; -@@ -229507,9 +261822,11 @@ struct Fts5VocabCursor { - /* - ** Bits for the mask used as the idxNum value by xBestIndex/xFilter. - */ --#define FTS5_VOCAB_TERM_EQ 0x01 --#define FTS5_VOCAB_TERM_GE 0x02 --#define FTS5_VOCAB_TERM_LE 0x04 -+#define FTS5_VOCAB_TERM_EQ 0x0100 -+#define FTS5_VOCAB_TERM_GE 0x0200 -+#define FTS5_VOCAB_TERM_LE 0x0400 -+ -+#define FTS5_VOCAB_COLUSED_MASK 0xFF - - - /* -@@ -229607,12 +261924,12 @@ static int fts5VocabInitVtab( - *pzErr = sqlite3_mprintf("wrong number of vtable arguments"); - rc = SQLITE_ERROR; - }else{ -- int nByte; /* Bytes of space to allocate */ -+ i64 nByte; /* Bytes of space to allocate */ - const char *zDb = bDb ? argv[3] : argv[1]; - const char *zTab = bDb ? argv[4] : argv[3]; - const char *zType = bDb ? argv[5] : argv[4]; -- int nDb = (int)strlen(zDb)+1; -- int nTab = (int)strlen(zTab)+1; -+ i64 nDb = strlen(zDb)+1; -+ i64 nTab = strlen(zTab)+1; - int eType = 0; - - rc = fts5VocabTableType(zType, pzErr, &eType); -@@ -229686,11 +262003,13 @@ static int fts5VocabBestIndexMethod( - int iTermEq = -1; - int iTermGe = -1; - int iTermLe = -1; -- int idxNum = 0; -+ int idxNum = (int)pInfo->colUsed; - int nArg = 0; - - UNUSED_PARAM(pUnused); - -+ assert( (pInfo->colUsed & FTS5_VOCAB_COLUSED_MASK)==pInfo->colUsed ); -+ - for(i=0; inConstraint; i++){ - struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; - if( p->usable==0 ) continue; -@@ -229782,7 +262101,7 @@ static int fts5VocabOpenMethod( - if( rc==SQLITE_OK ){ - pVTab->zErrMsg = sqlite3_mprintf( - "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl -- ); -+ ); - rc = SQLITE_ERROR; - } - }else{ -@@ -229791,7 +262110,7 @@ static int fts5VocabOpenMethod( - } - - if( rc==SQLITE_OK ){ -- int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); -+ i64 nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); - pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte); - } - -@@ -229811,6 +262130,8 @@ static int fts5VocabOpenMethod( - static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ - pCsr->rowid = 0; - sqlite3Fts5IterClose(pCsr->pIter); -+ sqlite3Fts5StructureRelease(pCsr->pStruct); -+ pCsr->pStruct = 0; - pCsr->pIter = 0; - sqlite3_free(pCsr->zLeTerm); - pCsr->nLeTerm = -1; -@@ -229888,9 +262209,11 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ - static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ - Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; - Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; -- int rc = SQLITE_OK; - int nCol = pCsr->pFts5->pConfig->nCol; -+ int rc; - -+ rc = sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct); -+ if( rc!=SQLITE_OK ) return rc; - pCsr->rowid++; - - if( pTab->eType==FTS5_VOCAB_INSTANCE ){ -@@ -229938,9 +262261,19 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ - - switch( pTab->eType ){ - case FTS5_VOCAB_ROW: -- if( eDetail==FTS5_DETAIL_FULL ){ -- while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ -- pCsr->aCnt[0]++; -+ /* Do not bother counting the number of instances if the "cnt" -+ ** column is not being read (according to colUsed). */ -+ if( eDetail==FTS5_DETAIL_FULL && (pCsr->colUsed & 0x04) ){ -+ while( iPosaCnt[] */ -+ pCsr->aCnt[0]++; -+ } - } - } - pCsr->aDoc[0]++; -@@ -230038,11 +262371,12 @@ static int fts5VocabFilterMethod( - if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++]; - if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++]; - if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++]; -+ pCsr->colUsed = (idxNum & FTS5_VOCAB_COLUSED_MASK); - - if( pEq ){ - zTerm = (const char *)sqlite3_value_text(pEq); - nTerm = sqlite3_value_bytes(pEq); -- f = 0; -+ f = FTS5INDEX_QUERY_NOTOKENDATA; - }else{ - if( pGe ){ - zTerm = (const char *)sqlite3_value_text(pGe); -@@ -230064,6 +262398,9 @@ static int fts5VocabFilterMethod( - if( rc==SQLITE_OK ){ - Fts5Index *pIndex = pCsr->pFts5->pIndex; - rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); -+ if( rc==SQLITE_OK ){ -+ pCsr->pStruct = sqlite3Fts5StructureRef(pIndex); -+ } - } - if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ - rc = fts5VocabInstanceNewTerm(pCsr); -@@ -230193,7 +262530,8 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ - /* xSavepoint */ 0, - /* xRelease */ 0, - /* xRollbackTo */ 0, -- /* xShadowName */ 0 -+ /* xShadowName */ 0, -+ /* xIntegrity */ 0 - }; - void *p = (void*)pGlobal; - -@@ -230201,7 +262539,7 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ - } - - -- -+/* Here ends the fts5.c composite file. */ - #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ - - /************** End of fts5.c ************************************************/ -@@ -230238,6 +262576,16 @@ SQLITE_EXTENSION_INIT1 - - #ifndef SQLITE_OMIT_VIRTUALTABLE - -+ -+#define STMT_NUM_INTEGER_COLUMN 10 -+typedef struct StmtRow StmtRow; -+struct StmtRow { -+ sqlite3_int64 iRowid; /* Rowid value */ -+ char *zSql; /* column "sql" */ -+ int aCol[STMT_NUM_INTEGER_COLUMN+1]; /* all other column values */ -+ StmtRow *pNext; /* Next row to return */ -+}; -+ - /* stmt_vtab is a subclass of sqlite3_vtab which will - ** serve as the underlying representation of a stmt virtual table - */ -@@ -230255,8 +262603,7 @@ typedef struct stmt_cursor stmt_cursor; - struct stmt_cursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - sqlite3 *db; /* Database connection for this cursor */ -- sqlite3_stmt *pStmt; /* Statement cursor is currently pointing at */ -- sqlite3_int64 iRowid; /* The rowid */ -+ StmtRow *pRow; /* Current row */ - }; - - /* -@@ -230296,11 +262643,15 @@ static int stmtConnect( - #define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */ - - -+ (void)pAux; -+ (void)argc; -+ (void)argv; -+ (void)pzErr; - rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep," - "reprep,run,mem)"); - if( rc==SQLITE_OK ){ -- pNew = sqlite3_malloc( sizeof(*pNew) ); -+ pNew = sqlite3_malloc64( sizeof(*pNew) ); - *ppVtab = (sqlite3_vtab*)pNew; - if( pNew==0 ) return SQLITE_NOMEM; - memset(pNew, 0, sizeof(*pNew)); -@@ -230322,7 +262673,7 @@ static int stmtDisconnect(sqlite3_vtab *pVtab){ - */ - static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ - stmt_cursor *pCur; -- pCur = sqlite3_malloc( sizeof(*pCur) ); -+ pCur = sqlite3_malloc64( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - pCur->db = ((stmt_vtab*)p)->db; -@@ -230330,10 +262681,21 @@ static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ - return SQLITE_OK; - } - -+static void stmtCsrReset(stmt_cursor *pCur){ -+ StmtRow *pRow = 0; -+ StmtRow *pNext = 0; -+ for(pRow=pCur->pRow; pRow; pRow=pNext){ -+ pNext = pRow->pNext; -+ sqlite3_free(pRow); -+ } -+ pCur->pRow = 0; -+} -+ - /* - ** Destructor for a stmt_cursor. - */ - static int stmtClose(sqlite3_vtab_cursor *cur){ -+ stmtCsrReset((stmt_cursor*)cur); - sqlite3_free(cur); - return SQLITE_OK; - } -@@ -230344,8 +262706,9 @@ static int stmtClose(sqlite3_vtab_cursor *cur){ - */ - static int stmtNext(sqlite3_vtab_cursor *cur){ - stmt_cursor *pCur = (stmt_cursor*)cur; -- pCur->iRowid++; -- pCur->pStmt = sqlite3_next_stmt(pCur->db, pCur->pStmt); -+ StmtRow *pNext = pCur->pRow->pNext; -+ sqlite3_free(pCur->pRow); -+ pCur->pRow = pNext; - return SQLITE_OK; - } - -@@ -230359,39 +262722,11 @@ static int stmtColumn( - int i /* Which column to return */ - ){ - stmt_cursor *pCur = (stmt_cursor*)cur; -- switch( i ){ -- case STMT_COLUMN_SQL: { -- sqlite3_result_text(ctx, sqlite3_sql(pCur->pStmt), -1, SQLITE_TRANSIENT); -- break; -- } -- case STMT_COLUMN_NCOL: { -- sqlite3_result_int(ctx, sqlite3_column_count(pCur->pStmt)); -- break; -- } -- case STMT_COLUMN_RO: { -- sqlite3_result_int(ctx, sqlite3_stmt_readonly(pCur->pStmt)); -- break; -- } -- case STMT_COLUMN_BUSY: { -- sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt)); -- break; -- } -- default: { -- assert( i==STMT_COLUMN_MEM ); -- i = SQLITE_STMTSTATUS_MEMUSED + -- STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP; -- /* Fall thru */ -- } -- case STMT_COLUMN_NSCAN: -- case STMT_COLUMN_NSORT: -- case STMT_COLUMN_NAIDX: -- case STMT_COLUMN_NSTEP: -- case STMT_COLUMN_REPREP: -- case STMT_COLUMN_RUN: { -- sqlite3_result_int(ctx, sqlite3_stmt_status(pCur->pStmt, -- i-STMT_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0)); -- break; -- } -+ StmtRow *pRow = pCur->pRow; -+ if( i==STMT_COLUMN_SQL ){ -+ sqlite3_result_text(ctx, pRow->zSql, -1, SQLITE_TRANSIENT); -+ }else{ -+ sqlite3_result_int(ctx, pRow->aCol[i]); - } - return SQLITE_OK; - } -@@ -230402,7 +262737,7 @@ static int stmtColumn( - */ - static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - stmt_cursor *pCur = (stmt_cursor*)cur; -- *pRowid = pCur->iRowid; -+ *pRowid = pCur->pRow->iRowid; - return SQLITE_OK; - } - -@@ -230412,7 +262747,7 @@ static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - */ - static int stmtEof(sqlite3_vtab_cursor *cur){ - stmt_cursor *pCur = (stmt_cursor*)cur; -- return pCur->pStmt==0; -+ return pCur->pRow==0; - } - - /* -@@ -230427,9 +262762,57 @@ static int stmtFilter( - int argc, sqlite3_value **argv - ){ - stmt_cursor *pCur = (stmt_cursor *)pVtabCursor; -- pCur->pStmt = 0; -- pCur->iRowid = 0; -- return stmtNext(pVtabCursor); -+ sqlite3_stmt *p = 0; -+ sqlite3_int64 iRowid = 1; -+ StmtRow **ppRow = 0; -+ -+ (void)idxNum; -+ (void)idxStr; -+ (void)argc; -+ (void)argv; -+ stmtCsrReset(pCur); -+ ppRow = &pCur->pRow; -+ for(p=sqlite3_next_stmt(pCur->db, 0); p; p=sqlite3_next_stmt(pCur->db, p)){ -+ const char *zSql = sqlite3_sql(p); -+ sqlite3_int64 nSql = zSql ? strlen(zSql)+1 : 0; -+ StmtRow *pNew = (StmtRow*)sqlite3_malloc64(sizeof(StmtRow) + nSql); -+ -+ if( pNew==0 ) return SQLITE_NOMEM; -+ memset(pNew, 0, sizeof(StmtRow)); -+ if( zSql ){ -+ pNew->zSql = (char*)&pNew[1]; -+ memcpy(pNew->zSql, zSql, nSql); -+ } -+ pNew->aCol[STMT_COLUMN_NCOL] = sqlite3_column_count(p); -+ pNew->aCol[STMT_COLUMN_RO] = sqlite3_stmt_readonly(p); -+ pNew->aCol[STMT_COLUMN_BUSY] = sqlite3_stmt_busy(p); -+ pNew->aCol[STMT_COLUMN_NSCAN] = sqlite3_stmt_status( -+ p, SQLITE_STMTSTATUS_FULLSCAN_STEP, 0 -+ ); -+ pNew->aCol[STMT_COLUMN_NSORT] = sqlite3_stmt_status( -+ p, SQLITE_STMTSTATUS_SORT, 0 -+ ); -+ pNew->aCol[STMT_COLUMN_NAIDX] = sqlite3_stmt_status( -+ p, SQLITE_STMTSTATUS_AUTOINDEX, 0 -+ ); -+ pNew->aCol[STMT_COLUMN_NSTEP] = sqlite3_stmt_status( -+ p, SQLITE_STMTSTATUS_VM_STEP, 0 -+ ); -+ pNew->aCol[STMT_COLUMN_REPREP] = sqlite3_stmt_status( -+ p, SQLITE_STMTSTATUS_REPREPARE, 0 -+ ); -+ pNew->aCol[STMT_COLUMN_RUN] = sqlite3_stmt_status( -+ p, SQLITE_STMTSTATUS_RUN, 0 -+ ); -+ pNew->aCol[STMT_COLUMN_MEM] = sqlite3_stmt_status( -+ p, SQLITE_STMTSTATUS_MEMUSED, 0 -+ ); -+ pNew->iRowid = iRowid++; -+ *ppRow = pNew; -+ ppRow = &pNew->pNext; -+ } -+ -+ return SQLITE_OK; - } - - /* -@@ -230442,6 +262825,7 @@ static int stmtBestIndex( - sqlite3_vtab *tab, - sqlite3_index_info *pIdxInfo - ){ -+ (void)tab; - pIdxInfo->estimatedCost = (double)500; - pIdxInfo->estimatedRows = 500; - return SQLITE_OK; -@@ -230476,6 +262860,7 @@ static sqlite3_module stmtModule = { - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0, /* xShadowName */ -+ 0 /* xIntegrity */ - }; - - #endif /* SQLITE_OMIT_VIRTUALTABLE */ -@@ -230508,10 +262893,7 @@ SQLITE_API int sqlite3_stmt_init( - #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ - - /************** End of stmt.c ************************************************/ --#if __LINE__!=230511 --#undef SQLITE_SOURCE_ID --#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0alt2" --#endif - /* Return the source-id for this library */ - SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } -+#endif /* SQLITE_AMALGAMATION */ - /************************** End of sqlite3.c ******************************/ -diff --git a/src/third_party/sqlite3.h b/src/third_party/sqlite3.h -index 910b687a..c2ed7503 100644 ---- a/src/third_party/sqlite3.h -+++ b/src/third_party/sqlite3.h -@@ -43,7 +43,30 @@ extern "C" { - - - /* --** Provide the ability to override linkage features of the interface. -+** Facilitate override of interface linkage and calling conventions. -+** Be aware that these macros may not be used within this particular -+** translation of the amalgamation and its associated header file. -+** -+** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the -+** compiler that the target identifier should have external linkage. -+** -+** The SQLITE_CDECL macro is used to set the calling convention for -+** public functions that accept a variable number of arguments. -+** -+** The SQLITE_APICALL macro is used to set the calling convention for -+** public functions that accept a fixed number of arguments. -+** -+** The SQLITE_STDCALL macro is no longer used and is now deprecated. -+** -+** The SQLITE_CALLBACK macro is used to set the calling convention for -+** function pointers. -+** -+** The SQLITE_SYSAPI macro is used to set the calling convention for -+** functions provided by the operating system. -+** -+** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and -+** SQLITE_SYSAPI macros are used only when building for environments -+** that require non-default calling conventions. - */ - #ifndef SQLITE_EXTERN - # define SQLITE_EXTERN extern -@@ -110,7 +133,7 @@ extern "C" { - ** - ** Since [version 3.6.18] ([dateof:3.6.18]), - ** SQLite source code has been stored in the --** Fossil configuration management -+** Fossil configuration management - ** system. ^The SQLITE_SOURCE_ID macro evaluates to - ** a string which identifies a particular check-in of SQLite - ** within its configuration management system. ^The SQLITE_SOURCE_ID -@@ -123,9 +146,9 @@ extern "C" { - ** [sqlite3_libversion_number()], [sqlite3_sourceid()], - ** [sqlite_version()] and [sqlite_source_id()]. - */ --#define SQLITE_VERSION "3.33.0" --#define SQLITE_VERSION_NUMBER 3033000 --#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f" -+#define SQLITE_VERSION "3.50.4" -+#define SQLITE_VERSION_NUMBER 3050004 -+#define SQLITE_SOURCE_ID "2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3" - - /* - ** CAPI3REF: Run-Time Library Version Numbers -@@ -397,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); - ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. - **
      • The application must not modify the SQL statement text passed into - ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. -+**
      • The application must not dereference the arrays or string pointers -+** passed as the 3rd and 4th callback parameters after it returns. - ** - */ - SQLITE_API int sqlite3_exec( -@@ -504,6 +529,8 @@ SQLITE_API int sqlite3_exec( - #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) - #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) - #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) -+#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) -+#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) - #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) - #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) - #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) -@@ -536,12 +563,14 @@ SQLITE_API int sqlite3_exec( - #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) - #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) - #define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8)) -+#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) - #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) - #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) -+#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) - #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) - #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) - #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) --#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) -+#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ - - /* - ** CAPI3REF: Flags For File Open Operations -@@ -549,6 +578,19 @@ SQLITE_API int sqlite3_exec( - ** These bit values are intended for use in the - ** 3rd parameter to the [sqlite3_open_v2()] interface and - ** in the 4th parameter to the [sqlite3_vfs.xOpen] method. -+** -+** Only those flags marked as "Ok for sqlite3_open_v2()" may be -+** used as the third argument to the [sqlite3_open_v2()] interface. -+** The other flags have historically been ignored by sqlite3_open_v2(), -+** though future versions of SQLite might change so that an error is -+** raised if any of the disallowed bits are passed into sqlite3_open_v2(). -+** Applications should not depend on the historical behavior. -+** -+** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into -+** [sqlite3_open_v2()] does *not* cause the underlying database file -+** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into -+** [sqlite3_open_v2()] has historically be a no-op and might become an -+** error in future versions of SQLite. - */ - #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ - #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ -@@ -571,6 +613,7 @@ SQLITE_API int sqlite3_exec( - #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ - #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ - #define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlite3_open_v2() */ -+#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */ - - /* Reserved: 0x00F00000 */ - /* Legacy compatibility: */ -@@ -609,6 +652,13 @@ SQLITE_API int sqlite3_exec( - ** filesystem supports doing multiple write operations atomically when those - ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and - ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. -+** -+** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read -+** from the database file in amounts that are not a multiple of the -+** page size and that do not begin at a page boundary. Without this -+** property, SQLite is careful to only do full-page reads and write -+** on aligned pages, with the one exception that it will do a sub-page -+** read of the first page to access the database header. - */ - #define SQLITE_IOCAP_ATOMIC 0x00000001 - #define SQLITE_IOCAP_ATOMIC512 0x00000002 -@@ -625,19 +675,24 @@ SQLITE_API int sqlite3_exec( - #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 - #define SQLITE_IOCAP_IMMUTABLE 0x00002000 - #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 -+#define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 - - /* - ** CAPI3REF: File Locking Levels - ** - ** SQLite uses one of these integer values as the second - ** argument to calls it makes to the xLock() and xUnlock() methods --** of an [sqlite3_io_methods] object. -+** of an [sqlite3_io_methods] object. These values are ordered from -+** lest restrictive to most restrictive. -+** -+** The argument to xLock() is always SHARED or higher. The argument to -+** xUnlock is either SHARED or NONE. - */ --#define SQLITE_LOCK_NONE 0 --#define SQLITE_LOCK_SHARED 1 --#define SQLITE_LOCK_RESERVED 2 --#define SQLITE_LOCK_PENDING 3 --#define SQLITE_LOCK_EXCLUSIVE 4 -+#define SQLITE_LOCK_NONE 0 /* xUnlock() only */ -+#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */ -+#define SQLITE_LOCK_RESERVED 2 /* xLock() only */ -+#define SQLITE_LOCK_PENDING 3 /* xLock() only */ -+#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */ - - /* - ** CAPI3REF: Synchronization Type Flags -@@ -715,11 +770,18 @@ struct sqlite3_file { - **
      • [SQLITE_LOCK_PENDING], or - **
      • [SQLITE_LOCK_EXCLUSIVE]. - ** --** xLock() increases the lock. xUnlock() decreases the lock. -+** xLock() upgrades the database file lock. In other words, xLock() moves the -+** database file lock in the direction NONE toward EXCLUSIVE. The argument to -+** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never -+** SQLITE_LOCK_NONE. If the database file lock is already at or above the -+** requested lock, then the call to xLock() is a no-op. -+** xUnlock() downgrades the database file lock to either SHARED or NONE. -+** If the lock is already at or below the requested lock state, then the call -+** to xUnlock() is a no-op. - ** The xCheckReservedLock() method checks whether any database connection, - ** either in this process or in some other process, is holding a RESERVED, --** PENDING, or EXCLUSIVE lock on the file. It returns true --** if such a lock exists and false otherwise. -+** PENDING, or EXCLUSIVE lock on the file. It returns, via its output -+** pointer parameter, true if such a lock exists and false otherwise. - ** - ** The xFileControl() method is a generic interface that allows custom - ** VFS implementations to directly control an open file using the -@@ -760,6 +822,7 @@ struct sqlite3_file { - **
      • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] - **
      • [SQLITE_IOCAP_IMMUTABLE] - **
      • [SQLITE_IOCAP_BATCH_ATOMIC] -+**
      • [SQLITE_IOCAP_SUBPAGE_READ] - ** - ** - ** The SQLITE_IOCAP_ATOMIC property means that all writes of -@@ -820,9 +883,8 @@ struct sqlite3_io_methods { - ** opcode causes the xFileControl method to write the current state of - ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], - ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) --** into an integer that the pArg argument points to. This capability --** is used during testing and is only available when the SQLITE_TEST --** compile-time option is used. -+** into an integer that the pArg argument points to. -+** This capability is only available if SQLite is compiled with [SQLITE_DEBUG]. - ** - **
      • [[SQLITE_FCNTL_SIZE_HINT]] - ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS -@@ -1038,6 +1100,11 @@ struct sqlite3_io_methods { - ** pointed to by the pArg argument. This capability is used during testing - ** and only needs to be supported when SQLITE_TEST is defined. - ** -+**
      • [[SQLITE_FCNTL_NULL_IO]] -+** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor -+** or file handle for the [sqlite3_file] object such that it will no longer -+** read or write to the database file. -+** - **
      • [[SQLITE_FCNTL_WAL_BLOCK]] - ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might - ** be advantageous to block on the next WAL lock if the lock is not immediately -@@ -1096,6 +1163,12 @@ struct sqlite3_io_methods { - ** the value that M is to be set to. Before returning, the 32-bit signed - ** integer is overwritten with the previous value of M. - ** -+**
      • [[SQLITE_FCNTL_BLOCK_ON_CONNECT]] -+** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the -+** VFS to block when taking a SHARED lock to connect to a wal mode database. -+** This is used to implement the functionality associated with -+** SQLITE_SETLK_BLOCK_ON_CONNECT. -+** - **
      • [[SQLITE_FCNTL_DATA_VERSION]] - ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to - ** a database file. The argument is a pointer to a 32-bit unsigned integer. -@@ -1126,6 +1199,28 @@ struct sqlite3_io_methods { - ** in wal mode after the client has finished copying pages from the wal - ** file to the database file, but before the *-shm file is updated to - ** record the fact that the pages have been checkpointed. -+** -+**
      • [[SQLITE_FCNTL_EXTERNAL_READER]] -+** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect -+** whether or not there is a database client in another process with a wal-mode -+** transaction open on the database or not. It is only available on unix.The -+** (void*) argument passed with this file-control should be a pointer to a -+** value of type (int). The integer value is set to 1 if the database is a wal -+** mode database and there exists at least one client in another process that -+** currently has an SQL transaction open on the database. It is set to 0 if -+** the database is not a wal-mode db, or if there is no such connection in any -+** other process. This opcode cannot be used to detect transactions opened -+** by clients within the current process, only within other processes. -+** -+**
      • [[SQLITE_FCNTL_CKSM_FILE]] -+** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the -+** [checksum VFS shim] only. -+** -+**
      • [[SQLITE_FCNTL_RESET_CACHE]] -+** If there is currently no transaction open on the database, and the -+** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control -+** purges the contents of the in-memory page cache. If there is an open -+** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. - ** - */ - #define SQLITE_FCNTL_LOCKSTATE 1 -@@ -1166,6 +1261,11 @@ struct sqlite3_io_methods { - #define SQLITE_FCNTL_CKPT_DONE 37 - #define SQLITE_FCNTL_RESERVE_BYTES 38 - #define SQLITE_FCNTL_CKPT_START 39 -+#define SQLITE_FCNTL_EXTERNAL_READER 40 -+#define SQLITE_FCNTL_CKSM_FILE 41 -+#define SQLITE_FCNTL_RESET_CACHE 42 -+#define SQLITE_FCNTL_NULL_IO 43 -+#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 - - /* deprecated names */ - #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE -@@ -1195,6 +1295,26 @@ typedef struct sqlite3_mutex sqlite3_mutex; - */ - typedef struct sqlite3_api_routines sqlite3_api_routines; - -+/* -+** CAPI3REF: File Name -+** -+** Type [sqlite3_filename] is used by SQLite to pass filenames to the -+** xOpen method of a [VFS]. It may be cast to (const char*) and treated -+** as a normal, nul-terminated, UTF-8 buffer containing the filename, but -+** may also be passed to special APIs such as: -+** -+**
          -+**
        • sqlite3_filename_database() -+**
        • sqlite3_filename_journal() -+**
        • sqlite3_filename_wal() -+**
        • sqlite3_uri_parameter() -+**
        • sqlite3_uri_boolean() -+**
        • sqlite3_uri_int64() -+**
        • sqlite3_uri_key() -+**
        -+*/ -+typedef const char *sqlite3_filename; -+ - /* - ** CAPI3REF: OS Interface Object - ** -@@ -1373,7 +1493,7 @@ struct sqlite3_vfs { - sqlite3_vfs *pNext; /* Next registered VFS */ - const char *zName; /* Name of this virtual file system */ - void *pAppData; /* Pointer to application-specific data */ -- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, -+ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*, - int flags, int *pOutFlags); - int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); - int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); -@@ -1560,20 +1680,23 @@ SQLITE_API int sqlite3_os_end(void); - ** must ensure that no other SQLite interfaces are invoked by other - ** threads while sqlite3_config() is running. - ** --** The sqlite3_config() interface --** may only be invoked prior to library initialization using --** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. --** ^If sqlite3_config() is called after [sqlite3_initialize()] and before --** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. --** Note, however, that ^sqlite3_config() can be called as part of the --** implementation of an application-defined [sqlite3_os_init()]. --** - ** The first argument to sqlite3_config() is an integer - ** [configuration option] that determines - ** what property of SQLite is to be configured. Subsequent arguments - ** vary depending on the [configuration option] - ** in the first argument. - ** -+** For most configuration options, the sqlite3_config() interface -+** may only be invoked prior to library initialization using -+** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -+** The exceptional configuration options that may be invoked at any time -+** are called "anytime configuration options". -+** ^If sqlite3_config() is called after [sqlite3_initialize()] and before -+** [sqlite3_shutdown()] with a first argument that is not an anytime -+** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. -+** Note, however, that ^sqlite3_config() can be called as part of the -+** implementation of an application-defined [sqlite3_os_init()]. -+** - ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. - ** ^If the option is unknown or SQLite is unable to set the option - ** then this routine returns a non-zero [error code]. -@@ -1681,6 +1804,23 @@ struct sqlite3_mem_methods { - ** These constants are the available integer configuration options that - ** can be passed as the first argument to the [sqlite3_config()] interface. - ** -+** Most of the configuration options for sqlite3_config() -+** will only work if invoked prior to [sqlite3_initialize()] or after -+** [sqlite3_shutdown()]. The few exceptions to this rule are called -+** "anytime configuration options". -+** ^Calling [sqlite3_config()] with a first argument that is not an -+** anytime configuration option in between calls to [sqlite3_initialize()] and -+** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE. -+** -+** The set of anytime configuration options can change (by insertions -+** and/or deletions) from one release of SQLite to the next. -+** As of SQLite version 3.42.0, the complete set of anytime configuration -+** options is: -+**
          -+**
        • SQLITE_CONFIG_LOG -+**
        • SQLITE_CONFIG_PCACHE_HDRSZ -+**
        -+** - ** New configuration options may be added in future releases of SQLite. - ** Existing configuration options might be discontinued. Applications - ** should check the return code from [sqlite3_config()] to make sure that -@@ -1856,13 +1996,16 @@ struct sqlite3_mem_methods { - ** - ** [[SQLITE_CONFIG_LOOKASIDE]]
        SQLITE_CONFIG_LOOKASIDE
        - **
        ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine --** the default size of lookaside memory on each [database connection]. -+** the default size of [lookaside memory] on each [database connection]. - ** The first argument is the --** size of each lookaside buffer slot and the second is the number of --** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE --** sets the default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] --** option to [sqlite3_db_config()] can be used to change the lookaside --** configuration on individual connections.)^
        -+** size of each lookaside buffer slot ("sz") and the second is the number of -+** slots allocated to each database connection ("cnt").)^ -+** ^(SQLITE_CONFIG_LOOKASIDE sets the default lookaside size. -+** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can -+** be used to change the lookaside configuration on individual connections.)^ -+** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the -+** default lookaside configuration at compile-time. -+** - ** - ** [[SQLITE_CONFIG_PCACHE2]]
        SQLITE_CONFIG_PCACHE2
        - **
        ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is -@@ -2011,7 +2154,7 @@ struct sqlite3_mem_methods { - ** is stored in each sorted record and the required column values loaded - ** from the database as records are returned in sorted order. The default - ** value for this option is to never use this optimization. Specifying a --** negative value for this option restores the default behaviour. -+** negative value for this option restores the default behavior. - ** This option is only available if SQLite is compiled with the - ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. - ** -@@ -2025,30 +2168,46 @@ struct sqlite3_mem_methods { - ** configuration setting is never used, then the default maximum is determined - ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that - ** compile-time option is not set, then the default maximum is 1073741824. -+** -+** [[SQLITE_CONFIG_ROWID_IN_VIEW]] -+**
        SQLITE_CONFIG_ROWID_IN_VIEW -+**
        The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability -+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is -+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability -+** defaults to on. This configuration option queries the current setting or -+** changes the setting to off or on. The argument is a pointer to an integer. -+** If that integer initially holds a value of 1, then the ability for VIEWs to -+** have ROWIDs is activated. If the integer initially holds zero, then the -+** ability is deactivated. Any other initial value for the integer leaves the -+** setting unchanged. After changes, if any, the integer is written with -+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite -+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and -+** recommended case) then the integer is always filled with zero, regardless -+** if its initial value. - ** - */ --#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ --#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ --#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ --#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ --#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ --#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ --#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ --#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ --#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ --#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ --#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ --/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ --#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ --#define SQLITE_CONFIG_PCACHE 14 /* no-op */ --#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ --#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ --#define SQLITE_CONFIG_URI 17 /* int */ --#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ --#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ -+#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ -+#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ -+#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ -+#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ -+#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ -+#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ -+#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ -+#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ -+#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ -+#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ -+#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ -+/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -+#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -+#define SQLITE_CONFIG_PCACHE 14 /* no-op */ -+#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ -+#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ -+#define SQLITE_CONFIG_URI 17 /* int */ -+#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ -+#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ - #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ --#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ --#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ -+#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ -+#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ - #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ - #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ - #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ -@@ -2056,12 +2215,21 @@ struct sqlite3_mem_methods { - #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ - #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ - #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ -+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ - - /* - ** CAPI3REF: Database Connection Configuration Options - ** - ** These constants are the available integer configuration options that --** can be passed as the second argument to the [sqlite3_db_config()] interface. -+** can be passed as the second parameter to the [sqlite3_db_config()] interface. -+** -+** The [sqlite3_db_config()] interface is a var-args functions. It takes a -+** variable number of parameters, though always at least two. The number of -+** parameters passed into sqlite3_db_config() depends on which of these -+** constants is given as the second parameter. This documentation page -+** refers to parameters beyond the second as "arguments". Thus, when this -+** page says "the N-th argument" it means "the N-th parameter past the -+** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()". - ** - ** New configuration options may be added in future releases of SQLite. - ** Existing configuration options might be discontinued. Applications -@@ -2073,31 +2241,57 @@ struct sqlite3_mem_methods { - **
        - ** [[SQLITE_DBCONFIG_LOOKASIDE]] - **
        SQLITE_DBCONFIG_LOOKASIDE
        --**
        ^This option takes three additional arguments that determine the --** [lookaside memory allocator] configuration for the [database connection]. --** ^The first argument (the third parameter to [sqlite3_db_config()] is a -+**
        The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the -+** configuration of the [lookaside memory allocator] within a database -+** connection. -+** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are not -+** in the [DBCONFIG arguments|usual format]. -+** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two, -+** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE -+** should have a total of five parameters. -+**
          -+**
        1. The first argument ("buf") is a - ** pointer to a memory buffer to use for lookaside memory. --** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb --** may be NULL in which case SQLite will allocate the --** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the --** size of each lookaside buffer slot. ^The third argument is the number of --** slots. The size of the buffer in the first argument must be greater than --** or equal to the product of the second and third arguments. The buffer --** must be aligned to an 8-byte boundary. ^If the second argument to --** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally --** rounded down to the next smaller multiple of 8. ^(The lookaside memory -+** The first argument may be NULL in which case SQLite will allocate the -+** lookaside buffer itself using [sqlite3_malloc()]. -+**

        2. The second argument ("sz") is the -+** size of each lookaside buffer slot. Lookaside is disabled if "sz" -+** is less than 8. The "sz" argument should be a multiple of 8 less than -+** 65536. If "sz" does not meet this constraint, it is reduced in size until -+** it does. -+**

        3. The third argument ("cnt") is the number of slots. Lookaside is disabled -+** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so -+** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" -+** parameter is usually chosen so that the product of "sz" and "cnt" is less -+** than 1,000,000. -+**

        -+**

        If the "buf" argument is not NULL, then it must -+** point to a memory buffer with a size that is greater than -+** or equal to the product of "sz" and "cnt". -+** The buffer must be aligned to an 8-byte boundary. -+** The lookaside memory - ** configuration for a database connection can only be changed when that - ** connection is not currently using lookaside memory, or in other words --** when the "current value" returned by --** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. -+** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero. - ** Any attempt to change the lookaside memory configuration when lookaside - ** memory is in use leaves the configuration unchanged and returns --** [SQLITE_BUSY].)^

        -+** [SQLITE_BUSY]. -+** If the "buf" argument is NULL and an attempt -+** to allocate memory based on "sz" and "cnt" fails, then -+** lookaside is silently disabled. -+**

        -+** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the -+** default lookaside configuration at initialization. The -+** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside -+** configuration at compile-time. Typical values for lookaside are 1200 for -+** "sz" and 40 to 100 for "cnt". -+**

        - ** - ** [[SQLITE_DBCONFIG_ENABLE_FKEY]] - **
        SQLITE_DBCONFIG_ENABLE_FKEY
        - **
        ^This option is used to enable or disable the enforcement of --** [foreign key constraints]. There should be two additional arguments. -+** [foreign key constraints]. This is the same setting that is -+** enabled or disabled by the [PRAGMA foreign_keys] statement. - ** The first argument is an integer which is 0 to disable FK enforcement, - ** positive to enable FK enforcement or negative to leave FK enforcement - ** unchanged. The second parameter is a pointer to an integer into which -@@ -2114,25 +2308,37 @@ struct sqlite3_mem_methods { - ** The second parameter is a pointer to an integer into which - ** is written 0 or 1 to indicate whether triggers are disabled or enabled - ** following this call. The second parameter may be a NULL pointer, in --** which case the trigger setting is not reported back.
        -+** which case the trigger setting is not reported back. -+** -+**

        Originally this option disabled all triggers. ^(However, since -+** SQLite version 3.35.0, TEMP triggers are still allowed even if -+** this option is off. So, in other words, this option now only disables -+** triggers in the main database schema or in the schemas of [ATTACH]-ed -+** databases.)^ - ** - ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] - **

        SQLITE_DBCONFIG_ENABLE_VIEW
        - **
        ^This option is used to enable or disable [CREATE VIEW | views]. --** There should be two additional arguments. -+** There must be two additional arguments. - ** The first argument is an integer which is 0 to disable views, - ** positive to enable views or negative to leave the setting unchanged. - ** The second parameter is a pointer to an integer into which - ** is written 0 or 1 to indicate whether views are disabled or enabled - ** following this call. The second parameter may be a NULL pointer, in --** which case the view setting is not reported back.
        -+** which case the view setting is not reported back. -+** -+**

        Originally this option disabled all views. ^(However, since -+** SQLite version 3.35.0, TEMP views are still allowed even if -+** this option is off. So, in other words, this option now only disables -+** views in the main database schema or in the schemas of ATTACH-ed -+** databases.)^ - ** - ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] - **

        SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
        - **
        ^This option is used to enable or disable the - ** [fts3_tokenizer()] function which is part of the - ** [FTS3] full-text search engine extension. --** There should be two additional arguments. -+** There must be two additional arguments. - ** The first argument is an integer which is 0 to disable fts3_tokenizer() or - ** positive to enable fts3_tokenizer() or negative to leave the setting - ** unchanged. -@@ -2147,7 +2353,7 @@ struct sqlite3_mem_methods { - ** interface independently of the [load_extension()] SQL function. - ** The [sqlite3_enable_load_extension()] API enables or disables both the - ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. --** There should be two additional arguments. -+** There must be two additional arguments. - ** When the first argument to this interface is 1, then only the C-API is - ** enabled and the SQL function remains disabled. If the first argument to - ** this interface is 0, then both the C-API and the SQL function are disabled. -@@ -2161,23 +2367,30 @@ struct sqlite3_mem_methods { - ** - ** [[SQLITE_DBCONFIG_MAINDBNAME]]
        SQLITE_DBCONFIG_MAINDBNAME
        - **
        ^This option is used to change the name of the "main" database --** schema. ^The sole argument is a pointer to a constant UTF8 string --** which will become the new schema name in place of "main". ^SQLite --** does not make a copy of the new main schema name string, so the application --** must ensure that the argument passed into this DBCONFIG option is unchanged --** until after the database connection closes. -+** schema. This option does not follow the -+** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. -+** This option takes exactly one additional argument so that the -+** [sqlite3_db_config()] call has a total of three parameters. The -+** extra argument must be a pointer to a constant UTF8 string which -+** will become the new schema name in place of "main". ^SQLite does -+** not make a copy of the new main schema name string, so the application -+** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME -+** is unchanged until after the database connection closes. - **
        - ** - ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] - **
        SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
        --**
        Usually, when a database in wal mode is closed or detached from a --** database handle, SQLite checks if this will mean that there are now no --** connections at all to the database. If so, it performs a checkpoint --** operation before closing the connection. This option may be used to --** override this behaviour. The first parameter passed to this operation --** is an integer - positive to disable checkpoints-on-close, or zero (the --** default) to enable them, and negative to leave the setting unchanged. --** The second parameter is a pointer to an integer -+**
        Usually, when a database in [WAL mode] is closed or detached from a -+** database handle, SQLite checks if if there are other connections to the -+** same database, and if there are no other database connection (if the -+** connection being closed is the last open connection to the database), -+** then SQLite performs a [checkpoint] before closing the connection and -+** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can -+** be used to override that behavior. The first argument passed to this -+** operation (the third parameter to [sqlite3_db_config()]) is an integer -+** which is positive to disable checkpoints-on-close, or zero (the default) -+** to enable them, and negative to leave the setting unchanged. -+** The second argument (the fourth parameter) is a pointer to an integer - ** into which is written 0 or 1 to indicate whether checkpoints-on-close - ** have been disabled - 0 if they are not disabled, 1 if they are. - **
        -@@ -2227,8 +2440,12 @@ struct sqlite3_mem_methods { - **
      • sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); - ** - ** Because resetting a database is destructive and irreversible, the --** process requires the use of this obscure API and multiple steps to help --** ensure that it does not happen by accident. -+** process requires the use of this obscure API and multiple steps to -+** help ensure that it does not happen by accident. Because this -+** feature must be capable of resetting corrupt databases, and -+** shutting down virtual tables may require access to that corrupt -+** storage, the library must abandon any installed virtual tables -+** without calling their xDestroy() methods. - ** - ** [[SQLITE_DBCONFIG_DEFENSIVE]]
        SQLITE_DBCONFIG_DEFENSIVE
        - **
        The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the -@@ -2239,6 +2456,7 @@ struct sqlite3_mem_methods { - **
          - **
        • The [PRAGMA writable_schema=ON] statement. - **
        • The [PRAGMA journal_mode=OFF] statement. -+**
        • The [PRAGMA schema_version=N] statement. - **
        • Writes to the [sqlite_dbpage] virtual table. - **
        • Direct writes to [shadow tables]. - **
        -@@ -2266,7 +2484,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_DQS_DML]] --**
        SQLITE_DBCONFIG_DQS_DML -+**
        SQLITE_DBCONFIG_DQS_DML
        - **
        The SQLITE_DBCONFIG_DQS_DML option activates or deactivates - ** the legacy [double-quoted string literal] misfeature for DML statements - ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The -@@ -2275,7 +2493,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_DQS_DDL]] --**
        SQLITE_DBCONFIG_DQS_DDL -+**
        SQLITE_DBCONFIG_DQS_DDL
        - **
        The SQLITE_DBCONFIG_DQS option activates or deactivates - ** the legacy [double-quoted string literal] misfeature for DDL statements, - ** such as CREATE TABLE and CREATE INDEX. The -@@ -2284,7 +2502,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] --**
        SQLITE_DBCONFIG_TRUSTED_SCHEMA -+**
        SQLITE_DBCONFIG_TRUSTED_SCHEMA
        - **
        The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to - ** assume that database schemas are untainted by malicious content. - ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite -@@ -2304,7 +2522,7 @@ struct sqlite3_mem_methods { - **
        - ** - ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] --**
        SQLITE_DBCONFIG_LEGACY_FILE_FORMAT -+**
        SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
        - **
        The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates - ** the legacy file format flag. When activated, this flag causes all newly - ** created database file to have a schema format version number (the 4-byte -@@ -2313,7 +2531,7 @@ struct sqlite3_mem_methods { - ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, - ** newly created databases are generally not understandable by SQLite versions - ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there --** is now scarcely any need to generated database files that are compatible -+** is now scarcely any need to generate database files that are compatible - ** all the way back to version 3.0.0, and so this setting is of little - ** practical use, but is provided so that SQLite can continue to claim the - ** ability to generate new database files that are compatible with version -@@ -2322,9 +2540,110 @@ struct sqlite3_mem_methods { - ** the [VACUUM] command will fail with an obscure error when attempting to - ** process a table with generated columns and a descending index. This is - ** not considered a bug since SQLite versions 3.3.0 and earlier do not support --** either generated columns or decending indexes. -+** either generated columns or descending indexes. -+**
        -+** -+** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] -+**
        SQLITE_DBCONFIG_STMT_SCANSTATUS
        -+**
        The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in -+** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears -+** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() -+** statistics. For statistics to be collected, the flag must be set on -+** the database handle both when the SQL statement is prepared and when it -+** is stepped. The flag is set (collection of statistics is enabled) -+** by default.

        This option takes two arguments: an integer and a pointer to -+** an integer.. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the statement scanstatus option. If the second argument -+** is not NULL, then the value of the statement scanstatus setting after -+** processing the first argument is written into the integer that the second -+** argument points to. -+**

        -+** -+** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] -+**
        SQLITE_DBCONFIG_REVERSE_SCANORDER
        -+**
        The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order -+** in which tables and indexes are scanned so that the scans start at the end -+** and work toward the beginning rather than starting at the beginning and -+** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the -+** same as setting [PRAGMA reverse_unordered_selects].

        This option takes -+** two arguments which are an integer and a pointer to an integer. The first -+** argument is 1, 0, or -1 to enable, disable, or leave unchanged the -+** reverse scan order flag, respectively. If the second argument is not NULL, -+** then 0 or 1 is written into the integer that the second argument points to -+** depending on if the reverse scan order flag is set after processing the -+** first argument. -+**

        -+** -+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] -+**
        SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
        -+**
        The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables -+** the ability of the [ATTACH DATABASE] SQL command to create a new database -+** file if the database filed named in the ATTACH command does not already -+** exist. This ability of ATTACH to create a new database is enabled by -+** default. Applications can disable or reenable the ability for ATTACH to -+** create new database files using this DBCONFIG option.

        -+** This option takes two arguments which are an integer and a pointer -+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the attach-create flag, respectively. If the second -+** argument is not NULL, then 0 or 1 is written into the integer that the -+** second argument points to depending on if the attach-create flag is set -+** after processing the first argument. - **

        -+** -+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] -+**
        SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
        -+**
        The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the -+** ability of the [ATTACH DATABASE] SQL command to open a database for writing. -+** This capability is enabled by default. Applications can disable or -+** reenable this capability using the current DBCONFIG option. If the -+** the this capability is disabled, the [ATTACH] command will still work, -+** but the database will be opened read-only. If this option is disabled, -+** then the ability to create a new database using [ATTACH] is also disabled, -+** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] -+** option.

        -+** This option takes two arguments which are an integer and a pointer -+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the ability to ATTACH another database for writing, -+** respectively. If the second argument is not NULL, then 0 or 1 is written -+** into the integer to which the second argument points, depending on whether -+** the ability to ATTACH a read/write database is enabled or disabled -+** after processing the first argument. -+**

        -+** -+** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] -+**
        SQLITE_DBCONFIG_ENABLE_COMMENTS
        -+**
        The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the -+** ability to include comments in SQL text. Comments are enabled by default. -+** An application can disable or reenable comments in SQL text using this -+** DBCONFIG option.

        -+** This option takes two arguments which are an integer and a pointer -+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or -+** leave unchanged the ability to use comments in SQL text, -+** respectively. If the second argument is not NULL, then 0 or 1 is written -+** into the integer that the second argument points to depending on if -+** comments are allowed in SQL text after processing the first argument. -+**

        -+** - ** -+** -+** [[DBCONFIG arguments]]

        Arguments To SQLITE_DBCONFIG Options

        -+** -+**

        Most of the SQLITE_DBCONFIG options take two arguments, so that the -+** overall call to [sqlite3_db_config()] has a total of four parameters. -+** The first argument (the third parameter to sqlite3_db_config()) is a integer. -+** The second argument is a pointer to an integer. If the first argument is 1, -+** then the option becomes enabled. If the first integer argument is 0, then the -+** option is disabled. If the first argument is -1, then the option setting -+** is unchanged. The second argument, the pointer to an integer, may be NULL. -+** If the second argument is not NULL, then a value of 0 or 1 is written into -+** the integer to which the second argument points, depending on whether the -+** setting is disabled or enabled after applying any changes specified by -+** the first argument. -+** -+**

        While most SQLITE_DBCONFIG options use the argument format -+** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] -+** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the -+** documentation of those exceptional options for details. - */ - #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ - #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ -@@ -2344,7 +2663,12 @@ struct sqlite3_mem_methods { - #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ - #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ - #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ --#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */ -+#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ -+#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ -+#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ -+#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ -+#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ -+#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ - - /* - ** CAPI3REF: Enable Or Disable Extended Result Codes -@@ -2432,11 +2756,18 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); - ** CAPI3REF: Count The Number Of Rows Modified - ** METHOD: sqlite3 - ** --** ^This function returns the number of rows modified, inserted or -+** ^These functions return the number of rows modified, inserted or - ** deleted by the most recently completed INSERT, UPDATE or DELETE - ** statement on the database connection specified by the only parameter. --** ^Executing any other type of SQL statement does not modify the value --** returned by this function. -+** The two functions are identical except for the type of the return value -+** and that if the number of rows modified by the most recent INSERT, UPDATE, -+** or DELETE is greater than the maximum value supported by type "int", then -+** the return value of sqlite3_changes() is undefined. ^Executing any other -+** type of SQL statement does not modify the value returned by these functions. -+** For the purposes of this interface, a CREATE TABLE AS SELECT statement -+** does not count as an INSERT, UPDATE or DELETE statement and hence the rows -+** added to the new table by the CREATE TABLE AS SELECT statement are not -+** counted. - ** - ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are - ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], -@@ -2485,16 +2816,21 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64); - ** - */ - SQLITE_API int sqlite3_changes(sqlite3*); -+SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*); - - /* - ** CAPI3REF: Total Number Of Rows Modified - ** METHOD: sqlite3 - ** --** ^This function returns the total number of rows inserted, modified or -+** ^These functions return the total number of rows inserted, modified or - ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed - ** since the database connection was opened, including those executed as --** part of trigger programs. ^Executing any other type of SQL statement --** does not affect the value returned by sqlite3_total_changes(). -+** part of trigger programs. The two functions are identical except for the -+** type of the return value and that if the number of rows modified by the -+** connection exceeds the maximum value supported by type "int", then -+** the return value of sqlite3_total_changes() is undefined. ^Executing -+** any other type of SQL statement does not affect the value returned by -+** sqlite3_total_changes(). - ** - ** ^Changes made as part of [foreign key actions] are included in the - ** count, but those made as part of REPLACE constraint resolution are -@@ -2522,6 +2858,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); - ** - */ - SQLITE_API int sqlite3_total_changes(sqlite3*); -+SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); - - /* - ** CAPI3REF: Interrupt A Long-Running Query -@@ -2557,8 +2894,13 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); - ** ^A call to sqlite3_interrupt(D) that occurs when there are no running - ** SQL statements is a no-op and has no effect on SQL statements - ** that are started after the sqlite3_interrupt() call returns. -+** -+** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether -+** or not an interrupt is currently in effect for [database connection] D. -+** It returns 1 if an interrupt is currently in effect, or 0 otherwise. - */ - SQLITE_API void sqlite3_interrupt(sqlite3*); -+SQLITE_API int sqlite3_is_interrupted(sqlite3*); - - /* - ** CAPI3REF: Determine If An SQL Statement Is Complete -@@ -2680,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*); - */ - SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); - -+/* -+** CAPI3REF: Set the Setlk Timeout -+** METHOD: sqlite3 -+** -+** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If -+** the VFS supports blocking locks, it sets the timeout in ms used by -+** eligible locks taken on wal mode databases by the specified database -+** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does -+** not support blocking locks, this function is a no-op. -+** -+** Passing 0 to this function disables blocking locks altogether. Passing -+** -1 to this function requests that the VFS blocks for a long time - -+** indefinitely if possible. The results of passing any other negative value -+** are undefined. -+** -+** Internally, each SQLite database handle store two timeout values - the -+** busy-timeout (used for rollback mode databases, or if the VFS does not -+** support blocking locks) and the setlk-timeout (used for blocking locks -+** on wal-mode databases). The sqlite3_busy_timeout() method sets both -+** values, this function sets only the setlk-timeout value. Therefore, -+** to configure separate busy-timeout and setlk-timeout values for a single -+** database handle, call sqlite3_busy_timeout() followed by this function. -+** -+** Whenever the number of connections to a wal mode database falls from -+** 1 to 0, the last connection takes an exclusive lock on the database, -+** then checkpoints and deletes the wal file. While it is doing this, any -+** new connection that tries to read from the database fails with an -+** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is -+** passed to this API, the new connection blocks until the exclusive lock -+** has been released. -+*/ -+SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); -+ -+/* -+** CAPI3REF: Flags for sqlite3_setlk_timeout() -+*/ -+#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 -+ - /* - ** CAPI3REF: Convenience Routines For Running Queries - ** METHOD: sqlite3 -@@ -3105,8 +3485,8 @@ SQLITE_API int sqlite3_set_authorizer( - #define SQLITE_RECURSIVE 33 /* NULL NULL */ - - /* --** CAPI3REF: Tracing And Profiling Functions --** METHOD: sqlite3 -+** CAPI3REF: Deprecated Tracing And Profiling Functions -+** DEPRECATED - ** - ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface - ** instead of the routines described here. -@@ -3176,8 +3556,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, - **

        ^An SQLITE_TRACE_PROFILE callback provides approximately the same - ** information as is provided by the [sqlite3_profile()] callback. - ** ^The P argument is a pointer to the [prepared statement] and the --** X argument points to a 64-bit integer which is the estimated of --** the number of nanosecond that the prepared statement took to run. -+** X argument points to a 64-bit integer which is approximately -+** the number of nanoseconds that the prepared statement took to run. - ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. - ** - ** [[SQLITE_TRACE_ROW]]
        SQLITE_TRACE_ROW
        -@@ -3209,8 +3589,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, - ** M argument should be the bitwise OR-ed combination of - ** zero or more [SQLITE_TRACE] constants. - ** --** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides --** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). -+** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P) -+** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or -+** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each -+** database connection may have at most one trace callback. - ** - ** ^The X callback is invoked whenever any of the events identified by - ** mask M occur. ^The integer return value from the callback is currently -@@ -3240,7 +3622,7 @@ SQLITE_API int sqlite3_trace_v2( - ** - ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback - ** function X to be invoked periodically during long running calls to --** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for -+** [sqlite3_step()] and [sqlite3_prepare()] and similar for - ** database connection D. An example use for this - ** interface is to keep a GUI updated during a large query. - ** -@@ -3265,6 +3647,13 @@ SQLITE_API int sqlite3_trace_v2( - ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their - ** database connections for the meaning of "modify" in this paragraph. - ** -+** The progress handler callback would originally only be invoked from the -+** bytecode engine. It still might be invoked during [sqlite3_prepare()] -+** and similar because those routines might force a reparse of the schema -+** which involves running the bytecode engine. However, beginning with -+** SQLite version 3.41.0, the progress handler callback might also be -+** invoked directly from [sqlite3_prepare()] while analyzing and generating -+** code for complex queries. - */ - SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - -@@ -3301,13 +3690,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - ** - **
        - ** ^(
        [SQLITE_OPEN_READONLY]
        --**
        The database is opened in read-only mode. If the database does not --** already exist, an error is returned.
        )^ -+**
        The database is opened in read-only mode. If the database does -+** not already exist, an error is returned.
        )^ - ** - ** ^(
        [SQLITE_OPEN_READWRITE]
        --**
        The database is opened for reading and writing if possible, or reading --** only if the file is write protected by the operating system. In either --** case the database must already exist, otherwise an error is returned.
        )^ -+**
        The database is opened for reading and writing if possible, or -+** reading only if the file is write protected by the operating -+** system. In either case the database must already exist, otherwise -+** an error is returned. For historical reasons, if opening in -+** read-write mode fails due to OS-level permissions, an attempt is -+** made to open it in read-only mode. [sqlite3_db_readonly()] can be -+** used to determine whether the database is actually -+** read-write.
        )^ - ** - ** ^(
        [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
        - **
        The database is opened for reading and writing, and is created if -@@ -3345,20 +3739,39 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - **
        The database is opened [shared cache] enabled, overriding - ** the default shared cache setting provided by - ** [sqlite3_enable_shared_cache()].)^ -+** The [use of shared cache mode is discouraged] and hence shared cache -+** capabilities may be omitted from many builds of SQLite. In such cases, -+** this option is a no-op. - ** - ** ^(
        [SQLITE_OPEN_PRIVATECACHE]
        - **
        The database is opened [shared cache] disabled, overriding - ** the default shared cache setting provided by - ** [sqlite3_enable_shared_cache()].)^ - ** -+** [[OPEN_EXRESCODE]] ^(
        [SQLITE_OPEN_EXRESCODE]
        -+**
        The database connection comes up in "extended result code mode". -+** In other words, the database behaves as if -+** [sqlite3_extended_result_codes(db,1)] were called on the database -+** connection as soon as the connection is created. In addition to setting -+** the extended result code mode, this flag also causes [sqlite3_open_v2()] -+** to return an extended result code.
        -+** - ** [[OPEN_NOFOLLOW]] ^(
        [SQLITE_OPEN_NOFOLLOW]
        --**
        The database filename is not allowed to be a symbolic link
        -+**
        The database filename is not allowed to contain a symbolic link
        - **
        )^ - ** - ** If the 3rd parameter to sqlite3_open_v2() is not one of the - ** required combinations shown above optionally combined with other - ** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] --** then the behavior is undefined. -+** then the behavior is undefined. Historic versions of SQLite -+** have silently ignored surplus bits in the flags parameter to -+** sqlite3_open_v2(), however that behavior might not be carried through -+** into future versions of SQLite and so applications should not rely -+** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op -+** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause -+** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE -+** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not -+** by sqlite3_open_v2(). - ** - ** ^The fourth parameter to sqlite3_open_v2() is the name of the - ** [sqlite3_vfs] object that defines the operating system interface that -@@ -3498,6 +3911,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); - ** that uses dot-files in place of posix advisory locking. - **
      • file:data.db?mode=readonly - ** An error. "readonly" is not a valid option for the "mode" parameter. -+** Use "ro" instead: "file:data.db?mode=ro". - **
        - ** - ** ^URI hexadecimal escape sequences (%HH) are supported within the path and -@@ -3547,7 +3961,7 @@ SQLITE_API int sqlite3_open_v2( - ** as F) must be one of: - **
          - **
        • A database filename pointer created by the SQLite core and --** passed into the xOpen() method of a VFS implemention, or -+** passed into the xOpen() method of a VFS implementation, or - **
        • A filename obtained from [sqlite3_db_filename()], or - **
        • A new filename constructed using [sqlite3_create_filename()]. - **
        -@@ -3602,10 +4016,10 @@ SQLITE_API int sqlite3_open_v2( - ** - ** See the [URI filename] documentation for additional information. - */ --SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); --SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); --SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); --SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); -+SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam); -+SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault); -+SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64); -+SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N); - - /* - ** CAPI3REF: Translate filenames -@@ -3634,9 +4048,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N); - ** return value from [sqlite3_db_filename()], then the result is - ** undefined and is likely a memory access violation. - */ --SQLITE_API const char *sqlite3_filename_database(const char*); --SQLITE_API const char *sqlite3_filename_journal(const char*); --SQLITE_API const char *sqlite3_filename_wal(const char*); -+SQLITE_API const char *sqlite3_filename_database(sqlite3_filename); -+SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename); -+SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename); - - /* - ** CAPI3REF: Database File Corresponding To A Journal -@@ -3660,12 +4074,12 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); - /* - ** CAPI3REF: Create and Destroy VFS Filenames - ** --** These interfces are provided for use by [VFS shim] implementations and -+** These interfaces are provided for use by [VFS shim] implementations and - ** are not useful outside of that context. - ** - ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of - ** database filename D with corresponding journal file J and WAL file W and --** with N URI parameters key/values pairs in the array P. The result from -+** an array P of N URI Key/Value pairs. The result from - ** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that - ** is safe to pass to routines like: - **
          -@@ -3696,20 +4110,20 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); - ** If the Y parameter to sqlite3_free_filename(Y) is anything other - ** than a NULL pointer or a pointer previously acquired from - ** sqlite3_create_filename(), then bad things such as heap --** corruption or segfaults may occur. The value Y should be -+** corruption or segfaults may occur. The value Y should not be - ** used again after sqlite3_free_filename(Y) has been called. This means - ** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y, - ** then the corresponding [sqlite3_module.xClose() method should also be - ** invoked prior to calling sqlite3_free_filename(Y). - */ --SQLITE_API char *sqlite3_create_filename( -+SQLITE_API sqlite3_filename sqlite3_create_filename( - const char *zDatabase, - const char *zJournal, - const char *zWal, - int nParam, - const char **azParam - ); --SQLITE_API void sqlite3_free_filename(char*); -+SQLITE_API void sqlite3_free_filename(sqlite3_filename); - - /* - ** CAPI3REF: Error Codes And Messages -@@ -3728,27 +4142,38 @@ SQLITE_API void sqlite3_free_filename(char*); - ** sqlite3_extended_errcode() might change with each API call. - ** Except, there are some interfaces that are guaranteed to never - ** change the value of the error code. The error-code preserving --** interfaces are: -+** interfaces include the following: - ** - **
            - **
          • sqlite3_errcode() - **
          • sqlite3_extended_errcode() - **
          • sqlite3_errmsg() - **
          • sqlite3_errmsg16() -+**
          • sqlite3_error_offset() - **
          - ** - ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language --** text that describes the error, as either UTF-8 or UTF-16 respectively. -+** text that describes the error, as either UTF-8 or UTF-16 respectively, -+** or NULL if no error message is available. -+** (See how SQLite handles [invalid UTF] for exceptions to this rule.) - ** ^(Memory to hold the error message string is managed internally. - ** The application does not need to worry about freeing the result. - ** However, the error string might be overwritten or deallocated by - ** subsequent calls to other SQLite interface functions.)^ - ** --** ^The sqlite3_errstr() interface returns the English-language text --** that describes the [result code], as UTF-8. -+** ^The sqlite3_errstr(E) interface returns the English-language text -+** that describes the [result code] E, as UTF-8, or NULL if E is not an -+** result code for which a text error message is available. - ** ^(Memory to hold the error message string is managed internally - ** and must not be freed by the application)^. - ** -+** ^If the most recent error references a specific token in the input -+** SQL, the sqlite3_error_offset() interface returns the byte offset -+** of the start of that token. ^The byte offset returned by -+** sqlite3_error_offset() assumes that the input SQL is UTF8. -+** ^If the most recent error does not reference a specific token in the input -+** SQL, then the sqlite3_error_offset() function returns -1. -+** - ** When the serialized [threading mode] is in use, it might be the - ** case that a second error occurs on a separate thread in between - ** the time of the first error and the call to these interfaces. -@@ -3768,6 +4193,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); - SQLITE_API const char *sqlite3_errmsg(sqlite3*); - SQLITE_API const void *sqlite3_errmsg16(sqlite3*); - SQLITE_API const char *sqlite3_errstr(int); -+SQLITE_API int sqlite3_error_offset(sqlite3 *db); - - /* - ** CAPI3REF: Prepared Statement Object -@@ -3939,11 +4365,22 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); - **
          The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler - ** to return an error (error code SQLITE_ERROR) if the statement uses - ** any virtual tables. -+** -+** [[SQLITE_PREPARE_DONT_LOG]]
          SQLITE_PREPARE_DONT_LOG
          -+**
          The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler -+** errors from being sent to the error log defined by -+** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test -+** compiles to see if some SQL syntax is well-formed, without generating -+** messages on the global error log when it is not. If the test compile -+** fails, the sqlite3_prepare_v3() call returns the same error indications -+** with or without this flag; it just omits the call to [sqlite3_log()] that -+** logs the error. - ** - */ - #define SQLITE_PREPARE_PERSISTENT 0x01 - #define SQLITE_PREPARE_NORMALIZE 0x02 - #define SQLITE_PREPARE_NO_VTAB 0x04 -+#define SQLITE_PREPARE_DONT_LOG 0x10 - - /* - ** CAPI3REF: Compiling An SQL Statement -@@ -3976,13 +4413,17 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); - ** and sqlite3_prepare16_v3() use UTF-16. - ** - ** ^If the nByte argument is negative, then zSql is read up to the --** first zero terminator. ^If nByte is positive, then it is the --** number of bytes read from zSql. ^If nByte is zero, then no prepared -+** first zero terminator. ^If nByte is positive, then it is the maximum -+** number of bytes read from zSql. When nByte is positive, zSql is read -+** up to the first zero terminator or until the nByte bytes have been read, -+** whichever comes first. ^If nByte is zero, then no prepared - ** statement is generated. - ** If the caller knows that the supplied string is nul-terminated, then - ** there is a small performance advantage to passing an nByte parameter that - ** is the number of bytes in the input string including - ** the nul-terminator. -+** Note that nByte measure the length of the input in bytes, not -+** characters, even for the UTF-16 interfaces. - ** - ** ^If pzTail is not NULL then *pzTail is made to point to the first byte - ** past the end of the first SQL statement in zSql. These routines only -@@ -4125,12 +4566,17 @@ SQLITE_API int sqlite3_prepare16_v3( - ** are managed by SQLite and are automatically freed when the prepared - ** statement is finalized. - ** ^The string returned by sqlite3_expanded_sql(P), on the other hand, --** is obtained from [sqlite3_malloc()] and must be free by the application -+** is obtained from [sqlite3_malloc()] and must be freed by the application - ** by passing it to [sqlite3_free()]. -+** -+** ^The sqlite3_normalized_sql() interface is only available if -+** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined. - */ - SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); - SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); -+#ifdef SQLITE_ENABLE_NORMALIZE - SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); -+#endif - - /* - ** CAPI3REF: Determine If An SQL Statement Writes The Database -@@ -4165,6 +4611,19 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); - ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and - ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so - ** sqlite3_stmt_readonly() returns false for those commands. -+** -+** ^This routine returns false if there is any possibility that the -+** statement might change the database file. ^A false return does -+** not guarantee that the statement will change the database file. -+** ^For example, an UPDATE statement might have a WHERE clause that -+** makes it a no-op, but the sqlite3_stmt_readonly() result would still -+** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a -+** read-only no-op if the table already exists, but -+** sqlite3_stmt_readonly() still returns false for such a statement. -+** -+** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN] -+** statement, then sqlite3_stmt_readonly(X) returns the same value as -+** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted. - */ - SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - -@@ -4180,6 +4639,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); - */ - SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); - -+/* -+** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement -+** METHOD: sqlite3_stmt -+** -+** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN -+** setting for [prepared statement] S. If E is zero, then S becomes -+** a normal prepared statement. If E is 1, then S behaves as if -+** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if -+** its SQL text began with "[EXPLAIN QUERY PLAN]". -+** -+** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared. -+** SQLite tries to avoid a reprepare, but a reprepare might be necessary -+** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode. -+** -+** Because of the potential need to reprepare, a call to -+** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be -+** reprepared because it was created using [sqlite3_prepare()] instead of -+** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and -+** hence has no saved SQL text with which to reprepare. -+** -+** Changing the explain setting for a prepared statement does not change -+** the original SQL text for the statement. Hence, if the SQL text originally -+** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) -+** is called to convert the statement into an ordinary statement, the EXPLAIN -+** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) -+** output, even though the statement now acts like a normal SQL statement. -+** -+** This routine returns SQLITE_OK if the explain mode is successfully -+** changed, or an error code if the explain mode could not be changed. -+** The explain mode cannot be changed while a statement is active. -+** Hence, it is good practice to call [sqlite3_reset(S)] -+** immediately prior to calling sqlite3_stmt_explain(S,E). -+*/ -+SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode); -+ - /* - ** CAPI3REF: Determine If A Prepared Statement Has Been Reset - ** METHOD: sqlite3_stmt -@@ -4233,6 +4727,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); - ** - ** ^The sqlite3_value objects that are passed as parameters into the - ** implementation of [application-defined SQL functions] are protected. -+** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()] -+** are protected. - ** ^The sqlite3_value object returned by - ** [sqlite3_column_value()] is unprotected. - ** Unprotected sqlite3_value objects may only be used as arguments -@@ -4264,7 +4760,7 @@ typedef struct sqlite3_context sqlite3_context; - ** METHOD: sqlite3_stmt - ** - ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, --** literals may be replaced by a [parameter] that matches one of following -+** literals may be replaced by a [parameter] that matches one of the following - ** templates: - ** - **
            -@@ -4309,7 +4805,7 @@ typedef struct sqlite3_context sqlite3_context; - ** - ** [[byte-order determination rules]] ^The byte-order of - ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) --** found in first character, which is removed, or in the absence of a BOM -+** found in the first character, which is removed, or in the absence of a BOM - ** the byte order is the native byte order of the host - ** machine for sqlite3_bind_text16() or the byte order specified in - ** the 6th parameter for sqlite3_bind_text64().)^ -@@ -4329,23 +4825,27 @@ typedef struct sqlite3_context sqlite3_context; - ** or sqlite3_bind_text16() or sqlite3_bind_text64() then - ** that parameter must be the byte offset - ** where the NUL terminator would occur assuming the string were NUL --** terminated. If any NUL characters occurs at byte offsets less than -+** terminated. If any NUL characters occur at byte offsets less than - ** the value of the fourth parameter then the resulting string value will - ** contain embedded NULs. The result of expressions involving strings - ** with embedded NULs is undefined. - ** --** ^The fifth argument to the BLOB and string binding interfaces --** is a destructor used to dispose of the BLOB or --** string after SQLite has finished with it. ^The destructor is called --** to dispose of the BLOB or string even if the call to the bind API fails, --** except the destructor is not called if the third parameter is a NULL --** pointer or the fourth parameter is negative. --** ^If the fifth argument is --** the special value [SQLITE_STATIC], then SQLite assumes that the --** information is in static, unmanaged space and does not need to be freed. --** ^If the fifth argument has the value [SQLITE_TRANSIENT], then --** SQLite makes its own private copy of the data immediately, before --** the sqlite3_bind_*() routine returns. -+** ^The fifth argument to the BLOB and string binding interfaces controls -+** or indicates the lifetime of the object referenced by the third parameter. -+** These three options exist: -+** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished -+** with it may be passed. ^It is called to dispose of the BLOB or string even -+** if the call to the bind API fails, except the destructor is not called if -+** the third parameter is a NULL pointer or the fourth parameter is negative. -+** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that -+** the application remains responsible for disposing of the object. ^In this -+** case, the object and the provided pointer to it must remain valid until -+** either the prepared statement is finalized or the same SQL parameter is -+** bound to something else, whichever occurs sooner. -+** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the -+** object is to be copied prior to the return from sqlite3_bind_*(). ^The -+** object and pointer to it must remain valid until then. ^SQLite will then -+** manage the lifetime of its private copy. - ** - ** ^The sixth argument to sqlite3_bind_text64() must be one of - ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] -@@ -4537,7 +5037,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); - ** METHOD: sqlite3_stmt - ** - ** ^These routines provide a means to determine the database, table, and --** table column that is the origin of a particular result column in -+** table column that is the origin of a particular result column in a - ** [SELECT] statement. - ** ^The name of the database or table or column can be returned as - ** either a UTF-8 or UTF-16 string. ^The _database_ routines return -@@ -4675,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); - ** other than [SQLITE_ROW] before any subsequent invocation of - ** sqlite3_step(). Failure to reset the prepared statement using - ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from --** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], -+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), - ** sqlite3_step() began - ** calling [sqlite3_reset()] automatically in this circumstance rather - ** than returning [SQLITE_MISUSE]. This is not considered a compatibility -@@ -4850,6 +5350,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - ** even empty strings, are always zero-terminated. ^The return - ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. - ** -+** ^Strings returned by sqlite3_column_text16() always have the endianness -+** which is native to the platform, regardless of the text encoding set -+** for the database. -+** - ** Warning: ^The object returned by [sqlite3_column_value()] is an - ** [unprotected sqlite3_value] object. In a multithreaded environment, - ** an unprotected sqlite3_value object may only be used safely with -@@ -4863,7 +5367,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - ** [application-defined SQL functions] or [virtual tables], not within - ** top-level application code. - ** --** The these routines may attempt to convert the datatype of the result. -+** These routines may attempt to convert the datatype of the result. - ** ^For example, if the internal representation is FLOAT and a text result - ** is requested, [sqlite3_snprintf()] is used internally to perform the - ** conversion automatically. ^(The following table details the conversions -@@ -4888,7 +5392,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); - **
        TEXT BLOB No change - **
        BLOB INTEGER [CAST] to INTEGER - **
        BLOB FLOAT [CAST] to REAL --**
        BLOB TEXT Add a zero terminator if needed -+**
        BLOB TEXT [CAST] to TEXT, ensure zero terminator - **
        - **
    )^ - ** -@@ -5012,20 +5516,33 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); - ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S - ** back to the beginning of its program. - ** --** ^If the most recent call to [sqlite3_step(S)] for the --** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], --** or if [sqlite3_step(S)] has never before been called on S, --** then [sqlite3_reset(S)] returns [SQLITE_OK]. -+** ^The return code from [sqlite3_reset(S)] indicates whether or not -+** the previous evaluation of prepared statement S completed successfully. -+** ^If [sqlite3_step(S)] has never before been called on S or if -+** [sqlite3_step(S)] has not been called since the previous call -+** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return -+** [SQLITE_OK]. - ** - ** ^If the most recent call to [sqlite3_step(S)] for the - ** [prepared statement] S indicated an error, then - ** [sqlite3_reset(S)] returns an appropriate [error code]. -+** ^The [sqlite3_reset(S)] interface might also return an [error code] -+** if there were no prior errors but the process of resetting -+** the prepared statement caused a new error. ^For example, if an -+** [INSERT] statement with a [RETURNING] clause is only stepped one time, -+** that one call to [sqlite3_step(S)] might return SQLITE_ROW but -+** the overall statement might still fail and the [sqlite3_reset(S)] call -+** might return SQLITE_BUSY if locking constraints prevent the -+** database change from committing. Therefore, it is important that -+** applications check the return code from [sqlite3_reset(S)] even if -+** no prior call to [sqlite3_step(S)] indicated a problem. - ** - ** ^The [sqlite3_reset(S)] interface does not change the values - ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. - */ - SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - -+ - /* - ** CAPI3REF: Create Or Redefine SQL Functions - ** KEYWORDS: {function creation routines} -@@ -5087,17 +5604,15 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, - ** index expressions, or the WHERE clause of partial indexes. - ** --** - ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for - ** all application-defined SQL functions that do not need to be --** used inside of triggers, view, CHECK constraints, or other elements of --** the database schema. This flags is especially recommended for SQL -+** used inside of triggers, views, CHECK constraints, or other elements of -+** the database schema. This flag is especially recommended for SQL - ** functions that have side effects or reveal internal application state. - ** Without this flag, an attacker might be able to modify the schema of - ** a database file to include invocations of the function with parameters - ** chosen by the attacker, which the application will then execute when - ** the database file is opened and read. --** - ** - ** ^(The fifth parameter is an arbitrary pointer. The implementation of the - ** function can gain access to this pointer using [sqlite3_user_data()].)^ -@@ -5123,7 +5638,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); - ** [user-defined window functions|available here]. - ** - ** ^(If the final parameter to sqlite3_create_function_v2() or --** sqlite3_create_window_function() is not NULL, then it is destructor for -+** sqlite3_create_window_function() is not NULL, then it is the destructor for - ** the application data pointer. The destructor is invoked when the function - ** is deleted, either by being overloaded or when the database connection - ** closes.)^ ^The destructor is also invoked if the call to -@@ -5233,10 +5748,21 @@ SQLITE_API int sqlite3_create_window_function( - ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in - ** schema structures such as [CHECK constraints], [DEFAULT clauses], - ** [expression indexes], [partial indexes], or [generated columns]. --** The SQLITE_DIRECTONLY flags is a security feature which is recommended --** for all [application-defined SQL functions], and especially for functions --** that have side-effects or that could potentially leak sensitive --** information. -+**

    -+** The SQLITE_DIRECTONLY flag is recommended for any -+** [application-defined SQL function] -+** that has side-effects or that could potentially leak sensitive information. -+** This will prevent attacks in which an application is tricked -+** into using a database file that has had its schema surreptitiously -+** modified to invoke the application-defined function in ways that are -+** harmful. -+**

    -+** Some people say it is good practice to set SQLITE_DIRECTONLY on all -+** [application-defined SQL functions], regardless of whether or not they -+** are security sensitive, as doing so prevents those functions from being used -+** inside of the database schema, and thus ensures that the database -+** can be inspected and modified using generic tools (such as the [CLI]) -+** that do not have access to the application-defined functions. - ** - ** - ** [[SQLITE_INNOCUOUS]]

    SQLITE_INNOCUOUS
    -@@ -5263,13 +5789,36 @@ SQLITE_API int sqlite3_create_window_function( - **
    - ** - ** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    --** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call -+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call - ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. --** Specifying this flag makes no difference for scalar or aggregate user --** functions. However, if it is not specified for a user-defined window --** function, then any sub-types belonging to arguments passed to the window --** function may be discarded before the window function is called (i.e. --** sqlite3_value_subtype() will always return 0). -+** This flag instructs SQLite to omit some corner-case optimizations that -+** might disrupt the operation of the [sqlite3_value_subtype()] function, -+** causing it to return zero rather than the correct subtype(). -+** All SQL functions that invoke [sqlite3_value_subtype()] should have this -+** property. If the SQLITE_SUBTYPE property is omitted, then the return -+** value from [sqlite3_value_subtype()] might sometimes be zero even though -+** a non-zero subtype was specified by the function argument expression. -+** -+** [[SQLITE_RESULT_SUBTYPE]]
    SQLITE_RESULT_SUBTYPE
    -+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call -+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its -+** result. -+** Every function that invokes [sqlite3_result_subtype()] should have this -+** property. If it does not, then the call to [sqlite3_result_subtype()] -+** might become a no-op if the function is used as term in an -+** [expression index]. On the other hand, SQL functions that never invoke -+** [sqlite3_result_subtype()] should avoid setting this property, as the -+** purpose of this property is to disable certain optimizations that are -+** incompatible with subtypes. -+** -+** [[SQLITE_SELFORDER1]]
    SQLITE_SELFORDER1
    -+** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate -+** that internally orders the values provided to the first argument. The -+** ordered-set aggregate SQL notation with a single ORDER BY term can be -+** used to invoke this function. If the ordered-set aggregate notation is -+** used on a function that lacks this flag, then an error is raised. Note -+** that the ordered-set aggregate syntax is only available if SQLite is -+** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. - **
    - **
    - */ -@@ -5277,6 +5826,8 @@ SQLITE_API int sqlite3_create_window_function( - #define SQLITE_DIRECTONLY 0x000080000 - #define SQLITE_SUBTYPE 0x000100000 - #define SQLITE_INNOCUOUS 0x000200000 -+#define SQLITE_RESULT_SUBTYPE 0x001000000 -+#define SQLITE_SELFORDER1 0x002000000 - - /* - ** CAPI3REF: Deprecated Functions -@@ -5442,6 +5993,28 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); - SQLITE_API int sqlite3_value_nochange(sqlite3_value*); - SQLITE_API int sqlite3_value_frombind(sqlite3_value*); - -+/* -+** CAPI3REF: Report the internal text encoding state of an sqlite3_value object -+** METHOD: sqlite3_value -+** -+** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], -+** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding -+** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) -+** returns something other than SQLITE_TEXT, then the return value from -+** sqlite3_value_encoding(X) is meaningless. ^Calls to -+** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)], -+** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or -+** [sqlite3_value_bytes16(X)] might change the encoding of the value X and -+** thus change the return from subsequent calls to sqlite3_value_encoding(X). -+** -+** This routine is intended for used by applications that test and validate -+** the SQLite implementation. This routine is inquiring about the opaque -+** internal state of an [sqlite3_value] object. Ordinary applications should -+** not need to know what the internal state of an sqlite3_value object is and -+** hence should not need to use this interface. -+*/ -+SQLITE_API int sqlite3_value_encoding(sqlite3_value*); -+ - /* - ** CAPI3REF: Finding The Subtype Of SQL Values - ** METHOD: sqlite3_value -@@ -5451,6 +6024,12 @@ SQLITE_API int sqlite3_value_frombind(sqlite3_value*); - ** information can be used to pass a limited amount of context from - ** one SQL function to another. Use the [sqlite3_result_subtype()] - ** routine to set the subtype for the return value of an SQL function. -+** -+** Every [application-defined SQL function] that invokes this interface -+** should include the [SQLITE_SUBTYPE] property in the text -+** encoding argument when the function is [sqlite3_create_function|registered]. -+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() -+** might return zero instead of the upstream subtype in some corner cases. - */ - SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); - -@@ -5459,10 +6038,11 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); - ** METHOD: sqlite3_value - ** - ** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value] --** object D and returns a pointer to that copy. ^The [sqlite3_value] returned -+** object V and returns a pointer to that copy. ^The [sqlite3_value] returned - ** is a [protected sqlite3_value] object even if the input is not. - ** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a --** memory allocation fails. -+** memory allocation fails. ^If V is a [pointer value], then the result -+** of sqlite3_value_dup(V) is a NULL value. - ** - ** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object - ** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer -@@ -5493,10 +6073,10 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*); - ** - ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer - ** when first called if N is less than or equal to zero or if a memory --** allocate error occurs. -+** allocation error occurs. - ** - ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is --** determined by the N parameter on first successful call. Changing the -+** determined by the N parameter on the first successful call. Changing the - ** value of N in any subsequent call to sqlite3_aggregate_context() within - ** the same aggregate function instance will not resize the memory - ** allocation.)^ Within the xFinal callback, it is customary to set -@@ -5548,48 +6128,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - ** METHOD: sqlite3_context - ** - ** These functions may be used by (non-aggregate) SQL functions to --** associate metadata with argument values. If the same value is passed to --** multiple invocations of the same SQL function during query execution, under --** some circumstances the associated metadata may be preserved. An example --** of where this might be useful is in a regular-expression matching --** function. The compiled version of the regular expression can be stored as --** metadata associated with the pattern string. -+** associate auxiliary data with argument values. If the same argument -+** value is passed to multiple invocations of the same SQL function during -+** query execution, under some circumstances the associated auxiliary data -+** might be preserved. An example of where this might be useful is in a -+** regular-expression matching function. The compiled version of the regular -+** expression can be stored as auxiliary data associated with the pattern string. - ** Then as long as the pattern string remains the same, - ** the compiled regular expression can be reused on multiple - ** invocations of the same function. - ** --** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata -+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data - ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument - ** value to the application-defined function. ^N is zero for the left-most --** function argument. ^If there is no metadata -+** function argument. ^If there is no auxiliary data - ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface - ** returns a NULL pointer. - ** --** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th --** argument of the application-defined function. ^Subsequent -+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the -+** N-th argument of the application-defined function. ^Subsequent - ** calls to sqlite3_get_auxdata(C,N) return P from the most recent --** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or --** NULL if the metadata has been discarded. -+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or -+** NULL if the auxiliary data has been discarded. - ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, - ** SQLite will invoke the destructor function X with parameter P exactly --** once, when the metadata is discarded. --** SQLite is free to discard the metadata at any time, including:
      -+** once, when the auxiliary data is discarded. -+** SQLite is free to discard the auxiliary data at any time, including:
        - **
      • ^(when the corresponding function parameter changes)^, or - **
      • ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the - ** SQL statement)^, or - **
      • ^(when sqlite3_set_auxdata() is invoked again on the same - ** parameter)^, or - **
      • ^(during the original sqlite3_set_auxdata() call when a memory --** allocation error occurs.)^
      -+** allocation error occurs.)^ -+**
    • ^(during the original sqlite3_set_auxdata() call if the function -+** is evaluated during query planning instead of during query execution, -+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^
    - ** --** Note the last bullet in particular. The destructor X in -+** Note the last two bullets in particular. The destructor X in - ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the - ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() - ** should be called near the end of the function implementation and the - ** function implementation should not make any use of P after --** sqlite3_set_auxdata() has been called. --** --** ^(In practice, metadata is preserved between function calls for -+** sqlite3_set_auxdata() has been called. Furthermore, a call to -+** sqlite3_get_auxdata() that occurs immediately after a corresponding call -+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory -+** condition occurred during the sqlite3_set_auxdata() call or if the -+** function is being evaluated during query planning rather than during -+** query execution. -+** -+** ^(In practice, auxiliary data is preserved between function calls for - ** function parameters that are compile-time constants, including literal - ** values and [parameters] and expressions composed from the same.)^ - ** -@@ -5599,10 +6187,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); - ** - ** These routines must be called from the same thread in which - ** the SQL function is running. -+** -+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()]. - */ - SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); - SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); - -+/* -+** CAPI3REF: Database Connection Client Data -+** METHOD: sqlite3 -+** -+** These functions are used to associate one or more named pointers -+** with a [database connection]. -+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P -+** to be attached to [database connection] D using name N. Subsequent -+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P -+** or a NULL pointer if there were no prior calls to -+** sqlite3_set_clientdata() with the same values of D and N. -+** Names are compared using strcmp() and are thus case sensitive. -+** -+** If P and X are both non-NULL, then the destructor X is invoked with -+** argument P on the first of the following occurrences: -+**
      -+**
    • An out-of-memory error occurs during the call to -+** sqlite3_set_clientdata() which attempts to register pointer P. -+**
    • A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made -+** with the same D and N parameters. -+**
    • The database connection closes. SQLite does not make any guarantees -+** about the order in which destructors are called, only that all -+** destructors will be called exactly once at some point during the -+** database connection closing process. -+**
    -+** -+** SQLite does not do anything with client data other than invoke -+** destructors on the client data at the appropriate time. The intended -+** use for client data is to provide a mechanism for wrapper libraries -+** to store additional information about an SQLite database connection. -+** -+** There is no limit (other than available memory) on the number of different -+** client data pointers (with different names) that can be attached to a -+** single database connection. However, the implementation is optimized -+** for the case of having only one or two different client data names. -+** Applications and wrapper libraries are discouraged from using more than -+** one client data name each. -+** -+** There is no way to enumerate the client data pointers -+** associated with a database connection. The N parameter can be thought -+** of as a secret key such that only code that knows the secret key is able -+** to access the associated data. -+** -+** Security Warning: These interfaces should not be exposed in scripting -+** languages or in other circumstances where it might be possible for an -+** attacker to invoke them. Any agent that can invoke these interfaces -+** can probably also take control of the process. -+** -+** Database connection client data is only available for SQLite -+** version 3.44.0 ([dateof:3.44.0]) and later. -+** -+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. -+*/ -+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*); -+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*)); - - /* - ** CAPI3REF: Constants Defining Special Destructor Behavior -@@ -5698,15 +6343,16 @@ typedef void (*sqlite3_destructor_type)(void*); - ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. - ** ^SQLite takes the text result from the application from - ** the 2nd parameter of the sqlite3_result_text* interfaces. --** ^If the 3rd parameter to the sqlite3_result_text* interfaces --** is negative, then SQLite takes result text from the 2nd parameter --** through the first zero character. -+** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces -+** other than sqlite3_result_text64() is negative, then SQLite computes -+** the string length itself by searching the 2nd parameter for the first -+** zero character. - ** ^If the 3rd parameter to the sqlite3_result_text* interfaces - ** is non-negative, then as many bytes (not characters) of the text - ** pointed to by the 2nd parameter are taken as the application-defined - ** function result. If the 3rd parameter is non-negative, then it - ** must be the byte offset into the string where the NUL terminator would --** appear if the string where NUL terminated. If any NUL characters occur -+** appear if the string were NUL terminated. If any NUL characters occur - ** in the string at a byte offset that is less than the value of the 3rd - ** parameter, then the resulting string will contain embedded NULs and the - ** result of expressions operating on strings with embedded NULs is undefined. -@@ -5764,7 +6410,7 @@ typedef void (*sqlite3_destructor_type)(void*); - ** string and preferably a string literal. The sqlite3_result_pointer() - ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. - ** --** If these routines are called from within the different thread -+** If these routines are called from within a different thread - ** than the one containing the application-defined function that received - ** the [sqlite3_context] pointer, the results are undefined. - */ -@@ -5803,6 +6449,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); - ** higher order bits are discarded. - ** The number of subtype bytes preserved by SQLite might increase - ** in future releases of SQLite. -+** -+** Every [application-defined SQL function] that invokes this interface -+** should include the [SQLITE_RESULT_SUBTYPE] property in its -+** text encoding argument when the SQL function is -+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] -+** property is omitted from the function that invokes sqlite3_result_subtype(), -+** then in some cases the sqlite3_result_subtype() might fail to set -+** the result subtype. -+** -+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any -+** SQL function that invokes the sqlite3_result_subtype() interface -+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise -+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 -+** by default. - */ - SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); - -@@ -5974,6 +6634,13 @@ SQLITE_API void sqlite3_activate_cerod( - ** of the default VFS is not implemented correctly, or not implemented at - ** all, then the behavior of sqlite3_sleep() may deviate from the description - ** in the previous paragraphs. -+** -+** If a negative argument is passed to sqlite3_sleep() the results vary by -+** VFS and operating system. Some system treat a negative argument as an -+** instruction to sleep forever. Others understand it to mean do not sleep -+** at all. ^In SQLite version 3.42.0 and later, a negative -+** argument passed into sqlite3_sleep() is changed to zero before it is relayed -+** down into the xSleep method of the VFS. - */ - SQLITE_API int sqlite3_sleep(int); - -@@ -6144,6 +6811,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); - */ - SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - -+/* -+** CAPI3REF: Return The Schema Name For A Database Connection -+** METHOD: sqlite3 -+** -+** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name -+** for the N-th database on database connection D, or a NULL pointer if N is -+** out of range. An N value of 0 means the main database file. An N of 1 is -+** the "temp" schema. Larger values of N correspond to various ATTACH-ed -+** databases. -+** -+** Space to hold the string that is returned by sqlite3_db_name() is managed -+** by SQLite itself. The string might be deallocated by any operation that -+** changes the schema, including [ATTACH] or [DETACH] or calls to -+** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that -+** occur on a different thread. Applications that need to -+** remember the string long-term should make their own copy. Applications that -+** are accessing the same database connection simultaneously on multiple -+** threads should mutex-protect calls to this API and should make their own -+** private copy of the result prior to releasing the mutex. -+*/ -+SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N); -+ - /* - ** CAPI3REF: Return The Filename For A Database Connection - ** METHOD: sqlite3 -@@ -6174,7 +6863,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); - **
  • [sqlite3_filename_wal()] - ** - */ --SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); -+SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName); - - /* - ** CAPI3REF: Determine if a database is read-only -@@ -6186,6 +6875,57 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); - */ - SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); - -+/* -+** CAPI3REF: Determine the transaction state of a database -+** METHOD: sqlite3 -+** -+** ^The sqlite3_txn_state(D,S) interface returns the current -+** [transaction state] of schema S in database connection D. ^If S is NULL, -+** then the highest transaction state of any schema on database connection D -+** is returned. Transaction states are (in order of lowest to highest): -+**
      -+**
    1. SQLITE_TXN_NONE -+**
    2. SQLITE_TXN_READ -+**
    3. SQLITE_TXN_WRITE -+**
    -+** ^If the S argument to sqlite3_txn_state(D,S) is not the name of -+** a valid schema, then -1 is returned. -+*/ -+SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); -+ -+/* -+** CAPI3REF: Allowed return values from sqlite3_txn_state() -+** KEYWORDS: {transaction state} -+** -+** These constants define the current transaction state of a database file. -+** ^The [sqlite3_txn_state(D,S)] interface returns one of these -+** constants in order to describe the transaction state of schema S -+** in [database connection] D. -+** -+**
    -+** [[SQLITE_TXN_NONE]]
    SQLITE_TXN_NONE
    -+**
    The SQLITE_TXN_NONE state means that no transaction is currently -+** pending.
    -+** -+** [[SQLITE_TXN_READ]]
    SQLITE_TXN_READ
    -+**
    The SQLITE_TXN_READ state means that the database is currently -+** in a read transaction. Content has been read from the database file -+** but nothing in the database file has changed. The transaction state -+** will be advanced to SQLITE_TXN_WRITE if any changes occur and there are -+** no other conflicting concurrent write transactions. The transaction -+** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or -+** [COMMIT].
    -+** -+** [[SQLITE_TXN_WRITE]]
    SQLITE_TXN_WRITE
    -+**
    The SQLITE_TXN_WRITE state means that the database is currently -+** in a write transaction. Content has been written to the database file -+** but has not yet committed. The transaction state will change to -+** SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].
    -+*/ -+#define SQLITE_TXN_NONE 0 -+#define SQLITE_TXN_READ 1 -+#define SQLITE_TXN_WRITE 2 -+ - /* - ** CAPI3REF: Find the next prepared statement - ** METHOD: sqlite3 -@@ -6252,6 +6992,72 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); - SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); - SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - -+/* -+** CAPI3REF: Autovacuum Compaction Amount Callback -+** METHOD: sqlite3 -+** -+** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback -+** function C that is invoked prior to each autovacuum of the database -+** file. ^The callback is passed a copy of the generic data pointer (P), -+** the schema-name of the attached database that is being autovacuumed, -+** the size of the database file in pages, the number of free pages, -+** and the number of bytes per page, respectively. The callback should -+** return the number of free pages that should be removed by the -+** autovacuum. ^If the callback returns zero, then no autovacuum happens. -+** ^If the value returned is greater than or equal to the number of -+** free pages, then a complete autovacuum happens. -+** -+**

    ^If there are multiple ATTACH-ed database files that are being -+** modified as part of a transaction commit, then the autovacuum pages -+** callback is invoked separately for each file. -+** -+**

    The callback is not reentrant. The callback function should -+** not attempt to invoke any other SQLite interface. If it does, bad -+** things may happen, including segmentation faults and corrupt database -+** files. The callback function should be a simple function that -+** does some arithmetic on its input parameters and returns a result. -+** -+** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional -+** destructor for the P parameter. ^If X is not NULL, then X(P) is -+** invoked whenever the database connection closes or when the callback -+** is overwritten by another invocation of sqlite3_autovacuum_pages(). -+** -+**

    ^There is only one autovacuum pages callback per database connection. -+** ^Each call to the sqlite3_autovacuum_pages() interface overrides all -+** previous invocations for that database connection. ^If the callback -+** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, -+** then the autovacuum steps callback is canceled. The return value -+** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might -+** be some other error code if something goes wrong. The current -+** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other -+** return codes might be added in future releases. -+** -+**

    If no autovacuum pages callback is specified (the usual case) or -+** a NULL pointer is provided for the callback, -+** then the default behavior is to vacuum all free pages. So, in other -+** words, the default behavior is the same as if the callback function -+** were something like this: -+** -+**

    -+**     unsigned int demonstration_autovac_pages_callback(
    -+**       void *pClientData,
    -+**       const char *zSchema,
    -+**       unsigned int nDbPage,
    -+**       unsigned int nFreePage,
    -+**       unsigned int nBytePerPage
    -+**     ){
    -+**       return nFreePage;
    -+**     }
    -+** 
    -+*/ -+SQLITE_API int sqlite3_autovacuum_pages( -+ sqlite3 *db, -+ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), -+ void*, -+ void(*)(void*) -+); -+ -+ - /* - ** CAPI3REF: Data Change Notification Callbacks - ** METHOD: sqlite3 -@@ -6265,6 +7071,8 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - ** - ** ^The second argument is a pointer to the function to invoke when a - ** row is updated, inserted or deleted in a rowid table. -+** ^The update hook is disabled by invoking sqlite3_update_hook() -+** with a NULL pointer as the second parameter. - ** ^The first argument to the callback is a copy of the third argument - ** to sqlite3_update_hook(). - ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], -@@ -6286,6 +7094,12 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); - ** The exceptions defined in this paragraph might change in a future - ** release of SQLite. - ** -+** Whether the update hook is invoked before or after the -+** corresponding change is currently unspecified and may differ -+** depending on the type of change. Do not rely on the order of the -+** hook call with regards to the final result of the operation which -+** triggers the hook. -+** - ** The update hook implementation must not do anything that will modify - ** the database connection that invoked the update hook. Any actions - ** to modify the database connection must be deferred until after the -@@ -6315,6 +7129,11 @@ SQLITE_API void *sqlite3_update_hook( - ** to the same database. Sharing is enabled if the argument is true - ** and disabled if the argument is false.)^ - ** -+** This interface is omitted if SQLite is compiled with -+** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE] -+** compile-time option is recommended because the -+** [use of shared cache mode is discouraged]. -+** - ** ^Cache sharing is enabled and disabled for an entire process. - ** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]). - ** In prior versions of SQLite, -@@ -6382,7 +7201,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); - ** CAPI3REF: Impose A Limit On Heap Size - ** - ** These interfaces impose limits on the amount of heap memory that will be --** by all database connections within a single process. -+** used by all database connections within a single process. - ** - ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the - ** soft limit on the amount of heap memory that may be allocated by SQLite. -@@ -6413,7 +7232,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); - ** ^The soft heap limit may not be greater than the hard heap limit. - ** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N) - ** is invoked with a value of N that is greater than the hard heap limit, --** the the soft heap limit is set to the value of the hard heap limit. -+** the soft heap limit is set to the value of the hard heap limit. - ** ^The soft heap limit is automatically enabled whenever the hard heap - ** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and - ** the soft heap limit is outside the range of 1..N, then the soft heap -@@ -6440,7 +7259,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); - ** )^ - ** - ** The circumstances under which SQLite will enforce the heap limits may --** changes in future releases of SQLite. -+** change in future releases of SQLite. - */ - SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); - SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N); -@@ -6555,8 +7374,8 @@ SQLITE_API int sqlite3_table_column_metadata( - ** ^The entry point is zProc. - ** ^(zProc may be 0, in which case SQLite will try to come up with an - ** entry point name on its own. It first tries "sqlite3_extension_init". --** If that does not work, it constructs a name "sqlite3_X_init" where the --** X is consists of the lower-case equivalent of all ASCII alphabetic -+** If that does not work, it constructs a name "sqlite3_X_init" where -+** X consists of the lower-case equivalent of all ASCII alphabetic - ** characters in the filename from the last "/" to the first following - ** "." and omitting any initial "lib".)^ - ** ^The sqlite3_load_extension() interface returns -@@ -6627,7 +7446,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); - ** ^(Even though the function prototype shows that xEntryPoint() takes - ** no arguments and returns void, SQLite invokes xEntryPoint() with three - ** arguments and expects an integer result as if the signature of the --** entry point where as follows: -+** entry point were as follows: - ** - **
    - **    int xEntryPoint(
    -@@ -6674,15 +7493,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
    - */
    - SQLITE_API void sqlite3_reset_auto_extension(void);
    - 
    --/*
    --** The interface to the virtual-table mechanism is currently considered
    --** to be experimental.  The interface might change in incompatible ways.
    --** If this is a problem for you, do not use the interface at this time.
    --**
    --** When the virtual-table mechanism stabilizes, we will declare the
    --** interface fixed, support it indefinitely, and remove this comment.
    --*/
    --
    - /*
    - ** Structures used by the virtual table interface
    - */
    -@@ -6743,6 +7553,10 @@ struct sqlite3_module {
    -   /* The methods above are in versions 1 and 2 of the sqlite_module object.
    -   ** Those below are for version 3 and greater. */
    -   int (*xShadowName)(const char*);
    -+  /* The methods above are in versions 1 through 3 of the sqlite_module object.
    -+  ** Those below are for version 4 and greater. */
    -+  int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
    -+                    const char *zTabName, int mFlags, char **pzErr);
    - };
    - 
    - /*
    -@@ -6796,15 +7610,15 @@ struct sqlite3_module {
    - ** virtual table and might not be checked again by the byte code.)^ ^(The
    - ** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
    - ** is left in its default setting of false, the constraint will always be
    --** checked separately in byte code.  If the omit flag is change to true, then
    -+** checked separately in byte code.  If the omit flag is changed to true, then
    - ** the constraint may or may not be checked in byte code.  In other words,
    - ** when the omit flag is true there is no guarantee that the constraint will
    - ** not be checked again using byte code.)^
    - **
    --** ^The idxNum and idxPtr values are recorded and passed into the
    -+** ^The idxNum and idxStr values are recorded and passed into the
    - ** [xFilter] method.
    --** ^[sqlite3_free()] is used to free idxPtr if and only if
    --** needToFreeIdxPtr is true.
    -+** ^[sqlite3_free()] is used to free idxStr if and only if
    -+** needToFreeIdxStr is true.
    - **
    - ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
    - ** the correct order to satisfy the ORDER BY clause so that no separate
    -@@ -6820,9 +7634,11 @@ struct sqlite3_module {
    - ** will be returned by the strategy.
    - **
    - ** The xBestIndex method may optionally populate the idxFlags field with a
    --** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
    --** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
    --** assumes that the strategy may visit at most one row.
    -+** mask of SQLITE_INDEX_SCAN_* flags. One such flag is
    -+** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN]
    -+** output to show the idxNum as hex instead of as decimal.  Another flag is
    -+** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will
    -+** return at most one row.
    - **
    - ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
    - ** SQLite also assumes that if a call to the xUpdate() method is made as
    -@@ -6886,31 +7702,65 @@ struct sqlite3_index_info {
    - ** [sqlite3_index_info].idxFlags field to some combination of
    - ** these bits.
    - */
    --#define SQLITE_INDEX_SCAN_UNIQUE      1     /* Scan visits at most 1 row */
    -+#define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */
    -+#define SQLITE_INDEX_SCAN_HEX    0x00000002 /* Display idxNum as hex */
    -+                                            /* in EXPLAIN QUERY PLAN */
    - 
    - /*
    - ** CAPI3REF: Virtual Table Constraint Operator Codes
    - **
    - ** These macros define the allowed values for the
    - ** [sqlite3_index_info].aConstraint[].op field.  Each value represents
    --** an operator that is part of a constraint term in the wHERE clause of
    -+** an operator that is part of a constraint term in the WHERE clause of
    - ** a query that uses a [virtual table].
    --*/
    --#define SQLITE_INDEX_CONSTRAINT_EQ         2
    --#define SQLITE_INDEX_CONSTRAINT_GT         4
    --#define SQLITE_INDEX_CONSTRAINT_LE         8
    --#define SQLITE_INDEX_CONSTRAINT_LT        16
    --#define SQLITE_INDEX_CONSTRAINT_GE        32
    --#define SQLITE_INDEX_CONSTRAINT_MATCH     64
    --#define SQLITE_INDEX_CONSTRAINT_LIKE      65
    --#define SQLITE_INDEX_CONSTRAINT_GLOB      66
    --#define SQLITE_INDEX_CONSTRAINT_REGEXP    67
    --#define SQLITE_INDEX_CONSTRAINT_NE        68
    --#define SQLITE_INDEX_CONSTRAINT_ISNOT     69
    --#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
    --#define SQLITE_INDEX_CONSTRAINT_ISNULL    71
    --#define SQLITE_INDEX_CONSTRAINT_IS        72
    --#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
    -+**
    -+** ^The left-hand operand of the operator is given by the corresponding
    -+** aConstraint[].iColumn field.  ^An iColumn of -1 indicates the left-hand
    -+** operand is the rowid.
    -+** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
    -+** operators have no left-hand operand, and so for those operators the
    -+** corresponding aConstraint[].iColumn is meaningless and should not be
    -+** used.
    -+**
    -+** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
    -+** value 255 are reserved to represent functions that are overloaded
    -+** by the [xFindFunction|xFindFunction method] of the virtual table
    -+** implementation.
    -+**
    -+** The right-hand operands for each constraint might be accessible using
    -+** the [sqlite3_vtab_rhs_value()] interface.  Usually the right-hand
    -+** operand is only available if it appears as a single constant literal
    -+** in the input SQL.  If the right-hand operand is another column or an
    -+** expression (even a constant expression) or a parameter, then the
    -+** sqlite3_vtab_rhs_value() probably will not be able to extract it.
    -+** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
    -+** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
    -+** and hence calls to sqlite3_vtab_rhs_value() for those operators will
    -+** always return SQLITE_NOTFOUND.
    -+**
    -+** The collating sequence to be used for comparison can be found using
    -+** the [sqlite3_vtab_collation()] interface.  For most real-world virtual
    -+** tables, the collating sequence of constraints does not matter (for example
    -+** because the constraints are numeric) and so the sqlite3_vtab_collation()
    -+** interface is not commonly needed.
    -+*/
    -+#define SQLITE_INDEX_CONSTRAINT_EQ          2
    -+#define SQLITE_INDEX_CONSTRAINT_GT          4
    -+#define SQLITE_INDEX_CONSTRAINT_LE          8
    -+#define SQLITE_INDEX_CONSTRAINT_LT         16
    -+#define SQLITE_INDEX_CONSTRAINT_GE         32
    -+#define SQLITE_INDEX_CONSTRAINT_MATCH      64
    -+#define SQLITE_INDEX_CONSTRAINT_LIKE       65
    -+#define SQLITE_INDEX_CONSTRAINT_GLOB       66
    -+#define SQLITE_INDEX_CONSTRAINT_REGEXP     67
    -+#define SQLITE_INDEX_CONSTRAINT_NE         68
    -+#define SQLITE_INDEX_CONSTRAINT_ISNOT      69
    -+#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL  70
    -+#define SQLITE_INDEX_CONSTRAINT_ISNULL     71
    -+#define SQLITE_INDEX_CONSTRAINT_IS         72
    -+#define SQLITE_INDEX_CONSTRAINT_LIMIT      73
    -+#define SQLITE_INDEX_CONSTRAINT_OFFSET     74
    -+#define SQLITE_INDEX_CONSTRAINT_FUNCTION  150
    - 
    - /*
    - ** CAPI3REF: Register A Virtual Table Implementation
    -@@ -6927,7 +7777,7 @@ struct sqlite3_index_info {
    - ** the implementation of the [virtual table module].   ^The fourth
    - ** parameter is an arbitrary client data pointer that is passed through
    - ** into the [xCreate] and [xConnect] methods of the virtual table module
    --** when a new virtual table is be being created or reinitialized.
    -+** when a new virtual table is being created or reinitialized.
    - **
    - ** ^The sqlite3_create_module_v2() interface has a fifth parameter which
    - ** is a pointer to a destructor for the pClientData.  ^SQLite will
    -@@ -6939,7 +7789,7 @@ struct sqlite3_index_info {
    - ** destructor.
    - **
    - ** ^If the third parameter (the pointer to the sqlite3_module object) is
    --** NULL then no new module is create and any existing modules with the
    -+** NULL then no new module is created and any existing modules with the
    - ** same name are dropped.
    - **
    - ** See also: [sqlite3_drop_modules()]
    -@@ -7051,16 +7901,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
    - */
    - SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
    - 
    --/*
    --** The interface to the virtual-table mechanism defined above (back up
    --** to a comment remarkably similar to this one) is currently considered
    --** to be experimental.  The interface might change in incompatible ways.
    --** If this is a problem for you, do not use the interface at this time.
    --**
    --** When the virtual-table mechanism stabilizes, we will declare the
    --** interface fixed, support it indefinitely, and remove this comment.
    --*/
    --
    - /*
    - ** CAPI3REF: A Handle To An Open BLOB
    - ** KEYWORDS: {BLOB handle} {BLOB handles}
    -@@ -7102,7 +7942,7 @@ typedef struct sqlite3_blob sqlite3_blob;
    - ** in *ppBlob. Otherwise an [error code] is returned and, unless the error
    - ** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
    - ** the API is not misused, it is always safe to call [sqlite3_blob_close()]
    --** on *ppBlob after this function it returns.
    -+** on *ppBlob after this function returns.
    - **
    - ** This function fails with SQLITE_ERROR if any of the following are true:
    - ** 
      -@@ -7208,7 +8048,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); - ** code is returned and the transaction rolled back. - ** - ** Calling this function with an argument that is not a NULL pointer or an --** open blob handle results in undefined behaviour. ^Calling this routine -+** open blob handle results in undefined behavior. ^Calling this routine - ** with a null pointer (such as would be returned by a failed call to - ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function - ** is passed a valid open blob handle, the values returned by the -@@ -7222,7 +8062,7 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); - ** - ** ^Returns the size in bytes of the BLOB accessible via the - ** successfully opened [BLOB handle] in its only argument. ^The --** incremental blob I/O routines can only read or overwriting existing -+** incremental blob I/O routines can only read or overwrite existing - ** blob content; they cannot change the size of a blob. - ** - ** This routine only works on a [BLOB handle] which has been created -@@ -7372,7 +8212,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); - ** ^The sqlite3_mutex_alloc() routine allocates a new - ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() - ** routine returns NULL if it is unable to allocate the requested --** mutex. The argument to sqlite3_mutex_alloc() must one of these -+** mutex. The argument to sqlite3_mutex_alloc() must be one of these - ** integer constants: - ** - **
        -@@ -7435,18 +8275,20 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); - ** - ** ^(Some systems (for example, Windows 95) do not support the operation - ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() --** will always return SQLITE_BUSY. The SQLite core only ever uses --** sqlite3_mutex_try() as an optimization so this is acceptable --** behavior.)^ -+** will always return SQLITE_BUSY. In most cases the SQLite core only uses -+** sqlite3_mutex_try() as an optimization, so this is acceptable -+** behavior. The exceptions are unix builds that set the -+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working -+** sqlite3_mutex_try() is required.)^ - ** - ** ^The sqlite3_mutex_leave() routine exits a mutex that was - ** previously entered by the same thread. The behavior - ** is undefined if the mutex is not currently entered by the - ** calling thread or is not currently allocated. - ** --** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or --** sqlite3_mutex_leave() is a NULL pointer, then all three routines --** behave as no-ops. -+** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), -+** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, -+** then any of the four routines behaves as a no-op. - ** - ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. - */ -@@ -7603,7 +8445,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); - ** CAPI3REF: Retrieve the mutex for a database connection - ** METHOD: sqlite3 - ** --** ^This interface returns a pointer the [sqlite3_mutex] object that -+** ^This interface returns a pointer to the [sqlite3_mutex] object that - ** serializes access to the [database connection] given in the argument - ** when the [threading mode] is Serialized. - ** ^If the [threading mode] is Single-thread or Multi-thread then this -@@ -7688,6 +8530,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); - #define SQLITE_TESTCTRL_PRNG_SAVE 5 - #define SQLITE_TESTCTRL_PRNG_RESTORE 6 - #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ -+#define SQLITE_TESTCTRL_FK_NO_ACTION 7 - #define SQLITE_TESTCTRL_BITVEC_TEST 8 - #define SQLITE_TESTCTRL_FAULT_INSTALL 9 - #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 -@@ -7695,8 +8538,10 @@ SQLITE_API int sqlite3_test_control(int op, ...); - #define SQLITE_TESTCTRL_ASSERT 12 - #define SQLITE_TESTCTRL_ALWAYS 13 - #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ -+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14 - #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 - #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ -+#define SQLITE_TESTCTRL_GETOPT 16 - #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ - #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 - #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 -@@ -7712,20 +8557,25 @@ SQLITE_API int sqlite3_test_control(int op, ...); - #define SQLITE_TESTCTRL_RESULT_INTREAL 27 - #define SQLITE_TESTCTRL_PRNG_SEED 28 - #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 --#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ -+#define SQLITE_TESTCTRL_SEEK_COUNT 30 -+#define SQLITE_TESTCTRL_TRACEFLAGS 31 -+#define SQLITE_TESTCTRL_TUNE 32 -+#define SQLITE_TESTCTRL_LOGEST 33 -+#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ -+#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ - - /* - ** CAPI3REF: SQL Keyword Checking - ** - ** These routines provide access to the set of SQL language keywords --** recognized by SQLite. Applications can uses these routines to determine -+** recognized by SQLite. Applications can use these routines to determine - ** whether or not a specific identifier needs to be escaped (for example, - ** by enclosing in double-quotes) so as not to confuse the parser. - ** - ** The sqlite3_keyword_count() interface returns the number of distinct - ** keywords understood by SQLite. - ** --** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and -+** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and - ** makes *Z point to that keyword expressed as UTF8 and writes the number - ** of bytes in the keyword into *L. The string that *Z points to is not - ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns -@@ -7886,7 +8736,7 @@ SQLITE_API void sqlite3_str_reset(sqlite3_str*); - ** content of the dynamic string under construction in X. The value - ** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X - ** and might be freed or altered by any subsequent method on the same --** [sqlite3_str] object. Applications must not used the pointer returned -+** [sqlite3_str] object. Applications must not use the pointer returned by - ** [sqlite3_str_value(X)] after any subsequent method call on the same - ** object. ^Applications may change the content of the string returned - ** by [sqlite3_str_value(X)] as long as they do not write into any bytes -@@ -7972,7 +8822,7 @@ SQLITE_API int sqlite3_status64( - ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] - ** buffer and where forced to overflow to [sqlite3_malloc()]. The - ** returned value includes allocations that overflowed because they --** where too large (they were larger than the "sz" parameter to -+** were too large (they were larger than the "sz" parameter to - ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because - ** no space was left in the page cache.)^ - ** -@@ -8056,28 +8906,29 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
        SQLITE_DBSTATUS_LOOKASIDE_HIT
        - **
        This parameter returns the number of malloc attempts that were - ** satisfied using lookaside memory. Only the high-water value is meaningful; --** the current value is always zero.)^ -+** the current value is always zero.
        )^ - ** - ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] - ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
        --**
        This parameter returns the number malloc attempts that might have -+**
        This parameter returns the number of malloc attempts that might have - ** been satisfied using lookaside memory but failed due to the amount of - ** memory requested being larger than the lookaside slot size. - ** Only the high-water value is meaningful; --** the current value is always zero.)^ -+** the current value is always zero.
        )^ - ** - ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] - ** ^(
        SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
        --**
        This parameter returns the number malloc attempts that might have -+**
        This parameter returns the number of malloc attempts that might have - ** been satisfied using lookaside memory but failed due to all lookaside - ** memory already being in use. - ** Only the high-water value is meaningful; --** the current value is always zero.)^ -+** the current value is always zero.
        )^ - ** - ** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
        SQLITE_DBSTATUS_CACHE_USED
        - **
        This parameter returns the approximate number of bytes of heap - ** memory used by all pager caches associated with the database connection.)^ - ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. -+**
        - ** - ** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]] - ** ^(
        SQLITE_DBSTATUS_CACHE_USED_SHARED
        -@@ -8086,10 +8937,10 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** memory used by that pager cache is divided evenly between the attached - ** connections.)^ In other words, if none of the pager caches associated - ** with the database connection are shared, this request returns the same --** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are -+** value as DBSTATUS_CACHE_USED. Or, if one or more of the pager caches are - ** shared, the value returned by this call will be smaller than that returned - ** by DBSTATUS_CACHE_USED. ^The highwater mark associated with --** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. -+** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. - ** - ** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
        SQLITE_DBSTATUS_SCHEMA_USED
        - **
        This parameter returns the approximate number of bytes of heap -@@ -8099,6 +8950,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** schema memory is shared with other database connections due to - ** [shared cache mode] being enabled. - ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. -+**
        - ** - ** [[SQLITE_DBSTATUS_STMT_USED]] ^(
        SQLITE_DBSTATUS_STMT_USED
        - **
        This parameter returns the approximate number of bytes of heap -@@ -8135,7 +8987,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r - ** been written to disk in the middle of a transaction due to the page - ** cache overflowing. Transactions are more efficient if they are written - ** to disk all at once. When pages spill mid-transaction, that introduces --** additional overhead. This parameter can be used help identify -+** additional overhead. This parameter can be used to help identify - ** inefficiencies that can be resolved by increasing the cache size. - **
        - ** -@@ -8206,13 +9058,13 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - ** [[SQLITE_STMTSTATUS_SORT]]
        SQLITE_STMTSTATUS_SORT
        - **
        ^This is the number of sort operations that have occurred. - ** A non-zero value in this counter may indicate an opportunity to --** improvement performance through careful use of indices.
        -+** improve performance through careful use of indices. - ** - ** [[SQLITE_STMTSTATUS_AUTOINDEX]]
        SQLITE_STMTSTATUS_AUTOINDEX
        - **
        ^This is the number of rows inserted into transient indices that - ** were created automatically in order to help joins run faster. - ** A non-zero value in this counter may indicate an opportunity to --** improvement performance by adding permanent indices that do not -+** improve performance by adding permanent indices that do not - ** need to be reinitialized each time the statement is run.
        - ** - ** [[SQLITE_STMTSTATUS_VM_STEP]]
        SQLITE_STMTSTATUS_VM_STEP
        -@@ -8221,19 +9073,29 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - ** to 2147483647. The number of virtual machine operations can be - ** used as a proxy for the total work done by the prepared statement. - ** If the number of virtual machine operations exceeds 2147483647 --** then the value returned by this statement status code is undefined. -+** then the value returned by this statement status code is undefined. - ** - ** [[SQLITE_STMTSTATUS_REPREPARE]]
        SQLITE_STMTSTATUS_REPREPARE
        - **
        ^This is the number of times that the prepare statement has been - ** automatically regenerated due to schema changes or changes to --** [bound parameters] that might affect the query plan. -+** [bound parameters] that might affect the query plan.
        - ** - ** [[SQLITE_STMTSTATUS_RUN]]
        SQLITE_STMTSTATUS_RUN
        - **
        ^This is the number of times that the prepared statement has - ** been run. A single "run" for the purposes of this counter is one - ** or more calls to [sqlite3_step()] followed by a call to [sqlite3_reset()]. - ** The counter is incremented on the first [sqlite3_step()] call of each --** cycle. -+** cycle.
        -+** -+** [[SQLITE_STMTSTATUS_FILTER_MISS]] -+** [[SQLITE_STMTSTATUS_FILTER HIT]] -+**
        SQLITE_STMTSTATUS_FILTER_HIT
        -+** SQLITE_STMTSTATUS_FILTER_MISS
        -+**
        ^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join -+** step was bypassed because a Bloom filter returned not-found. The -+** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of -+** times that the Bloom filter returned a find, and thus the join step -+** had to be processed as normal.
        - ** - ** [[SQLITE_STMTSTATUS_MEMUSED]]
        SQLITE_STMTSTATUS_MEMUSED
        - **
        ^This is the approximate number of bytes of heap memory -@@ -8249,6 +9111,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); - #define SQLITE_STMTSTATUS_VM_STEP 4 - #define SQLITE_STMTSTATUS_REPREPARE 5 - #define SQLITE_STMTSTATUS_RUN 6 -+#define SQLITE_STMTSTATUS_FILTER_MISS 7 -+#define SQLITE_STMTSTATUS_FILTER_HIT 8 - #define SQLITE_STMTSTATUS_MEMUSED 99 - - /* -@@ -8336,9 +9200,9 @@ struct sqlite3_pcache_page { - ** SQLite will typically create one cache instance for each open database file, - ** though this is not guaranteed. ^The - ** first parameter, szPage, is the size in bytes of the pages that must --** be allocated by the cache. ^szPage will always a power of two. ^The -+** be allocated by the cache. ^szPage will always be a power of two. ^The - ** second parameter szExtra is a number of bytes of extra storage --** associated with each page cache entry. ^The szExtra parameter will -+** associated with each page cache entry. ^The szExtra parameter will be - ** a number less than 250. SQLite will use the - ** extra szExtra bytes on each page to store metadata about the underlying - ** database page on disk. The value passed into szExtra depends -@@ -8346,17 +9210,17 @@ struct sqlite3_pcache_page { - ** ^The third argument to xCreate(), bPurgeable, is true if the cache being - ** created will be used to cache database pages of a file stored on disk, or - ** false if it is used for an in-memory database. The cache implementation --** does not have to do anything special based with the value of bPurgeable; -+** does not have to do anything special based upon the value of bPurgeable; - ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will - ** never invoke xUnpin() except to deliberately delete a page. - ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to - ** false will always have the "discard" flag set to true. --** ^Hence, a cache created with bPurgeable false will -+** ^Hence, a cache created with bPurgeable set to false will - ** never contain any unpinned pages. - ** - ** [[the xCachesize() page cache method]] - ** ^(The xCachesize() method may be called at any time by SQLite to set the --** suggested maximum cache-size (number of pages stored by) the cache -+** suggested maximum cache-size (number of pages stored) for the cache - ** instance passed as the first argument. This is the value configured using - ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable - ** parameter, the implementation is not required to do anything with this -@@ -8383,12 +9247,12 @@ struct sqlite3_pcache_page { - ** implementation must return a pointer to the page buffer with its content - ** intact. If the requested page is not already in the cache, then the - ** cache implementation should use the value of the createFlag --** parameter to help it determined what action to take: -+** parameter to help it determine what action to take: - ** - ** - **
        createFlag Behavior when page is not already in cache - **
        0 Do not allocate a new page. Return NULL. --**
        1 Allocate a new page if it easy and convenient to do so. -+**
        1 Allocate a new page if it is easy and convenient to do so. - ** Otherwise return NULL. - **
        2 Make every effort to allocate a new page. Only return - ** NULL if allocating a new page is effectively impossible. -@@ -8405,7 +9269,7 @@ struct sqlite3_pcache_page { - ** as its second argument. If the third parameter, discard, is non-zero, - ** then the page must be evicted from the cache. - ** ^If the discard parameter is --** zero, then the page may be discarded or retained at the discretion of -+** zero, then the page may be discarded or retained at the discretion of the - ** page cache implementation. ^The page cache implementation - ** may choose to evict unpinned pages at any time. - ** -@@ -8423,7 +9287,7 @@ struct sqlite3_pcache_page { - ** When SQLite calls the xTruncate() method, the cache must discard all - ** existing cache entries with page numbers (keys) greater than or equal - ** to the value of the iLimit parameter passed to xTruncate(). If any --** of these pages are pinned, they are implicitly unpinned, meaning that -+** of these pages are pinned, they become implicitly unpinned, meaning that - ** they can be safely discarded. - ** - ** [[the xDestroy() page cache method]] -@@ -8603,7 +9467,7 @@ typedef struct sqlite3_backup sqlite3_backup; - ** external process or via a database connection other than the one being - ** used by the backup operation, then the backup will be automatically - ** restarted by the next call to sqlite3_backup_step(). ^If the source --** database is modified by the using the same database connection as is used -+** database is modified by using the same database connection as is used - ** by the backup operation, then the backup database is automatically - ** updated at the same time. - ** -@@ -8620,7 +9484,7 @@ typedef struct sqlite3_backup sqlite3_backup; - ** and may not be used following a call to sqlite3_backup_finish(). - ** - ** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no --** sqlite3_backup_step() errors occurred, regardless or whether or not -+** sqlite3_backup_step() errors occurred, regardless of whether or not - ** sqlite3_backup_step() completed. - ** ^If an out-of-memory condition or IO error occurred during any prior - ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then -@@ -8660,7 +9524,7 @@ typedef struct sqlite3_backup sqlite3_backup; - ** if the application incorrectly accesses the destination [database connection] - ** and so no error code is reported, but the operations may malfunction - ** nevertheless. Use of the destination database connection while a --** backup is in progress might also also cause a mutex deadlock. -+** backup is in progress might also cause a mutex deadlock. - ** - ** If running in [shared cache mode], the application must - ** guarantee that the shared cache used by the destination database -@@ -8675,6 +9539,16 @@ typedef struct sqlite3_backup sqlite3_backup; - ** APIs are not strictly speaking threadsafe. If they are invoked at the - ** same time as another thread is invoking sqlite3_backup_step() it is - ** possible that they return invalid values. -+** -+** Alternatives To Using The Backup API -+** -+** Other techniques for safely creating a consistent backup of an SQLite -+** database include: -+** -+**
          -+**
        • The [VACUUM INTO] command. -+**
        • The [sqlite3_rsync] utility program. -+**
        - */ - SQLITE_API sqlite3_backup *sqlite3_backup_init( - sqlite3 *pDest, /* Destination database handle */ -@@ -8712,7 +9586,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); - ** application receives an SQLITE_LOCKED error, it may call the - ** sqlite3_unlock_notify() method with the blocked connection handle as - ** the first argument to register for a callback that will be invoked --** when the blocking connections current transaction is concluded. ^The -+** when the blocking connection's current transaction is concluded. ^The - ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] - ** call that concludes the blocking connection's transaction. - ** -@@ -8732,7 +9606,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); - ** blocked connection already has a registered unlock-notify callback, - ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is - ** called with a NULL pointer as its second argument, then any existing --** unlock-notify callback is canceled. ^The blocked connections -+** unlock-notify callback is canceled. ^The blocked connection's - ** unlock-notify callback may also be canceled by closing the blocked - ** connection using [sqlite3_close()]. - ** -@@ -8912,8 +9786,9 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); - ** - ** A single database handle may have at most a single write-ahead log callback - ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any --** previously registered write-ahead log callback. ^Note that the --** [sqlite3_wal_autocheckpoint()] interface and the -+** previously registered write-ahead log callback. ^The return value is -+** a copy of the third parameter from the previous call, if any, or 0. -+** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the - ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will - ** overwrite any prior [sqlite3_wal_hook()] settings. - */ -@@ -9087,7 +9962,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( - */ - #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ - #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ --#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ -+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */ - #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ - - /* -@@ -9129,7 +10004,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - ** support constraints. In this configuration (which is the default) if - ** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire - ** statement is rolled back as if [ON CONFLICT | OR ABORT] had been --** specified as part of the users SQL statement, regardless of the actual -+** specified as part of the user's SQL statement, regardless of the actual - ** ON CONFLICT mode specified. - ** - ** If X is non-zero, then the virtual table implementation guarantees -@@ -9155,7 +10030,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - ** [[SQLITE_VTAB_DIRECTONLY]]
        SQLITE_VTAB_DIRECTONLY
        - **
        Calls of the form - ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the --** the [xConnect] or [xCreate] methods of a [virtual table] implmentation -+** the [xConnect] or [xCreate] methods of a [virtual table] implementation - ** prohibits that virtual table from being used from within triggers and - ** views. - **
        -@@ -9163,18 +10038,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); - ** [[SQLITE_VTAB_INNOCUOUS]]
        SQLITE_VTAB_INNOCUOUS
        - **
        Calls of the form - ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the --** the [xConnect] or [xCreate] methods of a [virtual table] implmentation -+** [xConnect] or [xCreate] methods of a [virtual table] implementation - ** identify that virtual table as being safe to use from within triggers - ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the - ** virtual table can do no serious harm even if it is controlled by a - ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS - ** flag unless absolutely necessary. - **
        -+** -+** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]
        SQLITE_VTAB_USES_ALL_SCHEMAS
        -+**
        Calls of the form -+** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the -+** the [xConnect] or [xCreate] methods of a [virtual table] implementation -+** instruct the query planner to begin at least a read transaction on -+** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the -+** virtual table is used. -+**
        - ** - */ - #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 - #define SQLITE_VTAB_INNOCUOUS 2 - #define SQLITE_VTAB_DIRECTONLY 3 -+#define SQLITE_VTAB_USES_ALL_SCHEMAS 4 - - /* - ** CAPI3REF: Determine The Virtual Table Conflict Policy -@@ -9192,10 +10077,11 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); - ** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE - ** - ** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn] --** method of a [virtual table], then it returns true if and only if the -+** method of a [virtual table], then it might return true if the - ** column is being fetched as part of an UPDATE operation during which the --** column value will not change. Applications might use this to substitute --** a return value that is less expensive to compute and that the corresponding -+** column value will not change. The virtual table implementation can use -+** this hint as permission to substitute a return value that is less -+** expensive to compute and that the corresponding - ** [xUpdate] method understands as a "no-change" value. - ** - ** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that -@@ -9204,31 +10090,314 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); - ** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces]. - ** In that case, [sqlite3_value_nochange(X)] will return true for the - ** same column in the [xUpdate] method. -+** -+** The sqlite3_vtab_nochange() routine is an optimization. Virtual table -+** implementations should continue to give a correct answer even if the -+** sqlite3_vtab_nochange() interface were to always return false. In the -+** current implementation, the sqlite3_vtab_nochange() interface does always -+** returns false for the enhanced [UPDATE FROM] statement. - */ - SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*); - - /* - ** CAPI3REF: Determine The Collation For a Virtual Table Constraint -+** METHOD: sqlite3_index_info - ** - ** This function may only be called from within a call to the [xBestIndex] --** method of a [virtual table]. -+** method of a [virtual table]. This function returns a pointer to a string -+** that is the name of the appropriate collation sequence to use for text -+** comparisons on the constraint identified by its arguments. - ** --** The first argument must be the sqlite3_index_info object that is the --** first parameter to the xBestIndex() method. The second argument must be --** an index into the aConstraint[] array belonging to the sqlite3_index_info --** structure passed to xBestIndex. This function returns a pointer to a buffer --** containing the name of the collation sequence for the corresponding --** constraint. -+** The first argument must be the pointer to the [sqlite3_index_info] object -+** that is the first parameter to the xBestIndex() method. The second argument -+** must be an index into the aConstraint[] array belonging to the -+** sqlite3_index_info structure passed to xBestIndex. -+** -+** Important: -+** The first parameter must be the same pointer that is passed into the -+** xBestMethod() method. The first parameter may not be a pointer to a -+** different [sqlite3_index_info] object, even an exact copy. -+** -+** The return value is computed as follows: -+** -+**
          -+**
        1. If the constraint comes from a WHERE clause expression that contains -+** a [COLLATE operator], then the name of the collation specified by -+** that COLLATE operator is returned. -+**

        2. If there is no COLLATE operator, but the column that is the subject -+** of the constraint specifies an alternative collating sequence via -+** a [COLLATE clause] on the column definition within the CREATE TABLE -+** statement that was passed into [sqlite3_declare_vtab()], then the -+** name of that alternative collating sequence is returned. -+**

        3. Otherwise, "BINARY" is returned. -+**

        - */ --SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); -+SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); -+ -+/* -+** CAPI3REF: Determine if a virtual table query is DISTINCT -+** METHOD: sqlite3_index_info -+** -+** This API may only be used from within an [xBestIndex|xBestIndex method] -+** of a [virtual table] implementation. The result of calling this -+** interface from outside of xBestIndex() is undefined and probably harmful. -+** -+** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and -+** 3. The integer returned by sqlite3_vtab_distinct() -+** gives the virtual table additional information about how the query -+** planner wants the output to be ordered. As long as the virtual table -+** can meet the ordering requirements of the query planner, it may set -+** the "orderByConsumed" flag. -+** -+**
        1. -+** ^If the sqlite3_vtab_distinct() interface returns 0, that means -+** that the query planner needs the virtual table to return all rows in the -+** sort order defined by the "nOrderBy" and "aOrderBy" fields of the -+** [sqlite3_index_info] object. This is the default expectation. If the -+** virtual table outputs all rows in sorted order, then it is always safe for -+** the xBestIndex method to set the "orderByConsumed" flag, regardless of -+** the return value from sqlite3_vtab_distinct(). -+**

        2. -+** ^(If the sqlite3_vtab_distinct() interface returns 1, that means -+** that the query planner does not need the rows to be returned in sorted order -+** as long as all rows with the same values in all columns identified by the -+** "aOrderBy" field are adjacent.)^ This mode is used when the query planner -+** is doing a GROUP BY. -+**

        3. -+** ^(If the sqlite3_vtab_distinct() interface returns 2, that means -+** that the query planner does not need the rows returned in any particular -+** order, as long as rows with the same values in all columns identified -+** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows -+** contain the same values for all columns identified by "colUsed", all but -+** one such row may optionally be omitted from the result.)^ -+** The virtual table is not required to omit rows that are duplicates -+** over the "colUsed" columns, but if the virtual table can do that without -+** too much extra effort, it could potentially help the query to run faster. -+** This mode is used for a DISTINCT query. -+**

        4. -+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the -+** virtual table must return rows in the order defined by "aOrderBy" as -+** if the sqlite3_vtab_distinct() interface had returned 0. However if -+** two or more rows in the result have the same values for all columns -+** identified by "colUsed", then all but one such row may optionally be -+** omitted.)^ Like when the return value is 2, the virtual table -+** is not required to omit rows that are duplicates over the "colUsed" -+** columns, but if the virtual table can do that without -+** too much extra effort, it could potentially help the query to run faster. -+** This mode is used for queries -+** that have both DISTINCT and ORDER BY clauses. -+**

        -+** -+**

        The following table summarizes the conditions under which the -+** virtual table is allowed to set the "orderByConsumed" flag based on -+** the value returned by sqlite3_vtab_distinct(). This table is a -+** restatement of the previous four paragraphs: -+** -+** -+** -+**
        sqlite3_vtab_distinct() return value -+** Rows are returned in aOrderBy order -+** Rows with the same value in all aOrderBy columns are adjacent -+** Duplicates over all colUsed columns may be omitted -+**
        0yesyesno -+**
        1noyesno -+**
        2noyesyes -+**
        3yesyesyes -+**
        -+** -+** ^For the purposes of comparing virtual table output values to see if the -+** values are the same value for sorting purposes, two NULL values are considered -+** to be the same. In other words, the comparison operator is "IS" -+** (or "IS NOT DISTINCT FROM") and not "==". -+** -+** If a virtual table implementation is unable to meet the requirements -+** specified above, then it must not set the "orderByConsumed" flag in the -+** [sqlite3_index_info] object or an incorrect answer may result. -+** -+** ^A virtual table implementation is always free to return rows in any order -+** it wants, as long as the "orderByConsumed" flag is not set. ^When the -+** "orderByConsumed" flag is unset, the query planner will add extra -+** [bytecode] to ensure that the final results returned by the SQL query are -+** ordered correctly. The use of the "orderByConsumed" flag and the -+** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful -+** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed" -+** flag might help queries against a virtual table to run faster. Being -+** overly aggressive and setting the "orderByConsumed" flag when it is not -+** valid to do so, on the other hand, might cause SQLite to return incorrect -+** results. -+*/ -+SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); -+ -+/* -+** CAPI3REF: Identify and handle IN constraints in xBestIndex -+** -+** This interface may only be used from within an -+** [xBestIndex|xBestIndex() method] of a [virtual table] implementation. -+** The result of invoking this interface from any other context is -+** undefined and probably harmful. -+** -+** ^(A constraint on a virtual table of the form -+** "[IN operator|column IN (...)]" is -+** communicated to the xBestIndex method as a -+** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use -+** this constraint, it must set the corresponding -+** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under -+** the usual mode of handling IN operators, SQLite generates [bytecode] -+** that invokes the [xFilter|xFilter() method] once for each value -+** on the right-hand side of the IN operator.)^ Thus the virtual table -+** only sees a single value from the right-hand side of the IN operator -+** at a time. -+** -+** In some cases, however, it would be advantageous for the virtual -+** table to see all values on the right-hand of the IN operator all at -+** once. The sqlite3_vtab_in() interfaces facilitates this in two ways: -+** -+**

          -+**
        1. -+** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero) -+** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint -+** is an [IN operator] that can be processed all at once. ^In other words, -+** sqlite3_vtab_in() with -1 in the third argument is a mechanism -+** by which the virtual table can ask SQLite if all-at-once processing -+** of the IN operator is even possible. -+** -+**

        2. -+** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates -+** to SQLite that the virtual table does or does not want to process -+** the IN operator all-at-once, respectively. ^Thus when the third -+** parameter (F) is non-negative, this interface is the mechanism by -+** which the virtual table tells SQLite how it wants to process the -+** IN operator. -+**

        -+** -+** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times -+** within the same xBestIndex method call. ^For any given P,N pair, -+** the return value from sqlite3_vtab_in(P,N,F) will always be the same -+** within the same xBestIndex call. ^If the interface returns true -+** (non-zero), that means that the constraint is an IN operator -+** that can be processed all-at-once. ^If the constraint is not an IN -+** operator or cannot be processed all-at-once, then the interface returns -+** false. -+** -+** ^(All-at-once processing of the IN operator is selected if both of the -+** following conditions are met: -+** -+**
          -+**
        1. The P->aConstraintUsage[N].argvIndex value is set to a positive -+** integer. This is how the virtual table tells SQLite that it wants to -+** use the N-th constraint. -+** -+**

        2. The last call to sqlite3_vtab_in(P,N,F) for which F was -+** non-negative had F>=1. -+**

        )^ -+** -+** ^If either or both of the conditions above are false, then SQLite uses -+** the traditional one-at-a-time processing strategy for the IN constraint. -+** ^If both conditions are true, then the argvIndex-th parameter to the -+** xFilter method will be an [sqlite3_value] that appears to be NULL, -+** but which can be passed to [sqlite3_vtab_in_first()] and -+** [sqlite3_vtab_in_next()] to find all values on the right-hand side -+** of the IN constraint. -+*/ -+SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); -+ -+/* -+** CAPI3REF: Find all elements on the right-hand side of an IN constraint. -+** -+** These interfaces are only useful from within the -+** [xFilter|xFilter() method] of a [virtual table] implementation. -+** The result of invoking these interfaces from any other context -+** is undefined and probably harmful. -+** -+** The X parameter in a call to sqlite3_vtab_in_first(X,P) or -+** sqlite3_vtab_in_next(X,P) should be one of the parameters to the -+** xFilter method which invokes these routines, and specifically -+** a parameter that was previously selected for all-at-once IN constraint -+** processing using the [sqlite3_vtab_in()] interface in the -+** [xBestIndex|xBestIndex method]. ^(If the X parameter is not -+** an xFilter argument that was selected for all-at-once IN constraint -+** processing, then these routines return [SQLITE_ERROR].)^ -+** -+** ^(Use these routines to access all values on the right-hand side -+** of the IN constraint using code like the following: -+** -+**
        -+**    for(rc=sqlite3_vtab_in_first(pList, &pVal);
        -+**        rc==SQLITE_OK && pVal;
        -+**        rc=sqlite3_vtab_in_next(pList, &pVal)
        -+**    ){
        -+**      // do something with pVal
        -+**    }
        -+**    if( rc!=SQLITE_OK ){
        -+**      // an error has occurred
        -+**    }
        -+** 
        )^ -+** -+** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P) -+** routines return SQLITE_OK and set *P to point to the first or next value -+** on the RHS of the IN constraint. ^If there are no more values on the -+** right hand side of the IN constraint, then *P is set to NULL and these -+** routines return [SQLITE_DONE]. ^The return value might be -+** some other value, such as SQLITE_NOMEM, in the event of a malfunction. -+** -+** The *ppOut values returned by these routines are only valid until the -+** next call to either of these routines or until the end of the xFilter -+** method from which these routines were called. If the virtual table -+** implementation needs to retain the *ppOut values for longer, it must make -+** copies. The *ppOut values are [protected sqlite3_value|protected]. -+*/ -+SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); -+SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); -+ -+/* -+** CAPI3REF: Constraint values in xBestIndex() -+** METHOD: sqlite3_index_info -+** -+** This API may only be used from within the [xBestIndex|xBestIndex method] -+** of a [virtual table] implementation. The result of calling this interface -+** from outside of an xBestIndex method are undefined and probably harmful. -+** -+** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within -+** the [xBestIndex] method of a [virtual table] implementation, with P being -+** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and -+** J being a 0-based index into P->aConstraint[], then this routine -+** attempts to set *V to the value of the right-hand operand of -+** that constraint if the right-hand operand is known. ^If the -+** right-hand operand is not known, then *V is set to a NULL pointer. -+** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if -+** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) -+** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th -+** constraint is not available. ^The sqlite3_vtab_rhs_value() interface -+** can return a result code other than SQLITE_OK or SQLITE_NOTFOUND if -+** something goes wrong. -+** -+** The sqlite3_vtab_rhs_value() interface is usually only successful if -+** the right-hand operand of a constraint is a literal value in the original -+** SQL statement. If the right-hand operand is an expression or a reference -+** to some other column or a [host parameter], then sqlite3_vtab_rhs_value() -+** will probably return [SQLITE_NOTFOUND]. -+** -+** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and -+** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such -+** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^ -+** -+** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value -+** and remains valid for the duration of the xBestIndex method call. -+** ^When xBestIndex returns, the sqlite3_value object returned by -+** sqlite3_vtab_rhs_value() is automatically deallocated. -+** -+** The "_rhs_" in the name of this routine is an abbreviation for -+** "Right-Hand Side". -+*/ -+SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal); - - /* - ** CAPI3REF: Conflict resolution modes - ** KEYWORDS: {conflict resolution mode} - ** - ** These constants are returned by [sqlite3_vtab_on_conflict()] to --** inform a [virtual table] implementation what the [ON CONFLICT] mode --** is for the SQL statement being evaluated. -+** inform a [virtual table] implementation of the [ON CONFLICT] mode -+** for the SQL statement being evaluated. - ** - ** Note that the [SQLITE_IGNORE] constant is also used as a potential - ** return value from the [sqlite3_set_authorizer()] callback and that -@@ -9252,6 +10421,10 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - ** managed by the prepared statement S and will be automatically freed when - ** S is finalized. - ** -+** Not all values are available for all query elements. When a value is -+** not available, the output variable is set to -1 if the value is numeric, -+** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). -+** - **
        - ** [[SQLITE_SCANSTAT_NLOOP]]
        SQLITE_SCANSTAT_NLOOP
        - **
        ^The [sqlite3_int64] variable pointed to by the V parameter will be -@@ -9264,27 +10437,39 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - ** [[SQLITE_SCANSTAT_EST]]
        SQLITE_SCANSTAT_EST
        - **
        ^The "double" variable pointed to by the V parameter will be set to the - ** query planner's estimate for the average number of rows output from each --** iteration of the X-th loop. If the query planner's estimates was accurate, -+** iteration of the X-th loop. If the query planner's estimate was accurate, - ** then this value will approximate the quotient NVISIT/NLOOP and the - ** product of this value for all prior loops with the same SELECTID will --** be the NLOOP value for the current loop. -+** be the NLOOP value for the current loop.
        - ** - ** [[SQLITE_SCANSTAT_NAME]]
        SQLITE_SCANSTAT_NAME
        - **
        ^The "const char *" variable pointed to by the V parameter will be set - ** to a zero-terminated UTF-8 string containing the name of the index or table --** used for the X-th loop. -+** used for the X-th loop.
        - ** - ** [[SQLITE_SCANSTAT_EXPLAIN]]
        SQLITE_SCANSTAT_EXPLAIN
        - **
        ^The "const char *" variable pointed to by the V parameter will be set - ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] --** description for the X-th loop. -+** description for the X-th loop.
        - ** --** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECT
        -+** [[SQLITE_SCANSTAT_SELECTID]]
        SQLITE_SCANSTAT_SELECTID
        - **
        ^The "int" variable pointed to by the V parameter will be set to the --** "select-id" for the X-th loop. The select-id identifies which query or --** subquery the loop is part of. The main query has a select-id of zero. --** The select-id is the same value as is output in the first column --** of an [EXPLAIN QUERY PLAN] query. -+** id for the X-th query plan element. The id value is unique within the -+** statement. The select-id is the same value as is output in the first -+** column of an [EXPLAIN QUERY PLAN] query.
        -+** -+** [[SQLITE_SCANSTAT_PARENTID]]
        SQLITE_SCANSTAT_PARENTID
        -+**
        The "int" variable pointed to by the V parameter will be set to the -+** id of the parent of the current query element, if applicable, or -+** to zero if the query element has no parent. This is the same value as -+** returned in the second column of an [EXPLAIN QUERY PLAN] query.
        -+** -+** [[SQLITE_SCANSTAT_NCYCLE]]
        SQLITE_SCANSTAT_NCYCLE
        -+**
        The sqlite3_int64 output value is set to the number of cycles, -+** according to the processor time-stamp counter, that elapsed while the -+** query element was being processed. This value is not available for -+** all query elements - if it is unavailable the output variable is -+** set to -1.
        - **
        - */ - #define SQLITE_SCANSTAT_NLOOP 0 -@@ -9293,12 +10478,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - #define SQLITE_SCANSTAT_NAME 3 - #define SQLITE_SCANSTAT_EXPLAIN 4 - #define SQLITE_SCANSTAT_SELECTID 5 -+#define SQLITE_SCANSTAT_PARENTID 6 -+#define SQLITE_SCANSTAT_NCYCLE 7 - - /* - ** CAPI3REF: Prepared Statement Scan Status - ** METHOD: sqlite3_stmt - ** --** This interface returns information about the predicted and measured -+** These interfaces return information about the predicted and measured - ** performance for pStmt. Advanced applications can use this - ** interface to compare the predicted and the measured performance and - ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. -@@ -9309,19 +10496,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_ - ** - ** The "iScanStatusOp" parameter determines which status information to return. - ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior --** of this interface is undefined. --** ^The requested measurement is written into a variable pointed to by --** the "pOut" parameter. --** Parameter "idx" identifies the specific loop to retrieve statistics for. --** Loops are numbered starting from zero. ^If idx is out of range - less than --** zero or greater than or equal to the total number of loops used to implement --** the statement - a non-zero value is returned and the variable that pOut --** points to is unchanged. --** --** ^Statistics might not be available for all loops in all statements. ^In cases --** where there exist loops with no available statistics, this function behaves --** as if the loop did not exist - it returns non-zero and leave the variable --** that pOut points to unchanged. -+** of this interface is undefined. ^The requested measurement is written into -+** a variable pointed to by the "pOut" parameter. -+** -+** The "flags" parameter must be passed a mask of flags. At present only -+** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX -+** is specified, then status information is available for all elements -+** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If -+** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements -+** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of -+** the EXPLAIN QUERY PLAN output) are available. Invoking API -+** sqlite3_stmt_scanstatus() is equivalent to calling -+** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. -+** -+** Parameter "idx" identifies the specific query element to retrieve statistics -+** for. Query elements are numbered starting from zero. A value of -1 may -+** retrieve statistics for the entire query. ^If idx is out of range -+** - less than -1 or greater than or equal to the total number of query -+** elements used to implement the statement - a non-zero value is returned and -+** the variable that pOut points to is unchanged. - ** - ** See also: [sqlite3_stmt_scanstatus_reset()] - */ -@@ -9331,6 +10524,19 @@ SQLITE_API int sqlite3_stmt_scanstatus( - int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ - void *pOut /* Result written here */ - ); -+SQLITE_API int sqlite3_stmt_scanstatus_v2( -+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ -+ int idx, /* Index of loop to report on */ -+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ -+ int flags, /* Mask of flags defined below */ -+ void *pOut /* Result written here */ -+); -+ -+/* -+** CAPI3REF: Prepared Statement Scan Status -+** KEYWORDS: {scan status flags} -+*/ -+#define SQLITE_SCANSTAT_COMPLEX 0x0001 - - /* - ** CAPI3REF: Zero Scan-Status Counters -@@ -9345,9 +10551,10 @@ SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); - - /* - ** CAPI3REF: Flush caches to disk mid-transaction -+** METHOD: sqlite3 - ** - ** ^If a write-transaction is open on [database connection] D when the --** [sqlite3_db_cacheflush(D)] interface invoked, any dirty -+** [sqlite3_db_cacheflush(D)] interface is invoked, any dirty - ** pages in the pager-cache that are not currently in use are written out - ** to disk. A dirty page may be in use if a database cursor created by an - ** active SQL statement is reading from it, or if it is page 1 of a database -@@ -9377,6 +10584,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); - - /* - ** CAPI3REF: The pre-update hook. -+** METHOD: sqlite3 - ** - ** ^These interfaces are only available if SQLite is compiled using the - ** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option. -@@ -9417,7 +10625,11 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); - ** seventh parameter is the final rowid value of the row being inserted - ** or updated. The value of the seventh parameter passed to the callback - ** function is not defined for operations on WITHOUT ROWID tables, or for --** INSERT operations on rowid tables. -+** DELETE operations on rowid tables. -+** -+** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from -+** the previous call on the same [database connection] D, or NULL for -+** the first call on D. - ** - ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], - ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces -@@ -9455,6 +10667,15 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); - ** triggers; or 2 for changes resulting from triggers called by top-level - ** triggers; and so forth. - ** -+** When the [sqlite3_blob_write()] API is used to update a blob column, -+** the pre-update hook is invoked with SQLITE_DELETE, because -+** the new values are not yet available. In this case, when a -+** callback made with op==SQLITE_DELETE is actually a write using the -+** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns -+** the index of the column being written. In other cases, where the -+** pre-update hook is being invoked for some other reason, including a -+** regular DELETE, sqlite3_preupdate_blobwrite() returns -1. -+** - ** See also: [sqlite3_update_hook()] - */ - #if defined(SQLITE_ENABLE_PREUPDATE_HOOK) -@@ -9475,10 +10696,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); - SQLITE_API int sqlite3_preupdate_count(sqlite3 *); - SQLITE_API int sqlite3_preupdate_depth(sqlite3 *); - SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); -+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *); - #endif - - /* - ** CAPI3REF: Low-level system error code -+** METHOD: sqlite3 - ** - ** ^Attempt to return the underlying operating system error code or error - ** number that caused the most recent I/O error or failure to open a file. -@@ -9525,6 +10748,14 @@ typedef struct sqlite3_snapshot { - ** If there is not already a read-transaction open on schema S when - ** this function is called, one is opened automatically. - ** -+** If a read-transaction is opened by this function, then it is guaranteed -+** that the returned snapshot object may not be invalidated by a database -+** writer or checkpointer until after the read-transaction is closed. This -+** is not guaranteed if a read-transaction is already open when this -+** function is called. In that case, any subsequent write or checkpoint -+** operation on the database may invalidate the returned snapshot handle, -+** even while the read-transaction remains open. -+** - ** The following must be true for this function to succeed. If any of - ** the following statements are false when sqlite3_snapshot_get() is - ** called, SQLITE_ERROR is returned. The final value of *P is undefined -@@ -9682,15 +10913,16 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c - /* - ** CAPI3REF: Serialize a database - ** --** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory --** that is a serialization of the S database on [database connection] D. -+** The sqlite3_serialize(D,S,P,F) interface returns a pointer to -+** memory that is a serialization of the S database on -+** [database connection] D. If S is a NULL pointer, the main database is used. - ** If P is not a NULL pointer, then the size of the database in bytes - ** is written into *P. - ** - ** For an ordinary on-disk database file, the serialization is just a - ** copy of the disk file. For an in-memory database or a "TEMP" database, - ** the serialization is the same sequence of bytes which would be written --** to disk if that database where backed up to disk. -+** to disk if that database were backed up to disk. - ** - ** The usual case is that sqlite3_serialize() copies the serialization of - ** the database into memory obtained from [sqlite3_malloc64()] and returns -@@ -9699,7 +10931,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c - ** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations - ** are made, and the sqlite3_serialize() function will return a pointer - ** to the contiguous memory representation of the database that SQLite --** is currently using for that database, or NULL if the no such contiguous -+** is currently using for that database, or NULL if no such contiguous - ** memory representation of the database exists. A contiguous memory - ** representation of the database will usually only exist if there has - ** been a prior call to [sqlite3_deserialize(D,S,...)] with the same -@@ -9708,12 +10940,19 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c - ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy - ** of the database exists. - ** -+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, -+** the returned buffer content will remain accessible and unchanged -+** until either the next write operation on the connection or when -+** the connection is closed, and applications must not modify the -+** buffer. If the bit had been clear, the returned buffer will not -+** be accessed by SQLite after the call. -+** - ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the - ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory - ** allocation error occurs. - ** --** This interface is only available if SQLite is compiled with the --** [SQLITE_ENABLE_DESERIALIZE] option. -+** This interface is omitted if SQLite is compiled with the -+** [SQLITE_OMIT_DESERIALIZE] option. - */ - SQLITE_API unsigned char *sqlite3_serialize( - sqlite3 *db, /* The database connection */ -@@ -9756,22 +10995,36 @@ SQLITE_API unsigned char *sqlite3_serialize( - ** SQLite will try to increase the buffer size using sqlite3_realloc64() - ** if writes on the database cause it to grow larger than M bytes. - ** -+** Applications must not modify the buffer P or invalidate it before -+** the database connection D is closed. -+** - ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the - ** database is currently in a read transaction or is involved in a backup - ** operation. - ** -+** It is not possible to deserialize into the TEMP database. If the -+** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the -+** function returns SQLITE_ERROR. -+** -+** The deserialized database should not be in [WAL mode]. If the database -+** is in WAL mode, then any attempt to use the database file will result -+** in an [SQLITE_CANTOPEN] error. The application can set the -+** [file format version numbers] (bytes 18 and 19) of the input database P -+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the -+** database file into rollback mode and work around this limitation. -+** - ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the - ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then - ** [sqlite3_free()] is invoked on argument P prior to returning. - ** --** This interface is only available if SQLite is compiled with the --** [SQLITE_ENABLE_DESERIALIZE] option. -+** This interface is omitted if SQLite is compiled with the -+** [SQLITE_OMIT_DESERIALIZE] option. - */ - SQLITE_API int sqlite3_deserialize( - sqlite3 *db, /* The database connection */ - const char *zSchema, /* Which DB to reopen with the deserialization */ - unsigned char *pData, /* The serialized database content */ -- sqlite3_int64 szDb, /* Number bytes in the deserialization */ -+ sqlite3_int64 szDb, /* Number of bytes in the deserialization */ - sqlite3_int64 szBuf, /* Total size of buffer pData[] */ - unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ - ); -@@ -9779,7 +11032,7 @@ SQLITE_API int sqlite3_deserialize( - /* - ** CAPI3REF: Flags for sqlite3_deserialize() - ** --** The following are allowed values for 6th argument (the F argument) to -+** The following are allowed values for the 6th argument (the F argument) to - ** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. - ** - ** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization -@@ -9809,10 +11062,21 @@ SQLITE_API int sqlite3_deserialize( - # undef double - #endif - -+#if defined(__wasi__) -+# undef SQLITE_WASI -+# define SQLITE_WASI 1 -+# ifndef SQLITE_OMIT_LOAD_EXTENSION -+# define SQLITE_OMIT_LOAD_EXTENSION -+# endif -+# ifndef SQLITE_THREADSAFE -+# define SQLITE_THREADSAFE 0 -+# endif -+#endif -+ - #ifdef __cplusplus - } /* End of the 'extern "C"' block */ - #endif --#endif /* SQLITE3_H */ -+/* #endif for SQLITE3_H will be added by mksqlite3.tcl */ - - /******** Begin file sqlite3rtree.h *********/ - /* -@@ -10014,6 +11278,51 @@ SQLITE_API int sqlite3session_create( - */ - SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); - -+/* -+** CAPI3REF: Configure a Session Object -+** METHOD: sqlite3_session -+** -+** This method is used to configure a session object after it has been -+** created. At present the only valid values for the second parameter are -+** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. -+** -+*/ -+SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); -+ -+/* -+** CAPI3REF: Options for sqlite3session_object_config -+** -+** The following values may passed as the the 2nd parameter to -+** sqlite3session_object_config(). -+** -+**
        SQLITE_SESSION_OBJCONFIG_SIZE
        -+** This option is used to set, clear or query the flag that enables -+** the [sqlite3session_changeset_size()] API. Because it imposes some -+** computational overhead, this API is disabled by default. Argument -+** pArg must point to a value of type (int). If the value is initially -+** 0, then the sqlite3session_changeset_size() API is disabled. If it -+** is greater than 0, then the same API is enabled. Or, if the initial -+** value is less than zero, no change is made. In all cases the (int) -+** variable is set to 1 if the sqlite3session_changeset_size() API is -+** enabled following the current call, or 0 otherwise. -+** -+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after -+** the first table has been attached to the session object. -+** -+**
        SQLITE_SESSION_OBJCONFIG_ROWID
        -+** This option is used to set, clear or query the flag that enables -+** collection of data for tables with no explicit PRIMARY KEY. -+** -+** Normally, tables with no explicit PRIMARY KEY are simply ignored -+** by the sessions module. However, if this flag is set, it behaves -+** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted -+** as their leftmost columns. -+** -+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after -+** the first table has been attached to the session object. -+*/ -+#define SQLITE_SESSION_OBJCONFIG_SIZE 1 -+#define SQLITE_SESSION_OBJCONFIG_ROWID 2 - - /* - ** CAPI3REF: Enable Or Disable A Session Object -@@ -10248,9 +11557,10 @@ SQLITE_API void sqlite3session_table_filter( - ** is inserted while a session object is enabled, then later deleted while - ** the same session object is disabled, no INSERT record will appear in the - ** changeset, even though the delete took place while the session was disabled. --** Or, if one field of a row is updated while a session is disabled, and --** another field of the same row is updated while the session is enabled, the --** resulting changeset will contain an UPDATE change that updates both fields. -+** Or, if one field of a row is updated while a session is enabled, and -+** then another field of the same row is updated while the session is disabled, -+** the resulting changeset will contain an UPDATE change that updates both -+** fields. - */ - SQLITE_API int sqlite3session_changeset( - sqlite3_session *pSession, /* Session object */ -@@ -10258,6 +11568,22 @@ SQLITE_API int sqlite3session_changeset( - void **ppChangeset /* OUT: Buffer containing changeset */ - ); - -+/* -+** CAPI3REF: Return An Upper-limit For The Size Of The Changeset -+** METHOD: sqlite3_session -+** -+** By default, this function always returns 0. For it to return -+** a useful result, the sqlite3_session object must have been configured -+** to enable this API using sqlite3session_object_config() with the -+** SQLITE_SESSION_OBJCONFIG_SIZE verb. -+** -+** When enabled, this function returns an upper limit, in bytes, for the size -+** of the changeset that might be produced if sqlite3session_changeset() were -+** called. The final changeset size might be equal to or smaller than the -+** size in bytes returned by this function. -+*/ -+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession); -+ - /* - ** CAPI3REF: Load The Difference Between Tables Into A Session - ** METHOD: sqlite3_session -@@ -10306,8 +11632,9 @@ SQLITE_API int sqlite3session_changeset( - ** database zFrom the contents of the two compatible tables would be - ** identical. - ** --** It an error if database zFrom does not exist or does not contain the --** required compatible table. -+** Unless the call to this function is a no-op as described above, it is an -+** error if database zFrom does not exist or does not contain the required -+** compatible table. - ** - ** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite - ** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg -@@ -10375,6 +11702,14 @@ SQLITE_API int sqlite3session_patchset( - */ - SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession); - -+/* -+** CAPI3REF: Query for the amount of heap memory used by a session object. -+** -+** This API returns the total amount of heap memory in bytes currently -+** used by the session object passed as the only argument. -+*/ -+SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession); -+ - /* - ** CAPI3REF: Create An Iterator To Traverse A Changeset - ** CONSTRUCTOR: sqlite3_changeset_iter -@@ -10434,7 +11769,7 @@ SQLITE_API int sqlite3changeset_start_v2( - ** The following flags may passed via the 4th parameter to - ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: - ** --**
        SQLITE_CHANGESETAPPLY_INVERT
        -+**
        SQLITE_CHANGESETSTART_INVERT
        - ** Invert the changeset while iterating through it. This is equivalent to - ** inverting a changeset using sqlite3changeset_invert() before applying it. - ** It is an error to specify this flag with a patchset. -@@ -10477,18 +11812,23 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); - ** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this - ** is not the case, this function returns [SQLITE_MISUSE]. - ** --** If argument pzTab is not NULL, then *pzTab is set to point to a --** nul-terminated utf-8 encoded string containing the name of the table --** affected by the current change. The buffer remains valid until either --** sqlite3changeset_next() is called on the iterator or until the --** conflict-handler function returns. If pnCol is not NULL, then *pnCol is --** set to the number of columns in the table affected by the change. If --** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change -+** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three -+** outputs are set through these pointers: -+** -+** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], -+** depending on the type of change that the iterator currently points to; -+** -+** *pnCol is set to the number of columns in the table affected by the change; and -+** -+** *pzTab is set to point to a nul-terminated utf-8 encoded string containing -+** the name of the table affected by the current change. The buffer remains -+** valid until either sqlite3changeset_next() is called on the iterator -+** or until the conflict-handler function returns. -+** -+** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change - ** is an indirect change, or false (0) otherwise. See the documentation for - ** [sqlite3session_indirect()] for a description of direct and indirect --** changes. Finally, if pOp is not NULL, then *pOp is set to one of --** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the --** type of change that the iterator currently points to. -+** changes. - ** - ** If no error occurs, SQLITE_OK is returned. If an error does occur, an - ** SQLite error code is returned. The values of the output variables may not -@@ -10744,7 +12084,6 @@ SQLITE_API int sqlite3changeset_concat( - void **ppOut /* OUT: Buffer containing output changeset */ - ); - -- - /* - ** CAPI3REF: Changegroup Handle - ** -@@ -10791,6 +12130,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; - */ - SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); - -+/* -+** CAPI3REF: Add a Schema to a Changegroup -+** METHOD: sqlite3_changegroup_schema -+** -+** This method may be used to optionally enforce the rule that the changesets -+** added to the changegroup handle must match the schema of database zDb -+** ("main", "temp", or the name of an attached database). If -+** sqlite3changegroup_add() is called to add a changeset that is not compatible -+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup -+** object is left in an undefined state. -+** -+** A changeset schema is considered compatible with the database schema in -+** the same way as for sqlite3changeset_apply(). Specifically, for each -+** table in the changeset, there exists a database table with: -+** -+**
          -+**
        • The name identified by the changeset, and -+**
        • at least as many columns as recorded in the changeset, and -+**
        • the primary key columns in the same position as recorded in -+** the changeset. -+**
        -+** -+** The output of the changegroup object always has the same schema as the -+** database nominated using this function. In cases where changesets passed -+** to sqlite3changegroup_add() have fewer columns than the corresponding table -+** in the database schema, these are filled in using the default column -+** values from the database schema. This makes it possible to combined -+** changesets that have different numbers of columns for a single table -+** within a changegroup, provided that they are otherwise compatible. -+*/ -+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); -+ - /* - ** CAPI3REF: Add A Changeset To A Changegroup - ** METHOD: sqlite3_changegroup -@@ -10859,16 +12230,45 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); - ** If the new changeset contains changes to a table that is already present - ** in the changegroup, then the number of columns and the position of the - ** primary key columns for the table must be consistent. If this is not the --** case, this function fails with SQLITE_SCHEMA. If the input changeset --** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is --** returned. Or, if an out-of-memory condition occurs during processing, this --** function returns SQLITE_NOMEM. In all cases, if an error occurs the state --** of the final contents of the changegroup is undefined. -+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup -+** object has been configured with a database schema using the -+** sqlite3changegroup_schema() API, then it is possible to combine changesets -+** with different numbers of columns for a single table, provided that -+** they are otherwise compatible. - ** --** If no error occurs, SQLITE_OK is returned. -+** If the input changeset appears to be corrupt and the corruption is -+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition -+** occurs during processing, this function returns SQLITE_NOMEM. -+** -+** In all cases, if an error occurs the state of the final contents of the -+** changegroup is undefined. If no error occurs, SQLITE_OK is returned. - */ - SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); - -+/* -+** CAPI3REF: Add A Single Change To A Changegroup -+** METHOD: sqlite3_changegroup -+** -+** This function adds the single change currently indicated by the iterator -+** passed as the second argument to the changegroup object. The rules for -+** adding the change are just as described for [sqlite3changegroup_add()]. -+** -+** If the change is successfully added to the changegroup, SQLITE_OK is -+** returned. Otherwise, an SQLite error code is returned. -+** -+** The iterator must point to a valid entry when this function is called. -+** If it does not, SQLITE_ERROR is returned and no change is added to the -+** changegroup. Additionally, the iterator must not have been opened with -+** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also -+** returned. -+*/ -+SQLITE_API int sqlite3changegroup_add_change( -+ sqlite3_changegroup*, -+ sqlite3_changeset_iter* -+); -+ -+ -+ - /* - ** CAPI3REF: Obtain A Composite Changeset From A Changegroup - ** METHOD: sqlite3_changegroup -@@ -11117,9 +12517,30 @@ SQLITE_API int sqlite3changeset_apply_v2( - ** Invert the changeset before applying it. This is equivalent to inverting - ** a changeset using sqlite3changeset_invert() before applying it. It is - ** an error to specify this flag with a patchset. -+** -+**
        SQLITE_CHANGESETAPPLY_IGNORENOOP
        -+** Do not invoke the conflict handler callback for any changes that -+** would not actually modify the database even if they were applied. -+** Specifically, this means that the conflict handler is not invoked -+** for: -+**
          -+**
        • a delete change if the row being deleted cannot be found, -+**
        • an update change if the modified fields are already set to -+** their new values in the conflicting row, or -+**
        • an insert change if all fields of the conflicting row match -+** the row being inserted. -+**
        -+** -+**
        SQLITE_CHANGESETAPPLY_FKNOACTION
        -+** If this flag it set, then all foreign key constraints in the target -+** database behave as if they were declared with "ON UPDATE NO ACTION ON -+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL -+** or SET DEFAULT. - */ - #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 - #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 -+#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 -+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 - - /* - ** CAPI3REF: Constants Passed To The Conflict Handler -@@ -11652,8 +13073,8 @@ struct Fts5PhraseIter { - ** EXTENSION API FUNCTIONS - ** - ** xUserData(pFts): --** Return a copy of the context pointer the extension function was --** registered with. -+** Return a copy of the pUserData pointer passed to the xCreateFunction() -+** API when the extension function was registered. - ** - ** xColumnTotalSize(pFts, iCol, pnToken): - ** If parameter iCol is less than zero, set output variable *pnToken -@@ -11685,8 +13106,11 @@ struct Fts5PhraseIter { - ** created with the "columnsize=0" option. - ** - ** xColumnText: --** This function attempts to retrieve the text of column iCol of the --** current document. If successful, (*pz) is set to point to a buffer -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of columns in the table, SQLITE_RANGE is returned. -+** -+** Otherwise, this function attempts to retrieve the text of column iCol of -+** the current document. If successful, (*pz) is set to point to a buffer - ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes - ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, - ** if an error occurs, an SQLite error code is returned and the final values -@@ -11696,8 +13120,10 @@ struct Fts5PhraseIter { - ** Returns the number of phrases in the current query expression. - ** - ** xPhraseSize: --** Returns the number of tokens in phrase iPhrase of the query. Phrases --** are numbered starting from zero. -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of phrases in the current query, as returned by xPhraseCount, -+** 0 is returned. Otherwise, this function returns the number of tokens in -+** phrase iPhrase of the query. Phrases are numbered starting from zero. - ** - ** xInstCount: - ** Set *pnInst to the total number of occurrences of all phrases within -@@ -11713,12 +13139,13 @@ struct Fts5PhraseIter { - ** Query for the details of phrase match iIdx within the current row. - ** Phrase matches are numbered starting from zero, so the iIdx argument - ** should be greater than or equal to zero and smaller than the value --** output by xInstCount(). -+** output by xInstCount(). If iIdx is less than zero or greater than -+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. - ** --** Usually, output parameter *piPhrase is set to the phrase number, *piCol -+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol - ** to the column in which it occurs and *piOff the token offset of the --** first token of the phrase. Returns SQLITE_OK if successful, or an error --** code (i.e. SQLITE_NOMEM) if an error occurs. -+** first token of the phrase. SQLITE_OK is returned if successful, or an -+** error code (i.e. SQLITE_NOMEM) if an error occurs. - ** - ** This API can be quite slow if used with an FTS5 table created with the - ** "detail=none" or "detail=column" option. -@@ -11744,6 +13171,10 @@ struct Fts5PhraseIter { - ** Invoking Api.xUserData() returns a copy of the pointer passed as - ** the third argument to pUserData. - ** -+** If parameter iPhrase is less than zero, or greater than or equal to -+** the number of phrases in the query, as returned by xPhraseCount(), -+** this function returns SQLITE_RANGE. -+** - ** If the callback function returns any value other than SQLITE_OK, the - ** query is abandoned and the xQueryPhrase function returns immediately. - ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. -@@ -11825,6 +13256,10 @@ struct Fts5PhraseIter { - ** (i.e. if it is a contentless table), then this API always iterates - ** through an empty set (all calls to xPhraseFirst() set iCol to -1). - ** -+** In all cases, matches are visited in (column ASC, offset ASC) order. -+** i.e. all those in column 0, sorted by offset, followed by those in -+** column 1, etc. -+** - ** xPhraseNext() - ** See xPhraseFirst above. - ** -@@ -11858,9 +13293,80 @@ struct Fts5PhraseIter { - ** - ** xPhraseNextColumn() - ** See xPhraseFirstColumn above. -+** -+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) -+** This is used to access token iToken of phrase iPhrase of the current -+** query. Before returning, output parameter *ppToken is set to point -+** to a buffer containing the requested token, and *pnToken to the -+** size of this buffer in bytes. -+** -+** If iPhrase or iToken are less than zero, or if iPhrase is greater than -+** or equal to the number of phrases in the query as reported by -+** xPhraseCount(), or if iToken is equal to or greater than the number of -+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken -+ are both zeroed. -+** -+** The output text is not a copy of the query text that specified the -+** token. It is the output of the tokenizer module. For tokendata=1 -+** tables, this includes any embedded 0x00 and trailing data. -+** -+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) -+** This is used to access token iToken of phrase hit iIdx within the -+** current row. If iIdx is less than zero or greater than or equal to the -+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, -+** output variable (*ppToken) is set to point to a buffer containing the -+** matching document token, and (*pnToken) to the size of that buffer in -+** bytes. -+** -+** The output text is not a copy of the document text that was tokenized. -+** It is the output of the tokenizer module. For tokendata=1 tables, this -+** includes any embedded 0x00 and trailing data. -+** -+** This API may be slow in some cases if the token identified by parameters -+** iIdx and iToken matched a prefix token in the query. In most cases, the -+** first call to this API for each prefix token in the query is forced -+** to scan the portion of the full-text index that matches the prefix -+** token to collect the extra data required by this API. If the prefix -+** token matches a large number of token instances in the document set, -+** this may be a performance problem. -+** -+** If the user knows in advance that a query may use this API for a -+** prefix token, FTS5 may be configured to collect all required data as part -+** of the initial querying of the full-text index, avoiding the second scan -+** entirely. This also causes prefix queries that do not use this API to -+** run more slowly and use more memory. FTS5 may be configured in this way -+** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] -+** option, or on a per-query basis using the -+** [fts5_insttoken | fts5_insttoken()] user function. -+** -+** This API can be quite slow if used with an FTS5 table created with the -+** "detail=none" or "detail=column" option. -+** -+** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) -+** If parameter iCol is less than zero, or greater than or equal to the -+** number of columns in the table, SQLITE_RANGE is returned. -+** -+** Otherwise, this function attempts to retrieve the locale associated -+** with column iCol of the current row. Usually, there is no associated -+** locale, and output parameters (*pzLocale) and (*pnLocale) are set -+** to NULL and 0, respectively. However, if the fts5_locale() function -+** was used to associate a locale with the value when it was inserted -+** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated -+** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) -+** is set to the size in bytes of the buffer, not including the -+** nul-terminator. -+** -+** If successful, SQLITE_OK is returned. Or, if an error occurs, an -+** SQLite error code is returned. The final value of the output parameters -+** is undefined in this case. -+** -+** xTokenize_v2: -+** Tokenize text using the tokenizer belonging to the FTS5 table. This -+** API is the same as the xTokenize() API, except that it allows a tokenizer -+** locale to be specified. - */ - struct Fts5ExtensionApi { -- int iVersion; /* Currently always set to 3 */ -+ int iVersion; /* Currently always set to 4 */ - - void *(*xUserData)(Fts5Context*); - -@@ -11895,6 +13401,22 @@ struct Fts5ExtensionApi { - - int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); - void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); -+ -+ /* Below this point are iVersion>=3 only */ -+ int (*xQueryToken)(Fts5Context*, -+ int iPhrase, int iToken, -+ const char **ppToken, int *pnToken -+ ); -+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); -+ -+ /* Below this point are iVersion>=4 only */ -+ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); -+ int (*xTokenize_v2)(Fts5Context*, -+ const char *pText, int nText, /* Text to tokenize */ -+ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ -+ void *pCtx, /* Context passed to xToken() */ -+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ -+ ); - }; - - /* -@@ -11915,7 +13437,7 @@ struct Fts5ExtensionApi { - ** A tokenizer instance is required to actually tokenize text. - ** - ** The first argument passed to this function is a copy of the (void*) --** pointer provided by the application when the fts5_tokenizer object -+** pointer provided by the application when the fts5_tokenizer_v2 object - ** was registered with FTS5 (the third argument to xCreateTokenizer()). - ** The second and third arguments are an array of nul-terminated strings - ** containing the tokenizer arguments, if any, specified following the -@@ -11939,7 +13461,7 @@ struct Fts5ExtensionApi { - ** argument passed to this function is a pointer to an Fts5Tokenizer object - ** returned by an earlier call to xCreate(). - ** --** The second argument indicates the reason that FTS5 is requesting -+** The third argument indicates the reason that FTS5 is requesting - ** tokenization of the supplied text. This is always one of the following - ** four values: - ** -@@ -11963,6 +13485,13 @@ struct Fts5ExtensionApi { - ** on a columnsize=0 database. - ** - ** -+** The sixth and seventh arguments passed to xTokenize() - pLocale and -+** nLocale - are a pointer to a buffer containing the locale to use for -+** tokenization (e.g. "en_US") and its size in bytes, respectively. The -+** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in -+** which case nLocale is always 0) to indicate that the tokenizer should -+** use its default locale. -+** - ** For each token in the input string, the supplied callback xToken() must - ** be invoked. The first argument to it should be a copy of the pointer - ** passed as the second argument to xTokenize(). The third and fourth -@@ -11986,6 +13515,30 @@ struct Fts5ExtensionApi { - ** may abandon the tokenization and return any error code other than - ** SQLITE_OK or SQLITE_DONE. - ** -+** If the tokenizer is registered using an fts5_tokenizer_v2 object, -+** then the xTokenize() method has two additional arguments - pLocale -+** and nLocale. These specify the locale that the tokenizer should use -+** for the current request. If pLocale and nLocale are both 0, then the -+** tokenizer should use its default locale. Otherwise, pLocale points to -+** an nLocale byte buffer containing the name of the locale to use as utf-8 -+** text. pLocale is not nul-terminated. -+** -+** FTS5_TOKENIZER -+** -+** There is also an fts5_tokenizer object. This is an older, deprecated, -+** version of fts5_tokenizer_v2. It is similar except that: -+** -+**
          -+**
        • There is no "iVersion" field, and -+**
        • The xTokenize() method does not take a locale argument. -+**
        -+** -+** Legacy fts5_tokenizer tokenizers must be registered using the -+** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). -+** -+** Tokenizer implementations registered using either API may be retrieved -+** using both xFindTokenizer() and xFindTokenizer_v2(). -+** - ** SYNONYM SUPPORT - ** - ** Custom tokenizers may also support synonyms. Consider a case in which a -@@ -12089,11 +13642,38 @@ struct Fts5ExtensionApi { - ** as separate queries of the FTS index are required for each synonym. - ** - ** When using methods (2) or (3), it is important that the tokenizer only --** provide synonyms when tokenizing document text (method (2)) or query --** text (method (3)), not both. Doing so will not cause any errors, but is -+** provide synonyms when tokenizing document text (method (3)) or query -+** text (method (2)), not both. Doing so will not cause any errors, but is - ** inefficient. - */ - typedef struct Fts5Tokenizer Fts5Tokenizer; -+typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; -+struct fts5_tokenizer_v2 { -+ int iVersion; /* Currently always 2 */ -+ -+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); -+ void (*xDelete)(Fts5Tokenizer*); -+ int (*xTokenize)(Fts5Tokenizer*, -+ void *pCtx, -+ int flags, /* Mask of FTS5_TOKENIZE_* flags */ -+ const char *pText, int nText, -+ const char *pLocale, int nLocale, -+ int (*xToken)( -+ void *pCtx, /* Copy of 2nd argument to xTokenize() */ -+ int tflags, /* Mask of FTS5_TOKEN_* flags */ -+ const char *pToken, /* Pointer to buffer containing token */ -+ int nToken, /* Size of token in bytes */ -+ int iStart, /* Byte offset of token within input text */ -+ int iEnd /* Byte offset of end of token within input text */ -+ ) -+ ); -+}; -+ -+/* -+** New code should use the fts5_tokenizer_v2 type to define tokenizer -+** implementations. The following type is included for legacy applications -+** that still use it. -+*/ - typedef struct fts5_tokenizer fts5_tokenizer; - struct fts5_tokenizer { - int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); -@@ -12113,6 +13693,7 @@ struct fts5_tokenizer { - ); - }; - -+ - /* Flags that may be passed as the third argument to xTokenize() */ - #define FTS5_TOKENIZE_QUERY 0x0001 - #define FTS5_TOKENIZE_PREFIX 0x0002 -@@ -12132,13 +13713,13 @@ struct fts5_tokenizer { - */ - typedef struct fts5_api fts5_api; - struct fts5_api { -- int iVersion; /* Currently always set to 2 */ -+ int iVersion; /* Currently always set to 3 */ - - /* Create a new tokenizer */ - int (*xCreateTokenizer)( - fts5_api *pApi, - const char *zName, -- void *pContext, -+ void *pUserData, - fts5_tokenizer *pTokenizer, - void (*xDestroy)(void*) - ); -@@ -12147,7 +13728,7 @@ struct fts5_api { - int (*xFindTokenizer)( - fts5_api *pApi, - const char *zName, -- void **ppContext, -+ void **ppUserData, - fts5_tokenizer *pTokenizer - ); - -@@ -12155,10 +13736,29 @@ struct fts5_api { - int (*xCreateFunction)( - fts5_api *pApi, - const char *zName, -- void *pContext, -+ void *pUserData, - fts5_extension_function xFunction, - void (*xDestroy)(void*) - ); -+ -+ /* APIs below this point are only available if iVersion>=3 */ -+ -+ /* Create a new tokenizer */ -+ int (*xCreateTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void *pUserData, -+ fts5_tokenizer_v2 *pTokenizer, -+ void (*xDestroy)(void*) -+ ); -+ -+ /* Find an existing tokenizer */ -+ int (*xFindTokenizer_v2)( -+ fts5_api *pApi, -+ const char *zName, -+ void **ppUserData, -+ fts5_tokenizer_v2 **ppTokenizer -+ ); - }; - - /* -@@ -12172,3 +13772,4 @@ struct fts5_api { - #endif /* _FTS5_H */ - - /******** End of fts5.h *********/ -+#endif /* SQLITE3_H */ -diff --git a/src/timer.inl b/src/timer.inl -index 9b8d5539..39d68dfc 100644 ---- a/src/timer.inl -+++ b/src/timer.inl -@@ -39,13 +39,16 @@ TIMER_API double - timer_getcurrenttime(struct mg_context *ctx) - { - #if defined(_WIN32) -+ uint64_t now_tick64 = 0; -+#if defined(_WIN64) -+ now_tick64 = GetTickCount64(); -+#else - /* GetTickCount returns milliseconds since system start as - * unsigned 32 bit value. It will wrap around every 49.7 days. - * We need to use a 64 bit counter (will wrap in 500 mio. years), - * by adding the 32 bit difference since the last call to a - * 64 bit counter. This algorithm will only work, if this - * function is called at least once every 7 weeks. */ -- uint64_t now_tick64 = 0; - DWORD now_tick = GetTickCount(); - - if (ctx->timers) { -@@ -55,6 +58,7 @@ timer_getcurrenttime(struct mg_context *ctx) - ctx->timers->last_tick = now_tick; - pthread_mutex_unlock(&ctx->timers->mutex); - } -+#endif - return (double)now_tick64 * 1.0E-3; - #else - struct timespec now_ts; -diff --git a/test/donate.html b/test/donate.html -deleted file mode 100644 -index da6e3978..00000000 ---- a/test/donate.html -+++ /dev/null -@@ -1,32 +0,0 @@ -- -- -- -- -- --CivetWeb web server maintenance (#523): -- --

        --

        -- -- -- -- --
        --

        -- --

        -- --donate -- --

        -- --

        -- --donate -- --

        -- -- -- -- -- -diff --git a/test/page3.ssjs b/test/page3.ssjs -index 71e55e34..6e4f62f9 100644 ---- a/test/page3.ssjs -+++ b/test/page3.ssjs -@@ -19,8 +19,10 @@ opts = [ - "extra_mime_types", - "listening_ports", - "document_root", -+"fallback_document_root", - "ssl_certificate", - "num_threads", -+"prespawn_threads", - "run_as_user", - "url_rewrite_patterns", - "hide_files_patterns", -@@ -32,6 +34,7 @@ opts = [ - "lua_server_page_pattern", - "_experimental_duktape_script_pattern", - "websocket_root", -+"fallback_websocket_root", - "lua_websocket_pattern", - "access_control_allow_origin", - "error_pages", -diff --git a/test/page4.lp b/test/page4.lp -index 52374907..7a4934e3 100644 ---- a/test/page4.lp -+++ b/test/page4.lp -@@ -22,10 +22,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr -

        Tags

        - - --<? greeting = 'CiwetWeb' ?>
        -+<? greeting = 'CivetWeb' ?>
        - <strong><?= greeting %></strong>
        -

        -- -+ - ==>
        - -
        -diff --git a/test/page4kepler.lp b/test/page4kepler.lp -index 8a986b5d..a7054897 100644 ---- a/test/page4kepler.lp -+++ b/test/page4kepler.lp -@@ -19,10 +19,10 @@ the "Kepler syntax" uses <?lua chunk ?>, <?lua= expr -

        Tags

        - - --<? greeting = 'CiwetWeb' ?>
        -+<? greeting = 'CivetWeb' ?>
        - <strong><?= greeting %></strong>
        -

        -- -+ - ==>
        - -
        -diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt -index 7aa34971..6aa8f0fd 100644 ---- a/unittest/CMakeLists.txt -+++ b/unittest/CMakeLists.txt -@@ -1,3 +1,6 @@ -+cmake_minimum_required(VERSION 3.10) -+cmake_policy(VERSION 3.10) -+ - # Determine if we should print to the output - if (CIVETWEB_ENABLE_THIRD_PARTY_OUTPUT) - set(THIRD_PARTY_LOGGING 0) -@@ -30,6 +33,7 @@ DOWNLOAD_NAME "master.zip" - "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" - "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" - "-DCMAKE_INSTALL_PREFIX=" -+ "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" - LOG_DOWNLOAD ${THIRD_PARTY_LOGGING} - LOG_UPDATE ${THIRD_PARTY_LOGGING} - LOG_CONFIGURE ${THIRD_PARTY_LOGGING} -diff --git a/unittest/private.c b/unittest/private.c -index 8e85d71e..7540dba6 100644 ---- a/unittest/private.c -+++ b/unittest/private.c -@@ -1621,10 +1621,13 @@ START_TEST(test_config_options) - ck_assert_str_eq("extra_mime_types", config_options[EXTRA_MIME_TYPES].name); - ck_assert_str_eq("listening_ports", config_options[LISTENING_PORTS].name); - ck_assert_str_eq("document_root", config_options[DOCUMENT_ROOT].name); -+ ck_assert_str_eq("fallback_document_root", -+ config_options[FALLBACK_DOCUMENT_ROOT].name); - ck_assert_str_eq("ssl_certificate", config_options[SSL_CERTIFICATE].name); - ck_assert_str_eq("ssl_certificate_chain", - config_options[SSL_CERTIFICATE_CHAIN].name); - ck_assert_str_eq("num_threads", config_options[NUM_THREADS].name); -+ ck_assert_str_eq("prespawn_threads", config_options[PRESPAWN_THREADS].name); - ck_assert_str_eq("run_as_user", config_options[RUN_AS_USER].name); - ck_assert_str_eq("url_rewrite_patterns", - config_options[URL_REWRITE_PATTERN].name); -@@ -1672,6 +1675,8 @@ START_TEST(test_config_options) - #endif - #if defined(USE_WEBSOCKET) - ck_assert_str_eq("websocket_root", config_options[WEBSOCKET_ROOT].name); -+ ck_assert_str_eq("fallback_websocket_root", -+ config_options[FALLBACK_WEBSOCKET_ROOT].name); - #endif - #if defined(USE_LUA) && defined(USE_WEBSOCKET) - ck_assert_str_eq("lua_websocket_pattern", -diff --git a/unittest/public_server.c b/unittest/public_server.c -index 02bf5255..27a3eb9f 100644 ---- a/unittest/public_server.c -+++ b/unittest/public_server.c -@@ -457,10 +457,14 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) - ck_assert_int_eq(portinfo[0].port, 0); - ck_assert_int_eq(portinfo[0].is_ssl, 0); - ck_assert_int_eq(portinfo[0].is_redirect, 0); -+ ck_assert_int_eq(portinfo[0].is_optional, 0); -+ ck_assert_int_eq(portinfo[0].is_bound, 0); - ck_assert_int_eq(portinfo[1].protocol, 0); - ck_assert_int_eq(portinfo[1].port, 0); - ck_assert_int_eq(portinfo[1].is_ssl, 0); - ck_assert_int_eq(portinfo[1].is_redirect, 0); -+ ck_assert_int_eq(portinfo[1].is_optional, 0); -+ ck_assert_int_eq(portinfo[1].is_bound, 0); - - ret = mg_get_server_ports(ctx, 4, portinfo); - ck_assert_int_eq(ret, 1); -@@ -472,10 +476,14 @@ test_mg_start_stop_http_server_impl(int ipv6, int bound) - ck_assert_int_eq(portinfo[0].port, 8080); - ck_assert_int_eq(portinfo[0].is_ssl, 0); - ck_assert_int_eq(portinfo[0].is_redirect, 0); -+ ck_assert_int_eq(portinfo[0].is_optional, 0); -+ ck_assert_int_eq(portinfo[0].is_bound, 1); - ck_assert_int_eq(portinfo[1].protocol, 0); - ck_assert_int_eq(portinfo[1].port, 0); - ck_assert_int_eq(portinfo[1].is_ssl, 0); - ck_assert_int_eq(portinfo[1].is_redirect, 0); -+ ck_assert_int_eq(portinfo[1].is_optional, 0); -+ ck_assert_int_eq(portinfo[1].is_bound, 0); - - test_sleep(1); - -@@ -649,7 +657,7 @@ START_TEST(test_mg_start_stop_https_server) - OPTIONS[opt_idx++] = "."; - #endif - OPTIONS[opt_idx++] = "listening_ports"; -- OPTIONS[opt_idx++] = "8080r,8443s"; -+ OPTIONS[opt_idx++] = "8080r,8443os"; - OPTIONS[opt_idx++] = "ssl_certificate"; - OPTIONS[opt_idx++] = ssl_cert; - -@@ -674,10 +682,14 @@ START_TEST(test_mg_start_stop_https_server) - ck_assert_int_eq(portinfo[0].port, 0); - ck_assert_int_eq(portinfo[0].is_ssl, 0); - ck_assert_int_eq(portinfo[0].is_redirect, 0); -+ ck_assert_int_eq(portinfo[0].is_optional, 0); -+ ck_assert_int_eq(portinfo[0].is_bound, 0); - ck_assert_int_eq(portinfo[1].protocol, 0); - ck_assert_int_eq(portinfo[1].port, 0); - ck_assert_int_eq(portinfo[1].is_ssl, 0); - ck_assert_int_eq(portinfo[1].is_redirect, 0); -+ ck_assert_int_eq(portinfo[1].is_optional, 0); -+ ck_assert_int_eq(portinfo[1].is_bound, 0); - - ret = mg_get_server_ports(ctx, 4, portinfo); - ck_assert_int_eq(ret, 2); -@@ -685,14 +697,20 @@ START_TEST(test_mg_start_stop_https_server) - ck_assert_int_eq(portinfo[0].port, 8080); - ck_assert_int_eq(portinfo[0].is_ssl, 0); - ck_assert_int_eq(portinfo[0].is_redirect, 1); -+ ck_assert_int_eq(portinfo[0].is_optional, 0); -+ ck_assert_int_eq(portinfo[0].is_bound, 1); - ck_assert_int_eq(portinfo[1].protocol, 1); - ck_assert_int_eq(portinfo[1].port, 8443); - ck_assert_int_eq(portinfo[1].is_ssl, 1); - ck_assert_int_eq(portinfo[1].is_redirect, 0); -+ ck_assert_int_eq(portinfo[1].is_optional, 1); -+ ck_assert_int_eq(portinfo[1].is_bound, 1); - ck_assert_int_eq(portinfo[2].protocol, 0); - ck_assert_int_eq(portinfo[2].port, 0); - ck_assert_int_eq(portinfo[2].is_ssl, 0); - ck_assert_int_eq(portinfo[2].is_redirect, 0); -+ ck_assert_int_eq(portinfo[2].is_optional, 0); -+ ck_assert_int_eq(portinfo[2].is_bound, 0); - - test_sleep(1); - -@@ -771,7 +789,7 @@ START_TEST(test_mg_server_and_client_tls) - OPTIONS[opt_idx++] = "."; - #endif - OPTIONS[opt_idx++] = "listening_ports"; -- OPTIONS[opt_idx++] = "8080r,8443s"; -+ OPTIONS[opt_idx++] = "8080r,8443os"; - OPTIONS[opt_idx++] = "ssl_certificate"; - OPTIONS[opt_idx++] = server_cert; - OPTIONS[opt_idx++] = "ssl_verify_peer"; -@@ -800,14 +818,20 @@ START_TEST(test_mg_server_and_client_tls) - ck_assert_int_eq(ports[0].port, 8080); - ck_assert_int_eq(ports[0].is_ssl, 0); - ck_assert_int_eq(ports[0].is_redirect, 1); -+ ck_assert_int_eq(ports[0].is_optional, 0); -+ ck_assert_int_eq(ports[0].is_bound, 1); - ck_assert_int_eq(ports[1].protocol, 1); - ck_assert_int_eq(ports[1].port, 8443); - ck_assert_int_eq(ports[1].is_ssl, 1); - ck_assert_int_eq(ports[1].is_redirect, 0); -+ ck_assert_int_eq(ports[1].is_optional, 1); -+ ck_assert_int_eq(ports[1].is_bound, 1); - ck_assert_int_eq(ports[2].protocol, 0); - ck_assert_int_eq(ports[2].port, 0); - ck_assert_int_eq(ports[2].is_ssl, 0); - ck_assert_int_eq(ports[2].is_redirect, 0); -+ ck_assert_int_eq(ports[2].is_optional, 0); -+ ck_assert_int_eq(ports[2].is_bound, 0); - - test_sleep(1); - -@@ -823,7 +847,7 @@ START_TEST(test_mg_server_and_client_tls) - * while Ubuntu Xenial, Ubuntu Trusty and Windows test containers at - * Travis CI do not. Maybe it is OpenSSL version specific. - */ --#if defined(OPENSSL_API_1_1) -+#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0) - if (client_conn) { - /* Connect succeeds, but the connection is unusable. */ - mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n"); -@@ -2608,6 +2632,36 @@ FormGet(struct mg_connection *conn, void *cbdata) - } - - -+static int -+FormError(struct mg_connection *conn, void *cbdata) -+{ -+ const struct mg_request_info *req_info = mg_get_request_info(conn); -+ int ret; -+ struct mg_form_data_handler fdh = {NULL, NULL, NULL, NULL}; -+ -+ (void)cbdata; -+ -+ ck_assert(req_info != NULL); -+ -+ mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n"); -+ fdh.user_data = (void *)&g_field_found_return; -+ -+ /* Call the form handler */ -+ g_field_step = 0; -+ g_field_found_return = MG_FORM_FIELD_STORAGE_GET; -+ ret = mg_handle_form_request(conn, &fdh); -+ g_field_found_return = -888; -+ ck_assert_int_eq(ret, -1); -+ ck_assert_int_eq(g_field_step, 0); -+ mg_printf(conn, "%i\r\n", ret); -+ g_field_step = 1000; -+ -+ mark_point(); -+ -+ return 1; -+} -+ -+ - static int - FormStore(struct mg_connection *conn, - void *cbdata, -@@ -2727,6 +2781,7 @@ START_TEST(test_handle_form) - ck_assert_str_eq(opt, "8884"); - - mg_set_request_handler(ctx, "/handle_form", FormGet, NULL); -+ mg_set_request_handler(ctx, "/handle_form_error", FormError, NULL); - mg_set_request_handler(ctx, "/handle_form_store", FormStore1, NULL); - mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, NULL); - -@@ -2797,6 +2852,23 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -+ /* -+ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ * -+ * preamble := discard-text -+ * epilogue := discard-text -+ * -+ * discard-text := *(*text CRLF) *text -+ * -+ * text := -+ */ -+ - /* Handle form: "POST multipart/form-data" */ - multipart_body = - "--multipart-form-data-boundary--see-RFC-2388\r\n" -@@ -2924,6 +2996,7 @@ START_TEST(test_handle_form) - - - /* Handle form: "POST multipart/form-data" with chunked transfer encoding */ -+ /* use the most universal possible (no edge cases) body*/ - client_conn = - mg_download("localhost", - 8884, -@@ -3016,6 +3089,786 @@ START_TEST(test_handle_form) - ck_assert_int_eq(client_ri->status_code, 200); - mg_close_connection(client_conn); - -+ /* Handle form: "POST multipart/form-data" without trailing CRLF*/ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2366); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" with epilogue*/ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n" -+ "epilogue\r\n" -+ "epilogue\r\n" -+ "\r\n" -+ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" -+ "\r\n" -+ "\r\n" -+ "\r\n" -+ "epilogue\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2453); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" with preamble*/ -+ /* -+ * https://datatracker.ietf.org/doc/html/rfc2046#section-5.1 -+ * -+ * multipart-body := [preamble CRLF] -+ * dash-boundary transport-padding CRLF -+ * body-part *encapsulation -+ * close-delimiter transport-padding -+ * [CRLF epilogue] -+ * -+ * preamble := discard-text -+ * -+ * discard-text := *(*text CRLF) *text -+ * -+ * text := -+ */ -+ multipart_body = -+ "\r\n" -+ "\r\npreamble" -+ "\r\npreamble" -+ "\r\npreamble" -+ "\r\n" -+ "\r\npreamble" -+ "\r\n" -+ "1234567890-=!@£$%^&*()_+[]{};'\\:\"|,./<>?`~§\r\n" -+ "\r\n" -+ "\r\n\t\t\t \t\t\t" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2478); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" with transport padding*/ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388 \r\n" -+ "Content-Disposition: form-data; name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2382); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" with custom name fields in the -+ * Content-Disposition */ -+ multipart_body = -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; " -+ "custom1name=\"1\"; " -+ "custom2name=\"2\"; " -+ "custom3name=\"3\"; " -+ "custom4name=\"4\"; " -+ "name=\"textin\"\r\n" -+ "\r\n" -+ "text\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\t\t\t\r\n" -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"radio1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=radio2\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"check1\"\r\n" -+ "\r\n" -+ "val1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"numberin\"\r\n" -+ "\r\n" -+ "1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datein\"\r\n" -+ "\r\n" -+ "1.1.2016\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"colorin\"\r\n" -+ "\r\n" -+ "#80ff00\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"rangein\"\r\n" -+ "\r\n" -+ "3\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"monthin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"weekin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"timein\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimen\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"emailin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"searchin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"telin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"urlin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=filesin; filename=\r\n" -+ "Content-Type: application/octet-stream\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"selectin\"\r\n" -+ "\r\n" -+ "opt1\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Disposition: form-data; name=\"message\"\r\n" -+ "\r\n" -+ "Text area default text.\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 2439); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form error cases */ -+ /* Handle form: "POST multipart/form-data" empty body */ -+ multipart_body = ""; -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 0); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form_error HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); -+ -+ /* Handle form: "POST multipart/form-data" very long preamble */ -+ multipart_body = "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "preamblepreamblepreamblepreamblepreamble\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388\r\n"; -+ "Content-Disposition: form-data; name=\"passwordin\"\r\n" -+ "\r\n" -+ "\r\n" -+ "--multipart-form-data-boundary--see-RFC-2388--\r\n"; -+ -+ body_len = strlen(multipart_body); -+ ck_assert_uint_eq(body_len, 1768); /* not required */ -+ -+ client_conn = -+ mg_download("localhost", -+ 8884, -+ 0, -+ ebuf, -+ sizeof(ebuf), -+ "POST /handle_form_error HTTP/1.1\r\n" -+ "Host: localhost:8884\r\n" -+ "Connection: close\r\n" -+ "Content-Type: multipart/form-data; " -+ "boundary=multipart-form-data-boundary--see-RFC-2388\r\n" -+ "Content-Length: %u\r\n" -+ "\r\n%s", -+ (unsigned int)body_len, -+ multipart_body); -+ -+ ck_assert(client_conn != NULL); -+ for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) { -+ test_sleep(1); -+ if (g_field_step == 1000) { -+ break; -+ } -+ } -+ client_ri = mg_get_response_info(client_conn); -+ -+ ck_assert(client_ri != NULL); -+ ck_assert_int_eq(client_ri->status_code, 200); -+ mg_close_connection(client_conn); - - /* Now test form_store */ - -diff --git a/unittest/timertest.c b/unittest/timertest.c -index 270dbec7..8c11b07e 100644 ---- a/unittest/timertest.c -+++ b/unittest/timertest.c -@@ -51,22 +51,22 @@ static int action_dec_ret; - static int - action_dec(void *arg) - { -- int *p = (int *)arg; -- (*p)--; -+ ptrdiff_t *p = (ptrdiff_t *)arg; -+ ptrdiff_t v = mg_atomic_dec(p); - -- if (*p < -1) { -+ if (v < -1) { - ck_abort_msg("Periodic timer called too often"); - /* return 0 here would be unreachable code */ - } - -- return (*p >= -3) ? action_dec_ret : 0; -+ return (v >= -3) ? action_dec_ret : 0; - } - - - static void - action_cancel(void *arg) - { -- int *p = (int *)arg; -+ ptrdiff_t *p = (ptrdiff_t *)arg; - - /* test convention: store cancel counter after timer counter */ - p += TIMERS_IN_TEST; -@@ -76,29 +76,29 @@ action_cancel(void *arg) - /* return 0 here would be unreachable code */ - } - -- (*p)++; -+ mg_atomic_inc(p); - } - - - static int - action_dec_to_0(void *arg) - { -- int *p = (int *)arg; -- (*p)--; -+ ptrdiff_t *p = (ptrdiff_t *)arg; -+ ptrdiff_t v = mg_atomic_dec(p); - -- if (*p <= -1) { -+ if (v <= -1) { - ck_abort_msg("Periodic timer called too often"); - /* return 0 here would be unreachable code */ - } - -- return (*p > 0); -+ return (v > 0); - } - - - START_TEST(test_timer_cyclic) - { -+ static ptrdiff_t c[TIMERS_IN_TEST * 2]; - struct mg_context ctx; -- int c[TIMERS_IN_TEST * 2]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); - -@@ -158,7 +158,7 @@ END_TEST - START_TEST(test_timer_oneshot_by_callback_retval) - { - struct mg_context ctx; -- int c[TIMERS_IN_TEST * 2]; -+ ptrdiff_t c[TIMERS_IN_TEST * 2]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); - -@@ -212,7 +212,7 @@ END_TEST - START_TEST(test_timer_oneshot_by_timer_add) - { - struct mg_context ctx; -- int c[TIMERS_IN_TEST * 2]; -+ ptrdiff_t c[TIMERS_IN_TEST * 2]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); - -@@ -266,7 +266,7 @@ END_TEST - START_TEST(test_timer_mixed) - { - struct mg_context ctx; -- int c[TIMERS_IN_TEST]; -+ ptrdiff_t c[TIMERS_IN_TEST]; - memset(&ctx, 0, sizeof(ctx)); - memset(c, 0, sizeof(c)); - --- -2.34.1 - diff --git a/builtins/civetweb/0002-only-include-header-if-necessary.patch b/builtins/civetweb/0002-only-include-header-if-necessary.patch deleted file mode 100644 index b2435527e1f4f..0000000000000 --- a/builtins/civetweb/0002-only-include-header-if-necessary.patch +++ /dev/null @@ -1,26 +0,0 @@ -From bbcfe76a87124685c5c9430e859d045fa3f50901 Mon Sep 17 00:00:00 2001 -From: ferdymercury -Date: Mon, 27 Apr 2026 17:07:22 +0200 -Subject: [PATCH 2/2] only include header if necessary - ---- - src/civetweb.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/civetweb.c b/src/civetweb.c -index f6a60e21..003e9e28 100644 ---- a/src/civetweb.c -+++ b/src/civetweb.c -@@ -1797,7 +1797,9 @@ typedef struct SSL_CTX SSL_CTX; - #include - #include - #include -+#if defined(OPENSSL_API_1_0) - #include -+#endif - #include - #include - #include --- -2.34.1 - diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index d79799e15a64f..077aca7c21123 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -6,13 +6,10 @@ # **PLEASE UPDATE ALSO THE FOLLOWING LINE WHEN UPDATING THE VERSION** # 10 Apr 2023, https://github.com/civetweb/civetweb/releases/tag/v1.16 -set(ROOT_CIVETWEB_VERSION 1.16) -set(ROOT_CIVETWEB_HASH "f0e471c1bf4e7804a6cfb41ea9d13e7d623b2bcc7bc1e2a4dd54951a24d60285") +set(ROOT_CIVETWEB_VERSION 1.16) # with some patches, so almost 1.17 +# set(ROOT_CIVETWEB_HASH "f0e471c1bf4e7804a6cfb41ea9d13e7d623b2bcc7bc1e2a4dd54951a24d60285") set(ROOT_CIVETWEB_PREFIX ${CMAKE_BINARY_DIR}/builtins/CIVETWEB-prefix) set(ROOT_CIVETWEB_LIBRARY ${ROOT_CIVETWEB_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}civetweb${CMAKE_STATIC_LIBRARY_SUFFIX}) -set(ROOT_CIVETWEB_PATCH_FILE_1 ${CMAKE_CURRENT_SOURCE_DIR}/0001-v117patches.patch) # From master branch (squash all until https://github.com/civetweb/civetweb/commit/588860e30721bf5453b0440c390865a8e85dcae5), commit -set(ROOT_CIVETWEB_PATCH_FILE_2 ${CMAKE_CURRENT_SOURCE_DIR}/0002-only-include-header-if-necessary.patch) # From https://github.com/civetweb/civetweb/pull/1389, commit -# Then call git format-patch -2 if (NOT DEFINED GIT_EXECUTABLE) set(GIT_EXECUTABLE "git") @@ -72,9 +69,10 @@ endif() ExternalProject_Add(BUILTIN_CIVETWEB PREFIX ${ROOT_CIVETWEB_PREFIX} # URL ${lcgpackages}/civetweb-${ROOT_CIVETWEB_VERSION}.tar.gz - URL https://github.com/civetweb/civetweb/archive/refs/tags/v${ROOT_CIVETWEB_VERSION}.tar.gz # TODO move to LCG - URL_HASH SHA256=${ROOT_CIVETWEB_HASH} - PATCH_COMMAND ${GIT_EXECUTABLE} apply ${ROOT_CIVETWEB_PATCH_FILE_1} ${ROOT_CIVETWEB_PATCH_FILE_2} + # URL https://github.com/civetweb/civetweb/archive/refs/tags/v${ROOT_CIVETWEB_VERSION}.tar.gz # TODO move to LCG + # URL_HASH SHA256=${ROOT_CIVETWEB_HASH} + GIT_REPOSITORY https://github.com/civetweb/civetweb.git + GIT_TAG 588860e30721bf5453b0440c390865a8e85dcae5 # v1.17.0 not yet released, v1.16.0 has missing CVE fixes LOG_DOWNLOAD TRUE LOG_CONFIGURE FALSE LOG_BUILD FALSE From 8a00d792b5b80914c103ae773377d1ed737372be Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 17:45:26 +0200 Subject: [PATCH 48/54] add patches --- builtins/civetweb/1385.patch | 45 ++++++++++++++++++++++++++++++++ builtins/civetweb/1389.patch | 26 ++++++++++++++++++ builtins/civetweb/CMakeLists.txt | 1 + 3 files changed, 72 insertions(+) create mode 100644 builtins/civetweb/1385.patch create mode 100644 builtins/civetweb/1389.patch diff --git a/builtins/civetweb/1385.patch b/builtins/civetweb/1385.patch new file mode 100644 index 0000000000000..356c8a8dfc4e7 --- /dev/null +++ b/builtins/civetweb/1385.patch @@ -0,0 +1,45 @@ +From 79077517e3d4ee648f47af12215dc1cf10e230a5 Mon Sep 17 00:00:00 2001 +From: "V.Shkriabets" +Date: Wed, 22 Apr 2026 16:14:18 +0300 +Subject: [PATCH] Fixed old variable names + +--- + src/civetweb.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index f6a60e214..006fa7a2b 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -19201,15 +19201,15 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) + conn->accept_gzip = 1; + } + #endif +- h_chunk = get_header(conn->request_info.http_headers, ++ h_chunk = get_header(conn->request_info.http_headers, + conn->request_info.num_headers, + "Transfer-Encoding"); +- h_len = get_header(conn->request_info.http_headers, ++ h_len = get_header(conn->request_info.http_headers, + conn->request_info.num_headers, + "Content-Length"); +- if (h_chunk != NULL) +- && mg_strcasecmp(cl, "identity")) { +- if ((0!=mg_strcasecmp(cl, "chunked")) || (h_len!=NULL)) { ++ if ((h_chunk != NULL) ++ && mg_strcasecmp(h_chunk, "identity")) { ++ if ((0!=mg_strcasecmp(h_chunk, "chunked")) || (h_len!=NULL)) { + mg_snprintf(conn, + NULL, /* No truncation check for ebuf */ + ebuf, +@@ -19224,8 +19224,8 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) + } else if (h_len != NULL) { + /* Request has content length set */ + char *endptr = NULL; +- conn->content_len = strtoll(cl, &endptr, 10); +- if ((endptr == cl) || (conn->content_len < 0)) { ++ conn->content_len = strtoll(h_len, &endptr, 10); ++ if ((endptr == h_len) || (conn->content_len < 0)) { + mg_snprintf(conn, + NULL, /* No truncation check for ebuf */ + ebuf, diff --git a/builtins/civetweb/1389.patch b/builtins/civetweb/1389.patch new file mode 100644 index 0000000000000..40c5352465a07 --- /dev/null +++ b/builtins/civetweb/1389.patch @@ -0,0 +1,26 @@ +From b8f93aafd3229fff5acea786b74b573202b169f3 Mon Sep 17 00:00:00 2001 +From: ferdymercury +Date: Mon, 27 Apr 2026 12:13:45 +0200 +Subject: [PATCH] Only include deprecated header when really necessary + +deprecated ENGINE API is only used #if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0) +--- + src/civetweb.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/civetweb.c b/src/civetweb.c +index f6a60e214..ecbd1a8a4 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1797,7 +1797,10 @@ typedef struct SSL_CTX SSL_CTX; + #include + #include + #include +-#include ++ ++#if defined(OPENSSL_API_1_0) ++#include // deprecated later on ++#endif + #include + #include + #include diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 077aca7c21123..29f67e66b7aee 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -73,6 +73,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB # URL_HASH SHA256=${ROOT_CIVETWEB_HASH} GIT_REPOSITORY https://github.com/civetweb/civetweb.git GIT_TAG 588860e30721bf5453b0440c390865a8e85dcae5 # v1.17.0 not yet released, v1.16.0 has missing CVE fixes + PATCH_COMMAND ${GIT_EXECUTABLE} apply 1389.patch 1385.patch # https://github.com/civetweb/civetweb/pull/1385 and https://github.com/civetweb/civetweb/pull/1389 LOG_DOWNLOAD TRUE LOG_CONFIGURE FALSE LOG_BUILD FALSE From 52c5e7af9ee8c0dc47f50892e443d232caac12d9 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 17:51:18 +0200 Subject: [PATCH 49/54] fix path and comments --- builtins/civetweb/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 29f67e66b7aee..143fccf0221ae 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -71,13 +71,14 @@ ExternalProject_Add(BUILTIN_CIVETWEB # URL ${lcgpackages}/civetweb-${ROOT_CIVETWEB_VERSION}.tar.gz # URL https://github.com/civetweb/civetweb/archive/refs/tags/v${ROOT_CIVETWEB_VERSION}.tar.gz # TODO move to LCG # URL_HASH SHA256=${ROOT_CIVETWEB_HASH} + # TODO: once 1.17 released, review _EXTERNAL_CIVETWEB workaround and bump SearchInstalledSoftware find_package to 1.17 plus remove builtin_ force-enable workaround with fail-on-missing GIT_REPOSITORY https://github.com/civetweb/civetweb.git GIT_TAG 588860e30721bf5453b0440c390865a8e85dcae5 # v1.17.0 not yet released, v1.16.0 has missing CVE fixes - PATCH_COMMAND ${GIT_EXECUTABLE} apply 1389.patch 1385.patch # https://github.com/civetweb/civetweb/pull/1385 and https://github.com/civetweb/civetweb/pull/1389 + PATCH_COMMAND ${GIT_EXECUTABLE} apply ${CMAKE_CURRENT_SOURCE_DIR}/1389.patch ${CMAKE_CURRENT_SOURCE_DIR}/1385.patch # https://github.com/civetweb/civetweb/pull/1385 and https://github.com/civetweb/civetweb/pull/1389 LOG_DOWNLOAD TRUE LOG_CONFIGURE FALSE LOG_BUILD FALSE - LOG_PATCH TRUE + LOG_PATCH FALSE LOG_INSTALL FALSE LOG_OUTPUT_ON_FAILURE FALSE USES_TERMINAL_DOWNLOAD true From a51da4280156dca71b617c763bb69ae6b6696bc1 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 18:07:20 +0200 Subject: [PATCH 50/54] commenting --- builtins/civetweb/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 143fccf0221ae..89b797e455eb2 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -72,6 +72,7 @@ ExternalProject_Add(BUILTIN_CIVETWEB # URL https://github.com/civetweb/civetweb/archive/refs/tags/v${ROOT_CIVETWEB_VERSION}.tar.gz # TODO move to LCG # URL_HASH SHA256=${ROOT_CIVETWEB_HASH} # TODO: once 1.17 released, review _EXTERNAL_CIVETWEB workaround and bump SearchInstalledSoftware find_package to 1.17 plus remove builtin_ force-enable workaround with fail-on-missing + # and potentially install libcivetweb-dev in root-ci-images and change RootBuildOptions default builtin_civetweb to OFF, if random crashes seen by linev are gone GIT_REPOSITORY https://github.com/civetweb/civetweb.git GIT_TAG 588860e30721bf5453b0440c390865a8e85dcae5 # v1.17.0 not yet released, v1.16.0 has missing CVE fixes PATCH_COMMAND ${GIT_EXECUTABLE} apply ${CMAKE_CURRENT_SOURCE_DIR}/1389.patch ${CMAKE_CURRENT_SOURCE_DIR}/1385.patch # https://github.com/civetweb/civetweb/pull/1385 and https://github.com/civetweb/civetweb/pull/1389 From 8c0772b1eba43ae1074a42f124a617b4831e52a4 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 21:46:26 +0200 Subject: [PATCH 51/54] fix indent warning --- builtins/civetweb/1385.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtins/civetweb/1385.patch b/builtins/civetweb/1385.patch index 356c8a8dfc4e7..a5e7e977ac54b 100644 --- a/builtins/civetweb/1385.patch +++ b/builtins/civetweb/1385.patch @@ -16,11 +16,11 @@ index f6a60e214..006fa7a2b 100644 } #endif - h_chunk = get_header(conn->request_info.http_headers, -+ h_chunk = get_header(conn->request_info.http_headers, ++ h_chunk = get_header(conn->request_info.http_headers, conn->request_info.num_headers, "Transfer-Encoding"); - h_len = get_header(conn->request_info.http_headers, -+ h_len = get_header(conn->request_info.http_headers, ++ h_len = get_header(conn->request_info.http_headers, conn->request_info.num_headers, "Content-Length"); - if (h_chunk != NULL) From ef8bc0b2fc1f74ef04e9f959b54c9ea1208517df Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 21:49:58 +0200 Subject: [PATCH 52/54] swap order, undefine update --- builtins/civetweb/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 89b797e455eb2..8e50adf093872 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -75,7 +75,8 @@ ExternalProject_Add(BUILTIN_CIVETWEB # and potentially install libcivetweb-dev in root-ci-images and change RootBuildOptions default builtin_civetweb to OFF, if random crashes seen by linev are gone GIT_REPOSITORY https://github.com/civetweb/civetweb.git GIT_TAG 588860e30721bf5453b0440c390865a8e85dcae5 # v1.17.0 not yet released, v1.16.0 has missing CVE fixes - PATCH_COMMAND ${GIT_EXECUTABLE} apply ${CMAKE_CURRENT_SOURCE_DIR}/1389.patch ${CMAKE_CURRENT_SOURCE_DIR}/1385.patch # https://github.com/civetweb/civetweb/pull/1385 and https://github.com/civetweb/civetweb/pull/1389 + PATCH_COMMAND ${GIT_EXECUTABLE} apply ${CMAKE_CURRENT_SOURCE_DIR}/1385.patch ${CMAKE_CURRENT_SOURCE_DIR}/1389.patch # https://github.com/civetweb/civetweb/pull/1385 and https://github.com/civetweb/civetweb/pull/1389 + UPDATE_COMMAND "" LOG_DOWNLOAD TRUE LOG_CONFIGURE FALSE LOG_BUILD FALSE From 0518ecaf4c0da8c205ff1171d3063e66ad2d488c Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 23:02:56 +0200 Subject: [PATCH 53/54] [ssl] missed one parent scope --- builtins/openssl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtins/openssl/CMakeLists.txt b/builtins/openssl/CMakeLists.txt index 477edf5146e5d..8d8945244c34c 100644 --- a/builtins/openssl/CMakeLists.txt +++ b/builtins/openssl/CMakeLists.txt @@ -92,5 +92,5 @@ set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIRS} PARENT_SCOPE) set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY} PARENT_SCOPE) set(OPENSSL_SSL_LIBRARY ${OPENSSL_SSL_LIBRARY} PARENT_SCOPE) set(OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES} PARENT_SCOPE) -set(OPENSSL_ROOT ${OPENSSL_ROOT}) +set(OPENSSL_ROOT ${OPENSSL_ROOT} PARENT_SCOPE) set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT} PARENT_SCOPE) From 8eb1f245e723dad98930e8349d525adfcb696818 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 27 Apr 2026 23:54:42 +0200 Subject: [PATCH 54/54] Update CMakeLists.txt --- builtins/civetweb/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtins/civetweb/CMakeLists.txt b/builtins/civetweb/CMakeLists.txt index 8e50adf093872..e9f350a197522 100644 --- a/builtins/civetweb/CMakeLists.txt +++ b/builtins/civetweb/CMakeLists.txt @@ -106,11 +106,11 @@ ExternalProject_Add(BUILTIN_CIVETWEB -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCIVETWEB_ENABLE_WEBSOCKETS=ON -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} - #-DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} - #-DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} + -DOPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} + -DOPENSSL_SSL_LIBRARY=${OPENSSL_SSL_LIBRARY} -DOPENSSL_LIBRARIES=${OPENSSL_LIBRARIES} #-DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} - #-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} + -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} #-DOPENSSL_FOUND=TRUE #-DOpenSSL_FOUND=TRUE #-DOpenSSL_ROOT=${OPENSSL_ROOT} #For CMake <3.27